Statistics
| Branch: | Revision:

root / hw / mipsnet.c @ 487414f1

History | View | Annotate | Download (6.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
    qemu_irq irq;
37
    VLANClientState *vc;
38
    NICInfo *nd;
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(void *opaque)
70
{
71
    MIPSnetState *s = opaque;
72

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

    
78
static void mipsnet_receive(void *opaque, const uint8_t *buf, int size)
79
{
80
    MIPSnetState *s = opaque;
81

    
82
#ifdef DEBUG_MIPSNET_RECEIVE
83
    printf("mipsnet: receiving len=%d\n", size);
84
#endif
85
    if (!mipsnet_can_receive(opaque))
86
        return;
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

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

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

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

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

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

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

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

    
220
    if (version_id > 0)
221
        return -EINVAL;
222

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

    
232
    return 0;
233
}
234

    
235
void mipsnet_init (int base, qemu_irq irq, NICInfo *nd)
236
{
237
    MIPSnetState *s;
238

    
239
    qemu_check_nic_model(nd, "mipsnet");
240

    
241
    s = qemu_mallocz(sizeof(MIPSnetState));
242

    
243
    register_ioport_write(base, 36, 1, mipsnet_ioport_write, s);
244
    register_ioport_read(base, 36, 1, mipsnet_ioport_read, s);
245
    register_ioport_write(base, 36, 2, mipsnet_ioport_write, s);
246
    register_ioport_read(base, 36, 2, mipsnet_ioport_read, s);
247
    register_ioport_write(base, 36, 4, mipsnet_ioport_write, s);
248
    register_ioport_read(base, 36, 4, mipsnet_ioport_read, s);
249

    
250
    s->irq = irq;
251
    s->nd = nd;
252
    if (nd && nd->vlan) {
253
        s->vc = qemu_new_vlan_client(nd->vlan, nd->model, nd->name,
254
                                     mipsnet_receive, mipsnet_can_receive, s);
255
    } else {
256
        s->vc = NULL;
257
    }
258

    
259
    qemu_format_nic_info_str(s->vc, s->nd->macaddr);
260

    
261
    mipsnet_reset(s);
262
    register_savevm("mipsnet", 0, 0, mipsnet_save, mipsnet_load, s);
263
}