* [patch 0/2] Winbond IR Driver - v2
@ 2009-08-09 9:56 ` david
0 siblings, 0 replies; 27+ messages in thread
From: david @ 2009-08-09 9:56 UTC (permalink / raw)
To: linux-kernel
Cc: linux-input, jbarnes, akpm, bjorn.helgaas, randy.dunlap, david
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain, Size: 403 bytes --]
Here's a new patch set which should replace all the patches currently in the
-mm tree for the winbond cir driver. The new series incorporate feedback from
Bjorn Helgaas (convert the driver from ACPI to PNP) and Randy Dunlap (Kconfig
fixes).
The IR decoding can still be improved but the driver works for in daily use
decoding RC6 commands, so I believe it is ready to go upstream.
--
David Härdeman
^ permalink raw reply [flat|nested] 27+ messages in thread
* [patch 0/2] Winbond IR Driver - v2
@ 2009-08-09 9:56 ` david
0 siblings, 0 replies; 27+ messages in thread
From: david @ 2009-08-09 9:56 UTC (permalink / raw)
To: linux-kernel
Cc: linux-input, jbarnes, akpm, bjorn.helgaas, randy.dunlap, david
Here's a new patch set which should replace all the patches currently in the
-mm tree for the winbond cir driver. The new series incorporate feedback from
Bjorn Helgaas (convert the driver from ACPI to PNP) and Randy Dunlap (Kconfig
fixes).
The IR decoding can still be improved but the driver works for in daily use
decoding RC6 commands, so I believe it is ready to go upstream.
--
David Härdeman
--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply [flat|nested] 27+ messages in thread
* [patch 1/2] Add a shutdown method to pnp drivers
2009-08-09 9:56 ` david
@ 2009-08-09 9:56 ` david
-1 siblings, 0 replies; 27+ messages in thread
From: david @ 2009-08-09 9:56 UTC (permalink / raw)
To: linux-kernel
Cc: linux-input, jbarnes, akpm, bjorn.helgaas, randy.dunlap, david
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: add-pnp-shutdown-method --]
[-- Type: text/plain, Size: 1893 bytes --]
The shutdown method is used by the winbond cir driver to setup the
hardware for wake-from-S5.
Changes since last version: the previous patch added a shutdown method
to acpi drivers, the winbond cir driver is now a pnp driver so the
shutdown method is added to pnp drivers instead. Patch by
Bjorn Helgaas <bjorn.helgaas@hp.com>.
Signed-off-by: Bjorn Helgaas <bjorn.helgaas@hp.com>
Signed-off-by: David Härdeman <david@hardeman.nu>
Index: linux-2.6/drivers/pnp/driver.c
===================================================================
--- linux-2.6.orig/drivers/pnp/driver.c 2009-07-25 19:53:32.000000000 +0200
+++ linux-2.6/drivers/pnp/driver.c 2009-07-25 19:54:21.000000000 +0200
@@ -135,6 +135,15 @@
return 0;
}
+static void pnp_device_shutdown(struct device *dev)
+{
+ struct pnp_dev *pnp_dev = to_pnp_dev(dev);
+ struct pnp_driver *drv = pnp_dev->driver;
+
+ if (drv && drv->shutdown)
+ drv->shutdown(pnp_dev);
+}
+
static int pnp_bus_match(struct device *dev, struct device_driver *drv)
{
struct pnp_dev *pnp_dev = to_pnp_dev(dev);
@@ -203,6 +212,7 @@
.match = pnp_bus_match,
.probe = pnp_device_probe,
.remove = pnp_device_remove,
+ .shutdown = pnp_device_shutdown,
.suspend = pnp_bus_suspend,
.resume = pnp_bus_resume,
.dev_attrs = pnp_interface_attrs,
Index: linux-2.6/include/linux/pnp.h
===================================================================
--- linux-2.6.orig/include/linux/pnp.h 2009-07-25 19:53:32.000000000 +0200
+++ linux-2.6/include/linux/pnp.h 2009-07-25 19:54:21.000000000 +0200
@@ -360,6 +360,7 @@
unsigned int flags;
int (*probe) (struct pnp_dev *dev, const struct pnp_device_id *dev_id);
void (*remove) (struct pnp_dev *dev);
+ void (*shutdown) (struct pnp_dev *dev);
int (*suspend) (struct pnp_dev *dev, pm_message_t state);
int (*resume) (struct pnp_dev *dev);
struct device_driver driver;
--
David Härdeman
^ permalink raw reply [flat|nested] 27+ messages in thread
* [patch 1/2] Add a shutdown method to pnp drivers
@ 2009-08-09 9:56 ` david
0 siblings, 0 replies; 27+ messages in thread
From: david @ 2009-08-09 9:56 UTC (permalink / raw)
To: linux-kernel
Cc: linux-input, jbarnes, akpm, bjorn.helgaas, randy.dunlap, david
[-- Attachment #1: add-pnp-shutdown-method --]
[-- Type: TEXT/PLAIN, Size: 1947 bytes --]
The shutdown method is used by the winbond cir driver to setup the
hardware for wake-from-S5.
Changes since last version: the previous patch added a shutdown method
to acpi drivers, the winbond cir driver is now a pnp driver so the
shutdown method is added to pnp drivers instead. Patch by
Bjorn Helgaas <bjorn.helgaas@hp.com>.
Signed-off-by: Bjorn Helgaas <bjorn.helgaas@hp.com>
Signed-off-by: David Härdeman <david@hardeman.nu>
Index: linux-2.6/drivers/pnp/driver.c
===================================================================
--- linux-2.6.orig/drivers/pnp/driver.c 2009-07-25 19:53:32.000000000 +0200
+++ linux-2.6/drivers/pnp/driver.c 2009-07-25 19:54:21.000000000 +0200
@@ -135,6 +135,15 @@
return 0;
}
+static void pnp_device_shutdown(struct device *dev)
+{
+ struct pnp_dev *pnp_dev = to_pnp_dev(dev);
+ struct pnp_driver *drv = pnp_dev->driver;
+
+ if (drv && drv->shutdown)
+ drv->shutdown(pnp_dev);
+}
+
static int pnp_bus_match(struct device *dev, struct device_driver *drv)
{
struct pnp_dev *pnp_dev = to_pnp_dev(dev);
@@ -203,6 +212,7 @@
.match = pnp_bus_match,
.probe = pnp_device_probe,
.remove = pnp_device_remove,
+ .shutdown = pnp_device_shutdown,
.suspend = pnp_bus_suspend,
.resume = pnp_bus_resume,
.dev_attrs = pnp_interface_attrs,
Index: linux-2.6/include/linux/pnp.h
===================================================================
--- linux-2.6.orig/include/linux/pnp.h 2009-07-25 19:53:32.000000000 +0200
+++ linux-2.6/include/linux/pnp.h 2009-07-25 19:54:21.000000000 +0200
@@ -360,6 +360,7 @@
unsigned int flags;
int (*probe) (struct pnp_dev *dev, const struct pnp_device_id *dev_id);
void (*remove) (struct pnp_dev *dev);
+ void (*shutdown) (struct pnp_dev *dev);
int (*suspend) (struct pnp_dev *dev, pm_message_t state);
int (*resume) (struct pnp_dev *dev);
struct device_driver driver;
--
David Härdeman
^ permalink raw reply [flat|nested] 27+ messages in thread
* [patch 2/2] Add a driver for the Winbond WPCD376I Consumer IR hardware
2009-08-09 9:56 ` david
@ 2009-08-09 9:56 ` david
-1 siblings, 0 replies; 27+ messages in thread
From: david @ 2009-08-09 9:56 UTC (permalink / raw)
To: linux-kernel
Cc: linux-input, jbarnes, akpm, bjorn.helgaas, randy.dunlap, david
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: add-a-driver-for-the-winbond-wpcd376i-cir-functionality --]
[-- Type: text/plain, Size: 46919 bytes --]
Add a driver for the the Consumer IR (CIR) functionality of the Winbond
WPCD376I chipset (found on e.g. Intel DG45FC motherboards).
Changes since the last version: converted from an ACPI to a PNP driver
using a patch provided by Bjorn Helgaas <bjorn.helgaas@hp.com>.
Signed-off-by: David Härdeman <david@hardeman.nu>
Reviewed-by: Jesse Barnes <jbarnes@virtuousgeek.org>
Index: linux-2.6/MAINTAINERS
===================================================================
--- linux-2.6.orig/MAINTAINERS 2009-07-25 21:18:57.000000000 +0200
+++ linux-2.6/MAINTAINERS 2009-07-25 21:19:28.000000000 +0200
@@ -6448,6 +6448,12 @@
S: Maintained
F: drivers/scsi/wd7000.c
+WINBOND CIR DRIVER
+P: David Härdeman
+M: david@hardeman.nu
+S: Maintained
+F: drivers/input/misc/winbond-cir.c
+
WIMAX STACK
P: Inaky Perez-Gonzalez
M: inaky.perez-gonzalez@intel.com
Index: linux-2.6/drivers/input/misc/Kconfig
===================================================================
--- linux-2.6.orig/drivers/input/misc/Kconfig 2009-07-25 21:18:57.000000000 +0200
+++ linux-2.6/drivers/input/misc/Kconfig 2009-08-07 21:45:41.000000000 +0200
@@ -222,6 +222,22 @@
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 && ACPI
+ select LEDS_CLASS
+ 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
Index: linux-2.6/drivers/input/misc/Makefile
===================================================================
--- linux-2.6.orig/drivers/input/misc/Makefile 2009-07-25 21:18:57.000000000 +0200
+++ linux-2.6/drivers/input/misc/Makefile 2009-07-25 21:19:28.000000000 +0200
@@ -19,6 +19,7 @@
obj-$(CONFIG_INPUT_PCSPKR) += pcspkr.o
obj-$(CONFIG_INPUT_POWERMATE) += powermate.o
obj-$(CONFIG_INPUT_RB532_BUTTON) += rb532_button.o
+obj-$(CONFIG_INPUT_WINBOND_CIR) += winbond-cir.o
obj-$(CONFIG_INPUT_GPIO_ROTARY_ENCODER) += rotary_encoder.o
obj-$(CONFIG_INPUT_SGI_BTNS) += sgi_btns.o
obj-$(CONFIG_INPUT_SPARCSPKR) += sparcspkr.o
Index: linux-2.6/drivers/input/misc/winbond-cir.c
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6/drivers/input/misc/winbond-cir.c 2009-07-25 21:19:28.000000000 +0200
@@ -0,0 +1,1644 @@
+/*
+ * winbond-cir.c - Driver for the Consumer IR functionality of Winbond
+ * SuperI/O chips.
+ *
+ * Currently supports the Winbond WPCD376i chip (PNP id WEC1022), but
+ * could probably support others (Winbond WEC102X, NatSemi, etc)
+ * with minor modifications.
+ *
+ * Original Author: David Härdeman <david@hardeman.nu>
+ * Copyright (C) 2009 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
+ * of the bugs.
+ *
+ * Written using:
+ * o Winbond WPCD376I datasheet helpfully provided by Jesse Barnes at Intel
+ * o NatSemi PC87338/PC97338 datasheet (for the serial port stuff)
+ * 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)
+ *
+ * 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
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#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>
+
+#define DRVNAME "winbond-cir"
+
+/* CEIR Wake-Up Registers, relative to data->wbase */
+#define WBCIR_REG_WCEIR_CTL 0x03 /* CEIR Receiver Control */
+#define WBCIR_REG_WCEIR_STS 0x04 /* CEIR Receiver Status */
+#define WBCIR_REG_WCEIR_EV_EN 0x05 /* CEIR Receiver Event Enable */
+#define WBCIR_REG_WCEIR_CNTL 0x06 /* CEIR Receiver Counter Low */
+#define WBCIR_REG_WCEIR_CNTH 0x07 /* CEIR Receiver Counter High */
+#define WBCIR_REG_WCEIR_INDEX 0x08 /* CEIR Receiver Index */
+#define WBCIR_REG_WCEIR_DATA 0x09 /* CEIR Receiver Data */
+#define WBCIR_REG_WCEIR_CSL 0x0A /* CEIR Re. Compare Strlen */
+#define WBCIR_REG_WCEIR_CFG1 0x0B /* CEIR Re. Configuration 1 */
+#define WBCIR_REG_WCEIR_CFG2 0x0C /* CEIR Re. Configuration 2 */
+
+/* CEIR Enhanced Functionality Registers, relative to data->ebase */
+#define WBCIR_REG_ECEIR_CTS 0x00 /* Enhanced IR Control Status */
+#define WBCIR_REG_ECEIR_CCTL 0x01 /* Infrared Counter Control */
+#define WBCIR_REG_ECEIR_CNT_LO 0x02 /* Infrared Counter LSB */
+#define WBCIR_REG_ECEIR_CNT_HI 0x03 /* Infrared Counter MSB */
+#define WBCIR_REG_ECEIR_IREM 0x04 /* Infrared Emitter Status */
+
+/* SP3 Banked Registers, relative to data->sbase */
+#define WBCIR_REG_SP3_BSR 0x03 /* Bank Select, all banks */
+ /* Bank 0 */
+#define WBCIR_REG_SP3_RXDATA 0x00 /* FIFO RX data (r) */
+#define WBCIR_REG_SP3_TXDATA 0x00 /* FIFO TX data (w) */
+#define WBCIR_REG_SP3_IER 0x01 /* Interrupt Enable */
+#define WBCIR_REG_SP3_EIR 0x02 /* Event Identification (r) */
+#define WBCIR_REG_SP3_FCR 0x02 /* FIFO Control (w) */
+#define WBCIR_REG_SP3_MCR 0x04 /* Mode Control */
+#define WBCIR_REG_SP3_LSR 0x05 /* Link Status */
+#define WBCIR_REG_SP3_MSR 0x06 /* Modem Status */
+#define WBCIR_REG_SP3_ASCR 0x07 /* Aux Status and Control */
+ /* Bank 2 */
+#define WBCIR_REG_SP3_BGDL 0x00 /* Baud Divisor LSB */
+#define WBCIR_REG_SP3_BGDH 0x01 /* Baud Divisor MSB */
+#define WBCIR_REG_SP3_EXCR1 0x02 /* Extended Control 1 */
+#define WBCIR_REG_SP3_EXCR2 0x04 /* Extended Control 2 */
+#define WBCIR_REG_SP3_TXFLV 0x06 /* TX FIFO Level */
+#define WBCIR_REG_SP3_RXFLV 0x07 /* RX FIFO Level */
+ /* Bank 3 */
+#define WBCIR_REG_SP3_MRID 0x00 /* Module Identification */
+#define WBCIR_REG_SP3_SH_LCR 0x01 /* LCR Shadow */
+#define WBCIR_REG_SP3_SH_FCR 0x02 /* FCR Shadow */
+ /* Bank 4 */
+#define WBCIR_REG_SP3_IRCR1 0x02 /* Infrared Control 1 */
+ /* Bank 5 */
+#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 */
+ /* Bank 7 */
+#define WBCIR_REG_SP3_IRRXDC 0x00 /* IR RX Demod Control */
+#define WBCIR_REG_SP3_IRTXMC 0x01 /* IR TX Mod Control */
+#define WBCIR_REG_SP3_RCCFG 0x02 /* CEIR Config */
+#define WBCIR_REG_SP3_IRCFG1 0x04 /* Infrared Config 1 */
+#define WBCIR_REG_SP3_IRCFG4 0x07 /* Infrared Config 4 */
+
+/*
+ * Magic values follow
+ */
+
+/* No interrupts for WBCIR_REG_SP3_IER and WBCIR_REG_SP3_EIR */
+#define WBCIR_IRQ_NONE 0x00
+/* RX data bit for WBCIR_REG_SP3_IER and WBCIR_REG_SP3_EIR */
+#define WBCIR_IRQ_RX 0x01
+/* Over/Under-flow bit for WBCIR_REG_SP3_IER and WBCIR_REG_SP3_EIR */
+#define WBCIR_IRQ_ERR 0x04
+/* Led enable/disable bit for WBCIR_REG_ECEIR_CTS */
+#define WBCIR_LED_ENABLE 0x80
+/* RX data available bit for WBCIR_REG_SP3_LSR */
+#define WBCIR_RX_AVAIL 0x01
+/* RX disable bit for WBCIR_REG_SP3_ASCR */
+#define WBCIR_RX_DISABLE 0x20
+/* Extended mode enable bit for WBCIR_REG_SP3_EXCR1 */
+#define WBCIR_EXT_ENABLE 0x01
+/* Select compare register in WBCIR_REG_WCEIR_INDEX (bits 5 & 6) */
+#define WBCIR_REGSEL_COMPARE 0x10
+/* Select mask register in WBCIR_REG_WCEIR_INDEX (bits 5 & 6) */
+#define WBCIR_REGSEL_MASK 0x20
+/* Starting address of selected register in WBCIR_REG_WCEIR_INDEX */
+#define WBCIR_REG_ADDR0 0x00
+
+/* Valid banks for the SP3 UART */
+enum wbcir_bank {
+ WBCIR_BANK_0 = 0x00,
+ WBCIR_BANK_1 = 0x80,
+ WBCIR_BANK_2 = 0xE0,
+ WBCIR_BANK_3 = 0xE4,
+ WBCIR_BANK_4 = 0xE8,
+ WBCIR_BANK_5 = 0xEC,
+ WBCIR_BANK_6 = 0xF0,
+ WBCIR_BANK_7 = 0xF4,
+};
+
+/* Supported IR Protocols */
+enum wbcir_protocol {
+ IR_PROTOCOL_RC5 = 0x0,
+ IR_PROTOCOL_NEC = 0x1,
+ IR_PROTOCOL_RC6 = 0x2,
+};
+
+/* Misc */
+#define WBCIR_ACPI_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_0 },
+ { 0x800F0401, KEY_1 },
+ { 0x800F0402, KEY_2 },
+ { 0x800F0403, KEY_3 },
+ { 0x800F0404, KEY_4 },
+ { 0x800F0405, KEY_5 },
+ { 0x800F0406, KEY_6 },
+ { 0x800F0407, KEY_7 },
+ { 0x800F0408, KEY_8 },
+ { 0x800F0409, KEY_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 */
+struct wbcir_data {
+ 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 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;
+};
+
+static enum wbcir_protocol protocol = IR_PROTOCOL_RC6;
+module_param(protocol, uint, 0444);
+MODULE_PARM_DESC(protocol, "IR protocol to use "
+ "(0 = RC5, 1 = NEC, 2 = RC6A, default)");
+
+static int invert; /* default = 0 */
+module_param(invert, bool, 0444);
+MODULE_PARM_DESC(invert, "Invert the signal from the IR receiver");
+
+static unsigned int wake_sc = 0x800F040C;
+module_param(wake_sc, uint, 0644);
+MODULE_PARM_DESC(wake_sc, "Scancode of the power-on IR command");
+
+static unsigned int wake_rc6mode = 6;
+module_param(wake_rc6mode, uint, 0644);
+MODULE_PARM_DESC(wake_rc6mode, "RC6 mode for the power-on command "
+ "(0 = 0, 6 = 6A, default)");
+
+
+
+/*****************************************************************************
+ *
+ * UTILITY FUNCTIONS
+ *
+ *****************************************************************************/
+
+/* Caller needs to hold wbcir_lock */
+static void
+wbcir_set_bits(unsigned long addr, u8 bits, u8 mask)
+{
+ u8 val;
+
+ val = inb(addr);
+ val = ((val & ~mask) | (bits & mask));
+ outb(val, addr);
+}
+
+/* Selects the register bank for the serial port */
+static inline void
+wbcir_select_bank(struct wbcir_data *data, enum wbcir_bank bank)
+{
+ outb(bank, data->sbase + WBCIR_REG_SP3_BSR);
+}
+
+static enum led_brightness
+wbcir_led_brightness_get(struct led_classdev *led_cdev)
+{
+ struct wbcir_data *data = container_of(led_cdev,
+ struct wbcir_data,
+ led);
+
+ if (inb(data->ebase + WBCIR_REG_ECEIR_CTS) & WBCIR_LED_ENABLE)
+ return LED_FULL;
+ else
+ return LED_OFF;
+}
+
+static void
+wbcir_led_brightness_set(struct led_classdev *led_cdev,
+ enum led_brightness brightness)
+{
+ struct wbcir_data *data = container_of(led_cdev,
+ struct wbcir_data,
+ led);
+
+ wbcir_set_bits(data->ebase + WBCIR_REG_ECEIR_CTS,
+ brightness == LED_OFF ? 0x00 : WBCIR_LED_ENABLE,
+ WBCIR_LED_ENABLE);
+}
+
+/* Manchester encodes bits to RC6 message cells (see wbcir_parse_rc6) */
+static u8
+wbcir_to_rc6cells(u8 val)
+{
+ u8 coded = 0x00;
+ int i;
+
+ val &= 0x0F;
+ for (i = 0; i < 4; i++) {
+ if (val & 0x01)
+ coded |= 0x02 << (i * 2);
+ else
+ coded |= 0x01 << (i * 2);
+ val >>= 1;
+ }
+
+ 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, int scancode, int *keycode)
+{
+ struct wbcir_data *data = input_get_drvdata(dev);
+
+ *keycode = (int)wbcir_do_getkeycode(data, (u32)scancode);
+ return 0;
+}
+
+static int
+wbcir_setkeycode(struct input_dev *dev, int sscancode, 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;
+ u32 scancode = (u32)sscancode;
+
+ if (keycode < 0 || keycode > KEY_MAX)
+ return -EINVAL;
+
+ 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;
+ }
+
+ /* 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);
+ input_sync(data->input_dev);
+ data->keypressed = 1;
+ data->last_keycode = keycode;
+ data->last_toggle = toggle;
+
+set_timer:
+ 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;
+}
+
+/* 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
+ *
+ *****************************************************************************/
+
+static irqreturn_t
+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[16];
+ int i;
+ unsigned int hw;
+
+ spin_lock_irqsave(&wbcir_lock, 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);
+ return IRQ_NONE;
+ }
+
+ if (status & WBCIR_IRQ_ERR)
+ data->irdata_error = 1;
+
+ if (!(status & WBCIR_IRQ_RX))
+ goto out;
+
+ /* Since RXHDLEV is set, at least 16 bytes are in the FIFO */
+ insb(data->sbase + WBCIR_REG_SP3_RXDATA, &irdata[0], 8);
+ insb(data->sbase + WBCIR_REG_SP3_RXDATA, &irdata[8], 8);
+
+ for (i = 0; i < sizeof(data); i++) {
+ hw = hweight8(irdata[i]);
+ if (hw > 4)
+ add_irdata_bit(data, 0);
+ else
+ add_irdata_bit(data, 1);
+
+ if (hw == 8)
+ data->idle_count++;
+ else
+ data->idle_count = 0;
+ }
+
+ if (data->idle_count > WBCIR_MAX_IDLE_BYTES) {
+ /* Drain the FIFO */
+ while (inb(data->sbase + WBCIR_REG_SP3_LSR) & WBCIR_RX_AVAIL)
+ inb(data->sbase + WBCIR_REG_SP3_RXDATA);
+
+ /* And set RXINACTIVE */
+ outb(WBCIR_RX_DISABLE, data->sbase + WBCIR_REG_SP3_ASCR);
+
+ 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);
+ data->idle_count = 0;
+ }
+
+out:
+ spin_unlock_irqrestore(&wbcir_lock, flags);
+ return IRQ_HANDLED;
+}
+
+
+
+/*****************************************************************************
+ *
+ * SUSPEND/RESUME FUNCTIONS
+ *
+ *****************************************************************************/
+
+static void
+wbcir_shutdown(struct pnp_dev *device)
+{
+ struct device *dev = &device->dev;
+ struct wbcir_data *data = pnp_get_drvdata(device);
+ int do_wake = 1;
+ u8 match[11];
+ u8 mask[11];
+ u8 rc6_csl = 0;
+ int i;
+
+ memset(match, 0, sizeof(match));
+ memset(mask, 0, sizeof(mask));
+
+ if (wake_sc == INVALID_SCANCODE || !device_may_wakeup(dev)) {
+ do_wake = 0;
+ goto finish;
+ }
+
+ switch (protocol) {
+ case IR_PROTOCOL_RC5:
+ if (wake_sc > 0xFFF) {
+ do_wake = 0;
+ dev_err(dev, "RC5 - Invalid wake scancode\n");
+ break;
+ }
+
+ /* Mask = 13 bits, ex toggle */
+ mask[0] = 0xFF;
+ mask[1] = 0x17;
+
+ match[0] = (wake_sc & 0x003F); /* 6 command bits */
+ match[0] |= (wake_sc & 0x0180) >> 1; /* 2 address bits */
+ match[1] = (wake_sc & 0x0E00) >> 9; /* 3 address bits */
+ if (!(wake_sc & 0x0040)) /* 2nd start bit */
+ match[1] |= 0x10;
+
+ break;
+
+ case IR_PROTOCOL_NEC:
+ if (wake_sc > 0xFFFFFF) {
+ do_wake = 0;
+ dev_err(dev, "NEC - Invalid wake scancode\n");
+ break;
+ }
+
+ mask[0] = mask[1] = mask[2] = mask[3] = 0xFF;
+
+ match[1] = bitrev8((wake_sc & 0xFF));
+ match[0] = ~match[1];
+
+ match[3] = bitrev8((wake_sc & 0xFF00) >> 8);
+ if (wake_sc > 0xFFFF)
+ match[2] = bitrev8((wake_sc & 0xFF0000) >> 16);
+ else
+ match[2] = ~match[3];
+
+ break;
+
+ case IR_PROTOCOL_RC6:
+
+ if (wake_rc6mode == 0) {
+ if (wake_sc > 0xFFFF) {
+ do_wake = 0;
+ dev_err(dev, "RC6 - Invalid wake scancode\n");
+ break;
+ }
+
+ /* Command */
+ match[0] = wbcir_to_rc6cells(wake_sc >> 0);
+ mask[0] = 0xFF;
+ match[1] = wbcir_to_rc6cells(wake_sc >> 4);
+ mask[1] = 0xFF;
+
+ /* Address */
+ match[2] = wbcir_to_rc6cells(wake_sc >> 8);
+ mask[2] = 0xFF;
+ match[3] = wbcir_to_rc6cells(wake_sc >> 12);
+ mask[3] = 0xFF;
+
+ /* Header */
+ match[4] = 0x50; /* mode1 = mode0 = 0, ignore toggle */
+ mask[4] = 0xF0;
+ match[5] = 0x09; /* start bit = 1, mode2 = 0 */
+ mask[5] = 0x0F;
+
+ rc6_csl = 44;
+
+ } else if (wake_rc6mode == 6) {
+ i = 0;
+
+ /* Command */
+ match[i] = wbcir_to_rc6cells(wake_sc >> 0);
+ mask[i++] = 0xFF;
+ match[i] = wbcir_to_rc6cells(wake_sc >> 4);
+ mask[i++] = 0xFF;
+
+ /* Address + Toggle */
+ match[i] = wbcir_to_rc6cells(wake_sc >> 8);
+ mask[i++] = 0xFF;
+ match[i] = wbcir_to_rc6cells(wake_sc >> 12);
+ mask[i++] = 0x3F;
+
+ /* Customer bits 7 - 0 */
+ match[i] = wbcir_to_rc6cells(wake_sc >> 16);
+ mask[i++] = 0xFF;
+ match[i] = wbcir_to_rc6cells(wake_sc >> 20);
+ mask[i++] = 0xFF;
+
+ if (wake_sc & 0x80000000) {
+ /* Customer range bit and bits 15 - 8 */
+ match[i] = wbcir_to_rc6cells(wake_sc >> 24);
+ mask[i++] = 0xFF;
+ match[i] = wbcir_to_rc6cells(wake_sc >> 28);
+ mask[i++] = 0xFF;
+ rc6_csl = 76;
+ } else if (wake_sc <= 0x007FFFFF) {
+ rc6_csl = 60;
+ } else {
+ do_wake = 0;
+ dev_err(dev, "RC6 - Invalid wake scancode\n");
+ break;
+ }
+
+ /* Header */
+ match[i] = 0x93; /* mode1 = mode0 = 1, submode = 0 */
+ mask[i++] = 0xFF;
+ match[i] = 0x0A; /* start bit = 1, mode2 = 1 */
+ mask[i++] = 0x0F;
+
+ } else {
+ do_wake = 0;
+ dev_err(dev, "RC6 - Invalid wake mode\n");
+ }
+
+ break;
+
+ default:
+ do_wake = 0;
+ break;
+ }
+
+finish:
+ if (do_wake) {
+ /* Set compare and compare mask */
+ wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_INDEX,
+ WBCIR_REGSEL_COMPARE | WBCIR_REG_ADDR0,
+ 0x3F);
+ outsb(data->wbase + WBCIR_REG_WCEIR_DATA, match, 11);
+ wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_INDEX,
+ WBCIR_REGSEL_MASK | WBCIR_REG_ADDR0,
+ 0x3F);
+ outsb(data->wbase + WBCIR_REG_WCEIR_DATA, mask, 11);
+
+ /* RC6 Compare String Len */
+ outb(rc6_csl, data->wbase + WBCIR_REG_WCEIR_CSL);
+
+ /* Clear status bits NEC_REP, BUFF, MSG_END, MATCH */
+ wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_STS, 0x17, 0x17);
+
+ /* Clear BUFF_EN, Clear END_EN, Set MATCH_EN */
+ wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_EV_EN, 0x01, 0x07);
+
+ /* Set CEIR_EN */
+ wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_CTL, 0x01, 0x01);
+
+ } else {
+ /* Clear BUFF_EN, Clear END_EN, Clear MATCH_EN */
+ wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_EV_EN, 0x00, 0x07);
+
+ /* Clear CEIR_EN */
+ wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_CTL, 0x00, 0x01);
+ }
+
+ /* Disable interrupts */
+ outb(WBCIR_IRQ_NONE, data->sbase + WBCIR_REG_SP3_IER);
+}
+
+static int
+wbcir_suspend(struct pnp_dev *device, pm_message_t state)
+{
+ wbcir_shutdown(device);
+ return 0;
+}
+
+static int
+wbcir_resume(struct pnp_dev *device)
+{
+ struct wbcir_data *data = pnp_get_drvdata(device);
+
+ /* Clear BUFF_EN, Clear END_EN, Clear MATCH_EN */
+ wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_EV_EN, 0x00, 0x07);
+
+ /* Clear CEIR_EN */
+ wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_CTL, 0x00, 0x01);
+
+ /* Enable interrupts */
+ wbcir_reset_irdata(data);
+ outb(WBCIR_IRQ_RX | WBCIR_IRQ_ERR, data->sbase + WBCIR_REG_SP3_IER);
+
+ return 0;
+}
+
+
+
+/*****************************************************************************
+ *
+ * SETUP/INIT FUNCTIONS
+ *
+ *****************************************************************************/
+
+static ssize_t
+wbcir_show_last_scancode(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct pnp_dev *device = container_of(dev, struct pnp_dev, dev);
+ struct wbcir_data *data = pnp_get_drvdata(device);
+ return sprintf(buf, "0x%08X\n", data->last_scancode);
+}
+
+static struct device_attribute dev_attr_last_scancode = {
+ .attr = {
+ .name = "last_scancode",
+ .mode = 0444,
+ },
+ .show = wbcir_show_last_scancode,
+ .store = NULL,
+
+};
+
+static struct attribute *wbcir_attributes[] = {
+ &dev_attr_last_scancode.attr,
+ NULL,
+};
+
+static struct attribute_group wbcir_attribute_group = {
+ .attrs = wbcir_attributes,
+};
+
+static void
+wbcir_cfg_ceir(struct wbcir_data *data)
+{
+ u8 tmp;
+
+ /* Set PROT_SEL, RX_INV, Clear CEIR_EN (needed for the led) */
+ tmp = protocol << 4;
+ if (invert)
+ tmp |= 0x08;
+ outb(tmp, data->wbase + WBCIR_REG_WCEIR_CTL);
+
+ /* Clear status bits NEC_REP, BUFF, MSG_END, MATCH */
+ wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_STS, 0x17, 0x17);
+
+ /* Clear BUFF_EN, Clear END_EN, Clear MATCH_EN */
+ wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_EV_EN, 0x00, 0x07);
+
+ /* Set RC5 cell time to correspond to 36 kHz */
+ wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_CFG1, 0x4A, 0x7F);
+
+ /* Set IRTX_INV */
+ if (invert)
+ outb(0x04, data->ebase + WBCIR_REG_ECEIR_CCTL);
+ else
+ outb(0x00, data->ebase + WBCIR_REG_ECEIR_CCTL);
+
+ /*
+ * Clear IR LED, set SP3 clock to 24Mhz
+ * set SP3_IRRX_SW to binary 01, helpfully not documented
+ */
+ outb(0x10, data->ebase + WBCIR_REG_ECEIR_CTS);
+}
+
+static int
+wbcir_probe(struct pnp_dev *device, const struct pnp_device_id *dev_id)
+{
+ struct device *dev = &device->dev;
+ struct wbcir_data *data;
+ int err;
+
+ if (!(pnp_port_len(device, 0) == EHFUNC_IOMEM_LEN &&
+ pnp_port_len(device, 1) == WAKEUP_IOMEM_LEN &&
+ pnp_port_len(device, 2) == SP_IOMEM_LEN)) {
+ dev_err(dev, "Invalid resources\n");
+ return -ENODEV;
+ }
+
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
+ if (!data) {
+ err = -ENOMEM;
+ goto exit;
+ }
+
+ pnp_set_drvdata(device, data);
+
+ data->ebase = pnp_port_start(device, 0);
+ data->wbase = pnp_port_start(device, 1);
+ data->sbase = pnp_port_start(device, 2);
+ data->irq = pnp_irq(device, 0);
+
+ if (data->wbase == 0 || data->ebase == 0 ||
+ data->sbase == 0 || data->irq == 0) {
+ err = -ENODEV;
+ dev_err(dev, "Invalid resources\n");
+ goto exit_free_data;
+ }
+
+ dev_info(&device->dev, "Found device "
+ "(w: 0x%lX, e: 0x%lX, s: 0x%lX, i: %u)\n",
+ data->wbase, data->ebase, data->sbase, data->irq);
+
+ if (!request_region(data->wbase, WAKEUP_IOMEM_LEN, DRVNAME)) {
+ dev_err(dev, "Region 0x%lx-0x%lx already in use!\n",
+ data->wbase, data->wbase + WAKEUP_IOMEM_LEN - 1);
+ err = -EBUSY;
+ goto exit_free_data;
+ }
+
+ if (!request_region(data->ebase, EHFUNC_IOMEM_LEN, DRVNAME)) {
+ dev_err(dev, "Region 0x%lx-0x%lx already in use!\n",
+ data->ebase, data->ebase + EHFUNC_IOMEM_LEN - 1);
+ err = -EBUSY;
+ goto exit_release_wbase;
+ }
+
+ if (!request_region(data->sbase, SP_IOMEM_LEN, DRVNAME)) {
+ dev_err(dev, "Region 0x%lx-0x%lx already in use!\n",
+ data->sbase, data->sbase + SP_IOMEM_LEN - 1);
+ err = -EBUSY;
+ goto exit_release_ebase;
+ }
+
+ err = request_irq(data->irq, wbcir_irq_handler,
+ IRQF_DISABLED, DRVNAME, device);
+ if (err) {
+ dev_err(dev, "Failed to claim IRQ %u\n", data->irq);
+ err = -EBUSY;
+ goto exit_release_sbase;
+ }
+
+ led_trigger_register_simple("cir-tx", &data->txtrigger);
+ if (!data->txtrigger) {
+ err = -ENOMEM;
+ goto exit_free_irq;
+ }
+
+ led_trigger_register_simple("cir-rx", &data->rxtrigger);
+ if (!data->rxtrigger) {
+ err = -ENOMEM;
+ goto exit_unregister_txtrigger;
+ }
+
+ data->led.name = "cir::activity";
+ data->led.default_trigger = "cir-rx";
+ data->led.brightness_set = wbcir_led_brightness_set;
+ data->led.brightness_get = wbcir_led_brightness_get;
+ err = led_classdev_register(&device->dev, &data->led);
+ if (err)
+ goto exit_unregister_rxtrigger;
+
+ data->input_dev = input_allocate_device();
+ if (!data->input_dev) {
+ err = -ENOMEM;
+ goto exit_unregister_led;
+ }
+
+ data->input_dev->evbit[0] = BIT(EV_KEY);
+ data->input_dev->name = WBCIR_ACPI_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_drvdata(data->input_dev, data);
+
+ err = input_register_device(data->input_dev);
+ if (err)
+ goto exit_free_input;
+
+ data->last_scancode = INVALID_SCANCODE;
+ err = sysfs_create_group(&device->dev.kobj, &wbcir_attribute_group);
+ if (err)
+ goto exit_unregister_input;
+
+ 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;
+ }
+ }
+
+ device_init_wakeup(&device->dev, 1);
+
+ wbcir_cfg_ceir(data);
+
+ /* Disable interrupts */
+ wbcir_select_bank(data, WBCIR_BANK_0);
+ outb(WBCIR_IRQ_NONE, data->sbase + WBCIR_REG_SP3_IER);
+
+ /* Enable extended mode */
+ wbcir_select_bank(data, WBCIR_BANK_2);
+ outb(WBCIR_EXT_ENABLE, data->sbase + WBCIR_REG_SP3_EXCR1);
+
+ /*
+ * Configure baud generator, IR data will be sampled at
+ * a bitrate of: (24Mhz * prescaler) / (divisor * 16).
+ *
+ * The ECIR registers include a flag to change the
+ * 24Mhz clock freq to 48Mhz.
+ */
+
+ /* prescaler 1.0, tx/rx fifo lvl 32 */
+ outb(0x35, data->sbase + WBCIR_REG_SP3_EXCR2);
+
+ /* Set baud divisor to generate one byte per bit/cell */
+ switch (protocol) {
+ case IR_PROTOCOL_RC5:
+ outb(0xA7, data->sbase + WBCIR_REG_SP3_BGDL);
+ break;
+ case IR_PROTOCOL_RC6:
+ outb(0x53, data->sbase + WBCIR_REG_SP3_BGDL);
+ break;
+ case IR_PROTOCOL_NEC:
+ outb(0x69, data->sbase + WBCIR_REG_SP3_BGDL);
+ break;
+ }
+ outb(0x00, data->sbase + WBCIR_REG_SP3_BGDH);
+
+ /* Set CEIR mode */
+ wbcir_select_bank(data, WBCIR_BANK_0);
+ outb(0xC0, data->sbase + WBCIR_REG_SP3_MCR);
+ inb(data->sbase + WBCIR_REG_SP3_LSR); /* Clear LSR */
+ inb(data->sbase + WBCIR_REG_SP3_MSR); /* Clear MSR */
+
+ /* Disable RX demod, run-length encoding/decoding, set freq span */
+ wbcir_select_bank(data, WBCIR_BANK_7);
+ outb(0x10, data->sbase + WBCIR_REG_SP3_RCCFG);
+
+ /* Disable timer */
+ wbcir_select_bank(data, WBCIR_BANK_4);
+ outb(0x00, data->sbase + WBCIR_REG_SP3_IRCR1);
+
+ /* Enable MSR interrupt, Clear AUX_IRX */
+ wbcir_select_bank(data, WBCIR_BANK_5);
+ outb(0x00, data->sbase + WBCIR_REG_SP3_IRCR2);
+
+ /* Disable CRC */
+ wbcir_select_bank(data, WBCIR_BANK_6);
+ outb(0x20, data->sbase + WBCIR_REG_SP3_IRCR3);
+
+ /* Set RX/TX (de)modulation freq, not really used */
+ wbcir_select_bank(data, WBCIR_BANK_7);
+ outb(0xF2, data->sbase + WBCIR_REG_SP3_IRRXDC);
+ outb(0x69, data->sbase + WBCIR_REG_SP3_IRTXMC);
+
+ /* Set invert and pin direction */
+ if (invert)
+ outb(0x10, data->sbase + WBCIR_REG_SP3_IRCFG4);
+ else
+ outb(0x00, data->sbase + WBCIR_REG_SP3_IRCFG4);
+
+ /* Set FIFO thresholds (RX = 16, TX = 7), reset RX/TX */
+ wbcir_select_bank(data, WBCIR_BANK_0);
+ outb(0x97, data->sbase + WBCIR_REG_SP3_FCR);
+
+ /* Clear AUX status bits */
+ outb(0xE0, data->sbase + WBCIR_REG_SP3_ASCR);
+
+ /* Enable interrupts */
+ wbcir_select_bank(data, WBCIR_BANK_0);
+ outb(WBCIR_IRQ_RX | WBCIR_IRQ_ERR, data->sbase + WBCIR_REG_SP3_IER);
+
+ 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);
+ }
+ }
+exit_unregister_input:
+ 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_unregister_led:
+ led_classdev_unregister(&data->led);
+exit_unregister_rxtrigger:
+ led_trigger_unregister_simple(data->rxtrigger);
+exit_unregister_txtrigger:
+ led_trigger_unregister_simple(data->txtrigger);
+exit_free_irq:
+ free_irq(data->irq, device);
+exit_release_sbase:
+ release_region(data->sbase, SP_IOMEM_LEN);
+exit_release_ebase:
+ release_region(data->ebase, EHFUNC_IOMEM_LEN);
+exit_release_wbase:
+ release_region(data->wbase, WAKEUP_IOMEM_LEN);
+exit_free_data:
+ kfree(data);
+ pnp_set_drvdata(device, NULL);
+exit:
+ return err;
+}
+
+static void
+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 */
+ wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_STS, 0x17, 0x17);
+
+ /* Clear CEIR_EN */
+ wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_CTL, 0x00, 0x01);
+
+ /* Clear BUFF_EN, END_EN, MATCH_EN */
+ wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_EV_EN, 0x00, 0x07);
+
+ sysfs_remove_group(&device->dev.kobj, &wbcir_attribute_group);
+
+ /* This will generate a keyup event if necessary */
+ input_unregister_device(data->input_dev);
+
+ led_trigger_unregister_simple(data->rxtrigger);
+ led_trigger_unregister_simple(data->txtrigger);
+ led_classdev_unregister(&data->led);
+
+ /* This is ok since &data->led isn't actually used */
+ wbcir_led_brightness_set(&data->led, LED_OFF);
+
+ release_region(data->wbase, WAKEUP_IOMEM_LEN);
+ 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);
+}
+
+static const struct pnp_device_id wbcir_ids[] = {
+ { "WEC1022", 0 },
+ { "", 0 }
+};
+MODULE_DEVICE_TABLE(pnp, wbcir_ids);
+
+static struct pnp_driver wbcir_driver = {
+ .name = WBCIR_ACPI_NAME,
+ .id_table = wbcir_ids,
+ .probe = wbcir_probe,
+ .remove = wbcir_remove,
+ .suspend = wbcir_suspend,
+ .resume = wbcir_resume,
+ .shutdown = wbcir_shutdown
+};
+
+static int __init
+wbcir_init(void)
+{
+ int ret;
+
+ switch (protocol) {
+ case IR_PROTOCOL_RC5:
+ case IR_PROTOCOL_NEC:
+ case IR_PROTOCOL_RC6:
+ break;
+ default:
+ printk(KERN_ERR DRVNAME ": Invalid protocol argument\n");
+ return -EINVAL;
+ }
+
+ ret = pnp_register_driver(&wbcir_driver);
+ if (ret)
+ printk(KERN_ERR DRVNAME ": Unable to register driver\n");
+
+ return ret;
+}
+
+static void __exit
+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(wbcir_exit);
+
+
--
David Härdeman
^ permalink raw reply [flat|nested] 27+ messages in thread
* [patch 2/2] Add a driver for the Winbond WPCD376I Consumer IR hardware
@ 2009-08-09 9:56 ` david
0 siblings, 0 replies; 27+ messages in thread
From: david @ 2009-08-09 9:56 UTC (permalink / raw)
To: linux-kernel
Cc: linux-input, jbarnes, akpm, bjorn.helgaas, randy.dunlap, david
[-- Attachment #1: add-a-driver-for-the-winbond-wpcd376i-cir-functionality --]
[-- Type: TEXT/PLAIN, Size: 48835 bytes --]
Add a driver for the the Consumer IR (CIR) functionality of the Winbond
WPCD376I chipset (found on e.g. Intel DG45FC motherboards).
Changes since the last version: converted from an ACPI to a PNP driver
using a patch provided by Bjorn Helgaas <bjorn.helgaas@hp.com>.
Signed-off-by: David Härdeman <david@hardeman.nu>
Reviewed-by: Jesse Barnes <jbarnes@virtuousgeek.org>
Index: linux-2.6/MAINTAINERS
===================================================================
--- linux-2.6.orig/MAINTAINERS 2009-07-25 21:18:57.000000000 +0200
+++ linux-2.6/MAINTAINERS 2009-07-25 21:19:28.000000000 +0200
@@ -6448,6 +6448,12 @@
S: Maintained
F: drivers/scsi/wd7000.c
+WINBOND CIR DRIVER
+P: David Härdeman
+M: david@hardeman.nu
+S: Maintained
+F: drivers/input/misc/winbond-cir.c
+
WIMAX STACK
P: Inaky Perez-Gonzalez
M: inaky.perez-gonzalez@intel.com
Index: linux-2.6/drivers/input/misc/Kconfig
===================================================================
--- linux-2.6.orig/drivers/input/misc/Kconfig 2009-07-25 21:18:57.000000000 +0200
+++ linux-2.6/drivers/input/misc/Kconfig 2009-08-07 21:45:41.000000000 +0200
@@ -222,6 +222,22 @@
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 && ACPI
+ select LEDS_CLASS
+ 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
Index: linux-2.6/drivers/input/misc/Makefile
===================================================================
--- linux-2.6.orig/drivers/input/misc/Makefile 2009-07-25 21:18:57.000000000 +0200
+++ linux-2.6/drivers/input/misc/Makefile 2009-07-25 21:19:28.000000000 +0200
@@ -19,6 +19,7 @@
obj-$(CONFIG_INPUT_PCSPKR) += pcspkr.o
obj-$(CONFIG_INPUT_POWERMATE) += powermate.o
obj-$(CONFIG_INPUT_RB532_BUTTON) += rb532_button.o
+obj-$(CONFIG_INPUT_WINBOND_CIR) += winbond-cir.o
obj-$(CONFIG_INPUT_GPIO_ROTARY_ENCODER) += rotary_encoder.o
obj-$(CONFIG_INPUT_SGI_BTNS) += sgi_btns.o
obj-$(CONFIG_INPUT_SPARCSPKR) += sparcspkr.o
Index: linux-2.6/drivers/input/misc/winbond-cir.c
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6/drivers/input/misc/winbond-cir.c 2009-07-25 21:19:28.000000000 +0200
@@ -0,0 +1,1644 @@
+/*
+ * winbond-cir.c - Driver for the Consumer IR functionality of Winbond
+ * SuperI/O chips.
+ *
+ * Currently supports the Winbond WPCD376i chip (PNP id WEC1022), but
+ * could probably support others (Winbond WEC102X, NatSemi, etc)
+ * with minor modifications.
+ *
+ * Original Author: David Härdeman <david@hardeman.nu>
+ * Copyright (C) 2009 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
+ * of the bugs.
+ *
+ * Written using:
+ * o Winbond WPCD376I datasheet helpfully provided by Jesse Barnes at Intel
+ * o NatSemi PC87338/PC97338 datasheet (for the serial port stuff)
+ * 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)
+ *
+ * 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
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#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>
+
+#define DRVNAME "winbond-cir"
+
+/* CEIR Wake-Up Registers, relative to data->wbase */
+#define WBCIR_REG_WCEIR_CTL 0x03 /* CEIR Receiver Control */
+#define WBCIR_REG_WCEIR_STS 0x04 /* CEIR Receiver Status */
+#define WBCIR_REG_WCEIR_EV_EN 0x05 /* CEIR Receiver Event Enable */
+#define WBCIR_REG_WCEIR_CNTL 0x06 /* CEIR Receiver Counter Low */
+#define WBCIR_REG_WCEIR_CNTH 0x07 /* CEIR Receiver Counter High */
+#define WBCIR_REG_WCEIR_INDEX 0x08 /* CEIR Receiver Index */
+#define WBCIR_REG_WCEIR_DATA 0x09 /* CEIR Receiver Data */
+#define WBCIR_REG_WCEIR_CSL 0x0A /* CEIR Re. Compare Strlen */
+#define WBCIR_REG_WCEIR_CFG1 0x0B /* CEIR Re. Configuration 1 */
+#define WBCIR_REG_WCEIR_CFG2 0x0C /* CEIR Re. Configuration 2 */
+
+/* CEIR Enhanced Functionality Registers, relative to data->ebase */
+#define WBCIR_REG_ECEIR_CTS 0x00 /* Enhanced IR Control Status */
+#define WBCIR_REG_ECEIR_CCTL 0x01 /* Infrared Counter Control */
+#define WBCIR_REG_ECEIR_CNT_LO 0x02 /* Infrared Counter LSB */
+#define WBCIR_REG_ECEIR_CNT_HI 0x03 /* Infrared Counter MSB */
+#define WBCIR_REG_ECEIR_IREM 0x04 /* Infrared Emitter Status */
+
+/* SP3 Banked Registers, relative to data->sbase */
+#define WBCIR_REG_SP3_BSR 0x03 /* Bank Select, all banks */
+ /* Bank 0 */
+#define WBCIR_REG_SP3_RXDATA 0x00 /* FIFO RX data (r) */
+#define WBCIR_REG_SP3_TXDATA 0x00 /* FIFO TX data (w) */
+#define WBCIR_REG_SP3_IER 0x01 /* Interrupt Enable */
+#define WBCIR_REG_SP3_EIR 0x02 /* Event Identification (r) */
+#define WBCIR_REG_SP3_FCR 0x02 /* FIFO Control (w) */
+#define WBCIR_REG_SP3_MCR 0x04 /* Mode Control */
+#define WBCIR_REG_SP3_LSR 0x05 /* Link Status */
+#define WBCIR_REG_SP3_MSR 0x06 /* Modem Status */
+#define WBCIR_REG_SP3_ASCR 0x07 /* Aux Status and Control */
+ /* Bank 2 */
+#define WBCIR_REG_SP3_BGDL 0x00 /* Baud Divisor LSB */
+#define WBCIR_REG_SP3_BGDH 0x01 /* Baud Divisor MSB */
+#define WBCIR_REG_SP3_EXCR1 0x02 /* Extended Control 1 */
+#define WBCIR_REG_SP3_EXCR2 0x04 /* Extended Control 2 */
+#define WBCIR_REG_SP3_TXFLV 0x06 /* TX FIFO Level */
+#define WBCIR_REG_SP3_RXFLV 0x07 /* RX FIFO Level */
+ /* Bank 3 */
+#define WBCIR_REG_SP3_MRID 0x00 /* Module Identification */
+#define WBCIR_REG_SP3_SH_LCR 0x01 /* LCR Shadow */
+#define WBCIR_REG_SP3_SH_FCR 0x02 /* FCR Shadow */
+ /* Bank 4 */
+#define WBCIR_REG_SP3_IRCR1 0x02 /* Infrared Control 1 */
+ /* Bank 5 */
+#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 */
+ /* Bank 7 */
+#define WBCIR_REG_SP3_IRRXDC 0x00 /* IR RX Demod Control */
+#define WBCIR_REG_SP3_IRTXMC 0x01 /* IR TX Mod Control */
+#define WBCIR_REG_SP3_RCCFG 0x02 /* CEIR Config */
+#define WBCIR_REG_SP3_IRCFG1 0x04 /* Infrared Config 1 */
+#define WBCIR_REG_SP3_IRCFG4 0x07 /* Infrared Config 4 */
+
+/*
+ * Magic values follow
+ */
+
+/* No interrupts for WBCIR_REG_SP3_IER and WBCIR_REG_SP3_EIR */
+#define WBCIR_IRQ_NONE 0x00
+/* RX data bit for WBCIR_REG_SP3_IER and WBCIR_REG_SP3_EIR */
+#define WBCIR_IRQ_RX 0x01
+/* Over/Under-flow bit for WBCIR_REG_SP3_IER and WBCIR_REG_SP3_EIR */
+#define WBCIR_IRQ_ERR 0x04
+/* Led enable/disable bit for WBCIR_REG_ECEIR_CTS */
+#define WBCIR_LED_ENABLE 0x80
+/* RX data available bit for WBCIR_REG_SP3_LSR */
+#define WBCIR_RX_AVAIL 0x01
+/* RX disable bit for WBCIR_REG_SP3_ASCR */
+#define WBCIR_RX_DISABLE 0x20
+/* Extended mode enable bit for WBCIR_REG_SP3_EXCR1 */
+#define WBCIR_EXT_ENABLE 0x01
+/* Select compare register in WBCIR_REG_WCEIR_INDEX (bits 5 & 6) */
+#define WBCIR_REGSEL_COMPARE 0x10
+/* Select mask register in WBCIR_REG_WCEIR_INDEX (bits 5 & 6) */
+#define WBCIR_REGSEL_MASK 0x20
+/* Starting address of selected register in WBCIR_REG_WCEIR_INDEX */
+#define WBCIR_REG_ADDR0 0x00
+
+/* Valid banks for the SP3 UART */
+enum wbcir_bank {
+ WBCIR_BANK_0 = 0x00,
+ WBCIR_BANK_1 = 0x80,
+ WBCIR_BANK_2 = 0xE0,
+ WBCIR_BANK_3 = 0xE4,
+ WBCIR_BANK_4 = 0xE8,
+ WBCIR_BANK_5 = 0xEC,
+ WBCIR_BANK_6 = 0xF0,
+ WBCIR_BANK_7 = 0xF4,
+};
+
+/* Supported IR Protocols */
+enum wbcir_protocol {
+ IR_PROTOCOL_RC5 = 0x0,
+ IR_PROTOCOL_NEC = 0x1,
+ IR_PROTOCOL_RC6 = 0x2,
+};
+
+/* Misc */
+#define WBCIR_ACPI_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_0 },
+ { 0x800F0401, KEY_1 },
+ { 0x800F0402, KEY_2 },
+ { 0x800F0403, KEY_3 },
+ { 0x800F0404, KEY_4 },
+ { 0x800F0405, KEY_5 },
+ { 0x800F0406, KEY_6 },
+ { 0x800F0407, KEY_7 },
+ { 0x800F0408, KEY_8 },
+ { 0x800F0409, KEY_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 */
+struct wbcir_data {
+ 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 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;
+};
+
+static enum wbcir_protocol protocol = IR_PROTOCOL_RC6;
+module_param(protocol, uint, 0444);
+MODULE_PARM_DESC(protocol, "IR protocol to use "
+ "(0 = RC5, 1 = NEC, 2 = RC6A, default)");
+
+static int invert; /* default = 0 */
+module_param(invert, bool, 0444);
+MODULE_PARM_DESC(invert, "Invert the signal from the IR receiver");
+
+static unsigned int wake_sc = 0x800F040C;
+module_param(wake_sc, uint, 0644);
+MODULE_PARM_DESC(wake_sc, "Scancode of the power-on IR command");
+
+static unsigned int wake_rc6mode = 6;
+module_param(wake_rc6mode, uint, 0644);
+MODULE_PARM_DESC(wake_rc6mode, "RC6 mode for the power-on command "
+ "(0 = 0, 6 = 6A, default)");
+
+
+
+/*****************************************************************************
+ *
+ * UTILITY FUNCTIONS
+ *
+ *****************************************************************************/
+
+/* Caller needs to hold wbcir_lock */
+static void
+wbcir_set_bits(unsigned long addr, u8 bits, u8 mask)
+{
+ u8 val;
+
+ val = inb(addr);
+ val = ((val & ~mask) | (bits & mask));
+ outb(val, addr);
+}
+
+/* Selects the register bank for the serial port */
+static inline void
+wbcir_select_bank(struct wbcir_data *data, enum wbcir_bank bank)
+{
+ outb(bank, data->sbase + WBCIR_REG_SP3_BSR);
+}
+
+static enum led_brightness
+wbcir_led_brightness_get(struct led_classdev *led_cdev)
+{
+ struct wbcir_data *data = container_of(led_cdev,
+ struct wbcir_data,
+ led);
+
+ if (inb(data->ebase + WBCIR_REG_ECEIR_CTS) & WBCIR_LED_ENABLE)
+ return LED_FULL;
+ else
+ return LED_OFF;
+}
+
+static void
+wbcir_led_brightness_set(struct led_classdev *led_cdev,
+ enum led_brightness brightness)
+{
+ struct wbcir_data *data = container_of(led_cdev,
+ struct wbcir_data,
+ led);
+
+ wbcir_set_bits(data->ebase + WBCIR_REG_ECEIR_CTS,
+ brightness == LED_OFF ? 0x00 : WBCIR_LED_ENABLE,
+ WBCIR_LED_ENABLE);
+}
+
+/* Manchester encodes bits to RC6 message cells (see wbcir_parse_rc6) */
+static u8
+wbcir_to_rc6cells(u8 val)
+{
+ u8 coded = 0x00;
+ int i;
+
+ val &= 0x0F;
+ for (i = 0; i < 4; i++) {
+ if (val & 0x01)
+ coded |= 0x02 << (i * 2);
+ else
+ coded |= 0x01 << (i * 2);
+ val >>= 1;
+ }
+
+ 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, int scancode, int *keycode)
+{
+ struct wbcir_data *data = input_get_drvdata(dev);
+
+ *keycode = (int)wbcir_do_getkeycode(data, (u32)scancode);
+ return 0;
+}
+
+static int
+wbcir_setkeycode(struct input_dev *dev, int sscancode, 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;
+ u32 scancode = (u32)sscancode;
+
+ if (keycode < 0 || keycode > KEY_MAX)
+ return -EINVAL;
+
+ 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;
+ }
+
+ /* 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);
+ input_sync(data->input_dev);
+ data->keypressed = 1;
+ data->last_keycode = keycode;
+ data->last_toggle = toggle;
+
+set_timer:
+ 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;
+}
+
+/* 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
+ *
+ *****************************************************************************/
+
+static irqreturn_t
+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[16];
+ int i;
+ unsigned int hw;
+
+ spin_lock_irqsave(&wbcir_lock, 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);
+ return IRQ_NONE;
+ }
+
+ if (status & WBCIR_IRQ_ERR)
+ data->irdata_error = 1;
+
+ if (!(status & WBCIR_IRQ_RX))
+ goto out;
+
+ /* Since RXHDLEV is set, at least 16 bytes are in the FIFO */
+ insb(data->sbase + WBCIR_REG_SP3_RXDATA, &irdata[0], 8);
+ insb(data->sbase + WBCIR_REG_SP3_RXDATA, &irdata[8], 8);
+
+ for (i = 0; i < sizeof(data); i++) {
+ hw = hweight8(irdata[i]);
+ if (hw > 4)
+ add_irdata_bit(data, 0);
+ else
+ add_irdata_bit(data, 1);
+
+ if (hw == 8)
+ data->idle_count++;
+ else
+ data->idle_count = 0;
+ }
+
+ if (data->idle_count > WBCIR_MAX_IDLE_BYTES) {
+ /* Drain the FIFO */
+ while (inb(data->sbase + WBCIR_REG_SP3_LSR) & WBCIR_RX_AVAIL)
+ inb(data->sbase + WBCIR_REG_SP3_RXDATA);
+
+ /* And set RXINACTIVE */
+ outb(WBCIR_RX_DISABLE, data->sbase + WBCIR_REG_SP3_ASCR);
+
+ 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);
+ data->idle_count = 0;
+ }
+
+out:
+ spin_unlock_irqrestore(&wbcir_lock, flags);
+ return IRQ_HANDLED;
+}
+
+
+
+/*****************************************************************************
+ *
+ * SUSPEND/RESUME FUNCTIONS
+ *
+ *****************************************************************************/
+
+static void
+wbcir_shutdown(struct pnp_dev *device)
+{
+ struct device *dev = &device->dev;
+ struct wbcir_data *data = pnp_get_drvdata(device);
+ int do_wake = 1;
+ u8 match[11];
+ u8 mask[11];
+ u8 rc6_csl = 0;
+ int i;
+
+ memset(match, 0, sizeof(match));
+ memset(mask, 0, sizeof(mask));
+
+ if (wake_sc == INVALID_SCANCODE || !device_may_wakeup(dev)) {
+ do_wake = 0;
+ goto finish;
+ }
+
+ switch (protocol) {
+ case IR_PROTOCOL_RC5:
+ if (wake_sc > 0xFFF) {
+ do_wake = 0;
+ dev_err(dev, "RC5 - Invalid wake scancode\n");
+ break;
+ }
+
+ /* Mask = 13 bits, ex toggle */
+ mask[0] = 0xFF;
+ mask[1] = 0x17;
+
+ match[0] = (wake_sc & 0x003F); /* 6 command bits */
+ match[0] |= (wake_sc & 0x0180) >> 1; /* 2 address bits */
+ match[1] = (wake_sc & 0x0E00) >> 9; /* 3 address bits */
+ if (!(wake_sc & 0x0040)) /* 2nd start bit */
+ match[1] |= 0x10;
+
+ break;
+
+ case IR_PROTOCOL_NEC:
+ if (wake_sc > 0xFFFFFF) {
+ do_wake = 0;
+ dev_err(dev, "NEC - Invalid wake scancode\n");
+ break;
+ }
+
+ mask[0] = mask[1] = mask[2] = mask[3] = 0xFF;
+
+ match[1] = bitrev8((wake_sc & 0xFF));
+ match[0] = ~match[1];
+
+ match[3] = bitrev8((wake_sc & 0xFF00) >> 8);
+ if (wake_sc > 0xFFFF)
+ match[2] = bitrev8((wake_sc & 0xFF0000) >> 16);
+ else
+ match[2] = ~match[3];
+
+ break;
+
+ case IR_PROTOCOL_RC6:
+
+ if (wake_rc6mode == 0) {
+ if (wake_sc > 0xFFFF) {
+ do_wake = 0;
+ dev_err(dev, "RC6 - Invalid wake scancode\n");
+ break;
+ }
+
+ /* Command */
+ match[0] = wbcir_to_rc6cells(wake_sc >> 0);
+ mask[0] = 0xFF;
+ match[1] = wbcir_to_rc6cells(wake_sc >> 4);
+ mask[1] = 0xFF;
+
+ /* Address */
+ match[2] = wbcir_to_rc6cells(wake_sc >> 8);
+ mask[2] = 0xFF;
+ match[3] = wbcir_to_rc6cells(wake_sc >> 12);
+ mask[3] = 0xFF;
+
+ /* Header */
+ match[4] = 0x50; /* mode1 = mode0 = 0, ignore toggle */
+ mask[4] = 0xF0;
+ match[5] = 0x09; /* start bit = 1, mode2 = 0 */
+ mask[5] = 0x0F;
+
+ rc6_csl = 44;
+
+ } else if (wake_rc6mode == 6) {
+ i = 0;
+
+ /* Command */
+ match[i] = wbcir_to_rc6cells(wake_sc >> 0);
+ mask[i++] = 0xFF;
+ match[i] = wbcir_to_rc6cells(wake_sc >> 4);
+ mask[i++] = 0xFF;
+
+ /* Address + Toggle */
+ match[i] = wbcir_to_rc6cells(wake_sc >> 8);
+ mask[i++] = 0xFF;
+ match[i] = wbcir_to_rc6cells(wake_sc >> 12);
+ mask[i++] = 0x3F;
+
+ /* Customer bits 7 - 0 */
+ match[i] = wbcir_to_rc6cells(wake_sc >> 16);
+ mask[i++] = 0xFF;
+ match[i] = wbcir_to_rc6cells(wake_sc >> 20);
+ mask[i++] = 0xFF;
+
+ if (wake_sc & 0x80000000) {
+ /* Customer range bit and bits 15 - 8 */
+ match[i] = wbcir_to_rc6cells(wake_sc >> 24);
+ mask[i++] = 0xFF;
+ match[i] = wbcir_to_rc6cells(wake_sc >> 28);
+ mask[i++] = 0xFF;
+ rc6_csl = 76;
+ } else if (wake_sc <= 0x007FFFFF) {
+ rc6_csl = 60;
+ } else {
+ do_wake = 0;
+ dev_err(dev, "RC6 - Invalid wake scancode\n");
+ break;
+ }
+
+ /* Header */
+ match[i] = 0x93; /* mode1 = mode0 = 1, submode = 0 */
+ mask[i++] = 0xFF;
+ match[i] = 0x0A; /* start bit = 1, mode2 = 1 */
+ mask[i++] = 0x0F;
+
+ } else {
+ do_wake = 0;
+ dev_err(dev, "RC6 - Invalid wake mode\n");
+ }
+
+ break;
+
+ default:
+ do_wake = 0;
+ break;
+ }
+
+finish:
+ if (do_wake) {
+ /* Set compare and compare mask */
+ wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_INDEX,
+ WBCIR_REGSEL_COMPARE | WBCIR_REG_ADDR0,
+ 0x3F);
+ outsb(data->wbase + WBCIR_REG_WCEIR_DATA, match, 11);
+ wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_INDEX,
+ WBCIR_REGSEL_MASK | WBCIR_REG_ADDR0,
+ 0x3F);
+ outsb(data->wbase + WBCIR_REG_WCEIR_DATA, mask, 11);
+
+ /* RC6 Compare String Len */
+ outb(rc6_csl, data->wbase + WBCIR_REG_WCEIR_CSL);
+
+ /* Clear status bits NEC_REP, BUFF, MSG_END, MATCH */
+ wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_STS, 0x17, 0x17);
+
+ /* Clear BUFF_EN, Clear END_EN, Set MATCH_EN */
+ wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_EV_EN, 0x01, 0x07);
+
+ /* Set CEIR_EN */
+ wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_CTL, 0x01, 0x01);
+
+ } else {
+ /* Clear BUFF_EN, Clear END_EN, Clear MATCH_EN */
+ wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_EV_EN, 0x00, 0x07);
+
+ /* Clear CEIR_EN */
+ wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_CTL, 0x00, 0x01);
+ }
+
+ /* Disable interrupts */
+ outb(WBCIR_IRQ_NONE, data->sbase + WBCIR_REG_SP3_IER);
+}
+
+static int
+wbcir_suspend(struct pnp_dev *device, pm_message_t state)
+{
+ wbcir_shutdown(device);
+ return 0;
+}
+
+static int
+wbcir_resume(struct pnp_dev *device)
+{
+ struct wbcir_data *data = pnp_get_drvdata(device);
+
+ /* Clear BUFF_EN, Clear END_EN, Clear MATCH_EN */
+ wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_EV_EN, 0x00, 0x07);
+
+ /* Clear CEIR_EN */
+ wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_CTL, 0x00, 0x01);
+
+ /* Enable interrupts */
+ wbcir_reset_irdata(data);
+ outb(WBCIR_IRQ_RX | WBCIR_IRQ_ERR, data->sbase + WBCIR_REG_SP3_IER);
+
+ return 0;
+}
+
+
+
+/*****************************************************************************
+ *
+ * SETUP/INIT FUNCTIONS
+ *
+ *****************************************************************************/
+
+static ssize_t
+wbcir_show_last_scancode(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct pnp_dev *device = container_of(dev, struct pnp_dev, dev);
+ struct wbcir_data *data = pnp_get_drvdata(device);
+ return sprintf(buf, "0x%08X\n", data->last_scancode);
+}
+
+static struct device_attribute dev_attr_last_scancode = {
+ .attr = {
+ .name = "last_scancode",
+ .mode = 0444,
+ },
+ .show = wbcir_show_last_scancode,
+ .store = NULL,
+
+};
+
+static struct attribute *wbcir_attributes[] = {
+ &dev_attr_last_scancode.attr,
+ NULL,
+};
+
+static struct attribute_group wbcir_attribute_group = {
+ .attrs = wbcir_attributes,
+};
+
+static void
+wbcir_cfg_ceir(struct wbcir_data *data)
+{
+ u8 tmp;
+
+ /* Set PROT_SEL, RX_INV, Clear CEIR_EN (needed for the led) */
+ tmp = protocol << 4;
+ if (invert)
+ tmp |= 0x08;
+ outb(tmp, data->wbase + WBCIR_REG_WCEIR_CTL);
+
+ /* Clear status bits NEC_REP, BUFF, MSG_END, MATCH */
+ wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_STS, 0x17, 0x17);
+
+ /* Clear BUFF_EN, Clear END_EN, Clear MATCH_EN */
+ wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_EV_EN, 0x00, 0x07);
+
+ /* Set RC5 cell time to correspond to 36 kHz */
+ wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_CFG1, 0x4A, 0x7F);
+
+ /* Set IRTX_INV */
+ if (invert)
+ outb(0x04, data->ebase + WBCIR_REG_ECEIR_CCTL);
+ else
+ outb(0x00, data->ebase + WBCIR_REG_ECEIR_CCTL);
+
+ /*
+ * Clear IR LED, set SP3 clock to 24Mhz
+ * set SP3_IRRX_SW to binary 01, helpfully not documented
+ */
+ outb(0x10, data->ebase + WBCIR_REG_ECEIR_CTS);
+}
+
+static int
+wbcir_probe(struct pnp_dev *device, const struct pnp_device_id *dev_id)
+{
+ struct device *dev = &device->dev;
+ struct wbcir_data *data;
+ int err;
+
+ if (!(pnp_port_len(device, 0) == EHFUNC_IOMEM_LEN &&
+ pnp_port_len(device, 1) == WAKEUP_IOMEM_LEN &&
+ pnp_port_len(device, 2) == SP_IOMEM_LEN)) {
+ dev_err(dev, "Invalid resources\n");
+ return -ENODEV;
+ }
+
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
+ if (!data) {
+ err = -ENOMEM;
+ goto exit;
+ }
+
+ pnp_set_drvdata(device, data);
+
+ data->ebase = pnp_port_start(device, 0);
+ data->wbase = pnp_port_start(device, 1);
+ data->sbase = pnp_port_start(device, 2);
+ data->irq = pnp_irq(device, 0);
+
+ if (data->wbase == 0 || data->ebase == 0 ||
+ data->sbase == 0 || data->irq == 0) {
+ err = -ENODEV;
+ dev_err(dev, "Invalid resources\n");
+ goto exit_free_data;
+ }
+
+ dev_info(&device->dev, "Found device "
+ "(w: 0x%lX, e: 0x%lX, s: 0x%lX, i: %u)\n",
+ data->wbase, data->ebase, data->sbase, data->irq);
+
+ if (!request_region(data->wbase, WAKEUP_IOMEM_LEN, DRVNAME)) {
+ dev_err(dev, "Region 0x%lx-0x%lx already in use!\n",
+ data->wbase, data->wbase + WAKEUP_IOMEM_LEN - 1);
+ err = -EBUSY;
+ goto exit_free_data;
+ }
+
+ if (!request_region(data->ebase, EHFUNC_IOMEM_LEN, DRVNAME)) {
+ dev_err(dev, "Region 0x%lx-0x%lx already in use!\n",
+ data->ebase, data->ebase + EHFUNC_IOMEM_LEN - 1);
+ err = -EBUSY;
+ goto exit_release_wbase;
+ }
+
+ if (!request_region(data->sbase, SP_IOMEM_LEN, DRVNAME)) {
+ dev_err(dev, "Region 0x%lx-0x%lx already in use!\n",
+ data->sbase, data->sbase + SP_IOMEM_LEN - 1);
+ err = -EBUSY;
+ goto exit_release_ebase;
+ }
+
+ err = request_irq(data->irq, wbcir_irq_handler,
+ IRQF_DISABLED, DRVNAME, device);
+ if (err) {
+ dev_err(dev, "Failed to claim IRQ %u\n", data->irq);
+ err = -EBUSY;
+ goto exit_release_sbase;
+ }
+
+ led_trigger_register_simple("cir-tx", &data->txtrigger);
+ if (!data->txtrigger) {
+ err = -ENOMEM;
+ goto exit_free_irq;
+ }
+
+ led_trigger_register_simple("cir-rx", &data->rxtrigger);
+ if (!data->rxtrigger) {
+ err = -ENOMEM;
+ goto exit_unregister_txtrigger;
+ }
+
+ data->led.name = "cir::activity";
+ data->led.default_trigger = "cir-rx";
+ data->led.brightness_set = wbcir_led_brightness_set;
+ data->led.brightness_get = wbcir_led_brightness_get;
+ err = led_classdev_register(&device->dev, &data->led);
+ if (err)
+ goto exit_unregister_rxtrigger;
+
+ data->input_dev = input_allocate_device();
+ if (!data->input_dev) {
+ err = -ENOMEM;
+ goto exit_unregister_led;
+ }
+
+ data->input_dev->evbit[0] = BIT(EV_KEY);
+ data->input_dev->name = WBCIR_ACPI_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_drvdata(data->input_dev, data);
+
+ err = input_register_device(data->input_dev);
+ if (err)
+ goto exit_free_input;
+
+ data->last_scancode = INVALID_SCANCODE;
+ err = sysfs_create_group(&device->dev.kobj, &wbcir_attribute_group);
+ if (err)
+ goto exit_unregister_input;
+
+ 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;
+ }
+ }
+
+ device_init_wakeup(&device->dev, 1);
+
+ wbcir_cfg_ceir(data);
+
+ /* Disable interrupts */
+ wbcir_select_bank(data, WBCIR_BANK_0);
+ outb(WBCIR_IRQ_NONE, data->sbase + WBCIR_REG_SP3_IER);
+
+ /* Enable extended mode */
+ wbcir_select_bank(data, WBCIR_BANK_2);
+ outb(WBCIR_EXT_ENABLE, data->sbase + WBCIR_REG_SP3_EXCR1);
+
+ /*
+ * Configure baud generator, IR data will be sampled at
+ * a bitrate of: (24Mhz * prescaler) / (divisor * 16).
+ *
+ * The ECIR registers include a flag to change the
+ * 24Mhz clock freq to 48Mhz.
+ */
+
+ /* prescaler 1.0, tx/rx fifo lvl 32 */
+ outb(0x35, data->sbase + WBCIR_REG_SP3_EXCR2);
+
+ /* Set baud divisor to generate one byte per bit/cell */
+ switch (protocol) {
+ case IR_PROTOCOL_RC5:
+ outb(0xA7, data->sbase + WBCIR_REG_SP3_BGDL);
+ break;
+ case IR_PROTOCOL_RC6:
+ outb(0x53, data->sbase + WBCIR_REG_SP3_BGDL);
+ break;
+ case IR_PROTOCOL_NEC:
+ outb(0x69, data->sbase + WBCIR_REG_SP3_BGDL);
+ break;
+ }
+ outb(0x00, data->sbase + WBCIR_REG_SP3_BGDH);
+
+ /* Set CEIR mode */
+ wbcir_select_bank(data, WBCIR_BANK_0);
+ outb(0xC0, data->sbase + WBCIR_REG_SP3_MCR);
+ inb(data->sbase + WBCIR_REG_SP3_LSR); /* Clear LSR */
+ inb(data->sbase + WBCIR_REG_SP3_MSR); /* Clear MSR */
+
+ /* Disable RX demod, run-length encoding/decoding, set freq span */
+ wbcir_select_bank(data, WBCIR_BANK_7);
+ outb(0x10, data->sbase + WBCIR_REG_SP3_RCCFG);
+
+ /* Disable timer */
+ wbcir_select_bank(data, WBCIR_BANK_4);
+ outb(0x00, data->sbase + WBCIR_REG_SP3_IRCR1);
+
+ /* Enable MSR interrupt, Clear AUX_IRX */
+ wbcir_select_bank(data, WBCIR_BANK_5);
+ outb(0x00, data->sbase + WBCIR_REG_SP3_IRCR2);
+
+ /* Disable CRC */
+ wbcir_select_bank(data, WBCIR_BANK_6);
+ outb(0x20, data->sbase + WBCIR_REG_SP3_IRCR3);
+
+ /* Set RX/TX (de)modulation freq, not really used */
+ wbcir_select_bank(data, WBCIR_BANK_7);
+ outb(0xF2, data->sbase + WBCIR_REG_SP3_IRRXDC);
+ outb(0x69, data->sbase + WBCIR_REG_SP3_IRTXMC);
+
+ /* Set invert and pin direction */
+ if (invert)
+ outb(0x10, data->sbase + WBCIR_REG_SP3_IRCFG4);
+ else
+ outb(0x00, data->sbase + WBCIR_REG_SP3_IRCFG4);
+
+ /* Set FIFO thresholds (RX = 16, TX = 7), reset RX/TX */
+ wbcir_select_bank(data, WBCIR_BANK_0);
+ outb(0x97, data->sbase + WBCIR_REG_SP3_FCR);
+
+ /* Clear AUX status bits */
+ outb(0xE0, data->sbase + WBCIR_REG_SP3_ASCR);
+
+ /* Enable interrupts */
+ wbcir_select_bank(data, WBCIR_BANK_0);
+ outb(WBCIR_IRQ_RX | WBCIR_IRQ_ERR, data->sbase + WBCIR_REG_SP3_IER);
+
+ 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);
+ }
+ }
+exit_unregister_input:
+ 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_unregister_led:
+ led_classdev_unregister(&data->led);
+exit_unregister_rxtrigger:
+ led_trigger_unregister_simple(data->rxtrigger);
+exit_unregister_txtrigger:
+ led_trigger_unregister_simple(data->txtrigger);
+exit_free_irq:
+ free_irq(data->irq, device);
+exit_release_sbase:
+ release_region(data->sbase, SP_IOMEM_LEN);
+exit_release_ebase:
+ release_region(data->ebase, EHFUNC_IOMEM_LEN);
+exit_release_wbase:
+ release_region(data->wbase, WAKEUP_IOMEM_LEN);
+exit_free_data:
+ kfree(data);
+ pnp_set_drvdata(device, NULL);
+exit:
+ return err;
+}
+
+static void
+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 */
+ wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_STS, 0x17, 0x17);
+
+ /* Clear CEIR_EN */
+ wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_CTL, 0x00, 0x01);
+
+ /* Clear BUFF_EN, END_EN, MATCH_EN */
+ wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_EV_EN, 0x00, 0x07);
+
+ sysfs_remove_group(&device->dev.kobj, &wbcir_attribute_group);
+
+ /* This will generate a keyup event if necessary */
+ input_unregister_device(data->input_dev);
+
+ led_trigger_unregister_simple(data->rxtrigger);
+ led_trigger_unregister_simple(data->txtrigger);
+ led_classdev_unregister(&data->led);
+
+ /* This is ok since &data->led isn't actually used */
+ wbcir_led_brightness_set(&data->led, LED_OFF);
+
+ release_region(data->wbase, WAKEUP_IOMEM_LEN);
+ 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);
+}
+
+static const struct pnp_device_id wbcir_ids[] = {
+ { "WEC1022", 0 },
+ { "", 0 }
+};
+MODULE_DEVICE_TABLE(pnp, wbcir_ids);
+
+static struct pnp_driver wbcir_driver = {
+ .name = WBCIR_ACPI_NAME,
+ .id_table = wbcir_ids,
+ .probe = wbcir_probe,
+ .remove = wbcir_remove,
+ .suspend = wbcir_suspend,
+ .resume = wbcir_resume,
+ .shutdown = wbcir_shutdown
+};
+
+static int __init
+wbcir_init(void)
+{
+ int ret;
+
+ switch (protocol) {
+ case IR_PROTOCOL_RC5:
+ case IR_PROTOCOL_NEC:
+ case IR_PROTOCOL_RC6:
+ break;
+ default:
+ printk(KERN_ERR DRVNAME ": Invalid protocol argument\n");
+ return -EINVAL;
+ }
+
+ ret = pnp_register_driver(&wbcir_driver);
+ if (ret)
+ printk(KERN_ERR DRVNAME ": Unable to register driver\n");
+
+ return ret;
+}
+
+static void __exit
+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(wbcir_exit);
+
+
--
David Härdeman
--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [patch 2/2] Add a driver for the Winbond WPCD376I Consumer IR hardware
2009-08-09 9:56 ` david
(?)
@ 2009-08-10 15:39 ` Bjorn Helgaas
-1 siblings, 0 replies; 27+ messages in thread
From: Bjorn Helgaas @ 2009-08-10 15:39 UTC (permalink / raw)
To: david; +Cc: linux-kernel, linux-input, jbarnes, akpm, randy.dunlap
On Sunday 09 August 2009 03:56:47 am david@hardeman.nu wrote:
> --- linux-2.6.orig/drivers/input/misc/Kconfig 2009-07-25 21:18:57.000000000 +0200
> +++ linux-2.6/drivers/input/misc/Kconfig 2009-08-07 21:45:41.000000000 +0200
> @@ -222,6 +222,22 @@
> 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 && ACPI
This should depend on PNP, not ACPI.
> +#define WBCIR_ACPI_NAME "Winbond CIR"
You might remove "ACPI" from "WBCIR_ACPI_NAME," since it has nothing
to do with ACPI.
Otherwise, looks good to me.
Bjorn
^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [patch 2/2] Add a driver for the Winbond WPCD376I Consumer IR hardware
2009-08-09 9:56 ` david
(?)
(?)
@ 2009-08-13 6:58 ` Dmitry Torokhov
2009-08-13 9:34 ` David Härdeman
2009-08-13 17:14 ` David Härdeman
-1 siblings, 2 replies; 27+ messages in thread
From: Dmitry Torokhov @ 2009-08-13 6:58 UTC (permalink / raw)
To: david
Cc: linux-kernel, linux-input, jbarnes, akpm, bjorn.helgaas, randy.dunlap
Hi David,
On Sun, Aug 09, 2009 at 11:56:47AM +0200, david@hardeman.nu wrote:
>
> +config INPUT_WINBOND_CIR
> + tristate "Winbond IR remote control"
> + depends on X86 && ACPI
> + select LEDS_CLASS
> + 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
> Index: linux-2.6/drivers/input/misc/Makefile
> ===================================================================
> --- linux-2.6.orig/drivers/input/misc/Makefile 2009-07-25 21:18:57.000000000 +0200
> +++ linux-2.6/drivers/input/misc/Makefile 2009-07-25 21:19:28.000000000 +0200
> @@ -19,6 +19,7 @@
> obj-$(CONFIG_INPUT_PCSPKR) += pcspkr.o
> obj-$(CONFIG_INPUT_POWERMATE) += powermate.o
> obj-$(CONFIG_INPUT_RB532_BUTTON) += rb532_button.o
> +obj-$(CONFIG_INPUT_WINBOND_CIR) += winbond-cir.o
> obj-$(CONFIG_INPUT_GPIO_ROTARY_ENCODER) += rotary_encoder.o
> obj-$(CONFIG_INPUT_SGI_BTNS) += sgi_btns.o
> obj-$(CONFIG_INPUT_SPARCSPKR) += sparcspkr.o
Please keep Makefile sorted alphabetically.
> Index: linux-2.6/drivers/input/misc/winbond-cir.c
> ===================================================================
> --- /dev/null 1970-01-01 00:00:00.000000000 +0000
> +++ linux-2.6/drivers/input/misc/winbond-cir.c 2009-07-25 21:19:28.000000000 +0200
> @@ -0,0 +1,1644 @@
> +/*
> + * winbond-cir.c - Driver for the Consumer IR functionality of Winbond
> + * SuperI/O chips.
> + *
> + * Currently supports the Winbond WPCD376i chip (PNP id WEC1022), but
> + * could probably support others (Winbond WEC102X, NatSemi, etc)
> + * with minor modifications.
> + *
> + * Original Author: David Härdeman <david@hardeman.nu>
> + * Copyright (C) 2009 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
> + * of the bugs.
> + *
> + * Written using:
> + * o Winbond WPCD376I datasheet helpfully provided by Jesse Barnes at Intel
> + * o NatSemi PC87338/PC97338 datasheet (for the serial port stuff)
> + * 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)
> + *
> + * 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
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
> + */
> +
> +#include <linux/module.h>
> +#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>
> +
> +#define DRVNAME "winbond-cir"
> +
> +/* CEIR Wake-Up Registers, relative to data->wbase */
> +#define WBCIR_REG_WCEIR_CTL 0x03 /* CEIR Receiver Control */
> +#define WBCIR_REG_WCEIR_STS 0x04 /* CEIR Receiver Status */
> +#define WBCIR_REG_WCEIR_EV_EN 0x05 /* CEIR Receiver Event Enable */
> +#define WBCIR_REG_WCEIR_CNTL 0x06 /* CEIR Receiver Counter Low */
> +#define WBCIR_REG_WCEIR_CNTH 0x07 /* CEIR Receiver Counter High */
> +#define WBCIR_REG_WCEIR_INDEX 0x08 /* CEIR Receiver Index */
> +#define WBCIR_REG_WCEIR_DATA 0x09 /* CEIR Receiver Data */
> +#define WBCIR_REG_WCEIR_CSL 0x0A /* CEIR Re. Compare Strlen */
> +#define WBCIR_REG_WCEIR_CFG1 0x0B /* CEIR Re. Configuration 1 */
> +#define WBCIR_REG_WCEIR_CFG2 0x0C /* CEIR Re. Configuration 2 */
> +
> +/* CEIR Enhanced Functionality Registers, relative to data->ebase */
> +#define WBCIR_REG_ECEIR_CTS 0x00 /* Enhanced IR Control Status */
> +#define WBCIR_REG_ECEIR_CCTL 0x01 /* Infrared Counter Control */
> +#define WBCIR_REG_ECEIR_CNT_LO 0x02 /* Infrared Counter LSB */
> +#define WBCIR_REG_ECEIR_CNT_HI 0x03 /* Infrared Counter MSB */
> +#define WBCIR_REG_ECEIR_IREM 0x04 /* Infrared Emitter Status */
> +
> +/* SP3 Banked Registers, relative to data->sbase */
> +#define WBCIR_REG_SP3_BSR 0x03 /* Bank Select, all banks */
> + /* Bank 0 */
> +#define WBCIR_REG_SP3_RXDATA 0x00 /* FIFO RX data (r) */
> +#define WBCIR_REG_SP3_TXDATA 0x00 /* FIFO TX data (w) */
> +#define WBCIR_REG_SP3_IER 0x01 /* Interrupt Enable */
> +#define WBCIR_REG_SP3_EIR 0x02 /* Event Identification (r) */
> +#define WBCIR_REG_SP3_FCR 0x02 /* FIFO Control (w) */
> +#define WBCIR_REG_SP3_MCR 0x04 /* Mode Control */
> +#define WBCIR_REG_SP3_LSR 0x05 /* Link Status */
> +#define WBCIR_REG_SP3_MSR 0x06 /* Modem Status */
> +#define WBCIR_REG_SP3_ASCR 0x07 /* Aux Status and Control */
> + /* Bank 2 */
> +#define WBCIR_REG_SP3_BGDL 0x00 /* Baud Divisor LSB */
> +#define WBCIR_REG_SP3_BGDH 0x01 /* Baud Divisor MSB */
> +#define WBCIR_REG_SP3_EXCR1 0x02 /* Extended Control 1 */
> +#define WBCIR_REG_SP3_EXCR2 0x04 /* Extended Control 2 */
> +#define WBCIR_REG_SP3_TXFLV 0x06 /* TX FIFO Level */
> +#define WBCIR_REG_SP3_RXFLV 0x07 /* RX FIFO Level */
> + /* Bank 3 */
> +#define WBCIR_REG_SP3_MRID 0x00 /* Module Identification */
> +#define WBCIR_REG_SP3_SH_LCR 0x01 /* LCR Shadow */
> +#define WBCIR_REG_SP3_SH_FCR 0x02 /* FCR Shadow */
> + /* Bank 4 */
> +#define WBCIR_REG_SP3_IRCR1 0x02 /* Infrared Control 1 */
> + /* Bank 5 */
> +#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 */
> + /* Bank 7 */
> +#define WBCIR_REG_SP3_IRRXDC 0x00 /* IR RX Demod Control */
> +#define WBCIR_REG_SP3_IRTXMC 0x01 /* IR TX Mod Control */
> +#define WBCIR_REG_SP3_RCCFG 0x02 /* CEIR Config */
> +#define WBCIR_REG_SP3_IRCFG1 0x04 /* Infrared Config 1 */
> +#define WBCIR_REG_SP3_IRCFG4 0x07 /* Infrared Config 4 */
> +
> +/*
> + * Magic values follow
> + */
> +
> +/* No interrupts for WBCIR_REG_SP3_IER and WBCIR_REG_SP3_EIR */
> +#define WBCIR_IRQ_NONE 0x00
> +/* RX data bit for WBCIR_REG_SP3_IER and WBCIR_REG_SP3_EIR */
> +#define WBCIR_IRQ_RX 0x01
> +/* Over/Under-flow bit for WBCIR_REG_SP3_IER and WBCIR_REG_SP3_EIR */
> +#define WBCIR_IRQ_ERR 0x04
> +/* Led enable/disable bit for WBCIR_REG_ECEIR_CTS */
> +#define WBCIR_LED_ENABLE 0x80
> +/* RX data available bit for WBCIR_REG_SP3_LSR */
> +#define WBCIR_RX_AVAIL 0x01
> +/* RX disable bit for WBCIR_REG_SP3_ASCR */
> +#define WBCIR_RX_DISABLE 0x20
> +/* Extended mode enable bit for WBCIR_REG_SP3_EXCR1 */
> +#define WBCIR_EXT_ENABLE 0x01
> +/* Select compare register in WBCIR_REG_WCEIR_INDEX (bits 5 & 6) */
> +#define WBCIR_REGSEL_COMPARE 0x10
> +/* Select mask register in WBCIR_REG_WCEIR_INDEX (bits 5 & 6) */
> +#define WBCIR_REGSEL_MASK 0x20
> +/* Starting address of selected register in WBCIR_REG_WCEIR_INDEX */
> +#define WBCIR_REG_ADDR0 0x00
> +
> +/* Valid banks for the SP3 UART */
> +enum wbcir_bank {
> + WBCIR_BANK_0 = 0x00,
> + WBCIR_BANK_1 = 0x80,
> + WBCIR_BANK_2 = 0xE0,
> + WBCIR_BANK_3 = 0xE4,
> + WBCIR_BANK_4 = 0xE8,
> + WBCIR_BANK_5 = 0xEC,
> + WBCIR_BANK_6 = 0xF0,
> + WBCIR_BANK_7 = 0xF4,
> +};
> +
> +/* Supported IR Protocols */
> +enum wbcir_protocol {
> + IR_PROTOCOL_RC5 = 0x0,
> + IR_PROTOCOL_NEC = 0x1,
> + IR_PROTOCOL_RC6 = 0x2,
> +};
> +
> +/* Misc */
> +#define WBCIR_ACPI_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_0 },
> + { 0x800F0401, KEY_1 },
> + { 0x800F0402, KEY_2 },
> + { 0x800F0403, KEY_3 },
> + { 0x800F0404, KEY_4 },
> + { 0x800F0405, KEY_5 },
> + { 0x800F0406, KEY_6 },
> + { 0x800F0407, KEY_7 },
> + { 0x800F0408, KEY_8 },
> + { 0x800F0409, KEY_9 },
Make these ones KEY_NUMERIC_* as well, this should help users whose
keymaps have numbers in upper register normally.
> + { 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 },
Umm, it looks like if you do (code & 0x800F0400) you can switch to
standard array-based keymap and won't even need list manipulation.
> +};
> +
> +/* Registers and other state is protected by wbcir_lock */
> +struct wbcir_data {
> + 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 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;
I think this has a potential to deadlock... Set and get keycodes are
called with event lock taken, and then your implementations acquire
keytable lock. When you emit input events the opposite happens - you
take the keytable lock and then input core takes event lock.
> +};
> +
> +static enum wbcir_protocol protocol = IR_PROTOCOL_RC6;
> +module_param(protocol, uint, 0444);
> +MODULE_PARM_DESC(protocol, "IR protocol to use "
> + "(0 = RC5, 1 = NEC, 2 = RC6A, default)");
> +
> +static int invert; /* default = 0 */
> +module_param(invert, bool, 0444);
> +MODULE_PARM_DESC(invert, "Invert the signal from the IR receiver");
> +
> +static unsigned int wake_sc = 0x800F040C;
> +module_param(wake_sc, uint, 0644);
> +MODULE_PARM_DESC(wake_sc, "Scancode of the power-on IR command");
> +
> +static unsigned int wake_rc6mode = 6;
> +module_param(wake_rc6mode, uint, 0644);
> +MODULE_PARM_DESC(wake_rc6mode, "RC6 mode for the power-on command "
> + "(0 = 0, 6 = 6A, default)");
> +
> +
> +
> +/*****************************************************************************
> + *
> + * UTILITY FUNCTIONS
> + *
> + *****************************************************************************/
> +
> +/* Caller needs to hold wbcir_lock */
> +static void
> +wbcir_set_bits(unsigned long addr, u8 bits, u8 mask)
> +{
> + u8 val;
> +
> + val = inb(addr);
> + val = ((val & ~mask) | (bits & mask));
> + outb(val, addr);
> +}
> +
> +/* Selects the register bank for the serial port */
> +static inline void
> +wbcir_select_bank(struct wbcir_data *data, enum wbcir_bank bank)
> +{
> + outb(bank, data->sbase + WBCIR_REG_SP3_BSR);
> +}
> +
> +static enum led_brightness
> +wbcir_led_brightness_get(struct led_classdev *led_cdev)
> +{
> + struct wbcir_data *data = container_of(led_cdev,
> + struct wbcir_data,
> + led);
> +
> + if (inb(data->ebase + WBCIR_REG_ECEIR_CTS) & WBCIR_LED_ENABLE)
> + return LED_FULL;
> + else
> + return LED_OFF;
> +}
> +
> +static void
> +wbcir_led_brightness_set(struct led_classdev *led_cdev,
> + enum led_brightness brightness)
> +{
> + struct wbcir_data *data = container_of(led_cdev,
> + struct wbcir_data,
> + led);
> +
> + wbcir_set_bits(data->ebase + WBCIR_REG_ECEIR_CTS,
> + brightness == LED_OFF ? 0x00 : WBCIR_LED_ENABLE,
> + WBCIR_LED_ENABLE);
> +}
> +
> +/* Manchester encodes bits to RC6 message cells (see wbcir_parse_rc6) */
> +static u8
> +wbcir_to_rc6cells(u8 val)
> +{
> + u8 coded = 0x00;
> + int i;
> +
> + val &= 0x0F;
> + for (i = 0; i < 4; i++) {
> + if (val & 0x01)
> + coded |= 0x02 << (i * 2);
> + else
> + coded |= 0x01 << (i * 2);
> + val >>= 1;
> + }
> +
> + 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, int scancode, int *keycode)
> +{
> + struct wbcir_data *data = input_get_drvdata(dev);
> +
> + *keycode = (int)wbcir_do_getkeycode(data, (u32)scancode);
> + return 0;
> +}
> +
> +static int
> +wbcir_setkeycode(struct input_dev *dev, int sscancode, 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;
> + u32 scancode = (u32)sscancode;
> +
> + if (keycode < 0 || keycode > KEY_MAX)
> + return -EINVAL;
> +
> + 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;
> + }
> +
> + /* 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);
> + input_sync(data->input_dev);
> + data->keypressed = 1;
> + data->last_keycode = keycode;
> + data->last_toggle = toggle;
> +
> +set_timer:
> + 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;
> +}
> +
> +/* 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
> + *
> + *****************************************************************************/
> +
> +static irqreturn_t
> +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[16];
> + int i;
> + unsigned int hw;
> +
> + spin_lock_irqsave(&wbcir_lock, 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);
> + return IRQ_NONE;
> + }
> +
> + if (status & WBCIR_IRQ_ERR)
> + data->irdata_error = 1;
> +
> + if (!(status & WBCIR_IRQ_RX))
> + goto out;
> +
> + /* Since RXHDLEV is set, at least 16 bytes are in the FIFO */
> + insb(data->sbase + WBCIR_REG_SP3_RXDATA, &irdata[0], 8);
> + insb(data->sbase + WBCIR_REG_SP3_RXDATA, &irdata[8], 8);
> +
> + for (i = 0; i < sizeof(data); i++) {
> + hw = hweight8(irdata[i]);
> + if (hw > 4)
> + add_irdata_bit(data, 0);
> + else
> + add_irdata_bit(data, 1);
> +
> + if (hw == 8)
> + data->idle_count++;
> + else
> + data->idle_count = 0;
> + }
> +
> + if (data->idle_count > WBCIR_MAX_IDLE_BYTES) {
> + /* Drain the FIFO */
> + while (inb(data->sbase + WBCIR_REG_SP3_LSR) & WBCIR_RX_AVAIL)
> + inb(data->sbase + WBCIR_REG_SP3_RXDATA);
> +
> + /* And set RXINACTIVE */
> + outb(WBCIR_RX_DISABLE, data->sbase + WBCIR_REG_SP3_ASCR);
> +
> + 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);
> + data->idle_count = 0;
> + }
> +
> +out:
> + spin_unlock_irqrestore(&wbcir_lock, flags);
> + return IRQ_HANDLED;
> +}
> +
> +
> +
> +/*****************************************************************************
> + *
> + * SUSPEND/RESUME FUNCTIONS
> + *
> + *****************************************************************************/
> +
> +static void
> +wbcir_shutdown(struct pnp_dev *device)
> +{
> + struct device *dev = &device->dev;
> + struct wbcir_data *data = pnp_get_drvdata(device);
> + int do_wake = 1;
> + u8 match[11];
> + u8 mask[11];
> + u8 rc6_csl = 0;
> + int i;
> +
> + memset(match, 0, sizeof(match));
> + memset(mask, 0, sizeof(mask));
> +
> + if (wake_sc == INVALID_SCANCODE || !device_may_wakeup(dev)) {
> + do_wake = 0;
> + goto finish;
> + }
> +
> + switch (protocol) {
> + case IR_PROTOCOL_RC5:
> + if (wake_sc > 0xFFF) {
> + do_wake = 0;
> + dev_err(dev, "RC5 - Invalid wake scancode\n");
> + break;
> + }
> +
> + /* Mask = 13 bits, ex toggle */
> + mask[0] = 0xFF;
> + mask[1] = 0x17;
> +
> + match[0] = (wake_sc & 0x003F); /* 6 command bits */
> + match[0] |= (wake_sc & 0x0180) >> 1; /* 2 address bits */
> + match[1] = (wake_sc & 0x0E00) >> 9; /* 3 address bits */
> + if (!(wake_sc & 0x0040)) /* 2nd start bit */
> + match[1] |= 0x10;
> +
> + break;
> +
> + case IR_PROTOCOL_NEC:
> + if (wake_sc > 0xFFFFFF) {
> + do_wake = 0;
> + dev_err(dev, "NEC - Invalid wake scancode\n");
> + break;
> + }
> +
> + mask[0] = mask[1] = mask[2] = mask[3] = 0xFF;
> +
> + match[1] = bitrev8((wake_sc & 0xFF));
> + match[0] = ~match[1];
> +
> + match[3] = bitrev8((wake_sc & 0xFF00) >> 8);
> + if (wake_sc > 0xFFFF)
> + match[2] = bitrev8((wake_sc & 0xFF0000) >> 16);
> + else
> + match[2] = ~match[3];
> +
> + break;
> +
> + case IR_PROTOCOL_RC6:
> +
> + if (wake_rc6mode == 0) {
> + if (wake_sc > 0xFFFF) {
> + do_wake = 0;
> + dev_err(dev, "RC6 - Invalid wake scancode\n");
> + break;
> + }
> +
> + /* Command */
> + match[0] = wbcir_to_rc6cells(wake_sc >> 0);
> + mask[0] = 0xFF;
> + match[1] = wbcir_to_rc6cells(wake_sc >> 4);
> + mask[1] = 0xFF;
> +
> + /* Address */
> + match[2] = wbcir_to_rc6cells(wake_sc >> 8);
> + mask[2] = 0xFF;
> + match[3] = wbcir_to_rc6cells(wake_sc >> 12);
> + mask[3] = 0xFF;
> +
> + /* Header */
> + match[4] = 0x50; /* mode1 = mode0 = 0, ignore toggle */
> + mask[4] = 0xF0;
> + match[5] = 0x09; /* start bit = 1, mode2 = 0 */
> + mask[5] = 0x0F;
> +
> + rc6_csl = 44;
> +
> + } else if (wake_rc6mode == 6) {
> + i = 0;
> +
> + /* Command */
> + match[i] = wbcir_to_rc6cells(wake_sc >> 0);
> + mask[i++] = 0xFF;
> + match[i] = wbcir_to_rc6cells(wake_sc >> 4);
> + mask[i++] = 0xFF;
> +
> + /* Address + Toggle */
> + match[i] = wbcir_to_rc6cells(wake_sc >> 8);
> + mask[i++] = 0xFF;
> + match[i] = wbcir_to_rc6cells(wake_sc >> 12);
> + mask[i++] = 0x3F;
> +
> + /* Customer bits 7 - 0 */
> + match[i] = wbcir_to_rc6cells(wake_sc >> 16);
> + mask[i++] = 0xFF;
> + match[i] = wbcir_to_rc6cells(wake_sc >> 20);
> + mask[i++] = 0xFF;
> +
> + if (wake_sc & 0x80000000) {
> + /* Customer range bit and bits 15 - 8 */
> + match[i] = wbcir_to_rc6cells(wake_sc >> 24);
> + mask[i++] = 0xFF;
> + match[i] = wbcir_to_rc6cells(wake_sc >> 28);
> + mask[i++] = 0xFF;
> + rc6_csl = 76;
> + } else if (wake_sc <= 0x007FFFFF) {
> + rc6_csl = 60;
> + } else {
> + do_wake = 0;
> + dev_err(dev, "RC6 - Invalid wake scancode\n");
> + break;
> + }
> +
> + /* Header */
> + match[i] = 0x93; /* mode1 = mode0 = 1, submode = 0 */
> + mask[i++] = 0xFF;
> + match[i] = 0x0A; /* start bit = 1, mode2 = 1 */
> + mask[i++] = 0x0F;
> +
> + } else {
> + do_wake = 0;
> + dev_err(dev, "RC6 - Invalid wake mode\n");
> + }
> +
> + break;
> +
> + default:
> + do_wake = 0;
> + break;
> + }
> +
> +finish:
> + if (do_wake) {
> + /* Set compare and compare mask */
> + wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_INDEX,
> + WBCIR_REGSEL_COMPARE | WBCIR_REG_ADDR0,
> + 0x3F);
> + outsb(data->wbase + WBCIR_REG_WCEIR_DATA, match, 11);
> + wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_INDEX,
> + WBCIR_REGSEL_MASK | WBCIR_REG_ADDR0,
> + 0x3F);
> + outsb(data->wbase + WBCIR_REG_WCEIR_DATA, mask, 11);
> +
> + /* RC6 Compare String Len */
> + outb(rc6_csl, data->wbase + WBCIR_REG_WCEIR_CSL);
> +
> + /* Clear status bits NEC_REP, BUFF, MSG_END, MATCH */
> + wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_STS, 0x17, 0x17);
> +
> + /* Clear BUFF_EN, Clear END_EN, Set MATCH_EN */
> + wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_EV_EN, 0x01, 0x07);
> +
> + /* Set CEIR_EN */
> + wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_CTL, 0x01, 0x01);
> +
> + } else {
> + /* Clear BUFF_EN, Clear END_EN, Clear MATCH_EN */
> + wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_EV_EN, 0x00, 0x07);
> +
> + /* Clear CEIR_EN */
> + wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_CTL, 0x00, 0x01);
> + }
> +
> + /* Disable interrupts */
> + outb(WBCIR_IRQ_NONE, data->sbase + WBCIR_REG_SP3_IER);
> +}
> +
> +static int
> +wbcir_suspend(struct pnp_dev *device, pm_message_t state)
> +{
> + wbcir_shutdown(device);
> + return 0;
> +}
> +
> +static int
> +wbcir_resume(struct pnp_dev *device)
> +{
> + struct wbcir_data *data = pnp_get_drvdata(device);
> +
> + /* Clear BUFF_EN, Clear END_EN, Clear MATCH_EN */
> + wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_EV_EN, 0x00, 0x07);
> +
> + /* Clear CEIR_EN */
> + wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_CTL, 0x00, 0x01);
> +
> + /* Enable interrupts */
> + wbcir_reset_irdata(data);
> + outb(WBCIR_IRQ_RX | WBCIR_IRQ_ERR, data->sbase + WBCIR_REG_SP3_IER);
> +
> + return 0;
> +}
> +
> +
> +
> +/*****************************************************************************
> + *
> + * SETUP/INIT FUNCTIONS
> + *
> + *****************************************************************************/
> +
> +static ssize_t
> +wbcir_show_last_scancode(struct device *dev,
> + struct device_attribute *attr, char *buf)
> +{
> + struct pnp_dev *device = container_of(dev, struct pnp_dev, dev);
> + struct wbcir_data *data = pnp_get_drvdata(device);
> + return sprintf(buf, "0x%08X\n", data->last_scancode);
> +}
> +
> +static struct device_attribute dev_attr_last_scancode = {
> + .attr = {
> + .name = "last_scancode",
> + .mode = 0444,
> + },
> + .show = wbcir_show_last_scancode,
> + .store = NULL,
> +
> +};
Why is this needed? And if this is needed we have a nice macro
for that.
> +
> +static struct attribute *wbcir_attributes[] = {
> + &dev_attr_last_scancode.attr,
> + NULL,
> +};
> +
> +static struct attribute_group wbcir_attribute_group = {
> + .attrs = wbcir_attributes,
> +};
> +
> +static void
> +wbcir_cfg_ceir(struct wbcir_data *data)
> +{
> + u8 tmp;
> +
> + /* Set PROT_SEL, RX_INV, Clear CEIR_EN (needed for the led) */
> + tmp = protocol << 4;
> + if (invert)
> + tmp |= 0x08;
> + outb(tmp, data->wbase + WBCIR_REG_WCEIR_CTL);
> +
> + /* Clear status bits NEC_REP, BUFF, MSG_END, MATCH */
> + wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_STS, 0x17, 0x17);
> +
> + /* Clear BUFF_EN, Clear END_EN, Clear MATCH_EN */
> + wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_EV_EN, 0x00, 0x07);
> +
> + /* Set RC5 cell time to correspond to 36 kHz */
> + wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_CFG1, 0x4A, 0x7F);
> +
> + /* Set IRTX_INV */
> + if (invert)
> + outb(0x04, data->ebase + WBCIR_REG_ECEIR_CCTL);
> + else
> + outb(0x00, data->ebase + WBCIR_REG_ECEIR_CCTL);
> +
> + /*
> + * Clear IR LED, set SP3 clock to 24Mhz
> + * set SP3_IRRX_SW to binary 01, helpfully not documented
> + */
> + outb(0x10, data->ebase + WBCIR_REG_ECEIR_CTS);
> +}
> +
> +static int
> +wbcir_probe(struct pnp_dev *device, const struct pnp_device_id *dev_id)
__devinit
> +{
> + struct device *dev = &device->dev;
> + struct wbcir_data *data;
> + int err;
> +
> + if (!(pnp_port_len(device, 0) == EHFUNC_IOMEM_LEN &&
> + pnp_port_len(device, 1) == WAKEUP_IOMEM_LEN &&
> + pnp_port_len(device, 2) == SP_IOMEM_LEN)) {
> + dev_err(dev, "Invalid resources\n");
> + return -ENODEV;
> + }
> +
> + data = kzalloc(sizeof(*data), GFP_KERNEL);
> + if (!data) {
> + err = -ENOMEM;
> + goto exit;
> + }
> +
> + pnp_set_drvdata(device, data);
> +
> + data->ebase = pnp_port_start(device, 0);
> + data->wbase = pnp_port_start(device, 1);
> + data->sbase = pnp_port_start(device, 2);
> + data->irq = pnp_irq(device, 0);
> +
> + if (data->wbase == 0 || data->ebase == 0 ||
> + data->sbase == 0 || data->irq == 0) {
> + err = -ENODEV;
> + dev_err(dev, "Invalid resources\n");
> + goto exit_free_data;
> + }
> +
> + dev_info(&device->dev, "Found device "
> + "(w: 0x%lX, e: 0x%lX, s: 0x%lX, i: %u)\n",
> + data->wbase, data->ebase, data->sbase, data->irq);
> +
dev_dbg() I think.
> + if (!request_region(data->wbase, WAKEUP_IOMEM_LEN, DRVNAME)) {
> + dev_err(dev, "Region 0x%lx-0x%lx already in use!\n",
> + data->wbase, data->wbase + WAKEUP_IOMEM_LEN - 1);
> + err = -EBUSY;
> + goto exit_free_data;
> + }
> +
> + if (!request_region(data->ebase, EHFUNC_IOMEM_LEN, DRVNAME)) {
> + dev_err(dev, "Region 0x%lx-0x%lx already in use!\n",
> + data->ebase, data->ebase + EHFUNC_IOMEM_LEN - 1);
> + err = -EBUSY;
> + goto exit_release_wbase;
> + }
> +
> + if (!request_region(data->sbase, SP_IOMEM_LEN, DRVNAME)) {
> + dev_err(dev, "Region 0x%lx-0x%lx already in use!\n",
> + data->sbase, data->sbase + SP_IOMEM_LEN - 1);
> + err = -EBUSY;
> + goto exit_release_ebase;
> + }
> +
> + err = request_irq(data->irq, wbcir_irq_handler,
> + IRQF_DISABLED, DRVNAME, device);
> + if (err) {
> + dev_err(dev, "Failed to claim IRQ %u\n", data->irq);
> + err = -EBUSY;
> + goto exit_release_sbase;
> + }
> +
> + led_trigger_register_simple("cir-tx", &data->txtrigger);
> + if (!data->txtrigger) {
> + err = -ENOMEM;
> + goto exit_free_irq;
> + }
> +
> + led_trigger_register_simple("cir-rx", &data->rxtrigger);
> + if (!data->rxtrigger) {
> + err = -ENOMEM;
> + goto exit_unregister_txtrigger;
> + }
> +
> + data->led.name = "cir::activity";
> + data->led.default_trigger = "cir-rx";
> + data->led.brightness_set = wbcir_led_brightness_set;
> + data->led.brightness_get = wbcir_led_brightness_get;
> + err = led_classdev_register(&device->dev, &data->led);
> + if (err)
> + goto exit_unregister_rxtrigger;
> +
> + data->input_dev = input_allocate_device();
> + if (!data->input_dev) {
> + err = -ENOMEM;
> + goto exit_unregister_led;
> + }
> +
> + data->input_dev->evbit[0] = BIT(EV_KEY);
> + data->input_dev->name = WBCIR_ACPI_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_drvdata(data->input_dev, data);
> +
> + err = input_register_device(data->input_dev);
> + if (err)
> + goto exit_free_input;
> +
> + data->last_scancode = INVALID_SCANCODE;
> + err = sysfs_create_group(&device->dev.kobj, &wbcir_attribute_group);
> + if (err)
> + goto exit_unregister_input;
> +
> + 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;
> + }
> + }
> +
> + device_init_wakeup(&device->dev, 1);
> +
> + wbcir_cfg_ceir(data);
> +
> + /* Disable interrupts */
> + wbcir_select_bank(data, WBCIR_BANK_0);
> + outb(WBCIR_IRQ_NONE, data->sbase + WBCIR_REG_SP3_IER);
> +
> + /* Enable extended mode */
> + wbcir_select_bank(data, WBCIR_BANK_2);
> + outb(WBCIR_EXT_ENABLE, data->sbase + WBCIR_REG_SP3_EXCR1);
> +
> + /*
> + * Configure baud generator, IR data will be sampled at
> + * a bitrate of: (24Mhz * prescaler) / (divisor * 16).
> + *
> + * The ECIR registers include a flag to change the
> + * 24Mhz clock freq to 48Mhz.
> + */
> +
> + /* prescaler 1.0, tx/rx fifo lvl 32 */
> + outb(0x35, data->sbase + WBCIR_REG_SP3_EXCR2);
> +
> + /* Set baud divisor to generate one byte per bit/cell */
> + switch (protocol) {
> + case IR_PROTOCOL_RC5:
> + outb(0xA7, data->sbase + WBCIR_REG_SP3_BGDL);
> + break;
> + case IR_PROTOCOL_RC6:
> + outb(0x53, data->sbase + WBCIR_REG_SP3_BGDL);
> + break;
> + case IR_PROTOCOL_NEC:
> + outb(0x69, data->sbase + WBCIR_REG_SP3_BGDL);
> + break;
> + }
> + outb(0x00, data->sbase + WBCIR_REG_SP3_BGDH);
> +
> + /* Set CEIR mode */
> + wbcir_select_bank(data, WBCIR_BANK_0);
> + outb(0xC0, data->sbase + WBCIR_REG_SP3_MCR);
> + inb(data->sbase + WBCIR_REG_SP3_LSR); /* Clear LSR */
> + inb(data->sbase + WBCIR_REG_SP3_MSR); /* Clear MSR */
> +
> + /* Disable RX demod, run-length encoding/decoding, set freq span */
> + wbcir_select_bank(data, WBCIR_BANK_7);
> + outb(0x10, data->sbase + WBCIR_REG_SP3_RCCFG);
> +
> + /* Disable timer */
> + wbcir_select_bank(data, WBCIR_BANK_4);
> + outb(0x00, data->sbase + WBCIR_REG_SP3_IRCR1);
> +
> + /* Enable MSR interrupt, Clear AUX_IRX */
> + wbcir_select_bank(data, WBCIR_BANK_5);
> + outb(0x00, data->sbase + WBCIR_REG_SP3_IRCR2);
> +
> + /* Disable CRC */
> + wbcir_select_bank(data, WBCIR_BANK_6);
> + outb(0x20, data->sbase + WBCIR_REG_SP3_IRCR3);
> +
> + /* Set RX/TX (de)modulation freq, not really used */
> + wbcir_select_bank(data, WBCIR_BANK_7);
> + outb(0xF2, data->sbase + WBCIR_REG_SP3_IRRXDC);
> + outb(0x69, data->sbase + WBCIR_REG_SP3_IRTXMC);
> +
> + /* Set invert and pin direction */
> + if (invert)
> + outb(0x10, data->sbase + WBCIR_REG_SP3_IRCFG4);
> + else
> + outb(0x00, data->sbase + WBCIR_REG_SP3_IRCFG4);
> +
> + /* Set FIFO thresholds (RX = 16, TX = 7), reset RX/TX */
> + wbcir_select_bank(data, WBCIR_BANK_0);
> + outb(0x97, data->sbase + WBCIR_REG_SP3_FCR);
> +
> + /* Clear AUX status bits */
> + outb(0xE0, data->sbase + WBCIR_REG_SP3_ASCR);
> +
> + /* Enable interrupts */
> + wbcir_select_bank(data, WBCIR_BANK_0);
> + outb(WBCIR_IRQ_RX | WBCIR_IRQ_ERR, data->sbase + WBCIR_REG_SP3_IER);
> +
> + 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);
> + }
> + }
> +exit_unregister_input:
> + 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_unregister_led:
> + led_classdev_unregister(&data->led);
> +exit_unregister_rxtrigger:
> + led_trigger_unregister_simple(data->rxtrigger);
> +exit_unregister_txtrigger:
> + led_trigger_unregister_simple(data->txtrigger);
> +exit_free_irq:
> + free_irq(data->irq, device);
> +exit_release_sbase:
> + release_region(data->sbase, SP_IOMEM_LEN);
> +exit_release_ebase:
> + release_region(data->ebase, EHFUNC_IOMEM_LEN);
> +exit_release_wbase:
> + release_region(data->wbase, WAKEUP_IOMEM_LEN);
> +exit_free_data:
> + kfree(data);
> + pnp_set_drvdata(device, NULL);
> +exit:
> + return err;
> +}
> +
> +static void
> +wbcir_remove(struct pnp_dev *device)
__devexit
> +{
> + 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 */
> + wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_STS, 0x17, 0x17);
> +
> + /* Clear CEIR_EN */
> + wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_CTL, 0x00, 0x01);
> +
> + /* Clear BUFF_EN, END_EN, MATCH_EN */
> + wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_EV_EN, 0x00, 0x07);
> +
> + sysfs_remove_group(&device->dev.kobj, &wbcir_attribute_group);
> +
> + /* This will generate a keyup event if necessary */
> + input_unregister_device(data->input_dev);
> +
> + led_trigger_unregister_simple(data->rxtrigger);
> + led_trigger_unregister_simple(data->txtrigger);
> + led_classdev_unregister(&data->led);
> +
> + /* This is ok since &data->led isn't actually used */
> + wbcir_led_brightness_set(&data->led, LED_OFF);
> +
> + release_region(data->wbase, WAKEUP_IOMEM_LEN);
> + 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);
> +}
> +
> +static const struct pnp_device_id wbcir_ids[] = {
> + { "WEC1022", 0 },
> + { "", 0 }
> +};
> +MODULE_DEVICE_TABLE(pnp, wbcir_ids);
> +
> +static struct pnp_driver wbcir_driver = {
> + .name = WBCIR_ACPI_NAME,
> + .id_table = wbcir_ids,
> + .probe = wbcir_probe,
> + .remove = wbcir_remove,
__devexit_p()
> + .suspend = wbcir_suspend,
> + .resume = wbcir_resume,
Switch to dev_pm_ops?
> + .shutdown = wbcir_shutdown
> +};
> +
> +static int __init
> +wbcir_init(void)
> +{
> + int ret;
> +
> + switch (protocol) {
> + case IR_PROTOCOL_RC5:
> + case IR_PROTOCOL_NEC:
> + case IR_PROTOCOL_RC6:
> + break;
> + default:
> + printk(KERN_ERR DRVNAME ": Invalid protocol argument\n");
> + return -EINVAL;
> + }
> +
> + ret = pnp_register_driver(&wbcir_driver);
> + if (ret)
> + printk(KERN_ERR DRVNAME ": Unable to register driver\n");
> +
> + return ret;
> +}
> +
> +static void __exit
> +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(wbcir_exit);
> +
> +
>
> --
> David Härdeman
> --
> To unsubscribe from this list: send the line "unsubscribe linux-input" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
--
Dmitry
^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [patch 2/2] Add a driver for the Winbond WPCD376I Consumer IR hardware
2009-08-13 6:58 ` Dmitry Torokhov
@ 2009-08-13 9:34 ` David Härdeman
2009-08-13 17:14 ` David Härdeman
1 sibling, 0 replies; 27+ messages in thread
From: David Härdeman @ 2009-08-13 9:34 UTC (permalink / raw)
To: Dmitry Torokhov
Cc: linux-kernel, linux-input, jbarnes, akpm, bjorn.helgaas, randy.dunlap
On Thu, August 13, 2009 08:58, Dmitry Torokhov wrote:
> Hi David,
Hi,
> Please keep Makefile sorted alphabetically.
Ok
>> +static struct wbcir_key rc6_def_keymap[] = {
>> + { 0x800F0400, KEY_0 },
>> + { 0x800F0401, KEY_1 },
>> + { 0x800F0402, KEY_2 },
>> + { 0x800F0403, KEY_3 },
>> + { 0x800F0404, KEY_4 },
>> + { 0x800F0405, KEY_5 },
>> + { 0x800F0406, KEY_6 },
>> + { 0x800F0407, KEY_7 },
>> + { 0x800F0408, KEY_8 },
>> + { 0x800F0409, KEY_9 },
>
> Make these ones KEY_NUMERIC_* as well, this should help users whose
> keymaps have numbers in upper register normally.
Ok
>> + { 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 },
>
> Umm, it looks like if you do (code & 0x800F0400) you can switch to
> standard array-based keymap and won't even need list manipulation.
I didn't want to do that since it would potentially tie the driver to one
particular remote (the one I used for testing while writing the driver).
The hardware isn't shipped with any specific remote so that wouldn't be
very user friendly.
This was the best solution I could come up with without adding some real
limitations to the functionality of the driver.
The main problem right now is that getkeycode is practically useless since
you can't blindly guess at a full range of 2^32 different scancodes to get
the complete keymap. Perhaps a index-based getkeycode would make sense...
Anyway, I hope that I can make this a moot point in the future by adding
more IR-specific functionality to the input system. I've been thinking
along the lines of IR-specific get/set-keycode ioctl's which would take a
struct which defines the IR command to map to a key.
>> +};
>> +
>> +/* Registers and other state is protected by wbcir_lock */
>> +struct wbcir_data {
>> + 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 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;
>
> I think this has a potential to deadlock... Set and get keycodes are
> called with event lock taken, and then your implementations acquire
> keytable lock. When you emit input events the opposite happens - you
> take the keytable lock and then input core takes event lock.
Good catch, I'll have to look into that...
>> +static struct device_attribute dev_attr_last_scancode = {
>> + .attr = {
>> + .name = "last_scancode",
>> + .mode = 0444,
>> + },
>> + .show = wbcir_show_last_scancode,
>> + .store = NULL,
>> +
>> +};
>
> Why is this needed? And if this is needed we have a nice macro
> for that.
I hope I've explained it wrt. EV_IR in my other mail. It's for building
keymaps of unknown remotes. And it'll go away once EV_IR is supported so I
don't think there's much point in fiddling with it now?
>> +static int
>> +wbcir_probe(struct pnp_dev *device, const struct pnp_device_id *dev_id)
>
> __devinit
>
...
>> + dev_info(&device->dev, "Found device "
>> + "(w: 0x%lX, e: 0x%lX, s: 0x%lX, i: %u)\n",
>> + data->wbase, data->ebase, data->sbase, data->irq);
>> +
>
> dev_dbg() I think.
Ok
>> +static void
>> +wbcir_remove(struct pnp_dev *device)
>
> __devexit
Ok
>> +static struct pnp_driver wbcir_driver = {
>> + .name = WBCIR_ACPI_NAME,
>> + .id_table = wbcir_ids,
>> + .probe = wbcir_probe,
>> + .remove = wbcir_remove,
>
> __devexit_p()
Ok
>> + .suspend = wbcir_suspend,
>> + .resume = wbcir_resume,
>
> Switch to dev_pm_ops?
Don't want to do that just yet. dev_pm_ops wasn't even on my radar until
yesterday when I stumbled upon the documentation (in a header file rather
than in Documentation/...eh?). I'll certainly look into it when the
suspend/resume functionality has seen more testing and bug fixing.
Thanks for the review. Are you willing to push the driver upstream through
the input tree once I've implemented your suggested fixes?
--
David Härdeman
^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [patch 2/2] Add a driver for the Winbond WPCD376I Consumer IR hardware
@ 2009-08-13 9:34 ` David Härdeman
0 siblings, 0 replies; 27+ messages in thread
From: David Härdeman @ 2009-08-13 9:34 UTC (permalink / raw)
To: Dmitry Torokhov
Cc: linux-kernel, linux-input, jbarnes, akpm, bjorn.helgaas, randy.dunlap
On Thu, August 13, 2009 08:58, Dmitry Torokhov wrote:
> Hi David,
Hi,
> Please keep Makefile sorted alphabetically.
Ok
>> +static struct wbcir_key rc6_def_keymap[] = {
>> + { 0x800F0400, KEY_0 },
>> + { 0x800F0401, KEY_1 },
>> + { 0x800F0402, KEY_2 },
>> + { 0x800F0403, KEY_3 },
>> + { 0x800F0404, KEY_4 },
>> + { 0x800F0405, KEY_5 },
>> + { 0x800F0406, KEY_6 },
>> + { 0x800F0407, KEY_7 },
>> + { 0x800F0408, KEY_8 },
>> + { 0x800F0409, KEY_9 },
>
> Make these ones KEY_NUMERIC_* as well, this should help users whose
> keymaps have numbers in upper register normally.
Ok
>> + { 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 },
>
> Umm, it looks like if you do (code & 0x800F0400) you can switch to
> standard array-based keymap and won't even need list manipulation.
I didn't want to do that since it would potentially tie the driver to one
particular remote (the one I used for testing while writing the driver).
The hardware isn't shipped with any specific remote so that wouldn't be
very user friendly.
This was the best solution I could come up with without adding some real
limitations to the functionality of the driver.
The main problem right now is that getkeycode is practically useless since
you can't blindly guess at a full range of 2^32 different scancodes to get
the complete keymap. Perhaps a index-based getkeycode would make sense...
Anyway, I hope that I can make this a moot point in the future by adding
more IR-specific functionality to the input system. I've been thinking
along the lines of IR-specific get/set-keycode ioctl's which would take a
struct which defines the IR command to map to a key.
>> +};
>> +
>> +/* Registers and other state is protected by wbcir_lock */
>> +struct wbcir_data {
>> + 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 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;
>
> I think this has a potential to deadlock... Set and get keycodes are
> called with event lock taken, and then your implementations acquire
> keytable lock. When you emit input events the opposite happens - you
> take the keytable lock and then input core takes event lock.
Good catch, I'll have to look into that...
>> +static struct device_attribute dev_attr_last_scancode = {
>> + .attr = {
>> + .name = "last_scancode",
>> + .mode = 0444,
>> + },
>> + .show = wbcir_show_last_scancode,
>> + .store = NULL,
>> +
>> +};
>
> Why is this needed? And if this is needed we have a nice macro
> for that.
I hope I've explained it wrt. EV_IR in my other mail. It's for building
keymaps of unknown remotes. And it'll go away once EV_IR is supported so I
don't think there's much point in fiddling with it now?
>> +static int
>> +wbcir_probe(struct pnp_dev *device, const struct pnp_device_id *dev_id)
>
> __devinit
>
...
>> + dev_info(&device->dev, "Found device "
>> + "(w: 0x%lX, e: 0x%lX, s: 0x%lX, i: %u)\n",
>> + data->wbase, data->ebase, data->sbase, data->irq);
>> +
>
> dev_dbg() I think.
Ok
>> +static void
>> +wbcir_remove(struct pnp_dev *device)
>
> __devexit
Ok
>> +static struct pnp_driver wbcir_driver = {
>> + .name = WBCIR_ACPI_NAME,
>> + .id_table = wbcir_ids,
>> + .probe = wbcir_probe,
>> + .remove = wbcir_remove,
>
> __devexit_p()
Ok
>> + .suspend = wbcir_suspend,
>> + .resume = wbcir_resume,
>
> Switch to dev_pm_ops?
Don't want to do that just yet. dev_pm_ops wasn't even on my radar until
yesterday when I stumbled upon the documentation (in a header file rather
than in Documentation/...eh?). I'll certainly look into it when the
suspend/resume functionality has seen more testing and bug fixing.
Thanks for the review. Are you willing to push the driver upstream through
the input tree once I've implemented your suggested fixes?
--
David Härdeman
--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [patch 2/2] Add a driver for the Winbond WPCD376I Consumer IR hardware
2009-08-13 9:34 ` David Härdeman
@ 2009-08-13 15:56 ` Dmitry Torokhov
-1 siblings, 0 replies; 27+ messages in thread
From: Dmitry Torokhov @ 2009-08-13 15:56 UTC (permalink / raw)
To: David Härdeman
Cc: linux-kernel, linux-input, jbarnes, akpm, bjorn.helgaas, randy.dunlap
On Thu, Aug 13, 2009 at 11:34:44AM +0200, David Härdeman wrote:
> On Thu, August 13, 2009 08:58, Dmitry Torokhov wrote:
> > Hi David,
>
> Hi,
>
> > Please keep Makefile sorted alphabetically.
>
> Ok
>
> >> +static struct wbcir_key rc6_def_keymap[] = {
> >> + { 0x800F0400, KEY_0 },
> >> + { 0x800F0401, KEY_1 },
> >> + { 0x800F0402, KEY_2 },
> >> + { 0x800F0403, KEY_3 },
> >> + { 0x800F0404, KEY_4 },
> >> + { 0x800F0405, KEY_5 },
> >> + { 0x800F0406, KEY_6 },
> >> + { 0x800F0407, KEY_7 },
> >> + { 0x800F0408, KEY_8 },
> >> + { 0x800F0409, KEY_9 },
> >
> > Make these ones KEY_NUMERIC_* as well, this should help users whose
> > keymaps have numbers in upper register normally.
>
> Ok
>
> >> + { 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 },
> >
> > Umm, it looks like if you do (code & 0x800F0400) you can switch to
> > standard array-based keymap and won't even need list manipulation.
>
> I didn't want to do that since it would potentially tie the driver to one
> particular remote (the one I used for testing while writing the driver).
> The hardware isn't shipped with any specific remote so that wouldn't be
> very user friendly.
>
> This was the best solution I could come up with without adding some real
> limitations to the functionality of the driver.
I see.
>
> The main problem right now is that getkeycode is practically useless since
> you can't blindly guess at a full range of 2^32 different scancodes to get
> the complete keymap. Perhaps a index-based getkeycode would make sense...
The drivers that have such sparce keymaps are expected to issue
EV_MSC/MSC_SCAN events to aid userspace in identifying the "scancodes"
that are emitted by the device.
>
> Anyway, I hope that I can make this a moot point in the future by adding
> more IR-specific functionality to the input system. I've been thinking
> along the lines of IR-specific get/set-keycode ioctl's which would take a
> struct which defines the IR command to map to a key.
>
> >> +};
> >> +
> >> +/* Registers and other state is protected by wbcir_lock */
> >> +struct wbcir_data {
> >> + 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 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;
> >
> > I think this has a potential to deadlock... Set and get keycodes are
> > called with event lock taken, and then your implementations acquire
> > keytable lock. When you emit input events the opposite happens - you
> > take the keytable lock and then input core takes event lock.
>
> Good catch, I'll have to look into that...
>
> >> +static struct device_attribute dev_attr_last_scancode = {
> >> + .attr = {
> >> + .name = "last_scancode",
> >> + .mode = 0444,
> >> + },
> >> + .show = wbcir_show_last_scancode,
> >> + .store = NULL,
> >> +
> >> +};
> >
> > Why is this needed? And if this is needed we have a nice macro
> > for that.
>
> I hope I've explained it wrt. EV_IR in my other mail. It's for building
> keymaps of unknown remotes. And it'll go away once EV_IR is supported so I
> don't think there's much point in fiddling with it now?
>
Because once the driver is in mainline it becomes part of userspace ABI
and has to stay for a looong time.
> >> +static int
> >> +wbcir_probe(struct pnp_dev *device, const struct pnp_device_id *dev_id)
> >
> > __devinit
> >
> ...
> >> + dev_info(&device->dev, "Found device "
> >> + "(w: 0x%lX, e: 0x%lX, s: 0x%lX, i: %u)\n",
> >> + data->wbase, data->ebase, data->sbase, data->irq);
> >> +
> >
> > dev_dbg() I think.
>
> Ok
>
> >> +static void
> >> +wbcir_remove(struct pnp_dev *device)
> >
> > __devexit
>
> Ok
>
> >> +static struct pnp_driver wbcir_driver = {
> >> + .name = WBCIR_ACPI_NAME,
> >> + .id_table = wbcir_ids,
> >> + .probe = wbcir_probe,
> >> + .remove = wbcir_remove,
> >
> > __devexit_p()
>
> Ok
>
> >> + .suspend = wbcir_suspend,
> >> + .resume = wbcir_resume,
> >
> > Switch to dev_pm_ops?
>
> Don't want to do that just yet. dev_pm_ops wasn't even on my radar until
> yesterday when I stumbled upon the documentation (in a header file rather
> than in Documentation/...eh?). I'll certainly look into it when the
> suspend/resume functionality has seen more testing and bug fixing.
>
> Thanks for the review. Are you willing to push the driver upstream through
> the input tree once I've implemented your suggested fixes?
>
I'd need to take a look at your EV_IR patyches and see how they will
affect this driver. I do nt want to merge something that will stay one
way for half a release and then will switch to completely new interface.
--
Dmitry
^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [patch 2/2] Add a driver for the Winbond WPCD376I Consumer IR hardware
@ 2009-08-13 15:56 ` Dmitry Torokhov
0 siblings, 0 replies; 27+ messages in thread
From: Dmitry Torokhov @ 2009-08-13 15:56 UTC (permalink / raw)
To: David Härdeman
Cc: linux-kernel, linux-input, jbarnes, akpm, bjorn.helgaas, randy.dunlap
On Thu, Aug 13, 2009 at 11:34:44AM +0200, David Härdeman wrote:
> On Thu, August 13, 2009 08:58, Dmitry Torokhov wrote:
> > Hi David,
>
> Hi,
>
> > Please keep Makefile sorted alphabetically.
>
> Ok
>
> >> +static struct wbcir_key rc6_def_keymap[] = {
> >> + { 0x800F0400, KEY_0 },
> >> + { 0x800F0401, KEY_1 },
> >> + { 0x800F0402, KEY_2 },
> >> + { 0x800F0403, KEY_3 },
> >> + { 0x800F0404, KEY_4 },
> >> + { 0x800F0405, KEY_5 },
> >> + { 0x800F0406, KEY_6 },
> >> + { 0x800F0407, KEY_7 },
> >> + { 0x800F0408, KEY_8 },
> >> + { 0x800F0409, KEY_9 },
> >
> > Make these ones KEY_NUMERIC_* as well, this should help users whose
> > keymaps have numbers in upper register normally.
>
> Ok
>
> >> + { 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 },
> >
> > Umm, it looks like if you do (code & 0x800F0400) you can switch to
> > standard array-based keymap and won't even need list manipulation.
>
> I didn't want to do that since it would potentially tie the driver to one
> particular remote (the one I used for testing while writing the driver).
> The hardware isn't shipped with any specific remote so that wouldn't be
> very user friendly.
>
> This was the best solution I could come up with without adding some real
> limitations to the functionality of the driver.
I see.
>
> The main problem right now is that getkeycode is practically useless since
> you can't blindly guess at a full range of 2^32 different scancodes to get
> the complete keymap. Perhaps a index-based getkeycode would make sense...
The drivers that have such sparce keymaps are expected to issue
EV_MSC/MSC_SCAN events to aid userspace in identifying the "scancodes"
that are emitted by the device.
>
> Anyway, I hope that I can make this a moot point in the future by adding
> more IR-specific functionality to the input system. I've been thinking
> along the lines of IR-specific get/set-keycode ioctl's which would take a
> struct which defines the IR command to map to a key.
>
> >> +};
> >> +
> >> +/* Registers and other state is protected by wbcir_lock */
> >> +struct wbcir_data {
> >> + 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 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;
> >
> > I think this has a potential to deadlock... Set and get keycodes are
> > called with event lock taken, and then your implementations acquire
> > keytable lock. When you emit input events the opposite happens - you
> > take the keytable lock and then input core takes event lock.
>
> Good catch, I'll have to look into that...
>
> >> +static struct device_attribute dev_attr_last_scancode = {
> >> + .attr = {
> >> + .name = "last_scancode",
> >> + .mode = 0444,
> >> + },
> >> + .show = wbcir_show_last_scancode,
> >> + .store = NULL,
> >> +
> >> +};
> >
> > Why is this needed? And if this is needed we have a nice macro
> > for that.
>
> I hope I've explained it wrt. EV_IR in my other mail. It's for building
> keymaps of unknown remotes. And it'll go away once EV_IR is supported so I
> don't think there's much point in fiddling with it now?
>
Because once the driver is in mainline it becomes part of userspace ABI
and has to stay for a looong time.
> >> +static int
> >> +wbcir_probe(struct pnp_dev *device, const struct pnp_device_id *dev_id)
> >
> > __devinit
> >
> ...
> >> + dev_info(&device->dev, "Found device "
> >> + "(w: 0x%lX, e: 0x%lX, s: 0x%lX, i: %u)\n",
> >> + data->wbase, data->ebase, data->sbase, data->irq);
> >> +
> >
> > dev_dbg() I think.
>
> Ok
>
> >> +static void
> >> +wbcir_remove(struct pnp_dev *device)
> >
> > __devexit
>
> Ok
>
> >> +static struct pnp_driver wbcir_driver = {
> >> + .name = WBCIR_ACPI_NAME,
> >> + .id_table = wbcir_ids,
> >> + .probe = wbcir_probe,
> >> + .remove = wbcir_remove,
> >
> > __devexit_p()
>
> Ok
>
> >> + .suspend = wbcir_suspend,
> >> + .resume = wbcir_resume,
> >
> > Switch to dev_pm_ops?
>
> Don't want to do that just yet. dev_pm_ops wasn't even on my radar until
> yesterday when I stumbled upon the documentation (in a header file rather
> than in Documentation/...eh?). I'll certainly look into it when the
> suspend/resume functionality has seen more testing and bug fixing.
>
> Thanks for the review. Are you willing to push the driver upstream through
> the input tree once I've implemented your suggested fixes?
>
I'd need to take a look at your EV_IR patyches and see how they will
affect this driver. I do nt want to merge something that will stay one
way for half a release and then will switch to completely new interface.
--
Dmitry
--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [patch 2/2] Add a driver for the Winbond WPCD376I Consumer IR hardware
2009-08-13 6:58 ` Dmitry Torokhov
@ 2009-08-13 17:14 ` David Härdeman
2009-08-13 17:14 ` David Härdeman
1 sibling, 0 replies; 27+ messages in thread
From: David Härdeman @ 2009-08-13 17:14 UTC (permalink / raw)
To: Dmitry Torokhov; +Cc: linux-kernel, linux-input
On Wed, Aug 12, 2009 at 11:58:22PM -0700, Dmitry Torokhov wrote:
>> +/* Registers and other state is protected by wbcir_lock */
>> +struct wbcir_data {
>> + 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 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;
>
>I think this has a potential to deadlock... Set and get keycodes are
>called with event lock taken, and then your implementations acquire
>keytable lock. When you emit input events the opposite happens - you
>take the keytable lock and then input core takes event lock.
I've taken a look at it, and I think it's ok. set/getkeycode take the
event lock and then the keytable lock.
The irqhandler call sequence (which generates the input events) will
take the keytable lock, copy the keycode, release the lock and report
the event (which will take the event lock).
--
David Härdeman
^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [patch 2/2] Add a driver for the Winbond WPCD376I Consumer IR hardware
@ 2009-08-13 17:14 ` David Härdeman
0 siblings, 0 replies; 27+ messages in thread
From: David Härdeman @ 2009-08-13 17:14 UTC (permalink / raw)
To: Dmitry Torokhov; +Cc: linux-kernel, linux-input
On Wed, Aug 12, 2009 at 11:58:22PM -0700, Dmitry Torokhov wrote:
>> +/* Registers and other state is protected by wbcir_lock */
>> +struct wbcir_data {
>> + 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 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;
>
>I think this has a potential to deadlock... Set and get keycodes are
>called with event lock taken, and then your implementations acquire
>keytable lock. When you emit input events the opposite happens - you
>take the keytable lock and then input core takes event lock.
I've taken a look at it, and I think it's ok. set/getkeycode take the
event lock and then the keytable lock.
The irqhandler call sequence (which generates the input events) will
take the keytable lock, copy the keycode, release the lock and report
the event (which will take the event lock).
--
David Härdeman
--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [patch 2/2] Add a driver for the Winbond WPCD376I Consumer IR hardware
2009-08-13 15:56 ` Dmitry Torokhov
@ 2009-08-13 17:58 ` David Härdeman
-1 siblings, 0 replies; 27+ messages in thread
From: David Härdeman @ 2009-08-13 17:58 UTC (permalink / raw)
To: Dmitry Torokhov; +Cc: linux-kernel, linux-input, jbarnes, akpm
On Thu, Aug 13, 2009 at 08:56:37AM -0700, Dmitry Torokhov wrote:
>On Thu, Aug 13, 2009 at 11:34:44AM +0200, David Härdeman wrote:
>> The main problem right now is that getkeycode is practically useless since
>> you can't blindly guess at a full range of 2^32 different scancodes to get
>> the complete keymap. Perhaps a index-based getkeycode would make sense...
>
>The drivers that have such sparce keymaps are expected to issue
>EV_MSC/MSC_SCAN events to aid userspace in identifying the "scancodes"
>that are emitted by the device.
Ok, I've added EV_MSC/MSC_SCAN support.
>>>> +static struct device_attribute dev_attr_last_scancode = {
>>>> + .attr = {
>>>> + .name = "last_scancode",
>>>> + .mode = 0444,
>>>> + },
>>>> + .show = wbcir_show_last_scancode,
>>>> + .store = NULL,
>>>> +
>>>> +};
>>>
>>> Why is this needed? And if this is needed we have a nice macro
>>> for that.
>>
>> I hope I've explained it wrt. EV_IR in my other mail. It's for building
>> keymaps of unknown remotes. And it'll go away once EV_IR is supported so I
>> don't think there's much point in fiddling with it now?
>>
>
>Because once the driver is in mainline it becomes part of userspace ABI
>and has to stay for a looong time.
I've removed the sysfs attribute as EV_MSC/MSC_SCAN provides the same
functionality.
>> Thanks for the review. Are you willing to push the driver upstream through
>> the input tree once I've implemented your suggested fixes?
>>
>
>I'd need to take a look at your EV_IR patyches and see how they will
>affect this driver. I do nt want to merge something that will stay one
>way for half a release and then will switch to completely new interface.
The EV_IR functionality is intended to be used in addition to regular
key up/down/repeat events. Much like EV_MSC/MSC_SCAN but more
descriptive and specific to IR protocols. Then advanced user-space apps
can choose whether to use the in-kernel keymap or map remote codes
directly to suitable events (and it allows remote keymaps to be built
easily in user-space).
As the future EV_IR functionality is additional to the current
functionality, I hope the driver still can be merged before I have EV_IR
patches ready for review. I'll post an updated patch shortly.
--
David Härdeman
^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [patch 2/2] Add a driver for the Winbond WPCD376I Consumer IR hardware
@ 2009-08-13 17:58 ` David Härdeman
0 siblings, 0 replies; 27+ messages in thread
From: David Härdeman @ 2009-08-13 17:58 UTC (permalink / raw)
To: Dmitry Torokhov; +Cc: linux-kernel, linux-input, jbarnes, akpm
On Thu, Aug 13, 2009 at 08:56:37AM -0700, Dmitry Torokhov wrote:
>On Thu, Aug 13, 2009 at 11:34:44AM +0200, David Härdeman wrote:
>> The main problem right now is that getkeycode is practically useless since
>> you can't blindly guess at a full range of 2^32 different scancodes to get
>> the complete keymap. Perhaps a index-based getkeycode would make sense...
>
>The drivers that have such sparce keymaps are expected to issue
>EV_MSC/MSC_SCAN events to aid userspace in identifying the "scancodes"
>that are emitted by the device.
Ok, I've added EV_MSC/MSC_SCAN support.
>>>> +static struct device_attribute dev_attr_last_scancode = {
>>>> + .attr = {
>>>> + .name = "last_scancode",
>>>> + .mode = 0444,
>>>> + },
>>>> + .show = wbcir_show_last_scancode,
>>>> + .store = NULL,
>>>> +
>>>> +};
>>>
>>> Why is this needed? And if this is needed we have a nice macro
>>> for that.
>>
>> I hope I've explained it wrt. EV_IR in my other mail. It's for building
>> keymaps of unknown remotes. And it'll go away once EV_IR is supported so I
>> don't think there's much point in fiddling with it now?
>>
>
>Because once the driver is in mainline it becomes part of userspace ABI
>and has to stay for a looong time.
I've removed the sysfs attribute as EV_MSC/MSC_SCAN provides the same
functionality.
>> Thanks for the review. Are you willing to push the driver upstream through
>> the input tree once I've implemented your suggested fixes?
>>
>
>I'd need to take a look at your EV_IR patyches and see how they will
>affect this driver. I do nt want to merge something that will stay one
>way for half a release and then will switch to completely new interface.
The EV_IR functionality is intended to be used in addition to regular
key up/down/repeat events. Much like EV_MSC/MSC_SCAN but more
descriptive and specific to IR protocols. Then advanced user-space apps
can choose whether to use the in-kernel keymap or map remote codes
directly to suitable events (and it allows remote keymaps to be built
easily in user-space).
As the future EV_IR functionality is additional to the current
functionality, I hope the driver still can be merged before I have EV_IR
patches ready for review. I'll post an updated patch shortly.
--
David Härdeman
--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [patch 0/2] Winbond IR Driver - v2
2009-08-09 9:56 ` david
` (2 preceding siblings ...)
(?)
@ 2009-08-15 3:02 ` Maxim Levitsky
2009-08-15 20:10 ` David Härdeman
-1 siblings, 1 reply; 27+ messages in thread
From: Maxim Levitsky @ 2009-08-15 3:02 UTC (permalink / raw)
To: david
Cc: linux-kernel, linux-input, jbarnes, akpm, bjorn.helgaas, randy.dunlap
On Sun, 2009-08-09 at 11:56 +0200, david@hardeman.nu wrote:
> Here's a new patch set which should replace all the patches currently in the
> -mm tree for the winbond cir driver. The new series incorporate feedback from
> Bjorn Helgaas (convert the driver from ACPI to PNP) and Randy Dunlap (Kconfig
> fixes).
>
> The IR decoding can still be improved but the driver works for in daily use
> decoding RC6 commands, so I believe it is ready to go upstream.
>
Hi.
As I understand, this hardware returns sampled IR signal, so it can be
used with any remote, right?
Then why not to implement lirc driver?
This driver as I understand is a driver for single remote shipped with
the notebook.
I have recently wrote a lirc driver for my receiver, a lirc driver. Now
I can use all my remotes.
And no, I don't need any software support for that. LIRC happily
forwards all events via uinput to kernel, so I use it a an ordinary
keyboard.
Best regards,
Maxim Levitsky
^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [patch 0/2] Winbond IR Driver - v2
2009-08-15 3:02 ` [patch 0/2] Winbond IR Driver - v2 Maxim Levitsky
@ 2009-08-15 20:10 ` David Härdeman
0 siblings, 0 replies; 27+ messages in thread
From: David Härdeman @ 2009-08-15 20:10 UTC (permalink / raw)
To: Maxim Levitsky
Cc: linux-kernel, linux-input, jbarnes, akpm, bjorn.helgaas,
randy.dunlap, dmitry.torokhov
On Sat, Aug 15, 2009 at 06:02:27AM +0300, Maxim Levitsky wrote:
>On Sun, 2009-08-09 at 11:56 +0200, david@hardeman.nu wrote:
>> Here's a new patch set which should replace all the patches currently in the
>> -mm tree for the winbond cir driver. The new series incorporate feedback from
>> Bjorn Helgaas (convert the driver from ACPI to PNP) and Randy Dunlap (Kconfig
>> fixes).
>>
>> The IR decoding can still be improved but the driver works for in daily use
>> decoding RC6 commands, so I believe it is ready to go upstream.
>>
>
>Hi.
Hi,
>As I understand, this hardware returns sampled IR signal, so it can be
>used with any remote, right?
Yes.
>Then why not to implement lirc driver?
That's a fair question, but I'm afraid you're putting the cart before
the horse.
The question is not why anyone would want to write an in-kernel driver
but rather why anyone would want to write an out-of-kernel driver.
There have been repeated attempts to get LIRC merged with the kernel,
and the feedback has been pretty consistent - make it part of the input
subsystem.
I have written the driver for the input system and it limits the driver
somewhat. I am working on extending the input system to accomodate IR
drivers (see the discussion of EV_IR on the linux-input list).
As an example, the input system already has a quite extensive set of
additional functions to deal with force-feedback hardware (mostly
additional ioctl's, see include/linux/input.h). I want the input system
to grow similar extensions for IR hardware which would then allow IR
drivers to be written as input drivers with their full functionality
exposed to user-space.
Feel free to help me out in implementing that API, and porting LIRC
drivers, and all the benefits of in-kernel drivers will flow from that
work.
To be more specific, the things that are on my radar right now are:
o IR TX - I believe an IOCTL which will take an array of signed integers
symbolizing IR on/off timings together with parameters for IR
carrier modulation is the way to go and to let userspace deal
with the generation of those integers (for a number of
reasons).
o IR RX - We need to expose the full functionality of hardware with
multiple receivers to userspace. Microsoft's current CIR specs
mandate that IR hardware that includes transmitters must
include both a long-distance, narrow-band receiver for common
use and a short-distance, wide-band receiver for learning
purposes. Expect future hardware to meet those demands...
o Hardware differences -
The lircd daemon currently has a "-H" parameter to specify the
driver to use. To me, that is a dead giveaway for an
inconsistent kernel <-> userspace API. We can do better, given
the proper HW flags from the input layer, userspace should
adapt automatically. Some hardware, say
drivers/media/dvb/ttpci/budget-ci.c (where you'll find my name
in the git logs) only support passing decoded RC5 events. Some
hardware (like the lirc mceusb(2) driver) passes the timing
for the IR signals and supports any remotely sane IR protocol.
The WPCD376I has even a bit more capabilities like
wake-from-poweroff, two receivers and up to four transmitters
with the capability to detect which transmitters are connected
and which ones are not.
The challenge at this point is not in writing drivers for the
hardware, it is designing an extension to the input layer that
can sanely deal with the diversity.
Anyhow, do check the linux-input list for my EV_IR patches, they are the
first step in that direction (and they're based on the API proposal by
Jon Smirl posted at the end of last year). Raw signalling information
for wonky remotes is part of that proposal (though the patches are not
fully fleshed out yet).
>This driver as I understand is a driver for single remote shipped with
>the notebook.
It's a driver for a winbond chipset shipped with many Intel desktop
"media" motherboards (DP35DP, DG33TL, DX388T, DX488T2, DP455G, DG45ID
and DG45FC are the ones I'm aware of).
>I have recently wrote a lirc driver for my receiver, a lirc driver. Now
>I can use all my remotes.
So can I. But for a easy-to-use and consistent user interface, some
changes are needed to the input layer.
>And no, I don't need any software support for that. LIRC happily
>forwards all events via uinput to kernel, so I use it a an ordinary
>keyboard.
I know LIRC and what it can do, look in the CVS logs and the LIRC
mailing list archives and I think you'll find my name there.
Regards,
David Härdeman
^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [patch 0/2] Winbond IR Driver - v2
@ 2009-08-15 20:10 ` David Härdeman
0 siblings, 0 replies; 27+ messages in thread
From: David Härdeman @ 2009-08-15 20:10 UTC (permalink / raw)
To: Maxim Levitsky
Cc: linux-kernel, linux-input, jbarnes, akpm, bjorn.helgaas,
randy.dunlap, dmitry.torokhov
On Sat, Aug 15, 2009 at 06:02:27AM +0300, Maxim Levitsky wrote:
>On Sun, 2009-08-09 at 11:56 +0200, david@hardeman.nu wrote:
>> Here's a new patch set which should replace all the patches currently in the
>> -mm tree for the winbond cir driver. The new series incorporate feedback from
>> Bjorn Helgaas (convert the driver from ACPI to PNP) and Randy Dunlap (Kconfig
>> fixes).
>>
>> The IR decoding can still be improved but the driver works for in daily use
>> decoding RC6 commands, so I believe it is ready to go upstream.
>>
>
>Hi.
Hi,
>As I understand, this hardware returns sampled IR signal, so it can be
>used with any remote, right?
Yes.
>Then why not to implement lirc driver?
That's a fair question, but I'm afraid you're putting the cart before
the horse.
The question is not why anyone would want to write an in-kernel driver
but rather why anyone would want to write an out-of-kernel driver.
There have been repeated attempts to get LIRC merged with the kernel,
and the feedback has been pretty consistent - make it part of the input
subsystem.
I have written the driver for the input system and it limits the driver
somewhat. I am working on extending the input system to accomodate IR
drivers (see the discussion of EV_IR on the linux-input list).
As an example, the input system already has a quite extensive set of
additional functions to deal with force-feedback hardware (mostly
additional ioctl's, see include/linux/input.h). I want the input system
to grow similar extensions for IR hardware which would then allow IR
drivers to be written as input drivers with their full functionality
exposed to user-space.
Feel free to help me out in implementing that API, and porting LIRC
drivers, and all the benefits of in-kernel drivers will flow from that
work.
To be more specific, the things that are on my radar right now are:
o IR TX - I believe an IOCTL which will take an array of signed integers
symbolizing IR on/off timings together with parameters for IR
carrier modulation is the way to go and to let userspace deal
with the generation of those integers (for a number of
reasons).
o IR RX - We need to expose the full functionality of hardware with
multiple receivers to userspace. Microsoft's current CIR specs
mandate that IR hardware that includes transmitters must
include both a long-distance, narrow-band receiver for common
use and a short-distance, wide-band receiver for learning
purposes. Expect future hardware to meet those demands...
o Hardware differences -
The lircd daemon currently has a "-H" parameter to specify the
driver to use. To me, that is a dead giveaway for an
inconsistent kernel <-> userspace API. We can do better, given
the proper HW flags from the input layer, userspace should
adapt automatically. Some hardware, say
drivers/media/dvb/ttpci/budget-ci.c (where you'll find my name
in the git logs) only support passing decoded RC5 events. Some
hardware (like the lirc mceusb(2) driver) passes the timing
for the IR signals and supports any remotely sane IR protocol.
The WPCD376I has even a bit more capabilities like
wake-from-poweroff, two receivers and up to four transmitters
with the capability to detect which transmitters are connected
and which ones are not.
The challenge at this point is not in writing drivers for the
hardware, it is designing an extension to the input layer that
can sanely deal with the diversity.
Anyhow, do check the linux-input list for my EV_IR patches, they are the
first step in that direction (and they're based on the API proposal by
Jon Smirl posted at the end of last year). Raw signalling information
for wonky remotes is part of that proposal (though the patches are not
fully fleshed out yet).
>This driver as I understand is a driver for single remote shipped with
>the notebook.
It's a driver for a winbond chipset shipped with many Intel desktop
"media" motherboards (DP35DP, DG33TL, DX388T, DX488T2, DP455G, DG45ID
and DG45FC are the ones I'm aware of).
>I have recently wrote a lirc driver for my receiver, a lirc driver. Now
>I can use all my remotes.
So can I. But for a easy-to-use and consistent user interface, some
changes are needed to the input layer.
>And no, I don't need any software support for that. LIRC happily
>forwards all events via uinput to kernel, so I use it a an ordinary
>keyboard.
I know LIRC and what it can do, look in the CVS logs and the LIRC
mailing list archives and I think you'll find my name there.
Regards,
David Härdeman
--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [patch 0/2] Winbond IR Driver - v2
2009-08-15 20:10 ` David Härdeman
@ 2009-08-15 22:10 ` Maxim Levitsky
-1 siblings, 0 replies; 27+ messages in thread
From: Maxim Levitsky @ 2009-08-15 22:10 UTC (permalink / raw)
To: David Härdeman
Cc: linux-kernel, linux-input, jbarnes, akpm, bjorn.helgaas,
randy.dunlap, dmitry.torokhov
On Sat, 2009-08-15 at 22:10 +0200, David Härdeman wrote:
> On Sat, Aug 15, 2009 at 06:02:27AM +0300, Maxim Levitsky wrote:
> >On Sun, 2009-08-09 at 11:56 +0200, david@hardeman.nu wrote:
> >> Here's a new patch set which should replace all the patches currently in the
> >> -mm tree for the winbond cir driver. The new series incorporate feedback from
> >> Bjorn Helgaas (convert the driver from ACPI to PNP) and Randy Dunlap (Kconfig
> >> fixes).
> >>
> >> The IR decoding can still be improved but the driver works for in daily use
> >> decoding RC6 commands, so I believe it is ready to go upstream.
> >>
> >
> >Hi.
>
> Hi,
>
> >As I understand, this hardware returns sampled IR signal, so it can be
> >used with any remote, right?
>
> Yes.
>
> >Then why not to implement lirc driver?
>
> That's a fair question, but I'm afraid you're putting the cart before
> the horse.
No, I just want to write the driver that fully exposes the hardware. I
don't care if it has to be outside or not.
>
> The question is not why anyone would want to write an in-kernel driver
> but rather why anyone would want to write an out-of-kernel driver.
>
> There have been repeated attempts to get LIRC merged with the kernel,
> and the feedback has been pretty consistent - make it part of the input
> subsystem.
>
> I have written the driver for theo input system and it limits the driver
> somewhat. I am working on extending the input system to accomodate IR
> drivers (see the discussion of EV_IR on the linux-input list).
The EV_IR thing is that he attempts to put all IR decoding in kernel,
and on top of that create a configfs config system.
I first thought it would be nice, but then realized that this is really
bad idea.
Currently LIRC has very oiled system for decoding pretty much every
remote that exist. It can cope with all kind of troubles, including not
very accurate receivers.
On top of that there are pure userspace devices, like a IR diode
connected to soundcard. It would be nice to do all the raw signal
decoding in one place. Once signal is decoded, lirc forwards the input
signal to the kernel via uinput, so it is a part of input system.
The way kernel hands in the raw IR data to lirc doesn't matter much. It
is really just a queue of numbers. It can be forced into input system,
but there is really no need for that.
>
> As an example, the input system already has a quite extensive set of
> additional functions to deal with force-feedback hardware (mostly
> additional ioctl's, see include/linux/input.h). I want the input system
> to grow similar extensions for IR hardware which would then allow IR
> drivers to be written as input drivers with their full functionality
> exposed to user-space.
This is straight forward to implement as an ioctl for that lirc device.
>
> Feel free to help me out in implementing that API, and porting LIRC
> drivers, and all the benefits of in-kernel drivers will flow from that
> work.
This isn't a bad idea.
>
> To be more specific, the things that are on my radar right now are:
>
> o IR TX - I believe an IOCTL which will take an array of signed integers
> symbolizing IR on/off timings together with parameters for IR
> carrier modulation is the way to go and to let userspace deal
> with the generation of those integers (for a number of
> reasons).
This is done in lirc already via ioctls.
>
> o IR RX - We need to expose the full functionality of hardware with
> multiple receivers to userspace. Microsoft's current CIR specs
> mandate that IR hardware that includes transmitters must
> include both a long-distance, narrow-band receiver for common
> use and a short-distance, wide-band receiver for learning
> purposes. Expect future hardware to meet those demands...
>
> o Hardware differences -
> The lircd daemon currently has a "-H" parameter to specify the
> driver to use. To me, that is a dead giveaway for an
> inconsistent kernel <-> userspace API. We can do better, given
> the proper HW flags from the input layer, userspace should
> adapt automatically. Some hardware, say
> drivers/media/dvb/ttpci/budget-ci.c (where you'll find my name
> in the git logs) only support passing decoded RC5 events. Some
> hardware (like the lirc mceusb(2) driver) passes the timing
> for the IR signals and supports any remotely sane IR protocol.
> The WPCD376I has even a bit more capabilities like
> wake-from-poweroff, two receivers and up to four transmitters
> with the capability to detect which transmitters are connected
> and which ones are not.
This isn't a problem. LIRC has a 'generic' driver "default" its name I
think, that deals with kernel drivers. It has the -H for all kinds of
wierd and old devices that work via userspace.
>
> The challenge at this point is not in writing drivers for the
> hardware, it is designing an extension to the input layer that
> can sanely deal with the diversity.
>
> Anyhow, do check the linux-input list for my EV_IR patches, they are the
> first step in that direction (and they're based on the API proposal by
> Jon Smirl posted at the end of last year). Raw signalling information
> for wonky remotes is part of that proposal (though the patches are not
> fully fleshed out yet).
>
> >This driver as I understand is a driver for single remote shipped with
> >the notebook.
>
> It's a driver for a winbond chipset shipped with many Intel desktop
> "media" motherboards (DP35DP, DG33TL, DX388T, DX488T2, DP455G, DG45ID
> and DG45FC are the ones I'm aware of).
But it won't work with my JVC remote?....
>
> >I have recently wrote a lirc driver for my receiver, a lirc driver. Now
> >I can use all my remotes.
>
> So can I. But for a easy-to-use and consistent user interface, some
> changes are needed to the input layer.
Best regards,
Maxim Levitsky
>
> >And no, I don't need any software support for that. LIRC happily
> >forwards all events via uinput to kernel, so I use it a an ordinary
> >keyboard.
>
> I know LIRC and what it can do, look in the CVS logs and the LIRC
> mailing list archives and I think you'll find my name there.
>
> Regards,
> David Härdeman
^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [patch 0/2] Winbond IR Driver - v2
@ 2009-08-15 22:10 ` Maxim Levitsky
0 siblings, 0 replies; 27+ messages in thread
From: Maxim Levitsky @ 2009-08-15 22:10 UTC (permalink / raw)
To: David Härdeman
Cc: linux-kernel, linux-input, jbarnes, akpm, bjorn.helgaas,
randy.dunlap, dmitry.torokhov
On Sat, 2009-08-15 at 22:10 +0200, David Härdeman wrote:
> On Sat, Aug 15, 2009 at 06:02:27AM +0300, Maxim Levitsky wrote:
> >On Sun, 2009-08-09 at 11:56 +0200, david@hardeman.nu wrote:
> >> Here's a new patch set which should replace all the patches currently in the
> >> -mm tree for the winbond cir driver. The new series incorporate feedback from
> >> Bjorn Helgaas (convert the driver from ACPI to PNP) and Randy Dunlap (Kconfig
> >> fixes).
> >>
> >> The IR decoding can still be improved but the driver works for in daily use
> >> decoding RC6 commands, so I believe it is ready to go upstream.
> >>
> >
> >Hi.
>
> Hi,
>
> >As I understand, this hardware returns sampled IR signal, so it can be
> >used with any remote, right?
>
> Yes.
>
> >Then why not to implement lirc driver?
>
> That's a fair question, but I'm afraid you're putting the cart before
> the horse.
No, I just want to write the driver that fully exposes the hardware. I
don't care if it has to be outside or not.
>
> The question is not why anyone would want to write an in-kernel driver
> but rather why anyone would want to write an out-of-kernel driver.
>
> There have been repeated attempts to get LIRC merged with the kernel,
> and the feedback has been pretty consistent - make it part of the input
> subsystem.
>
> I have written the driver for theo input system and it limits the driver
> somewhat. I am working on extending the input system to accomodate IR
> drivers (see the discussion of EV_IR on the linux-input list).
The EV_IR thing is that he attempts to put all IR decoding in kernel,
and on top of that create a configfs config system.
I first thought it would be nice, but then realized that this is really
bad idea.
Currently LIRC has very oiled system for decoding pretty much every
remote that exist. It can cope with all kind of troubles, including not
very accurate receivers.
On top of that there are pure userspace devices, like a IR diode
connected to soundcard. It would be nice to do all the raw signal
decoding in one place. Once signal is decoded, lirc forwards the input
signal to the kernel via uinput, so it is a part of input system.
The way kernel hands in the raw IR data to lirc doesn't matter much. It
is really just a queue of numbers. It can be forced into input system,
but there is really no need for that.
>
> As an example, the input system already has a quite extensive set of
> additional functions to deal with force-feedback hardware (mostly
> additional ioctl's, see include/linux/input.h). I want the input system
> to grow similar extensions for IR hardware which would then allow IR
> drivers to be written as input drivers with their full functionality
> exposed to user-space.
This is straight forward to implement as an ioctl for that lirc device.
>
> Feel free to help me out in implementing that API, and porting LIRC
> drivers, and all the benefits of in-kernel drivers will flow from that
> work.
This isn't a bad idea.
>
> To be more specific, the things that are on my radar right now are:
>
> o IR TX - I believe an IOCTL which will take an array of signed integers
> symbolizing IR on/off timings together with parameters for IR
> carrier modulation is the way to go and to let userspace deal
> with the generation of those integers (for a number of
> reasons).
This is done in lirc already via ioctls.
>
> o IR RX - We need to expose the full functionality of hardware with
> multiple receivers to userspace. Microsoft's current CIR specs
> mandate that IR hardware that includes transmitters must
> include both a long-distance, narrow-band receiver for common
> use and a short-distance, wide-band receiver for learning
> purposes. Expect future hardware to meet those demands...
>
> o Hardware differences -
> The lircd daemon currently has a "-H" parameter to specify the
> driver to use. To me, that is a dead giveaway for an
> inconsistent kernel <-> userspace API. We can do better, given
> the proper HW flags from the input layer, userspace should
> adapt automatically. Some hardware, say
> drivers/media/dvb/ttpci/budget-ci.c (where you'll find my name
> in the git logs) only support passing decoded RC5 events. Some
> hardware (like the lirc mceusb(2) driver) passes the timing
> for the IR signals and supports any remotely sane IR protocol.
> The WPCD376I has even a bit more capabilities like
> wake-from-poweroff, two receivers and up to four transmitters
> with the capability to detect which transmitters are connected
> and which ones are not.
This isn't a problem. LIRC has a 'generic' driver "default" its name I
think, that deals with kernel drivers. It has the -H for all kinds of
wierd and old devices that work via userspace.
>
> The challenge at this point is not in writing drivers for the
> hardware, it is designing an extension to the input layer that
> can sanely deal with the diversity.
>
> Anyhow, do check the linux-input list for my EV_IR patches, they are the
> first step in that direction (and they're based on the API proposal by
> Jon Smirl posted at the end of last year). Raw signalling information
> for wonky remotes is part of that proposal (though the patches are not
> fully fleshed out yet).
>
> >This driver as I understand is a driver for single remote shipped with
> >the notebook.
>
> It's a driver for a winbond chipset shipped with many Intel desktop
> "media" motherboards (DP35DP, DG33TL, DX388T, DX488T2, DP455G, DG45ID
> and DG45FC are the ones I'm aware of).
But it won't work with my JVC remote?....
>
> >I have recently wrote a lirc driver for my receiver, a lirc driver. Now
> >I can use all my remotes.
>
> So can I. But for a easy-to-use and consistent user interface, some
> changes are needed to the input layer.
Best regards,
Maxim Levitsky
>
> >And no, I don't need any software support for that. LIRC happily
> >forwards all events via uinput to kernel, so I use it a an ordinary
> >keyboard.
>
> I know LIRC and what it can do, look in the CVS logs and the LIRC
> mailing list archives and I think you'll find my name there.
>
> Regards,
> David Härdeman
--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [patch 0/2] Winbond IR Driver - v2
2009-08-15 22:10 ` Maxim Levitsky
@ 2009-08-16 18:43 ` David Härdeman
-1 siblings, 0 replies; 27+ messages in thread
From: David Härdeman @ 2009-08-16 18:43 UTC (permalink / raw)
To: Maxim Levitsky; +Cc: linux-kernel, linux-input, jbarnes, akpm, dmitry.torokhov
On Sun, Aug 16, 2009 at 01:10:11AM +0300, Maxim Levitsky wrote:
>On Sat, 2009-08-15 at 22:10 +0200, David Härdeman wrote:
>> On Sat, Aug 15, 2009 at 06:02:27AM +0300, Maxim Levitsky wrote:
>>>
>>> Then why not to implement lirc driver?
>>
>> That's a fair question, but I'm afraid you're putting the cart before
>> the horse.
>
>No, I just want to write the driver that fully exposes the hardware. I
>don't care if it has to be outside or not.
That's not a very user-friendly sentiment. The entire idea is to extend
the input subsystem so that it fully exposes the hardware *and* gives
users the benefit of in-kernel drivers.
>> The question is not why anyone would want to write an in-kernel driver
>> but rather why anyone would want to write an out-of-kernel driver.
>>
>> There have been repeated attempts to get LIRC merged with the kernel,
>> and the feedback has been pretty consistent - make it part of the input
>> subsystem.
>>
>> I have written the driver for theo input system and it limits the driver
>> somewhat. I am working on extending the input system to accomodate IR
>> drivers (see the discussion of EV_IR on the linux-input list).
>
>The EV_IR thing is that he attempts to put all IR decoding in kernel,
>and on top of that create a configfs config system.
I never proposed a configfs system. I only proposed a part of Jon
Smirl's EV_IR functionality. I think the configfs system as well as the
in-kernel protocol _en_coders are overengineering.
>I first thought it would be nice, but then realized that this is really
>bad idea.
>Currently LIRC has very oiled system for decoding pretty much every
>remote that exist. It can cope with all kind of troubles, including not
>very accurate receivers.
I think you've misunderstood my EV_IR suggestion on the linux-input
list. Part of that proposal is to allow drivers to generate IR_RAW
timing events (if asked to do so via an ioctl), then you could continue
to use lirc with some minimal changes to the lirc daemon while still
getting the benefits of in-kernel drivers. I have a hard time seeing
what would be wrong with that? Whether the input subsystem *also*
includes (optional) IR decoding or not should not matter to lirc fans as
long as it includes some kind of IR_RAW support (which it does both in
Jon's proposal and in mine).
>On top of that there are pure userspace devices, like a IR diode
>connected to soundcard. It would be nice to do all the raw signal
>decoding in one place. Once signal is decoded, lirc forwards the input
>signal to the kernel via uinput, so it is a part of input system.
That's a red herring, there is always going to be esoteric DIY hardware,
and no matter which API is used in the kernel, that hardware can always
use user-space drivers.
>The way kernel hands in the raw IR data to lirc doesn't matter much. It
>is really just a queue of numbers. It can be forced into input system,
>but there is really no need for that.
There really is a need if you want in-kernel drivers.
>> Feel free to help me out in implementing that API, and porting LIRC
>> drivers, and all the benefits of in-kernel drivers will flow from that
>> work.
>
>This isn't a bad idea.
Good, we agree on this point, which is the most important one. IR_RAW
should be enough for the lirc daemon, right? So let's make sure
something like that gets added to the input subsystem and we can take it
from there...
>>>This driver as I understand is a driver for single remote shipped with
>>>the notebook.
>>
>> It's a driver for a winbond chipset shipped with many Intel desktop
>> "media" motherboards (DP35DP, DG33TL, DX388T, DX488T2, DP455G, DG45ID
>> and DG45FC are the ones I'm aware of).
>
>But it won't work with my JVC remote?....
Not sure what you JVC remote has to do with the Intel mainboards that
include the WPCD376I chipset.
Anyway, based on past experience of JVC remotes I'm guessing that your
remote uses some version of the NEC protocol, in that case it will be
supported...and IR_RAW (or something similar) will be supported if it is
accepted by the input subsystem maintainers so you will additionally be
able to use that air conditioning remote which implements a completely
wonky IR protocol together with lirc - if you want to...
Regards,
David Härdeman
(trimmed some people of the CC list which are unlikely to be interested
in this discussion, I hope Dmitry will speak up soon).
^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [patch 0/2] Winbond IR Driver - v2
@ 2009-08-16 18:43 ` David Härdeman
0 siblings, 0 replies; 27+ messages in thread
From: David Härdeman @ 2009-08-16 18:43 UTC (permalink / raw)
To: Maxim Levitsky; +Cc: linux-kernel, linux-input, jbarnes, akpm, dmitry.torokhov
On Sun, Aug 16, 2009 at 01:10:11AM +0300, Maxim Levitsky wrote:
>On Sat, 2009-08-15 at 22:10 +0200, David Härdeman wrote:
>> On Sat, Aug 15, 2009 at 06:02:27AM +0300, Maxim Levitsky wrote:
>>>
>>> Then why not to implement lirc driver?
>>
>> That's a fair question, but I'm afraid you're putting the cart before
>> the horse.
>
>No, I just want to write the driver that fully exposes the hardware. I
>don't care if it has to be outside or not.
That's not a very user-friendly sentiment. The entire idea is to extend
the input subsystem so that it fully exposes the hardware *and* gives
users the benefit of in-kernel drivers.
>> The question is not why anyone would want to write an in-kernel driver
>> but rather why anyone would want to write an out-of-kernel driver.
>>
>> There have been repeated attempts to get LIRC merged with the kernel,
>> and the feedback has been pretty consistent - make it part of the input
>> subsystem.
>>
>> I have written the driver for theo input system and it limits the driver
>> somewhat. I am working on extending the input system to accomodate IR
>> drivers (see the discussion of EV_IR on the linux-input list).
>
>The EV_IR thing is that he attempts to put all IR decoding in kernel,
>and on top of that create a configfs config system.
I never proposed a configfs system. I only proposed a part of Jon
Smirl's EV_IR functionality. I think the configfs system as well as the
in-kernel protocol _en_coders are overengineering.
>I first thought it would be nice, but then realized that this is really
>bad idea.
>Currently LIRC has very oiled system for decoding pretty much every
>remote that exist. It can cope with all kind of troubles, including not
>very accurate receivers.
I think you've misunderstood my EV_IR suggestion on the linux-input
list. Part of that proposal is to allow drivers to generate IR_RAW
timing events (if asked to do so via an ioctl), then you could continue
to use lirc with some minimal changes to the lirc daemon while still
getting the benefits of in-kernel drivers. I have a hard time seeing
what would be wrong with that? Whether the input subsystem *also*
includes (optional) IR decoding or not should not matter to lirc fans as
long as it includes some kind of IR_RAW support (which it does both in
Jon's proposal and in mine).
>On top of that there are pure userspace devices, like a IR diode
>connected to soundcard. It would be nice to do all the raw signal
>decoding in one place. Once signal is decoded, lirc forwards the input
>signal to the kernel via uinput, so it is a part of input system.
That's a red herring, there is always going to be esoteric DIY hardware,
and no matter which API is used in the kernel, that hardware can always
use user-space drivers.
>The way kernel hands in the raw IR data to lirc doesn't matter much. It
>is really just a queue of numbers. It can be forced into input system,
>but there is really no need for that.
There really is a need if you want in-kernel drivers.
>> Feel free to help me out in implementing that API, and porting LIRC
>> drivers, and all the benefits of in-kernel drivers will flow from that
>> work.
>
>This isn't a bad idea.
Good, we agree on this point, which is the most important one. IR_RAW
should be enough for the lirc daemon, right? So let's make sure
something like that gets added to the input subsystem and we can take it
from there...
>>>This driver as I understand is a driver for single remote shipped with
>>>the notebook.
>>
>> It's a driver for a winbond chipset shipped with many Intel desktop
>> "media" motherboards (DP35DP, DG33TL, DX388T, DX488T2, DP455G, DG45ID
>> and DG45FC are the ones I'm aware of).
>
>But it won't work with my JVC remote?....
Not sure what you JVC remote has to do with the Intel mainboards that
include the WPCD376I chipset.
Anyway, based on past experience of JVC remotes I'm guessing that your
remote uses some version of the NEC protocol, in that case it will be
supported...and IR_RAW (or something similar) will be supported if it is
accepted by the input subsystem maintainers so you will additionally be
able to use that air conditioning remote which implements a completely
wonky IR protocol together with lirc - if you want to...
Regards,
David Härdeman
(trimmed some people of the CC list which are unlikely to be interested
in this discussion, I hope Dmitry will speak up soon).
--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [patch 0/2] Winbond IR Driver - v2
2009-08-16 18:43 ` David Härdeman
@ 2009-08-17 3:56 ` Maxim Levitsky
-1 siblings, 0 replies; 27+ messages in thread
From: Maxim Levitsky @ 2009-08-17 3:56 UTC (permalink / raw)
To: David Härdeman
Cc: linux-kernel, linux-input, jbarnes, akpm, dmitry.torokhov
On Sun, 2009-08-16 at 20:43 +0200, David Härdeman wrote:
> On Sun, Aug 16, 2009 at 01:10:11AM +0300, Maxim Levitsky wrote:
> >On Sat, 2009-08-15 at 22:10 +0200, David Härdeman wrote:
> >> On Sat, Aug 15, 2009 at 06:02:27AM +0300, Maxim Levitsky wrote:
> >>>
> >>> Then why not to implement lirc driver?
> >>
> >> That's a fair question, but I'm afraid you're putting the cart before
> >> the horse.
> >
> >No, I just want to write the driver that fully exposes the hardware. I
> >don't care if it has to be outside or not.
>
> That's not a very user-friendly sentiment. The entire idea is to extend
> the input subsystem so that it fully exposes the hardware *and* gives
> users the benefit of in-kernel drivers.
>
> >> The question is not why anyone would want to write an in-kernel driver
> >> but rather why anyone would want to write an out-of-kernel driver.
It doesn't matter that much. I have written a driver. It works.
Eventually some IR subsystem will make into kernel, I then port my
driver to that system.
Currently, there is no such system. attempting to write a driver that
decode just one remote, in my opinion aren't that great.
IR_RAW of course, it nice, and will solve all problems, so go ahead with
that.
As long as there is a normal (not debug or so) access to raw data, its
fine with me.
I do think however that in kernel ir decoders are redundant, except
maybe some embedded systems.
> >>
> >> There have been repeated attempts to get LIRC merged with the kernel,
> >> and the feedback has been pretty consistent - make it part of the input
> >> subsystem.
> >>
> >> I have written the driver for theo input system and it limits the driver
> >> somewhat. I am working on extending the input system to accomodate IR
> >> drivers (see the discussion of EV_IR on the linux-input list).
> >
> >The EV_IR thing is that he attempts to put all IR decoding in kernel,
> >and on top of that create a configfs config system.
>
> I never proposed a configfs system. I only proposed a part of Jon
> Smirl's EV_IR functionality. I think the configfs system as well as the
> in-kernel protocol _en_coders are overengineering.
and decoders?
>
> >I first thought it would be nice, but then realized that this is really
> >bad idea.
> >Currently LIRC has very oiled system for decoding pretty much every
> >remote that exist. It can cope with all kind of troubles, including not
> >very accurate receivers.
>
> I think you've misunderstood my EV_IR suggestion on the linux-input
> list. Part of that proposal is to allow drivers to generate IR_RAW
> timing events (if asked to do so via an ioctl), then you could continue
> to use lirc with some minimal changes to the lirc daemon while still
> getting the benefits of in-kernel drivers. I have a hard time seeing
> what would be wrong with that? Whether the input subsystem *also*
> includes (optional) IR decoding or not should not matter to lirc fans as
> long as it includes some kind of IR_RAW support (which it does both in
> Jon's proposal and in mine).
Thats of course is different.
I am not yet a lirc fan :-), but I can assure you that there are many
protocols. much more that we think.
The way lirc works, ensures that it can adapt to any remote.
>
> >On top of that there are pure userspace devices, like a IR diode
> >connected to soundcard. It would be nice to do all the raw signal
> >decoding in one place. Once signal is decoded, lirc forwards the input
> >signal to the kernel via uinput, so it is a part of input system.
>
> That's a red herring, there is always going to be esoteric DIY hardware,
> and no matter which API is used in the kernel, that hardware can always
> use user-space drivers.
The point is that doing it all in userspace helps ensure that settings
are kept in one place.
I have a lirc.conf. It will work with any receiver, homebrew or not.
>
> >The way kernel hands in the raw IR data to lirc doesn't matter much. It
> >is really just a queue of numbers. It can be forced into input system,
> >but there is really no need for that.
I personally don't care. If you want to send IR raw data via input
layer, it is very fine with me. I am sure that lirc devs won't mind that
ether.
>
> There really is a need if you want in-kernel drivers.
>
> >> Feel free to help me out in implementing that API, and porting LIRC
> >> drivers, and all the benefits of in-kernel drivers will flow from that
> >> work.
> >
> >This isn't a bad idea.
>
> Good, we agree on this point, which is the most important one. IR_RAW
> should be enough for the lirc daemon, right? So let's make sure
> something like that gets added to the input subsystem and we can take it
> from there...
This indeed will be great.
>
> >>>This driver as I understand is a driver for single remote shipped with
> >>>the notebook.
> >>
> >> It's a driver for a winbond chipset shipped with many Intel desktop
> >> "media" motherboards (DP35DP, DG33TL, DX388T, DX488T2, DP455G, DG45ID
> >> and DG45FC are the ones I'm aware of).
> >
> >But it won't work with my JVC remote?....
>
> Not sure what you JVC remote has to do with the Intel mainboards that
> include the WPCD376I chipset.
Lets see. I have a remote from a VCR I no longer use, I now use it with
my computer....
It isn't a NEC protocol, it actually has its own protocol (JVC)
But all these protocols are very similar.
>
> Anyway, based on past experience of JVC remotes I'm guessing that your
> remote uses some version of the NEC protocol, in that case it will be
> supported...and IR_RAW (or something similar) will be supported if it is
> accepted by the input subsystem maintainers so you will additionally be
> able to use that air conditioning remote which implements a completely
> wonky IR protocol together with lirc - if you want to...
Indeed. Not with lirc, as it doesn't implement support for decoding such
remotes... but it is trivial to add such support, or write my own app
for that.
So I do agree with you mostly. I don't care how the raw timings will be
given to lirc.
Best regards,
Maxim Levitsky
>
> Regards,
> David Härdeman
>
> (trimmed some people of the CC list which are unlikely to be interested
> in this discussion, I hope Dmitry will speak up soon).
>
^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [patch 0/2] Winbond IR Driver - v2
@ 2009-08-17 3:56 ` Maxim Levitsky
0 siblings, 0 replies; 27+ messages in thread
From: Maxim Levitsky @ 2009-08-17 3:56 UTC (permalink / raw)
To: David Härdeman
Cc: linux-kernel, linux-input, jbarnes, akpm, dmitry.torokhov
On Sun, 2009-08-16 at 20:43 +0200, David Härdeman wrote:
> On Sun, Aug 16, 2009 at 01:10:11AM +0300, Maxim Levitsky wrote:
> >On Sat, 2009-08-15 at 22:10 +0200, David Härdeman wrote:
> >> On Sat, Aug 15, 2009 at 06:02:27AM +0300, Maxim Levitsky wrote:
> >>>
> >>> Then why not to implement lirc driver?
> >>
> >> That's a fair question, but I'm afraid you're putting the cart before
> >> the horse.
> >
> >No, I just want to write the driver that fully exposes the hardware. I
> >don't care if it has to be outside or not.
>
> That's not a very user-friendly sentiment. The entire idea is to extend
> the input subsystem so that it fully exposes the hardware *and* gives
> users the benefit of in-kernel drivers.
>
> >> The question is not why anyone would want to write an in-kernel driver
> >> but rather why anyone would want to write an out-of-kernel driver.
It doesn't matter that much. I have written a driver. It works.
Eventually some IR subsystem will make into kernel, I then port my
driver to that system.
Currently, there is no such system. attempting to write a driver that
decode just one remote, in my opinion aren't that great.
IR_RAW of course, it nice, and will solve all problems, so go ahead with
that.
As long as there is a normal (not debug or so) access to raw data, its
fine with me.
I do think however that in kernel ir decoders are redundant, except
maybe some embedded systems.
> >>
> >> There have been repeated attempts to get LIRC merged with the kernel,
> >> and the feedback has been pretty consistent - make it part of the input
> >> subsystem.
> >>
> >> I have written the driver for theo input system and it limits the driver
> >> somewhat. I am working on extending the input system to accomodate IR
> >> drivers (see the discussion of EV_IR on the linux-input list).
> >
> >The EV_IR thing is that he attempts to put all IR decoding in kernel,
> >and on top of that create a configfs config system.
>
> I never proposed a configfs system. I only proposed a part of Jon
> Smirl's EV_IR functionality. I think the configfs system as well as the
> in-kernel protocol _en_coders are overengineering.
and decoders?
>
> >I first thought it would be nice, but then realized that this is really
> >bad idea.
> >Currently LIRC has very oiled system for decoding pretty much every
> >remote that exist. It can cope with all kind of troubles, including not
> >very accurate receivers.
>
> I think you've misunderstood my EV_IR suggestion on the linux-input
> list. Part of that proposal is to allow drivers to generate IR_RAW
> timing events (if asked to do so via an ioctl), then you could continue
> to use lirc with some minimal changes to the lirc daemon while still
> getting the benefits of in-kernel drivers. I have a hard time seeing
> what would be wrong with that? Whether the input subsystem *also*
> includes (optional) IR decoding or not should not matter to lirc fans as
> long as it includes some kind of IR_RAW support (which it does both in
> Jon's proposal and in mine).
Thats of course is different.
I am not yet a lirc fan :-), but I can assure you that there are many
protocols. much more that we think.
The way lirc works, ensures that it can adapt to any remote.
>
> >On top of that there are pure userspace devices, like a IR diode
> >connected to soundcard. It would be nice to do all the raw signal
> >decoding in one place. Once signal is decoded, lirc forwards the input
> >signal to the kernel via uinput, so it is a part of input system.
>
> That's a red herring, there is always going to be esoteric DIY hardware,
> and no matter which API is used in the kernel, that hardware can always
> use user-space drivers.
The point is that doing it all in userspace helps ensure that settings
are kept in one place.
I have a lirc.conf. It will work with any receiver, homebrew or not.
>
> >The way kernel hands in the raw IR data to lirc doesn't matter much. It
> >is really just a queue of numbers. It can be forced into input system,
> >but there is really no need for that.
I personally don't care. If you want to send IR raw data via input
layer, it is very fine with me. I am sure that lirc devs won't mind that
ether.
>
> There really is a need if you want in-kernel drivers.
>
> >> Feel free to help me out in implementing that API, and porting LIRC
> >> drivers, and all the benefits of in-kernel drivers will flow from that
> >> work.
> >
> >This isn't a bad idea.
>
> Good, we agree on this point, which is the most important one. IR_RAW
> should be enough for the lirc daemon, right? So let's make sure
> something like that gets added to the input subsystem and we can take it
> from there...
This indeed will be great.
>
> >>>This driver as I understand is a driver for single remote shipped with
> >>>the notebook.
> >>
> >> It's a driver for a winbond chipset shipped with many Intel desktop
> >> "media" motherboards (DP35DP, DG33TL, DX388T, DX488T2, DP455G, DG45ID
> >> and DG45FC are the ones I'm aware of).
> >
> >But it won't work with my JVC remote?....
>
> Not sure what you JVC remote has to do with the Intel mainboards that
> include the WPCD376I chipset.
Lets see. I have a remote from a VCR I no longer use, I now use it with
my computer....
It isn't a NEC protocol, it actually has its own protocol (JVC)
But all these protocols are very similar.
>
> Anyway, based on past experience of JVC remotes I'm guessing that your
> remote uses some version of the NEC protocol, in that case it will be
> supported...and IR_RAW (or something similar) will be supported if it is
> accepted by the input subsystem maintainers so you will additionally be
> able to use that air conditioning remote which implements a completely
> wonky IR protocol together with lirc - if you want to...
Indeed. Not with lirc, as it doesn't implement support for decoding such
remotes... but it is trivial to add such support, or write my own app
for that.
So I do agree with you mostly. I don't care how the raw timings will be
given to lirc.
Best regards,
Maxim Levitsky
>
> Regards,
> David Härdeman
>
> (trimmed some people of the CC list which are unlikely to be interested
> in this discussion, I hope Dmitry will speak up soon).
>
--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply [flat|nested] 27+ messages in thread
* [patch 0/2] Winbond IR Driver - v2
@ 2009-08-11 16:26 ` David Härdeman
0 siblings, 0 replies; 27+ messages in thread
From: David Härdeman @ 2009-08-11 16:26 UTC (permalink / raw)
To: linux-kernel; +Cc: jbarnes, akpm, bjorn.helgaas, linux-input
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain, Size: 496 bytes --]
Here's a new patch set which should replace all the patches currently in the
-mm tree for the winbond cir driver. The new series incorporate feedback from
Bjorn Helgaas (convert the driver from ACPI to PNP) and Randy Dunlap (Kconfig
fixes).
This set fixes the comments Bjorn Helgaas had on the previous set (wrong Kconfig
depends).
The IR decoding can still be improved but the driver works for in daily use
decoding RC6 commands, so I believe it is ready to go upstream.
--
David Härdeman
^ permalink raw reply [flat|nested] 27+ messages in thread
* [patch 0/2] Winbond IR Driver - v2
@ 2009-08-11 16:26 ` David Härdeman
0 siblings, 0 replies; 27+ messages in thread
From: David Härdeman @ 2009-08-11 16:26 UTC (permalink / raw)
To: linux-kernel; +Cc: jbarnes, akpm, bjorn.helgaas, linux-input
Here's a new patch set which should replace all the patches currently in the
-mm tree for the winbond cir driver. The new series incorporate feedback from
Bjorn Helgaas (convert the driver from ACPI to PNP) and Randy Dunlap (Kconfig
fixes).
This set fixes the comments Bjorn Helgaas had on the previous set (wrong Kconfig
depends).
The IR decoding can still be improved but the driver works for in daily use
decoding RC6 commands, so I believe it is ready to go upstream.
--
David Härdeman
--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply [flat|nested] 27+ messages in thread
end of thread, other threads:[~2009-08-17 3:57 UTC | newest]
Thread overview: 27+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2009-08-09 9:56 [patch 0/2] Winbond IR Driver - v2 david
2009-08-09 9:56 ` david
2009-08-09 9:56 ` [patch 1/2] Add a shutdown method to pnp drivers david
2009-08-09 9:56 ` david
2009-08-09 9:56 ` [patch 2/2] Add a driver for the Winbond WPCD376I Consumer IR hardware david
2009-08-09 9:56 ` david
2009-08-10 15:39 ` Bjorn Helgaas
2009-08-13 6:58 ` Dmitry Torokhov
2009-08-13 9:34 ` David Härdeman
2009-08-13 9:34 ` David Härdeman
2009-08-13 15:56 ` Dmitry Torokhov
2009-08-13 15:56 ` Dmitry Torokhov
2009-08-13 17:58 ` David Härdeman
2009-08-13 17:58 ` David Härdeman
2009-08-13 17:14 ` David Härdeman
2009-08-13 17:14 ` David Härdeman
2009-08-15 3:02 ` [patch 0/2] Winbond IR Driver - v2 Maxim Levitsky
2009-08-15 20:10 ` David Härdeman
2009-08-15 20:10 ` David Härdeman
2009-08-15 22:10 ` Maxim Levitsky
2009-08-15 22:10 ` Maxim Levitsky
2009-08-16 18:43 ` David Härdeman
2009-08-16 18:43 ` David Härdeman
2009-08-17 3:56 ` Maxim Levitsky
2009-08-17 3:56 ` Maxim Levitsky
2009-08-11 16:26 David Härdeman
2009-08-11 16:26 ` David Härdeman
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.