diff --git a/headers/posix/termios.h b/headers/posix/termios.h index f1c800f8ef..141746eb23 100644 --- a/headers/posix/termios.h +++ b/headers/posix/termios.h @@ -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 */ }; diff --git a/src/add-ons/kernel/drivers/ports/usb_serial/FTDI.cpp b/src/add-ons/kernel/drivers/ports/usb_serial/FTDI.cpp index 649975bbd9..60f2a79b97 100644 --- a/src/add-ons/kernel/drivers/ports/usb_serial/FTDI.cpp +++ b/src/add-ons/kernel/drivers/ports/usb_serial/FTDI.cpp @@ -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; } } diff --git a/src/add-ons/kernel/drivers/ports/usb_serial/FTDIRegs.h b/src/add-ons/kernel/drivers/ports/usb_serial/FTDIRegs.h index 2ae840130f..dd2e552cce 100644 --- a/src/add-ons/kernel/drivers/ports/usb_serial/FTDIRegs.h +++ b/src/add-ons/kernel/drivers/ports/usb_serial/FTDIRegs.h @@ -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 */ }; /* diff --git a/src/add-ons/kernel/drivers/ports/usb_serial/SerialDevice.cpp b/src/add-ons/kernel/drivers/ports/usb_serial/SerialDevice.cpp index 4af8c16a30..eca908f222 100644 --- a/src/add-ons/kernel/drivers/ports/usb_serial/SerialDevice.cpp +++ b/src/add-ons/kernel/drivers/ports/usb_serial/SerialDevice.cpp @@ -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 diff --git a/src/system/libroot/posix/termios.c b/src/system/libroot/posix/termios.c index 9a6e57bf94..f9191da6fd 100644 --- a/src/system/libroot/posix/termios.c +++ b/src/system/libroot/posix/termios.c @@ -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;