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
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,

View file

@ -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++];
}

View file

@ -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. */

View file

@ -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. */

View file

@ -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;
}

View file

@ -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);
}

View file

@ -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;
};

View file

@ -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 */

View file

@ -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

View file

@ -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;