xref: /openbmc/linux/drivers/hid/hid-appleir.c (revision 8ebc80a25f9d9bf7a8e368b266d5b740c485c362)
19c92ab61SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
29a4a5574SBenjamin Tissoires /*
39a4a5574SBenjamin Tissoires  * HID driver for the apple ir device
49a4a5574SBenjamin Tissoires  *
59a4a5574SBenjamin Tissoires  * Original driver written by James McKenzie
69a4a5574SBenjamin Tissoires  * Ported to recent 2.6 kernel versions by Greg Kroah-Hartman <gregkh@suse.de>
79a4a5574SBenjamin Tissoires  * Updated to support newer remotes by Bastien Nocera <hadess@hadess.net>
89a4a5574SBenjamin Tissoires  * Ported to HID subsystem by Benjamin Tissoires <benjamin.tissoires@gmail.com>
99a4a5574SBenjamin Tissoires  *
109a4a5574SBenjamin Tissoires  * Copyright (C) 2006 James McKenzie
119a4a5574SBenjamin Tissoires  * Copyright (C) 2008 Greg Kroah-Hartman <greg@kroah.com>
129a4a5574SBenjamin Tissoires  * Copyright (C) 2008 Novell Inc.
139a4a5574SBenjamin Tissoires  * Copyright (C) 2010, 2012 Bastien Nocera <hadess@hadess.net>
149a4a5574SBenjamin Tissoires  * Copyright (C) 2013 Benjamin Tissoires <benjamin.tissoires@gmail.com>
159a4a5574SBenjamin Tissoires  * Copyright (C) 2013 Red Hat Inc. All Rights Reserved
169a4a5574SBenjamin Tissoires  */
179a4a5574SBenjamin Tissoires 
189a4a5574SBenjamin Tissoires #include <linux/device.h>
199a4a5574SBenjamin Tissoires #include <linux/hid.h>
209a4a5574SBenjamin Tissoires #include <linux/module.h>
219a4a5574SBenjamin Tissoires #include "hid-ids.h"
229a4a5574SBenjamin Tissoires 
239a4a5574SBenjamin Tissoires MODULE_AUTHOR("James McKenzie");
249a4a5574SBenjamin Tissoires MODULE_AUTHOR("Benjamin Tissoires <benjamin.tissoires@redhat.com>");
259a4a5574SBenjamin Tissoires MODULE_DESCRIPTION("HID Apple IR remote controls");
269a4a5574SBenjamin Tissoires MODULE_LICENSE("GPL");
279a4a5574SBenjamin Tissoires 
289a4a5574SBenjamin Tissoires #define KEY_MASK		0x0F
299a4a5574SBenjamin Tissoires #define TWO_PACKETS_MASK	0x40
309a4a5574SBenjamin Tissoires 
319a4a5574SBenjamin Tissoires /*
329a4a5574SBenjamin Tissoires  * James McKenzie has two devices both of which report the following
339a4a5574SBenjamin Tissoires  * 25 87 ee 83 0a	+
349a4a5574SBenjamin Tissoires  * 25 87 ee 83 0c	-
359a4a5574SBenjamin Tissoires  * 25 87 ee 83 09	<<
369a4a5574SBenjamin Tissoires  * 25 87 ee 83 06	>>
379a4a5574SBenjamin Tissoires  * 25 87 ee 83 05	>"
389a4a5574SBenjamin Tissoires  * 25 87 ee 83 03	menu
399a4a5574SBenjamin Tissoires  * 26 00 00 00 00	for key repeat
409a4a5574SBenjamin Tissoires  */
419a4a5574SBenjamin Tissoires 
429a4a5574SBenjamin Tissoires /*
439a4a5574SBenjamin Tissoires  * Thomas Glanzmann reports the following responses
449a4a5574SBenjamin Tissoires  * 25 87 ee ca 0b	+
459a4a5574SBenjamin Tissoires  * 25 87 ee ca 0d	-
469a4a5574SBenjamin Tissoires  * 25 87 ee ca 08	<<
479a4a5574SBenjamin Tissoires  * 25 87 ee ca 07	>>
489a4a5574SBenjamin Tissoires  * 25 87 ee ca 04	>"
499a4a5574SBenjamin Tissoires  * 25 87 ee ca 02	menu
509a4a5574SBenjamin Tissoires  * 26 00 00 00 00       for key repeat
519a4a5574SBenjamin Tissoires  *
529a4a5574SBenjamin Tissoires  * He also observes the following event sometimes
539a4a5574SBenjamin Tissoires  * sent after a key is release, which I interpret
549a4a5574SBenjamin Tissoires  * as a flat battery message
559a4a5574SBenjamin Tissoires  * 25 87 e0 ca 06	flat battery
569a4a5574SBenjamin Tissoires  */
579a4a5574SBenjamin Tissoires 
589a4a5574SBenjamin Tissoires /*
599a4a5574SBenjamin Tissoires  * Alexandre Karpenko reports the following responses for Device ID 0x8242
609a4a5574SBenjamin Tissoires  * 25 87 ee 47 0b	+
619a4a5574SBenjamin Tissoires  * 25 87 ee 47 0d	-
629a4a5574SBenjamin Tissoires  * 25 87 ee 47 08	<<
639a4a5574SBenjamin Tissoires  * 25 87 ee 47 07	>>
649a4a5574SBenjamin Tissoires  * 25 87 ee 47 04	>"
659a4a5574SBenjamin Tissoires  * 25 87 ee 47 02	menu
669a4a5574SBenjamin Tissoires  * 26 87 ee 47 **	for key repeat (** is the code of the key being held)
679a4a5574SBenjamin Tissoires  */
689a4a5574SBenjamin Tissoires 
699a4a5574SBenjamin Tissoires /*
709a4a5574SBenjamin Tissoires  * Bastien Nocera's remote
719a4a5574SBenjamin Tissoires  * 25 87 ee 91 5f	followed by
729a4a5574SBenjamin Tissoires  * 25 87 ee 91 05	gives you >"
739a4a5574SBenjamin Tissoires  *
749a4a5574SBenjamin Tissoires  * 25 87 ee 91 5c	followed by
759a4a5574SBenjamin Tissoires  * 25 87 ee 91 05	gives you the middle button
769a4a5574SBenjamin Tissoires  */
779a4a5574SBenjamin Tissoires 
789a4a5574SBenjamin Tissoires /*
799a4a5574SBenjamin Tissoires  * Fabien Andre's remote
809a4a5574SBenjamin Tissoires  * 25 87 ee a3 5e	followed by
819a4a5574SBenjamin Tissoires  * 25 87 ee a3 04	gives you >"
829a4a5574SBenjamin Tissoires  *
839a4a5574SBenjamin Tissoires  * 25 87 ee a3 5d	followed by
849a4a5574SBenjamin Tissoires  * 25 87 ee a3 04	gives you the middle button
859a4a5574SBenjamin Tissoires  */
869a4a5574SBenjamin Tissoires 
879a4a5574SBenjamin Tissoires static const unsigned short appleir_key_table[] = {
889a4a5574SBenjamin Tissoires 	KEY_RESERVED,
899a4a5574SBenjamin Tissoires 	KEY_MENU,
909a4a5574SBenjamin Tissoires 	KEY_PLAYPAUSE,
919a4a5574SBenjamin Tissoires 	KEY_FORWARD,
929a4a5574SBenjamin Tissoires 	KEY_BACK,
939a4a5574SBenjamin Tissoires 	KEY_VOLUMEUP,
949a4a5574SBenjamin Tissoires 	KEY_VOLUMEDOWN,
959a4a5574SBenjamin Tissoires 	KEY_RESERVED,
969a4a5574SBenjamin Tissoires 	KEY_RESERVED,
979a4a5574SBenjamin Tissoires 	KEY_RESERVED,
989a4a5574SBenjamin Tissoires 	KEY_RESERVED,
999a4a5574SBenjamin Tissoires 	KEY_RESERVED,
1009a4a5574SBenjamin Tissoires 	KEY_RESERVED,
1019a4a5574SBenjamin Tissoires 	KEY_RESERVED,
1029a4a5574SBenjamin Tissoires 	KEY_ENTER,
1039a4a5574SBenjamin Tissoires 	KEY_PLAYPAUSE,
1049a4a5574SBenjamin Tissoires 	KEY_RESERVED,
1059a4a5574SBenjamin Tissoires };
1069a4a5574SBenjamin Tissoires 
1079a4a5574SBenjamin Tissoires struct appleir {
1089a4a5574SBenjamin Tissoires 	struct input_dev *input_dev;
1099a4a5574SBenjamin Tissoires 	struct hid_device *hid;
1109a4a5574SBenjamin Tissoires 	unsigned short keymap[ARRAY_SIZE(appleir_key_table)];
1119a4a5574SBenjamin Tissoires 	struct timer_list key_up_timer;	/* timer for key up */
1129a4a5574SBenjamin Tissoires 	spinlock_t lock;		/* protects .current_key */
1139a4a5574SBenjamin Tissoires 	int current_key;		/* the currently pressed key */
1149a4a5574SBenjamin Tissoires 	int prev_key_idx;		/* key index in a 2 packets message */
1159a4a5574SBenjamin Tissoires };
1169a4a5574SBenjamin Tissoires 
get_key(int data)1179a4a5574SBenjamin Tissoires static int get_key(int data)
1189a4a5574SBenjamin Tissoires {
1199a4a5574SBenjamin Tissoires 	/*
1209a4a5574SBenjamin Tissoires 	 * The key is coded accross bits 2..9:
1219a4a5574SBenjamin Tissoires 	 *
1229a4a5574SBenjamin Tissoires 	 * 0x00 or 0x01 (        )	key:  0		-> KEY_RESERVED
1239a4a5574SBenjamin Tissoires 	 * 0x02 or 0x03 (  menu  )	key:  1		-> KEY_MENU
1249a4a5574SBenjamin Tissoires 	 * 0x04 or 0x05 (   >"   )	key:  2		-> KEY_PLAYPAUSE
1259a4a5574SBenjamin Tissoires 	 * 0x06 or 0x07 (   >>   )	key:  3		-> KEY_FORWARD
1269a4a5574SBenjamin Tissoires 	 * 0x08 or 0x09 (   <<   )	key:  4		-> KEY_BACK
1279a4a5574SBenjamin Tissoires 	 * 0x0a or 0x0b (    +   )	key:  5		-> KEY_VOLUMEUP
1289a4a5574SBenjamin Tissoires 	 * 0x0c or 0x0d (    -   )	key:  6		-> KEY_VOLUMEDOWN
1299a4a5574SBenjamin Tissoires 	 * 0x0e or 0x0f (        )	key:  7		-> KEY_RESERVED
1309a4a5574SBenjamin Tissoires 	 * 0x50 or 0x51 (        )	key:  8		-> KEY_RESERVED
1319a4a5574SBenjamin Tissoires 	 * 0x52 or 0x53 (        )	key:  9		-> KEY_RESERVED
1329a4a5574SBenjamin Tissoires 	 * 0x54 or 0x55 (        )	key: 10		-> KEY_RESERVED
1339a4a5574SBenjamin Tissoires 	 * 0x56 or 0x57 (        )	key: 11		-> KEY_RESERVED
1349a4a5574SBenjamin Tissoires 	 * 0x58 or 0x59 (        )	key: 12		-> KEY_RESERVED
1359a4a5574SBenjamin Tissoires 	 * 0x5a or 0x5b (        )	key: 13		-> KEY_RESERVED
1369a4a5574SBenjamin Tissoires 	 * 0x5c or 0x5d ( middle )	key: 14		-> KEY_ENTER
1379a4a5574SBenjamin Tissoires 	 * 0x5e or 0x5f (   >"   )	key: 15		-> KEY_PLAYPAUSE
1389a4a5574SBenjamin Tissoires 	 *
1399a4a5574SBenjamin Tissoires 	 * Packets starting with 0x5 are part of a two-packets message,
1409a4a5574SBenjamin Tissoires 	 * we notify the caller by sending a negative value.
1419a4a5574SBenjamin Tissoires 	 */
1429a4a5574SBenjamin Tissoires 	int key = (data >> 1) & KEY_MASK;
1439a4a5574SBenjamin Tissoires 
1449a4a5574SBenjamin Tissoires 	if ((data & TWO_PACKETS_MASK))
1459a4a5574SBenjamin Tissoires 		/* Part of a 2 packets-command */
1469a4a5574SBenjamin Tissoires 		key = -key;
1479a4a5574SBenjamin Tissoires 
1489a4a5574SBenjamin Tissoires 	return key;
1499a4a5574SBenjamin Tissoires }
1509a4a5574SBenjamin Tissoires 
key_up(struct hid_device * hid,struct appleir * appleir,int key)1519a4a5574SBenjamin Tissoires static void key_up(struct hid_device *hid, struct appleir *appleir, int key)
1529a4a5574SBenjamin Tissoires {
1539a4a5574SBenjamin Tissoires 	input_report_key(appleir->input_dev, key, 0);
1549a4a5574SBenjamin Tissoires 	input_sync(appleir->input_dev);
1559a4a5574SBenjamin Tissoires }
1569a4a5574SBenjamin Tissoires 
key_down(struct hid_device * hid,struct appleir * appleir,int key)1579a4a5574SBenjamin Tissoires static void key_down(struct hid_device *hid, struct appleir *appleir, int key)
1589a4a5574SBenjamin Tissoires {
1599a4a5574SBenjamin Tissoires 	input_report_key(appleir->input_dev, key, 1);
1609a4a5574SBenjamin Tissoires 	input_sync(appleir->input_dev);
1619a4a5574SBenjamin Tissoires }
1629a4a5574SBenjamin Tissoires 
battery_flat(struct appleir * appleir)1639a4a5574SBenjamin Tissoires static void battery_flat(struct appleir *appleir)
1649a4a5574SBenjamin Tissoires {
1659a4a5574SBenjamin Tissoires 	dev_err(&appleir->input_dev->dev, "possible flat battery?\n");
1669a4a5574SBenjamin Tissoires }
1679a4a5574SBenjamin Tissoires 
key_up_tick(struct timer_list * t)168e99e88a9SKees Cook static void key_up_tick(struct timer_list *t)
1699a4a5574SBenjamin Tissoires {
170e99e88a9SKees Cook 	struct appleir *appleir = from_timer(appleir, t, key_up_timer);
1719a4a5574SBenjamin Tissoires 	struct hid_device *hid = appleir->hid;
1729a4a5574SBenjamin Tissoires 	unsigned long flags;
1739a4a5574SBenjamin Tissoires 
1749a4a5574SBenjamin Tissoires 	spin_lock_irqsave(&appleir->lock, flags);
1759a4a5574SBenjamin Tissoires 	if (appleir->current_key) {
1769a4a5574SBenjamin Tissoires 		key_up(hid, appleir, appleir->current_key);
1779a4a5574SBenjamin Tissoires 		appleir->current_key = 0;
1789a4a5574SBenjamin Tissoires 	}
1799a4a5574SBenjamin Tissoires 	spin_unlock_irqrestore(&appleir->lock, flags);
1809a4a5574SBenjamin Tissoires }
1819a4a5574SBenjamin Tissoires 
appleir_raw_event(struct hid_device * hid,struct hid_report * report,u8 * data,int len)1829a4a5574SBenjamin Tissoires static int appleir_raw_event(struct hid_device *hid, struct hid_report *report,
1839a4a5574SBenjamin Tissoires 	 u8 *data, int len)
1849a4a5574SBenjamin Tissoires {
1859a4a5574SBenjamin Tissoires 	struct appleir *appleir = hid_get_drvdata(hid);
1869a4a5574SBenjamin Tissoires 	static const u8 keydown[] = { 0x25, 0x87, 0xee };
1879a4a5574SBenjamin Tissoires 	static const u8 keyrepeat[] = { 0x26, };
1889a4a5574SBenjamin Tissoires 	static const u8 flatbattery[] = { 0x25, 0x87, 0xe0 };
1899a4a5574SBenjamin Tissoires 	unsigned long flags;
1909a4a5574SBenjamin Tissoires 
191*fc69e2c3SDaniil Dulov 	if (len != 5 || !(hid->claimed & HID_CLAIMED_INPUT))
1929a4a5574SBenjamin Tissoires 		goto out;
1939a4a5574SBenjamin Tissoires 
1949a4a5574SBenjamin Tissoires 	if (!memcmp(data, keydown, sizeof(keydown))) {
1959a4a5574SBenjamin Tissoires 		int index;
1969a4a5574SBenjamin Tissoires 
1979a4a5574SBenjamin Tissoires 		spin_lock_irqsave(&appleir->lock, flags);
1989a4a5574SBenjamin Tissoires 		/*
1999a4a5574SBenjamin Tissoires 		 * If we already have a key down, take it up before marking
2009a4a5574SBenjamin Tissoires 		 * this one down
2019a4a5574SBenjamin Tissoires 		 */
2029a4a5574SBenjamin Tissoires 		if (appleir->current_key)
2039a4a5574SBenjamin Tissoires 			key_up(hid, appleir, appleir->current_key);
2049a4a5574SBenjamin Tissoires 
2059a4a5574SBenjamin Tissoires 		/* Handle dual packet commands */
2069a4a5574SBenjamin Tissoires 		if (appleir->prev_key_idx > 0)
2079a4a5574SBenjamin Tissoires 			index = appleir->prev_key_idx;
2089a4a5574SBenjamin Tissoires 		else
2099a4a5574SBenjamin Tissoires 			index = get_key(data[4]);
2109a4a5574SBenjamin Tissoires 
2119a4a5574SBenjamin Tissoires 		if (index >= 0) {
2129a4a5574SBenjamin Tissoires 			appleir->current_key = appleir->keymap[index];
2139a4a5574SBenjamin Tissoires 
2149a4a5574SBenjamin Tissoires 			key_down(hid, appleir, appleir->current_key);
2159a4a5574SBenjamin Tissoires 			/*
2169a4a5574SBenjamin Tissoires 			 * Remote doesn't do key up, either pull them up, in
2179a4a5574SBenjamin Tissoires 			 * the test above, or here set a timer which pulls
2189a4a5574SBenjamin Tissoires 			 * them up after 1/8 s
2199a4a5574SBenjamin Tissoires 			 */
2209a4a5574SBenjamin Tissoires 			mod_timer(&appleir->key_up_timer, jiffies + HZ / 8);
2219a4a5574SBenjamin Tissoires 			appleir->prev_key_idx = 0;
2229a4a5574SBenjamin Tissoires 		} else
2239a4a5574SBenjamin Tissoires 			/* Remember key for next packet */
2249a4a5574SBenjamin Tissoires 			appleir->prev_key_idx = -index;
2259a4a5574SBenjamin Tissoires 		spin_unlock_irqrestore(&appleir->lock, flags);
2269a4a5574SBenjamin Tissoires 		goto out;
2279a4a5574SBenjamin Tissoires 	}
2289a4a5574SBenjamin Tissoires 
2299a4a5574SBenjamin Tissoires 	appleir->prev_key_idx = 0;
2309a4a5574SBenjamin Tissoires 
2319a4a5574SBenjamin Tissoires 	if (!memcmp(data, keyrepeat, sizeof(keyrepeat))) {
2329a4a5574SBenjamin Tissoires 		key_down(hid, appleir, appleir->current_key);
2339a4a5574SBenjamin Tissoires 		/*
2349a4a5574SBenjamin Tissoires 		 * Remote doesn't do key up, either pull them up, in the test
2359a4a5574SBenjamin Tissoires 		 * above, or here set a timer which pulls them up after 1/8 s
2369a4a5574SBenjamin Tissoires 		 */
2379a4a5574SBenjamin Tissoires 		mod_timer(&appleir->key_up_timer, jiffies + HZ / 8);
2389a4a5574SBenjamin Tissoires 		goto out;
2399a4a5574SBenjamin Tissoires 	}
2409a4a5574SBenjamin Tissoires 
2419a4a5574SBenjamin Tissoires 	if (!memcmp(data, flatbattery, sizeof(flatbattery))) {
2429a4a5574SBenjamin Tissoires 		battery_flat(appleir);
2439a4a5574SBenjamin Tissoires 		/* Fall through */
2449a4a5574SBenjamin Tissoires 	}
2459a4a5574SBenjamin Tissoires 
2469a4a5574SBenjamin Tissoires out:
2479a4a5574SBenjamin Tissoires 	/* let hidraw and hiddev handle the report */
2489a4a5574SBenjamin Tissoires 	return 0;
2499a4a5574SBenjamin Tissoires }
2509a4a5574SBenjamin Tissoires 
appleir_input_configured(struct hid_device * hid,struct hid_input * hidinput)2519154301aSDmitry Torokhov static int appleir_input_configured(struct hid_device *hid,
2529a4a5574SBenjamin Tissoires 		struct hid_input *hidinput)
2539a4a5574SBenjamin Tissoires {
2549a4a5574SBenjamin Tissoires 	struct input_dev *input_dev = hidinput->input;
2559a4a5574SBenjamin Tissoires 	struct appleir *appleir = hid_get_drvdata(hid);
2569a4a5574SBenjamin Tissoires 	int i;
2579a4a5574SBenjamin Tissoires 
2589a4a5574SBenjamin Tissoires 	appleir->input_dev = input_dev;
2599a4a5574SBenjamin Tissoires 
2609a4a5574SBenjamin Tissoires 	input_dev->keycode = appleir->keymap;
2619a4a5574SBenjamin Tissoires 	input_dev->keycodesize = sizeof(unsigned short);
2629a4a5574SBenjamin Tissoires 	input_dev->keycodemax = ARRAY_SIZE(appleir->keymap);
2639a4a5574SBenjamin Tissoires 
2649a4a5574SBenjamin Tissoires 	input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP);
2659a4a5574SBenjamin Tissoires 
2669a4a5574SBenjamin Tissoires 	memcpy(appleir->keymap, appleir_key_table, sizeof(appleir->keymap));
2679a4a5574SBenjamin Tissoires 	for (i = 0; i < ARRAY_SIZE(appleir_key_table); i++)
2689a4a5574SBenjamin Tissoires 		set_bit(appleir->keymap[i], input_dev->keybit);
2699a4a5574SBenjamin Tissoires 	clear_bit(KEY_RESERVED, input_dev->keybit);
2709154301aSDmitry Torokhov 
2719154301aSDmitry Torokhov 	return 0;
2729a4a5574SBenjamin Tissoires }
2739a4a5574SBenjamin Tissoires 
appleir_input_mapping(struct hid_device * hid,struct hid_input * hi,struct hid_field * field,struct hid_usage * usage,unsigned long ** bit,int * max)2749a4a5574SBenjamin Tissoires static int appleir_input_mapping(struct hid_device *hid,
2759a4a5574SBenjamin Tissoires 		struct hid_input *hi, struct hid_field *field,
2769a4a5574SBenjamin Tissoires 		struct hid_usage *usage, unsigned long **bit, int *max)
2779a4a5574SBenjamin Tissoires {
2789a4a5574SBenjamin Tissoires 	return -1;
2799a4a5574SBenjamin Tissoires }
2809a4a5574SBenjamin Tissoires 
appleir_probe(struct hid_device * hid,const struct hid_device_id * id)2819a4a5574SBenjamin Tissoires static int appleir_probe(struct hid_device *hid, const struct hid_device_id *id)
2829a4a5574SBenjamin Tissoires {
2839a4a5574SBenjamin Tissoires 	int ret;
2849a4a5574SBenjamin Tissoires 	struct appleir *appleir;
2859a4a5574SBenjamin Tissoires 
286910a7e89SLucas Tanure 	appleir = devm_kzalloc(&hid->dev, sizeof(struct appleir), GFP_KERNEL);
287b08e8d8aSLucas Tanure 	if (!appleir)
288b08e8d8aSLucas Tanure 		return -ENOMEM;
2899a4a5574SBenjamin Tissoires 
2909a4a5574SBenjamin Tissoires 	appleir->hid = hid;
2919a4a5574SBenjamin Tissoires 
2923d18bd41SBenjamin Tissoires 	/* force input as some remotes bypass the input registration */
2933d18bd41SBenjamin Tissoires 	hid->quirks |= HID_QUIRK_HIDINPUT_FORCE;
2943d18bd41SBenjamin Tissoires 
2959a4a5574SBenjamin Tissoires 	spin_lock_init(&appleir->lock);
296e99e88a9SKees Cook 	timer_setup(&appleir->key_up_timer, key_up_tick, 0);
2979a4a5574SBenjamin Tissoires 
2989a4a5574SBenjamin Tissoires 	hid_set_drvdata(hid, appleir);
2999a4a5574SBenjamin Tissoires 
3009a4a5574SBenjamin Tissoires 	ret = hid_parse(hid);
3019a4a5574SBenjamin Tissoires 	if (ret) {
3029a4a5574SBenjamin Tissoires 		hid_err(hid, "parse failed\n");
3039a4a5574SBenjamin Tissoires 		goto fail;
3049a4a5574SBenjamin Tissoires 	}
3059a4a5574SBenjamin Tissoires 
3069a4a5574SBenjamin Tissoires 	ret = hid_hw_start(hid, HID_CONNECT_DEFAULT | HID_CONNECT_HIDDEV_FORCE);
3079a4a5574SBenjamin Tissoires 	if (ret) {
3089a4a5574SBenjamin Tissoires 		hid_err(hid, "hw start failed\n");
3099a4a5574SBenjamin Tissoires 		goto fail;
3109a4a5574SBenjamin Tissoires 	}
3119a4a5574SBenjamin Tissoires 
3129a4a5574SBenjamin Tissoires 	return 0;
3139a4a5574SBenjamin Tissoires fail:
314910a7e89SLucas Tanure 	devm_kfree(&hid->dev, appleir);
3159a4a5574SBenjamin Tissoires 	return ret;
3169a4a5574SBenjamin Tissoires }
3179a4a5574SBenjamin Tissoires 
appleir_remove(struct hid_device * hid)3189a4a5574SBenjamin Tissoires static void appleir_remove(struct hid_device *hid)
3199a4a5574SBenjamin Tissoires {
3209a4a5574SBenjamin Tissoires 	struct appleir *appleir = hid_get_drvdata(hid);
3219a4a5574SBenjamin Tissoires 	hid_hw_stop(hid);
3229a4a5574SBenjamin Tissoires 	del_timer_sync(&appleir->key_up_timer);
3239a4a5574SBenjamin Tissoires }
3249a4a5574SBenjamin Tissoires 
3259a4a5574SBenjamin Tissoires static const struct hid_device_id appleir_devices[] = {
3269a4a5574SBenjamin Tissoires 	{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL) },
3279a4a5574SBenjamin Tissoires 	{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL2) },
3289a4a5574SBenjamin Tissoires 	{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL3) },
3299a4a5574SBenjamin Tissoires 	{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL4) },
3309a4a5574SBenjamin Tissoires 	{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL5) },
3319a4a5574SBenjamin Tissoires 	{ }
3329a4a5574SBenjamin Tissoires };
3339a4a5574SBenjamin Tissoires MODULE_DEVICE_TABLE(hid, appleir_devices);
3349a4a5574SBenjamin Tissoires 
3359a4a5574SBenjamin Tissoires static struct hid_driver appleir_driver = {
3369a4a5574SBenjamin Tissoires 	.name = "appleir",
3379a4a5574SBenjamin Tissoires 	.id_table = appleir_devices,
3389a4a5574SBenjamin Tissoires 	.raw_event = appleir_raw_event,
3399a4a5574SBenjamin Tissoires 	.input_configured = appleir_input_configured,
3409a4a5574SBenjamin Tissoires 	.probe = appleir_probe,
3419a4a5574SBenjamin Tissoires 	.remove = appleir_remove,
3429a4a5574SBenjamin Tissoires 	.input_mapping = appleir_input_mapping,
3439a4a5574SBenjamin Tissoires };
3449a4a5574SBenjamin Tissoires module_hid_driver(appleir_driver);
345