1#!/usr/bin/env python
2#
3# Python Serial Port Extension for Win32, Linux, BSD, Jython
4# module for serial IO for POSIX compatible systems, like Linux
5# see __init__.py
6#
7# (C) 2001-2010 Chris Liechti <cliechti@gmx.net>
8# this is distributed under a free software license, see license.txt
9#
10# parts based on code from Grant B. Edwards  <grante@visi.com>:
11#  ftp://ftp.visi.com/users/grante/python/PosixSerial.py
12#
13# references: http://www.easysw.com/~mike/serial/serial.html
14
15import sys, os, fcntl, termios, struct, select, errno, time
16from serial.serialutil import *
17
18# Do check the Python version as some constants have moved.
19if (sys.hexversion < 0x020100f0):
20    import TERMIOS
21else:
22    TERMIOS = termios
23
24if (sys.hexversion < 0x020200f0):
25    import FCNTL
26else:
27    FCNTL = fcntl
28
29# try to detect the OS so that a device can be selected...
30# this code block should supply a device() and set_special_baudrate() function
31# for the platform
32plat = sys.platform.lower()
33
34if   plat[:5] == 'linux':    # Linux (confirmed)
35
36    def device(port):
37        return '/dev/ttyS%d' % port
38
39    TCGETS2 = 0x802C542A
40    TCSETS2 = 0x402C542B
41    BOTHER = 0o010000
42
43    def set_special_baudrate(port, baudrate):
44        # right size is 44 on x86_64, allow for some growth
45        import array
46        buf = array.array('i', [0] * 64)
47
48        try:
49            # get serial_struct
50            FCNTL.ioctl(port.fd, TCGETS2, buf)
51            # set custom speed
52            buf[2] &= ~TERMIOS.CBAUD
53            buf[2] |= BOTHER
54            buf[9] = buf[10] = baudrate
55
56            # set serial_struct
57            res = FCNTL.ioctl(port.fd, TCSETS2, buf)
58        except IOError, e:
59            raise ValueError('Failed to set custom baud rate (%s): %s' % (baudrate, e))
60
61    baudrate_constants = {
62        0:       0000000,  # hang up
63        50:      0000001,
64        75:      0000002,
65        110:     0000003,
66        134:     0000004,
67        150:     0000005,
68        200:     0000006,
69        300:     0000007,
70        600:     0000010,
71        1200:    0000011,
72        1800:    0000012,
73        2400:    0000013,
74        4800:    0000014,
75        9600:    0000015,
76        19200:   0000016,
77        38400:   0000017,
78        57600:   0010001,
79        115200:  0010002,
80        230400:  0010003,
81        460800:  0010004,
82        500000:  0010005,
83        576000:  0010006,
84        921600:  0010007,
85        1000000: 0010010,
86        1152000: 0010011,
87        1500000: 0010012,
88        2000000: 0010013,
89        2500000: 0010014,
90        3000000: 0010015,
91        3500000: 0010016,
92        4000000: 0010017
93    }
94
95elif plat == 'cygwin':       # cygwin/win32 (confirmed)
96
97    def device(port):
98        return '/dev/com%d' % (port + 1)
99
100    def set_special_baudrate(port, baudrate):
101        raise ValueError("sorry don't know how to handle non standard baud rate on this platform")
102
103    baudrate_constants = {
104        128000: 0x01003,
105        256000: 0x01005,
106        500000: 0x01007,
107        576000: 0x01008,
108        921600: 0x01009,
109        1000000: 0x0100a,
110        1152000: 0x0100b,
111        1500000: 0x0100c,
112        2000000: 0x0100d,
113        2500000: 0x0100e,
114        3000000: 0x0100f
115    }
116
117elif plat[:7] == 'openbsd':    # OpenBSD
118
119    def device(port):
120        return '/dev/cua%02d' % port
121
122    def set_special_baudrate(port, baudrate):
123        raise ValueError("sorry don't know how to handle non standard baud rate on this platform")
124
125    baudrate_constants = {}
126
127elif plat[:3] == 'bsd' or  \
128    plat[:7] == 'freebsd':
129
130    def device(port):
131        return '/dev/cuad%d' % port
132
133    def set_special_baudrate(port, baudrate):
134        raise ValueError("sorry don't know how to handle non standard baud rate on this platform")
135
136    baudrate_constants = {}
137
138elif plat[:6] == 'darwin':   # OS X
139
140    version = os.uname()[2].split('.')
141    # Tiger or above can support arbitrary serial speeds
142    if int(version[0]) >= 8:
143        def set_special_baudrate(port, baudrate):
144            # use IOKit-specific call to set up high speeds
145            import array, fcntl
146            buf = array.array('i', [baudrate])
147            IOSSIOSPEED = 0x80045402 #_IOW('T', 2, speed_t)
148            fcntl.ioctl(port.fd, IOSSIOSPEED, buf, 1)
149    else: # version < 8
150        def set_special_baudrate(port, baudrate):
151            raise ValueError("baud rate not supported")
152
153    def device(port):
154        return '/dev/cuad%d' % port
155
156    baudrate_constants = {}
157
158
159elif plat[:6] == 'netbsd':   # NetBSD 1.6 testing by Erk
160
161    def device(port):
162        return '/dev/dty%02d' % port
163
164    def set_special_baudrate(port, baudrate):
165        raise ValueError("sorry don't know how to handle non standard baud rate on this platform")
166
167    baudrate_constants = {}
168
169elif plat[:4] == 'irix':     # IRIX (partially tested)
170
171    def device(port):
172        return '/dev/ttyf%d' % (port+1) #XXX different device names depending on flow control
173
174    def set_special_baudrate(port, baudrate):
175        raise ValueError("sorry don't know how to handle non standard baud rate on this platform")
176
177    baudrate_constants = {}
178
179elif plat[:2] == 'hp':       # HP-UX (not tested)
180
181    def device(port):
182        return '/dev/tty%dp0' % (port+1)
183
184    def set_special_baudrate(port, baudrate):
185        raise ValueError("sorry don't know how to handle non standard baud rate on this platform")
186
187    baudrate_constants = {}
188
189elif plat[:5] == 'sunos':    # Solaris/SunOS (confirmed)
190
191    def device(port):
192        return '/dev/tty%c' % (ord('a')+port)
193
194    def set_special_baudrate(port, baudrate):
195        raise ValueError("sorry don't know how to handle non standard baud rate on this platform")
196
197    baudrate_constants = {}
198
199elif plat[:3] == 'aix':      # AIX
200
201    def device(port):
202        return '/dev/tty%d' % (port)
203
204    def set_special_baudrate(port, baudrate):
205        raise ValueError("sorry don't know how to handle non standard baud rate on this platform")
206
207    baudrate_constants = {}
208
209else:
210    # platform detection has failed...
211    sys.stderr.write("""\
212don't know how to number ttys on this system.
213! Use an explicit path (eg /dev/ttyS1) or send this information to
214! the author of this module:
215
216sys.platform = %r
217os.name = %r
218serialposix.py version = %s
219
220also add the device name of the serial port and where the
221counting starts for the first serial port.
222e.g. 'first serial port: /dev/ttyS0'
223and with a bit luck you can get this module running...
224""" % (sys.platform, os.name, VERSION))
225    # no exception, just continue with a brave attempt to build a device name
226    # even if the device name is not correct for the platform it has chances
227    # to work using a string with the real device name as port parameter.
228    def device(portum):
229        return '/dev/ttyS%d' % portnum
230    def set_special_baudrate(port, baudrate):
231        raise SerialException("sorry don't know how to handle non standard baud rate on this platform")
232    baudrate_constants = {}
233    #~ raise Exception, "this module does not run on this platform, sorry."
234
235# whats up with "aix", "beos", ....
236# they should work, just need to know the device names.
237
238
239# load some constants for later use.
240# try to use values from TERMIOS, use defaults from linux otherwise
241TIOCMGET  = hasattr(TERMIOS, 'TIOCMGET') and TERMIOS.TIOCMGET or 0x5415
242TIOCMBIS  = hasattr(TERMIOS, 'TIOCMBIS') and TERMIOS.TIOCMBIS or 0x5416
243TIOCMBIC  = hasattr(TERMIOS, 'TIOCMBIC') and TERMIOS.TIOCMBIC or 0x5417
244TIOCMSET  = hasattr(TERMIOS, 'TIOCMSET') and TERMIOS.TIOCMSET or 0x5418
245
246#TIOCM_LE = hasattr(TERMIOS, 'TIOCM_LE') and TERMIOS.TIOCM_LE or 0x001
247TIOCM_DTR = hasattr(TERMIOS, 'TIOCM_DTR') and TERMIOS.TIOCM_DTR or 0x002
248TIOCM_RTS = hasattr(TERMIOS, 'TIOCM_RTS') and TERMIOS.TIOCM_RTS or 0x004
249#TIOCM_ST = hasattr(TERMIOS, 'TIOCM_ST') and TERMIOS.TIOCM_ST or 0x008
250#TIOCM_SR = hasattr(TERMIOS, 'TIOCM_SR') and TERMIOS.TIOCM_SR or 0x010
251
252TIOCM_CTS = hasattr(TERMIOS, 'TIOCM_CTS') and TERMIOS.TIOCM_CTS or 0x020
253TIOCM_CAR = hasattr(TERMIOS, 'TIOCM_CAR') and TERMIOS.TIOCM_CAR or 0x040
254TIOCM_RNG = hasattr(TERMIOS, 'TIOCM_RNG') and TERMIOS.TIOCM_RNG or 0x080
255TIOCM_DSR = hasattr(TERMIOS, 'TIOCM_DSR') and TERMIOS.TIOCM_DSR or 0x100
256TIOCM_CD  = hasattr(TERMIOS, 'TIOCM_CD') and TERMIOS.TIOCM_CD or TIOCM_CAR
257TIOCM_RI  = hasattr(TERMIOS, 'TIOCM_RI') and TERMIOS.TIOCM_RI or TIOCM_RNG
258#TIOCM_OUT1 = hasattr(TERMIOS, 'TIOCM_OUT1') and TERMIOS.TIOCM_OUT1 or 0x2000
259#TIOCM_OUT2 = hasattr(TERMIOS, 'TIOCM_OUT2') and TERMIOS.TIOCM_OUT2 or 0x4000
260if hasattr(TERMIOS, 'TIOCINQ'):
261    TIOCINQ = TERMIOS.TIOCINQ
262else:
263    TIOCINQ = hasattr(TERMIOS, 'FIONREAD') and TERMIOS.FIONREAD or 0x541B
264TIOCOUTQ   = hasattr(TERMIOS, 'TIOCOUTQ') and TERMIOS.TIOCOUTQ or 0x5411
265
266TIOCM_zero_str = struct.pack('I', 0)
267TIOCM_RTS_str = struct.pack('I', TIOCM_RTS)
268TIOCM_DTR_str = struct.pack('I', TIOCM_DTR)
269
270TIOCSBRK  = hasattr(TERMIOS, 'TIOCSBRK') and TERMIOS.TIOCSBRK or 0x5427
271TIOCCBRK  = hasattr(TERMIOS, 'TIOCCBRK') and TERMIOS.TIOCCBRK or 0x5428
272
273
274class PosixSerial(SerialBase):
275    """Serial port class POSIX implementation. Serial port configuration is
276    done with termios and fcntl. Runs on Linux and many other Un*x like
277    systems."""
278
279    def open(self):
280        """Open port with current settings. This may throw a SerialException
281           if the port cannot be opened."""
282        if self._port is None:
283            raise SerialException("Port must be configured before it can be used.")
284        if self._isOpen:
285            raise SerialException("Port is already open.")
286        self.fd = None
287        # open
288        try:
289            self.fd = os.open(self.portstr, os.O_RDWR|os.O_NOCTTY|os.O_NONBLOCK)
290        except IOError, msg:
291            self.fd = None
292            raise SerialException(msg.errno, "could not open port %s: %s" % (self._port, msg))
293        #~ fcntl.fcntl(self.fd, FCNTL.F_SETFL, 0)  # set blocking
294
295        try:
296            self._reconfigurePort()
297        except:
298            try:
299                os.close(self.fd)
300            except:
301                # ignore any exception when closing the port
302                # also to keep original exception that happened when setting up
303                pass
304            self.fd = None
305            raise
306        else:
307            self._isOpen = True
308        self.flushInput()
309
310
311    def _reconfigurePort(self):
312        """Set communication parameters on opened port."""
313        if self.fd is None:
314            raise SerialException("Can only operate on a valid file descriptor")
315        custom_baud = None
316
317        vmin = vtime = 0                # timeout is done via select
318        if self._interCharTimeout is not None:
319            vmin = 1
320            vtime = int(self._interCharTimeout * 10)
321        try:
322            orig_attr = termios.tcgetattr(self.fd)
323            iflag, oflag, cflag, lflag, ispeed, ospeed, cc = orig_attr
324        except termios.error, msg:      # if a port is nonexistent but has a /dev file, it'll fail here
325            raise SerialException("Could not configure port: %s" % msg)
326        # set up raw mode / no echo / binary
327        cflag |=  (TERMIOS.CLOCAL|TERMIOS.CREAD)
328        lflag &= ~(TERMIOS.ICANON|TERMIOS.ECHO|TERMIOS.ECHOE|TERMIOS.ECHOK|TERMIOS.ECHONL|
329                     TERMIOS.ISIG|TERMIOS.IEXTEN) #|TERMIOS.ECHOPRT
330        for flag in ('ECHOCTL', 'ECHOKE'): # netbsd workaround for Erk
331            if hasattr(TERMIOS, flag):
332                lflag &= ~getattr(TERMIOS, flag)
333
334        oflag &= ~(TERMIOS.OPOST)
335        iflag &= ~(TERMIOS.INLCR|TERMIOS.IGNCR|TERMIOS.ICRNL|TERMIOS.IGNBRK)
336        if hasattr(TERMIOS, 'IUCLC'):
337            iflag &= ~TERMIOS.IUCLC
338        if hasattr(TERMIOS, 'PARMRK'):
339            iflag &= ~TERMIOS.PARMRK
340
341        # setup baud rate
342        try:
343            ispeed = ospeed = getattr(TERMIOS, 'B%s' % (self._baudrate))
344        except AttributeError:
345            try:
346                ispeed = ospeed = baudrate_constants[self._baudrate]
347            except KeyError:
348                #~ raise ValueError('Invalid baud rate: %r' % self._baudrate)
349                # may need custom baud rate, it isn't in our list.
350                ispeed = ospeed = getattr(TERMIOS, 'B38400')
351                try:
352                    custom_baud = int(self._baudrate) # store for later
353                except ValueError:
354                    raise ValueError('Invalid baud rate: %r' % self._baudrate)
355                else:
356                    if custom_baud < 0:
357                        raise ValueError('Invalid baud rate: %r' % self._baudrate)
358
359        # setup char len
360        cflag &= ~TERMIOS.CSIZE
361        if self._bytesize == 8:
362            cflag |= TERMIOS.CS8
363        elif self._bytesize == 7:
364            cflag |= TERMIOS.CS7
365        elif self._bytesize == 6:
366            cflag |= TERMIOS.CS6
367        elif self._bytesize == 5:
368            cflag |= TERMIOS.CS5
369        else:
370            raise ValueError('Invalid char len: %r' % self._bytesize)
371        # setup stopbits
372        if self._stopbits == STOPBITS_ONE:
373            cflag &= ~(TERMIOS.CSTOPB)
374        elif self._stopbits == STOPBITS_ONE_POINT_FIVE:
375            cflag |=  (TERMIOS.CSTOPB)  # XXX same as TWO.. there is no POSIX support for 1.5
376        elif self._stopbits == STOPBITS_TWO:
377            cflag |=  (TERMIOS.CSTOPB)
378        else:
379            raise ValueError('Invalid stop bit specification: %r' % self._stopbits)
380        # setup parity
381        iflag &= ~(TERMIOS.INPCK|TERMIOS.ISTRIP)
382        if self._parity == PARITY_NONE:
383            cflag &= ~(TERMIOS.PARENB|TERMIOS.PARODD)
384        elif self._parity == PARITY_EVEN:
385            cflag &= ~(TERMIOS.PARODD)
386            cflag |=  (TERMIOS.PARENB)
387        elif self._parity == PARITY_ODD:
388            cflag |=  (TERMIOS.PARENB|TERMIOS.PARODD)
389        else:
390            raise ValueError('Invalid parity: %r' % self._parity)
391        # setup flow control
392        # xonxoff
393        if hasattr(TERMIOS, 'IXANY'):
394            if self._xonxoff:
395                iflag |=  (TERMIOS.IXON|TERMIOS.IXOFF) #|TERMIOS.IXANY)
396            else:
397                iflag &= ~(TERMIOS.IXON|TERMIOS.IXOFF|TERMIOS.IXANY)
398        else:
399            if self._xonxoff:
400                iflag |=  (TERMIOS.IXON|TERMIOS.IXOFF)
401            else:
402                iflag &= ~(TERMIOS.IXON|TERMIOS.IXOFF)
403        # rtscts
404        if hasattr(TERMIOS, 'CRTSCTS'):
405            if self._rtscts:
406                cflag |=  (TERMIOS.CRTSCTS)
407            else:
408                cflag &= ~(TERMIOS.CRTSCTS)
409        elif hasattr(TERMIOS, 'CNEW_RTSCTS'):   # try it with alternate constant name
410            if self._rtscts:
411                cflag |=  (TERMIOS.CNEW_RTSCTS)
412            else:
413                cflag &= ~(TERMIOS.CNEW_RTSCTS)
414        # XXX should there be a warning if setting up rtscts (and xonxoff etc) fails??
415
416        # buffer
417        # vmin "minimal number of characters to be read. = for non blocking"
418        if vmin < 0 or vmin > 255:
419            raise ValueError('Invalid vmin: %r ' % vmin)
420        cc[TERMIOS.VMIN] = vmin
421        # vtime
422        if vtime < 0 or vtime > 255:
423            raise ValueError('Invalid vtime: %r' % vtime)
424        cc[TERMIOS.VTIME] = vtime
425        # activate settings
426        if [iflag, oflag, cflag, lflag, ispeed, ospeed, cc] != orig_attr:
427            termios.tcsetattr(self.fd, TERMIOS.TCSANOW, [iflag, oflag, cflag, lflag, ispeed, ospeed, cc])
428
429        # apply custom baud rate, if any
430        if custom_baud is not None:
431            set_special_baudrate(self, custom_baud)
432
433    def close(self):
434        """Close port"""
435        if self._isOpen:
436            if self.fd is not None:
437                os.close(self.fd)
438                self.fd = None
439            self._isOpen = False
440
441    def makeDeviceName(self, port):
442        return device(port)
443
444    #  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -
445
446    def inWaiting(self):
447        """Return the number of characters currently in the input buffer."""
448        #~ s = fcntl.ioctl(self.fd, TERMIOS.FIONREAD, TIOCM_zero_str)
449        s = fcntl.ioctl(self.fd, TIOCINQ, TIOCM_zero_str)
450        return struct.unpack('I',s)[0]
451
452    # select based implementation, proved to work on many systems
453    def read(self, size=1):
454        """Read size bytes from the serial port. If a timeout is set it may
455           return less characters as requested. With no timeout it will block
456           until the requested number of bytes is read."""
457        if not self._isOpen: raise portNotOpenError
458        read = bytearray()
459        while len(read) < size:
460            try:
461                ready,_,_ = select.select([self.fd],[],[], self._timeout)
462                # If select was used with a timeout, and the timeout occurs, it
463                # returns with empty lists -> thus abort read operation.
464                # For timeout == 0 (non-blocking operation) also abort when there
465                # is nothing to read.
466                if not ready:
467                    break   # timeout
468                buf = os.read(self.fd, size-len(read))
469                # read should always return some data as select reported it was
470                # ready to read when we get to this point.
471                if not buf:
472                    # Disconnected devices, at least on Linux, show the
473                    # behavior that they are always ready to read immediately
474                    # but reading returns nothing.
475                    raise SerialException('device reports readiness to read but returned no data (device disconnected or multiple access on port?)')
476                read.extend(buf)
477            except select.error, e:
478                # ignore EAGAIN errors. all other errors are shown
479                # see also http://www.python.org/dev/peps/pep-3151/#select
480                if e[0] != errno.EAGAIN:
481                    raise SerialException('read failed: %s' % (e,))
482            except OSError, e:
483                # ignore EAGAIN errors. all other errors are shown
484                if e.errno != errno.EAGAIN:
485                    raise SerialException('read failed: %s' % (e,))
486        return bytes(read)
487
488    def write(self, data):
489        """Output the given string over the serial port."""
490        if not self._isOpen: raise portNotOpenError
491        d = to_bytes(data)
492        tx_len = len(d)
493        if self._writeTimeout is not None and self._writeTimeout > 0:
494            timeout = time.time() + self._writeTimeout
495        else:
496            timeout = None
497        while tx_len > 0:
498            try:
499                n = os.write(self.fd, d)
500                if timeout:
501                    # when timeout is set, use select to wait for being ready
502                    # with the time left as timeout
503                    timeleft = timeout - time.time()
504                    if timeleft < 0:
505                        raise writeTimeoutError
506                    _, ready, _ = select.select([], [self.fd], [], timeleft)
507                    if not ready:
508                        raise writeTimeoutError
509                else:
510                    # wait for write operation
511                    _, ready, _ = select.select([], [self.fd], [], None)
512                    if not ready:
513                        raise SerialException('write failed (select)')
514                d = d[n:]
515                tx_len -= n
516            except OSError, v:
517                if v.errno != errno.EAGAIN:
518                    raise SerialException('write failed: %s' % (v,))
519        return len(data)
520
521    def flush(self):
522        """Flush of file like objects. In this case, wait until all data
523           is written."""
524        self.drainOutput()
525
526    def flushInput(self):
527        """Clear input buffer, discarding all that is in the buffer."""
528        if not self._isOpen: raise portNotOpenError
529        termios.tcflush(self.fd, TERMIOS.TCIFLUSH)
530
531    def flushOutput(self):
532        """Clear output buffer, aborting the current output and
533        discarding all that is in the buffer."""
534        if not self._isOpen: raise portNotOpenError
535        termios.tcflush(self.fd, TERMIOS.TCOFLUSH)
536
537    def sendBreak(self, duration=0.25):
538        """Send break condition. Timed, returns to idle state after given duration."""
539        if not self._isOpen: raise portNotOpenError
540        termios.tcsendbreak(self.fd, int(duration/0.25))
541
542    def setBreak(self, level=1):
543        """Set break: Controls TXD. When active, no transmitting is possible."""
544        if self.fd is None: raise portNotOpenError
545        if level:
546            fcntl.ioctl(self.fd, TIOCSBRK)
547        else:
548            fcntl.ioctl(self.fd, TIOCCBRK)
549
550    def setRTS(self, level=1):
551        """Set terminal status line: Request To Send"""
552        if not self._isOpen: raise portNotOpenError
553        if level:
554            fcntl.ioctl(self.fd, TIOCMBIS, TIOCM_RTS_str)
555        else:
556            fcntl.ioctl(self.fd, TIOCMBIC, TIOCM_RTS_str)
557
558    def setDTR(self, level=1):
559        """Set terminal status line: Data Terminal Ready"""
560        if not self._isOpen: raise portNotOpenError
561        if level:
562            fcntl.ioctl(self.fd, TIOCMBIS, TIOCM_DTR_str)
563        else:
564            fcntl.ioctl(self.fd, TIOCMBIC, TIOCM_DTR_str)
565
566    def getCTS(self):
567        """Read terminal status line: Clear To Send"""
568        if not self._isOpen: raise portNotOpenError
569        s = fcntl.ioctl(self.fd, TIOCMGET, TIOCM_zero_str)
570        return struct.unpack('I',s)[0] & TIOCM_CTS != 0
571
572    def getDSR(self):
573        """Read terminal status line: Data Set Ready"""
574        if not self._isOpen: raise portNotOpenError
575        s = fcntl.ioctl(self.fd, TIOCMGET, TIOCM_zero_str)
576        return struct.unpack('I',s)[0] & TIOCM_DSR != 0
577
578    def getRI(self):
579        """Read terminal status line: Ring Indicator"""
580        if not self._isOpen: raise portNotOpenError
581        s = fcntl.ioctl(self.fd, TIOCMGET, TIOCM_zero_str)
582        return struct.unpack('I',s)[0] & TIOCM_RI != 0
583
584    def getCD(self):
585        """Read terminal status line: Carrier Detect"""
586        if not self._isOpen: raise portNotOpenError
587        s = fcntl.ioctl(self.fd, TIOCMGET, TIOCM_zero_str)
588        return struct.unpack('I',s)[0] & TIOCM_CD != 0
589
590    # - - platform specific - - - -
591
592    def outWaiting(self):
593        """Return the number of characters currently in the output buffer."""
594        #~ s = fcntl.ioctl(self.fd, TERMIOS.FIONREAD, TIOCM_zero_str)
595        s = fcntl.ioctl(self.fd, TIOCOUTQ, TIOCM_zero_str)
596        return struct.unpack('I',s)[0]
597
598    def drainOutput(self):
599        """internal - not portable!"""
600        if not self._isOpen: raise portNotOpenError
601        termios.tcdrain(self.fd)
602
603    def nonblocking(self):
604        """internal - not portable!"""
605        if not self._isOpen: raise portNotOpenError
606        fcntl.fcntl(self.fd, FCNTL.F_SETFL, os.O_NONBLOCK)
607
608    def fileno(self):
609        """\
610        For easier use of the serial port instance with select.
611        WARNING: this function is not portable to different platforms!
612        """
613        if not self._isOpen: raise portNotOpenError
614        return self.fd
615
616    def setXON(self, level=True):
617        """\
618        Manually control flow - when software flow control is enabled.
619        This will send XON (true) and XOFF (false) to the other device.
620        WARNING: this function is not portable to different platforms!
621        """
622        if not self.hComPort: raise portNotOpenError
623        if enable:
624            termios.tcflow(self.fd, TERMIOS.TCION)
625        else:
626            termios.tcflow(self.fd, TERMIOS.TCIOFF)
627
628    def flowControlOut(self, enable):
629        """\
630        Manually control flow of outgoing data - when hardware or software flow
631        control is enabled.
632        WARNING: this function is not portable to different platforms!
633        """
634        if not self._isOpen: raise portNotOpenError
635        if enable:
636            termios.tcflow(self.fd, TERMIOS.TCOON)
637        else:
638            termios.tcflow(self.fd, TERMIOS.TCOOFF)
639
640
641# assemble Serial class with the platform specifc implementation and the base
642# for file-like behavior. for Python 2.6 and newer, that provide the new I/O
643# library, derrive from io.RawIOBase
644try:
645    import io
646except ImportError:
647    # classic version with our own file-like emulation
648    class Serial(PosixSerial, FileLike):
649        pass
650else:
651    # io library present
652    class Serial(PosixSerial, io.RawIOBase):
653        pass
654
655class PosixPollSerial(Serial):
656    """poll based read implementation. not all systems support poll properly.
657    however this one has better handling of errors, such as a device
658    disconnecting while it's in use (e.g. USB-serial unplugged)"""
659
660    def read(self, size=1):
661        """Read size bytes from the serial port. If a timeout is set it may
662           return less characters as requested. With no timeout it will block
663           until the requested number of bytes is read."""
664        if self.fd is None: raise portNotOpenError
665        read = bytearray()
666        poll = select.poll()
667        poll.register(self.fd, select.POLLIN|select.POLLERR|select.POLLHUP|select.POLLNVAL)
668        if size > 0:
669            while len(read) < size:
670                # print "\tread(): size",size, "have", len(read)    #debug
671                # wait until device becomes ready to read (or something fails)
672                for fd, event in poll.poll(self._timeout*1000):
673                    if event & (select.POLLERR|select.POLLHUP|select.POLLNVAL):
674                        raise SerialException('device reports error (poll)')
675                    #  we don't care if it is select.POLLIN or timeout, that's
676                    #  handled below
677                buf = os.read(self.fd, size - len(read))
678                read.extend(buf)
679                if ((self._timeout is not None and self._timeout >= 0) or
680                    (self._interCharTimeout is not None and self._interCharTimeout > 0)) and not buf:
681                    break   # early abort on timeout
682        return bytes(read)
683
684
685if __name__ == '__main__':
686    s = Serial(0,
687                 baudrate=19200,        # baud rate
688                 bytesize=EIGHTBITS,    # number of data bits
689                 parity=PARITY_EVEN,    # enable parity checking
690                 stopbits=STOPBITS_ONE, # number of stop bits
691                 timeout=3,             # set a timeout value, None for waiting forever
692                 xonxoff=0,             # enable software flow control
693                 rtscts=0,              # enable RTS/CTS flow control
694               )
695    s.setRTS(1)
696    s.setDTR(1)
697    s.flushInput()
698    s.flushOutput()
699    s.write('hello')
700    sys.stdout.write('%r\n' % s.read(5))
701    sys.stdout.write('%s\n' % s.inWaiting())
702    del s
703
704