1 /* 2 ** File: 3c501.c Jan. 14, 1997 3 ** 4 ** Author: Giovanni Falzoni <gfalzoni@inwind.it> 5 ** 6 ** This file contains specific implementation of the ethernet 7 ** device driver for 3Com Etherlink (3c501) boards. This is a 8 ** very old board and its performances are very poor for today 9 ** network environments. 10 */ 11 12 #include <minix/drivers.h> 13 #include <minix/com.h> 14 #include <net/gen/ether.h> 15 #include <net/gen/eth_io.h> 16 #include "dp.h" 17 18 #if (ENABLE_3C501 == 1) 19 20 #include "3c501.h" 21 22 static unsigned char StationAddress[SA_ADDR_LEN] = {0, 0, 0, 0, 0, 0,}; 23 static buff_t *TxBuff = NULL; 24 25 /* 26 ** Name: void el1_getstats(dpeth_t *dep) 27 ** Function: Reads statistics counters from board. 28 **/ 29 static void el1_getstats(dpeth_t * dep) 30 { 31 32 return; /* Nothing to do */ 33 } 34 35 /* 36 ** Name: void el1_reset(dpeth_t *dep) 37 ** Function: Reset function specific for Etherlink hardware. 38 */ 39 static void el1_reset(dpeth_t * dep) 40 { 41 int ix; 42 43 for (ix = 0; ix < 8; ix += 1) /* Resets the board */ 44 outb_el1(dep, EL1_CSR, ECSR_RESET); 45 outb_el1(dep, EL1_CSR, ECSR_RIDE | ECSR_SYS); 46 47 /* Set Ethernet Address on controller */ 48 outb_el1(dep, EL1_CSR, ECSR_LOOP); /* Loopback mode */ 49 for (ix = EL1_ADDRESS; ix < SA_ADDR_LEN; ix += 1) 50 outb_el1(dep, ix, StationAddress[ix]); 51 52 lock(); 53 /* Enable DMA/Interrupt, gain control of Buffer */ 54 outb_el1(dep, EL1_CSR, ECSR_RIDE | ECSR_SYS); 55 /* Clear RX packet area */ 56 outw_el1(dep, EL1_RECVPTR, 0); 57 /* Enable transmit/receive configuration and flush pending interrupts */ 58 outb_el1(dep, EL1_XMIT, EXSR_IDLE | EXSR_16JAM | EXSR_JAM | EXSR_UNDER); 59 outb_el1(dep, EL1_RECV, dep->de_recv_mode); 60 inb_el1(dep, EL1_RECV); 61 inb_el1(dep, EL1_XMIT); 62 dep->de_flags &= NOT(DEF_XMIT_BUSY); 63 unlock(); 64 return; /* Done */ 65 } 66 67 /* 68 ** Name: void el1_dumpstats(dpeth_t *dep, int port, vir_bytes size) 69 ** Function: Dumps counter on screen (support for console display). 70 */ 71 static void el1_dumpstats(dpeth_t * UNUSED(dep)) 72 { 73 74 return; 75 } 76 77 /* 78 ** Name: void el1_mode_init(dpeth_t *dep) 79 ** Function: Initializes receicer mode 80 */ 81 static void el1_mode_init(dpeth_t * dep) 82 { 83 84 if (dep->de_flags & DEF_BROAD) { 85 dep->de_recv_mode = ERSR_BROAD | ERSR_RMASK; 86 87 } else if (dep->de_flags & DEF_PROMISC) { 88 dep->de_recv_mode = ERSR_ALL | ERSR_RMASK; 89 90 } else if (dep->de_flags & DEF_MULTI) { 91 dep->de_recv_mode = ERSR_MULTI | ERSR_RMASK; 92 93 } else { 94 dep->de_recv_mode = ERSR_NONE | ERSR_RMASK; 95 } 96 outb_el1(dep, EL1_RECV, dep->de_recv_mode); 97 inb_el1(dep, EL1_RECV); 98 return; 99 } 100 101 /* 102 ** Name: void el1_recv(dpeth_t *dep, int from, int size) 103 ** Function: Receive function. Called from interrupt handler to 104 ** unload recv. buffer or from main (packet to client) 105 */ 106 static void el1_recv(dpeth_t * dep, int from, int size) 107 { 108 buff_t *rxptr; 109 110 while ((dep->de_flags & DEF_READING) && (rxptr = dep->de_recvq_head)) { 111 112 /* Remove buffer from queue and free buffer */ 113 lock(); 114 if (dep->de_recvq_tail == dep->de_recvq_head) 115 dep->de_recvq_head = dep->de_recvq_tail = NULL; 116 else 117 dep->de_recvq_head = rxptr->next; 118 unlock(); 119 120 /* Copy buffer to user area */ 121 mem2user(dep, rxptr); 122 123 /* Reply information */ 124 dep->de_read_s = rxptr->size; 125 dep->de_flags |= DEF_ACK_RECV; 126 dep->de_flags &= NOT(DEF_READING); 127 128 /* Return buffer to the idle pool */ 129 free_buff(dep, rxptr); 130 } 131 return; 132 } 133 134 /* 135 ** Name: void el1_send(dpeth_t *dep, int from_int, int pktsize) 136 ** Function: Send function. Called from main to transit a packet or 137 ** from interrupt handler when a new packet was queued. 138 */ 139 static void el1_send(dpeth_t * dep, int from_int, int pktsize) 140 { 141 buff_t *txbuff; 142 clock_t now; 143 144 if (from_int == FALSE) { 145 146 if ((txbuff = alloc_buff(dep, pktsize + sizeof(buff_t))) != NULL) { 147 148 /* Fill transmit buffer from user area */ 149 txbuff->next = NULL; 150 txbuff->size = pktsize; 151 txbuff->client = dep->de_client; 152 user2mem(dep, txbuff); 153 } else 154 panic("out of memory for Tx"); 155 156 } else if ((txbuff = dep->de_xmitq_head) != NULL) { 157 158 /* Get first packet in queue */ 159 lock(); 160 if (dep->de_xmitq_tail == dep->de_xmitq_head) 161 dep->de_xmitq_head = dep->de_xmitq_tail = NULL; 162 else 163 dep->de_xmitq_head = txbuff->next; 164 unlock(); 165 pktsize = txbuff->size; 166 167 } else 168 panic("should not be sending "); 169 170 if ((dep->de_flags & DEF_XMIT_BUSY)) { 171 if (from_int) panic("should not be sending "); 172 getticks(&now); 173 if ((now - dep->de_xmit_start) > 4) { 174 /* Transmitter timed out */ 175 DEBUG(printf("3c501: transmitter timed out ... \n")); 176 dep->de_stat.ets_sendErr += 1; 177 dep->de_flags &= NOT(DEF_XMIT_BUSY); 178 el1_reset(dep); 179 } 180 181 /* Queue packet */ 182 lock(); /* Queue packet to receive queue */ 183 if (dep->de_xmitq_head == NULL) 184 dep->de_xmitq_head = txbuff; 185 else 186 dep->de_xmitq_tail->next = txbuff; 187 dep->de_xmitq_tail = txbuff; 188 unlock(); 189 } else { 190 /* Save for retransmission */ 191 TxBuff = txbuff; 192 dep->de_flags |= (DEF_XMIT_BUSY | DEF_ACK_SEND); 193 194 /* Setup board for packet loading */ 195 lock(); /* Buffer to processor */ 196 outb_el1(dep, EL1_CSR, ECSR_RIDE | ECSR_SYS); 197 inb_el1(dep, EL1_RECV); /* Clears any spurious interrupt */ 198 inb_el1(dep, EL1_XMIT); 199 outw_el1(dep, EL1_RECVPTR, 0); /* Clears RX packet area */ 200 201 /* Loads packet */ 202 outw_el1(dep, EL1_XMITPTR, (EL1_BFRSIZ - pktsize)); 203 outsb(dep->de_data_port, SELF, txbuff->buffer, pktsize); 204 /* Starts transmitter */ 205 outw_el1(dep, EL1_XMITPTR, (EL1_BFRSIZ - pktsize)); 206 outb_el1(dep, EL1_CSR, ECSR_RIDE | ECSR_XMIT); /* There it goes... */ 207 unlock(); 208 209 getticks(&dep->de_xmit_start); 210 dep->de_flags &= NOT(DEF_SENDING); 211 } 212 return; 213 } 214 215 /* 216 ** Name: void el1_stop(dpeth_t *dep) 217 ** Function: Stops board and disable interrupts. 218 */ 219 static void el1_stop(dpeth_t * dep) 220 { 221 int ix; 222 223 DEBUG(printf("%s: stopping Etherlink ....\n", dep->de_name)); 224 for (ix = 0; ix < 8; ix += 1) /* Reset board */ 225 outb_el1(dep, EL1_CSR, ECSR_RESET); 226 outb_el1(dep, EL1_CSR, ECSR_SYS); 227 sys_irqdisable(&dep->de_hook); /* Disable interrupt */ 228 return; 229 } 230 231 /* 232 ** Name: void el1_interrupt(dpeth_t *dep) 233 ** Function: Interrupt handler. Acknwledges transmit interrupts 234 ** or unloads receive buffer to memory queue. 235 */ 236 static void el1_interrupt(dpeth_t * dep) 237 { 238 u16_t csr, isr; 239 int pktsize; 240 buff_t *rxptr; 241 242 csr = inb_el1(dep, EL1_CSR); 243 if ((csr & ECSR_XMIT) && (dep->de_flags & DEF_XMIT_BUSY)) { 244 245 /* Got a transmit interrupt */ 246 isr = inb_el1(dep, EL1_XMIT); 247 if ((isr & (EXSR_16JAM | EXSR_UNDER | EXSR_JAM)) || !(isr & EXSR_IDLE)) { 248 DEBUG(printf("3c501: got xmit interrupt (ASR=0x%02X XSR=0x%02X)\n", csr, isr)); 249 if (isr & EXSR_JAM) { 250 /* Sending, packet got a collision */ 251 dep->de_stat.ets_collision += 1; 252 /* Put pointer back to beginning of packet */ 253 outb_el1(dep, EL1_CSR, ECSR_RIDE | ECSR_SYS); 254 outw_el1(dep, EL1_XMITPTR, (EL1_BFRSIZ - TxBuff->size)); 255 /* And retrigger transmission */ 256 outb_el1(dep, EL1_CSR, ECSR_RIDE | ECSR_XMIT); 257 return; 258 259 } else if ((isr & EXSR_16JAM) || !(isr & EXSR_IDLE)) { 260 dep->de_stat.ets_sendErr += 1; 261 262 } else if (isr & EXSR_UNDER) { 263 dep->de_stat.ets_fifoUnder += 1; 264 } 265 DEBUG(printf("3c501: got xmit interrupt (0x%02X)\n", isr)); 266 el1_reset(dep); 267 268 } else { 269 /** if (inw_el1(dep, EL1_XMITPTR) == EL1_BFRSIZ) **/ 270 /* Packet transmitted successfully */ 271 dep->de_stat.ets_packetT += 1; 272 dep->bytes_Tx += (long) (TxBuff->size); 273 free_buff(dep, TxBuff); 274 dep->de_flags &= NOT(DEF_XMIT_BUSY); 275 if ((dep->de_flags & DEF_SENDING) && dep->de_xmitq_head) { 276 /* Pending transmit request available in queue */ 277 el1_send(dep, TRUE, 0); 278 if (dep->de_flags & (DEF_XMIT_BUSY | DEF_ACK_SEND)) 279 return; 280 } 281 } 282 283 } else if ((csr & (ECSR_RECV | ECSR_XMTBSY)) == (ECSR_RECV | ECSR_XMTBSY)) { 284 285 /* Got a receive interrupt */ 286 isr = inb_el1(dep, EL1_RECV); 287 pktsize = inw_el1(dep, EL1_RECVPTR); 288 if ((isr & ERSR_RERROR) || (isr & ERSR_STALE)) { 289 DEBUG(printf("Rx0 (ASR=0x%02X RSR=0x%02X size=%d)\n", csr, isr, pktsize)); 290 dep->de_stat.ets_recvErr += 1; 291 292 } else if (pktsize < ETH_MIN_PACK_SIZE || pktsize > ETH_MAX_PACK_SIZE) { 293 DEBUG(printf("Rx1 (ASR=0x%02X RSR=0x%02X size=%d)\n", csr, isr, pktsize)); 294 dep->de_stat.ets_recvErr += 1; 295 296 } else if ((rxptr = alloc_buff(dep, pktsize + sizeof(buff_t))) == NULL) { 297 /* Memory not available. Drop packet */ 298 dep->de_stat.ets_fifoOver += 1; 299 300 } else if (isr & (ERSR_GOOD | ERSR_ANY)) { 301 /* Got a good packet. Read it from buffer */ 302 outb_el1(dep, EL1_CSR, ECSR_RIDE | ECSR_SYS); 303 outw_el1(dep, EL1_XMITPTR, 0); 304 insb(dep->de_data_port, SELF, rxptr->buffer, pktsize); 305 rxptr->next = NULL; 306 rxptr->size = pktsize; 307 dep->de_stat.ets_packetR += 1; 308 dep->bytes_Rx += (long) pktsize; 309 lock(); /* Queue packet to receive queue */ 310 if (dep->de_recvq_head == NULL) 311 dep->de_recvq_head = rxptr; 312 else 313 dep->de_recvq_tail->next = rxptr; 314 dep->de_recvq_tail = rxptr; 315 unlock(); 316 317 /* Reply to pending Receive requests, if any */ 318 el1_recv(dep, TRUE, 0); 319 } 320 } else { /* Nasty condition, should never happen */ 321 DEBUG( 322 printf("3c501: got interrupt with status 0x%02X\n" 323 " de_flags=0x%04X XSR=0x%02X RSR=0x%02X \n" 324 " xmit buffer = 0x%4X recv buffer = 0x%4X\n", 325 csr, dep->de_flags, 326 inb_el1(dep, EL1_RECV), 327 inb_el1(dep, EL1_XMIT), 328 inw_el1(dep, EL1_XMITPTR), 329 inw_el1(dep, EL1_RECVPTR)) 330 ); 331 el1_reset(dep); 332 } 333 334 /* Move into receive mode */ 335 outb_el1(dep, EL1_CSR, ECSR_RIDE | ECSR_RECV); 336 outw_el1(dep, EL1_RECVPTR, 0); 337 /* Be sure that interrupts are cleared */ 338 inb_el1(dep, EL1_RECV); 339 inb_el1(dep, EL1_XMIT); 340 return; 341 } 342 343 /* 344 ** Name: void el1_init(dpeth_t *dep) 345 ** Function: Initalizes board hardware and driver data structures. 346 */ 347 static void el1_init(dpeth_t * dep) 348 { 349 int ix; 350 351 dep->de_irq &= NOT(DEI_DEFAULT); /* Strip the default flag. */ 352 dep->de_offset_page = 0; 353 dep->de_data_port = dep->de_base_port + EL1_DATAPORT; 354 355 el1_reset(dep); /* Reset and initialize board */ 356 357 /* Start receiver (default mode) */ 358 outw_el1(dep, EL1_RECVPTR, 0); 359 outb_el1(dep, EL1_CSR, ECSR_RIDE | ECSR_RECV); 360 361 /* Initializes buffer pool */ 362 init_buff(dep, NULL); 363 el1_mode_init(dep); 364 365 printf("%s: Etherlink (%s) at %X:%d - ", 366 dep->de_name, "3c501", dep->de_base_port, dep->de_irq); 367 for (ix = 0; ix < SA_ADDR_LEN; ix += 1) 368 printf("%02X%c", (dep->de_address.ea_addr[ix] = StationAddress[ix]), 369 ix < SA_ADDR_LEN - 1 ? ':' : '\n'); 370 371 /* Device specific functions */ 372 dep->de_recvf = el1_recv; 373 dep->de_sendf = el1_send; 374 dep->de_flagsf = el1_mode_init; 375 dep->de_resetf = el1_reset; 376 dep->de_getstatsf = el1_getstats; 377 dep->de_dumpstatsf = el1_dumpstats; 378 dep->de_interruptf = el1_interrupt; 379 380 return; /* Done */ 381 } 382 383 /* 384 ** Name: int el1_probe(dpeth_t *dep) 385 ** Function: Checks for presence of the board. 386 */ 387 int el1_probe(dpeth_t * dep) 388 { 389 int ix; 390 391 for (ix = 0; ix < 8; ix += 1) /* Reset the board */ 392 outb_el1(dep, EL1_CSR, ECSR_RESET); 393 outb_el1(dep, EL1_CSR, ECSR_SYS); /* Leaves buffer to system */ 394 395 /* Check station address */ 396 for (ix = 0; ix < SA_ADDR_LEN; ix += 1) { 397 outw_el1(dep, EL1_XMITPTR, ix); 398 StationAddress[ix] = inb_el1(dep, EL1_SAPROM); 399 } 400 if (StationAddress[0] != 0x02 || /* Etherlink Station address */ 401 StationAddress[1] != 0x60 || /* MUST be 02:60:8c:xx:xx:xx */ 402 StationAddress[2] != 0x8C) 403 return FALSE; /* No Etherlink board at this address */ 404 405 dep->de_ramsize = 0; /* RAM size is meaningless */ 406 dep->de_linmem = 0L; /* Access is via I/O port */ 407 408 /* Device specific functions */ 409 dep->de_initf = el1_init; 410 dep->de_stopf = el1_stop; 411 412 return TRUE; /* Etherlink board found */ 413 } 414 415 #endif /* ENABLE_3C501 */ 416 417 /** 3c501.c **/ 418