source: mainline/uspace/drv/usbhub/usbhub.c@ 5c3ace2

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 5c3ace2 was 357a302, checked in by Vojtech Horky <vojtechhorky@…>, 15 years ago

USB interfaces reorganization

The most important change is that getting USB address of a device
is an operation of generic USB interface, not USB-HC interface.
That is needed for proper functionality of a MID driver.

Also added sample implementation of USB interface operations as is
needed by most drivers (sample does not mean unfunctional or partially
implemented here).
They are stored in libusb/ddfiface.h

Updated UHCI, UHCI-RH, hub, VHC drivers to use these sample
implementations.

Updated libusb device recognition routines to route get_address requests
through USB interface.

  • Property mode set to 100644
File size: 14.9 KB
Line 
1/*
2 * Copyright (c) 2010 Matus Dekanek
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/** @addtogroup drvusbhub
29 * @{
30 */
31/** @file
32 * @brief usb hub main functionality
33 */
34
35#include <driver.h>
36#include <bool.h>
37#include <errno.h>
38#include <str_error.h>
39
40#include <usb_iface.h>
41#include <usb/ddfiface.h>
42#include <usb/usbdrv.h>
43#include <usb/descriptor.h>
44#include <usb/recognise.h>
45#include <usb/devreq.h>
46#include <usb/classes/hub.h>
47
48#include "usbhub.h"
49#include "usbhub_private.h"
50#include "port_status.h"
51#include "usb/usb.h"
52
53static device_ops_t hub_device_ops = {
54 .interfaces[USB_DEV_IFACE] = &usb_iface_hub_impl
55};
56
57//*********************************************
58//
59// hub driver code, initialization
60//
61//*********************************************
62
63usb_hub_info_t * usb_create_hub_info(device_t * device, int hc) {
64 usb_hub_info_t* result = usb_new(usb_hub_info_t);
65 //result->device = device;
66 result->port_count = -1;
67 /// \TODO is this correct? is the device stored?
68 result->device = device;
69
70
71 dprintf(USB_LOG_LEVEL_DEBUG, "phone to hc = %d", hc);
72 if (hc < 0) {
73 return result;
74 }
75 //get some hub info
76 usb_address_t addr = usb_drv_get_my_address(hc, device);
77 dprintf(USB_LOG_LEVEL_DEBUG, "address of newly created hub = %d", addr);
78 /*if(addr<0){
79 //return result;
80
81 }*/
82
83 result->address = addr;
84
85 // get hub descriptor
86
87 dprintf(USB_LOG_LEVEL_DEBUG, "creating serialized descripton");
88 void * serialized_descriptor = malloc(USB_HUB_MAX_DESCRIPTOR_SIZE);
89 usb_hub_descriptor_t * descriptor;
90 size_t received_size;
91 int opResult;
92 dprintf(USB_LOG_LEVEL_DEBUG, "starting control transaction");
93
94 opResult = usb_drv_req_get_descriptor(hc, addr,
95 USB_REQUEST_TYPE_CLASS,
96 USB_DESCTYPE_HUB, 0, 0, serialized_descriptor,
97 USB_HUB_MAX_DESCRIPTOR_SIZE, &received_size);
98
99 if (opResult != EOK) {
100 dprintf(USB_LOG_LEVEL_ERROR, "failed when receiving hub descriptor, badcode = %d",opResult);
101 free(serialized_descriptor);
102 return result;
103 }
104 dprintf(USB_LOG_LEVEL_DEBUG2, "deserializing descriptor");
105 descriptor = usb_deserialize_hub_desriptor(serialized_descriptor);
106 if(descriptor==NULL){
107 dprintf(USB_LOG_LEVEL_WARNING, "could not deserialize descriptor ");
108 result->port_count = 1;///\TODO this code is only for debug!!!
109 return result;
110 }
111 dprintf(USB_LOG_LEVEL_INFO, "setting port count to %d",descriptor->ports_count);
112 result->port_count = descriptor->ports_count;
113 result->attached_devs = (usb_hub_attached_device_t*)
114 malloc((result->port_count+1) * sizeof(usb_hub_attached_device_t));
115 int i;
116 for(i=0;i<result->port_count+1;++i){
117 result->attached_devs[i].devman_handle=0;
118 result->attached_devs[i].address=0;
119 }
120 dprintf(USB_LOG_LEVEL_DEBUG2, "freeing data");
121 free(serialized_descriptor);
122 free(descriptor->devices_removable);
123 free(descriptor);
124
125 //finish
126
127 dprintf(USB_LOG_LEVEL_INFO, "hub info created");
128
129 return result;
130}
131
132int usb_add_hub_device(device_t *dev) {
133 dprintf(USB_LOG_LEVEL_INFO, "add_hub_device(handle=%d)", (int) dev->handle);
134
135 /*
136 * We are some (probably deeply nested) hub.
137 * Thus, assign our own operations and explore already
138 * connected devices.
139 */
140 dev->ops = &hub_device_ops;
141
142 //create the hub structure
143 //get hc connection
144 int hc = usb_drv_hc_connect_auto(dev, 0);
145 if (hc < 0) {
146 return hc;
147 }
148
149 usb_hub_info_t * hub_info = usb_create_hub_info(dev, hc);
150 int port;
151 int opResult;
152 usb_target_t target;
153 target.address = hub_info->address;
154 target.endpoint = 0;
155
156 //get configuration descriptor
157 // this is not fully correct - there are more configurations
158 // and all should be checked
159 usb_standard_device_descriptor_t std_descriptor;
160 opResult = usb_drv_req_get_device_descriptor(hc, target.address,
161 &std_descriptor);
162 if(opResult!=EOK){
163 dprintf(USB_LOG_LEVEL_ERROR, "could not get device descriptor, %d",opResult);
164 return opResult;
165 }
166 dprintf(USB_LOG_LEVEL_INFO, "hub has %d configurations",std_descriptor.configuration_count);
167 if(std_descriptor.configuration_count<1){
168 dprintf(USB_LOG_LEVEL_ERROR, "THERE ARE NO CONFIGURATIONS AVAILABLE");
169 //shouldn`t I return?
170 }
171 /// \TODO check other configurations
172 usb_standard_configuration_descriptor_t config_descriptor;
173 opResult = usb_drv_req_get_bare_configuration_descriptor(hc,
174 target.address, 0,
175 &config_descriptor);
176 if(opResult!=EOK){
177 dprintf(USB_LOG_LEVEL_ERROR, "could not get configuration descriptor, %d",opResult);
178 return opResult;
179 }
180 //set configuration
181 opResult = usb_drv_req_set_configuration(hc, target.address,
182 config_descriptor.configuration_number);
183
184 if (opResult != EOK) {
185 dprintf(USB_LOG_LEVEL_ERROR, "something went wrong when setting hub`s configuration, %d", opResult);
186 }
187
188 usb_device_request_setup_packet_t request;
189 for (port = 1; port < hub_info->port_count+1; ++port) {
190 usb_hub_set_power_port_request(&request, port);
191 opResult = usb_drv_sync_control_write(hc, target, &request, NULL, 0);
192 dprintf(USB_LOG_LEVEL_INFO, "powering port %d",port);
193 if (opResult != EOK) {
194 dprintf(USB_LOG_LEVEL_WARNING, "something went wrong when setting hub`s %dth port", port);
195 }
196 }
197 //ports powered, hub seems to be enabled
198
199 async_hangup(hc);
200
201 //add the hub to list
202 fibril_mutex_lock(&usb_hub_list_lock);
203 usb_lst_append(&usb_hub_list, hub_info);
204 fibril_mutex_unlock(&usb_hub_list_lock);
205
206 dprintf(USB_LOG_LEVEL_DEBUG, "hub info added to list");
207 //(void)hub_info;
208 usb_hub_check_hub_changes();
209
210
211
212 dprintf(USB_LOG_LEVEL_INFO, "hub dev added");
213 dprintf(USB_LOG_LEVEL_DEBUG, "\taddress %d, has %d ports ",
214 hub_info->address,
215 hub_info->port_count);
216 dprintf(USB_LOG_LEVEL_DEBUG, "\tused configuration %d",config_descriptor.configuration_number);
217
218 return EOK;
219 //return ENOTSUP;
220}
221
222
223//*********************************************
224//
225// hub driver code, main loop
226//
227//*********************************************
228
229/**
230 * Convenience function for releasing default address and writing debug info
231 * (these few lines are used too often to be written again and again).
232 * @param hc
233 * @return
234 */
235inline static int usb_hub_release_default_address(int hc){
236 int opResult;
237 dprintf(USB_LOG_LEVEL_INFO, "releasing default address");
238 opResult = usb_drv_release_default_address(hc);
239 if (opResult != EOK) {
240 dprintf(USB_LOG_LEVEL_WARNING, "failed to release default address");
241 }
242 return opResult;
243}
244
245/**
246 * Reset the port with new device and reserve the default address.
247 * @param hc
248 * @param port
249 * @param target
250 */
251static void usb_hub_init_add_device(int hc, uint16_t port, usb_target_t target) {
252 usb_device_request_setup_packet_t request;
253 int opResult;
254 dprintf(USB_LOG_LEVEL_INFO, "some connection changed");
255 //get default address
256 opResult = usb_drv_reserve_default_address(hc);
257 if (opResult != EOK) {
258 dprintf(USB_LOG_LEVEL_WARNING, "cannot assign default address, it is probably used");
259 return;
260 }
261 //reset port
262 usb_hub_set_reset_port_request(&request, port);
263 opResult = usb_drv_sync_control_write(
264 hc, target,
265 &request,
266 NULL, 0
267 );
268 if (opResult != EOK) {
269 dprintf(USB_LOG_LEVEL_ERROR, "something went wrong when reseting a port");
270 usb_hub_release_default_address(hc);
271 }
272}
273
274/**
275 * Finalize adding new device after port reset
276 * @param hc
277 * @param port
278 * @param target
279 */
280static void usb_hub_finalize_add_device( usb_hub_info_t * hub,
281 int hc, uint16_t port, usb_target_t target) {
282
283 int opResult;
284 dprintf(USB_LOG_LEVEL_INFO, "finalizing add device");
285 opResult = usb_hub_clear_port_feature(hc, target.address,
286 port, USB_HUB_FEATURE_C_PORT_RESET);
287 if (opResult != EOK) {
288 dprintf(USB_LOG_LEVEL_ERROR, "failed to clear port reset feature");
289 usb_hub_release_default_address(hc);
290 return;
291 }
292
293 /* Request address at from host controller. */
294 usb_address_t new_device_address = usb_drv_request_address(hc);
295 if (new_device_address < 0) {
296 dprintf(USB_LOG_LEVEL_ERROR, "failed to get free USB address");
297 opResult = new_device_address;
298 usb_hub_release_default_address(hc);
299 return;
300 }
301 dprintf(USB_LOG_LEVEL_INFO, "setting new address %d",new_device_address);
302 opResult = usb_drv_req_set_address(hc, USB_ADDRESS_DEFAULT,
303 new_device_address);
304
305 if (opResult != EOK) {
306 dprintf(USB_LOG_LEVEL_ERROR, "could not set address for new device");
307 usb_hub_release_default_address(hc);
308 return;
309 }
310
311
312 opResult = usb_hub_release_default_address(hc);
313 if(opResult!=EOK){
314 return;
315 }
316
317 devman_handle_t hc_handle;
318 opResult = usb_drv_find_hc(hub->device, &hc_handle);
319 if (opResult != EOK) {
320 usb_log_error("Failed to get handle of host controller: %s.\n",
321 str_error(opResult));
322 return;
323 }
324
325 devman_handle_t child_handle;
326 opResult = usb_device_register_child_in_devman(new_device_address,
327 hc_handle, hub->device, &child_handle);
328 if (opResult != EOK) {
329 dprintf(USB_LOG_LEVEL_ERROR, "could not start driver for new device");
330 return;
331 }
332 hub->attached_devs[port].devman_handle = child_handle;
333 hub->attached_devs[port].address = new_device_address;
334
335 opResult = usb_drv_bind_address(hc, new_device_address, child_handle);
336 if (opResult != EOK) {
337 dprintf(USB_LOG_LEVEL_ERROR, "could not assign address of device in hcd");
338 return;
339 }
340 dprintf(USB_LOG_LEVEL_INFO, "new device address %d, handle %zu",
341 new_device_address, child_handle);
342
343}
344
345/**
346 * Unregister device address in hc
347 * @param hc
348 * @param port
349 * @param target
350 */
351static void usb_hub_removed_device(
352 usb_hub_info_t * hub, int hc, uint16_t port, usb_target_t target) {
353 //usb_device_request_setup_packet_t request;
354 int opResult;
355
356 /** \TODO remove device from device manager - not yet implemented in
357 * devide manager
358 */
359
360 hub->attached_devs[port].devman_handle=0;
361 //close address
362 if(hub->attached_devs[port].address!=0){
363 opResult = usb_drv_release_address(hc,hub->attached_devs[port].address);
364 if(opResult != EOK) {
365 dprintf(USB_LOG_LEVEL_WARNING, "could not release address of " \
366 "removed device: %d", opResult);
367 }
368 hub->attached_devs[port].address = 0;
369 }else{
370 dprintf(USB_LOG_LEVEL_WARNING, "this is strange, disconnected device had no address");
371 //device was disconnected before it`s port was reset - return default address
372 usb_drv_release_default_address(hc);
373 }
374}
375
376/**
377 * Process interrupts on given hub port
378 * @param hc
379 * @param port
380 * @param target
381 */
382static void usb_hub_process_interrupt(usb_hub_info_t * hub, int hc,
383 uint16_t port, usb_address_t address) {
384 dprintf(USB_LOG_LEVEL_DEBUG, "interrupt at port %d", port);
385 //determine type of change
386 usb_target_t target;
387 target.address=address;
388 target.endpoint=0;
389 usb_port_status_t status;
390 size_t rcvd_size;
391 usb_device_request_setup_packet_t request;
392 int opResult;
393 usb_hub_set_port_status_request(&request, port);
394 //endpoint 0
395
396 opResult = usb_drv_sync_control_read(
397 hc, target,
398 &request,
399 &status, 4, &rcvd_size
400 );
401 if (opResult != EOK) {
402 dprintf(USB_LOG_LEVEL_ERROR, "ERROR: could not get port status");
403 return;
404 }
405 if (rcvd_size != sizeof (usb_port_status_t)) {
406 dprintf(USB_LOG_LEVEL_ERROR, "ERROR: received status has incorrect size");
407 return;
408 }
409 //something connected/disconnected
410 if (usb_port_connect_change(&status)) {
411 opResult = usb_hub_clear_port_feature(hc, target.address,
412 port, USB_HUB_FEATURE_C_PORT_CONNECTION);
413 // TODO: check opResult
414 if (usb_port_dev_connected(&status)) {
415 dprintf(USB_LOG_LEVEL_INFO, "some connection changed");
416 usb_hub_init_add_device(hc, port, target);
417 } else {
418 usb_hub_removed_device(hub, hc, port, target);
419 }
420 }
421 //port reset
422 if (usb_port_reset_completed(&status)) {
423 dprintf(USB_LOG_LEVEL_INFO, "port reset complete");
424 if (usb_port_enabled(&status)) {
425 usb_hub_finalize_add_device(hub, hc, port, target);
426 } else {
427 dprintf(USB_LOG_LEVEL_WARNING, "ERROR: port reset, but port still not enabled");
428 }
429 }
430
431 usb_port_set_connect_change(&status, false);
432 usb_port_set_reset(&status, false);
433 usb_port_set_reset_completed(&status, false);
434 usb_port_set_dev_connected(&status, false);
435 if (status>>16) {
436 dprintf(USB_LOG_LEVEL_INFO, "there was some unsupported change on port %d: %X",port,status);
437
438 }
439 /// \TODO handle other changes
440 /// \TODO debug log for various situations
441
442}
443
444/**
445 * Check changes on all known hubs.
446 */
447void usb_hub_check_hub_changes(void) {
448 /*
449 * Iterate through all hubs.
450 */
451 usb_general_list_t * lst_item;
452 fibril_mutex_lock(&usb_hub_list_lock);
453 for (lst_item = usb_hub_list.next;
454 lst_item != &usb_hub_list;
455 lst_item = lst_item->next) {
456 fibril_mutex_unlock(&usb_hub_list_lock);
457 usb_hub_info_t * hub_info = ((usb_hub_info_t*)lst_item->data);
458 /*
459 * Check status change pipe of this hub.
460 */
461
462 usb_target_t target;
463 target.address = hub_info->address;
464 target.endpoint = 1;/// \TODO get from endpoint descriptor
465 dprintf(USB_LOG_LEVEL_INFO, "checking changes for hub at addr %d",
466 target.address);
467
468 size_t port_count = hub_info->port_count;
469
470 /*
471 * Connect to respective HC.
472 */
473 int hc = usb_drv_hc_connect_auto(hub_info->device, 0);
474 if (hc < 0) {
475 continue;
476 }
477
478 /// FIXME: count properly
479 size_t byte_length = ((port_count+1) / 8) + 1;
480
481 void *change_bitmap = malloc(byte_length);
482 size_t actual_size;
483 usb_handle_t handle;
484
485 /*
486 * Send the request.
487 */
488 int opResult = usb_drv_async_interrupt_in(hc, target,
489 change_bitmap, byte_length, &actual_size,
490 &handle);
491
492 usb_drv_async_wait_for(handle);
493
494 if (opResult != EOK) {
495 free(change_bitmap);
496 dprintf(USB_LOG_LEVEL_WARNING, "something went wrong while getting status of hub");
497 continue;
498 }
499 unsigned int port;
500 for (port = 1; port < port_count+1; ++port) {
501 bool interrupt =
502 (((uint8_t*) change_bitmap)[port / 8] >> (port % 8)) % 2;
503 if (interrupt) {
504 usb_hub_process_interrupt(
505 hub_info, hc, port, hub_info->address);
506 }
507 }
508 free(change_bitmap);
509
510 async_hangup(hc);
511 fibril_mutex_lock(&usb_hub_list_lock);
512 }
513 fibril_mutex_unlock(&usb_hub_list_lock);
514}
515
516
517
518
519
520/**
521 * @}
522 */
Note: See TracBrowser for help on using the repository browser.