diff --git a/distrib/sets/lists/minix/mi b/distrib/sets/lists/minix/mi index 6f3ad627b..8546028f2 100644 --- a/distrib/sets/lists/minix/mi +++ b/distrib/sets/lists/minix/mi @@ -82,6 +82,7 @@ ./etc/devmand/scripts minix-sys ./etc/devmand/scripts/block minix-sys ./etc/devmand/scripts/singlechar minix-sys +./etc/devmand/usb_hub.cfg minix-sys ./etc/devmand/usb_storage.cfg minix-sys ./etc/gettytab minix-sys ./etc/group minix-sys @@ -116,6 +117,7 @@ ./etc/system.conf minix-sys ./etc/system.conf.d minix-sys ./etc/system.conf.d/ipc minix-sys +./etc/system.conf.d/usb_hub minix-sys ./etc/system.conf.d/usb_storage minix-sys ./etc/termcap minix-sys ./etc/ttys minix-sys @@ -172,6 +174,7 @@ ./service/sched minix-sys ./service/tty minix-sys ./service/uds minix-sys +./service/usb_hub minix-sys ./service/usb_storage minix-sys ./service/vfs minix-sys ./service/vm minix-sys diff --git a/etc/Makefile b/etc/Makefile index 766ff3762..8f1662064 100644 --- a/etc/Makefile +++ b/etc/Makefile @@ -430,6 +430,7 @@ install-etc-files-safe: .PHONY .MAKE check_DESTDIR MAKEDEV ${BINOWN} ${BINGRP} ${BINMODE} ${NETBSDSRCDIR}/etc/ ${DESTDIR}/usr/lib/ crontab \ ${BINOWN} ${BINGRP} ${BINMODE} ${NETBSDSRCDIR}/etc/ ${DESTDIR}/etc/ system.conf \ ${BINOWN} ${BINGRP} ${NONBINMODE} ${NETBSDSRCDIR}/etc/usr/ ${DESTDIR}/usr/ Makefile \ + ${BINOWN} ${BINGRP} ${BINMODE} ${NETBSDSRCDIR}/etc/devmand/ ${DESTDIR}/etc/devmand/ usb_hub.cfg \ ${BINOWN} ${BINGRP} ${BINMODE} ${NETBSDSRCDIR}/etc/devmand/ ${DESTDIR}/etc/devmand/ usb_storage.cfg \ ${BINOWN} ${BINGRP} ${BINMODE} ${NETBSDSRCDIR}/etc/devmand/scripts/ ${DESTDIR}/etc/devmand/scripts/ block \ ${BINOWN} ${BINGRP} ${BINMODE} ${NETBSDSRCDIR}/etc/devmand/scripts/ ${DESTDIR}/etc/devmand/scripts/ singlechar \ diff --git a/etc/devmand/usb_hub.cfg b/etc/devmand/usb_hub.cfg new file mode 100644 index 000000000..71b534aee --- /dev/null +++ b/etc/devmand/usb_hub.cfg @@ -0,0 +1,8 @@ +usb_driver usb_hub +{ + binary = /service/usb_hub; + id { + bInterfaceClass = 0x09; + } + devprefix = usb_hub; +} diff --git a/etc/devmand/usb_storage.cfg b/etc/devmand/usb_storage.cfg index 7839f9b13..03c85955c 100644 --- a/etc/devmand/usb_storage.cfg +++ b/etc/devmand/usb_storage.cfg @@ -1,6 +1,6 @@ usb_driver usb_storage { - binary = /usr/sbin/usb_storage; + binary = /service/usb_storage; id { bInterfaceClass = 0x08; } diff --git a/minix/drivers/usb/Makefile b/minix/drivers/usb/Makefile index 992e26ac4..6ebeeb593 100644 --- a/minix/drivers/usb/Makefile +++ b/minix/drivers/usb/Makefile @@ -4,6 +4,6 @@ SUBDIR+= usbd .endif # ${MACHINE_ARCH} == "earm" -SUBDIR+= usb_storage +SUBDIR+= usb_storage usb_hub .include diff --git a/minix/drivers/usb/usb_hub/Makefile b/minix/drivers/usb/usb_hub/Makefile new file mode 100644 index 000000000..acfd5406c --- /dev/null +++ b/minix/drivers/usb/usb_hub/Makefile @@ -0,0 +1,16 @@ +# Makefile for USB hub driver +PROG=usb_hub +SRCS=usb_hub.c urb_helper.c + +FILES=$(PROG).conf +FILESNAME=$(PROG) +FILESDIR=/etc/system.conf.d + +DPADD+=${LIBDDEKIT_USB_CLIENT} ${LIBDDEKIT} ${LIBUSB} ${LIBSYS} ${LIBMINLIB} +LDADD+=-lddekit_usb_client -lddekit -lusb -lsys + +#For easier debugging, uncomment: +#LDADD+=-Wl,-Ttext=0x800000 +#CPPFLAGS+=-DHUB_DEBUG + +.include diff --git a/minix/drivers/usb/usb_hub/common.h b/minix/drivers/usb/usb_hub/common.h new file mode 100644 index 000000000..de891acbd --- /dev/null +++ b/minix/drivers/usb/usb_hub/common.h @@ -0,0 +1,35 @@ +/* + * Whatever is commonly used in hub driver, should be here + */ + +#ifndef _COMMON_H_ +#define _COMMON_H_ + +/*---------------------------* + * commonly used headers: * + *---------------------------*/ +#include /* For things, like EXIT_*, NULL, ... */ +#include + +/*---------------------------* + * commonly used defines: * + *---------------------------*/ +#define THIS_EXEC_NAME "usb_hub" +#define HUB_MSG(...) do { \ + printf(THIS_EXEC_NAME": "); \ + printf(__VA_ARGS__); \ + printf("; %s:%d\n", __func__, __LINE__); \ + } while(0) + +/*---------------------------* + * debug helpers: * + *---------------------------*/ +#ifdef HUB_DEBUG +#define HUB_DEBUG_MSG HUB_MSG +#define HUB_DEBUG_DUMP printf("%s():%d\n", __func__, __LINE__) +#else +#define HUB_DEBUG_MSG(...) +#define HUB_DEBUG_DUMP +#endif + +#endif /* !_COMMON_H_ */ diff --git a/minix/drivers/usb/usb_hub/urb_helper.c b/minix/drivers/usb/usb_hub/urb_helper.c new file mode 100644 index 000000000..c7a93a35c --- /dev/null +++ b/minix/drivers/usb/usb_hub/urb_helper.c @@ -0,0 +1,111 @@ +/* + * URB formatting related implementation + */ + +#include /* panic */ +#include /* struct usb_ctrlrequest */ + +#include /* memset */ +#include + +#include "common.h" +#include "urb_helper.h" + +/*---------------------------* + * defined functions * + *---------------------------*/ +/*===========================================================================* + * init_urb * + *===========================================================================*/ +void +init_urb(struct ddekit_usb_urb * urb, struct ddekit_usb_dev * dev, + urb_ep_config * conf) +{ + HUB_DEBUG_DUMP; + + /* Sanity checks */ + assert(NULL != urb); + assert(NULL != dev); + assert((DDEKIT_USB_TRANSFER_BLK == conf->type) || + (DDEKIT_USB_TRANSFER_CTL == conf->type) || + (DDEKIT_USB_TRANSFER_INT == conf->type) || + (DDEKIT_USB_TRANSFER_ISO == conf->type)); + assert((conf->ep_num >= 0) && (conf->ep_num < 16)); + assert((DDEKIT_USB_IN == conf->direction) || + (DDEKIT_USB_OUT == conf->direction)); + + /* Clear block first */ + memset(urb, 0, sizeof(*urb)); + + /* Set supplied values */ + urb->dev = dev; + urb->type = conf->type; + urb->endpoint = conf->ep_num; + urb->direction = conf->direction; + urb->interval = conf->interval; +} + + +/*===========================================================================* + * attach_urb_data * + *===========================================================================*/ +void +attach_urb_data(struct ddekit_usb_urb * urb, int buf_type, + void * buf, ddekit_uint32_t buf_len) +{ + HUB_DEBUG_DUMP; + + assert(NULL != urb); + assert(NULL != buf); + + /* Mutual exclusion */ + if (URB_BUF_TYPE_DATA == buf_type) { + urb->data = buf; + urb->size = buf_len; + } else if ( URB_BUF_TYPE_SETUP == buf_type ) { + assert(sizeof(struct usb_ctrlrequest) == buf_len); + urb->setup_packet = buf; + } else + panic("Unexpected buffer type!"); +} + + +/*===========================================================================* + * blocking_urb_submit * + *===========================================================================*/ +int +blocking_urb_submit(struct ddekit_usb_urb * urb, ddekit_sem_t * sem, + int check_len) +{ + HUB_DEBUG_DUMP; + + assert(NULL != urb); + assert(NULL != sem); + assert((check_len == URB_SUBMIT_CHECK_LEN) || + (check_len == URB_SUBMIT_ALLOW_MISMATCH)); + + /* Submit and block until semaphore gets up */ + if (ddekit_usb_submit_urb(urb)) { + HUB_MSG("Submitting DDEKit URB failed"); + return EXIT_FAILURE; + } else { + /* Submitting succeeded so block and wait for reply */ + ddekit_sem_down(sem); + + /* Check for DDEKit status first */ + if (urb->status) { + HUB_MSG("Invalid DDEKit URB status"); + return EXIT_FAILURE; + } else { + if (URB_SUBMIT_CHECK_LEN == check_len) { + /* Compare lengths */ + if (urb->actual_length != urb->size) { + HUB_MSG("URB different than expected"); + return EXIT_FAILURE; + } + } + + return EXIT_SUCCESS; + } + } +} diff --git a/minix/drivers/usb/usb_hub/urb_helper.h b/minix/drivers/usb/usb_hub/urb_helper.h new file mode 100644 index 000000000..c9033c93b --- /dev/null +++ b/minix/drivers/usb/usb_hub/urb_helper.h @@ -0,0 +1,47 @@ +/* + * URB formatting related definitions + */ + +#ifndef _URB_HELPER_H_ +#define _URB_HELPER_H_ + +#include +#include + +/* Possible values for attach_urb_data's buf_type */ +/* Both may be used for single URB */ +#define URB_BUF_TYPE_DATA 0 /* attached buffer is data buffer */ +#define URB_BUF_TYPE_SETUP 1 /* attached buffer is setup structure */ + +/* Possible values for blocking_urb_submit's check_len */ +/* Use URB_SUBMIT_CHECK_LEN when actual data buffer length returned + * by HCD must match expected length, supplied in attach_urb_data */ +#define URB_SUBMIT_CHECK_LEN 0 /* return error on length mismatch */ +#define URB_SUBMIT_ALLOW_MISMATCH 1 /* ignore length check */ + +/* Endpoint configuration related */ +#define URB_INVALID_EP (-1) /* default for unset endpoint */ + +/*---------------------------* + * declared types * + *---------------------------*/ +/* URB's endpoint configuration */ +typedef struct urb_ep_config { + + ddekit_int32_t ep_num; + ddekit_int32_t direction; + ddekit_int32_t type; + ddekit_int32_t max_packet_size; + ddekit_int32_t interval; +} +urb_ep_config; + +/*---------------------------* + * declared functions * + *---------------------------*/ +void init_urb(struct ddekit_usb_urb *, struct ddekit_usb_dev *, + urb_ep_config *); +void attach_urb_data(struct ddekit_usb_urb *, int, void *, ddekit_uint32_t); +int blocking_urb_submit(struct ddekit_usb_urb *, ddekit_sem_t *, int); + +#endif /* !_URB_HELPER_H_ */ diff --git a/minix/drivers/usb/usb_hub/usb_hub.c b/minix/drivers/usb/usb_hub/usb_hub.c new file mode 100644 index 000000000..6925f681c --- /dev/null +++ b/minix/drivers/usb/usb_hub/usb_hub.c @@ -0,0 +1,937 @@ +/* + * Minix3 USB hub driver implementation + */ + +#include /* memset */ +#include +#include /* nanosleep */ + +#include +#include +#include /* panic */ +#include /* usb_ctrlrequest TODO: remove me */ + +#include "common.h" +#include "urb_helper.h" + + +/*---------------------------* + * declared functions * + *---------------------------*/ +/* TODO: these are missing from DDE header files */ +extern void ddekit_minix_wait_exit(void); +extern void ddekit_shutdown(void); + +/* SEF related functions */ +static int hub_sef_hdlr(int, sef_init_info_t *); +static void hub_signal_handler(int); + +/* DDEKit IPC related */ +static void ddekit_usb_task(void *); + +/* DDEKit's USB driver callbacks */ +static void usb_driver_completion(void *); +static void usb_driver_connect(struct ddekit_usb_dev *, unsigned int); +static void usb_driver_disconnect(struct ddekit_usb_dev *); + +/* Hub driver main task */ +static void hub_task(void *); + + +/*---------------------------* + * class specific stuff * + *---------------------------*/ +#define HUB_PACKED __attribute__((__packed__)) + +/* How often to check for changes */ +#define USB_HUB_POLLING_INTERVAL 1000 + +/* Max number of hub ports */ +#define USB_HUB_PORT_LIMIT 8 + +/* 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 + +/* Hub descriptor structure */ +typedef struct HUB_PACKED hub_descriptor { + + uint8_t bDescLength; + uint8_t bDescriptorType; + uint8_t bNbrPorts; + uint16_t wHubCharacteristics; + uint8_t bPwrOn2PwrGood; + uint8_t bHubContrCurrent; + /* Remaining variable length fields are ignored for now */ +} +hub_descriptor; + +/* Hub port status structure, as defined in USB 2.0 document */ +typedef struct HUB_PACKED hub_port_status { + + uint32_t PORT_CONNECTION : 1; + uint32_t PORT_ENABLE : 1; + uint32_t PORT_SUSPEND : 1; + uint32_t PORT_OVER_CURRENT : 1; + uint32_t PORT_RESET : 1; + uint32_t RESERVED1 : 3; + + uint32_t PORT_POWER : 1; + uint32_t PORT_LOW_SPEED : 1; + uint32_t PORT_HIGH_SPEED : 1; + uint32_t PORT_TEST : 1; + uint32_t PORT_INDICATOR : 1; + uint32_t RESERVED2 : 3; + + uint32_t C_PORT_CONNECTION : 1; + uint32_t C_PORT_ENABLE : 1; + uint32_t C_PORT_SUSPEND : 1; + uint32_t C_PORT_OVER_CURRENT : 1; + uint32_t C_PORT_RESET : 1; + uint32_t RESERVED3 : 11; +} +hub_port_status; + +/* Hub Class Feature Selectors */ +typedef enum { + + C_HUB_LOCAL_POWER = 0 , + C_HUB_OVER_CURRENT = 1 , + PORT_CONNECTION = 0 , + PORT_ENABLE = 1 , + PORT_SUSPEND = 2 , + PORT_OVER_CURRENT = 3 , + PORT_RESET = 4 , + PORT_POWER = 8 , + PORT_LOW_SPEED = 9 , + C_PORT_CONNECTION = 16, + C_PORT_ENABLE = 17, + C_PORT_SUSPEND = 18, + C_PORT_OVER_CURRENT = 19, + C_PORT_RESET = 20, + PORT_TEST = 21, + PORT_INDICATOR = 22 +} +class_feature; + +/* Hub Class Request Codes */ +typedef enum { + + GET_STATUS = 0 , + CLEAR_FEATURE = 1 , + RESERVED1 = 2 , + SET_FEATURE = 3 , + RESERVED2 = 4 , + RESERVED3 = 5 , + GET_DESCRIPTOR = 6 , + SET_DESCRIPTOR = 7 , + CLEAR_TT_BUFFER = 8 , + RESET_TT = 9 , + GET_TT_STATE = 10, + STOP_TT = 11 +} +class_code; + +/* Hub port connection state */ +typedef enum { + + HUB_PORT_DISCONN = 0, + HUB_PORT_CONN = 1, + HUB_PORT_ERROR = 2 +} +port_conn; + +/* Hub port connection changes */ +typedef enum { + + HUB_CHANGE_NONE = 0, /* Nothing changed since last poll */ + HUB_CHANGE_CONN = 1, /* Device was just connected */ + HUB_CHANGE_DISCONN= 2, /* Device was just disconnected */ + HUB_CHANGE_STATUS_ERR = 3, /* Port status mismatch */ + HUB_CHANGE_COM_ERR = 4 /* Something wrong happened to driver */ +} +port_change; + +/* Hub get class specific descriptor call */ +static int hub_get_descriptor(hub_descriptor *); + +/* Hub Set/ClearPortFeature call */ +static int hub_port_feature(int, class_code, class_feature); + +/* Hub GetPortStatus call */ +static int hub_get_port_status(int, hub_port_status *); + +/* Handle port status change */ +static port_change hub_handle_change(int, hub_port_status *); + +/* Handle port connection */ +static int hub_handle_connection(int, hub_port_status *); + +/* Handle port disconnection */ +static int hub_handle_disconnection(int); + + +/*---------------------------* + * defined variables * + *---------------------------*/ +/* USB hub driver state */ +typedef struct hub_state { + + hub_descriptor descriptor; /* Class specific descriptor */ + struct ddekit_usb_dev * dev; /* DDEKit device */ + int num_ports; /* Number of hub ports */ + port_conn conn[USB_HUB_PORT_LIMIT]; /* Map of connected ports */ +} +hub_state; + +/* Current hub driver state */ +static hub_state driver_state; + +/* USB callback structure */ +static struct ddekit_usb_driver usb_driver = { + .completion = usb_driver_completion, + .connect = usb_driver_connect, + .disconnect = usb_driver_disconnect +}; + +/* Semaphore used to block hub thread to + * allow DDE dispatcher operation */ +static ddekit_sem_t * hub_sem = NULL; + +/* USB hub thread */ +ddekit_thread_t * hub_thread = NULL; + +/* DDEKit USB message handling thread */ +ddekit_thread_t * ddekit_usb_thread = NULL; + + +/*---------------------------* + * defined functions * + *---------------------------*/ +/*===========================================================================* + * main * + *===========================================================================*/ +int +main(int argc, char * argv[]) +{ + HUB_MSG("Starting driver... (built: %s %s)", __DATE__, __TIME__); + + /* Store arguments for future parsing */ + env_setargs(argc, argv); + + /* Clear current state */ + memset(&driver_state, 0, sizeof(driver_state)); + + /* Initialize SEF related callbacks */ + sef_setcb_init_fresh(hub_sef_hdlr); + sef_setcb_init_lu(hub_sef_hdlr); + sef_setcb_init_restart(hub_sef_hdlr); + sef_setcb_signal_handler(hub_signal_handler); + + /* Initialize DDEkit (involves sef_startup()) */ + ddekit_init(); + HUB_DEBUG_MSG("DDEkit ready..."); + + /* Semaphore initialization */ + hub_sem = ddekit_sem_init(0); + if (NULL == hub_sem) + panic("Initializing USB hub semaphore, failed!"); + + /* Starting hub thread */ + hub_thread = ddekit_thread_create(hub_task, NULL, "hub_task"); + if (NULL == hub_thread) + panic("Initializing USB hub thread failed!"); + + HUB_DEBUG_MSG("USB HUB task ready..."); + + /* Run USB IPC task to collect messages */ + ddekit_usb_thread = ddekit_thread_create(ddekit_usb_task, NULL, + "ddekit_task" ); + if (NULL == ddekit_usb_thread) + panic("Initializing ddekit_usb_thread failed!"); + + HUB_DEBUG_MSG("USB IPC task ready..."); + + /* Block and wait until exit signal is received */ + ddekit_minix_wait_exit(); + HUB_DEBUG_MSG("Exiting..."); + + /* Release objects that were explicitly allocated above */ + ddekit_thread_terminate(ddekit_usb_thread); + ddekit_thread_terminate(hub_thread); + ddekit_sem_deinit(hub_sem); + + /* TODO: No ddekit_deinit for proper cleanup? */ + + HUB_DEBUG_MSG("Cleanup completed..."); + + return EXIT_SUCCESS; +} + + +/*===========================================================================* + * hub_sef_hdlr * + *===========================================================================*/ +static int +hub_sef_hdlr(int type, sef_init_info_t * UNUSED(info)) +{ + HUB_DEBUG_DUMP; + + switch (type) { + case SEF_INIT_FRESH: + return EXIT_SUCCESS; + case SEF_INIT_LU: + case SEF_INIT_RESTART: + HUB_MSG("Only 'fresh' SEF initialization supported"); + break; + default: + HUB_MSG("Illegal SEF type"); + break; + } + + return EXIT_FAILURE; +} + + +/*===========================================================================* + * hub_signal_handler * + *===========================================================================*/ +static void +hub_signal_handler(int this_signal) +{ + HUB_DEBUG_DUMP; + + HUB_MSG("Handling signal 0x%X", this_signal); + + /* TODO: Any signal means shutdown for now (it may be OK anyway) */ + /* Try graceful DDEKit exit */ + ddekit_shutdown(); + + /* Unreachable, when ddekit_shutdown works correctly */ + panic("Calling ddekit_shutdown failed!"); +} + + +/*===========================================================================* + * ddekit_usb_task * + *===========================================================================*/ +static void +ddekit_usb_task(void * UNUSED(arg)) +{ + HUB_DEBUG_DUMP; + + /* TODO: This call was meant to return 'int' but loops forever instead, + * so no return value is checked */ + ddekit_usb_init(&usb_driver, NULL, NULL); +} + + +/*===========================================================================* + * usb_driver_completion * + *===========================================================================*/ +static void +usb_driver_completion(void * UNUSED(priv)) +{ + HUB_DEBUG_DUMP; + + /* Last request was completed so allow continuing + * execution from place where semaphore was downed */ + ddekit_sem_up(hub_sem); +} + + +/*===========================================================================* + * usb_driver_connect * + *===========================================================================*/ +static void +usb_driver_connect(struct ddekit_usb_dev * dev, unsigned int interfaces) +{ + HUB_DEBUG_DUMP; + + if (NULL != driver_state.dev) + panic("HUB device driver can be connected only once!"); + + /* Clear current state */ + memset(&driver_state, 0, sizeof(driver_state)); + + /* Hold host information for future use */ + driver_state.dev = dev; + + /* Let driver logic work */ + ddekit_sem_up(hub_sem); +} + + +/*===========================================================================* + * usb_driver_disconnect * + *===========================================================================*/ +static void +usb_driver_disconnect(struct ddekit_usb_dev * UNUSED(dev)) +{ + HUB_DEBUG_DUMP; + + if (NULL == driver_state.dev) + panic("HUB device driver was never connected!"); + + /* Discard connected device information */ + driver_state.dev = NULL; +} + + +/*===========================================================================* + * hub_task * + *===========================================================================*/ +static void +hub_task(void * UNUSED(arg)) +{ + hub_port_status port_status; + hub_state * s; + hub_descriptor * d; + int port; + + HUB_DEBUG_DUMP; + + /* For short */ + s = &(driver_state); + d = &(s->descriptor); + + /* Wait for connection */ + ddekit_sem_down(hub_sem); + + if (hub_get_descriptor(d)) { + HUB_MSG("Getting hub descriptor failed"); + goto HUB_ERROR; + } + + /* Output hub descriptor in debug mode */ + HUB_DEBUG_MSG("bDescLength %4X", d->bDescLength); + HUB_DEBUG_MSG("bDescriptorType %4X", d->bDescriptorType); + HUB_DEBUG_MSG("bNbrPorts %4X", d->bNbrPorts); + HUB_DEBUG_MSG("wHubCharacteristics %4X", d->wHubCharacteristics); + HUB_DEBUG_MSG("bPwrOn2PwrGood %4X", d->bPwrOn2PwrGood); + HUB_DEBUG_MSG("bHubContrCurrent %4X", d->bHubContrCurrent); + + /* Check for sane number of ports... */ + if (d->bNbrPorts > USB_HUB_PORT_LIMIT) { + HUB_MSG("Too many hub ports declared: %d", d->bNbrPorts); + goto HUB_ERROR; + } + + /* ...and reassign */ + s->num_ports = (int)d->bNbrPorts; + + /* Initialize all available ports starting + * from 1, as defined by USB 2.0 document */ + for (port = 1; port <= s->num_ports; port++) { + if (hub_port_feature(port, SET_FEATURE, PORT_POWER)) { + HUB_MSG("Powering port%d failed", port); + goto HUB_ERROR; + } + } + + /* + * Connection polling loop + */ + for (;;) { + for (port = 1; port <= s->num_ports; port++) { + + /* Ignore previously blocked ports */ + if (HUB_PORT_ERROR == s->conn[port]) { + HUB_DEBUG_MSG("Blocked hub port ignored"); + continue; + } + + /* Get port status */ + if (hub_get_port_status(port, &port_status)) { + HUB_MSG("Reading port%d status failed", port); + goto HUB_ERROR; + } + + /* Resolve port changes */ + switch (hub_handle_change(port, &port_status)) { + + case HUB_CHANGE_NONE: + break; + + case HUB_CHANGE_CONN: + s->conn[port] = HUB_PORT_CONN; + break; + + case HUB_CHANGE_DISCONN: + s->conn[port] = HUB_PORT_DISCONN; + break; + + case HUB_CHANGE_STATUS_ERR: + /* Turn off port */ + if (hub_port_feature(port, + CLEAR_FEATURE, + PORT_POWER)) { + HUB_MSG("Halting port%d " + "failed", port); + goto HUB_ERROR; + } + /* Block this port forever */ + s->conn[port] = HUB_PORT_ERROR; + + HUB_MSG("Port%d status ERROR", port); + HUB_MSG("Port%d will be blocked, until " + "hub is detached", port); + + break; + + case HUB_CHANGE_COM_ERR: + /* Serious error, hang */ + HUB_MSG("Handling port%d " + "change failed", port); + goto HUB_ERROR; + } + } + + ddekit_thread_msleep(USB_HUB_POLLING_INTERVAL); + HUB_DEBUG_MSG("Polling USB hub for status change"); + } + + return; + + HUB_ERROR: + for (;;) { + /* Hang till removed by devmand */ + HUB_MSG("Hub driver error occurred, hanging up"); + ddekit_sem_down(hub_sem); + } +} + + +/*===========================================================================* + * hub_get_descriptor * + *===========================================================================*/ +static int +hub_get_descriptor(hub_descriptor * descriptor) +{ + /* URB to be send */ + struct ddekit_usb_urb urb; + + /* Setup buffer to be attached */ + struct usb_ctrlrequest setup_buf; + + /* Control EP configuration */ + urb_ep_config ep_conf; + + HUB_DEBUG_DUMP; + + /* Initialize EP configuration */ + ep_conf.ep_num = 0; + ep_conf.direction = DDEKIT_USB_IN; + ep_conf.type = DDEKIT_USB_TRANSFER_CTL; + ep_conf.max_packet_size = 0; + ep_conf.interval = 0; + + /* Reset URB and assign given values */ + init_urb(&urb, driver_state.dev, &ep_conf); + + /* Clear setup data */ + memset(&setup_buf, 0, sizeof(setup_buf)); + + /* Class get hub descriptor request */ + setup_buf.bRequestType = 0xA0; + setup_buf.bRequest = 0x06; + setup_buf.wValue = USB_HUB_DESCRIPTOR_TYPE << 8; + setup_buf.wIndex = 0x00; + setup_buf.wLength = sizeof(*descriptor); + + /* Attach buffers to URB */ + attach_urb_data(&urb, URB_BUF_TYPE_SETUP, + &setup_buf, sizeof(setup_buf)); + attach_urb_data(&urb, URB_BUF_TYPE_DATA, + descriptor, sizeof(*descriptor)); + + /* Send and wait for response */ + if (blocking_urb_submit(&urb, hub_sem, URB_SUBMIT_CHECK_LEN)) { + HUB_MSG("Submitting HUB URB failed"); + return EXIT_FAILURE; + } else { + HUB_DEBUG_MSG("HUB descriptor received"); + return EXIT_SUCCESS; + } +} + + +/*===========================================================================* + * hub_port_feature * + *===========================================================================*/ +static int +hub_port_feature(int port_num, class_code code, class_feature feature) +{ + /* URB to be send */ + struct ddekit_usb_urb urb; + + /* Setup buffer to be attached */ + struct usb_ctrlrequest setup_buf; + + /* Control EP configuration */ + urb_ep_config ep_conf; + + HUB_DEBUG_DUMP; + + /* TODO: Add more checks when needed */ + if (!((port_num <= driver_state.num_ports) && (port_num > 0))) + return EXIT_FAILURE; + + if (!((code == SET_FEATURE) || (code == CLEAR_FEATURE))) + return EXIT_FAILURE; + + if (!((feature == PORT_RESET) || (feature == PORT_POWER) || + (feature == C_PORT_CONNECTION) || (feature == C_PORT_RESET))) + return EXIT_FAILURE; + + /* Initialize EP configuration */ + ep_conf.ep_num = 0; + ep_conf.direction = DDEKIT_USB_OUT; + ep_conf.type = DDEKIT_USB_TRANSFER_CTL; + ep_conf.max_packet_size = 0; + ep_conf.interval = 0; + + /* Reset URB and assign given values */ + init_urb(&urb, driver_state.dev, &ep_conf); + + /* Clear setup data */ + memset(&setup_buf, 0, sizeof(setup_buf)); + + /* Standard get endpoint request */ + setup_buf.bRequestType = 0x23; + setup_buf.bRequest = (u8_t)code; + setup_buf.wValue = (u16_t)feature; + setup_buf.wIndex = (u16_t)port_num; + setup_buf.wLength = 0; + + /* Attach buffers to URB */ + attach_urb_data(&urb, URB_BUF_TYPE_SETUP, + &setup_buf, sizeof(setup_buf)); + + /* Send and wait for response */ + if (blocking_urb_submit(&urb, hub_sem, URB_SUBMIT_CHECK_LEN)) { + HUB_MSG("Submitting HUB URB failed"); + return EXIT_FAILURE; + } else { + HUB_DEBUG_MSG("PortFeature operation completed"); + return EXIT_SUCCESS; + } +} + + +/*===========================================================================* + * hub_get_port_status * + *===========================================================================*/ +static int +hub_get_port_status(int port_num, hub_port_status * p) +{ + /* URB to be send */ + struct ddekit_usb_urb urb; + + /* Setup buffer to be attached */ + struct usb_ctrlrequest setup_buf; + + /* Control EP configuration */ + urb_ep_config ep_conf; + + HUB_DEBUG_DUMP; + + if (!((port_num <= driver_state.num_ports) && (port_num > 0))) + return EXIT_FAILURE; + + /* Initialize EP configuration */ + ep_conf.ep_num = 0; + ep_conf.direction = DDEKIT_USB_IN; + ep_conf.type = DDEKIT_USB_TRANSFER_CTL; + ep_conf.max_packet_size = 0; + ep_conf.interval = 0; + + /* Reset URB and assign given values */ + init_urb(&urb, driver_state.dev, &ep_conf); + + /* Clear setup data */ + memset(&setup_buf, 0, sizeof(setup_buf)); + + /* Standard get endpoint request */ + setup_buf.bRequestType = 0xA3; + setup_buf.bRequest = (u8_t)GET_STATUS; + setup_buf.wValue = 0x00; + setup_buf.wIndex = (u16_t)port_num; + setup_buf.wLength = sizeof(*p); + + /* Attach buffers to URB */ + attach_urb_data(&urb, URB_BUF_TYPE_SETUP, + &setup_buf, sizeof(setup_buf)); + attach_urb_data(&urb, URB_BUF_TYPE_DATA, + p, sizeof(*p)); + + /* Send and wait for response */ + if (blocking_urb_submit(&urb, hub_sem, URB_SUBMIT_CHECK_LEN)) { + HUB_MSG("Submitting HUB URB failed"); + return EXIT_FAILURE; + } else { + HUB_DEBUG_MSG("Port%d status: ", port_num); + HUB_DEBUG_MSG("PORT_CONNECTION %01X", p->PORT_CONNECTION); + HUB_DEBUG_MSG("PORT_ENABLE %01X", p->PORT_ENABLE); + HUB_DEBUG_MSG("PORT_POWER %01X", p->PORT_POWER); + HUB_DEBUG_MSG("C_PORT_CONNECTION %01X", p->C_PORT_CONNECTION); + HUB_DEBUG_MSG("C_PORT_ENABLE %01X", p->C_PORT_ENABLE); + return EXIT_SUCCESS; + } +} + + +/*===========================================================================* + * hub_handle_change * + *===========================================================================*/ +static port_change +hub_handle_change(int port_num, hub_port_status * status) +{ + port_conn * c; + + HUB_DEBUG_DUMP; + + /* Possible combinations: */ + /* Change = status->C_PORT_CONNECTION (hub connection change bit) + * Local = driver_state.conn[port_num] (local connection status) + * Remote = status->PORT_CONNECTION (hub connection status) */ + /* + Case Change Local Remote Description + 1. 1 1 1 Polling mismatch (quick disconn-conn) + 2. 1 1 0 Just disconnected + 3. 1 0 1 Just connected + 4. 1 0 0 Polling mismatch (quick conn-disconn) + 5. 0 1 1 Still connected + 6. 0 1 0 Serious ERROR + 7. 0 0 1 Serious ERROR + 8. 0 0 0 Still disconnected + */ + + /* Reassign for code cleanliness */ + c = driver_state.conn; + + /* Resolve combination */ + if (status->C_PORT_CONNECTION) { + + /* C_PORT_CONNECTION was set, so clear change bit + * to allow further polling */ + if (hub_port_feature(port_num, CLEAR_FEATURE, + C_PORT_CONNECTION)) { + HUB_MSG("Clearing port%d change bit failed", port_num); + return HUB_CHANGE_COM_ERR; + } + + if (HUB_PORT_CONN == c[port_num]) { + if (status->PORT_CONNECTION) { + + /* + * 1 + */ + /* Make hub disconnect and connect again */ + if (hub_handle_disconnection(port_num) || + hub_handle_connection(port_num, status)) + return HUB_CHANGE_STATUS_ERR; + else + return HUB_CHANGE_CONN; + + } else { + + /* + * 2 + */ + /* Handle disconnection */ + if (hub_handle_disconnection(port_num)) + return HUB_CHANGE_STATUS_ERR; + else + return HUB_CHANGE_DISCONN; + + } + } else if (HUB_PORT_DISCONN == c[port_num]) { + if (status->PORT_CONNECTION) { + + /* + * 3 + */ + /* Handle connection */ + if (hub_handle_connection(port_num, status)) + return HUB_CHANGE_STATUS_ERR; + else + return HUB_CHANGE_CONN; + + } else { + + /* + * 4 + */ + /* Since we were disconnected before and + * are disconnected now, additional handling + * may be ignored */ + return HUB_CHANGE_NONE; + + } + } + } else { + if (HUB_PORT_CONN == c[port_num]) { + if (status->PORT_CONNECTION) { + + /* + * 5 + */ + /* Connected (nothing changed) */ + return HUB_CHANGE_NONE; + + } else { + + /* + * 6 + */ + /* Serious status error */ + return HUB_CHANGE_STATUS_ERR; + + } + } else if (HUB_PORT_DISCONN == c[port_num]) { + if (status->PORT_CONNECTION) { + + /* + * 7 + */ + /* Serious status error */ + return HUB_CHANGE_STATUS_ERR; + + } else { + + /* + * 8 + */ + /* Disconnected (nothing changed) */ + return HUB_CHANGE_NONE; + + } + } + } + + return HUB_CHANGE_COM_ERR; +} + + +/*===========================================================================* + * hub_handle_connection * + *===========================================================================*/ +static int +hub_handle_connection(int port_num, hub_port_status * status) +{ + struct timespec wait_time; + int reset_tries; + long port_speed; + + HUB_DEBUG_DUMP; + + HUB_MSG("Device connected to port%d", port_num); + + /* This should never happen if power-off works as intended */ + if (status->C_PORT_RESET) { + HUB_MSG("Unexpected reset state for port%d", port_num); + return EXIT_FAILURE; + } + + /* Start reset signaling for this port */ + if (hub_port_feature(port_num, SET_FEATURE, PORT_RESET)) { + HUB_MSG("Resetting port%d failed", port_num); + return EXIT_FAILURE; + } + + 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) { + /* To avoid endless loop */ + if (reset_tries >= USB_HUB_MAX_TRIES) { + HUB_MSG("Port%d reset took too long", port_num); + return EXIT_FAILURE; + } + + /* Get port status again */ + if (hub_get_port_status(port_num, status)) { + HUB_MSG("Reading port%d status failed", port_num); + return EXIT_FAILURE; + } + + 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 */ + HUB_DEBUG_MSG("Port%d reset complete", port_num); + + /* Dump full status for analysis (high-speed, ...) */ + HUB_DEBUG_MSG("C_PORT_CONNECTION %1X", status->C_PORT_CONNECTION ); + HUB_DEBUG_MSG("C_PORT_ENABLE %1X", status->C_PORT_ENABLE ); + HUB_DEBUG_MSG("C_PORT_OVER_CURRENT %1X", status->C_PORT_OVER_CURRENT); + HUB_DEBUG_MSG("C_PORT_RESET %1X", status->C_PORT_RESET ); + HUB_DEBUG_MSG("C_PORT_SUSPEND %1X", status->C_PORT_SUSPEND ); + HUB_DEBUG_MSG("PORT_CONNECTION %1X", status->PORT_CONNECTION ); + HUB_DEBUG_MSG("PORT_ENABLE %1X", status->PORT_ENABLE ); + HUB_DEBUG_MSG("PORT_HIGH_SPEED %1X", status->PORT_HIGH_SPEED ); + HUB_DEBUG_MSG("PORT_INDICATOR %1X", status->PORT_INDICATOR ); + HUB_DEBUG_MSG("PORT_LOW_SPEED %1X", status->PORT_LOW_SPEED ); + HUB_DEBUG_MSG("PORT_OVER_CURRENT %1X", status->PORT_OVER_CURRENT ); + HUB_DEBUG_MSG("PORT_POWER %1X", status->PORT_POWER ); + HUB_DEBUG_MSG("PORT_RESET %1X", status->PORT_RESET ); + HUB_DEBUG_MSG("PORT_SUSPEND %1X", status->PORT_SUSPEND ); + HUB_DEBUG_MSG("PORT_TEST %1X", status->PORT_TEST ); + + /* Clear reset change bit for further devices */ + if (hub_port_feature(port_num, CLEAR_FEATURE, C_PORT_RESET)) { + HUB_MSG("Clearing port%d reset bit failed", port_num); + return EXIT_FAILURE; + } + + /* Should never happen */ + if (!status->PORT_CONNECTION || !status->PORT_ENABLE) { + HUB_MSG("Port%d unexpectedly unavailable", port_num); + return EXIT_FAILURE; + } + + /* Determine port speed from status bits */ + if (status->PORT_LOW_SPEED) { + if (status->PORT_HIGH_SPEED) { + HUB_MSG("Port%d has invalid speed flags", port_num); + return EXIT_FAILURE; + } else + port_speed = (long)DDEKIT_HUB_PORT_LS_CONN; + } else { + if (status->PORT_HIGH_SPEED) + port_speed = (long)DDEKIT_HUB_PORT_HS_CONN; + else + port_speed = (long)DDEKIT_HUB_PORT_FS_CONN; + } + + /* Signal to HCD that port has device connected at given speed */ + return ddekit_usb_info(driver_state.dev, port_speed, (long)port_num); +} + + +/*===========================================================================* + * hub_handle_disconnection * + *===========================================================================*/ +static int +hub_handle_disconnection(int port_num) +{ + HUB_DEBUG_DUMP; + + HUB_MSG("Device disconnected from port%d", port_num); + + return ddekit_usb_info(driver_state.dev, (long)DDEKIT_HUB_PORT_DISCONN, + (long)port_num); +} diff --git a/minix/drivers/usb/usb_hub/usb_hub.conf b/minix/drivers/usb/usb_hub/usb_hub.conf new file mode 100644 index 000000000..f0fecca84 --- /dev/null +++ b/minix/drivers/usb/usb_hub/usb_hub.conf @@ -0,0 +1,15 @@ +service usb_hub +{ + system + PRIVCTL # 4 + UMAP # 14 + IRQCTL # 19 + DEVIO # 21 + SDEVIO # 22 + ; + ipc + SYSTEM pm rs log tty ds vfs vm amddev devman + pci usbd + ; + uid 0; +}; diff --git a/minix/drivers/usb/usb_storage/scsi.c b/minix/drivers/usb/usb_storage/scsi.c index 8ed74d419..128039f8b 100644 --- a/minix/drivers/usb/usb_storage/scsi.c +++ b/minix/drivers/usb/usb_storage/scsi.c @@ -20,6 +20,7 @@ static int create_read_capacity_scsi_cmd(mass_storage_cbw *); static int create_write_scsi_cmd(mass_storage_cbw *, scsi_transfer *); static int create_read_scsi_cmd(mass_storage_cbw *, scsi_transfer *); static int create_mode_sense_scsi_cmd(mass_storage_cbw *); +static int create_request_sense_scsi_cmd(mass_storage_cbw *); /*---------------------------* * defined functions * @@ -47,6 +48,8 @@ create_scsi_cmd(mass_storage_cbw * cbw, int cmd, scsi_transfer * info) return create_read_scsi_cmd(cbw, info); case SCSI_MODE_SENSE: return create_mode_sense_scsi_cmd(cbw); + case SCSI_REQUEST_SENSE: + return create_request_sense_scsi_cmd(cbw); default: MASS_MSG("Invalid SCSI command!"); return EXIT_FAILURE; @@ -175,6 +178,25 @@ create_mode_sense_scsi_cmd(mass_storage_cbw * cbw) } +/*===========================================================================* + * create_request_sense_scsi_cmd * + *===========================================================================*/ +static int +create_request_sense_scsi_cmd(mass_storage_cbw * cbw) +{ + MASS_DEBUG_DUMP; + + cbw->dCBWDataTransferLength = SCSI_REQUEST_SENSE_DATA_LEN; + cbw->bCBWFlags = CBW_FLAGS_IN; + cbw->bCDBLength = SCSI_REQUEST_SENSE_CMD_LEN; + + SCSI_SET_REQUEST_SENSE_OP_CODE(cbw->CBWCB); + SCSI_SET_REQUEST_SENSE_ALLOC(cbw->CBWCB, SCSI_REQUEST_SENSE_DATA_LEN); + + return EXIT_SUCCESS; +} + + /*===========================================================================* * check_inquiry_reply * *===========================================================================*/ diff --git a/minix/drivers/usb/usb_storage/scsi.h b/minix/drivers/usb/usb_storage/scsi.h index 91d28ef75..32a728b7c 100644 --- a/minix/drivers/usb/usb_storage/scsi.h +++ b/minix/drivers/usb/usb_storage/scsi.h @@ -27,25 +27,13 @@ #include "bulk.h" -#define SCSI_FORMAT_UNIT (0x04) #define SCSI_INQUIRY (0x12) -#define SCSI_START_STOP (0x1B) -#define SCSI_MODE_SELECT (0x55) #define SCSI_MODE_SENSE (0x5A) -#define SCSI_PREVENT_ALLOW (0x1E) #define SCSI_READ (0x28) -#define SCSI_READ_12 (0xA8) #define SCSI_READ_CAPACITY (0x25) -#define SCSI_READ_FORMAT_CAP (0x23) #define SCSI_REQUEST_SENSE (0x03) -#define SCSI_REZERO_UNIT (0x01) -#define SCSI_SEEK (0x2B) -#define SCSI_SEND_DIAGNOSTIC (0x1D) #define SCSI_TEST_UNIT_READY (0x00) -#define SCSI_VERIFY (0x2F) #define SCSI_WRITE (0x2A) -#define SCSI_WRITE_12 (0xAA) -#define SCSI_WRITE_VERIFY (0x2E) #define SCSI_INQUIRY_DATA_LEN (36) #define SCSI_INQUIRY_CMD_LEN (6) @@ -59,6 +47,9 @@ #define SCSI_READ_CAPACITY_DATA_LEN (8) #define SCSI_READ_CAPACITY_CMD_LEN (10) +#define SCSI_REQUEST_SENSE_DATA_LEN (18) +#define SCSI_REQUEST_SENSE_CMD_LEN (6) + #define SCSI_TEST_DATA_LEN (0) #define SCSI_TEST_CMD_LEN (6) @@ -117,6 +108,10 @@ #define SCSI_GET_READ_CAPACITY_LBA(x) SCSI_RD4((x), 0) #define SCSI_GET_READ_CAPACITY_BLEN(x) SCSI_RD4((x), 4) +#define SCSI_SET_REQUEST_SENSE_OP_CODE(x) SCSI_WR1((x), 0, \ + SCSI_REQUEST_SENSE) +#define SCSI_SET_REQUEST_SENSE_ALLOC(x, alloc) SCSI_WR1((x), 4, (alloc)) + #define SCSI_SET_TEST_OP_CODE(x) SCSI_WR1((x), 0, \ SCSI_TEST_UNIT_READY) diff --git a/minix/drivers/usb/usb_storage/usb_storage.c b/minix/drivers/usb/usb_storage/usb_storage.c index ecafdccba..c40789a37 100644 --- a/minix/drivers/usb/usb_storage/usb_storage.c +++ b/minix/drivers/usb/usb_storage/usb_storage.c @@ -3,6 +3,7 @@ * using DDEkit, and libblockdriver */ +#include /* __CTASSERT() */ #include /* cases for mass_storage_ioctl */ #ifdef USB_STORAGE_SIGNAL #include /* signal handling */ @@ -24,6 +25,7 @@ #include #include /* ULONG_MAX */ +#include /* nanosleep */ #include "common.h" #include "bulk.h" @@ -62,6 +64,7 @@ static void ddekit_usb_task(void *); /* Mass storage related prototypes */ static void mass_storage_task(void *); static int mass_storage_test(void); +static int mass_storage_check_error(void); static int mass_storage_try_first_open(void); static int mass_storage_transfer_restrictions(u64_t, unsigned long); static ssize_t mass_storage_write(unsigned long, endpoint_t, iovec_t *, @@ -96,6 +99,8 @@ static int mass_storage_parse_descriptors(char *, unsigned int, urb_ep_config *, /*---------------------------* * defined variables * *---------------------------*/ +#define MASS_PACKED __attribute__((__packed__)) + /* Mass Storage callback structure */ static struct blockdriver mass_storage = { .bdr_type = BLOCKDRIVER_TYPE_DISK, @@ -149,6 +154,10 @@ static unsigned char buffer[BUFFER_SIZE]; /* Maximum 'Test Unit Ready' command retries */ #define MAX_TEST_RETRIES 3 +/* 'Test Unit Ready' failure delay time (in nanoseconds) */ +#define NEXT_TEST_DELAY 50000000 /* 50ms */ + + /*---------------------------* * defined functions * *---------------------------*/ @@ -589,23 +598,121 @@ static int mass_storage_test(void) { int repeat; + int error; + + struct timespec test_wait; MASS_DEBUG_DUMP; + /* Delay between consecutive test commands, in case of their failure */ + test_wait.tv_nsec = NEXT_TEST_DELAY; + test_wait.tv_sec = 0; + for (repeat = 0; repeat < MAX_TEST_RETRIES; repeat++) { /* SCSI TEST UNIT READY OUT stage */ if (mass_storage_send_scsi_cbw_out(SCSI_TEST_UNIT_READY, NULL)) - return EXIT_FAILURE; + return EIO; /* TODO: Only CSW failure should normally contribute to retry */ /* SCSI TEST UNIT READY IN stage */ if (EXIT_SUCCESS == mass_storage_send_scsi_csw_in()) return EXIT_SUCCESS; + + /* Check for errors */ + if (EXIT_SUCCESS != (error = mass_storage_check_error())) { + MASS_MSG("SCSI sense error checking failed"); + return error; + } + + /* Ignore potential signal interruption (no return value check), + * since it causes driver termination anyway */ + if (nanosleep(&test_wait, NULL)) + MASS_MSG("Calling nanosleep() failed"); } - return EXIT_FAILURE; + return EIO; +} + + +/*===========================================================================* + * mass_storage_check_error * + *===========================================================================*/ +static int +mass_storage_check_error(void) +{ + /* SCSI sense structure for local use */ + typedef struct MASS_PACKED scsi_sense { + + uint8_t code : 7; + uint8_t valid : 1; + uint8_t obsolete : 8; + uint8_t sense : 4; + uint8_t reserved : 1; + uint8_t ili : 1; + uint8_t eom : 1; + uint8_t filemark : 1; + uint32_t information : 32; + uint8_t additional_len : 8; + uint32_t command_specific : 32; + uint8_t additional_code : 8; + uint8_t additional_qual : 8; + uint8_t unit_code : 8; + uint8_t key_specific1 : 7; + uint8_t sksv : 1; + uint16_t key_specific2 : 16; + } + scsi_sense; + + /* Sense variable to hold received data */ + scsi_sense sense; + + MASS_DEBUG_DUMP; + + /* Check if bit-fields are packed correctly */ + __CTASSERT(sizeof(sense) == SCSI_REQUEST_SENSE_DATA_LEN); + + /* SCSI REQUEST SENSE OUT stage */ + if (mass_storage_send_scsi_cbw_out(SCSI_REQUEST_SENSE, NULL)) + return EIO; + + /* SCSI REQUEST SENSE first IN stage */ + if (mass_storage_send_scsi_data_in(&sense, sizeof(sense))) + return EIO; + + /* SCSI REQUEST SENSE second IN stage */ + if (mass_storage_send_scsi_csw_in()) + return EIO; + + /* When any sense code is present something may have failed */ + if (sense.sense) { +#ifdef MASS_DEBUG + MASS_MSG("SCSI sense: "); + MASS_MSG("code : %8X", sense.code ); + MASS_MSG("valid : %8X", sense.valid ); + MASS_MSG("obsolete : %8X", sense.obsolete ); + MASS_MSG("sense : %8X", sense.sense ); + MASS_MSG("reserved : %8X", sense.reserved ); + MASS_MSG("ili : %8X", sense.ili ); + MASS_MSG("eom : %8X", sense.eom ); + MASS_MSG("filemark : %8X", sense.filemark ); + MASS_MSG("information : %8X", sense.information ); + MASS_MSG("additional_len : %8X", sense.additional_len ); + MASS_MSG("command_specific : %8X", sense.command_specific); + MASS_MSG("additional_code : %8X", sense.additional_code ); + MASS_MSG("additional_qual : %8X", sense.additional_qual ); + MASS_MSG("unit_code : %8X", sense.unit_code ); + MASS_MSG("key_specific1 : %8X", sense.key_specific1 ); + MASS_MSG("sksv : %8X", sense.sksv ); + MASS_MSG("key_specific2 : %8X", sense.key_specific2 ); +#else + MASS_MSG("SCSI sense: 0x%02X 0x%02X 0x%02X", sense.sense, + sense.additional_code, sense.additional_qual); +#endif + } + + return EXIT_SUCCESS; } @@ -1072,6 +1179,12 @@ mass_storage_open(devminor_t minor, int UNUSED(access)) if ((r = mass_storage_try_first_open())) { MASS_MSG("Opening mass storage device" " for the first time failed"); + + /* Do one more test before failing, to output + * sense errors in case they weren't dumped already */ + if (mass_storage_test()) + MASS_MSG("Final TEST UNIT READY failed"); + return r; } @@ -1356,6 +1469,8 @@ mass_storage_part(devminor_t minor) /*===========================================================================* * mass_storage_geometry * *===========================================================================*/ +/* This command is optional for most mass storage devices + * It should rather be used with USB floppy disk reader */ #ifdef MASS_USE_GEOMETRY static void mass_storage_geometry(devminor_t minor, struct part_geom * part) diff --git a/minix/drivers/usb/usbd/base/earm/Makefile b/minix/drivers/usb/usbd/base/earm/Makefile index c5c7e1726..b0df30ec3 100644 --- a/minix/drivers/usb/usbd/base/earm/Makefile +++ b/minix/drivers/usb/usbd/base/earm/Makefile @@ -1,7 +1,7 @@ # Makefile for the EARM USBD PROG= usbd -SRCS= usbd.c usbd_earm.c hcd.c hcd_common.c hcd_ddekit.c musb_am335x.c musb_core.c +SRCS= usbd.c usbd_earm.c hcd.c hcd_common.c hcd_ddekit.c hcd_schedule.c musb_am335x.c musb_core.c MAN= BINDIR= /service diff --git a/minix/drivers/usb/usbd/base/earm/usbd_earm.c b/minix/drivers/usb/usbd/base/earm/usbd_earm.c index 79d8df3fe..24a08b864 100644 --- a/minix/drivers/usb/usbd/base/earm/usbd_earm.c +++ b/minix/drivers/usb/usbd/base/earm/usbd_earm.c @@ -5,9 +5,9 @@ #include #include -#include -#include -#include +#include +#include +#include /*===========================================================================* diff --git a/minix/drivers/usb/usbd/base/usbd.c b/minix/drivers/usb/usbd/base/usbd.c index f45929cba..809bdce2a 100644 --- a/minix/drivers/usb/usbd/base/usbd.c +++ b/minix/drivers/usb/usbd/base/usbd.c @@ -10,8 +10,9 @@ #include /* Initializing 'devman' */ #include /* SEF handling */ -#include -#include +#include +#include +#include /*===========================================================================* @@ -37,7 +38,7 @@ main(int UNUSED(argc), char * UNUSED(argv[])) int ret_val; USB_MSG("Starting USBD"); - USB_DBG("Built: %s %s", __DATE__, __TIME__); + USB_MSG("Built: %s %s", __DATE__, __TIME__); /* Basic SEF,DDE,... initialization */ usbd_init(); @@ -74,7 +75,7 @@ main(int UNUSED(argc), char * UNUSED(argv[])) static int usbd_sef_handler(int type, sef_init_info_t * UNUSED(info)) { - DEBUG_DUMP; + /* No DEBUG_DUMP, threading unavailable yet */ switch (type) { case SEF_INIT_FRESH: @@ -125,12 +126,21 @@ usbd_start(void) DEBUG_DUMP; /* Driver's "main loop" is within DDEKit server thread */ - usbd_th = ddekit_thread_create(usbd_server_thread, NULL, "USBD"); + usbd_th = ddekit_thread_create(usbd_server_thread, NULL, "ddekit_usb"); /* After spawning, allow server thread to work */ if (NULL != usbd_th) { - /* This will lock current thread until DDEKit exits */ - ddekit_minix_wait_exit(); + + /* Allow URB scheduling */ + if (usbd_init_scheduler()) { + USB_MSG("Failed to start URB scheduler"); + } else { + /* This will lock current thread until DDEKit exits */ + ddekit_minix_wait_exit(); + } + + /* Disallow URB scheduling */ + usbd_deinit_scheduler(); /* Cleanup */ ddekit_thread_terminate(usbd_th); @@ -147,16 +157,18 @@ usbd_start(void) static void usbd_init(void) { - DEBUG_DUMP; + /* No DEBUG_DUMP, threading unavailable yet */ /* Set one handler for all messages */ sef_setcb_init_fresh(usbd_sef_handler); sef_setcb_init_lu(usbd_sef_handler); sef_setcb_init_restart(usbd_sef_handler); - sef_setcb_signal_handler(usbd_signal_handler); /* Initialize DDEkit (involves sef_startup()) */ ddekit_init(); + + /* After threading initialization, add signal handler */ + sef_setcb_signal_handler(usbd_signal_handler); } diff --git a/minix/drivers/usb/usbd/hcd/hcd.c b/minix/drivers/usb/usbd/hcd/hcd.c index 56687c5b7..018ceea1f 100644 --- a/minix/drivers/usb/usbd/hcd/hcd.c +++ b/minix/drivers/usb/usbd/hcd/hcd.c @@ -6,10 +6,11 @@ #include /* errno with sign */ -#include -#include -#include -#include +#include +#include +#include +#include +#include /*===========================================================================* @@ -21,103 +22,223 @@ static void hcd_device_thread(void *); /* Procedure that locks device thread forever in case of error/completion */ static void hcd_device_finish(hcd_device_state *, const char *); +/* Procedure that finds device, waiting for given EP interrupt */ +static hcd_device_state * hcd_get_child_for_ep(hcd_device_state *, hcd_reg1); + +/* For HCD level, hub handling */ +static void hcd_add_child(hcd_device_state *, hcd_reg1, hcd_speed); +static void hcd_delete_child(hcd_device_state *, hcd_reg1); +static void hcd_disconnect_tree(hcd_device_state *); +static void hcd_dump_tree(hcd_device_state *, hcd_reg1); + /* Typical USD device communication procedures */ static int hcd_enumerate(hcd_device_state *); static int hcd_get_device_descriptor(hcd_device_state *); -static int hcd_set_address(hcd_device_state *, hcd_reg1); +static int hcd_set_address(hcd_device_state *); static int hcd_get_descriptor_tree(hcd_device_state *); static int hcd_set_configuration(hcd_device_state *, hcd_reg1); -static int hcd_handle_urb(hcd_device_state *, hcd_urb *); +static void hcd_handle_urb(hcd_device_state *); +static void hcd_complete_urb(hcd_device_state *); static int hcd_control_urb(hcd_device_state *, hcd_urb *); 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 + * but there is no other way to set thread priority for now */ +extern void _ddekit_thread_set_myprio(int); + /*===========================================================================* * Local definitions * *===========================================================================*/ -/* TODO: Only one device at a time - * If ever HUB functionality is added, one must remember that disconnecting - * HUB, means disconnecting every device attached to it, so data structure may - * have to be altered to allow that */ -static hcd_device_state hcd_device[1]; - /* TODO: This was added for compatibility with DDELinux drivers that * 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 * *===========================================================================*/ void -hcd_handle_event(hcd_driver_state * driver) +hcd_handle_event(hcd_device_state * device, hcd_event event, hcd_reg1 val) { - hcd_device_state * this_device; - DEBUG_DUMP; - /* TODO: Finding which hcd_device is in use should be performed here */ - this_device = &(hcd_device[0]); + /* Invalid device may be supplied */ + if (EXIT_SUCCESS != hcd_check_device(device)) { + USB_MSG("No device available for event: 0x%02X, value: 0x%02X", + event, val); + return; + } - /* Sometimes interrupts occur in a weird order (EP after disconnect) - * This helps finding ordering errors in DEBUG */ - USB_DBG("Event: 0x%02X, state: 0x%02X", - driver->current_event, this_device->state); +#ifdef HCD_DUMP_DEVICE_TREE + /* This can be unlocked to dump current USB device tree on event */ + { + /* Go to the base of USB device tree and + * print the current state of it */ + hcd_device_state * base; - /* Set what was received for device thread to use */ - this_device->driver = driver; + base = device; + + while (NULL != base->parent) + base = base->parent; + + USB_MSG("Current state of USB device tree:"); + hcd_dump_tree(base, 0); + } +#endif /* Handle event and forward control to device thread when required */ - switch (driver->current_event) { + switch (event) { case HCD_EVENT_CONNECTED: - if (HCD_STATE_DISCONNECTED == this_device->state) { - if (EXIT_SUCCESS != hcd_connect_device( - this_device, - hcd_device_thread)) - USB_MSG("Device creation failed"); - } else - USB_MSG("Device not marked as 'disconnected' " - "for 'connection' event"); + USB_ASSERT((HCD_STATE_DISCONNECTED == device->state), + "Device not marked as 'disconnected' " + "for 'connection' event"); + + /* Try creating new thread for device */ + if (hcd_connect_device(device, hcd_device_thread)) + USB_MSG("Device creation failed, nothing more " + "will happen until disconnected"); break; case HCD_EVENT_DISCONNECTED: - if (HCD_STATE_DISCONNECTED != this_device->state) { - /* If connect callback was used before, call - * it's equivalent to signal disconnection */ - if (HCD_STATE_CONNECTED == this_device->state) - hcd_disconnect_cb(this_device); - hcd_disconnect_device(this_device); - this_device->state = HCD_STATE_DISCONNECTED; + USB_ASSERT((HCD_STATE_DISCONNECTED != device->state), + "Device is marked as 'disconnected' " + "for 'disconnection' event"); - /* Finally, zero everything to allow - * further connections with this object */ - memset(this_device, 0x00, sizeof(*this_device)); - } else - USB_MSG("Device is marked as 'disconnected' " - "for 'disconnection' event"); + /* Make this device and all attached children + * disconnect recursively */ + hcd_disconnect_tree(device); + + break; + + case HCD_EVENT_PORT_LS_CONNECTED: + USB_ASSERT((HCD_STATE_DISCONNECTED != device->state), + "Device is marked as 'disconnected' " + "for 'hub port LS attach' event"); + + USB_MSG("Low speed device connected at " + "hub 0x%08X, port %u", device, val); + + hcd_add_child(device, val, HCD_SPEED_LOW); + break; + + case HCD_EVENT_PORT_FS_CONNECTED: + USB_ASSERT((HCD_STATE_DISCONNECTED != device->state), + "Device is marked as 'disconnected' " + "for 'hub port FS attach' event"); + + USB_MSG("Full speed device connected at " + "hub 0x%08X, port %u", device, val); + + hcd_add_child(device, val, HCD_SPEED_FULL); + break; + + case HCD_EVENT_PORT_HS_CONNECTED: + USB_ASSERT((HCD_STATE_DISCONNECTED != device->state), + "Device is marked as 'disconnected' " + "for 'hub port HS attach' event"); + + USB_MSG("High speed device connected at " + "hub 0x%08X, port %u", device, val); + + hcd_add_child(device, val, HCD_SPEED_HIGH); + break; + + case HCD_EVENT_PORT_DISCONNECTED: + USB_ASSERT((HCD_STATE_DISCONNECTED != device->state), + "Device is marked as 'disconnected' " + "for 'hub port detach' event"); + + hcd_delete_child(device, val); + + USB_MSG("Device disconnected from " + "hub 0x%08X, port %u", device, val); break; case HCD_EVENT_ENDPOINT: - case HCD_EVENT_URB: - /* Allow device thread to continue with it's logic */ - if (HCD_STATE_DISCONNECTED != this_device->state) - hcd_device_continue(this_device); + USB_ASSERT((HCD_STATE_DISCONNECTED != device->state), + "Parent device is marked as 'disconnected' " + "for 'endpoint' event"); + + /* Alters 'device' when endpoint is allocated to + * child rather than parent (hub), which allows + * proper thread to continue */ + device = hcd_get_child_for_ep(device, val); + + /* Check if anything at all, waits for such endpoint */ + if (device) + /* Allow device thread, waiting for endpoint + * event, to continue with its logic */ + hcd_device_continue(device, event, val); else - USB_MSG("Device is marked as 'disconnected' " - "for 'EP' event"); + USB_MSG("No device waits for endpoint %u", val); + + break; + + case HCD_EVENT_URB: + USB_ASSERT((HCD_STATE_DISCONNECTED != device->state), + "Device is marked as 'disconnected' " + "for 'URB' event"); + + /* Allow device thread to continue with it's logic */ + hcd_device_continue(device, event, val); break; default: USB_ASSERT(0, "Illegal HCD event"); + } +} + + +/*===========================================================================* + * hcd_update_port * + *===========================================================================*/ +void +hcd_update_port(hcd_driver_state * driver, hcd_event event) +{ + DEBUG_DUMP; + + switch (event) { + case HCD_EVENT_CONNECTED: + /* Check if already assigned */ + USB_ASSERT(NULL == driver->port_device, + "Device was already connected before " + "receiving 'connection' event"); + + /* Assign new blank device */ + driver->port_device = hcd_new_device(); + + /* Associate this device with driver */ + driver->port_device->driver = driver; break; + + case HCD_EVENT_DISCONNECTED: + /* Check if already released */ + USB_ASSERT(NULL != driver->port_device, + "Device was already disconnected before " + "receiving 'disconnection' event"); + + /* Release device */ + hcd_delete_device(driver->port_device); + + /* Clear port device pointer */ + driver->port_device = NULL; + break; + + default: + USB_ASSERT(0, "Illegal port update event"); } } @@ -132,12 +253,13 @@ hcd_device_thread(void * thread_args) DEBUG_DUMP; + /* Set device thread priority higher so it + * won't change context unless explicitly locked */ + _ddekit_thread_set_myprio(2); + /* Retrieve structures from generic data */ this_device = (hcd_device_state *)thread_args; - /* Plugged in */ - this_device->state = HCD_STATE_CONNECTION_PENDING; - /* Enumeration sequence */ if (EXIT_SUCCESS != hcd_enumerate(this_device)) hcd_device_finish(this_device, "USB device enumeration failed"); @@ -153,11 +275,8 @@ hcd_device_thread(void * thread_args) /* Start handling URB's */ for(;;) { /* Block and wait for something like 'submit URB' */ - hcd_device_wait(this_device, HCD_EVENT_URB, HCD_ANY_EP); - - if (EXIT_SUCCESS != hcd_handle_urb(this_device, - &(this_device->urb))) - hcd_device_finish(this_device, "URB handling failed"); + hcd_device_wait(this_device, HCD_EVENT_URB, HCD_UNUSED_VAL); + hcd_handle_urb(this_device); } /* Finish device handling to avoid leaving thread */ @@ -177,12 +296,176 @@ hcd_device_finish(hcd_device_state * this_device, const char * finish_msg) /* Lock forever */ for (;;) { - hcd_device_wait(this_device, HCD_EVENT_URB, HCD_ANY_EP); + hcd_device_wait(this_device, HCD_EVENT_URB, HCD_UNUSED_VAL); USB_MSG("Failed attempt to continue finished thread"); } } +/*===========================================================================* + * hcd_get_child_for_ep * + *===========================================================================*/ +static hcd_device_state * +hcd_get_child_for_ep(hcd_device_state * device, hcd_reg1 ep) +{ + hcd_device_state * child_found; + hcd_device_state * final_found; + hcd_device_state * child; + hcd_reg1 child_num; + + DEBUG_DUMP; + + /* Nothing yet */ + final_found = NULL; + + /* Check if any children (and their children) wait for EP event */ + /* Every device in tree is checked every time so errors can be found */ + for (child_num = 0; child_num < HCD_CHILDREN; child_num++) { + /* Device, to be checked for EP event recursively... */ + child = device->child[child_num]; + + /* ...but only if attached */ + if (NULL != child) { + /* Look deeper first */ + child_found = hcd_get_child_for_ep(child, ep); + + if (NULL != child_found) { + /* Only one device can wait for EP event */ + USB_ASSERT((NULL == final_found), + "More than one device waits for EP"); + /* Remember what was found */ + final_found = child_found; + } + } + } + + /* Check this device last */ + if ((HCD_EVENT_ENDPOINT == device->wait_event) && + (ep == device->wait_ep)) { + /* Only one device can wait for EP event */ + USB_ASSERT((NULL == final_found), + "More than one device waits for EP"); + /* Remember what was found */ + final_found = device; + } + + return final_found; +} + + +/*===========================================================================* + * hcd_add_child * + *===========================================================================*/ +static void +hcd_add_child(hcd_device_state * parent, hcd_reg1 port, hcd_speed speed) +{ + DEBUG_DUMP; + + USB_ASSERT(port < HCD_CHILDREN, "Port number too high"); + USB_ASSERT(NULL == parent->child[port], "Child device already exists"); + + /* Basic addition */ + parent->child[port] = hcd_new_device(); + parent->child[port]->parent = parent; + + /* Inherit parent's driver */ + parent->child[port]->driver = parent->driver; + + /* Remember speed, determined by hub driver */ + parent->child[port]->speed = speed; + + /* Try creating new thread for device */ + if (hcd_connect_device(parent->child[port], hcd_device_thread)) + USB_MSG("Device creation failed, nothing more " + "will happen until disconnected"); +} + + +/*===========================================================================* + * hcd_delete_child * + *===========================================================================*/ +static void +hcd_delete_child(hcd_device_state * parent, hcd_reg1 port) +{ + hcd_device_state * child; + + DEBUG_DUMP; + + USB_ASSERT(port < HCD_CHILDREN, "Port number too high"); + + child = parent->child[port]; /* Child to be detached */ + + USB_ASSERT(NULL != child, "Child device does not exist"); + + /* Make this child device and all its attached children + * disconnect recursively */ + hcd_disconnect_tree(child); + + /* Delete to release device itself */ + hcd_delete_device(child); + + /* Mark as released */ + parent->child[port] = NULL; +} + + +/*===========================================================================* + * hcd_disconnect_tree * + *===========================================================================*/ +static void +hcd_disconnect_tree(hcd_device_state * device) +{ + hcd_reg1 child_num; + + DEBUG_DUMP; + + /* Generate disconnect event for all children */ + for (child_num = 0; child_num < HCD_CHILDREN; child_num++) { + if (NULL != device->child[child_num]) + hcd_handle_event(device, HCD_EVENT_PORT_DISCONNECTED, + child_num); + } + + /* If this device was detached during URB handling, some steps must be + * taken to ensure that no process/thread is waiting for completion */ + if (NULL != device->urb) { + USB_MSG("Unplugged device had unhandled URB"); + /* Tell device driver that device was detached */ + /* TODO: ENODEV selected for that */ + device->urb->inout_status = ENODEV; + hcd_complete_urb(device); + } + + /* If connect callback was used before, call + * it's equivalent to signal disconnection */ + if (HCD_STATE_CONNECTED == device->state) + hcd_disconnect_cb(device); + + /* Handle device disconnection (freeing memory etc.) */ + hcd_disconnect_device(device); +} + + +/*===========================================================================* + * hcd_dump_tree * + *===========================================================================*/ +static void +hcd_dump_tree(hcd_device_state * device, hcd_reg1 level) +{ + hcd_reg1 child_num; + + /* DEBUG_DUMP; */ /* Let's keep tree output cleaner */ + + USB_MSG("Device on level %03u: 0x%08X", level, device); + + /* Traverse device tree recursively */ + for (child_num = 0; child_num < HCD_CHILDREN; child_num++) { + if (NULL != device->child[child_num]) + hcd_dump_tree(device->child[child_num], level + 1); + } +} + + /*===========================================================================* * hcd_enumerate * *===========================================================================*/ @@ -195,18 +478,22 @@ hcd_enumerate(hcd_device_state * this_device) d = this_device->driver; - /* First let driver reset device */ - if (EXIT_SUCCESS != d->reset_device(d->private_data, - &(this_device->speed))) { - USB_MSG("Failed to reset device"); - return EXIT_FAILURE; + /* Having a parent device also means being reseted by it + * so only reset devices that have no parents */ + if (NULL == this_device->parent) { + /* First let driver reset device */ + if (EXIT_SUCCESS != d->reset_device(d->private_data, + &(this_device->speed))) { + USB_MSG("Failed to reset device"); + return EXIT_FAILURE; + } } /* Default MaxPacketSize, based on speed */ - if (HCD_SPEED_LOW == this_device->speed) - this_device->max_packet_size = HCD_LS_MAXPACKETSIZE; - else + if (HCD_SPEED_HIGH == this_device->speed) this_device->max_packet_size = HCD_HS_MAXPACKETSIZE; + else + this_device->max_packet_size = HCD_LS_MAXPACKETSIZE; /* Get device descriptor */ if (EXIT_SUCCESS != hcd_get_device_descriptor(this_device)) { @@ -214,15 +501,45 @@ hcd_enumerate(hcd_device_state * this_device) return EXIT_FAILURE; } - /* TODO: Dynamic device addressing should be added here, when more - * than one device can be handled at a time */ + /* Remember max packet size from device descriptor */ + this_device->max_packet_size = this_device->device_desc.bMaxPacketSize; - /* Set address */ - if (EXIT_SUCCESS != hcd_set_address(this_device, HCD_ATTACHED_ADDR)) { + /* 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"); @@ -252,11 +569,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 */ @@ -264,45 +581,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; } @@ -312,35 +615,50 @@ hcd_get_device_descriptor(hcd_device_state * this_device) * hcd_set_address * *===========================================================================*/ static int -hcd_set_address(hcd_device_state * this_device, hcd_reg1 address) +hcd_set_address(hcd_device_state * this_device) { hcd_ctrlrequest setup; + hcd_urb urb; DEBUG_DUMP; /* Check for legal USB device address (must be non-zero as well) */ - USB_ASSERT((address > HCD_DEFAULT_ADDR) && (address <= HCD_LAST_ADDR), + USB_ASSERT((this_device->reserved_address > HCD_DEFAULT_ADDR) && + (this_device->reserved_address <= HCD_LAST_ADDR), "Illegal device address supplied"); /* TODO: magic numbers, no header for these */ setup.bRequestType = 0x00; /* OUT */ setup.bRequest = 0x05; /* Set address */ - setup.wValue = address; + setup.wValue = this_device->reserved_address; 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->address = 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; } @@ -352,19 +670,24 @@ hcd_set_address(hcd_device_state * this_device, hcd_reg1 address) 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 */ @@ -372,62 +695,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; } @@ -438,6 +806,7 @@ static int hcd_set_configuration(hcd_device_state * this_device, hcd_reg1 configuration) { hcd_ctrlrequest setup; + hcd_urb urb; DEBUG_DUMP; @@ -448,29 +817,39 @@ 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; } /*===========================================================================* * hcd_handle_urb * *===========================================================================*/ -static int -hcd_handle_urb(hcd_device_state * this_device, hcd_urb * urb) +static void +hcd_handle_urb(hcd_device_state * this_device) { + hcd_urb * urb; int transfer_status; DEBUG_DUMP; - /* TODO: One device only */ - USB_ASSERT(NULL != urb, "NULL URB given"); + /* Retrieve URB */ + urb = this_device->urb; + + USB_ASSERT(NULL != urb, "No URB supplied"); USB_ASSERT(this_device == urb->target_device, "Unknown device for URB"); /* Only if URB parsing was completed... */ @@ -491,13 +870,8 @@ hcd_handle_urb(hcd_device_state * this_device, hcd_urb * urb) this_device, urb); break; - case HCD_TRANSFER_ISOCHRONOUS: - /* TODO: ISO transfer */ - USB_MSG("ISO transfer not supported"); - break; - default: - USB_MSG("Invalid transfer type 0x%02X", + USB_MSG("Unsupported transfer type 0x%02X", (int)urb->type); break; } @@ -509,13 +883,27 @@ hcd_handle_urb(hcd_device_state * this_device, hcd_urb * urb) } else USB_MSG("Invalid URB supplied"); - /* Call completion regardless of status */ - hcd_completion_cb(urb); + /* Perform completion routine */ + hcd_complete_urb(this_device); +} - /* TODO: Only critical failures should ever yield EXIT_FAILURE, so - * return is not bound to transfer_status for now, to let device - * driver act accordingly */ - return EXIT_SUCCESS; + +/*===========================================================================* + * hcd_complete_urb * + *===========================================================================*/ +static void +hcd_complete_urb(hcd_device_state * this_device) +{ + DEBUG_DUMP; + + /* Signal scheduler that URB was handled */ + this_device->urb->handled(this_device->urb); + + /* Use this callback in case it is an external URB */ + hcd_completion_cb(this_device->urb); + + /* Make device forget about this URB */ + this_device->urb = NULL; } @@ -557,9 +945,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 */ @@ -627,7 +1013,14 @@ hcd_non_control_urb(hcd_device_state * this_device, hcd_urb * urb) request.direction = urb->direction; request.data_left = (int)urb->in_size; request.data = urb->inout_data; + /* TODO: This was changed to allow software scheduler to work correctly + * by switching URBs when they NAK, rather than waiting forever if URB + * which requires such waiting, was issued */ +#if 0 request.interval = urb->interval; +#else + request.interval = HCD_DEFAULT_NAKLIMIT; +#endif /* Assign to let know how much data can be transfered at a time */ request.max_packet_size = UGETW(e->descriptor.wMaxPacketSize); @@ -669,7 +1062,7 @@ hcd_setup_packet(hcd_device_state * this_device, hcd_ctrlrequest * setup, USB_ASSERT(this_device->max_packet_size >= HCD_LS_MAXPACKETSIZE, "Illegal MaxPacketSize"); USB_ASSERT(ep <= HCD_LAST_EP, "Invalid EP number"); - USB_ASSERT(this_device->address <= HCD_LAST_ADDR, + USB_ASSERT(this_device->current_address <= HCD_LAST_ADDR, "Invalid device address"); /* Initially... */ @@ -678,7 +1071,8 @@ hcd_setup_packet(hcd_device_state * this_device, hcd_ctrlrequest * setup, this_device->control_len = 0; /* Nothing read yet */ /* Set parameters for further communication */ - d->setup_device(d->private_data, ep, this_device->address); + d->setup_device(d->private_data, ep, this_device->current_address, + NULL, NULL); /* Send setup packet */ d->setup_stage(d->private_data, setup); @@ -725,8 +1119,8 @@ hcd_setup_packet(hcd_device_state * this_device, hcd_ctrlrequest * setup, current_byte += rx_len; this_device->control_len += rx_len; - /* If full max sized packet was read... */ - if (rx_len == (int)this_device->max_packet_size) + /* If max sized packet was read (or more)... */ + if (rx_len >= (int)this_device->max_packet_size) /* ...try reading next packet even if * zero bytes may be received */ continue; @@ -796,8 +1190,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; @@ -807,19 +1200,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"); - 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; @@ -846,8 +1226,8 @@ hcd_data_transfer(hcd_device_state * this_device, hcd_datarequest * request) USB_ASSERT((request->endpoint <= HCD_LAST_EP) && (request->endpoint > HCD_DEFAULT_EP), "Invalid EP number"); - USB_ASSERT((this_device->address <= HCD_LAST_ADDR) && - (this_device->address > HCD_DEFAULT_ADDR), + USB_ASSERT((this_device->current_address <= HCD_LAST_ADDR) && + (this_device->current_address > HCD_DEFAULT_ADDR), "Invalid device address"); /* Initially... */ @@ -855,7 +1235,9 @@ hcd_data_transfer(hcd_device_state * this_device, hcd_datarequest * request) /* Set parameters for further communication */ d->setup_device(d->private_data, request->endpoint, - this_device->address); + this_device->current_address, + &(this_device->ep_tx_tog[request->endpoint]), + &(this_device->ep_rx_tog[request->endpoint])); /* Check transfer direction first */ if (HCD_DIRECTION_IN == request->direction) { diff --git a/minix/drivers/usb/usbd/hcd/hcd_common.c b/minix/drivers/usb/usbd/hcd/hcd_common.c index 562496612..d6b6f798c 100644 --- a/minix/drivers/usb/usbd/hcd/hcd_common.c +++ b/minix/drivers/usb/usbd/hcd/hcd_common.c @@ -13,18 +13,30 @@ #include /* clkconf_* */ #include /* sys_privctl */ -#include -#include -#include +#include +#include +#include /*===========================================================================* * Local prototypes * *===========================================================================*/ +/* Descriptor related operations */ static int hcd_fill_configuration(hcd_reg1 *, int, hcd_configuration *, int); static int hcd_fill_interface(hcd_reg1 *, int, hcd_interface *, int); static int hcd_fill_endpoint(hcd_reg1 *, int, hcd_endpoint *); +/* Handling free USB device addresses */ +static hcd_reg1 hcd_reserve_addr(hcd_driver_state *); +static void hcd_release_addr(hcd_driver_state *, hcd_reg1); + + +/*===========================================================================* + * Local definitions * + *===========================================================================*/ +/* List of all allocated devices */ +static hcd_device_state * dev_list = NULL; + /*===========================================================================* * hcd_os_interrupt_attach * @@ -188,49 +200,84 @@ hcd_os_nanosleep(int nanosec) /*===========================================================================* - * hcd_init_device * + * hcd_connect_device * *===========================================================================*/ int hcd_connect_device(hcd_device_state * this_device, hcd_thread_function funct) { DEBUG_DUMP; - if ((NULL != this_device->lock) || (NULL != this_device->thread)) { - USB_MSG("Device data already allocated"); + /* This is meant to allow thread name distinction + * and should not be used for anything else */ + static unsigned int devnum = 0; + + /* Should be able to hold device prefix and some number */ + char devname[] = "dev.........."; + + USB_ASSERT((NULL == this_device->lock) && + (NULL == this_device->thread) && + (HCD_DEFAULT_ADDR == this_device->reserved_address) && + (HCD_STATE_DISCONNECTED == this_device->state), + "Device structure not clean"); + + /* Mark as 'plugged in' to avoid treating device + * as 'disconnected' in case of errors below */ + this_device->state = HCD_STATE_CONNECTION_PENDING; + + /* Reserve device address for further use if available */ + if (HCD_DEFAULT_ADDR == (this_device->reserved_address = + hcd_reserve_addr(this_device->driver))) { + USB_MSG("No free device addresses"); return EXIT_FAILURE; } - if (NULL == (this_device->lock = ddekit_sem_init(0))) + /* Get 'lock' that makes device thread wait for events to occur */ + if (NULL == (this_device->lock = ddekit_sem_init(0))) { + USB_MSG("Failed to initialize thread lock"); return EXIT_FAILURE; + } + /* Prepare name */ + snprintf(devname, sizeof(devname), "dev%u", devnum++); + + /* Get thread itself */ if (NULL == (this_device->thread = ddekit_thread_create(funct, - this_device, - "Device"))) { - ddekit_sem_deinit(this_device->lock); + this_device, + (const char *)devname))) { + USB_MSG("Failed to initialize USB device thread"); return EXIT_FAILURE; } - /* Allow device thread to work */ - ddekit_yield(); - return EXIT_SUCCESS; } + /*===========================================================================* - * hcd_deinit_device * + * hcd_disconnect_device * *===========================================================================*/ void hcd_disconnect_device(hcd_device_state * this_device) { DEBUG_DUMP; + /* TODO: This should disconnect all the children if they exist */ + + /* Clean configuration tree in case it was acquired */ hcd_tree_cleanup(&(this_device->config_tree)); - ddekit_thread_terminate(this_device->thread); - ddekit_sem_deinit(this_device->lock); + /* Release allocated resources */ + if (NULL != this_device->thread) + ddekit_thread_terminate(this_device->thread); + if (NULL != this_device->lock) + ddekit_sem_deinit(this_device->lock); - this_device->thread = NULL; - this_device->lock = NULL; + /* Release reserved address */ + if (HCD_DEFAULT_ADDR != this_device->reserved_address) + hcd_release_addr(this_device->driver, + this_device->reserved_address); + + /* Mark as disconnected */ + this_device->state = HCD_STATE_DISCONNECTED; } @@ -238,20 +285,16 @@ hcd_disconnect_device(hcd_device_state * this_device) * hcd_device_wait * *===========================================================================*/ void -hcd_device_wait(hcd_device_state * this_device, hcd_event event, hcd_reg1 ep) +hcd_device_wait(hcd_device_state * device, hcd_event event, hcd_reg1 ep) { - hcd_driver_state * drv; - DEBUG_DUMP; - drv = (hcd_driver_state *)this_device->driver; + USB_DBG("0x%08X wait (0x%02X, 0x%02X)", device, event, ep); - drv->expected_event = event; - drv->expected_endpoint = ep; + device->wait_event = event; + device->wait_ep = ep; - USB_DBG("Waiting for: ev=0x%X, ep=0x%X", (int)event, ep); - - ddekit_sem_down(this_device->lock); + ddekit_sem_down(device->lock); } @@ -259,25 +302,128 @@ hcd_device_wait(hcd_device_state * this_device, hcd_event event, hcd_reg1 ep) * hcd_device_continue * *===========================================================================*/ void -hcd_device_continue(hcd_device_state * this_device) +hcd_device_continue(hcd_device_state * device, hcd_event event, hcd_reg1 ep) { - hcd_driver_state * drv; + DEBUG_DUMP; + + USB_DBG("0x%08X continue (0x%02X, 0x%02X)", device, event, ep); + + USB_ASSERT(device->wait_event == event, "Unexpected event"); + USB_ASSERT(device->wait_ep == ep, "Unexpected endpoint"); + + ddekit_sem_up(device->lock); +} + + +/*===========================================================================* + * hcd_new_device * + *===========================================================================*/ +hcd_device_state * +hcd_new_device(void) +{ + hcd_device_state * d; DEBUG_DUMP; - drv = (hcd_driver_state *)this_device->driver; + /* One new blank device */ + d = calloc(1, sizeof(*d)); - /* We need to get what was expected... */ - USB_ASSERT(drv->current_event == drv->expected_event, - "Unexpected event occurred"); + USB_ASSERT(NULL != d, "Failed to allocate device"); - /* ...including endpoint interrupts */ - if (HCD_EVENT_ENDPOINT == drv->current_event) { - USB_ASSERT(drv->current_endpoint == drv->expected_endpoint, - "Unexpected endpoint interrupt"); + if (NULL == dev_list) { + dev_list = d; + } else { + d->_next = dev_list; + dev_list = d; } - ddekit_sem_up(this_device->lock); +#ifdef HCD_DUMP_DEVICE_LIST + /* Dump updated state of device list */ + hcd_dump_devices(); +#endif + + return d; +} + + +/*===========================================================================* + * hcd_delete_device * + *===========================================================================*/ +void +hcd_delete_device(hcd_device_state * d) +{ + hcd_device_state * temp; + + DEBUG_DUMP; + + if (d == dev_list) { + dev_list = dev_list->_next; + } else { + temp = dev_list; + + /* Find the device and ... */ + while (temp->_next != d) { + USB_ASSERT(NULL != temp->_next, + "Invalid state of device list"); + temp = temp->_next; + } + + /* ...make device list forget about it */ + temp->_next = temp->_next->_next; + } + + free(d); + +#ifdef HCD_DUMP_DEVICE_LIST + /* Dump updated state of device list */ + hcd_dump_devices(); +#endif +} + + +/*===========================================================================* + * hcd_dump_devices * + *===========================================================================*/ +void +hcd_dump_devices(void) +{ + hcd_device_state * temp; + + DEBUG_DUMP; + + temp = dev_list; + + USB_MSG("Allocated devices:"); + + while (NULL != temp) { + USB_MSG("0x%08X", (int)temp); + temp = temp->_next; + } +} + + +/*===========================================================================* + * hcd_check_device * + *===========================================================================*/ +int +hcd_check_device(hcd_device_state * d) +{ + hcd_device_state * temp; + + DEBUG_DUMP; + + temp = dev_list; + + /* Traverse the list of allocated devices + * to determine validity of this one */ + while (NULL != temp) { + if (temp == d) + return EXIT_SUCCESS; /* Device found within the list */ + temp = temp->_next; + } + + /* Device was not found, may have been removed earlier */ + return EXIT_FAILURE; } @@ -571,3 +717,44 @@ hcd_fill_endpoint(hcd_reg1 * buf, int len, hcd_endpoint * e) return EXIT_SUCCESS; } + + +/*===========================================================================* + * hcd_reserve_addr * + *===========================================================================*/ +static hcd_reg1 +hcd_reserve_addr(hcd_driver_state * driver) +{ + hcd_reg1 addr; + + DEBUG_DUMP; + + for (addr = HCD_FIRST_ADDR; addr <= HCD_LAST_ADDR; addr++) { + if (HCD_ADDR_AVAILABLE == driver->dev_addr[addr]) { + USB_DBG("Reserved address: %u", addr); + driver->dev_addr[addr] = HCD_ADDR_USED; + return addr; + } + } + + /* This means error */ + return HCD_DEFAULT_ADDR; +} + + +/*===========================================================================* + * hcd_release_addr * + *===========================================================================*/ +static void +hcd_release_addr(hcd_driver_state * driver, hcd_reg1 addr) +{ + DEBUG_DUMP; + + USB_ASSERT((addr > HCD_DEFAULT_ADDR) && (addr <= HCD_LAST_ADDR), + "Invalid device address to be released"); + USB_ASSERT(HCD_ADDR_USED == driver->dev_addr[addr], + "Attempted to release unused address"); + + USB_DBG("Released address: %u", addr); + driver->dev_addr[addr] = HCD_ADDR_AVAILABLE; +} diff --git a/minix/drivers/usb/usbd/hcd/hcd_ddekit.c b/minix/drivers/usb/usbd/hcd/hcd_ddekit.c index 0606c20df..c9862c865 100644 --- a/minix/drivers/usb/usbd/hcd/hcd_ddekit.c +++ b/minix/drivers/usb/usbd/hcd/hcd_ddekit.c @@ -6,9 +6,10 @@ #include -#include -#include -#include +#include +#include +#include +#include /*===========================================================================* @@ -16,7 +17,7 @@ *===========================================================================*/ /* * In this file "struct ddekit_usb_dev" equals "hcd_device_state" - * */ + */ struct ddekit_usb_device_id; struct ddekit_usb_urb; struct ddekit_usb_dev; @@ -25,6 +26,13 @@ struct ddekit_usb_dev; static void hcd_decode_urb(hcd_urb *, struct ddekit_usb_urb *); static void hcd_encode_urb(hcd_urb *, struct ddekit_usb_urb *); +/* HCD's URB create/destroy */ +static hcd_urb * hcd_new_urb(void); +static void hcd_free_urb(hcd_urb *); + +/* Decodes event from received info */ +static void hcd_decode_info(long, long, hcd_event *, hcd_reg1 *); + /*===========================================================================* * Global definitions * @@ -176,36 +184,19 @@ ddekit_usb_get_device_id(struct ddekit_usb_dev * dev, int ddekit_usb_submit_urb(struct ddekit_usb_urb * d_urb) { - hcd_device_state * dev; - hcd_driver_state * drv; + hcd_urb * urb; DEBUG_DUMP; - /* Retrieve info on device/driver state from DDEKit's USB */ - dev = (hcd_device_state *)(d_urb->dev); - drv = (hcd_driver_state *)(dev->driver); + /* Get new URB */ + urb = hcd_new_urb(); - /* Check for latest URB completion */ - if (NULL == dev->urb.original_urb) { + /* Turn DDEKit URB format to one that is easier to + * handle by HCD, also check if URB is valid */ + hcd_decode_urb(urb, d_urb); - /* Remember original URB */ - dev->urb.original_urb = (void *)d_urb; - - /* TODO: If multiple URB's have to be queued, this code - * or DDEKit's must be altered accordingly */ - /* Turn DDEKit URB format to one that is easier to - * handle by HCD, also check if URB is valid */ - hcd_decode_urb(&(dev->urb), d_urb); - - /* Start handling URB event */ - drv->current_event = HCD_EVENT_URB; - hcd_handle_event(drv); - - return EXIT_SUCCESS; - } - - /* Last URB must not have been completed */ - return EXIT_FAILURE; + /* Add URB to scheduler */ + return hcd_schedule_external_urb(urb); } @@ -218,10 +209,37 @@ ddekit_usb_cancle_urb(struct ddekit_usb_urb * d_urb) DEBUG_DUMP; /* TODO: UNUSED for argument won't work */ ((void)d_urb); + /* TODO: No driver will require this any time soon */ + USB_ASSERT(0, "URB cancellation not supported"); return EXIT_SUCCESS; } +/*===========================================================================* + * ddekit_usb_info * + *===========================================================================*/ +long +ddekit_usb_info(struct ddekit_usb_dev * dev, long type, long value) +{ + hcd_event event; + hcd_reg1 val; + + DEBUG_DUMP; + + /* Decode event */ + hcd_decode_info(type, value, &event, &val); + + if (HCD_EVENT_INVALID == event) { + USB_MSG("Invalid info message received"); + return EXIT_FAILURE; + } else { + /* Let HCD handle info message */ + hcd_handle_event((hcd_device_state *)dev, event, val); + return EXIT_SUCCESS; + } +} + + /*===========================================================================* * ddekit_usb_init * *===========================================================================*/ @@ -290,13 +308,17 @@ hcd_completion_cb(hcd_urb * urb) /* Recollect original URB */ d_urb = (struct ddekit_usb_urb *)urb->original_urb; - /* Turn HCD URB format to one handled by DDEKit */ - hcd_encode_urb(urb, d_urb); + /* Original URB will not be NULL if URB + * was external (from device driver) */ + if (NULL != d_urb) { + /* Turn HCD URB format to one handled by DDEKit */ + hcd_encode_urb(urb, d_urb); - completion_cb(d_urb->priv); + /* No need for this URB anymore */ + hcd_free_urb(urb); - /* URB was handled, forget about it */ - urb->original_urb = NULL; + completion_cb(d_urb->priv); + } } @@ -308,6 +330,9 @@ hcd_decode_urb(hcd_urb * urb, struct ddekit_usb_urb * dde_urb) { DEBUG_DUMP; + /* Remember original */ + urb->original_urb = (void *)dde_urb; + /* No UBR error initially */ urb->inout_status = EXIT_SUCCESS; @@ -397,7 +422,63 @@ hcd_encode_urb(hcd_urb * urb, struct ddekit_usb_urb * dde_urb) { DEBUG_DUMP; + /* Data buffers are the same, no need to copy */ /* Rewrite output for DDEKit part */ dde_urb->actual_length = urb->out_size; dde_urb->status = urb->inout_status; } + + +/*===========================================================================* + * hcd_new_urb * + *===========================================================================*/ +static hcd_urb * +hcd_new_urb(void) +{ + DEBUG_DUMP; + return malloc(sizeof(hcd_urb)); +} + + +/*===========================================================================* + * hcd_free_urb * + *===========================================================================*/ +static void +hcd_free_urb(hcd_urb * urb) +{ + DEBUG_DUMP; + free(urb); +} + + +/*===========================================================================* + * hcd_decode_info * + *===========================================================================*/ +static void +hcd_decode_info(long type, long invalue, hcd_event * event, hcd_reg1 * outvalue) +{ + DEBUG_DUMP; + + USB_ASSERT((invalue >= 0) && (invalue <= 0xFF), + "Illegal USB info value received"); + + switch ((ddekit_msg_type_t)type) { + case DDEKIT_HUB_PORT_LS_CONN: + *event = HCD_EVENT_PORT_LS_CONNECTED; + break; + case DDEKIT_HUB_PORT_FS_CONN: + *event = HCD_EVENT_PORT_FS_CONNECTED; + break; + case DDEKIT_HUB_PORT_HS_CONN: + *event = HCD_EVENT_PORT_HS_CONNECTED; + break; + case DDEKIT_HUB_PORT_DISCONN: + *event = HCD_EVENT_PORT_DISCONNECTED; + break; + default: + *event = HCD_EVENT_INVALID; + break; + } + + *outvalue = (hcd_reg1)invalue; +} diff --git a/minix/drivers/usb/usbd/hcd/hcd_schedule.c b/minix/drivers/usb/usbd/hcd/hcd_schedule.c new file mode 100644 index 000000000..feacd214e --- /dev/null +++ b/minix/drivers/usb/usbd/hcd/hcd_schedule.c @@ -0,0 +1,305 @@ +/* + * Implementation of HCD URB scheduler + */ + +#include /* memset */ + +#include +#include +#include +#include +#include +#include + + +/*===========================================================================* + * Required for scheduling * + *===========================================================================*/ +/* TODO: Like in DDEKit but power of 2 */ +#define HCD_MAX_URBS 16 + +/* TODO: Structure to hold URBs in DDEKit is limited so this is no better + * (but because of that, there is no need for another malloc) */ +static hcd_urb * stored_urb[HCD_MAX_URBS]; + +/* Number of URBs stored during operation */ +static int num_stored_urbs; + +/* Scheduler thread */ +static hcd_thread * urb_thread; + +/* This allows waiting for URB */ +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 *); + +/* Completion callback */ +static void hcd_urb_handled(hcd_urb *); + +/* Stores URB to be handled */ +static int hcd_store_urb(hcd_urb *); + +/* Removes stored URB */ +static void hcd_remove_urb(hcd_urb *); + +/* Gets URB to be handled next (based on priority) */ +static hcd_urb * hcd_get_urb(void); + + +/*===========================================================================* + * usbd_init_scheduler * + *===========================================================================*/ +int +usbd_init_scheduler(void) +{ + DEBUG_DUMP; + + /* Reset everything */ + num_stored_urbs = 0; + memset(stored_urb, 0, sizeof(stored_urb)); + + urb_thread = ddekit_thread_create(hcd_urb_scheduler_task, NULL, + "scheduler"); + if (NULL == urb_thread) + goto ERR1; + + urb_lock = ddekit_sem_init(0); + if (NULL == urb_lock) + goto ERR2; + + handled_lock = ddekit_sem_init(0); + if (NULL == handled_lock) + goto ERR3; + + return EXIT_SUCCESS; + + ERR3: + ddekit_sem_deinit(urb_lock); + ERR2: + ddekit_thread_terminate(urb_thread); + ERR1: + return EXIT_FAILURE; +} + + +/*===========================================================================* + * usbd_deinit_scheduler * + *===========================================================================*/ +void +usbd_deinit_scheduler(void) +{ + DEBUG_DUMP; + + ddekit_sem_deinit(handled_lock); + + ddekit_sem_deinit(urb_lock); + + ddekit_thread_terminate(urb_thread); +} + + +/*===========================================================================* + * 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; + + /* Tell URB what to call on completion */ + urb->handled = hcd_urb_handled; + + /* Store and check if scheduler should be unlocked */ + if (EXIT_SUCCESS == hcd_store_urb(urb)) { + ddekit_sem_up(urb_lock); + return EXIT_SUCCESS; + } + + return EXIT_FAILURE; +} + + +/*===========================================================================* + * hcd_unschedule_urb * + *===========================================================================*/ +static void +hcd_unschedule_urb(hcd_urb * urb) +{ + DEBUG_DUMP; + + hcd_remove_urb(urb); +} + + +/*===========================================================================* + * hcd_urb_scheduler_task * + *===========================================================================*/ +static void +hcd_urb_scheduler_task(void * UNUSED(arg)) +{ + hcd_device_state * current_device; + hcd_urb * current_urb; + + DEBUG_DUMP; + + for (;;) { + /* Wait for scheduler to unlock on any URB submit */ + ddekit_sem_down(urb_lock); + + /* Get URB */ + current_urb = hcd_get_urb(); + + /* Get URB's target device */ + current_device = current_urb->target_device; + + /* Check for mismatch */ + USB_ASSERT(NULL != current_urb, "URB missing after URB unlock"); + + /* Check if URB's device is still allocated */ + if (EXIT_SUCCESS == hcd_check_device(current_device)) { + /* Tell device that this is its URB */ + current_device->urb = current_urb; + + /* Start handling URB event */ + hcd_handle_event(current_device, HCD_EVENT_URB, + HCD_UNUSED_VAL); + + /* Wait for completion */ + ddekit_sem_down(handled_lock); + + /* TODO: Not enough DDEKit thread priorities + * for a better solution */ + /* Yield, to allow unlocking thread, to continue + * before next URB is used */ + ddekit_yield(); + + /* Makes thread debugging easier */ + USB_DBG("URB handled, scheduler unlocked"); + } else { + USB_MSG("Device 0x%08X for URB 0x%08X, is unavailable", + (int)current_device, + (int)current_urb); + } + } +} + + +/*===========================================================================* + * hcd_urb_handled * + *===========================================================================*/ +static void +hcd_urb_handled(hcd_urb * urb) +{ + DEBUG_DUMP; + + /* This URB will be scheduled no more */ + hcd_unschedule_urb(urb); + + /* Handling completed */ + ddekit_sem_up(handled_lock); +} + + +/*===========================================================================* + * hcd_store_urb * + *===========================================================================*/ +static int +hcd_store_urb(hcd_urb * urb) +{ + int i; + + DEBUG_DUMP; + + for (i = 0; i < HCD_MAX_URBS; i++) { + if (NULL == stored_urb[i]) { + stored_urb[i] = urb; + num_stored_urbs++; + return EXIT_SUCCESS; + } + } + + USB_MSG("No more free URBs"); + + return EXIT_FAILURE; +} + +/*===========================================================================* + * hcd_remove_urb * + *===========================================================================*/ +static void +hcd_remove_urb(hcd_urb * urb) +{ + int i; + + DEBUG_DUMP; + + for (i = 0; i < HCD_MAX_URBS; i++) { + if (urb == stored_urb[i]) { + stored_urb[i] = NULL; + num_stored_urbs--; + return; + } + } + + USB_ASSERT(0, "URB to be removed, was never stored"); +} + +/*===========================================================================* + * hcd_get_urb * + *===========================================================================*/ +static hcd_urb * +hcd_get_urb(void) +{ + static int i = 0; + int checked; + + DEBUG_DUMP; + + /* TODO: Some priority checking may be here */ + for (checked = 0; checked < HCD_MAX_URBS; checked++) { + /* To avoid starting from 0 every + * time (potential starvation) */ + i = (i + 1) % HCD_MAX_URBS; + + /* When found */ + if (NULL != stored_urb[i]) + return stored_urb[i]; + } + + /* Nothing submitted yet */ + return NULL; +} diff --git a/minix/drivers/usb/usbd/hcd/musb/musb_am335x.c b/minix/drivers/usb/usbd/hcd/musb/musb_am335x.c index 4c419904d..b8d25313a 100644 --- a/minix/drivers/usb/usbd/hcd/musb/musb_am335x.c +++ b/minix/drivers/usb/usbd/hcd/musb/musb_am335x.c @@ -4,10 +4,10 @@ #include /* memset */ -#include -#include -#include -#include +#include +#include +#include +#include #include "musb_core.h" @@ -348,6 +348,7 @@ musb_am335x_init(void) ctrl->driver.out_status_stage = musb_out_status_stage; ctrl->driver.read_data = musb_read_data; ctrl->driver.check_error = musb_check_error; + ctrl->driver.port_device = NULL; } #endif @@ -388,6 +389,7 @@ musb_am335x_init(void) ctrl->driver.out_status_stage = musb_out_status_stage; ctrl->driver.read_data = musb_read_data; ctrl->driver.check_error = musb_check_error; + ctrl->driver.port_device = NULL; } return musb_am335x_internal_init(); @@ -563,25 +565,26 @@ musb_am335x_usbx_isr(void * data) if (irqstat1 & AM335X_VAL_USBXIRQENABLEXXX1_CONNECTED) { USB_DBG("Device connected"); CLEAR_IRQ1(AM335X_VAL_USBXIRQENABLEXXX1_CONNECTED); - driver->current_event = HCD_EVENT_CONNECTED; - hcd_handle_event(driver); + hcd_update_port(driver, HCD_EVENT_CONNECTED); + hcd_handle_event(driver->port_device, HCD_EVENT_CONNECTED, + HCD_UNUSED_VAL); return; } if (irqstat1 & AM335X_VAL_USBXIRQENABLEXXX1_DISCONNECTED) { USB_DBG("Device disconnected"); CLEAR_IRQ1(AM335X_VAL_USBXIRQENABLEXXX1_DISCONNECTED); - driver->current_event = HCD_EVENT_DISCONNECTED; - hcd_handle_event(driver); + hcd_handle_event(driver->port_device, HCD_EVENT_DISCONNECTED, + HCD_UNUSED_VAL); + hcd_update_port(driver, HCD_EVENT_DISCONNECTED); return; } if (0 != irqstat0) { USB_DBG("EP interrupt"); CLEAR_IRQ0(irqstat0); - driver->current_event = HCD_EVENT_ENDPOINT; - driver->current_endpoint = musb_am335x_irqstat0_to_ep(irqstat0); - hcd_handle_event(driver); + hcd_handle_event(driver->port_device, HCD_EVENT_ENDPOINT, + musb_am335x_irqstat0_to_ep(irqstat0)); return; } diff --git a/minix/drivers/usb/usbd/hcd/musb/musb_core.c b/minix/drivers/usb/usbd/hcd/musb/musb_core.c index 2b6c61da3..37417d5e2 100644 --- a/minix/drivers/usb/usbd/hcd/musb/musb_core.c +++ b/minix/drivers/usb/usbd/hcd/musb/musb_core.c @@ -4,9 +4,9 @@ #include /* memcpy */ -#include -#include -#include +#include +#include +#include #include "musb_core.h" #include "musb_regs.h" @@ -345,13 +345,16 @@ musb_core_stop(void * cfg) * musb_setup_device * *===========================================================================*/ void -musb_setup_device(void * cfg, hcd_reg1 ep, hcd_reg1 addr) +musb_setup_device(void * cfg, hcd_reg1 ep, hcd_reg1 addr, + hcd_datatog * tx_tog, hcd_datatog * rx_tog) { DEBUG_DUMP; /* Assign */ ((musb_core_config *)cfg)->ep = ep; ((musb_core_config *)cfg)->addr = addr; + ((musb_core_config *)cfg)->datatog_tx = tx_tog; + ((musb_core_config *)cfg)->datatog_rx = rx_tog; } @@ -372,7 +375,7 @@ musb_reset_device(void * cfg, hcd_speed * speed) r = core->regs; /* Set initial parameters */ - musb_setup_device(core, HCD_DEFAULT_EP, HCD_DEFAULT_ADDR); + musb_setup_device(core, HCD_DEFAULT_EP, HCD_DEFAULT_ADDR, NULL, NULL); /* Set EP and device address to be used in this command */ musb_set_state(core); @@ -408,9 +411,14 @@ musb_reset_device(void * cfg, hcd_speed * speed) USB_DBG("High speed USB enabled"); } else { /* Only full-speed supported */ - USB_DBG("High speed USB disabled"); + host_type0 = HCD_RD1(r, MUSB_REG_HOST_TYPE0); + HCD_CLR(host_type0, MUSB_VAL_HOST_TYPE0_MASK); + HCD_SET(host_type0, MUSB_VAL_HOST_TYPE0_FULL_SPEED); + HCD_WR1(r, MUSB_REG_HOST_TYPE0, host_type0); *speed = HCD_SPEED_FULL; + + USB_DBG("High speed USB disabled"); } return EXIT_SUCCESS; @@ -529,16 +537,16 @@ musb_rx_stage(void * cfg, hcd_datarequest * request) /* Make controller reconfigure */ host_rxcsr = HCD_RD2(r, MUSB_REG_HOST_RXCSR); - if (MUSB_DATATOG_UNKNOWN == core->datatog_rx[core->ep]) { - /* Reset DATA toggle on first transfer */ - HCD_SET(host_rxcsr, MUSB_VAL_HOST_RXCSR_CLRDATATOG); - core->datatog_rx[core->ep] = MUSB_DATATOG_INIT; - } + HCD_SET(host_rxcsr, MUSB_VAL_HOST_RXCSR_DATATOGWREN); /* Enable first */ HCD_SET(host_rxcsr, MUSB_VAL_HOST_RXCSR_FLUSHFIFO); HCD_WR2(r, MUSB_REG_HOST_RXCSR, host_rxcsr); - /* Request packet */ + /* Set data toggle and start receiving */ host_rxcsr = HCD_RD2(r, MUSB_REG_HOST_RXCSR); + if (HCD_DATATOG_DATA0 == *(core->datatog_rx)) + HCD_CLR(host_rxcsr, MUSB_VAL_HOST_RXCSR_DATATOG); + else + HCD_SET(host_rxcsr, MUSB_VAL_HOST_RXCSR_DATATOG); HCD_SET(host_rxcsr, MUSB_VAL_HOST_RXCSR_REQPKT); HCD_WR2(r, MUSB_REG_HOST_RXCSR, host_rxcsr); } @@ -625,19 +633,20 @@ musb_tx_stage(void * cfg, hcd_datarequest * request) HCD_SET(host_txcsr, MUSB_VAL_HOST_TXCSR_MODE); HCD_CLR(host_txcsr, MUSB_VAL_HOST_TXCSR_ISO); HCD_CLR(host_txcsr, MUSB_VAL_HOST_TXCSR_AUTOSET); - if (MUSB_DATATOG_UNKNOWN == core->datatog_tx[core->ep]) { - /* Reset DATA toggle on first transfer */ - HCD_SET(host_txcsr, MUSB_VAL_HOST_TXCSR_CLRDATATOG); - core->datatog_tx[core->ep] = MUSB_DATATOG_INIT; - } + HCD_SET(host_txcsr, MUSB_VAL_HOST_TXCSR_DATATOGWREN); /* Enable first */ + /* TODO: May have no effect */ HCD_SET(host_txcsr, MUSB_VAL_HOST_TXCSR_FLUSHFIFO); HCD_WR2(r, MUSB_REG_HOST_TXCSR, host_txcsr); /* Put data in FIFO */ musb_write_fifo(cfg, request->data, request->data_left, core->ep); - /* Request packet */ + /* Set data toggle and start transmitting */ host_txcsr = HCD_RD2(r, MUSB_REG_HOST_TXCSR); + if (HCD_DATATOG_DATA0 == *(core->datatog_tx)) + HCD_CLR(host_txcsr, MUSB_VAL_HOST_TXCSR_DATATOG); + else + HCD_SET(host_txcsr, MUSB_VAL_HOST_TXCSR_DATATOG); HCD_SET(host_txcsr, MUSB_VAL_HOST_TXCSR_TXPKTRDY); HCD_WR2(r, MUSB_REG_HOST_TXCSR, host_txcsr); } @@ -785,6 +794,7 @@ musb_check_error(void * cfg, hcd_transfer xfer, hcd_reg1 ep, hcd_direction dir) } musb_error_case; + musb_core_config * core; void * r; hcd_reg2 host_csr; musb_error_case error_case; @@ -795,7 +805,8 @@ musb_check_error(void * cfg, hcd_transfer xfer, hcd_reg1 ep, hcd_direction dir) USB_ASSERT(HCD_TRANSFER_ISOCHRONOUS != xfer, "ISO transfer not supported"); - r = ((musb_core_config *)cfg)->regs; + core = (musb_core_config *)cfg; + r = core->regs; /* Set EP and device address to be used in this command */ musb_set_state((musb_core_config *)cfg); @@ -854,6 +865,13 @@ musb_check_error(void * cfg, hcd_transfer xfer, hcd_reg1 ep, hcd_direction dir) /* Get TX status register */ host_csr = HCD_RD2(r, MUSB_REG_HOST_TXCSR); + /* Check for completion */ + if (!(host_csr & MUSB_VAL_HOST_TXCSR_TXPKTRDY)) { + /* ACK received update data toggle */ + *(core->datatog_tx) ^= HCD_DATATOG_DATA1; + return EXIT_SUCCESS; + } + /* Check for common errors */ if (host_csr & MUSB_VAL_HOST_TXCSR_ERROR) { USB_MSG("HOST_TXCSR ERROR: %04X", host_csr); @@ -871,18 +889,30 @@ musb_check_error(void * cfg, hcd_transfer xfer, hcd_reg1 ep, hcd_direction dir) if (host_csr & MUSB_VAL_HOST_TXCSR_NAK_TIMEOUT) { USB_MSG("HOST_TXCSR NAK_TIMEOUT: %04X", host_csr); + /* Flush FIFO before clearing NAKTIMEOUT + * to abort transfer */ + HCD_SET(host_csr, MUSB_VAL_HOST_TXCSR_FLUSHFIFO); + HCD_WR2(r, MUSB_REG_HOST_TXCSR, host_csr); + host_csr = HCD_RD2(r, MUSB_REG_HOST_TXCSR); HCD_CLR(host_csr, MUSB_VAL_HOST_TXCSR_NAK_TIMEOUT); HCD_WR2(r, MUSB_REG_HOST_TXCSR, host_csr); return EXIT_FAILURE; } - return EXIT_SUCCESS; + USB_ASSERT(0, "Invalid state of HOST_TXCSR"); } if (MUSB_IN_ERROR_CASE == error_case) { /* Get RX status register */ host_csr = HCD_RD2(r, MUSB_REG_HOST_RXCSR); + /* Check for completion */ + if (host_csr & MUSB_VAL_HOST_RXCSR_RXPKTRDY) { + /* ACK received update data toggle */ + *(core->datatog_rx) ^= HCD_DATATOG_DATA1; + return EXIT_SUCCESS; + } + /* Check for common errors */ if (host_csr & MUSB_VAL_HOST_RXCSR_ERROR) { USB_MSG("HOST_RXCSR ERROR: %04X", host_csr); @@ -900,12 +930,16 @@ musb_check_error(void * cfg, hcd_transfer xfer, hcd_reg1 ep, hcd_direction dir) if (host_csr & MUSB_VAL_HOST_RXCSR_NAKTIMEOUT) { USB_MSG("HOST_RXCSR NAK_TIMEOUT: %04X", host_csr); + /* Clear REQPKT before NAKTIMEOUT to abort transfer */ + HCD_CLR(host_csr, MUSB_VAL_HOST_RXCSR_REQPKT); + HCD_WR2(r, MUSB_REG_HOST_RXCSR, host_csr); + host_csr = HCD_RD2(r, MUSB_REG_HOST_RXCSR); HCD_CLR(host_csr, MUSB_VAL_HOST_RXCSR_NAKTIMEOUT); HCD_WR2(r, MUSB_REG_HOST_RXCSR, host_csr); return EXIT_FAILURE; } - return EXIT_SUCCESS; + USB_ASSERT(0, "Invalid state of HOST_RXCSR"); } USB_MSG("Invalid USB transfer error check: 0x%X, 0x%X, 0x%X", diff --git a/minix/drivers/usb/usbd/hcd/musb/musb_core.h b/minix/drivers/usb/usbd/hcd/musb/musb_core.h index 48f5d0bdb..f8f2987e7 100644 --- a/minix/drivers/usb/usbd/hcd/musb/musb_core.h +++ b/minix/drivers/usb/usbd/hcd/musb/musb_core.h @@ -5,21 +5,12 @@ #ifndef _MUSB_CORE_H_ #define _MUSB_CORE_H_ -#include +#include /*===========================================================================* * Types and constants * *===========================================================================*/ -/* Holds info on DATA toggle (DATA0/DATA1) initialization, - * required by bulk transfers */ -typedef enum { - - MUSB_DATATOG_UNKNOWN = 0, /* Default with memset 0 */ - MUSB_DATATOG_INIT -} -musb_datatog; - /* Structure to hold Mentor USB core configuration * May be more than one on a single chip * Should be initialized by MUSB's variant specific code (like AM335x) */ @@ -28,8 +19,8 @@ typedef struct { void * regs; /* Points to beginning of memory mapped registers */ hcd_reg1 ep; /* Currently used endpoint */ hcd_reg1 addr; /* Currently used address */ - musb_datatog datatog_tx[HCD_TOTAL_EP]; - musb_datatog datatog_rx[HCD_TOTAL_EP]; + hcd_datatog * datatog_tx; /* Should point at currently used TX toggle */ + hcd_datatog * datatog_rx; /* Should point at currently used RX toggle */ } musb_core_config; @@ -43,7 +34,8 @@ void musb_core_stop(void *); /* For HCD interface */ -void musb_setup_device(void *, hcd_reg1, hcd_reg1); +void musb_setup_device(void *, hcd_reg1, hcd_reg1, + hcd_datatog *, hcd_datatog *); int musb_reset_device(void *, hcd_speed *); void musb_setup_stage(void *, hcd_ctrlrequest *); void musb_rx_stage(void *, hcd_datarequest *); diff --git a/minix/drivers/usb/usbd/hcd/musb/musb_regs.h b/minix/drivers/usb/usbd/hcd/musb/musb_regs.h index 02028a0f5..49348302f 100644 --- a/minix/drivers/usb/usbd/hcd/musb/musb_regs.h +++ b/minix/drivers/usb/usbd/hcd/musb/musb_regs.h @@ -5,7 +5,7 @@ #ifndef _MUSB_REGS_H_ #define _MUSB_REGS_H_ -#include +#include /*===========================================================================* diff --git a/minix/drivers/usb/usbd/include/usb/hcd_common.h b/minix/drivers/usb/usbd/include/usbd/hcd_common.h similarity index 82% rename from minix/drivers/usb/usbd/include/usb/hcd_common.h rename to minix/drivers/usb/usbd/include/usbd/hcd_common.h index 72a55b9f1..d89c449aa 100644 --- a/minix/drivers/usb/usbd/include/usb/hcd_common.h +++ b/minix/drivers/usb/usbd/include/usbd/hcd_common.h @@ -124,10 +124,15 @@ hcd_direction; /* Possible asynchronous HCD events */ typedef enum { - HCD_EVENT_CONNECTED, - HCD_EVENT_DISCONNECTED, - HCD_EVENT_ENDPOINT, - HCD_EVENT_URB + HCD_EVENT_CONNECTED = 0, /* Device connected directly to root */ + HCD_EVENT_DISCONNECTED, /* Directly connected device removed */ + HCD_EVENT_PORT_LS_CONNECTED, /* Low speed device connected to hub */ + HCD_EVENT_PORT_FS_CONNECTED, /* Full speed device connected to hub */ + HCD_EVENT_PORT_HS_CONNECTED, /* High speed device connected to hub */ + HCD_EVENT_PORT_DISCONNECTED, /* Device disconnected from hub */ + HCD_EVENT_ENDPOINT, /* Something happened at endpoint */ + HCD_EVENT_URB, /* URB was submitted by device driver */ + HCD_EVENT_INVALID = 0xFF } hcd_event; @@ -149,6 +154,14 @@ typedef enum { } hcd_speed; +/* Possible data toggle values (at least for bulk transfer) */ +typedef enum { + + HCD_DATATOG_DATA0 = 0, + HCD_DATATOG_DATA1 = 1 +} +hcd_datatog; + /*===========================================================================* * HCD threading/device/URB types * @@ -161,7 +174,14 @@ typedef struct usb_ctrlrequest hcd_ctrlrequest; /* Largest value that can be transfered by this driver at a time * see MAXPAYLOAD in TXMAXP/RXMAXP */ -#define MAX_WTOTALLENGTH 1024 +#define MAX_WTOTALLENGTH 1024u + +/* TODO: This has corresponding redefinition in hub driver */ +/* Limit of child devices for each parent */ +#define HCD_CHILDREN 8u + +/* Total number of endpoints available in USB 2.0 */ +#define HCD_TOTAL_EP 16u /* Forward declarations */ typedef struct hcd_datarequest hcd_datarequest; @@ -189,6 +209,7 @@ struct hcd_urb { /* Basic */ void * original_urb; hcd_device_state * target_device; + void (*handled)(hcd_urb *); /* URB handled callback */ /* Transfer (in/out signifies what may be overwritten by HCD) */ hcd_ctrlrequest * in_setup; @@ -207,18 +228,26 @@ struct hcd_urb { /* Current state of attached device */ struct hcd_device_state { - hcd_driver_state * driver; /* Specific HCD driver object */ + hcd_device_state * parent; /* In case of hub attachment */ + hcd_device_state * child[HCD_CHILDREN]; /* In case of being hub */ + hcd_device_state * _next; /* To allow device lists */ + hcd_driver_state * driver; /* Specific HCD driver object */ hcd_thread * thread; hcd_lock * lock; void * data; - hcd_urb urb; + hcd_urb * urb; /* URB to be used by device */ + hcd_event wait_event; /* Expected event */ + hcd_reg1 wait_ep; /* Expected event's endpoint */ hcd_device_descriptor device_desc; hcd_configuration config_tree; hcd_reg1 max_packet_size; hcd_speed speed; hcd_state state; - hcd_reg1 address; + hcd_reg1 reserved_address; + hcd_reg1 current_address; + hcd_datatog ep_tx_tog[HCD_TOTAL_EP]; + hcd_datatog ep_rx_tog[HCD_TOTAL_EP]; /* * Control transfer's local data: @@ -247,24 +276,24 @@ struct hcd_device_state { #define HCD_DEFAULT_EP 0x00u #define HCD_DEFAULT_ADDR 0x00u #define HCD_DEFAULT_CONFIG 0x00u +#define HCD_FIRST_ADDR 0x01u #define HCD_LAST_ADDR 0x7Fu +#define HCD_TOTAL_ADDR 0x80u #define HCD_LAST_EP 0x0Fu -#define HCD_TOTAL_EP 0x10u -#define HCD_ANY_EP 0xFFu +#define HCD_UNUSED_VAL 0xFFu /* When number not needed */ +#define HCD_DEFAULT_NAKLIMIT 0x10u + /* Legal interval values */ #define HCD_LOWEST_INTERVAL 0x00u #define HCD_HIGHEST_INTERVAL 0xFFu -/* TODO: One device only */ -#define HCD_ATTACHED_ADDR 0x01u - /* Translates configuration number for 'set configuration' */ #define HCD_SET_CONFIG_NUM(num) ((num)+0x01u) /* Default MaxPacketSize for control transfer */ -#define HCD_LS_MAXPACKETSIZE 8u -#define HCD_HS_MAXPACKETSIZE 64u +#define HCD_LS_MAXPACKETSIZE 8u /* Low-speed, Full-speed */ +#define HCD_HS_MAXPACKETSIZE 64u /* High-speed */ #define HCD_MAX_MAXPACKETSIZE 1024u @@ -313,7 +342,13 @@ void hcd_disconnect_device(hcd_device_state *); void hcd_device_wait(hcd_device_state *, hcd_event, hcd_reg1); /* Unlocks device thread halted by 'hcd_device_wait' */ -void hcd_device_continue(hcd_device_state *); +void hcd_device_continue(hcd_device_state *, hcd_event, hcd_reg1); + +/* Allocation/deallocation of device structures */ +hcd_device_state * hcd_new_device(void); +void hcd_delete_device(hcd_device_state *); +void hcd_dump_devices(void); +int hcd_check_device(hcd_device_state *); /*===========================================================================* diff --git a/minix/drivers/usb/usbd/include/usb/hcd_ddekit.h b/minix/drivers/usb/usbd/include/usbd/hcd_ddekit.h similarity index 94% rename from minix/drivers/usb/usbd/include/usb/hcd_ddekit.h rename to minix/drivers/usb/usbd/include/usbd/hcd_ddekit.h index 446e9cd5c..b51365173 100644 --- a/minix/drivers/usb/usbd/include/usb/hcd_ddekit.h +++ b/minix/drivers/usb/usbd/include/usbd/hcd_ddekit.h @@ -5,7 +5,7 @@ #ifndef _HCD_DDEKIT_H_ #define _HCD_DDEKIT_H_ -#include +#include /*===========================================================================* * External declarations * diff --git a/minix/drivers/usb/usbd/include/usb/hcd_interface.h b/minix/drivers/usb/usbd/include/usbd/hcd_interface.h similarity index 62% rename from minix/drivers/usb/usbd/include/usb/hcd_interface.h rename to minix/drivers/usb/usbd/include/usbd/hcd_interface.h index 66a92665b..be225aded 100644 --- a/minix/drivers/usb/usbd/include/usb/hcd_interface.h +++ b/minix/drivers/usb/usbd/include/usbd/hcd_interface.h @@ -2,13 +2,14 @@ * Interface for HCD * * This file holds prototypes that must be implemented by HCD - * and event call that should be called when interrupt occurred + * and call that should be used for asynchronous events + * (interrupts, UBR submits, hub events, ...) */ #ifndef _HCD_INTERFACE_H_ #define _HCD_INTERFACE_H_ -#include +#include /*===========================================================================* @@ -17,13 +18,22 @@ /* Can be returned by 'read_data' to indicate error */ #define HCD_READ_ERR -1 +/* Possible states of USB device address */ +typedef enum { + + HCD_ADDR_AVAILABLE = 0, /* Default for reset */ + HCD_ADDR_USED +} +hcd_addr_state; + /*===========================================================================* - * HCD driver structure to be filled + * HCD driver structure to be filled * *===========================================================================*/ struct hcd_driver_state { /* Standard USB controller procedures */ - void (*setup_device) (void *, hcd_reg1, hcd_reg1); + void (*setup_device) (void *, hcd_reg1, hcd_reg1, + hcd_datatog *, hcd_datatog *); int (*reset_device) (void *, hcd_speed *); void (*setup_stage) (void *, hcd_ctrlrequest *); void (*rx_stage) (void *, hcd_datarequest *); @@ -39,20 +49,23 @@ struct hcd_driver_state { /* Controller's private data (like mapped registers) */ void * private_data; - /* Current state to be handled by driver */ - hcd_event current_event; - hcd_reg1 current_endpoint; - hcd_event expected_event; - hcd_reg1 expected_endpoint; + /* TODO: Only one port for each driver */ + /* Represents device attached to USB port handled by this driver */ + hcd_device_state * port_device; + + /* Array to hold information of unused device addresses */ + hcd_addr_state dev_addr[HCD_TOTAL_ADDR]; }; /*===========================================================================* * HCD event handling routine * *===========================================================================*/ -/* Handle asynchronous event - * This must be called in case of specific HCD interrupts listed above */ -void hcd_handle_event(hcd_driver_state *); +/* Handle asynchronous event */ +void hcd_handle_event(hcd_device_state *, hcd_event, hcd_reg1); + +/* This resolves port's device structure for given driver and event */ +void hcd_update_port(hcd_driver_state *, hcd_event); #endif /* !_HCD_INTERFACE_H_ */ diff --git a/minix/drivers/usb/usbd/include/usb/hcd_platforms.h b/minix/drivers/usb/usbd/include/usbd/hcd_platforms.h similarity index 100% rename from minix/drivers/usb/usbd/include/usb/hcd_platforms.h rename to minix/drivers/usb/usbd/include/usbd/hcd_platforms.h diff --git a/minix/drivers/usb/usbd/include/usbd/hcd_schedule.h b/minix/drivers/usb/usbd/include/usbd/hcd_schedule.h new file mode 100644 index 000000000..ba34f99fd --- /dev/null +++ b/minix/drivers/usb/usbd/include/usbd/hcd_schedule.h @@ -0,0 +1,16 @@ +/* + * HCD URB scheduler interface + */ + +#ifndef _HCD_SCHEDULE_H_ +#define _HCD_SCHEDULE_H_ + +#include + +/* Makes external (device driver) URB schedule enabled */ +int hcd_schedule_external_urb(hcd_urb *); + +/* Makes internal (HCD) URB schedule enabled */ +int hcd_schedule_internal_urb(hcd_urb *); + +#endif /* !_HCD_SCHEDULE_H_ */ diff --git a/minix/drivers/usb/usbd/include/usb/usb_common.h b/minix/drivers/usb/usbd/include/usbd/usbd_common.h similarity index 56% rename from minix/drivers/usb/usbd/include/usb/usb_common.h rename to minix/drivers/usb/usbd/include/usbd/usbd_common.h index 803699ec7..5d1bfc074 100644 --- a/minix/drivers/usb/usbd/include/usb/usb_common.h +++ b/minix/drivers/usb/usbd/include/usbd/usbd_common.h @@ -1,9 +1,9 @@ /* - * Whatever is commonly used throughout USB code + * Whatever is commonly used throughout USBD code */ -#ifndef _USB_COMMON_H_ -#define _USB_COMMON_H_ +#ifndef _USBD_COMMON_H_ +#define _USBD_COMMON_H_ /* For commonly used: NULL, EXIT_*, and stuff like that */ #include @@ -16,16 +16,23 @@ #define DEBUG #endif +/* This allows us to analyze thread context in + * consecutive function calls (DEBUG_DUMP) */ +#include + +/* Represents current thread's name string */ +#define HCD_THREAD_NAME ddekit_thread_get_name(ddekit_thread_myself()) + /*===========================================================================* * Standard output message * *===========================================================================*/ #define USB_MSG(fmt, ...) \ - do { \ - printf("USBD: "); \ - printf(fmt, ##__VA_ARGS__); \ - printf("\n"); \ - } while(0) + do { \ + printf("USBD: "); \ + printf(fmt, ##__VA_ARGS__); \ + printf("\n"); \ + } while(0) /*===========================================================================* @@ -33,16 +40,16 @@ *===========================================================================*/ #ifdef DEBUG #define DEBUG_DUMP \ - do { \ - printf("USBD (DEBUG %s)\n", __func__); \ - } while(0) + do { \ + printf("USBD: [%s -> %s]\n", HCD_THREAD_NAME, __func__);\ + } while(0) #define USB_DBG(fmt, ...) \ - do { \ - printf("USBD (DEBUG %s): ", __func__); \ - printf(fmt, ##__VA_ARGS__); \ - printf("\n"); \ - } while(0) + do { \ + printf("USBD: [%s -> %s] ", HCD_THREAD_NAME, __func__); \ + printf(fmt, ##__VA_ARGS__); \ + printf("\n"); \ + } while(0) #else #define DEBUG_DUMP ((void)0) @@ -54,13 +61,14 @@ * Assert for USB code * *===========================================================================*/ #define USB_ASSERT(cond, otherwise) \ - do { \ - if(!(cond)) { \ - USB_MSG("ASSERTION ERROR (%s:%d) - " \ - otherwise, __func__, __LINE__); \ - exit(EXIT_FAILURE); \ - } \ - } while(0) + do { \ + if (!(cond)) { \ + USB_MSG("ASSERTION ERROR (%s -> %s:%d) - " \ + otherwise, HCD_THREAD_NAME, \ + __func__, __LINE__); \ + exit(EXIT_FAILURE); \ + } \ + } while(0) -#endif /* !_USB_COMMON_H_ */ +#endif /* !_USBD_COMMON_H_ */ diff --git a/minix/drivers/usb/usbd/include/usb/usbd_interface.h b/minix/drivers/usb/usbd/include/usbd/usbd_interface.h similarity index 100% rename from minix/drivers/usb/usbd/include/usb/usbd_interface.h rename to minix/drivers/usb/usbd/include/usbd/usbd_interface.h diff --git a/minix/drivers/usb/usbd/include/usbd/usbd_schedule.h b/minix/drivers/usb/usbd/include/usbd/usbd_schedule.h new file mode 100644 index 000000000..2ee55e78c --- /dev/null +++ b/minix/drivers/usb/usbd/include/usbd/usbd_schedule.h @@ -0,0 +1,12 @@ +/* + * USBD URB scheduler interface + */ + +#ifndef _USBD_SCHEDULE_H_ +#define _USBD_SCHEDULE_H_ + +/* Should be used to create/destroy URB scheduler in base code */ +int usbd_init_scheduler(void); +void usbd_deinit_scheduler(void); + +#endif /* !_USBD_SCHEDULE_H_ */ diff --git a/minix/include/ddekit/usb.h b/minix/include/ddekit/usb.h index c3b514e5a..e6bb81066 100644 --- a/minix/include/ddekit/usb.h +++ b/minix/include/ddekit/usb.h @@ -78,12 +78,23 @@ struct ddekit_usb_urb { void *ddekit_priv; }; +/* USB message types */ +typedef enum { + + DDEKIT_HUB_PORT_LS_CONN, /* Low speed device connected */ + DDEKIT_HUB_PORT_FS_CONN, /* Full speed device connected */ + DDEKIT_HUB_PORT_HS_CONN, /* High speed device connected */ + DDEKIT_HUB_PORT_DISCONN /* Device disconnected */ +} +ddekit_msg_type_t; + int ddekit_usb_dev_set_data(struct ddekit_usb_dev *dev, void *data); void *ddekit_usb_dev_get_data(struct ddekit_usb_dev *dev); void ddekit_usb_get_device_id(struct ddekit_usb_dev *dev, struct ddekit_usb_device_id *id); int ddekit_usb_submit_urb(struct ddekit_usb_urb *d_urb); int ddekit_usb_cancle_urb(struct ddekit_usb_urb *d_urb); +long ddekit_usb_info(struct ddekit_usb_dev *, long, long); /* * This one is only implemented for the client side. For the server side is diff --git a/minix/include/minix/com.h b/minix/include/minix/com.h index 9b5d22b24..637f77c0b 100644 --- a/minix/include/minix/com.h +++ b/minix/include/minix/com.h @@ -775,7 +775,8 @@ #define USB_RQ_DEINIT (USB_BASE + 1) /* Quit the session */ #define USB_RQ_SEND_URB (USB_BASE + 2) /* Send URB */ #define USB_RQ_CANCEL_URB (USB_BASE + 3) /* Cancel URB */ -#define USB_REPLY (USB_BASE + 4) +#define USB_RQ_SEND_INFO (USB_BASE + 4) /* Sends various information */ +#define USB_REPLY (USB_BASE + 5) /* those are from USBD to driver */ @@ -793,6 +794,9 @@ # define USB_INTERFACES m4_l3 # define USB_RB_INIT_NAME m3_ca1 +# define USB_INFO_TYPE m4_l1 +# define USB_INFO_VALUE m4_l2 + /*===========================================================================* * Messages for DeviceManager (s/t like SysFS) * *===========================================================================*/ diff --git a/minix/include/minix/usb.h b/minix/include/minix/usb.h index 07845527d..1e52d68c4 100644 --- a/minix/include/minix/usb.h +++ b/minix/include/minix/usb.h @@ -152,4 +152,7 @@ int usb_init(char *name); /** This functions handles a message from the HCD */ int usb_handle_msg(struct usb_driver *ubd, message *msg); +/** Lets device driver send HCD various information */ +int usb_send_info(long, long); + #endif /* _MINIX_USB_H */ diff --git a/minix/lib/libc/arch/i386/Makefile.inc b/minix/lib/libc/arch/i386/Makefile.inc index 9b4457a93..6476ea62e 100644 --- a/minix/lib/libc/arch/i386/Makefile.inc +++ b/minix/lib/libc/arch/i386/Makefile.inc @@ -1,8 +1,6 @@ C_HERE=${NETBSDSRCDIR}/minix/lib/libc/arch/${ARCHSUBDIR} .PATH: ${C_HERE} -.warning looking into ${C_HERE} - SRCS+= _cpuid.S \ get_bp.S \ getprocessor.S \ diff --git a/minix/lib/libddekit/src/irq.c b/minix/lib/libddekit/src/irq.c index b60981aeb..da0a949ea 100644 --- a/minix/lib/libddekit/src/irq.c +++ b/minix/lib/libddekit/src/irq.c @@ -288,13 +288,13 @@ void _ddekit_interrupt_trigger(int irq_id) irq_s = find_by_irq_id(irq_id); if (irq_s) { - DDEBUG_MSG_VERBOSE("Triggering IRQ %d", irq); + DDEBUG_MSG_VERBOSE("Triggering IRQ %d", irq_s->irq); ddekit_sem_up(irq_s->sem); if (0 != (err_code = sys_irqenable(&irq_s->irq_hook))) ddekit_panic("Failed to enable interrupt " "(ERROR %d)", err_code); } else { - DDEBUG_MSG_WARN("no handler for IRQ %d", irq); + DDEBUG_MSG_WARN("no handler for IRQ %d", irq_s->irq); } } diff --git a/minix/lib/libddekit/src/usb_client.c b/minix/lib/libddekit/src/usb_client.c index c232e953e..5e56aeca3 100644 --- a/minix/lib/libddekit/src/usb_client.c +++ b/minix/lib/libddekit/src/usb_client.c @@ -186,6 +186,17 @@ int ddekit_usb_cancle_urb(struct ddekit_usb_urb *d_urb) return res; } + +/***************************************************************************** + * ddekit_usb_info * + *****************************************************************************/ +long +ddekit_usb_info(struct ddekit_usb_dev * UNUSED(dev), long type, long value) +{ + return usb_send_info(type, value); +} + + static void _ddekit_usb_thread() { struct ddekit_minix_msg_q *mq = ddekit_minix_create_msg_q(USB_BASE, diff --git a/minix/lib/libddekit/src/usb_server.c b/minix/lib/libddekit/src/usb_server.c index a45b65bd4..df200435c 100644 --- a/minix/lib/libddekit/src/usb_server.c +++ b/minix/lib/libddekit/src/usb_server.c @@ -71,6 +71,7 @@ static struct ddekit_usb_urb *ddekit_usb_urb_from_mx_urb(struct usb_urb *mx_urb); static void submit_urb(message *msg); static void cancle_urb(message *msg); +static void get_info(message *msg); static void completion_callback(void *priv); static void prepare_devman_usbdev(struct ddekit_usb_dev * dev, int @@ -441,6 +442,39 @@ static void cancle_urb(message *msg) } +/***************************************************************************** + * get_info * + *****************************************************************************/ +static void +get_info(message * msg) +{ + struct minix_usb_driver * drv; + endpoint_t ep; + long info_type; + long info_value; + + /* Read */ + ep = msg->m_source; + info_type = msg->USB_INFO_TYPE; + info_value = msg->USB_INFO_VALUE; + + /* Reuse as reply */ + msg->m_type = USB_REPLY; + msg->USB_RESULT = -1; + + /* Try and find driver first */ + if (NULL == (drv = find_driver(ep))) + ddekit_printf("Non-registered driver tries to send info"); + else + /* Route info to device */ + msg->USB_RESULT = ddekit_usb_info(_devices[drv->dev].dev, + info_type, info_value); + + /* Reply */ + ipc_send(ep, msg); +} + + /***************************************************************************** * completion_callback * ****************************************************************************/ @@ -713,6 +747,9 @@ static int handle_msg(message *msg) case USB_RQ_CANCEL_URB: cancle_urb(msg); return 1; + case USB_RQ_SEND_INFO: + get_info(msg); + return 1; default: return 0; } diff --git a/minix/lib/libusb/usb.c b/minix/lib/libusb/usb.c index a6c8f6f6e..772c58702 100644 --- a/minix/lib/libusb/usb.c +++ b/minix/lib/libusb/usb.c @@ -223,3 +223,32 @@ int usb_handle_msg(struct usb_driver *ud, message *msg) } } + +/***************************************************************************** + * usb_send_info * + *****************************************************************************/ +int +usb_send_info(long info_type, long info_value) +{ + int res; + message msg; + + /* Prepare message */ + msg.m_type = USB_RQ_SEND_INFO; + msg.USB_INFO_TYPE = info_type; + msg.USB_INFO_VALUE = info_value; + + /* Send/receive message */ + res = ipc_sendrec(hcd_ep, &msg); + + if (res != 0) + panic("usb_send_info: could not talk to HCD: %d", res); + + if (msg.m_type != USB_REPLY) + panic("usb_send_info: got illegal response from HCD: %d", msg.m_type); + + if (msg.USB_RESULT != 0) + panic("usb_send_info: got illegal response from HCD: %d", msg.m_type); + + return msg.USB_RESULT; +}