summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'app-misc/lirc/files/lirc_wb677/lirc_wb677_main.c')
-rwxr-xr-xapp-misc/lirc/files/lirc_wb677/lirc_wb677_main.c1717
1 files changed, 1717 insertions, 0 deletions
diff --git a/app-misc/lirc/files/lirc_wb677/lirc_wb677_main.c b/app-misc/lirc/files/lirc_wb677/lirc_wb677_main.c
new file mode 100755
index 0000000..946f852
--- /dev/null
+++ b/app-misc/lirc/files/lirc_wb677/lirc_wb677_main.c
@@ -0,0 +1,1717 @@
+#define _GNU_SOURCE
+#include "lirc_wb677.h"
+#include "lirc_wb677_mouse_kbd.h"
+#include "wb83677hg_ir.h"
+
+struct irctl w83667hg_irctl;
+
+/* chip id string, at most 7 characters */
+char *chip_id = "w677hga";
+
+
+
+
+/* enter extended function mode */
+static inline void cr_enter_ext(void)
+{
+ outb(0x87, CFG_idx);
+ outb(0x87, CFG_idx);
+
+ if (0xff == cr_read(0x20)) {
+ CFG_idx = cr_cfg_idx2;
+ CFG_dat = cr_cfg_dat2;
+ outb(0x87, CFG_idx);
+ outb(0x87, CFG_idx);
+ }
+}
+
+
+/* exit extended function mode */
+static inline void cr_exit_ext(void)
+{
+ outb(0xaa, CFG_idx);
+}
+
+/* select logical device */
+static inline void cr_select_log_dev(int cr)
+{
+ outb(0x07, CFG_idx);
+ outb(cr, CFG_dat);
+}
+
+static inline void cr_update(int dat, int cr)
+{
+ outb(cr, CFG_idx);
+ outb(dat, CFG_dat);
+}
+
+static inline u8 cr_read(int cr)
+{
+ outb(cr, CFG_idx);
+ return inb(CFG_dat);
+}
+
+static inline void cr_safe_update(u8 dat, int cr)
+{
+ cr_update(cr_read(cr) | dat, cr);
+}
+
+static inline void cr_safe_clear(u8 dat, int cr)
+{
+ cr_update(cr_read(cr) & dat, cr);
+}
+
+
+/* read/write cir registers */
+
+static inline void cir_update(u8 dat, int cir)
+{
+ outb(dat, cir_address + (cir & 0xff));
+}
+
+static u8 cir_read(int cir)
+{
+ u8 val;
+
+ val = inb(cir_address + (cir & 0xff));
+
+ return val;
+}
+
+static inline void cir_wake_update(u8 dat, int cir)
+{
+
+ outb(dat, cir_wake_address + (cir & 0xff));
+
+}
+
+static u8 cir_wake_read(int cir)
+{
+ u8 val;
+
+ val = inb(cir_wake_address + (cir & 0xff));
+
+ return val;
+}
+
+static void cir_dump_reg(void)
+{
+ cr_enter_ext();
+ printk("Dump CIR CR logical device:\n");
+ cr_select_log_dev(CIR_LOG_DEV);
+ printk("CR CIR ACTIVE : 0x%x\n", cr_read(0x30));
+ printk("CR CIR BASE ADDR: 0x%x\n", (cr_read(0x61) << 8)|cr_read(0x60));
+ printk("CR CIR IRQ NUM: 0x%x\n", cr_read(0x70));
+ cr_exit_ext();
+
+ printk("Dump CIR Registers\n");
+ printk("CIR IRCON: 0x%x\n", cir_read(CIR_IRCON));
+ printk("CIR IRSTS: 0x%x\n", cir_read(CIR_IRSTS));
+ printk("CIR IREN: 0x%x\n", cir_read(CIR_IREN));
+ printk("CIR RXFCONT: 0x%x\n", cir_read(CIR_RXFCONT));
+ printk("CIR CP: 0x%x\n", cir_read(CIR_CP));
+ printk("CIR CC: 0x%x\n", cir_read(CIR_CC));
+ printk("CIR SLCH: 0x%x\n", cir_read(CIR_SLCH));
+ printk("CIR SLCL: 0x%x\n", cir_read(CIR_SLCL));
+ printk("CIR FIFOCON: 0x%x\n", cir_read(CIR_FIFOCON));
+ printk("CIR IRFIFOSTS: 0x%x\n", cir_read(CIR_IRFIFOSTS));
+ printk("CIR SRXFIFO: 0x%x\n", cir_read(CIR_SRXFIFO));
+ printk("CIR TXFCONT: 0x%x\n", cir_read(CIR_TXFCONT));
+ printk("CIR STXFIFO: 0x%x\n", cir_read(CIR_STXFIFO));
+ printk("CIR FCCH: 0x%x\n", cir_read(CIR_FCCH));
+ printk("CIR FCCL: 0x%x\n", cir_read(CIR_FCCL));
+ printk("CIR IRFSM: 0x%x\n", cir_read(CIR_IRFSM));
+
+}
+
+static void cir_wake_dump_reg(void)
+{
+ u8 i = 0;
+
+ cr_enter_ext();
+ printk("Dump CIR WKAE CR logical device:\n");
+ cr_select_log_dev(CIR_WAKE_LOG_DEV);
+ printk("CR CIR WAKE ACTIVE : 0x%x \n", cr_read(0x30));
+ printk("CR CIR WAKE BASE ADDR: 0x%x\n", (cr_read(0x61) << 8)|cr_read(0x60));
+ printk("CR CIR WAKE IRQ NUM: 0x%x\n", cr_read(0x70));
+ cr_exit_ext();
+
+ printk("Dump CIR WAKE Registers\n");
+ printk("CIR WAKE IRCON: 0x%x\n", cir_wake_read(CIR_WAKE_IRCON));
+ printk("CIR IRSTS: 0x%x\n", cir_wake_read(CIR_WAKE_IRSTS));
+ printk("CIR IREN: 0x%x\n", cir_wake_read(CIR_WAKE_IREN));
+ printk("CIR WAKE FIFO CMP DEEP: 0x%x\n", cir_wake_read(CIR_WAKE_FIFO_CMP_DEEP));
+ printk("CIR WAKE FIFO CMP TOL: 0x%x\n", cir_wake_read(CIR_WAKE_FIFO_CMP_TOL));
+ printk("CIR WAKE FIFO COUNT: 0x%x\n", cir_wake_read(CIR_WAKE_FIFO_COUNT));
+ printk("CIR WAKE SLCH: 0x%x\n", cir_wake_read(CIR_WAKE_SLCH));
+ printk("CIR WAKE SLCL: 0x%x\n", cir_wake_read(CIR_WAKE_SLCL));
+ printk("CIR WAKE FIFOCON: 0x%x\n", cir_wake_read(CIR_WAKE_FIFOCON));
+ printk("CIR WAKE SRXFSTS: 0x%x\n", cir_wake_read(CIR_WAKE_SRXFSTS));
+ printk("CIR WAKE SAMPLE RX FIFO: 0x%x\n", cir_wake_read(CIR_WAKE_SAMPLE_RX_FIFO));
+ printk("CIR WAKE WR FIFO DATA: 0x%x\n", cir_wake_read(CIR_WAKE_WR_FIFO_DATA));
+ printk("CIR WAKE RD FIFO ONLY: 0x%x\n", cir_wake_read(CIR_WAKE_RD_FIFO_ONLY));
+ printk("CIR WAKE RD FIFO ONLY IDX: 0x%x\n", cir_wake_read(CIR_WAKE_RD_FIFO_ONLY_IDX));
+ printk("CIR WAKE FIFO IGNORE: 0x%x\n", cir_wake_read(CIR_WAKE_FIFO_IGNORE));
+ printk("CIR WAKE IRFSM: 0x%x\n", cir_wake_read(CIR_WAKE_IRFSM));
+
+ printk("Dump CIR WAKE keys\n");
+ printk("%s FIFO count len = %d\n", DRVNAME, cir_wake_read(CIR_WAKE_FIFO_COUNT));
+ i = 0;
+ for (; i < 67; i++) {
+ printk("%s FIFO = 0x%x\n", DRVNAME, cir_wake_read(CIR_WAKE_RD_FIFO_ONLY));
+ }
+
+}
+
+
+/* 1. */
+/* 677HG Config Registers init */
+static int w83667hg_cr_init(void)
+{
+ int val = 0;
+
+ cr_enter_ext();
+
+ /* Check 677 CHIP ID first */
+ val = cr_read(CHIP_ID_HIGH_ADDR);
+ if (val != CHIP_ID_HIGH) {
+ printk("%s %s: chip id high: 0x%x expect:0x%x\n", DRVNAME, chip_id, val, CHIP_ID_HIGH);
+ /*// cr_exit_ext();
+ // return -ENODEV;*/
+ }
+ else{
+ printk("%s %s: chip id high: 0x%x\n", DRVNAME, chip_id, val);
+ }
+
+ /* now check chip id, LSB */
+ val = cr_read(CHIP_ID_LOW_ADDR);
+ if (CHIP_ID_LOW == (val & CHIP_ID_LOW)) {
+ printk("%s %s: chip id low: 0x%x expect:0x%x\n", DRVNAME, chip_id, val, CHIP_ID_LOW);
+ /*// cr_exit_ext();
+ // return -ENODEV;*/
+ } else{
+ printk("%s %s: chip id low: 0x%x\n", DRVNAME, chip_id, val);
+ }
+
+ /* for multi-function pin selection */
+ val = cr_read(0x27);
+ val = (val&0xbc) | 0x41;
+ cr_update(val, 0x27); /*For W83677, CIR TX,RX, CIRWB pin selection*/
+
+
+
+ /* FIXME*/
+ /* set Logical Dev 1: LPT */
+ /* not sure really need it or not, find it in 667 wake up windows driver */
+ cr_select_log_dev(0x01);
+ cr_update(DEACTIVE_LOG_DEV, 0x30);
+ cr_update(0, 0x70);
+
+ /* Then set Logical Dev 6: CIR */
+ cr_select_log_dev(CIR_LOG_DEV);
+ cr_update(ACTIVE_LOG_DEV, 0x30);
+
+ cir_address = CIR_BASE;
+ cr_update(((CIR_BASE & 0xff00) >> 8), 0x60);
+ cr_update((CIR_BASE & 0xff), 0x61);
+#ifdef IR_667_DEBUG
+ printk("%s base io port address: 0x%x\n", DRVNAME, cir_address);
+#endif
+
+ cr_update(CIR_IRQ_NUM, 0x70);
+#ifdef IR_667_DEBUG
+ printk("%s irq number: %d\n", DRVNAME, CIR_IRQ_NUM);
+#endif
+
+
+ /* Then set Logical Dev A: ACPI */
+ cr_select_log_dev(ACPI_LOG_DEV);
+
+ cr_update(ACTIVE_LOG_DEV, 0x30);
+
+ /* enable cir wake up via PSOUT# (pin 60) */
+ cr_safe_update(ENABLE_CIR_WAKE, 0xe0);
+
+ /* enable cir interrupt of mouse IRQ event */
+ /*//cr_safe_update(ENABLE_CIR_INTR_OF_MOUSE_IRQ, 0xf6);*/
+
+ /* enable pme interrupt of cir password event */
+ /*//cr_safe_update(ENABLE_PME_INTR_OF_CIR_PASS, 0xf7);*/
+
+
+ /* Then set Logical Dev E: CIR WAKE */
+ cr_select_log_dev(CIR_WAKE_LOG_DEV);
+ cr_update(ACTIVE_LOG_DEV, 0x30);
+
+ cir_wake_address = CIR_WAKE_BASE;
+ cr_update(((CIR_WAKE_BASE & 0xff00) >> 8), 0x60);
+ cr_update((CIR_WAKE_BASE & 0xff), 0x61);
+#ifdef IR_667_DEBUG
+ printk("%s cir wake up base io port address: 0x%x\n", DRVNAME, cir_wake_address);
+#endif
+
+#ifdef ENABLE_CIR_WAKE_IRQ
+ cr_update(CIR_WAKE_IRQ_NUM, 0x70);
+#ifdef IR_667_DEBUG
+ printk("%s cir wake up irq number: %d\n", DRVNAME, CIR_WAKE_IRQ_NUM);
+#endif
+#endif
+
+ cr_exit_ext();
+
+ return 0;
+}
+
+static void w83667hg_cr_uninit(void)
+{
+ cr_enter_ext();
+
+ /* close CIR */
+ /* Don't close CIR Wake. When wake-up at power-on, it needs the function. 20091224
+ cr_select_log_dev(CIR_WAKE_LOG_DEV);
+ cr_update(DEACTIVE_LOG_DEV, 0x30);
+ */
+
+
+ /* Close Logical Dev A: ACPI */
+ /*//cr_select_log_dev(ACPI_LOG_DEV);*/
+
+ /* enable cir wake up via PSOUT# (pin 60) */
+ /*//cr_safe_clear(DISABLE_CIR_WAKE, 0xe0);*/
+
+ /* enable cir interrupt of mouse IRQ event */
+ /*//cr_safe_clear(DISABLE_CIR_INTR_OF_MOUSE_IRQ, 0xf6);*/
+
+ /* enable pme interrupt of cir password event */
+ /*//cr_safe_clear(DISABLE_PME_INTR_OF_CIR_PASS, 0xf7);*/
+
+
+ /* close CIR wake up XXX */
+ /*//cr_select_log_dev(CIR_WAKE_LOG_DEV);
+ //cr_update(DEACTIVE_LOG_DEV, 0x30);*/
+
+ cr_exit_ext();
+}
+
+static int w83667hg_input_open(struct input_dev *cur_input_dev)
+{
+ return 0;
+}
+
+static void w83667hg_input_close(struct input_dev *cur_input_dev)
+{
+}
+
+/* 2. */
+/* init linux input structure */
+static struct input_dev *w83667hg_input_init(void)
+{
+ int i;
+ struct input_dev *cur_input_dev = NULL;
+
+ cur_input_dev = input_allocate_device();
+ if (NULL == cur_input_dev) {
+ printk("alloc input device error\n");
+ return NULL;
+ }
+
+ cur_input_dev->name = INPUTNAME;
+ cur_input_dev->phys = DRVNAME;
+ memcpy(&cur_input_dev->id, &w83667hg_input_id, sizeof(struct input_id));
+
+ cur_input_dev->open = w83667hg_input_open;
+ cur_input_dev->close = w83667hg_input_close;
+
+ /*//cur_input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REL);
+ //cur_input_dev->relbit[0] = BIT(REL_X) | BIT(REL_Y);*/
+ cur_input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL);
+ cur_input_dev->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(REL_Y);
+ for (i = 0; i < 256; i++) {
+ set_bit(usb_kbd_keycode[i], cur_input_dev->keybit);
+ }
+ clear_bit(0, cur_input_dev->keybit);
+/*// cur_input_dev->keybit[LONG(BTN_MOUSE)] = BIT(BTN_LEFT) | BIT(BTN_RIGHT);*/
+ cur_input_dev->keybit[BIT_WORD(BTN_MOUSE)] = BIT_MASK(BTN_LEFT) | BIT_MASK(BTN_RIGHT);
+
+
+ if (input_register_device(cur_input_dev)) {
+ printk("register input device error\n");
+ return NULL;
+ }
+
+ return cur_input_dev;
+}
+
+static void w83667hg_input_uninit(struct input_dev *cur_input_dev)
+{
+ input_get_device(cur_input_dev);
+ input_unregister_device(cur_input_dev);
+ input_free_device(cur_input_dev);
+ cur_input_dev = NULL;
+}
+
+
+/* internal call for register lirc */
+static int lirc_set_use_inc(void *data)
+{
+ struct irctl *ir = data;
+
+ if (!ir) {
+ printk("%s: set_use_inc called with no context\n", DRVNAME);
+ return -EIO;
+ }
+#ifdef IR_667_DEBUG
+ printk("%s : set use inc\n", DRVNAME);
+#endif
+ spin_lock(&ir->lock);
+ ir->ctrl_fix_head = 1;
+ spin_unlock(&ir->lock);
+
+
+ MOD_INC_USE_COUNT;
+
+ return 0;
+}
+
+static void lirc_set_use_dec(void *data)
+{
+ struct irctl *ir = data;
+
+ if (!ir) {
+ printk("%s: set_use_dec called with no context\n", DRVNAME);
+ return;
+ }
+#ifdef IR_667_DEBUG
+ printk("%s : set use dec\n", DRVNAME);
+#endif
+
+ MOD_DEC_USE_COUNT;
+}
+
+
+static int lirc_ioctl(struct inode *node, struct file *filep, unsigned int cmd, unsigned long arg)
+{
+ int ret = 0;
+ void __user *uptr = (void __user *)arg;
+
+ int mode = 0;
+ struct irctl *ir = &w83667hg_irctl;
+ struct ir_recv_t *ir_recv = &w83667hg_ir_recv;
+
+#ifdef ALLOW_DEBUG_IOCTL
+ printk("%s: IO Ctrl Code:%d\n", DRVNAME, cmd);
+#endif
+
+ switch (cmd) {
+ /* lirc ioctl commands */
+ case LIRC_GET_FEATURES:
+ ret = put_user(w83667hg_lirc_plugin->features, (unsigned long *)arg);
+ break;
+ case LIRC_GET_SEND_MODE:
+ if (!(w83667hg_lirc_plugin->features & LIRC_CAN_REC_MASK))
+ return -ENOSYS;
+
+ ret = put_user(LIRC_REC2MODE
+ (w83667hg_lirc_plugin->features & LIRC_CAN_REC_MASK),
+ (unsigned long *)arg);
+ if (ret)
+ return ret;
+ break;
+ case LIRC_SET_SEND_MODE:
+ ret = get_user(mode, (unsigned long *)arg);
+ if (ret)
+ return ret;
+ if (mode != (LIRC_MODE_PULSE&LIRC_CAN_SEND_MASK))
+ return -EINVAL;
+ break;
+ case LIRC_GET_LENGTH:
+ ret = put_user((unsigned long)w83667hg_lirc_plugin->code_length,
+ (unsigned long *)arg);
+ break;
+
+ /* ioctl commands for lirc_wb667 */
+ case IR_DUMPCIRREG:
+ cir_dump_reg();
+ break;
+ case IR_DUMPWAKEREG:
+ cir_wake_dump_reg();
+ break;
+ case IR_IOLEARNWAKE:
+ spin_lock(&ir_recv->lock);
+ if (ir_recv->wake_state) {
+ spin_unlock(&ir_recv->lock);
+ ret = -EFAULT;
+ break;
+ }
+ ir_recv->wake_state = ST_WAKE_START;
+ spin_unlock(&ir_recv->lock);
+
+ /* close cir first */
+ cr_enter_ext();
+ cr_select_log_dev(CIR_LOG_DEV);
+ cr_update(DEACTIVE_LOG_DEV, 0x30);
+ cr_exit_ext();
+ /*//cir_update(0, CIR_IREN);*/
+ cir_update(0xff, CIR_IRSTS);
+
+ /* set cir wake */
+ cir_wake_update(CIR_WAKE_IRCON_RXEN | CIR_WAKE_IRCON_RXINV | CIR_WAKE_IRCON_SAMPLE_PERIOD_SEL, CIR_WAKE_IRCON);
+ cir_wake_update(0xff, CIR_WAKE_IRSTS);
+ cir_wake_update(0xff, CIR_WAKE_FIFOCON);
+ cir_wake_update(0, CIR_WAKE_FIFOCON);
+#ifdef ALLOW_DEBUG_WAKE
+ printk("%s FIFO count len = %d\n", DRVNAME, cir_wake_read(CIR_WAKE_FIFO_COUNT));
+#endif
+ cir_wake_update(CIR_WAKE_IREN_PE, CIR_WAKE_IREN);
+
+ wait_event(ir_recv->queue, ir_recv->wake_state == ST_WAKE_FINISH);
+ while ((cir_wake_read(CIR_WAKE_RD_FIFO_ONLY_IDX)) != 0) {
+#ifdef ALLOW_DEBUG_WAKE
+ printk("%s setting wake up key: 0x%x\n", DRVNAME, cir_wake_read(CIR_WAKE_RD_FIFO_ONLY));
+#else
+ cir_wake_read(CIR_WAKE_RD_FIFO_ONLY);
+#endif
+ }
+
+ /* learn wake up complete */
+ spin_lock(&ir_recv->lock);
+ ir_recv->wake_state = ST_WAKE_NONE;
+ spin_unlock(&ir_recv->lock);
+
+#ifdef ALLOW_DEBUG_WAKE
+ ret = 0;
+ for (; ret < 67; ret++) {
+ printk("%s FIFO count = 0x%x\n", DRVNAME, cir_wake_read(CIR_WAKE_RD_FIFO_ONLY));
+ }
+ printk("%s FIFO count len = %d\n", DRVNAME, cir_wake_read(CIR_WAKE_FIFO_COUNT));
+#endif
+
+ /* cir wake interrupt stop, resume cir */
+ cir_wake_update(CIR_WAKE_IRCON_MODE0 | CIR_WAKE_IRCON_RXEN | CIR_WAKE_IRCON_R | CIR_WAKE_IRCON_RXINV | CIR_WAKE_IRCON_SAMPLE_PERIOD_SEL, CIR_WAKE_IRCON);
+ cir_wake_update(0xff, CIR_WAKE_IRSTS);
+ cir_wake_update(0, CIR_WAKE_IREN);
+ cr_enter_ext();
+ cr_select_log_dev(CIR_LOG_DEV);
+ cr_update(ACTIVE_LOG_DEV, 0x30);
+ cr_exit_ext();
+ ret = 0;
+
+ break;
+ case IR_IOUNSETWAKE:
+ cir_wake_update(0, CIR_WAKE_IRCON);
+ cir_wake_update(0, CIR_WAKE_IREN);
+ cir_wake_update(0xFF, CIR_WAKE_IRSTS);
+ cir_wake_update(0, CIR_WAKE_IRSTS);
+ cr_enter_ext();
+ cr_select_log_dev(CIR_WAKE_LOG_DEV);
+ cr_update(DEACTIVE_LOG_DEV, 0x30);
+ cr_exit_ext();
+ /*//cir_wake_update(CIR_WAKE_IRCON_MODE0 | CIR_WAKE_IRCON_RXEN | CIR_WAKE_IRCON_R | CIR_WAKE_IRCON_RXINV | CIR_WAKE_IRCON_SAMPLE_PERIOD_SEL, CIR_WAKE_IRCON);*/
+ break;
+ case IR_IOSETWAKE:
+ cr_enter_ext();
+ cr_select_log_dev(CIR_WAKE_LOG_DEV);
+ cr_update(ACTIVE_LOG_DEV, 0x30);
+ cr_exit_ext();
+ cir_wake_update(CIR_WAKE_IRCON_MODE0 | CIR_WAKE_IRCON_RXEN | CIR_WAKE_IRCON_R | CIR_WAKE_IRCON_RXINV | CIR_WAKE_IRCON_SAMPLE_PERIOD_SEL, CIR_WAKE_IRCON);
+ cir_wake_update(0xFF, CIR_WAKE_IRSTS);
+ cir_wake_update(0, CIR_WAKE_IRSTS);
+ cir_wake_update(0, CIR_WAKE_IREN);
+ break;
+ case IR_IOCLEANDATA:
+ w83667hg_clean_data(&w83667hg_ir_recv, &w83667hg_irctl);
+ break;
+ case IR_IOSTARTSTUDY:
+ spin_lock(&ir->lock);
+ ir->study_state = ST_STUDY_START;
+ spin_unlock(&ir->lock);
+ /*//cir_update(cir_read(CIR_IRCON) | CIR_IRCON_WIREN, CIR_IRCON);*/
+ cir_update(CIR_IRCON_WIREN | CIR_IRCON_RXEN | CIR_IRCON_SAMPLE_PERIOD_SEL, CIR_IRCON);
+
+/* for WRX RTR
+ cir_update(CIR_FIFOCON_RX_TRIGGER_LEV_8, CIR_FIFOCON);
+ //cir_update(CIR_IREN_RTR, CIR_IREN);
+ cir_update(0, CIR_IREN);
+*/
+ break;
+ case IR_IOSTOPSTUDY:
+ cir_update(CIR_IRCON_TXEN | CIR_IRCON_RXEN | CIR_IRCON_RXINV | CIR_IRCON_SAMPLE_PERIOD_SEL, CIR_IRCON);
+ spin_lock(&ir->lock);
+ ir->study_state = ST_STUDY_NONE;
+ ir->buf_count = 0;
+ ir->cur_buf_num = 0;
+ wake_up(&ir->queue);
+ spin_unlock(&ir->lock);
+ break;
+ case IR_IOGETCARRIER:
+ spin_lock(&ir->lock);
+ if (!(ir->study_state > ST_STUDY_NONE)) {
+ ret = -EFAULT;
+ spin_unlock(&ir->lock);
+ break;
+ }
+ /*//w83667hg_record_study_carrier(ir);*/
+#ifdef ALLOW_DEBUG_IOCTL
+ printk("%s: current get carrier: %d\n", DRVNAME, ir->carrier);
+#endif
+ if (copy_to_user(uptr, &ir->carrier, sizeof(unsigned int))) {
+ ret = -EFAULT;
+ }
+ spin_unlock(&ir->lock);
+ break;
+ case IR_IOSETCARRIER:
+ spin_lock(&ir->lock);
+ /* you can set carrier at any time */
+ /*
+ if (~(ir->send_state)) {
+ ret = -EFAULT;
+ up(&ir->lock);
+ break;
+ }
+ */
+ if (copy_from_user(&ir->carrier, uptr, sizeof(unsigned int))) {
+ ret = -EFAULT;
+ spin_unlock(&ir->lock);
+ break;
+ }
+#ifdef ALLOW_DEBUG_IOCTL
+ printk("%s: current set carrier: %x\n", DRVNAME, ir->carrier);
+#endif
+ w83667hg_set_carrier(&ir->carrier);
+ spin_unlock(&ir->lock);
+ break;
+ case IR_IOSTUDYLEN:
+ spin_lock(&ir->lock);
+ if (ir->study_state == ST_STUDY_NONE) {
+ spin_unlock(&ir->lock);
+#ifdef ALLOW_DEBUG_IOCTL
+ printk("%s: open STUDY first\n", DRVNAME);
+#endif
+ ret = -EFAULT;
+ break;
+ }
+ ir->study_state = ST_STUDY_START;
+ spin_unlock(&ir->lock);
+ wait_event(ir->queue, ir->study_state == ST_STUDY_ALL_RECV);
+ spin_lock(&ir->lock);
+
+ /* in STUDY, copy data buf len to user space for read() */
+ ir->cur_buf_num = 0;
+ if (ir->buf_count == 0) {
+ ret = -EFAULT;
+ } else if (copy_to_user(uptr, &ir->buf_count, sizeof(unsigned int))) {
+ ret = -EFAULT;
+ }
+ /* copy data to lirc plugin buffer, ready copy to user space */
+ spin_unlock(&ir->lock);
+ break;
+ case IR_IOSTUDYBUF:
+ spin_lock(&ir->lock);
+ if (ir->study_state != ST_STUDY_ALL_RECV) {
+ spin_unlock(&ir->lock);
+ ret = -EFAULT;
+ break;
+ }
+ if (copy_to_user(uptr, &ir->buf[ir->cur_buf_num], sizeof(unsigned char))) {
+ spin_unlock(&ir->lock);
+ ret = -EFAULT;
+ break;
+ }
+ if (ir->cur_buf_num < ir->buf_count) {
+ ir->cur_buf_num++;
+ }
+ spin_unlock(&ir->lock);
+ break;
+ case IR_IORESTUDYBUF:
+ spin_lock(&ir->lock);
+ ir->cur_buf_num = 0;
+ spin_unlock(&ir->lock);
+ break;
+ case IR_CHECKCHIP:
+ if (copy_to_user(uptr, chip_id, sizeof(unsigned long long))) {
+ ret = -EFAULT;
+ }
+ break;
+ default:
+ return -ENOIOCTLCMD;
+ }
+
+ return ret;
+}
+
+static int w83667hg_set_carrier(unsigned int *carrier)
+{
+ u16 val;
+
+ cir_update(1, CIR_CP);
+ val = 3000000 / (*carrier) - 1;
+ cir_update(val & 0xff, CIR_CC);
+
+#ifdef ALLOW_DEBUG_STUDY
+ printk("cp: 0x%x cc: 0x%x\n", cir_read(CIR_CP), cir_read(CIR_CC));
+#endif
+ return 0;
+}
+
+static ssize_t lirc_write(struct file *file, const char *buf, size_t n, loff_t * ppos)
+{
+ size_t cur_count;
+ struct ir_send_t *ir_send = &w83667hg_ir_send;
+ unsigned int i;
+ ssize_t ret;
+
+ spin_lock(&ir_send->lock);
+
+ if (n >= IRCTL_BUF_LIMIT) {
+ ir_send->buf_count = cur_count = IRCTL_BUF_LIMIT;
+ ret = IRCTL_BUF_LIMIT;
+ } else {
+ ir_send->buf_count = cur_count = n;
+ ret = n;
+ }
+
+ /* the first copy from user and open interrupt for TX */
+ if (copy_from_user(ir_send->buf, buf, ir_send->buf_count)) {
+ ir_send->buf_count = 0;
+ spin_unlock(&ir_send->lock);
+ return -EFAULT;
+ }
+
+ ir_send->cur_buf_num = 0;
+
+ /* for safety, close RX while TX */
+ cir_update(CIR_IREN_TFU | CIR_IREN_TTR, CIR_IREN);
+
+ ir_send->send_state = ST_SEND_REPLY;
+
+ cir_update(CIR_FIFOCON_TX_TRIGGER_LEV_8 | CIR_FIFOCON_RXFIFOCLR, CIR_FIFOCON);
+ /* turn on TTR interrupt, it's ugly */
+ i = 0;
+ for (; i < 9; i++) {
+ cir_update(0x01, CIR_STXFIFO);
+ }
+ spin_unlock(&ir_send->lock);
+
+ wait_event(ir_send->queue, ir_send->send_state == ST_SEND_REQUEST);
+
+ spin_lock(&ir_send->lock);
+ ir_send->send_state = ST_SEND_NONE;
+ spin_unlock(&ir_send->lock);
+
+ cir_update(CIR_IREN_RDR | CIR_IREN_PE, CIR_IREN);
+
+ return ret;
+}
+
+static void w83667hg_clean_data(struct ir_recv_t *ir_recv, struct irctl *ir)
+{
+ spin_lock(&ir_recv->lock);
+ ir_recv->buf_count = 0;
+ /*//ir_recv->wake_state = ST_WAKE_NONE;*/
+ wake_up(&ir_recv->queue);
+ spin_unlock(&ir_recv->lock);
+
+ spin_lock(&ir->lock);
+ ir->lircdata = 0;
+ /*
+ for (i = 0; i < IRCTL_BUF_LIMIT; i++) {
+ ir->buf = 0;
+ }
+ */
+ ir->pressed_keycode = 0;
+ ir->pressed_shiftmask = 0;
+ ir->buf_count = 0;
+ ir->cur_buf_num = 0;
+ /*
+ ir->study_state = ST_STUDY_NONE;
+ ir->send_state = ST_SEND_NONE;
+ */
+ wake_up(&ir->queue);
+ spin_unlock(&ir->lock);
+
+ cir_update(cir_read(CIR_FIFOCON) | 0x88, CIR_FIFOCON);
+ cir_wake_update(cir_wake_read(CIR_WAKE_FIFOCON) | 0x8, CIR_WAKE_FIFOCON);
+
+}
+
+/* 3. */
+static void w83667hg_ir_recv_init(struct ir_recv_t *ir_recv)
+{
+ ir_recv->buf_count = 0;
+ spin_lock_init(&ir_recv->lock);
+ ir_recv->wake_state = ST_WAKE_NONE;
+ init_waitqueue_head(&ir_recv->queue);
+ ir_recv->recv_state = ST_RECV_WAIT_7F;
+}
+
+static void w83667hg_ir_send_init(struct ir_send_t *ir_send)
+{
+ ir_send->buf_count = 0;
+ spin_lock_init(&ir_send->lock);
+ ir_send->send_state = ST_SEND_NONE;
+ init_waitqueue_head(&ir_send->queue);
+}
+
+
+/* lirc_fops
+ *
+ * 1) it's LIRC's fops, so NOT allow add owner in it
+ * 2) define lirc functions at here will replace the lirc original fops functions, so lirc_write() in lirc_dev will not work
+ * 3) lirc has its own ioctl, lirc_ioctl() in current file not register in lirc_fops, so it only work for lirc_wb667
+ * lirc_ioctl() in current file register in w83667hg_irctl_init() as lirc_plugin's ioctl while initial driver, and it does not effect on lirc_dev
+ */
+static struct file_operations lirc_fops = {
+ write: lirc_write,
+ ioctl : lirc_ioctl,
+};
+
+
+/* init lirc buffer, register, irctl */
+static int w83667hg_irctl_init(struct irctl *ir)
+{
+ int err = 0, minor = -1;
+
+ w83667hg_lirc_buffer = kmalloc(sizeof(struct lirc_buffer), GFP_KERNEL);
+ if (!w83667hg_lirc_buffer) {
+ err = -ENOMEM;
+ goto out_lirc_buffer;
+ }
+ memset(w83667hg_lirc_buffer, 0, sizeof(struct lirc_buffer));
+
+ if (lirc_buffer_init(w83667hg_lirc_buffer, sizeof(lirc_t), LIRCBUF_SIZE)) {
+ err = -ENOMEM;
+ goto out_lirc_buffer;
+ }
+
+ w83667hg_lirc_plugin = kmalloc(sizeof(struct lirc_driver), GFP_KERNEL);
+ if (!w83667hg_lirc_plugin) {
+ err = -ENOMEM;
+ goto out_lirc_plugin;
+ }
+ memset(w83667hg_lirc_plugin, 0, sizeof(struct lirc_driver));
+
+ ir->lirc_plugin = w83667hg_lirc_plugin;
+
+ strcpy(w83667hg_lirc_plugin->name, DRVNAME);
+ w83667hg_lirc_plugin->minor = -1;
+ w83667hg_lirc_plugin->features = LIRC_CAN_SEND_PULSE |
+ LIRC_CAN_SET_TRANSMITTER_MASK |
+ LIRC_CAN_REC_MODE2;
+ /*// LIRC_CAN_SET_SEND_DUTY_CYCLE |
+ // LIRC_CAN_SET_SEND_CARRIER;*/
+ w83667hg_lirc_plugin->data = &w83667hg_irctl;
+ w83667hg_lirc_plugin->rbuf = w83667hg_lirc_buffer;
+ w83667hg_lirc_plugin->set_use_inc = &lirc_set_use_inc;
+ w83667hg_lirc_plugin->set_use_dec = &lirc_set_use_dec;
+ w83667hg_lirc_plugin->code_length = sizeof(lirc_t) * 8;
+/*// w83667hg_lirc_plugin->ioctl = lirc_ioctl;*/
+ w83667hg_lirc_plugin->fops = &lirc_fops;
+ w83667hg_lirc_plugin->dev = NULL;
+ w83667hg_lirc_plugin->owner = THIS_MODULE;
+
+ minor = lirc_register_driver(w83667hg_lirc_plugin);
+ if (minor < 0) {
+ err = -ENOMEM;
+ goto out_lirc_plugin_register;
+ }
+#ifdef IR_667_DEBUG
+ printk("%s register lirc minor: %d\n", DRVNAME, minor);
+#endif
+
+ w83667hg_lirc_plugin->minor = minor;
+ ir->lirc_plugin = w83667hg_lirc_plugin;
+
+ /* init irctl */
+ ir->pressed_keycode = 0;
+ ir->pressed_shiftmask = 0;
+ ir->buf_count = 0;
+ ir->ctrl_fix_head = 1;
+ spin_lock_init(&ir->lock);
+ ir->study_state = ST_STUDY_NONE;
+ init_waitqueue_head(&ir->queue);
+
+ goto out;
+
+ out_lirc_plugin_register:
+ kfree(w83667hg_lirc_plugin);
+ out_lirc_plugin:
+ lirc_buffer_free(w83667hg_lirc_buffer);
+ out_lirc_buffer:
+ kfree(w83667hg_lirc_buffer);
+ out:
+ return err;
+}
+
+static void w83667hg_irctl_uninit(struct irctl *ir)
+{
+ /*//lirc_unregister_plugin(w83667hg_lirc_plugin->minor);*/
+ lirc_unregister_driver(w83667hg_lirc_plugin->minor);
+ kfree(w83667hg_lirc_plugin);
+ ir->lirc_plugin = NULL;
+ lirc_buffer_free(w83667hg_lirc_buffer);
+ kfree(w83667hg_lirc_buffer);
+ ir->lirc_buffer = NULL;
+}
+
+#ifdef ALLOW_DEBUG_PRINT_PULSE
+void debug_print_pulse(void)
+{
+ u8 count, i;
+
+ count = cir_read(CIR_RXFCONT);
+ for (i = 0; i < count; i++) {
+ printk("%s current cir pluse: 0x%x\n", DRVNAME, cir_read(CIR_SRXFIFO));
+ }
+}
+
+void debug_print_wake_pulse(void)
+{
+ u8 count, i;
+
+ count = cir_wake_read(CIR_WAKE_FIFO_COUNT);
+ for (i = 0; i < count; i++) {
+ printk("%s current cir wake pluse: 0x%x\n", DRVNAME, cir_wake_read(CIR_WAKE_SRXFSTS));
+ }
+}
+#endif
+
+static void w83667hg_study_recv(struct ir_recv_t *ir_recv, struct irctl *ir)
+{
+ unsigned int i = 0;
+ unsigned int packet_on_dur = 0;
+
+ spin_lock(&ir_recv->lock);
+ ir_recv->buf[ir_recv->buf_count] = cir_read(CIR_SRXFIFO);
+ if (ir_recv->buf[ir_recv->buf_count] == 0x80) {
+ /* close interrupt now */
+ /*//cir_wake_update(0, CIR_WAKE_IREN);*/
+
+ spin_lock(&ir->lock);
+
+ /* 1. get carrier */
+ ir->carrier = cir_read(CIR_FCCL);
+ ir->carrier |= cir_read(CIR_FCCH) << 8;
+
+ if (ir->carrier == 0) {
+ printk("%s: get carrier error!\n", DRVNAME);
+ }
+
+ i = 0;
+ for (; i < ir_recv->buf_count; i++) {
+ if (ir_recv->buf[i] & BIT_PULSE_MASK) {
+ packet_on_dur += ir_recv->buf[i] & BIT_LEN;
+ }
+ }
+ packet_on_dur *= MCE_TIME_UNIT;
+
+#ifdef ALLOW_DEBUG_STUDY
+ printk("%s: carrier count: 0x%x\n", DRVNAME, ir->carrier);
+ printk("%s: packet on duration: %u\n", DRVNAME, packet_on_dur);
+#endif
+ ir->carrier *= 1000000;
+ ir->carrier /= packet_on_dur;
+#ifdef ALLOW_DEBUG_STUDY
+ printk("%s: final carrier frequency: %u\n", DRVNAME, ir->carrier);
+#endif
+ if ((ir->carrier > MAX_CARRIER) || (ir->carrier < MIN_CARRIER)) {
+ /* carrier is too large or too small */
+#ifdef ALLOW_DEBUG_STUDY
+ printk("%s: current received carrier is too large or too small\n", DRVNAME);
+#endif
+ ir_recv->buf_count = 0;
+ spin_unlock(&ir_recv->lock);
+
+ ir->buf_count = 0;
+ ir->study_state = ST_STUDY_ALL_RECV;
+ wake_up(&ir->queue);
+ spin_unlock(&ir->lock);
+ return;
+ }
+
+ /* 2. get pulse */
+ i = 0;
+ /* 1) find sync head */
+ while (!((ir_recv->buf[i] & BIT_PULSE_MASK) & 0x80)) {
+ i++;
+ }
+ /* 2) find 0x7f */
+ /* FIXME, the silent part of infrared signal may change by protocol or sample period. but current value fits MCE RC-6*/
+ while ((ir_recv->buf[i] != 0x7f) ||
+ (ir_recv->buf[i + 1] != 0x7f) ||
+ (ir_recv->buf[i + 2] != 0x7f)) {
+ i++;
+ }
+ /* 3) find next head */
+ while (!((ir_recv->buf[i] & BIT_PULSE_MASK) & 0x80)) {
+ i++;
+ }
+ /*//ir->buf_count = --i;*/
+ /* now buf_count direct to next pulse sync head */
+ ir->buf_count = i;
+ /* 4) copy pluse from recv to ir */
+ i = 0;
+ for (; i <= ir->buf_count; i++) {
+ ir->buf[i] = ir_recv->buf[i];
+ }
+
+
+ ir_recv->buf_count = 0;
+ spin_unlock(&ir_recv->lock);
+
+ ir->study_state = ST_STUDY_ALL_RECV;
+ wake_up(&ir->queue);
+ spin_unlock(&ir->lock);
+ } else {
+ ir_recv->buf_count++;
+ spin_unlock(&ir_recv->lock);
+ }
+}
+
+
+static void w83667hg_recv(struct ir_recv_t *ir_recv, struct irctl *ir)
+{
+ u8 buf;
+ unsigned int i = 0, rlc = 0;
+
+ spin_lock(&ir_recv->lock);
+
+ ir_recv->buf[ir_recv->buf_count] = cir_read(CIR_SRXFIFO);
+ if (ir_recv->buf[ir_recv->buf_count] == 0x7f) {
+ if (ir_recv->recv_state & ST_RECV_WAIT_7F) {
+ ir_recv->recv_state = ST_RECV_WAIT_HEAD;
+ ir_recv->buf_count++;
+ { /* decode begin*/
+ spin_lock(&ir->lock);
+
+ ir->buf_count = ir_recv->buf_count;
+ for (; i < ir->buf_count; i++) {
+ ir->buf[i] = ir_recv->buf[i];
+ }
+ ir->cur_buf_num = 0;
+
+ /*//ir_recv->buf_count++;*/
+ spin_unlock(&ir_recv->lock);
+
+ i = 0;
+ while (i < ir->buf_count) {
+ rlc += ir->buf[i] & BIT_LEN;
+ i++;
+ }
+#ifdef ALLOW_DEBUG_DECODE
+ printk("\n%s cur rlc len: %d\n", DRVNAME, rlc);
+#endif
+
+#ifdef DECODE_KEYBOARD_MOUSE
+ if (rlc >= CONTROLLER_BUF_LEN_MIN) {
+#endif
+ /* lirc controller*/
+ w83667hg_submit_controller(ir);
+
+#ifdef DECODE_KEYBOARD_MOUSE
+ } else if ((rlc >= KEYBOARD_BUF_LEN_MIN) &&
+ (rlc < KEYBOARD_BUF_LEN_MAX)) {
+ w83667hg_submit_key(ir);
+ input_sync(ir->input_dev);
+ } else if ((rlc >= MOUSE_BUF_LEN_MIN) &&
+ (rlc < KEYBOARD_BUF_LEN_MIN)) {
+ w83667hg_submit_mouse(ir);
+ input_sync(ir->input_dev);
+ }
+#endif
+
+ ir->buf_count = 0;
+ spin_unlock(&ir->lock);
+ } /* decode end*/
+ } else {
+ ir_recv->buf_count++;
+ spin_unlock(&ir_recv->lock);
+ }
+ } else { /* normal recv*/
+ if (ir_recv->recv_state & ST_RECV_WAIT_HEAD) {
+ if (ir_recv->buf[ir_recv->buf_count] & BIT_PULSE_MASK) {
+ ir_recv->recv_state = ST_RECV_WAIT_7F;
+ buf = ir_recv->buf[ir_recv->buf_count];
+ ir_recv->buf_count = 0;
+ ir_recv->buf[0] = buf;
+ }
+ }
+ ir_recv->buf_count++;
+ spin_unlock(&ir_recv->lock);
+ }
+}
+
+
+
+static void w83667hg_send_packet_to_lirc_1(struct irctl *ir, lirc_t *val)
+{
+#ifdef ALLOW_DEBUG_DECODE
+ printk("%s: send data to lirc : 0x%x\n", DRVNAME, (*val));
+#endif
+/*// lirc_buffer_write_1(ir->lirc_plugin->rbuf, (char *)val);*/
+ lirc_buffer_write(ir->lirc_plugin->rbuf, (char *)val);
+ wake_up(&ir->lirc_plugin->rbuf->wait_poll);
+}
+
+
+static void w83667hg_submit_controller(struct irctl *ir)
+{
+ unsigned int buf_num;
+ u8 bit;
+
+ static struct timeval last_time;
+ static bool is_not_initialed_yet = true; /* lirc group does not allow to initialize static variable to false(0) */
+ struct timeval curr_time;
+ long duration;
+
+ /* silence time */
+
+ ir->lircdata = 0;
+
+ if (is_not_initialed_yet == true) {
+ duration = MAX_SILENCE_TIME;
+ is_not_initialed_yet = false;
+ } else{
+ do_gettimeofday(&curr_time);
+ duration = (curr_time.tv_usec - last_time.tv_usec) +
+ (curr_time.tv_sec - last_time.tv_sec) * 1000000;
+ }
+
+ if (duration >= MAX_SILENCE_TIME) {
+ ir->lircdata = MAX_SILENCE_TIME;
+ } else{
+ ir->lircdata = duration;
+ }
+ w83667hg_send_packet_to_lirc_1(ir, &ir->lircdata);
+
+
+ /* fix controller head not sync problem */
+ /* lirc doesn't response DK MCE controller signal until press several times, not find same problem on M$ MCE controller keyboard */
+ /* for lirc group's suggestion, we need not add these code to prevent from this problem.
+ Just make it as comment. If we do not get any problem report from our customer, we will remove it at next some version.
+ if (ir->ctrl_fix_head) {
+ ir->lircdata = 50000;
+ w83667hg_send_packet_to_lirc_1(ir, &ir->lircdata);
+ ir->ctrl_fix_head = 0;
+ }
+ */
+
+
+ buf_num = 0;
+ bit = BIT_PULSE_MASK;
+ ir->lircdata = 0;
+ for (; buf_num < ir->buf_count; buf_num++) {
+ if (bit == (ir->buf[buf_num] & BIT_PULSE_MASK)) {
+ ir->lircdata += (ir->buf[buf_num] & BIT_LEN) * MCE_TIME_UNIT;
+ } else {
+ if (bit) {
+ ir->lircdata |= PULSE_BIT;
+ }
+ w83667hg_send_packet_to_lirc_1(ir, &ir->lircdata);
+ bit = ir->buf[buf_num] & BIT_PULSE_MASK;
+ ir->lircdata = (ir->buf[buf_num] & BIT_LEN) * MCE_TIME_UNIT;
+ }
+ }
+
+ /* update last_time for measure silence time*/
+ do_gettimeofday(&last_time);
+
+ /* for final silent pulse */
+ /*ir->lircdata = 50000;
+ w83667hg_send_packet_to_lirc_1(ir, &ir->lircdata);*/
+
+#ifdef ALLOW_DEBUG_DECODE
+ printk("\n");
+#endif
+}
+
+static irqreturn_t w83667hg_interrupt_handler(int irq, void *dev)
+{
+ u8 tmp = 0;
+ struct irctl *ir = (struct irctl *)dev;
+ struct ir_send_t *ir_send = &w83667hg_ir_send;
+
+
+ /*Because interrupt is shared, check IREN first. */
+ tmp = cir_read(CIR_IREN);
+ if (!tmp) {
+ return IRQ_RETVAL(IRQ_NONE);
+ }
+ tmp = cir_read(CIR_IRSTS);
+ cir_update(0xff, CIR_IRSTS);
+ if (!tmp) {
+ return IRQ_NONE;
+ }
+ if (tmp & CIR_IRSTS_RDR) {
+
+
+#ifdef ALLOW_DEBUG_INTERRUPT
+ printk("get CIR_IRSTS_RDR\n");
+#endif
+#ifdef ALLOW_DEBUG_PRINT_PULSE
+ debug_print_pulse();
+#else
+ spin_lock(&ir_send->lock);
+ if (ir_send->send_state == ST_SEND_NONE) {
+ spin_unlock(&ir_send->lock);
+ if (ir->study_state & ST_STUDY_START) {
+ w83667hg_study_recv(&w83667hg_ir_recv, &w83667hg_irctl);
+ } else {
+ w83667hg_recv(&w83667hg_ir_recv, &w83667hg_irctl);
+ }
+ } else {
+ spin_unlock(&ir_send->lock);
+ }
+#endif
+ }
+ if (tmp & CIR_IRSTS_RTR) {
+#ifdef ALLOW_DEBUG_INTERRUPT
+ printk("get CIR_IRSTS_RTR\n");
+#endif
+ }
+ if (tmp & CIR_IRSTS_PE) {
+#ifdef ALLOW_DEBUG_INTERRUPT
+ printk("get CIR_IRSTS_PE\n");
+#endif
+#ifdef ALLOW_DEBUG_PRINT_PULSE
+ printk("\n now get interrupt PE\n\n");
+ debug_print_pulse();
+#else
+ if (ir->study_state == ST_STUDY_NONE) {
+ w83667hg_clean_data(&w83667hg_ir_recv, &w83667hg_irctl);
+ }
+#endif
+ }
+ if (tmp & CIR_IRSTS_RFO) {
+#ifdef ALLOW_DEBUG_INTERRUPT
+ printk("get CIR_IRSTS_RFO\n");
+#endif
+ }
+ if (tmp & CIR_IRSTS_TE) {
+#ifdef ALLOW_DEBUG_INTERRUPT
+ printk("get CIR_IRSTS_TE\n");
+#endif
+ }
+ if (tmp & CIR_IRSTS_TTR) {
+#ifdef ALLOW_DEBUG_INTERRUPT
+ printk("get CIR_IRSTS_TTR\n");
+#endif
+
+ spin_lock(&ir_send->lock);
+ if (ir_send->cur_buf_num < ir_send->buf_count) {
+ cir_update(ir_send->buf[ir_send->cur_buf_num++], CIR_STXFIFO);
+ } else {
+ cir_update(cir_read(CIR_IREN) & (~CIR_IREN_TTR), CIR_IREN);
+ }
+ spin_unlock(&ir_send->lock);
+
+ }
+ if (tmp & CIR_IRSTS_TFU) {
+#ifdef ALLOW_DEBUG_INTERRUPT
+ printk("get CIR_IRSTS_TFU\n");
+#endif
+ spin_lock(&ir_send->lock);
+ if (ST_SEND_REPLY == ir_send->send_state) {
+ ir_send->send_state = ST_SEND_REQUEST;
+ wake_up(&ir_send->queue);
+ }
+ spin_unlock(&ir_send->lock);
+ }
+ if (tmp & CIR_IRSTS_GH) {
+#ifdef ALLOW_DEBUG_INTERRUPT
+ printk("get CIR_IRSTS_GH\n");
+#endif
+ }
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t w83667hg_wake_interrupt_handler(int irq, void *dev)
+{
+ u8 tmp;
+ struct ir_recv_t *ir_recv = (struct ir_recv_t *)dev;
+
+
+ /*Because interrupt is shared, check IREN first. */
+ tmp = cir_wake_read(CIR_WAKE_IREN);
+ if (!tmp) {
+ return IRQ_RETVAL(IRQ_NONE);
+ }
+
+ tmp = cir_wake_read(CIR_WAKE_IRSTS);
+ if (!tmp) {
+ return IRQ_NONE;
+ }
+ cir_wake_update(0xff, CIR_WAKE_IRSTS);
+
+ if (tmp & CIR_WAKE_IRSTS_RDR) {
+#ifdef ALLOW_DEBUG_INTERRUPT
+ printk("get CIR_WAKE_IRSTS_RDR\n");
+#endif
+#ifdef ALLOW_DEBUG_PRINT_PULSE
+ debug_print_wake_pulse();
+#endif
+ }
+ if (tmp & CIR_WAKE_IRSTS_RTR) {
+#ifdef ALLOW_DEBUG_INTERRUPT
+ printk("get CIR_WAKE_IRSTS_RTR\n");
+#endif
+ }
+ if ((tmp & CIR_WAKE_IRSTS_PE) &&
+ (ST_WAKE_START == ir_recv->wake_state)) {
+#ifdef ALLOW_DEBUG_INTERRUPT
+ printk("get CIR_WAKE_IRSTS_PE\n");
+#endif
+#ifdef ALLOW_DEBUG_PRINT_PULSE
+ printk("\n now get interrupt PE\n\n");
+ debug_print_wake_pulse();
+#else
+ while ((cir_wake_read(CIR_WAKE_RD_FIFO_ONLY_IDX)) != 0) {
+#ifdef ALLOW_DEBUG_WAKE
+ printk("%s setting wake up key: 0x%x\n", DRVNAME, cir_wake_read(CIR_WAKE_RD_FIFO_ONLY));
+#else
+ cir_wake_read(CIR_WAKE_RD_FIFO_ONLY);
+#endif
+ }
+
+ cir_wake_update(0, CIR_WAKE_IREN);
+ spin_lock(&ir_recv->lock);
+ ir_recv->wake_state = ST_WAKE_FINISH;
+ wake_up(&ir_recv->queue);
+ spin_unlock(&ir_recv->lock);
+#endif
+ }
+ if (tmp & CIR_WAKE_IRSTS_RFO) {
+#ifdef ALLOW_DEBUG_INTERRUPT
+ printk("get CIR_WAKE_IRSTS_RFO\n");
+#endif
+ }
+ if (tmp & CIR_WAKE_IRSTS_GH) {
+#ifdef ALLOW_DEBUG_INTERRUPT
+ printk("get CIR_WAKE_IRSTS_GH\n");
+#endif
+ }
+
+ return IRQ_HANDLED;
+}
+
+/* 4. */
+/* init 667 cir dev, req_region, req_irq */
+static int w83667hg_cir_probe(void)
+{
+ int err = 0;
+
+ if (!request_region(cir_address, CIR_IOREG_LENGTH, DRVNAME)) {
+ err = -EBUSY;
+#ifdef IR_667_DEBUG
+ printk("%s request 667 cir io port error! \n", DRVNAME);
+#endif
+ goto exit;
+ }
+
+ cir_update(CIR_IRCON_RXINV | CIR_IRCON_SAMPLE_PERIOD_SEL, CIR_IRCON);
+ cir_update(0xFF, CIR_IRSTS);
+ cir_update(CIR_RX_LIMIT_COUNT >> 8, CIR_SLCH);
+ cir_update(CIR_RX_LIMIT_COUNT & 0xff, CIR_SLCL);
+ cir_update(CIR_FIFOCON_TXFIFOCLR | CIR_FIFOCON_TX_TRIGGER_LEV_24 | CIR_FIFOCON_RXFIFOCLR, CIR_FIFOCON);
+ cir_update(CIR_IRCON_RECV | CIR_IRCON_RXINV | CIR_IRCON_SAMPLE_PERIOD_SEL, CIR_IRCON);
+ cir_update(CIR_IRCON_TXEN | CIR_IRCON_RXEN | CIR_IRCON_RXINV | CIR_IRCON_SAMPLE_PERIOD_SEL, CIR_IRCON);
+
+ if (0 != request_irq(CIR_IRQ_NUM, w83667hg_interrupt_handler, IRQF_SHARED,
+ DRVNAME, &w83667hg_irctl)) {
+ err = -EINTR;
+#ifdef IR_667_DEBUG
+ printk("%s : request cir irq error\n", DRVNAME);
+#endif
+ goto rel_irq_exit;
+ }
+ /* open interrupt */
+ cir_update(CIR_IREN_RDR | CIR_IREN_PE, CIR_IREN);
+
+ if (!request_region(cir_wake_address, CIR_IOREG_LENGTH, DRVNAME)) {
+ err = -EBUSY;
+#ifdef IR_667_DEBUG
+ printk("%s request 667 cir wake io port error! \n", DRVNAME);
+#endif
+ goto rel_wake_exit;
+ }
+
+ cir_wake_update(0xff, CIR_WAKE_IRSTS);
+ /* Modify it as more safe values: CIR_WAKE_FIFO_CMP_DEEP reg:0x41,
+ CIR_WAKE_FIFO_CMP_TOL reg: 0x05. 20091224
+ */
+ cir_wake_update(0x41, CIR_WAKE_FIFO_CMP_DEEP); /* 0x41 = 65 */
+ cir_wake_update(0x05, CIR_WAKE_FIFO_CMP_TOL);
+ cir_wake_update(CIR_RX_LIMIT_COUNT >> 8, CIR_WAKE_SLCH);
+ cir_wake_update(CIR_RX_LIMIT_COUNT & 0xff, CIR_WAKE_SLCL);
+ cir_wake_update(0xff, CIR_WAKE_FIFOCON);
+ cir_wake_update(0, CIR_WAKE_FIFOCON);
+ cir_wake_update(CIR_WAKE_IRCON_MODE0 | CIR_WAKE_IRCON_RXEN | CIR_WAKE_IRCON_R | CIR_WAKE_IRCON_RXINV | CIR_WAKE_IRCON_SAMPLE_PERIOD_SEL, CIR_WAKE_IRCON);
+
+ /* cir wake has irq_handler, open interrupt after received ioctl IR_IOLEARNWAKE */
+ if (0 != request_irq(CIR_WAKE_IRQ_NUM, w83667hg_wake_interrupt_handler, IRQF_SHARED,
+ DRVNAME, &w83667hg_ir_recv)) {
+ err = -EINTR;
+#ifdef IR_667_DEBUG
+ printk("%s : request cir wake irq error\n", DRVNAME);
+#endif
+ goto rel_wake_irq_exit;
+ }
+
+#ifdef IR_667_DEBUG
+ printk("%s : init cir success\n", DRVNAME);
+#endif
+ goto exit;
+
+ /* error exit */
+ rel_wake_irq_exit:
+ release_region(cir_wake_address, CIR_IOREG_LENGTH);
+ rel_wake_exit:
+ cir_update(0, CIR_IREN);
+ free_irq(CIR_IRQ_NUM, &w83667hg_irctl);
+ rel_irq_exit:
+ release_region(cir_address, CIR_IOREG_LENGTH);
+
+ /* final exit */
+ exit:
+ return err;
+}
+
+static void w83667hg_cir_remove(void)
+{
+ /* Don't clean CIR_WAKE_IRCON. When wake-up at power-on, it needs the function. 20091224
+ cir_wake_update(0, CIR_WAKE_IRCON);
+ */
+ free_irq(CIR_WAKE_IRQ_NUM, &w83667hg_ir_recv);
+ release_region(cir_wake_address, CIR_IOREG_LENGTH);
+ cir_update(0, CIR_IRCON);
+ free_irq(CIR_IRQ_NUM, &w83667hg_irctl);
+ release_region(cir_address, CIR_IOREG_LENGTH);
+}
+
+static int lirc_wb667_init(void)
+{
+ int err = 0;
+
+ /* Initialise global static variables here instead of at declaration becuase of
+ lirc group does not allow it.*/
+#ifdef CONFIG_PNP
+ nopnp = 0;
+#else
+ nopnp = 1;
+#endif
+ w83667hg_input_dev = NULL;
+ w83667hg_lirc_plugin = NULL;
+ w83667hg_lirc_buffer = NULL;
+
+ /* 1. init cr */
+ if (w83667hg_cr_init()) {
+ printk("%s: Unable to init device.\n", DRVNAME);
+ err = -ENODEV;
+ goto out;
+ }
+
+ /* 2. init input */
+ w83667hg_input_dev = w83667hg_input_init();
+ if (!w83667hg_input_dev) {
+ printk("%s: Unable to register input device.\n", DRVNAME);
+ err = -ENODEV;
+ goto out_input;
+ }
+ w83667hg_irctl.input_dev = w83667hg_input_dev;
+
+ /* 3. init lirc buffer, register, irctl */
+ w83667hg_ir_recv_init(&w83667hg_ir_recv);
+ w83667hg_ir_send_init(&w83667hg_ir_send);
+ err = w83667hg_irctl_init(&w83667hg_irctl);
+ if (err) {
+ printk("%s: Unable to register lirc.\n", DRVNAME);
+ goto out_irctl;
+ }
+
+
+ /* 4. init 667 cir dev, req_region, req_irq */
+ err = w83667hg_cir_probe();
+ if (err) {
+ printk("%s: Unable to probe cir device.\n", DRVNAME);
+ goto out_cir_probe;
+ }
+
+ goto out;
+
+ /* error exit */
+ out_cir_probe:
+ w83667hg_irctl_uninit(&w83667hg_irctl);
+ w83667hg_irctl.input_dev = NULL;
+ out_irctl:
+ w83667hg_input_uninit(w83667hg_input_dev);
+ out_input:
+ w83667hg_cr_uninit();
+
+ /* final exit */
+ out:
+ return err;
+}
+
+void lirc_wb667_uninit(void)
+{
+ w83667hg_cir_remove();
+ w83667hg_irctl_uninit(&w83667hg_irctl);
+ w83667hg_irctl.input_dev = NULL;
+ w83667hg_input_uninit(w83667hg_input_dev);
+ w83667hg_cr_uninit();
+
+}
+
+
+
+/* For resume function use only. 20100119*/
+static void w83667hg_irctl_resume_reset(struct irctl *ir)
+{
+ /* init irctl */
+ ir->pressed_keycode = 0;
+ ir->pressed_shiftmask = 0;
+ ir->buf_count = 0;
+ ir->ctrl_fix_head = 1;
+ spin_lock_init(&ir->lock);
+ ir->study_state = ST_STUDY_NONE;
+ init_waitqueue_head(&ir->queue);
+}
+
+
+/* For resume function use only. 20100119*/
+static int w83667hg_cir_resume_reset(void)
+{
+ int err = 0;
+
+ cir_update(CIR_IRCON_RXINV | CIR_IRCON_SAMPLE_PERIOD_SEL, CIR_IRCON);
+ cir_update(0xFF, CIR_IRSTS);
+ cir_update(CIR_RX_LIMIT_COUNT >> 8, CIR_SLCH);
+ cir_update(CIR_RX_LIMIT_COUNT & 0xff, CIR_SLCL);
+ cir_update(CIR_FIFOCON_TXFIFOCLR | CIR_FIFOCON_TX_TRIGGER_LEV_24 | CIR_FIFOCON_RXFIFOCLR, CIR_FIFOCON);
+ cir_update(CIR_IRCON_RECV | CIR_IRCON_RXINV | CIR_IRCON_SAMPLE_PERIOD_SEL, CIR_IRCON);
+ cir_update(CIR_IRCON_TXEN | CIR_IRCON_RXEN | CIR_IRCON_RXINV | CIR_IRCON_SAMPLE_PERIOD_SEL, CIR_IRCON);
+
+ /* open interrupt */
+ cir_update(CIR_IREN_RDR | CIR_IREN_PE, CIR_IREN);
+
+
+ cir_wake_update(0xff, CIR_WAKE_IRSTS);
+
+ cir_wake_update(0x41, CIR_WAKE_FIFO_CMP_DEEP); /* 0x41 = 65 */
+ cir_wake_update(0x05, CIR_WAKE_FIFO_CMP_TOL);
+ cir_wake_update(CIR_RX_LIMIT_COUNT >> 8, CIR_WAKE_SLCH);
+ cir_wake_update(CIR_RX_LIMIT_COUNT & 0xff, CIR_WAKE_SLCL);
+ cir_wake_update(0xff, CIR_WAKE_FIFOCON);
+ cir_wake_update(0, CIR_WAKE_FIFOCON);
+ cir_wake_update(CIR_WAKE_IRCON_MODE0 | CIR_WAKE_IRCON_RXEN | CIR_WAKE_IRCON_R | CIR_WAKE_IRCON_RXINV | CIR_WAKE_IRCON_SAMPLE_PERIOD_SEL, CIR_WAKE_IRCON);
+
+
+#ifdef IR_667_DEBUG
+ printk("%s : cir_resume_reset finish\n", DRVNAME);
+#endif
+
+ return err;
+}
+
+
+/* For resume function use only. 20100119*/
+static int lirc_wb667_resume_init(void)
+{
+ int err = 0;
+
+ /* init lirc buffer */
+ w83667hg_ir_recv_init(&w83667hg_ir_recv);
+ w83667hg_ir_send_init(&w83667hg_ir_send);
+
+ /* reset variables in irctl, but no re-locate and no re-register 20100119*/
+ w83667hg_irctl_resume_reset(&w83667hg_irctl);
+
+
+ /* init 667 cir dev */
+
+ w83667hg_cir_resume_reset();
+
+
+ return err;
+}
+
+
+
+#ifdef CONFIG_PNP
+
+/* CIR and CIR WAKE */
+
+static struct pnp_driver lirc_wb667_pnp_driver = {
+ .name = PLATNAME,
+ .id_table = pnp_dev_table,
+ .probe = lirc_wb667_pnp_probe,
+ .remove = __devexit_p(lirc_wb667_pnp_remove),
+ .suspend = lirc_wb667_pnp_suspend,
+ .resume = lirc_wb667_pnp_resume,
+};
+
+static int __devinit lirc_wb667_pnp_probe(struct pnp_dev *dev, const struct pnp_device_id *dev_id)
+{
+ /* CIR */
+
+ #ifdef IR_667_DEBUG
+ printk("%s receive probe\n", DRVNAME);
+ #endif
+
+ if (!pnp_port_valid(dev, 0))
+ return -ENODEV;
+
+
+ CIR_BASE = (unsigned int)pnp_port_start(dev, 0);
+ CIR_IRQ_NUM = (unsigned short)pnp_irq(dev, 0);
+
+
+ /* CIR WAKE*/
+ if (!pnp_port_valid(dev, 1))
+ return -ENODEV;
+ CIR_WAKE_BASE = (unsigned int)pnp_port_start(dev, 1);
+ CIR_WAKE_IRQ_NUM = (unsigned short)pnp_irq(dev, 0); /* share the same irq with CIR device.*/
+
+ return 0;
+}
+
+static void __devexit lirc_wb667_pnp_remove(struct pnp_dev *dev)
+{
+}
+
+
+
+static int lirc_wb667_pnp_suspend(struct pnp_dev *dev, pm_message_t state)
+{
+ struct irctl *ir = &w83667hg_irctl;
+ struct ir_recv_t *ir_recv = &w83667hg_ir_recv;
+ struct ir_send_t *ir_send = &w83667hg_ir_send;
+
+#ifdef IR_667_DEBUG
+ printk("%s receive suspend\n", DRVNAME);
+#endif
+
+ /* 1. */
+ spin_lock(&ir->lock);
+ ir->study_state = ST_STUDY_NONE;
+ spin_unlock(&ir->lock);
+
+ spin_lock(&ir_recv->lock);
+ ir_recv->wake_state = ST_WAKE_NONE;
+ spin_unlock(&ir_recv->lock);
+
+ spin_lock(&ir_send->lock);
+ ir_send->send_state = ST_SEND_NONE;
+ spin_unlock(&ir_send->lock);
+
+ /* 2. */
+ cir_update(0, CIR_IREN);
+ cir_wake_update(0, CIR_WAKE_IREN);
+
+
+ /* 3. */
+ cr_enter_ext();
+ cr_select_log_dev(CIR_LOG_DEV);
+ cr_update(DEACTIVE_LOG_DEV, 0x30);
+
+ /*Don't close CIR Wake. When wake-up at power-on, it needs the function. 20091224
+ cr_select_log_dev(CIR_WAKE_LOG_DEV);
+ cr_update(DEACTIVE_LOG_DEV, 0x30);
+ */
+
+ cr_exit_ext();
+
+
+ return 0;
+}
+
+static int lirc_wb667_pnp_resume(struct pnp_dev *dev)
+{
+ int ret = 0;
+
+#ifdef IR_667_DEBUG
+ printk("%s receive resume\n", DRVNAME);
+#endif
+
+
+ /* open interrupt */
+ cir_update(CIR_IREN_RDR | CIR_IREN_PE, CIR_IREN);
+
+
+ /* Enable CIR logical device */
+ cr_enter_ext();
+ cr_select_log_dev(CIR_LOG_DEV);
+ cr_update(ACTIVE_LOG_DEV, 0x30);
+
+
+ /*Don't close CIR Wake. When wake-up at power-on, it needs the function. 20091224
+ cr_select_log_dev(CIR_WAKE_LOG_DEV);
+ cr_update(ACTIVE_LOG_DEV, 0x30);
+ */
+ cr_exit_ext();
+
+ /* special uninit function for resume only. 20100119 */
+ lirc_wb667_resume_init();
+
+ return ret;
+}
+
+
+
+
+
+
+#endif
+
+
+/*//int init_module(void)*/
+int init_module_wb667(void)
+{
+ int ret;
+
+ if (nopnp) {
+ printk("%s does not support Non-PNP kernel now.\n", DRVNAME);
+ }
+
+#ifdef CONFIG_PNP
+ if (!nopnp) {
+
+ ret = pnp_register_driver(&lirc_wb667_pnp_driver);
+ if (ret < 0)
+ return ret;
+
+
+
+ }
+#endif /* CONFIG_PNP */
+
+
+
+
+ ret = lirc_wb667_init();
+ if (ret) {
+ return ret;
+ }
+
+ return 0;
+}
+
+/*//void cleanup_module(void)*/
+void cleanup_module_wb667(void)
+{
+
+#ifdef CONFIG_PNP
+ if (!nopnp) {
+ pnp_unregister_driver(&lirc_wb667_pnp_driver);
+
+ }
+#endif /* CONFIG_PNP */
+
+
+ lirc_wb667_uninit();
+}
+
+module_init(init_module_wb667);
+module_exit(cleanup_module_wb667);
+
+