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