source: mainline/uspace/drv/usbhub/usbhub.c@ 8f198c9

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

Merge mainline changes

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