Allow psartial transfers and use them for usbserial

This commit is contained in:
Vladimir 'phcoder' Serbinenko 2010-07-19 08:43:01 +02:00
parent 824e1447ac
commit 34787305df
10 changed files with 93 additions and 59 deletions

View file

@ -654,7 +654,8 @@ grub_ohci_transaction (grub_ohci_td_t td,
static grub_usb_err_t static grub_usb_err_t
grub_ohci_transfer (grub_usb_controller_t dev, 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; struct grub_ohci *o = (struct grub_ohci *) dev->data;
grub_ohci_ed_t ed_virt; grub_ohci_ed_t ed_virt;
@ -680,6 +681,8 @@ grub_ohci_transfer (grub_usb_controller_t dev,
int err_unrec = 0; int err_unrec = 0;
grub_uint32_t intstatus; grub_uint32_t intstatus;
*actual = 0;
/* Pre-set target for ED - we need it to find proper ED */ /* Pre-set target for ED - we need it to find proper ED */
/* Set the device address. */ /* Set the device address. */
target = transfer->devaddr; target = transfer->devaddr;
@ -1078,12 +1081,14 @@ grub_ohci_transfer (grub_usb_controller_t dev,
case 9: case 9:
/* XXX: Data underrun error. */ /* XXX: Data underrun error. */
err = GRUB_USB_ERR_DATA;
grub_dprintf ("ohci", "Underrun, failed TD address: %p, index: %d\n", grub_dprintf ("ohci", "Underrun, failed TD address: %p, index: %d\n",
tderr_virt, tderr_virt->tr_index); tderr_virt, tderr_virt->tr_index);
grub_dprintf ("ohci", "Underrun, number of not transferred bytes: %d\n", if (transfer->last_trans == -1)
1 + grub_le_to_cpu32 (tderr_virt->buffer_end) break;
- grub_le_to_cpu32 (tderr_virt->buffer)); *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; break;
case 10: case 10:
@ -1172,12 +1177,12 @@ grub_ohci_transfer (grub_usb_controller_t dev,
transfer->last_trans = tderr_virt->tr_index; transfer->last_trans = tderr_virt->tr_index;
else else
transfer->last_trans = -1; transfer->last_trans = -1;
} }
else
*actual = transfer->size;
/* Set empty ED - set HEAD = TAIL = last (not processed) TD */ /* 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_head = grub_cpu_to_le32 (grub_le_to_cpu32 (ed_virt->td_tail) & ~0xf);
ed_virt->td_tail) & ~0xf);
/* At this point always should be: /* At this point always should be:
* ED has skip bit set and halted or empty or after next SOF, * ED has skip bit set and halted or empty or after next SOF,

View file

@ -100,3 +100,26 @@ grub_usbserial_attach (grub_usb_device_t usbdev, int configno, int interfno,
return 1; 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++];
}

View file

@ -111,16 +111,9 @@ real_config (struct grub_serial_port *port)
static int static int
ftdi_hw_fetch (struct grub_serial_port *port) ftdi_hw_fetch (struct grub_serial_port *port)
{ {
char cc[3];
grub_usb_err_t err;
real_config (port); real_config (port);
err = grub_usb_bulk_read (port->usbdev, port->in_endp->endp_addr, 3, cc); return grub_usbserial_fetch (port, 2);
if (err != GRUB_USB_ERR_NONE)
return -1;
return cc[2];
} }
/* Put a character. */ /* Put a character. */

View file

@ -127,26 +127,9 @@ real_config (struct grub_serial_port *port)
static int static int
pl2303_hw_fetch (struct grub_serial_port *port) pl2303_hw_fetch (struct grub_serial_port *port)
{ {
grub_usb_err_t err;
real_config (port); real_config (port);
if (port->bufstart < port->bufend) return grub_usbserial_fetch (port, 0);
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++];
} }
/* Put a character. */ /* Put a character. */

View file

@ -75,8 +75,10 @@ struct grub_uhci_td
This is GRUB specific. */ This is GRUB specific. */
grub_uint32_t linkptr2; grub_uint32_t linkptr2;
/* 3 additional 32 bits words reserved for the Host Controller Driver. */ grub_uint32_t buffer0;
grub_uint32_t data[3];
/* 2 additional 32 bits words reserved for the Host Controller Driver. */
grub_uint32_t data[2];
} __attribute__ ((packed)); } __attribute__ ((packed));
typedef volatile struct grub_uhci_td *grub_uhci_td_t; typedef volatile struct grub_uhci_td *grub_uhci_td_t;
@ -333,10 +335,12 @@ grub_free_td (struct grub_uhci *u, grub_uhci_td_t td)
static void static void
grub_free_queue (struct grub_uhci *u, grub_uhci_td_t td, 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 */ int i; /* Index of TD in transfer */
*actual = 0;
/* Free the TDs in this queue and set last_trans. */ /* Free the TDs in this queue and set last_trans. */
for (i=0; td; i++) for (i=0; td; i++)
{ {
@ -346,6 +350,8 @@ grub_free_queue (struct grub_uhci *u, grub_uhci_td_t td,
if (transfer && (td->linkptr & 1)) if (transfer && (td->linkptr & 1))
transfer->last_trans = i; transfer->last_trans = i;
*actual += (td->ctrl_status + 1) & 0x7ff;
/* Unlink the queue. */ /* Unlink the queue. */
tdprev = td; tdprev = td;
td = (grub_uhci_td_t) td->linkptr2; td = (grub_uhci_td_t) td->linkptr2;
@ -430,6 +436,7 @@ grub_uhci_transaction (struct grub_uhci *u, unsigned int endp,
| (addr << 8) | tf[type]); | (addr << 8) | tf[type]);
td->buffer = data; td->buffer = data;
td->buffer0 = data;
return td; return td;
} }
@ -437,7 +444,7 @@ grub_uhci_transaction (struct grub_uhci *u, unsigned int endp,
static grub_usb_err_t static grub_usb_err_t
grub_uhci_transfer (grub_usb_controller_t dev, grub_uhci_transfer (grub_usb_controller_t dev,
grub_usb_transfer_t transfer, grub_usb_transfer_t transfer,
int timeout) int timeout, grub_size_t *actual)
{ {
struct grub_uhci *u = (struct grub_uhci *) dev->data; struct grub_uhci *u = (struct grub_uhci *) dev->data;
grub_uhci_qh_t qh; grub_uhci_qh_t qh;
@ -448,10 +455,12 @@ grub_uhci_transfer (grub_usb_controller_t dev,
int i; int i;
grub_uint64_t endtime; grub_uint64_t endtime;
*actual = 0;
/* Allocate a queue head for the transfer queue. */ /* Allocate a queue head for the transfer queue. */
qh = grub_alloc_qh (u, GRUB_USB_TRANSACTION_TYPE_CONTROL); qh = grub_alloc_qh (u, GRUB_USB_TRANSACTION_TYPE_CONTROL);
if (! qh) if (! qh)
return grub_errno; return GRUB_USB_ERR_INTERNAL;
grub_dprintf ("uhci", "transfer, iobase:%08x\n", u->iobase); 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; td_prev->linkptr = 1;
if (td_first) if (td_first)
grub_free_queue (u, td_first, NULL); grub_free_queue (u, td_first, NULL, actual);
return GRUB_USB_ERR_INTERNAL; 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 /* Place the QH back in the free list and deallocate the associated
TDs. */ TDs. */
qh->elinkptr = 1; qh->elinkptr = 1;
grub_free_queue (u, td_first, transfer); grub_free_queue (u, td_first, transfer, actual);
return err; return err;
} }

View file

@ -43,6 +43,7 @@ grub_usb_control_msg (grub_usb_device_t dev,
volatile char *data; volatile char *data;
grub_uint32_t data_addr; grub_uint32_t data_addr;
grub_size_t size = size0; grub_size_t size = size0;
grub_size_t actual;
/* FIXME: avoid allocation any kind of buffer in a first place. */ /* FIXME: avoid allocation any kind of buffer in a first place. */
data_chunk = grub_memalign_dma32 (128, size ? : 16); data_chunk = grub_memalign_dma32 (128, size ? : 16);
@ -132,6 +133,7 @@ grub_usb_control_msg (grub_usb_device_t dev,
else else
tr->pid = GRUB_USB_TRANSFER_TYPE_OUT; tr->pid = GRUB_USB_TRANSFER_TYPE_OUT;
tr->data = data_addr + i * max; tr->data = data_addr + i * max;
tr->preceding = i * max;
size -= max; size -= max;
} }
@ -145,7 +147,8 @@ grub_usb_control_msg (grub_usb_device_t dev,
transfer->transactions[datablocks + 1].toggle = 1; 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_dprintf ("usb", "control: err=%d\n", err);
grub_free (transfer->transactions); grub_free (transfer->transactions);
@ -162,7 +165,8 @@ grub_usb_control_msg (grub_usb_device_t dev,
static grub_usb_err_t static grub_usb_err_t
grub_usb_bulk_readwrite (grub_usb_device_t dev, grub_usb_bulk_readwrite (grub_usb_device_t dev,
int endpoint, grub_size_t size0, char *data_in, 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; int i;
grub_usb_transfer_t transfer; grub_usb_transfer_t transfer;
@ -240,10 +244,12 @@ grub_usb_bulk_readwrite (grub_usb_device_t dev,
toggle = toggle ? 0 : 1; toggle = toggle ? 0 : 1;
tr->pid = type; tr->pid = type;
tr->data = data_addr + i * max; tr->data = data_addr + i * max;
tr->preceding = i * max;
size -= tr->size; 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 /* We must remember proper toggle value even if some transactions
* were not processed - correct value should be inversion of last * were not processed - correct value should be inversion of last
* processed transaction (TD). */ * processed transaction (TD). */
@ -268,23 +274,34 @@ grub_usb_err_t
grub_usb_bulk_write (grub_usb_device_t dev, grub_usb_bulk_write (grub_usb_device_t dev,
int endpoint, grub_size_t size, char *data) int endpoint, grub_size_t size, char *data)
{ {
return grub_usb_bulk_readwrite (dev, endpoint, size, data, grub_size_t actual;
GRUB_USB_TRANSFER_TYPE_OUT, 1000); 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_err_t
grub_usb_bulk_read (grub_usb_device_t dev, grub_usb_bulk_read (grub_usb_device_t dev,
int endpoint, grub_size_t size, char *data) int endpoint, grub_size_t size, char *data)
{ {
return grub_usb_bulk_readwrite (dev, endpoint, size, data, grub_size_t actual;
GRUB_USB_TRANSFER_TYPE_IN, 1000); 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_err_t
grub_usb_bulk_read_timeout (grub_usb_device_t dev, grub_usb_bulk_read_extended (grub_usb_device_t dev,
int endpoint, grub_size_t size, char *data, int endpoint, grub_size_t size, char *data,
int timeout) int timeout, grub_size_t *actual)
{ {
return grub_usb_bulk_readwrite (dev, endpoint, size, data, return grub_usb_bulk_readwrite (dev, endpoint, size, data,
GRUB_USB_TRANSFER_TYPE_IN, timeout); GRUB_USB_TRANSFER_TYPE_IN, timeout, actual);
} }

View file

@ -67,8 +67,6 @@ struct grub_serial_port
struct grub_serial_driver *driver; struct grub_serial_driver *driver;
struct grub_serial_config config; struct grub_serial_config config;
int configured; int configured;
char buf[64];
int bufstart, bufend;
/* This should be void *data but since serial is useful as an early console /* This should be void *data but since serial is useful as an early console
when malloc isn't available it's a union. when malloc isn't available it's a union.
*/ */
@ -80,6 +78,8 @@ struct grub_serial_port
grub_usb_device_t usbdev; grub_usb_device_t usbdev;
int configno; int configno;
int interfno; int interfno;
char buf[64];
int bufstart, bufend;
struct grub_usb_desc_endp *in_endp; struct grub_usb_desc_endp *in_endp;
struct grub_usb_desc_endp *out_endp; struct grub_usb_desc_endp *out_endp;
}; };

View file

@ -107,7 +107,7 @@ struct grub_usb_controller_dev
grub_usb_err_t (*transfer) (grub_usb_controller_t dev, grub_usb_err_t (*transfer) (grub_usb_controller_t dev,
grub_usb_transfer_t transfer, grub_usb_transfer_t transfer,
int timeout); int timeout, grub_size_t *actual);
int (*hubports) (grub_usb_controller_t dev); 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); void grub_usb_device_attach (grub_usb_device_t dev);
grub_usb_err_t grub_usb_err_t
grub_usb_bulk_read_timeout (grub_usb_device_t dev, grub_usb_bulk_read_extended (grub_usb_device_t dev,
int endpoint, grub_size_t size, char *data, int endpoint, grub_size_t size, char *data,
int timeout); int timeout, grub_size_t *actual);
#endif /* GRUB_USB_H */ #endif /* GRUB_USB_H */

View file

@ -28,4 +28,7 @@ void grub_usbserial_detach (grub_usb_device_t usbdev, int configno,
int int
grub_usbserial_attach (grub_usb_device_t usbdev, int configno, int interfno, grub_usbserial_attach (grub_usb_device_t usbdev, int configno, int interfno,
struct grub_serial_driver *driver); struct grub_serial_driver *driver);
int
grub_usbserial_fetch (struct grub_serial_port *port, grub_size_t header_size);
#endif #endif

View file

@ -38,6 +38,7 @@ struct grub_usb_transaction
int toggle; int toggle;
grub_transfer_type_t pid; grub_transfer_type_t pid;
grub_uint32_t data; grub_uint32_t data;
grub_size_t preceding;
}; };
typedef struct grub_usb_transaction *grub_usb_transaction_t; typedef struct grub_usb_transaction *grub_usb_transaction_t;