Statistics
| Branch: | Revision:

root / hw / mipsnet.c @ ac0cdda3

History | View | Annotate | Download (7.3 kB)

1 87ecb68b pbrook
#include "hw.h"
2 87ecb68b pbrook
#include "mips.h"
3 87ecb68b pbrook
#include "net.h"
4 87ecb68b pbrook
#include "isa.h"
5 f0fc6f8f ths
6 57ba97de ths
//#define DEBUG_MIPSNET_SEND
7 57ba97de ths
//#define DEBUG_MIPSNET_RECEIVE
8 f0fc6f8f ths
//#define DEBUG_MIPSNET_DATA
9 57ba97de ths
//#define DEBUG_MIPSNET_IRQ
10 f0fc6f8f ths
11 f0fc6f8f ths
/* MIPSnet register offsets */
12 f0fc6f8f ths
13 f0fc6f8f ths
#define MIPSNET_DEV_ID                0x00
14 f0fc6f8f ths
#define MIPSNET_BUSY                0x08
15 f0fc6f8f ths
#define MIPSNET_RX_DATA_COUNT        0x0c
16 f0fc6f8f ths
#define MIPSNET_TX_DATA_COUNT        0x10
17 f0fc6f8f ths
#define MIPSNET_INT_CTL                0x14
18 f0fc6f8f ths
# define MIPSNET_INTCTL_TXDONE                0x00000001
19 f0fc6f8f ths
# define MIPSNET_INTCTL_RXDONE                0x00000002
20 f0fc6f8f ths
# define MIPSNET_INTCTL_TESTBIT                0x80000000
21 f0fc6f8f ths
#define MIPSNET_INTERRUPT_INFO        0x18
22 f0fc6f8f ths
#define MIPSNET_RX_DATA_BUFFER        0x1c
23 f0fc6f8f ths
#define MIPSNET_TX_DATA_BUFFER        0x20
24 f0fc6f8f ths
25 f0fc6f8f ths
#define MAX_ETH_FRAME_SIZE        1514
26 f0fc6f8f ths
27 f0fc6f8f ths
typedef struct MIPSnetState {
28 f0fc6f8f ths
    uint32_t busy;
29 f0fc6f8f ths
    uint32_t rx_count;
30 f0fc6f8f ths
    uint32_t rx_read;
31 f0fc6f8f ths
    uint32_t tx_count;
32 f0fc6f8f ths
    uint32_t tx_written;
33 f0fc6f8f ths
    uint32_t intctl;
34 f0fc6f8f ths
    uint8_t rx_buffer[MAX_ETH_FRAME_SIZE];
35 f0fc6f8f ths
    uint8_t tx_buffer[MAX_ETH_FRAME_SIZE];
36 b946a153 aliguori
    int io_base;
37 f0fc6f8f ths
    qemu_irq irq;
38 1f30d10a Mark McLoughlin
    NICState *nic;
39 1f30d10a Mark McLoughlin
    NICConf conf;
40 f0fc6f8f ths
} MIPSnetState;
41 f0fc6f8f ths
42 f0fc6f8f ths
static void mipsnet_reset(MIPSnetState *s)
43 f0fc6f8f ths
{
44 f0fc6f8f ths
    s->busy = 1;
45 f0fc6f8f ths
    s->rx_count = 0;
46 f0fc6f8f ths
    s->rx_read = 0;
47 f0fc6f8f ths
    s->tx_count = 0;
48 f0fc6f8f ths
    s->tx_written = 0;
49 f0fc6f8f ths
    s->intctl = 0;
50 f0fc6f8f ths
    memset(s->rx_buffer, 0, MAX_ETH_FRAME_SIZE);
51 f0fc6f8f ths
    memset(s->tx_buffer, 0, MAX_ETH_FRAME_SIZE);
52 f0fc6f8f ths
}
53 f0fc6f8f ths
54 f0fc6f8f ths
static void mipsnet_update_irq(MIPSnetState *s)
55 f0fc6f8f ths
{
56 f0fc6f8f ths
    int isr = !!s->intctl;
57 f0fc6f8f ths
#ifdef DEBUG_MIPSNET_IRQ
58 f0fc6f8f ths
    printf("mipsnet: Set IRQ to %d (%02x)\n", isr, s->intctl);
59 f0fc6f8f ths
#endif
60 f0fc6f8f ths
    qemu_set_irq(s->irq, isr);
61 f0fc6f8f ths
}
62 f0fc6f8f ths
63 f0fc6f8f ths
static int mipsnet_buffer_full(MIPSnetState *s)
64 f0fc6f8f ths
{
65 f0fc6f8f ths
    if (s->rx_count >= MAX_ETH_FRAME_SIZE)
66 f0fc6f8f ths
        return 1;
67 f0fc6f8f ths
    return 0;
68 f0fc6f8f ths
}
69 f0fc6f8f ths
70 1f30d10a Mark McLoughlin
static int mipsnet_can_receive(VLANClientState *nc)
71 f0fc6f8f ths
{
72 1f30d10a Mark McLoughlin
    MIPSnetState *s = DO_UPCAST(NICState, nc, nc)->opaque;
73 f0fc6f8f ths
74 f0fc6f8f ths
    if (s->busy)
75 f0fc6f8f ths
        return 0;
76 f0fc6f8f ths
    return !mipsnet_buffer_full(s);
77 f0fc6f8f ths
}
78 f0fc6f8f ths
79 1f30d10a Mark McLoughlin
static ssize_t mipsnet_receive(VLANClientState *nc, const uint8_t *buf, size_t size)
80 f0fc6f8f ths
{
81 1f30d10a Mark McLoughlin
    MIPSnetState *s = DO_UPCAST(NICState, nc, nc)->opaque;
82 f0fc6f8f ths
83 f0fc6f8f ths
#ifdef DEBUG_MIPSNET_RECEIVE
84 f0fc6f8f ths
    printf("mipsnet: receiving len=%d\n", size);
85 f0fc6f8f ths
#endif
86 1f30d10a Mark McLoughlin
    if (!mipsnet_can_receive(nc))
87 4f1c942b Mark McLoughlin
        return -1;
88 f0fc6f8f ths
89 f0fc6f8f ths
    s->busy = 1;
90 f0fc6f8f ths
91 f0fc6f8f ths
    /* Just accept everything. */
92 f0fc6f8f ths
93 f0fc6f8f ths
    /* Write packet data. */
94 f0fc6f8f ths
    memcpy(s->rx_buffer, buf, size);
95 f0fc6f8f ths
96 f0fc6f8f ths
    s->rx_count = size;
97 f0fc6f8f ths
    s->rx_read = 0;
98 f0fc6f8f ths
99 f0fc6f8f ths
    /* Now we can signal we have received something. */
100 f0fc6f8f ths
    s->intctl |= MIPSNET_INTCTL_RXDONE;
101 f0fc6f8f ths
    mipsnet_update_irq(s);
102 4f1c942b Mark McLoughlin
103 4f1c942b Mark McLoughlin
    return size;
104 f0fc6f8f ths
}
105 f0fc6f8f ths
106 f0fc6f8f ths
static uint32_t mipsnet_ioport_read(void *opaque, uint32_t addr)
107 f0fc6f8f ths
{
108 f0fc6f8f ths
    MIPSnetState *s = opaque;
109 f0fc6f8f ths
    int ret = 0;
110 f0fc6f8f ths
111 f0fc6f8f ths
    addr &= 0x3f;
112 f0fc6f8f ths
    switch (addr) {
113 f0fc6f8f ths
    case MIPSNET_DEV_ID:
114 9b595395 aurel32
        ret = be32_to_cpu(0x4d495053);                /* MIPS */
115 f0fc6f8f ths
        break;
116 f0fc6f8f ths
    case MIPSNET_DEV_ID + 4:
117 9b595395 aurel32
        ret = be32_to_cpu(0x4e455430);                /* NET0 */
118 f0fc6f8f ths
        break;
119 f0fc6f8f ths
    case MIPSNET_BUSY:
120 f0fc6f8f ths
        ret = s->busy;
121 f0fc6f8f ths
        break;
122 f0fc6f8f ths
    case MIPSNET_RX_DATA_COUNT:
123 f0fc6f8f ths
        ret = s->rx_count;
124 f0fc6f8f ths
        break;
125 f0fc6f8f ths
    case MIPSNET_TX_DATA_COUNT:
126 f0fc6f8f ths
        ret = s->tx_count;
127 f0fc6f8f ths
        break;
128 f0fc6f8f ths
    case MIPSNET_INT_CTL:
129 f0fc6f8f ths
        ret = s->intctl;
130 f0fc6f8f ths
        s->intctl &= ~MIPSNET_INTCTL_TESTBIT;
131 f0fc6f8f ths
        break;
132 f0fc6f8f ths
    case MIPSNET_INTERRUPT_INFO:
133 f0fc6f8f ths
        /* XXX: This seems to be a per-VPE interrupt number. */
134 f0fc6f8f ths
        ret = 0;
135 f0fc6f8f ths
        break;
136 f0fc6f8f ths
    case MIPSNET_RX_DATA_BUFFER:
137 f0fc6f8f ths
        if (s->rx_count) {
138 f0fc6f8f ths
            s->rx_count--;
139 f0fc6f8f ths
            ret = s->rx_buffer[s->rx_read++];
140 f0fc6f8f ths
        }
141 f0fc6f8f ths
        break;
142 f0fc6f8f ths
    /* Reads as zero. */
143 f0fc6f8f ths
    case MIPSNET_TX_DATA_BUFFER:
144 f0fc6f8f ths
    default:
145 f0fc6f8f ths
        break;
146 f0fc6f8f ths
    }
147 f0fc6f8f ths
#ifdef DEBUG_MIPSNET_DATA
148 f0fc6f8f ths
    printf("mipsnet: read addr=0x%02x val=0x%02x\n", addr, ret);
149 f0fc6f8f ths
#endif
150 f0fc6f8f ths
    return ret;
151 f0fc6f8f ths
}
152 f0fc6f8f ths
153 f0fc6f8f ths
static void mipsnet_ioport_write(void *opaque, uint32_t addr, uint32_t val)
154 f0fc6f8f ths
{
155 f0fc6f8f ths
    MIPSnetState *s = opaque;
156 f0fc6f8f ths
157 f0fc6f8f ths
    addr &= 0x3f;
158 f0fc6f8f ths
#ifdef DEBUG_MIPSNET_DATA
159 f0fc6f8f ths
    printf("mipsnet: write addr=0x%02x val=0x%02x\n", addr, val);
160 f0fc6f8f ths
#endif
161 f0fc6f8f ths
    switch (addr) {
162 f0fc6f8f ths
    case MIPSNET_TX_DATA_COUNT:
163 f0fc6f8f ths
        s->tx_count = (val <= MAX_ETH_FRAME_SIZE) ? val : 0;
164 f0fc6f8f ths
        s->tx_written = 0;
165 f0fc6f8f ths
        break;
166 f0fc6f8f ths
    case MIPSNET_INT_CTL:
167 f0fc6f8f ths
        if (val & MIPSNET_INTCTL_TXDONE) {
168 f0fc6f8f ths
            s->intctl &= ~MIPSNET_INTCTL_TXDONE;
169 f0fc6f8f ths
        } else if (val & MIPSNET_INTCTL_RXDONE) {
170 f0fc6f8f ths
            s->intctl &= ~MIPSNET_INTCTL_RXDONE;
171 f0fc6f8f ths
        } else if (val & MIPSNET_INTCTL_TESTBIT) {
172 f0fc6f8f ths
            mipsnet_reset(s);
173 f0fc6f8f ths
            s->intctl |= MIPSNET_INTCTL_TESTBIT;
174 f0fc6f8f ths
        } else if (!val) {
175 f0fc6f8f ths
            /* ACK testbit interrupt, flag was cleared on read. */
176 f0fc6f8f ths
        }
177 f0fc6f8f ths
        s->busy = !!s->intctl;
178 f0fc6f8f ths
        mipsnet_update_irq(s);
179 f0fc6f8f ths
        break;
180 f0fc6f8f ths
    case MIPSNET_TX_DATA_BUFFER:
181 f0fc6f8f ths
        s->tx_buffer[s->tx_written++] = val;
182 f0fc6f8f ths
        if (s->tx_written == s->tx_count) {
183 f0fc6f8f ths
            /* Send buffer. */
184 f0fc6f8f ths
#ifdef DEBUG_MIPSNET_SEND
185 f0fc6f8f ths
            printf("mipsnet: sending len=%d\n", s->tx_count);
186 f0fc6f8f ths
#endif
187 1f30d10a Mark McLoughlin
            qemu_send_packet(&s->nic->nc, s->tx_buffer, s->tx_count);
188 f0fc6f8f ths
            s->tx_count = s->tx_written = 0;
189 f0fc6f8f ths
            s->intctl |= MIPSNET_INTCTL_TXDONE;
190 f0fc6f8f ths
            s->busy = 1;
191 f0fc6f8f ths
            mipsnet_update_irq(s);
192 f0fc6f8f ths
        }
193 f0fc6f8f ths
        break;
194 f0fc6f8f ths
    /* Read-only registers */
195 f0fc6f8f ths
    case MIPSNET_DEV_ID:
196 f0fc6f8f ths
    case MIPSNET_BUSY:
197 f0fc6f8f ths
    case MIPSNET_RX_DATA_COUNT:
198 f0fc6f8f ths
    case MIPSNET_INTERRUPT_INFO:
199 f0fc6f8f ths
    case MIPSNET_RX_DATA_BUFFER:
200 f0fc6f8f ths
    default:
201 f0fc6f8f ths
        break;
202 f0fc6f8f ths
    }
203 f0fc6f8f ths
}
204 f0fc6f8f ths
205 f0fc6f8f ths
static void mipsnet_save(QEMUFile *f, void *opaque)
206 f0fc6f8f ths
{
207 f0fc6f8f ths
    MIPSnetState *s = opaque;
208 f0fc6f8f ths
209 f0fc6f8f ths
    qemu_put_be32s(f, &s->busy);
210 f0fc6f8f ths
    qemu_put_be32s(f, &s->rx_count);
211 f0fc6f8f ths
    qemu_put_be32s(f, &s->rx_read);
212 f0fc6f8f ths
    qemu_put_be32s(f, &s->tx_count);
213 f0fc6f8f ths
    qemu_put_be32s(f, &s->tx_written);
214 f0fc6f8f ths
    qemu_put_be32s(f, &s->intctl);
215 f0fc6f8f ths
    qemu_put_buffer(f, s->rx_buffer, MAX_ETH_FRAME_SIZE);
216 f0fc6f8f ths
    qemu_put_buffer(f, s->tx_buffer, MAX_ETH_FRAME_SIZE);
217 f0fc6f8f ths
}
218 f0fc6f8f ths
219 f0fc6f8f ths
static int mipsnet_load(QEMUFile *f, void *opaque, int version_id)
220 f0fc6f8f ths
{
221 f0fc6f8f ths
    MIPSnetState *s = opaque;
222 f0fc6f8f ths
223 f0fc6f8f ths
    if (version_id > 0)
224 f0fc6f8f ths
        return -EINVAL;
225 f0fc6f8f ths
226 f0fc6f8f ths
    qemu_get_be32s(f, &s->busy);
227 f0fc6f8f ths
    qemu_get_be32s(f, &s->rx_count);
228 f0fc6f8f ths
    qemu_get_be32s(f, &s->rx_read);
229 f0fc6f8f ths
    qemu_get_be32s(f, &s->tx_count);
230 f0fc6f8f ths
    qemu_get_be32s(f, &s->tx_written);
231 f0fc6f8f ths
    qemu_get_be32s(f, &s->intctl);
232 f0fc6f8f ths
    qemu_get_buffer(f, s->rx_buffer, MAX_ETH_FRAME_SIZE);
233 f0fc6f8f ths
    qemu_get_buffer(f, s->tx_buffer, MAX_ETH_FRAME_SIZE);
234 f0fc6f8f ths
235 f0fc6f8f ths
    return 0;
236 f0fc6f8f ths
}
237 f0fc6f8f ths
238 1f30d10a Mark McLoughlin
static void mipsnet_cleanup(VLANClientState *nc)
239 b946a153 aliguori
{
240 1f30d10a Mark McLoughlin
    MIPSnetState *s = DO_UPCAST(NICState, nc, nc)->opaque;
241 b946a153 aliguori
242 0be71e32 Alex Williamson
    unregister_savevm(NULL, "mipsnet", s);
243 b946a153 aliguori
244 b946a153 aliguori
    isa_unassign_ioport(s->io_base, 36);
245 b946a153 aliguori
246 b946a153 aliguori
    qemu_free(s);
247 b946a153 aliguori
}
248 b946a153 aliguori
249 1f30d10a Mark McLoughlin
static NetClientInfo net_mipsnet_info = {
250 1f30d10a Mark McLoughlin
    .type = NET_CLIENT_TYPE_NIC,
251 1f30d10a Mark McLoughlin
    .size = sizeof(NICState),
252 1f30d10a Mark McLoughlin
    .can_receive = mipsnet_can_receive,
253 1f30d10a Mark McLoughlin
    .receive = mipsnet_receive,
254 1f30d10a Mark McLoughlin
    .cleanup = mipsnet_cleanup,
255 1f30d10a Mark McLoughlin
};
256 1f30d10a Mark McLoughlin
257 f0fc6f8f ths
void mipsnet_init (int base, qemu_irq irq, NICInfo *nd)
258 f0fc6f8f ths
{
259 f0fc6f8f ths
    MIPSnetState *s;
260 f0fc6f8f ths
261 0ae18cee aliguori
    qemu_check_nic_model(nd, "mipsnet");
262 0ae18cee aliguori
263 f0fc6f8f ths
    s = qemu_mallocz(sizeof(MIPSnetState));
264 f0fc6f8f ths
265 f0fc6f8f ths
    register_ioport_write(base, 36, 1, mipsnet_ioport_write, s);
266 f0fc6f8f ths
    register_ioport_read(base, 36, 1, mipsnet_ioport_read, s);
267 f0fc6f8f ths
    register_ioport_write(base, 36, 2, mipsnet_ioport_write, s);
268 f0fc6f8f ths
    register_ioport_read(base, 36, 2, mipsnet_ioport_read, s);
269 f0fc6f8f ths
    register_ioport_write(base, 36, 4, mipsnet_ioport_write, s);
270 f0fc6f8f ths
    register_ioport_read(base, 36, 4, mipsnet_ioport_read, s);
271 f0fc6f8f ths
272 b946a153 aliguori
    s->io_base = base;
273 f0fc6f8f ths
    s->irq = irq;
274 1f30d10a Mark McLoughlin
275 283c7c63 Mark McLoughlin
    if (nd) {
276 1f30d10a Mark McLoughlin
        memcpy(s->conf.macaddr.a, nd->macaddr, sizeof(nd->macaddr));
277 1f30d10a Mark McLoughlin
        s->conf.vlan = nd->vlan;
278 1f30d10a Mark McLoughlin
        s->conf.peer = nd->netdev;
279 f0fc6f8f ths
280 1f30d10a Mark McLoughlin
        s->nic = qemu_new_nic(&net_mipsnet_info, &s->conf,
281 1f30d10a Mark McLoughlin
                              nd->model, nd->name, s);
282 1f30d10a Mark McLoughlin
283 1f30d10a Mark McLoughlin
        qemu_format_nic_info_str(&s->nic->nc, s->conf.macaddr.a);
284 1f30d10a Mark McLoughlin
    }
285 f0fc6f8f ths
286 f0fc6f8f ths
    mipsnet_reset(s);
287 0be71e32 Alex Williamson
    register_savevm(NULL, "mipsnet", 0, 0, mipsnet_save, mipsnet_load, s);
288 f0fc6f8f ths
}