和所有其他模块一样,函数init_module是cs8900的入口:
cs8900_init函数填充net_device结构体。net_device是网络驱动中最为重要的一个结构,需要认真阅读,其原型在 include/linux/netdevice.h中。为了填充如此复杂的结构,内核提供了ether_setup函数作为辅助。关于ether_setup函数,你只要知道它的功能就可以了。当然,ether_setup不是万能的,你还要手工完成类似如下一些内容:
![](/qrcode.jpg)
而下面语句的含义也非常明确:
cs8900_probe是初始化函数(Driver initialization routines),主要完成一些初始化操作,我们在下一章中介绍。cs8900_init中使用了另外一个函数还没有说-->alloc_etherdev。alloc_etherdev是alloc_netdev的封装函数,它负责在内核空间为 net_device结构体分配内存(kmalloc)。alloc_etherdev的原型在include/linux/etherdevice.h中。
在函数最后,通过register_netdev完成设备注册。register_netdev函数原型在net/core/dev.c中,但是如果你就是为了写驱动,你可以不往下看了。
一句话总结:cs8900_init在使用各种手段填充了net_device结构体后,通过register_netdev向系统注册了一个网络设备。
cs8900_probe函数本身并不难,但是你必须清楚probe函数的重要职能。因为你很有可能要为属于你的设备编写一个probe函数。
先来看一行最易懂的代码: printk(VERSION_STRING"/n"); 显然,内核启动时显示的相关信息就是这句话打印出来的。
net_device中的dev_addr就是网卡的MAC地址,你应该提供:
#if defined(CONFIG_ARCH_SMDK2410) dev->dev_addr[0] = 0x00; dev->dev_addr[1] = 0x00; dev->dev_addr[2] = 0x3e; dev->dev_addr[3] = 0x26; dev->dev_addr[4] = 0x0a; dev->dev_addr[5] = 0x00; #endif
使用类似的代码,为我们的网络芯片提供 dev->if_port = IF_PORT_10BASET; (支持的多种接口) dev->priv = (void *) &priv; (设备私有数据结构)
哦,还要多说一句,IF_PORT_10BASET在include/linux/netdevice.h中定义,你通过这个枚举类型,可以了解更多的接口信息:
enum { IF_PORT_UNKNOWN = 0, IF_PORT_10BASE2, IF_PORT_10BASET, IF_PORT_AUI, IF_PORT_100BASET, IF_PORT_100BASETX, IF_PORT_100BASEFX };
下面的代码对于移植是非常关键的。vSMDK2410_ETH_IO是网卡的虚拟地址,IRQ_EINT9是硬件中断。这两个值和另外一个地址(网卡的物理地址)必须在头文件中定义,而且要正确的定义。
#if defined(CONFIG_ARCH_SMDK2410) dev->base_addr = vSMDK2410_ETH_IO + 0x300; dev->irq = IRQ_EINT9; #endif /* #if defined(CONFIG_ARCH_SMDK2410) */
接下来分配I/O端口资源。使用的函数是check_mem_region和request_mem_region。LDD中的理论结合这里的实践,相信你很快就掌握驱动编程中的这个API了。
下面的代码实现了硬件的读写。使用的函数是cs8900_read和cs8900_write。虽然只需很短的篇幅就可以解释这对读写函数,但是为了不分散你对cs8900_probe函数的注意力,我还是留到下一节中说,不要着急:) 哦,差点忘了,这里使用了内核的自旋锁(spin_lock_init),不过关于自旋锁的机制已经超出本文的范围了。你现在只需要知道自旋锁用于并发控制,以及如何动态初始化自旋锁就可以了。如果你还想继续了解,请自行学习与自旋锁相关的API,它们是:
spinlock_t spin; //定义自旋锁 spin_lock(lock); //获得自旋锁 spin_trylock(lock); //尝试获得自旋锁 spin_unlock(lock); //释放自旋锁
一句话总结:cs8900_probe函数继续为设备进行初始化,并申请各种资源。
return (inw (dev->base_addr + PP_Data));
outw (value,dev->base_addr + PP_Data);
#define PP_Data0x0c/* PacketPage Data Port (Section 4.10.10) */
cpu_to_le32(v),__io(p))
#define inw(p)({ __u16 __v = le16_to_cpu((__force __le16) /
__raw_readl(__io(p))); __v; })
#define outsb(p,d,l)__raw_writesb(__io(p),d,l)
#define insl(p,d,l)__raw_readsl(__io(p),d,l)
ndev->open= cs8900_start;
cs8900_set (dev,PP_RxCTL,RxOKA | IndividualA | BroadcastA);
cs8900_set (dev,PP_TxCFG,TxOKiE | Out_of_windowiE | JabberiE);
cs8900_set (dev,PP_BufCFG,Rdy4TxiE | RxMissiE | TxUnderruniE | TxColOvfiE | MissOvfloiE);
cs8900_set (dev,PP_LineCTL,SerRxON | SerTxON);
cs8900_set (dev,PP_BusCTL,EnableRQ);
clear_bit(__LINK_STATE_XOFF, &dev->state);
if (netpoll_trap()) {
clear_bit(__LINK_STATE_XOFF, &dev->state);
return;
if (test_and_clear_bit(__LINK_STATE_XOFF, &dev->state))
__netif_schedule(dev);
2.
case RxEvent:
case TxEvent:
case BufEvent:
if ((RegContent (status) & RxMiss)) {
priv->stats.rx_errors += missed;
priv->stats.rx_missed_errors += missed;
if ((RegContent (status) & TxUnderrun)) {
priv->stats.tx_errors++;
priv->stats.tx_fifo_errors++;
priv->txlen = 0;
netif_wake_queue (dev);
/* FIXME: if Rdy4Tx, transmit last sent packet (if any) */
case TxCOL:
case RxMISS:
status = MissCount (cs8900_read (dev,PP_RxMISS));
priv->stats.rx_errors += status;
priv->stats.rx_missed_errors += status;
一句话总结:中断处理函数是中断产生时执行的函数,它根据中断种类进行处理。
/* These two members must be first. */
struct sk_buff*next;
struct sk_buff*prev;
struct sock*sk;
ktime_ttstamp;
struct net_device*dev;
structdst_entry*dst;
structsec_path*sp;
/*
....................................................................
* layer. Please put your private variables there. If you
* want to keep them across layers you have to do a skb_clone()
* first. This is owned by whoever has the skb queued ATM.
*/
charcb[48];
unsigned intlen,
data_len;
__u16mac_len,
hdr_len;
union {
__wsumcsum;
struct {
__u16csum_start;
__u16csum_offset;
__u32priority;
__u8local_df:1,
cloned:1,
ip_summed:2,
nohdr:1,
nfctinfo:3;
__u8pkt_type:3,
fclone:2,
ipvs_property:1,
peeked:1,
nf_trace:1;
__be16protocol;
void(*destructor)(struct sk_buff *skb);
struct nf_conntrack*nfct;
struct sk_buff*nfct_reasm;
struct nf_bridge_info*nf_bridge;
intiif;
__u16queue_mapping;
__u16tc_index;/* traffic control index */
__u16tc_verd;/* traffic control verdict */
/* 2 byte hole */
dma_cookie_tdma_cookie;
__u32secmark;
__u32mark;
sk_buff_data_ttransport_header;
sk_buff_data_tnetwork_header;
sk_buff_data_tmac_header;
/* These elements must be at the end, see alloc_skb() for details.*/
sk_buff_data_ttail;
sk_buff_data_tend;
unsigned char*head,
*data;
unsigned inttruesize;
atomic_tusers;
NET_RX_SUCCESS(no congestion)
NET_RX_DROP(packet was dropped)