- 论坛徽章:
- 0
|
int register_netdev(struct net_device *dev)
void unregister_netdev(struct net_device *dev);
struct net_device *alloc_netdev(int sizeof_priv, const char *name, void (*setup)(struct
net_device *));
struct net_device *alloc_etherdev(int size_prive){
return alloc_netdev(sizeof_priv, "eth%d", ether_setup);
}
void free_netdev(struct net_device *dev);
==================================================================================
void xxx_init(struct net_device *dev);
{
struct xxx_priv *priv;
//检查设备是否存在和设备所使用的硬件资源
xxx_hw_init()
//初始化以太网设备的公用成员
ether_setup(dev);
//设备成员函数指针
dev->open= xxx_open;
dev->stop=xxx_release;
dev->set_config = xxx_config;
dev->hard_start_xmit = xxx_tx;
dev->do_ioctl = xxx_ioctl;
dev->get_stats = xxx_stats;
dev->change_mtu = xxx_change_mtu;
dev->rebuild_header = xxx_rebuild_header;
dev->hard_header = xxx_header;
dev->tx_timeout = xxx_tx_timeout;
dev->watchdog_timeo= timeout;
//如果使用NAPI 设置poll 函数
if(use_napi)
{
dev->poll = xxx_poll;
}
//取得私有信息并初始化它
priv = netdev_priv(dev)
...//初始化设备私有数据区
}
xxx_hw_init() 函数完成硬件想管你的初始化操作
1探测xxx网络设备是否存在。
2探测硬件的具体配置
3申请设备所需要的各种硬件资源,如,request_region()函数进行io端口的申请,可以放在open;
=====================================================
网络设备的打开
1使能设备使用的硬件资源,申请IO区域,中断和DMA通道等
2调用内核提供的netif_start_queue()函数,激活设备发送队列
关闭相反
========================================================
数据发送流程
1网络设备驱动将从上层协议传递过来的sk_buff参数获得的数据包的有效数据和长度,将有效数据
放入临时的缓冲区
2对于以太网,如果有效数据的长度小于以太网冲突检测所要求数据帧的最小长度ETH_ZLEN,填0
3设置硬件的寄存器,驱使网络设备发送
int xxx_tx(struct sk_buff *skb, struct net_device *dev)
{
int len;
char *data, shortpkt[ETH_ZLEN];
//获得有效数据指针和长度
data = skb->data;
len = skb->len;
if(lendata, skb->len);
len = ETH_ZLEN;
data = shortpkt;
}
dev->trans_start = jiffies;
//设置硬件寄存器发送
xxx_hw_tx(data,len,dev);
....
}
void xxx_tx_timeout(struct net_device *dev)
{
...
netify_wake_queue(dev);
}
=================================================================================
数据接收流程
中断处理:判断中断类型,若为接收中断,则读取接受到的数据,分配sk_buff数据结构和数据缓冲
区,将接收到的数据复制到数据缓冲区,并调用netify_rx()将sk_buff将传递给上层协议。
static void xxx_interrupt(int irq, void * dev_id,struct pt_regs *regs)
{
...
switch(status &ISQ_EVENT_MASK)
{
case ISQ_RECEIVER_EVENT:
xxx_rx(dev);
break;
....
}
}
static void xxx_rx(struct xxx_device *dev)
{
...
length = get_rev_len(...);
//分配新的套接字缓冲区
skb= dev_alloc_skb(length+2);
skb_reserve(skb, 2);
skb->dev = dev;
//读取硬件接收的数据
insw(ioaddr + RX_FRAME_PORT,skb_put(skb,length),length)>>1);
if(length &1)
skb->data[length-1] = inw(ioaddr+RX_FRAME_PORT);
//获取上层协议类型
skb->protocal = eth_type_trans(skb,dev);
//交给上层
netif_rx(skb);
dev->last_rx = jiffies;
}
=====================
若是NAPI,可以通过poll方式接收数据包
static int xxx_poll(struct net_device *dev, int *budget)
{
int npackets=0, quota = min(dev->quota, *budget);
struct sk_buff *skb;
struct xxx_priv *priv = netdev_priv(dev);
struct xxx_packet *pkt;
while(npackets rx_queue)
{
//从队列中取出数据包
pkt = xxx_dequeue_buf(dev);
//接下来的操作和硬件中断一致
skb= dev_alloc_skb(pkt->datalen+2);
if(!skb){.... continue;}
skb_reserve(skb,2);
memcpy(skb_put(skb,pkt->datalen),pkt->data,pkt->datalen);
skb->dev = dev;
skb->protocal = eth_type_trans(skb,dev);
//
netif_receive_skb(skb);
//更改统计数据
priv->stats.rx_packets++;
priv->stats.rx_bytes += pkt->datalen;
xxx_release_buffer(pkt);
}
*budget -=npackets;
dev->quota -=npackets;
if(!priv->rx_queue)
{
netif_rx_complete(dev);
xxx_enable_rx_int(..);
return 0;
}
return 1;
}
budget 是初始化阶段分配给接口的weight,quota是从所有接口中能接收的数据包最大数
以POLL方式接收数据包,但是仍需要首次数据包中断来触发poll过程
static void xxx_poll_interrupt(int irq, void *dev_id, struct pt_regs)
{
switch(status&ISQ_EVENT_MASK)
{
case ISQ_RECEIVE_EVENT:
...//获取数据包
xxx_disable_rx_int(...);
netif_rx_schedule(dev);
break;
../其他中断
}
}
netif_rx_schedule()将设备的poll方法加入到网络层的poll处理队列中,排队并准备接收数据包
,最终触发NET_RX_SOFTIRQ软中断,通知网络层接收数据包。
================================================================================
网络连接状态
void netif_carrier_on(struct net_device);
void netif_carrier_off(struct net_device);
void netif_carrier_ok(struct net_device);
网络设备驱动用定时器检查链路状态
static void xxx_timer(unsigned long data)
{
struct net_device =(struct net_device *)data;
un16 link;
....
if(!(dev->flags & IFF_UP)
{
goto set_timer;
}
//获得链路的连接状态
if(link= xxx_chr_link(dev))
{
if(!(dev->flags &IFF_RUNNING))
{ netif_carrier_on(dev);
dev->flags |= IFF_RUNNING;
prink(KERN_DEBUG "%s:link up\n",dev->name);
}
}
else{
if(dev->flags &IFF_RUNNING)
{ netif_carrier_off(dev);
dev->flags &= ~IFF_RUNNING;
prink(KERN_DEBUG "%s:link down\n",dev->name);
}
}
set_timer:
priv->timer.expires =jiffies +1*HZ;
priv->timer.data =( unsigned long )dev;
priv->timer.function = &xxx_timer;
add_timer(&priv->timer);
}
在open函数中初始化定时器
=================================================================================
#define netify_running(dev) (dev->flag & IFF_UP)
///检测设备是否正在运行
struct net_device_stats *xxx_stats(struct net_device *dev)
{
struct xxx_priv *priv = netdev->priv(dev);
return &priv->stats;
}
struct net_device_stats
{
unsigned long rx_packets;
unsigned long tx_packets;
unsigned long rx_bytes;
unsigned long tx_bytes;
unsigned long rx_errors;
unsigned long tx_errors;
...
}
net_device_stats其中的统计信息的修改应该在设备驱动的与发送和接收相关的函数中完成。
void xxx_tx_timeout(struct net_device *dev)
{
struct xxx_priv *priv =netdev_priv(dev);
...
priv->stats.tx_error++;
...
}
//中断处理函数
static void xxx_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
switch(status&ISQ_EVENT_MASK)
{
....
case ISQ_TRANSMITTER_EVENT:
priv->stats.tx_packets++;
netif_wait_queue(dev)
if((status&(TX_OK|TX_LOST_CRS|TX_SQR_ERROR|TX_LATE_COL|TX_16_COL))!= TX_OK)
{
if((status&TX_OK)==0)
priv->stats.tx_errors++;
if((status&TX_LOST_CRS)==0)
priv->stats.tx_carriter_errors++;
if((status&TX_SQR_ERROR)==0)
priv->stats.tx_heartbeat_errors++;
if((status&TX_LATE_COL)==0)
priv->stats.tx_window_errors++;
if((status&TX_TX_16_COL)==0)
priv->stats.tx_aborted_errors++;
}
case ISQ_RX_MISS_EVENT:
priv->stats.rx_missed_errors+= (status>>6);
break;
case ISQ_TX_COL_EVENT:
priv->status.collision +=(status>>6);
break;
}
}
}
本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u2/69624/showart_1071890.html |
|