Bastien Nocera 9ea075e
From 46fadae732d825141f45bf2fbd6381451da26ad7 Mon Sep 17 00:00:00 2001
Bastien Nocera ea6d873
From: Bastien Nocera <hadess@hadess.net>
Bastien Nocera 9ea075e
Date: Fri, 10 Sep 2010 16:40:46 +0100
Bastien Nocera ea6d873
Subject: [PATCH] Input: add appleir USB driver
Kyle McMartin d3a72ea
Bastien Nocera ea6d873
This driver was originally written by James McKenzie, updated by
Bastien Nocera ea6d873
Greg Kroah-Hartman, further updated by myself, with suspend support
Bastien Nocera ea6d873
added.
Kyle McMartin d3a72ea
Bastien Nocera ea6d873
More recent versions of the IR receiver are also supported through
Bastien Nocera ea6d873
a patch by Alex Karpenko. The patch also adds support for the 2nd
Bastien Nocera ea6d873
and 5th generation of the controller, and the menu key on newer
Bastien Nocera ea6d873
brushed metal remotes.
Jarod Wilson 8ccfe4c
Bastien Nocera ea6d873
Tested on a MacbookAir1,1
Bastien Nocera ea6d873
Bastien Nocera ea6d873
Signed-off-by: Bastien Nocera <hadess@hadess.net>
Bastien Nocera ea6d873
---
Bastien Nocera ea6d873
 Documentation/input/appleir.txt |   46 ++++
Bastien Nocera ea6d873
 drivers/hid/hid-apple.c         |    4 -
Bastien Nocera ea6d873
 drivers/hid/hid-core.c          |    7 +-
Bastien Nocera ea6d873
 drivers/hid/hid-ids.h           |    5 +-
Bastien Nocera ea6d873
 drivers/input/misc/Kconfig      |   13 +
Bastien Nocera ea6d873
 drivers/input/misc/Makefile     |    1 +
Bastien Nocera ea6d873
 drivers/input/misc/appleir.c    |  519 +++++++++++++++++++++++++++++++++++++++
Bastien Nocera ea6d873
 7 files changed, 588 insertions(+), 7 deletions(-)
Bastien Nocera ea6d873
 create mode 100644 Documentation/input/appleir.txt
Bastien Nocera ea6d873
 create mode 100644 drivers/input/misc/appleir.c
Jarod Wilson 8ccfe4c
Jarod Wilson 8ccfe4c
diff --git a/Documentation/input/appleir.txt b/Documentation/input/appleir.txt
Jarod Wilson 8ccfe4c
new file mode 100644
Bastien Nocera ea6d873
index 0000000..db637fb
Jarod Wilson 8ccfe4c
--- /dev/null
Jarod Wilson 8ccfe4c
+++ b/Documentation/input/appleir.txt
Bastien Nocera ea6d873
@@ -0,0 +1,46 @@
Jarod Wilson 8ccfe4c
+Apple IR receiver Driver (appleir)
Jarod Wilson 8ccfe4c
+----------------------------------
Jarod Wilson 8ccfe4c
+	Copyright (C) 2009 Bastien Nocera <hadess@hadess.net>
Jarod Wilson 8ccfe4c
+
Jarod Wilson 8ccfe4c
+The appleir driver is a kernel input driver to handle Apple's IR
Jarod Wilson 8ccfe4c
+receivers (and associated remotes) in the kernel.
Jarod Wilson 8ccfe4c
+
Jarod Wilson 8ccfe4c
+The driver is an input driver which only handles "official" remotes
Jarod Wilson 8ccfe4c
+as built and sold by Apple.
Jarod Wilson 8ccfe4c
+
Jarod Wilson 8ccfe4c
+Authors
Jarod Wilson 8ccfe4c
+-------
Jarod Wilson 8ccfe4c
+
Jarod Wilson 8ccfe4c
+James McKenzie (original driver)
Jarod Wilson 8ccfe4c
+Alex Karpenko (05ac:8242 support)
Jarod Wilson 8ccfe4c
+Greg Kroah-Hartman (cleanups and original submission)
Bastien Nocera ea6d873
+Bastien Nocera (further cleanups, brushed metal "enter"
Bastien Nocera ea6d873
+button support and suspend support)
Jarod Wilson 8ccfe4c
+
Jarod Wilson 8ccfe4c
+Supported hardware
Jarod Wilson 8ccfe4c
+------------------
Jarod Wilson 8ccfe4c
+
Jarod Wilson 8ccfe4c
+- All Apple laptops and desktops from 2005 onwards, except:
Jarod Wilson 8ccfe4c
+  - the unibody Macbook (2009)
Jarod Wilson 8ccfe4c
+  - Mac Pro (all versions)
Bastien Nocera ea6d873
+- Apple TV (all revisions prior to September 2010)
Jarod Wilson 8ccfe4c
+
Bastien Nocera ea6d873
+The remote will only support the 6 (old white) or 7 (brushed metal) buttons
Bastien Nocera ea6d873
+of the remotes as sold by Apple. See the next section if you want to use
Bastien Nocera ea6d873
+other remotes or want to use lirc with the device instead of the kernel driver.
Jarod Wilson 8ccfe4c
+
Jarod Wilson 8ccfe4c
+Using lirc (native) instead of the kernel driver
Jarod Wilson 8ccfe4c
+------------------------------------------------
Jarod Wilson 8ccfe4c
+
Jarod Wilson 8ccfe4c
+First, you will need to disable the kernel driver for the receiver.
Jarod Wilson 8ccfe4c
+
Jarod Wilson 8ccfe4c
+This can be achieved by passing quirks to the usbhid driver.
Jarod Wilson 8ccfe4c
+The quirk line would be:
Jarod Wilson 8ccfe4c
+usbhid.quirks=0x05ac:0x8242:0x40000010
Jarod Wilson 8ccfe4c
+
Jarod Wilson 8ccfe4c
+With 0x05ac being the vendor ID (Apple, you shouldn't need to change this)
Jarod Wilson 8ccfe4c
+With 0x8242 being the product ID (check the output of lsusb for your hardware)
Jarod Wilson 8ccfe4c
+And 0x10 being "HID_QUIRK_HIDDEV_FORCE" and 0x40000000 being "HID_QUIRK_NO_IGNORE"
Jarod Wilson 8ccfe4c
+
Jarod Wilson 8ccfe4c
+This should force the creation of a hiddev device for the receiver, and
Jarod Wilson 8ccfe4c
+make it usable under lirc.
Kyle McMartin d3a72ea
diff --git a/drivers/hid/hid-apple.c b/drivers/hid/hid-apple.c
Bastien Nocera ea6d873
index bba05d0..0059d5a 100644
Kyle McMartin d3a72ea
--- a/drivers/hid/hid-apple.c
Kyle McMartin d3a72ea
+++ b/drivers/hid/hid-apple.c
Bastien Nocera ea6d873
@@ -361,10 +361,6 @@ static void apple_remove(struct hid_device *hdev)
Kyle McMartin d3a72ea
 }
Kyle McMartin d3a72ea
 
Kyle McMartin d3a72ea
 static const struct hid_device_id apple_devices[] = {
Kyle McMartin d3a72ea
-	{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ATV_IRCONTROL),
Kyle McMartin d3a72ea
-		.driver_data = APPLE_HIDDEV | APPLE_IGNORE_HIDINPUT },
Kyle McMartin d3a72ea
-	{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL4),
Kyle McMartin d3a72ea
-		.driver_data = APPLE_HIDDEV | APPLE_IGNORE_HIDINPUT },
Kyle McMartin d3a72ea
 	{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MIGHTYMOUSE),
Kyle McMartin d3a72ea
 		.driver_data = APPLE_MIGHTYMOUSE | APPLE_INVERT_HWHEEL },
Kyle McMartin d3a72ea
 
Bastien Nocera ea6d873
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
Bastien Nocera 9ea075e
index 866e54e..1d5e284 100644
Bastien Nocera ea6d873
--- a/drivers/hid/hid-core.c
Bastien Nocera ea6d873
+++ b/drivers/hid/hid-core.c
Bastien Nocera 9ea075e
@@ -1239,8 +1239,6 @@ static const struct hid_device_id hid_blacklist[] = {
Bastien Nocera 9ea075e
 	{ HID_USB_DEVICE(USB_VENDOR_ID_3M, USB_DEVICE_ID_3M2256) },
Bastien Nocera 9ea075e
 	{ HID_USB_DEVICE(USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_WCP32PU) },
Bastien Nocera 9ea075e
 	{ HID_USB_DEVICE(USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_X5_005D) },
Bastien Nocera ea6d873
-	{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ATV_IRCONTROL) },
Bastien Nocera ea6d873
-	{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL4) },
Bastien Nocera ea6d873
 	{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MIGHTYMOUSE) },
Bastien Nocera ea6d873
 	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGICMOUSE) },
Bastien Nocera ea6d873
 	{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_ANSI) },
Bastien Nocera 9ea075e
@@ -1571,6 +1569,11 @@ static const struct hid_device_id hid_ignore_list[] = {
Bastien Nocera ea6d873
 	{ HID_USB_DEVICE(USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_24) },
Bastien Nocera ea6d873
 	{ HID_USB_DEVICE(USB_VENDOR_ID_AIRCABLE, USB_DEVICE_ID_AIRCABLE1) },
Bastien Nocera ea6d873
 	{ HID_USB_DEVICE(USB_VENDOR_ID_ALCOR, USB_DEVICE_ID_ALCOR_USBRS232) },
Bastien Nocera ea6d873
+	{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL) },
Bastien Nocera ea6d873
+	{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL2) },
Bastien Nocera ea6d873
+	{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL3) },
Bastien Nocera ea6d873
+	{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL4) },
Bastien Nocera ea6d873
+	{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL5) },
Bastien Nocera ea6d873
 	{ HID_USB_DEVICE(USB_VENDOR_ID_ASUS, USB_DEVICE_ID_ASUS_T91MT)},
Bastien Nocera ea6d873
 	{ HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK, USB_DEVICE_ID_ASUSTEK_LCM)},
Bastien Nocera ea6d873
 	{ HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK, USB_DEVICE_ID_ASUSTEK_LCM2)},
Kyle McMartin d3a72ea
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
Bastien Nocera 9ea075e
index 31601ee..9afe3bc 100644
Kyle McMartin d3a72ea
--- a/drivers/hid/hid-ids.h
Kyle McMartin d3a72ea
+++ b/drivers/hid/hid-ids.h
Bastien Nocera 9ea075e
@@ -98,8 +98,11 @@
Kyle McMartin d3a72ea
 #define USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_JIS   0x023b
Kyle McMartin d3a72ea
 #define USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY	0x030a
Kyle McMartin d3a72ea
 #define USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY	0x030b
Bastien Nocera ea6d873
-#define USB_DEVICE_ID_APPLE_ATV_IRCONTROL	0x8241
Kyle McMartin d3a72ea
+#define USB_DEVICE_ID_APPLE_IRCONTROL	0x8240
Bastien Nocera ea6d873
+#define USB_DEVICE_ID_APPLE_IRCONTROL2	0x1440
Bastien Nocera ea6d873
+#define USB_DEVICE_ID_APPLE_IRCONTROL3	0x8241
Kyle McMartin d3a72ea
 #define USB_DEVICE_ID_APPLE_IRCONTROL4	0x8242
Bastien Nocera ea6d873
+#define USB_DEVICE_ID_APPLE_IRCONTROL5	0x8243
Kyle McMartin d3a72ea
 
Bastien Nocera ea6d873
 #define USB_VENDOR_ID_ASUS		0x0486
Bastien Nocera ea6d873
 #define USB_DEVICE_ID_ASUS_T91MT	0x0185
Kyle McMartin d3a72ea
diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
Bastien Nocera 9ea075e
index c44b9ea..76a12b7 100644
Kyle McMartin d3a72ea
--- a/drivers/input/misc/Kconfig
Kyle McMartin d3a72ea
+++ b/drivers/input/misc/Kconfig
Bastien Nocera 9ea075e
@@ -199,6 +199,19 @@ config INPUT_KEYSPAN_REMOTE
Kyle McMartin d3a72ea
 	  To compile this driver as a module, choose M here: the module will
Kyle McMartin d3a72ea
 	  be called keyspan_remote.
Kyle McMartin d3a72ea
 
Kyle McMartin d3a72ea
+config INPUT_APPLEIR
Kyle McMartin d3a72ea
+	tristate "Apple infrared receiver (built in)"
Kyle McMartin d3a72ea
+	depends on USB_ARCH_HAS_HCD
Kyle McMartin d3a72ea
+	select USB
Kyle McMartin d3a72ea
+	help
Kyle McMartin d3a72ea
+	  Say Y here if you want to use a Apple infrared remote control. All
Kyle McMartin d3a72ea
+	  the Apple computers from 2005 onwards include such a port, except
Kyle McMartin d3a72ea
+	  the unibody Macbook (2009), and Mac Pros. This receiver is also
Bastien Nocera ea6d873
+	  used in the Apple TV set-top box prior to the 2010 model.
Kyle McMartin d3a72ea
+
Kyle McMartin d3a72ea
+	  To compile this driver as a module, choose M here: the module will
Kyle McMartin d3a72ea
+	  be called appleir.
Kyle McMartin d3a72ea
+
Kyle McMartin d3a72ea
 config INPUT_POWERMATE
Kyle McMartin d3a72ea
 	tristate "Griffin PowerMate and Contour Jog support"
Kyle McMartin d3a72ea
 	depends on USB_ARCH_HAS_HCD
Kyle McMartin d3a72ea
diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
Bastien Nocera 9ea075e
index 71fe57d..62a5c60 100644
Kyle McMartin d3a72ea
--- a/drivers/input/misc/Makefile
Kyle McMartin d3a72ea
+++ b/drivers/input/misc/Makefile
Bastien Nocera 9ea075e
@@ -9,6 +9,7 @@ obj-$(CONFIG_INPUT_AD714X)		+= ad714x.o
Bastien Nocera 9ea075e
 obj-$(CONFIG_INPUT_AD714X_I2C)		+= ad714x-i2c.o
Bastien Nocera 9ea075e
 obj-$(CONFIG_INPUT_AD714X_SPI)		+= ad714x-spi.o
Kyle McMartin d3a72ea
 obj-$(CONFIG_INPUT_APANEL)		+= apanel.o
Kyle McMartin d3a72ea
+obj-$(CONFIG_INPUT_APPLEIR)		+= appleir.o
Kyle McMartin d3a72ea
 obj-$(CONFIG_INPUT_ATI_REMOTE)		+= ati_remote.o
Kyle McMartin d3a72ea
 obj-$(CONFIG_INPUT_ATI_REMOTE2)		+= ati_remote2.o
Kyle McMartin d3a72ea
 obj-$(CONFIG_INPUT_ATLAS_BTNS)		+= atlas_btns.o
Jarod Wilson 8ccfe4c
diff --git a/drivers/input/misc/appleir.c b/drivers/input/misc/appleir.c
Jarod Wilson 8ccfe4c
new file mode 100644
Bastien Nocera ea6d873
index 0000000..3817a3c
Jarod Wilson 8ccfe4c
--- /dev/null
Jarod Wilson 8ccfe4c
+++ b/drivers/input/misc/appleir.c
Bastien Nocera ea6d873
@@ -0,0 +1,519 @@
Jarod Wilson 8ccfe4c
+/*
Jarod Wilson 8ccfe4c
+ * appleir: USB driver for the apple ir device
Jarod Wilson 8ccfe4c
+ *
Jarod Wilson 8ccfe4c
+ * Original driver written by James McKenzie
Jarod Wilson 8ccfe4c
+ * Ported to recent 2.6 kernel versions by Greg Kroah-Hartman <gregkh@suse.de>
Jarod Wilson 8ccfe4c
+ *
Jarod Wilson 8ccfe4c
+ * Copyright (C) 2006 James McKenzie
Jarod Wilson 8ccfe4c
+ * Copyright (C) 2008 Greg Kroah-Hartman <greg@kroah.com>
Jarod Wilson 8ccfe4c
+ * Copyright (C) 2008 Novell Inc.
Jarod Wilson 8ccfe4c
+ *
Jarod Wilson 8ccfe4c
+ * This program is free software; you can redistribute it and/or modify it
Jarod Wilson 8ccfe4c
+ * under the terms of the GNU General Public License as published by the Free
Jarod Wilson 8ccfe4c
+ * Software Foundation, version 2.
Jarod Wilson 8ccfe4c
+ *
Jarod Wilson 8ccfe4c
+ */
Jarod Wilson 8ccfe4c
+
Jarod Wilson 8ccfe4c
+#include <linux/kernel.h>
Jarod Wilson 8ccfe4c
+#include <linux/slab.h>
Jarod Wilson 8ccfe4c
+#include <linux/input.h>
Jarod Wilson 8ccfe4c
+#include <linux/usb/input.h>
Jarod Wilson 8ccfe4c
+#include <linux/module.h>
Jarod Wilson 8ccfe4c
+#include <linux/init.h>
Jarod Wilson 8ccfe4c
+#include <linux/usb.h>
Jarod Wilson 8ccfe4c
+#include <linux/usb/input.h>
Jarod Wilson 8ccfe4c
+#include <asm/unaligned.h>
Jarod Wilson 8ccfe4c
+#include <asm/byteorder.h>
Jarod Wilson 8ccfe4c
+
Jarod Wilson 8ccfe4c
+#define DRIVER_VERSION "v1.2"
Jarod Wilson 8ccfe4c
+#define DRIVER_AUTHOR "James McKenzie"
Jarod Wilson 8ccfe4c
+#define DRIVER_DESC "Apple infrared receiver driver"
Jarod Wilson 8ccfe4c
+#define DRIVER_LICENSE "GPL"
Jarod Wilson 8ccfe4c
+
Jarod Wilson 8ccfe4c
+MODULE_AUTHOR(DRIVER_AUTHOR);
Jarod Wilson 8ccfe4c
+MODULE_DESCRIPTION(DRIVER_DESC);
Jarod Wilson 8ccfe4c
+MODULE_LICENSE(DRIVER_LICENSE);
Jarod Wilson 8ccfe4c
+
Jarod Wilson 8ccfe4c
+#define USB_VENDOR_ID_APPLE			0x05ac
Jarod Wilson 8ccfe4c
+#define USB_DEVICE_ID_APPLE_IRCONTROL		0x8240
Bastien Nocera ea6d873
+#define USB_DEVICE_ID_APPLE_IRCONTROL2		0x1440
Bastien Nocera ea6d873
+#define USB_DEVICE_ID_APPLE_IRCONTROL3		0x8241
Jarod Wilson 8ccfe4c
+#define USB_DEVICE_ID_APPLE_IRCONTROL4		0x8242
Bastien Nocera ea6d873
+#define USB_DEVICE_ID_APPLE_IRCONTROL5		0x8243
Jarod Wilson 8ccfe4c
+
Jarod Wilson 8ccfe4c
+#define URB_SIZE	32
Jarod Wilson 8ccfe4c
+
Bastien Nocera ea6d873
+#define MAX_KEYS	9
Jarod Wilson 8ccfe4c
+#define MAX_KEYS_MASK	(MAX_KEYS - 1)
Jarod Wilson 8ccfe4c
+
Jarod Wilson 8ccfe4c
+#define dbginfo(dev, format, arg...) do { if (debug) dev_info(dev , format , ## arg); } while (0)
Jarod Wilson 8ccfe4c
+
Jarod Wilson 8ccfe4c
+static int debug;
Jarod Wilson 8ccfe4c
+module_param(debug, int, 0644);
Jarod Wilson 8ccfe4c
+MODULE_PARM_DESC(debug, "Enable extra debug messages and information");
Jarod Wilson 8ccfe4c
+
Jarod Wilson 8ccfe4c
+/* I have two devices both of which report the following */
Jarod Wilson 8ccfe4c
+/* 25 87 ee 83 0a  	+  */
Jarod Wilson 8ccfe4c
+/* 25 87 ee 83 0c  	-  */
Jarod Wilson 8ccfe4c
+/* 25 87 ee 83 09	<< */
Jarod Wilson 8ccfe4c
+/* 25 87 ee 83 06	>> */
Jarod Wilson 8ccfe4c
+/* 25 87 ee 83 05	>" */
Jarod Wilson 8ccfe4c
+/* 25 87 ee 83 03	menu */
Jarod Wilson 8ccfe4c
+/* 26 00 00 00 00	for key repeat*/
Jarod Wilson 8ccfe4c
+
Jarod Wilson 8ccfe4c
+/* Thomas Glanzmann reports the following responses */
Jarod Wilson 8ccfe4c
+/* 25 87 ee ca 0b	+  */
Jarod Wilson 8ccfe4c
+/* 25 87 ee ca 0d	-  */
Jarod Wilson 8ccfe4c
+/* 25 87 ee ca 08	<< */
Jarod Wilson 8ccfe4c
+/* 25 87 ee ca 07	>> */
Jarod Wilson 8ccfe4c
+/* 25 87 ee ca 04	>" */
Jarod Wilson 8ccfe4c
+/* 25 87 ee ca 02 	menu */
Jarod Wilson 8ccfe4c
+/* 26 00 00 00 00       for key repeat*/
Jarod Wilson 8ccfe4c
+/* He also observes the following event sometimes */
Jarod Wilson 8ccfe4c
+/* sent after a key is release, which I interpret */
Jarod Wilson 8ccfe4c
+/* as a flat battery message */
Jarod Wilson 8ccfe4c
+/* 25 87 e0 ca 06	flat battery */
Jarod Wilson 8ccfe4c
+
Jarod Wilson 8ccfe4c
+/* Alexandre Karpenko reports the following responses for Device ID 0x8242 */
Jarod Wilson 8ccfe4c
+/* 25 87 ee 47 0b	+  */
Jarod Wilson 8ccfe4c
+/* 25 87 ee 47 0d	-  */
Jarod Wilson 8ccfe4c
+/* 25 87 ee 47 08	<< */
Jarod Wilson 8ccfe4c
+/* 25 87 ee 47 07	>> */
Jarod Wilson 8ccfe4c
+/* 25 87 ee 47 04	>" */
Jarod Wilson 8ccfe4c
+/* 25 87 ee 47 02 	menu */
Jarod Wilson 8ccfe4c
+/* 26 87 ee 47 ** 	for key repeat (** is the code of the key being held) */
Jarod Wilson 8ccfe4c
+
Bastien Nocera ea6d873
+/* Bastien Nocera's "new" remote */
Bastien Nocera ea6d873
+/* 25 87 ee 91 5f	followed by
Bastien Nocera ea6d873
+ * 25 87 ee 91 05	gives you >"
Bastien Nocera ea6d873
+ *
Bastien Nocera ea6d873
+ * 25 87 ee 91 5c	followed by
Bastien Nocera ea6d873
+ * 25 87 ee 91 05	gives you the middle button */
Bastien Nocera ea6d873
+
Jarod Wilson 8ccfe4c
+static const unsigned short appleir_key_table[] = {
Jarod Wilson 8ccfe4c
+	KEY_RESERVED,
Jarod Wilson 8ccfe4c
+	KEY_MENU,
Jarod Wilson 8ccfe4c
+	KEY_PLAYPAUSE,
Jarod Wilson 8ccfe4c
+	KEY_FORWARD,
Jarod Wilson 8ccfe4c
+	KEY_BACK,
Jarod Wilson 8ccfe4c
+	KEY_VOLUMEUP,
Jarod Wilson 8ccfe4c
+	KEY_VOLUMEDOWN,
Bastien Nocera ea6d873
+	KEY_ENTER,
Jarod Wilson 8ccfe4c
+	KEY_RESERVED,
Jarod Wilson 8ccfe4c
+};
Jarod Wilson 8ccfe4c
+
Jarod Wilson 8ccfe4c
+struct appleir {
Jarod Wilson 8ccfe4c
+	struct input_dev *input_dev;
Jarod Wilson 8ccfe4c
+	unsigned short keymap[ARRAY_SIZE(appleir_key_table)];
Jarod Wilson 8ccfe4c
+	u8 *data;
Jarod Wilson 8ccfe4c
+	dma_addr_t dma_buf;
Jarod Wilson 8ccfe4c
+	struct usb_device *usbdev;
Jarod Wilson 8ccfe4c
+	unsigned int flags;
Jarod Wilson 8ccfe4c
+	struct urb *urb;
Jarod Wilson 8ccfe4c
+	struct timer_list key_up_timer;
Jarod Wilson 8ccfe4c
+	int current_key;
Bastien Nocera ea6d873
+	int prev_key_idx;
Jarod Wilson 8ccfe4c
+	char phys[32];
Jarod Wilson 8ccfe4c
+};
Jarod Wilson 8ccfe4c
+
Jarod Wilson 8ccfe4c
+static DEFINE_MUTEX(appleir_mutex);
Jarod Wilson 8ccfe4c
+
Jarod Wilson 8ccfe4c
+enum {
Jarod Wilson 8ccfe4c
+	APPLEIR_OPENED = 0x1,
Jarod Wilson 8ccfe4c
+	APPLEIR_SUSPENDED = 0x2,
Jarod Wilson 8ccfe4c
+};
Jarod Wilson 8ccfe4c
+
Jarod Wilson 8ccfe4c
+static struct usb_device_id appleir_ids[] = {
Jarod Wilson 8ccfe4c
+	{ USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL) },
Bastien Nocera ea6d873
+	{ USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL2) },
Bastien Nocera ea6d873
+	{ USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL3) },
Jarod Wilson 8ccfe4c
+	{ USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL4) },
Bastien Nocera ea6d873
+	{ USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL5) },
Jarod Wilson 8ccfe4c
+	{}
Jarod Wilson 8ccfe4c
+};
Jarod Wilson 8ccfe4c
+MODULE_DEVICE_TABLE(usb, appleir_ids);
Jarod Wilson 8ccfe4c
+
Jarod Wilson 8ccfe4c
+static void dump_packet(struct appleir *appleir, char *msg, u8 *data, int len)
Jarod Wilson 8ccfe4c
+{
Jarod Wilson 8ccfe4c
+	int i;
Jarod Wilson 8ccfe4c
+
Jarod Wilson 8ccfe4c
+	printk(KERN_ERR "appleir: %s (%d bytes)", msg, len);
Jarod Wilson 8ccfe4c
+
Jarod Wilson 8ccfe4c
+	for (i = 0; i < len; ++i)
Jarod Wilson 8ccfe4c
+		printk(" %02x", data[i]);
Bastien Nocera ea6d873
+	printk(" (should be command %d)\n", (data[4] >> 1) & MAX_KEYS_MASK);
Bastien Nocera ea6d873
+}
Bastien Nocera ea6d873
+
Bastien Nocera ea6d873
+static int get_key(int data)
Bastien Nocera ea6d873
+{
Bastien Nocera ea6d873
+	switch (data) {
Bastien Nocera ea6d873
+	case 0x02:
Bastien Nocera ea6d873
+	case 0x03:
Bastien Nocera ea6d873
+		/* menu */
Bastien Nocera ea6d873
+		return 1;
Bastien Nocera ea6d873
+	case 0x04:
Bastien Nocera ea6d873
+	case 0x05:
Bastien Nocera ea6d873
+		/* >" */
Bastien Nocera ea6d873
+		return 2;
Bastien Nocera ea6d873
+	case 0x06:
Bastien Nocera ea6d873
+	case 0x07:
Bastien Nocera ea6d873
+		/* >> */
Bastien Nocera ea6d873
+		return 3;
Bastien Nocera ea6d873
+	case 0x08:
Bastien Nocera ea6d873
+	case 0x09:
Bastien Nocera ea6d873
+		/* << */
Bastien Nocera ea6d873
+		return 4;
Bastien Nocera ea6d873
+	case 0x0a:
Bastien Nocera ea6d873
+	case 0x0b:
Bastien Nocera ea6d873
+		/* + */
Bastien Nocera ea6d873
+		return 5;
Bastien Nocera ea6d873
+	case 0x0c:
Bastien Nocera ea6d873
+	case 0x0d:
Bastien Nocera ea6d873
+		/* - */
Bastien Nocera ea6d873
+		return 6;
Bastien Nocera ea6d873
+	case 0x5c:
Bastien Nocera ea6d873
+		/* Middle button, on newer remotes,
Bastien Nocera ea6d873
+		 * part of a 2 packet-command */
Bastien Nocera ea6d873
+		return -7;
Bastien Nocera ea6d873
+	default:
Bastien Nocera ea6d873
+		return -1;
Bastien Nocera ea6d873
+	}
Jarod Wilson 8ccfe4c
+}
Jarod Wilson 8ccfe4c
+
Jarod Wilson 8ccfe4c
+static void key_up(struct appleir *appleir, int key)
Jarod Wilson 8ccfe4c
+{
Jarod Wilson 8ccfe4c
+	dbginfo(&appleir->input_dev->dev, "key %d up\n", key);
Jarod Wilson 8ccfe4c
+	input_report_key(appleir->input_dev, key, 0);
Jarod Wilson 8ccfe4c
+	input_sync(appleir->input_dev);
Jarod Wilson 8ccfe4c
+}
Jarod Wilson 8ccfe4c
+
Jarod Wilson 8ccfe4c
+static void key_down(struct appleir *appleir, int key)
Jarod Wilson 8ccfe4c
+{
Jarod Wilson 8ccfe4c
+	dbginfo(&appleir->input_dev->dev, "key %d down\n", key);
Jarod Wilson 8ccfe4c
+	input_report_key(appleir->input_dev, key, 1);
Jarod Wilson 8ccfe4c
+	input_sync(appleir->input_dev);
Jarod Wilson 8ccfe4c
+}
Jarod Wilson 8ccfe4c
+
Jarod Wilson 8ccfe4c
+static void battery_flat(struct appleir *appleir)
Jarod Wilson 8ccfe4c
+{
Jarod Wilson 8ccfe4c
+	dev_err(&appleir->input_dev->dev, "possible flat battery?\n");
Jarod Wilson 8ccfe4c
+}
Jarod Wilson 8ccfe4c
+
Jarod Wilson 8ccfe4c
+static void key_up_tick(unsigned long data)
Jarod Wilson 8ccfe4c
+{
Jarod Wilson 8ccfe4c
+	struct appleir *appleir = (struct appleir *)data;
Jarod Wilson 8ccfe4c
+
Jarod Wilson 8ccfe4c
+	if (appleir->current_key) {
Jarod Wilson 8ccfe4c
+		key_up(appleir, appleir->current_key);
Jarod Wilson 8ccfe4c
+		appleir->current_key = 0;
Jarod Wilson 8ccfe4c
+	}
Jarod Wilson 8ccfe4c
+}
Jarod Wilson 8ccfe4c
+
Jarod Wilson 8ccfe4c
+static void new_data(struct appleir *appleir, u8 *data, int len)
Jarod Wilson 8ccfe4c
+{
Jarod Wilson 8ccfe4c
+	static const u8 keydown[] = { 0x25, 0x87, 0xee };
Jarod Wilson 8ccfe4c
+	static const u8 keyrepeat[] = { 0x26, };
Jarod Wilson 8ccfe4c
+	static const u8 flatbattery[] = { 0x25, 0x87, 0xe0 };
Jarod Wilson 8ccfe4c
+
Jarod Wilson 8ccfe4c
+	if (debug)
Jarod Wilson 8ccfe4c
+		dump_packet(appleir, "received", data, len);
Jarod Wilson 8ccfe4c
+
Jarod Wilson 8ccfe4c
+	if (len != 5)
Jarod Wilson 8ccfe4c
+		return;
Jarod Wilson 8ccfe4c
+
Jarod Wilson 8ccfe4c
+	if (!memcmp(data, keydown, sizeof(keydown))) {
Bastien Nocera ea6d873
+		int index;
Bastien Nocera ea6d873
+
Jarod Wilson 8ccfe4c
+		/* If we already have a key down, take it up before marking
Jarod Wilson 8ccfe4c
+		   this one down */
Jarod Wilson 8ccfe4c
+		if (appleir->current_key)
Jarod Wilson 8ccfe4c
+			key_up(appleir, appleir->current_key);
Jarod Wilson 8ccfe4c
+
Bastien Nocera ea6d873
+		/* Handle dual packet commands */
Bastien Nocera ea6d873
+		if (appleir->prev_key_idx > 0)
Bastien Nocera ea6d873
+			index = appleir->prev_key_idx;
Bastien Nocera ea6d873
+		else
Bastien Nocera ea6d873
+			index = get_key(data[4]);
Bastien Nocera ea6d873
+
Bastien Nocera ea6d873
+		if (index > 0) {
Bastien Nocera ea6d873
+			appleir->current_key = appleir->keymap[index];
Bastien Nocera ea6d873
+
Bastien Nocera ea6d873
+			key_down(appleir, appleir->current_key);
Bastien Nocera ea6d873
+			/* Remote doesn't do key up, either pull them up, in the test
Bastien Nocera ea6d873
+			   above, or here set a timer which pulls them up after 1/8 s */
Bastien Nocera ea6d873
+			mod_timer(&appleir->key_up_timer, jiffies + HZ / 8);
Bastien Nocera ea6d873
+			appleir->prev_key_idx = 0;
Bastien Nocera ea6d873
+			return;
Bastien Nocera ea6d873
+		} else if (index == -7) {
Bastien Nocera ea6d873
+			/* Remember key for next packet */
Bastien Nocera ea6d873
+			appleir->prev_key_idx = 0 - index;
Bastien Nocera ea6d873
+			return;
Bastien Nocera ea6d873
+		}
Jarod Wilson 8ccfe4c
+	}
Jarod Wilson 8ccfe4c
+
Bastien Nocera ea6d873
+	appleir->prev_key_idx = 0;
Bastien Nocera ea6d873
+
Jarod Wilson 8ccfe4c
+	if (!memcmp(data, keyrepeat, sizeof(keyrepeat))) {
Jarod Wilson 8ccfe4c
+		key_down(appleir, appleir->current_key);
Jarod Wilson 8ccfe4c
+		/* Remote doesn't do key up, either pull them up, in the test
Jarod Wilson 8ccfe4c
+		   above, or here set a timer which pulls them up after 1/8 s */
Jarod Wilson 8ccfe4c
+		mod_timer(&appleir->key_up_timer, jiffies + HZ / 8);
Jarod Wilson 8ccfe4c
+		return;
Jarod Wilson 8ccfe4c
+	}
Jarod Wilson 8ccfe4c
+
Jarod Wilson 8ccfe4c
+	if (!memcmp(data, flatbattery, sizeof(flatbattery))) {
Jarod Wilson 8ccfe4c
+		battery_flat(appleir);
Jarod Wilson 8ccfe4c
+		/* Fall through */
Jarod Wilson 8ccfe4c
+	}
Jarod Wilson 8ccfe4c
+
Jarod Wilson 8ccfe4c
+	dump_packet(appleir, "unknown packet", data, len);
Jarod Wilson 8ccfe4c
+}
Jarod Wilson 8ccfe4c
+
Jarod Wilson 8ccfe4c
+static void appleir_urb(struct urb *urb)
Jarod Wilson 8ccfe4c
+{
Jarod Wilson 8ccfe4c
+	struct appleir *appleir = urb->context;
Jarod Wilson 8ccfe4c
+	int status = urb->status;
Jarod Wilson 8ccfe4c
+	int retval;
Jarod Wilson 8ccfe4c
+
Jarod Wilson 8ccfe4c
+	switch (status) {
Jarod Wilson 8ccfe4c
+	case 0:
Jarod Wilson 8ccfe4c
+		new_data(appleir, urb->transfer_buffer, urb->actual_length);
Jarod Wilson 8ccfe4c
+		break;
Jarod Wilson 8ccfe4c
+	case -ECONNRESET:
Jarod Wilson 8ccfe4c
+	case -ENOENT:
Jarod Wilson 8ccfe4c
+	case -ESHUTDOWN:
Jarod Wilson 8ccfe4c
+		/* This urb is terminated, clean up */
Jarod Wilson 8ccfe4c
+		dbginfo(&appleir->input_dev->dev, "%s - urb shutting down with status: %d", __func__,
Jarod Wilson 8ccfe4c
+			urb->status);
Jarod Wilson 8ccfe4c
+		return;
Jarod Wilson 8ccfe4c
+	default:
Jarod Wilson 8ccfe4c
+		dbginfo(&appleir->input_dev->dev, "%s - nonzero urb status received: %d", __func__,
Jarod Wilson 8ccfe4c
+			urb->status);
Jarod Wilson 8ccfe4c
+	}
Jarod Wilson 8ccfe4c
+
Jarod Wilson 8ccfe4c
+	retval = usb_submit_urb(urb, GFP_ATOMIC);
Jarod Wilson 8ccfe4c
+	if (retval)
Jarod Wilson 8ccfe4c
+		err("%s - usb_submit_urb failed with result %d", __func__,
Jarod Wilson 8ccfe4c
+		    retval);
Jarod Wilson 8ccfe4c
+}
Jarod Wilson 8ccfe4c
+
Jarod Wilson 8ccfe4c
+static int appleir_open(struct input_dev *dev)
Jarod Wilson 8ccfe4c
+{
Jarod Wilson 8ccfe4c
+	struct appleir *appleir = input_get_drvdata(dev);
Jarod Wilson 8ccfe4c
+	struct usb_interface *intf = usb_ifnum_to_if(appleir->usbdev, 0);
Jarod Wilson 8ccfe4c
+	int r;
Jarod Wilson 8ccfe4c
+
Jarod Wilson 8ccfe4c
+	r = usb_autopm_get_interface(intf);
Jarod Wilson 8ccfe4c
+	if (r) {
Jarod Wilson 8ccfe4c
+		dev_err(&intf->dev,
Jarod Wilson 8ccfe4c
+			"%s(): usb_autopm_get_interface() = %d\n", __func__, r);
Jarod Wilson 8ccfe4c
+		return r;
Jarod Wilson 8ccfe4c
+	}
Jarod Wilson 8ccfe4c
+
Jarod Wilson 8ccfe4c
+	mutex_lock(&appleir_mutex);
Jarod Wilson 8ccfe4c
+
Jarod Wilson 8ccfe4c
+	if (usb_submit_urb(appleir->urb, GFP_ATOMIC)) {
Jarod Wilson 8ccfe4c
+		r = -EIO;
Jarod Wilson 8ccfe4c
+		goto fail;
Jarod Wilson 8ccfe4c
+	}
Jarod Wilson 8ccfe4c
+
Jarod Wilson 8ccfe4c
+	appleir->flags |= APPLEIR_OPENED;
Jarod Wilson 8ccfe4c
+
Jarod Wilson 8ccfe4c
+	mutex_unlock(&appleir_mutex);
Jarod Wilson 8ccfe4c
+
Jarod Wilson 8ccfe4c
+	usb_autopm_put_interface(intf);
Jarod Wilson 8ccfe4c
+
Jarod Wilson 8ccfe4c
+	return 0;
Jarod Wilson 8ccfe4c
+fail:
Jarod Wilson 8ccfe4c
+	mutex_unlock(&appleir_mutex);
Jarod Wilson 8ccfe4c
+	usb_autopm_put_interface(intf);
Jarod Wilson 8ccfe4c
+	return r;
Jarod Wilson 8ccfe4c
+}
Jarod Wilson 8ccfe4c
+
Jarod Wilson 8ccfe4c
+static void appleir_close(struct input_dev *dev)
Jarod Wilson 8ccfe4c
+{
Jarod Wilson 8ccfe4c
+	struct appleir *appleir = input_get_drvdata(dev);
Jarod Wilson 8ccfe4c
+
Jarod Wilson 8ccfe4c
+	mutex_lock(&appleir_mutex);
Jarod Wilson 8ccfe4c
+
Jarod Wilson 8ccfe4c
+	if (!(appleir->flags & APPLEIR_SUSPENDED)) {
Jarod Wilson 8ccfe4c
+		usb_kill_urb(appleir->urb);
Jarod Wilson 8ccfe4c
+		del_timer_sync(&appleir->key_up_timer);
Jarod Wilson 8ccfe4c
+	}
Jarod Wilson 8ccfe4c
+
Jarod Wilson 8ccfe4c
+	appleir->flags &= ~APPLEIR_OPENED;
Jarod Wilson 8ccfe4c
+
Jarod Wilson 8ccfe4c
+	mutex_unlock(&appleir_mutex);
Jarod Wilson 8ccfe4c
+}
Jarod Wilson 8ccfe4c
+
Jarod Wilson 8ccfe4c
+static int appleir_probe(struct usb_interface *intf,
Jarod Wilson 8ccfe4c
+			 const struct usb_device_id *id)
Jarod Wilson 8ccfe4c
+{
Jarod Wilson 8ccfe4c
+	struct usb_device *dev = interface_to_usbdev(intf);
Jarod Wilson 8ccfe4c
+	struct usb_endpoint_descriptor *endpoint;
Jarod Wilson 8ccfe4c
+	struct appleir *appleir = NULL;
Jarod Wilson 8ccfe4c
+	struct input_dev *input_dev;
Jarod Wilson 8ccfe4c
+	int retval = -ENOMEM;
Jarod Wilson 8ccfe4c
+	int i;
Jarod Wilson 8ccfe4c
+
Jarod Wilson 8ccfe4c
+	appleir = kzalloc(sizeof(struct appleir), GFP_KERNEL);
Jarod Wilson 8ccfe4c
+	if (!appleir)
Jarod Wilson 8ccfe4c
+		goto allocfail;
Jarod Wilson 8ccfe4c
+
Jarod Wilson 8ccfe4c
+	appleir->data = usb_alloc_coherent(dev, URB_SIZE, GFP_KERNEL,
Jarod Wilson 8ccfe4c
+					 &appleir->dma_buf);
Jarod Wilson 8ccfe4c
+	if (!appleir->data)
Jarod Wilson 8ccfe4c
+		goto usbfail;
Jarod Wilson 8ccfe4c
+
Jarod Wilson 8ccfe4c
+	appleir->urb = usb_alloc_urb(0, GFP_KERNEL);
Jarod Wilson 8ccfe4c
+	if (!appleir->urb)
Jarod Wilson 8ccfe4c
+		goto urbfail;
Jarod Wilson 8ccfe4c
+
Jarod Wilson 8ccfe4c
+	appleir->usbdev = dev;
Jarod Wilson 8ccfe4c
+
Jarod Wilson 8ccfe4c
+	input_dev = input_allocate_device();
Jarod Wilson 8ccfe4c
+	if (!input_dev)
Jarod Wilson 8ccfe4c
+		goto inputfail;
Jarod Wilson 8ccfe4c
+
Jarod Wilson 8ccfe4c
+	appleir->input_dev = input_dev;
Jarod Wilson 8ccfe4c
+
Jarod Wilson 8ccfe4c
+	usb_make_path(dev, appleir->phys, sizeof(appleir->phys));
Jarod Wilson 8ccfe4c
+	strlcpy(appleir->phys, "/input0", sizeof(appleir->phys));
Jarod Wilson 8ccfe4c
+
Jarod Wilson 8ccfe4c
+	input_dev->name = "Apple Infrared Remote Controller";
Jarod Wilson 8ccfe4c
+	input_dev->phys = appleir->phys;
Jarod Wilson 8ccfe4c
+	usb_to_input_id(dev, &input_dev->id);
Jarod Wilson 8ccfe4c
+	input_dev->dev.parent = &intf->dev;
Jarod Wilson 8ccfe4c
+	input_dev->keycode = appleir->keymap;
Jarod Wilson 8ccfe4c
+	input_dev->keycodesize = sizeof(unsigned short);
Jarod Wilson 8ccfe4c
+	input_dev->keycodemax = ARRAY_SIZE(appleir->keymap);
Jarod Wilson 8ccfe4c
+
Jarod Wilson 8ccfe4c
+	input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP);
Jarod Wilson 8ccfe4c
+
Jarod Wilson 8ccfe4c
+	memcpy(appleir->keymap, appleir_key_table, sizeof(appleir->keymap));
Jarod Wilson 8ccfe4c
+	for (i = 0; i < ARRAY_SIZE(appleir_key_table); i++)
Jarod Wilson 8ccfe4c
+		set_bit(appleir->keymap[i], input_dev->keybit);
Jarod Wilson 8ccfe4c
+	clear_bit(KEY_RESERVED, input_dev->keybit);
Jarod Wilson 8ccfe4c
+
Jarod Wilson 8ccfe4c
+	input_set_drvdata(input_dev, appleir);
Jarod Wilson 8ccfe4c
+	input_dev->open = appleir_open;
Jarod Wilson 8ccfe4c
+	input_dev->close = appleir_close;
Jarod Wilson 8ccfe4c
+
Jarod Wilson 8ccfe4c
+	endpoint = &intf->cur_altsetting->endpoint[0].desc;
Jarod Wilson 8ccfe4c
+
Jarod Wilson 8ccfe4c
+	usb_fill_int_urb(appleir->urb, dev,
Jarod Wilson 8ccfe4c
+			 usb_rcvintpipe(dev, endpoint->bEndpointAddress),
Jarod Wilson 8ccfe4c
+			 appleir->data, 8,
Jarod Wilson 8ccfe4c
+			 appleir_urb, appleir, endpoint->bInterval);
Jarod Wilson 8ccfe4c
+
Jarod Wilson 8ccfe4c
+	appleir->urb->transfer_dma = appleir->dma_buf;
Jarod Wilson 8ccfe4c
+	appleir->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
Jarod Wilson 8ccfe4c
+
Jarod Wilson 8ccfe4c
+	setup_timer(&appleir->key_up_timer,
Jarod Wilson 8ccfe4c
+		    key_up_tick, (unsigned long) appleir);
Jarod Wilson 8ccfe4c
+
Jarod Wilson 8ccfe4c
+	retval = input_register_device(appleir->input_dev);
Jarod Wilson 8ccfe4c
+	if (retval)
Jarod Wilson 8ccfe4c
+		goto inputfail;
Jarod Wilson 8ccfe4c
+
Jarod Wilson 8ccfe4c
+	usb_set_intfdata(intf, appleir);
Jarod Wilson 8ccfe4c
+
Jarod Wilson 8ccfe4c
+	return 0;
Jarod Wilson 8ccfe4c
+
Jarod Wilson 8ccfe4c
+inputfail:
Jarod Wilson 8ccfe4c
+	input_free_device(appleir->input_dev);
Jarod Wilson 8ccfe4c
+
Jarod Wilson 8ccfe4c
+urbfail:
Jarod Wilson 8ccfe4c
+	usb_free_urb(appleir->urb);
Jarod Wilson 8ccfe4c
+
Jarod Wilson 8ccfe4c
+usbfail:
Jarod Wilson 8ccfe4c
+	usb_free_coherent(dev, URB_SIZE, appleir->data,
Jarod Wilson 8ccfe4c
+			appleir->dma_buf);
Jarod Wilson 8ccfe4c
+
Jarod Wilson 8ccfe4c
+allocfail:
Jarod Wilson 8ccfe4c
+	kfree(appleir);
Jarod Wilson 8ccfe4c
+
Jarod Wilson 8ccfe4c
+	return retval;
Jarod Wilson 8ccfe4c
+}
Jarod Wilson 8ccfe4c
+
Jarod Wilson 8ccfe4c
+static void appleir_disconnect(struct usb_interface *intf)
Jarod Wilson 8ccfe4c
+{
Jarod Wilson 8ccfe4c
+	struct appleir *appleir = usb_get_intfdata(intf);
Jarod Wilson 8ccfe4c
+
Jarod Wilson 8ccfe4c
+	usb_set_intfdata(intf, NULL);
Jarod Wilson 8ccfe4c
+	input_unregister_device(appleir->input_dev);
Jarod Wilson 8ccfe4c
+	usb_free_urb(appleir->urb);
Jarod Wilson 8ccfe4c
+	usb_free_coherent(interface_to_usbdev(intf), URB_SIZE,
Jarod Wilson 8ccfe4c
+			appleir->data, appleir->dma_buf);
Jarod Wilson 8ccfe4c
+	kfree(appleir);
Jarod Wilson 8ccfe4c
+}
Jarod Wilson 8ccfe4c
+
Jarod Wilson 8ccfe4c
+static int appleir_suspend(struct usb_interface *interface,
Jarod Wilson 8ccfe4c
+			   pm_message_t message)
Jarod Wilson 8ccfe4c
+{
Jarod Wilson 8ccfe4c
+	struct appleir *appleir = usb_get_intfdata(interface);
Jarod Wilson 8ccfe4c
+
Jarod Wilson 8ccfe4c
+	mutex_lock(&appleir_mutex);
Jarod Wilson 8ccfe4c
+	if (appleir->flags & APPLEIR_OPENED)
Jarod Wilson 8ccfe4c
+		usb_kill_urb(appleir->urb);
Jarod Wilson 8ccfe4c
+
Jarod Wilson 8ccfe4c
+	appleir->flags |= APPLEIR_SUSPENDED;
Jarod Wilson 8ccfe4c
+
Jarod Wilson 8ccfe4c
+	mutex_unlock(&appleir_mutex);
Jarod Wilson 8ccfe4c
+
Jarod Wilson 8ccfe4c
+	return 0;
Jarod Wilson 8ccfe4c
+}
Jarod Wilson 8ccfe4c
+
Jarod Wilson 8ccfe4c
+static int appleir_resume(struct usb_interface *interface)
Jarod Wilson 8ccfe4c
+{
Jarod Wilson 8ccfe4c
+	struct appleir *appleir;
Jarod Wilson 8ccfe4c
+	int r = 0;
Jarod Wilson 8ccfe4c
+
Jarod Wilson 8ccfe4c
+	appleir = usb_get_intfdata(interface);
Jarod Wilson 8ccfe4c
+
Jarod Wilson 8ccfe4c
+	mutex_lock(&appleir_mutex);
Jarod Wilson 8ccfe4c
+	if (appleir->flags & APPLEIR_OPENED) {
Jarod Wilson 8ccfe4c
+		struct usb_endpoint_descriptor *endpoint;
Jarod Wilson 8ccfe4c
+
Jarod Wilson 8ccfe4c
+		endpoint = &interface->cur_altsetting->endpoint[0].desc;
Jarod Wilson 8ccfe4c
+		usb_fill_int_urb(appleir->urb, appleir->usbdev,
Jarod Wilson 8ccfe4c
+				 usb_rcvintpipe(appleir->usbdev, endpoint->bEndpointAddress),
Jarod Wilson 8ccfe4c
+				 appleir->data, 8,
Jarod Wilson 8ccfe4c
+				 appleir_urb, appleir, endpoint->bInterval);
Jarod Wilson 8ccfe4c
+		appleir->urb->transfer_dma = appleir->dma_buf;
Jarod Wilson 8ccfe4c
+		appleir->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
Jarod Wilson 8ccfe4c
+
Jarod Wilson 8ccfe4c
+		/* And reset the USB device */
Jarod Wilson 8ccfe4c
+		if (usb_submit_urb(appleir->urb, GFP_ATOMIC))
Jarod Wilson 8ccfe4c
+			r = -EIO;
Jarod Wilson 8ccfe4c
+	}
Jarod Wilson 8ccfe4c
+
Jarod Wilson 8ccfe4c
+	appleir->flags &= ~APPLEIR_SUSPENDED;
Jarod Wilson 8ccfe4c
+
Jarod Wilson 8ccfe4c
+	mutex_unlock(&appleir_mutex);
Jarod Wilson 8ccfe4c
+
Jarod Wilson 8ccfe4c
+	return r;
Jarod Wilson 8ccfe4c
+}
Jarod Wilson 8ccfe4c
+
Jarod Wilson 8ccfe4c
+static struct usb_driver appleir_driver = {
Jarod Wilson 8ccfe4c
+	.name                 = "appleir",
Jarod Wilson 8ccfe4c
+	.probe                = appleir_probe,
Jarod Wilson 8ccfe4c
+	.disconnect           = appleir_disconnect,
Jarod Wilson 8ccfe4c
+	.suspend              = appleir_suspend,
Jarod Wilson 8ccfe4c
+	.resume               = appleir_resume,
Jarod Wilson 8ccfe4c
+	.reset_resume         = appleir_resume,
Jarod Wilson 8ccfe4c
+	.id_table             = appleir_ids,
Jarod Wilson 8ccfe4c
+};
Jarod Wilson 8ccfe4c
+
Jarod Wilson 8ccfe4c
+static int __init appleir_init(void)
Jarod Wilson 8ccfe4c
+{
Jarod Wilson 8ccfe4c
+	return usb_register(&appleir_driver);
Jarod Wilson 8ccfe4c
+}
Jarod Wilson 8ccfe4c
+
Jarod Wilson 8ccfe4c
+static void __exit appleir_exit(void)
Jarod Wilson 8ccfe4c
+{
Jarod Wilson 8ccfe4c
+	usb_deregister(&appleir_driver);
Jarod Wilson 8ccfe4c
+}
Jarod Wilson 8ccfe4c
+
Jarod Wilson 8ccfe4c
+module_init(appleir_init);
Jarod Wilson 8ccfe4c
+module_exit(appleir_exit);
Bastien Nocera ea6d873
-- 
Bastien Nocera ea6d873
1.7.2.2
Bastien Nocera ea6d873