source: mainline/uspace/drv/usbhub/usbhub.c@ fa2f79d

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

Unused header removal from libusb

Another remainder of coupling hub and host controller drivers.

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