Allow custom baud rates for FTDI serial ports

- Termios: cf{get,set}{i,o}speed can handle arbitrary speed values.
- The value is stored in the appropriate fields of the termios structure
  in this case. The old constants (stored in the flags) are preserved
  for BeOS binary compatibility.
- Adjust the FTDI FT232* driver to accept custom rates, by replacing the
  hardcoded regster values with a function that will compute it
  according to FTDI documentation (confirmed giving the same values for
  the existing baudrates).
This commit is contained in:
Adrien Destugues 2016-02-27 19:08:53 +01:00
parent 44547a899c
commit 93ea83e53d
5 changed files with 54 additions and 48 deletions

View File

@ -21,8 +21,8 @@ struct termios {
tcflag_t c_cflag; /* control modes */
tcflag_t c_lflag; /* local modes */
char c_line; /* line discipline */
speed_t c_ispeed; /* (unused) */
speed_t c_ospeed; /* (unused) */
speed_t c_ispeed; /* custom input baudrate */
speed_t c_ospeed; /* custom output baudrate */
cc_t c_cc[NCCS]; /* control characters */
};

View File

@ -111,26 +111,24 @@ FTDIDevice::SetLineCoding(usb_cdc_line_coding *lineCoding)
break;
}
} else {
switch (lineCoding->speed) {
case 300: rate = ftdi_8u232am_b300; break;
case 600: rate = ftdi_8u232am_b600; break;
case 1200: rate = ftdi_8u232am_b1200; break;
case 2400: rate = ftdi_8u232am_b2400; break;
case 4800: rate = ftdi_8u232am_b4800; break;
case 9600: rate = ftdi_8u232am_b9600; break;
case 19200: rate = ftdi_8u232am_b19200; break;
case 38400: rate = ftdi_8u232am_b38400; break;
case 57600: rate = ftdi_8u232am_b57600; break;
case 115200: rate = ftdi_8u232am_b115200; break;
case 230400: rate = ftdi_8u232am_b230400; break;
case 460800: rate = ftdi_8u232am_b460800; break;
case 921600: rate = ftdi_8u232am_b921600; break;
default:
rate = ftdi_sio_b19200;
/* Compute baudrate register value as documented in AN232B-05 from FTDI.
Bits 13-0 are the integer divider, and bits 16-14 are the fractional
divider setting. 3Mbaud and 2Mbaud are special values, and at such
high speeds the use of the fractional divider is not possible. */
if (lineCoding->speed == 3000000)
rate = 0;
else if (lineCoding->speed == 2000000)
rate = 1;
else {
if (lineCoding->speed > 1500000) {
TRACE_ALWAYS("= FTDIDevice::SetLineCoding(): Datarate: %d is "
"not supported by this hardware. Defaulted to %d\n",
lineCoding->speed, rate);
break;
lineCoding->speed, 19200);
lineCoding->speed = 19200;
}
rate = 3000000 * 8 / lineCoding->speed;
int frac = ftdi_8u232am_frac[rate & 0x7];
rate = (rate >> 3) | frac;
}
}

View File

@ -101,20 +101,17 @@ enum {
ftdi_sio_b115200 = 9
};
enum {
ftdi_8u232am_b300 = 0x2710,
ftdi_8u232am_b600 = 0x1388,
ftdi_8u232am_b1200 = 0x09c4,
ftdi_8u232am_b2400 = 0x04e2,
ftdi_8u232am_b4800 = 0x0271,
ftdi_8u232am_b9600 = 0x4138,
ftdi_8u232am_b19200 = 0x809c,
ftdi_8u232am_b38400 = 0xc04e,
ftdi_8u232am_b57600 = 0x0034,
ftdi_8u232am_b115200 = 0x001a,
ftdi_8u232am_b230400 = 0x000d,
ftdi_8u232am_b460800 = 0x4006,
ftdi_8u232am_b921600 = 0x8003
/* Fractional divider values for FT232A/B devices */
static const int ftdi_8u232am_frac[8] = {
0x0 << 14, /* .0 */
0x3 << 14, /* .125 */
0x2 << 14, /* .25 */
0x4 << 14, /* .375 */
0x1 << 14, /* .5 */
0x5 << 14, /* .625 */
0x6 << 14, /* .75 */
0x7 << 14 /* .875 */
};
/*

View File

@ -161,8 +161,8 @@ SerialDevice::SetModes(struct termios *tios)
uint8 baud = tios->c_cflag & CBAUD;
int32 speed = baud_index_to_speed(baud);
if (speed < 0) {
baud = B19200;
speed = 19200;
baud = CBAUD;
speed = tios->c_ospeed;
}
// update our master config in full

View File

@ -105,6 +105,11 @@ tcsendbreak(int fd, int duration)
speed_t
cfgetispeed(const struct termios *termios)
{
if (termios->c_cflag & CBAUD == CBAUD)
{
return termios->c_ispeed;
}
return termios->c_cflag & CBAUD;
}
@ -112,14 +117,14 @@ cfgetispeed(const struct termios *termios)
int
cfsetispeed(struct termios *termios, speed_t speed)
{
/* Check for values that the system cannot handle:
greater values than B230400 which is
the maximum value defined in termios.h
Note that errors from hardware device are detected only
until the tcsetattr() function is called */
if (speed > B230400 || (speed & CBAUD) != speed) {
__set_errno(EINVAL);
return -1;
/* Check for custom baudrates, which must be stored in the c_ispeed
field instead of inlined in the flags.
Note that errors from hardware device (unsupported baudrates, etc) are
detected only when the tcsetattr() function is called */
if (speed > B31250) {
termios->c_cflag |= CBAUD;
termios->c_ispeed = speed;
return 0;
}
termios->c_cflag &= ~CBAUD;
@ -131,6 +136,11 @@ cfsetispeed(struct termios *termios, speed_t speed)
speed_t
cfgetospeed(const struct termios *termios)
{
if (termios->c_cflag & CBAUD == CBAUD)
{
return termios->c_ospeed;
}
return termios->c_cflag & CBAUD;
}
@ -138,10 +148,11 @@ cfgetospeed(const struct termios *termios)
int
cfsetospeed(struct termios *termios, speed_t speed)
{
/* Check for unaccepted speed values (see above) */
if (speed > B230400 || (speed & CBAUD) != speed) {
__set_errno(EINVAL);
return -1;
/* Check for custom speed values (see above) */
if (speed > B31250) {
termios->c_cflag |= CBAUD;
termios->c_ospeed = speed;
return 0;
}
termios->c_cflag &= ~CBAUD;