[MSPORTS]
[reactos.git] / reactos / dll / win32 / msports / classinst.c
1 /*
2 * PROJECT: ReactOS system libraries
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: dlls\win32\msports\classinst.c
5 * PURPOSE: Ports class installer
6 * PROGRAMMERS: Copyright 2011 Eric Kohl
7 */
8
9 #include "precomp.h"
10
11 typedef enum _PORT_TYPE
12 {
13 UnknownPort,
14 ParallelPort,
15 SerialPort
16 } PORT_TYPE;
17
18 LPWSTR pszCom = L"COM";
19 LPWSTR pszLpt = L"LPT";
20
21
22 BOOL
23 GetBootResourceList(HDEVINFO DeviceInfoSet,
24 PSP_DEVINFO_DATA DeviceInfoData,
25 PCM_RESOURCE_LIST *ppResourceList)
26 {
27 HKEY hDeviceKey = NULL;
28 HKEY hConfigKey = NULL;
29 LPBYTE lpBuffer = NULL;
30 DWORD dwDataSize;
31 LONG lError;
32 BOOL ret = FALSE;
33
34 *ppResourceList = NULL;
35
36 hDeviceKey = SetupDiCreateDevRegKeyW(DeviceInfoSet,
37 DeviceInfoData,
38 DICS_FLAG_GLOBAL,
39 0,
40 DIREG_DEV,
41 NULL,
42 NULL);
43 if (!hDeviceKey)
44 return FALSE;
45
46 lError = RegOpenKeyExW(hDeviceKey,
47 L"LogConf",
48 0,
49 KEY_QUERY_VALUE,
50 &hConfigKey);
51 if (lError != ERROR_SUCCESS)
52 goto done;
53
54 /* Get the configuration data size */
55 lError = RegQueryValueExW(hConfigKey,
56 L"BootConfig",
57 NULL,
58 NULL,
59 NULL,
60 &dwDataSize);
61 if (lError != ERROR_SUCCESS)
62 goto done;
63
64 /* Allocate the buffer */
65 lpBuffer = HeapAlloc(GetProcessHeap(), 0, dwDataSize);
66 if (lpBuffer == NULL)
67 goto done;
68
69 /* Retrieve the configuration data */
70 lError = RegQueryValueExW(hConfigKey,
71 L"BootConfig",
72 NULL,
73 NULL,
74 (LPBYTE)lpBuffer,
75 &dwDataSize);
76 if (lError == ERROR_SUCCESS)
77 ret = TRUE;
78
79 done:
80 if (ret == FALSE && lpBuffer != NULL)
81 HeapFree(GetProcessHeap(), 0, lpBuffer);
82
83 if (hConfigKey)
84 RegCloseKey(hConfigKey);
85
86 if (hDeviceKey)
87 RegCloseKey(hDeviceKey);
88
89 if (ret == TRUE)
90 *ppResourceList = (PCM_RESOURCE_LIST)lpBuffer;
91
92 return ret;
93 }
94
95
96 DWORD
97 GetSerialPortNumber(IN HDEVINFO DeviceInfoSet,
98 IN PSP_DEVINFO_DATA DeviceInfoData)
99 {
100 PCM_RESOURCE_LIST lpResourceList = NULL;
101 PCM_PARTIAL_RESOURCE_DESCRIPTOR lpResDes;
102 ULONG i;
103 DWORD dwBaseAddress = 0;
104 DWORD dwPortNumber = 0;
105
106 TRACE("GetSerialPortNumber(%p, %p)\n",
107 DeviceInfoSet, DeviceInfoData);
108
109 if (GetBootResourceList(DeviceInfoSet,
110 DeviceInfoData,
111 &lpResourceList))
112 {
113 TRACE("Full resource descriptors: %ul\n", lpResourceList->Count);
114 if (lpResourceList->Count > 0)
115 {
116 TRACE("Partial resource descriptors: %ul\n", lpResourceList->List[0].PartialResourceList.Count);
117
118 for (i = 0; i < lpResourceList->List[0].PartialResourceList.Count; i++)
119 {
120 lpResDes = &lpResourceList->List[0].PartialResourceList.PartialDescriptors[i];
121 TRACE("Type: %u\n", lpResDes->Type);
122
123 switch (lpResDes->Type)
124 {
125 case CmResourceTypePort:
126 TRACE("Port: Start: %I64x Length: %lu\n",
127 lpResDes->u.Port.Start.QuadPart,
128 lpResDes->u.Port.Length);
129 if ((lpResDes->u.Port.Start.HighPart == 0) && (dwBaseAddress == 0))
130 dwBaseAddress = (DWORD)lpResDes->u.Port.Start.LowPart;
131 break;
132
133 case CmResourceTypeInterrupt:
134 TRACE("Interrupt: Level: %lu Vector: %lu\n",
135 lpResDes->u.Interrupt.Level,
136 lpResDes->u.Interrupt.Vector);
137 break;
138 }
139 }
140 }
141
142 HeapFree(GetProcessHeap(), 0, lpResourceList);
143 }
144
145 switch (dwBaseAddress)
146 {
147 case 0x3f8:
148 dwPortNumber = 1;
149 break;
150
151 case 0x2f8:
152 dwPortNumber = 2;
153 break;
154
155 case 0x3e8:
156 dwPortNumber = 3;
157 break;
158
159 case 0x2e8:
160 dwPortNumber = 4;
161 break;
162 }
163
164 return dwPortNumber;
165 }
166
167
168 DWORD
169 GetParallelPortNumber(IN HDEVINFO DeviceInfoSet,
170 IN PSP_DEVINFO_DATA DeviceInfoData)
171 {
172 PCM_RESOURCE_LIST lpResourceList = NULL;
173 PCM_PARTIAL_RESOURCE_DESCRIPTOR lpResDes;
174 ULONG i;
175 DWORD dwBaseAddress = 0;
176 DWORD dwPortNumber = 0;
177
178 TRACE("GetParallelPortNumber(%p, %p)\n",
179 DeviceInfoSet, DeviceInfoData);
180
181 if (GetBootResourceList(DeviceInfoSet,
182 DeviceInfoData,
183 &lpResourceList))
184 {
185 TRACE("Full resource descriptors: %ul\n", lpResourceList->Count);
186 if (lpResourceList->Count > 0)
187 {
188 TRACE("Partial resource descriptors: %ul\n", lpResourceList->List[0].PartialResourceList.Count);
189
190 for (i = 0; i < lpResourceList->List[0].PartialResourceList.Count; i++)
191 {
192 lpResDes = &lpResourceList->List[0].PartialResourceList.PartialDescriptors[i];
193 TRACE("Type: %u\n", lpResDes->Type);
194
195 switch (lpResDes->Type)
196 {
197 case CmResourceTypePort:
198 TRACE("Port: Start: %I64x Length: %lu\n",
199 lpResDes->u.Port.Start.QuadPart,
200 lpResDes->u.Port.Length);
201 if ((lpResDes->u.Port.Start.HighPart == 0) && (dwBaseAddress == 0))
202 dwBaseAddress = (DWORD)lpResDes->u.Port.Start.LowPart;
203 break;
204
205 case CmResourceTypeInterrupt:
206 TRACE("Interrupt: Level: %lu Vector: %lu\n",
207 lpResDes->u.Interrupt.Level,
208 lpResDes->u.Interrupt.Vector);
209 break;
210 }
211
212 }
213
214 }
215
216 HeapFree(GetProcessHeap(), 0, lpResourceList);
217 }
218
219 switch (dwBaseAddress)
220 {
221 case 0x378:
222 dwPortNumber = 1;
223 break;
224
225 case 0x278:
226 dwPortNumber = 2;
227 break;
228 }
229
230 return dwPortNumber;
231 }
232
233
234 static DWORD
235 InstallSerialPort(IN HDEVINFO DeviceInfoSet,
236 IN PSP_DEVINFO_DATA DeviceInfoData)
237 {
238 WCHAR szDeviceDescription[256];
239 WCHAR szFriendlyName[256];
240 WCHAR szPortName[8];
241 DWORD dwPortNumber = 0;
242 DWORD dwSize;
243 HCOMDB hComDB = HCOMDB_INVALID_HANDLE_VALUE;
244 HKEY hKey;
245 LONG lError;
246
247 TRACE("InstallSerialPort(%p, %p)\n",
248 DeviceInfoSet, DeviceInfoData);
249
250 /* Open the com port database */
251 ComDBOpen(&hComDB);
252
253 /* Try to read the 'PortName' value and determine the port number */
254 hKey = SetupDiCreateDevRegKeyW(DeviceInfoSet,
255 DeviceInfoData,
256 DICS_FLAG_GLOBAL,
257 0,
258 DIREG_DEV,
259 NULL,
260 NULL);
261 if (hKey != INVALID_HANDLE_VALUE)
262 {
263 dwSize = sizeof(szPortName);
264 lError = RegQueryValueEx(hKey,
265 L"PortName",
266 NULL,
267 NULL,
268 (PBYTE)szPortName,
269 &dwSize);
270 if (lError == ERROR_SUCCESS)
271 {
272 if (_wcsnicmp(szPortName, pszCom, wcslen(pszCom)) == 0)
273 {
274 dwPortNumber = _wtoi(szPortName + wcslen(pszCom));
275 TRACE("COM port number found: %lu\n", dwPortNumber);
276 }
277 }
278
279 RegCloseKey(hKey);
280 }
281
282 /* Determine the port number from its resources ... */
283 if (dwPortNumber == 0)
284 dwPortNumber = GetSerialPortNumber(DeviceInfoSet,
285 DeviceInfoData);
286
287 if (dwPortNumber != 0)
288 {
289 /* ... and claim the port number in the database */
290 ComDBClaimPort(hComDB,
291 dwPortNumber,
292 FALSE,
293 NULL);
294 }
295 else
296 {
297 /* ... or claim the next free port number */
298 ComDBClaimNextFreePort(hComDB,
299 &dwPortNumber);
300 }
301
302 /* Build the name of the port device */
303 swprintf(szPortName, L"%s%u", pszCom, dwPortNumber);
304
305 /* Close the com port database */
306 if (hComDB != HCOMDB_INVALID_HANDLE_VALUE)
307 ComDBClose(hComDB);
308
309 /* Set the 'PortName' value */
310 hKey = SetupDiCreateDevRegKeyW(DeviceInfoSet,
311 DeviceInfoData,
312 DICS_FLAG_GLOBAL,
313 0,
314 DIREG_DEV,
315 NULL,
316 NULL);
317 if (hKey != INVALID_HANDLE_VALUE)
318 {
319 RegSetValueExW(hKey,
320 L"PortName",
321 0,
322 REG_SZ,
323 (LPBYTE)szPortName,
324 (wcslen(szPortName) + 1) * sizeof(WCHAR));
325
326 RegCloseKey(hKey);
327 }
328
329 /* Install the device */
330 if (!SetupDiInstallDevice(DeviceInfoSet,
331 DeviceInfoData))
332 {
333 return GetLastError();
334 }
335
336 /* Get the device description... */
337 if (SetupDiGetDeviceRegistryPropertyW(DeviceInfoSet,
338 DeviceInfoData,
339 SPDRP_DEVICEDESC,
340 NULL,
341 (LPBYTE)szDeviceDescription,
342 256 * sizeof(WCHAR),
343 NULL))
344 {
345 /* ... and use it to build a new friendly name */
346 swprintf(szFriendlyName,
347 L"%s (%s)",
348 szDeviceDescription,
349 szPortName);
350 }
351 else
352 {
353 /* ... or build a generic friendly name */
354 swprintf(szFriendlyName,
355 L"Serial Port (%s)",
356 szPortName);
357 }
358
359 /* Set the friendly name for the device */
360 SetupDiSetDeviceRegistryPropertyW(DeviceInfoSet,
361 DeviceInfoData,
362 SPDRP_FRIENDLYNAME,
363 (LPBYTE)szFriendlyName,
364 (wcslen(szFriendlyName) + 1) * sizeof(WCHAR));
365
366 return ERROR_SUCCESS;
367 }
368
369
370 static DWORD
371 InstallParallelPort(IN HDEVINFO DeviceInfoSet,
372 IN PSP_DEVINFO_DATA DeviceInfoData)
373 {
374 WCHAR szDeviceDescription[256];
375 WCHAR szFriendlyName[256];
376 WCHAR szPortName[8];
377 DWORD dwPortNumber = 0;
378 DWORD dwSize;
379 LONG lError;
380 HKEY hKey;
381
382 TRACE("InstallParallelPort(%p, %p)\n",
383 DeviceInfoSet, DeviceInfoData);
384
385 /* Try to read the 'PortName' value and determine the port number */
386 hKey = SetupDiCreateDevRegKeyW(DeviceInfoSet,
387 DeviceInfoData,
388 DICS_FLAG_GLOBAL,
389 0,
390 DIREG_DEV,
391 NULL,
392 NULL);
393 if (hKey != INVALID_HANDLE_VALUE)
394 {
395 dwSize = sizeof(szPortName);
396 lError = RegQueryValueEx(hKey,
397 L"PortName",
398 NULL,
399 NULL,
400 (PBYTE)szPortName,
401 &dwSize);
402 if (lError == ERROR_SUCCESS)
403 {
404 if (_wcsnicmp(szPortName, pszLpt, wcslen(pszLpt)) == 0)
405 {
406 dwPortNumber = _wtoi(szPortName + wcslen(pszLpt));
407 TRACE("LPT port number found: %lu\n", dwPortNumber);
408 }
409 }
410
411 RegCloseKey(hKey);
412 }
413
414 /* ... try to determine the port number from its resources */
415 if (dwPortNumber == 0)
416 {
417 dwPortNumber = GetParallelPortNumber(DeviceInfoSet,
418 DeviceInfoData);
419 TRACE("GetParallelPortNumber() returned port number: %lu\n", dwPortNumber);
420 }
421
422 if (dwPortNumber == 0)
423 {
424 /* FIXME */
425 FIXME("Got no valid port numer!\n");
426 }
427
428 if (dwPortNumber != 0)
429 {
430 swprintf(szPortName, L"%s%u", pszLpt, dwPortNumber);
431 }
432 else
433 {
434 wcscpy(szPortName, L"LPTx");
435 }
436
437 if (dwPortNumber != 0)
438 {
439 /* Set the 'PortName' value */
440 hKey = SetupDiCreateDevRegKeyW(DeviceInfoSet,
441 DeviceInfoData,
442 DICS_FLAG_GLOBAL,
443 0,
444 DIREG_DEV,
445 NULL,
446 NULL);
447 if (hKey != INVALID_HANDLE_VALUE)
448 {
449 RegSetValueExW(hKey,
450 L"PortName",
451 0,
452 REG_SZ,
453 (LPBYTE)szPortName,
454 (wcslen(szPortName) + 1) * sizeof(WCHAR));
455
456 RegCloseKey(hKey);
457 }
458 }
459
460 /* Install the device */
461 if (!SetupDiInstallDevice(DeviceInfoSet,
462 DeviceInfoData))
463 {
464 return GetLastError();
465 }
466
467 /* Get the device description... */
468 if (SetupDiGetDeviceRegistryPropertyW(DeviceInfoSet,
469 DeviceInfoData,
470 SPDRP_DEVICEDESC,
471 NULL,
472 (LPBYTE)szDeviceDescription,
473 256 * sizeof(WCHAR),
474 NULL))
475 {
476 /* ... and use it to build a new friendly name */
477 swprintf(szFriendlyName,
478 L"%s (%s)",
479 szDeviceDescription,
480 szPortName);
481 }
482 else
483 {
484 /* ... or build a generic friendly name */
485 swprintf(szFriendlyName,
486 L"Parallel Port (%s)",
487 szPortName);
488 }
489
490 TRACE("Friendly name: %S\n", szFriendlyName);
491
492 /* Set the friendly name for the device */
493 SetupDiSetDeviceRegistryPropertyW(DeviceInfoSet,
494 DeviceInfoData,
495 SPDRP_FRIENDLYNAME,
496 (LPBYTE)szFriendlyName,
497 (wcslen(szFriendlyName) + 1) * sizeof(WCHAR));
498
499 return ERROR_SUCCESS;
500 }
501
502
503 VOID
504 InstallDeviceData(IN HDEVINFO DeviceInfoSet,
505 IN PSP_DEVINFO_DATA DeviceInfoData OPTIONAL)
506 {
507 HKEY hKey = NULL;
508 HINF hInf = INVALID_HANDLE_VALUE;
509 SP_DRVINFO_DATA DriverInfoData;
510 PSP_DRVINFO_DETAIL_DATA DriverInfoDetailData;
511 WCHAR InfSectionWithExt[256];
512 BYTE buffer[2048];
513 DWORD dwRequired;
514
515 TRACE("InstallDeviceData()\n");
516
517 hKey = SetupDiCreateDevRegKeyW(DeviceInfoSet,
518 DeviceInfoData,
519 DICS_FLAG_GLOBAL,
520 0,
521 DIREG_DRV,
522 NULL,
523 NULL);
524 if (hKey == NULL)
525 goto done;
526
527 DriverInfoData.cbSize = sizeof(SP_DRVINFO_DATA);
528 if (!SetupDiGetSelectedDriverW(DeviceInfoSet,
529 DeviceInfoData,
530 &DriverInfoData))
531 {
532 goto done;
533 }
534
535 DriverInfoDetailData = (PSP_DRVINFO_DETAIL_DATA)buffer;
536 DriverInfoDetailData->cbSize = sizeof(SP_DRVINFO_DETAIL_DATA);
537 if (!SetupDiGetDriverInfoDetailW(DeviceInfoSet,
538 DeviceInfoData,
539 &DriverInfoData,
540 DriverInfoDetailData,
541 2048,
542 &dwRequired))
543 {
544 if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
545 goto done;
546 }
547
548 TRACE("Inf file name: %S\n", DriverInfoDetailData->InfFileName);
549
550 hInf = SetupOpenInfFileW(DriverInfoDetailData->InfFileName,
551 NULL,
552 INF_STYLE_WIN4,
553 NULL);
554 if (hInf == INVALID_HANDLE_VALUE)
555 goto done;
556
557 TRACE("Section name: %S\n", DriverInfoDetailData->SectionName);
558
559 SetupDiGetActualSectionToInstallW(hInf,
560 DriverInfoDetailData->SectionName,
561 InfSectionWithExt,
562 256,
563 NULL,
564 NULL);
565
566 TRACE("InfSectionWithExt: %S\n", InfSectionWithExt);
567
568 SetupInstallFromInfSectionW(NULL,
569 hInf,
570 InfSectionWithExt,
571 SPINST_REGISTRY,
572 hKey,
573 NULL,
574 0,
575 NULL,
576 NULL,
577 NULL,
578 NULL);
579
580 TRACE("Done\n");
581
582 done:;
583 if (hKey != NULL)
584 RegCloseKey(hKey);
585
586 if (hInf != INVALID_HANDLE_VALUE)
587 SetupCloseInfFile(hInf);
588 }
589
590
591
592 PORT_TYPE
593 GetPortType(IN HDEVINFO DeviceInfoSet,
594 IN PSP_DEVINFO_DATA DeviceInfoData)
595 {
596 HKEY hKey = NULL;
597 DWORD dwSize;
598 DWORD dwType = 0;
599 BYTE bData = 0;
600 PORT_TYPE PortType = UnknownPort;
601 LONG lError;
602
603 TRACE("GetPortType()\n");
604
605 hKey = SetupDiCreateDevRegKeyW(DeviceInfoSet,
606 DeviceInfoData,
607 DICS_FLAG_GLOBAL,
608 0,
609 DIREG_DRV,
610 NULL,
611 NULL);
612 if (hKey == NULL)
613 {
614 goto done;
615 }
616
617 dwSize = sizeof(BYTE);
618 lError = RegQueryValueExW(hKey,
619 L"PortSubClass",
620 NULL,
621 &dwType,
622 &bData,
623 &dwSize);
624
625 TRACE("lError: %ld\n", lError);
626 TRACE("dwSize: %lu\n", dwSize);
627 TRACE("dwType: %lu\n", dwType);
628
629 if (lError == ERROR_SUCCESS &&
630 dwSize == sizeof(BYTE) &&
631 dwType == REG_BINARY)
632 {
633 if (bData == 0)
634 PortType = ParallelPort;
635 else
636 PortType = SerialPort;
637 }
638
639 done:;
640 if (hKey != NULL)
641 RegCloseKey(hKey);
642
643 TRACE("GetPortType() returns %u \n", PortType);
644
645 return PortType;
646 }
647
648
649 static DWORD
650 InstallPort(IN HDEVINFO DeviceInfoSet,
651 IN PSP_DEVINFO_DATA DeviceInfoData)
652 {
653 PORT_TYPE PortType;
654
655 InstallDeviceData(DeviceInfoSet, DeviceInfoData);
656
657 PortType = GetPortType(DeviceInfoSet, DeviceInfoData);
658 switch (PortType)
659 {
660 case ParallelPort:
661 return InstallParallelPort(DeviceInfoSet, DeviceInfoData);
662
663 case SerialPort:
664 return InstallSerialPort(DeviceInfoSet, DeviceInfoData);
665
666 default:
667 return ERROR_DI_DO_DEFAULT;
668 }
669 }
670
671
672 static DWORD
673 RemovePort(IN HDEVINFO DeviceInfoSet,
674 IN PSP_DEVINFO_DATA DeviceInfoData)
675 {
676 PORT_TYPE PortType;
677 HCOMDB hComDB = HCOMDB_INVALID_HANDLE_VALUE;
678 HKEY hKey;
679 LONG lError;
680 DWORD dwPortNumber;
681 DWORD dwPortNameSize;
682 WCHAR szPortName[8];
683
684 /* If we are removing a serial port ... */
685 PortType = GetPortType(DeviceInfoSet, DeviceInfoData);
686 if (PortType == SerialPort)
687 {
688 /* Open the port database */
689 if (ComDBOpen(&hComDB) == ERROR_SUCCESS)
690 {
691 /* Open the device key */
692 hKey = SetupDiOpenDevRegKey(DeviceInfoSet,
693 DeviceInfoData,
694 DICS_FLAG_GLOBAL,
695 0,
696 DIREG_DEV,
697 KEY_READ);
698 if (hKey != INVALID_HANDLE_VALUE)
699 {
700 /* Query the port name */
701 dwPortNameSize = sizeof(szPortName);
702 lError = RegQueryValueEx(hKey,
703 L"PortName",
704 NULL,
705 NULL,
706 (PBYTE)szPortName,
707 &dwPortNameSize);
708
709 /* Close the device key */
710 RegCloseKey(hKey);
711
712 /* If we got a valid port name ...*/
713 if (lError == ERROR_SUCCESS)
714 {
715 /* Get the port number */
716 dwPortNumber = _wtoi(szPortName + wcslen(pszCom));
717
718 /* Release the port */
719 ComDBReleasePort(hComDB, dwPortNumber);
720 }
721 }
722
723 /* Close the port database */
724 ComDBClose(hComDB);
725 }
726 }
727
728 /* Remove the device */
729 if (!SetupDiRemoveDevice(DeviceInfoSet, DeviceInfoData))
730 return GetLastError();
731
732 return ERROR_SUCCESS;
733 }
734
735
736 DWORD
737 WINAPI
738 PortsClassInstaller(IN DI_FUNCTION InstallFunction,
739 IN HDEVINFO DeviceInfoSet,
740 IN PSP_DEVINFO_DATA DeviceInfoData OPTIONAL)
741 {
742 TRACE("PortsClassInstaller(%lu, %p, %p)\n",
743 InstallFunction, DeviceInfoSet, DeviceInfoData);
744
745 switch (InstallFunction)
746 {
747 case DIF_INSTALLDEVICE:
748 return InstallPort(DeviceInfoSet, DeviceInfoData);
749
750 case DIF_REMOVE:
751 return RemovePort(DeviceInfoSet, DeviceInfoData);
752
753 default:
754 TRACE("Install function %u ignored\n", InstallFunction);
755 return ERROR_DI_DO_DEFAULT;
756 }
757 }
758
759 /* EOF */