Revision 89b190a2 hw/pcnet.c

b/hw/pcnet.c
35 35
 * http://www.ibiblio.org/pub/historic-linux/early-ports/Sparc/NCR/NCR92C990.txt
36 36
 */
37 37

  
38
#include <netinet/in.h>
39

  
38 40
#include "hw.h"
39 41
#include "pci.h"
40 42
#include "net.h"
......
52 54
#define PCNET_IOPORT_SIZE       0x20
53 55
#define PCNET_PNPMMIO_SIZE      0x20
54 56

  
57
#define PCNET_LOOPTEST_CRC	1
58
#define PCNET_LOOPTEST_NOCRC	2
59

  
55 60

  
56 61
typedef struct PCNetState_st PCNetState;
57 62

  
......
76 81
    void (*phys_mem_write)(void *dma_opaque, target_phys_addr_t addr,
77 82
                          uint8_t *buf, int len, int do_bswap);
78 83
    void *dma_opaque;
84
    int looptest;
79 85
};
80 86

  
81 87
struct qemu_ether_header {
......
120 126
#define CSR_DRX(S)       !!(((S)->csr[15])&0x0001)
121 127
#define CSR_DTX(S)       !!(((S)->csr[15])&0x0002)
122 128
#define CSR_LOOP(S)      !!(((S)->csr[15])&0x0004)
129
#define CSR_DXMTFCS(S)   !!(((S)->csr[15])&0x0008)
123 130
#define CSR_DRCVPA(S)    !!(((S)->csr[15])&0x2000)
124 131
#define CSR_DRCVBC(S)    !!(((S)->csr[15])&0x4000)
125 132
#define CSR_PROM(S)      !!(((S)->csr[15])&0x8000)
......
202 209
#define TMDS_LTINT_SH   12
203 210
#define TMDS_NOFCS_MASK 0x2000
204 211
#define TMDS_NOFCS_SH   13
212
#define TMDS_ADDFCS_MASK TMDS_NOFCS_MASK
213
#define TMDS_ADDFCS_SH  TMDS_NOFCS_SH
205 214
#define TMDS_ERR_MASK   0x4000
206 215
#define TMDS_ERR_SH     14
207 216
#define TMDS_OWN_MASK   0x8000
......
1064 1073
    PCNetState *s = opaque;
1065 1074
    int is_padr = 0, is_bcast = 0, is_ladr = 0;
1066 1075
    uint8_t buf1[60];
1076
    int remaining;
1077
    int crc_err = 0;
1067 1078

  
1068 1079
    if (CSR_DRX(s) || CSR_STOP(s) || CSR_SPND(s) || !size)
1069 1080
        return;
......
1117 1128
            s->csr[0] |= 0x1000; /* Set MISS flag */
1118 1129
            CSR_MISSC(s)++;
1119 1130
        } else {
1120
            uint8_t *src = &s->buffer[8];
1131
            uint8_t *src = s->buffer;
1121 1132
            target_phys_addr_t crda = CSR_CRDA(s);
1122 1133
            struct pcnet_RMD rmd;
1123 1134
            int pktcount = 0;
1124 1135

  
1125
            memcpy(src, buf, size);
1126

  
1127
#if 1
1128
            /* no need to compute the CRC */
1129
            src[size] = 0;
1130
            src[size + 1] = 0;
1131
            src[size + 2] = 0;
1132
            src[size + 3] = 0;
1133
            size += 4;
1134
#else
1135
            /* XXX: avoid CRC generation */
1136
            if (!CSR_ASTRP_RCV(s)) {
1136
            if (!s->looptest) {
1137
                memcpy(src, buf, size);
1138
                /* no need to compute the CRC */
1139
                src[size] = 0;
1140
                src[size + 1] = 0;
1141
                src[size + 2] = 0;
1142
                src[size + 3] = 0;
1143
                size += 4;
1144
            } else if (s->looptest == PCNET_LOOPTEST_CRC ||
1145
                       !CSR_DXMTFCS(s) || size < MIN_BUF_SIZE+4) {
1137 1146
                uint32_t fcs = ~0;
1138 1147
                uint8_t *p = src;
1139 1148

  
1140
                while (size < 46) {
1141
                    src[size++] = 0;
1142
                }
1149
                while (p != &src[size])
1150
                    CRC(fcs, *p++);
1151
                *(uint32_t *)p = htonl(fcs);
1152
                size += 4;
1153
            } else {
1154
                uint32_t fcs = ~0;
1155
                uint8_t *p = src;
1143 1156

  
1144
                while (p != &src[size]) {
1157
                while (p != &src[size-4])
1145 1158
                    CRC(fcs, *p++);
1146
                }
1147
                ((uint32_t *)&src[size])[0] = htonl(fcs);
1148
                size += 4; /* FCS at end of packet */
1149
            } else size += 4;
1150
#endif
1159
                crc_err = (*(uint32_t *)p != htonl(fcs));
1160
            }
1151 1161

  
1152 1162
#ifdef PCNET_DEBUG_MATCH
1153 1163
            PRINT_PKTHDR(buf);
......
1158 1168
                SET_FIELD(&rmd.status, RMDS, STP, 1);
1159 1169

  
1160 1170
#define PCNET_RECV_STORE() do {                                 \
1161
    int count = MIN(4096 - GET_FIELD(rmd.buf_length, RMDL, BCNT),size); \
1171
    int count = MIN(4096 - GET_FIELD(rmd.buf_length, RMDL, BCNT),remaining); \
1162 1172
    target_phys_addr_t rbadr = PHYSADDR(s, rmd.rbadr);          \
1163 1173
    s->phys_mem_write(s->dma_opaque, rbadr, src, count, CSR_BSWP(s)); \
1164
    src += count; size -= count;                                \
1165
    SET_FIELD(&rmd.msg_length, RMDM, MCNT, count);              \
1174
    src += count; remaining -= count;                           \
1166 1175
    SET_FIELD(&rmd.status, RMDS, OWN, 0);                       \
1167 1176
    RMDSTORE(&rmd, PHYSADDR(s,crda));                           \
1168 1177
    pktcount++;                                                 \
1169 1178
} while (0)
1170 1179

  
1180
            remaining = size;
1171 1181
            PCNET_RECV_STORE();
1172
            if ((size > 0) && CSR_NRDA(s)) {
1182
            if ((remaining > 0) && CSR_NRDA(s)) {
1173 1183
                target_phys_addr_t nrda = CSR_NRDA(s);
1184
#ifdef PCNET_DEBUG_RMD
1185
                PRINT_RMD(&rmd);
1186
#endif
1174 1187
                RMDLOAD(&rmd, PHYSADDR(s,nrda));
1175 1188
                if (GET_FIELD(rmd.status, RMDS, OWN)) {
1176 1189
                    crda = nrda;
1177 1190
                    PCNET_RECV_STORE();
1178
                    if ((size > 0) && (nrda=CSR_NNRD(s))) {
1191
#ifdef PCNET_DEBUG_RMD
1192
                    PRINT_RMD(&rmd);
1193
#endif
1194
                    if ((remaining > 0) && (nrda=CSR_NNRD(s))) {
1179 1195
                        RMDLOAD(&rmd, PHYSADDR(s,nrda));
1180 1196
                        if (GET_FIELD(rmd.status, RMDS, OWN)) {
1181 1197
                            crda = nrda;
......
1188 1204
#undef PCNET_RECV_STORE
1189 1205

  
1190 1206
            RMDLOAD(&rmd, PHYSADDR(s,crda));
1191
            if (size == 0) {
1207
            if (remaining == 0) {
1208
                SET_FIELD(&rmd.msg_length, RMDM, MCNT, size);
1192 1209
                SET_FIELD(&rmd.status, RMDS, ENP, 1);
1193 1210
                SET_FIELD(&rmd.status, RMDS, PAM, !CSR_PROM(s) && is_padr);
1194 1211
                SET_FIELD(&rmd.status, RMDS, LFAM, !CSR_PROM(s) && is_ladr);
1195 1212
                SET_FIELD(&rmd.status, RMDS, BAM, !CSR_PROM(s) && is_bcast);
1213
                if (crc_err) {
1214
                    SET_FIELD(&rmd.status, RMDS, CRC, 1);
1215
                    SET_FIELD(&rmd.status, RMDS, ERR, 1);
1216
                }
1196 1217
            } else {
1197 1218
                SET_FIELD(&rmd.status, RMDS, OFLO, 1);
1198 1219
                SET_FIELD(&rmd.status, RMDS, BUFF, 1);
......
1229 1250
{
1230 1251
    target_phys_addr_t xmit_cxda = 0;
1231 1252
    int count = CSR_XMTRL(s)-1;
1253
    int add_crc = 0;
1254

  
1232 1255
    s->xmit_pos = -1;
1233 1256

  
1234 1257
    if (!CSR_TXON(s)) {
......
1251 1274
        if (GET_FIELD(tmd.status, TMDS, STP)) {
1252 1275
            s->xmit_pos = 0;
1253 1276
            xmit_cxda = PHYSADDR(s,CSR_CXDA(s));
1277
            if (BCR_SWSTYLE(s) != 1)
1278
                add_crc = GET_FIELD(tmd.status, TMDS, ADDFCS);
1254 1279
        }
1255 1280
        if (!GET_FIELD(tmd.status, TMDS, ENP)) {
1256 1281
            int bcnt = 4096 - GET_FIELD(tmd.length, TMDL, BCNT);
......
1265 1290
#ifdef PCNET_DEBUG
1266 1291
            printf("pcnet_transmit size=%d\n", s->xmit_pos);
1267 1292
#endif
1268
            if (CSR_LOOP(s))
1293
            if (CSR_LOOP(s)) {
1294
                if (BCR_SWSTYLE(s) == 1)
1295
                    add_crc = !GET_FIELD(tmd.status, TMDS, NOFCS);
1296
                s->looptest = add_crc ? PCNET_LOOPTEST_CRC : PCNET_LOOPTEST_NOCRC;
1269 1297
                pcnet_receive(s, s->buffer, s->xmit_pos);
1270
            else
1298
                s->looptest = 0;
1299
            } else
1271 1300
                if (s->vc)
1272 1301
                    qemu_send_packet(s->vc, s->buffer, s->xmit_pos);
1273 1302

  

Also available in: Unified diff