Пример #1
0
/*!
 * \brief Read contents of PHY register.
 *
 * \note NIC interrupts must have been disabled before calling this routine.
 *
 * \param reg PHY register number.
 *
 * \return Contents of the specified register.
 */
static u_int16_t NicPhyRead(phantom_device_t * dev, uint8_t reg)
{
    u_int16_t rc = 0;
    uint8_t rs;
    uint8_t i;

    /* Select register for reading. */
    rs = NicPhyRegSelect(dev, 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 Read contents of PHY register.
 *
 * \param reg PHY register number.
 *
 * \return Contents of the specified register.
 */
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;
}
Пример #3
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(phantom_device_t * dev, u_int16_t tmo)
{
    while (tmo--) {
        if ((nic_inlb(NIC_MMUCR) & MMUCR_BUSY) == 0)
            break;
        NutDelay(1);
    }
    return tmo ? 0 : -1;
}
/*!
 * \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 int NicMmuWait(u_short tmo)
{
    while (tmo--) {
        if ((nic_inlb(NIC_MMUCR) & MMUCR_BUSY) == 0)
            break;
        MicroDelay(1000);
    }
    return tmo ? 0 : -1;
}
Пример #5
0
/*!
 * \brief Select specified PHY register for reading or writing.
 *
 * \note NIC interrupts must have been disabled before calling this routine.
 *
 * \param reg PHY register number.
 * \param we  Indicates type of access, 1 for write and 0 for read.
 *
 * \return Contents of the PHY interface rgister.
 */
static uint8_t NicPhyRegSelect(phantom_device_t * dev, uint8_t reg, uint8_t we)
{
    uint8_t rs;
    uint8_t msk;
    uint8_t 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 Receive an Ethernet frame.
 *
 * \param tms  Return with timeout after the specified
 *             number of waiting loops. On a 14 Mhz ATmega
 *             this value represents approximately the number
 *             of milliseconds to wait.
 *
 * \return The number of bytes received, 0 on timeout 
 *         or -1 in case of a failure.
 */
int EtherInput(u_short type, u_short tms)
{
    register u_short fsw;
    register u_char *buf;
    u_short fbc;

    /* Check the fifo empty bit. If it is set, then there is 
       nothing in the receiver fifo. */
    nic_bs(2);
    while (tms--) {
        if ((nic_inw(NIC_FIFO) & 0x8000) == 0) {
            break;
        }
        MicroDelay(1000);
    }
    if (nic_inw(NIC_FIFO) & 0x8000) {
        return 0;
    }

    /* Inialize pointer register. */
    nic_outw(NIC_PTR, PTR_READ | PTR_RCV | PTR_AUTO_INCR);
    MicroDelay(1);

    /* Read status word and byte count. */
    fsw = nic_inw(NIC_DATA);
    fbc = nic_inw(NIC_DATA) - 3;

    /* Check for frame errors. */
    if ((fsw & 0xAC00) == 0) {
        DEBUG("\nRx(");
        DEBUGUSHORT(fbc);
        DEBUG(")");
        if(fbc > sizeof(rframe)) {
            fbc = sizeof(rframe);
        }
        /* Perform the read. */
        buf = (u_char *) & rframe;
        fsw = fbc;
        while (fsw--) {
            *buf++ = nic_inlb(NIC_DATA);
        }
    }
    else {
        fbc = -1;
    }

    /* Release the packet. */
    nic_outlb(NIC_MMUCR, MMU_TOP);

    return fbc;
}
Пример #7
0
static int RealtekProbe(size_t addr)
{
    uint8_t bv;

    nic_base = (uint8_t *)addr;
    bv = nic_inlb(NIC_CR);
    if(bv & (NIC_CR_PS0 | NIC_CR_PS1)) {
        nic_outlb(NIC_CR, NIC_CR_STP | NIC_CR_RD2);
    }
    if(nic_inw(NIC_PG0_RBCR0) != 0x7050) {
        return -1;
    }
    return 0;
}
Пример #8
0
/*
 * Read data block from the NIC.
 */
static void NicRead(phantom_device_t * dev, uint8_t * buf, u_int16_t len)
{
    register u_int16_t l = len - 1;
    register uint8_t ih = (u_int16_t) l >> 8;
    register uint8_t il = (uint8_t) l;

    if (!len)
        return;

    do {
        do {
            *buf++ = nic_inlb(NIC_DATA);
        } while (il-- != 0);
    } while (ih-- != 0);
}
Пример #9
0
static int DetectNicEeprom(void)
{
#ifdef __AVR_ENHANCED__
    register unsigned int cnt = 0;

    cli();
    /*
     * Prepare the EEPROM emulation port bits. Configure the EEDO
     * and the EEMU lines as outputs and set both lines to high.
     */
    outb(PORTC, 0xC0);
    outb(DDRC, 0xC0);

    /*
     * Force the chip to re-read the EEPROM contents.
     */
    nic_outlb(NIC_CR, NIC_CR_STP | NIC_CR_RD2 | NIC_CR_PS0 | NIC_CR_PS1);
    nic_outlb(NIC_PG3_EECR, NIC_EECR_EEM0);

    /*
     * No external memory access beyond this point.
     */
#if defined(__AVR_AT90CAN128__) || defined(__AVR_ATmega2561__)
    cbi(XMCRA, SRE);
#else
    cbi(MCUCR, SRE);
#endif

    /*
     * Check, if the chip toggles our EESK input. If not, we do not
     * have EEPROM emulation hardware.
     */
    if(bit_is_set(PINC, 5)) {
        while(++cnt && bit_is_set(PINC, 5));
    }
    else {
        while(++cnt && bit_is_clear(PINC, 5));
    }

    /*
     * Enable memory interface.
     */
#if defined(__AVR_AT90CAN128__) || defined(__AVR_ATmega2561__)
    sbi(XMCRA, SRE);
#else
    sbi(MCUCR, SRE);
#endif

    /* Reset port outputs to default. */
    outb(PORTC, 0x00);
    outb(DDRC, 0x00);

    /* Wait until controller ready. */
    while(nic_inlb(NIC_CR) != (NIC_CR_STP | NIC_CR_RD2));

    sei();
    return cnt ? 0 : -1;
#else
    return -1;
#endif
}
Пример #10
0
/*! \fn NicRxLanc(void *arg)
 * \brief NIC receiver thread.
 *
 */
THREAD(NicRxLanc, arg)
{
    NUTDEVICE *dev;
    IFNET *ifn;
    lanc111_nic_t *ni;
    NETBUF *nb;
    uint8_t imsk;

    dev = arg;
    ifn = (IFNET *) dev->dev_icb;
    ni = (lanc111_nic_t *) dev->dev_dcb;

    /*
     * This is a temporary hack. Due to a change in initialization,
     * we may not have got a MAC address yet. Wait until a valid one
     * has been set.
     */
    while (!ETHER_IS_UNICAST(ifn->if_mac)) {
        NutSleep(10);
    }

    /*
     * Do not continue unless we managed to start the NIC. We are
     * trapped here if the Ethernet link cannot be established.
     * This happens, for example, if no Ethernet cable is plugged
     * in.
     */
    while(NicStart(ifn->if_mac)) {
        NutSleep(1000);
    }

    //LANC111_SIGNAL_MODE();

    // Enable IRQs. Enabled on irq allocation.
    //sbi(EIMSK, LANC111_SIGNAL_IRQ);

    NutEventPost(&nic->ni_tx_rdy);

    /* Run at high priority. */
    NutThreadSetPriority(9);

    for (;;) {

        /*
         * Wait for the arrival of new packets or
         * check the receiver every two second.
         */
        NutEventWait(&ni->ni_rx_rdy, 2000);

        /*
         * Fetch all packets from the NIC's internal
         * buffer and pass them to the registered handler.
         */
        imsk = nic_inlb(NIC_MSK);
        nic_outlb(NIC_MSK, 0);
        while ((nb = NicGetPacket()) != 0) {
            if (nb != (NETBUF *) 0xFFFF) {
                ni->ni_rx_packets++;
                (*ifn->if_recv) (dev, nb);
            }
        }
        nic_outlb(NIC_MSK, imsk | INT_RCV | INT_ERCV);
    }
}
Пример #11
0
/*!
 * \brief Load a packet into the nic's transmit ring buffer.
 *
 * Interupts must have been disabled when calling this function.
 *
 * \param nb Network buffer structure containing the packet to be sent.
 *           The structure must have been allocated by a previous
 *           call NutNetBufAlloc(). This routine will automatically
 *           release the buffer in case of an error.
 *
 * \return 0 on success, -1 in case of any errors. Errors
 *         will automatically release the network buffer 
 *         structure.
 */
static int NicPutPacket(NETBUF * nb)
{
    u_int16_t sz;
    uint8_t odd = 0;
    uint8_t imsk;

    //printf("[P]");
    /*
     * Calculate the number of bytes to be send. Do not send packets 
     * larger than the Ethernet maximum transfer unit. The MTU
     * consist of 1500 data bytes plus the 14 byte Ethernet header
     * plus 4 bytes CRC. We check the data bytes only.
     */
    if ((sz = nb->nb_nw.sz + nb->nb_tp.sz + nb->nb_ap.sz) > ETH_DATA_LEN)
        return -1;

    /* Disable all interrupts. */
    imsk = nic_inlb(NIC_MSK);
    nic_outlb(NIC_MSK, 0);

    /* 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, imsk | INT_ALLOC);

    /* The MMU needs some time. Use it to calculate the byte count. */
    sz += nb->nb_dl.sz;
    sz += 6;
    if (sz & 1) {
        sz++;
        odd++;
    }

    /* Wait for allocation success. */
    while ((nic_inlb(NIC_IST) & INT_ALLOC) == 0) {
        if (NutEventWait(&maq, 125)) {
            nic_outlb(NIC_MMUCR, MMU_RST);
            NicMmuWait(1000);
            nic_outlb(NIC_MMUCR, MMU_ALO);
            if (NicMmuWait(100) || (nic_inlb(NIC_IST) & INT_ALLOC) == 0) {
                if (NutEventWait(&maq, 125)) {
                    return -1;
                }
            }
        }
    }

    /* Disable interrupts. */
    imsk = nic_inlb(NIC_MSK);
    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. */
    NicWrite(nb->nb_dl.vp, nb->nb_dl.sz);
    NicWrite(nb->nb_nw.vp, nb->nb_nw.sz);
    NicWrite(nb->nb_tp.vp, nb->nb_tp.sz);
    NicWrite(nb->nb_ap.vp, nb->nb_ap.sz);

    if (odd)
        nic_outlb(NIC_DATA, 0);

    /* Transfer the control word. */
    nic_outw(NIC_DATA, 0);

    /* Enqueue packet. */
    if (NicMmuWait(100))
        return -1;
    nic_outlb(NIC_MMUCR, MMU_ENQ);

    /* Enable interrupts. */
    imsk |= INT_TX | INT_TX_EMPTY;
    nic_outlb(NIC_MSK, imsk);

    return 0;
}
Пример #12
0
/*
 * NIC interrupt entry.
 */
static void NicInterrupt(void *arg)
{
    phantom_device_t * dev = arg;

    uint8_t isr;
    uint8_t imr;
    lanc111_nic_t *ni = (lanc111_nic_t *) ((NUTDEVICE *) arg)->dev_dcb;


    ni->ni_interrupts++;

    /* Read the interrupt mask and disable all interrupts. */
    nic_bs(2);
    imr = nic_inlb(NIC_MSK);
    nic_outlb(NIC_MSK, 0);

    /* Read the interrupt status and acknowledge all interrupts. */
    isr = nic_inlb(NIC_IST);
    //printf("\n!%02X-%02X ", isr, imr);
    isr &= imr;

    /*
     * If this is a transmit interrupt, then a packet has been sent. 
     * So we can clear the transmitter busy flag and wake up the 
     * transmitter thread.
     */
    if (isr & INT_TX_EMPTY) {
        nic_outlb(NIC_ACK, INT_TX_EMPTY);
        imr &= ~INT_TX_EMPTY;
    }
    /* Transmit error. */
    else if (isr & INT_TX) {
        /* re-enable transmit */
        nic_bs(0);
        nic_outw(NIC_TCR, nic_inlb(NIC_TCR) | TCR_TXENA);
        nic_bs(2);
        nic_outlb(NIC_ACK, INT_TX);
        /* kill the packet */
        nic_outlb(NIC_MMUCR, MMU_PKT);
    }


    /*
     * If this is a receive interrupt, then wake up the receiver 
     * thread.
     */
    if (isr & INT_RX_OVRN) {
        nic_outlb(NIC_ACK, INT_RX_OVRN);
        //nic_outlb(NIC_MMUCR, MMU_TOP);
        NutEventPostFromIrq(&ni->ni_rx_rdy);
    }
    if (isr & INT_ERCV) {
        nic_outlb(NIC_ACK, INT_ERCV);
        NutEventPostFromIrq(&ni->ni_rx_rdy);
    }
    if (isr & INT_RCV) {
        nic_outlb(NIC_ACK, INT_RCV);
        imr &= ~INT_RCV;
        NutEventPostFromIrq(&ni->ni_rx_rdy);
    }

    if (isr & INT_ALLOC) {
        imr &= ~INT_ALLOC;
        NutEventPostFromIrq(&maq);
    }
    //printf(" -%02X-%02X- ", nic_inlb(NIC_IST), inb(PINE) & 0x20);
    nic_outlb(NIC_MSK, imr);
}
/*!
 * \brief Send an Ethernet frame.
 *
 * \param dmac Destination MAC address.
 * \param type Frame type.
 * \param len  Frame size.
 *
 * \return 0 on success, -1 otherwise.
 */
int EtherOutput(const u_char * dmac, u_short type, u_short len)
{
    ETHERHDR *eh;
    u_char *cp;

    /*
     * Set the Ethernet header.
     */
    if (type == ETHERTYPE_ARP) {
        cp = (u_char *) & arpframe;
    } else {
        cp = (u_char *) & sframe;
    }
    eh = (ETHERHDR *)cp;
    memcpy_(eh->ether_shost, confnet.cdn_mac, 6);
    memcpy_(eh->ether_dhost, dmac, 6);
    eh->ether_type = type;

    /*
     * The total packet length includes 
     * - status word (2 bytes)
     * - byte count (2 bytes)
     * - destination address (6 bytes)
     * - source address (6 bytes)
     * - Ethernet type (2 bytes)
     * - data bytes (variable)
     * - control word (2 bytes)
     * Thus we add 20 to the number of data bytes. We didn't
     * manage to get an odd number of bytes transmitted, so
     * add another byte.
     */
    if((len += 20) & 1) {
        len++;
    }

    DEBUG(" Tx(");
    DEBUGUSHORT(len);
    DEBUG(")");

    /* Allocate transmit packet buffer space. */
    nic_bs(2);
    nic_outlb(NIC_MMUCR, MMU_ALO);
    if (NicMmuWait(100)) {
        return -1;
    }

    /*
     * An allocation error might appear when incoming packets occupy 
     * all the buffer. Reset the MMU to release all memory. This is
     * very drastic, but OK for our sequential boot loader.
     */
    if ((nic_inlb(NIC_IST) & INT_ALLOC) == 0) {
        DEBUG("[MMURST]");
        nic_outlb(NIC_MMUCR, MMU_RST);
        NicMmuWait(1000);
        nic_outlb(NIC_MMUCR, MMU_ALO);
        if (NicMmuWait(100) || (nic_inlb(NIC_IST) & INT_ALLOC) == 0) {
            return -1;
        }
    }

    /* 
     * Read the number of the allcocated packet from the allocation 
     * result register and write it to the packet number register.
     */
    nic_outlb(NIC_PNR, nic_inhb(NIC_PNR));

    /*
     * Initially set the pointer register address to 2 and enable
     * auto increment. The first two bytes will be used by the CSMA 
     * to store the status word upon transmit completion. 
     */
    nic_outw(NIC_PTR, PTR_AUTO_INCR | 2);

    /* 
     * Transfer the byte count and the data bytes.
     */
    nic_outw(NIC_DATA, len);
    while (len--) {
        nic_outlb(NIC_DATA, *cp);
        cp++;
    }
    
    /* 
     * Transfer the control word. As stated above, we never succeeded
     * in sending an odd number of bytes.
     */
    nic_outw(NIC_DATA, 0);

    /* Enqueue packet. */
    NicMmuWait(100);
    nic_outlb(NIC_MMUCR, MMU_ENQ);

    return 0;
}