123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773 |
- /*++
- Copyright (c) 2014 Minoca Corp. All Rights Reserved
- Module Name:
- sm91c1hw.c
- Abstract:
- This module implements device support for the SMSC91C111 LAN Ethernet
- Controller.
- Author:
- Chris Stevens 16-Apr-2014
- Environment:
- Kernel
- --*/
- //
- // ------------------------------------------------------------------- Includes
- //
- #include <minoca/kernel/driver.h>
- #include <minoca/net/netdrv.h>
- #include "sm91c1.h"
- //
- // --------------------------------------------------------------------- Macros
- //
- #define SM91C1_WRITE_ZERO_TO_MI(_Device) \
- Sm91c1pWriteRegister(_Device, Sm91c1RegisterManagementInterface, 0x8); \
- Sm91c1pWriteRegister(_Device, Sm91c1RegisterManagementInterface, 0xC); \
- Sm91c1pWriteRegister(_Device, Sm91c1RegisterManagementInterface, 0x8);
- #define SM91C1_WRITE_ONE_TO_MI(_Device) \
- Sm91c1pWriteRegister(_Device, Sm91c1RegisterManagementInterface, 0x9); \
- Sm91c1pWriteRegister(_Device, Sm91c1RegisterManagementInterface, 0xD); \
- Sm91c1pWriteRegister(_Device, Sm91c1RegisterManagementInterface, 0x9);
- #define SM91C1_WRITE_Z_TO_MI(_Device) \
- Sm91c1pWriteRegister(_Device, Sm91c1RegisterManagementInterface, 0x0); \
- Sm91c1pWriteRegister(_Device, Sm91c1RegisterManagementInterface, 0x4); \
- Sm91c1pWriteRegister(_Device, Sm91c1RegisterManagementInterface, 0x0);
- //
- // ---------------------------------------------------------------- Definitions
- //
- //
- // Define the maximum number of pending transfers allowed.
- //
- #define SM91C1_MAX_TRANSMIT_PACKET_LIST_COUNT 64
- //
- // ------------------------------------------------------ Data Type Definitions
- //
- //
- // ----------------------------------------------- Internal Function Prototypes
- //
- VOID
- Sm91c1pSendPacket (
- PSM91C1_DEVICE Device,
- PNET_PACKET_BUFFER Packet
- );
- VOID
- Sm91c1pReceivePacket (
- PSM91C1_DEVICE Device
- );
- VOID
- Sm91c1pInitializePhy (
- PSM91C1_DEVICE Device
- );
- KSTATUS
- Sm91c1pReadMacAddress (
- PSM91C1_DEVICE Device
- );
- USHORT
- Sm91c1pReadRegister (
- PSM91C1_DEVICE Device,
- SM91C1_REGISTER Register
- );
- VOID
- Sm91c1pWriteRegister (
- PSM91C1_DEVICE Device,
- SM91C1_REGISTER Register,
- USHORT Value
- );
- USHORT
- Sm91c1pReadMdio (
- PSM91C1_DEVICE Device,
- SM91C1_MII_REGISTER Register
- );
- VOID
- Sm91c1pWriteMdio (
- PSM91C1_DEVICE Device,
- SM91C1_MII_REGISTER Register,
- USHORT Value
- );
- VOID
- Sm91c1SynchronizeMdio (
- PSM91C1_DEVICE Device
- );
- //
- // -------------------------------------------------------------------- Globals
- //
- BOOL Sm91c1DisablePacketDropping = FALSE;
- // ------------------------------------------------------------------ Functions
- //
- KSTATUS
- Sm91c1Send (
- PVOID DeviceContext,
- PNET_PACKET_LIST PacketList
- )
- /*++
- Routine Description:
- This routine sends data through the network.
- Arguments:
- DeviceContext - Supplies a pointer to the device context associated with
- the link down which this data is to be sent.
- PacketList - Supplies a pointer to a list of network packets to send. Data
- in these packets may be modified by this routine, but must not be used
- once this routine returns.
- Return Value:
- STATUS_SUCCESS if all packets were sent.
- STATUS_RESOURCE_IN_USE if some or all of the packets were dropped due to
- the hardware being backed up with too many packets to send.
- Other failure codes indicate that none of the packets were sent.
- --*/
- {
- BOOL AllocatePacket;
- PSM91C1_DEVICE Device;
- USHORT InterruptMask;
- USHORT MmuCommand;
- UINTN PacketListCount;
- KSTATUS Status;
- ASSERT(KeGetRunLevel() == RunLevelLow);
- AllocatePacket = FALSE;
- Device = (PSM91C1_DEVICE)DeviceContext;
- //
- // If there is any room in the packet list (or dropping packets is
- // disabled), add all of the packets to the list waiting to be sent.
- //
- KeAcquireQueuedLock(Device->Lock);
- PacketListCount = Device->TransmitPacketList.Count;
- if ((PacketListCount < SM91C1_MAX_TRANSMIT_PACKET_LIST_COUNT) ||
- (Sm91c1DisablePacketDropping != FALSE)) {
- NET_APPEND_PACKET_LIST(PacketList, &(Device->TransmitPacketList));
- AllocatePacket = TRUE;
- Status = STATUS_SUCCESS;
- //
- // Otherwise report that the resource is use as it is too busy to handle
- // more packets.
- //
- } else {
- Status = STATUS_RESOURCE_IN_USE;
- }
- //
- // If necessary and an allocation isn't already in flight, allocate a
- // packet. The actual sending of a packet is handled when the allocate
- // interrupt is fired.
- //
- if ((AllocatePacket != FALSE) && (Device->AllocateInProgress == FALSE)) {
- Device->AllocateInProgress = TRUE;
- MmuCommand = (SM91C1_MMU_OPERATION_ALLOCATE_FOR_TRANSMIT <<
- SM91C1_MMU_COMMAND_OPERATION_SHIFT) &
- SM91C1_MMU_COMMAND_OPERATION_MASK;
- Sm91c1pWriteRegister(Device, Sm91c1RegisterMmuCommand, MmuCommand);
- //
- // Re-enable the allocation interrupt. Do this after the allocate
- // command is set because the previous allocate interrupt is not
- // cleared until a new allocate command is sent.
- //
- InterruptMask = Sm91c1pReadRegister(Device,
- Sm91c1RegisterInterruptMask);
- InterruptMask |= SM91C1_INTERRUPT_ALLOCATE;
- Sm91c1pWriteRegister(Device,
- Sm91c1RegisterInterruptMask,
- InterruptMask);
- }
- KeReleaseQueuedLock(Device->Lock);
- return Status;
- }
- KSTATUS
- Sm91c1GetSetInformation (
- PVOID DeviceContext,
- NET_LINK_INFORMATION_TYPE InformationType,
- PVOID Data,
- PUINTN DataSize,
- BOOL Set
- )
- /*++
- Routine Description:
- This routine gets or sets the network device layer's link information.
- Arguments:
- DeviceContext - Supplies a pointer to the device context associated with
- the link for which information is being set or queried.
- InformationType - Supplies the type of information being queried or set.
- Data - Supplies a pointer to the data buffer where the data is either
- returned for a get operation or given for a set operation.
- DataSize - Supplies a pointer that on input contains the size of the data
- buffer. On output, contains the required size of the data buffer.
- Set - Supplies a boolean indicating if this is a get operation (FALSE) or a
- set operation (TRUE).
- Return Value:
- Status code.
- --*/
- {
- PULONG Flags;
- KSTATUS Status;
- switch (InformationType) {
- case NetLinkInformationChecksumOffload:
- if (*DataSize != sizeof(ULONG)) {
- return STATUS_INVALID_PARAMETER;
- }
- if (Set != FALSE) {
- return STATUS_NOT_SUPPORTED;
- }
- Flags = (PULONG)Data;
- *Flags = 0;
- break;
- default:
- Status = STATUS_NOT_SUPPORTED;
- break;
- }
- return Status;
- }
- KSTATUS
- Sm91c1pInitializeDeviceStructures (
- PSM91C1_DEVICE Device
- )
- /*++
- Routine Description:
- This routine performs housekeeping preparation for resetting and enabling
- an SM91C1 device.
- Arguments:
- Device - Supplies a pointer to the device.
- Return Value:
- Status code.
- --*/
- {
- ULONG IoBufferFlags;
- KSTATUS Status;
- KeInitializeSpinLock(&(Device->InterruptLock));
- KeInitializeSpinLock(&(Device->BankLock));
- NET_INITIALIZE_PACKET_LIST(&(Device->TransmitPacketList));
- Device->SelectedBank = -1;
- ASSERT(Device->Lock == NULL);
- Device->Lock = KeCreateQueuedLock();
- if (Device->Lock == NULL) {
- Status = STATUS_INSUFFICIENT_RESOURCES;
- goto InitializeDeviceStructuresEnd;
- }
- IoBufferFlags = IO_BUFFER_FLAG_PHYSICALLY_CONTIGUOUS;
- Device->ReceiveIoBuffer = MmAllocateNonPagedIoBuffer(0,
- MAX_ULONGLONG,
- 0,
- SM91C1_MAX_PACKET_SIZE,
- IoBufferFlags);
- if (Device->ReceiveIoBuffer == NULL) {
- Status = STATUS_INSUFFICIENT_RESOURCES;
- goto InitializeDeviceStructuresEnd;
- }
- Status = STATUS_SUCCESS;
- InitializeDeviceStructuresEnd:
- return Status;
- }
- VOID
- Sm91c1pDestroyDeviceStructures (
- PSM91C1_DEVICE Device
- )
- /*++
- Routine Description:
- This routine performs destroy any device structures allocated for the
- SM91C1 device.
- Arguments:
- Device - Supplies a pointer to the device.
- Return Value:
- None.
- --*/
- {
- if (Device->ReceiveIoBuffer == NULL) {
- MmFreeIoBuffer(Device->ReceiveIoBuffer);
- }
- return;
- }
- KSTATUS
- Sm91c1pInitialize (
- PSM91C1_DEVICE Device
- )
- /*++
- Routine Description:
- This routine initializes and enables the SMSC91C1 device.
- Arguments:
- Device - Supplies a pointer to the device.
- Return Value:
- Status code.
- --*/
- {
- BOOL LinkUp;
- USHORT MmuCommand;
- KSTATUS Status;
- USHORT Value;
- //
- // Reset the device.
- //
- Sm91c1pWriteRegister(Device,
- Sm91c1RegisterReceiveControl,
- SM91C1_RECEIVE_CONTROL_SOFT_RESET);
- Sm91c1pWriteRegister(Device, Sm91c1RegisterReceiveControl, 0);
- //
- // Delay here to let the reset settle down.
- //
- KeDelayExecution(FALSE, FALSE, 50 * MICROSECONDS_PER_MILLISECOND);
- //
- // Disable all interrupts.
- //
- Sm91c1pWriteRegister(Device, Sm91c1RegisterInterruptMask, 0);
- //
- // Enable the power by setting the EPH Power Enable bit in the
- // configuration register.
- //
- Value = Sm91c1pReadRegister(Device, Sm91c1RegisterConfiguration);
- Value |= SM91C1_CONFIGURATION_REGISTER_EPH_POWER_ENABLE;
- Sm91c1pWriteRegister(Device, Sm91c1RegisterConfiguration, Value);
- //
- // Clear the power down bit in the PHY MII control register.
- //
- Value = Sm91c1pReadMdio(Device, Sm91c1MiiRegisterBasicControl);
- Value &= ~SM91C1_MII_BASIC_CONTROL_POWER_DOWN;
- Sm91c1pWriteMdio(Device, Sm91c1MiiRegisterBasicControl, Value);
- //
- // Reset the MMU.
- //
- MmuCommand = (SM91C1_MMU_OPERATION_RESET <<
- SM91C1_MMU_COMMAND_OPERATION_SHIFT) &
- SM91C1_MMU_COMMAND_OPERATION_MASK;
- Sm91c1pWriteRegister(Device, Sm91c1RegisterMmuCommand, MmuCommand);
- //
- // Initialize the PHY, starting auto-negotiation.
- //
- Sm91c1pInitializePhy(Device);
- //
- // Set the transmit packets to auto-release.
- //
- Value = Sm91c1pReadRegister(Device, Sm91c1RegisterControl);
- Value |= SM91C1_CONTROL_AUTO_RELEASE;
- Sm91c1pWriteRegister(Device, Sm91c1RegisterControl, Value);
- //
- // Enable transmitter by setting the TXENA bit in the Transmit Control
- // Register.
- //
- Sm91c1pWriteRegister(Device,
- Sm91c1RegisterTransmitControl,
- SM91C1_TRANSMIT_CONTROL_ENABLE);
- //
- // Enable the receiver by setting the RXENA bit in the Receive Control
- // Register.
- //
- Sm91c1pWriteRegister(Device,
- Sm91c1RegisterReceiveControl,
- SM91C1_RECEIVE_CONTROL_ENABLE);
- //
- // Get the MAC address out of the EEPROM.
- //
- Status = Sm91c1pReadMacAddress(Device);
- if (!KSUCCESS(Status)) {
- goto InitializeEnd;
- }
- //
- // Notify the networking core of this new link now that the device is ready
- // to send and receive data, pending media being present.
- //
- Status = Sm91c1pAddNetworkDevice(Device);
- if (!KSUCCESS(Status)) {
- goto InitializeEnd;
- }
- //
- // If the network link is up, notify networking core.
- //
- LinkUp = FALSE;
- Value = Sm91c1pReadRegister(Device, Sm91c1RegisterEphStatus);
- if ((Value & SM91C1_EPH_STATUS_LINK_OK) != 0) {
- LinkUp = TRUE;
- } else {
- Value = Sm91c1pReadMdio(Device, Sm91c1MiiRegisterBasicStatus);
- if (((Value & SM91C1_MII_BASIC_STATUS_LINK_STATUS) != 0) &&
- ((Value & SM91C1_MII_BASIC_STATUS_AUTONEGOTIATE_COMPLETE) != 0)) {
- LinkUp = TRUE;
- }
- }
- if (LinkUp != FALSE) {
- //
- // TODO: Get the real device speed when generic MII support is added.
- //
- NetSetLinkState(Device->NetworkLink, TRUE, NET_SPEED_100_MBPS);
- }
- //
- // Clear all the interrupts and then enable the desired ones.
- //
- Sm91c1pWriteRegister(Device, Sm91c1RegisterInterrupt, 0xFF);
- Sm91c1pWriteRegister(Device,
- Sm91c1RegisterInterruptMask,
- SM91C1_DEFAULT_INTERRUPTS);
- Status = STATUS_SUCCESS;
- InitializeEnd:
- return Status;
- }
- INTERRUPT_STATUS
- Sm91c1pInterruptService (
- PVOID Context
- )
- /*++
- Routine Description:
- This routine implements the SM91C1 interrupt service routine.
- Arguments:
- Context - Supplies the context pointer given to the system when the
- interrupt was connected. In this case, this points to the SM91c1 device
- structure.
- Return Value:
- Interrupt status.
- --*/
- {
- PSM91C1_DEVICE Device;
- USHORT Interrupts;
- USHORT InterruptsMask;
- INTERRUPT_STATUS InterruptStatus;
- USHORT PacketNumber;
- USHORT PhyInterrupts;
- Device = (PSM91C1_DEVICE)Context;
- InterruptStatus = InterruptStatusNotClaimed;
- PhyInterrupts = 0;
- //
- // Read the interrupt register.
- //
- Interrupts = Sm91c1pReadRegister(Device, Sm91c1RegisterInterrupt);
- InterruptsMask = Sm91c1pReadRegister(Device, Sm91c1RegisterInterruptMask);
- Interrupts &= InterruptsMask;
- if (Interrupts != 0) {
- KeAcquireSpinLock(&(Device->InterruptLock));
- //
- // If the MD interrupt bit is set, then gather the interrupt state from
- // the PHY MII. This read clears the interrupts as well.
- //
- if ((Interrupts & SM91C1_INTERRUPT_MD) != 0) {
- PhyInterrupts = Sm91c1pReadMdio(Device, Sm91c1MiiRegisterInterrupt);
- }
- //
- // The allocate interrupt remains high until the next interrupt
- // attempt. Unmask it now.
- //
- if ((Interrupts & SM91C1_INTERRUPT_ALLOCATE) != 0) {
- ASSERT(Device->AllocateInProgress != FALSE);
- InterruptsMask &= ~SM91C1_INTERRUPT_ALLOCATE;
- Sm91c1pWriteRegister(Device,
- Sm91c1RegisterInterruptMask,
- InterruptsMask);
- }
- //
- // The receive interrupt remains high until the receive FIFO is empty,
- // but only one receive interrupt can really be handled at a time.
- // Unmask it until it's handled.
- //
- if ((Interrupts & SM91C1_INTERRUPT_RECEIVE) != 0) {
- InterruptsMask &= ~SM91C1_INTERRUPT_RECEIVE;
- Sm91c1pWriteRegister(Device,
- Sm91c1RegisterInterruptMask,
- InterruptsMask);
- }
- //
- // The device is set to auto-release transmit packets. If a packet
- // interrupt is fired, that means there was a transmit failure. Save
- // the packet.
- //
- if ((Interrupts & SM91C1_INTERRUPT_TRANSMIT) != 0) {
- PacketNumber = Sm91c1pReadRegister(Device,
- Sm91c1RegisterTransmitFifo);
- ASSERT((PacketNumber & SM91C1_FIFO_PORTS_TRANSMIT_EMPTY) == 0);
- PacketNumber &= SM91C1_FIFO_PORTS_TRANSMIT_PACKET_NUMBER_MASK;
- }
- InterruptStatus = InterruptStatusClaimed;
- RtlAtomicOr32(&(Device->PendingInterrupts), Interrupts);
- RtlAtomicOr32(&(Device->PendingPhyInterrupts), PhyInterrupts);
- if ((Interrupts & SM91C1_INTERRUPT_TRANSMIT) != 0) {
- Device->PendingTransmitPacket = PacketNumber;
- }
- Device->PendingInterrupts |= Interrupts;
- Device->PendingPhyInterrupts |= PhyInterrupts;
- //
- // Clear the pending interrupt bits that can be acknowledged through
- // standard means.
- //
- Interrupts &= SM91C1_ACKNOWLEDGE_INTERRUPT_MASK;
- if (Interrupts != 0) {
- Sm91c1pWriteRegister(Device, Sm91c1RegisterInterrupt, Interrupts);
- }
- KeReleaseSpinLock(&(Device->InterruptLock));
- }
- return InterruptStatus;
- }
- INTERRUPT_STATUS
- Sm91c1pInterruptServiceWorker (
- PVOID Context
- )
- /*++
- Routine Description:
- This routine implements the SM91C1 low level interrupt service routine.
- Arguments:
- Context - Supplies the context pointer given to the system when the
- interrupt was connected. In this case, this points to the SM91c1 device
- structure.
- Return Value:
- Interrupt status.
- --*/
- {
- PSM91C1_DEVICE Device;
- PLIST_ENTRY FirstEntry;
- ULONG Interrupts;
- USHORT InterruptsMask;
- USHORT MmuCommand;
- RUNLEVEL OldRunLevel;
- PNET_PACKET_BUFFER Packet;
- USHORT PendingPacket;
- ULONG PhyInterrupts;
- USHORT PointerValue;
- USHORT StatusWord;
- USHORT Value;
- Device = (PSM91C1_DEVICE)Context;
- ASSERT(KeGetRunLevel() == RunLevelLow);
- //
- // Clear out the pending bits.
- //
- Interrupts = RtlAtomicExchange32(&(Device->PendingInterrupts), 0);
- PhyInterrupts = RtlAtomicExchange32(&(Device->PendingPhyInterrupts), 0);
- PendingPacket = Device->PendingTransmitPacket;
- if ((Interrupts == 0) && (PhyInterrupts == 0)) {
- return InterruptStatusNotClaimed;
- }
- ASSERT((PhyInterrupts == 0) ||
- ((Interrupts & SM91C1_INTERRUPT_MD) != 0));
- //
- // Handle link status changes.
- //
- if ((Interrupts & SM91C1_INTERRUPT_MD) != 0) {
- OldRunLevel = IoRaiseToInterruptRunLevel(Device->InterruptHandle);
- KeAcquireSpinLock(&(Device->InterruptLock));
- Value = Sm91c1pReadMdio(Device, Sm91c1MiiRegisterBasicStatus);
- KeReleaseSpinLock(&(Device->InterruptLock));
- KeLowerRunLevel(OldRunLevel);
- if ((Value & SM91C1_MII_BASIC_STATUS_LINK_STATUS) != 0) {
- if ((Value & SM91C1_MII_BASIC_STATUS_AUTONEGOTIATE_COMPLETE) != 0) {
- //
- // TODO: Get the real device speed when generic MII support is
- // added.
- //
- NetSetLinkState(Device->NetworkLink, TRUE, NET_SPEED_100_MBPS);
- }
- } else {
- NetSetLinkState(Device->NetworkLink, FALSE, 0);
- }
- }
- //
- // If the transmit interrupt was returned, check the transmit status.
- //
- if ((Interrupts & SM91C1_INTERRUPT_TRANSMIT) != 0 ) {
- KeAcquireQueuedLock(Device->Lock);
- Sm91c1pWriteRegister(Device, Sm91c1RegisterPacketNumber, PendingPacket);
- PointerValue = SM91C1_POINTER_READ |
- SM91C1_POINTER_AUTO_INCREMENT |
- SM91C1_POINTER_TRANSMIT;
- Sm91c1pWriteRegister(Device, Sm91c1RegisterPointer, PointerValue);
- StatusWord = Sm91c1pReadRegister(Device, Sm91c1RegisterData);
- //
- // Release the packet now that its status has been retrieved.
- //
- MmuCommand = (SM91C1_MMU_OPERATION_RELEASE_PACKET <<
- SM91C1_MMU_COMMAND_OPERATION_SHIFT) &
- SM91C1_MMU_COMMAND_OPERATION_MASK;
- Sm91c1pWriteRegister(Device, Sm91c1RegisterMmuCommand, MmuCommand);
- KeReleaseQueuedLock(Device->Lock);
- RtlDebugPrint("SM91C1: TX failed with status 0x%04x.\n", StatusWord);
- //
- // Re-enable transmission. It was disabled when the packet failed.
- //
- Sm91c1pWriteRegister(Device,
- Sm91c1RegisterTransmitControl,
- SM91C1_TRANSMIT_CONTROL_ENABLE);
- }
- //
- // If the receive interrupt was returned, process the data.
- //
- if ((Interrupts & SM91C1_INTERRUPT_RECEIVE) != 0) {
- Sm91c1pReceivePacket(Device);
- //
- // Re-enable the receive interrupt. It was disabled by the ISR.
- //
- InterruptsMask = Sm91c1pReadRegister(Device,
- Sm91c1RegisterInterruptMask);
- ASSERT((InterruptsMask & SM91C1_INTERRUPT_RECEIVE) == 0);
- InterruptsMask |= SM91C1_INTERRUPT_RECEIVE;
- Sm91c1pWriteRegister(Device,
- Sm91c1RegisterInterruptMask,
- InterruptsMask);
- }
- //
- // If a packet was allocated and there are packets to transmit, try to
- // send some data.
- //
- if ((Interrupts & SM91C1_INTERRUPT_ALLOCATE) != 0) {
- Packet = NULL;
- KeAcquireQueuedLock(Device->Lock);
- //
- // Send the first packet on the transmission list using the packet
- // that was allocated.
- //
- if (NET_PACKET_LIST_EMPTY(&(Device->TransmitPacketList)) == FALSE) {
- FirstEntry = Device->TransmitPacketList.Head.Next;
- Packet = LIST_VALUE(FirstEntry, NET_PACKET_BUFFER, ListEntry);
- NET_REMOVE_PACKET_FROM_LIST(Packet, &(Device->TransmitPacketList));
- Sm91c1pSendPacket(Device, Packet);
- }
- //
- // If the list is still not empty then allocate another packet.
- //
- if (NET_PACKET_LIST_EMPTY(&(Device->TransmitPacketList)) == FALSE) {
- ASSERT(Device->AllocateInProgress != FALSE);
- MmuCommand = (SM91C1_MMU_OPERATION_ALLOCATE_FOR_TRANSMIT <<
- SM91C1_MMU_COMMAND_OPERATION_SHIFT) &
- SM91C1_MMU_COMMAND_OPERATION_MASK;
- Sm91c1pWriteRegister(Device, Sm91c1RegisterMmuCommand, MmuCommand);
- //
- // Re-enable the allocation interrupt. Do this after the allocate
- // command is set because the previous allocate interrupt is not
- // cleared until a new allocate command is sent.
- //
- InterruptsMask = Sm91c1pReadRegister(Device,
- Sm91c1RegisterInterruptMask);
- ASSERT((InterruptsMask & SM91C1_INTERRUPT_ALLOCATE) == 0);
- InterruptsMask |= SM91C1_INTERRUPT_ALLOCATE;
- Sm91c1pWriteRegister(Device,
- Sm91c1RegisterInterruptMask,
- InterruptsMask);
- //
- // Otherwise note that no allocations are in progress, meaning that the
- // next send call should trigger an allocation.
- //
- } else {
- Device->AllocateInProgress = FALSE;
- }
- KeReleaseQueuedLock(Device->Lock);
- //
- // If a packet was transmitted, release it now.
- //
- if (Packet != NULL) {
- NetFreeBuffer(Packet);
- }
- }
- return InterruptStatusClaimed;
- }
- //
- // --------------------------------------------------------- Internal Functions
- //
- VOID
- Sm91c1pSendPacket (
- PSM91C1_DEVICE Device,
- PNET_PACKET_BUFFER Packet
- )
- /*++
- Routine Description:
- This routine sends the given packet using the packet sitting in the
- allocation result register. This routine assumes that the device spin lock
- is held.
- Arguments:
- Device - Supplies a pointer to the SM91C1 device that owns the packet.
- Packet - Supplies a pointer to the network packet to send.
- Return Value:
- None.
- --*/
- {
- BYTE AllocationResult;
- USHORT ByteCount;
- PUSHORT Data;
- ULONG DataSize;
- PBYTE Footer;
- PUSHORT Header;
- USHORT MmuCommand;
- BYTE PacketNumber;
- USHORT PointerValue;
- ASSERT(KeIsQueuedLockHeld(Device->Lock) != FALSE);
- //
- // There should be space in the packet for the header.
- //
- ASSERT(Packet->DataOffset == SM91C1_PACKET_HEADER_SIZE);
- //
- // Get the current data size.
- //
- DataSize = Packet->FooterOffset - Packet->DataOffset;
- ASSERT(DataSize == (USHORT)DataSize);
- Packet->DataOffset -= SM91C1_PACKET_HEADER_SIZE;
- Header = Packet->Buffer;
- //
- // Initialize the SM91c111 packet header. The first two bytes are the
- // status word. This gets set to 0. The second word is the byte count,
- // which includes the data size, the status word, the byte count word,
- // and the control word. The byte count is always even because any odd
- // byte in the data is included in the lower byte of the control word.
- //
- *Header = 0x0;
- ByteCount = DataSize +
- SM91C1_PACKET_HEADER_SIZE +
- SM91C1_PACKET_FOOTER_SIZE;
- *(Header + 1) = ALIGN_RANGE_DOWN(ByteCount, sizeof(USHORT));
- Footer = Packet->Buffer + Packet->FooterOffset;
- //
- // If the original byte count was odd, then the footer points at the
- // high byte of the control word. Set the ODD bit there. The low byte
- // of the control word correctly contains the last byte of data.
- //
- if ((ByteCount & 0x1) != 0) {
- *Footer = SM91C1_CONTROL_BYTE_ODD;
- Packet->FooterOffset += 1;
- //
- // Otherwise, the footer points at the low byte of the control word.
- // Zero the entire control word.
- //
- } else {
- *Footer = 0;
- *(Footer + 1) = 0;
- Packet->FooterOffset += 2;
- }
- //
- // Read the allocated packet from the allocation result register.
- //
- AllocationResult = Sm91c1pReadRegister(Device,
- Sm91c1RegisterAllocationResult);
- ASSERT((AllocationResult & SM91C1_ALLOCATION_RESULT_FAILED) == 0);
- PacketNumber = AllocationResult &
- SM91C1_ALLOCATION_RESULT_PACKET_NUMBER_MASK;
- //
- // Write the packet number to the packet number register.
- //
- Sm91c1pWriteRegister(Device, Sm91c1RegisterPacketNumber, PacketNumber);
- //
- // Initialize the pointer register for transmit, write, and auto-increment.
- //
- PointerValue = SM91C1_POINTER_WRITE |
- SM91C1_POINTER_AUTO_INCREMENT |
- SM91C1_POINTER_TRANSMIT;
- Sm91c1pWriteRegister(Device, Sm91c1RegisterPointer, PointerValue);
- //
- // Now write the packet data into the data register.
- //
- Data = Packet->Buffer;
- DataSize = Packet->FooterOffset - Packet->DataOffset;
- ASSERT(IS_ALIGNED(DataSize, sizeof(USHORT)) != FALSE);
- ASSERT(DataSize <= Packet->BufferSize);
- while (DataSize != 0) {
- Sm91c1pWriteRegister(Device, Sm91c1RegisterData, *Data);
- Data += 1;
- DataSize -= sizeof(USHORT);
- }
- //
- // Queue the packet. It will get automatically release once it is sent.
- //
- MmuCommand = (SM91C1_MMU_OPERATION_QUEUE_PACKET_FOR_TRANSMIT <<
- SM91C1_MMU_COMMAND_OPERATION_SHIFT) &
- SM91C1_MMU_COMMAND_OPERATION_MASK;
- Sm91c1pWriteRegister(Device, Sm91c1RegisterMmuCommand, MmuCommand);
- return;
- }
- VOID
- Sm91c1pReceivePacket (
- PSM91C1_DEVICE Device
- )
- /*++
- Routine Description:
- This routine handles receiving and processing a packet for the SM91c111 LAN
- Ethernet Controller.
- Arguments:
- Device - Supplies a pointer to the SM91C1 device.
- Return Value:
- None.
- --*/
- {
- USHORT ByteCount;
- USHORT BytesRemaining;
- BYTE ControlByte;
- USHORT ControlWord;
- PUSHORT Data;
- ULONG Index;
- USHORT MmuCommand;
- NET_PACKET_BUFFER Packet;
- USHORT PacketNumber;
- ULONG PacketSize;
- USHORT PointerValue;
- USHORT StatusWord;
- //
- // Read the packet number from the received FIFO.
- //
- PacketNumber = Sm91c1pReadRegister(Device, Sm91c1RegisterReceiveFifo);
- if ((PacketNumber & SM91C1_FIFO_PORTS_RECEIVE_EMPTY) != 0) {
- RtlDebugPrint("SM91C1: Receive interrupt lacks packet.\n");
- return;
- }
- //
- // Acquire the lock to protect access to the pointer and data registers.
- //
- KeAcquireQueuedLock(Device->Lock);
- //
- // Set the pointer register to receive, read, and auto-increment.
- //
- PointerValue = SM91C1_POINTER_READ |
- SM91C1_POINTER_AUTO_INCREMENT |
- SM91C1_POINTER_RECEIVE;
- Sm91c1pWriteRegister(Device, Sm91c1RegisterPointer, PointerValue);
- //
- // Read the status word.
- //
- StatusWord = Sm91c1pReadRegister(Device, Sm91c1RegisterData);
- //
- // Read the byte count and calculate the packet size. The byte count
- // contains the header, footer, and CRC size.
- //
- ByteCount = Sm91c1pReadRegister(Device, Sm91c1RegisterData);
- PacketSize = ByteCount -
- (SM91C1_PACKET_HEADER_SIZE +
- SM91C1_PACKET_FOOTER_SIZE +
- SM91C1_PACKET_CRC_SIZE);
- //
- // Read the data out of the data register and into the receive I/O buffer.
- //
- Data = Device->ReceiveIoBuffer->Fragment[0].VirtualAddress;
- BytesRemaining = PacketSize;
- ASSERT((BytesRemaining & 0x1) == 0);
- while (BytesRemaining != 0) {
- *Data = Sm91c1pReadRegister(Device, Sm91c1RegisterData);
- Data += 1;
- BytesRemaining -= 2;
- }
- //
- // Read the CRC.
- //
- for (Index = 0; Index < SM91C1_PACKET_CRC_SIZE; Index += sizeof(USHORT)) {
- Sm91c1pReadRegister(Device, Sm91c1RegisterData);
- }
- //
- // Read the control word. If the high byte (the control byte) indicates
- // that the packet has an odd length, the the low byte is the last byte of
- // data.
- //
- ControlWord = Sm91c1pReadRegister(Device, Sm91c1RegisterData);
- ControlByte = (BYTE)(ControlWord >> 8);
- if ((ControlByte & SM91C1_CONTROL_BYTE_ODD) != 0) {
- ASSERT((StatusWord & 0x1000) != 0);
- *Data = (BYTE)ControlWord;
- PacketSize += 1;
- }
- //
- // Relesae the lock as use of the data register is done. The receive buffer
- // is protected as there is only ever one receive in flight at a time.
- //
- KeReleaseQueuedLock(Device->Lock);
- //
- // Initialize the packet and notify the networking core.
- //
- Packet.Buffer = Device->ReceiveIoBuffer->Fragment[0].VirtualAddress;
- Packet.BufferPhysicalAddress =
- Device->ReceiveIoBuffer->Fragment[0].PhysicalAddress;
- Packet.IoBuffer = Device->ReceiveIoBuffer;
- Packet.Flags = 0;
- Packet.BufferSize = PacketSize;
- Packet.DataSize = PacketSize;
- Packet.DataOffset = 0;
- Packet.FooterOffset = PacketSize;
- NetProcessReceivedPacket(Device->NetworkLink, &Packet);
- //
- // Release the packet.
- //
- MmuCommand = (SM91C1_MMU_OPERATION_RECEiVE_FIFO_REMOVE_AND_RELEASE <<
- SM91C1_MMU_COMMAND_OPERATION_SHIFT) &
- SM91C1_MMU_COMMAND_OPERATION_MASK;
- Sm91c1pWriteRegister(Device, Sm91c1RegisterMmuCommand, MmuCommand);
- return;
- }
- VOID
- Sm91c1pInitializePhy (
- PSM91C1_DEVICE Device
- )
- /*++
- Routine Description:
- This routine initializes the PHY on the SMSC91C111.
- Arguments:
- Device - Supplies a pointer to the device.
- Return Value:
- None.
- --*/
- {
- USHORT PhyControl;
- ULONG Value;
- //
- // Enable auto-negotiation and set the LED state. LED A remains in the
- // default 10/100 link detected state and LED B gets set to full-duplex.
- //
- PhyControl = SM91C1_PHY_CONTROL_AUTONEGOTIATION |
- SM91C1_PHY_CONTROL_LED_SELECT_0B |
- SM91C1_PHY_CONTROL_LED_SELECT_1B;
- Sm91c1pWriteRegister(Device, Sm91c1RegisterPhyControl, PhyControl);
- //
- // Reset the PHY.
- //
- Sm91c1pWriteMdio(Device,
- Sm91c1MiiRegisterBasicControl,
- SM91C1_MII_BASIC_CONTROL_RESET);
- while (TRUE) {
- KeDelayExecution(FALSE, FALSE, 50 * MICROSECONDS_PER_MILLISECOND);
- Value = Sm91c1pReadMdio(Device, Sm91c1MiiRegisterBasicControl);
- if ((Value & SM91C1_MII_BASIC_CONTROL_RESET) == 0) {
- break;
- }
- }
- //
- // Start the auto-negotiation process.
- //
- Value = Sm91c1pReadMdio(Device, Sm91c1MiiRegisterBasicControl);
- Value |= SM91C1_MII_BASIC_CONTROL_ENABLE_AUTONEGOTIATION;
- Sm91c1pWriteMdio(Device, Sm91c1MiiRegisterBasicControl, Value);
- //
- // Read the interrupt status register to clear the bits.
- //
- Sm91c1pReadMdio(Device, Sm91c1MiiRegisterInterrupt);
- //
- // Write the interrupt mask.
- //
- Value = SM91C1_MII_INTERRUPT_STATUS_LINK_FAIL |
- SM91C1_MII_INTERRUPT_STATUS_INTERRUPT;
- Sm91c1pWriteMdio(Device, Sm91c1MiiRegisterInterruptMask, ~Value);
- return;
- }
- KSTATUS
- Sm91c1pReadMacAddress (
- PSM91C1_DEVICE Device
- )
- /*++
- Routine Description:
- This routine reads the MAC address out of the EEPROM on the SMSC91C1. The
- MAC address will be stored in the device structure.
- Arguments:
- Device - Supplies a pointer to the device.
- Return Value:
- Status code.
- --*/
- {
- SM91C1_REGISTER AddressRegister;
- ULONG Index;
- USHORT Value;
- //
- // Trigger a reload of the EEPROM values into the configuration, base, and
- // individual address registers. Do not set the EEPROM select bit and set
- // the RELOAD bit.
- //
- Value = Sm91c1pReadRegister(Device, Sm91c1RegisterControl);
- Value |= SM91C1_CONTROL_EEPROM_RELOAD;
- Sm91c1pWriteRegister(Device, Sm91c1RegisterControl, Value);
- //
- // Wait until the reload bit is cleared.
- //
- while (TRUE) {
- KeDelayExecution(FALSE, FALSE, 50 * MICROSECONDS_PER_MILLISECOND);
- Value = Sm91c1pReadRegister(Device, Sm91c1RegisterControl);
- if ((Value & SM91C1_CONTROL_EEPROM_RELOAD) == 0) {
- break;
- }
- }
- //
- // Now the MAC address should be filled into the individual address
- // registers. There is one byte in each but two can be read at a time as
- // they are sequential registers.
- //
- AddressRegister = Sm91c1RegisterIndividualAddress0;
- for (Index = 0; Index < sizeof(Device->MacAddress); Index += 1) {
- Value = Sm91c1pReadRegister(Device, AddressRegister);
- Device->MacAddress[Index] = (BYTE)Value;
- AddressRegister += 1;
- }
- //
- // Check to determine if this is a valid MAC address.
- //
- if (NetIsEthernetAddressValid(Device->MacAddress) == FALSE) {
- return STATUS_INVALID_ADDRESS;
- }
- return STATUS_SUCCESS;
- }
- USHORT
- Sm91c1pReadRegister (
- PSM91C1_DEVICE Device,
- SM91C1_REGISTER Register
- )
- /*++
- Routine Description:
- This routine reads from the specified register for the given SMSC91C1
- device.
- Arguments:
- Device - Supplies a pointer to the SMSC91C1 device whose register is to be
- read.
- Register - Supplies the register to read.
- Return Value:
- Returns the value of the register.
- --*/
- {
- BYTE Bank;
- BYTE ByteCount;
- BYTE Offset;
- RUNLEVEL OldRunLevel;
- USHORT Value;
- ASSERT((Sm91c1RegisterBankSelect & SM91C1_REGISTER_BANK_MASK) == 0);
- if (Device->InterruptHandle == INVALID_HANDLE) {
- OldRunLevel = KeRaiseRunLevel(RunLevelHigh);
- } else {
- OldRunLevel = IoRaiseToInterruptRunLevel(Device->InterruptHandle);
- }
- KeAcquireSpinLock(&(Device->BankLock));
- //
- // First select the correct bank. The bank register can always be accessed
- // from the currently selected bank.
- //
- Bank = (Register & SM91C1_REGISTER_BANK_MASK) >> SM91C1_REGISTER_BANK_SHIFT;
- if (Bank != Device->SelectedBank) {
- Offset = (Sm91c1RegisterBankSelect & SM91C1_REGISTER_OFFSET_MASK) >>
- SM91C1_REGISTER_OFFSET_SHIFT;
- ByteCount = (Sm91c1RegisterBankSelect &
- SM91C1_REGISTER_BYTE_COUNT_MASK) >>
- SM91C1_REGISTER_BYTE_COUNT_SHIFT;
- ASSERT(ByteCount == sizeof(USHORT));
- HlWriteRegister16(Device->ControllerBase + Offset, Bank);
- Device->SelectedBank = Bank;
- }
- //
- // Now read the register. Act according to the byte count.
- //
- ByteCount = (Register & SM91C1_REGISTER_BYTE_COUNT_MASK) >>
- SM91C1_REGISTER_BYTE_COUNT_SHIFT;
- Offset = (Register & SM91C1_REGISTER_OFFSET_MASK) >>
- SM91C1_REGISTER_OFFSET_SHIFT;
- if (ByteCount == sizeof(BYTE)) {
- Value = HlReadRegister8(Device->ControllerBase + Offset);
- } else {
- ASSERT(ByteCount == sizeof(USHORT));
- Value = HlReadRegister16(Device->ControllerBase + Offset);
- }
- KeReleaseSpinLock(&(Device->BankLock));
- KeLowerRunLevel(OldRunLevel);
- return Value;
- }
- VOID
- Sm91c1pWriteRegister (
- PSM91C1_DEVICE Device,
- SM91C1_REGISTER Register,
- USHORT Value
- )
- /*++
- Routine Description:
- This routine writes to the specified register for the given SMSC91C1
- device.
- Arguments:
- Device - Supplies a pointer to the SMSC91C1 device whose register is to be
- written.
- Register - Supplies the register to write.
- Value - Supplies the value to write to the given register.
- Return Value:
- Returns the value of the register.
- --*/
- {
- BYTE Bank;
- BYTE ByteCount;
- BYTE Offset;
- RUNLEVEL OldRunLevel;
- if (Device->InterruptHandle == INVALID_HANDLE) {
- OldRunLevel = KeRaiseRunLevel(RunLevelHigh);
- } else {
- OldRunLevel = IoRaiseToInterruptRunLevel(Device->InterruptHandle);
- }
- //
- // First select the correct bank, if necessary. The bank register can
- // always be accessed from the currently selected bank.
- //
- KeAcquireSpinLock(&(Device->BankLock));
- Bank = (Register & SM91C1_REGISTER_BANK_MASK) >> SM91C1_REGISTER_BANK_SHIFT;
- if (Bank != Device->SelectedBank) {
- Offset = (Sm91c1RegisterBankSelect & SM91C1_REGISTER_OFFSET_MASK) >>
- SM91C1_REGISTER_OFFSET_SHIFT;
- ByteCount = (Sm91c1RegisterBankSelect &
- SM91C1_REGISTER_BYTE_COUNT_MASK) >>
- SM91C1_REGISTER_BYTE_COUNT_SHIFT;
- ASSERT(ByteCount == sizeof(USHORT));
- HlWriteRegister16(Device->ControllerBase + Offset, Bank);
- Device->SelectedBank = Bank;
- }
- //
- // Now write the register. Act according to the byte count.
- //
- ByteCount = (Register & SM91C1_REGISTER_BYTE_COUNT_MASK) >>
- SM91C1_REGISTER_BYTE_COUNT_SHIFT;
- Offset = (Register & SM91C1_REGISTER_OFFSET_MASK) >>
- SM91C1_REGISTER_OFFSET_SHIFT;
- if (ByteCount == sizeof(BYTE)) {
- HlWriteRegister8(Device->ControllerBase + Offset, (BYTE)Value);
- } else {
- ASSERT(ByteCount == sizeof(USHORT));
- HlWriteRegister16(Device->ControllerBase + Offset, Value);
- }
- KeReleaseSpinLock(&(Device->BankLock));
- KeLowerRunLevel(OldRunLevel);
- return;
- }
- USHORT
- Sm91c1pReadMdio (
- PSM91C1_DEVICE Device,
- SM91C1_MII_REGISTER Register
- )
- /*++
- Routine Description:
- This routine performs an MDIO register read.
- Arguments:
- Device - Supplies a pointer to the SMSC91C1 device context for the read.
- Register - Supplies the SMSC91C1 MII register to read.
- Return Value:
- Returns the value of the MDO register.
- --*/
- {
- USHORT Data;
- ULONG Index;
- USHORT Value;
- //
- // Synchronize the MI to prepare for the start bits.
- //
- Sm91c1SynchronizeMdio(Device);
- //
- // Issue the start bits: a 0 followed by a 1.
- //
- SM91C1_WRITE_ZERO_TO_MI(Device);
- SM91C1_WRITE_ONE_TO_MI(Device);
- //
- // Issue a read command by writing a 1 followed by a 0.
- //
- SM91C1_WRITE_ONE_TO_MI(Device);
- SM91C1_WRITE_ZERO_TO_MI(Device);
- //
- // Write the PHY device address which is 00000.
- //
- for (Index = 0; Index < 5; Index += 1) {
- SM91C1_WRITE_ZERO_TO_MI(Device);
- }
- //
- // Write the MII register to read. Most significant bit first.
- //
- for (Index = 0; Index < 5; Index += 1) {
- if ((Register & 0x10) != 0) {
- SM91C1_WRITE_ONE_TO_MI(Device);
- } else {
- SM91C1_WRITE_ZERO_TO_MI(Device);
- }
- Register <<= 1;
- }
- //
- // Write Z for the turnaround time.
- //
- SM91C1_WRITE_Z_TO_MI(Device);
- //
- // Read the data bit by bit.
- //
- Data = 0;
- for (Index = 0; Index < 16; Index += 1) {
- Data <<= 1;
- Sm91c1pWriteRegister(Device, Sm91c1RegisterManagementInterface, 0x0);
- Sm91c1pWriteRegister(Device, Sm91c1RegisterManagementInterface, 0x4);
- Value = Sm91c1pReadRegister(Device, Sm91c1RegisterManagementInterface);
- Sm91c1pWriteRegister(Device, Sm91c1RegisterManagementInterface, 0x0);
- if ((Value & SM91C1_MANAGEMENT_INTERFACE_MII_MDI) != 0) {
- Data |= 0x1;
- }
- }
- //
- // Send the turnaround bit again.
- //
- SM91C1_WRITE_Z_TO_MI(Device);
- return Data;
- }
- VOID
- Sm91c1pWriteMdio (
- PSM91C1_DEVICE Device,
- SM91C1_MII_REGISTER Register,
- USHORT Value
- )
- /*++
- Routine Description:
- This routine performs a write to an MDIO register.
- Arguments:
- Device - Supplies a pointer to the SMSC91C1 device context for the write.
- Register - Supplies the SMSC91C1 MII register to write.
- Value - Supplies the value to be written to the MDIO register.
- Return Value:
- None.
- --*/
- {
- ULONG Index;
- //
- // Synchronize the MI to prepare for the start bits.
- //
- Sm91c1SynchronizeMdio(Device);
- //
- // Issue the start bits: a 0 followed by a 1.
- //
- SM91C1_WRITE_ZERO_TO_MI(Device);
- SM91C1_WRITE_ONE_TO_MI(Device);
- //
- // Issue a write command by writing a 0 followed by a 1.
- //
- SM91C1_WRITE_ZERO_TO_MI(Device);
- SM91C1_WRITE_ONE_TO_MI(Device);
- //
- // Write the PHY device address which is 00000.
- //
- for (Index = 0; Index < 5; Index += 1) {
- SM91C1_WRITE_ZERO_TO_MI(Device);
- }
- //
- // Write the MII register to read. Most significant bit first.
- //
- for (Index = 0; Index < 5; Index += 1) {
- if ((Register & 0x10) != 0) {
- SM91C1_WRITE_ONE_TO_MI(Device);
- } else {
- SM91C1_WRITE_ZERO_TO_MI(Device);
- }
- Register <<= 1;
- }
- //
- // Send the turnaround sequence: a 1 and then a 0.
- //
- SM91C1_WRITE_ONE_TO_MI(Device);
- SM91C1_WRITE_ZERO_TO_MI(Device);
- //
- // Write the data bit by bit, starting with the most significant bit.
- //
- for (Index = 0; Index < 16; Index += 1) {
- if ((Value & 0x8000) != 0) {
- SM91C1_WRITE_ONE_TO_MI(Device);
- } else {
- SM91C1_WRITE_ZERO_TO_MI(Device);
- }
- Value <<= 1;
- }
- //
- // Send the turnaround Z.
- //
- SM91C1_WRITE_Z_TO_MI(Device);
- return;
- }
- VOID
- Sm91c1SynchronizeMdio (
- PSM91C1_DEVICE Device
- )
- /*++
- Routine Description:
- This routine synchronizes the MDIO to prepare it for a register read or
- write.
- Arguments:
- Device - Supplies a pointer to the SMSC91C1 device context.
- Return Value:
- None.
- --*/
- {
- ULONG Index;
- //
- // Synchronize the MII by writing at least 32 ones.
- //
- for (Index = 0; Index < SM91C1_MII_SYNCHRONIZE_COUNT; Index += 1) {
- SM91C1_WRITE_ONE_TO_MI(Device);
- }
- return;
- }
|