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