1 /*
2 * winserial.cxx
3 *
4 * Miscellaneous implementation of classes for Win32
5 *
6 * Portable Windows Library
7 *
8 * Copyright (c) 1993-1998 Equivalence Pty. Ltd.
9 *
10 * The contents of this file are subject to the Mozilla Public License
11 * Version 1.0 (the "License"); you may not use this file except in
12 * compliance with the License. You may obtain a copy of the License at
13 * http://www.mozilla.org/MPL/
14 *
15 * Software distributed under the License is distributed on an "AS IS"
16 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
17 * the License for the specific language governing rights and limitations
18 * under the License.
19 *
20 * The Original Code is Portable Windows Library.
21 *
22 * The Initial Developer of the Original Code is Equivalence Pty. Ltd.
23 *
24 * Portions are Copyright (C) 1993 Free Software Foundation, Inc.
25 * All Rights Reserved.
26 *
27 * Contributor(s): ______________________________________.
28 *
29 * $Revision: 20385 $
30 * $Author: rjongbloed $
31 * $Date: 2008-06-04 05:40:38 -0500 (Wed, 04 Jun 2008) $
32 */
33
34 #include <ptlib.h>
35 #include <ptlib/serchan.h>
36
37
38 #define QUEUE_SIZE 2048
39
40
41 ///////////////////////////////////////////////////////////////////////////////
42 // PSerialChannel
43
Construct()44 void PSerialChannel::Construct()
45 {
46 commsResource = INVALID_HANDLE_VALUE;
47
48 char str[50];
49 strcpy(str, "com1");
50 GetProfileString("ports", str, "9600,n,8,1,x", &str[5], sizeof(str)-6);
51 str[4] = ':';
52 memset(&deviceControlBlock, 0, sizeof(deviceControlBlock));
53 deviceControlBlock.DCBlength = sizeof(deviceControlBlock);
54 BuildCommDCB(str, &deviceControlBlock);
55
56 // These values are not set by BuildCommDCB
57 deviceControlBlock.XoffChar = 19;
58 deviceControlBlock.XonChar = 17;
59 deviceControlBlock.XoffLim = (QUEUE_SIZE * 7)/8; // upper limit before XOFF is sent to stop reception
60 deviceControlBlock.XonLim = (QUEUE_SIZE * 3)/4; // lower limit before XON is sent to re-enabled reception
61 }
62
63
GetName() const64 PString PSerialChannel::GetName() const
65 {
66 return portName;
67 }
68
69
Read(void * buf,PINDEX len)70 PBoolean PSerialChannel::Read(void * buf, PINDEX len)
71 {
72 lastReadCount = 0;
73
74 if (!IsOpen())
75 return SetErrorValues(NotOpen, EBADF, LastReadError);
76
77 COMMTIMEOUTS cto;
78 PAssertOS(GetCommTimeouts(commsResource, &cto));
79 cto.ReadIntervalTimeout = 0;
80 cto.ReadTotalTimeoutMultiplier = 0;
81 cto.ReadTotalTimeoutConstant = 0;
82 cto.ReadIntervalTimeout = MAXDWORD; // Immediate timeout
83 PAssertOS(SetCommTimeouts(commsResource, &cto));
84
85 DWORD eventMask;
86 PAssertOS(GetCommMask(commsResource, &eventMask));
87 if (eventMask != (EV_RXCHAR|EV_TXEMPTY))
88 PAssertOS(SetCommMask(commsResource, EV_RXCHAR|EV_TXEMPTY));
89
90 DWORD timeToGo = readTimeout.GetInterval();
91 DWORD bytesToGo = len;
92 char * bufferPtr = (char *)buf;
93
94 for (;;) {
95 PWin32Overlapped overlap;
96 DWORD readCount = 0;
97 if (!ReadFile(commsResource, bufferPtr, bytesToGo, &readCount, &overlap)) {
98 if (::GetLastError() != ERROR_IO_PENDING)
99 return ConvertOSError(-2, LastReadError);
100 if (!::GetOverlappedResult(commsResource, &overlap, &readCount, PFalse))
101 return ConvertOSError(-2, LastReadError);
102 }
103
104 bytesToGo -= readCount;
105 bufferPtr += readCount;
106 lastReadCount += readCount;
107 if (lastReadCount >= len || timeToGo == 0)
108 return lastReadCount > 0;
109
110 if (!::WaitCommEvent(commsResource, &eventMask, &overlap)) {
111 if (::GetLastError()!= ERROR_IO_PENDING)
112 return ConvertOSError(-2, LastReadError);
113 DWORD err = ::WaitForSingleObject(overlap.hEvent, timeToGo);
114 if (err == WAIT_TIMEOUT) {
115 SetErrorValues(Timeout, EAGAIN, LastReadError);
116 ::CancelIo(commsResource);
117 return lastReadCount > 0;
118 }
119 else if (err == WAIT_FAILED)
120 return ConvertOSError(-2, LastReadError);
121 }
122 }
123 }
124
125
Write(const void * buf,PINDEX len)126 PBoolean PSerialChannel::Write(const void * buf, PINDEX len)
127 {
128 lastWriteCount = 0;
129
130 if (!IsOpen())
131 return SetErrorValues(NotOpen, EBADF, LastWriteError);
132
133 COMMTIMEOUTS cto;
134 PAssertOS(GetCommTimeouts(commsResource, &cto));
135 cto.WriteTotalTimeoutMultiplier = 0;
136 if (writeTimeout == PMaxTimeInterval)
137 cto.WriteTotalTimeoutConstant = 0;
138 else if (writeTimeout <= PTimeInterval(0))
139 cto.WriteTotalTimeoutConstant = 1;
140 else
141 cto.WriteTotalTimeoutConstant = writeTimeout.GetInterval();
142 PAssertOS(SetCommTimeouts(commsResource, &cto));
143
144 PWin32Overlapped overlap;
145 if (WriteFile(commsResource, buf, len, (LPDWORD)&lastWriteCount, &overlap))
146 return lastWriteCount == len;
147
148 if (GetLastError() == ERROR_IO_PENDING)
149 if (GetOverlappedResult(commsResource, &overlap, (LPDWORD)&lastWriteCount, PTrue)) {
150 return lastWriteCount == len;
151 }
152
153 ConvertOSError(-2, LastWriteError);
154
155 return PFalse;
156 }
157
158
Close()159 PBoolean PSerialChannel::Close()
160 {
161 if (!IsOpen())
162 return SetErrorValues(NotOpen, EBADF);
163
164 CloseHandle(commsResource);
165 commsResource = INVALID_HANDLE_VALUE;
166 os_handle = -1;
167 return ConvertOSError(-2);
168 }
169
170
SetCommsParam(DWORD speed,BYTE data,Parity parity,BYTE stop,FlowControl inputFlow,FlowControl outputFlow)171 PBoolean PSerialChannel::SetCommsParam(DWORD speed, BYTE data, Parity parity,
172 BYTE stop, FlowControl inputFlow, FlowControl outputFlow)
173 {
174 if (speed > 0)
175 deviceControlBlock.BaudRate = speed;
176
177 if (data > 0)
178 deviceControlBlock.ByteSize = data;
179
180 switch (parity) {
181 case NoParity :
182 deviceControlBlock.Parity = NOPARITY;
183 break;
184 case OddParity :
185 deviceControlBlock.Parity = ODDPARITY;
186 break;
187 case EvenParity :
188 deviceControlBlock.Parity = EVENPARITY;
189 break;
190 case MarkParity :
191 deviceControlBlock.Parity = MARKPARITY;
192 break;
193 case SpaceParity :
194 deviceControlBlock.Parity = SPACEPARITY;
195 break;
196 }
197
198 switch (stop) {
199 case 1 :
200 deviceControlBlock.StopBits = ONESTOPBIT;
201 break;
202 case 2 :
203 deviceControlBlock.StopBits = TWOSTOPBITS;
204 break;
205 }
206
207 switch (inputFlow) {
208 case NoFlowControl :
209 deviceControlBlock.fRtsControl = RTS_CONTROL_DISABLE;
210 deviceControlBlock.fInX = PFalse;
211 break;
212 case XonXoff :
213 deviceControlBlock.fRtsControl = RTS_CONTROL_DISABLE;
214 deviceControlBlock.fInX = PTrue;
215 break;
216 case RtsCts :
217 deviceControlBlock.fRtsControl = RTS_CONTROL_HANDSHAKE;
218 deviceControlBlock.fInX = PFalse;
219 break;
220 }
221
222 switch (outputFlow) {
223 case NoFlowControl :
224 deviceControlBlock.fOutxCtsFlow = PFalse;
225 deviceControlBlock.fOutxDsrFlow = PFalse;
226 deviceControlBlock.fOutX = PFalse;
227 break;
228 case XonXoff :
229 deviceControlBlock.fOutxCtsFlow = PFalse;
230 deviceControlBlock.fOutxDsrFlow = PFalse;
231 deviceControlBlock.fOutX = PTrue;
232 break;
233 case RtsCts :
234 deviceControlBlock.fOutxCtsFlow = PTrue;
235 deviceControlBlock.fOutxDsrFlow = PFalse;
236 deviceControlBlock.fOutX = PFalse;
237 break;
238 }
239
240 if (!IsOpen())
241 return SetErrorValues(NotOpen, EBADF);
242
243 return ConvertOSError(SetCommState(commsResource, &deviceControlBlock) ? 0 : -2);
244 }
245
246
Open(const PString & port,DWORD speed,BYTE data,Parity parity,BYTE stop,FlowControl inputFlow,FlowControl outputFlow)247 PBoolean PSerialChannel::Open(const PString & port, DWORD speed, BYTE data,
248 Parity parity, BYTE stop, FlowControl inputFlow, FlowControl outputFlow)
249 {
250 Close();
251
252 portName = port;
253 if (portName.Find(PDIR_SEPARATOR) == P_MAX_INDEX)
254 portName = "\\\\.\\" + port;
255 commsResource = CreateFile(portName,
256 GENERIC_READ|GENERIC_WRITE,
257 0,
258 NULL,
259 OPEN_EXISTING,
260 FILE_FLAG_OVERLAPPED,
261 NULL);
262 if (commsResource == INVALID_HANDLE_VALUE)
263 return ConvertOSError(-2);
264
265 os_handle = 0;
266
267 SetupComm(commsResource, QUEUE_SIZE, QUEUE_SIZE);
268
269 if (SetCommsParam(speed, data, parity, stop, inputFlow, outputFlow))
270 return PTrue;
271
272 ConvertOSError(-2);
273 CloseHandle(commsResource);
274 os_handle = -1;
275 return PFalse;
276 }
277
278
SetSpeed(DWORD speed)279 PBoolean PSerialChannel::SetSpeed(DWORD speed)
280 {
281 return SetCommsParam(speed,
282 0, DefaultParity, 0, DefaultFlowControl, DefaultFlowControl);
283 }
284
285
GetSpeed() const286 DWORD PSerialChannel::GetSpeed() const
287 {
288 return deviceControlBlock.BaudRate;
289 }
290
291
SetDataBits(BYTE data)292 PBoolean PSerialChannel::SetDataBits(BYTE data)
293 {
294 return SetCommsParam(0,
295 data, DefaultParity, 0, DefaultFlowControl, DefaultFlowControl);
296 }
297
298
GetDataBits() const299 BYTE PSerialChannel::GetDataBits() const
300 {
301 return deviceControlBlock.ByteSize;
302 }
303
304
SetParity(Parity parity)305 PBoolean PSerialChannel::SetParity(Parity parity)
306 {
307 return SetCommsParam(0,0,parity,0,DefaultFlowControl,DefaultFlowControl);
308 }
309
310
GetParity() const311 PSerialChannel::Parity PSerialChannel::GetParity() const
312 {
313 switch (deviceControlBlock.Parity) {
314 case ODDPARITY :
315 return OddParity;
316 case EVENPARITY :
317 return EvenParity;
318 case MARKPARITY :
319 return MarkParity;
320 case SPACEPARITY :
321 return SpaceParity;
322 }
323 return NoParity;
324 }
325
326
SetStopBits(BYTE stop)327 PBoolean PSerialChannel::SetStopBits(BYTE stop)
328 {
329 return SetCommsParam(0,
330 0, DefaultParity, stop, DefaultFlowControl, DefaultFlowControl);
331 }
332
333
GetStopBits() const334 BYTE PSerialChannel::GetStopBits() const
335 {
336 return (BYTE)(deviceControlBlock.StopBits == ONESTOPBIT ? 1 : 2);
337 }
338
339
SetInputFlowControl(FlowControl flowControl)340 PBoolean PSerialChannel::SetInputFlowControl(FlowControl flowControl)
341 {
342 return SetCommsParam(0,0,DefaultParity,0,flowControl,DefaultFlowControl);
343 }
344
345
GetInputFlowControl() const346 PSerialChannel::FlowControl PSerialChannel::GetInputFlowControl() const
347 {
348 if (deviceControlBlock.fRtsControl == RTS_CONTROL_HANDSHAKE)
349 return RtsCts;
350 if (deviceControlBlock.fInX != 0)
351 return XonXoff;
352 return NoFlowControl;
353 }
354
355
SetOutputFlowControl(FlowControl flowControl)356 PBoolean PSerialChannel::SetOutputFlowControl(FlowControl flowControl)
357 {
358 return SetCommsParam(0,0,DefaultParity,0,DefaultFlowControl,flowControl);
359 }
360
361
GetOutputFlowControl() const362 PSerialChannel::FlowControl PSerialChannel::GetOutputFlowControl() const
363 {
364 if (deviceControlBlock.fOutxCtsFlow != 0)
365 return RtsCts;
366 if (deviceControlBlock.fOutX != 0)
367 return XonXoff;
368 return NoFlowControl;
369 }
370
371
SetDTR(PBoolean state)372 void PSerialChannel::SetDTR(PBoolean state)
373 {
374 if (IsOpen())
375 PAssertOS(EscapeCommFunction(commsResource, state ? SETDTR : CLRDTR));
376 else
377 SetErrorValues(NotOpen, EBADF);
378 }
379
380
SetRTS(PBoolean state)381 void PSerialChannel::SetRTS(PBoolean state)
382 {
383 if (IsOpen())
384 PAssertOS(EscapeCommFunction(commsResource, state ? SETRTS : CLRRTS));
385 else
386 SetErrorValues(NotOpen, EBADF);
387 }
388
389
SetBreak(PBoolean state)390 void PSerialChannel::SetBreak(PBoolean state)
391 {
392 if (IsOpen())
393 if (state)
394 PAssertOS(SetCommBreak(commsResource));
395 else
396 PAssertOS(ClearCommBreak(commsResource));
397 else
398 SetErrorValues(NotOpen, EBADF);
399 }
400
401
GetCTS()402 PBoolean PSerialChannel::GetCTS()
403 {
404 if (!IsOpen())
405 return SetErrorValues(NotOpen, EBADF);
406
407 DWORD stat;
408 PAssertOS(GetCommModemStatus(commsResource, &stat));
409 return (stat&MS_CTS_ON) != 0;
410 }
411
412
GetDSR()413 PBoolean PSerialChannel::GetDSR()
414 {
415 if (!IsOpen())
416 return SetErrorValues(NotOpen, EBADF);
417
418 DWORD stat;
419 PAssertOS(GetCommModemStatus(commsResource, &stat));
420 return (stat&MS_DSR_ON) != 0;
421 }
422
423
GetDCD()424 PBoolean PSerialChannel::GetDCD()
425 {
426 if (!IsOpen())
427 return SetErrorValues(NotOpen, EBADF);
428
429 DWORD stat;
430 PAssertOS(GetCommModemStatus(commsResource, &stat));
431 return (stat&MS_RLSD_ON) != 0;
432 }
433
434
GetRing()435 PBoolean PSerialChannel::GetRing()
436 {
437 if (!IsOpen())
438 return SetErrorValues(NotOpen, EBADF);
439
440 DWORD stat;
441 PAssertOS(GetCommModemStatus(commsResource, &stat));
442 return (stat&MS_RING_ON) != 0;
443 }
444
445
GetPortNames()446 PStringList PSerialChannel::GetPortNames()
447 {
448 PStringList ports;
449 for (char p = 1; p <= 9; p++)
450 ports.AppendString(psprintf("\\\\.\\COM%u", p));
451 return ports;
452 }
453