162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * HID driver for the apple ir device 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Original driver written by James McKenzie 662306a36Sopenharmony_ci * Ported to recent 2.6 kernel versions by Greg Kroah-Hartman <gregkh@suse.de> 762306a36Sopenharmony_ci * Updated to support newer remotes by Bastien Nocera <hadess@hadess.net> 862306a36Sopenharmony_ci * Ported to HID subsystem by Benjamin Tissoires <benjamin.tissoires@gmail.com> 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * Copyright (C) 2006 James McKenzie 1162306a36Sopenharmony_ci * Copyright (C) 2008 Greg Kroah-Hartman <greg@kroah.com> 1262306a36Sopenharmony_ci * Copyright (C) 2008 Novell Inc. 1362306a36Sopenharmony_ci * Copyright (C) 2010, 2012 Bastien Nocera <hadess@hadess.net> 1462306a36Sopenharmony_ci * Copyright (C) 2013 Benjamin Tissoires <benjamin.tissoires@gmail.com> 1562306a36Sopenharmony_ci * Copyright (C) 2013 Red Hat Inc. All Rights Reserved 1662306a36Sopenharmony_ci */ 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#include <linux/device.h> 1962306a36Sopenharmony_ci#include <linux/hid.h> 2062306a36Sopenharmony_ci#include <linux/module.h> 2162306a36Sopenharmony_ci#include "hid-ids.h" 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ciMODULE_AUTHOR("James McKenzie"); 2462306a36Sopenharmony_ciMODULE_AUTHOR("Benjamin Tissoires <benjamin.tissoires@redhat.com>"); 2562306a36Sopenharmony_ciMODULE_DESCRIPTION("HID Apple IR remote controls"); 2662306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci#define KEY_MASK 0x0F 2962306a36Sopenharmony_ci#define TWO_PACKETS_MASK 0x40 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci/* 3262306a36Sopenharmony_ci * James McKenzie has two devices both of which report the following 3362306a36Sopenharmony_ci * 25 87 ee 83 0a + 3462306a36Sopenharmony_ci * 25 87 ee 83 0c - 3562306a36Sopenharmony_ci * 25 87 ee 83 09 << 3662306a36Sopenharmony_ci * 25 87 ee 83 06 >> 3762306a36Sopenharmony_ci * 25 87 ee 83 05 >" 3862306a36Sopenharmony_ci * 25 87 ee 83 03 menu 3962306a36Sopenharmony_ci * 26 00 00 00 00 for key repeat 4062306a36Sopenharmony_ci */ 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci/* 4362306a36Sopenharmony_ci * Thomas Glanzmann reports the following responses 4462306a36Sopenharmony_ci * 25 87 ee ca 0b + 4562306a36Sopenharmony_ci * 25 87 ee ca 0d - 4662306a36Sopenharmony_ci * 25 87 ee ca 08 << 4762306a36Sopenharmony_ci * 25 87 ee ca 07 >> 4862306a36Sopenharmony_ci * 25 87 ee ca 04 >" 4962306a36Sopenharmony_ci * 25 87 ee ca 02 menu 5062306a36Sopenharmony_ci * 26 00 00 00 00 for key repeat 5162306a36Sopenharmony_ci * 5262306a36Sopenharmony_ci * He also observes the following event sometimes 5362306a36Sopenharmony_ci * sent after a key is release, which I interpret 5462306a36Sopenharmony_ci * as a flat battery message 5562306a36Sopenharmony_ci * 25 87 e0 ca 06 flat battery 5662306a36Sopenharmony_ci */ 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci/* 5962306a36Sopenharmony_ci * Alexandre Karpenko reports the following responses for Device ID 0x8242 6062306a36Sopenharmony_ci * 25 87 ee 47 0b + 6162306a36Sopenharmony_ci * 25 87 ee 47 0d - 6262306a36Sopenharmony_ci * 25 87 ee 47 08 << 6362306a36Sopenharmony_ci * 25 87 ee 47 07 >> 6462306a36Sopenharmony_ci * 25 87 ee 47 04 >" 6562306a36Sopenharmony_ci * 25 87 ee 47 02 menu 6662306a36Sopenharmony_ci * 26 87 ee 47 ** for key repeat (** is the code of the key being held) 6762306a36Sopenharmony_ci */ 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci/* 7062306a36Sopenharmony_ci * Bastien Nocera's remote 7162306a36Sopenharmony_ci * 25 87 ee 91 5f followed by 7262306a36Sopenharmony_ci * 25 87 ee 91 05 gives you >" 7362306a36Sopenharmony_ci * 7462306a36Sopenharmony_ci * 25 87 ee 91 5c followed by 7562306a36Sopenharmony_ci * 25 87 ee 91 05 gives you the middle button 7662306a36Sopenharmony_ci */ 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci/* 7962306a36Sopenharmony_ci * Fabien Andre's remote 8062306a36Sopenharmony_ci * 25 87 ee a3 5e followed by 8162306a36Sopenharmony_ci * 25 87 ee a3 04 gives you >" 8262306a36Sopenharmony_ci * 8362306a36Sopenharmony_ci * 25 87 ee a3 5d followed by 8462306a36Sopenharmony_ci * 25 87 ee a3 04 gives you the middle button 8562306a36Sopenharmony_ci */ 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_cistatic const unsigned short appleir_key_table[] = { 8862306a36Sopenharmony_ci KEY_RESERVED, 8962306a36Sopenharmony_ci KEY_MENU, 9062306a36Sopenharmony_ci KEY_PLAYPAUSE, 9162306a36Sopenharmony_ci KEY_FORWARD, 9262306a36Sopenharmony_ci KEY_BACK, 9362306a36Sopenharmony_ci KEY_VOLUMEUP, 9462306a36Sopenharmony_ci KEY_VOLUMEDOWN, 9562306a36Sopenharmony_ci KEY_RESERVED, 9662306a36Sopenharmony_ci KEY_RESERVED, 9762306a36Sopenharmony_ci KEY_RESERVED, 9862306a36Sopenharmony_ci KEY_RESERVED, 9962306a36Sopenharmony_ci KEY_RESERVED, 10062306a36Sopenharmony_ci KEY_RESERVED, 10162306a36Sopenharmony_ci KEY_RESERVED, 10262306a36Sopenharmony_ci KEY_ENTER, 10362306a36Sopenharmony_ci KEY_PLAYPAUSE, 10462306a36Sopenharmony_ci KEY_RESERVED, 10562306a36Sopenharmony_ci}; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_cistruct appleir { 10862306a36Sopenharmony_ci struct input_dev *input_dev; 10962306a36Sopenharmony_ci struct hid_device *hid; 11062306a36Sopenharmony_ci unsigned short keymap[ARRAY_SIZE(appleir_key_table)]; 11162306a36Sopenharmony_ci struct timer_list key_up_timer; /* timer for key up */ 11262306a36Sopenharmony_ci spinlock_t lock; /* protects .current_key */ 11362306a36Sopenharmony_ci int current_key; /* the currently pressed key */ 11462306a36Sopenharmony_ci int prev_key_idx; /* key index in a 2 packets message */ 11562306a36Sopenharmony_ci}; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_cistatic int get_key(int data) 11862306a36Sopenharmony_ci{ 11962306a36Sopenharmony_ci /* 12062306a36Sopenharmony_ci * The key is coded accross bits 2..9: 12162306a36Sopenharmony_ci * 12262306a36Sopenharmony_ci * 0x00 or 0x01 ( ) key: 0 -> KEY_RESERVED 12362306a36Sopenharmony_ci * 0x02 or 0x03 ( menu ) key: 1 -> KEY_MENU 12462306a36Sopenharmony_ci * 0x04 or 0x05 ( >" ) key: 2 -> KEY_PLAYPAUSE 12562306a36Sopenharmony_ci * 0x06 or 0x07 ( >> ) key: 3 -> KEY_FORWARD 12662306a36Sopenharmony_ci * 0x08 or 0x09 ( << ) key: 4 -> KEY_BACK 12762306a36Sopenharmony_ci * 0x0a or 0x0b ( + ) key: 5 -> KEY_VOLUMEUP 12862306a36Sopenharmony_ci * 0x0c or 0x0d ( - ) key: 6 -> KEY_VOLUMEDOWN 12962306a36Sopenharmony_ci * 0x0e or 0x0f ( ) key: 7 -> KEY_RESERVED 13062306a36Sopenharmony_ci * 0x50 or 0x51 ( ) key: 8 -> KEY_RESERVED 13162306a36Sopenharmony_ci * 0x52 or 0x53 ( ) key: 9 -> KEY_RESERVED 13262306a36Sopenharmony_ci * 0x54 or 0x55 ( ) key: 10 -> KEY_RESERVED 13362306a36Sopenharmony_ci * 0x56 or 0x57 ( ) key: 11 -> KEY_RESERVED 13462306a36Sopenharmony_ci * 0x58 or 0x59 ( ) key: 12 -> KEY_RESERVED 13562306a36Sopenharmony_ci * 0x5a or 0x5b ( ) key: 13 -> KEY_RESERVED 13662306a36Sopenharmony_ci * 0x5c or 0x5d ( middle ) key: 14 -> KEY_ENTER 13762306a36Sopenharmony_ci * 0x5e or 0x5f ( >" ) key: 15 -> KEY_PLAYPAUSE 13862306a36Sopenharmony_ci * 13962306a36Sopenharmony_ci * Packets starting with 0x5 are part of a two-packets message, 14062306a36Sopenharmony_ci * we notify the caller by sending a negative value. 14162306a36Sopenharmony_ci */ 14262306a36Sopenharmony_ci int key = (data >> 1) & KEY_MASK; 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci if ((data & TWO_PACKETS_MASK)) 14562306a36Sopenharmony_ci /* Part of a 2 packets-command */ 14662306a36Sopenharmony_ci key = -key; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci return key; 14962306a36Sopenharmony_ci} 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_cistatic void key_up(struct hid_device *hid, struct appleir *appleir, int key) 15262306a36Sopenharmony_ci{ 15362306a36Sopenharmony_ci input_report_key(appleir->input_dev, key, 0); 15462306a36Sopenharmony_ci input_sync(appleir->input_dev); 15562306a36Sopenharmony_ci} 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_cistatic void key_down(struct hid_device *hid, struct appleir *appleir, int key) 15862306a36Sopenharmony_ci{ 15962306a36Sopenharmony_ci input_report_key(appleir->input_dev, key, 1); 16062306a36Sopenharmony_ci input_sync(appleir->input_dev); 16162306a36Sopenharmony_ci} 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_cistatic void battery_flat(struct appleir *appleir) 16462306a36Sopenharmony_ci{ 16562306a36Sopenharmony_ci dev_err(&appleir->input_dev->dev, "possible flat battery?\n"); 16662306a36Sopenharmony_ci} 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_cistatic void key_up_tick(struct timer_list *t) 16962306a36Sopenharmony_ci{ 17062306a36Sopenharmony_ci struct appleir *appleir = from_timer(appleir, t, key_up_timer); 17162306a36Sopenharmony_ci struct hid_device *hid = appleir->hid; 17262306a36Sopenharmony_ci unsigned long flags; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci spin_lock_irqsave(&appleir->lock, flags); 17562306a36Sopenharmony_ci if (appleir->current_key) { 17662306a36Sopenharmony_ci key_up(hid, appleir, appleir->current_key); 17762306a36Sopenharmony_ci appleir->current_key = 0; 17862306a36Sopenharmony_ci } 17962306a36Sopenharmony_ci spin_unlock_irqrestore(&appleir->lock, flags); 18062306a36Sopenharmony_ci} 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_cistatic int appleir_raw_event(struct hid_device *hid, struct hid_report *report, 18362306a36Sopenharmony_ci u8 *data, int len) 18462306a36Sopenharmony_ci{ 18562306a36Sopenharmony_ci struct appleir *appleir = hid_get_drvdata(hid); 18662306a36Sopenharmony_ci static const u8 keydown[] = { 0x25, 0x87, 0xee }; 18762306a36Sopenharmony_ci static const u8 keyrepeat[] = { 0x26, }; 18862306a36Sopenharmony_ci static const u8 flatbattery[] = { 0x25, 0x87, 0xe0 }; 18962306a36Sopenharmony_ci unsigned long flags; 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci if (len != 5) 19262306a36Sopenharmony_ci goto out; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci if (!memcmp(data, keydown, sizeof(keydown))) { 19562306a36Sopenharmony_ci int index; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci spin_lock_irqsave(&appleir->lock, flags); 19862306a36Sopenharmony_ci /* 19962306a36Sopenharmony_ci * If we already have a key down, take it up before marking 20062306a36Sopenharmony_ci * this one down 20162306a36Sopenharmony_ci */ 20262306a36Sopenharmony_ci if (appleir->current_key) 20362306a36Sopenharmony_ci key_up(hid, appleir, appleir->current_key); 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci /* Handle dual packet commands */ 20662306a36Sopenharmony_ci if (appleir->prev_key_idx > 0) 20762306a36Sopenharmony_ci index = appleir->prev_key_idx; 20862306a36Sopenharmony_ci else 20962306a36Sopenharmony_ci index = get_key(data[4]); 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci if (index >= 0) { 21262306a36Sopenharmony_ci appleir->current_key = appleir->keymap[index]; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci key_down(hid, appleir, appleir->current_key); 21562306a36Sopenharmony_ci /* 21662306a36Sopenharmony_ci * Remote doesn't do key up, either pull them up, in 21762306a36Sopenharmony_ci * the test above, or here set a timer which pulls 21862306a36Sopenharmony_ci * them up after 1/8 s 21962306a36Sopenharmony_ci */ 22062306a36Sopenharmony_ci mod_timer(&appleir->key_up_timer, jiffies + HZ / 8); 22162306a36Sopenharmony_ci appleir->prev_key_idx = 0; 22262306a36Sopenharmony_ci } else 22362306a36Sopenharmony_ci /* Remember key for next packet */ 22462306a36Sopenharmony_ci appleir->prev_key_idx = -index; 22562306a36Sopenharmony_ci spin_unlock_irqrestore(&appleir->lock, flags); 22662306a36Sopenharmony_ci goto out; 22762306a36Sopenharmony_ci } 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci appleir->prev_key_idx = 0; 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci if (!memcmp(data, keyrepeat, sizeof(keyrepeat))) { 23262306a36Sopenharmony_ci key_down(hid, appleir, appleir->current_key); 23362306a36Sopenharmony_ci /* 23462306a36Sopenharmony_ci * Remote doesn't do key up, either pull them up, in the test 23562306a36Sopenharmony_ci * above, or here set a timer which pulls them up after 1/8 s 23662306a36Sopenharmony_ci */ 23762306a36Sopenharmony_ci mod_timer(&appleir->key_up_timer, jiffies + HZ / 8); 23862306a36Sopenharmony_ci goto out; 23962306a36Sopenharmony_ci } 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci if (!memcmp(data, flatbattery, sizeof(flatbattery))) { 24262306a36Sopenharmony_ci battery_flat(appleir); 24362306a36Sopenharmony_ci /* Fall through */ 24462306a36Sopenharmony_ci } 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ciout: 24762306a36Sopenharmony_ci /* let hidraw and hiddev handle the report */ 24862306a36Sopenharmony_ci return 0; 24962306a36Sopenharmony_ci} 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_cistatic int appleir_input_configured(struct hid_device *hid, 25262306a36Sopenharmony_ci struct hid_input *hidinput) 25362306a36Sopenharmony_ci{ 25462306a36Sopenharmony_ci struct input_dev *input_dev = hidinput->input; 25562306a36Sopenharmony_ci struct appleir *appleir = hid_get_drvdata(hid); 25662306a36Sopenharmony_ci int i; 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci appleir->input_dev = input_dev; 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci input_dev->keycode = appleir->keymap; 26162306a36Sopenharmony_ci input_dev->keycodesize = sizeof(unsigned short); 26262306a36Sopenharmony_ci input_dev->keycodemax = ARRAY_SIZE(appleir->keymap); 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP); 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci memcpy(appleir->keymap, appleir_key_table, sizeof(appleir->keymap)); 26762306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(appleir_key_table); i++) 26862306a36Sopenharmony_ci set_bit(appleir->keymap[i], input_dev->keybit); 26962306a36Sopenharmony_ci clear_bit(KEY_RESERVED, input_dev->keybit); 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci return 0; 27262306a36Sopenharmony_ci} 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_cistatic int appleir_input_mapping(struct hid_device *hid, 27562306a36Sopenharmony_ci struct hid_input *hi, struct hid_field *field, 27662306a36Sopenharmony_ci struct hid_usage *usage, unsigned long **bit, int *max) 27762306a36Sopenharmony_ci{ 27862306a36Sopenharmony_ci return -1; 27962306a36Sopenharmony_ci} 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_cistatic int appleir_probe(struct hid_device *hid, const struct hid_device_id *id) 28262306a36Sopenharmony_ci{ 28362306a36Sopenharmony_ci int ret; 28462306a36Sopenharmony_ci struct appleir *appleir; 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci appleir = devm_kzalloc(&hid->dev, sizeof(struct appleir), GFP_KERNEL); 28762306a36Sopenharmony_ci if (!appleir) 28862306a36Sopenharmony_ci return -ENOMEM; 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci appleir->hid = hid; 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci /* force input as some remotes bypass the input registration */ 29362306a36Sopenharmony_ci hid->quirks |= HID_QUIRK_HIDINPUT_FORCE; 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci spin_lock_init(&appleir->lock); 29662306a36Sopenharmony_ci timer_setup(&appleir->key_up_timer, key_up_tick, 0); 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci hid_set_drvdata(hid, appleir); 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci ret = hid_parse(hid); 30162306a36Sopenharmony_ci if (ret) { 30262306a36Sopenharmony_ci hid_err(hid, "parse failed\n"); 30362306a36Sopenharmony_ci goto fail; 30462306a36Sopenharmony_ci } 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci ret = hid_hw_start(hid, HID_CONNECT_DEFAULT | HID_CONNECT_HIDDEV_FORCE); 30762306a36Sopenharmony_ci if (ret) { 30862306a36Sopenharmony_ci hid_err(hid, "hw start failed\n"); 30962306a36Sopenharmony_ci goto fail; 31062306a36Sopenharmony_ci } 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci return 0; 31362306a36Sopenharmony_cifail: 31462306a36Sopenharmony_ci devm_kfree(&hid->dev, appleir); 31562306a36Sopenharmony_ci return ret; 31662306a36Sopenharmony_ci} 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_cistatic void appleir_remove(struct hid_device *hid) 31962306a36Sopenharmony_ci{ 32062306a36Sopenharmony_ci struct appleir *appleir = hid_get_drvdata(hid); 32162306a36Sopenharmony_ci hid_hw_stop(hid); 32262306a36Sopenharmony_ci del_timer_sync(&appleir->key_up_timer); 32362306a36Sopenharmony_ci} 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_cistatic const struct hid_device_id appleir_devices[] = { 32662306a36Sopenharmony_ci { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL) }, 32762306a36Sopenharmony_ci { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL2) }, 32862306a36Sopenharmony_ci { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL3) }, 32962306a36Sopenharmony_ci { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL4) }, 33062306a36Sopenharmony_ci { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL5) }, 33162306a36Sopenharmony_ci { } 33262306a36Sopenharmony_ci}; 33362306a36Sopenharmony_ciMODULE_DEVICE_TABLE(hid, appleir_devices); 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_cistatic struct hid_driver appleir_driver = { 33662306a36Sopenharmony_ci .name = "appleir", 33762306a36Sopenharmony_ci .id_table = appleir_devices, 33862306a36Sopenharmony_ci .raw_event = appleir_raw_event, 33962306a36Sopenharmony_ci .input_configured = appleir_input_configured, 34062306a36Sopenharmony_ci .probe = appleir_probe, 34162306a36Sopenharmony_ci .remove = appleir_remove, 34262306a36Sopenharmony_ci .input_mapping = appleir_input_mapping, 34362306a36Sopenharmony_ci}; 34462306a36Sopenharmony_cimodule_hid_driver(appleir_driver); 345