📄 rtl8139.c
字号:
/* * rtl8139.c * * This file contains a ethernet device driver for Realtek rtl8139 based * ethernet cards. * * The valid messages and their parameters are: * * m_type DL_PORT DL_PROC DL_COUNT DL_MODE DL_ADDR * |------------+----------+---------+----------+---------+---------| * | HARD_INT | | | | | | * |------------|----------|---------|----------|---------|---------| * | SYS_SIG | | | | | | * |------------|----------|---------|----------|---------|---------| * | DL_WRITE | port nr | proc nr | count | mode | address | * |------------|----------|---------|----------|---------|---------| * | DL_WRITEV | port nr | proc nr | count | mode | address | * |------------|----------|---------|----------|---------|---------| * | DL_READ | port nr | proc nr | count | | address | * |------------|----------|---------|----------|---------|---------| * | DL_READV | port nr | proc nr | count | | address | * |------------|----------|---------|----------|---------|---------| * | DL_INIT | port nr | proc nr | mode | | address | * |------------|----------|---------|----------|---------|---------| * | DL_GETSTAT | port nr | proc nr | | | address | * |------------|----------|---------|----------|---------|---------| * | DL_GETNAME | | | | | | * |------------|----------|---------|----------|---------|---------| * | DL_STOP | port_nr | | | | | * |------------|----------|---------|----------|---------|---------| * * The messages sent are: * * m-type DL_POR T DL_PROC DL_COUNT DL_STAT DL_CLCK * |------------|----------|---------|----------|---------|---------| * |DL_TASK_REPL| port nr | proc nr | rd-count | err|stat| clock | * |------------|----------|---------|----------|---------|---------| * * m_type m3_i1 m3_i2 m3_ca1 * |------------+---------+-----------+---------------| * |DL_INIT_REPL| port nr | last port | ethernet addr | * |------------|---------|-----------|---------------| * * Created: Aug 2003 by Philip Homburg <philip@cs.vu.nl> * Changes: * Aug 15, 2004 sync alarms replace watchdogs timers (Jorrit N. Herder) * May 02, 2004 flag alarms replace micro_elapsed() (Jorrit N. Herder) * */#include "../drivers.h"#include <stdlib.h>#include <stdio.h>#include <string.h>#include <stddef.h>#include <minix/com.h>#include <minix/keymap.h>#include <minix/syslib.h>#include <minix/type.h>#include <minix/sysutil.h>#include <timers.h>#include <ibm/portio.h>#include <net/hton.h>#include <net/gen/ether.h>#include <net/gen/eth_io.h>#include <sys/types.h>#include <fcntl.h>#include <assert.h>#include <unistd.h>#include <sys/ioc_memory.h>#include "../../kernel/const.h"#include "../../kernel/config.h"#include "../../kernel/type.h"#define tmra_ut timer_t#define tmra_inittimer(tp) tmr_inittimer(tp)#define Proc_number(p) proc_number(p)#define debug 0#define printW() ((void)0)#define vm_1phys2bus(p) (p)#define VERBOSE 0 /* display message during init */#include "../libpci/pci.h"#include "rtl8139.h"#define RX_BUFSIZE RL_RCR_RBLEN_64K_SIZE#define RX_BUFBITS RL_RCR_RBLEN_64K#define N_TX_BUF RL_N_TX#define RE_PORT_NR 1 /* Minix *//* I/O vectors are handled IOVEC_NR entries at a time. */#define IOVEC_NR 16/* Configuration */#define RL_ENVVAR "RTLETH"PRIVATE struct pcitab{ u16_t vid; u16_t did; int checkclass;} pcitab[]={ { 0x10ec, 0x8139, 0 }, /* Realtek RTL8139 */ { 0x1186, 0x1300, 0 }, /* D-Link RTL8139 */ { 0x0000, 0x0000, 0 }};typedef struct re{ port_t re_base_port; int re_irq; int re_mode; int re_flags; int re_client; int re_link_up; int re_got_int; int re_send_int; int re_report_link; int re_clear_rx; int re_need_reset; int re_tx_alive; char *re_model; /* Rx */ phys_bytes re_rx_buf; char *v_re_rx_buf; vir_bytes re_read_s; /* Tx */ int re_tx_head; int re_tx_tail; struct { int ret_busy; phys_bytes ret_buf; char * v_ret_buf; } re_tx[N_TX_BUF]; u32_t re_ertxth; /* Early Tx Threshold */ /* PCI related */ int re_seen; /* TRUE iff device available */ u8_t re_pcibus; u8_t re_pcidev; u8_t re_pcifunc; /* 'large' items */ int re_hook_id; /* IRQ hook id at kernel */ eth_stat_t re_stat; ether_addr_t re_address; message re_rx_mess; message re_tx_mess; char re_name[sizeof("rtl8139#n")]; iovec_t re_iovec[IOVEC_NR];}re_t;#define REM_DISABLED 0x0#define REM_ENABLED 0x1#define REF_PACK_SENT 0x001#define REF_PACK_RECV 0x002#define REF_SEND_AVAIL 0x004#define REF_READING 0x010#define REF_EMPTY 0x000#define REF_PROMISC 0x040#define REF_MULTI 0x080#define REF_BROAD 0x100#define REF_ENABLED 0x200static re_t re_table[RE_PORT_NR];static u16_t eth_ign_proto;static tmra_ut rl_watchdog;FORWARD _PROTOTYPE( unsigned my_inb, (U16_t port) );FORWARD _PROTOTYPE( unsigned my_inw, (U16_t port) );FORWARD _PROTOTYPE( unsigned my_inl, (U16_t port) );static unsigned my_inb(U16_t port) { U8_t value; int s; if ((s=sys_inb(port, &value)) !=OK) printf("RTL8139: warning, sys_inb failed: %d\n", s); return value;}static unsigned my_inw(U16_t port) { U16_t value; int s; if ((s=sys_inw(port, &value)) !=OK) printf("RTL8139: warning, sys_inw failed: %d\n", s); return value;}static unsigned my_inl(U16_t port) { U32_t value; int s; if ((s=sys_inl(port, &value)) !=OK) printf("RTL8139: warning, sys_inl failed: %d\n", s); return value;}#define rl_inb(port, offset) (my_inb((port) + (offset)))#define rl_inw(port, offset) (my_inw((port) + (offset)))#define rl_inl(port, offset) (my_inl((port) + (offset)))FORWARD _PROTOTYPE( void my_outb, (U16_t port, U8_t value) );FORWARD _PROTOTYPE( void my_outw, (U16_t port, U16_t value) );FORWARD _PROTOTYPE( void my_outl, (U16_t port, U32_t value) );static void my_outb(U16_t port, U8_t value) { int s; if ((s=sys_outb(port, value)) !=OK) printf("RTL8139: warning, sys_outb failed: %d\n", s);}static void my_outw(U16_t port, U16_t value) { int s; if ((s=sys_outw(port, value)) !=OK) printf("RTL8139: warning, sys_outw failed: %d\n", s);}static void my_outl(U16_t port, U32_t value) { int s; if ((s=sys_outl(port, value)) !=OK) printf("RTL8139: warning, sys_outl failed: %d\n", s);}#define rl_outb(port, offset, value) (my_outb((port) + (offset), (value)))#define rl_outw(port, offset, value) (my_outw((port) + (offset), (value)))#define rl_outl(port, offset, value) (my_outl((port) + (offset), (value)))_PROTOTYPE( static void rl_init, (message *mp) );_PROTOTYPE( static void rl_pci_conf, (void) );_PROTOTYPE( static int rl_probe, (re_t *rep) );_PROTOTYPE( static void rl_conf_hw, (re_t *rep) );_PROTOTYPE( static void rl_init_buf, (re_t *rep) );_PROTOTYPE( static void rl_init_hw, (re_t *rep) );_PROTOTYPE( static void rl_reset_hw, (re_t *rep) );_PROTOTYPE( static void rl_confaddr, (re_t *rep) );_PROTOTYPE( static void rl_rec_mode, (re_t *rep) );_PROTOTYPE( static void rl_readv, (message *mp, int from_int, int vectored) );_PROTOTYPE( static void rl_writev, (message *mp, int from_int, int vectored) );_PROTOTYPE( static void rl_check_ints, (re_t *rep) );_PROTOTYPE( static void rl_report_link, (re_t *rep) );_PROTOTYPE( static void mii_print_techab, (U16_t techab) );_PROTOTYPE( static void mii_print_stat_speed, (U16_t stat, U16_t extstat) );_PROTOTYPE( static void rl_clear_rx, (re_t *rep) );_PROTOTYPE( static void rl_do_reset, (re_t *rep) );_PROTOTYPE( static void rl_getstat, (message *mp) );_PROTOTYPE( static void rl_getname, (message *mp) );_PROTOTYPE( static void reply, (re_t *rep, int err, int may_block) );_PROTOTYPE( static void mess_reply, (message *req, message *reply) );_PROTOTYPE( static void put_userdata, (int user_proc, vir_bytes user_addr, vir_bytes count, void *loc_addr) );_PROTOTYPE( static void rtl8139_stop, (void) );_PROTOTYPE( static void check_int_events, (void) );_PROTOTYPE( static int do_hard_int, (void) );_PROTOTYPE( static void rtl8139_dump, (message *m) );#if 0_PROTOTYPE( static void dump_phy, (re_t *rep) );#endif_PROTOTYPE( static int rl_handler, (re_t *rep) );_PROTOTYPE( static void rl_watchdog_f, (timer_t *tp) );/* The message used in the main loop is made global, so that rl_watchdog_f() * can change its message type to fake a HARD_INT message. */PRIVATE message m;PRIVATE int int_event_check; /* set to TRUE if events arrived */static char *progname;extern int errno;/*===========================================================================* * main * *===========================================================================*/int main(int argc, char *argv[]){ int fkeys, sfkeys; int inet_proc_nr; int i, r; re_t *rep; long v; env_setargs(argc, argv); v= 0; (void) env_parse("ETH_IGN_PROTO", "x", 0, &v, 0x0000L, 0xFFFFL); eth_ign_proto= htons((u16_t) v); /* Observe some function key for debug dumps. */ fkeys = sfkeys = 0; bit_set(sfkeys, 9); if ((r=fkey_map(&fkeys, &sfkeys)) != OK) printf("Warning: RTL8139 couldn't observe Shift+F9 key: %d\n",r); /* Claim buffer memory now under Minix, before MM takes it all. */ for (rep= &re_table[0]; rep < re_table+RE_PORT_NR; rep++) rl_init_buf(rep); /* Try to notify INET that we are present (again). If INET cannot * be found, assume this is the first time we started and INET is * not yet alive. */ (progname=strrchr(argv[0],'/')) ? progname++ : (progname=argv[0]); r = findproc("inet", &inet_proc_nr); if (r == OK) notify(inet_proc_nr); while (TRUE) { if ((r= receive(ANY, &m)) != OK) panic("rtl8139","receive failed", r); switch (m.m_type) { case DEV_PING: notify(m.m_source); continue; case DL_WRITEV: rl_writev(&m, FALSE, TRUE); break; case DL_WRITE: rl_writev(&m, FALSE, FALSE); break;#if 0 case DL_READ: do_vread(&m, FALSE); break;#endif case DL_READV: rl_readv(&m, FALSE, TRUE); break; case DL_INIT: rl_init(&m); break; case DL_GETSTAT: rl_getstat(&m); break; case DL_GETNAME: rl_getname(&m); break;#if 0 case DL_STOP: do_stop(&m); break;#endif case SYN_ALARM: /* Under MINIX, synchronous alarms are used instead of * watchdog functions. The approach is very different: * MINIX VMD timeouts are handled within the kernel * (the watchdog is executed by CLOCK), and notify() * the driver in some cases. * MINIX timeouts result in a SYN_ALARM message to the * driver and thus are handled where they should be * handled. Locally, watchdog functions are used again. */ rl_watchdog_f(NULL); break; case HARD_INT: do_hard_int(); if (int_event_check) check_int_events(); break ; case FKEY_PRESSED: rtl8139_dump(&m); break; case SYS_SIG: { sigset_t sigset = m.NOTIFY_ARG; if (sigismember(&sigset, SIGKSTOP)) rtl8139_stop(); break; } default: panic("rtl8139","illegal message", m.m_type); } }}static void check_int_events(void) { int i; re_t *rep; for (i= 0, rep= &re_table[0]; i<RE_PORT_NR; i++, rep++) { if (rep->re_mode != REM_ENABLED) continue; if (!rep->re_got_int) continue; rep->re_got_int= 0; assert(rep->re_flags & REF_ENABLED); rl_check_ints(rep); }}/*===========================================================================* * rtl8139_stop * *===========================================================================*/static void rtl8139_stop(){ int i; re_t *rep; for (i= 0, rep= &re_table[0]; i<RE_PORT_NR; i++, rep++) { if (rep->re_mode != REM_ENABLED) continue; rl_outb(rep->re_base_port, RL_CR, 0); } sys_exit(0);}/*===========================================================================* * rtl8139_dump * *===========================================================================*/static void rtl8139_dump(m)message *m; /* pointer to request message */{ re_t *rep; int i; printf("\n"); for (i= 0, rep = &re_table[0]; i<RE_PORT_NR; i++, rep++) { if (rep->re_mode == REM_DISABLED) printf("Realtek RTL 8139 port %d is disabled\n", i); if (rep->re_mode != REM_ENABLED) continue; printf("Realtek RTL 8139 statistics of port %d:\n", i); printf("recvErr :%8ld\t", rep->re_stat.ets_recvErr); printf("sendErr :%8ld\t", rep->re_stat.ets_sendErr); printf("OVW :%8ld\n", rep->re_stat.ets_OVW); printf("CRCerr :%8ld\t", rep->re_stat.ets_CRCerr); printf("frameAll :%8ld\t", rep->re_stat.ets_frameAll); printf("missedP :%8ld\n", rep->re_stat.ets_missedP); printf("packetR :%8ld\t", rep->re_stat.ets_packetR); printf("packetT :%8ld\t", rep->re_stat.ets_packetT); printf("transDef :%8ld\n", rep->re_stat.ets_transDef); printf("collision :%8ld\t", rep->re_stat.ets_collision); printf("transAb :%8ld\t", rep->re_stat.ets_transAb); printf("carrSense :%8ld\n", rep->re_stat.ets_carrSense); printf("fifoUnder :%8ld\t", rep->re_stat.ets_fifoUnder); printf("fifoOver :%8ld\t", rep->re_stat.ets_fifoOver); printf("CDheartbeat:%8ld\n", rep->re_stat.ets_CDheartbeat); printf("OWC :%8ld\t", rep->re_stat.ets_OWC); printf("re_flags = 0x%x\n", rep->re_flags); printf( "TSAD: 0x%04x, TSD: 0x%08x, 0x%08x, 0x%08x, 0x%08x\n", rl_inw(rep->re_base_port, RL_TSAD), rl_inl(rep->re_base_port, RL_TSD0+0*4), rl_inl(rep->re_base_port, RL_TSD0+1*4), rl_inl(rep->re_base_port, RL_TSD0+2*4), rl_inl(rep->re_base_port, RL_TSD0+3*4)); printf("tx_head %d, tx_tail %d, busy: %d %d %d %d\n", rep->re_tx_head, rep->re_tx_tail, rep->re_tx[0].ret_busy, rep->re_tx[1].ret_busy, rep->re_tx[2].ret_busy, rep->re_tx[3].ret_busy); }}/*===========================================================================* * do_init * *===========================================================================*/static void rl_init(mp)message *mp;{ static int first_time= 1; int port; re_t *rep; message reply_mess; if (first_time) { first_time= 0; rl_pci_conf(); /* Configure PCI devices. */ tmra_inittimer(&rl_watchdog); /* Use a synchronous alarm instead of a watchdog timer. */ sys_setalarm(HZ, 0); } port = mp->DL_PORT; if (port < 0 || port >= RE_PORT_NR) { reply_mess.m_type= DL_INIT_REPLY; reply_mess.m3_i1= ENXIO; mess_reply(mp, &reply_mess); return; } rep= &re_table[port]; if (rep->re_mode == REM_DISABLED) { /* This is the default, try to (re)locate the device. */ rl_conf_hw(rep); if (rep->re_mode == REM_DISABLED) { /* Probe failed, or the device is configured off. */ reply_mess.m_type= DL_INIT_REPLY; reply_mess.m3_i1= ENXIO; mess_reply(mp, &reply_mess); return; } if (rep->re_mode == REM_ENABLED) rl_init_hw(rep);#if VERBOSE /* load silently ... can always check status later */ rl_report_link(rep);#endif } assert(rep->re_mode == REM_ENABLED); assert(rep->re_flags & REF_ENABLED); rep->re_flags &= ~(REF_PROMISC | REF_MULTI | REF_BROAD); if (mp->DL_MODE & DL_PROMISC_REQ) rep->re_flags |= REF_PROMISC; if (mp->DL_MODE & DL_MULTI_REQ) rep->re_flags |= REF_MULTI; if (mp->DL_MODE & DL_BROAD_REQ) rep->re_flags |= REF_BROAD; rep->re_client = mp->m_source; rl_rec_mode(rep); reply_mess.m_type = DL_INIT_REPLY; reply_mess.m3_i1 = mp->DL_PORT; reply_mess.m3_i2 = RE_PORT_NR;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -