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:
@@ -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
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
Reference in New Issue
Block a user