Statistics
| Branch: | Revision:

root / hw / smc91c111.c @ 80337b66

History | View | Annotate | Download (17.9 kB)

1 80337b66 bellard
/* 
2 80337b66 bellard
 * SMSC 91C111 Ethernet interface emulation
3 80337b66 bellard
 *
4 80337b66 bellard
 * Copyright (c) 2005 CodeSourcery, LLC.
5 80337b66 bellard
 * Written by Paul Brook
6 80337b66 bellard
 *
7 80337b66 bellard
 * This code is licenced under the GPL
8 80337b66 bellard
 */
9 80337b66 bellard
10 80337b66 bellard
#include "vl.h"
11 80337b66 bellard
/* For crc32 */
12 80337b66 bellard
#include <zlib.h>
13 80337b66 bellard
14 80337b66 bellard
/* Number of 2k memory pages available.  */
15 80337b66 bellard
#define NUM_PACKETS 4
16 80337b66 bellard
17 80337b66 bellard
typedef struct {
18 80337b66 bellard
    uint32_t base;
19 80337b66 bellard
    VLANClientState *vc;
20 80337b66 bellard
    uint16_t tcr;
21 80337b66 bellard
    uint16_t rcr;
22 80337b66 bellard
    uint16_t cr;
23 80337b66 bellard
    uint16_t ctr;
24 80337b66 bellard
    uint16_t gpr;
25 80337b66 bellard
    uint16_t ptr;
26 80337b66 bellard
    uint16_t ercv;
27 80337b66 bellard
    void *pic;
28 80337b66 bellard
    int irq;
29 80337b66 bellard
    int bank;
30 80337b66 bellard
    int packet_num;
31 80337b66 bellard
    int tx_alloc;
32 80337b66 bellard
    /* Bitmask of allocated packets.  */
33 80337b66 bellard
    int allocated;
34 80337b66 bellard
    int tx_fifo_len;
35 80337b66 bellard
    int tx_fifo[NUM_PACKETS];
36 80337b66 bellard
    int rx_fifo_len;
37 80337b66 bellard
    int rx_fifo[NUM_PACKETS];
38 80337b66 bellard
    /* Packet buffer memory.  */
39 80337b66 bellard
    uint8_t data[2048][NUM_PACKETS];
40 80337b66 bellard
    uint8_t int_level;
41 80337b66 bellard
    uint8_t int_mask;
42 80337b66 bellard
    uint8_t macaddr[6];
43 80337b66 bellard
} smc91c111_state;
44 80337b66 bellard
45 80337b66 bellard
#define RCR_SOFT_RST  0x8000
46 80337b66 bellard
#define RCR_STRIP_CRC 0x0200
47 80337b66 bellard
#define RCR_RXEN      0x0100
48 80337b66 bellard
49 80337b66 bellard
#define TCR_EPH_LOOP  0x2000
50 80337b66 bellard
#define TCR_NOCRC     0x0100
51 80337b66 bellard
#define TCR_PAD_EN    0x0080
52 80337b66 bellard
#define TCR_FORCOL    0x0004
53 80337b66 bellard
#define TCR_LOOP      0x0002
54 80337b66 bellard
#define TCR_TXEN      0x0001
55 80337b66 bellard
56 80337b66 bellard
#define INT_MD        0x80
57 80337b66 bellard
#define INT_ERCV      0x40
58 80337b66 bellard
#define INT_EPH       0x20
59 80337b66 bellard
#define INT_RX_OVRN   0x10
60 80337b66 bellard
#define INT_ALLOC     0x08
61 80337b66 bellard
#define INT_TX_EMPTY  0x04
62 80337b66 bellard
#define INT_TX        0x02
63 80337b66 bellard
#define INT_RCV       0x01
64 80337b66 bellard
65 80337b66 bellard
#define CTR_AUTO_RELEASE  0x0800
66 80337b66 bellard
#define CTR_RELOAD        0x0002
67 80337b66 bellard
#define CTR_STORE         0x0001
68 80337b66 bellard
69 80337b66 bellard
#define RS_ALGNERR      0x8000
70 80337b66 bellard
#define RS_BRODCAST     0x4000
71 80337b66 bellard
#define RS_BADCRC       0x2000
72 80337b66 bellard
#define RS_ODDFRAME     0x1000
73 80337b66 bellard
#define RS_TOOLONG      0x0800
74 80337b66 bellard
#define RS_TOOSHORT     0x0400
75 80337b66 bellard
#define RS_MULTICAST    0x0001
76 80337b66 bellard
77 80337b66 bellard
/* Update interrupt status.  */
78 80337b66 bellard
static void smc91c111_update(smc91c111_state *s)
79 80337b66 bellard
{
80 80337b66 bellard
    int level;
81 80337b66 bellard
82 80337b66 bellard
    if (s->tx_fifo_len == 0)
83 80337b66 bellard
        s->int_level |= INT_TX_EMPTY;
84 80337b66 bellard
    level = (s->int_level & s->int_mask) != 0;
85 80337b66 bellard
    pic_set_irq_new(s->pic, s->irq, level);
86 80337b66 bellard
}
87 80337b66 bellard
88 80337b66 bellard
/* Try to allocate a packet.  Returns 0x80 on failure.  */
89 80337b66 bellard
static int smc91c111_allocate_packet(smc91c111_state *s)
90 80337b66 bellard
{
91 80337b66 bellard
    int i;
92 80337b66 bellard
    if (s->allocated == (1 << NUM_PACKETS) - 1) {
93 80337b66 bellard
        return 0x80;
94 80337b66 bellard
    }
95 80337b66 bellard
96 80337b66 bellard
    for (i = 0; i < NUM_PACKETS; i++) {
97 80337b66 bellard
        if ((s->allocated & (1 << i)) == 0)
98 80337b66 bellard
            break;
99 80337b66 bellard
    }
100 80337b66 bellard
    s->allocated |= 1 << i;
101 80337b66 bellard
    return i;
102 80337b66 bellard
}
103 80337b66 bellard
104 80337b66 bellard
105 80337b66 bellard
/* Process a pending TX allocate.  */
106 80337b66 bellard
static void smc91c111_tx_alloc(smc91c111_state *s)
107 80337b66 bellard
{
108 80337b66 bellard
    s->tx_alloc = smc91c111_allocate_packet(s);
109 80337b66 bellard
    if (s->tx_alloc == 0x80)
110 80337b66 bellard
        return;
111 80337b66 bellard
    s->int_level |= INT_ALLOC;
112 80337b66 bellard
    smc91c111_update(s);
113 80337b66 bellard
}
114 80337b66 bellard
115 80337b66 bellard
/* Remove and item from the RX FIFO.  */
116 80337b66 bellard
static void smc91c111_pop_rx_fifo(smc91c111_state *s)
117 80337b66 bellard
{
118 80337b66 bellard
    int i;
119 80337b66 bellard
120 80337b66 bellard
    s->rx_fifo_len--;
121 80337b66 bellard
    if (s->rx_fifo_len) {
122 80337b66 bellard
        for (i = 0; i < s->rx_fifo_len; i++)
123 80337b66 bellard
            s->rx_fifo[i] = s->rx_fifo[i + 1];
124 80337b66 bellard
        s->int_level |= INT_RCV;
125 80337b66 bellard
    } else {
126 80337b66 bellard
        s->int_level &= ~INT_RCV;
127 80337b66 bellard
    }
128 80337b66 bellard
    smc91c111_update(s);
129 80337b66 bellard
}
130 80337b66 bellard
131 80337b66 bellard
/* Release the memory allocated to a packet.  */
132 80337b66 bellard
static void smc91c111_release_packet(smc91c111_state *s, int packet)
133 80337b66 bellard
{
134 80337b66 bellard
    s->allocated &= ~(1 << packet);
135 80337b66 bellard
    if (s->tx_alloc == 0x80)
136 80337b66 bellard
        smc91c111_tx_alloc(s);
137 80337b66 bellard
}
138 80337b66 bellard
139 80337b66 bellard
/* Flush the TX FIFO.  */
140 80337b66 bellard
static void smc91c111_do_tx(smc91c111_state *s)
141 80337b66 bellard
{
142 80337b66 bellard
    int i;
143 80337b66 bellard
    int len;
144 80337b66 bellard
    int control;
145 80337b66 bellard
    int add_crc;
146 80337b66 bellard
    uint32_t crc;
147 80337b66 bellard
    int packetnum;
148 80337b66 bellard
    uint8_t *p;
149 80337b66 bellard
150 80337b66 bellard
    if ((s->tcr & TCR_TXEN) == 0)
151 80337b66 bellard
        return;
152 80337b66 bellard
    if (s->tx_fifo_len == 0)
153 80337b66 bellard
        return;
154 80337b66 bellard
    for (i = 0; i < s->tx_fifo_len; i++) {
155 80337b66 bellard
        packetnum = s->tx_fifo[i];
156 80337b66 bellard
        p = &s->data[packetnum][0];
157 80337b66 bellard
        /* Set status word.  */
158 80337b66 bellard
        *(p++) = 0x01;
159 80337b66 bellard
        *(p++) = 0x40;
160 80337b66 bellard
        len = *(p++);
161 80337b66 bellard
        len |= ((int)*(p++)) << 8;
162 80337b66 bellard
        len -= 6;
163 80337b66 bellard
        control = p[len + 1];
164 80337b66 bellard
        if (control & 0x20)
165 80337b66 bellard
            len++;
166 80337b66 bellard
        /* ??? This overwrites the data following the buffer.
167 80337b66 bellard
           Don't know what real hardware does.  */
168 80337b66 bellard
        if (len < 64 && (s->tcr & TCR_PAD_EN)) {
169 80337b66 bellard
            memset(p + len, 0, 64 - len);
170 80337b66 bellard
            len = 64;
171 80337b66 bellard
        }
172 80337b66 bellard
#if 0
173 80337b66 bellard
        /* The card is supposed to append the CRC to the frame.  However
174 80337b66 bellard
           none of the other network traffic has the CRC appended.
175 80337b66 bellard
           Suspect this is low level ethernet detail we don't need to worry
176 80337b66 bellard
           about.  */
177 80337b66 bellard
        add_crc = (control & 0x10) || (s->tcr & TCR_NOCRC) == 0;
178 80337b66 bellard
        if (add_crc) {
179 80337b66 bellard
            crc = crc32(~0, p, len);
180 80337b66 bellard
            memcpy(p + len, &crc, 4);
181 80337b66 bellard
            len += 4;
182 80337b66 bellard
        }
183 80337b66 bellard
#else
184 80337b66 bellard
        add_crc = 0;
185 80337b66 bellard
#endif
186 80337b66 bellard
        if (s->ctr & CTR_AUTO_RELEASE)
187 80337b66 bellard
            smc91c111_release_packet(s, packetnum);
188 80337b66 bellard
        qemu_send_packet(s->vc, p, len);
189 80337b66 bellard
    }
190 80337b66 bellard
    s->tx_fifo_len = 0;
191 80337b66 bellard
    if ((s->ctr & CTR_AUTO_RELEASE) == 0)
192 80337b66 bellard
        s->int_level |= INT_TX;
193 80337b66 bellard
    smc91c111_update(s);
194 80337b66 bellard
}
195 80337b66 bellard
196 80337b66 bellard
/* Add a packet to the TX FIFO.  */
197 80337b66 bellard
static void smc91c111_queue_tx(smc91c111_state *s, int packet)
198 80337b66 bellard
{
199 80337b66 bellard
    if (s->tx_fifo_len == NUM_PACKETS)
200 80337b66 bellard
        return;
201 80337b66 bellard
    s->tx_fifo[s->tx_fifo_len++] = packet;
202 80337b66 bellard
    smc91c111_do_tx(s);
203 80337b66 bellard
}
204 80337b66 bellard
205 80337b66 bellard
static void smc91c111_reset(smc91c111_state *s)
206 80337b66 bellard
{
207 80337b66 bellard
    s->bank = 0;
208 80337b66 bellard
    s->tx_fifo_len = 0;
209 80337b66 bellard
    s->rx_fifo_len = 0;
210 80337b66 bellard
    s->allocated = 0;
211 80337b66 bellard
    s->packet_num = 0;
212 80337b66 bellard
    s->tx_alloc = 0;
213 80337b66 bellard
    s->tcr = 0;
214 80337b66 bellard
    s->rcr = 0;
215 80337b66 bellard
    s->cr = 0xa0b1;
216 80337b66 bellard
    s->ctr = 0x1210;
217 80337b66 bellard
    s->ptr = 0;
218 80337b66 bellard
    s->ercv = 0x1f;
219 80337b66 bellard
    s->int_level = INT_TX_EMPTY;
220 80337b66 bellard
    s->int_mask = 0;
221 80337b66 bellard
    smc91c111_update(s);
222 80337b66 bellard
}
223 80337b66 bellard
224 80337b66 bellard
#define SET_LOW(name, val) s->name = (s->name & 0xff00) | val
225 80337b66 bellard
#define SET_HIGH(name, val) s->name = (s->name & 0xff) | (val << 8)
226 80337b66 bellard
227 80337b66 bellard
static void smc91c111_writeb(void *opaque, target_phys_addr_t offset,
228 80337b66 bellard
                             uint32_t value)
229 80337b66 bellard
{
230 80337b66 bellard
    smc91c111_state *s = (smc91c111_state *)opaque;
231 80337b66 bellard
232 80337b66 bellard
    offset -= s->base;
233 80337b66 bellard
    if (offset == 14) {
234 80337b66 bellard
        s->bank = value;
235 80337b66 bellard
        return;
236 80337b66 bellard
    }
237 80337b66 bellard
    if (offset == 15)
238 80337b66 bellard
        return;
239 80337b66 bellard
    switch (s->bank) {
240 80337b66 bellard
    case 0:
241 80337b66 bellard
        switch (offset) {
242 80337b66 bellard
        case 0: /* TCR */
243 80337b66 bellard
            SET_LOW(tcr, value);
244 80337b66 bellard
            return;
245 80337b66 bellard
        case 1:
246 80337b66 bellard
            SET_HIGH(tcr, value);
247 80337b66 bellard
            return;
248 80337b66 bellard
        case 4: /* RCR */
249 80337b66 bellard
            SET_LOW(rcr, value);
250 80337b66 bellard
            return;
251 80337b66 bellard
        case 5:
252 80337b66 bellard
            SET_HIGH(rcr, value);
253 80337b66 bellard
            if (s->rcr & RCR_SOFT_RST)
254 80337b66 bellard
                smc91c111_reset(s);
255 80337b66 bellard
            return;
256 80337b66 bellard
        case 10: case 11: /* RPCR */
257 80337b66 bellard
            /* Ignored */
258 80337b66 bellard
            return;
259 80337b66 bellard
        }
260 80337b66 bellard
        break;
261 80337b66 bellard
262 80337b66 bellard
    case 1:
263 80337b66 bellard
        switch (offset) {
264 80337b66 bellard
        case 0: /* CONFIG */
265 80337b66 bellard
            SET_LOW(cr, value);
266 80337b66 bellard
            return;
267 80337b66 bellard
        case 1:
268 80337b66 bellard
            SET_HIGH(cr,value);
269 80337b66 bellard
            return;
270 80337b66 bellard
        case 2: case 3: /* BASE */
271 80337b66 bellard
        case 4: case 5: case 6: case 7: case 8: case 9: /* IA */
272 80337b66 bellard
            /* Not implemented.  */
273 80337b66 bellard
            return;
274 80337b66 bellard
        case 10: /* Genral Purpose */
275 80337b66 bellard
            SET_LOW(gpr, value);
276 80337b66 bellard
            return;
277 80337b66 bellard
        case 11:
278 80337b66 bellard
            SET_HIGH(gpr, value);
279 80337b66 bellard
            return;
280 80337b66 bellard
        case 12: /* Control */
281 80337b66 bellard
            if (value & 1)
282 80337b66 bellard
                fprintf(stderr, "smc91c111:EEPROM store not implemented\n");
283 80337b66 bellard
            if (value & 2)
284 80337b66 bellard
                fprintf(stderr, "smc91c111:EEPROM reload not implemented\n");
285 80337b66 bellard
            value &= ~3;
286 80337b66 bellard
            SET_LOW(ctr, value);
287 80337b66 bellard
            return;
288 80337b66 bellard
        case 13:
289 80337b66 bellard
            SET_HIGH(ctr, value);
290 80337b66 bellard
            return;
291 80337b66 bellard
        }
292 80337b66 bellard
        break;
293 80337b66 bellard
294 80337b66 bellard
    case 2:
295 80337b66 bellard
        switch (offset) {
296 80337b66 bellard
        case 0: /* MMU Command */
297 80337b66 bellard
            switch (value >> 5) {
298 80337b66 bellard
            case 0: /* no-op */
299 80337b66 bellard
                break;
300 80337b66 bellard
            case 1: /* Allocate for TX.  */
301 80337b66 bellard
                s->tx_alloc = 0x80;
302 80337b66 bellard
                s->int_level &= ~INT_ALLOC;
303 80337b66 bellard
                smc91c111_update(s);
304 80337b66 bellard
                smc91c111_tx_alloc(s);
305 80337b66 bellard
                break;
306 80337b66 bellard
            case 2: /* Reset MMU.  */
307 80337b66 bellard
                s->allocated = 0;
308 80337b66 bellard
                s->tx_fifo_len = 0;
309 80337b66 bellard
                s->rx_fifo_len = 0;
310 80337b66 bellard
                s->tx_alloc = 0;
311 80337b66 bellard
                break;
312 80337b66 bellard
            case 3: /* Remove from RX FIFO.  */
313 80337b66 bellard
                smc91c111_pop_rx_fifo(s);
314 80337b66 bellard
                break;
315 80337b66 bellard
            case 4: /* Remove from RX FIFO and release.  */
316 80337b66 bellard
                if (s->rx_fifo_len > 0) {
317 80337b66 bellard
                    smc91c111_release_packet(s, s->rx_fifo[0]);
318 80337b66 bellard
                }
319 80337b66 bellard
                smc91c111_pop_rx_fifo(s);
320 80337b66 bellard
                break;
321 80337b66 bellard
            case 5: /* Release.  */
322 80337b66 bellard
                smc91c111_release_packet(s, s->packet_num);
323 80337b66 bellard
                break;
324 80337b66 bellard
            case 6: /* Add to TX FIFO.  */
325 80337b66 bellard
                smc91c111_queue_tx(s, s->packet_num);
326 80337b66 bellard
                break;
327 80337b66 bellard
            case 7: /* Reset TX FIFO.  */
328 80337b66 bellard
                s->tx_fifo_len = 0;
329 80337b66 bellard
                break;
330 80337b66 bellard
            }
331 80337b66 bellard
            return;
332 80337b66 bellard
        case 1:
333 80337b66 bellard
            /* Ignore.  */
334 80337b66 bellard
            return;
335 80337b66 bellard
        case 2: /* Packet Number Register */
336 80337b66 bellard
            s->packet_num = value;
337 80337b66 bellard
            return;
338 80337b66 bellard
        case 3: case 4: case 5:
339 80337b66 bellard
            /* Should be readonly, but linux writes to them anyway. Ignore.  */
340 80337b66 bellard
            return;
341 80337b66 bellard
        case 6: /* Pointer */
342 80337b66 bellard
            SET_LOW(ptr, value);
343 80337b66 bellard
            return;
344 80337b66 bellard
        case 7:
345 80337b66 bellard
            SET_HIGH(ptr, value);
346 80337b66 bellard
            return;
347 80337b66 bellard
        case 8: case 9: case 10: case 11: /* Data */
348 80337b66 bellard
            {
349 80337b66 bellard
                int p;
350 80337b66 bellard
                int n;
351 80337b66 bellard
352 80337b66 bellard
                if (s->ptr & 0x8000)
353 80337b66 bellard
                    n = s->rx_fifo[0];
354 80337b66 bellard
                else
355 80337b66 bellard
                    n = s->packet_num;
356 80337b66 bellard
                p = s->ptr & 0x07ff;
357 80337b66 bellard
                if (s->ptr & 0x4000) {
358 80337b66 bellard
                    s->ptr = (s->ptr & 0xf800) | ((s->ptr + 1) & 0x7ff);
359 80337b66 bellard
                } else {
360 80337b66 bellard
                    p += (offset & 3);
361 80337b66 bellard
                }
362 80337b66 bellard
                s->data[n][p] = value;
363 80337b66 bellard
            }
364 80337b66 bellard
            return;
365 80337b66 bellard
        case 12: /* Interrupt ACK.  */
366 80337b66 bellard
            s->int_level &= ~(value & 0xd6);
367 80337b66 bellard
            smc91c111_update(s);
368 80337b66 bellard
            return;
369 80337b66 bellard
        case 13: /* Interrupt mask.  */
370 80337b66 bellard
            s->int_mask = value;
371 80337b66 bellard
            smc91c111_update(s);
372 80337b66 bellard
            return;
373 80337b66 bellard
        }
374 80337b66 bellard
        break;;
375 80337b66 bellard
376 80337b66 bellard
    case 3:
377 80337b66 bellard
        switch (offset) {
378 80337b66 bellard
        case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
379 80337b66 bellard
            /* Multicast table.  */
380 80337b66 bellard
            /* Not implemented.  */
381 80337b66 bellard
            return;
382 80337b66 bellard
        case 8: case 9: /* Management Interface.  */
383 80337b66 bellard
            /* Not implemented.  */
384 80337b66 bellard
            return;
385 80337b66 bellard
        case 12: /* Early receive.  */
386 80337b66 bellard
            s->ercv = value & 0x1f;
387 80337b66 bellard
        case 13:
388 80337b66 bellard
            /* Ignore.  */
389 80337b66 bellard
            return;
390 80337b66 bellard
        }
391 80337b66 bellard
        break;
392 80337b66 bellard
    }
393 80337b66 bellard
    cpu_abort (cpu_single_env, "smc91c111_write: Bad reg %d:%x\n",
394 80337b66 bellard
               s->bank, offset);
395 80337b66 bellard
}
396 80337b66 bellard
397 80337b66 bellard
static uint32_t smc91c111_readb(void *opaque, target_phys_addr_t offset)
398 80337b66 bellard
{
399 80337b66 bellard
    smc91c111_state *s = (smc91c111_state *)opaque;
400 80337b66 bellard
401 80337b66 bellard
    offset -= s->base;
402 80337b66 bellard
    if (offset == 14) {
403 80337b66 bellard
        return s->bank;
404 80337b66 bellard
    }
405 80337b66 bellard
    if (offset == 15)
406 80337b66 bellard
        return 0x33;
407 80337b66 bellard
    switch (s->bank) {
408 80337b66 bellard
    case 0:
409 80337b66 bellard
        switch (offset) {
410 80337b66 bellard
        case 0: /* TCR */
411 80337b66 bellard
            return s->tcr & 0xff;
412 80337b66 bellard
        case 1:
413 80337b66 bellard
            return s->tcr >> 8;
414 80337b66 bellard
        case 2: /* EPH Status */
415 80337b66 bellard
            return 0;
416 80337b66 bellard
        case 3:
417 80337b66 bellard
            return 0x40;
418 80337b66 bellard
        case 4: /* RCR */
419 80337b66 bellard
            return s->rcr & 0xff;
420 80337b66 bellard
        case 5:
421 80337b66 bellard
            return s->rcr >> 8;
422 80337b66 bellard
        case 6: /* Counter */
423 80337b66 bellard
        case 7:
424 80337b66 bellard
            /* Not implemented.  */
425 80337b66 bellard
            return 0;
426 80337b66 bellard
        case 8: /* Free memory available.  */
427 80337b66 bellard
            {
428 80337b66 bellard
                int i;
429 80337b66 bellard
                int n;
430 80337b66 bellard
                n = 0;
431 80337b66 bellard
                for (i = 0; i < NUM_PACKETS; i++) {
432 80337b66 bellard
                    if (s->allocated & (1 << i))
433 80337b66 bellard
                        n++;
434 80337b66 bellard
                }
435 80337b66 bellard
                return n;
436 80337b66 bellard
            }
437 80337b66 bellard
        case 9: /* Memory size.  */
438 80337b66 bellard
            return NUM_PACKETS;
439 80337b66 bellard
        case 10: case 11: /* RPCR */
440 80337b66 bellard
            /* Not implemented.  */
441 80337b66 bellard
            return 0;
442 80337b66 bellard
        }
443 80337b66 bellard
        break;
444 80337b66 bellard
445 80337b66 bellard
    case 1:
446 80337b66 bellard
        switch (offset) {
447 80337b66 bellard
        case 0: /* CONFIG */
448 80337b66 bellard
            return s->cr & 0xff;
449 80337b66 bellard
        case 1:
450 80337b66 bellard
            return s->cr >> 8;
451 80337b66 bellard
        case 2: case 3: /* BASE */
452 80337b66 bellard
            /* Not implemented.  */
453 80337b66 bellard
            return 0;
454 80337b66 bellard
        case 4: case 5: case 6: case 7: case 8: case 9: /* IA */
455 80337b66 bellard
            return s->macaddr[offset - 4];
456 80337b66 bellard
        case 10: /* General Purpose */
457 80337b66 bellard
            return s->gpr & 0xff;
458 80337b66 bellard
        case 11:
459 80337b66 bellard
            return s->gpr >> 8;
460 80337b66 bellard
        case 12: /* Control */
461 80337b66 bellard
            return s->ctr & 0xff;
462 80337b66 bellard
        case 13:
463 80337b66 bellard
            return s->ctr >> 8;
464 80337b66 bellard
        }
465 80337b66 bellard
        break;
466 80337b66 bellard
467 80337b66 bellard
    case 2:
468 80337b66 bellard
        switch (offset) {
469 80337b66 bellard
        case 0: case 1: /* MMUCR Busy bit.  */
470 80337b66 bellard
            return 0;
471 80337b66 bellard
        case 2: /* Packet Number.  */
472 80337b66 bellard
            return s->packet_num;
473 80337b66 bellard
        case 3: /* Allocation Result.  */
474 80337b66 bellard
            return s->tx_alloc;
475 80337b66 bellard
        case 4: /* TX FIFO */
476 80337b66 bellard
            if (s->tx_fifo_len == 0)
477 80337b66 bellard
                return 0x80;
478 80337b66 bellard
            else
479 80337b66 bellard
                return s->tx_fifo[0];
480 80337b66 bellard
        case 5: /* RX FIFO */
481 80337b66 bellard
            if (s->rx_fifo_len == 0)
482 80337b66 bellard
                return 0x80;
483 80337b66 bellard
            else
484 80337b66 bellard
                return s->rx_fifo[0];
485 80337b66 bellard
        case 6: /* Pointer */
486 80337b66 bellard
            return s->ptr & 0xff;
487 80337b66 bellard
        case 7:
488 80337b66 bellard
            return (s->ptr >> 8) & 0xf7;
489 80337b66 bellard
        case 8: case 9: case 10: case 11: /* Data */
490 80337b66 bellard
            {
491 80337b66 bellard
                int p;
492 80337b66 bellard
                int n;
493 80337b66 bellard
494 80337b66 bellard
                if (s->ptr & 0x8000)
495 80337b66 bellard
                    n = s->rx_fifo[0];
496 80337b66 bellard
                else
497 80337b66 bellard
                    n = s->packet_num;
498 80337b66 bellard
                p = s->ptr & 0x07ff;
499 80337b66 bellard
                if (s->ptr & 0x4000) {
500 80337b66 bellard
                    s->ptr = (s->ptr & 0xf800) | ((s->ptr + 1) & 0x07ff);
501 80337b66 bellard
                } else {
502 80337b66 bellard
                    p += (offset & 3);
503 80337b66 bellard
                }
504 80337b66 bellard
                return s->data[n][p];
505 80337b66 bellard
            }
506 80337b66 bellard
        case 12: /* Interrupt status.  */
507 80337b66 bellard
            return s->int_level;
508 80337b66 bellard
        case 13: /* Interrupt mask.  */
509 80337b66 bellard
            return s->int_mask;
510 80337b66 bellard
        }
511 80337b66 bellard
        break;
512 80337b66 bellard
513 80337b66 bellard
    case 3:
514 80337b66 bellard
        switch (offset) {
515 80337b66 bellard
        case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
516 80337b66 bellard
            /* Multicast table.  */
517 80337b66 bellard
            /* Not implemented.  */
518 80337b66 bellard
            return 0;
519 80337b66 bellard
        case 8: /* Management Interface.  */
520 80337b66 bellard
            /* Not implemented.  */
521 80337b66 bellard
            return 0x30;
522 80337b66 bellard
        case 9:
523 80337b66 bellard
            return 0x33;
524 80337b66 bellard
        case 10: /* Revision.  */
525 80337b66 bellard
            return 0x91;
526 80337b66 bellard
        case 11:
527 80337b66 bellard
            return 0x33;
528 80337b66 bellard
        case 12:
529 80337b66 bellard
            return s->ercv;
530 80337b66 bellard
        case 13:
531 80337b66 bellard
            return 0;
532 80337b66 bellard
        }
533 80337b66 bellard
        break;
534 80337b66 bellard
    }
535 80337b66 bellard
    cpu_abort (cpu_single_env, "smc91c111_read: Bad reg %d:%x\n",
536 80337b66 bellard
               s->bank, offset);
537 80337b66 bellard
    return 0;
538 80337b66 bellard
}
539 80337b66 bellard
540 80337b66 bellard
static void smc91c111_writew(void *opaque, target_phys_addr_t offset,
541 80337b66 bellard
                             uint32_t value)
542 80337b66 bellard
{
543 80337b66 bellard
    smc91c111_writeb(opaque, offset, value & 0xff);
544 80337b66 bellard
    smc91c111_writeb(opaque, offset + 1, value >> 8);
545 80337b66 bellard
}
546 80337b66 bellard
547 80337b66 bellard
static void smc91c111_writel(void *opaque, target_phys_addr_t offset,
548 80337b66 bellard
                             uint32_t value)
549 80337b66 bellard
{
550 80337b66 bellard
    smc91c111_state *s = (smc91c111_state *)opaque;
551 80337b66 bellard
    /* 32-bit writes to offset 0xc only actually write to the bank select
552 80337b66 bellard
       register (offset 0xe)  */
553 80337b66 bellard
    if (offset != s->base + 0xc)
554 80337b66 bellard
        smc91c111_writew(opaque, offset, value & 0xffff);
555 80337b66 bellard
    smc91c111_writew(opaque, offset + 2, value >> 16);
556 80337b66 bellard
}
557 80337b66 bellard
558 80337b66 bellard
static uint32_t smc91c111_readw(void *opaque, target_phys_addr_t offset)
559 80337b66 bellard
{
560 80337b66 bellard
    uint32_t val;
561 80337b66 bellard
    val = smc91c111_readb(opaque, offset);
562 80337b66 bellard
    val |= smc91c111_readb(opaque, offset + 1) << 8;
563 80337b66 bellard
    return val;
564 80337b66 bellard
}
565 80337b66 bellard
566 80337b66 bellard
static uint32_t smc91c111_readl(void *opaque, target_phys_addr_t offset)
567 80337b66 bellard
{
568 80337b66 bellard
    uint32_t val;
569 80337b66 bellard
    val = smc91c111_readw(opaque, offset);
570 80337b66 bellard
    val |= smc91c111_readw(opaque, offset + 2) << 16;
571 80337b66 bellard
    return val;
572 80337b66 bellard
}
573 80337b66 bellard
574 80337b66 bellard
static void smc91c111_receive(void *opaque, const uint8_t *buf, int size)
575 80337b66 bellard
{
576 80337b66 bellard
    smc91c111_state *s = (smc91c111_state *)opaque;
577 80337b66 bellard
    int status;
578 80337b66 bellard
    int packetsize;
579 80337b66 bellard
    uint32_t crc;
580 80337b66 bellard
    int packetnum;
581 80337b66 bellard
    uint8_t *p;
582 80337b66 bellard
583 80337b66 bellard
    if ((s->rcr & RCR_RXEN) == 0 || (s->rcr & RCR_SOFT_RST))
584 80337b66 bellard
        return;
585 80337b66 bellard
    /* Short packets are padded with zeros.  Recieveing a packet
586 80337b66 bellard
       < 64 bytes long is considered an error condition.  */
587 80337b66 bellard
    if (size < 64)
588 80337b66 bellard
        packetsize = 64;
589 80337b66 bellard
    else
590 80337b66 bellard
        packetsize = (size & ~1);
591 80337b66 bellard
    packetsize += 6;
592 80337b66 bellard
    crc = (s->rcr & RCR_STRIP_CRC) == 0;
593 80337b66 bellard
    if (crc)
594 80337b66 bellard
        packetsize += 4;
595 80337b66 bellard
    /* TODO: Flag overrun and receive errors.  */
596 80337b66 bellard
    if (packetsize > 2048)
597 80337b66 bellard
        return;
598 80337b66 bellard
    packetnum = smc91c111_allocate_packet(s);
599 80337b66 bellard
    if (packetnum == 0x80)
600 80337b66 bellard
        return;
601 80337b66 bellard
    s->rx_fifo[s->rx_fifo_len++] = packetnum;
602 80337b66 bellard
603 80337b66 bellard
    p = &s->data[packetnum][0];
604 80337b66 bellard
    /* ??? Multicast packets?  */
605 80337b66 bellard
    status = 0;
606 80337b66 bellard
    if (size > 1518)
607 80337b66 bellard
        status |= RS_TOOLONG;
608 80337b66 bellard
    if (size & 1)
609 80337b66 bellard
        status |= RS_ODDFRAME;
610 80337b66 bellard
    *(p++) = status & 0xff;
611 80337b66 bellard
    *(p++) = status >> 8;
612 80337b66 bellard
    *(p++) = packetsize & 0xff;
613 80337b66 bellard
    *(p++) = packetsize >> 8;
614 80337b66 bellard
    memcpy(p, buf, size & ~1);
615 80337b66 bellard
    p += (size & ~1);
616 80337b66 bellard
    /* Pad short packets.  */
617 80337b66 bellard
    if (size < 64) {
618 80337b66 bellard
        int pad;
619 80337b66 bellard
        
620 80337b66 bellard
        if (size & 1)
621 80337b66 bellard
            *(p++) = buf[size - 1];
622 80337b66 bellard
        pad = 64 - size;
623 80337b66 bellard
        memset(p, 0, pad);
624 80337b66 bellard
        p += pad;
625 80337b66 bellard
        size = 64;
626 80337b66 bellard
    }
627 80337b66 bellard
    /* It's not clear if the CRC should go before or after the last byte in
628 80337b66 bellard
       odd sized packets.  Linux disables the CRC, so that's no help.
629 80337b66 bellard
       The pictures in the documentation show the CRC aligned on a 16-bit
630 80337b66 bellard
       boundary before the last odd byte, so that's what we do.  */
631 80337b66 bellard
    if (crc) {
632 80337b66 bellard
        crc = crc32(~0, buf, size);
633 80337b66 bellard
        *(p++) = crc & 0xff; crc >>= 8;
634 80337b66 bellard
        *(p++) = crc & 0xff; crc >>= 8;
635 80337b66 bellard
        *(p++) = crc & 0xff; crc >>= 8;
636 80337b66 bellard
        *(p++) = crc & 0xff; crc >>= 8;
637 80337b66 bellard
    }
638 80337b66 bellard
    if (size & 1) {
639 80337b66 bellard
        *(p++) = buf[size - 1];
640 80337b66 bellard
        *(p++) = 0x60;
641 80337b66 bellard
    } else {
642 80337b66 bellard
        *(p++) = 0;
643 80337b66 bellard
        *(p++) = 0x40;
644 80337b66 bellard
    }
645 80337b66 bellard
    /* TODO: Raise early RX interrupt?  */
646 80337b66 bellard
    s->int_level |= INT_RCV;
647 80337b66 bellard
    smc91c111_update(s);
648 80337b66 bellard
}
649 80337b66 bellard
650 80337b66 bellard
static CPUReadMemoryFunc *smc91c111_readfn[] = {
651 80337b66 bellard
    smc91c111_readb,
652 80337b66 bellard
    smc91c111_readw,
653 80337b66 bellard
    smc91c111_readl
654 80337b66 bellard
};
655 80337b66 bellard
656 80337b66 bellard
static CPUWriteMemoryFunc *smc91c111_writefn[] = {
657 80337b66 bellard
    smc91c111_writeb,
658 80337b66 bellard
    smc91c111_writew,
659 80337b66 bellard
    smc91c111_writel
660 80337b66 bellard
};
661 80337b66 bellard
662 80337b66 bellard
void smc91c111_init(NICInfo *nd, uint32_t base, void *pic, int irq)
663 80337b66 bellard
{
664 80337b66 bellard
    smc91c111_state *s;
665 80337b66 bellard
    int iomemtype;
666 80337b66 bellard
667 80337b66 bellard
    s = (smc91c111_state *)qemu_mallocz(sizeof(smc91c111_state));
668 80337b66 bellard
    iomemtype = cpu_register_io_memory(0, smc91c111_readfn,
669 80337b66 bellard
                                       smc91c111_writefn, s);
670 80337b66 bellard
    cpu_register_physical_memory(base, 16, iomemtype);
671 80337b66 bellard
    s->base = base;
672 80337b66 bellard
    s->pic = pic;
673 80337b66 bellard
    s->irq = irq;
674 80337b66 bellard
    memcpy(s->macaddr, nd->macaddr, 6);
675 80337b66 bellard
676 80337b66 bellard
    smc91c111_reset(s);
677 80337b66 bellard
678 80337b66 bellard
    s->vc = qemu_new_vlan_client(nd->vlan, smc91c111_receive, s);
679 80337b66 bellard
    /* ??? Save/restore.  */
680 80337b66 bellard
}