]> Zhao Yanbai Git Server - minix.git/commitdiff
Much USB code for ARM USB support 91/2791/3
authorWojciech Zajac <wzajac@jpembedded.eu>
Thu, 26 Jun 2014 12:05:41 +0000 (14:05 +0200)
committerBen Gras <ben@minix3.org>
Fri, 29 Aug 2014 16:52:49 +0000 (18:52 +0200)
Written by JP Embedded.

Host controller (HCD), mass storage, and hub drivers.

Change-Id: I4237cf7aeb4a1c0205a1876593a9cc67ef3d577e

41 files changed:
distrib/sets/lists/minix/mi
etc/Makefile
etc/devmand/usb_hub.cfg [new file with mode: 0644]
etc/devmand/usb_storage.cfg
minix/drivers/usb/Makefile
minix/drivers/usb/usb_hub/Makefile [new file with mode: 0644]
minix/drivers/usb/usb_hub/common.h [new file with mode: 0644]
minix/drivers/usb/usb_hub/urb_helper.c [new file with mode: 0644]
minix/drivers/usb/usb_hub/urb_helper.h [new file with mode: 0644]
minix/drivers/usb/usb_hub/usb_hub.c [new file with mode: 0644]
minix/drivers/usb/usb_hub/usb_hub.conf [new file with mode: 0644]
minix/drivers/usb/usb_storage/scsi.c
minix/drivers/usb/usb_storage/scsi.h
minix/drivers/usb/usb_storage/usb_storage.c
minix/drivers/usb/usbd/base/earm/Makefile
minix/drivers/usb/usbd/base/earm/usbd_earm.c
minix/drivers/usb/usbd/base/usbd.c
minix/drivers/usb/usbd/hcd/hcd.c
minix/drivers/usb/usbd/hcd/hcd_common.c
minix/drivers/usb/usbd/hcd/hcd_ddekit.c
minix/drivers/usb/usbd/hcd/hcd_schedule.c [new file with mode: 0644]
minix/drivers/usb/usbd/hcd/musb/musb_am335x.c
minix/drivers/usb/usbd/hcd/musb/musb_core.c
minix/drivers/usb/usbd/hcd/musb/musb_core.h
minix/drivers/usb/usbd/hcd/musb/musb_regs.h
minix/drivers/usb/usbd/include/usbd/hcd_common.h [moved from minix/drivers/usb/usbd/include/usb/hcd_common.h with 82% similarity]
minix/drivers/usb/usbd/include/usbd/hcd_ddekit.h [moved from minix/drivers/usb/usbd/include/usb/hcd_ddekit.h with 94% similarity]
minix/drivers/usb/usbd/include/usbd/hcd_interface.h [moved from minix/drivers/usb/usbd/include/usb/hcd_interface.h with 62% similarity]
minix/drivers/usb/usbd/include/usbd/hcd_platforms.h [moved from minix/drivers/usb/usbd/include/usb/hcd_platforms.h with 100% similarity]
minix/drivers/usb/usbd/include/usbd/hcd_schedule.h [new file with mode: 0644]
minix/drivers/usb/usbd/include/usbd/usbd_common.h [moved from minix/drivers/usb/usbd/include/usb/usb_common.h with 56% similarity]
minix/drivers/usb/usbd/include/usbd/usbd_interface.h [moved from minix/drivers/usb/usbd/include/usb/usbd_interface.h with 100% similarity]
minix/drivers/usb/usbd/include/usbd/usbd_schedule.h [new file with mode: 0644]
minix/include/ddekit/usb.h
minix/include/minix/com.h
minix/include/minix/usb.h
minix/lib/libc/arch/i386/Makefile.inc
minix/lib/libddekit/src/irq.c
minix/lib/libddekit/src/usb_client.c
minix/lib/libddekit/src/usb_server.c
minix/lib/libusb/usb.c

index 6f3ad627b821441d1b8295cb584ebc4a36ed7756..8546028f28eaa85aeb85374423eadc48571593a8 100644 (file)
@@ -82,6 +82,7 @@
 ./etc/devmand/scripts                  minix-sys
 ./etc/devmand/scripts/block            minix-sys
 ./etc/devmand/scripts/singlechar       minix-sys
+./etc/devmand/usb_hub.cfg              minix-sys
 ./etc/devmand/usb_storage.cfg          minix-sys
 ./etc/gettytab                         minix-sys
 ./etc/group                            minix-sys
 ./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
index 766ff3762b3613b9f97fb889ed48f55ea14a89fc..8f166206463f6d6a6e79ad0008677e05813f6cb5 100644 (file)
@@ -430,6 +430,7 @@ install-etc-files-safe: .PHONY .MAKE check_DESTDIR MAKEDEV
                ${BINOWN} ${BINGRP}     ${BINMODE}      ${NETBSDSRCDIR}/etc/ ${DESTDIR}/usr/lib/ crontab \
                ${BINOWN} ${BINGRP}     ${BINMODE}      ${NETBSDSRCDIR}/etc/ ${DESTDIR}/etc/ system.conf \
                ${BINOWN} ${BINGRP}     ${NONBINMODE}   ${NETBSDSRCDIR}/etc/usr/ ${DESTDIR}/usr/ Makefile \
+               ${BINOWN} ${BINGRP}     ${BINMODE}      ${NETBSDSRCDIR}/etc/devmand/ ${DESTDIR}/etc/devmand/ usb_hub.cfg \
                ${BINOWN} ${BINGRP}     ${BINMODE}      ${NETBSDSRCDIR}/etc/devmand/ ${DESTDIR}/etc/devmand/ usb_storage.cfg \
                ${BINOWN} ${BINGRP}     ${BINMODE}      ${NETBSDSRCDIR}/etc/devmand/scripts/ ${DESTDIR}/etc/devmand/scripts/ block  \
                ${BINOWN} ${BINGRP}     ${BINMODE}      ${NETBSDSRCDIR}/etc/devmand/scripts/ ${DESTDIR}/etc/devmand/scripts/ singlechar \
diff --git a/etc/devmand/usb_hub.cfg b/etc/devmand/usb_hub.cfg
new file mode 100644 (file)
index 0000000..71b534a
--- /dev/null
@@ -0,0 +1,8 @@
+usb_driver usb_hub
+{
+       binary = /service/usb_hub;
+       id {
+               bInterfaceClass = 0x09;
+       }
+       devprefix = usb_hub;
+}
index 7839f9b139b435d483738da8d2c6f07a6eb8e040..03c85955c8ee89e6a1721be2d7576939264d0c39 100644 (file)
@@ -1,6 +1,6 @@
 usb_driver usb_storage
 {
-       binary = /usr/sbin/usb_storage;
+       binary = /service/usb_storage;
        id {
                bInterfaceClass = 0x08;
        }
index 992e26ac4e8e7c063a08aeb0f2ce5cdb30a1050a..6ebeeb593988975fe8b1a7b35bb81e8858696ca6 100644 (file)
@@ -4,6 +4,6 @@
 SUBDIR+=       usbd
 .endif # ${MACHINE_ARCH} == "earm"
 
-SUBDIR+=       usb_storage
+SUBDIR+=       usb_storage usb_hub
 
 .include <bsd.subdir.mk>
diff --git a/minix/drivers/usb/usb_hub/Makefile b/minix/drivers/usb/usb_hub/Makefile
new file mode 100644 (file)
index 0000000..acfd540
--- /dev/null
@@ -0,0 +1,16 @@
+# Makefile for USB hub driver
+PROG=usb_hub
+SRCS=usb_hub.c urb_helper.c
+
+FILES=$(PROG).conf
+FILESNAME=$(PROG)
+FILESDIR=/etc/system.conf.d
+
+DPADD+=${LIBDDEKIT_USB_CLIENT} ${LIBDDEKIT} ${LIBUSB} ${LIBSYS} ${LIBMINLIB}
+LDADD+=-lddekit_usb_client -lddekit -lusb -lsys
+
+#For easier debugging, uncomment:
+#LDADD+=-Wl,-Ttext=0x800000
+#CPPFLAGS+=-DHUB_DEBUG
+
+.include <minix.service.mk>
diff --git a/minix/drivers/usb/usb_hub/common.h b/minix/drivers/usb/usb_hub/common.h
new file mode 100644 (file)
index 0000000..de891ac
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * 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_ */
diff --git a/minix/drivers/usb/usb_hub/urb_helper.c b/minix/drivers/usb/usb_hub/urb_helper.c
new file mode 100644 (file)
index 0000000..c7a93a3
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+ * 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;
+               }
+       }
+}
diff --git a/minix/drivers/usb/usb_hub/urb_helper.h b/minix/drivers/usb/usb_hub/urb_helper.h
new file mode 100644 (file)
index 0000000..c9033c9
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * 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_ */
diff --git a/minix/drivers/usb/usb_hub/usb_hub.c b/minix/drivers/usb/usb_hub/usb_hub.c
new file mode 100644 (file)
index 0000000..6925f68
--- /dev/null
@@ -0,0 +1,937 @@
+/*
+ * 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);
+}
diff --git a/minix/drivers/usb/usb_hub/usb_hub.conf b/minix/drivers/usb/usb_hub/usb_hub.conf
new file mode 100644 (file)
index 0000000..f0fecca
--- /dev/null
@@ -0,0 +1,15 @@
+service usb_hub
+{
+       system
+               PRIVCTL     #  4
+               UMAP        # 14
+               IRQCTL      # 19
+               DEVIO       # 21
+               SDEVIO      # 22
+               ;
+       ipc
+               SYSTEM pm rs log tty ds vfs vm amddev devman
+               pci usbd
+               ;
+       uid     0;
+};
index 8ed74d419e4d71ba48c366a4367c877edd1e0b49..128039f8b57ca72b45fd1e2a5de8f9e5f1e7a504 100644 (file)
@@ -20,6 +20,7 @@ static int create_read_capacity_scsi_cmd(mass_storage_cbw *);
 static int create_write_scsi_cmd(mass_storage_cbw *, scsi_transfer *);
 static int create_read_scsi_cmd(mass_storage_cbw *, scsi_transfer *);
 static int create_mode_sense_scsi_cmd(mass_storage_cbw *);
+static int create_request_sense_scsi_cmd(mass_storage_cbw *);
 
 /*---------------------------*
  *    defined functions      *
@@ -47,6 +48,8 @@ create_scsi_cmd(mass_storage_cbw * cbw, int cmd, scsi_transfer * info)
                        return create_read_scsi_cmd(cbw, info);
                case SCSI_MODE_SENSE:
                        return create_mode_sense_scsi_cmd(cbw);
+               case SCSI_REQUEST_SENSE:
+                       return create_request_sense_scsi_cmd(cbw);
                default:
                        MASS_MSG("Invalid SCSI command!");
                        return EXIT_FAILURE;
@@ -175,6 +178,25 @@ create_mode_sense_scsi_cmd(mass_storage_cbw * cbw)
 }
 
 
+/*===========================================================================*
+ *    create_request_sense_scsi_cmd                                          *
+ *===========================================================================*/
+static int
+create_request_sense_scsi_cmd(mass_storage_cbw * cbw)
+{
+       MASS_DEBUG_DUMP;
+
+       cbw->dCBWDataTransferLength = SCSI_REQUEST_SENSE_DATA_LEN;
+       cbw->bCBWFlags = CBW_FLAGS_IN;
+       cbw->bCDBLength = SCSI_REQUEST_SENSE_CMD_LEN;
+
+       SCSI_SET_REQUEST_SENSE_OP_CODE(cbw->CBWCB);
+       SCSI_SET_REQUEST_SENSE_ALLOC(cbw->CBWCB, SCSI_REQUEST_SENSE_DATA_LEN);
+
+       return EXIT_SUCCESS;
+}
+
+
 /*===========================================================================*
  *    check_inquiry_reply                                                    *
  *===========================================================================*/
index 91d28ef75ec77d19ecdeeb84f80689d0b8a1d47c..32a728b7c38512d37f121e20a51d3f4c917c9a24 100644 (file)
 
 #include "bulk.h"
 
-#define SCSI_FORMAT_UNIT                       (0x04)
 #define SCSI_INQUIRY                           (0x12)
-#define SCSI_START_STOP                                (0x1B)
-#define SCSI_MODE_SELECT                       (0x55)
 #define SCSI_MODE_SENSE                                (0x5A)
-#define SCSI_PREVENT_ALLOW                     (0x1E)
 #define SCSI_READ                              (0x28)
-#define SCSI_READ_12                           (0xA8)
 #define SCSI_READ_CAPACITY                     (0x25)
-#define SCSI_READ_FORMAT_CAP                   (0x23)
 #define SCSI_REQUEST_SENSE                     (0x03)
-#define SCSI_REZERO_UNIT                       (0x01)
-#define SCSI_SEEK                              (0x2B)
-#define SCSI_SEND_DIAGNOSTIC                   (0x1D)
 #define SCSI_TEST_UNIT_READY                   (0x00)
-#define SCSI_VERIFY                            (0x2F)
 #define SCSI_WRITE                             (0x2A)
-#define SCSI_WRITE_12                          (0xAA)
-#define SCSI_WRITE_VERIFY                      (0x2E)
 
 #define SCSI_INQUIRY_DATA_LEN                  (36)
 #define SCSI_INQUIRY_CMD_LEN                   (6)
@@ -59,6 +47,9 @@
 #define SCSI_READ_CAPACITY_DATA_LEN            (8)
 #define SCSI_READ_CAPACITY_CMD_LEN             (10)
 
+#define SCSI_REQUEST_SENSE_DATA_LEN            (18)
+#define SCSI_REQUEST_SENSE_CMD_LEN             (6)
+
 #define SCSI_TEST_DATA_LEN                     (0)
 #define SCSI_TEST_CMD_LEN                      (6)
 
 #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)
 
index ecafdccba253a52aa6bc2ee670648406c735d102..c40789a374ea6668a7ddb72459ffc0ea0bbe4eee 100644 (file)
@@ -3,6 +3,7 @@
  * 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 */
@@ -24,6 +25,7 @@
 
 #include <assert.h>
 #include <limits.h>                    /* ULONG_MAX */
+#include <time.h>                      /* nanosleep */
 
 #include "common.h"
 #include "bulk.h"
@@ -62,6 +64,7 @@ static void ddekit_usb_task(void *);
 /* Mass storage related prototypes */
 static void mass_storage_task(void *);
 static int mass_storage_test(void);
+static int mass_storage_check_error(void);
 static int mass_storage_try_first_open(void);
 static int mass_storage_transfer_restrictions(u64_t, unsigned long);
 static ssize_t mass_storage_write(unsigned long, endpoint_t, iovec_t *,
@@ -96,6 +99,8 @@ static int mass_storage_parse_descriptors(char *, unsigned int, urb_ep_config *,
 /*---------------------------*
  *    defined variables      *
  *---------------------------*/
+#define MASS_PACKED __attribute__((__packed__))
+
 /* Mass Storage callback structure */
 static struct blockdriver mass_storage = {
        .bdr_type       = BLOCKDRIVER_TYPE_DISK,
@@ -149,6 +154,10 @@ static unsigned char buffer[BUFFER_SIZE];
 /* Maximum 'Test Unit Ready' command retries */
 #define MAX_TEST_RETRIES 3
 
+/* 'Test Unit Ready' failure delay time (in nanoseconds) */
+#define NEXT_TEST_DELAY 50000000 /* 50ms */
+
+
 /*---------------------------*
  *    defined functions      *
  *---------------------------*/
@@ -589,23 +598,121 @@ static int
 mass_storage_test(void)
 {
        int repeat;
+       int error;
+
+       struct timespec test_wait;
 
        MASS_DEBUG_DUMP;
 
+       /* Delay between consecutive test commands, in case of their failure */
+       test_wait.tv_nsec = NEXT_TEST_DELAY;
+       test_wait.tv_sec = 0;
+
        for (repeat = 0; repeat < MAX_TEST_RETRIES; repeat++) {
 
                /* SCSI TEST UNIT READY OUT stage */
                if (mass_storage_send_scsi_cbw_out(SCSI_TEST_UNIT_READY, NULL))
-                       return EXIT_FAILURE;
+                       return EIO;
 
                /* TODO: Only CSW failure should normally contribute to retry */
 
                /* SCSI TEST UNIT READY IN stage */
                if (EXIT_SUCCESS == mass_storage_send_scsi_csw_in())
                        return EXIT_SUCCESS;
+
+               /* Check for errors */
+               if (EXIT_SUCCESS != (error = mass_storage_check_error())) {
+                       MASS_MSG("SCSI sense error checking failed");
+                       return error;
+               }
+
+               /* Ignore potential signal interruption (no return value check),
+                * since it causes driver termination anyway */
+               if (nanosleep(&test_wait, NULL))
+                       MASS_MSG("Calling nanosleep() failed");
        }
 
-       return EXIT_FAILURE;
+       return EIO;
+}
+
+
+/*===========================================================================*
+ *    mass_storage_check_error                                               *
+ *===========================================================================*/
+static int
+mass_storage_check_error(void)
+{
+       /* SCSI sense structure for local use */
+       typedef struct MASS_PACKED scsi_sense {
+
+               uint8_t code : 7;
+               uint8_t valid : 1;
+               uint8_t obsolete : 8;
+               uint8_t sense : 4;
+               uint8_t reserved : 1;
+               uint8_t ili : 1;
+               uint8_t eom : 1;
+               uint8_t filemark : 1;
+               uint32_t information : 32;
+               uint8_t additional_len : 8;
+               uint32_t command_specific : 32;
+               uint8_t additional_code : 8;
+               uint8_t additional_qual : 8;
+               uint8_t unit_code : 8;
+               uint8_t key_specific1 : 7;
+               uint8_t sksv : 1;
+               uint16_t key_specific2 : 16;
+       }
+       scsi_sense;
+
+       /* Sense variable to hold received data */
+       scsi_sense sense;
+
+       MASS_DEBUG_DUMP;
+
+       /* Check if bit-fields are packed correctly */
+       __CTASSERT(sizeof(sense) == SCSI_REQUEST_SENSE_DATA_LEN);
+
+       /* SCSI REQUEST SENSE OUT stage */
+       if (mass_storage_send_scsi_cbw_out(SCSI_REQUEST_SENSE, NULL))
+               return EIO;
+
+       /* SCSI REQUEST SENSE first IN stage */
+       if (mass_storage_send_scsi_data_in(&sense, sizeof(sense)))
+               return EIO;
+
+       /* SCSI REQUEST SENSE second IN stage */
+       if (mass_storage_send_scsi_csw_in())
+               return EIO;
+
+       /* When any sense code is present something may have failed */
+       if (sense.sense) {
+#ifdef MASS_DEBUG
+               MASS_MSG("SCSI sense:                ");
+               MASS_MSG("code             : %8X", sense.code            );
+               MASS_MSG("valid            : %8X", sense.valid           );
+               MASS_MSG("obsolete         : %8X", sense.obsolete        );
+               MASS_MSG("sense            : %8X", sense.sense           );
+               MASS_MSG("reserved         : %8X", sense.reserved        );
+               MASS_MSG("ili              : %8X", sense.ili             );
+               MASS_MSG("eom              : %8X", sense.eom             );
+               MASS_MSG("filemark         : %8X", sense.filemark        );
+               MASS_MSG("information      : %8X", sense.information     );
+               MASS_MSG("additional_len   : %8X", sense.additional_len  );
+               MASS_MSG("command_specific : %8X", sense.command_specific);
+               MASS_MSG("additional_code  : %8X", sense.additional_code );
+               MASS_MSG("additional_qual  : %8X", sense.additional_qual );
+               MASS_MSG("unit_code        : %8X", sense.unit_code       );
+               MASS_MSG("key_specific1    : %8X", sense.key_specific1   );
+               MASS_MSG("sksv             : %8X", sense.sksv            );
+               MASS_MSG("key_specific2    : %8X", sense.key_specific2   );
+#else
+               MASS_MSG("SCSI sense: 0x%02X 0x%02X 0x%02X", sense.sense,
+                       sense.additional_code, sense.additional_qual);
+#endif
+       }
+
+       return EXIT_SUCCESS;
 }
 
 
@@ -1072,6 +1179,12 @@ mass_storage_open(devminor_t minor, int UNUSED(access))
                if ((r = mass_storage_try_first_open())) {
                        MASS_MSG("Opening mass storage device"
                                " for the first time failed");
+
+                       /* Do one more test before failing, to output
+                        * sense errors in case they weren't dumped already */
+                       if (mass_storage_test())
+                               MASS_MSG("Final TEST UNIT READY failed");
+
                        return r;
                }
 
@@ -1356,6 +1469,8 @@ mass_storage_part(devminor_t minor)
 /*===========================================================================*
  *    mass_storage_geometry                                                  *
  *===========================================================================*/
+/* This command is optional for most mass storage devices
+ * It should rather be used with USB floppy disk reader */
 #ifdef MASS_USE_GEOMETRY
 static void
 mass_storage_geometry(devminor_t minor, struct part_geom * part)
index c5c7e172648dbe8c44ae4822c0311b4dcf3e5069..b0df30ec31e07c537a977a7a8b29541b506ddb98 100644 (file)
@@ -1,7 +1,7 @@
 # Makefile for the EARM USBD
 
 PROG=          usbd
-SRCS=          usbd.c usbd_earm.c hcd.c hcd_common.c hcd_ddekit.c musb_am335x.c musb_core.c
+SRCS=          usbd.c usbd_earm.c hcd.c hcd_common.c hcd_ddekit.c hcd_schedule.c musb_am335x.c musb_core.c
 MAN=
 BINDIR=                /service
 
index 79d8df3fe4397034338325bd49f00f67398617e5..24a08b8649dc089def97988640daff072f4aab43 100644 (file)
@@ -5,9 +5,9 @@
 #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>
 
 
 /*===========================================================================*
index f45929cba6ae7b90cacbef922d8dd4c1ac941d15..809bdce2a4da51f18c2d2accb1d93905f5439433 100644 (file)
@@ -10,8 +10,9 @@
 #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>
 
 
 /*===========================================================================*
@@ -37,7 +38,7 @@ main(int UNUSED(argc), char * UNUSED(argv[]))
        int ret_val;
 
        USB_MSG("Starting USBD");
-       USB_DBG("Built: %s %s", __DATE__, __TIME__);
+       USB_MSG("Built: %s %s", __DATE__, __TIME__);
 
        /* Basic SEF,DDE,... initialization */
        usbd_init();
@@ -74,7 +75,7 @@ main(int UNUSED(argc), char * UNUSED(argv[]))
 static int
 usbd_sef_handler(int type, sef_init_info_t * UNUSED(info))
 {
-       DEBUG_DUMP;
+       /* No DEBUG_DUMP, threading unavailable yet */
 
        switch (type) {
                case SEF_INIT_FRESH:
@@ -125,12 +126,21 @@ usbd_start(void)
        DEBUG_DUMP;
 
        /* Driver's "main loop" is within DDEKit server thread */
-       usbd_th = ddekit_thread_create(usbd_server_thread, NULL, "USBD");
+       usbd_th = ddekit_thread_create(usbd_server_thread, NULL, "ddekit_usb");
 
        /* After spawning, allow server thread to work */
        if (NULL != usbd_th) {
-               /* This will lock current thread until DDEKit exits */
-               ddekit_minix_wait_exit();
+
+               /* Allow URB scheduling */
+               if (usbd_init_scheduler()) {
+                       USB_MSG("Failed to start URB scheduler");
+               } else {
+                       /* This will lock current thread until DDEKit exits */
+                       ddekit_minix_wait_exit();
+               }
+
+               /* Disallow URB scheduling */
+               usbd_deinit_scheduler();
 
                /* Cleanup */
                ddekit_thread_terminate(usbd_th);
@@ -147,16 +157,18 @@ usbd_start(void)
 static void
 usbd_init(void)
 {
-       DEBUG_DUMP;
+       /* No DEBUG_DUMP, threading unavailable yet */
 
        /* Set one handler for all messages */
        sef_setcb_init_fresh(usbd_sef_handler);
        sef_setcb_init_lu(usbd_sef_handler);
        sef_setcb_init_restart(usbd_sef_handler);
-       sef_setcb_signal_handler(usbd_signal_handler);
 
        /* Initialize DDEkit (involves sef_startup()) */
        ddekit_init();
+
+       /* After threading initialization, add signal handler */
+       sef_setcb_signal_handler(usbd_signal_handler);
 }
 
 
index 56687c5b759cf9b3c518821de0cc42c698fbe060..018ceea1ff22144bcaeabcd81a9fa190d70e2d1f 100644 (file)
@@ -6,10 +6,11 @@
 
 #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>
 
 
 /*===========================================================================*
@@ -21,103 +22,223 @@ static void hcd_device_thread(void *);
 /* Procedure that locks device thread forever in case of error/completion */
 static void hcd_device_finish(hcd_device_state *, const char *);
 
+/* Procedure that finds device, waiting for given EP interrupt */
+static hcd_device_state * hcd_get_child_for_ep(hcd_device_state *, hcd_reg1);
+
+/* For HCD level, hub handling */
+static void hcd_add_child(hcd_device_state *, hcd_reg1, hcd_speed);
+static void hcd_delete_child(hcd_device_state *, hcd_reg1);
+static void hcd_disconnect_tree(hcd_device_state *);
+static void hcd_dump_tree(hcd_device_state *, hcd_reg1);
+
 /* Typical USD device communication procedures */
 static int hcd_enumerate(hcd_device_state *);
 static int hcd_get_device_descriptor(hcd_device_state *);
-static int hcd_set_address(hcd_device_state *, hcd_reg1);
+static int hcd_set_address(hcd_device_state *);
 static int hcd_get_descriptor_tree(hcd_device_state *);
 static int hcd_set_configuration(hcd_device_state *, hcd_reg1);
-static int hcd_handle_urb(hcd_device_state *, hcd_urb *);
+static void hcd_handle_urb(hcd_device_state *);
+static void hcd_complete_urb(hcd_device_state *);
 static int hcd_control_urb(hcd_device_state *, hcd_urb *);
 static int hcd_non_control_urb(hcd_device_state *, hcd_urb *);
 
 /* For internal use by more general methods */
 static int hcd_setup_packet(hcd_device_state *, hcd_ctrlrequest *, hcd_reg1);
-static int hcd_finish_setup(hcd_device_state *, void *, hcd_reg4);
+static int hcd_finish_setup(hcd_device_state *, void *);
 static int hcd_data_transfer(hcd_device_state *, hcd_datarequest *);
 
+/* TODO: This is not meant to be explicitly visible outside DDEKit library
+ * but there is no other way to set thread priority for now */
+extern void _ddekit_thread_set_myprio(int);
+
 
 /*===========================================================================*
  *    Local definitions                                                      *
  *===========================================================================*/
-/* TODO: Only one device at a time
- * If ever HUB functionality is added, one must remember that disconnecting
- * HUB, means disconnecting every device attached to it, so data structure may
- * have to be altered to allow that */
-static hcd_device_state hcd_device[1];
-
 /* TODO: This was added for compatibility with DDELinux drivers that
  * allow receiving less data than expected in URB, without error */
 #define HCD_ANY_LENGTH 0xFFFFFFFFu
 
+/* This doesn't seem to be specified in standard but abnormal values
+ * are unlikely so check for this was added below */
+#define HCD_SANE_DESCRIPTOR_LENGTH 2048
+
 
 /*===========================================================================*
  *    hcd_handle_event                                                       *
  *===========================================================================*/
 void
-hcd_handle_event(hcd_driver_state * driver)
+hcd_handle_event(hcd_device_state * device, hcd_event event, hcd_reg1 val)
 {
-       hcd_device_state * this_device;
-
        DEBUG_DUMP;
 
-       /* TODO: Finding which hcd_device is in use should be performed here */
-       this_device = &(hcd_device[0]);
+       /* Invalid device may be supplied */
+       if (EXIT_SUCCESS != hcd_check_device(device)) {
+               USB_MSG("No device available for event: 0x%02X, value: 0x%02X",
+                       event, val);
+               return;
+       }
+
+#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");
        }
 }
 
@@ -132,12 +253,13 @@ hcd_device_thread(void * thread_args)
 
        DEBUG_DUMP;
 
+       /* Set device thread priority higher so it
+        * won't change context unless explicitly locked */
+       _ddekit_thread_set_myprio(2);
+
        /* Retrieve structures from generic data */
        this_device = (hcd_device_state *)thread_args;
 
-       /* Plugged in */
-       this_device->state = HCD_STATE_CONNECTION_PENDING;
-
        /* Enumeration sequence */
        if (EXIT_SUCCESS != hcd_enumerate(this_device))
                hcd_device_finish(this_device, "USB device enumeration failed");
@@ -153,11 +275,8 @@ hcd_device_thread(void * thread_args)
        /* Start handling URB's */
        for(;;) {
                /* Block and wait for something like 'submit URB' */
-               hcd_device_wait(this_device, HCD_EVENT_URB, HCD_ANY_EP);
-
-               if (EXIT_SUCCESS != hcd_handle_urb(this_device,
-                                               &(this_device->urb)))
-                       hcd_device_finish(this_device, "URB handling failed");
+               hcd_device_wait(this_device, HCD_EVENT_URB, HCD_UNUSED_VAL);
+               hcd_handle_urb(this_device);
        }
 
        /* Finish device handling to avoid leaving thread */
@@ -177,12 +296,176 @@ hcd_device_finish(hcd_device_state * this_device, const char * finish_msg)
 
        /* Lock forever */
        for (;;) {
-               hcd_device_wait(this_device, HCD_EVENT_URB, HCD_ANY_EP);
+               hcd_device_wait(this_device, HCD_EVENT_URB, HCD_UNUSED_VAL);
                USB_MSG("Failed attempt to continue finished thread");
        }
 }
 
 
+/*===========================================================================*
+ *    hcd_get_child_for_ep                                                   *
+ *===========================================================================*/
+static hcd_device_state *
+hcd_get_child_for_ep(hcd_device_state * device, hcd_reg1 ep)
+{
+       hcd_device_state * child_found;
+       hcd_device_state * final_found;
+       hcd_device_state * child;
+       hcd_reg1 child_num;
+
+       DEBUG_DUMP;
+
+       /* Nothing yet */
+       final_found = NULL;
+
+       /* Check if any children (and their children) wait for EP event */
+       /* Every device in tree is checked every time so errors can be found */
+       for (child_num = 0; child_num < HCD_CHILDREN; child_num++) {
+               /* Device, to be checked for EP event recursively... */
+               child = device->child[child_num];
+
+               /* ...but only if attached */
+               if (NULL != child) {
+                       /* Look deeper first */
+                       child_found = hcd_get_child_for_ep(child, ep);
+
+                       if (NULL != child_found) {
+                               /* Only one device can wait for EP event */
+                               USB_ASSERT((NULL == final_found),
+                                       "More than one device waits for EP");
+                               /* Remember what was found */
+                               final_found = child_found;
+                       }
+               }
+       }
+
+       /* Check this device last */
+       if ((HCD_EVENT_ENDPOINT == device->wait_event) &&
+           (ep == device->wait_ep)) {
+               /* Only one device can wait for EP event */
+               USB_ASSERT((NULL == final_found),
+                       "More than one device waits for EP");
+               /* Remember what was found */
+               final_found = device;
+       }
+
+       return final_found;
+}
+
+
+/*===========================================================================*
+ *    hcd_add_child                                                          *
+ *===========================================================================*/
+static void
+hcd_add_child(hcd_device_state * parent, hcd_reg1 port, hcd_speed speed)
+{
+       DEBUG_DUMP;
+
+       USB_ASSERT(port < HCD_CHILDREN, "Port number too high");
+       USB_ASSERT(NULL == parent->child[port], "Child device already exists");
+
+       /* Basic addition */
+       parent->child[port] = hcd_new_device();
+       parent->child[port]->parent = parent;
+
+       /* Inherit parent's driver */
+       parent->child[port]->driver = parent->driver;
+
+       /* Remember speed, determined by hub driver */
+       parent->child[port]->speed = speed;
+
+       /* Try creating new thread for device */
+       if (hcd_connect_device(parent->child[port], hcd_device_thread))
+               USB_MSG("Device creation failed, nothing more "
+                       "will happen until disconnected");
+}
+
+
+/*===========================================================================*
+ *    hcd_delete_child                                                       *
+ *===========================================================================*/
+static void
+hcd_delete_child(hcd_device_state * parent, hcd_reg1 port)
+{
+       hcd_device_state * child;
+
+       DEBUG_DUMP;
+
+       USB_ASSERT(port < HCD_CHILDREN, "Port number too high");
+
+       child = parent->child[port]; /* Child to be detached */
+
+       USB_ASSERT(NULL != child, "Child device does not exist");
+
+       /* Make this child device and all its attached children
+        * disconnect recursively */
+       hcd_disconnect_tree(child);
+
+       /* Delete to release device itself */
+       hcd_delete_device(child);
+
+       /* Mark as released */
+       parent->child[port] = NULL;
+}
+
+
+/*===========================================================================*
+ *    hcd_disconnect_tree                                                    *
+ *===========================================================================*/
+static void
+hcd_disconnect_tree(hcd_device_state * device)
+{
+       hcd_reg1 child_num;
+
+       DEBUG_DUMP;
+
+       /* Generate disconnect event for all children */
+       for (child_num = 0; child_num < HCD_CHILDREN; child_num++) {
+               if (NULL != device->child[child_num])
+                       hcd_handle_event(device, HCD_EVENT_PORT_DISCONNECTED,
+                                       child_num);
+       }
+
+       /* If this device was detached during URB handling, some steps must be
+        * taken to ensure that no process/thread is waiting for completion */
+       if (NULL != device->urb) {
+               USB_MSG("Unplugged device had unhandled URB");
+               /* Tell device driver that device was detached */
+               /* TODO: ENODEV selected for that */
+               device->urb->inout_status = ENODEV;
+               hcd_complete_urb(device);
+       }
+
+       /* If connect callback was used before, call
+        * it's equivalent to signal disconnection */
+       if (HCD_STATE_CONNECTED == device->state)
+               hcd_disconnect_cb(device);
+
+       /* Handle device disconnection (freeing memory etc.) */
+       hcd_disconnect_device(device);
+}
+
+
+/*===========================================================================*
+ *    hcd_dump_tree                                                          *
+ *===========================================================================*/
+static void
+hcd_dump_tree(hcd_device_state * device, hcd_reg1 level)
+{
+       hcd_reg1 child_num;
+
+       /* DEBUG_DUMP; */ /* Let's keep tree output cleaner */
+
+       USB_MSG("Device on level %03u: 0x%08X", level, device);
+
+       /* Traverse device tree recursively */
+       for (child_num = 0; child_num < HCD_CHILDREN; child_num++) {
+               if (NULL != device->child[child_num])
+                       hcd_dump_tree(device->child[child_num], level + 1);
+       }
+}
+
+
 /*===========================================================================*
  *    hcd_enumerate                                                          *
  *===========================================================================*/
@@ -195,18 +478,22 @@ hcd_enumerate(hcd_device_state * this_device)
 
        d = this_device->driver;
 
-       /* First let driver reset device */
-       if (EXIT_SUCCESS != d->reset_device(d->private_data,
-                                       &(this_device->speed))) {
-               USB_MSG("Failed to reset device");
-               return EXIT_FAILURE;
+       /* Having a parent device also means being reseted by it
+        * so only reset devices that have no parents */
+       if (NULL == this_device->parent) {
+               /* First let driver reset device */
+               if (EXIT_SUCCESS != d->reset_device(d->private_data,
+                                               &(this_device->speed))) {
+                       USB_MSG("Failed to reset device");
+                       return EXIT_FAILURE;
+               }
        }
 
        /* Default MaxPacketSize, based on speed */
-       if (HCD_SPEED_LOW == this_device->speed)
-               this_device->max_packet_size = HCD_LS_MAXPACKETSIZE;
-       else
+       if (HCD_SPEED_HIGH == this_device->speed)
                this_device->max_packet_size = HCD_HS_MAXPACKETSIZE;
+       else
+               this_device->max_packet_size = HCD_LS_MAXPACKETSIZE;
 
        /* Get device descriptor */
        if (EXIT_SUCCESS != hcd_get_device_descriptor(this_device)) {
@@ -214,15 +501,45 @@ hcd_enumerate(hcd_device_state * this_device)
                return EXIT_FAILURE;
        }
 
-       /* TODO: Dynamic device addressing should be added here, when more
-        * than one device can be handled at a time */
+       /* Remember max packet size from device descriptor */
+       this_device->max_packet_size = this_device->device_desc.bMaxPacketSize;
+
+       /* 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");
@@ -252,11 +569,11 @@ static int
 hcd_get_device_descriptor(hcd_device_state * this_device)
 {
        hcd_ctrlrequest setup;
+       hcd_urb urb;
 
        DEBUG_DUMP;
 
        /* TODO: magic numbers, no header for these */
-
        /* Format setup packet */
        setup.bRequestType      = 0x80;                 /* IN */
        setup.bRequest          = 0x06;                 /* Get descriptor */
@@ -264,45 +581,31 @@ hcd_get_device_descriptor(hcd_device_state * this_device)
        setup.wIndex            = 0x0000;
        setup.wLength           = sizeof(this_device->device_desc);
 
-       /* Handle formatted setup packet */
-       if (EXIT_SUCCESS != hcd_setup_packet(this_device, &setup,
-                                               HCD_DEFAULT_EP)) {
-               USB_MSG("Handling setup packet failed");
+       /* Prepare self-URB */
+       memset(&urb, 0, sizeof(urb));
+       urb.direction = HCD_DIRECTION_IN;
+       urb.endpoint = HCD_DEFAULT_EP;
+       urb.in_setup = &setup;
+       urb.inout_data = (hcd_reg1 *)(&(this_device->device_desc));
+       urb.target_device = this_device;
+       urb.type = HCD_TRANSFER_CONTROL;
+
+       /* Put it to be scheduled and wait for control to get back */
+       hcd_schedule_internal_urb(&urb);
+       hcd_device_wait(this_device, HCD_EVENT_URB, HCD_UNUSED_VAL);
+       hcd_handle_urb(this_device);
+
+       /* Check if URB submission completed successfully */
+       if (urb.inout_status) {
+               USB_MSG("URB submission failed");
                return EXIT_FAILURE;
        }
 
-       /* Put what was read in device descriptor */
-       if (EXIT_SUCCESS != hcd_finish_setup(this_device,
-                                       &(this_device->device_desc),
-                                       sizeof(this_device->device_desc)))
+       /* Check if expected size was received */
+       if (urb.out_size != setup.wLength) {
+               USB_MSG("URB submission returned invalid amount of data");
                return EXIT_FAILURE;
-
-       /* Remember max packet size from device descriptor */
-       this_device->max_packet_size = this_device->device_desc.bMaxPacketSize;
-
-       /* Dump device descriptor in debug mode */
-#ifdef DEBUG
-       {
-               hcd_device_descriptor * d;
-               d = &(this_device->device_desc);
-
-               USB_DBG("<<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;
 }
@@ -312,35 +615,50 @@ hcd_get_device_descriptor(hcd_device_state * this_device)
  *    hcd_set_address                                                        *
  *===========================================================================*/
 static int
-hcd_set_address(hcd_device_state * this_device, hcd_reg1 address)
+hcd_set_address(hcd_device_state * this_device)
 {
        hcd_ctrlrequest setup;
+       hcd_urb urb;
 
        DEBUG_DUMP;
 
        /* Check for legal USB device address (must be non-zero as well) */
-       USB_ASSERT((address > HCD_DEFAULT_ADDR) && (address <= HCD_LAST_ADDR),
+       USB_ASSERT((this_device->reserved_address > HCD_DEFAULT_ADDR) &&
+               (this_device->reserved_address <= HCD_LAST_ADDR),
                "Illegal device address supplied");
 
        /* TODO: magic numbers, no header for these */
        setup.bRequestType      = 0x00;                 /* OUT */
        setup.bRequest          = 0x05;                 /* Set address */
-       setup.wValue            = address;
+       setup.wValue            = this_device->reserved_address;
        setup.wIndex            = 0x0000;
        setup.wLength           = 0x0000;
 
-       /* Handle formatted setup packet */
-       if (EXIT_SUCCESS != hcd_setup_packet(this_device, &setup,
-                                               HCD_DEFAULT_EP)) {
-               USB_MSG("Handling setup packet failed");
+       /* Prepare self-URB */
+       memset(&urb, 0, sizeof(urb));
+       urb.direction = HCD_DIRECTION_OUT;
+       urb.endpoint = HCD_DEFAULT_EP;
+       urb.in_setup = &setup;
+       urb.inout_data = NULL;
+       urb.target_device = this_device;
+       urb.type = HCD_TRANSFER_CONTROL;
+
+       /* Put it to be scheduled and wait for control to get back */
+       hcd_schedule_internal_urb(&urb);
+       hcd_device_wait(this_device, HCD_EVENT_URB, HCD_UNUSED_VAL);
+       hcd_handle_urb(this_device);
+
+       /* Check if URB submission completed successfully */
+       if (urb.inout_status) {
+               USB_MSG("URB submission failed");
                return EXIT_FAILURE;
        }
 
-       /* Sleep 5msec to allow addressing */
-       hcd_os_nanosleep(HCD_NANOSLEEP_MSEC(5));
-
-       /* Remember what was assigned in hardware */
-       this_device->address = address;
+       /* Check if expected size was received */
+       if (urb.out_size != setup.wLength) {
+               USB_MSG("URB submission returned invalid amount of data");
+               return EXIT_FAILURE;
+       }
 
        return EXIT_SUCCESS;
 }
@@ -352,19 +670,24 @@ hcd_set_address(hcd_device_state * this_device, hcd_reg1 address)
 static int
 hcd_get_descriptor_tree(hcd_device_state * this_device)
 {
-       hcd_config_descriptor config_descriptor;
+       hcd_config_descriptor temp_config_descriptor;
        hcd_ctrlrequest setup;
-       hcd_reg4 total_length;
-       hcd_reg4 buffer_length;
-       int completed;
+       hcd_urb urb;
+
+       /* To receive data */
+       hcd_reg4 expected_length;
+       hcd_reg1 * expected_buffer;
+
+       int retval;
 
        DEBUG_DUMP;
 
-       /* First, ask only for configuration itself to get length info */
-       buffer_length = sizeof(config_descriptor);
-       completed = 0;
+       /* Initially */
+       retval = EXIT_FAILURE;
+       expected_buffer = NULL;
 
-       do {
+       /* First part gets only configuration to find out total length */
+       {
                /* TODO: Default configuration is hard-coded
                 * but others are rarely used anyway */
                /* TODO: magic numbers, no header for these */
@@ -372,62 +695,107 @@ hcd_get_descriptor_tree(hcd_device_state * this_device)
                setup.bRequest          = 0x06;         /* Get descriptor */
                setup.wValue            = 0x0200 | HCD_DEFAULT_CONFIG;
                setup.wIndex            = 0x0000;
-               setup.wLength           = buffer_length;
+               setup.wLength           = sizeof(temp_config_descriptor);
+
+               /* 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;
 }
 
 
@@ -438,6 +806,7 @@ static int
 hcd_set_configuration(hcd_device_state * this_device, hcd_reg1 configuration)
 {
        hcd_ctrlrequest setup;
+       hcd_urb urb;
 
        DEBUG_DUMP;
 
@@ -448,29 +817,39 @@ hcd_set_configuration(hcd_device_state * this_device, hcd_reg1 configuration)
        setup.wIndex            = 0x0000;
        setup.wLength           = 0x0000;
 
-       /* Handle formatted setup packet */
-       if (EXIT_SUCCESS != hcd_setup_packet(this_device, &setup,
-                                               HCD_DEFAULT_EP)) {
-               USB_MSG("Handling setup packet failed");
-               return EXIT_FAILURE;
-       }
-
-       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... */
@@ -491,13 +870,8 @@ hcd_handle_urb(hcd_device_state * this_device, hcd_urb * urb)
                                                        this_device, urb);
                                break;
 
-                       case HCD_TRANSFER_ISOCHRONOUS:
-                               /* TODO: ISO transfer */
-                               USB_MSG("ISO transfer not supported");
-                               break;
-
                        default:
-                               USB_MSG("Invalid transfer type 0x%02X",
+                               USB_MSG("Unsupported transfer type 0x%02X",
                                                        (int)urb->type);
                                break;
                }
@@ -509,13 +883,27 @@ hcd_handle_urb(hcd_device_state * this_device, hcd_urb * urb)
        } else
                USB_MSG("Invalid URB supplied");
 
-       /* Call completion regardless of status */
-       hcd_completion_cb(urb);
+       /* Perform completion routine */
+       hcd_complete_urb(this_device);
+}
 
-       /* TODO: Only critical failures should ever yield EXIT_FAILURE, so
-        * return is not bound to transfer_status for now, to let device
-        * driver act accordingly */
-       return EXIT_SUCCESS;
+
+/*===========================================================================*
+ *    hcd_complete_urb                                                       *
+ *===========================================================================*/
+static void
+hcd_complete_urb(hcd_device_state * this_device)
+{
+       DEBUG_DUMP;
+
+       /* Signal scheduler that URB was handled */
+       this_device->urb->handled(this_device->urb);
+
+       /* Use this callback in case it is an external URB */
+       hcd_completion_cb(this_device->urb);
+
+       /* Make device forget about this URB */
+       this_device->urb = NULL;
 }
 
 
@@ -557,9 +945,7 @@ hcd_control_urb(hcd_device_state * this_device, hcd_urb * urb)
        }
 
        /* Put what was read back into URB */
-       if (EXIT_SUCCESS != hcd_finish_setup(this_device,
-                                       urb->inout_data,
-                                       HCD_ANY_LENGTH))
+       if (EXIT_SUCCESS != hcd_finish_setup(this_device, urb->inout_data))
                return EXIT_FAILURE;
 
        /* Write transfer output info to URB */
@@ -627,7 +1013,14 @@ hcd_non_control_urb(hcd_device_state * this_device, hcd_urb * urb)
        request.direction = urb->direction;
        request.data_left = (int)urb->in_size;
        request.data = urb->inout_data;
+       /* TODO: This was changed to allow software scheduler to work correctly
+        * by switching URBs when they NAK, rather than waiting forever if URB
+        * which requires such waiting, was issued */
+#if 0
        request.interval = urb->interval;
+#else
+       request.interval = HCD_DEFAULT_NAKLIMIT;
+#endif
 
        /* Assign to let know how much data can be transfered at a time */
        request.max_packet_size = UGETW(e->descriptor.wMaxPacketSize);
@@ -669,7 +1062,7 @@ hcd_setup_packet(hcd_device_state * this_device, hcd_ctrlrequest * setup,
        USB_ASSERT(this_device->max_packet_size >= HCD_LS_MAXPACKETSIZE,
                "Illegal MaxPacketSize");
        USB_ASSERT(ep <= HCD_LAST_EP, "Invalid EP number");
-       USB_ASSERT(this_device->address <= HCD_LAST_ADDR,
+       USB_ASSERT(this_device->current_address <= HCD_LAST_ADDR,
                "Invalid device address");
 
        /* Initially... */
@@ -678,7 +1071,8 @@ hcd_setup_packet(hcd_device_state * this_device, hcd_ctrlrequest * setup,
        this_device->control_len = 0;           /* Nothing read yet */
 
        /* Set parameters for further communication */
-       d->setup_device(d->private_data, ep, this_device->address);
+       d->setup_device(d->private_data, ep, this_device->current_address,
+                       NULL, NULL);
 
        /* Send setup packet */
        d->setup_stage(d->private_data, setup);
@@ -725,8 +1119,8 @@ hcd_setup_packet(hcd_device_state * this_device, hcd_ctrlrequest * setup,
                                current_byte += rx_len;
                                this_device->control_len += rx_len;
 
-                               /* If full max sized packet was read... */
-                               if (rx_len == (int)this_device->max_packet_size)
+                               /* If max sized packet was read (or more)... */
+                               if (rx_len >= (int)this_device->max_packet_size)
                                        /* ...try reading next packet even if
                                         * zero bytes may be received */
                                        continue;
@@ -796,8 +1190,7 @@ hcd_setup_packet(hcd_device_state * this_device, hcd_ctrlrequest * setup,
  *    hcd_finish_setup                                                       *
  *===========================================================================*/
 static int
-hcd_finish_setup(hcd_device_state * this_device, void * output,
-               hcd_reg4 expected)
+hcd_finish_setup(hcd_device_state * this_device, void * output)
 {
        DEBUG_DUMP;
 
@@ -807,19 +1200,6 @@ hcd_finish_setup(hcd_device_state * this_device, void * output,
                return EXIT_FAILURE;
        }
 
-       /* In case it is required... */
-       if (HCD_ANY_LENGTH != expected) {
-               /* ...check for expected length */
-               if ((hcd_reg4)this_device->control_len != expected) {
-                       USB_MSG("Control transfer output length mismatch");
-                       return EXIT_FAILURE;
-               }
-
-               /* Valid but there is no need to copy anything */
-               if (0u == expected)
-                       return EXIT_SUCCESS;
-       }
-
        /* Length is valid but output not supplied */
        if (NULL == output)
                return EXIT_SUCCESS;
@@ -846,8 +1226,8 @@ hcd_data_transfer(hcd_device_state * this_device, hcd_datarequest * request)
        USB_ASSERT((request->endpoint <= HCD_LAST_EP) &&
                (request->endpoint > HCD_DEFAULT_EP),
                "Invalid EP number");
-       USB_ASSERT((this_device->address <= HCD_LAST_ADDR) &&
-               (this_device->address > HCD_DEFAULT_ADDR),
+       USB_ASSERT((this_device->current_address <= HCD_LAST_ADDR) &&
+               (this_device->current_address > HCD_DEFAULT_ADDR),
                "Invalid device address");
 
        /* Initially... */
@@ -855,7 +1235,9 @@ hcd_data_transfer(hcd_device_state * this_device, hcd_datarequest * request)
 
        /* Set parameters for further communication */
        d->setup_device(d->private_data, request->endpoint,
-                       this_device->address);
+                       this_device->current_address,
+                       &(this_device->ep_tx_tog[request->endpoint]),
+                       &(this_device->ep_rx_tog[request->endpoint]));
 
        /* Check transfer direction first */
        if (HCD_DIRECTION_IN == request->direction) {
index 56249661298c7132f1ef72c7f4601b8ea361ff68..d6b6f798cd912da72633d21dbe2f11f57236e258 100644 (file)
 #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                                                *
@@ -188,49 +200,84 @@ hcd_os_nanosleep(int nanosec)
 
 
 /*===========================================================================*
- *    hcd_init_device                                                        *
+ *    hcd_connect_device                                                     *
  *===========================================================================*/
 int
 hcd_connect_device(hcd_device_state * this_device, hcd_thread_function funct)
 {
        DEBUG_DUMP;
 
-       if ((NULL != this_device->lock) || (NULL != this_device->thread)) {
-               USB_MSG("Device data already allocated");
+       /* This is meant to allow thread name distinction
+        * and should not be used for anything else */
+       static unsigned int devnum = 0;
+
+       /* Should be able to hold device prefix and some number */
+       char devname[] = "dev..........";
+
+       USB_ASSERT((NULL == this_device->lock) &&
+               (NULL == this_device->thread) &&
+               (HCD_DEFAULT_ADDR == this_device->reserved_address) &&
+               (HCD_STATE_DISCONNECTED == this_device->state),
+               "Device structure not clean");
+
+       /* Mark as 'plugged in' to avoid treating device
+        * as 'disconnected' in case of errors below */
+       this_device->state = HCD_STATE_CONNECTION_PENDING;
+
+       /* Reserve device address for further use if available */
+       if (HCD_DEFAULT_ADDR == (this_device->reserved_address =
+                               hcd_reserve_addr(this_device->driver))) {
+               USB_MSG("No free device addresses");
                return EXIT_FAILURE;
        }
 
-       if (NULL == (this_device->lock = ddekit_sem_init(0)))
+       /* Get 'lock' that makes device thread wait for events to occur */
+       if (NULL == (this_device->lock = ddekit_sem_init(0))) {
+               USB_MSG("Failed to initialize thread lock");
                return EXIT_FAILURE;
+       }
+
+       /* Prepare name */
+       snprintf(devname, sizeof(devname), "dev%u", devnum++);
 
+       /* Get thread itself */
        if (NULL == (this_device->thread = ddekit_thread_create(funct,
-                                                               this_device,
-                                                               "Device"))) {
-               ddekit_sem_deinit(this_device->lock);
+                                               this_device,
+                                               (const char *)devname))) {
+               USB_MSG("Failed to initialize USB device thread");
                return EXIT_FAILURE;
        }
 
-       /* Allow device thread to work */
-       ddekit_yield();
-
        return EXIT_SUCCESS;
 }
 
+
 /*===========================================================================*
- *    hcd_deinit_device                                                      *
+ *    hcd_disconnect_device                                                  *
  *===========================================================================*/
 void
 hcd_disconnect_device(hcd_device_state * this_device)
 {
        DEBUG_DUMP;
 
+       /* TODO: This should disconnect all the children if they exist */
+
+       /* Clean configuration tree in case it was acquired */
        hcd_tree_cleanup(&(this_device->config_tree));
 
-       ddekit_thread_terminate(this_device->thread);
-       ddekit_sem_deinit(this_device->lock);
+       /* Release allocated resources */
+       if (NULL != this_device->thread)
+               ddekit_thread_terminate(this_device->thread);
+       if (NULL != this_device->lock)
+               ddekit_sem_deinit(this_device->lock);
+
+       /* 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;
 }
 
 
@@ -238,46 +285,145 @@ hcd_disconnect_device(hcd_device_state * this_device)
  *    hcd_device_wait                                                        *
  *===========================================================================*/
 void
-hcd_device_wait(hcd_device_state * this_device, hcd_event event, hcd_reg1 ep)
+hcd_device_wait(hcd_device_state * device, hcd_event event, hcd_reg1 ep)
 {
-       hcd_driver_state * drv;
+       DEBUG_DUMP;
 
+       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;
 }
 
 
@@ -571,3 +717,44 @@ hcd_fill_endpoint(hcd_reg1 * buf, int len, hcd_endpoint * e)
 
        return EXIT_SUCCESS;
 }
+
+
+/*===========================================================================*
+ *    hcd_reserve_addr                                                       *
+ *===========================================================================*/
+static hcd_reg1
+hcd_reserve_addr(hcd_driver_state * driver)
+{
+       hcd_reg1 addr;
+
+       DEBUG_DUMP;
+
+       for (addr = HCD_FIRST_ADDR; addr <= HCD_LAST_ADDR; addr++) {
+               if (HCD_ADDR_AVAILABLE == driver->dev_addr[addr]) {
+                       USB_DBG("Reserved address: %u", addr);
+                       driver->dev_addr[addr] = HCD_ADDR_USED;
+                       return addr;
+               }
+       }
+
+       /* This means error */
+       return HCD_DEFAULT_ADDR;
+}
+
+
+/*===========================================================================*
+ *    hcd_release_addr                                                       *
+ *===========================================================================*/
+static void
+hcd_release_addr(hcd_driver_state * driver, hcd_reg1 addr)
+{
+       DEBUG_DUMP;
+
+       USB_ASSERT((addr > HCD_DEFAULT_ADDR) && (addr <= HCD_LAST_ADDR),
+               "Invalid device address to be released");
+       USB_ASSERT(HCD_ADDR_USED == driver->dev_addr[addr],
+               "Attempted to release unused address");
+
+       USB_DBG("Released address: %u", addr);
+       driver->dev_addr[addr] = HCD_ADDR_AVAILABLE;
+}
index 0606c20df01cd3d6c5fed21feaaf04ee0eaecfcc..c9862c8651a59f7fcec43f8f110a08c5119d9cee 100644 (file)
@@ -6,9 +6,10 @@
 
 #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>
 
 
 /*===========================================================================*
@@ -16,7 +17,7 @@
  *===========================================================================*/
 /*
  * In this file "struct ddekit_usb_dev" equals "hcd_device_state"
- * */
+ */
 struct ddekit_usb_device_id;
 struct ddekit_usb_urb;
 struct ddekit_usb_dev;
@@ -25,6 +26,13 @@ struct ddekit_usb_dev;
 static void hcd_decode_urb(hcd_urb *, struct ddekit_usb_urb *);
 static void hcd_encode_urb(hcd_urb *, struct ddekit_usb_urb *);
 
+/* HCD's URB create/destroy */
+static hcd_urb * hcd_new_urb(void);
+static void hcd_free_urb(hcd_urb *);
+
+/* Decodes event from received info */
+static void hcd_decode_info(long, long, hcd_event *, hcd_reg1 *);
+
 
 /*===========================================================================*
  *    Global definitions                                                     *
@@ -176,36 +184,19 @@ ddekit_usb_get_device_id(struct ddekit_usb_dev * dev,
 int
 ddekit_usb_submit_urb(struct ddekit_usb_urb * d_urb)
 {
-       hcd_device_state * dev;
-       hcd_driver_state * drv;
+       hcd_urb * urb;
 
        DEBUG_DUMP;
 
-       /* Retrieve info on device/driver state from DDEKit's USB */
-       dev = (hcd_device_state *)(d_urb->dev);
-       drv = (hcd_driver_state *)(dev->driver);
+       /* Get new URB */
+       urb = hcd_new_urb();
 
-       /* Check for latest URB completion */
-       if (NULL == dev->urb.original_urb) {
+       /* Turn DDEKit URB format to one that is easier to
+        * handle by HCD, also check if URB is valid */
+       hcd_decode_urb(urb, d_urb);
 
-               /* Remember original URB */
-               dev->urb.original_urb = (void *)d_urb;
-
-               /* TODO: If multiple URB's have to be queued, this code
-                * or DDEKit's must be altered accordingly */
-               /* Turn DDEKit URB format to one that is easier to
-                * handle by HCD, also check if URB is valid */
-               hcd_decode_urb(&(dev->urb), d_urb);
-
-               /* Start handling URB event */
-               drv->current_event = HCD_EVENT_URB;
-               hcd_handle_event(drv);
-
-               return EXIT_SUCCESS;
-       }
-
-       /* Last URB must not have been completed */
-       return EXIT_FAILURE;
+       /* Add URB to scheduler */
+       return hcd_schedule_external_urb(urb);
 }
 
 
@@ -218,10 +209,37 @@ ddekit_usb_cancle_urb(struct ddekit_usb_urb * d_urb)
        DEBUG_DUMP;
        /* TODO: UNUSED for argument won't work */
        ((void)d_urb);
+       /* TODO: No driver will require this any time soon */
+       USB_ASSERT(0, "URB cancellation not supported");
        return EXIT_SUCCESS;
 }
 
 
+/*===========================================================================*
+ *    ddekit_usb_info                                                        *
+ *===========================================================================*/
+long
+ddekit_usb_info(struct ddekit_usb_dev * dev, long type, long value)
+{
+       hcd_event event;
+       hcd_reg1 val;
+
+       DEBUG_DUMP;
+
+       /* Decode event */
+       hcd_decode_info(type, value, &event, &val);
+
+       if (HCD_EVENT_INVALID == event) {
+               USB_MSG("Invalid info message received");
+               return EXIT_FAILURE;
+       } else {
+               /* Let HCD handle info message */
+               hcd_handle_event((hcd_device_state *)dev, event, val);
+               return EXIT_SUCCESS;
+       }
+}
+
+
 /*===========================================================================*
  *    ddekit_usb_init                                                        *
  *===========================================================================*/
@@ -290,13 +308,17 @@ hcd_completion_cb(hcd_urb * urb)
        /* Recollect original URB */
        d_urb = (struct ddekit_usb_urb *)urb->original_urb;
 
-       /* Turn HCD URB format to one handled by DDEKit */
-       hcd_encode_urb(urb, d_urb);
+       /* Original URB will not be NULL if URB
+        * was external (from device driver) */
+       if (NULL != d_urb) {
+               /* Turn HCD URB format to one handled by DDEKit */
+               hcd_encode_urb(urb, d_urb);
 
-       completion_cb(d_urb->priv);
+               /* No need for this URB anymore */
+               hcd_free_urb(urb);
 
-       /* URB was handled, forget about it */
-       urb->original_urb = NULL;
+               completion_cb(d_urb->priv);
+       }
 }
 
 
@@ -308,6 +330,9 @@ hcd_decode_urb(hcd_urb * urb, struct ddekit_usb_urb * dde_urb)
 {
        DEBUG_DUMP;
 
+       /* Remember original */
+       urb->original_urb = (void *)dde_urb;
+
        /* No UBR error initially */
        urb->inout_status = EXIT_SUCCESS;
 
@@ -397,7 +422,63 @@ hcd_encode_urb(hcd_urb * urb, struct ddekit_usb_urb * dde_urb)
 {
        DEBUG_DUMP;
 
+       /* Data buffers are the same, no need to copy */
        /* Rewrite output for DDEKit part */
        dde_urb->actual_length = urb->out_size;
        dde_urb->status = urb->inout_status;
 }
+
+
+/*===========================================================================*
+ *    hcd_new_urb                                                            *
+ *===========================================================================*/
+static hcd_urb *
+hcd_new_urb(void)
+{
+       DEBUG_DUMP;
+       return malloc(sizeof(hcd_urb));
+}
+
+
+/*===========================================================================*
+ *    hcd_free_urb                                                           *
+ *===========================================================================*/
+static void
+hcd_free_urb(hcd_urb * urb)
+{
+       DEBUG_DUMP;
+       free(urb);
+}
+
+
+/*===========================================================================*
+ *    hcd_decode_info                                                        *
+ *===========================================================================*/
+static void
+hcd_decode_info(long type, long invalue, hcd_event * event, hcd_reg1 * outvalue)
+{
+       DEBUG_DUMP;
+
+       USB_ASSERT((invalue >= 0) && (invalue <= 0xFF),
+               "Illegal USB info value received");
+
+       switch ((ddekit_msg_type_t)type) {
+               case DDEKIT_HUB_PORT_LS_CONN:
+                       *event = HCD_EVENT_PORT_LS_CONNECTED;
+                       break;
+               case DDEKIT_HUB_PORT_FS_CONN:
+                       *event = HCD_EVENT_PORT_FS_CONNECTED;
+                       break;
+               case DDEKIT_HUB_PORT_HS_CONN:
+                       *event = HCD_EVENT_PORT_HS_CONNECTED;
+                       break;
+               case DDEKIT_HUB_PORT_DISCONN:
+                       *event = HCD_EVENT_PORT_DISCONNECTED;
+                       break;
+               default:
+                       *event = HCD_EVENT_INVALID;
+                       break;
+       }
+
+       *outvalue = (hcd_reg1)invalue;
+}
diff --git a/minix/drivers/usb/usbd/hcd/hcd_schedule.c b/minix/drivers/usb/usbd/hcd/hcd_schedule.c
new file mode 100644 (file)
index 0000000..feacd21
--- /dev/null
@@ -0,0 +1,305 @@
+/*
+ * 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;
+}
index 4c419904d6976e19838d609f35c47b4f3bd9d1b0..b8d25313a63f95fd8b59b6ceab552cefb2a5f607 100644 (file)
@@ -4,10 +4,10 @@
 
 #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"
 
@@ -348,6 +348,7 @@ musb_am335x_init(void)
                ctrl->driver.out_status_stage = musb_out_status_stage;
                ctrl->driver.read_data = musb_read_data;
                ctrl->driver.check_error = musb_check_error;
+               ctrl->driver.port_device = NULL;
        }
 #endif
 
@@ -388,6 +389,7 @@ musb_am335x_init(void)
                ctrl->driver.out_status_stage = musb_out_status_stage;
                ctrl->driver.read_data = musb_read_data;
                ctrl->driver.check_error = musb_check_error;
+               ctrl->driver.port_device = NULL;
        }
 
        return musb_am335x_internal_init();
@@ -563,25 +565,26 @@ musb_am335x_usbx_isr(void * data)
        if (irqstat1 & AM335X_VAL_USBXIRQENABLEXXX1_CONNECTED) {
                USB_DBG("Device connected");
                CLEAR_IRQ1(AM335X_VAL_USBXIRQENABLEXXX1_CONNECTED);
-               driver->current_event = HCD_EVENT_CONNECTED;
-               hcd_handle_event(driver);
+               hcd_update_port(driver, HCD_EVENT_CONNECTED);
+               hcd_handle_event(driver->port_device, HCD_EVENT_CONNECTED,
+                               HCD_UNUSED_VAL);
                return;
        }
 
        if (irqstat1 & AM335X_VAL_USBXIRQENABLEXXX1_DISCONNECTED) {
                USB_DBG("Device disconnected");
                CLEAR_IRQ1(AM335X_VAL_USBXIRQENABLEXXX1_DISCONNECTED);
-               driver->current_event = HCD_EVENT_DISCONNECTED;
-               hcd_handle_event(driver);
+               hcd_handle_event(driver->port_device, HCD_EVENT_DISCONNECTED,
+                               HCD_UNUSED_VAL);
+               hcd_update_port(driver, HCD_EVENT_DISCONNECTED);
                return;
        }
 
        if (0 != irqstat0) {
                USB_DBG("EP interrupt");
                CLEAR_IRQ0(irqstat0);
-               driver->current_event = HCD_EVENT_ENDPOINT;
-               driver->current_endpoint = musb_am335x_irqstat0_to_ep(irqstat0);
-               hcd_handle_event(driver);
+               hcd_handle_event(driver->port_device, HCD_EVENT_ENDPOINT,
+                               musb_am335x_irqstat0_to_ep(irqstat0));
                return;
        }
 
index 2b6c61da3c5a08ccaa9a978eedf177318f6d7384..37417d5e20e671c94dc753ec9e5786371cff3894 100644 (file)
@@ -4,9 +4,9 @@
 
 #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"
@@ -345,13 +345,16 @@ musb_core_stop(void * cfg)
  *    musb_setup_device                                                      *
  *===========================================================================*/
 void
-musb_setup_device(void * cfg, hcd_reg1 ep, hcd_reg1 addr)
+musb_setup_device(void * cfg, hcd_reg1 ep, hcd_reg1 addr,
+               hcd_datatog * tx_tog, hcd_datatog * rx_tog)
 {
        DEBUG_DUMP;
 
        /* Assign  */
        ((musb_core_config *)cfg)->ep = ep;
        ((musb_core_config *)cfg)->addr = addr;
+       ((musb_core_config *)cfg)->datatog_tx = tx_tog;
+       ((musb_core_config *)cfg)->datatog_rx = rx_tog;
 }
 
 
@@ -372,7 +375,7 @@ musb_reset_device(void * cfg, hcd_speed * speed)
        r = core->regs;
 
        /* Set initial parameters */
-       musb_setup_device(core, HCD_DEFAULT_EP, HCD_DEFAULT_ADDR);
+       musb_setup_device(core, HCD_DEFAULT_EP, HCD_DEFAULT_ADDR, NULL, NULL);
 
        /* Set EP and device address to be used in this command */
        musb_set_state(core);
@@ -408,9 +411,14 @@ musb_reset_device(void * cfg, hcd_speed * speed)
                USB_DBG("High speed USB enabled");
        } else {
                /* Only full-speed supported */
-               USB_DBG("High speed USB disabled");
+               host_type0 = HCD_RD1(r, MUSB_REG_HOST_TYPE0);
+               HCD_CLR(host_type0, MUSB_VAL_HOST_TYPE0_MASK);
+               HCD_SET(host_type0, MUSB_VAL_HOST_TYPE0_FULL_SPEED);
+               HCD_WR1(r, MUSB_REG_HOST_TYPE0, host_type0);
 
                *speed = HCD_SPEED_FULL;
+
+               USB_DBG("High speed USB disabled");
        }
 
        return EXIT_SUCCESS;
@@ -529,16 +537,16 @@ musb_rx_stage(void * cfg, hcd_datarequest * request)
 
        /* Make controller reconfigure */
        host_rxcsr = HCD_RD2(r, MUSB_REG_HOST_RXCSR);
-       if (MUSB_DATATOG_UNKNOWN == core->datatog_rx[core->ep]) {
-               /* Reset DATA toggle on first transfer */
-               HCD_SET(host_rxcsr, MUSB_VAL_HOST_RXCSR_CLRDATATOG);
-               core->datatog_rx[core->ep] = MUSB_DATATOG_INIT;
-       }
+       HCD_SET(host_rxcsr, MUSB_VAL_HOST_RXCSR_DATATOGWREN); /* Enable first */
        HCD_SET(host_rxcsr, MUSB_VAL_HOST_RXCSR_FLUSHFIFO);
        HCD_WR2(r, MUSB_REG_HOST_RXCSR, host_rxcsr);
 
-       /* Request packet */
+       /* Set data toggle and start receiving */
        host_rxcsr = HCD_RD2(r, MUSB_REG_HOST_RXCSR);
+       if (HCD_DATATOG_DATA0 == *(core->datatog_rx))
+               HCD_CLR(host_rxcsr, MUSB_VAL_HOST_RXCSR_DATATOG);
+       else
+               HCD_SET(host_rxcsr, MUSB_VAL_HOST_RXCSR_DATATOG);
        HCD_SET(host_rxcsr, MUSB_VAL_HOST_RXCSR_REQPKT);
        HCD_WR2(r, MUSB_REG_HOST_RXCSR, host_rxcsr);
 }
@@ -625,19 +633,20 @@ musb_tx_stage(void * cfg, hcd_datarequest * request)
        HCD_SET(host_txcsr, MUSB_VAL_HOST_TXCSR_MODE);
        HCD_CLR(host_txcsr, MUSB_VAL_HOST_TXCSR_ISO);
        HCD_CLR(host_txcsr, MUSB_VAL_HOST_TXCSR_AUTOSET);
-       if (MUSB_DATATOG_UNKNOWN == core->datatog_tx[core->ep]) {
-               /* Reset DATA toggle on first transfer */
-               HCD_SET(host_txcsr, MUSB_VAL_HOST_TXCSR_CLRDATATOG);
-               core->datatog_tx[core->ep] = MUSB_DATATOG_INIT;
-       }
+       HCD_SET(host_txcsr, MUSB_VAL_HOST_TXCSR_DATATOGWREN); /* Enable first */
+       /* TODO: May have no effect */
        HCD_SET(host_txcsr, MUSB_VAL_HOST_TXCSR_FLUSHFIFO);
        HCD_WR2(r, MUSB_REG_HOST_TXCSR, host_txcsr);
 
        /* Put data in FIFO */
        musb_write_fifo(cfg, request->data, request->data_left, core->ep);
 
-       /* Request packet */
+       /* Set data toggle and start transmitting */
        host_txcsr = HCD_RD2(r, MUSB_REG_HOST_TXCSR);
+       if (HCD_DATATOG_DATA0 == *(core->datatog_tx))
+               HCD_CLR(host_txcsr, MUSB_VAL_HOST_TXCSR_DATATOG);
+       else
+               HCD_SET(host_txcsr, MUSB_VAL_HOST_TXCSR_DATATOG);
        HCD_SET(host_txcsr, MUSB_VAL_HOST_TXCSR_TXPKTRDY);
        HCD_WR2(r, MUSB_REG_HOST_TXCSR, host_txcsr);
 }
@@ -785,6 +794,7 @@ musb_check_error(void * cfg, hcd_transfer xfer, hcd_reg1 ep, hcd_direction dir)
        }
        musb_error_case;
 
+       musb_core_config * core;
        void * r;
        hcd_reg2 host_csr;
        musb_error_case error_case;
@@ -795,7 +805,8 @@ musb_check_error(void * cfg, hcd_transfer xfer, hcd_reg1 ep, hcd_direction dir)
        USB_ASSERT(HCD_TRANSFER_ISOCHRONOUS != xfer,
                "ISO transfer not supported");
 
-       r = ((musb_core_config *)cfg)->regs;
+       core = (musb_core_config *)cfg;
+       r = core->regs;
 
        /* Set EP and device address to be used in this command */
        musb_set_state((musb_core_config *)cfg);
@@ -854,6 +865,13 @@ musb_check_error(void * cfg, hcd_transfer xfer, hcd_reg1 ep, hcd_direction dir)
                /* Get TX status register */
                host_csr = HCD_RD2(r, MUSB_REG_HOST_TXCSR);
 
+               /* Check for completion */
+               if (!(host_csr & MUSB_VAL_HOST_TXCSR_TXPKTRDY)) {
+                       /* ACK received update data toggle */
+                       *(core->datatog_tx) ^= HCD_DATATOG_DATA1;
+                       return EXIT_SUCCESS;
+               }
+
                /* Check for common errors */
                if (host_csr & MUSB_VAL_HOST_TXCSR_ERROR) {
                        USB_MSG("HOST_TXCSR ERROR: %04X", host_csr);
@@ -871,18 +889,30 @@ musb_check_error(void * cfg, hcd_transfer xfer, hcd_reg1 ep, hcd_direction dir)
 
                if (host_csr & MUSB_VAL_HOST_TXCSR_NAK_TIMEOUT) {
                        USB_MSG("HOST_TXCSR NAK_TIMEOUT: %04X", host_csr);
+                       /* Flush FIFO before clearing NAKTIMEOUT
+                        * to abort transfer */
+                       HCD_SET(host_csr, MUSB_VAL_HOST_TXCSR_FLUSHFIFO);
+                       HCD_WR2(r, MUSB_REG_HOST_TXCSR, host_csr);
+                       host_csr = HCD_RD2(r, MUSB_REG_HOST_TXCSR);
                        HCD_CLR(host_csr, MUSB_VAL_HOST_TXCSR_NAK_TIMEOUT);
                        HCD_WR2(r, MUSB_REG_HOST_TXCSR, host_csr);
                        return EXIT_FAILURE;
                }
 
-               return EXIT_SUCCESS;
+               USB_ASSERT(0, "Invalid state of HOST_TXCSR");
        }
 
        if (MUSB_IN_ERROR_CASE == error_case) {
                /* Get RX status register */
                host_csr = HCD_RD2(r, MUSB_REG_HOST_RXCSR);
 
+               /* Check for completion */
+               if (host_csr & MUSB_VAL_HOST_RXCSR_RXPKTRDY) {
+                       /* ACK received update data toggle */
+                       *(core->datatog_rx) ^= HCD_DATATOG_DATA1;
+                       return EXIT_SUCCESS;
+               }
+
                /* Check for common errors */
                if (host_csr & MUSB_VAL_HOST_RXCSR_ERROR) {
                        USB_MSG("HOST_RXCSR ERROR: %04X", host_csr);
@@ -900,12 +930,16 @@ musb_check_error(void * cfg, hcd_transfer xfer, hcd_reg1 ep, hcd_direction dir)
 
                if (host_csr & MUSB_VAL_HOST_RXCSR_NAKTIMEOUT) {
                        USB_MSG("HOST_RXCSR NAK_TIMEOUT: %04X", host_csr);
+                       /* Clear REQPKT before NAKTIMEOUT to abort transfer */
+                       HCD_CLR(host_csr, MUSB_VAL_HOST_RXCSR_REQPKT);
+                       HCD_WR2(r, MUSB_REG_HOST_RXCSR, host_csr);
+                       host_csr = HCD_RD2(r, MUSB_REG_HOST_RXCSR);
                        HCD_CLR(host_csr, MUSB_VAL_HOST_RXCSR_NAKTIMEOUT);
                        HCD_WR2(r, MUSB_REG_HOST_RXCSR, host_csr);
                        return EXIT_FAILURE;
                }
 
-               return EXIT_SUCCESS;
+               USB_ASSERT(0, "Invalid state of HOST_RXCSR");
        }
 
        USB_MSG("Invalid USB transfer error check: 0x%X, 0x%X, 0x%X",
index 48f5d0bdba43741c1e2b03ac71dd104018bdaf95..f8f2987e73bdf0307f8a4e87d8e4fc36b3d79a64 100644 (file)
@@ -5,21 +5,12 @@
 #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) */
@@ -28,8 +19,8 @@ typedef struct {
        void * regs;    /* Points to beginning of memory mapped registers */
        hcd_reg1 ep;    /* Currently used endpoint */
        hcd_reg1 addr;  /* Currently used address */
-       musb_datatog datatog_tx[HCD_TOTAL_EP];
-       musb_datatog datatog_rx[HCD_TOTAL_EP];
+       hcd_datatog * datatog_tx; /* Should point at currently used TX toggle */
+       hcd_datatog * datatog_rx; /* Should point at currently used RX toggle */
 }
 musb_core_config;
 
@@ -43,7 +34,8 @@ void musb_core_stop(void *);
 
 
 /* For HCD interface */
-void musb_setup_device(void *, hcd_reg1, hcd_reg1);
+void musb_setup_device(void *, hcd_reg1, hcd_reg1,
+                       hcd_datatog *, hcd_datatog *);
 int musb_reset_device(void *, hcd_speed *);
 void musb_setup_stage(void *, hcd_ctrlrequest *);
 void musb_rx_stage(void *, hcd_datarequest *);
index 02028a0f5ce9655f43a900df5c8cb70caefc7ca9..49348302fa2a9366575d1e85b812b2aad3d855ed 100644 (file)
@@ -5,7 +5,7 @@
 #ifndef _MUSB_REGS_H_
 #define _MUSB_REGS_H_
 
-#include <usb/hcd_common.h>
+#include <usbd/hcd_common.h>
 
 
 /*===========================================================================*
similarity index 82%
rename from minix/drivers/usb/usbd/include/usb/hcd_common.h
rename to minix/drivers/usb/usbd/include/usbd/hcd_common.h
index 72a55b9f15c15d569dd4d27dd05f758f107ff9cd..d89c449aa4d0478296a76f93532466a364c4f354 100644 (file)
@@ -124,10 +124,15 @@ hcd_direction;
 /* Possible asynchronous HCD events */
 typedef enum {
 
-       HCD_EVENT_CONNECTED,
-       HCD_EVENT_DISCONNECTED,
-       HCD_EVENT_ENDPOINT,
-       HCD_EVENT_URB
+       HCD_EVENT_CONNECTED = 0,        /* Device connected directly to root */
+       HCD_EVENT_DISCONNECTED,         /* Directly connected device removed */
+       HCD_EVENT_PORT_LS_CONNECTED,    /* Low speed device connected to hub */
+       HCD_EVENT_PORT_FS_CONNECTED,    /* Full speed device connected to hub */
+       HCD_EVENT_PORT_HS_CONNECTED,    /* High speed device connected to hub */
+       HCD_EVENT_PORT_DISCONNECTED,    /* Device disconnected from hub */
+       HCD_EVENT_ENDPOINT,             /* Something happened at endpoint */
+       HCD_EVENT_URB,                  /* URB was submitted by device driver */
+       HCD_EVENT_INVALID = 0xFF
 }
 hcd_event;
 
@@ -149,6 +154,14 @@ typedef enum {
 }
 hcd_speed;
 
+/* Possible data toggle values (at least for bulk transfer) */
+typedef enum {
+
+       HCD_DATATOG_DATA0 = 0,
+       HCD_DATATOG_DATA1 = 1
+}
+hcd_datatog;
+
 
 /*===========================================================================*
  *    HCD threading/device/URB types                                         *
@@ -161,7 +174,14 @@ typedef struct usb_ctrlrequest             hcd_ctrlrequest;
 
 /* Largest value that can be transfered by this driver at a time
  * see MAXPAYLOAD in TXMAXP/RXMAXP */
-#define MAX_WTOTALLENGTH               1024
+#define MAX_WTOTALLENGTH               1024u
+
+/* TODO: This has corresponding redefinition in hub driver */
+/* Limit of child devices for each parent */
+#define HCD_CHILDREN                   8u
+
+/* Total number of endpoints available in USB 2.0 */
+#define HCD_TOTAL_EP                   16u
 
 /* Forward declarations */
 typedef struct hcd_datarequest         hcd_datarequest;
@@ -189,6 +209,7 @@ struct hcd_urb {
        /* Basic */
        void * original_urb;
        hcd_device_state * target_device;
+       void (*handled)(hcd_urb *);     /* URB handled callback */
 
        /* Transfer (in/out signifies what may be overwritten by HCD) */
        hcd_ctrlrequest * in_setup;
@@ -207,18 +228,26 @@ struct hcd_urb {
 /* Current state of attached device */
 struct hcd_device_state {
 
-       hcd_driver_state * driver;      /* Specific HCD driver object */
+       hcd_device_state * parent;              /* In case of hub attachment */
+       hcd_device_state * child[HCD_CHILDREN]; /* In case of being hub */
+       hcd_device_state * _next;               /* To allow device lists */
+       hcd_driver_state * driver;              /* Specific HCD driver object */
        hcd_thread * thread;
        hcd_lock * lock;
        void * data;
 
-       hcd_urb urb;
+       hcd_urb * urb;                          /* URB to be used by device */
+       hcd_event wait_event;                   /* Expected event */
+       hcd_reg1 wait_ep;                       /* Expected event's endpoint */
        hcd_device_descriptor device_desc;
        hcd_configuration config_tree;
        hcd_reg1 max_packet_size;
        hcd_speed speed;
        hcd_state state;
-       hcd_reg1 address;
+       hcd_reg1 reserved_address;
+       hcd_reg1 current_address;
+       hcd_datatog ep_tx_tog[HCD_TOTAL_EP];
+       hcd_datatog ep_rx_tog[HCD_TOTAL_EP];
 
        /*
         * Control transfer's local data:
@@ -247,24 +276,24 @@ struct hcd_device_state {
 #define HCD_DEFAULT_EP                 0x00u
 #define HCD_DEFAULT_ADDR               0x00u
 #define HCD_DEFAULT_CONFIG             0x00u
+#define HCD_FIRST_ADDR                 0x01u
 #define HCD_LAST_ADDR                  0x7Fu
+#define HCD_TOTAL_ADDR                 0x80u
 #define HCD_LAST_EP                    0x0Fu
-#define HCD_TOTAL_EP                   0x10u
-#define HCD_ANY_EP                     0xFFu
+#define HCD_UNUSED_VAL                 0xFFu   /* When number not needed */
+#define HCD_DEFAULT_NAKLIMIT           0x10u
+
 
 /* Legal interval values */
 #define HCD_LOWEST_INTERVAL            0x00u
 #define HCD_HIGHEST_INTERVAL           0xFFu
 
-/* TODO: One device only */
-#define HCD_ATTACHED_ADDR              0x01u
-
 /* Translates configuration number for 'set configuration' */
 #define HCD_SET_CONFIG_NUM(num)                ((num)+0x01u)
 
 /* Default MaxPacketSize for control transfer */
-#define HCD_LS_MAXPACKETSIZE           8u
-#define HCD_HS_MAXPACKETSIZE           64u
+#define HCD_LS_MAXPACKETSIZE           8u      /* Low-speed, Full-speed */
+#define HCD_HS_MAXPACKETSIZE           64u     /* High-speed */
 #define HCD_MAX_MAXPACKETSIZE          1024u
 
 
@@ -313,7 +342,13 @@ void hcd_disconnect_device(hcd_device_state *);
 void hcd_device_wait(hcd_device_state *, hcd_event, hcd_reg1);
 
 /* Unlocks device thread halted by 'hcd_device_wait' */
-void hcd_device_continue(hcd_device_state *);
+void hcd_device_continue(hcd_device_state *, hcd_event, hcd_reg1);
+
+/* Allocation/deallocation of device structures */
+hcd_device_state * hcd_new_device(void);
+void hcd_delete_device(hcd_device_state *);
+void hcd_dump_devices(void);
+int hcd_check_device(hcd_device_state *);
 
 
 /*===========================================================================*
similarity index 94%
rename from minix/drivers/usb/usbd/include/usb/hcd_ddekit.h
rename to minix/drivers/usb/usbd/include/usbd/hcd_ddekit.h
index 446e9cd5c86cf2162f95112486590f6d7bf5f8ea..b5136517326202e4e7372c8129f6ffa372e8af40 100644 (file)
@@ -5,7 +5,7 @@
 #ifndef _HCD_DDEKIT_H_
 #define _HCD_DDEKIT_H_
 
-#include <usb/hcd_common.h>
+#include <usbd/hcd_common.h>
 
 /*===========================================================================*
  *    External declarations                                                  *
similarity index 62%
rename from minix/drivers/usb/usbd/include/usb/hcd_interface.h
rename to minix/drivers/usb/usbd/include/usbd/hcd_interface.h
index 66a92665b0c59c856a5a8f4e5249722a08fbdf64..be225adedacbceb3cc33a9ce77fa5027a5ad0ba6 100644 (file)
@@ -2,13 +2,14 @@
  * Interface for HCD
  *
  * This file holds prototypes that must be implemented by HCD
- * and event call that should be called when interrupt occurred
+ * and call that should be used for asynchronous events
+ * (interrupts, UBR submits, hub events, ...)
  */
 
 #ifndef _HCD_INTERFACE_H_
 #define _HCD_INTERFACE_H_
 
-#include <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 *);
@@ -39,20 +49,23 @@ struct hcd_driver_state {
        /* Controller's private data (like mapped registers) */
        void *          private_data;
 
-       /* Current state to be handled by driver */
-       hcd_event       current_event;
-       hcd_reg1        current_endpoint;
-       hcd_event       expected_event;
-       hcd_reg1        expected_endpoint;
+       /* TODO: Only one port for each driver */
+       /* Represents device attached to USB port handled by this driver */
+       hcd_device_state * port_device;
+
+       /* Array to hold information of unused device addresses */
+       hcd_addr_state dev_addr[HCD_TOTAL_ADDR];
 };
 
 
 /*===========================================================================*
  *    HCD event handling routine                                             *
  *===========================================================================*/
-/* Handle asynchronous event
- * This must be called in case of specific HCD interrupts listed above */
-void hcd_handle_event(hcd_driver_state *);
+/* Handle asynchronous event */
+void hcd_handle_event(hcd_device_state *, hcd_event, hcd_reg1);
+
+/* This resolves port's device structure for given driver and event */
+void hcd_update_port(hcd_driver_state *, hcd_event);
 
 
 #endif /* !_HCD_INTERFACE_H_ */
diff --git a/minix/drivers/usb/usbd/include/usbd/hcd_schedule.h b/minix/drivers/usb/usbd/include/usbd/hcd_schedule.h
new file mode 100644 (file)
index 0000000..ba34f99
--- /dev/null
@@ -0,0 +1,16 @@
+/*
+ * 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_ */
similarity index 56%
rename from minix/drivers/usb/usbd/include/usb/usb_common.h
rename to minix/drivers/usb/usbd/include/usbd/usbd_common.h
index 803699ec7d16457b1e483c7bf7e416ae6fe69c64..5d1bfc074bc2b0a88c91bd055bb9677593fcca64 100644 (file)
@@ -1,9 +1,9 @@
 /*
- * Whatever is commonly used throughout USB code
+ * Whatever is commonly used throughout USBD code
  */
 
-#ifndef _USB_COMMON_H_
-#define _USB_COMMON_H_
+#ifndef _USBD_COMMON_H_
+#define _USBD_COMMON_H_
 
 /* For commonly used: NULL, EXIT_*, and stuff like that */
 #include <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_ */
diff --git a/minix/drivers/usb/usbd/include/usbd/usbd_schedule.h b/minix/drivers/usb/usbd/include/usbd/usbd_schedule.h
new file mode 100644 (file)
index 0000000..2ee55e7
--- /dev/null
@@ -0,0 +1,12 @@
+/*
+ * USBD URB scheduler interface
+ */
+
+#ifndef _USBD_SCHEDULE_H_
+#define _USBD_SCHEDULE_H_
+
+/* Should be used to create/destroy URB scheduler in base code */
+int usbd_init_scheduler(void);
+void usbd_deinit_scheduler(void);
+
+#endif /* !_USBD_SCHEDULE_H_ */
index c3b514e5af926575bf51a33fd84c2493e5f5476b..e6bb810665b34e4ea225410a2ddfa0291b5e2f84 100644 (file)
@@ -78,12 +78,23 @@ struct ddekit_usb_urb {
        void *ddekit_priv;
 };
 
+/* USB message types */
+typedef enum {
+
+       DDEKIT_HUB_PORT_LS_CONN,        /* Low speed device connected */
+       DDEKIT_HUB_PORT_FS_CONN,        /* Full speed device connected */
+       DDEKIT_HUB_PORT_HS_CONN,        /* High speed device connected */
+       DDEKIT_HUB_PORT_DISCONN         /* Device disconnected */
+}
+ddekit_msg_type_t;
+
 int ddekit_usb_dev_set_data(struct ddekit_usb_dev *dev, void *data);
 void *ddekit_usb_dev_get_data(struct ddekit_usb_dev *dev);
 void ddekit_usb_get_device_id(struct ddekit_usb_dev *dev, struct
        ddekit_usb_device_id *id);
 int ddekit_usb_submit_urb(struct ddekit_usb_urb *d_urb);
 int ddekit_usb_cancle_urb(struct ddekit_usb_urb *d_urb);
+long ddekit_usb_info(struct ddekit_usb_dev *, long, long);
 
 /*
  * This one is only implemented for the client side. For the server side is
index 9b5d22b2499061115296e29a3d60b6557b33f76c..637f77c0b22f2c6527d7d9c57d0a73027f57d9d4 100644 (file)
 #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)                  *
  *===========================================================================*/
index 07845527dfffd06faac5385a5c93a881354efc2f..1e52d68c4c8f5accf6fc860d92e5b8dbbe168b40 100644 (file)
@@ -152,4 +152,7 @@ int usb_init(char *name);
 /** This functions handles a message from the HCD */
 int usb_handle_msg(struct usb_driver *ubd, message *msg);
 
+/** Lets device driver send HCD various information */
+int usb_send_info(long, long);
+
 #endif /* _MINIX_USB_H */
index 9b4457a938f4444a4bb269015c4b8562354a3f31..6476ea62eacf8f858c5353f1fa5a3ff745a3151b 100644 (file)
@@ -1,8 +1,6 @@
 C_HERE=${NETBSDSRCDIR}/minix/lib/libc/arch/${ARCHSUBDIR}
 .PATH: ${C_HERE}
 
-.warning looking into ${C_HERE}
-
 SRCS+= _cpuid.S        \
        get_bp.S        \
        getprocessor.S  \
index b60981aebe3161a0ff4cd515d42d1456d1853dc4..da0a949ea4c519aa89f14c89d9344d3999fc53b8 100644 (file)
@@ -288,13 +288,13 @@ void _ddekit_interrupt_trigger(int irq_id)
        irq_s = find_by_irq_id(irq_id);
 
        if (irq_s)      {
-               DDEBUG_MSG_VERBOSE("Triggering IRQ %d", irq);
+               DDEBUG_MSG_VERBOSE("Triggering IRQ %d", irq_s->irq);
                ddekit_sem_up(irq_s->sem);
                if (0 != (err_code = sys_irqenable(&irq_s->irq_hook)))
                        ddekit_panic("Failed to enable interrupt "
                                        "(ERROR %d)", err_code);
        } else {
-               DDEBUG_MSG_WARN("no handler for IRQ %d", irq);
+               DDEBUG_MSG_WARN("no handler for IRQ %d", irq_s->irq);
        }
 }
 
index c232e953e1dcff2cdc1e6ea1aa483e552605cf38..5e56aeca334d5d68c381de8d3d2d03287211a395 100644 (file)
@@ -186,6 +186,17 @@ int ddekit_usb_cancle_urb(struct ddekit_usb_urb *d_urb)
        return res;
 }
 
+
+/*****************************************************************************
+ *         ddekit_usb_info                                                   *
+ *****************************************************************************/
+long
+ddekit_usb_info(struct ddekit_usb_dev * UNUSED(dev), long type, long value)
+{
+       return usb_send_info(type, value);
+}
+
+
 static void _ddekit_usb_thread()
 { 
        struct ddekit_minix_msg_q *mq = ddekit_minix_create_msg_q(USB_BASE, 
index a45b65bd4a13c00167a107efd9c6aeff89c25ac2..df200435c9ad8644890f38470cdd29c41fa040f7 100644 (file)
@@ -71,6 +71,7 @@ static struct ddekit_usb_urb *ddekit_usb_urb_from_mx_urb(struct usb_urb
        *mx_urb);
 static void submit_urb(message *msg);
 static void cancle_urb(message *msg);
+static void get_info(message *msg);
 static void completion_callback(void *priv);
 
 static void prepare_devman_usbdev(struct ddekit_usb_dev * dev, int
@@ -441,6 +442,39 @@ static void cancle_urb(message *msg)
 }
 
 
+/*****************************************************************************
+ *         get_info                                                          *
+ *****************************************************************************/
+static void
+get_info(message * msg)
+{
+       struct minix_usb_driver * drv;
+       endpoint_t ep;
+       long info_type;
+       long info_value;
+
+       /* Read */
+       ep              = msg->m_source;
+       info_type       = msg->USB_INFO_TYPE;
+       info_value      = msg->USB_INFO_VALUE;
+
+       /* Reuse as reply */
+       msg->m_type     = USB_REPLY;
+       msg->USB_RESULT = -1;
+
+       /* Try and find driver first */
+       if (NULL == (drv = find_driver(ep)))
+               ddekit_printf("Non-registered driver tries to send info");
+       else
+               /* Route info to device */
+               msg->USB_RESULT = ddekit_usb_info(_devices[drv->dev].dev,
+                                               info_type, info_value);
+
+       /* Reply */
+       ipc_send(ep, msg);
+}
+
+
 /*****************************************************************************
  *         completion_callback                                               *
  ****************************************************************************/
@@ -713,6 +747,9 @@ static int handle_msg(message *msg)
                case USB_RQ_CANCEL_URB:
                        cancle_urb(msg);
                        return 1;
+               case USB_RQ_SEND_INFO:
+                       get_info(msg);
+                       return 1;
                default:
                        return 0;
        }
index a6c8f6f6e2f4cc07f2f89c8aa1b2a3af019a1960..772c587029ef610eb040bd3e9e3f95cd48595ca9 100644 (file)
@@ -223,3 +223,32 @@ int usb_handle_msg(struct usb_driver *ud, message *msg)
        }
 }
 
+
+/*****************************************************************************
+ *         usb_send_info                                                     *
+ *****************************************************************************/
+int
+usb_send_info(long info_type, long info_value)
+{
+       int res;
+       message msg;
+
+       /* Prepare message */
+       msg.m_type              = USB_RQ_SEND_INFO;
+       msg.USB_INFO_TYPE       = info_type;
+       msg.USB_INFO_VALUE      = info_value;
+
+       /* Send/receive message */
+       res = ipc_sendrec(hcd_ep, &msg);
+
+       if (res != 0)
+               panic("usb_send_info: could not talk to HCD: %d", res);
+
+       if (msg.m_type != USB_REPLY)
+               panic("usb_send_info: got illegal response from HCD: %d", msg.m_type);
+
+       if (msg.USB_RESULT != 0)
+               panic("usb_send_info: got illegal response from HCD: %d", msg.m_type);
+
+       return msg.USB_RESULT;
+}