Written by JP Embedded.
Host controller (HCD), mass storage, and hub drivers.
Change-Id: I4237cf7aeb4a1c0205a1876593a9cc67ef3d577e
./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
./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
./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
${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 \
--- /dev/null
+usb_driver usb_hub
+{
+ binary = /service/usb_hub;
+ id {
+ bInterfaceClass = 0x09;
+ }
+ devprefix = usb_hub;
+}
usb_driver usb_storage
{
- binary = /usr/sbin/usb_storage;
+ binary = /service/usb_storage;
id {
bInterfaceClass = 0x08;
}
SUBDIR+= usbd
.endif # ${MACHINE_ARCH} == "earm"
-SUBDIR+= usb_storage
+SUBDIR+= usb_storage usb_hub
.include <bsd.subdir.mk>
--- /dev/null
+# 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 <minix.service.mk>
--- /dev/null
+/*
+ * Whatever is commonly used in hub driver, should be here
+ */
+
+#ifndef _COMMON_H_
+#define _COMMON_H_
+
+/*---------------------------*
+ * commonly used headers: *
+ *---------------------------*/
+#include <stdlib.h> /* For things, like EXIT_*, NULL, ... */
+#include <stdio.h>
+
+/*---------------------------*
+ * 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_ */
--- /dev/null
+/*
+ * URB formatting related implementation
+ */
+
+#include <minix/sysutil.h> /* panic */
+#include <minix/usb.h> /* struct usb_ctrlrequest */
+
+#include <string.h> /* memset */
+#include <assert.h>
+
+#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;
+ }
+ }
+}
--- /dev/null
+/*
+ * URB formatting related definitions
+ */
+
+#ifndef _URB_HELPER_H_
+#define _URB_HELPER_H_
+
+#include <ddekit/usb.h>
+#include <ddekit/semaphore.h>
+
+/* 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_ */
--- /dev/null
+/*
+ * Minix3 USB hub driver implementation
+ */
+
+#include <string.h> /* memset */
+#include <stdint.h>
+#include <time.h> /* nanosleep */
+
+#include <ddekit/thread.h>
+#include <minix/sef.h>
+#include <minix/sysutil.h> /* panic */
+#include <minix/usb.h> /* 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);
+}
--- /dev/null
+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;
+};
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 *
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;
}
+/*===========================================================================*
+ * 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 *
*===========================================================================*/
#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)
#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)
#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)
* using DDEkit, and libblockdriver
*/
+#include <sys/cdefs.h> /* __CTASSERT() */
#include <sys/ioc_disk.h> /* cases for mass_storage_ioctl */
#ifdef USB_STORAGE_SIGNAL
#include <sys/signal.h> /* signal handling */
#include <assert.h>
#include <limits.h> /* ULONG_MAX */
+#include <time.h> /* nanosleep */
#include "common.h"
#include "bulk.h"
/* 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 *,
/*---------------------------*
* defined variables *
*---------------------------*/
+#define MASS_PACKED __attribute__((__packed__))
+
/* Mass Storage callback structure */
static struct blockdriver mass_storage = {
.bdr_type = BLOCKDRIVER_TYPE_DISK,
/* 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 *
*---------------------------*/
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;
}
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;
}
/*===========================================================================*
* 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)
# 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
#include <minix/board.h>
#include <minix/syslib.h>
-#include <usb/hcd_platforms.h>
-#include <usb/usb_common.h>
-#include <usb/usbd_interface.h>
+#include <usbd/hcd_platforms.h>
+#include <usbd/usbd_common.h>
+#include <usbd/usbd_interface.h>
/*===========================================================================*
#include <minix/devman.h> /* Initializing 'devman' */
#include <minix/sef.h> /* SEF handling */
-#include <usb/usb_common.h>
-#include <usb/usbd_interface.h>
+#include <usbd/usbd_common.h>
+#include <usbd/usbd_interface.h>
+#include <usbd/usbd_schedule.h>
/*===========================================================================*
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();
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:
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);
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);
}
#include <minix/drivers.h> /* errno with sign */
-#include <usb/hcd_common.h>
-#include <usb/hcd_ddekit.h>
-#include <usb/hcd_interface.h>
-#include <usb/usb_common.h>
+#include <usbd/hcd_common.h>
+#include <usbd/hcd_ddekit.h>
+#include <usbd/hcd_interface.h>
+#include <usbd/hcd_schedule.h>
+#include <usbd/usbd_common.h>
/*===========================================================================*
/* 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;
+ }
+
+#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;
+
+ base = device;
- /* 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);
+ while (NULL != base->parent)
+ base = base->parent;
- /* Set what was received for device thread to use */
- this_device->driver = driver;
+ 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;
-
- /* 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");
+ USB_ASSERT((HCD_STATE_DISCONNECTED != device->state),
+ "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:
+ 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("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 */
- if (HCD_STATE_DISCONNECTED != this_device->state)
- hcd_device_continue(this_device);
- else
- USB_MSG("Device is marked as 'disconnected' "
- "for 'EP' event");
+ 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");
}
}
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");
/* 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 */
/* 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 *
*===========================================================================*/
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)) {
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;
+
+ /* Dump device descriptor in debug mode */
+#ifdef DEBUG
+ {
+ hcd_device_descriptor * d;
+ d = &(this_device->device_desc);
+
+ USB_DBG("<<DEVICE>>");
+ 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 address */
- if (EXIT_SUCCESS != hcd_set_address(this_device, HCD_ATTACHED_ADDR)) {
+ /* 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");
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 */
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("<<DEVICE>>");
- 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;
}
* 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;
}
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 */
setup.bRequest = 0x06; /* Get descriptor */
setup.wValue = 0x0200 | HCD_DEFAULT_CONFIG;
setup.wIndex = 0x0000;
- setup.wLength = buffer_length;
+ setup.wLength = sizeof(temp_config_descriptor);
+
+ /* 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;
+ }
- /* 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;
+ /* Check if expected size was received */
+ if (urb.out_size != setup.wLength) {
+ USB_MSG("URB submission returned "
+ "invalid amount of data");
+ goto FINISH;
}
+ }
- /* If we only asked for configuration itself
- * then ask again for other descriptors */
- if (sizeof(config_descriptor) == buffer_length) {
+ /* Get total expected length */
+ expected_length = UGETW(temp_config_descriptor.wTotalLength);
- /* 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;
+ /* Check for abnormal value */
+ if (expected_length > HCD_SANE_DESCRIPTOR_LENGTH) {
+ USB_MSG("Total descriptor length declared is too high");
+ goto FINISH;
+ }
- /* Continue only if there is more data */
- total_length = UGETW(config_descriptor.wTotalLength);
+ /* Get descriptor buffer to hold everything expected */
+ if (NULL == (expected_buffer = malloc(expected_length))) {
+ USB_MSG("Descriptor allocation failed");
+ goto FINISH;
+ }
- 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;
- }
+ /* 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;
+ }
- } 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;
- /* ... and create tree based on received buffer */
- if (EXIT_SUCCESS != hcd_buffer_to_tree(this_device->control_data,
- this_device->control_len,
+ 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;
}
hcd_set_configuration(hcd_device_state * this_device, hcd_reg1 configuration)
{
hcd_ctrlrequest setup;
+ hcd_urb urb;
DEBUG_DUMP;
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;
- }
-
- return EXIT_SUCCESS;
+ /* 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);
+
+ 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... */
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;
}
} 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;
}
}
/* 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 */
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);
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... */
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);
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;
* 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;
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;
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... */
/* 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) {
#include <minix/clkconf.h> /* clkconf_* */
#include <minix/syslib.h> /* sys_privctl */
-#include <usb/hcd_common.h>
-#include <usb/hcd_interface.h>
-#include <usb/usb_common.h>
+#include <usbd/hcd_common.h>
+#include <usbd/hcd_interface.h>
+#include <usbd/usbd_common.h>
/*===========================================================================*
* 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 *
/*===========================================================================*
- * 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);
+
+ /* Release reserved address */
+ if (HCD_DEFAULT_ADDR != this_device->reserved_address)
+ hcd_release_addr(this_device->driver,
+ this_device->reserved_address);
- this_device->thread = NULL;
- this_device->lock = NULL;
+ /* Mark as disconnected */
+ this_device->state = HCD_STATE_DISCONNECTED;
}
* 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;
+ USB_DBG("0x%08X wait (0x%02X, 0x%02X)", device, event, ep);
+
+ device->wait_event = event;
+ device->wait_ep = ep;
+
+ ddekit_sem_down(device->lock);
+}
+
+
+/*===========================================================================*
+ * hcd_device_continue *
+ *===========================================================================*/
+void
+hcd_device_continue(hcd_device_state * device, hcd_event event, hcd_reg1 ep)
+{
DEBUG_DUMP;
- drv = (hcd_driver_state *)this_device->driver;
+ 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);
+}
+
- drv->expected_event = event;
- drv->expected_endpoint = ep;
+/*===========================================================================*
+ * hcd_new_device *
+ *===========================================================================*/
+hcd_device_state *
+hcd_new_device(void)
+{
+ hcd_device_state * d;
- USB_DBG("Waiting for: ev=0x%X, ep=0x%X", (int)event, ep);
+ DEBUG_DUMP;
- ddekit_sem_down(this_device->lock);
+ /* One new blank device */
+ d = calloc(1, sizeof(*d));
+
+ USB_ASSERT(NULL != d, "Failed to allocate device");
+
+ if (NULL == dev_list) {
+ dev_list = d;
+ } else {
+ d->_next = dev_list;
+ dev_list = d;
+ }
+
+#ifdef HCD_DUMP_DEVICE_LIST
+ /* Dump updated state of device list */
+ hcd_dump_devices();
+#endif
+
+ return d;
}
/*===========================================================================*
- * hcd_device_continue *
+ * 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_device_continue(hcd_device_state * this_device)
+hcd_dump_devices(void)
{
- hcd_driver_state * drv;
+ hcd_device_state * temp;
DEBUG_DUMP;
- drv = (hcd_driver_state *)this_device->driver;
+ temp = dev_list;
- /* We need to get what was expected... */
- USB_ASSERT(drv->current_event == drv->expected_event,
- "Unexpected event occurred");
+ USB_MSG("Allocated devices:");
- /* ...including endpoint interrupts */
- if (HCD_EVENT_ENDPOINT == drv->current_event) {
- USB_ASSERT(drv->current_endpoint == drv->expected_endpoint,
- "Unexpected endpoint interrupt");
+ 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;
- ddekit_sem_up(this_device->lock);
+ /* 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;
}
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;
+}
#include <ddekit/usb.h>
-#include <usb/hcd_ddekit.h>
-#include <usb/hcd_interface.h>
-#include <usb/usb_common.h>
+#include <usbd/hcd_ddekit.h>
+#include <usbd/hcd_interface.h>
+#include <usbd/hcd_schedule.h>
+#include <usbd/usbd_common.h>
/*===========================================================================*
*===========================================================================*/
/*
* In this file "struct ddekit_usb_dev" equals "hcd_device_state"
- * */
+ */
struct ddekit_usb_device_id;
struct ddekit_usb_urb;
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 *
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);
}
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 *
*===========================================================================*/
/* 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);
+ }
}
{
DEBUG_DUMP;
+ /* Remember original */
+ urb->original_urb = (void *)dde_urb;
+
/* No UBR error initially */
urb->inout_status = EXIT_SUCCESS;
{
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;
+}
--- /dev/null
+/*
+ * Implementation of HCD URB scheduler
+ */
+
+#include <string.h> /* memset */
+
+#include <usbd/hcd_common.h>
+#include <usbd/hcd_ddekit.h>
+#include <usbd/hcd_interface.h>
+#include <usbd/hcd_schedule.h>
+#include <usbd/usbd_common.h>
+#include <usbd/usbd_schedule.h>
+
+
+/*===========================================================================*
+ * 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;
+}
#include <string.h> /* memset */
-#include <usb/hcd_common.h>
-#include <usb/hcd_platforms.h>
-#include <usb/hcd_interface.h>
-#include <usb/usb_common.h>
+#include <usbd/hcd_common.h>
+#include <usbd/hcd_platforms.h>
+#include <usbd/hcd_interface.h>
+#include <usbd/usbd_common.h>
#include "musb_core.h"
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
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();
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;
}
#include <string.h> /* memcpy */
-#include <usb/hcd_common.h>
-#include <usb/hcd_interface.h>
-#include <usb/usb_common.h>
+#include <usbd/hcd_common.h>
+#include <usbd/hcd_interface.h>
+#include <usbd/usbd_common.h>
#include "musb_core.h"
#include "musb_regs.h"
* 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;
}
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);
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;
/* 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);
}
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);
}
}
musb_error_case;
+ musb_core_config * core;
void * r;
hcd_reg2 host_csr;
musb_error_case error_case;
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);
/* 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);
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);
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",
#ifndef _MUSB_CORE_H_
#define _MUSB_CORE_H_
-#include <usb/hcd_common.h>
+#include <usbd/hcd_common.h>
/*===========================================================================*
* 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) */
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;
/* 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 *);
#ifndef _MUSB_REGS_H_
#define _MUSB_REGS_H_
-#include <usb/hcd_common.h>
+#include <usbd/hcd_common.h>
/*===========================================================================*
/* 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;
}
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 *
/* 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;
/* 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;
/* 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:
#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
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 *);
/*===========================================================================*
#ifndef _HCD_DDEKIT_H_
#define _HCD_DDEKIT_H_
-#include <usb/hcd_common.h>
+#include <usbd/hcd_common.h>
/*===========================================================================*
* External declarations *
* 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 <usb/hcd_common.h>
+#include <usbd/hcd_common.h>
/*===========================================================================*
/* 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 *);
/* 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_ */
--- /dev/null
+/*
+ * HCD URB scheduler interface
+ */
+
+#ifndef _HCD_SCHEDULE_H_
+#define _HCD_SCHEDULE_H_
+
+#include <usbd/hcd_common.h>
+
+/* 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_ */
/*
- * 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 <stdlib.h>
#define DEBUG
#endif
+/* This allows us to analyze thread context in
+ * consecutive function calls (DEBUG_DUMP) */
+#include <ddekit/thread.h>
+
+/* 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)
/*===========================================================================*
*===========================================================================*/
#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)
* 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_ */
--- /dev/null
+/*
+ * 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_ */
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
#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 */
# 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) *
*===========================================================================*/
/** 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 */
C_HERE=${NETBSDSRCDIR}/minix/lib/libc/arch/${ARCHSUBDIR}
.PATH: ${C_HERE}
-.warning looking into ${C_HERE}
-
SRCS+= _cpuid.S \
get_bp.S \
getprocessor.S \
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);
}
}
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,
*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
}
+/*****************************************************************************
+ * 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 *
****************************************************************************/
case USB_RQ_CANCEL_URB:
cancle_urb(msg);
return 1;
+ case USB_RQ_SEND_INFO:
+ get_info(msg);
+ return 1;
default:
return 0;
}
}
}
+
+/*****************************************************************************
+ * 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;
+}