1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
|
/****************************************************************************
** COPYRIGHT (C) 1994-1997 INTEL CORPORATION **
** DEVELOPED FOR MICROSOFT BY INTEL CORP., HILLSBORO, OREGON **
** HTTP://WWW.INTEL.COM/ **
** THIS FILE IS PART OF THE INTEL ETHEREXPRESS PRO/100B(TM) AND **
** ETHEREXPRESS PRO/100+(TM) NDIS 5.0 MINIPORT SAMPLE DRIVER **
****************************************************************************/
/****************************************************************************
Module Name:
routines.c
This driver runs on the following hardware:
- 82558 based PCI 10/100Mb ethernet adapters
(aka Intel EtherExpress(TM) PRO Adapters)
Environment:
Kernel Mode - Or whatever is the equivalent on WinNT
*****************************************************************************/
//#pragma TRACE_LEVEL_WARNING (disable: 4514 4706)
//-----------------------------------------------------------------------------
// Procedure: MdiWrite
//
// Description: This routine will write a value to the specified MII register
// of an external MDI compliant device (e.g. PHY 100). The
// command will execute in polled mode.
//
// Arguments:
// Adapter - ptr to Adapter object instance
// RegAddress - The MII register that we are writing to
// PhyAddress - The MDI address of the Phy component.
// DataValue - The value that we are writing to the MII register.
//
// Returns:
// NOTHING
//-----------------------------------------------------------------------------
#include "precomp.h"
#if defined(EVENT_TRACING)
#include "routines.tmh"
#endif
//-----------------------------------------------------------------------------
// Procedure: WaitScb
//
// Description: This routine checks to see if the D100 has accepted a command.
// It does so by checking the command field in the SCB, which will
// be zeroed by the D100 upon accepting a command. The loop waits
// for up to 600 milliseconds for command acceptance.
//
// Arguments:
// Adapter - ptr to Adapter object instance
//
// Returns:
// TRUE if the SCB cleared within 600 milliseconds.
// FALSE if it didn't clear within 600 milliseconds
//-----------------------------------------------------------------------------
__inline BOOLEAN
WaitScb(
IN PFDO_DATA FdoData
)
{
BOOLEAN bResult;
HW_CSR volatile *pCSRAddress = FdoData->CSRAddress;
MP_STALL_AND_WAIT(pCSRAddress->ScbCommandLow == 0, 600, bResult);
if(!bResult)
{
TraceEvents(TRACE_LEVEL_ERROR, DBG_HW_ACCESS, "WaitScb failed, ScbCommandLow=%x\n", pCSRAddress->ScbCommandLow);
if(pCSRAddress->ScbCommandLow != 0x80)
{
//ASSERT(FALSE);
}
MP_SET_HARDWARE_ERROR(FdoData);
}
return bResult;
}
VOID
MdiWrite(
IN PFDO_DATA Adapter,
IN ULONG RegAddress,
IN ULONG PhyAddress,
IN USHORT DataValue
)
{
BOOLEAN bResult;
// Issue the write command to the MDI control register.
Adapter->CSRAddress->MDIControl = (((ULONG) DataValue) |
(RegAddress << 16) |
(PhyAddress << 21) |
(MDI_WRITE << 26));
// wait 20usec before checking status
KeStallExecutionProcessor (20);
// wait 2 seconds for the mdi write to complete
MP_STALL_AND_WAIT(Adapter->CSRAddress->MDIControl & MDI_PHY_READY, 2000, bResult);
if (!bResult)
{
MP_SET_HARDWARE_ERROR(Adapter);
}
}
//-----------------------------------------------------------------------------
// Procedure: MdiRead
//
// Description: This routine will read a value from the specified MII register
// of an external MDI compliant device (e.g. PHY 100), and return
// it to the calling routine. The command will execute in polled
// mode.
//
// Arguments:
// Adapter - ptr to Adapter object instance
// RegAddress - The MII register that we are reading from
// PhyAddress - The MDI address of the Phy component.
// Recoverable - Whether the hardware TRACE_LEVEL_ERROR(if any)if recoverable or not
//
// Results:
// DataValue - The value that we read from the MII register.
//
// Returns:
// None
//-----------------------------------------------------------------------------
BOOLEAN
MdiRead(
IN PFDO_DATA Adapter,
IN ULONG RegAddress,
IN ULONG PhyAddress,
IN BOOLEAN Recoverable,
IN OUT PUSHORT DataValue
)
{
BOOLEAN bResult;
// Issue the read command to the MDI control register.
Adapter->CSRAddress->MDIControl = ((RegAddress << 16) |
(PhyAddress << 21) |
(MDI_READ << 26));
// wait 20usec before checking status
KeStallExecutionProcessor (20);
// Wait up to 2 seconds for the mdi read to complete
MP_STALL_AND_WAIT(Adapter->CSRAddress->MDIControl & MDI_PHY_READY, 2000, bResult);
if (!bResult)
{
if (!Recoverable)
{
MP_SET_NON_RECOVER_ERROR(Adapter);
}
MP_SET_HARDWARE_ERROR(Adapter);
return bResult;
}
*DataValue = (USHORT) Adapter->CSRAddress->MDIControl;
return bResult;
}
//-----------------------------------------------------------------------------
// Procedure: DumpStatsCounters
//
// Description: This routine will dump and reset the 82557's internal
// Statistics counters. The current stats dump values will be
// added to the "Adapter's" overall statistics.
// Arguments:
// Adapter - ptr to Adapter object instance
//
// Returns:
// NOTHING
//-----------------------------------------------------------------------------
VOID
DumpStatsCounters(
IN PFDO_DATA Adapter
)
{
BOOLEAN bResult;
//KIRQL oldIrql;
// The query is for a driver statistic, so we need to first
// update our statistics in software.
// clear the dump counters complete DWORD
Adapter->StatsCounters->CommandComplete = 0;
WdfSpinLockAcquire(Adapter->Lock);
// Dump and reset the hardware's statistic counters
D100IssueScbCommand(Adapter, SCB_CUC_DUMP_RST_STAT, TRUE);
// Restore the resume transmit software flag. After the dump counters
// command is issued, we should do a WaitSCB before issuing the next send.
Adapter->ResumeWait = TRUE;
WdfSpinLockRelease(Adapter->Lock);
// wait up to 2 seconds for the dump/reset to complete
MP_STALL_AND_WAIT(Adapter->StatsCounters->CommandComplete == 0xA007, 2000, bResult);
if (!bResult)
{
MP_SET_HARDWARE_ERROR(Adapter);
return;
}
// Output the debug counters to the debug terminal.
TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTLS, "Good Transmits %d\n", Adapter->StatsCounters->XmtGoodFrames);
TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTLS, "Good Receives %d\n", Adapter->StatsCounters->RcvGoodFrames);
TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTLS, "Max Collisions %d\n", Adapter->StatsCounters->XmtMaxCollisions);
TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTLS, "Late Collisions %d\n", Adapter->StatsCounters->XmtLateCollisions);
TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTLS, "Transmit Underruns %d\n", Adapter->StatsCounters->XmtUnderruns);
TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTLS, "Transmit Lost CRS %d\n", Adapter->StatsCounters->XmtLostCRS);
TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTLS, "Transmits Deferred %d\n", Adapter->StatsCounters->XmtDeferred);
TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTLS, "One Collision xmits %d\n", Adapter->StatsCounters->XmtSingleCollision);
TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTLS, "Mult Collision xmits %d\n", Adapter->StatsCounters->XmtMultCollisions);
TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTLS, "Total Collisions %d\n", Adapter->StatsCounters->XmtTotalCollisions);
TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTLS, "Receive CRC errors %d\n", Adapter->StatsCounters->RcvCrcErrors);
TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTLS, "Receive Alignment errors %d\n", Adapter->StatsCounters->RcvAlignmentErrors);
TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTLS, "Receive no resources %d\n", Adapter->StatsCounters->RcvResourceErrors);
TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTLS, "Receive overrun errors %d\n", Adapter->StatsCounters->RcvOverrunErrors);
TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTLS, "Receive CDT errors %d\n", Adapter->StatsCounters->RcvCdtErrors);
TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTLS, "Receive short frames %d\n", Adapter->StatsCounters->RcvShortFrames);
// update packet counts
Adapter->GoodTransmits += Adapter->StatsCounters->XmtGoodFrames;
Adapter->GoodReceives += Adapter->StatsCounters->RcvGoodFrames;
// update transmit TRACE_LEVEL_ERROR counts
Adapter->TxAbortExcessCollisions += Adapter->StatsCounters->XmtMaxCollisions;
Adapter->TxLateCollisions += Adapter->StatsCounters->XmtLateCollisions;
Adapter->TxDmaUnderrun += Adapter->StatsCounters->XmtUnderruns;
Adapter->TxLostCRS += Adapter->StatsCounters->XmtLostCRS;
Adapter->TxOKButDeferred += Adapter->StatsCounters->XmtDeferred;
Adapter->OneRetry += Adapter->StatsCounters->XmtSingleCollision;
Adapter->MoreThanOneRetry += Adapter->StatsCounters->XmtMultCollisions;
Adapter->TotalRetries += Adapter->StatsCounters->XmtTotalCollisions;
// update receive TRACE_LEVEL_ERROR counts
Adapter->RcvCrcErrors += Adapter->StatsCounters->RcvCrcErrors;
Adapter->RcvAlignmentErrors += Adapter->StatsCounters->RcvAlignmentErrors;
Adapter->RcvResourceErrors += Adapter->StatsCounters->RcvResourceErrors;
Adapter->RcvDmaOverrunErrors += Adapter->StatsCounters->RcvOverrunErrors;
Adapter->RcvCdtFrames += Adapter->StatsCounters->RcvCdtErrors;
Adapter->RcvRuntErrors += Adapter->StatsCounters->RcvShortFrames;
}
//-----------------------------------------------------------------------------
// Procedure: NICIssueSelectiveReset
//
// Description: This routine will issue a selective reset, forcing the adapter
// the CU and RU back into their idle states. The receive unit
// will then be re-enabled if it was previously enabled, because
// an RNR interrupt will be generated when we abort the RU.
//
// Arguments:
// Adapter - ptr to Adapter object instance
//
// Returns:
// NOTHING
//-----------------------------------------------------------------------------
VOID
NICIssueSelectiveReset(
PFDO_DATA Adapter
)
{
NTSTATUS status;
BOOLEAN bResult;
// Wait for the SCB to clear before we check the CU status.
if (!MP_TEST_FLAG(Adapter, fMP_ADAPTER_HARDWARE_ERROR))
{
WaitScb(Adapter);
}
// If we have issued any transmits, then the CU will either be active, or
// in the suspended state. If the CU is active, then we wait for it to be
// suspended. If the the CU is suspended, then we need to put the CU back
// into the idle state by issuing a selective reset.
if (Adapter->TransmitIdle == FALSE)
{
// Wait up to 2 seconds for suspended state
MP_STALL_AND_WAIT((Adapter->CSRAddress->ScbStatus & SCB_CUS_MASK) != SCB_CUS_ACTIVE, 2000, bResult)
if (!bResult)
{
MP_SET_HARDWARE_ERROR(Adapter);
}
// Check the current status of the receive unit
if ((Adapter->CSRAddress->ScbStatus & SCB_RUS_MASK) != SCB_RUS_IDLE)
{
// Issue an RU abort. Since an interrupt will be issued, the
// RU will be started by the DPC.
status = D100IssueScbCommand(Adapter, SCB_RUC_ABORT, TRUE);
}
// Issue a selective reset.
TraceEvents(TRACE_LEVEL_VERBOSE, DBG_HW_ACCESS, "CU suspended. ScbStatus=%04x Issue selective reset\n", Adapter->CSRAddress->ScbStatus);
Adapter->CSRAddress->Port = PORT_SELECTIVE_RESET;
// Wait after a port sel-reset command
KeStallExecutionProcessor (NIC_DELAY_POST_RESET);
// wait up to 2 ms for port command to complete
MP_STALL_AND_WAIT(Adapter->CSRAddress->Port == 0, 2, bResult)
if (!bResult)
{
MP_SET_HARDWARE_ERROR(Adapter);
}
// disable interrupts after issuing reset, because the int
// line gets raised when reset completes.
NICDisableInterrupt(Adapter);
// Restore the transmit software flags.
Adapter->TransmitIdle = TRUE;
Adapter->ResumeWait = TRUE;
}
}
VOID
NICIssueFullReset(
PFDO_DATA Adapter
)
{
BOOLEAN bResult;
NICIssueSelectiveReset(Adapter);
Adapter->CSRAddress->Port = PORT_SOFTWARE_RESET;
// wait up to 2 ms for port command to complete
MP_STALL_AND_WAIT(Adapter->CSRAddress->Port == 0, 2, bResult);
if (!bResult)
{
MP_SET_HARDWARE_ERROR(Adapter);
return;
}
NICDisableInterrupt(Adapter);
}
//-----------------------------------------------------------------------------
// Procedure: D100SubmitCommandBlockAndWait
//
// Description: This routine will submit a command block to be executed, and
// then it will wait for that command block to be executed. Since
// board ints will be disabled, we will ack the interrupt in
// this routine.
//
// Arguments:
// Adapter - ptr to Adapter object instance
//
// Returns:
// NDIS_STATUS_SUCCESS
// STATUS_DEVICE_DATA_ERROR
//-----------------------------------------------------------------------------
NTSTATUS
D100SubmitCommandBlockAndWait(
IN PFDO_DATA Adapter
)
{
NTSTATUS status;
BOOLEAN bResult;
// Points to the Non Tx Command Block.
NON_TRANSMIT_CB volatile *CommandBlock = Adapter->NonTxCmdBlock;
// Set the Command Block to be the last command block
CommandBlock->NonTxCb.Config.ConfigCBHeader.CbCommand |= CB_EL_BIT;
// Clear the status of the command block
CommandBlock->NonTxCb.Config.ConfigCBHeader.CbStatus = 0;
#if DBG
// Don't try to start the CU if the command unit is active.
if ((Adapter->CSRAddress->ScbStatus & SCB_CUS_MASK) == SCB_CUS_ACTIVE)
{
TraceEvents(TRACE_LEVEL_ERROR, DBG_HW_ACCESS, "Scb %p ScbStatus %04x\n", Adapter->CSRAddress, Adapter->CSRAddress->ScbStatus);
ASSERT(FALSE);
MP_SET_HARDWARE_ERROR(Adapter);
return(STATUS_DEVICE_DATA_ERROR);
}
#endif
// Start the command unit.
D100IssueScbCommand(Adapter, SCB_CUC_START, FALSE);
// Wait for the SCB to clear, indicating the completion of the command.
if (!WaitScb(Adapter))
{
return(STATUS_DEVICE_DATA_ERROR);
}
// Wait for some status, timeout value 3 secs
MP_STALL_AND_WAIT(CommandBlock->NonTxCb.Config.ConfigCBHeader.CbStatus & CB_STATUS_COMPLETE, 3000, bResult);
if (!bResult)
{
MP_SET_HARDWARE_ERROR(Adapter);
return(STATUS_DEVICE_DATA_ERROR);
}
// Ack any interrupts
if (Adapter->CSRAddress->ScbStatus & SCB_ACK_MASK)
{
// Ack all pending interrupts now
Adapter->CSRAddress->ScbStatus &= SCB_ACK_MASK;
}
// Check the status of the command, and if the command failed return FALSE,
// otherwise return TRUE.
if (!(CommandBlock->NonTxCb.Config.ConfigCBHeader.CbStatus & CB_STATUS_OK))
{
TraceEvents(TRACE_LEVEL_ERROR, DBG_HW_ACCESS, "Command failed\n");
MP_SET_HARDWARE_ERROR(Adapter);
status = STATUS_DEVICE_DATA_ERROR;
}
else
status = STATUS_SUCCESS;
return(status);
}
//-----------------------------------------------------------------------------
// Procedure: GetConnectionStatus
//
// Description: This function returns the connection status that is
// a required indication for PC 97 specification from MS
// the value we are looking for is if there is link to the
// wire or not.
//
// Arguments: IN Adapter structure pointer
//
// Returns: NdisMediaStateConnected
// NdisMediaStateDisconnected
//-----------------------------------------------------------------------------
MEDIA_STATE
GetMediaState(
IN PFDO_DATA Adapter
)
{
USHORT MdiStatusReg = 0;
BOOLEAN bResult1;
BOOLEAN bResult2;
// Read the status register at phy 1
bResult1 = MdiRead(Adapter, MDI_STATUS_REG, Adapter->PhyAddress, TRUE, &MdiStatusReg);
bResult2 = MdiRead(Adapter, MDI_STATUS_REG, Adapter->PhyAddress, TRUE, &MdiStatusReg);
// if there is hardware failure, or let the state remains the same
if (!bResult1 || !bResult2)
{
return Adapter->MediaState;
}
if (MdiStatusReg & MDI_SR_LINK_STATUS)
return(Connected);
else
return(Disconnected);
}
|