资料
dm9000-中文
dm9000--ae
make menuconfig
Device Drivers ---> [*] Network device support ---> [*] Ethernet (10 or 100Mbit) ---> <*> DM9000 support
dm9000的结构图比较清晰,外层是platform,内层是net_device。
平台设备在mach-mini2440.c注册,平台驱动在dm9000.c的init注册。
net_device在平台的probe注册。
*************************************************************************
平台设备信息,
mach-smdk6410.c
/* Ethernet */
#ifdef CONFIG_DM9000
#define S3C64XX_PA_DM9000 (0x18000000)
#define S3C64XX_SZ_DM9000 SZ_1M
#define S3C64XX_VA_DM9000 S3C_ADDR(0x03b00300)
static struct resource dm9000_resources[] = {//平台设备的资源
[0] = {
.start = S3C64XX_PA_DM9000,//0x18000000
.end = S3C64XX_PA_DM9000 + 3,//0x18000003
.flags = IORESOURCE_MEM,
},
[1] = {
.start = S3C64XX_PA_DM9000 + 4,//0x18000004
.end = S3C64XX_PA_DM9000 + S3C64XX_SZ_DM9000 - 1,//180fffff
.flags = IORESOURCE_MEM,
},
[2] = {
.start = IRQ_EINT(7),//网卡的中断引脚连到6410的eint7
.end = IRQ_EINT(7),
.flags = IORESOURCE_IRQ | IRQF_TRIGGER_HIGH,//高电平触发中断
},
};
/*从打印信息可以验证
[root@FORLINX6410]# cat /proc/iomem
18000000-18000003 : dm9000.0 //dm9000
18000000-18000003 : dm9000
18000004-180fffff : dm9000.0
18000004-180fffff : dm9000
*/
static struct dm9000_plat_data dm9000_setup = {//平台设备de私有数据
.flags = DM9000_PLATF_16BITONLY,//dm9000a的16位模式
.dev_addr = { 0x08, 0x90, 0x00, 0xa0, 0x90, 0x90 },//默认mac,当网卡没接eeprom来存放mac时,使用这个当做默认mac
};
static struct platform_device s3c_device_dm9000 = {//平台设备
.name = "dm9000",
.id = 0,
.num_resources = ARRAY_SIZE(dm9000_resources),
.resource = dm9000_resources,
.dev = {
.platform_data = &dm9000_setup,
}
};
#endif //#ifdef CONFIG_DM9000
//然后是注册平台设备
...
*************************************************************************
dm9000.c中
有两个结构体很重要
/* Structure/enum declaration ------------------------------- */
typedef struct board_info {
void __iomem *io_addr; /* Register I/O base address */
void __iomem *io_data; /* Data I/O address */
u16 irq; /* IRQ */
u16 tx_pkt_cnt;
u16 queue_pkt_len;
u16 queue_start_addr;
u16 queue_ip_summed;
u16 dbug_cnt;
u8 io_mode; /* 0:word, 2:byte */
u8 phy_addr;
u8 imr_all;
unsigned int flags;
unsigned int in_suspend :1;
unsigned int wake_supported :1;
int debug_level;
enum dm9000_type type;
void (*inblk)(void __iomem *port, void *data, int length);
void (*outblk)(void __iomem *port, void *data, int length);
void (*dumpblk)(void __iomem *port, int length);
struct device *dev; /* parent device */
struct resource *addr_res; /* resources found */
struct resource *data_res;
struct resource *addr_req; /* resources requested */
struct resource *data_req;
struct resource *irq_res;
int irq_wake;
struct mutex addr_lock; /* phy and eeprom access lock */
struct delayed_work phy_poll;
struct net_device *ndev;
spinlock_t lock;
struct mii_if_info mii;
u32 msg_enable;
u32 wake_state;
int rx_csum;
int can_csum;
int ip_summed;
} board_info_t;
/*
* The DEVICE structure.
* Actually, this whole structure is a big mistake. It mixes I/O
* data with strictly "high-level" data, and it has to know about
* almost every data structure used in the INET module.
*
* FIXME: cleanup struct net_device such that network protocol info
* moves out.
*/
struct net_device {
/*
* This is the first field of the "visible" part of this structure
* (i.e. as seen by users in the "Space.c" file). It is the name
* of the interface.
*/
char name[IFNAMSIZ];
struct pm_qos_request_list pm_qos_req;
/* device name hash chain */
struct hlist_node name_hlist;
/* snmp alias */
char *ifalias;
/*
* I/O specific fields
* FIXME: Merge these and struct ifmap into one
*/
unsigned long mem_end; /* shared mem end */
unsigned long mem_start; /* shared mem start */
unsigned long base_addr; /* device I/O address */
unsigned int irq; /* device IRQ number */
/*
* Some hardware also needs these fields, but they are not
* part of the usual set specified in Space.c.
*/
unsigned char if_port; /* Selectable AUI, TP,..*/
unsigned char dma; /* DMA channel */
unsigned long state;
struct list_head dev_list;
struct list_head napi_list;
struct list_head unreg_list;
/* Management operations */
const struct net_device_ops *netdev_ops;//dm9000的net_device的file-operation
const struct ethtool_ops *ethtool_ops;
const struct header_ops *header_ops;
unsigned int flags; /* interface flags (a la BSD) */
unsigned short gflags;
unsigned short priv_flags; /* Like 'flags' but invisible to userspace. */
unsigned short padded; /* How much padding added by alloc_netdev() */
unsigned char operstate; /* RFC2863 operstate */
unsigned char link_mode; /* mapping policy to operstate */
unsigned int mtu; /* interface MTU value */
unsigned short type; /* interface hardware type */
unsigned short hard_header_len; /* hardware hdr length */
/* extra head- and tailroom the hardware may need, but not in all cases
* can this be guaranteed, especially tailroom. Some cases also use
* LL_MAX_HEADER instead to allocate the skb.
*/
unsigned short needed_headroom;
unsigned short needed_tailroom;
struct net_device *master; /* Pointer to master device of a group,
* which this device is member of.
*/
/* Interface address info. */
unsigned char perm_addr[MAX_ADDR_LEN]; /* permanent hw address */
unsigned char addr_assign_type; /* hw address assignment type */
unsigned char addr_len; /* hardware address length */
unsigned short dev_id; /* for shared network cards */
spinlock_t addr_list_lock;
struct netdev_hw_addr_list uc; /* Unicast mac addresses */
struct netdev_hw_addr_list mc; /* Multicast mac addresses */
int uc_promisc;
unsigned int promiscuity;
unsigned int allmulti;
};
static const struct net_device_ops dm9000_netdev_ops = {
.ndo_open = dm9000_open,//执行ifconfig eth0 up时调用
.ndo_stop = dm9000_stop,//执行ifconfig eth0 down时调用
.ndo_start_xmit = dm9000_start_xmit,//上层应用执行write socket等时调用
.ndo_tx_timeout = dm9000_timeout,
.ndo_set_multicast_list = dm9000_hash_table,//哈希表
.ndo_do_ioctl = dm9000_ioctl,
.ndo_change_mtu = eth_change_mtu,
.ndo_validate_addr = eth_validate_addr,
.ndo_set_mac_address = eth_mac_addr,
#ifdef CONFIG_NET_POLL_CONTROLLER
.ndo_poll_controller = dm9000_poll_controller,
#endif
};
*************************************************************************
在init函数中注册平台驱动
dm9000_driver
static int __init
dm9000_init(void)
{
printk(KERN_INFO "%s Ethernet Driver, V%s\n", CARDNAME, DRV_VERSION);
return platform_driver_register(&dm9000_driver);
}
在平台驱动dm9000_driver的probe函数中分配net_device
static struct platform_driver dm9000_driver = {
.driver = {
.name = "dm9000",
.owner = THIS_MODULE,
.pm = &dm9000_drv_pm_ops,
},
.probe = dm9000_probe,
.remove = __devexit_p(dm9000_drv_remove),
};
static int __devinit dm9000_probe(struct platform_device *pdev)
{
ndev = alloc_etherdev(sizeof(struct board_info));
if (!ndev) {
dev_err(&pdev->dev, "could not allocate device.\n");
return -ENOMEM;
}
//
INIT_DELAYED_WORK(&db->phy_poll, dm9000_poll_work);
//注册net_device
register_netdev(ndev);
}
//net_device的operation结构体指定了操作函数集合
static const struct net_device_ops dm9000_netdev_ops = {
.ndo_open = dm9000_open,
.ndo_stop = dm9000_stop,
.ndo_start_xmit = dm9000_start_xmit,
.ndo_tx_timeout = dm9000_timeout,
.ndo_set_multicast_list = dm9000_hash_table,
.ndo_do_ioctl = dm9000_ioctl,
.ndo_change_mtu = eth_change_mtu,
.ndo_validate_addr = eth_validate_addr,
.ndo_set_mac_address = eth_mac_addr,
#ifdef CONFIG_NET_POLL_CONTROLLER
.ndo_poll_controller = dm9000_poll_controller,
#endif
};
应用层执行ifconfig eth0 up时会调用到dm9000_open
在mcp251x_open函数中
//申请中断
if (request_irq(dev->irq, dm9000_interrupt, irqflags, dev->name, dev))
return -EAGAIN;
//开启协议栈下传skb给驱动
netif_start_queue(dev);
//启动
dm9000_schedule_poll(db);
应用层执行write socket时会调用到m9000_start_xmit来发送数据。
发送流程:
在m9000_start_xmit函数中直接发送。
如果当前只有一个包需要发送,就直接调用dm9000_send_packet发送出去
如果当前正在发包,就返回busy,让待发包先在上层网络协议栈里面排队一下
netif_start_queue用来告诉上层网络协议这个驱动程序还有空的缓冲区可用,请把下 一个封包送进来。
netif_wake_queue()会使得上层协定开始传送新的资料下来,这个函数除了通知网络系统可再次开始传输数据包之外
这些函数的queue应该是指的协议栈里面的queue
/*
* Hardware start transmission.
* Send a packet to media from the upper layer.
*/
static int
dm9000_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
unsigned long flags;
board_info_t *db = netdev_priv(dev);
dm9000_dbg(db, 3, "%s:\n", __func__);
if (db->tx_pkt_cnt > 1)
return NETDEV_TX_BUSY;
spin_lock_irqsave(&db->lock, flags);
/* Move data to DM9000 TX RAM */
writeb(DM9000_MWCMD, db->io_addr);
(db->outblk)(db->io_data, skb->data, skb->len);
dev->stats.tx_bytes += skb->len;
db->tx_pkt_cnt++;
/* TX control: First packet immediately send, second packet queue */
if (db->tx_pkt_cnt == 1) {
dm9000_send_packet(dev, skb->ip_summed, skb->len);
} else {
/* Second packet */
db->queue_pkt_len = skb->len;
db->queue_ip_summed = skb->ip_summed;
netif_stop_queue(dev);
}
spin_unlock_irqrestore(&db->lock, flags);
/* free this SKB */
dev_kfree_skb(skb);
return NETDEV_TX_OK;
}
怎么接收呢?当然是在中断处理函数中接收,有中断产生时,在中断处理函数中(即上半部)直接接收。并将接收到的数据保存,以供应用层使用read socket等来读取。未使用工作队列。
接收流程:
1.分配skb
2.从硬件即通过总线从dm9000 rx ram中读取数据到skb
3.调用netif_rx将skb交给设备无关接口(它再交给协议栈处理skb中数据)
static irqreturn_t dm9000_interrupt(int irq, void *dev_id)
{
struct net_device *dev = dev_id;
board_info_t *db = netdev_priv(dev);
int int_status;
unsigned long flags;
u8 reg_save;
dm9000_dbg(db, 3, "entering %s\n", __func__);
/* A real interrupt coming */
/* holders of db->lock must always block IRQs */
spin_lock_irqsave(&db->lock, flags);//获取锁,同同时也禁止中断
/* Save previous register address */
reg_save = readb(db->io_addr);
/* Disable all interrupts */
iow(db, DM9000_IMR, IMR_PAR);
/* Got DM9000 interrupt status */
int_status = ior(db, DM9000_ISR); /* Got ISR */
iow(db, DM9000_ISR, int_status); /* Clear ISR status */
if (netif_msg_intr(db))
dev_dbg(db->dev, "interrupt status %02x\n", int_status);
/* Received the coming packet */
if (int_status & ISR_PRS)//是接收中断
dm9000_rx(dev);
/* Trnasmit Interrupt check */
if (int_status & ISR_PTS)//是发送完成中断
dm9000_tx_done(dev, db);
if (db->type != TYPE_DM9000E) {//如果不是dm9000e还需要进行下面
if (int_status & ISR_LNKCHNG) {
/* fire a link-change request */
schedule_delayed_work(&db->phy_poll, 1);
}
}
/* Re-enable interrupt mask */
iow(db, DM9000_IMR, db->imr_all);//重新使能中断
/* Restore previous register address */
writeb(reg_save, db->io_addr);
spin_unlock_irqrestore(&db->lock, flags);
return IRQ_HANDLED;
}
接收工作由函数dm9000_tx()完成,
/*
* Received a packet and pass to upper layer
*/
static void
dm9000_rx(struct net_device *dev)
{
board_info_t *db = netdev_priv(dev);
struct dm9000_rxhdr rxhdr;
struct sk_buff *skb;
u8 rxbyte, *rdptr;
bool GoodPacket;
int RxLen;
/* Check packet ready or not */
do {
ior(db, DM9000_MRCMDX); /* Dummy read */
/* Get most updated data */
rxbyte = readb(db->io_data);
/* Status check: this byte must be 0 or 1 */
if (rxbyte & DM9000_PKT_ERR) {
dev_warn(db->dev, "status check fail: %d\n", rxbyte);
iow(db, DM9000_RCR, 0x00); /* Stop Device */
iow(db, DM9000_ISR, IMR_PAR); /* Stop INT request */
return;
}
if (!(rxbyte & DM9000_PKT_RDY))
return;
/* A packet ready now & Get status/length */
GoodPacket = true;
writeb(DM9000_MRCMD, db->io_addr);
(db->inblk)(db->io_data, &rxhdr, sizeof(rxhdr));
RxLen = le16_to_cpu(rxhdr.RxLen);
if (netif_msg_rx_status(db))
dev_dbg(db->dev, "RX: status %02x, length %04x\n",
rxhdr.RxStatus, RxLen);
/* Packet Status check */
if (RxLen < 0x40) {
GoodPacket = false;
if (netif_msg_rx_err(db))
dev_dbg(db->dev, "RX: Bad Packet (runt)\n");
}
if (RxLen > DM9000_PKT_MAX) {
dev_dbg(db->dev, "RST: RX Len:%x\n", RxLen);
}
/* rxhdr.RxStatus is identical to RSR register. */
if (rxhdr.RxStatus & (RSR_FOE | RSR_CE | RSR_AE |
RSR_PLE | RSR_RWTO |
RSR_LCS | RSR_RF)) {
GoodPacket = false;
if (rxhdr.RxStatus & RSR_FOE) {
if (netif_msg_rx_err(db))
dev_dbg(db->dev, "fifo error\n");
dev->stats.rx_fifo_errors++;
}
if (rxhdr.RxStatus & RSR_CE) {
if (netif_msg_rx_err(db))
dev_dbg(db->dev, "crc error\n");
dev->stats.rx_crc_errors++;
}
if (rxhdr.RxStatus & RSR_RF) {
if (netif_msg_rx_err(db))
dev_dbg(db->dev, "length error\n");
dev->stats.rx_length_errors++;
}
}
/* Move data from DM9000 */
if (GoodPacket &&
((skb = dev_alloc_skb(RxLen + 4)) != NULL)) {
skb_reserve(skb, 2);
rdptr = (u8 *) skb_put(skb, RxLen - 4);
/* Read received packet from RX SRAM */
(db->inblk)(db->io_data, rdptr, RxLen);
dev->stats.rx_bytes += RxLen;
/* Pass to upper layer */
skb->protocol = eth_type_trans(skb, dev);
if (db->rx_csum) {
if ((((rxbyte & 0x1c) << 3) & rxbyte) == 0)
skb->ip_summed = CHECKSUM_UNNECESSARY;
else
skb->ip_summed = CHECKSUM_NONE;
}
netif_rx(skb);
dev->stats.rx_packets++;
} else {
/* need to dump the packet's data */
(db->dumpblk)(db->io_data, RxLen);
}
} while (rxbyte & DM9000_PKT_RDY);
}