]> git.tdb.fi Git - libs/core.git/blob - source/serial.cpp
Add a class for serial port communications
[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 #include <termios.h>
9 #include <fcntl.h>
10 #include <errno.h>
11 #include <msp/strings/formatter.h>
12 #include "except.h"
13 #include "serial.h"
14
15 using namespace std;
16
17 namespace {
18
19 using namespace Msp;
20 using namespace Msp::IO;
21
22 #ifndef WIN32
23 void set_baud_rate(termios &t, unsigned baud)
24 {
25         speed_t speed;
26         switch(baud)
27         {
28         case 0:      speed = B0; break;
29         case 50:     speed = B50; break;
30         case 75:     speed = B75; break;
31         case 110:    speed = B110; break;
32         case 134:    speed = B134; break;
33         case 150:    speed = B150; break;
34         case 200:    speed = B200; break;
35         case 300:    speed = B300; break;
36         case 600:    speed = B600; break;
37         case 1200:   speed = B1200; break;
38         case 1800:   speed = B1800; break;
39         case 2400:   speed = B2400; break;
40         case 4800:   speed = B4800; break;
41         case 9600:   speed = B9600; break;
42         case 19200:  speed = B19200; break;
43         case 38400:  speed = B38400; break;
44         case 57600:  speed = B57600; break;
45         case 115200: speed = B115200; break;
46         case 230400: speed = B230400; break;
47         default: throw InvalidParameterValue("Invalid baud rate");
48         }
49
50         cfsetospeed(&t, speed);
51         cfsetispeed(&t, speed);
52 }
53
54 void set_data_bits(termios &t, unsigned bits)
55 {
56         tcflag_t flag;
57         switch(bits)
58         {
59         case 5: flag = CS5; break;
60         case 6: flag = CS6; break;
61         case 7: flag = CS7; break;
62         case 8: flag = CS8; break;
63         default: throw InvalidParameterValue("Invalid data bit count");
64         }
65
66         t.c_cflag = (t.c_cflag&~CSIZE)|flag;
67 }
68
69 void set_parity(termios &t, Serial::Parity par)
70 {
71         tcflag_t flag;
72         switch(par)
73         {
74         case Serial::NONE: flag = 0; break;
75         case Serial::EVEN: flag = PARENB; break;
76         case Serial::ODD:  flag = PARENB|PARODD; break;
77         default: throw InvalidParameterValue("Invalid parity");
78         }
79
80         t.c_cflag = (t.c_cflag&~(PARENB|PARODD))|flag;
81 }
82
83 void set_stop_bits(termios &t, unsigned bits)
84 {
85         tcflag_t flag;
86         switch(bits)
87         {
88         case 1: flag = 0; break;
89         case 2: flag = CSTOPB; break;
90         default: throw InvalidParameterValue("Invalid stop bit count");
91         }
92
93         t.c_cflag = (t.c_cflag&~CSTOPB)|flag;
94 }
95 #endif
96
97 }
98
99
100 namespace Msp {
101 namespace IO {
102
103 Serial::Serial(const string &descr)
104 {
105         string::size_type comma = descr.find(',');
106         string port = descr.substr(0, comma);
107
108 #ifndef WIN32
109         if(port.compare(0, 5, "/dev/"))
110                 port = "/dev/"+port;
111
112         handle = open(port.c_str(), O_RDWR);
113         if(handle==-1)
114                 throw SystemError(format("Can't open serial port '%s'", port), errno);
115         mode = M_READ|M_WRITE;
116
117         termios t;
118         tcgetattr(handle, &t);
119         t.c_lflag &= ~(ECHO|ICANON);
120         tcsetattr(handle, TCSADRAIN, &t);
121 #else
122         throw Exception("Serial ports not supported in win32 yet");
123 #endif
124
125         if(comma!=string::npos)
126         {
127                 try
128                 {
129                         set_parameters(descr.substr(comma+1));
130                 }
131                 catch(...)
132                 {
133                         close();
134                         throw;
135                 }
136         }
137
138         set_events(P_INPUT);
139 }
140
141 Serial::~Serial()
142 {
143         close();
144 }
145
146 void Serial::set_block(bool b)
147 {
148         if(b)
149                 mode = mode|M_NONBLOCK;
150         else
151                 mode = mode&~M_NONBLOCK;
152
153 #ifndef WIN32
154         int flags = fcntl(handle, F_GETFD);
155         fcntl(handle, F_SETFL, (flags&O_NONBLOCK)|(b?0:O_NONBLOCK));
156 #endif
157 }
158
159 void Serial::set_baud_rate(unsigned rate)
160 {
161 #ifndef WIN32
162         termios t;
163         tcgetattr(handle, &t);
164         ::set_baud_rate(t, rate);
165         tcsetattr(handle, TCSADRAIN, &t);
166 #endif
167 }
168
169 void Serial::set_data_bits(unsigned bits)
170 {
171 #ifndef WIN32
172         termios t;
173         tcgetattr(handle, &t);
174         ::set_data_bits(t, bits);
175         tcsetattr(handle, TCSADRAIN, &t);
176 #endif
177 }
178
179 void Serial::set_parity(Parity par)
180 {
181 #ifndef WIN32
182         termios t;
183         tcgetattr(handle, &t);
184         ::set_parity(t, par);
185         tcsetattr(handle, TCSADRAIN, &t);
186 #endif
187 }
188
189 void Serial::set_stop_bits(unsigned bits)
190 {
191 #ifndef WIN32
192         termios t;
193         tcgetattr(handle, &t);
194         ::set_stop_bits(t, bits);
195         tcsetattr(handle, TCSADRAIN, &t);
196 #endif
197 }
198
199 void Serial::set_parameters(const string &params)
200 {
201         unsigned i;
202         for(i=0; i<params.size() && isdigit(params[i]); ++i) ;
203         if(i+4!=params.size() || params[i]!=',')
204                 throw InvalidParameterValue("Invalid parameter string");
205         if(params[i+1]<'5' || params[i+1]>'8')
206                 throw InvalidParameterValue("Invalid data bit count");
207         if(params[i+2]!='N' && params[i+2]!='E' && params[i+2]!='O')
208                 throw InvalidParameterValue("Invalid parity");
209         if(params[i+3]!='1' && params[i+3]!='2')
210                 throw InvalidParameterValue("Invalid stop bit count");
211
212 #ifndef WIN32
213         termios t;
214         tcgetattr(handle, &t);
215         ::set_baud_rate(t, lexical_cast<unsigned>(params.substr(0, i)));
216         ::set_data_bits(t, params[i+1]-'0');
217         ::set_parity(t, (params[i+2]=='E' ? EVEN : params[i+2]=='O' ? ODD : NONE));
218         ::set_stop_bits(t, params[i+3]-'0');
219         tcsetattr(handle, TCSADRAIN, &t);
220 #endif
221 }
222
223 void Serial::close()
224 {
225 #ifndef WIN32
226         ::close(handle);
227 #endif
228 }
229
230 unsigned Serial::do_write(const char *buf, unsigned size)
231 {
232         if(size==0)
233                 return 0;
234
235 #ifndef WIN32
236         int ret = ::write(handle, buf, size);
237         if(ret==-1)
238         {
239                 if(errno==EAGAIN)
240                         return 0;
241                 else
242                         throw SystemError("Writing to serial port failed", errno);
243         }
244 #endif
245
246         return ret;
247 }
248
249 unsigned Serial::do_read(char *buf, unsigned size)
250 {
251         if(size==0)
252                 return 0;
253
254 #ifndef WIN32
255         int ret = ::read(handle, buf, size);
256         if(ret==-1)
257         {
258                 if(errno==EAGAIN)
259                         return 0;
260                 else
261                         throw SystemError("Reading from serial port failed", errno);
262         }
263 #endif
264
265         return ret;
266 }
267
268 } // namespace IO
269 } // namespace Msp