BOROSOFTWAREAll work →
scantool/diag_tty_win.c 617 lines
1/* freediag
2 * windows-specific tty code
3 * (c) fenugrec 2014-2016
4 * GPL3
5 */
6
7#include <stdlib.h>
8#include <string.h>
9#include <assert.h>
10
11#include "diag.h"
12#include "diag_err.h"
13#include "diag_os.h"
14#include "diag_tty_win.h"
15
16#include <windows.h>
17#include <basetsd.h>
18
19extern LARGE_INTEGER perfo_freq;
20extern float pf_conv; //these two are defined in diag_os
21
22//struct tty_int : internal data, one per L0 struct
23struct tty_int {
24 char *name; //port name, alloc'd
25 HANDLE fd;
26 DCB dcb;
27};
28
29//diag_tty_open : open specified port for L0
30ttyp *diag_tty_open(const char *portname) {
31 int rv;
32 struct tty_int *wti;
33 size_t n = strlen(portname) + 1;
34 COMMTIMEOUTS devtimeouts;
35
36 assert(portname);
37
38 if ((rv=diag_calloc(&wti,1))) {
39 return diag_pseterr(rv);
40 }
41
42 wti->fd = INVALID_HANDLE_VALUE;
43
44 //allocate space for portname name
45 if ((rv=diag_malloc(&wti->name, n))) {
46 free(wti);
47 return diag_pseterr(rv);
48 }
49 //Now, in case of errors we can call diag_tty_close() on wti since its members are alloc'ed
50 strncpy(wti->name, portname, n);
51
52 wti->fd=CreateFile(portname, GENERIC_READ | GENERIC_WRITE, 0, NULL,
53 OPEN_EXISTING,
54 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_NO_BUFFERING | FILE_FLAG_WRITE_THROUGH,
55 NULL);
56 //File hande is created as non-overlapped. This may change eventually.
57
58 if (wti->fd != INVALID_HANDLE_VALUE) {
59 DIAG_DBGM(diag_l0_debug, DIAG_DEBUG_OPEN, DIAG_DBGLEVEL_V,
60 FLFMT "Device %s opened, fd %p\n",
61 FL, wti->name, wti->fd);
62 } else {
63 fprintf(stderr,
64 FLFMT "Open of device interface \"%s\" failed: %s\n",
65 FL, wti->name, diag_os_geterr(0));
66 fprintf(stderr, FLFMT
67 "(Make sure the device specified corresponds to the\n", FL );
68 fprintf(stderr,
69 FLFMT "serial device your interface is connected to.\n", FL);
70
71 diag_tty_close(wti);
72 return diag_pseterr(DIAG_ERR_GENERAL);
73 }
74
75 //purge & abort everything.
76 PurgeComm(wti->fd,PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR);
77
78 //as opposed to the unix diag_tty.c ; this one doesn't save previous commstate. The next program to use the COM port
79 //will need to deal with it...
80
81 //We will load the DCB with the current comm state. This way we only need to call GetCommState once during a session
82 //and the DCB should contain coherent initial values
83 if (!GetCommState(wti->fd, &wti->dcb)) {
84 fprintf(stderr, FLFMT "Could not get comm state: %s\n",FL, diag_os_geterr(0));
85 diag_tty_close(wti);
86 return diag_pseterr(DIAG_ERR_GENERAL);
87 }
88
89 //Finally set COMMTIMEOUTS to reasonable values (all in ms) ?
90 devtimeouts.ReadIntervalTimeout=30; //i.e. more than 30ms between received bytes
91 devtimeouts.ReadTotalTimeoutMultiplier=5; //timeout per requested byte
92 devtimeouts.ReadTotalTimeoutConstant=20; // (constant + multiplier*numbytes) = total timeout on read(buf, numbytes)
93 devtimeouts.WriteTotalTimeoutMultiplier=0; //probably useless as all flow control will be disabled ??
94 devtimeouts.WriteTotalTimeoutConstant=0;
95 if (!SetCommTimeouts(wti->fd,&devtimeouts)) {
96 fprintf(stderr, FLFMT "Could not set comm timeouts: %s\n",FL, diag_os_geterr(0));
97 diag_tty_close(wti);
98 return diag_pseterr(DIAG_ERR_GENERAL);
99 }
100
101 return wti;
102} //diag_tty_open
103
104/* Close up the TTY and restore. */
105void diag_tty_close(ttyp *ttyh) {
106 struct tty_int *wti = ttyh;
107
108 if (!wti) {
109 return;
110 }
111
112 if (wti->name) {
113 free(wti->name);
114 }
115
116 if (wti->fd != INVALID_HANDLE_VALUE) {
117 PurgeComm(wti->fd,PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR);
118 CloseHandle(wti->fd);
119 DIAG_DBGM(diag_l0_debug, DIAG_DEBUG_CLOSE, DIAG_DBGLEVEL_V,
120 FLFMT "diag_tty_close : closing fd %p\n", FL, wti->fd);
121 }
122 free(wti);
123
124 return;
125} //diag_tty_close
126
127
128/*
129 * Set speed/parity etc of tty with settings in pset
130 * ret 0 if ok
131 */
132int diag_tty_setup(ttyp *ttyh,
133 const struct diag_serial_settings *pset) {
134 HANDLE devhandle; //just to clarify code
135 struct tty_int *wti = ttyh;
136 DCB *devstate;
137 COMMPROP supportedprops;
138 DCB verif_dcb;
139
140 devhandle = wti->fd;
141 devstate = &wti->dcb;
142
143 if (devhandle == INVALID_HANDLE_VALUE) {
144 fprintf(stderr, FLFMT "setup: something is not right\n", FL);
145 return diag_iseterr(DIAG_ERR_GENERAL);
146 }
147
148 //optional : verify if device supports the requested settings. Testing required to see if USB->serial bridges support this !
149 //i.e. some l0 devices try to set 5bps which isn't supported on some devices.
150 //For now let's just check if it supports custom baud rates. This check should be added to diag_tty_open where
151 //it would set appropriate flags to allow _l0 devices to adapt their functionality.
152 if (!GetCommProperties(devhandle,&supportedprops)) {
153 fprintf(stderr, FLFMT "could not getcommproperties: %s\n",FL, diag_os_geterr(0));
154 return diag_iseterr(DIAG_ERR_GENERAL);
155 }
156 //simple test : only check if custom baud rates are supported, but don't abort if they aren't. Just notify user.
157 if ( !(supportedprops.dwMaxBaud & BAUD_USER)) {
158 fprintf(stderr, FLFMT "warning : device does not support custom baud rates !\n", FL);
159 }
160
161
162
163 DIAG_DBGM(diag_l0_debug, DIAG_DEBUG_IOCTL, DIAG_DBGLEVEL_V,
164 FLFMT "dev %p; %ubps %d,%d,%d \n",
165 FL, (void *)devhandle, pset->speed, pset->databits,
166 pset->stopbits, pset->parflag);
167
168 /*
169 * Now load the DCB with the parameters requested.
170 */
171 // the DCB (devstate) has been loaded with initial values in diag_tty_open so it should already coherent.
172 devstate->BaudRate = pset->speed;
173 devstate->fBinary=1; // always binary mode.
174 switch (pset->parflag) {
175 case diag_par_n:
176 //no parity : disable parity in the DCB
177 devstate->fParity=0;
178 devstate->Parity=NOPARITY;
179 break;
180 case diag_par_e:
181 devstate->fParity=1;
182 devstate->Parity=EVENPARITY;
183 break;
184 case diag_par_o:
185 devstate->fParity=1;
186 devstate->Parity=ODDPARITY;
187 break;
188 default:
189 fprintf(stderr,
190 FLFMT "bad parity setting used !\n", FL);
191 return diag_iseterr(DIAG_ERR_GENERAL);
192 break;
193 }
194 devstate->fOutxCtsFlow=0;
195 devstate->fOutxDsrFlow=0; //disable output flow control
196 devstate->fDtrControl=DTR_CONTROL_DISABLE; //XXX allows to permanently set the DTR line !
197 devstate->fDsrSensitivity=0; //pay no attention to DSR for receiving
198 devstate->fTXContinueOnXoff=1; //probably irrelevant ?
199 devstate->fOutX=0; //disable Xon/Xoff tx flow ctl
200 devstate->fInX=0; //disable XonXoff rx flow ctl
201 devstate->fErrorChar=0; //do not replace data with bad parity
202 devstate->fNull=0; // do not discard null bytes ! on rx
203 devstate->fRtsControl=RTS_CONTROL_DISABLE; //XXX allows to set the RTS line!
204 devstate->fAbortOnError=0; //do not abort transfers on error ?
205 devstate->wReserved=0;
206 devstate->ByteSize=pset->databits; //bits per byte
207 switch (pset->stopbits) {
208 case diag_stopbits_1:
209 devstate->StopBits=ONESTOPBIT;
210 break;
211 case diag_stopbits_2:
212 devstate->StopBits=TWOSTOPBITS;
213 break;
214 default:
215 fprintf(stderr, FLFMT "bad stopbit setting used!)\n", FL);
216 return diag_iseterr(DIAG_ERR_GENERAL);
217 break;
218 }
219 // DCB in devstate is now filled.
220 if (!SetCommState(devhandle, devstate)) {
221 fprintf(stderr, FLFMT "Could not SetCommState: %s\n",FL, diag_os_geterr(0));
222 return diag_iseterr(DIAG_ERR_GENERAL);
223 }
224
225 //to be really thorough we do another GetCommState and check that the speed was set properly.
226 //This *may* help to detect if the serial port supports non-standard baudrates (5bps being
227 //particularly problematic with USB->serial converters)
228 //I see no particular reason to check all the other fields though.
229
230 if (!GetCommState(devhandle, &verif_dcb)) {
231 fprintf(stderr, FLFMT "Could not verify with GetCommState: %s\n", FL, diag_os_geterr(0));
232 return diag_iseterr(DIAG_ERR_GENERAL);
233 }
234 if (verif_dcb.BaudRate != pset->speed) {
235 fprintf(stderr, FLFMT "SetCommState failed : speed is currently %u\n", FL, (unsigned int) verif_dcb.BaudRate);
236 return diag_iseterr(DIAG_ERR_GENERAL);
237 }
238
239 return 0;
240} //diag_tty_setup
241
242/*
243 * Set/Clear DTR and RTS lines, as specified
244 * on Win32 this can easily be done by calling EscapeCommFunction twice.
245 * This takes around ~15-20us to do; probably with ~10 us skew between setting RTS and DTR.
246 * If it proves to be a problem, it's possible to change both RTS and DTR at once by updating
247 * the DCB and calling SetCommState. That call would take around 30-40us.
248 * Note : passing 1 in dtr or rts means "set DTR/RTS", i.e. positive voltage.
249 * ret 0 if ok
250 */
251int diag_tty_control(ttyp *ttyh, unsigned int dtr, unsigned int rts) {
252 unsigned int escapefunc;
253 struct tty_int *wti = ttyh;
254
255
256 if (wti->fd == INVALID_HANDLE_VALUE) {
257 fprintf(stderr, FLFMT "Error. Is the port open ?\n", FL);
258 return diag_iseterr(DIAG_ERR_GENERAL);
259 }
260
261 if (dtr) {
262 escapefunc=SETDTR;
263 } else {
264 escapefunc=CLRDTR;
265 }
266
267 if (!EscapeCommFunction(wti->fd,escapefunc)) {
268 fprintf(stderr, FLFMT "Could not change DTR: %s\n", FL, diag_os_geterr(0));
269 return diag_iseterr(DIAG_ERR_GENERAL);
270 }
271
272 if (rts) {
273 escapefunc=SETRTS;
274 } else {
275 escapefunc=CLRRTS;
276 }
277
278 if (!EscapeCommFunction(wti->fd,escapefunc)) {
279 fprintf(stderr, FLFMT "Could not change RTS: %s\n", FL, diag_os_geterr(0));
280 return diag_iseterr(DIAG_ERR_GENERAL);
281 }
282
283 return 0;
284} //diag_tty_control
285
286
287//diag_tty_write : return # of bytes or <0 if error?
288//this is an intimidating function to design.
289//test #2 : non-overlapped write (i.e. blocking AKA synchronous).
290//
291//flush buffers before returning; we could also SetCommMask
292//to wait for "EV_TXEMPTY" which would give a better idea
293//of when the data has been sent.
294
295ssize_t diag_tty_write(ttyp *ttyh, const void *buf, const size_t count) {
296 DWORD byteswritten;
297 OVERLAPPED *pOverlap;
298 struct tty_int *wti = ttyh;
299 pOverlap=NULL; //note : if overlap is eventually enabled, the CreateFile flags should be adjusted
300
301 if (wti->fd == INVALID_HANDLE_VALUE) {
302 fprintf(stderr, FLFMT "Error. Is the port open ?\n", FL);
303 return diag_iseterr(DIAG_ERR_GENERAL);
304 }
305
306 if (count == 0) {
307 return diag_iseterr(DIAG_ERR_BADLEN);
308 }
309
310 if (!WriteFile(wti->fd, buf, count, &byteswritten, pOverlap)) {
311 fprintf(stderr, FLFMT "WriteFile error:%s. %u bytes written, %u requested\n", FL, diag_os_geterr(0), (unsigned int) byteswritten, (unsigned) count);
312 return diag_iseterr(DIAG_ERR_GENERAL);
313 }
314 if (!FlushFileBuffers(wti->fd)) {
315 fprintf(stderr, FLFMT "tty_write : could not flush buffers, %s\n", FL, diag_os_geterr(0));
316 return diag_iseterr(DIAG_ERR_GENERAL);
317 }
318 return byteswritten;
319} //diag_tty_write
320
321
322// diag_tty_read
323//attempt to read (count) bytes until (timeout) passes.
324//This one returns # of bytes read (if any)
325//This is non-overlapped for now i.e. blocking.
326//timeouts and incomplete data are not handled properly. This is alpha...
327//From the API docs :
328// ReadFile returns when the number of bytes requested has been read, or an error occurs.
329
330
331ssize_t diag_tty_read(ttyp *ttyh, void *buf, size_t count, unsigned int timeout) {
332 DWORD bytesread;
333 ssize_t rv=DIAG_ERR_TIMEOUT;
334 OVERLAPPED *pOverlap;
335 struct tty_int *wti = ttyh;
336 pOverlap=NULL;
337 COMMTIMEOUTS devtimeouts;
338
339 if ((count == 0) || (timeout == 0)) {
340 return DIAG_ERR_BADLEN;
341 }
342
343 DIAG_DBGM(diag_l0_debug, DIAG_DEBUG_READ, DIAG_DBGLEVEL_V,
344 FLFMT "tty_read: ttyh=%p, fd=%p, len=%zu, t=%u\n",
345 FL, (void *)wti, (void *)wti->fd, count, timeout);
346
347 if (wti->fd == INVALID_HANDLE_VALUE) {
348 fprintf(stderr, FLFMT "Error. Is the port open ?\n", FL);
349 return diag_iseterr(DIAG_ERR_GENERAL);
350 }
351
352// GetCommTimeouts(wti->, &devtimeouts); //get current timeouts
353 //and modify them
354 devtimeouts.ReadIntervalTimeout= 0; //disabled
355 devtimeouts.ReadTotalTimeoutMultiplier=0; //timeout per requested byte
356 devtimeouts.ReadTotalTimeoutConstant=timeout; // (tconst + mult*numbytes) = total timeout on read
357 devtimeouts.WriteTotalTimeoutMultiplier=0; //probably useless as all flow control will be disabled ??
358 devtimeouts.WriteTotalTimeoutConstant=0;
359 if (!SetCommTimeouts(wti->fd, &devtimeouts)) {
360 fprintf(stderr, FLFMT "Could not set comm timeouts: %s\n",FL, diag_os_geterr(0));
361 return diag_iseterr(DIAG_ERR_GENERAL);
362 }
363
364 if (!ReadFile(wti->fd, buf, count, &bytesread, pOverlap)) {
365 fprintf(stderr, FLFMT "ReadFile error: %s\n",FL, diag_os_geterr(0));
366 return diag_iseterr(DIAG_ERR_GENERAL);
367 }
368 if (bytesread > 0) {
369 rv=bytesread;
370 }
371 return rv;
372
373}
374
375
376
377/*
378 * flush input buffer and display some of the discarded data
379 * ret 0 if ok
380 *
381 */
382int diag_tty_iflush(ttyp *ttyh) {
383 uint8_t buf[MAXRBUF];
384 int rv;
385 struct tty_int *wti = ttyh;
386
387 /* Read any old data hanging about on the port */
388 rv = diag_tty_read(wti, buf, sizeof(buf), IFLUSH_TIMEOUT);
389 if (rv > 0) {
390 //don't dump data : flood
391 DIAG_DBGM(diag_l0_debug, DIAG_DEBUG_DATA, DIAG_DBGLEVEL_V,
392 FLFMT "tty_iflush: >=%d junk bytes discarded: 0x%X...\n",
393 FL, rv, buf[0]);
394 }
395 PurgeComm(wti->fd, PURGE_RXABORT | PURGE_RXCLEAR);
396
397 return 0;
398}
399
400
401
402// diag_tty_break #1 : use Set / ClearCommBreak
403// and return as soon as break is cleared.
404// ret 0 if ok
405int diag_tty_break(ttyp *ttyh, const unsigned int ms) {
406 LARGE_INTEGER qpc1, qpc2; //for timing verification
407 long real_t; //"real" duration
408 struct tty_int *wti = ttyh;
409 int errval=0;
410
411 if (wti->fd == INVALID_HANDLE_VALUE) {
412 fprintf(stderr, FLFMT "Error. Is the port open ?\n", FL);
413 return diag_iseterr(DIAG_ERR_GENERAL);
414 }
415
416 if (ms <= 1) {
417 return diag_iseterr(DIAG_ERR_GENERAL);
418 }
419
420 QueryPerformanceCounter(&qpc1);
421 errval = !SetCommBreak(wti->fd);
422 QueryPerformanceCounter(&qpc2);
423 //that call can take quite a while (6ms !!) on some setups (win7 + CH340 USB-Serial).
424 //It's still impossible to know (from here) when exactly TXD goes low (beginning or end of the call)
425 real_t=(long) (pf_conv * (qpc2.QuadPart-qpc1.QuadPart)) / 1000L;
426 real_t = (long) ms - real_t; //time remaining
427 if (real_t <= 0) {
428 real_t = 0;
429 }
430 diag_os_millisleep((unsigned int ) real_t);
431
432 errval |= !ClearCommBreak(wti->fd);
433
434 if (errval) {
435 //if either of the calls failed
436 fprintf(stderr, FLFMT "tty_break could not set/clear break!\n", FL);
437 return diag_iseterr(DIAG_ERR_GENERAL);
438 }
439
440 return 0;
441}
442
443
444/*
445 * diag_tty_fastbreak: send 0x00 at 360bps => fixed 25ms break; return [ms] after starting break.
446 * This is for ISO14230 fast init : typically diag_tty_fastbreak(tty_int, 50)
447 * It assumes the interface is half-duplex.
448 * Ret 0 if ok
449 */
450int diag_tty_fastbreak(ttyp *ttyh, const unsigned int ms) {
451 HANDLE dh; //just to clarify code
452 struct tty_int *wti = ttyh;
453 DCB tempDCB; //for sabotaging the settings just to do the break
454 DCB origDCB;
455 LARGE_INTEGER qpc1, qpc2, qpc3; //to time the break period
456 LONGLONG timediff; //64bit delta
457 long int tremain,counts, break_error;
458
459 uint8_t cbuf;
460 int xferd;
461 DWORD byteswritten;
462
463 dh = wti->fd;
464 if (ms<25) { //very funny
465 return diag_iseterr(DIAG_ERR_TIMEOUT);
466 }
467
468 if (dh == INVALID_HANDLE_VALUE) {
469 fprintf(stderr, FLFMT "Error. Is the port open ?\n", FL);
470 return diag_iseterr(DIAG_ERR_GENERAL);
471 }
472
473 GetCommState(dh, &origDCB);
474 GetCommState(dh, &tempDCB); //ugly, but a memcpy would be worse
475
476 tempDCB.BaudRate=360;
477 tempDCB.ByteSize=8;
478 tempDCB.fParity=0;
479 tempDCB.Parity=NOPARITY;
480 tempDCB.StopBits=ONESTOPBIT;
481
482 if (!SetCommState(dh, &tempDCB)) {
483 fprintf(stderr, FLFMT "SetCommState error\n", FL);
484 return diag_iseterr(DIAG_ERR_GENERAL);
485 }
486
487 /* Send a 0x00 byte message */
488
489 if (!WriteFile(dh, "\0", 1, &byteswritten, NULL)) {
490 fprintf(stderr, FLFMT "WriteFile error:%s\n", FL, diag_os_geterr(0));
491 SetCommState(dh, &origDCB);
492 return diag_iseterr(DIAG_ERR_GENERAL);
493 }
494 //get approx starting time. I think this is the closest we can
495 //get to the actual time the byte gets sent since we call FFB
496 //right after.
497 QueryPerformanceCounter(&qpc1);
498
499 if (!FlushFileBuffers(dh)) {
500 fprintf(stderr, FLFMT "FFB error, %s\n", FL, diag_os_geterr(0));
501 SetCommState(dh, &origDCB);
502 return diag_iseterr(DIAG_ERR_GENERAL);
503 }
504
505
506 /*
507 * And read back the single byte echo, which shows TX completes
508 */
509 xferd = diag_tty_read(wti, &cbuf, 1, ms + 20);
510
511 //we'll usually have a few ms left to wait; we'll use this
512 //to restore the port settings
513
514 if (!SetCommState(dh, &origDCB)) {
515 fprintf(stderr, FLFMT "tty_fastbreak: could not restore setting: %s\n", FL, diag_os_geterr(0));
516 return diag_iseterr(DIAG_ERR_GENERAL);
517 }
518
519 //Not getting the echo byte doesn't mean fastbreak has necessarily
520 // failed. But we really should be getting an echo back...
521 if (xferd < 0) {
522 return diag_iseterr(xferd);
523 }
524 if ((xferd == 0) || (cbuf != 0)) {
525 /* Error, EOF or bad echo */
526 fprintf(stderr, FLFMT "Did not get fastbreak echo!\n", FL);
527 return diag_iseterr(DIAG_ERR_GENERAL);
528 }
529
530
531 QueryPerformanceCounter(&qpc2); //get current time,
532 timediff=qpc2.QuadPart-qpc1.QuadPart; //elapsed counts since diag_tty_write
533 counts=(ms*perfo_freq.QuadPart)/1000; //total # of counts for requested tWUP
534 tremain=counts-timediff; //counts remaining
535 if (tremain<=0) {
536 return 0;
537 }
538
539 tremain = ((LONGLONG) tremain*1000)/perfo_freq.QuadPart; //convert to ms; imprecise but that should be OK.
540 diag_os_millisleep((unsigned int) tremain);
541 QueryPerformanceCounter(&qpc3);
542
543 timediff=qpc3.QuadPart-qpc1.QuadPart; //total cycle time.
544 break_error= (long) timediff - counts; //real - requested
545 break_error= (long) (break_error * pf_conv); //convert to us !
546 if (break_error > 1000 || break_error < -1000) {
547 fprintf(stderr, FLFMT "tty_fastbreak: tWUP out of spec by %ldus!\n", FL, break_error);
548 }
549
550
551 return 0;
552} //diag_tty_fastbreak
553
554/* Find valid serial ports.
555 * Adapted from FreeSSM :
556 * https://github.com/Comer352L/FreeSSM
557 */
558
559char **diag_tty_getportlist(int *numports) {
560 HKEY hKey; // handle to registry key
561 DWORD index = 0; // index registry-key: unsigned int (32bit)
562 char ValueName[256] = "";
563 unsigned long szValueName = 256; // variable that specifies the size (in characters, including the terminating null char) of the buffer pointed to by the "ValueName" parameter.
564 unsigned char Data[256] = ""; // buffer that receives the data for the value entry. This parameter can be NULL if the data is not required
565 unsigned long szData = 256; // variable that specifies the size, in bytes, of the buffer pointed to by the lpData parameter.
566 long cv;
567 HANDLE hCom_t = NULL;
568
569 char **portlist=NULL;
570 int elems=0; //temp number of ports found
571
572 assert(numports != NULL);
573 *numports = 0;
574
575 // OPEN REGISTRY-KEY AND BROWSE ENTRYS:
576 cv = RegOpenKeyExA(HKEY_LOCAL_MACHINE, "HARDWARE\\DEVICEMAP\\SERIALCOMM", 0, KEY_READ, &hKey);
577 if (cv == ERROR_SUCCESS) {
578 while ((RegEnumValueA(hKey, index, ValueName, &szValueName, NULL, NULL,Data,&szData)) == ERROR_SUCCESS) {
579 if (!strncmp((char *)Data,"COM",3)) {
580 // CHECK IF PORT IS AVAILABLE (not in use):
581 char NTdevName[30] = "\\\\.\\"; // => "\\.\"
582 strncpy(NTdevName+4, (char *)Data, 25);
583 /* NOTE: MS-DOS device names ("COMx") are not reliable if x is > 9 !!!
584 => device can not be opened (error 2 "The system cannot find the file specified.")
585 Using NT device names instead ("\\.\COMx") which work in all cases.
586 */
587 hCom_t = CreateFileA(NTdevName, // device name of the port
588 GENERIC_READ | GENERIC_WRITE, // read/write access
589 0, // must be opened with exclusive-access
590 NULL, // default security attributes
591 OPEN_EXISTING, // must use OPEN_EXISTING
592 0, // not overlapped I/O
593 NULL // must be NULL for comm devices
594 );
595 if (hCom_t != INVALID_HANDLE_VALUE) {
596 CloseHandle(hCom_t);
597 char **templist = strlist_add(portlist, NTdevName, elems);
598 if (!templist) {
599 strlist_free(portlist, elems);
600 return diag_pseterr(DIAG_ERR_NOMEM);
601 }
602 portlist = templist;
603 elems++;
604 }
605 }
606 szValueName = 256; // because RegEnumValue has changed value
607 szData = 256; // because RegEnumValue has changed value
608 index++;
609 } //while
610 //std::sort(portlist.begin(), portlist.end()); // quicksort from <algorithm>
611 (void) RegCloseKey(hKey);
612 }
613
614 *numports = elems;
615 return portlist;
616}
617