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