免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 1366 | 回复: 0
打印 上一主题 下一主题

3c501芯片的驱动的代码理解 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2005-06-24 08:36 |只看该作者 |倒序浏览
6.21
这个驱动是3c501芯片的驱动。 是作为模块插入的。
模块的开始在

init_module()开始 其中:
首先,net_device的结构 的中断请求编号irq赋值,这个值默认为5,可以在插入模块的时候指定;设定 i/o地址,默认为0x280,也可以在加入模块的时候指定。
其次,register_netdev(&net_device)注册这个设备。这个函数做的事情就是调用net_device 的init定义的函数入口:el1_probe()

二 el1_probe(struct net_device *dev)函数
        首先 设置为内核模块的设备。
                SET_MODULE_OWER(dev) ;//equal to dev->;owner=THIS_MODULE;
        而后 根据设置的i/o地址范围  调用el1_probe1(dev,base_addr)

三 el1_probe1
        首先request_region(ioaddr,16 ,dev->;name) 为设备请求一个从ioaddr开始,16个端口数目的I/O端口。
        而后 向el1_dataptr写入0――5 , 端口地址el2_saprom 读取字节。保存在sataion_addr中//这个是做什么呢?给不同的地址写入,然后看saprom的值?
        再根据station_addr的前3个参数来确定vender的值 。如果没有信息则 释放所占用的i/o端口  release_region(ioaddr,El1_IO_EXTENT);
       
        接下来 对dev->;irq进行判断,主要是考虑用户指定irq小于2的情况。
                Autoirq_setup(2)  自动设定中断探测
                Inb(RX_STATUS) //程序中注释为清楚未决的中断 ,inb除了读出还有清空?
                Inb(TX_STATUS) 读出i/o地址的信息?可是为什么没有保存给一个变量呢?
                然后给ax寄存器分别写入一些值,具体值的含义也许和芯片有关系吧,
                再调用autoirq_report(1) 表示1个 jiffies之后 调用autoirq_setup返回irq数量。如                为零则表示无法分配中断号了,则释放先前申请的I/O地址

void autoirq_setup(int waittime); 该函数设置中断探测,在这里忽略了waittime参数。
int autoirq_report(int waittime); 该函数延迟给定的时间(以jiffies计算),然后返回自调用autoirq_setup以后产生的IRQ数量。 这些函数最初是在网络驱动程序的代码中使用,由于历史原因,它们现在用probe_irq_on和probe_irq_off实现。通常没有必要使用 autoirq_ 函数,而应该使用 probe_irq_。

如果dev->;irq>;2,则直接 设定寄存器值, 设定为loopback模式
设定dev->;base_addr的io地址
设定dev->;dev_addr ,即硬件地址,//大概是网卡mac地址?

if (dev->;mem_start & 0xf) 这个是什么意思呢? 设备在存储器中占用的起始位置不能是从0000开始嘛?从0000开始又意味着什么呢?  
如果不是从零地址开始的,就要和0x7与,获得一个状态信息?为什么是和0x7比较呢? 0111

明显结果是获得低三位的值, 如果不为零就答应一个版本信息
BIOS会在内存的开始地址(地址0000:0000)建立一个表格,表格内记载了每一个中断服务程序的起始地址。只要有中断调用产生,就可先到此表格中根据中断编号找出该中断服务程序的起始地址,进而执行中断服务程

到这里完成了对设备 i/o地址的分配和中断的分配 ,接下来要做的就是对dev结构中的一些入口函数进行设定了:
首先 分配一个net_local 自定义的空间
而后 生成一个自选锁, ,设定每个入口函数地址:
dev->;open = &el_open;    //打开设备的函数
        dev->;hard_start_xmit = &el_start_xmit;   //开始发送数据的函数 这个是由网络协议栈调用的
        dev->;tx_timeout = &el_timeout;     //超时函数
        dev->;watchdog_timeo = HZ;
        dev->;stop = &el1_close;                  //关闭设备调用
        dev->;get_stats = &el1_get_stats;
        dev->;set_multicast_list = &set_multicast_list;  //多波的处理函数
        dev->;do_ioctl = netdev_ioctl;  //ioctl的功能,对网卡的状态进行设定.


//今天晚上根据tcp/ip那个文章,自己把流程画出来把.
画着玩.vsd

相关函数设定结束后调用  ether_setup(dev);  针对以太网 填充dev相应的一些字段 包括
……
dev->;hard_header        = eth_header;
dev->;rebuild_header     = eth_rebuild_header;
……
http://lxr.linux.no/source/drivers/net/net_init.c?v=2.4.18#L405


这样就完成了对这个设备的i/o分配,地址分配和相关函数的设定了。下面看每个函数的调用说明和实现

el_open()  当通过ifconfig改变一些设定时 ,才会调用次函数。
首先调用 request_irq (dev->;irq, &el_interrupt, 0, dev->;name, dev) 来为el_interrupt函数申请一个值为dev->;irq的中断号.在Intel平台,硬件中断号范围0--15,如果成功则返回 ,当由中断发生是就调用el_interrupt函数 ,第三个参数代表irq的类型
SA_SHIRQ                Interrupt is shared
  SA_INTERRUPT            Disable local interrupts while processin

. http://lxr.linux.no/source/arch/i386/kernel/irq.c?v=2.4.18#L677  

如果申请中断号失败,则 开启自选锁,
调用el_reset()
关闭自选锁.

设置RX/TX模式 接受/发送
outb(); 给状态寄存器设定值,设定为接受模式
netif_start_queue(dev)    //允许网卡开始传输包

现在看看el_interrrupt函数
这部分代码好像寄存器德读写比较多,明天再看把. 明天就得看完啊~

6.23
加锁
首先读取 状态寄存器的值.
然后判断是否处于发送状态,
是则读取寄存器中发送状态的值,如果装载冲突,则重新装载 lp->;loading=2 下一步 调用netif_wake_queue() 会使得上层协定开始传送新的资料下来 然后又判断是否超时,是的话,改变寄存器的一些值,保存一些信息,然后再调用netif_wake_queue 然后是重新发送时的处理.其他情况则进入接受状态 ,进行接受
如果处于发送状态  首先看是否处于rx-runt rx_missed状态 如果正常则进入el_receve函数进行处理,   其他情况则调用el_reset

最后重新设置为接受状态
outb(AX_RX, AX_CMD);
        outw(0x00, RX_BUF_CLR);
        inb(RX_STATUS);                /* Be certain that interrupts are cleared. */
        inb(TX_STATUS);

el_interrupt就结束了,看看el_reset函数的过程  

el_reset() 完成了对各个寄存器的复位工作和读取hw地址信息

再看el_receive的过程 ,在这里终于看到了sk_buffer这个结构体了 .
pkt_len从rx_low中读取的是包的长度 ,应该是包括eth头在内的包长 。
以太网最小包长为60字节 最大传输数据帧的长度为1500字节 ,为什么这里比较上限是1536

答:
以太网数据帧最小为60字节,最大为1500字节 也就是MTU ,这个MTU是包含了上层协议头和payload , 再加上以太网14字节的头部 和4个字节的crc 才1518,
可是代码了怎么判断的是1536呢?

自己找到的一些答案 ,给大家分享一下了:)
MTU在IEEE 802.3中 定义为1500 那么加上以太网的头 和crc  就是1518.
在ETHER II 中 头有了一些变化,多了一些字段
1514                 Basic maximum Ethernet packet size w/headers
    +4                   Packet with 4 byte CRC
    +2                 Align the IP header               
   +16                Prepend a descriptor

进入命令模式
dev_alloc_skb()分配一个sbk的空间,不过这里为什么又多分2个字节长度 ,看后面的skb_reserver应该是预留出2个字节空间 给后移用的。
dev_alloc_skb()保留16字节的帧头空间。主要用在Ethernet驱动程序
如果申请到了空间,则调用skb_reserve() ,在一个申请好的sk_buff的缓冲区里保留一块空间。这个空间一般是用做下一层协议的头空间的 .数据区和尾部指针都后移一个长度。
Insb() ;从8位的dataport的中读取一个pkt_len的字节,保存到skb中,因为刚分配的skb->;tail和data执行的地址一样,所以就是把内容从data起始的地址保存 。
Skb->;protocol=eth_type_trans;获得数据帧的协议类型。
netif_rx(dev);//终于看到这个函数了,这个是连接底层驱动和网络核心的关键函数。看看这个函数的流程:
  最主要的就是将skb放入队列,然后调用cpu_raise_softirq(this_cpu, NET_RX_SOFTIRQ);

然后再设置一些统计信息,el_receive函数就结束了.那么这个skb就送交网络核心层进行处理了.和底层驱动就没有关系了.


El_open 和相关调用看完了,下面看看el_start_xmit;就是发送的函数

网络接口核心层通过 dev_queue_xmit()(net/core/dev.c,line975)这个函数向上层提供统一的发送接口,也就是说无论是IP,还是ARP 协议,通过这个函数把要发送的数据传递给这一层,想发送数据的时候就调用这个函数就可以了。dev_queue_xmit()做的工作最后会落实到dev ->;hard_start_xmit(),而dev->;hard_start_xmit()会调用实际的驱动程序来完成发送的任务 在这也就是el_start_xmit函数了.
首先 spin_lock_irqsave 就是保存flag,然后将设备锁住.
Netif_stop_queue 使系统暂停向驱动程序请求发送包.
然后进入无限循环
{
获得skb的长度,
pad这个参数,大概是为了填充慢以太网的字段而设定的吧??
Gp_start=0x800-(len+pad)   //是什么意思嗯???
Lp->;tx_pkt_start=gp_start,// 表示从gp_start的地址处作为发送的数据的开始地址?
outb_p(AX_SYS, AX_CMD); 看起来像是给ax状态寄存器写入AX_SYS ,而ax_sys预定义为0x40 注释为loadthe buffer. 莫非只此方法就可以是网卡装载一个上层来得skb?

Inb_p(RX_STATUS); //从一个端口读取一个字节,其中inb_p() 会一直阻塞直到从端口得到字节为止 ,看这个意思只是读取,不会改变端口的值,可是程序中的注释维和说clear the status呢???
而后关闭自旋锁.
随后的三个语句程序中有注释, 其中outsb(DATAPORT,buf,len); 就是向ioaddr+oxof发送以buff开始的一个长度为len的字节,
随后的pad填充数据段的尾部,看来前面对pad用途的判断还算是正确的
是不是这样一个数据报就算完全填充完毕,应该是一个封装了以太网头的数据帧了.
接着又调用outw(gp_start,GP_LOW); 重新把gp_low赋值为gp_start ,   这个复位会直接影响发送时读取数据读取地址吧,各个寄存器至今也是互相协调工作的应该。不过这个和硬件有关应该。
如果ip->;loading!=2,那么就调用了outb(AXMIT,AX_CMD)  //真的就把数据帧发送出去了.
结束循环
完成收尾工作.

}无限循环结束


下面看超时的处理函数 el_timeout , 在那里引起这个函数调用呢?为什么会超时呢? 会不会是一直处于接受状,态,接受的数据量较大,虽然网卡处于双工状态,仍旧会发生无法分得处理机会呢?
仍旧是对网卡本身寄存器的写入操作,
首先中断正在进行的事情,而后单独占用oxA8地址, 再关闭中断请求,放开对缓冲区的访问(应该是网卡缓冲区,不是skb吧???) 而后进入接收状态
netif_wake_queue();会使得上层协定开始传送新的资料下来


现在el1_close()函数
netif_stop_queue 使系统暂时停止发送数据包.
释放中断号.
置状态字寄存器为reset

设定多波和读取状态信息就不说了,完全使寄存器的读写.应该适合硬件设计有关系,人家就定义写一个0x07给某个寄存器就是干什么事情.

Dev->;do_ioctl =netdev_ioctl  理解为内核态的ioctl应该没有错误吧J
Get_user从用户空间获得命令 根据不同的命令执行不同的函数
copy_from_user copy_to_user 的意思就是将得到的结果返回给用户空间或者从用户空间读取值,这个用户空间就是由useraddr来衔接用户命令和内核的调用 。偶是这么以为的

至此所有函数就分析完了
clean_module就不用说了,不外乎释放一些变量和i/o资源



对简单的网卡驱动的过程有了一个大概地认识,不过对那些寄存器的读写还是糊里糊涂的,是不是真的和网卡硬件的设置有关系呢?


参考了交叉索引和李元佳的tcp/ip协议占阅读笔记
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP