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
+2 -2
View File
@@ -21,8 +21,8 @@ struct termios {
tcflag_t c_cflag; /* control modes */ tcflag_t c_cflag; /* control modes */
tcflag_t c_lflag; /* local modes */ tcflag_t c_lflag; /* local modes */
char c_line; /* line discipline */ char c_line; /* line discipline */
speed_t c_ispeed; /* (unused) */ speed_t c_ispeed; /* custom input baudrate */
speed_t c_ospeed; /* (unused) */ speed_t c_ospeed; /* custom output baudrate */
cc_t c_cc[NCCS]; /* control characters */ cc_t c_cc[NCCS]; /* control characters */
}; };
@@ -111,26 +111,24 @@ FTDIDevice::SetLineCoding(usb_cdc_line_coding *lineCoding)
break; break;
} }
} else { } else {
switch (lineCoding->speed) { /* Compute baudrate register value as documented in AN232B-05 from FTDI.
case 300: rate = ftdi_8u232am_b300; break; Bits 13-0 are the integer divider, and bits 16-14 are the fractional
case 600: rate = ftdi_8u232am_b600; break; divider setting. 3Mbaud and 2Mbaud are special values, and at such
case 1200: rate = ftdi_8u232am_b1200; break; high speeds the use of the fractional divider is not possible. */
case 2400: rate = ftdi_8u232am_b2400; break; if (lineCoding->speed == 3000000)
case 4800: rate = ftdi_8u232am_b4800; break; rate = 0;
case 9600: rate = ftdi_8u232am_b9600; break; else if (lineCoding->speed == 2000000)
case 19200: rate = ftdi_8u232am_b19200; break; rate = 1;
case 38400: rate = ftdi_8u232am_b38400; break; else {
case 57600: rate = ftdi_8u232am_b57600; break; if (lineCoding->speed > 1500000) {
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;
TRACE_ALWAYS("= FTDIDevice::SetLineCoding(): Datarate: %d is " TRACE_ALWAYS("= FTDIDevice::SetLineCoding(): Datarate: %d is "
"not supported by this hardware. Defaulted to %d\n", "not supported by this hardware. Defaulted to %d\n",
lineCoding->speed, rate); lineCoding->speed, 19200);
break; lineCoding->speed = 19200;
}
rate = 3000000 * 8 / lineCoding->speed;
int frac = ftdi_8u232am_frac[rate & 0x7];
rate = (rate >> 3) | frac;
} }
} }
@@ -101,20 +101,17 @@ enum {
ftdi_sio_b115200 = 9 ftdi_sio_b115200 = 9
}; };
enum {
ftdi_8u232am_b300 = 0x2710, /* Fractional divider values for FT232A/B devices */
ftdi_8u232am_b600 = 0x1388, static const int ftdi_8u232am_frac[8] = {
ftdi_8u232am_b1200 = 0x09c4, 0x0 << 14, /* .0 */
ftdi_8u232am_b2400 = 0x04e2, 0x3 << 14, /* .125 */
ftdi_8u232am_b4800 = 0x0271, 0x2 << 14, /* .25 */
ftdi_8u232am_b9600 = 0x4138, 0x4 << 14, /* .375 */
ftdi_8u232am_b19200 = 0x809c, 0x1 << 14, /* .5 */
ftdi_8u232am_b38400 = 0xc04e, 0x5 << 14, /* .625 */
ftdi_8u232am_b57600 = 0x0034, 0x6 << 14, /* .75 */
ftdi_8u232am_b115200 = 0x001a, 0x7 << 14 /* .875 */
ftdi_8u232am_b230400 = 0x000d,
ftdi_8u232am_b460800 = 0x4006,
ftdi_8u232am_b921600 = 0x8003
}; };
/* /*
@@ -161,8 +161,8 @@ SerialDevice::SetModes(struct termios *tios)
uint8 baud = tios->c_cflag & CBAUD; uint8 baud = tios->c_cflag & CBAUD;
int32 speed = baud_index_to_speed(baud); int32 speed = baud_index_to_speed(baud);
if (speed < 0) { if (speed < 0) {
baud = B19200; baud = CBAUD;
speed = 19200; speed = tios->c_ospeed;
} }
// update our master config in full // update our master config in full
+23 -12
View File
@@ -105,6 +105,11 @@ tcsendbreak(int fd, int duration)
speed_t speed_t
cfgetispeed(const struct termios *termios) cfgetispeed(const struct termios *termios)
{ {
if (termios->c_cflag & CBAUD == CBAUD)
{
return termios->c_ispeed;
}
return termios->c_cflag & CBAUD; return termios->c_cflag & CBAUD;
} }
@@ -112,14 +117,14 @@ cfgetispeed(const struct termios *termios)
int int
cfsetispeed(struct termios *termios, speed_t speed) cfsetispeed(struct termios *termios, speed_t speed)
{ {
/* Check for values that the system cannot handle: /* Check for custom baudrates, which must be stored in the c_ispeed
greater values than B230400 which is field instead of inlined in the flags.
the maximum value defined in termios.h Note that errors from hardware device (unsupported baudrates, etc) are
Note that errors from hardware device are detected only detected only when the tcsetattr() function is called */
until the tcsetattr() function is called */ if (speed > B31250) {
if (speed > B230400 || (speed & CBAUD) != speed) { termios->c_cflag |= CBAUD;
__set_errno(EINVAL); termios->c_ispeed = speed;
return -1; return 0;
} }
termios->c_cflag &= ~CBAUD; termios->c_cflag &= ~CBAUD;
@@ -131,6 +136,11 @@ cfsetispeed(struct termios *termios, speed_t speed)
speed_t speed_t
cfgetospeed(const struct termios *termios) cfgetospeed(const struct termios *termios)
{ {
if (termios->c_cflag & CBAUD == CBAUD)
{
return termios->c_ospeed;
}
return termios->c_cflag & CBAUD; return termios->c_cflag & CBAUD;
} }
@@ -138,10 +148,11 @@ cfgetospeed(const struct termios *termios)
int int
cfsetospeed(struct termios *termios, speed_t speed) cfsetospeed(struct termios *termios, speed_t speed)
{ {
/* Check for unaccepted speed values (see above) */ /* Check for custom speed values (see above) */
if (speed > B230400 || (speed & CBAUD) != speed) { if (speed > B31250) {
__set_errno(EINVAL); termios->c_cflag |= CBAUD;
return -1; termios->c_ospeed = speed;
return 0;
} }
termios->c_cflag &= ~CBAUD; termios->c_cflag &= ~CBAUD;