vmstate: port cirrus_vga device
[qemu] / hw / eepro100.c
index 18d8115..e28fd5b 100644 (file)
@@ -17,8 +17,7 @@
  * GNU General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
  *
  * Tested features (i82559):
  *      PXE boot (i386) no valid link
@@ -38,8 +37,8 @@
 # warning "PXE boot still not working!"
 #endif
 
-#include <assert.h>
 #include <stddef.h>             /* offsetof */
+#include <stdbool.h>
 #include "hw.h"
 #include "pci.h"
 #include "net.h"
@@ -60,9 +59,9 @@
 //~ #define DEBUG_EEPRO100
 
 #ifdef DEBUG_EEPRO100
-#define logout(fmt, args...) fprintf(stderr, "EE100\t%-24s" fmt, __func__, ##args)
+#define logout(fmt, ...) fprintf(stderr, "EE100\t%-24s" fmt, __func__, ## __VA_ARGS__)
 #else
-#define logout(fmt, args...) ((void)0)
+#define logout(fmt, ...) ((void)0)
 #endif
 
 /* Set flags to 0 to disable debug output. */
 #define INT_MASK        0x0100
 #define DRVR_INT        0x0200  /* Driver generated interrupt. */
 
-typedef unsigned char bool;
-
 /* Offsets to the various registers.
    All accesses need not be longword aligned. */
 enum speedo_offsets {
@@ -183,6 +180,7 @@ typedef enum {
 } ru_state_t;
 
 typedef struct {
+    PCIDevice dev;
 #if 1
     uint8_t cmd;
     uint32_t start;
@@ -202,7 +200,6 @@ typedef struct {
     uint8_t curpag;
     uint8_t mult[8];            /* multicast mask array */
     int mmio_index;
-    PCIDevice *pci_dev;
     VLANClientState *vc;
 #endif
     uint8_t scb_stat;           /* SCB stat/ack byte */
@@ -306,7 +303,7 @@ static void disable_interrupt(EEPRO100State * s)
 {
     if (s->int_stat) {
         logout("interrupt disabled\n");
-        qemu_irq_lower(s->pci_dev->irq[0]);
+        qemu_irq_lower(s->dev.irq[0]);
         s->int_stat = 0;
     }
 }
@@ -315,7 +312,7 @@ static void enable_interrupt(EEPRO100State * s)
 {
     if (!s->int_stat) {
         logout("interrupt enabled\n");
-        qemu_irq_raise(s->pci_dev->irq[0]);
+        qemu_irq_raise(s->dev.irq[0]);
         s->int_stat = 1;
     }
 }
@@ -394,14 +391,13 @@ static void eepro100_fcp_interrupt(EEPRO100State * s)
 static void pci_reset(EEPRO100State * s)
 {
     uint32_t device = s->device;
-    uint8_t *pci_conf = s->pci_dev->config;
+    uint8_t *pci_conf = s->dev.config;
 
     logout("%p\n", s);
 
     /* PCI Vendor ID */
     pci_config_set_vendor_id(pci_conf, PCI_VENDOR_ID_INTEL);
-    /* PCI Device ID */
-    pci_config_set_device_id(pci_conf, PCI_DEVICE_ID_INTEL_82551IT);
+    /* PCI Device ID depends on device and is set below. */
     /* PCI Command */
     PCI_CONFIG_16(PCI_COMMAND, 0x0000);
     /* PCI Status */
@@ -449,29 +445,29 @@ static void pci_reset(EEPRO100State * s)
 
     switch (device) {
     case i82551:
-        //~ PCI_CONFIG_16(PCI_DEVICE_ID, 0x1209);
+        pci_config_set_device_id(pci_conf, PCI_DEVICE_ID_INTEL_82551IT);
         PCI_CONFIG_8(PCI_REVISION_ID, 0x0f);
         break;
     case i82557B:
-        PCI_CONFIG_16(PCI_DEVICE_ID, 0x1229);
+        pci_config_set_device_id(pci_conf, PCI_DEVICE_ID_INTEL_82557);
         PCI_CONFIG_8(PCI_REVISION_ID, 0x02);
         break;
     case i82557C:
-        PCI_CONFIG_16(PCI_DEVICE_ID, 0x1229);
+        pci_config_set_device_id(pci_conf, PCI_DEVICE_ID_INTEL_82557);
         PCI_CONFIG_8(PCI_REVISION_ID, 0x03);
         break;
     case i82558B:
-        PCI_CONFIG_16(PCI_DEVICE_ID, 0x1229);
+        pci_config_set_device_id(pci_conf, PCI_DEVICE_ID_INTEL_82557);
         PCI_CONFIG_16(PCI_STATUS, 0x2810);
         PCI_CONFIG_8(PCI_REVISION_ID, 0x05);
         break;
     case i82559C:
-        PCI_CONFIG_16(PCI_DEVICE_ID, 0x1229);
+        pci_config_set_device_id(pci_conf, PCI_DEVICE_ID_INTEL_82557);
         PCI_CONFIG_16(PCI_STATUS, 0x2810);
         //~ PCI_CONFIG_8(PCI_REVISION_ID, 0x08);
         break;
     case i82559ER:
-        //~ PCI_CONFIG_16(PCI_DEVICE_ID, 0x1209);
+        pci_config_set_device_id(pci_conf, PCI_DEVICE_ID_INTEL_82551IT);
         PCI_CONFIG_16(PCI_STATUS, 0x2810);
         PCI_CONFIG_8(PCI_REVISION_ID, 0x09);
         break;
@@ -509,7 +505,7 @@ static void nic_selective_reset(EEPRO100State * s)
 
 static void nic_reset(void *opaque)
 {
-    EEPRO100State *s = (EEPRO100State *) opaque;
+    EEPRO100State *s = opaque;
     logout("%p\n", s);
     static int first;
     if (!first) {
@@ -577,6 +573,11 @@ static uint16_t eepro100_read_command(EEPRO100State * s)
 }
 #endif
 
+static bool device_supports_eTxCB(EEPRO100State * s)
+{
+    return (s->device != i82557B && s->device != i82557C);
+}
+
 /* Commands that can be put in a command list entry. */
 enum commands {
     CmdNOp = 0,
@@ -696,7 +697,8 @@ static void eepro100_cu_command(EEPRO100State * s, uint8_t val)
                 logout
                     ("illegal values of TBD array address and TCB byte count!\n");
             }
-            uint8_t buf[MAX_ETH_FRAME_SIZE + 4];
+            // sends larger than MAX_ETH_FRAME_SIZE are allowed, up to 2600 bytes
+            uint8_t buf[2600];
             uint16_t size = 0;
             uint32_t tbd_address = cb_address + 0x10;
             assert(tcb_bytes <= sizeof(buf));
@@ -708,6 +710,7 @@ static void eepro100_cu_command(EEPRO100State * s, uint8_t val)
                 logout
                     ("TBD (simplified mode): buffer address 0x%08x, size 0x%04x\n",
                      tx_buffer_address, tx_buffer_size);
+                tx_buffer_size = MIN(tx_buffer_size, sizeof(buf) - size);
                 cpu_physical_memory_read(tx_buffer_address, &buf[size],
                                          tx_buffer_size);
                 size += tx_buffer_size;
@@ -717,8 +720,8 @@ static void eepro100_cu_command(EEPRO100State * s, uint8_t val)
             } else {
                 /* Flexible mode. */
                 uint8_t tbd_count = 0;
-                if (!(s->configuration[6] & BIT(4))) {
-                    /* Extended TCB. */
+                if (device_supports_eTxCB(s) && !(s->configuration[6] & BIT(4))) {
+                    /* Extended Flexible TCB. */
                     assert(tcb_bytes == 0);
                     for (; tbd_count < 2; tbd_count++) {
                         uint32_t tx_buffer_address = ldl_phys(tbd_address);
@@ -726,8 +729,9 @@ static void eepro100_cu_command(EEPRO100State * s, uint8_t val)
                         uint16_t tx_buffer_el = lduw_phys(tbd_address + 6);
                         tbd_address += 8;
                         logout
-                            ("TBD (extended mode): buffer address 0x%08x, size 0x%04x\n",
+                            ("TBD (extended flexible mode): buffer address 0x%08x, size 0x%04x\n",
                              tx_buffer_address, tx_buffer_size);
+                        tx_buffer_size = MIN(tx_buffer_size, sizeof(buf) - size);
                         cpu_physical_memory_read(tx_buffer_address, &buf[size],
                                                  tx_buffer_size);
                         size += tx_buffer_size;
@@ -745,6 +749,7 @@ static void eepro100_cu_command(EEPRO100State * s, uint8_t val)
                     logout
                         ("TBD (flexible mode): buffer address 0x%08x, size 0x%04x\n",
                          tx_buffer_address, tx_buffer_size);
+                    tx_buffer_size = MIN(tx_buffer_size, sizeof(buf) - size);
                     cpu_physical_memory_read(tx_buffer_address, &buf[size],
                                              tx_buffer_size);
                     size += tx_buffer_size;
@@ -1340,16 +1345,10 @@ static void ioport_write4(void *opaque, uint32_t addr, uint32_t val)
 /***********************************************************/
 /* PCI EEPRO100 definitions */
 
-typedef struct PCIEEPRO100State {
-    PCIDevice dev;
-    EEPRO100State eepro100;
-} PCIEEPRO100State;
-
 static void pci_map(PCIDevice * pci_dev, int region_num,
                     uint32_t addr, uint32_t size, int type)
 {
-    PCIEEPRO100State *d = (PCIEEPRO100State *) pci_dev;
-    EEPRO100State *s = &d->eepro100;
+    EEPRO100State *s = DO_UPCAST(EEPRO100State, dev, pci_dev);
 
     logout("region %d, addr=0x%08x, size=0x%08x, type=%d\n",
            region_num, addr, size, type);
@@ -1407,13 +1406,13 @@ static uint32_t pci_mmio_readl(void *opaque, target_phys_addr_t addr)
     return eepro100_read4(s, addr);
 }
 
-static CPUWriteMemoryFunc *pci_mmio_write[] = {
+static CPUWriteMemoryFunc * const pci_mmio_write[] = {
     pci_mmio_writeb,
     pci_mmio_writew,
     pci_mmio_writel
 };
 
-static CPUReadMemoryFunc *pci_mmio_read[] = {
+static CPUReadMemoryFunc * const pci_mmio_read[] = {
     pci_mmio_readb,
     pci_mmio_readw,
     pci_mmio_readl
@@ -1422,33 +1421,33 @@ static CPUReadMemoryFunc *pci_mmio_read[] = {
 static void pci_mmio_map(PCIDevice * pci_dev, int region_num,
                          uint32_t addr, uint32_t size, int type)
 {
-    PCIEEPRO100State *d = (PCIEEPRO100State *) pci_dev;
+    EEPRO100State *s = DO_UPCAST(EEPRO100State, dev, pci_dev);
 
     logout("region %d, addr=0x%08x, size=0x%08x, type=%d\n",
            region_num, addr, size, type);
 
     if (region_num == 0) {
         /* Map control / status registers. */
-        cpu_register_physical_memory(addr, size, d->eepro100.mmio_index);
-        d->eepro100.region[region_num] = addr;
+        cpu_register_physical_memory(addr, size, s->mmio_index);
+        s->region[region_num] = addr;
     }
 }
 
-static int nic_can_receive(void *opaque)
+static int nic_can_receive(VLANClientState *vc)
 {
-    EEPRO100State *s = opaque;
+    EEPRO100State *s = vc->opaque;
     logout("%p\n", s);
     return get_ru_state(s) == ru_ready;
     //~ return !eepro100_buffer_full(s);
 }
 
-static void nic_receive(void *opaque, const uint8_t * buf, int size)
+static ssize_t nic_receive(VLANClientState *vc, const uint8_t * buf, size_t size)
 {
     /* TODO:
      * - Magic packets should set bit 30 in power management driver register.
      * - Interesting packets should set bit 29 in power management driver register.
      */
-    EEPRO100State *s = opaque;
+    EEPRO100State *s = vc->opaque;
     uint16_t rfd_status = 0xa000;
     static const uint8_t broadcast_macaddr[6] =
         { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
@@ -1459,18 +1458,18 @@ static void nic_receive(void *opaque, const uint8_t * buf, int size)
     if (s->configuration[8] & 0x80) {
         /* CSMA is disabled. */
         logout("%p received while CSMA is disabled\n", s);
-        return;
+        return -1;
     } else if (size < 64 && (s->configuration[7] & 1)) {
         /* Short frame and configuration byte 7/0 (discard short receive) set:
          * Short frame is discarded */
         logout("%p received short frame (%d byte)\n", s, size);
         s->statistics.rx_short_frame_errors++;
-        //~ return;
+        //~ return -1;
     } else if ((size > MAX_ETH_FRAME_SIZE + 4) && !(s->configuration[18] & 8)) {
         /* Long frame and configuration byte 18/3 (long receive ok) not set:
          * Long frames are discarded. */
         logout("%p received long frame (%d byte), ignored\n", s, size);
-        return;
+        return -1;
     } else if (memcmp(buf, s->macaddr, 6) == 0) {       // !!!
         /* Frame matches individual address. */
         /* TODO: check configuration byte 15/4 (ignore U/L). */
@@ -1486,7 +1485,7 @@ static void nic_receive(void *opaque, const uint8_t * buf, int size)
         assert(!(s->configuration[21] & BIT(3)));
         int mcast_idx = compute_mcast_idx(buf);
         if (!(s->mult[mcast_idx >> 3] & (1 << (mcast_idx & 7)))) {
-            return;
+            return size;
         }
         rfd_status |= 0x0002;
     } else if (s->configuration[15] & 1) {
@@ -1496,7 +1495,7 @@ static void nic_receive(void *opaque, const uint8_t * buf, int size)
     } else {
         logout("%p received frame, ignored, len=%d,%s\n", s, size,
                nic_dump(buf, size));
-        return;
+        return size;
     }
 
     if (get_ru_state(s) != ru_ready) {
@@ -1504,7 +1503,7 @@ static void nic_receive(void *opaque, const uint8_t * buf, int size)
         logout("no ressources, state=%u\n", get_ru_state(s));
         s->statistics.rx_resource_errors++;
         //~ assert(!"no ressources");
-        return;
+        return -1;
     }
     //~ !!!
 //~ $3 = {status = 0x0, command = 0xc000, link = 0x2d220, rx_buf_addr = 0x207dc, count = 0x0, size = 0x5f8, packet = {0x0 <repeats 1518 times>}}
@@ -1541,19 +1540,20 @@ static void nic_receive(void *opaque, const uint8_t * buf, int size)
         /* S bit is set. */
         set_ru_state(s, ru_suspended);
     }
+    return size;
 }
 
 static int nic_load(QEMUFile * f, void *opaque, int version_id)
 {
-    EEPRO100State *s = (EEPRO100State *) opaque;
+    EEPRO100State *s = opaque;
     int i;
     int ret;
 
     if (version_id > 3)
         return -EINVAL;
 
-    if (s->pci_dev && version_id >= 3) {
-        ret = pci_device_load(s->pci_dev, f);
+    if (version_id >= 3) {
+        ret = pci_device_load(&s->dev, f);
         if (ret < 0)
             return ret;
     }
@@ -1635,11 +1635,10 @@ static int nic_load(QEMUFile * f, void *opaque, int version_id)
 
 static void nic_save(QEMUFile * f, void *opaque)
 {
-    EEPRO100State *s = (EEPRO100State *) opaque;
+    EEPRO100State *s = opaque;
     int i;
 
-    if (s->pci_dev)
-        pci_device_save(s->pci_dev, f);
+    pci_device_save(&s->dev, f);
 
     qemu_put_8s(f, &s->rxcr);
 
@@ -1721,29 +1720,22 @@ static void nic_cleanup(VLANClientState *vc)
 
 static int pci_nic_uninit(PCIDevice *dev)
 {
-    PCIEEPRO100State *d = (PCIEEPRO100State *) dev;
-    EEPRO100State *s = &d->eepro100;
+    EEPRO100State *s = DO_UPCAST(EEPRO100State, dev, dev);
 
     cpu_unregister_io_memory(s->mmio_index);
 
     return 0;
 }
 
-static PCIDevice *nic_init(PCIBus * bus, NICInfo * nd, uint32_t device)
+static int nic_init(PCIDevice *pci_dev, uint32_t device)
 {
-    PCIEEPRO100State *d;
-    EEPRO100State *s;
+    EEPRO100State *s = DO_UPCAST(EEPRO100State, dev, pci_dev);
 
     logout("\n");
 
-    d = (PCIEEPRO100State *) pci_register_device(bus, nd->model,
-                                                 sizeof(PCIEEPRO100State), -1,
-                                                 NULL, NULL);
-    d->dev.unregister = pci_nic_uninit;
+    s->dev.unregister = pci_nic_uninit;
 
-    s = &d->eepro100;
     s->device = device;
-    s->pci_dev = &d->dev;
 
     pci_reset(s);
 
@@ -1752,25 +1744,25 @@ static PCIDevice *nic_init(PCIBus * bus, NICInfo * nd, uint32_t device)
     s->eeprom = eeprom93xx_new(EEPROM_SIZE);
 
     /* Handler for memory-mapped I/O */
-    d->eepro100.mmio_index =
-        cpu_register_io_memory(0, pci_mmio_read, pci_mmio_write, s);
+    s->mmio_index =
+        cpu_register_io_memory(pci_mmio_read, pci_mmio_write, s);
 
-    pci_register_io_region(&d->dev, 0, PCI_MEM_SIZE,
+    pci_register_bar(&s->dev, 0, PCI_MEM_SIZE,
                            PCI_ADDRESS_SPACE_MEM |
                            PCI_ADDRESS_SPACE_MEM_PREFETCH, pci_mmio_map);
-    pci_register_io_region(&d->dev, 1, PCI_IO_SIZE, PCI_ADDRESS_SPACE_IO,
+    pci_register_bar(&s->dev, 1, PCI_IO_SIZE, PCI_ADDRESS_SPACE_IO,
                            pci_map);
-    pci_register_io_region(&d->dev, 2, PCI_FLASH_SIZE, PCI_ADDRESS_SPACE_MEM,
+    pci_register_bar(&s->dev, 2, PCI_FLASH_SIZE, PCI_ADDRESS_SPACE_MEM,
                            pci_mmio_map);
 
-    memcpy(s->macaddr, nd->macaddr, 6);
+    qdev_get_macaddr(&s->dev.qdev, s->macaddr);
     logout("macaddr: %s\n", nic_dump(&s->macaddr[0], 6));
     assert(s->region[1] == 0);
 
     nic_reset(s);
 
-    s->vc = qemu_new_vlan_client(nd->vlan, nd->model, nd->name,
-                                 nic_receive, nic_can_receive,
+    s->vc = qdev_get_vlan_client(&s->dev.qdev,
+                                 nic_can_receive, nic_receive, NULL,
                                  nic_cleanup, s);
 
     qemu_format_nic_info_str(s->vc, s->macaddr);
@@ -1778,22 +1770,45 @@ static PCIDevice *nic_init(PCIBus * bus, NICInfo * nd, uint32_t device)
     qemu_register_reset(nic_reset, s);
 
     register_savevm(s->vc->model, -1, 3, nic_save, nic_load, s);
-    return (PCIDevice *)d;
+    return 0;
+}
+
+static int pci_i82551_init(PCIDevice *dev)
+{
+    return nic_init(dev, i82551);
 }
 
-PCIDevice *pci_i82551_init(PCIBus * bus, NICInfo * nd, int devfn)
+static int pci_i82557b_init(PCIDevice *dev)
 {
-    return nic_init(bus, nd, i82551);
+    return nic_init(dev, i82557B);
 }
 
-PCIDevice *pci_i82557b_init(PCIBus * bus, NICInfo * nd, int devfn)
+static int pci_i82559er_init(PCIDevice *dev)
 {
-    return nic_init(bus, nd, i82557B);
+    return nic_init(dev, i82559ER);
 }
 
-PCIDevice *pci_i82559er_init(PCIBus * bus, NICInfo * nd, int devfn)
+static PCIDeviceInfo eepro100_info[] = {
+    {
+        .qdev.name = "i82551",
+        .qdev.size = sizeof(EEPRO100State),
+        .init      = pci_i82551_init,
+    },{
+        .qdev.name = "i82557b",
+        .qdev.size = sizeof(EEPRO100State),
+        .init      = pci_i82557b_init,
+    },{
+        .qdev.name = "i82559er",
+        .qdev.size = sizeof(EEPRO100State),
+        .init      = pci_i82559er_init,
+    },{
+        /* end of list */
+    }
+};
+
+static void eepro100_register_devices(void)
 {
-    return nic_init(bus, nd, i82559ER);
+    pci_qdev_register_many(eepro100_info);
 }
 
-/* eof */
+device_init(eepro100_register_devices)