source: mainline/uspace/drv/bus/usb/usbhub/usbhub.c@ 983e135

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 983e135 was 983e135, checked in by Jan Vesely <jano.vesely@…>, 14 years ago

usbhub: Further codestyle changes

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