xref: /openbmc/qemu/hw/net/smc91c111.c (revision aead95c7fafdac3fe8380c1e9f1be38122eb1b7e)
149ab747fSPaolo Bonzini /*
249ab747fSPaolo Bonzini  * SMSC 91C111 Ethernet interface emulation
349ab747fSPaolo Bonzini  *
449ab747fSPaolo Bonzini  * Copyright (c) 2005 CodeSourcery, LLC.
549ab747fSPaolo Bonzini  * Written by Paul Brook
649ab747fSPaolo Bonzini  *
749ab747fSPaolo Bonzini  * This code is licensed under the GPL
849ab747fSPaolo Bonzini  */
949ab747fSPaolo Bonzini 
10e8d40465SPeter Maydell #include "qemu/osdep.h"
1149ab747fSPaolo Bonzini #include "hw/sysbus.h"
12d6454270SMarkus Armbruster #include "migration/vmstate.h"
1349ab747fSPaolo Bonzini #include "net/net.h"
1464552b6bSMarkus Armbruster #include "hw/irq.h"
15437cc27dSPhilippe Mathieu-Daudé #include "hw/net/smc91c111.h"
16a27bd6c7SMarkus Armbruster #include "hw/qdev-properties.h"
173e80f690SMarkus Armbruster #include "qapi/error.h"
18b9992d12SPhilippe Mathieu-Daudé #include "qemu/log.h"
190b8fa32fSMarkus Armbruster #include "qemu/module.h"
205691f477SMichael Tokarev #include <zlib.h> /* for crc32 */
21db1015e9SEduardo Habkost #include "qom/object.h"
2249ab747fSPaolo Bonzini 
2349ab747fSPaolo Bonzini /* Number of 2k memory pages available.  */
2449ab747fSPaolo Bonzini #define NUM_PACKETS 4
2549ab747fSPaolo Bonzini 
26926d152eSAndreas Färber #define TYPE_SMC91C111 "smc91c111"
278063396bSEduardo Habkost OBJECT_DECLARE_SIMPLE_TYPE(smc91c111_state, SMC91C111)
28926d152eSAndreas Färber 
29db1015e9SEduardo Habkost struct smc91c111_state {
30926d152eSAndreas Färber     SysBusDevice parent_obj;
31926d152eSAndreas Färber 
3249ab747fSPaolo Bonzini     NICState *nic;
3349ab747fSPaolo Bonzini     NICConf conf;
3449ab747fSPaolo Bonzini     uint16_t tcr;
3549ab747fSPaolo Bonzini     uint16_t rcr;
3649ab747fSPaolo Bonzini     uint16_t cr;
3749ab747fSPaolo Bonzini     uint16_t ctr;
3849ab747fSPaolo Bonzini     uint16_t gpr;
3949ab747fSPaolo Bonzini     uint16_t ptr;
4049ab747fSPaolo Bonzini     uint16_t ercv;
4149ab747fSPaolo Bonzini     qemu_irq irq;
4249ab747fSPaolo Bonzini     int bank;
4349ab747fSPaolo Bonzini     int packet_num;
4449ab747fSPaolo Bonzini     int tx_alloc;
4549ab747fSPaolo Bonzini     /* Bitmask of allocated packets.  */
4649ab747fSPaolo Bonzini     int allocated;
4749ab747fSPaolo Bonzini     int tx_fifo_len;
4849ab747fSPaolo Bonzini     int tx_fifo[NUM_PACKETS];
4949ab747fSPaolo Bonzini     int rx_fifo_len;
5049ab747fSPaolo Bonzini     int rx_fifo[NUM_PACKETS];
5149ab747fSPaolo Bonzini     int tx_fifo_done_len;
5249ab747fSPaolo Bonzini     int tx_fifo_done[NUM_PACKETS];
5349ab747fSPaolo Bonzini     /* Packet buffer memory.  */
5449ab747fSPaolo Bonzini     uint8_t data[NUM_PACKETS][2048];
5549ab747fSPaolo Bonzini     uint8_t int_level;
5649ab747fSPaolo Bonzini     uint8_t int_mask;
5749ab747fSPaolo Bonzini     MemoryRegion mmio;
58db1015e9SEduardo Habkost };
5949ab747fSPaolo Bonzini 
6049ab747fSPaolo Bonzini static const VMStateDescription vmstate_smc91c111 = {
6149ab747fSPaolo Bonzini     .name = "smc91c111",
6249ab747fSPaolo Bonzini     .version_id = 1,
6349ab747fSPaolo Bonzini     .minimum_version_id = 1,
641de81b42SRichard Henderson     .fields = (const VMStateField[]) {
6549ab747fSPaolo Bonzini         VMSTATE_UINT16(tcr, smc91c111_state),
6649ab747fSPaolo Bonzini         VMSTATE_UINT16(rcr, smc91c111_state),
6749ab747fSPaolo Bonzini         VMSTATE_UINT16(cr, smc91c111_state),
6849ab747fSPaolo Bonzini         VMSTATE_UINT16(ctr, smc91c111_state),
6949ab747fSPaolo Bonzini         VMSTATE_UINT16(gpr, smc91c111_state),
7049ab747fSPaolo Bonzini         VMSTATE_UINT16(ptr, smc91c111_state),
7149ab747fSPaolo Bonzini         VMSTATE_UINT16(ercv, smc91c111_state),
7249ab747fSPaolo Bonzini         VMSTATE_INT32(bank, smc91c111_state),
7349ab747fSPaolo Bonzini         VMSTATE_INT32(packet_num, smc91c111_state),
7449ab747fSPaolo Bonzini         VMSTATE_INT32(tx_alloc, smc91c111_state),
7549ab747fSPaolo Bonzini         VMSTATE_INT32(allocated, smc91c111_state),
7649ab747fSPaolo Bonzini         VMSTATE_INT32(tx_fifo_len, smc91c111_state),
7749ab747fSPaolo Bonzini         VMSTATE_INT32_ARRAY(tx_fifo, smc91c111_state, NUM_PACKETS),
7849ab747fSPaolo Bonzini         VMSTATE_INT32(rx_fifo_len, smc91c111_state),
7949ab747fSPaolo Bonzini         VMSTATE_INT32_ARRAY(rx_fifo, smc91c111_state, NUM_PACKETS),
8049ab747fSPaolo Bonzini         VMSTATE_INT32(tx_fifo_done_len, smc91c111_state),
8149ab747fSPaolo Bonzini         VMSTATE_INT32_ARRAY(tx_fifo_done, smc91c111_state, NUM_PACKETS),
8249ab747fSPaolo Bonzini         VMSTATE_BUFFER_UNSAFE(data, smc91c111_state, 0, NUM_PACKETS * 2048),
8349ab747fSPaolo Bonzini         VMSTATE_UINT8(int_level, smc91c111_state),
8449ab747fSPaolo Bonzini         VMSTATE_UINT8(int_mask, smc91c111_state),
8549ab747fSPaolo Bonzini         VMSTATE_END_OF_LIST()
8649ab747fSPaolo Bonzini     }
8749ab747fSPaolo Bonzini };
8849ab747fSPaolo Bonzini 
8949ab747fSPaolo Bonzini #define RCR_SOFT_RST  0x8000
9049ab747fSPaolo Bonzini #define RCR_STRIP_CRC 0x0200
9149ab747fSPaolo Bonzini #define RCR_RXEN      0x0100
9249ab747fSPaolo Bonzini 
9349ab747fSPaolo Bonzini #define TCR_EPH_LOOP  0x2000
9449ab747fSPaolo Bonzini #define TCR_NOCRC     0x0100
9549ab747fSPaolo Bonzini #define TCR_PAD_EN    0x0080
9649ab747fSPaolo Bonzini #define TCR_FORCOL    0x0004
9749ab747fSPaolo Bonzini #define TCR_LOOP      0x0002
9849ab747fSPaolo Bonzini #define TCR_TXEN      0x0001
9949ab747fSPaolo Bonzini 
10049ab747fSPaolo Bonzini #define INT_MD        0x80
10149ab747fSPaolo Bonzini #define INT_ERCV      0x40
10249ab747fSPaolo Bonzini #define INT_EPH       0x20
10349ab747fSPaolo Bonzini #define INT_RX_OVRN   0x10
10449ab747fSPaolo Bonzini #define INT_ALLOC     0x08
10549ab747fSPaolo Bonzini #define INT_TX_EMPTY  0x04
10649ab747fSPaolo Bonzini #define INT_TX        0x02
10749ab747fSPaolo Bonzini #define INT_RCV       0x01
10849ab747fSPaolo Bonzini 
10949ab747fSPaolo Bonzini #define CTR_AUTO_RELEASE  0x0800
11049ab747fSPaolo Bonzini #define CTR_RELOAD        0x0002
11149ab747fSPaolo Bonzini #define CTR_STORE         0x0001
11249ab747fSPaolo Bonzini 
11349ab747fSPaolo Bonzini #define RS_ALGNERR      0x8000
11449ab747fSPaolo Bonzini #define RS_BRODCAST     0x4000
11549ab747fSPaolo Bonzini #define RS_BADCRC       0x2000
11649ab747fSPaolo Bonzini #define RS_ODDFRAME     0x1000
11749ab747fSPaolo Bonzini #define RS_TOOLONG      0x0800
11849ab747fSPaolo Bonzini #define RS_TOOSHORT     0x0400
11949ab747fSPaolo Bonzini #define RS_MULTICAST    0x0001
12049ab747fSPaolo Bonzini 
12149ab747fSPaolo Bonzini /* Update interrupt status.  */
smc91c111_update(smc91c111_state * s)12249ab747fSPaolo Bonzini static void smc91c111_update(smc91c111_state *s)
12349ab747fSPaolo Bonzini {
12449ab747fSPaolo Bonzini     int level;
12549ab747fSPaolo Bonzini 
12649ab747fSPaolo Bonzini     if (s->tx_fifo_len == 0)
12749ab747fSPaolo Bonzini         s->int_level |= INT_TX_EMPTY;
12849ab747fSPaolo Bonzini     if (s->tx_fifo_done_len != 0)
12949ab747fSPaolo Bonzini         s->int_level |= INT_TX;
13049ab747fSPaolo Bonzini     level = (s->int_level & s->int_mask) != 0;
13149ab747fSPaolo Bonzini     qemu_set_irq(s->irq, level);
13249ab747fSPaolo Bonzini }
13349ab747fSPaolo Bonzini 
smc91c111_can_receive(smc91c111_state * s)1340002c3a6SPhilippe Mathieu-Daudé static bool smc91c111_can_receive(smc91c111_state *s)
1358d06b149SPeter Crosthwaite {
1368d06b149SPeter Crosthwaite     if ((s->rcr & RCR_RXEN) == 0 || (s->rcr & RCR_SOFT_RST)) {
1370002c3a6SPhilippe Mathieu-Daudé         return true;
1388d06b149SPeter Crosthwaite     }
139e62cb54cSPeter Crosthwaite     if (s->allocated == (1 << NUM_PACKETS) - 1 ||
140e62cb54cSPeter Crosthwaite         s->rx_fifo_len == NUM_PACKETS) {
1410002c3a6SPhilippe Mathieu-Daudé         return false;
1428d06b149SPeter Crosthwaite     }
1430002c3a6SPhilippe Mathieu-Daudé     return true;
1448d06b149SPeter Crosthwaite }
1458d06b149SPeter Crosthwaite 
smc91c111_flush_queued_packets(smc91c111_state * s)1468d06b149SPeter Crosthwaite static inline void smc91c111_flush_queued_packets(smc91c111_state *s)
1478d06b149SPeter Crosthwaite {
1488d06b149SPeter Crosthwaite     if (smc91c111_can_receive(s)) {
1498d06b149SPeter Crosthwaite         qemu_flush_queued_packets(qemu_get_queue(s->nic));
1508d06b149SPeter Crosthwaite     }
1518d06b149SPeter Crosthwaite }
1528d06b149SPeter Crosthwaite 
15349ab747fSPaolo Bonzini /* Try to allocate a packet.  Returns 0x80 on failure.  */
smc91c111_allocate_packet(smc91c111_state * s)15449ab747fSPaolo Bonzini static int smc91c111_allocate_packet(smc91c111_state *s)
15549ab747fSPaolo Bonzini {
15649ab747fSPaolo Bonzini     int i;
15749ab747fSPaolo Bonzini     if (s->allocated == (1 << NUM_PACKETS) - 1) {
15849ab747fSPaolo Bonzini         return 0x80;
15949ab747fSPaolo Bonzini     }
16049ab747fSPaolo Bonzini 
16149ab747fSPaolo Bonzini     for (i = 0; i < NUM_PACKETS; i++) {
16249ab747fSPaolo Bonzini         if ((s->allocated & (1 << i)) == 0)
16349ab747fSPaolo Bonzini             break;
16449ab747fSPaolo Bonzini     }
16549ab747fSPaolo Bonzini     s->allocated |= 1 << i;
16649ab747fSPaolo Bonzini     return i;
16749ab747fSPaolo Bonzini }
16849ab747fSPaolo Bonzini 
16949ab747fSPaolo Bonzini 
17049ab747fSPaolo Bonzini /* Process a pending TX allocate.  */
smc91c111_tx_alloc(smc91c111_state * s)17149ab747fSPaolo Bonzini static void smc91c111_tx_alloc(smc91c111_state *s)
17249ab747fSPaolo Bonzini {
17349ab747fSPaolo Bonzini     s->tx_alloc = smc91c111_allocate_packet(s);
17449ab747fSPaolo Bonzini     if (s->tx_alloc == 0x80)
17549ab747fSPaolo Bonzini         return;
17649ab747fSPaolo Bonzini     s->int_level |= INT_ALLOC;
17749ab747fSPaolo Bonzini     smc91c111_update(s);
17849ab747fSPaolo Bonzini }
17949ab747fSPaolo Bonzini 
18049ab747fSPaolo Bonzini /* Remove and item from the RX FIFO.  */
smc91c111_pop_rx_fifo(smc91c111_state * s)18149ab747fSPaolo Bonzini static void smc91c111_pop_rx_fifo(smc91c111_state *s)
18249ab747fSPaolo Bonzini {
18349ab747fSPaolo Bonzini     int i;
18449ab747fSPaolo Bonzini 
185*aead95c7SPeter Maydell     if (s->rx_fifo_len == 0) {
186*aead95c7SPeter Maydell         /*
187*aead95c7SPeter Maydell          * The datasheet doesn't document what the behaviour is if the
188*aead95c7SPeter Maydell          * guest tries to pop an empty RX FIFO, and there's no obvious
189*aead95c7SPeter Maydell          * error status register to report it. Just ignore the attempt.
190*aead95c7SPeter Maydell          */
191*aead95c7SPeter Maydell         return;
192*aead95c7SPeter Maydell     }
193*aead95c7SPeter Maydell 
19449ab747fSPaolo Bonzini     s->rx_fifo_len--;
19549ab747fSPaolo Bonzini     if (s->rx_fifo_len) {
19649ab747fSPaolo Bonzini         for (i = 0; i < s->rx_fifo_len; i++)
19749ab747fSPaolo Bonzini             s->rx_fifo[i] = s->rx_fifo[i + 1];
19849ab747fSPaolo Bonzini         s->int_level |= INT_RCV;
19949ab747fSPaolo Bonzini     } else {
20049ab747fSPaolo Bonzini         s->int_level &= ~INT_RCV;
20149ab747fSPaolo Bonzini     }
202e62cb54cSPeter Crosthwaite     smc91c111_flush_queued_packets(s);
20349ab747fSPaolo Bonzini     smc91c111_update(s);
20449ab747fSPaolo Bonzini }
20549ab747fSPaolo Bonzini 
20649ab747fSPaolo Bonzini /* Remove an item from the TX completion FIFO.  */
smc91c111_pop_tx_fifo_done(smc91c111_state * s)20749ab747fSPaolo Bonzini static void smc91c111_pop_tx_fifo_done(smc91c111_state *s)
20849ab747fSPaolo Bonzini {
20949ab747fSPaolo Bonzini     int i;
21049ab747fSPaolo Bonzini 
21149ab747fSPaolo Bonzini     if (s->tx_fifo_done_len == 0)
21249ab747fSPaolo Bonzini         return;
21349ab747fSPaolo Bonzini     s->tx_fifo_done_len--;
21449ab747fSPaolo Bonzini     for (i = 0; i < s->tx_fifo_done_len; i++)
21549ab747fSPaolo Bonzini         s->tx_fifo_done[i] = s->tx_fifo_done[i + 1];
21649ab747fSPaolo Bonzini }
21749ab747fSPaolo Bonzini 
21849ab747fSPaolo Bonzini /* Release the memory allocated to a packet.  */
smc91c111_release_packet(smc91c111_state * s,int packet)21949ab747fSPaolo Bonzini static void smc91c111_release_packet(smc91c111_state *s, int packet)
22049ab747fSPaolo Bonzini {
22149ab747fSPaolo Bonzini     s->allocated &= ~(1 << packet);
22249ab747fSPaolo Bonzini     if (s->tx_alloc == 0x80)
22349ab747fSPaolo Bonzini         smc91c111_tx_alloc(s);
2248d06b149SPeter Crosthwaite     smc91c111_flush_queued_packets(s);
22549ab747fSPaolo Bonzini }
22649ab747fSPaolo Bonzini 
22749ab747fSPaolo Bonzini /* Flush the TX FIFO.  */
smc91c111_do_tx(smc91c111_state * s)22849ab747fSPaolo Bonzini static void smc91c111_do_tx(smc91c111_state *s)
22949ab747fSPaolo Bonzini {
23049ab747fSPaolo Bonzini     int i;
23149ab747fSPaolo Bonzini     int len;
23249ab747fSPaolo Bonzini     int control;
23349ab747fSPaolo Bonzini     int packetnum;
23449ab747fSPaolo Bonzini     uint8_t *p;
23549ab747fSPaolo Bonzini 
23649ab747fSPaolo Bonzini     if ((s->tcr & TCR_TXEN) == 0)
23749ab747fSPaolo Bonzini         return;
23849ab747fSPaolo Bonzini     if (s->tx_fifo_len == 0)
23949ab747fSPaolo Bonzini         return;
24049ab747fSPaolo Bonzini     for (i = 0; i < s->tx_fifo_len; i++) {
24149ab747fSPaolo Bonzini         packetnum = s->tx_fifo[i];
24249ab747fSPaolo Bonzini         p = &s->data[packetnum][0];
24349ab747fSPaolo Bonzini         /* Set status word.  */
24449ab747fSPaolo Bonzini         *(p++) = 0x01;
24549ab747fSPaolo Bonzini         *(p++) = 0x40;
24649ab747fSPaolo Bonzini         len = *(p++);
24749ab747fSPaolo Bonzini         len |= ((int)*(p++)) << 8;
24849ab747fSPaolo Bonzini         len -= 6;
24949ab747fSPaolo Bonzini         control = p[len + 1];
25049ab747fSPaolo Bonzini         if (control & 0x20)
25149ab747fSPaolo Bonzini             len++;
25249ab747fSPaolo Bonzini         /* ??? This overwrites the data following the buffer.
25349ab747fSPaolo Bonzini            Don't know what real hardware does.  */
25449ab747fSPaolo Bonzini         if (len < 64 && (s->tcr & TCR_PAD_EN)) {
25549ab747fSPaolo Bonzini             memset(p + len, 0, 64 - len);
25649ab747fSPaolo Bonzini             len = 64;
25749ab747fSPaolo Bonzini         }
25849ab747fSPaolo Bonzini #if 0
25949ab747fSPaolo Bonzini         {
26049ab747fSPaolo Bonzini             int add_crc;
26149ab747fSPaolo Bonzini 
26249ab747fSPaolo Bonzini             /* The card is supposed to append the CRC to the frame.
26349ab747fSPaolo Bonzini                However none of the other network traffic has the CRC
26449ab747fSPaolo Bonzini                appended.  Suspect this is low level ethernet detail we
26549ab747fSPaolo Bonzini                don't need to worry about.  */
26649ab747fSPaolo Bonzini             add_crc = (control & 0x10) || (s->tcr & TCR_NOCRC) == 0;
26749ab747fSPaolo Bonzini             if (add_crc) {
26849ab747fSPaolo Bonzini                 uint32_t crc;
26949ab747fSPaolo Bonzini 
27049ab747fSPaolo Bonzini                 crc = crc32(~0, p, len);
27149ab747fSPaolo Bonzini                 memcpy(p + len, &crc, 4);
27249ab747fSPaolo Bonzini                 len += 4;
27349ab747fSPaolo Bonzini             }
27449ab747fSPaolo Bonzini         }
27549ab747fSPaolo Bonzini #endif
27649ab747fSPaolo Bonzini         if (s->ctr & CTR_AUTO_RELEASE)
27749ab747fSPaolo Bonzini             /* Race?  */
27849ab747fSPaolo Bonzini             smc91c111_release_packet(s, packetnum);
27949ab747fSPaolo Bonzini         else if (s->tx_fifo_done_len < NUM_PACKETS)
28049ab747fSPaolo Bonzini             s->tx_fifo_done[s->tx_fifo_done_len++] = packetnum;
28149ab747fSPaolo Bonzini         qemu_send_packet(qemu_get_queue(s->nic), p, len);
28249ab747fSPaolo Bonzini     }
28349ab747fSPaolo Bonzini     s->tx_fifo_len = 0;
28449ab747fSPaolo Bonzini     smc91c111_update(s);
28549ab747fSPaolo Bonzini }
28649ab747fSPaolo Bonzini 
28749ab747fSPaolo Bonzini /* Add a packet to the TX FIFO.  */
smc91c111_queue_tx(smc91c111_state * s,int packet)28849ab747fSPaolo Bonzini static void smc91c111_queue_tx(smc91c111_state *s, int packet)
28949ab747fSPaolo Bonzini {
29049ab747fSPaolo Bonzini     if (s->tx_fifo_len == NUM_PACKETS)
29149ab747fSPaolo Bonzini         return;
29249ab747fSPaolo Bonzini     s->tx_fifo[s->tx_fifo_len++] = packet;
29349ab747fSPaolo Bonzini     smc91c111_do_tx(s);
29449ab747fSPaolo Bonzini }
29549ab747fSPaolo Bonzini 
smc91c111_reset(DeviceState * dev)29649ab747fSPaolo Bonzini static void smc91c111_reset(DeviceState *dev)
29749ab747fSPaolo Bonzini {
298926d152eSAndreas Färber     smc91c111_state *s = SMC91C111(dev);
299926d152eSAndreas Färber 
30049ab747fSPaolo Bonzini     s->bank = 0;
30149ab747fSPaolo Bonzini     s->tx_fifo_len = 0;
30249ab747fSPaolo Bonzini     s->tx_fifo_done_len = 0;
30349ab747fSPaolo Bonzini     s->rx_fifo_len = 0;
30449ab747fSPaolo Bonzini     s->allocated = 0;
30549ab747fSPaolo Bonzini     s->packet_num = 0;
30649ab747fSPaolo Bonzini     s->tx_alloc = 0;
30749ab747fSPaolo Bonzini     s->tcr = 0;
30849ab747fSPaolo Bonzini     s->rcr = 0;
30949ab747fSPaolo Bonzini     s->cr = 0xa0b1;
31049ab747fSPaolo Bonzini     s->ctr = 0x1210;
31149ab747fSPaolo Bonzini     s->ptr = 0;
31249ab747fSPaolo Bonzini     s->ercv = 0x1f;
31349ab747fSPaolo Bonzini     s->int_level = INT_TX_EMPTY;
31449ab747fSPaolo Bonzini     s->int_mask = 0;
31549ab747fSPaolo Bonzini     smc91c111_update(s);
31649ab747fSPaolo Bonzini }
31749ab747fSPaolo Bonzini 
31849ab747fSPaolo Bonzini #define SET_LOW(name, val) s->name = (s->name & 0xff00) | val
31949ab747fSPaolo Bonzini #define SET_HIGH(name, val) s->name = (s->name & 0xff) | (val << 8)
32049ab747fSPaolo Bonzini 
smc91c111_writeb(void * opaque,hwaddr offset,uint32_t value)32149ab747fSPaolo Bonzini static void smc91c111_writeb(void *opaque, hwaddr offset,
32249ab747fSPaolo Bonzini                              uint32_t value)
32349ab747fSPaolo Bonzini {
32449ab747fSPaolo Bonzini     smc91c111_state *s = (smc91c111_state *)opaque;
32549ab747fSPaolo Bonzini 
32649ab747fSPaolo Bonzini     offset = offset & 0xf;
32749ab747fSPaolo Bonzini     if (offset == 14) {
32849ab747fSPaolo Bonzini         s->bank = value;
32949ab747fSPaolo Bonzini         return;
33049ab747fSPaolo Bonzini     }
33149ab747fSPaolo Bonzini     if (offset == 15)
33249ab747fSPaolo Bonzini         return;
33349ab747fSPaolo Bonzini     switch (s->bank) {
33449ab747fSPaolo Bonzini     case 0:
33549ab747fSPaolo Bonzini         switch (offset) {
33649ab747fSPaolo Bonzini         case 0: /* TCR */
33749ab747fSPaolo Bonzini             SET_LOW(tcr, value);
33849ab747fSPaolo Bonzini             return;
33949ab747fSPaolo Bonzini         case 1:
34049ab747fSPaolo Bonzini             SET_HIGH(tcr, value);
34149ab747fSPaolo Bonzini             return;
34249ab747fSPaolo Bonzini         case 4: /* RCR */
34349ab747fSPaolo Bonzini             SET_LOW(rcr, value);
34449ab747fSPaolo Bonzini             return;
34549ab747fSPaolo Bonzini         case 5:
34649ab747fSPaolo Bonzini             SET_HIGH(rcr, value);
347926d152eSAndreas Färber             if (s->rcr & RCR_SOFT_RST) {
348926d152eSAndreas Färber                 smc91c111_reset(DEVICE(s));
349926d152eSAndreas Färber             }
350271a234aSPeter Crosthwaite             smc91c111_flush_queued_packets(s);
35149ab747fSPaolo Bonzini             return;
35249ab747fSPaolo Bonzini         case 10: case 11: /* RPCR */
35349ab747fSPaolo Bonzini             /* Ignored */
35449ab747fSPaolo Bonzini             return;
35549ab747fSPaolo Bonzini         case 12: case 13: /* Reserved */
35649ab747fSPaolo Bonzini             return;
35749ab747fSPaolo Bonzini         }
35849ab747fSPaolo Bonzini         break;
35949ab747fSPaolo Bonzini 
36049ab747fSPaolo Bonzini     case 1:
36149ab747fSPaolo Bonzini         switch (offset) {
36249ab747fSPaolo Bonzini         case 0: /* CONFIG */
36349ab747fSPaolo Bonzini             SET_LOW(cr, value);
36449ab747fSPaolo Bonzini             return;
36549ab747fSPaolo Bonzini         case 1:
36649ab747fSPaolo Bonzini             SET_HIGH(cr,value);
36749ab747fSPaolo Bonzini             return;
36849ab747fSPaolo Bonzini         case 2: case 3: /* BASE */
36949ab747fSPaolo Bonzini         case 4: case 5: case 6: case 7: case 8: case 9: /* IA */
37049ab747fSPaolo Bonzini             /* Not implemented.  */
37149ab747fSPaolo Bonzini             return;
3722431f4f1SMichael Tokarev         case 10: /* General Purpose */
37349ab747fSPaolo Bonzini             SET_LOW(gpr, value);
37449ab747fSPaolo Bonzini             return;
37549ab747fSPaolo Bonzini         case 11:
37649ab747fSPaolo Bonzini             SET_HIGH(gpr, value);
37749ab747fSPaolo Bonzini             return;
37849ab747fSPaolo Bonzini         case 12: /* Control */
379637e5d86SPhilippe Mathieu-Daudé             if (value & 1) {
380637e5d86SPhilippe Mathieu-Daudé                 qemu_log_mask(LOG_UNIMP,
381637e5d86SPhilippe Mathieu-Daudé                               "smc91c111: EEPROM store not implemented\n");
382637e5d86SPhilippe Mathieu-Daudé             }
383637e5d86SPhilippe Mathieu-Daudé             if (value & 2) {
384637e5d86SPhilippe Mathieu-Daudé                 qemu_log_mask(LOG_UNIMP,
385637e5d86SPhilippe Mathieu-Daudé                               "smc91c111: EEPROM reload not implemented\n");
386637e5d86SPhilippe Mathieu-Daudé             }
38749ab747fSPaolo Bonzini             value &= ~3;
38849ab747fSPaolo Bonzini             SET_LOW(ctr, value);
38949ab747fSPaolo Bonzini             return;
39049ab747fSPaolo Bonzini         case 13:
39149ab747fSPaolo Bonzini             SET_HIGH(ctr, value);
39249ab747fSPaolo Bonzini             return;
39349ab747fSPaolo Bonzini         }
39449ab747fSPaolo Bonzini         break;
39549ab747fSPaolo Bonzini 
39649ab747fSPaolo Bonzini     case 2:
39749ab747fSPaolo Bonzini         switch (offset) {
39849ab747fSPaolo Bonzini         case 0: /* MMU Command */
39949ab747fSPaolo Bonzini             switch (value >> 5) {
40049ab747fSPaolo Bonzini             case 0: /* no-op */
40149ab747fSPaolo Bonzini                 break;
40249ab747fSPaolo Bonzini             case 1: /* Allocate for TX.  */
40349ab747fSPaolo Bonzini                 s->tx_alloc = 0x80;
40449ab747fSPaolo Bonzini                 s->int_level &= ~INT_ALLOC;
40549ab747fSPaolo Bonzini                 smc91c111_update(s);
40649ab747fSPaolo Bonzini                 smc91c111_tx_alloc(s);
40749ab747fSPaolo Bonzini                 break;
40849ab747fSPaolo Bonzini             case 2: /* Reset MMU.  */
40949ab747fSPaolo Bonzini                 s->allocated = 0;
41049ab747fSPaolo Bonzini                 s->tx_fifo_len = 0;
41149ab747fSPaolo Bonzini                 s->tx_fifo_done_len = 0;
41249ab747fSPaolo Bonzini                 s->rx_fifo_len = 0;
41349ab747fSPaolo Bonzini                 s->tx_alloc = 0;
41449ab747fSPaolo Bonzini                 break;
41549ab747fSPaolo Bonzini             case 3: /* Remove from RX FIFO.  */
41649ab747fSPaolo Bonzini                 smc91c111_pop_rx_fifo(s);
41749ab747fSPaolo Bonzini                 break;
41849ab747fSPaolo Bonzini             case 4: /* Remove from RX FIFO and release.  */
41949ab747fSPaolo Bonzini                 if (s->rx_fifo_len > 0) {
42049ab747fSPaolo Bonzini                     smc91c111_release_packet(s, s->rx_fifo[0]);
42149ab747fSPaolo Bonzini                 }
42249ab747fSPaolo Bonzini                 smc91c111_pop_rx_fifo(s);
42349ab747fSPaolo Bonzini                 break;
42449ab747fSPaolo Bonzini             case 5: /* Release.  */
42549ab747fSPaolo Bonzini                 smc91c111_release_packet(s, s->packet_num);
42649ab747fSPaolo Bonzini                 break;
42749ab747fSPaolo Bonzini             case 6: /* Add to TX FIFO.  */
42849ab747fSPaolo Bonzini                 smc91c111_queue_tx(s, s->packet_num);
42949ab747fSPaolo Bonzini                 break;
43049ab747fSPaolo Bonzini             case 7: /* Reset TX FIFO.  */
43149ab747fSPaolo Bonzini                 s->tx_fifo_len = 0;
43249ab747fSPaolo Bonzini                 s->tx_fifo_done_len = 0;
43349ab747fSPaolo Bonzini                 break;
43449ab747fSPaolo Bonzini             }
43549ab747fSPaolo Bonzini             return;
43649ab747fSPaolo Bonzini         case 1:
43749ab747fSPaolo Bonzini             /* Ignore.  */
43849ab747fSPaolo Bonzini             return;
43949ab747fSPaolo Bonzini         case 2: /* Packet Number Register */
44049ab747fSPaolo Bonzini             s->packet_num = value;
44149ab747fSPaolo Bonzini             return;
44249ab747fSPaolo Bonzini         case 3: case 4: case 5:
44349ab747fSPaolo Bonzini             /* Should be readonly, but linux writes to them anyway. Ignore.  */
44449ab747fSPaolo Bonzini             return;
44549ab747fSPaolo Bonzini         case 6: /* Pointer */
44649ab747fSPaolo Bonzini             SET_LOW(ptr, value);
44749ab747fSPaolo Bonzini             return;
44849ab747fSPaolo Bonzini         case 7:
44949ab747fSPaolo Bonzini             SET_HIGH(ptr, value);
45049ab747fSPaolo Bonzini             return;
45149ab747fSPaolo Bonzini         case 8: case 9: case 10: case 11: /* Data */
45249ab747fSPaolo Bonzini             {
45349ab747fSPaolo Bonzini                 int p;
45449ab747fSPaolo Bonzini                 int n;
45549ab747fSPaolo Bonzini 
45649ab747fSPaolo Bonzini                 if (s->ptr & 0x8000)
45749ab747fSPaolo Bonzini                     n = s->rx_fifo[0];
45849ab747fSPaolo Bonzini                 else
45949ab747fSPaolo Bonzini                     n = s->packet_num;
46049ab747fSPaolo Bonzini                 p = s->ptr & 0x07ff;
46149ab747fSPaolo Bonzini                 if (s->ptr & 0x4000) {
46249ab747fSPaolo Bonzini                     s->ptr = (s->ptr & 0xf800) | ((s->ptr + 1) & 0x7ff);
46349ab747fSPaolo Bonzini                 } else {
46449ab747fSPaolo Bonzini                     p += (offset & 3);
46549ab747fSPaolo Bonzini                 }
46649ab747fSPaolo Bonzini                 s->data[n][p] = value;
46749ab747fSPaolo Bonzini             }
46849ab747fSPaolo Bonzini             return;
46949ab747fSPaolo Bonzini         case 12: /* Interrupt ACK.  */
47049ab747fSPaolo Bonzini             s->int_level &= ~(value & 0xd6);
47149ab747fSPaolo Bonzini             if (value & INT_TX)
47249ab747fSPaolo Bonzini                 smc91c111_pop_tx_fifo_done(s);
47349ab747fSPaolo Bonzini             smc91c111_update(s);
47449ab747fSPaolo Bonzini             return;
47549ab747fSPaolo Bonzini         case 13: /* Interrupt mask.  */
47649ab747fSPaolo Bonzini             s->int_mask = value;
47749ab747fSPaolo Bonzini             smc91c111_update(s);
47849ab747fSPaolo Bonzini             return;
47949ab747fSPaolo Bonzini         }
48049ab747fSPaolo Bonzini         break;
48149ab747fSPaolo Bonzini 
48249ab747fSPaolo Bonzini     case 3:
48349ab747fSPaolo Bonzini         switch (offset) {
48449ab747fSPaolo Bonzini         case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
48549ab747fSPaolo Bonzini             /* Multicast table.  */
48649ab747fSPaolo Bonzini             /* Not implemented.  */
48749ab747fSPaolo Bonzini             return;
48849ab747fSPaolo Bonzini         case 8: case 9: /* Management Interface.  */
48949ab747fSPaolo Bonzini             /* Not implemented.  */
49049ab747fSPaolo Bonzini             return;
49149ab747fSPaolo Bonzini         case 12: /* Early receive.  */
49249ab747fSPaolo Bonzini             s->ercv = value & 0x1f;
49349ab747fSPaolo Bonzini             return;
49449ab747fSPaolo Bonzini         case 13:
49549ab747fSPaolo Bonzini             /* Ignore.  */
49649ab747fSPaolo Bonzini             return;
49749ab747fSPaolo Bonzini         }
49849ab747fSPaolo Bonzini         break;
49949ab747fSPaolo Bonzini     }
500b9992d12SPhilippe Mathieu-Daudé     qemu_log_mask(LOG_GUEST_ERROR, "smc91c111_write(bank:%d) Illegal register"
501b9992d12SPhilippe Mathieu-Daudé                                    " 0x%" HWADDR_PRIx " = 0x%x\n",
502b9992d12SPhilippe Mathieu-Daudé                   s->bank, offset, value);
50349ab747fSPaolo Bonzini }
50449ab747fSPaolo Bonzini 
smc91c111_readb(void * opaque,hwaddr offset)50549ab747fSPaolo Bonzini static uint32_t smc91c111_readb(void *opaque, hwaddr offset)
50649ab747fSPaolo Bonzini {
50749ab747fSPaolo Bonzini     smc91c111_state *s = (smc91c111_state *)opaque;
50849ab747fSPaolo Bonzini 
50949ab747fSPaolo Bonzini     offset = offset & 0xf;
51049ab747fSPaolo Bonzini     if (offset == 14) {
51149ab747fSPaolo Bonzini         return s->bank;
51249ab747fSPaolo Bonzini     }
51349ab747fSPaolo Bonzini     if (offset == 15)
51449ab747fSPaolo Bonzini         return 0x33;
51549ab747fSPaolo Bonzini     switch (s->bank) {
51649ab747fSPaolo Bonzini     case 0:
51749ab747fSPaolo Bonzini         switch (offset) {
51849ab747fSPaolo Bonzini         case 0: /* TCR */
51949ab747fSPaolo Bonzini             return s->tcr & 0xff;
52049ab747fSPaolo Bonzini         case 1:
52149ab747fSPaolo Bonzini             return s->tcr >> 8;
52249ab747fSPaolo Bonzini         case 2: /* EPH Status */
52349ab747fSPaolo Bonzini             return 0;
52449ab747fSPaolo Bonzini         case 3:
52549ab747fSPaolo Bonzini             return 0x40;
52649ab747fSPaolo Bonzini         case 4: /* RCR */
52749ab747fSPaolo Bonzini             return s->rcr & 0xff;
52849ab747fSPaolo Bonzini         case 5:
52949ab747fSPaolo Bonzini             return s->rcr >> 8;
53049ab747fSPaolo Bonzini         case 6: /* Counter */
53149ab747fSPaolo Bonzini         case 7:
53249ab747fSPaolo Bonzini             /* Not implemented.  */
53349ab747fSPaolo Bonzini             return 0;
53449ab747fSPaolo Bonzini         case 8: /* Memory size.  */
53549ab747fSPaolo Bonzini             return NUM_PACKETS;
53649ab747fSPaolo Bonzini         case 9: /* Free memory available.  */
53749ab747fSPaolo Bonzini             {
53849ab747fSPaolo Bonzini                 int i;
53949ab747fSPaolo Bonzini                 int n;
54049ab747fSPaolo Bonzini                 n = 0;
54149ab747fSPaolo Bonzini                 for (i = 0; i < NUM_PACKETS; i++) {
54249ab747fSPaolo Bonzini                     if (s->allocated & (1 << i))
54349ab747fSPaolo Bonzini                         n++;
54449ab747fSPaolo Bonzini                 }
54549ab747fSPaolo Bonzini                 return n;
54649ab747fSPaolo Bonzini             }
54749ab747fSPaolo Bonzini         case 10: case 11: /* RPCR */
54849ab747fSPaolo Bonzini             /* Not implemented.  */
54949ab747fSPaolo Bonzini             return 0;
55049ab747fSPaolo Bonzini         case 12: case 13: /* Reserved */
55149ab747fSPaolo Bonzini             return 0;
55249ab747fSPaolo Bonzini         }
55349ab747fSPaolo Bonzini         break;
55449ab747fSPaolo Bonzini 
55549ab747fSPaolo Bonzini     case 1:
55649ab747fSPaolo Bonzini         switch (offset) {
55749ab747fSPaolo Bonzini         case 0: /* CONFIG */
55849ab747fSPaolo Bonzini             return s->cr & 0xff;
55949ab747fSPaolo Bonzini         case 1:
56049ab747fSPaolo Bonzini             return s->cr >> 8;
56149ab747fSPaolo Bonzini         case 2: case 3: /* BASE */
56249ab747fSPaolo Bonzini             /* Not implemented.  */
56349ab747fSPaolo Bonzini             return 0;
56449ab747fSPaolo Bonzini         case 4: case 5: case 6: case 7: case 8: case 9: /* IA */
56549ab747fSPaolo Bonzini             return s->conf.macaddr.a[offset - 4];
56649ab747fSPaolo Bonzini         case 10: /* General Purpose */
56749ab747fSPaolo Bonzini             return s->gpr & 0xff;
56849ab747fSPaolo Bonzini         case 11:
56949ab747fSPaolo Bonzini             return s->gpr >> 8;
57049ab747fSPaolo Bonzini         case 12: /* Control */
57149ab747fSPaolo Bonzini             return s->ctr & 0xff;
57249ab747fSPaolo Bonzini         case 13:
57349ab747fSPaolo Bonzini             return s->ctr >> 8;
57449ab747fSPaolo Bonzini         }
57549ab747fSPaolo Bonzini         break;
57649ab747fSPaolo Bonzini 
57749ab747fSPaolo Bonzini     case 2:
57849ab747fSPaolo Bonzini         switch (offset) {
57949ab747fSPaolo Bonzini         case 0: case 1: /* MMUCR Busy bit.  */
58049ab747fSPaolo Bonzini             return 0;
58149ab747fSPaolo Bonzini         case 2: /* Packet Number.  */
58249ab747fSPaolo Bonzini             return s->packet_num;
58349ab747fSPaolo Bonzini         case 3: /* Allocation Result.  */
58449ab747fSPaolo Bonzini             return s->tx_alloc;
58549ab747fSPaolo Bonzini         case 4: /* TX FIFO */
58649ab747fSPaolo Bonzini             if (s->tx_fifo_done_len == 0)
58749ab747fSPaolo Bonzini                 return 0x80;
58849ab747fSPaolo Bonzini             else
58949ab747fSPaolo Bonzini                 return s->tx_fifo_done[0];
59049ab747fSPaolo Bonzini         case 5: /* RX FIFO */
59149ab747fSPaolo Bonzini             if (s->rx_fifo_len == 0)
59249ab747fSPaolo Bonzini                 return 0x80;
59349ab747fSPaolo Bonzini             else
59449ab747fSPaolo Bonzini                 return s->rx_fifo[0];
59549ab747fSPaolo Bonzini         case 6: /* Pointer */
59649ab747fSPaolo Bonzini             return s->ptr & 0xff;
59749ab747fSPaolo Bonzini         case 7:
59849ab747fSPaolo Bonzini             return (s->ptr >> 8) & 0xf7;
59949ab747fSPaolo Bonzini         case 8: case 9: case 10: case 11: /* Data */
60049ab747fSPaolo Bonzini             {
60149ab747fSPaolo Bonzini                 int p;
60249ab747fSPaolo Bonzini                 int n;
60349ab747fSPaolo Bonzini 
60449ab747fSPaolo Bonzini                 if (s->ptr & 0x8000)
60549ab747fSPaolo Bonzini                     n = s->rx_fifo[0];
60649ab747fSPaolo Bonzini                 else
60749ab747fSPaolo Bonzini                     n = s->packet_num;
60849ab747fSPaolo Bonzini                 p = s->ptr & 0x07ff;
60949ab747fSPaolo Bonzini                 if (s->ptr & 0x4000) {
61049ab747fSPaolo Bonzini                     s->ptr = (s->ptr & 0xf800) | ((s->ptr + 1) & 0x07ff);
61149ab747fSPaolo Bonzini                 } else {
61249ab747fSPaolo Bonzini                     p += (offset & 3);
61349ab747fSPaolo Bonzini                 }
61449ab747fSPaolo Bonzini                 return s->data[n][p];
61549ab747fSPaolo Bonzini             }
61649ab747fSPaolo Bonzini         case 12: /* Interrupt status.  */
61749ab747fSPaolo Bonzini             return s->int_level;
61849ab747fSPaolo Bonzini         case 13: /* Interrupt mask.  */
61949ab747fSPaolo Bonzini             return s->int_mask;
62049ab747fSPaolo Bonzini         }
62149ab747fSPaolo Bonzini         break;
62249ab747fSPaolo Bonzini 
62349ab747fSPaolo Bonzini     case 3:
62449ab747fSPaolo Bonzini         switch (offset) {
62549ab747fSPaolo Bonzini         case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
62649ab747fSPaolo Bonzini             /* Multicast table.  */
62749ab747fSPaolo Bonzini             /* Not implemented.  */
62849ab747fSPaolo Bonzini             return 0;
62949ab747fSPaolo Bonzini         case 8: /* Management Interface.  */
63049ab747fSPaolo Bonzini             /* Not implemented.  */
63149ab747fSPaolo Bonzini             return 0x30;
63249ab747fSPaolo Bonzini         case 9:
63349ab747fSPaolo Bonzini             return 0x33;
63449ab747fSPaolo Bonzini         case 10: /* Revision.  */
63549ab747fSPaolo Bonzini             return 0x91;
63649ab747fSPaolo Bonzini         case 11:
63749ab747fSPaolo Bonzini             return 0x33;
63849ab747fSPaolo Bonzini         case 12:
63949ab747fSPaolo Bonzini             return s->ercv;
64049ab747fSPaolo Bonzini         case 13:
64149ab747fSPaolo Bonzini             return 0;
64249ab747fSPaolo Bonzini         }
64349ab747fSPaolo Bonzini         break;
64449ab747fSPaolo Bonzini     }
645b9992d12SPhilippe Mathieu-Daudé     qemu_log_mask(LOG_GUEST_ERROR, "smc91c111_read(bank:%d) Illegal register"
646b9992d12SPhilippe Mathieu-Daudé                                    " 0x%" HWADDR_PRIx "\n",
647b9992d12SPhilippe Mathieu-Daudé                   s->bank, offset);
64849ab747fSPaolo Bonzini     return 0;
64949ab747fSPaolo Bonzini }
65049ab747fSPaolo Bonzini 
smc91c111_readfn(void * opaque,hwaddr addr,unsigned size)65150a22d0dSPeter Maydell static uint64_t smc91c111_readfn(void *opaque, hwaddr addr, unsigned size)
65249ab747fSPaolo Bonzini {
65350a22d0dSPeter Maydell     int i;
65450a22d0dSPeter Maydell     uint32_t val = 0;
65550a22d0dSPeter Maydell 
65650a22d0dSPeter Maydell     for (i = 0; i < size; i++) {
65750a22d0dSPeter Maydell         val |= smc91c111_readb(opaque, addr + i) << (i * 8);
65850a22d0dSPeter Maydell     }
65950a22d0dSPeter Maydell     return val;
66049ab747fSPaolo Bonzini }
66149ab747fSPaolo Bonzini 
smc91c111_writefn(void * opaque,hwaddr addr,uint64_t value,unsigned size)66250a22d0dSPeter Maydell static void smc91c111_writefn(void *opaque, hwaddr addr,
66350a22d0dSPeter Maydell                                uint64_t value, unsigned size)
66449ab747fSPaolo Bonzini {
66550a22d0dSPeter Maydell     int i = 0;
66650a22d0dSPeter Maydell 
66749ab747fSPaolo Bonzini     /* 32-bit writes to offset 0xc only actually write to the bank select
66850a22d0dSPeter Maydell      * register (offset 0xe), so skip the first two bytes we would write.
66950a22d0dSPeter Maydell      */
67050a22d0dSPeter Maydell     if (addr == 0xc && size == 4) {
67150a22d0dSPeter Maydell         i += 2;
67249ab747fSPaolo Bonzini     }
67349ab747fSPaolo Bonzini 
67450a22d0dSPeter Maydell     for (; i < size; i++) {
67550a22d0dSPeter Maydell         smc91c111_writeb(opaque, addr + i,
67650a22d0dSPeter Maydell                          extract32(value, i * 8, 8));
67749ab747fSPaolo Bonzini     }
67849ab747fSPaolo Bonzini }
67949ab747fSPaolo Bonzini 
smc91c111_can_receive_nc(NetClientState * nc)680b8c4b67eSPhilippe Mathieu-Daudé static bool smc91c111_can_receive_nc(NetClientState *nc)
68149ab747fSPaolo Bonzini {
68249ab747fSPaolo Bonzini     smc91c111_state *s = qemu_get_nic_opaque(nc);
68349ab747fSPaolo Bonzini 
6848d06b149SPeter Crosthwaite     return smc91c111_can_receive(s);
68549ab747fSPaolo Bonzini }
68649ab747fSPaolo Bonzini 
smc91c111_receive(NetClientState * nc,const uint8_t * buf,size_t size)68749ab747fSPaolo Bonzini static ssize_t smc91c111_receive(NetClientState *nc, const uint8_t *buf, size_t size)
68849ab747fSPaolo Bonzini {
68949ab747fSPaolo Bonzini     smc91c111_state *s = qemu_get_nic_opaque(nc);
69049ab747fSPaolo Bonzini     int status;
69149ab747fSPaolo Bonzini     int packetsize;
69249ab747fSPaolo Bonzini     uint32_t crc;
69349ab747fSPaolo Bonzini     int packetnum;
69449ab747fSPaolo Bonzini     uint8_t *p;
69549ab747fSPaolo Bonzini 
69649ab747fSPaolo Bonzini     if ((s->rcr & RCR_RXEN) == 0 || (s->rcr & RCR_SOFT_RST))
69749ab747fSPaolo Bonzini         return -1;
69849ab747fSPaolo Bonzini     /* Short packets are padded with zeros.  Receiving a packet
69949ab747fSPaolo Bonzini        < 64 bytes long is considered an error condition.  */
70049ab747fSPaolo Bonzini     if (size < 64)
70149ab747fSPaolo Bonzini         packetsize = 64;
70249ab747fSPaolo Bonzini     else
70349ab747fSPaolo Bonzini         packetsize = (size & ~1);
70449ab747fSPaolo Bonzini     packetsize += 6;
70549ab747fSPaolo Bonzini     crc = (s->rcr & RCR_STRIP_CRC) == 0;
70649ab747fSPaolo Bonzini     if (crc)
70749ab747fSPaolo Bonzini         packetsize += 4;
70849ab747fSPaolo Bonzini     /* TODO: Flag overrun and receive errors.  */
70949ab747fSPaolo Bonzini     if (packetsize > 2048)
71049ab747fSPaolo Bonzini         return -1;
71149ab747fSPaolo Bonzini     packetnum = smc91c111_allocate_packet(s);
71249ab747fSPaolo Bonzini     if (packetnum == 0x80)
71349ab747fSPaolo Bonzini         return -1;
71449ab747fSPaolo Bonzini     s->rx_fifo[s->rx_fifo_len++] = packetnum;
71549ab747fSPaolo Bonzini 
71649ab747fSPaolo Bonzini     p = &s->data[packetnum][0];
71749ab747fSPaolo Bonzini     /* ??? Multicast packets?  */
71849ab747fSPaolo Bonzini     status = 0;
71949ab747fSPaolo Bonzini     if (size > 1518)
72049ab747fSPaolo Bonzini         status |= RS_TOOLONG;
72149ab747fSPaolo Bonzini     if (size & 1)
72249ab747fSPaolo Bonzini         status |= RS_ODDFRAME;
72349ab747fSPaolo Bonzini     *(p++) = status & 0xff;
72449ab747fSPaolo Bonzini     *(p++) = status >> 8;
72549ab747fSPaolo Bonzini     *(p++) = packetsize & 0xff;
72649ab747fSPaolo Bonzini     *(p++) = packetsize >> 8;
72749ab747fSPaolo Bonzini     memcpy(p, buf, size & ~1);
72849ab747fSPaolo Bonzini     p += (size & ~1);
72949ab747fSPaolo Bonzini     /* Pad short packets.  */
73049ab747fSPaolo Bonzini     if (size < 64) {
73149ab747fSPaolo Bonzini         int pad;
73249ab747fSPaolo Bonzini 
73349ab747fSPaolo Bonzini         if (size & 1)
73449ab747fSPaolo Bonzini             *(p++) = buf[size - 1];
73549ab747fSPaolo Bonzini         pad = 64 - size;
73649ab747fSPaolo Bonzini         memset(p, 0, pad);
73749ab747fSPaolo Bonzini         p += pad;
73849ab747fSPaolo Bonzini         size = 64;
73949ab747fSPaolo Bonzini     }
74049ab747fSPaolo Bonzini     /* It's not clear if the CRC should go before or after the last byte in
74149ab747fSPaolo Bonzini        odd sized packets.  Linux disables the CRC, so that's no help.
74249ab747fSPaolo Bonzini        The pictures in the documentation show the CRC aligned on a 16-bit
74349ab747fSPaolo Bonzini        boundary before the last odd byte, so that's what we do.  */
74449ab747fSPaolo Bonzini     if (crc) {
74549ab747fSPaolo Bonzini         crc = crc32(~0, buf, size);
74649ab747fSPaolo Bonzini         *(p++) = crc & 0xff; crc >>= 8;
74749ab747fSPaolo Bonzini         *(p++) = crc & 0xff; crc >>= 8;
74849ab747fSPaolo Bonzini         *(p++) = crc & 0xff; crc >>= 8;
74949ab747fSPaolo Bonzini         *(p++) = crc & 0xff;
75049ab747fSPaolo Bonzini     }
75149ab747fSPaolo Bonzini     if (size & 1) {
75249ab747fSPaolo Bonzini         *(p++) = buf[size - 1];
75349ab747fSPaolo Bonzini         *p = 0x60;
75449ab747fSPaolo Bonzini     } else {
75549ab747fSPaolo Bonzini         *(p++) = 0;
75649ab747fSPaolo Bonzini         *p = 0x40;
75749ab747fSPaolo Bonzini     }
75849ab747fSPaolo Bonzini     /* TODO: Raise early RX interrupt?  */
75949ab747fSPaolo Bonzini     s->int_level |= INT_RCV;
76049ab747fSPaolo Bonzini     smc91c111_update(s);
76149ab747fSPaolo Bonzini 
76249ab747fSPaolo Bonzini     return size;
76349ab747fSPaolo Bonzini }
76449ab747fSPaolo Bonzini 
76549ab747fSPaolo Bonzini static const MemoryRegionOps smc91c111_mem_ops = {
76649ab747fSPaolo Bonzini     /* The special case for 32 bit writes to 0xc means we can't just
76749ab747fSPaolo Bonzini      * set .impl.min/max_access_size to 1, unfortunately
76849ab747fSPaolo Bonzini      */
76950a22d0dSPeter Maydell     .read = smc91c111_readfn,
77050a22d0dSPeter Maydell     .write = smc91c111_writefn,
77150a22d0dSPeter Maydell     .valid.min_access_size = 1,
77250a22d0dSPeter Maydell     .valid.max_access_size = 4,
77349ab747fSPaolo Bonzini     .endianness = DEVICE_NATIVE_ENDIAN,
77449ab747fSPaolo Bonzini };
77549ab747fSPaolo Bonzini 
77649ab747fSPaolo Bonzini static NetClientInfo net_smc91c111_info = {
777f394b2e2SEric Blake     .type = NET_CLIENT_DRIVER_NIC,
77849ab747fSPaolo Bonzini     .size = sizeof(NICState),
7798d06b149SPeter Crosthwaite     .can_receive = smc91c111_can_receive_nc,
78049ab747fSPaolo Bonzini     .receive = smc91c111_receive,
78149ab747fSPaolo Bonzini };
78249ab747fSPaolo Bonzini 
smc91c111_realize(DeviceState * dev,Error ** errp)783f5ac82ceSCédric Le Goater static void smc91c111_realize(DeviceState *dev, Error **errp)
78449ab747fSPaolo Bonzini {
785f5ac82ceSCédric Le Goater     SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
786926d152eSAndreas Färber     smc91c111_state *s = SMC91C111(dev);
787926d152eSAndreas Färber 
788eedfac6fSPaolo Bonzini     memory_region_init_io(&s->mmio, OBJECT(s), &smc91c111_mem_ops, s,
78949ab747fSPaolo Bonzini                           "smc91c111-mmio", 16);
790926d152eSAndreas Färber     sysbus_init_mmio(sbd, &s->mmio);
791926d152eSAndreas Färber     sysbus_init_irq(sbd, &s->irq);
79249ab747fSPaolo Bonzini     qemu_macaddr_default_if_unset(&s->conf.macaddr);
79349ab747fSPaolo Bonzini     s->nic = qemu_new_nic(&net_smc91c111_info, &s->conf,
7947d0fefdfSAkihiko Odaki                           object_get_typename(OBJECT(dev)), dev->id,
7957d0fefdfSAkihiko Odaki                           &dev->mem_reentrancy_guard, s);
79649ab747fSPaolo Bonzini     qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
79749ab747fSPaolo Bonzini     /* ??? Save/restore.  */
79849ab747fSPaolo Bonzini }
79949ab747fSPaolo Bonzini 
80049ab747fSPaolo Bonzini static Property smc91c111_properties[] = {
80149ab747fSPaolo Bonzini     DEFINE_NIC_PROPERTIES(smc91c111_state, conf),
80249ab747fSPaolo Bonzini     DEFINE_PROP_END_OF_LIST(),
80349ab747fSPaolo Bonzini };
80449ab747fSPaolo Bonzini 
smc91c111_class_init(ObjectClass * klass,void * data)80549ab747fSPaolo Bonzini static void smc91c111_class_init(ObjectClass *klass, void *data)
80649ab747fSPaolo Bonzini {
80749ab747fSPaolo Bonzini     DeviceClass *dc = DEVICE_CLASS(klass);
80849ab747fSPaolo Bonzini 
809f5ac82ceSCédric Le Goater     dc->realize = smc91c111_realize;
810e3d08143SPeter Maydell     device_class_set_legacy_reset(dc, smc91c111_reset);
81149ab747fSPaolo Bonzini     dc->vmsd = &vmstate_smc91c111;
8124f67d30bSMarc-André Lureau     device_class_set_props(dc, smc91c111_properties);
81349ab747fSPaolo Bonzini }
81449ab747fSPaolo Bonzini 
81549ab747fSPaolo Bonzini static const TypeInfo smc91c111_info = {
816926d152eSAndreas Färber     .name          = TYPE_SMC91C111,
81749ab747fSPaolo Bonzini     .parent        = TYPE_SYS_BUS_DEVICE,
81849ab747fSPaolo Bonzini     .instance_size = sizeof(smc91c111_state),
81949ab747fSPaolo Bonzini     .class_init    = smc91c111_class_init,
82049ab747fSPaolo Bonzini };
82149ab747fSPaolo Bonzini 
smc91c111_register_types(void)82249ab747fSPaolo Bonzini static void smc91c111_register_types(void)
82349ab747fSPaolo Bonzini {
82449ab747fSPaolo Bonzini     type_register_static(&smc91c111_info);
82549ab747fSPaolo Bonzini }
82649ab747fSPaolo Bonzini 
82749ab747fSPaolo Bonzini /* Legacy helper function.  Should go away when machine config files are
82849ab747fSPaolo Bonzini    implemented.  */
smc91c111_init(uint32_t base,qemu_irq irq)829cd53991dSDavid Woodhouse void smc91c111_init(uint32_t base, qemu_irq irq)
83049ab747fSPaolo Bonzini {
83149ab747fSPaolo Bonzini     DeviceState *dev;
83249ab747fSPaolo Bonzini     SysBusDevice *s;
83349ab747fSPaolo Bonzini 
8343e80f690SMarkus Armbruster     dev = qdev_new(TYPE_SMC91C111);
835cd53991dSDavid Woodhouse     qemu_configure_nic_device(dev, true, NULL);
83649ab747fSPaolo Bonzini     s = SYS_BUS_DEVICE(dev);
8373c6ef471SMarkus Armbruster     sysbus_realize_and_unref(s, &error_fatal);
83849ab747fSPaolo Bonzini     sysbus_mmio_map(s, 0, base);
83949ab747fSPaolo Bonzini     sysbus_connect_irq(s, 0, irq);
84049ab747fSPaolo Bonzini }
84149ab747fSPaolo Bonzini 
84249ab747fSPaolo Bonzini type_init(smc91c111_register_types)
843