Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
hid-quirks.c
Go to the documentation of this file.
1 /*
2  * USB HID quirks support for Linux
3  *
4  * Copyright (c) 1999 Andreas Gal
5  * Copyright (c) 2000-2005 Vojtech Pavlik <[email protected]>
6  * Copyright (c) 2005 Michael Haboustak <[email protected]> for Concept2, Inc
7  * Copyright (c) 2006-2007 Jiri Kosina
8  * Copyright (c) 2007 Paul Walmsley
9  */
10 
11 /*
12  * This program is free software; you can redistribute it and/or modify it
13  * under the terms of the GNU General Public License as published by the Free
14  * Software Foundation; either version 2 of the License, or (at your option)
15  * any later version.
16  */
17 
18 #include <linux/hid.h>
19 #include <linux/export.h>
20 #include <linux/slab.h>
21 
22 #include "../hid-ids.h"
23 
24 /*
25  * Alphabetically sorted blacklist by quirk type.
26  */
27 
28 static const struct hid_blacklist {
31  __u32 quirks;
32 } hid_blacklist[] = {
47 
49 
56 
96 
98 
100 
106  { 0, 0 }
107 };
108 
109 /* Dynamic HID quirks list - specified at runtime */
111  struct hid_blacklist hid_bl_item;
112  struct list_head node;
113 };
114 
115 static LIST_HEAD(dquirks_list);
116 static DECLARE_RWSEM(dquirks_rwsem);
117 
118 /* Runtime ("dynamic") quirks manipulation functions */
119 
132 static struct hid_blacklist *usbhid_exists_dquirk(const u16 idVendor,
133  const u16 idProduct)
134 {
135  struct quirks_list_struct *q;
136  struct hid_blacklist *bl_entry = NULL;
137 
138  list_for_each_entry(q, &dquirks_list, node) {
139  if (q->hid_bl_item.idVendor == idVendor &&
140  q->hid_bl_item.idProduct == idProduct) {
141  bl_entry = &q->hid_bl_item;
142  break;
143  }
144  }
145 
146  if (bl_entry != NULL)
147  dbg_hid("Found dynamic quirk 0x%x for USB HID vendor 0x%hx prod 0x%hx\n",
148  bl_entry->quirks, bl_entry->idVendor,
149  bl_entry->idProduct);
150 
151  return bl_entry;
152 }
153 
154 
168 static int usbhid_modify_dquirk(const u16 idVendor, const u16 idProduct,
169  const u32 quirks)
170 {
171  struct quirks_list_struct *q_new, *q;
172  int list_edited = 0;
173 
174  if (!idVendor) {
175  dbg_hid("Cannot add a quirk with idVendor = 0\n");
176  return -EINVAL;
177  }
178 
179  q_new = kmalloc(sizeof(struct quirks_list_struct), GFP_KERNEL);
180  if (!q_new) {
181  dbg_hid("Could not allocate quirks_list_struct\n");
182  return -ENOMEM;
183  }
184 
185  q_new->hid_bl_item.idVendor = idVendor;
186  q_new->hid_bl_item.idProduct = idProduct;
187  q_new->hid_bl_item.quirks = quirks;
188 
189  down_write(&dquirks_rwsem);
190 
191  list_for_each_entry(q, &dquirks_list, node) {
192 
193  if (q->hid_bl_item.idVendor == idVendor &&
194  q->hid_bl_item.idProduct == idProduct) {
195 
196  list_replace(&q->node, &q_new->node);
197  kfree(q);
198  list_edited = 1;
199  break;
200 
201  }
202 
203  }
204 
205  if (!list_edited)
206  list_add_tail(&q_new->node, &dquirks_list);
207 
208  up_write(&dquirks_rwsem);
209 
210  return 0;
211 }
212 
221 static void usbhid_remove_all_dquirks(void)
222 {
223  struct quirks_list_struct *q, *temp;
224 
225  down_write(&dquirks_rwsem);
226  list_for_each_entry_safe(q, temp, &dquirks_list, node) {
227  list_del(&q->node);
228  kfree(q);
229  }
230  up_write(&dquirks_rwsem);
231 
232 }
233 
237 int usbhid_quirks_init(char **quirks_param)
238 {
239  u16 idVendor, idProduct;
240  u32 quirks;
241  int n = 0, m;
242 
243  for (; n < MAX_USBHID_BOOT_QUIRKS && quirks_param[n]; n++) {
244 
245  m = sscanf(quirks_param[n], "0x%hx:0x%hx:0x%x",
246  &idVendor, &idProduct, &quirks);
247 
248  if (m != 3 ||
249  usbhid_modify_dquirk(idVendor, idProduct, quirks) != 0) {
251  "Could not parse HID quirk module param %s\n",
252  quirks_param[n]);
253  }
254  }
255 
256  return 0;
257 }
258 
269 {
270  usbhid_remove_all_dquirks();
271 }
272 
284 static const struct hid_blacklist *usbhid_exists_squirk(const u16 idVendor,
285  const u16 idProduct)
286 {
287  const struct hid_blacklist *bl_entry = NULL;
288  int n = 0;
289 
290  for (; hid_blacklist[n].idVendor; n++)
291  if (hid_blacklist[n].idVendor == idVendor &&
292  hid_blacklist[n].idProduct == idProduct)
293  bl_entry = &hid_blacklist[n];
294 
295  if (bl_entry != NULL)
296  dbg_hid("Found squirk 0x%x for USB HID vendor 0x%hx prod 0x%hx\n",
297  bl_entry->quirks, bl_entry->idVendor,
298  bl_entry->idProduct);
299  return bl_entry;
300 }
301 
313 u32 usbhid_lookup_quirk(const u16 idVendor, const u16 idProduct)
314 {
315  u32 quirks = 0;
316  const struct hid_blacklist *bl_entry = NULL;
317 
318  /* NCR devices must not be queried for reports */
319  if (idVendor == USB_VENDOR_ID_NCR &&
320  idProduct >= USB_DEVICE_ID_NCR_FIRST &&
321  idProduct <= USB_DEVICE_ID_NCR_LAST)
323 
324  down_read(&dquirks_rwsem);
325  bl_entry = usbhid_exists_dquirk(idVendor, idProduct);
326  if (!bl_entry)
327  bl_entry = usbhid_exists_squirk(idVendor, idProduct);
328  if (bl_entry)
329  quirks = bl_entry->quirks;
330  up_read(&dquirks_rwsem);
331 
332  return quirks;
333 }
334