usbcomp.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779
  1. /*++
  2. Copyright (c) 2013 Minoca Corp. All Rights Reserved
  3. Module Name:
  4. usbcomp.c
  5. Abstract:
  6. This module implements support for USB compound devices (devices with
  7. multiple interfaces).
  8. Author:
  9. Evan Green 20-Mar-2013
  10. Environment:
  11. Kernel
  12. --*/
  13. //
  14. // ------------------------------------------------------------------- Includes
  15. //
  16. #include <minoca/kernel/driver.h>
  17. #include <minoca/usb/usb.h>
  18. //
  19. // --------------------------------------------------------------------- Macros
  20. //
  21. //
  22. // ---------------------------------------------------------------- Definitions
  23. //
  24. //
  25. // Define the allocation tag used throughout the USB compound device driver.
  26. //
  27. #define USB_COMPOUND_ALLOCATION_TAG 0x43627355 // 'CbsU'
  28. //
  29. // ------------------------------------------------------ Data Type Definitions
  30. //
  31. /*++
  32. Structure Description:
  33. This structure stores context about a USB compound device.
  34. Members:
  35. UsbCoreHandle - Stores the handle to the device as identified by the USB
  36. core library.
  37. InterfaceCount - Stores the number of interfaces this device has.
  38. Children - Stores the array of pointers to child devices, one for each
  39. exposed interface.
  40. --*/
  41. typedef struct _USB_COMPOUND_DEVICE {
  42. HANDLE UsbCoreHandle;
  43. ULONG InterfaceCount;
  44. PDEVICE *Children;
  45. } USB_COMPOUND_DEVICE, *PUSB_COMPOUND_DEVICE;
  46. //
  47. // ----------------------------------------------- Internal Function Prototypes
  48. //
  49. KSTATUS
  50. UsbCmpAddDevice (
  51. PVOID Driver,
  52. PSTR DeviceId,
  53. PSTR ClassId,
  54. PSTR CompatibleIds,
  55. PVOID DeviceToken
  56. );
  57. VOID
  58. UsbCmpDispatchStateChange (
  59. PIRP Irp,
  60. PVOID DeviceContext,
  61. PVOID IrpContext
  62. );
  63. VOID
  64. UsbCmpDispatchOpen (
  65. PIRP Irp,
  66. PVOID DeviceContext,
  67. PVOID IrpContext
  68. );
  69. VOID
  70. UsbCmpDispatchClose (
  71. PIRP Irp,
  72. PVOID DeviceContext,
  73. PVOID IrpContext
  74. );
  75. VOID
  76. UsbCmpDispatchIo (
  77. PIRP Irp,
  78. PVOID DeviceContext,
  79. PVOID IrpContext
  80. );
  81. VOID
  82. UsbCmpDispatchSystemControl (
  83. PIRP Irp,
  84. PVOID DeviceContext,
  85. PVOID IrpContext
  86. );
  87. KSTATUS
  88. UsbCmppStartDevice (
  89. PIRP Irp,
  90. PUSB_COMPOUND_DEVICE Device
  91. );
  92. KSTATUS
  93. UsbCmppEnumerateChildren (
  94. PIRP Irp,
  95. PUSB_COMPOUND_DEVICE Device
  96. );
  97. VOID
  98. UsbCmppRemoveDevice (
  99. PIRP Irp,
  100. PUSB_COMPOUND_DEVICE Device
  101. );
  102. //
  103. // -------------------------------------------------------------------- Globals
  104. //
  105. PDRIVER UsbCmpDriver = NULL;
  106. //
  107. // ------------------------------------------------------------------ Functions
  108. //
  109. KSTATUS
  110. DriverEntry (
  111. PDRIVER Driver
  112. )
  113. /*++
  114. Routine Description:
  115. This routine is the entry point for the USB compound device driver. It
  116. registers the other dispatch functions, and performs driver-wide
  117. initialization.
  118. Arguments:
  119. Driver - Supplies a pointer to the driver object.
  120. Return Value:
  121. STATUS_SUCCESS on success.
  122. Failure code on error.
  123. --*/
  124. {
  125. DRIVER_FUNCTION_TABLE FunctionTable;
  126. KSTATUS Status;
  127. UsbCmpDriver = Driver;
  128. RtlZeroMemory(&FunctionTable, sizeof(DRIVER_FUNCTION_TABLE));
  129. FunctionTable.Version = DRIVER_FUNCTION_TABLE_VERSION;
  130. FunctionTable.AddDevice = UsbCmpAddDevice;
  131. FunctionTable.DispatchStateChange = UsbCmpDispatchStateChange;
  132. FunctionTable.DispatchOpen = UsbCmpDispatchOpen;
  133. FunctionTable.DispatchClose = UsbCmpDispatchClose;
  134. FunctionTable.DispatchIo = UsbCmpDispatchIo;
  135. FunctionTable.DispatchSystemControl = UsbCmpDispatchSystemControl;
  136. Status = IoRegisterDriverFunctions(Driver, &FunctionTable);
  137. return Status;
  138. }
  139. //
  140. // --------------------------------------------------------- Internal Functions
  141. //
  142. KSTATUS
  143. UsbCmpAddDevice (
  144. PVOID Driver,
  145. PSTR DeviceId,
  146. PSTR ClassId,
  147. PSTR CompatibleIds,
  148. PVOID DeviceToken
  149. )
  150. /*++
  151. Routine Description:
  152. This routine is called when a device is detected for which the USB compound
  153. device driver acts as the function driver. The driver will attach itself to
  154. the stack.
  155. Arguments:
  156. Driver - Supplies a pointer to the driver being called.
  157. DeviceId - Supplies a pointer to a string with the device ID.
  158. ClassId - Supplies a pointer to a string containing the device's class ID.
  159. CompatibleIds - Supplies a pointer to a string containing device IDs
  160. that would be compatible with this device.
  161. DeviceToken - Supplies an opaque token that the driver can use to identify
  162. the device in the system. This token should be used when attaching to
  163. the stack.
  164. Return Value:
  165. STATUS_SUCCESS on success.
  166. Failure code if the driver was unsuccessful in attaching itself.
  167. --*/
  168. {
  169. PUSB_COMPOUND_DEVICE NewDevice;
  170. KSTATUS Status;
  171. //
  172. // Create the device context and attach to the device.
  173. //
  174. NewDevice = MmAllocatePagedPool(sizeof(USB_COMPOUND_DEVICE),
  175. USB_COMPOUND_ALLOCATION_TAG);
  176. if (NewDevice == NULL) {
  177. return STATUS_INSUFFICIENT_RESOURCES;
  178. }
  179. RtlZeroMemory(NewDevice, sizeof(USB_COMPOUND_DEVICE));
  180. NewDevice->UsbCoreHandle = INVALID_HANDLE;
  181. //
  182. // Attempt to attach to the USB core.
  183. //
  184. Status = UsbDriverAttach(DeviceToken,
  185. UsbCmpDriver,
  186. &(NewDevice->UsbCoreHandle));
  187. if (!KSUCCESS(Status)) {
  188. goto AddDeviceEnd;
  189. }
  190. ASSERT(NewDevice->UsbCoreHandle != INVALID_HANDLE);
  191. Status = IoAttachDriverToDevice(Driver, DeviceToken, NewDevice);
  192. AddDeviceEnd:
  193. if (!KSUCCESS(Status)) {
  194. if (NewDevice->UsbCoreHandle != INVALID_HANDLE) {
  195. UsbDeviceClose(NewDevice->UsbCoreHandle);
  196. }
  197. if (NewDevice != NULL) {
  198. MmFreePagedPool(NewDevice);
  199. }
  200. }
  201. return Status;
  202. }
  203. VOID
  204. UsbCmpDispatchStateChange (
  205. PIRP Irp,
  206. PVOID DeviceContext,
  207. PVOID IrpContext
  208. )
  209. /*++
  210. Routine Description:
  211. This routine handles State Change IRPs.
  212. Arguments:
  213. Irp - Supplies a pointer to the I/O request packet.
  214. DeviceContext - Supplies the context pointer supplied by the driver when it
  215. attached itself to the driver stack. Presumably this pointer contains
  216. driver-specific device context.
  217. IrpContext - Supplies the context pointer supplied by the driver when
  218. the IRP was created.
  219. Return Value:
  220. None.
  221. --*/
  222. {
  223. PUSB_COMPOUND_DEVICE Device;
  224. KSTATUS Status;
  225. ASSERT(Irp->MajorCode == IrpMajorStateChange);
  226. Device = (PUSB_COMPOUND_DEVICE)DeviceContext;
  227. //
  228. // If this is the parent device, enumerate children.
  229. //
  230. if (Device != NULL) {
  231. switch (Irp->MinorCode) {
  232. case IrpMinorQueryResources:
  233. if (Irp->Direction == IrpUp) {
  234. IoCompleteIrp(UsbCmpDriver, Irp, STATUS_SUCCESS);
  235. }
  236. break;
  237. case IrpMinorStartDevice:
  238. //
  239. // Attempt to fire the thing up if the bus has already started it.
  240. //
  241. if (Irp->Direction == IrpUp) {
  242. Status = UsbCmppStartDevice(Irp, Device);
  243. IoCompleteIrp(UsbCmpDriver, Irp, Status);
  244. }
  245. break;
  246. case IrpMinorQueryChildren:
  247. if (Irp->Direction == IrpUp) {
  248. Status = UsbCmppEnumerateChildren(Irp, Device);
  249. IoCompleteIrp(UsbCmpDriver, Irp, Status);
  250. }
  251. break;
  252. case IrpMinorRemoveDevice:
  253. if (Irp->Direction == IrpUp) {
  254. UsbCmppRemoveDevice(Irp, Device);
  255. }
  256. break;
  257. //
  258. // For all other IRPs, do nothing.
  259. //
  260. default:
  261. break;
  262. }
  263. //
  264. // If this driver is acting as the bus driver for the child device, then
  265. // simply complete things as a happy bus.
  266. //
  267. } else {
  268. switch (Irp->MinorCode) {
  269. case IrpMinorRemoveDevice:
  270. case IrpMinorQueryResources:
  271. case IrpMinorStartDevice:
  272. case IrpMinorQueryChildren:
  273. IoCompleteIrp(UsbCmpDriver, Irp, STATUS_SUCCESS);
  274. break;
  275. default:
  276. break;
  277. }
  278. }
  279. return;
  280. }
  281. VOID
  282. UsbCmpDispatchOpen (
  283. PIRP Irp,
  284. PVOID DeviceContext,
  285. PVOID IrpContext
  286. )
  287. /*++
  288. Routine Description:
  289. This routine handles Open IRPs.
  290. Arguments:
  291. Irp - Supplies a pointer to the I/O request packet.
  292. DeviceContext - Supplies the context pointer supplied by the driver when it
  293. attached itself to the driver stack. Presumably this pointer contains
  294. driver-specific device context.
  295. IrpContext - Supplies the context pointer supplied by the driver when
  296. the IRP was created.
  297. Return Value:
  298. None.
  299. --*/
  300. {
  301. return;
  302. }
  303. VOID
  304. UsbCmpDispatchClose (
  305. PIRP Irp,
  306. PVOID DeviceContext,
  307. PVOID IrpContext
  308. )
  309. /*++
  310. Routine Description:
  311. This routine handles Close IRPs.
  312. Arguments:
  313. Irp - Supplies a pointer to the I/O request packet.
  314. DeviceContext - Supplies the context pointer supplied by the driver when it
  315. attached itself to the driver stack. Presumably this pointer contains
  316. driver-specific device context.
  317. IrpContext - Supplies the context pointer supplied by the driver when
  318. the IRP was created.
  319. Return Value:
  320. None.
  321. --*/
  322. {
  323. return;
  324. }
  325. VOID
  326. UsbCmpDispatchIo (
  327. PIRP Irp,
  328. PVOID DeviceContext,
  329. PVOID IrpContext
  330. )
  331. /*++
  332. Routine Description:
  333. This routine handles I/O IRPs.
  334. Arguments:
  335. Irp - Supplies a pointer to the I/O request packet.
  336. DeviceContext - Supplies the context pointer supplied by the driver when it
  337. attached itself to the driver stack. Presumably this pointer contains
  338. driver-specific device context.
  339. IrpContext - Supplies the context pointer supplied by the driver when
  340. the IRP was created.
  341. Return Value:
  342. None.
  343. --*/
  344. {
  345. return;
  346. }
  347. VOID
  348. UsbCmpDispatchSystemControl (
  349. PIRP Irp,
  350. PVOID DeviceContext,
  351. PVOID IrpContext
  352. )
  353. /*++
  354. Routine Description:
  355. This routine handles System Control IRPs.
  356. Arguments:
  357. Irp - Supplies a pointer to the I/O request packet.
  358. DeviceContext - Supplies the context pointer supplied by the driver when it
  359. attached itself to the driver stack. Presumably this pointer contains
  360. driver-specific device context.
  361. IrpContext - Supplies the context pointer supplied by the driver when
  362. the IRP was created.
  363. Return Value:
  364. None.
  365. --*/
  366. {
  367. ASSERT(Irp->MajorCode == IrpMajorSystemControl);
  368. //
  369. // Do no processing on any IRPs. Let them flow.
  370. //
  371. return;
  372. }
  373. KSTATUS
  374. UsbCmppStartDevice (
  375. PIRP Irp,
  376. PUSB_COMPOUND_DEVICE Device
  377. )
  378. /*++
  379. Routine Description:
  380. This routine starts up the USB compound device.
  381. Arguments:
  382. Irp - Supplies a pointer to the I/O request packet.
  383. Device - Supplies a pointer to this USB compound device.
  384. Return Value:
  385. Status code.
  386. --*/
  387. {
  388. ULONG AllocationSize;
  389. PUSB_CONFIGURATION_DESCRIPTION Configuration;
  390. PLIST_ENTRY CurrentEntry;
  391. ULONG InterfaceCount;
  392. KSTATUS Status;
  393. //
  394. // If the configuration isn't yet set, set the first one.
  395. //
  396. Configuration = UsbGetActiveConfiguration(Device->UsbCoreHandle);
  397. if (Configuration == NULL) {
  398. Status = UsbSetConfiguration(Device->UsbCoreHandle, 0, TRUE);
  399. if (!KSUCCESS(Status)) {
  400. goto StartDeviceEnd;
  401. }
  402. Configuration = UsbGetActiveConfiguration(Device->UsbCoreHandle);
  403. ASSERT(Configuration != NULL);
  404. }
  405. if (Device->InterfaceCount == 0) {
  406. //
  407. // Loop through once counting the number of interfaces.
  408. //
  409. InterfaceCount = 0;
  410. CurrentEntry = Configuration->InterfaceListHead.Next;
  411. while (CurrentEntry != &(Configuration->InterfaceListHead)) {
  412. CurrentEntry = CurrentEntry->Next;
  413. InterfaceCount += 1;
  414. }
  415. if (InterfaceCount == 0) {
  416. ASSERT(FALSE);
  417. Status = STATUS_NO_INTERFACE;
  418. goto StartDeviceEnd;
  419. }
  420. //
  421. // Allocate the device pointer list.
  422. //
  423. AllocationSize = InterfaceCount * sizeof(PDEVICE);
  424. Device->Children = MmAllocatePagedPool(AllocationSize,
  425. USB_COMPOUND_ALLOCATION_TAG);
  426. if (Device->Children == NULL) {
  427. Status = STATUS_INSUFFICIENT_RESOURCES;
  428. goto StartDeviceEnd;
  429. }
  430. RtlZeroMemory(Device->Children, AllocationSize);
  431. Device->InterfaceCount = InterfaceCount;
  432. }
  433. Status = STATUS_SUCCESS;
  434. StartDeviceEnd:
  435. return Status;
  436. }
  437. KSTATUS
  438. UsbCmppEnumerateChildren (
  439. PIRP Irp,
  440. PUSB_COMPOUND_DEVICE Device
  441. )
  442. /*++
  443. Routine Description:
  444. This routine enumerates the children of the given USB compound device.
  445. Arguments:
  446. Irp - Supplies a pointer to the I/O request packet.
  447. Device - Supplies a pointer to this USB compound device.
  448. Return Value:
  449. Status code.
  450. --*/
  451. {
  452. PUSB_CONFIGURATION_DESCRIPTION Configuration;
  453. PLIST_ENTRY CurrentEntry;
  454. PUSB_INTERFACE_DESCRIPTION Interface;
  455. ULONG InterfaceIndex;
  456. KSTATUS Status;
  457. Configuration = UsbGetActiveConfiguration(Device->UsbCoreHandle);
  458. if (Configuration == NULL) {
  459. Status = STATUS_NOT_CONFIGURED;
  460. goto EnumerateChildrenEnd;
  461. }
  462. //
  463. // Loop through each child.
  464. //
  465. CurrentEntry = Configuration->InterfaceListHead.Next;
  466. for (InterfaceIndex = 0;
  467. InterfaceIndex < Device->InterfaceCount;
  468. InterfaceIndex += 1) {
  469. if (CurrentEntry == &(Configuration->InterfaceListHead)) {
  470. ASSERT(FALSE);
  471. Status = STATUS_DATA_LENGTH_MISMATCH;
  472. goto EnumerateChildrenEnd;
  473. }
  474. Interface = LIST_VALUE(CurrentEntry,
  475. USB_INTERFACE_DESCRIPTION,
  476. ListEntry);
  477. CurrentEntry = CurrentEntry->Next;
  478. //
  479. // Ask the USB core to enumerate a device for this interface.
  480. //
  481. Status = UsbEnumerateDeviceForInterface(
  482. Device->UsbCoreHandle,
  483. Interface,
  484. &(Device->Children[InterfaceIndex]));
  485. if (!KSUCCESS(Status)) {
  486. goto EnumerateChildrenEnd;
  487. }
  488. }
  489. Status = IoMergeChildArrays(Irp,
  490. Device->Children,
  491. Device->InterfaceCount,
  492. USB_COMPOUND_ALLOCATION_TAG);
  493. if (!KSUCCESS(Status)) {
  494. goto EnumerateChildrenEnd;
  495. }
  496. EnumerateChildrenEnd:
  497. return Status;
  498. }
  499. VOID
  500. UsbCmppRemoveDevice (
  501. PIRP Irp,
  502. PUSB_COMPOUND_DEVICE Device
  503. )
  504. /*++
  505. Routine Description:
  506. This routine removes the USB compound device.
  507. Arguments:
  508. Irp - Supplies a pointer to the I/O request packet.
  509. Device - Supplies a pointer to this USB compound device.
  510. Return Value:
  511. Status code.
  512. --*/
  513. {
  514. //
  515. // Detach the device from USB core's grasp. This marks it as disconnected.
  516. //
  517. UsbDetachDevice(Device->UsbCoreHandle);
  518. //
  519. // Destroy the interface device list. By the time the removal IRP reaches
  520. // the compound device driver, all of the children have already been
  521. // released. Do not interate over the pointers in this array because they
  522. // are invalid.
  523. //
  524. if (Device->Children != NULL) {
  525. MmFreePagedPool(Device->Children);
  526. }
  527. //
  528. // Release the reference taken on the USB core handle. This will clean up
  529. // the cached configurations.
  530. //
  531. UsbDeviceClose(Device->UsbCoreHandle);
  532. //
  533. // Release the USB compound device.
  534. //
  535. MmFreePagedPool(Device);
  536. return;
  537. }