[MSPORTS] Introduce a setting that makes the driver accept resources with an IRQ...
[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 DWORD dwValue;
385 LONG lError;
386 HKEY hKey;
387
388 TRACE("InstallParallelPort(%p, %p)\n",
389 DeviceInfoSet, DeviceInfoData);
390
391 /* Try to read the 'PortName' value and determine the port number */
392 hKey = SetupDiCreateDevRegKeyW(DeviceInfoSet,
393 DeviceInfoData,
394 DICS_FLAG_GLOBAL,
395 0,
396 DIREG_DEV,
397 NULL,
398 NULL);
399 if (hKey != INVALID_HANDLE_VALUE)
400 {
401 dwSize = sizeof(szPortName);
402 lError = RegQueryValueEx(hKey,
403 L"PortName",
404 NULL,
405 NULL,
406 (PBYTE)szPortName,
407 &dwSize);
408 if (lError == ERROR_SUCCESS)
409 {
410 if (_wcsnicmp(szPortName, pszLpt, wcslen(pszLpt)) == 0)
411 {
412 dwPortNumber = _wtoi(szPortName + wcslen(pszLpt));
413 TRACE("LPT port number found: %lu\n", dwPortNumber);
414 }
415 }
416
417 RegCloseKey(hKey);
418 }
419
420 /* ... try to determine the port number from its resources */
421 if (dwPortNumber == 0)
422 {
423 dwPortNumber = GetParallelPortNumber(DeviceInfoSet,
424 DeviceInfoData);
425 TRACE("GetParallelPortNumber() returned port number: %lu\n", dwPortNumber);
426 }
427
428 if (dwPortNumber == 0)
429 {
430 /* FIXME */
431 FIXME("Got no valid port numer!\n");
432 }
433
434 if (dwPortNumber != 0)
435 {
436 swprintf(szPortName, L"%s%u", pszLpt, dwPortNumber);
437 }
438 else
439 {
440 wcscpy(szPortName, L"LPTx");
441 }
442
443 if (dwPortNumber != 0)
444 {
445 /* Set the 'PortName' value */
446 hKey = SetupDiCreateDevRegKeyW(DeviceInfoSet,
447 DeviceInfoData,
448 DICS_FLAG_GLOBAL,
449 0,
450 DIREG_DEV,
451 NULL,
452 NULL);
453 if (hKey != INVALID_HANDLE_VALUE)
454 {
455 RegSetValueExW(hKey,
456 L"PortName",
457 0,
458 REG_SZ,
459 (LPBYTE)szPortName,
460 (wcslen(szPortName) + 1) * sizeof(WCHAR));
461
462 /*
463 * FIXME / HACK:
464 * This is to get the w2k3 parport.sys to work until we have our own.
465 * This setting makes the driver accept resources with an IRQ instead
466 * of only resources without an IRQ.
467 *
468 * We should probably also fix IO manager to actually give devices a
469 * chance to register without an IRQ. CORE-9645
470 */
471 dwValue = 0;
472 RegSetValueExW(hKey,
473 L"FilterResourceMethod",
474 0,
475 REG_DWORD,
476 (LPBYTE)&dwValue,
477 sizeof(dwValue));
478
479 RegCloseKey(hKey);
480 }
481 }
482
483 /* Install the device */
484 if (!SetupDiInstallDevice(DeviceInfoSet,
485 DeviceInfoData))
486 {
487 return GetLastError();
488 }
489
490 /* Get the device description... */
491 if (SetupDiGetDeviceRegistryPropertyW(DeviceInfoSet,
492 DeviceInfoData,
493 SPDRP_DEVICEDESC,
494 NULL,
495 (LPBYTE)szDeviceDescription,
496 256 * sizeof(WCHAR),
497 NULL))
498 {
499 /* ... and use it to build a new friendly name */
500 swprintf(szFriendlyName,
501 L"%s (%s)",
502 szDeviceDescription,
503 szPortName);
504 }
505 else
506 {
507 /* ... or build a generic friendly name */
508 swprintf(szFriendlyName,
509 L"Parallel Port (%s)",
510 szPortName);
511 }
512
513 TRACE("Friendly name: %S\n", szFriendlyName);
514
515 /* Set the friendly name for the device */
516 SetupDiSetDeviceRegistryPropertyW(DeviceInfoSet,
517 DeviceInfoData,
518 SPDRP_FRIENDLYNAME,
519 (LPBYTE)szFriendlyName,
520 (wcslen(szFriendlyName) + 1) * sizeof(WCHAR));
521
522 return ERROR_SUCCESS;
523 }
524
525
526 VOID
527 InstallDeviceData(IN HDEVINFO DeviceInfoSet,
528 IN PSP_DEVINFO_DATA DeviceInfoData OPTIONAL)
529 {
530 HKEY hKey = NULL;
531 HINF hInf = INVALID_HANDLE_VALUE;
532 SP_DRVINFO_DATA DriverInfoData;
533 PSP_DRVINFO_DETAIL_DATA DriverInfoDetailData;
534 WCHAR InfSectionWithExt[256];
535 BYTE buffer[2048];
536 DWORD dwRequired;
537
538 TRACE("InstallDeviceData()\n");
539
540 hKey = SetupDiCreateDevRegKeyW(DeviceInfoSet,
541 DeviceInfoData,
542 DICS_FLAG_GLOBAL,
543 0,
544 DIREG_DRV,
545 NULL,
546 NULL);
547 if (hKey == NULL)
548 goto done;
549
550 DriverInfoData.cbSize = sizeof(SP_DRVINFO_DATA);
551 if (!SetupDiGetSelectedDriverW(DeviceInfoSet,
552 DeviceInfoData,
553 &DriverInfoData))
554 {
555 goto done;
556 }
557
558 DriverInfoDetailData = (PSP_DRVINFO_DETAIL_DATA)buffer;
559 DriverInfoDetailData->cbSize = sizeof(SP_DRVINFO_DETAIL_DATA);
560 if (!SetupDiGetDriverInfoDetailW(DeviceInfoSet,
561 DeviceInfoData,
562 &DriverInfoData,
563 DriverInfoDetailData,
564 2048,
565 &dwRequired))
566 {
567 if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
568 goto done;
569 }
570
571 TRACE("Inf file name: %S\n", DriverInfoDetailData->InfFileName);
572
573 hInf = SetupOpenInfFileW(DriverInfoDetailData->InfFileName,
574 NULL,
575 INF_STYLE_WIN4,
576 NULL);
577 if (hInf == INVALID_HANDLE_VALUE)
578 goto done;
579
580 TRACE("Section name: %S\n", DriverInfoDetailData->SectionName);
581
582 SetupDiGetActualSectionToInstallW(hInf,
583 DriverInfoDetailData->SectionName,
584 InfSectionWithExt,
585 256,
586 NULL,
587 NULL);
588
589 TRACE("InfSectionWithExt: %S\n", InfSectionWithExt);
590
591 SetupInstallFromInfSectionW(NULL,
592 hInf,
593 InfSectionWithExt,
594 SPINST_REGISTRY,
595 hKey,
596 NULL,
597 0,
598 NULL,
599 NULL,
600 NULL,
601 NULL);
602
603 TRACE("Done\n");
604
605 done:;
606 if (hKey != NULL)
607 RegCloseKey(hKey);
608
609 if (hInf != INVALID_HANDLE_VALUE)
610 SetupCloseInfFile(hInf);
611 }
612
613
614
615 PORT_TYPE
616 GetPortType(IN HDEVINFO DeviceInfoSet,
617 IN PSP_DEVINFO_DATA DeviceInfoData)
618 {
619 HKEY hKey = NULL;
620 DWORD dwSize;
621 DWORD dwType = 0;
622 BYTE bData = 0;
623 PORT_TYPE PortType = UnknownPort;
624 LONG lError;
625
626 TRACE("GetPortType()\n");
627
628 hKey = SetupDiCreateDevRegKeyW(DeviceInfoSet,
629 DeviceInfoData,
630 DICS_FLAG_GLOBAL,
631 0,
632 DIREG_DRV,
633 NULL,
634 NULL);
635 if (hKey == NULL)
636 {
637 goto done;
638 }
639
640 dwSize = sizeof(BYTE);
641 lError = RegQueryValueExW(hKey,
642 L"PortSubClass",
643 NULL,
644 &dwType,
645 &bData,
646 &dwSize);
647
648 TRACE("lError: %ld\n", lError);
649 TRACE("dwSize: %lu\n", dwSize);
650 TRACE("dwType: %lu\n", dwType);
651
652 if (lError == ERROR_SUCCESS &&
653 dwSize == sizeof(BYTE) &&
654 dwType == REG_BINARY)
655 {
656 if (bData == 0)
657 PortType = ParallelPort;
658 else
659 PortType = SerialPort;
660 }
661
662 done:;
663 if (hKey != NULL)
664 RegCloseKey(hKey);
665
666 TRACE("GetPortType() returns %u \n", PortType);
667
668 return PortType;
669 }
670
671
672 static DWORD
673 InstallPort(IN HDEVINFO DeviceInfoSet,
674 IN PSP_DEVINFO_DATA DeviceInfoData)
675 {
676 PORT_TYPE PortType;
677
678 InstallDeviceData(DeviceInfoSet, DeviceInfoData);
679
680 PortType = GetPortType(DeviceInfoSet, DeviceInfoData);
681 switch (PortType)
682 {
683 case ParallelPort:
684 return InstallParallelPort(DeviceInfoSet, DeviceInfoData);
685
686 case SerialPort:
687 return InstallSerialPort(DeviceInfoSet, DeviceInfoData);
688
689 default:
690 return ERROR_DI_DO_DEFAULT;
691 }
692 }
693
694
695 static DWORD
696 RemovePort(IN HDEVINFO DeviceInfoSet,
697 IN PSP_DEVINFO_DATA DeviceInfoData)
698 {
699 PORT_TYPE PortType;
700 HCOMDB hComDB = HCOMDB_INVALID_HANDLE_VALUE;
701 HKEY hKey;
702 LONG lError;
703 DWORD dwPortNumber;
704 DWORD dwPortNameSize;
705 WCHAR szPortName[8];
706
707 /* If we are removing a serial port ... */
708 PortType = GetPortType(DeviceInfoSet, DeviceInfoData);
709 if (PortType == SerialPort)
710 {
711 /* Open the port database */
712 if (ComDBOpen(&hComDB) == ERROR_SUCCESS)
713 {
714 /* Open the device key */
715 hKey = SetupDiOpenDevRegKey(DeviceInfoSet,
716 DeviceInfoData,
717 DICS_FLAG_GLOBAL,
718 0,
719 DIREG_DEV,
720 KEY_READ);
721 if (hKey != INVALID_HANDLE_VALUE)
722 {
723 /* Query the port name */
724 dwPortNameSize = sizeof(szPortName);
725 lError = RegQueryValueEx(hKey,
726 L"PortName",
727 NULL,
728 NULL,
729 (PBYTE)szPortName,
730 &dwPortNameSize);
731
732 /* Close the device key */
733 RegCloseKey(hKey);
734
735 /* If we got a valid port name ...*/
736 if (lError == ERROR_SUCCESS)
737 {
738 /* Get the port number */
739 dwPortNumber = _wtoi(szPortName + wcslen(pszCom));
740
741 /* Release the port */
742 ComDBReleasePort(hComDB, dwPortNumber);
743 }
744 }
745
746 /* Close the port database */
747 ComDBClose(hComDB);
748 }
749 }
750
751 /* Remove the device */
752 if (!SetupDiRemoveDevice(DeviceInfoSet, DeviceInfoData))
753 return GetLastError();
754
755 return ERROR_SUCCESS;
756 }
757
758
759 DWORD
760 WINAPI
761 PortsClassInstaller(IN DI_FUNCTION InstallFunction,
762 IN HDEVINFO DeviceInfoSet,
763 IN PSP_DEVINFO_DATA DeviceInfoData OPTIONAL)
764 {
765 TRACE("PortsClassInstaller(%lu, %p, %p)\n",
766 InstallFunction, DeviceInfoSet, DeviceInfoData);
767
768 switch (InstallFunction)
769 {
770 case DIF_INSTALLDEVICE:
771 return InstallPort(DeviceInfoSet, DeviceInfoData);
772
773 case DIF_REMOVE:
774 return RemovePort(DeviceInfoSet, DeviceInfoData);
775
776 default:
777 TRACE("Install function %u ignored\n", InstallFunction);
778 return ERROR_DI_DO_DEFAULT;
779 }
780 }
781
782 /* EOF */