/* ** File: eth.c Version 1.00, Jan. 14, 1997 ** ** Author: Giovanni Falzoni ** ** This file contains the ethernet device driver main task. ** It has to be integrated with the board specific drivers. ** It is a rewriting of Minix 2.0.0 ethernet driver (dp8390.c) ** to remove bord specific code. It should operate (I hope) ** with any board driver. ** ** The valid messages and their parameters are: ** ** m_type DL_PORT DL_PROC DL_COUNT DL_MODE DL_ADDR ** +------------+---------+---------+--------+-------+---------+ ** | HARD_INT | | | | | | ** +------------+---------+---------+--------+-------+---------+ ** | SYN_ALARM | | | | | | ** +------------+---------+---------+--------+-------+---------+ ** | HARD_STOP | | | | | | ** +------------+---------+---------+--------+-------+---------+ ** | FKEY_PRESSED | | | | | (99) ** +------------+---------+---------+--------+-------+---------+ ** | DL_WRITE | port nr | proc nr | count | mode | address | (3) ** +------------+---------+---------+--------+-------+---------+ ** | DL_WRITEV | port nr | proc nr | count | mode | address | (4) ** +------------+---------+---------+--------+-------+---------+ ** | DL_READ | port nr | proc nr | count | | address | (5) ** +------------+---------+---------+--------+-------+---------+ ** | DL_READV | port nr | proc nr | count | | address | (6) ** +------------+---------+---------+--------+-------+---------+ ** | DL_INIT | port nr | proc nr | | mode | address | (7) ** +------------+---------+---------+--------+-------+---------+ ** | DL_STOP | port_nr | | | | | (8) ** +------------+---------+---------+--------+-------+---------+ ** | DL_GETSTAT | port nr | proc nr | | | address | (9) ** +------------+---------+---------+--------+-------+---------+ ** ** The messages sent are: ** ** m-type DL_PORT DL_PROC DL_COUNT DL_STAT DL_CLCK ** +------------+---------+---------+--------+---------+---------+ ** |DL_TASK_REPL| port nr | proc nr |rd-count| err|stat| clock | (21) ** +------------+---------+---------+--------+---------+---------+ ** ** m_type m3_i1 m3_i2 m3_ca1 ** +------------+---------+---------+---------------+ ** |DL_INIT_REPL| port nr |last port| ethernet addr | (20) ** +------------+---------+---------+---------------+ ** ** $Id: dp.c,v 1.13 2006/03/25 04:49:03 beng Exp $ */ #include "drivers.h" #include #include #include #include #include "dp.h" /* ** Local data */ extern int errno; static dpeth_t de_table[DE_PORT_NR]; static char *progname; typedef struct dp_conf { /* Configuration description structure */ port_t dpc_port; int dpc_irq; phys_bytes dpc_mem; char *dpc_envvar; } dp_conf_t; /* Device default configuration */ static dp_conf_t dp_conf[DE_PORT_NR] = { /* I/O port, IRQ, Buff addr, Env. var, Buf. selector */ { 0x300, 5, 0xC8000, "DPETH0", }, { 0x280, 10, 0xCC000, "DPETH1", }, }; static const char CopyErrMsg[] = "unable to read/write user data"; static const char PortErrMsg[] = "illegal port"; static const char RecvErrMsg[] = "receive failed"; static const char SendErrMsg[] = "send failed"; static const char SizeErrMsg[] = "illegal packet size"; static const char TypeErrMsg[] = "illegal message type"; static const char DevName[] = "eth#?"; static void do_getname(message *mp); /* ** Name: void reply(dpeth_t *dep, int err) ** Function: Fills a DL_TASK_REPLY reply message and sends it. */ static void reply(dpeth_t * dep, int err) { message reply; int status = FALSE; if (dep->de_flags & DEF_ACK_SEND) status |= DL_PACK_SEND; if (dep->de_flags & DEF_ACK_RECV) status |= DL_PACK_RECV; reply.m_type = DL_TASK_REPLY; reply.DL_PORT = dep - de_table; reply.DL_PROC = dep->de_client; reply.DL_STAT = status /* | ((u32_t) err << 16) */; reply.DL_COUNT = dep->de_read_s; getuptime(&reply.DL_CLCK); DEBUG(printf("\t reply %d (%ld)\n", reply.m_type, reply.DL_STAT)); if ((status = send(dep->de_client, &reply)) == OK) { dep->de_read_s = 0; dep->de_flags &= NOT(DEF_ACK_SEND | DEF_ACK_RECV); } else if (status != ELOCKED || err == OK) panic(dep->de_name, SendErrMsg, status); return; } /* ** Name: void dp_confaddr(dpeth_t *dep) ** Function: Checks environment for a User defined ethernet address. */ static void dp_confaddr(dpeth_t * dep) { static char ea_fmt[] = "x:x:x:x:x:x"; char ea_key[16]; int ix; long val; strcpy(ea_key, dp_conf[dep - de_table].dpc_envvar); strcat(ea_key, "_EA"); for (ix = 0; ix < SA_ADDR_LEN; ix++) { val = dep->de_address.ea_addr[ix]; if (env_parse(ea_key, ea_fmt, ix, &val, 0x00L, 0xFFL) != EP_SET) break; dep->de_address.ea_addr[ix] = val; } if (ix != 0 && ix != SA_ADDR_LEN) /* It's all or nothing, force a panic */ env_parse(ea_key, "?", 0, &val, 0L, 0L); return; } /* ** Name: void update_conf(dpeth_t *dep, dp_conf_t *dcp) ** Function: Gets the default settings from 'dp_conf' table and ** modifies them from the environment. */ static void update_conf(dpeth_t * dep, dp_conf_t * dcp) { static char dpc_fmt[] = "x:d:x"; long val; dep->de_mode = DEM_SINK; val = dcp->dpc_port; /* Get I/O port address */ switch (env_parse(dcp->dpc_envvar, dpc_fmt, 0, &val, 0x000L, 0x3FFL)) { case EP_OFF: dep->de_mode = DEM_DISABLED; break; case EP_ON: case EP_SET: dep->de_mode = DEM_ENABLED; break; } dep->de_base_port = val; val = dcp->dpc_irq | DEI_DEFAULT; /* Get Interrupt line (IRQ) */ env_parse(dcp->dpc_envvar, dpc_fmt, 1, &val, 0L, (long) NR_IRQ_VECTORS - 1); dep->de_irq = val; val = dcp->dpc_mem; /* Get shared memory address */ env_parse(dcp->dpc_envvar, dpc_fmt, 2, &val, 0L, LONG_MAX); dep->de_linmem = val; return; } /* ** Name: void do_dump(message *mp) ** Function: Displays statistics on screen (SFx key from console) */ static void do_dump(message *mp) { dpeth_t *dep; int port; printf("\n\n"); for (port = 0, dep = de_table; port < DE_PORT_NR; port += 1, dep += 1) { if (dep->de_mode == DEM_DISABLED) continue; printf("%s statistics:\t\t", dep->de_name); /* Network interface status */ printf("Status: 0x%04x (%d)\n\n", dep->de_flags, dep->de_int_pending); (*dep->de_dumpstatsf) (dep); /* Transmitted/received bytes */ printf("Tx bytes:%10ld\t", dep->bytes_Tx); printf("Rx bytes:%10ld\n", dep->bytes_Rx); /* Transmitted/received packets */ printf("Tx OK: %8ld\t", dep->de_stat.ets_packetT); printf("Rx OK: %8ld\n", dep->de_stat.ets_packetR); /* Transmit/receive errors */ printf("Tx Err: %8ld\t", dep->de_stat.ets_sendErr); printf("Rx Err: %8ld\n", dep->de_stat.ets_recvErr); /* Transmit unnerruns/receive overrruns */ printf("Tx Und: %8ld\t", dep->de_stat.ets_fifoUnder); printf("Rx Ovr: %8ld\n", dep->de_stat.ets_fifoOver); /* Transmit collisions/receive CRC errors */ printf("Tx Coll: %8ld\t", dep->de_stat.ets_collision); printf("Rx CRC: %8ld\n", dep->de_stat.ets_CRCerr); } return; } /* ** Name: void get_userdata(int user_proc, vir_bytes user_addr, int count, void *loc_addr) ** Function: Copies data from user area. */ static void get_userdata(int user_proc, vir_bytes user_addr, int count, void *loc_addr) { int rc; vir_bytes len; len = (count > IOVEC_NR ? IOVEC_NR : count) * sizeof(iovec_t); if ((rc = sys_datacopy(user_proc, user_addr, SELF, (vir_bytes)loc_addr, len)) != OK) panic(DevName, CopyErrMsg, rc); return; } /* ** Name: void do_first_init(dpeth_t *dep, dp_conf_t *dcp); ** Function: Init action to setup task */ static void do_first_init(dpeth_t *dep, dp_conf_t *dcp) { if (dep->de_linmem != 0) { dep->de_memsegm = BIOS_SEG; /* phys2seg(&dep->de_memsegm, &dep->de_memoffs, dep->de_linmem); */ } else dep->de_linmem = 0xFFFF0000; /* Make sure statisics are cleared */ memset((void *) &(dep->de_stat), 0, sizeof(eth_stat_t)); /* Device specific initialization */ (*dep->de_initf) (dep); /* Set the interrupt handler policy. Request interrupts not to be reenabled * automatically. Return the IRQ line number when an interrupt occurs. */ dep->de_hook = dep->de_irq; sys_irqsetpolicy(dep->de_irq, 0 /*IRQ_REENABLE*/, &dep->de_hook); dep->de_int_pending = FALSE; sys_irqenable(&dep->de_hook); return; } /* ** Name: void do_init(message *mp) ** Function: Checks for hardware presence. ** Provides initialization of hardware and data structures */ static void do_init(message * mp) { int port; dpeth_t *dep; dp_conf_t *dcp; message reply_mess; port = mp->DL_PORT; if (port >= 0 && port < DE_PORT_NR) { dep = &de_table[port]; dcp = &dp_conf[port]; strcpy(dep->de_name, DevName); dep->de_name[4] = '0' + port; if (dep->de_mode == DEM_DISABLED) { update_conf(dep, dcp); /* First time thru */ if (dep->de_mode == DEM_ENABLED && !el1_probe(dep) && /* Probe for 3c501 */ !wdeth_probe(dep) && /* Probe for WD80x3 */ !ne_probe(dep) && /* Probe for NEx000 */ !el2_probe(dep) && /* Probe for 3c503 */ !el3_probe(dep)) { /* Probe for 3c509 */ printf("%s: warning no ethernet card found at 0x%04X\n", dep->de_name, dep->de_base_port); dep->de_mode = DEM_DISABLED; } } /* 'de_mode' may change if probe routines fail, test again */ switch (dep->de_mode) { case DEM_DISABLED: /* Device is configured OFF or hardware probe failed */ port = ENXIO; break; case DEM_ENABLED: /* Device is present and probed */ if (dep->de_flags == DEF_EMPTY) { /* These actions only the first time */ do_first_init(dep, dcp); dep->de_flags |= DEF_ENABLED; } dep->de_flags &= NOT(DEF_PROMISC | DEF_MULTI | DEF_BROAD); if (mp->DL_MODE & DL_PROMISC_REQ) dep->de_flags |= DEF_PROMISC | DEF_MULTI | DEF_BROAD; if (mp->DL_MODE & DL_MULTI_REQ) dep->de_flags |= DEF_MULTI; if (mp->DL_MODE & DL_BROAD_REQ) dep->de_flags |= DEF_BROAD; (*dep->de_flagsf) (dep); dep->de_client = mp->m_source; break; case DEM_SINK: /* Device not present (sink mode) */ memset(dep->de_address.ea_addr, 0, sizeof(ether_addr_t)); dp_confaddr(dep); /* Station address from env. */ break; default: break; } *(ether_addr_t *) reply_mess.m3_ca1 = dep->de_address; } else /* Port number is out of range */ port = ENXIO; reply_mess.m_type = DL_INIT_REPLY; reply_mess.m3_i1 = port; reply_mess.m3_i2 = DE_PORT_NR; DEBUG(printf("\t reply %d\n", reply_mess.m_type)); if (send(mp->m_source, &reply_mess) != OK) /* Can't send */ panic(dep->de_name, SendErrMsg, mp->m_source); return; } /* ** Name: void dp_next_iovec(iovec_dat_t *iovp) ** Function: Retrieves data from next iovec element. */ PUBLIC void dp_next_iovec(iovec_dat_t * iovp) { iovp->iod_iovec_s -= IOVEC_NR; iovp->iod_iovec_addr += IOVEC_NR * sizeof(iovec_t); get_userdata(iovp->iod_proc_nr, iovp->iod_iovec_addr, iovp->iod_iovec_s, iovp->iod_iovec); return; } /* ** Name: int calc_iovec_size(iovec_dat_t *iovp) ** Function: Compute the size of a request. */ static int calc_iovec_size(iovec_dat_t * iovp) { int size, ix; size = ix = 0; do { size += iovp->iod_iovec[ix].iov_size; if (++ix >= IOVEC_NR) { dp_next_iovec(iovp); ix = 0; } /* Till all vectors added */ } while (ix < iovp->iod_iovec_s); return size; } /* ** Name: void do_vwrite(message *mp, int vectored) ** Function: */ static void do_vwrite(message * mp, int vectored) { int port, size; dpeth_t *dep; port = mp->DL_PORT; if (port < 0 || port >= DE_PORT_NR) /* Check for illegal port number */ panic(dep->de_name, PortErrMsg, port); dep = &de_table[port]; dep->de_client = mp->DL_PROC; if (dep->de_mode == DEM_ENABLED) { if (dep->de_flags & DEF_SENDING) /* Is sending in progress? */ panic(dep->de_name, "send already in progress ", NO_NUM); dep->de_write_iovec.iod_proc_nr = mp->DL_PROC; if (vectored) { get_userdata(mp->DL_PROC, (vir_bytes) mp->DL_ADDR, mp->DL_COUNT, dep->de_write_iovec.iod_iovec); dep->de_write_iovec.iod_iovec_s = mp->DL_COUNT; dep->de_write_iovec.iod_iovec_addr = (vir_bytes) mp->DL_ADDR; size = calc_iovec_size(&dep->de_write_iovec); } else { dep->de_write_iovec.iod_iovec[0].iov_addr = (vir_bytes) mp->DL_ADDR; dep->de_write_iovec.iod_iovec[0].iov_size = size = mp->DL_COUNT; dep->de_write_iovec.iod_iovec_s = 1; dep->de_write_iovec.iod_iovec_addr = 0; } if (size < ETH_MIN_PACK_SIZE || size > ETH_MAX_PACK_SIZE) panic(dep->de_name, SizeErrMsg, size); dep->de_flags |= DEF_SENDING; (*dep->de_sendf) (dep, FALSE, size); } else if (dep->de_mode == DEM_SINK) dep->de_flags |= DEF_ACK_SEND; reply(dep, OK); return; } /* ** Name: void do_vread(message *mp, int vectored) ** Function: */ static void do_vread(message * mp, int vectored) { int port, size; dpeth_t *dep; port = mp->DL_PORT; if (port < 0 || port >= DE_PORT_NR) /* Check for illegal port number */ panic(dep->de_name, PortErrMsg, port); dep = &de_table[port]; dep->de_client = mp->DL_PROC; if (dep->de_mode == DEM_ENABLED) { if (dep->de_flags & DEF_READING) /* Reading in progress */ panic(dep->de_name, "read already in progress", NO_NUM); dep->de_read_iovec.iod_proc_nr = mp->DL_PROC; if (vectored) { get_userdata(mp->DL_PROC, (vir_bytes) mp->DL_ADDR, mp->DL_COUNT, dep->de_read_iovec.iod_iovec); dep->de_read_iovec.iod_iovec_s = mp->DL_COUNT; dep->de_read_iovec.iod_iovec_addr = (vir_bytes) mp->DL_ADDR; size = calc_iovec_size(&dep->de_read_iovec); } else { dep->de_read_iovec.iod_iovec[0].iov_addr = (vir_bytes) mp->DL_ADDR; dep->de_read_iovec.iod_iovec[0].iov_size = size = mp->DL_COUNT; dep->de_read_iovec.iod_iovec_s = 1; dep->de_read_iovec.iod_iovec_addr = 0; } if (size < ETH_MAX_PACK_SIZE) panic(dep->de_name, SizeErrMsg, size); dep->de_flags |= DEF_READING; (*dep->de_recvf) (dep, FALSE, size); #if 0 if ((dep->de_flags & (DEF_READING | DEF_STOPPED)) == (DEF_READING | DEF_STOPPED)) /* The chip is stopped, and all arrived packets delivered */ (*dep->de_resetf) (dep); dep->de_flags &= NOT(DEF_STOPPED); #endif } reply(dep, OK); return; } /* ** Name: void do_getstat(message *mp) ** Function: Reports device statistics. */ static void do_getstat(message * mp) { int port, rc; dpeth_t *dep; port = mp->DL_PORT; if (port < 0 || port >= DE_PORT_NR) /* Check for illegal port number */ panic(dep->de_name, PortErrMsg, port); dep = &de_table[port]; dep->de_client = mp->DL_PROC; if (dep->de_mode == DEM_ENABLED) (*dep->de_getstatsf) (dep); if ((rc = sys_datacopy(SELF, (vir_bytes)&dep->de_stat, mp->DL_PROC, (vir_bytes)mp->DL_ADDR, (vir_bytes) sizeof(dep->de_stat))) != OK) panic(DevName, CopyErrMsg, rc); reply(dep, OK); return; } static void do_getname(mp) message *mp; { int r; strncpy(mp->DL_NAME, progname, sizeof(mp->DL_NAME)); mp->DL_NAME[sizeof(mp->DL_NAME)-1]= '\0'; mp->m_type= DL_NAME_REPLY; r= send(mp->m_source, mp); if (r != OK) panic("dpeth", "do_getname: send failed: %d\n", r); } /* ** Name: void do_stop(message *mp) ** Function: Stops network interface. */ static void do_stop(message * mp) { int port; dpeth_t *dep; port = mp->DL_PORT; if (port < 0 || port >= DE_PORT_NR) /* Check for illegal port number */ panic(dep->de_name, PortErrMsg, port); dep = &de_table[port]; if (dep->de_mode == DEM_ENABLED && (dep->de_flags & DEF_ENABLED)) { /* Stop device */ (dep->de_stopf) (dep); dep->de_flags = DEF_EMPTY; dep->de_mode = DEM_DISABLED; } return; } static void do_watchdog(void *message) { DEBUG(printf("\t no reply")); return; } /* ** Name: int dpeth_task(void) ** Function: Main entry for dp task */ PUBLIC int main(int argc, char **argv) { message m; dpeth_t *dep; int rc, fkeys, sfkeys, tasknr; (progname=strrchr(argv[0],'/')) ? progname++ : (progname=argv[0]); env_setargs(argc, argv); /* Request function key for debug dumps */ fkeys = sfkeys = 0; bit_set(sfkeys, 8); if ((fkey_map(&fkeys, &sfkeys)) != OK) printf("%s: couldn't program Shift+F8 key (%d)\n", DevName, errno); #ifdef ETH_IGN_PROTO { static u16_t eth_ign_proto = 0; long val; val = 0xFFFF; env_parse("ETH_IGN_PROTO", "x", 0, &val, 0x0000L, 0xFFFFL); eth_ign_proto = htons((u16_t) val); } #endif /* Try to notify inet that we are present (again) */ rc = _pm_findproc("inet", &tasknr); if (rc == OK) notify(tasknr); while (TRUE) { if ((rc = receive(ANY, &m)) != OK) panic(dep->de_name, RecvErrMsg, rc); DEBUG(printf("eth: got message %d, ", m.m_type)); switch (m.m_type) { case DEV_PING: /* Status request from RS */ notify(m.m_source); continue; case DL_WRITE: /* Write message to device */ do_vwrite(&m, FALSE); break; case DL_WRITEV: /* Write message to device */ do_vwrite(&m, TRUE); break; case DL_READ: /* Read message from device */ do_vread(&m, FALSE); break; case DL_READV: /* Read message from device */ do_vread(&m, TRUE); break; case DL_INIT: /* Initialize device */ do_init(&m); break; case DL_GETSTAT: /* Get device statistics */ do_getstat(&m); break; case DL_GETNAME: do_getname(&m); break; case SYN_ALARM: /* to be defined */ do_watchdog(&m); break; case DL_STOP: /* Stop device */ do_stop(&m); break; case SYS_SIG: { sigset_t sigset = m.NOTIFY_ARG; if (sigismember(&sigset, SIGKSTOP)) { /* Shut down */ for (rc = 0; rc < DE_PORT_NR; rc += 1) { if (de_table[rc].de_mode == DEM_ENABLED) { m.m_type = DL_STOP; m.DL_PORT = rc; do_stop(&m); } } } break; } case HARD_INT: /* Interrupt from device */ for (dep = de_table; dep < &de_table[DE_PORT_NR]; dep += 1) { /* If device is enabled and interrupt pending */ if (dep->de_mode == DEM_ENABLED) { dep->de_int_pending = TRUE; (*dep->de_interruptf) (dep); if (dep->de_flags & (DEF_ACK_SEND | DEF_ACK_RECV)) reply(dep, !OK); dep->de_int_pending = FALSE; sys_irqenable(&dep->de_hook); } } break; case FKEY_PRESSED: /* Function key pressed */ do_dump(&m); break; case PROC_EVENT: break; default: /* Invalid message type */ panic(DevName, TypeErrMsg, m.m_type); break; } } return OK; /* Never reached, but keeps compiler happy */ } /** dp.c **/