I have an application requiring non-standard baud rates and have modified synaser.pas to accommodate this for Linux and Mac (Windows works with non-standard rates already).
Attached is the patch (against synaser.pas 007.005.002) which implements the TIOCSSERIAL ioctl (for Linux) and the FIOSSIOSPEED ioctl (for Mac). It has been tested and works with several FT232xx USB/Serial chips and it should at the very least be benign when used with other devices that don't support non-standard rates. This patch also fixes a number of bugs with the DARWIN (Mac) implementation, in particular the open call (which must be non-blocking to succeed) and modified TCIOflush call (previously the third argument was not correct). There's also a typecast at the end required to avoid strict compilation errors. I recommend changes derived from this patch be included in synaser.pas upstream. For more information see: http://lists.freepascal.org/lists/fpc-pascal/2011-August/029996.html http://lists.freepascal.org/lists/fpc-pascal/2011-August/030025.html I hope this update is useful to others. Cheers, Bruce.
*** a/synaser.pas Tue Aug 16 10:02:01 2011 --- b/synaser.pas Tue Aug 16 10:04:09 2011 *************** *** 195,200 **** --- 195,232 ---- end; PDCB = ^TDCB; + {$IFDEF LINUX} + { Translated from include/linux/serial.h } + type + TSerialStruct = packed record + typ: Integer; + line: Integer; + port: Cardinal; + irq: Integer; + flags: Integer; + xmit_fifo_size: Integer; + custom_divisor: Integer; + baud_base: Integer; + close_delay: Word; + io_type: Char; + reserved_char: Char; + hub6: Integer; + closing_wait: Word; // time to wait before closing + closing_wait2: Word; // no longer used... + iomem_base: ^Char; + iomem_reg_shift: Word; + port_high: Cardinal; + iomap_base: LongWord; // cookie passed into ioremap + end; + + const + { Translated from include/linux/serial.h | 1818 } + ASYNC_SPD_MASK = $1030; + ASYNC_SPD_CUST = $0030; + { Baud rate dividend assuming FT232BM compatible devices | 1818 } + FT_BAUD_BASE = 48000000; + {$ENDIF} + const {$IFDEF UNIX} {$IFDEF DARWIN} *************** *** 248,253 **** --- 280,289 ---- {$IFDEF DARWIN} const // From fcntl.h O_SYNC = $0080; { synchronous writes } + { FIOSSIOSPEED is from /System/Library/Frameworks/IOKit.framework/Versions/A/Headers/serial/ioss.h + Its definition will be in $FPC/rtl/darwin/termios.inc soon with any luck. For details see: + http://lists.freepascal.org/lists/fpc-pascal/2011-August/030025.html } + FIOSSIOSPEED = (IOC_IN or (sizeof(culong) and IOCPARM_MASK) << 16) or ((ord('T') << 8) or 2); { 18399.13 } {$ENDIF} const *************** *** 943,949 **** {$IFNDEF FPC} FHandle := THandle(Libc.open(pchar(FDevice), O_RDWR or O_SYNC)); {$ELSE} ! FHandle := THandle(fpOpen(FDevice, O_RDWR or O_SYNC)); {$ENDIF} if FHandle = INVALID_HANDLE_VALUE then //because THandle is not integer on all platforms! SerialCheck(-1) --- 979,985 ---- {$IFNDEF FPC} FHandle := THandle(Libc.open(pchar(FDevice), O_RDWR or O_SYNC)); {$ELSE} ! FHandle := THandle(fpOpen(FDevice, O_RDWR or O_SYNC or O_NONBLOCK)); {$ENDIF} if FHandle = INVALID_HANDLE_VALUE then //because THandle is not integer on all platforms! SerialCheck(-1) *************** *** 1556,1562 **** else term.c_cflag := term.c_cflag and (not CSTOPB); //set baudrate; ! x := 0; for n := 0 to Maxrates do if rates[n, 0] = dcb.BaudRate then begin --- 1592,1598 ---- else term.c_cflag := term.c_cflag and (not CSTOPB); //set baudrate; ! x := B38400; { default (and aliased) baud rate, see TBlockSerial.SetCommState } for n := 0 to Maxrates do if rates[n, 0] = dcb.BaudRate then begin *************** *** 1627,1636 **** --- 1663,1721 ---- {$IFNDEF MSWINDOWS} procedure TBlockSerial.SetCommState; + {$IFDEF LINUX} + var + Ser : TSerialStruct; + {$ENDIF} begin DcbToTermios(dcb, termiosstruc); SerialCheck(tcsetattr(FHandle, TCSANOW, termiosstruc)); ExceptCheck; + {$IFDEF DARWIN} + { Use FIOSSIOSPEED to assign the baud rate directly. This overides the + baud rate assigned via the cfsetXspeed functions above but they must + still be called to assign valid control flag values in the termios } + SerialCheck(fpIoctl(integer(FHandle), FIOSSIOSPEED, @dcb.BaudRate)); + ExceptCheck; + {$ELSE} + { Use TIOCSSERIAL to assign the baud rate directly. This overrides the + baud rate assigned via the cfsetXspeed functions above but they must + still be called to assign valid control flag values in the termios. + If the standard rate B38400 is used the following code will assign + the actual baud rate. Otherwise it does nothing. This assumes we're + dealing with an FT232xx (or compatible) device per step 3 in comments + from the Linux driver reproduced here | 1818 + + * The logic involved in setting the baudrate can be cleanly split in 3 steps. + * Obtaining the actual baud rate is a little tricky since unix traditionally + * somehow ignored the possibility to set non-standard baud rates. + * 1. Standard baud rates are set in tty->termios->c_cflag + * 2. If these are not enough, you can set any speed using alt_speed as follows: + * - set tty->termios->c_cflag speed to B38400 + * - set your real speed in tty->alt_speed; it gets ignored when + * alt_speed==0, (or) + * - call TIOCSSERIAL ioctl with (struct serial_struct) set as follows: + * flags & ASYNC_SPD_MASK == ASYNC_SPD_[HI, VHI, SHI, WARP], this just + * sets alt_speed to (HI: 57600, VHI: 115200, SHI: 230400, WARP: 460800) + * ** Steps 1, 2 are done courtesy of tty_get_baud_rate + * 3. You can also set baud rate by setting custom divisor as follows + * - set tty->termios->c_cflag speed to B38400 + * - call TIOCSSERIAL ioctl with (struct serial_struct) set as follows: + * o flags & ASYNC_SPD_MASK == ASYNC_SPD_CUST + * o custom_divisor set to baud_base / your_new_baudrate + * ** Step 3 is done courtesy of code borrowed from serial.c - I should really + * spend some time and separate+move this common code to serial.c, it is + * replicated in nearly every serial driver you see } + + fpIoctl(integer(FHandle), TIOCGSERIAL, @Ser); + with Ser do begin + baud_base := FT_BAUD_BASE; + custom_divisor := (Cardinal(baud_base) div dcb.BaudRate) div 2; + flags := flags or ASYNC_SPD_CUST; + end; + SerialCheck(fpIoctl(integer(FHandle), TIOCSSERIAL, @Ser)); + ExceptCheck; + {$ENDIF} end; {$ELSE} procedure TBlockSerial.SetCommState; *************** *** 1936,1942 **** SerialCheck(ioctl(FHandle, TCFLSH, TCIOFLUSH)); {$ELSE} {$IFDEF DARWIN} ! SerialCheck(fpioctl(FHandle, TCIOflush, TCIOFLUSH)); {$ELSE} SerialCheck(fpioctl(FHandle, TCFLSH, Pointer(PtrInt(TCIOFLUSH)))); {$ENDIF} --- 2021,2027 ---- SerialCheck(ioctl(FHandle, TCFLSH, TCIOFLUSH)); {$ELSE} {$IFDEF DARWIN} ! SerialCheck(fpioctl(FHandle, TCIOflush, Pointer(PtrInt(TCIOFLUSH)))); {$ELSE} SerialCheck(fpioctl(FHandle, TCFLSH, Pointer(PtrInt(TCIOFLUSH)))); {$ENDIF} *************** *** 2316,2322 **** begin try TmpPorts := ''; ! if FindFirst('/dev/ttyS*', $FFFFFFFF, sr) = 0 then begin repeat if (sr.Attr and $FFFFFFFF) = Sr.Attr then --- 2401,2407 ---- begin try TmpPorts := ''; ! if FindFirst('/dev/ttyS*', LongInt($FFFFFFFF), sr) = 0 then begin repeat if (sr.Attr and $FFFFFFFF) = Sr.Attr then
------------------------------------------------------------------------------ uberSVN's rich system and user administration capabilities and model configuration take the hassle out of deploying and managing Subversion and the tools developers use with it. Learn more about uberSVN and get a free download at: http://p.sf.net/sfu/wandisco-dev2dev
_______________________________________________ synalist-public mailing list synalist-public@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/synalist-public