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.

1449 lines
41 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. for (--BuffersToGet; BuffersToGet; --BuffersToGet) {
  394. pBuffer = (PLLC_DOS_BUFFER)READ_FAR_POINTER(&(pBuffer->pNext));
  395. if (pBuffer) {
  396. //
  397. // Eicon Access wants the size of the buffer in the buffer
  398. // when it is returned by BUFFER.GET. Oblige
  399. //
  400. WRITE_WORD(&pBuffer->Next.cbBuffer, bufferSize);
  401. }
  402. }
  403. //
  404. // set the new buffer pool head
  405. //
  406. pBufferPool->dpBuffer = READ_DWORD(&pBuffer->pNext);
  407. //
  408. // terminate the chain
  409. //
  410. WRITE_FAR_POINTER(&pBuffer->pNext, NULL);
  411. status = LLC_STATUS_SUCCESS;
  412. #if DBG
  413. IF_DEBUG(DLC_BUFFERS) {
  414. DumpDosDlcBufferChain(*pdpBuffer, numBufs ? numBufs : 1);
  415. }
  416. #endif
  417. } else {
  418. //
  419. // if no buffers are obtained, the returned list is set to 0
  420. //
  421. status = LLC_STATUS_LOST_DATA_NO_BUFFERS;
  422. n = 0;
  423. }
  424. //
  425. // return the number of buffers left after this call. Works if we
  426. // allocated some or not
  427. //
  428. *pusBuffersLeft = pBufferPool->BufferCount;
  429. } else {
  430. //
  431. // bad SAP - no buffer pool for this one
  432. //
  433. status = LLC_STATUS_INVALID_STATION_ID;
  434. n = 0;
  435. }
  436. LeaveCriticalSection(&BufferSemaphore);
  437. //
  438. // if BuffersGot was specified then return the number of buffers allocated
  439. // and chained
  440. //
  441. if (ARGUMENT_PRESENT(BuffersGot)) {
  442. *BuffersGot = n;
  443. }
  444. IF_DEBUG(DLC_BUFFERS) {
  445. DPUT2("GetBuffers returning status=%x, BuffersLeft=%d\n", status, *pusBuffersLeft);
  446. }
  447. return status;
  448. }
  449. LLC_STATUS
  450. FreeBuffers(
  451. IN DWORD PoolIndex,
  452. IN DPLLC_DOS_BUFFER dpBuffer,
  453. OUT LPWORD pusBuffersLeft
  454. )
  455. /*++
  456. Routine Description:
  457. Free a DOS buffer to a DLC buffer pool
  458. Arguments:
  459. PoolIndex - SAP and adapter number (bit0 defines 0 or 1 adapter)
  460. dpBuffer - the released buffers (DOS pointer)
  461. pusBuffersLeft - the number of buffers left after the free
  462. Return Value:
  463. --*/
  464. {
  465. DPLLC_DOS_BUFFER dpBase; // DOS pointer
  466. PLLC_DOS_BUFFER pNextBuffer; // flat NT pointer
  467. PLLC_DOS_BUFFER pBuffer; // flat NT pointer
  468. PDOS_DLC_BUFFER_POOL pBufferPool = &aBufferPools[PoolIndex];
  469. #if DBG
  470. int n = 0;
  471. #endif
  472. IF_DEBUG(DLC_BUFFERS) {
  473. DPUT2("FreeBuffers: PoolIndex=%x dpBuffer=%x\n", PoolIndex, dpBuffer);
  474. }
  475. if (pBufferPool->BufferSize == 0) {
  476. return LLC_STATUS_INVALID_STATION_ID;
  477. }
  478. EnterCriticalSection(&BufferSemaphore);
  479. dpBase = dpBuffer;
  480. pNextBuffer = pBuffer = DOS_PTR_TO_FLAT(dpBuffer);
  481. //
  482. // the manual says for BUFFER.FREE (p3-4):
  483. //
  484. // "When the buffer is placed back in the buffer pool, bytes 4 and 5
  485. // (buffer length) of the buffer are set to zero."
  486. //
  487. // So, we oblige
  488. //
  489. WRITE_WORD(&pBuffer->Next.cbFrame, 0);
  490. if (pNextBuffer) {
  491. //
  492. // count the number of buffers being freed. Hopefully, the application
  493. // hasn't chenged over our terminating NULL pointer
  494. //
  495. while (pNextBuffer) {
  496. ++pBufferPool->BufferCount;
  497. #if DBG
  498. ++n;
  499. #endif
  500. pBuffer = pNextBuffer;
  501. pNextBuffer = (PLLC_DOS_BUFFER)READ_FAR_POINTER(&pBuffer->pNext);
  502. //
  503. // see above, about bytes 4 and 5
  504. //
  505. WRITE_WORD(&pBuffer->Next.cbFrame, 0);
  506. }
  507. //
  508. // put the freed chain at the head of the list, after linking the
  509. // buffer currently at the head of the list to the end of the freed
  510. // chain
  511. //
  512. WRITE_DWORD(&pBuffer->pNext, pBufferPool->dpBuffer);
  513. pBufferPool->dpBuffer = dpBase;
  514. #if DBG
  515. IF_DEBUG(DLC_BUFFERS) {
  516. DumpDosDlcBufferChain(dpBuffer, n);
  517. }
  518. } else {
  519. DPUT("ERROR: App tried to free NULL buffer chain\n");
  520. #endif
  521. }
  522. *pusBuffersLeft = pBufferPool->BufferCount;
  523. if (pBufferPool->BufferCount > pBufferPool->MaximumBufferCount) {
  524. pBufferPool->MaximumBufferCount = pBufferPool->BufferCount;
  525. }
  526. LeaveCriticalSection(&BufferSemaphore);
  527. return STATUS_SUCCESS;
  528. }
  529. WORD
  530. CalculateBufferRequirement(
  531. IN UCHAR Adapter,
  532. IN WORD StationId,
  533. IN PLLC_BUFFER pFrame,
  534. IN LLC_DOS_PARMS UNALIGNED * pDosParms,
  535. OUT PWORD BufferSize
  536. )
  537. /*++
  538. Routine Description:
  539. Calculate the number of DOS buffers required to hold the data received into
  540. an NT buffer. We have to go through this laborious phase because we need to
  541. know ahead of time if we have enough DOS buffers into which we will receive
  542. an I-Frame.
  543. Also, the size of DOS buffers is fixed, but the size of NT receive frame
  544. buffers can vary depending on the size of the received frame, the options
  545. requested and the 'binary buddy' allocator algorithm in the DLC driver. You
  546. may think that since we specify to the driver the buffer pool size in the
  547. DLC.OPEN.SAP call that it would allocate buffers of the specified size.
  548. Well, you'd be wrong: the DLC driver ignores this information and creates
  549. a buffer pool which can dole out variable size buffers, making my life more
  550. difficult than it ought to be
  551. Arguments:
  552. Adapter - which adapter we're receiving from
  553. StationId - the Station Id we're receiving on
  554. pFrame - pointer to the received frame in NT buffer
  555. pDosParms - pointer to the original DOS RECEIVE parameters
  556. BufferSize - pointer to the returned DOS buffer size
  557. Return Value:
  558. WORD
  559. --*/
  560. {
  561. //
  562. // pBufferPool points to the DOS buffer pool for this adapter/station ID
  563. //
  564. PDOS_DLC_BUFFER_POOL pBufferPool = &aBufferPools[GET_POOL_INDEX(Adapter, StationId)];
  565. //
  566. // dosUserLength is the USER_LENGTH value the DOS client requested when
  567. // the RECEIVE was submitted. This value may well be different than the
  568. // USER_LENGTH in the NT receive frame buffer
  569. //
  570. WORD dosUserLength = READ_WORD(&pDosParms->DosReceive.usUserLength);
  571. //
  572. // buffersRequired is the number of DOS buffers we need to allocate in order
  573. // to return the received frame. It will be at least 1
  574. //
  575. WORD buffersRequired = 1;
  576. //
  577. // dataSpace is the area in a DOS buffer available for data (after the
  578. // Buffer 1 or Buffer 2 header and the USER_LENGTH consideration)
  579. //
  580. WORD dataSpace;
  581. //
  582. // dataLeft is the amount of data in an NT buffer needing to be copied to
  583. // a DOS buffer
  584. //
  585. WORD dataLeft = 0;
  586. //
  587. // calculate the number of DOS buffers required to hold the data frame
  588. // received into the NT buffer. Note that we can't simply use the size
  589. // of the received frame because we need the size of the buffer headers
  590. // and if the NT frame is larger than the DOS buffers then we may end
  591. // up with more DOS buffers required than NT buffers which in turn
  592. // results in more overhead which we have to factor in
  593. //
  594. WORD bufferSize = pBufferPool->BufferSize;
  595. IF_DEBUG(DLC_BUFFERS) {
  596. DPUT("CalculateBufferRequirement\n");
  597. }
  598. //
  599. // calculate the amount of space in a DOS buffer after the Buffer 1 structure
  600. // (contiguous or non-contiguous, smoking or non-smoking, ah, ah, ah ahh-haa)
  601. // The buffer size MUST be large enough to hold the Buffer 1 overhead. This
  602. // is a FACT
  603. //
  604. dataSpace = bufferSize
  605. - (BUFFER_1_SIZE(
  606. pFrame->Contiguous.uchOptions
  607. & (LLC_CONTIGUOUS_MAC | LLC_CONTIGUOUS_DATA)
  608. )
  609. + dosUserLength
  610. );
  611. //
  612. // if there is less data space available in the first DOS receive buffer
  613. // than received data in the NT buffer then our first NT buffer will be
  614. // mapped to >1 DOS buffers: a Buffer 1 and 1 or more Buffer 2s. This is
  615. // before we even get to any associated Buffer 2s in the NT receive frame.
  616. //
  617. // Also: if the LLC_BREAK option is specified in the receive parameters
  618. // then the first data buffer will contain the header information. Note:
  619. // we assume this can only be for NotContiguous data, else how would we
  620. // know the size of the header information?
  621. //
  622. if (pFrame->Contiguous.uchOptions & LLC_BREAK) {
  623. if (!(pFrame->Contiguous.uchOptions & (LLC_CONTIGUOUS_MAC | LLC_CONTIGUOUS_DATA))) {
  624. dataLeft = pFrame->NotContiguous.cbBuffer;
  625. }
  626. #if DBG
  627. else {
  628. DPUT("CalculateBufferRequirement: Error: invalid options: BREAK && CONTIGUOUS???\n");
  629. }
  630. #endif
  631. } else if (dataSpace < pFrame->Contiguous.cbBuffer) {
  632. dataLeft = pFrame->Contiguous.cbBuffer - dataSpace;
  633. } else {
  634. //
  635. // we have enough space in the DOS buffer to copy all the received data
  636. // and some more
  637. //
  638. dataSpace -= pFrame->Next.cbBuffer;
  639. dataLeft = 0;
  640. }
  641. //
  642. // if there is more data in the NT buffer than we can fit in a DOS buffer,
  643. // either because the buffer sizes are different or due to the DOS client
  644. // requesting the BREAK option, then generate Buffer 2 requirements
  645. //
  646. while (dataLeft) {
  647. ++buffersRequired;
  648. dataSpace = bufferSize - (BUFFER_2_SIZE + dosUserLength);
  649. if (dataLeft > dataSpace) {
  650. dataLeft -= dataSpace;
  651. dataSpace = 0;
  652. } else {
  653. dataSpace -= dataLeft;
  654. dataLeft = 0;
  655. }
  656. }
  657. //
  658. // if the NT received frame has any associated Buffer 2 structures then
  659. // calculate the additional buffer requirement. Again, the NT buffers may
  660. // be a different size(s) than the DOS buffers.
  661. //
  662. // At this point, dataSpace is the amount of remaining data area in the
  663. // previous DOS buffer - Buffer 1 or Buffer 2. Use this before we allocate
  664. // a new DOS buffer
  665. //
  666. for (pFrame = pFrame->pNext; pFrame; pFrame = pFrame->pNext) {
  667. if (pFrame->Next.cbBuffer > dataSpace) {
  668. dataLeft = pFrame->Next.cbBuffer - dataSpace;
  669. dataSpace = 0;
  670. while (dataLeft) {
  671. ++buffersRequired;
  672. dataSpace = bufferSize - (BUFFER_2_SIZE + dosUserLength);
  673. if (dataLeft > dataSpace) {
  674. dataLeft -= dataSpace;
  675. dataSpace = 0;
  676. } else {
  677. dataSpace -= dataLeft;
  678. dataLeft = 0;
  679. }
  680. }
  681. } else {
  682. //
  683. // we have enough space in the DOS buffer to copy all the received data
  684. // and some more
  685. //
  686. dataSpace -= pFrame->Next.cbBuffer;
  687. dataLeft = 0;
  688. }
  689. }
  690. IF_DEBUG(DLC_BUFFERS) {
  691. DPUT1("CalculateBufferRequirement: %d buffers required\n", buffersRequired);
  692. }
  693. //
  694. // set the output DOS buffer size and return the number of DOS buffers
  695. // required
  696. //
  697. *BufferSize = bufferSize;
  698. return buffersRequired;
  699. }
  700. LLC_STATUS
  701. CopyFrame(
  702. IN PLLC_BUFFER pFrame,
  703. IN DPLLC_DOS_BUFFER DosBuffers,
  704. IN WORD UserLength,
  705. IN WORD BufferSize,
  706. IN DWORD Flags
  707. )
  708. /*++
  709. Routine Description:
  710. Copies a received NT frame into DOS buffers. We have previously calculated
  711. the DOS buffer requirement and allocated that requirement
  712. We may copy the entire received frame or only part of it. We can only copy
  713. partial frames if the frame is NOT an I-Frame
  714. Notes: 1. the DOS buffer manager returns the orginal DOS 16:16 buffer
  715. pointers, we must use those original pointers, when the buffers
  716. are linked to each other.
  717. 2. We do NOT chain frames - DOS DLC cannot handle frames being
  718. chained, and there is nothing to be gained by us chaining them
  719. except that we reduce the number of completed READs. However,
  720. we still have to generate the same number of simulated hardware
  721. interrupts to the VDM
  722. 3. Unlike DOS buffer pools, NT does not deal in buffers of a
  723. specific size. Rather, it allocates buffers from a pool based
  724. on a 'binary buddy' algorithm and the size of the data to be
  725. returned. Therefore, there is no correspondence between the
  726. size of DOS buffers (for a station) and the NT buffers which
  727. were used to receive the data
  728. 4. We only copy the data in this routine - whether it is deferred
  729. data from some prior local busy state or current data is immaterial
  730. to this routine. Responsibility for managing current/deferred frames
  731. is left to the caller
  732. Arguments:
  733. pFrame - pointer to received frame in NT buffer(s)
  734. DosBuffers - DOS pointer to chain of DOS receive buffers
  735. UserLength - the USER_LENGTH value specified in the DOS RECEIVE
  736. BufferSize - size of a DOS buffer
  737. Flags - various flags:
  738. CF_CONTIGUOUS
  739. Set if this frame is contiguous
  740. CF_BREAK
  741. Set if the DOS client requested that the buffers be
  742. broken into header, data
  743. CF_PARTIAL
  744. Set if we are copying a partial frame - ok for non
  745. I-Frames
  746. Return Value:
  747. LLC_STATUS
  748. LLC_STATUS_SUCCESS
  749. All data copied from NT buffer to DOS buffer(s)
  750. LLC_STATUS_LOST_DATA_INADEQUATE_SPACE
  751. A partial copy was performed. Some data ended up in DOS buffer(s),
  752. the rest is lost. Cannot be I-Frame!
  753. --*/
  754. {
  755. //
  756. // pDosBuffer - pointer to the DOS buffer which is usable in 32-bit mode
  757. //
  758. PLLC_DOS_BUFFER pDosBuffer = (PLLC_DOS_BUFFER)DOS_PTR_TO_FLAT(DosBuffers);
  759. //
  760. // dataSpace - amount of data space available in the current DOS buffer.
  761. // Initialize it for common Buffer 1 case
  762. //
  763. WORD dataSpace = BufferSize - (WORD)&(((PLLC_BUFFER)0)->Contiguous.pNextFrame);
  764. PBYTE pDosData; // pointer to place in DOS buffer where we copy data TO
  765. PBYTE pNtData; // corresponding place in NT buffer where we copy data FROM
  766. WORD headerLength; // amount of data in headers
  767. WORD dataLeft; // amount of data to be copied FROM the NT buffer
  768. WORD userOffset; // offset of USER_SPACE
  769. WORD bufferLeft; // amount of data left in current NT buffer
  770. WORD dataToCopy; // amount of data to copy to DOS buffer
  771. WORD dataCopied; // amount of data copied to Buffer 1/Buffer 2
  772. WORD frameLength; // length of entire frame
  773. //
  774. // bufferOffset - used in generating the correct userOffset
  775. //
  776. WORD bufferOffset = LOWORD(DosBuffers);
  777. IF_DEBUG(DLC_BUFFERS) {
  778. DPUT5("CopyFrame: pFrame=%x DosBuffers=%x UserLength=%x Flags=%x pDosBuffer=%x\n",
  779. pFrame, DosBuffers, UserLength, Flags, pDosBuffer);
  780. }
  781. //
  782. // copy the first buffer. If the BREAK option is set then we only copy the
  783. // header part (ASSUMES NotContiguous)! NB: we KNOW that we can fit at least
  784. // this amount of data in the DOS buffer. Also: it is safe to use RtlCopyMemory ?
  785. //
  786. RtlCopyMemory(&pDosBuffer->Contiguous.cbFrame,
  787. &pFrame->Contiguous.cbFrame,
  788. (DWORD)&(((PLLC_BUFFER)0)->Contiguous.pNextFrame)
  789. - (DWORD)&(((PLLC_BUFFER)0)->Contiguous.cbFrame)
  790. );
  791. //
  792. // pDosData points to the area in the DOS buffer where the LAN header info
  793. // or data will go, depending on format
  794. //
  795. pDosData = &pDosBuffer->NotContiguous.cbLanHeader;
  796. //
  797. // if the CF_CONTIGUOUS flag is not set in the Flags parameter then this is
  798. // a NotContiguous frame. We must copy the header in 2 parts, because the
  799. // NT buffer contains the NEXT_FRAME field which the DOS buffer does not
  800. //
  801. if (!(Flags & CF_CONTIGUOUS)) {
  802. IF_DEBUG(DLC_BUFFERS) {
  803. DPUT("Buffer is NOT CONTIGUOUS\n");
  804. }
  805. RtlCopyMemory(pDosData,
  806. &pFrame->NotContiguous.cbLanHeader,
  807. (DWORD)&(((PLLC_BUFFER)0)->NotContiguous.usPadding)
  808. - (DWORD)&(((PLLC_BUFFER)0)->NotContiguous.cbLanHeader)
  809. );
  810. pDosData += (DWORD)&(((PLLC_BUFFER)0)->NotContiguous.usPadding)
  811. - (DWORD)&(((PLLC_BUFFER)0)->NotContiguous.cbLanHeader);
  812. dataSpace -= (WORD)&(((PLLC_BUFFER)0)->NotContiguous.usPadding)
  813. - (WORD)&(((PLLC_BUFFER)0)->NotContiguous.cbLanHeader);
  814. userOffset = (WORD)&(((PLLC_DOS_BUFFER)0)->NotContiguous.auchDlcHeader)
  815. + sizeof(((PLLC_DOS_BUFFER)0)->NotContiguous.auchDlcHeader);
  816. //
  817. // sanity check
  818. //
  819. ASSERT(userOffset == 58);
  820. //
  821. // amount of data in headers
  822. //
  823. headerLength = pFrame->NotContiguous.cbLanHeader
  824. + pFrame->NotContiguous.cbDlcHeader;
  825. } else {
  826. IF_DEBUG(DLC_BUFFERS) {
  827. DPUT("Buffer is CONTIGUOUS\n");
  828. }
  829. userOffset = (WORD)&(((PLLC_DOS_BUFFER)0)->Contiguous.uchAdapterNumber)
  830. + sizeof(((PLLC_DOS_BUFFER)0)->Contiguous.uchAdapterNumber);
  831. //
  832. // sanity check
  833. //
  834. ASSERT(userOffset == 20);
  835. //
  836. // no header info in contiguous buffer
  837. //
  838. headerLength = 0;
  839. }
  840. //
  841. // if the CF_BREAK flag is set in the Flags parameter then the DOS app
  842. // requested that the first buffer (presumed NotContiguous) be broken into
  843. // one buffer containing just the header information and another containing
  844. // the data. In this case copy no more data to the first buffer
  845. //
  846. if (!(Flags & CF_BREAK)) {
  847. //
  848. // pDosData points at USER_SPACE - offset 58 for NotContiguous buffer,
  849. // offset 20 for Contiguous buffer. Bump it by USER_LENGTH (still don't
  850. // know if we ever expect anything meaningful to be placed at USER_SPACE
  851. // before we give the buffer to DOS
  852. //
  853. pDosData += UserLength;
  854. //
  855. // get in dataSpace the amount of space left in the DOS buffer where
  856. // we are able to copy data. Assume that UserLength doesn't make this
  857. // go negative (ie LARGE)
  858. //
  859. ASSERT(dataSpace >= UserLength);
  860. dataSpace -= UserLength;
  861. } else {
  862. IF_DEBUG(DLC_BUFFERS) {
  863. DPUT("Buffer has BREAK\n");
  864. }
  865. //
  866. // the DOS app requested BREAK. Set the count of data in this buffer
  867. // to 0. Use WRITE_WORD since it may be unaligned. Update the other
  868. // header fields we can't just copy from the NT buffer
  869. //
  870. WRITE_WORD(&pDosBuffer->NotContiguous.cbBuffer, 0);
  871. WRITE_WORD(&pDosBuffer->NotContiguous.offUserData, userOffset + bufferOffset);
  872. WRITE_WORD(&pDosBuffer->NotContiguous.cbUserData, UserLength);
  873. //
  874. // get the next DOS buffer in the list. There may not be one? (Don't
  875. // expect such a situation)
  876. //
  877. bufferOffset = READ_WORD(&pDosBuffer->pNext);
  878. pDosBuffer = DOS_PTR_TO_FLAT(pDosBuffer->pNext);
  879. if (pDosBuffer) {
  880. userOffset = (WORD)&(((PLLC_DOS_BUFFER)0)->Next.cbUserData)
  881. + sizeof(((PLLC_DOS_BUFFER)0)->Next.cbUserData);
  882. //
  883. // sanity check
  884. //
  885. ASSERT(userOffset == 12);
  886. dataSpace = BufferSize - (BUFFER_2_SIZE + UserLength);
  887. pDosData = (PBYTE)pDosBuffer + BUFFER_2_SIZE + UserLength;
  888. } else {
  889. //
  890. // that was the last buffer. Either there was just header data or
  891. // this is a partial copy and therefore not an I-Frame
  892. //
  893. IF_DEBUG(DLC_BUFFERS) {
  894. DPUT("CopyFrame: returning early\n");
  895. }
  896. return (Flags & CF_PARTIAL)
  897. ? LLC_STATUS_LOST_DATA_INADEQUATE_SPACE
  898. : LLC_STATUS_SUCCESS;
  899. }
  900. }
  901. //
  902. // frameLength is length of entire frame - must appear in Buffer 1 and
  903. // Buffer 2s
  904. //
  905. frameLength = pFrame->Contiguous.cbFrame;
  906. //
  907. // dataLeft is the amount of data left to copy from the frame after the
  908. // headers have been taken care of. For a Contiguous buffer, this is the
  909. // same as the length of the frame; for a NotContiguous buffer, this is
  910. // the length of the frame - the combined length of the LAN and DLC
  911. // headers
  912. //
  913. dataLeft = frameLength - headerLength;
  914. //
  915. // get pointer to data in NT buffer and the amount of data to copy (from
  916. // rest of NT frame)
  917. //
  918. pNtData = (PBYTE)pFrame
  919. + pFrame->Contiguous.offUserData
  920. + pFrame->Contiguous.cbUserData;
  921. bufferLeft = pFrame->Contiguous.cbBuffer;
  922. //
  923. // dataCopied is amount of data copied to current buffer (1 or 2) and goes
  924. // in cbBuffer field (aka LENGTH_IN_BUFFER)
  925. //
  926. dataCopied = 0;
  927. //
  928. // we have copied all the data we could to the first buffer. While there
  929. // are more DOS buffers, copy data from NT buffer
  930. //
  931. do {
  932. //
  933. // calculate the amount of space available in the current buffer
  934. //
  935. if (dataSpace >= bufferLeft) {
  936. dataToCopy = bufferLeft;
  937. dataLeft -= dataToCopy;
  938. dataSpace -= dataToCopy;
  939. bufferLeft = 0;
  940. } else {
  941. dataToCopy = dataSpace;
  942. bufferLeft -= dataSpace;
  943. dataLeft -= dataSpace;
  944. dataSpace = 0;
  945. }
  946. //
  947. // copy the data. This will fill up the current DOS buffer, exhaust the
  948. // current NT buffer, or both
  949. //
  950. if (dataToCopy) {
  951. IF_DEBUG(DLC_RX_DATA) {
  952. DPUT3("CopyFrame: Copying %d bytes from %x to %x\n", dataToCopy, pNtData, pDosData);
  953. }
  954. RtlCopyMemory(pDosData, pNtData, dataToCopy);
  955. //
  956. // dataCopied accumulates until we fill a DOS buffer
  957. //
  958. dataCopied += dataToCopy;
  959. //
  960. // update to- and from- pointers for next go round loop
  961. //
  962. pDosData += dataToCopy;
  963. pNtData += dataToCopy;
  964. }
  965. //
  966. // we have run out of space in a DOS buffer, or out of data to copy from
  967. // the NT buffer
  968. //
  969. if (dataLeft) {
  970. //
  971. // we think there is data left to copy
  972. //
  973. if (!bufferLeft) {
  974. //
  975. // finished current NT buffer. Get next one
  976. //
  977. pFrame = pFrame->pNext;
  978. if (pFrame) {
  979. bufferLeft = pFrame->Next.cbBuffer;
  980. pNtData = (PBYTE)pFrame
  981. + pFrame->Contiguous.offUserData
  982. + pFrame->Contiguous.cbUserData;
  983. IF_DEBUG(DLC_RX_DATA) {
  984. DPUT3("CopyFrame: new NT buffer @%x pNtData @%x bufferLeft=%d\n",
  985. pFrame, pNtData, bufferLeft);
  986. }
  987. } else {
  988. //
  989. // no more NT buffers. Is this a partial copy?
  990. //
  991. DPUT("*** ERROR: dataLeft && no more NT buffers ***\n");
  992. ASSERT(Flags & CF_PARTIAL);
  993. break;
  994. }
  995. }
  996. if (!dataSpace) {
  997. //
  998. // update the current DOS buffer header (it doesn't matter that
  999. // we use Contiguous, NotContiguous, or Next: these fields are
  1000. // present in all 3 buffer types)
  1001. //
  1002. WRITE_WORD(&pDosBuffer->Contiguous.cbFrame, frameLength);
  1003. WRITE_WORD(&pDosBuffer->Contiguous.cbBuffer, dataCopied);
  1004. WRITE_WORD(&pDosBuffer->Contiguous.offUserData, userOffset + bufferOffset);
  1005. WRITE_WORD(&pDosBuffer->Contiguous.cbUserData, UserLength);
  1006. //
  1007. // and get the next one
  1008. //
  1009. bufferOffset = READ_WORD(&pDosBuffer->pNext);
  1010. pDosBuffer = DOS_PTR_TO_FLAT(pDosBuffer->pNext);
  1011. //
  1012. // if we have another DOS buffer, then it is a Next buffer. Get the
  1013. // buffer variables
  1014. //
  1015. if (pDosBuffer) {
  1016. pDosData = (PBYTE)pDosBuffer + BUFFER_2_SIZE + UserLength;
  1017. userOffset = (WORD)&(((PLLC_DOS_BUFFER)0)->Next.cbUserData)
  1018. + sizeof(((PLLC_DOS_BUFFER)0)->Next.cbUserData);
  1019. //
  1020. // sanity check
  1021. //
  1022. ASSERT(userOffset == 12);
  1023. //
  1024. // get new available space (constant)
  1025. //
  1026. dataSpace = BufferSize - (BUFFER_2_SIZE + UserLength);
  1027. dataCopied = 0;
  1028. IF_DEBUG(DLC_RX_DATA) {
  1029. DPUT3("CopyFrame: new DOS buffer @%x pDosData @%x dataSpace=%d\n",
  1030. pDosBuffer, pDosData, dataSpace);
  1031. }
  1032. } else {
  1033. //
  1034. // no more DOS buffers. Is this a partial copy?
  1035. //
  1036. DPUT("*** ERROR: dataLeft && no more DOS buffers ***\n");
  1037. ASSERT(Flags & CF_PARTIAL);
  1038. break;
  1039. }
  1040. }
  1041. } else {
  1042. //
  1043. // update the current DOS buffer header
  1044. //
  1045. WRITE_WORD(&pDosBuffer->Contiguous.cbFrame, frameLength);
  1046. WRITE_WORD(&pDosBuffer->Contiguous.cbBuffer, dataCopied);
  1047. WRITE_WORD(&pDosBuffer->Contiguous.offUserData, userOffset + bufferOffset);
  1048. WRITE_WORD(&pDosBuffer->Contiguous.cbUserData, UserLength);
  1049. }
  1050. } while ( dataLeft );
  1051. //
  1052. // if CF_PARTIAL set then we knew we were copying a partial frame before
  1053. // we got here
  1054. //
  1055. return (Flags & CF_PARTIAL)
  1056. ? LLC_STATUS_LOST_DATA_INADEQUATE_SPACE
  1057. : LLC_STATUS_SUCCESS;
  1058. }
  1059. BOOLEAN
  1060. AllBuffersInPool(
  1061. IN DWORD PoolIndex
  1062. )
  1063. /*++
  1064. Routine Description:
  1065. Returns TRUE if all buffers that a pool has held are currently in the pool.
  1066. Once a buffer has been added to a pool, it cannot be removed, saved by not
  1067. returning it to the pool. Hence this function will always return TRUE if
  1068. the app is well-behaved and all buffers that have been placed in the pool
  1069. are currently in the pool
  1070. Arguments:
  1071. PoolIndex - pool id
  1072. Return Value:
  1073. BOOLEAN
  1074. TRUE - all buffers back
  1075. FALSE - buffer pool currently contains less than full number of buffers
  1076. --*/
  1077. {
  1078. BOOLEAN result;
  1079. PDOS_DLC_BUFFER_POOL pBufferPool = &aBufferPools[PoolIndex];
  1080. EnterCriticalSection(&BufferSemaphore);
  1081. result = (pBufferPool->BufferCount == pBufferPool->MaximumBufferCount);
  1082. LeaveCriticalSection(&BufferSemaphore);
  1083. return result;
  1084. }