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