source: mainline/uspace/drv/usbkbd/main.c@ 45019865

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 45019865 was 45019865, checked in by Lubos Slovak <lubos.slovak@…>, 15 years ago

Fixed descriptor parsing.

  • Properly parses standard descriptors.
  • Properly gets report descriptor.
  • Property mode set to 100644
File size: 9.3 KB
Line 
1/*
2 * Copyright (c) 2010 Vojtech Horky
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28#include <usb/usbdrv.h>
29#include <driver.h>
30#include <ipc/driver.h>
31#include <ipc/kbd.h>
32#include <io/keycode.h>
33#include <io/console.h>
34#include <errno.h>
35#include <fibril.h>
36#include <usb/classes/hid.h>
37#include <usb/classes/hidparser.h>
38#include <usb/devreq.h>
39#include <usb/descriptor.h>
40#include "descparser.h"
41#include "descdump.h"
42
43#define BUFFER_SIZE 32
44#define NAME "usbkbd"
45
46#define GUESSED_POLL_ENDPOINT 1
47
48static void default_connection_handler(device_t *, ipc_callid_t, ipc_call_t *);
49static device_ops_t keyboard_ops = {
50 .default_handler = default_connection_handler
51};
52
53static int console_callback_phone = -1;
54
55/** Default handler for IPC methods not handled by DDF.
56 *
57 * @param dev Device handling the call.
58 * @param icallid Call id.
59 * @param icall Call data.
60 */
61void default_connection_handler(device_t *dev,
62 ipc_callid_t icallid, ipc_call_t *icall)
63{
64 sysarg_t method = IPC_GET_IMETHOD(*icall);
65
66 if (method == IPC_M_CONNECT_TO_ME) {
67 int callback = IPC_GET_ARG5(*icall);
68
69 if (console_callback_phone != -1) {
70 ipc_answer_0(icallid, ELIMIT);
71 return;
72 }
73
74 console_callback_phone = callback;
75 ipc_answer_0(icallid, EOK);
76 return;
77 }
78
79 ipc_answer_0(icallid, EINVAL);
80}
81
82#if 0
83static void send_key(int key, int type, wchar_t c) {
84 async_msg_4(console_callback_phone, KBD_EVENT, type, key,
85 KM_NUM_LOCK, c);
86}
87#endif
88
89/*
90 * Callbacks for parser
91 */
92static void usbkbd_process_keycodes(const uint16_t *key_codes, size_t count,
93 void *arg)
94{
95 printf("Got keys: ");
96 unsigned i;
97 for (i = 0; i < count; ++i) {
98 printf("%d ", key_codes[i]);
99 }
100 printf("\n");
101}
102
103/*
104 * Kbd functions
105 */
106static int usbkbd_get_report_descriptor(usb_hid_dev_kbd_t *kbd_dev)
107{
108 // iterate over all configurations and interfaces
109 // TODO: more configurations!!
110 unsigned i;
111 for (i = 0; i < kbd_dev->conf->config_descriptor.interface_count; ++i) {
112 // TODO: endianness
113 uint16_t length =
114 kbd_dev->conf->interfaces[i].hid_desc.report_desc_info.length;
115 size_t actual_size = 0;
116
117 // allocate space for the report descriptor
118 kbd_dev->conf->interfaces[i].report_desc = (uint8_t *)malloc(length);
119
120 // get the descriptor from the device
121 int rc = usb_drv_req_get_descriptor(kbd_dev->device->parent_phone,
122 kbd_dev->address, USB_REQUEST_TYPE_CLASS, USB_DESCTYPE_HID_REPORT,
123 0, i, kbd_dev->conf->interfaces[i].report_desc, length,
124 &actual_size);
125
126 if (rc != EOK) {
127 return rc;
128 }
129
130 assert(actual_size == length);
131
132 dump_hid_class_descriptor(0, USB_DESCTYPE_HID_REPORT,
133 kbd_dev->conf->interfaces[i].report_desc, length);
134 }
135
136 return EOK;
137}
138
139static int usbkbd_process_descriptors(usb_hid_dev_kbd_t *kbd_dev)
140{
141 // get the first configuration descriptor (TODO: parse also other!)
142 usb_standard_configuration_descriptor_t config_desc;
143
144 int rc = usb_drv_req_get_bare_configuration_descriptor(
145 kbd_dev->device->parent_phone, kbd_dev->address, 0, &config_desc);
146
147 if (rc != EOK) {
148 return rc;
149 }
150
151 // prepare space for all underlying descriptors
152 uint8_t *descriptors = (uint8_t *)malloc(config_desc.total_length);
153 if (descriptors == NULL) {
154 return ENOMEM;
155 }
156
157 size_t transferred = 0;
158 // get full configuration descriptor
159 rc = usb_drv_req_get_full_configuration_descriptor(
160 kbd_dev->device->parent_phone, kbd_dev->address, 0, descriptors,
161 config_desc.total_length, &transferred);
162
163 if (rc != EOK) {
164 return rc;
165 }
166 if (transferred != config_desc.total_length) {
167 return ELIMIT;
168 }
169
170 kbd_dev->conf = (usb_hid_configuration_t *)calloc(1,
171 sizeof(usb_hid_configuration_t));
172 if (kbd_dev->conf == NULL) {
173 free(descriptors);
174 return ENOMEM;
175 }
176
177 rc = usbkbd_parse_descriptors(descriptors, transferred, kbd_dev->conf);
178 free(descriptors);
179 if (rc != EOK) {
180 printf("Problem with parsing standard descriptors.\n");
181 return rc;
182 }
183
184 // get and report descriptors
185 rc = usbkbd_get_report_descriptor(kbd_dev);
186 if (rc != EOK) {
187 printf("Problem with parsing HID REPORT descriptor.\n");
188 return rc;
189 }
190
191 usbkbd_print_config(kbd_dev->conf);
192
193 return EOK;
194}
195
196static usb_hid_dev_kbd_t *usbkbd_init_device(device_t *dev)
197{
198 usb_hid_dev_kbd_t *kbd_dev = (usb_hid_dev_kbd_t *)calloc(1,
199 sizeof(usb_hid_dev_kbd_t));
200
201 if (kbd_dev == NULL) {
202 fprintf(stderr, NAME ": No memory!\n");
203 return NULL;
204 }
205
206 kbd_dev->device = dev;
207
208 // get phone to my HC and save it as my parent's phone
209 // TODO: maybe not a good idea if DDF will use parent_phone
210 kbd_dev->device->parent_phone = usb_drv_hc_connect_auto(dev, 0);
211
212 kbd_dev->address = usb_drv_get_my_address(dev->parent_phone,
213 dev);
214
215 // doesn't matter now that we have no address
216// if (kbd_dev->address < 0) {
217// fprintf(stderr, NAME ": No device address!\n");
218// free(kbd_dev);
219// return NULL;
220// }
221
222 // default endpoint
223 kbd_dev->poll_endpoint = GUESSED_POLL_ENDPOINT;
224
225 /*
226 * will need all descriptors:
227 * 1) choose one configuration from configuration descriptors
228 * (set it to the device)
229 * 2) set endpoints from endpoint descriptors
230 */
231
232 // TODO: get descriptors, parse descriptors and save endpoints
233 usbkbd_process_descriptors(kbd_dev);
234
235 return kbd_dev;
236}
237
238static void usbkbd_process_interrupt_in(usb_hid_dev_kbd_t *kbd_dev,
239 uint8_t *buffer, size_t actual_size)
240{
241 /*
242 * here, the parser will be called, probably with some callbacks
243 * now only take last 6 bytes and process, i.e. send to kbd
244 */
245
246 usb_hid_report_in_callbacks_t *callbacks =
247 (usb_hid_report_in_callbacks_t *)malloc(
248 sizeof(usb_hid_report_in_callbacks_t));
249 callbacks->keyboard = usbkbd_process_keycodes;
250
251 usb_hid_parse_report(kbd_dev->parser, buffer, actual_size, callbacks,
252 NULL);
253}
254
255static void usbkbd_poll_keyboard(usb_hid_dev_kbd_t *kbd_dev)
256{
257 return;
258
259 int rc;
260 usb_handle_t handle;
261 uint8_t buffer[BUFFER_SIZE];
262 size_t actual_size;
263 //usb_endpoint_t poll_endpoint = 1;
264
265// usb_address_t my_address = usb_drv_get_my_address(dev->parent_phone,
266// dev);
267// if (my_address < 0) {
268// return;
269// }
270
271 usb_target_t poll_target = {
272 .address = kbd_dev->address,
273 .endpoint = kbd_dev->poll_endpoint
274 };
275
276 while (true) {
277 async_usleep(1000 * 1000);
278 rc = usb_drv_async_interrupt_in(kbd_dev->device->parent_phone,
279 poll_target, buffer, BUFFER_SIZE, &actual_size, &handle);
280
281 if (rc != EOK) {
282 continue;
283 }
284
285 rc = usb_drv_async_wait_for(handle);
286 if (rc != EOK) {
287 continue;
288 }
289
290 /*
291 * If the keyboard answered with NAK, it returned no data.
292 * This implies that no change happened since last query.
293 */
294 if (actual_size == 0) {
295 continue;
296 }
297
298 /*
299 * TODO: Process pressed keys.
300 */
301 usbkbd_process_interrupt_in(kbd_dev, buffer, actual_size);
302 }
303
304 // not reached
305 assert(0);
306}
307
308static int usbkbd_fibril_device(void *arg)
309{
310 printf("!!! USB device fibril\n");
311
312 if (arg == NULL) {
313 printf("No device!\n");
314 return -1;
315 }
316
317 device_t *dev = (device_t *)arg;
318
319 // initialize device (get and process descriptors, get address, etc.)
320 usb_hid_dev_kbd_t *kbd_dev = usbkbd_init_device(dev);
321
322 usbkbd_poll_keyboard(kbd_dev);
323
324 return EOK;
325}
326
327static int usbkbd_add_device(device_t *dev)
328{
329 /* For now, fail immediately. */
330 //return ENOTSUP;
331
332 /*
333 * When everything is okay, connect to "our" HC.
334 *
335 * Not supported yet, skip..
336 */
337// int phone = usb_drv_hc_connect_auto(dev, 0);
338// if (phone < 0) {
339// /*
340// * Connecting to HC failed, roll-back and announce
341// * failure.
342// */
343// return phone;
344// }
345
346// dev->parent_phone = phone;
347
348 /*
349 * Create new fibril for handling this keyboard
350 */
351 fid_t fid = fibril_create(usbkbd_fibril_device, dev);
352 if (fid == 0) {
353 printf("%s: failed to start fibril for HID device\n", NAME);
354 return ENOMEM;
355 }
356 fibril_add_ready(fid);
357
358 dev->ops = &keyboard_ops;
359
360 add_device_to_class(dev, "keyboard");
361
362 /*
363 * Hurrah, device is initialized.
364 */
365 return EOK;
366}
367
368static driver_ops_t kbd_driver_ops = {
369 .add_device = usbkbd_add_device,
370};
371
372static driver_t kbd_driver = {
373 .name = NAME,
374 .driver_ops = &kbd_driver_ops
375};
376
377int main(int argc, char *argv[])
378{
379 return driver_main(&kbd_driver);
380}
Note: See TracBrowser for help on using the repository browser.