source: mainline/uspace/drv/usbkbd/main.c@ 71ed4849

lfn serial ticket/834-toolchain-update topic/fix-logger-deadlock topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 71ed4849 was 71ed4849, checked in by Vojtech Horky <vojtechhorky@…>, 16 years ago

Connecting to "parent" host controller

USB drivers can now connect to host controller drivers they physically
belong to (so far, everything connected to VHC).

Each USB device must implement the USB interface of libdrv to allow
this (it is absolutely neccesary for hubs).

USB drivers can use usb_drv_hc_connect() to connect to "their" host
controller.

Child devices created with usb_drv_register_child_in_devman()are set
to handle this connection automatically.

UHCI and VHC drivers were updated.

  • Property mode set to 100644
File size: 9.1 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
41#define BUFFER_SIZE 32
42#define NAME "usbkbd"
43
44#define GUESSED_POLL_ENDPOINT 1
45
46static void default_connection_handler(device_t *, ipc_callid_t, ipc_call_t *);
47static device_ops_t keyboard_ops = {
48 .default_handler = default_connection_handler
49};
50
51static int console_callback_phone = -1;
52
53/** Default handler for IPC methods not handled by DDF.
54 *
55 * @param dev Device handling the call.
56 * @param icallid Call id.
57 * @param icall Call data.
58 */
59void default_connection_handler(device_t *dev,
60 ipc_callid_t icallid, ipc_call_t *icall)
61{
62 sysarg_t method = IPC_GET_IMETHOD(*icall);
63
64 if (method == IPC_M_CONNECT_TO_ME) {
65 int callback = IPC_GET_ARG5(*icall);
66
67 if (console_callback_phone != -1) {
68 ipc_answer_0(icallid, ELIMIT);
69 return;
70 }
71
72 console_callback_phone = callback;
73 ipc_answer_0(icallid, EOK);
74 return;
75 }
76
77 ipc_answer_0(icallid, EINVAL);
78}
79
80static void send_key(int key, int type, wchar_t c) {
81 async_msg_4(console_callback_phone, KBD_EVENT, type, key,
82 KM_NUM_LOCK, c);
83}
84
85static void send_alnum(int key, wchar_t c) {
86 printf(NAME ": sending key '%lc' to console\n", (wint_t) c);
87 send_key(key, KEY_PRESS, c);
88 send_key(key, KEY_RELEASE, c);
89}
90
91/*
92 * Callbacks for parser
93 */
94static void usbkbd_process_keycodes(const uint16_t *key_codes, size_t count,
95 void *arg)
96{
97
98}
99
100/*
101 * Kbd functions
102 */
103static int usbkbd_parse_descriptors(usb_hid_dev_kbd_t *kbd_dev,
104 const uint8_t *data, size_t size)
105{
106// const uint8_t *pos = data;
107
108// // get the configuration descriptor (should be first)
109// if (*pos != sizeof(usb_standard_configuration_descriptor_t)
110// || *(pos + 1) != USB_DESCTYPE_CONFIGURATION) {
111// fprintf(stderr, "Wrong format of configuration descriptor");
112// return EINVAL;
113// }
114
115// usb_standard_configuration_descriptor_t config_descriptor;
116// memcpy(&config_descriptor, pos,
117// sizeof(usb_standard_configuration_descriptor_t));
118// pos += sizeof(usb_standard_configuration_descriptor_t);
119
120// // parse other descriptors
121// while (pos - data < size) {
122// //uint8_t desc_size = *pos;
123// uint8_t desc_type = *(pos + 1);
124// switch (desc_type) {
125// case USB_DESCTYPE_INTERFACE:
126// break;
127// case USB_DESCTYPE_ENDPOINT:
128// break;
129// case USB_DESCTYPE_HID:
130// break;
131// case USB_DESCTYPE_HID_REPORT:
132// break;
133// case USB_DESCTYPE_HID_PHYSICAL:
134// break;
135// }
136// }
137
138 return EOK;
139}
140
141static int usbkbd_get_descriptors(usb_hid_dev_kbd_t *kbd_dev)
142{
143 // get the first configuration descriptor (TODO: or some other??)
144 usb_standard_configuration_descriptor_t config_desc;
145
146 int rc = usb_drv_req_get_bare_configuration_descriptor(
147 kbd_dev->device->parent_phone, kbd_dev->address, 0, &config_desc);
148
149 if (rc != EOK) {
150 return rc;
151 }
152
153 // prepare space for all underlying descriptors
154 uint8_t *descriptors = (uint8_t *)malloc(config_desc.total_length);
155 if (descriptors == NULL) {
156 return ENOMEM;
157 }
158
159 size_t transferred = 0;
160 // get full configuration descriptor
161 rc = usb_drv_req_get_full_configuration_descriptor(
162 kbd_dev->device->parent_phone, kbd_dev->address, 0, descriptors,
163 config_desc.total_length, &transferred);
164
165 if (rc != EOK) {
166 return rc;
167 }
168 if (transferred != config_desc.total_length) {
169 return ELIMIT;
170 }
171
172 rc = usbkbd_parse_descriptors(kbd_dev, descriptors, transferred);
173 free(descriptors);
174
175 return rc;
176}
177
178static usb_hid_dev_kbd_t *usbkbd_init_device(device_t *dev)
179{
180 usb_hid_dev_kbd_t *kbd_dev = (usb_hid_dev_kbd_t *)malloc(
181 sizeof(usb_hid_dev_kbd_t));
182
183 if (kbd_dev == NULL) {
184 fprintf(stderr, NAME ": No memory!\n");
185 return NULL;
186 }
187
188 kbd_dev->device = dev;
189
190 // get phone to my HC and save it as my parent's phone
191 // TODO: maybe not a good idea if DDF will use parent_phone
192 kbd_dev->device->parent_phone = usb_drv_hc_connect_auto(dev, 0);
193
194 kbd_dev->address = usb_drv_get_my_address(dev->parent_phone,
195 dev);
196
197 // doesn't matter now that we have no address
198// if (kbd_dev->address < 0) {
199// fprintf(stderr, NAME ": No device address!\n");
200// free(kbd_dev);
201// return NULL;
202// }
203
204 // default endpoint
205 kbd_dev->poll_endpoint = GUESSED_POLL_ENDPOINT;
206
207 /*
208 * will need all descriptors:
209 * 1) choose one configuration from configuration descriptors
210 * (set it to the device)
211 * 2) set endpoints from endpoint descriptors
212 */
213
214 // TODO: get descriptors
215 usbkbd_get_descriptors(kbd_dev);
216 // TODO: parse descriptors and save endpoints
217
218 return kbd_dev;
219}
220
221static void usbkbd_process_interrupt_in(usb_hid_dev_kbd_t *kbd_dev,
222 uint8_t *buffer, size_t actual_size)
223{
224 /*
225 * here, the parser will be called, probably with some callbacks
226 * now only take last 6 bytes and process, i.e. send to kbd
227 */
228
229 usb_hid_report_in_callbacks_t *callbacks =
230 (usb_hid_report_in_callbacks_t *)malloc(
231 sizeof(usb_hid_report_in_callbacks_t));
232 callbacks->keyboard = usbkbd_process_keycodes;
233
234 if (console_callback_phone != -1) {
235 static size_t counter = 0;
236 counter++;
237 if (counter > 3) {
238 counter = 0;
239 send_alnum(KC_A, L'a');
240 }
241 }
242
243 usb_hid_parse_report(kbd_dev->parser, buffer, actual_size, callbacks,
244 NULL);
245}
246
247static void usbkbd_poll_keyboard(usb_hid_dev_kbd_t *kbd_dev)
248{
249 int rc;
250 usb_handle_t handle;
251 uint8_t buffer[BUFFER_SIZE];
252 size_t actual_size;
253 //usb_endpoint_t poll_endpoint = 1;
254
255// usb_address_t my_address = usb_drv_get_my_address(dev->parent_phone,
256// dev);
257// if (my_address < 0) {
258// return;
259// }
260
261 usb_target_t poll_target = {
262 .address = kbd_dev->address,
263 .endpoint = kbd_dev->poll_endpoint
264 };
265
266 while (true) {
267 async_usleep(1000 * 1000);
268 rc = usb_drv_async_interrupt_in(kbd_dev->device->parent_phone,
269 poll_target, buffer, BUFFER_SIZE, &actual_size, &handle);
270
271 if (rc != EOK) {
272 continue;
273 }
274
275 rc = usb_drv_async_wait_for(handle);
276 if (rc != EOK) {
277 continue;
278 }
279
280 /*
281 * If the keyboard answered with NAK, it returned no data.
282 * This implies that no change happened since last query.
283 */
284 if (actual_size == 0) {
285 continue;
286 }
287
288 /*
289 * TODO: Process pressed keys.
290 */
291 usbkbd_process_interrupt_in(kbd_dev, buffer, actual_size);
292 }
293
294 // not reached
295 assert(0);
296}
297
298static int usbkbd_fibril_device(void *arg)
299{
300 printf("!!! USB device fibril\n");
301
302 if (arg == NULL) {
303 printf("No device!\n");
304 return -1;
305 }
306
307 device_t *dev = (device_t *)arg;
308
309 // initialize device (get and process descriptors, get address, etc.)
310 usb_hid_dev_kbd_t *kbd_dev = usbkbd_init_device(dev);
311
312 usbkbd_poll_keyboard(kbd_dev);
313
314 return EOK;
315}
316
317static int usbkbd_add_device(device_t *dev)
318{
319 /* For now, fail immediately. */
320 //return ENOTSUP;
321
322 /*
323 * When everything is okay, connect to "our" HC.
324 *
325 * Not supported yet, skip..
326 */
327// int phone = usb_drv_hc_connect_auto(dev, 0);
328// if (phone < 0) {
329// /*
330// * Connecting to HC failed, roll-back and announce
331// * failure.
332// */
333// return phone;
334// }
335
336// dev->parent_phone = phone;
337
338 /*
339 * Create new fibril for handling this keyboard
340 */
341 fid_t fid = fibril_create(usbkbd_fibril_device, dev);
342 if (fid == 0) {
343 printf("%s: failed to start fibril for HID device\n", NAME);
344 return ENOMEM;
345 }
346 fibril_add_ready(fid);
347
348 dev->ops = &keyboard_ops;
349
350 add_device_to_class(dev, "keyboard");
351
352 /*
353 * Hurrah, device is initialized.
354 */
355 return EOK;
356}
357
358static driver_ops_t kbd_driver_ops = {
359 .add_device = usbkbd_add_device,
360};
361
362static driver_t kbd_driver = {
363 .name = NAME,
364 .driver_ops = &kbd_driver_ops
365};
366
367int main(int argc, char *argv[])
368{
369 return driver_main(&kbd_driver);
370}
Note: See TracBrowser for help on using the repository browser.