merge with mainline

This commit is contained in:
BVK Chaitanya 2010-08-22 21:00:22 +05:30
commit d84666e6bb
37 changed files with 2533 additions and 641 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)
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;
@ -832,7 +835,7 @@ grub_ohci_transfer (grub_usb_controller_t dev,
}
/* Safety measure to avoid a hang. */
maxtime = grub_get_time_ms () + 1000;
maxtime = grub_get_time_ms () + timeout;
/* Wait until the transfer is completed or STALLs. */
do
@ -986,6 +989,7 @@ grub_ohci_transfer (grub_usb_controller_t dev,
transfer->last_trans = tderr_virt->tr_index;
else
transfer->last_trans = -1;
*actual = transfer->size + 1;
}
else if (err_halt) /* error, ED is halted by OHCI, i.e. can be modified */
@ -1032,7 +1036,7 @@ grub_ohci_transfer (grub_usb_controller_t dev,
{
case 0:
/* XXX: Should not happen! */
grub_error (GRUB_ERR_IO, "OHCI without reporting the reason");
grub_error (GRUB_ERR_IO, "OHCI failed without reporting the reason");
err = GRUB_USB_ERR_INTERNAL;
break;
@ -1078,12 +1082,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 +1178,10 @@ grub_ohci_transfer (grub_usb_controller_t dev,
transfer->last_trans = tderr_virt->tr_index;
else
transfer->last_trans = -1;
}
/* 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

@ -0,0 +1,125 @@
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2000,2001,2002,2003,2004,2005,2007,2008,2009,2010 Free Software Foundation, Inc.
*
* GRUB is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* GRUB is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GRUB. If not, see <http://www.gnu.org/licenses/>.
*/
#include <grub/serial.h>
#include <grub/usbserial.h>
void
grub_usbserial_fini (struct grub_serial_port *port)
{
port->usbdev->config[port->configno].interf[port->interfno].detach_hook = 0;
port->usbdev->config[port->configno].interf[port->interfno].attached = 0;
}
void
grub_usbserial_detach (grub_usb_device_t usbdev, int configno, int interfno)
{
static struct grub_serial_port *port;
port = usbdev->config[configno].interf[interfno].detach_data;
grub_serial_unregister (port);
}
static int usbnum = 0;
int
grub_usbserial_attach (grub_usb_device_t usbdev, int configno, int interfno,
struct grub_serial_driver *driver)
{
struct grub_serial_port *port;
int j;
struct grub_usb_desc_if *interf;
interf = usbdev->config[configno].interf[interfno].descif;
port = grub_malloc (sizeof (*port));
if (!port)
{
grub_print_error ();
return 0;
}
port->name = grub_xasprintf ("usb%d", usbnum++);
if (!port->name)
{
grub_free (port);
grub_print_error ();
return 0;
}
port->usbdev = usbdev;
port->driver = driver;
for (j = 0; j < interf->endpointcnt; j++)
{
struct grub_usb_desc_endp *endp;
endp = &usbdev->config[0].interf[interfno].descendp[j];
if ((endp->endp_addr & 128) && (endp->attrib & 3) == 2)
{
/* Bulk IN endpoint. */
port->in_endp = endp;
}
else if (!(endp->endp_addr & 128) && (endp->attrib & 3) == 2)
{
/* Bulk OUT endpoint. */
port->out_endp = endp;
}
}
if (!port->out_endp || !port->in_endp)
{
grub_free (port->name);
grub_free (port);
return 0;
}
port->configno = configno;
port->interfno = interfno;
grub_serial_config_defaults (port);
grub_serial_register (port);
port->usbdev->config[port->configno].interf[port->interfno].detach_hook
= grub_usbserial_detach;
port->usbdev->config[port->configno].interf[port->interfno].detach_data
= port;
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

@ -0,0 +1,205 @@
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2000,2001,2002,2003,2004,2005,2007,2008,2009,2010 Free Software Foundation, Inc.
*
* GRUB is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* GRUB is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GRUB. If not, see <http://www.gnu.org/licenses/>.
*/
#include <grub/serial.h>
#include <grub/types.h>
#include <grub/dl.h>
#include <grub/misc.h>
#include <grub/mm.h>
#include <grub/usb.h>
#include <grub/usbserial.h>
enum
{
GRUB_FTDI_MODEM_CTRL = 0x01,
GRUB_FTDI_FLOW_CTRL = 0x02,
GRUB_FTDI_SPEED_CTRL = 0x03,
GRUB_FTDI_DATA_CTRL = 0x04
};
#define GRUB_FTDI_MODEM_CTRL_DTRRTS 3
#define GRUB_FTDI_FLOW_CTRL_DTRRTS 3
/* Convert speed to divisor. */
static grub_uint32_t
get_divisor (unsigned int speed)
{
unsigned int i;
/* The structure for speed vs. divisor. */
struct divisor
{
unsigned int speed;
grub_uint32_t div;
};
/* The table which lists common configurations. */
/* Computed with a division formula with 3MHz as base frequency. */
static struct divisor divisor_tab[] =
{
{ 2400, 0x04e2 },
{ 4800, 0x0271 },
{ 9600, 0x4138 },
{ 19200, 0x809c },
{ 38400, 0xc04e },
{ 57600, 0xc034 },
{ 115200, 0x001a }
};
/* Set the baud rate. */
for (i = 0; i < ARRAY_SIZE (divisor_tab); i++)
if (divisor_tab[i].speed == speed)
return divisor_tab[i].div;
return 0;
}
static void
real_config (struct grub_serial_port *port)
{
grub_uint32_t divisor;
const grub_uint16_t parities[] = {
[GRUB_SERIAL_PARITY_NONE] = 0x0000,
[GRUB_SERIAL_PARITY_ODD] = 0x0100,
[GRUB_SERIAL_PARITY_EVEN] = 0x0200
};
const grub_uint16_t stop_bits[] = {
[GRUB_SERIAL_STOP_BITS_1] = 0x0000,
[GRUB_SERIAL_STOP_BITS_2] = 0x1000,
};
if (port->configured)
return;
grub_usb_control_msg (port->usbdev, GRUB_USB_REQTYPE_VENDOR_OUT,
GRUB_FTDI_MODEM_CTRL,
GRUB_FTDI_MODEM_CTRL_DTRRTS, 0, 0, 0);
grub_usb_control_msg (port->usbdev, GRUB_USB_REQTYPE_VENDOR_OUT,
GRUB_FTDI_FLOW_CTRL,
GRUB_FTDI_FLOW_CTRL_DTRRTS, 0, 0, 0);
divisor = get_divisor (port->config.speed);
grub_usb_control_msg (port->usbdev, GRUB_USB_REQTYPE_VENDOR_OUT,
GRUB_FTDI_SPEED_CTRL,
divisor & 0xffff, divisor >> 16, 0, 0);
grub_usb_control_msg (port->usbdev, GRUB_USB_REQTYPE_VENDOR_OUT,
GRUB_FTDI_DATA_CTRL,
parities[port->config.parity]
| stop_bits[port->config.stop_bits]
| port->config.word_len, 0, 0, 0);
port->configured = 1;
}
/* Fetch a key. */
static int
ftdi_hw_fetch (struct grub_serial_port *port)
{
real_config (port);
return grub_usbserial_fetch (port, 2);
}
/* Put a character. */
static void
ftdi_hw_put (struct grub_serial_port *port, const int c)
{
char cc = c;
real_config (port);
grub_usb_bulk_write (port->usbdev, port->out_endp->endp_addr, 1, &cc);
}
static grub_err_t
ftdi_hw_configure (struct grub_serial_port *port,
struct grub_serial_config *config)
{
grub_uint16_t divisor;
divisor = get_divisor (config->speed);
if (divisor == 0)
return grub_error (GRUB_ERR_BAD_ARGUMENT, "bad speed");
if (config->parity != GRUB_SERIAL_PARITY_NONE
&& config->parity != GRUB_SERIAL_PARITY_ODD
&& config->parity != GRUB_SERIAL_PARITY_EVEN)
return grub_error (GRUB_ERR_BAD_ARGUMENT, "unsupported parity");
if (config->stop_bits != GRUB_SERIAL_STOP_BITS_1
&& config->stop_bits != GRUB_SERIAL_STOP_BITS_2)
return grub_error (GRUB_ERR_BAD_ARGUMENT, "unsupported stop bits");
if (config->word_len < 5 || config->word_len > 8)
return grub_error (GRUB_ERR_BAD_ARGUMENT, "unsupported word length");
port->config = *config;
port->configured = 0;
return GRUB_ERR_NONE;
}
static struct grub_serial_driver grub_ftdi_driver =
{
.configure = ftdi_hw_configure,
.fetch = ftdi_hw_fetch,
.put = ftdi_hw_put,
.fini = grub_usbserial_fini
};
static const struct
{
grub_uint16_t vendor, product;
} products[] =
{
{0x0403, 0x6001} /* QEMU virtual USBserial. */
};
static int
grub_ftdi_attach (grub_usb_device_t usbdev, int configno, int interfno)
{
unsigned j;
for (j = 0; j < ARRAY_SIZE (products); j++)
if (usbdev->descdev.vendorid == products[j].vendor
&& usbdev->descdev.prodid == products[j].product)
break;
if (j == ARRAY_SIZE (products))
return 0;
return grub_usbserial_attach (usbdev, configno, interfno,
&grub_ftdi_driver);
}
static struct grub_usb_attach_desc attach_hook =
{
.class = 0xff,
.hook = grub_ftdi_attach
};
GRUB_MOD_INIT(usbserial_ftdi)
{
grub_usb_register_attach_hook_class (&attach_hook);
}
GRUB_MOD_FINI(usbserial_ftdi)
{
grub_serial_unregister_driver (&grub_ftdi_driver);
grub_usb_unregister_attach_hook_class (&attach_hook);
}

View file

@ -0,0 +1,218 @@
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2000,2001,2002,2003,2004,2005,2007,2008,2009,2010 Free Software Foundation, Inc.
*
* GRUB is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* GRUB is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GRUB. If not, see <http://www.gnu.org/licenses/>.
*/
#include <grub/serial.h>
#include <grub/types.h>
#include <grub/dl.h>
#include <grub/misc.h>
#include <grub/mm.h>
#include <grub/usb.h>
#include <grub/usbserial.h>
/* Convert speed to divisor. */
static grub_uint32_t
is_speed_supported (unsigned int speed)
{
unsigned int i;
unsigned int supported[] = { 2400, 4800, 9600, 19200, 38400, 57600, 115200};
for (i = 0; i < ARRAY_SIZE (supported); i++)
if (supported[i] == speed)
return 1;
return 0;
}
#define GRUB_PL2303_REQUEST_SET_CONFIG 0x20
#define GRUB_PL2303_STOP_BITS_1 0x0
#define GRUB_PL2303_STOP_BITS_2 0x2
#define GRUB_PL2303_PARITY_NONE 0
#define GRUB_PL2303_PARITY_ODD 1
#define GRUB_PL2303_PARITY_EVEN 2
struct grub_pl2303_config
{
grub_uint32_t speed;
grub_uint8_t stop_bits;
grub_uint8_t parity;
grub_uint8_t word_len;
} __attribute__ ((packed));
static void
real_config (struct grub_serial_port *port)
{
struct grub_pl2303_config config_pl2303;
char xx;
if (port->configured)
return;
grub_usb_control_msg (port->usbdev, GRUB_USB_REQTYPE_VENDOR_IN,
1, 0x8484, 0, 1, &xx);
grub_usb_control_msg (port->usbdev, GRUB_USB_REQTYPE_VENDOR_OUT,
1, 0x0404, 0, 0, 0);
grub_usb_control_msg (port->usbdev, GRUB_USB_REQTYPE_VENDOR_IN,
1, 0x8484, 0, 1, &xx);
grub_usb_control_msg (port->usbdev, GRUB_USB_REQTYPE_VENDOR_IN,
1, 0x8383, 0, 1, &xx);
grub_usb_control_msg (port->usbdev, GRUB_USB_REQTYPE_VENDOR_IN,
1, 0x8484, 0, 1, &xx);
grub_usb_control_msg (port->usbdev, GRUB_USB_REQTYPE_VENDOR_OUT,
1, 0x0404, 1, 0, 0);
grub_usb_control_msg (port->usbdev, GRUB_USB_REQTYPE_VENDOR_IN,
1, 0x8484, 0, 1, &xx);
grub_usb_control_msg (port->usbdev, GRUB_USB_REQTYPE_VENDOR_IN,
1, 0x8383, 0, 1, &xx);
grub_usb_control_msg (port->usbdev, GRUB_USB_REQTYPE_VENDOR_OUT,
1, 0, 1, 0, 0);
grub_usb_control_msg (port->usbdev, GRUB_USB_REQTYPE_VENDOR_OUT,
1, 1, 0, 0, 0);
grub_usb_control_msg (port->usbdev, GRUB_USB_REQTYPE_VENDOR_OUT,
1, 2, 0x44, 0, 0);
grub_usb_control_msg (port->usbdev, GRUB_USB_REQTYPE_VENDOR_OUT,
1, 8, 0, 0, 0);
grub_usb_control_msg (port->usbdev, GRUB_USB_REQTYPE_VENDOR_OUT,
1, 9, 0, 0, 0);
if (port->config.stop_bits == GRUB_SERIAL_STOP_BITS_2)
config_pl2303.stop_bits = GRUB_PL2303_STOP_BITS_2;
else
config_pl2303.stop_bits = GRUB_PL2303_STOP_BITS_1;
switch (port->config.parity)
{
case GRUB_SERIAL_PARITY_NONE:
config_pl2303.parity = GRUB_PL2303_PARITY_NONE;
break;
case GRUB_SERIAL_PARITY_ODD:
config_pl2303.parity = GRUB_PL2303_PARITY_ODD;
break;
case GRUB_SERIAL_PARITY_EVEN:
config_pl2303.parity = GRUB_PL2303_PARITY_EVEN;
break;
}
config_pl2303.word_len = port->config.word_len;
config_pl2303.speed = port->config.speed;
grub_usb_control_msg (port->usbdev, GRUB_USB_REQTYPE_CLASS_INTERFACE_OUT,
GRUB_PL2303_REQUEST_SET_CONFIG, 0, 0,
sizeof (config_pl2303), (char *) &config_pl2303);
grub_usb_control_msg (port->usbdev, GRUB_USB_REQTYPE_CLASS_INTERFACE_OUT,
0x22, 3, 0, 0, 0);
grub_usb_control_msg (port->usbdev, GRUB_USB_REQTYPE_VENDOR_OUT,
1, 0, 0x61, 0, 0);
port->configured = 1;
}
/* Fetch a key. */
static int
pl2303_hw_fetch (struct grub_serial_port *port)
{
real_config (port);
return grub_usbserial_fetch (port, 0);
}
/* Put a character. */
static void
pl2303_hw_put (struct grub_serial_port *port, const int c)
{
char cc = c;
real_config (port);
grub_usb_bulk_write (port->usbdev, port->out_endp->endp_addr, 1, &cc);
}
static grub_err_t
pl2303_hw_configure (struct grub_serial_port *port,
struct grub_serial_config *config)
{
if (!is_speed_supported (config->speed))
return grub_error (GRUB_ERR_BAD_ARGUMENT, "bad speed");
if (config->parity != GRUB_SERIAL_PARITY_NONE
&& config->parity != GRUB_SERIAL_PARITY_ODD
&& config->parity != GRUB_SERIAL_PARITY_EVEN)
return grub_error (GRUB_ERR_BAD_ARGUMENT, "unsupported parity");
if (config->stop_bits != GRUB_SERIAL_STOP_BITS_1
&& config->stop_bits != GRUB_SERIAL_STOP_BITS_2)
return grub_error (GRUB_ERR_BAD_ARGUMENT, "unsupported stop bits");
if (config->word_len < 5 || config->word_len > 8)
return grub_error (GRUB_ERR_BAD_ARGUMENT, "unsupported word length");
port->config = *config;
port->configured = 0;
return GRUB_ERR_NONE;
}
static struct grub_serial_driver grub_pl2303_driver =
{
.configure = pl2303_hw_configure,
.fetch = pl2303_hw_fetch,
.put = pl2303_hw_put,
.fini = grub_usbserial_fini
};
static const struct
{
grub_uint16_t vendor, product;
} products[] =
{
{0x067b, 0x2303}
};
static int
grub_pl2303_attach (grub_usb_device_t usbdev, int configno, int interfno)
{
unsigned j;
for (j = 0; j < ARRAY_SIZE (products); j++)
if (usbdev->descdev.vendorid == products[j].vendor
&& usbdev->descdev.prodid == products[j].product)
break;
if (j == ARRAY_SIZE (products))
return 0;
return grub_usbserial_attach (usbdev, configno, interfno,
&grub_pl2303_driver);
}
static struct grub_usb_attach_desc attach_hook =
{
.class = 0xff,
.hook = grub_pl2303_attach
};
GRUB_MOD_INIT(usbserial_pl2303)
{
grub_usb_register_attach_hook_class (&attach_hook);
}
GRUB_MOD_FINI(usbserial_pl2303)
{
grub_serial_unregister_driver (&grub_pl2303_driver);
grub_usb_unregister_attach_hook_class (&attach_hook);
}

View file

@ -333,9 +333,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 +347,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;
@ -436,7 +440,8 @@ 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)
grub_usb_transfer_t transfer,
int timeout, grub_size_t *actual)
{
struct grub_uhci *u = (struct grub_uhci *) dev->data;
grub_uhci_qh_t qh;
@ -447,10 +452,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);
@ -468,7 +475,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;
}
@ -496,7 +503,7 @@ grub_uhci_transfer (grub_usb_controller_t dev,
/* Wait until either the transaction completed or an error
occurred. */
endtime = grub_get_time_ms () + 1000;
endtime = grub_get_time_ms () + timeout;
for (;;)
{
grub_uhci_td_t errtd;
@ -512,42 +519,37 @@ grub_uhci_transfer (grub_usb_controller_t dev,
grub_dprintf ("uhci", "t status=0x%02x\n", errtd->ctrl_status);
/* Check if the TD is not longer active. */
if (! (errtd->ctrl_status & (1 << 23)))
{
grub_dprintf ("uhci", ">>t status=0x%02x\n", errtd->ctrl_status);
/* Check if the endpoint is stalled. */
if (errtd->ctrl_status & (1 << 22))
err = GRUB_USB_ERR_STALL;
/* Check if the endpoint is stalled. */
if (errtd->ctrl_status & (1 << 22))
err = GRUB_USB_ERR_STALL;
/* Check if an error related to the data buffer occurred. */
if (errtd->ctrl_status & (1 << 21))
err = GRUB_USB_ERR_DATA;
/* Check if an error related to the data buffer occurred. */
if (errtd->ctrl_status & (1 << 21))
err = GRUB_USB_ERR_DATA;
/* Check if a babble error occurred. */
if (errtd->ctrl_status & (1 << 20))
err = GRUB_USB_ERR_BABBLE;
/* Check if a babble error occurred. */
if (errtd->ctrl_status & (1 << 20))
err = GRUB_USB_ERR_BABBLE;
/* Check if a NAK occurred. */
if (errtd->ctrl_status & (1 << 19))
err = GRUB_USB_ERR_NAK;
/* Check if a NAK occurred. */
if (errtd->ctrl_status & (1 << 19))
err = GRUB_USB_ERR_NAK;
/* Check if a timeout occurred. */
if (errtd->ctrl_status & (1 << 18))
err = GRUB_USB_ERR_TIMEOUT;
/* Check if a timeout occurred. */
if (errtd->ctrl_status & (1 << 18))
err = GRUB_USB_ERR_TIMEOUT;
/* Check if a bitstuff error occurred. */
if (errtd->ctrl_status & (1 << 17))
err = GRUB_USB_ERR_BITSTUFF;
/* Check if a bitstuff error occurred. */
if (errtd->ctrl_status & (1 << 17))
err = GRUB_USB_ERR_BITSTUFF;
if (err)
goto fail;
if (err)
goto fail;
/* Fall through, no errors occurred, so the QH might be
updated. */
grub_dprintf ("uhci", "transaction fallthrough\n");
/* Fall through, no errors occurred, so the QH might be
updated. */
grub_dprintf ("uhci", "transaction fallthrough\n");
}
if (grub_get_time_ms () > endtime)
{
err = GRUB_USB_ERR_STALL;
@ -567,7 +569,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

@ -22,6 +22,7 @@
#include <grub/usb.h>
#include <grub/misc.h>
#include <grub/list.h>
#include <grub/term.h>
static grub_usb_controller_dev_t grub_usb_list;
struct grub_usb_attach_desc *attach_hooks;
@ -208,14 +209,23 @@ grub_usb_device_initialize (grub_usb_device_t dev)
goto fail;
/* Skip the configuration descriptor. */
pos = sizeof (struct grub_usb_desc_config);
pos = dev->config[i].descconf->length;
/* Read all interfaces. */
for (currif = 0; currif < dev->config[i].descconf->numif; currif++)
{
while (pos < config.totallen
&& ((struct grub_usb_desc *)&data[pos])->type
!= GRUB_USB_DESCRIPTOR_INTERFACE)
pos += ((struct grub_usb_desc *)&data[pos])->length;
dev->config[i].interf[currif].descif
= (struct grub_usb_desc_if *) &data[pos];
pos += sizeof (struct grub_usb_desc_if);
pos += dev->config[i].interf[currif].descif->length;
while (pos < config.totallen
&& ((struct grub_usb_desc *)&data[pos])->type
!= GRUB_USB_DESCRIPTOR_ENDPOINT)
pos += ((struct grub_usb_desc *)&data[pos])->length;
/* Point to the first endpoint. */
dev->config[i].interf[currif].descendp
@ -256,6 +266,24 @@ void grub_usb_device_attach (grub_usb_device_t dev)
for (desc = attach_hooks; desc; desc = desc->next)
if (interf->class == desc->class && desc->hook (dev, 0, i))
dev->config[0].interf[i].attached = 1;
if (dev->config[0].interf[i].attached)
continue;
switch (interf->class)
{
case GRUB_USB_CLASS_MASS_STORAGE:
grub_dl_load ("usbms");
break;
case GRUB_USB_CLASS_HID:
grub_dl_load ("usb_keyboard");
break;
case 0xff:
/* FIXME: don't load useless modules. */
grub_dl_load ("usbserial_ftdi");
grub_dl_load ("usbserial_pl2303");
break;
}
}
}
@ -306,3 +334,14 @@ grub_usb_unregister_attach_hook_class (struct grub_usb_attach_desc *desc)
{
grub_list_remove (GRUB_AS_LIST_P (&attach_hooks), GRUB_AS_LIST (desc));
}
GRUB_MOD_INIT(usb)
{
grub_term_poll_usb = grub_usb_poll_devices;
}
GRUB_MOD_FINI(usb)
{
grub_term_poll_usb = NULL;
}

View file

@ -33,7 +33,7 @@ struct grub_usb_hub
struct grub_usb_hub *next;
grub_usb_controller_t controller;
int nports;
grub_usb_speed_t *speed;
struct grub_usb_device **devices;
grub_usb_device_t dev;
};
@ -104,7 +104,7 @@ grub_usb_hub_add_dev (grub_usb_controller_t controller, grub_usb_speed_t speed)
}
static grub_err_t
static grub_usb_err_t
grub_usb_add_hub (grub_usb_device_t dev)
{
struct grub_usb_usb_hubdesc hubdesc;
@ -112,6 +112,7 @@ grub_usb_add_hub (grub_usb_device_t dev)
int i;
grub_uint64_t timeout;
grub_usb_device_t next_dev;
grub_usb_device_t *attached_devices;
err = grub_usb_control_msg (dev, (GRUB_USB_REQTYPE_IN
| GRUB_USB_REQTYPE_CLASS
@ -130,6 +131,13 @@ grub_usb_add_hub (grub_usb_device_t dev)
grub_dprintf ("usb", "Hub set configuration\n");
grub_usb_set_configuration (dev, 1);
attached_devices = grub_zalloc (hubdesc.portcnt
* sizeof (attached_devices[0]));
if (!attached_devices)
return GRUB_USB_ERR_INTERNAL;
dev->children = attached_devices;
dev->nports = hubdesc.portcnt;
/* Power on all Hub ports. */
for (i = 1; i <= hubdesc.portcnt; i++)
{
@ -236,6 +244,8 @@ grub_usb_add_hub (grub_usb_device_t dev)
if (! next_dev)
continue;
attached_devices[i - 1] = next_dev;
/* If the device is a Hub, scan it for more devices. */
if (next_dev->descdev.class == 0x09)
grub_usb_add_hub (next_dev);
@ -246,27 +256,29 @@ grub_usb_add_hub (grub_usb_device_t dev)
}
static void
attach_root_port (grub_usb_controller_t controller, int portno,
attach_root_port (struct grub_usb_hub *hub, int portno,
grub_usb_speed_t speed)
{
grub_usb_device_t dev;
grub_err_t err;
/* Disable the port. XXX: Why? */
err = controller->dev->portstatus (controller, portno, 0);
err = hub->controller->dev->portstatus (hub->controller, portno, 0);
if (err)
return;
/* Enable the port. */
err = controller->dev->portstatus (controller, portno, 1);
err = hub->controller->dev->portstatus (hub->controller, portno, 1);
if (err)
return;
/* Enable the port and create a device. */
dev = grub_usb_hub_add_dev (controller, speed);
dev = grub_usb_hub_add_dev (hub->controller, speed);
if (! dev)
return;
hub->devices[portno] = dev;
/* If the device is a Hub, scan it for more devices. */
if (dev->descdev.class == 0x09)
grub_usb_add_hub (dev);
@ -297,8 +309,8 @@ grub_usb_root_hub (grub_usb_controller_t controller)
/* Query the number of ports the root Hub has. */
hub->nports = controller->dev->hubports (controller);
hub->speed = grub_malloc (sizeof (hub->speed[0]) * hub->nports);
if (!hub->speed)
hub->devices = grub_zalloc (sizeof (hub->devices[0]) * hub->nports);
if (!hub->devices)
{
grub_free (hub->controller);
grub_free (hub);
@ -307,36 +319,54 @@ grub_usb_root_hub (grub_usb_controller_t controller)
for (i = 0; i < hub->nports; i++)
{
hub->speed[i] = controller->dev->detect_dev (hub->controller, i,
&changed);
grub_usb_speed_t speed;
speed = controller->dev->detect_dev (hub->controller, i,
&changed);
if (hub->speed[i] != GRUB_USB_SPEED_NONE)
attach_root_port (hub->controller, i, hub->speed[i]);
if (speed != GRUB_USB_SPEED_NONE)
attach_root_port (hub, i, speed);
}
return GRUB_USB_ERR_NONE;
}
static void detach_device (grub_usb_device_t dev);
static void
detach_device (grub_usb_device_t dev)
{
unsigned i;
int k;
if (!dev)
return;
if (dev->descdev.class == GRUB_USB_CLASS_HUB)
{
for (i = 0; i < dev->nports; i++)
detach_device (dev->children[i]);
grub_free (dev->children);
}
for (i = 0; i < ARRAY_SIZE (dev->config); i++)
if (dev->config[i].descconf)
for (k = 0; k < dev->config[i].descconf->numif; k++)
{
struct grub_usb_interface *inter = &dev->config[i].interf[k];
if (inter && inter->detach_hook)
inter->detach_hook (dev, i, k);
}
grub_usb_devs[dev->addr] = 0;
}
static void
poll_nonroot_hub (grub_usb_device_t dev)
{
struct grub_usb_usb_hubdesc hubdesc;
grub_err_t err;
int i;
unsigned i;
grub_uint64_t timeout;
grub_usb_device_t next_dev;
err = grub_usb_control_msg (dev, (GRUB_USB_REQTYPE_IN
| GRUB_USB_REQTYPE_CLASS
| GRUB_USB_REQTYPE_TARGET_DEV),
GRUB_USB_REQ_GET_DESCRIPTOR,
(GRUB_USB_DESCRIPTOR_HUB << 8) | 0,
0, sizeof (hubdesc), (char *) &hubdesc);
if (err)
return;
grub_usb_device_t *attached_devices = dev->children;
/* Iterate over the Hub ports. */
for (i = 1; i <= hubdesc.portcnt; i++)
for (i = 1; i <= dev->nports; i++)
{
grub_uint32_t status;
@ -350,6 +380,12 @@ poll_nonroot_hub (grub_usb_device_t dev)
status. */
if (err)
continue;
if (status & GRUB_USB_HUB_STATUS_C_CONNECTED)
{
detach_device (attached_devices[i-1]);
attached_devices[i - 1] = NULL;
}
/* Connected and status of connection changed ? */
if ((status & GRUB_USB_HUB_STATUS_CONNECTED)
@ -417,6 +453,8 @@ poll_nonroot_hub (grub_usb_device_t dev)
if (! next_dev)
continue;
attached_devices[i - 1] = next_dev;
/* If the device is a Hub, scan it for more devices. */
if (next_dev->descdev.class == 0x09)
grub_usb_add_hub (next_dev);
@ -434,26 +472,23 @@ grub_usb_poll_devices (void)
for (hub = hubs; hub; hub = hub->next)
{
int changed=0;
/* Do we have to recheck number of ports? */
/* No, it should be never changed, it should be constant. */
for (i = 0; i < hub->nports; i++)
{
grub_usb_speed_t speed;
int changed = 0;
speed = hub->controller->dev->detect_dev (hub->controller, i,
&changed);
if (speed != GRUB_USB_SPEED_NONE)
if (changed)
{
if (changed)
attach_root_port (hub->controller, i, speed);
detach_device (hub->devices[i]);
hub->devices[i] = NULL;
if (speed != GRUB_USB_SPEED_NONE)
attach_root_port (hub, i, speed);
}
/* XXX: There should be also handling
* of disconnected devices. */
hub->speed[i] = speed;
}
}
@ -463,9 +498,7 @@ grub_usb_poll_devices (void)
grub_usb_device_t dev = grub_usb_devs[i];
if (dev && dev->descdev.class == 0x09)
{
poll_nonroot_hub (dev);
}
poll_nonroot_hub (dev);
}
}

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);
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)
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);
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,14 +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);
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);
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_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, actual);
}