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