blob: 1eaba74e0539b4f208782a0b78a864ba46602d8f [file] [log] [blame]
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08001/*
2 * SMSC 91C111 Ethernet interface emulation
3 *
4 * Copyright (c) 2005 CodeSourcery, LLC.
5 * Written by Paul Brook
6 *
7 * This code is licenced under the GPL
8 */
9
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -070010#include "sysbus.h"
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -080011#include "net.h"
12#include "devices.h"
Ot ten Thije043b3932010-09-08 12:38:43 +010013#include "hw/hw.h"
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -080014/* For crc32 */
15#include <zlib.h>
16
17/* Number of 2k memory pages available. */
18#define NUM_PACKETS 4
Ot ten Thije043b3932010-09-08 12:38:43 +010019#define BUFFER_PER_PACKET 2048
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -080020
21typedef struct {
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -070022 SysBusDevice busdev;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -080023 VLANClientState *vc;
24 uint16_t tcr;
25 uint16_t rcr;
26 uint16_t cr;
27 uint16_t ctr;
28 uint16_t gpr;
29 uint16_t ptr;
30 uint16_t ercv;
31 qemu_irq irq;
32 int bank;
33 int packet_num;
34 int tx_alloc;
35 /* Bitmask of allocated packets. */
36 int allocated;
37 int tx_fifo_len;
38 int tx_fifo[NUM_PACKETS];
39 int rx_fifo_len;
40 int rx_fifo[NUM_PACKETS];
41 int tx_fifo_done_len;
42 int tx_fifo_done[NUM_PACKETS];
43 /* Packet buffer memory. */
Ot ten Thije043b3932010-09-08 12:38:43 +010044 uint8_t data[NUM_PACKETS][BUFFER_PER_PACKET];
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -080045 uint8_t int_level;
46 uint8_t int_mask;
47 uint8_t macaddr[6];
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -070048 int mmio_index;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -080049} smc91c111_state;
50
Ot ten Thije043b3932010-09-08 12:38:43 +010051#define SMC91C111_SAVE_VERSION 1
52
53static void smc91c111_save(QEMUFile *f, void *opaque)
54{
55 smc91c111_state *s = opaque;
56
57 /* busdev, vc, macaddr and mmio_index are linked to the host state and
58 * initialized when the emulator starts (in smc91c111_init1 below).
59 * Saving/restoring those values is therefore useless and may even be
60 * harmful, so they are omitted.
61 */
62 qemu_put_be16(f, s->tcr);
63 qemu_put_be16(f, s->rcr);
64 qemu_put_be16(f, s->cr);
65 qemu_put_be16(f, s->ctr);
66 qemu_put_be16(f, s->gpr);
67 qemu_put_be16(f, s->ptr);
68 qemu_put_be16(f, s->ercv);
69
70 qemu_put_be32(f, s->bank);
71 qemu_put_be32(f, s->packet_num);
72
73 qemu_put_be32(f, s->tx_alloc);
74 qemu_put_be32(f, s->allocated);
75 qemu_put_be32(f, s->tx_fifo_len);
76 qemu_put_buffer(f, (uint8_t*) s->tx_fifo, sizeof(s->tx_fifo));
77 qemu_put_be32(f, s->rx_fifo_len);
78 qemu_put_buffer(f, (uint8_t*) s->rx_fifo, sizeof(s->rx_fifo));
79 qemu_put_be32(f, s->tx_fifo_done_len);
80 qemu_put_buffer(f, (uint8_t*) s->tx_fifo_done, sizeof(s->tx_fifo_done));
81
82 /* Packet buffer memory. */
83 qemu_put_buffer(f, (uint8_t*) s->data, sizeof(s->data));
84 qemu_put_byte(f, s->int_level);
85 qemu_put_byte(f, s->int_mask);
86
87 /* macaddr, mmio_index omitted intentionally */
88}
89
90static int smc91c111_load(QEMUFile *f, void *opaque, int version_id)
91{
92 smc91c111_state *s = opaque;
93
94 if (version_id != SMC91C111_SAVE_VERSION) {
95 return -1;
96 }
97
98 s->tcr = qemu_get_be16(f);
99 s->rcr = qemu_get_be16(f);
100 s->cr = qemu_get_be16(f);
101 s->ctr = qemu_get_be16(f);
102 s->gpr = qemu_get_be16(f);
103 s->ptr = qemu_get_be16(f);
104 s->ercv = qemu_get_be16(f);
105
106 s->bank = qemu_get_be32(f);
107 s->packet_num = qemu_get_be32(f);
108
109 s->tx_alloc = qemu_get_be32(f);
110 s->allocated = qemu_get_be32(f);
111 s->tx_fifo_len = qemu_get_be32(f);
112 qemu_get_buffer(f, (uint8_t*) s->tx_fifo, sizeof(s->tx_fifo));
113 s->rx_fifo_len = qemu_get_be32(f);
114 qemu_get_buffer(f, (uint8_t*) s->rx_fifo, sizeof(s->rx_fifo));
115 s->tx_fifo_done_len = qemu_get_be32(f);
116 qemu_get_buffer(f, (uint8_t*) s->tx_fifo_done, sizeof(s->tx_fifo_done));
117
118 /* Packet buffer memory. */
119 qemu_get_buffer(f, (uint8_t*) s->data, sizeof(s->data));
120 s->int_level = qemu_get_byte(f);
121 s->int_mask = qemu_get_byte(f);
122
123 return 0;
124}
125
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800126#define RCR_SOFT_RST 0x8000
127#define RCR_STRIP_CRC 0x0200
128#define RCR_RXEN 0x0100
129
130#define TCR_EPH_LOOP 0x2000
131#define TCR_NOCRC 0x0100
132#define TCR_PAD_EN 0x0080
133#define TCR_FORCOL 0x0004
134#define TCR_LOOP 0x0002
135#define TCR_TXEN 0x0001
136
137#define INT_MD 0x80
138#define INT_ERCV 0x40
139#define INT_EPH 0x20
140#define INT_RX_OVRN 0x10
141#define INT_ALLOC 0x08
142#define INT_TX_EMPTY 0x04
143#define INT_TX 0x02
144#define INT_RCV 0x01
145
146#define CTR_AUTO_RELEASE 0x0800
147#define CTR_RELOAD 0x0002
148#define CTR_STORE 0x0001
149
150#define RS_ALGNERR 0x8000
151#define RS_BRODCAST 0x4000
152#define RS_BADCRC 0x2000
153#define RS_ODDFRAME 0x1000
154#define RS_TOOLONG 0x0800
155#define RS_TOOSHORT 0x0400
156#define RS_MULTICAST 0x0001
157
158/* Update interrupt status. */
159static void smc91c111_update(smc91c111_state *s)
160{
161 int level;
162
163 if (s->tx_fifo_len == 0)
164 s->int_level |= INT_TX_EMPTY;
165 if (s->tx_fifo_done_len != 0)
166 s->int_level |= INT_TX;
167 level = (s->int_level & s->int_mask) != 0;
168 qemu_set_irq(s->irq, level);
169}
170
171/* Try to allocate a packet. Returns 0x80 on failure. */
172static int smc91c111_allocate_packet(smc91c111_state *s)
173{
174 int i;
175 if (s->allocated == (1 << NUM_PACKETS) - 1) {
176 return 0x80;
177 }
178
179 for (i = 0; i < NUM_PACKETS; i++) {
180 if ((s->allocated & (1 << i)) == 0)
181 break;
182 }
183 s->allocated |= 1 << i;
184 return i;
185}
186
187
188/* Process a pending TX allocate. */
189static void smc91c111_tx_alloc(smc91c111_state *s)
190{
191 s->tx_alloc = smc91c111_allocate_packet(s);
192 if (s->tx_alloc == 0x80)
193 return;
194 s->int_level |= INT_ALLOC;
195 smc91c111_update(s);
196}
197
198/* Remove and item from the RX FIFO. */
199static void smc91c111_pop_rx_fifo(smc91c111_state *s)
200{
201 int i;
202
203 s->rx_fifo_len--;
204 if (s->rx_fifo_len) {
205 for (i = 0; i < s->rx_fifo_len; i++)
206 s->rx_fifo[i] = s->rx_fifo[i + 1];
207 s->int_level |= INT_RCV;
208 } else {
209 s->int_level &= ~INT_RCV;
210 }
211 smc91c111_update(s);
212}
213
214/* Remove an item from the TX completion FIFO. */
215static void smc91c111_pop_tx_fifo_done(smc91c111_state *s)
216{
217 int i;
218
219 if (s->tx_fifo_done_len == 0)
220 return;
221 s->tx_fifo_done_len--;
222 for (i = 0; i < s->tx_fifo_done_len; i++)
223 s->tx_fifo_done[i] = s->tx_fifo_done[i + 1];
224}
225
226/* Release the memory allocated to a packet. */
227static void smc91c111_release_packet(smc91c111_state *s, int packet)
228{
229 s->allocated &= ~(1 << packet);
230 if (s->tx_alloc == 0x80)
231 smc91c111_tx_alloc(s);
232}
233
234/* Flush the TX FIFO. */
235static void smc91c111_do_tx(smc91c111_state *s)
236{
237 int i;
238 int len;
239 int control;
240 int add_crc;
241 int packetnum;
242 uint8_t *p;
243
244 if ((s->tcr & TCR_TXEN) == 0)
245 return;
246 if (s->tx_fifo_len == 0)
247 return;
248 for (i = 0; i < s->tx_fifo_len; i++) {
249 packetnum = s->tx_fifo[i];
250 p = &s->data[packetnum][0];
251 /* Set status word. */
252 *(p++) = 0x01;
253 *(p++) = 0x40;
254 len = *(p++);
255 len |= ((int)*(p++)) << 8;
256 len -= 6;
257 control = p[len + 1];
258 if (control & 0x20)
259 len++;
260 /* ??? This overwrites the data following the buffer.
261 Don't know what real hardware does. */
262 if (len < 64 && (s->tcr & TCR_PAD_EN)) {
263 memset(p + len, 0, 64 - len);
264 len = 64;
265 }
266#if 0
267 /* The card is supposed to append the CRC to the frame. However
268 none of the other network traffic has the CRC appended.
269 Suspect this is low level ethernet detail we don't need to worry
270 about. */
271 add_crc = (control & 0x10) || (s->tcr & TCR_NOCRC) == 0;
272 if (add_crc) {
273 uint32_t crc;
274
275 crc = crc32(~0, p, len);
276 memcpy(p + len, &crc, 4);
277 len += 4;
278 }
279#else
280 add_crc = 0;
281#endif
282 if (s->ctr & CTR_AUTO_RELEASE)
283 /* Race? */
284 smc91c111_release_packet(s, packetnum);
285 else if (s->tx_fifo_done_len < NUM_PACKETS)
286 s->tx_fifo_done[s->tx_fifo_done_len++] = packetnum;
287 qemu_send_packet(s->vc, p, len);
288 }
289 s->tx_fifo_len = 0;
290 smc91c111_update(s);
291}
292
293/* Add a packet to the TX FIFO. */
294static void smc91c111_queue_tx(smc91c111_state *s, int packet)
295{
296 if (s->tx_fifo_len == NUM_PACKETS)
297 return;
298 s->tx_fifo[s->tx_fifo_len++] = packet;
299 smc91c111_do_tx(s);
300}
301
302static void smc91c111_reset(smc91c111_state *s)
303{
304 s->bank = 0;
305 s->tx_fifo_len = 0;
306 s->tx_fifo_done_len = 0;
307 s->rx_fifo_len = 0;
308 s->allocated = 0;
309 s->packet_num = 0;
310 s->tx_alloc = 0;
311 s->tcr = 0;
312 s->rcr = 0;
313 s->cr = 0xa0b1;
314 s->ctr = 0x1210;
315 s->ptr = 0;
316 s->ercv = 0x1f;
317 s->int_level = INT_TX_EMPTY;
318 s->int_mask = 0;
319 smc91c111_update(s);
320}
321
322#define SET_LOW(name, val) s->name = (s->name & 0xff00) | val
323#define SET_HIGH(name, val) s->name = (s->name & 0xff) | (val << 8)
324
325static void smc91c111_writeb(void *opaque, target_phys_addr_t offset,
326 uint32_t value)
327{
328 smc91c111_state *s = (smc91c111_state *)opaque;
329
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800330 if (offset == 14) {
331 s->bank = value;
332 return;
333 }
334 if (offset == 15)
335 return;
336 switch (s->bank) {
337 case 0:
338 switch (offset) {
339 case 0: /* TCR */
340 SET_LOW(tcr, value);
341 return;
342 case 1:
343 SET_HIGH(tcr, value);
344 return;
345 case 4: /* RCR */
346 SET_LOW(rcr, value);
347 return;
348 case 5:
349 SET_HIGH(rcr, value);
350 if (s->rcr & RCR_SOFT_RST)
351 smc91c111_reset(s);
352 return;
353 case 10: case 11: /* RPCR */
354 /* Ignored */
355 return;
356 }
357 break;
358
359 case 1:
360 switch (offset) {
361 case 0: /* CONFIG */
362 SET_LOW(cr, value);
363 return;
364 case 1:
365 SET_HIGH(cr,value);
366 return;
367 case 2: case 3: /* BASE */
368 case 4: case 5: case 6: case 7: case 8: case 9: /* IA */
369 /* Not implemented. */
370 return;
371 case 10: /* Genral Purpose */
372 SET_LOW(gpr, value);
373 return;
374 case 11:
375 SET_HIGH(gpr, value);
376 return;
377 case 12: /* Control */
378 if (value & 1)
379 fprintf(stderr, "smc91c111:EEPROM store not implemented\n");
380 if (value & 2)
381 fprintf(stderr, "smc91c111:EEPROM reload not implemented\n");
382 value &= ~3;
383 SET_LOW(ctr, value);
384 return;
385 case 13:
386 SET_HIGH(ctr, value);
387 return;
388 }
389 break;
390
391 case 2:
392 switch (offset) {
393 case 0: /* MMU Command */
394 switch (value >> 5) {
395 case 0: /* no-op */
396 break;
397 case 1: /* Allocate for TX. */
398 s->tx_alloc = 0x80;
399 s->int_level &= ~INT_ALLOC;
400 smc91c111_update(s);
401 smc91c111_tx_alloc(s);
402 break;
403 case 2: /* Reset MMU. */
404 s->allocated = 0;
405 s->tx_fifo_len = 0;
406 s->tx_fifo_done_len = 0;
407 s->rx_fifo_len = 0;
408 s->tx_alloc = 0;
409 break;
410 case 3: /* Remove from RX FIFO. */
411 smc91c111_pop_rx_fifo(s);
412 break;
413 case 4: /* Remove from RX FIFO and release. */
414 if (s->rx_fifo_len > 0) {
415 smc91c111_release_packet(s, s->rx_fifo[0]);
416 }
417 smc91c111_pop_rx_fifo(s);
418 break;
419 case 5: /* Release. */
420 smc91c111_release_packet(s, s->packet_num);
421 break;
422 case 6: /* Add to TX FIFO. */
423 smc91c111_queue_tx(s, s->packet_num);
424 break;
425 case 7: /* Reset TX FIFO. */
426 s->tx_fifo_len = 0;
427 s->tx_fifo_done_len = 0;
428 break;
429 }
430 return;
431 case 1:
432 /* Ignore. */
433 return;
434 case 2: /* Packet Number Register */
435 s->packet_num = value;
436 return;
437 case 3: case 4: case 5:
438 /* Should be readonly, but linux writes to them anyway. Ignore. */
439 return;
440 case 6: /* Pointer */
441 SET_LOW(ptr, value);
442 return;
443 case 7:
444 SET_HIGH(ptr, value);
445 return;
446 case 8: case 9: case 10: case 11: /* Data */
447 {
448 int p;
449 int n;
450
451 if (s->ptr & 0x8000)
452 n = s->rx_fifo[0];
453 else
454 n = s->packet_num;
455 p = s->ptr & 0x07ff;
456 if (s->ptr & 0x4000) {
457 s->ptr = (s->ptr & 0xf800) | ((s->ptr + 1) & 0x7ff);
458 } else {
459 p += (offset & 3);
460 }
461 s->data[n][p] = value;
462 }
463 return;
464 case 12: /* Interrupt ACK. */
465 s->int_level &= ~(value & 0xd6);
466 if (value & INT_TX)
467 smc91c111_pop_tx_fifo_done(s);
468 smc91c111_update(s);
469 return;
470 case 13: /* Interrupt mask. */
471 s->int_mask = value;
472 smc91c111_update(s);
473 return;
474 }
475 break;;
476
477 case 3:
478 switch (offset) {
479 case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
480 /* Multicast table. */
481 /* Not implemented. */
482 return;
483 case 8: case 9: /* Management Interface. */
484 /* Not implemented. */
485 return;
486 case 12: /* Early receive. */
487 s->ercv = value & 0x1f;
488 case 13:
489 /* Ignore. */
490 return;
491 }
492 break;
493 }
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700494 hw_error("smc91c111_write: Bad reg %d:%x\n", s->bank, (int)offset);
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800495}
496
497static uint32_t smc91c111_readb(void *opaque, target_phys_addr_t offset)
498{
499 smc91c111_state *s = (smc91c111_state *)opaque;
500
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800501 if (offset == 14) {
502 return s->bank;
503 }
504 if (offset == 15)
505 return 0x33;
506 switch (s->bank) {
507 case 0:
508 switch (offset) {
509 case 0: /* TCR */
510 return s->tcr & 0xff;
511 case 1:
512 return s->tcr >> 8;
513 case 2: /* EPH Status */
514 return 0;
515 case 3:
516 return 0x40;
517 case 4: /* RCR */
518 return s->rcr & 0xff;
519 case 5:
520 return s->rcr >> 8;
521 case 6: /* Counter */
522 case 7:
523 /* Not implemented. */
524 return 0;
525 case 8: /* Memory size. */
526 return NUM_PACKETS;
527 case 9: /* Free memory available. */
528 {
529 int i;
530 int n;
531 n = 0;
532 for (i = 0; i < NUM_PACKETS; i++) {
533 if (s->allocated & (1 << i))
534 n++;
535 }
536 return n;
537 }
538 case 10: case 11: /* RPCR */
539 /* Not implemented. */
540 return 0;
541 }
542 break;
543
544 case 1:
545 switch (offset) {
546 case 0: /* CONFIG */
547 return s->cr & 0xff;
548 case 1:
549 return s->cr >> 8;
550 case 2: case 3: /* BASE */
551 /* Not implemented. */
552 return 0;
553 case 4: case 5: case 6: case 7: case 8: case 9: /* IA */
554 return s->macaddr[offset - 4];
555 case 10: /* General Purpose */
556 return s->gpr & 0xff;
557 case 11:
558 return s->gpr >> 8;
559 case 12: /* Control */
560 return s->ctr & 0xff;
561 case 13:
562 return s->ctr >> 8;
563 }
564 break;
565
566 case 2:
567 switch (offset) {
568 case 0: case 1: /* MMUCR Busy bit. */
569 return 0;
570 case 2: /* Packet Number. */
571 return s->packet_num;
572 case 3: /* Allocation Result. */
573 return s->tx_alloc;
574 case 4: /* TX FIFO */
575 if (s->tx_fifo_done_len == 0)
576 return 0x80;
577 else
578 return s->tx_fifo_done[0];
579 case 5: /* RX FIFO */
580 if (s->rx_fifo_len == 0)
581 return 0x80;
582 else
583 return s->rx_fifo[0];
584 case 6: /* Pointer */
585 return s->ptr & 0xff;
586 case 7:
587 return (s->ptr >> 8) & 0xf7;
588 case 8: case 9: case 10: case 11: /* Data */
589 {
590 int p;
591 int n;
592
593 if (s->ptr & 0x8000)
594 n = s->rx_fifo[0];
595 else
596 n = s->packet_num;
597 p = s->ptr & 0x07ff;
598 if (s->ptr & 0x4000) {
599 s->ptr = (s->ptr & 0xf800) | ((s->ptr + 1) & 0x07ff);
600 } else {
601 p += (offset & 3);
602 }
603 return s->data[n][p];
604 }
605 case 12: /* Interrupt status. */
606 return s->int_level;
607 case 13: /* Interrupt mask. */
608 return s->int_mask;
609 }
610 break;
611
612 case 3:
613 switch (offset) {
614 case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
615 /* Multicast table. */
616 /* Not implemented. */
617 return 0;
618 case 8: /* Management Interface. */
619 /* Not implemented. */
620 return 0x30;
621 case 9:
622 return 0x33;
623 case 10: /* Revision. */
624 return 0x91;
625 case 11:
626 return 0x33;
627 case 12:
628 return s->ercv;
629 case 13:
630 return 0;
631 }
632 break;
633 }
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700634 hw_error("smc91c111_read: Bad reg %d:%x\n", s->bank, (int)offset);
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800635 return 0;
636}
637
638static void smc91c111_writew(void *opaque, target_phys_addr_t offset,
639 uint32_t value)
640{
641 smc91c111_writeb(opaque, offset, value & 0xff);
642 smc91c111_writeb(opaque, offset + 1, value >> 8);
643}
644
645static void smc91c111_writel(void *opaque, target_phys_addr_t offset,
646 uint32_t value)
647{
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800648 /* 32-bit writes to offset 0xc only actually write to the bank select
649 register (offset 0xe) */
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700650 if (offset != 0xc)
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800651 smc91c111_writew(opaque, offset, value & 0xffff);
652 smc91c111_writew(opaque, offset + 2, value >> 16);
653}
654
655static uint32_t smc91c111_readw(void *opaque, target_phys_addr_t offset)
656{
657 uint32_t val;
658 val = smc91c111_readb(opaque, offset);
659 val |= smc91c111_readb(opaque, offset + 1) << 8;
660 return val;
661}
662
663static uint32_t smc91c111_readl(void *opaque, target_phys_addr_t offset)
664{
665 uint32_t val;
666 val = smc91c111_readw(opaque, offset);
667 val |= smc91c111_readw(opaque, offset + 2) << 16;
668 return val;
669}
670
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700671static int smc91c111_can_receive(VLANClientState *vc)
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800672{
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700673 smc91c111_state *s = vc->opaque;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800674
675 if ((s->rcr & RCR_RXEN) == 0 || (s->rcr & RCR_SOFT_RST))
676 return 1;
677 if (s->allocated == (1 << NUM_PACKETS) - 1)
678 return 0;
679 return 1;
680}
681
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700682static ssize_t smc91c111_receive(VLANClientState *vc, const uint8_t *buf, size_t size)
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800683{
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700684 smc91c111_state *s = vc->opaque;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800685 int status;
686 int packetsize;
687 uint32_t crc;
688 int packetnum;
689 uint8_t *p;
690
691 if ((s->rcr & RCR_RXEN) == 0 || (s->rcr & RCR_SOFT_RST))
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700692 return -1;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800693 /* Short packets are padded with zeros. Receiving a packet
694 < 64 bytes long is considered an error condition. */
695 if (size < 64)
696 packetsize = 64;
697 else
698 packetsize = (size & ~1);
699 packetsize += 6;
700 crc = (s->rcr & RCR_STRIP_CRC) == 0;
701 if (crc)
702 packetsize += 4;
703 /* TODO: Flag overrun and receive errors. */
704 if (packetsize > 2048)
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700705 return -1;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800706 packetnum = smc91c111_allocate_packet(s);
707 if (packetnum == 0x80)
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700708 return -1;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800709 s->rx_fifo[s->rx_fifo_len++] = packetnum;
710
711 p = &s->data[packetnum][0];
712 /* ??? Multicast packets? */
713 status = 0;
714 if (size > 1518)
715 status |= RS_TOOLONG;
716 if (size & 1)
717 status |= RS_ODDFRAME;
718 *(p++) = status & 0xff;
719 *(p++) = status >> 8;
720 *(p++) = packetsize & 0xff;
721 *(p++) = packetsize >> 8;
722 memcpy(p, buf, size & ~1);
723 p += (size & ~1);
724 /* Pad short packets. */
725 if (size < 64) {
726 int pad;
727
728 if (size & 1)
729 *(p++) = buf[size - 1];
730 pad = 64 - size;
731 memset(p, 0, pad);
732 p += pad;
733 size = 64;
734 }
735 /* It's not clear if the CRC should go before or after the last byte in
736 odd sized packets. Linux disables the CRC, so that's no help.
737 The pictures in the documentation show the CRC aligned on a 16-bit
738 boundary before the last odd byte, so that's what we do. */
739 if (crc) {
740 crc = crc32(~0, buf, size);
741 *(p++) = crc & 0xff; crc >>= 8;
742 *(p++) = crc & 0xff; crc >>= 8;
743 *(p++) = crc & 0xff; crc >>= 8;
744 *(p++) = crc & 0xff; crc >>= 8;
745 }
746 if (size & 1) {
747 *(p++) = buf[size - 1];
748 *(p++) = 0x60;
749 } else {
750 *(p++) = 0;
751 *(p++) = 0x40;
752 }
753 /* TODO: Raise early RX interrupt? */
754 s->int_level |= INT_RCV;
755 smc91c111_update(s);
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700756
757 return size;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800758}
759
760static CPUReadMemoryFunc *smc91c111_readfn[] = {
761 smc91c111_readb,
762 smc91c111_readw,
763 smc91c111_readl
764};
765
766static CPUWriteMemoryFunc *smc91c111_writefn[] = {
767 smc91c111_writeb,
768 smc91c111_writew,
769 smc91c111_writel
770};
771
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700772static void smc91c111_cleanup(VLANClientState *vc)
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800773{
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700774 smc91c111_state *s = vc->opaque;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800775
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700776 cpu_unregister_io_memory(s->mmio_index);
777 qemu_free(s);
778}
779
780static void smc91c111_init1(SysBusDevice *dev)
781{
782 smc91c111_state *s = FROM_SYSBUS(smc91c111_state, dev);
783
784 s->mmio_index = cpu_register_io_memory(smc91c111_readfn,
785 smc91c111_writefn, s);
786 sysbus_init_mmio(dev, 16, s->mmio_index);
787 sysbus_init_irq(dev, &s->irq);
788 qdev_get_macaddr(&dev->qdev, s->macaddr);
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800789
790 smc91c111_reset(s);
791
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700792 s->vc = qdev_get_vlan_client(&dev->qdev,
793 smc91c111_can_receive, smc91c111_receive, NULL,
794 smc91c111_cleanup, s);
795 qemu_format_nic_info_str(s->vc, s->macaddr);
Ot ten Thije043b3932010-09-08 12:38:43 +0100796
797 register_savevm( "smc91c111", 0, SMC91C111_SAVE_VERSION,
798 smc91c111_save, smc91c111_load, s);
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800799}
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700800
801static void smc91c111_register_devices(void)
802{
803 sysbus_register_dev("smc91c111", sizeof(smc91c111_state), smc91c111_init1);
804}
805
806/* Legacy helper function. Should go away when machine config files are
807 implemented. */
808void smc91c111_init(NICInfo *nd, uint32_t base, qemu_irq irq)
809{
810 DeviceState *dev;
811 SysBusDevice *s;
812
813 qemu_check_nic_model(nd, "smc91c111");
814 dev = qdev_create(NULL, "smc91c111");
815 qdev_set_netdev(dev, nd);
816 qdev_init(dev);
817 s = sysbus_from_qdev(dev);
818 sysbus_mmio_map(s, 0, base);
819 sysbus_connect_irq(s, 0, irq);
820}
821
822device_init(smc91c111_register_devices)