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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since fa8f1f7 was 09daa8b, checked in by Matus Dekanek <smekideki@…>, 15 years ago

using usb/devdrv interface to simplify hub communication initialization
small code correction in devdrv.c and devdrv.h

  • Property mode set to 100644
File size: 18.4 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_info("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 int opResult = usb_request_get_device_descriptor(
161 &hub_info->usb_device->ctrl_pipe,
162 &std_descriptor);
163 if(opResult!=EOK){
164 usb_log_error("could not get device descriptor, %d\n",opResult);
165 return opResult;
166 }
167 usb_log_info("hub has %d configurations\n",
168 std_descriptor.configuration_count);
169 if(std_descriptor.configuration_count<1){
170 usb_log_error("THERE ARE NO CONFIGURATIONS AVAILABLE\n");
171 //shouldn`t I return?
172 }
173
174 /* Retrieve full configuration descriptor. */
175 uint8_t *descriptors = NULL;
176 size_t descriptors_size = 0;
177 opResult = usb_request_get_full_configuration_descriptor_alloc(
178 &hub_info->usb_device->ctrl_pipe, 0,
179 (void **) &descriptors, &descriptors_size);
180 if (opResult != EOK) {
181 usb_log_error("Could not get configuration descriptor: %s.\n",
182 str_error(opResult));
183 return opResult;
184 }
185 usb_standard_configuration_descriptor_t *config_descriptor
186 = (usb_standard_configuration_descriptor_t *) descriptors;
187
188 /* Set configuration. */
189 opResult = usb_request_set_configuration(&hub_info->usb_device->ctrl_pipe,
190 config_descriptor->configuration_number);
191
192 if (opResult != EOK) {
193 usb_log_error("Failed to set hub configuration: %s.\n",
194 str_error(opResult));
195 return opResult;
196 }
197 usb_log_debug("\tused configuration %d\n",
198 config_descriptor->configuration_number);
199 free(descriptors);
200 return EOK;
201}
202
203/**
204 * Initialize hub device driver fibril
205 *
206 * Creates hub representation and fibril that periodically checks hub`s status.
207 * Hub representation is passed to the fibril.
208 * @param usb_dev generic usb device information
209 * @return error code
210 */
211int usb_hub_add_device(usb_device_t * usb_dev){
212 if(!usb_dev) return EINVAL;
213 usb_hub_info_t * hub_info = usb_hub_info_create(usb_dev);
214 //create hc connection
215 usb_log_debug("Initializing USB wire abstraction.\n");
216 int opResult = usb_hc_connection_initialize_from_device(
217 &hub_info->connection,
218 hub_info->usb_device->ddf_dev);
219 if(opResult != EOK){
220 usb_log_error("could not initialize connection to device, errno %d\n",
221 opResult);
222 free(hub_info);
223 return opResult;
224 }
225
226 usb_endpoint_pipe_start_session(hub_info->control_pipe);
227 //set hub configuration
228 opResult = usb_hub_set_configuration(hub_info);
229 if(opResult!=EOK){
230 usb_log_error("could not set hub configuration, errno %d\n",opResult);
231 free(hub_info);
232 return opResult;
233 }
234 //get port count and create attached_devs
235 opResult = usb_hub_get_hub_specific_info(hub_info);
236 if(opResult!=EOK){
237 usb_log_error("could not set hub configuration, errno %d\n",opResult);
238 free(hub_info);
239 return opResult;
240 }
241 usb_endpoint_pipe_end_session(hub_info->control_pipe);
242
243
244 /// \TODO what is this?
245 usb_log_debug("adding to ddf");
246 ddf_fun_t *hub_fun = ddf_fun_create(hub_info->usb_device->ddf_dev,
247 fun_exposed, "hub");
248 assert(hub_fun != NULL);
249 hub_fun->ops = NULL;
250
251 int rc = ddf_fun_bind(hub_fun);
252 assert(rc == EOK);
253 rc = ddf_fun_add_to_class(hub_fun, "hub");
254 assert(rc == EOK);
255
256 //create fibril for the hub control loop
257 fid_t fid = fibril_create(usb_hub_control_loop, hub_info);
258 if (fid == 0) {
259 usb_log_error("failed to start monitoring fibril for new hub");
260 return ENOMEM;
261 }
262 fibril_add_ready(fid);
263 usb_log_debug("hub fibril created");
264 usb_log_debug("has %d ports ",hub_info->port_count);
265 return EOK;
266}
267
268
269//*********************************************
270//
271// hub driver code, main loop
272//
273//*********************************************
274
275/**
276 * release default address used by given hub
277 *
278 * Also unsets hub->is_default_address_used. Convenience wrapper function.
279 * @note hub->connection MUST be open for communication
280 * @param hub hub representation
281 * @return error code
282 */
283static int usb_hub_release_default_address(usb_hub_info_t * hub){
284 int opResult = usb_hc_release_default_address(&hub->connection);
285 if(opResult!=EOK){
286 usb_log_error("could not release default address, errno %d\n",opResult);
287 return opResult;
288 }
289 hub->is_default_address_used = false;
290 return EOK;
291}
292
293/**
294 * Reset the port with new device and reserve the default address.
295 * @param hc
296 * @param port
297 * @param target
298 */
299static void usb_hub_init_add_device(usb_hub_info_t * hub, uint16_t port,
300 usb_speed_t speed) {
301 //if this hub already uses default address, it cannot request it once more
302 if(hub->is_default_address_used) return;
303 usb_log_info("some connection changed\n");
304 assert(hub->control_pipe->hc_phone);
305 int opResult = usb_hub_clear_port_feature(hub->control_pipe,
306 port, USB_HUB_FEATURE_C_PORT_CONNECTION);
307 if(opResult != EOK){
308 usb_log_warning("could not clear port-change-connection flag\n");
309 }
310 usb_device_request_setup_packet_t request;
311
312 //get default address
313 opResult = usb_hc_reserve_default_address(&hub->connection, speed);
314
315 if (opResult != EOK) {
316 usb_log_warning("cannot assign default address, it is probably used %d\n",
317 opResult);
318 return;
319 }
320 hub->is_default_address_used = true;
321 //reset port
322 usb_hub_set_reset_port_request(&request, port);
323 opResult = usb_endpoint_pipe_control_write(
324 hub->control_pipe,
325 &request,sizeof(usb_device_request_setup_packet_t),
326 NULL, 0
327 );
328 if (opResult != EOK) {
329 usb_log_error("something went wrong when reseting a port %d\n",opResult);
330 //usb_hub_release_default_address(hc);
331 usb_hub_release_default_address(hub);
332 }
333 return;
334}
335
336/**
337 * Finalize adding new device after port reset
338 * @param hc
339 * @param port
340 * @param target
341 */
342static void usb_hub_finalize_add_device( usb_hub_info_t * hub,
343 uint16_t port, usb_speed_t speed) {
344
345 int opResult;
346 usb_log_info("finalizing add device\n");
347 opResult = usb_hub_clear_port_feature(hub->control_pipe,
348 port, USB_HUB_FEATURE_C_PORT_RESET);
349
350 if (opResult != EOK) {
351 usb_log_error("failed to clear port reset feature\n");
352 usb_hub_release_default_address(hub);
353 return;
354 }
355 //create connection to device
356 usb_endpoint_pipe_t new_device_pipe;
357 usb_device_connection_t new_device_connection;
358 usb_device_connection_initialize_on_default_address(
359 &new_device_connection,
360 &hub->connection
361 );
362 usb_endpoint_pipe_initialize_default_control(
363 &new_device_pipe,
364 &new_device_connection);
365 usb_endpoint_pipe_probe_default_control(&new_device_pipe);
366
367 /* Request address from host controller. */
368 usb_address_t new_device_address = usb_hc_request_address(
369 &hub->connection,
370 speed
371 );
372 if (new_device_address < 0) {
373 usb_log_error("failed to get free USB address\n");
374 opResult = new_device_address;
375 usb_hub_release_default_address(hub);
376 return;
377 }
378 usb_log_info("setting new address %d\n",new_device_address);
379 //opResult = usb_drv_req_set_address(hc, USB_ADDRESS_DEFAULT,
380 // new_device_address);
381 usb_endpoint_pipe_start_session(&new_device_pipe);
382 opResult = usb_request_set_address(&new_device_pipe,new_device_address);
383 usb_endpoint_pipe_end_session(&new_device_pipe);
384 if (opResult != EOK) {
385 usb_log_error("could not set address for new device %d\n",opResult);
386 usb_hub_release_default_address(hub);
387 return;
388 }
389
390
391 //opResult = usb_hub_release_default_address(hc);
392 opResult = usb_hub_release_default_address(hub);
393 if(opResult!=EOK){
394 return;
395 }
396
397 devman_handle_t child_handle;
398 //??
399 opResult = usb_device_register_child_in_devman(new_device_address,
400 hub->connection.hc_handle, hub->usb_device->ddf_dev, &child_handle,
401 NULL, NULL, NULL);
402
403 if (opResult != EOK) {
404 usb_log_error("could not start driver for new device %d\n",opResult);
405 return;
406 }
407 hub->attached_devs[port].handle = child_handle;
408 hub->attached_devs[port].address = new_device_address;
409
410 //opResult = usb_drv_bind_address(hc, new_device_address, child_handle);
411 opResult = usb_hc_register_device(
412 &hub->connection,
413 &hub->attached_devs[port]);
414 if (opResult != EOK) {
415 usb_log_error("could not assign address of device in hcd %d\n",opResult);
416 return;
417 }
418 usb_log_info("new device address %d, handle %zu\n",
419 new_device_address, child_handle);
420
421}
422
423/**
424 * Unregister device address in hc
425 * @param hc
426 * @param port
427 * @param target
428 */
429static void usb_hub_removed_device(
430 usb_hub_info_t * hub,uint16_t port) {
431
432 int opResult = usb_hub_clear_port_feature(hub->control_pipe,
433 port, USB_HUB_FEATURE_C_PORT_CONNECTION);
434 if(opResult != EOK){
435 usb_log_warning("could not clear port-change-connection flag\n");
436 }
437 /** \TODO remove device from device manager - not yet implemented in
438 * devide manager
439 */
440
441 //close address
442 if(hub->attached_devs[port].address!=0){
443 /*uncomment this code to use it when DDF allows device removal
444 opResult = usb_hc_unregister_device(
445 &hub->connection, hub->attached_devs[port].address);
446 if(opResult != EOK) {
447 dprintf(USB_LOG_LEVEL_WARNING, "could not release address of " \
448 "removed device: %d", opResult);
449 }
450 hub->attached_devs[port].address = 0;
451 hub->attached_devs[port].handle = 0;
452 */
453 }else{
454 usb_log_warning("this is strange, disconnected device had no address\n");
455 //device was disconnected before it`s port was reset - return default address
456 usb_hub_release_default_address(hub);
457 }
458}
459
460
461/**
462 * Process over current condition on port.
463 *
464 * Turn off the power on the port.
465 *
466 * @param hub
467 * @param port
468 */
469static void usb_hub_over_current( usb_hub_info_t * hub,
470 uint16_t port){
471 int opResult;
472 opResult = usb_hub_clear_port_feature(hub->control_pipe,
473 port, USB_HUB_FEATURE_PORT_POWER);
474 if(opResult!=EOK){
475 usb_log_error("cannot power off port %d; %d\n",
476 port, opResult);
477 }
478}
479
480/**
481 * Process interrupts on given hub port
482 * @param hc
483 * @param port
484 * @param target
485 */
486static void usb_hub_process_interrupt(usb_hub_info_t * hub,
487 uint16_t port) {
488 usb_log_debug("interrupt at port %d\n", port);
489 //determine type of change
490 usb_endpoint_pipe_t *pipe = hub->control_pipe;
491
492 int opResult;
493
494 usb_port_status_t status;
495 size_t rcvd_size;
496 usb_device_request_setup_packet_t request;
497 //int opResult;
498 usb_hub_set_port_status_request(&request, port);
499 //endpoint 0
500
501 opResult = usb_endpoint_pipe_control_read(
502 pipe,
503 &request, sizeof(usb_device_request_setup_packet_t),
504 &status, 4, &rcvd_size
505 );
506 if (opResult != EOK) {
507 usb_log_error("could not get port status\n");
508 return;
509 }
510 if (rcvd_size != sizeof (usb_port_status_t)) {
511 usb_log_error("received status has incorrect size\n");
512 return;
513 }
514 //something connected/disconnected
515 if (usb_port_connect_change(&status)) {
516 if (usb_port_dev_connected(&status)) {
517 usb_log_info("some connection changed\n");
518 usb_hub_init_add_device(hub, port, usb_port_speed(&status));
519 } else {
520 usb_hub_removed_device(hub, port);
521 }
522 }
523 //over current
524 if (usb_port_overcurrent_change(&status)) {
525 //check if it was not auto-resolved
526 if(usb_port_over_current(&status)){
527 usb_hub_over_current(hub,port);
528 }else{
529 usb_log_info("over current condition was auto-resolved on port %d\n",
530 port);
531 }
532 }
533 //port reset
534 if (usb_port_reset_completed(&status)) {
535 usb_log_info("port reset complete");
536 if (usb_port_enabled(&status)) {
537 usb_hub_finalize_add_device(hub, port, usb_port_speed(&status));
538 } else {
539 usb_log_warning("port reset, but port still not enabled\n");
540 }
541 }
542
543 usb_port_set_connect_change(&status, false);
544 usb_port_set_reset(&status, false);
545 usb_port_set_reset_completed(&status, false);
546 usb_port_set_dev_connected(&status, false);
547 if (status>>16) {
548 usb_log_info("there was some unsupported change on port %d: %X\n",
549 port,status);
550
551 }
552}
553
554/**
555 * Check changes on particular hub
556 * @param hub_info_param pointer to usb_hub_info_t structure
557 * @return error code if there is problem when initializing communication with
558 * hub, EOK otherwise
559 */
560int usb_hub_check_hub_changes(usb_hub_info_t * hub_info){
561 int opResult;
562 opResult = usb_endpoint_pipe_start_session(
563 hub_info->status_change_pipe);
564 if(opResult != EOK){
565 usb_log_error("could not initialize communication for hub; %d\n",
566 opResult);
567 return opResult;
568 }
569
570 size_t port_count = hub_info->port_count;
571
572 /// FIXME: count properly
573 size_t byte_length = ((port_count+1) / 8) + 1;
574 void *change_bitmap = malloc(byte_length);
575 size_t actual_size;
576
577 /*
578 * Send the request.
579 */
580 opResult = usb_endpoint_pipe_read(
581 hub_info->status_change_pipe,
582 change_bitmap, byte_length, &actual_size
583 );
584
585 if (opResult != EOK) {
586 free(change_bitmap);
587 usb_log_warning("something went wrong while getting status of hub\n");
588 usb_endpoint_pipe_end_session(hub_info->status_change_pipe);
589 return opResult;
590 }
591 unsigned int port;
592 opResult = usb_endpoint_pipe_start_session(hub_info->control_pipe);
593 if(opResult!=EOK){
594 usb_log_error("could not start control pipe session %d\n", opResult);
595 usb_endpoint_pipe_end_session(hub_info->status_change_pipe);
596 return opResult;
597 }
598 opResult = usb_hc_connection_open(&hub_info->connection);
599 if(opResult!=EOK){
600 usb_log_error("could not start host controller session %d\n",
601 opResult);
602 usb_endpoint_pipe_end_session(hub_info->control_pipe);
603 usb_endpoint_pipe_end_session(hub_info->status_change_pipe);
604 return opResult;
605 }
606
607 ///todo, opresult check, pre obe konekce
608 for (port = 1; port < port_count+1; ++port) {
609 bool interrupt =
610 (((uint8_t*) change_bitmap)[port / 8] >> (port % 8)) % 2;
611 if (interrupt) {
612 usb_hub_process_interrupt(
613 hub_info, port);
614 }
615 }
616 usb_hc_connection_close(&hub_info->connection);
617 usb_endpoint_pipe_end_session(hub_info->control_pipe);
618 usb_endpoint_pipe_end_session(hub_info->status_change_pipe);
619 free(change_bitmap);
620 return EOK;
621}
622
623
624
625/**
626 * @}
627 */
Note: See TracBrowser for help on using the repository browser.