* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
+#include "hw/net/mii.h"
#include "hw/sysbus.h"
#include "net/net.h"
#include "sysemu/sysemu.h"
/* PHY MII registers */
enum {
- MII_BMCR,
- MII_BMSR,
- MII_PHYIDR1,
- MII_PHYIDR2,
- MII_ANAR,
- MII_ANLPAR,
MII_REG_MAX = 16,
};
static void mii_set_link(Mii *s, bool link_ok)
{
if (link_ok) {
- s->regs[MII_BMSR] |= 0x4;
- s->regs[MII_ANLPAR] |= 0x01e1;
+ s->regs[MII_BMSR] |= MII_BMSR_LINK_ST;
+ s->regs[MII_ANLPAR] |= MII_ANLPAR_TXFD | MII_ANLPAR_TX |
+ MII_ANLPAR_10FD | MII_ANLPAR_10 | MII_ANLPAR_CSMACD;
} else {
- s->regs[MII_BMSR] &= ~0x4;
+ s->regs[MII_BMSR] &= ~MII_BMSR_LINK_ST;
s->regs[MII_ANLPAR] &= 0x01ff;
}
s->link_ok = link_ok;
static void mii_reset(Mii *s)
{
memset(s->regs, 0, sizeof(s->regs));
- s->regs[MII_BMCR] = 0x1000;
- s->regs[MII_BMSR] = 0x7848; /* no ext regs */
- s->regs[MII_PHYIDR1] = 0x2000;
- s->regs[MII_PHYIDR2] = 0x5c90;
- s->regs[MII_ANAR] = 0x01e1;
+ s->regs[MII_BMCR] = MII_BMCR_AUTOEN;
+ s->regs[MII_BMSR] = MII_BMSR_100TX_FD | MII_BMSR_100TX_HD |
+ MII_BMSR_10T_FD | MII_BMSR_10T_HD | MII_BMSR_MFPS |
+ MII_BMSR_AN_COMP | MII_BMSR_AUTONEG;
+ s->regs[MII_PHYID1] = 0x2000;
+ s->regs[MII_PHYID2] = 0x5c90;
+ s->regs[MII_ANAR] = MII_ANAR_TXFD | MII_ANAR_TX |
+ MII_ANAR_10FD | MII_ANAR_10 | MII_ANAR_CSMACD;
mii_set_link(s, s->link_ok);
}
static void mii_write_bmcr(Mii *s, uint16_t v)
{
- if (v & 0x8000) {
+ if (v & MII_BMCR_RESET) {
mii_reset(s);
} else {
s->regs[MII_BMCR] = v;
static void (*reg_write[MII_REG_MAX])(Mii *s, uint16_t v) = {
[MII_BMCR] = mii_write_bmcr,
[MII_BMSR] = mii_ro,
- [MII_PHYIDR1] = mii_ro,
- [MII_PHYIDR2] = mii_ro,
+ [MII_PHYID1] = mii_ro,
+ [MII_PHYID2] = mii_ro,
};
if (idx < MII_REG_MAX) {
};
enum {
+ INT_SOURCE_BUSY = 0x10,
INT_SOURCE_RXB = 0x4,
INT_SOURCE_TXB = 0x1,
};
#define DEFAULT_PHY 1
+#define TYPE_OPEN_ETH "open_eth"
+#define OPEN_ETH(obj) OBJECT_CHECK(OpenEthState, (obj), TYPE_OPEN_ETH)
+
typedef struct OpenEthState {
- SysBusDevice dev;
+ SysBusDevice parent_obj;
+
NICState *nic;
NICConf conf;
MemoryRegion reg_io;
OpenEthState *s = qemu_get_nic_opaque(nc);
return GET_REGBIT(s, MODER, RXEN) &&
- (s->regs[TX_BD_NUM] < 0x80) &&
- (rx_desc(s)->len_flags & RXD_E);
+ (s->regs[TX_BD_NUM] < 0x80);
}
static ssize_t open_eth_receive(NetClientState *nc,
desc *desc = rx_desc(s);
size_t copy_size = GET_REGBIT(s, MODER, HUGEN) ? 65536 : maxfl;
+ if (!(desc->len_flags & RXD_E)) {
+ open_eth_int_source_write(s,
+ s->regs[INT_SOURCE] | INT_SOURCE_BUSY);
+ return size;
+ }
+
desc->len_flags &= ~(RXD_CF | RXD_M | RXD_OR |
RXD_IS | RXD_DN | RXD_TL | RXD_SF | RXD_CRC | RXD_LC);
return size;
}
-static void open_eth_cleanup(NetClientState *nc)
-{
-}
-
static NetClientInfo net_open_eth_info = {
- .type = NET_CLIENT_OPTIONS_KIND_NIC,
+ .type = NET_CLIENT_DRIVER_NIC,
.size = sizeof(NICState),
.can_receive = open_eth_can_receive,
.receive = open_eth_receive,
- .cleanup = open_eth_cleanup,
.link_status_changed = open_eth_set_link_status,
};
static void open_eth_start_xmit(OpenEthState *s, desc *tx)
{
- uint8_t buf[65536];
+ uint8_t *buf = NULL;
+ uint8_t buffer[0x600];
unsigned len = GET_FIELD(tx->len_flags, TXD_LEN);
unsigned tx_len = len;
trace_open_eth_start_xmit(tx->buf_ptr, len, tx_len);
+ if (tx_len > sizeof(buffer)) {
+ buf = g_new(uint8_t, tx_len);
+ } else {
+ buf = buffer;
+ }
if (len > tx_len) {
len = tx_len;
}
memset(buf + len, 0, tx_len - len);
}
qemu_send_packet(qemu_get_queue(s->nic), buf, tx_len);
+ if (tx_len > sizeof(buffer)) {
+ g_free(buf);
+ }
if (tx->len_flags & TXD_WR) {
s->tx_desc = 0;
return v;
}
+static void open_eth_notify_can_receive(OpenEthState *s)
+{
+ NetClientState *nc = qemu_get_queue(s->nic);
+
+ if (open_eth_can_receive(nc)) {
+ qemu_flush_queued_packets(nc);
+ }
+}
+
static void open_eth_ro(OpenEthState *s, uint32_t val)
{
}
if (set & MODER_RXEN) {
s->rx_desc = s->regs[TX_BD_NUM];
+ open_eth_notify_can_receive(s);
}
if (set & MODER_TXEN) {
s->tx_desc = 0;
s->regs[INT_SOURCE] & s->regs[INT_MASK]);
}
+static void open_eth_tx_bd_num_host_write(OpenEthState *s, uint32_t val)
+{
+ if (val < 0x80) {
+ bool enable = s->regs[TX_BD_NUM] == 0x80;
+
+ s->regs[TX_BD_NUM] = val;
+ if (enable) {
+ open_eth_notify_can_receive(s);
+ }
+ }
+}
+
static void open_eth_mii_command_host_write(OpenEthState *s, uint32_t val)
{
unsigned fiad = GET_REGFIELD(s, MIIADDRESS, FIAD);
[MODER] = open_eth_moder_host_write,
[INT_SOURCE] = open_eth_int_source_host_write,
[INT_MASK] = open_eth_int_mask_host_write,
+ [TX_BD_NUM] = open_eth_tx_bd_num_host_write,
[MIICOMMAND] = open_eth_mii_command_host_write,
[MIITX_DATA] = open_eth_mii_tx_host_write,
[MIISTATUS] = open_eth_ro,
.write = open_eth_desc_write,
};
-static int sysbus_open_eth_init(SysBusDevice *dev)
+static int sysbus_open_eth_init(SysBusDevice *sbd)
{
- OpenEthState *s = DO_UPCAST(OpenEthState, dev, dev);
+ DeviceState *dev = DEVICE(sbd);
+ OpenEthState *s = OPEN_ETH(dev);
memory_region_init_io(&s->reg_io, OBJECT(dev), &open_eth_reg_ops, s,
"open_eth.regs", 0x54);
- sysbus_init_mmio(dev, &s->reg_io);
+ sysbus_init_mmio(sbd, &s->reg_io);
memory_region_init_io(&s->desc_io, OBJECT(dev), &open_eth_desc_ops, s,
"open_eth.desc", 0x400);
- sysbus_init_mmio(dev, &s->desc_io);
+ sysbus_init_mmio(sbd, &s->desc_io);
- sysbus_init_irq(dev, &s->irq);
+ sysbus_init_irq(sbd, &s->irq);
s->nic = qemu_new_nic(&net_open_eth_info, &s->conf,
- object_get_typename(OBJECT(s)), s->dev.qdev.id, s);
+ object_get_typename(OBJECT(s)), dev->id, s);
return 0;
}
static void qdev_open_eth_reset(DeviceState *dev)
{
- OpenEthState *d = DO_UPCAST(OpenEthState, dev.qdev, dev);
+ OpenEthState *d = OPEN_ETH(dev);
+
open_eth_reset(d);
}
}
static const TypeInfo open_eth_info = {
- .name = "open_eth",
+ .name = TYPE_OPEN_ETH,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(OpenEthState),
.class_init = open_eth_class_init,