Menu

#53 High baudrates not supported on Mac OS X

closed-accepted
None
5
2009-07-24
2009-02-04
No

Mac OS X TERMIOS API only supports baudrates up to 230400 bps.

Higher baudrates are not supported, and any attempt to use them ends up with an exception.

On Tiger (10.4.x) and above, it is nevertheless possible to use higher baudrates, using an IOKit-specific ioctl call.

I have not written a patch yet, but here is a code sample I use to circumvent the current pyserial limitation on Mac OS X. It should be rather trivial to adapt it to the actual pyserial mainstream code.

serialclass = serial.Serial
if sys.platform.lower() in ('darwin'):
version = os.uname()[2].split('.')
# Tiger or above can support arbitrary serial speeds
if int(version[0]) >= 8:
# first step: remove all speeds not supported with TERMIOS
# so that pyserial never attempts to use them directly
for b in serial.baudrate_constants.keys():
if b > 230400:
del serial.baudrate_constants[b]
# second step: override the default _reconfigurePort function
class DarwinSerial(serial.Serial):
def _reconfigurePort(port):
try:
serial.Serial._reconfigurePort(port)
except AttributeError:
# third step: use IOKit-specific call to set up
# high speeds
import array, fcntl
buf = array.array('i', [int(port._baudrate)])
IOSSIOSPEED = 0x80045402 #_IOW('T', 2, speed_t)
fcntl.ioctl(port.fd, IOSSIOSPEED, buf, 1)
serialclass = DarwinSerial
if os.path.exists(device):
if stat.S_ISSOCK(os.stat(device)[0]):
from neo.serialext import SerialExtender
serialclass = SerialExtender.serialsocketclass()
port = serialclass(port=device,
baudrate=self.DEFAULT_BAUDRATE,
timeout=0)

Discussion

  • Emmanuel Blot

    Emmanuel Blot - 2009-02-04
    • summary: High baudrates no supported on Mac OS X --> High baudrates not supported on Mac OS X
     
  • Emmanuel Blot

    Emmanuel Blot - 2009-02-04

    File Added: hsserial.py

     
  • Emmanuel Blot

    Emmanuel Blot - 2009-02-04

    High speed workaround on Mac OS X

     
  • Nobody/Anonymous

    This is actually a broader issue than just support of high baud rates under Darwin:

    1) There's some non-POSIX, Linux-specific code in serialposix.py.

    2) Serial.open() silently fails when _reconfigurePort throws an exception.

    3) This pertains to all non-standard baud rates, not just high-speed ones.

    4) Darwin has its own way of handling non-POSIX baud rates.

    Details follow:

    1a) The baudrate_constants defined in serialposix.py are not POSIX -- they are Linux-specific. As a result, when using a "supported" baud rate that is not supported by termios, the following fails on Darwin (and other BSDs):

    Traceback (most recent call last):
    File "./serialtest.py", line 71, in <module>
    port.setBaudrate(500000)
    File "/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/site-packages/serial/serialutil.py", line 231, in setBaudrate
    if self._isOpen: self._reconfigurePort()
    File "/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/site-packages/serial/serialposix.py", line 298, in _reconfigurePort
    termios.tcsetattr(self.fd, TERMIOS.TCSANOW, [iflag, oflag, cflag, lflag, ispeed, ospeed, cc])
    termios.error: (22, 'Invalid argument')

    Emmanuel Blot's code works around this by deleting all high-speed entries from baudrate_constants. However, this isn't strictly correct, as _none_ of the values in baudrate_constants work outside of Linux. The only reason the lower constants don't cause a problem is that the lower baud rates are defined by termio (e.g., "termios.B38400"), in which case baudrate_constants doesn't get used.

    In short, any use of these baudrate_constants is Linux-specific.

    1b) TIOCGSERIAL and TIOCSSERIAL are also Linux-specific and not POSIX. (Also, there's some indication that ASYNC_SPD_CUST is deprecated in Linux.) Therefore, picking a non-POSIX baud rate that's not in the list of "supported" constants causes the following failure:

    Traceback (most recent call last):
    File "./serialtest.py", line 71, in <module>
    port.setBaudrate(400000)
    File "/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/site-packages/serial/serialutil.py", line 231, in setBaudrate
    if self._isOpen: self._reconfigurePort()
    File "/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/site-packages/serial/serialposix.py", line 306, in _reconfigurePort
    FCNTL.ioctl(self.fd, TERMIOS.TIOCGSERIAL, buf)
    AttributeError: 'module' object has no attribute 'TIOCGSERIAL'

    Emmanuel Blot's code works around this by catching the AttributeError exception raised when the TIOCGSERIAL ioctl is attempted (since termios doesn't define TIOCGSERIAL under Darwin). Fortunately, since setting the baud rate is the last thing _reconfigurePort does, the workaround can rely on _reconfigurePort for everything except the custom baud rate.

    Again, this method of supporting non-POSIX baud rates is Linux-specific.

    2) When using a baud rate not supported by the OS, creating a new Serial() object silently fails, because open() doesn't raise an exception when _reconfigurePort fails. Instead, you get the following error as soon as you try to use the port:

    Traceback (most recent call last):
    File "./serialtest.py", line 72, in <module>
    port.write("\r")
    File "/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/site-packages/serial/serialposix.py", line 361, in write
    if self.fd is None: raise portNotOpenError
    serial.serialutil.SerialException: Port not open

    3) As mentioned above, this isn't just high-speed baud rates. Any non-standard baud rate causes failures:

    Traceback (most recent call last):
    File "./serialtest.py", line 71, in <module>
    port.setBaudrate(128)
    File "/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/site-packages/serial/serialutil.py", line 231, in setBaudrate
    if self._isOpen: self._reconfigurePort()
    File "/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/site-packages/serial/serialposix.py", line 306, in _reconfigurePort
    FCNTL.ioctl(self.fd, TERMIOS.TIOCGSERIAL, buf)
    AttributeError: 'module' object has no attribute 'TIOCGSERIAL'

    4) As Emmanuel Blot pointed out, Darwin (8 and higher) can handle custom, non-POSIX baud rates via its own special ioctl.

    import array
    buf = array.array('I', [int(self._baudrate)])
    IOSSIOSPEED = 0x80045402 # _IOW('T', 2, speed_t)
    fcntl.ioctl(port.fd, IOSSIOSPEED, buf, 1)

    I don't know how other BSDs handle non-POSIX baud rates.

     
  • Chris Liechti

    Chris Liechti - 2009-07-21

    the SVN HEAD already contains code to support non standard baudrate on POSIX systems. did you test if that works for you too?

     
  • Chris Liechti

    Chris Liechti - 2009-07-21
    • assigned_to: nobody --> cliechti
    • status: open --> pending
     
  • Emmanuel Blot

    Emmanuel Blot - 2009-07-22

    > the SVN HEAD already contains code to support non standard baudrate on
    > POSIX systems. did you test if that works for you too?

    I've set up a clean Python installation (2.6.2), to be sure that no previously modules get in the way.
    I may have forget something obvious, but I see no improvement:

    # try speed 460800
    Traceback (most recent call last):
    File "host/bin/neoflasher.py", line 1822, in <module>
    loader = NeoFlasher(loadopts)
    File "host/bin/neoflasher.py", line 302, in __init__
    self._baudrates = self._detect_baudrates(self._port)
    File "host/bin/neoflasher.py", line 1615, in _detect_baudrates
    port.setBaudrate(speed)
    File "/Users/eblot/Sources/Svn/sourceforge.net/pyserial/trunk/pyserial/serial/serialutil.py", line 231, in setBaudrate
    if self._isOpen: self._reconfigurePort()
    File "/Users/eblot/Sources/Svn/sourceforge.net/pyserial/trunk/pyserial/serial/serialposix.py", line 308, in _reconfigurePort
    termios.tcsetattr(self.fd, TERMIOS.TCSANOW, [iflag, oflag, cflag, lflag, ispeed, ospeed, cc])
    termios.error: (22, 'Invalid argument')

    Speeds > 230400 still get rejected.

    BTW, you setup.py file is erroneous: it contains an extra ')' character at the very bottom line, which make the setuptools parser to fail.

     
  • Emmanuel Blot

    Emmanuel Blot - 2009-07-22
    • status: pending --> open
     
  • Emmanuel Blot

    Emmanuel Blot - 2009-07-22

    Sorry, I meant:

    I've set up a clean Python installation (2.6.2), to be sure that no
    previously *installed* modules get in the way.
    I may have *forgot* something obvious, but I see no improvement:

     
  • Chris Liechti

    Chris Liechti - 2009-07-23

    thanks for the feedback. i've updated the posix backend so that is uses a platform specific way to set non standard baud rates. if you have time, could you test SVN HEAD?

     
  • Chris Liechti

    Chris Liechti - 2009-07-23
    • status: open --> pending
     
  • Emmanuel Blot

    Emmanuel Blot - 2009-07-24
    • status: pending --> open
     
  • Emmanuel Blot

    Emmanuel Blot - 2009-07-24

    Ok, great, it works with [242], up to 3Mbps on my machine - thanks a lot.

    I've added the following piece of code to enable or not the Darwin hack, depending on the actual pyserial version number. Please, let me know if there is a better way:
    {{{
    serialclass = serial.Serial
    # hack for Mac OS X hosts: the underlying termios system library
    # cannot cope with baudrates > 230kbps with pyserial << 9.7
    if sys.platform.lower() in ('darwin'):
    if 'version' not in serial.__dict__ or \
    serial.version[0] < 9 or serial.version[1] < 7:
    from neo.serialext import SerialExtender
    serialclass = SerialExtender.darwinserialclass()
    }}}

     
  • Chris Liechti

    Chris Liechti - 2009-07-24
    • status: open --> closed-accepted
     
  • Chris Liechti

    Chris Liechti - 2009-07-24

    you could check for the set_special_baudrate function with hasattr()

    thanks for your feedback, closing the bug

     

Log in to post a comment.

MongoDB Logo MongoDB