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.

1845 lines
51 KiB

  1. /*++
  2. Copyright (c) 1997-2000 Microsoft Corporation
  3. Module Name:
  4. socket.c
  5. Abstract:
  6. This module contains the socket functions of the pcmcia driver
  7. Author:
  8. Neil Sandlin (neilsa) 3-Mar-1999
  9. Environment:
  10. Kernel mode
  11. Revision History :
  12. --*/
  13. #include "pch.h"
  14. //
  15. // Internal References
  16. //
  17. NTSTATUS
  18. PcmciaQueueSocketPowerRequest(
  19. IN PSOCKET Socket,
  20. IN UCHAR InitialState,
  21. IN PPCMCIA_COMPLETION_ROUTINE PowerCompletionRoutine,
  22. IN PVOID Context
  23. );
  24. VOID
  25. PcmciaConfigurationWorkerInitialization(
  26. PPDO_EXTENSION pdoExtension
  27. );
  28. NTSTATUS
  29. PcmciaConfigurePcCardMemIoWindows(
  30. IN PSOCKET Socket,
  31. IN PSOCKET_CONFIGURATION SocketConfig
  32. );
  33. NTSTATUS
  34. PcmciaConfigurePcCardIrq(
  35. IN PSOCKET Socket,
  36. IN PSOCKET_CONFIGURATION SocketConfig
  37. );
  38. NTSTATUS
  39. PcmciaConfigurePcCardRegisters(
  40. PPDO_EXTENSION pdoExtension
  41. );
  42. VOID
  43. PcmciaConfigureModemHack(
  44. IN PSOCKET Socket,
  45. IN PSOCKET_CONFIGURATION SocketConfig
  46. );
  47. BOOLEAN
  48. PcmciaProcessConfigureRequest(
  49. IN PFDO_EXTENSION DeviceExtension,
  50. IN PSOCKET Socket,
  51. IN PCARD_REQUEST CardConfigurationRequest
  52. );
  53. #ifdef ALLOC_PRAGMA
  54. #pragma alloc_text(PAGE, PcmciaGetConfigData)
  55. #endif
  56. NTSTATUS
  57. PcmciaRequestSocketPower(
  58. IN PPDO_EXTENSION PdoExtension,
  59. IN PPCMCIA_COMPLETION_ROUTINE PowerCompletionRoutine
  60. )
  61. /*++
  62. Routine Description:
  63. This routine maintains a reference count of how many devices have requested power.
  64. When the count increments from zero to one, a call is made to actually turn power
  65. on.
  66. Arguments:
  67. Socket - Pointer to the socket for which power is to be applied
  68. PowerCompletionRoutine - routine to be called after configuration is complete
  69. Return value:
  70. status
  71. --*/
  72. {
  73. NTSTATUS status = STATUS_SUCCESS;
  74. PSOCKET socket = PdoExtension->Socket;
  75. PFDO_EXTENSION fdoExtension = socket->DeviceExtension;
  76. DebugPrint((PCMCIA_DEBUG_SOCKET, "skt %08x request power\n", socket));
  77. if (PCMCIA_TEST_AND_SET(&PdoExtension->SocketPowerRequested)) {
  78. if (InterlockedIncrement(&socket->PowerRequests) == 1) {
  79. DebugPrint((PCMCIA_DEBUG_SOCKET, "skt %08x power requests now %d, status %08x\n", socket, socket->PowerRequests));
  80. status = PcmciaSetSocketPower(socket, PowerCompletionRoutine, PdoExtension, TRUE);
  81. }
  82. }
  83. return status;
  84. }
  85. NTSTATUS
  86. PcmciaReleaseSocketPower(
  87. IN PPDO_EXTENSION PdoExtension,
  88. IN PPCMCIA_COMPLETION_ROUTINE PowerCompletionRoutine
  89. )
  90. /*++
  91. Routine Description:
  92. This routine maintains a reference count of how many devices have requested power.
  93. When the count decrements from one to zero, a call is made to actually turn power
  94. off.
  95. Arguments:
  96. Socket - Pointer to the socket for which power is to be removed
  97. PowerCompletionRoutine - routine to be called after configuration is complete
  98. Return value:
  99. status
  100. --*/
  101. {
  102. NTSTATUS status = STATUS_SUCCESS;
  103. PSOCKET socket = PdoExtension->Socket;
  104. PFDO_EXTENSION fdoExtension = socket->DeviceExtension;
  105. DebugPrint((PCMCIA_DEBUG_SOCKET, "skt %08x release power\n", socket));
  106. if (PCMCIA_TEST_AND_RESET(&PdoExtension->SocketPowerRequested)) {
  107. if (InterlockedDecrement(&socket->PowerRequests) == 0) {
  108. DebugPrint((PCMCIA_DEBUG_SOCKET, "skt %08x power requests now %d, status %08x\n", socket, socket->PowerRequests));
  109. //
  110. // Never actually turn off the drive rails for cardbus functions since
  111. // we don't have tight integration with pci.sys, and config space will
  112. // disappear
  113. //
  114. if (!IsCardBusCardInSocket(socket)) {
  115. status = PcmciaSetSocketPower(socket, PowerCompletionRoutine, PdoExtension, FALSE);
  116. }
  117. }
  118. ASSERT(socket->PowerRequests >= 0);
  119. }
  120. return status;
  121. }
  122. NTSTATUS
  123. PcmciaSetSocketPower(
  124. IN PSOCKET Socket,
  125. IN PPCMCIA_COMPLETION_ROUTINE PowerCompletionRoutine,
  126. IN PVOID Context,
  127. IN BOOLEAN PowerOn
  128. )
  129. /*++
  130. Routine Description:
  131. This routine is entered when we know the power state of the socket will
  132. actually be set.
  133. NOTE: If this routine is called at less than DISPATCH_LEVEL, then the call
  134. will complete (not return STATUS_PENDING). If this routine is called at
  135. DISPATCH_LEVEL or greater, this routine returns STATUS_PENDING, and completes
  136. the power process using a KTIMER.
  137. Arguments:
  138. Socket - Pointer to the socket for which power is to be removed
  139. PowerCompletionRoutine - routine to be called after configuration is complete
  140. Return value:
  141. status
  142. --*/
  143. {
  144. NTSTATUS status;
  145. PFDO_EXTENSION fdoExtension = Socket->DeviceExtension;
  146. SPW_STATE InitialState = PowerOn ? SPW_RequestPower : SPW_ReleasePower;
  147. DebugPrint((PCMCIA_DEBUG_SOCKET, "skt %08x set power %s\n", Socket, PowerOn ? "ON" : "OFF"));
  148. if (!PCMCIA_TEST_AND_SET(&Socket->WorkerBusy)) {
  149. return STATUS_DEVICE_BUSY;
  150. }
  151. ASSERT(Socket->WorkerState == SPW_Stopped);
  152. //
  153. // committed, will enter SocketPowerWorker now
  154. //
  155. Socket->WorkerState = InitialState;
  156. Socket->PowerCompletionRoutine = PowerCompletionRoutine;
  157. Socket->PowerCompletionContext = Context;
  158. PcmciaSocketPowerWorker(&Socket->PowerDpc, Socket, NULL, NULL);
  159. DebugPrint((PCMCIA_DEBUG_SOCKET, "skt %08x SetSocketPower returning %08x\n",
  160. Socket, Socket->CallerStatus));
  161. return(Socket->CallerStatus);
  162. }
  163. VOID
  164. PcmciaSocketPowerWorker(
  165. IN PKDPC Dpc,
  166. IN PVOID Context,
  167. IN PVOID SystemArgument1,
  168. IN PVOID SystemArgument2
  169. )
  170. /*++
  171. Routine Description
  172. This routine handles the power up process of a socket. Because such
  173. long delays are needed, and because this routine may be called at
  174. raised irql, this is a state machine that has the capability of
  175. calling itself on a KTIMER.
  176. Arguments
  177. same as KDPC (DeferredContext is socket)
  178. Return Value
  179. status
  180. --*/
  181. {
  182. PSOCKET Socket = Context;
  183. PFDO_EXTENSION fdoExtension = Socket->DeviceExtension;
  184. NTSTATUS status = Socket->DeferredStatus;
  185. ULONG DelayTime = 0;
  186. BOOLEAN ContinueExecution = TRUE;
  187. #if DBG
  188. {
  189. ULONG Phase = 0;
  190. switch(Socket->WorkerState) {
  191. case SPW_SetPowerOn:
  192. case SPW_SetPowerOff:
  193. Phase = Socket->PowerPhase;
  194. break;
  195. }
  196. if (Phase) {
  197. DebugPrint((PCMCIA_DEBUG_SOCKET, "skt %08x power worker - %s(%d)\n", Socket,
  198. SOCKET_POWER_WORKER_STRING(Socket->WorkerState), Phase));
  199. } else {
  200. DebugPrint((PCMCIA_DEBUG_SOCKET, "skt %08x power worker - %s\n", Socket,
  201. SOCKET_POWER_WORKER_STRING(Socket->WorkerState)));
  202. }
  203. }
  204. #endif
  205. //
  206. // Socket power state machine
  207. //
  208. switch(Socket->WorkerState) {
  209. case SPW_RequestPower:
  210. status = STATUS_SUCCESS;
  211. if (IsSocketFlagSet(Socket, SOCKET_CARD_POWERED_UP)) {
  212. Socket->WorkerState = SPW_Exit;
  213. } else {
  214. if ((KeGetCurrentIrql() >= DISPATCH_LEVEL) && (Socket->PowerCompletionRoutine == NULL)) {
  215. ASSERT((KeGetCurrentIrql() < DISPATCH_LEVEL) || (Socket->PowerCompletionRoutine != NULL));
  216. //
  217. // no completion routine at raised irql
  218. //
  219. status = STATUS_INVALID_PARAMETER;
  220. } else {
  221. //
  222. // All ok, continue to next state
  223. //
  224. Socket->PowerPhase = 1;
  225. Socket->WorkerState = SPW_SetPowerOn;
  226. }
  227. }
  228. break;
  229. case SPW_ReleasePower:
  230. status = STATUS_SUCCESS;
  231. if (!IsSocketFlagSet(Socket, SOCKET_CARD_POWERED_UP)) {
  232. Socket->WorkerState = SPW_Exit;
  233. } else {
  234. if ((KeGetCurrentIrql() >= DISPATCH_LEVEL) && (Socket->PowerCompletionRoutine == NULL)) {
  235. ASSERT((KeGetCurrentIrql() < DISPATCH_LEVEL) || (Socket->PowerCompletionRoutine != NULL));
  236. //
  237. // no completion routine at raised irql
  238. //
  239. status = STATUS_INVALID_PARAMETER;
  240. } else {
  241. //
  242. // All ok, continue to next state
  243. //
  244. Socket->WorkerState = SPW_Deconfigure;
  245. }
  246. }
  247. break;
  248. case SPW_SetPowerOn:
  249. //
  250. // Turn power ON
  251. //
  252. status = (*(DeviceDispatchTable[fdoExtension->DeviceDispatchIndex].SetPower))
  253. (Socket, TRUE, &DelayTime);
  254. Socket->PowerPhase++;
  255. if (status != STATUS_MORE_PROCESSING_REQUIRED) {
  256. if (NT_SUCCESS(status)) {
  257. //
  258. // Done with power up, proceed to the init sequence
  259. //
  260. SetSocketFlag(Socket, SOCKET_CARD_POWERED_UP);
  261. DebugPrint((PCMCIA_DEBUG_SOCKET, "skt %08x power UP\n", Socket));
  262. Socket->WorkerState = SPW_ResetCard;
  263. Socket->CardResetPhase = 1;
  264. } else if (status == STATUS_INVALID_DEVICE_STATE) {
  265. //
  266. // Power was already on, don't reset the card
  267. //
  268. SetSocketFlag(Socket, SOCKET_CARD_POWERED_UP);
  269. DebugPrint((PCMCIA_DEBUG_SOCKET, "skt %08x power already UP\n", Socket));
  270. Socket->WorkerState = SPW_Exit;
  271. status = STATUS_SUCCESS;
  272. } else {
  273. //
  274. // Power didn't go on
  275. //
  276. DebugPrint((PCMCIA_DEBUG_SOCKET, "skt %08x poweron fail %08x\n", Socket, status));
  277. Socket->WorkerState = SPW_Exit;
  278. }
  279. }
  280. break;
  281. case SPW_ResetCard:
  282. //
  283. // Make sure the card is ready to be enumerated
  284. //
  285. status = (*(Socket->SocketFnPtr->PCBResetCard))(Socket, &DelayTime);
  286. Socket->CardResetPhase++;
  287. if (status != STATUS_MORE_PROCESSING_REQUIRED) {
  288. Socket->WorkerState = SPW_Exit;
  289. }
  290. break;
  291. case SPW_Deconfigure:
  292. PcmciaSocketDeconfigure(Socket);
  293. Socket->PowerPhase = 1;
  294. Socket->WorkerState = SPW_SetPowerOff;
  295. break;
  296. case SPW_SetPowerOff:
  297. //
  298. // Turn power OFF
  299. //
  300. status = (*(DeviceDispatchTable[fdoExtension->DeviceDispatchIndex].SetPower))
  301. (Socket, FALSE, &DelayTime);
  302. Socket->PowerPhase++;
  303. if (status != STATUS_MORE_PROCESSING_REQUIRED) {
  304. Socket->WorkerState = SPW_Exit;
  305. if (NT_SUCCESS(status)) {
  306. //
  307. // Power is now off
  308. //
  309. ResetSocketFlag(Socket, SOCKET_CARD_POWERED_UP);
  310. DebugPrint((PCMCIA_DEBUG_SOCKET, "skt %08x power DOWN\n", Socket));
  311. } else if (status == STATUS_INVALID_DEVICE_STATE) {
  312. //
  313. // Power was already off
  314. //
  315. ResetSocketFlag(Socket, SOCKET_CARD_POWERED_UP);
  316. DebugPrint((PCMCIA_DEBUG_SOCKET, "skt %08x power already DOWN\n", Socket));
  317. status = STATUS_SUCCESS;
  318. } else {
  319. //
  320. // Power didn't go off
  321. //
  322. DebugPrint((PCMCIA_DEBUG_SOCKET, "skt %08x poweroff fail %08x\n", Socket, status));
  323. Socket->WorkerState = SPW_Exit;
  324. }
  325. }
  326. break;
  327. case SPW_Exit:
  328. if (!NT_SUCCESS(status)) {
  329. DebugPrint((PCMCIA_DEBUG_FAIL, "skt %08x SocketPowerWorker FAILED, status %08x\n", Socket, status));
  330. ASSERT(NT_SUCCESS(status));
  331. }
  332. //
  333. // Done. Update flags, and call the completion routine if required
  334. //
  335. if (PCMCIA_TEST_AND_RESET(&Socket->DeferredStatusLock)) {
  336. PPCMCIA_COMPLETION_ROUTINE PowerCompletionRoutine = Socket->PowerCompletionRoutine;
  337. PVOID PowerCompletionContext = Socket->PowerCompletionContext;
  338. Socket->WorkerState = SPW_Stopped;
  339. PCMCIA_TEST_AND_RESET(&Socket->WorkerBusy);
  340. if (PowerCompletionRoutine) {
  341. (*PowerCompletionRoutine)(PowerCompletionContext, status);
  342. } else {
  343. ASSERT(PowerCompletionRoutine != NULL);
  344. }
  345. } else {
  346. Socket->CallerStatus = status;
  347. Socket->WorkerState = SPW_Stopped;
  348. PCMCIA_TEST_AND_RESET(&Socket->WorkerBusy);
  349. }
  350. return;
  351. default:
  352. ASSERT(FALSE);
  353. return;
  354. }
  355. //
  356. // Now check the results
  357. //
  358. if (status == STATUS_PENDING) {
  359. DebugPrint((PCMCIA_DEBUG_SOCKET, "skt %08x worker exit, status pending\n", Socket));
  360. //
  361. // whatever returned pending will call us back
  362. //
  363. if (PCMCIA_TEST_AND_SET(&Socket->DeferredStatusLock)) {
  364. //
  365. // First time that we are waiting, we will return to original
  366. // caller. So update the main power status just this time.
  367. //
  368. Socket->CallerStatus = STATUS_PENDING;
  369. }
  370. return;
  371. }
  372. //
  373. // remember for next time
  374. //
  375. Socket->DeferredStatus = status;
  376. if (!NT_SUCCESS(status) && (status != STATUS_MORE_PROCESSING_REQUIRED)) {
  377. Socket->WorkerState = SPW_Exit;
  378. DelayTime = 0;
  379. }
  380. //
  381. // Not done yet. Recurse or call timer
  382. //
  383. if (DelayTime) {
  384. DebugPrint((PCMCIA_DEBUG_SOCKET, "skt %08x power worker delay type %s, %d usec\n", Socket,
  385. (KeGetCurrentIrql() < DISPATCH_LEVEL) ? "Wait" : "Timer",
  386. DelayTime));
  387. if (KeGetCurrentIrql() < DISPATCH_LEVEL) {
  388. PcmciaWait(DelayTime);
  389. } else {
  390. LARGE_INTEGER dueTime;
  391. //
  392. // Running on a DPC, kick of a kernel timer
  393. //
  394. if (PCMCIA_TEST_AND_SET(&Socket->DeferredStatusLock)) {
  395. //
  396. // First time that we are waiting, we will return to original
  397. // caller. So update the main power status just this time.
  398. //
  399. Socket->CallerStatus = STATUS_PENDING;
  400. }
  401. dueTime.QuadPart = -((LONG) DelayTime*10);
  402. KeSetTimer(&Socket->PowerTimer, dueTime, &Socket->PowerDpc);
  403. //
  404. // We will reenter on timer dpc
  405. //
  406. return;
  407. }
  408. }
  409. //
  410. // Recurse
  411. //
  412. PcmciaSocketPowerWorker(&Socket->PowerDpc, Socket, NULL, NULL);
  413. }
  414. VOID
  415. PcmciaGetSocketStatus(
  416. IN PSOCKET Socket
  417. )
  418. /*++
  419. Routine Description:
  420. A small utility routine that returns some common socket flags. The reason
  421. it exists is to allow the Enumerate Devices routine to remain pagable.
  422. NOTE: This routine updates the "software view" of the device state. This
  423. should only be done at well-defined points in the driver. In particular,
  424. you do not want to be updating the software state immediately after
  425. a surprise remove. Instead, most of the driver needs to continue to
  426. believe the card is still there while it does its unconfigure and
  427. poweroff.
  428. Arguments:
  429. Socket - The socket in which the PC-Card resides
  430. boolean parameters are written according to socket flags
  431. Return Value:
  432. none
  433. --*/
  434. {
  435. BOOLEAN isCardInSocket, isCardBusCard;
  436. UCHAR previousDeviceState;
  437. PCMCIA_ACQUIRE_DEVICE_LOCK(Socket->DeviceExtension);
  438. isCardInSocket = (*(Socket->SocketFnPtr->PCBDetectCardInSocket))(Socket);
  439. isCardBusCard = FALSE;
  440. if (isCardInSocket && CardBus(Socket)) {
  441. isCardBusCard = ((CBReadSocketRegister(Socket, CARDBUS_SOCKET_PRESENT_STATE_REG) & CARDBUS_CB_CARD) != 0);
  442. }
  443. previousDeviceState = Socket->DeviceState;
  444. if (!isCardInSocket) {
  445. SetSocketEmpty(Socket);
  446. } else if (isCardBusCard) {
  447. SetCardBusCardInSocket(Socket);
  448. } else {
  449. Set16BitCardInSocket(Socket);
  450. }
  451. if (previousDeviceState != Socket->DeviceState) {
  452. DebugPrint((PCMCIA_DEBUG_SOCKET, "skt %x Socket Status: Card Status Change!\n", Socket));
  453. SetSocketFlag(Socket, SOCKET_CARD_STATUS_CHANGE);
  454. }
  455. DebugPrint((PCMCIA_DEBUG_SOCKET, "skt %x Socket Status: %s\n",
  456. Socket, isCardInSocket ? (isCardBusCard ? "INSERTED Cardbus" : "INSERTED R2") : "EMPTY"));
  457. PCMCIA_RELEASE_DEVICE_LOCK(Socket->DeviceExtension);
  458. //
  459. // Fill in socket power values
  460. //
  461. if (isCardInSocket && IsSocketFlagSet(Socket, SOCKET_CARD_STATUS_CHANGE) && Socket->SocketFnPtr->PCBGetPowerRequirements) {
  462. (*(Socket->SocketFnPtr->PCBGetPowerRequirements))(Socket);
  463. }
  464. }
  465. NTSTATUS
  466. PcmciaGetConfigData(
  467. PPDO_EXTENSION PdoExtension
  468. )
  469. /*++
  470. Routine Description:
  471. This routine controls the translation of the CIS config data for the card into
  472. SOCKET_DATA structures chained onto the PDO. The action of this routine depends
  473. on the type of device:
  474. 1) For a standard R2 card, a single SOCKET_DATA structure is linked to the pdo
  475. extension, which contains a straight translation of the CIS contents.
  476. 2) For a fully compliant true R2 MF card, a chain of SOCKET_DATA structures is
  477. created, one for each function on the card.
  478. 3) For a non-conforming R2 MF card (the typical case), a single structure is
  479. linked just like case #1.
  480. 4) For a CardBus card, a single SOCKET_DATA is linked to the pdo extension. If
  481. there are multiple functions on the device, then there will be multiple pdo
  482. extensions, each with a single SOCKET_DATA structure.
  483. Arguments:
  484. pdoExtension - The pdo extension corresponding to the specified pccard or cb function.
  485. Return Value:
  486. STATUS_SUCCESS
  487. STATUS_NO_SUCH_DEVICE if no card is present in the socket (i.e. the passed in PDO is 'dead')
  488. --*/
  489. {
  490. NTSTATUS status = STATUS_SUCCESS;
  491. PSOCKET_DATA socketData, prevSocketData;
  492. PSOCKET_DATA socketDataList = NULL;
  493. UCHAR function = 0;
  494. PSOCKET Socket = PdoExtension->Socket;
  495. PAGED_CODE ();
  496. if (!IsCardInSocket(Socket)) {
  497. //
  498. // Card probably removed,
  499. // and Pdo's ghost still hanging around
  500. //
  501. return STATUS_NO_SUCH_DEVICE;
  502. }
  503. ResetSocketFlag(Socket, SOCKET_CARD_MEMORY);
  504. ResetSocketFlag(Socket, SOCKET_CARD_CONFIGURED);
  505. DebugPrint((PCMCIA_DEBUG_SOCKET, "skt %08x performing GetSocketData\n", Socket));
  506. Socket->NumberOfFunctions = 1;
  507. prevSocketData = NULL;
  508. while (function < 255) {
  509. //
  510. // Parse tuples of next function on the card
  511. //
  512. socketData = ExAllocatePool(NonPagedPool, sizeof(SOCKET_DATA));
  513. if (socketData == NULL) {
  514. status = STATUS_INSUFFICIENT_RESOURCES;
  515. break;
  516. }
  517. RtlZeroMemory(socketData, sizeof(SOCKET_DATA));
  518. socketData->Function = function;
  519. socketData->PdoExtension = PdoExtension;
  520. socketData->Socket = Socket;
  521. DebugPrint((PCMCIA_DEBUG_SOCKET, "Parsing function %d...\n", socketData->Function));
  522. status = PcmciaParseFunctionData(Socket, socketData);
  523. if (NT_SUCCESS(status)) {
  524. //
  525. // Link it to the list of socketdata structures
  526. //
  527. socketData->Prev = prevSocketData;
  528. socketData->Next = NULL;
  529. if (prevSocketData) {
  530. prevSocketData->Next = socketData;
  531. } else {
  532. //
  533. // This is the first function on the card
  534. // Make it the head of the list
  535. //
  536. socketDataList = socketData;
  537. }
  538. if (socketData->DeviceType == PCCARD_TYPE_MODEM) {
  539. SetDeviceFlag(PdoExtension, PCMCIA_PDO_ENABLE_AUDIO);
  540. }
  541. } else {
  542. //
  543. // no more functions on this card
  544. //
  545. ExFreePool(socketData);
  546. if ((function > 0) && (status == STATUS_NO_MORE_ENTRIES)) {
  547. status = STATUS_SUCCESS;
  548. }
  549. break;
  550. }
  551. function++;
  552. prevSocketData = socketData;
  553. }
  554. if (!NT_SUCCESS(status)) {
  555. socketData = socketDataList;
  556. while(socketData) {
  557. prevSocketData = socketData;
  558. socketData = socketData->Next;
  559. ExFreePool(prevSocketData);
  560. }
  561. } else {
  562. PdoExtension->SocketData = socketDataList;
  563. }
  564. return status;
  565. }
  566. UCHAR
  567. PcmciaReadCISChar(
  568. PPDO_EXTENSION PdoExtension,
  569. IN MEMORY_SPACE MemorySpace,
  570. IN ULONG Offset
  571. )
  572. /*++
  573. Routine Description:
  574. Returns the card data. This information is cached in the socket
  575. structure. This way once a PCCARD is enabled it will not be touched
  576. due to a query ioctl.
  577. Arguments:
  578. Context
  579. Return Value:
  580. TRUE
  581. --*/
  582. {
  583. PSOCKET socket = PdoExtension->Socket;
  584. PDEVICE_OBJECT pdo;
  585. UCHAR retValue = 0xff;
  586. ULONG relativeOffset;
  587. ULONG bytesRead;
  588. if (socket && IsCardInSocket(socket)) {
  589. if (!PdoExtension->CisCache) {
  590. #define PCMCIA_CIS_CACHE_SIZE 2048
  591. PdoExtension->CisCache = ExAllocatePool(NonPagedPool, PCMCIA_CIS_CACHE_SIZE);
  592. PdoExtension->CisCacheSpace = 0xff;
  593. PdoExtension->CisCacheBase = 0;
  594. }
  595. if (PdoExtension->CisCache) {
  596. if ((MemorySpace != PdoExtension->CisCacheSpace) ||
  597. (Offset < PdoExtension->CisCacheBase) ||
  598. (Offset > PdoExtension->CisCacheBase + PCMCIA_CIS_CACHE_SIZE - 1)) {
  599. //
  600. // LATER: If devices have CIS > CacheSize, then we should window this
  601. //
  602. bytesRead = (*(socket->SocketFnPtr->PCBReadCardMemory))(PdoExtension,
  603. MemorySpace,
  604. 0,
  605. PdoExtension->CisCache,
  606. PCMCIA_CIS_CACHE_SIZE);
  607. PdoExtension->CisCacheSpace = MemorySpace;
  608. }
  609. relativeOffset = Offset - PdoExtension->CisCacheBase;
  610. if (relativeOffset < PCMCIA_CIS_CACHE_SIZE) {
  611. retValue = PdoExtension->CisCache[relativeOffset];
  612. }
  613. }
  614. }
  615. return retValue;
  616. }
  617. NTSTATUS
  618. PcmciaReadWriteCardMemory(
  619. IN PDEVICE_OBJECT Pdo,
  620. IN ULONG WhichSpace,
  621. IN OUT PUCHAR Buffer,
  622. IN ULONG Offset,
  623. IN ULONG Length,
  624. IN BOOLEAN Read
  625. )
  626. /*++
  627. Routine Description:
  628. This routine is to provide IRP_MN_READ_CONFIG/WRITE_CONFIG support: this would locate the
  629. socket on which Pdo resides and map the card's memory into the system space.
  630. If Read is TRUE it would:
  631. copy the contents of the config memory at a specified offset and length to the
  632. caller supplied buffer.
  633. else
  634. copy the contents of the caller specified buffer at the specified offset and length of
  635. the config memory
  636. Note: this has to be non-paged since it can be called by
  637. clients at DISPATCH_LEVEL
  638. Arguments:
  639. Pdo - Device object representing the PC-CARD whose config memory needs to be read/written
  640. WhichSpace - Indicates which memory space needs to be mapped: one of
  641. PCCARD_COMMON_MEMORY_SPACE
  642. PCCARD_ATTRIBUTE_MEMORY_SPACE
  643. PCCARD_PCI_CONFIGURATION_MEMORY_SPACE (only for cardbus cards)
  644. Buffer - Caller supplied buffer into/out of which the memory contents are copied
  645. Offset - Offset of the attribute memory at which we copy
  646. Length - Number of bytes of attribute memory/buffer to be copied
  647. Return value:
  648. STATUS_INVALID_PARAMETER_1
  649. STATUS_INVALID_PARAMETER_2
  650. STATUS_INVALID_PARAMETER_3 If supplied parameters are not valid
  651. STATUS_NO_SUCH_DEVICE No PC-Card in the socket
  652. STATUS_DEVICE_NOT_READY PC-Card not initialized yet or some other hardware related error
  653. STATUS_SUCCESS Contents copied as requested
  654. --*/
  655. {
  656. PSOCKET socket;
  657. PSOCKET_DATA socketData;
  658. PUCHAR tupleData;
  659. ULONG tupleDataSize;
  660. PPDO_EXTENSION pdoExtension;
  661. NTSTATUS status = STATUS_UNSUCCESSFUL;
  662. pdoExtension = Pdo->DeviceExtension;
  663. socket= pdoExtension->Socket;
  664. //
  665. // Got to have a card in the socket to read/write from it..
  666. //
  667. if (!IsCardInSocket(socket)) {
  668. return STATUS_NO_SUCH_DEVICE;
  669. }
  670. //
  671. // Memory space has to be one of the defined ones.
  672. //
  673. if ((WhichSpace != PCCARD_COMMON_MEMORY) &&
  674. (WhichSpace != PCCARD_ATTRIBUTE_MEMORY) &&
  675. (WhichSpace != PCCARD_PCI_CONFIGURATION_SPACE)) {
  676. return STATUS_INVALID_PARAMETER_1;
  677. }
  678. //
  679. // We support PCCARD_PCI_CONFIGURATION_SPACE only
  680. // for cardbus cards (doesn't make sense for R2 cards)
  681. // Similarily PCCARD_ATTRIBUTE/COMMON_MEMORY only for
  682. // R2 cards
  683. //
  684. if ((((WhichSpace == PCCARD_ATTRIBUTE_MEMORY) ||
  685. (WhichSpace == PCCARD_COMMON_MEMORY)) && !Is16BitCard(pdoExtension)) ||
  686. ((WhichSpace == PCCARD_PCI_CONFIGURATION_SPACE) && !IsCardBusCard(pdoExtension))) {
  687. return STATUS_INVALID_PARAMETER_1;
  688. }
  689. if (!Buffer) {
  690. return STATUS_INVALID_PARAMETER_2;
  691. }
  692. if (WhichSpace == PCCARD_PCI_CONFIGURATION_SPACE) {
  693. //
  694. // This has to be a cardbus card.
  695. //
  696. // NOTE: unimplemented: fill this in! send an IRP down to PCI
  697. // to get the config space
  698. status = STATUS_NOT_SUPPORTED;
  699. } else {
  700. //
  701. // This has to be an R2 Card.
  702. // Attribute/common memory space
  703. //
  704. ASSERT ((WhichSpace == PCCARD_ATTRIBUTE_MEMORY) ||
  705. (WhichSpace == PCCARD_COMMON_MEMORY));
  706. //
  707. // Offset and length are >= 0 because they are ULONGs,
  708. // so don't worry about that.
  709. //
  710. if (!IsSocketFlagSet(socket, SOCKET_CARD_POWERED_UP)) {
  711. return STATUS_DEVICE_NOT_READY;
  712. }
  713. PCMCIA_ACQUIRE_DEVICE_LOCK(socket->DeviceExtension);
  714. if (Read && (socket->SocketFnPtr->PCBReadCardMemory != NULL)) {
  715. //
  716. // Read from card memory
  717. //
  718. status = ((*(socket->SocketFnPtr->PCBReadCardMemory))(pdoExtension,
  719. WhichSpace,
  720. Offset,
  721. Buffer,
  722. Length) == Length)
  723. ? STATUS_SUCCESS : STATUS_UNSUCCESSFUL;
  724. } else if (socket->SocketFnPtr->PCBWriteCardMemory != NULL) {
  725. //
  726. // Write to card memory
  727. //
  728. status = ((*(socket->SocketFnPtr->PCBWriteCardMemory))(pdoExtension,
  729. WhichSpace,
  730. Offset,
  731. Buffer,
  732. Length) == Length)
  733. ? STATUS_SUCCESS : STATUS_UNSUCCESSFUL;
  734. }
  735. PCMCIA_RELEASE_DEVICE_LOCK(socket->DeviceExtension);
  736. }
  737. return status;
  738. }
  739. NTSTATUS
  740. PcmciaConfigureCardBusCard(
  741. PPDO_EXTENSION pdoExtension
  742. )
  743. /*++
  744. Routine Description:
  745. This routine does some verification, and applies hacks
  746. Arguments:
  747. pdoExtension - Pointer to the physical device object extension for the pc-card
  748. Return value:
  749. status
  750. --*/
  751. {
  752. ULONG i, pciConfig;
  753. NTSTATUS status = STATUS_SUCCESS;
  754. PSOCKET Socket = pdoExtension->Socket;
  755. for (i = 0; i < CARDBUS_CONFIG_RETRY_COUNT; i++) {
  756. GetPciConfigSpace(pdoExtension, CFGSPACE_VENDOR_ID, &pciConfig, sizeof(pciConfig));
  757. if (pciConfig != 0xffffffff) {
  758. break;
  759. }
  760. }
  761. if (pciConfig == 0xffffffff) {
  762. DebugPrint((PCMCIA_DEBUG_FAIL, "pdo %08x failed to verify CardBus config space\n", pdoExtension->DeviceObject));
  763. status = STATUS_DEVICE_NOT_READY;
  764. } else {
  765. PFDO_EXTENSION fdoExtension = Socket->DeviceExtension;
  766. //
  767. // The TI1130, 1131, 1031 have a bug such that CAUDIO on a cardbus card
  768. // is gated by the following bit (which normally only has meaning only
  769. // for R2 cards). We can workaround the problem simply by turning it on
  770. // for cardbus cards.
  771. //
  772. if ((fdoExtension->ControllerType == PcmciaTI1130) ||
  773. (fdoExtension->ControllerType == PcmciaTI1131) ||
  774. (fdoExtension->ControllerType == PcmciaTI1031)) {
  775. UCHAR byte;
  776. byte = PcicReadSocket(Socket, PCIC_INTERRUPT);
  777. byte |= IGC_PCCARD_IO;
  778. PcicWriteSocket(Socket, PCIC_INTERRUPT, byte);
  779. }
  780. }
  781. return status;
  782. }
  783. NTSTATUS
  784. PcmciaConfigurePcCard(
  785. PPDO_EXTENSION pdoExtension,
  786. IN PPCMCIA_COMPLETION_ROUTINE ConfigCompletionRoutine
  787. )
  788. /*++
  789. Routine Description:
  790. This routine does the brunt work of enabling the PC-Card using the supplied
  791. resources.
  792. NOTE: If this routine is called at less than DISPATCH_LEVEL, then the call
  793. will complete (not return STATUS_PENDING). If this routine is called at
  794. DISPATCH_LEVEL or greater, this routine returns STATUS_PENDING, and completes
  795. the configuration process using a KTIMER.
  796. Arguments:
  797. pdoExtension - Pointer to the physical device object extension for the pc-card
  798. ConfigCompletionRoutine - routine to be called after configuration is complete
  799. Return value:
  800. status
  801. --*/
  802. {
  803. DebugPrint((PCMCIA_DEBUG_CONFIG, "pdo %08x ConfigurePcCard entered\n", pdoExtension->DeviceObject));
  804. if (!PCMCIA_TEST_AND_SET(&pdoExtension->Socket->WorkerBusy)) {
  805. return STATUS_DEVICE_BUSY;
  806. }
  807. ASSERT(pdoExtension->ConfigurationPhase == CW_Stopped);
  808. pdoExtension->ConfigurationPhase = CW_InitialState;
  809. pdoExtension->ConfigCompletionRoutine = ConfigCompletionRoutine;
  810. pdoExtension->ConfigurationStatus = STATUS_SUCCESS;
  811. PcmciaConfigurationWorker(&pdoExtension->ConfigurationDpc, pdoExtension, NULL, NULL);
  812. DebugPrint((PCMCIA_DEBUG_CONFIG, "pdo %08x ConfigurePcCard returning %08x\n", pdoExtension->DeviceObject, pdoExtension->ConfigurationStatus));
  813. return(pdoExtension->ConfigurationStatus);
  814. }
  815. VOID
  816. PcmciaConfigurationWorker(
  817. IN PKDPC Dpc,
  818. IN PVOID DeferredContext,
  819. IN PVOID SystemArgument1,
  820. IN PVOID SystemArgument2
  821. )
  822. /*++
  823. Routine Description
  824. This routine handles the configuration process of a 16-bit R2 pccard.
  825. Because certain cards are finicky (modems), and require gigantic pauses
  826. between steps, this worker routine acts as a state machine, and will
  827. delay after each step.
  828. Arguments
  829. same as KDPC (DeferredContext is pdoExtension)
  830. Return Value
  831. status
  832. --*/
  833. {
  834. PPDO_EXTENSION pdoExtension = DeferredContext;
  835. PSOCKET Socket = pdoExtension->Socket;
  836. PSOCKET_CONFIGURATION SocketConfig = pdoExtension->SocketConfiguration;
  837. NTSTATUS status = pdoExtension->DeferredConfigurationStatus;
  838. ULONG DelayUsec = 0;
  839. DebugPrint((PCMCIA_DEBUG_CONFIG, "pdo %08x config worker - %s\n", pdoExtension->DeviceObject,
  840. CONFIGURATION_WORKER_STRING(pdoExtension->ConfigurationPhase)));
  841. switch(pdoExtension->ConfigurationPhase) {
  842. case CW_InitialState:
  843. if (IsSocketFlagSet(pdoExtension->Socket, SOCKET_CARD_CONFIGURED)) {
  844. pdoExtension->ConfigurationPhase = CW_Exit;
  845. break;
  846. }
  847. if (!IsCardInSocket(pdoExtension->Socket)) {
  848. status = STATUS_NO_SUCH_DEVICE;
  849. pdoExtension->ConfigurationPhase = CW_Exit;
  850. break;
  851. }
  852. pdoExtension->ConfigurationPhase = CW_ResetCard;
  853. Socket->CardResetPhase = 1;
  854. break;
  855. case CW_ResetCard:
  856. //
  857. // Reset the card
  858. //
  859. status = (*(Socket->SocketFnPtr->PCBResetCard))(Socket, &DelayUsec);
  860. Socket->CardResetPhase++;
  861. if (status != STATUS_MORE_PROCESSING_REQUIRED) {
  862. pdoExtension->ConfigurationPhase = CW_Phase1;
  863. }
  864. break;
  865. case CW_Phase1:
  866. //
  867. // Initialize variables
  868. //
  869. PcmciaConfigurationWorkerInitialization(pdoExtension);
  870. //
  871. // Configure the cards configuration registers, and the socket mem
  872. // and I/O windows
  873. //
  874. status = PcmciaConfigurePcCardRegisters(pdoExtension);
  875. if (NT_SUCCESS(status)) {
  876. status = PcmciaConfigurePcCardMemIoWindows(Socket, SocketConfig);
  877. }
  878. DelayUsec = 1000 * (ULONG)pdoExtension->ConfigureDelay1;
  879. pdoExtension->ConfigurationPhase = CW_Phase2;
  880. break;
  881. case CW_Phase2:
  882. //
  883. // Take this opportunity to "poke" the modem
  884. //
  885. if (pdoExtension->ConfigurationFlags & CONFIG_WORKER_APPLY_MODEM_HACK) {
  886. PcmciaConfigureModemHack(Socket, SocketConfig);
  887. }
  888. DelayUsec = 1000 * (ULONG)pdoExtension->ConfigureDelay2;
  889. pdoExtension->ConfigurationPhase = CW_Phase3;
  890. break;
  891. case CW_Phase3:
  892. //
  893. // Configure the IRQ
  894. //
  895. status = PcmciaConfigurePcCardIrq(Socket, SocketConfig);
  896. DelayUsec = 1000 * (ULONG)pdoExtension->ConfigureDelay3;
  897. pdoExtension->ConfigurationPhase = CW_Exit;
  898. break;
  899. case CW_Exit:
  900. //
  901. // Done. Update flags, and call the completion routine if required
  902. //
  903. if (IsDeviceFlagSet(pdoExtension, PCMCIA_CONFIG_STATUS_DEFERRED)) {
  904. if (pdoExtension->ConfigCompletionRoutine) {
  905. (*pdoExtension->ConfigCompletionRoutine)(pdoExtension,
  906. pdoExtension->DeferredConfigurationStatus);
  907. }
  908. ResetDeviceFlag(pdoExtension, PCMCIA_CONFIG_STATUS_DEFERRED);
  909. } else {
  910. pdoExtension->ConfigurationStatus = status;
  911. }
  912. if (NT_SUCCESS(status)) {
  913. SetSocketFlag(Socket, SOCKET_CARD_CONFIGURED);
  914. }
  915. pdoExtension->ConfigurationPhase = CW_Stopped;
  916. PCMCIA_TEST_AND_RESET(&Socket->WorkerBusy);
  917. DebugPrint((PCMCIA_DEBUG_CONFIG, "pdo %08x config worker exit %08x\n", pdoExtension->DeviceObject, status));
  918. return;
  919. default:
  920. ASSERT(FALSE);
  921. return;
  922. }
  923. pdoExtension->DeferredConfigurationStatus = status;
  924. if (!NT_SUCCESS(status) && (status != STATUS_MORE_PROCESSING_REQUIRED)) {
  925. DelayUsec = 0;
  926. pdoExtension->ConfigurationPhase = CW_Exit;
  927. }
  928. //
  929. // Not done yet. Recurse or call timer
  930. //
  931. if (DelayUsec) {
  932. DebugPrint((PCMCIA_DEBUG_CONFIG, "pdo %08x config worker delay type %s, %d usec\n",
  933. pdoExtension->DeviceObject,
  934. (KeGetCurrentIrql() < DISPATCH_LEVEL) ? "Wait" : "Timer",
  935. DelayUsec));
  936. if (KeGetCurrentIrql() < DISPATCH_LEVEL) {
  937. PcmciaWait(DelayUsec);
  938. } else {
  939. LARGE_INTEGER dueTime;
  940. dueTime.QuadPart = -((LONG) DelayUsec*10);
  941. //
  942. // Running on a DPC, kick of a kernel timer
  943. //
  944. KeSetTimer(&pdoExtension->ConfigurationTimer,
  945. dueTime,
  946. &pdoExtension->ConfigurationDpc);
  947. if (!IsDeviceFlagSet(pdoExtension, PCMCIA_CONFIG_STATUS_DEFERRED)) {
  948. SetDeviceFlag(pdoExtension, PCMCIA_CONFIG_STATUS_DEFERRED);
  949. pdoExtension->ConfigurationStatus = STATUS_PENDING;
  950. }
  951. return;
  952. }
  953. }
  954. PcmciaConfigurationWorker(&pdoExtension->ConfigurationDpc, pdoExtension, NULL, NULL);
  955. }
  956. VOID
  957. PcmciaConfigurationWorkerInitialization(
  958. PPDO_EXTENSION pdoExtension
  959. )
  960. /*++
  961. Routine Description:
  962. This routine sets variables which control the configuration process.
  963. Arguments:
  964. pdoExtension - Pointer to the physical device object extension for the pc-card
  965. Return value:
  966. none
  967. --*/
  968. {
  969. PSOCKET_DATA socketData = pdoExtension->SocketData;
  970. ULONG i;
  971. pdoExtension->ConfigurationFlags = 0;
  972. pdoExtension->ConfigureDelay1 = 0;
  973. pdoExtension->ConfigureDelay2 = 0;
  974. pdoExtension->ConfigureDelay3 = 0;
  975. while (socketData) {
  976. i = 0;
  977. while (DeviceConfigParams[i].ValidEntry) {
  978. if (((DeviceConfigParams[i].DeviceType == 0xff) ||
  979. (DeviceConfigParams[i].DeviceType == socketData->DeviceType)) &&
  980. ((DeviceConfigParams[i].ManufacturerCode == 0xffff) ||
  981. (DeviceConfigParams[i].ManufacturerCode == socketData->ManufacturerCode)) &&
  982. ((DeviceConfigParams[i].ManufacturerInfo == 0xffff) ||
  983. (DeviceConfigParams[i].ManufacturerInfo == socketData->ManufacturerInfo)) &&
  984. ((DeviceConfigParams[i].CisCrc == 0xffff) ||
  985. (DeviceConfigParams[i].CisCrc == socketData->CisCrc))) {
  986. pdoExtension->ConfigurationFlags = DeviceConfigParams[i].ConfigFlags;
  987. pdoExtension->ConfigureDelay1 = DeviceConfigParams[i].ConfigDelay1;
  988. pdoExtension->ConfigureDelay2 = DeviceConfigParams[i].ConfigDelay2;
  989. pdoExtension->ConfigureDelay3 = DeviceConfigParams[i].ConfigDelay3;
  990. break;
  991. }
  992. i++;
  993. }
  994. socketData = socketData->Next;
  995. }
  996. }
  997. NTSTATUS
  998. PcmciaConfigurePcCardMemIoWindows(
  999. IN PSOCKET Socket,
  1000. IN PSOCKET_CONFIGURATION SocketConfig
  1001. )
  1002. /*++
  1003. Routine Description:
  1004. This routine enables the socket memory and I/O windows
  1005. Arguments:
  1006. Socket - Pointer to the socket containing the PC-Card
  1007. SocketConfig - Pointer to the socket configuration structure which contains the
  1008. resources required to enable this pc-card
  1009. Return value:
  1010. status
  1011. --*/
  1012. {
  1013. CARD_REQUEST cardRequest = {0};
  1014. PFDO_EXTENSION fdoExtension = Socket->DeviceExtension;
  1015. NTSTATUS status = STATUS_SUCCESS;
  1016. ULONG i;
  1017. DebugPrint((PCMCIA_DEBUG_CONFIG, "socket %08x config MemIo\n", Socket));
  1018. //
  1019. // Setup IO ranges if there are any
  1020. //
  1021. if (SocketConfig->NumberOfIoPortRanges) {
  1022. cardRequest.RequestType = IO_REQUEST;
  1023. cardRequest.u.Io.NumberOfRanges = (USHORT) SocketConfig->NumberOfIoPortRanges;
  1024. for (i = 0; i < SocketConfig->NumberOfIoPortRanges; i++) {
  1025. DebugPrint((PCMCIA_DEBUG_CONFIG, "\tport range: 0x%x-0x%x\n",
  1026. SocketConfig->Io[i].Base,
  1027. SocketConfig->Io[i].Base + SocketConfig->Io[i].Length));
  1028. cardRequest.u.Io.IoEntry[i].BasePort = SocketConfig->Io[i].Base;
  1029. cardRequest.u.Io.IoEntry[i].NumPorts = SocketConfig->Io[i].Length;
  1030. cardRequest.u.Io.IoEntry[i].Attributes = 0;
  1031. if (SocketConfig->Io[i].Width16) {
  1032. cardRequest.u.Io.IoEntry[i].Attributes |= IO_DATA_PATH_WIDTH;
  1033. }
  1034. if (SocketConfig->Io[i].WaitState16) {
  1035. cardRequest.u.Io.IoEntry[i].Attributes |= IO_WAIT_STATE_16;
  1036. }
  1037. if (SocketConfig->Io[i].Source16) {
  1038. cardRequest.u.Io.IoEntry[i].Attributes |= IO_SOURCE_16;
  1039. }
  1040. if (SocketConfig->Io[i].ZeroWait8) {
  1041. cardRequest.u.Io.IoEntry[i].Attributes |= IO_ZERO_WAIT_8;
  1042. }
  1043. }
  1044. if (!PcmciaProcessConfigureRequest(fdoExtension, Socket, &cardRequest)) {
  1045. status = STATUS_UNSUCCESSFUL;
  1046. DebugPrint((PCMCIA_DEBUG_FAIL, "Failed to configure PcCardIO for socket %x\n", Socket));
  1047. }
  1048. }
  1049. //
  1050. // Set up Memory space if there is some.
  1051. //
  1052. if (NT_SUCCESS(status) && SocketConfig->NumberOfMemoryRanges) {
  1053. cardRequest.RequestType = MEM_REQUEST;
  1054. cardRequest.u.Memory.NumberOfRanges = (USHORT) SocketConfig->NumberOfMemoryRanges;
  1055. for (i = 0; i < SocketConfig->NumberOfMemoryRanges; i++) {
  1056. DebugPrint((PCMCIA_DEBUG_CONFIG, "\tmemory: host %08x for 0x%x, card %08x\n",
  1057. SocketConfig->Memory[i].HostBase,
  1058. SocketConfig->Memory[i].Length,
  1059. SocketConfig->Memory[i].CardBase));
  1060. cardRequest.u.Memory.MemoryEntry[i].BaseAddress = SocketConfig->Memory[i].CardBase;
  1061. cardRequest.u.Memory.MemoryEntry[i].HostAddress = SocketConfig->Memory[i].HostBase;
  1062. cardRequest.u.Memory.MemoryEntry[i].WindowSize = SocketConfig->Memory[i].Length;
  1063. cardRequest.u.Memory.MemoryEntry[i].AttributeMemory = SocketConfig->Memory[i].IsAttribute;
  1064. cardRequest.u.Memory.MemoryEntry[i].WindowDataSize16 = SocketConfig->Memory[i].Width16;
  1065. cardRequest.u.Memory.MemoryEntry[i].WaitStates = SocketConfig->Memory[i].WaitState;
  1066. }
  1067. if (!PcmciaProcessConfigureRequest(fdoExtension, Socket, &cardRequest)) {
  1068. status = STATUS_UNSUCCESSFUL;
  1069. DebugPrint((PCMCIA_DEBUG_FAIL, "Failed to configure PcCardMem for socket %x\n", Socket));
  1070. }
  1071. }
  1072. return status;
  1073. }
  1074. NTSTATUS
  1075. PcmciaConfigurePcCardIrq(
  1076. IN PSOCKET Socket,
  1077. IN PSOCKET_CONFIGURATION SocketConfig
  1078. )
  1079. /*++
  1080. Routine Description:
  1081. This routine enables the socket IRQ
  1082. Arguments:
  1083. Socket - Pointer to the socket containing the PC-Card
  1084. SocketConfig - Pointer to the socket configuration structure which contains the
  1085. resources required to enable this pc-card
  1086. Return value:
  1087. status
  1088. --*/
  1089. {
  1090. CARD_REQUEST cardRequest = {0};
  1091. PFDO_EXTENSION fdoExtension = Socket->DeviceExtension;
  1092. NTSTATUS status = STATUS_SUCCESS;
  1093. DebugPrint((PCMCIA_DEBUG_CONFIG, "skt %08x irq=0x%x\n",
  1094. Socket,
  1095. SocketConfig->Irq));
  1096. //
  1097. // Set the IRQ on the controller.
  1098. //
  1099. if (SocketConfig->Irq) {
  1100. cardRequest.RequestType = IRQ_REQUEST;
  1101. cardRequest.u.Irq.AssignedIRQ = (UCHAR) SocketConfig->Irq;
  1102. cardRequest.u.Irq.ReadyIRQ = (UCHAR) SocketConfig->ReadyIrq;
  1103. if (!PcmciaProcessConfigureRequest(fdoExtension, Socket, &cardRequest)) {
  1104. status = STATUS_UNSUCCESSFUL;
  1105. DebugPrint((PCMCIA_DEBUG_FAIL, "Failed to configure PcCardIrq for socket %x\n", Socket));
  1106. }
  1107. }
  1108. return status;
  1109. }
  1110. NTSTATUS
  1111. PcmciaConfigurePcCardRegisters(
  1112. PPDO_EXTENSION pdoExtension
  1113. )
  1114. /*++
  1115. Routine Description:
  1116. This routine does the work of configuring the function configuration registers
  1117. on the card.
  1118. Arguments:
  1119. pdoExtension - Pointer to the physical device object extension for the pc-card
  1120. Return value:
  1121. status
  1122. --*/
  1123. {
  1124. PSOCKET Socket = pdoExtension->Socket;
  1125. PSOCKET_CONFIGURATION SocketConfig = pdoExtension->SocketConfiguration;
  1126. PSOCKET_DATA socketData ;
  1127. CARD_REQUEST cardRequest = {0};
  1128. PFDO_EXTENSION fdoExtension = Socket->DeviceExtension;
  1129. NTSTATUS status = STATUS_UNSUCCESSFUL;
  1130. ULONG ccrBase;
  1131. PFUNCTION_CONFIGURATION fnConfig;
  1132. UCHAR configIndex;
  1133. //
  1134. // Set up the configuration index on the PCCARD.
  1135. //
  1136. cardRequest.RequestType = CONFIGURE_REQUEST;
  1137. fnConfig = SocketConfig->FunctionConfiguration;
  1138. socketData = pdoExtension->SocketData;
  1139. ASSERT(socketData != NULL);
  1140. do {
  1141. cardRequest.u.Config.RegisterWriteMask = 0;
  1142. if (fnConfig) {
  1143. //
  1144. // MF card -
  1145. // pick up the base and options from the linked list
  1146. //
  1147. ccrBase = fnConfig->ConfigRegisterBase;
  1148. configIndex = fnConfig->ConfigOptions;
  1149. } else {
  1150. //
  1151. // Single function card -
  1152. // get the base and index from base config structure
  1153. //
  1154. ccrBase = SocketConfig->ConfigRegisterBase;
  1155. configIndex = SocketConfig->IndexForCurrentConfiguration;
  1156. }
  1157. DebugPrint((PCMCIA_DEBUG_CONFIG, "pdo %08x config registers ccr %x\n", pdoExtension->DeviceObject, ccrBase));
  1158. //
  1159. // We support only 2 interfaces:
  1160. // Memory only
  1161. // I/o and memory
  1162. // We consider a card to be memory only if:
  1163. // The card is of device type PCCARD_TYPE_MEMORY: this is true
  1164. // for flash memory cards currently
  1165. // OR
  1166. // The card doesn't have any i/o ranges & the config register base is 0.
  1167. //
  1168. if (((ccrBase == 0) && (SocketConfig->NumberOfIoPortRanges == 0)) ||
  1169. (socketData->DeviceType == PCCARD_TYPE_MEMORY) ||
  1170. (socketData->DeviceType == PCCARD_TYPE_FLASH_MEMORY)) {
  1171. cardRequest.u.Config.InterfaceType = CONFIG_INTERFACE_MEM;
  1172. } else {
  1173. //
  1174. // i/o mem card
  1175. //
  1176. cardRequest.u.Config.ConfigBase = ccrBase;
  1177. cardRequest.u.Config.InterfaceType = CONFIG_INTERFACE_IO_MEM;
  1178. cardRequest.u.Config.RegisterWriteMask |= REGISTER_WRITE_CONFIGURATION_INDEX;
  1179. cardRequest.u.Config.ConfigIndex = configIndex;
  1180. if (IsConfigRegisterPresent(socketData, 1)) {
  1181. cardRequest.u.Config.RegisterWriteMask |= REGISTER_WRITE_CARD_CONFIGURATION;
  1182. cardRequest.u.Config.CardConfiguration = 0;
  1183. }
  1184. if (fnConfig) {
  1185. //
  1186. // MF card - set up the rest of the configuration registers
  1187. //
  1188. // Just check audio for now
  1189. if (fnConfig->ConfigFlags & 0x8) {
  1190. // probably a modem
  1191. cardRequest.u.Config.CardConfiguration = 0x08;
  1192. }
  1193. if (fnConfig->ConfigOptions & 0x02) {
  1194. cardRequest.u.Config.IoBaseRegister = fnConfig->IoBase;
  1195. cardRequest.u.Config.IoLimitRegister = fnConfig->IoLimit;
  1196. cardRequest.u.Config.RegisterWriteMask |= (REGISTER_WRITE_IO_BASE | REGISTER_WRITE_IO_LIMIT);
  1197. }
  1198. } else if (IsDeviceFlagSet(pdoExtension, PCMCIA_PDO_ENABLE_AUDIO)) {
  1199. //
  1200. // Request that the audio pin in the card configuration register
  1201. // be set.
  1202. //
  1203. cardRequest.u.Config.CardConfiguration = 0x08;
  1204. }
  1205. }
  1206. if (!PcmciaProcessConfigureRequest(fdoExtension, Socket, &cardRequest)) {
  1207. DebugPrint((PCMCIA_DEBUG_FAIL, "Failed to configure PcCardRegisters for PDO %x\n", pdoExtension->DeviceObject));
  1208. return status;
  1209. }
  1210. if (fnConfig) {
  1211. fnConfig = fnConfig->Next;
  1212. } else {
  1213. //
  1214. // Remember that the socket is configured and what index was used.
  1215. //
  1216. socketData->ConfigIndexUsed = configIndex;
  1217. }
  1218. } while(fnConfig);
  1219. status = STATUS_SUCCESS;
  1220. return status;
  1221. }
  1222. VOID
  1223. PcmciaConfigureModemHack(
  1224. IN PSOCKET Socket,
  1225. IN PSOCKET_CONFIGURATION SocketConfig
  1226. )
  1227. /*++
  1228. Routine Description:
  1229. This routine does magic to wake the modem up. It is written to accomodate
  1230. the Motorola MobileSURFR 56k, but there may be other modems that need it.
  1231. Arguments:
  1232. Socket - Pointer to the socket containing the PC-Card
  1233. SocketConfig - Pointer to the socket configuration structure which contains the
  1234. resources required to enable this pc-card
  1235. Return value:
  1236. status
  1237. --*/
  1238. {
  1239. static const ULONG ValidPortBases[4] = {0x3f8, 0x2f8, 0x3e8, 0x2e8};
  1240. ULONG i;
  1241. UCHAR ch;
  1242. ULONG base;
  1243. for (i = 0; i < 4; i++) {
  1244. base = SocketConfig->Io[0].Base;
  1245. if (base == ValidPortBases[i]) {
  1246. DebugPrint((PCMCIA_DEBUG_CONFIG, "skt %08x ModemHack base %x\n", Socket, base));
  1247. // read and write the modem control register
  1248. ch = READ_PORT_UCHAR((PUCHAR)ULongToPtr(base + 4));
  1249. WRITE_PORT_UCHAR((PUCHAR)ULongToPtr(base + 4), ch);
  1250. break;
  1251. }
  1252. }
  1253. }
  1254. VOID
  1255. PcmciaSocketDeconfigure(
  1256. IN PSOCKET Socket
  1257. )
  1258. /*++
  1259. Routine Description:
  1260. deconfigures the card
  1261. Arguments:
  1262. Socket - Pointer to the socket containing the PC-Card
  1263. Return Value
  1264. none
  1265. --*/
  1266. {
  1267. CARD_REQUEST cardReq;
  1268. if (IsSocketFlagSet(Socket, SOCKET_CARD_CONFIGURED)) {
  1269. cardReq.RequestType = DECONFIGURE_REQUEST;
  1270. PcmciaProcessConfigureRequest(Socket->DeviceExtension, Socket, &cardReq);
  1271. ResetSocketFlag(Socket, SOCKET_CARD_CONFIGURED);
  1272. }
  1273. //
  1274. // If a query_device_relations came in after a card was inserted, but before
  1275. // we have removed the previous card configuration, the enumeration would have been
  1276. // postponed. Here, we start it up again
  1277. //
  1278. if (IsSocketFlagSet(Socket, SOCKET_ENUMERATE_PENDING)) {
  1279. ResetSocketFlag(Socket, SOCKET_ENUMERATE_PENDING);
  1280. SetSocketFlag(Socket, SOCKET_CARD_STATUS_CHANGE);
  1281. IoInvalidateDeviceRelations(Socket->DeviceExtension->Pdo, BusRelations);
  1282. }
  1283. }
  1284. BOOLEAN
  1285. PcmciaProcessConfigureRequest(
  1286. IN PFDO_EXTENSION DeviceExtension,
  1287. IN PSOCKET Socket,
  1288. IN PCARD_REQUEST CardConfigurationRequest
  1289. )
  1290. /*++
  1291. Routine Description:
  1292. Actually configures the card
  1293. Arguments:
  1294. Context
  1295. Return Value
  1296. True
  1297. --*/
  1298. {
  1299. BOOLEAN status;
  1300. ULONG counter;
  1301. PCMCIA_ACQUIRE_DEVICE_LOCK(DeviceExtension);
  1302. //
  1303. // Configuring the card can be tricky: the user may pop out the card while
  1304. // configuration is taking place.
  1305. //
  1306. counter = 0;
  1307. do {
  1308. status = (*(Socket->SocketFnPtr->PCBProcessConfigureRequest))(Socket,
  1309. CardConfigurationRequest,
  1310. Socket->AddressPort);
  1311. if (!status) {
  1312. if (!(Socket->SocketFnPtr->PCBDetectCardInSocket(Socket))) {
  1313. //
  1314. // Somebody popped out the card
  1315. //
  1316. break;
  1317. }
  1318. }
  1319. counter++;
  1320. } while (!status && counter < PCMCIA_MAX_CONFIG_TRIES);
  1321. PCMCIA_RELEASE_DEVICE_LOCK(DeviceExtension);
  1322. return status;
  1323. }
  1324. BOOLEAN
  1325. PcmciaVerifyCardInSocket(
  1326. IN PSOCKET Socket
  1327. )
  1328. /*++
  1329. Routine Description:
  1330. This routine compares the current known state to the id of the
  1331. card in the slot to determine if the state is consistent. That is,
  1332. if there is no card in the socket, then we would expect to see no
  1333. cards enumerated in the socket data. If there is a card in the socket,
  1334. then we would expect to see the socket data match the card.
  1335. Arguments
  1336. Socket - Point to the socket to verify
  1337. Return Value
  1338. TRUE if the logical state of the socket matches its physical state
  1339. FALSE otherwise
  1340. --*/
  1341. {
  1342. NTSTATUS status;
  1343. PDEVICE_OBJECT pdo, nextPdo;
  1344. PPDO_EXTENSION pdoExtension;
  1345. BOOLEAN verified = FALSE;
  1346. try {
  1347. if (!IsCardInSocket(Socket)) {
  1348. leave;
  1349. }
  1350. if (IsCardBusCardInSocket(Socket)) {
  1351. ULONG pciConfig;
  1352. ULONG i;
  1353. //
  1354. // Cardbus card now in slot, check to see if it matches the
  1355. // PdoList state.
  1356. //
  1357. if (!Socket->PdoList) {
  1358. leave;
  1359. }
  1360. for (pdo = Socket->PdoList; pdo!=NULL; pdo=nextPdo) {
  1361. pdoExtension = pdo->DeviceExtension;
  1362. nextPdo = pdoExtension->NextPdoInSocket;
  1363. if (!IsCardBusCard(pdoExtension)) {
  1364. leave;
  1365. }
  1366. for (i = 0; i < 1000; i++) {
  1367. GetPciConfigSpace(pdoExtension, CFGSPACE_VENDOR_ID, &pciConfig, sizeof(pciConfig));
  1368. if (pdoExtension->CardBusId == pciConfig) {
  1369. break;
  1370. }
  1371. PcmciaWait(10);
  1372. }
  1373. if (i > 0) {
  1374. DebugPrint((PCMCIA_DEBUG_FAIL, "pdo %08x waited %d usec to verify device id %08x\n",
  1375. pdoExtension->DeviceObject, i*10, pdoExtension->CardBusId));
  1376. }
  1377. if (pdoExtension->CardBusId != pciConfig) {
  1378. DebugPrint((PCMCIA_DEBUG_FAIL, "pdo %08x verify device id FAILED: %08x %08x\n",
  1379. pdoExtension->DeviceObject, pdoExtension->CardBusId, pciConfig));
  1380. leave;
  1381. }
  1382. }
  1383. verified = TRUE;
  1384. } else {
  1385. //
  1386. // R2 card now in slot
  1387. //
  1388. pdo = Socket->PdoList;
  1389. if (pdo) {
  1390. pdoExtension = pdo->DeviceExtension;
  1391. if (Is16BitCard(pdoExtension)) {
  1392. //
  1393. // Invalidate the cache to force re-reading the CIS
  1394. //
  1395. pdoExtension->CisCacheSpace = 0xff;
  1396. if ((NT_SUCCESS(PcmciaParseFunctionDataForID(pdoExtension->SocketData)))) {
  1397. verified = TRUE;
  1398. }
  1399. }
  1400. }
  1401. }
  1402. } finally {
  1403. if (!verified) {
  1404. SetSocketFlag(Socket, SOCKET_CARD_STATUS_CHANGE);
  1405. }
  1406. DebugPrint((PCMCIA_DEBUG_INFO, "skt %08x - card %s\n", Socket, verified ? "not changed" : "changed!"));
  1407. }
  1408. return verified;
  1409. }