📄 网卡驱动程序详解.doc
字号:
本驱动程序中并不支持该函数,在我的rtl8139说明中有*/ <br>
static void el_stop(void *);/*停止接口,在el_ioctl()和el_reset()中调用*/ <br>
static int el_xmit(struct el_softc *,int);/*把数据包放到芯片内,发送到以太网上*/ <br>
static ointhand2_t elintr;/*中断例程*/ <br>
static __inline void elread(struct el_softc *,caddr_t,int);/* 传递包到更高一级协议处理,即ether_input()例程.由elintr()调用
*/ <br>
static struct mbuf *elget(caddr_t,int,struct ifnet *); /* 从网卡上下载数据包,len是数据的长度,本地以太网头部被分开*/
<br>
static __inline void el_hardreset(void *);/* 这是一个子程序,目的是重设硬件.*/ <br>
<br>
/* isa_driver结构 为 autoconf准备 */ <br>
/* isa_driver结构说明: <br>
该结构来之于文件isa_device.h头文件 <br>
结构成员: <br>
/* <br>
* 通用的设备驱动程序结构. <br>
* <br>
* 没一设备驱动程序定义一组例程入口,由设置程序在启动时使用. <br>
<br>
struct isa_driver { <br>
int (*probe) __P((struct isa_device *idp)); <br>
/* 测试设备是否存在 <br>
int (*attach) __P((struct isa_device *idp)); <br>
/* 为设备设置驱动程序 <br>
char *name; /* 设备名称 <br>
int sensitive_hw; /* 探测发生有冲突时为真,ISA设备的老毛病 <br>
}; <br>
*/ <br>
struct isa_driver eldriver = { <br>
el_probe, el_attach, "el" <br>
}; <br>
<br>
<br>
/* 探测程序.查看是否卡存在和是否在正确的位置. */ <br>
static int <br>
el_probe(struct isa_device *idev) <br>
{ <br>
/* <br>
isa_device 是设备的通用结构,该结构说明在isa_device.h头文件中,说明如下: <br>
struct isa_device { <br>
int id_id; /* 设备的 id <br>
struct isa_driver *id_driver; 指向设备的驱动程序结构 <br>
int id_iobase; /* 基本IO地址 <br>
int id_iosize; /* 基本IO地址的长度 <br>
u_int id_irq; /* 中断 <br>
int id_drq; /* DMA <br>
caddr_t id_maddr; /* 在总线中的物理IO内存地址(即便要) <br>
int id_msize; /* IO内存的大小 <br>
union { <br>
inthand2_t *id_i; <br>
ointhand2_t *id_oi;中断例程指针 <br>
} id_iu; /* 中断接口例程 <br>
#define id_intr id_iu.id_i <br>
#define id_ointr id_iu.id_oi <br>
int id_unit; /* 在该类设备中是第几个 <br>
int id_flags; /* flags <br>
int id_enabled; /* 设备激活了吗 <br>
struct isa_device *id_next; /* 在 userconfig()中用于isa_devlist <br>
struct device *id_device; <br>
}; <br>
<br>
*/ <br>
struct el_softc *sc; <br>
u_short base; /* 仅仅为了方便 */ <br>
u_char station_addr[ETHER_ADDR_LEN];/*以太网的硬件地址*/ <br>
int i; <br>
<br>
/* 搜集一些信息 */ <br>
sc = &el_softc[idev->id_unit];/*sc是softc结构,如果你有NEL块el卡的话就有NEL个softc <br>
结构,当然也有可能同时还有其他的xx_softc结构*/ <br>
sc->el_base = idev->id_iobase;/*该块卡的基本I/O地址*/ <br>
base = sc->el_base;/*有一点多余,只是为了方便下面的引用*/ <br>
<br>
/* 第一次检查地址,看看基本地址是否在0X280到0X3F0之内 */ <br>
if((base < 0x280) || (base > 0x3f0)) { <br>
printf("el%d: ioaddr must be between 0x280 and 0x3f0\n", <br>
idev->id_unit); <br>
return(0); <br>
} <br>
<br>
/* 现在尝试从PROM中获取地址,看看是否包含了3COM供应商的标识代码. <br>
*/ <br>
dprintf(("Probing 3c501 at 0x%x...\n",base));/*在调试时会打印出*/ <br>
<br>
/* 重置板卡 */ <br>
dprintf(("Resetting board...\n")); <br>
outb(base+EL_AC,EL_AC_RESET);/*我们一般定义基地址为0X300,EL_AC=0E,是辅助命令寄存器*/ <br>
DELAY(5);/*延迟5毫秒*/ <br>
outb(base+EL_AC,0); <br>
dprintf(("Reading station address...\n")); <br>
/* 读硬件地址,共六次 */ <br>
for(i=0;i<ETHER_ADDR_LEN;i++) { <br>
outb(base+EL_GPBL,i); <br>
station_addr[i] = inb(base+EL_EAW);/*EL_EAW是该卡的地址口,station_addr是函数内部变量, <br>
下面判断了生产厂家后就没用的*/ <br>
} <br>
dprintf(("Address is %6D\n",station_addr, ":")); <br>
<br>
/* 如果厂商标识代码正确,那么返回1. <br>
*/ <br>
if((station_addr[0] != 0x02) || (station_addr[1] != 0x60) <br>
|| (station_addr[2] != 0x8c)) { <br>
dprintf(("Bad vendor code.\n"));/*3COM厂商此种卡的代码为02608C*/ <br>
return(0); <br>
} else { <br>
dprintf(("Vendor code ok.\n")); <br>
/* 把地址拷贝到arpcom结构中 */ <br>
bcopy(station_addr,sc->arpcom.ac_enaddr,ETHER_ADDR_LEN); <br>
return(1); <br>
} <br>
} <br>
<br>
/* 这是一个子程序,目的是重设硬件. 在el_init()中调用,在elintr()中调用,产生中断,有溢出发生时调用*/ <br>
static __inline void <br>
el_hardreset(xsc) <br>
void *xsc; <br>
{ <br>
register struct el_softc *sc = xsc;/*记住在C中,寄存器变量只能有三个,可加快速度*/ <br>
register int base; <br>
register int j; <br>
<br>
base = sc->el_base; <br>
<br>
/* 第一步,重设板卡,和el_probe中的一样(前面) */ <br>
outb(base+EL_AC,EL_AC_RESET); <br>
DELAY(5); <br>
outb(base+EL_AC,0); <br>
<br>
/* 又把地址填回去,为什么?没有为什么,就是厂商规定的,一些端口填什么数据时会怎么样,只有厂商知道,我相信, <br>
在同一厂商之间的网卡,交换机,路由器进行秘密通讯是非常可能的,他可以不返回到CPU层*/ <br>
for(j=0;j<ETHER_ADDR_LEN;j++) <br>
outb(base+j,sc->arpcom.ac_enaddr[j]); <br>
} <br>
<br>
/* 连接该接口到核心数据结构.被调用时,我们已经知道该卡已经存在在给定的I/O <br>
* 地址,我们还假定中断号是正确的. <br>
*/ <br>
static int <br>
el_attach(struct isa_device *idev) <br>
{ <br>
struct el_softc *sc; <br>
struct ifnet *ifp;/*该结构是一个巨大的结构,在STEVEN的书中有描述,我也写了一篇*/ <br>
u_short base;/*没用上,可以去掉*/ <br>
<br>
dprintf(("Attaching el%d...\n",idev->id_unit)); <br>
<br>
/* 放置一些指针. */ <br>
idev->id_ointr = elintr;/*放置中断例程指针,中断例程在下面*/ <br>
sc = &el_softc[idev->id_unit];/*定位本设备的softc结构指针*/ <br>
ifp = &sc->arpcom.ac_if;/*定位ifnet结构*/ <br>
base = sc->el_base;/*从程序来看,这一句可以去掉,根本没用,因为在该函数中没用到base*/ <br>
<br>
/* 重设板卡 */ <br>
dprintf(("Resetting board...\n")); <br>
el_hardreset(sc);/*该程序在上面*/ <br>
<br>
/* 初始化ifnet结构,该结构的成员经常用来被ether网子程序,arp,bridge等调用 */ <br>
ifp->if_softc = sc;/*该网卡的IFP(通用接口结构)的专用结构指针(softc结构)*/ <br>
ifp->if_unit = idev->id_unit;/*第几块网卡*/ <br>
ifp->if_name = "el";/*网络卡的名称*/ <br>
ifp->if_mtu = ETHERMTU;/*1500*/ <br>
ifp->if_output = ether_output;/*以太网的输出子程序指针(不要搞错了,是向IP层 <br>
输出,按我们的理解是数据输入了,再转送到上一层协议)*/ <br>
ifp->if_start = el_start;/*把数据包从硬件接口输出去*/ <br>
ifp->if_ioctl = el_ioctl;/*控制网卡的函树指针*/ <br>
ifp->if_watchdog = el_watchdog;/*一般该函数用于包在一定时间内没发送出去,就调用他,在 <br>
本驱动程序中并不支持该函数,在我的rtl8139说明中有*/ <br>
ifp->if_init = el_init; /*不用说,是初始化,在probe,attach之后被调用*/ <br>
ifp->if_flags = (IFF_BROADCAST | IFF_SIMPLEX);/*支持广播和单播*/ <br>
<br>
/* 调用通用以太网初始化例程 */ <br>
dprintf(("Attaching interface...\n")); <br>
ether_ifattach(ifp, ETHER_BPF_SUPPORTED); <br>
/* <br>
在if_ethersubr.c中的ether_ifattach例程 <br>
void ether_ifattach(ifp, bpf) 调用时,ETHER_BPF_SUPPORTED是BSD的 <br>
包过滤器,如果在编译时设置文件没有 <br>
打开包过滤器,那么代表0,否则是1 <br>
register struct ifnet *ifp; <br>
int bpf; <br>
{ <br>
register struct ifaddr *ifa; <br>
register struct sockaddr_dl *sdl; <br>
<br>
if_attach(ifp); 此例程在if.c 中 <br>
ifp->if_type = IFT_ETHER;代表以太网 <br>
ifp->if_addrlen = 6;硬件地址长度是6 <br>
ifp->if_hdrlen = 14;包的头长度是6+6+2=14,其中2是协议类型 <br>
ifp->if_mtu = ETHERMTU; 为1500,多此一举,在前面你可看到,已 <br>
经填充了. <br>
ifp->if_resolvemulti = ether_resolvemulti; 以太网解析多播例程指针 <br>
if (ifp->if_baudrate == 0) 波特率 <br>
ifp->if_baudrate = 10000000; <br>
ifa = ifnet_addrs[ifp->if_index - 1];在ifnet_addrs[]数组中找到本地址指针 <br>
KASSERT(ifa != NULL, ("%s: no lladdr!\n", __FUNCTION__)); <br>
sdl = (struct sockaddr_dl *)ifa->ifa_addr; ifa->ifa_addr在此时指向的是sockaddr_dl结构.
<br>
sdl->sdl_type = IFT_ETHER; <br>
sdl->sdl_alen = ifp->if_addrlen; <br>
bcopy((IFP2AC(ifp))->ac_enaddr, LLADDR(sdl), ifp->if_addrlen);把硬件地址拷贝到sdl结构中
<br>
if (bpf) bpf为真,即加入了BSD包过滤 <br>
bpfattach(ifp, DLT_EN10MB, sizeof(struct ether_header)); <br>
if (ng_ether_attach_p != NULL) <br>
(*ng_ether_attach_p)(ifp); <br>
} <br>
<br>
*/ <br>
printf("el%d: 3c501 address %6D\n",idev->id_unit, <br>
sc->arpcom.ac_enaddr, ":"); <br>
<br>
dprintf(("el_attach() finished.\n")); <br>
return(1); <br>
} <br>
<br>
/* 该例程重设接口. 在el_watchdog()中调用,因为watchdog不在本驱动程序中支持,所以从不被调用*/ <br>
static void <br>
el_reset(xsc)/*上面的一个函数,重设硬件*/ <br>
void *xsc; <br>
{ <br>
struct el_softc *sc = xsc; <br>
int s; <br>
<br>
dprintf(("elreset()\n")); <br>
s = splimp();/*关网络中断*/ <br>
el_stop(sc);/*下面的一个函数*/ <br>
el_init(sc);/*重新初始化卡*/ <br>
splx(s);/*开网络中断*/ <br>
} <br>
/*停止接口,在el_ioctl()和el_reset()中调用*/ <br>
static void el_stop(xsc) <br>
void *xsc; <br>
{ <br>
struct el_softc *sc = xsc; <br>
<br>
outb(sc->el_base+EL_AC,0);/*用0写辅助命令寄存器*/ <br>
} <br>
<br>
/* 初始化接口. */ <br>
static void <br>
el_init(xsc) <br>
void *xsc; <br>
{ <br>
struct el_softc *sc = xsc; <br>
struct ifnet *ifp; <br>
int s; <br>
u_short base; <br>
<br>
ifp = &sc->arpcom.ac_if;/*定位ifnet结构*/ <br>
base = sc->el_base;/*网卡基本I/O地址*/ <br>
<br>
/* 如果地址不知道,什么也不做. */ <br>
if(TAILQ_EMPTY(&ifp->if_addrhead)) /* 在if.c中的if_attach例程 <br>
中已经填充,由el_attach调用 <br>
ether_attach时再调用if_attach */ <br>
return; <br>
<br>
s = splimp();/*关网络中断*/ <br>
<br>
/* 重设板卡. */ <br>
dprintf(("Resetting board...\n")); <br>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -