Exemple #1
0
/* Process a pending TX allocate.  */
static void smc91c111_tx_alloc(smc91c111_state *s)
{
    s->tx_alloc = smc91c111_allocate_packet(s);
    if (s->tx_alloc == 0x80)
        return;
    s->int_level |= INT_ALLOC;
    smc91c111_update(s);
}
Exemple #2
0
/* Flush the TX FIFO.  */
static void smc91c111_do_tx(smc91c111_state *s)
{
    int i;
    int len;
    int control;
    int packetnum;
    uint8_t *p;

    if ((s->tcr & TCR_TXEN) == 0)
        return;
    if (s->tx_fifo_len == 0)
        return;
    for (i = 0; i < s->tx_fifo_len; i++) {
        packetnum = s->tx_fifo[i];
        p = &s->data[packetnum][0];
        /* Set status word.  */
        *(p++) = 0x01;
        *(p++) = 0x40;
        len = *(p++);
        len |= ((int)*(p++)) << 8;
        len -= 6;
        control = p[len + 1];
        if (control & 0x20)
            len++;
        /* ??? This overwrites the data following the buffer.
           Don't know what real hardware does.  */
        if (len < 64 && (s->tcr & TCR_PAD_EN)) {
            memset(p + len, 0, 64 - len);
            len = 64;
        }
#if 0
        {
            int add_crc;

            /* The card is supposed to append the CRC to the frame.
               However none of the other network traffic has the CRC
               appended.  Suspect this is low level ethernet detail we
               don't need to worry about.  */
            add_crc = (control & 0x10) || (s->tcr & TCR_NOCRC) == 0;
            if (add_crc) {
                uint32_t crc;

                crc = crc32(~0, p, len);
                memcpy(p + len, &crc, 4);
                len += 4;
            }
        }
#endif
        if (s->ctr & CTR_AUTO_RELEASE)
            /* Race?  */
            smc91c111_release_packet(s, packetnum);
        else if (s->tx_fifo_done_len < NUM_PACKETS)
            s->tx_fifo_done[s->tx_fifo_done_len++] = packetnum;
        qemu_send_packet(&s->nic->nc, p, len);
    }
    s->tx_fifo_len = 0;
    smc91c111_update(s);
}
static void smc91c111_do_tx(smc91c111_state *s)
{
    int i;
    int len;
    int control;
    int add_crc;
    int packetnum;
    uint8_t *p;

    if ((s->tcr & TCR_TXEN) == 0)
        return;
    if (s->tx_fifo_len == 0)
        return;
    for (i = 0; i < s->tx_fifo_len; i++) {
        packetnum = s->tx_fifo[i];
        p = &s->data[packetnum][0];
        
        *(p++) = 0x01;
        *(p++) = 0x40;
        len = *(p++);
        len |= ((int)*(p++)) << 8;
        len -= 6;
        control = p[len + 1];
        if (control & 0x20)
            len++;
        if (len < 64 && (s->tcr & TCR_PAD_EN)) {
            memset(p + len, 0, 64 - len);
            len = 64;
        }
#if 0
        add_crc = (control & 0x10) || (s->tcr & TCR_NOCRC) == 0;
        if (add_crc) {
            uint32_t crc;

            crc = crc32(~0, p, len);
            memcpy(p + len, &crc, 4);
            len += 4;
        }
#else
        add_crc = 0;
#endif
        if (s->ctr & CTR_AUTO_RELEASE)
            
            smc91c111_release_packet(s, packetnum);
        else if (s->tx_fifo_done_len < NUM_PACKETS)
            s->tx_fifo_done[s->tx_fifo_done_len++] = packetnum;
        qemu_send_packet(s->vc, p, len);
    }
    s->tx_fifo_len = 0;
    smc91c111_update(s);
}
Exemple #4
0
/* Remove and item from the RX FIFO.  */
static void smc91c111_pop_rx_fifo(smc91c111_state *s)
{
    int i;

    s->rx_fifo_len--;
    if (s->rx_fifo_len) {
        for (i = 0; i < s->rx_fifo_len; i++)
            s->rx_fifo[i] = s->rx_fifo[i + 1];
        s->int_level |= INT_RCV;
    } else {
        s->int_level &= ~INT_RCV;
    }
    smc91c111_update(s);
}
static void smc91c111_link_status_changed(VLANClientState *vc)
{
    smc91c111_state *s = vc->opaque;
    uint16_t ephsr;

    ephsr = s->ephsr & ~EPHSR_LINK_OK;
    if (!vc->link_down) {
        ephsr |= EPHSR_LINK_OK;
    }
    if (ephsr == s->ephsr) {
        return;
    }

    s->ephsr = ephsr;
    s->int_level |= INT_EPH;
    smc91c111_update(s);
}
Exemple #6
0
static void smc91c111_reset(smc91c111_state *s)
{
    s->bank = 0;
    s->tx_fifo_len = 0;
    s->tx_fifo_done_len = 0;
    s->rx_fifo_len = 0;
    s->allocated = 0;
    s->packet_num = 0;
    s->tx_alloc = 0;
    s->tcr = 0;
    s->rcr = 0;
    s->cr = 0xa0b1;
    s->ctr = 0x1210;
    s->ptr = 0;
    s->ercv = 0x1f;
    s->int_level = INT_TX_EMPTY;
    s->int_mask = 0;
    smc91c111_update(s);
}
Exemple #7
0
static void smc91c111_reset(DeviceState *dev)
{
    smc91c111_state *s = FROM_SYSBUS(smc91c111_state, SYS_BUS_DEVICE(dev));
    s->bank = 0;
    s->tx_fifo_len = 0;
    s->tx_fifo_done_len = 0;
    s->rx_fifo_len = 0;
    s->allocated = 0;
    s->packet_num = 0;
    s->tx_alloc = 0;
    s->tcr = 0;
    s->rcr = 0;
    s->cr = 0xa0b1;
    s->ctr = 0x1210;
    s->ptr = 0;
    s->ercv = 0x1f;
    s->int_level = INT_TX_EMPTY;
    s->int_mask = 0;
    smc91c111_update(s);
}
Exemple #8
0
static ssize_t smc91c111_receive(VLANClientState *vc, const uint8_t *buf, size_t size)
{
    smc91c111_state *s = vc->opaque;
    int status;
    int packetsize;
    uint32_t crc;
    int packetnum;
    uint8_t *p;

    if ((s->rcr & RCR_RXEN) == 0 || (s->rcr & RCR_SOFT_RST))
        return -1;
    /* Short packets are padded with zeros.  Receiving a packet
       < 64 bytes long is considered an error condition.  */
    if (size < 64)
        packetsize = 64;
    else
        packetsize = (size & ~1);
    packetsize += 6;
    crc = (s->rcr & RCR_STRIP_CRC) == 0;
    if (crc)
        packetsize += 4;
    /* TODO: Flag overrun and receive errors.  */
    if (packetsize > 2048)
        return -1;
    packetnum = smc91c111_allocate_packet(s);
    if (packetnum == 0x80)
        return -1;
    s->rx_fifo[s->rx_fifo_len++] = packetnum;

    p = &s->data[packetnum][0];
    /* ??? Multicast packets?  */
    status = 0;
    if (size > 1518)
        status |= RS_TOOLONG;
    if (size & 1)
        status |= RS_ODDFRAME;
    *(p++) = status & 0xff;
    *(p++) = status >> 8;
    *(p++) = packetsize & 0xff;
    *(p++) = packetsize >> 8;
    memcpy(p, buf, size & ~1);
    p += (size & ~1);
    /* Pad short packets.  */
    if (size < 64) {
        int pad;

        if (size & 1)
            *(p++) = buf[size - 1];
        pad = 64 - size;
        memset(p, 0, pad);
        p += pad;
        size = 64;
    }
    /* It's not clear if the CRC should go before or after the last byte in
       odd sized packets.  Linux disables the CRC, so that's no help.
       The pictures in the documentation show the CRC aligned on a 16-bit
       boundary before the last odd byte, so that's what we do.  */
    if (crc) {
        crc = crc32(~0, buf, size);
        *(p++) = crc & 0xff; crc >>= 8;
        *(p++) = crc & 0xff; crc >>= 8;
        *(p++) = crc & 0xff; crc >>= 8;
        *(p++) = crc & 0xff; crc >>= 8;
    }
    if (size & 1) {
        *(p++) = buf[size - 1];
        *(p++) = 0x60;
    } else {
        *(p++) = 0;
        *(p++) = 0x40;
    }
    /* TODO: Raise early RX interrupt?  */
    s->int_level |= INT_RCV;
    smc91c111_update(s);

    return size;
}
Exemple #9
0
static void smc91c111_writeb(void *opaque, hwaddr offset,
                             uint32_t value)
{
    smc91c111_state *s = (smc91c111_state *)opaque;

    if (offset == 14) {
        s->bank = value;
        return;
    }
    if (offset == 15)
        return;
    switch (s->bank) {
    case 0:
        switch (offset) {
        case 0: /* TCR */
            SET_LOW(tcr, value);
            return;
        case 1:
            SET_HIGH(tcr, value);
            return;
        case 4: /* RCR */
            SET_LOW(rcr, value);
            return;
        case 5:
            SET_HIGH(rcr, value);
            if (s->rcr & RCR_SOFT_RST)
                smc91c111_reset(s);
            return;
        case 10: case 11: /* RPCR */
            /* Ignored */
            return;
        }
        break;

    case 1:
        switch (offset) {
        case 0: /* CONFIG */
            SET_LOW(cr, value);
            return;
        case 1:
            SET_HIGH(cr,value);
            return;
        case 2: case 3: /* BASE */
        case 4: case 5: case 6: case 7: case 8: case 9: /* IA */
            /* Not implemented.  */
            return;
        case 10: /* Genral Purpose */
            SET_LOW(gpr, value);
            return;
        case 11:
            SET_HIGH(gpr, value);
            return;
        case 12: /* Control */
            if (value & 1)
                fprintf(stderr, "smc91c111:EEPROM store not implemented\n");
            if (value & 2)
                fprintf(stderr, "smc91c111:EEPROM reload not implemented\n");
            value &= ~3;
            SET_LOW(ctr, value);
            return;
        case 13:
            SET_HIGH(ctr, value);
            return;
        }
        break;

    case 2:
        switch (offset) {
        case 0: /* MMU Command */
            switch (value >> 5) {
            case 0: /* no-op */
                break;
            case 1: /* Allocate for TX.  */
                s->tx_alloc = 0x80;
                s->int_level &= ~INT_ALLOC;
                smc91c111_update(s);
                smc91c111_tx_alloc(s);
                break;
            case 2: /* Reset MMU.  */
                s->allocated = 0;
                s->tx_fifo_len = 0;
                s->tx_fifo_done_len = 0;
                s->rx_fifo_len = 0;
                s->tx_alloc = 0;
                break;
            case 3: /* Remove from RX FIFO.  */
                smc91c111_pop_rx_fifo(s);
                break;
            case 4: /* Remove from RX FIFO and release.  */
                if (s->rx_fifo_len > 0) {
                    smc91c111_release_packet(s, s->rx_fifo[0]);
                }
                smc91c111_pop_rx_fifo(s);
                break;
            case 5: /* Release.  */
                smc91c111_release_packet(s, s->packet_num);
                break;
            case 6: /* Add to TX FIFO.  */
                smc91c111_queue_tx(s, s->packet_num);
                break;
            case 7: /* Reset TX FIFO.  */
                s->tx_fifo_len = 0;
                s->tx_fifo_done_len = 0;
                break;
            }
            return;
        case 1:
            /* Ignore.  */
            return;
        case 2: /* Packet Number Register */
            s->packet_num = value;
            return;
        case 3: case 4: case 5:
            /* Should be readonly, but linux writes to them anyway. Ignore.  */
            return;
        case 6: /* Pointer */
            SET_LOW(ptr, value);
            return;
        case 7:
            SET_HIGH(ptr, value);
            return;
        case 8: case 9: case 10: case 11: /* Data */
            {
                int p;
                int n;

                if (s->ptr & 0x8000)
                    n = s->rx_fifo[0];
                else
                    n = s->packet_num;
                p = s->ptr & 0x07ff;
                if (s->ptr & 0x4000) {
                    s->ptr = (s->ptr & 0xf800) | ((s->ptr + 1) & 0x7ff);
                } else {
                    p += (offset & 3);
                }
                s->data[n][p] = value;
            }
            return;
        case 12: /* Interrupt ACK.  */
            s->int_level &= ~(value & 0xd6);
            if (value & INT_TX)
                smc91c111_pop_tx_fifo_done(s);
            smc91c111_update(s);
            return;
        case 13: /* Interrupt mask.  */
            s->int_mask = value;
            smc91c111_update(s);
            return;
        }
        break;;

    case 3:
        switch (offset) {
        case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
            /* Multicast table.  */
            /* Not implemented.  */
            return;
        case 8: case 9: /* Management Interface.  */
            /* Not implemented.  */
            return;
        case 12: /* Early receive.  */
            s->ercv = value & 0x1f;
        case 13:
            /* Ignore.  */
            return;
        }
        break;
    }
    hw_error("smc91c111_write: Bad reg %d:%x\n", s->bank, (int)offset);
}
static ssize_t smc91c111_receive(VLANClientState *vc, const uint8_t *buf, size_t size)
{
    smc91c111_state *s = vc->opaque;
    int status;
    int packetsize;
    uint32_t crc;
    int packetnum;
    uint8_t *p;

    if ((s->rcr & RCR_RXEN) == 0 || (s->rcr & RCR_SOFT_RST))
        return -1;
    if (size < 64)
        packetsize = 64;
    else
        packetsize = (size & ~1);
    packetsize += 6;
    crc = (s->rcr & RCR_STRIP_CRC) == 0;
    if (crc)
        packetsize += 4;
    
    if (packetsize > 2048)
        return -1;
    packetnum = smc91c111_allocate_packet(s);
    if (packetnum == 0x80)
        return -1;
    s->rx_fifo[s->rx_fifo_len++] = packetnum;

    p = &s->data[packetnum][0];
    
    status = 0;
    if (size > 1518)
        status |= RS_TOOLONG;
    if (size & 1)
        status |= RS_ODDFRAME;
    *(p++) = status & 0xff;
    *(p++) = status >> 8;
    *(p++) = packetsize & 0xff;
    *(p++) = packetsize >> 8;
    memcpy(p, buf, size & ~1);
    p += (size & ~1);
    
    if (size < 64) {
        int pad;

        if (size & 1)
            *(p++) = buf[size - 1];
        pad = 64 - size;
        memset(p, 0, pad);
        p += pad;
        size = 64;
    }
    if (crc) {
        crc = crc32(~0, buf, size);
        *(p++) = crc & 0xff; crc >>= 8;
        *(p++) = crc & 0xff; crc >>= 8;
        *(p++) = crc & 0xff; crc >>= 8;
        *(p++) = crc & 0xff; crc >>= 8;
    }
    if (size & 1) {
        *(p++) = buf[size - 1];
        *(p++) = 0x60;
    } else {
        *(p++) = 0;
        *(p++) = 0x40;
    }
    
    s->int_level |= INT_RCV;
    smc91c111_update(s);

    return size;
}
static void smc91c111_writeb(void *opaque, target_phys_addr_t offset,
                             uint32_t value)
{
    smc91c111_state *s = (smc91c111_state *)opaque;

    if (offset == 14) {
        s->bank = value;
        return;
    }
    if (offset == 15)
        return;
    switch (s->bank) {
    case 0:
        switch (offset) {
        case 0: 
            SET_LOW(tcr, value);
            return;
        case 1:
            SET_HIGH(tcr, value);
            return;
        case 4: 
            SET_LOW(rcr, value);
            return;
        case 5:
            SET_HIGH(rcr, value);
            if (s->rcr & RCR_SOFT_RST)
                smc91c111_reset(s);
            return;
        case 10: case 11: 
            
            return;
        }
        break;

    case 1:
        switch (offset) {
        case 0: 
            SET_LOW(cr, value);
            return;
        case 1:
            SET_HIGH(cr,value);
            return;
        case 2: case 3: 
        case 4: case 5: case 6: case 7: case 8: case 9: 
            
            return;
        case 10: 
            SET_LOW(gpr, value);
            return;
        case 11:
            SET_HIGH(gpr, value);
            return;
        case 12: 
            if (value & 1)
                fprintf(stderr, "smc91c111:EEPROM store not implemented\n");
            if (value & 2)
                fprintf(stderr, "smc91c111:EEPROM reload not implemented\n");
            value &= ~3;
            SET_LOW(ctr, value);
            return;
        case 13:
            SET_HIGH(ctr, value);
            return;
        }
        break;

    case 2:
        switch (offset) {
        case 0: 
            switch (value >> 5) {
            case 0: 
                break;
            case 1: 
                s->tx_alloc = 0x80;
                s->int_level &= ~INT_ALLOC;
                smc91c111_update(s);
                smc91c111_tx_alloc(s);
                break;
            case 2: 
                s->allocated = 0;
                s->tx_fifo_len = 0;
                s->tx_fifo_done_len = 0;
                s->rx_fifo_len = 0;
                s->tx_alloc = 0;
                break;
            case 3: 
                smc91c111_pop_rx_fifo(s);
                break;
            case 4: 
                if (s->rx_fifo_len > 0) {
                    smc91c111_release_packet(s, s->rx_fifo[0]);
                }
                smc91c111_pop_rx_fifo(s);
                break;
            case 5: 
                smc91c111_release_packet(s, s->packet_num);
                break;
            case 6: 
                smc91c111_queue_tx(s, s->packet_num);
                break;
            case 7: 
                s->tx_fifo_len = 0;
                s->tx_fifo_done_len = 0;
                break;
            }
            return;
        case 1:
            
            return;
        case 2: 
            s->packet_num = value;
            return;
        case 3: case 4: case 5:
            
            return;
        case 6: 
            SET_LOW(ptr, value);
            return;
        case 7:
            SET_HIGH(ptr, value);
            return;
        case 8: case 9: case 10: case 11: 
            {
                int p;
                int n;

                if (s->ptr & 0x8000)
                    n = s->rx_fifo[0];
                else
                    n = s->packet_num;
                p = s->ptr & 0x07ff;
                if (s->ptr & 0x4000) {
                    s->ptr = (s->ptr & 0xf800) | ((s->ptr + 1) & 0x7ff);
                } else {
                    p += (offset & 3);
                }
                s->data[n][p] = value;
            }
            return;
        case 12: 
            s->int_level &= ~(value & 0xd6);
            if (value & INT_TX)
                smc91c111_pop_tx_fifo_done(s);
            smc91c111_update(s);
            return;
        case 13: 
            s->int_mask = value;
            smc91c111_update(s);
            return;
        }
        break;;

    case 3:
        switch (offset) {
        case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
            
            
            return;
        case 8: case 9: 
            
            return;
        case 12: 
            s->ercv = value & 0x1f;
        case 13:
            
            return;
        }
        break;
    }
    hw_error("smc91c111_write: Bad reg %d:%x\n", s->bank, (int)offset);
}