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.

703 lines
20 KiB

  1. /*++
  2. Copyright (c) 1997-2000 Microsoft Corporation
  3. Module Name:
  4. io.c
  5. Abstract:
  6. This module contains the work engine for all SD card operations
  7. Authors:
  8. Neil Sandlin (neilsa) 1-Jan-2002
  9. Environment:
  10. Kernel mode
  11. Revision History :
  12. Notes:
  13. STATE DIAGRAM
  14. IDLE <-------------------+
  15. | |
  16. |--new work |--done
  17. | |
  18. v |
  19. PACKET_QUEUED <=========> IN_PROCESS <==========> WAITING_FOR_TIMER
  20. ^ |
  21. | |--interrupt
  22. +----------------------------------------------------+
  23. --*/
  24. #include "pch.h"
  25. VOID
  26. SdbusWorker(
  27. IN PFDO_EXTENSION FdoExtension,
  28. IN PSD_WORK_PACKET WorkPacket
  29. );
  30. NTSTATUS
  31. SdbusSendCmdAsync(
  32. IN PSD_WORK_PACKET WorkPacket
  33. );
  34. NTSTATUS
  35. SdbusQueueCardReset(
  36. IN PFDO_EXTENSION FdoExtension
  37. );
  38. //
  39. //
  40. //
  41. VOID
  42. SdbusQueueWorkPacket(
  43. IN PFDO_EXTENSION FdoExtension,
  44. IN PSD_WORK_PACKET WorkPacket,
  45. IN UCHAR WorkPacketType
  46. )
  47. /*++
  48. Routine Description:
  49. Queue a new work packet.
  50. Synchronization:
  51. If the worker state is anything but IDLE, then all we need to do here
  52. is queue the work packet onto the FdoExtension's queue. That is because
  53. the non-idle worker is responsible for launching a DPC for any new work
  54. coming in.
  55. If the worker is IDLE, we need to launch the DPC here.
  56. Arguments:
  57. FdoExtension - Pointer to the device object extension for the host controller
  58. WorkPacket - Pointer to the work packet
  59. Return Value:
  60. none
  61. --*/
  62. {
  63. KIRQL Irql;
  64. KeAcquireSpinLock(&FdoExtension->WorkerSpinLock, &Irql);
  65. if (FdoExtension->WorkerState != WORKER_IDLE) {
  66. switch(WorkPacketType){
  67. case WP_TYPE_SYSTEM:
  68. InsertTailList(&FdoExtension->SystemWorkPacketQueue, &WorkPacket->WorkPacketQueue);
  69. break;
  70. case WP_TYPE_SYSTEM_PRIORITY:
  71. InsertHeadList(&FdoExtension->SystemWorkPacketQueue, &WorkPacket->WorkPacketQueue);
  72. break;
  73. case WP_TYPE_IO:
  74. InsertTailList(&FdoExtension->IoWorkPacketQueue, &WorkPacket->WorkPacketQueue);
  75. break;
  76. default:
  77. ASSERT(FALSE);
  78. }
  79. } else {
  80. FdoExtension->WorkerState = PACKET_PENDING;
  81. KeInsertQueueDpc(&FdoExtension->WorkerDpc, WorkPacket, NULL);
  82. }
  83. KeReleaseSpinLock(&FdoExtension->WorkerSpinLock, Irql);
  84. }
  85. PSD_WORK_PACKET
  86. SdbusGetNextWorkPacket(
  87. IN PFDO_EXTENSION FdoExtension
  88. )
  89. /*++
  90. Routine Description:
  91. Remove a work packet from a queue
  92. Synchronization:
  93. No synchronization is needed here, it is assumed that the worker's spin lock
  94. is held when called.
  95. Arguments:
  96. FdoExtension - Pointer to the device object extension for the host controller
  97. Return Value:
  98. WorkPacket - Pointer to the work packet
  99. --*/
  100. {
  101. PSD_WORK_PACKET workPacket = NULL;
  102. PLIST_ENTRY NextEntry;
  103. if (!IsListEmpty(&FdoExtension->SystemWorkPacketQueue)) {
  104. NextEntry = RemoveHeadList(&FdoExtension->SystemWorkPacketQueue);
  105. workPacket = CONTAINING_RECORD(NextEntry, SD_WORK_PACKET, WorkPacketQueue);
  106. } else if (!IsListEmpty(&FdoExtension->IoWorkPacketQueue)) {
  107. NextEntry = RemoveHeadList(&FdoExtension->IoWorkPacketQueue);
  108. workPacket = CONTAINING_RECORD(NextEntry, SD_WORK_PACKET, WorkPacketQueue);
  109. }
  110. return workPacket;
  111. }
  112. VOID
  113. SdbusWorkerTimeoutDpc(
  114. IN PKDPC Dpc,
  115. IN PFDO_EXTENSION FdoExtension,
  116. IN PVOID SystemContext1,
  117. IN PVOID SystemContext2
  118. )
  119. /*++
  120. Routine Description:
  121. DPC entered when a timeout occurs
  122. Synchronization:
  123. There is a potential race condition between the timer DPC and a hardware interrupt
  124. from the controller. If we enter here, and manage to get the spin lock for
  125. the worker thread, it means we "beat" the hardware interrupt, which will
  126. call into SdbusPushWorkerEvent(). In that case, we can transition to IN_PROCESS,
  127. and start running the Worker.
  128. If we detect that the hardware interrupt has beat us to it, we need to exit.
  129. Arguments:
  130. FdoExtension - Pointer to the device object extension for the host controller
  131. WorkPacket - Pointer to the work packet
  132. Return Value:
  133. --*/
  134. {
  135. BOOLEAN callWorker = FALSE;
  136. DebugPrint((SDBUS_DEBUG_EVENT, "SdbusWorkerTimeoutDpc entered\n"));
  137. KeAcquireSpinLockAtDpcLevel(&FdoExtension->WorkerSpinLock);
  138. DebugPrint((SDBUS_DEBUG_EVENT, "SdbusWorkerTimeoutDpc has spinlock, WorkerState = %s\n",
  139. WORKER_STATE_STRING(FdoExtension->WorkerState)));
  140. if (FdoExtension->WorkerState == WAITING_FOR_TIMER) {
  141. callWorker = TRUE;
  142. FdoExtension->WorkerState = IN_PROCESS;
  143. }
  144. KeReleaseSpinLockFromDpcLevel(&FdoExtension->WorkerSpinLock);
  145. if (callWorker) {
  146. SdbusWorker(FdoExtension, FdoExtension->TimeoutPacket);
  147. }
  148. }
  149. VOID
  150. SdbusWorkerDpc(
  151. IN PKDPC Dpc,
  152. IN PFDO_EXTENSION FdoExtension,
  153. IN PVOID SystemContext1,
  154. IN PVOID SystemContext2
  155. )
  156. /*++
  157. Routine Description:
  158. This DPC is entered in one of three ways:
  159. 1) When new work comes in, this is launched from SdbusQueueWorkPacket()
  160. 2) When the IO worker detects new work, and pops a work packet from its queue
  161. 3) When an interrupt has cancelled a timer, and is delivering an event
  162. Synchronization:
  163. In all cases, if the worker state is PACKET_QUEUED, it means we "own" the Io
  164. worker, an can proceed to set it in process.
  165. Arguments:
  166. FdoExtension - Pointer to the device object extension for the host controller
  167. WorkPacket - Pointer to the work packet
  168. Return Value:
  169. --*/
  170. {
  171. PSD_WORK_PACKET WorkPacket = SystemContext1;
  172. BOOLEAN callWorker = FALSE;
  173. DebugPrint((SDBUS_DEBUG_EVENT, "SdbusWorkerDpc entered\n"));
  174. KeAcquireSpinLockAtDpcLevel(&FdoExtension->WorkerSpinLock);
  175. DebugPrint((SDBUS_DEBUG_EVENT, "SdbusWorkerDpc has spinlock, WorkerState = %s\n",
  176. WORKER_STATE_STRING(FdoExtension->WorkerState)));
  177. if (FdoExtension->WorkerState == PACKET_PENDING) {
  178. callWorker = TRUE;
  179. FdoExtension->WorkerState = IN_PROCESS;
  180. }
  181. KeReleaseSpinLockFromDpcLevel(&FdoExtension->WorkerSpinLock);
  182. if (callWorker) {
  183. if (!WorkPacket->PacketStarted) {
  184. //
  185. // This is the first entry to the worker for this packet. Do some
  186. // initialization
  187. //
  188. //ISSUE: should call SetFunctionType here
  189. if (!WorkPacket->DisableCardEvents) {
  190. (*(FdoExtension->FunctionBlock->EnableEvent))(FdoExtension, FdoExtension->CardEvents);
  191. }
  192. WorkPacket->PacketStarted = TRUE;
  193. }
  194. SdbusWorker(FdoExtension, WorkPacket);
  195. }
  196. }
  197. VOID
  198. SdbusPushWorkerEvent(
  199. IN PFDO_EXTENSION FdoExtension,
  200. IN ULONG EventStatus
  201. )
  202. /*++
  203. Routine Description:
  204. This routine is entered when a hardware interrupt has occurred. Here we need
  205. to cancel the timer, if set, and queue the DPC to start the worker.
  206. Synchronization:
  207. Arguments:
  208. FdoExtension - Pointer to the device object extension for the host controller
  209. EventStatus - New event
  210. Return Value:
  211. --*/
  212. {
  213. DebugPrint((SDBUS_DEBUG_EVENT, "SdbusPushWorkerEvent entered, event=%08x\n", EventStatus));
  214. KeAcquireSpinLockAtDpcLevel(&FdoExtension->WorkerSpinLock);
  215. DebugPrint((SDBUS_DEBUG_EVENT, "SdbusPushWorkerEvent has spinlock, WorkerState = %s\n",
  216. WORKER_STATE_STRING(FdoExtension->WorkerState)));
  217. FdoExtension->WorkerEventStatus |= EventStatus;
  218. if (FdoExtension->WorkerState == WAITING_FOR_TIMER) {
  219. FdoExtension->WorkerState = PACKET_PENDING;
  220. KeCancelTimer(&FdoExtension->WorkerTimer);
  221. KeInsertQueueDpc(&FdoExtension->WorkerDpc, FdoExtension->TimeoutPacket, NULL);
  222. }
  223. KeReleaseSpinLockFromDpcLevel(&FdoExtension->WorkerSpinLock);
  224. }
  225. BOOLEAN
  226. SdbusHasRequiredEventFired(
  227. IN PFDO_EXTENSION FdoExtension,
  228. IN PSD_WORK_PACKET workPacket
  229. )
  230. /*++
  231. Routine Description:
  232. This routine checks for a hardware event, and rolls it into the workpacket.
  233. Note that this has the side effect of CLEARING the corresponding required event
  234. bits in the workpacket.
  235. Arguments:
  236. FdoExtension - device extension for the SD host controller
  237. WorkPacket - defines the current SD operation in progress
  238. Return Value:
  239. TRUE if the required event in the workpacket has fired
  240. --*/
  241. {
  242. BOOLEAN bRet = FALSE;
  243. //
  244. // pull the latest event status
  245. //
  246. KeAcquireSpinLockAtDpcLevel(&FdoExtension->WorkerSpinLock);
  247. workPacket->EventStatus |= FdoExtension->WorkerEventStatus;
  248. FdoExtension->WorkerEventStatus = 0;
  249. KeReleaseSpinLockFromDpcLevel(&FdoExtension->WorkerSpinLock);
  250. if ((workPacket->EventStatus & workPacket->RequiredEvent) != 0) {
  251. bRet = TRUE;
  252. workPacket->EventStatus &= ~workPacket->RequiredEvent;
  253. }
  254. return bRet;
  255. }
  256. VOID
  257. SdbusWorker(
  258. IN PFDO_EXTENSION FdoExtension,
  259. IN PSD_WORK_PACKET workPacket
  260. )
  261. /*++
  262. Routine Description:
  263. IO worker - This is the main entry point for the IO engine. The purpose of this
  264. routine is to run the individual units of work defined by a single work packet, and
  265. provide waits between units.
  266. This routine runs at DPC level.
  267. Arguments:
  268. FdoExtension - device extension for the SD host controller
  269. WorkPacket - defines the current SD operation in progress
  270. Return Value:
  271. None
  272. --*/
  273. {
  274. NTSTATUS status;
  275. PIRP irp;
  276. static ULONG ioCount = 0;
  277. DebugPrint((SDBUS_DEBUG_WORKENG, "IOW: fdo %08x workpacket %08x\n",
  278. FdoExtension->DeviceObject, workPacket));
  279. try{
  280. if (workPacket->RequiredEvent) {
  281. //
  282. // See if an event we are interested in has occurred
  283. //
  284. if (!SdbusHasRequiredEventFired(FdoExtension, workPacket)) {
  285. //
  286. // We are waiting for an event, but it hasn't happened yet
  287. //
  288. if (workPacket->Retries == 0) {
  289. DebugPrint((SDBUS_DEBUG_FAIL, "IOW: EventStatus %08x missing %08x, ABORTING!\n",
  290. workPacket->EventStatus, workPacket->RequiredEvent));
  291. SdbusDumpDbgLog();
  292. status = STATUS_UNSUCCESSFUL;
  293. ASSERT(NT_SUCCESS(status));
  294. leave;
  295. }
  296. DebugPrint((SDBUS_DEBUG_WORKENG, "IOW: EventStatus %08x missing %08x, waiting...\n",
  297. workPacket->EventStatus, workPacket->RequiredEvent));
  298. workPacket->Retries--;
  299. status = STATUS_MORE_PROCESSING_REQUIRED;
  300. leave;
  301. }
  302. //
  303. // Event has occurred, so fall through and begin processing
  304. //
  305. }
  306. while(TRUE) {
  307. status = (*(FdoExtension->FunctionBlock->CheckStatus))(FdoExtension);
  308. if (!NT_SUCCESS(status)) {
  309. if (workPacket->FunctionPhaseOnError) {
  310. //
  311. // here the worker miniproc has specified it can handle errors.
  312. // Just give it one shot to clean up and exit
  313. //
  314. ASSERT(workPacket->WorkerMiniProc);
  315. workPacket->FunctionPhase = workPacket->FunctionPhaseOnError;
  316. status = (*(workPacket->WorkerMiniProc))(workPacket);
  317. leave;
  318. } else {
  319. DebugPrint((SDBUS_DEBUG_FAIL, "IOW: ErrorStatus %08x, unhandled error!\n", status));
  320. SdbusDumpDbgLog();
  321. ASSERT(NT_SUCCESS(status));
  322. // SdbusQueueCardReset(FdoExtension);
  323. leave;
  324. }
  325. }
  326. DebugPrint((SDBUS_DEBUG_WORKENG, "fdo %08x sdwp %08x IOW start - func %s phase %d\n",
  327. FdoExtension->DeviceObject, workPacket, WP_FUNC_STRING(workPacket->Function), workPacket->FunctionPhase));
  328. workPacket->DelayTime = 0;
  329. //
  330. // Call the mini proc
  331. //
  332. if (workPacket->ExecutingSDCommand) {
  333. status = SdbusSendCmdAsync(workPacket);
  334. } else {
  335. if (workPacket->WorkerMiniProc) {
  336. status = (*(workPacket->WorkerMiniProc))(workPacket);
  337. } else {
  338. //
  339. // no miniproc - this must be the end of a synchronous command
  340. //
  341. status = STATUS_SUCCESS;
  342. }
  343. }
  344. DebugPrint((SDBUS_DEBUG_WORKENG, "fdo %08x IOW end - func %s ph%d st=%08x to=%08x\n",
  345. FdoExtension->DeviceObject, WP_FUNC_STRING(workPacket->Function), workPacket->FunctionPhase, status, workPacket->DelayTime));
  346. if (workPacket->ExecutingSDCommand && NT_SUCCESS(status)) {
  347. //
  348. // We've reached the successful end of an individual SD command, so
  349. // iterate back to the normal MiniProc handler
  350. //
  351. workPacket->ExecutingSDCommand = FALSE;
  352. continue;
  353. }
  354. if (status != STATUS_MORE_PROCESSING_REQUIRED) {
  355. //
  356. // done for now
  357. //
  358. leave;
  359. }
  360. if (workPacket->DelayTime) {
  361. //
  362. // miniproc requested a wait... check to see if event is also required
  363. //
  364. if (workPacket->RequiredEvent && SdbusHasRequiredEventFired(FdoExtension, workPacket)) {
  365. //
  366. // event fired as we were processing the command... pre-empt the
  367. // delay and just continue back to the miniproc
  368. //
  369. continue;
  370. }
  371. //
  372. // go off to do the delay
  373. //
  374. leave;
  375. }
  376. }
  377. } finally {
  378. if (status == STATUS_MORE_PROCESSING_REQUIRED) {
  379. ASSERT(workPacket->DelayTime);
  380. ASSERT(FdoExtension->WorkerState == IN_PROCESS);
  381. //
  382. // At this point, we will now want to schedule a reentry.
  383. // If the hardware routine has already passed new status, then just queue
  384. // a DPC immediately
  385. //
  386. KeAcquireSpinLockAtDpcLevel(&FdoExtension->WorkerSpinLock);
  387. if ((FdoExtension->WorkerEventStatus) || (workPacket->DelayTime == 0)) {
  388. FdoExtension->WorkerState = PACKET_PENDING;
  389. KeInsertQueueDpc(&FdoExtension->WorkerDpc, workPacket, NULL);
  390. } else {
  391. LARGE_INTEGER dueTime;
  392. FdoExtension->WorkerState = WAITING_FOR_TIMER;
  393. FdoExtension->TimeoutPacket = workPacket;
  394. DebugPrint((SDBUS_DEBUG_WORKENG, "fdo %.08x sdwp %08x Worker Delay %08x\n",
  395. FdoExtension->DeviceObject, workPacket, workPacket->DelayTime));
  396. dueTime.QuadPart = -((LONG) workPacket->DelayTime*10);
  397. KeSetTimer(&FdoExtension->WorkerTimer, dueTime, &FdoExtension->WorkerTimeoutDpc);
  398. }
  399. KeReleaseSpinLockFromDpcLevel(&FdoExtension->WorkerSpinLock);
  400. } else {
  401. PSD_WORK_PACKET chainedWorkPacket = workPacket->NextWorkPacketInChain;
  402. //
  403. // The worker is done with the current work packet.
  404. //
  405. DebugPrint((SDBUS_DEBUG_WORKENG, "fdo %08x sdwp %08x Worker %s - COMPLETE %08x\n",
  406. FdoExtension->DeviceObject, workPacket, WP_FUNC_STRING(workPacket->Function), status));
  407. (*(FdoExtension->FunctionBlock->DisableEvent))(FdoExtension, FdoExtension->CardEvents);
  408. (*(workPacket->CompletionRoutine))(workPacket, status);
  409. // The workpacket should have been freed by the completion routine,
  410. // so at this point, the contents of workPacket are not reliable
  411. workPacket = NULL;
  412. KeAcquireSpinLockAtDpcLevel(&FdoExtension->WorkerSpinLock);
  413. if (chainedWorkPacket) {
  414. workPacket = chainedWorkPacket;
  415. } else {
  416. workPacket = SdbusGetNextWorkPacket(FdoExtension);
  417. }
  418. if (workPacket) {
  419. FdoExtension->WorkerState = PACKET_PENDING;
  420. KeInsertQueueDpc(&FdoExtension->WorkerDpc, workPacket, NULL);
  421. } else {
  422. FdoExtension->WorkerState = WORKER_IDLE;
  423. }
  424. KeReleaseSpinLockFromDpcLevel(&FdoExtension->WorkerSpinLock);
  425. }
  426. }
  427. }
  428. NTSTATUS
  429. SdbusSendCmdAsync(
  430. IN PSD_WORK_PACKET WorkPacket
  431. )
  432. /*++
  433. Routine Description:
  434. This routine is the "worker within the worker" for the operation of the "MiniProc"
  435. worker routines. Take any miniproc, for example, the one that handles memory block
  436. operations for an SD storage card. That miniproc directs the high level sequence for
  437. reading/writing sectors to the card. For each individual SD command that makes up that
  438. sequence, the work engine will "drop out" of the miniproc, and come here to handle
  439. the task of completing that SD command.
  440. Arguments:
  441. WorkPacket - defines the current SD operation in progress
  442. Return value:
  443. status
  444. --*/
  445. {
  446. PFDO_EXTENSION FdoExtension = WorkPacket->FdoExtension;
  447. NTSTATUS status;
  448. DebugPrint((SDBUS_DEBUG_DEVICE, "SEND async: Phase(%d) Cmd%d (0x%02x) %08x\n",
  449. WorkPacket->CmdPhase, WorkPacket->Cmd, WorkPacket->Cmd, WorkPacket->Argument));
  450. switch(WorkPacket->CmdPhase) {
  451. case 0:
  452. WorkPacket->Retries = 5;
  453. WorkPacket->DelayTime = 1000;
  454. WorkPacket->RequiredEvent = SDBUS_EVENT_CARD_RESPONSE;
  455. (*(FdoExtension->FunctionBlock->SendSDCommand))(FdoExtension, WorkPacket);
  456. WorkPacket->CmdPhase++;
  457. status = STATUS_MORE_PROCESSING_REQUIRED;
  458. break;
  459. case 1:
  460. WorkPacket->RequiredEvent = 0;
  461. status = (*(FdoExtension->FunctionBlock->GetSDResponse))(FdoExtension, WorkPacket);
  462. break;
  463. default:
  464. ASSERT(FALSE);
  465. status = STATUS_UNSUCCESSFUL;
  466. }
  467. DebugPrint((SDBUS_DEBUG_DEVICE, "SEND async: Exit Cmd%d (0x%02x) status %08x\n",
  468. WorkPacket->Cmd, WorkPacket->Cmd, status));
  469. #if DBG
  470. if (NT_SUCCESS(status)) {
  471. DebugDumpSdResponse(WorkPacket->ResponseBuffer, WorkPacket->ResponseType);
  472. }
  473. #endif
  474. return status;
  475. }
  476. NTSTATUS
  477. SdbusQueueCardReset(
  478. IN PFDO_EXTENSION FdoExtension
  479. )
  480. {
  481. DebugPrint((SDBUS_DEBUG_FAIL, "IOW: QueueCardReset NOT IMPLEMENTED!\n"));
  482. return STATUS_SUCCESS;
  483. }