2370 lines
73 KiB
C
2370 lines
73 KiB
C
/*++
|
||
|
||
Copyright (c) 1991 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
llcsend.c
|
||
|
||
Abstract:
|
||
|
||
The module implements all sending functions and the main
|
||
send process. There three different send queues:
|
||
- I_Queue
|
||
- DirU_Queue
|
||
- ExpiditedQueue (for LLC commands)
|
||
|
||
Each queue has the pointer of a packet building primitive, that
|
||
takes an NDIS packet of an queue element.
|
||
|
||
Contents:
|
||
RunSendTaskAndUnlock
|
||
BackgroundProcessAndUnlock
|
||
BackgroundProcess
|
||
LlcNdisSendComplete
|
||
GetI_Packet
|
||
StartSendProcess
|
||
EnableSendProcess
|
||
StopSendProcess
|
||
DisableSendProcess
|
||
BuildDirOrU_Packet
|
||
SendLlcFrame
|
||
GetLlcCommandPacket
|
||
SendNdisPacket
|
||
CompleteSendAndLock
|
||
RespondTestOrXid
|
||
LlcSendU
|
||
LlcSendI
|
||
QueuePacket
|
||
CheckAndDuplicatePacket
|
||
BackgroundProcessWithinLock
|
||
|
||
Author:
|
||
|
||
Antti Saarenheimo (o-anttis) 23-MAY-1991
|
||
|
||
Revision History:
|
||
|
||
--*/
|
||
|
||
#include <llc.h>
|
||
|
||
//
|
||
// The IEEE XID frame is constant data: (id, support Class II, maxin = 127};
|
||
//
|
||
|
||
LLC_XID_INFORMATION Ieee802Xid = {IEEE_802_XID_ID, LLC_CLASS_II, (127 << 1)};
|
||
PMDL pXidMdl = NULL;
|
||
|
||
|
||
//
|
||
// Because normally LAN networks are error free, we added this option
|
||
// to test the error recovery of DLC protocol. It seems to work now
|
||
// quite well (but first we had to fix a fundamental bug in the sending
|
||
// of REJ-r0).
|
||
//
|
||
|
||
//
|
||
// Enable this to test REJECT states (after a major changes in
|
||
// the state machine).
|
||
//
|
||
|
||
//#define LLC_LOSE_I_PACKETS
|
||
|
||
#ifdef LLC_LOSE_I_PACKETS
|
||
|
||
#define DBG_ERROR_PERCENT(a) (((a) * 0x8000) / 100)
|
||
|
||
|
||
//
|
||
// Pseudo random table to lose packets
|
||
//
|
||
|
||
static USHORT aRandom[1000] = {
|
||
41, 18467, 6334, 26500, 19169, 15724, 11478, 29358, 26962, 24464,
|
||
5705, 28145, 23281, 16827, 9961, 491, 2995, 11942, 4827, 5436,
|
||
32391, 14604, 3902, 153, 292, 12382, 17421, 18716, 19718, 19895,
|
||
5447, 21726, 14771, 11538, 1869, 19912, 25667, 26299, 17035, 9894,
|
||
28703, 23811, 31322, 30333, 17673, 4664, 15141, 7711, 28253, 6868,
|
||
25547, 27644, 32662, 32757, 20037, 12859, 8723, 9741, 27529, 778,
|
||
12316, 3035, 22190, 1842, 288, 30106, 9040, 8942, 19264, 22648,
|
||
27446, 23805, 15890, 6729, 24370, 15350, 15006, 31101, 24393, 3548,
|
||
19629, 12623, 24084, 19954, 18756, 11840, 4966, 7376, 13931, 26308,
|
||
16944, 32439, 24626, 11323, 5537, 21538, 16118, 2082, 22929, 16541,
|
||
4833, 31115, 4639, 29658, 22704, 9930, 13977, 2306, 31673, 22386,
|
||
5021, 28745, 26924, 19072, 6270, 5829, 26777, 15573, 5097, 16512,
|
||
23986, 13290, 9161, 18636, 22355, 24767, 23655, 15574, 4031, 12052,
|
||
27350, 1150, 16941, 21724, 13966, 3430, 31107, 30191, 18007, 11337,
|
||
15457, 12287, 27753, 10383, 14945, 8909, 32209, 9758, 24221, 18588,
|
||
6422, 24946, 27506, 13030, 16413, 29168, 900, 32591, 18762, 1655,
|
||
17410, 6359, 27624, 20537, 21548, 6483, 27595, 4041, 3602, 24350,
|
||
10291, 30836, 9374, 11020, 4596, 24021, 27348, 23199, 19668, 24484,
|
||
8281, 4734, 53, 1999, 26418, 27938, 6900, 3788, 18127, 467,
|
||
3728, 14893, 24648, 22483, 17807, 2421, 14310, 6617, 22813, 9514,
|
||
14309, 7616, 18935, 17451, 20600, 5249, 16519, 31556, 22798, 30303,
|
||
6224, 11008, 5844, 32609, 14989, 32702, 3195, 20485, 3093, 14343,
|
||
30523, 1587, 29314, 9503, 7448, 25200, 13458, 6618, 20580, 19796,
|
||
14798, 15281, 19589, 20798, 28009, 27157, 20472, 23622, 18538, 12292,
|
||
6038, 24179, 18190, 29657, 7958, 6191, 19815, 22888, 19156, 11511,
|
||
16202, 2634, 24272, 20055, 20328, 22646, 26362, 4886, 18875, 28433,
|
||
29869, 20142, 23844, 1416, 21881, 31998, 10322, 18651, 10021, 5699,
|
||
3557, 28476, 27892, 24389, 5075, 10712, 2600, 2510, 21003, 26869,
|
||
17861, 14688, 13401, 9789, 15255, 16423, 5002, 10585, 24182, 10285,
|
||
27088, 31426, 28617, 23757, 9832, 30932, 4169, 2154, 25721, 17189,
|
||
19976, 31329, 2368, 28692, 21425, 10555, 3434, 16549, 7441, 9512,
|
||
30145, 18060, 21718, 3753, 16139, 12423, 16279, 25996, 16687, 12529,
|
||
22549, 17437, 19866, 12949, 193, 23195, 3297, 20416, 28286, 16105,
|
||
24488, 16282, 12455, 25734, 18114, 11701, 31316, 20671, 5786, 12263,
|
||
4313, 24355, 31185, 20053, 912, 10808, 1832, 20945, 4313, 27756,
|
||
28321, 19558, 23646, 27982, 481, 4144, 23196, 20222, 7129, 2161,
|
||
5535, 20450, 11173, 10466, 12044, 21659, 26292, 26439, 17253, 20024,
|
||
26154, 29510, 4745, 20649, 13186, 8313, 4474, 28022, 2168, 14018,
|
||
18787, 9905, 17958, 7391, 10202, 3625, 26477, 4414, 9314, 25824,
|
||
29334, 25874, 24372, 20159, 11833, 28070, 7487, 28297, 7518, 8177,
|
||
17773, 32270, 1763, 2668, 17192, 13985, 3102, 8480, 29213, 7627,
|
||
4802, 4099, 30527, 2625, 1543, 1924, 11023, 29972, 13061, 14181,
|
||
31003, 27432, 17505, 27593, 22725, 13031, 8492, 142, 17222, 31286,
|
||
13064, 7900, 19187, 8360, 22413, 30974, 14270, 29170, 235, 30833,
|
||
19711, 25760, 18896, 4667, 7285, 12550, 140, 13694, 2695, 21624,
|
||
28019, 2125, 26576, 21694, 22658, 26302, 17371, 22466, 4678, 22593,
|
||
23851, 25484, 1018, 28464, 21119, 23152, 2800, 18087, 31060, 1926,
|
||
9010, 4757, 32170, 20315, 9576, 30227, 12043, 22758, 7164, 5109,
|
||
7882, 17086, 29565, 3487, 29577, 14474, 2625, 25627, 5629, 31928,
|
||
25423, 28520, 6902, 14962, 123, 24596, 3737, 13261, 10195, 32525,
|
||
1264, 8260, 6202, 8116, 5030, 20326, 29011, 30771, 6411, 25547,
|
||
21153, 21520, 29790, 14924, 30188, 21763, 4940, 20851, 18662, 13829,
|
||
30900, 17713, 18958, 17578, 8365, 13007, 11477, 1200, 26058, 6439,
|
||
2303, 12760, 19357, 2324, 6477, 5108, 21113, 14887, 19801, 22850,
|
||
14460, 22428, 12993, 27384, 19405, 6540, 31111, 28704, 12835, 32356,
|
||
6072, 29350, 18823, 14485, 20556, 23216, 1626, 9357, 8526, 13357,
|
||
29337, 23271, 23869, 29361, 12896, 13022, 29617, 10112, 12717, 18696,
|
||
11585, 24041, 24423, 24129, 24229, 4565, 6559, 8932, 22296, 29855,
|
||
12053, 16962, 3584, 29734, 6654, 16972, 21457, 14369, 22532, 2963,
|
||
2607, 2483, 911, 11635, 10067, 22848, 4675, 12938, 2223, 22142,
|
||
23754, 6511, 22741, 20175, 21459, 17825, 3221, 17870, 1626, 31934,
|
||
15205, 31783, 23850, 17398, 22279, 22701, 12193, 12734, 1637, 26534,
|
||
5556, 1993, 10176, 25705, 6962, 10548, 15881, 300, 14413, 16641,
|
||
19855, 24855, 13142, 11462, 27611, 30877, 20424, 32678, 1752, 18443,
|
||
28296, 12673, 10040, 9313, 875, 20072, 12818, 610, 1017, 14932,
|
||
28112, 30695, 13169, 23831, 20040, 26488, 28685, 19090, 19497, 2589,
|
||
25990, 15145, 19353, 19314, 18651, 26740, 22044, 11258, 335, 8759,
|
||
11192, 7605, 25264, 12181, 28503, 3829, 23775, 20608, 29292, 5997,
|
||
17549, 29556, 25561, 31627, 6467, 29541, 26129, 31240, 27813, 29174,
|
||
20601, 6077, 20215, 8683, 8213, 23992, 25824, 5601, 23392, 15759,
|
||
2670, 26428, 28027, 4084, 10075, 18786, 15498, 24970, 6287, 23847,
|
||
32604, 503, 21221, 22663, 5706, 2363, 9010, 22171, 27489, 18240,
|
||
12164, 25542, 7619, 20913, 7591, 6704, 31818, 9232, 750, 25205,
|
||
4975, 1539, 303, 11422, 21098, 11247, 13584, 13648, 2971, 17864,
|
||
22913, 11075, 21545, 28712, 17546, 18678, 1769, 15262, 8519, 13985,
|
||
28289, 15944, 2865, 18540, 23245, 25508, 28318, 27870, 9601, 28323,
|
||
21132, 24472, 27152, 25087, 28570, 29763, 29901, 17103, 14423, 3527,
|
||
11600, 26969, 14015, 5565, 28, 21543, 25347, 2088, 2943, 12637,
|
||
22409, 26463, 5049, 4681, 1588, 11342, 608, 32060, 21221, 1758,
|
||
29954, 20888, 14146, 690, 7949, 12843, 21430, 25620, 748, 27067,
|
||
4536, 20783, 18035, 32226, 15185, 7038, 9853, 25629, 11224, 15748,
|
||
19923, 3359, 32257, 24766, 4944, 14955, 23318, 32726, 25411, 21025,
|
||
20355, 31001, 22549, 9496, 18584, 9515, 17964, 23342, 8075, 17913,
|
||
16142, 31196, 21948, 25072, 20426, 14606, 26173, 24429, 32404, 6705,
|
||
20626, 29812, 19375, 30093, 16565, 16036, 14736, 29141, 30814, 5994,
|
||
8256, 6652, 23936, 30838, 20482, 1355, 21015, 1131, 18230, 17841,
|
||
14625, 2011, 32637, 4186, 19690, 1650, 5662, 21634, 10893, 10353,
|
||
21416, 13452, 14008, 7262, 22233, 5454, 16303, 16634, 26303, 14256,
|
||
148, 11124, 12317, 4213, 27109, 24028, 29200, 21080, 21318, 16858,
|
||
24050, 24155, 31361, 15264, 11903, 3676, 29643, 26909, 14902, 3561,
|
||
28489, 24948, 1282, 13653, 30674, 2220, 5402, 6923, 3831, 19369,
|
||
3878, 20259, 19008, 22619, 23971, 30003, 21945, 9781, 26504, 12392,
|
||
32685, 25313, 6698, 5589, 12722, 5938, 19037, 6410, 31461, 6234,
|
||
12508, 9961, 3959, 6493, 1515, 25269, 24937, 28869, 58, 14700,
|
||
13971, 26264, 15117, 16215, 24555, 7815, 18330, 3039, 30212, 29288,
|
||
28082, 1954, 16085, 20710, 24484, 24774, 8380, 29815, 25951, 6541,
|
||
18115, 1679, 17110, 25898, 23073, 788, 23977, 18132, 29956, 28689,
|
||
26113, 10008, 12941, 15790, 1723, 21363, 28, 25184, 24778, 7200,
|
||
5071, 1885, 21974, 1071, 11333, 22867, 26153, 14295, 32168, 20825,
|
||
9676, 15629, 28650, 2598, 3309, 4693, 4686, 30080, 10116, 12249,
|
||
};
|
||
|
||
#endif
|
||
|
||
|
||
|
||
VOID
|
||
RunSendTaskAndUnlock(
|
||
IN PADAPTER_CONTEXT pAdapterContext
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Function is the send engine of the data link driver
|
||
and the background task.
|
||
It sends the queue objects as far as there is
|
||
free NDIS packets in a small packet queue.
|
||
The number of NDIS packets are limited because
|
||
too deep send queues are bad for the connection based protocols.
|
||
|
||
This is called from NdisIndicateReceiveComplete,
|
||
NdisSendComplete and almost all LLC commands.
|
||
|
||
Arguments:
|
||
|
||
pAdapterContext - adapter context
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
|
||
{
|
||
ASSUME_IRQL(DISPATCH_LEVEL);
|
||
|
||
//
|
||
// We must serialize the sending. 802.2 protocol
|
||
// will simply die if two sequential packets are sent in a wrong
|
||
// order. The receiving and DLC level transmit processing can still
|
||
// work even if the sending is syncronous in the data link level.
|
||
//
|
||
|
||
if (pAdapterContext->SendProcessIsActive == FALSE) {
|
||
|
||
pAdapterContext->SendProcessIsActive = TRUE;
|
||
|
||
while (!IsListEmpty(&pAdapterContext->NextSendTask)
|
||
&& pAdapterContext->pNdisPacketPool != NULL
|
||
&& !pAdapterContext->ResetInProgress) {
|
||
|
||
//
|
||
// executed the next send task in the send queue,
|
||
// expidited data (if any) is always the first and
|
||
// it is executed as far as there is any expidited packets.
|
||
// The rest (I, UI, DIR) are executed in a round robin
|
||
//
|
||
|
||
SendNdisPacket(pAdapterContext,
|
||
|
||
//
|
||
// this next generates a pointer to a function which returns a
|
||
// packet to send (eg. GetI_Packet)
|
||
//
|
||
|
||
((PF_GET_PACKET)((PLLC_QUEUE)pAdapterContext->NextSendTask.Flink)->pObject)(pAdapterContext)
|
||
);
|
||
}
|
||
|
||
pAdapterContext->SendProcessIsActive = FALSE;
|
||
}
|
||
|
||
RELEASE_SPIN_LOCK(&pAdapterContext->SendSpinLock);
|
||
}
|
||
|
||
|
||
VOID
|
||
BackgroundProcessAndUnlock(
|
||
IN PADAPTER_CONTEXT pAdapterContext
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Function is both the send engine of the data link driver
|
||
and the background task.
|
||
It executes the queue objects as far as there is
|
||
free NDIS packets in a small packet queue.
|
||
The number of NDIS packets are limited because
|
||
too deep send queues are bad for the connection based protocols.
|
||
|
||
This is called from NdisIndicateReceiveComplete,
|
||
NdisSendComplete and almost all LLC commands.
|
||
|
||
Arguments:
|
||
|
||
pAdapterContext - adapter context
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
|
||
{
|
||
ASSUME_IRQL(DISPATCH_LEVEL);
|
||
|
||
//
|
||
// Prevent recursive background process calls, we don't need to start
|
||
// new loop, if there is already one instance running somewhere
|
||
// up in the stack. Still we must do everything again,
|
||
// if there was another backgroun process request, because
|
||
// it may have been saved before the current position.
|
||
//
|
||
|
||
pAdapterContext->BackgroundProcessRequests++;
|
||
|
||
if (pAdapterContext->BackgroundProcessRequests == 1) {
|
||
|
||
//
|
||
// repeat this as far as there are new tasks
|
||
//
|
||
|
||
do {
|
||
|
||
USHORT InitialRequestCount;
|
||
|
||
InitialRequestCount = pAdapterContext->BackgroundProcessRequests;
|
||
|
||
//
|
||
// This actually completes only link transmit, connect and
|
||
// disconnect commands. The connectionless frames
|
||
// are completed immediately when NDIS send completes.
|
||
// Usually several frames are acknowledged in the same time.
|
||
// Thus we create a local command list and execute
|
||
// its all completions with a single spin locking.
|
||
//
|
||
|
||
while (!IsListEmpty(&pAdapterContext->QueueCommands)) {
|
||
|
||
PLLC_PACKET pCommand;
|
||
|
||
pCommand = LlcRemoveHeadList(&pAdapterContext->QueueCommands);
|
||
|
||
RELEASE_SPIN_LOCK(&pAdapterContext->SendSpinLock);
|
||
|
||
pCommand->pBinding->pfCommandComplete(pCommand->pBinding->hClientContext,
|
||
pCommand->Data.Completion.hClientHandle,
|
||
pCommand
|
||
);
|
||
|
||
ACQUIRE_SPIN_LOCK(&pAdapterContext->SendSpinLock);
|
||
}
|
||
|
||
//
|
||
// indicate the queued events
|
||
//
|
||
|
||
while (!IsListEmpty(&pAdapterContext->QueueEvents)) {
|
||
|
||
PEVENT_PACKET pEvent;
|
||
|
||
pEvent = LlcRemoveHeadList(&pAdapterContext->QueueEvents);
|
||
|
||
RELEASE_SPIN_LOCK(&pAdapterContext->SendSpinLock);
|
||
|
||
pEvent->pBinding->pfEventIndication(pEvent->pBinding->hClientContext,
|
||
pEvent->hClientHandle,
|
||
pEvent->Event,
|
||
pEvent->pEventInformation,
|
||
pEvent->SecondaryInfo
|
||
);
|
||
|
||
ACQUIRE_SPIN_LOCK(&pAdapterContext->SendSpinLock);
|
||
|
||
DEALLOCATE_PACKET_LLC_PKT(pAdapterContext->hPacketPool, pEvent);
|
||
|
||
}
|
||
pAdapterContext->BackgroundProcessRequests -= InitialRequestCount;
|
||
|
||
} while (pAdapterContext->BackgroundProcessRequests > 0);
|
||
}
|
||
|
||
//
|
||
// also execute the send task if the send queue is not empty
|
||
//
|
||
|
||
pAdapterContext->LlcPacketInSendQueue = FALSE;
|
||
RunSendTaskAndUnlock(pAdapterContext);
|
||
}
|
||
|
||
|
||
//
|
||
// Background process entry for those, that don't
|
||
// want to play with SendSpinLock.
|
||
// We will execute the DPC taks on DPC level (hLockHandle = NULL),
|
||
// that's perfectly OK as far as the major send operations by
|
||
// LlcSendI and LlcSendU lower the IRQL level while they are sending
|
||
// (to allow the DPC processing when we doing a long string io or
|
||
// memory move to a slow ISA adapter)
|
||
//
|
||
|
||
VOID
|
||
BackgroundProcess(
|
||
IN PADAPTER_CONTEXT pAdapterContext
|
||
)
|
||
{
|
||
ACQUIRE_SPIN_LOCK(&pAdapterContext->SendSpinLock);
|
||
|
||
BackgroundProcessAndUnlock(pAdapterContext);
|
||
}
|
||
|
||
|
||
VOID
|
||
LlcNdisSendComplete(
|
||
IN PADAPTER_CONTEXT pAdapterContext,
|
||
IN PNDIS_PACKET pNdisPacket,
|
||
IN NDIS_STATUS NdisStatus
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
The routine handles NdisCompleteSend indication, it makes send
|
||
completed indication to upper protocol drivers if necessary and
|
||
executes the background process to find if there is any other
|
||
frames in the send queue.
|
||
This is usually called below the DPC level.
|
||
|
||
Arguments:
|
||
|
||
pAdapterContext - adapter context
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
|
||
{
|
||
//
|
||
// this function may be called from NDIS wrapper at DPC level or from
|
||
// SendNdisPacket() at passive level
|
||
//
|
||
|
||
ASSUME_IRQL(ANY_IRQL);
|
||
|
||
ACQUIRE_DRIVER_LOCK();
|
||
|
||
CompleteSendAndLock(pAdapterContext,
|
||
(PLLC_NDIS_PACKET)pNdisPacket,
|
||
NdisStatus
|
||
);
|
||
|
||
//
|
||
// Send command completion should not queue any command
|
||
// completions or events. The send queue is the only possiblity.
|
||
//
|
||
|
||
if (!IsListEmpty(&pAdapterContext->NextSendTask)) {
|
||
RunSendTaskAndUnlock(pAdapterContext);
|
||
} else {
|
||
|
||
RELEASE_SPIN_LOCK(&pAdapterContext->SendSpinLock);
|
||
|
||
}
|
||
|
||
RELEASE_DRIVER_LOCK();
|
||
|
||
#ifdef NDIS40
|
||
REFDEL(&pAdapterContext->AdapterRefCnt, 'dneS');
|
||
#endif // NDIS40
|
||
}
|
||
|
||
|
||
PLLC_PACKET
|
||
GetI_Packet(
|
||
IN PADAPTER_CONTEXT pAdapterContext
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Function does:
|
||
- selects the current link station in the queue and schedules
|
||
(round robin) the queues for the next send
|
||
- executes its send procedure
|
||
- initializes the data link packet for the I frame
|
||
|
||
Arguments:
|
||
|
||
pAdapterContext - adapter context
|
||
|
||
Return Value:
|
||
|
||
PLLC_PACKET
|
||
|
||
--*/
|
||
|
||
{
|
||
PDATA_LINK pLink;
|
||
PLLC_PACKET pPacket;
|
||
|
||
//
|
||
// search the link (three LLC queues linked together!)
|
||
//
|
||
|
||
pLink = (((PLLC_QUEUE)pAdapterContext->QueueI.ListHead.Flink)->pObject);
|
||
|
||
/*
|
||
This is probably just wasting of CPU cycles. Remove the comments,
|
||
if somebody has troubles because of this. Stop send process will
|
||
reschedule the queues in any way.
|
||
|
||
//
|
||
// We have a round robin scheduling for all main queues (U, I
|
||
// and expidited) and for all sending links within the I- queue.
|
||
// Select the next main task and the next sending link, if
|
||
// there is any. (Usually we have only one sending object)
|
||
//
|
||
|
||
ScheduleQueue(&pAdapterContext->NextSendTask);
|
||
ScheduleQueue(&pAdapterContext->QueueI.ListHead);
|
||
*/
|
||
|
||
//
|
||
// A resent packet may still not be completed by NDIS,
|
||
// a very, very bad things begin to happen, if we try
|
||
// to send packet again before it has been completed
|
||
// by NDIS (we may complete the same packet twice).
|
||
// The flag indicates, that the send process should be
|
||
// restarted.
|
||
//
|
||
|
||
if (((PLLC_PACKET)pLink->SendQueue.ListHead.Flink)->CompletionType & LLC_I_PACKET_PENDING_NDIS) {
|
||
((PLLC_PACKET)pLink->SendQueue.ListHead.Flink)->CompletionType |= LLC_I_PACKET_WAITING_NDIS;
|
||
StopSendProcess(pAdapterContext, pLink);
|
||
return NULL;
|
||
}
|
||
|
||
//
|
||
// move the next element in the send list to the list of unacknowledged packets
|
||
//
|
||
|
||
pPacket = LlcRemoveHeadList(&pLink->SendQueue.ListHead);
|
||
LlcInsertTailList(&pLink->SentQueue, pPacket);
|
||
|
||
if (IsListEmpty(&pLink->SendQueue.ListHead)) {
|
||
StopSendProcess(pAdapterContext, pLink);
|
||
}
|
||
|
||
//
|
||
// Copy SSAP and DSAP, reset the default stuff.
|
||
// Set the POLL bit if this is the last frame of the send window.
|
||
//
|
||
|
||
pPacket->Data.Xmit.LlcHeader.I.Dsap = pLink->Dsap;
|
||
pPacket->Data.Xmit.LlcHeader.I.Ssap = pLink->Ssap;
|
||
pPacket->Data.Xmit.LlcHeader.I.Ns = pLink->Vs;
|
||
pPacket->Data.Xmit.LlcHeader.I.Nr = pLink->Vr;
|
||
pPacket->CompletionType = LLC_I_PACKET;
|
||
|
||
//
|
||
// We should actually lock the link, but we cannot do it,
|
||
// because it is against the spin lock rules: SendSpinLock has already
|
||
// been acquired. But nothing terrible can happen: in the worst case
|
||
// pLink->Ir_Ct update is lost and we send an extra ack. All Vs updates
|
||
// are done behind SendSpinLock in any way and the timers are
|
||
// protected by the timer spin lock.
|
||
//
|
||
|
||
pLink->Vs += 2; // modulo 128 increment for 7 highest bit
|
||
|
||
// Update VsMax only if this is a new send.
|
||
// .... pLink->VsMax = pLink->Vs;
|
||
|
||
if( pLink->Va <= pLink->VsMax ){
|
||
if( pLink->VsMax < pLink->Vs ){
|
||
pLink->VsMax = pLink->Vs;
|
||
}else if( pLink->Vs < pLink->Va ){
|
||
pLink->VsMax = pLink->Vs;
|
||
}else{
|
||
// Don't change, we are resending.
|
||
}
|
||
}else{
|
||
if( pLink->Va < pLink->Vs ){
|
||
// Don't change, wrapping.
|
||
}else if( pLink->VsMax < pLink->Vs ){
|
||
pLink->VsMax = pLink->Vs;
|
||
}else{
|
||
// Don't change, we are resending.
|
||
}
|
||
}
|
||
|
||
|
||
|
||
//
|
||
// We are now sending the acknowledge, we can stop the ack timer
|
||
// if it has been running. T1 timer must be started or reinitialized
|
||
// and Ti must be stopped (as always when T1 is started).
|
||
//
|
||
|
||
if (pLink->T2.pNext != NULL) {
|
||
StopTimer(&pLink->T2);
|
||
}
|
||
if (pLink->Ti.pNext != NULL) {
|
||
StopTimer(&pLink->Ti);
|
||
}
|
||
|
||
//
|
||
// Normally send an I- frame as Command-0 (without the poll bit),
|
||
// but Command-Poll when the send window is full.
|
||
// BUT! we cannot resend the packets with the poll bit (what?)
|
||
//
|
||
|
||
if (pLink->Vs == (UCHAR)(pLink->Va + pLink->Ww)) {
|
||
|
||
//
|
||
// The send process must be stopped until we have got
|
||
// a response for this poll. THE SEND PROCESS MUST BE
|
||
// STOPPED BEFORE SendSpinLock IS OPENED. Otherwise
|
||
// simultaneous execution could send two polls, corrupt
|
||
// the send queues, etc.
|
||
//
|
||
|
||
pLink->Flags |= DLC_SEND_DISABLED;
|
||
StopSendProcess(pAdapterContext, pLink);
|
||
|
||
//
|
||
// IBM TR network architecture reference gives some hints how
|
||
// to prevent the looping between check and sending states,
|
||
// if link can send small S- frames, but not bigger data frames.
|
||
// Unfortunately they do not provide any working solution.
|
||
// They have described the problem on page 11-22 and in the
|
||
// T1 expiration handler of all sending states (different T1
|
||
// for sending and poll states in state machine). All Is_Ct stuff
|
||
// in the state machine is garbage, because the link sets the
|
||
// transmit window to 1 immediately after a failed xmit and enters
|
||
// to a check state after every retransmit => T1 timeout happens
|
||
// in the current check state, but P_Ct never expires, because
|
||
// the other side sends always S acknowledge and link returns
|
||
// to open state until the nexting retransmit (which action
|
||
// resets the P_Ct counter).
|
||
// I added this check to send process and the decrement of
|
||
// Is_Ct counter to all SEND_I_POLL actions => The link times out,
|
||
// when it cannot send I-frames even if S- exchange works.
|
||
//
|
||
|
||
if (pLink->Vp == pLink->Vs && pLink->Is_Ct == 0) {
|
||
|
||
//
|
||
// The same I- frame has been retransmitted too many times.
|
||
// We must shut down this link. This happen now, when
|
||
// we give T1 expired indication and and Is_Ct == 0.
|
||
//
|
||
|
||
RunStateMachineCommand(pLink, T1_Expired);
|
||
|
||
//
|
||
// We must (NDIS) complete the last packet now, because
|
||
// the data link protocol may have already cancelled it.
|
||
//
|
||
|
||
pPacket->CompletionType &= ~LLC_I_PACKET_PENDING_NDIS;
|
||
if (pPacket->CompletionType == LLC_I_PACKET_COMPLETE) {
|
||
LlcInsertTailList(&pAdapterContext->QueueCommands, pPacket);
|
||
}
|
||
|
||
//
|
||
// We must execute the background process from here,
|
||
// because the background process is not called from
|
||
// the send task
|
||
//
|
||
|
||
BackgroundProcessAndUnlock(pAdapterContext);
|
||
|
||
ACQUIRE_SPIN_LOCK(&pAdapterContext->SendSpinLock);
|
||
|
||
return NULL;
|
||
} else {
|
||
|
||
//
|
||
// This is a Command-Poll, set the flag and the current
|
||
// time stamp, whenever a new command poll was queued.
|
||
//
|
||
|
||
pLink->LastTimeWhenCmdPollWasSent = (USHORT)AbsoluteTime;
|
||
pLink->Flags |= DLC_WAITING_RESPONSE_TO_POLL;
|
||
|
||
pPacket->Data.Xmit.LlcHeader.I.Nr |= (UCHAR)LLC_I_S_POLL_FINAL;
|
||
RunStateMachineCommand(pLink, SEND_I_POLL);
|
||
}
|
||
} else {
|
||
pLink->Ir_Ct = pLink->N3;
|
||
if (pLink->T1.pNext == NULL) {
|
||
StartTimer(&pLink->T1);
|
||
}
|
||
}
|
||
return pPacket;
|
||
}
|
||
|
||
|
||
VOID
|
||
StartSendProcess(
|
||
IN PADAPTER_CONTEXT pAdapterContext,
|
||
IN PDATA_LINK pLink
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
The routine starts the send process of a data link station.
|
||
It links the data link send queue to the send
|
||
queue of all link stations and again that queue
|
||
to the main send queue.
|
||
THE QUEUES MUST BE SPIN LOCKED WHEN THIS IS CALLED!
|
||
|
||
Arguments:
|
||
|
||
pAdapterContext - adapter context
|
||
|
||
pLink -
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
|
||
{
|
||
//
|
||
// This procedure can be called when there is nothing to send,
|
||
// or when the send process is already running or when
|
||
// the link is not in a active state for send.
|
||
//
|
||
|
||
if (pLink->SendQueue.ListEntry.Flink == NULL
|
||
&& !(pLink->Flags & DLC_SEND_DISABLED)
|
||
&& !IsListEmpty(&pLink->SendQueue.ListHead)) {
|
||
|
||
//
|
||
// Link the queue to the active I send tasks of all links
|
||
//
|
||
|
||
LlcInsertTailList(&pAdapterContext->QueueI.ListHead,
|
||
&pLink->SendQueue.ListEntry
|
||
);
|
||
|
||
//
|
||
// Link first the queue of I send tasks to the generic main
|
||
// send task queue, if it has not yet been linked
|
||
//
|
||
|
||
if (pAdapterContext->QueueI.ListEntry.Flink == NULL) {
|
||
LlcInsertTailList(&pAdapterContext->NextSendTask,
|
||
&pAdapterContext->QueueI.ListEntry
|
||
);
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
//
|
||
// Procedure is a space saving version of the send process enabling
|
||
// for the state machine. It also reset any bits disabling the send.
|
||
// CALL THIS ONLY FROM THE STATE MACHINE!!!!
|
||
//
|
||
|
||
VOID
|
||
EnableSendProcess(
|
||
IN PDATA_LINK pLink
|
||
)
|
||
{
|
||
//
|
||
// reset the disabled send state
|
||
//
|
||
|
||
pLink->Flags &= ~DLC_SEND_DISABLED;
|
||
pLink->Gen.pAdapterContext->LlcPacketInSendQueue = TRUE;
|
||
StartSendProcess(pLink->Gen.pAdapterContext, pLink);
|
||
}
|
||
|
||
|
||
VOID
|
||
StopSendProcess(
|
||
IN PADAPTER_CONTEXT pAdapterContext,
|
||
IN PDATA_LINK pLink
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
|
||
The routine stops the send process of a data link station.
|
||
It unlinks the data link send queue from the send
|
||
queue of all link stations and again that queue
|
||
from the main send queue.
|
||
THE QUEUES MUST BE SPIN LOCKED WHEN THIS IS CALLED!
|
||
|
||
Arguments:
|
||
|
||
pAdapterContext - adapter context
|
||
|
||
pLink - data link object
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
|
||
{
|
||
//
|
||
// Do all this only if the send process is really running.
|
||
// The NULL pointer marks a list element as a disconnected,
|
||
// A non-empty I- queue of a link may be disconencted from
|
||
// the link station send queue of the adapter, if the link is
|
||
// not in a sending state. The same thing is true also on
|
||
// the next level.
|
||
//
|
||
|
||
if (pLink->SendQueue.ListEntry.Flink != NULL) {
|
||
|
||
//
|
||
// unlink the queue from the active I send tasks of all links
|
||
//
|
||
|
||
LlcRemoveEntryList(&pLink->SendQueue.ListEntry);
|
||
pLink->SendQueue.ListEntry.Flink = NULL;
|
||
|
||
//
|
||
// Unlink first the queue of all I send tasks from the
|
||
// generic main send task queue, if it is now empty.
|
||
//
|
||
|
||
if (IsListEmpty(&pAdapterContext->QueueI.ListHead)) {
|
||
LlcRemoveEntryList(&pAdapterContext->QueueI.ListEntry);
|
||
pAdapterContext->QueueI.ListEntry.Flink = NULL;
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
//
|
||
// Procedure is a space saving version of the send process disabling
|
||
// for the state machine.
|
||
//
|
||
|
||
VOID
|
||
DisableSendProcess(
|
||
IN PDATA_LINK pLink
|
||
)
|
||
{
|
||
//
|
||
// set the send state variable disabled
|
||
//
|
||
|
||
pLink->Flags |= DLC_SEND_DISABLED;
|
||
StopSendProcess(pLink->Gen.pAdapterContext, pLink);
|
||
}
|
||
|
||
|
||
PLLC_PACKET
|
||
BuildDirOrU_Packet(
|
||
IN PADAPTER_CONTEXT pAdapterContext
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Function selects the next packet from the queue of connectionless
|
||
frames (U, TEST, XID, DIX and Direct),
|
||
initilizes the LLC packet for the send.
|
||
|
||
Arguments:
|
||
|
||
pAdapterContext - adapter context
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
|
||
{
|
||
PLLC_PACKET pPacket;
|
||
|
||
//
|
||
// Take next element, select the next send queue and
|
||
// unlink the current queue, if this was the only packet left.
|
||
//
|
||
|
||
pPacket = LlcRemoveHeadList(&pAdapterContext->QueueDirAndU.ListHead);
|
||
|
||
/*
|
||
This is probably just wasting of CPU cycles. Remove the comments,
|
||
if somebody has troubles because of this.
|
||
|
||
ScheduleQueue(&pAdapterContext->NextSendTask);
|
||
*/
|
||
|
||
if (IsListEmpty(&pAdapterContext->QueueDirAndU.ListHead)) {
|
||
LlcRemoveEntryList(&pAdapterContext->QueueDirAndU.ListEntry);
|
||
pAdapterContext->QueueDirAndU.ListEntry.Flink = NULL;
|
||
}
|
||
return pPacket;
|
||
}
|
||
|
||
|
||
DLC_STATUS
|
||
SendLlcFrame(
|
||
IN PDATA_LINK pLink,
|
||
IN UCHAR LlcCommandId
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Function queues a Type 2 LLC S or U command frame.
|
||
The LLC command code includes also the command/response and
|
||
poll/final bits. That saves quite a lot space in the state machine,
|
||
because this function is called from very many places.
|
||
The code execution may also be faster because of this packing.
|
||
|
||
Arguments:
|
||
|
||
pLink - current data link station
|
||
LlcCommand - Packed LLC command (bit 0 is the Poll-Final bit,
|
||
bit 1 is the command/response and higher bits inlcude
|
||
the enumerated LLC command code.
|
||
|
||
Return Value:
|
||
|
||
DLC_STATUS
|
||
|
||
--*/
|
||
|
||
{
|
||
PLLC_PACKET pPacket;
|
||
PADAPTER_CONTEXT pAdapterContext = pLink->Gen.pAdapterContext;
|
||
|
||
ASSUME_IRQL(DISPATCH_LEVEL);
|
||
|
||
pPacket = ALLOCATE_PACKET_LLC_PKT(pAdapterContext->hPacketPool);
|
||
|
||
if (pPacket == NULL) {
|
||
|
||
//
|
||
// The non paged pool is empty, we must drop this
|
||
// frame and hope that the protocol can recover (or disconnect)
|
||
//
|
||
|
||
return DLC_STATUS_NO_MEMORY;
|
||
}
|
||
pPacket->InformationLength = 0;
|
||
pPacket->pBinding = NULL;
|
||
|
||
//
|
||
// Supervisory S commands (RR, RNR, REJ) consists 4 bytes and poll/final
|
||
// bit is in a different place. The unnumbered U commands are only 3
|
||
// bytes, but FRMR has some special data in the info field, that will be
|
||
// added also to the 'extended LLC header'. We must reserve some
|
||
// extra space for the FRMR data in the few NDIS packets!!!!!
|
||
//
|
||
|
||
if ((auchLlcCommands[LlcCommandId >> 2] & LLC_S_U_TYPE_MASK) == LLC_S_TYPE) {
|
||
|
||
//
|
||
// Build S frame
|
||
//
|
||
|
||
pPacket->Data.Xmit.LlcHeader.S.Command = auchLlcCommands[LlcCommandId >> 2];
|
||
|
||
#if(0)
|
||
if(pPacket->Data.Xmit.LlcHeader.S.Command == LLC_REJ)
|
||
{
|
||
DbgPrint("SendLlcFrame: REJ\n");
|
||
}
|
||
#endif
|
||
pPacket->Data.Xmit.pLlcObject = (PLLC_OBJECT)pLink;
|
||
pPacket->Data.Xmit.pLanHeader = pLink->auchLanHeader;
|
||
pPacket->Data.Xmit.LlcHeader.S.Dsap = pLink->Dsap;
|
||
pPacket->Data.Xmit.LlcHeader.S.Ssap = pLink->Ssap;
|
||
pPacket->CompletionType = LLC_I_PACKET_UNACKNOWLEDGED;
|
||
pPacket->cbLlcHeader = sizeof(LLC_S_HEADER); // 4
|
||
pPacket->Data.Xmit.LlcHeader.S.Nr = pLink->Vr | (LlcCommandId & (UCHAR)LLC_I_S_POLL_FINAL);
|
||
|
||
//
|
||
// Second bit is the LLC command flag, set it to the source SAP
|
||
//
|
||
|
||
if (!(LlcCommandId & 2)) {
|
||
pPacket->Data.Xmit.LlcHeader.S.Ssap |= LLC_SSAP_RESPONSE;
|
||
|
||
//
|
||
// We must have only one final response in LLC or NDIS
|
||
// send queues in any time. Thus we just discard any further
|
||
// final responses until the previous one is sent.
|
||
// This is a partial solution to the problem, when the
|
||
// the Elnkii send queue is totally hung because of overflowing
|
||
// packets.
|
||
//
|
||
|
||
if ((LlcCommandId & (UCHAR)LLC_I_S_POLL_FINAL)) {
|
||
// >>> SNA bug #9517 (NT bug #12907)
|
||
#if(0)
|
||
if (pLink->Flags & DLC_FINAL_RESPONSE_PENDING_IN_NDIS) {
|
||
|
||
DEALLOCATE_PACKET_LLC_PKT(pAdapterContext->hPacketPool, pPacket);
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
#endif
|
||
|
||
// Changed the if statment to ignore the Poll only if the link
|
||
// speed is 10MB (the unit of link speed measurement is 100 bps).
|
||
//
|
||
// Ignoring the Poll kills the DLC performance on 100MB ethernet
|
||
// (particularly on MP machines). The other end must time out (T1 timer)
|
||
// before it can send more data if we ignore the Poll here.
|
||
|
||
if ((pLink->Flags & DLC_FINAL_RESPONSE_PENDING_IN_NDIS) &&
|
||
pAdapterContext->LinkSpeed <= 100000) {
|
||
|
||
DEALLOCATE_PACKET_LLC_PKT(pAdapterContext->hPacketPool, pPacket);
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
// >>> SNA bug #9517
|
||
pLink->Flags |= DLC_FINAL_RESPONSE_PENDING_IN_NDIS;
|
||
}
|
||
} else if (LlcCommandId & (UCHAR)LLC_I_S_POLL_FINAL) {
|
||
|
||
//
|
||
// This is a Command-Poll, set the flag and the current
|
||
// time stamp, whenever a new command poll was queued
|
||
//
|
||
|
||
pLink->LastTimeWhenCmdPollWasSent = (USHORT)AbsoluteTime;
|
||
pLink->Flags |= DLC_WAITING_RESPONSE_TO_POLL;
|
||
}
|
||
|
||
//
|
||
// The last sent command/response is included in the DLC statistics
|
||
//
|
||
|
||
pLink->LastCmdOrRespSent = pPacket->Data.Xmit.LlcHeader.S.Command;
|
||
} else {
|
||
pPacket->Data.XmitU.Command = auchLlcCommands[LlcCommandId >> 2];
|
||
pPacket->Data.XmitU.Dsap = pLink->Dsap;
|
||
pPacket->Data.XmitU.Ssap = pLink->Ssap;
|
||
|
||
//
|
||
// Second bit is the LLC command flag, set it to the source SAP
|
||
//
|
||
|
||
if (!(LlcCommandId & 2)) {
|
||
pPacket->Data.XmitU.Ssap |= LLC_SSAP_RESPONSE;
|
||
}
|
||
|
||
//
|
||
// Build a U command frame (FRMR has some data!!!)
|
||
//
|
||
|
||
pPacket->cbLlcHeader = sizeof(LLC_U_HEADER); // 3
|
||
|
||
if (pPacket->Data.XmitU.Command == LLC_FRMR) {
|
||
pPacket->cbLlcHeader += sizeof(LLC_FRMR_INFORMATION);
|
||
pPacket->Data.Response.Info.Frmr = pLink->DlcStatus.FrmrData;
|
||
}
|
||
if (LlcCommandId & 1) {
|
||
pPacket->Data.XmitU.Command |= LLC_U_POLL_FINAL;
|
||
}
|
||
|
||
//
|
||
// U- commands (eg. UA response for DISC) may be sent after
|
||
// the link object has been deleted. This invalidates
|
||
// the lan header pointer => we must change all U- commands
|
||
// to response types. Null object handle prevents the
|
||
// the close process to cancel the packet, when the
|
||
// station is closed.
|
||
//
|
||
|
||
//
|
||
// RLF 05/09/94
|
||
//
|
||
// If the framing type stored in the link structure is unspecified then
|
||
// either this is an AUTO configured binding and we haven't worked out
|
||
// the type of framing to use, or this is not an AUTO configured binding.
|
||
// In this case, defer to the address translation stored in the binding.
|
||
// If the framing type is known, use it
|
||
//
|
||
|
||
if (pLink->FramingType == LLC_SEND_UNSPECIFIED) {
|
||
pPacket->Data.XmitU.TranslationType = (UCHAR)pLink->Gen.pLlcBinding->InternalAddressTranslation;
|
||
} else {
|
||
pPacket->Data.XmitU.TranslationType = (UCHAR)pLink->FramingType;
|
||
}
|
||
pPacket->CompletionType = LLC_U_COMMAND_RESPONSE;
|
||
|
||
pPacket->Data.XmitU.pLanHeader = (PUCHAR)ALLOCATE_PACKET_LLC_PKT(pAdapterContext->hPacketPool);
|
||
|
||
if (pPacket->Data.XmitU.pLanHeader == NULL) {
|
||
|
||
DEALLOCATE_PACKET_LLC_PKT(pAdapterContext->hPacketPool, pPacket);
|
||
|
||
return DLC_STATUS_NO_MEMORY;
|
||
}
|
||
|
||
LlcMemCpy(pPacket->Data.XmitU.pLanHeader,
|
||
pLink->auchLanHeader,
|
||
pLink->cbLanHeaderLength
|
||
);
|
||
|
||
//
|
||
// In the AUTO mode in ethernet we duplicate all
|
||
// TEST, XID and SABME packets and send them both in
|
||
// 802.3 and DIX formats.
|
||
//
|
||
|
||
//
|
||
// RLF 05/09/94
|
||
//
|
||
// Similarly, we duplicate DISC frames (since right now we don't
|
||
// keep per-destination frame state information)
|
||
//
|
||
|
||
if (((pPacket->Data.XmitU.Command & ~LLC_U_POLL_FINAL) == LLC_SABME)
|
||
// || ((pPacket->Data.XmitU.Command & ~LLC_U_POLL_FINAL) == LLC_DISC)
|
||
) {
|
||
CheckAndDuplicatePacket(
|
||
#if DBG
|
||
pAdapterContext,
|
||
#endif
|
||
pLink->Gen.pLlcBinding,
|
||
pPacket,
|
||
&pAdapterContext->QueueExpidited
|
||
);
|
||
}
|
||
|
||
//
|
||
// The last sent command/response is included in the DLC statistics
|
||
//
|
||
|
||
pLink->LastCmdOrRespSent = pPacket->Data.XmitU.Command;
|
||
}
|
||
|
||
LlcInsertTailList(&pAdapterContext->QueueExpidited.ListHead, pPacket);
|
||
|
||
//
|
||
// The S- frames must be sent immediately before any I- frames,
|
||
// because otherwise the sequential frames may have NRs in a
|
||
// wrong order => FRMR (that's why we insert the expidited
|
||
// queue to the head instead of the tail.
|
||
//
|
||
|
||
pAdapterContext->LlcPacketInSendQueue = TRUE;
|
||
if (pAdapterContext->QueueExpidited.ListEntry.Flink == NULL) {
|
||
LlcInsertHeadList(&pAdapterContext->NextSendTask,
|
||
&pAdapterContext->QueueExpidited.ListEntry
|
||
);
|
||
}
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
|
||
PLLC_PACKET
|
||
GetLlcCommandPacket(
|
||
IN PADAPTER_CONTEXT pAdapterContext
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Function selects the next LLC command from the expidited queue.
|
||
|
||
Arguments:
|
||
|
||
pAdapterContext - adapter context
|
||
|
||
Return Value:
|
||
|
||
PLLC_PACKET
|
||
|
||
--*/
|
||
|
||
{
|
||
PLLC_PACKET pPacket;
|
||
|
||
//
|
||
// Unlink the current task, if this was the only one left.
|
||
// We will send the expidited packets as far as there is any
|
||
//
|
||
|
||
pPacket = LlcRemoveHeadList(&pAdapterContext->QueueExpidited.ListHead);
|
||
if (pPacket->CompletionType == LLC_I_PACKET_UNACKNOWLEDGED) {
|
||
pPacket->CompletionType = LLC_I_PACKET;
|
||
}
|
||
if (IsListEmpty(&pAdapterContext->QueueExpidited.ListHead)) {
|
||
LlcRemoveEntryList(&pAdapterContext->QueueExpidited.ListEntry);
|
||
pAdapterContext->QueueExpidited.ListEntry.Flink = NULL;
|
||
}
|
||
return pPacket;
|
||
}
|
||
|
||
|
||
VOID
|
||
SendNdisPacket(
|
||
IN PADAPTER_CONTEXT pAdapterContext,
|
||
IN PLLC_PACKET pPacket
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Function builds NDIS packet. LAN and LLC headers can be
|
||
given separately to this routine. All NDIS packets
|
||
include a fixed MDL descriptor and buffer for the headers.
|
||
The actual data is linked after that header.
|
||
I would say, that this is a clever algorithm,
|
||
in this way we avoid quite well the supid NDIS packet management.
|
||
We have just a few (5) NDIS packets for each adapter.
|
||
|
||
The direct frames includes only the LAN header and MDL pointer are
|
||
linked directly to the packet
|
||
|
||
Steps:
|
||
|
||
1. Reset the NDIS packet
|
||
2. Get the frame translation type and initialize the completion
|
||
packet.
|
||
3. Build the LAN header into a small buffer in NDIS packet.
|
||
4. Copy optional LLC header behind it
|
||
5. Initialize NDIS packet for the send
|
||
6. Send the packet
|
||
7. if command not pending
|
||
- Complete the packet (if there was a non-null request handle)
|
||
- Link the NDIS packet back to the send queue.
|
||
|
||
Arguments:
|
||
|
||
pAdapterContext - NDIS adapter context
|
||
pPacket - generic LLC transmit packet used for all transmit types
|
||
|
||
Return Value:
|
||
|
||
NDIS_STATUS (status of NdisSend)
|
||
|
||
--*/
|
||
|
||
{
|
||
UCHAR LlcOffset;
|
||
PLLC_NDIS_PACKET pNdisPacket;
|
||
NDIS_STATUS Status = NDIS_STATUS_FAILURE;
|
||
|
||
ASSUME_IRQL(DISPATCH_LEVEL);
|
||
|
||
//
|
||
// Sometimes we must discard I-frame in GetI_Packet routine and
|
||
// return a NULL packet
|
||
//
|
||
|
||
if (pPacket == NULL) {
|
||
return;
|
||
}
|
||
|
||
|
||
//
|
||
// Allocate an NDIS packet from the pool and reset the private NDIS header!
|
||
//
|
||
|
||
pNdisPacket = PopFromList((PLLC_PACKET)pAdapterContext->pNdisPacketPool);
|
||
ResetNdisPacket(pNdisPacket);
|
||
|
||
//
|
||
// The internal LAN headers always have the correct address format. Only
|
||
// Dir and Type 1 LAN headers need to be swapped, because they are owned
|
||
// by users. The internal address swapping is defined by binding basis
|
||
// because some transports may want to use DIX\DLC Saps, and others just
|
||
// the normal 802.3 DLC
|
||
//
|
||
|
||
if (pPacket->CompletionType == LLC_I_PACKET) {
|
||
pNdisPacket->pMdl->Next = pPacket->Data.Xmit.pMdl;
|
||
ReferenceObject(pPacket->Data.Xmit.pLlcObject);
|
||
|
||
//
|
||
// Type 2 packets use always the LAN header of the link station
|
||
//
|
||
|
||
LlcMemCpy(pNdisPacket->auchLanHeader,
|
||
pPacket->Data.Xmit.pLanHeader,
|
||
LlcOffset = pPacket->Data.Xmit.pLlcObject->Link.cbLanHeaderLength
|
||
);
|
||
|
||
//
|
||
// Copy the LLC header as it is, the case set its offset
|
||
//
|
||
|
||
LlcMemCpy(&pNdisPacket->auchLanHeader[LlcOffset],
|
||
&pPacket->Data.Xmit.LlcHeader,
|
||
4
|
||
);
|
||
} else {
|
||
|
||
//
|
||
// We must increment the reference counter of an LLC object, when
|
||
// we give its pointer to NDIS queue (and increment it, when the
|
||
// command is complete)
|
||
//-------
|
||
// We need two references for each transmit, First caller (DLC module)
|
||
// must reference and dereference the object to keep it alive over
|
||
// the synchronous code path here we do it second time to keep the
|
||
// object alive, when it has pointers queued on NDIS
|
||
//
|
||
|
||
if (pPacket->CompletionType > LLC_MAX_RESPONSE_PACKET) {
|
||
pNdisPacket->pMdl->Next = pPacket->Data.Xmit.pMdl;
|
||
ReferenceObject(pPacket->Data.Xmit.pLlcObject);
|
||
} else if (pPacket->CompletionType > LLC_MIN_MDL_PACKET) {
|
||
pNdisPacket->pMdl->Next = pPacket->Data.Xmit.pMdl;
|
||
} else {
|
||
pNdisPacket->pMdl->Next = NULL;
|
||
}
|
||
|
||
//
|
||
// LLC_TYPE_1 packets have non-null binding, the internally
|
||
// sent packets (ie. XID and TEST frames) use the current
|
||
// internal default format (tr, ethernet or dix)
|
||
//
|
||
|
||
LlcOffset = CopyLanHeader(pPacket->Data.XmitU.TranslationType,
|
||
pPacket->Data.XmitU.pLanHeader,
|
||
pAdapterContext->NodeAddress,
|
||
pNdisPacket->auchLanHeader,
|
||
pAdapterContext->ConfigInfo.SwapAddressBits
|
||
);
|
||
LlcMemCpy(&pNdisPacket->auchLanHeader[LlcOffset],
|
||
&pPacket->Data.XmitU.Dsap,
|
||
pPacket->cbLlcHeader
|
||
);
|
||
}
|
||
pNdisPacket->pCompletionPacket = pPacket;
|
||
MmGetMdlByteCount(pNdisPacket->pMdl) = LlcOffset + pPacket->cbLlcHeader;
|
||
|
||
//
|
||
// We must set the lenth field of all 802.2 or DIX DLC Ethernet frames,
|
||
// BUT NOT FOR DIX ethernet types having 2 bytes long 'LLC header'
|
||
//
|
||
|
||
if ((pAdapterContext->NdisMedium == NdisMedium802_3) && (pPacket->cbLlcHeader != 2)) {
|
||
|
||
UINT InformationLength;
|
||
|
||
InformationLength = pPacket->cbLlcHeader + pPacket->InformationLength;
|
||
|
||
//
|
||
// The possible offets are 12 and 14 and LLC offsets are 14 and 17
|
||
//
|
||
|
||
pNdisPacket->auchLanHeader[(LlcOffset & 0xfe) - 2] = (UCHAR)(InformationLength >> 8);
|
||
pNdisPacket->auchLanHeader[(LlcOffset & 0xfe) - 1] = (UCHAR)InformationLength;
|
||
}
|
||
|
||
RELEASE_SPIN_LOCK(&pAdapterContext->SendSpinLock);
|
||
|
||
RELEASE_DRIVER_LOCK();
|
||
|
||
NdisChainBufferAtFront((PNDIS_PACKET)pNdisPacket, pNdisPacket->pMdl);
|
||
|
||
#if LLC_DBG
|
||
|
||
if (pNdisPacket->ReferenceCount != 0) {
|
||
DbgBreakPoint();
|
||
}
|
||
pNdisPacket->ReferenceCount++;
|
||
|
||
#endif
|
||
|
||
#ifdef LLC_LOSE_I_PACKETS
|
||
|
||
//
|
||
// This code tests the error recoverability of the LLC protocol.
|
||
// We randomly delete packets to check how the protocol recovers.
|
||
// We use current timer tick, running static and a table of random
|
||
// numbers.
|
||
//
|
||
|
||
if (pPacket->CompletionType == LLC_I_PACKET) {
|
||
|
||
static UINT i = 0;
|
||
|
||
//
|
||
// 2 % is high enough. With 20 percent its takes forever to
|
||
// send the data. We send all discarded packets to Richard =>
|
||
// we can see in the net which one packets were lost.
|
||
//
|
||
|
||
i++;
|
||
if (aRandom[(i % 1000)] <= (USHORT)DBG_ERROR_PERCENT(2)) {
|
||
if (pAdapterContext->NdisMedium == NdisMedium802_3) {
|
||
memcpy(pNdisPacket->auchLanHeader,
|
||
"\0FIRTH",
|
||
6
|
||
);
|
||
} else {
|
||
memcpy(&pNdisPacket->auchLanHeader[2],
|
||
"\0FIRTH",
|
||
6
|
||
);
|
||
}
|
||
}
|
||
}
|
||
|
||
#endif
|
||
|
||
#ifdef NDIS40
|
||
REFADD(&pAdapterContext->AdapterRefCnt, 'dneS');
|
||
|
||
if (InterlockedCompareExchange(
|
||
&pAdapterContext->BindState,
|
||
BIND_STATE_BOUND,
|
||
BIND_STATE_BOUND) == BIND_STATE_BOUND)
|
||
{
|
||
NdisSend(&Status,
|
||
pAdapterContext->NdisBindingHandle,
|
||
(PNDIS_PACKET)pNdisPacket
|
||
);
|
||
}
|
||
// Above reference is removed by LlcNdisSendComplete handler.
|
||
#endif // NDIS40
|
||
|
||
|
||
//
|
||
// Ndis may return a synchronous status!
|
||
//
|
||
|
||
if (Status != NDIS_STATUS_PENDING) {
|
||
LlcNdisSendComplete(pAdapterContext,
|
||
(PNDIS_PACKET)pNdisPacket,
|
||
Status
|
||
);
|
||
}
|
||
|
||
ACQUIRE_DRIVER_LOCK();
|
||
|
||
ACQUIRE_SPIN_LOCK(&pAdapterContext->SendSpinLock);
|
||
|
||
}
|
||
|
||
|
||
VOID
|
||
CompleteSendAndLock(
|
||
IN PADAPTER_CONTEXT pAdapterContext,
|
||
IN PLLC_NDIS_PACKET pNdisPacket,
|
||
IN NDIS_STATUS NdisStatus
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
The routines completes the connectionless packets and also the
|
||
I-frames if they have already acknowledged by the other side.
|
||
We will leave the send spinlock locked.
|
||
|
||
Arguments:
|
||
|
||
pAdapterContext - current adapter context
|
||
pNdisPacket - the NDIS packet used in teh send.
|
||
NdisStatus - the status of the send operation
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
|
||
{
|
||
PLLC_PACKET pPacket;
|
||
PLLC_OBJECT pLlcObject;
|
||
UCHAR CompletionType;
|
||
|
||
ASSUME_IRQL(DISPATCH_LEVEL);
|
||
|
||
DLC_TRACE( 'A' );
|
||
|
||
//
|
||
// Only the connectionless packets issued by user needs
|
||
// a command completion. I- frames are indicated when they
|
||
// are acknowledged by the remote link station.
|
||
//
|
||
|
||
pPacket = pNdisPacket->pCompletionPacket;
|
||
pLlcObject = pPacket->Data.Xmit.pLlcObject;
|
||
if ((CompletionType = pPacket->CompletionType) == LLC_TYPE_1_PACKET) {
|
||
|
||
DLC_TRACE( 'j' );
|
||
|
||
pPacket->Data.Completion.Status = NdisStatus;
|
||
pPacket->Data.Completion.CompletedCommand = LLC_SEND_COMPLETION;
|
||
pPacket->pBinding->pfCommandComplete(pPacket->pBinding->hClientContext,
|
||
pLlcObject->Gen.hClientHandle,
|
||
pPacket
|
||
);
|
||
}
|
||
|
||
//
|
||
// !!! DON'T TOUCH PACKET AFTER THE PREVIOUS PROCEDURE CALL
|
||
// (unless the packet type is different from Type 1)
|
||
//
|
||
|
||
ACQUIRE_SPIN_LOCK(&pAdapterContext->SendSpinLock);
|
||
|
||
#if LLC_DBG
|
||
|
||
pNdisPacket->ReferenceCount--;
|
||
if (pNdisPacket->ReferenceCount != 0) {
|
||
DbgBreakPoint();
|
||
}
|
||
|
||
#endif
|
||
|
||
PushToList((PLLC_PACKET)pAdapterContext->pNdisPacketPool, (PLLC_PACKET)pNdisPacket);
|
||
|
||
//
|
||
// We first complete the internal packets of the data link driver,
|
||
// that has no connection to the link objects.
|
||
//
|
||
|
||
if (CompletionType <= LLC_MAX_RESPONSE_PACKET) {
|
||
|
||
DLC_TRACE('l');
|
||
|
||
//
|
||
// XID and U- command reponses have allocated two packets.
|
||
// TEST reponses have allocated a non paged pool buffer
|
||
// and MDL for the echones frame (it might have been 17 kB)
|
||
//
|
||
|
||
switch(CompletionType) {
|
||
case LLC_XID_RESPONSE:
|
||
pAdapterContext->XidTestResponses--;
|
||
|
||
#if LLC_DBG
|
||
|
||
((PLLC_PACKET)pPacket->Data.Response.pLanHeader)->pNext = NULL;
|
||
|
||
#endif
|
||
|
||
DEALLOCATE_PACKET_LLC_PKT(pAdapterContext->hPacketPool, pPacket->Data.Response.pLanHeader);
|
||
|
||
break;
|
||
|
||
case LLC_U_COMMAND_RESPONSE:
|
||
|
||
#if LLC_DBG
|
||
|
||
//
|
||
// Break immediately, when we have sent a FRMR packet
|
||
//
|
||
|
||
if (pPacket->Data.Xmit.LlcHeader.U.Command == LLC_FRMR) {
|
||
DbgBreakPoint();
|
||
}
|
||
((PLLC_PACKET)pPacket->Data.Response.pLanHeader)->pNext = NULL;
|
||
|
||
#endif
|
||
|
||
DEALLOCATE_PACKET_LLC_PKT(pAdapterContext->hPacketPool, pPacket->Data.Response.pLanHeader);
|
||
|
||
break;
|
||
|
||
case LLC_TEST_RESPONSE:
|
||
pAdapterContext->XidTestResponses--;
|
||
|
||
//
|
||
// RLF 03/30/93
|
||
//
|
||
// The TEST response packet may have had 0 information field length,
|
||
// in which case the MDL will be NULL
|
||
//
|
||
|
||
if (pPacket->Data.Response.Info.Test.pMdl) {
|
||
IoFreeMdl(pPacket->Data.Response.Info.Test.pMdl);
|
||
}
|
||
FREE_MEMORY_ADAPTER(pPacket->Data.Response.pLanHeader);
|
||
break;
|
||
|
||
#if LLC_DBG
|
||
|
||
case LLC_DIX_DUPLICATE:
|
||
break;
|
||
|
||
default:
|
||
LlcInvalidObjectType();
|
||
break;
|
||
|
||
#endif
|
||
|
||
}
|
||
|
||
DEALLOCATE_PACKET_LLC_PKT(pAdapterContext->hPacketPool, pPacket);
|
||
|
||
} else {
|
||
|
||
//
|
||
// We use extra status bits to indicate, when I- packet has been both
|
||
// completed by NDIS and acknowledged by the other side of link
|
||
// connection. An I packet can be queued to the completion queue by
|
||
// the second quy (either state machine or SendCompletion handler)
|
||
// only when the first guy has completed its work.
|
||
// An I packet could be acknowledged by the other side before
|
||
// its completion is indicated by NDIS. Dlc Driver deallocates
|
||
// the packet immediately, when Llc driver completes the acknowledged
|
||
// packet => possible data corruption (if packet is reused before
|
||
// NDIS has completed it). This is probably not possible in a
|
||
// single processor NT- system, but very possible in multiprocessor
|
||
// NT or systems without a single level DPC queue (like OS/2 and DOS).
|
||
//
|
||
|
||
if (CompletionType != LLC_TYPE_1_PACKET) {
|
||
|
||
DLC_TRACE( 'k' );
|
||
|
||
//
|
||
// All packets allocated for S-type frames have null
|
||
// binding context. All the rest of packets must
|
||
// be I- completions.
|
||
//
|
||
|
||
if (pPacket->pBinding == NULL) {
|
||
|
||
//
|
||
// We cannot send a new final response before
|
||
// the previous one has been complete by NDIS.
|
||
//
|
||
|
||
if ((pPacket->Data.Xmit.LlcHeader.S.Nr & LLC_I_S_POLL_FINAL)
|
||
&& (pPacket->Data.Xmit.LlcHeader.S.Ssap & LLC_SSAP_RESPONSE)) {
|
||
pLlcObject->Link.Flags &= ~DLC_FINAL_RESPONSE_PENDING_IN_NDIS;
|
||
}
|
||
|
||
DEALLOCATE_PACKET_LLC_PKT(pAdapterContext->hPacketPool, pPacket);
|
||
|
||
} else {
|
||
pPacket->CompletionType &= ~LLC_I_PACKET_PENDING_NDIS;
|
||
|
||
//
|
||
// A packet cannot be resent before the previous send
|
||
// has been completed by NDIS. We have simply stopped
|
||
// the send process until the NDIS is completed here.
|
||
//
|
||
|
||
if (pPacket->CompletionType & LLC_I_PACKET_WAITING_NDIS) {
|
||
pPacket->CompletionType &= ~LLC_I_PACKET_WAITING_NDIS;
|
||
StartSendProcess(pAdapterContext, (PDATA_LINK)pLlcObject);
|
||
} else if (pPacket->CompletionType == LLC_I_PACKET_COMPLETE) {
|
||
|
||
//
|
||
// We don't care at all about the result of the
|
||
// NDIS send operation with the I-frames.
|
||
// If the other side has acknowledged the packet,
|
||
// it is OK. In that case we had to wait the send
|
||
// to complete, because an too early ack and
|
||
// command completion would have invalidated
|
||
// the pointer on NDIS.
|
||
//
|
||
|
||
LlcInsertTailList(&pAdapterContext->QueueCommands, pPacket);
|
||
BackgroundProcessWithinLock(pAdapterContext);
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
// Pending close commands of LLC object must wait until all its
|
||
// NDIS send commands have been completed.
|
||
// We must also indicate the completed send command.
|
||
// The same must be true, when we are cancelling transmit commands.
|
||
// The system crashes, if we remove a transmit command, that is
|
||
// not yet sent or it is just being sent by NDIS.
|
||
// => Dereference LlcObject when the ndis packet is complete,
|
||
// We must run the background process
|
||
//
|
||
|
||
pLlcObject->Gen.ReferenceCount--;
|
||
if (pLlcObject->Gen.ReferenceCount == 0) {
|
||
CompletePendingLlcCommand(pLlcObject);
|
||
BackgroundProcessWithinLock(pAdapterContext);
|
||
}
|
||
DLC_TRACE((UCHAR)pLlcObject->Gen.ReferenceCount);
|
||
}
|
||
}
|
||
|
||
|
||
VOID
|
||
RespondTestOrXid(
|
||
IN PADAPTER_CONTEXT pAdapterContext,
|
||
IN NDIS_HANDLE MacReceiveContext,
|
||
IN LLC_HEADER LlcHeader,
|
||
IN UINT SourceSap
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Function builds a response packet for the XID or TEST frame.
|
||
All TEST Commands are echoed directy back as responses.
|
||
802.2 XID header is the only supported XID command type.
|
||
|
||
Arguments:
|
||
|
||
pAdapterContext - current adapter context
|
||
MacReceiveContext - For NdisTransferData
|
||
LlcHeader - The received LLC header
|
||
SourceSap - current source SAP
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
|
||
{
|
||
PLLC_PACKET pPacket = NULL;
|
||
USHORT InfoFieldLength;
|
||
UINT BytesCopied;
|
||
NDIS_STATUS Status;
|
||
PMDL pTestMdl = NULL;
|
||
PUCHAR pBuffer = NULL;
|
||
|
||
ASSUME_IRQL(DISPATCH_LEVEL);
|
||
|
||
//
|
||
// Respond to a 802.2 XIDs and TESTs, and discard everything else
|
||
// Echo the TEST commands back with the same information field
|
||
// (but that's limited by our buffer capasity).
|
||
//
|
||
|
||
if ((LlcHeader.U.Command & ~LLC_U_POLL_FINAL) == LLC_TEST) {
|
||
|
||
//
|
||
// Echo the TEST frames back to the sender, but only, we do:
|
||
// 1. Allocate a buffer from the non-paged pool
|
||
// 2. Allocate a MDL for it
|
||
// 3. Transfer the data
|
||
//
|
||
|
||
if (pAdapterContext->cbPacketSize < (pAdapterContext->RcvLanHeaderLength + sizeof(LLC_U_HEADER)) ) {
|
||
return;
|
||
}
|
||
|
||
|
||
InfoFieldLength = (USHORT)(pAdapterContext->cbPacketSize
|
||
- (pAdapterContext->RcvLanHeaderLength
|
||
+ sizeof(LLC_U_HEADER)));
|
||
pBuffer = ALLOCATE_ZEROMEMORY_ADAPTER(pAdapterContext->cbPacketSize);
|
||
if (pBuffer == NULL) {
|
||
return;
|
||
}
|
||
|
||
//
|
||
// RLF 03/30/93
|
||
//
|
||
// There may be no data in the info field to transfer. In this case
|
||
// don't allocate a MDL
|
||
//
|
||
|
||
if (InfoFieldLength) {
|
||
pTestMdl = IoAllocateMdl(pBuffer
|
||
+ pAdapterContext->RcvLanHeaderLength
|
||
+ sizeof(LLC_U_HEADER),
|
||
InfoFieldLength,
|
||
FALSE,
|
||
FALSE,
|
||
NULL
|
||
);
|
||
if (pTestMdl == NULL) {
|
||
goto ProcedureErrorExit;
|
||
}
|
||
MmBuildMdlForNonPagedPool(pTestMdl);
|
||
|
||
//
|
||
// Copy the TEST data from NDIS to our buffer
|
||
//
|
||
|
||
ResetNdisPacket(&pAdapterContext->TransferDataPacket);
|
||
|
||
RELEASE_DRIVER_LOCK();
|
||
|
||
NdisChainBufferAtFront((PNDIS_PACKET)&pAdapterContext->TransferDataPacket, pTestMdl);
|
||
|
||
//
|
||
// ADAMBA - Removed pAdapterContext->RcvLanHeaderLength
|
||
// from ByteOffset (the fourth param).
|
||
//
|
||
|
||
NdisTransferData(&Status,
|
||
pAdapterContext->NdisBindingHandle,
|
||
MacReceiveContext,
|
||
sizeof(LLC_U_HEADER)
|
||
|
||
//
|
||
// RLF 05/09/94
|
||
//
|
||
// if we have received a DIX packet then the data
|
||
// starts 3 bytes from where NDIS thinks the start
|
||
// of non-header data is
|
||
//
|
||
// ASSUME: Only DIX frames have header length of
|
||
// 17 (i.e. on Ethernet)
|
||
//
|
||
// What about FDDI?
|
||
//
|
||
|
||
+ ((pAdapterContext->RcvLanHeaderLength == 17) ? 3 : 0),
|
||
InfoFieldLength,
|
||
(PNDIS_PACKET)&pAdapterContext->TransferDataPacket,
|
||
&BytesCopied
|
||
);
|
||
|
||
ACQUIRE_DRIVER_LOCK();
|
||
|
||
//
|
||
// We don't care if the transfer data is still pending,
|
||
// If very, very unlikely, that the received dma would
|
||
// write the data later, than a new transmit command
|
||
// would read the same data. BUT we cannot continue,
|
||
// if transfer data failed.
|
||
//
|
||
|
||
if ((Status != STATUS_SUCCESS) && (Status != STATUS_PENDING)) {
|
||
goto ProcedureErrorExit;
|
||
}
|
||
}
|
||
} else if (((LlcHeader.U.Command & ~LLC_U_POLL_FINAL) != LLC_XID)
|
||
|| (LlcHeader.auchRawBytes[3] != IEEE_802_XID_ID)) {
|
||
|
||
//
|
||
// This was not a IEEE 802.2 XID !!!
|
||
//
|
||
|
||
return;
|
||
}
|
||
|
||
//
|
||
// We have only a limited number reponse packets available
|
||
// for the XID and TEST responses. Thus we will
|
||
// drop many packets in a broadcast storms created by token-ring
|
||
// source routing bridges, that is
|
||
// actually a good thing. On the other hand we may
|
||
// also loose some packets that should have been reponsed,
|
||
// but who cares (this is a connectionless thing).
|
||
// (This is probably wasted effort, XID and TEST frames are not
|
||
// usually sent with the broadcast bit set).
|
||
//
|
||
|
||
ACQUIRE_SPIN_LOCK(&pAdapterContext->SendSpinLock);
|
||
|
||
if ((pAdapterContext->XidTestResponses < MAX_XID_TEST_RESPONSES)
|
||
&& ((pPacket = (PLLC_PACKET)ALLOCATE_PACKET_LLC_PKT(pAdapterContext->hPacketPool)) != NULL)) {
|
||
|
||
if ((LlcHeader.U.Command & ~LLC_U_POLL_FINAL) == LLC_XID) {
|
||
|
||
pPacket->Data.Xmit.pLanHeader = (PUCHAR)ALLOCATE_PACKET_LLC_PKT(pAdapterContext->hPacketPool);
|
||
|
||
if (pPacket->Data.Xmit.pLanHeader == NULL) {
|
||
|
||
DEALLOCATE_PACKET_LLC_PKT(pAdapterContext->hPacketPool, pPacket);
|
||
|
||
pPacket = NULL;
|
||
goto LockedErrorExit;
|
||
} else {
|
||
LlcMemCpy(&pPacket->Data.Response.Info,
|
||
&Ieee802Xid,
|
||
sizeof(Ieee802Xid)
|
||
);
|
||
pPacket->InformationLength = 0;
|
||
pPacket->cbLlcHeader = sizeof(Ieee802Xid) + sizeof(LLC_U_HEADER);
|
||
pPacket->CompletionType = LLC_XID_RESPONSE;
|
||
}
|
||
} else {
|
||
pPacket->Data.Xmit.pLanHeader = pBuffer;
|
||
pPacket->cbLlcHeader = sizeof(LLC_U_HEADER);
|
||
pPacket->CompletionType = LLC_TEST_RESPONSE;
|
||
pPacket->Data.Response.Info.Test.pMdl = pTestMdl;
|
||
pPacket->InformationLength = InfoFieldLength;
|
||
}
|
||
pAdapterContext->XidTestResponses++;
|
||
|
||
//
|
||
// The packet initialization is the same for XID and TEST
|
||
//
|
||
|
||
pPacket->Data.XmitU.Dsap = (UCHAR)(LlcHeader.U.Ssap & ~LLC_SSAP_RESPONSE);
|
||
pPacket->Data.XmitU.Ssap = (UCHAR)(SourceSap | LLC_SSAP_RESPONSE);
|
||
pPacket->Data.XmitU.Command = LlcHeader.U.Command;
|
||
|
||
if (pAdapterContext->NdisMedium == NdisMedium802_5) {
|
||
pPacket->Data.Response.TranslationType = LLC_SEND_802_5_TO_802_5;
|
||
} else if (pAdapterContext->NdisMedium == NdisMediumFddi) {
|
||
pPacket->Data.Response.TranslationType = LLC_SEND_FDDI_TO_FDDI;
|
||
} else if (pAdapterContext->RcvLanHeaderLength == 17) {
|
||
pPacket->Data.Response.TranslationType = LLC_SEND_802_3_TO_DIX;
|
||
} else {
|
||
pPacket->Data.Response.TranslationType = LLC_SEND_802_3_TO_802_3;
|
||
}
|
||
LlcBuildAddressFromLanHeader(pAdapterContext->NdisMedium,
|
||
pAdapterContext->pHeadBuf,
|
||
pPacket->Data.Xmit.pLanHeader
|
||
);
|
||
|
||
//
|
||
// Connect the packet to the send queues, we can use a subprocedure
|
||
// because this is not on the main code path
|
||
//
|
||
|
||
QueuePacket(pAdapterContext, &pAdapterContext->QueueDirAndU, pPacket);
|
||
|
||
//
|
||
// Request and send process execution from the receive indication
|
||
//
|
||
|
||
pAdapterContext->LlcPacketInSendQueue = TRUE;
|
||
}
|
||
|
||
LockedErrorExit:
|
||
|
||
RELEASE_SPIN_LOCK(&pAdapterContext->SendSpinLock);
|
||
|
||
ProcedureErrorExit:
|
||
|
||
if (pPacket == NULL) {
|
||
if (pBuffer) {
|
||
FREE_MEMORY_ADAPTER(pBuffer);
|
||
}
|
||
if (pTestMdl != NULL) {
|
||
IoFreeMdl(pTestMdl);
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
// The table maps all SAP send commands to the actual LLC commands
|
||
//
|
||
|
||
static struct {
|
||
UCHAR ResponseFlag;
|
||
UCHAR Command;
|
||
} Type1_Commands[LLC_LAST_FRAME_TYPE / 2] = {
|
||
{(UCHAR)-1, (UCHAR)-1},
|
||
{(UCHAR)-1, (UCHAR)-1},
|
||
{(UCHAR)-1, (UCHAR)-1},
|
||
{0, LLC_UI}, // UI command
|
||
{0, LLC_XID | LLC_U_POLL_FINAL}, // XID_COMMAND_POLL
|
||
{0, LLC_XID}, // XID_COMMAND_NOT_POLL
|
||
{LLC_SSAP_RESPONSE, LLC_XID | LLC_U_POLL_FINAL}, // XID_RESPONSE_FINAL
|
||
{LLC_SSAP_RESPONSE, LLC_XID}, // XID_RESPONSE_NOT_FINAL
|
||
{LLC_SSAP_RESPONSE, LLC_TEST | LLC_U_POLL_FINAL}, // TEST_RESPONSE_FINAL
|
||
{LLC_SSAP_RESPONSE, LLC_TEST}, // TEST_RESPONSE_NOT_FINAL
|
||
{(UCHAR)-1, (UCHAR)-1},
|
||
{0, LLC_TEST | LLC_U_POLL_FINAL} // TEST_RESPONSE_FINAL
|
||
};
|
||
|
||
|
||
VOID
|
||
LlcSendU(
|
||
IN PLLC_OBJECT pStation,
|
||
IN PLLC_PACKET pPacket,
|
||
IN UINT eFrameType,
|
||
IN UINT uDestinationSap
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Function sends the given network frame. and sets up
|
||
The frame may be a direct frame or Type 1 connectionless
|
||
frame (UI, XID or TEST).
|
||
|
||
First we build LLC (or ethernet type) header for the frame
|
||
and then we either send the packet directly or queue it
|
||
on data link.
|
||
|
||
Arguments:
|
||
|
||
pStation - Link, SAP or Direct station handle
|
||
pPacket - data link packet, also the completion handle for
|
||
the upper protocol.
|
||
eFrameType - the sent frame type
|
||
uDestinationSap - destination sap or dix ethernet type
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PADAPTER_CONTEXT pAdapterContext = pStation->Gen.pAdapterContext;
|
||
UINT Status;
|
||
|
||
ASSUME_IRQL(DISPATCH_LEVEL);
|
||
|
||
DLC_TRACE('U');
|
||
|
||
pPacket->pBinding = pStation->Gen.pLlcBinding;
|
||
pPacket->Data.Xmit.pLlcObject = pStation;
|
||
pPacket->CompletionType = LLC_TYPE_1_PACKET;
|
||
|
||
ACQUIRE_SPIN_LOCK(&pAdapterContext->SendSpinLock);
|
||
|
||
//
|
||
// Build LLC header for SAP stations, the direct stations do not have any
|
||
// LLC header
|
||
//
|
||
|
||
switch (pStation->Gen.ObjectType) {
|
||
case LLC_SAP_OBJECT:
|
||
pPacket->cbLlcHeader = sizeof(LLC_U_HEADER);
|
||
pPacket->Data.XmitU.TranslationType = (UCHAR)pStation->Gen.pLlcBinding->AddressTranslation;
|
||
pPacket->Data.XmitU.Dsap = (UCHAR)uDestinationSap;
|
||
pPacket->Data.XmitU.Ssap = (UCHAR)pStation->Sap.SourceSap;
|
||
pPacket->Data.XmitU.Ssap |= Type1_Commands[eFrameType >> 1].ResponseFlag;
|
||
pPacket->Data.XmitU.Command = Type1_Commands[eFrameType >> 1].Command;
|
||
|
||
//
|
||
// Do the UI- code path ASAP, then check TEST and XID special cases
|
||
//
|
||
|
||
if (pPacket->Data.XmitU.Command != LLC_UI) {
|
||
|
||
//
|
||
// Data link driver must build the DLC headers if it handles XID
|
||
// frames internally. In this case we use a constant XID info field
|
||
//
|
||
|
||
if ((pStation->Sap.OpenOptions & LLC_HANDLE_XID_COMMANDS)
|
||
&& ((eFrameType == LLC_XID_COMMAND_POLL)
|
||
|| (eFrameType == LLC_XID_COMMAND_NOT_POLL))) {
|
||
|
||
pPacket->Data.XmitU.pMdl = pXidMdl;
|
||
}
|
||
|
||
//
|
||
// duplicated TEST and XID frame responses are in a separate
|
||
// function since they're off the main code path. The code is also
|
||
// used in more than one place
|
||
//
|
||
|
||
Status = CheckAndDuplicatePacket(
|
||
#if DBG
|
||
pAdapterContext,
|
||
#endif
|
||
pStation->Gen.pLlcBinding,
|
||
pPacket,
|
||
&pAdapterContext->QueueDirAndU
|
||
);
|
||
if (Status != DLC_STATUS_SUCCESS) {
|
||
goto ErrorExit;
|
||
}
|
||
}
|
||
break;
|
||
|
||
case LLC_DIRECT_OBJECT:
|
||
|
||
//
|
||
// We must not send MAC frames to an ethernet network!!!
|
||
// Bit7 and bit6 in FC byte defines the frame type in token ring.
|
||
// 00 => MAC frame (no LLC), 01 => LLC, 10,11 => reserved.
|
||
// We send all other frames to direct except 01 (LLC)
|
||
//
|
||
|
||
if (pAdapterContext->NdisMedium != NdisMedium802_5
|
||
&& (pPacket->Data.XmitU.pLanHeader[1] & 0xC0) != 0x40) {
|
||
goto ErrorExit;
|
||
}
|
||
pPacket->Data.XmitU.TranslationType = (UCHAR)pStation->Gen.pLlcBinding->AddressTranslation;
|
||
pPacket->cbLlcHeader = 0;
|
||
break;
|
||
|
||
case LLC_DIX_OBJECT:
|
||
|
||
//
|
||
// Return error if we are sending DIX frames to a token-ring network.
|
||
// The DIX lan header is always in an ethernet format.
|
||
// (But lan headers for LLC and DIR frames are in token-ring
|
||
// format)
|
||
//
|
||
|
||
if (pAdapterContext->NdisMedium != NdisMedium802_3) {
|
||
Status = DLC_STATUS_UNAUTHORIZED_MAC;
|
||
goto ErrorExit;
|
||
}
|
||
pPacket->cbLlcHeader = 2;
|
||
pPacket->Data.XmitDix.TranslationType = LLC_SEND_DIX_TO_DIX;
|
||
pPacket->Data.XmitDix.EthernetTypeLowByte = (UCHAR)uDestinationSap;
|
||
pPacket->Data.XmitDix.EthernetTypeHighByte = (UCHAR)(uDestinationSap >> 8);
|
||
break;
|
||
|
||
#if LLC_DBG
|
||
default:
|
||
LlcInvalidObjectType();
|
||
break;
|
||
#endif
|
||
}
|
||
|
||
//
|
||
// Update the statistics, we may count the transmits as well here because
|
||
// the failed transmissions are not counted. This should be moved to
|
||
// SendComplete and be incremented only if STATUS_SUCCESS and if we counted
|
||
// only the successful transmits. I don't really know which one should be
|
||
// counted
|
||
//
|
||
|
||
pStation->Sap.Statistics.FramesTransmitted++;
|
||
|
||
LlcInsertTailList(&pAdapterContext->QueueDirAndU.ListHead, pPacket);
|
||
|
||
if (pAdapterContext->QueueDirAndU.ListEntry.Flink == NULL) {
|
||
LlcInsertTailList(&pAdapterContext->NextSendTask,
|
||
&pAdapterContext->QueueDirAndU.ListEntry
|
||
);
|
||
}
|
||
|
||
RunSendTaskAndUnlock(pAdapterContext);
|
||
return;
|
||
|
||
ErrorExit:
|
||
|
||
RELEASE_SPIN_LOCK(&pAdapterContext->SendSpinLock);
|
||
|
||
pPacket->Data.Completion.Status = Status;
|
||
pPacket->Data.Completion.CompletedCommand = LLC_SEND_COMPLETION;
|
||
pPacket->pBinding->pfCommandComplete(pPacket->pBinding->hClientContext,
|
||
pStation->Gen.hClientHandle,
|
||
pPacket
|
||
);
|
||
}
|
||
|
||
|
||
VOID
|
||
LlcSendI(
|
||
IN PDATA_LINK pStation,
|
||
IN PLLC_PACKET pPacket
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
The primitive implements a pure connection-oriented LLC Class II send.
|
||
It sends frame to the remote link station and
|
||
indicates the upper protocol when the data has been acknowledged.
|
||
The link station provides all address information and LLC header.
|
||
Function queues the given I packet to the queue and connects the
|
||
I- packet queue to the main send queue, if it has not
|
||
yet been connected.
|
||
|
||
Arguments:
|
||
|
||
pStation - link, sap or direct station handle
|
||
pPacket - data link packet, it is used also a request handle
|
||
to identify the command completion
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
ASSUME_IRQL(DISPATCH_LEVEL);
|
||
|
||
DLC_TRACE('I');
|
||
|
||
pPacket->pBinding = pStation->Gen.pLlcBinding;
|
||
pPacket->cbLlcHeader = sizeof(LLC_I_HEADER);
|
||
|
||
//
|
||
// We keep the acknowledge bit set, because it identifies that
|
||
// the packet is not yet in the NDIS queue
|
||
//
|
||
|
||
pPacket->CompletionType = LLC_I_PACKET_UNACKNOWLEDGED;
|
||
pPacket->Data.Xmit.pLlcObject = (PLLC_OBJECT)pStation;
|
||
pPacket->Data.Xmit.pLanHeader = pStation->auchLanHeader;
|
||
|
||
//
|
||
// We check the info field length for I- frames.
|
||
// All Type 1 frames are checked by the data link.
|
||
// Actually it checks also the I-frames, but
|
||
// data links do not care about the physical errors.
|
||
// It would disconnect the link after the too
|
||
// many retries.
|
||
//
|
||
|
||
if (pPacket->InformationLength > pStation->MaxIField) {
|
||
pPacket->Data.Completion.Status = DLC_STATUS_INVALID_FRAME_LENGTH;
|
||
pPacket->Data.Completion.CompletedCommand = LLC_SEND_COMPLETION;
|
||
pPacket->pBinding->pfCommandComplete(pPacket->pBinding->hClientContext,
|
||
pStation->Gen.hClientHandle,
|
||
pPacket
|
||
);
|
||
} else {
|
||
|
||
PADAPTER_CONTEXT pAdapterContext = pStation->Gen.pAdapterContext;
|
||
|
||
//
|
||
// We must do all queue handling inside the send spin lock. We also have
|
||
// to enable the send process and run the background process only when
|
||
// the send queue has been emptied
|
||
//
|
||
|
||
ACQUIRE_SPIN_LOCK(&pAdapterContext->SendSpinLock);
|
||
|
||
if (!(PrimaryStates[pStation->State] & LLC_LINK_OPENED)) {
|
||
|
||
RELEASE_SPIN_LOCK(&pAdapterContext->SendSpinLock);
|
||
|
||
//
|
||
// The 802.2 state machine may discard the data send request.
|
||
// It may also only queue the packet, but to keep the send process
|
||
// disabled
|
||
//
|
||
|
||
pPacket->Data.Completion.Status = DLC_STATUS_LINK_NOT_TRANSMITTING;
|
||
pPacket->Data.Completion.CompletedCommand = LLC_SEND_COMPLETION;
|
||
pPacket->pBinding->pfCommandComplete(pPacket->pBinding->hClientContext,
|
||
pStation->Gen.hClientHandle,
|
||
pPacket
|
||
);
|
||
} else {
|
||
LlcInsertTailList(&pStation->SendQueue.ListHead, pPacket);
|
||
|
||
if (pStation->SendQueue.ListEntry.Flink == NULL) {
|
||
StartSendProcess(pAdapterContext, pStation);
|
||
}
|
||
RunSendTaskAndUnlock(pAdapterContext);
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
VOID
|
||
QueuePacket(
|
||
IN PADAPTER_CONTEXT pAdapterContext,
|
||
IN PLLC_QUEUE pQueue,
|
||
IN PLLC_PACKET pPacket
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
The routines queues a packet to a queue and connects the
|
||
queue to the send tack list, if it was not connected.
|
||
This procedure is called from the non-timecritical code paths
|
||
just to save some extra code.
|
||
|
||
Arguments:
|
||
|
||
pAdapterContext - context of the data link adapter
|
||
pQueue - a special send queue structure
|
||
pPacket - transmit packet
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
|
||
{
|
||
LlcInsertTailList(&pQueue->ListHead, pPacket);
|
||
|
||
if (pQueue->ListEntry.Flink == NULL) {
|
||
LlcInsertTailList(&pAdapterContext->NextSendTask, &pQueue->ListEntry);
|
||
}
|
||
}
|
||
|
||
|
||
DLC_STATUS
|
||
CheckAndDuplicatePacket(
|
||
#if DBG
|
||
IN PADAPTER_CONTEXT pAdapterContext,
|
||
#endif
|
||
IN PBINDING_CONTEXT pBinding,
|
||
IN PLLC_PACKET pPacket,
|
||
IN PLLC_QUEUE pQueue
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
If determining the ethernet type dynamically, create a duplicate DIX frame
|
||
for a SABME or XID or TEST frame
|
||
|
||
Arguments:
|
||
|
||
pBindingContext - current data link binding context
|
||
pPacket - transmit packet
|
||
pQueue - a special send queue structure
|
||
|
||
Return Value:
|
||
|
||
DLC_STATUS
|
||
Success - DLC_STATUS_SUCCESS
|
||
Failure - DLC_STATUS_NO_MEMORY
|
||
|
||
--*/
|
||
|
||
{
|
||
PLLC_PACKET pNewPacket;
|
||
|
||
ASSUME_IRQL(DISPATCH_LEVEL);
|
||
|
||
if (pBinding->EthernetType == LLC_ETHERNET_TYPE_AUTO) {
|
||
|
||
pNewPacket = ALLOCATE_PACKET_LLC_PKT(pBinding->pAdapterContext->hPacketPool);
|
||
|
||
if (pNewPacket == NULL) {
|
||
return DLC_STATUS_NO_MEMORY;
|
||
} else {
|
||
|
||
*pNewPacket = *pPacket;
|
||
pNewPacket->pBinding = NULL;
|
||
pNewPacket->CompletionType = LLC_DIX_DUPLICATE;
|
||
|
||
//
|
||
// We always send first the 802.3 packet and then the DIX one.
|
||
// The new packet must be sent first, because it has no resources
|
||
// associated with it. Therefore we must change the type of the
|
||
// old packet
|
||
//
|
||
//
|
||
|
||
if (pPacket->Data.XmitU.TranslationType == LLC_SEND_802_5_TO_802_3) {
|
||
|
||
//
|
||
// token-ring -> dix
|
||
//
|
||
|
||
pPacket->Data.XmitU.TranslationType = LLC_SEND_802_5_TO_DIX;
|
||
} else if (pPacket->Data.XmitU.TranslationType == LLC_SEND_802_3_TO_802_3) {
|
||
|
||
//
|
||
// ethernet 802.3 -> dix
|
||
//
|
||
|
||
pPacket->Data.XmitU.TranslationType = LLC_SEND_802_3_TO_DIX;
|
||
}
|
||
QueuePacket(pBinding->pAdapterContext, pQueue, pNewPacket);
|
||
}
|
||
}
|
||
return DLC_STATUS_SUCCESS;
|
||
}
|
||
|
||
|
||
VOID
|
||
BackgroundProcessWithinLock(
|
||
IN PADAPTER_CONTEXT pAdapterContext
|
||
)
|
||
{
|
||
ASSUME_IRQL(DISPATCH_LEVEL);
|
||
|
||
BackgroundProcessAndUnlock(pAdapterContext);
|
||
|
||
ACQUIRE_SPIN_LOCK(&pAdapterContext->SendSpinLock);
|
||
}
|