diff options
Diffstat (limited to 'serial/serialposix.py')
-rw-r--r-- | serial/serialposix.py | 74 |
1 files changed, 59 insertions, 15 deletions
diff --git a/serial/serialposix.py b/serial/serialposix.py index bb2fa03..507e2fe 100644 --- a/serial/serialposix.py +++ b/serial/serialposix.py @@ -26,6 +26,8 @@ # - aix (AIX) /dev/tty%d +from __future__ import absolute_import + # pylint: disable=abstract-method import errno import fcntl @@ -49,6 +51,9 @@ class PlatformSpecificBase(object): def _set_rs485_mode(self, rs485_settings): raise NotImplementedError('RS485 not supported on this platform') + def set_low_latency_mode(self, low_latency_settings): + raise NotImplementedError('Low latency not supported on this platform') + # some systems support an extra flag to enable the two in POSIX unsupported # paritiy settings for MARK and SPACE @@ -113,6 +118,24 @@ if plat[:5] == 'linux': # Linux (confirmed) # noqa 4000000: 0o010017 } + def set_low_latency_mode(self, low_latency_settings): + buf = array.array('i', [0] * 32) + + try: + # get serial_struct + fcntl.ioctl(self.fd, termios.TIOCGSERIAL, buf) + + # set or unset ASYNC_LOW_LATENCY flag + if low_latency_settings: + buf[4] |= 0x2000 + else: + buf[4] &= ~0x2000 + + # set serial_struct + fcntl.ioctl(self.fd, termios.TIOCSSERIAL, buf) + except IOError as e: + raise ValueError('Failed to update ASYNC_LOW_LATENCY flag to {}: {}'.format(low_latency_settings, e)) + def _set_special_baudrate(self, baudrate): # right size is 44 on x86_64, allow for some growth buf = array.array('i', [0] * 64) @@ -503,24 +526,27 @@ class Serial(SerialBase, PlatformSpecific): read.extend(buf) except OSError as e: # this is for Python 3.x where select.error is a subclass of - # OSError ignore EAGAIN errors. all other errors are shown - if e.errno != errno.EAGAIN and e.errno != errno.EINTR: + # OSError ignore BlockingIOErrors and EINTR. other errors are shown + # https://www.python.org/dev/peps/pep-0475. + if e.errno not in (errno.EAGAIN, errno.EALREADY, errno.EWOULDBLOCK, errno.EINPROGRESS, errno.EINTR): raise SerialException('read failed: {}'.format(e)) except select.error as e: # this is for Python 2.x - # ignore EAGAIN errors. all other errors are shown + # ignore BlockingIOErrors and EINTR. all errors are shown # see also http://www.python.org/dev/peps/pep-3151/#select - if e[0] != errno.EAGAIN: + if e[0] not in (errno.EAGAIN, errno.EALREADY, errno.EWOULDBLOCK, errno.EINPROGRESS, errno.EINTR): raise SerialException('read failed: {}'.format(e)) if timeout.expired(): break return bytes(read) def cancel_read(self): - os.write(self.pipe_abort_read_w, b"x") + if self.is_open: + os.write(self.pipe_abort_read_w, b"x") def cancel_write(self): - os.write(self.pipe_abort_write_w, b"x") + if self.is_open: + os.write(self.pipe_abort_write_w, b"x") def write(self, data): """Output the given byte string over the serial port.""" @@ -560,12 +586,20 @@ class Serial(SerialBase, PlatformSpecific): tx_len -= n except SerialException: raise - except OSError as v: - if v.errno != errno.EAGAIN: - raise SerialException('write failed: {}'.format(v)) - # still calculate and check timeout - if timeout.expired(): - raise writeTimeoutError + except OSError as e: + # this is for Python 3.x where select.error is a subclass of + # OSError ignore BlockingIOErrors and EINTR. other errors are shown + # https://www.python.org/dev/peps/pep-0475. + if e.errno not in (errno.EAGAIN, errno.EALREADY, errno.EWOULDBLOCK, errno.EINPROGRESS, errno.EINTR): + raise SerialException('write failed: {}'.format(e)) + except select.error as e: + # this is for Python 2.x + # ignore BlockingIOErrors and EINTR. all errors are shown + # see also http://www.python.org/dev/peps/pep-3151/#select + if e[0] not in (errno.EAGAIN, errno.EALREADY, errno.EWOULDBLOCK, errno.EINPROGRESS, errno.EINTR): + raise SerialException('write failed: {}'.format(e)) + if not timeout.is_non_blocking and timeout.expired(): + raise writeTimeoutError return length - len(d) def flush(self): @@ -722,21 +756,28 @@ class PosixPollSerial(Serial): if not self.is_open: raise portNotOpenError read = bytearray() + timeout = Timeout(self._timeout) poll = select.poll() poll.register(self.fd, select.POLLIN | select.POLLERR | select.POLLHUP | select.POLLNVAL) + poll.register(self.pipe_abort_read_r, select.POLLIN | select.POLLERR | select.POLLHUP | select.POLLNVAL) if size > 0: while len(read) < size: # print "\tread(): size",size, "have", len(read) #debug # wait until device becomes ready to read (or something fails) - for fd, event in poll.poll(self._timeout * 1000): + for fd, event in poll.poll(None if timeout.is_infinite else (timeout.time_left() * 1000)): + if fd == self.pipe_abort_read_r: + break if event & (select.POLLERR | select.POLLHUP | select.POLLNVAL): raise SerialException('device reports error (poll)') # we don't care if it is select.POLLIN or timeout, that's # handled below + if fd == self.pipe_abort_read_r: + os.read(self.pipe_abort_read_r, 1000) + break buf = os.read(self.fd, size - len(read)) read.extend(buf) - if ((self._timeout is not None and self._timeout >= 0) or - (self._inter_byte_timeout is not None and self._inter_byte_timeout > 0)) and not buf: + if timeout.expired() \ + or (self._inter_byte_timeout is not None and self._inter_byte_timeout > 0) and not buf: break # early abort on timeout return bytes(read) @@ -748,6 +789,9 @@ class VTIMESerial(Serial): the error handling is degraded. Overall timeout is disabled when inter-character timeout is used. + + Note that this implementation does NOT support cancel_read(), it will + just ignore that. """ def _reconfigure_port(self, force_update=True): |