/* * ***************************************************************** * * * * * Copyright Compaq Computer Corporation, 2000 * * * * * * The software contained on this media is proprietary to * * * and embodies the confidential technology of Compaq * * * Computer Corporation. Possession, use, duplication or * * * dissemination of the software and media is authorized only * * * pursuant to a valid written license from Compaq Computer * * * Corporation. * * * * * * RESTRICTED RIGHTS LEGEND Use, duplication, or disclosure * * * by the U.S. Government is subject to restrictions as set * * * forth in Subparagraph (c)(1)(ii) of DFARS 252.227-7013, * * * or in FAR 52.227-19, as applicable. * * * * * ***************************************************************** */ /* * HISTORY */ #pragma ident "@(#)$RCSfile: if_el.c,v $ $Revision: 1.1.11.8 $ (DEC) $Date: 1999/03/18 20:48:00 $" /* * 3COM EtherLink III * ================== * * This driver supports the driver interface requirements for the * 3COM 3C5x9 series Ethernet adapter. This initial debug / development * is for the 3C589C series PCMCIA adapter. * * Specifications for this driver are detailed in the document; * * EtherLink III Paraller Tasking * ISA, EISA, Micro Channel, and PCMCIA * Adapter Drivers Technical Reference * * Published by 3COM Corporation * Manual Part No. 09-0398-002B * */ /* * Include files * ============= */ #include #include #include #include #include #include #include #include #include #include #include #include #include /* Needed for nonsymmetric drivers. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* For thread access */ #include #include /* For locking */ /* * Bus support include files (for both ISA and PCMCIA variants). */ #include /* Covers ISA bus too */ #include /* pcmcia files */ #include /* * LAN Driver common include file. */ #include /* * CSR definitions/macros. */ #include /* * Softc structure definition * ========================== * * The 3c5x9 adapters do not perform any mulitcast filtering. Either you * turn on multicast reception or you have it off. This is similar to All * Multicast mode. */ struct el_softc { struct ether_driver *is_ed; /* driver */ #define is_ac is_ed->ess_ac /* common part */ #define ztime is_ed->ess_ztime /* Time since last zeroed */ #define ctrblk is_ed->ess_ctrblk /* Counter block */ #define is_if is_ac.ac_if /* network-visible interface */ #define is_addr is_ac.ac_enaddr /* hardware address */ struct net_hw_mgmt ehm; /* Enhanced Hw Mgmt Support */ struct lan_media lan_media; /* Media state/values */ #define lm_media_mode lan_media.lan_media_mode #define lm_media_state lan_media.lan_media_state #define lm_media lan_media.lan_media vm_offset_t basereg; /* base register */ struct lan_multi is_multi; /* Multicast table */ /* * Interrupt handler id */ ihandler_id_t *hid; /* set from handler_add */ /* * Pointers to registers */ io_handle_t regE; /* io_addr + E */ io_handle_t regC; /* io_addr + C */ io_handle_t regA; /* io_addr + A */ io_handle_t reg8; /* io_addr + 8 */ io_handle_t reg6; /* io_addr + 6 */ io_handle_t reg4; /* io_addr + 4 */ io_handle_t reg2; /* io_addr + 2 */ io_handle_t reg0; /* io_addr + 0 */ io_handle_t data; /* 32-bit Data In/Out */ /* * FIFO maintenance. The 3C5x9 keeps this value on board, but it * would be too expensive for us to copy it all the time. */ unsigned long txfree; /* * General stuff. */ int irq; /* IRQ to use */ int iobase; /* I/O Base to program card */ int is_broadcast; /* Boolean -- True if bcast is set */ int isa_tag; /* tag value */ unsigned long txreset; /* transmitter error resets */ unsigned long xmit_tmo; /* Transmit timeouts */ unsigned long tint; /* Transmit interrupts */ unsigned long rint; /* Receive interrupts */ int debug; /* Set if IFF_DEBUG is set */ int cardout; /* If pcmcia card has been ejected */ int reprobe; /* If pcmcia card has been reloaded */ int ispcmcia; /* True if unit is pcmcia unit */ struct card_info *cinfop; /* Card info pointer (pcmcia only) */ /* * Autosense thread context. */ thread_t autosense_thread; /* Autosense thread */ int autosense_flag; /* Autosense thread blocking flag */ /* * Polling context. */ int polling_flag; /* Polling thread blocking flag */ /* * EEPROM copy. */ struct w3_eeprom eeprom; /* * NETSYNC lock definition. All LAN drivers must use this lock (look * in probe routine for initialization). */ decl_simple_lock_data(, el_softc_lock) }; /* * Forward/External references * =========================== */ /* * Function prototype definitions for all exported functions */ int el_configure(cfg_op_t, cfg_attr_t *, size_t, cfg_attr_t *, size_t); /* * Implementation specific functions */ static int el_probe (io_handle_t, struct controller *); static int el_attach(struct controller *); static int el_unattach(struct bus *, struct controller *); static int el_init_locked(struct el_softc *, struct ifnet *, int); static int el_init(int); static void el_start_locked(struct el_softc *, struct ifnet *); static void el_start(struct ifnet *); static int el_watch(int); static void el_reset_locked(struct el_softc *, struct ifnet *, int); static void el_reset(int); static int el_ioctl(struct ifnet *, u_int, caddr_t); static int el_intr(int); static void el_rint(struct el_softc *, struct ifnet *); static void el_tint(struct el_softc *, struct ifnet *); static void el_error(struct el_softc *, struct ifnet *); static void el_shutdown(struct el_softc *); static void el_card_remove(int, int, struct el_softc *); static int el_isa_reset_all(io_handle_t, int *, struct controller *); static int el_isa_activate(io_handle_t, int *, struct controller *); static unsigned short el_isa_read_offset(io_handle_t, int); static void el_wait(struct el_softc *); static void el_autosense_thread(struct el_softc *); static int el_card_out(struct el_softc *); /* * All externally declared data */ extern struct timeval time; extern task_t first_task; /* * Softc and controller arrays * =========================== * * Unfortunately, these arrays have to be static. These are used to find * out the driver's softc and controller structure when the driver is called * with just an 'ifnet' structure. The ifnet's if_unit field is used as an * index into these arrays. The unfortunate side effect of this is that * there's now a fixed max limit to the number of devices you can have... * * These arrays are initialized at probe (only when the driver knows that * the probe is going to suceed). */ #define el_MAXDEV 7 /* Product only supports 7 adapters */ static struct el_softc *el_softc[el_MAXDEV]={0}; static struct controller *el_info[el_MAXDEV]={0}; static int el_isa_tag = 0; static int el_isa_reset = 0; decl_simple_lock_info(static, el_lock_info); /* * The "driver" structure * ====================== * * See the Digital UNIX driver writer's manual for more information about * these fields. */ static struct driver eldriver = { el_probe, /* probe */ 0, /* slave */ el_attach, /* cattach */ 0, /* dattach */ 0, /* go */ 0, /* addr_list */ 0, /* dev_name */ 0, /* dev_list */ "el", /* ctlr_name */ el_info, /* ctlr_list */ 0, /* xclu */ 0, /* addr1_size */ 0, /* addr1_atype */ 0, /* addr2_size */ 0, /* addr2_atype */ el_unattach, /* ctlr_unattach */ 0 /* dev_unattach */ }; /* * Macros, etc. * ============ * * Any driver specific macros, constants, variables (CSR read/write, reset, * etc.). */ #define READ_CCR(sc) READ_BUS_D16((sc)->reg4); mb(); #define WRITE_CCR(sc, val) WRITE_BUS_D16((sc)->reg4, (val)); mb(); #define READ_ACR(sc) READ_BUS_D16((sc)->reg6); mb(); #define WRITE_ACR(sc, val) WRITE_BUS_D16((sc)->reg6, (val)); mb(); #define WRITE_RCR(sc, val) WRITE_BUS_D16((sc)->reg8, (val)); mb(); #define WRITE_ECR(sc, val) WRITE_BUS_D16((sc)->regA, (val)); mb(); #define READ_EDR(sc) READ_BUS_D16((sc)->regC); mb(); #define WRITE_CMD(sc, val) WRITE_BUS_D16((sc)->regE, (val)); mb(); el_wait((sc)) #define READ_STS(sc) READ_BUS_D16((sc)->regE); mb(); #define WRITE_DATA(sc, val) WRITE_BUS_D32((sc)->data, (val)); mb(); #define READ_DATA(sc) READ_BUS_D32((sc)->data); mb(); #define READ_ND(sc) READ_BUS_D16((sc)->reg6); mb(); #define WRITE_ND(sc, val) WRITE_BUS_D16((sc)->reg6, (val)); mb(); #define READ_MD(sc) READ_BUS_D16((sc)->regA); mb(); #define WRITE_MD(sc, val) WRITE_BUS_D16((sc)->regA, (val)); mb(); #define READ_TXF(sc) READ_BUS_D16((sc)->regC); mb(); #define READ_RXF(sc) READ_BUS_D16((sc)->regA); mb(); #define WRITE_AD1(sc, val) WRITE_BUS_D16((sc)->reg0, (val)); mb(); #define WRITE_AD2(sc, val) WRITE_BUS_D16((sc)->reg2, (val)); mb(); #define WRITE_AD3(sc, val) WRITE_BUS_D16((sc)->reg4, (val)); mb(); #define READ_TXS(sc) READ_BUS_D16((sc)->regA); mb(); #define WRITE_TXS(sc, val) WRITE_BUS_D16((sc)->regA, (val)); mb(); #define READ_RXS(sc) READ_BUS_D16((sc)->reg8); mb(); #define READ_FDP(sc) READ_BUS_D16((sc)->reg4); mb(); /* * Configuration data * ================== * */ static unsigned char el_pcmcia_optiondata[400] = ""; static unsigned char el_isa_optiondata[300] = ""; static unsigned char el_unused[300] = ""; int el_polling=0; int el_pollint=16; static int el_configured = 0; static struct lan_config_data el_data = { LAN_CONFIG_VERSION_ID, 0, &eldriver, &el_configured }; cfg_subsys_attr_t el_attributes[] = { /* * PCMCIA requires the following field (with a corresponding 400 byte * array). */ {"PCMCIA_Option", CFG_ATTR_STRTYPE, CFG_OP_CONFIGURE | CFG_OP_QUERY | CFG_HIDDEN_ATTR, (caddr_t)el_pcmcia_optiondata, 0, 400, 0}, /* * ISA requires the following field (with a corresponding 300 byte * array). */ {"ISA_Option", CFG_ATTR_STRTYPE, CFG_OP_CONFIGURE | CFG_OP_QUERY | CFG_HIDDEN_ATTR, (caddr_t)el_isa_optiondata, 0, 300, 0}, /* * Set style of interrupt processing. */ {"Polling", CFG_ATTR_INTTYPE, CFG_OP_QUERY | CFG_OP_CONFIGURE | CFG_HIDDEN_ATTR, (caddr_t)&el_polling, 0, 1, sizeof(int)}, {"Polls_Per_Second", CFG_ATTR_INTTYPE, CFG_OP_QUERY | CFG_OP_CONFIGURE | CFG_HIDDEN_ATTR, (caddr_t)&el_pollint, 10, 100, sizeof(int)}, /* * Mark the end of the list. */ {"", 0, 0, 0, 0, 0, 0} }; /* * el_configure() * Configures, Unconfigures, and returns status based on requests * from cfgmgr. * * Arguments: * op Operation type (CFG_OP_CONFIGURE, CFG_OP_UNCONFIGURE, * CFG_OP_QUERY). * indata Input data structure * indatalen Number of cfg_attr_t entries * * Return Value: * 0 success * EINVAL Invalid parameter * ENOPEN not applicable to LAN drivers * EBUSY Device is active * ESRCH not applicable to LAN drivers */ /*ARGSUSED*/ int el_configure(cfg_op_t op, /* Operation */ cfg_attr_t *indata, /* Input data structure */ size_t indatalen, /* Size of input data structure */ cfg_attr_t *outdata, /* Output data structure */ size_t outdatalen) /* Size of output data structure */ { return (lan_configure (op, &el_data)); } /* * el_probe() * Allocate and initialize the softc for this device (if it's really * on this system). The following other actions must occur here; * * set the ethernet hardware address * perform bus specific initialization * register the shutdown routine * initialize the hardware (reset) * * Arguments: * io_handle io-handle for this device as determined by caller. * ctlr pointer to controller struct for the interface. * * Return Value: * Success: ~0 * Failure: 0 */ static int el_probe (io_handle_t io_handle, struct controller *ctlr) { struct el_softc *sc; int unit = ctlr->ctlr_num, i, j, isatag=0, status, multi_func_flag=0; struct handler_intr_info el_intr_info; ihandler_t el_ihandle; struct card_info *card_infop = (struct card_info *)(ctlr->card_info_ptr); io_handle_t reg; struct e_port port_sel; struct irq irq_sel; unsigned short *ed; unsigned char *ee; struct tuple_info *tuple_infop; struct tuple_data_info tuple_data; struct tuple_data_info *tuple_data_infop; /* * Make sure we haven't exceeded the maximum number of units that this * driver supports. */ if (unit >= el_MAXDEV) { printf("el%d: el_probe: unit exceeds max supported devices\n", unit); return(0); } /* * Verify that the device we're called with is what we think it is. * Return 0 if not recognized (this is the actual function of the * probe routine). Otherwise continue. */ switch (ctlr->bus_hd->bus_type) { case BUS_PCMCIA: /* * Find the base address of the card. */ reg = io_handle+card_infop->io_addr[0]; /* * Find out if this is a multi-function card or a single * function card. */ multi_func_flag = card_infop->card_option->multi_func_flag; /* * The manufacturer's ID (MID) doesn't exist for the combo card * so don't try to check it. This makes the assumption that the * combo card is in the reset state when we get to this probe * routine (probably a fair assumption). */ if (!multi_func_flag) { /* * Verify that we might have a EtherLink III card attached. */ if (READ_BUS_D16(reg+W0_MID) != 0x6d50) { /* * Try reseting the card to see if it can remember who it is. * This might have a negative effect if there really is a * device at the given ioaddress (writing random commands to * csrs is known to confuse hardware). */ WRITE_BUS_D16(reg+CMD_PORT, CMD_RESET); DELAY(1000); /* Wait for a ms */ /* * Now, check again, believe it if it fails this time. */ if (READ_BUS_D16(reg+W0_MID) != 0x6d50) { printf("el%d: EtherLink III not found on bus\n", unit); return(0); } } } break; case BUS_ISA: /* * Get the base I/O address */ if (get_config(ctlr, RES_PORT, NULL, &port_sel, 0) >= 0) { reg = port_sel.base_address; } else { printf("el%d: Can't get assigned IOBASE\n",unit); return(0); } /* * Get the IRQ */ if (get_config(ctlr, RES_IRQ, NULL, &irq_sel, 0) < 0) { printf("el%d: Can't get assigned IRQ\n", unit); return(0); } /* * Reset all adapters on the ISA bus once (to clear any bad * state data that may be around). */ if (el_isa_reset++ == 0) el_isa_reset_all(reg, &isatag, ctlr); /* * Attempt to activate the lowest addressed adapter on the * bus, and configure it with the given base address. */ if (el_isa_activate(reg, &isatag, ctlr)) { printf("el%d: 3C509 not present or not responding at 0x%x\n", unit, reg); return(0); } break; default: printf("el%d: Unrecognized bus type\n", unit); return(0); break; } /* * Allocate space for the softc. Or, if this is just a re-probe * of a pcmcia card that was ejected, recall what the softc pointer * was. */ if (el_softc[unit]) { sc = el_softc[unit]; sc->cardout = 0; sc->reprobe = 1; } else { MALLOC(sc, void*, sizeof(struct el_softc), M_DEVBUF, M_WAIT | M_ZERO); if (!sc) { printf("el%d: el_probe: failed to get buffer memory for softc\n", unit); return(0); } /* * Allocate an ifnet structure for this device. If one already * exists, it will be returned to us (this is unlikely for PCMCIA). * Upon return, the following fields will be initialized: if_name, * if_unit. And, if this structure is being reused, if_index will * also be initialized with its old value. All other fields are * unknown and must be initialized. Invoke a common macro to clear * all statistics fields in the ether_driver structure. The remaining * fields will be initialized in the rest of this probe function and * the attach function. */ sc->is_ed = if_alloc("el", unit, sizeof(struct ether_driver)); CLEAR_LAN_COUNTERS(sc->is_ed); /* * Allocate and initialize data structure for Enhanced Hardware * Management (ehm) support. */ lan_ehm_init(&sc->ehm, NET_EHM_VERSION_ID); } /* * Fill in softc structure with CSR addresses, etc. */ sc->regE = reg+0xe; sc->regC = reg+0xc; sc->regA = reg+0xa; sc->reg8 = reg+0x8; sc->reg6 = reg+0x6; sc->reg4 = reg+0x4; sc->reg2 = reg+0x2; sc->reg0 = reg+0x0; sc->data = reg+0x0; /* * Save the base register location */ sc->basereg = reg; /* * Set some bus specific fields that we couldn't do above since the * softc structure wasn't created yet. */ switch (ctlr->bus_hd->bus_type) { case BUS_PCMCIA: /* * Fake the irq and iobase out to the required value. */ sc->irq = 3; sc->iobase = 0; /* * Indiciate that this unit is a pcmcia unit, and save card info * pointer. */ sc->ispcmcia = 1; sc->cinfop = card_infop; /* * Register the removal event routine. */ pcmcia_register_event_callback(card_infop->socket_vnum, CARD_REMOVAL_EVENT, (caddr_t)el_card_remove, (caddr_t)sc); /* * Initialize the if_snd queue lock and maxlen fields. This is * unique to pcmcia since pcmcia is probed so late (it misses the * the common ifnet initialization). */ IFQ_LOCKINIT_SPLIMP(&sc->is_if.if_snd); sc->is_if.if_snd.ifq_maxlen = IFQ_MAXLEN; /* * Set the model identification string */ if (multi_func_flag) lan_set_attribute(sc->ehm.current_val, NET_MODEL_NDX, "3C562"); else lan_set_attribute(sc->ehm.current_val, NET_MODEL_NDX, "3C589"); break; case BUS_ISA: /* * Save irq from isacfg */ sc->irq = irq_sel.channel; /* * Save tag from activation process */ sc->isa_tag = isatag; /* * Compute iobase to give device */ sc->iobase = ((reg-0x200)/0x10)&0x1f; /* * Set the model identification string */ lan_set_attribute(sc->ehm.current_val, NET_MODEL_NDX, "3C509"); break; } /* * If the device is being probed again (e.g., the second time), this * is a pcmcia device that has been put back into the slot. We don't * need to redo much of the initial work in this case. */ if (!sc->reprobe) { /* * Read the EEPROM and save it in sc->eeprom. If this is a 3COM * 3C562 multi-function PC Card, the EEPROM data is located in Card * Information Structure of the card using the 3COM vendor-specific * tuple code of 0x88. */ if (multi_func_flag) { /* * Clear out any old data from the tuple data argument structure, * and retrieve the desired (0x88) tuple. */ bzero((caddr_t)&tuple_data, sizeof(struct tuple_data_info)); tuple_data_infop = &tuple_data; tuple_infop = (struct tuple_info *)&tuple_data; tuple_infop->socket = (short) card_infop->socket_vnum; tuple_infop->attributes = 0; tuple_infop->DesiredTuple = 0x88; status = GetFirstTuple(tuple_infop); /* * If successfull, copy the eeprom data out of the tuple area. * Otherwise, fail probe. */ if (status == SUCCESS) { tuple_data_infop->TupleOffset = 0; tuple_data_infop->TupleDataMax = (u_short)TUPLE_DATA_MAX; status = GetTupleData(tuple_data_infop); if (status == SUCCESS) { ee = (unsigned char *)&sc->eeprom; for (i = 0; i < (sizeof(struct w3_eeprom)); i++) { *ee = tuple_data_infop->TupleData[i]; ee++; } } else { printf("el%d: Can't read multifunction card's eeprom.\n", unit); if (sc->ispcmcia) pcmcia_unregister_event_callback(card_infop->socket_vnum, CARD_REMOVAL_EVENT, (caddr_t)el_card_remove); if_dealloc(sc->is_ed); lan_ehm_free(&sc->ehm); FREE(sc, M_DEVBUF); return(0); } } else { printf("el%d: Can't read multifunction card's eeprom.\n", unit); if (sc->ispcmcia) pcmcia_unregister_event_callback(card_infop->socket_vnum, CARD_REMOVAL_EVENT, (caddr_t)el_card_remove); if_dealloc(sc->is_ed); lan_ehm_free(&sc->ehm); FREE(sc, M_DEVBUF); return(0); } } else { /* * Copy the eeprom the old fashion way. */ ed = (unsigned short *)&sc->eeprom; for (i=0; i<(sizeof(struct w3_eeprom)/2); i++) { WRITE_ECR(sc, ECR_READ+i); /* Write the 'read' cmd + offset */ DELAY(1000); /* Wait for a msec */ *ed = READ_EDR(sc); /* Read the data register */ ed++; /* Point to next */ } } /* * Read and save the 48-bit physical address of the device into the * sc->is_addr field. */ for (i=0; i<3; i++) { j = sc->eeprom.addr[i]; sc->is_addr[(i*2)] = (j>>8) & 0xff; sc->is_addr[(i*2)+1] = (j) & 0xff; } /* * Set default autosense media. Start with UTP media. */ sc->lm_media_mode = LAN_MODE_AUTOSENSE; sc->lm_media_state = LAN_MEDIA_STATE_SENSING; sc->lm_media = LAN_MEDIA_UTP; /* * Start/Init the autosense thread (it actually will not start anything * until if_init -- if autosense mode is still selected). * * Terminate this thread with the following code. * sc->autosense_thread->ast |= 1; * thread_halt(sc->autosense_thread); * thread_deallocate(sc->autosense_thread); * Or, if you know for a fact that force_terminate has been fixed. * thread_force_terminate(sc->autosense_thread); * * Wake this thread up with the following code. * thread_wakeup_one((vm_offset_t)&sc->autosense_flag); */ sc->autosense_thread = kernel_thread_w_arg(first_task, el_autosense_thread, (void *)sc); if (sc->autosense_thread == NULL) { printf("el%d: Can't create autosense thread.\n", unit); if (sc->ispcmcia) pcmcia_unregister_event_callback(card_infop->socket_vnum, CARD_REMOVAL_EVENT, (caddr_t)el_card_remove); if_dealloc(sc->is_ed); lan_ehm_free(&sc->ehm); FREE(sc, M_DEVBUF); return(0); } } else { struct w3_eeprom ee_copy; unsigned char tmp_addr[8]; struct ifreq ifr; struct ifnet *ifp = &sc->is_if; /* * Unit has been re-inserted into the system. Let's make sure that * it has the same hardware address. If it does, great. If it * doesn't, we have to do a little house cleaning. */ /* * Read the EEPROM and save it in temporary struct. If this is a 3COM * 3C562 multi-function PC Card, the EEPROM data is located in Card * Information Structure of the card using the 3COM vendor-specific * tuple code of 0x88. */ if (multi_func_flag) { /* * Clear out any old data from the tuple data argument structure, * and retrieve the desired (0x88) tuple. */ bzero((caddr_t)&tuple_data, sizeof(struct tuple_data_info)); tuple_data_infop = &tuple_data; tuple_infop = (struct tuple_info *)&tuple_data; tuple_infop->socket = (short) card_infop->socket_vnum; tuple_infop->attributes = 0; tuple_infop->DesiredTuple = 0x88; status = GetFirstTuple(tuple_infop); /* * If successfull, copy the eeprom data out of the tuple area. * Otherwise, fail probe. */ if (status == SUCCESS) { tuple_data_infop->TupleOffset = 0; tuple_data_infop->TupleDataMax = (u_short)TUPLE_DATA_MAX; status = GetTupleData(tuple_data_infop); if (status == SUCCESS) { ee = (unsigned char *)&ee_copy; for (i = 0; i < (sizeof(struct w3_eeprom)); i++) { *ee = tuple_data_infop->TupleData[i]; ee++; } } else { printf("el%d: Can't read multifunction card's eeprom.\n", unit); if (sc->ispcmcia) pcmcia_unregister_event_callback(card_infop->socket_vnum, CARD_REMOVAL_EVENT, (caddr_t)el_card_remove); return(0); } } else { printf("el%d: Can't read multifunction card's eeprom.\n", unit); if (sc->ispcmcia) pcmcia_unregister_event_callback(card_infop->socket_vnum, CARD_REMOVAL_EVENT, (caddr_t)el_card_remove); return(0); } } else { /* * Read the EEPROM the old way and save it in temporary structure. */ ed = (unsigned short *)&ee_copy; for (i=0; i<(sizeof(struct w3_eeprom)/2); i++) { WRITE_ECR(sc, ECR_READ+i); /* Write the 'read' cmd + offset */ DELAY(1000); /* Wait for a msec */ *ed = READ_EDR(sc); /* Read the data register */ ed++; /* Point to next */ } } /* * See if the address has changed. */ if (bcmp(sc->eeprom.addr, ee_copy.addr, 6)) { /* * Hardware address has changed. Get cannonical form. */ for (i=0; i<3; i++) { j = sc->eeprom.addr[i]; tmp_addr[(i*2)] = (j>>8) & 0xff; tmp_addr[(i*2)+1] = (j) & 0xff; } /* * Is the hardware address currently the set physical address? * If it isn't, we don't have anything to do. If it is, we have * a little work to do. */ if (bcmp(tmp_addr, sc->is_addr, 6) == 0) { /* * Get new address in cannonical form. */ for (i=0; i<3; i++) { j = ee_copy.addr[i]; tmp_addr[(i*2)] = (j>>8) & 0xff; tmp_addr[(i*2)+1] = (j) & 0xff; } /* * Copy in new address and inform everyone of the * change. This is highly unusual. */ bzero(&ifr, sizeof(struct ifreq)); bcopy(tmp_addr, ifr.ifr_addr.sa_data, 6); bcopy(tmp_addr, sc->is_addr, 6); if (((struct arpcom *)ifp)->ac_flag & AC_IPUP) { rearpwhohas((struct arpcom *)ifp); } if_sphyaddr(ifp, &ifr); pfilt_newaddress(sc->is_ed->ess_enetunit, sc->is_addr); } /* * Copy new eeprom struct. */ bcopy(&ee_copy, &sc->eeprom, sizeof(struct w3_eeprom)); } } /* * Register interrupt routine (enable in attach). */ el_intr_info.configuration_st = (caddr_t)ctlr; el_intr_info.intr = el_intr; el_intr_info.param = (caddr_t)unit; el_intr_info.config_type = CONTROLLER_CONFIG_TYPE; if (ctlr->bus_hd->bus_type == BUS_PCMCIA) el_intr_info.config_type |= SHARED_INTR_CAPABLE; /* * If driver is capable of sharing interrupts, indicate * this via the following; * * el_intr_info.config_type |= SHARED_INTR_CAPABLE; * */ el_ihandle.ih_bus = ctlr->bus_hd; el_ihandle.ih_bus_info = (char *)&el_intr_info; sc->hid = handler_add(&el_ihandle); if (sc->hid == (ihandler_id_t *)(NULL)) { printf("el%d: interrrupt handler add failed\n", unit); if (sc->ispcmcia) pcmcia_unregister_event_callback(card_infop->socket_vnum, CARD_REMOVAL_EVENT, (caddr_t)el_card_remove); if_dealloc(sc->is_ed); lan_ehm_free(&sc->ehm); FREE(sc, M_DEVBUF); return(0); } /* * Save controller and softc pointers in our tables. */ el_softc[unit] = sc; el_info[unit] = ctlr; /* * Try to allocate another controller so we can support additional * units. */ if (!sc->reprobe && lan_create_controller(&el_data) != ESUCCESS) { printf("el%d: WARNING: create_controller failed\n", unit); } /* * Register shutdown routine. This is required to make the device * stop all activity as a result of a system crash/shutdown. */ if (!sc->reprobe) drvr_register_shutdown(el_shutdown, (void*)sc, DRVR_REGISTER); return(~0); } /* * el_attach() * Fill in ifnet structure and attach to the network layers. * * Arguments: * ctlr pointer to controller struct for the interface. * * Return Value: * None. */ static int el_attach(struct controller *ctlr) { register int unit = ctlr->ctlr_num; register struct el_softc *sc = el_softc[unit]; register struct ifnet *ifp = &sc->is_if; register struct sockaddr_in *sin; /* * Watch for pcmcia card being reloaded. */ if (!sc->reprobe) { /* * If this subsystem must always be linked into the kernel, set * the ALV_STATIC bit. If this subsystem can be dynamically * loaded, set the ALV_NOSIZER bit. * * 9/30/98 -- Dynamic config for PCMCIA is still not available. * * If it were available, you'd need the following code. * * if (el_data.cfg_state == SUBSYSTEM_DYNAMICALLY_CONFIGURED) * ctlr->alive |= ALV_NOSIZER; * else * cltr->alive |= ALV_STATIC; */ ctlr->alive |= ALV_STATIC; /* * Setup address/header information for this media's type. */ ifp->if_addrlen = 6; /* media address len */ ifp->if_hdrlen = /* Max datalink header size */ sizeof(struct ether_header) + 8; /* src+dst+pid/size+pid */ /* * Setup media specific portions */ sc->is_ac.ac_bcastaddr = (u_char *)etherbroadcastaddr; sc->is_ac.ac_arphrd = ARPHRD_ETHER; /* * Done for us by if_alloc * ifp->if_unit = unit; * ifp->if_name = "el"; */ ifp->if_mtu = ETHERMTU; ifp->if_mediamtu = ETHERMTU; ifp->if_type = IFT_ETHER; ((struct arpcom *)ifp)->ac_flag = 0; sin = (struct sockaddr_in *)&ifp->if_addr; sin->sin_family = AF_INET; /* * Setup lock info. It's no longer necessary to #if NETSYNC_LOCK since * it's always defined now. However, it's the LAN driver convention * to use the symbol. */ #if NETSYNC_LOCK ifp->if_affinity = NETALLCPU; ifp->lk_softc = &sc->el_softc_lock; #endif simple_lock_setup(&sc->el_softc_lock, el_lock_info); /* * Print banner indicating that the device is loaded and print * the hardware address (this code assumes that you've already * stored it in sc->is_addr). */ printf("el%d: %s, hardware address: %s\n", unit, ifp->if_version, ether_sprintf(sc->is_addr)); /* * Setup dispatch routines -- used by network layer to call driver * * The order in which these routines are defined is important. If * this is a re-attach, there may be other users in the system trying * to use these functions. We can help guard against this by * re-establishing the function pointers in the order below. It is * important to remember that others may be using those functions NOW! */ ifp->if_init = el_init; /* init routine */ ifp->if_ioctl = el_ioctl; /* "I" octal */ ifp->if_watchdog = el_watch; /* Optional, but we use it */ ifp->if_reset = (int (*)())el_reset;/* reset routine */ ifp->if_start = (int (*)())el_start;/* start (output, transmit) routine */ mb(); ifp->if_output = ether_output; /* Common for all drivers */ mb(); ifp->if_flags = IFF_BROADCAST|IFF_MULTICAST|IFF_NOTRAILERS|IFF_SIMPLEX; ifp->if_timer = 0; /* Secs until if_watchdog is called */ ifp->if_sysid_type = 0; /* Optional -- See DNA spec for MOP number */ ifp->if_version = "3Com EtherLink III"; /* * Set baudrate (used by SNMP). */ ifp->if_baudrate = ETHER_BANDWIDTH_10MB; /* * Attach the packet filter. */ attachpfilter(sc->is_ed); /* * Attach the network layer. */ if_attach(ifp); el_configured ++; /* * Set the known non-zero network attributes for the hardware * management facility and register the adapter. */ lan_set_common_attributes(sc->ehm.current_val, sc->is_ed); lan_set_attribute(sc->ehm.current_val, NET_METHOD_NDX, net_method_automatic); /* * Some other values that would be good to register in the * future would be NET_HWREV_NDX and NET_FWREV_NDX. All * attributes maintained by the LAN drivers are specified * in /usr/include/io/dec/netif/lan_common.h. */ lan_register_adapter(&sc->ehm, ctlr); } else { printf("el%d: %s, reloaded -- current lan address: %s\n", unit, ifp->if_version, ether_sprintf(sc->is_addr)); /* * If running, call init to bring the device back up. */ if (ifp->if_flags & IFF_RUNNING) el_init(unit); } /* * Enable the interrupt handler (interrupts may now occur). */ handler_enable(sc->hid); /* * If polling, kick the polling process (if not started already). */ if (el_polling && !sc->polling_flag) { sc->polling_flag = 1; timeout((void *)el_intr, (void *)unit, (1*hz)/el_pollint); } else sc->polling_flag = 0; return(0); } /* * el_unattach * Called per controller to stop device and free memory and/or * any other dynamically allocated resources in preparation for * unloading the driver or for powering off the bus to which this * device is attached. This function should undo everything that * was done in the probe/attach routines. * * 10/6/98 this currently isn't supported by the PCMCIA bus code, but * it's here when the support is ready. PCI bus code supports this * unattach operation. * * Arguments: * bus The bus that our adapter is on * ctlr The controller being unattached * * Return Values: * ESUCCESS The device was successfully stopped * and all associated memory and DMA * resources freed. * EBUSY The device is still in use * ENOTSUP Unattach/unload not supported for * this device */ static int el_unattach(struct bus *bus, struct controller *ctlr) { int unit = ctlr->ctlr_num; int s, status; struct el_softc *sc = el_softc[unit]; struct ifnet *ifp = &sc->is_if; /* * Try to detach the interface from the system. If this fails, then * we have to abort the unattach operation. It is required that * the system has no IP addresses assigned to this interface for * the detach to be successful. */ status = if_detach(ifp); if (status == EBUSY) return(status); else if (status == ESUCCESS) detachpfilter(sc->is_ed); ifp->if_flags &= ~IFF_RUNNING; /* * Acquire the softc lock and shutdown the device. */ s = splimp(); simple_lock(&sc->el_softc_lock); el_shutdown(sc); simple_unlock(&sc->el_softc_lock); splx(s); /* * Disable/delete our interrupt handler. */ if (sc->hid) { handler_disable(sc->hid); handler_del(sc->hid); sc->hid = NULL; } /* * Kill the autosense thread. */ if (sc->autosense_thread) { thread_force_terminate(sc->autosense_thread); sc->autosense_thread = NULL; } /* * Unregister the pcmcia event callback. */ if (sc->ispcmcia) pcmcia_unregister_event_callback(sc->cinfop->socket_vnum, CARD_REMOVAL_EVENT, (caddr_t)el_card_remove); /* * Unregister the polling timeout (if polling). */ s = splimp(); simple_lock(&sc->el_softc_lock); if (el_polling && sc->polling_flag) { untimeout((void *)el_intr, (void *)ifp->if_unit); sc->polling_flag = 0; } simple_unlock(&sc->el_softc_lock); splx(s); /* * Unregister the driver shutdown routine. */ drvr_register_shutdown(el_shutdown, (void *)ctlr, DRVR_UNREGISTER); /* * Free up the softc lock. */ simple_lock_terminate(&sc->el_softc_lock); /* * Remove us from the Hardware Managment database */ lan_ehm_free(&sc->ehm); /* * Finally, deallocate the softc structure and cleanup. */ FREE(sc, M_DEVBUF); el_softc[unit] = NULL; el_info[unit] = NULL; el_configured--; /* * Done. */ return (ESUCCESS); } /* * el_init_locked() * Initialize interface. * * This must be called with the softc lock taken and SPL == IMP. * * Arguments: * sc The softc structure * ifp The interface pointer * unit The unit number of the interface * * Return Value: * - EIO * - ENOMEM * - ENOBUFS * - ESUCCESS */ static int el_init_locked(struct el_softc *sc, struct ifnet *ifp, int unit) { register struct controller *ctlr = el_info[unit]; int i; /* * Reset transmitter and Receiver */ WRITE_CMD(sc, CMD_TXRESET); WRITE_CMD(sc, CMD_RXRESET); /* * Clear any interrupts left lying around. This has to be done incase * there was an interrupt we missed. */ WRITE_CMD(sc, CMD_ACKINT+0xff); /* * Start the device */ WRITE_CMD(sc, CMD_WINDOW0); /* Select window 0 */ i = READ_CCR(sc); WRITE_CCR(sc, CCR_ENA | i); /* Set enable in CCR */ WRITE_RCR(sc, /* Set IRQ */ (sc->irq << 12) | RCR_RSV); /* * Make sure that the 10Base2 transceiver is off. */ WRITE_CMD(sc, CMD_STOP2); DELAY(800); /* * Set the LAN media based off of settings in softc. */ i = READ_ACR(sc); i &= ~(ACR_BASE|ACR_10B2); /* Clear all media bits */ switch (sc->lm_media) { case LAN_MEDIA_BNC: WRITE_ACR(sc, i | ACR_10B2 | sc->iobase); WRITE_CMD(sc, CMD_START2); DELAY(800); break; case LAN_MEDIA_AUI: WRITE_ACR(sc, i | ACR_10B5 | sc->iobase); break; default: sc->lm_media = LAN_MEDIA_UTP; case LAN_MEDIA_UTP: WRITE_ACR(sc, i | ACR_10BT | sc->iobase); WRITE_CMD(sc, CMD_WINDOW4); i = READ_MD(sc); WRITE_MD(sc, i | (MD_LBE | MD_JABE)); break; } lan_set_attribute(sc->ehm.current_val, NET_MEDIA_NDX, lan_media_strings[sc->lm_media]); /* * Select memory mapping for 3C589B only. */ if (ctlr->bus_hd->bus_type == BUS_PCMCIA) { WRITE_CMD(sc, CMD_WINDOW0); i = READ_CCR(sc); if ((i & 0xc000) == 0x8000) { WRITE_CMD(sc, CMD_WINDOW3); i = sc->eeprom.icw & ~(ASI_RS|ASI_RS|ASI_RSIZE8|ASI_RSIZE32| ASI_PAR_35|ASI_PAR_13|ASI_PAR_11); i |= (ASI_PAR_11 | ASI_RSIZE32); /* 1:1 32K buffer size */ WRITE_DATA(sc, i); } } /* * Reset transmitter and Receiver (again) */ WRITE_CMD(sc, CMD_TXRESET); WRITE_CMD(sc, CMD_RXRESET); /* * Set LAN address */ WRITE_CMD(sc, CMD_WINDOW2); i = (sc->is_addr[1] << 8) + sc->is_addr[0]; WRITE_AD1(sc, i); i = (sc->is_addr[3] << 8) + sc->is_addr[2]; WRITE_AD2(sc, i); i = (sc->is_addr[5] << 8) + sc->is_addr[4]; WRITE_AD3(sc, i); lan_set_attribute(sc->ehm.current_val, NET_MAC_NDX, ether_sprintf(sc->is_addr)); /* * Process any special flags in the ifp->if_flags field. The most * notable flags are; * * IFF_LOOPBACK * IFF_ALLMULTI * IFF_PROMISC */ if (ifp->if_flags & IFF_LOOPBACK) { WRITE_CMD(sc, CMD_WINDOW4); /* Reset clears the ND_LOOP bit */ i = READ_ND(sc); WRITE_ND(sc, ND_LOOP | i); lan_set_attribute(sc->ehm.current_val, NET_LOOP_NDX, (void *)1); } else { lan_set_attribute(sc->ehm.current_val, NET_LOOP_NDX, (void *)0); } i = RF_IND | RF_BRD; /* Always Recieve Individual and Broadcasts */ if ((ifp->if_flags & IFF_ALLMULTI) || (sc->is_multi.lan_nmulti)) { i |= RF_GRP; /* Receives All Multicasts */ } if (ifp->if_flags & IFF_PROMISC) { i |= RF_PRM; /* HAH HAH HAH */ lan_set_attribute(sc->ehm.current_val, NET_PROMISC_NDX, (void *)1); } else { lan_set_attribute(sc->ehm.current_val, NET_PROMISC_NDX, (void *)0); } WRITE_CMD(sc, CMD_FILTER+i); /* * Set debug if IFF_DEBUG is set */ if (ifp->if_flags & IFF_DEBUG) sc->debug++; else sc->debug = 0; /* * If debugging, print ram sizes. */ if (sc->debug) { WRITE_CMD(sc, CMD_WINDOW3); i = READ_TXF(sc); printf("el%d: Transmit FIFO size == %d\n", unit, i); i = READ_RXF(sc); WRITE_CMD(sc, CMD_WINDOW1); printf("el%d: Receive FIFO size == %d\n", unit, i); } /* * Enable TX and RX. */ WRITE_CMD(sc, CMD_RXENA); WRITE_CMD(sc, CMD_TXENA); /* * Enable Interrupts if not polling. */ if (!el_polling) { WRITE_CMD(sc, CMD_ZINTMASK+0xfe); WRITE_CMD(sc, CMD_SINTMASK+(S_AF|S_TC|S_RC)); /* Adapter Failure */ /* Transmit Complete */ /* Receive Complete */ } else { WRITE_CMD(sc, CMD_ZINTMASK+0xfe); WRITE_CMD(sc, CMD_SINTMASK+0); /* No interrupts */ } /* * Init FIFO (SET OPERATIONAL WINDOW) */ WRITE_CMD(sc, CMD_WINDOW1); sc->txfree = READ_TXF(sc); /* * Mark the device as running and mark that there is no output * outstanding. */ ifp->if_flags |= IFF_RUNNING; ifp->if_flags &= ~IFF_OACTIVE; /* * If in autosense mode, kick the autosense thread into operation. */ if (sc->lm_media_mode == LAN_MODE_AUTOSENSE) { sc->lm_media_state = LAN_MEDIA_STATE_SENSING; thread_wakeup_one((vm_offset_t)&sc->autosense_flag); } /* * If there are any pending packets, start transmit of them now. */ if (ifp->if_snd.ifq_head) el_start_locked(sc, ifp); return ESUCCESS; } /* * el_init() * Jacket to Initialize interface. * * Arguments: * unit The unit number of the interface * * Return Value: * - EIO * - ENOMEM * - ENOBUFS * - ESUCCESS */ static int el_init(int unit) { register struct el_softc *sc = el_softc[unit]; register struct ifnet *ifp = &sc->is_if; int i, s; /* * If the pcmcia card is out... return. */ if (sc->cardout) return(EIO); /* * Set IPL, and attain lock. */ s = splimp(); simple_lock(&sc->el_softc_lock); /* * Call the initialize interface routine. */ i = el_init_locked(sc, ifp, unit); /* * Unlock, reset IPL. */ simple_unlock(&sc->el_softc_lock); splx(s); /* * Exit with status returned for initialization routine. */ return(i); } /* * el_start_locked() * Interface start routine - start output on the interface * * Requires softc lock to be locked, and splimp. * * Arguments: * sc The softc pointer. * ifp The ifnet pointer, pointing to the intf. to o/p on. * * Return Value: * None. */ static void el_start_locked(struct el_softc *sc, struct ifnet *ifp) { struct mbuf *m, *ms, *mp, *mn; int len, i, j, val; unsigned char *dat; struct ether_header *eh; /* * If the pcmcia card is out (ejected), discard all transmits. */ if (sc->cardout) { IF_DEQUEUE(&ifp->if_snd, m); while (m) { m_freem(m); IF_DEQUEUE(&ifp->if_snd, m); } return; } /* * Remove packets from the pending queue and have the device * transmit the packet. */ while(1) { IF_DEQUEUE(&ifp->if_snd, m); if ((m) && ((m->m_pkthdr.len+8) < sc->txfree) ) { /* * First, get rid of any zero length segments. 'ms' will point * to first buffer segment with data. */ ms = m; while (ms && (ms->m_len == 0)) /* Get rid of leading 0 length */ ms = ms->m_next; if (ms == NULL) { /* If a zero length transmit */ m_freem(m); continue; } mp = ms; mn = mp->m_next; len = mp->m_len; while (mn != NULL) { /* Get rid of middle 0s */ if (mn->m_len == 0) { mp->m_next = mn->m_next; mn->m_next = NULL; m_free(mn); } else { len += mn->m_len; mp = mn; } mn = mp->m_next; } /* * Transmit the buffer. */ WRITE_DATA(sc, len | TX_INT); /* Say we want an interrupt */ dat = mtod(ms, unsigned char *); len = ms->m_len; while (ms != NULL) { /* * Write data portion of the message using 32 bit writes (this * assumes that the hardware can handle this). We have to do * byte copies from the buffer since we can't assume anything * about the alignment of these buffers. */ io_blockwrite((vm_offset_t)dat, sc->data, (u_long)(len & ~3), HANDLE_LONGWORD); dat += (len & ~3); /* * Point to next segment (if any) */ ms = ms->m_next; /* * All transmits are dword (32 bit) aligned. This handles * any bytes left over from the copy above and when switching * from segment to next segment. */ i = len % 4; if (ms == NULL) { /* If next segment doesn't exist */ if (i) { /* If bytes are remaining */ val = 0; for (j=0; jm_len <= (4-i)) { for (j=0; jm_len; j++) val |= (*dat++ << (8*(j+i))); ms = NULL; } else { len = ms->m_len - (4-i); for (j=i; j<4; j++) val |= (*dat++ << (8*j)); } WRITE_DATA(sc, val); } else { /* * The easy case. No bytes remaining in the * previous segment. Just point to the next * segment and continue. */ dat = mtod(ms, unsigned char *); len = ms->m_len; } } } /* * Account for the bytes going out. */ sc->txfree -= ((m->m_pkthdr.len + 3) & ~0x3); sc->txfree -= 4; /* Account for the "preamble" bytes */ /* * Update counters. Most drivers do this in the transmit * complete routine. */ ADD_XMIT_PACKET(ifp, sc, m->m_pkthdr.len); eh = mtod(m, struct ether_header *); if (eh->ether_dhost[0] & 0x1) { ADD_XMIT_MPACKET(ifp, sc, m->m_pkthdr.len); } /* * Free the transmit buffer */ m_freem(m); /* * Indicate output process is active (clear in transmit * interrupt). */ ifp->if_flags |= IFF_OACTIVE; } else if (m) { IF_PREPEND(&ifp->if_snd, m); break; } else break; } /* * Start the watchdog. */ ifp->if_timer = 3; } /* * el_start() * Jacket to el_start_locked. Simply sets IPL and locks the softc. * * Arguments: * ifp The ifnet pointer, pointing to the intf. to o/p on. * * Return Value: * None. */ static void el_start(struct ifnet *ifp) { register int unit = ifp->if_unit, s; register struct el_softc *sc = el_softc[unit]; /* * Set IPL, and take lock. */ s = splimp(); if (!simple_lock_try(&sc->el_softc_lock)) { splx(s); return; } /* * Call the interface start routine. */ el_start_locked(sc, ifp); /* * Unlock, reset IPL. */ simple_unlock(&sc->el_softc_lock); splx(s); } /* * el_watch() * Watchdog timer routine - goes off whenever a transmit times-out. * * Arguments: * unit The unit number of the interface * * Return Value: * None. */ static int el_watch(int unit) { register struct el_softc *sc = el_softc[unit]; register struct ifnet *ifp = &sc->is_if; int s; s = splimp(); simple_lock(&sc->el_softc_lock); /* * Count the transmit timeout. */ sc->xmit_tmo++; /* * Clear the timer and reset the unit. */ ifp->if_timer = 0; el_reset_locked(sc, ifp, unit); simple_unlock(&sc->el_softc_lock); splx(s); return(0); } /* * el_reset_locked() * Reset the interface. * * This must be called with the softc lock taken and SPL == IMP. * * Argument * sc The softc structure * ifp The interface pointer * unit The unit number of the interface * * Return Value * none */ static void el_reset_locked(struct el_softc *sc, struct ifnet *ifp, int unit) { /* * Stop the hardware and indicate that the device is no longer running. */ ifp->if_flags &= ~IFF_RUNNING; el_init_locked(sc, ifp, unit); } /* * el_reset() * Jacket routine into the Reset interface. * * Argument * unit The unit number of the interface * * Return Value * none */ static void el_reset(int unit) { struct el_softc *sc = el_softc[unit]; struct ifnet *ifp = &sc->is_if; int s; /* * If the pcmcia card is out... return. */ if (sc->cardout) return; /* * Set IPL, and attain lock. */ s = splimp(); simple_lock(&sc->el_softc_lock); /* * Call the initialize interface routine. */ el_reset_locked(sc, ifp, unit); /* * Unlock, reset IPL. */ simple_unlock(&sc->el_softc_lock); splx(s); } /* * el_ioctl() * Process an ioctl request. In doing so, the interface is * restarted and reinitialized (in most cases). * * Arguments: * ifp Pointer to the ifnet structure * cmd The ioctl to be performed * data The corresponding data for the ioctl. * * Return Value: * - EIO * - EINVAL * - ENOMEM * - ENOBUFS * - ESUCCESS */ static int el_ioctl(struct ifnet *ifp, u_int cmd, caddr_t data) { register struct el_softc *sc = el_softc[ifp->if_unit]; register unit = ifp->if_unit; struct ifreq *ifr = (struct ifreq *)data; struct ifdevea *ifd = (struct ifdevea *)data; struct ctrreq *ctr = (struct ctrreq *)data; struct ifchar *ifc = (struct ifchar *)data; int s, i, j, need_reset, lock_on = 1, status = ESUCCESS; unsigned short ifmtu, speed; u_char mclist_buf[NET_SZ_MCLIST]; /* * If the pcmcia card is out... return. */ if (sc->cardout) return(EIO); s = splimp(); simple_lock(&sc->el_softc_lock); switch (cmd) { /* * Enable Loopback mode */ case SIOCENABLBACK: ifp->if_flags |= IFF_LOOPBACK; if (ifp->if_flags & IFF_RUNNING) el_reset_locked(sc, ifp, unit); break; /* * Disable Loopback mode */ case SIOCDISABLBACK: ifp->if_flags &= ~IFF_LOOPBACK; if (ifp->if_flags & IFF_RUNNING) el_reset_locked(sc, ifp, unit); break; /* * Read current physical and hardware addresses */ case SIOCRPHYSADDR: /* * Copy current address */ bcopy(sc->is_addr, ifd->current_pa, 6); /* * Copy hardware address (defined in a ROM somewhere) */ for (i=0; i<3; i++) { j = sc->eeprom.addr[i]; ifd->default_pa[(i*2)] = (j>>8) & 0xff; ifd->default_pa[(i*2)+1] = (j) & 0xff; } break; /* * Set locally administered MAC address. If not supported, change * status to a meaningful errno, and break. */ case SIOCSPHYSADDR: /* * Copy to ifnet struct */ bcopy(ifr->ifr_addr.sa_data, sc->is_addr, 6); /* * Notify the packet filter */ pfilt_newaddress(sc->is_ed->ess_enetunit, sc->is_addr); /* * Notify the hardware. This may require a reset of the device * if not sufficiently intelligent. */ if (ifp->if_flags & IFF_RUNNING) { /* * Only change the hardware if it's running. */ el_reset_locked(sc, ifp, unit); } /* * Drop lock and ipl. The functions used from here on out don't * require synch. */ simple_unlock(&sc->el_softc_lock); splx(s); lock_on = 0; /* * If an IP address has been configured then an ARP * packet must be broadcast to tell all hosts which * currently have our address in their ARP tables to * update their information. */ if (((struct arpcom *)ifp)->ac_flag & AC_IPUP) { rearpwhohas((struct arpcom *)ifp); } /* * Notify about possible change in af_link address. */ if_sphyaddr(ifp, ifr); break; /* * Delete a multicast */ case SIOCDELMULTI: /* * Check what type of multicast address this is and decrement * the appropriate counter. Only remove the capability from the * device when no more of a type exists. */ need_reset = 0; if (bcmp(ifr->ifr_addr.sa_data, etherbroadcastaddr, 6) == 0) { sc->is_broadcast--; } else { i = lan_del_multi(&sc->is_multi, (unsigned char *)ifr->ifr_addr.sa_data); switch (i) { case LAN_MULTI_CHANGED: if (sc->is_multi.lan_nmulti == 0) need_reset++; break; case LAN_MULTI_NOT_CHANGED: break; case LAN_MULTI_FAILED: default: status = EINVAL; break; } } if ((ifp->if_flags & IFF_RUNNING) && (need_reset)) el_reset_locked(sc, ifp, unit); if (sc->debug) { j = 0; printf("el%d: Dump of multicast table after DEL (%d entries)\n", unit, sc->is_multi.lan_nmulti); for (i=0; iis_multi.lan_nmulti; i++) { unsigned char *maddr; LAN_GET_MULTI(&sc->is_multi, maddr, j); printf(" %d %s (muse==%d)\n", i+1, ether_sprintf(maddr), sc->is_multi.lan_mtable[j-1].muse); } } lan_build_mclist (mclist_buf, NET_SZ_MCLIST, &sc->is_multi); lan_set_attribute(sc->ehm.current_val, NET_MCLIST_NDX, mclist_buf); break; /* * Add a multicast */ case SIOCADDMULTI: /* * The EtherLink III family does not support any multicast * filtering. Either you receive ALL multicasts or you don't. It * does special case the broadcast address. * * If address is broadcast, indicate that another broadcast user is * around. If multicast, add it to the table. */ need_reset = 0; if (bcmp(ifr->ifr_addr.sa_data, etherbroadcastaddr, 6) == 0) { sc->is_broadcast++; } else { i = lan_add_multi(&sc->is_multi, (unsigned char *)ifr->ifr_addr.sa_data); switch (i) { case LAN_MULTI_CHANGED: /* * Add succeeded, reset the device only if there are no * other multicasts enabled. */ if (sc->is_multi.lan_nmulti == 1) need_reset++; break; case LAN_MULTI_NOT_CHANGED: break; case LAN_MULTI_FAILED: default: status = EINVAL; break; } } /* * If the device is running and multicasts/broadcasts haven't * already been enabled, enable them. */ if ((ifp->if_flags & IFF_RUNNING) && (need_reset)) el_reset_locked(sc, ifp, unit); if (sc->debug) { j = 0; printf("el%d: Dump of multicast table after ADD (%d entries)\n", unit, sc->is_multi.lan_nmulti); for (i=0; iis_multi.lan_nmulti; i++) { unsigned char *maddr; LAN_GET_MULTI(&sc->is_multi, maddr, j); printf(" %d %s (muse==%d)\n", i+1, ether_sprintf(maddr), sc->is_multi.lan_mtable[j-1].muse); } } lan_build_mclist (mclist_buf, NET_SZ_MCLIST, &sc->is_multi); lan_set_attribute(sc->ehm.current_val, NET_MCLIST_NDX, mclist_buf); break; /* * Read & Read/Zero counters. */ case SIOCRDCTRS: case SIOCRDZCTRS: ctr->ctr_ether = sc->ctrblk; /* Copy current counters */ ctr->ctr_type = CTR_ETHER; /* These are Ethernet Counters */ /* * Return time since last zeroed */ ctr->ctr_ether.est_seconds = (time.tv_sec - sc->ztime) > 0xfffe ? 0xffff : (time.tv_sec - sc->ztime); /* * Zero the counters if requested. */ if (cmd == SIOCRDZCTRS) { sc->ztime = time.tv_sec; bzero(&sc->ctrblk, sizeof(struct estat)); } break; /* * Bring UP the device */ case SIOCSIFADDR: ifp->if_flags |= IFF_UP; el_reset_locked(sc, ifp, unit); /* * Set counter cleared time (used by DECnet/netstat/clusters/etc.) */ if (sc->ztime == 0) sc->ztime = time.tv_sec; break; /* * Use currently set flags (only if running) */ case SIOCSIFFLAGS: if (ifp->if_flags & IFF_RUNNING) el_reset_locked(sc, ifp, unit); break; /* * Set IP MTU */ case SIOCSIPMTU: bcopy(ifr->ifr_data, (u_char *)&ifmtu, sizeof(u_short)); /* * Compare what is passed to the media's max. */ if (ifmtu > ETHERMTU || ifmtu < IP_MINMTU) status = EINVAL; else { ifp->if_mtu = ifmtu; lan_set_attribute(sc->ehm.current_val, NET_MTU_NDX, (void *)ifmtu); } break; /* * Set Media speed * * The EtherLink III only supports 10Mbps speed. */ case SIOCSMACSPEED: bcopy(ifr->ifr_data, (u_char *)&speed, sizeof(u_short)); /* * If LAN speed passed is anything other than 10 (0 means no * change), fail the request. */ if ((speed != 0) && (speed != 10)) { status = EINVAL; break; } break; /* * Reset -- reset the device */ case SIOCIFRESET: el_reset_locked(sc, ifp, unit); break; /* * Set Characteristics */ case SIOCIFSETCHAR: /* * Parameters to set are retrieved from 'ifc' */ need_reset = 0; /* Assume no reset is needed */ /* * If LAN speed passed is anything other than 10 (-1 means no * change), fail the request. */ if ((ifc->ifc_media_speed != -1) && (ifc->ifc_media_speed != 10)) { status = EINVAL; break; } /* * Check the media mode settings. If the request specifies both * autosense enable and an explicit media setting, we fail the * request. */ if ((ifc->ifc_auto_sense == LAN_AUTOSENSE_ENABLE) && (ifc->ifc_media_type != -1)) { status = EINVAL; break; } /* * Check to see if autosensing has changed. */ if (ifc->ifc_auto_sense != -1) { if ((ifc->ifc_auto_sense == LAN_AUTOSENSE_ENABLE) && (sc->lm_media_mode != LAN_MODE_AUTOSENSE)) { sc->lm_media_mode = LAN_MODE_AUTOSENSE; need_reset++; } else if ((ifc->ifc_auto_sense == LAN_AUTOSENSE_DISABLE) && (sc->lm_media_mode == LAN_MODE_AUTOSENSE)) { sc->lm_media_mode = sc->lm_media; /* Set to last known media */ need_reset++; } } /* * Check to see if the media has changed. */ if (ifc->ifc_media_type != -1) { /* * If the requested media value is out of range or not * supported by the EtherLink III, fail the request immediately. * * The EtherLink III supports the usual 802.3 medias. We don't * check the card's capability in the registers because it's * reported that it's less than useful to do so (it always says * it has all medias regardless of what it really has). * * If the user sets a media that the card doesn't have, the * interface probably won't work. */ switch (ifc->ifc_media_type) { case LAN_MEDIA_UTP: case LAN_MEDIA_AUI: case LAN_MEDIA_BNC: /* * Select the new mode. The autosense thread is out * there waiting regardless of the mode we're in. If * it becomes an issue, the thread may be killed when * switching to a different media mode. */ if (ifc->ifc_media_type != sc->lm_media) need_reset++; sc->lm_media_mode = sc->lm_media = ifc->ifc_media_type; break; default: status = EINVAL; break; } } /* * Reset the device to pick up the new mode (if runnig). */ if (need_reset && (ifp->if_flags & IFF_RUNNING)) el_reset_locked(sc, ifp, unit); break; /* * Default case */ default: status = EINVAL; } /* * If already unlocked and splx'ed, don't do it again. */ if (lock_on) { simple_unlock(&sc->el_softc_lock); splx(s); } return (status); } /* * el_intr() * Interface interrupt routine. * * Arguments: * unit The unit number of the interface * * Return Value: * None. */ static int el_intr(int unit) { register u_int s; volatile u_int status; register struct el_softc *sc = el_softc[unit]; register struct ifnet *ifp = &sc->is_if; /* * Check to see if card is still in socket. */ if (el_card_out(sc)) return (INTR_NOT_SERVICED); s = splimp(); simple_lock(&sc->el_softc_lock); /* * If polling, re-arm the next timeout. */ if (sc->polling_flag) timeout((void *)el_intr, (void *)unit, (1*hz)/el_pollint); /* * Read interrupt status. */ status = READ_STS(sc); /* * Process receive and transmit rings if we have an interrupt and * the card is still in the socket. */ if (((status & (S_RC|S_TC|S_AF)) == 0) || sc->cardout) { simple_unlock(&sc->el_softc_lock); splx(s); return INTR_NOT_SERVICED; } while ((status & (S_RC|S_TC|S_AF)) && (!el_card_out(sc))) { if (status & S_RC) el_rint(sc, ifp); if (status & S_TC) el_tint(sc, ifp); if (status & S_AF) el_error(sc, ifp); status = READ_STS(sc); } /* * Acknowledge the interrupt */ WRITE_CMD(sc, CMD_ACKINT+(S_IL)); /* * Check to see if there are any transmits pending and that there * is room on the device for more transmits. If so, transmit. */ if (ifp->if_snd.ifq_head) { el_start_locked(sc, ifp); } else { ifp->if_timer = 0; } /* * Release lock and resume IPL */ simple_unlock(&sc->el_softc_lock); splx(s); /* * Indicate that an interrupt was serviced */ return INTR_SERVICED; } /* * el_rint() * Interface receive interrupt completion routine. Calls ether_input * with completed buffers. * * Arguments: * sc The unit's softc structure * ifp The unit's ifnet structure * * Return Value: * None. */ #define RXLOOP ((16*1024)/64) static void el_rint(struct el_softc *sc, struct ifnet *ifp) { int len, i, count=RXLOOP; volatile short status; struct mbuf *m; unsigned char *dat; unsigned int in; struct ether_header eh; /* * Count the receive interrupt */ sc->rint++; /* * Read the RX status. */ status = READ_RXS(sc); /* * While there are complete packets, pull them out of the FIFO. */ while ((status > 0) && (count-- > 0)) { /* * First check for errors. */ len = status & RX_BYTES; if ((status & RX_ER) || (len > 1518) || (len < 60)) { /* * Process Errors */ if (status & RX_ER) { status &= RX_EM; if (sc->ctrblk.est_recvfail != 0xffff) sc->ctrblk.est_recvfail++; switch (status) { case RX_EOR: /* Overrun */ if (sc->ctrblk.est_overrun != 0xffff) sc->ctrblk.est_overrun++; if (sc->debug) printf("el%d: Overrun\n", ifp->if_unit); break; case RX_ERT: /* Runt */ case RX_EOS: /* Oversized Error */ sc->ctrblk.est_recvfail_bm |= 4; if (sc->debug) printf("el%d: Bad Sized packet\n", ifp->if_unit); break; case RX_ECR: /* CRC Error */ sc->ctrblk.est_recvfail_bm |= 1; if (sc->debug) printf("el%d: CRC\n", ifp->if_unit); break; case RX_EAL: /* Alignment Error */ default: sc->ctrblk.est_recvfail_bm |= 2; if (sc->debug) printf("el%d: Alignment\n", ifp->if_unit); break; } } else if ((sc->debug) && (len != 0)) printf("el%d: Received illegal size packet (%d)\n", ifp->if_unit, len); /* * If none of the above, then it's a size error. Just discard * the packet. */ } else { /* * Normal receive. * * If length is less than a small mbuf, copy all the data * into a small buffer, otherwise use a 2K cluster. * * Since we do all 32 bit reads from the device, we actually * make sure to add some padding to 'len' (hence the MHLEN-2-4) * so that we will not write past the end of the buffer. */ if (len <= MHLEN-2-4) { MGETHDR(m, M_DONTWAIT, MT_DATA); } else { MGETHDR(m, M_DONTWAIT, MT_DATA); if (m) { MCLGET2(m, M_DONTWAIT); if ((m->m_flags & M_EXT) == 0) { m_freem(m); m = (struct mbuf *)NULL; } } } /* * Copy packet data into the mbuf (receives are dword {32bit} * aligned). */ if (m != NULL) { /* * Set length and ifp pointer in mbuf. */ m->m_pkthdr.len = m->m_len = len - sizeof(struct ether_header); m->m_pkthdr.rcvif = ifp; m->m_data += 2; /* Align for IP */ /* * Get pointer to data, calculate number of longwords in * FIFO transfer. */ dat = mtod(m, unsigned char *); len = (len + 3) & ~3; /* * Since the EtherLink III performs no multicast filtering, * if promisc and allmulti are not set, check to make sure * any multicast addresses are actually wanted. */ if ((ifp->if_flags & (IFF_PROMISC|IFF_ALLMULTI)) == 0) { /* * Read the first two longwords to see if the packet * is sent to a multicast address. */ io_blockread(sc->data, (vm_offset_t)dat, 2UL*4UL, HANDLE_LONGWORD); len -= (2*4); dat += (2*4); /* * If the packet is multicast, and we don't have * the multicast enabled, don't do any more work. */ if (*mtod(m, unsigned char *) & 0x01) { /* * Since we want all broadcasts, make sure that * the multicast isn't a broadcast address. */ if (bcmp(mtod(m, unsigned char *), etherbroadcastaddr, 6) != 0) { int ix; /* * Try to find the multicast address. */ LAN_FIND_MULTI(&sc->is_multi, mtod(m, unsigned char *), ix, i); /* * If the multicast isn't found, scrap the packet. */ if ( (i != LAN_MULTI_FOUND) || (sc->is_multi.lan_mtable[ix].muse == 0) ) { m_freem(m); goto scrap; } } } } /* * Perform the transfer */ io_blockread(sc->data, (vm_offset_t)dat, (u_long)len, HANDLE_LONGWORD); /* * Make copy of ether_header for ether_input. */ eh = *(mtod(m, struct ether_header *)); eh.ether_type = ntohs((unsigned short)eh.ether_type); m->m_data += sizeof(struct ether_header); /* * Update counters. */ ADD_RECV_PACKET(ifp, sc, m->m_pkthdr.len); if (eh.ether_dhost[0] & 0x1) { ADD_RECV_MPACKET(ifp, sc, m->m_pkthdr.len); } /* * The following code should never actually execute. It's * agressively paranoid (must discard packets that are not * received fully). */ i = READ_RXS(sc); if (i &= 0x7ff) { if ((i & 0x400) == 0) { m_freem(m); goto scrap; } } /* * Give the buffer to ether_input */ ether_input(ifp, &eh, m); } } /* * Discard this packet and move onto the next. */ scrap: WRITE_CMD(sc, CMD_RXDTP); status = READ_RXS(sc); } if ((sc->debug) && (count <= 0)) printf("el%d: Receive in INFINITE loop %04X\n", ifp->if_unit, status); } /* * el_tint() * Interface transmit interrupt completion routine. * * Arguments: * sc The unit's softc structure * ifp The unit's ifnet structure * * Return Value: * None. */ #define TXLOOP ((16*1024)/64) static void el_tint(struct el_softc *sc, struct ifnet *ifp) { int count=TXLOOP; volatile unsigned int status; /* * Count the transmit interrupt */ sc->tint++; /* * Read transmit status and count all sigificant events looking out * for events that require transmit reset. */ status = READ_TXS(sc); while ((status & (TX_CM<<8)) && (count-- > 0)) { /* * The 3C5x9 does keep enough statistics internally to satisfy * the DECnet counter requirements. However, it requires more * work than this already overloaded driver should do. Therefore, * we'll only maintain a few error counters. */ /* * If jabber and/or underrun is set, this is a condition that * require reset of the transmiter. * * XYZ be sure to test this with all medias. */ if (status & ((TX_JB|TX_UN)<<8)) { ifp->if_oerrors++; /* At least one */ sc->ctrblk.est_sendfail++; sc->txreset++; WRITE_TXS(sc, status); /* clear current */ WRITE_CMD(sc, CMD_TXRESET); DELAY(10); /* Wait 10 usec */ WRITE_CMD(sc, CMD_TXENA); } else if (status & (TX_MC<<8)) { /* * If max collisions, this means that the send failed. */ ifp->if_oerrors++; ifp->if_collisions+=2; if (sc->ctrblk.est_sendfail != 0xffff) sc->ctrblk.est_sendfail++; sc->ctrblk.est_sendfail_bm |= 1;/* Indicate excessive collisions */ WRITE_TXS(sc, status); WRITE_CMD(sc, CMD_TXENA); } else { /* * Write the status register to get the next value (if any). */ WRITE_TXS(sc, status); } status = READ_TXS(sc); } sc->txfree = READ_TXF(sc); if (sc->debug) if (count <= 0) printf("el%d: Transmit in INFINITE loop %04X\n", ifp->if_unit, status); /* * Clear output active flag so that other transmits may be queued. */ ifp->if_flags &= ~IFF_OACTIVE; } /* * el_error() * Interface adapter error routine. * * Arguments: * sc The unit's softc structure * ifp The unit's ifnet structure * * Return Value: * None. */ static void el_error(struct el_softc *sc, struct ifnet *ifp) { int i; WRITE_CMD(sc, CMD_WINDOW4); i = READ_FDP(sc); /* Read FIFO Diagnostic Port */ printf("el%d: Adapter Failure - %04X\n", ifp->if_unit, i); el_reset_locked(sc, ifp, ifp->if_unit); } /* * el_shutdown * Shutdown the controller. * * Arguments * sc The unit's softc pointer * * Return Value: * None. */ static void el_shutdown(struct el_softc *sc) { WRITE_CMD(sc, CMD_RESET); /* Reset the Device */ DELAY(1000); /* Wait for a msec to make sure */ } /* * el_card_remove * For the removal of the PCMCIA card (3C589). Must disable the device * from running incase we're being called from ctlr_unattach. * 'unattach' and card removal are similar events. All resources * acquired by this unit must be released. * * Arguments: * socket_number * event_number * sc * * Return Value: * None */ /*ARGSUSED*/ static void el_card_remove(int socket_number, int event_number, struct el_softc *sc) { struct ifnet *ifp = &sc->is_if; int s; /* * Unregister the removal event */ pcmcia_unregister_event_callback(socket_number, event_number, (caddr_t)el_card_remove); s = splimp(); simple_lock(&sc->el_softc_lock); /* * If polling, untimeout the interrupt service routine. */ if (el_polling && sc->polling_flag) { untimeout((void *)el_intr, (void *)ifp->if_unit); sc->polling_flag = 0; } /* * Un-register the interrupt service routine. */ if (sc->hid) { handler_disable(sc->hid); handler_del(sc->hid); sc->hid = NULL; } /* * Kill transmit timeout. */ ifp->if_timer = 0; /* * Indicate card removal. */ sc->cardout++; simple_unlock(&sc->el_softc_lock); splx(s); } /* * el_isa_reset_all * Write out the string of 255 bytes to the ioaddr in the range * 0x1?0 as specified in the Technical Reference. This puts all * ISA 3C509 series adapters in a state where they're waiting to * be identified. Unfortunately, resets clear all configuration, * and the process has to be repeated. Then WACK all devices * with global reset to make sure all adapters are in a quiet state. * * Arguments: * base request base address of this adapter * tag pointer to store this adapter's tag * ctlr pointer to controller struct * * Return Value: * 0 Success * ~0 Failure */ /*ARGSUSED*/ static int el_isa_reset_all(io_handle_t base, int *tag, struct controller *ctlr) { io_handle_t idport; int i, x; struct resource_blk rb={0}; struct io_mem_req_t ioadd={0}; /* * Try to reserve a location in the ioaddr range 0x1X0 to send the * id sequence to. */ rb.resource_type = RES_PORT; rb.bus_ctlr_p = (caddr_t)ctlr; rb.caller_type = RES_CONTROLLER_TYPE; rb.busptr = ctlr->bus_hd; rb.flags = RES_SHARABLE; rb.req_param = (caddr_t)&ioadd; ioadd.baseaddr = 0x100; ioadd.numbytes = 0x10; i = 0; while ( (request_resource(&rb) != RES_SUCCESS) && (i<0x10)) { ioadd.baseaddr += 0x10; i++; } if (i >= 0x10) { printf("el.mod: All IO space between 0x100 and 0x1F0 is reserved.\n"); printf("el.mod: 3C509 cannot operate without these addresses.\n"); return(1); } /* * Compute open spot in the ID range. */ idport = busphys_to_iohandle(ioadd.baseaddr, BUS_IO, ctlr); /* * Write initial 2 zero bytes to get all adapters listening. */ WRITE_BUS_D8(idport, 0x0); mb(); WRITE_BUS_D8(idport, 0x0); mb(); /* * Write out the identification sequence. */ x=0xff; for (i=0; i<255; i++) { WRITE_BUS_D8(idport, x); mb(); x <<= 1; if (x & 0x100) x ^= 0xcf; } /* * Wack all devices, then wait a msec... */ WRITE_BUS_D8(idport, 0xc0); mb(); DELAY(1000); if (release_resource(rb.resource_handle) != RES_SUCCESS) { printf("el.mod: io resource return failure\n"); } return(0); } /* * el_isa_activate * Write out the string of 255 bytes to the ioaddr in the range * 0x1?0 as specified in the Technical Reference. This puts all * ISA 3C509 series adapters in a state where they're waiting to * be identified. Unfortunately, resets clear all configuration, * and the process has to be repeated. Read the comments if you * wish to learn more about this process. It must have been the * start of Plug and Play... * * Arguments: * base request base address of this adapter * tag pointer to store this adapter's tag * ctlr pointer to controller struct * * Return Value: * 0 Success * ~0 Failure */ static int el_isa_activate(io_handle_t base, int *tag, struct controller *ctlr) { io_handle_t idport; int i, x; struct resource_blk rb={0}; struct io_mem_req_t ioadd={0}; /* * Try to reserve a location in the ioaddr range 0x1X0 to send the * id sequence to. */ rb.resource_type = RES_PORT; rb.bus_ctlr_p = (caddr_t)ctlr; rb.caller_type = RES_CONTROLLER_TYPE; rb.busptr = ctlr->bus_hd; rb.flags = RES_SHARABLE; rb.req_param = (caddr_t)&ioadd; ioadd.baseaddr = 0x100; ioadd.numbytes = 0x10; i = 0; while ( (request_resource(&rb) != RES_SUCCESS) && (i<0x10)) { ioadd.baseaddr += 0x10; i++; } if (i >= 0x10) { printf("el.mod: All IO space between 0x100 and 0x1F0 is reserved.\n"); printf("el.mod: 3C509 cannot operate without these addresses.\n"); return(1); } /* * Find open spot in the ID range. */ idport = busphys_to_iohandle(ioadd.baseaddr, BUS_IO, ctlr); /* * Write initial 2 zero bytes to get all adapters listening. */ WRITE_BUS_D8(idport, 0x0); mb(); WRITE_BUS_D8(idport, 0x0); mb(); /* * Write out the identification sequence. * * The identification sequence is described by the following x86 * machine language "algorithm"... For the unwary, I found in my * manual that the cx and al registers are both 8bits (that's why * carry gets set on the first shift). * * mov cx, 0FFh * mov dx, IDport * mov al, 0FFh * @@1: out dx, al * shl al * jnc @@2 * xor al, 0CFh * @@2: loop @@1 * */ x=0xff; for (i=0; i<255; i++) { WRITE_BUS_D8(idport, x); mb(); x <<= 1; if (x & 0x100) x ^= 0xcf; } /* * If first time probing, reset all adapters to tag 0. Otherwise, * turn off adapters that have already been "tagged"... */ if (el_isa_tag == 0) { WRITE_BUS_D8(idport, 0xd0); } else { WRITE_BUS_D8(idport, 0xd8); /* Only 'untagged' adapters will be left */ } mb(); /* * Make sure that there's an adapter out there. If not, return failure. */ if (el_isa_read_offset(idport, 7) != 0x6D50) return(1); /* * Read the hardware address. Since the hardware address is unique, only * one adapter should survive this process alive. */ el_isa_read_offset(idport, 0); el_isa_read_offset(idport, 1); el_isa_read_offset(idport, 2); /* * If this adapter hasn't been "tagged" before, tag it now and save * the tag in the softc structure. */ if (*tag == 0) *tag = ++el_isa_tag; WRITE_BUS_D8(idport, 0xd0 + *tag); mb(); /* * Activate the adapter, and let 'er rip. */ WRITE_BUS_D8(idport, 0xe0 + (((base-0x200)/0x10)&0x1f)); mb(); /* * Return the io resource (won't need it anymore) */ if (release_resource(rb.resource_handle) != RES_SUCCESS) { printf("el.mod: io resource return failure\n"); } return(0); } /* * el_isa_read_offset * A utility routine to the el_isa_activate function. After the * adapter is put into the ISA contention state, we read 16 bits from * the IDport bit 0 which come from the EEPROM. This is actually * a very clever way to uniquely identify multiple units on a ISA * bus. * * Arguments: * idport ioaddr of the idport * offset address of EEPROM to read */ static unsigned short el_isa_read_offset(io_handle_t idport, int offset) { int i; unsigned short v=0, b; /* * Program the adapter(s) to send the requested location. */ WRITE_BUS_D8(idport, 0x80+(offset&0x3f)); mb(); DELAY(1000); /* wait a msec */ /* * Read the 16 bits of the EEPROM location. */ for (i=0; i<16; i++) { b = READ_BUS_D8(idport); v <<= 1; v |= (b & 0x1); } return(v); } /* * el_wait * Wait for the current command to complete. * * Arguments: * sc softc structure * * Return Value: * none */ static void el_wait(struct el_softc *sc) { volatile int status; int i=0; /* * Exit if card is gone. */ if (el_card_out(sc)) return; status = READ_STS(sc); while ((status & S_IP) && (!el_card_out(sc))) { DELAY(100); status = READ_STS(sc); if (i++ > 100) { if (sc->debug) printf("el%d: Exiting delay loop while command isn't complete\n", sc->is_if.if_unit); break; } } } unsigned char el_junk_msg[] = { 0xaa, 0x00, 0x04, 0xff, 0xff, 0xff, 0, 0, 0, 0, 0, 0, 0x60, 0x06, 't', 'h', 'i', 's', ' ', 'i', 's', ' ', 'a', ' ', 'j', 'u', 'n', 'k', ' ', 'a', 'u', 't', 'o', 's', 'e', 'n', 's', 'e', ' ', 'm', 'e', 's', 's', 'a', 'g', 'e', '.' }; #define EL_JUNK_SIZE 46 /* * el_autosense_thread * Perform the autosense process. The logic for this process is mostly * local to this routine. Runs in the "background" as to not interfere * with nominal operation of the driver. * * There is one autosense thread per controller. * * Arguments: * sc the softc structure * * Return Value: * none -- routine never returns. */ #define EL_AUTOSENSE_PASSES 3*10 static void el_autosense_thread(struct el_softc *sc) { struct ifnet *ifp = &sc->is_if; unsigned long prev_tint, prev_tmo, prev_err; struct mbuf *m; int good_xmits, wait, s, i, link_beat, passes; unsigned long wait_flag=0; /* * sc->lm_media_mode contains how the driver is configured (via ioctl) to * handle media settings. The default mode is autosense. * The possible values are; * * LAN_MEDIA_UTP Any Unshielded Twisted Pair * LAN_MEDIA_BNC Thinwire (10Base2) * LAN_MEDIA_STP Any Shielded Twisted Pair * LAN_MEDIA_FIBER Any Fiber * LAN_MEDIA_AUI Thickwire (10Base5) * LAN_MODE_AUTOSENSE Automatic * * sc->lm_media_state contains the autosense state (if autosense is * selected in media_mode). The possible values are; * * LAN_MEDIA_STATE_SENSING 1 Sensing media * LAN_MEDIA_STATE_DETERMINED 0 Determined what media is * * sc->lm_media contains the currently set media. Contains all the * non-zero values that are possible in lm_media_mode. This value * is not valid in autosense mode unless media_state is * LAN_MEDIA_STATE_DETERMINED. */ while(1) { /* * Wait for someone to tell us to proceed with the autosense * test. */ assert_wait((vm_offset_t)&sc->autosense_flag, TRUE); thread_block(); /* * Test initially for the termination flag. */ while (thread_should_halt(sc->autosense_thread)) { printf("el%d: Autosense thread exiting\n", ifp->if_unit); thread_halt_self(); } /* * Start up stats so we can account for loss of carrier during * transmit. */ s = splimp(); simple_lock(&sc->el_softc_lock); WRITE_CMD(sc, CMD_STATSENA); /* Enable stats */ simple_unlock(&sc->el_softc_lock); splx(s); /* * Enter loop waiting transmitting a packet and seeing if it succeeds. * A packet has to go out twice successfully for media selection * to succeed. * * It's fair to warn that this algorithm will probably not work in * all cases. */ good_xmits = passes = 0; sc->lm_media_state = LAN_MEDIA_STATE_SENSING; while (good_xmits < 5) { /* * Check to see if we should terminate. */ while (thread_should_halt(sc->autosense_thread)) { printf("el%d: Autosense thread exiting\n", ifp->if_unit); s = splimp(); simple_lock(&sc->el_softc_lock); WRITE_CMD(sc, CMD_STATSDIS); /* Disable stats */ simple_unlock(&sc->el_softc_lock); splx(s); thread_halt_self(); } /* * See if somebody has set a media while we're determining * media type... */ if (sc->lm_media_mode != LAN_MODE_AUTOSENSE) break; /* * Save counters prior to transmit */ prev_tint= sc->tint; prev_err = ifp->if_oerrors; prev_tmo = sc->xmit_tmo; /* * Build junk transmit to send out. */ MGETHDR(m, M_WAIT, MT_DATA); if ((passes++ > EL_AUTOSENSE_PASSES) || (m == NULL)) { if (m) { m_freem(m); printf("el%d: Autosense thread cannot determine media\n", ifp->if_unit); printf("el%d: Use lan_config to configure if necessary\n", ifp->if_unit); } else { printf("el%d: Autosense thread cannot get xmit buffer\n", ifp->if_unit); } /* * Use default from the ROM. */ switch (sc->eeprom.addrconf & 0xc) { case ACR_10B5: if (sc->lm_media_mode == LAN_MODE_AUTOSENSE) sc->lm_media = LAN_MEDIA_AUI; break; case ACR_10B2: if (sc->lm_media_mode == LAN_MODE_AUTOSENSE) sc->lm_media = LAN_MEDIA_BNC; break; case ACR_10BT: default: if (sc->lm_media_mode == LAN_MODE_AUTOSENSE) sc->lm_media = LAN_MEDIA_UTP; break; } printf("el%d: Used %s setting from eeprom\n", ifp->if_unit, lan_media_strings_10[sc->lm_media]); good_xmits = 100; /* Exit loop and go to sleep */ /* * Set the media setting in hardware... */ el_reset(ifp->if_unit); break; } else { /* * Load the junk message into the mbuf. */ bcopy(el_junk_msg, mtod(m, caddr_t), EL_JUNK_SIZE); bcopy(sc->is_addr, mtod(m, caddr_t), 6); bcopy(sc->is_addr, mtod(m, caddr_t)+6, 6); m->m_pkthdr.len = m->m_len = EL_JUNK_SIZE; /* * Transmit the packet */ s = splimp(); simple_lock(&sc->el_softc_lock); IF_ENQUEUE(&ifp->if_snd, m); el_start_locked(sc, ifp); simple_unlock(&sc->el_softc_lock); splx(s); /* * While the transmit hasn't made it out, a timeout * hasn't occured, and we haven't waited 4 seconds. */ wait = 0; while ((prev_tint == sc->tint) && (prev_tmo == sc->xmit_tmo) && (wait++ < 4)) { assert_wait((vm_offset_t)&wait_flag, TRUE); thread_set_timeout(1*hz); thread_block(); } /* * Check carrier for transmits. All medias will give carrier * errors if there's no cable plugged in or transceivers * present. For UTP, we just keep track of link beat for * our interest (there are 10BaseT repeaters that do not * provide link beat). */ link_beat = 0; switch (sc->lm_media) { case LAN_MEDIA_UTP: s = splimp(); /* Read the Media Type */ simple_lock(&sc->el_softc_lock);/* and status register. */ WRITE_CMD(sc, CMD_WINDOW4); /* Return to window 1 */ i = READ_MD(sc); /* when finished. */ if ((i & MD_VLB) != 0) /* Valid Link Beat? */ link_beat=1; /* Yes, indicate so */ WRITE_CMD(sc, CMD_WINDOW1); simple_unlock(&sc->el_softc_lock); splx(s); /* Fall through to carrier */ case LAN_MEDIA_BNC: case LAN_MEDIA_AUI: s = splimp(); simple_lock(&sc->el_softc_lock); WRITE_CMD(sc, CMD_WINDOW6); WRITE_CMD(sc, CMD_STATSDIS); i = READ_BUS_D8(sc->basereg); if (i != 0) { /* Fail if carrier lost */ wait = 100; if (sc->debug) printf("el%d: autosense: %s carrier loss\n", ifp->if_unit, lan_media_strings_10[sc->lm_media]); } WRITE_CMD(sc, CMD_STATSENA); WRITE_CMD(sc, CMD_WINDOW1); simple_unlock(&sc->el_softc_lock); splx(s); break; default: break; } /* * See if traffic successfully went out. */ if ((prev_err == ifp->if_oerrors) && (prev_tmo == sc->xmit_tmo) && (wait < 5)) { /* * Packet is assumed to have made it. */ good_xmits++; if (sc->debug) printf("el%d: autosense: %s packet sent OK (%d)\n", ifp->if_unit, lan_media_strings_10[sc->lm_media], good_xmits); } else { /* * Packet is assumed to not have made it. * Select a new media, reset and try again. */ good_xmits = 0; /* * Print debugging info (if requested). */ if (sc->debug) { if (prev_err != ifp->if_oerrors) printf("el%d: autosense: %s transmit error\n", ifp->if_unit, lan_media_strings_10[sc->lm_media]); if (prev_tmo != sc->xmit_tmo) printf("el%d: autosense: %s driver transmit timeout\n", ifp->if_unit, lan_media_strings_10[sc->lm_media]); if ((wait >= 5) && (wait < 100)) printf("el%d: autosense: %s transmit timeout\n", ifp->if_unit, lan_media_strings_10[sc->lm_media]); } /* * Set a new media. */ switch (sc->lm_media) { case LAN_MEDIA_AUI: if (sc->lm_media_mode == LAN_MODE_AUTOSENSE) sc->lm_media = LAN_MEDIA_UTP; break; case LAN_MEDIA_BNC: if (sc->lm_media_mode == LAN_MODE_AUTOSENSE) sc->lm_media = LAN_MEDIA_AUI; break; case LAN_MEDIA_UTP: default: if (sc->lm_media_mode == LAN_MODE_AUTOSENSE) sc->lm_media = LAN_MEDIA_BNC; break; } /* * Reset the hardware (note: we're still not running * at raised IPL or locked). */ el_reset(ifp->if_unit); } } } if (sc->debug) { if ((sc->lm_media == LAN_MEDIA_UTP) && !link_beat && (passes <= EL_AUTOSENSE_PASSES)) printf("el%d: No Link Beat signal\n", ifp->if_unit); } sc->lm_media_state = LAN_MEDIA_STATE_DETERMINED; printf("el%d: Autosense selected %s media\n", ifp->if_unit, lan_media_strings_10[sc->lm_media]); s = splimp(); simple_lock(&sc->el_softc_lock); WRITE_CMD(sc, CMD_STATSDIS); /* Disable stats */ simple_unlock(&sc->el_softc_lock); splx(s); } } /* * el_card_out * A pcmcia specific routine which checks with the pcmcia hardware * to see if the card is actually still in the socket. * * It is an unfortunate penalty that must be paid for pcmcia drivers * since the driver can never predict when a card will be removed * from the socket (can also occur while interrupt processing is * occuring). * * Arguments: * sc the softc structure * * Return Value: * 1 = card out * 0 = card in */ static int el_card_out(struct el_softc * sc) { unsigned int card_data = 0; unsigned int change_data = 0; /* * Indicate card is still there if we are not a plugged into a * pcmcia adapter. */ if (!sc->ispcmcia) return 0; /* * Get the status from the pcmcia hardware. */ GetStatus(sc->cinfop->socket_vnum, 0, &card_data, &change_data); if ((card_data & PC_CARD_PRESENT) == 0) { /* * The card is no longer present. We may be here because the shared * interrupt disaptch code called us first instead of the card * management interrupt handler routine. Since the card is gone, we * just dismiss the interrupt */ sc->cardout++; return (1); } /* * The card is still inserted. */ return (0); }