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.

892 lines
25 KiB

  1. /*++
  2. Copyright (c) 1991 Microsoft Corporation
  3. Module Name:
  4. llcsmsb.c
  5. Abstract:
  6. The module implements the subroutines used by a IEEE 802.2
  7. compatible state machine.
  8. To understand the procedure of this module, you should read
  9. Chapters 11 and 12 in IBM Token-Ring Architecture Reference.
  10. The procedures in this module can be called only when
  11. SendSpinLock is set.
  12. Contents:
  13. SaveStatusChangeEvent
  14. ResendPackets
  15. UpdateVa
  16. UpdateVaChkpt
  17. AdjustWw
  18. SendAck
  19. QueueCommandCompletion
  20. (DynamicWindowAlgorithm)
  21. Author:
  22. Antti Saarenheimo (o-anttis) 23-MAY-1991
  23. Revision History:
  24. --*/
  25. #include <llc.h>
  26. //
  27. // private prototypes
  28. //
  29. VOID
  30. DynamicWindowAlgorithm(
  31. IN OUT PDATA_LINK pLink // data link station strcuture
  32. );
  33. //
  34. // functions
  35. //
  36. VOID
  37. SaveStatusChangeEvent(
  38. IN PDATA_LINK pLink,
  39. IN PUCHAR puchLlcHdr,
  40. IN BOOLEAN boolResponse
  41. )
  42. /*++
  43. Routine Description:
  44. Procedure saves Status Change event of the link to the event queue.
  45. to be indicated later to upper protocol.
  46. Arguments:
  47. pLink - LLC link station object
  48. puchLlcHdr - the received LLC header
  49. boolResponse - flag set if the received frame was response
  50. Return Value:
  51. None
  52. --*/
  53. {
  54. UINT Event;
  55. PEVENT_PACKET pEvent;
  56. PVOID hClientHandle;
  57. PADAPTER_CONTEXT pAdapterContext = pLink->Gen.pAdapterContext;
  58. ASSUME_IRQL(DISPATCH_LEVEL);
  59. //
  60. // Set the ethernet header length (== type) the same as
  61. // in the current receive frame, that was either the first SABME or
  62. // any response to it, that opened the link connection.
  63. //
  64. if ((pLink->DlcStatus.StatusCode & (CONFIRM_CONNECT | LLC_INDICATE_CONNECT_REQUEST))
  65. && pAdapterContext->RcvLanHeaderLength != pLink->cbLanHeaderLength
  66. && pLink->Gen.pLlcBinding->EthernetType == LLC_ETHERNET_TYPE_AUTO) {
  67. pLink->cbLanHeaderLength = (UCHAR)pLink->Gen.pAdapterContext->RcvLanHeaderLength;
  68. }
  69. //
  70. // Handle first the disconnect/connect complete
  71. //
  72. if (pLink->DlcStatus.StatusCode & (CONFIRM_CONNECT | CONFIRM_DISCONNECT | CONFIRM_CONNECT_FAILED)) {
  73. //
  74. // We cannot indicate any events to non-existing stations
  75. //
  76. if (pLink->Gen.hClientHandle != NULL) {
  77. if (pLink->DlcStatus.StatusCode & CONFIRM_DISCONNECT) {
  78. QueueCommandCompletion((PLLC_OBJECT)pLink,
  79. LLC_DISCONNECT_COMPLETION,
  80. STATUS_SUCCESS
  81. );
  82. }
  83. if (pLink->DlcStatus.StatusCode & (CONFIRM_CONNECT | CONFIRM_CONNECT_FAILED)) {
  84. UINT Status;
  85. if (pLink->DlcStatus.StatusCode & CONFIRM_CONNECT) {
  86. //
  87. // Set the T1 timeout for the first checkpointing state.
  88. // This value will be changed when we have got the response
  89. // to the first poll, but the initial value is big to
  90. // be able to run DLC over a WAN connection
  91. //
  92. pLink->AverageResponseTime = 100; // 100 * 40 = 4 seconds
  93. pLink->Flags |= DLC_FIRST_POLL;
  94. InitializeLinkTimers(pLink);
  95. Status = STATUS_SUCCESS;
  96. } else {
  97. Status = DLC_STATUS_CONNECT_FAILED;
  98. }
  99. QueueCommandCompletion((PLLC_OBJECT)pLink,
  100. LLC_CONNECT_COMPLETION,
  101. Status
  102. );
  103. }
  104. }
  105. pLink->DlcStatus.StatusCode &= ~(CONFIRM_CONNECT | CONFIRM_DISCONNECT | CONFIRM_CONNECT_FAILED);
  106. }
  107. if (pLink->DlcStatus.StatusCode != 0) {
  108. if (pLink->DlcStatus.StatusCode & INDICATE_FRMR_SENT) {
  109. #if LLC_DBG
  110. PrintLastInputs("FRMR SENT!\n", pLink);
  111. #endif
  112. pLink->DlcStatus.FrmrData.Command = puchLlcHdr[2];
  113. pLink->DlcStatus.FrmrData.Ctrl = puchLlcHdr[3];
  114. if ((pLink->DlcStatus.FrmrData.Command & LLC_S_U_TYPE_MASK) == LLC_U_TYPE) {
  115. pLink->DlcStatus.FrmrData.Ctrl = 0;
  116. }
  117. pLink->DlcStatus.FrmrData.Vs = pLink->Vs;
  118. pLink->DlcStatus.FrmrData.Vr = pLink->Vr | boolResponse;
  119. } else if (pLink->DlcStatus.StatusCode & INDICATE_FRMR_RECEIVED) {
  120. #if LLC_DBG
  121. PrintLastInputs("FRMR RECEIVED!\n", pLink);
  122. DbgBreakPoint();
  123. #endif
  124. LlcMemCpy(&pLink->DlcStatus.FrmrData,
  125. &puchLlcHdr[3],
  126. sizeof(LLC_FRMR_INFORMATION)
  127. );
  128. }
  129. //
  130. // A remote connect request may have created a link station
  131. // in link driver. The upper protocol must be able to separate
  132. // sap handle from the data link
  133. //
  134. if (pLink->Gen.hClientHandle == NULL) {
  135. //
  136. // Indicate the event on the sap, because the upper protocol
  137. // has not yet any link station create for this link, because
  138. // it has been created remotely.
  139. //
  140. hClientHandle = pLink->pSap->Gen.hClientHandle,
  141. Event = LLC_STATUS_CHANGE_ON_SAP;
  142. } else {
  143. //
  144. // Indicate the event on the link station
  145. //
  146. hClientHandle = pLink->Gen.hClientHandle,
  147. Event = LLC_STATUS_CHANGE;
  148. }
  149. //
  150. // The indications of the received SABMEs must be queued,
  151. // but all other events are indicated directy to
  152. // the upper protocol, because those indications must never
  153. // be lost because of an out of memory condition.
  154. //
  155. if (pLink->DlcStatus.StatusCode & INDICATE_CONNECT_REQUEST) {
  156. pEvent = ALLOCATE_PACKET_LLC_PKT(pAdapterContext->hPacketPool);
  157. if (pEvent != NULL) {
  158. LlcInsertTailList(&pAdapterContext->QueueEvents, pEvent);
  159. pEvent->pBinding = pLink->Gen.pLlcBinding;
  160. pEvent->hClientHandle = hClientHandle;
  161. pEvent->Event = Event;
  162. pEvent->pEventInformation = (PVOID)&pLink->DlcStatus;
  163. //
  164. // RLF 11/18/92
  165. //
  166. // INDICATE_CONNECT_REQUEST is generated when we receive a
  167. // SABME for a station in the DISCONNECTED state. However,
  168. // we need to generate either INDICATE_CONNECT_REQUEST (0x0400)
  169. // or INDICATE_RESET (0x0800) depending on whether the SABME
  170. // created the link station or whether it was created by a
  171. // DLC.OPEN.STATION at this end. pLink->RemoteOpen is TRUE if
  172. // the link was created due to receipt of the SABME
  173. // This routine is only called by RunStateMachine and
  174. // INDICATE_CONNECT_REQUEST is never combined with any other
  175. // status codes
  176. //
  177. //pEvent->SecondaryInfo = pLink->DlcStatus.StatusCode;
  178. pEvent->SecondaryInfo = pLink->RemoteOpen
  179. ? INDICATE_CONNECT_REQUEST
  180. : INDICATE_RESET;
  181. }
  182. } else {
  183. //
  184. // We must do this with a locked SendSpinLock, because
  185. // otherwise somebody might delete the link, while
  186. // we are still using it.
  187. // THIS IS ACTAULLY QUITE DIRTY (callback with locked
  188. // spinlocks), BUT WE CANNOT USE THE PACKET POOLS WHEN
  189. // WE INDICATE AN EVENT, THAT MUST NOT BE LOST!
  190. //
  191. pLink->Gen.pLlcBinding->pfEventIndication(
  192. pLink->Gen.pLlcBinding->hClientContext,
  193. hClientHandle,
  194. Event,
  195. (PVOID)&pLink->DlcStatus,
  196. pLink->DlcStatus.StatusCode
  197. );
  198. }
  199. //
  200. // We must cancel all queued transmit commands, if the link
  201. // is lost, disconnected or reset.
  202. //
  203. if (pLink->DlcStatus.StatusCode
  204. & (INDICATE_LINK_LOST
  205. | INDICATE_DM_DISC_RECEIVED
  206. | INDICATE_FRMR_RECEIVED
  207. | INDICATE_FRMR_SENT
  208. | INDICATE_RESET)) {
  209. CancelTransmitCommands((PLLC_OBJECT)pLink, DLC_STATUS_LINK_NOT_TRANSMITTING);
  210. }
  211. //
  212. // Reset the status code!
  213. //
  214. pLink->DlcStatus.StatusCode = 0;
  215. }
  216. }
  217. VOID
  218. ResendPackets(
  219. IN OUT PDATA_LINK pLink // data link strcuture
  220. )
  221. /*++
  222. Routine Description:
  223. Function initializes the send process to resend the rejected
  224. packets and resets the adaptive working window variables.
  225. The operations defined in IBM state machine are:
  226. Vs=Nr, Ww=1, Ia_Ct=0, but this also resorts the packet queue.
  227. Arguments:
  228. pLink - LLC link station object
  229. Return Value:
  230. None
  231. --*/
  232. {
  233. PADAPTER_CONTEXT pAdapterContext = pLink->Gen.pAdapterContext;
  234. //
  235. // Complete all frames, that were acknowledged by the reject (if any)
  236. //
  237. if (pLink->Nr != pLink->Va) {
  238. DynamicWindowAlgorithm(pLink);
  239. }
  240. if ( (pLink->Vs != pLink->VsMax) &&
  241. (((pLink->Vs < pLink->VsMax) && (pLink->Nr >= pLink->Vs) &&
  242. (pLink->Nr <= pLink->VsMax)
  243. ) ||
  244. (!((pLink->Vs > pLink->VsMax) && (pLink->Nr > pLink->VsMax) &&
  245. (pLink->Nr < pLink->Vs))
  246. )
  247. )
  248. )
  249. {
  250. return;
  251. }
  252. //
  253. // Move all rejected packets from the queue sent packets back
  254. // to the send queue. We have already completed all acknowledged
  255. // packets => we can take the packet from the tail and put them
  256. // to the head of the send queue.
  257. // We can trust, that the reject window is correct, because
  258. // Nr has been checked before the state machine was called.
  259. // (note: the counters are a modulo 256, but we use bytes).
  260. //
  261. for (;pLink->Vs != pLink->Nr; pLink->Vs -= 2) {
  262. PLLC_PACKET pPacket;
  263. if (!IsListEmpty(&pLink->SentQueue) ){
  264. pLink->Statistics.I_FrameTransmissionErrors++;
  265. if (pLink->Statistics.I_FrameTransmissionErrors == 0x80) {
  266. pLink->DlcStatus.StatusCode |= INDICATE_DLC_COUNTER_OVERFLOW;
  267. }
  268. pPacket = (PLLC_PACKET)LlcRemoveTailList(&pLink->SentQueue);
  269. LlcInsertHeadList(&pLink->SendQueue.ListHead, pPacket);
  270. }
  271. }
  272. //
  273. // The procedure starts the send process only if it has been
  274. // enabled by the state machine. Only StartSendProcessLocked
  275. // may start the process, if it has been locked by
  276. // StopSendProcess
  277. //
  278. StartSendProcess(pAdapterContext, pLink);
  279. //
  280. // Reset the current window (Vs=Nr, Ww=1, Ia_Ct=0)
  281. //
  282. pLink->Ww = 2;
  283. pLink->Ia_Ct = 0;
  284. }
  285. VOID
  286. UpdateVa(
  287. IN OUT PDATA_LINK pLink // data link station strcuture
  288. )
  289. /*++
  290. Routine Description:
  291. Function updates Va (last valid Nr received) and
  292. makes also some other actions needed in the normal
  293. receive operations.
  294. Arguments:
  295. pLink - LLC link station object
  296. Return Value:
  297. None
  298. --*/
  299. {
  300. //
  301. // Reset the initilization state variable
  302. //
  303. pLink->Vi = 0;
  304. //
  305. // Update the receive state variable Va (the last valid received
  306. // frame), but update some Ww variables before that.
  307. //
  308. if (pLink->Nr != pLink->Va) {
  309. DynamicWindowAlgorithm(pLink);
  310. //
  311. // T1 reply timer must be running as far as there are
  312. // out (or sent) any unacknowledged frames.
  313. // Ti timer must be stopped whenever T1 is running and vice versa
  314. //
  315. if (pLink->Nr != pLink->Vs) {
  316. //
  317. // There still are some unacknowledged frames,
  318. // start or restart the reply timer.
  319. //
  320. StartTimer(&pLink->T1); // reply timer
  321. StopTimer(&pLink->Ti);
  322. } else {
  323. //
  324. // All sent frames have been acknowledged,
  325. // => We may stop the reply timer.
  326. //
  327. StopTimer(&pLink->T1); // reply timer
  328. StartTimer(&pLink->Ti);
  329. }
  330. //
  331. // Reset the I- frame retry counter whenever we do
  332. // any kind of progress
  333. //
  334. pLink->Is_Ct = pLink->N2;
  335. }
  336. }
  337. VOID
  338. UpdateVaChkpt(
  339. IN OUT PDATA_LINK pLink // data link station strcuture
  340. )
  341. /*++
  342. Routine Description:
  343. Function updates Va (last valid Nr received) and
  344. makes also some other actions needed in the check
  345. point receive operations.
  346. Arguments:
  347. pLink - LLC link station object
  348. Return Value:
  349. None
  350. --*/
  351. {
  352. UCHAR OrginalWw = pLink->Ww;
  353. //
  354. // Reset the initilization state variable
  355. //
  356. pLink->Vi = 0;
  357. //
  358. // Update the receive state variable Va (the last valid received
  359. // frame), but update the acknowledged frames counter before that.
  360. // That counter is used by the Adaptive window algorithm.
  361. //
  362. if (pLink->Nr != pLink->Va) {
  363. //
  364. // Run adaptive transmit window (TW/T1) algorithm.
  365. //
  366. if (pLink->Ww == pLink->TW) {
  367. //
  368. // Update the counters of adaptive transmit window algorithm,
  369. // We need (LLC_MAX_T1_TO_I_RATIO/2) successful transmit using
  370. // the full window size, before we again try increase the
  371. // maximum transmit window size.
  372. //
  373. pLink->FullWindowTransmits += pLink->Ww;
  374. if ((UINT)pLink->FullWindowTransmits >= LLC_MAX_T1_TO_I_RATIO) {
  375. pLink->FullWindowTransmits = 2;
  376. if (pLink->TW < pLink->MaxOut) {
  377. pLink->TW += 2;
  378. }
  379. }
  380. }
  381. DynamicWindowAlgorithm(pLink);
  382. //
  383. // Reset the I- frame and Poll retry counters whenever
  384. // we do any kind of progress with the acknowledged I-frames.
  385. //
  386. pLink->P_Ct = pLink->N2;
  387. pLink->Is_Ct = pLink->N2;
  388. }
  389. //
  390. // Stop the reply timer, if we are not waiting
  391. // anything else from the other side.
  392. //
  393. if (pLink->Nr != pLink->Vs) {
  394. //
  395. // There still are some unacknowledged frames,
  396. // start or restart the reply timer.
  397. //
  398. StartTimer(&pLink->T1); // reply timer
  399. StopTimer(&pLink->Ti);
  400. } else {
  401. //
  402. // All sent frames have been acknowledged,
  403. // => We may stop the reply timer.
  404. //
  405. StopTimer(&pLink->T1); // reply timer
  406. StartTimer(&pLink->Ti);
  407. }
  408. //
  409. // Bugfix in 3-3-1992, Vp (!= pLInk->Vp) seems to be wrong here,
  410. // because in many cases it is not set when a checkpointing state is
  411. // entered. In the chekpointing state Vs=Vp, because the
  412. // send process is always stopped in our implementation,
  413. // when a checkpointing state is entered.
  414. // Why do we actually need the Vp? A: It's needed to prevent
  415. // forever looping between chekpointing and open states.
  416. //
  417. if (pLink->Nr != pLink->Vs) {
  418. //
  419. // We use a very simple adaptive transmit window (TW/T1) algorithm:
  420. //
  421. // TW is set the same as the last successful Working window
  422. // size (Ww), whenever T1 has been lost. We increase TW after
  423. // a constant number of full window transmits.
  424. //
  425. // The more complicated TW/T1 algorithms usually work worse
  426. // produce more code and decrease the performace, but this algorithm
  427. // is quite vulnerable to unreliable media (=> TW=1, a lot of T1
  428. // timeouts). A better algorithm could also try to increase TW,
  429. // if the ratio of T1 timeouts to transferred I- frames increases
  430. // when the TW is decreased. I will leave this matter to my
  431. // successors (AS 19-MAR-1992).
  432. //
  433. // Another problem in this algorithm is the too fast increasing
  434. // of big working windows (Ww). In that case Ww is incremented by
  435. // more than 1 => the other side may lose I-frames before I-c1.
  436. // This is not very serious situation, we reset working window to 1
  437. // and start it again.
  438. //
  439. //
  440. // Update the transmit window always after a T1 timeout.
  441. //
  442. if (pLink->P_Ct < pLink->N2) {
  443. //
  444. // Reset the maximum transmit window size whenever
  445. // we have lost the last I-C1 (poll).
  446. // In the first time the current windows size
  447. // becomes the maximum windows size (we never hit
  448. // the maximum tranmit window size, if the other
  449. // size have receive problems). This algorithm assumes,
  450. // that we have otherwise very reliable network.
  451. //
  452. if (OrginalWw > 2) {
  453. pLink->TW = (UCHAR)(OrginalWw - 2);
  454. } else if (pLink->TW > 2) {
  455. //
  456. // We may have already reset Ww because of REJ of a
  457. // I-c0 before the actual poll, that was lost also.
  458. // In that case we don't have any idea of the actual
  459. // window size, but we decrement TW in any case.
  460. //
  461. pLink->TW -= 2;
  462. }
  463. pLink->FullWindowTransmits = 2;
  464. }
  465. ResendPackets(pLink);
  466. }
  467. }
  468. VOID
  469. AdjustWw(
  470. IN OUT PDATA_LINK pLink // data link strcuture
  471. )
  472. /*++
  473. Routine Description:
  474. Procedure adjust the working window of a data link station.
  475. Arguments:
  476. pLink - LLC link station object
  477. Nr - NR of the received LLC LDPU
  478. Return Value:
  479. None
  480. --*/
  481. {
  482. //
  483. // Update the receive state variable Va (the last valid received
  484. // frame), but update some Ww variables before that.
  485. //
  486. if (pLink->Nr != pLink->Va) {
  487. DynamicWindowAlgorithm(pLink);
  488. //
  489. // Reset the I- frame and Poll retry counters whenever
  490. // we do any kind of progress with the acknowledged I-frames.
  491. //
  492. pLink->P_Ct = pLink->N2;
  493. pLink->Is_Ct = pLink->N2;
  494. }
  495. }
  496. VOID
  497. SendAck(
  498. IN OUT PDATA_LINK pLink
  499. )
  500. /*++
  501. Routine Description:
  502. Procedure sends the ack, if the received unacknowledged frames
  503. counter expires and stops the acknowledge delay timer (T2).
  504. Otherwise it start (or restarts) the acknowledge delay timer.
  505. Arguments:
  506. pLink - LLC link station object
  507. Return Value:
  508. Returns the token of the next sent command frame.
  509. --*/
  510. {
  511. pLink->Ir_Ct--;
  512. if (pLink->Ir_Ct == 0) {
  513. pLink->Ir_Ct = pLink->N3; // MaxIn
  514. StopTimer(&pLink->T2);
  515. //
  516. // Send RR-r0 to acknowledge the response
  517. //
  518. SendLlcFrame(pLink, (UCHAR)DLC_RR_TOKEN);
  519. } else {
  520. StartTimer(&pLink->T2);
  521. }
  522. }
  523. VOID
  524. QueueCommandCompletion(
  525. IN PLLC_OBJECT pLlcObject,
  526. IN UINT CompletionCode,
  527. IN UINT Status
  528. )
  529. /*++
  530. Routine Description:
  531. The function queues a command completion (if there was an allcoated
  532. packet in the completion queue).
  533. Arguments:
  534. pLlcObject - LLC object (link, sap or direct)
  535. CompletionCode - command completion code returned to upper protocol
  536. Status - returned status
  537. Return Value:
  538. None -
  539. --*/
  540. {
  541. PLLC_PACKET *ppPacket;
  542. //
  543. // Search the command from the completion list.
  544. // (use the "address of address" scanning to take the
  545. // searched element from the middle of one way linked list)
  546. //
  547. ppPacket = &pLlcObject->Gen.pCompletionPackets;
  548. while (*ppPacket != NULL
  549. && (*ppPacket)->Data.Completion.CompletedCommand != CompletionCode) {
  550. ppPacket = &(*ppPacket)->pNext;
  551. }
  552. if (*ppPacket != NULL) {
  553. PLLC_PACKET pPacket = *ppPacket;
  554. *ppPacket = pPacket->pNext;
  555. pPacket->pBinding = pLlcObject->Gen.pLlcBinding;
  556. pPacket->Data.Completion.Status = Status;
  557. pPacket->Data.Completion.CompletedCommand = CompletionCode;
  558. pPacket->Data.Completion.hClientHandle = pLlcObject->Gen.hClientHandle;
  559. #if LLC_DBG
  560. pPacket->pNext = NULL;
  561. #endif
  562. LlcInsertTailList(&pLlcObject->Gen.pAdapterContext->QueueCommands, pPacket);
  563. }
  564. }
  565. VOID
  566. DynamicWindowAlgorithm(
  567. IN OUT PDATA_LINK pLink // data link station strcuture
  568. )
  569. /*++
  570. Routine Description:
  571. The function runs the dynamic window algorithm and updates
  572. the dynamic window size of used by the link's send process.
  573. This routine also completes the acknowledged transmissions.
  574. Arguments:
  575. pLink - LLC link station object
  576. Return Value:
  577. None
  578. --*/
  579. {
  580. PADAPTER_CONTEXT pAdapterContext;
  581. //
  582. // Run Dynamic Window algorithm of IBM TR Architecture Ref:
  583. //
  584. // if (Working window less that the maximum window)
  585. // then
  586. // The Acknowledged frame count += The acknowledged frames
  587. //
  588. // if (The Acknowledged frame count >
  589. // packets to be aknowledged before next increment)
  590. // then
  591. // Increment the working window
  592. // endif
  593. // endif
  594. //
  595. if (pLink->Ww < pLink->TW) {
  596. //
  597. // The Acknowledged frame count += The acknowledged frames
  598. // (handle the wrap around of UCHAR counters)
  599. //
  600. if (pLink->Va > pLink->Nr) {
  601. pLink->Ia_Ct += (256 + pLink->Nr) - pLink->Va;
  602. } else {
  603. pLink->Ia_Ct += pLink->Nr - pLink->Va;
  604. }
  605. //
  606. // if (The Acknowledged frame count
  607. // > packets to be aknowledged before next increment)
  608. // then
  609. // Increment the working window
  610. // endif
  611. //
  612. if (pLink->Ia_Ct > pLink->Nw) {
  613. USHORT usWw;
  614. usWw = (USHORT)(pLink->Ww + (pLink->Ia_Ct / pLink->Nw) * 2);
  615. pLink->Ia_Ct = pLink->Ia_Ct % pLink->Nw;
  616. if (usWw > pLink->TW) {
  617. pLink->Ww = pLink->TW;
  618. } else {
  619. pLink->Ww = (UCHAR)usWw;
  620. }
  621. }
  622. }
  623. //
  624. // Complete all acknowledged I-frame packets
  625. //
  626. pAdapterContext = pLink->Gen.pAdapterContext;
  627. for (; pLink->Va != pLink->Nr; pLink->Va += 2) {
  628. PLLC_PACKET pPacket;
  629. MY_ASSERT(!IsListEmpty(&pLink->SentQueue));
  630. if (IsListEmpty(&pLink->SentQueue)) {
  631. return;
  632. }
  633. pPacket = LlcRemoveHeadList(&pLink->SentQueue);
  634. pPacket->Data.Completion.Status = STATUS_SUCCESS;
  635. pPacket->Data.Completion.CompletedCommand = LLC_SEND_COMPLETION;
  636. pPacket->Data.Completion.hClientHandle = pPacket->Data.Xmit.pLlcObject->Gen.hClientHandle;
  637. //
  638. // We use extra status bits to indicate, when I- packet has been both
  639. // completed by NDIS and acknowledged by the other side of the link
  640. // connection. An I- packet can be queued to the completion queue by
  641. // the second quy (either state machine or SendCompletion handler)
  642. // only when the first guy has set completed its work.
  643. // An I packet could be acknowledged by the other side before
  644. // its completion is indicated by NDIS. Dlc Driver deallocates
  645. // the packet immediately, when Llc driver completes the acknowledged
  646. // packet => possible data corruption (if packet is reused before
  647. // NDIS has completed it). This is probably not possible in a
  648. // single processor NT- system, but very possible in multiprocessor
  649. // NT or systems without a single level DPC queue (like OS/2 and DOS).
  650. //
  651. pPacket->CompletionType &= ~LLC_I_PACKET_UNACKNOWLEDGED;
  652. if (pPacket->CompletionType == LLC_I_PACKET_COMPLETE) {
  653. LlcInsertTailList(&pAdapterContext->QueueCommands, pPacket);
  654. }
  655. //
  656. // Increment counter, when the I- frame has
  657. // succesfully received and acknowledged by the other side.
  658. // We must also send status changes indication, when
  659. // the USHORT counter hits the half way.
  660. //
  661. pLink->Statistics.I_FramesTransmitted++;
  662. if (pLink->Statistics.I_FramesTransmitted == 0x8000) {
  663. pLink->DlcStatus.StatusCode |= INDICATE_DLC_COUNTER_OVERFLOW;
  664. }
  665. pLink->pSap->Statistics.FramesTransmitted++;
  666. }
  667. }