1 // Copyright (C) 2008-2011 David Sugar, Tycho Softworks.
2 //
3 // This file is part of GNU Bayonne.
4 //
5 // GNU Bayonne is free software; you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation; either version 3 of the License, or
8 // (at your option) any later version.
9 //
10 // GNU Bayonne is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with GNU Bayonne. If not, see <http://www.gnu.org/licenses/>.
17
18 #include <config.h>
19 #include <ucommon/ucommon.h>
20 #include <ucommon/export.h>
22
23 #ifdef _MSWINDOWS_
24 #include <io.h>
25
26 #define ASCII_XON 0x11
27 #define ASCII_XOFF 0x13
28
30 using namespace UCOMMON_NAMESPACE;
31
32 class __LOCAL serial : public Serial
33 {
34 private:
35 HANDLE fd;
36 DCB original, current;
37
38 void restore(void);
39 bool set(const char *format);
40 size_t get(void *addr, size_t len);
41 size_t put(void *addr, size_t len);
42 void dtr(timeout_t toggle_time);
43 void bin(size_t size, timeout_t timeout);
44 void text(char nl1, char nl2);
45 void clear(void);
46 bool flush(timeout_t timeout);
47 bool wait(timeout_t timeout);
48 void sync(void);
49
50 operator bool();
51 bool operator!();
52
53 public:
54 serial(const char *name);
55 ~serial();
56 };
57
58 serial::serial(const char *name) : Serial()
59 {
60 fd = CreateFile(name,
61 GENERIC_READ | GENERIC_WRITE,
62 0, // exclusive access
63 NULL, // no security attrs
64 OPEN_EXISTING,
65 FILE_FLAG_OVERLAPPED | FILE_ATTRIBUTE_NORMAL | FILE_FLAG_WRITE_THROUGH | FILE_FLAG_NO_BUFFERING,
66 NULL);
67
68 if(fd == INVALID_HANDLE_VALUE) {
69 return;
70 }
71
72 current.DCBlength = sizeof(DCB);
73 original.DCBlength = sizeof(DCB);
74
75 GetCommState(fd, &original);
76 GetCommState(fd, ¤t);
77
78 current.DCBlength = sizeof(DCB);
79 current.BaudRate = 1200;
80 current.Parity = NOPARITY;
81 current.ByteSize = 8;
82
83 current.XonChar = ASCII_XON;
84 current.XoffChar = ASCII_XOFF;
85 current.XonLim = 100;
86 current.XoffLim = 100;
87 current.fOutxDsrFlow = 0;
88 current.fDtrControl = DTR_CONTROL_ENABLE;
89 current.fOutxCtsFlow = 1;
90 current.fRtsControl = RTS_CONTROL_ENABLE;
91 current.fInX = current.fOutX = 0;
92
93 current.fBinary = true;
94 current.fParity = true;
95
96 SetCommState(fd, ¤t);
97 }
98
99 serial::~serial()
100 {
101 if(fd == INVALID_HANDLE_VALUE)
102 return;
103
104 restore();
105 CloseHandle(fd);
106 fd = INVALID_HANDLE_VALUE;
107 }
108
109 serial::operator bool()
110 {
111 if(fd == INVALID_HANDLE_VALUE)
112 return false;
113
114 return true;
115 }
116
117 bool serial::operator!()
118 {
119 if(fd == INVALID_HANDLE_VALUE)
120 return true;
121
122 return false;
123 }
124
125 void serial::restore(void)
126 {
127 if(fd == INVALID_HANDLE_VALUE)
128 return;
129
130 memcpy(¤t, &original, sizeof(current));
131 SetCommState(fd, ¤t);
132 }
133
134 bool serial::set(const char *format)
135 {
136 assert(format != NULL);
137
138 if(fd == INVALID_HANDLE_VALUE)
139 return false;
140
141 unsigned long opt;
142 char buf[256];
143 String::set(buf, sizeof(buf), format);
144
145 char *cp = strtok(buf, ",");
146 while(cp) {
147 switch(*cp) {
148 case 'n':
149 case 'N':
150 current.Parity = NOPARITY;
151 break;
152 case 'e':
153 case 'E':
154 current.Parity = EVENPARITY;
155 break;
156 case 'o':
157 case 'O':
158 current.Parity = ODDPARITY;
159 break;
160 case 's':
161 case 'S':
162 current.fInX = current.fOutX = TRUE;
163 current.fOutxCtsFlow = FALSE;
164 current.fRtsControl = FALSE;
165 break;
166 case 'H':
167 case 'h':
168 current.fInX = current.fOutX = FALSE;
169 current.fOutxCtsFlow = TRUE;
170 current.fRtsControl = RTS_CONTROL_HANDSHAKE;
171 break;
172 case 'b':
173 case 'B':
174 current.fInX = current.fOutX = TRUE;
175 current.fOutxCtsFlow = TRUE;
176 current.fRtsControl = RTS_CONTROL_HANDSHAKE;
177 break;
178 case '0':
179 case '1':
180 case '2':
181 case '3':
182 case '4':
183 case '5':
184 case '6':
185 case '7':
186 case '8':
187 case '9':
188 opt = atol(cp);
189 switch(opt) {
190 case 1:
191 current.StopBits = ONESTOPBIT;
192 break;
193 case 2:
194 current.StopBits = TWOSTOPBITS;
195 break;
196 case 5:
197 case 6:
198 case 7:
199 case 8:
200 current.ByteSize = (unsigned char)opt;
201 break;
202 default:
203 current.BaudRate = opt;
204 break;
205 }
206 break;
207 default:
208 error = EINVAL;
209 return false;
210 }
211 cp = strtok(NULL, ",");
212 }
213 current.DCBlength = sizeof(DCB);
214 SetCommState(fd, ¤t);
215 return true;
216 }
217
218 size_t serial::get(void *data, size_t len)
219 {
220 DWORD dwLength = 0, dwError, dwReadLength;
221 COMSTAT cs;
222 OVERLAPPED ol;
223
224 // Return zero if handle is invalid
225 if(fd == INVALID_HANDLE_VALUE)
226 return 0;
227
228 // Read max length or only what is available
229 ClearCommError(fd, &dwError, &cs);
230
231 // If not requiring an exact byte count, get whatever is available
232 if(len > (size_t)cs.cbInQue)
233 dwReadLength = cs.cbInQue;
234 else
235 dwReadLength = len;
236
237 memset(&ol, 0, sizeof(OVERLAPPED));
238 ol.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
239
240 if(dwReadLength > 0) {
241 if(ReadFile(fd, data, dwReadLength, &dwLength, &ol) == FALSE) {
242 if(GetLastError() == ERROR_IO_PENDING) {
243 WaitForSingleObject(ol.hEvent, INFINITE);
244 GetOverlappedResult(fd, &ol, &dwLength, TRUE);
245 }
246 else
247 ClearCommError(fd, &dwError, &cs);
248 }
249 }
250
251 if(ol.hEvent != INVALID_HANDLE_VALUE)
252 CloseHandle(ol.hEvent);
253
254 return dwLength;
255 }
256
257 size_t serial::put(void *data, size_t len)
258 {
259 COMSTAT cs;
260 unsigned long dwError = 0;
261 OVERLAPPED ol;
262
263 // Clear the com port of any error condition prior to read
264 ClearCommError(fd, &dwError, &cs);
265
266 unsigned long retSize = 0;
267
268 memset(&ol, 0, sizeof(OVERLAPPED));
269 ol.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
270
271 if(WriteFile(fd, data, len, &retSize, &ol) == FALSE) {
272 if(GetLastError() == ERROR_IO_PENDING) {
273 WaitForSingleObject(ol.hEvent, INFINITE);
274 GetOverlappedResult(fd, &ol, &retSize, TRUE);
275 }
276 else
277 ClearCommError(fd, &dwError, &cs);
278 }
279
280 if(ol.hEvent != INVALID_HANDLE_VALUE)
281 CloseHandle(ol.hEvent);
282
283 return retSize;
284 }
285
286 bool serial::wait(timeout_t timeout)
287 {
288 unsigned long dwError;
289 COMSTAT cs;
290
291 if(fd == INVALID_HANDLE_VALUE)
292 return false;
293
294 ClearCommError(fd, &dwError, &cs);
295
296 if(timeout == 0)
297 return (0 != cs.cbInQue);
298
299 OVERLAPPED ol;
300 DWORD dwMask;
301 DWORD dwEvents = 0;
302 BOOL suc;
303
304 memset(&ol, 0, sizeof(OVERLAPPED));
305 ol.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
306 dwMask = EV_RXCHAR;
307 SetCommMask(fd, dwMask);
308 // let's wait for event or timeout
309 if((suc = WaitCommEvent(fd, &dwEvents, &ol)) == FALSE) {
310 if(GetLastError() == ERROR_IO_PENDING) {
311 DWORD transferred;
312
313 dwError = WaitForSingleObject(ol.hEvent, timeout);
314 if (dwError != WAIT_OBJECT_0)
315 SetCommMask(fd, 0);
316
317 suc = GetOverlappedResult(fd, &ol, &transferred, TRUE);
318 if (suc)
319 suc = (dwEvents & dwMask) ? TRUE : FALSE;
320 }
321 else
322 ClearCommError(fd, &dwError, &cs);
323 }
324
325 if(ol.hEvent != INVALID_HANDLE_VALUE)
326 CloseHandle(ol.hEvent);
327
328 if(suc == FALSE)
329 return false;
330 return true;
331 }
332
333 bool serial::flush(timeout_t timeout)
334 {
335 unsigned long dwError;
336 COMSTAT cs;
337
338 if(fd == INVALID_HANDLE_VALUE)
339 return false;
340
341 ClearCommError(fd, &dwError, &cs);
342
343 if(timeout == 0 && cs.cbOutQue > 0) {
344 PurgeComm(fd, PURGE_TXABORT | PURGE_TXCLEAR);
345 return true;
346 }
347
348 if(cs.cbOutQue == 0)
349 return false;
350
351 OVERLAPPED ol;
352 DWORD dwMask;
353 DWORD dwEvents = 0;
354 BOOL suc;
355
356 memset(&ol, 0, sizeof(OVERLAPPED));
357 ol.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
358 dwMask = EV_TXEMPTY;
359 SetCommMask(fd, dwMask);
360 // let's wait for event or timeout
361 if((suc = WaitCommEvent(fd, &dwEvents, &ol)) == FALSE) {
362 if(GetLastError() == ERROR_IO_PENDING) {
363 DWORD transferred;
364
365 dwError = WaitForSingleObject(ol.hEvent, timeout);
366 if (dwError != WAIT_OBJECT_0)
367 SetCommMask(fd, 0);
368
369 suc = GetOverlappedResult(fd, &ol, &transferred, TRUE);
370 if (suc)
371 suc = (dwEvents & dwMask) ? TRUE : FALSE;
372 }
373 else
374 ClearCommError(fd, &dwError, &cs);
375 }
376
377 if(ol.hEvent != INVALID_HANDLE_VALUE)
378 CloseHandle(ol.hEvent);
379
380 if(suc == TRUE)
381 return false;
382
383 PurgeComm(fd, PURGE_TXABORT | PURGE_TXCLEAR);
384 return true;
385 }
386
387 void serial::clear(void)
388 {
389 PurgeComm(fd, PURGE_RXABORT | PURGE_RXCLEAR);
390 }
391
392 void serial::sync(void)
393 {
394 }
395
396 void serial::bin(size_t size, timeout_t timeout)
397 {
398 }
399
400 void serial::text(char nl1, char nl2)
401 {
402 }
403
404 void serial::dtr(timeout_t timeout)
405 {
406 EscapeCommFunction(fd, CLRDTR);
407 if(timeout) {
408 Thread::sleep(timeout);
409 EscapeCommFunction(fd, SETDTR);
410 }
411 }
412
413 Serial *Serial::create(const char *name)
414 {
415 char buf[65];
416
417 String::set(buf, sizeof(buf), name);
418
419 char *cp = strchr(buf, ':');
420 if(cp)
421 *cp = 0;
422
423 serial_t dev = new serial(buf);
424 name = strchr(name, ':');
425 if(dev && name)
426 dev->set(++name);
427
428 return dev;
429 }
430
431 stringpager *Serial::list(void)
432 {
433 DWORD index = 0;
434 TCHAR keyname[4096];
435 DWORD size = sizeof(keyname);
436 stringpager *list = new stringpager;
437 HKEY reg;
438 FILETIME fTime;
439
440 if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, "HARDWARE\\DEVICEMAP\\SERIALCOMM", 0, KEY_READ, ®) == ERROR_SUCCESS) {
441 while(RegEnumKeyEx(reg, index++, keyname, &size, NULL, NULL, NULL, &fTime) == ERROR_SUCCESS) {
442 list->add(keyname);
443 }
444 RegCloseKey(reg);
445 }
446 return list;
447 }
448
449 #endif
450
#define BAYONNE_NAMESPACE
GNU Bayonne library namespace.