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