Support --base-clock for serial command to handle weird cards with
non-standard base clock.
This commit is contained in:
parent
eb03ede014
commit
89295a0628
4 changed files with 64 additions and 48 deletions
|
@ -1,3 +1,8 @@
|
|||
2013-11-01 Vladimir Serbinenko <phcoder@gmail.com>
|
||||
|
||||
Support --base-clock for serial command to handle weird cards with
|
||||
non-standard base clock.
|
||||
|
||||
2013-11-01 Vladimir Serbinenko <phcoder@gmail.com>
|
||||
|
||||
* grub-core/fs/ext2.c (grub_ext2_read_symlink): Use memcpy rather
|
||||
|
|
|
@ -38,46 +38,33 @@ static const grub_port_t serial_hw_io_addr[] = GRUB_MACHINE_SERIAL_PORTS;
|
|||
|
||||
static int dead_ports = 0;
|
||||
|
||||
#ifdef GRUB_MACHINE_MIPS_LOONGSON
|
||||
#define DEFAULT_BASE_CLOCK (2 * 115200)
|
||||
#else
|
||||
#define DEFAULT_BASE_CLOCK 115200
|
||||
#endif
|
||||
|
||||
|
||||
/* Convert speed to divisor. */
|
||||
static unsigned short
|
||||
serial_get_divisor (const struct grub_serial_port *port __attribute__ ((unused)),
|
||||
const struct grub_serial_config *config)
|
||||
{
|
||||
unsigned int i;
|
||||
grub_uint32_t base_clock;
|
||||
grub_uint32_t divisor;
|
||||
grub_uint32_t actual_speed, error;
|
||||
|
||||
/* The structure for speed vs. divisor. */
|
||||
struct divisor
|
||||
{
|
||||
unsigned int speed;
|
||||
unsigned short div;
|
||||
};
|
||||
base_clock = config->base_clock ? (config->base_clock >> 4) : DEFAULT_BASE_CLOCK;
|
||||
|
||||
/* 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 == config->speed)
|
||||
{
|
||||
/* internal Loongson UART runs twice the usual rate. */
|
||||
#ifdef GRUB_MACHINE_MIPS_LOONGSON
|
||||
if (port->port == 0xbff003f8)
|
||||
return 2 * divisor_tab[i].div;
|
||||
else
|
||||
#endif
|
||||
return divisor_tab[i].div;
|
||||
}
|
||||
divisor = (base_clock + (config->speed / 2)) / config->speed;
|
||||
if (divisor > 0xffff || divisor == 0)
|
||||
return 0;
|
||||
actual_speed = base_clock / divisor;
|
||||
error = actual_speed > config->speed ? (actual_speed - config->speed)
|
||||
: (config->speed - actual_speed);
|
||||
if (error > (config->speed / 30 + 1))
|
||||
return 0;
|
||||
return divisor;
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
|
@ -39,6 +39,17 @@ GRUB_MOD_LICENSE ("GPLv3+");
|
|||
|
||||
#define FOR_SERIAL_PORTS(var) FOR_LIST_ELEMENTS((var), (grub_serial_ports))
|
||||
|
||||
enum
|
||||
{
|
||||
OPTION_UNIT,
|
||||
OPTION_PORT,
|
||||
OPTION_SPEED,
|
||||
OPTION_WORD,
|
||||
OPTION_PARITY,
|
||||
OPTION_STOP,
|
||||
OPTION_BASE_CLOCK
|
||||
};
|
||||
|
||||
/* Argument options. */
|
||||
static const struct grub_arg_option options[] =
|
||||
{
|
||||
|
@ -48,6 +59,7 @@ static const struct grub_arg_option options[] =
|
|||
{"word", 'w', 0, N_("Set the serial port word length."), 0, ARG_TYPE_INT},
|
||||
{"parity", 'r', 0, N_("Set the serial port parity."), 0, ARG_TYPE_STRING},
|
||||
{"stop", 't', 0, N_("Set the serial port stop bits."), 0, ARG_TYPE_INT},
|
||||
{"base-clock", 'b', 0, N_("Set the base clock."), 0, ARG_TYPE_INT},
|
||||
{0, 0, 0, 0, 0, 0}
|
||||
};
|
||||
|
||||
|
@ -178,14 +190,14 @@ grub_cmd_serial (grub_extcmd_context_t ctxt, int argc, char **args)
|
|||
struct grub_serial_config config;
|
||||
grub_err_t err;
|
||||
|
||||
if (state[0].set)
|
||||
if (state[OPTION_UNIT].set)
|
||||
{
|
||||
grub_snprintf (pname, sizeof (pname), "com%ld",
|
||||
grub_strtoul (state[0].arg, 0, 0));
|
||||
name = pname;
|
||||
}
|
||||
|
||||
if (state[1].set)
|
||||
if (state[OPTION_PORT].set)
|
||||
{
|
||||
grub_snprintf (pname, sizeof (pname), "port%lx",
|
||||
grub_strtoul (state[1].arg, 0, 0));
|
||||
|
@ -206,38 +218,48 @@ grub_cmd_serial (grub_extcmd_context_t ctxt, int argc, char **args)
|
|||
|
||||
config = port->config;
|
||||
|
||||
if (state[2].set)
|
||||
config.speed = grub_strtoul (state[2].arg, 0, 0);
|
||||
if (state[OPTION_SPEED].set)
|
||||
config.speed = grub_strtoul (state[OPTION_SPEED].arg, 0, 0);
|
||||
|
||||
if (state[3].set)
|
||||
config.word_len = grub_strtoul (state[3].arg, 0, 0);
|
||||
if (state[OPTION_WORD].set)
|
||||
config.word_len = grub_strtoul (state[OPTION_WORD].arg, 0, 0);
|
||||
|
||||
if (state[4].set)
|
||||
if (state[OPTION_PARITY].set)
|
||||
{
|
||||
if (! grub_strcmp (state[4].arg, "no"))
|
||||
if (! grub_strcmp (state[OPTION_PARITY].arg, "no"))
|
||||
config.parity = GRUB_SERIAL_PARITY_NONE;
|
||||
else if (! grub_strcmp (state[4].arg, "odd"))
|
||||
else if (! grub_strcmp (state[OPTION_PARITY].arg, "odd"))
|
||||
config.parity = GRUB_SERIAL_PARITY_ODD;
|
||||
else if (! grub_strcmp (state[4].arg, "even"))
|
||||
else if (! grub_strcmp (state[OPTION_PARITY].arg, "even"))
|
||||
config.parity = GRUB_SERIAL_PARITY_EVEN;
|
||||
else
|
||||
return grub_error (GRUB_ERR_BAD_ARGUMENT,
|
||||
N_("unsupported serial port parity"));
|
||||
}
|
||||
|
||||
if (state[5].set)
|
||||
if (state[OPTION_STOP].set)
|
||||
{
|
||||
if (! grub_strcmp (state[5].arg, "1"))
|
||||
if (! grub_strcmp (state[OPTION_STOP].arg, "1"))
|
||||
config.stop_bits = GRUB_SERIAL_STOP_BITS_1;
|
||||
else if (! grub_strcmp (state[5].arg, "2"))
|
||||
else if (! grub_strcmp (state[OPTION_STOP].arg, "2"))
|
||||
config.stop_bits = GRUB_SERIAL_STOP_BITS_2;
|
||||
else if (! grub_strcmp (state[5].arg, "1.5"))
|
||||
else if (! grub_strcmp (state[OPTION_STOP].arg, "1.5"))
|
||||
config.stop_bits = GRUB_SERIAL_STOP_BITS_1_5;
|
||||
else
|
||||
return grub_error (GRUB_ERR_BAD_ARGUMENT,
|
||||
N_("unsupported serial port stop bits number"));
|
||||
}
|
||||
|
||||
if (state[OPTION_BASE_CLOCK].set)
|
||||
{
|
||||
char *ptr;
|
||||
config.base_clock = grub_strtoull (state[OPTION_BASE_CLOCK].arg, &ptr, 0);
|
||||
if (ptr && *ptr == 'M')
|
||||
config.base_clock *= 1000000;
|
||||
if (ptr && (*ptr == 'k' || *ptr == 'K'))
|
||||
config.base_clock *= 1000;
|
||||
}
|
||||
|
||||
/* Initialize with new settings. */
|
||||
err = port->driver->configure (port, &config);
|
||||
if (err)
|
||||
|
|
|
@ -67,6 +67,7 @@ struct grub_serial_config
|
|||
int word_len;
|
||||
grub_serial_parity_t parity;
|
||||
grub_serial_stop_bits_t stop_bits;
|
||||
grub_uint64_t base_clock;
|
||||
};
|
||||
|
||||
struct grub_serial_port
|
||||
|
@ -163,7 +164,8 @@ grub_serial_config_defaults (struct grub_serial_port *port)
|
|||
#endif
|
||||
.word_len = 8,
|
||||
.parity = GRUB_SERIAL_PARITY_NONE,
|
||||
.stop_bits = GRUB_SERIAL_STOP_BITS_1
|
||||
.stop_bits = GRUB_SERIAL_STOP_BITS_1,
|
||||
.base_clock = 0
|
||||
};
|
||||
|
||||
return port->driver->configure (port, &config);
|
||||
|
|
Loading…
Reference in a new issue