Jarod Wilson 8ccfe4
commit 92c912df2a0725d719263357176f98b2201a2acd
Jarod Wilson 8ccfe4
Author: Bastien Nocera <hadess@hadess.net>
Jarod Wilson 8ccfe4
Date:   Wed Apr 21 14:51:58 2010 +0100
Kyle McMartin d3a72e
Jarod Wilson 8ccfe4
    Input: add appleir USB driver
Jarod Wilson 8ccfe4
    
Jarod Wilson 8ccfe4
    This driver was originally written by James McKenzie, updated by
Jarod Wilson 8ccfe4
    Greg Kroah-Hartman, further updated by myself, with suspend support
Jarod Wilson 8ccfe4
    added.
Jarod Wilson 8ccfe4
    
Jarod Wilson 8ccfe4
    More recent versions of the IR receiver are also supported through
Jarod Wilson 8ccfe4
    a patch by Alex Karpenko.
Jarod Wilson 8ccfe4
    
Jarod Wilson 8ccfe4
    Tested on a MacbookAir1,1
Jarod Wilson 8ccfe4
    
Jarod Wilson 8ccfe4
    Signed-off-by: Bastien Nocera <hadess@hadess.net>
Kyle McMartin d3a72e
Jarod Wilson 8ccfe4
commit 6ffcbf68913840e9e882db14441576ffee6eba0c
Jarod Wilson 8ccfe4
Author: Bastien Nocera <hadess@hadess.net>
Jarod Wilson 8ccfe4
Date:   Fri Apr 16 17:19:50 2010 +0100
Jarod Wilson 8ccfe4
Jarod Wilson 8ccfe4
    Add HID_QUIRK_HIDDEV_FORCE and HID_QUIRK_NO_IGNORE
Jarod Wilson 8ccfe4
    
Jarod Wilson 8ccfe4
    Add two quirks to make it possible for usbhid module options to
Jarod Wilson 8ccfe4
    override whether a device is ignored (HID_QUIRK_NO_IGNORE) and
Jarod Wilson 8ccfe4
    whether to connect a hiddev device (HID_QUIRK_HIDDEV_FORCE).
Jarod Wilson 8ccfe4
    
Jarod Wilson 8ccfe4
    Passing HID_QUIRK_NO_IGNORE for your device means that it will
Jarod Wilson 8ccfe4
    not be ignored by the HID layer, even if present in a blacklist.
Jarod Wilson 8ccfe4
    
Jarod Wilson 8ccfe4
    HID_QUIRK_HIDDEV_FORCE will force the creation of a hiddev for that
Jarod Wilson 8ccfe4
    device, making it accessible from user-space.
Jarod Wilson 8ccfe4
    
Jarod Wilson 8ccfe4
    Tested with an Apple IR Receiver, switching it from using appleir
Jarod Wilson 8ccfe4
    to using lirc's macmini driver.
Jarod Wilson 8ccfe4
    
Jarod Wilson 8ccfe4
    Signed-off-by: Bastien Nocera <hadess@hadess.net>
Jarod Wilson 8ccfe4
Jarod Wilson 8ccfe4
diff --git a/Documentation/input/appleir.txt b/Documentation/input/appleir.txt
Jarod Wilson 8ccfe4
new file mode 100644
Jarod Wilson 8ccfe4
index 0000000..0267a4b
Jarod Wilson 8ccfe4
--- /dev/null
Jarod Wilson 8ccfe4
+++ b/Documentation/input/appleir.txt
Jarod Wilson 8ccfe4
@@ -0,0 +1,45 @@
Jarod Wilson 8ccfe4
+Apple IR receiver Driver (appleir)
Jarod Wilson 8ccfe4
+----------------------------------
Jarod Wilson 8ccfe4
+	Copyright (C) 2009 Bastien Nocera <hadess@hadess.net>
Jarod Wilson 8ccfe4
+
Jarod Wilson 8ccfe4
+The appleir driver is a kernel input driver to handle Apple's IR
Jarod Wilson 8ccfe4
+receivers (and associated remotes) in the kernel.
Jarod Wilson 8ccfe4
+
Jarod Wilson 8ccfe4
+The driver is an input driver which only handles "official" remotes
Jarod Wilson 8ccfe4
+as built and sold by Apple.
Jarod Wilson 8ccfe4
+
Jarod Wilson 8ccfe4
+Authors
Jarod Wilson 8ccfe4
+-------
Jarod Wilson 8ccfe4
+
Jarod Wilson 8ccfe4
+James McKenzie (original driver)
Jarod Wilson 8ccfe4
+Alex Karpenko (05ac:8242 support)
Jarod Wilson 8ccfe4
+Greg Kroah-Hartman (cleanups and original submission)
Jarod Wilson 8ccfe4
+Bastien Nocera (further cleanups and suspend support)
Jarod Wilson 8ccfe4
+
Jarod Wilson 8ccfe4
+Supported hardware
Jarod Wilson 8ccfe4
+------------------
Jarod Wilson 8ccfe4
+
Jarod Wilson 8ccfe4
+- All Apple laptops and desktops from 2005 onwards, except:
Jarod Wilson 8ccfe4
+  - the unibody Macbook (2009)
Jarod Wilson 8ccfe4
+  - Mac Pro (all versions)
Jarod Wilson 8ccfe4
+- Apple TV (all revisions)
Jarod Wilson 8ccfe4
+
Jarod Wilson 8ccfe4
+The remote will only support the 6 buttons of the original remotes
Jarod Wilson 8ccfe4
+as sold by Apple. See the next section if you want to use other remotes
Jarod Wilson 8ccfe4
+or want to use lirc with the device instead of the kernel driver.
Jarod Wilson 8ccfe4
+
Jarod Wilson 8ccfe4
+Using lirc (native) instead of the kernel driver
Jarod Wilson 8ccfe4
+------------------------------------------------
Jarod Wilson 8ccfe4
+
Jarod Wilson 8ccfe4
+First, you will need to disable the kernel driver for the receiver.
Jarod Wilson 8ccfe4
+
Jarod Wilson 8ccfe4
+This can be achieved by passing quirks to the usbhid driver.
Jarod Wilson 8ccfe4
+The quirk line would be:
Jarod Wilson 8ccfe4
+usbhid.quirks=0x05ac:0x8242:0x40000010
Jarod Wilson 8ccfe4
+
Jarod Wilson 8ccfe4
+With 0x05ac being the vendor ID (Apple, you shouldn't need to change this)
Jarod Wilson 8ccfe4
+With 0x8242 being the product ID (check the output of lsusb for your hardware)
Jarod Wilson 8ccfe4
+And 0x10 being "HID_QUIRK_HIDDEV_FORCE" and 0x40000000 being "HID_QUIRK_NO_IGNORE"
Jarod Wilson 8ccfe4
+
Jarod Wilson 8ccfe4
+This should force the creation of a hiddev device for the receiver, and
Jarod Wilson 8ccfe4
+make it usable under lirc.
Kyle McMartin d3a72e
diff --git a/drivers/hid/hid-apple.c b/drivers/hid/hid-apple.c
Jarod Wilson 8ccfe4
index 5b4d66d..b0e1811 100644
Kyle McMartin d3a72e
--- a/drivers/hid/hid-apple.c
Kyle McMartin d3a72e
+++ b/drivers/hid/hid-apple.c
Jarod Wilson 8ccfe4
@@ -353,10 +353,6 @@ static void apple_remove(struct hid_device *hdev)
Kyle McMartin d3a72e
 }
Kyle McMartin d3a72e
 
Kyle McMartin d3a72e
 static const struct hid_device_id apple_devices[] = {
Kyle McMartin d3a72e
-	{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ATV_IRCONTROL),
Kyle McMartin d3a72e
-		.driver_data = APPLE_HIDDEV | APPLE_IGNORE_HIDINPUT },
Kyle McMartin d3a72e
-	{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL4),
Kyle McMartin d3a72e
-		.driver_data = APPLE_HIDDEV | APPLE_IGNORE_HIDINPUT },
Kyle McMartin d3a72e
 	{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MIGHTYMOUSE),
Kyle McMartin d3a72e
 		.driver_data = APPLE_MIGHTYMOUSE | APPLE_INVERT_HWHEEL },
Kyle McMartin d3a72e
 
Kyle McMartin d3a72e
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
Jarod Wilson 8ccfe4
index 793691f..9255c1a 100644
Kyle McMartin d3a72e
--- a/drivers/hid/hid-ids.h
Kyle McMartin d3a72e
+++ b/drivers/hid/hid-ids.h
Jarod Wilson 8ccfe4
@@ -93,6 +93,7 @@
Kyle McMartin d3a72e
 #define USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_JIS   0x023b
Kyle McMartin d3a72e
 #define USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY	0x030a
Kyle McMartin d3a72e
 #define USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY	0x030b
Kyle McMartin d3a72e
+#define USB_DEVICE_ID_APPLE_IRCONTROL	0x8240
Kyle McMartin d3a72e
 #define USB_DEVICE_ID_APPLE_ATV_IRCONTROL	0x8241
Kyle McMartin d3a72e
 #define USB_DEVICE_ID_APPLE_IRCONTROL4	0x8242
Kyle McMartin d3a72e
 
Kyle McMartin d3a72e
diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
Jarod Wilson 8ccfe4
index 16ec523..4340986 100644
Kyle McMartin d3a72e
--- a/drivers/input/misc/Kconfig
Kyle McMartin d3a72e
+++ b/drivers/input/misc/Kconfig
Jarod Wilson 8ccfe4
@@ -149,6 +149,19 @@ config INPUT_KEYSPAN_REMOTE
Kyle McMartin d3a72e
 	  To compile this driver as a module, choose M here: the module will
Kyle McMartin d3a72e
 	  be called keyspan_remote.
Kyle McMartin d3a72e
 
Kyle McMartin d3a72e
+config INPUT_APPLEIR
Kyle McMartin d3a72e
+	tristate "Apple infrared receiver (built in)"
Kyle McMartin d3a72e
+	depends on USB_ARCH_HAS_HCD
Kyle McMartin d3a72e
+	select USB
Kyle McMartin d3a72e
+	help
Kyle McMartin d3a72e
+	  Say Y here if you want to use a Apple infrared remote control. All
Kyle McMartin d3a72e
+	  the Apple computers from 2005 onwards include such a port, except
Kyle McMartin d3a72e
+	  the unibody Macbook (2009), and Mac Pros. This receiver is also
Kyle McMartin d3a72e
+	  used in the Apple TV set-top box.
Kyle McMartin d3a72e
+
Kyle McMartin d3a72e
+	  To compile this driver as a module, choose M here: the module will
Kyle McMartin d3a72e
+	  be called appleir.
Kyle McMartin d3a72e
+
Kyle McMartin d3a72e
 config INPUT_POWERMATE
Kyle McMartin d3a72e
 	tristate "Griffin PowerMate and Contour Jog support"
Kyle McMartin d3a72e
 	depends on USB_ARCH_HAS_HCD
Kyle McMartin d3a72e
diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
Jarod Wilson 8ccfe4
index a8b8485..041e6f5 100644
Kyle McMartin d3a72e
--- a/drivers/input/misc/Makefile
Kyle McMartin d3a72e
+++ b/drivers/input/misc/Makefile
Jarod Wilson 8ccfe4
@@ -5,6 +5,7 @@
Kyle McMartin d3a72e
 obj-$(CONFIG_INPUT_AD714X_I2C)		+= ad714x-i2c.o
Kyle McMartin d3a72e
 obj-$(CONFIG_INPUT_AD714X_SPI)		+= ad714x-spi.o
Kyle McMartin d3a72e
 obj-$(CONFIG_INPUT_APANEL)		+= apanel.o
Kyle McMartin d3a72e
+obj-$(CONFIG_INPUT_APPLEIR)		+= appleir.o
Kyle McMartin d3a72e
 obj-$(CONFIG_INPUT_ATI_REMOTE)		+= ati_remote.o
Kyle McMartin d3a72e
 obj-$(CONFIG_INPUT_ATI_REMOTE2)		+= ati_remote2.o
Kyle McMartin d3a72e
 obj-$(CONFIG_INPUT_ATLAS_BTNS)		+= atlas_btns.o
Jarod Wilson 8ccfe4
diff --git a/drivers/input/misc/appleir.c b/drivers/input/misc/appleir.c
Jarod Wilson 8ccfe4
new file mode 100644
Jarod Wilson 8ccfe4
index 0000000..cff4df6
Jarod Wilson 8ccfe4
--- /dev/null
Jarod Wilson 8ccfe4
+++ b/drivers/input/misc/appleir.c
Jarod Wilson 8ccfe4
@@ -0,0 +1,453 @@
Jarod Wilson 8ccfe4
+/*
Jarod Wilson 8ccfe4
+ * appleir: USB driver for the apple ir device
Jarod Wilson 8ccfe4
+ *
Jarod Wilson 8ccfe4
+ * Original driver written by James McKenzie
Jarod Wilson 8ccfe4
+ * Ported to recent 2.6 kernel versions by Greg Kroah-Hartman <gregkh@suse.de>
Jarod Wilson 8ccfe4
+ *
Jarod Wilson 8ccfe4
+ * Copyright (C) 2006 James McKenzie
Jarod Wilson 8ccfe4
+ * Copyright (C) 2008 Greg Kroah-Hartman <greg@kroah.com>
Jarod Wilson 8ccfe4
+ * Copyright (C) 2008 Novell Inc.
Jarod Wilson 8ccfe4
+ *
Jarod Wilson 8ccfe4
+ * This program is free software; you can redistribute it and/or modify it
Jarod Wilson 8ccfe4
+ * under the terms of the GNU General Public License as published by the Free
Jarod Wilson 8ccfe4
+ * Software Foundation, version 2.
Jarod Wilson 8ccfe4
+ *
Jarod Wilson 8ccfe4
+ */
Jarod Wilson 8ccfe4
+
Jarod Wilson 8ccfe4
+#include <linux kernel.h="">
Jarod Wilson 8ccfe4
+#include <linux slab.h="">
Jarod Wilson 8ccfe4
+#include <linux input.h="">
Jarod Wilson 8ccfe4
+#include <linux usb="" input.h="">
Jarod Wilson 8ccfe4
+#include <linux module.h="">
Jarod Wilson 8ccfe4
+#include <linux init.h="">
Jarod Wilson 8ccfe4
+#include <linux usb.h="">
Jarod Wilson 8ccfe4
+#include <linux usb="" input.h="">
Jarod Wilson 8ccfe4
+#include <asm unaligned.h="">
Jarod Wilson 8ccfe4
+#include <asm byteorder.h="">
Jarod Wilson 8ccfe4
+
Jarod Wilson 8ccfe4
+#define DRIVER_VERSION "v1.2"
Jarod Wilson 8ccfe4
+#define DRIVER_AUTHOR "James McKenzie"
Jarod Wilson 8ccfe4
+#define DRIVER_DESC "Apple infrared receiver driver"
Jarod Wilson 8ccfe4
+#define DRIVER_LICENSE "GPL"
Jarod Wilson 8ccfe4
+
Jarod Wilson 8ccfe4
+MODULE_AUTHOR(DRIVER_AUTHOR);
Jarod Wilson 8ccfe4
+MODULE_DESCRIPTION(DRIVER_DESC);
Jarod Wilson 8ccfe4
+MODULE_LICENSE(DRIVER_LICENSE);
Jarod Wilson 8ccfe4
+
Jarod Wilson 8ccfe4
+#define USB_VENDOR_ID_APPLE			0x05ac
Jarod Wilson 8ccfe4
+#define USB_DEVICE_ID_APPLE_IRCONTROL		0x8240
Jarod Wilson 8ccfe4
+#define USB_DEVICE_ID_APPLE_ATV_IRCONTROL	0x8241
Jarod Wilson 8ccfe4
+#define USB_DEVICE_ID_APPLE_IRCONTROL4		0x8242
Jarod Wilson 8ccfe4
+
Jarod Wilson 8ccfe4
+#define URB_SIZE	32
Jarod Wilson 8ccfe4
+
Jarod Wilson 8ccfe4
+#define MAX_KEYS	8
Jarod Wilson 8ccfe4
+#define MAX_KEYS_MASK	(MAX_KEYS - 1)
Jarod Wilson 8ccfe4
+
Jarod Wilson 8ccfe4
+#define dbginfo(dev, format, arg...) do { if (debug) dev_info(dev , format , ## arg); } while (0)
Jarod Wilson 8ccfe4
+
Jarod Wilson 8ccfe4
+static int debug;
Jarod Wilson 8ccfe4
+module_param(debug, int, 0644);
Jarod Wilson 8ccfe4
+MODULE_PARM_DESC(debug, "Enable extra debug messages and information");
Jarod Wilson 8ccfe4
+
Jarod Wilson 8ccfe4
+/* I have two devices both of which report the following */
Jarod Wilson 8ccfe4
+/* 25 87 ee 83 0a  	+  */
Jarod Wilson 8ccfe4
+/* 25 87 ee 83 0c  	-  */
Jarod Wilson 8ccfe4
+/* 25 87 ee 83 09	<< */
Jarod Wilson 8ccfe4
+/* 25 87 ee 83 06	>> */
Jarod Wilson 8ccfe4
+/* 25 87 ee 83 05	>" */
Jarod Wilson 8ccfe4
+/* 25 87 ee 83 03	menu */
Jarod Wilson 8ccfe4
+/* 26 00 00 00 00	for key repeat*/
Jarod Wilson 8ccfe4
+
Jarod Wilson 8ccfe4
+/* Thomas Glanzmann reports the following responses */
Jarod Wilson 8ccfe4
+/* 25 87 ee ca 0b	+  */
Jarod Wilson 8ccfe4
+/* 25 87 ee ca 0d	-  */
Jarod Wilson 8ccfe4
+/* 25 87 ee ca 08	<< */
Jarod Wilson 8ccfe4
+/* 25 87 ee ca 07	>> */
Jarod Wilson 8ccfe4
+/* 25 87 ee ca 04	>" */
Jarod Wilson 8ccfe4
+/* 25 87 ee ca 02 	menu */
Jarod Wilson 8ccfe4
+/* 26 00 00 00 00       for key repeat*/
Jarod Wilson 8ccfe4
+/* He also observes the following event sometimes */
Jarod Wilson 8ccfe4
+/* sent after a key is release, which I interpret */
Jarod Wilson 8ccfe4
+/* as a flat battery message */
Jarod Wilson 8ccfe4
+/* 25 87 e0 ca 06	flat battery */
Jarod Wilson 8ccfe4
+
Jarod Wilson 8ccfe4
+/* Alexandre Karpenko reports the following responses for Device ID 0x8242 */
Jarod Wilson 8ccfe4
+/* 25 87 ee 47 0b	+  */
Jarod Wilson 8ccfe4
+/* 25 87 ee 47 0d	-  */
Jarod Wilson 8ccfe4
+/* 25 87 ee 47 08	<< */
Jarod Wilson 8ccfe4
+/* 25 87 ee 47 07	>> */
Jarod Wilson 8ccfe4
+/* 25 87 ee 47 04	>" */
Jarod Wilson 8ccfe4
+/* 25 87 ee 47 02 	menu */
Jarod Wilson 8ccfe4
+/* 26 87 ee 47 ** 	for key repeat (** is the code of the key being held) */
Jarod Wilson 8ccfe4
+
Jarod Wilson 8ccfe4
+static const unsigned short appleir_key_table[] = {
Jarod Wilson 8ccfe4
+	KEY_RESERVED,
Jarod Wilson 8ccfe4
+	KEY_MENU,
Jarod Wilson 8ccfe4
+	KEY_PLAYPAUSE,
Jarod Wilson 8ccfe4
+	KEY_FORWARD,
Jarod Wilson 8ccfe4
+	KEY_BACK,
Jarod Wilson 8ccfe4
+	KEY_VOLUMEUP,
Jarod Wilson 8ccfe4
+	KEY_VOLUMEDOWN,
Jarod Wilson 8ccfe4
+	KEY_RESERVED,
Jarod Wilson 8ccfe4
+};
Jarod Wilson 8ccfe4
+
Jarod Wilson 8ccfe4
+struct appleir {
Jarod Wilson 8ccfe4
+	struct input_dev *input_dev;
Jarod Wilson 8ccfe4
+	unsigned short keymap[ARRAY_SIZE(appleir_key_table)];
Jarod Wilson 8ccfe4
+	u8 *data;
Jarod Wilson 8ccfe4
+	dma_addr_t dma_buf;
Jarod Wilson 8ccfe4
+	struct usb_device *usbdev;
Jarod Wilson 8ccfe4
+	unsigned int flags;
Jarod Wilson 8ccfe4
+	struct urb *urb;
Jarod Wilson 8ccfe4
+	struct timer_list key_up_timer;
Jarod Wilson 8ccfe4
+	int current_key;
Jarod Wilson 8ccfe4
+	char phys[32];
Jarod Wilson 8ccfe4
+};
Jarod Wilson 8ccfe4
+
Jarod Wilson 8ccfe4
+static DEFINE_MUTEX(appleir_mutex);
Jarod Wilson 8ccfe4
+
Jarod Wilson 8ccfe4
+enum {
Jarod Wilson 8ccfe4
+	APPLEIR_OPENED = 0x1,
Jarod Wilson 8ccfe4
+	APPLEIR_SUSPENDED = 0x2,
Jarod Wilson 8ccfe4
+};
Jarod Wilson 8ccfe4
+
Jarod Wilson 8ccfe4
+static struct usb_device_id appleir_ids[] = {
Jarod Wilson 8ccfe4
+	{ USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL) },
Jarod Wilson 8ccfe4
+	{ USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ATV_IRCONTROL) },
Jarod Wilson 8ccfe4
+	{ USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL4) },
Jarod Wilson 8ccfe4
+	{}
Jarod Wilson 8ccfe4
+};
Jarod Wilson 8ccfe4
+MODULE_DEVICE_TABLE(usb, appleir_ids);
Jarod Wilson 8ccfe4
+
Jarod Wilson 8ccfe4
+static void dump_packet(struct appleir *appleir, char *msg, u8 *data, int len)
Jarod Wilson 8ccfe4
+{
Jarod Wilson 8ccfe4
+	int i;
Jarod Wilson 8ccfe4
+
Jarod Wilson 8ccfe4
+	printk(KERN_ERR "appleir: %s (%d bytes)", msg, len);
Jarod Wilson 8ccfe4
+
Jarod Wilson 8ccfe4
+	for (i = 0; i < len; ++i)
Jarod Wilson 8ccfe4
+		printk(" %02x", data[i]);
Jarod Wilson 8ccfe4
+	printk("\n");
Jarod Wilson 8ccfe4
+}
Jarod Wilson 8ccfe4
+
Jarod Wilson 8ccfe4
+static void key_up(struct appleir *appleir, int key)
Jarod Wilson 8ccfe4
+{
Jarod Wilson 8ccfe4
+	dbginfo(&appleir->input_dev->dev, "key %d up\n", key);
Jarod Wilson 8ccfe4
+	input_report_key(appleir->input_dev, key, 0);
Jarod Wilson 8ccfe4
+	input_sync(appleir->input_dev);
Jarod Wilson 8ccfe4
+}
Jarod Wilson 8ccfe4
+
Jarod Wilson 8ccfe4
+static void key_down(struct appleir *appleir, int key)
Jarod Wilson 8ccfe4
+{
Jarod Wilson 8ccfe4
+	dbginfo(&appleir->input_dev->dev, "key %d down\n", key);
Jarod Wilson 8ccfe4
+	input_report_key(appleir->input_dev, key, 1);
Jarod Wilson 8ccfe4
+	input_sync(appleir->input_dev);
Jarod Wilson 8ccfe4
+}
Jarod Wilson 8ccfe4
+
Jarod Wilson 8ccfe4
+static void battery_flat(struct appleir *appleir)
Jarod Wilson 8ccfe4
+{
Jarod Wilson 8ccfe4
+	dev_err(&appleir->input_dev->dev, "possible flat battery?\n");
Jarod Wilson 8ccfe4
+}
Jarod Wilson 8ccfe4
+
Jarod Wilson 8ccfe4
+static void key_up_tick(unsigned long data)
Jarod Wilson 8ccfe4
+{
Jarod Wilson 8ccfe4
+	struct appleir *appleir = (struct appleir *)data;
Jarod Wilson 8ccfe4
+
Jarod Wilson 8ccfe4
+	if (appleir->current_key) {
Jarod Wilson 8ccfe4
+		key_up(appleir, appleir->current_key);
Jarod Wilson 8ccfe4
+		appleir->current_key = 0;
Jarod Wilson 8ccfe4
+	}
Jarod Wilson 8ccfe4
+}
Jarod Wilson 8ccfe4
+
Jarod Wilson 8ccfe4
+static void new_data(struct appleir *appleir, u8 *data, int len)
Jarod Wilson 8ccfe4
+{
Jarod Wilson 8ccfe4
+	static const u8 keydown[] = { 0x25, 0x87, 0xee };
Jarod Wilson 8ccfe4
+	static const u8 keyrepeat[] = { 0x26, };
Jarod Wilson 8ccfe4
+	static const u8 flatbattery[] = { 0x25, 0x87, 0xe0 };
Jarod Wilson 8ccfe4
+
Jarod Wilson 8ccfe4
+	if (debug)
Jarod Wilson 8ccfe4
+		dump_packet(appleir, "received", data, len);
Jarod Wilson 8ccfe4
+
Jarod Wilson 8ccfe4
+	if (len != 5)
Jarod Wilson 8ccfe4
+		return;
Jarod Wilson 8ccfe4
+
Jarod Wilson 8ccfe4
+	if (!memcmp(data, keydown, sizeof(keydown))) {
Jarod Wilson 8ccfe4
+		/* If we already have a key down, take it up before marking
Jarod Wilson 8ccfe4
+		   this one down */
Jarod Wilson 8ccfe4
+		if (appleir->current_key)
Jarod Wilson 8ccfe4
+			key_up(appleir, appleir->current_key);
Jarod Wilson 8ccfe4
+		appleir->current_key = appleir->keymap[(data[4] >> 1) & MAX_KEYS_MASK];
Jarod Wilson 8ccfe4
+
Jarod Wilson 8ccfe4
+		key_down(appleir, appleir->current_key);
Jarod Wilson 8ccfe4
+		/* Remote doesn't do key up, either pull them up, in the test
Jarod Wilson 8ccfe4
+		   above, or here set a timer which pulls them up after 1/8 s */
Jarod Wilson 8ccfe4
+		mod_timer(&appleir->key_up_timer, jiffies + HZ / 8);
Jarod Wilson 8ccfe4
+
Jarod Wilson 8ccfe4
+		return;
Jarod Wilson 8ccfe4
+	}
Jarod Wilson 8ccfe4
+
Jarod Wilson 8ccfe4
+	if (!memcmp(data, keyrepeat, sizeof(keyrepeat))) {
Jarod Wilson 8ccfe4
+		key_down(appleir, appleir->current_key);
Jarod Wilson 8ccfe4
+		/* Remote doesn't do key up, either pull them up, in the test
Jarod Wilson 8ccfe4
+		   above, or here set a timer which pulls them up after 1/8 s */
Jarod Wilson 8ccfe4
+		mod_timer(&appleir->key_up_timer, jiffies + HZ / 8);
Jarod Wilson 8ccfe4
+		return;
Jarod Wilson 8ccfe4
+	}
Jarod Wilson 8ccfe4
+
Jarod Wilson 8ccfe4
+	if (!memcmp(data, flatbattery, sizeof(flatbattery))) {
Jarod Wilson 8ccfe4
+		battery_flat(appleir);
Jarod Wilson 8ccfe4
+		/* Fall through */
Jarod Wilson 8ccfe4
+	}
Jarod Wilson 8ccfe4
+
Jarod Wilson 8ccfe4
+	dump_packet(appleir, "unknown packet", data, len);
Jarod Wilson 8ccfe4
+}
Jarod Wilson 8ccfe4
+
Jarod Wilson 8ccfe4
+static void appleir_urb(struct urb *urb)
Jarod Wilson 8ccfe4
+{
Jarod Wilson 8ccfe4
+	struct appleir *appleir = urb->context;
Jarod Wilson 8ccfe4
+	int status = urb->status;
Jarod Wilson 8ccfe4
+	int retval;
Jarod Wilson 8ccfe4
+
Jarod Wilson 8ccfe4
+	switch (status) {
Jarod Wilson 8ccfe4
+	case 0:
Jarod Wilson 8ccfe4
+		new_data(appleir, urb->transfer_buffer, urb->actual_length);
Jarod Wilson 8ccfe4
+		break;
Jarod Wilson 8ccfe4
+	case -ECONNRESET:
Jarod Wilson 8ccfe4
+	case -ENOENT:
Jarod Wilson 8ccfe4
+	case -ESHUTDOWN:
Jarod Wilson 8ccfe4
+		/* This urb is terminated, clean up */
Jarod Wilson 8ccfe4
+		dbginfo(&appleir->input_dev->dev, "%s - urb shutting down with status: %d", __func__,
Jarod Wilson 8ccfe4
+			urb->status);
Jarod Wilson 8ccfe4
+		return;
Jarod Wilson 8ccfe4
+	default:
Jarod Wilson 8ccfe4
+		dbginfo(&appleir->input_dev->dev, "%s - nonzero urb status received: %d", __func__,
Jarod Wilson 8ccfe4
+			urb->status);
Jarod Wilson 8ccfe4
+	}
Jarod Wilson 8ccfe4
+
Jarod Wilson 8ccfe4
+	retval = usb_submit_urb(urb, GFP_ATOMIC);
Jarod Wilson 8ccfe4
+	if (retval)
Jarod Wilson 8ccfe4
+		err("%s - usb_submit_urb failed with result %d", __func__,
Jarod Wilson 8ccfe4
+		    retval);
Jarod Wilson 8ccfe4
+}
Jarod Wilson 8ccfe4
+
Jarod Wilson 8ccfe4
+static int appleir_open(struct input_dev *dev)
Jarod Wilson 8ccfe4
+{
Jarod Wilson 8ccfe4
+	struct appleir *appleir = input_get_drvdata(dev);
Jarod Wilson 8ccfe4
+	struct usb_interface *intf = usb_ifnum_to_if(appleir->usbdev, 0);
Jarod Wilson 8ccfe4
+	int r;
Jarod Wilson 8ccfe4
+
Jarod Wilson 8ccfe4
+	r = usb_autopm_get_interface(intf);
Jarod Wilson 8ccfe4
+	if (r) {
Jarod Wilson 8ccfe4
+		dev_err(&intf->dev,
Jarod Wilson 8ccfe4
+			"%s(): usb_autopm_get_interface() = %d\n", __func__, r);
Jarod Wilson 8ccfe4
+		return r;
Jarod Wilson 8ccfe4
+	}
Jarod Wilson 8ccfe4
+
Jarod Wilson 8ccfe4
+	mutex_lock(&appleir_mutex);
Jarod Wilson 8ccfe4
+
Jarod Wilson 8ccfe4
+	if (usb_submit_urb(appleir->urb, GFP_ATOMIC)) {
Jarod Wilson 8ccfe4
+		r = -EIO;
Jarod Wilson 8ccfe4
+		goto fail;
Jarod Wilson 8ccfe4
+	}
Jarod Wilson 8ccfe4
+
Jarod Wilson 8ccfe4
+	appleir->flags |= APPLEIR_OPENED;
Jarod Wilson 8ccfe4
+
Jarod Wilson 8ccfe4
+	mutex_unlock(&appleir_mutex);
Jarod Wilson 8ccfe4
+
Jarod Wilson 8ccfe4
+	usb_autopm_put_interface(intf);
Jarod Wilson 8ccfe4
+
Jarod Wilson 8ccfe4
+	return 0;
Jarod Wilson 8ccfe4
+fail:
Jarod Wilson 8ccfe4
+	mutex_unlock(&appleir_mutex);
Jarod Wilson 8ccfe4
+	usb_autopm_put_interface(intf);
Jarod Wilson 8ccfe4
+	return r;
Jarod Wilson 8ccfe4
+}
Jarod Wilson 8ccfe4
+
Jarod Wilson 8ccfe4
+static void appleir_close(struct input_dev *dev)
Jarod Wilson 8ccfe4
+{
Jarod Wilson 8ccfe4
+	struct appleir *appleir = input_get_drvdata(dev);
Jarod Wilson 8ccfe4
+
Jarod Wilson 8ccfe4
+	mutex_lock(&appleir_mutex);
Jarod Wilson 8ccfe4
+
Jarod Wilson 8ccfe4
+	if (!(appleir->flags & APPLEIR_SUSPENDED)) {
Jarod Wilson 8ccfe4
+		usb_kill_urb(appleir->urb);
Jarod Wilson 8ccfe4
+		del_timer_sync(&appleir->key_up_timer);
Jarod Wilson 8ccfe4
+	}
Jarod Wilson 8ccfe4
+
Jarod Wilson 8ccfe4
+	appleir->flags &= ~APPLEIR_OPENED;
Jarod Wilson 8ccfe4
+
Jarod Wilson 8ccfe4
+	mutex_unlock(&appleir_mutex);
Jarod Wilson 8ccfe4
+}
Jarod Wilson 8ccfe4
+
Jarod Wilson 8ccfe4
+static int appleir_probe(struct usb_interface *intf,
Jarod Wilson 8ccfe4
+			 const struct usb_device_id *id)
Jarod Wilson 8ccfe4
+{
Jarod Wilson 8ccfe4
+	struct usb_device *dev = interface_to_usbdev(intf);
Jarod Wilson 8ccfe4
+	struct usb_endpoint_descriptor *endpoint;
Jarod Wilson 8ccfe4
+	struct appleir *appleir = NULL;
Jarod Wilson 8ccfe4
+	struct input_dev *input_dev;
Jarod Wilson 8ccfe4
+	int retval = -ENOMEM;
Jarod Wilson 8ccfe4
+	int i;
Jarod Wilson 8ccfe4
+
Jarod Wilson 8ccfe4
+	appleir = kzalloc(sizeof(struct appleir), GFP_KERNEL);
Jarod Wilson 8ccfe4
+	if (!appleir)
Jarod Wilson 8ccfe4
+		goto allocfail;
Jarod Wilson 8ccfe4
+
Jarod Wilson 8ccfe4
+	appleir->data = usb_alloc_coherent(dev, URB_SIZE, GFP_KERNEL,
Jarod Wilson 8ccfe4
+					 &appleir->dma_buf);
Jarod Wilson 8ccfe4
+	if (!appleir->data)
Jarod Wilson 8ccfe4
+		goto usbfail;
Jarod Wilson 8ccfe4
+
Jarod Wilson 8ccfe4
+	appleir->urb = usb_alloc_urb(0, GFP_KERNEL);
Jarod Wilson 8ccfe4
+	if (!appleir->urb)
Jarod Wilson 8ccfe4
+		goto urbfail;
Jarod Wilson 8ccfe4
+
Jarod Wilson 8ccfe4
+	appleir->usbdev = dev;
Jarod Wilson 8ccfe4
+
Jarod Wilson 8ccfe4
+	input_dev = input_allocate_device();
Jarod Wilson 8ccfe4
+	if (!input_dev)
Jarod Wilson 8ccfe4
+		goto inputfail;
Jarod Wilson 8ccfe4
+
Jarod Wilson 8ccfe4
+	appleir->input_dev = input_dev;
Jarod Wilson 8ccfe4
+
Jarod Wilson 8ccfe4
+	usb_make_path(dev, appleir->phys, sizeof(appleir->phys));
Jarod Wilson 8ccfe4
+	strlcpy(appleir->phys, "/input0", sizeof(appleir->phys));
Jarod Wilson 8ccfe4
+
Jarod Wilson 8ccfe4
+	input_dev->name = "Apple Infrared Remote Controller";
Jarod Wilson 8ccfe4
+	input_dev->phys = appleir->phys;
Jarod Wilson 8ccfe4
+	usb_to_input_id(dev, &input_dev->id);
Jarod Wilson 8ccfe4
+	input_dev->dev.parent = &intf->dev;
Jarod Wilson 8ccfe4
+	input_dev->keycode = appleir->keymap;
Jarod Wilson 8ccfe4
+	input_dev->keycodesize = sizeof(unsigned short);
Jarod Wilson 8ccfe4
+	input_dev->keycodemax = ARRAY_SIZE(appleir->keymap);
Jarod Wilson 8ccfe4
+
Jarod Wilson 8ccfe4
+	input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP);
Jarod Wilson 8ccfe4
+
Jarod Wilson 8ccfe4
+	memcpy(appleir->keymap, appleir_key_table, sizeof(appleir->keymap));
Jarod Wilson 8ccfe4
+	for (i = 0; i < ARRAY_SIZE(appleir_key_table); i++)
Jarod Wilson 8ccfe4
+		set_bit(appleir->keymap[i], input_dev->keybit);
Jarod Wilson 8ccfe4
+	clear_bit(KEY_RESERVED, input_dev->keybit);
Jarod Wilson 8ccfe4
+
Jarod Wilson 8ccfe4
+	input_set_drvdata(input_dev, appleir);
Jarod Wilson 8ccfe4
+	input_dev->open = appleir_open;
Jarod Wilson 8ccfe4
+	input_dev->close = appleir_close;
Jarod Wilson 8ccfe4
+
Jarod Wilson 8ccfe4
+	endpoint = &intf->cur_altsetting->endpoint[0].desc;
Jarod Wilson 8ccfe4
+
Jarod Wilson 8ccfe4
+	usb_fill_int_urb(appleir->urb, dev,
Jarod Wilson 8ccfe4
+			 usb_rcvintpipe(dev, endpoint->bEndpointAddress),
Jarod Wilson 8ccfe4
+			 appleir->data, 8,
Jarod Wilson 8ccfe4
+			 appleir_urb, appleir, endpoint->bInterval);
Jarod Wilson 8ccfe4
+
Jarod Wilson 8ccfe4
+	appleir->urb->transfer_dma = appleir->dma_buf;
Jarod Wilson 8ccfe4
+	appleir->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
Jarod Wilson 8ccfe4
+
Jarod Wilson 8ccfe4
+	setup_timer(&appleir->key_up_timer,
Jarod Wilson 8ccfe4
+		    key_up_tick, (unsigned long) appleir);
Jarod Wilson 8ccfe4
+
Jarod Wilson 8ccfe4
+	retval = input_register_device(appleir->input_dev);
Jarod Wilson 8ccfe4
+	if (retval)
Jarod Wilson 8ccfe4
+		goto inputfail;
Jarod Wilson 8ccfe4
+
Jarod Wilson 8ccfe4
+	usb_set_intfdata(intf, appleir);
Jarod Wilson 8ccfe4
+
Jarod Wilson 8ccfe4
+	return 0;
Jarod Wilson 8ccfe4
+
Jarod Wilson 8ccfe4
+inputfail:
Jarod Wilson 8ccfe4
+	input_free_device(appleir->input_dev);
Jarod Wilson 8ccfe4
+
Jarod Wilson 8ccfe4
+urbfail:
Jarod Wilson 8ccfe4
+	usb_free_urb(appleir->urb);
Jarod Wilson 8ccfe4
+
Jarod Wilson 8ccfe4
+usbfail:
Jarod Wilson 8ccfe4
+	usb_free_coherent(dev, URB_SIZE, appleir->data,
Jarod Wilson 8ccfe4
+			appleir->dma_buf);
Jarod Wilson 8ccfe4
+
Jarod Wilson 8ccfe4
+allocfail:
Jarod Wilson 8ccfe4
+	kfree(appleir);
Jarod Wilson 8ccfe4
+
Jarod Wilson 8ccfe4
+	return retval;
Jarod Wilson 8ccfe4
+}
Jarod Wilson 8ccfe4
+
Jarod Wilson 8ccfe4
+static void appleir_disconnect(struct usb_interface *intf)
Jarod Wilson 8ccfe4
+{
Jarod Wilson 8ccfe4
+	struct appleir *appleir = usb_get_intfdata(intf);
Jarod Wilson 8ccfe4
+
Jarod Wilson 8ccfe4
+	usb_set_intfdata(intf, NULL);
Jarod Wilson 8ccfe4
+	input_unregister_device(appleir->input_dev);
Jarod Wilson 8ccfe4
+	usb_free_urb(appleir->urb);
Jarod Wilson 8ccfe4
+	usb_free_coherent(interface_to_usbdev(intf), URB_SIZE,
Jarod Wilson 8ccfe4
+			appleir->data, appleir->dma_buf);
Jarod Wilson 8ccfe4
+	kfree(appleir);
Jarod Wilson 8ccfe4
+}
Jarod Wilson 8ccfe4
+
Jarod Wilson 8ccfe4
+static int appleir_suspend(struct usb_interface *interface,
Jarod Wilson 8ccfe4
+			   pm_message_t message)
Jarod Wilson 8ccfe4
+{
Jarod Wilson 8ccfe4
+	struct appleir *appleir = usb_get_intfdata(interface);
Jarod Wilson 8ccfe4
+
Jarod Wilson 8ccfe4
+	mutex_lock(&appleir_mutex);
Jarod Wilson 8ccfe4
+	if (appleir->flags & APPLEIR_OPENED)
Jarod Wilson 8ccfe4
+		usb_kill_urb(appleir->urb);
Jarod Wilson 8ccfe4
+
Jarod Wilson 8ccfe4
+	appleir->flags |= APPLEIR_SUSPENDED;
Jarod Wilson 8ccfe4
+
Jarod Wilson 8ccfe4
+	mutex_unlock(&appleir_mutex);
Jarod Wilson 8ccfe4
+
Jarod Wilson 8ccfe4
+	return 0;
Jarod Wilson 8ccfe4
+}
Jarod Wilson 8ccfe4
+
Jarod Wilson 8ccfe4
+static int appleir_resume(struct usb_interface *interface)
Jarod Wilson 8ccfe4
+{
Jarod Wilson 8ccfe4
+	struct appleir *appleir;
Jarod Wilson 8ccfe4
+	int r = 0;
Jarod Wilson 8ccfe4
+
Jarod Wilson 8ccfe4
+	appleir = usb_get_intfdata(interface);
Jarod Wilson 8ccfe4
+
Jarod Wilson 8ccfe4
+	mutex_lock(&appleir_mutex);
Jarod Wilson 8ccfe4
+	if (appleir->flags & APPLEIR_OPENED) {
Jarod Wilson 8ccfe4
+		struct usb_endpoint_descriptor *endpoint;
Jarod Wilson 8ccfe4
+
Jarod Wilson 8ccfe4
+		endpoint = &interface->cur_altsetting->endpoint[0].desc;
Jarod Wilson 8ccfe4
+		usb_fill_int_urb(appleir->urb, appleir->usbdev,
Jarod Wilson 8ccfe4
+				 usb_rcvintpipe(appleir->usbdev, endpoint->bEndpointAddress),
Jarod Wilson 8ccfe4
+				 appleir->data, 8,
Jarod Wilson 8ccfe4
+				 appleir_urb, appleir, endpoint->bInterval);
Jarod Wilson 8ccfe4
+		appleir->urb->transfer_dma = appleir->dma_buf;
Jarod Wilson 8ccfe4
+		appleir->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
Jarod Wilson 8ccfe4
+
Jarod Wilson 8ccfe4
+		/* And reset the USB device */
Jarod Wilson 8ccfe4
+		if (usb_submit_urb(appleir->urb, GFP_ATOMIC))
Jarod Wilson 8ccfe4
+			r = -EIO;
Jarod Wilson 8ccfe4
+	}
Jarod Wilson 8ccfe4
+
Jarod Wilson 8ccfe4
+	appleir->flags &= ~APPLEIR_SUSPENDED;
Jarod Wilson 8ccfe4
+
Jarod Wilson 8ccfe4
+	mutex_unlock(&appleir_mutex);
Jarod Wilson 8ccfe4
+
Jarod Wilson 8ccfe4
+	return r;
Jarod Wilson 8ccfe4
+}
Jarod Wilson 8ccfe4
+
Jarod Wilson 8ccfe4
+static struct usb_driver appleir_driver = {
Jarod Wilson 8ccfe4
+	.name                 = "appleir",
Jarod Wilson 8ccfe4
+	.probe                = appleir_probe,
Jarod Wilson 8ccfe4
+	.disconnect           = appleir_disconnect,
Jarod Wilson 8ccfe4
+	.suspend              = appleir_suspend,
Jarod Wilson 8ccfe4
+	.resume               = appleir_resume,
Jarod Wilson 8ccfe4
+	.reset_resume         = appleir_resume,
Jarod Wilson 8ccfe4
+	.id_table             = appleir_ids,
Jarod Wilson 8ccfe4
+};
Jarod Wilson 8ccfe4
+
Jarod Wilson 8ccfe4
+static int __init appleir_init(void)
Jarod Wilson 8ccfe4
+{
Jarod Wilson 8ccfe4
+	return usb_register(&appleir_driver);
Jarod Wilson 8ccfe4
+}
Jarod Wilson 8ccfe4
+
Jarod Wilson 8ccfe4
+static void __exit appleir_exit(void)
Jarod Wilson 8ccfe4
+{
Jarod Wilson 8ccfe4
+	usb_deregister(&appleir_driver);
Jarod Wilson 8ccfe4
+}
Jarod Wilson 8ccfe4
+
Jarod Wilson 8ccfe4
+module_init(appleir_init);
Jarod Wilson 8ccfe4
+module_exit(appleir_exit);