Source code of Windows XP (NT5)
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.

575 lines
15 KiB

  1. /*++
  2. Copyright (c) 1989 Microsoft Corporation
  3. Module Name:
  4. devquobj.c
  5. Abstract:
  6. This module implements the kernel device queue object. Functions are
  7. provided to initialize a device queue object and to insert and remove
  8. device queue entries in a device queue object.
  9. Author:
  10. David N. Cutler (davec) 1-Apr-1989
  11. Environment:
  12. Kernel mode only.
  13. Revision History:
  14. --*/
  15. #include "ki.h"
  16. //
  17. // The following assert macro is used to check that an input device queue
  18. // is really a kdevice_queue and not something else, like deallocated pool.
  19. //
  20. #define ASSERT_DEVICE_QUEUE(E) { \
  21. ASSERT((E)->Type == DeviceQueueObject); \
  22. }
  23. VOID
  24. KeInitializeDeviceQueue (
  25. IN PKDEVICE_QUEUE DeviceQueue
  26. )
  27. /*++
  28. Routine Description:
  29. This function initializes a kernel device queue object.
  30. Arguments:
  31. DeviceQueue - Supplies a pointer to a control object of type device
  32. queue.
  33. SpinLock - Supplies a pointer to an executive spin lock.
  34. Return Value:
  35. None.
  36. --*/
  37. {
  38. //
  39. // Initialize standard control object header.
  40. //
  41. DeviceQueue->Type = DeviceQueueObject;
  42. DeviceQueue->Size = sizeof(KDEVICE_QUEUE);
  43. //
  44. // Initialize the device queue list head, spin lock, and busy indicator.
  45. //
  46. InitializeListHead(&DeviceQueue->DeviceListHead);
  47. KeInitializeSpinLock(&DeviceQueue->Lock);
  48. DeviceQueue->Busy = FALSE;
  49. return;
  50. }
  51. BOOLEAN
  52. KeInsertDeviceQueue (
  53. IN PKDEVICE_QUEUE DeviceQueue,
  54. IN PKDEVICE_QUEUE_ENTRY DeviceQueueEntry
  55. )
  56. /*++
  57. Routine Description:
  58. This function inserts a device queue entry at the tail of the specified
  59. device queue. If the device is not busy, then it is set busy and the entry
  60. is not placed in the device queue. Otherwise the specified entry is placed
  61. at the end of the device queue.
  62. N.B. This function can only be called from DISPATCH_LEVEL.
  63. Arguments:
  64. DeviceQueue - Supplies a pointer to a control object of type device queue.
  65. DeviceQueueEntry - Supplies a pointer to a device queue entry.
  66. Return Value:
  67. If the device is not busy, then a value of FALSE is returned. Otherwise a
  68. value of TRUE is returned.
  69. --*/
  70. {
  71. BOOLEAN Inserted;
  72. KLOCK_QUEUE_HANDLE LockHandle;
  73. ASSERT_DEVICE_QUEUE(DeviceQueue);
  74. ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
  75. //
  76. // Lock specified device queue.
  77. //
  78. KeAcquireInStackQueuedSpinLockAtDpcLevel(&DeviceQueue->Lock, &LockHandle);
  79. //
  80. // Insert the specified device queue entry at the end of the device queue
  81. // if the device queue is busy. Otherwise set the device queue busy and
  82. // don't insert the device queue entry.
  83. //
  84. if (DeviceQueue->Busy == TRUE) {
  85. Inserted = TRUE;
  86. InsertTailList(&DeviceQueue->DeviceListHead,
  87. &DeviceQueueEntry->DeviceListEntry);
  88. } else {
  89. DeviceQueue->Busy = TRUE;
  90. Inserted = FALSE;
  91. }
  92. DeviceQueueEntry->Inserted = Inserted;
  93. //
  94. // Unlock specified device queue.
  95. //
  96. KeReleaseInStackQueuedSpinLockFromDpcLevel(&LockHandle);
  97. return Inserted;
  98. }
  99. BOOLEAN
  100. KeInsertByKeyDeviceQueue (
  101. IN PKDEVICE_QUEUE DeviceQueue,
  102. IN PKDEVICE_QUEUE_ENTRY DeviceQueueEntry,
  103. IN ULONG SortKey
  104. )
  105. /*++
  106. Routine Description:
  107. This function inserts a device queue entry into the specified device
  108. queue according to a sort key. If the device is not busy, then it is
  109. set busy and the entry is not placed in the device queue. Otherwise
  110. the specified entry is placed in the device queue at a position such
  111. that the specified sort key is greater than or equal to its predecessor
  112. and less than its successor.
  113. N.B. This function can only be called from DISPATCH_LEVEL.
  114. Arguments:
  115. DeviceQueue - Supplies a pointer to a control object of type device queue.
  116. DeviceQueueEntry - Supplies a pointer to a device queue entry.
  117. SortKey - Supplies the sort key by which the position to insert the device
  118. queue entry is to be determined.
  119. Return Value:
  120. If the device is not busy, then a value of FALSE is returned. Otherwise a
  121. value of TRUE is returned.
  122. --*/
  123. {
  124. BOOLEAN Inserted;
  125. KLOCK_QUEUE_HANDLE LockHandle;
  126. PLIST_ENTRY NextEntry;
  127. PKDEVICE_QUEUE_ENTRY QueueEntry;
  128. ASSERT_DEVICE_QUEUE(DeviceQueue);
  129. ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
  130. //
  131. // Lock specified device queue.
  132. //
  133. KeAcquireInStackQueuedSpinLockAtDpcLevel(&DeviceQueue->Lock, &LockHandle);
  134. //
  135. // Insert the specified device queue entry in the device queue at the
  136. // position specified by the sort key if the device queue is busy.
  137. // Otherwise set the device queue busy an don't insert the device queue
  138. // entry.
  139. //
  140. DeviceQueueEntry->SortKey = SortKey;
  141. if (DeviceQueue->Busy == TRUE) {
  142. Inserted = TRUE;
  143. NextEntry = DeviceQueue->DeviceListHead.Flink;
  144. while (NextEntry != &DeviceQueue->DeviceListHead) {
  145. QueueEntry = CONTAINING_RECORD(NextEntry,
  146. KDEVICE_QUEUE_ENTRY,
  147. DeviceListEntry);
  148. if (SortKey < QueueEntry->SortKey) {
  149. break;
  150. }
  151. NextEntry = NextEntry->Flink;
  152. }
  153. NextEntry = NextEntry->Blink;
  154. InsertHeadList(NextEntry, &DeviceQueueEntry->DeviceListEntry);
  155. } else {
  156. DeviceQueue->Busy = TRUE;
  157. Inserted = FALSE;
  158. }
  159. DeviceQueueEntry->Inserted = Inserted;
  160. //
  161. // Unlock specified device queue.
  162. //
  163. KeReleaseInStackQueuedSpinLockFromDpcLevel(&LockHandle);
  164. return Inserted;
  165. }
  166. PKDEVICE_QUEUE_ENTRY
  167. KeRemoveDeviceQueue (
  168. IN PKDEVICE_QUEUE DeviceQueue
  169. )
  170. /*++
  171. Routine Description:
  172. This function removes an entry from the head of the specified device
  173. queue. If the device queue is empty, then the device is set Not-Busy
  174. and a NULL pointer is returned. Otherwise the next entry is removed
  175. from the head of the device queue and the address of device queue entry
  176. is returned.
  177. N.B. This function can only be called from DISPATCH_LEVEL.
  178. Arguments:
  179. DeviceQueue - Supplies a pointer to a control object of type device queue.
  180. Return Value:
  181. A NULL pointer is returned if the device queue is empty. Otherwise a
  182. pointer to a device queue entry is returned.
  183. --*/
  184. {
  185. PKDEVICE_QUEUE_ENTRY DeviceQueueEntry;
  186. KLOCK_QUEUE_HANDLE LockHandle;
  187. PLIST_ENTRY NextEntry;
  188. ASSERT_DEVICE_QUEUE(DeviceQueue);
  189. ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
  190. //
  191. // Lock specified device queue.
  192. //
  193. KeAcquireInStackQueuedSpinLockAtDpcLevel(&DeviceQueue->Lock, &LockHandle);
  194. //
  195. // If the device queue is not empty, then remove the first entry from
  196. // the queue. Otherwise set the device queue not busy.
  197. //
  198. ASSERT(DeviceQueue->Busy == TRUE);
  199. if (IsListEmpty(&DeviceQueue->DeviceListHead) == TRUE) {
  200. DeviceQueue->Busy = FALSE;
  201. DeviceQueueEntry = NULL;
  202. } else {
  203. NextEntry = RemoveHeadList(&DeviceQueue->DeviceListHead);
  204. DeviceQueueEntry = CONTAINING_RECORD(NextEntry,
  205. KDEVICE_QUEUE_ENTRY,
  206. DeviceListEntry);
  207. DeviceQueueEntry->Inserted = FALSE;
  208. }
  209. //
  210. // Unlock specified device queue and return address of device queue
  211. // entry.
  212. //
  213. KeReleaseInStackQueuedSpinLockFromDpcLevel(&LockHandle);
  214. return DeviceQueueEntry;
  215. }
  216. PKDEVICE_QUEUE_ENTRY
  217. KeRemoveByKeyDeviceQueue (
  218. IN PKDEVICE_QUEUE DeviceQueue,
  219. IN ULONG SortKey
  220. )
  221. /*++
  222. Routine Description:
  223. This function removes an entry from the specified device
  224. queue. If the device queue is empty, then the device is set Not-Busy
  225. and a NULL pointer is returned. Otherwise the an entry is removed
  226. from the device queue and the address of device queue entry
  227. is returned. The queue is search for the first entry which has a value
  228. greater than or equal to the SortKey. If no such entry is found then the
  229. first entry of the queue is returned.
  230. N.B. This function can only be called from DISPATCH_LEVEL.
  231. Arguments:
  232. DeviceQueue - Supplies a pointer to a control object of type device queue.
  233. SortKey - Supplies the sort key by which the position to remove the device
  234. queue entry is to be determined.
  235. Return Value:
  236. A NULL pointer is returned if the device queue is empty. Otherwise a
  237. pointer to a device queue entry is returned.
  238. --*/
  239. {
  240. PKDEVICE_QUEUE_ENTRY DeviceQueueEntry;
  241. KLOCK_QUEUE_HANDLE LockHandle;
  242. PLIST_ENTRY NextEntry;
  243. ASSERT_DEVICE_QUEUE(DeviceQueue);
  244. ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
  245. //
  246. // Lock specified device queue.
  247. //
  248. KeAcquireInStackQueuedSpinLockAtDpcLevel(&DeviceQueue->Lock, &LockHandle);
  249. //
  250. // If the device queue is not empty, then remove the first entry from
  251. // the queue. Otherwise set the device queue not busy.
  252. //
  253. ASSERT(DeviceQueue->Busy == TRUE);
  254. if (IsListEmpty(&DeviceQueue->DeviceListHead) == TRUE) {
  255. DeviceQueue->Busy = FALSE;
  256. DeviceQueueEntry = NULL;
  257. } else {
  258. NextEntry = DeviceQueue->DeviceListHead.Flink;
  259. while (NextEntry != &DeviceQueue->DeviceListHead) {
  260. DeviceQueueEntry = CONTAINING_RECORD(NextEntry,
  261. KDEVICE_QUEUE_ENTRY,
  262. DeviceListEntry);
  263. if (SortKey <= DeviceQueueEntry->SortKey) {
  264. break;
  265. }
  266. NextEntry = NextEntry->Flink;
  267. }
  268. if (NextEntry != &DeviceQueue->DeviceListHead) {
  269. RemoveEntryList(&DeviceQueueEntry->DeviceListEntry);
  270. } else {
  271. NextEntry = RemoveHeadList(&DeviceQueue->DeviceListHead);
  272. DeviceQueueEntry = CONTAINING_RECORD(NextEntry,
  273. KDEVICE_QUEUE_ENTRY,
  274. DeviceListEntry);
  275. }
  276. DeviceQueueEntry->Inserted = FALSE;
  277. }
  278. //
  279. // Unlock specified device queue and return address of device queue
  280. // entry.
  281. //
  282. KeReleaseInStackQueuedSpinLockFromDpcLevel(&LockHandle);
  283. return DeviceQueueEntry;
  284. }
  285. PKDEVICE_QUEUE_ENTRY
  286. KeRemoveByKeyDeviceQueueIfBusy (
  287. IN PKDEVICE_QUEUE DeviceQueue,
  288. IN ULONG SortKey
  289. )
  290. /*++
  291. Routine Description:
  292. This function removes an entry from the specified device queue if and
  293. only if the device is currently busy. If the device queue is empty or
  294. the device is not busy, then the device is set Not-Busy and a NULL is
  295. returned. Otherwise, an entry is removed from the device queue and the
  296. address of device queue entry is returned. The queue is search for the
  297. first entry which has a value greater than or equal to the SortKey. If
  298. no such entry is found then the first entry of the queue is returned.
  299. N.B. This function can only be called from DISPATCH_LEVEL.
  300. Arguments:
  301. DeviceQueue - Supplies a pointer to a control object of type device queue.
  302. SortKey - Supplies the sort key by which the position to remove the device
  303. queue entry is to be determined.
  304. Return Value:
  305. A NULL pointer is returned if the device queue is empty. Otherwise a
  306. pointer to a device queue entry is returned.
  307. --*/
  308. {
  309. PKDEVICE_QUEUE_ENTRY DeviceQueueEntry;
  310. KLOCK_QUEUE_HANDLE LockHandle;
  311. PLIST_ENTRY NextEntry;
  312. ASSERT_DEVICE_QUEUE(DeviceQueue);
  313. ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
  314. //
  315. // Lock specified device queue.
  316. //
  317. KeAcquireInStackQueuedSpinLockAtDpcLevel(&DeviceQueue->Lock, &LockHandle);
  318. //
  319. // If the device queue is busy, then attempt to remove an entry from
  320. // the queue using the sort key. Otherwise, set the device queue not
  321. // busy.
  322. //
  323. if (DeviceQueue->Busy != FALSE) {
  324. if (IsListEmpty(&DeviceQueue->DeviceListHead) != FALSE) {
  325. DeviceQueue->Busy = FALSE;
  326. DeviceQueueEntry = NULL;
  327. } else {
  328. NextEntry = DeviceQueue->DeviceListHead.Flink;
  329. while (NextEntry != &DeviceQueue->DeviceListHead) {
  330. DeviceQueueEntry = CONTAINING_RECORD(NextEntry,
  331. KDEVICE_QUEUE_ENTRY,
  332. DeviceListEntry);
  333. if (SortKey <= DeviceQueueEntry->SortKey) {
  334. break;
  335. }
  336. NextEntry = NextEntry->Flink;
  337. }
  338. if (NextEntry != &DeviceQueue->DeviceListHead) {
  339. RemoveEntryList(&DeviceQueueEntry->DeviceListEntry);
  340. } else {
  341. NextEntry = RemoveHeadList(&DeviceQueue->DeviceListHead);
  342. DeviceQueueEntry = CONTAINING_RECORD(NextEntry,
  343. KDEVICE_QUEUE_ENTRY,
  344. DeviceListEntry);
  345. }
  346. DeviceQueueEntry->Inserted = FALSE;
  347. }
  348. } else {
  349. DeviceQueueEntry = NULL;
  350. }
  351. //
  352. // Unlock specified device queue and return address of device queue
  353. // entry.
  354. //
  355. KeReleaseInStackQueuedSpinLockFromDpcLevel(&LockHandle);
  356. return DeviceQueueEntry;
  357. }
  358. BOOLEAN
  359. KeRemoveEntryDeviceQueue (
  360. IN PKDEVICE_QUEUE DeviceQueue,
  361. IN PKDEVICE_QUEUE_ENTRY DeviceQueueEntry
  362. )
  363. /*++
  364. Routine Description:
  365. This function removes a specified entry from the the specified device
  366. queue. If the device queue entry is not in the device queue, then no
  367. operation is performed. Otherwise the specified device queue entry is
  368. removed from the device queue and its inserted status is set to FALSE.
  369. Arguments:
  370. DeviceQueue - Supplies a pointer to a control object of type device queue.
  371. DeviceQueueEntry - Supplies a pointer to a device queue entry which is to
  372. be removed from its device queue.
  373. Return Value:
  374. A value of TRUE is returned if the device queue entry is removed from its
  375. device queue. Otherwise a value of FALSE is returned.
  376. --*/
  377. {
  378. KLOCK_QUEUE_HANDLE LockHandle;
  379. BOOLEAN Removed;
  380. ASSERT_DEVICE_QUEUE(DeviceQueue);
  381. ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
  382. //
  383. // Raise IRQL to dispatcher level and lock specified device queue.
  384. //
  385. KeAcquireInStackQueuedSpinLock(&DeviceQueue->Lock, &LockHandle);
  386. //
  387. // If the device queue entry is not in a device queue, then no operation
  388. // is performed. Otherwise remove the specified device queue entry from its
  389. // device queue.
  390. //
  391. Removed = DeviceQueueEntry->Inserted;
  392. if (Removed == TRUE) {
  393. DeviceQueueEntry->Inserted = FALSE;
  394. RemoveEntryList(&DeviceQueueEntry->DeviceListEntry);
  395. }
  396. //
  397. // Unlock specified device queue, lower IRQL to its previous level, and
  398. // return whether the device queue entry was removed from its queue.
  399. //
  400. KeReleaseInStackQueuedSpinLock(&LockHandle);
  401. return Removed;
  402. }