📄 smsc.c
字号:
#include <stdio.h>#include <sys/timer.h>#include "utils.h"#include "uart.h"#include "smscregs.h"#include "smsc.h"/*! * \brief Select specified PHY register for reading or writing. * * \param reg PHY register number. * \param we Should be 1 for write access, 0 for read access. * * \return Contents of the PHY interface rgister. */static u_char NicPhyRegSelect(u_char reg, u_char we){ u_char rs; u_char msk; u_char i; nic_bs(3); rs = (nic_inlb(NIC_MGMT) & ~(MGMT_MCLK | MGMT_MDO)) | MGMT_MDOE; /* Send idle pattern. */ for (i = 0; i < 33; i++) { nic_outlb(NIC_MGMT, rs | MGMT_MDO); nic_outlb(NIC_MGMT, rs | MGMT_MDO | MGMT_MCLK); } /* Send start sequence. */ nic_outlb(NIC_MGMT, rs); nic_outlb(NIC_MGMT, rs | MGMT_MCLK); nic_outlb(NIC_MGMT, rs | MGMT_MDO); nic_outlb(NIC_MGMT, rs | MGMT_MDO | MGMT_MCLK); /* Write or read mode. */ if (we) { nic_outlb(NIC_MGMT, rs); nic_outlb(NIC_MGMT, rs | MGMT_MCLK); nic_outlb(NIC_MGMT, rs | MGMT_MDO); nic_outlb(NIC_MGMT, rs | MGMT_MDO | MGMT_MCLK); } else { nic_outlb(NIC_MGMT, rs | MGMT_MDO); nic_outlb(NIC_MGMT, rs | MGMT_MDO | MGMT_MCLK); nic_outlb(NIC_MGMT, rs); nic_outlb(NIC_MGMT, rs | MGMT_MCLK); } /* Send PHY address. Zero is used for the internal PHY. */ for (i = 0; i < 5; i++) { nic_outlb(NIC_MGMT, rs); nic_outlb(NIC_MGMT, rs | MGMT_MCLK); } /* Send PHY register number. */ for (msk = 0x10; msk; msk >>= 1) { if (reg & msk) { nic_outlb(NIC_MGMT, rs | MGMT_MDO); nic_outlb(NIC_MGMT, rs | MGMT_MDO | MGMT_MCLK); } else { nic_outlb(NIC_MGMT, rs); nic_outlb(NIC_MGMT, rs | MGMT_MCLK); } } nic_outlb(NIC_MGMT, rs); return rs;}/*! * \brief Read contents of PHY register. * * \param reg PHY register number. * * \return Contents of the specified register. */static u_short NicPhyRead(u_char reg){ u_short rc = 0; u_char rs; u_char i; /* Select register for reading. */ rs = NicPhyRegSelect(reg, 0); /* Switch data direction. */ rs &= ~MGMT_MDOE; nic_outlb(NIC_MGMT, rs); nic_outlb(NIC_MGMT, rs | MGMT_MCLK); /* Clock data in. */ for (i = 0; i < 16; i++) { nic_outlb(NIC_MGMT, rs); nic_outlb(NIC_MGMT, rs | MGMT_MCLK); rc <<= 1; rc |= (nic_inlb(NIC_MGMT) & MGMT_MDI) != 0; } /* This will set the clock line to low. */ nic_outlb(NIC_MGMT, rs); return rc;}/*! * \brief Write value to PHY register. * * \param reg PHY register number. * \param val Value to write. */static void NicPhyWrite(u_char reg, u_short val){ u_short msk; u_char rs; /* Select register for writing. */ rs = NicPhyRegSelect(reg, 1); /* Switch data direction dummy. */ nic_outlb(NIC_MGMT, rs | MGMT_MDO); nic_outlb(NIC_MGMT, rs | MGMT_MDO | MGMT_MCLK); nic_outlb(NIC_MGMT, rs); nic_outlb(NIC_MGMT, rs | MGMT_MCLK); /* Clock data out. */ for (msk = 0x8000; msk; msk >>= 1) { if (val & msk) { nic_outlb(NIC_MGMT, rs | MGMT_MDO); nic_outlb(NIC_MGMT, rs | MGMT_MDO | MGMT_MCLK); } else { nic_outlb(NIC_MGMT, rs); nic_outlb(NIC_MGMT, rs | MGMT_MCLK); } } /* Set clock line low and output line int z-state. */ nic_outlb(NIC_MGMT, rs & ~MGMT_MDOE);}/*! * \brief Configure the internal PHY. * * Reset the PHY and initiate auto-negotiation. */static int NicPhyConfig(void){ u_short phy_sor; u_short phy_sr; u_short phy_to; u_short mode; /* * Reset the PHY and wait until this self clearing bit * becomes zero. We sleep 63 ms before each poll and * give up after 3 retries. */ NicPhyWrite(NIC_PHYCR, PHYCR_RST); for (phy_to = 0;; phy_to++) { Delay(100000); if ((NicPhyRead(NIC_PHYCR) & PHYCR_RST) == 0) break; if (phy_to > 3) { return -1; } } Delay(200000); /* Store PHY status output. */ phy_sor = NicPhyRead(NIC_PHYSOR); /* Enable PHY interrupts. */ NicPhyWrite(NIC_PHYMSK, PHYMSK_MLOSSSYN | PHYMSK_MCWRD | PHYMSK_MSSD | PHYMSK_MESD | PHYMSK_MRPOL | PHYMSK_MJAB | PHYMSK_MSPDDT | PHYMSK_MDPLDT); /* Set RPC register. */ mode = RPCR_ANEG | RPCR_LEDA_PAT | RPCR_LEDB_PAT; nic_bs(0); nic_outw(NIC_RPCR, mode); /* * Advertise our capabilities, initiate auto negotiation * and wait until this has been completed. */ NicPhyWrite(NIC_PHYANAD, PHYANAD_TX_FDX | PHYANAD_TX_HDX | PHYANAD_10FDX | PHYANAD_10_HDX | PHYANAD_CSMA); for (phy_to = 0, phy_sr = 0;; phy_to++) { /* Give up after long time wait. */ if (phy_to >= 32) { return -1; } /* Restart auto negotiation every 4 seconds or on failures. */ if ((phy_to & 127) == 0 /* || (phy_sr & PHYSR_REM_FLT) != 0 */) { NicPhyWrite(NIC_PHYCR, PHYCR_ANEG_EN | PHYCR_ANEG_RST); Delay(200000); } /* Check if link status detected. */ phy_sr = NicPhyRead(NIC_PHYSR); if (phy_sr & PHYSR_ANEG_ACK) break; Delay(300000); } return 0;}/*! * \brief Wait until MMU is ready. * * Poll the MMU command register until \ref MMUCR_BUSY * is cleared. * * \param tmo Timeout in milliseconds. * * \return 0 on success or -1 on timeout. */static INLINE int NicMmuWait(u_short tmo){ while (tmo--) { if ((nic_inlb(NIC_MMUCR) & MMUCR_BUSY) == 0) break; Delay(2000); } return tmo ? 0 : -1;}/*! * \brief Reset the Ethernet controller. * * \return 0 on success, -1 otherwise. */static int NicReset(void){ /* Disable all interrupts. */ nic_outlb(NIC_MSK, 0); /* MAC and PHY software reset. */ nic_bs(0); nic_outw(NIC_RCR, RCR_SOFT_RST); /* Enable Ethernet protocol handler. */ nic_bs(1); nic_outw(NIC_CR, CR_EPH_EN); Delay(20000); /* Disable transmit and receive. */ nic_bs(0); nic_outw(NIC_RCR, 0); nic_outw(NIC_TCR, 0); /* Enable auto release. */ nic_bs(1); nic_outw(NIC_CTR, CTR_AUTO_RELEASE); /* Reset MMU. */ nic_bs(2); nic_outlb(NIC_MMUCR, MMU_RST); if (NicMmuWait(1000)) return -1; return 0;}int SmscDetect(void){ u_char bv; /* High byte of base select is always 0x33. */ if((bv = nic_inhb(NIC_BSR)) != 0x33) { return -1; } /* Read revision. */ nic_bs(3); if(((bv = nic_inlb(NIC_REV)) & 0xF0) != 0x90) { return -1; } return 0;}/*! * \brief Test NIC interrupt line. */static int SmscTestInterrupt(void){ u_char tmo; /* * Set PE5 to input. This is our interrupt signal line. */ cbi(DDRE, 5); if (NicReset()) { puts("reset failed"); return -1; } if(bit_is_set(PINE, 5)) { puts("IRQ stuck"); return -1; } /* Enable receiver. */ nic_bs(3); nic_outlb(NIC_ERCV, 7); nic_bs(0); nic_outw(NIC_RCR, RCR_RXEN); /* Enable transmitter and padding. */ nic_outw(NIC_TCR, TCR_PAD_EN | TCR_TXENA); /* Configure the PHY. */ if (NicPhyConfig()) { puts("link failed"); return -1; } /* Allocate packet buffer space. */ nic_bs(2); nic_outlb(NIC_MMUCR, MMU_ALO); if (NicMmuWait(100)) return -1; /* Enable interrupts including allocation success. */ nic_outlb(NIC_MSK, INT_ALLOC); /* Wait for allocation success. */ tmo = 255; while ((nic_inlb(NIC_IST) & INT_ALLOC) == 0) { if(--tmo == 0) { puts("IRQ failed"); return -1; } Delay(2000); } if(bit_is_clear(PINE, 5)) { puts("no IRQ"); return -1; } return 0;}/*! * \brief Test NIC RAM buffer. */static int SmscTestBuffer(void){ ureg_t i; ureg_t tmo; u_short cnt; u_short val; /* Disable interrupts. */ if (NicReset()) { puts("reset failed"); return -1; } for(i = 0; i < 3; i++) { /* Allocate packet buffer space. */ nic_bs(2); nic_outlb(NIC_MMUCR, MMU_ALO); if (NicMmuWait(100)) { printf("Alloc failed\n"); return -1; } /* Wait for allocation success. */ tmo = 255; while ((nic_inlb(NIC_IST) & INT_ALLOC) == 0) { if(--tmo == 0) { puts("IRQ failed"); return -1; } Delay(2000); } /* * Transfer the allocated packet number to TX packet number. */ nic_outlb(NIC_PNR, nic_inhb(NIC_PNR)); /* * Reset the pointer register, no auto increment. */ nic_outw(NIC_PTR, 0); /* * Data write. */ for(cnt = 0; cnt < 1024; cnt++) { val = ~cnt; nic_outw(NIC_PTR, cnt * 2); nic_outlb(NIC_DATA, (u_char)val); nic_outw(NIC_PTR, cnt * 2 + 1); nic_outlb(NIC_DATA, val >> 8); } /* * Data read. */ for(cnt = 0; cnt < 1024; cnt++) { nic_outw(NIC_PTR, PTR_READ | (cnt * 2)); if((val = ~nic_inw(NIC_DATA)) != cnt) { printf("bad val %04X at %04X\n", val, cnt); return -1; } } } return 0;}/*! * \brief Test NIC functions. */int SmscTest(void){ if(SmscTestInterrupt()) { puts("IRQ failed"); return -1; } if(SmscTestBuffer()) { puts("Buf failed"); return -1; } puts("OK"); return 0;}/* * \brief Continously send baroadcasts. */void SmscSend(void){ u_char mac[] = { 0x00, 0x06, 0x98, 0x00, 0x00, 0x00 }; ureg_t i; u_short sz; u_long cnt = 0; printf("\nInit controller..."); if (NicReset()) { puts("reset failed"); return; } /* Enable receiver. */ nic_bs(3); nic_outlb(NIC_ERCV, 7); nic_bs(0); //nic_outw(NIC_RCR, RCR_RXEN); /* Enable transmitter and padding. */ nic_outw(NIC_TCR, TCR_PAD_EN | TCR_TXENA); /* Configure the PHY. */ if (NicPhyConfig()) { puts("link failed"); return; } /* Set MAC address. */ nic_bs(1); for (i = 0; i < 6; i++) nic_outlb(NIC_IAR + i, mac[i]); puts("OK"); for(cnt = 0;; cnt++) { Delay(500000); printf("\r%lu", cnt); sz = 1500; /* Allocate packet buffer space. */ nic_bs(2); nic_outlb(NIC_MMUCR, MMU_ALO); if (NicMmuWait(100)) { puts("Alloc"); break; } /* Enable allocation interrupt. */ nic_outlb(NIC_MSK, INT_ALLOC); /* * Wait for allocation success. This fails quite often, possibly * because we do not clear our receive buffer. */ if ((nic_inlb(NIC_IST) & INT_ALLOC) == 0) { puts(" Alloc"); Delay(500000); if ((nic_inlb(NIC_IST) & INT_ALLOC) == 0) { nic_outlb(NIC_MMUCR, MMU_RST); NicMmuWait(1000); nic_outlb(NIC_MMUCR, MMU_ALO); if (NicMmuWait(100) || (nic_inlb(NIC_IST) & INT_ALLOC) == 0) { break; } } } /* Disable interrupts. */ nic_outlb(NIC_MSK, 0); nic_outlb(NIC_PNR, nic_inhb(NIC_PNR)); nic_outw(NIC_PTR, 0x4000); /* Transfer control word. */ nic_outlb(NIC_DATA, 0); nic_outlb(NIC_DATA, 0); /* Transfer the byte count. */ nic_outw(NIC_DATA, sz); /* Transfer the Ethernet frame. */ for (i = 0; i < 6; i++) { nic_outlb(NIC_DATA, 0xFF); } for (i = 0; i < 6; i++) { nic_outlb(NIC_DATA, mac[i]); } nic_outlb(NIC_DATA, 0x08); nic_outlb(NIC_DATA, 0x00); /* * Add pad bytes. */ while (sz--) { nic_outlb(NIC_DATA, 0x00); } /* Transfer the control word. */ nic_outlb(NIC_DATA, 0); nic_outlb(NIC_DATA, 0); /* Enqueue packet. */ if (NicMmuWait(100)) { puts("Enqueue"); break; } nic_outlb(NIC_MMUCR, MMU_ENQ); if (GetChar()) break; }}void SmscLoop(void){ printf_P(presskey_P); for (;;) { nic_bs(3); printf("\rrev=0x%02X ", nic_inlb(NIC_REV)); if (GetChar()) { puts(""); return; } }}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -