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/netdriver.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: el1_getstats 27 ** Function: Reads statistics counters from board. 28 **/ 29 static void el1_getstats(dpeth_t * dep) 30 { 31 32 /* Nothing to do */ 33 } 34 35 /* 36 ** Name: el1_reset 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 /* Enable DMA/Interrupt, gain control of Buffer */ 53 outb_el1(dep, EL1_CSR, ECSR_RIDE | ECSR_SYS); 54 /* Clear RX packet area */ 55 outw_el1(dep, EL1_RECVPTR, 0); 56 /* Enable transmit/receive configuration and flush pending interrupts */ 57 outb_el1(dep, EL1_XMIT, EXSR_IDLE | EXSR_16JAM | EXSR_JAM | EXSR_UNDER); 58 outb_el1(dep, EL1_RECV, dep->de_recv_mode); 59 inb_el1(dep, EL1_RECV); 60 inb_el1(dep, EL1_XMIT); 61 dep->de_flags &= NOT(DEF_XMIT_BUSY); 62 } 63 64 /* 65 ** Name: el1_dumpstats 66 ** Function: Dumps counter on screen (support for console display). 67 */ 68 static void el1_dumpstats(dpeth_t * UNUSED(dep)) 69 { 70 71 } 72 73 /* 74 ** Name: el1_mode_init 75 ** Function: Initializes receicer mode 76 */ 77 static void el1_mode_init(dpeth_t * dep) 78 { 79 80 if (dep->de_flags & DEF_BROAD) { 81 dep->de_recv_mode = ERSR_BROAD | ERSR_RMASK; 82 83 } else if (dep->de_flags & DEF_PROMISC) { 84 dep->de_recv_mode = ERSR_ALL | ERSR_RMASK; 85 86 } else if (dep->de_flags & DEF_MULTI) { 87 dep->de_recv_mode = ERSR_MULTI | ERSR_RMASK; 88 89 } else { 90 dep->de_recv_mode = ERSR_NONE | ERSR_RMASK; 91 } 92 outb_el1(dep, EL1_RECV, dep->de_recv_mode); 93 inb_el1(dep, EL1_RECV); 94 } 95 96 /* 97 ** Name: el1_recv 98 ** Function: Receive function. Called from interrupt handler to 99 ** unload recv. buffer or from main (packet to client) 100 */ 101 static ssize_t el1_recv(dpeth_t *dep, struct netdriver_data *data, size_t max) 102 { 103 buff_t *rxptr; 104 size_t size; 105 106 if ((rxptr = dep->de_recvq_head) == NULL) 107 return SUSPEND; 108 109 /* Remove buffer from queue and free buffer */ 110 if (dep->de_recvq_tail == dep->de_recvq_head) 111 dep->de_recvq_head = dep->de_recvq_tail = NULL; 112 else 113 dep->de_recvq_head = rxptr->next; 114 115 /* Copy buffer to user area */ 116 size = MIN(rxptr->size, max); 117 118 netdriver_copyout(data, 0, rxptr->buffer, size); 119 120 /* Return buffer to the idle pool */ 121 free_buff(dep, rxptr); 122 123 return size; 124 } 125 126 /* 127 ** Name: el1_send 128 ** Function: Send function. 129 */ 130 static int el1_send(dpeth_t *dep, struct netdriver_data *data, size_t size) 131 { 132 buff_t *txbuff; 133 clock_t now; 134 135 if (dep->de_flags & DEF_XMIT_BUSY) { 136 now = getticks(); 137 if ((now - dep->de_xmit_start) > 4) { 138 /* Transmitter timed out */ 139 DEBUG(printf("3c501: transmitter timed out ... \n")); 140 dep->de_stat.ets_sendErr += 1; 141 dep->de_flags &= NOT(DEF_XMIT_BUSY); 142 /* Try sending anyway. */ 143 } else 144 return SUSPEND; 145 } 146 147 /* Since we may have to retransmit, we need a local copy. */ 148 if ((txbuff = alloc_buff(dep, size + sizeof(buff_t))) == NULL) 149 panic("out of memory"); 150 151 /* Fill transmit buffer from user area */ 152 txbuff->next = NULL; 153 txbuff->size = size; 154 155 netdriver_copyin(data, 0, txbuff->buffer, size); 156 157 /* Save for retransmission */ 158 TxBuff = txbuff; 159 dep->de_flags |= DEF_XMIT_BUSY; 160 161 /* Setup board for packet loading */ 162 outb_el1(dep, EL1_CSR, ECSR_RIDE | ECSR_SYS); 163 inb_el1(dep, EL1_RECV); /* Clears any spurious interrupt */ 164 inb_el1(dep, EL1_XMIT); 165 outw_el1(dep, EL1_RECVPTR, 0); /* Clears RX packet area */ 166 167 /* Loads packet */ 168 outw_el1(dep, EL1_XMITPTR, (EL1_BFRSIZ - size)); 169 outsb(dep->de_data_port, txbuff->buffer, size); 170 /* Starts transmitter */ 171 outw_el1(dep, EL1_XMITPTR, (EL1_BFRSIZ - size)); 172 outb_el1(dep, EL1_CSR, ECSR_RIDE | ECSR_XMIT); /* There it goes... */ 173 174 dep->de_xmit_start = getticks(); 175 176 return OK; 177 } 178 179 /* 180 ** Name: el1_stop 181 ** Function: Stops board and disable interrupts. 182 */ 183 static void el1_stop(dpeth_t * dep) 184 { 185 int ix; 186 187 DEBUG(printf("%s: stopping Etherlink ....\n", dep->de_name)); 188 for (ix = 0; ix < 8; ix += 1) /* Reset board */ 189 outb_el1(dep, EL1_CSR, ECSR_RESET); 190 outb_el1(dep, EL1_CSR, ECSR_SYS); 191 sys_irqdisable(&dep->de_hook); /* Disable interrupt */ 192 } 193 194 /* 195 ** Name: el1_interrupt 196 ** Function: Interrupt handler. Acknwledges transmit interrupts 197 ** or unloads receive buffer to memory queue. 198 */ 199 static void el1_interrupt(dpeth_t * dep) 200 { 201 u16_t csr, isr; 202 int pktsize; 203 buff_t *rxptr; 204 205 csr = inb_el1(dep, EL1_CSR); 206 if ((csr & ECSR_XMIT) && (dep->de_flags & DEF_XMIT_BUSY)) { 207 208 /* Got a transmit interrupt */ 209 isr = inb_el1(dep, EL1_XMIT); 210 if ((isr & (EXSR_16JAM | EXSR_UNDER | EXSR_JAM)) || !(isr & EXSR_IDLE)) { 211 DEBUG(printf("3c501: got xmit interrupt (ASR=0x%02X XSR=0x%02X)\n", csr, isr)); 212 if (isr & EXSR_JAM) { 213 /* Sending, packet got a collision */ 214 dep->de_stat.ets_collision += 1; 215 /* Put pointer back to beginning of packet */ 216 outb_el1(dep, EL1_CSR, ECSR_RIDE | ECSR_SYS); 217 outw_el1(dep, EL1_XMITPTR, (EL1_BFRSIZ - TxBuff->size)); 218 /* And retrigger transmission */ 219 outb_el1(dep, EL1_CSR, ECSR_RIDE | ECSR_XMIT); 220 return; 221 222 } else if ((isr & EXSR_16JAM) || !(isr & EXSR_IDLE)) { 223 dep->de_stat.ets_sendErr += 1; 224 225 } else if (isr & EXSR_UNDER) { 226 dep->de_stat.ets_fifoUnder += 1; 227 } 228 DEBUG(printf("3c501: got xmit interrupt (0x%02X)\n", isr)); 229 el1_reset(dep); 230 } else { 231 /** if (inw_el1(dep, EL1_XMITPTR) == EL1_BFRSIZ) **/ 232 /* Packet transmitted successfully */ 233 dep->de_stat.ets_packetT += 1; 234 dep->bytes_Tx += (long) (TxBuff->size); 235 free_buff(dep, TxBuff); 236 dep->de_flags &= NOT(DEF_XMIT_BUSY); 237 netdriver_send(); 238 if (dep->de_flags & DEF_XMIT_BUSY) 239 return; 240 } 241 242 } else if ((csr & (ECSR_RECV | ECSR_XMTBSY)) == (ECSR_RECV | ECSR_XMTBSY)) { 243 244 /* Got a receive interrupt */ 245 isr = inb_el1(dep, EL1_RECV); 246 pktsize = inw_el1(dep, EL1_RECVPTR); 247 if ((isr & ERSR_RERROR) || (isr & ERSR_STALE)) { 248 DEBUG(printf("Rx0 (ASR=0x%02X RSR=0x%02X size=%d)\n", csr, isr, pktsize)); 249 dep->de_stat.ets_recvErr += 1; 250 251 } else if (pktsize < ETH_MIN_PACK_SIZE || pktsize > ETH_MAX_PACK_SIZE) { 252 DEBUG(printf("Rx1 (ASR=0x%02X RSR=0x%02X size=%d)\n", csr, isr, pktsize)); 253 dep->de_stat.ets_recvErr += 1; 254 255 } else if ((rxptr = alloc_buff(dep, pktsize + sizeof(buff_t))) == NULL) { 256 /* Memory not available. Drop packet */ 257 dep->de_stat.ets_fifoOver += 1; 258 259 } else if (isr & (ERSR_GOOD | ERSR_ANY)) { 260 /* Got a good packet. Read it from buffer */ 261 outb_el1(dep, EL1_CSR, ECSR_RIDE | ECSR_SYS); 262 outw_el1(dep, EL1_XMITPTR, 0); 263 insb(dep->de_data_port, rxptr->buffer, pktsize); 264 rxptr->next = NULL; 265 rxptr->size = pktsize; 266 dep->de_stat.ets_packetR += 1; 267 dep->bytes_Rx += (long) pktsize; 268 /* Queue packet to receive queue */ 269 if (dep->de_recvq_head == NULL) 270 dep->de_recvq_head = rxptr; 271 else 272 dep->de_recvq_tail->next = rxptr; 273 dep->de_recvq_tail = rxptr; 274 275 /* Reply to pending Receive requests, if any */ 276 netdriver_recv(); 277 } 278 } else { /* Nasty condition, should never happen */ 279 DEBUG( 280 printf("3c501: got interrupt with status 0x%02X\n" 281 " de_flags=0x%04X XSR=0x%02X RSR=0x%02X \n" 282 " xmit buffer = 0x%4X recv buffer = 0x%4X\n", 283 csr, dep->de_flags, 284 inb_el1(dep, EL1_RECV), 285 inb_el1(dep, EL1_XMIT), 286 inw_el1(dep, EL1_XMITPTR), 287 inw_el1(dep, EL1_RECVPTR)) 288 ); 289 el1_reset(dep); 290 } 291 292 /* Move into receive mode */ 293 outb_el1(dep, EL1_CSR, ECSR_RIDE | ECSR_RECV); 294 outw_el1(dep, EL1_RECVPTR, 0); 295 /* Be sure that interrupts are cleared */ 296 inb_el1(dep, EL1_RECV); 297 inb_el1(dep, EL1_XMIT); 298 } 299 300 /* 301 ** Name: el1_init 302 ** Function: Initalizes board hardware and driver data structures. 303 */ 304 static void el1_init(dpeth_t * dep) 305 { 306 int ix; 307 308 dep->de_irq &= NOT(DEI_DEFAULT); /* Strip the default flag. */ 309 dep->de_offset_page = 0; 310 dep->de_data_port = dep->de_base_port + EL1_DATAPORT; 311 312 el1_reset(dep); /* Reset and initialize board */ 313 314 /* Start receiver (default mode) */ 315 outw_el1(dep, EL1_RECVPTR, 0); 316 outb_el1(dep, EL1_CSR, ECSR_RIDE | ECSR_RECV); 317 318 /* Initializes buffer pool */ 319 init_buff(dep, NULL); 320 el1_mode_init(dep); 321 322 printf("%s: Etherlink (%s) at %X:%d - ", 323 dep->de_name, "3c501", dep->de_base_port, dep->de_irq); 324 for (ix = 0; ix < SA_ADDR_LEN; ix += 1) 325 printf("%02X%c", (dep->de_address.ea_addr[ix] = StationAddress[ix]), 326 ix < SA_ADDR_LEN - 1 ? ':' : '\n'); 327 328 /* Device specific functions */ 329 dep->de_recvf = el1_recv; 330 dep->de_sendf = el1_send; 331 dep->de_flagsf = el1_mode_init; 332 dep->de_resetf = el1_reset; 333 dep->de_getstatsf = el1_getstats; 334 dep->de_dumpstatsf = el1_dumpstats; 335 dep->de_interruptf = el1_interrupt; 336 } 337 338 /* 339 ** Name: el1_probe 340 ** Function: Checks for presence of the board. 341 */ 342 int el1_probe(dpeth_t * dep) 343 { 344 int ix; 345 346 for (ix = 0; ix < 8; ix += 1) /* Reset the board */ 347 outb_el1(dep, EL1_CSR, ECSR_RESET); 348 outb_el1(dep, EL1_CSR, ECSR_SYS); /* Leaves buffer to system */ 349 350 /* Check station address */ 351 for (ix = 0; ix < SA_ADDR_LEN; ix += 1) { 352 outw_el1(dep, EL1_XMITPTR, ix); 353 StationAddress[ix] = inb_el1(dep, EL1_SAPROM); 354 } 355 if (StationAddress[0] != 0x02 || /* Etherlink Station address */ 356 StationAddress[1] != 0x60 || /* MUST be 02:60:8c:xx:xx:xx */ 357 StationAddress[2] != 0x8C) 358 return FALSE; /* No Etherlink board at this address */ 359 360 dep->de_ramsize = 0; /* RAM size is meaningless */ 361 dep->de_linmem = 0L; /* Access is via I/O port */ 362 363 /* Device specific functions */ 364 dep->de_initf = el1_init; 365 dep->de_stopf = el1_stop; 366 367 return TRUE; /* Etherlink board found */ 368 } 369 370 #endif /* ENABLE_3C501 */ 371 372 /** 3c501.c **/ 373