USB hotunplugging and USB serial support.

* bus/usb/ohci.c (grub_ohci_transfer): Fill *actual and respect timeout.
	* bus/usb/uhci.c (grub_free_queue): Compute *actual.
	(grub_uhci_transfer): Respect timeout and set *actual.
	* bus/usb/usb.c (grub_usb_device_initialize): Correctly skip fields of
	non-standard length.
	(grub_usb_device_attach): Autoload modules.
	(GRUB_MOD_INIT): Set grub_term_poll_usb.
	(GRUB_MOD_FINI): Unset grub_term_poll_usb.
	* bus/usb/usbhub.c (grub_usb_hub): Replace speed with devices. All
	users updated.
	(grub_usb_add_hub): Fill nports and children.
	(attach_root_port): Receive hub instead of controller.
	All users updated. Fill hub->devices.
	(grub_usb_root_hub): Allocate hub->devices.
	(detach_device): New function.
	(poll_nonroot_hub): Fill children and detach devices.
	* bus/usb/usbtrans.c (grub_usb_bulk_readwrite): Accept timeout and
	actual arguments. All users updated.
	(grub_usb_bulk_read_extended): New function.
	* bus/usb/serial/common.c: New file.
	* bus/usb/serial/ftdi.c: Likewise.
	* bus/usb/serial/pl2303.c: Likewise.
	* commands/terminal.c (handle_command): Support wildcard.
	* commands/usbtest.c: Output "Unknown" instead of empty string.
	* conf/any-emu.rmk (pkglib_MODULES): Add usbserial_common.mod.
	(usbserial_common_mod_SOURCES): New variable.
	(usbserial_common_mod_CFLAGS): Likewise.
	(usbserial_common_mod_LDFLAGS): Likewise.
	(pkglib_MODULES): Add usbserial_pl2303.mod.
	(usbserial_pl2303_mod_SOURCES): New variable.
	(usbserial_pl2303_mod_CFLAGS): Likewise.
	(usbserial_pl2303_mod_LDFLAGS): Likewise.
	(pkglib_MODULES): Add usbserial_ftdi.mod.
	(usbserial_ftdi_mod_SOURCES): New variable.
	(usbserial_ftdi_mod_CFLAGS): Likewise.
	(usbserial_ftdi_mod_LDFLAGS): Likewise.
	(pkglib_MODULES): Add serial.mod.
	(serial_mod_SOURCES): New variable.
	(serial_mod_CFLAGS): Likewise.
	(serial_mod_LDFLAGS): Likewise.
	* conf/i386-pc.rmk: Likewise.
	* conf/mips-yeeloong.rmk: Likewise.
	* conf/i386.rmk (serial_mod_SOURCES): Add term/ns8250.c.
	* conf/mips-yeeloong.rmk (kernel_img_SOURCES): Likewise.
	* disk/usbms.c (first_available_slot): New variable.
	(grub_usbms_attach): Don't reuse free slots due to potential cache
	problems.
	* include/grub/serial.h: Moved to ..
	* include/grub/ns8250.h: ...this.
	* include/grub/serial.h: New file.
	* include/grub/term.h (grub_term_poll_usb): New variable.
	* include/grub/terminfo.h (grub_terminfo_input_state): Pass term to
	readkey. All users updated.
	(grub_terminfo_output_state): Pass term to put.
	* include/grub/usb.h (GRUB_USB_REQTYPE): New enum.
	(grub_usb_controller_dev): Add timeout and actual arguments to
	transfer. All users updated.
	(grub_usb_interface): New field detach_data.
	(grub_usb_device): New fields children and nports.
	(grub_usb_ep_type_t): New type.
	(grub_usb_get_ep_type): New function.
	(grub_usb_bulk_read_extended): Likewise.
	* include/grub/usbdesc.h (grub_usb_desc): New type.
	* include/grub/usbserial.h: New file.
	* include/grub/usbtrans.h (grub_usb_transaction): New field preceding.
	* kern/term.c (grub_term_poll_usb): New variable.
	(grub_getkey): Call grub_term_poll_usb if set.
	(grub_checkkey): Likewise.
	(grub_getkeystatus): Likewise.
	* term/serial.c: Moved controller-specific parts to ...
	* term/ns8250.c: ... here.
	* term/serial.c: Mostly rewritten.
	* term/usb_keyboard.c: Reorganised to use GET_REPORT only on attaching
	according to spec.
This commit is contained in:
Vladimir 'phcoder' Serbinenko 2010-08-20 21:34:52 +02:00
commit 05a765c88b
30 changed files with 1830 additions and 616 deletions

View file

@ -1,3 +1,82 @@
2010-08-20 Vladimir Serbinenko <phcoder@gmail.com>
USB hotunplugging and USB serial support.
* bus/usb/ohci.c (grub_ohci_transfer): Fill *actual and respect timeout.
* bus/usb/uhci.c (grub_free_queue): Compute *actual.
(grub_uhci_transfer): Respect timeout and set *actual.
* bus/usb/usb.c (grub_usb_device_initialize): Correctly skip fields of
non-standard length.
(grub_usb_device_attach): Autoload modules.
(GRUB_MOD_INIT): Set grub_term_poll_usb.
(GRUB_MOD_FINI): Unset grub_term_poll_usb.
* bus/usb/usbhub.c (grub_usb_hub): Replace speed with devices. All
users updated.
(grub_usb_add_hub): Fill nports and children.
(attach_root_port): Receive hub instead of controller.
All users updated. Fill hub->devices.
(grub_usb_root_hub): Allocate hub->devices.
(detach_device): New function.
(poll_nonroot_hub): Fill children and detach devices.
* bus/usb/usbtrans.c (grub_usb_bulk_readwrite): Accept timeout and
actual arguments. All users updated.
(grub_usb_bulk_read_extended): New function.
* bus/usb/serial/common.c: New file.
* bus/usb/serial/ftdi.c: Likewise.
* bus/usb/serial/pl2303.c: Likewise.
* commands/terminal.c (handle_command): Support wildcard.
* commands/usbtest.c: Output "Unknown" instead of empty string.
* conf/any-emu.rmk (pkglib_MODULES): Add usbserial_common.mod.
(usbserial_common_mod_SOURCES): New variable.
(usbserial_common_mod_CFLAGS): Likewise.
(usbserial_common_mod_LDFLAGS): Likewise.
(pkglib_MODULES): Add usbserial_pl2303.mod.
(usbserial_pl2303_mod_SOURCES): New variable.
(usbserial_pl2303_mod_CFLAGS): Likewise.
(usbserial_pl2303_mod_LDFLAGS): Likewise.
(pkglib_MODULES): Add usbserial_ftdi.mod.
(usbserial_ftdi_mod_SOURCES): New variable.
(usbserial_ftdi_mod_CFLAGS): Likewise.
(usbserial_ftdi_mod_LDFLAGS): Likewise.
(pkglib_MODULES): Add serial.mod.
(serial_mod_SOURCES): New variable.
(serial_mod_CFLAGS): Likewise.
(serial_mod_LDFLAGS): Likewise.
* conf/i386-pc.rmk: Likewise.
* conf/mips-yeeloong.rmk: Likewise.
* conf/i386.rmk (serial_mod_SOURCES): Add term/ns8250.c.
* conf/mips-yeeloong.rmk (kernel_img_SOURCES): Likewise.
* disk/usbms.c (first_available_slot): New variable.
(grub_usbms_attach): Don't reuse free slots due to potential cache
problems.
* include/grub/serial.h: Moved to ..
* include/grub/ns8250.h: ...this.
* include/grub/serial.h: New file.
* include/grub/term.h (grub_term_poll_usb): New variable.
* include/grub/terminfo.h (grub_terminfo_input_state): Pass term to
readkey. All users updated.
(grub_terminfo_output_state): Pass term to put.
* include/grub/usb.h (GRUB_USB_REQTYPE): New enum.
(grub_usb_controller_dev): Add timeout and actual arguments to
transfer. All users updated.
(grub_usb_interface): New field detach_data.
(grub_usb_device): New fields children and nports.
(grub_usb_ep_type_t): New type.
(grub_usb_get_ep_type): New function.
(grub_usb_bulk_read_extended): Likewise.
* include/grub/usbdesc.h (grub_usb_desc): New type.
* include/grub/usbserial.h: New file.
* include/grub/usbtrans.h (grub_usb_transaction): New field preceding.
* kern/term.c (grub_term_poll_usb): New variable.
(grub_getkey): Call grub_term_poll_usb if set.
(grub_checkkey): Likewise.
(grub_getkeystatus): Likewise.
* term/serial.c: Moved controller-specific parts to ...
* term/ns8250.c: ... here.
* term/serial.c: Mostly rewritten.
* term/usb_keyboard.c: Reorganised to use GET_REPORT only on attaching
according to spec.
2010-08-20 Robert Millan <rmh@gnu.org>
Make kFreeBSD code more generic to support ext2fs as root, ufs as

View file

@ -20,7 +20,8 @@
#include <grub/mips/yeeloong/pci.h>
#include <grub/mips/loongson.h>
#include <grub/pci.h>
#include <grub/serial.h>
#include <grub/machine/serial.h>
#include <grub/ns8250.h>
#include <grub/cs5536.h>
#include <grub/smbus.h>

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

125
bus/usb/serial/common.c Normal file
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++];
}

205
bus/usb/serial/ftdi.c Normal file
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);
}

218
bus/usb/serial/pl2303.c Normal file
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,10 +333,12 @@ 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++)
{
@ -346,6 +348,8 @@ grub_free_queue (struct grub_uhci *u, grub_uhci_td_t td,
if (transfer && (td->linkptr & 1))
transfer->last_trans = i;
*actual += (td->ctrl_status + 1) & 0x7ff;
/* Unlink the queue. */
tdprev = td;
td = (grub_uhci_td_t) td->linkptr2;
@ -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,11 +519,6 @@ 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;
@ -547,7 +549,7 @@ grub_uhci_transfer (grub_usb_controller_t dev,
/* 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,
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;
@ -351,6 +381,12 @@ poll_nonroot_hub (grub_usb_device_t dev)
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)
&& (status & GRUB_USB_HUB_STATUS_C_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)
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,10 +498,8 @@ grub_usb_poll_devices (void)
grub_usb_device_t dev = grub_usb_devs[i];
if (dev && dev->descdev.class == 0x09)
{
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);
}

View file

@ -59,11 +59,17 @@ handle_command (int argc, char **args, struct abstract_terminal **enabled,
for (aut = autoloads; aut; aut = aut->next)
{
for (term = *disabled; term; term = term->next)
if (grub_strcmp (term->name, aut->name) == 0)
if (grub_strcmp (term->name, aut->name) == 0
|| (aut->name[0] && aut->name[grub_strlen (aut->name) - 1] == '*'
&& grub_memcmp (term->name, aut->name,
grub_strlen (aut->name) - 1) == 0))
break;
if (!term)
for (term = *enabled; term; term = term->next)
if (grub_strcmp (term->name, aut->name) == 0)
if (grub_strcmp (term->name, aut->name) == 0
|| (aut->name[0] && aut->name[grub_strlen (aut->name) - 1] == '*'
&& grub_memcmp (term->name, aut->name,
grub_strlen (aut->name) - 1) == 0))
break;
if (!term)
grub_printf ("%s ", aut->name);
@ -98,7 +104,10 @@ handle_command (int argc, char **args, struct abstract_terminal **enabled,
return grub_error (GRUB_ERR_BAD_ARGUMENT, "unknown terminal '%s'\n",
args[i]);
for (aut = autoloads; aut; aut = aut->next)
if (grub_strcmp (args[i], aut->name) == 0)
if (grub_strcmp (args[i], aut->name) == 0
|| (aut->name[0] && aut->name[grub_strlen (aut->name) - 1] == '*'
&& grub_memcmp (args[i], aut->name,
grub_strlen (aut->name) - 1) == 0))
{
grub_dl_t mod;
mod = grub_dl_load (aut->modname);

View file

@ -29,11 +29,11 @@
static const char *usb_classes[] =
{
"",
"Unknown",
"Audio",
"Communication Interface",
"HID",
"",
"Unknown",
"Physical",
"Image",
"Printer",
@ -138,9 +138,9 @@ usb_iterate (grub_usb_device_t dev)
usb_print_str ("Vendor", dev, descdev->strvendor);
usb_print_str ("Serial", dev, descdev->strserial);
if (descdev->class > 0 && descdev->class <= 0x0E)
grub_printf ("Class: (0x%02x) %s, Subclass: 0x%02x, Protocol: 0x%02x\n",
descdev->class, usb_classes[descdev->class],
descdev->class, descdev->class < ARRAY_SIZE (usb_classes)
? usb_classes[descdev->class] : "Unknown",
descdev->subclass, descdev->protocol);
grub_printf ("USB version %d.%d, VendorID: 0x%02x, ProductID: 0x%02x, #conf: %d\n",
descdev->usbrel >> 8, (descdev->usbrel >> 4) & 0x0F,
@ -164,9 +164,9 @@ usb_iterate (grub_usb_device_t dev)
grub_printf ("Interface #%d: #Endpoints: %d ",
i, interf->endpointcnt);
if (interf->class > 0 && interf->class <= 0x0E)
grub_printf ("Class: (0x%02x) %s, Subclass: 0x%02x, Protocol: 0x%02x\n",
interf->class, usb_classes[interf->class],
interf->class, interf->class < ARRAY_SIZE (usb_classes)
? usb_classes[interf->class] : "Unknown",
interf->subclass, interf->protocol);
usb_print_str ("Interface", dev, interf->strif);

View file

@ -70,6 +70,30 @@ usbms_mod_SOURCES = disk/usbms.c
usbms_mod_CFLAGS = $(COMMON_CFLAGS)
usbms_mod_LDFLAGS = $(COMMON_LDFLAGS)
# For serial.mod.
pkglib_MODULES += usbserial_common.mod
usbserial_common_mod_SOURCES = bus/usb/serial/common.c
usbserial_common_mod_CFLAGS = $(COMMON_CFLAGS)
usbserial_common_mod_LDFLAGS = $(COMMON_LDFLAGS)
# For serial.mod.
pkglib_MODULES += usbserial_pl2303.mod
usbserial_pl2303_mod_SOURCES = bus/usb/serial/pl2303.c
usbserial_pl2303_mod_CFLAGS = $(COMMON_CFLAGS)
usbserial_pl2303_mod_LDFLAGS = $(COMMON_LDFLAGS)
# For serial.mod.
pkglib_MODULES += usbserial_ftdi.mod
usbserial_ftdi_mod_SOURCES = bus/usb/serial/ftdi.c
usbserial_ftdi_mod_CFLAGS = $(COMMON_CFLAGS)
usbserial_ftdi_mod_LDFLAGS = $(COMMON_LDFLAGS)
# For serial.mod.
pkglib_MODULES += serial.mod
serial_mod_SOURCES = term/serial.c
serial_mod_CFLAGS = $(COMMON_CFLAGS)
serial_mod_LDFLAGS = $(COMMON_LDFLAGS)
grub_emu_LDFLAGS += $(LIBUSB)
endif

View file

@ -195,6 +195,24 @@ usb_mod_SOURCES = bus/usb/usb.c bus/usb/usbtrans.c bus/usb/usbhub.c
usb_mod_CFLAGS = $(COMMON_CFLAGS)
usb_mod_LDFLAGS = $(COMMON_LDFLAGS)
# For serial.mod.
pkglib_MODULES += usbserial_common.mod
usbserial_common_mod_SOURCES = bus/usb/serial/common.c
usbserial_common_mod_CFLAGS = $(COMMON_CFLAGS)
usbserial_common_mod_LDFLAGS = $(COMMON_LDFLAGS)
# For serial.mod.
pkglib_MODULES += usbserial_pl2303.mod
usbserial_pl2303_mod_SOURCES = bus/usb/serial/pl2303.c
usbserial_pl2303_mod_CFLAGS = $(COMMON_CFLAGS)
usbserial_pl2303_mod_LDFLAGS = $(COMMON_LDFLAGS)
# For serial.mod.
pkglib_MODULES += usbserial_ftdi.mod
usbserial_ftdi_mod_SOURCES = bus/usb/serial/ftdi.c
usbserial_ftdi_mod_CFLAGS = $(COMMON_CFLAGS)
usbserial_ftdi_mod_LDFLAGS = $(COMMON_LDFLAGS)
# For usbtest.mod
usbtest_mod_SOURCES = commands/usbtest.c
usbtest_mod_CFLAGS = $(COMMON_CFLAGS)

View file

@ -56,7 +56,7 @@ multiboot2_mod_ASFLAGS = $(COMMON_ASFLAGS)
# For serial.mod.
pkglib_MODULES += serial.mod
serial_mod_SOURCES = term/serial.c
serial_mod_SOURCES = term/serial.c term/ns8250.c
serial_mod_CFLAGS = $(COMMON_CFLAGS)
serial_mod_LDFLAGS = $(COMMON_LDFLAGS)

View file

@ -5,7 +5,7 @@ COMMON_CFLAGS += -march=mips3
COMMON_ASFLAGS += -march=mips3
kernel_img_HEADERS += pci.h bitmap.h video.h gfxterm.h font.h \
bitmap_scale.h bufio.h cs5536.h machine/pci.h
bitmap_scale.h bufio.h cs5536.h machine/pci.h serial.h
include $(srcdir)/conf/mips.mk
@ -27,7 +27,7 @@ kernel_img_SOURCES = kern/$(target_cpu)/startup.S \
video/fb/fbfill.c video/fb/fbutil.c video/bitmap.c \
video/bitmap_scale.c video/sm712.c bus/pci.c bus/bonito.c \
term/gfxterm.c commands/extcmd.c lib/arg.c \
bus/cs5536.c term/serial.c term/terminfo.c term/tparm.c \
bus/cs5536.c term/serial.c term/ns8250.c term/terminfo.c term/tparm.c \
symlist.c
kernel_img_CFLAGS = $(COMMON_CFLAGS) -DUSE_ASCII_FAILBACK
kernel_img_ASFLAGS = $(COMMON_ASFLAGS)
@ -97,6 +97,24 @@ usb_mod_SOURCES = bus/usb/usb.c bus/usb/usbtrans.c bus/usb/usbhub.c
usb_mod_CFLAGS = $(COMMON_CFLAGS)
usb_mod_LDFLAGS = $(COMMON_LDFLAGS)
# For serial.mod.
pkglib_MODULES += usbserial_common.mod
usbserial_common_mod_SOURCES = bus/usb/serial/common.c
usbserial_common_mod_CFLAGS = $(COMMON_CFLAGS)
usbserial_common_mod_LDFLAGS = $(COMMON_LDFLAGS)
# For serial.mod.
pkglib_MODULES += usbserial_pl2303.mod
usbserial_pl2303_mod_SOURCES = bus/usb/serial/pl2303.c
usbserial_pl2303_mod_CFLAGS = $(COMMON_CFLAGS)
usbserial_pl2303_mod_LDFLAGS = $(COMMON_LDFLAGS)
# For serial.mod.
pkglib_MODULES += usbserial_ftdi.mod
usbserial_ftdi_mod_SOURCES = bus/usb/serial/ftdi.c
usbserial_ftdi_mod_CFLAGS = $(COMMON_CFLAGS)
usbserial_ftdi_mod_LDFLAGS = $(COMMON_LDFLAGS)
# For usbtest.mod
pkglib_MODULES += usbtest.mod
usbtest_mod_SOURCES = commands/usbtest.c

View file

@ -65,6 +65,7 @@ typedef struct grub_usbms_dev *grub_usbms_dev_t;
/* FIXME: remove limit. */
#define MAX_USBMS_DEVICES 128
static grub_usbms_dev_t grub_usbms_devices[MAX_USBMS_DEVICES];
static int first_available_slot = 0;
static grub_err_t
grub_usbms_reset (grub_usb_device_t dev, int interface)
@ -96,13 +97,12 @@ grub_usbms_attach (grub_usb_device_t usbdev, int configno, int interfno)
unsigned curnum;
grub_usb_err_t err;
for (curnum = 0; curnum < ARRAY_SIZE (grub_usbms_devices); curnum++)
if (!grub_usbms_devices[curnum])
break;
if (curnum == ARRAY_SIZE (grub_usbms_devices))
if (first_available_slot == ARRAY_SIZE (grub_usbms_devices))
return 0;
curnum = first_available_slot;
first_available_slot++;
interf = usbdev->config[configno].interf[interfno].descif;
if ((interf->subclass != GRUB_USBMS_SUBCLASS_BULK

73
include/grub/ns8250.h Normal file
View file

@ -0,0 +1,73 @@
/* serial.h - serial device interface */
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2000,2001,2002,2005,2007 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/>.
*/
#ifndef GRUB_NS8250_HEADER
#define GRUB_NS8250_HEADER 1
/* Macros. */
/* The offsets of UART registers. */
#define UART_TX 0
#define UART_RX 0
#define UART_DLL 0
#define UART_IER 1
#define UART_DLH 1
#define UART_IIR 2
#define UART_FCR 2
#define UART_LCR 3
#define UART_MCR 4
#define UART_LSR 5
#define UART_MSR 6
#define UART_SR 7
/* For LSR bits. */
#define UART_DATA_READY 0x01
#define UART_EMPTY_TRANSMITTER 0x20
/* The type of parity. */
#define UART_NO_PARITY 0x00
#define UART_ODD_PARITY 0x08
#define UART_EVEN_PARITY 0x18
/* The type of word length. */
#define UART_5BITS_WORD 0x00
#define UART_6BITS_WORD 0x01
#define UART_7BITS_WORD 0x02
#define UART_8BITS_WORD 0x03
/* The type of the length of stop bit. */
#define UART_1_STOP_BIT 0x00
#define UART_2_STOP_BITS 0x04
/* the switch of DLAB. */
#define UART_DLAB 0x80
/* Enable the FIFO. */
#define UART_ENABLE_FIFO_TRIGGER14 0xC7
/* Enable the FIFO. */
#define UART_ENABLE_FIFO_TRIGGER1 0x07
/* Turn on DTR, RTS, and OUT2. */
#define UART_ENABLE_DTRRTS 0x03
/* Turn on DTR, RTS, and OUT2. */
#define UART_ENABLE_OUT2 0x08
#endif /* ! GRUB_SERIAL_MACHINE_HEADER */

View file

@ -1,7 +1,7 @@
/* serial.h - serial device interface */
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2000,2001,2002,2005,2007 Free Software Foundation, Inc.
* Copyright (C) 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
@ -20,54 +20,100 @@
#ifndef GRUB_SERIAL_HEADER
#define GRUB_SERIAL_HEADER 1
/* Macros. */
#include <grub/types.h>
#include <grub/cpu/io.h>
#include <grub/usb.h>
#include <grub/list.h>
#include <grub/term.h>
/* The offsets of UART registers. */
#define UART_TX 0
#define UART_RX 0
#define UART_DLL 0
#define UART_IER 1
#define UART_DLH 1
#define UART_IIR 2
#define UART_FCR 2
#define UART_LCR 3
#define UART_MCR 4
#define UART_LSR 5
#define UART_MSR 6
#define UART_SR 7
struct grub_serial_port;
struct grub_serial_config;
/* For LSR bits. */
#define UART_DATA_READY 0x01
#define UART_EMPTY_TRANSMITTER 0x20
struct grub_serial_driver
{
grub_err_t (*configure) (struct grub_serial_port *port,
struct grub_serial_config *config);
int (*fetch) (struct grub_serial_port *port);
void (*put) (struct grub_serial_port *port, const int c);
void (*fini) (struct grub_serial_port *port);
};
/* The type of parity. */
#define UART_NO_PARITY 0x00
#define UART_ODD_PARITY 0x08
#define UART_EVEN_PARITY 0x18
typedef enum
{
GRUB_SERIAL_PARITY_NONE,
GRUB_SERIAL_PARITY_ODD,
GRUB_SERIAL_PARITY_EVEN,
} grub_serial_parity_t;
/* The type of word length. */
#define UART_5BITS_WORD 0x00
#define UART_6BITS_WORD 0x01
#define UART_7BITS_WORD 0x02
#define UART_8BITS_WORD 0x03
typedef enum
{
GRUB_SERIAL_STOP_BITS_1,
GRUB_SERIAL_STOP_BITS_2,
} grub_serial_stop_bits_t;
/* The type of the length of stop bit. */
#define UART_1_STOP_BIT 0x00
#define UART_2_STOP_BITS 0x04
struct grub_serial_config
{
unsigned speed;
int word_len;
grub_serial_parity_t parity;
grub_serial_stop_bits_t stop_bits;
};
/* the switch of DLAB. */
#define UART_DLAB 0x80
struct grub_serial_port
{
struct grub_serial_port *next;
char *name;
struct grub_serial_driver *driver;
struct grub_serial_config config;
int configured;
/* This should be void *data but since serial is useful as an early console
when malloc isn't available it's a union.
*/
union
{
grub_port_t port;
struct
{
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;
};
};
grub_term_output_t term_out;
grub_term_input_t term_in;
};
/* Enable the FIFO. */
#define UART_ENABLE_FIFO_TRIGGER14 0xC7
grub_err_t EXPORT_FUNC(grub_serial_register) (struct grub_serial_port *port);
/* Enable the FIFO. */
#define UART_ENABLE_FIFO_TRIGGER1 0x07
void EXPORT_FUNC(grub_serial_unregister) (struct grub_serial_port *port);
/* Turn on DTR, RTS, and OUT2. */
#define UART_ENABLE_DTRRTS 0x03
/* Set default settings. */
static inline grub_err_t
grub_serial_config_defaults (struct grub_serial_port *port)
{
struct grub_serial_config config =
{
#ifdef GRUB_MACHINE_MIPS_YEELOONG
.speed = 115200,
#else
.speed = 9600,
#endif
.word_len = 8,
.parity = GRUB_SERIAL_PARITY_NONE,
.stop_bits = GRUB_SERIAL_STOP_BITS_1
};
/* Turn on DTR, RTS, and OUT2. */
#define UART_ENABLE_OUT2 0x08
return port->driver->configure (port, &config);
}
#endif /* ! GRUB_SERIAL_MACHINE_HEADER */
void grub_ns8250_init (void);
char *grub_serial_ns8250_add_port (grub_port_t port);
extern struct grub_serial_driver grub_ns8250_driver;
void EXPORT_FUNC(grub_serial_unregister_driver) (struct grub_serial_driver *driver);
#endif

View file

@ -459,6 +459,7 @@ grub_print_spaces (struct grub_term_output *term, int number_spaces)
grub_putcode (' ', term);
}
extern void (*EXPORT_VAR (grub_term_poll_usb)) (void);
/* For convenience. */
#define GRUB_TERM_ASCII_CHAR(c) ((c) & 0xff)

View file

@ -32,7 +32,7 @@ struct grub_terminfo_input_state
{
int input_buf[GRUB_TERMINFO_READKEY_MAX_LEN];
int npending;
int (*readkey) (void);
int (*readkey) (struct grub_term_input *term);
};
struct grub_terminfo_output_state
@ -51,7 +51,7 @@ struct grub_terminfo_output_state
unsigned int xpos, ypos;
void (*put) (const int c);
void (*put) (struct grub_term_output *term, const int c);
};
void EXPORT_FUNC(grub_terminfo_gotoxy) (grub_term_output_t term,

View file

@ -19,6 +19,7 @@
#ifndef GRUB_USB_H
#define GRUB_USB_H 1
#include <grub/err.h>
#include <grub/usbdesc.h>
#include <grub/usbtrans.h>
@ -47,6 +48,14 @@ typedef enum
GRUB_USB_SPEED_HIGH
} grub_usb_speed_t;
enum
{
GRUB_USB_REQTYPE_CLASS_INTERFACE_OUT = 0x21,
GRUB_USB_REQTYPE_VENDOR_OUT = 0x40,
GRUB_USB_REQTYPE_CLASS_INTERFACE_IN = 0xa1,
GRUB_USB_REQTYPE_VENDOR_IN = 0xc0
};
/* Call HOOK with each device, until HOOK returns non-zero. */
int grub_usb_iterate (int (*hook) (grub_usb_device_t dev));
@ -97,7 +106,8 @@ struct grub_usb_controller_dev
int (*iterate) (int (*hook) (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, grub_size_t *actual);
int (*hubports) (grub_usb_controller_t dev);
@ -132,6 +142,8 @@ struct grub_usb_interface
int attached;
void (*detach_hook) (struct grub_usb_device *dev, int config, int interface);
void *detach_data;
};
struct grub_usb_configuration
@ -166,12 +178,29 @@ struct grub_usb_device
/* Data toggle values (used for bulk transfers only). */
int toggle[256];
/* Device-specific data. */
void *data;
/* Array of children for a hub. */
grub_usb_device_t *children;
/* Number of hub ports. */
unsigned nports;
};
typedef enum grub_usb_ep_type
{
GRUB_USB_EP_CONTROL,
GRUB_USB_EP_ISOCHRONOUS,
GRUB_USB_EP_BULK,
GRUB_USB_EP_INTERRUPT
} grub_usb_ep_type_t;
static inline enum grub_usb_ep_type
grub_usb_get_ep_type (struct grub_usb_desc_endp *ep)
{
return ep->attrib & 3;
}
typedef enum
{
GRUB_USB_CLASS_NOTHERE,
@ -230,5 +259,9 @@ void grub_usb_unregister_attach_hook_class (struct grub_usb_attach_desc *desc);
void grub_usb_poll_devices (void);
void grub_usb_device_attach (grub_usb_device_t dev);
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);
#endif /* GRUB_USB_H */

View file

@ -31,6 +31,12 @@ typedef enum {
GRUB_USB_DESCRIPTOR_HUB = 0x29
} grub_usb_descriptor_t;
struct grub_usb_desc
{
grub_uint8_t length;
grub_uint8_t type;
} __attribute__ ((packed));
struct grub_usb_desc_device
{
grub_uint8_t length;

34
include/grub/usbserial.h Normal file
View file

@ -0,0 +1,34 @@
/* serial.h - serial device interface */
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 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/>.
*/
#ifndef GRUB_USBSERIAL_HEADER
#define GRUB_USBSERIAL_HEADER 1
void grub_usbserial_fini (struct grub_serial_port *port);
void grub_usbserial_detach (grub_usb_device_t usbdev, int configno,
int interfno);
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;

View file

@ -28,6 +28,8 @@ struct grub_term_input *grub_term_inputs_disabled;
struct grub_term_output *grub_term_outputs;
struct grub_term_input *grub_term_inputs;
void (*grub_term_poll_usb) (void) = NULL;
/* Put a Unicode character. */
static void
grub_putcode_dumb (grub_uint32_t code,
@ -85,6 +87,9 @@ grub_getkey (void)
while (1)
{
if (grub_term_poll_usb)
grub_term_poll_usb ();
FOR_ACTIVE_TERM_INPUTS(term)
{
int key = term->checkkey (term);
@ -101,6 +106,9 @@ grub_checkkey (void)
{
grub_term_input_t term;
if (grub_term_poll_usb)
grub_term_poll_usb ();
FOR_ACTIVE_TERM_INPUTS(term)
{
int key = term->checkkey (term);
@ -117,6 +125,9 @@ grub_getkeystatus (void)
int status = 0;
grub_term_input_t term;
if (grub_term_poll_usb)
grub_term_poll_usb ();
FOR_ACTIVE_TERM_INPUTS(term)
{
if (term->getkeystatus)

253
term/ns8250.c Normal file
View file

@ -0,0 +1,253 @@
/*
* 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/machine/memory.h>
#include <grub/serial.h>
#include <grub/ns8250.h>
#include <grub/types.h>
#include <grub/dl.h>
#include <grub/misc.h>
#include <grub/cpu/io.h>
#include <grub/mm.h>
#ifdef GRUB_MACHINE_PCBIOS
static const unsigned short *serial_hw_io_addr = (const unsigned short *) GRUB_MEMORY_MACHINE_BIOS_DATA_AREA_ADDR;
#define GRUB_SERIAL_PORT_NUM 4
#else
#include <grub/machine/serial.h>
static const grub_port_t serial_hw_io_addr[] = GRUB_MACHINE_SERIAL_PORTS;
#define GRUB_SERIAL_PORT_NUM (ARRAY_SIZE(serial_hw_io_addr))
#endif
/* Convert speed to divisor. */
static unsigned short
serial_get_divisor (unsigned int speed)
{
unsigned int i;
/* The structure for speed vs. divisor. */
struct divisor
{
unsigned int speed;
unsigned short div;
};
/* The table which lists common configurations. */
/* 1843200 / (speed * 16) */
static struct divisor divisor_tab[] =
{
{ 2400, 0x0030 },
{ 4800, 0x0018 },
{ 9600, 0x000C },
{ 19200, 0x0006 },
{ 38400, 0x0003 },
{ 57600, 0x0002 },
{ 115200, 0x0001 }
};
/* Set the baud rate. */
for (i = 0; i < ARRAY_SIZE (divisor_tab); i++)
if (divisor_tab[i].speed == speed)
/* UART in Yeeloong runs twice the usual rate. */
#ifdef GRUB_MACHINE_MIPS_YEELOONG
return 2 * divisor_tab[i].div;
#else
return divisor_tab[i].div;
#endif
return 0;
}
static void
do_real_config (struct grub_serial_port *port)
{
int divisor;
unsigned char status = 0;
const unsigned char parities[] = {
[GRUB_SERIAL_PARITY_NONE] = UART_NO_PARITY,
[GRUB_SERIAL_PARITY_ODD] = UART_ODD_PARITY,
[GRUB_SERIAL_PARITY_EVEN] = UART_EVEN_PARITY
};
const unsigned char stop_bits[] = {
[GRUB_SERIAL_STOP_BITS_1] = UART_1_STOP_BIT,
[GRUB_SERIAL_STOP_BITS_2] = UART_2_STOP_BITS,
};
if (port->configured)
return;
divisor = serial_get_divisor (port->config.speed);
/* Turn off the interrupt. */
grub_outb (0, port->port + UART_IER);
/* Set DLAB. */
grub_outb (UART_DLAB, port->port + UART_LCR);
/* Set the baud rate. */
grub_outb (divisor & 0xFF, port->port + UART_DLL);
grub_outb (divisor >> 8, port->port + UART_DLH);
/* Set the line status. */
status |= (parities[port->config.parity]
| (port->config.word_len - 5)
| stop_bits[port->config.stop_bits]);
grub_outb (status, port->port + UART_LCR);
/* In Yeeloong serial port has only 3 wires. */
#ifndef GRUB_MACHINE_MIPS_YEELOONG
/* Enable the FIFO. */
grub_outb (UART_ENABLE_FIFO_TRIGGER1, port->port + UART_FCR);
/* Turn on DTR and RTS. */
grub_outb (UART_ENABLE_DTRRTS, port->port + UART_MCR);
#else
/* Enable the FIFO. */
grub_outb (UART_ENABLE_FIFO_TRIGGER14, port->port + UART_FCR);
/* Turn on DTR, RTS, and OUT2. */
grub_outb (UART_ENABLE_DTRRTS | UART_ENABLE_OUT2, port->port + UART_MCR);
#endif
/* Drain the input buffer. */
while (grub_inb (port->port + UART_LSR) & UART_DATA_READY)
grub_inb (port->port + UART_RX);
port->configured = 1;
}
/* Fetch a key. */
static int
serial_hw_fetch (struct grub_serial_port *port)
{
do_real_config (port);
if (grub_inb (port->port + UART_LSR) & UART_DATA_READY)
return grub_inb (port->port + UART_RX);
return -1;
}
/* Put a character. */
static void
serial_hw_put (struct grub_serial_port *port, const int c)
{
unsigned int timeout = 100000;
do_real_config (port);
/* Wait until the transmitter holding register is empty. */
while ((grub_inb (port->port + UART_LSR) & UART_EMPTY_TRANSMITTER) == 0)
{
if (--timeout == 0)
/* There is something wrong. But what can I do? */
return;
}
grub_outb (c, port->port + UART_TX);
}
/* Initialize a serial device. PORT is the port number for a serial device.
SPEED is a DTE-DTE speed which must be one of these: 2400, 4800, 9600,
19200, 38400, 57600 and 115200. WORD_LEN is the word length to be used
for the device. Likewise, PARITY is the type of the parity and
STOP_BIT_LEN is the length of the stop bit. The possible values for
WORD_LEN, PARITY and STOP_BIT_LEN are defined in the header file as
macros. */
static grub_err_t
serial_hw_configure (struct grub_serial_port *port,
struct grub_serial_config *config)
{
unsigned short divisor;
divisor = serial_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;
/* FIXME: should check if the serial terminal was found. */
return GRUB_ERR_NONE;
}
struct grub_serial_driver grub_ns8250_driver =
{
.configure = serial_hw_configure,
.fetch = serial_hw_fetch,
.put = serial_hw_put
};
static char com_names[GRUB_SERIAL_PORT_NUM][20];
static struct grub_serial_port com_ports[GRUB_SERIAL_PORT_NUM];
void
grub_ns8250_init (void)
{
unsigned i;
for (i = 0; i < GRUB_SERIAL_PORT_NUM; i++)
if (serial_hw_io_addr[i])
{
grub_err_t err;
grub_snprintf (com_names[i], sizeof (com_names[i]), "com%d", i);
com_ports[i].name = com_names[i];
com_ports[i].driver = &grub_ns8250_driver;
com_ports[i].port = serial_hw_io_addr[i];
err = grub_serial_config_defaults (&com_ports[i]);
if (err)
grub_print_error ();
grub_serial_register (&com_ports[i]);
}
}
char *
grub_serial_ns8250_add_port (grub_port_t port)
{
struct grub_serial_port *p;
unsigned i;
for (i = 0; i < GRUB_SERIAL_PORT_NUM; i++)
if (com_ports[i].port == port)
return com_names[i];
p = grub_malloc (sizeof (*p));
if (!p)
return NULL;
p->name = grub_xasprintf ("port%lx", (unsigned long) port);
if (!p->name)
{
grub_free (p);
return NULL;
}
p->driver = &grub_ns8250_driver;
grub_serial_config_defaults (p);
p->port = port;
grub_serial_register (p);
return p->name;
}

View file

@ -1,6 +1,6 @@
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2000,2001,2002,2003,2004,2005,2007,2008,2009 Free Software Foundation, Inc.
* 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
@ -16,7 +16,6 @@
* along with GRUB. If not, see <http://www.gnu.org/licenses/>.
*/
#include <grub/machine/memory.h>
#include <grub/serial.h>
#include <grub/term.h>
#include <grub/types.h>
@ -26,8 +25,9 @@
#include <grub/cpu/io.h>
#include <grub/extcmd.h>
#include <grub/i18n.h>
#include <grub/list.h>
static unsigned int registered = 0;
#define FOR_SERIAL_PORTS(var) FOR_LIST_ELEMENTS((var), (grub_serial_ports))
/* Argument options. */
static const struct grub_arg_option options[] =
@ -41,155 +41,20 @@ static const struct grub_arg_option options[] =
{0, 0, 0, 0, 0, 0}
};
/* Serial port settings. */
struct serial_port
struct grub_serial_port *grub_serial_ports;
struct grub_serial_output_state
{
grub_port_t port;
unsigned short divisor;
unsigned short word_len;
unsigned int parity;
unsigned short stop_bits;
struct grub_terminfo_output_state tinfo;
struct grub_serial_port *port;
};
/* Serial port settings. */
static struct serial_port serial_settings;
#ifdef GRUB_MACHINE_PCBIOS
static const unsigned short *serial_hw_io_addr = (const unsigned short *) GRUB_MEMORY_MACHINE_BIOS_DATA_AREA_ADDR;
#define GRUB_SERIAL_PORT_NUM 4
#else
#include <grub/machine/serial.h>
static const grub_port_t serial_hw_io_addr[] = GRUB_MACHINE_SERIAL_PORTS;
#define GRUB_SERIAL_PORT_NUM (ARRAY_SIZE(serial_hw_io_addr))
#endif
/* Return the port number for the UNITth serial device. */
static inline grub_port_t
serial_hw_get_port (const unsigned int unit)
struct grub_serial_input_state
{
if (unit < GRUB_SERIAL_PORT_NUM)
return serial_hw_io_addr[unit];
else
return 0;
}
/* Fetch a key. */
static int
serial_hw_fetch (void)
{
if (grub_inb (serial_settings.port + UART_LSR) & UART_DATA_READY)
return grub_inb (serial_settings.port + UART_RX);
return -1;
}
/* Put a character. */
static void
serial_hw_put (const int c)
{
unsigned int timeout = 100000;
/* Wait until the transmitter holding register is empty. */
while ((grub_inb (serial_settings.port + UART_LSR) & UART_EMPTY_TRANSMITTER) == 0)
{
if (--timeout == 0)
/* There is something wrong. But what can I do? */
return;
}
grub_outb (c, serial_settings.port + UART_TX);
}
/* Convert speed to divisor. */
static unsigned short
serial_get_divisor (unsigned int speed)
{
unsigned int i;
/* The structure for speed vs. divisor. */
struct divisor
{
unsigned int speed;
unsigned short div;
struct grub_terminfo_input_state tinfo;
struct grub_serial_port *port;
};
/* The table which lists common configurations. */
/* 1843200 / (speed * 16) */
static struct divisor divisor_tab[] =
{
{ 2400, 0x0030 },
{ 4800, 0x0018 },
{ 9600, 0x000C },
{ 19200, 0x0006 },
{ 38400, 0x0003 },
{ 57600, 0x0002 },
{ 115200, 0x0001 }
};
/* Set the baud rate. */
for (i = 0; i < sizeof (divisor_tab) / sizeof (divisor_tab[0]); i++)
if (divisor_tab[i].speed == speed)
/* UART in Yeeloong runs twice the usual rate. */
#ifdef GRUB_MACHINE_MIPS_YEELOONG
return 2 * divisor_tab[i].div;
#else
return divisor_tab[i].div;
#endif
return 0;
}
/* Initialize a serial device. PORT is the port number for a serial device.
SPEED is a DTE-DTE speed which must be one of these: 2400, 4800, 9600,
19200, 38400, 57600 and 115200. WORD_LEN is the word length to be used
for the device. Likewise, PARITY is the type of the parity and
STOP_BIT_LEN is the length of the stop bit. The possible values for
WORD_LEN, PARITY and STOP_BIT_LEN are defined in the header file as
macros. */
static grub_err_t
serial_hw_init (void)
{
unsigned char status = 0;
/* Turn off the interrupt. */
grub_outb (0, serial_settings.port + UART_IER);
/* Set DLAB. */
grub_outb (UART_DLAB, serial_settings.port + UART_LCR);
/* Set the baud rate. */
grub_outb (serial_settings.divisor & 0xFF, serial_settings.port + UART_DLL);
grub_outb (serial_settings.divisor >> 8, serial_settings.port + UART_DLH);
/* Set the line status. */
status |= (serial_settings.parity
| serial_settings.word_len
| serial_settings.stop_bits);
grub_outb (status, serial_settings.port + UART_LCR);
/* In Yeeloong serial port has only 3 wires. */
#ifndef GRUB_MACHINE_MIPS_YEELOONG
/* Enable the FIFO. */
grub_outb (UART_ENABLE_FIFO_TRIGGER1, serial_settings.port + UART_FCR);
/* Turn on DTR and RTS. */
grub_outb (UART_ENABLE_DTRRTS, serial_settings.port + UART_MCR);
#else
/* Enable the FIFO. */
grub_outb (UART_ENABLE_FIFO_TRIGGER14, serial_settings.port + UART_FCR);
/* Turn on DTR, RTS, and OUT2. */
grub_outb (UART_ENABLE_DTRRTS | UART_ENABLE_OUT2,
serial_settings.port + UART_MCR);
#endif
/* Drain the input buffer. */
while (serial_hw_fetch () != -1);
/* FIXME: should check if the serial terminal was found. */
return GRUB_ERR_NONE;
}
static grub_uint16_t
grub_serial_getwh (struct grub_term_output *term __attribute__ ((unused)))
{
@ -198,16 +63,38 @@ grub_serial_getwh (struct grub_term_output *term __attribute__ ((unused)))
return (TEXT_WIDTH << 8) | TEXT_HEIGHT;
}
struct grub_terminfo_input_state grub_serial_terminfo_input =
static void
serial_put (grub_term_output_t term, const int c)
{
.readkey = serial_hw_fetch
struct grub_serial_output_state *data = term->data;
data->port->driver->put (data->port, c);
}
static int
serial_fetch (grub_term_input_t term)
{
struct grub_serial_input_state *data = term->data;
return data->port->driver->fetch (data->port);
}
struct grub_serial_input_state grub_serial_terminfo_input =
{
.tinfo =
{
.readkey = serial_fetch
}
};
struct grub_terminfo_output_state grub_serial_terminfo_output =
struct grub_serial_output_state grub_serial_terminfo_output =
{
.put = serial_hw_put
.tinfo =
{
.put = serial_put
}
};
int registered = 0;
static struct grub_term_input grub_serial_term_input =
{
.name = "serial",
@ -235,120 +122,219 @@ static struct grub_term_output grub_serial_term_output =
static struct grub_serial_port *
grub_serial_find (char *name)
{
struct grub_serial_port *port;
FOR_SERIAL_PORTS (port)
if (grub_strcmp (port->name, name) == 0)
break;
#ifndef GRUB_MACHINE_EMU
if (!port && grub_memcmp (name, "port", sizeof ("port") - 1) == 0
&& grub_isdigit (name [sizeof ("port") - 1]))
{
name = grub_serial_ns8250_add_port (grub_strtoul (&name[sizeof ("port") - 1],
0, 16));
if (!name)
return NULL;
FOR_SERIAL_PORTS (port)
if (grub_strcmp (port->name, name) == 0)
break;
}
#endif
return port;
}
static grub_err_t
grub_cmd_serial (grub_extcmd_t cmd,
int argc __attribute__ ((unused)),
char **args __attribute__ ((unused)))
grub_cmd_serial (grub_extcmd_t cmd, int argc, char **args)
{
struct grub_arg_list *state = cmd->state;
struct serial_port backup_settings = serial_settings;
grub_err_t hwiniterr;
char pname[40];
char *name = NULL;
struct grub_serial_port *port;
struct grub_serial_config config;
grub_err_t err;
if (state[0].set)
{
unsigned int unit;
unit = grub_strtoul (state[0].arg, 0, 0);
serial_settings.port = serial_hw_get_port (unit);
if (!serial_settings.port)
return grub_error (GRUB_ERR_BAD_ARGUMENT, "bad unit number");
grub_snprintf (pname, sizeof (pname), "com%ld",
grub_strtoul (state[0].arg, 0, 0));
name = pname;
}
if (state[1].set)
serial_settings.port = (grub_port_t) grub_strtoul (state[1].arg, 0, 0);
{
grub_snprintf (pname, sizeof (pname), "port%lx",
grub_strtoul (state[1].arg, 0, 0));
name = pname;
}
if (argc >= 1)
name = args[0];
if (!name)
name = "com0";
port = grub_serial_find (name);
if (!port)
return grub_error (GRUB_ERR_BAD_ARGUMENT, "unknown serial port");
config = port->config;
if (state[2].set)
{
unsigned long speed;
speed = grub_strtoul (state[2].arg, 0, 0);
serial_settings.divisor = serial_get_divisor ((unsigned int) speed);
if (serial_settings.divisor == 0)
{
serial_settings = backup_settings;
return grub_error (GRUB_ERR_BAD_ARGUMENT, "bad speed");
}
}
config.speed = grub_strtoul (state[2].arg, 0, 0);
if (state[3].set)
{
if (! grub_strcmp (state[3].arg, "5"))
serial_settings.word_len = UART_5BITS_WORD;
else if (! grub_strcmp (state[3].arg, "6"))
serial_settings.word_len = UART_6BITS_WORD;
else if (! grub_strcmp (state[3].arg, "7"))
serial_settings.word_len = UART_7BITS_WORD;
else if (! grub_strcmp (state[3].arg, "8"))
serial_settings.word_len = UART_8BITS_WORD;
else
{
serial_settings = backup_settings;
return grub_error (GRUB_ERR_BAD_ARGUMENT, "bad word length");
}
}
config.word_len = grub_strtoul (state[3].arg, 0, 0);
if (state[4].set)
{
if (! grub_strcmp (state[4].arg, "no"))
serial_settings.parity = UART_NO_PARITY;
config.parity = GRUB_SERIAL_PARITY_NONE;
else if (! grub_strcmp (state[4].arg, "odd"))
serial_settings.parity = UART_ODD_PARITY;
config.parity = GRUB_SERIAL_PARITY_ODD;
else if (! grub_strcmp (state[4].arg, "even"))
serial_settings.parity = UART_EVEN_PARITY;
config.parity = GRUB_SERIAL_PARITY_EVEN;
else
{
serial_settings = backup_settings;
return grub_error (GRUB_ERR_BAD_ARGUMENT, "bad parity");
}
}
if (state[5].set)
{
if (! grub_strcmp (state[5].arg, "1"))
serial_settings.stop_bits = UART_1_STOP_BIT;
config.stop_bits = GRUB_SERIAL_STOP_BITS_1;
else if (! grub_strcmp (state[5].arg, "2"))
serial_settings.stop_bits = UART_2_STOP_BITS;
config.stop_bits = GRUB_SERIAL_STOP_BITS_2;
else
{
serial_settings = backup_settings;
return grub_error (GRUB_ERR_BAD_ARGUMENT, "bad number of stop bits");
}
}
/* Initialize with new settings. */
hwiniterr = serial_hw_init ();
if (hwiniterr == GRUB_ERR_NONE)
err = port->driver->configure (port, &config);
if (err)
return err;
#ifndef GRUB_MACHINE_EMU
/* Compatibility kludge. */
if (port->driver == &grub_ns8250_driver)
{
/* Register terminal if not yet registered. */
if (registered == 0)
if (!registered)
{
grub_term_register_input ("serial", &grub_serial_term_input);
grub_term_register_output ("serial", &grub_serial_term_output);
grub_terminfo_output_register (&grub_serial_term_output, "vt100");
}
grub_serial_terminfo_output.port = port;
grub_serial_terminfo_input.port = port;
registered = 1;
}
}
else
{
/* Initialization with new settings failed. */
if (registered == 1)
{
/* If the terminal is registered, attempt to restore previous
settings. */
serial_settings = backup_settings;
if (serial_hw_init () != GRUB_ERR_NONE)
{
/* If unable to restore settings, unregister terminal. */
grub_term_unregister_input (&grub_serial_term_input);
grub_term_unregister_output (&grub_serial_term_output);
grub_terminfo_output_unregister (&grub_serial_term_output);
registered = 0;
}
}
#endif
return GRUB_ERR_NONE;
}
return hwiniterr;
grub_err_t
grub_serial_register (struct grub_serial_port *port)
{
struct grub_term_input *in;
struct grub_term_output *out;
struct grub_serial_input_state *indata;
struct grub_serial_output_state *outdata;
in = grub_malloc (sizeof (*in));
if (!in)
return grub_errno;
indata = grub_malloc (sizeof (*indata));
if (!indata)
{
grub_free (in);
return grub_errno;
}
grub_memcpy (in, &grub_serial_term_input, sizeof (*in));
in->data = indata;
in->name = grub_xasprintf ("serial_%s", port->name);
grub_memcpy (indata, &grub_serial_terminfo_input, sizeof (*indata));
if (!in->name)
{
grub_free (in);
grub_free (indata);
return grub_errno;
}
out = grub_malloc (sizeof (*out));
if (!out)
{
grub_free (in);
grub_free (indata);
grub_free ((char *) in->name);
return grub_errno;
}
outdata = grub_malloc (sizeof (*outdata));
if (!outdata)
{
grub_free (in);
grub_free (indata);
grub_free ((char *) in->name);
grub_free (out);
return grub_errno;
}
grub_memcpy (out, &grub_serial_term_output, sizeof (*out));
out->data = outdata;
out->name = in->name;
grub_memcpy (outdata, &grub_serial_terminfo_output, sizeof (*outdata));
grub_list_push (GRUB_AS_LIST_P (&grub_serial_ports), GRUB_AS_LIST (port));
((struct grub_serial_input_state *) in->data)->port = port;
((struct grub_serial_output_state *) out->data)->port = port;
port->term_in = in;
port->term_out = out;
grub_terminfo_output_register (out, "vt100");
#ifdef GRUB_MACHINE_MIPS_YEELOONG
if (grub_strcmp (port->name, "com0") == 0)
{
grub_term_register_input_active ("serial_*", in);
grub_term_register_output_active ("serial_*", out);
}
else
#endif
{
grub_term_register_input ("serial_*", in);
grub_term_register_output ("serial_*", out);
}
return GRUB_ERR_NONE;
}
void
grub_serial_unregister (struct grub_serial_port *port)
{
if (port->driver->fini)
port->driver->fini (port);
if (port->term_in)
grub_term_unregister_input (port->term_in);
if (port->term_out)
grub_term_unregister_output (port->term_out);
grub_list_remove (GRUB_AS_LIST_P (&grub_serial_ports), GRUB_AS_LIST (port));
}
void
grub_serial_unregister_driver (struct grub_serial_driver *driver)
{
struct grub_serial_port *port, *next;
for (port = grub_serial_ports; port; port = next)
{
next = port->next;
if (port->driver == driver)
grub_serial_unregister (port);
}
}
static grub_extcmd_t cmd;
@ -359,41 +345,19 @@ GRUB_MOD_INIT(serial)
GRUB_COMMAND_FLAG_BOTH,
N_("[OPTIONS...]"),
N_("Configure serial port."), options);
/* Set default settings. */
serial_settings.port = serial_hw_get_port (0);
#ifdef GRUB_MACHINE_MIPS_YEELOONG
serial_settings.divisor = serial_get_divisor (115200);
#else
serial_settings.divisor = serial_get_divisor (9600);
#endif
serial_settings.word_len = UART_8BITS_WORD;
serial_settings.parity = UART_NO_PARITY;
serial_settings.stop_bits = UART_1_STOP_BIT;
#ifdef GRUB_MACHINE_MIPS_YEELOONG
{
grub_err_t hwiniterr;
hwiniterr = serial_hw_init ();
if (hwiniterr == GRUB_ERR_NONE)
{
grub_term_register_input_active ("serial", &grub_serial_term_input);
grub_term_register_output_active ("serial", &grub_serial_term_output);
registered = 1;
}
}
#ifndef GRUB_MACHINE_EMU
grub_ns8250_init ();
#endif
}
GRUB_MOD_FINI(serial)
{
grub_unregister_extcmd (cmd);
if (registered == 1) /* Unregister terminal only if registered. */
while (grub_serial_ports)
grub_serial_unregister (grub_serial_ports);
if (registered)
{
grub_term_unregister_input (&grub_serial_term_input);
grub_term_unregister_output (&grub_serial_term_output);
grub_terminfo_output_unregister (&grub_serial_term_output);
}
grub_unregister_extcmd (cmd);
}

View file

@ -195,7 +195,7 @@ putstr (struct grub_term_output *term, const char *str)
struct grub_terminfo_output_state *data
= (struct grub_terminfo_output_state *) term->data;
while (*str)
data->put (*str++);
data->put (term, *str++);
}
grub_uint16_t
@ -225,7 +225,7 @@ grub_terminfo_gotoxy (struct grub_term_output *term,
else
{
if ((y == data->ypos) && (x == data->xpos - 1))
data->put ('\b');
data->put (term, '\b');
}
data->xpos = x;
@ -348,20 +348,21 @@ grub_terminfo_putchar (struct grub_term_output *term,
data->xpos = 0;
if (data->ypos < grub_term_height (term) - 1)
data->ypos++;
data->put ('\r');
data->put ('\n');
data->put (term, '\r');
data->put (term, '\n');
}
data->xpos += c->estimated_width;
break;
}
data->put (c->base);
data->put (term, c->base);
}
#define ANSI_C0 0x9b
static void
grub_terminfo_readkey (int *keys, int *len, int (*readkey) (void))
grub_terminfo_readkey (struct grub_term_input *term, int *keys, int *len,
int (*readkey) (struct grub_term_input *term))
{
int c;
@ -371,7 +372,7 @@ grub_terminfo_readkey (int *keys, int *len, int (*readkey) (void))
/* On 9600 we have to wait up to 12 milliseconds. */ \
start = grub_get_time_ms (); \
do \
c = readkey (); \
c = readkey (term); \
while (c == -1 && grub_get_time_ms () - start < 12); \
if (c == -1) \
return; \
@ -380,7 +381,7 @@ grub_terminfo_readkey (int *keys, int *len, int (*readkey) (void))
(*len)++; \
}
c = readkey ();
c = readkey (term);
if (c < 0)
{
*len = 0;
@ -475,7 +476,8 @@ grub_terminfo_checkkey (struct grub_term_input *termi)
if (data->npending)
return data->input_buf[0];
grub_terminfo_readkey (data->input_buf, &data->npending, data->readkey);
grub_terminfo_readkey (termi, data->input_buf,
&data->npending, data->readkey);
if (data->npending)
return data->input_buf[0];
@ -491,7 +493,8 @@ grub_terminfo_getkey (struct grub_term_input *termi)
= (struct grub_terminfo_input_state *) (termi->data);
int ret;
while (! data->npending)
grub_terminfo_readkey (data->input_buf, &data->npending, data->readkey);
grub_terminfo_readkey (termi, data->input_buf, &data->npending,
data->readkey);
ret = data->input_buf[0];
data->npending--;

View file

@ -54,12 +54,6 @@ static char keyboard_map_shift[128] =
'?'
};
static grub_usb_device_t usbdev;
/* Valid values for bmRequestType. See HID definition version 1.11 section
7.2. */
#define USB_HID_HOST_TO_DEVICE 0x21
#define USB_HID_DEVICE_TO_HOST 0xA1
/* Valid values for bRequest. See HID definition version 1.11 section 7.2. */
#define USB_HID_GET_REPORT 0x01
@ -69,78 +63,176 @@ static grub_usb_device_t usbdev;
#define USB_HID_SET_IDLE 0x0A
#define USB_HID_SET_PROTOCOL 0x0B
#define USB_HID_BOOT_SUBCLASS 0x01
#define USB_HID_KBD_PROTOCOL 0x01
static int grub_usb_keyboard_checkkey (struct grub_term_input *term);
static int grub_usb_keyboard_getkey (struct grub_term_input *term);
static int grub_usb_keyboard_getkeystatus (struct grub_term_input *term);
static struct grub_term_input grub_usb_keyboard_term =
{
.checkkey = grub_usb_keyboard_checkkey,
.getkey = grub_usb_keyboard_getkey,
.getkeystatus = grub_usb_keyboard_getkeystatus,
.next = 0
};
struct grub_usb_keyboard_data
{
grub_usb_device_t usbdev;
grub_uint8_t status;
int key;
struct grub_usb_desc_endp *endp;
};
static struct grub_term_input grub_usb_keyboards[16];
static void
grub_usb_hid (void)
grub_usb_keyboard_detach (grub_usb_device_t usbdev,
int config __attribute__ ((unused)),
int interface __attribute__ ((unused)))
{
struct grub_usb_desc_device *descdev;
auto int usb_iterate (grub_usb_device_t dev);
int usb_iterate (grub_usb_device_t dev)
unsigned i;
for (i = 0; i < ARRAY_SIZE (grub_usb_keyboards); i++)
{
descdev = &dev->descdev;
struct grub_usb_keyboard_data *data = grub_usb_keyboards[i].data;
grub_dprintf ("usb_keyboard", "%x %x %x\n",
descdev->class, descdev->subclass, descdev->protocol);
if (!data)
continue;
#if 0
if (descdev->class != 0x09
|| descdev->subclass == 0x01
|| descdev->protocol != 0x02)
return 0;
#endif
if (data->usbdev != usbdev)
continue;
if (descdev->class != 0 || descdev->subclass != 0 || descdev->protocol != 0)
return 0;
grub_printf ("HID found!\n");
usbdev = dev;
return 1;
grub_term_unregister_input (&grub_usb_keyboards[i]);
grub_free ((char *) grub_usb_keyboards[i].name);
grub_usb_keyboards[i].name = NULL;
grub_free (grub_usb_keyboards[i].data);
grub_usb_keyboards[i].data = 0;
}
grub_usb_iterate (usb_iterate);
}
static int
grub_usb_keyboard_attach (grub_usb_device_t usbdev, int configno, int interfno)
{
unsigned curnum;
struct grub_usb_keyboard_data *data;
struct grub_usb_desc_endp *endp = NULL;
int j;
grub_dprintf ("usb_keyboard", "%x %x %x %d %d\n",
usbdev->descdev.class, usbdev->descdev.subclass,
usbdev->descdev.protocol, configno, interfno);
for (curnum = 0; curnum < ARRAY_SIZE (grub_usb_keyboards); curnum++)
if (!grub_usb_keyboards[curnum].data)
break;
if (curnum == ARRAY_SIZE (grub_usb_keyboards))
return 0;
if (usbdev->descdev.class != 0
|| usbdev->descdev.subclass != 0 || usbdev->descdev.protocol != 0)
return 0;
if (usbdev->config[configno].interf[interfno].descif->subclass
!= USB_HID_BOOT_SUBCLASS
|| usbdev->config[configno].interf[interfno].descif->protocol
!= USB_HID_KBD_PROTOCOL)
return 0;
for (j = 0; j < usbdev->config[configno].interf[interfno].descif->endpointcnt;
j++)
{
endp = &usbdev->config[configno].interf[interfno].descendp[j];
if ((endp->endp_addr & 128) && grub_usb_get_ep_type(endp)
== GRUB_USB_EP_INTERRUPT)
break;
}
if (j == usbdev->config[configno].interf[interfno].descif->endpointcnt)
return 0;
grub_dprintf ("usb_keyboard", "HID found!\n");
data = grub_malloc (sizeof (*data));
if (!data)
{
grub_print_error ();
return 0;
}
data->usbdev = usbdev;
data->endp = endp;
/* Place the device in boot mode. */
grub_usb_control_msg (usbdev, USB_HID_HOST_TO_DEVICE, USB_HID_SET_PROTOCOL,
0, 0, 0, 0);
grub_usb_control_msg (usbdev, GRUB_USB_REQTYPE_CLASS_INTERFACE_OUT,
USB_HID_SET_PROTOCOL, 0, 0, 0, 0);
/* Reports every time an event occurs and not more often than that. */
grub_usb_control_msg (usbdev, USB_HID_HOST_TO_DEVICE, USB_HID_SET_IDLE,
0<<8, 0, 0, 0);
grub_usb_control_msg (usbdev, GRUB_USB_REQTYPE_CLASS_INTERFACE_OUT,
USB_HID_SET_IDLE, 0<<8, 0, 0, 0);
grub_memcpy (&grub_usb_keyboards[curnum], &grub_usb_keyboard_term,
sizeof (grub_usb_keyboards[curnum]));
grub_usb_keyboards[curnum].data = data;
usbdev->config[configno].interf[interfno].detach_hook
= grub_usb_keyboard_detach;
grub_usb_keyboards[curnum].name = grub_xasprintf ("usb_keyboard%d", curnum);
if (!grub_usb_keyboards[curnum].name)
{
grub_print_error ();
return 0;
}
static grub_err_t
grub_usb_keyboard_getreport (grub_usb_device_t dev, grub_uint8_t *report)
{
return grub_usb_control_msg (dev, USB_HID_DEVICE_TO_HOST, USB_HID_GET_REPORT,
0, 0, 8, (char *) report);
grub_uint8_t report[8];
grub_usb_err_t err;
grub_memset (report, 0, sizeof (report));
err = grub_usb_control_msg (usbdev, GRUB_USB_REQTYPE_CLASS_INTERFACE_IN,
USB_HID_GET_REPORT, 0x0000, interfno,
sizeof (report), (char *) report);
if (err)
{
data->status = 0;
data->key = -1;
}
else
{
data->status = report[0];
data->key = report[2] ? : -1;
}
}
grub_term_register_input_active ("usb_keyboard", &grub_usb_keyboards[curnum]);
return 1;
}
static int
grub_usb_keyboard_checkkey (struct grub_term_input *term __attribute__ ((unused)))
grub_usb_keyboard_checkkey (struct grub_term_input *term)
{
grub_uint8_t data[8];
int key;
grub_err_t err;
grub_uint64_t currtime;
int timeout = 50;
grub_usb_err_t err;
struct grub_usb_keyboard_data *termdata = term->data;
grub_size_t actual;
if (termdata->key != -1)
return termdata->key;
data[2] = 0;
currtime = grub_get_time_ms ();
do
{
/* Get_Report. */
err = grub_usb_keyboard_getreport (usbdev, data);
/* Poll interrupt pipe. */
err = grub_usb_bulk_read_extended (termdata->usbdev,
termdata->endp->endp_addr, sizeof (data),
(char *) data, 10, &actual);
if (err || actual < 1)
return -1;
/* Implement a timeout. */
if (grub_get_time_ms () > currtime + timeout)
break;
}
while (err || !data[2]);
termdata->status = data[0];
if (err || !data[2])
if (actual < 3 || !data[2])
return -1;
grub_dprintf ("usb_keyboard",
@ -151,178 +243,74 @@ grub_usb_keyboard_checkkey (struct grub_term_input *term __attribute__ ((unused)
/* Check if the Control or Shift key was pressed. */
if (data[0] & 0x01 || data[0] & 0x10)
key = keyboard_map[data[2]] - 'a' + 1;
termdata->key = keyboard_map[data[2]] - 'a' + 1;
else if (data[0] & 0x02 || data[0] & 0x20)
key = keyboard_map_shift[data[2]];
termdata->key = keyboard_map_shift[data[2]];
else
key = keyboard_map[data[2]];
termdata->key = keyboard_map[data[2]];
if (key == 0)
if (termdata->key == 0)
grub_printf ("Unknown key 0x%x detected\n", data[2]);
#if 0
/* Wait until the key is released. */
while (!err && data[2])
{
err = grub_usb_control_msg (usbdev, USB_HID_DEVICE_TO_HOST,
USB_HID_GET_REPORT, 0, 0,
sizeof (data), (char *) data);
grub_dprintf ("usb_keyboard",
"report2: 0x%02x 0x%02x 0x%02x 0x%02x"
" 0x%02x 0x%02x 0x%02x 0x%02x\n",
data[0], data[1], data[2], data[3],
data[4], data[5], data[6], data[7]);
}
#endif
grub_errno = GRUB_ERR_NONE;
return key;
return termdata->key;
}
typedef enum
{
GRUB_HIDBOOT_REPEAT_NONE,
GRUB_HIDBOOT_REPEAT_FIRST,
GRUB_HIDBOOT_REPEAT
} grub_usb_keyboard_repeat_t;
static int
grub_usb_keyboard_getkey (struct grub_term_input *term)
{
int key;
grub_err_t err;
grub_uint8_t data[8];
grub_uint64_t currtime;
int timeout;
static grub_usb_keyboard_repeat_t repeat = GRUB_HIDBOOT_REPEAT_NONE;
int ret;
struct grub_usb_keyboard_data *termdata = term->data;
again:
while (termdata->key == -1)
grub_usb_keyboard_checkkey (term);
do
{
key = grub_usb_keyboard_checkkey (term);
} while (key == -1);
ret = termdata->key;
data[2] = !0; /* Or whatever. */
err = 0;
termdata->key = -1;
switch (repeat)
{
case GRUB_HIDBOOT_REPEAT_FIRST:
timeout = 500;
break;
case GRUB_HIDBOOT_REPEAT:
timeout = 50;
break;
default:
timeout = 100;
break;
}
/* Wait until the key is released. */
currtime = grub_get_time_ms ();
while (!err && data[2])
{
/* Implement a timeout. */
if (grub_get_time_ms () > currtime + timeout)
{
if (repeat == 0)
repeat = 1;
else
repeat = 2;
grub_errno = GRUB_ERR_NONE;
return key;
}
err = grub_usb_keyboard_getreport (usbdev, data);
}
if (repeat)
{
repeat = 0;
goto again;
}
repeat = 0;
grub_errno = GRUB_ERR_NONE;
return key;
return ret;
}
static int
grub_usb_keyboard_getkeystatus (struct grub_term_input *term __attribute__ ((unused)))
grub_usb_keyboard_getkeystatus (struct grub_term_input *term)
{
grub_uint8_t data[8];
struct grub_usb_keyboard_data *termdata = term->data;
int mods = 0;
grub_err_t err;
grub_uint64_t currtime;
int timeout = 50;
/* Set idle time to the minimum offered by the spec (4 milliseconds) so
that we can find out the current state. */
grub_usb_control_msg (usbdev, USB_HID_HOST_TO_DEVICE, USB_HID_SET_IDLE,
0<<8, 0, 0, 0);
currtime = grub_get_time_ms ();
do
{
/* Get_Report. */
err = grub_usb_keyboard_getreport (usbdev, data);
/* Implement a timeout. */
if (grub_get_time_ms () > currtime + timeout)
break;
}
while (err || !data[0]);
/* Go back to reporting every time an event occurs and not more often than
that. */
grub_usb_control_msg (usbdev, USB_HID_HOST_TO_DEVICE, USB_HID_SET_IDLE,
0<<8, 0, 0, 0);
/* We allowed a while for modifiers to show up in the report, but it is
not an error if they never did. */
if (err)
return -1;
grub_dprintf ("usb_keyboard",
"report: 0x%02x 0x%02x 0x%02x 0x%02x"
" 0x%02x 0x%02x 0x%02x 0x%02x\n",
data[0], data[1], data[2], data[3],
data[4], data[5], data[6], data[7]);
/* Check Shift, Control, and Alt status. */
if (data[0] & 0x02 || data[0] & 0x20)
if (termdata->status & 0x02 || termdata->status & 0x20)
mods |= GRUB_TERM_STATUS_SHIFT;
if (data[0] & 0x01 || data[0] & 0x10)
if (termdata->status & 0x01 || termdata->status & 0x10)
mods |= GRUB_TERM_STATUS_CTRL;
if (data[0] & 0x04 || data[0] & 0x40)
if (termdata->status & 0x04 || termdata->status & 0x40)
mods |= GRUB_TERM_STATUS_ALT;
grub_errno = GRUB_ERR_NONE;
return mods;
}
static struct grub_term_input grub_usb_keyboard_term =
struct grub_usb_attach_desc attach_hook =
{
.name = "usb_keyboard",
.checkkey = grub_usb_keyboard_checkkey,
.getkey = grub_usb_keyboard_getkey,
.getkeystatus = grub_usb_keyboard_getkeystatus,
.next = 0
.class = GRUB_USB_CLASS_HID,
.hook = grub_usb_keyboard_attach
};
GRUB_MOD_INIT(usb_keyboard)
{
grub_usb_hid ();
grub_term_register_input ("usb_keyboard", &grub_usb_keyboard_term);
grub_usb_register_attach_hook_class (&attach_hook);
}
GRUB_MOD_FINI(usb_keyboard)
{
grub_term_unregister_input (&grub_usb_keyboard_term);
unsigned i;
for (i = 0; i < ARRAY_SIZE (grub_usb_keyboards); i++)
if (grub_usb_keyboards[i].data)
{
grub_term_unregister_input (&grub_usb_keyboards[i]);
grub_free ((char *) grub_usb_keyboards[i].name);
grub_usb_keyboards[i].name = NULL;
grub_usb_keyboards[i].data = 0;
}
grub_usb_unregister_attach_hook_class (&attach_hook);
}