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

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

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