18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * USB ATI Remote support 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2011, 2012 Anssi Hannula <anssi.hannula@iki.fi> 68c2ecf20Sopenharmony_ci * Version 2.2.0 Copyright (c) 2004 Torrey Hoffman <thoffman@arnor.net> 78c2ecf20Sopenharmony_ci * Version 2.1.1 Copyright (c) 2002 Vladimir Dergachev 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * This 2.2.0 version is a rewrite / cleanup of the 2.1.1 driver, including 108c2ecf20Sopenharmony_ci * porting to the 2.6 kernel interfaces, along with other modification 118c2ecf20Sopenharmony_ci * to better match the style of the existing usb/input drivers. However, the 128c2ecf20Sopenharmony_ci * protocol and hardware handling is essentially unchanged from 2.1.1. 138c2ecf20Sopenharmony_ci * 148c2ecf20Sopenharmony_ci * The 2.1.1 driver was derived from the usbati_remote and usbkbd drivers by 158c2ecf20Sopenharmony_ci * Vojtech Pavlik. 168c2ecf20Sopenharmony_ci * 178c2ecf20Sopenharmony_ci * Changes: 188c2ecf20Sopenharmony_ci * 198c2ecf20Sopenharmony_ci * Feb 2004: Torrey Hoffman <thoffman@arnor.net> 208c2ecf20Sopenharmony_ci * Version 2.2.0 218c2ecf20Sopenharmony_ci * Jun 2004: Torrey Hoffman <thoffman@arnor.net> 228c2ecf20Sopenharmony_ci * Version 2.2.1 238c2ecf20Sopenharmony_ci * Added key repeat support contributed by: 248c2ecf20Sopenharmony_ci * Vincent Vanackere <vanackere@lif.univ-mrs.fr> 258c2ecf20Sopenharmony_ci * Added support for the "Lola" remote contributed by: 268c2ecf20Sopenharmony_ci * Seth Cohn <sethcohn@yahoo.com> 278c2ecf20Sopenharmony_ci * 288c2ecf20Sopenharmony_ci * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 298c2ecf20Sopenharmony_ci * 308c2ecf20Sopenharmony_ci * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 318c2ecf20Sopenharmony_ci * 328c2ecf20Sopenharmony_ci * Hardware & software notes 338c2ecf20Sopenharmony_ci * 348c2ecf20Sopenharmony_ci * These remote controls are distributed by ATI as part of their 358c2ecf20Sopenharmony_ci * "All-In-Wonder" video card packages. The receiver self-identifies as a 368c2ecf20Sopenharmony_ci * "USB Receiver" with manufacturer "X10 Wireless Technology Inc". 378c2ecf20Sopenharmony_ci * 388c2ecf20Sopenharmony_ci * The "Lola" remote is available from X10. See: 398c2ecf20Sopenharmony_ci * http://www.x10.com/products/lola_sg1.htm 408c2ecf20Sopenharmony_ci * The Lola is similar to the ATI remote but has no mouse support, and slightly 418c2ecf20Sopenharmony_ci * different keys. 428c2ecf20Sopenharmony_ci * 438c2ecf20Sopenharmony_ci * It is possible to use multiple receivers and remotes on multiple computers 448c2ecf20Sopenharmony_ci * simultaneously by configuring them to use specific channels. 458c2ecf20Sopenharmony_ci * 468c2ecf20Sopenharmony_ci * The RF protocol used by the remote supports 16 distinct channels, 1 to 16. 478c2ecf20Sopenharmony_ci * Actually, it may even support more, at least in some revisions of the 488c2ecf20Sopenharmony_ci * hardware. 498c2ecf20Sopenharmony_ci * 508c2ecf20Sopenharmony_ci * Each remote can be configured to transmit on one channel as follows: 518c2ecf20Sopenharmony_ci * - Press and hold the "hand icon" button. 528c2ecf20Sopenharmony_ci * - When the red LED starts to blink, let go of the "hand icon" button. 538c2ecf20Sopenharmony_ci * - When it stops blinking, input the channel code as two digits, from 01 548c2ecf20Sopenharmony_ci * to 16, and press the hand icon again. 558c2ecf20Sopenharmony_ci * 568c2ecf20Sopenharmony_ci * The timing can be a little tricky. Try loading the module with debug=1 578c2ecf20Sopenharmony_ci * to have the kernel print out messages about the remote control number 588c2ecf20Sopenharmony_ci * and mask. Note: debugging prints remote numbers as zero-based hexadecimal. 598c2ecf20Sopenharmony_ci * 608c2ecf20Sopenharmony_ci * The driver has a "channel_mask" parameter. This bitmask specifies which 618c2ecf20Sopenharmony_ci * channels will be ignored by the module. To mask out channels, just add 628c2ecf20Sopenharmony_ci * all the 2^channel_number values together. 638c2ecf20Sopenharmony_ci * 648c2ecf20Sopenharmony_ci * For instance, set channel_mask = 2^4 = 16 (binary 10000) to make ati_remote 658c2ecf20Sopenharmony_ci * ignore signals coming from remote controls transmitting on channel 4, but 668c2ecf20Sopenharmony_ci * accept all other channels. 678c2ecf20Sopenharmony_ci * 688c2ecf20Sopenharmony_ci * Or, set channel_mask = 65533, (0xFFFD), and all channels except 1 will be 698c2ecf20Sopenharmony_ci * ignored. 708c2ecf20Sopenharmony_ci * 718c2ecf20Sopenharmony_ci * The default is 0 (respond to all channels). Bit 0 and bits 17-32 of this 728c2ecf20Sopenharmony_ci * parameter are unused. 738c2ecf20Sopenharmony_ci */ 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci#include <linux/kernel.h> 768c2ecf20Sopenharmony_ci#include <linux/errno.h> 778c2ecf20Sopenharmony_ci#include <linux/init.h> 788c2ecf20Sopenharmony_ci#include <linux/slab.h> 798c2ecf20Sopenharmony_ci#include <linux/module.h> 808c2ecf20Sopenharmony_ci#include <linux/mutex.h> 818c2ecf20Sopenharmony_ci#include <linux/usb/input.h> 828c2ecf20Sopenharmony_ci#include <linux/wait.h> 838c2ecf20Sopenharmony_ci#include <linux/jiffies.h> 848c2ecf20Sopenharmony_ci#include <media/rc-core.h> 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci/* 878c2ecf20Sopenharmony_ci * Module and Version Information, Module Parameters 888c2ecf20Sopenharmony_ci */ 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci#define ATI_REMOTE_VENDOR_ID 0x0bc7 918c2ecf20Sopenharmony_ci#define LOLA_REMOTE_PRODUCT_ID 0x0002 928c2ecf20Sopenharmony_ci#define LOLA2_REMOTE_PRODUCT_ID 0x0003 938c2ecf20Sopenharmony_ci#define ATI_REMOTE_PRODUCT_ID 0x0004 948c2ecf20Sopenharmony_ci#define NVIDIA_REMOTE_PRODUCT_ID 0x0005 958c2ecf20Sopenharmony_ci#define MEDION_REMOTE_PRODUCT_ID 0x0006 968c2ecf20Sopenharmony_ci#define FIREFLY_REMOTE_PRODUCT_ID 0x0008 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci#define DRIVER_VERSION "2.2.1" 998c2ecf20Sopenharmony_ci#define DRIVER_AUTHOR "Torrey Hoffman <thoffman@arnor.net>" 1008c2ecf20Sopenharmony_ci#define DRIVER_DESC "ATI/X10 RF USB Remote Control" 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci#define NAME_BUFSIZE 80 /* size of product name, path buffers */ 1038c2ecf20Sopenharmony_ci#define DATA_BUFSIZE 63 /* size of URB data buffers */ 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci/* 1068c2ecf20Sopenharmony_ci * Duplicate event filtering time. 1078c2ecf20Sopenharmony_ci * Sequential, identical KIND_FILTERED inputs with less than 1088c2ecf20Sopenharmony_ci * FILTER_TIME milliseconds between them are considered as repeat 1098c2ecf20Sopenharmony_ci * events. The hardware generates 5 events for the first keypress 1108c2ecf20Sopenharmony_ci * and we have to take this into account for an accurate repeat 1118c2ecf20Sopenharmony_ci * behaviour. 1128c2ecf20Sopenharmony_ci */ 1138c2ecf20Sopenharmony_ci#define FILTER_TIME 60 /* msec */ 1148c2ecf20Sopenharmony_ci#define REPEAT_DELAY 500 /* msec */ 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_cistatic unsigned long channel_mask; 1178c2ecf20Sopenharmony_cimodule_param(channel_mask, ulong, 0644); 1188c2ecf20Sopenharmony_ciMODULE_PARM_DESC(channel_mask, "Bitmask of remote control channels to ignore"); 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_cistatic int debug; 1218c2ecf20Sopenharmony_cimodule_param(debug, int, 0644); 1228c2ecf20Sopenharmony_ciMODULE_PARM_DESC(debug, "Enable extra debug messages and information"); 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_cistatic int repeat_filter = FILTER_TIME; 1258c2ecf20Sopenharmony_cimodule_param(repeat_filter, int, 0644); 1268c2ecf20Sopenharmony_ciMODULE_PARM_DESC(repeat_filter, "Repeat filter time, default = 60 msec"); 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_cistatic int repeat_delay = REPEAT_DELAY; 1298c2ecf20Sopenharmony_cimodule_param(repeat_delay, int, 0644); 1308c2ecf20Sopenharmony_ciMODULE_PARM_DESC(repeat_delay, "Delay before sending repeats, default = 500 msec"); 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_cistatic bool mouse = true; 1338c2ecf20Sopenharmony_cimodule_param(mouse, bool, 0444); 1348c2ecf20Sopenharmony_ciMODULE_PARM_DESC(mouse, "Enable mouse device, default = yes"); 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci#define dbginfo(dev, format, arg...) \ 1378c2ecf20Sopenharmony_ci do { if (debug) dev_info(dev , format , ## arg); } while (0) 1388c2ecf20Sopenharmony_ci#undef err 1398c2ecf20Sopenharmony_ci#define err(format, arg...) printk(KERN_ERR format , ## arg) 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_cistruct ati_receiver_type { 1428c2ecf20Sopenharmony_ci /* either default_keymap or get_default_keymap should be set */ 1438c2ecf20Sopenharmony_ci const char *default_keymap; 1448c2ecf20Sopenharmony_ci const char *(*get_default_keymap)(struct usb_interface *interface); 1458c2ecf20Sopenharmony_ci}; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_cistatic const char *get_medion_keymap(struct usb_interface *interface) 1488c2ecf20Sopenharmony_ci{ 1498c2ecf20Sopenharmony_ci struct usb_device *udev = interface_to_usbdev(interface); 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci /* 1528c2ecf20Sopenharmony_ci * There are many different Medion remotes shipped with a receiver 1538c2ecf20Sopenharmony_ci * with the same usb id, but the receivers have subtle differences 1548c2ecf20Sopenharmony_ci * in the USB descriptors allowing us to detect them. 1558c2ecf20Sopenharmony_ci */ 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci if (udev->manufacturer && udev->product) { 1588c2ecf20Sopenharmony_ci if (udev->actconfig->desc.bmAttributes & USB_CONFIG_ATT_WAKEUP) { 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci if (!strcmp(udev->manufacturer, "X10 Wireless Technology Inc") 1618c2ecf20Sopenharmony_ci && !strcmp(udev->product, "USB Receiver")) 1628c2ecf20Sopenharmony_ci return RC_MAP_MEDION_X10_DIGITAINER; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci if (!strcmp(udev->manufacturer, "X10 WTI") 1658c2ecf20Sopenharmony_ci && !strcmp(udev->product, "RF receiver")) 1668c2ecf20Sopenharmony_ci return RC_MAP_MEDION_X10_OR2X; 1678c2ecf20Sopenharmony_ci } else { 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci if (!strcmp(udev->manufacturer, "X10 Wireless Technology Inc") 1708c2ecf20Sopenharmony_ci && !strcmp(udev->product, "USB Receiver")) 1718c2ecf20Sopenharmony_ci return RC_MAP_MEDION_X10; 1728c2ecf20Sopenharmony_ci } 1738c2ecf20Sopenharmony_ci } 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci dev_info(&interface->dev, 1768c2ecf20Sopenharmony_ci "Unknown Medion X10 receiver, using default ati_remote Medion keymap\n"); 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci return RC_MAP_MEDION_X10; 1798c2ecf20Sopenharmony_ci} 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_cistatic const struct ati_receiver_type type_ati = { 1828c2ecf20Sopenharmony_ci .default_keymap = RC_MAP_ATI_X10 1838c2ecf20Sopenharmony_ci}; 1848c2ecf20Sopenharmony_cistatic const struct ati_receiver_type type_medion = { 1858c2ecf20Sopenharmony_ci .get_default_keymap = get_medion_keymap 1868c2ecf20Sopenharmony_ci}; 1878c2ecf20Sopenharmony_cistatic const struct ati_receiver_type type_firefly = { 1888c2ecf20Sopenharmony_ci .default_keymap = RC_MAP_SNAPSTREAM_FIREFLY 1898c2ecf20Sopenharmony_ci}; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_cistatic const struct usb_device_id ati_remote_table[] = { 1928c2ecf20Sopenharmony_ci { 1938c2ecf20Sopenharmony_ci USB_DEVICE(ATI_REMOTE_VENDOR_ID, LOLA_REMOTE_PRODUCT_ID), 1948c2ecf20Sopenharmony_ci .driver_info = (unsigned long)&type_ati 1958c2ecf20Sopenharmony_ci }, 1968c2ecf20Sopenharmony_ci { 1978c2ecf20Sopenharmony_ci USB_DEVICE(ATI_REMOTE_VENDOR_ID, LOLA2_REMOTE_PRODUCT_ID), 1988c2ecf20Sopenharmony_ci .driver_info = (unsigned long)&type_ati 1998c2ecf20Sopenharmony_ci }, 2008c2ecf20Sopenharmony_ci { 2018c2ecf20Sopenharmony_ci USB_DEVICE(ATI_REMOTE_VENDOR_ID, ATI_REMOTE_PRODUCT_ID), 2028c2ecf20Sopenharmony_ci .driver_info = (unsigned long)&type_ati 2038c2ecf20Sopenharmony_ci }, 2048c2ecf20Sopenharmony_ci { 2058c2ecf20Sopenharmony_ci USB_DEVICE(ATI_REMOTE_VENDOR_ID, NVIDIA_REMOTE_PRODUCT_ID), 2068c2ecf20Sopenharmony_ci .driver_info = (unsigned long)&type_ati 2078c2ecf20Sopenharmony_ci }, 2088c2ecf20Sopenharmony_ci { 2098c2ecf20Sopenharmony_ci USB_DEVICE(ATI_REMOTE_VENDOR_ID, MEDION_REMOTE_PRODUCT_ID), 2108c2ecf20Sopenharmony_ci .driver_info = (unsigned long)&type_medion 2118c2ecf20Sopenharmony_ci }, 2128c2ecf20Sopenharmony_ci { 2138c2ecf20Sopenharmony_ci USB_DEVICE(ATI_REMOTE_VENDOR_ID, FIREFLY_REMOTE_PRODUCT_ID), 2148c2ecf20Sopenharmony_ci .driver_info = (unsigned long)&type_firefly 2158c2ecf20Sopenharmony_ci }, 2168c2ecf20Sopenharmony_ci {} /* Terminating entry */ 2178c2ecf20Sopenharmony_ci}; 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(usb, ati_remote_table); 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci/* Get hi and low bytes of a 16-bits int */ 2228c2ecf20Sopenharmony_ci#define HI(a) ((unsigned char)((a) >> 8)) 2238c2ecf20Sopenharmony_ci#define LO(a) ((unsigned char)((a) & 0xff)) 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci#define SEND_FLAG_IN_PROGRESS 1 2268c2ecf20Sopenharmony_ci#define SEND_FLAG_COMPLETE 2 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci/* Device initialization strings */ 2298c2ecf20Sopenharmony_cistatic char init1[] = { 0x01, 0x00, 0x20, 0x14 }; 2308c2ecf20Sopenharmony_cistatic char init2[] = { 0x01, 0x00, 0x20, 0x14, 0x20, 0x20, 0x20 }; 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_cistruct ati_remote { 2338c2ecf20Sopenharmony_ci struct input_dev *idev; 2348c2ecf20Sopenharmony_ci struct rc_dev *rdev; 2358c2ecf20Sopenharmony_ci struct usb_device *udev; 2368c2ecf20Sopenharmony_ci struct usb_interface *interface; 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci struct urb *irq_urb; 2398c2ecf20Sopenharmony_ci struct urb *out_urb; 2408c2ecf20Sopenharmony_ci struct usb_endpoint_descriptor *endpoint_in; 2418c2ecf20Sopenharmony_ci struct usb_endpoint_descriptor *endpoint_out; 2428c2ecf20Sopenharmony_ci unsigned char *inbuf; 2438c2ecf20Sopenharmony_ci unsigned char *outbuf; 2448c2ecf20Sopenharmony_ci dma_addr_t inbuf_dma; 2458c2ecf20Sopenharmony_ci dma_addr_t outbuf_dma; 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci unsigned char old_data; /* Detect duplicate events */ 2488c2ecf20Sopenharmony_ci unsigned long old_jiffies; 2498c2ecf20Sopenharmony_ci unsigned long acc_jiffies; /* handle acceleration */ 2508c2ecf20Sopenharmony_ci unsigned long first_jiffies; 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci unsigned int repeat_count; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci char rc_name[NAME_BUFSIZE]; 2558c2ecf20Sopenharmony_ci char rc_phys[NAME_BUFSIZE]; 2568c2ecf20Sopenharmony_ci char mouse_name[NAME_BUFSIZE]; 2578c2ecf20Sopenharmony_ci char mouse_phys[NAME_BUFSIZE]; 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci wait_queue_head_t wait; 2608c2ecf20Sopenharmony_ci int send_flags; 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci int users; /* 0-2, users are rc and input */ 2638c2ecf20Sopenharmony_ci struct mutex open_mutex; 2648c2ecf20Sopenharmony_ci}; 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci/* "Kinds" of messages sent from the hardware to the driver. */ 2678c2ecf20Sopenharmony_ci#define KIND_END 0 2688c2ecf20Sopenharmony_ci#define KIND_LITERAL 1 /* Simply pass to input system as EV_KEY */ 2698c2ecf20Sopenharmony_ci#define KIND_FILTERED 2 /* Add artificial key-up events, drop keyrepeats */ 2708c2ecf20Sopenharmony_ci#define KIND_ACCEL 3 /* Translate to EV_REL mouse-move events */ 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci/* Translation table from hardware messages to input events. */ 2738c2ecf20Sopenharmony_cistatic const struct { 2748c2ecf20Sopenharmony_ci unsigned char kind; 2758c2ecf20Sopenharmony_ci unsigned char data; /* Raw key code from remote */ 2768c2ecf20Sopenharmony_ci unsigned short code; /* Input layer translation */ 2778c2ecf20Sopenharmony_ci} ati_remote_tbl[] = { 2788c2ecf20Sopenharmony_ci /* Directional control pad axes. Code is xxyy */ 2798c2ecf20Sopenharmony_ci {KIND_ACCEL, 0x70, 0xff00}, /* left */ 2808c2ecf20Sopenharmony_ci {KIND_ACCEL, 0x71, 0x0100}, /* right */ 2818c2ecf20Sopenharmony_ci {KIND_ACCEL, 0x72, 0x00ff}, /* up */ 2828c2ecf20Sopenharmony_ci {KIND_ACCEL, 0x73, 0x0001}, /* down */ 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci /* Directional control pad diagonals */ 2858c2ecf20Sopenharmony_ci {KIND_ACCEL, 0x74, 0xffff}, /* left up */ 2868c2ecf20Sopenharmony_ci {KIND_ACCEL, 0x75, 0x01ff}, /* right up */ 2878c2ecf20Sopenharmony_ci {KIND_ACCEL, 0x77, 0xff01}, /* left down */ 2888c2ecf20Sopenharmony_ci {KIND_ACCEL, 0x76, 0x0101}, /* right down */ 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci /* "Mouse button" buttons. The code below uses the fact that the 2918c2ecf20Sopenharmony_ci * lsbit of the raw code is a down/up indicator. */ 2928c2ecf20Sopenharmony_ci {KIND_LITERAL, 0x78, BTN_LEFT}, /* left btn down */ 2938c2ecf20Sopenharmony_ci {KIND_LITERAL, 0x79, BTN_LEFT}, /* left btn up */ 2948c2ecf20Sopenharmony_ci {KIND_LITERAL, 0x7c, BTN_RIGHT},/* right btn down */ 2958c2ecf20Sopenharmony_ci {KIND_LITERAL, 0x7d, BTN_RIGHT},/* right btn up */ 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci /* Artificial "double-click" events are generated by the hardware. 2988c2ecf20Sopenharmony_ci * They are mapped to the "side" and "extra" mouse buttons here. */ 2998c2ecf20Sopenharmony_ci {KIND_FILTERED, 0x7a, BTN_SIDE}, /* left dblclick */ 3008c2ecf20Sopenharmony_ci {KIND_FILTERED, 0x7e, BTN_EXTRA},/* right dblclick */ 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci /* Non-mouse events are handled by rc-core */ 3038c2ecf20Sopenharmony_ci {KIND_END, 0x00, 0} 3048c2ecf20Sopenharmony_ci}; 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci/* 3078c2ecf20Sopenharmony_ci * ati_remote_dump_input 3088c2ecf20Sopenharmony_ci */ 3098c2ecf20Sopenharmony_cistatic void ati_remote_dump(struct device *dev, unsigned char *data, 3108c2ecf20Sopenharmony_ci unsigned int len) 3118c2ecf20Sopenharmony_ci{ 3128c2ecf20Sopenharmony_ci if (len == 1) { 3138c2ecf20Sopenharmony_ci if (data[0] != (unsigned char)0xff && data[0] != 0x00) 3148c2ecf20Sopenharmony_ci dev_warn(dev, "Weird byte 0x%02x\n", data[0]); 3158c2ecf20Sopenharmony_ci } else if (len == 4) 3168c2ecf20Sopenharmony_ci dev_warn(dev, "Weird key %*ph\n", 4, data); 3178c2ecf20Sopenharmony_ci else 3188c2ecf20Sopenharmony_ci dev_warn(dev, "Weird data, len=%d %*ph ...\n", len, 6, data); 3198c2ecf20Sopenharmony_ci} 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci/* 3228c2ecf20Sopenharmony_ci * ati_remote_open 3238c2ecf20Sopenharmony_ci */ 3248c2ecf20Sopenharmony_cistatic int ati_remote_open(struct ati_remote *ati_remote) 3258c2ecf20Sopenharmony_ci{ 3268c2ecf20Sopenharmony_ci int err = 0; 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci mutex_lock(&ati_remote->open_mutex); 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci if (ati_remote->users++ != 0) 3318c2ecf20Sopenharmony_ci goto out; /* one was already active */ 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci /* On first open, submit the read urb which was set up previously. */ 3348c2ecf20Sopenharmony_ci ati_remote->irq_urb->dev = ati_remote->udev; 3358c2ecf20Sopenharmony_ci if (usb_submit_urb(ati_remote->irq_urb, GFP_KERNEL)) { 3368c2ecf20Sopenharmony_ci dev_err(&ati_remote->interface->dev, 3378c2ecf20Sopenharmony_ci "%s: usb_submit_urb failed!\n", __func__); 3388c2ecf20Sopenharmony_ci err = -EIO; 3398c2ecf20Sopenharmony_ci } 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ciout: mutex_unlock(&ati_remote->open_mutex); 3428c2ecf20Sopenharmony_ci return err; 3438c2ecf20Sopenharmony_ci} 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci/* 3468c2ecf20Sopenharmony_ci * ati_remote_close 3478c2ecf20Sopenharmony_ci */ 3488c2ecf20Sopenharmony_cistatic void ati_remote_close(struct ati_remote *ati_remote) 3498c2ecf20Sopenharmony_ci{ 3508c2ecf20Sopenharmony_ci mutex_lock(&ati_remote->open_mutex); 3518c2ecf20Sopenharmony_ci if (--ati_remote->users == 0) 3528c2ecf20Sopenharmony_ci usb_kill_urb(ati_remote->irq_urb); 3538c2ecf20Sopenharmony_ci mutex_unlock(&ati_remote->open_mutex); 3548c2ecf20Sopenharmony_ci} 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_cistatic int ati_remote_input_open(struct input_dev *inputdev) 3578c2ecf20Sopenharmony_ci{ 3588c2ecf20Sopenharmony_ci struct ati_remote *ati_remote = input_get_drvdata(inputdev); 3598c2ecf20Sopenharmony_ci return ati_remote_open(ati_remote); 3608c2ecf20Sopenharmony_ci} 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_cistatic void ati_remote_input_close(struct input_dev *inputdev) 3638c2ecf20Sopenharmony_ci{ 3648c2ecf20Sopenharmony_ci struct ati_remote *ati_remote = input_get_drvdata(inputdev); 3658c2ecf20Sopenharmony_ci ati_remote_close(ati_remote); 3668c2ecf20Sopenharmony_ci} 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_cistatic int ati_remote_rc_open(struct rc_dev *rdev) 3698c2ecf20Sopenharmony_ci{ 3708c2ecf20Sopenharmony_ci struct ati_remote *ati_remote = rdev->priv; 3718c2ecf20Sopenharmony_ci return ati_remote_open(ati_remote); 3728c2ecf20Sopenharmony_ci} 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_cistatic void ati_remote_rc_close(struct rc_dev *rdev) 3758c2ecf20Sopenharmony_ci{ 3768c2ecf20Sopenharmony_ci struct ati_remote *ati_remote = rdev->priv; 3778c2ecf20Sopenharmony_ci ati_remote_close(ati_remote); 3788c2ecf20Sopenharmony_ci} 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci/* 3818c2ecf20Sopenharmony_ci * ati_remote_irq_out 3828c2ecf20Sopenharmony_ci */ 3838c2ecf20Sopenharmony_cistatic void ati_remote_irq_out(struct urb *urb) 3848c2ecf20Sopenharmony_ci{ 3858c2ecf20Sopenharmony_ci struct ati_remote *ati_remote = urb->context; 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci if (urb->status) { 3888c2ecf20Sopenharmony_ci dev_dbg(&ati_remote->interface->dev, "%s: status %d\n", 3898c2ecf20Sopenharmony_ci __func__, urb->status); 3908c2ecf20Sopenharmony_ci return; 3918c2ecf20Sopenharmony_ci } 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci ati_remote->send_flags |= SEND_FLAG_COMPLETE; 3948c2ecf20Sopenharmony_ci wmb(); 3958c2ecf20Sopenharmony_ci wake_up(&ati_remote->wait); 3968c2ecf20Sopenharmony_ci} 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci/* 3998c2ecf20Sopenharmony_ci * ati_remote_sendpacket 4008c2ecf20Sopenharmony_ci * 4018c2ecf20Sopenharmony_ci * Used to send device initialization strings 4028c2ecf20Sopenharmony_ci */ 4038c2ecf20Sopenharmony_cistatic int ati_remote_sendpacket(struct ati_remote *ati_remote, u16 cmd, 4048c2ecf20Sopenharmony_ci unsigned char *data) 4058c2ecf20Sopenharmony_ci{ 4068c2ecf20Sopenharmony_ci int retval = 0; 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci /* Set up out_urb */ 4098c2ecf20Sopenharmony_ci memcpy(ati_remote->out_urb->transfer_buffer + 1, data, LO(cmd)); 4108c2ecf20Sopenharmony_ci ((char *) ati_remote->out_urb->transfer_buffer)[0] = HI(cmd); 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci ati_remote->out_urb->transfer_buffer_length = LO(cmd) + 1; 4138c2ecf20Sopenharmony_ci ati_remote->out_urb->dev = ati_remote->udev; 4148c2ecf20Sopenharmony_ci ati_remote->send_flags = SEND_FLAG_IN_PROGRESS; 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci retval = usb_submit_urb(ati_remote->out_urb, GFP_ATOMIC); 4178c2ecf20Sopenharmony_ci if (retval) { 4188c2ecf20Sopenharmony_ci dev_dbg(&ati_remote->interface->dev, 4198c2ecf20Sopenharmony_ci "sendpacket: usb_submit_urb failed: %d\n", retval); 4208c2ecf20Sopenharmony_ci return retval; 4218c2ecf20Sopenharmony_ci } 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci wait_event_timeout(ati_remote->wait, 4248c2ecf20Sopenharmony_ci ((ati_remote->out_urb->status != -EINPROGRESS) || 4258c2ecf20Sopenharmony_ci (ati_remote->send_flags & SEND_FLAG_COMPLETE)), 4268c2ecf20Sopenharmony_ci HZ); 4278c2ecf20Sopenharmony_ci usb_kill_urb(ati_remote->out_urb); 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci return retval; 4308c2ecf20Sopenharmony_ci} 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_cistruct accel_times { 4338c2ecf20Sopenharmony_ci const char value; 4348c2ecf20Sopenharmony_ci unsigned int msecs; 4358c2ecf20Sopenharmony_ci}; 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_cistatic const struct accel_times accel[] = { 4388c2ecf20Sopenharmony_ci { 1, 125 }, 4398c2ecf20Sopenharmony_ci { 2, 250 }, 4408c2ecf20Sopenharmony_ci { 4, 500 }, 4418c2ecf20Sopenharmony_ci { 6, 1000 }, 4428c2ecf20Sopenharmony_ci { 9, 1500 }, 4438c2ecf20Sopenharmony_ci { 13, 2000 }, 4448c2ecf20Sopenharmony_ci { 20, 0 }, 4458c2ecf20Sopenharmony_ci}; 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci/* 4488c2ecf20Sopenharmony_ci * ati_remote_compute_accel 4498c2ecf20Sopenharmony_ci * 4508c2ecf20Sopenharmony_ci * Implements acceleration curve for directional control pad 4518c2ecf20Sopenharmony_ci * If elapsed time since last event is > 1/4 second, user "stopped", 4528c2ecf20Sopenharmony_ci * so reset acceleration. Otherwise, user is probably holding the control 4538c2ecf20Sopenharmony_ci * pad down, so we increase acceleration, ramping up over two seconds to 4548c2ecf20Sopenharmony_ci * a maximum speed. 4558c2ecf20Sopenharmony_ci */ 4568c2ecf20Sopenharmony_cistatic int ati_remote_compute_accel(struct ati_remote *ati_remote) 4578c2ecf20Sopenharmony_ci{ 4588c2ecf20Sopenharmony_ci unsigned long now = jiffies, reset_time; 4598c2ecf20Sopenharmony_ci int i; 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci reset_time = msecs_to_jiffies(250); 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci if (time_after(now, ati_remote->old_jiffies + reset_time)) { 4648c2ecf20Sopenharmony_ci ati_remote->acc_jiffies = now; 4658c2ecf20Sopenharmony_ci return 1; 4668c2ecf20Sopenharmony_ci } 4678c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(accel) - 1; i++) { 4688c2ecf20Sopenharmony_ci unsigned long timeout = msecs_to_jiffies(accel[i].msecs); 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci if (time_before(now, ati_remote->acc_jiffies + timeout)) 4718c2ecf20Sopenharmony_ci return accel[i].value; 4728c2ecf20Sopenharmony_ci } 4738c2ecf20Sopenharmony_ci return accel[i].value; 4748c2ecf20Sopenharmony_ci} 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci/* 4778c2ecf20Sopenharmony_ci * ati_remote_report_input 4788c2ecf20Sopenharmony_ci */ 4798c2ecf20Sopenharmony_cistatic void ati_remote_input_report(struct urb *urb) 4808c2ecf20Sopenharmony_ci{ 4818c2ecf20Sopenharmony_ci struct ati_remote *ati_remote = urb->context; 4828c2ecf20Sopenharmony_ci unsigned char *data= ati_remote->inbuf; 4838c2ecf20Sopenharmony_ci struct input_dev *dev = ati_remote->idev; 4848c2ecf20Sopenharmony_ci int index = -1; 4858c2ecf20Sopenharmony_ci int remote_num; 4868c2ecf20Sopenharmony_ci unsigned char scancode; 4878c2ecf20Sopenharmony_ci u32 wheel_keycode = KEY_RESERVED; 4888c2ecf20Sopenharmony_ci int i; 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci /* 4918c2ecf20Sopenharmony_ci * data[0] = 0x14 4928c2ecf20Sopenharmony_ci * data[1] = data[2] + data[3] + 0xd5 (a checksum byte) 4938c2ecf20Sopenharmony_ci * data[2] = the key code (with toggle bit in MSB with some models) 4948c2ecf20Sopenharmony_ci * data[3] = channel << 4 (the low 4 bits must be zero) 4958c2ecf20Sopenharmony_ci */ 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci /* Deal with strange looking inputs */ 4988c2ecf20Sopenharmony_ci if ( urb->actual_length != 4 || data[0] != 0x14 || 4998c2ecf20Sopenharmony_ci data[1] != (unsigned char)(data[2] + data[3] + 0xD5) || 5008c2ecf20Sopenharmony_ci (data[3] & 0x0f) != 0x00) { 5018c2ecf20Sopenharmony_ci ati_remote_dump(&urb->dev->dev, data, urb->actual_length); 5028c2ecf20Sopenharmony_ci return; 5038c2ecf20Sopenharmony_ci } 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci if (data[1] != ((data[2] + data[3] + 0xd5) & 0xff)) { 5068c2ecf20Sopenharmony_ci dbginfo(&ati_remote->interface->dev, 5078c2ecf20Sopenharmony_ci "wrong checksum in input: %*ph\n", 4, data); 5088c2ecf20Sopenharmony_ci return; 5098c2ecf20Sopenharmony_ci } 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci /* Mask unwanted remote channels. */ 5128c2ecf20Sopenharmony_ci /* note: remote_num is 0-based, channel 1 on remote == 0 here */ 5138c2ecf20Sopenharmony_ci remote_num = (data[3] >> 4) & 0x0f; 5148c2ecf20Sopenharmony_ci if (channel_mask & (1 << (remote_num + 1))) { 5158c2ecf20Sopenharmony_ci dbginfo(&ati_remote->interface->dev, 5168c2ecf20Sopenharmony_ci "Masked input from channel 0x%02x: data %02x, mask= 0x%02lx\n", 5178c2ecf20Sopenharmony_ci remote_num, data[2], channel_mask); 5188c2ecf20Sopenharmony_ci return; 5198c2ecf20Sopenharmony_ci } 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci /* 5228c2ecf20Sopenharmony_ci * MSB is a toggle code, though only used by some devices 5238c2ecf20Sopenharmony_ci * (e.g. SnapStream Firefly) 5248c2ecf20Sopenharmony_ci */ 5258c2ecf20Sopenharmony_ci scancode = data[2] & 0x7f; 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci dbginfo(&ati_remote->interface->dev, 5288c2ecf20Sopenharmony_ci "channel 0x%02x; key data %02x, scancode %02x\n", 5298c2ecf20Sopenharmony_ci remote_num, data[2], scancode); 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci if (scancode >= 0x70) { 5328c2ecf20Sopenharmony_ci /* 5338c2ecf20Sopenharmony_ci * This is either a mouse or scrollwheel event, depending on 5348c2ecf20Sopenharmony_ci * the remote/keymap. 5358c2ecf20Sopenharmony_ci * Get the keycode assigned to scancode 0x78/0x70. If it is 5368c2ecf20Sopenharmony_ci * set, assume this is a scrollwheel up/down event. 5378c2ecf20Sopenharmony_ci */ 5388c2ecf20Sopenharmony_ci wheel_keycode = rc_g_keycode_from_table(ati_remote->rdev, 5398c2ecf20Sopenharmony_ci scancode & 0x78); 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ci if (wheel_keycode == KEY_RESERVED) { 5428c2ecf20Sopenharmony_ci /* scrollwheel was not mapped, assume mouse */ 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci /* Look up event code index in the mouse translation 5458c2ecf20Sopenharmony_ci * table. 5468c2ecf20Sopenharmony_ci */ 5478c2ecf20Sopenharmony_ci for (i = 0; ati_remote_tbl[i].kind != KIND_END; i++) { 5488c2ecf20Sopenharmony_ci if (scancode == ati_remote_tbl[i].data) { 5498c2ecf20Sopenharmony_ci index = i; 5508c2ecf20Sopenharmony_ci break; 5518c2ecf20Sopenharmony_ci } 5528c2ecf20Sopenharmony_ci } 5538c2ecf20Sopenharmony_ci } 5548c2ecf20Sopenharmony_ci } 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci if (index >= 0 && ati_remote_tbl[index].kind == KIND_LITERAL) { 5578c2ecf20Sopenharmony_ci /* 5588c2ecf20Sopenharmony_ci * The lsbit of the raw key code is a down/up flag. 5598c2ecf20Sopenharmony_ci * Invert it to match the input layer's conventions. 5608c2ecf20Sopenharmony_ci */ 5618c2ecf20Sopenharmony_ci input_event(dev, EV_KEY, ati_remote_tbl[index].code, 5628c2ecf20Sopenharmony_ci !(data[2] & 1)); 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci ati_remote->old_jiffies = jiffies; 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_ci } else if (index < 0 || ati_remote_tbl[index].kind == KIND_FILTERED) { 5678c2ecf20Sopenharmony_ci unsigned long now = jiffies; 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci /* Filter duplicate events which happen "too close" together. */ 5708c2ecf20Sopenharmony_ci if (ati_remote->old_data == data[2] && 5718c2ecf20Sopenharmony_ci time_before(now, ati_remote->old_jiffies + 5728c2ecf20Sopenharmony_ci msecs_to_jiffies(repeat_filter))) { 5738c2ecf20Sopenharmony_ci ati_remote->repeat_count++; 5748c2ecf20Sopenharmony_ci } else { 5758c2ecf20Sopenharmony_ci ati_remote->repeat_count = 0; 5768c2ecf20Sopenharmony_ci ati_remote->first_jiffies = now; 5778c2ecf20Sopenharmony_ci } 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci ati_remote->old_jiffies = now; 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ci /* Ensure we skip at least the 4 first duplicate events 5828c2ecf20Sopenharmony_ci * (generated by a single keypress), and continue skipping 5838c2ecf20Sopenharmony_ci * until repeat_delay msecs have passed. 5848c2ecf20Sopenharmony_ci */ 5858c2ecf20Sopenharmony_ci if (ati_remote->repeat_count > 0 && 5868c2ecf20Sopenharmony_ci (ati_remote->repeat_count < 5 || 5878c2ecf20Sopenharmony_ci time_before(now, ati_remote->first_jiffies + 5888c2ecf20Sopenharmony_ci msecs_to_jiffies(repeat_delay)))) 5898c2ecf20Sopenharmony_ci return; 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_ci if (index >= 0) { 5928c2ecf20Sopenharmony_ci input_event(dev, EV_KEY, ati_remote_tbl[index].code, 1); 5938c2ecf20Sopenharmony_ci input_event(dev, EV_KEY, ati_remote_tbl[index].code, 0); 5948c2ecf20Sopenharmony_ci } else { 5958c2ecf20Sopenharmony_ci /* Not a mouse event, hand it to rc-core. */ 5968c2ecf20Sopenharmony_ci int count = 1; 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_ci if (wheel_keycode != KEY_RESERVED) { 5998c2ecf20Sopenharmony_ci /* 6008c2ecf20Sopenharmony_ci * This is a scrollwheel event, send the 6018c2ecf20Sopenharmony_ci * scroll up (0x78) / down (0x70) scancode 6028c2ecf20Sopenharmony_ci * repeatedly as many times as indicated by 6038c2ecf20Sopenharmony_ci * rest of the scancode. 6048c2ecf20Sopenharmony_ci */ 6058c2ecf20Sopenharmony_ci count = (scancode & 0x07) + 1; 6068c2ecf20Sopenharmony_ci scancode &= 0x78; 6078c2ecf20Sopenharmony_ci } 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ci while (count--) { 6108c2ecf20Sopenharmony_ci /* 6118c2ecf20Sopenharmony_ci * We don't use the rc-core repeat handling yet as 6128c2ecf20Sopenharmony_ci * it would cause ghost repeats which would be a 6138c2ecf20Sopenharmony_ci * regression for this driver. 6148c2ecf20Sopenharmony_ci */ 6158c2ecf20Sopenharmony_ci rc_keydown_notimeout(ati_remote->rdev, 6168c2ecf20Sopenharmony_ci RC_PROTO_OTHER, 6178c2ecf20Sopenharmony_ci scancode, data[2]); 6188c2ecf20Sopenharmony_ci rc_keyup(ati_remote->rdev); 6198c2ecf20Sopenharmony_ci } 6208c2ecf20Sopenharmony_ci goto nosync; 6218c2ecf20Sopenharmony_ci } 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_ci } else if (ati_remote_tbl[index].kind == KIND_ACCEL) { 6248c2ecf20Sopenharmony_ci signed char dx = ati_remote_tbl[index].code >> 8; 6258c2ecf20Sopenharmony_ci signed char dy = ati_remote_tbl[index].code & 255; 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_ci /* 6288c2ecf20Sopenharmony_ci * Other event kinds are from the directional control pad, and 6298c2ecf20Sopenharmony_ci * have an acceleration factor applied to them. Without this 6308c2ecf20Sopenharmony_ci * acceleration, the control pad is mostly unusable. 6318c2ecf20Sopenharmony_ci */ 6328c2ecf20Sopenharmony_ci int acc = ati_remote_compute_accel(ati_remote); 6338c2ecf20Sopenharmony_ci if (dx) 6348c2ecf20Sopenharmony_ci input_report_rel(dev, REL_X, dx * acc); 6358c2ecf20Sopenharmony_ci if (dy) 6368c2ecf20Sopenharmony_ci input_report_rel(dev, REL_Y, dy * acc); 6378c2ecf20Sopenharmony_ci ati_remote->old_jiffies = jiffies; 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_ci } else { 6408c2ecf20Sopenharmony_ci dev_dbg(&ati_remote->interface->dev, "ati_remote kind=%d\n", 6418c2ecf20Sopenharmony_ci ati_remote_tbl[index].kind); 6428c2ecf20Sopenharmony_ci return; 6438c2ecf20Sopenharmony_ci } 6448c2ecf20Sopenharmony_ci input_sync(dev); 6458c2ecf20Sopenharmony_cinosync: 6468c2ecf20Sopenharmony_ci ati_remote->old_data = data[2]; 6478c2ecf20Sopenharmony_ci} 6488c2ecf20Sopenharmony_ci 6498c2ecf20Sopenharmony_ci/* 6508c2ecf20Sopenharmony_ci * ati_remote_irq_in 6518c2ecf20Sopenharmony_ci */ 6528c2ecf20Sopenharmony_cistatic void ati_remote_irq_in(struct urb *urb) 6538c2ecf20Sopenharmony_ci{ 6548c2ecf20Sopenharmony_ci struct ati_remote *ati_remote = urb->context; 6558c2ecf20Sopenharmony_ci int retval; 6568c2ecf20Sopenharmony_ci 6578c2ecf20Sopenharmony_ci switch (urb->status) { 6588c2ecf20Sopenharmony_ci case 0: /* success */ 6598c2ecf20Sopenharmony_ci ati_remote_input_report(urb); 6608c2ecf20Sopenharmony_ci break; 6618c2ecf20Sopenharmony_ci case -ECONNRESET: /* unlink */ 6628c2ecf20Sopenharmony_ci case -ENOENT: 6638c2ecf20Sopenharmony_ci case -ESHUTDOWN: 6648c2ecf20Sopenharmony_ci dev_dbg(&ati_remote->interface->dev, 6658c2ecf20Sopenharmony_ci "%s: urb error status, unlink?\n", 6668c2ecf20Sopenharmony_ci __func__); 6678c2ecf20Sopenharmony_ci return; 6688c2ecf20Sopenharmony_ci default: /* error */ 6698c2ecf20Sopenharmony_ci dev_dbg(&ati_remote->interface->dev, 6708c2ecf20Sopenharmony_ci "%s: Nonzero urb status %d\n", 6718c2ecf20Sopenharmony_ci __func__, urb->status); 6728c2ecf20Sopenharmony_ci } 6738c2ecf20Sopenharmony_ci 6748c2ecf20Sopenharmony_ci retval = usb_submit_urb(urb, GFP_ATOMIC); 6758c2ecf20Sopenharmony_ci if (retval) 6768c2ecf20Sopenharmony_ci dev_err(&ati_remote->interface->dev, 6778c2ecf20Sopenharmony_ci "%s: usb_submit_urb()=%d\n", 6788c2ecf20Sopenharmony_ci __func__, retval); 6798c2ecf20Sopenharmony_ci} 6808c2ecf20Sopenharmony_ci 6818c2ecf20Sopenharmony_ci/* 6828c2ecf20Sopenharmony_ci * ati_remote_alloc_buffers 6838c2ecf20Sopenharmony_ci */ 6848c2ecf20Sopenharmony_cistatic int ati_remote_alloc_buffers(struct usb_device *udev, 6858c2ecf20Sopenharmony_ci struct ati_remote *ati_remote) 6868c2ecf20Sopenharmony_ci{ 6878c2ecf20Sopenharmony_ci ati_remote->inbuf = usb_alloc_coherent(udev, DATA_BUFSIZE, GFP_ATOMIC, 6888c2ecf20Sopenharmony_ci &ati_remote->inbuf_dma); 6898c2ecf20Sopenharmony_ci if (!ati_remote->inbuf) 6908c2ecf20Sopenharmony_ci return -1; 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_ci ati_remote->outbuf = usb_alloc_coherent(udev, DATA_BUFSIZE, GFP_ATOMIC, 6938c2ecf20Sopenharmony_ci &ati_remote->outbuf_dma); 6948c2ecf20Sopenharmony_ci if (!ati_remote->outbuf) 6958c2ecf20Sopenharmony_ci return -1; 6968c2ecf20Sopenharmony_ci 6978c2ecf20Sopenharmony_ci ati_remote->irq_urb = usb_alloc_urb(0, GFP_KERNEL); 6988c2ecf20Sopenharmony_ci if (!ati_remote->irq_urb) 6998c2ecf20Sopenharmony_ci return -1; 7008c2ecf20Sopenharmony_ci 7018c2ecf20Sopenharmony_ci ati_remote->out_urb = usb_alloc_urb(0, GFP_KERNEL); 7028c2ecf20Sopenharmony_ci if (!ati_remote->out_urb) 7038c2ecf20Sopenharmony_ci return -1; 7048c2ecf20Sopenharmony_ci 7058c2ecf20Sopenharmony_ci return 0; 7068c2ecf20Sopenharmony_ci} 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_ci/* 7098c2ecf20Sopenharmony_ci * ati_remote_free_buffers 7108c2ecf20Sopenharmony_ci */ 7118c2ecf20Sopenharmony_cistatic void ati_remote_free_buffers(struct ati_remote *ati_remote) 7128c2ecf20Sopenharmony_ci{ 7138c2ecf20Sopenharmony_ci usb_free_urb(ati_remote->irq_urb); 7148c2ecf20Sopenharmony_ci usb_free_urb(ati_remote->out_urb); 7158c2ecf20Sopenharmony_ci 7168c2ecf20Sopenharmony_ci usb_free_coherent(ati_remote->udev, DATA_BUFSIZE, 7178c2ecf20Sopenharmony_ci ati_remote->inbuf, ati_remote->inbuf_dma); 7188c2ecf20Sopenharmony_ci 7198c2ecf20Sopenharmony_ci usb_free_coherent(ati_remote->udev, DATA_BUFSIZE, 7208c2ecf20Sopenharmony_ci ati_remote->outbuf, ati_remote->outbuf_dma); 7218c2ecf20Sopenharmony_ci} 7228c2ecf20Sopenharmony_ci 7238c2ecf20Sopenharmony_cistatic void ati_remote_input_init(struct ati_remote *ati_remote) 7248c2ecf20Sopenharmony_ci{ 7258c2ecf20Sopenharmony_ci struct input_dev *idev = ati_remote->idev; 7268c2ecf20Sopenharmony_ci int i; 7278c2ecf20Sopenharmony_ci 7288c2ecf20Sopenharmony_ci idev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL); 7298c2ecf20Sopenharmony_ci idev->keybit[BIT_WORD(BTN_MOUSE)] = BIT_MASK(BTN_LEFT) | 7308c2ecf20Sopenharmony_ci BIT_MASK(BTN_RIGHT) | BIT_MASK(BTN_SIDE) | BIT_MASK(BTN_EXTRA); 7318c2ecf20Sopenharmony_ci idev->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(REL_Y); 7328c2ecf20Sopenharmony_ci for (i = 0; ati_remote_tbl[i].kind != KIND_END; i++) 7338c2ecf20Sopenharmony_ci if (ati_remote_tbl[i].kind == KIND_LITERAL || 7348c2ecf20Sopenharmony_ci ati_remote_tbl[i].kind == KIND_FILTERED) 7358c2ecf20Sopenharmony_ci __set_bit(ati_remote_tbl[i].code, idev->keybit); 7368c2ecf20Sopenharmony_ci 7378c2ecf20Sopenharmony_ci input_set_drvdata(idev, ati_remote); 7388c2ecf20Sopenharmony_ci 7398c2ecf20Sopenharmony_ci idev->open = ati_remote_input_open; 7408c2ecf20Sopenharmony_ci idev->close = ati_remote_input_close; 7418c2ecf20Sopenharmony_ci 7428c2ecf20Sopenharmony_ci idev->name = ati_remote->mouse_name; 7438c2ecf20Sopenharmony_ci idev->phys = ati_remote->mouse_phys; 7448c2ecf20Sopenharmony_ci 7458c2ecf20Sopenharmony_ci usb_to_input_id(ati_remote->udev, &idev->id); 7468c2ecf20Sopenharmony_ci idev->dev.parent = &ati_remote->interface->dev; 7478c2ecf20Sopenharmony_ci} 7488c2ecf20Sopenharmony_ci 7498c2ecf20Sopenharmony_cistatic void ati_remote_rc_init(struct ati_remote *ati_remote) 7508c2ecf20Sopenharmony_ci{ 7518c2ecf20Sopenharmony_ci struct rc_dev *rdev = ati_remote->rdev; 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_ci rdev->priv = ati_remote; 7548c2ecf20Sopenharmony_ci rdev->allowed_protocols = RC_PROTO_BIT_OTHER; 7558c2ecf20Sopenharmony_ci rdev->driver_name = "ati_remote"; 7568c2ecf20Sopenharmony_ci 7578c2ecf20Sopenharmony_ci rdev->open = ati_remote_rc_open; 7588c2ecf20Sopenharmony_ci rdev->close = ati_remote_rc_close; 7598c2ecf20Sopenharmony_ci 7608c2ecf20Sopenharmony_ci rdev->device_name = ati_remote->rc_name; 7618c2ecf20Sopenharmony_ci rdev->input_phys = ati_remote->rc_phys; 7628c2ecf20Sopenharmony_ci 7638c2ecf20Sopenharmony_ci usb_to_input_id(ati_remote->udev, &rdev->input_id); 7648c2ecf20Sopenharmony_ci rdev->dev.parent = &ati_remote->interface->dev; 7658c2ecf20Sopenharmony_ci} 7668c2ecf20Sopenharmony_ci 7678c2ecf20Sopenharmony_cistatic int ati_remote_initialize(struct ati_remote *ati_remote) 7688c2ecf20Sopenharmony_ci{ 7698c2ecf20Sopenharmony_ci struct usb_device *udev = ati_remote->udev; 7708c2ecf20Sopenharmony_ci int pipe, maxp; 7718c2ecf20Sopenharmony_ci 7728c2ecf20Sopenharmony_ci init_waitqueue_head(&ati_remote->wait); 7738c2ecf20Sopenharmony_ci 7748c2ecf20Sopenharmony_ci /* Set up irq_urb */ 7758c2ecf20Sopenharmony_ci pipe = usb_rcvintpipe(udev, ati_remote->endpoint_in->bEndpointAddress); 7768c2ecf20Sopenharmony_ci maxp = usb_maxpacket(udev, pipe, usb_pipeout(pipe)); 7778c2ecf20Sopenharmony_ci maxp = (maxp > DATA_BUFSIZE) ? DATA_BUFSIZE : maxp; 7788c2ecf20Sopenharmony_ci 7798c2ecf20Sopenharmony_ci usb_fill_int_urb(ati_remote->irq_urb, udev, pipe, ati_remote->inbuf, 7808c2ecf20Sopenharmony_ci maxp, ati_remote_irq_in, ati_remote, 7818c2ecf20Sopenharmony_ci ati_remote->endpoint_in->bInterval); 7828c2ecf20Sopenharmony_ci ati_remote->irq_urb->transfer_dma = ati_remote->inbuf_dma; 7838c2ecf20Sopenharmony_ci ati_remote->irq_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; 7848c2ecf20Sopenharmony_ci 7858c2ecf20Sopenharmony_ci /* Set up out_urb */ 7868c2ecf20Sopenharmony_ci pipe = usb_sndintpipe(udev, ati_remote->endpoint_out->bEndpointAddress); 7878c2ecf20Sopenharmony_ci maxp = usb_maxpacket(udev, pipe, usb_pipeout(pipe)); 7888c2ecf20Sopenharmony_ci maxp = (maxp > DATA_BUFSIZE) ? DATA_BUFSIZE : maxp; 7898c2ecf20Sopenharmony_ci 7908c2ecf20Sopenharmony_ci usb_fill_int_urb(ati_remote->out_urb, udev, pipe, ati_remote->outbuf, 7918c2ecf20Sopenharmony_ci maxp, ati_remote_irq_out, ati_remote, 7928c2ecf20Sopenharmony_ci ati_remote->endpoint_out->bInterval); 7938c2ecf20Sopenharmony_ci ati_remote->out_urb->transfer_dma = ati_remote->outbuf_dma; 7948c2ecf20Sopenharmony_ci ati_remote->out_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; 7958c2ecf20Sopenharmony_ci 7968c2ecf20Sopenharmony_ci /* send initialization strings */ 7978c2ecf20Sopenharmony_ci if ((ati_remote_sendpacket(ati_remote, 0x8004, init1)) || 7988c2ecf20Sopenharmony_ci (ati_remote_sendpacket(ati_remote, 0x8007, init2))) { 7998c2ecf20Sopenharmony_ci dev_err(&ati_remote->interface->dev, 8008c2ecf20Sopenharmony_ci "Initializing ati_remote hardware failed.\n"); 8018c2ecf20Sopenharmony_ci return -EIO; 8028c2ecf20Sopenharmony_ci } 8038c2ecf20Sopenharmony_ci 8048c2ecf20Sopenharmony_ci return 0; 8058c2ecf20Sopenharmony_ci} 8068c2ecf20Sopenharmony_ci 8078c2ecf20Sopenharmony_ci/* 8088c2ecf20Sopenharmony_ci * ati_remote_probe 8098c2ecf20Sopenharmony_ci */ 8108c2ecf20Sopenharmony_cistatic int ati_remote_probe(struct usb_interface *interface, 8118c2ecf20Sopenharmony_ci const struct usb_device_id *id) 8128c2ecf20Sopenharmony_ci{ 8138c2ecf20Sopenharmony_ci struct usb_device *udev = interface_to_usbdev(interface); 8148c2ecf20Sopenharmony_ci struct usb_host_interface *iface_host = interface->cur_altsetting; 8158c2ecf20Sopenharmony_ci struct usb_endpoint_descriptor *endpoint_in, *endpoint_out; 8168c2ecf20Sopenharmony_ci struct ati_receiver_type *type = (struct ati_receiver_type *)id->driver_info; 8178c2ecf20Sopenharmony_ci struct ati_remote *ati_remote; 8188c2ecf20Sopenharmony_ci struct input_dev *input_dev; 8198c2ecf20Sopenharmony_ci struct rc_dev *rc_dev; 8208c2ecf20Sopenharmony_ci int err = -ENOMEM; 8218c2ecf20Sopenharmony_ci 8228c2ecf20Sopenharmony_ci if (iface_host->desc.bNumEndpoints != 2) { 8238c2ecf20Sopenharmony_ci err("%s: Unexpected desc.bNumEndpoints\n", __func__); 8248c2ecf20Sopenharmony_ci return -ENODEV; 8258c2ecf20Sopenharmony_ci } 8268c2ecf20Sopenharmony_ci 8278c2ecf20Sopenharmony_ci endpoint_in = &iface_host->endpoint[0].desc; 8288c2ecf20Sopenharmony_ci endpoint_out = &iface_host->endpoint[1].desc; 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_ci if (!usb_endpoint_is_int_in(endpoint_in)) { 8318c2ecf20Sopenharmony_ci err("%s: Unexpected endpoint_in\n", __func__); 8328c2ecf20Sopenharmony_ci return -ENODEV; 8338c2ecf20Sopenharmony_ci } 8348c2ecf20Sopenharmony_ci if (le16_to_cpu(endpoint_in->wMaxPacketSize) == 0) { 8358c2ecf20Sopenharmony_ci err("%s: endpoint_in message size==0? \n", __func__); 8368c2ecf20Sopenharmony_ci return -ENODEV; 8378c2ecf20Sopenharmony_ci } 8388c2ecf20Sopenharmony_ci if (!usb_endpoint_is_int_out(endpoint_out)) { 8398c2ecf20Sopenharmony_ci err("%s: Unexpected endpoint_out\n", __func__); 8408c2ecf20Sopenharmony_ci return -ENODEV; 8418c2ecf20Sopenharmony_ci } 8428c2ecf20Sopenharmony_ci 8438c2ecf20Sopenharmony_ci ati_remote = kzalloc(sizeof (struct ati_remote), GFP_KERNEL); 8448c2ecf20Sopenharmony_ci rc_dev = rc_allocate_device(RC_DRIVER_SCANCODE); 8458c2ecf20Sopenharmony_ci if (!ati_remote || !rc_dev) 8468c2ecf20Sopenharmony_ci goto exit_free_dev_rdev; 8478c2ecf20Sopenharmony_ci 8488c2ecf20Sopenharmony_ci /* Allocate URB buffers, URBs */ 8498c2ecf20Sopenharmony_ci if (ati_remote_alloc_buffers(udev, ati_remote)) 8508c2ecf20Sopenharmony_ci goto exit_free_buffers; 8518c2ecf20Sopenharmony_ci 8528c2ecf20Sopenharmony_ci ati_remote->endpoint_in = endpoint_in; 8538c2ecf20Sopenharmony_ci ati_remote->endpoint_out = endpoint_out; 8548c2ecf20Sopenharmony_ci ati_remote->udev = udev; 8558c2ecf20Sopenharmony_ci ati_remote->rdev = rc_dev; 8568c2ecf20Sopenharmony_ci ati_remote->interface = interface; 8578c2ecf20Sopenharmony_ci 8588c2ecf20Sopenharmony_ci usb_make_path(udev, ati_remote->rc_phys, sizeof(ati_remote->rc_phys)); 8598c2ecf20Sopenharmony_ci strscpy(ati_remote->mouse_phys, ati_remote->rc_phys, 8608c2ecf20Sopenharmony_ci sizeof(ati_remote->mouse_phys)); 8618c2ecf20Sopenharmony_ci 8628c2ecf20Sopenharmony_ci strlcat(ati_remote->rc_phys, "/input0", sizeof(ati_remote->rc_phys)); 8638c2ecf20Sopenharmony_ci strlcat(ati_remote->mouse_phys, "/input1", sizeof(ati_remote->mouse_phys)); 8648c2ecf20Sopenharmony_ci 8658c2ecf20Sopenharmony_ci snprintf(ati_remote->rc_name, sizeof(ati_remote->rc_name), "%s%s%s", 8668c2ecf20Sopenharmony_ci udev->manufacturer ?: "", 8678c2ecf20Sopenharmony_ci udev->manufacturer && udev->product ? " " : "", 8688c2ecf20Sopenharmony_ci udev->product ?: ""); 8698c2ecf20Sopenharmony_ci 8708c2ecf20Sopenharmony_ci if (!strlen(ati_remote->rc_name)) 8718c2ecf20Sopenharmony_ci snprintf(ati_remote->rc_name, sizeof(ati_remote->rc_name), 8728c2ecf20Sopenharmony_ci DRIVER_DESC "(%04x,%04x)", 8738c2ecf20Sopenharmony_ci le16_to_cpu(ati_remote->udev->descriptor.idVendor), 8748c2ecf20Sopenharmony_ci le16_to_cpu(ati_remote->udev->descriptor.idProduct)); 8758c2ecf20Sopenharmony_ci 8768c2ecf20Sopenharmony_ci snprintf(ati_remote->mouse_name, sizeof(ati_remote->mouse_name), 8778c2ecf20Sopenharmony_ci "%s mouse", ati_remote->rc_name); 8788c2ecf20Sopenharmony_ci 8798c2ecf20Sopenharmony_ci rc_dev->map_name = RC_MAP_ATI_X10; /* default map */ 8808c2ecf20Sopenharmony_ci 8818c2ecf20Sopenharmony_ci /* set default keymap according to receiver model */ 8828c2ecf20Sopenharmony_ci if (type) { 8838c2ecf20Sopenharmony_ci if (type->default_keymap) 8848c2ecf20Sopenharmony_ci rc_dev->map_name = type->default_keymap; 8858c2ecf20Sopenharmony_ci else if (type->get_default_keymap) 8868c2ecf20Sopenharmony_ci rc_dev->map_name = type->get_default_keymap(interface); 8878c2ecf20Sopenharmony_ci } 8888c2ecf20Sopenharmony_ci 8898c2ecf20Sopenharmony_ci ati_remote_rc_init(ati_remote); 8908c2ecf20Sopenharmony_ci mutex_init(&ati_remote->open_mutex); 8918c2ecf20Sopenharmony_ci 8928c2ecf20Sopenharmony_ci /* Device Hardware Initialization - fills in ati_remote->idev from udev. */ 8938c2ecf20Sopenharmony_ci err = ati_remote_initialize(ati_remote); 8948c2ecf20Sopenharmony_ci if (err) 8958c2ecf20Sopenharmony_ci goto exit_kill_urbs; 8968c2ecf20Sopenharmony_ci 8978c2ecf20Sopenharmony_ci /* Set up and register rc device */ 8988c2ecf20Sopenharmony_ci err = rc_register_device(ati_remote->rdev); 8998c2ecf20Sopenharmony_ci if (err) 9008c2ecf20Sopenharmony_ci goto exit_kill_urbs; 9018c2ecf20Sopenharmony_ci 9028c2ecf20Sopenharmony_ci /* Set up and register mouse input device */ 9038c2ecf20Sopenharmony_ci if (mouse) { 9048c2ecf20Sopenharmony_ci input_dev = input_allocate_device(); 9058c2ecf20Sopenharmony_ci if (!input_dev) { 9068c2ecf20Sopenharmony_ci err = -ENOMEM; 9078c2ecf20Sopenharmony_ci goto exit_unregister_device; 9088c2ecf20Sopenharmony_ci } 9098c2ecf20Sopenharmony_ci 9108c2ecf20Sopenharmony_ci ati_remote->idev = input_dev; 9118c2ecf20Sopenharmony_ci ati_remote_input_init(ati_remote); 9128c2ecf20Sopenharmony_ci err = input_register_device(input_dev); 9138c2ecf20Sopenharmony_ci 9148c2ecf20Sopenharmony_ci if (err) 9158c2ecf20Sopenharmony_ci goto exit_free_input_device; 9168c2ecf20Sopenharmony_ci } 9178c2ecf20Sopenharmony_ci 9188c2ecf20Sopenharmony_ci usb_set_intfdata(interface, ati_remote); 9198c2ecf20Sopenharmony_ci return 0; 9208c2ecf20Sopenharmony_ci 9218c2ecf20Sopenharmony_ci exit_free_input_device: 9228c2ecf20Sopenharmony_ci input_free_device(input_dev); 9238c2ecf20Sopenharmony_ci exit_unregister_device: 9248c2ecf20Sopenharmony_ci rc_unregister_device(rc_dev); 9258c2ecf20Sopenharmony_ci rc_dev = NULL; 9268c2ecf20Sopenharmony_ci exit_kill_urbs: 9278c2ecf20Sopenharmony_ci usb_kill_urb(ati_remote->irq_urb); 9288c2ecf20Sopenharmony_ci usb_kill_urb(ati_remote->out_urb); 9298c2ecf20Sopenharmony_ci exit_free_buffers: 9308c2ecf20Sopenharmony_ci ati_remote_free_buffers(ati_remote); 9318c2ecf20Sopenharmony_ci exit_free_dev_rdev: 9328c2ecf20Sopenharmony_ci rc_free_device(rc_dev); 9338c2ecf20Sopenharmony_ci kfree(ati_remote); 9348c2ecf20Sopenharmony_ci return err; 9358c2ecf20Sopenharmony_ci} 9368c2ecf20Sopenharmony_ci 9378c2ecf20Sopenharmony_ci/* 9388c2ecf20Sopenharmony_ci * ati_remote_disconnect 9398c2ecf20Sopenharmony_ci */ 9408c2ecf20Sopenharmony_cistatic void ati_remote_disconnect(struct usb_interface *interface) 9418c2ecf20Sopenharmony_ci{ 9428c2ecf20Sopenharmony_ci struct ati_remote *ati_remote; 9438c2ecf20Sopenharmony_ci 9448c2ecf20Sopenharmony_ci ati_remote = usb_get_intfdata(interface); 9458c2ecf20Sopenharmony_ci usb_set_intfdata(interface, NULL); 9468c2ecf20Sopenharmony_ci if (!ati_remote) { 9478c2ecf20Sopenharmony_ci dev_warn(&interface->dev, "%s - null device?\n", __func__); 9488c2ecf20Sopenharmony_ci return; 9498c2ecf20Sopenharmony_ci } 9508c2ecf20Sopenharmony_ci 9518c2ecf20Sopenharmony_ci usb_kill_urb(ati_remote->irq_urb); 9528c2ecf20Sopenharmony_ci usb_kill_urb(ati_remote->out_urb); 9538c2ecf20Sopenharmony_ci if (ati_remote->idev) 9548c2ecf20Sopenharmony_ci input_unregister_device(ati_remote->idev); 9558c2ecf20Sopenharmony_ci rc_unregister_device(ati_remote->rdev); 9568c2ecf20Sopenharmony_ci ati_remote_free_buffers(ati_remote); 9578c2ecf20Sopenharmony_ci kfree(ati_remote); 9588c2ecf20Sopenharmony_ci} 9598c2ecf20Sopenharmony_ci 9608c2ecf20Sopenharmony_ci/* usb specific object to register with the usb subsystem */ 9618c2ecf20Sopenharmony_cistatic struct usb_driver ati_remote_driver = { 9628c2ecf20Sopenharmony_ci .name = "ati_remote", 9638c2ecf20Sopenharmony_ci .probe = ati_remote_probe, 9648c2ecf20Sopenharmony_ci .disconnect = ati_remote_disconnect, 9658c2ecf20Sopenharmony_ci .id_table = ati_remote_table, 9668c2ecf20Sopenharmony_ci}; 9678c2ecf20Sopenharmony_ci 9688c2ecf20Sopenharmony_cimodule_usb_driver(ati_remote_driver); 9698c2ecf20Sopenharmony_ci 9708c2ecf20Sopenharmony_ciMODULE_AUTHOR(DRIVER_AUTHOR); 9718c2ecf20Sopenharmony_ciMODULE_DESCRIPTION(DRIVER_DESC); 9728c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 973