Support --base-clock for serial command to handle weird cards with

non-standard base clock.
This commit is contained in:
Vladimir Serbinenko 2013-11-01 19:33:22 +01:00
parent eb03ede014
commit 89295a0628
4 changed files with 64 additions and 48 deletions

View File

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

View File

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

View File

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

View File

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