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.

1466 lines
43 KiB

  1. /*++
  2. Copyright (c) 1991 Microsoft Corporation
  3. Copyright (c) 1991 Nokia Data Systems
  4. Module Name:
  5. vrdlcbuf.c
  6. Abstract:
  7. The module implements the buffer management used by DOS DLC applications
  8. Contents:
  9. InitializeBufferPools
  10. CreateBufferPool
  11. DeleteBufferPool
  12. GetBuffers
  13. FreeBuffers
  14. CalculateBufferRequirement
  15. CopyFrame
  16. AllBuffersInPool
  17. Author:
  18. Antti Saarenheimo (o-anttis) 26-DEC-1991
  19. Notes:
  20. Originally, this code created a list of DOS buffers by keeping the segment
  21. constant, and updating the offset. For example, if a buffer pool was
  22. supplied starting at 0x1234:0000, buffers 0x100 bytes long, then the
  23. chain would be:
  24. 1234:0000 -> 1234:0100 -> 1234:0200 -> ... -> 0000:0000
  25. But it turns out that some DOS DLC apps (Rumba) expect the offset to remain
  26. constant (at 0), and the segment to change(!). Thus, given the same buffer
  27. pool address, we would have a chain:
  28. 1234:0000 -> 1244:0000 -> 1254:0000 -> ... -> 0000:0000
  29. As far as DOS apps are concerned, there is no difference, since the
  30. effective 20-bit address is the same.
  31. This is mainly done so that an app can take the USER_OFFSET field of a
  32. received buffer and glue it to the segment, without having to do any
  33. arithmetic
  34. Revision History:
  35. --*/
  36. #include <nt.h>
  37. #include <ntrtl.h> // ASSERT, DbgPrint
  38. #include <nturtl.h>
  39. #include <windows.h>
  40. #include <softpc.h> // x86 virtual machine definitions
  41. #include <vrdlctab.h>
  42. #include <vdmredir.h>
  43. #include <dlcapi.h> // Official DLC API definition
  44. #include <ntdddlc.h> // IOCTL commands
  45. #include <dlcio.h> // Internal IOCTL API interface structures
  46. #include "vrdlc.h"
  47. #include "vrdebug.h"
  48. #include "vrdlcdbg.h"
  49. //
  50. // defines
  51. //
  52. //
  53. // BUFFER_2_SIZE - this is the size of the fixed part of a DOS receive Buffer 2.
  54. // It just so happens that DOS and NT Buffer 2 (aka Next) are the same size
  55. //
  56. #define BUFFER_2_SIZE sizeof(((PLLC_DOS_BUFFER)0)->Next)
  57. //
  58. // macros
  59. //
  60. //
  61. // BUFFER_1_SIZE - return the size of the fixed part of a DOS receive Buffer 1.
  62. // The size is dependent on whether the receive options specified contiguous or
  63. // non-contiguous receive buffers. The size of a DOS Buffer 1 (of either type)
  64. // is 4 bytes smaller than the equivalent NT Buffer 1 because the NEXT_FRAME
  65. // field is absent
  66. //
  67. #define BUFFER_1_SIZE(contiguous) ((contiguous) \
  68. ? sizeof(((PLLC_DOS_BUFFER)0)->Contiguous) \
  69. : sizeof(((PLLC_DOS_BUFFER)0)->NotContiguous))
  70. //
  71. // private prototypes
  72. //
  73. //
  74. // public data
  75. //
  76. //
  77. // private data
  78. //
  79. //
  80. // DOS Buffer Pools - there can be one buffer pool per SAP. Protect access
  81. // using critical section.
  82. // There are 256 SAPs max, which breaks down into a maximum of 128 SAPs per
  83. // adapter. We can accomodate a maximum of 2 adapters - one Token Ring (adapter
  84. // 0) and one Ether Link (adapter 1)
  85. //
  86. DOS_DLC_BUFFER_POOL aBufferPools[DOS_DLC_MAX_SAPS * DOS_DLC_MAX_ADAPTERS];
  87. CRITICAL_SECTION BufferSemaphore;
  88. //
  89. // functions
  90. //
  91. VOID
  92. InitializeBufferPools(
  93. VOID
  94. )
  95. /*++
  96. Routine Description:
  97. Clears all buffer pools - sets structures to 0 - and initializes the buffer
  98. synchronization semaphore
  99. Arguments:
  100. None.
  101. Return Value:
  102. None.
  103. --*/
  104. {
  105. IF_DEBUG(DLC_BUFFERS) {
  106. DPUT("InitializeBufferPools\n");
  107. }
  108. InitializeCriticalSection(&BufferSemaphore);
  109. RtlZeroMemory(aBufferPools, sizeof(aBufferPools));
  110. }
  111. LLC_STATUS
  112. CreateBufferPool(
  113. IN DWORD PoolIndex,
  114. IN DOS_ADDRESS dpBuffer,
  115. IN WORD PoolBlocks,
  116. IN WORD BufferSize
  117. )
  118. /*++
  119. Routine Description:
  120. The function initializes buffer pool for a DLC application.
  121. DOS DLC applications do not necessarily need to create the
  122. buffer pool immediately in DlcOpenSap (or DirOpenAdapter).
  123. We initialize the buffer pool for DOS memory mode using
  124. parallel pointers in flat and DOS side.
  125. Arguments:
  126. PoolIndex - SAP and adapter number (bit 0 defines 0 or 1 adapter)
  127. dpBuffer - DOS pointer, space for the buffer segments. May be 0 in which
  128. case the app maintains its own buffers, we just get to know
  129. how many there are
  130. PoolBlocks - number of 16 byte blocks which comprise the buffer pool.
  131. If this is 0 then the default of 256 (*16 = 4096) is used
  132. BufferSize - size of an individual buffer in bytes and an integral multiple
  133. of 16. The minimum size is 80. If it is zero then the default
  134. of 160 is used
  135. Return Value:
  136. LLC_STATUS
  137. Success - LLC_STATUS_SUCCESS
  138. Buffer pool for this SAP has been created
  139. Failure - LLC_STATUS_DUPLICATE_COMMAND
  140. The buffer pool for this SAP already exists
  141. LLC_STATUS_INVALID_BUFFER_LENGTH
  142. The given buffer size is not a multiple of 16 or is less
  143. than 80 bytes (default minimum buffer size)
  144. LLC_STATUS_BUFFER_SIZE_EXCEEDED
  145. The buffer pool isn't big enough to hold 1 buffer of the
  146. requested size
  147. --*/
  148. {
  149. WORD BufferSizeInBlocks;
  150. WORD BufferCount;
  151. DWORD i;
  152. IF_DEBUG(DLC_BUFFERS) {
  153. DPUT4("CreateBufferPool(PoolIndex=%#x, dpBuffer=%#x, PoolBlocks=%d, BufferSize=%d)\n",
  154. PoolIndex, dpBuffer, PoolBlocks, BufferSize);
  155. }
  156. //
  157. // An app may reinitialize the buffer with DIR.MODIFY.OPEN.PARMS but the
  158. // command must fail, if there are already buffers in the pool. We should
  159. // also check if there is a pending receive command, but we cannot do it
  160. // without major changes in the receive handling architecture
  161. //
  162. if (aBufferPools[PoolIndex].BufferSize) {
  163. IF_DEBUG(DLC_BUFFERS) {
  164. DPUT1("CreateBufferPool: already have buffer pool for %#x\n", PoolIndex);
  165. }
  166. return LLC_STATUS_DUPLICATE_COMMAND;
  167. }
  168. //
  169. // Use the default, if the orginal value is 0
  170. //
  171. if (BufferSize == 0) {
  172. BufferSize = 160;
  173. }
  174. if (PoolBlocks == 0) {
  175. PoolBlocks = 256;
  176. }
  177. //
  178. // The buffer size must be at least 80 and an even 16 bytes
  179. //
  180. if ((BufferSize < 80) || (BufferSize % 16)) {
  181. return LLC_STATUS_INVALID_BUFFER_LENGTH;
  182. }
  183. BufferSizeInBlocks = BufferSize / 16;
  184. if (BufferSizeInBlocks > PoolBlocks) {
  185. IF_DEBUG(DLC_BUFFERS) {
  186. DPUT("CreateBufferPool: Error: BufferSizeInBlocks > PoolBlocks\n");
  187. }
  188. return LLC_STATUS_BUFFER_SIZE_EXCEEDED;
  189. }
  190. EnterCriticalSection(&BufferSemaphore);
  191. //
  192. // A DLC application may want to manage the buffer pool itself and to
  193. // provide the receive buffers with FreeBuffers, but the buffer size must
  194. // always be defined here.
  195. //
  196. aBufferPools[PoolIndex].BufferSize = BufferSize;
  197. aBufferPools[PoolIndex].dpBuffer = dpBuffer; // may be 0!
  198. aBufferPools[PoolIndex].BufferCount = 0;
  199. //
  200. // if the app has actually given us a buffer to use then we initialize it
  201. // else the app must manage its own buffers; we just maintain the metrics.
  202. // Note that the app's buffer might be aligned any old way, but we have to
  203. // put up with it at the expense of speed
  204. //
  205. if (dpBuffer) {
  206. LPBYTE ptr32;
  207. BufferCount = PoolBlocks/BufferSizeInBlocks;
  208. //
  209. // if the number of buffers we can fit in our pool is zero then inform
  210. // the app that we can't proceed with this request and clear out the
  211. // information for this buffer pool
  212. //
  213. if (BufferCount == 0) {
  214. aBufferPools[PoolIndex].BufferSize = 0;
  215. aBufferPools[PoolIndex].dpBuffer = 0;
  216. aBufferPools[PoolIndex].BufferCount = 0;
  217. LeaveCriticalSection(&BufferSemaphore);
  218. return LLC_STATUS_BUFFER_SIZE_EXCEEDED;
  219. }
  220. aBufferPools[PoolIndex].BufferCount = BufferCount;
  221. aBufferPools[PoolIndex].MaximumBufferCount = BufferCount;
  222. //
  223. // convert the DOS address to a flat 32-bit pointer
  224. //
  225. ptr32 = (LPBYTE)DOS_PTR_TO_FLAT(dpBuffer);
  226. //
  227. // link the buffers together and initialize the headers. The headers
  228. // are only intialized with 2 pieces of info: the size of the buffer
  229. // and the pointer to the next buffer
  230. //
  231. aBufferPools[PoolIndex].dpBuffer = dpBuffer;
  232. //
  233. // update the segment only - leave the offset
  234. //
  235. dpBuffer += BufferSize / 16 * 65536;
  236. for (i = BufferCount; i; --i) {
  237. //
  238. // do we really need this? I don't think so: looking at the manual,
  239. // this field is to report size of data received, not size of
  240. // buffer, which we know anyway
  241. //
  242. //WRITE_WORD(((PLLC_DOS_BUFFER)ptr32)->Next.cbBuffer, BufferSize);
  243. //
  244. // if this is the last buffer then set its NextBuffer field to
  245. // NULL. All buffers get the size info
  246. //
  247. if (i - 1) {
  248. WRITE_DWORD(&((PLLC_DOS_BUFFER)ptr32)->Next.pNextBuffer, dpBuffer);
  249. //
  250. // update the segment only - leave the offset
  251. //
  252. dpBuffer += BufferSize / 16 * 65536;
  253. ptr32 += BufferSize;
  254. } else {
  255. WRITE_DWORD(&((PLLC_DOS_BUFFER)ptr32)->Next.pNextBuffer, NULL);
  256. }
  257. }
  258. #if DBG
  259. IF_DEBUG(DLC_BUFFERS) {
  260. DumpDosDlcBufferPool(&aBufferPools[PoolIndex]);
  261. }
  262. #endif
  263. }
  264. LeaveCriticalSection(&BufferSemaphore);
  265. return LLC_STATUS_SUCCESS;
  266. }
  267. VOID
  268. DeleteBufferPool(
  269. IN DWORD PoolIndex
  270. )
  271. /*++
  272. Routine Description:
  273. The function deletes a buffer pool
  274. Arguments:
  275. PoolIndex - pool index based on SAP and adapter number
  276. Return Value:
  277. None.
  278. --*/
  279. {
  280. IF_DEBUG(DLC_BUFFERS) {
  281. DPUT("DeleteBufferPool\n");
  282. }
  283. //
  284. // DLC.RESET for all adapters calls this 127 times
  285. //
  286. EnterCriticalSection(&BufferSemaphore);
  287. if (aBufferPools[PoolIndex].BufferSize != 0) {
  288. RtlZeroMemory(&aBufferPools[PoolIndex], sizeof(aBufferPools[PoolIndex]));
  289. }
  290. LeaveCriticalSection(&BufferSemaphore);
  291. }
  292. LLC_STATUS
  293. GetBuffers(
  294. IN DWORD PoolIndex,
  295. IN WORD BuffersToGet,
  296. OUT DPLLC_DOS_BUFFER *pdpBuffer,
  297. OUT LPWORD pusBuffersLeft,
  298. IN BOOLEAN PartialList,
  299. OUT PWORD BuffersGot OPTIONAL
  300. )
  301. /*++
  302. Routine Description:
  303. The function allocates DLC buffers. It will allocate buffers as a chain
  304. if >1 is requested. If PartialList is TRUE then will allocate as many
  305. buffers as are available up to BuffersToGet and return the number in
  306. BuffersGot
  307. Arguments:
  308. PoolIndex - SAP and adapter number
  309. BuffersToGet - numbers of buffers to get. If this is 0, defaults to 1
  310. pdpBuffer - the returned link list of LLC buffers
  311. pusBuffersLeft - returned count of buffers left after this call
  312. PartialList - TRUE if the caller wants a partial list
  313. BuffersGot - pointer to returned number of buffers allocated
  314. Return Value:
  315. LLC_STATUS
  316. Success - LLC_STATUS_SUCCESS
  317. The requested number of buffers have been returned, or a
  318. number of buffers less than the original request if
  319. PartialList is TRUE
  320. Failure - LLC_STATUS_LOST_DATA_NO_BUFFERS
  321. The request could not be satistfied - not enough buffers
  322. in pool
  323. LLC_STATUS_INVALID_STATION_ID
  324. The request was mad to an invalid SAP
  325. --*/
  326. {
  327. PLLC_DOS_BUFFER pBuffer;
  328. PDOS_DLC_BUFFER_POOL pBufferPool = &aBufferPools[PoolIndex];
  329. LLC_STATUS status;
  330. WORD n;
  331. WORD bufferSize;
  332. #if DBG
  333. DWORD numBufs = BuffersToGet;
  334. IF_DEBUG(DLC_BUFFERS) {
  335. DPUT2("GetBuffers(PoolIndex=%#02x, BuffersToGet=%d)\n", PoolIndex, BuffersToGet);
  336. }
  337. #endif
  338. EnterCriticalSection(&BufferSemaphore);
  339. //
  340. // if the caller specified PartialList then return whatever we've got. If
  341. // whatever we've got is 0 then we'll default it to 1 and fail the allocation
  342. // since 0 is less than 1
  343. //
  344. if (PartialList) {
  345. if (pBufferPool->BufferCount < BuffersToGet) {
  346. BuffersToGet = pBufferPool->BufferCount;
  347. }
  348. }
  349. //
  350. // IBM DLC allows a default value of 1 to be used if the caller specified 0
  351. //
  352. if (!BuffersToGet) {
  353. ++BuffersToGet;
  354. }
  355. //
  356. // default the returned DOS buffer chain pointer to NULL
  357. //
  358. *pdpBuffer = 0;
  359. //
  360. // if there are no buffers defined then this is an erroneous request
  361. //
  362. if (pBufferPool->BufferSize) {
  363. //
  364. // calculate the size of the data part of the buffer. We put this value
  365. // in the LENGTH_IN_BUFFER field
  366. //
  367. bufferSize = pBufferPool->BufferSize
  368. - (WORD)sizeof(pBuffer->Next);
  369. //
  370. // there may be no buffers left, in which case the next buffer pointer
  371. // (in DOS 16:16 format) will be 0 (0:0). If, on the other hand, it's
  372. // not 0 then we're in business: see if we can't allocate the buffers
  373. // requested
  374. //
  375. if (pBufferPool->dpBuffer && pBufferPool->BufferCount >= BuffersToGet) {
  376. pBuffer = (PLLC_DOS_BUFFER)DOS_PTR_TO_FLAT(pBufferPool->dpBuffer);
  377. *pdpBuffer = pBufferPool->dpBuffer;
  378. pBufferPool->BufferCount -= BuffersToGet;
  379. n = BuffersToGet;
  380. //
  381. // Eicon Access wants the size of the buffer in the buffer
  382. // when it is returned by BUFFER.GET. Oblige
  383. //
  384. WRITE_WORD(&pBuffer->Next.cbBuffer, bufferSize);
  385. //
  386. // we will return a chain of buffers, so we (nicely) terminate it
  387. // with a NULL for the last NextBuffer field. It doesn't say in
  388. // the lovely IBM Tech Ref whether this should be done, but its
  389. // probably the best thing to do. Because this buffer pool lives
  390. // in DOS memory, we have to use READ_POINTER and WRITE_FAR_POINTER
  391. // macros, lest we get an alignment fault on RISC
  392. //
  393. status = LLC_STATUS_SUCCESS;
  394. for (--BuffersToGet; BuffersToGet; --BuffersToGet) {
  395. pBuffer = (PLLC_DOS_BUFFER)READ_FAR_POINTER(&(pBuffer->pNext));
  396. if (pBuffer) {
  397. //
  398. // Eicon Access wants the size of the buffer in the buffer
  399. // when it is returned by BUFFER.GET. Oblige
  400. //
  401. WRITE_WORD(&pBuffer->Next.cbBuffer, bufferSize);
  402. } else {
  403. //
  404. // As Prefix found out, the next lines below would
  405. // dereference a NULL. This is a bad case since the
  406. // buffer creation process somehow failed, or, more
  407. // likely, someone stumped the memory. We thus assume
  408. // success above and set the error in this case.
  409. //
  410. status = LLC_STATUS_LOST_DATA_NO_BUFFERS;
  411. n = 0;
  412. break;
  413. }
  414. }
  415. //
  416. // set the new buffer pool head
  417. //
  418. if (status == LLC_STATUS_SUCCESS) {
  419. pBufferPool->dpBuffer = READ_DWORD(&pBuffer->pNext);
  420. //
  421. // terminate the chain
  422. //
  423. WRITE_FAR_POINTER(&pBuffer->pNext, NULL);
  424. #if DBG
  425. IF_DEBUG(DLC_BUFFERS) {
  426. DumpDosDlcBufferChain(*pdpBuffer, numBufs ? numBufs : 1);
  427. }
  428. #endif
  429. }
  430. } else {
  431. //
  432. // if no buffers are obtained, the returned list is set to 0
  433. //
  434. status = LLC_STATUS_LOST_DATA_NO_BUFFERS;
  435. n = 0;
  436. }
  437. //
  438. // return the number of buffers left after this call. Works if we
  439. // allocated some or not
  440. //
  441. *pusBuffersLeft = pBufferPool->BufferCount;
  442. } else {
  443. //
  444. // bad SAP - no buffer pool for this one
  445. //
  446. status = LLC_STATUS_INVALID_STATION_ID;
  447. n = 0;
  448. }
  449. LeaveCriticalSection(&BufferSemaphore);
  450. //
  451. // if BuffersGot was specified then return the number of buffers allocated
  452. // and chained
  453. //
  454. if (ARGUMENT_PRESENT(BuffersGot)) {
  455. *BuffersGot = n;
  456. }
  457. IF_DEBUG(DLC_BUFFERS) {
  458. DPUT2("GetBuffers returning status=%x, BuffersLeft=%d\n", status, *pusBuffersLeft);
  459. }
  460. return status;
  461. }
  462. LLC_STATUS
  463. FreeBuffers(
  464. IN DWORD PoolIndex,
  465. IN DPLLC_DOS_BUFFER dpBuffer,
  466. OUT LPWORD pusBuffersLeft
  467. )
  468. /*++
  469. Routine Description:
  470. Free a DOS buffer to a DLC buffer pool
  471. Arguments:
  472. PoolIndex - SAP and adapter number (bit0 defines 0 or 1 adapter)
  473. dpBuffer - the released buffers (DOS pointer)
  474. pusBuffersLeft - the number of buffers left after the free
  475. Return Value:
  476. --*/
  477. {
  478. DPLLC_DOS_BUFFER dpBase; // DOS pointer
  479. PLLC_DOS_BUFFER pNextBuffer; // flat NT pointer
  480. PLLC_DOS_BUFFER pBuffer; // flat NT pointer
  481. PDOS_DLC_BUFFER_POOL pBufferPool = &aBufferPools[PoolIndex];
  482. #if DBG
  483. int n = 0;
  484. #endif
  485. IF_DEBUG(DLC_BUFFERS) {
  486. DPUT2("FreeBuffers: PoolIndex=%x dpBuffer=%x\n", PoolIndex, dpBuffer);
  487. }
  488. if (pBufferPool->BufferSize == 0) {
  489. return LLC_STATUS_INVALID_STATION_ID;
  490. }
  491. EnterCriticalSection(&BufferSemaphore);
  492. dpBase = dpBuffer;
  493. pNextBuffer = pBuffer = DOS_PTR_TO_FLAT(dpBuffer);
  494. //
  495. // the manual says for BUFFER.FREE (p3-4):
  496. //
  497. // "When the buffer is placed back in the buffer pool, bytes 4 and 5
  498. // (buffer length) of the buffer are set to zero."
  499. //
  500. // So, we oblige
  501. //
  502. WRITE_WORD(&pBuffer->Next.cbFrame, 0);
  503. if (pNextBuffer) {
  504. //
  505. // count the number of buffers being freed. Hopefully, the application
  506. // hasn't chenged over our terminating NULL pointer
  507. //
  508. while (pNextBuffer) {
  509. ++pBufferPool->BufferCount;
  510. #if DBG
  511. ++n;
  512. #endif
  513. pBuffer = pNextBuffer;
  514. pNextBuffer = (PLLC_DOS_BUFFER)READ_FAR_POINTER(&pBuffer->pNext);
  515. //
  516. // see above, about bytes 4 and 5
  517. //
  518. WRITE_WORD(&pBuffer->Next.cbFrame, 0);
  519. }
  520. //
  521. // put the freed chain at the head of the list, after linking the
  522. // buffer currently at the head of the list to the end of the freed
  523. // chain
  524. //
  525. WRITE_DWORD(&pBuffer->pNext, pBufferPool->dpBuffer);
  526. pBufferPool->dpBuffer = dpBase;
  527. #if DBG
  528. IF_DEBUG(DLC_BUFFERS) {
  529. DumpDosDlcBufferChain(dpBuffer, n);
  530. }
  531. } else {
  532. DPUT("ERROR: App tried to free NULL buffer chain\n");
  533. #endif
  534. }
  535. *pusBuffersLeft = pBufferPool->BufferCount;
  536. if (pBufferPool->BufferCount > pBufferPool->MaximumBufferCount) {
  537. pBufferPool->MaximumBufferCount = pBufferPool->BufferCount;
  538. }
  539. LeaveCriticalSection(&BufferSemaphore);
  540. return STATUS_SUCCESS;
  541. }
  542. WORD
  543. CalculateBufferRequirement(
  544. IN UCHAR Adapter,
  545. IN WORD StationId,
  546. IN PLLC_BUFFER pFrame,
  547. IN LLC_DOS_PARMS UNALIGNED * pDosParms,
  548. OUT PWORD BufferSize
  549. )
  550. /*++
  551. Routine Description:
  552. Calculate the number of DOS buffers required to hold the data received into
  553. an NT buffer. We have to go through this laborious phase because we need to
  554. know ahead of time if we have enough DOS buffers into which we will receive
  555. an I-Frame.
  556. Also, the size of DOS buffers is fixed, but the size of NT receive frame
  557. buffers can vary depending on the size of the received frame, the options
  558. requested and the 'binary buddy' allocator algorithm in the DLC driver. You
  559. may think that since we specify to the driver the buffer pool size in the
  560. DLC.OPEN.SAP call that it would allocate buffers of the specified size.
  561. Well, you'd be wrong: the DLC driver ignores this information and creates
  562. a buffer pool which can dole out variable size buffers, making my life more
  563. difficult than it ought to be
  564. Arguments:
  565. Adapter - which adapter we're receiving from
  566. StationId - the Station Id we're receiving on
  567. pFrame - pointer to the received frame in NT buffer
  568. pDosParms - pointer to the original DOS RECEIVE parameters
  569. BufferSize - pointer to the returned DOS buffer size
  570. Return Value:
  571. WORD
  572. --*/
  573. {
  574. //
  575. // pBufferPool points to the DOS buffer pool for this adapter/station ID
  576. //
  577. PDOS_DLC_BUFFER_POOL pBufferPool = &aBufferPools[GET_POOL_INDEX(Adapter, StationId)];
  578. //
  579. // dosUserLength is the USER_LENGTH value the DOS client requested when
  580. // the RECEIVE was submitted. This value may well be different than the
  581. // USER_LENGTH in the NT receive frame buffer
  582. //
  583. WORD dosUserLength = READ_WORD(&pDosParms->DosReceive.usUserLength);
  584. //
  585. // buffersRequired is the number of DOS buffers we need to allocate in order
  586. // to return the received frame. It will be at least 1
  587. //
  588. WORD buffersRequired = 1;
  589. //
  590. // dataSpace is the area in a DOS buffer available for data (after the
  591. // Buffer 1 or Buffer 2 header and the USER_LENGTH consideration)
  592. //
  593. WORD dataSpace;
  594. //
  595. // dataLeft is the amount of data in an NT buffer needing to be copied to
  596. // a DOS buffer
  597. //
  598. WORD dataLeft = 0;
  599. //
  600. // calculate the number of DOS buffers required to hold the data frame
  601. // received into the NT buffer. Note that we can't simply use the size
  602. // of the received frame because we need the size of the buffer headers
  603. // and if the NT frame is larger than the DOS buffers then we may end
  604. // up with more DOS buffers required than NT buffers which in turn
  605. // results in more overhead which we have to factor in
  606. //
  607. WORD bufferSize = pBufferPool->BufferSize;
  608. IF_DEBUG(DLC_BUFFERS) {
  609. DPUT("CalculateBufferRequirement\n");
  610. }
  611. //
  612. // calculate the amount of space in a DOS buffer after the Buffer 1 structure
  613. // (contiguous or non-contiguous, smoking or non-smoking, ah, ah, ah ahh-haa)
  614. // The buffer size MUST be large enough to hold the Buffer 1 overhead. This
  615. // is a FACT
  616. //
  617. dataSpace = bufferSize
  618. - (BUFFER_1_SIZE(
  619. pFrame->Contiguous.uchOptions
  620. & (LLC_CONTIGUOUS_MAC | LLC_CONTIGUOUS_DATA)
  621. )
  622. + dosUserLength
  623. );
  624. //
  625. // if there is less data space available in the first DOS receive buffer
  626. // than received data in the NT buffer then our first NT buffer will be
  627. // mapped to >1 DOS buffers: a Buffer 1 and 1 or more Buffer 2s. This is
  628. // before we even get to any associated Buffer 2s in the NT receive frame.
  629. //
  630. // Also: if the LLC_BREAK option is specified in the receive parameters
  631. // then the first data buffer will contain the header information. Note:
  632. // we assume this can only be for NotContiguous data, else how would we
  633. // know the size of the header information?
  634. //
  635. if (pFrame->Contiguous.uchOptions & LLC_BREAK) {
  636. if (!(pFrame->Contiguous.uchOptions & (LLC_CONTIGUOUS_MAC | LLC_CONTIGUOUS_DATA))) {
  637. dataLeft = pFrame->NotContiguous.cbBuffer;
  638. }
  639. #if DBG
  640. else {
  641. DPUT("CalculateBufferRequirement: Error: invalid options: BREAK && CONTIGUOUS???\n");
  642. }
  643. #endif
  644. } else if (dataSpace < pFrame->Contiguous.cbBuffer) {
  645. dataLeft = pFrame->Contiguous.cbBuffer - dataSpace;
  646. } else {
  647. //
  648. // we have enough space in the DOS buffer to copy all the received data
  649. // and some more
  650. //
  651. dataSpace -= pFrame->Next.cbBuffer;
  652. dataLeft = 0;
  653. }
  654. //
  655. // if there is more data in the NT buffer than we can fit in a DOS buffer,
  656. // either because the buffer sizes are different or due to the DOS client
  657. // requesting the BREAK option, then generate Buffer 2 requirements
  658. //
  659. while (dataLeft) {
  660. ++buffersRequired;
  661. dataSpace = bufferSize - (BUFFER_2_SIZE + dosUserLength);
  662. if (dataLeft > dataSpace) {
  663. dataLeft -= dataSpace;
  664. dataSpace = 0;
  665. } else {
  666. dataSpace -= dataLeft;
  667. dataLeft = 0;
  668. }
  669. }
  670. //
  671. // if the NT received frame has any associated Buffer 2 structures then
  672. // calculate the additional buffer requirement. Again, the NT buffers may
  673. // be a different size(s) than the DOS buffers.
  674. //
  675. // At this point, dataSpace is the amount of remaining data area in the
  676. // previous DOS buffer - Buffer 1 or Buffer 2. Use this before we allocate
  677. // a new DOS buffer
  678. //
  679. for (pFrame = pFrame->pNext; pFrame; pFrame = pFrame->pNext) {
  680. if (pFrame->Next.cbBuffer > dataSpace) {
  681. dataLeft = pFrame->Next.cbBuffer - dataSpace;
  682. dataSpace = 0;
  683. while (dataLeft) {
  684. ++buffersRequired;
  685. dataSpace = bufferSize - (BUFFER_2_SIZE + dosUserLength);
  686. if (dataLeft > dataSpace) {
  687. dataLeft -= dataSpace;
  688. dataSpace = 0;
  689. } else {
  690. dataSpace -= dataLeft;
  691. dataLeft = 0;
  692. }
  693. }
  694. } else {
  695. //
  696. // we have enough space in the DOS buffer to copy all the received data
  697. // and some more
  698. //
  699. dataSpace -= pFrame->Next.cbBuffer;
  700. dataLeft = 0;
  701. }
  702. }
  703. IF_DEBUG(DLC_BUFFERS) {
  704. DPUT1("CalculateBufferRequirement: %d buffers required\n", buffersRequired);
  705. }
  706. //
  707. // set the output DOS buffer size and return the number of DOS buffers
  708. // required
  709. //
  710. *BufferSize = bufferSize;
  711. return buffersRequired;
  712. }
  713. LLC_STATUS
  714. CopyFrame(
  715. IN PLLC_BUFFER pFrame,
  716. IN DPLLC_DOS_BUFFER DosBuffers,
  717. IN WORD UserLength,
  718. IN WORD BufferSize,
  719. IN DWORD Flags
  720. )
  721. /*++
  722. Routine Description:
  723. Copies a received NT frame into DOS buffers. We have previously calculated
  724. the DOS buffer requirement and allocated that requirement
  725. We may copy the entire received frame or only part of it. We can only copy
  726. partial frames if the frame is NOT an I-Frame
  727. Notes: 1. the DOS buffer manager returns the orginal DOS 16:16 buffer
  728. pointers, we must use those original pointers, when the buffers
  729. are linked to each other.
  730. 2. We do NOT chain frames - DOS DLC cannot handle frames being
  731. chained, and there is nothing to be gained by us chaining them
  732. except that we reduce the number of completed READs. However,
  733. we still have to generate the same number of simulated hardware
  734. interrupts to the VDM
  735. 3. Unlike DOS buffer pools, NT does not deal in buffers of a
  736. specific size. Rather, it allocates buffers from a pool based
  737. on a 'binary buddy' algorithm and the size of the data to be
  738. returned. Therefore, there is no correspondence between the
  739. size of DOS buffers (for a station) and the NT buffers which
  740. were used to receive the data
  741. 4. We only copy the data in this routine - whether it is deferred
  742. data from some prior local busy state or current data is immaterial
  743. to this routine. Responsibility for managing current/deferred frames
  744. is left to the caller
  745. Arguments:
  746. pFrame - pointer to received frame in NT buffer(s)
  747. DosBuffers - DOS pointer to chain of DOS receive buffers
  748. UserLength - the USER_LENGTH value specified in the DOS RECEIVE
  749. BufferSize - size of a DOS buffer
  750. Flags - various flags:
  751. CF_CONTIGUOUS
  752. Set if this frame is contiguous
  753. CF_BREAK
  754. Set if the DOS client requested that the buffers be
  755. broken into header, data
  756. CF_PARTIAL
  757. Set if we are copying a partial frame - ok for non
  758. I-Frames
  759. Return Value:
  760. LLC_STATUS
  761. LLC_STATUS_SUCCESS
  762. All data copied from NT buffer to DOS buffer(s)
  763. LLC_STATUS_LOST_DATA_INADEQUATE_SPACE
  764. A partial copy was performed. Some data ended up in DOS buffer(s),
  765. the rest is lost. Cannot be I-Frame!
  766. --*/
  767. {
  768. //
  769. // pDosBuffer - pointer to the DOS buffer which is usable in 32-bit mode
  770. //
  771. PLLC_DOS_BUFFER pDosBuffer = (PLLC_DOS_BUFFER)DOS_PTR_TO_FLAT(DosBuffers);
  772. //
  773. // dataSpace - amount of data space available in the current DOS buffer.
  774. // Initialize it for common Buffer 1 case
  775. //
  776. WORD dataSpace = BufferSize - (WORD)&(((PLLC_BUFFER)0)->Contiguous.pNextFrame);
  777. PBYTE pDosData; // pointer to place in DOS buffer where we copy data TO
  778. PBYTE pNtData; // corresponding place in NT buffer where we copy data FROM
  779. WORD headerLength; // amount of data in headers
  780. WORD dataLeft; // amount of data to be copied FROM the NT buffer
  781. WORD userOffset; // offset of USER_SPACE
  782. WORD bufferLeft; // amount of data left in current NT buffer
  783. WORD dataToCopy; // amount of data to copy to DOS buffer
  784. WORD dataCopied; // amount of data copied to Buffer 1/Buffer 2
  785. WORD frameLength; // length of entire frame
  786. //
  787. // bufferOffset - used in generating the correct userOffset
  788. //
  789. WORD bufferOffset = LOWORD(DosBuffers);
  790. IF_DEBUG(DLC_BUFFERS) {
  791. DPUT5("CopyFrame: pFrame=%x DosBuffers=%x UserLength=%x Flags=%x pDosBuffer=%x\n",
  792. pFrame, DosBuffers, UserLength, Flags, pDosBuffer);
  793. }
  794. //
  795. // copy the first buffer. If the BREAK option is set then we only copy the
  796. // header part (ASSUMES NotContiguous)! NB: we KNOW that we can fit at least
  797. // this amount of data in the DOS buffer. Also: it is safe to use RtlCopyMemory ?
  798. //
  799. RtlCopyMemory(&pDosBuffer->Contiguous.cbFrame,
  800. &pFrame->Contiguous.cbFrame,
  801. (DWORD)&(((PLLC_BUFFER)0)->Contiguous.pNextFrame)
  802. - (DWORD)&(((PLLC_BUFFER)0)->Contiguous.cbFrame)
  803. );
  804. //
  805. // pDosData points to the area in the DOS buffer where the LAN header info
  806. // or data will go, depending on format
  807. //
  808. pDosData = &pDosBuffer->NotContiguous.cbLanHeader;
  809. //
  810. // if the CF_CONTIGUOUS flag is not set in the Flags parameter then this is
  811. // a NotContiguous frame. We must copy the header in 2 parts, because the
  812. // NT buffer contains the NEXT_FRAME field which the DOS buffer does not
  813. //
  814. if (!(Flags & CF_CONTIGUOUS)) {
  815. IF_DEBUG(DLC_BUFFERS) {
  816. DPUT("Buffer is NOT CONTIGUOUS\n");
  817. }
  818. RtlCopyMemory(pDosData,
  819. &pFrame->NotContiguous.cbLanHeader,
  820. (DWORD)&(((PLLC_BUFFER)0)->NotContiguous.usPadding)
  821. - (DWORD)&(((PLLC_BUFFER)0)->NotContiguous.cbLanHeader)
  822. );
  823. pDosData += (DWORD)&(((PLLC_BUFFER)0)->NotContiguous.usPadding)
  824. - (DWORD)&(((PLLC_BUFFER)0)->NotContiguous.cbLanHeader);
  825. dataSpace -= (WORD)&(((PLLC_BUFFER)0)->NotContiguous.usPadding)
  826. - (WORD)&(((PLLC_BUFFER)0)->NotContiguous.cbLanHeader);
  827. userOffset = (WORD)&(((PLLC_DOS_BUFFER)0)->NotContiguous.auchDlcHeader)
  828. + sizeof(((PLLC_DOS_BUFFER)0)->NotContiguous.auchDlcHeader);
  829. //
  830. // sanity check
  831. //
  832. ASSERT(userOffset == 58);
  833. //
  834. // amount of data in headers
  835. //
  836. headerLength = pFrame->NotContiguous.cbLanHeader
  837. + pFrame->NotContiguous.cbDlcHeader;
  838. } else {
  839. IF_DEBUG(DLC_BUFFERS) {
  840. DPUT("Buffer is CONTIGUOUS\n");
  841. }
  842. userOffset = (WORD)&(((PLLC_DOS_BUFFER)0)->Contiguous.uchAdapterNumber)
  843. + sizeof(((PLLC_DOS_BUFFER)0)->Contiguous.uchAdapterNumber);
  844. //
  845. // sanity check
  846. //
  847. ASSERT(userOffset == 20);
  848. //
  849. // no header info in contiguous buffer
  850. //
  851. headerLength = 0;
  852. }
  853. //
  854. // if the CF_BREAK flag is set in the Flags parameter then the DOS app
  855. // requested that the first buffer (presumed NotContiguous) be broken into
  856. // one buffer containing just the header information and another containing
  857. // the data. In this case copy no more data to the first buffer
  858. //
  859. if (!(Flags & CF_BREAK)) {
  860. //
  861. // pDosData points at USER_SPACE - offset 58 for NotContiguous buffer,
  862. // offset 20 for Contiguous buffer. Bump it by USER_LENGTH (still don't
  863. // know if we ever expect anything meaningful to be placed at USER_SPACE
  864. // before we give the buffer to DOS
  865. //
  866. pDosData += UserLength;
  867. //
  868. // get in dataSpace the amount of space left in the DOS buffer where
  869. // we are able to copy data. Assume that UserLength doesn't make this
  870. // go negative (ie LARGE)
  871. //
  872. ASSERT(dataSpace >= UserLength);
  873. dataSpace -= UserLength;
  874. } else {
  875. IF_DEBUG(DLC_BUFFERS) {
  876. DPUT("Buffer has BREAK\n");
  877. }
  878. //
  879. // the DOS app requested BREAK. Set the count of data in this buffer
  880. // to 0. Use WRITE_WORD since it may be unaligned. Update the other
  881. // header fields we can't just copy from the NT buffer
  882. //
  883. WRITE_WORD(&pDosBuffer->NotContiguous.cbBuffer, 0);
  884. WRITE_WORD(&pDosBuffer->NotContiguous.offUserData, userOffset + bufferOffset);
  885. WRITE_WORD(&pDosBuffer->NotContiguous.cbUserData, UserLength);
  886. //
  887. // get the next DOS buffer in the list. There may not be one? (Don't
  888. // expect such a situation)
  889. //
  890. bufferOffset = READ_WORD(&pDosBuffer->pNext);
  891. pDosBuffer = DOS_PTR_TO_FLAT(pDosBuffer->pNext);
  892. if (pDosBuffer) {
  893. userOffset = (WORD)&(((PLLC_DOS_BUFFER)0)->Next.cbUserData)
  894. + sizeof(((PLLC_DOS_BUFFER)0)->Next.cbUserData);
  895. //
  896. // sanity check
  897. //
  898. ASSERT(userOffset == 12);
  899. dataSpace = BufferSize - (BUFFER_2_SIZE + UserLength);
  900. pDosData = (PBYTE)pDosBuffer + BUFFER_2_SIZE + UserLength;
  901. } else {
  902. //
  903. // that was the last buffer. Either there was just header data or
  904. // this is a partial copy and therefore not an I-Frame
  905. //
  906. IF_DEBUG(DLC_BUFFERS) {
  907. DPUT("CopyFrame: returning early\n");
  908. }
  909. return (Flags & CF_PARTIAL)
  910. ? LLC_STATUS_LOST_DATA_INADEQUATE_SPACE
  911. : LLC_STATUS_SUCCESS;
  912. }
  913. }
  914. //
  915. // frameLength is length of entire frame - must appear in Buffer 1 and
  916. // Buffer 2s
  917. //
  918. frameLength = pFrame->Contiguous.cbFrame;
  919. //
  920. // dataLeft is the amount of data left to copy from the frame after the
  921. // headers have been taken care of. For a Contiguous buffer, this is the
  922. // same as the length of the frame; for a NotContiguous buffer, this is
  923. // the length of the frame - the combined length of the LAN and DLC
  924. // headers
  925. //
  926. dataLeft = frameLength - headerLength;
  927. //
  928. // get pointer to data in NT buffer and the amount of data to copy (from
  929. // rest of NT frame)
  930. //
  931. pNtData = (PBYTE)pFrame
  932. + pFrame->Contiguous.offUserData
  933. + pFrame->Contiguous.cbUserData;
  934. bufferLeft = pFrame->Contiguous.cbBuffer;
  935. //
  936. // dataCopied is amount of data copied to current buffer (1 or 2) and goes
  937. // in cbBuffer field (aka LENGTH_IN_BUFFER)
  938. //
  939. dataCopied = 0;
  940. //
  941. // we have copied all the data we could to the first buffer. While there
  942. // are more DOS buffers, copy data from NT buffer
  943. //
  944. do {
  945. //
  946. // calculate the amount of space available in the current buffer
  947. //
  948. if (dataSpace >= bufferLeft) {
  949. dataToCopy = bufferLeft;
  950. dataLeft -= dataToCopy;
  951. dataSpace -= dataToCopy;
  952. bufferLeft = 0;
  953. } else {
  954. dataToCopy = dataSpace;
  955. bufferLeft -= dataSpace;
  956. dataLeft -= dataSpace;
  957. dataSpace = 0;
  958. }
  959. //
  960. // copy the data. This will fill up the current DOS buffer, exhaust the
  961. // current NT buffer, or both
  962. //
  963. if (dataToCopy) {
  964. IF_DEBUG(DLC_RX_DATA) {
  965. DPUT3("CopyFrame: Copying %d bytes from %x to %x\n", dataToCopy, pNtData, pDosData);
  966. }
  967. RtlCopyMemory(pDosData, pNtData, dataToCopy);
  968. //
  969. // dataCopied accumulates until we fill a DOS buffer
  970. //
  971. dataCopied += dataToCopy;
  972. //
  973. // update to- and from- pointers for next go round loop
  974. //
  975. pDosData += dataToCopy;
  976. pNtData += dataToCopy;
  977. }
  978. //
  979. // we have run out of space in a DOS buffer, or out of data to copy from
  980. // the NT buffer
  981. //
  982. if (dataLeft) {
  983. //
  984. // we think there is data left to copy
  985. //
  986. if (!bufferLeft) {
  987. //
  988. // finished current NT buffer. Get next one
  989. //
  990. pFrame = pFrame->pNext;
  991. if (pFrame) {
  992. bufferLeft = pFrame->Next.cbBuffer;
  993. pNtData = (PBYTE)pFrame
  994. + pFrame->Contiguous.offUserData
  995. + pFrame->Contiguous.cbUserData;
  996. IF_DEBUG(DLC_RX_DATA) {
  997. DPUT3("CopyFrame: new NT buffer @%x pNtData @%x bufferLeft=%d\n",
  998. pFrame, pNtData, bufferLeft);
  999. }
  1000. } else {
  1001. //
  1002. // no more NT buffers. Is this a partial copy?
  1003. //
  1004. DPUT("*** ERROR: dataLeft && no more NT buffers ***\n");
  1005. ASSERT(Flags & CF_PARTIAL);
  1006. break;
  1007. }
  1008. }
  1009. if (!dataSpace) {
  1010. //
  1011. // update the current DOS buffer header (it doesn't matter that
  1012. // we use Contiguous, NotContiguous, or Next: these fields are
  1013. // present in all 3 buffer types)
  1014. //
  1015. WRITE_WORD(&pDosBuffer->Contiguous.cbFrame, frameLength);
  1016. WRITE_WORD(&pDosBuffer->Contiguous.cbBuffer, dataCopied);
  1017. WRITE_WORD(&pDosBuffer->Contiguous.offUserData, userOffset + bufferOffset);
  1018. WRITE_WORD(&pDosBuffer->Contiguous.cbUserData, UserLength);
  1019. //
  1020. // and get the next one
  1021. //
  1022. bufferOffset = READ_WORD(&pDosBuffer->pNext);
  1023. pDosBuffer = DOS_PTR_TO_FLAT(pDosBuffer->pNext);
  1024. //
  1025. // if we have another DOS buffer, then it is a Next buffer. Get the
  1026. // buffer variables
  1027. //
  1028. if (pDosBuffer) {
  1029. pDosData = (PBYTE)pDosBuffer + BUFFER_2_SIZE + UserLength;
  1030. userOffset = (WORD)&(((PLLC_DOS_BUFFER)0)->Next.cbUserData)
  1031. + sizeof(((PLLC_DOS_BUFFER)0)->Next.cbUserData);
  1032. //
  1033. // sanity check
  1034. //
  1035. ASSERT(userOffset == 12);
  1036. //
  1037. // get new available space (constant)
  1038. //
  1039. dataSpace = BufferSize - (BUFFER_2_SIZE + UserLength);
  1040. dataCopied = 0;
  1041. IF_DEBUG(DLC_RX_DATA) {
  1042. DPUT3("CopyFrame: new DOS buffer @%x pDosData @%x dataSpace=%d\n",
  1043. pDosBuffer, pDosData, dataSpace);
  1044. }
  1045. } else {
  1046. //
  1047. // no more DOS buffers. Is this a partial copy?
  1048. //
  1049. DPUT("*** ERROR: dataLeft && no more DOS buffers ***\n");
  1050. ASSERT(Flags & CF_PARTIAL);
  1051. break;
  1052. }
  1053. }
  1054. } else {
  1055. //
  1056. // update the current DOS buffer header
  1057. //
  1058. WRITE_WORD(&pDosBuffer->Contiguous.cbFrame, frameLength);
  1059. WRITE_WORD(&pDosBuffer->Contiguous.cbBuffer, dataCopied);
  1060. WRITE_WORD(&pDosBuffer->Contiguous.offUserData, userOffset + bufferOffset);
  1061. WRITE_WORD(&pDosBuffer->Contiguous.cbUserData, UserLength);
  1062. }
  1063. } while ( dataLeft );
  1064. //
  1065. // if CF_PARTIAL set then we knew we were copying a partial frame before
  1066. // we got here
  1067. //
  1068. return (Flags & CF_PARTIAL)
  1069. ? LLC_STATUS_LOST_DATA_INADEQUATE_SPACE
  1070. : LLC_STATUS_SUCCESS;
  1071. }
  1072. BOOLEAN
  1073. AllBuffersInPool(
  1074. IN DWORD PoolIndex
  1075. )
  1076. /*++
  1077. Routine Description:
  1078. Returns TRUE if all buffers that a pool has held are currently in the pool.
  1079. Once a buffer has been added to a pool, it cannot be removed, saved by not
  1080. returning it to the pool. Hence this function will always return TRUE if
  1081. the app is well-behaved and all buffers that have been placed in the pool
  1082. are currently in the pool
  1083. Arguments:
  1084. PoolIndex - pool id
  1085. Return Value:
  1086. BOOLEAN
  1087. TRUE - all buffers back
  1088. FALSE - buffer pool currently contains less than full number of buffers
  1089. --*/
  1090. {
  1091. BOOLEAN result;
  1092. PDOS_DLC_BUFFER_POOL pBufferPool = &aBufferPools[PoolIndex];
  1093. EnterCriticalSection(&BufferSemaphore);
  1094. result = (pBufferPool->BufferCount == pBufferPool->MaximumBufferCount);
  1095. LeaveCriticalSection(&BufferSemaphore);
  1096. return result;
  1097. }