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.

672 lines
16 KiB

  1. /*
  2. * UNIMODEM "Fakemodem" controllerless driver illustrative example
  3. *
  4. * (C) 2000 Microsoft Corporation
  5. * All Rights Reserved
  6. *
  7. * The code in this module simply supports the very basic AT command parser.
  8. * This code should be completely replaced with the actual code to support
  9. * your controllerless modem.
  10. */
  11. #include "fakemodem.h"
  12. NTSTATUS
  13. FakeModemRead(
  14. IN PDEVICE_OBJECT DeviceObject,
  15. IN PIRP Irp
  16. )
  17. {
  18. PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
  19. NTSTATUS status=STATUS_UNSUCCESSFUL;
  20. KIRQL OldIrql;
  21. KIRQL CancelIrql;
  22. Irp->IoStatus.Information = 0;
  23. //
  24. // make sure the device is ready for irp's
  25. //
  26. status=CheckStateAndAddReference( DeviceObject, Irp);
  27. if (STATUS_SUCCESS != status) {
  28. //
  29. // not accepting irp's. The irp has already been completed
  30. //
  31. return status;
  32. }
  33. Irp->IoStatus.Status=STATUS_PENDING;
  34. IoMarkIrpPending(Irp);
  35. KeAcquireSpinLock(&deviceExtension->SpinLock, &OldIrql);
  36. //
  37. // make irp cancelable
  38. //
  39. IoAcquireCancelSpinLock(&CancelIrql);
  40. IoSetCancelRoutine(Irp, ReadCancelRoutine);
  41. IoReleaseCancelSpinLock(CancelIrql);
  42. //
  43. // put it on queue
  44. //
  45. InsertTailList(&deviceExtension->ReadQueue, &Irp->Tail.Overlay.ListEntry);
  46. KeReleaseSpinLock(&deviceExtension->SpinLock, OldIrql);
  47. //
  48. // call the real work function to process the irps
  49. //
  50. ReadIrpWorker( DeviceObject);
  51. RemoveReferenceForDispatch(DeviceObject);
  52. return STATUS_PENDING;
  53. }
  54. NTSTATUS
  55. FakeModemWrite(
  56. IN PDEVICE_OBJECT DeviceObject,
  57. IN PIRP Irp
  58. )
  59. {
  60. PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
  61. NTSTATUS status=STATUS_UNSUCCESSFUL;
  62. KIRQL OldIrql;
  63. KIRQL CancelIrql;
  64. Irp->IoStatus.Information = 0;
  65. // make sure the device is ready for irp's
  66. status=CheckStateAndAddReference( DeviceObject, Irp);
  67. if (STATUS_SUCCESS != status) {
  68. // not accepting irp's. The irp has already been complted
  69. return status;
  70. }
  71. Irp->IoStatus.Status=STATUS_PENDING;
  72. IoMarkIrpPending(Irp);
  73. KeAcquireSpinLock( &deviceExtension->SpinLock, &OldIrql);
  74. // make irp cancelable
  75. IoAcquireCancelSpinLock(&CancelIrql);
  76. IoSetCancelRoutine(Irp, WriteCancelRoutine);
  77. IoReleaseCancelSpinLock(CancelIrql);
  78. // put it on queue
  79. InsertTailList( &deviceExtension->WriteQueue, &Irp->Tail.Overlay.ListEntry);
  80. KeReleaseSpinLock(&deviceExtension->SpinLock, OldIrql);
  81. // call the real work function to process the irps
  82. if (deviceExtension->Started)
  83. {
  84. WriteIrpWorker(DeviceObject);
  85. }
  86. RemoveReferenceForDispatch(DeviceObject);
  87. return STATUS_PENDING;
  88. }
  89. VOID
  90. WriteIrpWorker(
  91. IN PDEVICE_OBJECT DeviceObject
  92. )
  93. {
  94. PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
  95. NTSTATUS status=STATUS_UNSUCCESSFUL;
  96. KIRQL OldIrql;
  97. KeAcquireSpinLock( &deviceExtension->SpinLock, &OldIrql);
  98. if (deviceExtension->CurrentWriteIrp != NULL) {
  99. // already in use
  100. goto Exit;
  101. }
  102. while (!IsListEmpty(&deviceExtension->WriteQueue)) {
  103. PLIST_ENTRY ListElement;
  104. PIRP Irp;
  105. PIO_STACK_LOCATION IrpSp;
  106. KIRQL CancelIrql;
  107. ListElement=RemoveHeadList( &deviceExtension->WriteQueue);
  108. Irp=CONTAINING_RECORD(ListElement,IRP,Tail.Overlay.ListEntry);
  109. IoAcquireCancelSpinLock(&CancelIrql);
  110. if (Irp->Cancel) {
  111. // this one has been canceled
  112. Irp->IoStatus.Information=STATUS_CANCELLED;
  113. IoReleaseCancelSpinLock(CancelIrql);
  114. continue;
  115. }
  116. IoSetCancelRoutine(
  117. Irp,
  118. NULL
  119. );
  120. IoReleaseCancelSpinLock(CancelIrql);
  121. deviceExtension->CurrentWriteIrp=Irp;
  122. IrpSp=IoGetCurrentIrpStackLocation(Irp);
  123. ProcessWriteBytes( deviceExtension, Irp->AssociatedIrp.SystemBuffer,
  124. IrpSp->Parameters.Write.Length);
  125. KeReleaseSpinLock( &deviceExtension->SpinLock, OldIrql);
  126. Irp->IoStatus.Information = IrpSp->Parameters.Write.Length;
  127. RemoveReferenceAndCompleteRequest( DeviceObject, Irp, STATUS_SUCCESS);
  128. KeAcquireSpinLock( &deviceExtension->SpinLock, &OldIrql);
  129. deviceExtension->CurrentWriteIrp=NULL;
  130. }
  131. Exit:
  132. KeReleaseSpinLock( &deviceExtension->SpinLock, OldIrql);
  133. TryToSatisfyRead( deviceExtension);
  134. ReadIrpWorker( DeviceObject);
  135. ProcessConnectionStateChange( DeviceObject);
  136. return;
  137. }
  138. VOID
  139. ProcessWriteBytes(
  140. PDEVICE_EXTENSION DeviceExtension,
  141. PUCHAR Characters,
  142. ULONG Length
  143. )
  144. {
  145. UCHAR CurrentCharacter;
  146. while (Length != 0) {
  147. CurrentCharacter=*Characters++;
  148. Length--;
  149. PutCharInReadBuffer( DeviceExtension, CurrentCharacter);
  150. switch (DeviceExtension->CommandMatchState) {
  151. case COMMAND_MATCH_STATE_IDLE:
  152. if ((CurrentCharacter == 'a') || (CurrentCharacter == 'A')) {
  153. // got an A
  154. DeviceExtension->CommandMatchState=COMMAND_MATCH_STATE_GOT_A;
  155. DeviceExtension->ConnectCommand=FALSE;
  156. DeviceExtension->IgnoreNextChar=FALSE;
  157. }
  158. break;
  159. case COMMAND_MATCH_STATE_GOT_A:
  160. if ((CurrentCharacter == 't') || (CurrentCharacter == 'T')) {
  161. // got an T
  162. DeviceExtension->CommandMatchState=COMMAND_MATCH_STATE_GOT_T;
  163. } else {
  164. if (CurrentCharacter == '\r') {
  165. DeviceExtension->CommandMatchState=COMMAND_MATCH_STATE_IDLE;
  166. }
  167. }
  168. break;
  169. case COMMAND_MATCH_STATE_GOT_T:
  170. if (!DeviceExtension->IgnoreNextChar) {
  171. // the last char was not a special char
  172. // check for CONNECT command
  173. if ((CurrentCharacter == 'A') || (CurrentCharacter == 'a')) {
  174. DeviceExtension->ConnectCommand=TRUE;
  175. }
  176. if ((CurrentCharacter == 'D') || (CurrentCharacter == 'd')) {
  177. DeviceExtension->ConnectCommand=TRUE;
  178. }
  179. }
  180. DeviceExtension->IgnoreNextChar=FALSE;
  181. if ((CurrentCharacter == '&')
  182. ||
  183. (CurrentCharacter == '/')
  184. ||
  185. (CurrentCharacter == '\\')
  186. ||
  187. (CurrentCharacter == '+')
  188. ||
  189. (CurrentCharacter == '%')) {
  190. // these characters are part of are used in init
  191. // strings and may be proceeding an A or D
  192. // which we don't want to misinterpret as a dial or answer
  193. DeviceExtension->IgnoreNextChar=TRUE;
  194. }
  195. if (CurrentCharacter == '\r') {
  196. //
  197. // got a CR, send a response to the command
  198. //
  199. DeviceExtension->CommandMatchState=COMMAND_MATCH_STATE_IDLE;
  200. if (DeviceExtension->ConnectCommand) {
  201. //
  202. // place <cr><lf>CONNECT<cr><lf> in the buffer
  203. //
  204. PutCharInReadBuffer(DeviceExtension,'\r');
  205. PutCharInReadBuffer(DeviceExtension,'\n');
  206. PutCharInReadBuffer(DeviceExtension,'C');
  207. PutCharInReadBuffer(DeviceExtension,'O');
  208. PutCharInReadBuffer(DeviceExtension,'N');
  209. PutCharInReadBuffer(DeviceExtension,'N');
  210. PutCharInReadBuffer(DeviceExtension,'E');
  211. PutCharInReadBuffer(DeviceExtension,'C');
  212. PutCharInReadBuffer(DeviceExtension,'T');
  213. PutCharInReadBuffer(DeviceExtension,'\r');
  214. PutCharInReadBuffer(DeviceExtension,'\n');
  215. //
  216. // connected now raise CD
  217. //
  218. DeviceExtension->CurrentlyConnected=TRUE;
  219. DeviceExtension->ConnectionStateChanged=TRUE;
  220. } else {
  221. // place <cr><lf>OK<cr><lf> in the buffer
  222. PutCharInReadBuffer(DeviceExtension,'\r');
  223. PutCharInReadBuffer(DeviceExtension,'\n');
  224. PutCharInReadBuffer(DeviceExtension,'O');
  225. PutCharInReadBuffer(DeviceExtension,'K');
  226. PutCharInReadBuffer(DeviceExtension,'\r');
  227. PutCharInReadBuffer(DeviceExtension,'\n');
  228. }
  229. }
  230. break;
  231. default:
  232. break;
  233. }
  234. }
  235. return;
  236. }
  237. VOID
  238. PutCharInReadBuffer(
  239. PDEVICE_EXTENSION DeviceExtension,
  240. UCHAR Character
  241. )
  242. {
  243. if (DeviceExtension->BytesInReadBuffer < READ_BUFFER_SIZE) {
  244. // room in buffer
  245. DeviceExtension->ReadBuffer[DeviceExtension->ReadBufferEnd]=Character;
  246. DeviceExtension->ReadBufferEnd++;
  247. DeviceExtension->ReadBufferEnd %= READ_BUFFER_SIZE;
  248. DeviceExtension->BytesInReadBuffer++;
  249. }
  250. return;
  251. }
  252. VOID
  253. ReadIrpWorker(
  254. PDEVICE_OBJECT DeviceObject
  255. )
  256. {
  257. PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
  258. NTSTATUS status=STATUS_UNSUCCESSFUL;
  259. KIRQL OldIrql;
  260. KeAcquireSpinLock( &deviceExtension->SpinLock, &OldIrql);
  261. while ((deviceExtension->CurrentReadIrp == NULL)
  262. && !IsListEmpty(&deviceExtension->ReadQueue)) {
  263. PLIST_ENTRY ListElement;
  264. PIRP Irp;
  265. PIO_STACK_LOCATION IrpSp;
  266. KIRQL CancelIrql;
  267. ListElement=RemoveHeadList( &deviceExtension->ReadQueue);
  268. Irp=CONTAINING_RECORD(ListElement,IRP,Tail.Overlay.ListEntry);
  269. IoAcquireCancelSpinLock(&CancelIrql);
  270. if (Irp->Cancel) {
  271. // this one has been canceled
  272. Irp->IoStatus.Information=STATUS_CANCELLED;
  273. IoReleaseCancelSpinLock(CancelIrql);
  274. continue;
  275. }
  276. IoSetCancelRoutine(Irp, NULL);
  277. IoReleaseCancelSpinLock(CancelIrql);
  278. deviceExtension->CurrentReadIrp=Irp;
  279. KeReleaseSpinLock(&deviceExtension->SpinLock, OldIrql);
  280. TryToSatisfyRead( deviceExtension);
  281. KeAcquireSpinLock(&deviceExtension->SpinLock, &OldIrql);
  282. }
  283. KeReleaseSpinLock( &deviceExtension->SpinLock, OldIrql);
  284. return;
  285. }
  286. VOID
  287. TryToSatisfyRead(
  288. PDEVICE_EXTENSION DeviceExtension
  289. )
  290. {
  291. NTSTATUS status=STATUS_UNSUCCESSFUL;
  292. KIRQL OldIrql;
  293. PIRP Irp=NULL;
  294. PIO_STACK_LOCATION IrpSp;
  295. ULONG BytesToMove;
  296. ULONG FirstHalf;
  297. ULONG SecondHalf;
  298. KeAcquireSpinLock(
  299. &DeviceExtension->SpinLock,
  300. &OldIrql
  301. );
  302. if ((DeviceExtension->CurrentReadIrp != NULL) && (DeviceExtension->BytesInReadBuffer > 0)) {
  303. //
  304. // there is an IRP and there are characters waiting
  305. //
  306. Irp=DeviceExtension->CurrentReadIrp;
  307. IrpSp=IoGetCurrentIrpStackLocation(Irp);
  308. BytesToMove=IrpSp->Parameters.Read.Length < DeviceExtension->BytesInReadBuffer ?
  309. IrpSp->Parameters.Read.Length : DeviceExtension->BytesInReadBuffer;
  310. if (DeviceExtension->ReadBufferBegin+BytesToMove > READ_BUFFER_SIZE) {
  311. //
  312. // the buffer is wrapped around, have move in two pieces
  313. //
  314. FirstHalf=READ_BUFFER_SIZE-DeviceExtension->ReadBufferBegin;
  315. SecondHalf=BytesToMove-FirstHalf;
  316. RtlCopyMemory(
  317. Irp->AssociatedIrp.SystemBuffer,
  318. &DeviceExtension->ReadBuffer[DeviceExtension->ReadBufferBegin],
  319. FirstHalf);
  320. RtlCopyMemory(
  321. (PUCHAR)Irp->AssociatedIrp.SystemBuffer+FirstHalf,
  322. &DeviceExtension->ReadBuffer[0], SecondHalf);
  323. } else {
  324. //
  325. // can do it all at once
  326. //
  327. RtlCopyMemory(
  328. Irp->AssociatedIrp.SystemBuffer,
  329. &DeviceExtension->ReadBuffer[DeviceExtension->ReadBufferBegin],
  330. BytesToMove);
  331. }
  332. //
  333. // fix up queue pointers
  334. //
  335. DeviceExtension->BytesInReadBuffer-=BytesToMove;
  336. DeviceExtension->ReadBufferBegin+= BytesToMove;
  337. DeviceExtension->ReadBufferBegin %= READ_BUFFER_SIZE;
  338. Irp->IoStatus.Information=BytesToMove;
  339. }
  340. KeReleaseSpinLock( &DeviceExtension->SpinLock, OldIrql);
  341. if (Irp != NULL) {
  342. //
  343. // if irp isn't null, then we handled one
  344. //
  345. RemoveReferenceAndCompleteRequest(
  346. DeviceExtension->DeviceObject, Irp, STATUS_SUCCESS);
  347. DeviceExtension->CurrentReadIrp=NULL;
  348. }
  349. return;
  350. }
  351. VOID
  352. WriteCancelRoutine(
  353. IN PDEVICE_OBJECT DeviceObject,
  354. IN PIRP Irp
  355. )
  356. {
  357. PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
  358. NTSTATUS status=STATUS_UNSUCCESSFUL;
  359. KIRQL OldIrql;
  360. //
  361. // release the cancel spinlock to avaoid deadlocks with deviceextension spinlock
  362. //
  363. IoReleaseCancelSpinLock(Irp->CancelIrql);
  364. KeAcquireSpinLock( &deviceExtension->SpinLock, &OldIrql);
  365. if (Irp->IoStatus.Information != STATUS_CANCELLED) {
  366. //
  367. // the irp is still in the queue, remove it
  368. //
  369. RemoveEntryList( &Irp->Tail.Overlay.ListEntry);
  370. }
  371. KeReleaseSpinLock( &deviceExtension->SpinLock, OldIrql);
  372. Irp->IoStatus.Information = 0;
  373. RemoveReferenceAndCompleteRequest( DeviceObject, Irp, STATUS_CANCELLED);
  374. return;
  375. }
  376. VOID
  377. ReadCancelRoutine(
  378. IN PDEVICE_OBJECT DeviceObject,
  379. IN PIRP Irp
  380. )
  381. {
  382. PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
  383. NTSTATUS status=STATUS_UNSUCCESSFUL;
  384. KIRQL OldIrql;
  385. // release the cancel spinlock to avoid deadlocks with deviceextension spinlock
  386. IoReleaseCancelSpinLock(Irp->CancelIrql);
  387. KeAcquireSpinLock( &deviceExtension->SpinLock, &OldIrql);
  388. if (Irp->IoStatus.Information != STATUS_CANCELLED) {
  389. // the irp is still in the queue, remove it
  390. RemoveEntryList( &Irp->Tail.Overlay.ListEntry);
  391. }
  392. KeReleaseSpinLock( &deviceExtension->SpinLock, OldIrql);
  393. Irp->IoStatus.Information = 0;
  394. RemoveReferenceAndCompleteRequest( DeviceObject, Irp, STATUS_CANCELLED);
  395. return;
  396. }
  397. VOID
  398. ProcessConnectionStateChange(
  399. IN PDEVICE_OBJECT DeviceObject
  400. )
  401. {
  402. PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
  403. KIRQL OldIrql;
  404. PIRP CurrentWaitIrp=NULL;
  405. KeAcquireSpinLock( &deviceExtension->SpinLock, &OldIrql);
  406. if (deviceExtension->ConnectionStateChanged) {
  407. //
  408. // state changed
  409. //
  410. deviceExtension->ConnectionStateChanged=FALSE;
  411. if (deviceExtension->CurrentlyConnected) {
  412. //
  413. // now it is connected, raise CD
  414. //
  415. deviceExtension->ModemStatus |= SERIAL_DCD_STATE;
  416. } else {
  417. //
  418. // not connected any more, clear CD
  419. //
  420. deviceExtension->ModemStatus &= ~(SERIAL_DCD_STATE);
  421. }
  422. if (deviceExtension->CurrentMask & SERIAL_EV_RLSD) {
  423. //
  424. // app want's to know about these changes, tell it
  425. //
  426. CurrentWaitIrp=deviceExtension->CurrentMaskIrp;
  427. deviceExtension->CurrentMaskIrp=NULL;
  428. }
  429. }
  430. KeReleaseSpinLock( &deviceExtension->SpinLock, OldIrql);
  431. if (CurrentWaitIrp != NULL) {
  432. D_TRACE(DbgPrint("FAKEMODEM: ProcessConectionState\n");)
  433. *((PULONG)CurrentWaitIrp->AssociatedIrp.SystemBuffer)=SERIAL_EV_RLSD;
  434. CurrentWaitIrp->IoStatus.Information=sizeof(ULONG);
  435. RemoveReferenceAndCompleteRequest(
  436. deviceExtension->DeviceObject, CurrentWaitIrp, STATUS_SUCCESS);
  437. }
  438. return;
  439. }