00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00036 #include <bool.h>
00037 #include <errno.h>
00038 #include <str_error.h>
00039 #include <inttypes.h>
00040 #include <fibril_synch.h>
00041
00042 #include <usb/debug.h>
00043
00044 #include "ports.h"
00045 #include "usbhub.h"
00046 #include "usbhub_private.h"
00047 #include "port_status.h"
00048
00050 struct add_device_phase1 {
00051 usb_hub_info_t *hub;
00052 size_t port;
00053 usb_speed_t speed;
00054 };
00055
00060 static const unsigned int non_handled_changes_count = 2;
00061
00066 static const int non_handled_changes[] = {
00067 USB_HUB_FEATURE_C_PORT_ENABLE,
00068 USB_HUB_FEATURE_C_PORT_SUSPEND
00069 };
00070
00071 static void usb_hub_removed_device(
00072 usb_hub_info_t *hub, uint16_t port);
00073
00074 static void usb_hub_port_reset_completed(usb_hub_info_t *hub,
00075 uint16_t port, uint32_t status);
00076
00077 static void usb_hub_port_over_current(usb_hub_info_t *hub,
00078 uint16_t port, uint32_t status);
00079
00080 static int get_port_status(usb_pipe_t *ctrl_pipe, size_t port,
00081 usb_port_status_t *status);
00082
00083 static int enable_port_callback(int port_no, void *arg);
00084
00085 static int add_device_phase1_worker_fibril(void *arg);
00086
00087 static int create_add_device_fibril(usb_hub_info_t *hub, size_t port,
00088 usb_speed_t speed);
00089
00097 void usb_hub_process_port_interrupt(usb_hub_info_t *hub,
00098 uint16_t port) {
00099 usb_log_debug("Interrupt at port %zu\n", (size_t) port);
00100
00101
00102
00103 int opResult;
00104
00105 usb_port_status_t status;
00106 opResult = get_port_status(&hub->usb_device->ctrl_pipe, port, &status);
00107 if (opResult != EOK) {
00108 usb_log_error("Failed to get port %zu status: %s.\n",
00109 (size_t) port, str_error(opResult));
00110 return;
00111 }
00112
00113 if (usb_port_is_status(status, USB_HUB_FEATURE_C_PORT_CONNECTION)) {
00114 bool device_connected = usb_port_is_status(status,
00115 USB_HUB_FEATURE_PORT_CONNECTION);
00116 usb_log_debug("Connection change on port %zu: %s.\n",
00117 (size_t) port,
00118 device_connected ? "device attached" : "device removed");
00119
00120 if (device_connected) {
00121 opResult = create_add_device_fibril(hub, port,
00122 usb_port_speed(status));
00123 if (opResult != EOK) {
00124 usb_log_error(
00125 "Cannot handle change on port %zu: %s.\n",
00126 (size_t) port, str_error(opResult));
00127 }
00128 } else {
00129 usb_hub_removed_device(hub, port);
00130 }
00131 }
00132
00133 if (usb_port_is_status(status, USB_HUB_FEATURE_C_PORT_OVER_CURRENT)) {
00134
00135 usb_log_debug("Overcurrent change on port\n");
00136 usb_hub_port_over_current(hub, port, status);
00137 }
00138
00139 if (usb_port_is_status(status, USB_HUB_FEATURE_C_PORT_RESET)) {
00140 usb_hub_port_reset_completed(hub, port, status);
00141 }
00142 usb_log_debug("Status x%x : %d\n ", status, status);
00143
00144 usb_port_status_set_bit(
00145 &status, USB_HUB_FEATURE_C_PORT_CONNECTION, false);
00146 usb_port_status_set_bit(
00147 &status, USB_HUB_FEATURE_C_PORT_RESET, false);
00148 usb_port_status_set_bit(
00149 &status, USB_HUB_FEATURE_C_PORT_OVER_CURRENT, false);
00150
00151
00152 unsigned int feature_idx;
00153 for (feature_idx = 0;
00154 feature_idx < non_handled_changes_count;
00155 ++feature_idx) {
00156 unsigned int bit_idx = non_handled_changes[feature_idx];
00157 if (status & (1 << bit_idx)) {
00158 usb_log_info(
00159 "There was not yet handled change on port %d: %d"
00160 ";clearing it\n",
00161 port, bit_idx);
00162 int opResult = usb_hub_clear_port_feature(
00163 hub->control_pipe,
00164 port, bit_idx);
00165 if (opResult != EOK) {
00166 usb_log_warning(
00167 "Could not clear port flag %d: %s\n",
00168 bit_idx, str_error(opResult)
00169 );
00170 }
00171 usb_port_status_set_bit(
00172 &status, bit_idx, false);
00173 }
00174 }
00175 if (status >> 16) {
00176 usb_log_info("There is still some unhandled change %X\n",
00177 status);
00178 }
00179 }
00180
00190 static void usb_hub_removed_device(
00191 usb_hub_info_t *hub, uint16_t port) {
00192
00193 int opResult = usb_hub_clear_port_feature(hub->control_pipe,
00194 port, USB_HUB_FEATURE_C_PORT_CONNECTION);
00195 if (opResult != EOK) {
00196 usb_log_warning("Could not clear port-change-connection flag\n");
00197 }
00202
00203
00204 usb_hub_port_t *the_port = hub->ports + port;
00205
00206 fibril_mutex_lock(&hub->port_mutex);
00207
00208 if (the_port->attached_device.address >= 0) {
00209 usb_log_warning("Device unplug on `%s' (port %zu): " \
00210 "not implemented.\n", hub->usb_device->ddf_dev->name,
00211 (size_t) port);
00212 the_port->attached_device.address = -1;
00213 the_port->attached_device.handle = 0;
00214 } else {
00215 usb_log_warning("Device removed before being registered.\n");
00216
00217
00218
00219
00220
00221
00222 fibril_mutex_lock(&the_port->reset_mutex);
00223 the_port->reset_completed = true;
00224 the_port->reset_okay = false;
00225 fibril_condvar_broadcast(&the_port->reset_cv);
00226 fibril_mutex_unlock(&the_port->reset_mutex);
00227 }
00228
00229 fibril_mutex_unlock(&hub->port_mutex);
00230 }
00231
00241 static void usb_hub_port_reset_completed(usb_hub_info_t *hub,
00242 uint16_t port, uint32_t status) {
00243 usb_log_debug("Port %zu reset complete.\n", (size_t) port);
00244 if (usb_port_is_status(status, USB_HUB_FEATURE_PORT_ENABLE)) {
00245
00246 usb_hub_port_t *the_port = hub->ports + port;
00247 fibril_mutex_lock(&the_port->reset_mutex);
00248 the_port->reset_completed = true;
00249 the_port->reset_okay = true;
00250 fibril_condvar_broadcast(&the_port->reset_cv);
00251 fibril_mutex_unlock(&the_port->reset_mutex);
00252 } else {
00253 usb_log_warning(
00254 "Port %zu reset complete but port not enabled.\n",
00255 (size_t) port);
00256 }
00257
00258 int rc = usb_hub_clear_port_feature(hub->control_pipe,
00259 port, USB_HUB_FEATURE_C_PORT_RESET);
00260 if (rc != EOK) {
00261 usb_log_error("Failed to clear port %d reset feature: %s.\n",
00262 port, str_error(rc));
00263 }
00264 }
00265
00274 static void usb_hub_port_over_current(usb_hub_info_t *hub,
00275 uint16_t port, uint32_t status) {
00276 int opResult;
00277 if (usb_port_is_status(status, USB_HUB_FEATURE_PORT_OVER_CURRENT)) {
00278 opResult = usb_hub_clear_port_feature(hub->control_pipe,
00279 port, USB_HUB_FEATURE_PORT_POWER);
00280 if (opResult != EOK) {
00281 usb_log_error("Cannot power off port %d; %s\n",
00282 port, str_error(opResult));
00283 }
00284 } else {
00285 opResult = usb_hub_set_port_feature(hub->control_pipe,
00286 port, USB_HUB_FEATURE_PORT_POWER);
00287 if (opResult != EOK) {
00288 usb_log_error("Cannot power on port %d; %s\n",
00289 port, str_error(opResult));
00290 }
00291 }
00292 }
00293
00301 static int get_port_status(usb_pipe_t *ctrl_pipe, size_t port,
00302 usb_port_status_t *status) {
00303 size_t recv_size;
00304 usb_device_request_setup_packet_t request;
00305 usb_port_status_t status_tmp;
00306
00307 usb_hub_set_port_status_request(&request, port);
00308 int rc = usb_pipe_control_read(ctrl_pipe,
00309 &request, sizeof (usb_device_request_setup_packet_t),
00310 &status_tmp, sizeof (status_tmp), &recv_size);
00311 if (rc != EOK) {
00312 return rc;
00313 }
00314
00315 if (recv_size != sizeof (status_tmp)) {
00316 return ELIMIT;
00317 }
00318
00319 if (status != NULL) {
00320 *status = status_tmp;
00321 }
00322
00323 return EOK;
00324 }
00325
00335 static int enable_port_callback(int port_no, void *arg) {
00336 usb_hub_info_t *hub = arg;
00337 int rc;
00338 usb_device_request_setup_packet_t request;
00339 usb_hub_port_t *my_port = hub->ports + port_no;
00340
00341 usb_hub_set_reset_port_request(&request, port_no);
00342 rc = usb_pipe_control_write(hub->control_pipe,
00343 &request, sizeof (request), NULL, 0);
00344 if (rc != EOK) {
00345 usb_log_warning("Port reset failed: %s.\n", str_error(rc));
00346 return rc;
00347 }
00348
00349
00350
00351
00352 fibril_mutex_lock(&my_port->reset_mutex);
00353 while (!my_port->reset_completed) {
00354 fibril_condvar_wait(&my_port->reset_cv, &my_port->reset_mutex);
00355 }
00356 fibril_mutex_unlock(&my_port->reset_mutex);
00357
00358 if (my_port->reset_okay) {
00359 return EOK;
00360 } else {
00361 return ESTALL;
00362 }
00363 }
00364
00373 static int add_device_phase1_worker_fibril(void *arg) {
00374 struct add_device_phase1 *data
00375 = (struct add_device_phase1 *) arg;
00376
00377 usb_address_t new_address;
00378 devman_handle_t child_handle;
00379
00380 int rc = usb_hc_new_device_wrapper(data->hub->usb_device->ddf_dev,
00381 &data->hub->connection, data->speed,
00382 enable_port_callback, (int) data->port, data->hub,
00383 &new_address, &child_handle,
00384 NULL, NULL, NULL);
00385
00386 if (rc != EOK) {
00387 usb_log_error("Failed registering device on port %zu: %s.\n",
00388 data->port, str_error(rc));
00389 goto leave;
00390 }
00391
00392 fibril_mutex_lock(&data->hub->port_mutex);
00393 data->hub->ports[data->port].attached_device.handle = child_handle;
00394 data->hub->ports[data->port].attached_device.address = new_address;
00395 fibril_mutex_unlock(&data->hub->port_mutex);
00396
00397 usb_log_info("Detected new device on `%s' (port %zu), "
00398 "address %d (handle %" PRIun ").\n",
00399 data->hub->usb_device->ddf_dev->name, data->port,
00400 new_address, child_handle);
00401
00402 leave:
00403 free(arg);
00404
00405 fibril_mutex_lock(&data->hub->pending_ops_mutex);
00406 assert(data->hub->pending_ops_count > 0);
00407 data->hub->pending_ops_count--;
00408 fibril_condvar_signal(&data->hub->pending_ops_cv);
00409 fibril_mutex_unlock(&data->hub->pending_ops_mutex);
00410
00411
00412 return EOK;
00413 }
00414
00424 static int create_add_device_fibril(usb_hub_info_t *hub, size_t port,
00425 usb_speed_t speed) {
00426 struct add_device_phase1 *data
00427 = malloc(sizeof (struct add_device_phase1));
00428 if (data == NULL) {
00429 return ENOMEM;
00430 }
00431 data->hub = hub;
00432 data->port = port;
00433 data->speed = speed;
00434
00435 usb_hub_port_t *the_port = hub->ports + port;
00436
00437 fibril_mutex_lock(&the_port->reset_mutex);
00438 the_port->reset_completed = false;
00439 fibril_mutex_unlock(&the_port->reset_mutex);
00440
00441 int rc = usb_hub_clear_port_feature(hub->control_pipe, port,
00442 USB_HUB_FEATURE_C_PORT_CONNECTION);
00443 if (rc != EOK) {
00444 free(data);
00445 usb_log_warning("Failed to clear port change flag: %s.\n",
00446 str_error(rc));
00447 return rc;
00448 }
00449
00450 fid_t fibril = fibril_create(add_device_phase1_worker_fibril, data);
00451 if (fibril == 0) {
00452 free(data);
00453 return ENOMEM;
00454 }
00455 fibril_mutex_lock(&hub->pending_ops_mutex);
00456 hub->pending_ops_count++;
00457 fibril_mutex_unlock(&hub->pending_ops_mutex);
00458 fibril_add_ready(fibril);
00459
00460 return EOK;
00461 }
00462