Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

947 lines
33 KiB

  1. /*++
  2. Copyright (c) 1999-2000 Microsoft Corporation
  3. Module Name :
  4. COMPORT.C
  5. Abstract:
  6. Most of this code was liberated from posusb.sys
  7. Author:
  8. Jeff Midkiff (jeffmi) 08-24-99
  9. -- */
  10. #include "wceusbsh.h"
  11. void NumToDecString(PWCHAR String, USHORT Number, USHORT stringLen);
  12. LONG MyLog(ULONG base, ULONG num);
  13. PVOID MemDup(PVOID dataPtr, ULONG length);
  14. LONG WStrNCmpI(PWCHAR s1, PWCHAR s2, ULONG n);
  15. ULONG LAtoD(PWCHAR string);
  16. #ifdef ALLOC_PRAGMA
  17. #pragma alloc_text(PAGEWCE0, GetFreeComPortNumber)
  18. #pragma alloc_text(PAGEWCE0, ReleaseCOMPort)
  19. #pragma alloc_text(PAGEWCE0, DoSerialPortNaming)
  20. #pragma alloc_text(PAGEWCE0, UndoSerialPortNaming)
  21. #pragma alloc_text(PAGEWCE0, NumToDecString)
  22. #pragma alloc_text(PAGEWCE0, MyLog)
  23. #pragma alloc_text(PAGEWCE0, MemDup)
  24. #pragma alloc_text(PAGEWCE0, WStrNCmpI)
  25. #pragma alloc_text(PAGEWCE0, LAtoD)
  26. #endif
  27. LONG
  28. GetFreeComPortNumber(
  29. VOID
  30. )
  31. /*++
  32. Routine Description:
  33. Find the index of the next unused serial COM port name in the system
  34. (e.g. COM3, COM4, etc).
  35. Arguments:
  36. Return Value:
  37. Return COM port number or -1 if unsuccessful.
  38. --*/
  39. {
  40. LONG comNumber = -1;
  41. DbgDump(DBG_INIT, (">GetFreeComPortNumber\n"));
  42. PAGED_CODE();
  43. if (g_isWin9x){
  44. /*
  45. * Windows 98
  46. * Find the first unused name under Hardware\DeviceMap\SerialComm.
  47. *
  48. * BUGBUG:
  49. * This algorithm does not find all the COM ports reserved
  50. * by modems. May want to port tomgreen's AllocateCommPort
  51. * function from \faulty\Wdm10\usb\driver\ccport\utils.c
  52. */
  53. HANDLE hKey;
  54. UNICODE_STRING keyName;
  55. NTSTATUS status;
  56. OBJECT_ATTRIBUTES objectAttributes;
  57. RtlInitUnicodeString(&keyName, L"\\Registry\\Machine\\Hardware\\DeviceMap\\SerialComm");
  58. InitializeObjectAttributes( &objectAttributes,
  59. &keyName,
  60. OBJ_CASE_INSENSITIVE,
  61. NULL,
  62. (PSECURITY_DESCRIPTOR)NULL);
  63. status = ZwOpenKey(&hKey, KEY_QUERY_VALUE | KEY_SET_VALUE, &objectAttributes);
  64. if (NT_SUCCESS(status)){
  65. #define MAX_COMPORT_NAME_LEN (sizeof("COMxxxx")-1)
  66. UCHAR keyValueBytes[sizeof(KEY_VALUE_FULL_INFORMATION)+(MAX_COMPORT_NAME_LEN+1)*sizeof(WCHAR)+sizeof(ULONG)];
  67. PKEY_VALUE_FULL_INFORMATION keyValueInfo = (PKEY_VALUE_FULL_INFORMATION)keyValueBytes;
  68. ULONG i, actualLen;
  69. ULONG keyIndex = 0;
  70. /*
  71. * This bitmask represents the used COM ports.
  72. * Bit i set indicates com port i+1 is reserved.
  73. * Initialize with COM1 and COM2 reserved.
  74. *
  75. * BUGBUG - only works for up to 32 ports.
  76. */
  77. ULONG comNameMask = 3;
  78. do {
  79. status = ZwEnumerateValueKey(
  80. hKey,
  81. keyIndex++,
  82. KeyValueFullInformation,
  83. keyValueInfo,
  84. sizeof(keyValueBytes),
  85. &actualLen);
  86. if (NT_SUCCESS(status)){
  87. if (keyValueInfo->Type == REG_SZ){
  88. PWCHAR valuePtr = (PWCHAR)(((PCHAR)keyValueInfo)+keyValueInfo->DataOffset);
  89. if (!WStrNCmpI(valuePtr, L"COM", 3)){
  90. /*
  91. * valuePtr+3 points the index portion of the COMx string,
  92. * but we can't call LAtoD on it because it is
  93. * NOT NULL-TERMINATED.
  94. * So copy the index into our own buffer,
  95. * null-terminate that,
  96. * and call LAtoD to get the numerical index.
  97. */
  98. WCHAR comPortIndexString[4+1];
  99. ULONG thisComNumber;
  100. for (i = 0; (i < 4) && (i < keyValueInfo->DataLength/sizeof(WCHAR)); i++){
  101. comPortIndexString[i] = valuePtr[3+i];
  102. }
  103. comPortIndexString[i] = UNICODE_NULL;
  104. thisComNumber = LAtoD(comPortIndexString);
  105. if (thisComNumber == 0){
  106. ASSERT(thisComNumber != 0);
  107. }
  108. else if (thisComNumber <= sizeof(ULONG)*8){
  109. comNameMask |= 1 << (thisComNumber-1);
  110. }
  111. else {
  112. ASSERT(thisComNumber <= sizeof(ULONG)*8);
  113. }
  114. }
  115. }
  116. }
  117. } while (NT_SUCCESS(status));
  118. /*
  119. * First clear bit in comNameMask represents the first available COM name.
  120. */
  121. for (i = 0; i < sizeof(ULONG)*8; i++){
  122. if (!(comNameMask & (1 << i))){
  123. WCHAR comName[] = L"COMxxxx";
  124. ULONG comNumLen;
  125. /*
  126. * Save the COM port number that we're returning.
  127. */
  128. comNumber = i+1;
  129. DbgDump(DBG_INIT, ("GetFreeComPortNumber: got free COM port #%d\n", comNumber));
  130. /*
  131. * Write a temporary COMx=COMx holder value to the SERIALCOMM key
  132. * so that no other PDOs get this COM port number.
  133. * This value will get overwritten by <symbolicLinkName=COMx> when the pdo is started.
  134. */
  135. comNumLen = MyLog(10, comNumber)+1;
  136. ASSERT(comNumLen <= 4);
  137. NumToDecString(comName+3, (USHORT)comNumber, (USHORT)comNumLen);
  138. comName[3+comNumLen] = UNICODE_NULL;
  139. status = RtlWriteRegistryValue( RTL_REGISTRY_DEVICEMAP,
  140. L"SERIALCOMM",
  141. comName,
  142. REG_SZ,
  143. comName,
  144. (3 + comNumLen + 1) * sizeof(WCHAR));
  145. ASSERT(NT_SUCCESS(status));
  146. break;
  147. }
  148. }
  149. }
  150. else {
  151. DbgDump(DBG_ERR, ("GetFreeComPortNumber: ZwOpenKey failed with status 0x%x\n", status));
  152. }
  153. }
  154. else {
  155. /*
  156. * Windows NT.
  157. * Use the COM Name Arbiter bitmap.
  158. */
  159. HANDLE hKey;
  160. OBJECT_ATTRIBUTES objectAttributes;
  161. UNICODE_STRING keyName;
  162. NTSTATUS status;
  163. RtlInitUnicodeString(&keyName, L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\COM Name Arbiter");
  164. InitializeObjectAttributes( &objectAttributes,
  165. &keyName,
  166. OBJ_CASE_INSENSITIVE,
  167. NULL,
  168. (PSECURITY_DESCRIPTOR)NULL);
  169. status = ZwOpenKey( &hKey,
  170. KEY_QUERY_VALUE | KEY_SET_VALUE,
  171. &objectAttributes);
  172. if (NT_SUCCESS(status)){
  173. UNICODE_STRING valueName;
  174. PVOID rawData;
  175. ULONG dataSize;
  176. RtlInitUnicodeString(&valueName, L"ComDB");
  177. ASSERT(hKey);
  178. dataSize = sizeof(KEY_VALUE_PARTIAL_INFORMATION);
  179. /*
  180. * Allocate one extra byte in case we have to add a byte to ComDB
  181. */
  182. rawData = ExAllocatePool(NonPagedPool, dataSize+1);
  183. if (rawData){
  184. status = ZwQueryValueKey( hKey,
  185. &valueName,
  186. KeyValuePartialInformation,
  187. rawData,
  188. dataSize,
  189. &dataSize);
  190. if (status == STATUS_BUFFER_OVERFLOW){
  191. ExFreePool(rawData);
  192. ASSERT(dataSize > FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data));
  193. /*
  194. * Allocate one extra byte in case we have to add a byte to ComDB
  195. */
  196. rawData = ExAllocatePool(NonPagedPool, dataSize+1);
  197. if (rawData){
  198. status = ZwQueryValueKey( hKey,
  199. &valueName,
  200. KeyValuePartialInformation,
  201. rawData,
  202. dataSize,
  203. &dataSize);
  204. }
  205. else {
  206. status = STATUS_INSUFFICIENT_RESOURCES;
  207. }
  208. }
  209. if (NT_SUCCESS(status)){
  210. PKEY_VALUE_PARTIAL_INFORMATION keyPartialInfo = (PKEY_VALUE_PARTIAL_INFORMATION)rawData;
  211. ULONG b, i;
  212. BOOLEAN done = FALSE;
  213. ASSERT(dataSize >= FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data));
  214. ASSERT(keyPartialInfo->Type == REG_BINARY);
  215. /*
  216. * The ComDB value is just a bit mask where bit n set indicates
  217. * that COM port # n+1 is taken.
  218. * Get the index of the first unset bit; starting with bit 2 (COM3).
  219. */
  220. for (b = 0; (b < dataSize-FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data)) && !done; b++){
  221. for (i = (b == 0) ? 2 : 0; (i < 8) && !done; i++){
  222. if (keyPartialInfo->Data[b] & (1 << i)){
  223. /*
  224. * This COM port (#8*b+i+1) is taken, go to the next one.
  225. */
  226. }
  227. else {
  228. /*
  229. * Found a free COM port.
  230. * Write the value back with the new bit set.
  231. * Only write back the number of bytes we read earlier.
  232. * Only use this COM port if the write succeeds.
  233. *
  234. * Note: careful with the size of the KEY_VALUE_PARTIAL_INFORMATION
  235. * struct. Its real size is 0x0D bytes,
  236. * but the compiler aligns it to 0x10 bytes.
  237. * So use FIELD_OFFSET, not sizeof, to determine
  238. * how many bytes to write.
  239. */
  240. keyPartialInfo->Data[b] |= (1 << i);
  241. status = ZwSetValueKey( hKey,
  242. &valueName,
  243. 0,
  244. REG_BINARY,
  245. (PVOID)keyPartialInfo->Data,
  246. dataSize-FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data));
  247. if (NT_SUCCESS(status)){
  248. comNumber = 8*b + i + 1;
  249. DbgDump(DBG_INIT, ("GetFreeComPortNumber: got free COM port #0x%x\n", comNumber));
  250. }
  251. else {
  252. DbgDump(DBG_ERR, ("GetFreeComPortNumber: ZwSetValueKey failed with 0x%x\n", status));
  253. }
  254. done = TRUE;
  255. }
  256. }
  257. }
  258. if ((b == dataSize-FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data)) && !done){
  259. /*
  260. * No more available bits in ComDB, so add a byte.
  261. */
  262. ASSERT(comNumber == -1);
  263. ASSERT(b > 0);
  264. DbgDump(DBG_WRN, ("ComDB overflow -- adding new byte"));
  265. keyPartialInfo->Data[b] = 1;
  266. dataSize++;
  267. status = ZwSetValueKey( hKey,
  268. &valueName,
  269. 0,
  270. REG_BINARY,
  271. (PVOID)keyPartialInfo->Data,
  272. dataSize-FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data));
  273. if (NT_SUCCESS(status)){
  274. comNumber = 8*b + 1;
  275. DbgDump(DBG_INIT, ("GetFreeComPortNumber: got free COM port #0x%x.", comNumber));
  276. }
  277. else {
  278. DbgDump(DBG_ERR, ("GetFreeComPortNumber: ZwSetValueKey #2 failed with 0x%x.", status));
  279. }
  280. }
  281. ASSERT(comNumber != -1);
  282. }
  283. else {
  284. DbgDump(DBG_ERR, ("GetFreeComPortNumber: ZwQueryValueKey failed with 0x%x.", status));
  285. }
  286. /*
  287. * Check that we didn't fail the second allocation before freeing this buffer.
  288. */
  289. if (rawData){
  290. ExFreePool(rawData);
  291. }
  292. }
  293. else {
  294. status = STATUS_INSUFFICIENT_RESOURCES;
  295. }
  296. status = ZwClose(hKey);
  297. ASSERT(NT_SUCCESS(status));
  298. }
  299. else {
  300. DbgDump(DBG_ERR, ("GetFreeComPortNumber: ZwOpenKey failed with 0x%x.", status));
  301. }
  302. }
  303. ASSERT(comNumber != -1);
  304. DbgDump(DBG_INIT, ("<GetFreeComPortNumber\n"));
  305. return comNumber;
  306. }
  307. //
  308. // the only time we want this is when we uninstall...
  309. //
  310. VOID
  311. ReleaseCOMPort(
  312. LONG comPortNumber
  313. )
  314. {
  315. DbgDump(DBG_INIT, (">ReleaseCOMPort: %d\n", comPortNumber));
  316. PAGED_CODE();
  317. if (g_isWin9x){
  318. /*
  319. * We punt on this for Win9x.
  320. * That's ok since the SERIALCOMM keys are dynamically-generated at each boot,
  321. * so if start fails a COM port number will just be unavailable until the next boot.
  322. */
  323. DbgDump(DBG_WRN, ("ReleaseCOMPort: not implemented for Win9x\n")); // BUGBUG
  324. }
  325. else {
  326. HANDLE hKey = NULL;
  327. OBJECT_ATTRIBUTES objectAttributes;
  328. UNICODE_STRING keyName;
  329. NTSTATUS status;
  330. if ( !(comPortNumber > 0)) {
  331. DbgDump(DBG_ERR, ("ReleaseCOMPort - INVALID_PARAMETER: %d\n", comPortNumber )); // BUGBUG
  332. return;
  333. }
  334. RtlInitUnicodeString(&keyName, L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\COM Name Arbiter");
  335. InitializeObjectAttributes( &objectAttributes,
  336. &keyName,
  337. OBJ_CASE_INSENSITIVE,
  338. NULL,
  339. (PSECURITY_DESCRIPTOR)NULL);
  340. status = ZwOpenKey(&hKey, KEY_QUERY_VALUE | KEY_SET_VALUE, &objectAttributes);
  341. if (NT_SUCCESS(status)){
  342. UNICODE_STRING valueName;
  343. PVOID rawData;
  344. ULONG dataSize;
  345. RtlInitUnicodeString(&valueName, L"ComDB");
  346. ASSERT(hKey);
  347. dataSize = sizeof(KEY_VALUE_PARTIAL_INFORMATION);
  348. rawData = ExAllocatePool(NonPagedPool, dataSize);
  349. if (rawData){
  350. status = ZwQueryValueKey( hKey,
  351. &valueName,
  352. KeyValuePartialInformation,
  353. rawData,
  354. dataSize,
  355. &dataSize);
  356. if (status == STATUS_BUFFER_OVERFLOW){
  357. ExFreePool(rawData);
  358. ASSERT(dataSize > FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data));
  359. rawData = ExAllocatePool(NonPagedPool, dataSize);
  360. if (rawData){
  361. status = ZwQueryValueKey( hKey,
  362. &valueName,
  363. KeyValuePartialInformation,
  364. rawData,
  365. dataSize,
  366. &dataSize);
  367. }
  368. else {
  369. status = STATUS_INSUFFICIENT_RESOURCES;
  370. }
  371. }
  372. if (NT_SUCCESS(status)){
  373. PKEY_VALUE_PARTIAL_INFORMATION keyPartialInfo = (PKEY_VALUE_PARTIAL_INFORMATION)rawData;
  374. ASSERT(dataSize > FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data));
  375. ASSERT(keyPartialInfo->Type == REG_BINARY);
  376. /*
  377. * The ComDB value is just a bit mask where bit n set indicates
  378. * that COM port # n+1 is taken.
  379. * Get the index of the first unset bit; starting with bit 2 (COM3).
  380. *
  381. * Note: careful with the size of the KEY_VALUE_PARTIAL_INFORMATION
  382. * struct. Its real size is 0x0D bytes,
  383. * but the compiler aligns it to 0x10 bytes.
  384. * So use FIELD_OFFSET, not sizeof, to determine
  385. * how many bytes to write.
  386. */
  387. ASSERT(comPortNumber >= 3);
  388. if ((comPortNumber > 0) && (comPortNumber <= (LONG)(dataSize-FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data))*8)){
  389. //ASSERT(keyPartialInfo->Data[(comPortNumber-1)/8] & (1 << ((comPortNumber-1) & 7)));
  390. keyPartialInfo->Data[(comPortNumber-1)/8] &= ~(1 << ((comPortNumber-1) & 7));
  391. status = ZwSetValueKey( hKey,
  392. &valueName,
  393. 0,
  394. REG_BINARY,
  395. (PVOID)keyPartialInfo->Data,
  396. dataSize-FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data));
  397. if (NT_SUCCESS(status)){
  398. DbgDump(DBG_INIT, ("ReleaseCOMPort: released COM port # 0x%x\n", comPortNumber));
  399. }
  400. else {
  401. DbgDump(DBG_ERR, ("ReleaseCOMPort: ZwSetValueKey failed with 0x%x\n", status));
  402. }
  403. }
  404. else {
  405. ASSERT((comPortNumber > 0) && (comPortNumber <= (LONG)(dataSize-FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data))*8));
  406. }
  407. }
  408. else {
  409. DbgDump(DBG_ERR, ("ReleaseCOMPort: ZwQueryValueKey failed with 0x%x\n", status));
  410. }
  411. /*
  412. * Check that we didn't fail the second allocation before freeing this buffer.
  413. */
  414. if (rawData){
  415. ExFreePool(rawData);
  416. }
  417. }
  418. else {
  419. status = STATUS_INSUFFICIENT_RESOURCES;
  420. }
  421. status = ZwClose(hKey);
  422. ASSERT(NT_SUCCESS(status));
  423. }
  424. else {
  425. DbgDump(DBG_ERR, ("ReleaseCOMPort: ZwOpenKey failed with 0x%x\n", status));
  426. }
  427. }
  428. DbgDump(DBG_INIT, ("<ReleaseCOMPort\n"));
  429. return;
  430. }
  431. //
  432. // Note: this is NT specific.
  433. // Win98 is entirely different.
  434. //
  435. NTSTATUS
  436. DoSerialPortNaming(
  437. IN PDEVICE_EXTENSION PDevExt,
  438. IN LONG ComPortNumber
  439. )
  440. {
  441. NTSTATUS status;
  442. PWCHAR pwcComPortName=NULL;
  443. static WCHAR comNamePrefix[] = L"\\DosDevices\\COM";
  444. WCHAR buf[sizeof(comNamePrefix)/sizeof(WCHAR) + 4];
  445. LONG numLen = MyLog(10, ComPortNumber)+1;
  446. DbgDump(DBG_INIT, (">DoSerialPortNaming %d\n", ComPortNumber));
  447. PAGED_CODE();
  448. ASSERT(PDevExt);
  449. ASSERT((numLen > 0) && (numLen <= 4));
  450. ASSERT_SERIAL_PORT(PDevExt->SerialPort);
  451. RtlCopyMemory(buf, comNamePrefix, sizeof(comNamePrefix));
  452. NumToDecString( buf+sizeof(comNamePrefix)/sizeof(WCHAR)-1,
  453. (USHORT)ComPortNumber,
  454. (USHORT)numLen );
  455. buf[sizeof(comNamePrefix)/sizeof(WCHAR) - 1 + numLen] = UNICODE_NULL;
  456. pwcComPortName = MemDup(buf, sizeof(buf));
  457. if (pwcComPortName) {
  458. //
  459. // create symbolic link for the SerialPort interface
  460. //
  461. RtlInitUnicodeString( &PDevExt->SerialPort.Com.SerialPortName, pwcComPortName);
  462. ASSERT( PDevExt->DeviceName.Buffer );
  463. ASSERT( PDevExt->SerialPort.Com.SerialPortName.Buffer );
  464. status = IoCreateSymbolicLink( &PDevExt->SerialPort.Com.SerialPortName, &PDevExt->DeviceName );
  465. if (NT_SUCCESS(status)) {
  466. //
  467. // let the system know there is another SERIALCOMM entry under HKLM\DEVICEMAP\SERIALCOMM
  468. //
  469. UNICODE_STRING comPortSuffix;
  470. PDevExt->SerialPort.Com.SerialSymbolicLink = TRUE;
  471. /*
  472. * Create the '\Device\WCEUSBSI000x = COMx' entry
  473. */
  474. RtlInitUnicodeString(&comPortSuffix, PDevExt->SerialPort.Com.SerialPortName.Buffer+(sizeof(L"\\DosDevices\\")-sizeof(WCHAR))/sizeof(WCHAR));
  475. //ASSERT( PDevExt->SerialPort.Com.SerialCOMMname.Buffer );
  476. status = RtlWriteRegistryValue( RTL_REGISTRY_DEVICEMAP,
  477. L"SERIALCOMM",
  478. PDevExt->DeviceName.Buffer,
  479. REG_SZ,
  480. comPortSuffix.Buffer,
  481. comPortSuffix.Length + sizeof(WCHAR) );
  482. if (NT_SUCCESS(status)){
  483. PDevExt->SerialPort.Com.PortNumber = ComPortNumber;
  484. if (g_isWin9x){
  485. NTSTATUS tmpStatus;
  486. /*
  487. * Delete the temporary 'COMx=COMx' holder value we created earlier.
  488. */
  489. tmpStatus = RtlDeleteRegistryValue( RTL_REGISTRY_DEVICEMAP,
  490. L"SERIALCOMM",
  491. comPortSuffix.Buffer);
  492. //ASSERT(NT_SUCCESS(tmpStatus));
  493. #if DBG
  494. if ( !NT_SUCCESS(tmpStatus) ) {
  495. DbgDump(DBG_WRN, ("RtlDeleteRegistryValue error: 0x%x\n", tmpStatus));
  496. }
  497. #endif
  498. }
  499. } else {
  500. DbgDump(DBG_ERR, ("RtlWriteRegistryValue error: 0x%x\n", status));
  501. LogError( NULL,
  502. PDevExt->DeviceObject,
  503. 0, 0, 0,
  504. ERR_SERIALCOMM,
  505. status,
  506. SERIAL_REGISTRY_WRITE_FAILED,
  507. PDevExt->DeviceName.Length + sizeof(WCHAR),
  508. PDevExt->DeviceName.Buffer,
  509. 0,
  510. NULL
  511. );
  512. }
  513. } else {
  514. DbgDump(DBG_ERR, ("IoCreateSymbolicLink error: 0x%x\n", status));
  515. LogError( NULL,
  516. PDevExt->DeviceObject,
  517. 0, 0, 0,
  518. ERR_COMM_SYMLINK,
  519. status,
  520. SERIAL_NO_SYMLINK_CREATED,
  521. PDevExt->SerialPort.Com.SerialPortName.Length + sizeof(WCHAR),
  522. PDevExt->SerialPort.Com.SerialPortName.Buffer,
  523. PDevExt->DeviceName.Length + sizeof(WCHAR),
  524. PDevExt->DeviceName.Buffer
  525. );
  526. TEST_TRAP();
  527. }
  528. } else {
  529. status = STATUS_INSUFFICIENT_RESOURCES;
  530. DbgDump(DBG_ERR, ("DoSerialPortNaming error: 0x%x\n", status));
  531. LogError( NULL,
  532. PDevExt->DeviceObject,
  533. 0, 0, 0,
  534. ERR_COMM_SYMLINK,
  535. status,
  536. SERIAL_INSUFFICIENT_RESOURCES,
  537. 0, NULL, 0, NULL );
  538. }
  539. DbgDump(DBG_INIT, ("<DoSerialPortNaming (0x%x)\n", status));
  540. return status;
  541. }
  542. VOID
  543. UndoSerialPortNaming(
  544. IN PDEVICE_EXTENSION PDevExt
  545. )
  546. {
  547. DbgDump(DBG_INIT, (">UndoSerialPortNaming\n"));
  548. PAGED_CODE();
  549. ASSERT(PDevExt);
  550. ASSERT_SERIAL_PORT(PDevExt->SerialPort);
  551. if (!g_ExposeComPort) {
  552. DbgDump(DBG_INIT, ("!g_ExposeComPort\n"));
  553. return;
  554. }
  555. // remove our entry from ComDB
  556. ReleaseCOMPort( PDevExt->SerialPort.Com.PortNumber );
  557. if (PDevExt->SerialPort.Com.SerialPortName.Buffer && PDevExt->SerialPort.Com.SerialSymbolicLink) {
  558. IoDeleteSymbolicLink(&PDevExt->SerialPort.Com.SerialPortName);
  559. }
  560. if (PDevExt->SerialPort.Com.SerialPortName.Buffer != NULL) {
  561. ExFreePool(PDevExt->SerialPort.Com.SerialPortName.Buffer);
  562. RtlInitUnicodeString(&PDevExt->SerialPort.Com.SerialPortName, NULL);
  563. }
  564. if (PDevExt->SerialPort.Com.SerialCOMMname.Buffer != NULL) {
  565. ExFreePool(PDevExt->SerialPort.Com.SerialCOMMname.Buffer);
  566. RtlInitUnicodeString(&PDevExt->SerialPort.Com.SerialCOMMname, NULL);
  567. }
  568. if (PDevExt->DeviceName.Buffer != NULL) {
  569. RtlDeleteRegistryValue( RTL_REGISTRY_DEVICEMAP,
  570. SERIAL_DEVICE_MAP,
  571. PDevExt->DeviceName.Buffer);
  572. ExFreePool(PDevExt->DeviceName.Buffer);
  573. RtlInitUnicodeString(&PDevExt->DeviceName, NULL);
  574. }
  575. DbgDump(DBG_INIT, ("<UndoSerialPortNaming\n"));
  576. }
  577. void NumToDecString(PWCHAR String, USHORT Number, USHORT stringLen)
  578. {
  579. const static WCHAR map[] = L"0123456789";
  580. LONG i = 0;
  581. PAGED_CODE();
  582. ASSERT(stringLen);
  583. for (i = stringLen-1; i >= 0; i--) {
  584. String[i] = map[Number % 10];
  585. Number /= 10;
  586. }
  587. }
  588. LONG MyLog(ULONG base, ULONG num)
  589. {
  590. LONG result;
  591. ASSERT(num);
  592. PAGED_CODE();
  593. for (result = -1; num; result++){
  594. num /= base;
  595. }
  596. return result;
  597. }
  598. PVOID MemDup(PVOID dataPtr, ULONG length)
  599. {
  600. PVOID newPtr;
  601. PAGED_CODE();
  602. newPtr = (PVOID)ExAllocatePool(NonPagedPool, length); // BUGBUG allow paged
  603. if (newPtr){
  604. RtlCopyMemory(newPtr, dataPtr, length);
  605. }
  606. return newPtr;
  607. }
  608. LONG WStrNCmpI(PWCHAR s1, PWCHAR s2, ULONG n)
  609. {
  610. ULONG result;
  611. PAGED_CODE();
  612. while (n && *s1 && *s2 && ((*s1|0x20) == (*s2|0x20))){
  613. s1++, s2++;
  614. n--;
  615. }
  616. if (n){
  617. result = ((*s1|0x20) > (*s2|0x20)) ? 1 : ((*s1|0x20) < (*s2|0x20)) ? -1 : 0;
  618. }
  619. else {
  620. result = 0;
  621. }
  622. return result;
  623. }
  624. ULONG LAtoD(PWCHAR string)
  625. /*++
  626. Routine Description:
  627. Convert a decimal string (without the '0x' prefix) to a ULONG.
  628. Arguments:
  629. string - null-terminated wide-char decimal-digit string
  630. Return Value:
  631. ULONG value
  632. --*/
  633. {
  634. ULONG i, result = 0;
  635. PAGED_CODE();
  636. for (i = 0; string[i]; i++){
  637. if ((string[i] >= L'0') && (string[i] <= L'9')){
  638. result *= 10;
  639. result += (string[i] - L'0');
  640. }
  641. else {
  642. ASSERT(0);
  643. break;
  644. }
  645. }
  646. return result;
  647. }
  648. #if 0
  649. VOID
  650. NumToHexString(
  651. PWCHAR String,
  652. USHORT Number,
  653. USHORT stringLen
  654. )
  655. {
  656. const static WCHAR map[] = L"0123456789ABCDEF";
  657. LONG i = 0;
  658. PAGED_CODE();
  659. ASSERT(stringLen);
  660. for (i = stringLen-1; i >= 0; i--) {
  661. String[i] = map[Number & 0x0F];
  662. Number >>= 4;
  663. }
  664. }
  665. LONG
  666. GetComPort(
  667. PDEVICE_OBJECT PDevObj,
  668. ULONG ComInterfaceIndex
  669. )
  670. /*++
  671. Routine Description:
  672. Get the serial COM port index for a serial interface we're about to create.
  673. If this is the first plug-in, call GetFreeComPortNumber to reserve a new
  674. static COM port for this device and store it in our software key.
  675. If this is not the first plug-in, it should be sitting in the registry.
  676. ComInterfaceIndex - is our zero-based device interface index, 0000, 0001, etc.
  677. Arguments:
  678. Return Value:
  679. Return COM port number or -1 if unsuccessful.
  680. --*/
  681. {
  682. PDEVICE_EXTENSION pDevExt = PDevObj->DeviceExtension;
  683. LONG comNumber = -1;
  684. NTSTATUS status;
  685. HANDLE hRegDevice;
  686. DbgDump(DBG_INIT, (">GetComPort\n"));
  687. PAGED_CODE();
  688. status = IoOpenDeviceRegistryKey( pDevExt->PDO,
  689. /*PLUGPLAY_REGKEY_DEVICE,*/ PLUGPLAY_REGKEY_DRIVER,
  690. KEY_READ,
  691. &hRegDevice);
  692. if (NT_SUCCESS(status)){
  693. UNICODE_STRING keyName;
  694. PKEY_VALUE_FULL_INFORMATION keyValueInfo;
  695. ULONG keyValueTotalSize, actualLength;
  696. //
  697. // PLUGPLAY_REGKEY_DEVICE is under HKLM\System\CCS\Enum\USB\ROOT_HUB\4&574193&0
  698. // PLUGPLAY_REGKEY_DRIVER is under HKLM\System\CCS\Class\{Your_GUID}\000x
  699. //
  700. WCHAR interfaceKeyName[] = L"COMPortForInterfaceXXXX";
  701. NumToHexString( interfaceKeyName+sizeof(interfaceKeyName)/sizeof(WCHAR)-1-4,
  702. (USHORT)ComInterfaceIndex,
  703. 4);
  704. RtlInitUnicodeString(&keyName, interfaceKeyName);
  705. keyValueTotalSize = sizeof(KEY_VALUE_FULL_INFORMATION) +
  706. keyName.Length*sizeof(WCHAR) +
  707. sizeof(ULONG);
  708. keyValueInfo = ExAllocatePool(PagedPool, keyValueTotalSize);
  709. if (keyValueInfo){
  710. status = ZwQueryValueKey( hRegDevice,
  711. &keyName,
  712. KeyValueFullInformation,
  713. keyValueInfo,
  714. keyValueTotalSize,
  715. &actualLength);
  716. if (NT_SUCCESS(status)){
  717. ASSERT(keyValueInfo->Type == REG_DWORD);
  718. ASSERT(keyValueInfo->DataLength == sizeof(ULONG));
  719. comNumber = (LONG)*((PULONG)(((PCHAR)keyValueInfo)+keyValueInfo->DataOffset));
  720. DbgDump(DBG_INIT, ("GetComPort: read COM port# 0x%x for interface 0x%x from registry\n", (ULONG)comNumber, ComInterfaceIndex));
  721. }
  722. else {
  723. /*
  724. * No COM port number recorded in registry.
  725. * Allocate a new static COM port from the COM name arbiter
  726. * and record it in our software key for the next PnP.
  727. */
  728. comNumber = GetFreeComPortNumber();
  729. if (comNumber == -1){
  730. DbgDump(DBG_ERR, ("GetComPort: GetFreeComPortNumber failed\n"));
  731. }
  732. else {
  733. status = ZwSetValueKey( hRegDevice,
  734. &keyName,
  735. 0,
  736. REG_DWORD,
  737. &comNumber,
  738. sizeof(ULONG));
  739. if (!NT_SUCCESS(status)){
  740. DbgDump(DBG_ERR, ("GetComPort: ZwSetValueKey failed with status 0x%x\n", status));
  741. }
  742. }
  743. }
  744. ExFreePool(keyValueInfo);
  745. }
  746. else {
  747. ASSERT(keyValueInfo);
  748. }
  749. ZwClose(hRegDevice);
  750. }
  751. else {
  752. DbgDump(DBG_ERR, ("GetComPort: IoOpenDeviceRegistryKey failed with 0x%x\n", status));
  753. }
  754. DbgDump(DBG_INIT, ("<GetComPort %d\n", comNumber));
  755. return comNumber;
  756. }
  757. #endif