网络设备驱动(Linux kernel 4.9.x)
网络接口层
- 网络协议接口层:net/core/dev.c
1.1 重要函数
int dev_queue_xmit(struct sk_buff *skb); /*发送*/
int netif_rx(struct sk_buff *skb); /*接收*/
1.2 重要结构体:套接字缓冲区
struct sk_buff{
...
...
/* These elements must be at the end, see alloc_skb() for details. */
sk_buff_data_t tail; /*与data指针相对应*/
sk_buff_data_t end; /*与head指针相对应*/
unsigned char *head, /*指向内存中已分配的用于承载网络数据的缓冲区的起始地址*/
*data; /*需随着当前拥有sk_buff的协议层的变化进行相应的移动*/
...
}
1.3 套接字缓冲区操作函数
1.3.1 分配
static inline struct sk_buff *alloc_skb(unsigned int size, gfp_t priority);
static inline struct sk_buff *netdev_alloc_skb(struct net_device *dev, unsigned int length);
NOTE:dev_alloc_skb(unsigned int length)== netdev_alloc_skb(NULL, unsigned int length);
1.3.2 释放
void kfree_skb(struct sk_buff *skb); /* 内核内部使用 */
void dev_kfree_skb/consume_skb(struct sk_buff *skb); /* 设备驱动程序使用 */
static inline void dev_kfree_skb_irq(struct sk_buff *skb); /* when caller drops a packet from irq context, replacing kfree_skb(skb) */
static inline void dev_kfree_skb_any(struct sk_buff *skb); /* when caller doesn't know its current irq context, replacing kfree_skb(skb) */
static inline void dev_consume_skb_irq(struct sk_buff *skb); /* when caller consumes a packet from irq context. Typically used in place of consume_skb(skb) in TX completion path */
static inline void dev_consume_skb_any(struct sk_buff *skb); /* when caller doesn't know its current irq context, and consumed a packet. Used in place of consume_skb(skb) */
1.3.3 移动数据缓冲区指针
skb_put: 向下移动tail指针。
skb_push: 向上移动data指针。
skb_pull: 向下移动data指针。一般用于下层协议向上层协议移交数据包,使data指针指向新的一层协议的协议头。
skb_reserve: 同时向下移动data和tail指针。主要用于在存储空间的头部预留len长度的空隙。
- 网络设备接口层:include/linux/netdevice.h
struct net_device{
char name[IFNAMSIZ]; /*网络设备名称*/
unsigned long mem_end; /*设备使用共享内存的结束地址*/
unsigned long mem_start; /*设备使用共享内存的起始地址*/
unsigned long base_addr; /*网络设备的I/O基地址*/
int irq; /*设备使用的中断号*/
...
unsigned char if_port; /*指定多端口设备使用哪一端口*/
unsigned char dma; /*分配给设备的DMA通道*/
unsigned int mtu; /*最大传输单元*/
unsigned short type; /*接口的硬件类型*/
unsigned short hard_header_len;/*网络设备的硬件头长度*/
...
unsigned int flags; /*网络接口标志,以IFF_(interface flags)开头*/
unsigned char *dev_addr; /*存放设备的硬件地址*/
unsigned char broadcast[MAX_ADDR_LEN]; /*存放设备广播地址*/
const struct net_device_ops *netdev_ops; /*指向网络设备的操作函数。详见net_device_ops结构体*/
unsigned long last_rx; /*记录最后一次接收到数据包时的时间戳*/
...
...
}
- 设备驱动功能层:驱动工程师要实现的!
1) 实现网络设备接口层net_device下net_device_ops的一些成员函数。
如xxx_open、xxx_stop、xxx_start_xmit、xxx_change_mtu等函数。
2) 实现中断处理函数(负责网络数据包的接收)。
读取硬件上接收的数据包并传送给上层协议。
3) 对于特定的设备,还可以定义其相关私有数据和操作,并封装为一个私有信息结构体xxx_private,让其指针被赋值给net_device的priv成员。
- 网络设备与媒介层:可以是虚拟的。
1) 直接对应于实际的硬件设备。
2) 可以定义一组宏和一组访问设备内部寄存器的函数。
补充:
<-------------------------------------------------------------------------- 发送
物理层 数据链路层 网络层 传输层 会话层 表示层 应用层
--------------------------------------------------------------------------> 接收
(详解)设备驱动功能层
- net_device结构体的填充/释放
struct net_device *alloc_netdev(int sizeof_priv, const char *name, void(*setup)(struct net_device*)); /*填充*/
struct net_device *alloc_etherdev(int sizeof_priv); /*填充(以太网设备)*/
NOTE: 第一个参数为设备私有成员的大小。
NOTE: alloc_etherdev最后是以alloc_netdev_mqs(sizeof_priv, "eth%d", NET_NAME_UNKNOWN, ether_setup, txqs, rxqs);实现的。
void free_netdev(struct net_device *dev); /*释放*/
- 网络设备驱动的注册/注销
int register_netdev(struct net_device *dev); /*注册*/
void unregister_netdev(struct net_device *dev); /*注销*/
- 网络设备初始化:初始化net_device下的某些成员
例如:
struct net_device *dev;
dev->irq = res->start;
dev->netdev_ops = &xxx_netdev_ops;
dev->watchdog_timeo = 2 * HZ;
dev->base_addr = 0;
dev->features = NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_TSO;
dev->vlan_features = dev->features;
dev->features |= NETIF_F_RXCSUM;
dev->hw_features = dev->features;
dev->priv_flags |= IFF_UNICAST_FLT;
dev->gso_max_segs = MV643XX_MAX_TSO_SEGS;
- 网络设备的打开/释放:.ndo_open/.ndo_stop
打开:
1)使能设备使用的硬件资源,申请I/O 区域、中断和DMA通道等。
2)激活设备发送队列(netif_start_queue()等函数)。
释放:
1)释放硬件资源。
2)停止设备发送队列(netif_stop_queue()等函数)。
- 启动数据包发送:.ndo_start_xmit
1)获取从上层传递过来sk_buff参数的有效数据和长度。
/* 对于以太网,如果有效数据的长度小于以太网冲突检测所要求数据帧的最小长度ETH_ZLEN,则给临时缓冲区的末尾填充0 */
2)设置硬件的寄存器,驱使网络设备进行数据发送操作。
- 发送超时:.ndo_tx_timeout
主要调用函数;netif_wake_queue();
- 数据接收:一般在中断处理函数中实现/调用。
1)读取接收到的数据,分配sk_buffer 数据结构和数据缓冲区;
2)复制数据到数据缓冲区;
3)调用netif_rx将sk_buffer传递给上层协议。
实例:某以太网设备
static int xxx_rx(struct net_device *dev)
{
...
unsigned char *data;
struct sk_buff *skb;
val = pfifo_pop(IND_FIFO_PORT_LO(priv->id));
...
len = (val & FIFO_PTR_FRAMELEN_MASK) >> FIFO_PTR_FRAMELEN_SHIFT;
skb = netdev_alloc_skb(ndev, len); /*分配sk_buff*/
...
data = skb_put(skb, len); /*数据缓冲区*/
memcpy_fromio(data, priv->sram_base + frameno * 1560, len); /*复制数据*/
pfifo_push(EMPTY_PTR_FIFO(priv->id),
FIFO_PTR_SEGMENT(seg) | FIFO_PTR_FRAMENO(frameno));
skb->protocol = eth_type_trans(skb, ndev); /*获取上层协议类型*/
netif_rx(skb); /*把数据交给上层*/
ndev->stats.rx_packets++;
ndev->stats.rx_bytes += len;
}
static irqreturn_t xxx_interrupt(int irq, void *dev_id)
{
...
xxx_rx(dev);
...
}
- 网络连接状态
硬件知识;
1)网络适配器硬件电路可以检测出链路上是否有载波;
2)载波反映了网络的连接是否正常。
相关函数:
void netif_carrier_on(struct net_device *dev);
void netif_carrier_off(struct net_device *dev);
static inline bool netif_carrier_ok(const struct net_device *dev);
用法:多用于检查link状态。
- 统计数据:struct net_device_stats stats; ------net_device结构体下的成员
NOTE:
1)该信息结构体适宜包含在设备的私有信息结构体中。
2)统计信息的修改在数据的发送和接收中完成。具体函数包含中断处理函数、数据发送、发送超时和数据接收等函数。
实例分析:仅设备驱动功能层。
cs89x0网卡设备驱动(drivers/net/ethernet/cirrus/cs89x0.c)
主要工作:
1)填充CS8900的私有信息结构体; --- struct net_local *lp;
2)填充设备初始化模板; --- 初始化net_device结构体,将其注册入内核。
3)填充设备打开与释放函数代码(net_open/net_close);
3)填充设备发送数据包函数(net_send_packet)模板;
4)填充设备驱动程序的中断处理程序及具体的数据包接收函数;
代码分析(省略返回值判断部分):
/*私有信息结构体net_local*/
struct net_local {
int chip_type; /* one of: CS8900, CS8920, CS8920M */
char chip_revision; /* revision letter of the chip ('A'...) */ /*revision不同,记录的send_cmd将不同*/
int send_cmd; /* the proper send command: TX_NOW, TX_AFTER_381, or TX_AFTER_ALL */
int auto_neg_cnf; /* auto-negotiation word from EEPROM */
int adapter_cnf; /* adapter configuration from EEPROM */
int isa_config; /* ISA configuration from EEPROM */
int irq_map; /* IRQ map from EEPROM */
int rx_mode; /* what mode are we in? 0, RX_MULTCAST_ACCEPT, or RX_ALL_ACCEPT */
int curr_rx_cfg; /* a copy of PP_RxCFG */
int linectl; /* either 0 or LOW_RX_SQUELCH, depending on configuration. */
int send_underrun; /* keep track of how many underruns in a row we get */
int force; /* force various values; see FORCE* above. */
spinlock_t lock;
void __iomem *virt_addr;/* CS89x0 virtual address. */
#if ALLOW_DMA
int use_dma; /* Flag: we're using dma */
int dma; /* DMA channel */
int dmasize; /* 16 or 64 */
unsigned char *dma_buff; /* points to the beginning of the buffer */
unsigned char *end_dma_buff; /* points to the end of the buffer */
unsigned char *rx_dma_ptr; /* points to the next packet */
#endif
};
/*数据包接收函数*/
static void net_rx(struct net_device *dev)
{
struct net_local *lp = netdev_priv(dev);
struct sk_buff *skb;
int status, length;
status = ioread16(lp->virt_addr + RX_FRAME_PORT);
length = ioread16(lp->virt_addr + RX_FRAME_PORT); //读取数据包接收长度
...
/*分配sk_buff缓冲区*/
skb = netdev_alloc_skb(dev, length + 2);
...
skb_reserve(skb, 2); /* longword align L3 header */
/*将硬件中的数据包放入数据缓冲区*/
readwords(lp, RX_FRAME_PORT, skb_put(skb, length), length >> 1);
if (length & 1)
skb->data[length-1] = ioread16(lp->virt_addr + RX_FRAME_PORT);
...
/*获取上层协议类型*/*/
skb->protocol = eth_type_trans(skb, dev);
/*将数据交给上层*/
netif_rx(skb);
/*更改统计信息*/
dev->stats.rx_packets++;
dev->stats.rx_bytes += length;
}
/*中断处理程序*/
static irqreturn_t net_interrupt(int irq, void *dev_id)
{
struct net_device *dev = dev_id;
...
...
while ((status = ioread16(lp->virt_addr + ISQ_PORT))) {
...
switch (status & ISQ_EVENT_MASK) {
case ISQ_RECEIVER_EVENT:
/* Got a packet(s). */
net_rx(dev);
break;
case ISQ_TRANSMITTER_EVENT:
dev->stats.tx_packets++;
netif_wake_queue(dev); /* Inform upper layers. */
...
...
break;
/*其他中断事件*/
...
...
}
}
return IRQ_RETVAL(handled);
}
/*发送超时函数*/
static void net_timeout(struct net_device *dev)
{
...
netif_wake_queue(dev); //尝试重启适配器
}
/*发送数据包函数*/
static netdev_tx_t net_send_packet(struct sk_buff *skb, struct net_device *dev)
{
struct net_local *lp = netdev_priv(dev);
unsigned long flags;
...
...
spin_lock_irqsave(&lp->lock, flags); //使用自旋锁防止多个数据包同时写入硬件
netif_stop_queue(dev);
/*启动一个传输序列*/
iowrite16(lp->send_cmd, lp->virt_addr + TX_CMD_PORT);
iowrite16(skb->len, lp->virt_addr + TX_LEN_PORT);
/*检测芯片是否为数据包分配了内存(检测硬件是否处于发送READY状态)*/
if ((readreg(dev, PP_BusST) & READY_FOR_TX_NOW) == 0) {
spin_unlock_irqrestore(&lp->lock, flags);
cs89_dbg(0, err, "Tx buffer not free!\n");
return NETDEV_TX_BUSY;
}
/*数据包内容写入内存 */
writewords(lp, TX_FRAME_PORT, skb->data, (skb->len + 1) >> 1);
spin_unlock_irqrestore(&lp->lock, flags);
dev->stats.tx_bytes += skb->len;
dev_consume_skb_any(skb); //释放sk_buff
/*在这里既不调用netif_wake_queue(),也未调用netif_start_queue().
*原因是:这两种方法都会导致该包完全输出之前,通过net_send_packet()运行另一个下半部分。
*故只需返回等待tx完成中断处理并重启netdevice层
*/
return NETDEV_TX_OK;
}
/*设备打开函数*/
static int net_open(struct net_device *dev)
{
struct net_local *lp = netdev_priv(dev);
...
if (dev->irq < 2) {
...
writereg(dev, PP_BusCTL, ENABLE_IRQ | MEMORY_ON);
for (i = 2; i < CS8920_NO_INTS; i++) {
if ((1 << i) & lp->irq_map) {
if (request_irq(i, net_interrupt, 0, dev->name,
dev) == 0) {
dev->irq = i;
write_irq(dev, lp->chip_type, i);
break;
}
}
}
...
} else {
#if !defined(CONFIG_CS89x0_PLATFORM)
...
#endif
...
write_irq(dev, lp->chip_type, dev->irq);
ret = request_irq(dev->irq, net_interrupt, 0, dev->name, dev);
...
}
...
/*设置以太网地址*/
for (i = 0; i < ETH_ALEN / 2; i++)
writereg(dev, PP_IA + i * 2, (dev->dev_addr[i * 2] | (dev->dev_addr[i * 2 + 1] << 8)));
/*在测试接口时,禁用中断*/
writereg(dev, PP_BusCTL, MEMORY_ON);
/* Set the LineCTL quintuplet based on adapter configuration read from EEPROM */
if ((lp->adapter_cnf & A_CNF_EXTND_10B_2) &&
(lp->adapter_cnf & A_CNF_LOW_RX_SQUELCH))
lp->linectl = LOW_RX_SQUELCH;
else
lp->linectl = 0;
/*检查以确保有可用的“正确”硬件*/
switch (lp->adapter_cnf & A_CNF_MEDIA_TYPE) {
case A_CNF_MEDIA_10B_T:
result = lp->adapter_cnf & A_CNF_10B_T;
break;
...
...//其他适配器配置选项
}
...
...
/*将硬件设置为配置选项*/
switch (lp->adapter_cnf & A_CNF_MEDIA_TYPE) {
case A_CNF_MEDIA_10B_T:
result = detect_tp(dev);
if (result == DETECTED_NONE) {
pr_warn("%s: 10Base-T (RJ-45) has no cable\n",
dev->name);
if (lp->auto_neg_cnf & IMM_BIT) /* check "ignore missing media" bit */
result = DETECTED_RJ45H; /* Yes! I don't care if I see a link pulse */
}
break;
...
...//其他适配器配置选项
}
switch (result) {
case DETECTED_NONE:
pr_err("%s: no network cable attached to configured media\n",
dev->name);
goto release_dma;
...
...//其他检测结果
}
/*同时打开接收和发送操作*/
writereg(dev, PP_LineCTL,
readreg(dev, PP_LineCTL) | SERIAL_RX_ON | SERIAL_TX_ON);
/* Receive only error free packets addressed to this card */
lp->rx_mode = 0;
writereg(dev, PP_RxCTL, DEF_RX_ACCEPT);
lp->curr_rx_cfg = RX_OK_ENBL | RX_CRC_ERROR_ENBL;
if (lp->isa_config & STREAM_TRANSFER)
lp->curr_rx_cfg |= RX_STREAM_ENBL;
#if ALLOW_DMA
set_dma_cfg(dev);
#endif
writereg(dev, PP_RxCFG, lp->curr_rx_cfg);
writereg(dev, PP_TxCFG, (TX_LOST_CRS_ENBL |
TX_SQE_ERROR_ENBL |
TX_OK_ENBL |
TX_LATE_COL_ENBL |
TX_JBR_ENBL |
TX_ANY_COL_ENBL |
TX_16_COL_ENBL));
writereg(dev, PP_BufCFG, (READY_FOR_TX_ENBL |
RX_MISS_COUNT_OVRFLOW_ENBL |
#if ALLOW_DMA
dma_bufcfg(dev) |
#endif
TX_COL_COUNT_OVRFLOW_ENBL |
TX_UNDERRUN_ENBL));
/* now that we've got our act together, enable everything */
writereg(dev, PP_BusCTL, (ENABLE_IRQ
| (dev->mem_start ? MEMORY_ON : 0) /* turn memory on */
#if ALLOW_DMA
| dma_busctl(dev)
#endif
));
/*激活设备发送队列*/
netif_start_queue(dev);
return 0;
...
}
/*设备释放函数*/
static int net_close(struct net_device *dev)
{
#if ALLOW_DMA
struct net_local *lp = netdev_priv(dev);
#endif
/*停止设备发送队列*/
netif_stop_queue(dev);
/*硬件寄存器清0*/
writereg(dev, PP_RxCFG, 0);
writereg(dev, PP_TxCFG, 0);
writereg(dev, PP_BufCFG, 0);
writereg(dev, PP_BusCTL, 0);
/*释放中断*/
free_irq(dev->irq, dev);
#if ALLOW_DMA
...
#endif
return 0;
}
/*设备操作结构体*/
static const struct net_device_ops net_ops = {
.ndo_open = net_open,
.ndo_stop = net_close,
.ndo_tx_timeout = net_timeout,
.ndo_start_xmit = net_send_packet,
.ndo_get_stats = net_get_stats,
.ndo_set_rx_mode = set_multicast_list,
.ndo_set_mac_address = set_mac_address,
#ifdef CONFIG_NET_POLL_CONTROLLER
.ndo_poll_controller = net_poll_controller,
#endif
.ndo_change_mtu = eth_change_mtu,
.ndo_validate_addr = eth_validate_addr,
};
/* 真正的probe函数*/
static int __init cs89x0_probe1(struct net_device *dev, void __iomem *ioaddr, int modular)
{
struct net_local *lp = netdev_priv(dev);
int i;
int tmp;
unsigned rev_type = 0;
int eeprom_buff[CHKSUM_LEN];
int retval;
/* 初始化设备私有结构体 */
...
iowrite16(PP_ChipID, ioaddr + ADD_PORT);
tmp = ioread16(ioaddr + DATA_PORT);
...
lp->virt_addr = ioaddr;
/* 获取芯片类型 */
rev_type = readreg(dev, PRODUCT_ID_ADD);
lp->chip_type = rev_type & ~REVISON_BITS;
lp->chip_revision = ((rev_type & REVISON_BITS) >> 8) + 'A';
/* 检查芯片类型和版本的目的:设置正确的发送命令*/
lp->send_cmd = TX_AFTER_381;
if (lp->chip_type == CS8900 && lp->chip_revision >= 'F')
lp->send_cmd = TX_NOW;
if (lp->chip_type != CS8900 && lp->chip_revision >= 'C')
lp->send_cmd = TX_NOW;
...
reset_chip(dev);
/* 读取芯片当前配置
...
*/
if ((readreg(dev, PP_SelfST) & (EEPROM_OK | EEPROM_PRESENT)) ==
(EEPROM_OK | EEPROM_PRESENT)) {
/* Load the MAC. */
for (i = 0; i < ETH_ALEN / 2; i++) {
unsigned int Addr;
Addr = readreg(dev, PP_IA + i * 2);
dev->dev_addr[i * 2] = Addr & 0xFF;
dev->dev_addr[i * 2 + 1] = Addr >> 8;
}
/* 加载适配器配置.
* Note: Barring any more specific information from some
* other source (ie EEPROM+Schematics), we would not know
* how to operate a 10Base2 interface on the AUI port.
* However, since we do read the status of HCB1 and use
* settings that always result in calls to control_dc_dc(dev,0)
* a BNC interface should work if the enable pin
* (dc/dc converter) is on HCB1.
* It will be called AUI however.
*/
lp->adapter_cnf = 0;
i = readreg(dev, PP_LineCTL);
/* Preserve the setting of the HCB1 pin. */
if ((i & (HCB1 | HCB1_ENBL)) == (HCB1 | HCB1_ENBL))
lp->adapter_cnf |= A_CNF_DC_DC_POLARITY;
/* Save the sqelch bit */
if ((i & LOW_RX_SQUELCH) == LOW_RX_SQUELCH)
lp->adapter_cnf |= A_CNF_EXTND_10B_2 | A_CNF_LOW_RX_SQUELCH;
/* Check if the card is in 10Base-t only mode */
if ((i & (AUI_ONLY | AUTO_AUI_10BASET)) == 0)
lp->adapter_cnf |= A_CNF_10B_T | A_CNF_MEDIA_10B_T;
/* Check if the card is in AUI only mode */
if ((i & (AUI_ONLY | AUTO_AUI_10BASET)) == AUI_ONLY)
lp->adapter_cnf |= A_CNF_AUI | A_CNF_MEDIA_AUI;
/* Check if the card is in Auto mode. */
if ((i & (AUI_ONLY | AUTO_AUI_10BASET)) == AUTO_AUI_10BASET)
lp->adapter_cnf |= A_CNF_AUI | A_CNF_10B_T |
A_CNF_MEDIA_AUI | A_CNF_MEDIA_10B_T | A_CNF_MEDIA_AUTO;
...
/* IRQ. Other chips already probe, see below. */
if (lp->chip_type == CS8900)
lp->isa_config = readreg(dev, PP_CS8900_ISAINT) & INT_NO_MASK;
}
/* 首先检查是否有EEPROM. */
if ((readreg(dev, PP_SelfST) & EEPROM_PRESENT) == 0)
pr_warn("No EEPROM, relying on command line....\n");
else if (get_eeprom_data(dev, START_EEPROM_DATA, CHKSUM_LEN, eeprom_buff) < 0) {
pr_warn("EEPROM read failed, relying on command line\n");
} else if (get_eeprom_cksum(START_EEPROM_DATA, CHKSUM_LEN, eeprom_buff) < 0) {
/* Check if the chip was able to read its own configuration starting
at 0 in the EEPROM*/
if ((readreg(dev, PP_SelfST) & (EEPROM_OK | EEPROM_PRESENT)) !=
(EEPROM_OK | EEPROM_PRESENT))
pr_warn("Extended EEPROM checksum bad and no Cirrus EEPROM, relying on command line\n");
} else {
/* This reads an extended EEPROM that is not documented
* in the CS8900 datasheet.
*/
/* get transmission control word but keep the autonegotiation bits */
if (!lp->auto_neg_cnf)
lp->auto_neg_cnf = eeprom_buff[AUTO_NEG_CNF_OFFSET / 2];
/* Store adapter configuration */
if (!lp->adapter_cnf)
lp->adapter_cnf = eeprom_buff[ADAPTER_CNF_OFFSET / 2];
/* Store ISA configuration */
lp->isa_config = eeprom_buff[ISA_CNF_OFFSET / 2];
dev->mem_start = eeprom_buff[PACKET_PAGE_OFFSET / 2] << 8;
/* eeprom_buff has 32-bit ints, so we can't just memcpy it */
/* store the initial memory base address */
for (i = 0; i < ETH_ALEN / 2; i++) {
dev->dev_addr[i * 2] = eeprom_buff[i];
dev->dev_addr[i * 2 + 1] = eeprom_buff[i] >> 8;
}
...
}
/* 允许使用多个收发器. If they force multiple, autosense */
{
int count = 0;
if (lp->force & FORCE_RJ45) {
lp->adapter_cnf |= A_CNF_10B_T;
count++;
}
if (lp->force & FORCE_AUI) {
lp->adapter_cnf |= A_CNF_AUI;
count++;
}
if (lp->force & FORCE_BNC) {
lp->adapter_cnf |= A_CNF_10B_2;
count++;
}
if (count > 1)
lp->adapter_cnf |= A_CNF_MEDIA_AUTO;
else if (lp->force & FORCE_RJ45)
lp->adapter_cnf |= A_CNF_MEDIA_10B_T;
else if (lp->force & FORCE_AUI)
lp->adapter_cnf |= A_CNF_MEDIA_AUI;
else if (lp->force & FORCE_BNC)
lp->adapter_cnf |= A_CNF_MEDIA_10B_2;
}
...
...
lp->irq_map = 0xffff;
/* If this is a CS8900 then no pnp soft */
if (lp->chip_type != CS8900 &&
/* 检查ISA IRQ是否已被设置 */
(i = readreg(dev, PP_CS8920_ISAINT) & 0xff,
(i != 0 && i < CS8920_NO_INTS))) {
if (!dev->irq)
dev->irq = i;
} else {
i = lp->isa_config & INT_NO_MASK;
#ifndef CONFIG_CS89x0_PLATFORM
if (lp->chip_type == CS8900) {
/* Translate the IRQ using the IRQ mapping table. */
if (i >= ARRAY_SIZE(cs8900_irq_map))
pr_err("invalid ISA interrupt number %d\n", i);
else
i = cs8900_irq_map[i];
lp->irq_map = CS8900_IRQ_MAP; /* fixed IRQ map for CS8900 */
} else {
int irq_map_buff[IRQ_MAP_LEN/2];
if (get_eeprom_data(dev, IRQ_MAP_EEPROM_DATA,
IRQ_MAP_LEN / 2,
irq_map_buff) >= 0) {
if ((irq_map_buff[0] & 0xff) == PNP_IRQ_FRMT)
lp->irq_map = ((irq_map_buff[0] >> 8) |
(irq_map_buff[1] << 8));
}
}
#endif
if (!dev->irq)
dev->irq = i;
}
...
#if ALLOW_DMA
if (lp->use_dma) {
/*若支持DMA, 获取dma通道*/
get_dma_channel(dev);
...
} else
#endif
...
/*打印以太网地址*/
pr_cont(", MAC %pM\n", dev->dev_addr);
dev->netdev_ops = &net_ops; /*设置设备操作结构体*/
dev->watchdog_timeo = HZ;
cs89_dbg(0, info, "cs89x0_probe1() successful\n");
retval = register_netdev(dev); /*注册该网络设备*//*函数内部自动调用init函数,初始化设备驱动程序*/
...
return 0;
...
}
/*使用平台设备*/
#ifdef CONFIG_CS89x0_PLATFORM
static int __init cs89x0_platform_probe(struct platform_device *pdev)
{
struct net_device *dev = alloc_etherdev(sizeof(struct net_local)); /*分配以太网设备*/
struct net_local *lp;
struct resource *mem_res;
void __iomem *virt_addr;
int err;
...
lp = netdev_priv(dev); /*私有信息结构体赋值*/
dev->irq = platform_get_irq(pdev, 0); /*获取设备中断号*/
...
mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); /*获取设备I/O内存*/
virt_addr = devm_ioremap_resource(&pdev->dev, mem_res); /*映射为虚拟地址*/
...
err = cs89x0_probe1(dev, virt_addr, 0); /*调用真正的probe函数*/
...
platform_set_drvdata(pdev, dev);
return 0;
...
}
static int cs89x0_platform_remove(struct platform_device *pdev)
{
struct net_device *dev = platform_get_drvdata(pdev);
unregister_netdev(dev);
free_netdev(dev);
return 0;
}
static const struct __maybe_unused of_device_id cs89x0_match[] = {
{ .compatible = "cirrus,cs8900", },
{ .compatible = "cirrus,cs8920", },
{ },
};
MODULE_DEVICE_TABLE(of, cs89x0_match);
static struct platform_driver cs89x0_driver = {
.driver = {
.name = DRV_NAME,
.of_match_table = of_match_ptr(cs89x0_match),
},
.remove = cs89x0_platform_remove,
};
module_platform_driver_probe(cs89x0_driver, cs89x0_platform_probe);
#endif
NOTE: 驱动文件中还提供初始化非平台设备的各函数(较老版kernel)。
具体包含init_module、cs89x0_probe、cs89x0_ioport_probe、cleanup_module等函数。