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