📄 ir-kbd-gpio.c
字号:
static void ir_timer(unsigned long data){ struct IR *ir = (struct IR*)data; schedule_work(&ir->work);}static void ir_work(void *data){ struct IR *ir = data; unsigned long timeout; ir_handle_key(ir); timeout = jiffies + (ir->polling * HZ / 1000); mod_timer(&ir->timer, timeout);}/* ---------------------------------------------------------------*/static int rc5_remote_gap = 885;module_param(rc5_remote_gap, int, 0644);static int rc5_key_timeout = 200;module_param(rc5_key_timeout, int, 0644);#define RC5_START(x) (((x)>>12)&3)#define RC5_TOGGLE(x) (((x)>>11)&1)#define RC5_ADDR(x) (((x)>>6)&31)#define RC5_INSTR(x) ((x)&63)/* decode raw bit pattern to RC5 code */static u32 rc5_decode(unsigned int code){ unsigned int org_code = code; unsigned int pair; unsigned int rc5 = 0; int i; code = (code << 1) | 1; for (i = 0; i < 14; ++i) { pair = code & 0x3; code >>= 2; rc5 <<= 1; switch (pair) { case 0: case 2: break; case 1: rc5 |= 1; break; case 3: dprintk("bad code: %x\n", org_code); return 0; } } dprintk("code=%x, rc5=%x, start=%x, toggle=%x, address=%x, " "instr=%x\n", rc5, org_code, RC5_START(rc5), RC5_TOGGLE(rc5), RC5_ADDR(rc5), RC5_INSTR(rc5)); return rc5;}static int ir_rc5_irq(struct bttv_sub_device *sub){ struct IR *ir = dev_get_drvdata(&sub->dev); struct timeval tv; u32 gpio; u32 gap; unsigned long current_jiffies, timeout; /* read gpio port */ gpio = bttv_gpio_read(ir->sub->core); /* remote IRQ? */ if (!(gpio & 0x20)) return 0; /* get time of bit */ current_jiffies = jiffies; do_gettimeofday(&tv); /* avoid overflow with gap >1s */ if (tv.tv_sec - ir->base_time.tv_sec > 1) { gap = 200000; } else { gap = 1000000 * (tv.tv_sec - ir->base_time.tv_sec) + tv.tv_usec - ir->base_time.tv_usec; } /* active code => add bit */ if (ir->active) { /* only if in the code (otherwise spurious IRQ or timer late) */ if (ir->last_bit < 28) { ir->last_bit = (gap - rc5_remote_gap / 2) / rc5_remote_gap; ir->code |= 1 << ir->last_bit; } /* starting new code */ } else { ir->active = 1; ir->code = 0; ir->base_time = tv; ir->last_bit = 0; timeout = current_jiffies + (500 + 30 * HZ) / 1000; mod_timer(&ir->timer_end, timeout); } /* toggle GPIO pin 4 to reset the irq */ bttv_gpio_write(ir->sub->core, gpio & ~(1 << 4)); bttv_gpio_write(ir->sub->core, gpio | (1 << 4)); return 1;}static void ir_rc5_timer_end(unsigned long data){ struct IR *ir = (struct IR *)data; struct timeval tv; unsigned long current_jiffies, timeout; u32 gap; /* get time */ current_jiffies = jiffies; do_gettimeofday(&tv); /* avoid overflow with gap >1s */ if (tv.tv_sec - ir->base_time.tv_sec > 1) { gap = 200000; } else { gap = 1000000 * (tv.tv_sec - ir->base_time.tv_sec) + tv.tv_usec - ir->base_time.tv_usec; } /* Allow some timmer jitter (RC5 is ~24ms anyway so this is ok) */ if (gap < 28000) { dprintk("spurious timer_end\n"); return; } ir->active = 0; if (ir->last_bit < 20) { /* ignore spurious codes (caused by light/other remotes) */ dprintk("short code: %x\n", ir->code); } else { u32 rc5 = rc5_decode(ir->code); /* two start bits? */ if (RC5_START(rc5) != 3) { dprintk("rc5 start bits invalid: %u\n", RC5_START(rc5)); /* right address? */ } else if (RC5_ADDR(rc5) == 0x0) { u32 toggle = RC5_TOGGLE(rc5); u32 instr = RC5_INSTR(rc5); /* Good code, decide if repeat/repress */ if (toggle != RC5_TOGGLE(ir->last_rc5) || instr != RC5_INSTR(ir->last_rc5)) { dprintk("instruction %x, toggle %x\n", instr, toggle); ir_input_nokey(ir->input, &ir->ir); ir_input_keydown(ir->input, &ir->ir, instr, instr); } /* Set/reset key-up timer */ timeout = current_jiffies + (500 + rc5_key_timeout * HZ) / 1000; mod_timer(&ir->timer_keyup, timeout); /* Save code for repeat test */ ir->last_rc5 = rc5; } }}static void ir_rc5_timer_keyup(unsigned long data){ struct IR *ir = (struct IR *)data; dprintk("key released\n"); ir_input_nokey(ir->input, &ir->ir);}/* ---------------------------------------------------------------------- */static int ir_probe(struct device *dev){ struct bttv_sub_device *sub = to_bttv_sub_dev(dev); struct IR *ir; struct input_dev *input_dev; IR_KEYTAB_TYPE *ir_codes = NULL; int ir_type = IR_TYPE_OTHER; ir = kzalloc(sizeof(*ir), GFP_KERNEL); input_dev = input_allocate_device(); if (!ir || !input_dev) { kfree(ir); input_free_device(input_dev); return -ENOMEM; } /* detect & configure */ switch (sub->core->type) { case BTTV_BOARD_AVERMEDIA: case BTTV_BOARD_AVPHONE98: case BTTV_BOARD_AVERMEDIA98: ir_codes = ir_codes_avermedia; ir->mask_keycode = 0xf88000; ir->mask_keydown = 0x010000; ir->polling = 50; // ms break; case BTTV_BOARD_AVDVBT_761: case BTTV_BOARD_AVDVBT_771: ir_codes = ir_codes_avermedia_dvbt; ir->mask_keycode = 0x0f00c0; ir->mask_keydown = 0x000020; ir->polling = 50; // ms break; case BTTV_BOARD_PXELVWPLTVPAK: ir_codes = ir_codes_pixelview; ir->mask_keycode = 0x003e00; ir->mask_keyup = 0x010000; ir->polling = 50; // ms break; case BTTV_BOARD_PV_BT878P_9B: case BTTV_BOARD_PV_BT878P_PLUS: ir_codes = ir_codes_pixelview; ir->mask_keycode = 0x001f00; ir->mask_keyup = 0x008000; ir->polling = 50; // ms break; case BTTV_BOARD_WINFAST2000: ir_codes = ir_codes_winfast; ir->mask_keycode = 0x1f8; break; case BTTV_BOARD_MAGICTVIEW061: case BTTV_BOARD_MAGICTVIEW063: ir_codes = ir_codes_winfast; ir->mask_keycode = 0x0008e000; ir->mask_keydown = 0x00200000; break; case BTTV_BOARD_APAC_VIEWCOMP: ir_codes = ir_codes_apac_viewcomp; ir->mask_keycode = 0x001f00; ir->mask_keyup = 0x008000; ir->polling = 50; // ms break; case BTTV_BOARD_CONCEPTRONIC_CTVFMI2: ir_codes = ir_codes_conceptronic; ir->mask_keycode = 0x001F00; ir->mask_keyup = 0x006000; ir->polling = 50; // ms break; case BTTV_BOARD_NEBULA_DIGITV: ir_codes = ir_codes_nebula; driver.any_irq = ir_rc5_irq; driver.gpio_irq = NULL; ir->rc5_gpio = 1; break; } if (NULL == ir_codes) { kfree(ir); input_free_device(input_dev); return -ENODEV; } if (ir->rc5_gpio) { u32 gpio; /* enable remote irq */ bttv_gpio_inout(sub->core, (1 << 4), 1 << 4); gpio = bttv_gpio_read(sub->core); bttv_gpio_write(sub->core, gpio & ~(1 << 4)); bttv_gpio_write(sub->core, gpio | (1 << 4)); } else { /* init hardware-specific stuff */ bttv_gpio_inout(sub->core, ir->mask_keycode | ir->mask_keydown, 0); } /* init input device */ snprintf(ir->name, sizeof(ir->name), "bttv IR (card=%d)", sub->core->type); snprintf(ir->phys, sizeof(ir->phys), "pci-%s/ir0", pci_name(sub->core->pci)); ir_input_init(input_dev, &ir->ir, ir_type, ir_codes); input_dev->name = ir->name; input_dev->phys = ir->phys; input_dev->id.bustype = BUS_PCI; input_dev->id.version = 1; if (sub->core->pci->subsystem_vendor) { input_dev->id.vendor = sub->core->pci->subsystem_vendor; input_dev->id.product = sub->core->pci->subsystem_device; } else { input_dev->id.vendor = sub->core->pci->vendor; input_dev->id.product = sub->core->pci->device; } input_dev->cdev.dev = &sub->core->pci->dev; ir->input = input_dev; ir->sub = sub; if (ir->polling) { INIT_WORK(&ir->work, ir_work, ir); init_timer(&ir->timer); ir->timer.function = ir_timer; ir->timer.data = (unsigned long)ir; schedule_work(&ir->work); } else if (ir->rc5_gpio) { /* set timer_end for code completion */ init_timer(&ir->timer_end); ir->timer_end.function = ir_rc5_timer_end; ir->timer_end.data = (unsigned long)ir; init_timer(&ir->timer_keyup); ir->timer_keyup.function = ir_rc5_timer_keyup; ir->timer_keyup.data = (unsigned long)ir; } /* all done */ dev_set_drvdata(dev, ir); input_register_device(ir->input); /* the remote isn't as bouncy as a keyboard */ ir->input->rep[REP_DELAY] = repeat_delay; ir->input->rep[REP_PERIOD] = repeat_period; return 0;}static int ir_remove(struct device *dev){ struct IR *ir = dev_get_drvdata(dev); if (ir->polling) { del_timer(&ir->timer); flush_scheduled_work(); } if (ir->rc5_gpio) { u32 gpio; del_timer(&ir->timer_end); flush_scheduled_work(); gpio = bttv_gpio_read(ir->sub->core); bttv_gpio_write(ir->sub->core, gpio & ~(1 << 4)); } input_unregister_device(ir->input); kfree(ir); return 0;}/* ---------------------------------------------------------------------- */MODULE_AUTHOR("Gerd Knorr, Pavel Machek");MODULE_DESCRIPTION("input driver for bt8x8 gpio IR remote controls");MODULE_LICENSE("GPL");static int ir_init(void){ return bttv_sub_register(&driver, "remote");}static void ir_fini(void){ bttv_sub_unregister(&driver);}module_init(ir_init);module_exit(ir_fini);/* * Local variables: * c-basic-offset: 8 * End: */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -