qemu/hw/smc91c111.c
<<
>>
Prefs
   1/*
   2 * SMSC 91C111 Ethernet interface emulation
   3 *
   4 * Copyright (c) 2005 CodeSourcery, LLC.
   5 * Written by Paul Brook
   6 *
   7 * This code is licenced under the GPL
   8 */
   9
  10#include "hw.h"
  11#include "net.h"
  12#include "devices.h"
  13/* For crc32 */
  14#include <zlib.h>
  15
  16/* Number of 2k memory pages available.  */
  17#define NUM_PACKETS 4
  18
  19typedef struct {
  20    VLANClientState *vc;
  21    uint16_t tcr;
  22    uint16_t rcr;
  23    uint16_t cr;
  24    uint16_t ctr;
  25    uint16_t gpr;
  26    uint16_t ptr;
  27    uint16_t ercv;
  28    qemu_irq irq;
  29    int bank;
  30    int packet_num;
  31    int tx_alloc;
  32    /* Bitmask of allocated packets.  */
  33    int allocated;
  34    int tx_fifo_len;
  35    int tx_fifo[NUM_PACKETS];
  36    int rx_fifo_len;
  37    int rx_fifo[NUM_PACKETS];
  38    int tx_fifo_done_len;
  39    int tx_fifo_done[NUM_PACKETS];
  40    /* Packet buffer memory.  */
  41    uint8_t data[NUM_PACKETS][2048];
  42    uint8_t int_level;
  43    uint8_t int_mask;
  44    uint8_t macaddr[6];
  45} smc91c111_state;
  46
  47#define RCR_SOFT_RST  0x8000
  48#define RCR_STRIP_CRC 0x0200
  49#define RCR_RXEN      0x0100
  50
  51#define TCR_EPH_LOOP  0x2000
  52#define TCR_NOCRC     0x0100
  53#define TCR_PAD_EN    0x0080
  54#define TCR_FORCOL    0x0004
  55#define TCR_LOOP      0x0002
  56#define TCR_TXEN      0x0001
  57
  58#define INT_MD        0x80
  59#define INT_ERCV      0x40
  60#define INT_EPH       0x20
  61#define INT_RX_OVRN   0x10
  62#define INT_ALLOC     0x08
  63#define INT_TX_EMPTY  0x04
  64#define INT_TX        0x02
  65#define INT_RCV       0x01
  66
  67#define CTR_AUTO_RELEASE  0x0800
  68#define CTR_RELOAD        0x0002
  69#define CTR_STORE         0x0001
  70
  71#define RS_ALGNERR      0x8000
  72#define RS_BRODCAST     0x4000
  73#define RS_BADCRC       0x2000
  74#define RS_ODDFRAME     0x1000
  75#define RS_TOOLONG      0x0800
  76#define RS_TOOSHORT     0x0400
  77#define RS_MULTICAST    0x0001
  78
  79/* Update interrupt status.  */
  80static void smc91c111_update(smc91c111_state *s)
  81{
  82    int level;
  83
  84    if (s->tx_fifo_len == 0)
  85        s->int_level |= INT_TX_EMPTY;
  86    if (s->tx_fifo_done_len != 0)
  87        s->int_level |= INT_TX;
  88    level = (s->int_level & s->int_mask) != 0;
  89    qemu_set_irq(s->irq, level);
  90}
  91
  92/* Try to allocate a packet.  Returns 0x80 on failure.  */
  93static int smc91c111_allocate_packet(smc91c111_state *s)
  94{
  95    int i;
  96    if (s->allocated == (1 << NUM_PACKETS) - 1) {
  97        return 0x80;
  98    }
  99
 100    for (i = 0; i < NUM_PACKETS; i++) {
 101        if ((s->allocated & (1 << i)) == 0)
 102            break;
 103    }
 104    s->allocated |= 1 << i;
 105    return i;
 106}
 107
 108
 109/* Process a pending TX allocate.  */
 110static void smc91c111_tx_alloc(smc91c111_state *s)
 111{
 112    s->tx_alloc = smc91c111_allocate_packet(s);
 113    if (s->tx_alloc == 0x80)
 114        return;
 115    s->int_level |= INT_ALLOC;
 116    smc91c111_update(s);
 117}
 118
 119/* Remove and item from the RX FIFO.  */
 120static void smc91c111_pop_rx_fifo(smc91c111_state *s)
 121{
 122    int i;
 123
 124    s->rx_fifo_len--;
 125    if (s->rx_fifo_len) {
 126        for (i = 0; i < s->rx_fifo_len; i++)
 127            s->rx_fifo[i] = s->rx_fifo[i + 1];
 128        s->int_level |= INT_RCV;
 129    } else {
 130        s->int_level &= ~INT_RCV;
 131    }
 132    smc91c111_update(s);
 133}
 134
 135/* Remove an item from the TX completion FIFO.  */
 136static void smc91c111_pop_tx_fifo_done(smc91c111_state *s)
 137{
 138    int i;
 139
 140    if (s->tx_fifo_done_len == 0)
 141        return;
 142    s->tx_fifo_done_len--;
 143    for (i = 0; i < s->tx_fifo_done_len; i++)
 144        s->tx_fifo_done[i] = s->tx_fifo_done[i + 1];
 145}
 146
 147/* Release the memory allocated to a packet.  */
 148static void smc91c111_release_packet(smc91c111_state *s, int packet)
 149{
 150    s->allocated &= ~(1 << packet);
 151    if (s->tx_alloc == 0x80)
 152        smc91c111_tx_alloc(s);
 153}
 154
 155/* Flush the TX FIFO.  */
 156static void smc91c111_do_tx(smc91c111_state *s)
 157{
 158    int i;
 159    int len;
 160    int control;
 161    int add_crc;
 162    int packetnum;
 163    uint8_t *p;
 164
 165    if ((s->tcr & TCR_TXEN) == 0)
 166        return;
 167    if (s->tx_fifo_len == 0)
 168        return;
 169    for (i = 0; i < s->tx_fifo_len; i++) {
 170        packetnum = s->tx_fifo[i];
 171        p = &s->data[packetnum][0];
 172        /* Set status word.  */
 173        *(p++) = 0x01;
 174        *(p++) = 0x40;
 175        len = *(p++);
 176        len |= ((int)*(p++)) << 8;
 177        len -= 6;
 178        control = p[len + 1];
 179        if (control & 0x20)
 180            len++;
 181        /* ??? This overwrites the data following the buffer.
 182           Don't know what real hardware does.  */
 183        if (len < 64 && (s->tcr & TCR_PAD_EN)) {
 184            memset(p + len, 0, 64 - len);
 185            len = 64;
 186        }
 187#if 0
 188        /* The card is supposed to append the CRC to the frame.  However
 189           none of the other network traffic has the CRC appended.
 190           Suspect this is low level ethernet detail we don't need to worry
 191           about.  */
 192        add_crc = (control & 0x10) || (s->tcr & TCR_NOCRC) == 0;
 193        if (add_crc) {
 194            uint32_t crc;
 195
 196            crc = crc32(~0, p, len);
 197            memcpy(p + len, &crc, 4);
 198            len += 4;
 199        }
 200#else
 201        add_crc = 0;
 202#endif
 203        if (s->ctr & CTR_AUTO_RELEASE)
 204            /* Race?  */
 205            smc91c111_release_packet(s, packetnum);
 206        else if (s->tx_fifo_done_len < NUM_PACKETS)
 207            s->tx_fifo_done[s->tx_fifo_done_len++] = packetnum;
 208        qemu_send_packet(s->vc, p, len);
 209    }
 210    s->tx_fifo_len = 0;
 211    smc91c111_update(s);
 212}
 213
 214/* Add a packet to the TX FIFO.  */
 215static void smc91c111_queue_tx(smc91c111_state *s, int packet)
 216{
 217    if (s->tx_fifo_len == NUM_PACKETS)
 218        return;
 219    s->tx_fifo[s->tx_fifo_len++] = packet;
 220    smc91c111_do_tx(s);
 221}
 222
 223static void smc91c111_reset(smc91c111_state *s)
 224{
 225    s->bank = 0;
 226    s->tx_fifo_len = 0;
 227    s->tx_fifo_done_len = 0;
 228    s->rx_fifo_len = 0;
 229    s->allocated = 0;
 230    s->packet_num = 0;
 231    s->tx_alloc = 0;
 232    s->tcr = 0;
 233    s->rcr = 0;
 234    s->cr = 0xa0b1;
 235    s->ctr = 0x1210;
 236    s->ptr = 0;
 237    s->ercv = 0x1f;
 238    s->int_level = INT_TX_EMPTY;
 239    s->int_mask = 0;
 240    smc91c111_update(s);
 241}
 242
 243#define SET_LOW(name, val) s->name = (s->name & 0xff00) | val
 244#define SET_HIGH(name, val) s->name = (s->name & 0xff) | (val << 8)
 245
 246static void smc91c111_writeb(void *opaque, target_phys_addr_t offset,
 247                             uint32_t value)
 248{
 249    smc91c111_state *s = (smc91c111_state *)opaque;
 250
 251    if (offset == 14) {
 252        s->bank = value;
 253        return;
 254    }
 255    if (offset == 15)
 256        return;
 257    switch (s->bank) {
 258    case 0:
 259        switch (offset) {
 260        case 0: /* TCR */
 261            SET_LOW(tcr, value);
 262            return;
 263        case 1:
 264            SET_HIGH(tcr, value);
 265            return;
 266        case 4: /* RCR */
 267            SET_LOW(rcr, value);
 268            return;
 269        case 5:
 270            SET_HIGH(rcr, value);
 271            if (s->rcr & RCR_SOFT_RST)
 272                smc91c111_reset(s);
 273            return;
 274        case 10: case 11: /* RPCR */
 275            /* Ignored */
 276            return;
 277        }
 278        break;
 279
 280    case 1:
 281        switch (offset) {
 282        case 0: /* CONFIG */
 283            SET_LOW(cr, value);
 284            return;
 285        case 1:
 286            SET_HIGH(cr,value);
 287            return;
 288        case 2: case 3: /* BASE */
 289        case 4: case 5: case 6: case 7: case 8: case 9: /* IA */
 290            /* Not implemented.  */
 291            return;
 292        case 10: /* Genral Purpose */
 293            SET_LOW(gpr, value);
 294            return;
 295        case 11:
 296            SET_HIGH(gpr, value);
 297            return;
 298        case 12: /* Control */
 299            if (value & 1)
 300                fprintf(stderr, "smc91c111:EEPROM store not implemented\n");
 301            if (value & 2)
 302                fprintf(stderr, "smc91c111:EEPROM reload not implemented\n");
 303            value &= ~3;
 304            SET_LOW(ctr, value);
 305            return;
 306        case 13:
 307            SET_HIGH(ctr, value);
 308            return;
 309        }
 310        break;
 311
 312    case 2:
 313        switch (offset) {
 314        case 0: /* MMU Command */
 315            switch (value >> 5) {
 316            case 0: /* no-op */
 317                break;
 318            case 1: /* Allocate for TX.  */
 319                s->tx_alloc = 0x80;
 320                s->int_level &= ~INT_ALLOC;
 321                smc91c111_update(s);
 322                smc91c111_tx_alloc(s);
 323                break;
 324            case 2: /* Reset MMU.  */
 325                s->allocated = 0;
 326                s->tx_fifo_len = 0;
 327                s->tx_fifo_done_len = 0;
 328                s->rx_fifo_len = 0;
 329                s->tx_alloc = 0;
 330                break;
 331            case 3: /* Remove from RX FIFO.  */
 332                smc91c111_pop_rx_fifo(s);
 333                break;
 334            case 4: /* Remove from RX FIFO and release.  */
 335                if (s->rx_fifo_len > 0) {
 336                    smc91c111_release_packet(s, s->rx_fifo[0]);
 337                }
 338                smc91c111_pop_rx_fifo(s);
 339                break;
 340            case 5: /* Release.  */
 341                smc91c111_release_packet(s, s->packet_num);
 342                break;
 343            case 6: /* Add to TX FIFO.  */
 344                smc91c111_queue_tx(s, s->packet_num);
 345                break;
 346            case 7: /* Reset TX FIFO.  */
 347                s->tx_fifo_len = 0;
 348                s->tx_fifo_done_len = 0;
 349                break;
 350            }
 351            return;
 352        case 1:
 353            /* Ignore.  */
 354            return;
 355        case 2: /* Packet Number Register */
 356            s->packet_num = value;
 357            return;
 358        case 3: case 4: case 5:
 359            /* Should be readonly, but linux writes to them anyway. Ignore.  */
 360            return;
 361        case 6: /* Pointer */
 362            SET_LOW(ptr, value);
 363            return;
 364        case 7:
 365            SET_HIGH(ptr, value);
 366            return;
 367        case 8: case 9: case 10: case 11: /* Data */
 368            {
 369                int p;
 370                int n;
 371
 372                if (s->ptr & 0x8000)
 373                    n = s->rx_fifo[0];
 374                else
 375                    n = s->packet_num;
 376                p = s->ptr & 0x07ff;
 377                if (s->ptr & 0x4000) {
 378                    s->ptr = (s->ptr & 0xf800) | ((s->ptr + 1) & 0x7ff);
 379                } else {
 380                    p += (offset & 3);
 381                }
 382                s->data[n][p] = value;
 383            }
 384            return;
 385        case 12: /* Interrupt ACK.  */
 386            s->int_level &= ~(value & 0xd6);
 387            if (value & INT_TX)
 388                smc91c111_pop_tx_fifo_done(s);
 389            smc91c111_update(s);
 390            return;
 391        case 13: /* Interrupt mask.  */
 392            s->int_mask = value;
 393            smc91c111_update(s);
 394            return;
 395        }
 396        break;;
 397
 398    case 3:
 399        switch (offset) {
 400        case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
 401            /* Multicast table.  */
 402            /* Not implemented.  */
 403            return;
 404        case 8: case 9: /* Management Interface.  */
 405            /* Not implemented.  */
 406            return;
 407        case 12: /* Early receive.  */
 408            s->ercv = value & 0x1f;
 409        case 13:
 410            /* Ignore.  */
 411            return;
 412        }
 413        break;
 414    }
 415    cpu_abort (cpu_single_env, "smc91c111_write: Bad reg %d:%x\n",
 416               s->bank, (int)offset);
 417}
 418
 419static uint32_t smc91c111_readb(void *opaque, target_phys_addr_t offset)
 420{
 421    smc91c111_state *s = (smc91c111_state *)opaque;
 422
 423    if (offset == 14) {
 424        return s->bank;
 425    }
 426    if (offset == 15)
 427        return 0x33;
 428    switch (s->bank) {
 429    case 0:
 430        switch (offset) {
 431        case 0: /* TCR */
 432            return s->tcr & 0xff;
 433        case 1:
 434            return s->tcr >> 8;
 435        case 2: /* EPH Status */
 436            return 0;
 437        case 3:
 438            return 0x40;
 439        case 4: /* RCR */
 440            return s->rcr & 0xff;
 441        case 5:
 442            return s->rcr >> 8;
 443        case 6: /* Counter */
 444        case 7:
 445            /* Not implemented.  */
 446            return 0;
 447        case 8: /* Memory size.  */
 448            return NUM_PACKETS;
 449        case 9: /* Free memory available.  */
 450            {
 451                int i;
 452                int n;
 453                n = 0;
 454                for (i = 0; i < NUM_PACKETS; i++) {
 455                    if (s->allocated & (1 << i))
 456                        n++;
 457                }
 458                return n;
 459            }
 460        case 10: case 11: /* RPCR */
 461            /* Not implemented.  */
 462            return 0;
 463        }
 464        break;
 465
 466    case 1:
 467        switch (offset) {
 468        case 0: /* CONFIG */
 469            return s->cr & 0xff;
 470        case 1:
 471            return s->cr >> 8;
 472        case 2: case 3: /* BASE */
 473            /* Not implemented.  */
 474            return 0;
 475        case 4: case 5: case 6: case 7: case 8: case 9: /* IA */
 476            return s->macaddr[offset - 4];
 477        case 10: /* General Purpose */
 478            return s->gpr & 0xff;
 479        case 11:
 480            return s->gpr >> 8;
 481        case 12: /* Control */
 482            return s->ctr & 0xff;
 483        case 13:
 484            return s->ctr >> 8;
 485        }
 486        break;
 487
 488    case 2:
 489        switch (offset) {
 490        case 0: case 1: /* MMUCR Busy bit.  */
 491            return 0;
 492        case 2: /* Packet Number.  */
 493            return s->packet_num;
 494        case 3: /* Allocation Result.  */
 495            return s->tx_alloc;
 496        case 4: /* TX FIFO */
 497            if (s->tx_fifo_done_len == 0)
 498                return 0x80;
 499            else
 500                return s->tx_fifo_done[0];
 501        case 5: /* RX FIFO */
 502            if (s->rx_fifo_len == 0)
 503                return 0x80;
 504            else
 505                return s->rx_fifo[0];
 506        case 6: /* Pointer */
 507            return s->ptr & 0xff;
 508        case 7:
 509            return (s->ptr >> 8) & 0xf7;
 510        case 8: case 9: case 10: case 11: /* Data */
 511            {
 512                int p;
 513                int n;
 514
 515                if (s->ptr & 0x8000)
 516                    n = s->rx_fifo[0];
 517                else
 518                    n = s->packet_num;
 519                p = s->ptr & 0x07ff;
 520                if (s->ptr & 0x4000) {
 521                    s->ptr = (s->ptr & 0xf800) | ((s->ptr + 1) & 0x07ff);
 522                } else {
 523                    p += (offset & 3);
 524                }
 525                return s->data[n][p];
 526            }
 527        case 12: /* Interrupt status.  */
 528            return s->int_level;
 529        case 13: /* Interrupt mask.  */
 530            return s->int_mask;
 531        }
 532        break;
 533
 534    case 3:
 535        switch (offset) {
 536        case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
 537            /* Multicast table.  */
 538            /* Not implemented.  */
 539            return 0;
 540        case 8: /* Management Interface.  */
 541            /* Not implemented.  */
 542            return 0x30;
 543        case 9:
 544            return 0x33;
 545        case 10: /* Revision.  */
 546            return 0x91;
 547        case 11:
 548            return 0x33;
 549        case 12:
 550            return s->ercv;
 551        case 13:
 552            return 0;
 553        }
 554        break;
 555    }
 556    cpu_abort (cpu_single_env, "smc91c111_read: Bad reg %d:%x\n",
 557               s->bank, (int)offset);
 558    return 0;
 559}
 560
 561static void smc91c111_writew(void *opaque, target_phys_addr_t offset,
 562                             uint32_t value)
 563{
 564    smc91c111_writeb(opaque, offset, value & 0xff);
 565    smc91c111_writeb(opaque, offset + 1, value >> 8);
 566}
 567
 568static void smc91c111_writel(void *opaque, target_phys_addr_t offset,
 569                             uint32_t value)
 570{
 571    /* 32-bit writes to offset 0xc only actually write to the bank select
 572       register (offset 0xe)  */
 573    if (offset != 0xc)
 574        smc91c111_writew(opaque, offset, value & 0xffff);
 575    smc91c111_writew(opaque, offset + 2, value >> 16);
 576}
 577
 578static uint32_t smc91c111_readw(void *opaque, target_phys_addr_t offset)
 579{
 580    uint32_t val;
 581    val = smc91c111_readb(opaque, offset);
 582    val |= smc91c111_readb(opaque, offset + 1) << 8;
 583    return val;
 584}
 585
 586static uint32_t smc91c111_readl(void *opaque, target_phys_addr_t offset)
 587{
 588    uint32_t val;
 589    val = smc91c111_readw(opaque, offset);
 590    val |= smc91c111_readw(opaque, offset + 2) << 16;
 591    return val;
 592}
 593
 594static int smc91c111_can_receive(void *opaque)
 595{
 596    smc91c111_state *s = (smc91c111_state *)opaque;
 597
 598    if ((s->rcr & RCR_RXEN) == 0 || (s->rcr & RCR_SOFT_RST))
 599        return 1;
 600    if (s->allocated == (1 << NUM_PACKETS) - 1)
 601        return 0;
 602    return 1;
 603}
 604
 605static void smc91c111_receive(void *opaque, const uint8_t *buf, int size)
 606{
 607    smc91c111_state *s = (smc91c111_state *)opaque;
 608    int status;
 609    int packetsize;
 610    uint32_t crc;
 611    int packetnum;
 612    uint8_t *p;
 613
 614    if ((s->rcr & RCR_RXEN) == 0 || (s->rcr & RCR_SOFT_RST))
 615        return;
 616    /* Short packets are padded with zeros.  Receiving a packet
 617       < 64 bytes long is considered an error condition.  */
 618    if (size < 64)
 619        packetsize = 64;
 620    else
 621        packetsize = (size & ~1);
 622    packetsize += 6;
 623    crc = (s->rcr & RCR_STRIP_CRC) == 0;
 624    if (crc)
 625        packetsize += 4;
 626    /* TODO: Flag overrun and receive errors.  */
 627    if (packetsize > 2048)
 628        return;
 629    packetnum = smc91c111_allocate_packet(s);
 630    if (packetnum == 0x80)
 631        return;
 632    s->rx_fifo[s->rx_fifo_len++] = packetnum;
 633
 634    p = &s->data[packetnum][0];
 635    /* ??? Multicast packets?  */
 636    status = 0;
 637    if (size > 1518)
 638        status |= RS_TOOLONG;
 639    if (size & 1)
 640        status |= RS_ODDFRAME;
 641    *(p++) = status & 0xff;
 642    *(p++) = status >> 8;
 643    *(p++) = packetsize & 0xff;
 644    *(p++) = packetsize >> 8;
 645    memcpy(p, buf, size & ~1);
 646    p += (size & ~1);
 647    /* Pad short packets.  */
 648    if (size < 64) {
 649        int pad;
 650
 651        if (size & 1)
 652            *(p++) = buf[size - 1];
 653        pad = 64 - size;
 654        memset(p, 0, pad);
 655        p += pad;
 656        size = 64;
 657    }
 658    /* It's not clear if the CRC should go before or after the last byte in
 659       odd sized packets.  Linux disables the CRC, so that's no help.
 660       The pictures in the documentation show the CRC aligned on a 16-bit
 661       boundary before the last odd byte, so that's what we do.  */
 662    if (crc) {
 663        crc = crc32(~0, buf, size);
 664        *(p++) = crc & 0xff; crc >>= 8;
 665        *(p++) = crc & 0xff; crc >>= 8;
 666        *(p++) = crc & 0xff; crc >>= 8;
 667        *(p++) = crc & 0xff; crc >>= 8;
 668    }
 669    if (size & 1) {
 670        *(p++) = buf[size - 1];
 671        *(p++) = 0x60;
 672    } else {
 673        *(p++) = 0;
 674        *(p++) = 0x40;
 675    }
 676    /* TODO: Raise early RX interrupt?  */
 677    s->int_level |= INT_RCV;
 678    smc91c111_update(s);
 679}
 680
 681static CPUReadMemoryFunc *smc91c111_readfn[] = {
 682    smc91c111_readb,
 683    smc91c111_readw,
 684    smc91c111_readl
 685};
 686
 687static CPUWriteMemoryFunc *smc91c111_writefn[] = {
 688    smc91c111_writeb,
 689    smc91c111_writew,
 690    smc91c111_writel
 691};
 692
 693void smc91c111_init(NICInfo *nd, uint32_t base, qemu_irq irq)
 694{
 695    smc91c111_state *s;
 696    int iomemtype;
 697
 698    qemu_check_nic_model(nd, "smc91c111");
 699
 700    s = (smc91c111_state *)qemu_mallocz(sizeof(smc91c111_state));
 701    iomemtype = cpu_register_io_memory(0, smc91c111_readfn,
 702                                       smc91c111_writefn, s);
 703    cpu_register_physical_memory(base, 16, iomemtype);
 704    s->irq = irq;
 705    memcpy(s->macaddr, nd->macaddr, 6);
 706
 707    smc91c111_reset(s);
 708
 709    s->vc = qemu_new_vlan_client(nd->vlan, nd->model, nd->name,
 710                                 smc91c111_receive, smc91c111_can_receive, s);
 711    qemu_format_nic_info_str(s->vc, s->macaddr);
 712    /* ??? Save/restore.  */
 713}
 714