From 34787305df83999507efdab0cc9279de8f243f47 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Mon, 19 Jul 2010 08:43:01 +0200 Subject: [PATCH] Allow psartial transfers and use them for usbserial --- bus/usb/ohci.c | 23 ++++++++++++++--------- bus/usb/serial/common.c | 23 +++++++++++++++++++++++ bus/usb/serial/ftdi.c | 9 +-------- bus/usb/serial/pl2303.c | 19 +------------------ bus/usb/uhci.c | 23 ++++++++++++++++------- bus/usb/usbtrans.c | 39 ++++++++++++++++++++++++++++----------- include/grub/serial.h | 4 ++-- include/grub/usb.h | 8 ++++---- include/grub/usbserial.h | 3 +++ include/grub/usbtrans.h | 1 + 10 files changed, 93 insertions(+), 59 deletions(-) diff --git a/bus/usb/ohci.c b/bus/usb/ohci.c index 73e3e01e8..d8abb343a 100644 --- a/bus/usb/ohci.c +++ b/bus/usb/ohci.c @@ -654,7 +654,8 @@ grub_ohci_transaction (grub_ohci_td_t td, static grub_usb_err_t grub_ohci_transfer (grub_usb_controller_t dev, - grub_usb_transfer_t transfer, int timeout) + grub_usb_transfer_t transfer, int timeout, + grub_size_t *actual) { struct grub_ohci *o = (struct grub_ohci *) dev->data; grub_ohci_ed_t ed_virt; @@ -680,6 +681,8 @@ grub_ohci_transfer (grub_usb_controller_t dev, int err_unrec = 0; grub_uint32_t intstatus; + *actual = 0; + /* Pre-set target for ED - we need it to find proper ED */ /* Set the device address. */ target = transfer->devaddr; @@ -1078,12 +1081,14 @@ grub_ohci_transfer (grub_usb_controller_t dev, case 9: /* XXX: Data underrun error. */ - err = GRUB_USB_ERR_DATA; grub_dprintf ("ohci", "Underrun, failed TD address: %p, index: %d\n", tderr_virt, tderr_virt->tr_index); - grub_dprintf ("ohci", "Underrun, number of not transferred bytes: %d\n", - 1 + grub_le_to_cpu32 (tderr_virt->buffer_end) - - grub_le_to_cpu32 (tderr_virt->buffer)); + if (transfer->last_trans == -1) + break; + *actual = transfer->transactions[transfer->last_trans].size + - (grub_le_to_cpu32 (tderr_virt->buffer_end) + - grub_le_to_cpu32 (tderr_virt->buffer)) + + transfer->transactions[transfer->last_trans].preceding; break; case 10: @@ -1172,12 +1177,12 @@ grub_ohci_transfer (grub_usb_controller_t dev, transfer->last_trans = tderr_virt->tr_index; else transfer->last_trans = -1; - } + else + *actual = transfer->size; - /* Set empty ED - set HEAD = TAIL = last (not processed) TD */ - ed_virt->td_head = grub_cpu_to_le32 ( grub_le_to_cpu32 ( - ed_virt->td_tail) & ~0xf); + /* Set empty ED - set HEAD = TAIL = last (not processed) TD */ + ed_virt->td_head = grub_cpu_to_le32 (grub_le_to_cpu32 (ed_virt->td_tail) & ~0xf); /* At this point always should be: * ED has skip bit set and halted or empty or after next SOF, diff --git a/bus/usb/serial/common.c b/bus/usb/serial/common.c index 1da1c5bcf..6b5b90059 100644 --- a/bus/usb/serial/common.c +++ b/bus/usb/serial/common.c @@ -100,3 +100,26 @@ grub_usbserial_attach (grub_usb_device_t usbdev, int configno, int interfno, return 1; } + +int +grub_usbserial_fetch (struct grub_serial_port *port, grub_size_t header_size) +{ + grub_usb_err_t err; + grub_size_t actual; + + if (port->bufstart < port->bufend) + return port->buf[port->bufstart++]; + + err = grub_usb_bulk_read_extended (port->usbdev, port->in_endp->endp_addr, + sizeof (port->buf), port->buf, 10, + &actual); + if (err != GRUB_USB_ERR_NONE) + return -1; + + port->bufstart = header_size; + port->bufend = actual; + if (port->bufstart >= port->bufend) + return -1; + + return port->buf[port->bufstart++]; +} diff --git a/bus/usb/serial/ftdi.c b/bus/usb/serial/ftdi.c index a4d88dbae..bd1713b27 100644 --- a/bus/usb/serial/ftdi.c +++ b/bus/usb/serial/ftdi.c @@ -111,16 +111,9 @@ real_config (struct grub_serial_port *port) static int ftdi_hw_fetch (struct grub_serial_port *port) { - char cc[3]; - grub_usb_err_t err; - real_config (port); - err = grub_usb_bulk_read (port->usbdev, port->in_endp->endp_addr, 3, cc); - if (err != GRUB_USB_ERR_NONE) - return -1; - - return cc[2]; + return grub_usbserial_fetch (port, 2); } /* Put a character. */ diff --git a/bus/usb/serial/pl2303.c b/bus/usb/serial/pl2303.c index e2dcd606e..9e3b9ae7e 100644 --- a/bus/usb/serial/pl2303.c +++ b/bus/usb/serial/pl2303.c @@ -127,26 +127,9 @@ real_config (struct grub_serial_port *port) static int pl2303_hw_fetch (struct grub_serial_port *port) { - grub_usb_err_t err; - real_config (port); - if (port->bufstart < port->bufend) - return port->buf[port->bufstart++]; - - err = grub_usb_bulk_read_timeout (port->usbdev, port->in_endp->endp_addr, - sizeof (port->buf), port->buf, 10); - if (err != GRUB_USB_ERR_NONE) - return -1; - - port->bufstart = 0; - /* FIXME: nearly always only one byte is transfered. - It happens however that more are transfered. - Setting sizeof (port->buf) to 1 leads code to stop reading after - such transfer. */ - port->bufend = 1; - - return port->buf[port->bufstart++]; + return grub_usbserial_fetch (port, 0); } /* Put a character. */ diff --git a/bus/usb/uhci.c b/bus/usb/uhci.c index d211de369..69fae60fd 100644 --- a/bus/usb/uhci.c +++ b/bus/usb/uhci.c @@ -75,8 +75,10 @@ struct grub_uhci_td This is GRUB specific. */ grub_uint32_t linkptr2; - /* 3 additional 32 bits words reserved for the Host Controller Driver. */ - grub_uint32_t data[3]; + grub_uint32_t buffer0; + + /* 2 additional 32 bits words reserved for the Host Controller Driver. */ + grub_uint32_t data[2]; } __attribute__ ((packed)); typedef volatile struct grub_uhci_td *grub_uhci_td_t; @@ -333,9 +335,11 @@ grub_free_td (struct grub_uhci *u, grub_uhci_td_t td) static void grub_free_queue (struct grub_uhci *u, grub_uhci_td_t td, - grub_usb_transfer_t transfer) + grub_usb_transfer_t transfer, grub_size_t *actual) { int i; /* Index of TD in transfer */ + + *actual = 0; /* Free the TDs in this queue and set last_trans. */ for (i=0; td; i++) @@ -345,6 +349,8 @@ grub_free_queue (struct grub_uhci *u, grub_uhci_td_t td, /* Check state of TD and possibly set last_trans */ if (transfer && (td->linkptr & 1)) transfer->last_trans = i; + + *actual += (td->ctrl_status + 1) & 0x7ff; /* Unlink the queue. */ tdprev = td; @@ -430,6 +436,7 @@ grub_uhci_transaction (struct grub_uhci *u, unsigned int endp, | (addr << 8) | tf[type]); td->buffer = data; + td->buffer0 = data; return td; } @@ -437,7 +444,7 @@ grub_uhci_transaction (struct grub_uhci *u, unsigned int endp, static grub_usb_err_t grub_uhci_transfer (grub_usb_controller_t dev, grub_usb_transfer_t transfer, - int timeout) + int timeout, grub_size_t *actual) { struct grub_uhci *u = (struct grub_uhci *) dev->data; grub_uhci_qh_t qh; @@ -448,10 +455,12 @@ grub_uhci_transfer (grub_usb_controller_t dev, int i; grub_uint64_t endtime; + *actual = 0; + /* Allocate a queue head for the transfer queue. */ qh = grub_alloc_qh (u, GRUB_USB_TRANSACTION_TYPE_CONTROL); if (! qh) - return grub_errno; + return GRUB_USB_ERR_INTERNAL; grub_dprintf ("uhci", "transfer, iobase:%08x\n", u->iobase); @@ -469,7 +478,7 @@ grub_uhci_transfer (grub_usb_controller_t dev, td_prev->linkptr = 1; if (td_first) - grub_free_queue (u, td_first, NULL); + grub_free_queue (u, td_first, NULL, actual); return GRUB_USB_ERR_INTERNAL; } @@ -563,7 +572,7 @@ grub_uhci_transfer (grub_usb_controller_t dev, /* Place the QH back in the free list and deallocate the associated TDs. */ qh->elinkptr = 1; - grub_free_queue (u, td_first, transfer); + grub_free_queue (u, td_first, transfer, actual); return err; } diff --git a/bus/usb/usbtrans.c b/bus/usb/usbtrans.c index 9dfef509f..db2ec097a 100644 --- a/bus/usb/usbtrans.c +++ b/bus/usb/usbtrans.c @@ -43,6 +43,7 @@ grub_usb_control_msg (grub_usb_device_t dev, volatile char *data; grub_uint32_t data_addr; grub_size_t size = size0; + grub_size_t actual; /* FIXME: avoid allocation any kind of buffer in a first place. */ data_chunk = grub_memalign_dma32 (128, size ? : 16); @@ -132,6 +133,7 @@ grub_usb_control_msg (grub_usb_device_t dev, else tr->pid = GRUB_USB_TRANSFER_TYPE_OUT; tr->data = data_addr + i * max; + tr->preceding = i * max; size -= max; } @@ -145,7 +147,8 @@ grub_usb_control_msg (grub_usb_device_t dev, transfer->transactions[datablocks + 1].toggle = 1; - err = dev->controller.dev->transfer (&dev->controller, transfer, 1000); + err = dev->controller.dev->transfer (&dev->controller, transfer, + 1000, &actual); grub_dprintf ("usb", "control: err=%d\n", err); grub_free (transfer->transactions); @@ -162,7 +165,8 @@ grub_usb_control_msg (grub_usb_device_t dev, static grub_usb_err_t grub_usb_bulk_readwrite (grub_usb_device_t dev, int endpoint, grub_size_t size0, char *data_in, - grub_transfer_type_t type, int timeout) + grub_transfer_type_t type, int timeout, + grub_size_t *actual) { int i; grub_usb_transfer_t transfer; @@ -240,10 +244,12 @@ grub_usb_bulk_readwrite (grub_usb_device_t dev, toggle = toggle ? 0 : 1; tr->pid = type; tr->data = data_addr + i * max; + tr->preceding = i * max; size -= tr->size; } - err = dev->controller.dev->transfer (&dev->controller, transfer, timeout); + err = dev->controller.dev->transfer (&dev->controller, transfer, timeout, + actual); /* We must remember proper toggle value even if some transactions * were not processed - correct value should be inversion of last * processed transaction (TD). */ @@ -268,23 +274,34 @@ grub_usb_err_t grub_usb_bulk_write (grub_usb_device_t dev, int endpoint, grub_size_t size, char *data) { - return grub_usb_bulk_readwrite (dev, endpoint, size, data, - GRUB_USB_TRANSFER_TYPE_OUT, 1000); + grub_size_t actual; + grub_usb_err_t err; + + err = grub_usb_bulk_readwrite (dev, endpoint, size, data, + GRUB_USB_TRANSFER_TYPE_OUT, 1000, &actual); + if (!err && actual != size) + err = GRUB_USB_ERR_DATA; + return err; } grub_usb_err_t grub_usb_bulk_read (grub_usb_device_t dev, int endpoint, grub_size_t size, char *data) { - return grub_usb_bulk_readwrite (dev, endpoint, size, data, - GRUB_USB_TRANSFER_TYPE_IN, 1000); + grub_size_t actual; + grub_usb_err_t err; + err = grub_usb_bulk_readwrite (dev, endpoint, size, data, + GRUB_USB_TRANSFER_TYPE_IN, 1000, &actual); + if (!err && actual != size) + err = GRUB_USB_ERR_DATA; + return err; } grub_usb_err_t -grub_usb_bulk_read_timeout (grub_usb_device_t dev, - int endpoint, grub_size_t size, char *data, - int timeout) +grub_usb_bulk_read_extended (grub_usb_device_t dev, + int endpoint, grub_size_t size, char *data, + int timeout, grub_size_t *actual) { return grub_usb_bulk_readwrite (dev, endpoint, size, data, - GRUB_USB_TRANSFER_TYPE_IN, timeout); + GRUB_USB_TRANSFER_TYPE_IN, timeout, actual); } diff --git a/include/grub/serial.h b/include/grub/serial.h index a7d37c7de..68cec6fdf 100644 --- a/include/grub/serial.h +++ b/include/grub/serial.h @@ -67,8 +67,6 @@ struct grub_serial_port struct grub_serial_driver *driver; struct grub_serial_config config; int configured; - char buf[64]; - int bufstart, bufend; /* This should be void *data but since serial is useful as an early console when malloc isn't available it's a union. */ @@ -80,6 +78,8 @@ struct grub_serial_port grub_usb_device_t usbdev; int configno; int interfno; + char buf[64]; + int bufstart, bufend; struct grub_usb_desc_endp *in_endp; struct grub_usb_desc_endp *out_endp; }; diff --git a/include/grub/usb.h b/include/grub/usb.h index 0ebb39478..b2dc77ce4 100644 --- a/include/grub/usb.h +++ b/include/grub/usb.h @@ -107,7 +107,7 @@ struct grub_usb_controller_dev grub_usb_err_t (*transfer) (grub_usb_controller_t dev, grub_usb_transfer_t transfer, - int timeout); + int timeout, grub_size_t *actual); int (*hubports) (grub_usb_controller_t dev); @@ -240,8 +240,8 @@ void grub_usb_poll_devices (void); void grub_usb_device_attach (grub_usb_device_t dev); grub_usb_err_t -grub_usb_bulk_read_timeout (grub_usb_device_t dev, - int endpoint, grub_size_t size, char *data, - int timeout); +grub_usb_bulk_read_extended (grub_usb_device_t dev, + int endpoint, grub_size_t size, char *data, + int timeout, grub_size_t *actual); #endif /* GRUB_USB_H */ diff --git a/include/grub/usbserial.h b/include/grub/usbserial.h index 786eff7fe..74201256e 100644 --- a/include/grub/usbserial.h +++ b/include/grub/usbserial.h @@ -28,4 +28,7 @@ void grub_usbserial_detach (grub_usb_device_t usbdev, int configno, int grub_usbserial_attach (grub_usb_device_t usbdev, int configno, int interfno, struct grub_serial_driver *driver); +int +grub_usbserial_fetch (struct grub_serial_port *port, grub_size_t header_size); + #endif diff --git a/include/grub/usbtrans.h b/include/grub/usbtrans.h index e68698c1d..a5bb2e8b2 100644 --- a/include/grub/usbtrans.h +++ b/include/grub/usbtrans.h @@ -38,6 +38,7 @@ struct grub_usb_transaction int toggle; grub_transfer_type_t pid; grub_uint32_t data; + grub_size_t preceding; }; typedef struct grub_usb_transaction *grub_usb_transaction_t;