diff options
-rw-r--r-- | drivers/input/misc/Kconfig | 18 | ||||
-rw-r--r-- | drivers/input/misc/Makefile | 1 | ||||
-rw-r--r-- | drivers/media/rc/Kconfig | 17 | ||||
-rw-r--r-- | drivers/media/rc/Makefile | 1 | ||||
-rw-r--r-- | drivers/media/rc/winbond-cir.c (renamed from drivers/input/misc/winbond-cir.c) | 886 |
5 files changed, 123 insertions, 800 deletions
diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index b99b8cbde02f..c1a81bcdb319 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -294,24 +294,6 @@ config INPUT_SGI_BTNS To compile this driver as a module, choose M here: the module will be called sgi_btns. -config INPUT_WINBOND_CIR - tristate "Winbond IR remote control" - depends on X86 && PNP - select NEW_LEDS - select LEDS_CLASS - select LEDS_TRIGGERS - select BITREVERSE - help - Say Y here if you want to use the IR remote functionality found - in some Winbond SuperI/O chips. Currently only the WPCD376I - chip is supported (included in some Intel Media series motherboards). - - IR Receive and wake-on-IR from suspend and power-off is currently - supported. - - To compile this driver as a module, choose M here: the module will be - called winbond_cir. - config HP_SDC_RTC tristate "HP SDC Real Time Clock" depends on (GSC || HP300) && SERIO diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile index 1fe1f6c8b737..06b2b5154038 100644 --- a/drivers/input/misc/Makefile +++ b/drivers/input/misc/Makefile @@ -38,7 +38,6 @@ obj-$(CONFIG_INPUT_SPARCSPKR) += sparcspkr.o obj-$(CONFIG_INPUT_TWL4030_PWRBUTTON) += twl4030-pwrbutton.o obj-$(CONFIG_INPUT_TWL4030_VIBRA) += twl4030-vibra.o obj-$(CONFIG_INPUT_UINPUT) += uinput.o -obj-$(CONFIG_INPUT_WINBOND_CIR) += winbond-cir.o obj-$(CONFIG_INPUT_WISTRON_BTNS) += wistron_btns.o obj-$(CONFIG_INPUT_WM831X_ON) += wm831x-on.o obj-$(CONFIG_INPUT_YEALINK) += yealink.o diff --git a/drivers/media/rc/Kconfig b/drivers/media/rc/Kconfig index d05003d0995f..2d1546889c9b 100644 --- a/drivers/media/rc/Kconfig +++ b/drivers/media/rc/Kconfig @@ -164,4 +164,21 @@ config IR_STREAMZAP To compile this driver as a module, choose M here: the module will be called streamzap. +config IR_WINBOND_CIR + tristate "Winbond IR remote control" + depends on X86 && PNP + depends on IR_CORE + select NEW_LEDS + select LEDS_CLASS + select LEDS_TRIGGERS + select BITREVERSE + ---help--- + Say Y here if you want to use the IR remote functionality found + in some Winbond SuperI/O chips. Currently only the WPCD376I + chip is supported (included in some Intel Media series + motherboards). + + To compile this driver as a module, choose M here: the module will + be called winbond_cir. + endif #IR_CORE diff --git a/drivers/media/rc/Makefile b/drivers/media/rc/Makefile index 1eb24e6bc55c..859c12cc03ff 100644 --- a/drivers/media/rc/Makefile +++ b/drivers/media/rc/Makefile @@ -20,3 +20,4 @@ obj-$(CONFIG_IR_MCEUSB) += mceusb.o obj-$(CONFIG_IR_NUVOTON) += nuvoton-cir.o obj-$(CONFIG_IR_ENE) += ene_ir.o obj-$(CONFIG_IR_STREAMZAP) += streamzap.o +obj-$(CONFIG_IR_WINBOND_CIR) += winbond-cir.o diff --git a/drivers/input/misc/winbond-cir.c b/drivers/media/rc/winbond-cir.c index 64f1de7960c6..0ee16ec23bbf 100644 --- a/drivers/input/misc/winbond-cir.c +++ b/drivers/media/rc/winbond-cir.c @@ -7,10 +7,10 @@ * with minor modifications. * * Original Author: David Härdeman <david@hardeman.nu> - * Copyright (C) 2009 David Härdeman <david@hardeman.nu> + * Copyright (C) 2009 - 2010 David Härdeman <david@hardeman.nu> * - * Dedicated to Matilda, my newborn daughter, without whose loving attention - * this driver would have been finished in half the time and with a fraction + * Dedicated to my daughter Matilda, without whose loving attention this + * driver would have been finished in half the time and with a fraction * of the bugs. * * Written using: @@ -19,15 +19,11 @@ * o DSDT dumps * * Supported features: - * o RC6 * o Wake-On-CIR functionality * * To do: - * o Test NEC and RC5 - * - * Left as an exercise for the reader: - * o Learning (I have neither the hardware, nor the need) - * o IR Transmit (ibid) + * o Learning + * o IR Transmit * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -48,15 +44,13 @@ #include <linux/pnp.h> #include <linux/interrupt.h> #include <linux/timer.h> -#include <linux/input.h> #include <linux/leds.h> -#include <linux/list.h> #include <linux/spinlock.h> #include <linux/pci_ids.h> #include <linux/io.h> #include <linux/bitrev.h> -#include <linux/bitops.h> #include <linux/slab.h> +#include <media/ir-core.h> #define DRVNAME "winbond-cir" @@ -108,7 +102,7 @@ #define WBCIR_REG_SP3_IRCR2 0x04 /* Infrared Control 2 */ /* Bank 6 */ #define WBCIR_REG_SP3_IRCR3 0x00 /* Infrared Control 3 */ -#define WBCIR_REG_SP3_SIR_PW 0x02 /* SIR Pulse Width */ +#define WBCIR_REG_SP3_SIR_PW 0x02 /* SIR Pulse Width */ /* Bank 7 */ #define WBCIR_REG_SP3_IRRXDC 0x00 /* IR RX Demod Control */ #define WBCIR_REG_SP3_IRTXMC 0x01 /* IR TX Mod Control */ @@ -153,7 +147,7 @@ enum wbcir_bank { WBCIR_BANK_7 = 0xF4, }; -/* Supported IR Protocols */ +/* Supported power-on IR Protocols */ enum wbcir_protocol { IR_PROTOCOL_RC5 = 0x0, IR_PROTOCOL_NEC = 0x1, @@ -164,113 +158,35 @@ enum wbcir_protocol { #define WBCIR_NAME "Winbond CIR" #define WBCIR_ID_FAMILY 0xF1 /* Family ID for the WPCD376I */ #define WBCIR_ID_CHIP 0x04 /* Chip ID for the WPCD376I */ -#define IR_KEYPRESS_TIMEOUT 250 /* FIXME: should be per-protocol? */ #define INVALID_SCANCODE 0x7FFFFFFF /* Invalid with all protos */ #define WAKEUP_IOMEM_LEN 0x10 /* Wake-Up I/O Reg Len */ #define EHFUNC_IOMEM_LEN 0x10 /* Enhanced Func I/O Reg Len */ #define SP_IOMEM_LEN 0x08 /* Serial Port 3 (IR) Reg Len */ -#define WBCIR_MAX_IDLE_BYTES 10 - -static DEFINE_SPINLOCK(wbcir_lock); -static DEFINE_RWLOCK(keytable_lock); -struct wbcir_key { - u32 scancode; - unsigned int keycode; -}; - -struct wbcir_keyentry { - struct wbcir_key key; - struct list_head list; -}; - -static struct wbcir_key rc6_def_keymap[] = { - { 0x800F0400, KEY_NUMERIC_0 }, - { 0x800F0401, KEY_NUMERIC_1 }, - { 0x800F0402, KEY_NUMERIC_2 }, - { 0x800F0403, KEY_NUMERIC_3 }, - { 0x800F0404, KEY_NUMERIC_4 }, - { 0x800F0405, KEY_NUMERIC_5 }, - { 0x800F0406, KEY_NUMERIC_6 }, - { 0x800F0407, KEY_NUMERIC_7 }, - { 0x800F0408, KEY_NUMERIC_8 }, - { 0x800F0409, KEY_NUMERIC_9 }, - { 0x800F041D, KEY_NUMERIC_STAR }, - { 0x800F041C, KEY_NUMERIC_POUND }, - { 0x800F0410, KEY_VOLUMEUP }, - { 0x800F0411, KEY_VOLUMEDOWN }, - { 0x800F0412, KEY_CHANNELUP }, - { 0x800F0413, KEY_CHANNELDOWN }, - { 0x800F040E, KEY_MUTE }, - { 0x800F040D, KEY_VENDOR }, /* Vista Logo Key */ - { 0x800F041E, KEY_UP }, - { 0x800F041F, KEY_DOWN }, - { 0x800F0420, KEY_LEFT }, - { 0x800F0421, KEY_RIGHT }, - { 0x800F0422, KEY_OK }, - { 0x800F0423, KEY_ESC }, - { 0x800F040F, KEY_INFO }, - { 0x800F040A, KEY_CLEAR }, - { 0x800F040B, KEY_ENTER }, - { 0x800F045B, KEY_RED }, - { 0x800F045C, KEY_GREEN }, - { 0x800F045D, KEY_YELLOW }, - { 0x800F045E, KEY_BLUE }, - { 0x800F045A, KEY_TEXT }, - { 0x800F0427, KEY_SWITCHVIDEOMODE }, - { 0x800F040C, KEY_POWER }, - { 0x800F0450, KEY_RADIO }, - { 0x800F0448, KEY_PVR }, - { 0x800F0447, KEY_AUDIO }, - { 0x800F0426, KEY_EPG }, - { 0x800F0449, KEY_CAMERA }, - { 0x800F0425, KEY_TV }, - { 0x800F044A, KEY_VIDEO }, - { 0x800F0424, KEY_DVD }, - { 0x800F0416, KEY_PLAY }, - { 0x800F0418, KEY_PAUSE }, - { 0x800F0419, KEY_STOP }, - { 0x800F0414, KEY_FASTFORWARD }, - { 0x800F041A, KEY_NEXT }, - { 0x800F041B, KEY_PREVIOUS }, - { 0x800F0415, KEY_REWIND }, - { 0x800F0417, KEY_RECORD }, -}; - -/* Registers and other state is protected by wbcir_lock */ +/* Per-device data */ struct wbcir_data { + spinlock_t spinlock; + unsigned long wbase; /* Wake-Up Baseaddr */ unsigned long ebase; /* Enhanced Func. Baseaddr */ unsigned long sbase; /* Serial Port Baseaddr */ unsigned int irq; /* Serial Port IRQ */ - struct input_dev *input_dev; - struct timer_list timer_keyup; + struct rc_dev *dev; + struct led_trigger *rxtrigger; struct led_trigger *txtrigger; struct led_classdev led; - u32 last_scancode; - unsigned int last_keycode; - u8 last_toggle; - u8 keypressed; - unsigned long keyup_jiffies; - unsigned int idle_count; - - /* RX irdata and parsing state */ - unsigned long irdata[30]; - unsigned int irdata_count; - unsigned int irdata_idle; - unsigned int irdata_off; - unsigned int irdata_error; - - /* Protected by keytable_lock */ - struct list_head keytable; + /* RX irdata state */ + bool irdata_active; + bool irdata_error; + struct ir_raw_event ev; }; static enum wbcir_protocol protocol = IR_PROTOCOL_RC6; module_param(protocol, uint, 0444); -MODULE_PARM_DESC(protocol, "IR protocol to use " +MODULE_PARM_DESC(protocol, "IR protocol to use for the power-on command " "(0 = RC5, 1 = NEC, 2 = RC6A, default)"); static int invert; /* default = 0 */ @@ -327,7 +243,7 @@ wbcir_led_brightness_get(struct led_classdev *led_cdev) static void wbcir_led_brightness_set(struct led_classdev *led_cdev, - enum led_brightness brightness) + enum led_brightness brightness) { struct wbcir_data *data = container_of(led_cdev, struct wbcir_data, @@ -338,7 +254,7 @@ wbcir_led_brightness_set(struct led_classdev *led_cdev, WBCIR_LED_ENABLE); } -/* Manchester encodes bits to RC6 message cells (see wbcir_parse_rc6) */ +/* Manchester encodes bits to RC6 message cells (see wbcir_shutdown) */ static u8 wbcir_to_rc6cells(u8 val) { @@ -357,579 +273,6 @@ wbcir_to_rc6cells(u8 val) return coded; } - - -/***************************************************************************** - * - * INPUT FUNCTIONS - * - *****************************************************************************/ - -static unsigned int -wbcir_do_getkeycode(struct wbcir_data *data, u32 scancode) -{ - struct wbcir_keyentry *keyentry; - unsigned int keycode = KEY_RESERVED; - unsigned long flags; - - read_lock_irqsave(&keytable_lock, flags); - - list_for_each_entry(keyentry, &data->keytable, list) { - if (keyentry->key.scancode == scancode) { - keycode = keyentry->key.keycode; - break; - } - } - - read_unlock_irqrestore(&keytable_lock, flags); - return keycode; -} - -static int -wbcir_getkeycode(struct input_dev *dev, - unsigned int scancode, unsigned int *keycode) -{ - struct wbcir_data *data = input_get_drvdata(dev); - - *keycode = wbcir_do_getkeycode(data, scancode); - return 0; -} - -static int -wbcir_setkeycode(struct input_dev *dev, - unsigned int scancode, unsigned int keycode) -{ - struct wbcir_data *data = input_get_drvdata(dev); - struct wbcir_keyentry *keyentry; - struct wbcir_keyentry *new_keyentry; - unsigned long flags; - unsigned int old_keycode = KEY_RESERVED; - - new_keyentry = kmalloc(sizeof(*new_keyentry), GFP_KERNEL); - if (!new_keyentry) - return -ENOMEM; - - write_lock_irqsave(&keytable_lock, flags); - - list_for_each_entry(keyentry, &data->keytable, list) { - if (keyentry->key.scancode != scancode) - continue; - - old_keycode = keyentry->key.keycode; - keyentry->key.keycode = keycode; - - if (keyentry->key.keycode == KEY_RESERVED) { - list_del(&keyentry->list); - kfree(keyentry); - } - - break; - } - - set_bit(keycode, dev->keybit); - - if (old_keycode == KEY_RESERVED) { - new_keyentry->key.scancode = scancode; - new_keyentry->key.keycode = keycode; - list_add(&new_keyentry->list, &data->keytable); - } else { - kfree(new_keyentry); - clear_bit(old_keycode, dev->keybit); - list_for_each_entry(keyentry, &data->keytable, list) { - if (keyentry->key.keycode == old_keycode) { - set_bit(old_keycode, dev->keybit); - break; - } - } - } - - write_unlock_irqrestore(&keytable_lock, flags); - return 0; -} - -/* - * Timer function to report keyup event some time after keydown is - * reported by the ISR. - */ -static void -wbcir_keyup(unsigned long cookie) -{ - struct wbcir_data *data = (struct wbcir_data *)cookie; - unsigned long flags; - - /* - * data->keyup_jiffies is used to prevent a race condition if a - * hardware interrupt occurs at this point and the keyup timer - * event is moved further into the future as a result. - * - * The timer will then be reactivated and this function called - * again in the future. We need to exit gracefully in that case - * to allow the input subsystem to do its auto-repeat magic or - * a keyup event might follow immediately after the keydown. - */ - - spin_lock_irqsave(&wbcir_lock, flags); - - if (time_is_after_eq_jiffies(data->keyup_jiffies) && data->keypressed) { - data->keypressed = 0; - led_trigger_event(data->rxtrigger, LED_OFF); - input_report_key(data->input_dev, data->last_keycode, 0); - input_sync(data->input_dev); - } - - spin_unlock_irqrestore(&wbcir_lock, flags); -} - -static void -wbcir_keydown(struct wbcir_data *data, u32 scancode, u8 toggle) -{ - unsigned int keycode; - - /* Repeat? */ - if (data->last_scancode == scancode && - data->last_toggle == toggle && - data->keypressed) - goto set_timer; - data->last_scancode = scancode; - - /* Do we need to release an old keypress? */ - if (data->keypressed) { - input_report_key(data->input_dev, data->last_keycode, 0); - input_sync(data->input_dev); - data->keypressed = 0; - } - - /* Report scancode */ - input_event(data->input_dev, EV_MSC, MSC_SCAN, (int)scancode); - - /* Do we know this scancode? */ - keycode = wbcir_do_getkeycode(data, scancode); - if (keycode == KEY_RESERVED) - goto set_timer; - - /* Register a keypress */ - input_report_key(data->input_dev, keycode, 1); - data->keypressed = 1; - data->last_keycode = keycode; - data->last_toggle = toggle; - -set_timer: - input_sync(data->input_dev); - led_trigger_event(data->rxtrigger, - data->keypressed ? LED_FULL : LED_OFF); - data->keyup_jiffies = jiffies + msecs_to_jiffies(IR_KEYPRESS_TIMEOUT); - mod_timer(&data->timer_keyup, data->keyup_jiffies); -} - - - -/***************************************************************************** - * - * IR PARSING FUNCTIONS - * - *****************************************************************************/ - -/* Resets all irdata */ -static void -wbcir_reset_irdata(struct wbcir_data *data) -{ - memset(data->irdata, 0, sizeof(data->irdata)); - data->irdata_count = 0; - data->irdata_off = 0; - data->irdata_error = 0; - data->idle_count = 0; -} - -/* Adds one bit of irdata */ -static void -add_irdata_bit(struct wbcir_data *data, int set) -{ - if (data->irdata_count >= sizeof(data->irdata) * 8) { - data->irdata_error = 1; - return; - } - - if (set) - __set_bit(data->irdata_count, data->irdata); - data->irdata_count++; -} - -/* Gets count bits of irdata */ -static u16 -get_bits(struct wbcir_data *data, int count) -{ - u16 val = 0x0; - - if (data->irdata_count - data->irdata_off < count) { - data->irdata_error = 1; - return 0x0; - } - - while (count > 0) { - val <<= 1; - if (test_bit(data->irdata_off, data->irdata)) - val |= 0x1; - count--; - data->irdata_off++; - } - - return val; -} - -/* Reads 16 cells and converts them to a byte */ -static u8 -wbcir_rc6cells_to_byte(struct wbcir_data *data) -{ - u16 raw = get_bits(data, 16); - u8 val = 0x00; - int bit; - - for (bit = 0; bit < 8; bit++) { - switch (raw & 0x03) { - case 0x01: - break; - case 0x02: - val |= (0x01 << bit); - break; - default: - data->irdata_error = 1; - break; - } - raw >>= 2; - } - - return val; -} - -/* Decodes a number of bits from raw RC5 data */ -static u8 -wbcir_get_rc5bits(struct wbcir_data *data, unsigned int count) -{ - u16 raw = get_bits(data, count * 2); - u8 val = 0x00; - int bit; - - for (bit = 0; bit < count; bit++) { - switch (raw & 0x03) { - case 0x01: - val |= (0x01 << bit); - break; - case 0x02: - break; - default: - data->irdata_error = 1; - break; - } - raw >>= 2; - } - - return val; -} - -static void -wbcir_parse_rc6(struct device *dev, struct wbcir_data *data) -{ - /* - * Normal bits are manchester coded as follows: - * cell0 + cell1 = logic "0" - * cell1 + cell0 = logic "1" - * - * The IR pulse has the following components: - * - * Leader - 6 * cell1 - discarded - * Gap - 2 * cell0 - discarded - * Start bit - Normal Coding - always "1" - * Mode Bit 2 - 0 - Normal Coding - * Toggle bit - Normal Coding with double bit time, - * e.g. cell0 + cell0 + cell1 + cell1 - * means logic "0". - * - * The rest depends on the mode, the following modes are known: - * - * MODE 0: - * Address Bit 7 - 0 - Normal Coding - * Command Bit 7 - 0 - Normal Coding - * - * MODE 6: - * The above Toggle Bit is used as a submode bit, 0 = A, 1 = B. - * Submode B is for pointing devices, only remotes using submode A - * are supported. - * - * Customer range bit - 0 => Customer = 7 bits, 0...127 - * 1 => Customer = 15 bits, 32768...65535 - * Customer Bits - Normal Coding - * - * Customer codes are allocated by Philips. The rest of the bits - * are customer dependent. The following is commonly used (and the - * only supported config): - * - * Toggle Bit - Normal Coding - * Address Bit 6 - 0 - Normal Coding - * Command Bit 7 - 0 - Normal Coding - * - * All modes are followed by at least 6 * cell0. - * - * MODE 0 msglen: - * 1 * 2 (start bit) + 3 * 2 (mode) + 2 * 2 (toggle) + - * 8 * 2 (address) + 8 * 2 (command) = - * 44 cells - * - * MODE 6A msglen: - * 1 * 2 (start bit) + 3 * 2 (mode) + 2 * 2 (submode) + - * 1 * 2 (customer range bit) + 7/15 * 2 (customer bits) + - * 1 * 2 (toggle bit) + 7 * 2 (address) + 8 * 2 (command) = - * 60 - 76 cells - */ - u8 mode; - u8 toggle; - u16 customer = 0x0; - u8 address; - u8 command; - u32 scancode; - - /* Leader mark */ - while (get_bits(data, 1) && !data->irdata_error) - /* Do nothing */; - - /* Leader space */ - if (get_bits(data, 1)) { - dev_dbg(dev, "RC6 - Invalid leader space\n"); - return; - } - - /* Start bit */ - if (get_bits(data, 2) != 0x02) { - dev_dbg(dev, "RC6 - Invalid start bit\n"); - return; - } - - /* Mode */ - mode = get_bits(data, 6); - switch (mode) { - case 0x15: /* 010101 = b000 */ - mode = 0; - break; - case 0x29: /* 101001 = b110 */ - mode = 6; - break; - default: - dev_dbg(dev, "RC6 - Invalid mode\n"); - return; - } - - /* Toggle bit / Submode bit */ - toggle = get_bits(data, 4); - switch (toggle) { - case 0x03: - toggle = 0; - break; - case 0x0C: - toggle = 1; - break; - default: - dev_dbg(dev, "RC6 - Toggle bit error\n"); - break; - } - - /* Customer */ - if (mode == 6) { - if (toggle != 0) { - dev_dbg(dev, "RC6B - Not Supported\n"); - return; - } - - customer = wbcir_rc6cells_to_byte(data); - - if (customer & 0x80) { - /* 15 bit customer value */ - customer <<= 8; - customer |= wbcir_rc6cells_to_byte(data); - } - } - - /* Address */ - address = wbcir_rc6cells_to_byte(data); - if (mode == 6) { - toggle = address >> 7; - address &= 0x7F; - } - - /* Command */ - command = wbcir_rc6cells_to_byte(data); - - /* Create scancode */ - scancode = command; - scancode |= address << 8; - scancode |= customer << 16; - - /* Last sanity check */ - if (data->irdata_error) { - dev_dbg(dev, "RC6 - Cell error(s)\n"); - return; - } - - dev_dbg(dev, "IR-RC6 ad 0x%02X cm 0x%02X cu 0x%04X " - "toggle %u mode %u scan 0x%08X\n", - address, - command, - customer, - (unsigned int)toggle, - (unsigned int)mode, - scancode); - - wbcir_keydown(data, scancode, toggle); -} - -static void -wbcir_parse_rc5(struct device *dev, struct wbcir_data *data) -{ - /* - * Bits are manchester coded as follows: - * cell1 + cell0 = logic "0" - * cell0 + cell1 = logic "1" - * (i.e. the reverse of RC6) - * - * Start bit 1 - "1" - discarded - * Start bit 2 - Must be inverted to get command bit 6 - * Toggle bit - * Address Bit 4 - 0 - * Command Bit 5 - 0 - */ - u8 toggle; - u8 address; - u8 command; - u32 scancode; - - /* Start bit 1 */ - if (!get_bits(data, 1)) { - dev_dbg(dev, "RC5 - Invalid start bit\n"); - return; - } - - /* Start bit 2 */ - if (!wbcir_get_rc5bits(data, 1)) - command = 0x40; - else - command = 0x00; - - toggle = wbcir_get_rc5bits(data, 1); - address = wbcir_get_rc5bits(data, 5); - command |= wbcir_get_rc5bits(data, 6); - scancode = address << 7 | command; - - /* Last sanity check */ - if (data->irdata_error) { - dev_dbg(dev, "RC5 - Invalid message\n"); - return; - } - - dev_dbg(dev, "IR-RC5 ad %u cm %u t %u s %u\n", - (unsigned int)address, - (unsigned int)command, - (unsigned int)toggle, - (unsigned int)scancode); - - wbcir_keydown(data, scancode, toggle); -} - -static void -wbcir_parse_nec(struct device *dev, struct wbcir_data *data) -{ - /* - * Each bit represents 560 us. - * - * Leader - 9 ms burst - * Gap - 4.5 ms silence - * Address1 bit 0 - 7 - Address 1 - * Address2 bit 0 - 7 - Address 2 - * Command1 bit 0 - 7 - Command 1 - * Command2 bit 0 - 7 - Command 2 - * - * Note the bit order! - * - * With the old NEC protocol, Address2 was the inverse of Address1 - * and Command2 was the inverse of Command1 and were used as - * an error check. - * - * With NEC extended, Address1 is the LSB of the Address and - * Address2 is the MSB, Command parsing remains unchanged. - * - * A repeat message is coded as: - * Leader - 9 ms burst - * Gap - 2.25 ms silence - * Repeat - 560 us active - */ - u8 address1; - u8 address2; - u8 command1; - u8 command2; - u16 address; - u32 scancode; - - /* Leader mark */ - while (get_bits(data, 1) && !data->irdata_error) - /* Do nothing */; - - /* Leader space */ - if (get_bits(data, 4)) { - dev_dbg(dev, "NEC - Invalid leader space\n"); - return; - } - - /* Repeat? */ - if (get_bits(data, 1)) { - if (!data->keypressed) { - dev_dbg(dev, "NEC - Stray repeat message\n"); - return; - } - - dev_dbg(dev, "IR-NEC repeat s %u\n", - (unsigned int)data->last_scancode); - - wbcir_keydown(data, data->last_scancode, data->last_toggle); - return; - } - - /* Remaining leader space */ - if (get_bits(data, 3)) { - dev_dbg(dev, "NEC - Invalid leader space\n"); - return; - } - - address1 = bitrev8(get_bits(data, 8)); - address2 = bitrev8(get_bits(data, 8)); - command1 = bitrev8(get_bits(data, 8)); - command2 = bitrev8(get_bits(data, 8)); - - /* Sanity check */ - if (data->irdata_error) { - dev_dbg(dev, "NEC - Invalid message\n"); - return; - } - - /* Check command validity */ - if (command1 != ~command2) { - dev_dbg(dev, "NEC - Command bytes mismatch\n"); - return; - } - - /* Check for extended NEC protocol */ - address = address1; - if (address1 != ~address2) - address |= address2 << 8; - - scancode = address << 8 | command1; - - dev_dbg(dev, "IR-NEC ad %u cm %u s %u\n", - (unsigned int)address, - (unsigned int)command1, - (unsigned int)scancode); - - wbcir_keydown(data, scancode, !data->last_toggle); -} - - - /***************************************************************************** * * INTERRUPT FUNCTIONS @@ -941,75 +284,88 @@ wbcir_irq_handler(int irqno, void *cookie) { struct pnp_dev *device = cookie; struct wbcir_data *data = pnp_get_drvdata(device); - struct device *dev = &device->dev; - u8 status; unsigned long flags; u8 irdata[8]; + u8 disable = true; + u8 status; int i; - unsigned int hw; - spin_lock_irqsave(&wbcir_lock, flags); + spin_lock_irqsave(&data->spinlock, flags); wbcir_select_bank(data, WBCIR_BANK_0); status = inb(data->sbase + WBCIR_REG_SP3_EIR); if (!(status & (WBCIR_IRQ_RX | WBCIR_IRQ_ERR))) { - spin_unlock_irqrestore(&wbcir_lock, flags); + spin_unlock_irqrestore(&data->spinlock, flags); return IRQ_NONE; } - if (status & WBCIR_IRQ_ERR) - data->irdata_error = 1; + /* Check for e.g. buffer overflow */ + if (status & WBCIR_IRQ_ERR) { + data->irdata_error = true; + ir_raw_event_reset(data->dev); + } if (!(status & WBCIR_IRQ_RX)) goto out; + if (!data->irdata_active) { + data->irdata_active = true; + led_trigger_event(data->rxtrigger, LED_FULL); + } + /* Since RXHDLEV is set, at least 8 bytes are in the FIFO */ insb(data->sbase + WBCIR_REG_SP3_RXDATA, &irdata[0], 8); - for (i = 0; i < sizeof(irdata); i++) { - hw = hweight8(irdata[i]); - if (hw > 4) - add_irdata_bit(data, 0); - else - add_irdata_bit(data, 1); + for (i = 0; i < 8; i++) { + u8 pulse; + u32 duration; - if (hw == 8) - data->idle_count++; - else - data->idle_count = 0; + if (irdata[i] != 0xFF && irdata[i] != 0x00) + disable = false; + + if (data->irdata_error) + continue; + + pulse = irdata[i] & 0x80 ? false : true; + duration = (irdata[i] & 0x7F) * 10000; /* ns */ + + if (data->ev.pulse != pulse) { + if (data->ev.duration != 0) { + ir_raw_event_store(data->dev, &data->ev); + data->ev.duration = 0; + } + + data->ev.pulse = pulse; + } + + data->ev.duration += duration; } - if (data->idle_count > WBCIR_MAX_IDLE_BYTES) { - /* Set RXINACTIVE... */ + if (disable) { + if (data->ev.duration != 0 && !data->irdata_error) { + ir_raw_event_store(data->dev, &data->ev); + data->ev.duration = 0; + } + + /* Set RXINACTIVE */ outb(WBCIR_RX_DISABLE, data->sbase + WBCIR_REG_SP3_ASCR); - /* ...and drain the FIFO */ + /* Drain the FIFO */ while (inb(data->sbase + WBCIR_REG_SP3_LSR) & WBCIR_RX_AVAIL) inb(data->sbase + WBCIR_REG_SP3_RXDATA); - dev_dbg(dev, "IRDATA:\n"); - for (i = 0; i < data->irdata_count; i += BITS_PER_LONG) - dev_dbg(dev, "0x%08lX\n", data->irdata[i/BITS_PER_LONG]); - - switch (protocol) { - case IR_PROTOCOL_RC5: - wbcir_parse_rc5(dev, data); - break; - case IR_PROTOCOL_RC6: - wbcir_parse_rc6(dev, data); - break; - case IR_PROTOCOL_NEC: - wbcir_parse_nec(dev, data); - break; - } - - wbcir_reset_irdata(data); + ir_raw_event_reset(data->dev); + data->irdata_error = false; + data->irdata_active = false; + led_trigger_event(data->rxtrigger, LED_OFF); } + ir_raw_event_handle(data->dev); + out: - spin_unlock_irqrestore(&wbcir_lock, flags); + spin_unlock_irqrestore(&data->spinlock, flags); return IRQ_HANDLED; } @@ -1199,6 +555,10 @@ finish: wbcir_select_bank(data, WBCIR_BANK_0); outb(WBCIR_IRQ_NONE, data->sbase + WBCIR_REG_SP3_IER); + /* Disable LED */ + data->irdata_active = false; + led_trigger_event(data->rxtrigger, LED_OFF); + /* * ACPI will set the HW disable bit for SP3 which means that the * output signals are left in an undefined state which may cause @@ -1323,8 +683,15 @@ wbcir_init_hw(struct wbcir_data *data) /* Clear AUX status bits */ outb(0xE0, data->sbase + WBCIR_REG_SP3_ASCR); + /* Clear IR decoding state */ + data->irdata_active = false; + led_trigger_event(data->rxtrigger, LED_OFF); + data->irdata_error = false; + data->ev.duration = 0; + ir_raw_event_reset(data->dev); + ir_raw_event_handle(data->dev); + /* Enable interrupts */ - wbcir_reset_irdata(data); outb(WBCIR_IRQ_RX | WBCIR_IRQ_ERR, data->sbase + WBCIR_REG_SP3_IER); } @@ -1361,6 +728,7 @@ wbcir_probe(struct pnp_dev *device, const struct pnp_device_id *dev_id) pnp_set_drvdata(device, data); + spin_lock_init(&data->spinlock); data->ebase = pnp_port_start(device, 0); data->wbase = pnp_port_start(device, 1); data->sbase = pnp_port_start(device, 2); @@ -1426,43 +794,25 @@ wbcir_probe(struct pnp_dev *device, const struct pnp_device_id *dev_id) if (err) goto exit_unregister_rxtrigger; - data->input_dev = input_allocate_device(); - if (!data->input_dev) { + data->dev = rc_allocate_device(); + if (!data->dev) { err = -ENOMEM; goto exit_unregister_led; } - data->input_dev->evbit[0] = BIT(EV_KEY); - data->input_dev->name = WBCIR_NAME; - data->input_dev->phys = "wbcir/cir0"; - data->input_dev->id.bustype = BUS_HOST; - data->input_dev->id.vendor = PCI_VENDOR_ID_WINBOND; - data->input_dev->id.product = WBCIR_ID_FAMILY; - data->input_dev->id.version = WBCIR_ID_CHIP; - data->input_dev->getkeycode = wbcir_getkeycode; - data->input_dev->setkeycode = wbcir_setkeycode; - input_set_capability(data->input_dev, EV_MSC, MSC_SCAN); - input_set_drvdata(data->input_dev, data); - - err = input_register_device(data->input_dev); + data->dev->driver_name = WBCIR_NAME; + data->dev->input_name = WBCIR_NAME; + data->dev->input_phys = "wbcir/cir0"; + data->dev->input_id.bustype = BUS_HOST; + data->dev->input_id.vendor = PCI_VENDOR_ID_WINBOND; + data->dev->input_id.product = WBCIR_ID_FAMILY; + data->dev->input_id.version = WBCIR_ID_CHIP; + data->dev->priv = data; + data->dev->dev.parent = &device->dev; + + err = rc_register_device(data->dev); if (err) - goto exit_free_input; - - data->last_scancode = INVALID_SCANCODE; - INIT_LIST_HEAD(&data->keytable); - setup_timer(&data->timer_keyup, wbcir_keyup, (unsigned long)data); - - /* Load default keymaps */ - if (protocol == IR_PROTOCOL_RC6) { - int i; - for (i = 0; i < ARRAY_SIZE(rc6_def_keymap); i++) { - err = wbcir_setkeycode(data->input_dev, - (int)rc6_def_keymap[i].scancode, - (int)rc6_def_keymap[i].keycode); - if (err) - goto exit_unregister_keys; - } - } + goto exit_free_rc; device_init_wakeup(&device->dev, 1); @@ -1470,21 +820,8 @@ wbcir_probe(struct pnp_dev *device, const struct pnp_device_id *dev_id) return 0; -exit_unregister_keys: - if (!list_empty(&data->keytable)) { - struct wbcir_keyentry *key; - struct wbcir_keyentry *keytmp; - - list_for_each_entry_safe(key, keytmp, &data->keytable, list) { - list_del(&key->list); - kfree(key); - } - } - input_unregister_device(data->input_dev); - /* Can't call input_free_device on an unregistered device */ - data->input_dev = NULL; -exit_free_input: - input_free_device(data->input_dev); +exit_free_rc: + rc_free_device(data->dev); exit_unregister_led: led_classdev_unregister(&data->led); exit_unregister_rxtrigger: @@ -1510,15 +847,11 @@ static void __devexit wbcir_remove(struct pnp_dev *device) { struct wbcir_data *data = pnp_get_drvdata(device); - struct wbcir_keyentry *key; - struct wbcir_keyentry *keytmp; /* Disable interrupts */ wbcir_select_bank(data, WBCIR_BANK_0); outb(WBCIR_IRQ_NONE, data->sbase + WBCIR_REG_SP3_IER); - del_timer_sync(&data->timer_keyup); - free_irq(data->irq, device); /* Clear status bits NEC_REP, BUFF, MSG_END, MATCH */ @@ -1530,8 +863,7 @@ wbcir_remove(struct pnp_dev *device) /* Clear BUFF_EN, END_EN, MATCH_EN */ wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_EV_EN, 0x00, 0x07); - /* This will generate a keyup event if necessary */ - input_unregister_device(data->input_dev); + rc_unregister_device(data->dev); led_trigger_unregister_simple(data->rxtrigger); led_trigger_unregister_simple(data->txtrigger); @@ -1544,11 +876,6 @@ wbcir_remove(struct pnp_dev *device) release_region(data->ebase, EHFUNC_IOMEM_LEN); release_region(data->sbase, SP_IOMEM_LEN); - list_for_each_entry_safe(key, keytmp, &data->keytable, list) { - list_del(&key->list); - kfree(key); - } - kfree(data); pnp_set_drvdata(device, NULL); @@ -1581,8 +908,7 @@ wbcir_init(void) case IR_PROTOCOL_RC6: break; default: - printk(KERN_ERR DRVNAME ": Invalid protocol argument\n"); - return -EINVAL; + printk(KERN_ERR DRVNAME ": Invalid power-on protocol\n"); } ret = pnp_register_driver(&wbcir_driver); @@ -1598,11 +924,9 @@ wbcir_exit(void) pnp_unregister_driver(&wbcir_driver); } -MODULE_AUTHOR("David Härdeman <david@hardeman.nu>"); -MODULE_DESCRIPTION("Winbond SuperI/O Consumer IR Driver"); -MODULE_LICENSE("GPL"); - module_init(wbcir_init); module_exit |