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

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

Fix seizure of default address in unplugged hub

This revision shall fix following scenario

  • hub is attached to the host
  • second hub has several devices connected to it bus is not connected to the host
  • second hub is plugged to the first one
  • device discovery takes place
  • second hub is unplugged

Before this fix, the hub might fail to return the default address
because it might wait for ever for completion of port reset
(waiting for change on status pipe that would send no more data).

  • Property mode set to 100644
File size: 15.2 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#include <inttypes.h>
40
41#include <usb_iface.h>
42#include <usb/ddfiface.h>
43#include <usb/descriptor.h>
44#include <usb/dev/recognise.h>
45#include <usb/dev/request.h>
46#include <usb/classes/hub.h>
47#include <usb/dev/poll.h>
48#include <stdio.h>
49
50#include "usbhub.h"
51#include "usbhub_private.h"
52#include "port_status.h"
53#include <usb/usb.h>
54#include <usb/dev/pipes.h>
55#include <usb/classes/classes.h>
56
57
58static usb_hub_info_t * usb_hub_info_create(usb_device_t * usb_dev);
59
60static int usb_hub_process_hub_specific_info(usb_hub_info_t * hub_info);
61
62static int usb_hub_set_configuration(usb_hub_info_t * hub_info);
63
64static int usb_hub_start_hub_fibril(usb_hub_info_t * hub_info);
65
66static int usb_process_hub_over_current(usb_hub_info_t * hub_info,
67 usb_hub_status_t status);
68
69static int usb_process_hub_local_power_change(usb_hub_info_t * hub_info,
70 usb_hub_status_t status);
71
72static void usb_hub_process_global_interrupt(usb_hub_info_t * hub_info);
73
74static void usb_hub_polling_terminated_callback(usb_device_t * device,
75 bool was_error, void * data);
76
77
78//*********************************************
79//
80// hub driver code, initialization
81//
82//*********************************************
83
84/**
85 * Initialize hub device driver fibril
86 *
87 * Creates hub representation and fibril that periodically checks hub`s status.
88 * Hub representation is passed to the fibril.
89 * @param usb_dev generic usb device information
90 * @return error code
91 */
92int usb_hub_add_device(usb_device_t * usb_dev) {
93 if (!usb_dev) return EINVAL;
94 usb_hub_info_t * hub_info = usb_hub_info_create(usb_dev);
95 //create hc connection
96 usb_log_debug("Initializing USB wire abstraction.\n");
97 int opResult = usb_hc_connection_initialize_from_device(
98 &hub_info->connection,
99 hub_info->usb_device->ddf_dev);
100 if (opResult != EOK) {
101 usb_log_error("could not initialize connection to device, "
102 "errno %d\n",
103 opResult);
104 free(hub_info);
105 return opResult;
106 }
107
108 //set hub configuration
109 opResult = usb_hub_set_configuration(hub_info);
110 if (opResult != EOK) {
111 usb_log_error("could not set hub configuration, errno %d\n",
112 opResult);
113 free(hub_info);
114 return opResult;
115 }
116 //get port count and create attached_devs
117 opResult = usb_hub_process_hub_specific_info(hub_info);
118 if (opResult != EOK) {
119 usb_log_error("could process hub specific info, errno %d\n",
120 opResult);
121 free(hub_info);
122 return opResult;
123 }
124
125 usb_log_debug("Creating 'hub' function in DDF.\n");
126 ddf_fun_t *hub_fun = ddf_fun_create(hub_info->usb_device->ddf_dev,
127 fun_exposed, "hub");
128 assert(hub_fun != NULL);
129 hub_fun->ops = NULL;
130
131 opResult = ddf_fun_bind(hub_fun);
132 assert(opResult == EOK);
133 opResult = ddf_fun_add_to_class(hub_fun, "hub");
134 assert(opResult == EOK);
135
136 opResult = usb_hub_start_hub_fibril(hub_info);
137 if(opResult!=EOK)
138 free(hub_info);
139 return opResult;
140}
141
142
143/** Callback for polling hub for changes.
144 *
145 * @param dev Device where the change occured.
146 * @param change_bitmap Bitmap of changed ports.
147 * @param change_bitmap_size Size of the bitmap in bytes.
148 * @param arg Custom argument, points to @c usb_hub_info_t.
149 * @return Whether to continue polling.
150 */
151bool hub_port_changes_callback(usb_device_t *dev,
152 uint8_t *change_bitmap, size_t change_bitmap_size, void *arg) {
153 usb_log_debug("hub_port_changes_callback\n");
154 usb_hub_info_t *hub = (usb_hub_info_t *) arg;
155
156 /* FIXME: check that we received enough bytes. */
157 if (change_bitmap_size == 0) {
158 goto leave;
159 }
160
161 bool change;
162 change = ((uint8_t*) change_bitmap)[0] & 1;
163 if (change) {
164 usb_hub_process_global_interrupt(hub);
165 }
166
167 size_t port;
168 for (port = 1; port < hub->port_count + 1; port++) {
169 bool change = (change_bitmap[port / 8] >> (port % 8)) % 2;
170 if (change) {
171 usb_hub_process_interrupt(hub, port);
172 }
173 }
174leave:
175 /* FIXME: proper interval. */
176 async_usleep(1000 * 250);
177
178 return true;
179}
180
181
182//*********************************************
183//
184// support functions
185//
186//*********************************************
187
188/**
189 * create usb_hub_info_t structure
190 *
191 * Does only basic copying of known information into new structure.
192 * @param usb_dev usb device structure
193 * @return basic usb_hub_info_t structure
194 */
195static usb_hub_info_t * usb_hub_info_create(usb_device_t * usb_dev) {
196 usb_hub_info_t * result = malloc(sizeof(usb_hub_info_t));
197 if (!result) return NULL;
198 result->usb_device = usb_dev;
199 result->status_change_pipe = usb_dev->pipes[0].pipe;
200 result->control_pipe = &usb_dev->ctrl_pipe;
201 result->is_default_address_used = false;
202
203 result->ports = NULL;
204 result->port_count = (size_t) -1;
205 fibril_mutex_initialize(&result->port_mutex);
206
207 fibril_mutex_initialize(&result->pending_ops_mutex);
208 fibril_condvar_initialize(&result->pending_ops_cv);
209 result->pending_ops_count = 0;
210 return result;
211}
212
213/**
214 * Load hub-specific information into hub_info structure and process if needed
215 *
216 * Particularly read port count and initialize structure holding port
217 * information. If there are non-removable devices, start initializing them.
218 * This function is hub-specific and should be run only after the hub is
219 * configured using usb_hub_set_configuration function.
220 * @param hub_info hub representation
221 * @return error code
222 */
223static int usb_hub_process_hub_specific_info(usb_hub_info_t * hub_info) {
224 // get hub descriptor
225 usb_log_debug("creating serialized descriptor\n");
226 uint8_t serialized_descriptor[USB_HUB_MAX_DESCRIPTOR_SIZE];
227 usb_hub_descriptor_t * descriptor;
228 int opResult;
229
230 size_t received_size;
231 opResult = usb_request_get_descriptor(hub_info->control_pipe,
232 USB_REQUEST_TYPE_CLASS, USB_REQUEST_RECIPIENT_DEVICE,
233 USB_DESCTYPE_HUB, 0, 0, serialized_descriptor,
234 USB_HUB_MAX_DESCRIPTOR_SIZE, &received_size);
235
236 if (opResult != EOK) {
237 usb_log_error("failed when receiving hub descriptor, "
238 "badcode = %d\n",
239 opResult);
240 free(serialized_descriptor);
241 return opResult;
242 }
243 usb_log_debug2("deserializing descriptor\n");
244 descriptor = usb_create_deserialized_hub_desriptor(
245 serialized_descriptor);
246 if (descriptor == NULL) {
247 usb_log_warning("could not deserialize descriptor \n");
248 return ENOMEM;
249 }
250 usb_log_debug("setting port count to %d\n", descriptor->ports_count);
251 hub_info->port_count = descriptor->ports_count;
252 /// \TODO this is not semantically correct
253 bool is_power_switched =
254 ((descriptor->hub_characteristics & 1) ==0);
255 bool has_individual_port_powering =
256 ((descriptor->hub_characteristics & 1) !=0);
257 hub_info->ports = malloc(
258 sizeof (usb_hub_port_t) * (hub_info->port_count + 1));
259 if(!hub_info->ports){
260 return ENOMEM;
261 }
262 size_t port;
263 for (port = 0; port < hub_info->port_count + 1; ++port) {
264 usb_hub_port_init(&hub_info->ports[port]);
265 }
266 if(is_power_switched){
267 usb_log_debug("is_power_switched\n");
268
269 if(!has_individual_port_powering){
270 usb_log_debug("!has_individual_port_powering\n");
271 opResult = usb_hub_set_feature(hub_info->control_pipe,
272 USB_HUB_FEATURE_C_HUB_LOCAL_POWER);
273 if (opResult != EOK) {
274 usb_log_error("cannot power hub: %s\n",
275 str_error(opResult));
276 }
277 }
278
279 for (port = 1; port <= hub_info->port_count; ++port) {
280 usb_log_debug("Powering port %zu.\n",port);
281 opResult = usb_hub_set_port_feature(hub_info->control_pipe,
282 port, USB_HUB_FEATURE_PORT_POWER);
283 if (opResult != EOK) {
284 usb_log_error("cannot power on port %zu: %s.\n",
285 port, str_error(opResult));
286 }
287 }
288
289 }else{
290 usb_log_debug("!is_power_switched, not going to be powered\n");
291 }
292 usb_log_debug2("freeing data\n");
293 free(descriptor);
294 return EOK;
295}
296
297/**
298 * Set configuration of hub
299 *
300 * Check whether there is at least one configuration and sets the first one.
301 * This function should be run prior to running any hub-specific action.
302 * @param hub_info hub representation
303 * @return error code
304 */
305static int usb_hub_set_configuration(usb_hub_info_t * hub_info) {
306 //device descriptor
307 usb_standard_device_descriptor_t *std_descriptor
308 = &hub_info->usb_device->descriptors.device;
309 usb_log_debug("hub has %d configurations\n",
310 std_descriptor->configuration_count);
311 if (std_descriptor->configuration_count < 1) {
312 usb_log_error("there are no configurations available\n");
313 return EINVAL;
314 }
315
316 usb_standard_configuration_descriptor_t *config_descriptor
317 = (usb_standard_configuration_descriptor_t *)
318 hub_info->usb_device->descriptors.configuration;
319
320 /* Set configuration. */
321 int opResult = usb_request_set_configuration(
322 &hub_info->usb_device->ctrl_pipe,
323 config_descriptor->configuration_number);
324
325 if (opResult != EOK) {
326 usb_log_error("Failed to set hub configuration: %s.\n",
327 str_error(opResult));
328 return opResult;
329 }
330 usb_log_debug("\tused configuration %d\n",
331 config_descriptor->configuration_number);
332
333 return EOK;
334}
335
336/**
337 * create and start fibril with hub control loop
338 *
339 * Before the fibril is started, the control pipe and host controller
340 * connection of the hub is open.
341 *
342 * @param hub_info hub representing structure
343 * @return error code
344 */
345static int usb_hub_start_hub_fibril(usb_hub_info_t * hub_info){
346 int rc;
347
348 rc = usb_device_auto_poll(hub_info->usb_device, 0,
349 hub_port_changes_callback, ((hub_info->port_count + 1) / 8) + 1,
350 usb_hub_polling_terminated_callback, hub_info);
351 if (rc != EOK) {
352 usb_log_error("Failed to create polling fibril: %s.\n",
353 str_error(rc));
354 free(hub_info);
355 return rc;
356 }
357
358 usb_log_info("Controlling hub `%s' (%zu ports).\n",
359 hub_info->usb_device->ddf_dev->name, hub_info->port_count);
360 return EOK;
361}
362
363//*********************************************
364//
365// change handling functions
366//
367//*********************************************
368
369
370/**
371 * process hub over current change
372 *
373 * This means either to power off the hub or power it on.
374 * @param hub_info hub instance
375 * @param status hub status bitmask
376 * @return error code
377 */
378static int usb_process_hub_over_current(usb_hub_info_t * hub_info,
379 usb_hub_status_t status) {
380 int opResult;
381 if (usb_hub_is_status(status,USB_HUB_FEATURE_HUB_OVER_CURRENT)){
382 //poweroff all ports
383 unsigned int port;
384 for(port = 1;port <= hub_info->port_count;++port){
385 opResult = usb_hub_clear_port_feature(
386 hub_info->control_pipe,port,
387 USB_HUB_FEATURE_PORT_POWER);
388 if (opResult != EOK) {
389 usb_log_warning(
390 "cannot power off port %d; %d\n",
391 port, opResult);
392 }
393 }
394 } else {
395 //power all ports
396 unsigned int port;
397 for(port = 1;port <= hub_info->port_count;++port){
398 opResult = usb_hub_set_port_feature(
399 hub_info->control_pipe,port,
400 USB_HUB_FEATURE_PORT_POWER);
401 if (opResult != EOK) {
402 usb_log_warning(
403 "cannot power off port %d; %d\n",
404 port, opResult);
405 }
406 }
407 }
408 return opResult;
409}
410
411/**
412 * process hub local power change
413 *
414 * This change is ignored.
415 * @param hub_info hub instance
416 * @param status hub status bitmask
417 * @return error code
418 */
419static int usb_process_hub_local_power_change(usb_hub_info_t * hub_info,
420 usb_hub_status_t status) {
421 int opResult = EOK;
422 opResult = usb_hub_clear_feature(hub_info->control_pipe,
423 USB_HUB_FEATURE_C_HUB_LOCAL_POWER);
424 if (opResult != EOK) {
425 usb_log_error("cannnot clear hub power change flag: "
426 "%d\n",
427 opResult);
428 }
429 return opResult;
430}
431
432/**
433 * process hub interrupts
434 *
435 * The change can be either in the over-current condition or
436 * local-power change.
437 * @param hub_info hub instance
438 */
439static void usb_hub_process_global_interrupt(usb_hub_info_t * hub_info) {
440 usb_log_debug("global interrupt on a hub\n");
441 usb_pipe_t *pipe = hub_info->control_pipe;
442 int opResult;
443
444 usb_port_status_t status;
445 size_t rcvd_size;
446 usb_device_request_setup_packet_t request;
447 //int opResult;
448 usb_hub_set_hub_status_request(&request);
449 //endpoint 0
450
451 opResult = usb_pipe_control_read(
452 pipe,
453 &request, sizeof (usb_device_request_setup_packet_t),
454 &status, 4, &rcvd_size
455 );
456 if (opResult != EOK) {
457 usb_log_error("could not get hub status\n");
458 return;
459 }
460 if (rcvd_size != sizeof (usb_port_status_t)) {
461 usb_log_error("received status has incorrect size\n");
462 return;
463 }
464 //port reset
465 if (
466 usb_hub_is_status(status,16+USB_HUB_FEATURE_C_HUB_OVER_CURRENT)) {
467 usb_process_hub_over_current(hub_info, status);
468 }
469 if (
470 usb_hub_is_status(status,16+USB_HUB_FEATURE_C_HUB_LOCAL_POWER)) {
471 usb_process_hub_local_power_change(hub_info, status);
472 }
473}
474
475/**
476 * callback called from hub polling fibril when the fibril terminates
477 *
478 * Should perform a cleanup - deletes hub_info.
479 * @param device usb device afected
480 * @param was_error indicates that the fibril is stoped due to an error
481 * @param data pointer to usb_hub_info_t structure
482 */
483static void usb_hub_polling_terminated_callback(usb_device_t * device,
484 bool was_error, void * data){
485 usb_hub_info_t * hub = data;
486 assert(hub);
487
488 fibril_mutex_lock(&hub->pending_ops_mutex);
489
490 /* The device is dead. However there might be some pending operations
491 * that we need to wait for.
492 * One of them is device adding in progress.
493 * The respective fibril is probably waiting for status change
494 * in port reset (port enable) callback.
495 * Such change would never come (otherwise we would not be here).
496 * Thus, we would flush all pending port resets.
497 */
498 if (hub->pending_ops_count > 0) {
499 fibril_mutex_lock(&hub->port_mutex);
500 size_t port;
501 for (port = 0; port < hub->port_count; port++) {
502 usb_hub_port_t *the_port = hub->ports + port;
503 fibril_mutex_lock(&the_port->reset_mutex);
504 the_port->reset_completed = true;
505 the_port->reset_okay = false;
506 fibril_condvar_broadcast(&the_port->reset_cv);
507 fibril_mutex_unlock(&the_port->reset_mutex);
508 }
509 fibril_mutex_unlock(&hub->port_mutex);
510 }
511 /* And now wait for them. */
512 while (hub->pending_ops_count > 0) {
513 fibril_condvar_wait(&hub->pending_ops_cv,
514 &hub->pending_ops_mutex);
515 }
516 fibril_mutex_unlock(&hub->pending_ops_mutex);
517
518 free(hub->ports);
519 free(hub);
520}
521
522
523
524
525/**
526 * @}
527 */
Note: See TracBrowser for help on using the repository browser.