FreeCalypso > hg > freecalypso-sw
diff target-utils/libcommon/cmd_baud_switch.c @ 37:437f9365249c
loadagent: baud rate switching implemented
author | Michael Spacefalcon <msokolov@ivan.Harhan.ORG> |
---|---|
date | Mon, 13 May 2013 06:56:54 +0000 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/target-utils/libcommon/cmd_baud_switch.c Mon May 13 06:56:54 2013 +0000 @@ -0,0 +1,112 @@ +/* + * Baud rate switching command + */ + +#include <stdlib.h> +#include "types.h" +#include "ns16550.h" + +extern struct ns16550_regs *uart_base; +extern int serial_in_poll(); + +static const struct tab { + int baud; + int divisor; +} rate_table[] = { + /* + * First support the rates and divisors implemented by the + * Calypso boot ROM. Dividing 13 MHz by 7 gives an approximation + * of 115200 (x16); the divisors used by the boot ROM code for + * the slower baud rates are all 7x the usual PC value. + */ + {115200, 7}, + {57600, 7 * 2}, + {38400, 7 * 3}, + {28800, 7 * 4}, + {19200, 7 * 6}, + /* + * Going faster than ~115200 baud means using a divisor + * less than 7, resulting in a non-standard baud rate. + * The /1, /2 and /4 seem like reasonable choices. + */ + {812500, 1}, + {406250, 2}, + {203125, 4}, + /* that's all we really need to support */ + {0, 0} +}; + +/* + * The following helper function actually switches the UART + * baud rate divisor. Call serial_flush() first. It returns the + * old divisor value. + * + * Note the u8 type for both the new and old divisor values. + * All supported divisors are well below 255, so we don't bother + * with the upper byte. + */ +static u8 +actually_switch_baud(newdiv) + u8 newdiv; +{ + volatile struct ns16550_regs *regs; + u8 save_lcr, save_old_baud; + + regs = uart_base; + save_lcr = regs->lcr; + regs->lcr = save_lcr | NS16550_LCR_DLAB; + save_old_baud = regs->datareg; + regs->datareg = newdiv; + regs->lcr = save_lcr; + return(save_old_baud); +} + +void +cmd_baud_switch(argbulk) + char *argbulk; +{ + char *argv[2]; + int baudarg; + struct tab *tp; + u8 save_old_baud; + int c; + + if (parse_args(argbulk, 1, 1, argv, 0) < 0) + return; + baudarg = atoi(argv[0]); + for (tp = rate_table; tp->baud; tp++) + if (tp->baud == baudarg) + break; + if (!tp->baud) { + printf("ERROR: invalid/unimplemented baud rate argument\n"); + return; + } + + /* do it */ + serial_flush(); + save_old_baud = actually_switch_baud(tp->divisor); + + /* + * After getting the echo of this command at the old baud rate + * (see the serial flush call just before switching the divisor), + * the line will go silent from the user's perspective. + * The user should wait just a little bit, then send us a 0x55 ('U') + * at the new baud rate - we should be in the below loop waiting + * for this character by then. Receiving that character + * correctly (0x55 was chosen for the bit pattern - unlikely to + * be received if the sender is sending at a wrong baud rate) + * will cause us to conclude this command and return a new '=' + * prompt at the new baud rate. + * + * If we get something else, we assume that someone messed up, + * switch back to the old baud rate, scribble an error message + * and return. + */ + do + c = serial_in_poll(); + while (c < 0); + if (c != 0x55) { + actually_switch_baud(save_old_baud); + printf("ERROR: no \'U\' received, switched back to old baud rate\n"); + } +}