#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define debug #include "k_debug.h" #include "k_file.h" #include "msp_bsl.h" #include "msp_ir.h" #include extern void board_setup_gpio(char *text, unsigned int gpio); #define USE_THREAD 0 #define USE_WLANSWITCH 0 //#define IRQ_IR IRQ_GPIO7 #define DAVINCI_GPIO_IRQ_PIN 7 #define IRQ_IR DAVINCI_GPIO_IRQ(DAVINCI_GPIO_IRQ_PIN) #if USE_WLANSWITCH #define IRQ_TASTER DAVINCI_GPIO_IRQ(48) #endif //static uint ir_i2c_addr = 0x23; static struct k_file *kfp=NULL; static char *devname = "/dev/ttyS2"; static char *fwname = "avmfw.txt"; static char filename[128]={0}; static char *cmd=NULL; static char buf[256]; static struct s_irdev { int thread_id; int is_registered; int has_irq; int has_resource; struct workqueue_struct *wq; struct input_dev *in_dev; int ledhandle; } irdev; static inline void input_report_misc(struct input_dev *dev,unsigned int code,int value) { input_event(dev, EV_MSC, code, value); } #define CMDBUFSIZE 128 static void wq_taster_func( void *data ) { /* standby code */ input_report_misc(irdev.in_dev, MSC_RAW, 0x260c); input_sync(irdev.in_dev); } static void wq_function( void *data ); static DECLARE_WORK( wq_object, wq_function, NULL ); static void wq_function( void *data ) { int rv; static unsigned char cmd[CMDBUFSIZE]; static unsigned int index=0; unsigned int code=-1; unsigned int cnt=0; unsigned int endmark=0; _D("START : %d\n",index); while(endmark==0 && index 1 ) { rv = parseIR( cmd, index-1, 0x803b, 6, &code ); if( rv>=0 ) { davinci_key_to_power_control(code); if( irdev.ledhandle ) avm_led_action_with_handle(irdev.ledhandle,avm_led_flash_on_125ms); input_report_misc(irdev.in_dev, MSC_RAW, code); input_sync(irdev.in_dev); } } index=0; queue_delayed_work( irdev.wq, &wq_object, HZ/20 ); _D("OK\n"); return; error: if( cnt==0 ) index=0; else queue_delayed_work( irdev.wq, &wq_object, HZ/20 ); _D("STOP : cnt=%d index=%d\n",cnt,index); return; } #if USE_WLANSWITCH static DECLARE_WORK( wq_taster, wq_taster_func, NULL ); static irqreturn_t taster_isr(int irq, void *dummy, struct pt_regs *fp) { int handled=1; _D("[taster_isr]\n"); if( !queue_work( irdev.wq, &wq_taster ) ) { _D("ERROR: queue_work failed !\n"); } return IRQ_RETVAL(handled); } #endif static irqreturn_t ir_isr(int irq, void *dummy, struct pt_regs *fp) { int handled=1; /* struct gpio_controller *GPIO = (struct gpio_controller *)IO_ADDRESS(DAVINCI_GPIO_BASE + 0x10); GPIO->clr_rising = 1 << DAVINCI_GPIO_IRQ_PIN; */ _D("[ir_isr]\n"); if( !queue_work( irdev.wq, &wq_object ) ) { _D("ERROR: queue_work failed !\n"); } return IRQ_RETVAL(handled); } #if USE_THREAD static DECLARE_COMPLETION( on_exit ); static int ir_thread( void *data ) { unsigned long timeout; daemonize("irkthread"); allow_signal( SIGTERM ); while(1) { timeout = HZ; timeout = wait_event_interruptible_timeout( irdev.wq, (timeout=0), timeout ); _D("[ir_thread] woke\n"); if( timeout==-ERESTARTSYS ) { _D("[ir_thread] go signal, break\n"); break; } } irdev.thread_id=0; complete_and_exit( &on_exit, 0 ); } #endif #define DRIVER_DESC "MSP430 IR-control device driver" #define LOAD_ERROR(x) \ {\ cleanup();\ return(x);\ } static int resetMSP430(void) { /* reset MSP430 via i2c or GPIO later */ /* kurz in den reset ziehen */ _D("[msp reset in]\n"); davinci_i2c_expander_op(CONFIG_DAVINCI_I2C_EXPANDER_ADDR,ATA_SEL,0); msleep(100);//udelay(10);/* min 2 micro seconds */ _D("[msp reset out]\n"); davinci_i2c_expander_op(CONFIG_DAVINCI_I2C_EXPANDER_ADDR,ATA_SEL,1); msleep(100);// udelay(10);/* min 2 micro seconds */ return 0; } static int OpenComDevice(char *name,unsigned int baud) { int rv=0; kfp = K_OPEN(name,O_NONBLOCK | O_RDONLY,0); if( kfp==NULL ) { _E("k_open of %s failed\n",name); return -1; } rv=SetBaudrate(kfp,baud); if( rv<0 ) { _E("SetBaudrate to %d failed (%d)\n",baud,rv); return -1; } return 0; } static void cbremove(int handle) { _D("[cbremove:%d]\n",handle); if( handle==irdev.ledhandle ) { irdev.ledhandle=0; } } static struct resource my_irq_gpio_resource = { .name = "msp430 irq", .flags = IORESOURCE_IO }; static int EnableGpioIrq(int gpio) { struct gpio_controller *GPIO = (struct gpio_controller *)IO_ADDRESS(DAVINCI_GPIO_BASE + 0x10); volatile unsigned int *gpio_bank_irq_enable = (volatile unsigned int *)IO_ADDRESS(DAVINCI_GPIO_BASE + 0x08); my_irq_gpio_resource.start = my_irq_gpio_resource.end = gpio; if(request_resource(&gpio_resource, &my_irq_gpio_resource)) { printk(KERN_ERR "ERROR: gpio %u is not avalible as irq pin\n", gpio); return -EFAULT; } printk("[EnableGpioIrq]\n"); /*--- board_setup_gpio("msp430", gpio); ---*/ davinci_gpio_ctrl(gpio, GPIO_PIN, GPIO_INPUT_PIN); GPIO->set_rising = 1 << gpio; /*--- GPIO->set_falling = 1 << gpio; ---*/ GPIO->clr_falling = 1 << gpio; *gpio_bank_irq_enable |= 1; GPIO->intstat = (1 << gpio); /* clear pending irqs */ return 0; } static int RegisterInputDevice(void) { int err=0; _D("[ir_init]\n"); memset( &irdev, 0, sizeof(irdev) ); irdev.wq = create_workqueue("irqueue"); #if USE_THREAD irdev.thread_id = kernel_thread( ir_thread, NULL, CLONE_KERNEL ); if( irdev.thread_id==0 ) { _D(KERN_ERR "msp430.c: Can't create thread\n"); return -EIO; } #endif irdev.in_dev = input_allocate_device(); if( !irdev.in_dev ) { _D(KERN_ERR "msp430.c: Can't allocate input device\n"); return -ENOMEM; } irdev.in_dev->name = "MSP430_IR"; irdev.in_dev->id.bustype = BUS_PCI; irdev.in_dev->id.vendor = PCI_VENDOR_ID_AVM; irdev.in_dev->id.product = 0x0000; irdev.in_dev->id.version = 0x0100; if( EnableGpioIrq(DAVINCI_GPIO_IRQ_PIN) ) { return -EFAULT; } irdev.has_resource=1; if (request_irq(IRQ_IR, ir_isr, IRQF_DISABLED | IRQF_SHARED | IRQF_TRIGGER_RISING, "MSP430_IR", irdev.in_dev )) { _D(KERN_ERR "msp430.c: Can't allocate irq %d\n", IRQ_IR); return -EBUSY; } irdev.has_irq++; #if USE_WLANSWITCH if (request_irq(IRQ_TASTER, taster_isr, IRQF_DISABLED | IRQF_SHARED | IRQF_TRIGGER_RISING, "TASTER", irdev.in_dev )) { _D(KERN_ERR "msp430.c: Can't allocate irq %d\n", IRQ_TASTER); return -EBUSY; } irdev.has_irq++; #endif irdev.in_dev->evbit[0] = BIT(EV_MSC); // | BIT(EV_REP); //irdev.in_dev->keybit[LONG(BTN_0)] = BIT(BTN_0); irdev.in_dev->mscbit[LONG(MSC_RAW)] = BIT(MSC_RAW); input_register_device(irdev.in_dev); irdev.is_registered=1; // irdev.ledhandle = avm_led_alloc_handle("power",0,cbremove); irdev.ledhandle = avm_led_alloc_handle("info",0,cbremove); _D("ledhandle:%x\n",irdev.ledhandle); return 0; } static void DeregisterInputDevice(void) { if( irdev.has_resource ) { release_resource(&my_irq_gpio_resource); } if( irdev.in_dev ) { if( irdev.is_registered ) { input_unregister_device(irdev.in_dev); } else { input_free_device(irdev.in_dev); } #if USE_WLANSWITCH if( irdev.has_irq>1 ) { free_irq(IRQ_TASTER, irdev.in_dev); } #endif if( irdev.has_irq>0 ) { free_irq(IRQ_IR, irdev.in_dev); } } #if USE_THREAD if( irdev.thread_id ) kill_proc( irdev.thread_id, SIGTERM, 1 ); #endif if( irdev.wq ) { destroy_workqueue( irdev.wq ); } if( irdev.ledhandle ) { avm_led_free_handle(irdev.ledhandle); } } static void cleanup( void ) { DeregisterInputDevice(); if( kfp ) K_CLOSE(kfp); kfp = NULL; } static unsigned int doreset=1; int __init msp_init(void) { int rv=0, pos=0, i=0; size_t len=0; if(davinci_revision[1] != 0 ) { printk("[msp_init] no MSP430 IR on new Hardware\n"); return 0; } if( filename[0]!=0 ) fwname = filename; _D("filename='%s'\n",fwname); _D("[msp_init]\n"); if( doreset ) resetMSP430(); /* TODO: move to msp_bsl.c, ee next comment */ /* call msp_bsl doing his own com setup, because will change to GPIO */ rv = msp_bsl(fwname,devname); if( rv ) { LOAD_ERROR(-1); } /* TODO: error code */ /* open comport */ /* set baudrate to fw baudrate 38400 */ rv = OpenComDevice(devname,38400); if( rv ) { LOAD_ERROR(rv); } /* register interrupt */ /* register input device */ rv = RegisterInputDevice(); if( rv ) { LOAD_ERROR(rv); } // LOAD_ERROR(-1); /* stop for testing */ return 0; } void __exit msp_exit(void) { _D("[msp_exit]\n"); cleanup(); } module_init(msp_init); module_exit(msp_exit); MODULE_PARM_DESC(doreset,"doreset=0 suppress reset of msp for debug purposes (default is 1)"); module_param(doreset,uint,0); MODULE_PARM_DESC(filename,"firmware filename (defaults to avmfw.txt)"); module_param_string(filename,filename,sizeof(filename),0); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION(DRIVER_DESC); MODULE_AUTHOR("g.diesing@avm.de");