1 /*
2 * PROJECT: Ports installer library
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: dll\win32\msports\serial.c
5 * PURPOSE: Serial Port property functions
6 * COPYRIGHT: Copyright 2011 Eric Kohl
7 */
8
9 #include "precomp.h"
10
11
12 typedef struct _PORT_DATA
13 {
14 HDEVINFO DeviceInfoSet;
15 PSP_DEVINFO_DATA DeviceInfoData;
16
17 WCHAR szPortName[16];
18 INT nBaudRateIndex;
19 INT nParityIndex;
20 INT nDataBitsIndex;
21 INT nStopBitsIndex;
22 INT nFlowControlIndex;
23
24 BOOL bChanged;
25 } PORT_DATA, *PPORT_DATA;
26
27 #define DEFAULT_BAUD_RATE_INDEX 11
28 #define DEFAULT_DATA_BITS_INDEX 4
29 #define DEFAULT_PARITY_INDEX 2
30 #define DEFAULT_STOP_BITS_INDEX 0
31 #define DEFAULT_FLOW_CONTROL_INDEX 2
32
33 DWORD BaudRates[] = {75, 110, 134, 150, 300, 600, 1200, 1800, 2400, 4800,
34 7200, 9600, 14400, 19200, 38400, 57600, 115200, 128000};
35 PWSTR Paritys[] = {L"e", L"o", L"n", L"m", L"s"};
36 PWSTR DataBits[] = {L"4", L"5", L"6", L"7", L"8"};
37 PWSTR StopBits[] = {L"1", L"1.5", L"2"};
38 PWSTR FlowControls[] = {L"x", L"p"};
39
40
41 static
42 VOID
FillComboBox(HWND hwnd,PWSTR szBuffer)43 FillComboBox(
44 HWND hwnd,
45 PWSTR szBuffer)
46 {
47 PWSTR pStart, pEnd;
48
49 pStart = szBuffer;
50 for (;;)
51 {
52 pEnd = wcschr(pStart, L',');
53 if (pEnd != NULL)
54 *pEnd = UNICODE_NULL;
55
56 ComboBox_AddString(hwnd, pStart);
57
58 if (pEnd == NULL)
59 break;
60
61 pStart = pEnd + 1;
62 }
63 }
64
65
66 static
67 VOID
ReadPortSettings(PPORT_DATA pPortData)68 ReadPortSettings(
69 PPORT_DATA pPortData)
70 {
71 WCHAR szPortData[32];
72 WCHAR szParity[4];
73 WCHAR szDataBits[4];
74 WCHAR szStopBits[4];
75 WCHAR szFlowControl[4];
76 DWORD dwType, dwSize;
77 DWORD dwBaudRate = 0;
78 HKEY hKey;
79 INT n, i;
80 LONG lError;
81
82 TRACE("ReadPortSettings(%p)\n", pPortData);
83
84 pPortData->nBaudRateIndex = DEFAULT_BAUD_RATE_INDEX; /* 9600 */
85 pPortData->nParityIndex = DEFAULT_PARITY_INDEX; /* None */
86 pPortData->nDataBitsIndex = DEFAULT_DATA_BITS_INDEX; /* 8 Data Bits */
87 pPortData->nStopBitsIndex = DEFAULT_STOP_BITS_INDEX; /* 1 Stop Bit */
88 pPortData->nFlowControlIndex = DEFAULT_FLOW_CONTROL_INDEX; /* None */
89 pPortData->bChanged = FALSE;
90
91 hKey = SetupDiOpenDevRegKey(pPortData->DeviceInfoSet,
92 pPortData->DeviceInfoData,
93 DICS_FLAG_GLOBAL,
94 0,
95 DIREG_DEV,
96 KEY_READ);
97 if (hKey == INVALID_HANDLE_VALUE)
98 {
99 ERR("SetupDiOpenDevRegKey() failed\n");
100 return;
101 }
102
103 dwSize = sizeof(pPortData->szPortName);
104 lError = RegQueryValueExW(hKey,
105 L"PortName",
106 NULL,
107 NULL,
108 (PBYTE)pPortData->szPortName,
109 &dwSize);
110 RegCloseKey(hKey);
111
112 if (lError != ERROR_SUCCESS)
113 {
114 ERR("RegQueryValueExW failed (Error %lu)\n", lError);
115 return;
116 }
117
118 wcscat(pPortData->szPortName, L":");
119 TRACE("PortName: '%S'\n", pPortData->szPortName);
120
121 lError = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
122 L"Software\\Microsoft\\Windows NT\\CurrentVersion\\Ports",
123 0,
124 KEY_READ,
125 &hKey);
126 if (lError != ERROR_SUCCESS)
127 {
128 ERR("RegOpenKeyExW failed (Error %lu)\n", lError);
129 return;
130 }
131
132 dwSize = sizeof(szPortData);
133 lError = RegQueryValueExW(hKey,
134 pPortData->szPortName,
135 NULL,
136 &dwType,
137 (LPBYTE)szPortData,
138 &dwSize);
139 RegCloseKey(hKey);
140
141 if (lError != ERROR_SUCCESS)
142 {
143 ERR("RegQueryValueExW failed (Error %lu)\n", lError);
144 return;
145 }
146
147 if ((dwType != REG_SZ) || (dwSize > sizeof(szPortData)))
148 {
149 ERR("Wrong type or size\n");
150 return;
151 }
152
153 TRACE("szPortData: '%S'\n", szPortData);
154
155 /* Replace commas by spaces */
156 for (i = 0; i < wcslen(szPortData); i++)
157 {
158 if (szPortData[i] == L',')
159 szPortData[i] = L' ';
160 }
161
162 TRACE("szPortData: '%S'\n", szPortData);
163
164 /* Parse the port settings */
165 n = swscanf(szPortData,
166 L"%lu %3s %3s %3s %3s",
167 &dwBaudRate,
168 &szParity,
169 &szDataBits,
170 &szStopBits,
171 &szFlowControl);
172
173 TRACE("dwBaudRate: %lu\n", dwBaudRate);
174 TRACE("szParity: '%S'\n", szParity);
175 TRACE("szDataBits: '%S'\n", szDataBits);
176 TRACE("szStopBits: '%S'\n", szStopBits);
177 TRACE("szFlowControl: '%S'\n", szFlowControl);
178
179 if (n > 0)
180 {
181 for (i = 0; i < ARRAYSIZE(BaudRates); i++)
182 {
183 if (dwBaudRate == BaudRates[i])
184 pPortData->nBaudRateIndex = i;
185 }
186 }
187
188 if (n > 1)
189 {
190 for (i = 0; i < ARRAYSIZE(Paritys); i++)
191 {
192 if (_wcsicmp(szParity, Paritys[i]) == 0)
193 pPortData->nParityIndex = i;
194 }
195 }
196
197 if (n > 2)
198 {
199 for (i = 0; i < ARRAYSIZE(DataBits); i++)
200 {
201 if (_wcsicmp(szDataBits, DataBits[i]) == 0)
202 pPortData->nDataBitsIndex = i;
203 }
204 }
205
206 if (n > 3)
207 {
208 for (i = 0; i < ARRAYSIZE(StopBits); i++)
209 {
210 if (_wcsicmp(szStopBits, StopBits[i]) == 0)
211 pPortData->nStopBitsIndex = i;
212 }
213 }
214
215 if (n > 4)
216 {
217 for (i = 0; i < ARRAYSIZE(FlowControls); i++)
218 {
219 if (_wcsicmp(szFlowControl, FlowControls[i]) == 0)
220 pPortData->nFlowControlIndex = i;
221 }
222 }
223 }
224
225
226 static
227 VOID
WritePortSettings(HWND hwnd,PPORT_DATA pPortData)228 WritePortSettings(
229 HWND hwnd,
230 PPORT_DATA pPortData)
231 {
232 SP_PROPCHANGE_PARAMS PropChangeParams;
233 WCHAR szPortData[32];
234 HWND hwndControl;
235 INT nBaudRateIndex;
236 INT nDataBitsIndex;
237 INT nParityIndex;
238 INT nStopBitsIndex;
239 INT nFlowControlIndex;
240 HKEY hKey;
241 LONG lError;
242
243 TRACE("WritePortSettings(%p)\n", pPortData);
244
245 if (pPortData->bChanged == FALSE)
246 {
247 TRACE("Nothing changed. Done!\n");
248 return;
249 }
250
251 nBaudRateIndex = pPortData->nBaudRateIndex;
252 nDataBitsIndex = pPortData->nDataBitsIndex;
253 nParityIndex = pPortData->nParityIndex;
254 nStopBitsIndex = pPortData->nStopBitsIndex;
255 nFlowControlIndex = pPortData->nFlowControlIndex;
256
257 hwndControl = GetDlgItem(hwnd, IDC_SERIAL_BITSPERSECOND);
258 if (hwndControl)
259 {
260 nBaudRateIndex = ComboBox_GetCurSel(hwndControl);
261 }
262
263 hwndControl = GetDlgItem(hwnd, IDC_SERIAL_DATABITS);
264 if (hwndControl)
265 {
266 nDataBitsIndex = ComboBox_GetCurSel(hwndControl);
267 }
268
269 hwndControl = GetDlgItem(hwnd, IDC_SERIAL_PARITY);
270 if (hwndControl)
271 {
272 nParityIndex = ComboBox_GetCurSel(hwndControl);
273 }
274
275 hwndControl = GetDlgItem(hwnd, IDC_SERIAL_STOPBITS);
276 if (hwndControl)
277 {
278 nStopBitsIndex = ComboBox_GetCurSel(hwndControl);
279 }
280
281 hwndControl = GetDlgItem(hwnd, IDC_SERIAL_FLOWCONTROL);
282 if (hwndControl)
283 {
284 nFlowControlIndex = ComboBox_GetCurSel(hwndControl);
285 }
286
287 swprintf(szPortData,
288 L"%lu,%s,%s,%s",
289 BaudRates[nBaudRateIndex],
290 Paritys[nParityIndex],
291 DataBits[nDataBitsIndex],
292 StopBits[nStopBitsIndex]);
293 if (nFlowControlIndex < 2)
294 {
295 wcscat(szPortData, L",");
296 wcscat(szPortData, FlowControls[nFlowControlIndex]);
297 }
298
299 TRACE("szPortData: '%S'\n", szPortData);
300
301 lError = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
302 L"Software\\Microsoft\\Windows NT\\CurrentVersion\\Ports",
303 0,
304 KEY_WRITE,
305 &hKey);
306 if (lError != ERROR_SUCCESS)
307 {
308 ERR("RegOpenKeyExW failed (Error %lu)\n", lError);
309 return;
310 }
311
312 lError = RegSetValueExW(hKey,
313 pPortData->szPortName,
314 0,
315 REG_SZ,
316 (LPBYTE)szPortData,
317 (wcslen(szPortData) + 1) * sizeof(WCHAR));
318
319 RegCloseKey(hKey);
320
321 if (lError != ERROR_SUCCESS)
322 {
323 ERR("RegSetValueExW failed (Error %lu)\n", lError);
324 return;
325 }
326
327 /* Notify the system */
328 PostMessageW(HWND_BROADCAST,
329 WM_WININICHANGE,
330 0,
331 (LPARAM)pPortData->szPortName);
332
333 /* Notify the installer (and device) */
334 PropChangeParams.ClassInstallHeader.cbSize = sizeof(SP_CLASSINSTALL_HEADER);
335 PropChangeParams.ClassInstallHeader.InstallFunction = DIF_PROPERTYCHANGE;
336 PropChangeParams.Scope = DICS_FLAG_GLOBAL;
337 PropChangeParams.StateChange = DICS_PROPCHANGE;
338
339 SetupDiSetClassInstallParams(pPortData->DeviceInfoSet,
340 pPortData->DeviceInfoData,
341 (PSP_CLASSINSTALL_HEADER)&PropChangeParams,
342 sizeof(SP_PROPCHANGE_PARAMS));
343
344 SetupDiCallClassInstaller(DIF_PROPERTYCHANGE,
345 pPortData->DeviceInfoSet,
346 pPortData->DeviceInfoData);
347
348 TRACE("Done!\n");
349 }
350
351
352 static
353 BOOL
OnInitDialog(HWND hwnd,WPARAM wParam,LPARAM lParam)354 OnInitDialog(
355 HWND hwnd,
356 WPARAM wParam,
357 LPARAM lParam)
358 {
359 PPORT_DATA pPortData;
360 WCHAR szBuffer[256];
361 UINT i;
362 HWND hwndControl;
363
364 TRACE("OnInitDialog()\n");
365
366 pPortData = (PPORT_DATA)((LPPROPSHEETPAGEW)lParam)->lParam;
367 if (pPortData == NULL)
368 {
369 ERR("pPortData is NULL\n");
370 return FALSE;
371 }
372
373 SetWindowLongPtr(hwnd, DWLP_USER, (LONG_PTR)pPortData);
374
375 /* Read and parse the port settings */
376 ReadPortSettings(pPortData);
377
378 /* Fill the 'Bits per second' combobox */
379 hwndControl = GetDlgItem(hwnd, IDC_SERIAL_BITSPERSECOND);
380 if (hwndControl)
381 {
382 for (i = 0; i < ARRAYSIZE(BaudRates); i++)
383 {
384 _ultow(BaudRates[i], szBuffer, 10);
385 ComboBox_AddString(hwndControl, szBuffer);
386 }
387
388 ComboBox_SetCurSel(hwndControl, pPortData->nBaudRateIndex);
389 }
390
391 /* Fill the 'Data bits' combobox */
392 hwndControl = GetDlgItem(hwnd, IDC_SERIAL_DATABITS);
393 if (hwndControl)
394 {
395 for (i = 0; i < ARRAYSIZE(DataBits); i++)
396 {
397 ComboBox_AddString(hwndControl, DataBits[i]);
398 }
399
400 ComboBox_SetCurSel(hwndControl, pPortData->nDataBitsIndex);
401 }
402
403 /* Fill the 'Parity' combobox */
404 LoadStringW(hInstance, IDS_PARITY, szBuffer, ARRAYSIZE(szBuffer));
405
406 hwndControl = GetDlgItem(hwnd, IDC_SERIAL_PARITY);
407 if (hwndControl)
408 {
409 FillComboBox(hwndControl, szBuffer);
410 ComboBox_SetCurSel(hwndControl, pPortData->nParityIndex);
411 }
412
413 /* Fill the 'Stop bits' combobox */
414 LoadStringW(hInstance, IDS_STOPBITS, szBuffer, ARRAYSIZE(szBuffer));
415
416 hwndControl = GetDlgItem(hwnd, IDC_SERIAL_STOPBITS);
417 if (hwndControl)
418 {
419 FillComboBox(hwndControl, szBuffer);
420 ComboBox_SetCurSel(hwndControl, pPortData->nStopBitsIndex);
421 }
422
423 /* Fill the 'Flow control' combobox */
424 LoadStringW(hInstance, IDS_FLOWCONTROL, szBuffer, ARRAYSIZE(szBuffer));
425
426 hwndControl = GetDlgItem(hwnd, IDC_SERIAL_FLOWCONTROL);
427 if (hwndControl)
428 {
429 FillComboBox(hwndControl, szBuffer);
430 ComboBox_SetCurSel(hwndControl, pPortData->nFlowControlIndex);
431 }
432
433 /* Disable the 'Advanced' button */
434 hwndControl = GetDlgItem(hwnd, IDC_SERIAL_ADVANCED);
435 if (hwndControl)
436 EnableWindow(hwndControl, FALSE);
437
438 return TRUE;
439 }
440
441
442 static
443 VOID
RestoreDefaultValues(HWND hwnd,PPORT_DATA pPortData)444 RestoreDefaultValues(
445 HWND hwnd,
446 PPORT_DATA pPortData)
447 {
448 HWND hwndControl;
449
450 /* Reset the 'Bits per second' combobox */
451 hwndControl = GetDlgItem(hwnd, IDC_SERIAL_BITSPERSECOND);
452 if (hwndControl)
453 {
454 ComboBox_SetCurSel(hwndControl, DEFAULT_BAUD_RATE_INDEX);
455 }
456
457 /* Reset the 'Data bits' combobox */
458 hwndControl = GetDlgItem(hwnd, IDC_SERIAL_DATABITS);
459 if (hwndControl)
460 {
461 ComboBox_SetCurSel(hwndControl, DEFAULT_DATA_BITS_INDEX);
462 }
463
464 /* Reset the 'Parity' combobox */
465 hwndControl = GetDlgItem(hwnd, IDC_SERIAL_PARITY);
466 if (hwndControl)
467 {
468 ComboBox_SetCurSel(hwndControl, DEFAULT_PARITY_INDEX);
469 }
470
471 /* Reset the 'Stop bits' combobox */
472 hwndControl = GetDlgItem(hwnd, IDC_SERIAL_STOPBITS);
473 if (hwndControl)
474 {
475 ComboBox_SetCurSel(hwndControl, DEFAULT_STOP_BITS_INDEX);
476 }
477
478 /* Reset the 'Flow control' combobox */
479 hwndControl = GetDlgItem(hwnd, IDC_SERIAL_FLOWCONTROL);
480 if (hwndControl)
481 {
482 ComboBox_SetCurSel(hwndControl, DEFAULT_FLOW_CONTROL_INDEX);
483 }
484
485 pPortData->bChanged = TRUE;
486 }
487
488
489 static
490 VOID
OnCommand(HWND hwnd,WPARAM wParam,LPARAM lParam)491 OnCommand(
492 HWND hwnd,
493 WPARAM wParam,
494 LPARAM lParam)
495 {
496 PPORT_DATA pPortData;
497
498 TRACE("OnCommand()\n");
499
500 pPortData = (PPORT_DATA)GetWindowLongPtr(hwnd, DWLP_USER);
501 if (pPortData == NULL)
502 {
503 ERR("pPortData is NULL\n");
504 return;
505 }
506
507 switch (LOWORD(wParam))
508 {
509 case IDC_SERIAL_BITSPERSECOND:
510 case IDC_SERIAL_DATABITS:
511 case IDC_SERIAL_PARITY:
512 case IDC_SERIAL_STOPBITS:
513 case IDC_SERIAL_FLOWCONTROL:
514 if (HIWORD(wParam) == CBN_SELCHANGE ||
515 HIWORD(wParam) == CBN_EDITCHANGE)
516 {
517 pPortData->bChanged = TRUE;
518 }
519 break;
520
521 // case IDC_SERIAL_ADVANCED:
522
523 case IDC_SERIAL_RESTORE:
524 RestoreDefaultValues(hwnd, pPortData);
525 break;
526
527 default:
528 break;
529 }
530 }
531
532
533 static
534 VOID
OnNotify(HWND hwnd,WPARAM wParam,LPARAM lParam)535 OnNotify(
536 HWND hwnd,
537 WPARAM wParam,
538 LPARAM lParam)
539 {
540 PPORT_DATA pPortData;
541
542 TRACE("OnCommand()\n");
543
544 pPortData = (PPORT_DATA)GetWindowLongPtr(hwnd, DWLP_USER);
545 if (pPortData == NULL)
546 {
547 ERR("pPortData is NULL\n");
548 return;
549 }
550
551 if (((LPNMHDR)lParam)->code == (UINT)PSN_APPLY)
552 {
553 FIXME("PSN_APPLY!\n");
554 WritePortSettings(hwnd, pPortData);
555 }
556 }
557
558
559 static
560 VOID
OnDestroy(HWND hwnd)561 OnDestroy(
562 HWND hwnd)
563 {
564 PPORT_DATA pPortData;
565
566 TRACE("OnDestroy()\n");
567
568 pPortData = (PPORT_DATA)GetWindowLongPtr(hwnd, DWLP_USER);
569 if (pPortData == NULL)
570 {
571 ERR("pPortData is NULL\n");
572 return;
573 }
574
575 HeapFree(GetProcessHeap(), 0, pPortData);
576 SetWindowLongPtr(hwnd, DWLP_USER, (LONG_PTR)NULL);
577 }
578
579
580 static
581 INT_PTR
582 CALLBACK
SerialSettingsDlgProc(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam)583 SerialSettingsDlgProc(HWND hwnd,
584 UINT uMsg,
585 WPARAM wParam,
586 LPARAM lParam)
587 {
588 TRACE("SerialSettingsDlgProc()\n");
589
590 switch (uMsg)
591 {
592 case WM_INITDIALOG:
593 return OnInitDialog(hwnd, wParam, lParam);
594
595 case WM_COMMAND:
596 OnCommand(hwnd, wParam, lParam);
597 break;
598
599 case WM_NOTIFY:
600 OnNotify(hwnd, wParam, lParam);
601 break;
602
603 case WM_DESTROY:
604 OnDestroy(hwnd);
605 break;
606 }
607
608 return FALSE;
609 }
610
611
612 BOOL
613 WINAPI
SerialPortPropPageProvider(PSP_PROPSHEETPAGE_REQUEST lpPropSheetPageRequest,LPFNADDPROPSHEETPAGE lpfnAddPropSheetPageProc,LPARAM lParam)614 SerialPortPropPageProvider(
615 PSP_PROPSHEETPAGE_REQUEST lpPropSheetPageRequest,
616 LPFNADDPROPSHEETPAGE lpfnAddPropSheetPageProc,
617 LPARAM lParam)
618 {
619 PROPSHEETPAGEW PropSheetPage;
620 HPROPSHEETPAGE hPropSheetPage;
621 PPORT_DATA pPortData;
622
623 TRACE("SerialPortPropPageProvider(%p %p %lx)\n",
624 lpPropSheetPageRequest, lpfnAddPropSheetPageProc, lParam);
625
626 pPortData = HeapAlloc(GetProcessHeap(),
627 HEAP_ZERO_MEMORY,
628 sizeof(PORT_DATA));
629 if (pPortData == NULL)
630 {
631 ERR("Port data allocation failed!\n");
632 return FALSE;
633 }
634
635 pPortData->DeviceInfoSet = lpPropSheetPageRequest->DeviceInfoSet;
636 pPortData->DeviceInfoData = lpPropSheetPageRequest->DeviceInfoData;
637
638 if (lpPropSheetPageRequest->PageRequested == SPPSR_ENUM_ADV_DEVICE_PROPERTIES)
639 {
640 TRACE("SPPSR_ENUM_ADV_DEVICE_PROPERTIES\n");
641
642 PropSheetPage.dwSize = sizeof(PROPSHEETPAGEW);
643 PropSheetPage.dwFlags = 0;
644 PropSheetPage.hInstance = hInstance;
645 PropSheetPage.pszTemplate = MAKEINTRESOURCE(IDD_SERIALSETTINGS);
646 PropSheetPage.pfnDlgProc = SerialSettingsDlgProc;
647 PropSheetPage.lParam = (LPARAM)pPortData;
648 PropSheetPage.pfnCallback = NULL;
649
650 hPropSheetPage = CreatePropertySheetPageW(&PropSheetPage);
651 if (hPropSheetPage == NULL)
652 {
653 ERR("CreatePropertySheetPageW() failed!\n");
654 return FALSE;
655 }
656
657 if (!(*lpfnAddPropSheetPageProc)(hPropSheetPage, lParam))
658 {
659 ERR("lpfnAddPropSheetPageProc() failed!\n");
660 DestroyPropertySheetPage(hPropSheetPage);
661 return FALSE;
662 }
663 }
664
665 TRACE("Done!\n");
666
667 return TRUE;
668 }
669
670 /* EOF */
671