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

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

USB drivers less verbose on info level

  • Property mode set to 100644
File size: 17.9 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 <ddf/driver.h>
36#include <bool.h>
37#include <errno.h>
38#include <str_error.h>
39
40#include <usb_iface.h>
41#include <usb/ddfiface.h>
42#include <usb/descriptor.h>
43#include <usb/recognise.h>
44#include <usb/request.h>
45#include <usb/classes/hub.h>
46#include <stdio.h>
47
48#include "usbhub.h"
49#include "usbhub_private.h"
50#include "port_status.h"
51#include "usb/usb.h"
52#include "usb/pipes.h"
53#include "usb/classes/classes.h"
54
55int usb_hub_control_loop(void * hub_info_param){
56 usb_hub_info_t * hub_info = (usb_hub_info_t*)hub_info_param;
57 int errorCode = EOK;
58
59 while(errorCode == EOK){
60 errorCode = usb_hub_check_hub_changes(hub_info);
61 async_usleep(1000 * 1000 );/// \TODO proper number once
62 }
63 usb_log_error("something in ctrl loop went wrong, errno %d\n",errorCode);
64
65 return 0;
66}
67
68
69//*********************************************
70//
71// hub driver code, initialization
72//
73//*********************************************
74
75/**
76 * create usb_hub_info_t structure
77 *
78 * Does only basic copying of known information into new structure.
79 * @param usb_dev usb device structure
80 * @return basic usb_hub_info_t structure
81 */
82static usb_hub_info_t * usb_hub_info_create(usb_device_t * usb_dev) {
83 usb_hub_info_t * result = usb_new(usb_hub_info_t);
84 if(!result) return NULL;
85 result->usb_device = usb_dev;
86 result->status_change_pipe = usb_dev->pipes[0].pipe;
87 result->control_pipe = &usb_dev->ctrl_pipe;
88 result->is_default_address_used = false;
89 return result;
90}
91
92/**
93 * Load hub-specific information into hub_info structure.
94 *
95 * Particularly read port count and initialize structure holding port
96 * information.
97 * This function is hub-specific and should be run only after the hub is
98 * configured using usb_hub_set_configuration function.
99 * @param hub_info pointer to structure with usb hub data
100 * @return error code
101 */
102static int usb_hub_get_hub_specific_info(usb_hub_info_t * hub_info){
103 // get hub descriptor
104 usb_log_debug("creating serialized descriptor\n");
105 void * serialized_descriptor = malloc(USB_HUB_MAX_DESCRIPTOR_SIZE);
106 usb_hub_descriptor_t * descriptor;
107
108 /* this was one fix of some bug, should not be needed anymore
109 int opResult = usb_request_set_configuration(&result->endpoints.control, 1);
110 if(opResult!=EOK){
111 usb_log_error("could not set default configuration, errno %d",opResult);
112 return opResult;
113 }
114 */
115 size_t received_size;
116 int opResult = usb_request_get_descriptor(&hub_info->usb_device->ctrl_pipe,
117 USB_REQUEST_TYPE_CLASS, USB_REQUEST_RECIPIENT_DEVICE,
118 USB_DESCTYPE_HUB,
119 0, 0, serialized_descriptor,
120 USB_HUB_MAX_DESCRIPTOR_SIZE, &received_size);
121
122 if (opResult != EOK) {
123 usb_log_error("failed when receiving hub descriptor, badcode = %d\n",
124 opResult);
125 free(serialized_descriptor);
126 return opResult;
127 }
128 usb_log_debug2("deserializing descriptor\n");
129 descriptor = usb_deserialize_hub_desriptor(serialized_descriptor);
130 if(descriptor==NULL){
131 usb_log_warning("could not deserialize descriptor \n");
132 return opResult;
133 }
134 usb_log_debug("setting port count to %d\n",descriptor->ports_count);
135 hub_info->port_count = descriptor->ports_count;
136 hub_info->attached_devs = (usb_hc_attached_device_t*)
137 malloc((hub_info->port_count+1) * sizeof(usb_hc_attached_device_t));
138 int i;
139 for(i=0;i<hub_info->port_count+1;++i){
140 hub_info->attached_devs[i].handle=0;
141 hub_info->attached_devs[i].address=0;
142 }
143 usb_log_debug2("freeing data\n");
144 free(serialized_descriptor);
145 free(descriptor->devices_removable);
146 free(descriptor);
147 return EOK;
148}
149/**
150 * Set configuration of hub
151 *
152 * Check whether there is at least one configuration and sets the first one.
153 * This function should be run prior to running any hub-specific action.
154 * @param hub_info
155 * @return
156 */
157static int usb_hub_set_configuration(usb_hub_info_t * hub_info){
158 //device descriptor
159 usb_standard_device_descriptor_t *std_descriptor
160 = &hub_info->usb_device->descriptors.device;
161 usb_log_debug("hub has %d configurations\n",
162 std_descriptor->configuration_count);
163 if(std_descriptor->configuration_count<1){
164 usb_log_error("THERE ARE NO CONFIGURATIONS AVAILABLE\n");
165 //shouldn`t I return?
166 //definitely
167 return EINVAL;
168 }
169
170 usb_standard_configuration_descriptor_t *config_descriptor
171 = (usb_standard_configuration_descriptor_t *)
172 hub_info->usb_device->descriptors.configuration;
173
174 /* Set configuration. */
175 int opResult = usb_request_set_configuration(
176 &hub_info->usb_device->ctrl_pipe,
177 config_descriptor->configuration_number);
178
179 if (opResult != EOK) {
180 usb_log_error("Failed to set hub configuration: %s.\n",
181 str_error(opResult));
182 return opResult;
183 }
184 usb_log_debug("\tused configuration %d\n",
185 config_descriptor->configuration_number);
186
187 return EOK;
188}
189
190/**
191 * Initialize hub device driver fibril
192 *
193 * Creates hub representation and fibril that periodically checks hub`s status.
194 * Hub representation is passed to the fibril.
195 * @param usb_dev generic usb device information
196 * @return error code
197 */
198int usb_hub_add_device(usb_device_t * usb_dev){
199 if(!usb_dev) return EINVAL;
200 usb_hub_info_t * hub_info = usb_hub_info_create(usb_dev);
201 //create hc connection
202 usb_log_debug("Initializing USB wire abstraction.\n");
203 int opResult = usb_hc_connection_initialize_from_device(
204 &hub_info->connection,
205 hub_info->usb_device->ddf_dev);
206 if(opResult != EOK){
207 usb_log_error("could not initialize connection to device, errno %d\n",
208 opResult);
209 free(hub_info);
210 return opResult;
211 }
212
213 usb_pipe_start_session(hub_info->control_pipe);
214 //set hub configuration
215 opResult = usb_hub_set_configuration(hub_info);
216 if(opResult!=EOK){
217 usb_log_error("could not set hub configuration, errno %d\n",opResult);
218 free(hub_info);
219 return opResult;
220 }
221 //get port count and create attached_devs
222 opResult = usb_hub_get_hub_specific_info(hub_info);
223 if(opResult!=EOK){
224 usb_log_error("could not set hub configuration, errno %d\n",opResult);
225 free(hub_info);
226 return opResult;
227 }
228 usb_pipe_end_session(hub_info->control_pipe);
229
230
231 /// \TODO what is this?
232 usb_log_debug("Creating `hub' function.\n");
233 ddf_fun_t *hub_fun = ddf_fun_create(hub_info->usb_device->ddf_dev,
234 fun_exposed, "hub");
235 assert(hub_fun != NULL);
236 hub_fun->ops = NULL;
237
238 int rc = ddf_fun_bind(hub_fun);
239 assert(rc == EOK);
240 rc = ddf_fun_add_to_class(hub_fun, "hub");
241 assert(rc == EOK);
242
243 //create fibril for the hub control loop
244 fid_t fid = fibril_create(usb_hub_control_loop, hub_info);
245 if (fid == 0) {
246 usb_log_error("failed to start monitoring fibril for new hub.\n");
247 return ENOMEM;
248 }
249 fibril_add_ready(fid);
250 usb_log_debug("Hub fibril created.\n");
251
252 usb_log_info("Controlling hub `%s' (%d ports).\n",
253 hub_info->usb_device->ddf_dev->name, hub_info->port_count);
254 return EOK;
255}
256
257
258//*********************************************
259//
260// hub driver code, main loop
261//
262//*********************************************
263
264/**
265 * release default address used by given hub
266 *
267 * Also unsets hub->is_default_address_used. Convenience wrapper function.
268 * @note hub->connection MUST be open for communication
269 * @param hub hub representation
270 * @return error code
271 */
272static int usb_hub_release_default_address(usb_hub_info_t * hub){
273 int opResult = usb_hc_release_default_address(&hub->connection);
274 if(opResult!=EOK){
275 usb_log_error("could not release default address, errno %d\n",opResult);
276 return opResult;
277 }
278 hub->is_default_address_used = false;
279 return EOK;
280}
281
282/**
283 * Reset the port with new device and reserve the default address.
284 * @param hc
285 * @param port
286 * @param target
287 */
288static void usb_hub_init_add_device(usb_hub_info_t * hub, uint16_t port,
289 usb_speed_t speed) {
290 //if this hub already uses default address, it cannot request it once more
291 if(hub->is_default_address_used) return;
292 usb_log_debug("some connection changed\n");
293 assert(hub->control_pipe->hc_phone);
294 int opResult = usb_hub_clear_port_feature(hub->control_pipe,
295 port, USB_HUB_FEATURE_C_PORT_CONNECTION);
296 if(opResult != EOK){
297 usb_log_warning("could not clear port-change-connection flag\n");
298 }
299 usb_device_request_setup_packet_t request;
300
301 //get default address
302 opResult = usb_hc_reserve_default_address(&hub->connection, speed);
303
304 if (opResult != EOK) {
305 usb_log_warning("cannot assign default address, it is probably used %d\n",
306 opResult);
307 return;
308 }
309 hub->is_default_address_used = true;
310 //reset port
311 usb_hub_set_reset_port_request(&request, port);
312 opResult = usb_pipe_control_write(
313 hub->control_pipe,
314 &request,sizeof(usb_device_request_setup_packet_t),
315 NULL, 0
316 );
317 if (opResult != EOK) {
318 usb_log_error("something went wrong when reseting a port %d\n",opResult);
319 //usb_hub_release_default_address(hc);
320 usb_hub_release_default_address(hub);
321 }
322 return;
323}
324
325/**
326 * Finalize adding new device after port reset
327 * @param hc
328 * @param port
329 * @param target
330 */
331static void usb_hub_finalize_add_device( usb_hub_info_t * hub,
332 uint16_t port, usb_speed_t speed) {
333
334 int opResult;
335 usb_log_debug("finalizing add device\n");
336 opResult = usb_hub_clear_port_feature(hub->control_pipe,
337 port, USB_HUB_FEATURE_C_PORT_RESET);
338
339 if (opResult != EOK) {
340 usb_log_error("failed to clear port reset feature\n");
341 usb_hub_release_default_address(hub);
342 return;
343 }
344 //create connection to device
345 usb_pipe_t new_device_pipe;
346 usb_device_connection_t new_device_connection;
347 usb_device_connection_initialize_on_default_address(
348 &new_device_connection,
349 &hub->connection
350 );
351 usb_pipe_initialize_default_control(
352 &new_device_pipe,
353 &new_device_connection);
354 usb_pipe_probe_default_control(&new_device_pipe);
355
356 /* Request address from host controller. */
357 usb_address_t new_device_address = usb_hc_request_address(
358 &hub->connection,
359 speed
360 );
361 if (new_device_address < 0) {
362 usb_log_error("failed to get free USB address\n");
363 opResult = new_device_address;
364 usb_hub_release_default_address(hub);
365 return;
366 }
367 usb_log_debug("setting new address %d\n",new_device_address);
368 //opResult = usb_drv_req_set_address(hc, USB_ADDRESS_DEFAULT,
369 // new_device_address);
370 usb_pipe_start_session(&new_device_pipe);
371 opResult = usb_request_set_address(&new_device_pipe,new_device_address);
372 usb_pipe_end_session(&new_device_pipe);
373 if (opResult != EOK) {
374 usb_log_error("could not set address for new device %d\n",opResult);
375 usb_hub_release_default_address(hub);
376 return;
377 }
378
379
380 //opResult = usb_hub_release_default_address(hc);
381 opResult = usb_hub_release_default_address(hub);
382 if(opResult!=EOK){
383 return;
384 }
385
386 devman_handle_t child_handle;
387 //??
388 opResult = usb_device_register_child_in_devman(new_device_address,
389 hub->connection.hc_handle, hub->usb_device->ddf_dev, &child_handle,
390 NULL, NULL, NULL);
391
392 if (opResult != EOK) {
393 usb_log_error("could not start driver for new device %d\n",opResult);
394 return;
395 }
396 hub->attached_devs[port].handle = child_handle;
397 hub->attached_devs[port].address = new_device_address;
398
399 //opResult = usb_drv_bind_address(hc, new_device_address, child_handle);
400 opResult = usb_hc_register_device(
401 &hub->connection,
402 &hub->attached_devs[port]);
403 if (opResult != EOK) {
404 usb_log_error("could not assign address of device in hcd %d\n",opResult);
405 return;
406 }
407 usb_log_info("Detected new device on `%s' (port %d), " \
408 "address %d (handle %llu).\n",
409 hub->usb_device->ddf_dev->name, (int) port,
410 new_device_address, child_handle);
411}
412
413/**
414 * Unregister device address in hc
415 * @param hc
416 * @param port
417 * @param target
418 */
419static void usb_hub_removed_device(
420 usb_hub_info_t * hub,uint16_t port) {
421
422 int opResult = usb_hub_clear_port_feature(hub->control_pipe,
423 port, USB_HUB_FEATURE_C_PORT_CONNECTION);
424 if(opResult != EOK){
425 usb_log_warning("could not clear port-change-connection flag\n");
426 }
427 /** \TODO remove device from device manager - not yet implemented in
428 * devide manager
429 */
430
431 //close address
432 if(hub->attached_devs[port].address!=0){
433 /*uncomment this code to use it when DDF allows device removal
434 opResult = usb_hc_unregister_device(
435 &hub->connection, hub->attached_devs[port].address);
436 if(opResult != EOK) {
437 dprintf(USB_LOG_LEVEL_WARNING, "could not release address of " \
438 "removed device: %d", opResult);
439 }
440 hub->attached_devs[port].address = 0;
441 hub->attached_devs[port].handle = 0;
442 */
443 }else{
444 usb_log_warning("this is strange, disconnected device had no address\n");
445 //device was disconnected before it`s port was reset - return default address
446 usb_hub_release_default_address(hub);
447 }
448}
449
450
451/**
452 * Process over current condition on port.
453 *
454 * Turn off the power on the port.
455 *
456 * @param hub
457 * @param port
458 */
459static void usb_hub_over_current( usb_hub_info_t * hub,
460 uint16_t port){
461 int opResult;
462 opResult = usb_hub_clear_port_feature(hub->control_pipe,
463 port, USB_HUB_FEATURE_PORT_POWER);
464 if(opResult!=EOK){
465 usb_log_error("cannot power off port %d; %d\n",
466 port, opResult);
467 }
468}
469
470/**
471 * Process interrupts on given hub port
472 * @param hc
473 * @param port
474 * @param target
475 */
476static void usb_hub_process_interrupt(usb_hub_info_t * hub,
477 uint16_t port) {
478 usb_log_debug("interrupt at port %d\n", port);
479 //determine type of change
480 usb_pipe_t *pipe = hub->control_pipe;
481
482 int opResult;
483
484 usb_port_status_t status;
485 size_t rcvd_size;
486 usb_device_request_setup_packet_t request;
487 //int opResult;
488 usb_hub_set_port_status_request(&request, port);
489 //endpoint 0
490
491 opResult = usb_pipe_control_read(
492 pipe,
493 &request, sizeof(usb_device_request_setup_packet_t),
494 &status, 4, &rcvd_size
495 );
496 if (opResult != EOK) {
497 usb_log_error("could not get port status\n");
498 return;
499 }
500 if (rcvd_size != sizeof (usb_port_status_t)) {
501 usb_log_error("received status has incorrect size\n");
502 return;
503 }
504 //something connected/disconnected
505 if (usb_port_connect_change(&status)) {
506 if (usb_port_dev_connected(&status)) {
507 usb_log_debug("some connection changed\n");
508 usb_hub_init_add_device(hub, port, usb_port_speed(&status));
509 } else {
510 usb_hub_removed_device(hub, port);
511 }
512 }
513 //over current
514 if (usb_port_overcurrent_change(&status)) {
515 //check if it was not auto-resolved
516 if(usb_port_over_current(&status)){
517 usb_hub_over_current(hub,port);
518 }else{
519 usb_log_debug("over current condition was auto-resolved on port %d\n",
520 port);
521 }
522 }
523 //port reset
524 if (usb_port_reset_completed(&status)) {
525 usb_log_debug("port reset complete\n");
526 if (usb_port_enabled(&status)) {
527 usb_hub_finalize_add_device(hub, port, usb_port_speed(&status));
528 } else {
529 usb_log_warning("port reset, but port still not enabled\n");
530 }
531 }
532
533 usb_port_set_connect_change(&status, false);
534 usb_port_set_reset(&status, false);
535 usb_port_set_reset_completed(&status, false);
536 usb_port_set_dev_connected(&status, false);
537 if (status>>16) {
538 usb_log_info("there was some unsupported change on port %d: %X\n",
539 port,status);
540
541 }
542}
543
544/**
545 * Check changes on particular hub
546 * @param hub_info_param pointer to usb_hub_info_t structure
547 * @return error code if there is problem when initializing communication with
548 * hub, EOK otherwise
549 */
550int usb_hub_check_hub_changes(usb_hub_info_t * hub_info){
551 int opResult;
552 opResult = usb_pipe_start_session(
553 hub_info->status_change_pipe);
554 if(opResult != EOK){
555 usb_log_error("could not initialize communication for hub; %d\n",
556 opResult);
557 return opResult;
558 }
559
560 size_t port_count = hub_info->port_count;
561
562 /// FIXME: count properly
563 size_t byte_length = ((port_count+1) / 8) + 1;
564 void *change_bitmap = malloc(byte_length);
565 size_t actual_size;
566
567 /*
568 * Send the request.
569 */
570 opResult = usb_pipe_read(
571 hub_info->status_change_pipe,
572 change_bitmap, byte_length, &actual_size
573 );
574
575 if (opResult != EOK) {
576 free(change_bitmap);
577 usb_log_warning("something went wrong while getting status of hub\n");
578 usb_pipe_end_session(hub_info->status_change_pipe);
579 return opResult;
580 }
581 unsigned int port;
582 opResult = usb_pipe_start_session(hub_info->control_pipe);
583 if(opResult!=EOK){
584 usb_log_error("could not start control pipe session %d\n", opResult);
585 usb_pipe_end_session(hub_info->status_change_pipe);
586 return opResult;
587 }
588 opResult = usb_hc_connection_open(&hub_info->connection);
589 if(opResult!=EOK){
590 usb_log_error("could not start host controller session %d\n",
591 opResult);
592 usb_pipe_end_session(hub_info->control_pipe);
593 usb_pipe_end_session(hub_info->status_change_pipe);
594 return opResult;
595 }
596
597 ///todo, opresult check, pre obe konekce
598 for (port = 1; port < port_count+1; ++port) {
599 bool interrupt =
600 (((uint8_t*) change_bitmap)[port / 8] >> (port % 8)) % 2;
601 if (interrupt) {
602 usb_hub_process_interrupt(
603 hub_info, port);
604 }
605 }
606 usb_hc_connection_close(&hub_info->connection);
607 usb_pipe_end_session(hub_info->control_pipe);
608 usb_pipe_end_session(hub_info->status_change_pipe);
609 free(change_bitmap);
610 return EOK;
611}
612
613
614
615/**
616 * @}
617 */
Note: See TracBrowser for help on using the repository browser.