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

Reply via email to