rename td_token to td_xtoken to deal with conflict against sys/thread.h
[dragonfly.git] / sys / bus / usb / hid.c
1 /*      $NetBSD: hid.c,v 1.15 2000/04/27 15:26:46 augustss Exp $        */
2 /*      $FreeBSD: src/sys/dev/usb/hid.c,v 1.11.2.6 2002/11/06 14:03:37 joe Exp $ */
3 /*      $DragonFly: src/sys/bus/usb/hid.c,v 1.2 2003/06/17 04:28:32 dillon Exp $ */
4
5 /*
6  * Copyright (c) 1998 The NetBSD Foundation, Inc.
7  * All rights reserved.
8  *
9  * This code is derived from software contributed to The NetBSD Foundation
10  * by Lennart Augustsson (lennart@augustsson.net) at
11  * Carlstedt Research & Technology.
12  *
13  * Redistribution and use in source and binary forms, with or without
14  * modification, are permitted provided that the following conditions
15  * are met:
16  * 1. Redistributions of source code must retain the above copyright
17  *    notice, this list of conditions and the following disclaimer.
18  * 2. Redistributions in binary form must reproduce the above copyright
19  *    notice, this list of conditions and the following disclaimer in the
20  *    documentation and/or other materials provided with the distribution.
21  * 3. All advertising materials mentioning features or use of this software
22  *    must display the following acknowledgement:
23  *        This product includes software developed by the NetBSD
24  *        Foundation, Inc. and its contributors.
25  * 4. Neither the name of The NetBSD Foundation nor the names of its
26  *    contributors may be used to endorse or promote products derived
27  *    from this software without specific prior written permission.
28  *
29  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
30  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
31  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
32  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
33  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
34  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
35  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
36  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
37  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
38  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
39  * POSSIBILITY OF SUCH DAMAGE.
40  */
41
42 #include <sys/param.h>
43 #include <sys/systm.h>
44 #if defined(__NetBSD__)
45 #include <sys/kernel.h>
46 #endif
47 #include <sys/malloc.h>
48  
49 #include <dev/usb/usb.h>
50 #include <dev/usb/usbhid.h>
51
52 #include <dev/usb/hid.h>
53
54 #ifdef USB_DEBUG
55 #define DPRINTF(x)      if (usbdebug) logprintf x
56 #define DPRINTFN(n,x)   if (usbdebug>(n)) logprintf x
57 extern int usbdebug;
58 #else
59 #define DPRINTF(x)
60 #define DPRINTFN(n,x)
61 #endif
62
63 Static void hid_clear_local(struct hid_item *);
64
65 #define MAXUSAGE 100
66 struct hid_data {
67         u_char *start;
68         u_char *end;
69         u_char *p;
70         struct hid_item cur;
71         int32_t usages[MAXUSAGE];
72         int nu;
73         int minset;
74         int multi;
75         int multimax;
76         int kindset;
77 };
78
79 Static void
80 hid_clear_local(struct hid_item *c)
81 {
82         c->usage = 0;
83         c->usage_minimum = 0;
84         c->usage_maximum = 0;
85         c->designator_index = 0;
86         c->designator_minimum = 0;
87         c->designator_maximum = 0;
88         c->string_index = 0;
89         c->string_minimum = 0;
90         c->string_maximum = 0;
91         c->set_delimiter = 0;
92 }
93
94 struct hid_data *
95 hid_start_parse(void *d, int len, int kindset)
96 {
97         struct hid_data *s;
98
99         s = malloc(sizeof *s, M_TEMP, M_WAITOK);
100         memset(s, 0, sizeof *s);
101         s->start = s->p = d;
102         s->end = (char *)d + len;
103         s->kindset = kindset;
104         return (s);
105 }
106
107 void
108 hid_end_parse(struct hid_data *s)
109 {
110         while (s->cur.next != NULL) {
111                 struct hid_item *hi = s->cur.next->next;
112                 free(s->cur.next, M_TEMP);
113                 s->cur.next = hi;
114         }
115         free(s, M_TEMP);
116 }
117
118 int
119 hid_get_item(struct hid_data *s, struct hid_item *h)
120 {
121         struct hid_item *c = &s->cur;
122         unsigned int bTag, bType, bSize;
123         u_int32_t oldpos;
124         u_char *data;
125         int32_t dval;
126         u_char *p;
127         struct hid_item *hi;
128         int i;
129
130  top:
131         if (s->multimax != 0) {
132                 if (s->multi < s->multimax) {
133                         c->usage = s->usages[min(s->multi, s->nu-1)];
134                         s->multi++;
135                         *h = *c;
136                         c->loc.pos += c->loc.size;
137                         h->next = 0;
138                         return (1);
139                 } else {
140                         c->loc.count = s->multimax;
141                         s->multimax = 0;
142                         s->nu = 0;
143                         hid_clear_local(c);
144                 }
145         }
146         for (;;) {
147                 p = s->p;
148                 if (p >= s->end)
149                         return (0);
150
151                 bSize = *p++;
152                 if (bSize == 0xfe) {
153                         /* long item */
154                         bSize = *p++;
155                         bSize |= *p++ << 8;
156                         bTag = *p++;
157                         data = p;
158                         p += bSize;
159                         bType = 0xff; /* XXX what should it be */
160                 } else {
161                         /* short item */
162                         bTag = bSize >> 4;
163                         bType = (bSize >> 2) & 3;
164                         bSize &= 3;
165                         if (bSize == 3) bSize = 4;
166                         data = p;
167                         p += bSize;
168                 }
169                 s->p = p;
170                 switch(bSize) {
171                 case 0:
172                         dval = 0;
173                         break;
174                 case 1:
175                         dval = (int8_t)*data++;
176                         break;
177                 case 2:
178                         dval = *data++;
179                         dval |= *data++ << 8;
180                         dval = (int16_t)dval;
181                         break;
182                 case 4:
183                         dval = *data++;
184                         dval |= *data++ << 8;
185                         dval |= *data++ << 16;
186                         dval |= *data++ << 24;
187                         break;
188                 default:
189                         printf("BAD LENGTH %d\n", bSize);
190                         continue;
191                 }
192                 
193                 switch (bType) {
194                 case 0:                 /* Main */
195                         switch (bTag) {
196                         case 8:         /* Input */
197                                 if (!(s->kindset & (1 << hid_input)))
198                                         continue;
199                                 c->kind = hid_input;
200                                 c->flags = dval;
201                         ret:
202                                 if (c->flags & HIO_VARIABLE) {
203                                         s->multimax = c->loc.count;
204                                         s->multi = 0;
205                                         c->loc.count = 1;
206                                         if (s->minset) {
207                                                 for (i = c->usage_minimum; 
208                                                      i <= c->usage_maximum; 
209                                                      i++) {
210                                                         s->usages[s->nu] = i;
211                                                         if (s->nu < MAXUSAGE-1)
212                                                                 s->nu++;
213                                                 }
214                                                 s->minset = 0;
215                                         }
216                                         goto top;
217                                 } else {
218                                         *h = *c;
219                                         h->next = 0;
220                                         c->loc.pos += 
221                                                 c->loc.size * c->loc.count;
222                                         hid_clear_local(c);
223                                         s->minset = 0;
224                                         return (1);
225                                 }
226                         case 9:         /* Output */
227                                 if (!(s->kindset & (1 << hid_output)))
228                                         continue;
229                                 c->kind = hid_output;
230                                 c->flags = dval;
231                                 goto ret;
232                         case 10:        /* Collection */
233                                 c->kind = hid_collection;
234                                 c->collection = dval;
235                                 c->collevel++;
236                                 *h = *c;
237                                 hid_clear_local(c);
238                                 s->nu = 0;
239                                 return (1);
240                         case 11:        /* Feature */
241                                 if (!(s->kindset & (1 << hid_feature)))
242                                         continue;
243                                 c->kind = hid_feature;
244                                 c->flags = dval;
245                                 goto ret;
246                         case 12:        /* End collection */
247                                 c->kind = hid_endcollection;
248                                 c->collevel--;
249                                 *h = *c;
250                                 hid_clear_local(c);
251                                 s->nu = 0;
252                                 return (1);
253                         default:
254                                 printf("Main bTag=%d\n", bTag);
255                                 break;
256                         }
257                         break;
258                 case 1:         /* Global */
259                         switch (bTag) {
260                         case 0:
261                                 c->_usage_page = dval << 16;
262                                 break;
263                         case 1:
264                                 c->logical_minimum = dval;
265                                 break;
266                         case 2:
267                                 c->logical_maximum = dval;
268                                 break;
269                         case 3:
270                                 c->physical_maximum = dval;
271                                 break;
272                         case 4:
273                                 c->physical_maximum = dval;
274                                 break;
275                         case 5:
276                                 c->unit_exponent = dval;
277                                 break;
278                         case 6:
279                                 c->unit = dval;
280                                 break;
281                         case 7:
282                                 c->loc.size = dval;
283                                 break;
284                         case 8:
285                                 c->report_ID = dval;
286                                 break;
287                         case 9:
288                                 c->loc.count = dval;
289                                 break;
290                         case 10: /* Push */
291                                 hi = malloc(sizeof *hi, M_TEMP, M_WAITOK);
292                                 *hi = s->cur;
293                                 c->next = hi;
294                                 break;
295                         case 11: /* Pop */
296                                 hi = c->next;
297                                 oldpos = c->loc.pos;
298                                 s->cur = *hi;
299                                 c->loc.pos = oldpos;
300                                 free(hi, M_TEMP);
301                                 break;
302                         default:
303                                 printf("Global bTag=%d\n", bTag);
304                                 break;
305                         }
306                         break;
307                 case 2:         /* Local */
308                         switch (bTag) {
309                         case 0:
310                                 if (bSize == 1) 
311                                         dval = c->_usage_page | (dval&0xff);
312                                 else if (bSize == 2) 
313                                         dval = c->_usage_page | (dval&0xffff);
314                                 c->usage = dval;
315                                 if (s->nu < MAXUSAGE)
316                                         s->usages[s->nu++] = dval;
317                                 /* else XXX */
318                                 break;
319                         case 1:
320                                 s->minset = 1;
321                                 if (bSize == 1) 
322                                         dval = c->_usage_page | (dval&0xff);
323                                 else if (bSize == 2) 
324                                         dval = c->_usage_page | (dval&0xffff);
325                                 c->usage_minimum = dval;
326                                 break;
327                         case 2:
328                                 if (bSize == 1) 
329                                         dval = c->_usage_page | (dval&0xff);
330                                 else if (bSize == 2) 
331                                         dval = c->_usage_page | (dval&0xffff);
332                                 c->usage_maximum = dval;
333                                 break;
334                         case 3:
335                                 c->designator_index = dval;
336                                 break;
337                         case 4:
338                                 c->designator_minimum = dval;
339                                 break;
340                         case 5:
341                                 c->designator_maximum = dval;
342                                 break;
343                         case 7:
344                                 c->string_index = dval;
345                                 break;
346                         case 8:
347                                 c->string_minimum = dval;
348                                 break;
349                         case 9:
350                                 c->string_maximum = dval;
351                                 break;
352                         case 10:
353                                 c->set_delimiter = dval;
354                                 break;
355                         default:
356                                 printf("Local bTag=%d\n", bTag);
357                                 break;
358                         }
359                         break;
360                 default:
361                         printf("default bType=%d\n", bType);
362                         break;
363                 }
364         }
365 }
366
367 int
368 hid_report_size(void *buf, int len, enum hid_kind k, u_int8_t *idp)
369 {
370         struct hid_data *d;
371         struct hid_item h;
372         int size, id;
373
374         id = 0;
375         for (d = hid_start_parse(buf, len, 1<<k); hid_get_item(d, &h); )
376                 if (h.report_ID != 0)
377                         id = h.report_ID;
378         hid_end_parse(d);
379         size = h.loc.pos;
380         if (id != 0) {
381                 size += 8;
382                 *idp = id;      /* XXX wrong */
383         } else
384                 *idp = 0;
385         return ((size + 7) / 8);
386 }
387
388 int
389 hid_locate(void *desc, int size, u_int32_t u, enum hid_kind k,
390            struct hid_location *loc, u_int32_t *flags)
391 {
392         struct hid_data *d;
393         struct hid_item h;
394
395         for (d = hid_start_parse(desc, size, 1<<k); hid_get_item(d, &h); ) {
396                 if (h.kind == k && !(h.flags & HIO_CONST) && h.usage == u) {
397                         if (loc != NULL)
398                                 *loc = h.loc;
399                         if (flags != NULL)
400                                 *flags = h.flags;
401                         hid_end_parse(d);
402                         return (1);
403                 }
404         }
405         hid_end_parse(d);
406         loc->size = 0;
407         return (0);
408 }
409
410 u_long
411 hid_get_data(u_char *buf, struct hid_location *loc)
412 {
413         u_int hpos = loc->pos;
414         u_int hsize = loc->size;
415         u_int32_t data;
416         int i, s;
417
418         DPRINTFN(10, ("hid_get_data: loc %d/%d\n", hpos, hsize));
419
420         if (hsize == 0)
421                 return (0);
422
423         data = 0;
424         s = hpos / 8; 
425         for (i = hpos; i < hpos+hsize; i += 8)
426                 data |= buf[i / 8] << ((i / 8 - s) * 8);
427         data >>= hpos % 8;
428         data &= (1 << hsize) - 1;
429         hsize = 32 - hsize;
430         /* Sign extend */
431         data = ((int32_t)data << hsize) >> hsize;
432         DPRINTFN(10,("hid_get_data: loc %d/%d = %lu\n", 
433                     loc->pos, loc->size, (long)data));
434         return (data);
435 }
436
437 int
438 hid_is_collection(void *desc, int size, u_int32_t usage)
439 {
440         struct hid_data *hd;
441         struct hid_item hi;
442         int err;
443
444         hd = hid_start_parse(desc, size, hid_input);
445         if (hd == NULL)
446                 return (0);
447
448         err = hid_get_item(hd, &hi) &&
449             hi.kind == hid_collection &&
450             hi.usage == usage;
451         hid_end_parse(hd);
452         return (err);
453 }