From 92adfc8ec3e23847134e8dc88619cc62cfa84cde Mon Sep 17 00:00:00 2001 From: Ben Pfaff Date: Thu, 11 Dec 2008 21:08:14 -0800 Subject: [PATCH] usb-bug-fixes.patch (applied cleanly) --- src/devices/usb.c | 238 +++---- src/devices/usb.h | 11 +- src/devices/usb_storage.c | 39 +- src/devices/usb_uhci.c | 1230 +++++++++++++++---------------------- src/threads/malloc.c | 35 +- 5 files changed, 633 insertions(+), 920 deletions(-) diff --git a/src/devices/usb.c b/src/devices/usb.c index 7cdb2f0..5eb670d 100644 --- a/src/devices/usb.c +++ b/src/devices/usb.c @@ -4,6 +4,7 @@ #include "threads/malloc.h" #include "devices/timer.h" #include +#include #include #include @@ -136,10 +137,7 @@ static char *usb_get_string (struct usb_dev *d, int ndx); static struct class *usb_get_class_by_id (int id); static void usb_setup_dev_addr (struct usb_dev *dev); static void usb_config_dev (struct usb_dev *dev, int config_val); -static int usb_load_config (struct usb_dev *dev, int idx, void *data, - int dsz); -static int usb_tx_all (struct usb_endpoint *eop, void *buf, - int max_bytes, int bailout, bool in); +static int usb_load_config (struct usb_dev *dev, int idx); static void usb_attach_interfaces (struct usb_dev *dev); static void usb_apply_class_to_interfaces (struct class *c); static size_t wchar_to_ascii (char *dst, const char *src); @@ -241,16 +239,16 @@ usb_configure_default (struct host *h) { struct usb_dev *dev; struct usb_setup_pkt sp; - char data[256]; - struct device_descriptor *dd; + struct device_descriptor dd; host_dev_info hi; host_eop_info cfg_eop; bool ignore_device = false; - int err, sz, txed; + int err; int config_val; + size_t size; hi = h->dev->create_dev_channel (h->info, ADDR_DEFAULT, USB_VERSION_1_1); - cfg_eop = h->dev->create_eop (hi, 0, 64); + cfg_eop = h->dev->create_eop (hi, 0, 8); /* determine device descriptor */ sp.recipient = USB_SETUP_RECIP_DEV; @@ -259,53 +257,36 @@ usb_configure_default (struct host *h) sp.request = REQ_STD_GET_DESC; sp.value = SETUP_DESC_DEVICE << 8; sp.index = 0; - sp.length = sizeof (struct device_descriptor); + sp.length = 8; - err = - h->dev->tx_pkt (cfg_eop, USB_TOKEN_SETUP, &sp, 0, sizeof (sp), NULL, - true); - /* did it timeout? - no device */ - if (err != USB_HOST_ERR_NONE) + size = sp.length; + err = h->dev->dev_control (cfg_eop, &sp, &dd, &size); + if (err) { - h->dev->remove_eop (cfg_eop); - h->dev->remove_dev_channel (hi); + /* FIXME: free memory. */ + printf ("err=%d: no more devices\n", err); return NULL; } - dd = (void *) &data; - memset (dd, 0, sizeof (data)); - txed = 0; - h->dev->tx_pkt (cfg_eop, USB_TOKEN_IN, data, 0, sizeof (data) - txed, &sz, - true); - - /* some devices only send 16 bytes for the device descriptor - - * they wind up with a NAK, waiting for another command to be issued - * - * if it's a 16 byte max packet device, we just take the first 16 bytes */ - - if (dd->usb_spec == USB_VERSION_1_0) + if (dd.usb_spec == USB_VERSION_1_0) { /* USB 1.0 devices have strict schedule requirements not yet supported */ printf ("USB 1.0 device detected - skipping\n"); } - else if (sz == 8) - { - /* read in the full descriptor */ - txed = sz; - while (txed <= 16) - { - if (h->dev-> - tx_pkt (cfg_eop, USB_TOKEN_IN, data + txed, 0, - sizeof (data) - txed, &sz, true) != USB_HOST_ERR_NONE) - break; - txed += sz; - } - } - if (dd->num_configs == 0) + if (dd.num_configs == 0) ignore_device = true; h->dev->remove_eop (cfg_eop); - cfg_eop = h->dev->create_eop (hi, 0, dd->max_pktsz); + cfg_eop = h->dev->create_eop (hi, 0, dd.max_pktsz); + + sp.length = sizeof dd; + size = sp.length; + err = h->dev->dev_control (cfg_eop, &sp, &dd, &size); + if (err) + { + /* FIXME: free memory. */ + return NULL; + } /* device exists - create device structure */ dev = malloc (sizeof (struct usb_dev)); @@ -318,37 +299,38 @@ usb_configure_default (struct host *h) dev->cfg_eop.h_eop = cfg_eop; - dev->usb_version = dd->usb_spec; - dev->default_iface.class_id = dd->dev_class; - dev->default_iface.subclass_id = dd->dev_subclass; + dev->usb_version = dd.usb_spec; + dev->default_iface.class_id = dd.dev_class; + dev->default_iface.subclass_id = dd.dev_subclass; dev->default_iface.iface_num = 0; - dev->default_iface.proto = dd->dev_proto; + dev->default_iface.proto = dd.dev_proto; dev->default_iface.class = NULL; dev->default_iface.c_info = NULL; dev->default_iface.dev = dev; dev->cfg_eop.iface = &dev->default_iface; - dev->cfg_eop.max_pkt = dd->max_pktsz; + dev->cfg_eop.max_pkt = dd.max_pktsz; dev->cfg_eop.eop = 0; dev->host = h; - dev->vendor_id = dd->vendor_id; - dev->product_id = dd->product_id; - dev->device_id = dd->device_id; + dev->vendor_id = dd.vendor_id; + dev->product_id = dd.product_id; + dev->device_id = dd.device_id; if (ignore_device == false) { - dev->product = usb_get_string (dev, dd->product); - dev->manufacturer = usb_get_string (dev, dd->manufacturer); + dev->product = usb_get_string (dev, dd.product); + dev->manufacturer = usb_get_string (dev, dd.manufacturer); + printf ("product=%s manufacturer=%s\n", dev->product, dev->manufacturer); } config_val = -123; /* read in configuration data if there are configurations available */ /* (which there should be...) */ - if (dd->num_configs > 0 && ignore_device == false) + if (dd.num_configs > 0 && ignore_device == false) { - config_val = usb_load_config (dev, 0, data, sizeof (data)); + config_val = usb_load_config (dev, 0); if (config_val < 0) { printf @@ -370,14 +352,16 @@ usb_configure_default (struct host *h) * XXX support multiple configurations */ static int -usb_load_config (struct usb_dev *dev, int idx, void *data, int dsz) +usb_load_config (struct usb_dev *dev, int idx) { struct usb_setup_pkt sp; struct config_descriptor *cd; struct host *h; host_eop_info cfg; void *ptr; - int config_val, err, sz; + int config_val, err; + size_t size; + uint8_t data[256]; int i; h = dev->host; @@ -389,34 +373,29 @@ usb_load_config (struct usb_dev *dev, int idx, void *data, int dsz) sp.request = REQ_STD_GET_DESC; sp.value = SETUP_DESC_CONFIG << 8 | idx; sp.index = 0; - sp.length = dsz; - cd = data; + sp.length = sizeof data; + cd = (struct config_descriptor *) data; - err = h->dev->tx_pkt (cfg, USB_TOKEN_SETUP, &sp, 0, sizeof (sp), &sz, true); + size = sp.length; + err = h->dev->dev_control (cfg, &sp, data, &size); if (err != USB_HOST_ERR_NONE) { printf ("USB: Could not setup GET descriptor\n"); return -err; } - sz = usb_tx_all (&dev->cfg_eop, cd, dsz, - sizeof (struct config_descriptor), true); - if (sz < sizeof (struct config_descriptor)) + if (size < sizeof *cd || cd->hdr.type != SETUP_DESC_CONFIG) { - printf ("USB: Did not rx GET descriptor (%d bytes, expected %d)\n", sz, - sizeof (struct config_descriptor)); - return -err; + printf ("USB: Invalid descriptor\n"); + return -1; } - if (sz == 0 || cd->hdr.type != SETUP_DESC_CONFIG) + if (size < cd->total_length) { - printf ("USB: Invalid descriptor\n"); + printf ("USB: configuration data too long\n"); return -1; } - if (sz < cd->total_length) - sz += usb_tx_all (&dev->cfg_eop, data+sz, dsz, cd->total_length - sz, true); - dev->pwr = cd->max_power; /* interface information comes right after config data */ @@ -499,13 +478,9 @@ usb_config_dev (struct usb_dev *dev, int config_val) sp.value = config_val; sp.index = 0; sp.length = 0; - err = h->dev->tx_pkt (cfg, USB_TOKEN_SETUP, &sp, 0, sizeof (sp), NULL, true); - if (err != USB_HOST_ERR_NONE) - PANIC ("USB: Config setup packet did not tx\n"); - - err = h->dev->tx_pkt (cfg, USB_TOKEN_IN, NULL, 0, 0, NULL, true); + err = h->dev->dev_control (cfg, &sp, 0, NULL); if (err != USB_HOST_ERR_NONE) - PANIC ("USB: Could not configure device!\n"); + PANIC ("USB: Could not configure device (err=%d)", err); } /** @@ -533,13 +508,7 @@ usb_setup_dev_addr (struct usb_dev *dev) sp.value = dev->addr; sp.index = 0; sp.length = 0; - err = - h->dev->tx_pkt (cfg, USB_TOKEN_SETUP, &sp, 0, sizeof (sp), NULL, true); - if (err != USB_HOST_ERR_NONE) - { - PANIC ("USB: WHOOPS!!!!!!!\n"); - } - err = h->dev->tx_pkt (cfg, USB_TOKEN_IN, NULL, 0, 0, NULL, true); + err = h->dev->dev_control (cfg, &sp, 0, NULL); if (err != USB_HOST_ERR_NONE) { PANIC ("USB: Error on setting device address (err = %d)\n", err); @@ -554,9 +523,10 @@ static char * usb_get_string (struct usb_dev *udev, int ndx) { struct usb_setup_pkt sp; - char str[MAX_USB_STR]; + char str[MAX_USB_STR + 1]; char *ret; - int sz; + size_t size; + int err; sp.recipient = USB_SETUP_RECIP_DEV; sp.type = USB_SETUP_TYPE_STD; @@ -564,26 +534,20 @@ usb_get_string (struct usb_dev *udev, int ndx) sp.request = REQ_STD_GET_DESC; sp.value = (SETUP_DESC_STRING << 8) | ndx; sp.index = 0; - sp.length = MAX_USB_STR; - udev->host->dev->tx_pkt (udev->h_cfg_eop, USB_TOKEN_SETUP, - &sp, 0, sizeof (sp), NULL, false); - sz = usb_tx_all (&udev->cfg_eop, &str, MAX_USB_STR, 2, true); - sz += - usb_tx_all (&udev->cfg_eop, str + sz, (uint8_t) (str[0]) - sz, 0, true); - - /* string failed to tx? */ - if (sz == 0) + size = sp.length = MAX_USB_STR; + err = udev->host->dev->dev_control (udev->h_cfg_eop, &sp, str, &size); + if (err) return NULL; /* some devices don't respect the string descriptor length value (str[0]) * and just send any old value they want, so we can't use it */ - - str[(sz < (MAX_USB_STR - 1)) ? (sz) : (MAX_USB_STR - 1)] = '\0'; + str[size] = '\0'; /* usb uses wchars for strings, convert to ASCII */ wchar_to_ascii (str, str + 2); ret = malloc (strlen (str) + 1); - strlcpy (ret, str, MAX_USB_STR); + if (ret != NULL) + strlcpy (ret, str, MAX_USB_STR); return ret; } @@ -679,47 +643,31 @@ usb_apply_class_to_interfaces (struct class *c) } int -usb_dev_bulk (struct usb_endpoint *eop, void *buf, int sz, int *tx) +usb_dev_control (struct usb_endpoint *eop, struct usb_setup_pkt *setup, + void *data, size_t *size) { struct host *h; int err; - int token; - ASSERT (eop != NULL); h = eop->iface->dev->host; - - if (eop->direction == 0) - token = USB_TOKEN_OUT; - else - token = USB_TOKEN_IN; - - err = h->dev->tx_pkt (eop->h_eop, token, buf, sz, sz, tx, true); - + err = h->dev->dev_control (eop->h_eop, setup, data, size); return err; } int -usb_dev_setup (struct usb_endpoint *eop, bool in, - struct usb_setup_pkt *s, void *buf, int sz) +usb_dev_bulk (struct usb_endpoint *eop, void *buf, int sz, int *tx) { struct host *h; + size_t size; int err; - ASSERT (eop != NULL); h = eop->iface->dev->host; - err = h->dev->tx_pkt (eop->h_eop, USB_TOKEN_SETUP, s, - 0, sizeof (struct usb_setup_pkt), NULL, true); - if (err != USB_HOST_ERR_NONE) - { - printf ("usb_dev_setup: failed\n"); - return 0; - } - - err = h->dev->tx_pkt (eop->h_eop, (in) ? USB_TOKEN_IN : USB_TOKEN_OUT, - buf, 0, sz, &sz, true); - - return sz; + size = sz; + err = h->dev->dev_bulk (eop->h_eop, eop->direction == 0, buf, &size); + *tx = size; + printf ("usb_dev_bulk=%d\n", err); + return err; } /** convert a wchar string to ascii in place */ @@ -731,46 +679,8 @@ wchar_to_ascii (char *dst, const char *src) { dst[sz] = src[sz * 2]; } + while (sz > 0 && isspace (dst[sz - 1])) + sz--; dst[sz] = '\0'; return sz; } - -/* this is used for variable sized transfers where a normal bulk transfer would - probably fail, since it expects some minimum size - we just want to - read/write as much to the pipe as we can - */ -static int -usb_tx_all (struct usb_endpoint *eop, void *buf, - int max_bytes, int bailout, bool in) -{ - int txed; - int token; - int prev_sz = 0; - struct host *h; - - if (max_bytes <= 0) - return 0; - - if (bailout == 0) - bailout = 512; - - txed = 0; - token = (in) ? USB_TOKEN_IN : USB_TOKEN_OUT; - h = eop->iface->dev->host; - while (txed < max_bytes && txed < bailout) - { - int sz, err; - sz = 0; - err = h->dev->tx_pkt (eop->h_eop, token, - buf + txed, 0, max_bytes - txed, &sz, true); - if (prev_sz == 0) - prev_sz = sz; - txed += sz; - /* this should probably be using short packet detection */ - if (err != USB_HOST_ERR_NONE || sz != prev_sz || sz == 0) - { - return txed; - } - } - return txed; -} diff --git a/src/devices/usb.h b/src/devices/usb.h index b15978e..30b400a 100644 --- a/src/devices/usb.h +++ b/src/devices/usb.h @@ -13,6 +13,7 @@ #define USB_HOST_ERR_BUFFER 5 #define USB_HOST_ERR_STALL 6 #define USB_HOST_ERR_NODEV 7 +#define USB_HOST_ERR_NOMEM 8 #define make_usb_pid(x) ((x) | ((~(x)) << 4)) @@ -57,6 +58,7 @@ struct class; struct host; +struct usb_setup_pkt; typedef void *host_info; typedef void *host_eop_info; typedef void *host_dev_info; @@ -88,6 +90,9 @@ struct usb_host { const char *name; int (*detect_change) (host_info); + int (*dev_control) (host_eop_info, struct usb_setup_pkt *, + void *data, size_t *size); + int (*dev_bulk) (host_eop_info, bool out, void *data, size_t *size); int (*tx_pkt) (host_eop_info, int pid, void *pkt, int min_sz, int max_sz, int *in_sz, bool wait); @@ -189,9 +194,11 @@ int usb_unregister_host (struct usb_host *, host_info info); int usb_register_class (struct usb_class *); int usb_unregister_class (struct usb_class *); +int usb_dev_control (struct usb_endpoint *, struct usb_setup_pkt *, + void *data, size_t *size); int usb_dev_bulk (struct usb_endpoint *eop, void *buf, int sz, int *tx); -int usb_dev_setup (struct usb_endpoint *eop, bool in, - struct usb_setup_pkt *s, void *buf, int sz); int usb_dev_wait_int (struct usb_dev *); +void usb_storage_init (void); + #endif diff --git a/src/devices/usb_storage.c b/src/devices/usb_storage.c index 2511439..56c940a 100644 --- a/src/devices/usb_storage.c +++ b/src/devices/usb_storage.c @@ -285,43 +285,47 @@ static void msc_get_geometry (struct msc_class_info *mci) { struct msc_cbw cbw; - struct scsi_capacity10 *cap; - uint8_t buf[sizeof (struct msc_csw) + sizeof (struct scsi_capacity10)]; + struct scsi_capacity10 cap; struct scsi_cdb10 *cdb; - struct msc_csw *csw; + struct msc_csw csw; int tx; - /* cap + csw must be read in one shot, combine into a single buffer */ - cap = (struct scsi_capacity10 *) (buf); - csw = (struct msc_csw *) (&buf[sizeof (struct scsi_capacity10)]); - + memset (&cbw, 0, sizeof cbw); cbw.sig = CBW_SIG_MAGIC; cbw.tag = mci->tag++; cbw.tx_len = sizeof (struct scsi_capacity10); cbw.flags = CBW_FL_IN; cbw.lun = 0; cbw.cb_len = sizeof (struct scsi_cdb10); - cdb = (void *) (&cbw.cb); - memset (cdb, 0, sizeof (struct scsi_cdb10)); cdb->op = SCSI_OP_READ_CAPACITY10; - usb_dev_bulk (mci->eop_out, &cbw, sizeof (cbw), &tx); - usb_dev_bulk (mci->eop_in, &buf, sizeof (buf), &tx); + usb_dev_bulk (mci->eop_out, &cbw, sizeof cbw, &tx); + if (tx != sizeof cbw) + PANIC ("send cbw"); + usb_dev_bulk (mci->eop_in, &cap, sizeof cap, &tx); + if (tx != sizeof cap) + PANIC ("recv cap"); + usb_dev_bulk (mci->eop_in, &csw, sizeof csw, &tx); + if (tx != sizeof csw) + PANIC ("recv csw"); - mci->blk_count = be32_to_machine (cap->blocks) + 1; - mci->blk_size = be32_to_machine (cap->block_len); + mci->blk_count = be32_to_machine (cap.blocks) + 1; + mci->blk_size = be32_to_machine (cap.block_len); +#if 0 /* did CSW stall? */ if (tx == sizeof (struct scsi_capacity10)) { + PANIC ("foo"); msc_reset_endpoint (mci->eop_in); usb_dev_bulk (mci->eop_in, csw, sizeof (*csw), &tx); } +#endif - ASSERT (csw->sig == CSW_SIG_MAGIC); + ASSERT (csw.sig == CSW_SIG_MAGIC); - if (csw->status != CSW_STATUS_PASSED) + if (csw.status != CSW_STATUS_PASSED) { PANIC ("USB storage geometry read failure!\n"); } @@ -408,6 +412,7 @@ msc_reset_endpoint (struct usb_endpoint *eop) { struct usb_setup_pkt sp; + printf ("*** reset endpoint\n"); sp.recipient = USB_SETUP_RECIP_ENDPT; sp.type = USB_SETUP_TYPE_STD; sp.direction = 0; @@ -415,7 +420,7 @@ msc_reset_endpoint (struct usb_endpoint *eop) sp.value = 0; /* 0 is ENDPOINT_HALT */ sp.index = eop->eop; sp.length = 0; - usb_dev_setup (eop, true, &sp, NULL, 0); + usb_dev_control (eop, &sp, NULL, NULL); } static void @@ -437,5 +442,5 @@ static void msc_bulk_reset(struct msc_class_info* mci) sp.value = 0; sp.index = mci->ui->iface_num; sp.length = 0; - usb_dev_setup (&mci->ui->dev->cfg_eop, true, &sp, NULL, 0); + usb_dev_control (&mci->ui->dev->cfg_eop, &sp, NULL, NULL); } diff --git a/src/devices/usb_uhci.c b/src/devices/usb_uhci.c index 1e5a64a..f92dd0e 100644 --- a/src/devices/usb_uhci.c +++ b/src/devices/usb_uhci.c @@ -8,7 +8,6 @@ #include #include #include -#include #include "threads/pte.h" #include "threads/malloc.h" #include "threads/palloc.h" @@ -76,17 +75,14 @@ #define ptr_to_flp(x) (((uintptr_t)x) >> 4) #define flp_to_ptr(x) (uintptr_t)(((uintptr_t)x) << 4) -/* frame structures */ +#define UHCI_LP_POINTER 0xfffffff0 /* Pointer field. */ +#define UHCI_LP_TERMINATE 0x00000001 /* 1=pointer not valid */ +#define UHCI_LP_QUEUE_HEAD 0x00000002 /* 1=points to queue head, + 0=points to tx descriptor. */ +#define UHCI_LP_DEPTH 0x00000004 /* Tx descriptor only: + 1=execute depth first, + 0=execute breadth first. */ #pragma pack(1) -struct frame_list_ptr -{ - uint32_t terminate:1; - uint32_t qh_select:1; - uint32_t depth_select:1; /* only for TD */ - uint32_t resv:1; /* zero */ - uint32_t flp:28; /* frame list pointer */ -}; - struct td_token { uint32_t pid:8; /* packet id */ @@ -126,20 +122,24 @@ struct td_control struct tx_descriptor { - struct frame_list_ptr flp; + uint32_t next_td; struct td_control control; struct td_token token; uint32_t buf_ptr; - - struct frame_list_ptr *head; /* for fast removal */ uint32_t flags; + struct list_elem td_elem; /* Element in queue_head's td_list. */ + size_t offset; }; struct queue_head { - struct frame_list_ptr qhlp; /* queue head link pointer */ - struct frame_list_ptr qelp; /* queue elem link pointer */ + uint32_t next_qh; /* Queue head link pointer. */ + uint32_t first_td; /* Queue element link pointer. */ + + struct list_elem qh_elem; /* Element in uhci_info's qh_list. */ + struct list td_list; /* List of tx_descriptor structures. */ + struct semaphore completion; /* Upped by interrupt on completion. */ }; #pragma pack() @@ -148,21 +148,12 @@ struct uhci_info struct pci_dev *dev; struct pci_io *io; /* pci io space */ struct lock lock; - struct frame_list_ptr *frame_list; /* page aligned frame list */ - - struct tx_descriptor *td_pool; - struct bitmap *td_used; - struct queue_head *qh_pool; - struct bitmap *qh_used; + uint32_t *frame_list; /* Page-aligned list of 1024 frame pointers. */ + struct list qh_list; /* List of queue_head structuress. */ + struct queue_head *term_qh; uint8_t num_ports; uint8_t attached_ports; - - int timeouts; /* number of timeouts */ - - struct semaphore td_sem; - struct list devices; /* devices on host */ - struct list waiting; /* threads waiting */ }; struct usb_wait @@ -181,7 +172,6 @@ struct uhci_dev_info int errors; /* aggregate errors */ struct list_elem peers; /* next dev on host */ struct lock lock; - struct queue_head *qh; }; struct uhci_eop_info @@ -200,29 +190,19 @@ struct uhci_eop_info static int token_to_pid (int token); -static int uhci_tx_pkt (host_eop_info eop, int token, void *pkt, - int min_sz, int max_sz, int *in_sz, bool wait); - +static int uhci_dev_control (host_eop_info, struct usb_setup_pkt *, + void *data, size_t *size); +static int uhci_dev_bulk (host_eop_info, bool out, void *data, size_t *size); static int uhci_detect_change (host_info); -static int uhci_tx_pkt_now (struct uhci_eop_info *ue, int token, void *pkt, - int sz); -static int uhci_tx_pkt_wait (struct uhci_eop_info *ue, int token, void *pkt, - int max_sz, int *in_sz); -static int uhci_tx_pkt_bulk (struct uhci_eop_info *ue, int token, void *buf, - int sz, int *tx); - +static struct queue_head *allocate_queue_head (void); +static struct tx_descriptor *allocate_transfer_descriptor (struct queue_head *); -static int uhci_process_completed (struct uhci_info *ui); -static struct tx_descriptor *uhci_acquire_td (struct uhci_info *); -static void uhci_release_td (struct uhci_info *, struct tx_descriptor *); -static void uhci_remove_qh (struct uhci_info *ui, struct queue_head *qh); +static void uhci_process_completed (struct uhci_info *ui); -static void qh_free (struct uhci_info *ui, struct queue_head *qh); -static struct queue_head *qh_alloc (struct uhci_info *ui); static struct uhci_info *uhci_create_info (struct pci_io *io); static void uhci_destroy_info (struct uhci_info *ui); @@ -232,31 +212,16 @@ static host_dev_info uhci_create_chan (host_info hi, int dev_addr, int ver); static void uhci_destroy_chan (host_dev_info); static void uhci_modify_chan (host_dev_info, int dev_addr, int ver); -static struct tx_descriptor *td_from_pool (struct uhci_info *ui, int idx); - static int check_and_flip_change (struct uhci_info *ui, int reg); -static void uhci_stop (struct uhci_info *ui); -static void uhci_run (struct uhci_info *ui); -static void uhci_stop_unlocked (struct uhci_info *ui); -static void uhci_run_unlocked (struct uhci_info *ui); #define uhci_is_stopped(x) (pci_reg_read16((x)->io, UHCI_REG_USBSTS) \ & USB_STATUS_HALTED) #define uhci_port_enabled(x, y) (pci_reg_read16((x)->io, (y)) & USB_PORT_ENABLE) -static void uhci_add_td_to_qh (struct queue_head *qh, - struct tx_descriptor *td); -static void uhci_remove_error_td (struct tx_descriptor *td); static void uhci_setup_td (struct tx_descriptor *td, int dev_addr, int token, int eop, void *pkt, int sz, int toggle, bool ls); static int uhci_enable_port (struct uhci_info *ui, int port); static void uhci_irq (void *uhci_data); static void uhci_detect_ports (struct uhci_info *ui); -static int uhci_remove_stalled (struct uhci_info *ui); -static void uhci_stall_watchdog (struct uhci_info *ui); - -static void dump_all_qh (struct uhci_info *ui); -static void dump_qh (struct queue_head *qh); - static void dump_regs (struct uhci_info *ui); void uhci_init (void); @@ -264,7 +229,8 @@ void uhci_init (void); static struct usb_host uhci_host = { .name = "UHCI", - .tx_pkt = uhci_tx_pkt, + .dev_control = uhci_dev_control, + .dev_bulk = uhci_dev_bulk, .detect_change = uhci_detect_change, .create_dev_channel = uhci_create_chan, .remove_dev_channel = uhci_destroy_chan, @@ -273,6 +239,14 @@ static struct usb_host uhci_host = { .remove_eop = uhci_remove_eop }; +static uint32_t +make_lp (void *p) +{ + uint32_t q = vtop (p); + ASSERT ((q & UHCI_LP_POINTER) == q); + return q; +} + void uhci_init (void) { @@ -285,7 +259,6 @@ uhci_init (void) { struct pci_io *io; struct uhci_info *ui; - uint8_t sof; int i; dev_num++; @@ -305,49 +278,44 @@ uhci_init (void) ui = uhci_create_info (io); ui->dev = pd; - sof = pci_reg_read8 (ui->io, UHCI_REG_SOFMOD); - uhci_detect_ports (ui); + //dump_regs (ui); - /* reset devices */ - pci_reg_write16 (ui->io, UHCI_REG_USBCMD, USB_CMD_GRESET); - timer_msleep (50); - pci_reg_write16 (ui->io, UHCI_REG_USBCMD, 0); - timer_msleep (1); + uhci_detect_ports (ui); - /* reset controller */ + pci_write_config16 (ui->dev, UHCI_REG_LEGSUP, 0x8f00); pci_reg_write16 (ui->io, UHCI_REG_USBCMD, USB_CMD_HCRESET); - timer_msleep (1); + asm volatile ("mfence":::"memory"); + timer_usleep (5); if (pci_reg_read16 (ui->io, UHCI_REG_USBCMD) & USB_CMD_HCRESET) - { - printf ("UHCI: Reset timed out\n"); - uhci_destroy_info (ui); - continue; - } + printf ("reset failed!\n"); pci_reg_write16 (ui->io, UHCI_REG_USBINTR, 0); pci_reg_write16 (ui->io, UHCI_REG_USBCMD, 0); - for (i = 0; i < ui->num_ports; i++) - pci_reg_write16 (ui->io, UHCI_REG_PORTSC1 + i * 2, 0); - - timer_msleep (100); - printf ("UHCI: Enabling %d root ports\n", ui->num_ports); - ui->attached_ports = 0; - for (i = 0; i < ui->num_ports; i++) - ui->attached_ports += uhci_enable_port (ui, i); + pci_reg_write16 (ui->io, UHCI_REG_PORTSC1, 0); + pci_reg_write16 (ui->io, UHCI_REG_PORTSC2, 0); - pci_reg_write8 (ui->io, UHCI_REG_SOFMOD, sof); + pci_reg_write8 (ui->io, UHCI_REG_SOFMOD, 64); + pci_reg_write32 (ui->io, UHCI_REG_FLBASEADD, make_lp (ui->frame_list)); pci_reg_write16 (ui->io, UHCI_REG_FRNUM, 0); - pci_reg_write32 (ui->io, UHCI_REG_FLBASEADD, vtop (ui->frame_list)); + asm volatile ("mfence":::"memory"); + + /* deactivate SMM junk, only enable IRQ */ + pci_write_config16 (ui->dev, UHCI_REG_LEGSUP, 0x2000); + + asm volatile ("mfence":::"memory"); + + pci_reg_write16 (ui->io, UHCI_REG_USBCMD, + USB_CMD_RS | USB_CMD_CONFIGURE | USB_CMD_MAX_PACKET); pci_reg_write16 (ui->io, UHCI_REG_USBINTR, USB_INTR_SHORT | USB_INTR_IOC | USB_INTR_TIMEOUT | USB_INTR_RESUME); - /* deactivate SMM junk, only enable IRQ */ - pci_write_config16 (ui->dev, UHCI_REG_LEGSUP, 0x2000); + asm volatile ("mfence":::"memory"); - uhci_lock (ui); - uhci_run (ui); - uhci_unlock (ui); + printf ("UHCI: Enabling %d root ports\n", ui->num_ports); + ui->attached_ports = 0; + for (i = 0; i < ui->num_ports; i++) + ui->attached_ports += uhci_enable_port (ui, i); pci_register_irq (pd, uhci_irq, ui); @@ -357,42 +325,75 @@ uhci_init (void) #define UHCI_PORT_TIMEOUT 1000 static int -uhci_enable_port (struct uhci_info *ui, int port) +uhci_enable_port (struct uhci_info *ui, int idx) { uint16_t status; + int time, stable_since; int count; + int port; - /* individual ports must be reset for QEMU to go into USB_STATE_DEFAULT */ - pci_reg_write16 (ui->io, UHCI_REG_PORTSC1 + port * 2, USB_PORT_RESET); - timer_msleep (50); + port = UHCI_REG_PORTSC1 + idx * 2; - /* ACK disconnect from reset so we can see if port is connected */ - pci_reg_write16 (ui->io, UHCI_REG_PORTSC1 + port * 2, - USB_PORT_ENABLE | USB_PORT_CONNECTCHG); - timer_msleep (10); - status = pci_reg_read16 (ui->io, UHCI_REG_PORTSC1 + port * 2); - if (!(status & USB_PORT_CONNECTSTATUS)) + status = 0xffff; + stable_since = 0; + for (time = 0; ; time += 25) { - return 0; + uint16_t new_status; + new_status = pci_reg_read16 (ui->io, port); + if (status != (new_status & USB_PORT_CONNECTSTATUS) + || new_status & USB_PORT_CONNECTCHG) + { + if (new_status & USB_PORT_CONNECTCHG) + pci_reg_write16 (ui->io, port, (new_status & ~0xe80a) | USB_PORT_CONNECTCHG); + stable_since = time; + status = new_status & USB_PORT_CONNECTSTATUS; + } + else if (time - stable_since >= 100) + break; + else if (time >= 1500) + return 0; + timer_msleep (25); } + //printf ("time=%d stable_since=%d\n", time, stable_since); + + if (!(status & USB_PORT_CONNECTSTATUS)) + return 0; - /* ACK CONNECTCHG status so port can be enabled */ - pci_reg_write16 (ui->io, UHCI_REG_PORTSC1 + port * 2, - USB_PORT_ENABLE | USB_PORT_CONNECTCHG); - for (count = 0; count < UHCI_PORT_TIMEOUT; count++) + for (count = 0; count < 3; count++) { - if (uhci_port_enabled (ui, UHCI_REG_PORTSC1 + port * 2)) - { - pci_reg_write16 (ui->io, UHCI_REG_PORTSC1 + port * 2, - USB_PORT_ENABLE | USB_PORT_CONNECTCHG); - return 1; - } + status = pci_reg_read16 (ui->io, port) & ~0xe80a; + //printf ("read %x, write %x, ", status, status | USB_PORT_RESET); + pci_reg_write16 (ui->io, port, status | USB_PORT_RESET); + timer_msleep (50); + + status = pci_reg_read16 (ui->io, port) & ~0xe80a; + //printf ("read %x, write %x, ", status, status & ~USB_PORT_RESET); + pci_reg_write16 (ui->io, port, status & ~USB_PORT_RESET); + timer_usleep (10); + + status = pci_reg_read16 (ui->io, port) & ~0xe80a; + //printf ("read %x, write %x, ", status, status & ~(USB_PORT_CONNECTCHG | USB_PORT_CHANGE)); + pci_reg_write16 (ui->io, port, status & ~(USB_PORT_CONNECTCHG | USB_PORT_CHANGE)); + + status = pci_reg_read16 (ui->io, port) & ~0xe80a; + //printf ("read %x, write %x, ", status, status | USB_PORT_ENABLE); + pci_reg_write16 (ui->io, port, status | USB_PORT_ENABLE); + + status = pci_reg_read16 (ui->io, port); + //printf ("read %x\n", status); + if (status & USB_PORT_ENABLE) + break; } - printf ("UHCI: Port %d enable timed out\n", port); - dump_regs (ui); + if (!(status & USB_PORT_CONNECTSTATUS)) + { + //printf ("port %d not connected\n", idx); + pci_reg_write16 (ui->io, port, 0); + return 0; + } - return 0; + //dump_regs (ui); + return 1; } @@ -404,14 +405,16 @@ dump_regs (struct uhci_info *ui) char *name[] = { "cmd", "sts", "intr", "frnum", "base", "sofmod", "portsc1", "portsc2" }; int i; - printf ("UHCI registers:\n"); + printf ("UHCI registers: "); for (i = 0; i < 8; i++) { - printf ("%s: %x\n", name[i], (sz[i] == 2) ? + if (i) + printf (", "); + printf ("%s: %x", name[i], (sz[i] == 2) ? pci_reg_read16 (ui->io, regs[i]) : pci_reg_read32 (ui->io, regs[i])); - } + printf (", legsup=%x\n", pci_read_config16 (ui->dev, UHCI_REG_LEGSUP)); } @@ -419,10 +422,6 @@ static void uhci_destroy_info (struct uhci_info *ui) { palloc_free_page (ui->frame_list); - palloc_free_page (ui->td_pool); - palloc_free_page (ui->qh_pool); - bitmap_destroy (ui->qh_used); - bitmap_destroy (ui->td_used); free (ui); } @@ -430,6 +429,8 @@ static struct uhci_info * uhci_create_info (struct pci_io *io) { struct uhci_info *ui; + struct queue_head *qh1, *qh2; + struct tx_descriptor *td; int i; ui = malloc (sizeof (struct uhci_info)); @@ -437,116 +438,329 @@ uhci_create_info (struct pci_io *io) ui->io = io; lock_init (&ui->lock); - /* create an empty schedule */ + /* Create initial queue head and put on qh list. */ + list_init (&ui->qh_list); + + qh1 = allocate_queue_head (); + list_push_back (&ui->qh_list, &qh1->qh_elem); + + ui->term_qh = qh2 = allocate_queue_head (); + qh1->next_qh = make_lp (qh2) | UHCI_LP_QUEUE_HEAD; + + td = allocate_transfer_descriptor (qh2); + td->token.maxlen = 0x7ff; + td->token.dev_addr = 0x7f; + td->token.pid = USB_TOKEN_IN; + + /* Create an empty schedule */ ui->frame_list = palloc_get_page (PAL_ASSERT | PAL_NOCACHE); - memset (ui->frame_list, 0, PGSIZE); for (i = 0; i < FRAME_LIST_ENTRIES; i++) - ui->frame_list[i].terminate = 1; + ui->frame_list[i] = make_lp (qh1) | UHCI_LP_QUEUE_HEAD; + //printf ("frame_list=%p: %08x\n", ui->frame_list, ui->frame_list[0]); - /* permit 3 timeouts */ - ui->timeouts = 3; // thread_create ("uhci watchdog", PRI_MIN, // (thread_func *) uhci_stall_watchdog, ui); - ui->td_pool = palloc_get_page (PAL_ASSERT | PAL_NOCACHE); - ui->td_used = bitmap_create (TD_ENTRIES); - ui->qh_pool = palloc_get_page (PAL_ASSERT | PAL_NOCACHE); - ui->qh_used = bitmap_create (QH_ENTRIES); - sema_init (&ui->td_sem, TD_ENTRIES); - - list_init (&ui->devices); - list_init (&ui->waiting); return ui; } +static void +print_td_list (struct queue_head *qh) +{ + struct list_elem *e; + + for (e = list_begin (&qh->td_list); e != list_end (&qh->td_list); + e = list_next (e)) + { + struct tx_descriptor *td = list_entry (e, struct tx_descriptor, td_elem); + printf (" %08x: next_td=%08x control=%08x %d,%d,%d,%d,%x buf_ptr=%08x\n", + vtop (td), td->next_td, *(uint32_t *) &td->control, + (td->token.maxlen + 1) & 0x7ff, td->token.data_toggle, + td->token.end_point, td->token.dev_addr, td->token.pid, + td->buf_ptr); + hex_dump (0, ptov (td->buf_ptr), (td->token.maxlen + 1) & 0x7ff, true); + } +} + +static void +print_qh_list (struct uhci_info *ui) +{ + struct list_elem *e; + + for (e = list_begin (&ui->qh_list); e != list_end (&ui->qh_list); + e = list_next (e)) + { + struct queue_head *qh = list_entry (e, struct queue_head, qh_elem); + printf ("%08x: next_qh=%08x first_td=%08x\n", + vtop (qh), qh->next_qh, qh->first_td); + print_td_list (qh); + } +} + +static struct tx_descriptor * +execute_qh (struct uhci_info *ui, struct queue_head *qh) +{ + struct queue_head *prev_qh; + struct tx_descriptor *last_td; + enum intr_level old_level; + + /* Mark the final tx descriptor to interrupt us when it's + done. */ + last_td = list_entry (list_back (&qh->td_list), struct tx_descriptor, td_elem); + last_td->control.ioc = 1; + + qh->next_qh = make_lp (ui->term_qh) | UHCI_LP_QUEUE_HEAD; + + /* Add the queue head to the list of queue heads. + qh_list is accessed from uhci_irq so we need to disable + interrupts. */ + old_level = intr_disable (); + list_push_back (&ui->qh_list, &qh->qh_elem); + prev_qh = list_entry (list_prev (&qh->qh_elem), struct queue_head, qh_elem); + prev_qh->next_qh = make_lp (qh) | UHCI_LP_QUEUE_HEAD; + print_qh_list (ui); + //dump_regs (ui); + intr_set_level (old_level); + + /* Wait until the queue has been processed. */ + //printf ("down..."); + sema_down (&qh->completion); + //printf ("up\n"); + + + /* Return the descriptor that failed, or a null pointer on + success. */ + return (qh->first_td & UHCI_LP_TERMINATE + ? NULL + : ptov (qh->first_td & UHCI_LP_POINTER)); +} static int -uhci_tx_pkt (host_eop_info hei, int token, void *pkt, int min_sz, - int max_sz, int *in_sz, bool wait) +uhci_dev_control (host_eop_info hei, struct usb_setup_pkt *setup, + void *data, size_t *size) { struct uhci_eop_info *ue; struct uhci_dev_info *ud; - - ASSERT (min_sz <= max_sz); - - /* can't have page overlap */ - if (pkt != NULL) - { - ASSERT (max_sz > 0); - ASSERT (pg_no (pkt + max_sz - 1) == pg_no (pkt)); - } + struct queue_head *qh; + struct tx_descriptor *td; + size_t size_alloced; + int status_token; + int err; ue = hei; ud = ue->ud; /* don't bother if ports are down */ if (ud->ui->attached_ports == 0) + return USB_HOST_ERR_NODEV; + + qh = allocate_queue_head (); + if (qh == NULL) + return USB_HOST_ERR_NOMEM; + + /* Setup stage: SETUP packet. */ + td = allocate_transfer_descriptor (qh); + if (td == NULL) + return USB_HOST_ERR_NOMEM; + ue->toggle = 0; + uhci_setup_td (td, ud->dev_addr, USB_TOKEN_SETUP, ue->eop, + setup, sizeof *setup, ue->toggle, ud->low_speed); + + /* Data stage: IN/OUT packets. */ + if (data != NULL) { - return USB_HOST_ERR_NODEV; + size_alloced = 0; + while (size_alloced < *size) + { + int packet = *size - size_alloced; + if (packet > ue->maxpkt) + packet = ue->maxpkt; + + td = allocate_transfer_descriptor (qh); + if (td == NULL) + return USB_HOST_ERR_NOMEM; + + ue->toggle = !ue->toggle; + uhci_setup_td (td, ud->dev_addr, + setup->direction ? USB_TOKEN_IN : USB_TOKEN_OUT, + ue->eop, data + size_alloced, packet, + ue->toggle, ud->low_speed); + td->control.spd = 1; + td->offset = size_alloced; + + size_alloced += packet; + } } + + /* Status stage: OUT/IN packet. */ + td = allocate_transfer_descriptor (qh); + if (td == NULL) + return USB_HOST_ERR_NOMEM; + ue->toggle = 1; + status_token = setup->direction ? USB_TOKEN_OUT : USB_TOKEN_IN; + uhci_setup_td (td, ud->dev_addr, status_token, ue->eop, + NULL, 0, ue->toggle, ud->low_speed); + + /* Execute. */ + td = execute_qh (ud->ui, qh); + if (td != NULL) + ue->toggle = td->token.data_toggle; + //printf ("execution done, td=%p\n", td); + if (td != NULL + && setup->direction + && list_front (&qh->td_list) != &td->td_elem + && list_back (&qh->td_list) != &td->td_elem + && !td->control.active + && !td->control.stalled + && !td->control.buffer_error + && !td->control.babble + && !td->control.nak + && !td->control.timeout + && !td->control.bitstuff + && (((td->control.actual_len + 1) & 0x7ff) + < ((td->token.maxlen + 1) & 0x7ff))) + { + /* Short packet detected in stream, which indicates that + the input data is shorter than the maximum that we + expected. - /* setup token acts to synchronize data toggle */ - if (token == USB_TOKEN_SETUP) - ue->toggle = 0; + Re-queue the Status stage. */ - if (min_sz != 0) - { - return uhci_tx_pkt_bulk (ue, token, pkt, max_sz, in_sz); + *size = td->offset + ((td->control.actual_len + 1) & 0x7ff); + + //delayed_free_qh (qh); + + qh = allocate_queue_head (); + if (qh == NULL) + return USB_HOST_ERR_NOMEM; + + td = allocate_transfer_descriptor (qh); + if (td == NULL) + return USB_HOST_ERR_NOMEM; + ue->toggle = 1; + uhci_setup_td (td, ud->dev_addr, status_token, ue->eop, + NULL, 0, ue->toggle, ud->low_speed); + + //printf ("short packet, requeuing status\n"); + td = execute_qh (ud->ui, qh); + if (td != NULL) + ue->toggle = td->token.data_toggle; } - else + + if (td != NULL) { - if (wait == false) - { - if (in_sz != NULL) - *in_sz = max_sz; - return uhci_tx_pkt_now (ue, token, pkt, max_sz); - } + if (td->control.bitstuff) + err = USB_HOST_ERR_BITSTUFF; + else if (td->control.timeout) + err = USB_HOST_ERR_TIMEOUT; + else if (td->control.nak) + err = USB_HOST_ERR_NAK; + else if (td->control.babble) + err = USB_HOST_ERR_BABBLE; + else if (td->control.buffer_error) + err = USB_HOST_ERR_BUFFER; + else if (td->control.stalled) + err = USB_HOST_ERR_STALL; else - { - return uhci_tx_pkt_wait (ue, token, pkt, max_sz, in_sz); - } + PANIC ("unknown USB error"); } + else + err = USB_HOST_ERR_NONE; - return 0; + //delayed_free_qh (qh); + + //printf ("err=%d\n", err); + if (err == 0 && data != NULL) + hex_dump (0, data, *size, true); + return err; } static int -uhci_tx_pkt_bulk (struct uhci_eop_info *ue, int token, void *buf, - int sz, int *tx) +uhci_dev_bulk (host_eop_info hei, bool out, void *data, size_t *size) { - /* XXX this can be made to use async packets */ - int bytes = 0; - int txed = 0; + struct uhci_eop_info *ue; + struct uhci_dev_info *ud; + struct queue_head *qh; + struct tx_descriptor *td; + size_t size_alloced; + int err; - /* send data in max_pkt sized chunks */ - while (bytes < sz) + ue = hei; + ud = ue->ud; + + /* don't bother if ports are down */ + if (ud->ui->attached_ports == 0) + return USB_HOST_ERR_NODEV; + + if (!out) + memset (data, 0xcc, *size); + + qh = allocate_queue_head (); + if (qh == NULL) + return USB_HOST_ERR_NOMEM; + + /* Data stage: IN/OUT packets. */ + size_alloced = 0; + while (size_alloced < *size) { - int to_tx, pkt_txed; - int left; - int err; - bool wait_on_pkt; - - left = sz - bytes; - to_tx = (left > ue->maxpkt) ? ue->maxpkt : left; - wait_on_pkt = (left <= to_tx) ? true : false; - - pkt_txed = 0; - err = uhci_tx_pkt (ue, token, buf + bytes, 0, to_tx, &pkt_txed, - wait_on_pkt); - if (err) - { - if (tx != NULL) - *tx = txed; - return err; - } - txed += pkt_txed; - bytes += pkt_txed; + int packet = *size - size_alloced; + if (packet > ue->maxpkt) + packet = ue->maxpkt; + + td = allocate_transfer_descriptor (qh); + if (td == NULL) + return USB_HOST_ERR_NOMEM; + + uhci_setup_td (td, ud->dev_addr, + out ? USB_TOKEN_OUT : USB_TOKEN_IN, + ue->eop, data + size_alloced, packet, + ue->toggle, ud->low_speed); + ue->toggle = !ue->toggle; + td->control.spd = 1; + td->offset = size_alloced; + + size_alloced += packet; + } + + /* Execute. */ + td = execute_qh (ud->ui, qh); + if (td != NULL) + { + ue->toggle = !td->token.data_toggle; + if (!out) + *size = td->offset + ((td->control.actual_len + 1) & 0x7ff); + + if (td->control.bitstuff) + err = USB_HOST_ERR_BITSTUFF; + else if (td->control.timeout) + err = USB_HOST_ERR_TIMEOUT; + else if (td->control.nak) + err = USB_HOST_ERR_NAK; + else if (td->control.babble) + err = USB_HOST_ERR_BABBLE; + else if (td->control.buffer_error) + err = USB_HOST_ERR_BUFFER; + else if (td->control.stalled) + err = USB_HOST_ERR_STALL; + else if (!out) + { + /* Just a short packet. */ + printf ("short packet\n"); + err = USB_HOST_ERR_NONE; + } + else + PANIC ("unknown USB error"); } + else + err = USB_HOST_ERR_NONE; - if (tx != NULL) - *tx = txed; + //delayed_free_qh (qh); - return USB_HOST_ERR_NONE; + if (err) + PANIC ("err=%d\n", err); + if (err == 0 && data != NULL && !out) + hex_dump (0, data, *size, true); + return err; } static int @@ -567,7 +781,7 @@ token_to_pid (int token) static void uhci_setup_td (struct tx_descriptor *td, int dev_addr, int token, - int eop, void *pkt, int sz, int toggle, bool ls) + int eop, void *pkt, int sz, int toggle, bool ls UNUSED) { td->buf_ptr = (sz == 0) ? 0 : vtop (pkt); @@ -580,263 +794,100 @@ uhci_setup_td (struct tx_descriptor *td, int dev_addr, int token, td->control.actual_len = 0; td->control.active = 1; - td->flp.qh_select = 0; - td->flp.depth_select = 0; + td->next_td = UHCI_LP_TERMINATE; /* kill packet if too many errors */ td->control.error_limit = 3; } -static int -uhci_tx_pkt_now (struct uhci_eop_info *ue, int token, void *pkt, int sz) -{ - struct tx_descriptor *td; - struct uhci_dev_info *ud; - - ud = ue->ud; - - uhci_lock (ud->ui); - - td = uhci_acquire_td (ud->ui); - memset (td, 0, sizeof (struct tx_descriptor)); - uhci_setup_td (td, ud->dev_addr, token, ue->eop, pkt, sz, ue->toggle, - ud->low_speed); - td->control.ioc = 1; - - uhci_stop (ud->ui); - - uhci_add_td_to_qh (ud->qh, td); - td->flags = TD_FL_ASYNC | TD_FL_USED; - - uhci_run (ud->ui); - uhci_unlock (ud->ui); - - ue->toggle ^= 1; - return USB_HOST_ERR_NONE; -} - -static int -uhci_tx_pkt_wait (struct uhci_eop_info *ue, int token, void *pkt, - int max_sz, int *in_sz) -{ - enum intr_level old_lvl; - struct tx_descriptor *td; - struct usb_wait w; - int err; - struct uhci_dev_info *ud; - - ud = ue->ud; - - uhci_lock (ud->ui); - - td = uhci_acquire_td (ud->ui); - memset (td, 0, sizeof (struct tx_descriptor)); - - uhci_setup_td (td, ud->dev_addr, token, ue->eop, pkt, max_sz, ue->toggle, - ud->low_speed); - td->control.ioc = 1; - - w.td = td; - w.ud = ud; - sema_init (&w.sem, 0); - - uhci_stop (ud->ui); - - /* put into device's queue and add to waiting packet list */ - uhci_add_td_to_qh (ud->qh, td); - td->flags = TD_FL_USED; - - list_push_back (&ud->ui->waiting, &w.peers); - - /* reactivate controller and wait */ - old_lvl = intr_disable (); - uhci_run (ud->ui); - uhci_unlock (ud->ui); - sema_down (&w.sem); - intr_set_level (old_lvl); - - if (in_sz != NULL) - { - if (w.td->control.actual_len == 0x7ff) - *in_sz = 0; - else - *in_sz = w.td->control.actual_len + 1; - } - - if (w.td->control.bitstuff) - err = USB_HOST_ERR_BITSTUFF; - else if (w.td->control.timeout) - err = USB_HOST_ERR_TIMEOUT; - else if (w.td->control.nak) - err = USB_HOST_ERR_NAK; - else if (w.td->control.babble) - err = USB_HOST_ERR_BABBLE; - else if (w.td->control.buffer_error) - err = USB_HOST_ERR_BUFFER; - else if (w.td->control.stalled) - err = USB_HOST_ERR_STALL; - else - { - err = USB_HOST_ERR_NONE; - ue->toggle ^= 1; - } - - uhci_release_td (ud->ui, td); - - return err; -} - -static void -uhci_add_td_to_qh (struct queue_head *qh, struct tx_descriptor *td) -{ - struct frame_list_ptr *fp; - - ASSERT (td != NULL); - - td->head = &qh->qelp; - if (qh->qelp.terminate == 1) - { - /* queue is empty */ - td->flp.terminate = 1; - barrier (); - td->flp.flp = 0; - qh->qelp.flp = ptr_to_flp (vtop (td)); - qh->qelp.terminate = 0; - } - else - { - /* find the last element in the queue */ - fp = ptov (flp_to_ptr (qh->qelp.flp)); - ASSERT (qh->qelp.terminate == 0); - while (!fp->terminate) - { - fp = ptov (flp_to_ptr (fp->flp)); - } - - /* set TD to terminated ptr */ - td->flp = *fp; - - fp->qh_select = 0; - fp->depth_select = 0; - fp->flp = ptr_to_flp (vtop (td)); - barrier (); - fp->terminate = 0; - } -} - static void uhci_irq (void *uhci_data) { struct uhci_info *ui; uint16_t status; + //printf ("uhci_irq\n"); ui = uhci_data; status = pci_reg_read16 (ui->io, UHCI_REG_USBSTS); if (status & USB_STATUS_PROCESSERR) { - dump_all_qh (ui); + //dump_all_qh (ui); dump_regs (ui); PANIC ("UHCI: Malformed schedule"); } else if (status & USB_STATUS_HOSTERR) { - dump_all_qh (ui); + //dump_all_qh (ui); dump_regs (ui); PANIC ("UHCI: Host system error"); } - else if (status & USB_STATUS_INTERR) + else if (status & (USB_STATUS_INTERR | USB_STATUS_USBINT)) { /* errors */ - pci_reg_write16 (ui->io, UHCI_REG_USBSTS, USB_STATUS_INTERR); - } - - if (status & USB_STATUS_USBINT) - { - /* turn off interrupt */ - uhci_stop_unlocked (ui); - pci_reg_write16 (ui->io, UHCI_REG_USBSTS, USB_STATUS_USBINT); + //pci_reg_write16 (ui->io, UHCI_REG_USBINTR, 0); + pci_reg_write16 (ui->io, UHCI_REG_USBSTS, + status & (USB_STATUS_INTERR | USB_STATUS_USBINT)); + if (status & USB_STATUS_INTERR) + { + printf ("USB_STATUS_INTERR\n"); + print_qh_list (ui); + dump_regs (ui); + } + //if (status & USB_STATUS_USBINT) + //printf ("USB_STATUS_USBINT\n"); + barrier (); uhci_process_completed (ui); - uhci_run_unlocked (ui); } + else + printf ("nothing?\n"); } -static int -uhci_process_completed (struct uhci_info *ui) +/* Queue processing finished. + Remove from lists, wake up waiter. */ +static void +finish_qh (struct queue_head *qh) { - struct list_elem *li; - int completed = 0; - size_t start = 0; - - li = list_begin (&ui->waiting); - while (li != list_end (&ui->waiting)) - { - struct usb_wait *uw; - struct list_elem *next; - - next = list_next (li); - uw = list_entry (li, struct usb_wait, peers); - - if (!uw->td->control.active) - { - list_remove (li); - if (uw->td->control.error_limit == 0 || uw->td->control.stalled) - { - uhci_remove_error_td (uw->td); - } - uw->td->flags = 0; - sema_up (&uw->sem); - completed++; - } - li = next; - } - - /* must be a completed async TD.. */ - /* is this too time consuming? I hope not */ - if (completed != 0) - return completed; - - while (start < TD_ENTRIES) - { - struct tx_descriptor *td; + struct list_elem *p = list_prev (&qh->qh_elem); + struct queue_head *prev_qh = list_entry (p, struct queue_head, qh_elem); - start = bitmap_scan (ui->td_used, start, 1, true); - if (start == BITMAP_ERROR) - break; - - td = td_from_pool (ui, start); - - if (!td->control.active && (td->flags & TD_FL_ASYNC) && - (td->flags & TD_FL_USED)) - { - if (td->control.error_limit == 0 || td->control.stalled) - { - uhci_remove_error_td (td); - } - uhci_release_td (ui, td); - completed++; - } - start++; - } - - return completed; + //printf ("finish_qh\n"); + prev_qh->next_qh = qh->next_qh; + list_remove (&qh->qh_elem); + sema_up (&qh->completion); } static void -uhci_remove_error_td (struct tx_descriptor *td) +uhci_process_completed (struct uhci_info *ui) { - struct frame_list_ptr *fp; - uint32_t td_flp; + struct list_elem *e; - ASSERT (td->head != NULL); + if (list_empty (&ui->qh_list)) + return; - fp = td->head; - td_flp = ptr_to_flp (vtop (td)); - while (fp->flp != td_flp) + /* Note that we skip the first element in the list, which is an + empty queue head pointed to by every frame list pointer. */ + for (e = list_next (list_begin (&ui->qh_list)); + e != list_end (&ui->qh_list); ) { - ASSERT (fp->terminate == 0); - fp = ptov (flp_to_ptr (fp->flp)); + struct queue_head *qh = list_entry (e, struct queue_head, qh_elem); + uint32_t first_td = qh->first_td; + bool delete = false; + + barrier (); + //printf ("first_td=%08x\n", first_td); + if ((first_td & UHCI_LP_POINTER) == 0) + delete = true; + else + { + struct tx_descriptor *td = ptov (first_td & UHCI_LP_POINTER); + if (!td->control.active) + delete = true; + } + + e = list_next (e); + if (delete) + finish_qh (qh); } - *fp = td->flp; } static int @@ -877,206 +928,72 @@ check_and_flip_change (struct uhci_info *ui, int port) return 0; } -static host_dev_info -uhci_create_chan (host_info hi, int dev_addr, int ver) +static struct queue_head * +allocate_queue_head (void) { - struct uhci_info *ui; - struct uhci_dev_info *ud; - int i; - - ASSERT (dev_addr <= 127 && dev_addr >= 0); - - ui = hi; - - ud = malloc (sizeof (struct uhci_dev_info)); - ud->dev_addr = dev_addr; - ud->low_speed = (ver == USB_VERSION_1_0) ? true : false; - - ud->errors = 0; - ud->ui = ui; - lock_init (&ud->lock); - - uhci_lock (ui); - - ud->qh = qh_alloc (ud->ui); - - /* queue data */ - memset (ud->qh, 0, sizeof (*ud->qh)); - ud->qh->qelp.terminate = 1; - barrier (); - ud->qh->qelp.flp = 0; - ud->qh->qelp.qh_select = 0; - ud->qh->qhlp.qh_select = 1; - - uhci_stop (ui); - - /* add to queues in frame list */ - ud->qh->qhlp.flp = ui->frame_list[0].flp; - ud->qh->qhlp.terminate = ui->frame_list[0].terminate; - for (i = 0; i < FRAME_LIST_ENTRIES; i++) + size_t qh_size = sizeof *allocate_queue_head (); + struct queue_head *qh = calloc (1, qh_size > 16 ? qh_size : 16); + if (qh != NULL) { - ui->frame_list[i].flp = ptr_to_flp (vtop (ud->qh)); - ui->frame_list[i].qh_select = 1; - ui->frame_list[i].terminate = 0; + qh->next_qh = UHCI_LP_TERMINATE; + qh->first_td = UHCI_LP_TERMINATE; + list_init (&qh->td_list); + sema_init (&qh->completion, 0); } - - /* add to device list */ - list_push_back (&ui->devices, &ud->peers); - - uhci_run (ui); - uhci_unlock (ui); - - return ud; + return qh; } -static void -uhci_destroy_chan (host_dev_info hd) +static struct tx_descriptor * +allocate_transfer_descriptor (struct queue_head *qh) { - struct uhci_dev_info *ud; - struct list_elem *li; - - ud = hd; - uhci_lock (ud->ui); - - uhci_stop (ud->ui); - - uhci_remove_qh (ud->ui, ud->qh); - - /* wake up all waiting */ - li = list_begin (&ud->ui->waiting); - while (li != list_end (&ud->ui->waiting)) + struct tx_descriptor *td = calloc (1, sizeof *td); + if (td == NULL) { - struct usb_wait *w; - w = list_entry (li, struct usb_wait, peers); - if (w->ud == ud) - { - sema_up (&w->sem); - list_remove (li); - } - li = list_next (li); + /* FIXME: free the whole queue head. */ + return NULL; } - list_remove (&ud->peers); - - uhci_run (ud->ui); - - qh_free (ud->ui, ud->qh); - - uhci_unlock (ud->ui); - - free (ud); -} - -/** - * Remove a queue from the UHCI schedule - */ -static void -uhci_remove_qh (struct uhci_info *ui, struct queue_head *qh) -{ - uintptr_t qh_flp; - - ASSERT (lock_held_by_current_thread (&ui->lock)); - ASSERT (uhci_is_stopped (ui)); - ASSERT (qh != NULL); - - qh_flp = ptr_to_flp (vtop (qh)); - /* remove from host queue */ - if (ui->frame_list[0].flp == qh_flp) - { - int i; - /* up top */ - for (i = 0; i < FRAME_LIST_ENTRIES; i++) - { - ui->frame_list[i] = qh->qhlp; - } - } + if (list_empty (&qh->td_list)) + qh->first_td = make_lp (td); else { - /* in the middle */ - struct frame_list_ptr *fp; - struct frame_list_ptr *prev; - - fp = ptov (flp_to_ptr (ui->frame_list[0].flp)); - ASSERT (!fp->terminate); - do - { - prev = fp; - fp = ptov (flp_to_ptr (fp->flp)); - } - while (!fp->terminate && fp->flp != qh_flp); - *prev = qh->qhlp; + struct list_elem *p = list_back (&qh->td_list); + struct tx_descriptor *prev_td = list_entry (p, struct tx_descriptor, + td_elem); + prev_td->next_td = make_lp (td); } + list_push_back (&qh->td_list, &td->td_elem); + td->next_td = UHCI_LP_TERMINATE; + return td; } -/** - * Put UHCI into stop state - * Wait until status register reflects setting - */ -static void -uhci_stop (struct uhci_info *ui) -{ - ASSERT (intr_get_level () != INTR_OFF); - ASSERT (lock_held_by_current_thread (&ui->lock)); - - uhci_stop_unlocked (ui); -} - -static void -uhci_stop_unlocked (struct uhci_info *ui) +static host_dev_info +uhci_create_chan (host_info hi, int dev_addr, int ver) { - uint16_t cmd; - int i; - - cmd = pci_reg_read16 (ui->io, UHCI_REG_USBCMD); - cmd = cmd & ~USB_CMD_RS; + struct uhci_info *ui; + struct uhci_dev_info *ud; - pci_reg_write16 (ui->io, UHCI_REG_USBCMD, cmd); + ASSERT (dev_addr <= 127 && dev_addr >= 0); - /* wait for execution schedule to finish up */ - for (i = 0; i < 1000; i++) - { - if (uhci_is_stopped (ui)) - return; - } + ui = hi; - PANIC ("UHCI: Controller did not halt\n"); + ud = malloc (sizeof (struct uhci_dev_info)); + ud->dev_addr = dev_addr; + ud->low_speed = (ver == USB_VERSION_1_0) ? true : false; -} + ud->errors = 0; + ud->ui = ui; + lock_init (&ud->lock); -static void -uhci_run_unlocked (struct uhci_info *ui) -{ - uint16_t cmd; - cmd = pci_reg_read16 (ui->io, UHCI_REG_USBCMD); - cmd = cmd | USB_CMD_RS | USB_CMD_MAX_PACKET; - pci_reg_write16 (ui->io, UHCI_REG_USBCMD, cmd); + return ud; } -/** - * Put UHCI into 'Run' State - */ static void -uhci_run (struct uhci_info *ui) +uhci_destroy_chan (host_dev_info hd UNUSED) { - ASSERT (lock_held_by_current_thread (&ui->lock)); - uhci_run_unlocked (ui); -} - -static struct tx_descriptor * -uhci_acquire_td (struct uhci_info *ui) -{ - size_t td_idx; - struct tx_descriptor *td; - - ASSERT (lock_held_by_current_thread (&ui->lock)); - ASSERT (!uhci_is_stopped (ui)); - - sema_down (&ui->td_sem); - td_idx = bitmap_scan_and_flip (ui->td_used, 0, 1, false); - ASSERT (td_idx != BITMAP_ERROR); - td = td_from_pool (ui, td_idx); - - return td; + /* FIXME */ + /* FIXME: wake up all waiting queue heads. */ + /* FIXME: arrange for qhs to be freed. */ } static void @@ -1089,44 +1006,6 @@ uhci_modify_chan (host_dev_info hd, int dev_addr, int ver) ud->low_speed = (ver == USB_VERSION_1_0) ? true : false; } -static void -dump_all_qh (struct uhci_info *ui) -{ - struct list_elem *li; - - printf ("schedule: %x...", vtop (ui->frame_list)); - printf ("%x", *((uint32_t *) ui->frame_list)); - li = list_begin (&ui->devices); - while (li != list_end (&ui->devices)) - { - struct uhci_dev_info *ud; - ud = list_entry (li, struct uhci_dev_info, peers); - dump_qh (ud->qh); - li = list_next (li); - } -} - -static void -dump_qh (struct queue_head *qh) -{ - struct frame_list_ptr *fp; - printf ("qh: %p %x\n", qh, vtop (qh)); - fp = &qh->qelp; - while (!fp->terminate) - { - printf ("%x %x\n", *((uint32_t *) fp), *(uint32_t *) (fp + 1)); - fp = ptov (flp_to_ptr (fp->flp)); - } - printf ("%x %x\n\n", *(uint32_t *) fp, *(uint32_t *) (fp + 1)); -} - -static struct tx_descriptor * -td_from_pool (struct uhci_info *ui, int idx) -{ - ASSERT (idx >= 0 && idx < TD_ENTRIES); - return (((void *) ui->td_pool) + idx * 32); -} - static void uhci_detect_ports (struct uhci_info *ui) { @@ -1142,75 +1021,6 @@ uhci_detect_ports (struct uhci_info *ui) } } -static void -uhci_stall_watchdog (struct uhci_info *ui) -{ - while (1) - { - int rmved; - timer_msleep (1000); - printf ("watchdog\n"); - uhci_lock (ui); - uhci_stop (ui); - rmved = uhci_remove_stalled (ui); - if (rmved > 0) - printf ("removed stalled packet in watchdog\n"); - uhci_run (ui); - uhci_unlock (ui); - } -} - -static int -uhci_remove_stalled (struct uhci_info *ui) -{ - struct list_elem *li; - int rmved; - - rmved = 0; - li = list_begin (&ui->waiting); - - intr_disable (); - - while (li != list_end (&ui->waiting)) - { - struct usb_wait *uw; - struct list_elem *next; - uint32_t ctrl; - - next = list_next (li); - uw = list_entry (li, struct usb_wait, peers); - - if ((!uw->td->control.active && uw->td->control.stalled) || - (uw->td->control.nak)) - { - memcpy (&ctrl, &uw->td->control, 4); - printf ("CTRL: %x\n", ctrl); - list_remove (li); - uhci_remove_error_td (uw->td); - sema_up (&uw->sem); - rmved++; - } - li = next; - } - - intr_enable (); - - return rmved; -} - -static void -uhci_release_td (struct uhci_info *ui, struct tx_descriptor *td) -{ - int ofs = (uintptr_t) td - (uintptr_t) ui->td_pool; - int entry = ofs / 32; - - ASSERT (entry < TD_ENTRIES); - - td->flags = 0; - bitmap_reset (ui->td_used, entry); - sema_up (&ui->td_sem); -} - static host_eop_info uhci_create_eop (host_dev_info hd, int eop, int maxpkt) { @@ -1233,31 +1043,3 @@ uhci_remove_eop (host_eop_info hei) { free (hei); } - -static struct queue_head * -qh_alloc (struct uhci_info *ui) -{ - size_t qh_idx; - struct queue_head *qh; - - ASSERT (lock_held_by_current_thread (&ui->lock)); - - qh_idx = bitmap_scan_and_flip (ui->qh_used, 0, 1, false); - if (qh_idx == BITMAP_ERROR) - { - PANIC ("UHCI: Too many queue heads in use-- runaway USB stack?\n"); - } - qh = (void *) (((intptr_t) ui->qh_pool) + qh_idx * 16); - - return qh; -} - -static void -qh_free (struct uhci_info *ui, struct queue_head *qh) -{ - size_t entry; - ASSERT (lock_held_by_current_thread (&ui->lock)); - - entry = ((intptr_t) qh - (intptr_t) ui->qh_pool) / 16; - bitmap_reset (ui->qh_used, entry); -} diff --git a/src/threads/malloc.c b/src/threads/malloc.c index f6f803b..e41baa7 100644 --- a/src/threads/malloc.c +++ b/src/threads/malloc.c @@ -20,8 +20,10 @@ Otherwise, a new page of memory, called an "arena", is obtained from the page allocator (if none is available, malloc() returns a null pointer). The new arena is divided - into blocks, all of which are added to the descriptor's free - list. Then we return one of the new blocks. + into blocks. The first block in the page is reserved for + arena bookkeeping data (struct arena). The remaining blocks + are added to the descriptor's free list. Then we return one + of the new blocks. When we free a block, we add it to its descriptor's free list. But if the arena that the block was in now has no in-use @@ -61,7 +63,7 @@ struct block }; /* Our set of descriptors. */ -static struct desc descs[10]; /* Descriptors. */ +static struct desc descs[11]; /* Descriptors. */ static size_t desc_cnt; /* Number of descriptors. */ static struct arena *block_to_arena (struct block *); @@ -73,19 +75,25 @@ malloc_init (void) { size_t block_size; - for (block_size = 16; block_size < PGSIZE / 2; block_size *= 2) + for (block_size = 16; block_size <= PGSIZE / 2; block_size *= 2) { struct desc *d = &descs[desc_cnt++]; ASSERT (desc_cnt <= sizeof descs / sizeof *descs); + ASSERT (block_size >= sizeof (struct arena)); d->block_size = block_size; - d->blocks_per_arena = (PGSIZE - sizeof (struct arena)) / block_size; + d->blocks_per_arena = (PGSIZE / block_size) - 1; list_init (&d->free_list); lock_init (&d->lock); } } /* Obtains and returns a new block of at least SIZE bytes. - Returns a null pointer if memory is not available. */ + Returns a null pointer if memory is not available. + + If SIZE is PGSIZE / 2 or less, the returned block is + guaranteed to be aligned on the least power-of-2 boundary + greater than or equal to SIZE. (Hence, such a block never + crosses the boundary between two physical pages.) */ void * malloc (size_t size) { @@ -174,7 +182,9 @@ calloc (size_t a, size_t b) return p; } -/* Returns the number of bytes allocated for BLOCK. */ +/* Returns the number of bytes allocated for BLOCK. If the + return value is PGSIZE / 2 or less, the block is also + guaranteed to be aligned to that power-of-2 boundary. */ static size_t block_size (void *block) { @@ -274,9 +284,10 @@ block_to_arena (struct block *b) ASSERT (a->magic == ARENA_MAGIC); /* Check that the block is properly aligned for the arena. */ - ASSERT (a->desc == NULL - || (pg_ofs (b) - sizeof *a) % a->desc->block_size == 0); - ASSERT (a->desc != NULL || pg_ofs (b) == sizeof *a); + ASSERT (a->desc != NULL + ? pg_ofs (b) % a->desc->block_size == 0 + : pg_ofs (b) == sizeof *a); + ASSERT (pg_ofs (b) != 0); return a; } @@ -288,7 +299,5 @@ arena_to_block (struct arena *a, size_t idx) ASSERT (a != NULL); ASSERT (a->magic == ARENA_MAGIC); ASSERT (idx < a->desc->blocks_per_arena); - return (struct block *) ((uint8_t *) a - + sizeof *a - + idx * a->desc->block_size); + return (struct block *) ((uint8_t *) a + (idx + 1) * a->desc->block_size); } -- 2.30.2