]> git.tdb.fi Git - libs/core.git/blob - source/serial.cpp
1aedd410eefa4e2e0e4d40638fefdc1ad4e87b81
[libs/core.git] / source / serial.cpp
1 /* $Id$
2
3 This file is part of libmspio
4 Copyright © 2010 Mikko Rasa, Mikkosoft Productions
5 Distributed under the LGPL
6 */
7
8 #ifdef WIN32
9 #include <windows.h>
10 #else
11 #include <termios.h>
12 #include <fcntl.h>
13 #include <errno.h>
14 #endif
15 #include <msp/strings/formatter.h>
16 #include "except.h"
17 #include "serial.h"
18
19 using namespace std;
20
21 namespace {
22
23 using namespace Msp;
24 using namespace Msp::IO;
25
26 #ifdef WIN32
27 typedef DCB DeviceState;
28 #else
29 typedef termios DeviceState;
30 #endif
31
32 void get_state(Handle handle, DeviceState &state)
33 {
34 #ifdef WIN32
35         GetCommState(handle, &state);
36 #else
37         tcgetattr(handle, &state);
38 #endif
39 }
40
41 void set_state(Handle handle, DeviceState &state)
42 {
43 #ifdef WIN32
44         if(SetCommState(handle, &state)==0)
45                 throw SystemError("Cannot set serial port parameters", GetLastError());
46 #else
47         if(tcsetattr(handle, TCSADRAIN, &state)==-1)
48                 throw SystemError("Cannot set serial port parameters", errno);
49 #endif
50 }
51
52 void set_baud_rate(DeviceState &state, unsigned baud)
53 {
54 #ifdef WIN32
55         state.BaudRate = baud;
56 #else
57         speed_t speed;
58         switch(baud)
59         {
60         case 0:      speed = B0; break;
61         case 50:     speed = B50; break;
62         case 75:     speed = B75; break;
63         case 110:    speed = B110; break;
64         case 134:    speed = B134; break;
65         case 150:    speed = B150; break;
66         case 200:    speed = B200; break;
67         case 300:    speed = B300; break;
68         case 600:    speed = B600; break;
69         case 1200:   speed = B1200; break;
70         case 1800:   speed = B1800; break;
71         case 2400:   speed = B2400; break;
72         case 4800:   speed = B4800; break;
73         case 9600:   speed = B9600; break;
74         case 19200:  speed = B19200; break;
75         case 38400:  speed = B38400; break;
76         case 57600:  speed = B57600; break;
77         case 115200: speed = B115200; break;
78         case 230400: speed = B230400; break;
79         default: throw InvalidParameterValue("Invalid baud rate");
80         }
81
82         cfsetospeed(&state, speed);
83         cfsetispeed(&state, speed);
84 #endif
85 }
86
87 void set_data_bits(DeviceState &state, unsigned bits)
88 {
89 #ifdef WIN32
90         state.ByteSize = bits;
91 #else
92         tcflag_t flag;
93         switch(bits)
94         {
95         case 5: flag = CS5; break;
96         case 6: flag = CS6; break;
97         case 7: flag = CS7; break;
98         case 8: flag = CS8; break;
99         default: throw InvalidParameterValue("Invalid data bit count");
100         }
101
102         state.c_cflag = (state.c_cflag&~CSIZE)|flag;
103 #endif
104 }
105
106 void set_parity(DeviceState &state, Serial::Parity par)
107 {
108 #ifdef WIN32
109         switch(par)
110         {
111         case Serial::NONE: state.Parity = NOPARITY; break;
112         case Serial::EVEN: state.Parity = EVENPARITY; break;
113         case Serial::ODD:  state.Parity = ODDPARITY; break;
114         default: throw InvalidParameterValue("Invalid parity");
115         }
116 #else
117         tcflag_t flag;
118         switch(par)
119         {
120         case Serial::NONE: flag = 0; break;
121         case Serial::EVEN: flag = PARENB; break;
122         case Serial::ODD:  flag = PARENB|PARODD; break;
123         default: throw InvalidParameterValue("Invalid parity");
124         }
125
126         state.c_cflag = (state.c_cflag&~(PARENB|PARODD))|flag;
127 #endif
128 }
129
130 void set_stop_bits(DeviceState &state, unsigned bits)
131 {
132 #ifdef WIN32
133         switch(bits)
134         {
135         case 1: state.StopBits = ONESTOPBIT; break;
136         case 2: state.StopBits = TWOSTOPBITS; break;
137         default: throw InvalidParameterValue("Invalid stop bit count");
138         }
139 #else
140         tcflag_t flag;
141         switch(bits)
142         {
143         case 1: flag = 0; break;
144         case 2: flag = CSTOPB; break;
145         default: throw InvalidParameterValue("Invalid stop bit count");
146         }
147
148         state.c_cflag = (state.c_cflag&~CSTOPB)|flag;
149 #endif
150 }
151
152 }
153
154
155 namespace Msp {
156 namespace IO {
157
158 Serial::Serial(const string &descr)
159 {
160         string::size_type comma = descr.find(',');
161         string port = descr.substr(0, comma);
162
163 #ifdef WIN32
164         port = "\\\\.\\"+port;
165
166         handle = CreateFile(port.c_str(), GENERIC_READ|GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
167         if(handle==INVALID_HANDLE_VALUE)
168                 throw SystemError(format("Can't open serial port '%s'", port), GetLastError());
169         mode = M_READ|M_WRITE;
170
171         COMMTIMEOUTS timeouts;
172         timeouts.ReadIntervalTimeout = MAXDWORD;
173         timeouts.ReadTotalTimeoutMultiplier = MAXDWORD;
174         timeouts.ReadTotalTimeoutConstant = MAXDWORD-1;
175         timeouts.WriteTotalTimeoutMultiplier = 0;
176         timeouts.WriteTotalTimeoutConstant = 0;
177         SetCommTimeouts(handle, &timeouts);
178 #else
179         if(port.compare(0, 5, "/dev/"))
180                 port = "/dev/"+port;
181
182         handle = open(port.c_str(), O_RDWR);
183         if(handle==-1)
184                 throw SystemError(format("Can't open serial port '%s'", port), errno);
185         mode = M_READ|M_WRITE;
186
187         termios t;
188         tcgetattr(handle, &t);
189         t.c_lflag &= ~(ECHO|ICANON);
190         t.c_oflag &= ~OPOST;
191         tcsetattr(handle, TCSADRAIN, &t);
192 #endif
193
194         if(comma!=string::npos)
195         {
196                 try
197                 {
198                         set_parameters(descr.substr(comma+1));
199                 }
200                 catch(...)
201                 {
202                         close();
203                         throw;
204                 }
205         }
206
207         set_events(P_INPUT);
208 }
209
210 Serial::~Serial()
211 {
212         close();
213 }
214
215 void Serial::set_block(bool b)
216 {
217         if(b)
218                 mode = mode|M_NONBLOCK;
219         else
220                 mode = mode&~M_NONBLOCK;
221
222 #ifndef WIN32
223         int flags = fcntl(handle, F_GETFD);
224         fcntl(handle, F_SETFL, (flags&O_NONBLOCK)|(b?0:O_NONBLOCK));
225 #endif
226 }
227
228 void Serial::set_baud_rate(unsigned rate)
229 {
230         DeviceState state;
231         get_state(handle, state);
232         ::set_baud_rate(state, rate);
233         set_state(handle, state);
234 }
235
236 void Serial::set_data_bits(unsigned bits)
237 {
238         DeviceState state;
239         get_state(handle, state);
240         ::set_data_bits(state, bits);
241         set_state(handle, state);
242 }
243
244 void Serial::set_parity(Parity par)
245 {
246         DeviceState state;
247         get_state(handle, state);
248         ::set_parity(state, par);
249         set_state(handle, state);
250 }
251
252 void Serial::set_stop_bits(unsigned bits)
253 {
254         DeviceState state;
255         get_state(handle, state);
256         ::set_stop_bits(state, bits);
257         set_state(handle, state);
258 }
259
260 void Serial::set_parameters(const string &params)
261 {
262         unsigned i;
263         for(i=0; i<params.size() && isdigit(params[i]); ++i) ;
264         if(i+4!=params.size() || params[i]!=',')
265                 throw InvalidParameterValue("Invalid parameter string");
266         if(params[i+1]<'5' || params[i+1]>'8')
267                 throw InvalidParameterValue("Invalid data bit count");
268         if(params[i+2]!='N' && params[i+2]!='E' && params[i+2]!='O')
269                 throw InvalidParameterValue("Invalid parity");
270         if(params[i+3]!='1' && params[i+3]!='2')
271                 throw InvalidParameterValue("Invalid stop bit count");
272
273         DeviceState state;
274         get_state(handle, state);
275         ::set_baud_rate(state, lexical_cast<unsigned>(params.substr(0, i)));
276         ::set_data_bits(state, params[i+1]-'0');
277         ::set_parity(state, (params[i+2]=='E' ? EVEN : params[i+2]=='O' ? ODD : NONE));
278         ::set_stop_bits(state, params[i+3]-'0');
279         set_state(handle, state);
280 }
281
282 Handle Serial::get_event_handle()
283 {
284 #ifdef WIN32
285         throw Exception("Serial port events not supported on win32 yet");
286 #else
287         return handle;
288 #endif
289 }
290
291 void Serial::close()
292 {
293 #ifdef WIN32
294         CloseHandle(handle);
295 #else
296         ::close(handle);
297 #endif
298 }
299
300 unsigned Serial::do_write(const char *buf, unsigned size)
301 {
302         if(size==0)
303                 return 0;
304
305 #ifdef WIN32
306         DWORD ret;
307         if(WriteFile(handle, buf, size, &ret, 0)==0)
308                 throw SystemError("Writing to serial port failed", GetLastError());
309 #else
310         int ret = ::write(handle, buf, size);
311         if(ret==-1)
312         {
313                 if(errno==EAGAIN)
314                         return 0;
315                 else
316                         throw SystemError("Writing to serial port failed", errno);
317         }
318 #endif
319
320         return ret;
321 }
322
323 unsigned Serial::do_read(char *buf, unsigned size)
324 {
325         if(size==0)
326                 return 0;
327
328 #ifdef WIN32
329         DWORD ret;
330         if(ReadFile(handle, buf, size, &ret, 0)==0)
331                 throw SystemError("Reading from serial port failed", GetLastError());
332 #else
333         int ret = ::read(handle, buf, size);
334         if(ret==-1)
335         {
336                 if(errno==EAGAIN)
337                         return 0;
338                 else
339                         throw SystemError("Reading from serial port failed", errno);
340         }
341 #endif
342
343         return ret;
344 }
345
346 } // namespace IO
347 } // namespace Msp