source: mainline/uspace/drv/usbhub/usbhub.c@ 4e8e1f5

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

Device recognition uses pipe API

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