Revision f3a52e50
b/hw/eepro100.c | ||
---|---|---|
212 | 212 |
uint32_t ru_offset; /* RU address offset */ |
213 | 213 |
uint32_t statsaddr; /* pointer to eepro100_stats_t */ |
214 | 214 |
|
215 |
/* Temporary status information (no need to save these values), |
|
216 |
* used while processing CU commands. */ |
|
217 |
eepro100_tx_t tx; /* transmit buffer descriptor */ |
|
218 |
uint32_t cb_address; /* = cu_base + cu_offset */ |
|
219 |
|
|
215 | 220 |
/* Statistical counters. Also used for wake-up packet (i82559). */ |
216 | 221 |
eepro100_stats_t statistics; |
217 | 222 |
|
... | ... | |
760 | 765 |
//~ missing("CU dump statistical counters"); |
761 | 766 |
} |
762 | 767 |
|
768 |
static void tx_command(EEPRO100State *s) |
|
769 |
{ |
|
770 |
uint32_t tbd_array = le32_to_cpu(s->tx.tx_desc_addr); |
|
771 |
uint16_t tcb_bytes = (le16_to_cpu(s->tx.tcb_bytes) & 0x3fff); |
|
772 |
/* Sends larger than MAX_ETH_FRAME_SIZE are allowed, up to 2600 bytes. */ |
|
773 |
uint8_t buf[2600]; |
|
774 |
uint16_t size = 0; |
|
775 |
uint32_t tbd_address = s->cb_address + 0x10; |
|
776 |
TRACE(RXTX, logout |
|
777 |
("transmit, TBD array address 0x%08x, TCB byte count 0x%04x, TBD count %u\n", |
|
778 |
tbd_array, tcb_bytes, s->tx.tbd_count)); |
|
779 |
|
|
780 |
if (tcb_bytes > 2600) { |
|
781 |
logout("TCB byte count too large, using 2600\n"); |
|
782 |
tcb_bytes = 2600; |
|
783 |
} |
|
784 |
if (!((tcb_bytes > 0) || (tbd_array != 0xffffffff))) { |
|
785 |
logout |
|
786 |
("illegal values of TBD array address and TCB byte count!\n"); |
|
787 |
} |
|
788 |
assert(tcb_bytes <= sizeof(buf)); |
|
789 |
while (size < tcb_bytes) { |
|
790 |
uint32_t tx_buffer_address = ldl_phys(tbd_address); |
|
791 |
uint16_t tx_buffer_size = lduw_phys(tbd_address + 4); |
|
792 |
//~ uint16_t tx_buffer_el = lduw_phys(tbd_address + 6); |
|
793 |
tbd_address += 8; |
|
794 |
TRACE(RXTX, logout |
|
795 |
("TBD (simplified mode): buffer address 0x%08x, size 0x%04x\n", |
|
796 |
tx_buffer_address, tx_buffer_size)); |
|
797 |
tx_buffer_size = MIN(tx_buffer_size, sizeof(buf) - size); |
|
798 |
cpu_physical_memory_read(tx_buffer_address, &buf[size], |
|
799 |
tx_buffer_size); |
|
800 |
size += tx_buffer_size; |
|
801 |
} |
|
802 |
if (tbd_array == 0xffffffff) { |
|
803 |
/* Simplified mode. Was already handled by code above. */ |
|
804 |
} else { |
|
805 |
/* Flexible mode. */ |
|
806 |
uint8_t tbd_count = 0; |
|
807 |
if (s->has_extended_tcb_support && !(s->configuration[6] & BIT(4))) { |
|
808 |
/* Extended Flexible TCB. */ |
|
809 |
for (; tbd_count < 2; tbd_count++) { |
|
810 |
uint32_t tx_buffer_address = ldl_phys(tbd_address); |
|
811 |
uint16_t tx_buffer_size = lduw_phys(tbd_address + 4); |
|
812 |
uint16_t tx_buffer_el = lduw_phys(tbd_address + 6); |
|
813 |
tbd_address += 8; |
|
814 |
TRACE(RXTX, logout |
|
815 |
("TBD (extended flexible mode): buffer address 0x%08x, size 0x%04x\n", |
|
816 |
tx_buffer_address, tx_buffer_size)); |
|
817 |
tx_buffer_size = MIN(tx_buffer_size, sizeof(buf) - size); |
|
818 |
cpu_physical_memory_read(tx_buffer_address, &buf[size], |
|
819 |
tx_buffer_size); |
|
820 |
size += tx_buffer_size; |
|
821 |
if (tx_buffer_el & 1) { |
|
822 |
break; |
|
823 |
} |
|
824 |
} |
|
825 |
} |
|
826 |
tbd_address = tbd_array; |
|
827 |
for (; tbd_count < s->tx.tbd_count; tbd_count++) { |
|
828 |
uint32_t tx_buffer_address = ldl_phys(tbd_address); |
|
829 |
uint16_t tx_buffer_size = lduw_phys(tbd_address + 4); |
|
830 |
uint16_t tx_buffer_el = lduw_phys(tbd_address + 6); |
|
831 |
tbd_address += 8; |
|
832 |
TRACE(RXTX, logout |
|
833 |
("TBD (flexible mode): buffer address 0x%08x, size 0x%04x\n", |
|
834 |
tx_buffer_address, tx_buffer_size)); |
|
835 |
tx_buffer_size = MIN(tx_buffer_size, sizeof(buf) - size); |
|
836 |
cpu_physical_memory_read(tx_buffer_address, &buf[size], |
|
837 |
tx_buffer_size); |
|
838 |
size += tx_buffer_size; |
|
839 |
if (tx_buffer_el & 1) { |
|
840 |
break; |
|
841 |
} |
|
842 |
} |
|
843 |
} |
|
844 |
TRACE(RXTX, logout("%p sending frame, len=%d,%s\n", s, size, nic_dump(buf, size))); |
|
845 |
qemu_send_packet(&s->nic->nc, buf, size); |
|
846 |
s->statistics.tx_good_frames++; |
|
847 |
/* Transmit with bad status would raise an CX/TNO interrupt. |
|
848 |
* (82557 only). Emulation never has bad status. */ |
|
849 |
//~ eepro100_cx_interrupt(s); |
|
850 |
} |
|
851 |
|
|
763 | 852 |
static void action_command(EEPRO100State *s) |
764 | 853 |
{ |
765 | 854 |
for (;;) { |
766 |
uint32_t cb_address = s->cu_base + s->cu_offset; |
|
767 |
eepro100_tx_t tx; |
|
768 |
cpu_physical_memory_read(cb_address, (uint8_t *) & tx, sizeof(tx)); |
|
769 |
uint16_t status = le16_to_cpu(tx.status); |
|
770 |
uint16_t command = le16_to_cpu(tx.command); |
|
855 |
s->cb_address = s->cu_base + s->cu_offset; |
|
856 |
cpu_physical_memory_read(s->cb_address, (uint8_t *)&s->tx, sizeof(s->tx)); |
|
857 |
uint16_t status = le16_to_cpu(s->tx.status); |
|
858 |
uint16_t command = le16_to_cpu(s->tx.command); |
|
771 | 859 |
logout |
772 | 860 |
("val=0x%02x (cu start), status=0x%04x, command=0x%04x, link=0x%08x\n", |
773 |
val, status, command, tx.link); |
|
861 |
val, status, command, s->tx.link);
|
|
774 | 862 |
bool bit_el = ((command & 0x8000) != 0); |
775 | 863 |
bool bit_s = ((command & 0x4000) != 0); |
776 | 864 |
bool bit_i = ((command & 0x2000) != 0); |
... | ... | |
778 | 866 |
bool success = true; |
779 | 867 |
//~ bool bit_sf = ((command & 0x0008) != 0); |
780 | 868 |
uint16_t cmd = command & 0x0007; |
781 |
s->cu_offset = le32_to_cpu(tx.link); |
|
869 |
s->cu_offset = le32_to_cpu(s->tx.link);
|
|
782 | 870 |
switch (cmd) { |
783 | 871 |
case CmdNOp: |
784 | 872 |
/* Do nothing. */ |
785 | 873 |
break; |
786 | 874 |
case CmdIASetup: |
787 |
cpu_physical_memory_read(cb_address + 8, &s->conf.macaddr.a[0], 6); |
|
875 |
cpu_physical_memory_read(s->cb_address + 8, &s->conf.macaddr.a[0], 6);
|
|
788 | 876 |
TRACE(OTHER, logout("macaddr: %s\n", nic_dump(&s->macaddr[0], 6))); |
789 | 877 |
break; |
790 | 878 |
case CmdConfigure: |
791 |
cpu_physical_memory_read(cb_address + 8, &s->configuration[0], |
|
879 |
cpu_physical_memory_read(s->cb_address + 8, &s->configuration[0],
|
|
792 | 880 |
sizeof(s->configuration)); |
793 | 881 |
TRACE(OTHER, logout("configuration: %s\n", nic_dump(&s->configuration[0], 16))); |
794 | 882 |
break; |
... | ... | |
796 | 884 |
//~ missing("multicast list"); |
797 | 885 |
break; |
798 | 886 |
case CmdTx: |
799 |
(void)0; |
|
800 |
uint32_t tbd_array = le32_to_cpu(tx.tx_desc_addr); |
|
801 |
uint16_t tcb_bytes = (le16_to_cpu(tx.tcb_bytes) & 0x3fff); |
|
802 |
TRACE(RXTX, logout |
|
803 |
("transmit, TBD array address 0x%08x, TCB byte count 0x%04x, TBD count %u\n", |
|
804 |
tbd_array, tcb_bytes, tx.tbd_count)); |
|
805 |
|
|
806 | 887 |
if (bit_nc) { |
807 | 888 |
missing("CmdTx: NC = 0"); |
808 | 889 |
success = false; |
809 | 890 |
break; |
810 | 891 |
} |
811 |
//~ assert(!bit_sf); |
|
812 |
if (tcb_bytes > 2600) { |
|
813 |
logout("TCB byte count too large, using 2600\n"); |
|
814 |
tcb_bytes = 2600; |
|
815 |
} |
|
816 |
/* Next assertion fails for local configuration. */ |
|
817 |
//~ assert((tcb_bytes > 0) || (tbd_array != 0xffffffff)); |
|
818 |
if (!((tcb_bytes > 0) || (tbd_array != 0xffffffff))) { |
|
819 |
logout |
|
820 |
("illegal values of TBD array address and TCB byte count!\n"); |
|
821 |
} |
|
822 |
// sends larger than MAX_ETH_FRAME_SIZE are allowed, up to 2600 bytes |
|
823 |
uint8_t buf[2600]; |
|
824 |
uint16_t size = 0; |
|
825 |
uint32_t tbd_address = cb_address + 0x10; |
|
826 |
assert(tcb_bytes <= sizeof(buf)); |
|
827 |
while (size < tcb_bytes) { |
|
828 |
uint32_t tx_buffer_address = ldl_phys(tbd_address); |
|
829 |
uint16_t tx_buffer_size = lduw_phys(tbd_address + 4); |
|
830 |
//~ uint16_t tx_buffer_el = lduw_phys(tbd_address + 6); |
|
831 |
tbd_address += 8; |
|
832 |
TRACE(RXTX, logout |
|
833 |
("TBD (simplified mode): buffer address 0x%08x, size 0x%04x\n", |
|
834 |
tx_buffer_address, tx_buffer_size)); |
|
835 |
tx_buffer_size = MIN(tx_buffer_size, sizeof(buf) - size); |
|
836 |
cpu_physical_memory_read(tx_buffer_address, &buf[size], |
|
837 |
tx_buffer_size); |
|
838 |
size += tx_buffer_size; |
|
839 |
} |
|
840 |
if (tbd_array == 0xffffffff) { |
|
841 |
/* Simplified mode. Was already handled by code above. */ |
|
842 |
} else { |
|
843 |
/* Flexible mode. */ |
|
844 |
uint8_t tbd_count = 0; |
|
845 |
if (s->has_extended_tcb_support && !(s->configuration[6] & BIT(4))) { |
|
846 |
/* Extended Flexible TCB. */ |
|
847 |
for (; tbd_count < 2; tbd_count++) { |
|
848 |
uint32_t tx_buffer_address = ldl_phys(tbd_address); |
|
849 |
uint16_t tx_buffer_size = lduw_phys(tbd_address + 4); |
|
850 |
uint16_t tx_buffer_el = lduw_phys(tbd_address + 6); |
|
851 |
tbd_address += 8; |
|
852 |
TRACE(RXTX, logout |
|
853 |
("TBD (extended flexible mode): buffer address 0x%08x, size 0x%04x\n", |
|
854 |
tx_buffer_address, tx_buffer_size)); |
|
855 |
tx_buffer_size = MIN(tx_buffer_size, sizeof(buf) - size); |
|
856 |
cpu_physical_memory_read(tx_buffer_address, &buf[size], |
|
857 |
tx_buffer_size); |
|
858 |
size += tx_buffer_size; |
|
859 |
if (tx_buffer_el & 1) { |
|
860 |
break; |
|
861 |
} |
|
862 |
} |
|
863 |
} |
|
864 |
tbd_address = tbd_array; |
|
865 |
for (; tbd_count < tx.tbd_count; tbd_count++) { |
|
866 |
uint32_t tx_buffer_address = ldl_phys(tbd_address); |
|
867 |
uint16_t tx_buffer_size = lduw_phys(tbd_address + 4); |
|
868 |
uint16_t tx_buffer_el = lduw_phys(tbd_address + 6); |
|
869 |
tbd_address += 8; |
|
870 |
TRACE(RXTX, logout |
|
871 |
("TBD (flexible mode): buffer address 0x%08x, size 0x%04x\n", |
|
872 |
tx_buffer_address, tx_buffer_size)); |
|
873 |
tx_buffer_size = MIN(tx_buffer_size, sizeof(buf) - size); |
|
874 |
cpu_physical_memory_read(tx_buffer_address, &buf[size], |
|
875 |
tx_buffer_size); |
|
876 |
size += tx_buffer_size; |
|
877 |
if (tx_buffer_el & 1) { |
|
878 |
break; |
|
879 |
} |
|
880 |
} |
|
881 |
} |
|
882 |
TRACE(RXTX, logout("%p sending frame, len=%d,%s\n", s, size, nic_dump(buf, size))); |
|
883 |
qemu_send_packet(&s->nic->nc, buf, size); |
|
884 |
s->statistics.tx_good_frames++; |
|
885 |
/* Transmit with bad status would raise an CX/TNO interrupt. |
|
886 |
* (82557 only). Emulation never has bad status. */ |
|
887 |
//~ eepro100_cx_interrupt(s); |
|
892 |
tx_command(s); |
|
888 | 893 |
break; |
889 | 894 |
case CmdTDR: |
890 | 895 |
TRACE(OTHER, logout("load microcode\n")); |
... | ... | |
897 | 902 |
break; |
898 | 903 |
} |
899 | 904 |
/* Write new status. */ |
900 |
stw_phys(cb_address, status | 0x8000 | (success ? 0x2000 : 0)); |
|
905 |
stw_phys(s->cb_address, status | 0x8000 | (success ? 0x2000 : 0));
|
|
901 | 906 |
if (bit_i) { |
902 | 907 |
/* CU completed action. */ |
903 | 908 |
eepro100_cx_interrupt(s); |
Also available in: Unified diff