* [PATCH] Input: add appleir USB driver
@ 2010-04-21 13:51 Bastien Nocera
0 siblings, 0 replies; 57+ messages in thread
From: Bastien Nocera @ 2010-04-21 13:51 UTC (permalink / raw)
To: linux-input; +Cc: Dmitry Torokhov
This driver was originally written by James McKenzie, updated by
Greg Kroah-Hartman, further updated by myself, with suspend support
added.
More recent versions of the IR receiver are also supported through
a patch by Alex Karpenko.
Tested on a MacbookAir1,1
Signed-off-by: Bastien Nocera <hadess@hadess.net>
---
Documentation/input/appleir.txt | 45 ++++
drivers/hid/hid-apple.c | 4 -
drivers/hid/hid-core.c | 5 +-
drivers/hid/hid-ids.h | 1 +
drivers/input/misc/Kconfig | 13 ++
drivers/input/misc/Makefile | 1 +
drivers/input/misc/appleir.c | 453 +++++++++++++++++++++++++++++++++++++++
7 files changed, 516 insertions(+), 6 deletions(-)
create mode 100644 Documentation/input/appleir.txt
create mode 100644 drivers/input/misc/appleir.c
diff --git a/Documentation/input/appleir.txt b/Documentation/input/appleir.txt
new file mode 100644
index 0000000..0267a4b
--- /dev/null
+++ b/Documentation/input/appleir.txt
@@ -0,0 +1,45 @@
+Apple IR receiver Driver (appleir)
+----------------------------------
+ Copyright (C) 2009 Bastien Nocera <hadess@hadess.net>
+
+The appleir driver is a kernel input driver to handle Apple's IR
+receivers (and associated remotes) in the kernel.
+
+The driver is an input driver which only handles "official" remotes
+as built and sold by Apple.
+
+Authors
+-------
+
+James McKenzie (original driver)
+Alex Karpenko (05ac:8242 support)
+Greg Kroah-Hartman (cleanups and original submission)
+Bastien Nocera (further cleanups and suspend support)
+
+Supported hardware
+------------------
+
+- All Apple laptops and desktops from 2005 onwards, except:
+ - the unibody Macbook (2009)
+ - Mac Pro (all versions)
+- Apple TV (all revisions)
+
+The remote will only support the 6 buttons of the original remotes
+as sold by Apple. See the next section if you want to use other remotes
+or want to use lirc with the device instead of the kernel driver.
+
+Using lirc (native) instead of the kernel driver
+------------------------------------------------
+
+First, you will need to disable the kernel driver for the receiver.
+
+This can be achieved by passing quirks to the usbhid driver.
+The quirk line would be:
+usbhid.quirks=0x05ac:0x8242:0x40000010
+
+With 0x05ac being the vendor ID (Apple, you shouldn't need to change this)
+With 0x8242 being the product ID (check the output of lsusb for your hardware)
+And 0x10 being "HID_QUIRK_HIDDEV_FORCE" and 0x40000000 being "HID_QUIRK_NO_IGNORE"
+
+This should force the creation of a hiddev device for the receiver, and
+make it usable under lirc.
diff --git a/drivers/hid/hid-apple.c b/drivers/hid/hid-apple.c
index 78286b1..5f2a731 100644
--- a/drivers/hid/hid-apple.c
+++ b/drivers/hid/hid-apple.c
@@ -360,10 +360,6 @@ static void apple_remove(struct hid_device *hdev)
}
static const struct hid_device_id apple_devices[] = {
- { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ATV_IRCONTROL),
- .driver_data = APPLE_HIDDEV | APPLE_IGNORE_HIDINPUT },
- { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL4),
- .driver_data = APPLE_HIDDEV | APPLE_IGNORE_HIDINPUT },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MIGHTYMOUSE),
.driver_data = APPLE_MIGHTYMOUSE | APPLE_INVERT_HWHEEL },
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index 368fbb0..b57e5f7 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -1253,8 +1253,6 @@ static const struct hid_device_id hid_blacklist[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_3M, USB_DEVICE_ID_3M1968) },
{ HID_USB_DEVICE(USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_WCP32PU) },
{ HID_USB_DEVICE(USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_X5_005D) },
- { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ATV_IRCONTROL) },
- { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL4) },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MIGHTYMOUSE) },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGICMOUSE) },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_ANSI) },
@@ -1553,6 +1551,9 @@ static const struct hid_device_id hid_ignore_list[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_24) },
{ HID_USB_DEVICE(USB_VENDOR_ID_AIRCABLE, USB_DEVICE_ID_AIRCABLE1) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ALCOR, USB_DEVICE_ID_ALCOR_USBRS232) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ATV_IRCONTROL) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL4) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ASUS, USB_DEVICE_ID_ASUS_T91MT)},
{ HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK, USB_DEVICE_ID_ASUSTEK_LCM)},
{ HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK, USB_DEVICE_ID_ASUSTEK_LCM2)},
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
index 72c05f9..66a2ca8 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -97,6 +97,7 @@
#define USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_JIS 0x023b
#define USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY 0x030a
#define USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY 0x030b
+#define USB_DEVICE_ID_APPLE_IRCONTROL 0x8240
#define USB_DEVICE_ID_APPLE_ATV_IRCONTROL 0x8241
#define USB_DEVICE_ID_APPLE_IRCONTROL4 0x8242
diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
index 23140a3..46614b2 100644
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -159,6 +159,19 @@ config INPUT_KEYSPAN_REMOTE
To compile this driver as a module, choose M here: the module will
be called keyspan_remote.
+config INPUT_APPLEIR
+ tristate "Apple infrared receiver (built in)"
+ depends on USB_ARCH_HAS_HCD
+ select USB
+ help
+ Say Y here if you want to use a Apple infrared remote control. All
+ the Apple computers from 2005 onwards include such a port, except
+ the unibody Macbook (2009), and Mac Pros. This receiver is also
+ used in the Apple TV set-top box.
+
+ To compile this driver as a module, choose M here: the module will
+ be called appleir.
+
config INPUT_POWERMATE
tristate "Griffin PowerMate and Contour Jog support"
depends on USB_ARCH_HAS_HCD
diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
index 7e95a5d..3fa4404 100644
--- a/drivers/input/misc/Makefile
+++ b/drivers/input/misc/Makefile
@@ -6,6 +6,7 @@
obj-$(CONFIG_INPUT_88PM860X_ONKEY) += 88pm860x_onkey.o
obj-$(CONFIG_INPUT_APANEL) += apanel.o
+obj-$(CONFIG_INPUT_APPLEIR) += appleir.o
obj-$(CONFIG_INPUT_ATI_REMOTE) += ati_remote.o
obj-$(CONFIG_INPUT_ATI_REMOTE2) += ati_remote2.o
obj-$(CONFIG_INPUT_ATLAS_BTNS) += atlas_btns.o
diff --git a/drivers/input/misc/appleir.c b/drivers/input/misc/appleir.c
new file mode 100644
index 0000000..cff4df6
--- /dev/null
+++ b/drivers/input/misc/appleir.c
@@ -0,0 +1,453 @@
+/*
+ * appleir: USB driver for the apple ir device
+ *
+ * Original driver written by James McKenzie
+ * Ported to recent 2.6 kernel versions by Greg Kroah-Hartman <gregkh@suse.de>
+ *
+ * Copyright (C) 2006 James McKenzie
+ * Copyright (C) 2008 Greg Kroah-Hartman <greg@kroah.com>
+ * Copyright (C) 2008 Novell Inc.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/usb/input.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/usb.h>
+#include <linux/usb/input.h>
+#include <asm/unaligned.h>
+#include <asm/byteorder.h>
+
+#define DRIVER_VERSION "v1.2"
+#define DRIVER_AUTHOR "James McKenzie"
+#define DRIVER_DESC "Apple infrared receiver driver"
+#define DRIVER_LICENSE "GPL"
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE(DRIVER_LICENSE);
+
+#define USB_VENDOR_ID_APPLE 0x05ac
+#define USB_DEVICE_ID_APPLE_IRCONTROL 0x8240
+#define USB_DEVICE_ID_APPLE_ATV_IRCONTROL 0x8241
+#define USB_DEVICE_ID_APPLE_IRCONTROL4 0x8242
+
+#define URB_SIZE 32
+
+#define MAX_KEYS 8
+#define MAX_KEYS_MASK (MAX_KEYS - 1)
+
+#define dbginfo(dev, format, arg...) do { if (debug) dev_info(dev , format , ## arg); } while (0)
+
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Enable extra debug messages and information");
+
+/* I have two devices both of which report the following */
+/* 25 87 ee 83 0a + */
+/* 25 87 ee 83 0c - */
+/* 25 87 ee 83 09 << */
+/* 25 87 ee 83 06 >> */
+/* 25 87 ee 83 05 >" */
+/* 25 87 ee 83 03 menu */
+/* 26 00 00 00 00 for key repeat*/
+
+/* Thomas Glanzmann reports the following responses */
+/* 25 87 ee ca 0b + */
+/* 25 87 ee ca 0d - */
+/* 25 87 ee ca 08 << */
+/* 25 87 ee ca 07 >> */
+/* 25 87 ee ca 04 >" */
+/* 25 87 ee ca 02 menu */
+/* 26 00 00 00 00 for key repeat*/
+/* He also observes the following event sometimes */
+/* sent after a key is release, which I interpret */
+/* as a flat battery message */
+/* 25 87 e0 ca 06 flat battery */
+
+/* Alexandre Karpenko reports the following responses for Device ID 0x8242 */
+/* 25 87 ee 47 0b + */
+/* 25 87 ee 47 0d - */
+/* 25 87 ee 47 08 << */
+/* 25 87 ee 47 07 >> */
+/* 25 87 ee 47 04 >" */
+/* 25 87 ee 47 02 menu */
+/* 26 87 ee 47 ** for key repeat (** is the code of the key being held) */
+
+static const unsigned short appleir_key_table[] = {
+ KEY_RESERVED,
+ KEY_MENU,
+ KEY_PLAYPAUSE,
+ KEY_FORWARD,
+ KEY_BACK,
+ KEY_VOLUMEUP,
+ KEY_VOLUMEDOWN,
+ KEY_RESERVED,
+};
+
+struct appleir {
+ struct input_dev *input_dev;
+ unsigned short keymap[ARRAY_SIZE(appleir_key_table)];
+ u8 *data;
+ dma_addr_t dma_buf;
+ struct usb_device *usbdev;
+ unsigned int flags;
+ struct urb *urb;
+ struct timer_list key_up_timer;
+ int current_key;
+ char phys[32];
+};
+
+static DEFINE_MUTEX(appleir_mutex);
+
+enum {
+ APPLEIR_OPENED = 0x1,
+ APPLEIR_SUSPENDED = 0x2,
+};
+
+static struct usb_device_id appleir_ids[] = {
+ { USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL) },
+ { USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ATV_IRCONTROL) },
+ { USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL4) },
+ {}
+};
+MODULE_DEVICE_TABLE(usb, appleir_ids);
+
+static void dump_packet(struct appleir *appleir, char *msg, u8 *data, int len)
+{
+ int i;
+
+ printk(KERN_ERR "appleir: %s (%d bytes)", msg, len);
+
+ for (i = 0; i < len; ++i)
+ printk(" %02x", data[i]);
+ printk("\n");
+}
+
+static void key_up(struct appleir *appleir, int key)
+{
+ dbginfo(&appleir->input_dev->dev, "key %d up\n", key);
+ input_report_key(appleir->input_dev, key, 0);
+ input_sync(appleir->input_dev);
+}
+
+static void key_down(struct appleir *appleir, int key)
+{
+ dbginfo(&appleir->input_dev->dev, "key %d down\n", key);
+ input_report_key(appleir->input_dev, key, 1);
+ input_sync(appleir->input_dev);
+}
+
+static void battery_flat(struct appleir *appleir)
+{
+ dev_err(&appleir->input_dev->dev, "possible flat battery?\n");
+}
+
+static void key_up_tick(unsigned long data)
+{
+ struct appleir *appleir = (struct appleir *)data;
+
+ if (appleir->current_key) {
+ key_up(appleir, appleir->current_key);
+ appleir->current_key = 0;
+ }
+}
+
+static void new_data(struct appleir *appleir, u8 *data, int len)
+{
+ static const u8 keydown[] = { 0x25, 0x87, 0xee };
+ static const u8 keyrepeat[] = { 0x26, };
+ static const u8 flatbattery[] = { 0x25, 0x87, 0xe0 };
+
+ if (debug)
+ dump_packet(appleir, "received", data, len);
+
+ if (len != 5)
+ return;
+
+ if (!memcmp(data, keydown, sizeof(keydown))) {
+ /* If we already have a key down, take it up before marking
+ this one down */
+ if (appleir->current_key)
+ key_up(appleir, appleir->current_key);
+ appleir->current_key = appleir->keymap[(data[4] >> 1) & MAX_KEYS_MASK];
+
+ key_down(appleir, appleir->current_key);
+ /* Remote doesn't do key up, either pull them up, in the test
+ above, or here set a timer which pulls them up after 1/8 s */
+ mod_timer(&appleir->key_up_timer, jiffies + HZ / 8);
+
+ return;
+ }
+
+ if (!memcmp(data, keyrepeat, sizeof(keyrepeat))) {
+ key_down(appleir, appleir->current_key);
+ /* Remote doesn't do key up, either pull them up, in the test
+ above, or here set a timer which pulls them up after 1/8 s */
+ mod_timer(&appleir->key_up_timer, jiffies + HZ / 8);
+ return;
+ }
+
+ if (!memcmp(data, flatbattery, sizeof(flatbattery))) {
+ battery_flat(appleir);
+ /* Fall through */
+ }
+
+ dump_packet(appleir, "unknown packet", data, len);
+}
+
+static void appleir_urb(struct urb *urb)
+{
+ struct appleir *appleir = urb->context;
+ int status = urb->status;
+ int retval;
+
+ switch (status) {
+ case 0:
+ new_data(appleir, urb->transfer_buffer, urb->actual_length);
+ break;
+ case -ECONNRESET:
+ case -ENOENT:
+ case -ESHUTDOWN:
+ /* This urb is terminated, clean up */
+ dbginfo(&appleir->input_dev->dev, "%s - urb shutting down with status: %d", __func__,
+ urb->status);
+ return;
+ default:
+ dbginfo(&appleir->input_dev->dev, "%s - nonzero urb status received: %d", __func__,
+ urb->status);
+ }
+
+ retval = usb_submit_urb(urb, GFP_ATOMIC);
+ if (retval)
+ err("%s - usb_submit_urb failed with result %d", __func__,
+ retval);
+}
+
+static int appleir_open(struct input_dev *dev)
+{
+ struct appleir *appleir = input_get_drvdata(dev);
+ struct usb_interface *intf = usb_ifnum_to_if(appleir->usbdev, 0);
+ int r;
+
+ r = usb_autopm_get_interface(intf);
+ if (r) {
+ dev_err(&intf->dev,
+ "%s(): usb_autopm_get_interface() = %d\n", __func__, r);
+ return r;
+ }
+
+ mutex_lock(&appleir_mutex);
+
+ if (usb_submit_urb(appleir->urb, GFP_ATOMIC)) {
+ r = -EIO;
+ goto fail;
+ }
+
+ appleir->flags |= APPLEIR_OPENED;
+
+ mutex_unlock(&appleir_mutex);
+
+ usb_autopm_put_interface(intf);
+
+ return 0;
+fail:
+ mutex_unlock(&appleir_mutex);
+ usb_autopm_put_interface(intf);
+ return r;
+}
+
+static void appleir_close(struct input_dev *dev)
+{
+ struct appleir *appleir = input_get_drvdata(dev);
+
+ mutex_lock(&appleir_mutex);
+
+ if (!(appleir->flags & APPLEIR_SUSPENDED)) {
+ usb_kill_urb(appleir->urb);
+ del_timer_sync(&appleir->key_up_timer);
+ }
+
+ appleir->flags &= ~APPLEIR_OPENED;
+
+ mutex_unlock(&appleir_mutex);
+}
+
+static int appleir_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ struct usb_device *dev = interface_to_usbdev(intf);
+ struct usb_endpoint_descriptor *endpoint;
+ struct appleir *appleir = NULL;
+ struct input_dev *input_dev;
+ int retval = -ENOMEM;
+ int i;
+
+ appleir = kzalloc(sizeof(struct appleir), GFP_KERNEL);
+ if (!appleir)
+ goto allocfail;
+
+ appleir->data = usb_buffer_alloc(dev, URB_SIZE, GFP_KERNEL,
+ &appleir->dma_buf);
+ if (!appleir->data)
+ goto usbfail;
+
+ appleir->urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!appleir->urb)
+ goto urbfail;
+
+ appleir->usbdev = dev;
+
+ input_dev = input_allocate_device();
+ if (!input_dev)
+ goto inputfail;
+
+ appleir->input_dev = input_dev;
+
+ usb_make_path(dev, appleir->phys, sizeof(appleir->phys));
+ strlcpy(appleir->phys, "/input0", sizeof(appleir->phys));
+
+ input_dev->name = "Apple Infrared Remote Controller";
+ input_dev->phys = appleir->phys;
+ usb_to_input_id(dev, &input_dev->id);
+ input_dev->dev.parent = &intf->dev;
+ input_dev->keycode = appleir->keymap;
+ input_dev->keycodesize = sizeof(unsigned short);
+ input_dev->keycodemax = ARRAY_SIZE(appleir->keymap);
+
+ input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP);
+
+ memcpy(appleir->keymap, appleir_key_table, sizeof(appleir->keymap));
+ for (i = 0; i < ARRAY_SIZE(appleir_key_table); i++)
+ set_bit(appleir->keymap[i], input_dev->keybit);
+ clear_bit(KEY_RESERVED, input_dev->keybit);
+
+ input_set_drvdata(input_dev, appleir);
+ input_dev->open = appleir_open;
+ input_dev->close = appleir_close;
+
+ endpoint = &intf->cur_altsetting->endpoint[0].desc;
+
+ usb_fill_int_urb(appleir->urb, dev,
+ usb_rcvintpipe(dev, endpoint->bEndpointAddress),
+ appleir->data, 8,
+ appleir_urb, appleir, endpoint->bInterval);
+
+ appleir->urb->transfer_dma = appleir->dma_buf;
+ appleir->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+ setup_timer(&appleir->key_up_timer,
+ key_up_tick, (unsigned long) appleir);
+
+ retval = input_register_device(appleir->input_dev);
+ if (retval)
+ goto inputfail;
+
+ usb_set_intfdata(intf, appleir);
+
+ return 0;
+
+inputfail:
+ input_free_device(appleir->input_dev);
+
+urbfail:
+ usb_free_urb(appleir->urb);
+
+usbfail:
+ usb_buffer_free(dev, URB_SIZE, appleir->data,
+ appleir->dma_buf);
+
+allocfail:
+ kfree(appleir);
+
+ return retval;
+}
+
+static void appleir_disconnect(struct usb_interface *intf)
+{
+ struct appleir *appleir = usb_get_intfdata(intf);
+
+ usb_set_intfdata(intf, NULL);
+ input_unregister_device(appleir->input_dev);
+ usb_free_urb(appleir->urb);
+ usb_buffer_free(interface_to_usbdev(intf), URB_SIZE,
+ appleir->data, appleir->dma_buf);
+ kfree(appleir);
+}
+
+static int appleir_suspend(struct usb_interface *interface,
+ pm_message_t message)
+{
+ struct appleir *appleir = usb_get_intfdata(interface);
+
+ mutex_lock(&appleir_mutex);
+ if (appleir->flags & APPLEIR_OPENED)
+ usb_kill_urb(appleir->urb);
+
+ appleir->flags |= APPLEIR_SUSPENDED;
+
+ mutex_unlock(&appleir_mutex);
+
+ return 0;
+}
+
+static int appleir_resume(struct usb_interface *interface)
+{
+ struct appleir *appleir;
+ int r = 0;
+
+ appleir = usb_get_intfdata(interface);
+
+ mutex_lock(&appleir_mutex);
+ if (appleir->flags & APPLEIR_OPENED) {
+ struct usb_endpoint_descriptor *endpoint;
+
+ endpoint = &interface->cur_altsetting->endpoint[0].desc;
+ usb_fill_int_urb(appleir->urb, appleir->usbdev,
+ usb_rcvintpipe(appleir->usbdev, endpoint->bEndpointAddress),
+ appleir->data, 8,
+ appleir_urb, appleir, endpoint->bInterval);
+ appleir->urb->transfer_dma = appleir->dma_buf;
+ appleir->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+ /* And reset the USB device */
+ if (usb_submit_urb(appleir->urb, GFP_ATOMIC))
+ r = -EIO;
+ }
+
+ appleir->flags &= ~APPLEIR_SUSPENDED;
+
+ mutex_unlock(&appleir_mutex);
+
+ return r;
+}
+
+static struct usb_driver appleir_driver = {
+ .name = "appleir",
+ .probe = appleir_probe,
+ .disconnect = appleir_disconnect,
+ .suspend = appleir_suspend,
+ .resume = appleir_resume,
+ .reset_resume = appleir_resume,
+ .id_table = appleir_ids,
+};
+
+static int __init appleir_init(void)
+{
+ return usb_register(&appleir_driver);
+}
+
+static void __exit appleir_exit(void)
+{
+ usb_deregister(&appleir_driver);
+}
+
+module_init(appleir_init);
+module_exit(appleir_exit);
--
1.6.6.1
^ permalink raw reply related [flat|nested] 57+ messages in thread
* [PATCH] Input: add appleir USB driver
@ 2012-11-15 18:13 Bastien Nocera
2012-11-19 15:32 ` Benjamin Tissoires
0 siblings, 1 reply; 57+ messages in thread
From: Bastien Nocera @ 2012-11-15 18:13 UTC (permalink / raw)
To: linux-input, Jarod Wilson, benjamin.tissoires
This driver was originally written by James McKenzie, updated by
Greg Kroah-Hartman, further updated by myself, with suspend support
added.
More recent versions of the IR receiver are also supported through
a patch by Alex Karpenko. The patch also adds support for the 2nd
and 5th generation of the controller, and the menu key on newer
brushed metal remotes.
Tested on a MacbookAir1,1
Signed-off-by: Bastien Nocera <hadess@hadess.net>
---
Resend, as the original patch never made it. I cleaned up the patch a
bit further, and test compiled it, but didn't have a chance to test it
as I don't have a machine with that hardware available anymore.
Documentation/input/appleir.txt | 46 ++++
drivers/hid/hid-apple.c | 4 -
drivers/hid/hid-core.c | 7 +-
drivers/hid/hid-ids.h | 5 +-
drivers/input/misc/Kconfig | 13 +
drivers/input/misc/Makefile | 1 +
drivers/input/misc/appleir.c | 527 ++++++++++++++++++++++++++++++++++++++++
7 files changed, 596 insertions(+), 7 deletions(-)
create mode 100644 Documentation/input/appleir.txt
create mode 100644 drivers/input/misc/appleir.c
diff --git a/Documentation/input/appleir.txt b/Documentation/input/appleir.txt
new file mode 100644
index 0000000..db637fb
--- /dev/null
+++ b/Documentation/input/appleir.txt
@@ -0,0 +1,46 @@
+Apple IR receiver Driver (appleir)
+----------------------------------
+ Copyright (C) 2009 Bastien Nocera <hadess@hadess.net>
+
+The appleir driver is a kernel input driver to handle Apple's IR
+receivers (and associated remotes) in the kernel.
+
+The driver is an input driver which only handles "official" remotes
+as built and sold by Apple.
+
+Authors
+-------
+
+James McKenzie (original driver)
+Alex Karpenko (05ac:8242 support)
+Greg Kroah-Hartman (cleanups and original submission)
+Bastien Nocera (further cleanups, brushed metal "enter"
+button support and suspend support)
+
+Supported hardware
+------------------
+
+- All Apple laptops and desktops from 2005 onwards, except:
+ - the unibody Macbook (2009)
+ - Mac Pro (all versions)
+- Apple TV (all revisions prior to September 2010)
+
+The remote will only support the 6 (old white) or 7 (brushed metal) buttons
+of the remotes as sold by Apple. See the next section if you want to use
+other remotes or want to use lirc with the device instead of the kernel driver.
+
+Using lirc (native) instead of the kernel driver
+------------------------------------------------
+
+First, you will need to disable the kernel driver for the receiver.
+
+This can be achieved by passing quirks to the usbhid driver.
+The quirk line would be:
+usbhid.quirks=0x05ac:0x8242:0x40000010
+
+With 0x05ac being the vendor ID (Apple, you shouldn't need to change this)
+With 0x8242 being the product ID (check the output of lsusb for your hardware)
+And 0x10 being "HID_QUIRK_HIDDEV_FORCE" and 0x40000000 being "HID_QUIRK_NO_IGNORE"
+
+This should force the creation of a hiddev device for the receiver, and
+make it usable under lirc.
diff --git a/drivers/hid/hid-apple.c b/drivers/hid/hid-apple.c
index fd7722a..30a4824 100644
--- a/drivers/hid/hid-apple.c
+++ b/drivers/hid/hid-apple.c
@@ -390,10 +390,6 @@ static void apple_remove(struct hid_device *hdev)
}
static const struct hid_device_id apple_devices[] = {
- { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ATV_IRCONTROL),
- .driver_data = APPLE_HIDDEV | APPLE_IGNORE_HIDINPUT },
- { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL4),
- .driver_data = APPLE_HIDDEV | APPLE_IGNORE_HIDINPUT },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MIGHTYMOUSE),
.driver_data = APPLE_MIGHTYMOUSE | APPLE_INVERT_HWHEEL },
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index f4109fd..3fd4c10 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -1471,8 +1471,6 @@ static const struct hid_device_id hid_have_special_driver[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_X5_005D) },
{ HID_USB_DEVICE(USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_RP_649) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ACRUX, 0x0802) },
- { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ATV_IRCONTROL) },
- { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL4) },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MIGHTYMOUSE) },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGICMOUSE) },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGICTRACKPAD) },
@@ -1925,6 +1923,11 @@ static const struct hid_device_id hid_ignore_list[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_24) },
{ HID_USB_DEVICE(USB_VENDOR_ID_AIRCABLE, USB_DEVICE_ID_AIRCABLE1) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ALCOR, USB_DEVICE_ID_ALCOR_USBRS232) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL2) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL3) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL4) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL5) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK, USB_DEVICE_ID_ASUSTEK_LCM)},
{ HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK, USB_DEVICE_ID_ASUSTEK_LCM2)},
{ HID_USB_DEVICE(USB_VENDOR_ID_AVERMEDIA, USB_DEVICE_ID_AVER_FM_MR800) },
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
index 9d7a428..a4af9a9 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -137,8 +137,11 @@
#define USB_DEVICE_ID_APPLE_ALU_WIRELESS_2011_ISO 0x0256
#define USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY 0x030a
#define USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY 0x030b
-#define USB_DEVICE_ID_APPLE_ATV_IRCONTROL 0x8241
+#define USB_DEVICE_ID_APPLE_IRCONTROL 0x8240
+#define USB_DEVICE_ID_APPLE_IRCONTROL2 0x1440
+#define USB_DEVICE_ID_APPLE_IRCONTROL3 0x8241
#define USB_DEVICE_ID_APPLE_IRCONTROL4 0x8242
+#define USB_DEVICE_ID_APPLE_IRCONTROL5 0x8243
#define USB_VENDOR_ID_ASUS 0x0486
#define USB_DEVICE_ID_ASUS_T91MT 0x0185
diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
index 2a1647e..c1890c3 100644
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -321,6 +321,19 @@ config INPUT_KXTJ9_POLLED_MODE
help
Say Y here if you need accelerometer to work in polling mode.
+config INPUT_APPLEIR
+ tristate "Apple infrared receiver (built in)"
+ depends on USB_ARCH_HAS_HCD
+ select USB
+ help
+ Say Y here if you want to use a Apple infrared remote control. All
+ the Apple computers from 2005 onwards include such a port, except
+ the unibody Macbook (2009), and Mac Pros. This receiver is also
+ used in the Apple TV set-top box prior to the 2010 model.
+
+ To compile this driver as a module, choose M here: the module will
+ be called appleir.
+
config INPUT_POWERMATE
tristate "Griffin PowerMate and Contour Jog support"
depends on USB_ARCH_HAS_HCD
diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
index 1f874af..c00d562 100644
--- a/drivers/input/misc/Makefile
+++ b/drivers/input/misc/Makefile
@@ -14,6 +14,7 @@ obj-$(CONFIG_INPUT_ADXL34X) += adxl34x.o
obj-$(CONFIG_INPUT_ADXL34X_I2C) += adxl34x-i2c.o
obj-$(CONFIG_INPUT_ADXL34X_SPI) += adxl34x-spi.o
obj-$(CONFIG_INPUT_APANEL) += apanel.o
+obj-$(CONFIG_INPUT_APPLEIR) += appleir.o
obj-$(CONFIG_INPUT_ATI_REMOTE2) += ati_remote2.o
obj-$(CONFIG_INPUT_ATLAS_BTNS) += atlas_btns.o
obj-$(CONFIG_INPUT_BFIN_ROTARY) += bfin_rotary.o
diff --git a/drivers/input/misc/appleir.c b/drivers/input/misc/appleir.c
new file mode 100644
index 0000000..c6ca58c
--- /dev/null
+++ b/drivers/input/misc/appleir.c
@@ -0,0 +1,527 @@
+/*
+ * appleir: USB driver for the apple ir device
+ *
+ * Original driver written by James McKenzie
+ * Ported to recent 2.6 kernel versions by Greg Kroah-Hartman <gregkh@suse.de>
+ *
+ * Copyright (C) 2006 James McKenzie
+ * Copyright (C) 2008 Greg Kroah-Hartman <greg@kroah.com>
+ * Copyright (C) 2008 Novell Inc.
+ * Copyright (C) 2010, 2012 Bastien Nocera <hadess@hadess.net>
+ *
+ * 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.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/usb/input.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/usb.h>
+#include <linux/usb/input.h>
+#include <asm/unaligned.h>
+#include <asm/byteorder.h>
+
+#define DRIVER_VERSION "v1.2"
+#define DRIVER_AUTHOR "James McKenzie"
+#define DRIVER_DESC "Apple infrared receiver driver"
+#define DRIVER_LICENSE "GPL"
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE(DRIVER_LICENSE);
+
+#define USB_VENDOR_ID_APPLE 0x05ac
+#define USB_DEVICE_ID_APPLE_IRCONTROL 0x8240
+#define USB_DEVICE_ID_APPLE_IRCONTROL2 0x1440
+#define USB_DEVICE_ID_APPLE_IRCONTROL3 0x8241
+#define USB_DEVICE_ID_APPLE_IRCONTROL4 0x8242
+#define USB_DEVICE_ID_APPLE_IRCONTROL5 0x8243
+
+#define URB_SIZE 32
+
+#define MAX_KEYS 9
+#define MAX_KEYS_MASK (MAX_KEYS - 1)
+
+#define dbginfo(dev, format, arg...) do { if (debug) dev_info(dev , format , ## arg); } while (0)
+
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Enable extra debug messages and information");
+
+/* I have two devices both of which report the following */
+/* 25 87 ee 83 0a + */
+/* 25 87 ee 83 0c - */
+/* 25 87 ee 83 09 << */
+/* 25 87 ee 83 06 >> */
+/* 25 87 ee 83 05 >" */
+/* 25 87 ee 83 03 menu */
+/* 26 00 00 00 00 for key repeat*/
+
+/* Thomas Glanzmann reports the following responses */
+/* 25 87 ee ca 0b + */
+/* 25 87 ee ca 0d - */
+/* 25 87 ee ca 08 << */
+/* 25 87 ee ca 07 >> */
+/* 25 87 ee ca 04 >" */
+/* 25 87 ee ca 02 menu */
+/* 26 00 00 00 00 for key repeat*/
+/* He also observes the following event sometimes */
+/* sent after a key is release, which I interpret */
+/* as a flat battery message */
+/* 25 87 e0 ca 06 flat battery */
+
+/* Alexandre Karpenko reports the following responses for Device ID 0x8242 */
+/* 25 87 ee 47 0b + */
+/* 25 87 ee 47 0d - */
+/* 25 87 ee 47 08 << */
+/* 25 87 ee 47 07 >> */
+/* 25 87 ee 47 04 >" */
+/* 25 87 ee 47 02 menu */
+/* 26 87 ee 47 ** for key repeat (** is the code of the key being held) */
+
+/* Bastien Nocera's "new" remote */
+/* 25 87 ee 91 5f followed by
+ * 25 87 ee 91 05 gives you >"
+ *
+ * 25 87 ee 91 5c followed by
+ * 25 87 ee 91 05 gives you the middle button */
+
+static const unsigned short appleir_key_table[] = {
+ KEY_RESERVED,
+ KEY_MENU,
+ KEY_PLAYPAUSE,
+ KEY_FORWARD,
+ KEY_BACK,
+ KEY_VOLUMEUP,
+ KEY_VOLUMEDOWN,
+ KEY_ENTER,
+ KEY_RESERVED,
+};
+
+struct appleir {
+ struct input_dev *input_dev;
+ unsigned short keymap[ARRAY_SIZE(appleir_key_table)];
+ u8 *data;
+ dma_addr_t dma_buf;
+ struct usb_device *usbdev;
+ unsigned int flags;
+ struct urb *urb;
+ struct timer_list key_up_timer;
+ int current_key;
+ int prev_key_idx;
+ char phys[32];
+};
+
+static DEFINE_MUTEX(appleir_mutex);
+
+enum {
+ APPLEIR_OPENED = 0x1,
+ APPLEIR_SUSPENDED = 0x2,
+};
+
+static struct usb_device_id appleir_ids[] = {
+ { USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL) },
+ { USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL2) },
+ { USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL3) },
+ { USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL4) },
+ { USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL5) },
+ {}
+};
+MODULE_DEVICE_TABLE(usb, appleir_ids);
+
+static void dump_packet(struct appleir *appleir, char *msg, u8 *data, int len)
+{
+ int i;
+
+ printk(KERN_ERR "appleir: %s (%d bytes)", msg, len);
+
+ for (i = 0; i < len; ++i)
+ printk(" %02x", data[i]);
+ printk(" (should be command %d)\n", (data[4] >> 1) & MAX_KEYS_MASK);
+}
+
+static int get_key(int data)
+{
+ switch (data) {
+ case 0x02:
+ case 0x03:
+ /* menu */
+ return 1;
+ case 0x04:
+ case 0x05:
+ /* >" */
+ return 2;
+ case 0x06:
+ case 0x07:
+ /* >> */
+ return 3;
+ case 0x08:
+ case 0x09:
+ /* << */
+ return 4;
+ case 0x0a:
+ case 0x0b:
+ /* + */
+ return 5;
+ case 0x0c:
+ case 0x0d:
+ /* - */
+ return 6;
+ case 0x5c:
+ /* Middle button, on newer remotes,
+ * part of a 2 packet-command */
+ return -7;
+ default:
+ return -1;
+ }
+}
+
+static void key_up(struct appleir *appleir, int key)
+{
+ dbginfo(&appleir->input_dev->dev, "key %d up\n", key);
+ input_report_key(appleir->input_dev, key, 0);
+ input_sync(appleir->input_dev);
+}
+
+static void key_down(struct appleir *appleir, int key)
+{
+ dbginfo(&appleir->input_dev->dev, "key %d down\n", key);
+ input_report_key(appleir->input_dev, key, 1);
+ input_sync(appleir->input_dev);
+}
+
+static void battery_flat(struct appleir *appleir)
+{
+ dev_err(&appleir->input_dev->dev, "possible flat battery?\n");
+}
+
+static void key_up_tick(unsigned long data)
+{
+ struct appleir *appleir = (struct appleir *)data;
+
+ if (appleir->current_key) {
+ key_up(appleir, appleir->current_key);
+ appleir->current_key = 0;
+ }
+}
+
+static void new_data(struct appleir *appleir, u8 *data, int len)
+{
+ static const u8 keydown[] = { 0x25, 0x87, 0xee };
+ static const u8 keyrepeat[] = { 0x26, };
+ static const u8 flatbattery[] = { 0x25, 0x87, 0xe0 };
+
+ if (debug)
+ dump_packet(appleir, "received", data, len);
+
+ if (len != 5)
+ return;
+
+ if (!memcmp(data, keydown, sizeof(keydown))) {
+ int index;
+
+ /* If we already have a key down, take it up before marking
+ this one down */
+ if (appleir->current_key)
+ key_up(appleir, appleir->current_key);
+
+ /* Handle dual packet commands */
+ if (appleir->prev_key_idx > 0)
+ index = appleir->prev_key_idx;
+ else
+ index = get_key(data[4]);
+
+ if (index > 0) {
+ appleir->current_key = appleir->keymap[index];
+
+ key_down(appleir, appleir->current_key);
+ /* Remote doesn't do key up, either pull them up, in
+ * the test above, or here set a timer which pulls
+ * them up after 1/8 s */
+ mod_timer(&appleir->key_up_timer, jiffies + HZ / 8);
+ appleir->prev_key_idx = 0;
+ return;
+ } else if (index == -7) {
+ /* Remember key for next packet */
+ appleir->prev_key_idx = 0 - index;
+ return;
+ }
+ }
+
+ appleir->prev_key_idx = 0;
+
+ if (!memcmp(data, keyrepeat, sizeof(keyrepeat))) {
+ key_down(appleir, appleir->current_key);
+ /* Remote doesn't do key up, either pull them up, in the test
+ above, or here set a timer which pulls them up after 1/8 s */
+ mod_timer(&appleir->key_up_timer, jiffies + HZ / 8);
+ return;
+ }
+
+ if (!memcmp(data, flatbattery, sizeof(flatbattery))) {
+ battery_flat(appleir);
+ /* Fall through */
+ }
+
+ dump_packet(appleir, "unknown packet", data, len);
+}
+
+static void appleir_urb(struct urb *urb)
+{
+ struct appleir *appleir = urb->context;
+ int status = urb->status;
+ int retval;
+
+ switch (status) {
+ case 0:
+ new_data(appleir, urb->transfer_buffer, urb->actual_length);
+ break;
+ case -ECONNRESET:
+ case -ENOENT:
+ case -ESHUTDOWN:
+ /* This urb is terminated, clean up */
+ dbginfo(&appleir->input_dev->dev,
+ "%s - urb shutting down with status: %d",
+ __func__, urb->status);
+ return;
+ default:
+ dbginfo(&appleir->input_dev->dev,
+ "%s - nonzero urb status received: %d",
+ __func__, urb->status);
+ }
+
+ retval = usb_submit_urb(urb, GFP_ATOMIC);
+ if (retval)
+ dev_err(&appleir->input_dev->dev,
+ "%s - usb_submit_urb failed with result %d", __func__,
+ retval);
+}
+
+static int appleir_open(struct input_dev *dev)
+{
+ struct appleir *appleir = input_get_drvdata(dev);
+ struct usb_interface *intf = usb_ifnum_to_if(appleir->usbdev, 0);
+ int r;
+
+ r = usb_autopm_get_interface(intf);
+ if (r) {
+ dev_err(&intf->dev,
+ "%s(): usb_autopm_get_interface() = %d\n", __func__, r);
+ return r;
+ }
+
+ mutex_lock(&appleir_mutex);
+
+ if (usb_submit_urb(appleir->urb, GFP_ATOMIC)) {
+ r = -EIO;
+ goto fail;
+ }
+
+ appleir->flags |= APPLEIR_OPENED;
+
+ mutex_unlock(&appleir_mutex);
+
+ usb_autopm_put_interface(intf);
+
+ return 0;
+fail:
+ mutex_unlock(&appleir_mutex);
+ usb_autopm_put_interface(intf);
+ return r;
+}
+
+static void appleir_close(struct input_dev *dev)
+{
+ struct appleir *appleir = input_get_drvdata(dev);
+
+ mutex_lock(&appleir_mutex);
+
+ if (!(appleir->flags & APPLEIR_SUSPENDED)) {
+ usb_kill_urb(appleir->urb);
+ del_timer_sync(&appleir->key_up_timer);
+ }
+
+ appleir->flags &= ~APPLEIR_OPENED;
+
+ mutex_unlock(&appleir_mutex);
+}
+
+static int appleir_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ struct usb_device *dev = interface_to_usbdev(intf);
+ struct usb_endpoint_descriptor *endpoint;
+ struct appleir *appleir = NULL;
+ struct input_dev *input_dev;
+ int retval = -ENOMEM;
+ int i;
+
+ appleir = kzalloc(sizeof(struct appleir), GFP_KERNEL);
+ if (!appleir)
+ goto allocfail;
+
+ appleir->data = usb_alloc_coherent(dev, URB_SIZE, GFP_KERNEL,
+ &appleir->dma_buf);
+ if (!appleir->data)
+ goto usbfail;
+
+ appleir->urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!appleir->urb)
+ goto urbfail;
+
+ appleir->usbdev = dev;
+
+ input_dev = input_allocate_device();
+ if (!input_dev)
+ goto inputfail;
+
+ appleir->input_dev = input_dev;
+
+ usb_make_path(dev, appleir->phys, sizeof(appleir->phys));
+ strlcpy(appleir->phys, "/input0", sizeof(appleir->phys));
+
+ input_dev->name = "Apple Infrared Remote Controller";
+ input_dev->phys = appleir->phys;
+ usb_to_input_id(dev, &input_dev->id);
+ input_dev->dev.parent = &intf->dev;
+ input_dev->keycode = appleir->keymap;
+ input_dev->keycodesize = sizeof(unsigned short);
+ input_dev->keycodemax = ARRAY_SIZE(appleir->keymap);
+
+ input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP);
+
+ memcpy(appleir->keymap, appleir_key_table, sizeof(appleir->keymap));
+ for (i = 0; i < ARRAY_SIZE(appleir_key_table); i++)
+ set_bit(appleir->keymap[i], input_dev->keybit);
+ clear_bit(KEY_RESERVED, input_dev->keybit);
+
+ input_set_drvdata(input_dev, appleir);
+ input_dev->open = appleir_open;
+ input_dev->close = appleir_close;
+
+ endpoint = &intf->cur_altsetting->endpoint[0].desc;
+
+ usb_fill_int_urb(appleir->urb, dev,
+ usb_rcvintpipe(dev, endpoint->bEndpointAddress),
+ appleir->data, 8,
+ appleir_urb, appleir, endpoint->bInterval);
+
+ appleir->urb->transfer_dma = appleir->dma_buf;
+ appleir->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+ setup_timer(&appleir->key_up_timer,
+ key_up_tick, (unsigned long) appleir);
+
+ retval = input_register_device(appleir->input_dev);
+ if (retval)
+ goto inputfail;
+
+ usb_set_intfdata(intf, appleir);
+
+ return 0;
+
+inputfail:
+ input_free_device(appleir->input_dev);
+
+urbfail:
+ usb_free_urb(appleir->urb);
+
+usbfail:
+ usb_free_coherent(dev, URB_SIZE, appleir->data,
+ appleir->dma_buf);
+
+allocfail:
+ kfree(appleir);
+
+ return retval;
+}
+
+static void appleir_disconnect(struct usb_interface *intf)
+{
+ struct appleir *appleir = usb_get_intfdata(intf);
+
+ usb_set_intfdata(intf, NULL);
+ input_unregister_device(appleir->input_dev);
+ usb_free_urb(appleir->urb);
+ usb_free_coherent(interface_to_usbdev(intf), URB_SIZE,
+ appleir->data, appleir->dma_buf);
+ kfree(appleir);
+}
+
+static int appleir_suspend(struct usb_interface *interface,
+ pm_message_t message)
+{
+ struct appleir *appleir = usb_get_intfdata(interface);
+
+ mutex_lock(&appleir_mutex);
+ if (appleir->flags & APPLEIR_OPENED)
+ usb_kill_urb(appleir->urb);
+
+ appleir->flags |= APPLEIR_SUSPENDED;
+
+ mutex_unlock(&appleir_mutex);
+
+ return 0;
+}
+
+static int appleir_resume(struct usb_interface *interface)
+{
+ struct appleir *appleir;
+ int r = 0;
+
+ appleir = usb_get_intfdata(interface);
+
+ mutex_lock(&appleir_mutex);
+ if (appleir->flags & APPLEIR_OPENED) {
+ struct usb_endpoint_descriptor *endpoint;
+ unsigned int pipe;
+
+ endpoint = &interface->cur_altsetting->endpoint[0].desc;
+ pipe = usb_rcvintpipe(appleir->usbdev,
+ endpoint->bEndpointAddress);
+ usb_fill_int_urb(appleir->urb, appleir->usbdev,
+ pipe,
+ appleir->data, 8,
+ appleir_urb, appleir, endpoint->bInterval);
+ appleir->urb->transfer_dma = appleir->dma_buf;
+ appleir->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+ /* And reset the USB device */
+ if (usb_submit_urb(appleir->urb, GFP_ATOMIC))
+ r = -EIO;
+ }
+
+ appleir->flags &= ~APPLEIR_SUSPENDED;
+
+ mutex_unlock(&appleir_mutex);
+
+ return r;
+}
+
+static struct usb_driver appleir_driver = {
+ .name = "appleir",
+ .probe = appleir_probe,
+ .disconnect = appleir_disconnect,
+ .suspend = appleir_suspend,
+ .resume = appleir_resume,
+ .reset_resume = appleir_resume,
+ .id_table = appleir_ids,
+};
+
+static int __init appleir_init(void)
+{
+ return usb_register(&appleir_driver);
+}
+
+static void __exit appleir_exit(void)
+{
+ usb_deregister(&appleir_driver);
+}
+
+module_init(appleir_init);
+module_exit(appleir_exit);
--
1.8.0
^ permalink raw reply related [flat|nested] 57+ messages in thread
* Re: [PATCH] Input: add appleir USB driver
2012-11-15 18:13 Bastien Nocera
@ 2012-11-19 15:32 ` Benjamin Tissoires
2012-11-19 15:44 ` Bastien Nocera
0 siblings, 1 reply; 57+ messages in thread
From: Benjamin Tissoires @ 2012-11-19 15:32 UTC (permalink / raw)
To: Bastien Nocera, Fabien André
Cc: linux-input, Jarod Wilson, Jiri Kosina, Dmitry Torokhov
Hi Bastien,
(adding the input and HID maintainers to the recipient list).
On Thu, Nov 15, 2012 at 7:13 PM, Bastien Nocera <hadess@hadess.net> wrote:
>
> This driver was originally written by James McKenzie, updated by
> Greg Kroah-Hartman, further updated by myself, with suspend support
> added.
>
> More recent versions of the IR receiver are also supported through
> a patch by Alex Karpenko. The patch also adds support for the 2nd
> and 5th generation of the controller, and the menu key on newer
> brushed metal remotes.
>
> Tested on a MacbookAir1,1
>
> Signed-off-by: Bastien Nocera <hadess@hadess.net>
> ---
>
> Resend, as the original patch never made it. I cleaned up the patch a
> bit further, and test compiled it, but didn't have a chance to test it
> as I don't have a machine with that hardware available anymore.
Fabien, in CC, gracefully accepted to test and to try to adapt this
patch depending on the reviews. So we can ask for tests and changes!
>
> Documentation/input/appleir.txt | 46 ++++
> drivers/hid/hid-apple.c | 4 -
> drivers/hid/hid-core.c | 7 +-
> drivers/hid/hid-ids.h | 5 +-
> drivers/input/misc/Kconfig | 13 +
> drivers/input/misc/Makefile | 1 +
> drivers/input/misc/appleir.c | 527 ++++++++++++++++++++++++++++++++++++++++
If this device presents itself as a hid device, there are much chances
that we can use the hid .raw_event interface. This will help us for
the usb part and remove potential bugs and support and greatly
simplify this driver.
> 7 files changed, 596 insertions(+), 7 deletions(-)
> create mode 100644 Documentation/input/appleir.txt
> create mode 100644 drivers/input/misc/appleir.c
>
> diff --git a/Documentation/input/appleir.txt b/Documentation/input/appleir.txt
> new file mode 100644
> index 0000000..db637fb
> --- /dev/null
> +++ b/Documentation/input/appleir.txt
> @@ -0,0 +1,46 @@
> +Apple IR receiver Driver (appleir)
> +----------------------------------
> + Copyright (C) 2009 Bastien Nocera <hadess@hadess.net>
> +
> +The appleir driver is a kernel input driver to handle Apple's IR
> +receivers (and associated remotes) in the kernel.
> +
> +The driver is an input driver which only handles "official" remotes
> +as built and sold by Apple.
> +
> +Authors
> +-------
> +
> +James McKenzie (original driver)
> +Alex Karpenko (05ac:8242 support)
> +Greg Kroah-Hartman (cleanups and original submission)
> +Bastien Nocera (further cleanups, brushed metal "enter"
> +button support and suspend support)
> +
> +Supported hardware
> +------------------
> +
> +- All Apple laptops and desktops from 2005 onwards, except:
> + - the unibody Macbook (2009)
> + - Mac Pro (all versions)
> +- Apple TV (all revisions prior to September 2010)
> +
> +The remote will only support the 6 (old white) or 7 (brushed metal) buttons
> +of the remotes as sold by Apple. See the next section if you want to use
> +other remotes or want to use lirc with the device instead of the kernel driver.
> +
> +Using lirc (native) instead of the kernel driver
> +------------------------------------------------
> +
> +First, you will need to disable the kernel driver for the receiver.
> +
> +This can be achieved by passing quirks to the usbhid driver.
> +The quirk line would be:
> +usbhid.quirks=0x05ac:0x8242:0x40000010
> +
> +With 0x05ac being the vendor ID (Apple, you shouldn't need to change this)
> +With 0x8242 being the product ID (check the output of lsusb for your hardware)
> +And 0x10 being "HID_QUIRK_HIDDEV_FORCE" and 0x40000000 being "HID_QUIRK_NO_IGNORE"
> +
> +This should force the creation of a hiddev device for the receiver, and
> +make it usable under lirc.
> diff --git a/drivers/hid/hid-apple.c b/drivers/hid/hid-apple.c
> index fd7722a..30a4824 100644
> --- a/drivers/hid/hid-apple.c
> +++ b/drivers/hid/hid-apple.c
> @@ -390,10 +390,6 @@ static void apple_remove(struct hid_device *hdev)
> }
>
> static const struct hid_device_id apple_devices[] = {
> - { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ATV_IRCONTROL),
> - .driver_data = APPLE_HIDDEV | APPLE_IGNORE_HIDINPUT },
> - { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL4),
> - .driver_data = APPLE_HIDDEV | APPLE_IGNORE_HIDINPUT },
> { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MIGHTYMOUSE),
> .driver_data = APPLE_MIGHTYMOUSE | APPLE_INVERT_HWHEEL },
>
> diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
> index f4109fd..3fd4c10 100644
> --- a/drivers/hid/hid-core.c
> +++ b/drivers/hid/hid-core.c
> @@ -1471,8 +1471,6 @@ static const struct hid_device_id hid_have_special_driver[] = {
> { HID_USB_DEVICE(USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_X5_005D) },
> { HID_USB_DEVICE(USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_RP_649) },
> { HID_USB_DEVICE(USB_VENDOR_ID_ACRUX, 0x0802) },
> - { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ATV_IRCONTROL) },
> - { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL4) },
> { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MIGHTYMOUSE) },
> { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGICMOUSE) },
> { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGICTRACKPAD) },
> @@ -1925,6 +1923,11 @@ static const struct hid_device_id hid_ignore_list[] = {
> { HID_USB_DEVICE(USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_24) },
> { HID_USB_DEVICE(USB_VENDOR_ID_AIRCABLE, USB_DEVICE_ID_AIRCABLE1) },
> { HID_USB_DEVICE(USB_VENDOR_ID_ALCOR, USB_DEVICE_ID_ALCOR_USBRS232) },
> + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL) },
> + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL2) },
> + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL3) },
> + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL4) },
> + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL5) },
Using a hid kernel driver will move these additions to hid_have_special_driver.
> { HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK, USB_DEVICE_ID_ASUSTEK_LCM)},
> { HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK, USB_DEVICE_ID_ASUSTEK_LCM2)},
> { HID_USB_DEVICE(USB_VENDOR_ID_AVERMEDIA, USB_DEVICE_ID_AVER_FM_MR800) },
> diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
> index 9d7a428..a4af9a9 100644
> --- a/drivers/hid/hid-ids.h
> +++ b/drivers/hid/hid-ids.h
> @@ -137,8 +137,11 @@
> #define USB_DEVICE_ID_APPLE_ALU_WIRELESS_2011_ISO 0x0256
> #define USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY 0x030a
> #define USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY 0x030b
> -#define USB_DEVICE_ID_APPLE_ATV_IRCONTROL 0x8241
not sure we should change this define to an undocumented one.
> +#define USB_DEVICE_ID_APPLE_IRCONTROL 0x8240
> +#define USB_DEVICE_ID_APPLE_IRCONTROL2 0x1440
> +#define USB_DEVICE_ID_APPLE_IRCONTROL3 0x8241
> #define USB_DEVICE_ID_APPLE_IRCONTROL4 0x8242
> +#define USB_DEVICE_ID_APPLE_IRCONTROL5 0x8243
>
> #define USB_VENDOR_ID_ASUS 0x0486
> #define USB_DEVICE_ID_ASUS_T91MT 0x0185
> diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
> index 2a1647e..c1890c3 100644
> --- a/drivers/input/misc/Kconfig
> +++ b/drivers/input/misc/Kconfig
> @@ -321,6 +321,19 @@ config INPUT_KXTJ9_POLLED_MODE
> help
> Say Y here if you need accelerometer to work in polling mode.
>
> +config INPUT_APPLEIR
> + tristate "Apple infrared receiver (built in)"
> + depends on USB_ARCH_HAS_HCD
> + select USB
> + help
> + Say Y here if you want to use a Apple infrared remote control. All
> + the Apple computers from 2005 onwards include such a port, except
> + the unibody Macbook (2009), and Mac Pros. This receiver is also
> + used in the Apple TV set-top box prior to the 2010 model.
> +
> + To compile this driver as a module, choose M here: the module will
> + be called appleir.
> +
> config INPUT_POWERMATE
> tristate "Griffin PowerMate and Contour Jog support"
> depends on USB_ARCH_HAS_HCD
> diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
> index 1f874af..c00d562 100644
> --- a/drivers/input/misc/Makefile
> +++ b/drivers/input/misc/Makefile
> @@ -14,6 +14,7 @@ obj-$(CONFIG_INPUT_ADXL34X) += adxl34x.o
> obj-$(CONFIG_INPUT_ADXL34X_I2C) += adxl34x-i2c.o
> obj-$(CONFIG_INPUT_ADXL34X_SPI) += adxl34x-spi.o
> obj-$(CONFIG_INPUT_APANEL) += apanel.o
> +obj-$(CONFIG_INPUT_APPLEIR) += appleir.o
> obj-$(CONFIG_INPUT_ATI_REMOTE2) += ati_remote2.o
> obj-$(CONFIG_INPUT_ATLAS_BTNS) += atlas_btns.o
> obj-$(CONFIG_INPUT_BFIN_ROTARY) += bfin_rotary.o
> diff --git a/drivers/input/misc/appleir.c b/drivers/input/misc/appleir.c
> new file mode 100644
> index 0000000..c6ca58c
> --- /dev/null
> +++ b/drivers/input/misc/appleir.c
> @@ -0,0 +1,527 @@
> +/*
> + * appleir: USB driver for the apple ir device
> + *
> + * Original driver written by James McKenzie
> + * Ported to recent 2.6 kernel versions by Greg Kroah-Hartman <gregkh@suse.de>
> + *
> + * Copyright (C) 2006 James McKenzie
> + * Copyright (C) 2008 Greg Kroah-Hartman <greg@kroah.com>
> + * Copyright (C) 2008 Novell Inc.
> + * Copyright (C) 2010, 2012 Bastien Nocera <hadess@hadess.net>
> + *
> + * 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.
> + *
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/slab.h>
> +#include <linux/input.h>
> +#include <linux/usb/input.h>
> +#include <linux/module.h>
> +#include <linux/init.h>
> +#include <linux/usb.h>
> +#include <linux/usb/input.h>
> +#include <asm/unaligned.h>
> +#include <asm/byteorder.h>
> +
> +#define DRIVER_VERSION "v1.2"
> +#define DRIVER_AUTHOR "James McKenzie"
> +#define DRIVER_DESC "Apple infrared receiver driver"
> +#define DRIVER_LICENSE "GPL"
No need to create these macros.
> +
> +MODULE_AUTHOR(DRIVER_AUTHOR);
> +MODULE_DESCRIPTION(DRIVER_DESC);
> +MODULE_LICENSE(DRIVER_LICENSE);
> +
> +#define USB_VENDOR_ID_APPLE 0x05ac
> +#define USB_DEVICE_ID_APPLE_IRCONTROL 0x8240
> +#define USB_DEVICE_ID_APPLE_IRCONTROL2 0x1440
> +#define USB_DEVICE_ID_APPLE_IRCONTROL3 0x8241
> +#define USB_DEVICE_ID_APPLE_IRCONTROL4 0x8242
> +#define USB_DEVICE_ID_APPLE_IRCONTROL5 0x8243
these definitions are already in drivers/hid/hid-ids.h, so they can be
skipped in the hid version.
> +
> +#define URB_SIZE 32
> +
> +#define MAX_KEYS 9
> +#define MAX_KEYS_MASK (MAX_KEYS - 1)
can be skipped too.
> +
> +#define dbginfo(dev, format, arg...) do { if (debug) dev_info(dev , format , ## arg); } while (0)
> +
> +static int debug;
> +module_param(debug, int, 0644);
> +MODULE_PARM_DESC(debug, "Enable extra debug messages and information");
> +
> +/* I have two devices both of which report the following */
> +/* 25 87 ee 83 0a + */
> +/* 25 87 ee 83 0c - */
> +/* 25 87 ee 83 09 << */
> +/* 25 87 ee 83 06 >> */
> +/* 25 87 ee 83 05 >" */
> +/* 25 87 ee 83 03 menu */
> +/* 26 00 00 00 00 for key repeat*/
> +
> +/* Thomas Glanzmann reports the following responses */
> +/* 25 87 ee ca 0b + */
> +/* 25 87 ee ca 0d - */
> +/* 25 87 ee ca 08 << */
> +/* 25 87 ee ca 07 >> */
> +/* 25 87 ee ca 04 >" */
> +/* 25 87 ee ca 02 menu */
> +/* 26 00 00 00 00 for key repeat*/
> +/* He also observes the following event sometimes */
> +/* sent after a key is release, which I interpret */
> +/* as a flat battery message */
> +/* 25 87 e0 ca 06 flat battery */
> +
> +/* Alexandre Karpenko reports the following responses for Device ID 0x8242 */
> +/* 25 87 ee 47 0b + */
> +/* 25 87 ee 47 0d - */
> +/* 25 87 ee 47 08 << */
> +/* 25 87 ee 47 07 >> */
> +/* 25 87 ee 47 04 >" */
> +/* 25 87 ee 47 02 menu */
> +/* 26 87 ee 47 ** for key repeat (** is the code of the key being held) */
> +
> +/* Bastien Nocera's "new" remote */
> +/* 25 87 ee 91 5f followed by
> + * 25 87 ee 91 05 gives you >"
> + *
> + * 25 87 ee 91 5c followed by
> + * 25 87 ee 91 05 gives you the middle button */
> +
> +static const unsigned short appleir_key_table[] = {
> + KEY_RESERVED,
> + KEY_MENU,
> + KEY_PLAYPAUSE,
> + KEY_FORWARD,
> + KEY_BACK,
> + KEY_VOLUMEUP,
> + KEY_VOLUMEDOWN,
> + KEY_ENTER,
> + KEY_RESERVED,
> +};
> +
> +struct appleir {
> + struct input_dev *input_dev;
> + unsigned short keymap[ARRAY_SIZE(appleir_key_table)];
why this keymap is embedded in the struct? It's basically just a copy
of appleir_key_table and it's not modified anytime.
> + u8 *data;
> + dma_addr_t dma_buf;
> + struct usb_device *usbdev;
> + unsigned int flags;
> + struct urb *urb;
> + struct timer_list key_up_timer;
all these usb stuff can be skipped with hid.
> + int current_key;
> + int prev_key_idx;
> + char phys[32];
same for phys
> +};
> +
> +static DEFINE_MUTEX(appleir_mutex);
no need to maintain a mutex with hid
> +
> +enum {
> + APPLEIR_OPENED = 0x1,
> + APPLEIR_SUSPENDED = 0x2,
> +};
> +
> +static struct usb_device_id appleir_ids[] = {
> + { USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL) },
> + { USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL2) },
> + { USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL3) },
> + { USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL4) },
> + { USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL5) },
> + {}
> +};
> +MODULE_DEVICE_TABLE(usb, appleir_ids);
> +
> +static void dump_packet(struct appleir *appleir, char *msg, u8 *data, int len)
> +{
> + int i;
> +
> + printk(KERN_ERR "appleir: %s (%d bytes)", msg, len);
Should be KERN_INFO when used with if (debug)
> +
> + for (i = 0; i < len; ++i)
> + printk(" %02x", data[i]);
> + printk(" (should be command %d)\n", (data[4] >> 1) & MAX_KEYS_MASK);
Since 3.6, you can use syntax like dev_XXX(&dev, "%*ph\n", n, buf);
So this function can be skipped entirely as you can use %*ph with dbginfo.
> +}
> +
> +static int get_key(int data)
> +{
> + switch (data) {
> + case 0x02:
> + case 0x03:
> + /* menu */
> + return 1;
> + case 0x04:
> + case 0x05:
> + /* >" */
> + return 2;
> + case 0x06:
> + case 0x07:
> + /* >> */
> + return 3;
> + case 0x08:
> + case 0x09:
> + /* << */
> + return 4;
> + case 0x0a:
> + case 0x0b:
> + /* + */
> + return 5;
> + case 0x0c:
> + case 0x0d:
> + /* - */
> + return 6;
> + case 0x5c:
> + /* Middle button, on newer remotes,
> + * part of a 2 packet-command */
> + return -7;
> + default:
> + return -1;
> + }
> +}
> +
> +static void key_up(struct appleir *appleir, int key)
> +{
> + dbginfo(&appleir->input_dev->dev, "key %d up\n", key);
> + input_report_key(appleir->input_dev, key, 0);
> + input_sync(appleir->input_dev);
> +}
> +
> +static void key_down(struct appleir *appleir, int key)
> +{
> + dbginfo(&appleir->input_dev->dev, "key %d down\n", key);
> + input_report_key(appleir->input_dev, key, 1);
> + input_sync(appleir->input_dev);
> +}
> +
> +static void battery_flat(struct appleir *appleir)
> +{
> + dev_err(&appleir->input_dev->dev, "possible flat battery?\n");
> +}
> +
> +static void key_up_tick(unsigned long data)
> +{
> + struct appleir *appleir = (struct appleir *)data;
> +
> + if (appleir->current_key) {
> + key_up(appleir, appleir->current_key);
> + appleir->current_key = 0;
> + }
> +}
> +
> +static void new_data(struct appleir *appleir, u8 *data, int len)
> +{
> + static const u8 keydown[] = { 0x25, 0x87, 0xee };
> + static const u8 keyrepeat[] = { 0x26, };
> + static const u8 flatbattery[] = { 0x25, 0x87, 0xe0 };
> +
> + if (debug)
> + dump_packet(appleir, "received", data, len);
use dginfo directly
> +
> + if (len != 5)
> + return;
> +
> + if (!memcmp(data, keydown, sizeof(keydown))) {
> + int index;
> +
> + /* If we already have a key down, take it up before marking
> + this one down */
> + if (appleir->current_key)
> + key_up(appleir, appleir->current_key);
> +
> + /* Handle dual packet commands */
> + if (appleir->prev_key_idx > 0)
> + index = appleir->prev_key_idx;
> + else
> + index = get_key(data[4]);
> +
> + if (index > 0) {
> + appleir->current_key = appleir->keymap[index];
> +
> + key_down(appleir, appleir->current_key);
> + /* Remote doesn't do key up, either pull them up, in
> + * the test above, or here set a timer which pulls
> + * them up after 1/8 s */
> + mod_timer(&appleir->key_up_timer, jiffies + HZ / 8);
> + appleir->prev_key_idx = 0;
> + return;
> + } else if (index == -7) {
> + /* Remember key for next packet */
> + appleir->prev_key_idx = 0 - index;
> + return;
> + }
> + }
> +
> + appleir->prev_key_idx = 0;
> +
> + if (!memcmp(data, keyrepeat, sizeof(keyrepeat))) {
> + key_down(appleir, appleir->current_key);
> + /* Remote doesn't do key up, either pull them up, in the test
> + above, or here set a timer which pulls them up after 1/8 s */
> + mod_timer(&appleir->key_up_timer, jiffies + HZ / 8);
> + return;
> + }
> +
> + if (!memcmp(data, flatbattery, sizeof(flatbattery))) {
> + battery_flat(appleir);
> + /* Fall through */
> + }
> +
> + dump_packet(appleir, "unknown packet", data, len);
> +}
Then, the usb part of the driver can be skipped.
> +
> +static void appleir_urb(struct urb *urb)
> +{
> + struct appleir *appleir = urb->context;
> + int status = urb->status;
> + int retval;
> +
> + switch (status) {
> + case 0:
> + new_data(appleir, urb->transfer_buffer, urb->actual_length);
> + break;
> + case -ECONNRESET:
> + case -ENOENT:
> + case -ESHUTDOWN:
> + /* This urb is terminated, clean up */
> + dbginfo(&appleir->input_dev->dev,
> + "%s - urb shutting down with status: %d",
> + __func__, urb->status);
> + return;
> + default:
> + dbginfo(&appleir->input_dev->dev,
> + "%s - nonzero urb status received: %d",
> + __func__, urb->status);
> + }
> +
> + retval = usb_submit_urb(urb, GFP_ATOMIC);
> + if (retval)
> + dev_err(&appleir->input_dev->dev,
> + "%s - usb_submit_urb failed with result %d", __func__,
> + retval);
> +}
> +
> +static int appleir_open(struct input_dev *dev)
> +{
> + struct appleir *appleir = input_get_drvdata(dev);
> + struct usb_interface *intf = usb_ifnum_to_if(appleir->usbdev, 0);
> + int r;
> +
> + r = usb_autopm_get_interface(intf);
> + if (r) {
> + dev_err(&intf->dev,
> + "%s(): usb_autopm_get_interface() = %d\n", __func__, r);
> + return r;
> + }
> +
> + mutex_lock(&appleir_mutex);
> +
> + if (usb_submit_urb(appleir->urb, GFP_ATOMIC)) {
> + r = -EIO;
> + goto fail;
> + }
> +
> + appleir->flags |= APPLEIR_OPENED;
> +
> + mutex_unlock(&appleir_mutex);
> +
> + usb_autopm_put_interface(intf);
> +
> + return 0;
> +fail:
> + mutex_unlock(&appleir_mutex);
> + usb_autopm_put_interface(intf);
> + return r;
> +}
> +
> +static void appleir_close(struct input_dev *dev)
> +{
> + struct appleir *appleir = input_get_drvdata(dev);
> +
> + mutex_lock(&appleir_mutex);
> +
> + if (!(appleir->flags & APPLEIR_SUSPENDED)) {
> + usb_kill_urb(appleir->urb);
> + del_timer_sync(&appleir->key_up_timer);
> + }
> +
> + appleir->flags &= ~APPLEIR_OPENED;
> +
> + mutex_unlock(&appleir_mutex);
> +}
> +
> +static int appleir_probe(struct usb_interface *intf,
> + const struct usb_device_id *id)
> +{
> + struct usb_device *dev = interface_to_usbdev(intf);
> + struct usb_endpoint_descriptor *endpoint;
> + struct appleir *appleir = NULL;
> + struct input_dev *input_dev;
> + int retval = -ENOMEM;
> + int i;
> +
> + appleir = kzalloc(sizeof(struct appleir), GFP_KERNEL);
> + if (!appleir)
> + goto allocfail;
> +
> + appleir->data = usb_alloc_coherent(dev, URB_SIZE, GFP_KERNEL,
> + &appleir->dma_buf);
> + if (!appleir->data)
> + goto usbfail;
> +
> + appleir->urb = usb_alloc_urb(0, GFP_KERNEL);
> + if (!appleir->urb)
> + goto urbfail;
> +
> + appleir->usbdev = dev;
> +
> + input_dev = input_allocate_device();
> + if (!input_dev)
> + goto inputfail;
> +
> + appleir->input_dev = input_dev;
> +
> + usb_make_path(dev, appleir->phys, sizeof(appleir->phys));
> + strlcpy(appleir->phys, "/input0", sizeof(appleir->phys));
> +
> + input_dev->name = "Apple Infrared Remote Controller";
> + input_dev->phys = appleir->phys;
> + usb_to_input_id(dev, &input_dev->id);
> + input_dev->dev.parent = &intf->dev;
> + input_dev->keycode = appleir->keymap;
> + input_dev->keycodesize = sizeof(unsigned short);
> + input_dev->keycodemax = ARRAY_SIZE(appleir->keymap);
> +
> + input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP);
> +
> + memcpy(appleir->keymap, appleir_key_table, sizeof(appleir->keymap));
> + for (i = 0; i < ARRAY_SIZE(appleir_key_table); i++)
> + set_bit(appleir->keymap[i], input_dev->keybit);
> + clear_bit(KEY_RESERVED, input_dev->keybit);
> +
> + input_set_drvdata(input_dev, appleir);
> + input_dev->open = appleir_open;
> + input_dev->close = appleir_close;
> +
> + endpoint = &intf->cur_altsetting->endpoint[0].desc;
> +
> + usb_fill_int_urb(appleir->urb, dev,
> + usb_rcvintpipe(dev, endpoint->bEndpointAddress),
> + appleir->data, 8,
> + appleir_urb, appleir, endpoint->bInterval);
> +
> + appleir->urb->transfer_dma = appleir->dma_buf;
> + appleir->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
> +
> + setup_timer(&appleir->key_up_timer,
> + key_up_tick, (unsigned long) appleir);
> +
> + retval = input_register_device(appleir->input_dev);
> + if (retval)
> + goto inputfail;
> +
> + usb_set_intfdata(intf, appleir);
> +
> + return 0;
> +
> +inputfail:
it seems that inputfail should not call input_free_device.
> + input_free_device(appleir->input_dev);
> +
> +urbfail:
urbfail should not free the urb, it has failed to allocate
> + usb_free_urb(appleir->urb);
> +
> +usbfail:
ditto
> + usb_free_coherent(dev, URB_SIZE, appleir->data,
> + appleir->dma_buf);
> +
> +allocfail:
this is called when appleir == NULL, so no need to free it.
Apparently, all these references should be move from 1 line :)
> + kfree(appleir);
> +
> + return retval;
> +}
> +
> +static void appleir_disconnect(struct usb_interface *intf)
> +{
> + struct appleir *appleir = usb_get_intfdata(intf);
> +
> + usb_set_intfdata(intf, NULL);
> + input_unregister_device(appleir->input_dev);
> + usb_free_urb(appleir->urb);
> + usb_free_coherent(interface_to_usbdev(intf), URB_SIZE,
> + appleir->data, appleir->dma_buf);
> + kfree(appleir);
> +}
> +
> +static int appleir_suspend(struct usb_interface *interface,
> + pm_message_t message)
I don't think suspend and resume should be set with the hid driver.
> +{
> + struct appleir *appleir = usb_get_intfdata(interface);
> +
> + mutex_lock(&appleir_mutex);
> + if (appleir->flags & APPLEIR_OPENED)
> + usb_kill_urb(appleir->urb);
> +
> + appleir->flags |= APPLEIR_SUSPENDED;
> +
> + mutex_unlock(&appleir_mutex);
> +
> + return 0;
> +}
> +
> +static int appleir_resume(struct usb_interface *interface)
> +{
> + struct appleir *appleir;
> + int r = 0;
> +
> + appleir = usb_get_intfdata(interface);
> +
> + mutex_lock(&appleir_mutex);
> + if (appleir->flags & APPLEIR_OPENED) {
> + struct usb_endpoint_descriptor *endpoint;
> + unsigned int pipe;
> +
> + endpoint = &interface->cur_altsetting->endpoint[0].desc;
> + pipe = usb_rcvintpipe(appleir->usbdev,
> + endpoint->bEndpointAddress);
> + usb_fill_int_urb(appleir->urb, appleir->usbdev,
> + pipe,
> + appleir->data, 8,
> + appleir_urb, appleir, endpoint->bInterval);
> + appleir->urb->transfer_dma = appleir->dma_buf;
> + appleir->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
> +
> + /* And reset the USB device */
> + if (usb_submit_urb(appleir->urb, GFP_ATOMIC))
> + r = -EIO;
> + }
> +
> + appleir->flags &= ~APPLEIR_SUSPENDED;
> +
> + mutex_unlock(&appleir_mutex);
> +
> + return r;
> +}
> +
> +static struct usb_driver appleir_driver = {
> + .name = "appleir",
> + .probe = appleir_probe,
> + .disconnect = appleir_disconnect,
> + .suspend = appleir_suspend,
> + .resume = appleir_resume,
> + .reset_resume = appleir_resume,
> + .id_table = appleir_ids,
> +};
> +
> +static int __init appleir_init(void)
> +{
> + return usb_register(&appleir_driver);
> +}
> +
> +static void __exit appleir_exit(void)
> +{
> + usb_deregister(&appleir_driver);
> +}
> +
> +module_init(appleir_init);
> +module_exit(appleir_exit);
> --
> 1.8.0
>
>
Cheers,
Benjamin
^ permalink raw reply [flat|nested] 57+ messages in thread
* Re: [PATCH] Input: add appleir USB driver
2012-11-19 15:32 ` Benjamin Tissoires
@ 2012-11-19 15:44 ` Bastien Nocera
2012-11-19 16:01 ` Benjamin Tissoires
0 siblings, 1 reply; 57+ messages in thread
From: Bastien Nocera @ 2012-11-19 15:44 UTC (permalink / raw)
To: Benjamin Tissoires
Cc: Fabien André,
linux-input, Jarod Wilson, Jiri Kosina, Dmitry Torokhov
On Mon, 2012-11-19 at 16:32 +0100, Benjamin Tissoires wrote:
> Hi Bastien,
>
> (adding the input and HID maintainers to the recipient list).
>
> On Thu, Nov 15, 2012 at 7:13 PM, Bastien Nocera <hadess@hadess.net> wrote:
> >
> > This driver was originally written by James McKenzie, updated by
> > Greg Kroah-Hartman, further updated by myself, with suspend support
> > added.
> >
> > More recent versions of the IR receiver are also supported through
> > a patch by Alex Karpenko. The patch also adds support for the 2nd
> > and 5th generation of the controller, and the menu key on newer
> > brushed metal remotes.
> >
> > Tested on a MacbookAir1,1
> >
> > Signed-off-by: Bastien Nocera <hadess@hadess.net>
> > ---
> >
> > Resend, as the original patch never made it. I cleaned up the patch a
> > bit further, and test compiled it, but didn't have a chance to test it
> > as I don't have a machine with that hardware available anymore.
>
> Fabien, in CC, gracefully accepted to test and to try to adapt this
> patch depending on the reviews. So we can ask for tests and changes!
\o/
> > diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
> > index 9d7a428..a4af9a9 100644
> > --- a/drivers/hid/hid-ids.h
> > +++ b/drivers/hid/hid-ids.h
> > @@ -137,8 +137,11 @@
> > #define USB_DEVICE_ID_APPLE_ALU_WIRELESS_2011_ISO 0x0256
> > #define USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY 0x030a
> > #define USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY 0x030b
> > -#define USB_DEVICE_ID_APPLE_ATV_IRCONTROL 0x8241
>
> not sure we should change this define to an undocumented one.
I don't understand the comment here. The name is an artifact of where
the receiver was first seen and the Apple TV receiver is actually just
another model of this same receiver. So it makes sense to consolidate
below.
> > +#define USB_DEVICE_ID_APPLE_IRCONTROL 0x8240
> > +#define USB_DEVICE_ID_APPLE_IRCONTROL2 0x1440
> > +#define USB_DEVICE_ID_APPLE_IRCONTROL3 0x8241
> > #define USB_DEVICE_ID_APPLE_IRCONTROL4 0x8242
> > +#define USB_DEVICE_ID_APPLE_IRCONTROL5 0x8243
> > +struct appleir {
> > + struct input_dev *input_dev;
> > + unsigned short keymap[ARRAY_SIZE(appleir_key_table)];
>
> why this keymap is embedded in the struct? It's basically just a copy
> of appleir_key_table and it's not modified anytime.
It would be modified if you change the keymap.
Cheers
^ permalink raw reply [flat|nested] 57+ messages in thread
* Re: [PATCH] Input: add appleir USB driver
2012-11-19 15:44 ` Bastien Nocera
@ 2012-11-19 16:01 ` Benjamin Tissoires
0 siblings, 0 replies; 57+ messages in thread
From: Benjamin Tissoires @ 2012-11-19 16:01 UTC (permalink / raw)
To: Bastien Nocera
Cc: Fabien André,
linux-input, Jarod Wilson, Jiri Kosina, Dmitry Torokhov
On Mon, Nov 19, 2012 at 4:44 PM, Bastien Nocera <hadess@hadess.net> wrote:
> On Mon, 2012-11-19 at 16:32 +0100, Benjamin Tissoires wrote:
>> Hi Bastien,
>>
>> (adding the input and HID maintainers to the recipient list).
>>
>> On Thu, Nov 15, 2012 at 7:13 PM, Bastien Nocera <hadess@hadess.net> wrote:
>> >
>> > This driver was originally written by James McKenzie, updated by
>> > Greg Kroah-Hartman, further updated by myself, with suspend support
>> > added.
>> >
>> > More recent versions of the IR receiver are also supported through
>> > a patch by Alex Karpenko. The patch also adds support for the 2nd
>> > and 5th generation of the controller, and the menu key on newer
>> > brushed metal remotes.
>> >
>> > Tested on a MacbookAir1,1
>> >
>> > Signed-off-by: Bastien Nocera <hadess@hadess.net>
>> > ---
>> >
>> > Resend, as the original patch never made it. I cleaned up the patch a
>> > bit further, and test compiled it, but didn't have a chance to test it
>> > as I don't have a machine with that hardware available anymore.
>>
>> Fabien, in CC, gracefully accepted to test and to try to adapt this
>> patch depending on the reviews. So we can ask for tests and changes!
>
> \o/
>
>> > diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
>> > index 9d7a428..a4af9a9 100644
>> > --- a/drivers/hid/hid-ids.h
>> > +++ b/drivers/hid/hid-ids.h
>> > @@ -137,8 +137,11 @@
>> > #define USB_DEVICE_ID_APPLE_ALU_WIRELESS_2011_ISO 0x0256
>> > #define USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY 0x030a
>> > #define USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY 0x030b
>> > -#define USB_DEVICE_ID_APPLE_ATV_IRCONTROL 0x8241
>>
>> not sure we should change this define to an undocumented one.
>
> I don't understand the comment here. The name is an artifact of where
> the receiver was first seen and the Apple TV receiver is actually just
> another model of this same receiver. So it makes sense to consolidate
> below.
ok, if this id is not specific to Apple TV, that makes sense.
>
>> > +#define USB_DEVICE_ID_APPLE_IRCONTROL 0x8240
>> > +#define USB_DEVICE_ID_APPLE_IRCONTROL2 0x1440
>> > +#define USB_DEVICE_ID_APPLE_IRCONTROL3 0x8241
>> > #define USB_DEVICE_ID_APPLE_IRCONTROL4 0x8242
>> > +#define USB_DEVICE_ID_APPLE_IRCONTROL5 0x8243
>
>
>> > +struct appleir {
>> > + struct input_dev *input_dev;
>> > + unsigned short keymap[ARRAY_SIZE(appleir_key_table)];
>>
>> why this keymap is embedded in the struct? It's basically just a copy
>> of appleir_key_table and it's not modified anytime.
>
> It would be modified if you change the keymap.
ouch, sorry, I read it too fast. You're perfectly right.
That makes me wondering if this will still be possible with a hid
driver that implements raw_event...
Cheers,
Benjamin
>
> Cheers
>
^ permalink raw reply [flat|nested] 57+ messages in thread
* [PATCH] Input: add appleir USB driver
@ 2010-09-10 15:19 Bastien Nocera
0 siblings, 0 replies; 57+ messages in thread
From: Bastien Nocera @ 2010-09-10 15:19 UTC (permalink / raw)
To: Dmitry Torokhov; +Cc: linux-input, Jiri Kosina
This driver was originally written by James McKenzie, updated by
Greg Kroah-Hartman, further updated by myself, with suspend support
added.
More recent versions of the IR receiver are also supported through
a patch by Alex Karpenko. The patch also adds support for the 2nd
and 5th generation of the controller, and the menu key on newer
brushed metal remotes.
Tested on a MacbookAir1,1
Signed-off-by: Bastien Nocera <hadess@hadess.net>
---
Documentation/input/appleir.txt | 46 ++++
drivers/hid/hid-apple.c | 4 -
drivers/hid/hid-core.c | 7 +-
drivers/hid/hid-ids.h | 5 +-
drivers/input/misc/Kconfig | 13 +
drivers/input/misc/Makefile | 1 +
drivers/input/misc/appleir.c | 519 +++++++++++++++++++++++++++++++++++++++
7 files changed, 588 insertions(+), 7 deletions(-)
create mode 100644 Documentation/input/appleir.txt
create mode 100644 drivers/input/misc/appleir.c
diff --git a/Documentation/input/appleir.txt b/Documentation/input/appleir.txt
new file mode 100644
index 0000000..db637fb
--- /dev/null
+++ b/Documentation/input/appleir.txt
@@ -0,0 +1,46 @@
+Apple IR receiver Driver (appleir)
+----------------------------------
+ Copyright (C) 2009 Bastien Nocera <hadess@hadess.net>
+
+The appleir driver is a kernel input driver to handle Apple's IR
+receivers (and associated remotes) in the kernel.
+
+The driver is an input driver which only handles "official" remotes
+as built and sold by Apple.
+
+Authors
+-------
+
+James McKenzie (original driver)
+Alex Karpenko (05ac:8242 support)
+Greg Kroah-Hartman (cleanups and original submission)
+Bastien Nocera (further cleanups, brushed metal "enter"
+button support and suspend support)
+
+Supported hardware
+------------------
+
+- All Apple laptops and desktops from 2005 onwards, except:
+ - the unibody Macbook (2009)
+ - Mac Pro (all versions)
+- Apple TV (all revisions prior to September 2010)
+
+The remote will only support the 6 (old white) or 7 (brushed metal) buttons
+of the remotes as sold by Apple. See the next section if you want to use
+other remotes or want to use lirc with the device instead of the kernel driver.
+
+Using lirc (native) instead of the kernel driver
+------------------------------------------------
+
+First, you will need to disable the kernel driver for the receiver.
+
+This can be achieved by passing quirks to the usbhid driver.
+The quirk line would be:
+usbhid.quirks=0x05ac:0x8242:0x40000010
+
+With 0x05ac being the vendor ID (Apple, you shouldn't need to change this)
+With 0x8242 being the product ID (check the output of lsusb for your hardware)
+And 0x10 being "HID_QUIRK_HIDDEV_FORCE" and 0x40000000 being "HID_QUIRK_NO_IGNORE"
+
+This should force the creation of a hiddev device for the receiver, and
+make it usable under lirc.
diff --git a/drivers/hid/hid-apple.c b/drivers/hid/hid-apple.c
index bba05d0..0059d5a 100644
--- a/drivers/hid/hid-apple.c
+++ b/drivers/hid/hid-apple.c
@@ -361,10 +361,6 @@ static void apple_remove(struct hid_device *hdev)
}
static const struct hid_device_id apple_devices[] = {
- { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ATV_IRCONTROL),
- .driver_data = APPLE_HIDDEV | APPLE_IGNORE_HIDINPUT },
- { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL4),
- .driver_data = APPLE_HIDDEV | APPLE_IGNORE_HIDINPUT },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MIGHTYMOUSE),
.driver_data = APPLE_MIGHTYMOUSE | APPLE_INVERT_HWHEEL },
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index baa25ad..abc5bd7 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -1244,8 +1244,6 @@ static const struct hid_device_id hid_blacklist[] = {
#if defined(CONFIG_HID_ACRUX_FF) || defined(CONFIG_HID_ACRUX_FF_MODULE)
{ HID_USB_DEVICE(USB_VENDOR_ID_ACRUX, 0x0802) },
#endif
- { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ATV_IRCONTROL) },
- { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL4) },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MIGHTYMOUSE) },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGICMOUSE) },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_ANSI) },
@@ -1577,6 +1575,11 @@ static const struct hid_device_id hid_ignore_list[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_24) },
{ HID_USB_DEVICE(USB_VENDOR_ID_AIRCABLE, USB_DEVICE_ID_AIRCABLE1) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ALCOR, USB_DEVICE_ID_ALCOR_USBRS232) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL2) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL3) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL4) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL5) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ASUS, USB_DEVICE_ID_ASUS_T91MT)},
{ HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK, USB_DEVICE_ID_ASUSTEK_LCM)},
{ HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK, USB_DEVICE_ID_ASUSTEK_LCM2)},
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
index 11af537..360a5ca 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -100,8 +100,11 @@
#define USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_JIS 0x023b
#define USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY 0x030a
#define USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY 0x030b
-#define USB_DEVICE_ID_APPLE_ATV_IRCONTROL 0x8241
+#define USB_DEVICE_ID_APPLE_IRCONTROL 0x8240
+#define USB_DEVICE_ID_APPLE_IRCONTROL2 0x1440
+#define USB_DEVICE_ID_APPLE_IRCONTROL3 0x8241
#define USB_DEVICE_ID_APPLE_IRCONTROL4 0x8242
+#define USB_DEVICE_ID_APPLE_IRCONTROL5 0x8243
#define USB_VENDOR_ID_ASUS 0x0486
#define USB_DEVICE_ID_ASUS_T91MT 0x0185
diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
index 60de906..2f2f2e7 100644
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -209,6 +209,19 @@ config INPUT_KEYSPAN_REMOTE
To compile this driver as a module, choose M here: the module will
be called keyspan_remote.
+config INPUT_APPLEIR
+ tristate "Apple infrared receiver (built in)"
+ depends on USB_ARCH_HAS_HCD
+ select USB
+ help
+ Say Y here if you want to use a Apple infrared remote control. All
+ the Apple computers from 2005 onwards include such a port, except
+ the unibody Macbook (2009), and Mac Pros. This receiver is also
+ used in the Apple TV set-top box prior to the 2010 model.
+
+ To compile this driver as a module, choose M here: the module will
+ be called appleir.
+
config INPUT_POWERMATE
tristate "Griffin PowerMate and Contour Jog support"
depends on USB_ARCH_HAS_HCD
diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
index 1fe1f6c..d5ef2b9 100644
--- a/drivers/input/misc/Makefile
+++ b/drivers/input/misc/Makefile
@@ -13,6 +13,7 @@ obj-$(CONFIG_INPUT_ADXL34X) += adxl34x.o
obj-$(CONFIG_INPUT_ADXL34X_I2C) += adxl34x-i2c.o
obj-$(CONFIG_INPUT_ADXL34X_SPI) += adxl34x-spi.o
obj-$(CONFIG_INPUT_APANEL) += apanel.o
+obj-$(CONFIG_INPUT_APPLEIR) += appleir.o
obj-$(CONFIG_INPUT_ATI_REMOTE) += ati_remote.o
obj-$(CONFIG_INPUT_ATI_REMOTE2) += ati_remote2.o
obj-$(CONFIG_INPUT_ATLAS_BTNS) += atlas_btns.o
diff --git a/drivers/input/misc/appleir.c b/drivers/input/misc/appleir.c
new file mode 100644
index 0000000..3817a3c
--- /dev/null
+++ b/drivers/input/misc/appleir.c
@@ -0,0 +1,519 @@
+/*
+ * appleir: USB driver for the apple ir device
+ *
+ * Original driver written by James McKenzie
+ * Ported to recent 2.6 kernel versions by Greg Kroah-Hartman <gregkh@suse.de>
+ *
+ * Copyright (C) 2006 James McKenzie
+ * Copyright (C) 2008 Greg Kroah-Hartman <greg@kroah.com>
+ * Copyright (C) 2008 Novell Inc.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/usb/input.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/usb.h>
+#include <linux/usb/input.h>
+#include <asm/unaligned.h>
+#include <asm/byteorder.h>
+
+#define DRIVER_VERSION "v1.2"
+#define DRIVER_AUTHOR "James McKenzie"
+#define DRIVER_DESC "Apple infrared receiver driver"
+#define DRIVER_LICENSE "GPL"
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE(DRIVER_LICENSE);
+
+#define USB_VENDOR_ID_APPLE 0x05ac
+#define USB_DEVICE_ID_APPLE_IRCONTROL 0x8240
+#define USB_DEVICE_ID_APPLE_IRCONTROL2 0x1440
+#define USB_DEVICE_ID_APPLE_IRCONTROL3 0x8241
+#define USB_DEVICE_ID_APPLE_IRCONTROL4 0x8242
+#define USB_DEVICE_ID_APPLE_IRCONTROL5 0x8243
+
+#define URB_SIZE 32
+
+#define MAX_KEYS 9
+#define MAX_KEYS_MASK (MAX_KEYS - 1)
+
+#define dbginfo(dev, format, arg...) do { if (debug) dev_info(dev , format , ## arg); } while (0)
+
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Enable extra debug messages and information");
+
+/* I have two devices both of which report the following */
+/* 25 87 ee 83 0a + */
+/* 25 87 ee 83 0c - */
+/* 25 87 ee 83 09 << */
+/* 25 87 ee 83 06 >> */
+/* 25 87 ee 83 05 >" */
+/* 25 87 ee 83 03 menu */
+/* 26 00 00 00 00 for key repeat*/
+
+/* Thomas Glanzmann reports the following responses */
+/* 25 87 ee ca 0b + */
+/* 25 87 ee ca 0d - */
+/* 25 87 ee ca 08 << */
+/* 25 87 ee ca 07 >> */
+/* 25 87 ee ca 04 >" */
+/* 25 87 ee ca 02 menu */
+/* 26 00 00 00 00 for key repeat*/
+/* He also observes the following event sometimes */
+/* sent after a key is release, which I interpret */
+/* as a flat battery message */
+/* 25 87 e0 ca 06 flat battery */
+
+/* Alexandre Karpenko reports the following responses for Device ID 0x8242 */
+/* 25 87 ee 47 0b + */
+/* 25 87 ee 47 0d - */
+/* 25 87 ee 47 08 << */
+/* 25 87 ee 47 07 >> */
+/* 25 87 ee 47 04 >" */
+/* 25 87 ee 47 02 menu */
+/* 26 87 ee 47 ** for key repeat (** is the code of the key being held) */
+
+/* Bastien Nocera's "new" remote */
+/* 25 87 ee 91 5f followed by
+ * 25 87 ee 91 05 gives you >"
+ *
+ * 25 87 ee 91 5c followed by
+ * 25 87 ee 91 05 gives you the middle button */
+
+static const unsigned short appleir_key_table[] = {
+ KEY_RESERVED,
+ KEY_MENU,
+ KEY_PLAYPAUSE,
+ KEY_FORWARD,
+ KEY_BACK,
+ KEY_VOLUMEUP,
+ KEY_VOLUMEDOWN,
+ KEY_ENTER,
+ KEY_RESERVED,
+};
+
+struct appleir {
+ struct input_dev *input_dev;
+ unsigned short keymap[ARRAY_SIZE(appleir_key_table)];
+ u8 *data;
+ dma_addr_t dma_buf;
+ struct usb_device *usbdev;
+ unsigned int flags;
+ struct urb *urb;
+ struct timer_list key_up_timer;
+ int current_key;
+ int prev_key_idx;
+ char phys[32];
+};
+
+static DEFINE_MUTEX(appleir_mutex);
+
+enum {
+ APPLEIR_OPENED = 0x1,
+ APPLEIR_SUSPENDED = 0x2,
+};
+
+static struct usb_device_id appleir_ids[] = {
+ { USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL) },
+ { USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL2) },
+ { USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL3) },
+ { USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL4) },
+ { USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL5) },
+ {}
+};
+MODULE_DEVICE_TABLE(usb, appleir_ids);
+
+static void dump_packet(struct appleir *appleir, char *msg, u8 *data, int len)
+{
+ int i;
+
+ printk(KERN_ERR "appleir: %s (%d bytes)", msg, len);
+
+ for (i = 0; i < len; ++i)
+ printk(" %02x", data[i]);
+ printk(" (should be command %d)\n", (data[4] >> 1) & MAX_KEYS_MASK);
+}
+
+static int get_key(int data)
+{
+ switch (data) {
+ case 0x02:
+ case 0x03:
+ /* menu */
+ return 1;
+ case 0x04:
+ case 0x05:
+ /* >" */
+ return 2;
+ case 0x06:
+ case 0x07:
+ /* >> */
+ return 3;
+ case 0x08:
+ case 0x09:
+ /* << */
+ return 4;
+ case 0x0a:
+ case 0x0b:
+ /* + */
+ return 5;
+ case 0x0c:
+ case 0x0d:
+ /* - */
+ return 6;
+ case 0x5c:
+ /* Middle button, on newer remotes,
+ * part of a 2 packet-command */
+ return -7;
+ default:
+ return -1;
+ }
+}
+
+static void key_up(struct appleir *appleir, int key)
+{
+ dbginfo(&appleir->input_dev->dev, "key %d up\n", key);
+ input_report_key(appleir->input_dev, key, 0);
+ input_sync(appleir->input_dev);
+}
+
+static void key_down(struct appleir *appleir, int key)
+{
+ dbginfo(&appleir->input_dev->dev, "key %d down\n", key);
+ input_report_key(appleir->input_dev, key, 1);
+ input_sync(appleir->input_dev);
+}
+
+static void battery_flat(struct appleir *appleir)
+{
+ dev_err(&appleir->input_dev->dev, "possible flat battery?\n");
+}
+
+static void key_up_tick(unsigned long data)
+{
+ struct appleir *appleir = (struct appleir *)data;
+
+ if (appleir->current_key) {
+ key_up(appleir, appleir->current_key);
+ appleir->current_key = 0;
+ }
+}
+
+static void new_data(struct appleir *appleir, u8 *data, int len)
+{
+ static const u8 keydown[] = { 0x25, 0x87, 0xee };
+ static const u8 keyrepeat[] = { 0x26, };
+ static const u8 flatbattery[] = { 0x25, 0x87, 0xe0 };
+
+ if (debug)
+ dump_packet(appleir, "received", data, len);
+
+ if (len != 5)
+ return;
+
+ if (!memcmp(data, keydown, sizeof(keydown))) {
+ int index;
+
+ /* If we already have a key down, take it up before marking
+ this one down */
+ if (appleir->current_key)
+ key_up(appleir, appleir->current_key);
+
+ /* Handle dual packet commands */
+ if (appleir->prev_key_idx > 0)
+ index = appleir->prev_key_idx;
+ else
+ index = get_key(data[4]);
+
+ if (index > 0) {
+ appleir->current_key = appleir->keymap[index];
+
+ key_down(appleir, appleir->current_key);
+ /* Remote doesn't do key up, either pull them up, in the test
+ above, or here set a timer which pulls them up after 1/8 s */
+ mod_timer(&appleir->key_up_timer, jiffies + HZ / 8);
+ appleir->prev_key_idx = 0;
+ return;
+ } else if (index == -7) {
+ /* Remember key for next packet */
+ appleir->prev_key_idx = 0 - index;
+ return;
+ }
+ }
+
+ appleir->prev_key_idx = 0;
+
+ if (!memcmp(data, keyrepeat, sizeof(keyrepeat))) {
+ key_down(appleir, appleir->current_key);
+ /* Remote doesn't do key up, either pull them up, in the test
+ above, or here set a timer which pulls them up after 1/8 s */
+ mod_timer(&appleir->key_up_timer, jiffies + HZ / 8);
+ return;
+ }
+
+ if (!memcmp(data, flatbattery, sizeof(flatbattery))) {
+ battery_flat(appleir);
+ /* Fall through */
+ }
+
+ dump_packet(appleir, "unknown packet", data, len);
+}
+
+static void appleir_urb(struct urb *urb)
+{
+ struct appleir *appleir = urb->context;
+ int status = urb->status;
+ int retval;
+
+ switch (status) {
+ case 0:
+ new_data(appleir, urb->transfer_buffer, urb->actual_length);
+ break;
+ case -ECONNRESET:
+ case -ENOENT:
+ case -ESHUTDOWN:
+ /* This urb is terminated, clean up */
+ dbginfo(&appleir->input_dev->dev, "%s - urb shutting down with status: %d", __func__,
+ urb->status);
+ return;
+ default:
+ dbginfo(&appleir->input_dev->dev, "%s - nonzero urb status received: %d", __func__,
+ urb->status);
+ }
+
+ retval = usb_submit_urb(urb, GFP_ATOMIC);
+ if (retval)
+ err("%s - usb_submit_urb failed with result %d", __func__,
+ retval);
+}
+
+static int appleir_open(struct input_dev *dev)
+{
+ struct appleir *appleir = input_get_drvdata(dev);
+ struct usb_interface *intf = usb_ifnum_to_if(appleir->usbdev, 0);
+ int r;
+
+ r = usb_autopm_get_interface(intf);
+ if (r) {
+ dev_err(&intf->dev,
+ "%s(): usb_autopm_get_interface() = %d\n", __func__, r);
+ return r;
+ }
+
+ mutex_lock(&appleir_mutex);
+
+ if (usb_submit_urb(appleir->urb, GFP_ATOMIC)) {
+ r = -EIO;
+ goto fail;
+ }
+
+ appleir->flags |= APPLEIR_OPENED;
+
+ mutex_unlock(&appleir_mutex);
+
+ usb_autopm_put_interface(intf);
+
+ return 0;
+fail:
+ mutex_unlock(&appleir_mutex);
+ usb_autopm_put_interface(intf);
+ return r;
+}
+
+static void appleir_close(struct input_dev *dev)
+{
+ struct appleir *appleir = input_get_drvdata(dev);
+
+ mutex_lock(&appleir_mutex);
+
+ if (!(appleir->flags & APPLEIR_SUSPENDED)) {
+ usb_kill_urb(appleir->urb);
+ del_timer_sync(&appleir->key_up_timer);
+ }
+
+ appleir->flags &= ~APPLEIR_OPENED;
+
+ mutex_unlock(&appleir_mutex);
+}
+
+static int appleir_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ struct usb_device *dev = interface_to_usbdev(intf);
+ struct usb_endpoint_descriptor *endpoint;
+ struct appleir *appleir = NULL;
+ struct input_dev *input_dev;
+ int retval = -ENOMEM;
+ int i;
+
+ appleir = kzalloc(sizeof(struct appleir), GFP_KERNEL);
+ if (!appleir)
+ goto allocfail;
+
+ appleir->data = usb_alloc_coherent(dev, URB_SIZE, GFP_KERNEL,
+ &appleir->dma_buf);
+ if (!appleir->data)
+ goto usbfail;
+
+ appleir->urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!appleir->urb)
+ goto urbfail;
+
+ appleir->usbdev = dev;
+
+ input_dev = input_allocate_device();
+ if (!input_dev)
+ goto inputfail;
+
+ appleir->input_dev = input_dev;
+
+ usb_make_path(dev, appleir->phys, sizeof(appleir->phys));
+ strlcpy(appleir->phys, "/input0", sizeof(appleir->phys));
+
+ input_dev->name = "Apple Infrared Remote Controller";
+ input_dev->phys = appleir->phys;
+ usb_to_input_id(dev, &input_dev->id);
+ input_dev->dev.parent = &intf->dev;
+ input_dev->keycode = appleir->keymap;
+ input_dev->keycodesize = sizeof(unsigned short);
+ input_dev->keycodemax = ARRAY_SIZE(appleir->keymap);
+
+ input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP);
+
+ memcpy(appleir->keymap, appleir_key_table, sizeof(appleir->keymap));
+ for (i = 0; i < ARRAY_SIZE(appleir_key_table); i++)
+ set_bit(appleir->keymap[i], input_dev->keybit);
+ clear_bit(KEY_RESERVED, input_dev->keybit);
+
+ input_set_drvdata(input_dev, appleir);
+ input_dev->open = appleir_open;
+ input_dev->close = appleir_close;
+
+ endpoint = &intf->cur_altsetting->endpoint[0].desc;
+
+ usb_fill_int_urb(appleir->urb, dev,
+ usb_rcvintpipe(dev, endpoint->bEndpointAddress),
+ appleir->data, 8,
+ appleir_urb, appleir, endpoint->bInterval);
+
+ appleir->urb->transfer_dma = appleir->dma_buf;
+ appleir->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+ setup_timer(&appleir->key_up_timer,
+ key_up_tick, (unsigned long) appleir);
+
+ retval = input_register_device(appleir->input_dev);
+ if (retval)
+ goto inputfail;
+
+ usb_set_intfdata(intf, appleir);
+
+ return 0;
+
+inputfail:
+ input_free_device(appleir->input_dev);
+
+urbfail:
+ usb_free_urb(appleir->urb);
+
+usbfail:
+ usb_free_coherent(dev, URB_SIZE, appleir->data,
+ appleir->dma_buf);
+
+allocfail:
+ kfree(appleir);
+
+ return retval;
+}
+
+static void appleir_disconnect(struct usb_interface *intf)
+{
+ struct appleir *appleir = usb_get_intfdata(intf);
+
+ usb_set_intfdata(intf, NULL);
+ input_unregister_device(appleir->input_dev);
+ usb_free_urb(appleir->urb);
+ usb_free_coherent(interface_to_usbdev(intf), URB_SIZE,
+ appleir->data, appleir->dma_buf);
+ kfree(appleir);
+}
+
+static int appleir_suspend(struct usb_interface *interface,
+ pm_message_t message)
+{
+ struct appleir *appleir = usb_get_intfdata(interface);
+
+ mutex_lock(&appleir_mutex);
+ if (appleir->flags & APPLEIR_OPENED)
+ usb_kill_urb(appleir->urb);
+
+ appleir->flags |= APPLEIR_SUSPENDED;
+
+ mutex_unlock(&appleir_mutex);
+
+ return 0;
+}
+
+static int appleir_resume(struct usb_interface *interface)
+{
+ struct appleir *appleir;
+ int r = 0;
+
+ appleir = usb_get_intfdata(interface);
+
+ mutex_lock(&appleir_mutex);
+ if (appleir->flags & APPLEIR_OPENED) {
+ struct usb_endpoint_descriptor *endpoint;
+
+ endpoint = &interface->cur_altsetting->endpoint[0].desc;
+ usb_fill_int_urb(appleir->urb, appleir->usbdev,
+ usb_rcvintpipe(appleir->usbdev, endpoint->bEndpointAddress),
+ appleir->data, 8,
+ appleir_urb, appleir, endpoint->bInterval);
+ appleir->urb->transfer_dma = appleir->dma_buf;
+ appleir->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+ /* And reset the USB device */
+ if (usb_submit_urb(appleir->urb, GFP_ATOMIC))
+ r = -EIO;
+ }
+
+ appleir->flags &= ~APPLEIR_SUSPENDED;
+
+ mutex_unlock(&appleir_mutex);
+
+ return r;
+}
+
+static struct usb_driver appleir_driver = {
+ .name = "appleir",
+ .probe = appleir_probe,
+ .disconnect = appleir_disconnect,
+ .suspend = appleir_suspend,
+ .resume = appleir_resume,
+ .reset_resume = appleir_resume,
+ .id_table = appleir_ids,
+};
+
+static int __init appleir_init(void)
+{
+ return usb_register(&appleir_driver);
+}
+
+static void __exit appleir_exit(void)
+{
+ usb_deregister(&appleir_driver);
+}
+
+module_init(appleir_init);
+module_exit(appleir_exit);
--
1.7.2.2
^ permalink raw reply related [flat|nested] 57+ messages in thread
* [PATCH] Input: add appleir USB driver
@ 2010-04-17 21:45 Bastien Nocera
0 siblings, 0 replies; 57+ messages in thread
From: Bastien Nocera @ 2010-04-17 21:45 UTC (permalink / raw)
To: linux-input, Jiri Kosina, Dmitry Torokhov
This driver was originally written by James McKenzie, updated by
Greg Kroah-Hartman, further updated by myself, with suspend support
added.
More recent versions of the IR receiver are also supported through
a patch by Alex Karpenko.
Tested on a MacbookAir1,1
Signed-off-by: Bastien Nocera <hadess@hadess.net>
---
Documentation/input/appleir.txt | 45 +++++
drivers/hid/hid-apple.c | 4 -
drivers/hid/hid-core.c | 5 +-
drivers/hid/hid-ids.h | 1 +
drivers/input/misc/Kconfig | 13 ++
drivers/input/misc/Makefile | 1 +
drivers/input/misc/appleir.c | 399 +++++++++++++++++++++++++++++++++++++++
7 files changed, 462 insertions(+), 6 deletions(-)
create mode 100644 Documentation/input/appleir.txt
create mode 100644 drivers/input/misc/appleir.c
diff --git a/Documentation/input/appleir.txt b/Documentation/input/appleir.txt
new file mode 100644
index 0000000..0267a4b
--- /dev/null
+++ b/Documentation/input/appleir.txt
@@ -0,0 +1,45 @@
+Apple IR receiver Driver (appleir)
+----------------------------------
+ Copyright (C) 2009 Bastien Nocera <hadess@hadess.net>
+
+The appleir driver is a kernel input driver to handle Apple's IR
+receivers (and associated remotes) in the kernel.
+
+The driver is an input driver which only handles "official" remotes
+as built and sold by Apple.
+
+Authors
+-------
+
+James McKenzie (original driver)
+Alex Karpenko (05ac:8242 support)
+Greg Kroah-Hartman (cleanups and original submission)
+Bastien Nocera (further cleanups and suspend support)
+
+Supported hardware
+------------------
+
+- All Apple laptops and desktops from 2005 onwards, except:
+ - the unibody Macbook (2009)
+ - Mac Pro (all versions)
+- Apple TV (all revisions)
+
+The remote will only support the 6 buttons of the original remotes
+as sold by Apple. See the next section if you want to use other remotes
+or want to use lirc with the device instead of the kernel driver.
+
+Using lirc (native) instead of the kernel driver
+------------------------------------------------
+
+First, you will need to disable the kernel driver for the receiver.
+
+This can be achieved by passing quirks to the usbhid driver.
+The quirk line would be:
+usbhid.quirks=0x05ac:0x8242:0x40000010
+
+With 0x05ac being the vendor ID (Apple, you shouldn't need to change this)
+With 0x8242 being the product ID (check the output of lsusb for your hardware)
+And 0x10 being "HID_QUIRK_HIDDEV_FORCE" and 0x40000000 being "HID_QUIRK_NO_IGNORE"
+
+This should force the creation of a hiddev device for the receiver, and
+make it usable under lirc.
diff --git a/drivers/hid/hid-apple.c b/drivers/hid/hid-apple.c
index 78286b1..5f2a731 100644
--- a/drivers/hid/hid-apple.c
+++ b/drivers/hid/hid-apple.c
@@ -360,10 +360,6 @@ static void apple_remove(struct hid_device *hdev)
}
static const struct hid_device_id apple_devices[] = {
- { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ATV_IRCONTROL),
- .driver_data = APPLE_HIDDEV | APPLE_IGNORE_HIDINPUT },
- { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL4),
- .driver_data = APPLE_HIDDEV | APPLE_IGNORE_HIDINPUT },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MIGHTYMOUSE),
.driver_data = APPLE_MIGHTYMOUSE | APPLE_INVERT_HWHEEL },
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index 368fbb0..b57e5f7 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -1253,8 +1253,6 @@ static const struct hid_device_id hid_blacklist[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_3M, USB_DEVICE_ID_3M1968) },
{ HID_USB_DEVICE(USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_WCP32PU) },
{ HID_USB_DEVICE(USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_X5_005D) },
- { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ATV_IRCONTROL) },
- { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL4) },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MIGHTYMOUSE) },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGICMOUSE) },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_ANSI) },
@@ -1553,6 +1551,9 @@ static const struct hid_device_id hid_ignore_list[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_24) },
{ HID_USB_DEVICE(USB_VENDOR_ID_AIRCABLE, USB_DEVICE_ID_AIRCABLE1) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ALCOR, USB_DEVICE_ID_ALCOR_USBRS232) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ATV_IRCONTROL) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL4) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ASUS, USB_DEVICE_ID_ASUS_T91MT)},
{ HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK, USB_DEVICE_ID_ASUSTEK_LCM)},
{ HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK, USB_DEVICE_ID_ASUSTEK_LCM2)},
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
index 72c05f9..66a2ca8 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -97,6 +97,7 @@
#define USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_JIS 0x023b
#define USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY 0x030a
#define USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY 0x030b
+#define USB_DEVICE_ID_APPLE_IRCONTROL 0x8240
#define USB_DEVICE_ID_APPLE_ATV_IRCONTROL 0x8241
#define USB_DEVICE_ID_APPLE_IRCONTROL4 0x8242
diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
index 23140a3..46614b2 100644
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -159,6 +159,19 @@ config INPUT_KEYSPAN_REMOTE
To compile this driver as a module, choose M here: the module will
be called keyspan_remote.
+config INPUT_APPLEIR
+ tristate "Apple infrared receiver (built in)"
+ depends on USB_ARCH_HAS_HCD
+ select USB
+ help
+ Say Y here if you want to use a Apple infrared remote control. All
+ the Apple computers from 2005 onwards include such a port, except
+ the unibody Macbook (2009), and Mac Pros. This receiver is also
+ used in the Apple TV set-top box.
+
+ To compile this driver as a module, choose M here: the module will
+ be called appleir.
+
config INPUT_POWERMATE
tristate "Griffin PowerMate and Contour Jog support"
depends on USB_ARCH_HAS_HCD
diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
index 7e95a5d..3fa4404 100644
--- a/drivers/input/misc/Makefile
+++ b/drivers/input/misc/Makefile
@@ -6,6 +6,7 @@
obj-$(CONFIG_INPUT_88PM860X_ONKEY) += 88pm860x_onkey.o
obj-$(CONFIG_INPUT_APANEL) += apanel.o
+obj-$(CONFIG_INPUT_APPLEIR) += appleir.o
obj-$(CONFIG_INPUT_ATI_REMOTE) += ati_remote.o
obj-$(CONFIG_INPUT_ATI_REMOTE2) += ati_remote2.o
obj-$(CONFIG_INPUT_ATLAS_BTNS) += atlas_btns.o
diff --git a/drivers/input/misc/appleir.c b/drivers/input/misc/appleir.c
new file mode 100644
index 0000000..6b2ec04
--- /dev/null
+++ b/drivers/input/misc/appleir.c
@@ -0,0 +1,399 @@
+/*
+ * appleir: USB driver for the apple ir device
+ *
+ * Original driver written by James McKenzie
+ * Ported to recent 2.6 kernel versions by Greg Kroah-Hartman <gregkh@suse.de>
+ *
+ * Copyright (C) 2006 James McKenzie
+ * Copyright (C) 2008 Greg Kroah-Hartman <greg@kroah.com>
+ * Copyright (C) 2008 Novell Inc.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/usb/input.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/usb.h>
+#include <linux/usb/input.h>
+#include <asm/unaligned.h>
+#include <asm/byteorder.h>
+
+#define DRIVER_VERSION "v1.2"
+#define DRIVER_AUTHOR "James McKenzie"
+#define DRIVER_DESC "Apple infrared receiver driver"
+#define DRIVER_LICENSE "GPL"
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE(DRIVER_LICENSE);
+
+#define USB_VENDOR_ID_APPLE 0x05ac
+#define USB_DEVICE_ID_APPLE_IRCONTROL 0x8240
+#define USB_DEVICE_ID_APPLE_ATV_IRCONTROL 0x8241
+#define USB_DEVICE_ID_APPLE_IRCONTROL4 0x8242
+
+#define URB_SIZE 32
+
+#define MAX_KEYS 8
+#define MAX_KEYS_MASK (MAX_KEYS - 1)
+
+#define dbginfo(dev, format, arg...) do { if (debug) dev_info(dev , format , ## arg); } while (0)
+
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Enable extra debug messages and information");
+
+/* I have two devices both of which report the following */
+/* 25 87 ee 83 0a + */
+/* 25 87 ee 83 0c - */
+/* 25 87 ee 83 09 << */
+/* 25 87 ee 83 06 >> */
+/* 25 87 ee 83 05 >" */
+/* 25 87 ee 83 03 menu */
+/* 26 00 00 00 00 for key repeat*/
+
+/* Thomas Glanzmann reports the following responses */
+/* 25 87 ee ca 0b + */
+/* 25 87 ee ca 0d - */
+/* 25 87 ee ca 08 << */
+/* 25 87 ee ca 07 >> */
+/* 25 87 ee ca 04 >" */
+/* 25 87 ee ca 02 menu */
+/* 26 00 00 00 00 for key repeat*/
+/* He also observes the following event sometimes */
+/* sent after a key is release, which I interpret */
+/* as a flat battery message */
+/* 25 87 e0 ca 06 flat battery */
+
+/* Alexandre Karpenko reports the following responses for Device ID 0x8242 */
+/* 25 87 ee 47 0b + */
+/* 25 87 ee 47 0d - */
+/* 25 87 ee 47 08 << */
+/* 25 87 ee 47 07 >> */
+/* 25 87 ee 47 04 >" */
+/* 25 87 ee 47 02 menu */
+/* 26 87 ee 47 ** for key repeat (** is the code of the key being held) */
+
+static const unsigned short appleir_key_table[MAX_KEYS] = {
+ KEY_RESERVED,
+ KEY_MENU,
+ KEY_PLAYPAUSE,
+ KEY_FORWARD,
+ KEY_BACK,
+ KEY_VOLUMEUP,
+ KEY_VOLUMEDOWN,
+ KEY_RESERVED,
+};
+
+struct appleir {
+ struct input_dev *input_dev;
+ unsigned short keymap[ARRAY_SIZE(appleir_key_table)];
+ u8 *data;
+ dma_addr_t dma_buf;
+ struct usb_device *usbdev;
+ unsigned int flags;
+ struct urb *urb;
+ struct timer_list key_up_timer;
+ int current_key;
+ char phys[32];
+};
+
+static DEFINE_MUTEX(appleir_mutex);
+
+enum {
+ APPLEIR_OPENED = 0x1,
+ APPLEIR_SUSPENDED = 0x2,
+};
+
+static struct usb_device_id appleir_ids[] = {
+ { USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL) },
+ { USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ATV_IRCONTROL) },
+ { USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL4) },
+ {}
+};
+MODULE_DEVICE_TABLE(usb, appleir_ids);
+
+static void dump_packet(struct appleir *appleir, char *msg, u8 *data, int len)
+{
+ int i;
+
+ printk(KERN_ERR "appleir: %s (%d bytes)", msg, len);
+
+ for (i = 0; i < len; ++i)
+ printk(" %02x", data[i]);
+ printk("\n");
+}
+
+static void key_up(struct appleir *appleir, int key)
+{
+ dbginfo(&appleir->input_dev->dev, "key %d up\n", key);
+ input_report_key(appleir->input_dev, key, 0);
+ input_sync(appleir->input_dev);
+}
+
+static void key_down(struct appleir *appleir, int key)
+{
+ dbginfo(&appleir->input_dev->dev, "key %d down\n", key);
+ input_report_key(appleir->input_dev, key, 1);
+ input_sync(appleir->input_dev);
+}
+
+static void battery_flat(struct appleir *appleir)
+{
+ dev_err(&appleir->input_dev->dev, "possible flat battery?\n");
+}
+
+static void key_up_tick(unsigned long data)
+{
+ struct appleir *appleir = (struct appleir *)data;
+
+ if (appleir->current_key) {
+ key_up(appleir, appleir->current_key);
+ appleir->current_key = 0;
+ }
+}
+
+static void new_data(struct appleir *appleir, u8 *data, int len)
+{
+ static const u8 keydown[] = { 0x25, 0x87, 0xee };
+ static const u8 keyrepeat[] = { 0x26, };
+ static const u8 flatbattery[] = { 0x25, 0x87, 0xe0 };
+
+ if (debug)
+ dump_packet(appleir, "received", data, len);
+
+ if (len != 5)
+ return;
+
+ if (!memcmp(data, keydown, sizeof(keydown))) {
+ /* If we already have a key down, take it up before marking
+ this one down */
+ if (appleir->current_key)
+ key_up(appleir, appleir->current_key);
+ appleir->current_key = appleir->keymap[(data[4] >> 1) & MAX_KEYS_MASK];
+
+ key_down(appleir, appleir->current_key);
+ /* Remote doesn't do key up, either pull them up, in the test
+ above, or here set a timer which pulls them up after 1/8 s */
+ mod_timer(&appleir->key_up_timer, jiffies + HZ / 8);
+
+ return;
+ }
+
+ if (!memcmp(data, keyrepeat, sizeof(keyrepeat))) {
+ key_down(appleir, appleir->current_key);
+ /* Remote doesn't do key up, either pull them up, in the test
+ above, or here set a timer which pulls them up after 1/8 s */
+ mod_timer(&appleir->key_up_timer, jiffies + HZ / 8);
+ return;
+ }
+
+ if (!memcmp(data, flatbattery, sizeof(flatbattery))) {
+ battery_flat(appleir);
+ /* Fall through */
+ }
+
+ dump_packet(appleir, "unknown packet", data, len);
+}
+
+static void appleir_urb(struct urb *urb)
+{
+ struct appleir *appleir = urb->context;
+ int status = urb->status;
+ int retval;
+
+ switch (status) {
+ case 0:
+ new_data(appleir, urb->transfer_buffer, urb->actual_length);
+ break;
+ case -ECONNRESET:
+ case -ENOENT:
+ case -ESHUTDOWN:
+ /* This urb is terminated, clean up */
+ dbginfo(&appleir->input_dev->dev, "%s - urb shutting down with status: %d", __func__,
+ urb->status);
+ return;
+ default:
+ dbginfo(&appleir->input_dev->dev, "%s - nonzero urb status received: %d", __func__,
+ urb->status);
+ }
+
+ retval = usb_submit_urb(urb, GFP_ATOMIC);
+ if (retval)
+ err("%s - usb_submit_urb failed with result %d", __func__,
+ retval);
+}
+
+static int appleir_open(struct input_dev *dev)
+{
+ struct appleir *appleir = input_get_drvdata(dev);
+ struct usb_interface *intf = usb_ifnum_to_if(appleir->usbdev, 0);
+ int r;
+
+ r = usb_autopm_get_interface(intf);
+ if (r) {
+ dev_err(&intf->dev,
+ "%s(): usb_autopm_get_interface() = %d\n", __func__, r);
+ return r;
+ }
+
+ mutex_lock(&appleir_mutex);
+
+ if (usb_submit_urb(appleir->urb, GFP_KERNEL)) {
+ r = -EIO;
+ goto fail;
+ }
+
+ appleir->flags |= APPLEIR_OPENED;
+
+ mutex_unlock(&appleir_mutex);
+
+ usb_autopm_put_interface(intf);
+
+ return 0;
+fail:
+ mutex_unlock(&appleir_mutex);
+ usb_autopm_put_interface(intf);
+ return r;
+}
+
+static void appleir_close(struct input_dev *dev)
+{
+ struct appleir *appleir = input_get_drvdata(dev);
+
+ mutex_lock(&appleir_mutex);
+
+ if (!(appleir->flags & APPLEIR_SUSPENDED)) {
+ usb_kill_urb(appleir->urb);
+ del_timer_sync(&appleir->key_up_timer);
+ }
+
+ appleir->flags &= ~APPLEIR_OPENED;
+
+ mutex_unlock(&appleir_mutex);
+}
+
+static int appleir_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ struct usb_device *dev = interface_to_usbdev(intf);
+ struct usb_endpoint_descriptor *endpoint;
+ struct appleir *appleir = NULL;
+ struct input_dev *input_dev;
+ int retval = -ENOMEM;
+ int i;
+
+ appleir = kzalloc(sizeof(struct appleir), GFP_KERNEL);
+ if (!appleir)
+ goto allocfail;
+
+ appleir->data = usb_buffer_alloc(dev, URB_SIZE, GFP_KERNEL,
+ &appleir->dma_buf);
+ if (!appleir->data)
+ goto usbfail;
+
+ appleir->urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!appleir->urb)
+ goto urbfail;
+
+ appleir->usbdev = dev;
+
+ input_dev = input_allocate_device();
+ if (!input_dev)
+ goto inputfail;
+
+ appleir->input_dev = input_dev;
+
+ usb_make_path(dev, appleir->phys, sizeof(appleir->phys));
+ strlcpy(appleir->phys, "/input0", sizeof(appleir->phys));
+
+ input_dev->name = "Apple Infrared Remote Controller";
+ input_dev->phys = appleir->phys;
+ usb_to_input_id(dev, &input_dev->id);
+ input_dev->dev.parent = &intf->dev;
+ input_set_drvdata(input_dev, appleir);
+
+ input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP);
+
+ memcpy(appleir->keymap, appleir_key_table, sizeof(appleir->keymap));
+ for (i = 0; i < MAX_KEYS; i++)
+ set_bit(appleir->keymap[i], input_dev->keybit);
+
+ input_dev->open = appleir_open;
+ input_dev->close = appleir_close;
+
+ endpoint = &intf->cur_altsetting->endpoint[0].desc;
+
+ usb_fill_int_urb(appleir->urb, dev,
+ usb_rcvintpipe(dev, endpoint->bEndpointAddress),
+ appleir->data, 8,
+ appleir_urb, appleir, endpoint->bInterval);
+
+ appleir->urb->transfer_dma = appleir->dma_buf;
+ appleir->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+ setup_timer(&appleir->key_up_timer,
+ key_up_tick, (unsigned long) appleir);
+
+ retval = input_register_device(appleir->input_dev);
+ if (retval)
+ goto inputfail;
+
+ usb_set_intfdata(intf, appleir);
+
+ return 0;
+
+inputfail:
+ input_free_device(appleir->input_dev);
+
+urbfail:
+ usb_free_urb(appleir->urb);
+
+usbfail:
+ usb_buffer_free(dev, URB_SIZE, appleir->data,
+ appleir->dma_buf);
+
+allocfail:
+ kfree(appleir);
+
+ return retval;
+}
+
+static void appleir_disconnect(struct usb_interface *intf)
+{
+ struct appleir *appleir = usb_get_intfdata(intf);
+
+ usb_set_intfdata(intf, NULL);
+ input_unregister_device(appleir->input_dev);
+ usb_free_urb(appleir->urb);
+ usb_buffer_free(interface_to_usbdev(intf), URB_SIZE,
+ appleir->data, appleir->dma_buf);
+ kfree(appleir);
+}
+
+static struct usb_driver appleir_driver = {
+ .name = "appleir",
+ .probe = appleir_probe,
+ .disconnect = appleir_disconnect,
+ .id_table = appleir_ids,
+};
+
+static int __init appleir_init(void)
+{
+ return usb_register(&appleir_driver);
+}
+
+static void __exit appleir_exit(void)
+{
+ usb_deregister(&appleir_driver);
+}
+
+module_init(appleir_init);
+module_exit(appleir_exit);
--
1.6.6.1
^ permalink raw reply related [flat|nested] 57+ messages in thread
* [PATCH] Input: add appleir USB driver
@ 2010-04-16 16:19 Bastien Nocera
2010-04-17 8:12 ` Dmitry Torokhov
0 siblings, 1 reply; 57+ messages in thread
From: Bastien Nocera @ 2010-04-16 16:19 UTC (permalink / raw)
To: linux-input; +Cc: Dmitry Torokhov, Jiri Kosina
This driver was originally written by James McKenzie, updated by
Greg Kroah-Hartman, further updated by myself, with suspend support
added.
More recent versions of the IR receiver are also supported through
a patch by Alex Karpenko.
Tested on a MacbookAir1,1
Signed-off-by: Bastien Nocera <hadess@hadess.net>
---
Documentation/input/appleir.txt | 45 ++++
drivers/hid/hid-apple.c | 4 -
drivers/hid/hid-core.c | 5 +-
drivers/hid/hid-ids.h | 1 +
drivers/input/misc/Kconfig | 13 +
drivers/input/misc/Makefile | 1 +
drivers/input/misc/appleir.c | 477 +++++++++++++++++++++++++++++++++++++++
7 files changed, 540 insertions(+), 6 deletions(-)
create mode 100644 Documentation/input/appleir.txt
create mode 100644 drivers/input/misc/appleir.c
diff --git a/Documentation/input/appleir.txt b/Documentation/input/appleir.txt
new file mode 100644
index 0000000..0267a4b
--- /dev/null
+++ b/Documentation/input/appleir.txt
@@ -0,0 +1,45 @@
+Apple IR receiver Driver (appleir)
+----------------------------------
+ Copyright (C) 2009 Bastien Nocera <hadess@hadess.net>
+
+The appleir driver is a kernel input driver to handle Apple's IR
+receivers (and associated remotes) in the kernel.
+
+The driver is an input driver which only handles "official" remotes
+as built and sold by Apple.
+
+Authors
+-------
+
+James McKenzie (original driver)
+Alex Karpenko (05ac:8242 support)
+Greg Kroah-Hartman (cleanups and original submission)
+Bastien Nocera (further cleanups and suspend support)
+
+Supported hardware
+------------------
+
+- All Apple laptops and desktops from 2005 onwards, except:
+ - the unibody Macbook (2009)
+ - Mac Pro (all versions)
+- Apple TV (all revisions)
+
+The remote will only support the 6 buttons of the original remotes
+as sold by Apple. See the next section if you want to use other remotes
+or want to use lirc with the device instead of the kernel driver.
+
+Using lirc (native) instead of the kernel driver
+------------------------------------------------
+
+First, you will need to disable the kernel driver for the receiver.
+
+This can be achieved by passing quirks to the usbhid driver.
+The quirk line would be:
+usbhid.quirks=0x05ac:0x8242:0x40000010
+
+With 0x05ac being the vendor ID (Apple, you shouldn't need to change this)
+With 0x8242 being the product ID (check the output of lsusb for your hardware)
+And 0x10 being "HID_QUIRK_HIDDEV_FORCE" and 0x40000000 being "HID_QUIRK_NO_IGNORE"
+
+This should force the creation of a hiddev device for the receiver, and
+make it usable under lirc.
diff --git a/drivers/hid/hid-apple.c b/drivers/hid/hid-apple.c
index 78286b1..5f2a731 100644
--- a/drivers/hid/hid-apple.c
+++ b/drivers/hid/hid-apple.c
@@ -360,10 +360,6 @@ static void apple_remove(struct hid_device *hdev)
}
static const struct hid_device_id apple_devices[] = {
- { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ATV_IRCONTROL),
- .driver_data = APPLE_HIDDEV | APPLE_IGNORE_HIDINPUT },
- { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL4),
- .driver_data = APPLE_HIDDEV | APPLE_IGNORE_HIDINPUT },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MIGHTYMOUSE),
.driver_data = APPLE_MIGHTYMOUSE | APPLE_INVERT_HWHEEL },
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index 368fbb0..b57e5f7 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -1253,8 +1253,6 @@ static const struct hid_device_id hid_blacklist[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_3M, USB_DEVICE_ID_3M1968) },
{ HID_USB_DEVICE(USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_WCP32PU) },
{ HID_USB_DEVICE(USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_X5_005D) },
- { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ATV_IRCONTROL) },
- { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL4) },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MIGHTYMOUSE) },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGICMOUSE) },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_ANSI) },
@@ -1553,6 +1551,9 @@ static const struct hid_device_id hid_ignore_list[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_24) },
{ HID_USB_DEVICE(USB_VENDOR_ID_AIRCABLE, USB_DEVICE_ID_AIRCABLE1) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ALCOR, USB_DEVICE_ID_ALCOR_USBRS232) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ATV_IRCONTROL) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL4) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ASUS, USB_DEVICE_ID_ASUS_T91MT)},
{ HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK, USB_DEVICE_ID_ASUSTEK_LCM)},
{ HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK, USB_DEVICE_ID_ASUSTEK_LCM2)},
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
index 72c05f9..66a2ca8 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -97,6 +97,7 @@
#define USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_JIS 0x023b
#define USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY 0x030a
#define USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY 0x030b
+#define USB_DEVICE_ID_APPLE_IRCONTROL 0x8240
#define USB_DEVICE_ID_APPLE_ATV_IRCONTROL 0x8241
#define USB_DEVICE_ID_APPLE_IRCONTROL4 0x8242
diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
index 23140a3..46614b2 100644
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -159,6 +159,19 @@ config INPUT_KEYSPAN_REMOTE
To compile this driver as a module, choose M here: the module will
be called keyspan_remote.
+config INPUT_APPLEIR
+ tristate "Apple infrared receiver (built in)"
+ depends on USB_ARCH_HAS_HCD
+ select USB
+ help
+ Say Y here if you want to use a Apple infrared remote control. All
+ the Apple computers from 2005 onwards include such a port, except
+ the unibody Macbook (2009), and Mac Pros. This receiver is also
+ used in the Apple TV set-top box.
+
+ To compile this driver as a module, choose M here: the module will
+ be called appleir.
+
config INPUT_POWERMATE
tristate "Griffin PowerMate and Contour Jog support"
depends on USB_ARCH_HAS_HCD
diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
index 7e95a5d..3fa4404 100644
--- a/drivers/input/misc/Makefile
+++ b/drivers/input/misc/Makefile
@@ -6,6 +6,7 @@
obj-$(CONFIG_INPUT_88PM860X_ONKEY) += 88pm860x_onkey.o
obj-$(CONFIG_INPUT_APANEL) += apanel.o
+obj-$(CONFIG_INPUT_APPLEIR) += appleir.o
obj-$(CONFIG_INPUT_ATI_REMOTE) += ati_remote.o
obj-$(CONFIG_INPUT_ATI_REMOTE2) += ati_remote2.o
obj-$(CONFIG_INPUT_ATLAS_BTNS) += atlas_btns.o
diff --git a/drivers/input/misc/appleir.c b/drivers/input/misc/appleir.c
new file mode 100644
index 0000000..138f4c8
--- /dev/null
+++ b/drivers/input/misc/appleir.c
@@ -0,0 +1,477 @@
+/*
+ * appleir: USB driver for the apple ir device
+ *
+ * Original driver written by James McKenzie
+ * Ported to recent 2.6 kernel versions by Greg Kroah-Hartman <gregkh@suse.de>
+ *
+ * Copyright (C) 2006 James McKenzie
+ * Copyright (C) 2008 Greg Kroah-Hartman <greg@kroah.com>
+ * Copyright (C) 2008 Novell Inc.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/usb/input.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/usb.h>
+#include <linux/usb/input.h>
+#include <asm/unaligned.h>
+#include <asm/byteorder.h>
+
+#define DRIVER_VERSION "v1.2"
+#define DRIVER_AUTHOR "James McKenzie"
+#define DRIVER_DESC "Apple infrared receiver driver"
+#define DRIVER_LICENSE "GPL"
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE(DRIVER_LICENSE);
+
+#define USB_VENDOR_ID_APPLE 0x05ac
+#define USB_DEVICE_ID_APPLE_IRCONTROL 0x8240
+#define USB_DEVICE_ID_APPLE_ATV_IRCONTROL 0x8241
+#define USB_DEVICE_ID_APPLE_IRCONTROL4 0x8242
+
+#define URB_SIZE 32
+
+#define MAX_KEYS 8
+#define MAX_KEYS_MASK (MAX_KEYS - 1)
+
+#define dbginfo(dev, format, arg...) do { if (debug) dev_info(dev , format , ## arg); } while (0)
+
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Enable extra debug messages and information");
+
+struct appleir {
+ struct input_dev *input_dev;
+ u8 *data;
+ dma_addr_t dma_buf;
+ struct usb_device *usbdev;
+ unsigned int flags;
+ struct urb *urb;
+ int timer_initted;
+ struct timer_list key_up_timer;
+ int current_key;
+ char phys[32];
+};
+
+static DEFINE_MUTEX(appleir_mutex);
+
+enum {
+ APPLEIR_OPENED = 0x1,
+ APPLEIR_SUSPENDED = 0x2,
+};
+
+static struct usb_device_id appleir_ids[] = {
+ { USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL) },
+ { USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ATV_IRCONTROL) },
+ { USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL4) },
+ {}
+};
+MODULE_DEVICE_TABLE(usb, appleir_ids);
+
+/* I have two devices both of which report the following */
+/* 25 87 ee 83 0a + */
+/* 25 87 ee 83 0c - */
+/* 25 87 ee 83 09 << */
+/* 25 87 ee 83 06 >> */
+/* 25 87 ee 83 05 >" */
+/* 25 87 ee 83 03 menu */
+/* 26 00 00 00 00 for key repeat*/
+
+/* Thomas Glanzmann reports the following responses */
+/* 25 87 ee ca 0b + */
+/* 25 87 ee ca 0d - */
+/* 25 87 ee ca 08 << */
+/* 25 87 ee ca 07 >> */
+/* 25 87 ee ca 04 >" */
+/* 25 87 ee ca 02 menu */
+/* 26 00 00 00 00 for key repeat*/
+/* He also observes the following event sometimes */
+/* sent after a key is release, which I interpret */
+/* as a flat battery message */
+/* 25 87 e0 ca 06 flat battery */
+
+/* Alexandre Karpenko reports the following responses for Device ID 0x8242 */
+/* 25 87 ee 47 0b + */
+/* 25 87 ee 47 0d - */
+/* 25 87 ee 47 08 << */
+/* 25 87 ee 47 07 >> */
+/* 25 87 ee 47 04 >" */
+/* 25 87 ee 47 02 menu */
+/* 26 87 ee 47 ** for key repeat (** is the code of the key being held) */
+
+static int keymap[MAX_KEYS] = {
+ KEY_RESERVED,
+ KEY_MENU,
+ KEY_PLAYPAUSE,
+ KEY_FORWARD,
+ KEY_BACK,
+ KEY_VOLUMEUP,
+ KEY_VOLUMEDOWN,
+ KEY_RESERVED,
+};
+
+static void dump_packet(struct appleir *appleir, char *msg, u8 *data, int len)
+{
+ int i;
+
+ printk(KERN_ERR "appleir: %s (%d bytes)", msg, len);
+
+ for (i = 0; i < len; ++i)
+ printk(" %02x", data[i]);
+ printk("\n");
+}
+
+static void key_up(struct appleir *appleir, int key)
+{
+ dbginfo (&appleir->input_dev->dev, "key %d up\n", key);
+ input_report_key(appleir->input_dev, key, 0);
+ input_sync(appleir->input_dev);
+}
+
+static void key_down(struct appleir *appleir, int key)
+{
+ dbginfo (&appleir->input_dev->dev, "key %d down\n", key);
+ input_report_key(appleir->input_dev, key, 1);
+ input_sync(appleir->input_dev);
+}
+
+static void battery_flat(struct appleir *appleir)
+{
+ dev_err(&appleir->input_dev->dev, "possible flat battery?\n");
+}
+
+static void key_up_tick(unsigned long data)
+{
+ struct appleir *appleir = (struct appleir *)data;
+
+ if (appleir->current_key) {
+ key_up(appleir, appleir->current_key);
+ appleir->current_key = 0;
+ }
+}
+
+static void new_data(struct appleir *appleir, u8 *data, int len)
+{
+ static const u8 keydown[] = { 0x25, 0x87, 0xee };
+ static const u8 keyrepeat[] = { 0x26, };
+ static const u8 flatbattery[] = { 0x25, 0x87, 0xe0 };
+
+ if (debug)
+ dump_packet(appleir, "received", data, len);
+
+ if (len != 5)
+ return;
+
+ if (!memcmp(data, keydown, sizeof(keydown))) {
+ /* If we already have a key down, take it up before marking
+ this one down */
+ if (appleir->current_key)
+ key_up(appleir, appleir->current_key);
+ appleir->current_key = keymap[(data[4] >> 1) & MAX_KEYS_MASK];
+
+ key_down(appleir, appleir->current_key);
+ /* Remote doesn't do key up, either pull them up, in the test
+ above, or here set a timer which pulls them up after 1/8 s */
+ mod_timer(&appleir->key_up_timer, jiffies + HZ / 8);
+
+ return;
+ }
+
+ if (!memcmp(data, keyrepeat, sizeof(keyrepeat))) {
+ key_down(appleir, appleir->current_key);
+ /* Remote doesn't do key up, either pull them up, in the test
+ above, or here set a timer which pulls them up after 1/8 s */
+ mod_timer(&appleir->key_up_timer, jiffies + HZ / 8);
+ return;
+ }
+
+ if (!memcmp(data, flatbattery, sizeof(flatbattery))) {
+ battery_flat(appleir);
+ /* Fall through */
+ }
+
+ dump_packet(appleir, "unknown packet", data, len);
+}
+
+static void appleir_urb(struct urb *urb)
+{
+ struct appleir *appleir = urb->context;
+ int status = urb->status;
+ int retval;
+
+ switch (status) {
+ case 0:
+ new_data(appleir, urb->transfer_buffer, urb->actual_length);
+ break;
+ case -ECONNRESET:
+ case -ENOENT:
+ case -ESHUTDOWN:
+ /* This urb is terminated, clean up */
+ dbginfo(&appleir->input_dev->dev, "%s - urb shutting down with status: %d", __func__,
+ urb->status);
+ return;
+ default:
+ dbginfo(&appleir->input_dev->dev, "%s - nonzero urb status received: %d", __func__,
+ urb->status);
+ }
+
+ retval = usb_submit_urb(urb, GFP_ATOMIC);
+ if (retval)
+ err("%s - usb_submit_urb failed with result %d", __func__,
+ retval);
+}
+
+static int appleir_open(struct input_dev *dev)
+{
+ struct appleir *appleir = input_get_drvdata(dev);
+ struct usb_interface *intf = usb_ifnum_to_if(appleir->usbdev, 0);
+ int r;
+
+ r = usb_autopm_get_interface(intf);
+ if (r) {
+ dev_err(&intf->dev,
+ "%s(): usb_autopm_get_interface() = %d\n", __func__, r);
+ return r;
+ }
+
+ mutex_lock(&appleir_mutex);
+
+ if (usb_submit_urb(appleir->urb, GFP_KERNEL)) {
+ r = -EIO;
+ goto fail;
+ }
+
+ appleir->flags |= APPLEIR_OPENED;
+
+ mutex_unlock(&appleir_mutex);
+
+ usb_autopm_put_interface(intf);
+
+ return 0;
+fail:
+ mutex_unlock(&appleir_mutex);
+ usb_autopm_put_interface(intf);
+ return r;
+}
+
+static void appleir_close(struct input_dev *dev)
+{
+ struct appleir *appleir = input_get_drvdata(dev);
+
+ mutex_lock(&appleir_mutex);
+
+ if (!(appleir->flags & APPLEIR_SUSPENDED)) {
+ usb_kill_urb(appleir->urb);
+ del_timer_sync(&appleir->key_up_timer);
+ }
+
+ appleir->flags &= ~APPLEIR_OPENED;
+
+ mutex_unlock(&appleir_mutex);
+}
+
+static int appleir_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ struct usb_device *dev = interface_to_usbdev(intf);
+ struct usb_endpoint_descriptor *endpoint;
+ struct appleir *appleir = NULL;
+ struct input_dev *input_dev;
+ int retval = -ENOMEM;
+ int i;
+
+ appleir = kzalloc(sizeof(struct appleir), GFP_KERNEL);
+ if (!appleir)
+ goto fail;
+
+ appleir->data = usb_buffer_alloc(dev, URB_SIZE, GFP_KERNEL,
+ &appleir->dma_buf);
+ if (!appleir->data)
+ goto fail;
+
+ appleir->urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!appleir->urb)
+ goto fail;
+
+ appleir->usbdev = dev;
+
+ input_dev = input_allocate_device();
+ if (!input_dev)
+ goto fail;
+
+ appleir->input_dev = input_dev;
+
+ usb_make_path(dev, appleir->phys, sizeof(appleir->phys));
+ strlcpy(appleir->phys, "/input0", sizeof(appleir->phys));
+
+ input_dev->name = "Apple infrared remote control driver";
+ input_dev->phys = appleir->phys;
+ usb_to_input_id(dev, &input_dev->id);
+ input_dev->dev.parent = &intf->dev;
+ input_set_drvdata(input_dev, appleir);
+
+ input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP);
+ input_dev->ledbit[0] = 0;
+
+ for (i = 0; i < MAX_KEYS; i++)
+ set_bit(keymap[i], input_dev->keybit);
+
+ clear_bit(0, input_dev->keybit);
+
+ input_dev->open = appleir_open;
+ input_dev->close = appleir_close;
+
+ endpoint = &intf->cur_altsetting->endpoint[0].desc;
+
+ usb_fill_int_urb(appleir->urb, dev,
+ usb_rcvintpipe(dev, endpoint->bEndpointAddress),
+ appleir->data, 8,
+ appleir_urb, appleir, endpoint->bInterval);
+
+ appleir->urb->transfer_dma = appleir->dma_buf;
+ appleir->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+ usb_set_intfdata(intf, appleir);
+
+ init_timer(&appleir->key_up_timer);
+
+ appleir->key_up_timer.function = key_up_tick;
+ appleir->key_up_timer.data = (unsigned long)appleir;
+
+ appleir->timer_initted++;
+
+ retval = input_register_device(appleir->input_dev);
+ if (retval)
+ goto fail;
+
+ return 0;
+
+fail:
+ printk(KERN_WARNING "Failed to load appleir\n");
+ if (appleir) {
+ if (appleir->data)
+ usb_buffer_free(dev, URB_SIZE, appleir->data,
+ appleir->dma_buf);
+
+ if (appleir->timer_initted)
+ del_timer_sync(&appleir->key_up_timer);
+
+ if (appleir->input_dev)
+ input_free_device(appleir->input_dev);
+
+ kfree(appleir);
+ }
+
+ return retval;
+}
+
+static void appleir_disconnect(struct usb_interface *intf)
+{
+ struct appleir *appleir = usb_get_intfdata(intf);
+
+ usb_set_intfdata(intf, NULL);
+ if (appleir) {
+ input_unregister_device(appleir->input_dev);
+ if (appleir->timer_initted)
+ del_timer_sync(&appleir->key_up_timer);
+ usb_kill_urb(appleir->urb);
+ usb_free_urb(appleir->urb);
+ usb_buffer_free(interface_to_usbdev(intf), URB_SIZE,
+ appleir->data, appleir->dma_buf);
+ kfree(appleir);
+ }
+}
+
+static int appleir_suspend(struct usb_interface *interface,
+ pm_message_t message)
+{
+ struct appleir *appleir;
+
+ appleir = usb_get_intfdata(interface);
+
+ mutex_lock(&appleir_mutex);
+
+ if (appleir->flags & APPLEIR_OPENED) {
+ usb_kill_urb(appleir->urb);
+ del_timer_sync(&appleir->key_up_timer);
+ }
+
+ appleir->flags |= APPLEIR_SUSPENDED;
+
+ mutex_unlock(&appleir_mutex);
+
+ return 0;
+}
+
+static int appleir_resume(struct usb_interface *interface)
+{
+ struct appleir *appleir;
+
+ appleir = usb_get_intfdata(interface);
+
+ mutex_lock(&appleir_mutex);
+
+ if (appleir->flags & APPLEIR_OPENED) {
+ struct usb_endpoint_descriptor *endpoint;
+
+ endpoint = &interface->cur_altsetting->endpoint[0].desc;
+ usb_fill_int_urb(appleir->urb, appleir->usbdev,
+ usb_rcvintpipe(appleir->usbdev, endpoint->bEndpointAddress),
+ appleir->data, 8,
+ appleir_urb, appleir, endpoint->bInterval);
+ appleir->urb->transfer_dma = appleir->dma_buf;
+ appleir->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+ init_timer(&appleir->key_up_timer);
+
+ appleir->key_up_timer.function = key_up_tick;
+ appleir->key_up_timer.data = (unsigned long)appleir;
+ }
+
+ appleir->flags &= ~APPLEIR_SUSPENDED;
+
+ mutex_unlock(&appleir_mutex);
+
+ return 0;
+}
+
+static struct usb_driver appleir_driver = {
+ .name = "appleir",
+ .probe = appleir_probe,
+ .disconnect = appleir_disconnect,
+ .suspend = appleir_suspend,
+ .resume = appleir_resume,
+ .reset_resume = NULL,
+ .id_table = appleir_ids,
+ .supports_autosuspend = 1,
+};
+
+static int __init appleir_init(void)
+{
+ int retval;
+
+ retval = usb_register(&appleir_driver);
+ if (retval)
+ goto out;
+ printk(KERN_INFO DRIVER_VERSION ":" DRIVER_DESC);
+out:
+ return retval;
+}
+
+static void __exit appleir_exit(void)
+{
+ usb_deregister(&appleir_driver);
+}
+
+module_init(appleir_init);
+module_exit(appleir_exit);
--
1.6.6.1
^ permalink raw reply related [flat|nested] 57+ messages in thread
* Re: [PATCH] Input: add appleir USB driver
2010-04-16 16:19 Bastien Nocera
@ 2010-04-17 8:12 ` Dmitry Torokhov
2010-04-17 21:44 ` Bastien Nocera
2010-04-19 9:22 ` Jiri Kosina
0 siblings, 2 replies; 57+ messages in thread
From: Dmitry Torokhov @ 2010-04-17 8:12 UTC (permalink / raw)
To: Bastien Nocera; +Cc: linux-input, Jiri Kosina
HI Bastien,
On Fri, Apr 16, 2010 at 05:19:52PM +0100, Bastien Nocera wrote:
> This driver was originally written by James McKenzie, updated by
> Greg Kroah-Hartman, further updated by myself, with suspend support
> added.
>
> More recent versions of the IR receiver are also supported through
> a patch by Alex Karpenko.
>
> Tested on a MacbookAir1,1
>
A few comments...
> Signed-off-by: Bastien Nocera <hadess@hadess.net>
> ---
> Documentation/input/appleir.txt | 45 ++++
> drivers/hid/hid-apple.c | 4 -
> drivers/hid/hid-core.c | 5 +-
> drivers/hid/hid-ids.h | 1 +
HID pieces need to go through Jiri or he needs to ack them to go through
my tree...
> drivers/input/misc/Kconfig | 13 +
> drivers/input/misc/Makefile | 1 +
> drivers/input/misc/appleir.c | 477 +++++++++++++++++++++++++++++++++++++++
> 7 files changed, 540 insertions(+), 6 deletions(-)
> create mode 100644 Documentation/input/appleir.txt
> create mode 100644 drivers/input/misc/appleir.c
>
> diff --git a/Documentation/input/appleir.txt b/Documentation/input/appleir.txt
> new file mode 100644
> index 0000000..0267a4b
> --- /dev/null
> +++ b/Documentation/input/appleir.txt
> @@ -0,0 +1,45 @@
> +Apple IR receiver Driver (appleir)
> +----------------------------------
> + Copyright (C) 2009 Bastien Nocera <hadess@hadess.net>
> +
> +The appleir driver is a kernel input driver to handle Apple's IR
> +receivers (and associated remotes) in the kernel.
> +
> +The driver is an input driver which only handles "official" remotes
> +as built and sold by Apple.
> +
> +Authors
> +-------
> +
> +James McKenzie (original driver)
> +Alex Karpenko (05ac:8242 support)
> +Greg Kroah-Hartman (cleanups and original submission)
> +Bastien Nocera (further cleanups and suspend support)
> +
> +Supported hardware
> +------------------
> +
> +- All Apple laptops and desktops from 2005 onwards, except:
> + - the unibody Macbook (2009)
> + - Mac Pro (all versions)
> +- Apple TV (all revisions)
> +
> +The remote will only support the 6 buttons of the original remotes
> +as sold by Apple. See the next section if you want to use other remotes
> +or want to use lirc with the device instead of the kernel driver.
> +
> +Using lirc (native) instead of the kernel driver
> +------------------------------------------------
> +
> +First, you will need to disable the kernel driver for the receiver.
> +
> +This can be achieved by passing quirks to the usbhid driver.
> +The quirk line would be:
> +usbhid.quirks=0x05ac:0x8242:0x40000010
> +
> +With 0x05ac being the vendor ID (Apple, you shouldn't need to change this)
> +With 0x8242 being the product ID (check the output of lsusb for your hardware)
> +And 0x10 being "HID_QUIRK_HIDDEV_FORCE" and 0x40000000 being "HID_QUIRK_NO_IGNORE"
> +
> +This should force the creation of a hiddev device for the receiver, and
> +make it usable under lirc.
> diff --git a/drivers/hid/hid-apple.c b/drivers/hid/hid-apple.c
> index 78286b1..5f2a731 100644
> --- a/drivers/hid/hid-apple.c
> +++ b/drivers/hid/hid-apple.c
> @@ -360,10 +360,6 @@ static void apple_remove(struct hid_device *hdev)
> }
>
> static const struct hid_device_id apple_devices[] = {
> - { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ATV_IRCONTROL),
> - .driver_data = APPLE_HIDDEV | APPLE_IGNORE_HIDINPUT },
> - { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL4),
> - .driver_data = APPLE_HIDDEV | APPLE_IGNORE_HIDINPUT },
> { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MIGHTYMOUSE),
> .driver_data = APPLE_MIGHTYMOUSE | APPLE_INVERT_HWHEEL },
>
> diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
> index 368fbb0..b57e5f7 100644
> --- a/drivers/hid/hid-core.c
> +++ b/drivers/hid/hid-core.c
> @@ -1253,8 +1253,6 @@ static const struct hid_device_id hid_blacklist[] = {
> { HID_USB_DEVICE(USB_VENDOR_ID_3M, USB_DEVICE_ID_3M1968) },
> { HID_USB_DEVICE(USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_WCP32PU) },
> { HID_USB_DEVICE(USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_X5_005D) },
> - { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ATV_IRCONTROL) },
> - { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL4) },
> { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MIGHTYMOUSE) },
> { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGICMOUSE) },
> { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_ANSI) },
> @@ -1553,6 +1551,9 @@ static const struct hid_device_id hid_ignore_list[] = {
> { HID_USB_DEVICE(USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_24) },
> { HID_USB_DEVICE(USB_VENDOR_ID_AIRCABLE, USB_DEVICE_ID_AIRCABLE1) },
> { HID_USB_DEVICE(USB_VENDOR_ID_ALCOR, USB_DEVICE_ID_ALCOR_USBRS232) },
> + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL) },
> + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ATV_IRCONTROL) },
> + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL4) },
> { HID_USB_DEVICE(USB_VENDOR_ID_ASUS, USB_DEVICE_ID_ASUS_T91MT)},
> { HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK, USB_DEVICE_ID_ASUSTEK_LCM)},
> { HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK, USB_DEVICE_ID_ASUSTEK_LCM2)},
> diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
> index 72c05f9..66a2ca8 100644
> --- a/drivers/hid/hid-ids.h
> +++ b/drivers/hid/hid-ids.h
> @@ -97,6 +97,7 @@
> #define USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_JIS 0x023b
> #define USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY 0x030a
> #define USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY 0x030b
> +#define USB_DEVICE_ID_APPLE_IRCONTROL 0x8240
> #define USB_DEVICE_ID_APPLE_ATV_IRCONTROL 0x8241
> #define USB_DEVICE_ID_APPLE_IRCONTROL4 0x8242
>
> diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
> index 23140a3..46614b2 100644
> --- a/drivers/input/misc/Kconfig
> +++ b/drivers/input/misc/Kconfig
> @@ -159,6 +159,19 @@ config INPUT_KEYSPAN_REMOTE
> To compile this driver as a module, choose M here: the module will
> be called keyspan_remote.
>
> +config INPUT_APPLEIR
> + tristate "Apple infrared receiver (built in)"
> + depends on USB_ARCH_HAS_HCD
> + select USB
> + help
> + Say Y here if you want to use a Apple infrared remote control. All
> + the Apple computers from 2005 onwards include such a port, except
> + the unibody Macbook (2009), and Mac Pros. This receiver is also
> + used in the Apple TV set-top box.
> +
> + To compile this driver as a module, choose M here: the module will
> + be called appleir.
> +
> config INPUT_POWERMATE
> tristate "Griffin PowerMate and Contour Jog support"
> depends on USB_ARCH_HAS_HCD
> diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
> index 7e95a5d..3fa4404 100644
> --- a/drivers/input/misc/Makefile
> +++ b/drivers/input/misc/Makefile
> @@ -6,6 +6,7 @@
>
> obj-$(CONFIG_INPUT_88PM860X_ONKEY) += 88pm860x_onkey.o
> obj-$(CONFIG_INPUT_APANEL) += apanel.o
> +obj-$(CONFIG_INPUT_APPLEIR) += appleir.o
> obj-$(CONFIG_INPUT_ATI_REMOTE) += ati_remote.o
> obj-$(CONFIG_INPUT_ATI_REMOTE2) += ati_remote2.o
> obj-$(CONFIG_INPUT_ATLAS_BTNS) += atlas_btns.o
> diff --git a/drivers/input/misc/appleir.c b/drivers/input/misc/appleir.c
> new file mode 100644
> index 0000000..138f4c8
> --- /dev/null
> +++ b/drivers/input/misc/appleir.c
> @@ -0,0 +1,477 @@
> +/*
> + * appleir: USB driver for the apple ir device
> + *
> + * Original driver written by James McKenzie
> + * Ported to recent 2.6 kernel versions by Greg Kroah-Hartman <gregkh@suse.de>
> + *
> + * Copyright (C) 2006 James McKenzie
> + * Copyright (C) 2008 Greg Kroah-Hartman <greg@kroah.com>
> + * Copyright (C) 2008 Novell Inc.
> + *
> + * 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.
> + *
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/slab.h>
> +#include <linux/input.h>
> +#include <linux/usb/input.h>
> +#include <linux/module.h>
> +#include <linux/init.h>
> +#include <linux/usb.h>
> +#include <linux/usb/input.h>
> +#include <asm/unaligned.h>
> +#include <asm/byteorder.h>
> +
> +#define DRIVER_VERSION "v1.2"
> +#define DRIVER_AUTHOR "James McKenzie"
> +#define DRIVER_DESC "Apple infrared receiver driver"
> +#define DRIVER_LICENSE "GPL"
> +
> +MODULE_AUTHOR(DRIVER_AUTHOR);
> +MODULE_DESCRIPTION(DRIVER_DESC);
> +MODULE_LICENSE(DRIVER_LICENSE);
> +
> +#define USB_VENDOR_ID_APPLE 0x05ac
> +#define USB_DEVICE_ID_APPLE_IRCONTROL 0x8240
> +#define USB_DEVICE_ID_APPLE_ATV_IRCONTROL 0x8241
> +#define USB_DEVICE_ID_APPLE_IRCONTROL4 0x8242
> +
> +#define URB_SIZE 32
> +
> +#define MAX_KEYS 8
> +#define MAX_KEYS_MASK (MAX_KEYS - 1)
> +
> +#define dbginfo(dev, format, arg...) do { if (debug) dev_info(dev , format , ## arg); } while (0)
> +
> +static int debug;
> +module_param(debug, int, 0644);
> +MODULE_PARM_DESC(debug, "Enable extra debug messages and information");
> +
> +struct appleir {
> + struct input_dev *input_dev;
> + u8 *data;
> + dma_addr_t dma_buf;
> + struct usb_device *usbdev;
> + unsigned int flags;
> + struct urb *urb;
> + int timer_initted;
> + struct timer_list key_up_timer;
> + int current_key;
> + char phys[32];
> +};
> +
> +static DEFINE_MUTEX(appleir_mutex);
> +
> +enum {
> + APPLEIR_OPENED = 0x1,
> + APPLEIR_SUSPENDED = 0x2,
> +};
> +
> +static struct usb_device_id appleir_ids[] = {
> + { USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL) },
> + { USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ATV_IRCONTROL) },
> + { USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL4) },
> + {}
> +};
> +MODULE_DEVICE_TABLE(usb, appleir_ids);
> +
> +/* I have two devices both of which report the following */
> +/* 25 87 ee 83 0a + */
> +/* 25 87 ee 83 0c - */
> +/* 25 87 ee 83 09 << */
> +/* 25 87 ee 83 06 >> */
> +/* 25 87 ee 83 05 >" */
> +/* 25 87 ee 83 03 menu */
> +/* 26 00 00 00 00 for key repeat*/
> +
> +/* Thomas Glanzmann reports the following responses */
> +/* 25 87 ee ca 0b + */
> +/* 25 87 ee ca 0d - */
> +/* 25 87 ee ca 08 << */
> +/* 25 87 ee ca 07 >> */
> +/* 25 87 ee ca 04 >" */
> +/* 25 87 ee ca 02 menu */
> +/* 26 00 00 00 00 for key repeat*/
> +/* He also observes the following event sometimes */
> +/* sent after a key is release, which I interpret */
> +/* as a flat battery message */
> +/* 25 87 e0 ca 06 flat battery */
> +
> +/* Alexandre Karpenko reports the following responses for Device ID 0x8242 */
> +/* 25 87 ee 47 0b + */
> +/* 25 87 ee 47 0d - */
> +/* 25 87 ee 47 08 << */
> +/* 25 87 ee 47 07 >> */
> +/* 25 87 ee 47 04 >" */
> +/* 25 87 ee 47 02 menu */
> +/* 26 87 ee 47 ** for key repeat (** is the code of the key being held) */
> +
> +static int keymap[MAX_KEYS] = {
> + KEY_RESERVED,
> + KEY_MENU,
> + KEY_PLAYPAUSE,
> + KEY_FORWARD,
> + KEY_BACK,
> + KEY_VOLUMEUP,
> + KEY_VOLUMEDOWN,
> + KEY_RESERVED,
> +};
> +
> +static void dump_packet(struct appleir *appleir, char *msg, u8 *data, int len)
> +{
> + int i;
> +
> + printk(KERN_ERR "appleir: %s (%d bytes)", msg, len);
> +
> + for (i = 0; i < len; ++i)
> + printk(" %02x", data[i]);
> + printk("\n");
> +}
> +
> +static void key_up(struct appleir *appleir, int key)
> +{
> + dbginfo (&appleir->input_dev->dev, "key %d up\n", key);
No space between function and opening parenthesis.
> + input_report_key(appleir->input_dev, key, 0);
> + input_sync(appleir->input_dev);
> +}
> +
> +static void key_down(struct appleir *appleir, int key)
> +{
> + dbginfo (&appleir->input_dev->dev, "key %d down\n", key);
> + input_report_key(appleir->input_dev, key, 1);
> + input_sync(appleir->input_dev);
> +}
> +
> +static void battery_flat(struct appleir *appleir)
> +{
> + dev_err(&appleir->input_dev->dev, "possible flat battery?\n");
> +}
> +
> +static void key_up_tick(unsigned long data)
> +{
> + struct appleir *appleir = (struct appleir *)data;
> +
> + if (appleir->current_key) {
> + key_up(appleir, appleir->current_key);
> + appleir->current_key = 0;
> + }
> +}
> +
> +static void new_data(struct appleir *appleir, u8 *data, int len)
> +{
> + static const u8 keydown[] = { 0x25, 0x87, 0xee };
> + static const u8 keyrepeat[] = { 0x26, };
> + static const u8 flatbattery[] = { 0x25, 0x87, 0xe0 };
> +
> + if (debug)
> + dump_packet(appleir, "received", data, len);
> +
> + if (len != 5)
> + return;
> +
> + if (!memcmp(data, keydown, sizeof(keydown))) {
> + /* If we already have a key down, take it up before marking
> + this one down */
> + if (appleir->current_key)
> + key_up(appleir, appleir->current_key);
> + appleir->current_key = keymap[(data[4] >> 1) & MAX_KEYS_MASK];
> +
> + key_down(appleir, appleir->current_key);
> + /* Remote doesn't do key up, either pull them up, in the test
> + above, or here set a timer which pulls them up after 1/8 s */
> + mod_timer(&appleir->key_up_timer, jiffies + HZ / 8);
> +
> + return;
> + }
> +
> + if (!memcmp(data, keyrepeat, sizeof(keyrepeat))) {
> + key_down(appleir, appleir->current_key);
> + /* Remote doesn't do key up, either pull them up, in the test
> + above, or here set a timer which pulls them up after 1/8 s */
> + mod_timer(&appleir->key_up_timer, jiffies + HZ / 8);
> + return;
> + }
> +
> + if (!memcmp(data, flatbattery, sizeof(flatbattery))) {
> + battery_flat(appleir);
> + /* Fall through */
> + }
> +
> + dump_packet(appleir, "unknown packet", data, len);
> +}
> +
> +static void appleir_urb(struct urb *urb)
> +{
> + struct appleir *appleir = urb->context;
> + int status = urb->status;
> + int retval;
> +
> + switch (status) {
> + case 0:
> + new_data(appleir, urb->transfer_buffer, urb->actual_length);
> + break;
> + case -ECONNRESET:
> + case -ENOENT:
> + case -ESHUTDOWN:
> + /* This urb is terminated, clean up */
> + dbginfo(&appleir->input_dev->dev, "%s - urb shutting down with status: %d", __func__,
> + urb->status);
> + return;
> + default:
> + dbginfo(&appleir->input_dev->dev, "%s - nonzero urb status received: %d", __func__,
> + urb->status);
> + }
> +
> + retval = usb_submit_urb(urb, GFP_ATOMIC);
> + if (retval)
> + err("%s - usb_submit_urb failed with result %d", __func__,
> + retval);
> +}
> +
> +static int appleir_open(struct input_dev *dev)
> +{
> + struct appleir *appleir = input_get_drvdata(dev);
> + struct usb_interface *intf = usb_ifnum_to_if(appleir->usbdev, 0);
> + int r;
> +
> + r = usb_autopm_get_interface(intf);
> + if (r) {
> + dev_err(&intf->dev,
> + "%s(): usb_autopm_get_interface() = %d\n", __func__, r);
> + return r;
> + }
> +
> + mutex_lock(&appleir_mutex);
> +
> + if (usb_submit_urb(appleir->urb, GFP_KERNEL)) {
> + r = -EIO;
> + goto fail;
> + }
> +
> + appleir->flags |= APPLEIR_OPENED;
> +
> + mutex_unlock(&appleir_mutex);
> +
> + usb_autopm_put_interface(intf);
> +
> + return 0;
> +fail:
> + mutex_unlock(&appleir_mutex);
> + usb_autopm_put_interface(intf);
> + return r;
> +}
> +
> +static void appleir_close(struct input_dev *dev)
> +{
> + struct appleir *appleir = input_get_drvdata(dev);
> +
> + mutex_lock(&appleir_mutex);
> +
> + if (!(appleir->flags & APPLEIR_SUSPENDED)) {
> + usb_kill_urb(appleir->urb);
> + del_timer_sync(&appleir->key_up_timer);
> + }
> +
> + appleir->flags &= ~APPLEIR_OPENED;
> +
> + mutex_unlock(&appleir_mutex);
> +}
> +
> +static int appleir_probe(struct usb_interface *intf,
> + const struct usb_device_id *id)
> +{
> + struct usb_device *dev = interface_to_usbdev(intf);
> + struct usb_endpoint_descriptor *endpoint;
> + struct appleir *appleir = NULL;
> + struct input_dev *input_dev;
> + int retval = -ENOMEM;
> + int i;
> +
> + appleir = kzalloc(sizeof(struct appleir), GFP_KERNEL);
> + if (!appleir)
> + goto fail;
> +
> + appleir->data = usb_buffer_alloc(dev, URB_SIZE, GFP_KERNEL,
> + &appleir->dma_buf);
> + if (!appleir->data)
> + goto fail;
> +
> + appleir->urb = usb_alloc_urb(0, GFP_KERNEL);
> + if (!appleir->urb)
> + goto fail;
> +
> + appleir->usbdev = dev;
> +
> + input_dev = input_allocate_device();
> + if (!input_dev)
> + goto fail;
> +
> + appleir->input_dev = input_dev;
> +
> + usb_make_path(dev, appleir->phys, sizeof(appleir->phys));
> + strlcpy(appleir->phys, "/input0", sizeof(appleir->phys));
> +
> + input_dev->name = "Apple infrared remote control driver";
Device is not driver. So it should probably read:
"Apple Infrared Remote Controller"
> + input_dev->phys = appleir->phys;
> + usb_to_input_id(dev, &input_dev->id);
> + input_dev->dev.parent = &intf->dev;
> + input_set_drvdata(input_dev, appleir);
> +
> + input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP);
> + input_dev->ledbit[0] = 0;
Input device is zeroed out by input_allocate_device().
> +
> + for (i = 0; i < MAX_KEYS; i++)
> + set_bit(keymap[i], input_dev->keybit);
Keymap should be part of appleir structure; also set up
input_dev->keycode, keycodemax and keycodesize so that keymap can be
adjusted from userspace on per-device basis.
> +
> + clear_bit(0, input_dev->keybit);
Not needed anymore.
> +
> + input_dev->open = appleir_open;
> + input_dev->close = appleir_close;
> +
> + endpoint = &intf->cur_altsetting->endpoint[0].desc;
> +
> + usb_fill_int_urb(appleir->urb, dev,
> + usb_rcvintpipe(dev, endpoint->bEndpointAddress),
> + appleir->data, 8,
> + appleir_urb, appleir, endpoint->bInterval);
> +
> + appleir->urb->transfer_dma = appleir->dma_buf;
> + appleir->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
> +
> + usb_set_intfdata(intf, appleir);
Should go directly in front of "return 0;".
> +
> + init_timer(&appleir->key_up_timer);
> +
> + appleir->key_up_timer.function = key_up_tick;
> + appleir->key_up_timer.data = (unsigned long)appleir;
setup_timer()?
> +
> + appleir->timer_initted++;
Pointless, really.
> +
> + retval = input_register_device(appleir->input_dev);
> + if (retval)
> + goto fail;
> +
> + return 0;
> +
> +fail:
Generally I prefer having multiple fail points instead of teting
conditions in fail path.
> + printk(KERN_WARNING "Failed to load appleir\n");
Not load but bind. And driver core will let us know already.
> + if (appleir) {
> + if (appleir->data)
> + usb_buffer_free(dev, URB_SIZE, appleir->data,
> + appleir->dma_buf);
> +
> + if (appleir->timer_initted)
> + del_timer_sync(&appleir->key_up_timer);
> +
No need (see comments in appleir_remove).
> + if (appleir->input_dev)
> + input_free_device(appleir->input_dev);
> +
> + kfree(appleir);
> + }
> +
> + return retval;
> +}
> +
> +static void appleir_disconnect(struct usb_interface *intf)
> +{
> + struct appleir *appleir = usb_get_intfdata(intf);
> +
> + usb_set_intfdata(intf, NULL);
> + if (appleir) {
Is it possible for appleir to be NULL at this point?
> + input_unregister_device(appleir->input_dev);
> + if (appleir->timer_initted)
How can this be possible?
> + del_timer_sync(&appleir->key_up_timer);
If this is needed then you are doing it too late (input deviceis already
gone). However this should not be needed since appleir_close is
guaranteed to be called if you opened the device and it will cancel the
timer for you.
> + usb_kill_urb(appleir->urb);
NOt needed here either.
> + usb_free_urb(appleir->urb);
> + usb_buffer_free(interface_to_usbdev(intf), URB_SIZE,
> + appleir->data, appleir->dma_buf);
> + kfree(appleir);
> + }
> +}
> +
> +static int appleir_suspend(struct usb_interface *interface,
> + pm_message_t message)
> +{
> + struct appleir *appleir;
> +
> + appleir = usb_get_intfdata(interface);
> +
> + mutex_lock(&appleir_mutex);
> +
> + if (appleir->flags & APPLEIR_OPENED) {
> + usb_kill_urb(appleir->urb);
> + del_timer_sync(&appleir->key_up_timer);
> + }
> +
> + appleir->flags |= APPLEIR_SUSPENDED;
> +
> + mutex_unlock(&appleir_mutex);
> +
> + return 0;
> +}
> +
> +static int appleir_resume(struct usb_interface *interface)
> +{
> + struct appleir *appleir;
> +
> + appleir = usb_get_intfdata(interface);
Combine definition with initialization?
> +
> + mutex_lock(&appleir_mutex);
> +
> + if (appleir->flags & APPLEIR_OPENED) {
> + struct usb_endpoint_descriptor *endpoint;
> +
> + endpoint = &interface->cur_altsetting->endpoint[0].desc;
> + usb_fill_int_urb(appleir->urb, appleir->usbdev,
> + usb_rcvintpipe(appleir->usbdev, endpoint->bEndpointAddress),
> + appleir->data, 8,
> + appleir_urb, appleir, endpoint->bInterval);
> + appleir->urb->transfer_dma = appleir->dma_buf;
> + appleir->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
> +
> + init_timer(&appleir->key_up_timer);
> +
> + appleir->key_up_timer.function = key_up_tick;
> + appleir->key_up_timer.data = (unsigned long)appleir;
Why do you need to re-initialize the timer?
> + }
> +
> + appleir->flags &= ~APPLEIR_SUSPENDED;
> +
> + mutex_unlock(&appleir_mutex);
> +
> + return 0;
> +}
> +
> +static struct usb_driver appleir_driver = {
> + .name = "appleir",
> + .probe = appleir_probe,
> + .disconnect = appleir_disconnect,
> + .suspend = appleir_suspend,
> + .resume = appleir_resume,
> + .reset_resume = NULL,
> + .id_table = appleir_ids,
> + .supports_autosuspend = 1,
> +};
> +
> +static int __init appleir_init(void)
> +{
> + int retval;
> +
> + retval = usb_register(&appleir_driver);
> + if (retval)
> + goto out;
> + printk(KERN_INFO DRIVER_VERSION ":" DRIVER_DESC);
> +out:
> + return retval;
How about
return usb_register(&appleir_driver);
?
Boot is noisy enough.
> +}
> +
> +static void __exit appleir_exit(void)
> +{
> + usb_deregister(&appleir_driver);
> +}
> +
> +module_init(appleir_init);
> +module_exit(appleir_exit);
Thanks.
--
Dmitry
^ permalink raw reply [flat|nested] 57+ messages in thread
* Re: [PATCH] Input: add appleir USB driver
2010-04-17 8:12 ` Dmitry Torokhov
@ 2010-04-17 21:44 ` Bastien Nocera
2010-04-18 19:43 ` Dmitry Torokhov
2010-04-19 9:22 ` Jiri Kosina
1 sibling, 1 reply; 57+ messages in thread
From: Bastien Nocera @ 2010-04-17 21:44 UTC (permalink / raw)
To: Dmitry Torokhov; +Cc: linux-input, Jiri Kosina
On Sat, 2010-04-17 at 01:12 -0700, Dmitry Torokhov wrote:
> HI Bastien,
>
> On Fri, Apr 16, 2010 at 05:19:52PM +0100, Bastien Nocera wrote:
> > This driver was originally written by James McKenzie, updated by
> > Greg Kroah-Hartman, further updated by myself, with suspend support
> > added.
> >
> > More recent versions of the IR receiver are also supported through
> > a patch by Alex Karpenko.
> >
> > Tested on a MacbookAir1,1
> >
>
> A few comments...
All fixed. I removed the suspend/resume code as it worked fine without
it (I was working of an old patch).
Sent the new patch separately.
> > Signed-off-by: Bastien Nocera <hadess@hadess.net>
> > ---
> > Documentation/input/appleir.txt | 45 ++++
> > drivers/hid/hid-apple.c | 4 -
> > drivers/hid/hid-core.c | 5 +-
> > drivers/hid/hid-ids.h | 1 +
>
> HID pieces need to go through Jiri or he needs to ack them to go through
> my tree...
The HID pieces are necessary to get the "lirc" part of the documentation
working (in appleir.txt). The driver is fine without it, but it won't
work with lirc until the HID patch is in.
Cheers
^ permalink raw reply [flat|nested] 57+ messages in thread
* Re: [PATCH] Input: add appleir USB driver
2010-04-17 21:44 ` Bastien Nocera
@ 2010-04-18 19:43 ` Dmitry Torokhov
2010-04-18 19:49 ` Bastien Nocera
0 siblings, 1 reply; 57+ messages in thread
From: Dmitry Torokhov @ 2010-04-18 19:43 UTC (permalink / raw)
To: Bastien Nocera; +Cc: linux-input, Jiri Kosina
On Sat, Apr 17, 2010 at 10:44:55PM +0100, Bastien Nocera wrote:
> On Sat, 2010-04-17 at 01:12 -0700, Dmitry Torokhov wrote:
> > HI Bastien,
> >
> > On Fri, Apr 16, 2010 at 05:19:52PM +0100, Bastien Nocera wrote:
> > > This driver was originally written by James McKenzie, updated by
> > > Greg Kroah-Hartman, further updated by myself, with suspend support
> > > added.
> > >
> > > More recent versions of the IR receiver are also supported through
> > > a patch by Alex Karpenko.
> > >
> > > Tested on a MacbookAir1,1
> > >
> >
> > A few comments...
>
> All fixed. I removed the suspend/resume code as it worked fine without
> it (I was working of an old patch).
>
> Sent the new patch separately.
Hmm, I am curious why suspend and resume is not necessary for this
device... Are you relying on the USB core to tear down and re-create the
device? Then you may lose user-applied settings (like changed keymap).
But if suspend and resume are really not needed then you need to
complete cleanup and get rid of APPLIEIR_OPENED and APPLEIR_SUSPENDED
and their handling in applieir_open() and appleir_close().
>
> > > Signed-off-by: Bastien Nocera <hadess@hadess.net>
> > > ---
> > > Documentation/input/appleir.txt | 45 ++++
> > > drivers/hid/hid-apple.c | 4 -
> > > drivers/hid/hid-core.c | 5 +-
> > > drivers/hid/hid-ids.h | 1 +
> >
> > HID pieces need to go through Jiri or he needs to ack them to go through
> > my tree...
>
> The HID pieces are necessary to get the "lirc" part of the documentation
> working (in appleir.txt). The driver is fine without it, but it won't
> work with lirc until the HID patch is in.
>
Right. I was just saying that Jiri needs to either take HID parts
through his tree or give OK for me to take through mine.
--
Dmitry
^ permalink raw reply [flat|nested] 57+ messages in thread
* Re: [PATCH] Input: add appleir USB driver
2010-04-18 19:43 ` Dmitry Torokhov
@ 2010-04-18 19:49 ` Bastien Nocera
2010-04-18 20:19 ` Dmitry Torokhov
0 siblings, 1 reply; 57+ messages in thread
From: Bastien Nocera @ 2010-04-18 19:49 UTC (permalink / raw)
To: Dmitry Torokhov; +Cc: linux-input, Jiri Kosina
On Sun, 2010-04-18 at 12:43 -0700, Dmitry Torokhov wrote:
> On Sat, Apr 17, 2010 at 10:44:55PM +0100, Bastien Nocera wrote:
> > On Sat, 2010-04-17 at 01:12 -0700, Dmitry Torokhov wrote:
> > > HI Bastien,
> > >
> > > On Fri, Apr 16, 2010 at 05:19:52PM +0100, Bastien Nocera wrote:
> > > > This driver was originally written by James McKenzie, updated by
> > > > Greg Kroah-Hartman, further updated by myself, with suspend support
> > > > added.
> > > >
> > > > More recent versions of the IR receiver are also supported through
> > > > a patch by Alex Karpenko.
> > > >
> > > > Tested on a MacbookAir1,1
> > > >
> > >
> > > A few comments...
> >
> > All fixed. I removed the suspend/resume code as it worked fine without
> > it (I was working of an old patch).
> >
> > Sent the new patch separately.
>
> Hmm, I am curious why suspend and resume is not necessary for this
> device... Are you relying on the USB core to tear down and re-create the
> device? Then you may lose user-applied settings (like changed keymap).
How could I test that?
> But if suspend and resume are really not needed then you need to
> complete cleanup and get rid of APPLIEIR_OPENED and APPLEIR_SUSPENDED
> and their handling in applieir_open() and appleir_close().
My mistake. I'll clean that up now.
> Right. I was just saying that Jiri needs to either take HID parts
> through his tree or give OK for me to take through mine.
OK.
^ permalink raw reply [flat|nested] 57+ messages in thread
* Re: [PATCH] Input: add appleir USB driver
2010-04-18 19:49 ` Bastien Nocera
@ 2010-04-18 20:19 ` Dmitry Torokhov
2010-04-19 0:31 ` Bastien Nocera
0 siblings, 1 reply; 57+ messages in thread
From: Dmitry Torokhov @ 2010-04-18 20:19 UTC (permalink / raw)
To: Bastien Nocera; +Cc: linux-input, Jiri Kosina
On Sunday 18 April 2010 12:49:37 pm Bastien Nocera wrote:
> On Sun, 2010-04-18 at 12:43 -0700, Dmitry Torokhov wrote:
> > On Sat, Apr 17, 2010 at 10:44:55PM +0100, Bastien Nocera wrote:
> > > On Sat, 2010-04-17 at 01:12 -0700, Dmitry Torokhov wrote:
> > > > HI Bastien,
> > > >
> > > > On Fri, Apr 16, 2010 at 05:19:52PM +0100, Bastien Nocera wrote:
> > > > > This driver was originally written by James McKenzie, updated by
> > > > > Greg Kroah-Hartman, further updated by myself, with suspend support
> > > > > added.
> > > > >
> > > > > More recent versions of the IR receiver are also supported through
> > > > > a patch by Alex Karpenko.
> > > > >
> > > > > Tested on a MacbookAir1,1
> > > >
> > > > A few comments...
> > >
> > > All fixed. I removed the suspend/resume code as it worked fine without
> > > it (I was working of an old patch).
> > >
> > > Sent the new patch separately.
> >
> > Hmm, I am curious why suspend and resume is not necessary for this
> > device... Are you relying on the USB core to tear down and re-create the
> > device? Then you may lose user-applied settings (like changed keymap).
>
> How could I test that?
Change the keymap (with keyfuzz or something similar), suspend. resume and see
if the mapping persisted.
>
> > But if suspend and resume are really not needed then you need to
> > complete cleanup and get rid of APPLIEIR_OPENED and APPLEIR_SUSPENDED
> > and their handling in applieir_open() and appleir_close().
>
> My mistake. I'll clean that up now.
>
> > Right. I was just saying that Jiri needs to either take HID parts
> > through his tree or give OK for me to take through mine.
>
> OK.
--
Dmitry
^ permalink raw reply [flat|nested] 57+ messages in thread
* Re: [PATCH] Input: add appleir USB driver
2010-04-18 20:19 ` Dmitry Torokhov
@ 2010-04-19 0:31 ` Bastien Nocera
2010-04-19 7:28 ` Dmitry Torokhov
0 siblings, 1 reply; 57+ messages in thread
From: Bastien Nocera @ 2010-04-19 0:31 UTC (permalink / raw)
To: Dmitry Torokhov; +Cc: linux-input, Jiri Kosina
On Sun, 2010-04-18 at 13:19 -0700, Dmitry Torokhov wrote:
<snip>
> > > Hmm, I am curious why suspend and resume is not necessary for this
> > > device... Are you relying on the USB core to tear down and re-create the
> > > device? Then you may lose user-applied settings (like changed keymap).
> >
> > How could I test that?
>
> Change the keymap (with keyfuzz or something similar), suspend. resume and see
> if the mapping persisted.
Tried using udev's keymap tool on it, but the default get function
doesn't give me any output.
Am I supposed to implement get/setkeycode myself for this to work, or
should the default input functions work? If the latter, is there
anything missing in the driver for me to call to enable that?
Trying to set a keycode says that the "EVIOCSKEYCODE" ioctl returns
EINVAL.
Cheers
^ permalink raw reply [flat|nested] 57+ messages in thread
* Re: [PATCH] Input: add appleir USB driver
2010-04-19 0:31 ` Bastien Nocera
@ 2010-04-19 7:28 ` Dmitry Torokhov
2010-04-19 10:08 ` Bastien Nocera
0 siblings, 1 reply; 57+ messages in thread
From: Dmitry Torokhov @ 2010-04-19 7:28 UTC (permalink / raw)
To: Bastien Nocera; +Cc: linux-input, Jiri Kosina
On Mon, Apr 19, 2010 at 01:31:49AM +0100, Bastien Nocera wrote:
> On Sun, 2010-04-18 at 13:19 -0700, Dmitry Torokhov wrote:
> <snip>
> > > > Hmm, I am curious why suspend and resume is not necessary for this
> > > > device... Are you relying on the USB core to tear down and re-create the
> > > > device? Then you may lose user-applied settings (like changed keymap).
> > >
> > > How could I test that?
> >
> > Change the keymap (with keyfuzz or something similar), suspend. resume and see
> > if the mapping persisted.
>
> Tried using udev's keymap tool on it, but the default get function
> doesn't give me any output.
>
> Am I supposed to implement get/setkeycode myself for this to work, or
> should the default input functions work? If the latter, is there
> anything missing in the driver for me to call to enable that?
>
> Trying to set a keycode says that the "EVIOCSKEYCODE" ioctl returns
> EINVAL.
>
As I said in an earlier e-mail:
> ... also set up input_dev->keycode, keycodemax and keycodesize so that
> keymap can be adjusted from userspace on per-device basis.
Thanks.
--
Dmitry
^ permalink raw reply [flat|nested] 57+ messages in thread
* Re: [PATCH] Input: add appleir USB driver
2010-04-19 7:28 ` Dmitry Torokhov
@ 2010-04-19 10:08 ` Bastien Nocera
2010-04-21 6:31 ` Dmitry Torokhov
0 siblings, 1 reply; 57+ messages in thread
From: Bastien Nocera @ 2010-04-19 10:08 UTC (permalink / raw)
To: Dmitry Torokhov; +Cc: linux-input, Jiri Kosina
On Mon, 2010-04-19 at 00:28 -0700, Dmitry Torokhov wrote:
> On Mon, Apr 19, 2010 at 01:31:49AM +0100, Bastien Nocera wrote:
> > On Sun, 2010-04-18 at 13:19 -0700, Dmitry Torokhov wrote:
> > <snip>
> > > > > Hmm, I am curious why suspend and resume is not necessary for this
> > > > > device... Are you relying on the USB core to tear down and re-create the
> > > > > device? Then you may lose user-applied settings (like changed keymap).
> > > >
> > > > How could I test that?
> > >
> > > Change the keymap (with keyfuzz or something similar), suspend. resume and see
> > > if the mapping persisted.
> >
> > Tried using udev's keymap tool on it, but the default get function
> > doesn't give me any output.
> >
> > Am I supposed to implement get/setkeycode myself for this to work, or
> > should the default input functions work? If the latter, is there
> > anything missing in the driver for me to call to enable that?
> >
> > Trying to set a keycode says that the "EVIOCSKEYCODE" ioctl returns
> > EINVAL.
> >
>
> As I said in an earlier e-mail:
>
> > ... also set up input_dev->keycode, keycodemax and keycodesize so that
> > keymap can be adjusted from userspace on per-device basis.
I missed that bit, sorry.
I tested udev's keymap, and the keymap isn't being saved across
suspend/resume cycles. I re-added the _suspend() and _resume() calls
(and their definitions in the usb_driver struct), but the keymap still
disappears.
Is there anything else I would need to do in this case to keep the
device (and its keymap) around during suspend?
I'm guessing that this problem wouldn't matter too much because udev
would re-apply any keymaps it had for the device when it reappears, and
in the lirc case, the keymap is in user-space.
Cheers
^ permalink raw reply [flat|nested] 57+ messages in thread
* Re: [PATCH] Input: add appleir USB driver
2010-04-19 10:08 ` Bastien Nocera
@ 2010-04-21 6:31 ` Dmitry Torokhov
2010-04-21 14:06 ` Bastien Nocera
0 siblings, 1 reply; 57+ messages in thread
From: Dmitry Torokhov @ 2010-04-21 6:31 UTC (permalink / raw)
To: Bastien Nocera; +Cc: linux-input, Jiri Kosina
On Mon, Apr 19, 2010 at 11:08:10AM +0100, Bastien Nocera wrote:
> On Mon, 2010-04-19 at 00:28 -0700, Dmitry Torokhov wrote:
> > On Mon, Apr 19, 2010 at 01:31:49AM +0100, Bastien Nocera wrote:
> > > On Sun, 2010-04-18 at 13:19 -0700, Dmitry Torokhov wrote:
> > > <snip>
> > > > > > Hmm, I am curious why suspend and resume is not necessary for this
> > > > > > device... Are you relying on the USB core to tear down and re-create the
> > > > > > device? Then you may lose user-applied settings (like changed keymap).
> > > > >
> > > > > How could I test that?
> > > >
> > > > Change the keymap (with keyfuzz or something similar), suspend. resume and see
> > > > if the mapping persisted.
> > >
> > > Tried using udev's keymap tool on it, but the default get function
> > > doesn't give me any output.
> > >
> > > Am I supposed to implement get/setkeycode myself for this to work, or
> > > should the default input functions work? If the latter, is there
> > > anything missing in the driver for me to call to enable that?
> > >
> > > Trying to set a keycode says that the "EVIOCSKEYCODE" ioctl returns
> > > EINVAL.
> > >
> >
> > As I said in an earlier e-mail:
> >
> > > ... also set up input_dev->keycode, keycodemax and keycodesize so that
> > > keymap can be adjusted from userspace on per-device basis.
>
> I missed that bit, sorry.
>
> I tested udev's keymap, and the keymap isn't being saved across
> suspend/resume cycles. I re-added the _suspend() and _resume() calls
> (and their definitions in the usb_driver struct), but the keymap still
> disappears.
Do you see another instance of the device appearing in dmesg?
>
> Is there anything else I would need to do in this case to keep the
> device (and its keymap) around during suspend?
>
> I'm guessing that this problem wouldn't matter too much because udev
> would re-apply any keymaps it had for the device when it reappears, and
> in the lirc case, the keymap is in user-space.
That assumes that user does use udev. Extremely likely, but still not certain.
--
Dmitry
^ permalink raw reply [flat|nested] 57+ messages in thread
* Re: [PATCH] Input: add appleir USB driver
2010-04-21 6:31 ` Dmitry Torokhov
@ 2010-04-21 14:06 ` Bastien Nocera
0 siblings, 0 replies; 57+ messages in thread
From: Bastien Nocera @ 2010-04-21 14:06 UTC (permalink / raw)
To: Dmitry Torokhov; +Cc: linux-input, Jiri Kosina
On Tue, 2010-04-20 at 23:31 -0700, Dmitry Torokhov wrote:
<snip>
> > I tested udev's keymap, and the keymap isn't being saved across
> > suspend/resume cycles. I re-added the _suspend() and _resume() calls
> > (and their definitions in the usb_driver struct), but the keymap still
> > disappears.
>
> Do you see another instance of the device appearing in dmesg?
The device was disappearing when a reset_resume wasn't present. I now
use resume for both reset_resume and resume, and force a URB on resume,
which should fix some cases of the device being dead on resume.
That also fixes the keymap disappearing on suspend.
Updated patch is now on the list
Cheers
^ permalink raw reply [flat|nested] 57+ messages in thread
* Re: [PATCH] Input: add appleir USB driver
2010-04-17 8:12 ` Dmitry Torokhov
2010-04-17 21:44 ` Bastien Nocera
@ 2010-04-19 9:22 ` Jiri Kosina
2010-04-19 9:31 ` Bastien Nocera
1 sibling, 1 reply; 57+ messages in thread
From: Jiri Kosina @ 2010-04-19 9:22 UTC (permalink / raw)
To: Dmitry Torokhov; +Cc: Bastien Nocera, linux-input
On Sat, 17 Apr 2010, Dmitry Torokhov wrote:
> > More recent versions of the IR receiver are also supported through
> > a patch by Alex Karpenko.
> >
> > Tested on a MacbookAir1,1
> >
>
> A few comments...
>
> > Signed-off-by: Bastien Nocera <hadess@hadess.net>
> > ---
> > Documentation/input/appleir.txt | 45 ++++
> > drivers/hid/hid-apple.c | 4 -
> > drivers/hid/hid-core.c | 5 +-
> > drivers/hid/hid-ids.h | 1 +
>
> HID pieces need to go through Jiri or he needs to ack them to go through
> my tree...
Please feel free to put
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
on the blakclist changes in drivers/hid once you merge the
standalone appleir driver from Bastien.
Thanks,
--
Jiri Kosina
SUSE Labs, Novell Inc.
^ permalink raw reply [flat|nested] 57+ messages in thread
* Re: [PATCH] Input: add appleir USB driver
2010-04-19 9:22 ` Jiri Kosina
@ 2010-04-19 9:31 ` Bastien Nocera
2010-04-19 10:00 ` Jiri Kosina
0 siblings, 1 reply; 57+ messages in thread
From: Bastien Nocera @ 2010-04-19 9:31 UTC (permalink / raw)
To: Jiri Kosina; +Cc: Dmitry Torokhov, linux-input
On Mon, 2010-04-19 at 11:22 +0200, Jiri Kosina wrote:
> On Sat, 17 Apr 2010, Dmitry Torokhov wrote:
>
> > > More recent versions of the IR receiver are also supported through
> > > a patch by Alex Karpenko.
> > >
> > > Tested on a MacbookAir1,1
> > >
> >
> > A few comments...
> >
> > > Signed-off-by: Bastien Nocera <hadess@hadess.net>
> > > ---
> > > Documentation/input/appleir.txt | 45 ++++
> > > drivers/hid/hid-apple.c | 4 -
> > > drivers/hid/hid-core.c | 5 +-
> > > drivers/hid/hid-ids.h | 1 +
> >
> > HID pieces need to go through Jiri or he needs to ack them to go through
> > my tree...
>
> Please feel free to put
>
> Signed-off-by: Jiri Kosina <jkosina@suse.cz>
>
> on the blakclist changes in drivers/hid once you merge the
> standalone appleir driver from Bastien.
Any comments on the additional quirks necessary to get lirc working?
^ permalink raw reply [flat|nested] 57+ messages in thread
* Re: [PATCH] Input: add appleir USB driver
2010-04-19 9:31 ` Bastien Nocera
@ 2010-04-19 10:00 ` Jiri Kosina
2010-04-19 10:14 ` Bastien Nocera
0 siblings, 1 reply; 57+ messages in thread
From: Jiri Kosina @ 2010-04-19 10:00 UTC (permalink / raw)
To: Bastien Nocera; +Cc: Dmitry Torokhov, linux-input
On Mon, 19 Apr 2010, Bastien Nocera wrote:
> > > A few comments...
> > >
> > > > Signed-off-by: Bastien Nocera <hadess@hadess.net>
> > > > ---
> > > > Documentation/input/appleir.txt | 45 ++++
> > > > drivers/hid/hid-apple.c | 4 -
> > > > drivers/hid/hid-core.c | 5 +-
> > > > drivers/hid/hid-ids.h | 1 +
> > >
> > > HID pieces need to go through Jiri or he needs to ack them to go through
> > > my tree...
> >
> > Please feel free to put
> >
> > Signed-off-by: Jiri Kosina <jkosina@suse.cz>
> >
> > on the blakclist changes in drivers/hid once you merge the
> > standalone appleir driver from Bastien.
>
> Any comments on the additional quirks necessary to get lirc working?
Ouch, I thought that we have discussed previously that I am basically fine
with adding those two quirks, but I haven't seen any patch doing that
since then ... ?
So I'd suggest you either remove that part from the appleir documentation
until this is implemented, or send me a patch adding those two flags.
Thanks,
--
Jiri Kosina
SUSE Labs, Novell Inc.
^ permalink raw reply [flat|nested] 57+ messages in thread
* Re: [PATCH] Input: add appleir USB driver
2010-04-19 10:00 ` Jiri Kosina
@ 2010-04-19 10:14 ` Bastien Nocera
2010-04-19 11:08 ` Jiri Kosina
0 siblings, 1 reply; 57+ messages in thread
From: Bastien Nocera @ 2010-04-19 10:14 UTC (permalink / raw)
To: Jiri Kosina; +Cc: Dmitry Torokhov, linux-input
On Mon, 2010-04-19 at 12:00 +0200, Jiri Kosina wrote:
> On Mon, 19 Apr 2010, Bastien Nocera wrote:
>
> > > > A few comments...
> > > >
> > > > > Signed-off-by: Bastien Nocera <hadess@hadess.net>
> > > > > ---
> > > > > Documentation/input/appleir.txt | 45 ++++
> > > > > drivers/hid/hid-apple.c | 4 -
> > > > > drivers/hid/hid-core.c | 5 +-
> > > > > drivers/hid/hid-ids.h | 1 +
> > > >
> > > > HID pieces need to go through Jiri or he needs to ack them to go through
> > > > my tree...
> > >
> > > Please feel free to put
> > >
> > > Signed-off-by: Jiri Kosina <jkosina@suse.cz>
> > >
> > > on the blakclist changes in drivers/hid once you merge the
> > > standalone appleir driver from Bastien.
> >
> > Any comments on the additional quirks necessary to get lirc working?
>
> Ouch, I thought that we have discussed previously that I am basically fine
> with adding those two quirks, but I haven't seen any patch doing that
> since then ... ?
>
> So I'd suggest you either remove that part from the appleir documentation
> until this is implemented, or send me a patch adding those two flags.
I sent it Friday with the subject:
[PATCH] Add HID_QUIRK_HIDDEV_FORCE and HID_QUIRK_NO_IGNORE
The patch was also sent to the input list:
http://thread.gmane.org/gmane.linux.kernel.input/12176
Cheers
^ permalink raw reply [flat|nested] 57+ messages in thread
* Re: [PATCH] Input: add appleir USB driver
2010-04-19 10:14 ` Bastien Nocera
@ 2010-04-19 11:08 ` Jiri Kosina
2010-04-21 20:09 ` Dmitry Torokhov
0 siblings, 1 reply; 57+ messages in thread
From: Jiri Kosina @ 2010-04-19 11:08 UTC (permalink / raw)
To: Bastien Nocera; +Cc: Dmitry Torokhov, linux-input
On Mon, 19 Apr 2010, Bastien Nocera wrote:
> > > > > > Signed-off-by: Bastien Nocera <hadess@hadess.net>
> > > > > > ---
> > > > > > Documentation/input/appleir.txt | 45 ++++
> > > > > > drivers/hid/hid-apple.c | 4 -
> > > > > > drivers/hid/hid-core.c | 5 +-
> > > > > > drivers/hid/hid-ids.h | 1 +
> > > > >
> > > > > HID pieces need to go through Jiri or he needs to ack them to go through
> > > > > my tree...
> > > >
> > > > Please feel free to put
> > > >
> > > > Signed-off-by: Jiri Kosina <jkosina@suse.cz>
> > > >
> > > > on the blakclist changes in drivers/hid once you merge the
> > > > standalone appleir driver from Bastien.
> > >
> > > Any comments on the additional quirks necessary to get lirc working?
> >
> > Ouch, I thought that we have discussed previously that I am basically fine
> > with adding those two quirks, but I haven't seen any patch doing that
> > since then ... ?
> >
> > So I'd suggest you either remove that part from the appleir documentation
> > until this is implemented, or send me a patch adding those two flags.
>
> I sent it Friday with the subject:
> [PATCH] Add HID_QUIRK_HIDDEV_FORCE and HID_QUIRK_NO_IGNORE
>
> The patch was also sent to the input list:
> http://thread.gmane.org/gmane.linux.kernel.input/12176
Ah, now I see it. It somehow didn't reach my inbox, though your other
mails do, odd.
Thanks, I will take it through my tree and queue it for next merge window
(if Dmitry is not going to push the driver even for current -rcs ... ?).
Thanks,
--
Jiri Kosina
SUSE Labs, Novell Inc.
^ permalink raw reply [flat|nested] 57+ messages in thread
* Re: [PATCH] Input: add appleir USB driver
2010-04-19 11:08 ` Jiri Kosina
@ 2010-04-21 20:09 ` Dmitry Torokhov
2010-09-03 16:58 ` Bastien Nocera
0 siblings, 1 reply; 57+ messages in thread
From: Dmitry Torokhov @ 2010-04-21 20:09 UTC (permalink / raw)
To: Jiri Kosina; +Cc: Bastien Nocera, linux-input
On Mon, Apr 19, 2010 at 01:08:57PM +0200, Jiri Kosina wrote:
> On Mon, 19 Apr 2010, Bastien Nocera wrote:
>
> > > > > > > Signed-off-by: Bastien Nocera <hadess@hadess.net>
> > > > > > > ---
> > > > > > > Documentation/input/appleir.txt | 45 ++++
> > > > > > > drivers/hid/hid-apple.c | 4 -
> > > > > > > drivers/hid/hid-core.c | 5 +-
> > > > > > > drivers/hid/hid-ids.h | 1 +
> > > > > >
> > > > > > HID pieces need to go through Jiri or he needs to ack them to go through
> > > > > > my tree...
> > > > >
> > > > > Please feel free to put
> > > > >
> > > > > Signed-off-by: Jiri Kosina <jkosina@suse.cz>
> > > > >
> > > > > on the blakclist changes in drivers/hid once you merge the
> > > > > standalone appleir driver from Bastien.
> > > >
> > > > Any comments on the additional quirks necessary to get lirc working?
> > >
> > > Ouch, I thought that we have discussed previously that I am basically fine
> > > with adding those two quirks, but I haven't seen any patch doing that
> > > since then ... ?
> > >
> > > So I'd suggest you either remove that part from the appleir documentation
> > > until this is implemented, or send me a patch adding those two flags.
> >
> > I sent it Friday with the subject:
> > [PATCH] Add HID_QUIRK_HIDDEV_FORCE and HID_QUIRK_NO_IGNORE
> >
> > The patch was also sent to the input list:
> > http://thread.gmane.org/gmane.linux.kernel.input/12176
>
> Ah, now I see it. It somehow didn't reach my inbox, though your other
> mails do, odd.
>
> Thanks, I will take it through my tree and queue it for next merge window
> (if Dmitry is not going to push the driver even for current -rcs ... ?).
>
No, even though it is a new driver it is gettign kind of late. I am
going to put it in my 'next' branch.
--
Dmitry
^ permalink raw reply [flat|nested] 57+ messages in thread
* Re: [PATCH] Input: add appleir USB driver
2010-04-21 20:09 ` Dmitry Torokhov
@ 2010-09-03 16:58 ` Bastien Nocera
0 siblings, 0 replies; 57+ messages in thread
From: Bastien Nocera @ 2010-09-03 16:58 UTC (permalink / raw)
To: Dmitry Torokhov; +Cc: Jiri Kosina, linux-input
On Wed, 2010-04-21 at 13:09 -0700, Dmitry Torokhov wrote:
> On Mon, Apr 19, 2010 at 01:08:57PM +0200, Jiri Kosina wrote:
> > On Mon, 19 Apr 2010, Bastien Nocera wrote:
> >
> > > > > > > > Signed-off-by: Bastien Nocera <hadess@hadess.net>
> > > > > > > > ---
> > > > > > > > Documentation/input/appleir.txt | 45 ++++
> > > > > > > > drivers/hid/hid-apple.c | 4 -
> > > > > > > > drivers/hid/hid-core.c | 5 +-
> > > > > > > > drivers/hid/hid-ids.h | 1 +
> > > > > > >
> > > > > > > HID pieces need to go through Jiri or he needs to ack them to go through
> > > > > > > my tree...
> > > > > >
> > > > > > Please feel free to put
> > > > > >
> > > > > > Signed-off-by: Jiri Kosina <jkosina@suse.cz>
> > > > > >
> > > > > > on the blakclist changes in drivers/hid once you merge the
> > > > > > standalone appleir driver from Bastien.
> > > > >
> > > > > Any comments on the additional quirks necessary to get lirc working?
> > > >
> > > > Ouch, I thought that we have discussed previously that I am basically fine
> > > > with adding those two quirks, but I haven't seen any patch doing that
> > > > since then ... ?
> > > >
> > > > So I'd suggest you either remove that part from the appleir documentation
> > > > until this is implemented, or send me a patch adding those two flags.
> > >
> > > I sent it Friday with the subject:
> > > [PATCH] Add HID_QUIRK_HIDDEV_FORCE and HID_QUIRK_NO_IGNORE
> > >
> > > The patch was also sent to the input list:
> > > http://thread.gmane.org/gmane.linux.kernel.input/12176
> >
> > Ah, now I see it. It somehow didn't reach my inbox, though your other
> > mails do, odd.
> >
> > Thanks, I will take it through my tree and queue it for next merge window
> > (if Dmitry is not going to push the driver even for current -rcs ... ?).
> >
>
> No, even though it is a new driver it is gettign kind of late. I am
> going to put it in my 'next' branch.
Could I please have this patch upstream somewhere?
The blocking patch has been merged for a long while now.
Cheers
^ permalink raw reply [flat|nested] 57+ messages in thread
* [PATCH] Input: add appleir USB driver
@ 2010-02-08 16:32 Bastien Nocera
0 siblings, 0 replies; 57+ messages in thread
From: Bastien Nocera @ 2010-02-08 16:32 UTC (permalink / raw)
To: Jiri Kosina; +Cc: linux-input, Dmitry Torokhov
This driver was originally written by James McKenzie, updated by
Greg Kroah-Hartman, further updated by myself, with suspend support
added.
More recent versions of the IR receiver are also supported through
a patch by Alex Karpenko.
Tested on a MacbookAir1,1
Signed-off-by: Bastien Nocera <hadess@hadess.net>
---
Documentation/input/appleir.txt | 45 ++++
drivers/hid/hid-apple.c | 4 -
drivers/hid/hid-core.c | 5 +-
drivers/hid/hid-ids.h | 1 +
drivers/input/misc/Kconfig | 13 +
drivers/input/misc/Makefile | 1 +
drivers/input/misc/appleir.c | 477 +++++++++++++++++++++++++++++++++++++++
7 files changed, 540 insertions(+), 6 deletions(-)
create mode 100644 Documentation/input/appleir.txt
create mode 100644 drivers/input/misc/appleir.c
diff --git a/Documentation/input/appleir.txt b/Documentation/input/appleir.txt
new file mode 100644
index 0000000..21e0d60
--- /dev/null
+++ b/Documentation/input/appleir.txt
@@ -0,0 +1,45 @@
+Apple IR receiver Driver (appleir)
+----------------------------------
+ Copyright (C) 2009 Bastien Nocera <hadess@hadess.net>
+
+The appleir driver is a kernel input driver to handle Apple's IR
+receivers (and associated remotes) in the kernel.
+
+The driver is an input driver which only handles "official" remotes
+as built and sold by Apple.
+
+Authors
+-------
+
+James McKenzie (original driver)
+Alex Karpenko (05ac:8242 support)
+Greg Kroah-Hartman (cleanups and original submission)
+Bastien Nocera (further cleanups and suspend support)
+
+Supported hardware
+------------------
+
+- All Apple laptops and desktops from 2005 onwards, except:
+ - the unibody Macbook (2009)
+ - Mac Pro (all versions)
+- Apple TV (all revisions)
+
+The remote will only support the 6 buttons of the original remotes
+as sold by Apple. See the next section if you want to use other remotes
+or want to use lirc with the device instead of the kernel driver.
+
+Using lirc (native) instead of the kernel driver
+------------------------------------------------
+
+First, you will need to disable the kernel driver for the receiver.
+
+This can be achieved by passing quirks to the usbhid driver.
+The quirk line would be:
+usbhid.quirks=0x05ac:0x8242:0x08
+
+With 0x05ac being the vendor ID (Apple, you shouldn't need to change this)
+With 0x8242 being the product ID (check the output of lsusb for your hardware)
+And 0x08 being "HID_CONNECT_HIDDEV"
+
+This should force the creation of a hiddev device for the receiver, and
+make it usable under lirc.
diff --git a/drivers/hid/hid-apple.c b/drivers/hid/hid-apple.c
index 4b96e7a..d1fdcd0 100644
--- a/drivers/hid/hid-apple.c
+++ b/drivers/hid/hid-apple.c
@@ -353,10 +353,6 @@ static void apple_remove(struct hid_device *hdev)
}
static const struct hid_device_id apple_devices[] = {
- { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ATV_IRCONTROL),
- .driver_data = APPLE_HIDDEV | APPLE_IGNORE_HIDINPUT },
- { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL4),
- .driver_data = APPLE_HIDDEV | APPLE_IGNORE_HIDINPUT },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MIGHTYMOUSE),
.driver_data = APPLE_MIGHTYMOUSE | APPLE_INVERT_HWHEEL },
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index 80792d3..61b199e 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -1250,8 +1250,6 @@ EXPORT_SYMBOL_GPL(hid_disconnect);
static const struct hid_device_id hid_blacklist[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_WCP32PU) },
{ HID_USB_DEVICE(USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_X5_005D) },
- { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ATV_IRCONTROL) },
- { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL4) },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MIGHTYMOUSE) },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_ANSI) },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_ISO) },
@@ -1540,6 +1538,9 @@ static const struct hid_device_id hid_ignore_list[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_24) },
{ HID_USB_DEVICE(USB_VENDOR_ID_AIRCABLE, USB_DEVICE_ID_AIRCABLE1) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ALCOR, USB_DEVICE_ID_ALCOR_USBRS232) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ATV_IRCONTROL) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL4) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ASUS, USB_DEVICE_ID_ASUS_LCM)},
{ HID_USB_DEVICE(USB_VENDOR_ID_ASUS, USB_DEVICE_ID_ASUS_LCM2)},
{ HID_USB_DEVICE(USB_VENDOR_ID_AVERMEDIA, USB_DEVICE_ID_AVER_FM_MR800) },
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
index 3839340..8b29a88 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -90,6 +90,7 @@
#define USB_DEVICE_ID_APPLE_WELLSPRING3_JIS 0x0238
#define USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY 0x030a
#define USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY 0x030b
+#define USB_DEVICE_ID_APPLE_IRCONTROL 0x8240
#define USB_DEVICE_ID_APPLE_ATV_IRCONTROL 0x8241
#define USB_DEVICE_ID_APPLE_IRCONTROL4 0x8242
diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
index 16ec523..4340986 100644
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -149,6 +149,19 @@ config INPUT_KEYSPAN_REMOTE
To compile this driver as a module, choose M here: the module will
be called keyspan_remote.
+config INPUT_APPLEIR
+ tristate "Apple infrared receiver (built in)"
+ depends on USB_ARCH_HAS_HCD
+ select USB
+ help
+ Say Y here if you want to use a Apple infrared remote control. All
+ the Apple computers from 2005 onwards include such a port, except
+ the unibody Macbook (2009), and Mac Pros. This receiver is also
+ used in the Apple TV set-top box.
+
+ To compile this driver as a module, choose M here: the module will
+ be called appleir.
+
config INPUT_POWERMATE
tristate "Griffin PowerMate and Contour Jog support"
depends on USB_ARCH_HAS_HCD
diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
index a8b8485..041e6f5 100644
--- a/drivers/input/misc/Makefile
+++ b/drivers/input/misc/Makefile
@@ -5,6 +5,7 @@
# Each configuration option enables a list of files.
obj-$(CONFIG_INPUT_APANEL) += apanel.o
+obj-$(CONFIG_INPUT_APPLEIR) += appleir.o
obj-$(CONFIG_INPUT_ATI_REMOTE) += ati_remote.o
obj-$(CONFIG_INPUT_ATI_REMOTE2) += ati_remote2.o
obj-$(CONFIG_INPUT_ATLAS_BTNS) += atlas_btns.o
diff --git a/drivers/input/misc/appleir.c b/drivers/input/misc/appleir.c
new file mode 100644
index 0000000..138f4c8
--- /dev/null
+++ b/drivers/input/misc/appleir.c
@@ -0,0 +1,477 @@
+/*
+ * appleir: USB driver for the apple ir device
+ *
+ * Original driver written by James McKenzie
+ * Ported to recent 2.6 kernel versions by Greg Kroah-Hartman <gregkh@suse.de>
+ *
+ * Copyright (C) 2006 James McKenzie
+ * Copyright (C) 2008 Greg Kroah-Hartman <greg@kroah.com>
+ * Copyright (C) 2008 Novell Inc.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/usb/input.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/usb.h>
+#include <linux/usb/input.h>
+#include <asm/unaligned.h>
+#include <asm/byteorder.h>
+
+#define DRIVER_VERSION "v1.2"
+#define DRIVER_AUTHOR "James McKenzie"
+#define DRIVER_DESC "Apple infrared receiver driver"
+#define DRIVER_LICENSE "GPL"
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE(DRIVER_LICENSE);
+
+#define USB_VENDOR_ID_APPLE 0x05ac
+#define USB_DEVICE_ID_APPLE_IRCONTROL 0x8240
+#define USB_DEVICE_ID_APPLE_ATV_IRCONTROL 0x8241
+#define USB_DEVICE_ID_APPLE_IRCONTROL4 0x8242
+
+#define URB_SIZE 32
+
+#define MAX_KEYS 8
+#define MAX_KEYS_MASK (MAX_KEYS - 1)
+
+#define dbginfo(dev, format, arg...) do { if (debug) dev_info(dev , format , ## arg); } while (0)
+
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Enable extra debug messages and information");
+
+struct appleir {
+ struct input_dev *input_dev;
+ u8 *data;
+ dma_addr_t dma_buf;
+ struct usb_device *usbdev;
+ unsigned int flags;
+ struct urb *urb;
+ int timer_initted;
+ struct timer_list key_up_timer;
+ int current_key;
+ char phys[32];
+};
+
+static DEFINE_MUTEX(appleir_mutex);
+
+enum {
+ APPLEIR_OPENED = 0x1,
+ APPLEIR_SUSPENDED = 0x2,
+};
+
+static struct usb_device_id appleir_ids[] = {
+ { USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL) },
+ { USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ATV_IRCONTROL) },
+ { USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL4) },
+ {}
+};
+MODULE_DEVICE_TABLE(usb, appleir_ids);
+
+/* I have two devices both of which report the following */
+/* 25 87 ee 83 0a + */
+/* 25 87 ee 83 0c - */
+/* 25 87 ee 83 09 << */
+/* 25 87 ee 83 06 >> */
+/* 25 87 ee 83 05 >" */
+/* 25 87 ee 83 03 menu */
+/* 26 00 00 00 00 for key repeat*/
+
+/* Thomas Glanzmann reports the following responses */
+/* 25 87 ee ca 0b + */
+/* 25 87 ee ca 0d - */
+/* 25 87 ee ca 08 << */
+/* 25 87 ee ca 07 >> */
+/* 25 87 ee ca 04 >" */
+/* 25 87 ee ca 02 menu */
+/* 26 00 00 00 00 for key repeat*/
+/* He also observes the following event sometimes */
+/* sent after a key is release, which I interpret */
+/* as a flat battery message */
+/* 25 87 e0 ca 06 flat battery */
+
+/* Alexandre Karpenko reports the following responses for Device ID 0x8242 */
+/* 25 87 ee 47 0b + */
+/* 25 87 ee 47 0d - */
+/* 25 87 ee 47 08 << */
+/* 25 87 ee 47 07 >> */
+/* 25 87 ee 47 04 >" */
+/* 25 87 ee 47 02 menu */
+/* 26 87 ee 47 ** for key repeat (** is the code of the key being held) */
+
+static int keymap[MAX_KEYS] = {
+ KEY_RESERVED,
+ KEY_MENU,
+ KEY_PLAYPAUSE,
+ KEY_FORWARD,
+ KEY_BACK,
+ KEY_VOLUMEUP,
+ KEY_VOLUMEDOWN,
+ KEY_RESERVED,
+};
+
+static void dump_packet(struct appleir *appleir, char *msg, u8 *data, int len)
+{
+ int i;
+
+ printk(KERN_ERR "appleir: %s (%d bytes)", msg, len);
+
+ for (i = 0; i < len; ++i)
+ printk(" %02x", data[i]);
+ printk("\n");
+}
+
+static void key_up(struct appleir *appleir, int key)
+{
+ dbginfo (&appleir->input_dev->dev, "key %d up\n", key);
+ input_report_key(appleir->input_dev, key, 0);
+ input_sync(appleir->input_dev);
+}
+
+static void key_down(struct appleir *appleir, int key)
+{
+ dbginfo (&appleir->input_dev->dev, "key %d down\n", key);
+ input_report_key(appleir->input_dev, key, 1);
+ input_sync(appleir->input_dev);
+}
+
+static void battery_flat(struct appleir *appleir)
+{
+ dev_err(&appleir->input_dev->dev, "possible flat battery?\n");
+}
+
+static void key_up_tick(unsigned long data)
+{
+ struct appleir *appleir = (struct appleir *)data;
+
+ if (appleir->current_key) {
+ key_up(appleir, appleir->current_key);
+ appleir->current_key = 0;
+ }
+}
+
+static void new_data(struct appleir *appleir, u8 *data, int len)
+{
+ static const u8 keydown[] = { 0x25, 0x87, 0xee };
+ static const u8 keyrepeat[] = { 0x26, };
+ static const u8 flatbattery[] = { 0x25, 0x87, 0xe0 };
+
+ if (debug)
+ dump_packet(appleir, "received", data, len);
+
+ if (len != 5)
+ return;
+
+ if (!memcmp(data, keydown, sizeof(keydown))) {
+ /* If we already have a key down, take it up before marking
+ this one down */
+ if (appleir->current_key)
+ key_up(appleir, appleir->current_key);
+ appleir->current_key = keymap[(data[4] >> 1) & MAX_KEYS_MASK];
+
+ key_down(appleir, appleir->current_key);
+ /* Remote doesn't do key up, either pull them up, in the test
+ above, or here set a timer which pulls them up after 1/8 s */
+ mod_timer(&appleir->key_up_timer, jiffies + HZ / 8);
+
+ return;
+ }
+
+ if (!memcmp(data, keyrepeat, sizeof(keyrepeat))) {
+ key_down(appleir, appleir->current_key);
+ /* Remote doesn't do key up, either pull them up, in the test
+ above, or here set a timer which pulls them up after 1/8 s */
+ mod_timer(&appleir->key_up_timer, jiffies + HZ / 8);
+ return;
+ }
+
+ if (!memcmp(data, flatbattery, sizeof(flatbattery))) {
+ battery_flat(appleir);
+ /* Fall through */
+ }
+
+ dump_packet(appleir, "unknown packet", data, len);
+}
+
+static void appleir_urb(struct urb *urb)
+{
+ struct appleir *appleir = urb->context;
+ int status = urb->status;
+ int retval;
+
+ switch (status) {
+ case 0:
+ new_data(appleir, urb->transfer_buffer, urb->actual_length);
+ break;
+ case -ECONNRESET:
+ case -ENOENT:
+ case -ESHUTDOWN:
+ /* This urb is terminated, clean up */
+ dbginfo(&appleir->input_dev->dev, "%s - urb shutting down with status: %d", __func__,
+ urb->status);
+ return;
+ default:
+ dbginfo(&appleir->input_dev->dev, "%s - nonzero urb status received: %d", __func__,
+ urb->status);
+ }
+
+ retval = usb_submit_urb(urb, GFP_ATOMIC);
+ if (retval)
+ err("%s - usb_submit_urb failed with result %d", __func__,
+ retval);
+}
+
+static int appleir_open(struct input_dev *dev)
+{
+ struct appleir *appleir = input_get_drvdata(dev);
+ struct usb_interface *intf = usb_ifnum_to_if(appleir->usbdev, 0);
+ int r;
+
+ r = usb_autopm_get_interface(intf);
+ if (r) {
+ dev_err(&intf->dev,
+ "%s(): usb_autopm_get_interface() = %d\n", __func__, r);
+ return r;
+ }
+
+ mutex_lock(&appleir_mutex);
+
+ if (usb_submit_urb(appleir->urb, GFP_KERNEL)) {
+ r = -EIO;
+ goto fail;
+ }
+
+ appleir->flags |= APPLEIR_OPENED;
+
+ mutex_unlock(&appleir_mutex);
+
+ usb_autopm_put_interface(intf);
+
+ return 0;
+fail:
+ mutex_unlock(&appleir_mutex);
+ usb_autopm_put_interface(intf);
+ return r;
+}
+
+static void appleir_close(struct input_dev *dev)
+{
+ struct appleir *appleir = input_get_drvdata(dev);
+
+ mutex_lock(&appleir_mutex);
+
+ if (!(appleir->flags & APPLEIR_SUSPENDED)) {
+ usb_kill_urb(appleir->urb);
+ del_timer_sync(&appleir->key_up_timer);
+ }
+
+ appleir->flags &= ~APPLEIR_OPENED;
+
+ mutex_unlock(&appleir_mutex);
+}
+
+static int appleir_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ struct usb_device *dev = interface_to_usbdev(intf);
+ struct usb_endpoint_descriptor *endpoint;
+ struct appleir *appleir = NULL;
+ struct input_dev *input_dev;
+ int retval = -ENOMEM;
+ int i;
+
+ appleir = kzalloc(sizeof(struct appleir), GFP_KERNEL);
+ if (!appleir)
+ goto fail;
+
+ appleir->data = usb_buffer_alloc(dev, URB_SIZE, GFP_KERNEL,
+ &appleir->dma_buf);
+ if (!appleir->data)
+ goto fail;
+
+ appleir->urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!appleir->urb)
+ goto fail;
+
+ appleir->usbdev = dev;
+
+ input_dev = input_allocate_device();
+ if (!input_dev)
+ goto fail;
+
+ appleir->input_dev = input_dev;
+
+ usb_make_path(dev, appleir->phys, sizeof(appleir->phys));
+ strlcpy(appleir->phys, "/input0", sizeof(appleir->phys));
+
+ input_dev->name = "Apple infrared remote control driver";
+ input_dev->phys = appleir->phys;
+ usb_to_input_id(dev, &input_dev->id);
+ input_dev->dev.parent = &intf->dev;
+ input_set_drvdata(input_dev, appleir);
+
+ input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP);
+ input_dev->ledbit[0] = 0;
+
+ for (i = 0; i < MAX_KEYS; i++)
+ set_bit(keymap[i], input_dev->keybit);
+
+ clear_bit(0, input_dev->keybit);
+
+ input_dev->open = appleir_open;
+ input_dev->close = appleir_close;
+
+ endpoint = &intf->cur_altsetting->endpoint[0].desc;
+
+ usb_fill_int_urb(appleir->urb, dev,
+ usb_rcvintpipe(dev, endpoint->bEndpointAddress),
+ appleir->data, 8,
+ appleir_urb, appleir, endpoint->bInterval);
+
+ appleir->urb->transfer_dma = appleir->dma_buf;
+ appleir->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+ usb_set_intfdata(intf, appleir);
+
+ init_timer(&appleir->key_up_timer);
+
+ appleir->key_up_timer.function = key_up_tick;
+ appleir->key_up_timer.data = (unsigned long)appleir;
+
+ appleir->timer_initted++;
+
+ retval = input_register_device(appleir->input_dev);
+ if (retval)
+ goto fail;
+
+ return 0;
+
+fail:
+ printk(KERN_WARNING "Failed to load appleir\n");
+ if (appleir) {
+ if (appleir->data)
+ usb_buffer_free(dev, URB_SIZE, appleir->data,
+ appleir->dma_buf);
+
+ if (appleir->timer_initted)
+ del_timer_sync(&appleir->key_up_timer);
+
+ if (appleir->input_dev)
+ input_free_device(appleir->input_dev);
+
+ kfree(appleir);
+ }
+
+ return retval;
+}
+
+static void appleir_disconnect(struct usb_interface *intf)
+{
+ struct appleir *appleir = usb_get_intfdata(intf);
+
+ usb_set_intfdata(intf, NULL);
+ if (appleir) {
+ input_unregister_device(appleir->input_dev);
+ if (appleir->timer_initted)
+ del_timer_sync(&appleir->key_up_timer);
+ usb_kill_urb(appleir->urb);
+ usb_free_urb(appleir->urb);
+ usb_buffer_free(interface_to_usbdev(intf), URB_SIZE,
+ appleir->data, appleir->dma_buf);
+ kfree(appleir);
+ }
+}
+
+static int appleir_suspend(struct usb_interface *interface,
+ pm_message_t message)
+{
+ struct appleir *appleir;
+
+ appleir = usb_get_intfdata(interface);
+
+ mutex_lock(&appleir_mutex);
+
+ if (appleir->flags & APPLEIR_OPENED) {
+ usb_kill_urb(appleir->urb);
+ del_timer_sync(&appleir->key_up_timer);
+ }
+
+ appleir->flags |= APPLEIR_SUSPENDED;
+
+ mutex_unlock(&appleir_mutex);
+
+ return 0;
+}
+
+static int appleir_resume(struct usb_interface *interface)
+{
+ struct appleir *appleir;
+
+ appleir = usb_get_intfdata(interface);
+
+ mutex_lock(&appleir_mutex);
+
+ if (appleir->flags & APPLEIR_OPENED) {
+ struct usb_endpoint_descriptor *endpoint;
+
+ endpoint = &interface->cur_altsetting->endpoint[0].desc;
+ usb_fill_int_urb(appleir->urb, appleir->usbdev,
+ usb_rcvintpipe(appleir->usbdev, endpoint->bEndpointAddress),
+ appleir->data, 8,
+ appleir_urb, appleir, endpoint->bInterval);
+ appleir->urb->transfer_dma = appleir->dma_buf;
+ appleir->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+ init_timer(&appleir->key_up_timer);
+
+ appleir->key_up_timer.function = key_up_tick;
+ appleir->key_up_timer.data = (unsigned long)appleir;
+ }
+
+ appleir->flags &= ~APPLEIR_SUSPENDED;
+
+ mutex_unlock(&appleir_mutex);
+
+ return 0;
+}
+
+static struct usb_driver appleir_driver = {
+ .name = "appleir",
+ .probe = appleir_probe,
+ .disconnect = appleir_disconnect,
+ .suspend = appleir_suspend,
+ .resume = appleir_resume,
+ .reset_resume = NULL,
+ .id_table = appleir_ids,
+ .supports_autosuspend = 1,
+};
+
+static int __init appleir_init(void)
+{
+ int retval;
+
+ retval = usb_register(&appleir_driver);
+ if (retval)
+ goto out;
+ printk(KERN_INFO DRIVER_VERSION ":" DRIVER_DESC);
+out:
+ return retval;
+}
+
+static void __exit appleir_exit(void)
+{
+ usb_deregister(&appleir_driver);
+}
+
+module_init(appleir_init);
+module_exit(appleir_exit);
--
1.6.6
^ permalink raw reply related [flat|nested] 57+ messages in thread
* [PATCH] Input: add appleir USB driver
@ 2010-01-20 14:17 Bastien Nocera
2010-01-27 15:40 ` Jiri Kosina
0 siblings, 1 reply; 57+ messages in thread
From: Bastien Nocera @ 2010-01-20 14:17 UTC (permalink / raw)
To: linux-input, Dmitry Torokhov, Jiri Kosina
From: Bastien Nocera <hadess@hadess.net>
This driver was originally written by James McKenzie, updated by
Greg Kroah-Hartman, further updated by myself, with suspend support
added.
More recent versions of the IR receiver are also supported through
a patch by Alex Karpenko.
Tested on a MacbookAir1,1
Signed-off-by: Bastien Nocera <hadess@hadess.net>
---
Documentation/input/appleir.txt | 46 ++++
drivers/hid/hid-apple.c | 4 -
drivers/hid/hid-core.c | 5 +-
drivers/hid/hid-ids.h | 1 +
drivers/input/misc/Kconfig | 13 +
drivers/input/misc/Makefile | 1 +
drivers/input/misc/appleir.c | 477 +++++++++++++++++++++++++++++++++++++++
7 files changed, 541 insertions(+), 6 deletions(-)
create mode 100644 Documentation/input/appleir.txt
create mode 100644 drivers/input/misc/appleir.c
diff --git a/Documentation/input/appleir.txt b/Documentation/input/appleir.txt
new file mode 100644
index 0000000..0aaf5fe
--- /dev/null
+++ b/Documentation/input/appleir.txt
@@ -0,0 +1,46 @@
+Apple IR receiver Driver (appleir)
+----------------------------------
+ Copyright (C) 2009 Bastien Nocera <hadess@hadess.net>
+
+The appleir driver is a kernel input driver to handle Apple's IR
+receivers (and associated remotes) in the kernel.
+
+The driver is an input driver which only handles "official" remotes
+as built and sold by Apple.
+
+Authors
+-------
+
+James McKenzie (original driver)
+Alex Karpenko (05ac:8242 support)
+Greg Kroah-Hartman (cleanups and original submission)
+Bastien Nocera (further cleanups and suspend support)
+
+Supported hardware
+------------------
+
+- All Apple laptops and desktops from 2005 onwards, except:
+ - the unibody Macbook (2009)
+ - Mac Pro (all versions)
+- Apple TV (all revisions)
+
+The remote will only support the 6 buttons of the original remotes
+as sold by Apple. See the next section if you want to use other remotes
+or want to use lirc with the device instead of the kernel driver.
+
+Using lirc (native) instead of the kernel driver
+------------------------------------------------
+
+First, you will need to disable the kernel driver for the receiver.
+
+This can be achieved by passing quirks to the usbhid driver.
+The quirk line would be:
+usbhid.quirks=0x05ac:0x8242:0x08
+
+With 0x05ac being the vendor ID (Apple, you shouldn't need to change this)
+With 0x8242 being the product ID (check the output of lsusb for your hardware)
+And 0x08 being "HID_CONNECT_HIDDEV"
+
+This should force the creation of a hiddev device for the receiver, and
+make it usable under lirc.
+
diff --git a/drivers/hid/hid-apple.c b/drivers/hid/hid-apple.c
index 4b96e7a..d1fdcd0 100644
--- a/drivers/hid/hid-apple.c
+++ b/drivers/hid/hid-apple.c
@@ -353,10 +353,6 @@ static void apple_remove(struct hid_device *hdev)
}
static const struct hid_device_id apple_devices[] = {
- { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ATV_IRCONTROL),
- .driver_data = APPLE_HIDDEV | APPLE_IGNORE_HIDINPUT },
- { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL4),
- .driver_data = APPLE_HIDDEV | APPLE_IGNORE_HIDINPUT },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MIGHTYMOUSE),
.driver_data = APPLE_MIGHTYMOUSE | APPLE_INVERT_HWHEEL },
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index 80792d3..61b199e 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -1250,8 +1250,6 @@ EXPORT_SYMBOL_GPL(hid_disconnect);
static const struct hid_device_id hid_blacklist[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_WCP32PU) },
{ HID_USB_DEVICE(USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_X5_005D) },
- { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ATV_IRCONTROL) },
- { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL4) },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MIGHTYMOUSE) },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_ANSI) },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_ISO) },
@@ -1540,6 +1538,9 @@ static const struct hid_device_id hid_ignore_list[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_24) },
{ HID_USB_DEVICE(USB_VENDOR_ID_AIRCABLE, USB_DEVICE_ID_AIRCABLE1) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ALCOR, USB_DEVICE_ID_ALCOR_USBRS232) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ATV_IRCONTROL) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL4) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ASUS, USB_DEVICE_ID_ASUS_LCM)},
{ HID_USB_DEVICE(USB_VENDOR_ID_ASUS, USB_DEVICE_ID_ASUS_LCM2)},
{ HID_USB_DEVICE(USB_VENDOR_ID_AVERMEDIA, USB_DEVICE_ID_AVER_FM_MR800) },
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
index 3839340..8b29a88 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -90,6 +90,7 @@
#define USB_DEVICE_ID_APPLE_WELLSPRING3_JIS 0x0238
#define USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY 0x030a
#define USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY 0x030b
+#define USB_DEVICE_ID_APPLE_IRCONTROL 0x8240
#define USB_DEVICE_ID_APPLE_ATV_IRCONTROL 0x8241
#define USB_DEVICE_ID_APPLE_IRCONTROL4 0x8242
diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
index 16ec523..4340986 100644
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -149,6 +149,19 @@ config INPUT_KEYSPAN_REMOTE
To compile this driver as a module, choose M here: the module will
be called keyspan_remote.
+config INPUT_APPLEIR
+ tristate "Apple infrared receiver (built in)"
+ depends on USB_ARCH_HAS_HCD
+ select USB
+ help
+ Say Y here if you want to use a Apple infrared remote control. All
+ the Apple computers from 2005 onwards include such a port, except
+ the unibody Macbook (2009), and Mac Pros. This receiver is also
+ used in the Apple TV set-top box.
+
+ To compile this driver as a module, choose M here: the module will
+ be called appleir.
+
config INPUT_POWERMATE
tristate "Griffin PowerMate and Contour Jog support"
depends on USB_ARCH_HAS_HCD
diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
index a8b8485..041e6f5 100644
--- a/drivers/input/misc/Makefile
+++ b/drivers/input/misc/Makefile
@@ -5,6 +5,7 @@
# Each configuration option enables a list of files.
obj-$(CONFIG_INPUT_APANEL) += apanel.o
+obj-$(CONFIG_INPUT_APPLEIR) += appleir.o
obj-$(CONFIG_INPUT_ATI_REMOTE) += ati_remote.o
obj-$(CONFIG_INPUT_ATI_REMOTE2) += ati_remote2.o
obj-$(CONFIG_INPUT_ATLAS_BTNS) += atlas_btns.o
diff --git a/drivers/input/misc/appleir.c b/drivers/input/misc/appleir.c
new file mode 100644
index 0000000..6e332ab
--- /dev/null
+++ b/drivers/input/misc/appleir.c
@@ -0,0 +1,477 @@
+/*
+ * appleir: USB driver for the apple ir device
+ *
+ * Original driver written by James McKenzie
+ * Ported to recent 2.6 kernel versions by Greg Kroah-Hartman <gregkh@suse.de>
+ *
+ * Copyright (C) 2006 James McKenzie
+ * Copyright (C) 2008 Greg Kroah-Hartman <greg@kroah.com>
+ * Copyright (C) 2008 Novell Inc.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/usb/input.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/usb.h>
+#include <linux/usb/input.h>
+#include <asm/unaligned.h>
+#include <asm/byteorder.h>
+
+#define DRIVER_VERSION "v1.2"
+#define DRIVER_AUTHOR "James McKenzie"
+#define DRIVER_DESC "Apple infrared receiver driver"
+#define DRIVER_LICENSE "GPL"
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE(DRIVER_LICENSE);
+
+#define USB_VENDOR_ID_APPLE 0x05ac
+#define USB_DEVICE_ID_APPLE_IRCONTROL 0x8240
+#define USB_DEVICE_ID_APPLE_ATV_IRCONTROL 0x8241
+#define USB_DEVICE_ID_APPLE_IRCONTROL4 0x8242
+
+#define URB_SIZE 32
+
+#define MAX_KEYS 8
+#define MAX_KEYS_MASK (MAX_KEYS - 1)
+
+#define dbginfo(dev, format, arg...) do { if (debug) dev_info(dev , format , ## arg); } while (0)
+
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Enable extra debug messages and information");
+
+struct appleir {
+ struct input_dev *input_dev;
+ u8 *data;
+ dma_addr_t dma_buf;
+ struct usb_device *usbdev;
+ unsigned int flags;
+ struct urb *urb;
+ int timer_initted;
+ struct timer_list key_up_timer;
+ int current_key;
+ char phys[32];
+};
+
+static DEFINE_MUTEX(appleir_mutex);
+
+enum {
+ APPLEIR_OPENED = 0x1,
+ APPLEIR_SUSPENDED = 0x2,
+};
+
+static struct usb_device_id appleir_ids[] = {
+ { USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL) },
+ { USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ATV_IRCONTROL) },
+ { USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL4) },
+ {}
+};
+MODULE_DEVICE_TABLE(usb, appleir_ids);
+
+/* I have two devices both of which report the following */
+/* 25 87 ee 83 0a + */
+/* 25 87 ee 83 0c - */
+/* 25 87 ee 83 09 << */
+/* 25 87 ee 83 06 >> */
+/* 25 87 ee 83 05 >" */
+/* 25 87 ee 83 03 menu */
+/* 26 00 00 00 00 for key repeat*/
+
+/* Thomas Glanzmann reports the following responses */
+/* 25 87 ee ca 0b + */
+/* 25 87 ee ca 0d - */
+/* 25 87 ee ca 08 << */
+/* 25 87 ee ca 07 >> */
+/* 25 87 ee ca 04 >" */
+/* 25 87 ee ca 02 menu */
+/* 26 00 00 00 00 for key repeat*/
+/* He also observes the following event sometimes */
+/* sent after a key is release, which I interpret */
+/* as a flat battery message */
+/* 25 87 e0 ca 06 flat battery */
+
+/* Alexandre Karpenko reports the following responses for Device ID 0x8242 */
+/* 25 87 ee 47 0b + */
+/* 25 87 ee 47 0d - */
+/* 25 87 ee 47 08 << */
+/* 25 87 ee 47 07 >> */
+/* 25 87 ee 47 04 >" */
+/* 25 87 ee 47 02 menu */
+/* 26 87 ee 47 ** for key repeat (** is the code of the key being held) */
+
+static int keymap[MAX_KEYS] = {
+ KEY_RESERVED,
+ KEY_MENU,
+ KEY_PLAYPAUSE,
+ KEY_FORWARD,
+ KEY_BACK,
+ KEY_VOLUMEUP,
+ KEY_VOLUMEDOWN,
+ KEY_RESERVED,
+};
+
+static void dump_packet(struct appleir *appleir, char *msg, u8 *data, int len)
+{
+ int i;
+
+ printk(KERN_ERR "appleir: %s (%d bytes)", msg, len);
+
+ for (i = 0; i < len; ++i)
+ printk(" %02x", data[i]);
+ printk("\n");
+}
+
+static void key_up(struct appleir *appleir, int key)
+{
+ dbginfo (&appleir->input_dev->dev, "key %d up\n", key);
+ input_report_key(appleir->input_dev, key, 0);
+ input_sync(appleir->input_dev);
+}
+
+static void key_down(struct appleir *appleir, int key)
+{
+ dbginfo (&appleir->input_dev->dev, "key %d down\n", key);
+ input_report_key(appleir->input_dev, key, 1);
+ input_sync(appleir->input_dev);
+}
+
+static void battery_flat(struct appleir *appleir)
+{
+ dev_err(&appleir->input_dev->dev, "possible flat battery?\n");
+}
+
+static void key_up_tick(unsigned long data)
+{
+ struct appleir *appleir = (struct appleir *)data;
+
+ if (appleir->current_key) {
+ key_up(appleir, appleir->current_key);
+ appleir->current_key = 0;
+ }
+}
+
+static void new_data(struct appleir *appleir, u8 *data, int len)
+{
+ static const u8 keydown[] = { 0x25, 0x87, 0xee };
+ static const u8 keyrepeat[] = { 0x26, };
+ static const u8 flatbattery[] = { 0x25, 0x87, 0xe0 };
+
+ if (debug)
+ dump_packet(appleir, "received", data, len);
+
+ if (len != 5)
+ return;
+
+ if (!memcmp(data, keydown, sizeof(keydown))) {
+ /*If we already have a key down, take it up before marking */
+ /*this one down */
+ if (appleir->current_key)
+ key_up(appleir, appleir->current_key);
+ appleir->current_key = keymap[(data[4] >> 1) & MAX_KEYS_MASK];
+
+ key_down(appleir, appleir->current_key);
+ /*remote doesn't do key up, either pull them up, in the test */
+ /*above, or here set a timer which pulls them up after 1/8 s */
+ mod_timer(&appleir->key_up_timer, jiffies + HZ / 8);
+
+ return;
+ }
+
+ if (!memcmp(data, keyrepeat, sizeof(keyrepeat))) {
+ key_down(appleir, appleir->current_key);
+ /*remote doesn't do key up, either pull them up, in the test */
+ /*above, or here set a timer which pulls them up after 1/8 s */
+ mod_timer(&appleir->key_up_timer, jiffies + HZ / 8);
+ return;
+ }
+
+ if (!memcmp(data, flatbattery, sizeof(flatbattery))) {
+ battery_flat(appleir);
+ /* Fall through */
+ }
+
+ dump_packet(appleir, "unknown packet", data, len);
+}
+
+static void appleir_urb(struct urb *urb)
+{
+ struct appleir *appleir = urb->context;
+ int status = urb->status;
+ int retval;
+
+ switch (status) {
+ case 0:
+ new_data(appleir, urb->transfer_buffer, urb->actual_length);
+ break;
+ case -ECONNRESET:
+ case -ENOENT:
+ case -ESHUTDOWN:
+ /* this urb is terminated, clean up */
+ dbginfo(&appleir->input_dev->dev, "%s - urb shutting down with status: %d", __func__,
+ urb->status);
+ return;
+ default:
+ dbginfo(&appleir->input_dev->dev, "%s - nonzero urb status received: %d", __func__,
+ urb->status);
+ }
+
+ retval = usb_submit_urb(urb, GFP_ATOMIC);
+ if (retval)
+ err("%s - usb_submit_urb failed with result %d", __func__,
+ retval);
+}
+
+static int appleir_open(struct input_dev *dev)
+{
+ struct appleir *appleir = input_get_drvdata(dev);
+ struct usb_interface *intf = usb_ifnum_to_if(appleir->usbdev, 0);
+ int r;
+
+ r = usb_autopm_get_interface(intf);
+ if (r) {
+ dev_err(&intf->dev,
+ "%s(): usb_autopm_get_interface() = %d\n", __func__, r);
+ return r;
+ }
+
+ mutex_lock(&appleir_mutex);
+
+ if (usb_submit_urb(appleir->urb, GFP_KERNEL)) {
+ r = -EIO;
+ goto fail;
+ }
+
+ appleir->flags |= APPLEIR_OPENED;
+
+ mutex_unlock(&appleir_mutex);
+
+ usb_autopm_put_interface(intf);
+
+ return 0;
+fail:
+ mutex_unlock(&appleir_mutex);
+ usb_autopm_put_interface(intf);
+ return r;
+}
+
+static void appleir_close(struct input_dev *dev)
+{
+ struct appleir *appleir = input_get_drvdata(dev);
+
+ mutex_lock(&appleir_mutex);
+
+ if (!(appleir->flags & APPLEIR_SUSPENDED)) {
+ usb_kill_urb(appleir->urb);
+ del_timer_sync(&appleir->key_up_timer);
+ }
+
+ appleir->flags &= ~APPLEIR_OPENED;
+
+ mutex_unlock(&appleir_mutex);
+}
+
+static int appleir_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ struct usb_device *dev = interface_to_usbdev(intf);
+ struct usb_endpoint_descriptor *endpoint;
+ struct appleir *appleir = NULL;
+ struct input_dev *input_dev;
+ int retval = -ENOMEM;
+ int i;
+
+ appleir = kzalloc(sizeof(struct appleir), GFP_KERNEL);
+ if (!appleir)
+ goto fail;
+
+ appleir->data = usb_buffer_alloc(dev, URB_SIZE, GFP_KERNEL,
+ &appleir->dma_buf);
+ if (!appleir->data)
+ goto fail;
+
+ appleir->urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!appleir->urb)
+ goto fail;
+
+ appleir->usbdev = dev;
+
+ input_dev = input_allocate_device();
+ if (!input_dev)
+ goto fail;
+
+ appleir->input_dev = input_dev;
+
+ usb_make_path(dev, appleir->phys, sizeof(appleir->phys));
+ strlcpy(appleir->phys, "/input0", sizeof(appleir->phys));
+
+ input_dev->name = "Apple infrared remote control driver";
+ input_dev->phys = appleir->phys;
+ usb_to_input_id(dev, &input_dev->id);
+ input_dev->dev.parent = &intf->dev;
+ input_set_drvdata(input_dev, appleir);
+
+ input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP);
+ input_dev->ledbit[0] = 0;
+
+ for (i = 0; i < MAX_KEYS; i++)
+ set_bit(keymap[i], input_dev->keybit);
+
+ clear_bit(0, input_dev->keybit);
+
+ input_dev->open = appleir_open;
+ input_dev->close = appleir_close;
+
+ endpoint = &intf->cur_altsetting->endpoint[0].desc;
+
+ usb_fill_int_urb(appleir->urb, dev,
+ usb_rcvintpipe(dev, endpoint->bEndpointAddress),
+ appleir->data, 8,
+ appleir_urb, appleir, endpoint->bInterval);
+
+ appleir->urb->transfer_dma = appleir->dma_buf;
+ appleir->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+ usb_set_intfdata(intf, appleir);
+
+ init_timer(&appleir->key_up_timer);
+
+ appleir->key_up_timer.function = key_up_tick;
+ appleir->key_up_timer.data = (unsigned long)appleir;
+
+ appleir->timer_initted++;
+
+ retval = input_register_device(appleir->input_dev);
+ if (retval)
+ goto fail;
+
+ return 0;
+
+fail:
+ printk(KERN_WARNING "Failed to load appleir\n");
+ if (appleir) {
+ if (appleir->data)
+ usb_buffer_free(dev, URB_SIZE, appleir->data,
+ appleir->dma_buf);
+
+ if (appleir->timer_initted)
+ del_timer_sync(&appleir->key_up_timer);
+
+ if (appleir->input_dev)
+ input_free_device(appleir->input_dev);
+
+ kfree(appleir);
+ }
+
+ return retval;
+}
+
+static void appleir_disconnect(struct usb_interface *intf)
+{
+ struct appleir *appleir = usb_get_intfdata(intf);
+
+ usb_set_intfdata(intf, NULL);
+ if (appleir) {
+ input_unregister_device(appleir->input_dev);
+ if (appleir->timer_initted)
+ del_timer_sync(&appleir->key_up_timer);
+ usb_kill_urb(appleir->urb);
+ usb_free_urb(appleir->urb);
+ usb_buffer_free(interface_to_usbdev(intf), URB_SIZE,
+ appleir->data, appleir->dma_buf);
+ kfree(appleir);
+ }
+}
+
+static int appleir_suspend(struct usb_interface *interface,
+ pm_message_t message)
+{
+ struct appleir *appleir;
+
+ appleir = usb_get_intfdata(interface);
+
+ mutex_lock(&appleir_mutex);
+
+ if (appleir->flags & APPLEIR_OPENED) {
+ usb_kill_urb(appleir->urb);
+ del_timer_sync(&appleir->key_up_timer);
+ }
+
+ appleir->flags |= APPLEIR_SUSPENDED;
+
+ mutex_unlock(&appleir_mutex);
+
+ return 0;
+}
+
+static int appleir_resume(struct usb_interface *interface)
+{
+ struct appleir *appleir;
+
+ appleir = usb_get_intfdata(interface);
+
+ mutex_lock(&appleir_mutex);
+
+ if (appleir->flags & APPLEIR_OPENED) {
+ struct usb_endpoint_descriptor *endpoint;
+
+ endpoint = &interface->cur_altsetting->endpoint[0].desc;
+ usb_fill_int_urb(appleir->urb, appleir->usbdev,
+ usb_rcvintpipe(appleir->usbdev, endpoint->bEndpointAddress),
+ appleir->data, 8,
+ appleir_urb, appleir, endpoint->bInterval);
+ appleir->urb->transfer_dma = appleir->dma_buf;
+ appleir->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+ init_timer(&appleir->key_up_timer);
+
+ appleir->key_up_timer.function = key_up_tick;
+ appleir->key_up_timer.data = (unsigned long)appleir;
+ }
+
+ appleir->flags &= ~APPLEIR_SUSPENDED;
+
+ mutex_unlock(&appleir_mutex);
+
+ return 0;
+}
+
+static struct usb_driver appleir_driver = {
+ .name = "appleir",
+ .probe = appleir_probe,
+ .disconnect = appleir_disconnect,
+ .suspend = appleir_suspend,
+ .resume = appleir_resume,
+ .reset_resume = appleir_resume,
+ .id_table = appleir_ids,
+ .supports_autosuspend = 1,
+};
+
+static int __init appleir_init(void)
+{
+ int retval;
+
+ retval = usb_register(&appleir_driver);
+ if (retval)
+ goto out;
+ printk(KERN_INFO DRIVER_VERSION ":" DRIVER_DESC);
+out:
+ return retval;
+}
+
+static void __exit appleir_exit(void)
+{
+ usb_deregister(&appleir_driver);
+}
+
+module_init(appleir_init);
+module_exit(appleir_exit);
--
1.6.6
^ permalink raw reply related [flat|nested] 57+ messages in thread
* Re: [PATCH] Input: add appleir USB driver
2010-01-20 14:17 Bastien Nocera
@ 2010-01-27 15:40 ` Jiri Kosina
2010-02-01 13:52 ` Bastien Nocera
0 siblings, 1 reply; 57+ messages in thread
From: Jiri Kosina @ 2010-01-27 15:40 UTC (permalink / raw)
To: Bastien Nocera; +Cc: linux-input, Dmitry Torokhov
On Wed, 20 Jan 2010, Bastien Nocera wrote:
> +First, you will need to disable the kernel driver for the receiver.
> +
> +This can be achieved by passing quirks to the usbhid driver.
> +The quirk line would be:
> +usbhid.quirks=0x05ac:0x8242:0x08
> +
> +With 0x05ac being the vendor ID (Apple, you shouldn't need to change this)
> +With 0x8242 being the product ID (check the output of lsusb for your hardware)
> +And 0x08 being "HID_CONNECT_HIDDEV"
I am afraid this is not true, 0x08 is HID_QUIRK_NOGET.
We currently don't have dynamic quirk for forcing HIDDEV creation from the
module parameter.
> + if (!memcmp(data, keydown, sizeof(keydown))) {
> + /*If we already have a key down, take it up before marking */
> + /*this one down */
> + if (appleir->current_key)
> + key_up(appleir, appleir->current_key);
> + appleir->current_key = keymap[(data[4] >> 1) & MAX_KEYS_MASK];
> +
> + key_down(appleir, appleir->current_key);
> + /*remote doesn't do key up, either pull them up, in the test */
> + /*above, or here set a timer which pulls them up after 1/8 s */
Could you please place spaces after the comment starters?
Thanks,
--
Jiri Kosina
SUSE Labs, Novell Inc.
^ permalink raw reply [flat|nested] 57+ messages in thread
* Re: [PATCH] Input: add appleir USB driver
2010-01-27 15:40 ` Jiri Kosina
@ 2010-02-01 13:52 ` Bastien Nocera
2010-02-03 15:54 ` Jiri Kosina
0 siblings, 1 reply; 57+ messages in thread
From: Bastien Nocera @ 2010-02-01 13:52 UTC (permalink / raw)
To: Jiri Kosina; +Cc: linux-input, Dmitry Torokhov
On Wed, 2010-01-27 at 16:40 +0100, Jiri Kosina wrote:
> On Wed, 20 Jan 2010, Bastien Nocera wrote:
>
> > +First, you will need to disable the kernel driver for the receiver.
> > +
> > +This can be achieved by passing quirks to the usbhid driver.
> > +The quirk line would be:
> > +usbhid.quirks=0x05ac:0x8242:0x08
> > +
> > +With 0x05ac being the vendor ID (Apple, you shouldn't need to change this)
> > +With 0x8242 being the product ID (check the output of lsusb for your hardware)
> > +And 0x08 being "HID_CONNECT_HIDDEV"
>
> I am afraid this is not true, 0x08 is HID_QUIRK_NOGET.
>
> We currently don't have dynamic quirk for forcing HIDDEV creation from the
> module parameter.
Right. Any chance to change that?
> > + if (!memcmp(data, keydown, sizeof(keydown))) {
> > + /*If we already have a key down, take it up before marking */
> > + /*this one down */
> > + if (appleir->current_key)
> > + key_up(appleir, appleir->current_key);
> > + appleir->current_key = keymap[(data[4] >> 1) & MAX_KEYS_MASK];
> > +
> > + key_down(appleir, appleir->current_key);
> > + /*remote doesn't do key up, either pull them up, in the test */
> > + /*above, or here set a timer which pulls them up after 1/8 s */
>
> Could you please place spaces after the comment starters?
I've done that on my local copy. I'm now trying to get in touch with
Matthew Garrett so he can help me out with the suspend parts of the
patch. I'll resubmit when that's done.
Cheers
^ permalink raw reply [flat|nested] 57+ messages in thread
* Re: [PATCH] Input: add appleir USB driver
2010-02-01 13:52 ` Bastien Nocera
@ 2010-02-03 15:54 ` Jiri Kosina
2010-02-08 16:32 ` Bastien Nocera
0 siblings, 1 reply; 57+ messages in thread
From: Jiri Kosina @ 2010-02-03 15:54 UTC (permalink / raw)
To: Bastien Nocera; +Cc: linux-input, Dmitry Torokhov
On Mon, 1 Feb 2010, Bastien Nocera wrote:
> > > +With 0x05ac being the vendor ID (Apple, you shouldn't need to change this)
> > > +With 0x8242 being the product ID (check the output of lsusb for your hardware)
> > > +And 0x08 being "HID_CONNECT_HIDDEV"
> >
> > I am afraid this is not true, 0x08 is HID_QUIRK_NOGET.
> >
> > We currently don't have dynamic quirk for forcing HIDDEV creation from the
> > module parameter.
>
> Right. Any chance to change that?
This particular one might actually make enough sense to be added (although
I am really trying to avoid quirk additions completely), for backwards
compatibility reasons.
> > > + if (!memcmp(data, keydown, sizeof(keydown))) {
> > > + /*If we already have a key down, take it up before marking */
> > > + /*this one down */
> > > + if (appleir->current_key)
> > > + key_up(appleir, appleir->current_key);
> > > + appleir->current_key = keymap[(data[4] >> 1) & MAX_KEYS_MASK];
> > > +
> > > + key_down(appleir, appleir->current_key);
> > > + /*remote doesn't do key up, either pull them up, in the test */
> > > + /*above, or here set a timer which pulls them up after 1/8 s */
> >
> > Could you please place spaces after the comment starters?
>
> I've done that on my local copy. I'm now trying to get in touch with
> Matthew Garrett so he can help me out with the suspend parts of the
> patch. I'll resubmit when that's done.
OK, thanks.
--
Jiri Kosina
SUSE Labs, Novell Inc.
^ permalink raw reply [flat|nested] 57+ messages in thread
* Re: [PATCH] Input: add appleir USB driver
2010-02-03 15:54 ` Jiri Kosina
@ 2010-02-08 16:32 ` Bastien Nocera
2010-02-10 12:52 ` Jiri Kosina
0 siblings, 1 reply; 57+ messages in thread
From: Bastien Nocera @ 2010-02-08 16:32 UTC (permalink / raw)
To: Jiri Kosina; +Cc: linux-input, Dmitry Torokhov
On Wed, 2010-02-03 at 16:54 +0100, Jiri Kosina wrote:
> On Mon, 1 Feb 2010, Bastien Nocera wrote:
>
> > > > +With 0x05ac being the vendor ID (Apple, you shouldn't need to change this)
> > > > +With 0x8242 being the product ID (check the output of lsusb for your hardware)
> > > > +And 0x08 being "HID_CONNECT_HIDDEV"
> > >
> > > I am afraid this is not true, 0x08 is HID_QUIRK_NOGET.
> > >
> > > We currently don't have dynamic quirk for forcing HIDDEV creation from the
> > > module parameter.
> >
> > Right. Any chance to change that?
>
> This particular one might actually make enough sense to be added (although
> I am really trying to avoid quirk additions completely), for backwards
> compatibility reasons.
Should I remove the comments there altogether then? That kind of defeats
my "but you can use lirc with some tweaks" argument, even though I still
think that those people needing more keys would be in the minority...
> > > > + if (!memcmp(data, keydown, sizeof(keydown))) {
> > > > + /*If we already have a key down, take it up before marking */
> > > > + /*this one down */
> > > > + if (appleir->current_key)
> > > > + key_up(appleir, appleir->current_key);
> > > > + appleir->current_key = keymap[(data[4] >> 1) & MAX_KEYS_MASK];
> > > > +
> > > > + key_down(appleir, appleir->current_key);
> > > > + /*remote doesn't do key up, either pull them up, in the test */
> > > > + /*above, or here set a timer which pulls them up after 1/8 s */
> > >
> > > Could you please place spaces after the comment starters?
> >
> > I've done that on my local copy. I'm now trying to get in touch with
> > Matthew Garrett so he can help me out with the suspend parts of the
> > patch. I'll resubmit when that's done.
>
> OK, thanks.
Jarod gave me a hand, and the problem is the same as with the bcm5974
driver, the platform reset_resume will work just fine.
Updated patch coming up.
Cheers
^ permalink raw reply [flat|nested] 57+ messages in thread
* Re: [PATCH] Input: add appleir USB driver
2010-02-08 16:32 ` Bastien Nocera
@ 2010-02-10 12:52 ` Jiri Kosina
2010-02-11 18:18 ` Bastien Nocera
0 siblings, 1 reply; 57+ messages in thread
From: Jiri Kosina @ 2010-02-10 12:52 UTC (permalink / raw)
To: Bastien Nocera; +Cc: linux-input, Dmitry Torokhov
On Mon, 8 Feb 2010, Bastien Nocera wrote:
> > > > > +With 0x05ac being the vendor ID (Apple, you shouldn't need to change this)
> > > > > +With 0x8242 being the product ID (check the output of lsusb for your hardware)
> > > > > +And 0x08 being "HID_CONNECT_HIDDEV"
> > > >
> > > > I am afraid this is not true, 0x08 is HID_QUIRK_NOGET.
> > > >
> > > > We currently don't have dynamic quirk for forcing HIDDEV creation from the
> > > > module parameter.
> > >
> > > Right. Any chance to change that?
> >
> > This particular one might actually make enough sense to be added (although
> > I am really trying to avoid quirk additions completely), for backwards
> > compatibility reasons.
>
> Should I remove the comments there altogether then? That kind of defeats
> my "but you can use lirc with some tweaks" argument, even though I still
> think that those people needing more keys would be in the minority...
I rather though that we could/should (re-)introduce the HID_QUIRK_HIDDEV
quirks so that it could still be applied manually.
Thanks,
--
Jiri Kosina
SUSE Labs, Novell Inc.
^ permalink raw reply [flat|nested] 57+ messages in thread
* Re: [PATCH] Input: add appleir USB driver
2010-02-10 12:52 ` Jiri Kosina
@ 2010-02-11 18:18 ` Bastien Nocera
0 siblings, 0 replies; 57+ messages in thread
From: Bastien Nocera @ 2010-02-11 18:18 UTC (permalink / raw)
To: Jiri Kosina; +Cc: linux-input, Dmitry Torokhov
On Wed, 2010-02-10 at 13:52 +0100, Jiri Kosina wrote:
> On Mon, 8 Feb 2010, Bastien Nocera wrote:
>
> > > > > > +With 0x05ac being the vendor ID (Apple, you shouldn't need to change this)
> > > > > > +With 0x8242 being the product ID (check the output of lsusb for your hardware)
> > > > > > +And 0x08 being "HID_CONNECT_HIDDEV"
> > > > >
> > > > > I am afraid this is not true, 0x08 is HID_QUIRK_NOGET.
> > > > >
> > > > > We currently don't have dynamic quirk for forcing HIDDEV creation from the
> > > > > module parameter.
> > > >
> > > > Right. Any chance to change that?
> > >
> > > This particular one might actually make enough sense to be added (although
> > > I am really trying to avoid quirk additions completely), for backwards
> > > compatibility reasons.
> >
> > Should I remove the comments there altogether then? That kind of defeats
> > my "but you can use lirc with some tweaks" argument, even though I still
> > think that those people needing more keys would be in the minority...
>
> I rather though that we could/should (re-)introduce the HID_QUIRK_HIDDEV
> quirks so that it could still be applied manually.
The interaction between the HID and the input layer isn't really my
forte (well, the kernel's not my forte :).
Wouldn't we have some trouble getting the HID layer provide a hiddev
device for the receiver, whilst being serviced by an input driver?
I wouldn't mind trying to port the driver to be a full HID driver, and
enable hiddev all the time on it. We just wouldn't do anything in the
input part if the hiddev device was claimed, and parse the raw_report
from the device otherwise.
Opinions? If it's a good idea, I'd be interested in some examples in
devices that do their own raw_report event parsing, instead of letting
the hid layer handle it.
^ permalink raw reply [flat|nested] 57+ messages in thread
* [PATCH] Input: add appleir USB driver
@ 2008-05-14 22:15 Greg KH
2008-05-14 23:27 ` Matthew Garrett
` (3 more replies)
0 siblings, 4 replies; 57+ messages in thread
From: Greg KH @ 2008-05-14 22:15 UTC (permalink / raw)
To: Dmitry Torokhov, jkosina; +Cc: linux-input, linux-usb, linux-kernel
From: Greg Kroah-Hartman <gregkh@suse.de>
This driver was originally written by James McKenzie but forward ported
and cleaned up by me to get it to work with modern kernel versions.
Tested on my mac mini and it actually works!
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
---
Jiri, is it ok for this quirks addtion to go through Dmitry's triee? Or
do you want me to split it out into two different patches?
Dmitry, is this ok to go through your tree? Or I can take it as well if
you don't want it :)
drivers/hid/usbhid/hid-quirks.c | 2
drivers/input/misc/Kconfig | 12 +
drivers/input/misc/Makefile | 1
drivers/input/misc/appleir.c | 361 ++++++++++++++++++++++++++++++++++++++++
4 files changed, 376 insertions(+)
--- a/drivers/hid/usbhid/hid-quirks.c
+++ b/drivers/hid/usbhid/hid-quirks.c
@@ -77,6 +77,7 @@
#define USB_DEVICE_ID_APPLE_ALU_WIRELESS_JIS 0x022e
#define USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY 0x030a
#define USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY 0x030b
+#define USB_DEVICE_ID_APPLE_IRCONTROL 0x8240
#define USB_DEVICE_ID_APPLE_IRCONTROL4 0x8242
#define USB_VENDOR_ID_ASUS 0x0b05
@@ -443,6 +444,7 @@ static const struct hid_blacklist {
{ USB_VENDOR_ID_AFATECH, USB_DEVICE_ID_AFATECH_AF9016, HID_QUIRK_FULLSPEED_INTERVAL },
{ USB_VENDOR_ID_BELKIN, USB_DEVICE_ID_FLIP_KVM, HID_QUIRK_HIDDEV },
+ { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL, HID_QUIRK_IGNORE },
{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL4, HID_QUIRK_HIDDEV | HID_QUIRK_IGNORE_HIDINPUT },
{ USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_IR_REMOTE, HID_QUIRK_HIDDEV | HID_QUIRK_IGNORE_HIDINPUT },
{ USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_SIDEWINDER_GV, HID_QUIRK_HIDINPUT },
--- /dev/null
+++ b/drivers/input/misc/appleir.c
@@ -0,0 +1,361 @@
+/*
+ * appleir: USB driver for the apple ir device
+ *
+ * Original driver written by James McKenzie
+ * Ported to recent 2.6 kernel versions by Greg Kroah-Hartman <gregkh@suse.de>
+ *
+ * Copyright (C) 2006 James McKenzie
+ * Copyright (C) 2008 Greg Kroah-Hartman <greg@kroah.com>
+ * Copyright (C) 2008 Novell Inc.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/usb.h>
+#include <linux/usb/input.h>
+#include <asm/unaligned.h>
+#include <asm/byteorder.h>
+
+#define DRIVER_VERSION "v1.2"
+#define DRIVER_AUTHOR "James McKenzie"
+#define DRIVER_DESC "USB Apple MacMini IR Receiver driver"
+#define DRIVER_LICENSE "GPL"
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE(DRIVER_LICENSE);
+
+#define USB_VENDOR_ID_APPLE 0x05ac
+#define USB_DEVICE_ID_APPLE_IR 0x8240
+
+#define URB_SIZE 32
+
+#define MAX_KEYS 8
+#define MAX_KEYS_MASK (MAX_KEYS - 1)
+
+struct appleir {
+ struct input_dev *input_dev;
+ u8 *data;
+ dma_addr_t dma_buf;
+ struct usb_device *usbdev;
+ struct urb *urb;
+ int timer_initted;
+ struct timer_list key_up_timer;
+ int current_key;
+ char phys[32];
+};
+
+static struct usb_device_id appleir_ids[] = {
+ {USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IR)},
+ {}
+};
+MODULE_DEVICE_TABLE(usb, appleir_ids);
+
+/* I have two devices both of which report the following */
+/* 25 87 ee 83 0a + */
+/* 25 87 ee 83 0c - */
+/* 25 87 ee 83 09 << */
+/* 25 87 ee 83 06 >> */
+/* 25 87 ee 83 05 >" */
+/* 25 87 ee 83 03 menu */
+/* 26 00 00 00 00 for key repeat*/
+
+/* Thomas Glanzmann reports the following responses */
+/* 25 87 ee ca 0b + */
+/* 25 87 ee ca 0d - */
+/* 25 87 ee ca 08 << */
+/* 25 87 ee ca 07 >> */
+/* 25 87 ee ca 04 >" */
+/* 25 87 ee ca 02 menu */
+/* 26 00 00 00 00 for key repeat*/
+/* He also observes the following event sometimes */
+/* sent after a key is release, which I interpret */
+/* as a flat battery message */
+/* 25 87 e0 ca 06 flat battery */
+
+static int keymap[MAX_KEYS] = {
+ KEY_RESERVED,
+ KEY_MENU,
+ KEY_PLAYPAUSE,
+ KEY_NEXTSONG,
+ KEY_PREVIOUSSONG,
+ KEY_VOLUMEUP,
+ KEY_VOLUMEDOWN,
+ KEY_RESERVED,
+};
+
+static void dump_packet(struct appleir *appleir, char *msg, u8 *data, int len)
+{
+ int i;
+
+ printk(KERN_ERR "appleir: %s (%d bytes)", msg, len);
+
+ for (i = 0; i < len; ++i)
+ printk(" %02x", data[i]);
+ printk("\n");
+}
+
+static void key_up(struct appleir *appleir, int key)
+{
+ /* printk (KERN_ERR "key %d up\n", key); */
+ input_report_key(appleir->input_dev, key, 0);
+ input_sync(appleir->input_dev);
+}
+
+static void key_down(struct appleir *appleir, int key)
+{
+ /* printk (KERN_ERR "key %d down\n", key); */
+ input_report_key(appleir->input_dev, key, 1);
+ input_sync(appleir->input_dev);
+}
+
+static void battery_flat(struct appleir *appleir)
+{
+ dev_err(&appleir->input_dev->dev, "possible flat battery?\n");
+}
+
+static void key_up_tick(unsigned long data)
+{
+ struct appleir *appleir = (struct appleir *)data;
+
+ if (appleir->current_key) {
+ key_up(appleir, appleir->current_key);
+ appleir->current_key = 0;
+ }
+}
+
+static void new_data(struct appleir *appleir, u8 *data, int len)
+{
+ static const u8 keydown[] = { 0x25, 0x87, 0xee };
+ static const u8 keyrepeat[] = { 0x26, 0x00, 0x00, 0x00, 0x00 };
+ static const u8 flatbattery[] = { 0x25, 0x87, 0xe0 };
+
+#if 0
+ dump_packet(appleir, "received", data, len);
+#endif
+
+ if (len != 5)
+ return;
+
+ if (!memcmp(data, keydown, sizeof(keydown))) {
+ /*If we already have a key down, take it up before marking */
+ /*this one down */
+ if (appleir->current_key)
+ key_up(appleir, appleir->current_key);
+ appleir->current_key = keymap[(data[4] >> 1) & MAX_KEYS_MASK];
+
+ key_down(appleir, appleir->current_key);
+ /*remote doesn't do key up, either pull them up, in the test */
+ /*above, or here set a timer which pulls them up after 1/8 s */
+ mod_timer(&appleir->key_up_timer, jiffies + HZ / 8);
+
+ return;
+ }
+
+ if (!memcmp(data, keyrepeat, sizeof(keyrepeat))) {
+ key_down(appleir, appleir->current_key);
+ /*remote doesn't do key up, either pull them up, in the test */
+ /*above, or here set a timer which pulls them up after 1/8 s */
+ mod_timer(&appleir->key_up_timer, jiffies + HZ / 8);
+ return;
+ }
+
+ if (!memcmp(data, flatbattery, sizeof(flatbattery))) {
+ battery_flat(appleir);
+ /* Fall through */
+ }
+
+ dump_packet(appleir, "unknown packet", data, len);
+}
+
+static void appleir_urb(struct urb *urb)
+{
+ struct appleir *appleir = urb->context;
+ int status = urb->status;
+ int retval;
+
+ switch (status) {
+ case 0:
+ new_data(appleir, urb->transfer_buffer, urb->actual_length);
+ 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);
+ }
+
+ retval = usb_submit_urb(urb, GFP_ATOMIC);
+ if (retval)
+ err("%s - usb_submit_urb failed with result %d", __func__,
+ retval);
+}
+
+static int appleir_open(struct input_dev *dev)
+{
+ struct appleir *appleir = input_get_drvdata(dev);
+
+ if (usb_submit_urb(appleir->urb, GFP_KERNEL))
+ return -EIO;
+
+ return 0;
+}
+
+static void appleir_close(struct input_dev *dev)
+{
+ struct appleir *appleir = input_get_drvdata(dev);
+
+ usb_kill_urb(appleir->urb);
+ del_timer_sync(&appleir->key_up_timer);
+}
+
+static int appleir_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ struct usb_device *dev = interface_to_usbdev(intf);
+ struct usb_endpoint_descriptor *endpoint;
+ struct appleir *appleir = NULL;
+ struct input_dev *input_dev;
+ int retval = -ENOMEM;
+ int i;
+
+ appleir = kzalloc(sizeof(struct appleir), GFP_KERNEL);
+ if (!appleir)
+ goto fail;
+
+ appleir->data = usb_buffer_alloc(dev, URB_SIZE, GFP_KERNEL,
+ &appleir->dma_buf);
+ if (!appleir->data)
+ goto fail;
+
+ appleir->urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!appleir->urb)
+ goto fail;
+
+ appleir->usbdev = dev;
+
+ input_dev = input_allocate_device();
+ if (!input_dev)
+ goto fail;
+
+ appleir->input_dev = input_dev;
+
+ usb_make_path(dev, appleir->phys, sizeof(appleir->phys));
+ strlcpy(appleir->phys, "/input0", sizeof(appleir->phys));
+
+ input_dev->name = "Apple Mac mini infrared remote control driver";
+ input_dev->phys = appleir->phys;
+ usb_to_input_id(dev, &input_dev->id);
+ input_dev->dev.parent = &intf->dev;
+ input_set_drvdata(input_dev, appleir);
+
+ input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP);
+ input_dev->ledbit[0] = 0;
+
+ for (i = 0; i < MAX_KEYS; i++)
+ set_bit(keymap[i], input_dev->keybit);
+
+ clear_bit(0, input_dev->keybit);
+
+ input_dev->open = appleir_open;
+ input_dev->close = appleir_close;
+
+ endpoint = &intf->cur_altsetting->endpoint[0].desc;
+
+ usb_fill_int_urb(appleir->urb, dev,
+ usb_rcvintpipe(dev, endpoint->bEndpointAddress),
+ appleir->data, 8,
+ appleir_urb, appleir, endpoint->bInterval);
+
+ appleir->urb->transfer_dma = appleir->dma_buf;
+ appleir->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+ usb_set_intfdata(intf, appleir);
+
+ init_timer(&appleir->key_up_timer);
+
+ appleir->key_up_timer.function = key_up_tick;
+ appleir->key_up_timer.data = (unsigned long)appleir;
+
+ appleir->timer_initted++;
+
+ retval = input_register_device(appleir->input_dev);
+ if (retval)
+ goto fail;
+
+ return 0;
+
+fail:
+ if (appleir) {
+ if (appleir->data)
+ usb_buffer_free(dev, URB_SIZE, appleir->data,
+ appleir->dma_buf);
+
+ if (appleir->timer_initted)
+ del_timer_sync(&appleir->key_up_timer);
+
+ if (appleir->input_dev)
+ input_free_device(appleir->input_dev);
+
+ kfree(appleir);
+ }
+
+ return retval;
+}
+
+static void appleir_disconnect(struct usb_interface *intf)
+{
+ struct appleir *appleir = usb_get_intfdata(intf);
+
+ usb_set_intfdata(intf, NULL);
+ if (appleir) {
+ input_unregister_device(appleir->input_dev);
+ if (appleir->timer_initted)
+ del_timer_sync(&appleir->key_up_timer);
+ usb_kill_urb(appleir->urb);
+ usb_free_urb(appleir->urb);
+ usb_buffer_free(interface_to_usbdev(intf), URB_SIZE,
+ appleir->data, appleir->dma_buf);
+ kfree(appleir);
+ }
+}
+
+static struct usb_driver appleir_driver = {
+ .name = "appleir",
+ .probe = appleir_probe,
+ .disconnect = appleir_disconnect,
+ .id_table = appleir_ids,
+};
+
+static int __init appleir_init(void)
+{
+ int retval;
+
+ retval = usb_register(&appleir_driver);
+ if (retval)
+ goto out;
+ info(DRIVER_VERSION ":" DRIVER_DESC);
+out:
+ return retval;
+}
+
+static void __exit appleir_exit(void)
+{
+ usb_deregister(&appleir_driver);
+}
+
+module_init(appleir_init);
+module_exit(appleir_exit);
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -149,6 +149,18 @@ config INPUT_KEYSPAN_REMOTE
To compile this driver as a module, choose M here: the module will
be called keyspan_remote.
+config INPUT_APPLEIR
+ tristate "Apple Mac Mini USB IR receiver (built in)"
+ depends on USB_ARCH_HAS_HCD
+ select USB
+ help
+ Say Y here if you want to use a Apple USB remote control. This
+ device is traditionally inside an Intel Apple Mac Mini, but might
+ show up in other places.
+
+ To compile this driver as a module, choose M here: the module will
+ be called appleir.
+
config INPUT_POWERMATE
tristate "Griffin PowerMate and Contour Jog support"
depends on USB_ARCH_HAS_HCD
--- a/drivers/input/misc/Makefile
+++ b/drivers/input/misc/Makefile
@@ -19,3 +19,4 @@ obj-$(CONFIG_INPUT_YEALINK) += yealink.
obj-$(CONFIG_HP_SDC_RTC) += hp_sdc_rtc.o
obj-$(CONFIG_INPUT_UINPUT) += uinput.o
obj-$(CONFIG_INPUT_APANEL) += apanel.o
+obj-$(CONFIG_INPUT_APPLEIR) += appleir.o
^ permalink raw reply [flat|nested] 57+ messages in thread
* Re: [PATCH] Input: add appleir USB driver
2008-05-14 22:15 Greg KH
@ 2008-05-14 23:27 ` Matthew Garrett
2008-05-14 23:49 ` Greg KH
2008-05-15 3:50 ` Dmitry Torokhov
` (2 subsequent siblings)
3 siblings, 1 reply; 57+ messages in thread
From: Matthew Garrett @ 2008-05-14 23:27 UTC (permalink / raw)
To: Greg KH; +Cc: Dmitry Torokhov, jkosina, linux-input, linux-usb, linux-kernel
On Wed, May 14, 2008 at 03:15:19PM -0700, Greg KH wrote:
> + { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL, HID_QUIRK_IGNORE },
> { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL4, HID_QUIRK_HIDDEV | HID_QUIRK_IGNORE_HIDINPUT },
Hm. How is the IRCONTROL4 handled? Is the protocol completely different?
> + Say Y here if you want to use a Apple USB remote control. This
> + device is traditionally inside an Intel Apple Mac Mini, but might
> + show up in other places.
Minor nit, but it's supported on all the desktop Intel Macs and not just
the Mini.
--
Matthew Garrett | mjg59@srcf.ucam.org
^ permalink raw reply [flat|nested] 57+ messages in thread
* Re: [PATCH] Input: add appleir USB driver
2008-05-14 23:27 ` Matthew Garrett
@ 2008-05-14 23:49 ` Greg KH
2008-05-15 6:20 ` Sitsofe Wheeler
0 siblings, 1 reply; 57+ messages in thread
From: Greg KH @ 2008-05-14 23:49 UTC (permalink / raw)
To: Matthew Garrett
Cc: Dmitry Torokhov, jkosina, linux-input, linux-usb, linux-kernel
On Thu, May 15, 2008 at 12:27:22AM +0100, Matthew Garrett wrote:
> On Wed, May 14, 2008 at 03:15:19PM -0700, Greg KH wrote:
>
> > + { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL, HID_QUIRK_IGNORE },
> > { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL4, HID_QUIRK_HIDDEV | HID_QUIRK_IGNORE_HIDINPUT },
>
> Hm. How is the IRCONTROL4 handled? Is the protocol completely different?
I know nothing about that device, sorry. I think it is a totally
different protocol from what I can tell looking at the code.
> > + Say Y here if you want to use a Apple USB remote control. This
> > + device is traditionally inside an Intel Apple Mac Mini, but might
> > + show up in other places.
>
> Minor nit, but it's supported on all the desktop Intel Macs and not just
> the Mini.
Ah, didn't realize that, nice to know, we should have a wider userbase
then :)
thanks,
greg k-h
^ permalink raw reply [flat|nested] 57+ messages in thread
* Re: [PATCH] Input: add appleir USB driver
2008-05-14 23:49 ` Greg KH
@ 2008-05-15 6:20 ` Sitsofe Wheeler
0 siblings, 0 replies; 57+ messages in thread
From: Sitsofe Wheeler @ 2008-05-15 6:20 UTC (permalink / raw)
To: linux-kernel; +Cc: linux-input, linux-usb
On Wed, 14 May 2008 16:49:09 -0700, Greg KH wrote:
> On Thu, May 15, 2008 at 12:27:22AM +0100, Matthew Garrett wrote:
>> On Wed, May 14, 2008 at 03:15:19PM -0700, Greg KH wrote:
>>
>> > + { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL,
>> > HID_QUIRK_IGNORE },
>> > { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL4,
>> > HID_QUIRK_HIDDEV | HID_QUIRK_IGNORE_HIDINPUT },
>>
>> Hm. How is the IRCONTROL4 handled? Is the protocol completely
>> different?
>
> I know nothing about that device, sorry. I think it is a totally
> different protocol from what I can tell looking at the code.
>
>> > + Say Y here if you want to use a Apple USB remote control. This
+
>> > device is traditionally inside an Intel Apple Mac Mini, but might +
>> > show up in other places.
>>
>> Minor nit, but it's supported on all the desktop Intel Macs and not
>> just the Mini.
>
> Ah, didn't realize that, nice to know, we should have a wider userbase
> then :)
I'm not 100% sure whether Mac Pro desktops support the remote controls.
Additionally saying USB remote makes it sound like a tethered device -
perhaps it should be "USB IR remote" (although maybe that's acronym
overload).
Do modern Mac Laptops (MacBook / MacBook Pro) support their IR remotes
some other way?
--
Sitsofe | http://sucs.org/~sits/
^ permalink raw reply [flat|nested] 57+ messages in thread
* Re: [PATCH] Input: add appleir USB driver
2008-05-14 22:15 Greg KH
2008-05-14 23:27 ` Matthew Garrett
@ 2008-05-15 3:50 ` Dmitry Torokhov
2008-05-15 13:21 ` Tino Keitel
[not found] ` <20080514221519.GA6575-U8xfFu+wG4EAvxtiuMwx3w@public.gmane.org>
3 siblings, 0 replies; 57+ messages in thread
From: Dmitry Torokhov @ 2008-05-15 3:50 UTC (permalink / raw)
To: Greg KH; +Cc: jkosina, linux-input, linux-usb, linux-kernel
Hi Greg,
On Wed, May 14, 2008 at 03:15:19PM -0700, Greg KH wrote:
> From: Greg Kroah-Hartman <gregkh@suse.de>
>
> This driver was originally written by James McKenzie but forward ported
> and cleaned up by me to get it to work with modern kernel versions.
>
> Tested on my mac mini and it actually works!
>
> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
>
> ---
>
> Jiri, is it ok for this quirks addtion to go through Dmitry's triee? Or
> do you want me to split it out into two different patches?
>
> Dmitry, is this ok to go through your tree? Or I can take it as well if
> you don't want it :)
>
I'll take it, although I have a couple of comments.
> +
> +struct appleir {
> + struct input_dev *input_dev;
> + u8 *data;
> + dma_addr_t dma_buf;
> + struct usb_device *usbdev;
> + struct urb *urb;
> + int timer_initted;
> + struct timer_list key_up_timer;
> + int current_key;
> + char phys[32];
> +};
> +
> +static struct usb_device_id appleir_ids[] = {
> + {USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IR)},
> + {}
> +};
> +MODULE_DEVICE_TABLE(usb, appleir_ids);
> +
> +/* I have two devices both of which report the following */
> +/* 25 87 ee 83 0a + */
> +/* 25 87 ee 83 0c - */
> +/* 25 87 ee 83 09 << */
> +/* 25 87 ee 83 06 >> */
> +/* 25 87 ee 83 05 >" */
> +/* 25 87 ee 83 03 menu */
> +/* 26 00 00 00 00 for key repeat*/
> +
> +/* Thomas Glanzmann reports the following responses */
> +/* 25 87 ee ca 0b + */
> +/* 25 87 ee ca 0d - */
> +/* 25 87 ee ca 08 << */
> +/* 25 87 ee ca 07 >> */
> +/* 25 87 ee ca 04 >" */
> +/* 25 87 ee ca 02 menu */
> +/* 26 00 00 00 00 for key repeat*/
> +/* He also observes the following event sometimes */
> +/* sent after a key is release, which I interpret */
> +/* as a flat battery message */
> +/* 25 87 e0 ca 06 flat battery */
> +
> +static int keymap[MAX_KEYS] = {
> + KEY_RESERVED,
> + KEY_MENU,
> + KEY_PLAYPAUSE,
> + KEY_NEXTSONG,
> + KEY_PREVIOUSSONG,
> + KEY_VOLUMEUP,
> + KEY_VOLUMEDOWN,
> + KEY_RESERVED,
> +};
> +
> +static void dump_packet(struct appleir *appleir, char *msg, u8 *data, int len)
> +{
> + int i;
> +
> + printk(KERN_ERR "appleir: %s (%d bytes)", msg, len);
> +
> + for (i = 0; i < len; ++i)
> + printk(" %02x", data[i]);
> + printk("\n");
> +}
> +
> +static void key_up(struct appleir *appleir, int key)
> +{
> + /* printk (KERN_ERR "key %d up\n", key); */
> + input_report_key(appleir->input_dev, key, 0);
> + input_sync(appleir->input_dev);
> +}
> +
> +static void key_down(struct appleir *appleir, int key)
> +{
> + /* printk (KERN_ERR "key %d down\n", key); */
> + input_report_key(appleir->input_dev, key, 1);
> + input_sync(appleir->input_dev);
> +}
> +
> +static void battery_flat(struct appleir *appleir)
> +{
> + dev_err(&appleir->input_dev->dev, "possible flat battery?\n");
> +}
> +
> +static void key_up_tick(unsigned long data)
> +{
> + struct appleir *appleir = (struct appleir *)data;
> +
> + if (appleir->current_key) {
> + key_up(appleir, appleir->current_key);
> + appleir->current_key = 0;
> + }
> +}
> +
> +static void new_data(struct appleir *appleir, u8 *data, int len)
> +{
> + static const u8 keydown[] = { 0x25, 0x87, 0xee };
> + static const u8 keyrepeat[] = { 0x26, 0x00, 0x00, 0x00, 0x00 };
> + static const u8 flatbattery[] = { 0x25, 0x87, 0xe0 };
> +
> +#if 0
> + dump_packet(appleir, "received", data, len);
> +#endif
> +
> + if (len != 5)
> + return;
> +
> + if (!memcmp(data, keydown, sizeof(keydown))) {
> + /*If we already have a key down, take it up before marking */
> + /*this one down */
> + if (appleir->current_key)
> + key_up(appleir, appleir->current_key);
> + appleir->current_key = keymap[(data[4] >> 1) & MAX_KEYS_MASK];
> +
> + key_down(appleir, appleir->current_key);
> + /*remote doesn't do key up, either pull them up, in the test */
> + /*above, or here set a timer which pulls them up after 1/8 s */
> + mod_timer(&appleir->key_up_timer, jiffies + HZ / 8);
> +
> + return;
> + }
> +
> + if (!memcmp(data, keyrepeat, sizeof(keyrepeat))) {
> + key_down(appleir, appleir->current_key);
Repeats are usually transmitted as an event different from normal
key down (event values for repeat is 2 vs 1 for key down).
> + /*remote doesn't do key up, either pull them up, in the test */
> + /*above, or here set a timer which pulls them up after 1/8 s */
> + mod_timer(&appleir->key_up_timer, jiffies + HZ / 8);
> + return;
> + }
> +
> + if (!memcmp(data, flatbattery, sizeof(flatbattery))) {
> + battery_flat(appleir);
> + /* Fall through */
> + }
> +
> + dump_packet(appleir, "unknown packet", data, len);
> +}
> +
> +static void appleir_urb(struct urb *urb)
> +{
> + struct appleir *appleir = urb->context;
> + int status = urb->status;
> + int retval;
> +
> + switch (status) {
> + case 0:
> + new_data(appleir, urb->transfer_buffer, urb->actual_length);
> + 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);
> + }
> +
> + retval = usb_submit_urb(urb, GFP_ATOMIC);
> + if (retval)
> + err("%s - usb_submit_urb failed with result %d", __func__,
> + retval);
> +}
> +
> +static int appleir_open(struct input_dev *dev)
> +{
> + struct appleir *appleir = input_get_drvdata(dev);
> +
> + if (usb_submit_urb(appleir->urb, GFP_KERNEL))
> + return -EIO;
> +
> + return 0;
> +}
> +
> +static void appleir_close(struct input_dev *dev)
> +{
> + struct appleir *appleir = input_get_drvdata(dev);
> +
> + usb_kill_urb(appleir->urb);
> + del_timer_sync(&appleir->key_up_timer);
> +}
> +
> +static int appleir_probe(struct usb_interface *intf,
> + const struct usb_device_id *id)
> +{
> + struct usb_device *dev = interface_to_usbdev(intf);
> + struct usb_endpoint_descriptor *endpoint;
> + struct appleir *appleir = NULL;
The assigment is not needed.
> + struct input_dev *input_dev;
> + int retval = -ENOMEM;
> + int i;
> +
> + appleir = kzalloc(sizeof(struct appleir), GFP_KERNEL);
> + if (!appleir)
> + goto fail;
> +
> + appleir->data = usb_buffer_alloc(dev, URB_SIZE, GFP_KERNEL,
> + &appleir->dma_buf);
> + if (!appleir->data)
> + goto fail;
> +
> + appleir->urb = usb_alloc_urb(0, GFP_KERNEL);
> + if (!appleir->urb)
> + goto fail;
> +
> + appleir->usbdev = dev;
> +
> + input_dev = input_allocate_device();
> + if (!input_dev)
> + goto fail;
> +
> + appleir->input_dev = input_dev;
> +
> + usb_make_path(dev, appleir->phys, sizeof(appleir->phys));
> + strlcpy(appleir->phys, "/input0", sizeof(appleir->phys));
> +
> + input_dev->name = "Apple Mac mini infrared remote control driver";
> + input_dev->phys = appleir->phys;
> + usb_to_input_id(dev, &input_dev->id);
> + input_dev->dev.parent = &intf->dev;
> + input_set_drvdata(input_dev, appleir);
> +
> + input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP);
> + input_dev->ledbit[0] = 0;
> +
> + for (i = 0; i < MAX_KEYS; i++)
> + set_bit(keymap[i], input_dev->keybit);
> +
> + clear_bit(0, input_dev->keybit);
> +
> + input_dev->open = appleir_open;
> + input_dev->close = appleir_close;
> +
> + endpoint = &intf->cur_altsetting->endpoint[0].desc;
> +
> + usb_fill_int_urb(appleir->urb, dev,
> + usb_rcvintpipe(dev, endpoint->bEndpointAddress),
> + appleir->data, 8,
> + appleir_urb, appleir, endpoint->bInterval);
> +
> + appleir->urb->transfer_dma = appleir->dma_buf;
> + appleir->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
> +
> + usb_set_intfdata(intf, appleir);
> +
> + init_timer(&appleir->key_up_timer);
> +
> + appleir->key_up_timer.function = key_up_tick;
> + appleir->key_up_timer.data = (unsigned long)appleir;
> +
> + appleir->timer_initted++;
> +
> + retval = input_register_device(appleir->input_dev);
> + if (retval)
> + goto fail;
> +
> + return 0;
> +
> +fail:
> + if (appleir) {
> + if (appleir->data)
> + usb_buffer_free(dev, URB_SIZE, appleir->data,
> + appleir->dma_buf);
> +
> + if (appleir->timer_initted)
> + del_timer_sync(&appleir->key_up_timer);
You dont need to do it here. The timer is guranteed to be not running
since it is starte din ->open().
> +
> + if (appleir->input_dev)
> + input_free_device(appleir->input_dev);
> +
> + kfree(appleir);
> + }
> +
> + return retval;
> +}
> +
> +static void appleir_disconnect(struct usb_interface *intf)
> +{
> + struct appleir *appleir = usb_get_intfdata(intf);
> +
> + usb_set_intfdata(intf, NULL);
> + if (appleir) {
> + input_unregister_device(appleir->input_dev);
> + if (appleir->timer_initted)
> + del_timer_sync(&appleir->key_up_timer);
> + usb_kill_urb(appleir->urb);
Already done in ->close()
> + usb_free_urb(appleir->urb);
> + usb_buffer_free(interface_to_usbdev(intf), URB_SIZE,
> + appleir->data, appleir->dma_buf);
> + kfree(appleir);
> + }
> +}
> +
> +static struct usb_driver appleir_driver = {
> + .name = "appleir",
> + .probe = appleir_probe,
> + .disconnect = appleir_disconnect,
> + .id_table = appleir_ids,
> +};
> +
> +static int __init appleir_init(void)
> +{
> + int retval;
> +
> + retval = usb_register(&appleir_driver);
> + if (retval)
> + goto out;
> + info(DRIVER_VERSION ":" DRIVER_DESC);
Do we need to print the driver identification? I personally like drivers
to be silent unless they find a device but if you prefer to have it
that's fine.
--
Dmitry
^ permalink raw reply [flat|nested] 57+ messages in thread
* Re: [PATCH] Input: add appleir USB driver
2008-05-14 22:15 Greg KH
2008-05-14 23:27 ` Matthew Garrett
2008-05-15 3:50 ` Dmitry Torokhov
@ 2008-05-15 13:21 ` Tino Keitel
[not found] ` <20080515132108.GA9327-z7fNteJZwjmqk56C3691EA@public.gmane.org>
[not found] ` <20080514221519.GA6575-U8xfFu+wG4EAvxtiuMwx3w@public.gmane.org>
3 siblings, 1 reply; 57+ messages in thread
From: Tino Keitel @ 2008-05-15 13:21 UTC (permalink / raw)
To: Greg KH; +Cc: Dmitry Torokhov, jkosina, linux-input, linux-usb, linux-kernel
On Wed, May 14, 2008 at 15:15:19 -0700, Greg KH wrote:
> From: Greg Kroah-Hartman <gregkh@suse.de>
>
> This driver was originally written by James McKenzie but forward ported
> and cleaned up by me to get it to work with modern kernel versions.
>
> Tested on my mac mini and it actually works!
>
> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
>
> ---
>
> Jiri, is it ok for this quirks addtion to go through Dmitry's triee? Or
> do you want me to split it out into two different patches?
>
> Dmitry, is this ok to go through your tree? Or I can take it as well if
> you don't want it :)
>
> drivers/hid/usbhid/hid-quirks.c | 2
> drivers/input/misc/Kconfig | 12 +
> drivers/input/misc/Makefile | 1
> drivers/input/misc/appleir.c | 361 ++++++++++++++++++++++++++++++++++++++++
> 4 files changed, 376 insertions(+)
>
> --- a/drivers/hid/usbhid/hid-quirks.c
> +++ b/drivers/hid/usbhid/hid-quirks.c
> @@ -77,6 +77,7 @@
> #define USB_DEVICE_ID_APPLE_ALU_WIRELESS_JIS 0x022e
> #define USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY 0x030a
> #define USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY 0x030b
> +#define USB_DEVICE_ID_APPLE_IRCONTROL 0x8240
> #define USB_DEVICE_ID_APPLE_IRCONTROL4 0x8242
>
> #define USB_VENDOR_ID_ASUS 0x0b05
> @@ -443,6 +444,7 @@ static const struct hid_blacklist {
> { USB_VENDOR_ID_AFATECH, USB_DEVICE_ID_AFATECH_AF9016, HID_QUIRK_FULLSPEED_INTERVAL },
>
> { USB_VENDOR_ID_BELKIN, USB_DEVICE_ID_FLIP_KVM, HID_QUIRK_HIDDEV },
> + { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL, HID_QUIRK_IGNORE },
> { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL4, HID_QUIRK_HIDDEV | HID_QUIRK_IGNORE_HIDINPUT },
> { USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_IR_REMOTE, HID_QUIRK_HIDDEV | HID_QUIRK_IGNORE_HIDINPUT },
> { USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_SIDEWINDER_GV, HID_QUIRK_HIDINPUT },
Hi,
I'm pretty sure that this breaks the macmini LIRC driver again, see
commit 3e1928e8793208802589aae851b6685671187242.
Regards,
Tino
^ permalink raw reply [flat|nested] 57+ messages in thread
[parent not found: <20080514221519.GA6575-U8xfFu+wG4EAvxtiuMwx3w@public.gmane.org>]
* Re: [PATCH] Input: add appleir USB driver
[not found] ` <20080514221519.GA6575-U8xfFu+wG4EAvxtiuMwx3w@public.gmane.org>
@ 2008-05-15 13:40 ` Tino Keitel
2008-05-15 18:41 ` Greg KH
0 siblings, 1 reply; 57+ messages in thread
From: Tino Keitel @ 2008-05-15 13:40 UTC (permalink / raw)
To: Greg KH
Cc: Dmitry Torokhov, jkosina-AlSwsSmVLrQ,
linux-input-u79uwXL29TY76Z2rM5mHXA,
linux-usb-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA
On Wed, May 14, 2008 at 15:15:19 -0700, Greg KH wrote:
> From: Greg Kroah-Hartman <gregkh-l3A5Bk7waGM@public.gmane.org>
>
> This driver was originally written by James McKenzie but forward ported
> and cleaned up by me to get it to work with modern kernel versions.
>
> Tested on my mac mini and it actually works!
Did you test suspend to RAM? Last time I used the original driver, I
had to reload it after resume, otherwise LIRC didn't receive events
anymore.
Regards,
Tino
--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply [flat|nested] 57+ messages in thread
* Re: [PATCH] Input: add appleir USB driver
2008-05-15 13:40 ` Tino Keitel
@ 2008-05-15 18:41 ` Greg KH
0 siblings, 0 replies; 57+ messages in thread
From: Greg KH @ 2008-05-15 18:41 UTC (permalink / raw)
To: Dmitry Torokhov, jkosina, linux-input, linux-usb, linux-kernel
On Thu, May 15, 2008 at 03:40:46PM +0200, Tino Keitel wrote:
> On Wed, May 14, 2008 at 15:15:19 -0700, Greg KH wrote:
> > From: Greg Kroah-Hartman <gregkh@suse.de>
> >
> > This driver was originally written by James McKenzie but forward ported
> > and cleaned up by me to get it to work with modern kernel versions.
> >
> > Tested on my mac mini and it actually works!
>
> Did you test suspend to RAM? Last time I used the original driver, I
> had to reload it after resume, otherwise LIRC didn't receive events
> anymore.
Yes, suspend to ram did seem to work for me with this driver. But I
haven't tested that in a while, will do so later today.
I don't use LIRC though, so I really can not speak for that chunk of
code at all.
thanks,
greg k-h
^ permalink raw reply [flat|nested] 57+ messages in thread
end of thread, other threads:[~2012-11-19 16:01 UTC | newest]
Thread overview: 57+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2010-04-21 13:51 [PATCH] Input: add appleir USB driver Bastien Nocera
-- strict thread matches above, loose matches on Subject: below --
2012-11-15 18:13 Bastien Nocera
2012-11-19 15:32 ` Benjamin Tissoires
2012-11-19 15:44 ` Bastien Nocera
2012-11-19 16:01 ` Benjamin Tissoires
2010-09-10 15:19 Bastien Nocera
2010-04-17 21:45 Bastien Nocera
2010-04-16 16:19 Bastien Nocera
2010-04-17 8:12 ` Dmitry Torokhov
2010-04-17 21:44 ` Bastien Nocera
2010-04-18 19:43 ` Dmitry Torokhov
2010-04-18 19:49 ` Bastien Nocera
2010-04-18 20:19 ` Dmitry Torokhov
2010-04-19 0:31 ` Bastien Nocera
2010-04-19 7:28 ` Dmitry Torokhov
2010-04-19 10:08 ` Bastien Nocera
2010-04-21 6:31 ` Dmitry Torokhov
2010-04-21 14:06 ` Bastien Nocera
2010-04-19 9:22 ` Jiri Kosina
2010-04-19 9:31 ` Bastien Nocera
2010-04-19 10:00 ` Jiri Kosina
2010-04-19 10:14 ` Bastien Nocera
2010-04-19 11:08 ` Jiri Kosina
2010-04-21 20:09 ` Dmitry Torokhov
2010-09-03 16:58 ` Bastien Nocera
2010-02-08 16:32 Bastien Nocera
2010-01-20 14:17 Bastien Nocera
2010-01-27 15:40 ` Jiri Kosina
2010-02-01 13:52 ` Bastien Nocera
2010-02-03 15:54 ` Jiri Kosina
2010-02-08 16:32 ` Bastien Nocera
2010-02-10 12:52 ` Jiri Kosina
2010-02-11 18:18 ` Bastien Nocera
2008-05-14 22:15 Greg KH
2008-05-14 23:27 ` Matthew Garrett
2008-05-14 23:49 ` Greg KH
2008-05-15 6:20 ` Sitsofe Wheeler
2008-05-15 3:50 ` Dmitry Torokhov
2008-05-15 13:21 ` Tino Keitel
[not found] ` <20080515132108.GA9327-z7fNteJZwjmqk56C3691EA@public.gmane.org>
2008-05-15 13:45 ` Dmitry Torokhov
2008-05-15 17:49 ` Tino Keitel
[not found] ` <20080515174939.GA10881-z7fNteJZwjmqk56C3691EA@public.gmane.org>
2008-05-15 18:35 ` Dmitry Torokhov
2008-05-15 20:59 ` Tino Keitel
2008-05-16 7:19 ` Jiri Kosina
2008-05-16 7:26 ` Tino Keitel
2008-05-16 13:13 ` Dmitry Torokhov
2008-05-16 13:32 ` Tino Keitel
[not found] ` <20080516133234.GA10193-Zv899e0YUSaDCaQdYfVI6sM6rOWSkUom@public.gmane.org>
2008-05-16 13:53 ` Dmitry Torokhov
[not found] ` <20080516095218.ZZRA012-NG0XCrj25/nJrYCpivWRnl5pS2h4L8biXqFh9Ls21Oc@public.gmane.org>
2008-05-16 14:07 ` Tino Keitel
2008-05-15 18:40 ` Greg KH
[not found] ` <20080515184034.GB15231-U8xfFu+wG4EAvxtiuMwx3w@public.gmane.org>
2008-05-15 20:59 ` Tino Keitel
[not found] ` <20080515205959.GA11683-z7fNteJZwjmqk56C3691EA@public.gmane.org>
2008-05-15 21:11 ` Greg KH
2008-05-15 23:27 ` Tino Keitel
2008-05-16 2:32 ` Greg KH
2008-05-16 5:44 ` Tino Keitel
[not found] ` <20080514221519.GA6575-U8xfFu+wG4EAvxtiuMwx3w@public.gmane.org>
2008-05-15 13:40 ` Tino Keitel
2008-05-15 18:41 ` Greg KH
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).