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

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

ohci root hub implementation basis, missing descriptors and interrupt transfer

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