source: mainline/uspace/drv/usbhub/usbhub.c@ 2b0db98

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

Connecting to "parent" host controller

USB drivers can now connect to host controller drivers they physically
belong to (so far, everything connected to VHC).

Each USB device must implement the USB interface of libdrv to allow
this (it is absolutely neccesary for hubs).

USB drivers can use usb_drv_hc_connect() to connect to "their" host
controller.

Child devices created with usb_drv_register_child_in_devman()are set
to handle this connection automatically.

UHCI and VHC drivers were updated.

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