Download Reference Manual
The Developer's Library for D
About Wiki Forums Source Search Contact

Ticket #1135: SerialPort.d

File SerialPort.d, 8.3 kB (added by elite01, 4 months ago)
Line 
1 /*******************************************************************************
2
3         copyright:      Copyright (c) 2008 Robin Kreis. All rights reserved
4
5         license:        BSD style: $(LICENSE)
6
7         author:         Robin Kreis
8
9 *******************************************************************************/
10
11 module tango.io.SerialPort;
12
13 import tango.core.Array : sort;
14 import
15         tango.core.Exception,
16         tango.io.DeviceConduit,
17         tango.stdc.stringz,
18         tango.sys.Common,
19         tango.text.Regex;
20
21 version(Windows) {
22     import Integer = tango.text.convert.Integer;
23     import tango.stdc.stringz;
24 } else version(Posix) {
25     import
26             tango.io.FilePath,
27             tango.stdc.posix.termios;
28 }
29
30 /*******************************************************************************
31
32         Enables applications to use a serial port (aka COM-port, ttyS).
33         Usage is similar to that of FileConduit:
34         ---
35         auto serPort = new SerialPort("ttyS0");
36         serPort.speed = 38400;
37         serPort.write("Hello world!");
38         serPort.close();
39         ----
40
41 *******************************************************************************/
42 class SerialPort : DeviceConduit
43 {
44     private static char[][] _ports;
45    
46     /*******************************************************************************
47     
48             Tries to enumerate all serial ports. While this usually works on
49             Windows, it's more problematic on other OS. Posix provides no way to
50             list serial ports, and the only option is searching through "/dev".
51             Because there's no naming standard for the device files, this method
52             must be ported for each OS. This method is also unreliable because
53             the user could have created invalid device files, or deleted them.
54             
55             Returns:
56             A string array of all the serial ports that could be found, in
57             alphabetical order. Every string is formatted as a valid argument to
58             the constructor, but the port may not be accessible.
59     
60     *******************************************************************************/
61     public static char[][] ports()
62     {
63         if(_ports !is null) {
64             return _ports;
65         }
66         version(Windows) {
67             // try opening COM1...COM255
68             for(int i = 1; i <= 255; ++i) {
69                 char[] p = `\\.\COM` ~ Integer.toString(i);
70                 HANDLE port = CreateFileA(toStringz(p), GENERIC_READ | GENERIC_WRITE, 0, null, OPEN_EXISTING, 0, null);
71                 if(port != INVALID_HANDLE_VALUE) {
72                     _ports ~= p[`\\.\`.length..$]; // cut the leading \\.\
73                     CloseHandle(port);
74                 }
75             }
76         } else version(Posix) {
77             version(linux) {
78                 auto serPattern = new RegExpT!(char)("^tty(:?USB|S)[0-9]+$");
79             } else version(freebsd) { // untested
80                 auto serPattern = new RegExpT!(char)("^cua[ad][0-9]+$");
81             } else version(openbsd) { // untested
82                 auto serPattern = new RegExpT!(char)("^tty[0-9]{2}$");
83             } else version(solaris) { // untested
84                 auto serPattern = new RegExpT!(char)("^tty[a-z]$");
85             } else {
86                 return null;
87             }
88             auto dev = FilePath("/dev");
89             FilePath[] serPorts = dev.toList((FilePath path, bool isFolder) {
90                 return !isFolder && serPattern.test(path.name);
91             });
92             _ports.length = serPorts.length;
93             foreach(i, path; serPorts) {
94                 _ports[i] = path.name;
95             }
96         }
97         sort(_ports);
98         return _ports;
99     }
100    
101     private char[] str;
102    
103     /*******************************************************************************
104     
105             Create a new SerialPort instance. The port will be opened and set to
106             raw mode with 9600-8N1.
107             
108             Params:
109             port = A string identifying the port. On Posix, this must be a device
110                    file like /dev/ttyS0. If the input doesn't begin with "/",
111                    "/dev/" is automatically prepended, so "ttyS0" is sufficent.
112                    On Windows, this must be a device name like COM1.
113             
114     *******************************************************************************/
115     public this(char[] port)
116     {
117         create(port);
118     }
119    
120     public ~this()
121     {
122         detach();
123     }
124
125     /*******************************************************************************
126
127             Sets the baud rate of this port. Usually, the baud rate can only be set
128             to fixed values (common values are 1200 * 2^n).
129
130     *******************************************************************************/
131     public SerialPort speed(uint speed)
132     {
133         version(Posix) {
134             speed_t *baud = speed in baudRates;
135             if(baud is null) {
136                 throw new IOException("Invalid baud rate.");
137             }
138            
139             termios options;
140             tcgetattr(handle, &options);
141             cfsetospeed(&options, *baud);
142             tcsetattr(handle, TCSANOW, &options);
143         }
144         version(Win32) {
145             DCB config;
146             GetCommState(handle, &config);
147             config.BaudRate = speed;
148             if(!SetCommState(handle, &config)) error();
149         }
150         return this;
151     }
152    
153     version(Win32) {
154         private void create(char[] port)
155         {
156             str = port;
157             handle = CreateFileA((`\\.\` ~ port).toStringz(), GENERIC_READ | GENERIC_WRITE, 0, null, OPEN_EXISTING, 0, null);
158             if(handle == INVALID_HANDLE_VALUE) {
159                 error();
160             }
161             DCB config;
162             GetCommState(handle, &config);
163             config.BaudRate = 9600;
164             config.ByteSize = 8;
165             config.Parity = NOPARITY;
166             config.StopBits = ONESTOPBIT;
167             config.flag0 |= bm_DCB_fBinary | bm_DCB_fParity;
168             if(!SetCommState(handle, &config)) error();
169         }
170     }
171    
172     version(Posix) {
173         private static speed_t[uint] baudRates;
174        
175         static this()
176         {
177             baudRates[50] = B50;
178             baudRates[75] = B75;
179             baudRates[110] = B110;
180             baudRates[134] = B134;
181             baudRates[150] = B150;
182             baudRates[200] = B200;
183             baudRates[300] = B300;
184             baudRates[600] = B600;
185             baudRates[1200] = B1200;
186             baudRates[1800] = B1800;
187             baudRates[2400] = B2400;
188             baudRates[9600] = B9600;
189             baudRates[4800] = B4800;
190             baudRates[19200] = B19200;
191             baudRates[38400] = B38400;
192         }
193        
194         private void create(char[] file)
195         {
196             if(file.length == 0) throw new IOException("Empty port name");
197             if(file[0] != '/') file = "/dev/" ~ file;
198            
199             if(file.length > 5 && file[0..5] == "/dev/")
200                 str = file[5..$];
201             else
202                 str = "SerialPort@" ~ file;
203            
204             handle = posix.open(file.toStringz(), O_RDWR | O_NOCTTY | O_NONBLOCK);
205             if(handle == -1) {
206                 error();
207             }
208             if(posix.fcntl(handle, F_SETFL, 0) == -1) { // disable O_NONBLOCK
209                 error();
210             }
211            
212             termios options;
213             if(tcgetattr(handle, &options) == -1) {
214                 error();
215             }
216             cfsetispeed(&options, B0); // same as output baud rate
217             cfsetospeed(&options, B9600);
218             makeRaw(&options); // disable echo and special characters
219             tcsetattr(handle, TCSANOW, &options);
220         }
221        
222         private void makeRaw(termios *options)
223         {
224             options.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP
225                     | INLCR | IGNCR | ICRNL | IXON);
226             options.c_oflag &= ~OPOST;
227             options.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
228             options.c_cflag &= ~(CSIZE | PARENB);
229             options.c_cflag |= CS8;
230         }
231     }
232    
233     /*******************************************************************************
234     
235             Returns a string describing this serial port.
236             For example: "ttyS0", "COM1", "cuad0"
237     
238     *******************************************************************************/
239     override public char[] toString()
240     {
241         return str;
242     }
243 }