Statistics
| Branch: | Revision:

root / hw / mipsnet.c @ 3a38d437

History | View | Annotate | Download (7 kB)

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
    VLANClientState *vc;
39
} MIPSnetState;
40

    
41
static void mipsnet_reset(MIPSnetState *s)
42
{
43
    s->busy = 1;
44
    s->rx_count = 0;
45
    s->rx_read = 0;
46
    s->tx_count = 0;
47
    s->tx_written = 0;
48
    s->intctl = 0;
49
    memset(s->rx_buffer, 0, MAX_ETH_FRAME_SIZE);
50
    memset(s->tx_buffer, 0, MAX_ETH_FRAME_SIZE);
51
}
52

    
53
static void mipsnet_update_irq(MIPSnetState *s)
54
{
55
    int isr = !!s->intctl;
56
#ifdef DEBUG_MIPSNET_IRQ
57
    printf("mipsnet: Set IRQ to %d (%02x)\n", isr, s->intctl);
58
#endif
59
    qemu_set_irq(s->irq, isr);
60
}
61

    
62
static int mipsnet_buffer_full(MIPSnetState *s)
63
{
64
    if (s->rx_count >= MAX_ETH_FRAME_SIZE)
65
        return 1;
66
    return 0;
67
}
68

    
69
static int mipsnet_can_receive(VLANClientState *vc)
70
{
71
    MIPSnetState *s = vc->opaque;
72

    
73
    if (s->busy)
74
        return 0;
75
    return !mipsnet_buffer_full(s);
76
}
77

    
78
static ssize_t mipsnet_receive(VLANClientState *vc, const uint8_t *buf, size_t size)
79
{
80
    MIPSnetState *s = vc->opaque;
81

    
82
#ifdef DEBUG_MIPSNET_RECEIVE
83
    printf("mipsnet: receiving len=%d\n", size);
84
#endif
85
    if (!mipsnet_can_receive(vc))
86
        return -1;
87

    
88
    s->busy = 1;
89

    
90
    /* Just accept everything. */
91

    
92
    /* Write packet data. */
93
    memcpy(s->rx_buffer, buf, size);
94

    
95
    s->rx_count = size;
96
    s->rx_read = 0;
97

    
98
    /* Now we can signal we have received something. */
99
    s->intctl |= MIPSNET_INTCTL_RXDONE;
100
    mipsnet_update_irq(s);
101

    
102
    return size;
103
}
104

    
105
static uint32_t mipsnet_ioport_read(void *opaque, uint32_t addr)
106
{
107
    MIPSnetState *s = opaque;
108
    int ret = 0;
109

    
110
    addr &= 0x3f;
111
    switch (addr) {
112
    case MIPSNET_DEV_ID:
113
        ret = be32_to_cpu(0x4d495053);                /* MIPS */
114
        break;
115
    case MIPSNET_DEV_ID + 4:
116
        ret = be32_to_cpu(0x4e455430);                /* NET0 */
117
        break;
118
    case MIPSNET_BUSY:
119
        ret = s->busy;
120
        break;
121
    case MIPSNET_RX_DATA_COUNT:
122
        ret = s->rx_count;
123
        break;
124
    case MIPSNET_TX_DATA_COUNT:
125
        ret = s->tx_count;
126
        break;
127
    case MIPSNET_INT_CTL:
128
        ret = s->intctl;
129
        s->intctl &= ~MIPSNET_INTCTL_TESTBIT;
130
        break;
131
    case MIPSNET_INTERRUPT_INFO:
132
        /* XXX: This seems to be a per-VPE interrupt number. */
133
        ret = 0;
134
        break;
135
    case MIPSNET_RX_DATA_BUFFER:
136
        if (s->rx_count) {
137
            s->rx_count--;
138
            ret = s->rx_buffer[s->rx_read++];
139
        }
140
        break;
141
    /* Reads as zero. */
142
    case MIPSNET_TX_DATA_BUFFER:
143
    default:
144
        break;
145
    }
146
#ifdef DEBUG_MIPSNET_DATA
147
    printf("mipsnet: read addr=0x%02x val=0x%02x\n", addr, ret);
148
#endif
149
    return ret;
150
}
151

    
152
static void mipsnet_ioport_write(void *opaque, uint32_t addr, uint32_t val)
153
{
154
    MIPSnetState *s = opaque;
155

    
156
    addr &= 0x3f;
157
#ifdef DEBUG_MIPSNET_DATA
158
    printf("mipsnet: write addr=0x%02x val=0x%02x\n", addr, val);
159
#endif
160
    switch (addr) {
161
    case MIPSNET_TX_DATA_COUNT:
162
        s->tx_count = (val <= MAX_ETH_FRAME_SIZE) ? val : 0;
163
        s->tx_written = 0;
164
        break;
165
    case MIPSNET_INT_CTL:
166
        if (val & MIPSNET_INTCTL_TXDONE) {
167
            s->intctl &= ~MIPSNET_INTCTL_TXDONE;
168
        } else if (val & MIPSNET_INTCTL_RXDONE) {
169
            s->intctl &= ~MIPSNET_INTCTL_RXDONE;
170
        } else if (val & MIPSNET_INTCTL_TESTBIT) {
171
            mipsnet_reset(s);
172
            s->intctl |= MIPSNET_INTCTL_TESTBIT;
173
        } else if (!val) {
174
            /* ACK testbit interrupt, flag was cleared on read. */
175
        }
176
        s->busy = !!s->intctl;
177
        mipsnet_update_irq(s);
178
        break;
179
    case MIPSNET_TX_DATA_BUFFER:
180
        s->tx_buffer[s->tx_written++] = val;
181
        if (s->tx_written == s->tx_count) {
182
            /* Send buffer. */
183
#ifdef DEBUG_MIPSNET_SEND
184
            printf("mipsnet: sending len=%d\n", s->tx_count);
185
#endif
186
            qemu_send_packet(s->vc, s->tx_buffer, s->tx_count);
187
            s->tx_count = s->tx_written = 0;
188
            s->intctl |= MIPSNET_INTCTL_TXDONE;
189
            s->busy = 1;
190
            mipsnet_update_irq(s);
191
        }
192
        break;
193
    /* Read-only registers */
194
    case MIPSNET_DEV_ID:
195
    case MIPSNET_BUSY:
196
    case MIPSNET_RX_DATA_COUNT:
197
    case MIPSNET_INTERRUPT_INFO:
198
    case MIPSNET_RX_DATA_BUFFER:
199
    default:
200
        break;
201
    }
202
}
203

    
204
static void mipsnet_save(QEMUFile *f, void *opaque)
205
{
206
    MIPSnetState *s = opaque;
207

    
208
    qemu_put_be32s(f, &s->busy);
209
    qemu_put_be32s(f, &s->rx_count);
210
    qemu_put_be32s(f, &s->rx_read);
211
    qemu_put_be32s(f, &s->tx_count);
212
    qemu_put_be32s(f, &s->tx_written);
213
    qemu_put_be32s(f, &s->intctl);
214
    qemu_put_buffer(f, s->rx_buffer, MAX_ETH_FRAME_SIZE);
215
    qemu_put_buffer(f, s->tx_buffer, MAX_ETH_FRAME_SIZE);
216
}
217

    
218
static int mipsnet_load(QEMUFile *f, void *opaque, int version_id)
219
{
220
    MIPSnetState *s = opaque;
221

    
222
    if (version_id > 0)
223
        return -EINVAL;
224

    
225
    qemu_get_be32s(f, &s->busy);
226
    qemu_get_be32s(f, &s->rx_count);
227
    qemu_get_be32s(f, &s->rx_read);
228
    qemu_get_be32s(f, &s->tx_count);
229
    qemu_get_be32s(f, &s->tx_written);
230
    qemu_get_be32s(f, &s->intctl);
231
    qemu_get_buffer(f, s->rx_buffer, MAX_ETH_FRAME_SIZE);
232
    qemu_get_buffer(f, s->tx_buffer, MAX_ETH_FRAME_SIZE);
233

    
234
    return 0;
235
}
236

    
237
static void mipsnet_cleanup(VLANClientState *vc)
238
{
239
    MIPSnetState *s = vc->opaque;
240

    
241
    unregister_savevm("mipsnet", s);
242

    
243
    isa_unassign_ioport(s->io_base, 36);
244

    
245
    qemu_free(s);
246
}
247

    
248
void mipsnet_init (int base, qemu_irq irq, NICInfo *nd)
249
{
250
    MIPSnetState *s;
251

    
252
    qemu_check_nic_model(nd, "mipsnet");
253

    
254
    s = qemu_mallocz(sizeof(MIPSnetState));
255

    
256
    register_ioport_write(base, 36, 1, mipsnet_ioport_write, s);
257
    register_ioport_read(base, 36, 1, mipsnet_ioport_read, s);
258
    register_ioport_write(base, 36, 2, mipsnet_ioport_write, s);
259
    register_ioport_read(base, 36, 2, mipsnet_ioport_read, s);
260
    register_ioport_write(base, 36, 4, mipsnet_ioport_write, s);
261
    register_ioport_read(base, 36, 4, mipsnet_ioport_read, s);
262

    
263
    s->io_base = base;
264
    s->irq = irq;
265
    if (nd && nd->vlan) {
266
        s->vc = nd->vc = qemu_new_vlan_client(nd->vlan, nd->model, nd->name,
267
                                              mipsnet_can_receive, mipsnet_receive,
268
                                              NULL, mipsnet_cleanup, s);
269
    } else {
270
        s->vc = NULL;
271
    }
272

    
273
    qemu_format_nic_info_str(s->vc, nd->macaddr);
274

    
275
    mipsnet_reset(s);
276
    register_savevm("mipsnet", 0, 0, mipsnet_save, mipsnet_load, s);
277
}