diff --git a/minix/drivers/usb/usb_hub/usb_hub.c b/minix/drivers/usb/usb_hub/usb_hub.c index 186bc8721..6925f681c 100644 --- a/minix/drivers/usb/usb_hub/usb_hub.c +++ b/minix/drivers/usb/usb_hub/usb_hub.c @@ -4,6 +4,7 @@ #include /* memset */ #include +#include /* nanosleep */ #include #include @@ -51,6 +52,9 @@ static void hub_task(void *); /* Limits number of communication retries (when needed) */ #define USB_HUB_MAX_TRIES 3 +/* How long to wait between retries, in case of reset error (in nanoseconds) */ +#define USB_HUB_RESET_DELAY 200000000 /* 200ms */ + /* Hub descriptor type */ #define USB_HUB_DESCRIPTOR_TYPE 0x29 @@ -821,6 +825,7 @@ hub_handle_change(int port_num, hub_port_status * status) static int hub_handle_connection(int port_num, hub_port_status * status) { + struct timespec wait_time; int reset_tries; long port_speed; @@ -841,6 +846,8 @@ hub_handle_connection(int port_num, hub_port_status * status) } reset_tries = 0; + wait_time.tv_sec = 0; + wait_time.tv_nsec = USB_HUB_RESET_DELAY; /* Wait for reset completion */ while (!status->C_PORT_RESET) { @@ -857,6 +864,11 @@ hub_handle_connection(int port_num, hub_port_status * status) } reset_tries++; + + /* Ignore potential signal interruption (no return value check), + * since it causes driver termination anyway */ + if (nanosleep(&wait_time, NULL)) + HUB_MSG("Calling nanosleep() failed"); } /* Reset completed */ diff --git a/minix/drivers/usb/usbd/hcd/hcd.c b/minix/drivers/usb/usbd/hcd/hcd.c index ad6ad6451..db5fd5a88 100644 --- a/minix/drivers/usb/usbd/hcd/hcd.c +++ b/minix/drivers/usb/usbd/hcd/hcd.c @@ -9,6 +9,7 @@ #include #include #include +#include #include @@ -40,7 +41,7 @@ static int hcd_non_control_urb(hcd_device_state *, hcd_urb *); /* For internal use by more general methods */ static int hcd_setup_packet(hcd_device_state *, hcd_ctrlrequest *, hcd_reg1); -static int hcd_finish_setup(hcd_device_state *, void *, hcd_reg4); +static int hcd_finish_setup(hcd_device_state *, void *); static int hcd_data_transfer(hcd_device_state *, hcd_datarequest *); /* TODO: This is not meant to be explicitly visible outside DDEKit library @@ -55,6 +56,10 @@ extern void _ddekit_thread_set_myprio(int); * allow receiving less data than expected in URB, without error */ #define HCD_ANY_LENGTH 0xFFFFFFFFu +/* This doesn't seem to be specified in standard but abnormal values + * are unlikely so check for this was added below */ +#define HCD_SANE_DESCRIPTOR_LENGTH 2048 + /*===========================================================================* * hcd_handle_event * @@ -255,6 +260,9 @@ hcd_device_thread(void * thread_args) /* Block and wait for something like 'submit URB' */ hcd_device_wait(this_device, HCD_EVENT_URB, HCD_UNUSED_VAL); hcd_handle_urb(this_device); + + /* Only external URBs should be submitted here */ + hcd_completion_cb(this_device->urb); } /* Finish device handling to avoid leaving thread */ @@ -413,12 +421,45 @@ hcd_enumerate(hcd_device_state * this_device) return EXIT_FAILURE; } + /* Remember max packet size from device descriptor */ + this_device->max_packet_size = this_device->device_desc.bMaxPacketSize; + + /* Dump device descriptor in debug mode */ +#ifdef DEBUG + { + hcd_device_descriptor * d; + d = &(this_device->device_desc); + + USB_DBG("<>"); + USB_DBG("bLength %02X", d->bLength); + USB_DBG("bDescriptorType %02X", d->bDescriptorType); + USB_DBG("bcdUSB %04X", UGETW(d->bcdUSB)); + USB_DBG("bDeviceClass %02X", d->bDeviceClass); + USB_DBG("bDeviceSubClass %02X", d->bDeviceSubClass); + USB_DBG("bDeviceProtocol %02X", d->bDeviceProtocol); + USB_DBG("bMaxPacketSize %02X", d->bMaxPacketSize); + USB_DBG("idVendor %04X", UGETW(d->idVendor)); + USB_DBG("idProduct %04X", UGETW(d->idProduct)); + USB_DBG("bcdDevice %04X", UGETW(d->bcdDevice)); + USB_DBG("iManufacturer %02X", d->iManufacturer); + USB_DBG("iProduct %02X", d->iProduct); + USB_DBG("iSerialNumber %02X", d->iSerialNumber); + USB_DBG("bNumConfigurations %02X", d->bNumConfigurations); + } +#endif + /* Set reserved address */ if (EXIT_SUCCESS != hcd_set_address(this_device)) { USB_MSG("Failed to set device address"); return EXIT_FAILURE; } + /* Sleep 5msec to allow addressing */ + hcd_os_nanosleep(HCD_NANOSLEEP_MSEC(5)); + + /* Remember what was assigned in hardware */ + this_device->current_address = this_device->reserved_address; + /* Get other descriptors */ if (EXIT_SUCCESS != hcd_get_descriptor_tree(this_device)) { USB_MSG("Failed to get configuration descriptor tree"); @@ -448,11 +489,11 @@ static int hcd_get_device_descriptor(hcd_device_state * this_device) { hcd_ctrlrequest setup; + hcd_urb urb; DEBUG_DUMP; /* TODO: magic numbers, no header for these */ - /* Format setup packet */ setup.bRequestType = 0x80; /* IN */ setup.bRequest = 0x06; /* Get descriptor */ @@ -460,45 +501,31 @@ hcd_get_device_descriptor(hcd_device_state * this_device) setup.wIndex = 0x0000; setup.wLength = sizeof(this_device->device_desc); - /* Handle formatted setup packet */ - if (EXIT_SUCCESS != hcd_setup_packet(this_device, &setup, - HCD_DEFAULT_EP)) { - USB_MSG("Handling setup packet failed"); + /* Prepare self-URB */ + memset(&urb, 0, sizeof(urb)); + urb.direction = HCD_DIRECTION_IN; + urb.endpoint = HCD_DEFAULT_EP; + urb.in_setup = &setup; + urb.inout_data = (hcd_reg1 *)(&(this_device->device_desc)); + urb.target_device = this_device; + urb.type = HCD_TRANSFER_CONTROL; + + /* Put it to be scheduled and wait for control to get back */ + hcd_schedule_internal_urb(&urb); + hcd_device_wait(this_device, HCD_EVENT_URB, HCD_UNUSED_VAL); + hcd_handle_urb(this_device); + + /* Check if URB submission completed successfully */ + if (urb.inout_status) { + USB_MSG("URB submission failed"); return EXIT_FAILURE; } - /* Put what was read in device descriptor */ - if (EXIT_SUCCESS != hcd_finish_setup(this_device, - &(this_device->device_desc), - sizeof(this_device->device_desc))) + /* Check if expected size was received */ + if (urb.out_size != setup.wLength) { + USB_MSG("URB submission returned invalid amount of data"); return EXIT_FAILURE; - - /* Remember max packet size from device descriptor */ - this_device->max_packet_size = this_device->device_desc.bMaxPacketSize; - - /* Dump device descriptor in debug mode */ -#ifdef DEBUG - { - hcd_device_descriptor * d; - d = &(this_device->device_desc); - - USB_DBG("<>"); - USB_DBG("bLength %02X", d->bLength); - USB_DBG("bDescriptorType %02X", d->bDescriptorType); - USB_DBG("bcdUSB %04X", UGETW(d->bcdUSB)); - USB_DBG("bDeviceClass %02X", d->bDeviceClass); - USB_DBG("bDeviceSubClass %02X", d->bDeviceSubClass); - USB_DBG("bDeviceProtocol %02X", d->bDeviceProtocol); - USB_DBG("bMaxPacketSize %02X", d->bMaxPacketSize); - USB_DBG("idVendor %04X", UGETW(d->idVendor)); - USB_DBG("idProduct %04X", UGETW(d->idProduct)); - USB_DBG("bcdDevice %04X", UGETW(d->bcdDevice)); - USB_DBG("iManufacturer %02X", d->iManufacturer); - USB_DBG("iProduct %02X", d->iProduct); - USB_DBG("iSerialNumber %02X", d->iSerialNumber); - USB_DBG("bNumConfigurations %02X", d->bNumConfigurations); } -#endif return EXIT_SUCCESS; } @@ -511,6 +538,7 @@ static int hcd_set_address(hcd_device_state * this_device) { hcd_ctrlrequest setup; + hcd_urb urb; DEBUG_DUMP; @@ -526,18 +554,31 @@ hcd_set_address(hcd_device_state * this_device) setup.wIndex = 0x0000; setup.wLength = 0x0000; - /* Handle formatted setup packet */ - if (EXIT_SUCCESS != hcd_setup_packet(this_device, &setup, - HCD_DEFAULT_EP)) { - USB_MSG("Handling setup packet failed"); + /* Prepare self-URB */ + memset(&urb, 0, sizeof(urb)); + urb.direction = HCD_DIRECTION_OUT; + urb.endpoint = HCD_DEFAULT_EP; + urb.in_setup = &setup; + urb.inout_data = NULL; + urb.target_device = this_device; + urb.type = HCD_TRANSFER_CONTROL; + + /* Put it to be scheduled and wait for control to get back */ + hcd_schedule_internal_urb(&urb); + hcd_device_wait(this_device, HCD_EVENT_URB, HCD_UNUSED_VAL); + hcd_handle_urb(this_device); + + /* Check if URB submission completed successfully */ + if (urb.inout_status) { + USB_MSG("URB submission failed"); return EXIT_FAILURE; } - /* Sleep 5msec to allow addressing */ - hcd_os_nanosleep(HCD_NANOSLEEP_MSEC(5)); - - /* Remember what was assigned in hardware */ - this_device->current_address = this_device->reserved_address; + /* Check if expected size was received */ + if (urb.out_size != setup.wLength) { + USB_MSG("URB submission returned invalid amount of data"); + return EXIT_FAILURE; + } return EXIT_SUCCESS; } @@ -549,19 +590,24 @@ hcd_set_address(hcd_device_state * this_device) static int hcd_get_descriptor_tree(hcd_device_state * this_device) { - hcd_config_descriptor config_descriptor; + hcd_config_descriptor temp_config_descriptor; hcd_ctrlrequest setup; - hcd_reg4 total_length; - hcd_reg4 buffer_length; - int completed; + hcd_urb urb; + + /* To receive data */ + hcd_reg4 expected_length; + hcd_reg1 * expected_buffer; + + int retval; DEBUG_DUMP; - /* First, ask only for configuration itself to get length info */ - buffer_length = sizeof(config_descriptor); - completed = 0; + /* Initially */ + retval = EXIT_FAILURE; + expected_buffer = NULL; - do { + /* First part gets only configuration to find out total length */ + { /* TODO: Default configuration is hard-coded * but others are rarely used anyway */ /* TODO: magic numbers, no header for these */ @@ -569,62 +615,107 @@ hcd_get_descriptor_tree(hcd_device_state * this_device) setup.bRequest = 0x06; /* Get descriptor */ setup.wValue = 0x0200 | HCD_DEFAULT_CONFIG; setup.wIndex = 0x0000; - setup.wLength = buffer_length; + setup.wLength = sizeof(temp_config_descriptor); - /* Handle formatted setup packet */ - if (EXIT_SUCCESS != hcd_setup_packet(this_device, &setup, - HCD_DEFAULT_EP)) { - USB_MSG("Handling setup packet failed"); - return EXIT_FAILURE; + /* Prepare self-URB */ + memset(&urb, 0, sizeof(urb)); + urb.direction = HCD_DIRECTION_IN; + urb.endpoint = HCD_DEFAULT_EP; + urb.in_setup = &setup; + urb.inout_data = (hcd_reg1 *)(&temp_config_descriptor); + urb.target_device = this_device; + urb.type = HCD_TRANSFER_CONTROL; + + /* Put it to be scheduled and wait for control to get back */ + hcd_schedule_internal_urb(&urb); + hcd_device_wait(this_device, HCD_EVENT_URB, HCD_UNUSED_VAL); + hcd_handle_urb(this_device); + + /* Check if URB submission completed successfully */ + if (urb.inout_status) { + USB_MSG("URB submission failed"); + goto FINISH; } - /* If we only asked for configuration itself - * then ask again for other descriptors */ - if (sizeof(config_descriptor) == buffer_length) { - - /* Put what was already read in configuration - * descriptor for analysis */ - if (EXIT_SUCCESS != hcd_finish_setup(this_device, - &config_descriptor, - buffer_length)) - return EXIT_FAILURE; - - /* Continue only if there is more data */ - total_length = UGETW(config_descriptor.wTotalLength); - - if (total_length < sizeof(config_descriptor)) { - /* This should never happen for a fine device */ - USB_MSG("Illegal wTotalLength value"); - return EXIT_FAILURE; - } else if (sizeof(config_descriptor) == total_length) { - /* Nothing more was in descriptor anyway */ - completed = 1; - } else { - /* Read whatever is needed */ - buffer_length = total_length; - } - - } else { - /* All data for given configuration was read */ - completed = 1; + /* Check if expected size was received */ + if (urb.out_size != setup.wLength) { + USB_MSG("URB submission returned " + "invalid amount of data"); + goto FINISH; } } - while (!completed); - /* Validate... */ - if (EXIT_SUCCESS != hcd_finish_setup(this_device, NULL, total_length)) - return EXIT_FAILURE; + /* Get total expected length */ + expected_length = UGETW(temp_config_descriptor.wTotalLength); - /* ... and create tree based on received buffer */ - if (EXIT_SUCCESS != hcd_buffer_to_tree(this_device->control_data, - this_device->control_len, + /* Check for abnormal value */ + if (expected_length > HCD_SANE_DESCRIPTOR_LENGTH) { + USB_MSG("Total descriptor length declared is too high"); + goto FINISH; + } + + /* Get descriptor buffer to hold everything expected */ + if (NULL == (expected_buffer = malloc(expected_length))) { + USB_MSG("Descriptor allocation failed"); + goto FINISH; + } + + /* Second part gets all available descriptors */ + { + /* TODO: Default configuration is hard-coded + * but others are rarely used anyway */ + /* TODO: magic numbers, no header for these */ + setup.bRequestType = 0x80; /* IN */ + setup.bRequest = 0x06; /* Get descriptor */ + setup.wValue = 0x0200 | HCD_DEFAULT_CONFIG; + setup.wIndex = 0x0000; + setup.wLength = expected_length; + + /* Prepare self-URB */ + memset(&urb, 0, sizeof(urb)); + urb.direction = HCD_DIRECTION_IN; + urb.endpoint = HCD_DEFAULT_EP; + urb.in_setup = &setup; + urb.inout_data = expected_buffer; + urb.target_device = this_device; + urb.type = HCD_TRANSFER_CONTROL; + + /* Put it to be scheduled and wait for control to get back */ + hcd_schedule_internal_urb(&urb); + hcd_device_wait(this_device, HCD_EVENT_URB, HCD_UNUSED_VAL); + hcd_handle_urb(this_device); + + /* Check if URB submission completed successfully */ + if (urb.inout_status) { + USB_MSG("URB submission failed"); + goto FINISH; + } + + /* Check if expected size was received */ + if (urb.out_size != setup.wLength) { + USB_MSG("URB submission returned " + "invalid amount of data"); + goto FINISH; + } + } + + if (EXIT_SUCCESS != hcd_buffer_to_tree(expected_buffer, + (int)expected_length, &(this_device->config_tree))) { - /* This should never happen for a fine device */ - USB_MSG("Illegal descriptor values"); - return EXIT_FAILURE; + USB_MSG("Broken descriptor data"); + goto FINISH; } - return EXIT_SUCCESS; + /* No errors occurred */ + retval = EXIT_SUCCESS; + + FINISH: + + /* Release allocated buffer */ + if (expected_buffer) + free(expected_buffer); + + return retval; } @@ -635,6 +726,7 @@ static int hcd_set_configuration(hcd_device_state * this_device, hcd_reg1 configuration) { hcd_ctrlrequest setup; + hcd_urb urb; DEBUG_DUMP; @@ -645,14 +737,21 @@ hcd_set_configuration(hcd_device_state * this_device, hcd_reg1 configuration) setup.wIndex = 0x0000; setup.wLength = 0x0000; - /* Handle formatted setup packet */ - if (EXIT_SUCCESS != hcd_setup_packet(this_device, &setup, - HCD_DEFAULT_EP)) { - USB_MSG("Handling setup packet failed"); - return EXIT_FAILURE; - } + /* Prepare self-URB */ + memset(&urb, 0, sizeof(urb)); + urb.direction = HCD_DIRECTION_OUT; + urb.endpoint = HCD_DEFAULT_EP; + urb.in_setup = &setup; + urb.inout_data = NULL; + urb.target_device = this_device; + urb.type = HCD_TRANSFER_CONTROL; - return EXIT_SUCCESS; + /* Put it to be scheduled and wait for control to get back */ + hcd_schedule_internal_urb(&urb); + hcd_device_wait(this_device, HCD_EVENT_URB, HCD_UNUSED_VAL); + hcd_handle_urb(this_device); + + return urb.inout_status; } @@ -705,6 +804,8 @@ hcd_handle_urb(hcd_device_state * this_device) USB_MSG("Invalid URB supplied"); /* Signal scheduler that URB was handled */ + /* TODO: This works based on the fact that device thread has higher + * priority than scheduler and won't change context within this call */ urb->handled(urb); } @@ -747,9 +848,7 @@ hcd_control_urb(hcd_device_state * this_device, hcd_urb * urb) } /* Put what was read back into URB */ - if (EXIT_SUCCESS != hcd_finish_setup(this_device, - urb->inout_data, - HCD_ANY_LENGTH)) + if (EXIT_SUCCESS != hcd_finish_setup(this_device, urb->inout_data)) return EXIT_FAILURE; /* Write transfer output info to URB */ @@ -986,8 +1085,7 @@ hcd_setup_packet(hcd_device_state * this_device, hcd_ctrlrequest * setup, * hcd_finish_setup * *===========================================================================*/ static int -hcd_finish_setup(hcd_device_state * this_device, void * output, - hcd_reg4 expected) +hcd_finish_setup(hcd_device_state * this_device, void * output) { DEBUG_DUMP; @@ -997,21 +1095,6 @@ hcd_finish_setup(hcd_device_state * this_device, void * output, return EXIT_FAILURE; } - /* In case it is required... */ - if (HCD_ANY_LENGTH != expected) { - /* ...check for expected length */ - if ((hcd_reg4)this_device->control_len != expected) { - USB_MSG("Control transfer output length mismatch:" - "len %d, expected %u", this_device->control_len, - expected); - return EXIT_FAILURE; - } - - /* Valid but there is no need to copy anything */ - if (0u == expected) - return EXIT_SUCCESS; - } - /* Length is valid but output not supplied */ if (NULL == output) return EXIT_SUCCESS; diff --git a/minix/drivers/usb/usbd/hcd/hcd_ddekit.c b/minix/drivers/usb/usbd/hcd/hcd_ddekit.c index dbbf6e120..9c9684663 100644 --- a/minix/drivers/usb/usbd/hcd/hcd_ddekit.c +++ b/minix/drivers/usb/usbd/hcd/hcd_ddekit.c @@ -196,7 +196,7 @@ ddekit_usb_submit_urb(struct ddekit_usb_urb * d_urb) hcd_decode_urb(urb, d_urb); /* Add URB to scheduler */ - return hcd_schedule_urb(urb); + return hcd_schedule_external_urb(urb); } @@ -308,8 +308,7 @@ hcd_completion_cb(hcd_urb * urb) /* Recollect original URB */ d_urb = (struct ddekit_usb_urb *)urb->original_urb; - /* This URB will be scheduled no more */ - hcd_unschedule_urb(urb); + USB_ASSERT(NULL != d_urb, "Original DDEKit URB missing"); /* Turn HCD URB format to one handled by DDEKit */ hcd_encode_urb(urb, d_urb); diff --git a/minix/drivers/usb/usbd/hcd/hcd_schedule.c b/minix/drivers/usb/usbd/hcd/hcd_schedule.c index 5da6bcb11..49906bf32 100644 --- a/minix/drivers/usb/usbd/hcd/hcd_schedule.c +++ b/minix/drivers/usb/usbd/hcd/hcd_schedule.c @@ -34,6 +34,12 @@ static hcd_lock * urb_lock; /* This allows waiting for completion */ static hcd_lock * handled_lock; +/* Makes URB schedule enabled */ +static int hcd_schedule_urb(hcd_urb *); + +/* Makes URB schedule disabled */ +static void hcd_unschedule_urb(hcd_urb *); + /* Scheduler task */ static void hcd_urb_scheduler_task(void *); @@ -102,9 +108,33 @@ usbd_deinit_scheduler(void) /*===========================================================================* - * hcd_schedule_urb * + * hcd_schedule_external_urb * *===========================================================================*/ int +hcd_schedule_external_urb(hcd_urb * urb) +{ + DEBUG_DUMP; + + return hcd_schedule_urb(urb); +} + + +/*===========================================================================* + * hcd_schedule_internal_urb * + *===========================================================================*/ +int +hcd_schedule_internal_urb(hcd_urb * urb) +{ + DEBUG_DUMP; + + return hcd_schedule_urb(urb); +} + + +/*===========================================================================* + * hcd_schedule_urb * + *===========================================================================*/ +static int hcd_schedule_urb(hcd_urb * urb) { DEBUG_DUMP; @@ -114,12 +144,7 @@ hcd_schedule_urb(hcd_urb * urb) /* Store and check if scheduler should be unlocked */ if (EXIT_SUCCESS == hcd_store_urb(urb)) { - - /* This is the first stored URB (possibly in a row) - * so unlock scheduler */ - if (1 == num_stored_urbs) - ddekit_sem_up(urb_lock); - + ddekit_sem_up(urb_lock); return EXIT_SUCCESS; } @@ -130,7 +155,7 @@ hcd_schedule_urb(hcd_urb * urb) /*===========================================================================* * hcd_unschedule_urb * *===========================================================================*/ -void +static void hcd_unschedule_urb(hcd_urb * urb) { DEBUG_DUMP; @@ -150,9 +175,8 @@ hcd_urb_scheduler_task(void * UNUSED(arg)) DEBUG_DUMP; for (;;) { - /* Wait for scheduler to unlock on first URB submit */ - if (0 == num_stored_urbs) - ddekit_sem_down(urb_lock); + /* Wait for scheduler to unlock on any URB submit */ + ddekit_sem_down(urb_lock); /* Get URB */ current_urb = hcd_get_urb(); @@ -169,9 +193,6 @@ hcd_urb_scheduler_task(void * UNUSED(arg)) /* Wait for completion */ ddekit_sem_down(handled_lock); - - /* Handled, forget about it */ - current_urb->target_device->urb = NULL; } } @@ -184,8 +205,10 @@ hcd_urb_handled(hcd_urb * urb) { DEBUG_DUMP; - /* Call completion regardless of status */ - hcd_completion_cb(urb); + /* This URB will be scheduled no more */ + /* TODO: It would be better if this was connected + * to setting urb_lock down */ + hcd_unschedule_urb(urb); /* Handling completed */ ddekit_sem_up(handled_lock); diff --git a/minix/drivers/usb/usbd/include/usbd/hcd_schedule.h b/minix/drivers/usb/usbd/include/usbd/hcd_schedule.h index 72fd560ac..ba34f99fd 100644 --- a/minix/drivers/usb/usbd/include/usbd/hcd_schedule.h +++ b/minix/drivers/usb/usbd/include/usbd/hcd_schedule.h @@ -7,10 +7,10 @@ #include -/* Makes URB schedule enabled */ -int hcd_schedule_urb(hcd_urb *); +/* Makes external (device driver) URB schedule enabled */ +int hcd_schedule_external_urb(hcd_urb *); -/* Makes URB schedule disabled */ -void hcd_unschedule_urb(hcd_urb *); +/* Makes internal (HCD) URB schedule enabled */ +int hcd_schedule_internal_urb(hcd_urb *); #endif /* !_HCD_SCHEDULE_H_ */