[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Xen-devel] [PATCH v3] ns16550-Add-command-line-parsing-adjustments
Add name=value parsing options for com1 and com2 to add flexibility in setting register values for MMIO UART devices. Maintain backward compatibility with previous positional parameter specfications. eg. com1=115200,8n1,0x3f8,4 eg. com1=baud=115200,parity=n,reg_width=4,reg_shift=2,irq=4 eg. com1=115200,8n1,0x3f8,4,reg_width=4,reg_shift=2 Increase length of opt_com1 and opt_com2 buffers. Signed-off-by: Swapnil Paratey <swapnil.paratey@xxxxxxx> --- Changed since v2: * Added name=value specification for com1 and com2 command line parameter input for UART devices Syntax: "com1=(positional parameters),(name-value parameters)" * Maintained previous positional specification for UART parameters * All parameters should be comma-separated Changed since v1: * Changed opt_com1 and opt_com2 array size to 64 (power of 2). * Added descriptions for reg_width and reg_shift in docs/misc/xen-command-line.markdown * Changed subject to ns16550 from 16550 for better tracking. --- docs/misc/xen-command-line.markdown | 37 ++++++ xen/common/kernel.c | 2 +- xen/drivers/char/ns16550.c | 224 +++++++++++++++++++++++++++++++++--- xen/include/xen/serial.h | 2 + 4 files changed, 250 insertions(+), 15 deletions(-) diff --git a/docs/misc/xen-command-line.markdown b/docs/misc/xen-command-line.markdown index 4daf5b5..0d9fdf1 100644 --- a/docs/misc/xen-command-line.markdown +++ b/docs/misc/xen-command-line.markdown @@ -324,6 +324,43 @@ Both option `com1` and `com2` follow the same format. A typical setup for most situations might be `com1=115200,8n1` +In addition to the above positional specification for UART parameters, +name=value pair specfications are also supported. This is used to add +flexibility for UART devices which require additional UART parameter +configurations. + +The comma separation still delineates positional parameters. Hence, +unless the parameter is explicitly specified with name=value option, it +will be considered a positional parameter. + +The syntax consists of +com1=(comma-separated positional parameters),(comma separated name-value pairs) + +The accepted name keywords for name=value pairs are + * `baud` - accepts integer baud rate (eg. 115200) or `auto` + * `bridge`- accepts xx:xx:xx. Similar to bridge-bdf in positional parameters. + notation is <bus>:<device>:<function> + * `clock_hz`- accepts large integers to setup UART clock frequencies. + Do note - these values are multiplied by 16. + * `data_bits` - integer between 5 and 8 + * `dev` - accepted values are `pci` OR `amt`. If this option + is used to specify if the serial device is pci-based. The io_base + cannot be specified when `dev=pci` or `dev=amt` is used. + * `io_base` - accepts integer which specified IO base port for UART registers + * `irq` - IRQ number to use + * `parity` - accepted values are same as positional parameters + * `port` - used to specify which port the PCI serial device is located on + notation is xx:xx:xx <bus>:<device>:<function> + * `reg_shift` - register shifts required to set UART registers + * `reg_width` - register width required to set UART registers + (only accepts 1 and 4) + * `stop_bits` - only accepts 1 or 2 for the number of stop bits + +The following are examples of correct specifications: +`com1=115200,8n1,0x3f8,4` +`com1=115200,8n1,0x3f8,4,reg_width=4,reg_shift=2` +`com1=baud=115200,parity=n,stop_bits=1,io_base=0x3f8,reg_width=4` + ### conring\_size > `= <size>` diff --git a/xen/common/kernel.c b/xen/common/kernel.c index 4b87c60..0cee43a 100644 --- a/xen/common/kernel.c +++ b/xen/common/kernel.c @@ -48,7 +48,7 @@ static void __init assign_integer_param( void __init cmdline_parse(const char *cmdline) { - char opt[100], *optval, *optkey, *q; + char opt[512], *optval, *optkey, *q; const char *p = cmdline; const struct kernel_param *param; int bool_assert; diff --git a/xen/drivers/char/ns16550.c b/xen/drivers/char/ns16550.c index e4de3b4..ca5c515 100644 --- a/xen/drivers/char/ns16550.c +++ b/xen/drivers/char/ns16550.c @@ -38,11 +38,27 @@ * can be specified in place of a numeric baud rate. Polled mode is specified * by requesting irq 0. */ -static char __initdata opt_com1[30] = ""; -static char __initdata opt_com2[30] = ""; +static char __initdata opt_com1[MAX_CMDLINE_LENGTH] = ""; +static char __initdata opt_com2[MAX_CMDLINE_LENGTH] = ""; string_param("com1", opt_com1); string_param("com2", opt_com2); +typedef enum e_serial_param_type { + BAUD=0, + BRIDGEBDF, + CLOCKHZ, + DATABITS, + DEVICE, + IO_BASE, + IRQ, + PARITY, + PORTBDF, + REG_SHIFT, + REG_WIDTH, + STOPBITS, + __MAX_SERIAL_PARAM /* introduce more parameters before this line */ +} serial_param_type; + static struct ns16550 { int baud, clock_hz, data_bits, parity, stop_bits, fifo_size, irq; u64 io_base; /* I/O port or memory-mapped I/O address. */ @@ -77,6 +93,29 @@ static struct ns16550 { #endif } ns16550_com[2] = { { 0 } }; +struct serial_param_var +{ + char *sp_name; + serial_param_type sp_type; +}; + +/* enum struct keeping a table of all accepted parameter names + * for parsing cmdline for serial port com1 and com2 */ +static struct serial_param_var sp_vars[] = { + {"baud", BAUD}, + {"bridge", BRIDGEBDF}, + {"clock_hz", CLOCKHZ}, + {"data_bits", DATABITS}, + {"dev", DEVICE}, + {"io_base", IO_BASE}, + {"irq", IRQ}, + {"parity", PARITY}, + {"port", PORTBDF}, + {"reg_shift", REG_SHIFT}, + {"reg_width", REG_WIDTH}, + {"stop_bits", STOPBITS}, +}; + #ifdef CONFIG_HAS_PCI struct ns16550_config { u16 vendor_id; @@ -1083,26 +1122,70 @@ pci_uart_config(struct ns16550 *uart, bool_t skip_amt, unsigned int idx) } #endif +/* used to parse name value pairs and return which value it is + * along with pointer for the extracted value - ext_value */ +static serial_param_type get_token_value(char *token, char *ext_value) +{ + char *param_name; + int i; + + param_name = strsep(&token, "="); + if ( param_name == NULL ) + return __MAX_SERIAL_PARAM; + /* token has the param value after the equal to sign */ + strlcpy(ext_value, token, MAX_PARAM_VALUE_LENGTH); + + /* linear search for the parameter */ + for ( i = 0; i < ARRAY_SIZE(sp_vars); i++ ) + { + if ( strcmp(sp_vars[i].sp_name, param_name) == 0 ) + return sp_vars[i].sp_type; + } + + return __MAX_SERIAL_PARAM; +} + #define PARSE_ERR(_f, _a...) \ do { \ printk( "ERROR: " _f "\n" , ## _a ); \ return; \ } while ( 0 ) -static void __init ns16550_parse_port_config( - struct ns16550 *uart, const char *conf) +#define PARSE_ERR_RET(_f, _a...) \ + do { \ + printk( "ERROR: " _f "\n" , ## _a ); \ + return 1; \ + } while ( 0 ) + + +int parse_positional(struct ns16550 *uart, char **str) { int baud; + const char *conf; + char *name_val_pos; - /* No user-specified configuration? */ - if ( (conf == NULL) || (*conf == '\0') ) + conf = (const char *)*str; + name_val_pos = strchr(*str, '='); + + /* finding the end of the positional parameters */ + if (name_val_pos) { - /* Some platforms may automatically probe the UART configuartion. */ - if ( uart->baud != 0 ) - goto config_parsed; - return; + while (name_val_pos > *str) + { + name_val_pos--; /* working backwards from the = sign */ + if (*name_val_pos == ',') + { + *name_val_pos = '\0'; + name_val_pos++; + break; + } + } } + *str = name_val_pos; + if (conf == *str) return 0; /* when there are no positional parameters */ + + /* parse positional parameters here */ if ( strncmp(conf, "auto", 4) == 0 ) { uart->baud = BAUD_AUTO; @@ -1132,13 +1215,13 @@ static void __init ns16550_parse_port_config( if ( strncmp(conf, "pci", 3) == 0 ) { if ( pci_uart_config(uart, 1/* skip AMT */, uart - ns16550_com) ) - return; + return 0; conf += 3; } else if ( strncmp(conf, "amt", 3) == 0 ) { if ( pci_uart_config(uart, 0, uart - ns16550_com) ) - return; + return 0; conf += 3; } else @@ -1157,7 +1240,7 @@ static void __init ns16550_parse_port_config( conf = parse_pci(conf, NULL, &uart->ps_bdf[0], &uart->ps_bdf[1], &uart->ps_bdf[2]); if ( !conf ) - PARSE_ERR("Bad port PCI coordinates"); + PARSE_ERR_RET("Bad port PCI coordinates"); uart->ps_bdf_enable = 1; } @@ -1165,11 +1248,122 @@ static void __init ns16550_parse_port_config( { if ( !parse_pci(conf, NULL, &uart->pb_bdf[0], &uart->pb_bdf[1], &uart->pb_bdf[2]) ) - PARSE_ERR("Bad bridge PCI coordinates"); + PARSE_ERR_RET("Bad bridge PCI coordinates"); uart->pb_bdf_enable = 1; } #endif + return 0; +} + +int parse_namevalue_pairs(char *str, struct ns16550 *uart) +{ + serial_param_type parsed_param; + char *token, *start; + char param_value[MAX_PARAM_VALUE_LENGTH]; + bool dev_set; + + dev_set = 0; + start = str; + + while (start != NULL) /* strsep gives NULL when there are no tokens found */ + { + /* when no tokens are found, start will be NULL */ + token = strsep(&start, ","); + + parsed_param = get_token_value(token, param_value); + switch(parsed_param) + { + case BAUD: + uart->baud = simple_strtoul(param_value, NULL, 0); + break; + case BRIDGEBDF: + if ( !parse_pci(param_value, NULL, &uart->ps_bdf[0], + &uart->ps_bdf[1], &uart->ps_bdf[2])) + PARSE_ERR_RET("Bad port PCI coordinates\n"); + uart->ps_bdf_enable = 1; + break; + case CLOCKHZ: + uart->clock_hz = (simple_strtoul(param_value, NULL, 0) << 4); + break; + case DEVICE: + if ((strncmp(param_value, "pci", 3) == 0)) + { + pci_uart_config(uart, 1/* skip AMT */, uart - ns16550_com); + dev_set = 1; + } + else if (strncmp(param_value, "amt", 3) == 0) + { + pci_uart_config(uart, 0, uart - ns16550_com); + dev_set = 1; + } + break; + case IO_BASE: + if (dev_set == 1) + PARSE_ERR_RET("Can't use io_base with dev=pci or dev=amt options\n"); + uart->io_base = simple_strtoul(param_value, NULL, 0); + break; + case IRQ: + uart->irq = simple_strtoul(param_value, NULL, 0); + break; + case DATABITS: + uart->data_bits = simple_strtoul(param_value, NULL, 0); + break; + case PARITY: + uart->parity = parse_parity_char(*param_value); + break; + case PORTBDF: + if ( !parse_pci(param_value, NULL, &uart->pb_bdf[0], + &uart->pb_bdf[1], &uart->pb_bdf[2])) + PARSE_ERR_RET("Bad port PCI coordinates\n"); + uart->pb_bdf_enable = 1; + break; + case STOPBITS: + uart->stop_bits = simple_strtoul(param_value, NULL, 0); + break; + case REG_WIDTH: + uart->reg_width = simple_strtoul(param_value, NULL, 0); + break; + case REG_SHIFT: + uart->reg_shift = simple_strtoul(param_value, NULL, 0); + break; + default: + printk("Invalid parameter: %s\n", token); + break; + } + } + + return 0; +} + +static void __init ns16550_parse_port_config( + struct ns16550 *uart, const char *conf) +{ + char cmdline[MAX_CMDLINE_LENGTH]; + char *str; + + /* No user-specified configuration? */ + if ( (conf == NULL) || (*conf == '\0') ) + { + /* Some platforms may automatically probe the UART configuartion. */ + if ( uart->baud != 0 ) + goto config_parsed; + return; + } + + strlcpy(cmdline, conf, MAX_CMDLINE_LENGTH); + str = cmdline; /* good idea to use another pointer and keep cmdline alone */ + + /* parse positional parameters and get pointer for name-value pairs */ + if ( parse_positional(uart, &str) ) + return; + + if ( (str == NULL) || (*str == '\0') ) + goto config_parsed; + + if ( parse_namevalue_pairs(str, uart) ) + return; + config_parsed: /* Sanity checks. */ if ( (uart->baud != BAUD_AUTO) && @@ -1177,6 +1371,8 @@ static void __init ns16550_parse_port_config( PARSE_ERR("Baud rate %d outside supported range.", uart->baud); if ( (uart->data_bits < 5) || (uart->data_bits > 8) ) PARSE_ERR("%d data bits are unsupported.", uart->data_bits); + if ( (uart->reg_width != 1) && (uart->reg_width != 4) ) + PARSE_ERR("red_width accepted values: 1 or 4."); if ( (uart->stop_bits < 1) || (uart->stop_bits > 2) ) PARSE_ERR("%d stop bits are unsupported.", uart->stop_bits); if ( uart->io_base == 0 ) diff --git a/xen/include/xen/serial.h b/xen/include/xen/serial.h index 1212a12..0fd1d76 100644 --- a/xen/include/xen/serial.h +++ b/xen/include/xen/serial.h @@ -20,6 +20,8 @@ void serial_set_rx_handler(int handle, serial_rx_fn fn); /* Number of characters we buffer for a polling receiver. */ #define serial_rxbufsz 32 +#define MAX_CMDLINE_LENGTH 512 +#define MAX_PARAM_VALUE_LENGTH 16 /* Number of characters we buffer for an interrupt-driven transmitter. */ extern unsigned int serial_txbufsz; -- 2.7.4 _______________________________________________ Xen-devel mailing list Xen-devel@xxxxxxxxxxxxx https://lists.xen.org/xen-devel
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |