hid-samsung.c (d9c5841e22231e4e49fd0a1004164e6fce59b7a6) | hid-samsung.c (b355850ba383232d4e0e357c1cda8cb7bfcc60bc) |
---|---|
1/* 2 * HID driver for some samsung "special" devices 3 * 4 * Copyright (c) 1999 Andreas Gal 5 * Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz> 6 * Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc 7 * Copyright (c) 2006-2007 Jiri Kosina 8 * Copyright (c) 2007 Paul Walmsley 9 * Copyright (c) 2008 Jiri Slaby | 1/* 2 * HID driver for some samsung "special" devices 3 * 4 * Copyright (c) 1999 Andreas Gal 5 * Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz> 6 * Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc 7 * Copyright (c) 2006-2007 Jiri Kosina 8 * Copyright (c) 2007 Paul Walmsley 9 * Copyright (c) 2008 Jiri Slaby |
10 * Copyright (c) 2010 Don Prince <dhprince.devel@yahoo.co.uk> 11 * 12 * 13 * This driver supports several HID devices: 14 * 15 * [0419:0001] Samsung IrDA remote controller (reports as Cypress USB Mouse). 16 * various hid report fixups for different variants. 17 * 18 * [0419:0600] Creative Desktop Wireless 6000 keyboard/mouse combo 19 * several key mappings used from the consumer usage page 20 * deviate from the USB HUT 1.12 standard. 21 * |
|
10 */ 11 12/* 13 * This program is free software; you can redistribute it and/or modify it 14 * under the terms of the GNU General Public License as published by the Free 15 * Software Foundation; either version 2 of the License, or (at your option) 16 * any later version. 17 */ 18 19#include <linux/device.h> | 22 */ 23 24/* 25 * This program is free software; you can redistribute it and/or modify it 26 * under the terms of the GNU General Public License as published by the Free 27 * Software Foundation; either version 2 of the License, or (at your option) 28 * any later version. 29 */ 30 31#include <linux/device.h> |
32#include <linux/usb.h> |
|
20#include <linux/hid.h> 21#include <linux/module.h> 22 23#include "hid-ids.h" 24 25/* | 33#include <linux/hid.h> 34#include <linux/module.h> 35 36#include "hid-ids.h" 37 38/* |
26 * Samsung IrDA remote controller (reports as Cypress USB Mouse). 27 * | |
28 * There are several variants for 0419:0001: 29 * 30 * 1. 184 byte report descriptor 31 * Vendor specific report #4 has a size of 48 bit, 32 * and therefore is not accepted when inspecting the descriptors. 33 * As a workaround we reinterpret the report as: 34 * Variable type, count 6, size 8 bit, log. maximum 255 35 * The burden to reconstruct the data is moved into user space. 36 * 37 * 2. 203 byte report descriptor 38 * Report #4 has an array field with logical range 0..18 instead of 1..15. 39 * 40 * 3. 135 byte report descriptor 41 * Report #4 has an array field with logical range 0..17 instead of 1..14. 42 * 43 * 4. 171 byte report descriptor 44 * Report #3 has an array field with logical range 0..1 instead of 1..3. 45 */ | 39 * There are several variants for 0419:0001: 40 * 41 * 1. 184 byte report descriptor 42 * Vendor specific report #4 has a size of 48 bit, 43 * and therefore is not accepted when inspecting the descriptors. 44 * As a workaround we reinterpret the report as: 45 * Variable type, count 6, size 8 bit, log. maximum 255 46 * The burden to reconstruct the data is moved into user space. 47 * 48 * 2. 203 byte report descriptor 49 * Report #4 has an array field with logical range 0..18 instead of 1..15. 50 * 51 * 3. 135 byte report descriptor 52 * Report #4 has an array field with logical range 0..17 instead of 1..14. 53 * 54 * 4. 171 byte report descriptor 55 * Report #3 has an array field with logical range 0..1 instead of 1..3. 56 */ |
46static inline void samsung_dev_trace(struct hid_device *hdev, | 57static inline void samsung_irda_dev_trace(struct hid_device *hdev, |
47 unsigned int rsize) 48{ 49 dev_info(&hdev->dev, "fixing up Samsung IrDA %d byte report " 50 "descriptor\n", rsize); 51} 52 | 58 unsigned int rsize) 59{ 60 dev_info(&hdev->dev, "fixing up Samsung IrDA %d byte report " 61 "descriptor\n", rsize); 62} 63 |
53static void samsung_report_fixup(struct hid_device *hdev, __u8 *rdesc, | 64static void samsung_irda_report_fixup(struct hid_device *hdev, __u8 *rdesc, |
54 unsigned int rsize) 55{ 56 if (rsize == 184 && rdesc[175] == 0x25 && rdesc[176] == 0x40 && 57 rdesc[177] == 0x75 && rdesc[178] == 0x30 && 58 rdesc[179] == 0x95 && rdesc[180] == 0x01 && 59 rdesc[182] == 0x40) { | 65 unsigned int rsize) 66{ 67 if (rsize == 184 && rdesc[175] == 0x25 && rdesc[176] == 0x40 && 68 rdesc[177] == 0x75 && rdesc[178] == 0x30 && 69 rdesc[179] == 0x95 && rdesc[180] == 0x01 && 70 rdesc[182] == 0x40) { |
60 samsung_dev_trace(hdev, 184); | 71 samsung_irda_dev_trace(hdev, 184); |
61 rdesc[176] = 0xff; 62 rdesc[178] = 0x08; 63 rdesc[180] = 0x06; 64 rdesc[182] = 0x42; 65 } else 66 if (rsize == 203 && rdesc[192] == 0x15 && rdesc[193] == 0x0 && 67 rdesc[194] == 0x25 && rdesc[195] == 0x12) { | 72 rdesc[176] = 0xff; 73 rdesc[178] = 0x08; 74 rdesc[180] = 0x06; 75 rdesc[182] = 0x42; 76 } else 77 if (rsize == 203 && rdesc[192] == 0x15 && rdesc[193] == 0x0 && 78 rdesc[194] == 0x25 && rdesc[195] == 0x12) { |
68 samsung_dev_trace(hdev, 203); | 79 samsung_irda_dev_trace(hdev, 203); |
69 rdesc[193] = 0x1; 70 rdesc[195] = 0xf; 71 } else 72 if (rsize == 135 && rdesc[124] == 0x15 && rdesc[125] == 0x0 && 73 rdesc[126] == 0x25 && rdesc[127] == 0x11) { | 80 rdesc[193] = 0x1; 81 rdesc[195] = 0xf; 82 } else 83 if (rsize == 135 && rdesc[124] == 0x15 && rdesc[125] == 0x0 && 84 rdesc[126] == 0x25 && rdesc[127] == 0x11) { |
74 samsung_dev_trace(hdev, 135); | 85 samsung_irda_dev_trace(hdev, 135); |
75 rdesc[125] = 0x1; 76 rdesc[127] = 0xe; 77 } else 78 if (rsize == 171 && rdesc[160] == 0x15 && rdesc[161] == 0x0 && 79 rdesc[162] == 0x25 && rdesc[163] == 0x01) { | 86 rdesc[125] = 0x1; 87 rdesc[127] = 0xe; 88 } else 89 if (rsize == 171 && rdesc[160] == 0x15 && rdesc[161] == 0x0 && 90 rdesc[162] == 0x25 && rdesc[163] == 0x01) { |
80 samsung_dev_trace(hdev, 171); | 91 samsung_irda_dev_trace(hdev, 171); |
81 rdesc[161] = 0x1; 82 rdesc[163] = 0x3; 83 } 84} 85 | 92 rdesc[161] = 0x1; 93 rdesc[163] = 0x3; 94 } 95} 96 |
97#define samsung_kbd_mouse_map_key_clear(c) \ 98 hid_map_usage_clear(hi, usage, bit, max, EV_KEY, (c)) 99 100static int samsung_kbd_mouse_input_mapping(struct hid_device *hdev, 101 struct hid_input *hi, struct hid_field *field, struct hid_usage *usage, 102 unsigned long **bit, int *max) 103{ 104 struct usb_interface *intf = to_usb_interface(hdev->dev.parent); 105 unsigned short ifnum = intf->cur_altsetting->desc.bInterfaceNumber; 106 107 if (1 != ifnum || HID_UP_CONSUMER != (usage->hid & HID_USAGE_PAGE)) 108 return 0; 109 110 dbg_hid("samsung wireless keyboard/mouse input mapping event [0x%x]\n", 111 usage->hid & HID_USAGE); 112 113 switch (usage->hid & HID_USAGE) { 114 /* report 2 */ 115 case 0x0b5: samsung_kbd_mouse_map_key_clear(KEY_NEXTSONG); break; 116 case 0x0b6: samsung_kbd_mouse_map_key_clear(KEY_PREVIOUSSONG); break; 117 case 0x0b7: samsung_kbd_mouse_map_key_clear(KEY_STOPCD); break; 118 case 0x0cd: samsung_kbd_mouse_map_key_clear(KEY_PLAYPAUSE); break; 119 case 0x0e2: samsung_kbd_mouse_map_key_clear(KEY_MUTE); break; 120 case 0x0e9: samsung_kbd_mouse_map_key_clear(KEY_VOLUMEUP); break; 121 case 0x0ea: samsung_kbd_mouse_map_key_clear(KEY_VOLUMEDOWN); break; 122 case 0x183: samsung_kbd_mouse_map_key_clear(KEY_MEDIA); break; 123 case 0x195: samsung_kbd_mouse_map_key_clear(KEY_EMAIL); break; 124 case 0x196: samsung_kbd_mouse_map_key_clear(KEY_CALC); break; 125 case 0x197: samsung_kbd_mouse_map_key_clear(KEY_COMPUTER); break; 126 case 0x22b: samsung_kbd_mouse_map_key_clear(KEY_SEARCH); break; 127 case 0x22c: samsung_kbd_mouse_map_key_clear(KEY_WWW); break; 128 case 0x22d: samsung_kbd_mouse_map_key_clear(KEY_BACK); break; 129 case 0x22e: samsung_kbd_mouse_map_key_clear(KEY_FORWARD); break; 130 case 0x22f: samsung_kbd_mouse_map_key_clear(KEY_FAVORITES); break; 131 case 0x230: samsung_kbd_mouse_map_key_clear(KEY_REFRESH); break; 132 case 0x231: samsung_kbd_mouse_map_key_clear(KEY_STOP); break; 133 default: 134 return 0; 135 } 136 137 return 1; 138} 139 140static void samsung_report_fixup(struct hid_device *hdev, __u8 *rdesc, 141 unsigned int rsize) 142{ 143 if (USB_DEVICE_ID_SAMSUNG_IR_REMOTE == hdev->product) 144 samsung_irda_report_fixup(hdev, rdesc, rsize); 145} 146 147static int samsung_input_mapping(struct hid_device *hdev, struct hid_input *hi, 148 struct hid_field *field, struct hid_usage *usage, 149 unsigned long **bit, int *max) 150{ 151 int ret = 0; 152 153 if (USB_DEVICE_ID_SAMSUNG_WIRELESS_KBD_MOUSE == hdev->product) 154 ret = samsung_kbd_mouse_input_mapping(hdev, 155 hi, field, usage, bit, max); 156 157 return ret; 158} 159 |
|
86static int samsung_probe(struct hid_device *hdev, 87 const struct hid_device_id *id) 88{ 89 int ret; 90 unsigned int cmask = HID_CONNECT_DEFAULT; 91 92 ret = hid_parse(hdev); 93 if (ret) { 94 dev_err(&hdev->dev, "parse failed\n"); 95 goto err_free; 96 } 97 | 160static int samsung_probe(struct hid_device *hdev, 161 const struct hid_device_id *id) 162{ 163 int ret; 164 unsigned int cmask = HID_CONNECT_DEFAULT; 165 166 ret = hid_parse(hdev); 167 if (ret) { 168 dev_err(&hdev->dev, "parse failed\n"); 169 goto err_free; 170 } 171 |
98 if (hdev->rsize == 184) { 99 /* disable hidinput, force hiddev */ 100 cmask = (cmask & ~HID_CONNECT_HIDINPUT) | 101 HID_CONNECT_HIDDEV_FORCE; | 172 if (USB_DEVICE_ID_SAMSUNG_IR_REMOTE == hdev->product) { 173 if (hdev->rsize == 184) { 174 /* disable hidinput, force hiddev */ 175 cmask = (cmask & ~HID_CONNECT_HIDINPUT) | 176 HID_CONNECT_HIDDEV_FORCE; 177 } |
102 } 103 104 ret = hid_hw_start(hdev, cmask); 105 if (ret) { 106 dev_err(&hdev->dev, "hw start failed\n"); 107 goto err_free; 108 } 109 110 return 0; 111err_free: 112 return ret; 113} 114 115static const struct hid_device_id samsung_devices[] = { 116 { HID_USB_DEVICE(USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_IR_REMOTE) }, | 178 } 179 180 ret = hid_hw_start(hdev, cmask); 181 if (ret) { 182 dev_err(&hdev->dev, "hw start failed\n"); 183 goto err_free; 184 } 185 186 return 0; 187err_free: 188 return ret; 189} 190 191static const struct hid_device_id samsung_devices[] = { 192 { HID_USB_DEVICE(USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_IR_REMOTE) }, |
193 { HID_USB_DEVICE(USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_WIRELESS_KBD_MOUSE) }, |
|
117 { } 118}; 119MODULE_DEVICE_TABLE(hid, samsung_devices); 120 121static struct hid_driver samsung_driver = { 122 .name = "samsung", 123 .id_table = samsung_devices, 124 .report_fixup = samsung_report_fixup, | 194 { } 195}; 196MODULE_DEVICE_TABLE(hid, samsung_devices); 197 198static struct hid_driver samsung_driver = { 199 .name = "samsung", 200 .id_table = samsung_devices, 201 .report_fixup = samsung_report_fixup, |
202 .input_mapping = samsung_input_mapping, |
|
125 .probe = samsung_probe, 126}; 127 128static int __init samsung_init(void) 129{ 130 return hid_register_driver(&samsung_driver); 131} 132 133static void __exit samsung_exit(void) 134{ 135 hid_unregister_driver(&samsung_driver); 136} 137 138module_init(samsung_init); 139module_exit(samsung_exit); 140MODULE_LICENSE("GPL"); | 203 .probe = samsung_probe, 204}; 205 206static int __init samsung_init(void) 207{ 208 return hid_register_driver(&samsung_driver); 209} 210 211static void __exit samsung_exit(void) 212{ 213 hid_unregister_driver(&samsung_driver); 214} 215 216module_init(samsung_init); 217module_exit(samsung_exit); 218MODULE_LICENSE("GPL"); |