summaryrefslogtreecommitdiffstats
path: root/drivers/input/misc
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/input/misc')
-rw-r--r--drivers/input/misc/Kconfig223
-rw-r--r--drivers/input/misc/Makefile23
-rw-r--r--drivers/input/misc/apanel.c377
-rw-r--r--drivers/input/misc/ati_remote.c863
-rw-r--r--drivers/input/misc/ati_remote2.c737
-rw-r--r--drivers/input/misc/atlas_btns.c182
-rw-r--r--drivers/input/misc/cm109.c917
-rw-r--r--drivers/input/misc/cobalt_btns.c177
-rw-r--r--drivers/input/misc/hp_sdc_rtc.c714
-rw-r--r--drivers/input/misc/ixp4xx-beeper.c186
-rw-r--r--drivers/input/misc/keyspan_remote.c607
-rw-r--r--drivers/input/misc/m68kspkr.c151
-rw-r--r--drivers/input/misc/pcspkr.c152
-rw-r--r--drivers/input/misc/powermate.c464
-rw-r--r--drivers/input/misc/sgi_btns.c179
-rw-r--r--drivers/input/misc/sparcspkr.c369
-rw-r--r--drivers/input/misc/uinput.c662
-rw-r--r--drivers/input/misc/wistron_btns.c1418
-rw-r--r--drivers/input/misc/yealink.c1019
-rw-r--r--drivers/input/misc/yealink.h220
20 files changed, 9640 insertions, 0 deletions
diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
new file mode 100644
index 0000000..199055d
--- /dev/null
+++ b/drivers/input/misc/Kconfig
@@ -0,0 +1,223 @@
+#
+# Input misc drivers configuration
+#
+menuconfig INPUT_MISC
+ bool "Miscellaneous devices"
+ help
+ Say Y here, and a list of miscellaneous input drivers will be displayed.
+ Everything that didn't fit into the other categories is here. This option
+ doesn't affect the kernel.
+
+ If unsure, say Y.
+
+if INPUT_MISC
+
+config INPUT_PCSPKR
+ tristate "PC Speaker support"
+ depends on PCSPKR_PLATFORM
+ help
+ Say Y here if you want the standard PC Speaker to be used for
+ bells and whistles.
+
+ If unsure, say Y.
+
+ To compile this driver as a module, choose M here: the
+ module will be called pcspkr.
+
+config INPUT_SPARCSPKR
+ tristate "SPARC Speaker support"
+ depends on PCI && SPARC64
+ help
+ Say Y here if you want the standard Speaker on Sparc PCI systems
+ to be used for bells and whistles.
+
+ If unsure, say Y.
+
+ To compile this driver as a module, choose M here: the
+ module will be called sparcspkr.
+
+config INPUT_M68K_BEEP
+ tristate "M68k Beeper support"
+ depends on M68K
+
+config INPUT_APANEL
+ tristate "Fujitsu Lifebook Application Panel buttons"
+ depends on X86 && I2C && LEDS_CLASS
+ select INPUT_POLLDEV
+ select CHECK_SIGNATURE
+ help
+ Say Y here for support of the Application Panel buttons, used on
+ Fujitsu Lifebook. These are attached to the mainboard through
+ an SMBus interface managed by the I2C Intel ICH (i801) driver,
+ which you should also build for this kernel.
+
+ To compile this driver as a module, choose M here: the module will
+ be called apanel.
+
+config INPUT_IXP4XX_BEEPER
+ tristate "IXP4XX Beeper support"
+ depends on ARCH_IXP4XX
+ help
+ If you say yes here, you can connect a beeper to the
+ ixp4xx gpio pins. This is used by the LinkSys NSLU2.
+
+ If unsure, say Y.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ixp4xx-beeper.
+
+config INPUT_COBALT_BTNS
+ tristate "Cobalt button interface"
+ depends on MIPS_COBALT
+ select INPUT_POLLDEV
+ help
+ Say Y here if you want to support MIPS Cobalt button interface.
+
+ To compile this driver as a module, choose M here: the
+ module will be called cobalt_btns.
+
+config INPUT_WISTRON_BTNS
+ tristate "x86 Wistron laptop button interface"
+ depends on X86 && !X86_64
+ select INPUT_POLLDEV
+ select NEW_LEDS
+ select LEDS_CLASS
+ select CHECK_SIGNATURE
+ help
+ Say Y here for support of Wistron laptop button interfaces, used on
+ laptops of various brands, including Acer and Fujitsu-Siemens. If
+ available, mail and wifi LEDs will be controllable via /sys/class/leds.
+
+ To compile this driver as a module, choose M here: the module will
+ be called wistron_btns.
+
+config INPUT_ATLAS_BTNS
+ tristate "x86 Atlas button interface"
+ depends on X86 && ACPI
+ help
+ Say Y here for support of Atlas wallmount touchscreen buttons.
+ The events will show up as scancodes F1 through F9 via evdev.
+
+ To compile this driver as a module, choose M here: the module will
+ be called atlas_btns.
+
+config INPUT_ATI_REMOTE
+ tristate "ATI / X10 USB RF remote control"
+ depends on USB_ARCH_HAS_HCD
+ select USB
+ help
+ Say Y here if you want to use an ATI or X10 "Lola" USB remote control.
+ These are RF remotes with USB receivers.
+ The ATI remote comes with many of ATI's All-In-Wonder video cards.
+ The X10 "Lola" remote is available at:
+ <http://www.x10.com/products/lola_sg1.htm>
+ This driver provides mouse pointer, left and right mouse buttons,
+ and maps all the other remote buttons to keypress events.
+
+ To compile this driver as a module, choose M here: the module will be
+ called ati_remote.
+
+config INPUT_ATI_REMOTE2
+ tristate "ATI / Philips USB RF remote control"
+ depends on USB_ARCH_HAS_HCD
+ select USB
+ help
+ Say Y here if you want to use an ATI or Philips USB RF remote control.
+ These are RF remotes with USB receivers.
+ ATI Remote Wonder II comes with some ATI's All-In-Wonder video cards
+ and is also available as a separate product.
+ This driver provides mouse pointer, left and right mouse buttons,
+ and maps all the other remote buttons to keypress events.
+
+ To compile this driver as a module, choose M here: the module will be
+ called ati_remote2.
+
+config INPUT_KEYSPAN_REMOTE
+ tristate "Keyspan DMR USB remote control (EXPERIMENTAL)"
+ depends on EXPERIMENTAL
+ depends on USB_ARCH_HAS_HCD
+ select USB
+ help
+ Say Y here if you want to use a Keyspan DMR USB remote control.
+ Currently only the UIA-11 type of receiver has been tested. The tag
+ on the receiver that connects to the USB port should have a P/N that
+ will tell you what type of DMR you have. The UIA-10 type is not
+ supported at this time. This driver maps all buttons to keypress
+ events.
+
+ To compile this driver as a module, choose M here: the module will
+ be called keyspan_remote.
+
+config INPUT_POWERMATE
+ tristate "Griffin PowerMate and Contour Jog support"
+ depends on USB_ARCH_HAS_HCD
+ select USB
+ help
+ Say Y here if you want to use Griffin PowerMate or Contour Jog devices.
+ These are aluminum dials which can measure clockwise and anticlockwise
+ rotation. The dial also acts as a pushbutton. The base contains an LED
+ which can be instructed to pulse or to switch to a particular intensity.
+
+ You can download userspace tools from
+ <http://sowerbutts.com/powermate/>.
+
+ To compile this driver as a module, choose M here: the
+ module will be called powermate.
+
+config INPUT_YEALINK
+ tristate "Yealink usb-p1k voip phone"
+ depends on EXPERIMENTAL
+ depends on USB_ARCH_HAS_HCD
+ select USB
+ help
+ Say Y here if you want to enable keyboard and LCD functions of the
+ Yealink usb-p1k usb phones. The audio part is enabled by the generic
+ usb sound driver, so you might want to enable that as well.
+
+ For information about how to use these additional functions, see
+ <file:Documentation/input/yealink.txt>.
+
+ To compile this driver as a module, choose M here: the module will be
+ called yealink.
+
+config INPUT_CM109
+ tristate "C-Media CM109 USB I/O Controller"
+ depends on EXPERIMENTAL
+ depends on USB_ARCH_HAS_HCD
+ select USB
+ help
+ Say Y here if you want to enable keyboard and buzzer functions of the
+ C-Media CM109 usb phones. The audio part is enabled by the generic
+ usb sound driver, so you might want to enable that as well.
+
+ To compile this driver as a module, choose M here: the module will be
+ called cm109.
+
+config INPUT_UINPUT
+ tristate "User level driver support"
+ help
+ Say Y here if you want to support user level drivers for input
+ subsystem accessible under char device 10:223 - /dev/input/uinput.
+
+ To compile this driver as a module, choose M here: the
+ module will be called uinput.
+
+config INPUT_SGI_BTNS
+ tristate "SGI Indy/O2 volume button interface"
+ depends on SGI_IP22 || SGI_IP32
+ select INPUT_POLLDEV
+ help
+ Say Y here if you want to support SGI Indy/O2 volume button interface.
+
+ To compile this driver as a module, choose M here: the
+ module will be called sgi_btns.
+
+config HP_SDC_RTC
+ tristate "HP SDC Real Time Clock"
+ depends on GSC || HP300
+ select HP_SDC
+ help
+ Say Y here if you want to support the built-in real time clock
+ of the HP SDC controller.
+
+endif
diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
new file mode 100644
index 0000000..d7db2ae
--- /dev/null
+++ b/drivers/input/misc/Makefile
@@ -0,0 +1,23 @@
+#
+# Makefile for the input misc drivers.
+#
+
+# Each configuration option enables a list of files.
+
+obj-$(CONFIG_INPUT_SPARCSPKR) += sparcspkr.o
+obj-$(CONFIG_INPUT_PCSPKR) += pcspkr.o
+obj-$(CONFIG_INPUT_M68K_BEEP) += m68kspkr.o
+obj-$(CONFIG_INPUT_IXP4XX_BEEPER) += ixp4xx-beeper.o
+obj-$(CONFIG_INPUT_COBALT_BTNS) += cobalt_btns.o
+obj-$(CONFIG_INPUT_WISTRON_BTNS) += wistron_btns.o
+obj-$(CONFIG_INPUT_ATLAS_BTNS) += atlas_btns.o
+obj-$(CONFIG_INPUT_ATI_REMOTE) += ati_remote.o
+obj-$(CONFIG_INPUT_ATI_REMOTE2) += ati_remote2.o
+obj-$(CONFIG_INPUT_KEYSPAN_REMOTE) += keyspan_remote.o
+obj-$(CONFIG_INPUT_POWERMATE) += powermate.o
+obj-$(CONFIG_INPUT_YEALINK) += yealink.o
+obj-$(CONFIG_INPUT_CM109) += cm109.o
+obj-$(CONFIG_HP_SDC_RTC) += hp_sdc_rtc.o
+obj-$(CONFIG_INPUT_UINPUT) += uinput.o
+obj-$(CONFIG_INPUT_APANEL) += apanel.o
+obj-$(CONFIG_INPUT_SGI_BTNS) += sgi_btns.o
diff --git a/drivers/input/misc/apanel.c b/drivers/input/misc/apanel.c
new file mode 100644
index 0000000..d82f7f7
--- /dev/null
+++ b/drivers/input/misc/apanel.c
@@ -0,0 +1,377 @@
+/*
+ * Fujitsu Lifebook Application Panel button drive
+ *
+ * Copyright (C) 2007 Stephen Hemminger <shemminger@linux-foundation.org>
+ * Copyright (C) 2001-2003 Jochen Eisinger <jochen@penguin-breeder.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * Many Fujitsu Lifebook laptops have a small panel of buttons that are
+ * accessible via the i2c/smbus interface. This driver polls those
+ * buttons and generates input events.
+ *
+ * For more details see:
+ * http://apanel.sourceforge.net/tech.php
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/ioport.h>
+#include <linux/io.h>
+#include <linux/input-polldev.h>
+#include <linux/i2c.h>
+#include <linux/workqueue.h>
+#include <linux/leds.h>
+
+#define APANEL_NAME "Fujitsu Application Panel"
+#define APANEL_VERSION "1.3.1"
+#define APANEL "apanel"
+
+/* How often we poll keys - msecs */
+#define POLL_INTERVAL_DEFAULT 1000
+
+/* Magic constants in BIOS that tell about buttons */
+enum apanel_devid {
+ APANEL_DEV_NONE = 0,
+ APANEL_DEV_APPBTN = 1,
+ APANEL_DEV_CDBTN = 2,
+ APANEL_DEV_LCD = 3,
+ APANEL_DEV_LED = 4,
+
+ APANEL_DEV_MAX,
+};
+
+enum apanel_chip {
+ CHIP_NONE = 0,
+ CHIP_OZ992C = 1,
+ CHIP_OZ163T = 2,
+ CHIP_OZ711M3 = 4,
+};
+
+/* Result of BIOS snooping/probing -- what features are supported */
+static enum apanel_chip device_chip[APANEL_DEV_MAX];
+
+#define MAX_PANEL_KEYS 12
+
+struct apanel {
+ struct input_polled_dev *ipdev;
+ struct i2c_client client;
+ unsigned short keymap[MAX_PANEL_KEYS];
+ u16 nkeys;
+ u16 led_bits;
+ struct work_struct led_work;
+ struct led_classdev mail_led;
+};
+
+
+static int apanel_probe(struct i2c_adapter *, int, int);
+
+/* for now, we only support one address */
+static unsigned short normal_i2c[] = {0, I2C_CLIENT_END};
+static unsigned short ignore = I2C_CLIENT_END;
+static struct i2c_client_address_data addr_data = {
+ .normal_i2c = normal_i2c,
+ .probe = &ignore,
+ .ignore = &ignore,
+};
+
+static void report_key(struct input_dev *input, unsigned keycode)
+{
+ pr_debug(APANEL ": report key %#x\n", keycode);
+ input_report_key(input, keycode, 1);
+ input_sync(input);
+
+ input_report_key(input, keycode, 0);
+ input_sync(input);
+}
+
+/* Poll for key changes
+ *
+ * Read Application keys via SMI
+ * A (0x4), B (0x8), Internet (0x2), Email (0x1).
+ *
+ * CD keys:
+ * Forward (0x100), Rewind (0x200), Stop (0x400), Pause (0x800)
+ */
+static void apanel_poll(struct input_polled_dev *ipdev)
+{
+ struct apanel *ap = ipdev->private;
+ struct input_dev *idev = ipdev->input;
+ u8 cmd = device_chip[APANEL_DEV_APPBTN] == CHIP_OZ992C ? 0 : 8;
+ s32 data;
+ int i;
+
+ data = i2c_smbus_read_word_data(&ap->client, cmd);
+ if (data < 0)
+ return; /* ignore errors (due to ACPI??) */
+
+ /* write back to clear latch */
+ i2c_smbus_write_word_data(&ap->client, cmd, 0);
+
+ if (!data)
+ return;
+
+ dev_dbg(&idev->dev, APANEL ": data %#x\n", data);
+ for (i = 0; i < idev->keycodemax; i++)
+ if ((1u << i) & data)
+ report_key(idev, ap->keymap[i]);
+}
+
+/* Track state changes of LED */
+static void led_update(struct work_struct *work)
+{
+ struct apanel *ap = container_of(work, struct apanel, led_work);
+
+ i2c_smbus_write_word_data(&ap->client, 0x10, ap->led_bits);
+}
+
+static void mail_led_set(struct led_classdev *led,
+ enum led_brightness value)
+{
+ struct apanel *ap = container_of(led, struct apanel, mail_led);
+
+ if (value != LED_OFF)
+ ap->led_bits |= 0x8000;
+ else
+ ap->led_bits &= ~0x8000;
+
+ schedule_work(&ap->led_work);
+}
+
+static int apanel_detach_client(struct i2c_client *client)
+{
+ struct apanel *ap = i2c_get_clientdata(client);
+
+ if (device_chip[APANEL_DEV_LED] != CHIP_NONE)
+ led_classdev_unregister(&ap->mail_led);
+
+ input_unregister_polled_device(ap->ipdev);
+ i2c_detach_client(&ap->client);
+ input_free_polled_device(ap->ipdev);
+
+ return 0;
+}
+
+/* Function is invoked for every i2c adapter. */
+static int apanel_attach_adapter(struct i2c_adapter *adap)
+{
+ dev_dbg(&adap->dev, APANEL ": attach adapter id=%d\n", adap->id);
+
+ /* Our device is connected only to i801 on laptop */
+ if (adap->id != I2C_HW_SMBUS_I801)
+ return -ENODEV;
+
+ return i2c_probe(adap, &addr_data, apanel_probe);
+}
+
+static void apanel_shutdown(struct i2c_client *client)
+{
+ apanel_detach_client(client);
+}
+
+static struct i2c_driver apanel_driver = {
+ .driver = {
+ .name = APANEL,
+ },
+ .attach_adapter = &apanel_attach_adapter,
+ .detach_client = &apanel_detach_client,
+ .shutdown = &apanel_shutdown,
+};
+
+static struct apanel apanel = {
+ .client = {
+ .driver = &apanel_driver,
+ .name = APANEL,
+ },
+ .keymap = {
+ [0] = KEY_MAIL,
+ [1] = KEY_WWW,
+ [2] = KEY_PROG2,
+ [3] = KEY_PROG1,
+
+ [8] = KEY_FORWARD,
+ [9] = KEY_REWIND,
+ [10] = KEY_STOPCD,
+ [11] = KEY_PLAYPAUSE,
+
+ },
+ .mail_led = {
+ .name = "mail:blue",
+ .brightness_set = mail_led_set,
+ },
+};
+
+/* NB: Only one panel on the i2c. */
+static int apanel_probe(struct i2c_adapter *bus, int address, int kind)
+{
+ struct apanel *ap;
+ struct input_polled_dev *ipdev;
+ struct input_dev *idev;
+ u8 cmd = device_chip[APANEL_DEV_APPBTN] == CHIP_OZ992C ? 0 : 8;
+ int i, err = -ENOMEM;
+
+ dev_dbg(&bus->dev, APANEL ": probe adapter %p addr %d kind %d\n",
+ bus, address, kind);
+
+ ap = &apanel;
+
+ ipdev = input_allocate_polled_device();
+ if (!ipdev)
+ goto out1;
+
+ ap->ipdev = ipdev;
+ ap->client.adapter = bus;
+ ap->client.addr = address;
+
+ i2c_set_clientdata(&ap->client, ap);
+
+ err = i2c_attach_client(&ap->client);
+ if (err)
+ goto out2;
+
+ err = i2c_smbus_write_word_data(&ap->client, cmd, 0);
+ if (err) {
+ dev_warn(&ap->client.dev, APANEL ": smbus write error %d\n",
+ err);
+ goto out3;
+ }
+
+ ipdev->poll = apanel_poll;
+ ipdev->poll_interval = POLL_INTERVAL_DEFAULT;
+ ipdev->private = ap;
+
+ idev = ipdev->input;
+ idev->name = APANEL_NAME " buttons";
+ idev->phys = "apanel/input0";
+ idev->id.bustype = BUS_HOST;
+ idev->dev.parent = &ap->client.dev;
+
+ set_bit(EV_KEY, idev->evbit);
+
+ idev->keycode = ap->keymap;
+ idev->keycodesize = sizeof(ap->keymap[0]);
+ idev->keycodemax = (device_chip[APANEL_DEV_CDBTN] != CHIP_NONE) ? 12 : 4;
+
+ for (i = 0; i < idev->keycodemax; i++)
+ if (ap->keymap[i])
+ set_bit(ap->keymap[i], idev->keybit);
+
+ err = input_register_polled_device(ipdev);
+ if (err)
+ goto out3;
+
+ INIT_WORK(&ap->led_work, led_update);
+ if (device_chip[APANEL_DEV_LED] != CHIP_NONE) {
+ err = led_classdev_register(&ap->client.dev, &ap->mail_led);
+ if (err)
+ goto out4;
+ }
+
+ return 0;
+out4:
+ input_unregister_polled_device(ipdev);
+out3:
+ i2c_detach_client(&ap->client);
+out2:
+ input_free_polled_device(ipdev);
+out1:
+ return err;
+}
+
+/* Scan the system ROM for the signature "FJKEYINF" */
+static __init const void __iomem *bios_signature(const void __iomem *bios)
+{
+ ssize_t offset;
+ const unsigned char signature[] = "FJKEYINF";
+
+ for (offset = 0; offset < 0x10000; offset += 0x10) {
+ if (check_signature(bios + offset, signature,
+ sizeof(signature)-1))
+ return bios + offset;
+ }
+ pr_notice(APANEL ": Fujitsu BIOS signature '%s' not found...\n",
+ signature);
+ return NULL;
+}
+
+static int __init apanel_init(void)
+{
+ void __iomem *bios;
+ const void __iomem *p;
+ u8 devno;
+ int found = 0;
+
+ bios = ioremap(0xF0000, 0x10000); /* Can't fail */
+
+ p = bios_signature(bios);
+ if (!p) {
+ iounmap(bios);
+ return -ENODEV;
+ }
+
+ /* just use the first address */
+ p += 8;
+ normal_i2c[0] = readb(p+3) >> 1;
+
+ for ( ; (devno = readb(p)) & 0x7f; p += 4) {
+ unsigned char method, slave, chip;
+
+ method = readb(p + 1);
+ chip = readb(p + 2);
+ slave = readb(p + 3) >> 1;
+
+ if (slave != normal_i2c[0]) {
+ pr_notice(APANEL ": only one SMBus slave "
+ "address supported, skiping device...\n");
+ continue;
+ }
+
+ /* translate alternative device numbers */
+ switch (devno) {
+ case 6:
+ devno = APANEL_DEV_APPBTN;
+ break;
+ case 7:
+ devno = APANEL_DEV_LED;
+ break;
+ }
+
+ if (devno >= APANEL_DEV_MAX)
+ pr_notice(APANEL ": unknown device %u found\n", devno);
+ else if (device_chip[devno] != CHIP_NONE)
+ pr_warning(APANEL ": duplicate entry for devno %u\n", devno);
+
+ else if (method != 1 && method != 2 && method != 4) {
+ pr_notice(APANEL ": unknown method %u for devno %u\n",
+ method, devno);
+ } else {
+ device_chip[devno] = (enum apanel_chip) chip;
+ ++found;
+ }
+ }
+ iounmap(bios);
+
+ if (found == 0) {
+ pr_info(APANEL ": no input devices reported by BIOS\n");
+ return -EIO;
+ }
+
+ return i2c_add_driver(&apanel_driver);
+}
+module_init(apanel_init);
+
+static void __exit apanel_cleanup(void)
+{
+ i2c_del_driver(&apanel_driver);
+}
+module_exit(apanel_cleanup);
+
+MODULE_AUTHOR("Stephen Hemminger <shemminger@linux-foundation.org>");
+MODULE_DESCRIPTION(APANEL_NAME " driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(APANEL_VERSION);
+
+MODULE_ALIAS("dmi:*:svnFUJITSU:pnLifeBook*:pvr*:rvnFUJITSU:*");
+MODULE_ALIAS("dmi:*:svnFUJITSU:pnLifebook*:pvr*:rvnFUJITSU:*");
diff --git a/drivers/input/misc/ati_remote.c b/drivers/input/misc/ati_remote.c
new file mode 100644
index 0000000..e290fde
--- /dev/null
+++ b/drivers/input/misc/ati_remote.c
@@ -0,0 +1,863 @@
+/*
+ * USB ATI Remote support
+ *
+ * Version 2.2.0 Copyright (c) 2004 Torrey Hoffman <thoffman@arnor.net>
+ * Version 2.1.1 Copyright (c) 2002 Vladimir Dergachev
+ *
+ * This 2.2.0 version is a rewrite / cleanup of the 2.1.1 driver, including
+ * porting to the 2.6 kernel interfaces, along with other modification
+ * to better match the style of the existing usb/input drivers. However, the
+ * protocol and hardware handling is essentially unchanged from 2.1.1.
+ *
+ * The 2.1.1 driver was derived from the usbati_remote and usbkbd drivers by
+ * Vojtech Pavlik.
+ *
+ * Changes:
+ *
+ * Feb 2004: Torrey Hoffman <thoffman@arnor.net>
+ * Version 2.2.0
+ * Jun 2004: Torrey Hoffman <thoffman@arnor.net>
+ * Version 2.2.1
+ * Added key repeat support contributed by:
+ * Vincent Vanackere <vanackere@lif.univ-mrs.fr>
+ * Added support for the "Lola" remote contributed by:
+ * Seth Cohn <sethcohn@yahoo.com>
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ *
+ * Hardware & software notes
+ *
+ * These remote controls are distributed by ATI as part of their
+ * "All-In-Wonder" video card packages. The receiver self-identifies as a
+ * "USB Receiver" with manufacturer "X10 Wireless Technology Inc".
+ *
+ * The "Lola" remote is available from X10. See:
+ * http://www.x10.com/products/lola_sg1.htm
+ * The Lola is similar to the ATI remote but has no mouse support, and slightly
+ * different keys.
+ *
+ * It is possible to use multiple receivers and remotes on multiple computers
+ * simultaneously by configuring them to use specific channels.
+ *
+ * The RF protocol used by the remote supports 16 distinct channels, 1 to 16.
+ * Actually, it may even support more, at least in some revisions of the
+ * hardware.
+ *
+ * Each remote can be configured to transmit on one channel as follows:
+ * - Press and hold the "hand icon" button.
+ * - When the red LED starts to blink, let go of the "hand icon" button.
+ * - When it stops blinking, input the channel code as two digits, from 01
+ * to 16, and press the hand icon again.
+ *
+ * The timing can be a little tricky. Try loading the module with debug=1
+ * to have the kernel print out messages about the remote control number
+ * and mask. Note: debugging prints remote numbers as zero-based hexadecimal.
+ *
+ * The driver has a "channel_mask" parameter. This bitmask specifies which
+ * channels will be ignored by the module. To mask out channels, just add
+ * all the 2^channel_number values together.
+ *
+ * For instance, set channel_mask = 2^4 = 16 (binary 10000) to make ati_remote
+ * ignore signals coming from remote controls transmitting on channel 4, but
+ * accept all other channels.
+ *
+ * Or, set channel_mask = 65533, (0xFFFD), and all channels except 1 will be
+ * ignored.
+ *
+ * The default is 0 (respond to all channels). Bit 0 and bits 17-32 of this
+ * parameter are unused.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/usb/input.h>
+#include <linux/wait.h>
+#include <linux/jiffies.h>
+
+/*
+ * Module and Version Information, Module Parameters
+ */
+
+#define ATI_REMOTE_VENDOR_ID 0x0bc7
+#define ATI_REMOTE_PRODUCT_ID 0x004
+#define LOLA_REMOTE_PRODUCT_ID 0x002
+#define MEDION_REMOTE_PRODUCT_ID 0x006
+
+#define DRIVER_VERSION "2.2.1"
+#define DRIVER_AUTHOR "Torrey Hoffman <thoffman@arnor.net>"
+#define DRIVER_DESC "ATI/X10 RF USB Remote Control"
+
+#define NAME_BUFSIZE 80 /* size of product name, path buffers */
+#define DATA_BUFSIZE 63 /* size of URB data buffers */
+
+/*
+ * Duplicate event filtering time.
+ * Sequential, identical KIND_FILTERED inputs with less than
+ * FILTER_TIME milliseconds between them are considered as repeat
+ * events. The hardware generates 5 events for the first keypress
+ * and we have to take this into account for an accurate repeat
+ * behaviour.
+ */
+#define FILTER_TIME 60 /* msec */
+#define REPEAT_DELAY 500 /* msec */
+
+static unsigned long channel_mask;
+module_param(channel_mask, ulong, 0644);
+MODULE_PARM_DESC(channel_mask, "Bitmask of remote control channels to ignore");
+
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Enable extra debug messages and information");
+
+static int repeat_filter = FILTER_TIME;
+module_param(repeat_filter, int, 0644);
+MODULE_PARM_DESC(repeat_filter, "Repeat filter time, default = 60 msec");
+
+static int repeat_delay = REPEAT_DELAY;
+module_param(repeat_delay, int, 0644);
+MODULE_PARM_DESC(repeat_delay, "Delay before sending repeats, default = 500 msec");
+
+#define dbginfo(dev, format, arg...) do { if (debug) dev_info(dev , format , ## arg); } while (0)
+#undef err
+#define err(format, arg...) printk(KERN_ERR format , ## arg)
+
+static struct usb_device_id ati_remote_table[] = {
+ { USB_DEVICE(ATI_REMOTE_VENDOR_ID, ATI_REMOTE_PRODUCT_ID) },
+ { USB_DEVICE(ATI_REMOTE_VENDOR_ID, LOLA_REMOTE_PRODUCT_ID) },
+ { USB_DEVICE(ATI_REMOTE_VENDOR_ID, MEDION_REMOTE_PRODUCT_ID) },
+ {} /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, ati_remote_table);
+
+/* Get hi and low bytes of a 16-bits int */
+#define HI(a) ((unsigned char)((a) >> 8))
+#define LO(a) ((unsigned char)((a) & 0xff))
+
+#define SEND_FLAG_IN_PROGRESS 1
+#define SEND_FLAG_COMPLETE 2
+
+/* Device initialization strings */
+static char init1[] = { 0x01, 0x00, 0x20, 0x14 };
+static char init2[] = { 0x01, 0x00, 0x20, 0x14, 0x20, 0x20, 0x20 };
+
+struct ati_remote {
+ struct input_dev *idev;
+ struct usb_device *udev;
+ struct usb_interface *interface;
+
+ struct urb *irq_urb;
+ struct urb *out_urb;
+ struct usb_endpoint_descriptor *endpoint_in;
+ struct usb_endpoint_descriptor *endpoint_out;
+ unsigned char *inbuf;
+ unsigned char *outbuf;
+ dma_addr_t inbuf_dma;
+ dma_addr_t outbuf_dma;
+
+ unsigned char old_data[2]; /* Detect duplicate events */
+ unsigned long old_jiffies;
+ unsigned long acc_jiffies; /* handle acceleration */
+ unsigned long first_jiffies;
+
+ unsigned int repeat_count;
+
+ char name[NAME_BUFSIZE];
+ char phys[NAME_BUFSIZE];
+
+ wait_queue_head_t wait;
+ int send_flags;
+};
+
+/* "Kinds" of messages sent from the hardware to the driver. */
+#define KIND_END 0
+#define KIND_LITERAL 1 /* Simply pass to input system */
+#define KIND_FILTERED 2 /* Add artificial key-up events, drop keyrepeats */
+#define KIND_LU 3 /* Directional keypad diagonals - left up, */
+#define KIND_RU 4 /* right up, */
+#define KIND_LD 5 /* left down, */
+#define KIND_RD 6 /* right down */
+#define KIND_ACCEL 7 /* Directional keypad - left, right, up, down.*/
+
+/* Translation table from hardware messages to input events. */
+static const struct {
+ short kind;
+ unsigned char data1, data2;
+ int type;
+ unsigned int code;
+ int value;
+} ati_remote_tbl[] = {
+ /* Directional control pad axes */
+ {KIND_ACCEL, 0x35, 0x70, EV_REL, REL_X, -1}, /* left */
+ {KIND_ACCEL, 0x36, 0x71, EV_REL, REL_X, 1}, /* right */
+ {KIND_ACCEL, 0x37, 0x72, EV_REL, REL_Y, -1}, /* up */
+ {KIND_ACCEL, 0x38, 0x73, EV_REL, REL_Y, 1}, /* down */
+ /* Directional control pad diagonals */
+ {KIND_LU, 0x39, 0x74, EV_REL, 0, 0}, /* left up */
+ {KIND_RU, 0x3a, 0x75, EV_REL, 0, 0}, /* right up */
+ {KIND_LD, 0x3c, 0x77, EV_REL, 0, 0}, /* left down */
+ {KIND_RD, 0x3b, 0x76, EV_REL, 0, 0}, /* right down */
+
+ /* "Mouse button" buttons */
+ {KIND_LITERAL, 0x3d, 0x78, EV_KEY, BTN_LEFT, 1}, /* left btn down */
+ {KIND_LITERAL, 0x3e, 0x79, EV_KEY, BTN_LEFT, 0}, /* left btn up */
+ {KIND_LITERAL, 0x41, 0x7c, EV_KEY, BTN_RIGHT, 1},/* right btn down */
+ {KIND_LITERAL, 0x42, 0x7d, EV_KEY, BTN_RIGHT, 0},/* right btn up */
+
+ /* Artificial "doubleclick" events are generated by the hardware.
+ * They are mapped to the "side" and "extra" mouse buttons here. */
+ {KIND_FILTERED, 0x3f, 0x7a, EV_KEY, BTN_SIDE, 1}, /* left dblclick */
+ {KIND_FILTERED, 0x43, 0x7e, EV_KEY, BTN_EXTRA, 1},/* right dblclick */
+
+ /* keyboard. */
+ {KIND_FILTERED, 0xd2, 0x0d, EV_KEY, KEY_1, 1},
+ {KIND_FILTERED, 0xd3, 0x0e, EV_KEY, KEY_2, 1},
+ {KIND_FILTERED, 0xd4, 0x0f, EV_KEY, KEY_3, 1},
+ {KIND_FILTERED, 0xd5, 0x10, EV_KEY, KEY_4, 1},
+ {KIND_FILTERED, 0xd6, 0x11, EV_KEY, KEY_5, 1},
+ {KIND_FILTERED, 0xd7, 0x12, EV_KEY, KEY_6, 1},
+ {KIND_FILTERED, 0xd8, 0x13, EV_KEY, KEY_7, 1},
+ {KIND_FILTERED, 0xd9, 0x14, EV_KEY, KEY_8, 1},
+ {KIND_FILTERED, 0xda, 0x15, EV_KEY, KEY_9, 1},
+ {KIND_FILTERED, 0xdc, 0x17, EV_KEY, KEY_0, 1},
+ {KIND_FILTERED, 0xc5, 0x00, EV_KEY, KEY_A, 1},
+ {KIND_FILTERED, 0xc6, 0x01, EV_KEY, KEY_B, 1},
+ {KIND_FILTERED, 0xde, 0x19, EV_KEY, KEY_C, 1},
+ {KIND_FILTERED, 0xe0, 0x1b, EV_KEY, KEY_D, 1},
+ {KIND_FILTERED, 0xe6, 0x21, EV_KEY, KEY_E, 1},
+ {KIND_FILTERED, 0xe8, 0x23, EV_KEY, KEY_F, 1},
+
+ /* "special" keys */
+ {KIND_FILTERED, 0xdd, 0x18, EV_KEY, KEY_KPENTER, 1}, /* "check" */
+ {KIND_FILTERED, 0xdb, 0x16, EV_KEY, KEY_MENU, 1}, /* "menu" */
+ {KIND_FILTERED, 0xc7, 0x02, EV_KEY, KEY_POWER, 1}, /* Power */
+ {KIND_FILTERED, 0xc8, 0x03, EV_KEY, KEY_TV, 1}, /* TV */
+ {KIND_FILTERED, 0xc9, 0x04, EV_KEY, KEY_DVD, 1}, /* DVD */
+ {KIND_FILTERED, 0xca, 0x05, EV_KEY, KEY_WWW, 1}, /* WEB */
+ {KIND_FILTERED, 0xcb, 0x06, EV_KEY, KEY_BOOKMARKS, 1}, /* "book" */
+ {KIND_FILTERED, 0xcc, 0x07, EV_KEY, KEY_EDIT, 1}, /* "hand" */
+ {KIND_FILTERED, 0xe1, 0x1c, EV_KEY, KEY_COFFEE, 1}, /* "timer" */
+ {KIND_FILTERED, 0xe5, 0x20, EV_KEY, KEY_FRONT, 1}, /* "max" */
+ {KIND_FILTERED, 0xe2, 0x1d, EV_KEY, KEY_LEFT, 1}, /* left */
+ {KIND_FILTERED, 0xe4, 0x1f, EV_KEY, KEY_RIGHT, 1}, /* right */
+ {KIND_FILTERED, 0xe7, 0x22, EV_KEY, KEY_DOWN, 1}, /* down */
+ {KIND_FILTERED, 0xdf, 0x1a, EV_KEY, KEY_UP, 1}, /* up */
+ {KIND_FILTERED, 0xe3, 0x1e, EV_KEY, KEY_OK, 1}, /* "OK" */
+ {KIND_FILTERED, 0xce, 0x09, EV_KEY, KEY_VOLUMEDOWN, 1}, /* VOL + */
+ {KIND_FILTERED, 0xcd, 0x08, EV_KEY, KEY_VOLUMEUP, 1}, /* VOL - */
+ {KIND_FILTERED, 0xcf, 0x0a, EV_KEY, KEY_MUTE, 1}, /* MUTE */
+ {KIND_FILTERED, 0xd0, 0x0b, EV_KEY, KEY_CHANNELUP, 1}, /* CH + */
+ {KIND_FILTERED, 0xd1, 0x0c, EV_KEY, KEY_CHANNELDOWN, 1},/* CH - */
+ {KIND_FILTERED, 0xec, 0x27, EV_KEY, KEY_RECORD, 1}, /* ( o) red */
+ {KIND_FILTERED, 0xea, 0x25, EV_KEY, KEY_PLAY, 1}, /* ( >) */
+ {KIND_FILTERED, 0xe9, 0x24, EV_KEY, KEY_REWIND, 1}, /* (<<) */
+ {KIND_FILTERED, 0xeb, 0x26, EV_KEY, KEY_FORWARD, 1}, /* (>>) */
+ {KIND_FILTERED, 0xed, 0x28, EV_KEY, KEY_STOP, 1}, /* ([]) */
+ {KIND_FILTERED, 0xee, 0x29, EV_KEY, KEY_PAUSE, 1}, /* ('') */
+ {KIND_FILTERED, 0xf0, 0x2b, EV_KEY, KEY_PREVIOUS, 1}, /* (<-) */
+ {KIND_FILTERED, 0xef, 0x2a, EV_KEY, KEY_NEXT, 1}, /* (>+) */
+ {KIND_FILTERED, 0xf2, 0x2D, EV_KEY, KEY_INFO, 1}, /* PLAYING */
+ {KIND_FILTERED, 0xf3, 0x2E, EV_KEY, KEY_HOME, 1}, /* TOP */
+ {KIND_FILTERED, 0xf4, 0x2F, EV_KEY, KEY_END, 1}, /* END */
+ {KIND_FILTERED, 0xf5, 0x30, EV_KEY, KEY_SELECT, 1}, /* SELECT */
+
+ {KIND_END, 0x00, 0x00, EV_MAX + 1, 0, 0}
+};
+
+/* Local function prototypes */
+static int ati_remote_open (struct input_dev *inputdev);
+static void ati_remote_close (struct input_dev *inputdev);
+static int ati_remote_sendpacket (struct ati_remote *ati_remote, u16 cmd, unsigned char *data);
+static void ati_remote_irq_out (struct urb *urb);
+static void ati_remote_irq_in (struct urb *urb);
+static void ati_remote_input_report (struct urb *urb);
+static int ati_remote_initialize (struct ati_remote *ati_remote);
+static int ati_remote_probe (struct usb_interface *interface, const struct usb_device_id *id);
+static void ati_remote_disconnect (struct usb_interface *interface);
+
+/* usb specific object to register with the usb subsystem */
+static struct usb_driver ati_remote_driver = {
+ .name = "ati_remote",
+ .probe = ati_remote_probe,
+ .disconnect = ati_remote_disconnect,
+ .id_table = ati_remote_table,
+};
+
+/*
+ * ati_remote_dump_input
+ */
+static void ati_remote_dump(struct device *dev, unsigned char *data,
+ unsigned int len)
+{
+ if ((len == 1) && (data[0] != (unsigned char)0xff) && (data[0] != 0x00))
+ dev_warn(dev, "Weird byte 0x%02x\n", data[0]);
+ else if (len == 4)
+ dev_warn(dev, "Weird key %02x %02x %02x %02x\n",
+ data[0], data[1], data[2], data[3]);
+ else
+ dev_warn(dev, "Weird data, len=%d %02x %02x %02x %02x %02x %02x ...\n",
+ len, data[0], data[1], data[2], data[3], data[4], data[5]);
+}
+
+/*
+ * ati_remote_open
+ */
+static int ati_remote_open(struct input_dev *inputdev)
+{
+ struct ati_remote *ati_remote = input_get_drvdata(inputdev);
+
+ /* On first open, submit the read urb which was set up previously. */
+ ati_remote->irq_urb->dev = ati_remote->udev;
+ if (usb_submit_urb(ati_remote->irq_urb, GFP_KERNEL)) {
+ dev_err(&ati_remote->interface->dev,
+ "%s: usb_submit_urb failed!\n", __func__);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+/*
+ * ati_remote_close
+ */
+static void ati_remote_close(struct input_dev *inputdev)
+{
+ struct ati_remote *ati_remote = input_get_drvdata(inputdev);
+
+ usb_kill_urb(ati_remote->irq_urb);
+}
+
+/*
+ * ati_remote_irq_out
+ */
+static void ati_remote_irq_out(struct urb *urb)
+{
+ struct ati_remote *ati_remote = urb->context;
+
+ if (urb->status) {
+ dev_dbg(&ati_remote->interface->dev, "%s: status %d\n",
+ __func__, urb->status);
+ return;
+ }
+
+ ati_remote->send_flags |= SEND_FLAG_COMPLETE;
+ wmb();
+ wake_up(&ati_remote->wait);
+}
+
+/*
+ * ati_remote_sendpacket
+ *
+ * Used to send device initialization strings
+ */
+static int ati_remote_sendpacket(struct ati_remote *ati_remote, u16 cmd, unsigned char *data)
+{
+ int retval = 0;
+
+ /* Set up out_urb */
+ memcpy(ati_remote->out_urb->transfer_buffer + 1, data, LO(cmd));
+ ((char *) ati_remote->out_urb->transfer_buffer)[0] = HI(cmd);
+
+ ati_remote->out_urb->transfer_buffer_length = LO(cmd) + 1;
+ ati_remote->out_urb->dev = ati_remote->udev;
+ ati_remote->send_flags = SEND_FLAG_IN_PROGRESS;
+
+ retval = usb_submit_urb(ati_remote->out_urb, GFP_ATOMIC);
+ if (retval) {
+ dev_dbg(&ati_remote->interface->dev,
+ "sendpacket: usb_submit_urb failed: %d\n", retval);
+ return retval;
+ }
+
+ wait_event_timeout(ati_remote->wait,
+ ((ati_remote->out_urb->status != -EINPROGRESS) ||
+ (ati_remote->send_flags & SEND_FLAG_COMPLETE)),
+ HZ);
+ usb_kill_urb(ati_remote->out_urb);
+
+ return retval;
+}
+
+/*
+ * ati_remote_event_lookup
+ */
+static int ati_remote_event_lookup(int rem, unsigned char d1, unsigned char d2)
+{
+ int i;
+
+ for (i = 0; ati_remote_tbl[i].kind != KIND_END; i++) {
+ /*
+ * Decide if the table entry matches the remote input.
+ */
+ if ((((ati_remote_tbl[i].data1 & 0x0f) == (d1 & 0x0f))) &&
+ ((((ati_remote_tbl[i].data1 >> 4) -
+ (d1 >> 4) + rem) & 0x0f) == 0x0f) &&
+ (ati_remote_tbl[i].data2 == d2))
+ return i;
+
+ }
+ return -1;
+}
+
+/*
+ * ati_remote_compute_accel
+ *
+ * Implements acceleration curve for directional control pad
+ * If elapsed time since last event is > 1/4 second, user "stopped",
+ * so reset acceleration. Otherwise, user is probably holding the control
+ * pad down, so we increase acceleration, ramping up over two seconds to
+ * a maximum speed.
+ */
+static int ati_remote_compute_accel(struct ati_remote *ati_remote)
+{
+ static const char accel[] = { 1, 2, 4, 6, 9, 13, 20 };
+ unsigned long now = jiffies;
+ int acc;
+
+ if (time_after(now, ati_remote->old_jiffies + msecs_to_jiffies(250))) {
+ acc = 1;
+ ati_remote->acc_jiffies = now;
+ }
+ else if (time_before(now, ati_remote->acc_jiffies + msecs_to_jiffies(125)))
+ acc = accel[0];
+ else if (time_before(now, ati_remote->acc_jiffies + msecs_to_jiffies(250)))
+ acc = accel[1];
+ else if (time_before(now, ati_remote->acc_jiffies + msecs_to_jiffies(500)))
+ acc = accel[2];
+ else if (time_before(now, ati_remote->acc_jiffies + msecs_to_jiffies(1000)))
+ acc = accel[3];
+ else if (time_before(now, ati_remote->acc_jiffies + msecs_to_jiffies(1500)))
+ acc = accel[4];
+ else if (time_before(now, ati_remote->acc_jiffies + msecs_to_jiffies(2000)))
+ acc = accel[5];
+ else
+ acc = accel[6];
+
+ return acc;
+}
+
+/*
+ * ati_remote_report_input
+ */
+static void ati_remote_input_report(struct urb *urb)
+{
+ struct ati_remote *ati_remote = urb->context;
+ unsigned char *data= ati_remote->inbuf;
+ struct input_dev *dev = ati_remote->idev;
+ int index, acc;
+ int remote_num;
+
+ /* Deal with strange looking inputs */
+ if ( (urb->actual_length != 4) || (data[0] != 0x14) ||
+ ((data[3] & 0x0f) != 0x00) ) {
+ ati_remote_dump(&urb->dev->dev, data, urb->actual_length);
+ return;
+ }
+
+ /* Mask unwanted remote channels. */
+ /* note: remote_num is 0-based, channel 1 on remote == 0 here */
+ remote_num = (data[3] >> 4) & 0x0f;
+ if (channel_mask & (1 << (remote_num + 1))) {
+ dbginfo(&ati_remote->interface->dev,
+ "Masked input from channel 0x%02x: data %02x,%02x, mask= 0x%02lx\n",
+ remote_num, data[1], data[2], channel_mask);
+ return;
+ }
+
+ /* Look up event code index in translation table */
+ index = ati_remote_event_lookup(remote_num, data[1], data[2]);
+ if (index < 0) {
+ dev_warn(&ati_remote->interface->dev,
+ "Unknown input from channel 0x%02x: data %02x,%02x\n",
+ remote_num, data[1], data[2]);
+ return;
+ }
+ dbginfo(&ati_remote->interface->dev,
+ "channel 0x%02x; data %02x,%02x; index %d; keycode %d\n",
+ remote_num, data[1], data[2], index, ati_remote_tbl[index].code);
+
+ if (ati_remote_tbl[index].kind == KIND_LITERAL) {
+ input_event(dev, ati_remote_tbl[index].type,
+ ati_remote_tbl[index].code,
+ ati_remote_tbl[index].value);
+ input_sync(dev);
+
+ ati_remote->old_jiffies = jiffies;
+ return;
+ }
+
+ if (ati_remote_tbl[index].kind == KIND_FILTERED) {
+ unsigned long now = jiffies;
+
+ /* Filter duplicate events which happen "too close" together. */
+ if (ati_remote->old_data[0] == data[1] &&
+ ati_remote->old_data[1] == data[2] &&
+ time_before(now, ati_remote->old_jiffies +
+ msecs_to_jiffies(repeat_filter))) {
+ ati_remote->repeat_count++;
+ } else {
+ ati_remote->repeat_count = 0;
+ ati_remote->first_jiffies = now;
+ }
+
+ ati_remote->old_data[0] = data[1];
+ ati_remote->old_data[1] = data[2];
+ ati_remote->old_jiffies = now;
+
+ /* Ensure we skip at least the 4 first duplicate events (generated
+ * by a single keypress), and continue skipping until repeat_delay
+ * msecs have passed
+ */
+ if (ati_remote->repeat_count > 0 &&
+ (ati_remote->repeat_count < 5 ||
+ time_before(now, ati_remote->first_jiffies +
+ msecs_to_jiffies(repeat_delay))))
+ return;
+
+
+ input_event(dev, ati_remote_tbl[index].type,
+ ati_remote_tbl[index].code, 1);
+ input_sync(dev);
+ input_event(dev, ati_remote_tbl[index].type,
+ ati_remote_tbl[index].code, 0);
+ input_sync(dev);
+
+ } else {
+
+ /*
+ * Other event kinds are from the directional control pad, and have an
+ * acceleration factor applied to them. Without this acceleration, the
+ * control pad is mostly unusable.
+ */
+ acc = ati_remote_compute_accel(ati_remote);
+
+ switch (ati_remote_tbl[index].kind) {
+ case KIND_ACCEL:
+ input_event(dev, ati_remote_tbl[index].type,
+ ati_remote_tbl[index].code,
+ ati_remote_tbl[index].value * acc);
+ break;
+ case KIND_LU:
+ input_report_rel(dev, REL_X, -acc);
+ input_report_rel(dev, REL_Y, -acc);
+ break;
+ case KIND_RU:
+ input_report_rel(dev, REL_X, acc);
+ input_report_rel(dev, REL_Y, -acc);
+ break;
+ case KIND_LD:
+ input_report_rel(dev, REL_X, -acc);
+ input_report_rel(dev, REL_Y, acc);
+ break;
+ case KIND_RD:
+ input_report_rel(dev, REL_X, acc);
+ input_report_rel(dev, REL_Y, acc);
+ break;
+ default:
+ dev_dbg(&ati_remote->interface->dev, "ati_remote kind=%d\n",
+ ati_remote_tbl[index].kind);
+ }
+ input_sync(dev);
+
+ ati_remote->old_jiffies = jiffies;
+ ati_remote->old_data[0] = data[1];
+ ati_remote->old_data[1] = data[2];
+ }
+}
+
+/*
+ * ati_remote_irq_in
+ */
+static void ati_remote_irq_in(struct urb *urb)
+{
+ struct ati_remote *ati_remote = urb->context;
+ int retval;
+
+ switch (urb->status) {
+ case 0: /* success */
+ ati_remote_input_report(urb);
+ break;
+ case -ECONNRESET: /* unlink */
+ case -ENOENT:
+ case -ESHUTDOWN:
+ dev_dbg(&ati_remote->interface->dev, "%s: urb error status, unlink? \n",
+ __func__);
+ return;
+ default: /* error */
+ dev_dbg(&ati_remote->interface->dev, "%s: Nonzero urb status %d\n",
+ __func__, urb->status);
+ }
+
+ retval = usb_submit_urb(urb, GFP_ATOMIC);
+ if (retval)
+ dev_err(&ati_remote->interface->dev, "%s: usb_submit_urb()=%d\n",
+ __func__, retval);
+}
+
+/*
+ * ati_remote_alloc_buffers
+ */
+static int ati_remote_alloc_buffers(struct usb_device *udev,
+ struct ati_remote *ati_remote)
+{
+ ati_remote->inbuf = usb_buffer_alloc(udev, DATA_BUFSIZE, GFP_ATOMIC,
+ &ati_remote->inbuf_dma);
+ if (!ati_remote->inbuf)
+ return -1;
+
+ ati_remote->outbuf = usb_buffer_alloc(udev, DATA_BUFSIZE, GFP_ATOMIC,
+ &ati_remote->outbuf_dma);
+ if (!ati_remote->outbuf)
+ return -1;
+
+ ati_remote->irq_urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!ati_remote->irq_urb)
+ return -1;
+
+ ati_remote->out_urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!ati_remote->out_urb)
+ return -1;
+
+ return 0;
+}
+
+/*
+ * ati_remote_free_buffers
+ */
+static void ati_remote_free_buffers(struct ati_remote *ati_remote)
+{
+ usb_free_urb(ati_remote->irq_urb);
+ usb_free_urb(ati_remote->out_urb);
+
+ usb_buffer_free(ati_remote->udev, DATA_BUFSIZE,
+ ati_remote->inbuf, ati_remote->inbuf_dma);
+
+ usb_buffer_free(ati_remote->udev, DATA_BUFSIZE,
+ ati_remote->outbuf, ati_remote->outbuf_dma);
+}
+
+static void ati_remote_input_init(struct ati_remote *ati_remote)
+{
+ struct input_dev *idev = ati_remote->idev;
+ int i;
+
+ idev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL);
+ idev->keybit[BIT_WORD(BTN_MOUSE)] = BIT_MASK(BTN_LEFT) |
+ BIT_MASK(BTN_RIGHT) | BIT_MASK(BTN_SIDE) | BIT_MASK(BTN_EXTRA);
+ idev->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(REL_Y);
+ for (i = 0; ati_remote_tbl[i].kind != KIND_END; i++)
+ if (ati_remote_tbl[i].type == EV_KEY)
+ set_bit(ati_remote_tbl[i].code, idev->keybit);
+
+ input_set_drvdata(idev, ati_remote);
+
+ idev->open = ati_remote_open;
+ idev->close = ati_remote_close;
+
+ idev->name = ati_remote->name;
+ idev->phys = ati_remote->phys;
+
+ usb_to_input_id(ati_remote->udev, &idev->id);
+ idev->dev.parent = &ati_remote->udev->dev;
+}
+
+static int ati_remote_initialize(struct ati_remote *ati_remote)
+{
+ struct usb_device *udev = ati_remote->udev;
+ int pipe, maxp;
+
+ init_waitqueue_head(&ati_remote->wait);
+
+ /* Set up irq_urb */
+ pipe = usb_rcvintpipe(udev, ati_remote->endpoint_in->bEndpointAddress);
+ maxp = usb_maxpacket(udev, pipe, usb_pipeout(pipe));
+ maxp = (maxp > DATA_BUFSIZE) ? DATA_BUFSIZE : maxp;
+
+ usb_fill_int_urb(ati_remote->irq_urb, udev, pipe, ati_remote->inbuf,
+ maxp, ati_remote_irq_in, ati_remote,
+ ati_remote->endpoint_in->bInterval);
+ ati_remote->irq_urb->transfer_dma = ati_remote->inbuf_dma;
+ ati_remote->irq_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+ /* Set up out_urb */
+ pipe = usb_sndintpipe(udev, ati_remote->endpoint_out->bEndpointAddress);
+ maxp = usb_maxpacket(udev, pipe, usb_pipeout(pipe));
+ maxp = (maxp > DATA_BUFSIZE) ? DATA_BUFSIZE : maxp;
+
+ usb_fill_int_urb(ati_remote->out_urb, udev, pipe, ati_remote->outbuf,
+ maxp, ati_remote_irq_out, ati_remote,
+ ati_remote->endpoint_out->bInterval);
+ ati_remote->out_urb->transfer_dma = ati_remote->outbuf_dma;
+ ati_remote->out_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+ /* send initialization strings */
+ if ((ati_remote_sendpacket(ati_remote, 0x8004, init1)) ||
+ (ati_remote_sendpacket(ati_remote, 0x8007, init2))) {
+ dev_err(&ati_remote->interface->dev,
+ "Initializing ati_remote hardware failed.\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+/*
+ * ati_remote_probe
+ */
+static int ati_remote_probe(struct usb_interface *interface, const struct usb_device_id *id)
+{
+ struct usb_device *udev = interface_to_usbdev(interface);
+ struct usb_host_interface *iface_host = interface->cur_altsetting;
+ struct usb_endpoint_descriptor *endpoint_in, *endpoint_out;
+ struct ati_remote *ati_remote;
+ struct input_dev *input_dev;
+ int err = -ENOMEM;
+
+ if (iface_host->desc.bNumEndpoints != 2) {
+ err("%s: Unexpected desc.bNumEndpoints\n", __func__);
+ return -ENODEV;
+ }
+
+ endpoint_in = &iface_host->endpoint[0].desc;
+ endpoint_out = &iface_host->endpoint[1].desc;
+
+ if (!usb_endpoint_is_int_in(endpoint_in)) {
+ err("%s: Unexpected endpoint_in\n", __func__);
+ return -ENODEV;
+ }
+ if (le16_to_cpu(endpoint_in->wMaxPacketSize) == 0) {
+ err("%s: endpoint_in message size==0? \n", __func__);
+ return -ENODEV;
+ }
+
+ ati_remote = kzalloc(sizeof (struct ati_remote), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!ati_remote || !input_dev)
+ goto fail1;
+
+ /* Allocate URB buffers, URBs */
+ if (ati_remote_alloc_buffers(udev, ati_remote))
+ goto fail2;
+
+ ati_remote->endpoint_in = endpoint_in;
+ ati_remote->endpoint_out = endpoint_out;
+ ati_remote->udev = udev;
+ ati_remote->idev = input_dev;
+ ati_remote->interface = interface;
+
+ usb_make_path(udev, ati_remote->phys, sizeof(ati_remote->phys));
+ strlcpy(ati_remote->phys, "/input0", sizeof(ati_remote->phys));
+
+ if (udev->manufacturer)
+ strlcpy(ati_remote->name, udev->manufacturer, sizeof(ati_remote->name));
+
+ if (udev->product)
+ snprintf(ati_remote->name, sizeof(ati_remote->name),
+ "%s %s", ati_remote->name, udev->product);
+
+ if (!strlen(ati_remote->name))
+ snprintf(ati_remote->name, sizeof(ati_remote->name),
+ DRIVER_DESC "(%04x,%04x)",
+ le16_to_cpu(ati_remote->udev->descriptor.idVendor),
+ le16_to_cpu(ati_remote->udev->descriptor.idProduct));
+
+ ati_remote_input_init(ati_remote);
+
+ /* Device Hardware Initialization - fills in ati_remote->idev from udev. */
+ err = ati_remote_initialize(ati_remote);
+ if (err)
+ goto fail3;
+
+ /* Set up and register input device */
+ err = input_register_device(ati_remote->idev);
+ if (err)
+ goto fail3;
+
+ usb_set_intfdata(interface, ati_remote);
+ return 0;
+
+ fail3: usb_kill_urb(ati_remote->irq_urb);
+ usb_kill_urb(ati_remote->out_urb);
+ fail2: ati_remote_free_buffers(ati_remote);
+ fail1: input_free_device(input_dev);
+ kfree(ati_remote);
+ return err;
+}
+
+/*
+ * ati_remote_disconnect
+ */
+static void ati_remote_disconnect(struct usb_interface *interface)
+{
+ struct ati_remote *ati_remote;
+
+ ati_remote = usb_get_intfdata(interface);
+ usb_set_intfdata(interface, NULL);
+ if (!ati_remote) {
+ dev_warn(&interface->dev, "%s - null device?\n", __func__);
+ return;
+ }
+
+ usb_kill_urb(ati_remote->irq_urb);
+ usb_kill_urb(ati_remote->out_urb);
+ input_unregister_device(ati_remote->idev);
+ ati_remote_free_buffers(ati_remote);
+ kfree(ati_remote);
+}
+
+/*
+ * ati_remote_init
+ */
+static int __init ati_remote_init(void)
+{
+ int result;
+
+ result = usb_register(&ati_remote_driver);
+ if (result)
+ printk(KERN_ERR KBUILD_MODNAME
+ ": usb_register error #%d\n", result);
+ else
+ printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":"
+ DRIVER_DESC "\n");
+
+ return result;
+}
+
+/*
+ * ati_remote_exit
+ */
+static void __exit ati_remote_exit(void)
+{
+ usb_deregister(&ati_remote_driver);
+}
+
+/*
+ * module specification
+ */
+
+module_init(ati_remote_init);
+module_exit(ati_remote_exit);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/misc/ati_remote2.c b/drivers/input/misc/ati_remote2.c
new file mode 100644
index 0000000..3c9988d
--- /dev/null
+++ b/drivers/input/misc/ati_remote2.c
@@ -0,0 +1,737 @@
+/*
+ * ati_remote2 - ATI/Philips USB RF remote driver
+ *
+ * Copyright (C) 2005-2008 Ville Syrjala <syrjala@sci.fi>
+ * Copyright (C) 2007-2008 Peter Stokes <linux@dadeos.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ */
+
+#include <linux/usb/input.h>
+
+#define DRIVER_DESC "ATI/Philips USB RF remote driver"
+#define DRIVER_VERSION "0.3"
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_VERSION(DRIVER_VERSION);
+MODULE_AUTHOR("Ville Syrjala <syrjala@sci.fi>");
+MODULE_LICENSE("GPL");
+
+/*
+ * ATI Remote Wonder II Channel Configuration
+ *
+ * The remote control can by assigned one of sixteen "channels" in order to facilitate
+ * the use of multiple remote controls within range of each other.
+ * A remote's "channel" may be altered by pressing and holding the "PC" button for
+ * approximately 3 seconds, after which the button will slowly flash the count of the
+ * currently configured "channel", using the numeric keypad enter a number between 1 and
+ * 16 and then press the "PC" button again, the button will slowly flash the count of the
+ * newly configured "channel".
+ */
+
+static unsigned int channel_mask = 0xFFFF;
+module_param(channel_mask, uint, 0644);
+MODULE_PARM_DESC(channel_mask, "Bitmask of channels to accept <15:Channel16>...<1:Channel2><0:Channel1>");
+
+static unsigned int mode_mask = 0x1F;
+module_param(mode_mask, uint, 0644);
+MODULE_PARM_DESC(mode_mask, "Bitmask of modes to accept <4:PC><3:AUX4><2:AUX3><1:AUX2><0:AUX1>");
+
+static struct usb_device_id ati_remote2_id_table[] = {
+ { USB_DEVICE(0x0471, 0x0602) }, /* ATI Remote Wonder II */
+ { }
+};
+MODULE_DEVICE_TABLE(usb, ati_remote2_id_table);
+
+static DEFINE_MUTEX(ati_remote2_mutex);
+
+enum {
+ ATI_REMOTE2_OPENED = 0x1,
+ ATI_REMOTE2_SUSPENDED = 0x2,
+};
+
+enum {
+ ATI_REMOTE2_AUX1,
+ ATI_REMOTE2_AUX2,
+ ATI_REMOTE2_AUX3,
+ ATI_REMOTE2_AUX4,
+ ATI_REMOTE2_PC,
+ ATI_REMOTE2_MODES,
+};
+
+static const struct {
+ u8 hw_code;
+ u16 keycode;
+} ati_remote2_key_table[] = {
+ { 0x00, KEY_0 },
+ { 0x01, KEY_1 },
+ { 0x02, KEY_2 },
+ { 0x03, KEY_3 },
+ { 0x04, KEY_4 },
+ { 0x05, KEY_5 },
+ { 0x06, KEY_6 },
+ { 0x07, KEY_7 },
+ { 0x08, KEY_8 },
+ { 0x09, KEY_9 },
+ { 0x0c, KEY_POWER },
+ { 0x0d, KEY_MUTE },
+ { 0x10, KEY_VOLUMEUP },
+ { 0x11, KEY_VOLUMEDOWN },
+ { 0x20, KEY_CHANNELUP },
+ { 0x21, KEY_CHANNELDOWN },
+ { 0x28, KEY_FORWARD },
+ { 0x29, KEY_REWIND },
+ { 0x2c, KEY_PLAY },
+ { 0x30, KEY_PAUSE },
+ { 0x31, KEY_STOP },
+ { 0x37, KEY_RECORD },
+ { 0x38, KEY_DVD },
+ { 0x39, KEY_TV },
+ { 0x3f, KEY_PROG1 }, /* AUX1-AUX4 and PC */
+ { 0x54, KEY_MENU },
+ { 0x58, KEY_UP },
+ { 0x59, KEY_DOWN },
+ { 0x5a, KEY_LEFT },
+ { 0x5b, KEY_RIGHT },
+ { 0x5c, KEY_OK },
+ { 0x78, KEY_A },
+ { 0x79, KEY_B },
+ { 0x7a, KEY_C },
+ { 0x7b, KEY_D },
+ { 0x7c, KEY_E },
+ { 0x7d, KEY_F },
+ { 0x82, KEY_ENTER },
+ { 0x8e, KEY_VENDOR },
+ { 0x96, KEY_COFFEE },
+ { 0xa9, BTN_LEFT },
+ { 0xaa, BTN_RIGHT },
+ { 0xbe, KEY_QUESTION },
+ { 0xd0, KEY_EDIT },
+ { 0xd5, KEY_FRONT },
+ { 0xf9, KEY_INFO },
+};
+
+struct ati_remote2 {
+ struct input_dev *idev;
+ struct usb_device *udev;
+
+ struct usb_interface *intf[2];
+ struct usb_endpoint_descriptor *ep[2];
+ struct urb *urb[2];
+ void *buf[2];
+ dma_addr_t buf_dma[2];
+
+ unsigned long jiffies;
+ int mode;
+
+ char name[64];
+ char phys[64];
+
+ /* Each mode (AUX1-AUX4 and PC) can have an independent keymap. */
+ u16 keycode[ATI_REMOTE2_MODES][ARRAY_SIZE(ati_remote2_key_table)];
+
+ unsigned int flags;
+};
+
+static int ati_remote2_probe(struct usb_interface *interface, const struct usb_device_id *id);
+static void ati_remote2_disconnect(struct usb_interface *interface);
+static int ati_remote2_suspend(struct usb_interface *interface, pm_message_t message);
+static int ati_remote2_resume(struct usb_interface *interface);
+
+static struct usb_driver ati_remote2_driver = {
+ .name = "ati_remote2",
+ .probe = ati_remote2_probe,
+ .disconnect = ati_remote2_disconnect,
+ .id_table = ati_remote2_id_table,
+ .suspend = ati_remote2_suspend,
+ .resume = ati_remote2_resume,
+ .supports_autosuspend = 1,
+};
+
+static int ati_remote2_submit_urbs(struct ati_remote2 *ar2)
+{
+ int r;
+
+ r = usb_submit_urb(ar2->urb[0], GFP_KERNEL);
+ if (r) {
+ dev_err(&ar2->intf[0]->dev,
+ "%s(): usb_submit_urb() = %d\n", __func__, r);
+ return r;
+ }
+ r = usb_submit_urb(ar2->urb[1], GFP_KERNEL);
+ if (r) {
+ usb_kill_urb(ar2->urb[0]);
+ dev_err(&ar2->intf[1]->dev,
+ "%s(): usb_submit_urb() = %d\n", __func__, r);
+ return r;
+ }
+
+ return 0;
+}
+
+static void ati_remote2_kill_urbs(struct ati_remote2 *ar2)
+{
+ usb_kill_urb(ar2->urb[1]);
+ usb_kill_urb(ar2->urb[0]);
+}
+
+static int ati_remote2_open(struct input_dev *idev)
+{
+ struct ati_remote2 *ar2 = input_get_drvdata(idev);
+ int r;
+
+ dev_dbg(&ar2->intf[0]->dev, "%s()\n", __func__);
+
+ r = usb_autopm_get_interface(ar2->intf[0]);
+ if (r) {
+ dev_err(&ar2->intf[0]->dev,
+ "%s(): usb_autopm_get_interface() = %d\n", __func__, r);
+ goto fail1;
+ }
+
+ mutex_lock(&ati_remote2_mutex);
+
+ if (!(ar2->flags & ATI_REMOTE2_SUSPENDED)) {
+ r = ati_remote2_submit_urbs(ar2);
+ if (r)
+ goto fail2;
+ }
+
+ ar2->flags |= ATI_REMOTE2_OPENED;
+
+ mutex_unlock(&ati_remote2_mutex);
+
+ usb_autopm_put_interface(ar2->intf[0]);
+
+ return 0;
+
+ fail2:
+ mutex_unlock(&ati_remote2_mutex);
+ usb_autopm_put_interface(ar2->intf[0]);
+ fail1:
+ return r;
+}
+
+static void ati_remote2_close(struct input_dev *idev)
+{
+ struct ati_remote2 *ar2 = input_get_drvdata(idev);
+
+ dev_dbg(&ar2->intf[0]->dev, "%s()\n", __func__);
+
+ mutex_lock(&ati_remote2_mutex);
+
+ if (!(ar2->flags & ATI_REMOTE2_SUSPENDED))
+ ati_remote2_kill_urbs(ar2);
+
+ ar2->flags &= ~ATI_REMOTE2_OPENED;
+
+ mutex_unlock(&ati_remote2_mutex);
+}
+
+static void ati_remote2_input_mouse(struct ati_remote2 *ar2)
+{
+ struct input_dev *idev = ar2->idev;
+ u8 *data = ar2->buf[0];
+ int channel, mode;
+
+ channel = data[0] >> 4;
+
+ if (!((1 << channel) & channel_mask))
+ return;
+
+ mode = data[0] & 0x0F;
+
+ if (mode > ATI_REMOTE2_PC) {
+ dev_err(&ar2->intf[0]->dev,
+ "Unknown mode byte (%02x %02x %02x %02x)\n",
+ data[3], data[2], data[1], data[0]);
+ return;
+ }
+
+ if (!((1 << mode) & mode_mask))
+ return;
+
+ input_event(idev, EV_REL, REL_X, (s8) data[1]);
+ input_event(idev, EV_REL, REL_Y, (s8) data[2]);
+ input_sync(idev);
+}
+
+static int ati_remote2_lookup(unsigned int hw_code)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(ati_remote2_key_table); i++)
+ if (ati_remote2_key_table[i].hw_code == hw_code)
+ return i;
+
+ return -1;
+}
+
+static void ati_remote2_input_key(struct ati_remote2 *ar2)
+{
+ struct input_dev *idev = ar2->idev;
+ u8 *data = ar2->buf[1];
+ int channel, mode, hw_code, index;
+
+ channel = data[0] >> 4;
+
+ if (!((1 << channel) & channel_mask))
+ return;
+
+ mode = data[0] & 0x0F;
+
+ if (mode > ATI_REMOTE2_PC) {
+ dev_err(&ar2->intf[1]->dev,
+ "Unknown mode byte (%02x %02x %02x %02x)\n",
+ data[3], data[2], data[1], data[0]);
+ return;
+ }
+
+ hw_code = data[2];
+ if (hw_code == 0x3f) {
+ /*
+ * For some incomprehensible reason the mouse pad generates
+ * events which look identical to the events from the last
+ * pressed mode key. Naturally we don't want to generate key
+ * events for the mouse pad so we filter out any subsequent
+ * events from the same mode key.
+ */
+ if (ar2->mode == mode)
+ return;
+
+ if (data[1] == 0)
+ ar2->mode = mode;
+ }
+
+ if (!((1 << mode) & mode_mask))
+ return;
+
+ index = ati_remote2_lookup(hw_code);
+ if (index < 0) {
+ dev_err(&ar2->intf[1]->dev,
+ "Unknown code byte (%02x %02x %02x %02x)\n",
+ data[3], data[2], data[1], data[0]);
+ return;
+ }
+
+ switch (data[1]) {
+ case 0: /* release */
+ break;
+ case 1: /* press */
+ ar2->jiffies = jiffies + msecs_to_jiffies(idev->rep[REP_DELAY]);
+ break;
+ case 2: /* repeat */
+
+ /* No repeat for mouse buttons. */
+ if (ar2->keycode[mode][index] == BTN_LEFT ||
+ ar2->keycode[mode][index] == BTN_RIGHT)
+ return;
+
+ if (!time_after_eq(jiffies, ar2->jiffies))
+ return;
+
+ ar2->jiffies = jiffies + msecs_to_jiffies(idev->rep[REP_PERIOD]);
+ break;
+ default:
+ dev_err(&ar2->intf[1]->dev,
+ "Unknown state byte (%02x %02x %02x %02x)\n",
+ data[3], data[2], data[1], data[0]);
+ return;
+ }
+
+ input_event(idev, EV_KEY, ar2->keycode[mode][index], data[1]);
+ input_sync(idev);
+}
+
+static void ati_remote2_complete_mouse(struct urb *urb)
+{
+ struct ati_remote2 *ar2 = urb->context;
+ int r;
+
+ switch (urb->status) {
+ case 0:
+ usb_mark_last_busy(ar2->udev);
+ ati_remote2_input_mouse(ar2);
+ break;
+ case -ENOENT:
+ case -EILSEQ:
+ case -ECONNRESET:
+ case -ESHUTDOWN:
+ dev_dbg(&ar2->intf[0]->dev,
+ "%s(): urb status = %d\n", __func__, urb->status);
+ return;
+ default:
+ usb_mark_last_busy(ar2->udev);
+ dev_err(&ar2->intf[0]->dev,
+ "%s(): urb status = %d\n", __func__, urb->status);
+ }
+
+ r = usb_submit_urb(urb, GFP_ATOMIC);
+ if (r)
+ dev_err(&ar2->intf[0]->dev,
+ "%s(): usb_submit_urb() = %d\n", __func__, r);
+}
+
+static void ati_remote2_complete_key(struct urb *urb)
+{
+ struct ati_remote2 *ar2 = urb->context;
+ int r;
+
+ switch (urb->status) {
+ case 0:
+ usb_mark_last_busy(ar2->udev);
+ ati_remote2_input_key(ar2);
+ break;
+ case -ENOENT:
+ case -EILSEQ:
+ case -ECONNRESET:
+ case -ESHUTDOWN:
+ dev_dbg(&ar2->intf[1]->dev,
+ "%s(): urb status = %d\n", __func__, urb->status);
+ return;
+ default:
+ usb_mark_last_busy(ar2->udev);
+ dev_err(&ar2->intf[1]->dev,
+ "%s(): urb status = %d\n", __func__, urb->status);
+ }
+
+ r = usb_submit_urb(urb, GFP_ATOMIC);
+ if (r)
+ dev_err(&ar2->intf[1]->dev,
+ "%s(): usb_submit_urb() = %d\n", __func__, r);
+}
+
+static int ati_remote2_getkeycode(struct input_dev *idev,
+ int scancode, int *keycode)
+{
+ struct ati_remote2 *ar2 = input_get_drvdata(idev);
+ int index, mode;
+
+ mode = scancode >> 8;
+ if (mode > ATI_REMOTE2_PC || !((1 << mode) & mode_mask))
+ return -EINVAL;
+
+ index = ati_remote2_lookup(scancode & 0xFF);
+ if (index < 0)
+ return -EINVAL;
+
+ *keycode = ar2->keycode[mode][index];
+ return 0;
+}
+
+static int ati_remote2_setkeycode(struct input_dev *idev, int scancode, int keycode)
+{
+ struct ati_remote2 *ar2 = input_get_drvdata(idev);
+ int index, mode, old_keycode;
+
+ mode = scancode >> 8;
+ if (mode > ATI_REMOTE2_PC || !((1 << mode) & mode_mask))
+ return -EINVAL;
+
+ index = ati_remote2_lookup(scancode & 0xFF);
+ if (index < 0)
+ return -EINVAL;
+
+ if (keycode < KEY_RESERVED || keycode > KEY_MAX)
+ return -EINVAL;
+
+ old_keycode = ar2->keycode[mode][index];
+ ar2->keycode[mode][index] = keycode;
+ set_bit(keycode, idev->keybit);
+
+ for (mode = 0; mode < ATI_REMOTE2_MODES; mode++) {
+ for (index = 0; index < ARRAY_SIZE(ati_remote2_key_table); index++) {
+ if (ar2->keycode[mode][index] == old_keycode)
+ return 0;
+ }
+ }
+
+ clear_bit(old_keycode, idev->keybit);
+
+ return 0;
+}
+
+static int ati_remote2_input_init(struct ati_remote2 *ar2)
+{
+ struct input_dev *idev;
+ int index, mode, retval;
+
+ idev = input_allocate_device();
+ if (!idev)
+ return -ENOMEM;
+
+ ar2->idev = idev;
+ input_set_drvdata(idev, ar2);
+
+ idev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP) | BIT_MASK(EV_REL);
+ idev->keybit[BIT_WORD(BTN_MOUSE)] = BIT_MASK(BTN_LEFT) |
+ BIT_MASK(BTN_RIGHT);
+ idev->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(REL_Y);
+
+ for (mode = 0; mode < ATI_REMOTE2_MODES; mode++) {
+ for (index = 0; index < ARRAY_SIZE(ati_remote2_key_table); index++) {
+ ar2->keycode[mode][index] = ati_remote2_key_table[index].keycode;
+ set_bit(ar2->keycode[mode][index], idev->keybit);
+ }
+ }
+
+ /* AUX1-AUX4 and PC generate the same scancode. */
+ index = ati_remote2_lookup(0x3f);
+ ar2->keycode[ATI_REMOTE2_AUX1][index] = KEY_PROG1;
+ ar2->keycode[ATI_REMOTE2_AUX2][index] = KEY_PROG2;
+ ar2->keycode[ATI_REMOTE2_AUX3][index] = KEY_PROG3;
+ ar2->keycode[ATI_REMOTE2_AUX4][index] = KEY_PROG4;
+ ar2->keycode[ATI_REMOTE2_PC][index] = KEY_PC;
+ set_bit(KEY_PROG1, idev->keybit);
+ set_bit(KEY_PROG2, idev->keybit);
+ set_bit(KEY_PROG3, idev->keybit);
+ set_bit(KEY_PROG4, idev->keybit);
+ set_bit(KEY_PC, idev->keybit);
+
+ idev->rep[REP_DELAY] = 250;
+ idev->rep[REP_PERIOD] = 33;
+
+ idev->open = ati_remote2_open;
+ idev->close = ati_remote2_close;
+
+ idev->getkeycode = ati_remote2_getkeycode;
+ idev->setkeycode = ati_remote2_setkeycode;
+
+ idev->name = ar2->name;
+ idev->phys = ar2->phys;
+
+ usb_to_input_id(ar2->udev, &idev->id);
+ idev->dev.parent = &ar2->udev->dev;
+
+ retval = input_register_device(idev);
+ if (retval)
+ input_free_device(idev);
+
+ return retval;
+}
+
+static int ati_remote2_urb_init(struct ati_remote2 *ar2)
+{
+ struct usb_device *udev = ar2->udev;
+ int i, pipe, maxp;
+
+ for (i = 0; i < 2; i++) {
+ ar2->buf[i] = usb_buffer_alloc(udev, 4, GFP_KERNEL, &ar2->buf_dma[i]);
+ if (!ar2->buf[i])
+ return -ENOMEM;
+
+ ar2->urb[i] = usb_alloc_urb(0, GFP_KERNEL);
+ if (!ar2->urb[i])
+ return -ENOMEM;
+
+ pipe = usb_rcvintpipe(udev, ar2->ep[i]->bEndpointAddress);
+ maxp = usb_maxpacket(udev, pipe, usb_pipeout(pipe));
+ maxp = maxp > 4 ? 4 : maxp;
+
+ usb_fill_int_urb(ar2->urb[i], udev, pipe, ar2->buf[i], maxp,
+ i ? ati_remote2_complete_key : ati_remote2_complete_mouse,
+ ar2, ar2->ep[i]->bInterval);
+ ar2->urb[i]->transfer_dma = ar2->buf_dma[i];
+ ar2->urb[i]->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+ }
+
+ return 0;
+}
+
+static void ati_remote2_urb_cleanup(struct ati_remote2 *ar2)
+{
+ int i;
+
+ for (i = 0; i < 2; i++) {
+ usb_free_urb(ar2->urb[i]);
+ usb_buffer_free(ar2->udev, 4, ar2->buf[i], ar2->buf_dma[i]);
+ }
+}
+
+static int ati_remote2_setup(struct ati_remote2 *ar2)
+{
+ int r, i, channel;
+
+ /*
+ * Configure receiver to only accept input from remote "channel"
+ * channel == 0 -> Accept input from any remote channel
+ * channel == 1 -> Only accept input from remote channel 1
+ * channel == 2 -> Only accept input from remote channel 2
+ * ...
+ * channel == 16 -> Only accept input from remote channel 16
+ */
+
+ channel = 0;
+ for (i = 0; i < 16; i++) {
+ if ((1 << i) & channel_mask) {
+ if (!(~(1 << i) & 0xFFFF & channel_mask))
+ channel = i + 1;
+ break;
+ }
+ }
+
+ r = usb_control_msg(ar2->udev, usb_sndctrlpipe(ar2->udev, 0),
+ 0x20,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
+ channel, 0x0, NULL, 0, USB_CTRL_SET_TIMEOUT);
+ if (r) {
+ dev_err(&ar2->udev->dev, "%s - failed to set channel due to error: %d\n",
+ __func__, r);
+ return r;
+ }
+
+ return 0;
+}
+
+static int ati_remote2_probe(struct usb_interface *interface, const struct usb_device_id *id)
+{
+ struct usb_device *udev = interface_to_usbdev(interface);
+ struct usb_host_interface *alt = interface->cur_altsetting;
+ struct ati_remote2 *ar2;
+ int r;
+
+ if (alt->desc.bInterfaceNumber)
+ return -ENODEV;
+
+ ar2 = kzalloc(sizeof (struct ati_remote2), GFP_KERNEL);
+ if (!ar2)
+ return -ENOMEM;
+
+ ar2->udev = udev;
+
+ ar2->intf[0] = interface;
+ ar2->ep[0] = &alt->endpoint[0].desc;
+
+ ar2->intf[1] = usb_ifnum_to_if(udev, 1);
+ r = usb_driver_claim_interface(&ati_remote2_driver, ar2->intf[1], ar2);
+ if (r)
+ goto fail1;
+ alt = ar2->intf[1]->cur_altsetting;
+ ar2->ep[1] = &alt->endpoint[0].desc;
+
+ r = ati_remote2_urb_init(ar2);
+ if (r)
+ goto fail2;
+
+ r = ati_remote2_setup(ar2);
+ if (r)
+ goto fail2;
+
+ usb_make_path(udev, ar2->phys, sizeof(ar2->phys));
+ strlcat(ar2->phys, "/input0", sizeof(ar2->phys));
+
+ strlcat(ar2->name, "ATI Remote Wonder II", sizeof(ar2->name));
+
+ r = ati_remote2_input_init(ar2);
+ if (r)
+ goto fail2;
+
+ usb_set_intfdata(interface, ar2);
+
+ interface->needs_remote_wakeup = 1;
+
+ return 0;
+
+ fail2:
+ ati_remote2_urb_cleanup(ar2);
+
+ usb_driver_release_interface(&ati_remote2_driver, ar2->intf[1]);
+ fail1:
+ kfree(ar2);
+
+ return r;
+}
+
+static void ati_remote2_disconnect(struct usb_interface *interface)
+{
+ struct ati_remote2 *ar2;
+ struct usb_host_interface *alt = interface->cur_altsetting;
+
+ if (alt->desc.bInterfaceNumber)
+ return;
+
+ ar2 = usb_get_intfdata(interface);
+ usb_set_intfdata(interface, NULL);
+
+ input_unregister_device(ar2->idev);
+
+ ati_remote2_urb_cleanup(ar2);
+
+ usb_driver_release_interface(&ati_remote2_driver, ar2->intf[1]);
+
+ kfree(ar2);
+}
+
+static int ati_remote2_suspend(struct usb_interface *interface,
+ pm_message_t message)
+{
+ struct ati_remote2 *ar2;
+ struct usb_host_interface *alt = interface->cur_altsetting;
+
+ if (alt->desc.bInterfaceNumber)
+ return 0;
+
+ ar2 = usb_get_intfdata(interface);
+
+ dev_dbg(&ar2->intf[0]->dev, "%s()\n", __func__);
+
+ mutex_lock(&ati_remote2_mutex);
+
+ if (ar2->flags & ATI_REMOTE2_OPENED)
+ ati_remote2_kill_urbs(ar2);
+
+ ar2->flags |= ATI_REMOTE2_SUSPENDED;
+
+ mutex_unlock(&ati_remote2_mutex);
+
+ return 0;
+}
+
+static int ati_remote2_resume(struct usb_interface *interface)
+{
+ struct ati_remote2 *ar2;
+ struct usb_host_interface *alt = interface->cur_altsetting;
+ int r = 0;
+
+ if (alt->desc.bInterfaceNumber)
+ return 0;
+
+ ar2 = usb_get_intfdata(interface);
+
+ dev_dbg(&ar2->intf[0]->dev, "%s()\n", __func__);
+
+ mutex_lock(&ati_remote2_mutex);
+
+ if (ar2->flags & ATI_REMOTE2_OPENED)
+ r = ati_remote2_submit_urbs(ar2);
+
+ if (!r)
+ ar2->flags &= ~ATI_REMOTE2_SUSPENDED;
+
+ mutex_unlock(&ati_remote2_mutex);
+
+ return r;
+}
+
+static int __init ati_remote2_init(void)
+{
+ int r;
+
+ r = usb_register(&ati_remote2_driver);
+ if (r)
+ printk(KERN_ERR "ati_remote2: usb_register() = %d\n", r);
+ else
+ printk(KERN_INFO "ati_remote2: " DRIVER_DESC " " DRIVER_VERSION "\n");
+
+ return r;
+}
+
+static void __exit ati_remote2_exit(void)
+{
+ usb_deregister(&ati_remote2_driver);
+}
+
+module_init(ati_remote2_init);
+module_exit(ati_remote2_exit);
diff --git a/drivers/input/misc/atlas_btns.c b/drivers/input/misc/atlas_btns.c
new file mode 100644
index 0000000..1b87191
--- /dev/null
+++ b/drivers/input/misc/atlas_btns.c
@@ -0,0 +1,182 @@
+/*
+ * atlas_btns.c - Atlas Wallmount Touchscreen ACPI Extras
+ *
+ * Copyright (C) 2006 Jaya Kumar
+ * Based on Toshiba ACPI by John Belmonte and ASUS ACPI
+ * This work was sponsored by CIS(M) Sdn Bhd.
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/types.h>
+#include <asm/uaccess.h>
+#include <acpi/acpi_drivers.h>
+
+#define ACPI_ATLAS_NAME "Atlas ACPI"
+#define ACPI_ATLAS_CLASS "Atlas"
+
+static unsigned short atlas_keymap[16];
+static struct input_dev *input_dev;
+
+/* button handling code */
+static acpi_status acpi_atlas_button_setup(acpi_handle region_handle,
+ u32 function, void *handler_context, void **return_context)
+{
+ *return_context =
+ (function != ACPI_REGION_DEACTIVATE) ? handler_context : NULL;
+
+ return AE_OK;
+}
+
+static acpi_status acpi_atlas_button_handler(u32 function,
+ acpi_physical_address address,
+ u32 bit_width, acpi_integer *value,
+ void *handler_context, void *region_context)
+{
+ acpi_status status;
+
+ if (function == ACPI_WRITE) {
+ int code = address & 0x0f;
+ int key_down = !(address & 0x10);
+
+ input_event(input_dev, EV_MSC, MSC_SCAN, code);
+ input_report_key(input_dev, atlas_keymap[code], key_down);
+ input_sync(input_dev);
+
+ status = 0;
+ } else {
+ printk(KERN_WARNING "atlas: shrugged on unexpected function"
+ ":function=%x,address=%lx,value=%x\n",
+ function, (unsigned long)address, (u32)*value);
+ status = -EINVAL;
+ }
+
+ return status;
+}
+
+static int atlas_acpi_button_add(struct acpi_device *device)
+{
+ acpi_status status;
+ int i;
+ int err;
+
+ input_dev = input_allocate_device();
+ if (!input_dev) {
+ printk(KERN_ERR "atlas: unable to allocate input device\n");
+ return -ENOMEM;
+ }
+
+ input_dev->name = "Atlas ACPI button driver";
+ input_dev->phys = "ASIM0000/atlas/input0";
+ input_dev->id.bustype = BUS_HOST;
+ input_dev->keycode = atlas_keymap;
+ input_dev->keycodesize = sizeof(unsigned short);
+ input_dev->keycodemax = ARRAY_SIZE(atlas_keymap);
+
+ input_set_capability(input_dev, EV_MSC, MSC_SCAN);
+ __set_bit(EV_KEY, input_dev->evbit);
+ for (i = 0; i < ARRAY_SIZE(atlas_keymap); i++) {
+ if (i < 9) {
+ atlas_keymap[i] = KEY_F1 + i;
+ __set_bit(KEY_F1 + i, input_dev->keybit);
+ } else
+ atlas_keymap[i] = KEY_RESERVED;
+ }
+
+ err = input_register_device(input_dev);
+ if (err) {
+ printk(KERN_ERR "atlas: couldn't register input device\n");
+ input_free_device(input_dev);
+ return err;
+ }
+
+ /* hookup button handler */
+ status = acpi_install_address_space_handler(device->handle,
+ 0x81, &acpi_atlas_button_handler,
+ &acpi_atlas_button_setup, device);
+ if (ACPI_FAILURE(status)) {
+ printk(KERN_ERR "Atlas: Error installing addr spc handler\n");
+ input_unregister_device(input_dev);
+ status = -EINVAL;
+ }
+
+ return status;
+}
+
+static int atlas_acpi_button_remove(struct acpi_device *device, int type)
+{
+ acpi_status status;
+
+ status = acpi_remove_address_space_handler(device->handle,
+ 0x81, &acpi_atlas_button_handler);
+ if (ACPI_FAILURE(status)) {
+ printk(KERN_ERR "Atlas: Error removing addr spc handler\n");
+ status = -EINVAL;
+ }
+
+ input_unregister_device(input_dev);
+
+ return status;
+}
+
+static const struct acpi_device_id atlas_device_ids[] = {
+ {"ASIM0000", 0},
+ {"", 0},
+};
+MODULE_DEVICE_TABLE(acpi, atlas_device_ids);
+
+static struct acpi_driver atlas_acpi_driver = {
+ .name = ACPI_ATLAS_NAME,
+ .class = ACPI_ATLAS_CLASS,
+ .ids = atlas_device_ids,
+ .ops = {
+ .add = atlas_acpi_button_add,
+ .remove = atlas_acpi_button_remove,
+ },
+};
+
+static int __init atlas_acpi_init(void)
+{
+ int result;
+
+ if (acpi_disabled)
+ return -ENODEV;
+
+ result = acpi_bus_register_driver(&atlas_acpi_driver);
+ if (result < 0) {
+ printk(KERN_ERR "Atlas ACPI: Unable to register driver\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static void __exit atlas_acpi_exit(void)
+{
+ acpi_bus_unregister_driver(&atlas_acpi_driver);
+}
+
+module_init(atlas_acpi_init);
+module_exit(atlas_acpi_exit);
+
+MODULE_AUTHOR("Jaya Kumar");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Atlas button driver");
+
diff --git a/drivers/input/misc/cm109.c b/drivers/input/misc/cm109.c
new file mode 100644
index 0000000..86457fe
--- /dev/null
+++ b/drivers/input/misc/cm109.c
@@ -0,0 +1,917 @@
+/*
+ * Driver for the VoIP USB phones with CM109 chipsets.
+ *
+ * Copyright (C) 2007 - 2008 Alfred E. Heggestad <aeh@db.org>
+ *
+ * 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, version 2.
+ */
+
+/*
+ * Tested devices:
+ * - Komunikate KIP1000
+ * - Genius G-talk
+ * - Allied-Telesis Corega USBPH01
+ * - ...
+ *
+ * This driver is based on the yealink.c driver
+ *
+ * Thanks to:
+ * - Authors of yealink.c
+ * - Thomas Reitmayr
+ * - Oliver Neukum for good review comments and code
+ * - Shaun Jackman <sjackman@gmail.com> for Genius G-talk keymap
+ * - Dmitry Torokhov for valuable input and review
+ *
+ * Todo:
+ * - Read/write EEPROM
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/rwsem.h>
+#include <linux/usb/input.h>
+
+#define DRIVER_VERSION "20080805"
+#define DRIVER_AUTHOR "Alfred E. Heggestad"
+#define DRIVER_DESC "CM109 phone driver"
+
+static char *phone = "kip1000";
+module_param(phone, charp, S_IRUSR);
+MODULE_PARM_DESC(phone, "Phone name {kip1000, gtalk, usbph01, atcom}");
+
+enum {
+ /* HID Registers */
+ HID_IR0 = 0x00, /* Record/Playback-mute button, Volume up/down */
+ HID_IR1 = 0x01, /* GPI, generic registers or EEPROM_DATA0 */
+ HID_IR2 = 0x02, /* Generic registers or EEPROM_DATA1 */
+ HID_IR3 = 0x03, /* Generic registers or EEPROM_CTRL */
+ HID_OR0 = 0x00, /* Mapping control, buzzer, SPDIF (offset 0x04) */
+ HID_OR1 = 0x01, /* GPO - General Purpose Output */
+ HID_OR2 = 0x02, /* Set GPIO to input/output mode */
+ HID_OR3 = 0x03, /* SPDIF status channel or EEPROM_CTRL */
+
+ /* HID_IR0 */
+ RECORD_MUTE = 1 << 3,
+ PLAYBACK_MUTE = 1 << 2,
+ VOLUME_DOWN = 1 << 1,
+ VOLUME_UP = 1 << 0,
+
+ /* HID_OR0 */
+ /* bits 7-6
+ 0: HID_OR1-2 are used for GPO; HID_OR0, 3 are used for buzzer
+ and SPDIF
+ 1: HID_OR0-3 are used as generic HID registers
+ 2: Values written to HID_OR0-3 are also mapped to MCU_CTRL,
+ EEPROM_DATA0-1, EEPROM_CTRL (see Note)
+ 3: Reserved
+ */
+ HID_OR_GPO_BUZ_SPDIF = 0 << 6,
+ HID_OR_GENERIC_HID_REG = 1 << 6,
+ HID_OR_MAP_MCU_EEPROM = 2 << 6,
+
+ BUZZER_ON = 1 << 5,
+
+ /* up to 256 normal keys, up to 16 special keys */
+ KEYMAP_SIZE = 256 + 16,
+};
+
+/* CM109 protocol packet */
+struct cm109_ctl_packet {
+ u8 byte[4];
+} __attribute__ ((packed));
+
+enum { USB_PKT_LEN = sizeof(struct cm109_ctl_packet) };
+
+/* CM109 device structure */
+struct cm109_dev {
+ struct input_dev *idev; /* input device */
+ struct usb_device *udev; /* usb device */
+ struct usb_interface *intf;
+
+ /* irq input channel */
+ struct cm109_ctl_packet *irq_data;
+ dma_addr_t irq_dma;
+ struct urb *urb_irq;
+
+ /* control output channel */
+ struct cm109_ctl_packet *ctl_data;
+ dma_addr_t ctl_dma;
+ struct usb_ctrlrequest *ctl_req;
+ dma_addr_t ctl_req_dma;
+ struct urb *urb_ctl;
+ /*
+ * The 3 bitfields below are protected by ctl_submit_lock.
+ * They have to be separate since they are accessed from IRQ
+ * context.
+ */
+ unsigned irq_urb_pending:1; /* irq_urb is in flight */
+ unsigned ctl_urb_pending:1; /* ctl_urb is in flight */
+ unsigned buzzer_pending:1; /* need to issue buzz command */
+ spinlock_t ctl_submit_lock;
+
+ unsigned char buzzer_state; /* on/off */
+
+ /* flags */
+ unsigned open:1;
+ unsigned resetting:1;
+ unsigned shutdown:1;
+
+ /* This mutex protects writes to the above flags */
+ struct mutex pm_mutex;
+
+ unsigned short keymap[KEYMAP_SIZE];
+
+ char phys[64]; /* physical device path */
+ int key_code; /* last reported key */
+ int keybit; /* 0=new scan 1,2,4,8=scan columns */
+ u8 gpi; /* Cached value of GPI (high nibble) */
+};
+
+/******************************************************************************
+ * CM109 key interface
+ *****************************************************************************/
+
+static unsigned short special_keymap(int code)
+{
+ if (code > 0xff) {
+ switch (code - 0xff) {
+ case RECORD_MUTE: return KEY_MUTE;
+ case PLAYBACK_MUTE: return KEY_MUTE;
+ case VOLUME_DOWN: return KEY_VOLUMEDOWN;
+ case VOLUME_UP: return KEY_VOLUMEUP;
+ }
+ }
+ return KEY_RESERVED;
+}
+
+/* Map device buttons to internal key events.
+ *
+ * The "up" and "down" keys, are symbolised by arrows on the button.
+ * The "pickup" and "hangup" keys are symbolised by a green and red phone
+ * on the button.
+
+ Komunikate KIP1000 Keyboard Matrix
+
+ -> -- 1 -- 2 -- 3 --> GPI pin 4 (0x10)
+ | | | |
+ <- -- 4 -- 5 -- 6 --> GPI pin 5 (0x20)
+ | | | |
+ END - 7 -- 8 -- 9 --> GPI pin 6 (0x40)
+ | | | |
+ OK -- * -- 0 -- # --> GPI pin 7 (0x80)
+ | | | |
+
+ /|\ /|\ /|\ /|\
+ | | | |
+GPO
+pin: 3 2 1 0
+ 0x8 0x4 0x2 0x1
+
+ */
+static unsigned short keymap_kip1000(int scancode)
+{
+ switch (scancode) { /* phone key: */
+ case 0x82: return KEY_NUMERIC_0; /* 0 */
+ case 0x14: return KEY_NUMERIC_1; /* 1 */
+ case 0x12: return KEY_NUMERIC_2; /* 2 */
+ case 0x11: return KEY_NUMERIC_3; /* 3 */
+ case 0x24: return KEY_NUMERIC_4; /* 4 */
+ case 0x22: return KEY_NUMERIC_5; /* 5 */
+ case 0x21: return KEY_NUMERIC_6; /* 6 */
+ case 0x44: return KEY_NUMERIC_7; /* 7 */
+ case 0x42: return KEY_NUMERIC_8; /* 8 */
+ case 0x41: return KEY_NUMERIC_9; /* 9 */
+ case 0x81: return KEY_NUMERIC_POUND; /* # */
+ case 0x84: return KEY_NUMERIC_STAR; /* * */
+ case 0x88: return KEY_ENTER; /* pickup */
+ case 0x48: return KEY_ESC; /* hangup */
+ case 0x28: return KEY_LEFT; /* IN */
+ case 0x18: return KEY_RIGHT; /* OUT */
+ default: return special_keymap(scancode);
+ }
+}
+
+/*
+ Contributed by Shaun Jackman <sjackman@gmail.com>
+
+ Genius G-Talk keyboard matrix
+ 0 1 2 3
+ 4: 0 4 8 Talk
+ 5: 1 5 9 End
+ 6: 2 6 # Up
+ 7: 3 7 * Down
+*/
+static unsigned short keymap_gtalk(int scancode)
+{
+ switch (scancode) {
+ case 0x11: return KEY_NUMERIC_0;
+ case 0x21: return KEY_NUMERIC_1;
+ case 0x41: return KEY_NUMERIC_2;
+ case 0x81: return KEY_NUMERIC_3;
+ case 0x12: return KEY_NUMERIC_4;
+ case 0x22: return KEY_NUMERIC_5;
+ case 0x42: return KEY_NUMERIC_6;
+ case 0x82: return KEY_NUMERIC_7;
+ case 0x14: return KEY_NUMERIC_8;
+ case 0x24: return KEY_NUMERIC_9;
+ case 0x44: return KEY_NUMERIC_POUND; /* # */
+ case 0x84: return KEY_NUMERIC_STAR; /* * */
+ case 0x18: return KEY_ENTER; /* Talk (green handset) */
+ case 0x28: return KEY_ESC; /* End (red handset) */
+ case 0x48: return KEY_UP; /* Menu up (rocker switch) */
+ case 0x88: return KEY_DOWN; /* Menu down (rocker switch) */
+ default: return special_keymap(scancode);
+ }
+}
+
+/*
+ * Keymap for Allied-Telesis Corega USBPH01
+ * http://www.alliedtelesis-corega.com/2/1344/1437/1360/chprd.html
+ *
+ * Contributed by july@nat.bg
+ */
+static unsigned short keymap_usbph01(int scancode)
+{
+ switch (scancode) {
+ case 0x11: return KEY_NUMERIC_0; /* 0 */
+ case 0x21: return KEY_NUMERIC_1; /* 1 */
+ case 0x41: return KEY_NUMERIC_2; /* 2 */
+ case 0x81: return KEY_NUMERIC_3; /* 3 */
+ case 0x12: return KEY_NUMERIC_4; /* 4 */
+ case 0x22: return KEY_NUMERIC_5; /* 5 */
+ case 0x42: return KEY_NUMERIC_6; /* 6 */
+ case 0x82: return KEY_NUMERIC_7; /* 7 */
+ case 0x14: return KEY_NUMERIC_8; /* 8 */
+ case 0x24: return KEY_NUMERIC_9; /* 9 */
+ case 0x44: return KEY_NUMERIC_POUND; /* # */
+ case 0x84: return KEY_NUMERIC_STAR; /* * */
+ case 0x18: return KEY_ENTER; /* pickup */
+ case 0x28: return KEY_ESC; /* hangup */
+ case 0x48: return KEY_LEFT; /* IN */
+ case 0x88: return KEY_RIGHT; /* OUT */
+ default: return special_keymap(scancode);
+ }
+}
+
+/*
+ * Keymap for ATCom AU-100
+ * http://www.atcom.cn/En_products_AU100.html
+ * http://www.packetizer.com/products/au100/
+ * http://www.voip-info.org/wiki/view/AU-100
+ *
+ * Contributed by daniel@gimpelevich.san-francisco.ca.us
+ */
+static unsigned short keymap_atcom(int scancode)
+{
+ switch (scancode) { /* phone key: */
+ case 0x82: return KEY_NUMERIC_0; /* 0 */
+ case 0x11: return KEY_NUMERIC_1; /* 1 */
+ case 0x12: return KEY_NUMERIC_2; /* 2 */
+ case 0x14: return KEY_NUMERIC_3; /* 3 */
+ case 0x21: return KEY_NUMERIC_4; /* 4 */
+ case 0x22: return KEY_NUMERIC_5; /* 5 */
+ case 0x24: return KEY_NUMERIC_6; /* 6 */
+ case 0x41: return KEY_NUMERIC_7; /* 7 */
+ case 0x42: return KEY_NUMERIC_8; /* 8 */
+ case 0x44: return KEY_NUMERIC_9; /* 9 */
+ case 0x84: return KEY_NUMERIC_POUND; /* # */
+ case 0x81: return KEY_NUMERIC_STAR; /* * */
+ case 0x18: return KEY_ENTER; /* pickup */
+ case 0x28: return KEY_ESC; /* hangup */
+ case 0x48: return KEY_LEFT; /* left arrow */
+ case 0x88: return KEY_RIGHT; /* right arrow */
+ default: return special_keymap(scancode);
+ }
+}
+
+static unsigned short (*keymap)(int) = keymap_kip1000;
+
+/*
+ * Completes a request by converting the data into events for the
+ * input subsystem.
+ */
+static void report_key(struct cm109_dev *dev, int key)
+{
+ struct input_dev *idev = dev->idev;
+
+ if (dev->key_code >= 0) {
+ /* old key up */
+ input_report_key(idev, dev->key_code, 0);
+ }
+
+ dev->key_code = key;
+ if (key >= 0) {
+ /* new valid key */
+ input_report_key(idev, key, 1);
+ }
+
+ input_sync(idev);
+}
+
+/******************************************************************************
+ * CM109 usb communication interface
+ *****************************************************************************/
+
+static void cm109_submit_buzz_toggle(struct cm109_dev *dev)
+{
+ int error;
+
+ if (dev->buzzer_state)
+ dev->ctl_data->byte[HID_OR0] |= BUZZER_ON;
+ else
+ dev->ctl_data->byte[HID_OR0] &= ~BUZZER_ON;
+
+ error = usb_submit_urb(dev->urb_ctl, GFP_ATOMIC);
+ if (error)
+ err("%s: usb_submit_urb (urb_ctl) failed %d", __func__, error);
+}
+
+/*
+ * IRQ handler
+ */
+static void cm109_urb_irq_callback(struct urb *urb)
+{
+ struct cm109_dev *dev = urb->context;
+ const int status = urb->status;
+ int error;
+
+ dev_dbg(&urb->dev->dev, "### URB IRQ: [0x%02x 0x%02x 0x%02x 0x%02x] keybit=0x%02x\n",
+ dev->irq_data->byte[0],
+ dev->irq_data->byte[1],
+ dev->irq_data->byte[2],
+ dev->irq_data->byte[3],
+ dev->keybit);
+
+ if (status) {
+ if (status == -ESHUTDOWN)
+ return;
+ err("%s: urb status %d", __func__, status);
+ }
+
+ /* Special keys */
+ if (dev->irq_data->byte[HID_IR0] & 0x0f) {
+ const int code = (dev->irq_data->byte[HID_IR0] & 0x0f);
+ report_key(dev, dev->keymap[0xff + code]);
+ }
+
+ /* Scan key column */
+ if (dev->keybit == 0xf) {
+
+ /* Any changes ? */
+ if ((dev->gpi & 0xf0) == (dev->irq_data->byte[HID_IR1] & 0xf0))
+ goto out;
+
+ dev->gpi = dev->irq_data->byte[HID_IR1] & 0xf0;
+ dev->keybit = 0x1;
+ } else {
+ report_key(dev, dev->keymap[dev->irq_data->byte[HID_IR1]]);
+
+ dev->keybit <<= 1;
+ if (dev->keybit > 0x8)
+ dev->keybit = 0xf;
+ }
+
+ out:
+
+ spin_lock(&dev->ctl_submit_lock);
+
+ dev->irq_urb_pending = 0;
+
+ if (likely(!dev->shutdown)) {
+
+ if (dev->buzzer_state)
+ dev->ctl_data->byte[HID_OR0] |= BUZZER_ON;
+ else
+ dev->ctl_data->byte[HID_OR0] &= ~BUZZER_ON;
+
+ dev->ctl_data->byte[HID_OR1] = dev->keybit;
+ dev->ctl_data->byte[HID_OR2] = dev->keybit;
+
+ dev->buzzer_pending = 0;
+ dev->ctl_urb_pending = 1;
+
+ error = usb_submit_urb(dev->urb_ctl, GFP_ATOMIC);
+ if (error)
+ err("%s: usb_submit_urb (urb_ctl) failed %d",
+ __func__, error);
+ }
+
+ spin_unlock(&dev->ctl_submit_lock);
+}
+
+static void cm109_urb_ctl_callback(struct urb *urb)
+{
+ struct cm109_dev *dev = urb->context;
+ const int status = urb->status;
+ int error;
+
+ dev_dbg(&urb->dev->dev, "### URB CTL: [0x%02x 0x%02x 0x%02x 0x%02x]\n",
+ dev->ctl_data->byte[0],
+ dev->ctl_data->byte[1],
+ dev->ctl_data->byte[2],
+ dev->ctl_data->byte[3]);
+
+ if (status)
+ err("%s: urb status %d", __func__, status);
+
+ spin_lock(&dev->ctl_submit_lock);
+
+ dev->ctl_urb_pending = 0;
+
+ if (likely(!dev->shutdown)) {
+
+ if (dev->buzzer_pending) {
+ dev->buzzer_pending = 0;
+ dev->ctl_urb_pending = 1;
+ cm109_submit_buzz_toggle(dev);
+ } else if (likely(!dev->irq_urb_pending)) {
+ /* ask for key data */
+ dev->irq_urb_pending = 1;
+ error = usb_submit_urb(dev->urb_irq, GFP_ATOMIC);
+ if (error)
+ err("%s: usb_submit_urb (urb_irq) failed %d",
+ __func__, error);
+ }
+ }
+
+ spin_unlock(&dev->ctl_submit_lock);
+}
+
+static void cm109_toggle_buzzer_async(struct cm109_dev *dev)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->ctl_submit_lock, flags);
+
+ if (dev->ctl_urb_pending) {
+ /* URB completion will resubmit */
+ dev->buzzer_pending = 1;
+ } else {
+ dev->ctl_urb_pending = 1;
+ cm109_submit_buzz_toggle(dev);
+ }
+
+ spin_unlock_irqrestore(&dev->ctl_submit_lock, flags);
+}
+
+static void cm109_toggle_buzzer_sync(struct cm109_dev *dev, int on)
+{
+ int error;
+
+ if (on)
+ dev->ctl_data->byte[HID_OR0] |= BUZZER_ON;
+ else
+ dev->ctl_data->byte[HID_OR0] &= ~BUZZER_ON;
+
+ error = usb_control_msg(dev->udev,
+ usb_sndctrlpipe(dev->udev, 0),
+ dev->ctl_req->bRequest,
+ dev->ctl_req->bRequestType,
+ le16_to_cpu(dev->ctl_req->wValue),
+ le16_to_cpu(dev->ctl_req->wIndex),
+ dev->ctl_data,
+ USB_PKT_LEN, USB_CTRL_SET_TIMEOUT);
+ if (error && error != EINTR)
+ err("%s: usb_control_msg() failed %d", __func__, error);
+}
+
+static void cm109_stop_traffic(struct cm109_dev *dev)
+{
+ dev->shutdown = 1;
+ /*
+ * Make sure other CPUs see this
+ */
+ smp_wmb();
+
+ usb_kill_urb(dev->urb_ctl);
+ usb_kill_urb(dev->urb_irq);
+
+ cm109_toggle_buzzer_sync(dev, 0);
+
+ dev->shutdown = 0;
+ smp_wmb();
+}
+
+static void cm109_restore_state(struct cm109_dev *dev)
+{
+ if (dev->open) {
+ /*
+ * Restore buzzer state.
+ * This will also kick regular URB submission
+ */
+ cm109_toggle_buzzer_async(dev);
+ }
+}
+
+/******************************************************************************
+ * input event interface
+ *****************************************************************************/
+
+static int cm109_input_open(struct input_dev *idev)
+{
+ struct cm109_dev *dev = input_get_drvdata(idev);
+ int error;
+
+ error = usb_autopm_get_interface(dev->intf);
+ if (error < 0) {
+ err("%s - cannot autoresume, result %d",
+ __func__, error);
+ return error;
+ }
+
+ mutex_lock(&dev->pm_mutex);
+
+ dev->buzzer_state = 0;
+ dev->key_code = -1; /* no keys pressed */
+ dev->keybit = 0xf;
+
+ /* issue INIT */
+ dev->ctl_data->byte[HID_OR0] = HID_OR_GPO_BUZ_SPDIF;
+ dev->ctl_data->byte[HID_OR1] = dev->keybit;
+ dev->ctl_data->byte[HID_OR2] = dev->keybit;
+ dev->ctl_data->byte[HID_OR3] = 0x00;
+
+ error = usb_submit_urb(dev->urb_ctl, GFP_KERNEL);
+ if (error)
+ err("%s: usb_submit_urb (urb_ctl) failed %d", __func__, error);
+ else
+ dev->open = 1;
+
+ mutex_unlock(&dev->pm_mutex);
+
+ if (error)
+ usb_autopm_put_interface(dev->intf);
+
+ return error;
+}
+
+static void cm109_input_close(struct input_dev *idev)
+{
+ struct cm109_dev *dev = input_get_drvdata(idev);
+
+ mutex_lock(&dev->pm_mutex);
+
+ /*
+ * Once we are here event delivery is stopped so we
+ * don't need to worry about someone starting buzzer
+ * again
+ */
+ cm109_stop_traffic(dev);
+ dev->open = 0;
+
+ mutex_unlock(&dev->pm_mutex);
+
+ usb_autopm_put_interface(dev->intf);
+}
+
+static int cm109_input_ev(struct input_dev *idev, unsigned int type,
+ unsigned int code, int value)
+{
+ struct cm109_dev *dev = input_get_drvdata(idev);
+
+ dev_dbg(&dev->udev->dev,
+ "input_ev: type=%u code=%u value=%d\n", type, code, value);
+
+ if (type != EV_SND)
+ return -EINVAL;
+
+ switch (code) {
+ case SND_TONE:
+ case SND_BELL:
+ dev->buzzer_state = !!value;
+ if (!dev->resetting)
+ cm109_toggle_buzzer_async(dev);
+ return 0;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+
+/******************************************************************************
+ * Linux interface and usb initialisation
+ *****************************************************************************/
+
+struct driver_info {
+ char *name;
+};
+
+static const struct driver_info info_cm109 = {
+ .name = "CM109 USB driver",
+};
+
+enum {
+ VENDOR_ID = 0x0d8c, /* C-Media Electronics */
+ PRODUCT_ID_CM109 = 0x000e, /* CM109 defines range 0x0008 - 0x000f */
+};
+
+/* table of devices that work with this driver */
+static const struct usb_device_id cm109_usb_table[] = {
+ {
+ .match_flags = USB_DEVICE_ID_MATCH_DEVICE |
+ USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = VENDOR_ID,
+ .idProduct = PRODUCT_ID_CM109,
+ .bInterfaceClass = USB_CLASS_HID,
+ .bInterfaceSubClass = 0,
+ .bInterfaceProtocol = 0,
+ .driver_info = (kernel_ulong_t) &info_cm109
+ },
+ /* you can add more devices here with product ID 0x0008 - 0x000f */
+ { }
+};
+
+static void cm109_usb_cleanup(struct cm109_dev *dev)
+{
+ if (dev->ctl_req)
+ usb_buffer_free(dev->udev, sizeof(*(dev->ctl_req)),
+ dev->ctl_req, dev->ctl_req_dma);
+ if (dev->ctl_data)
+ usb_buffer_free(dev->udev, USB_PKT_LEN,
+ dev->ctl_data, dev->ctl_dma);
+ if (dev->irq_data)
+ usb_buffer_free(dev->udev, USB_PKT_LEN,
+ dev->irq_data, dev->irq_dma);
+
+ usb_free_urb(dev->urb_irq); /* parameter validation in core/urb */
+ usb_free_urb(dev->urb_ctl); /* parameter validation in core/urb */
+ kfree(dev);
+}
+
+static void cm109_usb_disconnect(struct usb_interface *interface)
+{
+ struct cm109_dev *dev = usb_get_intfdata(interface);
+
+ usb_set_intfdata(interface, NULL);
+ input_unregister_device(dev->idev);
+ cm109_usb_cleanup(dev);
+}
+
+static int cm109_usb_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ struct usb_device *udev = interface_to_usbdev(intf);
+ struct driver_info *nfo = (struct driver_info *)id->driver_info;
+ struct usb_host_interface *interface;
+ struct usb_endpoint_descriptor *endpoint;
+ struct cm109_dev *dev;
+ struct input_dev *input_dev = NULL;
+ int ret, pipe, i;
+ int error = -ENOMEM;
+
+ interface = intf->cur_altsetting;
+ endpoint = &interface->endpoint[0].desc;
+
+ if (!usb_endpoint_is_int_in(endpoint))
+ return -ENODEV;
+
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ if (!dev)
+ return -ENOMEM;
+
+ spin_lock_init(&dev->ctl_submit_lock);
+ mutex_init(&dev->pm_mutex);
+
+ dev->udev = udev;
+ dev->intf = intf;
+
+ dev->idev = input_dev = input_allocate_device();
+ if (!input_dev)
+ goto err_out;
+
+ /* allocate usb buffers */
+ dev->irq_data = usb_buffer_alloc(udev, USB_PKT_LEN,
+ GFP_KERNEL, &dev->irq_dma);
+ if (!dev->irq_data)
+ goto err_out;
+
+ dev->ctl_data = usb_buffer_alloc(udev, USB_PKT_LEN,
+ GFP_KERNEL, &dev->ctl_dma);
+ if (!dev->ctl_data)
+ goto err_out;
+
+ dev->ctl_req = usb_buffer_alloc(udev, sizeof(*(dev->ctl_req)),
+ GFP_KERNEL, &dev->ctl_req_dma);
+ if (!dev->ctl_req)
+ goto err_out;
+
+ /* allocate urb structures */
+ dev->urb_irq = usb_alloc_urb(0, GFP_KERNEL);
+ if (!dev->urb_irq)
+ goto err_out;
+
+ dev->urb_ctl = usb_alloc_urb(0, GFP_KERNEL);
+ if (!dev->urb_ctl)
+ goto err_out;
+
+ /* get a handle to the interrupt data pipe */
+ pipe = usb_rcvintpipe(udev, endpoint->bEndpointAddress);
+ ret = usb_maxpacket(udev, pipe, usb_pipeout(pipe));
+ if (ret != USB_PKT_LEN)
+ err("invalid payload size %d, expected %d", ret, USB_PKT_LEN);
+
+ /* initialise irq urb */
+ usb_fill_int_urb(dev->urb_irq, udev, pipe, dev->irq_data,
+ USB_PKT_LEN,
+ cm109_urb_irq_callback, dev, endpoint->bInterval);
+ dev->urb_irq->transfer_dma = dev->irq_dma;
+ dev->urb_irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+ dev->urb_irq->dev = udev;
+
+ /* initialise ctl urb */
+ dev->ctl_req->bRequestType = USB_TYPE_CLASS | USB_RECIP_INTERFACE |
+ USB_DIR_OUT;
+ dev->ctl_req->bRequest = USB_REQ_SET_CONFIGURATION;
+ dev->ctl_req->wValue = cpu_to_le16(0x200);
+ dev->ctl_req->wIndex = cpu_to_le16(interface->desc.bInterfaceNumber);
+ dev->ctl_req->wLength = cpu_to_le16(USB_PKT_LEN);
+
+ usb_fill_control_urb(dev->urb_ctl, udev, usb_sndctrlpipe(udev, 0),
+ (void *)dev->ctl_req, dev->ctl_data, USB_PKT_LEN,
+ cm109_urb_ctl_callback, dev);
+ dev->urb_ctl->setup_dma = dev->ctl_req_dma;
+ dev->urb_ctl->transfer_dma = dev->ctl_dma;
+ dev->urb_ctl->transfer_flags |= URB_NO_SETUP_DMA_MAP |
+ URB_NO_TRANSFER_DMA_MAP;
+ dev->urb_ctl->dev = udev;
+
+ /* find out the physical bus location */
+ usb_make_path(udev, dev->phys, sizeof(dev->phys));
+ strlcat(dev->phys, "/input0", sizeof(dev->phys));
+
+ /* register settings for the input device */
+ input_dev->name = nfo->name;
+ input_dev->phys = dev->phys;
+ usb_to_input_id(udev, &input_dev->id);
+ input_dev->dev.parent = &intf->dev;
+
+ input_set_drvdata(input_dev, dev);
+ input_dev->open = cm109_input_open;
+ input_dev->close = cm109_input_close;
+ input_dev->event = cm109_input_ev;
+
+ input_dev->keycode = dev->keymap;
+ input_dev->keycodesize = sizeof(unsigned char);
+ input_dev->keycodemax = ARRAY_SIZE(dev->keymap);
+
+ input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_SND);
+ input_dev->sndbit[0] = BIT_MASK(SND_BELL) | BIT_MASK(SND_TONE);
+
+ /* register available key events */
+ for (i = 0; i < KEYMAP_SIZE; i++) {
+ unsigned short k = keymap(i);
+ dev->keymap[i] = k;
+ __set_bit(k, input_dev->keybit);
+ }
+ __clear_bit(KEY_RESERVED, input_dev->keybit);
+
+ error = input_register_device(dev->idev);
+ if (error)
+ goto err_out;
+
+ usb_set_intfdata(intf, dev);
+
+ return 0;
+
+ err_out:
+ input_free_device(input_dev);
+ cm109_usb_cleanup(dev);
+ return error;
+}
+
+static int cm109_usb_suspend(struct usb_interface *intf, pm_message_t message)
+{
+ struct cm109_dev *dev = usb_get_intfdata(intf);
+
+ dev_info(&intf->dev, "cm109: usb_suspend (event=%d)\n", message.event);
+
+ mutex_lock(&dev->pm_mutex);
+ cm109_stop_traffic(dev);
+ mutex_unlock(&dev->pm_mutex);
+
+ return 0;
+}
+
+static int cm109_usb_resume(struct usb_interface *intf)
+{
+ struct cm109_dev *dev = usb_get_intfdata(intf);
+
+ dev_info(&intf->dev, "cm109: usb_resume\n");
+
+ mutex_lock(&dev->pm_mutex);
+ cm109_restore_state(dev);
+ mutex_unlock(&dev->pm_mutex);
+
+ return 0;
+}
+
+static int cm109_usb_pre_reset(struct usb_interface *intf)
+{
+ struct cm109_dev *dev = usb_get_intfdata(intf);
+
+ mutex_lock(&dev->pm_mutex);
+
+ /*
+ * Make sure input events don't try to toggle buzzer
+ * while we are resetting
+ */
+ dev->resetting = 1;
+ smp_wmb();
+
+ cm109_stop_traffic(dev);
+
+ return 0;
+}
+
+static int cm109_usb_post_reset(struct usb_interface *intf)
+{
+ struct cm109_dev *dev = usb_get_intfdata(intf);
+
+ dev->resetting = 0;
+ smp_wmb();
+
+ cm109_restore_state(dev);
+
+ mutex_unlock(&dev->pm_mutex);
+
+ return 0;
+}
+
+static struct usb_driver cm109_driver = {
+ .name = "cm109",
+ .probe = cm109_usb_probe,
+ .disconnect = cm109_usb_disconnect,
+ .suspend = cm109_usb_suspend,
+ .resume = cm109_usb_resume,
+ .reset_resume = cm109_usb_resume,
+ .pre_reset = cm109_usb_pre_reset,
+ .post_reset = cm109_usb_post_reset,
+ .id_table = cm109_usb_table,
+ .supports_autosuspend = 1,
+};
+
+static int __init cm109_select_keymap(void)
+{
+ /* Load the phone keymap */
+ if (!strcasecmp(phone, "kip1000")) {
+ keymap = keymap_kip1000;
+ printk(KERN_INFO KBUILD_MODNAME ": "
+ "Keymap for Komunikate KIP1000 phone loaded\n");
+ } else if (!strcasecmp(phone, "gtalk")) {
+ keymap = keymap_gtalk;
+ printk(KERN_INFO KBUILD_MODNAME ": "
+ "Keymap for Genius G-talk phone loaded\n");
+ } else if (!strcasecmp(phone, "usbph01")) {
+ keymap = keymap_usbph01;
+ printk(KERN_INFO KBUILD_MODNAME ": "
+ "Keymap for Allied-Telesis Corega USBPH01 phone loaded\n");
+ } else if (!strcasecmp(phone, "atcom")) {
+ keymap = keymap_atcom;
+ printk(KERN_INFO KBUILD_MODNAME ": "
+ "Keymap for ATCom AU-100 phone loaded\n");
+ } else {
+ printk(KERN_ERR KBUILD_MODNAME ": "
+ "Unsupported phone: %s\n", phone);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int __init cm109_init(void)
+{
+ int err;
+
+ err = cm109_select_keymap();
+ if (err)
+ return err;
+
+ err = usb_register(&cm109_driver);
+ if (err)
+ return err;
+
+ printk(KERN_INFO KBUILD_MODNAME ": "
+ DRIVER_DESC ": " DRIVER_VERSION " (C) " DRIVER_AUTHOR "\n");
+
+ return 0;
+}
+
+static void __exit cm109_exit(void)
+{
+ usb_deregister(&cm109_driver);
+}
+
+module_init(cm109_init);
+module_exit(cm109_exit);
+
+MODULE_DEVICE_TABLE(usb, cm109_usb_table);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/misc/cobalt_btns.c b/drivers/input/misc/cobalt_btns.c
new file mode 100644
index 0000000..2adf9cb
--- /dev/null
+++ b/drivers/input/misc/cobalt_btns.c
@@ -0,0 +1,177 @@
+/*
+ * Cobalt button interface driver.
+ *
+ * Copyright (C) 2007-2008 Yoichi Yuasa <yoichi_yuasa@tripeaks.co.jp>
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#include <linux/init.h>
+#include <linux/input-polldev.h>
+#include <linux/ioport.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#define BUTTONS_POLL_INTERVAL 30 /* msec */
+#define BUTTONS_COUNT_THRESHOLD 3
+#define BUTTONS_STATUS_MASK 0xfe000000
+
+static const unsigned short cobalt_map[] = {
+ KEY_RESERVED,
+ KEY_RESTART,
+ KEY_LEFT,
+ KEY_UP,
+ KEY_DOWN,
+ KEY_RIGHT,
+ KEY_ENTER,
+ KEY_SELECT
+};
+
+struct buttons_dev {
+ struct input_polled_dev *poll_dev;
+ unsigned short keymap[ARRAY_SIZE(cobalt_map)];
+ int count[ARRAY_SIZE(cobalt_map)];
+ void __iomem *reg;
+};
+
+static void handle_buttons(struct input_polled_dev *dev)
+{
+ struct buttons_dev *bdev = dev->private;
+ struct input_dev *input = dev->input;
+ uint32_t status;
+ int i;
+
+ status = ~readl(bdev->reg) >> 24;
+
+ for (i = 0; i < ARRAY_SIZE(bdev->keymap); i++) {
+ if (status & (1U << i)) {
+ if (++bdev->count[i] == BUTTONS_COUNT_THRESHOLD) {
+ input_event(input, EV_MSC, MSC_SCAN, i);
+ input_report_key(input, bdev->keymap[i], 1);
+ input_sync(input);
+ }
+ } else {
+ if (bdev->count[i] >= BUTTONS_COUNT_THRESHOLD) {
+ input_event(input, EV_MSC, MSC_SCAN, i);
+ input_report_key(input, bdev->keymap[i], 0);
+ input_sync(input);
+ }
+ bdev->count[i] = 0;
+ }
+ }
+}
+
+static int __devinit cobalt_buttons_probe(struct platform_device *pdev)
+{
+ struct buttons_dev *bdev;
+ struct input_polled_dev *poll_dev;
+ struct input_dev *input;
+ struct resource *res;
+ int error, i;
+
+ bdev = kzalloc(sizeof(struct buttons_dev), GFP_KERNEL);
+ poll_dev = input_allocate_polled_device();
+ if (!bdev || !poll_dev) {
+ error = -ENOMEM;
+ goto err_free_mem;
+ }
+
+ memcpy(bdev->keymap, cobalt_map, sizeof(bdev->keymap));
+
+ poll_dev->private = bdev;
+ poll_dev->poll = handle_buttons;
+ poll_dev->poll_interval = BUTTONS_POLL_INTERVAL;
+
+ input = poll_dev->input;
+ input->name = "Cobalt buttons";
+ input->phys = "cobalt/input0";
+ input->id.bustype = BUS_HOST;
+ input->dev.parent = &pdev->dev;
+
+ input->keycode = bdev->keymap;
+ input->keycodemax = ARRAY_SIZE(bdev->keymap);
+ input->keycodesize = sizeof(unsigned short);
+
+ input_set_capability(input, EV_MSC, MSC_SCAN);
+ __set_bit(EV_KEY, input->evbit);
+ for (i = 0; i < ARRAY_SIZE(cobalt_map); i++)
+ __set_bit(bdev->keymap[i], input->keybit);
+ __clear_bit(KEY_RESERVED, input->keybit);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ error = -EBUSY;
+ goto err_free_mem;
+ }
+
+ bdev->poll_dev = poll_dev;
+ bdev->reg = ioremap(res->start, res->end - res->start + 1);
+ dev_set_drvdata(&pdev->dev, bdev);
+
+ error = input_register_polled_device(poll_dev);
+ if (error)
+ goto err_iounmap;
+
+ return 0;
+
+ err_iounmap:
+ iounmap(bdev->reg);
+ err_free_mem:
+ input_free_polled_device(poll_dev);
+ kfree(bdev);
+ dev_set_drvdata(&pdev->dev, NULL);
+ return error;
+}
+
+static int __devexit cobalt_buttons_remove(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct buttons_dev *bdev = dev_get_drvdata(dev);
+
+ input_unregister_polled_device(bdev->poll_dev);
+ input_free_polled_device(bdev->poll_dev);
+ iounmap(bdev->reg);
+ kfree(bdev);
+ dev_set_drvdata(dev, NULL);
+
+ return 0;
+}
+
+MODULE_AUTHOR("Yoichi Yuasa <yoichi_yuasa@tripeaks.co.jp>");
+MODULE_DESCRIPTION("Cobalt button interface driver");
+MODULE_LICENSE("GPL");
+/* work with hotplug and coldplug */
+MODULE_ALIAS("platform:Cobalt buttons");
+
+static struct platform_driver cobalt_buttons_driver = {
+ .probe = cobalt_buttons_probe,
+ .remove = __devexit_p(cobalt_buttons_remove),
+ .driver = {
+ .name = "Cobalt buttons",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init cobalt_buttons_init(void)
+{
+ return platform_driver_register(&cobalt_buttons_driver);
+}
+
+static void __exit cobalt_buttons_exit(void)
+{
+ platform_driver_unregister(&cobalt_buttons_driver);
+}
+
+module_init(cobalt_buttons_init);
+module_exit(cobalt_buttons_exit);
diff --git a/drivers/input/misc/hp_sdc_rtc.c b/drivers/input/misc/hp_sdc_rtc.c
new file mode 100644
index 0000000..216a559
--- /dev/null
+++ b/drivers/input/misc/hp_sdc_rtc.c
@@ -0,0 +1,714 @@
+/*
+ * HP i8042 SDC + MSM-58321 BBRTC driver.
+ *
+ * Copyright (c) 2001 Brian S. Julin
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL").
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ *
+ * References:
+ * System Device Controller Microprocessor Firmware Theory of Operation
+ * for Part Number 1820-4784 Revision B. Dwg No. A-1820-4784-2
+ * efirtc.c by Stephane Eranian/Hewlett Packard
+ *
+ */
+
+#include <linux/hp_sdc.h>
+#include <linux/errno.h>
+#include <linux/smp_lock.h>
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/time.h>
+#include <linux/miscdevice.h>
+#include <linux/proc_fs.h>
+#include <linux/poll.h>
+#include <linux/rtc.h>
+#include <linux/semaphore.h>
+
+MODULE_AUTHOR("Brian S. Julin <bri@calyx.com>");
+MODULE_DESCRIPTION("HP i8042 SDC + MSM-58321 RTC Driver");
+MODULE_LICENSE("Dual BSD/GPL");
+
+#define RTC_VERSION "1.10d"
+
+static unsigned long epoch = 2000;
+
+static struct semaphore i8042tregs;
+
+static hp_sdc_irqhook hp_sdc_rtc_isr;
+
+static struct fasync_struct *hp_sdc_rtc_async_queue;
+
+static DECLARE_WAIT_QUEUE_HEAD(hp_sdc_rtc_wait);
+
+static ssize_t hp_sdc_rtc_read(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos);
+
+static int hp_sdc_rtc_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg);
+
+static unsigned int hp_sdc_rtc_poll(struct file *file, poll_table *wait);
+
+static int hp_sdc_rtc_open(struct inode *inode, struct file *file);
+static int hp_sdc_rtc_fasync (int fd, struct file *filp, int on);
+
+static int hp_sdc_rtc_read_proc(char *page, char **start, off_t off,
+ int count, int *eof, void *data);
+
+static void hp_sdc_rtc_isr (int irq, void *dev_id,
+ uint8_t status, uint8_t data)
+{
+ return;
+}
+
+static int hp_sdc_rtc_do_read_bbrtc (struct rtc_time *rtctm)
+{
+ struct semaphore tsem;
+ hp_sdc_transaction t;
+ uint8_t tseq[91];
+ int i;
+
+ i = 0;
+ while (i < 91) {
+ tseq[i++] = HP_SDC_ACT_DATAREG |
+ HP_SDC_ACT_POSTCMD | HP_SDC_ACT_DATAIN;
+ tseq[i++] = 0x01; /* write i8042[0x70] */
+ tseq[i] = i / 7; /* BBRTC reg address */
+ i++;
+ tseq[i++] = HP_SDC_CMD_DO_RTCR; /* Trigger command */
+ tseq[i++] = 2; /* expect 1 stat/dat pair back. */
+ i++; i++; /* buffer for stat/dat pair */
+ }
+ tseq[84] |= HP_SDC_ACT_SEMAPHORE;
+ t.endidx = 91;
+ t.seq = tseq;
+ t.act.semaphore = &tsem;
+ init_MUTEX_LOCKED(&tsem);
+
+ if (hp_sdc_enqueue_transaction(&t)) return -1;
+
+ down_interruptible(&tsem); /* Put ourselves to sleep for results. */
+
+ /* Check for nonpresence of BBRTC */
+ if (!((tseq[83] | tseq[90] | tseq[69] | tseq[76] |
+ tseq[55] | tseq[62] | tseq[34] | tseq[41] |
+ tseq[20] | tseq[27] | tseq[6] | tseq[13]) & 0x0f))
+ return -1;
+
+ memset(rtctm, 0, sizeof(struct rtc_time));
+ rtctm->tm_year = (tseq[83] & 0x0f) + (tseq[90] & 0x0f) * 10;
+ rtctm->tm_mon = (tseq[69] & 0x0f) + (tseq[76] & 0x0f) * 10;
+ rtctm->tm_mday = (tseq[55] & 0x0f) + (tseq[62] & 0x0f) * 10;
+ rtctm->tm_wday = (tseq[48] & 0x0f);
+ rtctm->tm_hour = (tseq[34] & 0x0f) + (tseq[41] & 0x0f) * 10;
+ rtctm->tm_min = (tseq[20] & 0x0f) + (tseq[27] & 0x0f) * 10;
+ rtctm->tm_sec = (tseq[6] & 0x0f) + (tseq[13] & 0x0f) * 10;
+
+ return 0;
+}
+
+static int hp_sdc_rtc_read_bbrtc (struct rtc_time *rtctm)
+{
+ struct rtc_time tm, tm_last;
+ int i = 0;
+
+ /* MSM-58321 has no read latch, so must read twice and compare. */
+
+ if (hp_sdc_rtc_do_read_bbrtc(&tm_last)) return -1;
+ if (hp_sdc_rtc_do_read_bbrtc(&tm)) return -1;
+
+ while (memcmp(&tm, &tm_last, sizeof(struct rtc_time))) {
+ if (i++ > 4) return -1;
+ memcpy(&tm_last, &tm, sizeof(struct rtc_time));
+ if (hp_sdc_rtc_do_read_bbrtc(&tm)) return -1;
+ }
+
+ memcpy(rtctm, &tm, sizeof(struct rtc_time));
+
+ return 0;
+}
+
+
+static int64_t hp_sdc_rtc_read_i8042timer (uint8_t loadcmd, int numreg)
+{
+ hp_sdc_transaction t;
+ uint8_t tseq[26] = {
+ HP_SDC_ACT_PRECMD | HP_SDC_ACT_POSTCMD | HP_SDC_ACT_DATAIN,
+ 0,
+ HP_SDC_CMD_READ_T1, 2, 0, 0,
+ HP_SDC_ACT_POSTCMD | HP_SDC_ACT_DATAIN,
+ HP_SDC_CMD_READ_T2, 2, 0, 0,
+ HP_SDC_ACT_POSTCMD | HP_SDC_ACT_DATAIN,
+ HP_SDC_CMD_READ_T3, 2, 0, 0,
+ HP_SDC_ACT_POSTCMD | HP_SDC_ACT_DATAIN,
+ HP_SDC_CMD_READ_T4, 2, 0, 0,
+ HP_SDC_ACT_POSTCMD | HP_SDC_ACT_DATAIN,
+ HP_SDC_CMD_READ_T5, 2, 0, 0
+ };
+
+ t.endidx = numreg * 5;
+
+ tseq[1] = loadcmd;
+ tseq[t.endidx - 4] |= HP_SDC_ACT_SEMAPHORE; /* numreg assumed > 1 */
+
+ t.seq = tseq;
+ t.act.semaphore = &i8042tregs;
+
+ down_interruptible(&i8042tregs); /* Sleep if output regs in use. */
+
+ if (hp_sdc_enqueue_transaction(&t)) return -1;
+
+ down_interruptible(&i8042tregs); /* Sleep until results come back. */
+ up(&i8042tregs);
+
+ return (tseq[5] |
+ ((uint64_t)(tseq[10]) << 8) | ((uint64_t)(tseq[15]) << 16) |
+ ((uint64_t)(tseq[20]) << 24) | ((uint64_t)(tseq[25]) << 32));
+}
+
+
+/* Read the i8042 real-time clock */
+static inline int hp_sdc_rtc_read_rt(struct timeval *res) {
+ int64_t raw;
+ uint32_t tenms;
+ unsigned int days;
+
+ raw = hp_sdc_rtc_read_i8042timer(HP_SDC_CMD_LOAD_RT, 5);
+ if (raw < 0) return -1;
+
+ tenms = (uint32_t)raw & 0xffffff;
+ days = (unsigned int)(raw >> 24) & 0xffff;
+
+ res->tv_usec = (suseconds_t)(tenms % 100) * 10000;
+ res->tv_sec = (time_t)(tenms / 100) + days * 86400;
+
+ return 0;
+}
+
+
+/* Read the i8042 fast handshake timer */
+static inline int hp_sdc_rtc_read_fhs(struct timeval *res) {
+ uint64_t raw;
+ unsigned int tenms;
+
+ raw = hp_sdc_rtc_read_i8042timer(HP_SDC_CMD_LOAD_FHS, 2);
+ if (raw < 0) return -1;
+
+ tenms = (unsigned int)raw & 0xffff;
+
+ res->tv_usec = (suseconds_t)(tenms % 100) * 10000;
+ res->tv_sec = (time_t)(tenms / 100);
+
+ return 0;
+}
+
+
+/* Read the i8042 match timer (a.k.a. alarm) */
+static inline int hp_sdc_rtc_read_mt(struct timeval *res) {
+ int64_t raw;
+ uint32_t tenms;
+
+ raw = hp_sdc_rtc_read_i8042timer(HP_SDC_CMD_LOAD_MT, 3);
+ if (raw < 0) return -1;
+
+ tenms = (uint32_t)raw & 0xffffff;
+
+ res->tv_usec = (suseconds_t)(tenms % 100) * 10000;
+ res->tv_sec = (time_t)(tenms / 100);
+
+ return 0;
+}
+
+
+/* Read the i8042 delay timer */
+static inline int hp_sdc_rtc_read_dt(struct timeval *res) {
+ int64_t raw;
+ uint32_t tenms;
+
+ raw = hp_sdc_rtc_read_i8042timer(HP_SDC_CMD_LOAD_DT, 3);
+ if (raw < 0) return -1;
+
+ tenms = (uint32_t)raw & 0xffffff;
+
+ res->tv_usec = (suseconds_t)(tenms % 100) * 10000;
+ res->tv_sec = (time_t)(tenms / 100);
+
+ return 0;
+}
+
+
+/* Read the i8042 cycle timer (a.k.a. periodic) */
+static inline int hp_sdc_rtc_read_ct(struct timeval *res) {
+ int64_t raw;
+ uint32_t tenms;
+
+ raw = hp_sdc_rtc_read_i8042timer(HP_SDC_CMD_LOAD_CT, 3);
+ if (raw < 0) return -1;
+
+ tenms = (uint32_t)raw & 0xffffff;
+
+ res->tv_usec = (suseconds_t)(tenms % 100) * 10000;
+ res->tv_sec = (time_t)(tenms / 100);
+
+ return 0;
+}
+
+
+/* Set the i8042 real-time clock */
+static int hp_sdc_rtc_set_rt (struct timeval *setto)
+{
+ uint32_t tenms;
+ unsigned int days;
+ hp_sdc_transaction t;
+ uint8_t tseq[11] = {
+ HP_SDC_ACT_PRECMD | HP_SDC_ACT_DATAOUT,
+ HP_SDC_CMD_SET_RTMS, 3, 0, 0, 0,
+ HP_SDC_ACT_PRECMD | HP_SDC_ACT_DATAOUT,
+ HP_SDC_CMD_SET_RTD, 2, 0, 0
+ };
+
+ t.endidx = 10;
+
+ if (0xffff < setto->tv_sec / 86400) return -1;
+ days = setto->tv_sec / 86400;
+ if (0xffff < setto->tv_usec / 1000000 / 86400) return -1;
+ days += ((setto->tv_sec % 86400) + setto->tv_usec / 1000000) / 86400;
+ if (days > 0xffff) return -1;
+
+ if (0xffffff < setto->tv_sec) return -1;
+ tenms = setto->tv_sec * 100;
+ if (0xffffff < setto->tv_usec / 10000) return -1;
+ tenms += setto->tv_usec / 10000;
+ if (tenms > 0xffffff) return -1;
+
+ tseq[3] = (uint8_t)(tenms & 0xff);
+ tseq[4] = (uint8_t)((tenms >> 8) & 0xff);
+ tseq[5] = (uint8_t)((tenms >> 16) & 0xff);
+
+ tseq[9] = (uint8_t)(days & 0xff);
+ tseq[10] = (uint8_t)((days >> 8) & 0xff);
+
+ t.seq = tseq;
+
+ if (hp_sdc_enqueue_transaction(&t)) return -1;
+ return 0;
+}
+
+/* Set the i8042 fast handshake timer */
+static int hp_sdc_rtc_set_fhs (struct timeval *setto)
+{
+ uint32_t tenms;
+ hp_sdc_transaction t;
+ uint8_t tseq[5] = {
+ HP_SDC_ACT_PRECMD | HP_SDC_ACT_DATAOUT,
+ HP_SDC_CMD_SET_FHS, 2, 0, 0
+ };
+
+ t.endidx = 4;
+
+ if (0xffff < setto->tv_sec) return -1;
+ tenms = setto->tv_sec * 100;
+ if (0xffff < setto->tv_usec / 10000) return -1;
+ tenms += setto->tv_usec / 10000;
+ if (tenms > 0xffff) return -1;
+
+ tseq[3] = (uint8_t)(tenms & 0xff);
+ tseq[4] = (uint8_t)((tenms >> 8) & 0xff);
+
+ t.seq = tseq;
+
+ if (hp_sdc_enqueue_transaction(&t)) return -1;
+ return 0;
+}
+
+
+/* Set the i8042 match timer (a.k.a. alarm) */
+#define hp_sdc_rtc_set_mt (setto) \
+ hp_sdc_rtc_set_i8042timer(setto, HP_SDC_CMD_SET_MT)
+
+/* Set the i8042 delay timer */
+#define hp_sdc_rtc_set_dt (setto) \
+ hp_sdc_rtc_set_i8042timer(setto, HP_SDC_CMD_SET_DT)
+
+/* Set the i8042 cycle timer (a.k.a. periodic) */
+#define hp_sdc_rtc_set_ct (setto) \
+ hp_sdc_rtc_set_i8042timer(setto, HP_SDC_CMD_SET_CT)
+
+/* Set one of the i8042 3-byte wide timers */
+static int hp_sdc_rtc_set_i8042timer (struct timeval *setto, uint8_t setcmd)
+{
+ uint32_t tenms;
+ hp_sdc_transaction t;
+ uint8_t tseq[6] = {
+ HP_SDC_ACT_PRECMD | HP_SDC_ACT_DATAOUT,
+ 0, 3, 0, 0, 0
+ };
+
+ t.endidx = 6;
+
+ if (0xffffff < setto->tv_sec) return -1;
+ tenms = setto->tv_sec * 100;
+ if (0xffffff < setto->tv_usec / 10000) return -1;
+ tenms += setto->tv_usec / 10000;
+ if (tenms > 0xffffff) return -1;
+
+ tseq[1] = setcmd;
+ tseq[3] = (uint8_t)(tenms & 0xff);
+ tseq[4] = (uint8_t)((tenms >> 8) & 0xff);
+ tseq[5] = (uint8_t)((tenms >> 16) & 0xff);
+
+ t.seq = tseq;
+
+ if (hp_sdc_enqueue_transaction(&t)) {
+ return -1;
+ }
+ return 0;
+}
+
+static ssize_t hp_sdc_rtc_read(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos) {
+ ssize_t retval;
+
+ if (count < sizeof(unsigned long))
+ return -EINVAL;
+
+ retval = put_user(68, (unsigned long __user *)buf);
+ return retval;
+}
+
+static unsigned int hp_sdc_rtc_poll(struct file *file, poll_table *wait)
+{
+ unsigned long l;
+
+ l = 0;
+ if (l != 0)
+ return POLLIN | POLLRDNORM;
+ return 0;
+}
+
+static int hp_sdc_rtc_open(struct inode *inode, struct file *file)
+{
+ cycle_kernel_lock();
+ return 0;
+}
+
+static int hp_sdc_rtc_fasync (int fd, struct file *filp, int on)
+{
+ return fasync_helper (fd, filp, on, &hp_sdc_rtc_async_queue);
+}
+
+static int hp_sdc_rtc_proc_output (char *buf)
+{
+#define YN(bit) ("no")
+#define NY(bit) ("yes")
+ char *p;
+ struct rtc_time tm;
+ struct timeval tv;
+
+ memset(&tm, 0, sizeof(struct rtc_time));
+
+ p = buf;
+
+ if (hp_sdc_rtc_read_bbrtc(&tm)) {
+ p += sprintf(p, "BBRTC\t\t: READ FAILED!\n");
+ } else {
+ p += sprintf(p,
+ "rtc_time\t: %02d:%02d:%02d\n"
+ "rtc_date\t: %04d-%02d-%02d\n"
+ "rtc_epoch\t: %04lu\n",
+ tm.tm_hour, tm.tm_min, tm.tm_sec,
+ tm.tm_year + 1900, tm.tm_mon + 1,
+ tm.tm_mday, epoch);
+ }
+
+ if (hp_sdc_rtc_read_rt(&tv)) {
+ p += sprintf(p, "i8042 rtc\t: READ FAILED!\n");
+ } else {
+ p += sprintf(p, "i8042 rtc\t: %ld.%02d seconds\n",
+ tv.tv_sec, (int)tv.tv_usec/1000);
+ }
+
+ if (hp_sdc_rtc_read_fhs(&tv)) {
+ p += sprintf(p, "handshake\t: READ FAILED!\n");
+ } else {
+ p += sprintf(p, "handshake\t: %ld.%02d seconds\n",
+ tv.tv_sec, (int)tv.tv_usec/1000);
+ }
+
+ if (hp_sdc_rtc_read_mt(&tv)) {
+ p += sprintf(p, "alarm\t\t: READ FAILED!\n");
+ } else {
+ p += sprintf(p, "alarm\t\t: %ld.%02d seconds\n",
+ tv.tv_sec, (int)tv.tv_usec/1000);
+ }
+
+ if (hp_sdc_rtc_read_dt(&tv)) {
+ p += sprintf(p, "delay\t\t: READ FAILED!\n");
+ } else {
+ p += sprintf(p, "delay\t\t: %ld.%02d seconds\n",
+ tv.tv_sec, (int)tv.tv_usec/1000);
+ }
+
+ if (hp_sdc_rtc_read_ct(&tv)) {
+ p += sprintf(p, "periodic\t: READ FAILED!\n");
+ } else {
+ p += sprintf(p, "periodic\t: %ld.%02d seconds\n",
+ tv.tv_sec, (int)tv.tv_usec/1000);
+ }
+
+ p += sprintf(p,
+ "DST_enable\t: %s\n"
+ "BCD\t\t: %s\n"
+ "24hr\t\t: %s\n"
+ "square_wave\t: %s\n"
+ "alarm_IRQ\t: %s\n"
+ "update_IRQ\t: %s\n"
+ "periodic_IRQ\t: %s\n"
+ "periodic_freq\t: %ld\n"
+ "batt_status\t: %s\n",
+ YN(RTC_DST_EN),
+ NY(RTC_DM_BINARY),
+ YN(RTC_24H),
+ YN(RTC_SQWE),
+ YN(RTC_AIE),
+ YN(RTC_UIE),
+ YN(RTC_PIE),
+ 1UL,
+ 1 ? "okay" : "dead");
+
+ return p - buf;
+#undef YN
+#undef NY
+}
+
+static int hp_sdc_rtc_read_proc(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ int len = hp_sdc_rtc_proc_output (page);
+ if (len <= off+count) *eof = 1;
+ *start = page + off;
+ len -= off;
+ if (len>count) len = count;
+ if (len<0) len = 0;
+ return len;
+}
+
+static int hp_sdc_rtc_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+#if 1
+ return -EINVAL;
+#else
+
+ struct rtc_time wtime;
+ struct timeval ttime;
+ int use_wtime = 0;
+
+ /* This needs major work. */
+
+ switch (cmd) {
+
+ case RTC_AIE_OFF: /* Mask alarm int. enab. bit */
+ case RTC_AIE_ON: /* Allow alarm interrupts. */
+ case RTC_PIE_OFF: /* Mask periodic int. enab. bit */
+ case RTC_PIE_ON: /* Allow periodic ints */
+ case RTC_UIE_ON: /* Allow ints for RTC updates. */
+ case RTC_UIE_OFF: /* Allow ints for RTC updates. */
+ {
+ /* We cannot mask individual user timers and we
+ cannot tell them apart when they occur, so it
+ would be disingenuous to succeed these IOCTLs */
+ return -EINVAL;
+ }
+ case RTC_ALM_READ: /* Read the present alarm time */
+ {
+ if (hp_sdc_rtc_read_mt(&ttime)) return -EFAULT;
+ if (hp_sdc_rtc_read_bbrtc(&wtime)) return -EFAULT;
+
+ wtime.tm_hour = ttime.tv_sec / 3600; ttime.tv_sec %= 3600;
+ wtime.tm_min = ttime.tv_sec / 60; ttime.tv_sec %= 60;
+ wtime.tm_sec = ttime.tv_sec;
+
+ break;
+ }
+ case RTC_IRQP_READ: /* Read the periodic IRQ rate. */
+ {
+ return put_user(hp_sdc_rtc_freq, (unsigned long *)arg);
+ }
+ case RTC_IRQP_SET: /* Set periodic IRQ rate. */
+ {
+ /*
+ * The max we can do is 100Hz.
+ */
+
+ if ((arg < 1) || (arg > 100)) return -EINVAL;
+ ttime.tv_sec = 0;
+ ttime.tv_usec = 1000000 / arg;
+ if (hp_sdc_rtc_set_ct(&ttime)) return -EFAULT;
+ hp_sdc_rtc_freq = arg;
+ return 0;
+ }
+ case RTC_ALM_SET: /* Store a time into the alarm */
+ {
+ /*
+ * This expects a struct hp_sdc_rtc_time. Writing 0xff means
+ * "don't care" or "match all" for PC timers. The HP SDC
+ * does not support that perk, but it could be emulated fairly
+ * easily. Only the tm_hour, tm_min and tm_sec are used.
+ * We could do it with 10ms accuracy with the HP SDC, if the
+ * rtc interface left us a way to do that.
+ */
+ struct hp_sdc_rtc_time alm_tm;
+
+ if (copy_from_user(&alm_tm, (struct hp_sdc_rtc_time*)arg,
+ sizeof(struct hp_sdc_rtc_time)))
+ return -EFAULT;
+
+ if (alm_tm.tm_hour > 23) return -EINVAL;
+ if (alm_tm.tm_min > 59) return -EINVAL;
+ if (alm_tm.tm_sec > 59) return -EINVAL;
+
+ ttime.sec = alm_tm.tm_hour * 3600 +
+ alm_tm.tm_min * 60 + alm_tm.tm_sec;
+ ttime.usec = 0;
+ if (hp_sdc_rtc_set_mt(&ttime)) return -EFAULT;
+ return 0;
+ }
+ case RTC_RD_TIME: /* Read the time/date from RTC */
+ {
+ if (hp_sdc_rtc_read_bbrtc(&wtime)) return -EFAULT;
+ break;
+ }
+ case RTC_SET_TIME: /* Set the RTC */
+ {
+ struct rtc_time hp_sdc_rtc_tm;
+ unsigned char mon, day, hrs, min, sec, leap_yr;
+ unsigned int yrs;
+
+ if (!capable(CAP_SYS_TIME))
+ return -EACCES;
+ if (copy_from_user(&hp_sdc_rtc_tm, (struct rtc_time *)arg,
+ sizeof(struct rtc_time)))
+ return -EFAULT;
+
+ yrs = hp_sdc_rtc_tm.tm_year + 1900;
+ mon = hp_sdc_rtc_tm.tm_mon + 1; /* tm_mon starts at zero */
+ day = hp_sdc_rtc_tm.tm_mday;
+ hrs = hp_sdc_rtc_tm.tm_hour;
+ min = hp_sdc_rtc_tm.tm_min;
+ sec = hp_sdc_rtc_tm.tm_sec;
+
+ if (yrs < 1970)
+ return -EINVAL;
+
+ leap_yr = ((!(yrs % 4) && (yrs % 100)) || !(yrs % 400));
+
+ if ((mon > 12) || (day == 0))
+ return -EINVAL;
+ if (day > (days_in_mo[mon] + ((mon == 2) && leap_yr)))
+ return -EINVAL;
+ if ((hrs >= 24) || (min >= 60) || (sec >= 60))
+ return -EINVAL;
+
+ if ((yrs -= eH) > 255) /* They are unsigned */
+ return -EINVAL;
+
+
+ return 0;
+ }
+ case RTC_EPOCH_READ: /* Read the epoch. */
+ {
+ return put_user (epoch, (unsigned long *)arg);
+ }
+ case RTC_EPOCH_SET: /* Set the epoch. */
+ {
+ /*
+ * There were no RTC clocks before 1900.
+ */
+ if (arg < 1900)
+ return -EINVAL;
+ if (!capable(CAP_SYS_TIME))
+ return -EACCES;
+
+ epoch = arg;
+ return 0;
+ }
+ default:
+ return -EINVAL;
+ }
+ return copy_to_user((void *)arg, &wtime, sizeof wtime) ? -EFAULT : 0;
+#endif
+}
+
+static const struct file_operations hp_sdc_rtc_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .read = hp_sdc_rtc_read,
+ .poll = hp_sdc_rtc_poll,
+ .ioctl = hp_sdc_rtc_ioctl,
+ .open = hp_sdc_rtc_open,
+ .fasync = hp_sdc_rtc_fasync,
+};
+
+static struct miscdevice hp_sdc_rtc_dev = {
+ .minor = RTC_MINOR,
+ .name = "rtc_HIL",
+ .fops = &hp_sdc_rtc_fops
+};
+
+static int __init hp_sdc_rtc_init(void)
+{
+ int ret;
+
+#ifdef __mc68000__
+ if (!MACH_IS_HP300)
+ return -ENODEV;
+#endif
+
+ init_MUTEX(&i8042tregs);
+
+ if ((ret = hp_sdc_request_timer_irq(&hp_sdc_rtc_isr)))
+ return ret;
+ if (misc_register(&hp_sdc_rtc_dev) != 0)
+ printk(KERN_INFO "Could not register misc. dev for i8042 rtc\n");
+
+ create_proc_read_entry ("driver/rtc", 0, NULL,
+ hp_sdc_rtc_read_proc, NULL);
+
+ printk(KERN_INFO "HP i8042 SDC + MSM-58321 RTC support loaded "
+ "(RTC v " RTC_VERSION ")\n");
+
+ return 0;
+}
+
+static void __exit hp_sdc_rtc_exit(void)
+{
+ remove_proc_entry ("driver/rtc", NULL);
+ misc_deregister(&hp_sdc_rtc_dev);
+ hp_sdc_release_timer_irq(hp_sdc_rtc_isr);
+ printk(KERN_INFO "HP i8042 SDC + MSM-58321 RTC support unloaded\n");
+}
+
+module_init(hp_sdc_rtc_init);
+module_exit(hp_sdc_rtc_exit);
diff --git a/drivers/input/misc/ixp4xx-beeper.c b/drivers/input/misc/ixp4xx-beeper.c
new file mode 100644
index 0000000..9946d73
--- /dev/null
+++ b/drivers/input/misc/ixp4xx-beeper.c
@@ -0,0 +1,186 @@
+/*
+ * Generic IXP4xx beeper driver
+ *
+ * Copyright (C) 2005 Tower Technologies
+ *
+ * based on nslu2-io.c
+ * Copyright (C) 2004 Karen Spearel
+ *
+ * Author: Alessandro Zummo <a.zummo@towertech.it>
+ * Maintainers: http://www.nslu2-linux.org/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/input.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <mach/hardware.h>
+
+MODULE_AUTHOR("Alessandro Zummo <a.zummo@towertech.it>");
+MODULE_DESCRIPTION("ixp4xx beeper driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:ixp4xx-beeper");
+
+static DEFINE_SPINLOCK(beep_lock);
+
+static void ixp4xx_spkr_control(unsigned int pin, unsigned int count)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&beep_lock, flags);
+
+ if (count) {
+ gpio_line_config(pin, IXP4XX_GPIO_OUT);
+ gpio_line_set(pin, IXP4XX_GPIO_LOW);
+
+ *IXP4XX_OSRT2 = (count & ~IXP4XX_OST_RELOAD_MASK) | IXP4XX_OST_ENABLE;
+ } else {
+ gpio_line_config(pin, IXP4XX_GPIO_IN);
+ gpio_line_set(pin, IXP4XX_GPIO_HIGH);
+
+ *IXP4XX_OSRT2 = 0;
+ }
+
+ spin_unlock_irqrestore(&beep_lock, flags);
+}
+
+static int ixp4xx_spkr_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
+{
+ unsigned int pin = (unsigned int) input_get_drvdata(dev);
+ unsigned int count = 0;
+
+ if (type != EV_SND)
+ return -1;
+
+ switch (code) {
+ case SND_BELL:
+ if (value)
+ value = 1000;
+ case SND_TONE:
+ break;
+ default:
+ return -1;
+ }
+
+ if (value > 20 && value < 32767)
+#ifndef FREQ
+ count = (ixp4xx_get_board_tick_rate() / (value * 4)) - 1;
+#else
+ count = (FREQ / (value * 4)) - 1;
+#endif
+
+ ixp4xx_spkr_control(pin, count);
+
+ return 0;
+}
+
+static irqreturn_t ixp4xx_spkr_interrupt(int irq, void *dev_id)
+{
+ /* clear interrupt */
+ *IXP4XX_OSST = IXP4XX_OSST_TIMER_2_PEND;
+
+ /* flip the beeper output */
+ *IXP4XX_GPIO_GPOUTR ^= (1 << (unsigned int) dev_id);
+
+ return IRQ_HANDLED;
+}
+
+static int __devinit ixp4xx_spkr_probe(struct platform_device *dev)
+{
+ struct input_dev *input_dev;
+ int err;
+
+ input_dev = input_allocate_device();
+ if (!input_dev)
+ return -ENOMEM;
+
+ input_set_drvdata(input_dev, (void *) dev->id);
+
+ input_dev->name = "ixp4xx beeper",
+ input_dev->phys = "ixp4xx/gpio";
+ input_dev->id.bustype = BUS_HOST;
+ input_dev->id.vendor = 0x001f;
+ input_dev->id.product = 0x0001;
+ input_dev->id.version = 0x0100;
+ input_dev->dev.parent = &dev->dev;
+
+ input_dev->evbit[0] = BIT_MASK(EV_SND);
+ input_dev->sndbit[0] = BIT_MASK(SND_BELL) | BIT_MASK(SND_TONE);
+ input_dev->event = ixp4xx_spkr_event;
+
+ err = request_irq(IRQ_IXP4XX_TIMER2, &ixp4xx_spkr_interrupt,
+ IRQF_DISABLED | IRQF_TIMER, "ixp4xx-beeper", (void *) dev->id);
+ if (err)
+ goto err_free_device;
+
+ err = input_register_device(input_dev);
+ if (err)
+ goto err_free_irq;
+
+ platform_set_drvdata(dev, input_dev);
+
+ return 0;
+
+ err_free_irq:
+ free_irq(IRQ_IXP4XX_TIMER2, dev);
+ err_free_device:
+ input_free_device(input_dev);
+
+ return err;
+}
+
+static int __devexit ixp4xx_spkr_remove(struct platform_device *dev)
+{
+ struct input_dev *input_dev = platform_get_drvdata(dev);
+ unsigned int pin = (unsigned int) input_get_drvdata(input_dev);
+
+ input_unregister_device(input_dev);
+ platform_set_drvdata(dev, NULL);
+
+ /* turn the speaker off */
+ disable_irq(IRQ_IXP4XX_TIMER2);
+ ixp4xx_spkr_control(pin, 0);
+
+ free_irq(IRQ_IXP4XX_TIMER2, dev);
+
+ return 0;
+}
+
+static void ixp4xx_spkr_shutdown(struct platform_device *dev)
+{
+ struct input_dev *input_dev = platform_get_drvdata(dev);
+ unsigned int pin = (unsigned int) input_get_drvdata(input_dev);
+
+ /* turn off the speaker */
+ disable_irq(IRQ_IXP4XX_TIMER2);
+ ixp4xx_spkr_control(pin, 0);
+}
+
+static struct platform_driver ixp4xx_spkr_platform_driver = {
+ .driver = {
+ .name = "ixp4xx-beeper",
+ .owner = THIS_MODULE,
+ },
+ .probe = ixp4xx_spkr_probe,
+ .remove = __devexit_p(ixp4xx_spkr_remove),
+ .shutdown = ixp4xx_spkr_shutdown,
+};
+
+static int __init ixp4xx_spkr_init(void)
+{
+ return platform_driver_register(&ixp4xx_spkr_platform_driver);
+}
+
+static void __exit ixp4xx_spkr_exit(void)
+{
+ platform_driver_unregister(&ixp4xx_spkr_platform_driver);
+}
+
+module_init(ixp4xx_spkr_init);
+module_exit(ixp4xx_spkr_exit);
diff --git a/drivers/input/misc/keyspan_remote.c b/drivers/input/misc/keyspan_remote.c
new file mode 100644
index 0000000..86afdd1
--- /dev/null
+++ b/drivers/input/misc/keyspan_remote.c
@@ -0,0 +1,607 @@
+/*
+ * keyspan_remote: USB driver for the Keyspan DMR
+ *
+ * Copyright (C) 2005 Zymeta Corporation - Michael Downey (downey@zymeta.com)
+ *
+ * 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, version 2.
+ *
+ * This driver has been put together with the support of Innosys, Inc.
+ * and Keyspan, Inc the manufacturers of the Keyspan USB DMR product.
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/usb/input.h>
+
+#define DRIVER_VERSION "v0.1"
+#define DRIVER_AUTHOR "Michael Downey <downey@zymeta.com>"
+#define DRIVER_DESC "Driver for the USB Keyspan remote control."
+#define DRIVER_LICENSE "GPL"
+
+/* Parameters that can be passed to the driver. */
+static int debug;
+module_param(debug, int, 0444);
+MODULE_PARM_DESC(debug, "Enable extra debug messages and information");
+
+/* Vendor and product ids */
+#define USB_KEYSPAN_VENDOR_ID 0x06CD
+#define USB_KEYSPAN_PRODUCT_UIA11 0x0202
+
+/* Defines for converting the data from the remote. */
+#define ZERO 0x18
+#define ZERO_MASK 0x1F /* 5 bits for a 0 */
+#define ONE 0x3C
+#define ONE_MASK 0x3F /* 6 bits for a 1 */
+#define SYNC 0x3F80
+#define SYNC_MASK 0x3FFF /* 14 bits for a SYNC sequence */
+#define STOP 0x00
+#define STOP_MASK 0x1F /* 5 bits for the STOP sequence */
+#define GAP 0xFF
+
+#define RECV_SIZE 8 /* The UIA-11 type have a 8 byte limit. */
+
+/*
+ * Table that maps the 31 possible keycodes to input keys.
+ * Currently there are 15 and 17 button models so RESERVED codes
+ * are blank areas in the mapping.
+ */
+static const unsigned short keyspan_key_table[] = {
+ KEY_RESERVED, /* 0 is just a place holder. */
+ KEY_RESERVED,
+ KEY_STOP,
+ KEY_PLAYCD,
+ KEY_RESERVED,
+ KEY_PREVIOUSSONG,
+ KEY_REWIND,
+ KEY_FORWARD,
+ KEY_NEXTSONG,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_PAUSE,
+ KEY_VOLUMEUP,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_VOLUMEDOWN,
+ KEY_RESERVED,
+ KEY_UP,
+ KEY_RESERVED,
+ KEY_MUTE,
+ KEY_LEFT,
+ KEY_ENTER,
+ KEY_RIGHT,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_DOWN,
+ KEY_RESERVED,
+ KEY_KPASTERISK,
+ KEY_RESERVED,
+ KEY_MENU
+};
+
+/* table of devices that work with this driver */
+static struct usb_device_id keyspan_table[] = {
+ { USB_DEVICE(USB_KEYSPAN_VENDOR_ID, USB_KEYSPAN_PRODUCT_UIA11) },
+ { } /* Terminating entry */
+};
+
+/* Structure to store all the real stuff that a remote sends to us. */
+struct keyspan_message {
+ u16 system;
+ u8 button;
+ u8 toggle;
+};
+
+/* Structure used for all the bit testing magic needed to be done. */
+struct bit_tester {
+ u32 tester;
+ int len;
+ int pos;
+ int bits_left;
+ u8 buffer[32];
+};
+
+/* Structure to hold all of our driver specific stuff */
+struct usb_keyspan {
+ char name[128];
+ char phys[64];
+ unsigned short keymap[ARRAY_SIZE(keyspan_key_table)];
+ struct usb_device *udev;
+ struct input_dev *input;
+ struct usb_interface *interface;
+ struct usb_endpoint_descriptor *in_endpoint;
+ struct urb* irq_urb;
+ int open;
+ dma_addr_t in_dma;
+ unsigned char *in_buffer;
+
+ /* variables used to parse messages from remote. */
+ struct bit_tester data;
+ int stage;
+ int toggle;
+};
+
+static struct usb_driver keyspan_driver;
+
+/*
+ * Debug routine that prints out what we've received from the remote.
+ */
+static void keyspan_print(struct usb_keyspan* dev) /*unsigned char* data)*/
+{
+ char codes[4 * RECV_SIZE];
+ int i;
+
+ for (i = 0; i < RECV_SIZE; i++)
+ snprintf(codes + i * 3, 4, "%02x ", dev->in_buffer[i]);
+
+ dev_info(&dev->udev->dev, "%s\n", codes);
+}
+
+/*
+ * Routine that manages the bit_tester structure. It makes sure that there are
+ * at least bits_needed bits loaded into the tester.
+ */
+static int keyspan_load_tester(struct usb_keyspan* dev, int bits_needed)
+{
+ if (dev->data.bits_left >= bits_needed)
+ return 0;
+
+ /*
+ * Somehow we've missed the last message. The message will be repeated
+ * though so it's not too big a deal
+ */
+ if (dev->data.pos >= dev->data.len) {
+ dev_dbg(&dev->udev->dev,
+ "%s - Error ran out of data. pos: %d, len: %d\n",
+ __func__, dev->data.pos, dev->data.len);
+ return -1;
+ }
+
+ /* Load as much as we can into the tester. */
+ while ((dev->data.bits_left + 7 < (sizeof(dev->data.tester) * 8)) &&
+ (dev->data.pos < dev->data.len)) {
+ dev->data.tester += (dev->data.buffer[dev->data.pos++] << dev->data.bits_left);
+ dev->data.bits_left += 8;
+ }
+
+ return 0;
+}
+
+static void keyspan_report_button(struct usb_keyspan *remote, int button, int press)
+{
+ struct input_dev *input = remote->input;
+
+ input_event(input, EV_MSC, MSC_SCAN, button);
+ input_report_key(input, remote->keymap[button], press);
+ input_sync(input);
+}
+
+/*
+ * Routine that handles all the logic needed to parse out the message from the remote.
+ */
+static void keyspan_check_data(struct usb_keyspan *remote)
+{
+ int i;
+ int found = 0;
+ struct keyspan_message message;
+
+ switch(remote->stage) {
+ case 0:
+ /*
+ * In stage 0 we want to find the start of a message. The remote sends a 0xFF as filler.
+ * So the first byte that isn't a FF should be the start of a new message.
+ */
+ for (i = 0; i < RECV_SIZE && remote->in_buffer[i] == GAP; ++i);
+
+ if (i < RECV_SIZE) {
+ memcpy(remote->data.buffer, remote->in_buffer, RECV_SIZE);
+ remote->data.len = RECV_SIZE;
+ remote->data.pos = 0;
+ remote->data.tester = 0;
+ remote->data.bits_left = 0;
+ remote->stage = 1;
+ }
+ break;
+
+ case 1:
+ /*
+ * Stage 1 we should have 16 bytes and should be able to detect a
+ * SYNC. The SYNC is 14 bits, 7 0's and then 7 1's.
+ */
+ memcpy(remote->data.buffer + remote->data.len, remote->in_buffer, RECV_SIZE);
+ remote->data.len += RECV_SIZE;
+
+ found = 0;
+ while ((remote->data.bits_left >= 14 || remote->data.pos < remote->data.len) && !found) {
+ for (i = 0; i < 8; ++i) {
+ if (keyspan_load_tester(remote, 14) != 0) {
+ remote->stage = 0;
+ return;
+ }
+
+ if ((remote->data.tester & SYNC_MASK) == SYNC) {
+ remote->data.tester = remote->data.tester >> 14;
+ remote->data.bits_left -= 14;
+ found = 1;
+ break;
+ } else {
+ remote->data.tester = remote->data.tester >> 1;
+ --remote->data.bits_left;
+ }
+ }
+ }
+
+ if (!found) {
+ remote->stage = 0;
+ remote->data.len = 0;
+ } else {
+ remote->stage = 2;
+ }
+ break;
+
+ case 2:
+ /*
+ * Stage 2 we should have 24 bytes which will be enough for a full
+ * message. We need to parse out the system code, button code,
+ * toggle code, and stop.
+ */
+ memcpy(remote->data.buffer + remote->data.len, remote->in_buffer, RECV_SIZE);
+ remote->data.len += RECV_SIZE;
+
+ message.system = 0;
+ for (i = 0; i < 9; i++) {
+ keyspan_load_tester(remote, 6);
+
+ if ((remote->data.tester & ZERO_MASK) == ZERO) {
+ message.system = message.system << 1;
+ remote->data.tester = remote->data.tester >> 5;
+ remote->data.bits_left -= 5;
+ } else if ((remote->data.tester & ONE_MASK) == ONE) {
+ message.system = (message.system << 1) + 1;
+ remote->data.tester = remote->data.tester >> 6;
+ remote->data.bits_left -= 6;
+ } else {
+ err("%s - Unknown sequence found in system data.\n", __func__);
+ remote->stage = 0;
+ return;
+ }
+ }
+
+ message.button = 0;
+ for (i = 0; i < 5; i++) {
+ keyspan_load_tester(remote, 6);
+
+ if ((remote->data.tester & ZERO_MASK) == ZERO) {
+ message.button = message.button << 1;
+ remote->data.tester = remote->data.tester >> 5;
+ remote->data.bits_left -= 5;
+ } else if ((remote->data.tester & ONE_MASK) == ONE) {
+ message.button = (message.button << 1) + 1;
+ remote->data.tester = remote->data.tester >> 6;
+ remote->data.bits_left -= 6;
+ } else {
+ err("%s - Unknown sequence found in button data.\n", __func__);
+ remote->stage = 0;
+ return;
+ }
+ }
+
+ keyspan_load_tester(remote, 6);
+ if ((remote->data.tester & ZERO_MASK) == ZERO) {
+ message.toggle = 0;
+ remote->data.tester = remote->data.tester >> 5;
+ remote->data.bits_left -= 5;
+ } else if ((remote->data.tester & ONE_MASK) == ONE) {
+ message.toggle = 1;
+ remote->data.tester = remote->data.tester >> 6;
+ remote->data.bits_left -= 6;
+ } else {
+ err("%s - Error in message, invalid toggle.\n", __func__);
+ remote->stage = 0;
+ return;
+ }
+
+ keyspan_load_tester(remote, 5);
+ if ((remote->data.tester & STOP_MASK) == STOP) {
+ remote->data.tester = remote->data.tester >> 5;
+ remote->data.bits_left -= 5;
+ } else {
+ err("Bad message recieved, no stop bit found.\n");
+ }
+
+ dev_dbg(&remote->udev->dev,
+ "%s found valid message: system: %d, button: %d, toggle: %d\n",
+ __func__, message.system, message.button, message.toggle);
+
+ if (message.toggle != remote->toggle) {
+ keyspan_report_button(remote, message.button, 1);
+ keyspan_report_button(remote, message.button, 0);
+ remote->toggle = message.toggle;
+ }
+
+ remote->stage = 0;
+ break;
+ }
+}
+
+/*
+ * Routine for sending all the initialization messages to the remote.
+ */
+static int keyspan_setup(struct usb_device* dev)
+{
+ int retval = 0;
+
+ retval = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+ 0x11, 0x40, 0x5601, 0x0, NULL, 0, 0);
+ if (retval) {
+ dev_dbg(&dev->dev, "%s - failed to set bit rate due to error: %d\n",
+ __func__, retval);
+ return(retval);
+ }
+
+ retval = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+ 0x44, 0x40, 0x0, 0x0, NULL, 0, 0);
+ if (retval) {
+ dev_dbg(&dev->dev, "%s - failed to set resume sensitivity due to error: %d\n",
+ __func__, retval);
+ return(retval);
+ }
+
+ retval = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+ 0x22, 0x40, 0x0, 0x0, NULL, 0, 0);
+ if (retval) {
+ dev_dbg(&dev->dev, "%s - failed to turn receive on due to error: %d\n",
+ __func__, retval);
+ return(retval);
+ }
+
+ dev_dbg(&dev->dev, "%s - Setup complete.\n", __func__);
+ return(retval);
+}
+
+/*
+ * Routine used to handle a new message that has come in.
+ */
+static void keyspan_irq_recv(struct urb *urb)
+{
+ struct usb_keyspan *dev = urb->context;
+ int retval;
+
+ /* Check our status in case we need to bail out early. */
+ switch (urb->status) {
+ case 0:
+ break;
+
+ /* Device went away so don't keep trying to read from it. */
+ case -ECONNRESET:
+ case -ENOENT:
+ case -ESHUTDOWN:
+ return;
+
+ default:
+ goto resubmit;
+ break;
+ }
+
+ if (debug)
+ keyspan_print(dev);
+
+ keyspan_check_data(dev);
+
+resubmit:
+ retval = usb_submit_urb(urb, GFP_ATOMIC);
+ if (retval)
+ err ("%s - usb_submit_urb failed with result: %d", __func__, retval);
+}
+
+static int keyspan_open(struct input_dev *dev)
+{
+ struct usb_keyspan *remote = input_get_drvdata(dev);
+
+ remote->irq_urb->dev = remote->udev;
+ if (usb_submit_urb(remote->irq_urb, GFP_KERNEL))
+ return -EIO;
+
+ return 0;
+}
+
+static void keyspan_close(struct input_dev *dev)
+{
+ struct usb_keyspan *remote = input_get_drvdata(dev);
+
+ usb_kill_urb(remote->irq_urb);
+}
+
+static struct usb_endpoint_descriptor *keyspan_get_in_endpoint(struct usb_host_interface *iface)
+{
+
+ struct usb_endpoint_descriptor *endpoint;
+ int i;
+
+ for (i = 0; i < iface->desc.bNumEndpoints; ++i) {
+ endpoint = &iface->endpoint[i].desc;
+
+ if (usb_endpoint_is_int_in(endpoint)) {
+ /* we found our interrupt in endpoint */
+ return endpoint;
+ }
+ }
+
+ return NULL;
+}
+
+/*
+ * Routine that sets up the driver to handle a specific USB device detected on the bus.
+ */
+static int keyspan_probe(struct usb_interface *interface, const struct usb_device_id *id)
+{
+ struct usb_device *udev = interface_to_usbdev(interface);
+ struct usb_endpoint_descriptor *endpoint;
+ struct usb_keyspan *remote;
+ struct input_dev *input_dev;
+ int i, error;
+
+ endpoint = keyspan_get_in_endpoint(interface->cur_altsetting);
+ if (!endpoint)
+ return -ENODEV;
+
+ remote = kzalloc(sizeof(*remote), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!remote || !input_dev) {
+ error = -ENOMEM;
+ goto fail1;
+ }
+
+ remote->udev = udev;
+ remote->input = input_dev;
+ remote->interface = interface;
+ remote->in_endpoint = endpoint;
+ remote->toggle = -1; /* Set to -1 so we will always not match the toggle from the first remote message. */
+
+ remote->in_buffer = usb_buffer_alloc(udev, RECV_SIZE, GFP_ATOMIC, &remote->in_dma);
+ if (!remote->in_buffer) {
+ error = -ENOMEM;
+ goto fail1;
+ }
+
+ remote->irq_urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!remote->irq_urb) {
+ error = -ENOMEM;
+ goto fail2;
+ }
+
+ error = keyspan_setup(udev);
+ if (error) {
+ error = -ENODEV;
+ goto fail3;
+ }
+
+ if (udev->manufacturer)
+ strlcpy(remote->name, udev->manufacturer, sizeof(remote->name));
+
+ if (udev->product) {
+ if (udev->manufacturer)
+ strlcat(remote->name, " ", sizeof(remote->name));
+ strlcat(remote->name, udev->product, sizeof(remote->name));
+ }
+
+ if (!strlen(remote->name))
+ snprintf(remote->name, sizeof(remote->name),
+ "USB Keyspan Remote %04x:%04x",
+ le16_to_cpu(udev->descriptor.idVendor),
+ le16_to_cpu(udev->descriptor.idProduct));
+
+ usb_make_path(udev, remote->phys, sizeof(remote->phys));
+ strlcat(remote->phys, "/input0", sizeof(remote->phys));
+ memcpy(remote->keymap, keyspan_key_table, sizeof(remote->keymap));
+
+ input_dev->name = remote->name;
+ input_dev->phys = remote->phys;
+ usb_to_input_id(udev, &input_dev->id);
+ input_dev->dev.parent = &interface->dev;
+ input_dev->keycode = remote->keymap;
+ input_dev->keycodesize = sizeof(unsigned short);
+ input_dev->keycodemax = ARRAY_SIZE(remote->keymap);
+
+ input_set_capability(input_dev, EV_MSC, MSC_SCAN);
+ __set_bit(EV_KEY, input_dev->evbit);
+ for (i = 0; i < ARRAY_SIZE(keyspan_key_table); i++)
+ __set_bit(keyspan_key_table[i], input_dev->keybit);
+ __clear_bit(KEY_RESERVED, input_dev->keybit);
+
+ input_set_drvdata(input_dev, remote);
+
+ input_dev->open = keyspan_open;
+ input_dev->close = keyspan_close;
+
+ /*
+ * Initialize the URB to access the device.
+ * The urb gets sent to the device in keyspan_open()
+ */
+ usb_fill_int_urb(remote->irq_urb,
+ remote->udev,
+ usb_rcvintpipe(remote->udev, endpoint->bEndpointAddress),
+ remote->in_buffer, RECV_SIZE, keyspan_irq_recv, remote,
+ endpoint->bInterval);
+ remote->irq_urb->transfer_dma = remote->in_dma;
+ remote->irq_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+ /* we can register the device now, as it is ready */
+ error = input_register_device(remote->input);
+ if (error)
+ goto fail3;
+
+ /* save our data pointer in this interface device */
+ usb_set_intfdata(interface, remote);
+
+ return 0;
+
+ fail3: usb_free_urb(remote->irq_urb);
+ fail2: usb_buffer_free(udev, RECV_SIZE, remote->in_buffer, remote->in_dma);
+ fail1: kfree(remote);
+ input_free_device(input_dev);
+
+ return error;
+}
+
+/*
+ * Routine called when a device is disconnected from the USB.
+ */
+static void keyspan_disconnect(struct usb_interface *interface)
+{
+ struct usb_keyspan *remote;
+
+ remote = usb_get_intfdata(interface);
+ usb_set_intfdata(interface, NULL);
+
+ if (remote) { /* We have a valid driver structure so clean up everything we allocated. */
+ input_unregister_device(remote->input);
+ usb_kill_urb(remote->irq_urb);
+ usb_free_urb(remote->irq_urb);
+ usb_buffer_free(remote->udev, RECV_SIZE, remote->in_buffer, remote->in_dma);
+ kfree(remote);
+ }
+}
+
+/*
+ * Standard driver set up sections
+ */
+static struct usb_driver keyspan_driver =
+{
+ .name = "keyspan_remote",
+ .probe = keyspan_probe,
+ .disconnect = keyspan_disconnect,
+ .id_table = keyspan_table
+};
+
+static int __init usb_keyspan_init(void)
+{
+ int result;
+
+ /* register this driver with the USB subsystem */
+ result = usb_register(&keyspan_driver);
+ if (result)
+ err("usb_register failed. Error number %d\n", result);
+
+ return result;
+}
+
+static void __exit usb_keyspan_exit(void)
+{
+ /* deregister this driver with the USB subsystem */
+ usb_deregister(&keyspan_driver);
+}
+
+module_init(usb_keyspan_init);
+module_exit(usb_keyspan_exit);
+
+MODULE_DEVICE_TABLE(usb, keyspan_table);
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE(DRIVER_LICENSE);
diff --git a/drivers/input/misc/m68kspkr.c b/drivers/input/misc/m68kspkr.c
new file mode 100644
index 0000000..0c64d9b
--- /dev/null
+++ b/drivers/input/misc/m68kspkr.c
@@ -0,0 +1,151 @@
+/*
+ * m68k beeper driver for Linux
+ *
+ * Copyright (c) 2002 Richard Zidlicky
+ * Copyright (c) 2002 Vojtech Pavlik
+ * Copyright (c) 1992 Orest Zborowski
+ *
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/platform_device.h>
+#include <asm/machdep.h>
+#include <asm/io.h>
+
+MODULE_AUTHOR("Richard Zidlicky <rz@linux-m68k.org>");
+MODULE_DESCRIPTION("m68k beeper driver");
+MODULE_LICENSE("GPL");
+
+static struct platform_device *m68kspkr_platform_device;
+
+static int m68kspkr_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
+{
+ unsigned int count = 0;
+
+ if (type != EV_SND)
+ return -1;
+
+ switch (code) {
+ case SND_BELL: if (value) value = 1000;
+ case SND_TONE: break;
+ default: return -1;
+ }
+
+ if (value > 20 && value < 32767)
+ count = 1193182 / value;
+
+ mach_beep(count, -1);
+
+ return 0;
+}
+
+static int __devinit m68kspkr_probe(struct platform_device *dev)
+{
+ struct input_dev *input_dev;
+ int err;
+
+ input_dev = input_allocate_device();
+ if (!input_dev)
+ return -ENOMEM;
+
+ input_dev->name = "m68k beeper";
+ input_dev->phys = "m68k/generic";
+ input_dev->id.bustype = BUS_HOST;
+ input_dev->id.vendor = 0x001f;
+ input_dev->id.product = 0x0001;
+ input_dev->id.version = 0x0100;
+ input_dev->dev.parent = &dev->dev;
+
+ input_dev->evbit[0] = BIT_MASK(EV_SND);
+ input_dev->sndbit[0] = BIT_MASK(SND_BELL) | BIT_MASK(SND_TONE);
+ input_dev->event = m68kspkr_event;
+
+ err = input_register_device(input_dev);
+ if (err) {
+ input_free_device(input_dev);
+ return err;
+ }
+
+ platform_set_drvdata(dev, input_dev);
+
+ return 0;
+}
+
+static int __devexit m68kspkr_remove(struct platform_device *dev)
+{
+ struct input_dev *input_dev = platform_get_drvdata(dev);
+
+ input_unregister_device(input_dev);
+ platform_set_drvdata(dev, NULL);
+ /* turn off the speaker */
+ m68kspkr_event(NULL, EV_SND, SND_BELL, 0);
+
+ return 0;
+}
+
+static void m68kspkr_shutdown(struct platform_device *dev)
+{
+ /* turn off the speaker */
+ m68kspkr_event(NULL, EV_SND, SND_BELL, 0);
+}
+
+static struct platform_driver m68kspkr_platform_driver = {
+ .driver = {
+ .name = "m68kspkr",
+ .owner = THIS_MODULE,
+ },
+ .probe = m68kspkr_probe,
+ .remove = __devexit_p(m68kspkr_remove),
+ .shutdown = m68kspkr_shutdown,
+};
+
+static int __init m68kspkr_init(void)
+{
+ int err;
+
+ if (!mach_beep) {
+ printk(KERN_INFO "m68kspkr: no lowlevel beep support\n");
+ return -ENODEV;
+ }
+
+ err = platform_driver_register(&m68kspkr_platform_driver);
+ if (err)
+ return err;
+
+ m68kspkr_platform_device = platform_device_alloc("m68kspkr", -1);
+ if (!m68kspkr_platform_device) {
+ err = -ENOMEM;
+ goto err_unregister_driver;
+ }
+
+ err = platform_device_add(m68kspkr_platform_device);
+ if (err)
+ goto err_free_device;
+
+ return 0;
+
+ err_free_device:
+ platform_device_put(m68kspkr_platform_device);
+ err_unregister_driver:
+ platform_driver_unregister(&m68kspkr_platform_driver);
+
+ return err;
+}
+
+static void __exit m68kspkr_exit(void)
+{
+ platform_device_unregister(m68kspkr_platform_device);
+ platform_driver_unregister(&m68kspkr_platform_driver);
+}
+
+module_init(m68kspkr_init);
+module_exit(m68kspkr_exit);
diff --git a/drivers/input/misc/pcspkr.c b/drivers/input/misc/pcspkr.c
new file mode 100644
index 0000000..43aaa5c
--- /dev/null
+++ b/drivers/input/misc/pcspkr.c
@@ -0,0 +1,152 @@
+/*
+ * PC Speaker beeper driver for Linux
+ *
+ * Copyright (c) 2002 Vojtech Pavlik
+ * Copyright (c) 1992 Orest Zborowski
+ *
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/platform_device.h>
+#include <asm/io.h>
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION("PC Speaker beeper driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:pcspkr");
+
+#if defined(CONFIG_MIPS) || defined(CONFIG_X86)
+/* Use the global PIT lock ! */
+#include <asm/i8253.h>
+#else
+#include <asm/8253pit.h>
+static DEFINE_SPINLOCK(i8253_lock);
+#endif
+
+static int pcspkr_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
+{
+ unsigned int count = 0;
+ unsigned long flags;
+
+ if (type != EV_SND)
+ return -1;
+
+ switch (code) {
+ case SND_BELL: if (value) value = 1000;
+ case SND_TONE: break;
+ default: return -1;
+ }
+
+ if (value > 20 && value < 32767)
+ count = PIT_TICK_RATE / value;
+
+ spin_lock_irqsave(&i8253_lock, flags);
+
+ if (count) {
+ /* enable counter 2 */
+ outb_p(inb_p(0x61) | 3, 0x61);
+ /* set command for counter 2, 2 byte write */
+ outb_p(0xB6, 0x43);
+ /* select desired HZ */
+ outb_p(count & 0xff, 0x42);
+ outb((count >> 8) & 0xff, 0x42);
+ } else {
+ /* disable counter 2 */
+ outb(inb_p(0x61) & 0xFC, 0x61);
+ }
+
+ spin_unlock_irqrestore(&i8253_lock, flags);
+
+ return 0;
+}
+
+static int __devinit pcspkr_probe(struct platform_device *dev)
+{
+ struct input_dev *pcspkr_dev;
+ int err;
+
+ pcspkr_dev = input_allocate_device();
+ if (!pcspkr_dev)
+ return -ENOMEM;
+
+ pcspkr_dev->name = "PC Speaker";
+ pcspkr_dev->phys = "isa0061/input0";
+ pcspkr_dev->id.bustype = BUS_ISA;
+ pcspkr_dev->id.vendor = 0x001f;
+ pcspkr_dev->id.product = 0x0001;
+ pcspkr_dev->id.version = 0x0100;
+ pcspkr_dev->dev.parent = &dev->dev;
+
+ pcspkr_dev->evbit[0] = BIT_MASK(EV_SND);
+ pcspkr_dev->sndbit[0] = BIT_MASK(SND_BELL) | BIT_MASK(SND_TONE);
+ pcspkr_dev->event = pcspkr_event;
+
+ err = input_register_device(pcspkr_dev);
+ if (err) {
+ input_free_device(pcspkr_dev);
+ return err;
+ }
+
+ platform_set_drvdata(dev, pcspkr_dev);
+
+ return 0;
+}
+
+static int __devexit pcspkr_remove(struct platform_device *dev)
+{
+ struct input_dev *pcspkr_dev = platform_get_drvdata(dev);
+
+ input_unregister_device(pcspkr_dev);
+ platform_set_drvdata(dev, NULL);
+ /* turn off the speaker */
+ pcspkr_event(NULL, EV_SND, SND_BELL, 0);
+
+ return 0;
+}
+
+static int pcspkr_suspend(struct platform_device *dev, pm_message_t state)
+{
+ pcspkr_event(NULL, EV_SND, SND_BELL, 0);
+
+ return 0;
+}
+
+static void pcspkr_shutdown(struct platform_device *dev)
+{
+ /* turn off the speaker */
+ pcspkr_event(NULL, EV_SND, SND_BELL, 0);
+}
+
+static struct platform_driver pcspkr_platform_driver = {
+ .driver = {
+ .name = "pcspkr",
+ .owner = THIS_MODULE,
+ },
+ .probe = pcspkr_probe,
+ .remove = __devexit_p(pcspkr_remove),
+ .suspend = pcspkr_suspend,
+ .shutdown = pcspkr_shutdown,
+};
+
+
+static int __init pcspkr_init(void)
+{
+ return platform_driver_register(&pcspkr_platform_driver);
+}
+
+static void __exit pcspkr_exit(void)
+{
+ platform_driver_unregister(&pcspkr_platform_driver);
+}
+
+module_init(pcspkr_init);
+module_exit(pcspkr_exit);
diff --git a/drivers/input/misc/powermate.c b/drivers/input/misc/powermate.c
new file mode 100644
index 0000000..a53c488
--- /dev/null
+++ b/drivers/input/misc/powermate.c
@@ -0,0 +1,464 @@
+/*
+ * A driver for the Griffin Technology, Inc. "PowerMate" USB controller dial.
+ *
+ * v1.1, (c)2002 William R Sowerbutts <will@sowerbutts.com>
+ *
+ * This device is a anodised aluminium knob which connects over USB. It can measure
+ * clockwise and anticlockwise rotation. The dial also acts as a pushbutton with
+ * a spring for automatic release. The base contains a pair of LEDs which illuminate
+ * the translucent base. It rotates without limit and reports its relative rotation
+ * back to the host when polled by the USB controller.
+ *
+ * Testing with the knob I have has shown that it measures approximately 94 "clicks"
+ * for one full rotation. Testing with my High Speed Rotation Actuator (ok, it was
+ * a variable speed cordless electric drill) has shown that the device can measure
+ * speeds of up to 7 clicks either clockwise or anticlockwise between pollings from
+ * the host. If it counts more than 7 clicks before it is polled, it will wrap back
+ * to zero and start counting again. This was at quite high speed, however, almost
+ * certainly faster than the human hand could turn it. Griffin say that it loses a
+ * pulse or two on a direction change; the granularity is so fine that I never
+ * noticed this in practice.
+ *
+ * The device's microcontroller can be programmed to set the LED to either a constant
+ * intensity, or to a rhythmic pulsing. Several patterns and speeds are available.
+ *
+ * Griffin were very happy to provide documentation and free hardware for development.
+ *
+ * Some userspace tools are available on the web: http://sowerbutts.com/powermate/
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/usb/input.h>
+
+#define POWERMATE_VENDOR 0x077d /* Griffin Technology, Inc. */
+#define POWERMATE_PRODUCT_NEW 0x0410 /* Griffin PowerMate */
+#define POWERMATE_PRODUCT_OLD 0x04AA /* Griffin soundKnob */
+
+#define CONTOUR_VENDOR 0x05f3 /* Contour Design, Inc. */
+#define CONTOUR_JOG 0x0240 /* Jog and Shuttle */
+
+/* these are the command codes we send to the device */
+#define SET_STATIC_BRIGHTNESS 0x01
+#define SET_PULSE_ASLEEP 0x02
+#define SET_PULSE_AWAKE 0x03
+#define SET_PULSE_MODE 0x04
+
+/* these refer to bits in the powermate_device's requires_update field. */
+#define UPDATE_STATIC_BRIGHTNESS (1<<0)
+#define UPDATE_PULSE_ASLEEP (1<<1)
+#define UPDATE_PULSE_AWAKE (1<<2)
+#define UPDATE_PULSE_MODE (1<<3)
+
+/* at least two versions of the hardware exist, with differing payload
+ sizes. the first three bytes always contain the "interesting" data in
+ the relevant format. */
+#define POWERMATE_PAYLOAD_SIZE_MAX 6
+#define POWERMATE_PAYLOAD_SIZE_MIN 3
+struct powermate_device {
+ signed char *data;
+ dma_addr_t data_dma;
+ struct urb *irq, *config;
+ struct usb_ctrlrequest *configcr;
+ dma_addr_t configcr_dma;
+ struct usb_device *udev;
+ struct input_dev *input;
+ spinlock_t lock;
+ int static_brightness;
+ int pulse_speed;
+ int pulse_table;
+ int pulse_asleep;
+ int pulse_awake;
+ int requires_update; // physical settings which are out of sync
+ char phys[64];
+};
+
+static char pm_name_powermate[] = "Griffin PowerMate";
+static char pm_name_soundknob[] = "Griffin SoundKnob";
+
+static void powermate_config_complete(struct urb *urb);
+
+/* Callback for data arriving from the PowerMate over the USB interrupt pipe */
+static void powermate_irq(struct urb *urb)
+{
+ struct powermate_device *pm = urb->context;
+ int retval;
+
+ switch (urb->status) {
+ case 0:
+ /* success */
+ break;
+ case -ECONNRESET:
+ case -ENOENT:
+ case -ESHUTDOWN:
+ /* this urb is terminated, clean up */
+ dbg("%s - urb shutting down with status: %d", __func__, urb->status);
+ return;
+ default:
+ dbg("%s - nonzero urb status received: %d", __func__, urb->status);
+ goto exit;
+ }
+
+ /* handle updates to device state */
+ input_report_key(pm->input, BTN_0, pm->data[0] & 0x01);
+ input_report_rel(pm->input, REL_DIAL, pm->data[1]);
+ input_sync(pm->input);
+
+exit:
+ retval = usb_submit_urb (urb, GFP_ATOMIC);
+ if (retval)
+ err ("%s - usb_submit_urb failed with result %d",
+ __func__, retval);
+}
+
+/* Decide if we need to issue a control message and do so. Must be called with pm->lock taken */
+static void powermate_sync_state(struct powermate_device *pm)
+{
+ if (pm->requires_update == 0)
+ return; /* no updates are required */
+ if (pm->config->status == -EINPROGRESS)
+ return; /* an update is already in progress; it'll issue this update when it completes */
+
+ if (pm->requires_update & UPDATE_PULSE_ASLEEP){
+ pm->configcr->wValue = cpu_to_le16( SET_PULSE_ASLEEP );
+ pm->configcr->wIndex = cpu_to_le16( pm->pulse_asleep ? 1 : 0 );
+ pm->requires_update &= ~UPDATE_PULSE_ASLEEP;
+ }else if (pm->requires_update & UPDATE_PULSE_AWAKE){
+ pm->configcr->wValue = cpu_to_le16( SET_PULSE_AWAKE );
+ pm->configcr->wIndex = cpu_to_le16( pm->pulse_awake ? 1 : 0 );
+ pm->requires_update &= ~UPDATE_PULSE_AWAKE;
+ }else if (pm->requires_update & UPDATE_PULSE_MODE){
+ int op, arg;
+ /* the powermate takes an operation and an argument for its pulse algorithm.
+ the operation can be:
+ 0: divide the speed
+ 1: pulse at normal speed
+ 2: multiply the speed
+ the argument only has an effect for operations 0 and 2, and ranges between
+ 1 (least effect) to 255 (maximum effect).
+
+ thus, several states are equivalent and are coalesced into one state.
+
+ we map this onto a range from 0 to 510, with:
+ 0 -- 254 -- use divide (0 = slowest)
+ 255 -- use normal speed
+ 256 -- 510 -- use multiple (510 = fastest).
+
+ Only values of 'arg' quite close to 255 are particularly useful/spectacular.
+ */
+ if (pm->pulse_speed < 255) {
+ op = 0; // divide
+ arg = 255 - pm->pulse_speed;
+ } else if (pm->pulse_speed > 255) {
+ op = 2; // multiply
+ arg = pm->pulse_speed - 255;
+ } else {
+ op = 1; // normal speed
+ arg = 0; // can be any value
+ }
+ pm->configcr->wValue = cpu_to_le16( (pm->pulse_table << 8) | SET_PULSE_MODE );
+ pm->configcr->wIndex = cpu_to_le16( (arg << 8) | op );
+ pm->requires_update &= ~UPDATE_PULSE_MODE;
+ } else if (pm->requires_update & UPDATE_STATIC_BRIGHTNESS) {
+ pm->configcr->wValue = cpu_to_le16( SET_STATIC_BRIGHTNESS );
+ pm->configcr->wIndex = cpu_to_le16( pm->static_brightness );
+ pm->requires_update &= ~UPDATE_STATIC_BRIGHTNESS;
+ } else {
+ printk(KERN_ERR "powermate: unknown update required");
+ pm->requires_update = 0; /* fudge the bug */
+ return;
+ }
+
+/* printk("powermate: %04x %04x\n", pm->configcr->wValue, pm->configcr->wIndex); */
+
+ pm->configcr->bRequestType = 0x41; /* vendor request */
+ pm->configcr->bRequest = 0x01;
+ pm->configcr->wLength = 0;
+
+ usb_fill_control_urb(pm->config, pm->udev, usb_sndctrlpipe(pm->udev, 0),
+ (void *) pm->configcr, NULL, 0,
+ powermate_config_complete, pm);
+ pm->config->setup_dma = pm->configcr_dma;
+ pm->config->transfer_flags |= URB_NO_SETUP_DMA_MAP;
+
+ if (usb_submit_urb(pm->config, GFP_ATOMIC))
+ printk(KERN_ERR "powermate: usb_submit_urb(config) failed");
+}
+
+/* Called when our asynchronous control message completes. We may need to issue another immediately */
+static void powermate_config_complete(struct urb *urb)
+{
+ struct powermate_device *pm = urb->context;
+ unsigned long flags;
+
+ if (urb->status)
+ printk(KERN_ERR "powermate: config urb returned %d\n", urb->status);
+
+ spin_lock_irqsave(&pm->lock, flags);
+ powermate_sync_state(pm);
+ spin_unlock_irqrestore(&pm->lock, flags);
+}
+
+/* Set the LED up as described and begin the sync with the hardware if required */
+static void powermate_pulse_led(struct powermate_device *pm, int static_brightness, int pulse_speed,
+ int pulse_table, int pulse_asleep, int pulse_awake)
+{
+ unsigned long flags;
+
+ if (pulse_speed < 0)
+ pulse_speed = 0;
+ if (pulse_table < 0)
+ pulse_table = 0;
+ if (pulse_speed > 510)
+ pulse_speed = 510;
+ if (pulse_table > 2)
+ pulse_table = 2;
+
+ pulse_asleep = !!pulse_asleep;
+ pulse_awake = !!pulse_awake;
+
+
+ spin_lock_irqsave(&pm->lock, flags);
+
+ /* mark state updates which are required */
+ if (static_brightness != pm->static_brightness) {
+ pm->static_brightness = static_brightness;
+ pm->requires_update |= UPDATE_STATIC_BRIGHTNESS;
+ }
+ if (pulse_asleep != pm->pulse_asleep) {
+ pm->pulse_asleep = pulse_asleep;
+ pm->requires_update |= (UPDATE_PULSE_ASLEEP | UPDATE_STATIC_BRIGHTNESS);
+ }
+ if (pulse_awake != pm->pulse_awake) {
+ pm->pulse_awake = pulse_awake;
+ pm->requires_update |= (UPDATE_PULSE_AWAKE | UPDATE_STATIC_BRIGHTNESS);
+ }
+ if (pulse_speed != pm->pulse_speed || pulse_table != pm->pulse_table) {
+ pm->pulse_speed = pulse_speed;
+ pm->pulse_table = pulse_table;
+ pm->requires_update |= UPDATE_PULSE_MODE;
+ }
+
+ powermate_sync_state(pm);
+
+ spin_unlock_irqrestore(&pm->lock, flags);
+}
+
+/* Callback from the Input layer when an event arrives from userspace to configure the LED */
+static int powermate_input_event(struct input_dev *dev, unsigned int type, unsigned int code, int _value)
+{
+ unsigned int command = (unsigned int)_value;
+ struct powermate_device *pm = input_get_drvdata(dev);
+
+ if (type == EV_MSC && code == MSC_PULSELED){
+ /*
+ bits 0- 7: 8 bits: LED brightness
+ bits 8-16: 9 bits: pulsing speed modifier (0 ... 510); 0-254 = slower, 255 = standard, 256-510 = faster.
+ bits 17-18: 2 bits: pulse table (0, 1, 2 valid)
+ bit 19: 1 bit : pulse whilst asleep?
+ bit 20: 1 bit : pulse constantly?
+ */
+ int static_brightness = command & 0xFF; // bits 0-7
+ int pulse_speed = (command >> 8) & 0x1FF; // bits 8-16
+ int pulse_table = (command >> 17) & 0x3; // bits 17-18
+ int pulse_asleep = (command >> 19) & 0x1; // bit 19
+ int pulse_awake = (command >> 20) & 0x1; // bit 20
+
+ powermate_pulse_led(pm, static_brightness, pulse_speed, pulse_table, pulse_asleep, pulse_awake);
+ }
+
+ return 0;
+}
+
+static int powermate_alloc_buffers(struct usb_device *udev, struct powermate_device *pm)
+{
+ pm->data = usb_buffer_alloc(udev, POWERMATE_PAYLOAD_SIZE_MAX,
+ GFP_ATOMIC, &pm->data_dma);
+ if (!pm->data)
+ return -1;
+
+ pm->configcr = usb_buffer_alloc(udev, sizeof(*(pm->configcr)),
+ GFP_ATOMIC, &pm->configcr_dma);
+ if (!pm->configcr)
+ return -1;
+
+ return 0;
+}
+
+static void powermate_free_buffers(struct usb_device *udev, struct powermate_device *pm)
+{
+ usb_buffer_free(udev, POWERMATE_PAYLOAD_SIZE_MAX,
+ pm->data, pm->data_dma);
+ usb_buffer_free(udev, sizeof(*(pm->configcr)),
+ pm->configcr, pm->configcr_dma);
+}
+
+/* Called whenever a USB device matching one in our supported devices table is connected */
+static int powermate_probe(struct usb_interface *intf, const struct usb_device_id *id)
+{
+ struct usb_device *udev = interface_to_usbdev (intf);
+ struct usb_host_interface *interface;
+ struct usb_endpoint_descriptor *endpoint;
+ struct powermate_device *pm;
+ struct input_dev *input_dev;
+ int pipe, maxp;
+ int error = -ENOMEM;
+
+ interface = intf->cur_altsetting;
+ endpoint = &interface->endpoint[0].desc;
+ if (!usb_endpoint_is_int_in(endpoint))
+ return -EIO;
+
+ usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+ 0x0a, USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+ 0, interface->desc.bInterfaceNumber, NULL, 0,
+ USB_CTRL_SET_TIMEOUT);
+
+ pm = kzalloc(sizeof(struct powermate_device), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!pm || !input_dev)
+ goto fail1;
+
+ if (powermate_alloc_buffers(udev, pm))
+ goto fail2;
+
+ pm->irq = usb_alloc_urb(0, GFP_KERNEL);
+ if (!pm->irq)
+ goto fail2;
+
+ pm->config = usb_alloc_urb(0, GFP_KERNEL);
+ if (!pm->config)
+ goto fail3;
+
+ pm->udev = udev;
+ pm->input = input_dev;
+
+ usb_make_path(udev, pm->phys, sizeof(pm->phys));
+ strlcpy(pm->phys, "/input0", sizeof(pm->phys));
+
+ spin_lock_init(&pm->lock);
+
+ switch (le16_to_cpu(udev->descriptor.idProduct)) {
+ case POWERMATE_PRODUCT_NEW:
+ input_dev->name = pm_name_powermate;
+ break;
+ case POWERMATE_PRODUCT_OLD:
+ input_dev->name = pm_name_soundknob;
+ break;
+ default:
+ input_dev->name = pm_name_soundknob;
+ printk(KERN_WARNING "powermate: unknown product id %04x\n",
+ le16_to_cpu(udev->descriptor.idProduct));
+ }
+
+ input_dev->phys = pm->phys;
+ usb_to_input_id(udev, &input_dev->id);
+ input_dev->dev.parent = &intf->dev;
+
+ input_set_drvdata(input_dev, pm);
+
+ input_dev->event = powermate_input_event;
+
+ input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL) |
+ BIT_MASK(EV_MSC);
+ input_dev->keybit[BIT_WORD(BTN_0)] = BIT_MASK(BTN_0);
+ input_dev->relbit[BIT_WORD(REL_DIAL)] = BIT_MASK(REL_DIAL);
+ input_dev->mscbit[BIT_WORD(MSC_PULSELED)] = BIT_MASK(MSC_PULSELED);
+
+ /* get a handle to the interrupt data pipe */
+ pipe = usb_rcvintpipe(udev, endpoint->bEndpointAddress);
+ maxp = usb_maxpacket(udev, pipe, usb_pipeout(pipe));
+
+ if (maxp < POWERMATE_PAYLOAD_SIZE_MIN || maxp > POWERMATE_PAYLOAD_SIZE_MAX) {
+ printk(KERN_WARNING "powermate: Expected payload of %d--%d bytes, found %d bytes!\n",
+ POWERMATE_PAYLOAD_SIZE_MIN, POWERMATE_PAYLOAD_SIZE_MAX, maxp);
+ maxp = POWERMATE_PAYLOAD_SIZE_MAX;
+ }
+
+ usb_fill_int_urb(pm->irq, udev, pipe, pm->data,
+ maxp, powermate_irq,
+ pm, endpoint->bInterval);
+ pm->irq->transfer_dma = pm->data_dma;
+ pm->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+ /* register our interrupt URB with the USB system */
+ if (usb_submit_urb(pm->irq, GFP_KERNEL)) {
+ error = -EIO;
+ goto fail4;
+ }
+
+ error = input_register_device(pm->input);
+ if (error)
+ goto fail5;
+
+
+ /* force an update of everything */
+ pm->requires_update = UPDATE_PULSE_ASLEEP | UPDATE_PULSE_AWAKE | UPDATE_PULSE_MODE | UPDATE_STATIC_BRIGHTNESS;
+ powermate_pulse_led(pm, 0x80, 255, 0, 1, 0); // set default pulse parameters
+
+ usb_set_intfdata(intf, pm);
+ return 0;
+
+ fail5: usb_kill_urb(pm->irq);
+ fail4: usb_free_urb(pm->config);
+ fail3: usb_free_urb(pm->irq);
+ fail2: powermate_free_buffers(udev, pm);
+ fail1: input_free_device(input_dev);
+ kfree(pm);
+ return error;
+}
+
+/* Called when a USB device we've accepted ownership of is removed */
+static void powermate_disconnect(struct usb_interface *intf)
+{
+ struct powermate_device *pm = usb_get_intfdata (intf);
+
+ usb_set_intfdata(intf, NULL);
+ if (pm) {
+ pm->requires_update = 0;
+ usb_kill_urb(pm->irq);
+ input_unregister_device(pm->input);
+ usb_free_urb(pm->irq);
+ usb_free_urb(pm->config);
+ powermate_free_buffers(interface_to_usbdev(intf), pm);
+
+ kfree(pm);
+ }
+}
+
+static struct usb_device_id powermate_devices [] = {
+ { USB_DEVICE(POWERMATE_VENDOR, POWERMATE_PRODUCT_NEW) },
+ { USB_DEVICE(POWERMATE_VENDOR, POWERMATE_PRODUCT_OLD) },
+ { USB_DEVICE(CONTOUR_VENDOR, CONTOUR_JOG) },
+ { } /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE (usb, powermate_devices);
+
+static struct usb_driver powermate_driver = {
+ .name = "powermate",
+ .probe = powermate_probe,
+ .disconnect = powermate_disconnect,
+ .id_table = powermate_devices,
+};
+
+static int __init powermate_init(void)
+{
+ return usb_register(&powermate_driver);
+}
+
+static void __exit powermate_cleanup(void)
+{
+ usb_deregister(&powermate_driver);
+}
+
+module_init(powermate_init);
+module_exit(powermate_cleanup);
+
+MODULE_AUTHOR( "William R Sowerbutts" );
+MODULE_DESCRIPTION( "Griffin Technology, Inc PowerMate driver" );
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/misc/sgi_btns.c b/drivers/input/misc/sgi_btns.c
new file mode 100644
index 0000000..be3a15f
--- /dev/null
+++ b/drivers/input/misc/sgi_btns.c
@@ -0,0 +1,179 @@
+/*
+ * SGI Volume Button interface driver
+ *
+ * Copyright (C) 2008 Thomas Bogendoerfer <tsbogend@alpha.franken.de>
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#include <linux/init.h>
+#include <linux/input-polldev.h>
+#include <linux/ioport.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#ifdef CONFIG_SGI_IP22
+#include <asm/sgi/ioc.h>
+
+static inline u8 button_status(void)
+{
+ u8 status;
+
+ status = readb(&sgioc->panel) ^ 0xa0;
+ return ((status & 0x80) >> 6) | ((status & 0x20) >> 5);
+}
+#endif
+
+#ifdef CONFIG_SGI_IP32
+#include <asm/ip32/mace.h>
+
+static inline u8 button_status(void)
+{
+ u64 status;
+
+ status = readq(&mace->perif.audio.control);
+ writeq(status & ~(3U << 23), &mace->perif.audio.control);
+
+ return (status >> 23) & 3;
+}
+#endif
+
+#define BUTTONS_POLL_INTERVAL 30 /* msec */
+#define BUTTONS_COUNT_THRESHOLD 3
+
+static const unsigned short sgi_map[] = {
+ KEY_VOLUMEDOWN,
+ KEY_VOLUMEUP
+};
+
+struct buttons_dev {
+ struct input_polled_dev *poll_dev;
+ unsigned short keymap[ARRAY_SIZE(sgi_map)];
+ int count[ARRAY_SIZE(sgi_map)];
+};
+
+static void handle_buttons(struct input_polled_dev *dev)
+{
+ struct buttons_dev *bdev = dev->private;
+ struct input_dev *input = dev->input;
+ u8 status;
+ int i;
+
+ status = button_status();
+
+ for (i = 0; i < ARRAY_SIZE(bdev->keymap); i++) {
+ if (status & (1U << i)) {
+ if (++bdev->count[i] == BUTTONS_COUNT_THRESHOLD) {
+ input_event(input, EV_MSC, MSC_SCAN, i);
+ input_report_key(input, bdev->keymap[i], 1);
+ input_sync(input);
+ }
+ } else {
+ if (bdev->count[i] >= BUTTONS_COUNT_THRESHOLD) {
+ input_event(input, EV_MSC, MSC_SCAN, i);
+ input_report_key(input, bdev->keymap[i], 0);
+ input_sync(input);
+ }
+ bdev->count[i] = 0;
+ }
+ }
+}
+
+static int __devinit sgi_buttons_probe(struct platform_device *pdev)
+{
+ struct buttons_dev *bdev;
+ struct input_polled_dev *poll_dev;
+ struct input_dev *input;
+ int error, i;
+
+ bdev = kzalloc(sizeof(struct buttons_dev), GFP_KERNEL);
+ poll_dev = input_allocate_polled_device();
+ if (!bdev || !poll_dev) {
+ error = -ENOMEM;
+ goto err_free_mem;
+ }
+
+ memcpy(bdev->keymap, sgi_map, sizeof(bdev->keymap));
+
+ poll_dev->private = bdev;
+ poll_dev->poll = handle_buttons;
+ poll_dev->poll_interval = BUTTONS_POLL_INTERVAL;
+
+ input = poll_dev->input;
+ input->name = "SGI buttons";
+ input->phys = "sgi/input0";
+ input->id.bustype = BUS_HOST;
+ input->dev.parent = &pdev->dev;
+
+ input->keycode = bdev->keymap;
+ input->keycodemax = ARRAY_SIZE(bdev->keymap);
+ input->keycodesize = sizeof(unsigned short);
+
+ input_set_capability(input, EV_MSC, MSC_SCAN);
+ __set_bit(EV_KEY, input->evbit);
+ for (i = 0; i < ARRAY_SIZE(sgi_map); i++)
+ __set_bit(bdev->keymap[i], input->keybit);
+ __clear_bit(KEY_RESERVED, input->keybit);
+
+ bdev->poll_dev = poll_dev;
+ dev_set_drvdata(&pdev->dev, bdev);
+
+ error = input_register_polled_device(poll_dev);
+ if (error)
+ goto err_free_mem;
+
+ return 0;
+
+ err_free_mem:
+ input_free_polled_device(poll_dev);
+ kfree(bdev);
+ dev_set_drvdata(&pdev->dev, NULL);
+ return error;
+}
+
+static int __devexit sgi_buttons_remove(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct buttons_dev *bdev = dev_get_drvdata(dev);
+
+ input_unregister_polled_device(bdev->poll_dev);
+ input_free_polled_device(bdev->poll_dev);
+ kfree(bdev);
+ dev_set_drvdata(dev, NULL);
+
+ return 0;
+}
+
+static struct platform_driver sgi_buttons_driver = {
+ .probe = sgi_buttons_probe,
+ .remove = __devexit_p(sgi_buttons_remove),
+ .driver = {
+ .name = "sgibtns",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init sgi_buttons_init(void)
+{
+ return platform_driver_register(&sgi_buttons_driver);
+}
+
+static void __exit sgi_buttons_exit(void)
+{
+ platform_driver_unregister(&sgi_buttons_driver);
+}
+
+MODULE_LICENSE("GPL");
+module_init(sgi_buttons_init);
+module_exit(sgi_buttons_exit);
diff --git a/drivers/input/misc/sparcspkr.c b/drivers/input/misc/sparcspkr.c
new file mode 100644
index 0000000..c4f4231
--- /dev/null
+++ b/drivers/input/misc/sparcspkr.c
@@ -0,0 +1,369 @@
+/*
+ * Driver for PC-speaker like devices found on various Sparc systems.
+ *
+ * Copyright (c) 2002 Vojtech Pavlik
+ * Copyright (c) 2002, 2006, 2008 David S. Miller (davem@davemloft.net)
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/of_device.h>
+
+#include <asm/io.h>
+
+MODULE_AUTHOR("David S. Miller <davem@davemloft.net>");
+MODULE_DESCRIPTION("Sparc Speaker beeper driver");
+MODULE_LICENSE("GPL");
+
+struct grover_beep_info {
+ void __iomem *freq_regs;
+ void __iomem *enable_reg;
+};
+
+struct bbc_beep_info {
+ u32 clock_freq;
+ void __iomem *regs;
+};
+
+struct sparcspkr_state {
+ const char *name;
+ int (*event)(struct input_dev *dev, unsigned int type, unsigned int code, int value);
+ spinlock_t lock;
+ struct input_dev *input_dev;
+ union {
+ struct grover_beep_info grover;
+ struct bbc_beep_info bbc;
+ } u;
+};
+
+static u32 bbc_count_to_reg(struct bbc_beep_info *info, unsigned int count)
+{
+ u32 val, clock_freq = info->clock_freq;
+ int i;
+
+ if (!count)
+ return 0;
+
+ if (count <= clock_freq >> 20)
+ return 1 << 18;
+
+ if (count >= clock_freq >> 12)
+ return 1 << 10;
+
+ val = 1 << 18;
+ for (i = 19; i >= 11; i--) {
+ val >>= 1;
+ if (count <= clock_freq >> i)
+ break;
+ }
+
+ return val;
+}
+
+static int bbc_spkr_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
+{
+ struct sparcspkr_state *state = dev_get_drvdata(dev->dev.parent);
+ struct bbc_beep_info *info = &state->u.bbc;
+ unsigned int count = 0;
+ unsigned long flags;
+
+ if (type != EV_SND)
+ return -1;
+
+ switch (code) {
+ case SND_BELL: if (value) value = 1000;
+ case SND_TONE: break;
+ default: return -1;
+ }
+
+ if (value > 20 && value < 32767)
+ count = 1193182 / value;
+
+ count = bbc_count_to_reg(info, count);
+
+ spin_lock_irqsave(&state->lock, flags);
+
+ if (count) {
+ outb(0x01, info->regs + 0);
+ outb(0x00, info->regs + 2);
+ outb((count >> 16) & 0xff, info->regs + 3);
+ outb((count >> 8) & 0xff, info->regs + 4);
+ outb(0x00, info->regs + 5);
+ } else {
+ outb(0x00, info->regs + 0);
+ }
+
+ spin_unlock_irqrestore(&state->lock, flags);
+
+ return 0;
+}
+
+static int grover_spkr_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
+{
+ struct sparcspkr_state *state = dev_get_drvdata(dev->dev.parent);
+ struct grover_beep_info *info = &state->u.grover;
+ unsigned int count = 0;
+ unsigned long flags;
+
+ if (type != EV_SND)
+ return -1;
+
+ switch (code) {
+ case SND_BELL: if (value) value = 1000;
+ case SND_TONE: break;
+ default: return -1;
+ }
+
+ if (value > 20 && value < 32767)
+ count = 1193182 / value;
+
+ spin_lock_irqsave(&state->lock, flags);
+
+ if (count) {
+ /* enable counter 2 */
+ outb(inb(info->enable_reg) | 3, info->enable_reg);
+ /* set command for counter 2, 2 byte write */
+ outb(0xB6, info->freq_regs + 1);
+ /* select desired HZ */
+ outb(count & 0xff, info->freq_regs + 0);
+ outb((count >> 8) & 0xff, info->freq_regs + 0);
+ } else {
+ /* disable counter 2 */
+ outb(inb_p(info->enable_reg) & 0xFC, info->enable_reg);
+ }
+
+ spin_unlock_irqrestore(&state->lock, flags);
+
+ return 0;
+}
+
+static int __devinit sparcspkr_probe(struct device *dev)
+{
+ struct sparcspkr_state *state = dev_get_drvdata(dev);
+ struct input_dev *input_dev;
+ int error;
+
+ input_dev = input_allocate_device();
+ if (!input_dev)
+ return -ENOMEM;
+
+ input_dev->name = state->name;
+ input_dev->phys = "sparc/input0";
+ input_dev->id.bustype = BUS_ISA;
+ input_dev->id.vendor = 0x001f;
+ input_dev->id.product = 0x0001;
+ input_dev->id.version = 0x0100;
+ input_dev->dev.parent = dev;
+
+ input_dev->evbit[0] = BIT_MASK(EV_SND);
+ input_dev->sndbit[0] = BIT_MASK(SND_BELL) | BIT_MASK(SND_TONE);
+
+ input_dev->event = state->event;
+
+ error = input_register_device(input_dev);
+ if (error) {
+ input_free_device(input_dev);
+ return error;
+ }
+
+ state->input_dev = input_dev;
+
+ return 0;
+}
+
+static int sparcspkr_shutdown(struct of_device *dev)
+{
+ struct sparcspkr_state *state = dev_get_drvdata(&dev->dev);
+ struct input_dev *input_dev = state->input_dev;
+
+ /* turn off the speaker */
+ state->event(input_dev, EV_SND, SND_BELL, 0);
+
+ return 0;
+}
+
+static int __devinit bbc_beep_probe(struct of_device *op, const struct of_device_id *match)
+{
+ struct sparcspkr_state *state;
+ struct bbc_beep_info *info;
+ struct device_node *dp;
+ int err = -ENOMEM;
+
+ state = kzalloc(sizeof(*state), GFP_KERNEL);
+ if (!state)
+ goto out_err;
+
+ state->name = "Sparc BBC Speaker";
+ state->event = bbc_spkr_event;
+ spin_lock_init(&state->lock);
+
+ dp = of_find_node_by_path("/");
+ err = -ENODEV;
+ if (!dp)
+ goto out_free;
+
+ info = &state->u.bbc;
+ info->clock_freq = of_getintprop_default(dp, "clock-frequency", 0);
+ if (!info->clock_freq)
+ goto out_free;
+
+ info->regs = of_ioremap(&op->resource[0], 0, 6, "bbc beep");
+ if (!info->regs)
+ goto out_free;
+
+ dev_set_drvdata(&op->dev, state);
+
+ err = sparcspkr_probe(&op->dev);
+ if (err)
+ goto out_clear_drvdata;
+
+ return 0;
+
+out_clear_drvdata:
+ dev_set_drvdata(&op->dev, NULL);
+ of_iounmap(&op->resource[0], info->regs, 6);
+
+out_free:
+ kfree(state);
+out_err:
+ return err;
+}
+
+static int bbc_remove(struct of_device *op)
+{
+ struct sparcspkr_state *state = dev_get_drvdata(&op->dev);
+ struct input_dev *input_dev = state->input_dev;
+ struct bbc_beep_info *info = &state->u.bbc;
+
+ /* turn off the speaker */
+ state->event(input_dev, EV_SND, SND_BELL, 0);
+
+ input_unregister_device(input_dev);
+
+ of_iounmap(&op->resource[0], info->regs, 6);
+
+ dev_set_drvdata(&op->dev, NULL);
+ kfree(state);
+
+ return 0;
+}
+
+static const struct of_device_id bbc_beep_match[] = {
+ {
+ .name = "beep",
+ .compatible = "SUNW,bbc-beep",
+ },
+ {},
+};
+
+static struct of_platform_driver bbc_beep_driver = {
+ .name = "bbcbeep",
+ .match_table = bbc_beep_match,
+ .probe = bbc_beep_probe,
+ .remove = __devexit_p(bbc_remove),
+ .shutdown = sparcspkr_shutdown,
+};
+
+static int __devinit grover_beep_probe(struct of_device *op, const struct of_device_id *match)
+{
+ struct sparcspkr_state *state;
+ struct grover_beep_info *info;
+ int err = -ENOMEM;
+
+ state = kzalloc(sizeof(*state), GFP_KERNEL);
+ if (!state)
+ goto out_err;
+
+ state->name = "Sparc Grover Speaker";
+ state->event = grover_spkr_event;
+ spin_lock_init(&state->lock);
+
+ info = &state->u.grover;
+ info->freq_regs = of_ioremap(&op->resource[2], 0, 2, "grover beep freq");
+ if (!info->freq_regs)
+ goto out_free;
+
+ info->enable_reg = of_ioremap(&op->resource[3], 0, 1, "grover beep enable");
+ if (!info->enable_reg)
+ goto out_unmap_freq_regs;
+
+ dev_set_drvdata(&op->dev, state);
+
+ err = sparcspkr_probe(&op->dev);
+ if (err)
+ goto out_clear_drvdata;
+
+ return 0;
+
+out_clear_drvdata:
+ dev_set_drvdata(&op->dev, NULL);
+ of_iounmap(&op->resource[3], info->enable_reg, 1);
+
+out_unmap_freq_regs:
+ of_iounmap(&op->resource[2], info->freq_regs, 2);
+out_free:
+ kfree(state);
+out_err:
+ return err;
+}
+
+static int grover_remove(struct of_device *op)
+{
+ struct sparcspkr_state *state = dev_get_drvdata(&op->dev);
+ struct grover_beep_info *info = &state->u.grover;
+ struct input_dev *input_dev = state->input_dev;
+
+ /* turn off the speaker */
+ state->event(input_dev, EV_SND, SND_BELL, 0);
+
+ input_unregister_device(input_dev);
+
+ of_iounmap(&op->resource[3], info->enable_reg, 1);
+ of_iounmap(&op->resource[2], info->freq_regs, 2);
+
+ dev_set_drvdata(&op->dev, NULL);
+ kfree(state);
+
+ return 0;
+}
+
+static const struct of_device_id grover_beep_match[] = {
+ {
+ .name = "beep",
+ .compatible = "SUNW,smbus-beep",
+ },
+ {},
+};
+
+static struct of_platform_driver grover_beep_driver = {
+ .name = "groverbeep",
+ .match_table = grover_beep_match,
+ .probe = grover_beep_probe,
+ .remove = __devexit_p(grover_remove),
+ .shutdown = sparcspkr_shutdown,
+};
+
+static int __init sparcspkr_init(void)
+{
+ int err = of_register_driver(&bbc_beep_driver,
+ &of_platform_bus_type);
+
+ if (!err) {
+ err = of_register_driver(&grover_beep_driver,
+ &of_platform_bus_type);
+ if (err)
+ of_unregister_driver(&bbc_beep_driver);
+ }
+
+ return err;
+}
+
+static void __exit sparcspkr_exit(void)
+{
+ of_unregister_driver(&bbc_beep_driver);
+ of_unregister_driver(&grover_beep_driver);
+}
+
+module_init(sparcspkr_init);
+module_exit(sparcspkr_exit);
diff --git a/drivers/input/misc/uinput.c b/drivers/input/misc/uinput.c
new file mode 100644
index 0000000..223d56d
--- /dev/null
+++ b/drivers/input/misc/uinput.c
@@ -0,0 +1,662 @@
+/*
+ * User level driver support for input subsystem
+ *
+ * Heavily based on evdev.c by Vojtech Pavlik
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Author: Aristeu Sergio Rozanski Filho <aris@cathedrallabs.org>
+ *
+ * Changes/Revisions:
+ * 0.3 09/04/2006 (Anssi Hannula <anssi.hannula@gmail.com>)
+ * - updated ff support for the changes in kernel interface
+ * - added MODULE_VERSION
+ * 0.2 16/10/2004 (Micah Dowty <micah@navi.cx>)
+ * - added force feedback support
+ * - added UI_SET_PHYS
+ * 0.1 20/06/2002
+ * - first public version
+ */
+#include <linux/poll.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/smp_lock.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/uinput.h>
+
+static int uinput_dev_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
+{
+ struct uinput_device *udev = input_get_drvdata(dev);
+
+ udev->buff[udev->head].type = type;
+ udev->buff[udev->head].code = code;
+ udev->buff[udev->head].value = value;
+ do_gettimeofday(&udev->buff[udev->head].time);
+ udev->head = (udev->head + 1) % UINPUT_BUFFER_SIZE;
+
+ wake_up_interruptible(&udev->waitq);
+
+ return 0;
+}
+
+static int uinput_request_alloc_id(struct uinput_device *udev, struct uinput_request *request)
+{
+ /* Atomically allocate an ID for the given request. Returns 0 on success. */
+ int id;
+ int err = -1;
+
+ spin_lock(&udev->requests_lock);
+
+ for (id = 0; id < UINPUT_NUM_REQUESTS; id++)
+ if (!udev->requests[id]) {
+ request->id = id;
+ udev->requests[id] = request;
+ err = 0;
+ break;
+ }
+
+ spin_unlock(&udev->requests_lock);
+ return err;
+}
+
+static struct uinput_request* uinput_request_find(struct uinput_device *udev, int id)
+{
+ /* Find an input request, by ID. Returns NULL if the ID isn't valid. */
+ if (id >= UINPUT_NUM_REQUESTS || id < 0)
+ return NULL;
+ return udev->requests[id];
+}
+
+static inline int uinput_request_reserve_slot(struct uinput_device *udev, struct uinput_request *request)
+{
+ /* Allocate slot. If none are available right away, wait. */
+ return wait_event_interruptible(udev->requests_waitq,
+ !uinput_request_alloc_id(udev, request));
+}
+
+static void uinput_request_done(struct uinput_device *udev, struct uinput_request *request)
+{
+ /* Mark slot as available */
+ udev->requests[request->id] = NULL;
+ wake_up(&udev->requests_waitq);
+
+ complete(&request->done);
+}
+
+static int uinput_request_submit(struct input_dev *dev, struct uinput_request *request)
+{
+ /* Tell our userspace app about this new request by queueing an input event */
+ uinput_dev_event(dev, EV_UINPUT, request->code, request->id);
+
+ /* Wait for the request to complete */
+ wait_for_completion(&request->done);
+ return request->retval;
+}
+
+static void uinput_dev_set_gain(struct input_dev *dev, u16 gain)
+{
+ uinput_dev_event(dev, EV_FF, FF_GAIN, gain);
+}
+
+static void uinput_dev_set_autocenter(struct input_dev *dev, u16 magnitude)
+{
+ uinput_dev_event(dev, EV_FF, FF_AUTOCENTER, magnitude);
+}
+
+static int uinput_dev_playback(struct input_dev *dev, int effect_id, int value)
+{
+ return uinput_dev_event(dev, EV_FF, effect_id, value);
+}
+
+static int uinput_dev_upload_effect(struct input_dev *dev, struct ff_effect *effect, struct ff_effect *old)
+{
+ struct uinput_request request;
+ int retval;
+
+ request.id = -1;
+ init_completion(&request.done);
+ request.code = UI_FF_UPLOAD;
+ request.u.upload.effect = effect;
+ request.u.upload.old = old;
+
+ retval = uinput_request_reserve_slot(input_get_drvdata(dev), &request);
+ if (!retval)
+ retval = uinput_request_submit(dev, &request);
+
+ return retval;
+}
+
+static int uinput_dev_erase_effect(struct input_dev *dev, int effect_id)
+{
+ struct uinput_request request;
+ int retval;
+
+ if (!test_bit(EV_FF, dev->evbit))
+ return -ENOSYS;
+
+ request.id = -1;
+ init_completion(&request.done);
+ request.code = UI_FF_ERASE;
+ request.u.effect_id = effect_id;
+
+ retval = uinput_request_reserve_slot(input_get_drvdata(dev), &request);
+ if (!retval)
+ retval = uinput_request_submit(dev, &request);
+
+ return retval;
+}
+
+static void uinput_destroy_device(struct uinput_device *udev)
+{
+ const char *name, *phys;
+
+ if (udev->dev) {
+ name = udev->dev->name;
+ phys = udev->dev->phys;
+ if (udev->state == UIST_CREATED)
+ input_unregister_device(udev->dev);
+ else
+ input_free_device(udev->dev);
+ kfree(name);
+ kfree(phys);
+ udev->dev = NULL;
+ }
+
+ udev->state = UIST_NEW_DEVICE;
+}
+
+static int uinput_create_device(struct uinput_device *udev)
+{
+ struct input_dev *dev = udev->dev;
+ int error;
+
+ if (udev->state != UIST_SETUP_COMPLETE) {
+ printk(KERN_DEBUG "%s: write device info first\n", UINPUT_NAME);
+ return -EINVAL;
+ }
+
+ if (udev->ff_effects_max) {
+ error = input_ff_create(dev, udev->ff_effects_max);
+ if (error)
+ goto fail1;
+
+ dev->ff->upload = uinput_dev_upload_effect;
+ dev->ff->erase = uinput_dev_erase_effect;
+ dev->ff->playback = uinput_dev_playback;
+ dev->ff->set_gain = uinput_dev_set_gain;
+ dev->ff->set_autocenter = uinput_dev_set_autocenter;
+ }
+
+ error = input_register_device(udev->dev);
+ if (error)
+ goto fail2;
+
+ udev->state = UIST_CREATED;
+
+ return 0;
+
+ fail2: input_ff_destroy(dev);
+ fail1: uinput_destroy_device(udev);
+ return error;
+}
+
+static int uinput_open(struct inode *inode, struct file *file)
+{
+ struct uinput_device *newdev;
+
+ newdev = kzalloc(sizeof(struct uinput_device), GFP_KERNEL);
+ if (!newdev)
+ return -ENOMEM;
+
+ lock_kernel();
+ mutex_init(&newdev->mutex);
+ spin_lock_init(&newdev->requests_lock);
+ init_waitqueue_head(&newdev->requests_waitq);
+ init_waitqueue_head(&newdev->waitq);
+ newdev->state = UIST_NEW_DEVICE;
+
+ file->private_data = newdev;
+ unlock_kernel();
+
+ return 0;
+}
+
+static int uinput_validate_absbits(struct input_dev *dev)
+{
+ unsigned int cnt;
+ int retval = 0;
+
+ for (cnt = 0; cnt < ABS_MAX + 1; cnt++) {
+ if (!test_bit(cnt, dev->absbit))
+ continue;
+
+ if ((dev->absmax[cnt] <= dev->absmin[cnt])) {
+ printk(KERN_DEBUG
+ "%s: invalid abs[%02x] min:%d max:%d\n",
+ UINPUT_NAME, cnt,
+ dev->absmin[cnt], dev->absmax[cnt]);
+ retval = -EINVAL;
+ break;
+ }
+
+ if (dev->absflat[cnt] > (dev->absmax[cnt] - dev->absmin[cnt])) {
+ printk(KERN_DEBUG
+ "%s: absflat[%02x] out of range: %d "
+ "(min:%d/max:%d)\n",
+ UINPUT_NAME, cnt, dev->absflat[cnt],
+ dev->absmin[cnt], dev->absmax[cnt]);
+ retval = -EINVAL;
+ break;
+ }
+ }
+ return retval;
+}
+
+static int uinput_allocate_device(struct uinput_device *udev)
+{
+ udev->dev = input_allocate_device();
+ if (!udev->dev)
+ return -ENOMEM;
+
+ udev->dev->event = uinput_dev_event;
+ input_set_drvdata(udev->dev, udev);
+
+ return 0;
+}
+
+static int uinput_setup_device(struct uinput_device *udev, const char __user *buffer, size_t count)
+{
+ struct uinput_user_dev *user_dev;
+ struct input_dev *dev;
+ char *name;
+ int size;
+ int retval;
+
+ if (count != sizeof(struct uinput_user_dev))
+ return -EINVAL;
+
+ if (!udev->dev) {
+ retval = uinput_allocate_device(udev);
+ if (retval)
+ return retval;
+ }
+
+ dev = udev->dev;
+
+ user_dev = kmalloc(sizeof(struct uinput_user_dev), GFP_KERNEL);
+ if (!user_dev)
+ return -ENOMEM;
+
+ if (copy_from_user(user_dev, buffer, sizeof(struct uinput_user_dev))) {
+ retval = -EFAULT;
+ goto exit;
+ }
+
+ udev->ff_effects_max = user_dev->ff_effects_max;
+
+ size = strnlen(user_dev->name, UINPUT_MAX_NAME_SIZE) + 1;
+ if (!size) {
+ retval = -EINVAL;
+ goto exit;
+ }
+
+ kfree(dev->name);
+ dev->name = name = kmalloc(size, GFP_KERNEL);
+ if (!name) {
+ retval = -ENOMEM;
+ goto exit;
+ }
+ strlcpy(name, user_dev->name, size);
+
+ dev->id.bustype = user_dev->id.bustype;
+ dev->id.vendor = user_dev->id.vendor;
+ dev->id.product = user_dev->id.product;
+ dev->id.version = user_dev->id.version;
+
+ size = sizeof(int) * (ABS_MAX + 1);
+ memcpy(dev->absmax, user_dev->absmax, size);
+ memcpy(dev->absmin, user_dev->absmin, size);
+ memcpy(dev->absfuzz, user_dev->absfuzz, size);
+ memcpy(dev->absflat, user_dev->absflat, size);
+
+ /* check if absmin/absmax/absfuzz/absflat are filled as
+ * told in Documentation/input/input-programming.txt */
+ if (test_bit(EV_ABS, dev->evbit)) {
+ retval = uinput_validate_absbits(dev);
+ if (retval < 0)
+ goto exit;
+ }
+
+ udev->state = UIST_SETUP_COMPLETE;
+ retval = count;
+
+ exit:
+ kfree(user_dev);
+ return retval;
+}
+
+static inline ssize_t uinput_inject_event(struct uinput_device *udev, const char __user *buffer, size_t count)
+{
+ struct input_event ev;
+
+ if (count != sizeof(struct input_event))
+ return -EINVAL;
+
+ if (copy_from_user(&ev, buffer, sizeof(struct input_event)))
+ return -EFAULT;
+
+ input_event(udev->dev, ev.type, ev.code, ev.value);
+
+ return sizeof(struct input_event);
+}
+
+static ssize_t uinput_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos)
+{
+ struct uinput_device *udev = file->private_data;
+ int retval;
+
+ retval = mutex_lock_interruptible(&udev->mutex);
+ if (retval)
+ return retval;
+
+ retval = udev->state == UIST_CREATED ?
+ uinput_inject_event(udev, buffer, count) :
+ uinput_setup_device(udev, buffer, count);
+
+ mutex_unlock(&udev->mutex);
+
+ return retval;
+}
+
+static ssize_t uinput_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos)
+{
+ struct uinput_device *udev = file->private_data;
+ int retval = 0;
+
+ if (udev->state != UIST_CREATED)
+ return -ENODEV;
+
+ if (udev->head == udev->tail && (file->f_flags & O_NONBLOCK))
+ return -EAGAIN;
+
+ retval = wait_event_interruptible(udev->waitq,
+ udev->head != udev->tail || udev->state != UIST_CREATED);
+ if (retval)
+ return retval;
+
+ retval = mutex_lock_interruptible(&udev->mutex);
+ if (retval)
+ return retval;
+
+ if (udev->state != UIST_CREATED) {
+ retval = -ENODEV;
+ goto out;
+ }
+
+ while (udev->head != udev->tail && retval + sizeof(struct input_event) <= count) {
+ if (copy_to_user(buffer + retval, &udev->buff[udev->tail], sizeof(struct input_event))) {
+ retval = -EFAULT;
+ goto out;
+ }
+ udev->tail = (udev->tail + 1) % UINPUT_BUFFER_SIZE;
+ retval += sizeof(struct input_event);
+ }
+
+ out:
+ mutex_unlock(&udev->mutex);
+
+ return retval;
+}
+
+static unsigned int uinput_poll(struct file *file, poll_table *wait)
+{
+ struct uinput_device *udev = file->private_data;
+
+ poll_wait(file, &udev->waitq, wait);
+
+ if (udev->head != udev->tail)
+ return POLLIN | POLLRDNORM;
+
+ return 0;
+}
+
+static int uinput_release(struct inode *inode, struct file *file)
+{
+ struct uinput_device *udev = file->private_data;
+
+ uinput_destroy_device(udev);
+ kfree(udev);
+
+ return 0;
+}
+
+#define uinput_set_bit(_arg, _bit, _max) \
+({ \
+ int __ret = 0; \
+ if (udev->state == UIST_CREATED) \
+ __ret = -EINVAL; \
+ else if ((_arg) > (_max)) \
+ __ret = -EINVAL; \
+ else set_bit((_arg), udev->dev->_bit); \
+ __ret; \
+})
+
+static long uinput_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ int retval;
+ struct uinput_device *udev;
+ void __user *p = (void __user *)arg;
+ struct uinput_ff_upload ff_up;
+ struct uinput_ff_erase ff_erase;
+ struct uinput_request *req;
+ int length;
+ char *phys;
+
+ udev = file->private_data;
+
+ retval = mutex_lock_interruptible(&udev->mutex);
+ if (retval)
+ return retval;
+
+ if (!udev->dev) {
+ retval = uinput_allocate_device(udev);
+ if (retval)
+ goto out;
+ }
+
+ switch (cmd) {
+ case UI_DEV_CREATE:
+ retval = uinput_create_device(udev);
+ break;
+
+ case UI_DEV_DESTROY:
+ uinput_destroy_device(udev);
+ break;
+
+ case UI_SET_EVBIT:
+ retval = uinput_set_bit(arg, evbit, EV_MAX);
+ break;
+
+ case UI_SET_KEYBIT:
+ retval = uinput_set_bit(arg, keybit, KEY_MAX);
+ break;
+
+ case UI_SET_RELBIT:
+ retval = uinput_set_bit(arg, relbit, REL_MAX);
+ break;
+
+ case UI_SET_ABSBIT:
+ retval = uinput_set_bit(arg, absbit, ABS_MAX);
+ break;
+
+ case UI_SET_MSCBIT:
+ retval = uinput_set_bit(arg, mscbit, MSC_MAX);
+ break;
+
+ case UI_SET_LEDBIT:
+ retval = uinput_set_bit(arg, ledbit, LED_MAX);
+ break;
+
+ case UI_SET_SNDBIT:
+ retval = uinput_set_bit(arg, sndbit, SND_MAX);
+ break;
+
+ case UI_SET_FFBIT:
+ retval = uinput_set_bit(arg, ffbit, FF_MAX);
+ break;
+
+ case UI_SET_SWBIT:
+ retval = uinput_set_bit(arg, swbit, SW_MAX);
+ break;
+
+ case UI_SET_PHYS:
+ if (udev->state == UIST_CREATED) {
+ retval = -EINVAL;
+ goto out;
+ }
+ length = strnlen_user(p, 1024);
+ if (length <= 0) {
+ retval = -EFAULT;
+ break;
+ }
+ kfree(udev->dev->phys);
+ udev->dev->phys = phys = kmalloc(length, GFP_KERNEL);
+ if (!phys) {
+ retval = -ENOMEM;
+ break;
+ }
+ if (copy_from_user(phys, p, length)) {
+ udev->dev->phys = NULL;
+ kfree(phys);
+ retval = -EFAULT;
+ break;
+ }
+ phys[length - 1] = '\0';
+ break;
+
+ case UI_BEGIN_FF_UPLOAD:
+ if (copy_from_user(&ff_up, p, sizeof(ff_up))) {
+ retval = -EFAULT;
+ break;
+ }
+ req = uinput_request_find(udev, ff_up.request_id);
+ if (!(req && req->code == UI_FF_UPLOAD && req->u.upload.effect)) {
+ retval = -EINVAL;
+ break;
+ }
+ ff_up.retval = 0;
+ memcpy(&ff_up.effect, req->u.upload.effect, sizeof(struct ff_effect));
+ if (req->u.upload.old)
+ memcpy(&ff_up.old, req->u.upload.old, sizeof(struct ff_effect));
+ else
+ memset(&ff_up.old, 0, sizeof(struct ff_effect));
+
+ if (copy_to_user(p, &ff_up, sizeof(ff_up))) {
+ retval = -EFAULT;
+ break;
+ }
+ break;
+
+ case UI_BEGIN_FF_ERASE:
+ if (copy_from_user(&ff_erase, p, sizeof(ff_erase))) {
+ retval = -EFAULT;
+ break;
+ }
+ req = uinput_request_find(udev, ff_erase.request_id);
+ if (!(req && req->code == UI_FF_ERASE)) {
+ retval = -EINVAL;
+ break;
+ }
+ ff_erase.retval = 0;
+ ff_erase.effect_id = req->u.effect_id;
+ if (copy_to_user(p, &ff_erase, sizeof(ff_erase))) {
+ retval = -EFAULT;
+ break;
+ }
+ break;
+
+ case UI_END_FF_UPLOAD:
+ if (copy_from_user(&ff_up, p, sizeof(ff_up))) {
+ retval = -EFAULT;
+ break;
+ }
+ req = uinput_request_find(udev, ff_up.request_id);
+ if (!(req && req->code == UI_FF_UPLOAD && req->u.upload.effect)) {
+ retval = -EINVAL;
+ break;
+ }
+ req->retval = ff_up.retval;
+ uinput_request_done(udev, req);
+ break;
+
+ case UI_END_FF_ERASE:
+ if (copy_from_user(&ff_erase, p, sizeof(ff_erase))) {
+ retval = -EFAULT;
+ break;
+ }
+ req = uinput_request_find(udev, ff_erase.request_id);
+ if (!(req && req->code == UI_FF_ERASE)) {
+ retval = -EINVAL;
+ break;
+ }
+ req->retval = ff_erase.retval;
+ uinput_request_done(udev, req);
+ break;
+
+ default:
+ retval = -EINVAL;
+ }
+
+ out:
+ mutex_unlock(&udev->mutex);
+ return retval;
+}
+
+static const struct file_operations uinput_fops = {
+ .owner = THIS_MODULE,
+ .open = uinput_open,
+ .release = uinput_release,
+ .read = uinput_read,
+ .write = uinput_write,
+ .poll = uinput_poll,
+ .unlocked_ioctl = uinput_ioctl,
+};
+
+static struct miscdevice uinput_misc = {
+ .fops = &uinput_fops,
+ .minor = UINPUT_MINOR,
+ .name = UINPUT_NAME,
+};
+
+static int __init uinput_init(void)
+{
+ return misc_register(&uinput_misc);
+}
+
+static void __exit uinput_exit(void)
+{
+ misc_deregister(&uinput_misc);
+}
+
+MODULE_AUTHOR("Aristeu Sergio Rozanski Filho");
+MODULE_DESCRIPTION("User level driver support for input subsystem");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("0.3");
+
+module_init(uinput_init);
+module_exit(uinput_exit);
+
diff --git a/drivers/input/misc/wistron_btns.c b/drivers/input/misc/wistron_btns.c
new file mode 100644
index 0000000..7c8957d
--- /dev/null
+++ b/drivers/input/misc/wistron_btns.c
@@ -0,0 +1,1418 @@
+/*
+ * Wistron laptop button driver
+ * Copyright (C) 2005 Miloslav Trmac <mitr@volny.cz>
+ * Copyright (C) 2005 Bernhard Rosenkraenzer <bero@arklinux.org>
+ * Copyright (C) 2005 Dmitry Torokhov <dtor@mail.ru>
+ *
+ * You can redistribute and/or modify this program under the terms of the
+ * GNU General Public License version 2 as published by the Free Software
+ * Foundation.
+ *
+ * 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.,
+ * 59 Temple Place Suite 330, Boston, MA 02111-1307, USA.
+ */
+#include <linux/io.h>
+#include <linux/dmi.h>
+#include <linux/init.h>
+#include <linux/input-polldev.h>
+#include <linux/interrupt.h>
+#include <linux/jiffies.h>
+#include <linux/kernel.h>
+#include <linux/mc146818rtc.h>
+#include <linux/module.h>
+#include <linux/preempt.h>
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/platform_device.h>
+#include <linux/leds.h>
+
+/* How often we poll keys - msecs */
+#define POLL_INTERVAL_DEFAULT 500 /* when idle */
+#define POLL_INTERVAL_BURST 100 /* when a key was recently pressed */
+
+/* BIOS subsystem IDs */
+#define WIFI 0x35
+#define BLUETOOTH 0x34
+#define MAIL_LED 0x31
+
+MODULE_AUTHOR("Miloslav Trmac <mitr@volny.cz>");
+MODULE_DESCRIPTION("Wistron laptop button driver");
+MODULE_LICENSE("GPL v2");
+MODULE_VERSION("0.3");
+
+static int force; /* = 0; */
+module_param(force, bool, 0);
+MODULE_PARM_DESC(force, "Load even if computer is not in database");
+
+static char *keymap_name; /* = NULL; */
+module_param_named(keymap, keymap_name, charp, 0);
+MODULE_PARM_DESC(keymap, "Keymap name, if it can't be autodetected [generic, 1557/MS2141]");
+
+static struct platform_device *wistron_device;
+
+ /* BIOS interface implementation */
+
+static void __iomem *bios_entry_point; /* BIOS routine entry point */
+static void __iomem *bios_code_map_base;
+static void __iomem *bios_data_map_base;
+
+static u8 cmos_address;
+
+struct regs {
+ u32 eax, ebx, ecx;
+};
+
+static void call_bios(struct regs *regs)
+{
+ unsigned long flags;
+
+ preempt_disable();
+ local_irq_save(flags);
+ asm volatile ("pushl %%ebp;"
+ "movl %7, %%ebp;"
+ "call *%6;"
+ "popl %%ebp"
+ : "=a" (regs->eax), "=b" (regs->ebx), "=c" (regs->ecx)
+ : "0" (regs->eax), "1" (regs->ebx), "2" (regs->ecx),
+ "m" (bios_entry_point), "m" (bios_data_map_base)
+ : "edx", "edi", "esi", "memory");
+ local_irq_restore(flags);
+ preempt_enable();
+}
+
+static ssize_t __init locate_wistron_bios(void __iomem *base)
+{
+ static unsigned char __initdata signature[] =
+ { 0x42, 0x21, 0x55, 0x30 };
+ ssize_t offset;
+
+ for (offset = 0; offset < 0x10000; offset += 0x10) {
+ if (check_signature(base + offset, signature,
+ sizeof(signature)) != 0)
+ return offset;
+ }
+ return -1;
+}
+
+static int __init map_bios(void)
+{
+ void __iomem *base;
+ ssize_t offset;
+ u32 entry_point;
+
+ base = ioremap(0xF0000, 0x10000); /* Can't fail */
+ offset = locate_wistron_bios(base);
+ if (offset < 0) {
+ printk(KERN_ERR "wistron_btns: BIOS entry point not found\n");
+ iounmap(base);
+ return -ENODEV;
+ }
+
+ entry_point = readl(base + offset + 5);
+ printk(KERN_DEBUG
+ "wistron_btns: BIOS signature found at %p, entry point %08X\n",
+ base + offset, entry_point);
+
+ if (entry_point >= 0xF0000) {
+ bios_code_map_base = base;
+ bios_entry_point = bios_code_map_base + (entry_point & 0xFFFF);
+ } else {
+ iounmap(base);
+ bios_code_map_base = ioremap(entry_point & ~0x3FFF, 0x4000);
+ if (bios_code_map_base == NULL) {
+ printk(KERN_ERR
+ "wistron_btns: Can't map BIOS code at %08X\n",
+ entry_point & ~0x3FFF);
+ goto err;
+ }
+ bios_entry_point = bios_code_map_base + (entry_point & 0x3FFF);
+ }
+ /* The Windows driver maps 0x10000 bytes, we keep only one page... */
+ bios_data_map_base = ioremap(0x400, 0xc00);
+ if (bios_data_map_base == NULL) {
+ printk(KERN_ERR "wistron_btns: Can't map BIOS data\n");
+ goto err_code;
+ }
+ return 0;
+
+err_code:
+ iounmap(bios_code_map_base);
+err:
+ return -ENOMEM;
+}
+
+static inline void unmap_bios(void)
+{
+ iounmap(bios_code_map_base);
+ iounmap(bios_data_map_base);
+}
+
+ /* BIOS calls */
+
+static u16 bios_pop_queue(void)
+{
+ struct regs regs;
+
+ memset(&regs, 0, sizeof (regs));
+ regs.eax = 0x9610;
+ regs.ebx = 0x061C;
+ regs.ecx = 0x0000;
+ call_bios(&regs);
+
+ return regs.eax;
+}
+
+static void __devinit bios_attach(void)
+{
+ struct regs regs;
+
+ memset(&regs, 0, sizeof (regs));
+ regs.eax = 0x9610;
+ regs.ebx = 0x012E;
+ call_bios(&regs);
+}
+
+static void bios_detach(void)
+{
+ struct regs regs;
+
+ memset(&regs, 0, sizeof (regs));
+ regs.eax = 0x9610;
+ regs.ebx = 0x002E;
+ call_bios(&regs);
+}
+
+static u8 __devinit bios_get_cmos_address(void)
+{
+ struct regs regs;
+
+ memset(&regs, 0, sizeof (regs));
+ regs.eax = 0x9610;
+ regs.ebx = 0x051C;
+ call_bios(&regs);
+
+ return regs.ecx;
+}
+
+static u16 __devinit bios_get_default_setting(u8 subsys)
+{
+ struct regs regs;
+
+ memset(&regs, 0, sizeof (regs));
+ regs.eax = 0x9610;
+ regs.ebx = 0x0200 | subsys;
+ call_bios(&regs);
+
+ return regs.eax;
+}
+
+static void bios_set_state(u8 subsys, int enable)
+{
+ struct regs regs;
+
+ memset(&regs, 0, sizeof (regs));
+ regs.eax = 0x9610;
+ regs.ebx = (enable ? 0x0100 : 0x0000) | subsys;
+ call_bios(&regs);
+}
+
+/* Hardware database */
+
+struct key_entry {
+ char type; /* See KE_* below */
+ u8 code;
+ union {
+ u16 keycode; /* For KE_KEY */
+ struct { /* For KE_SW */
+ u8 code;
+ u8 value;
+ } sw;
+ };
+};
+
+enum { KE_END, KE_KEY, KE_SW, KE_WIFI, KE_BLUETOOTH };
+
+#define FE_MAIL_LED 0x01
+#define FE_WIFI_LED 0x02
+#define FE_UNTESTED 0x80
+
+static struct key_entry *keymap; /* = NULL; Current key map */
+static int have_wifi;
+static int have_bluetooth;
+static int have_leds;
+
+static int __init dmi_matched(const struct dmi_system_id *dmi)
+{
+ const struct key_entry *key;
+
+ keymap = dmi->driver_data;
+ for (key = keymap; key->type != KE_END; key++) {
+ if (key->type == KE_WIFI)
+ have_wifi = 1;
+ else if (key->type == KE_BLUETOOTH)
+ have_bluetooth = 1;
+ }
+ have_leds = key->code & (FE_MAIL_LED | FE_WIFI_LED);
+
+ return 1;
+}
+
+static struct key_entry keymap_empty[] __initdata = {
+ { KE_END, 0 }
+};
+
+static struct key_entry keymap_fs_amilo_pro_v2000[] __initdata = {
+ { KE_KEY, 0x01, {KEY_HELP} },
+ { KE_KEY, 0x11, {KEY_PROG1} },
+ { KE_KEY, 0x12, {KEY_PROG2} },
+ { KE_WIFI, 0x30 },
+ { KE_KEY, 0x31, {KEY_MAIL} },
+ { KE_KEY, 0x36, {KEY_WWW} },
+ { KE_END, 0 }
+};
+
+static struct key_entry keymap_fs_amilo_pro_v3505[] __initdata = {
+ { KE_KEY, 0x01, {KEY_HELP} }, /* Fn+F1 */
+ { KE_KEY, 0x06, {KEY_DISPLAYTOGGLE} }, /* Fn+F4 */
+ { KE_BLUETOOTH, 0x30 }, /* Fn+F10 */
+ { KE_KEY, 0x31, {KEY_MAIL} }, /* mail button */
+ { KE_KEY, 0x36, {KEY_WWW} }, /* www button */
+ { KE_WIFI, 0x78 }, /* satelite dish button */
+ { KE_END, 0 }
+};
+
+static struct key_entry keymap_fujitsu_n3510[] __initdata = {
+ { KE_KEY, 0x11, {KEY_PROG1} },
+ { KE_KEY, 0x12, {KEY_PROG2} },
+ { KE_KEY, 0x36, {KEY_WWW} },
+ { KE_KEY, 0x31, {KEY_MAIL} },
+ { KE_KEY, 0x71, {KEY_STOPCD} },
+ { KE_KEY, 0x72, {KEY_PLAYPAUSE} },
+ { KE_KEY, 0x74, {KEY_REWIND} },
+ { KE_KEY, 0x78, {KEY_FORWARD} },
+ { KE_END, 0 }
+};
+
+static struct key_entry keymap_wistron_ms2111[] __initdata = {
+ { KE_KEY, 0x11, {KEY_PROG1} },
+ { KE_KEY, 0x12, {KEY_PROG2} },
+ { KE_KEY, 0x13, {KEY_PROG3} },
+ { KE_KEY, 0x31, {KEY_MAIL} },
+ { KE_KEY, 0x36, {KEY_WWW} },
+ { KE_END, FE_MAIL_LED }
+};
+
+static struct key_entry keymap_wistron_md40100[] __initdata = {
+ { KE_KEY, 0x01, {KEY_HELP} },
+ { KE_KEY, 0x02, {KEY_CONFIG} },
+ { KE_KEY, 0x31, {KEY_MAIL} },
+ { KE_KEY, 0x36, {KEY_WWW} },
+ { KE_KEY, 0x37, {KEY_DISPLAYTOGGLE} }, /* Display on/off */
+ { KE_END, FE_MAIL_LED | FE_WIFI_LED | FE_UNTESTED }
+};
+
+static struct key_entry keymap_wistron_ms2141[] __initdata = {
+ { KE_KEY, 0x11, {KEY_PROG1} },
+ { KE_KEY, 0x12, {KEY_PROG2} },
+ { KE_WIFI, 0x30 },
+ { KE_KEY, 0x22, {KEY_REWIND} },
+ { KE_KEY, 0x23, {KEY_FORWARD} },
+ { KE_KEY, 0x24, {KEY_PLAYPAUSE} },
+ { KE_KEY, 0x25, {KEY_STOPCD} },
+ { KE_KEY, 0x31, {KEY_MAIL} },
+ { KE_KEY, 0x36, {KEY_WWW} },
+ { KE_END, 0 }
+};
+
+static struct key_entry keymap_acer_aspire_1500[] __initdata = {
+ { KE_KEY, 0x01, {KEY_HELP} },
+ { KE_KEY, 0x03, {KEY_POWER} },
+ { KE_KEY, 0x11, {KEY_PROG1} },
+ { KE_KEY, 0x12, {KEY_PROG2} },
+ { KE_WIFI, 0x30 },
+ { KE_KEY, 0x31, {KEY_MAIL} },
+ { KE_KEY, 0x36, {KEY_WWW} },
+ { KE_KEY, 0x49, {KEY_CONFIG} },
+ { KE_BLUETOOTH, 0x44 },
+ { KE_END, FE_UNTESTED }
+};
+
+static struct key_entry keymap_acer_aspire_1600[] __initdata = {
+ { KE_KEY, 0x01, {KEY_HELP} },
+ { KE_KEY, 0x03, {KEY_POWER} },
+ { KE_KEY, 0x08, {KEY_MUTE} },
+ { KE_KEY, 0x11, {KEY_PROG1} },
+ { KE_KEY, 0x12, {KEY_PROG2} },
+ { KE_KEY, 0x13, {KEY_PROG3} },
+ { KE_KEY, 0x31, {KEY_MAIL} },
+ { KE_KEY, 0x36, {KEY_WWW} },
+ { KE_KEY, 0x49, {KEY_CONFIG} },
+ { KE_WIFI, 0x30 },
+ { KE_BLUETOOTH, 0x44 },
+ { KE_END, FE_MAIL_LED | FE_UNTESTED }
+};
+
+/* 3020 has been tested */
+static struct key_entry keymap_acer_aspire_5020[] __initdata = {
+ { KE_KEY, 0x01, {KEY_HELP} },
+ { KE_KEY, 0x03, {KEY_POWER} },
+ { KE_KEY, 0x05, {KEY_SWITCHVIDEOMODE} }, /* Display selection */
+ { KE_KEY, 0x11, {KEY_PROG1} },
+ { KE_KEY, 0x12, {KEY_PROG2} },
+ { KE_KEY, 0x31, {KEY_MAIL} },
+ { KE_KEY, 0x36, {KEY_WWW} },
+ { KE_KEY, 0x6a, {KEY_CONFIG} },
+ { KE_WIFI, 0x30 },
+ { KE_BLUETOOTH, 0x44 },
+ { KE_END, FE_MAIL_LED | FE_UNTESTED }
+};
+
+static struct key_entry keymap_acer_travelmate_2410[] __initdata = {
+ { KE_KEY, 0x01, {KEY_HELP} },
+ { KE_KEY, 0x6d, {KEY_POWER} },
+ { KE_KEY, 0x11, {KEY_PROG1} },
+ { KE_KEY, 0x12, {KEY_PROG2} },
+ { KE_KEY, 0x31, {KEY_MAIL} },
+ { KE_KEY, 0x36, {KEY_WWW} },
+ { KE_KEY, 0x6a, {KEY_CONFIG} },
+ { KE_WIFI, 0x30 },
+ { KE_BLUETOOTH, 0x44 },
+ { KE_END, FE_MAIL_LED | FE_UNTESTED }
+};
+
+static struct key_entry keymap_acer_travelmate_110[] __initdata = {
+ { KE_KEY, 0x01, {KEY_HELP} },
+ { KE_KEY, 0x02, {KEY_CONFIG} },
+ { KE_KEY, 0x03, {KEY_POWER} },
+ { KE_KEY, 0x08, {KEY_MUTE} },
+ { KE_KEY, 0x11, {KEY_PROG1} },
+ { KE_KEY, 0x12, {KEY_PROG2} },
+ { KE_KEY, 0x20, {KEY_VOLUMEUP} },
+ { KE_KEY, 0x21, {KEY_VOLUMEDOWN} },
+ { KE_KEY, 0x31, {KEY_MAIL} },
+ { KE_KEY, 0x36, {KEY_WWW} },
+ { KE_SW, 0x4a, {.sw = {SW_LID, 1}} }, /* lid close */
+ { KE_SW, 0x4b, {.sw = {SW_LID, 0}} }, /* lid open */
+ { KE_WIFI, 0x30 },
+ { KE_END, FE_MAIL_LED | FE_UNTESTED }
+};
+
+static struct key_entry keymap_acer_travelmate_300[] __initdata = {
+ { KE_KEY, 0x01, {KEY_HELP} },
+ { KE_KEY, 0x02, {KEY_CONFIG} },
+ { KE_KEY, 0x03, {KEY_POWER} },
+ { KE_KEY, 0x08, {KEY_MUTE} },
+ { KE_KEY, 0x11, {KEY_PROG1} },
+ { KE_KEY, 0x12, {KEY_PROG2} },
+ { KE_KEY, 0x20, {KEY_VOLUMEUP} },
+ { KE_KEY, 0x21, {KEY_VOLUMEDOWN} },
+ { KE_KEY, 0x31, {KEY_MAIL} },
+ { KE_KEY, 0x36, {KEY_WWW} },
+ { KE_WIFI, 0x30 },
+ { KE_BLUETOOTH, 0x44 },
+ { KE_END, FE_MAIL_LED | FE_UNTESTED }
+};
+
+static struct key_entry keymap_acer_travelmate_380[] __initdata = {
+ { KE_KEY, 0x01, {KEY_HELP} },
+ { KE_KEY, 0x02, {KEY_CONFIG} },
+ { KE_KEY, 0x03, {KEY_POWER} }, /* not 370 */
+ { KE_KEY, 0x11, {KEY_PROG1} },
+ { KE_KEY, 0x12, {KEY_PROG2} },
+ { KE_KEY, 0x13, {KEY_PROG3} },
+ { KE_KEY, 0x31, {KEY_MAIL} },
+ { KE_KEY, 0x36, {KEY_WWW} },
+ { KE_WIFI, 0x30 },
+ { KE_END, FE_MAIL_LED | FE_UNTESTED }
+};
+
+/* unusual map */
+static struct key_entry keymap_acer_travelmate_220[] __initdata = {
+ { KE_KEY, 0x01, {KEY_HELP} },
+ { KE_KEY, 0x02, {KEY_CONFIG} },
+ { KE_KEY, 0x11, {KEY_MAIL} },
+ { KE_KEY, 0x12, {KEY_WWW} },
+ { KE_KEY, 0x13, {KEY_PROG2} },
+ { KE_KEY, 0x31, {KEY_PROG1} },
+ { KE_END, FE_WIFI_LED | FE_UNTESTED }
+};
+
+static struct key_entry keymap_acer_travelmate_230[] __initdata = {
+ { KE_KEY, 0x01, {KEY_HELP} },
+ { KE_KEY, 0x02, {KEY_CONFIG} },
+ { KE_KEY, 0x11, {KEY_PROG1} },
+ { KE_KEY, 0x12, {KEY_PROG2} },
+ { KE_KEY, 0x31, {KEY_MAIL} },
+ { KE_KEY, 0x36, {KEY_WWW} },
+ { KE_END, FE_WIFI_LED | FE_UNTESTED }
+};
+
+static struct key_entry keymap_acer_travelmate_240[] __initdata = {
+ { KE_KEY, 0x01, {KEY_HELP} },
+ { KE_KEY, 0x02, {KEY_CONFIG} },
+ { KE_KEY, 0x03, {KEY_POWER} },
+ { KE_KEY, 0x08, {KEY_MUTE} },
+ { KE_KEY, 0x31, {KEY_MAIL} },
+ { KE_KEY, 0x36, {KEY_WWW} },
+ { KE_KEY, 0x11, {KEY_PROG1} },
+ { KE_KEY, 0x12, {KEY_PROG2} },
+ { KE_BLUETOOTH, 0x44 },
+ { KE_WIFI, 0x30 },
+ { KE_END, FE_UNTESTED }
+};
+
+static struct key_entry keymap_acer_travelmate_350[] __initdata = {
+ { KE_KEY, 0x01, {KEY_HELP} },
+ { KE_KEY, 0x02, {KEY_CONFIG} },
+ { KE_KEY, 0x11, {KEY_PROG1} },
+ { KE_KEY, 0x12, {KEY_PROG2} },
+ { KE_KEY, 0x13, {KEY_MAIL} },
+ { KE_KEY, 0x14, {KEY_PROG3} },
+ { KE_KEY, 0x15, {KEY_WWW} },
+ { KE_END, FE_MAIL_LED | FE_WIFI_LED | FE_UNTESTED }
+};
+
+static struct key_entry keymap_acer_travelmate_360[] __initdata = {
+ { KE_KEY, 0x01, {KEY_HELP} },
+ { KE_KEY, 0x02, {KEY_CONFIG} },
+ { KE_KEY, 0x11, {KEY_PROG1} },
+ { KE_KEY, 0x12, {KEY_PROG2} },
+ { KE_KEY, 0x13, {KEY_MAIL} },
+ { KE_KEY, 0x14, {KEY_PROG3} },
+ { KE_KEY, 0x15, {KEY_WWW} },
+ { KE_KEY, 0x40, {KEY_WLAN} },
+ { KE_END, FE_WIFI_LED | FE_UNTESTED } /* no mail led */
+};
+
+/* Wifi subsystem only activates the led. Therefore we need to pass
+ * wifi event as a normal key, then userspace can really change the wifi state.
+ * TODO we need to export led state to userspace (wifi and mail) */
+static struct key_entry keymap_acer_travelmate_610[] __initdata = {
+ { KE_KEY, 0x01, {KEY_HELP} },
+ { KE_KEY, 0x02, {KEY_CONFIG} },
+ { KE_KEY, 0x11, {KEY_PROG1} },
+ { KE_KEY, 0x12, {KEY_PROG2} },
+ { KE_KEY, 0x13, {KEY_PROG3} },
+ { KE_KEY, 0x14, {KEY_MAIL} },
+ { KE_KEY, 0x15, {KEY_WWW} },
+ { KE_KEY, 0x40, {KEY_WLAN} },
+ { KE_END, FE_MAIL_LED | FE_WIFI_LED }
+};
+
+static struct key_entry keymap_acer_travelmate_630[] __initdata = {
+ { KE_KEY, 0x01, {KEY_HELP} },
+ { KE_KEY, 0x02, {KEY_CONFIG} },
+ { KE_KEY, 0x03, {KEY_POWER} },
+ { KE_KEY, 0x08, {KEY_MUTE} }, /* not 620 */
+ { KE_KEY, 0x11, {KEY_PROG1} },
+ { KE_KEY, 0x12, {KEY_PROG2} },
+ { KE_KEY, 0x13, {KEY_PROG3} },
+ { KE_KEY, 0x20, {KEY_VOLUMEUP} },
+ { KE_KEY, 0x21, {KEY_VOLUMEDOWN} },
+ { KE_KEY, 0x31, {KEY_MAIL} },
+ { KE_KEY, 0x36, {KEY_WWW} },
+ { KE_WIFI, 0x30 },
+ { KE_END, FE_MAIL_LED | FE_UNTESTED }
+};
+
+static struct key_entry keymap_aopen_1559as[] __initdata = {
+ { KE_KEY, 0x01, {KEY_HELP} },
+ { KE_KEY, 0x06, {KEY_PROG3} },
+ { KE_KEY, 0x11, {KEY_PROG1} },
+ { KE_KEY, 0x12, {KEY_PROG2} },
+ { KE_WIFI, 0x30 },
+ { KE_KEY, 0x31, {KEY_MAIL} },
+ { KE_KEY, 0x36, {KEY_WWW} },
+ { KE_END, 0 },
+};
+
+static struct key_entry keymap_fs_amilo_d88x0[] __initdata = {
+ { KE_KEY, 0x01, {KEY_HELP} },
+ { KE_KEY, 0x08, {KEY_MUTE} },
+ { KE_KEY, 0x31, {KEY_MAIL} },
+ { KE_KEY, 0x36, {KEY_WWW} },
+ { KE_KEY, 0x11, {KEY_PROG1} },
+ { KE_KEY, 0x12, {KEY_PROG2} },
+ { KE_KEY, 0x13, {KEY_PROG3} },
+ { KE_END, FE_MAIL_LED | FE_WIFI_LED | FE_UNTESTED }
+};
+
+static struct key_entry keymap_wistron_md2900[] __initdata = {
+ { KE_KEY, 0x01, {KEY_HELP} },
+ { KE_KEY, 0x02, {KEY_CONFIG} },
+ { KE_KEY, 0x11, {KEY_PROG1} },
+ { KE_KEY, 0x12, {KEY_PROG2} },
+ { KE_KEY, 0x31, {KEY_MAIL} },
+ { KE_KEY, 0x36, {KEY_WWW} },
+ { KE_WIFI, 0x30 },
+ { KE_END, FE_MAIL_LED | FE_UNTESTED }
+};
+
+static struct key_entry keymap_wistron_md96500[] __initdata = {
+ { KE_KEY, 0x01, {KEY_HELP} },
+ { KE_KEY, 0x02, {KEY_CONFIG} },
+ { KE_KEY, 0x05, {KEY_SWITCHVIDEOMODE} }, /* Display selection */
+ { KE_KEY, 0x06, {KEY_DISPLAYTOGGLE} }, /* Display on/off */
+ { KE_KEY, 0x08, {KEY_MUTE} },
+ { KE_KEY, 0x11, {KEY_PROG1} },
+ { KE_KEY, 0x12, {KEY_PROG2} },
+ { KE_KEY, 0x20, {KEY_VOLUMEUP} },
+ { KE_KEY, 0x21, {KEY_VOLUMEDOWN} },
+ { KE_KEY, 0x22, {KEY_REWIND} },
+ { KE_KEY, 0x23, {KEY_FORWARD} },
+ { KE_KEY, 0x24, {KEY_PLAYPAUSE} },
+ { KE_KEY, 0x25, {KEY_STOPCD} },
+ { KE_KEY, 0x31, {KEY_MAIL} },
+ { KE_KEY, 0x36, {KEY_WWW} },
+ { KE_WIFI, 0x30 },
+ { KE_BLUETOOTH, 0x44 },
+ { KE_END, FE_UNTESTED }
+};
+
+static struct key_entry keymap_wistron_generic[] __initdata = {
+ { KE_KEY, 0x01, {KEY_HELP} },
+ { KE_KEY, 0x02, {KEY_CONFIG} },
+ { KE_KEY, 0x03, {KEY_POWER} },
+ { KE_KEY, 0x05, {KEY_SWITCHVIDEOMODE} }, /* Display selection */
+ { KE_KEY, 0x06, {KEY_DISPLAYTOGGLE} }, /* Display on/off */
+ { KE_KEY, 0x08, {KEY_MUTE} },
+ { KE_KEY, 0x11, {KEY_PROG1} },
+ { KE_KEY, 0x12, {KEY_PROG2} },
+ { KE_KEY, 0x13, {KEY_PROG3} },
+ { KE_KEY, 0x14, {KEY_MAIL} },
+ { KE_KEY, 0x15, {KEY_WWW} },
+ { KE_KEY, 0x20, {KEY_VOLUMEUP} },
+ { KE_KEY, 0x21, {KEY_VOLUMEDOWN} },
+ { KE_KEY, 0x22, {KEY_REWIND} },
+ { KE_KEY, 0x23, {KEY_FORWARD} },
+ { KE_KEY, 0x24, {KEY_PLAYPAUSE} },
+ { KE_KEY, 0x25, {KEY_STOPCD} },
+ { KE_KEY, 0x31, {KEY_MAIL} },
+ { KE_KEY, 0x36, {KEY_WWW} },
+ { KE_KEY, 0x37, {KEY_DISPLAYTOGGLE} }, /* Display on/off */
+ { KE_KEY, 0x40, {KEY_WLAN} },
+ { KE_KEY, 0x49, {KEY_CONFIG} },
+ { KE_SW, 0x4a, {.sw = {SW_LID, 1}} }, /* lid close */
+ { KE_SW, 0x4b, {.sw = {SW_LID, 0}} }, /* lid open */
+ { KE_KEY, 0x6a, {KEY_CONFIG} },
+ { KE_KEY, 0x6d, {KEY_POWER} },
+ { KE_KEY, 0x71, {KEY_STOPCD} },
+ { KE_KEY, 0x72, {KEY_PLAYPAUSE} },
+ { KE_KEY, 0x74, {KEY_REWIND} },
+ { KE_KEY, 0x78, {KEY_FORWARD} },
+ { KE_WIFI, 0x30 },
+ { KE_BLUETOOTH, 0x44 },
+ { KE_END, 0 }
+};
+
+/*
+ * If your machine is not here (which is currently rather likely), please send
+ * a list of buttons and their key codes (reported when loading this module
+ * with force=1) and the output of dmidecode to $MODULE_AUTHOR.
+ */
+static struct dmi_system_id dmi_ids[] __initdata = {
+ {
+ .callback = dmi_matched,
+ .ident = "Fujitsu-Siemens Amilo Pro V2000",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Pro V2000"),
+ },
+ .driver_data = keymap_fs_amilo_pro_v2000
+ },
+ {
+ .callback = dmi_matched,
+ .ident = "Fujitsu-Siemens Amilo Pro Edition V3505",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Pro Edition V3505"),
+ },
+ .driver_data = keymap_fs_amilo_pro_v3505
+ },
+ {
+ .callback = dmi_matched,
+ .ident = "Fujitsu-Siemens Amilo M7400",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "AMILO M "),
+ },
+ .driver_data = keymap_fs_amilo_pro_v2000
+ },
+ {
+ .callback = dmi_matched,
+ .ident = "Fujitsu N3510",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "N3510"),
+ },
+ .driver_data = keymap_fujitsu_n3510
+ },
+ {
+ .callback = dmi_matched,
+ .ident = "Acer Aspire 1500",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 1500"),
+ },
+ .driver_data = keymap_acer_aspire_1500
+ },
+ {
+ .callback = dmi_matched,
+ .ident = "Acer Aspire 1600",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 1600"),
+ },
+ .driver_data = keymap_acer_aspire_1600
+ },
+ {
+ .callback = dmi_matched,
+ .ident = "Acer Aspire 3020",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 3020"),
+ },
+ .driver_data = keymap_acer_aspire_5020
+ },
+ {
+ .callback = dmi_matched,
+ .ident = "Acer Aspire 5020",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5020"),
+ },
+ .driver_data = keymap_acer_aspire_5020
+ },
+ {
+ .callback = dmi_matched,
+ .ident = "Acer TravelMate 2100",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 2100"),
+ },
+ .driver_data = keymap_acer_aspire_5020
+ },
+ {
+ .callback = dmi_matched,
+ .ident = "Acer TravelMate 2410",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 2410"),
+ },
+ .driver_data = keymap_acer_travelmate_2410
+ },
+ {
+ .callback = dmi_matched,
+ .ident = "Acer TravelMate C300",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate C300"),
+ },
+ .driver_data = keymap_acer_travelmate_300
+ },
+ {
+ .callback = dmi_matched,
+ .ident = "Acer TravelMate C100",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate C100"),
+ },
+ .driver_data = keymap_acer_travelmate_300
+ },
+ {
+ .callback = dmi_matched,
+ .ident = "Acer TravelMate C110",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate C110"),
+ },
+ .driver_data = keymap_acer_travelmate_110
+ },
+ {
+ .callback = dmi_matched,
+ .ident = "Acer TravelMate 380",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 380"),
+ },
+ .driver_data = keymap_acer_travelmate_380
+ },
+ {
+ .callback = dmi_matched,
+ .ident = "Acer TravelMate 370",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 370"),
+ },
+ .driver_data = keymap_acer_travelmate_380 /* keyboard minus 1 key */
+ },
+ {
+ .callback = dmi_matched,
+ .ident = "Acer TravelMate 220",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 220"),
+ },
+ .driver_data = keymap_acer_travelmate_220
+ },
+ {
+ .callback = dmi_matched,
+ .ident = "Acer TravelMate 260",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 260"),
+ },
+ .driver_data = keymap_acer_travelmate_220
+ },
+ {
+ .callback = dmi_matched,
+ .ident = "Acer TravelMate 230",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 230"),
+ /* acerhk looks for "TravelMate F4..." ?! */
+ },
+ .driver_data = keymap_acer_travelmate_230
+ },
+ {
+ .callback = dmi_matched,
+ .ident = "Acer TravelMate 280",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 280"),
+ },
+ .driver_data = keymap_acer_travelmate_230
+ },
+ {
+ .callback = dmi_matched,
+ .ident = "Acer TravelMate 240",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 240"),
+ },
+ .driver_data = keymap_acer_travelmate_240
+ },
+ {
+ .callback = dmi_matched,
+ .ident = "Acer TravelMate 250",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 250"),
+ },
+ .driver_data = keymap_acer_travelmate_240
+ },
+ {
+ .callback = dmi_matched,
+ .ident = "Acer TravelMate 2424NWXCi",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 2420"),
+ },
+ .driver_data = keymap_acer_travelmate_240
+ },
+ {
+ .callback = dmi_matched,
+ .ident = "Acer TravelMate 350",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 350"),
+ },
+ .driver_data = keymap_acer_travelmate_350
+ },
+ {
+ .callback = dmi_matched,
+ .ident = "Acer TravelMate 360",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 360"),
+ },
+ .driver_data = keymap_acer_travelmate_360
+ },
+ {
+ .callback = dmi_matched,
+ .ident = "Acer TravelMate 610",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ACER"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 610"),
+ },
+ .driver_data = keymap_acer_travelmate_610
+ },
+ {
+ .callback = dmi_matched,
+ .ident = "Acer TravelMate 620",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 620"),
+ },
+ .driver_data = keymap_acer_travelmate_630
+ },
+ {
+ .callback = dmi_matched,
+ .ident = "Acer TravelMate 630",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 630"),
+ },
+ .driver_data = keymap_acer_travelmate_630
+ },
+ {
+ .callback = dmi_matched,
+ .ident = "AOpen 1559AS",
+ .matches = {
+ DMI_MATCH(DMI_PRODUCT_NAME, "E2U"),
+ DMI_MATCH(DMI_BOARD_NAME, "E2U"),
+ },
+ .driver_data = keymap_aopen_1559as
+ },
+ {
+ .callback = dmi_matched,
+ .ident = "Medion MD 9783",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "MEDIONNB"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "MD 9783"),
+ },
+ .driver_data = keymap_wistron_ms2111
+ },
+ {
+ .callback = dmi_matched,
+ .ident = "Medion MD 40100",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "MEDIONNB"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "WID2000"),
+ },
+ .driver_data = keymap_wistron_md40100
+ },
+ {
+ .callback = dmi_matched,
+ .ident = "Medion MD 2900",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "MEDIONNB"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "WIM 2000"),
+ },
+ .driver_data = keymap_wistron_md2900
+ },
+ {
+ .callback = dmi_matched,
+ .ident = "Medion MD 96500",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "MEDIONPC"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "WIM 2040"),
+ },
+ .driver_data = keymap_wistron_md96500
+ },
+ {
+ .callback = dmi_matched,
+ .ident = "Medion MD 95400",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "MEDIONPC"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "WIM 2050"),
+ },
+ .driver_data = keymap_wistron_md96500
+ },
+ {
+ .callback = dmi_matched,
+ .ident = "Fujitsu Siemens Amilo D7820",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), /* not sure */
+ DMI_MATCH(DMI_PRODUCT_NAME, "Amilo D"),
+ },
+ .driver_data = keymap_fs_amilo_d88x0
+ },
+ {
+ .callback = dmi_matched,
+ .ident = "Fujitsu Siemens Amilo D88x0",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "AMILO D"),
+ },
+ .driver_data = keymap_fs_amilo_d88x0
+ },
+ { NULL, }
+};
+
+/* Copy the good keymap, as the original ones are free'd */
+static int __init copy_keymap(void)
+{
+ const struct key_entry *key;
+ struct key_entry *new_keymap;
+ unsigned int length = 1;
+
+ for (key = keymap; key->type != KE_END; key++)
+ length++;
+
+ new_keymap = kmalloc(length * sizeof(struct key_entry), GFP_KERNEL);
+ if (!new_keymap)
+ return -ENOMEM;
+
+ memcpy(new_keymap, keymap, length * sizeof(struct key_entry));
+ keymap = new_keymap;
+
+ return 0;
+}
+
+static int __init select_keymap(void)
+{
+ dmi_check_system(dmi_ids);
+ if (keymap_name != NULL) {
+ if (strcmp (keymap_name, "1557/MS2141") == 0)
+ keymap = keymap_wistron_ms2141;
+ else if (strcmp (keymap_name, "generic") == 0)
+ keymap = keymap_wistron_generic;
+ else {
+ printk(KERN_ERR "wistron_btns: Keymap unknown\n");
+ return -EINVAL;
+ }
+ }
+ if (keymap == NULL) {
+ if (!force) {
+ printk(KERN_ERR "wistron_btns: System unknown\n");
+ return -ENODEV;
+ }
+ keymap = keymap_empty;
+ }
+
+ return copy_keymap();
+}
+
+ /* Input layer interface */
+
+static struct input_polled_dev *wistron_idev;
+static unsigned long jiffies_last_press;
+static int wifi_enabled;
+static int bluetooth_enabled;
+
+static void report_key(struct input_dev *dev, unsigned int keycode)
+{
+ input_report_key(dev, keycode, 1);
+ input_sync(dev);
+ input_report_key(dev, keycode, 0);
+ input_sync(dev);
+}
+
+static void report_switch(struct input_dev *dev, unsigned int code, int value)
+{
+ input_report_switch(dev, code, value);
+ input_sync(dev);
+}
+
+
+ /* led management */
+static void wistron_mail_led_set(struct led_classdev *led_cdev,
+ enum led_brightness value)
+{
+ bios_set_state(MAIL_LED, (value != LED_OFF) ? 1 : 0);
+}
+
+/* same as setting up wifi card, but for laptops on which the led is managed */
+static void wistron_wifi_led_set(struct led_classdev *led_cdev,
+ enum led_brightness value)
+{
+ bios_set_state(WIFI, (value != LED_OFF) ? 1 : 0);
+}
+
+static struct led_classdev wistron_mail_led = {
+ .name = "wistron:green:mail",
+ .brightness_set = wistron_mail_led_set,
+};
+
+static struct led_classdev wistron_wifi_led = {
+ .name = "wistron:red:wifi",
+ .brightness_set = wistron_wifi_led_set,
+};
+
+static void __devinit wistron_led_init(struct device *parent)
+{
+ if (have_leds & FE_WIFI_LED) {
+ u16 wifi = bios_get_default_setting(WIFI);
+ if (wifi & 1) {
+ wistron_wifi_led.brightness = (wifi & 2) ? LED_FULL : LED_OFF;
+ if (led_classdev_register(parent, &wistron_wifi_led))
+ have_leds &= ~FE_WIFI_LED;
+ else
+ bios_set_state(WIFI, wistron_wifi_led.brightness);
+
+ } else
+ have_leds &= ~FE_WIFI_LED;
+ }
+
+ if (have_leds & FE_MAIL_LED) {
+ /* bios_get_default_setting(MAIL) always retuns 0, so just turn the led off */
+ wistron_mail_led.brightness = LED_OFF;
+ if (led_classdev_register(parent, &wistron_mail_led))
+ have_leds &= ~FE_MAIL_LED;
+ else
+ bios_set_state(MAIL_LED, wistron_mail_led.brightness);
+ }
+}
+
+static void __devexit wistron_led_remove(void)
+{
+ if (have_leds & FE_MAIL_LED)
+ led_classdev_unregister(&wistron_mail_led);
+
+ if (have_leds & FE_WIFI_LED)
+ led_classdev_unregister(&wistron_wifi_led);
+}
+
+static inline void wistron_led_suspend(void)
+{
+ if (have_leds & FE_MAIL_LED)
+ led_classdev_suspend(&wistron_mail_led);
+
+ if (have_leds & FE_WIFI_LED)
+ led_classdev_suspend(&wistron_wifi_led);
+}
+
+static inline void wistron_led_resume(void)
+{
+ if (have_leds & FE_MAIL_LED)
+ led_classdev_resume(&wistron_mail_led);
+
+ if (have_leds & FE_WIFI_LED)
+ led_classdev_resume(&wistron_wifi_led);
+}
+
+static struct key_entry *wistron_get_entry_by_scancode(int code)
+{
+ struct key_entry *key;
+
+ for (key = keymap; key->type != KE_END; key++)
+ if (code == key->code)
+ return key;
+
+ return NULL;
+}
+
+static struct key_entry *wistron_get_entry_by_keycode(int keycode)
+{
+ struct key_entry *key;
+
+ for (key = keymap; key->type != KE_END; key++)
+ if (key->type == KE_KEY && keycode == key->keycode)
+ return key;
+
+ return NULL;
+}
+
+static void handle_key(u8 code)
+{
+ const struct key_entry *key = wistron_get_entry_by_scancode(code);
+
+ if (key) {
+ switch (key->type) {
+ case KE_KEY:
+ report_key(wistron_idev->input, key->keycode);
+ break;
+
+ case KE_SW:
+ report_switch(wistron_idev->input,
+ key->sw.code, key->sw.value);
+ break;
+
+ case KE_WIFI:
+ if (have_wifi) {
+ wifi_enabled = !wifi_enabled;
+ bios_set_state(WIFI, wifi_enabled);
+ }
+ break;
+
+ case KE_BLUETOOTH:
+ if (have_bluetooth) {
+ bluetooth_enabled = !bluetooth_enabled;
+ bios_set_state(BLUETOOTH, bluetooth_enabled);
+ }
+ break;
+
+ default:
+ BUG();
+ }
+ jiffies_last_press = jiffies;
+ } else
+ printk(KERN_NOTICE
+ "wistron_btns: Unknown key code %02X\n", code);
+}
+
+static void poll_bios(bool discard)
+{
+ u8 qlen;
+ u16 val;
+
+ for (;;) {
+ qlen = CMOS_READ(cmos_address);
+ if (qlen == 0)
+ break;
+ val = bios_pop_queue();
+ if (val != 0 && !discard)
+ handle_key((u8)val);
+ }
+}
+
+static void wistron_flush(struct input_polled_dev *dev)
+{
+ /* Flush stale event queue */
+ poll_bios(true);
+}
+
+static void wistron_poll(struct input_polled_dev *dev)
+{
+ poll_bios(false);
+
+ /* Increase poll frequency if user is currently pressing keys (< 2s ago) */
+ if (time_before(jiffies, jiffies_last_press + 2 * HZ))
+ dev->poll_interval = POLL_INTERVAL_BURST;
+ else
+ dev->poll_interval = POLL_INTERVAL_DEFAULT;
+}
+
+static int wistron_getkeycode(struct input_dev *dev, int scancode, int *keycode)
+{
+ const struct key_entry *key = wistron_get_entry_by_scancode(scancode);
+
+ if (key && key->type == KE_KEY) {
+ *keycode = key->keycode;
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static int wistron_setkeycode(struct input_dev *dev, int scancode, int keycode)
+{
+ struct key_entry *key;
+ int old_keycode;
+
+ if (keycode < 0 || keycode > KEY_MAX)
+ return -EINVAL;
+
+ key = wistron_get_entry_by_scancode(scancode);
+ if (key && key->type == KE_KEY) {
+ old_keycode = key->keycode;
+ key->keycode = keycode;
+ set_bit(keycode, dev->keybit);
+ if (!wistron_get_entry_by_keycode(old_keycode))
+ clear_bit(old_keycode, dev->keybit);
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static int __devinit setup_input_dev(void)
+{
+ struct key_entry *key;
+ struct input_dev *input_dev;
+ int error;
+
+ wistron_idev = input_allocate_polled_device();
+ if (!wistron_idev)
+ return -ENOMEM;
+
+ wistron_idev->flush = wistron_flush;
+ wistron_idev->poll = wistron_poll;
+ wistron_idev->poll_interval = POLL_INTERVAL_DEFAULT;
+
+ input_dev = wistron_idev->input;
+ input_dev->name = "Wistron laptop buttons";
+ input_dev->phys = "wistron/input0";
+ input_dev->id.bustype = BUS_HOST;
+ input_dev->dev.parent = &wistron_device->dev;
+
+ input_dev->getkeycode = wistron_getkeycode;
+ input_dev->setkeycode = wistron_setkeycode;
+
+ for (key = keymap; key->type != KE_END; key++) {
+ switch (key->type) {
+ case KE_KEY:
+ set_bit(EV_KEY, input_dev->evbit);
+ set_bit(key->keycode, input_dev->keybit);
+ break;
+
+ case KE_SW:
+ set_bit(EV_SW, input_dev->evbit);
+ set_bit(key->sw.code, input_dev->swbit);
+ break;
+
+ /* if wifi or bluetooth are not available, create normal keys */
+ case KE_WIFI:
+ if (!have_wifi) {
+ key->type = KE_KEY;
+ key->keycode = KEY_WLAN;
+ key--;
+ }
+ break;
+
+ case KE_BLUETOOTH:
+ if (!have_bluetooth) {
+ key->type = KE_KEY;
+ key->keycode = KEY_BLUETOOTH;
+ key--;
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ /* reads information flags on KE_END */
+ if (key->code & FE_UNTESTED)
+ printk(KERN_WARNING "Untested laptop multimedia keys, "
+ "please report success or failure to eric.piel"
+ "@tremplin-utc.net\n");
+
+ error = input_register_polled_device(wistron_idev);
+ if (error) {
+ input_free_polled_device(wistron_idev);
+ return error;
+ }
+
+ return 0;
+}
+
+/* Driver core */
+
+static int __devinit wistron_probe(struct platform_device *dev)
+{
+ int err;
+
+ bios_attach();
+ cmos_address = bios_get_cmos_address();
+
+ if (have_wifi) {
+ u16 wifi = bios_get_default_setting(WIFI);
+ if (wifi & 1)
+ wifi_enabled = (wifi & 2) ? 1 : 0;
+ else
+ have_wifi = 0;
+
+ if (have_wifi)
+ bios_set_state(WIFI, wifi_enabled);
+ }
+
+ if (have_bluetooth) {
+ u16 bt = bios_get_default_setting(BLUETOOTH);
+ if (bt & 1)
+ bluetooth_enabled = (bt & 2) ? 1 : 0;
+ else
+ have_bluetooth = 0;
+
+ if (have_bluetooth)
+ bios_set_state(BLUETOOTH, bluetooth_enabled);
+ }
+
+ wistron_led_init(&dev->dev);
+ err = setup_input_dev();
+ if (err) {
+ bios_detach();
+ return err;
+ }
+
+ return 0;
+}
+
+static int __devexit wistron_remove(struct platform_device *dev)
+{
+ wistron_led_remove();
+ input_unregister_polled_device(wistron_idev);
+ input_free_polled_device(wistron_idev);
+ bios_detach();
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int wistron_suspend(struct platform_device *dev, pm_message_t state)
+{
+ if (have_wifi)
+ bios_set_state(WIFI, 0);
+
+ if (have_bluetooth)
+ bios_set_state(BLUETOOTH, 0);
+
+ wistron_led_suspend();
+ return 0;
+}
+
+static int wistron_resume(struct platform_device *dev)
+{
+ if (have_wifi)
+ bios_set_state(WIFI, wifi_enabled);
+
+ if (have_bluetooth)
+ bios_set_state(BLUETOOTH, bluetooth_enabled);
+
+ wistron_led_resume();
+ poll_bios(true);
+
+ return 0;
+}
+#else
+#define wistron_suspend NULL
+#define wistron_resume NULL
+#endif
+
+static struct platform_driver wistron_driver = {
+ .driver = {
+ .name = "wistron-bios",
+ .owner = THIS_MODULE,
+ },
+ .probe = wistron_probe,
+ .remove = __devexit_p(wistron_remove),
+ .suspend = wistron_suspend,
+ .resume = wistron_resume,
+};
+
+static int __init wb_module_init(void)
+{
+ int err;
+
+ err = select_keymap();
+ if (err)
+ return err;
+
+ err = map_bios();
+ if (err)
+ return err;
+
+ err = platform_driver_register(&wistron_driver);
+ if (err)
+ goto err_unmap_bios;
+
+ wistron_device = platform_device_alloc("wistron-bios", -1);
+ if (!wistron_device) {
+ err = -ENOMEM;
+ goto err_unregister_driver;
+ }
+
+ err = platform_device_add(wistron_device);
+ if (err)
+ goto err_free_device;
+
+ return 0;
+
+ err_free_device:
+ platform_device_put(wistron_device);
+ err_unregister_driver:
+ platform_driver_unregister(&wistron_driver);
+ err_unmap_bios:
+ unmap_bios();
+
+ return err;
+}
+
+static void __exit wb_module_exit(void)
+{
+ platform_device_unregister(wistron_device);
+ platform_driver_unregister(&wistron_driver);
+ unmap_bios();
+ kfree(keymap);
+}
+
+module_init(wb_module_init);
+module_exit(wb_module_exit);
diff --git a/drivers/input/misc/yealink.c b/drivers/input/misc/yealink.c
new file mode 100644
index 0000000..93a22ac
--- /dev/null
+++ b/drivers/input/misc/yealink.c
@@ -0,0 +1,1019 @@
+/*
+ * drivers/usb/input/yealink.c
+ *
+ * Copyright (c) 2005 Henk Vergonet <Henk.Vergonet@gmail.com>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+/*
+ * Description:
+ * Driver for the USB-P1K voip usb phone.
+ * This device is produced by Yealink Network Technology Co Ltd
+ * but may be branded under several names:
+ * - Yealink usb-p1k
+ * - Tiptel 115
+ * - ...
+ *
+ * This driver is based on:
+ * - the usbb2k-api http://savannah.nongnu.org/projects/usbb2k-api/
+ * - information from http://memeteau.free.fr/usbb2k
+ * - the xpad-driver drivers/input/joystick/xpad.c
+ *
+ * Thanks to:
+ * - Olivier Vandorpe, for providing the usbb2k-api.
+ * - Martin Diehl, for spotting my memory allocation bug.
+ *
+ * History:
+ * 20050527 henk First version, functional keyboard. Keyboard events
+ * will pop-up on the ../input/eventX bus.
+ * 20050531 henk Added led, LCD, dialtone and sysfs interface.
+ * 20050610 henk Cleanups, make it ready for public consumption.
+ * 20050630 henk Cleanups, fixes in response to comments.
+ * 20050701 henk sysfs write serialisation, fix potential unload races
+ * 20050801 henk Added ringtone, restructure USB
+ * 20050816 henk Merge 2.6.13-rc6
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/rwsem.h>
+#include <linux/usb/input.h>
+#include <linux/map_to_7segment.h>
+
+#include "yealink.h"
+
+#define DRIVER_VERSION "yld-20051230"
+#define DRIVER_AUTHOR "Henk Vergonet"
+#define DRIVER_DESC "Yealink phone driver"
+
+#define YEALINK_POLLING_FREQUENCY 10 /* in [Hz] */
+
+struct yld_status {
+ u8 lcd[24];
+ u8 led;
+ u8 dialtone;
+ u8 ringtone;
+ u8 keynum;
+} __attribute__ ((packed));
+
+/*
+ * Register the LCD segment and icon map
+ */
+#define _LOC(k,l) { .a = (k), .m = (l) }
+#define _SEG(t, a, am, b, bm, c, cm, d, dm, e, em, f, fm, g, gm) \
+ { .type = (t), \
+ .u = { .s = { _LOC(a, am), _LOC(b, bm), _LOC(c, cm), \
+ _LOC(d, dm), _LOC(e, em), _LOC(g, gm), \
+ _LOC(f, fm) } } }
+#define _PIC(t, h, hm, n) \
+ { .type = (t), \
+ .u = { .p = { .name = (n), .a = (h), .m = (hm) } } }
+
+static const struct lcd_segment_map {
+ char type;
+ union {
+ struct pictogram_map {
+ u8 a,m;
+ char name[10];
+ } p;
+ struct segment_map {
+ u8 a,m;
+ } s[7];
+ } u;
+} lcdMap[] = {
+#include "yealink.h"
+};
+
+struct yealink_dev {
+ struct input_dev *idev; /* input device */
+ struct usb_device *udev; /* usb device */
+
+ /* irq input channel */
+ struct yld_ctl_packet *irq_data;
+ dma_addr_t irq_dma;
+ struct urb *urb_irq;
+
+ /* control output channel */
+ struct yld_ctl_packet *ctl_data;
+ dma_addr_t ctl_dma;
+ struct usb_ctrlrequest *ctl_req;
+ dma_addr_t ctl_req_dma;
+ struct urb *urb_ctl;
+
+ char phys[64]; /* physical device path */
+
+ u8 lcdMap[ARRAY_SIZE(lcdMap)]; /* state of LCD, LED ... */
+ int key_code; /* last reported key */
+
+ unsigned int shutdown:1;
+
+ int stat_ix;
+ union {
+ struct yld_status s;
+ u8 b[sizeof(struct yld_status)];
+ } master, copy;
+};
+
+
+/*******************************************************************************
+ * Yealink lcd interface
+ ******************************************************************************/
+
+/*
+ * Register a default 7 segment character set
+ */
+static SEG7_DEFAULT_MAP(map_seg7);
+
+ /* Display a char,
+ * char '\9' and '\n' are placeholders and do not overwrite the original text.
+ * A space will always hide an icon.
+ */
+static int setChar(struct yealink_dev *yld, int el, int chr)
+{
+ int i, a, m, val;
+
+ if (el >= ARRAY_SIZE(lcdMap))
+ return -EINVAL;
+
+ if (chr == '\t' || chr == '\n')
+ return 0;
+
+ yld->lcdMap[el] = chr;
+
+ if (lcdMap[el].type == '.') {
+ a = lcdMap[el].u.p.a;
+ m = lcdMap[el].u.p.m;
+ if (chr != ' ')
+ yld->master.b[a] |= m;
+ else
+ yld->master.b[a] &= ~m;
+ return 0;
+ }
+
+ val = map_to_seg7(&map_seg7, chr);
+ for (i = 0; i < ARRAY_SIZE(lcdMap[0].u.s); i++) {
+ m = lcdMap[el].u.s[i].m;
+
+ if (m == 0)
+ continue;
+
+ a = lcdMap[el].u.s[i].a;
+ if (val & 1)
+ yld->master.b[a] |= m;
+ else
+ yld->master.b[a] &= ~m;
+ val = val >> 1;
+ }
+ return 0;
+};
+
+/*******************************************************************************
+ * Yealink key interface
+ ******************************************************************************/
+
+/* Map device buttons to internal key events.
+ *
+ * USB-P1K button layout:
+ *
+ * up
+ * IN OUT
+ * down
+ *
+ * pickup C hangup
+ * 1 2 3
+ * 4 5 6
+ * 7 8 9
+ * * 0 #
+ *
+ * The "up" and "down" keys, are symbolised by arrows on the button.
+ * The "pickup" and "hangup" keys are symbolised by a green and red phone
+ * on the button.
+ */
+static int map_p1k_to_key(int scancode)
+{
+ switch(scancode) { /* phone key: */
+ case 0x23: return KEY_LEFT; /* IN */
+ case 0x33: return KEY_UP; /* up */
+ case 0x04: return KEY_RIGHT; /* OUT */
+ case 0x24: return KEY_DOWN; /* down */
+ case 0x03: return KEY_ENTER; /* pickup */
+ case 0x14: return KEY_BACKSPACE; /* C */
+ case 0x13: return KEY_ESC; /* hangup */
+ case 0x00: return KEY_1; /* 1 */
+ case 0x01: return KEY_2; /* 2 */
+ case 0x02: return KEY_3; /* 3 */
+ case 0x10: return KEY_4; /* 4 */
+ case 0x11: return KEY_5; /* 5 */
+ case 0x12: return KEY_6; /* 6 */
+ case 0x20: return KEY_7; /* 7 */
+ case 0x21: return KEY_8; /* 8 */
+ case 0x22: return KEY_9; /* 9 */
+ case 0x30: return KEY_KPASTERISK; /* * */
+ case 0x31: return KEY_0; /* 0 */
+ case 0x32: return KEY_LEFTSHIFT |
+ KEY_3 << 8; /* # */
+ }
+ return -EINVAL;
+}
+
+/* Completes a request by converting the data into events for the
+ * input subsystem.
+ *
+ * The key parameter can be cascaded: key2 << 8 | key1
+ */
+static void report_key(struct yealink_dev *yld, int key)
+{
+ struct input_dev *idev = yld->idev;
+
+ if (yld->key_code >= 0) {
+ /* old key up */
+ input_report_key(idev, yld->key_code & 0xff, 0);
+ if (yld->key_code >> 8)
+ input_report_key(idev, yld->key_code >> 8, 0);
+ }
+
+ yld->key_code = key;
+ if (key >= 0) {
+ /* new valid key */
+ input_report_key(idev, key & 0xff, 1);
+ if (key >> 8)
+ input_report_key(idev, key >> 8, 1);
+ }
+ input_sync(idev);
+}
+
+/*******************************************************************************
+ * Yealink usb communication interface
+ ******************************************************************************/
+
+static int yealink_cmd(struct yealink_dev *yld, struct yld_ctl_packet *p)
+{
+ u8 *buf = (u8 *)p;
+ int i;
+ u8 sum = 0;
+
+ for(i=0; i<USB_PKT_LEN-1; i++)
+ sum -= buf[i];
+ p->sum = sum;
+ return usb_control_msg(yld->udev,
+ usb_sndctrlpipe(yld->udev, 0),
+ USB_REQ_SET_CONFIGURATION,
+ USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT,
+ 0x200, 3,
+ p, sizeof(*p),
+ USB_CTRL_SET_TIMEOUT);
+}
+
+static u8 default_ringtone[] = {
+ 0xEF, /* volume [0-255] */
+ 0xFB, 0x1E, 0x00, 0x0C, /* 1250 [hz], 12/100 [s] */
+ 0xFC, 0x18, 0x00, 0x0C, /* 1000 [hz], 12/100 [s] */
+ 0xFB, 0x1E, 0x00, 0x0C,
+ 0xFC, 0x18, 0x00, 0x0C,
+ 0xFB, 0x1E, 0x00, 0x0C,
+ 0xFC, 0x18, 0x00, 0x0C,
+ 0xFB, 0x1E, 0x00, 0x0C,
+ 0xFC, 0x18, 0x00, 0x0C,
+ 0xFF, 0xFF, 0x01, 0x90, /* silent, 400/100 [s] */
+ 0x00, 0x00 /* end of sequence */
+};
+
+static int yealink_set_ringtone(struct yealink_dev *yld, u8 *buf, size_t size)
+{
+ struct yld_ctl_packet *p = yld->ctl_data;
+ int ix, len;
+
+ if (size <= 0)
+ return -EINVAL;
+
+ /* Set the ringtone volume */
+ memset(yld->ctl_data, 0, sizeof(*(yld->ctl_data)));
+ yld->ctl_data->cmd = CMD_RING_VOLUME;
+ yld->ctl_data->size = 1;
+ yld->ctl_data->data[0] = buf[0];
+ yealink_cmd(yld, p);
+
+ buf++;
+ size--;
+
+ p->cmd = CMD_RING_NOTE;
+ ix = 0;
+ while (size != ix) {
+ len = size - ix;
+ if (len > sizeof(p->data))
+ len = sizeof(p->data);
+ p->size = len;
+ p->offset = cpu_to_be16(ix);
+ memcpy(p->data, &buf[ix], len);
+ yealink_cmd(yld, p);
+ ix += len;
+ }
+ return 0;
+}
+
+/* keep stat_master & stat_copy in sync.
+ */
+static int yealink_do_idle_tasks(struct yealink_dev *yld)
+{
+ u8 val;
+ int i, ix, len;
+
+ ix = yld->stat_ix;
+
+ memset(yld->ctl_data, 0, sizeof(*(yld->ctl_data)));
+ yld->ctl_data->cmd = CMD_KEYPRESS;
+ yld->ctl_data->size = 1;
+ yld->ctl_data->sum = 0xff - CMD_KEYPRESS;
+
+ /* If state update pointer wraps do a KEYPRESS first. */
+ if (ix >= sizeof(yld->master)) {
+ yld->stat_ix = 0;
+ return 0;
+ }
+
+ /* find update candidates: copy != master */
+ do {
+ val = yld->master.b[ix];
+ if (val != yld->copy.b[ix])
+ goto send_update;
+ } while (++ix < sizeof(yld->master));
+
+ /* nothing todo, wait a bit and poll for a KEYPRESS */
+ yld->stat_ix = 0;
+ /* TODO how can we wait abit. ??
+ * msleep_interruptible(1000 / YEALINK_POLLING_FREQUENCY);
+ */
+ return 0;
+
+send_update:
+
+ /* Setup an appropriate update request */
+ yld->copy.b[ix] = val;
+ yld->ctl_data->data[0] = val;
+
+ switch(ix) {
+ case offsetof(struct yld_status, led):
+ yld->ctl_data->cmd = CMD_LED;
+ yld->ctl_data->sum = -1 - CMD_LED - val;
+ break;
+ case offsetof(struct yld_status, dialtone):
+ yld->ctl_data->cmd = CMD_DIALTONE;
+ yld->ctl_data->sum = -1 - CMD_DIALTONE - val;
+ break;
+ case offsetof(struct yld_status, ringtone):
+ yld->ctl_data->cmd = CMD_RINGTONE;
+ yld->ctl_data->sum = -1 - CMD_RINGTONE - val;
+ break;
+ case offsetof(struct yld_status, keynum):
+ val--;
+ val &= 0x1f;
+ yld->ctl_data->cmd = CMD_SCANCODE;
+ yld->ctl_data->offset = cpu_to_be16(val);
+ yld->ctl_data->data[0] = 0;
+ yld->ctl_data->sum = -1 - CMD_SCANCODE - val;
+ break;
+ default:
+ len = sizeof(yld->master.s.lcd) - ix;
+ if (len > sizeof(yld->ctl_data->data))
+ len = sizeof(yld->ctl_data->data);
+
+ /* Combine up to <len> consecutive LCD bytes in a singe request
+ */
+ yld->ctl_data->cmd = CMD_LCD;
+ yld->ctl_data->offset = cpu_to_be16(ix);
+ yld->ctl_data->size = len;
+ yld->ctl_data->sum = -CMD_LCD - ix - val - len;
+ for(i=1; i<len; i++) {
+ ix++;
+ val = yld->master.b[ix];
+ yld->copy.b[ix] = val;
+ yld->ctl_data->data[i] = val;
+ yld->ctl_data->sum -= val;
+ }
+ }
+ yld->stat_ix = ix + 1;
+ return 1;
+}
+
+/* Decide on how to handle responses
+ *
+ * The state transition diagram is somethhing like:
+ *
+ * syncState<--+
+ * | |
+ * | idle
+ * \|/ |
+ * init --ok--> waitForKey --ok--> getKey
+ * ^ ^ |
+ * | +-------ok-------+
+ * error,start
+ *
+ */
+static void urb_irq_callback(struct urb *urb)
+{
+ struct yealink_dev *yld = urb->context;
+ int ret, status = urb->status;
+
+ if (status)
+ err("%s - urb status %d", __func__, status);
+
+ switch (yld->irq_data->cmd) {
+ case CMD_KEYPRESS:
+
+ yld->master.s.keynum = yld->irq_data->data[0];
+ break;
+
+ case CMD_SCANCODE:
+ dbg("get scancode %x", yld->irq_data->data[0]);
+
+ report_key(yld, map_p1k_to_key(yld->irq_data->data[0]));
+ break;
+
+ default:
+ err("unexpected response %x", yld->irq_data->cmd);
+ }
+
+ yealink_do_idle_tasks(yld);
+
+ if (!yld->shutdown) {
+ ret = usb_submit_urb(yld->urb_ctl, GFP_ATOMIC);
+ if (ret && ret != -EPERM)
+ err("%s - usb_submit_urb failed %d", __func__, ret);
+ }
+}
+
+static void urb_ctl_callback(struct urb *urb)
+{
+ struct yealink_dev *yld = urb->context;
+ int ret = 0, status = urb->status;
+
+ if (status)
+ err("%s - urb status %d", __func__, status);
+
+ switch (yld->ctl_data->cmd) {
+ case CMD_KEYPRESS:
+ case CMD_SCANCODE:
+ /* ask for a response */
+ if (!yld->shutdown)
+ ret = usb_submit_urb(yld->urb_irq, GFP_ATOMIC);
+ break;
+ default:
+ /* send new command */
+ yealink_do_idle_tasks(yld);
+ if (!yld->shutdown)
+ ret = usb_submit_urb(yld->urb_ctl, GFP_ATOMIC);
+ break;
+ }
+
+ if (ret && ret != -EPERM)
+ err("%s - usb_submit_urb failed %d", __func__, ret);
+}
+
+/*******************************************************************************
+ * input event interface
+ ******************************************************************************/
+
+/* TODO should we issue a ringtone on a SND_BELL event?
+static int input_ev(struct input_dev *dev, unsigned int type,
+ unsigned int code, int value)
+{
+
+ if (type != EV_SND)
+ return -EINVAL;
+
+ switch (code) {
+ case SND_BELL:
+ case SND_TONE:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+*/
+
+static int input_open(struct input_dev *dev)
+{
+ struct yealink_dev *yld = input_get_drvdata(dev);
+ int i, ret;
+
+ dbg("%s", __func__);
+
+ /* force updates to device */
+ for (i = 0; i<sizeof(yld->master); i++)
+ yld->copy.b[i] = ~yld->master.b[i];
+ yld->key_code = -1; /* no keys pressed */
+
+ yealink_set_ringtone(yld, default_ringtone, sizeof(default_ringtone));
+
+ /* issue INIT */
+ memset(yld->ctl_data, 0, sizeof(*(yld->ctl_data)));
+ yld->ctl_data->cmd = CMD_INIT;
+ yld->ctl_data->size = 10;
+ yld->ctl_data->sum = 0x100-CMD_INIT-10;
+ if ((ret = usb_submit_urb(yld->urb_ctl, GFP_KERNEL)) != 0) {
+ dbg("%s - usb_submit_urb failed with result %d",
+ __func__, ret);
+ return ret;
+ }
+ return 0;
+}
+
+static void input_close(struct input_dev *dev)
+{
+ struct yealink_dev *yld = input_get_drvdata(dev);
+
+ yld->shutdown = 1;
+ /*
+ * Make sure the flag is seen by other CPUs before we start
+ * killing URBs so new URBs won't be submitted
+ */
+ smp_wmb();
+
+ usb_kill_urb(yld->urb_ctl);
+ usb_kill_urb(yld->urb_irq);
+
+ yld->shutdown = 0;
+ smp_wmb();
+}
+
+/*******************************************************************************
+ * sysfs interface
+ ******************************************************************************/
+
+static DECLARE_RWSEM(sysfs_rwsema);
+
+/* Interface to the 7-segments translation table aka. char set.
+ */
+static ssize_t show_map(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ memcpy(buf, &map_seg7, sizeof(map_seg7));
+ return sizeof(map_seg7);
+}
+
+static ssize_t store_map(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t cnt)
+{
+ if (cnt != sizeof(map_seg7))
+ return -EINVAL;
+ memcpy(&map_seg7, buf, sizeof(map_seg7));
+ return sizeof(map_seg7);
+}
+
+/* Interface to the LCD.
+ */
+
+/* Reading /sys/../lineX will return the format string with its settings:
+ *
+ * Example:
+ * cat ./line3
+ * 888888888888
+ * Linux Rocks!
+ */
+static ssize_t show_line(struct device *dev, char *buf, int a, int b)
+{
+ struct yealink_dev *yld;
+ int i;
+
+ down_read(&sysfs_rwsema);
+ yld = dev_get_drvdata(dev);
+ if (yld == NULL) {
+ up_read(&sysfs_rwsema);
+ return -ENODEV;
+ }
+
+ for (i = a; i < b; i++)
+ *buf++ = lcdMap[i].type;
+ *buf++ = '\n';
+ for (i = a; i < b; i++)
+ *buf++ = yld->lcdMap[i];
+ *buf++ = '\n';
+ *buf = 0;
+
+ up_read(&sysfs_rwsema);
+ return 3 + ((b - a) << 1);
+}
+
+static ssize_t show_line1(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ return show_line(dev, buf, LCD_LINE1_OFFSET, LCD_LINE2_OFFSET);
+}
+
+static ssize_t show_line2(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ return show_line(dev, buf, LCD_LINE2_OFFSET, LCD_LINE3_OFFSET);
+}
+
+static ssize_t show_line3(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ return show_line(dev, buf, LCD_LINE3_OFFSET, LCD_LINE4_OFFSET);
+}
+
+/* Writing to /sys/../lineX will set the coresponding LCD line.
+ * - Excess characters are ignored.
+ * - If less characters are written than allowed, the remaining digits are
+ * unchanged.
+ * - The '\n' or '\t' char is a placeholder, it does not overwrite the
+ * original content.
+ */
+static ssize_t store_line(struct device *dev, const char *buf, size_t count,
+ int el, size_t len)
+{
+ struct yealink_dev *yld;
+ int i;
+
+ down_write(&sysfs_rwsema);
+ yld = dev_get_drvdata(dev);
+ if (yld == NULL) {
+ up_write(&sysfs_rwsema);
+ return -ENODEV;
+ }
+
+ if (len > count)
+ len = count;
+ for (i = 0; i < len; i++)
+ setChar(yld, el++, buf[i]);
+
+ up_write(&sysfs_rwsema);
+ return count;
+}
+
+static ssize_t store_line1(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ return store_line(dev, buf, count, LCD_LINE1_OFFSET, LCD_LINE1_SIZE);
+}
+
+static ssize_t store_line2(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ return store_line(dev, buf, count, LCD_LINE2_OFFSET, LCD_LINE2_SIZE);
+}
+
+static ssize_t store_line3(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ return store_line(dev, buf, count, LCD_LINE3_OFFSET, LCD_LINE3_SIZE);
+}
+
+/* Interface to visible and audible "icons", these include:
+ * pictures on the LCD, the LED, and the dialtone signal.
+ */
+
+/* Get a list of "switchable elements" with their current state. */
+static ssize_t get_icons(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct yealink_dev *yld;
+ int i, ret = 1;
+
+ down_read(&sysfs_rwsema);
+ yld = dev_get_drvdata(dev);
+ if (yld == NULL) {
+ up_read(&sysfs_rwsema);
+ return -ENODEV;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(lcdMap); i++) {
+ if (lcdMap[i].type != '.')
+ continue;
+ ret += sprintf(&buf[ret], "%s %s\n",
+ yld->lcdMap[i] == ' ' ? " " : "on",
+ lcdMap[i].u.p.name);
+ }
+ up_read(&sysfs_rwsema);
+ return ret;
+}
+
+/* Change the visibility of a particular element. */
+static ssize_t set_icon(struct device *dev, const char *buf, size_t count,
+ int chr)
+{
+ struct yealink_dev *yld;
+ int i;
+
+ down_write(&sysfs_rwsema);
+ yld = dev_get_drvdata(dev);
+ if (yld == NULL) {
+ up_write(&sysfs_rwsema);
+ return -ENODEV;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(lcdMap); i++) {
+ if (lcdMap[i].type != '.')
+ continue;
+ if (strncmp(buf, lcdMap[i].u.p.name, count) == 0) {
+ setChar(yld, i, chr);
+ break;
+ }
+ }
+
+ up_write(&sysfs_rwsema);
+ return count;
+}
+
+static ssize_t show_icon(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ return set_icon(dev, buf, count, buf[0]);
+}
+
+static ssize_t hide_icon(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ return set_icon(dev, buf, count, ' ');
+}
+
+/* Upload a ringtone to the device.
+ */
+
+/* Stores raw ringtone data in the phone */
+static ssize_t store_ringtone(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct yealink_dev *yld;
+
+ down_write(&sysfs_rwsema);
+ yld = dev_get_drvdata(dev);
+ if (yld == NULL) {
+ up_write(&sysfs_rwsema);
+ return -ENODEV;
+ }
+
+ /* TODO locking with async usb control interface??? */
+ yealink_set_ringtone(yld, (char *)buf, count);
+ up_write(&sysfs_rwsema);
+ return count;
+}
+
+#define _M444 S_IRUGO
+#define _M664 S_IRUGO|S_IWUSR|S_IWGRP
+#define _M220 S_IWUSR|S_IWGRP
+
+static DEVICE_ATTR(map_seg7 , _M664, show_map , store_map );
+static DEVICE_ATTR(line1 , _M664, show_line1 , store_line1 );
+static DEVICE_ATTR(line2 , _M664, show_line2 , store_line2 );
+static DEVICE_ATTR(line3 , _M664, show_line3 , store_line3 );
+static DEVICE_ATTR(get_icons , _M444, get_icons , NULL );
+static DEVICE_ATTR(show_icon , _M220, NULL , show_icon );
+static DEVICE_ATTR(hide_icon , _M220, NULL , hide_icon );
+static DEVICE_ATTR(ringtone , _M220, NULL , store_ringtone);
+
+static struct attribute *yld_attributes[] = {
+ &dev_attr_line1.attr,
+ &dev_attr_line2.attr,
+ &dev_attr_line3.attr,
+ &dev_attr_get_icons.attr,
+ &dev_attr_show_icon.attr,
+ &dev_attr_hide_icon.attr,
+ &dev_attr_map_seg7.attr,
+ &dev_attr_ringtone.attr,
+ NULL
+};
+
+static struct attribute_group yld_attr_group = {
+ .attrs = yld_attributes
+};
+
+/*******************************************************************************
+ * Linux interface and usb initialisation
+ ******************************************************************************/
+
+struct driver_info {
+ char *name;
+};
+
+static const struct driver_info info_P1K = {
+ .name = "Yealink usb-p1k",
+};
+
+static const struct usb_device_id usb_table [] = {
+ {
+ .match_flags = USB_DEVICE_ID_MATCH_DEVICE |
+ USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x6993,
+ .idProduct = 0xb001,
+ .bInterfaceClass = USB_CLASS_HID,
+ .bInterfaceSubClass = 0,
+ .bInterfaceProtocol = 0,
+ .driver_info = (kernel_ulong_t)&info_P1K
+ },
+ { }
+};
+
+static int usb_cleanup(struct yealink_dev *yld, int err)
+{
+ if (yld == NULL)
+ return err;
+
+ if (yld->idev) {
+ if (err)
+ input_free_device(yld->idev);
+ else
+ input_unregister_device(yld->idev);
+ }
+
+ usb_free_urb(yld->urb_irq);
+ usb_free_urb(yld->urb_ctl);
+
+ usb_buffer_free(yld->udev, sizeof(*(yld->ctl_req)),
+ yld->ctl_req, yld->ctl_req_dma);
+ usb_buffer_free(yld->udev, USB_PKT_LEN,
+ yld->ctl_data, yld->ctl_dma);
+ usb_buffer_free(yld->udev, USB_PKT_LEN,
+ yld->irq_data, yld->irq_dma);
+
+ kfree(yld);
+ return err;
+}
+
+static void usb_disconnect(struct usb_interface *intf)
+{
+ struct yealink_dev *yld;
+
+ down_write(&sysfs_rwsema);
+ yld = usb_get_intfdata(intf);
+ sysfs_remove_group(&intf->dev.kobj, &yld_attr_group);
+ usb_set_intfdata(intf, NULL);
+ up_write(&sysfs_rwsema);
+
+ usb_cleanup(yld, 0);
+}
+
+static int usb_probe(struct usb_interface *intf, const struct usb_device_id *id)
+{
+ struct usb_device *udev = interface_to_usbdev (intf);
+ struct driver_info *nfo = (struct driver_info *)id->driver_info;
+ struct usb_host_interface *interface;
+ struct usb_endpoint_descriptor *endpoint;
+ struct yealink_dev *yld;
+ struct input_dev *input_dev;
+ int ret, pipe, i;
+
+ interface = intf->cur_altsetting;
+ endpoint = &interface->endpoint[0].desc;
+ if (!usb_endpoint_is_int_in(endpoint))
+ return -ENODEV;
+
+ yld = kzalloc(sizeof(struct yealink_dev), GFP_KERNEL);
+ if (!yld)
+ return -ENOMEM;
+
+ yld->udev = udev;
+
+ yld->idev = input_dev = input_allocate_device();
+ if (!input_dev)
+ return usb_cleanup(yld, -ENOMEM);
+
+ /* allocate usb buffers */
+ yld->irq_data = usb_buffer_alloc(udev, USB_PKT_LEN,
+ GFP_ATOMIC, &yld->irq_dma);
+ if (yld->irq_data == NULL)
+ return usb_cleanup(yld, -ENOMEM);
+
+ yld->ctl_data = usb_buffer_alloc(udev, USB_PKT_LEN,
+ GFP_ATOMIC, &yld->ctl_dma);
+ if (!yld->ctl_data)
+ return usb_cleanup(yld, -ENOMEM);
+
+ yld->ctl_req = usb_buffer_alloc(udev, sizeof(*(yld->ctl_req)),
+ GFP_ATOMIC, &yld->ctl_req_dma);
+ if (yld->ctl_req == NULL)
+ return usb_cleanup(yld, -ENOMEM);
+
+ /* allocate urb structures */
+ yld->urb_irq = usb_alloc_urb(0, GFP_KERNEL);
+ if (yld->urb_irq == NULL)
+ return usb_cleanup(yld, -ENOMEM);
+
+ yld->urb_ctl = usb_alloc_urb(0, GFP_KERNEL);
+ if (yld->urb_ctl == NULL)
+ return usb_cleanup(yld, -ENOMEM);
+
+ /* get a handle to the interrupt data pipe */
+ pipe = usb_rcvintpipe(udev, endpoint->bEndpointAddress);
+ ret = usb_maxpacket(udev, pipe, usb_pipeout(pipe));
+ if (ret != USB_PKT_LEN)
+ err("invalid payload size %d, expected %zd", ret, USB_PKT_LEN);
+
+ /* initialise irq urb */
+ usb_fill_int_urb(yld->urb_irq, udev, pipe, yld->irq_data,
+ USB_PKT_LEN,
+ urb_irq_callback,
+ yld, endpoint->bInterval);
+ yld->urb_irq->transfer_dma = yld->irq_dma;
+ yld->urb_irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+ yld->urb_irq->dev = udev;
+
+ /* initialise ctl urb */
+ yld->ctl_req->bRequestType = USB_TYPE_CLASS | USB_RECIP_INTERFACE |
+ USB_DIR_OUT;
+ yld->ctl_req->bRequest = USB_REQ_SET_CONFIGURATION;
+ yld->ctl_req->wValue = cpu_to_le16(0x200);
+ yld->ctl_req->wIndex = cpu_to_le16(interface->desc.bInterfaceNumber);
+ yld->ctl_req->wLength = cpu_to_le16(USB_PKT_LEN);
+
+ usb_fill_control_urb(yld->urb_ctl, udev, usb_sndctrlpipe(udev, 0),
+ (void *)yld->ctl_req, yld->ctl_data, USB_PKT_LEN,
+ urb_ctl_callback, yld);
+ yld->urb_ctl->setup_dma = yld->ctl_req_dma;
+ yld->urb_ctl->transfer_dma = yld->ctl_dma;
+ yld->urb_ctl->transfer_flags |= URB_NO_SETUP_DMA_MAP |
+ URB_NO_TRANSFER_DMA_MAP;
+ yld->urb_ctl->dev = udev;
+
+ /* find out the physical bus location */
+ usb_make_path(udev, yld->phys, sizeof(yld->phys));
+ strlcat(yld->phys, "/input0", sizeof(yld->phys));
+
+ /* register settings for the input device */
+ input_dev->name = nfo->name;
+ input_dev->phys = yld->phys;
+ usb_to_input_id(udev, &input_dev->id);
+ input_dev->dev.parent = &intf->dev;
+
+ input_set_drvdata(input_dev, yld);
+
+ input_dev->open = input_open;
+ input_dev->close = input_close;
+ /* input_dev->event = input_ev; TODO */
+
+ /* register available key events */
+ input_dev->evbit[0] = BIT_MASK(EV_KEY);
+ for (i = 0; i < 256; i++) {
+ int k = map_p1k_to_key(i);
+ if (k >= 0) {
+ set_bit(k & 0xff, input_dev->keybit);
+ if (k >> 8)
+ set_bit(k >> 8, input_dev->keybit);
+ }
+ }
+
+ ret = input_register_device(yld->idev);
+ if (ret)
+ return usb_cleanup(yld, ret);
+
+ usb_set_intfdata(intf, yld);
+
+ /* clear visible elements */
+ for (i = 0; i < ARRAY_SIZE(lcdMap); i++)
+ setChar(yld, i, ' ');
+
+ /* display driver version on LCD line 3 */
+ store_line3(&intf->dev, NULL,
+ DRIVER_VERSION, sizeof(DRIVER_VERSION));
+
+ /* Register sysfs hooks (don't care about failure) */
+ ret = sysfs_create_group(&intf->dev.kobj, &yld_attr_group);
+ return 0;
+}
+
+static struct usb_driver yealink_driver = {
+ .name = "yealink",
+ .probe = usb_probe,
+ .disconnect = usb_disconnect,
+ .id_table = usb_table,
+};
+
+static int __init yealink_dev_init(void)
+{
+ int ret = usb_register(&yealink_driver);
+ if (ret == 0)
+ printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":"
+ DRIVER_DESC "\n");
+ return ret;
+}
+
+static void __exit yealink_dev_exit(void)
+{
+ usb_deregister(&yealink_driver);
+}
+
+module_init(yealink_dev_init);
+module_exit(yealink_dev_exit);
+
+MODULE_DEVICE_TABLE (usb, usb_table);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/misc/yealink.h b/drivers/input/misc/yealink.h
new file mode 100644
index 0000000..48af0be
--- /dev/null
+++ b/drivers/input/misc/yealink.h
@@ -0,0 +1,220 @@
+/*
+ * drivers/usb/input/yealink.h
+ *
+ * Copyright (c) 2005 Henk Vergonet <Henk.Vergonet@gmail.com>
+ *
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef INPUT_YEALINK_H
+#define INPUT_YEALINK_H
+
+/* Using the control channel on interface 3 various aspects of the phone
+ * can be controlled like LCD, LED, dialtone and the ringtone.
+ */
+
+struct yld_ctl_packet {
+ u8 cmd; /* command code, see below */
+ u8 size; /* 1-11, size of used data bytes. */
+ u16 offset; /* internal packet offset */
+ u8 data[11];
+ s8 sum; /* negative sum of 15 preceding bytes */
+} __attribute__ ((packed));
+
+#define USB_PKT_LEN sizeof(struct yld_ctl_packet)
+
+/* The following yld_ctl_packet's are available: */
+
+/* Init registers
+ *
+ * cmd 0x8e
+ * size 10
+ * offset 0
+ * data 0,0,0,0....
+ */
+#define CMD_INIT 0x8e
+
+/* Request key scan
+ *
+ * cmd 0x80
+ * size 1
+ * offset 0
+ * data[0] on return returns the key number, if it changes there's a new
+ * key pressed.
+ */
+#define CMD_KEYPRESS 0x80
+
+/* Request scancode
+ *
+ * cmd 0x81
+ * size 1
+ * offset key number [0-1f]
+ * data[0] on return returns the scancode
+ */
+#define CMD_SCANCODE 0x81
+
+/* Set LCD
+ *
+ * cmd 0x04
+ * size 1-11
+ * offset 0-23
+ * data segment bits
+ */
+#define CMD_LCD 0x04
+
+/* Set led
+ *
+ * cmd 0x05
+ * size 1
+ * offset 0
+ * data[0] 0 OFF / 1 ON
+ */
+#define CMD_LED 0x05
+
+/* Set ringtone volume
+ *
+ * cmd 0x11
+ * size 1
+ * offset 0
+ * data[0] 0-0xff volume
+ */
+#define CMD_RING_VOLUME 0x11
+
+/* Set ringtone notes
+ *
+ * cmd 0x02
+ * size 1-11
+ * offset 0->
+ * data binary representation LE16(-freq), LE16(duration) ....
+ */
+#define CMD_RING_NOTE 0x02
+
+/* Sound ringtone via the speaker on the back
+ *
+ * cmd 0x03
+ * size 1
+ * offset 0
+ * data[0] 0 OFF / 0x24 ON
+ */
+#define CMD_RINGTONE 0x03
+
+/* Sound dial tone via the ear speaker
+ *
+ * cmd 0x09
+ * size 1
+ * offset 0
+ * data[0] 0 OFF / 1 ON
+ */
+#define CMD_DIALTONE 0x09
+
+#endif /* INPUT_YEALINK_H */
+
+
+#if defined(_SEG) && defined(_PIC)
+/* This table maps the LCD segments onto individual bit positions in the
+ * yld_status struct.
+ */
+
+/* LCD, each segment must be driven seperately.
+ *
+ * Layout:
+ *
+ * |[] [][] [][] [][] in |[][]
+ * |[] M [][] D [][] : [][] out |[][]
+ * store
+ *
+ * NEW REP SU MO TU WE TH FR SA
+ *
+ * [] [] [] [] [] [] [] [] [] [] [] []
+ * [] [] [] [] [] [] [] [] [] [] [] []
+ */
+
+/* Line 1
+ * Format : 18.e8.M8.88...188
+ * Icon names : M D : IN OUT STORE
+ */
+#define LCD_LINE1_OFFSET 0
+#define LCD_LINE1_SIZE 17
+
+/* Note: first g then f => ! ! */
+/* _SEG( type a b c d e g f ) */
+ _SEG('1', 0,0 , 22,2 , 22,2 , 0,0 , 0,0 , 0,0 , 0,0 ),
+ _SEG('8', 20,1 , 20,2 , 20,4 , 20,8 , 21,4 , 21,2 , 21,1 ),
+ _PIC('.', 22,1 , "M" ),
+ _SEG('e', 18,1 , 18,2 , 18,4 , 18,1 , 19,2 , 18,1 , 19,1 ),
+ _SEG('8', 16,1 , 16,2 , 16,4 , 16,8 , 17,4 , 17,2 , 17,1 ),
+ _PIC('.', 15,8 , "D" ),
+ _SEG('M', 14,1 , 14,2 , 14,4 , 14,1 , 15,4 , 15,2 , 15,1 ),
+ _SEG('8', 12,1 , 12,2 , 12,4 , 12,8 , 13,4 , 13,2 , 13,1 ),
+ _PIC('.', 11,8 , ":" ),
+ _SEG('8', 10,1 , 10,2 , 10,4 , 10,8 , 11,4 , 11,2 , 11,1 ),
+ _SEG('8', 8,1 , 8,2 , 8,4 , 8,8 , 9,4 , 9,2 , 9,1 ),
+ _PIC('.', 7,1 , "IN" ),
+ _PIC('.', 7,2 , "OUT" ),
+ _PIC('.', 7,4 , "STORE" ),
+ _SEG('1', 0,0 , 5,1 , 5,1 , 0,0 , 0,0 , 0,0 , 0,0 ),
+ _SEG('8', 4,1 , 4,2 , 4,4 , 4,8 , 5,8 , 5,4 , 5,2 ),
+ _SEG('8', 2,1 , 2,2 , 2,4 , 2,8 , 3,4 , 3,2 , 3,1 ),
+
+/* Line 2
+ * Format : .........
+ * Pict. name : NEW REP SU MO TU WE TH FR SA
+ */
+#define LCD_LINE2_OFFSET LCD_LINE1_OFFSET + LCD_LINE1_SIZE
+#define LCD_LINE2_SIZE 9
+
+ _PIC('.', 23,2 , "NEW" ),
+ _PIC('.', 23,4 , "REP" ),
+ _PIC('.', 1,8 , "SU" ),
+ _PIC('.', 1,4 , "MO" ),
+ _PIC('.', 1,2 , "TU" ),
+ _PIC('.', 1,1 , "WE" ),
+ _PIC('.', 0,1 , "TH" ),
+ _PIC('.', 0,2 , "FR" ),
+ _PIC('.', 0,4 , "SA" ),
+
+/* Line 3
+ * Format : 888888888888
+ */
+#define LCD_LINE3_OFFSET LCD_LINE2_OFFSET + LCD_LINE2_SIZE
+#define LCD_LINE3_SIZE 12
+
+ _SEG('8', 22,16, 22,32, 22,64, 22,128, 23,128, 23,64, 23,32 ),
+ _SEG('8', 20,16, 20,32, 20,64, 20,128, 21,128, 21,64, 21,32 ),
+ _SEG('8', 18,16, 18,32, 18,64, 18,128, 19,128, 19,64, 19,32 ),
+ _SEG('8', 16,16, 16,32, 16,64, 16,128, 17,128, 17,64, 17,32 ),
+ _SEG('8', 14,16, 14,32, 14,64, 14,128, 15,128, 15,64, 15,32 ),
+ _SEG('8', 12,16, 12,32, 12,64, 12,128, 13,128, 13,64, 13,32 ),
+ _SEG('8', 10,16, 10,32, 10,64, 10,128, 11,128, 11,64, 11,32 ),
+ _SEG('8', 8,16, 8,32, 8,64, 8,128, 9,128, 9,64, 9,32 ),
+ _SEG('8', 6,16, 6,32, 6,64, 6,128, 7,128, 7,64, 7,32 ),
+ _SEG('8', 4,16, 4,32, 4,64, 4,128, 5,128, 5,64, 5,32 ),
+ _SEG('8', 2,16, 2,32, 2,64, 2,128, 3,128, 3,64, 3,32 ),
+ _SEG('8', 0,16, 0,32, 0,64, 0,128, 1,128, 1,64, 1,32 ),
+
+/* Line 4
+ *
+ * The LED, DIALTONE and RINGTONE are implemented as icons and use the same
+ * sysfs interface.
+ */
+#define LCD_LINE4_OFFSET LCD_LINE3_OFFSET + LCD_LINE3_SIZE
+
+ _PIC('.', offsetof(struct yld_status, led) , 0x01, "LED" ),
+ _PIC('.', offsetof(struct yld_status, dialtone) , 0x01, "DIALTONE" ),
+ _PIC('.', offsetof(struct yld_status, ringtone) , 0x24, "RINGTONE" ),
+
+#undef _SEG
+#undef _PIC
+#endif /* _SEG && _PIC */
OpenPOWER on IntegriCloud