本文我們主要來分析.probe的第一個操作函數組dm9000_netdev_ops(一個結構體),貼出該結構體代碼如下
static const struct net_device_ops dm9000_netdev_ops = {<br />.ndo_open= dm9000_open,<br />.ndo_stop= dm9000_stop,<br />.ndo_start_xmit= dm9000_start_xmit,<br />.ndo_tx_timeout= dm9000_timeout,<br />.ndo_set_multicast_list= dm9000_hash_table,<br />.ndo_do_ioctl= dm9000_ioctl,<br />.ndo_change_mtu= eth_change_mtu,<br />.ndo_validate_addr= eth_validate_addr,<br />.ndo_set_mac_address= eth_mac_addr,<br />#ifdef CONFIG_NET_POLL_CONTROLLER<br />.ndo_poll_controller= dm9000_poll_controller,<br />#endif<br />};
這裡定義了操作net_device的一些重要操作,分別介紹如下:
1.dm9000_open
static int dm9000_open(struct net_device *dev)<br />{<br />board_info_t *db = netdev_priv(dev);<br />unsigned long irqflags = db->irq_res->flags & IRQF_TRIGGER_MASK;</p><p>if (netif_msg_ifup(db))<br />dev_dbg(db->dev, "enabling %s/n", dev->name);</p><p>/* If there is no IRQ type specified, default to something that<br /> * may work, and tell the user that this is a problem */</p><p>if (irqflags == IRQF_TRIGGER_NONE)<br />dev_warn(db->dev, "WARNING: no IRQ resource flags set./n");</p><p>irqflags |= IRQF_SHARED;</p><p>if (request_irq(dev->irq, dm9000_interrupt, irqflags, dev->name, dev))<br />return -EAGAIN; //申請中斷資源</p><p>/* GPIO0 on pre-activate PHY, Reg 1F is not set by reset */<br />iow(db, DM9000_GPR, 0);/* REG_1F bit0 activate phyxcer */<br />mdelay(1); /* delay needs by DM9000B */</p><p>/* Initialize DM9000 board */<br />dm9000_reset(db); //複位DM9000晶片<br /> dm9000_init_dm9000(dev); //配置DM9000晶片內寄存器,使其能工作</p><p>/* Init driver variable */<br />db->dbug_cnt = 0;</p><p>mii_check_media(&db->mii, netif_msg_link(db), 1); //檢測mii介面的狀態<br />netif_start_queue(dev); //啟動裝置工作隊列</p><p>dm9000_schedule_poll(db); //阻塞</p><p>return 0;<br />}<br />
2.dm9000_stop
static int dm9000_stop(struct net_device *ndev)<br />{<br />board_info_t *db = netdev_priv(ndev);</p><p>if (netif_msg_ifdown(db))<br />dev_dbg(db->dev, "shutting down %s/n", ndev->name);</p><p>cancel_delayed_work_sync(&db->phy_poll); //取消裝置阻塞</p><p>netif_stop_queue(ndev); //停止工作隊列<br />netif_carrier_off(ndev); </p><p>/* free interrupt */<br />free_irq(ndev->irq, ndev); //釋放中斷資源</p><p>dm9000_shutdown(ndev);</p><p>return 0;<br />}<br />
其工作與open函數基本是相對應的,主要是資源的釋放和停止裝置發送隊列。
3.dm9000_start_xmit
static int dm9000_start_xmit(struct sk_buff *skb, struct net_device *dev)<br />{<br />unsigned long flags;<br />board_info_t *db = netdev_priv(dev);</p><p>dm9000_dbg(db, 3, "%s:/n", __func__);</p><p>if (db->tx_pkt_cnt > 1) //檢查裝置是否忙狀態<br />return NETDEV_TX_BUSY;</p><p>spin_lock_irqsave(&db->lock, flags); //上鎖的同時記錄中斷狀態</p><p>/* Move data to DM9000 TX RAM */<br />writeb(DM9000_MWCMD, db->io_addr); //寫資料到DM9000的發送緩衝區</p><p>(db->outblk)(db->io_data, skb->data, skb->len);<br />dev->stats.tx_bytes += skb->len;</p><p>db->tx_pkt_cnt++;<br />/* TX control: First packet immediately send, second packet queue */<br />if (db->tx_pkt_cnt == 1) { //第一個隊列立刻發送<br />dm9000_send_packet(dev, skb->ip_summed, skb->len);<br />} else {<br />/* Second packet */<br />db->queue_pkt_len = skb->len;<br />db->queue_ip_summed = skb->ip_summed;<br />netif_stop_queue(dev);<br />}</p><p>spin_unlock_irqrestore(&db->lock, flags); //解鎖,恢複上鎖前的中斷狀態</p><p>/* free this SKB */<br />dev_kfree_skb(skb); //釋放skb</p><p>return NETDEV_TX_OK;<br />}<br />
主要功能是將從上層傳入的資料發送到media中。
4.dm9000_timeout
static void dm9000_timeout(struct net_device *dev)<br />{<br />board_info_t *db = netdev_priv(dev);<br />u8 reg_save;<br />unsigned long flags;</p><p>/* Save previous register address */<br />spin_lock_irqsave(&db->lock, flags); //上鎖<br />reg_save = readb(db->io_addr); //把殘留資料儲存起來</p><p>netif_stop_queue(dev);<br />dm9000_reset(db);<br />dm9000_init_dm9000(dev);</p><p>/* We can accept TX packets again */<br />dev->trans_start = jiffies; /* prevent tx timeout */<br />netif_wake_queue(dev);</p><p>/* Restore previous register address */<br />writeb(reg_save, db->io_addr); //複原上次的殘留資料<br />spin_unlock_irqrestore(&db->lock, flags); //解鎖<br />}<br />
看門狗被觸發後調用此函數,主要執行裝置的重啟動作。
5.dm9000_hash_table
static void dm9000_hash_table(struct net_device *dev)<br />{<br />board_info_t *db = netdev_priv(dev);<br />unsigned long flags;</p><p>spin_lock_irqsave(&db->lock, flags);<br />dm9000_hash_table_unlocked(dev);<br />spin_unlock_irqrestore(&db->lock, flags);<br />}
核心函數dm9000_hash_table_unlocked(dev)完成設定組播地址的功能。
6.dm9000_ioctl
static int dm9000_ioctl(struct net_device *dev, struct ifreq *req, int cmd)<br />{<br />board_info_t *dm = to_dm9000_board(dev);</p><p>if (!netif_running(dev))<br />return -EINVAL;</p><p>return generic_mii_ioctl(&dm->mii, if_mii(req), cmd, NULL);<br />}
核心函數 generic_mii_ioctl(&dm->mii, if_mii(req), cmd, NULL)代碼如下
int generic_mii_ioctl(struct mii_if_info *mii_if,<br /> struct mii_ioctl_data *mii_data, int cmd,<br /> unsigned int *duplex_chg_out)<br />{<br />int rc = 0;<br />unsigned int duplex_changed = 0;</p><p>if (duplex_chg_out)<br />*duplex_chg_out = 0;</p><p>mii_data->phy_id &= mii_if->phy_id_mask;<br />mii_data->reg_num &= mii_if->reg_num_mask;</p><p>switch(cmd) {<br />case SIOCGMIIPHY:<br />mii_data->phy_id = mii_if->phy_id;<br />/* fall through */</p><p>case SIOCGMIIREG:<br />mii_data->val_out =<br />mii_if->mdio_read(mii_if->dev, mii_data->phy_id,<br /> mii_data->reg_num);<br />break;</p><p>case SIOCSMIIREG: {<br />u16 val = mii_data->val_in;</p><p>if (mii_data->phy_id == mii_if->phy_id) {<br />switch(mii_data->reg_num) {<br />case MII_BMCR: {<br />unsigned int new_duplex = 0;<br />if (val & (BMCR_RESET|BMCR_ANENABLE))<br />mii_if->force_media = 0;<br />else<br />mii_if->force_media = 1;<br />if (mii_if->force_media &&<br /> (val & BMCR_FULLDPLX))<br />new_duplex = 1;<br />if (mii_if->full_duplex != new_duplex) {<br />duplex_changed = 1;<br />mii_if->full_duplex = new_duplex;<br />}<br />break;<br />}<br />case MII_ADVERTISE:<br />mii_if->advertising = val;<br />break;<br />default:<br />/* do nothing */<br />break;<br />}<br />}</p><p>mii_if->mdio_write(mii_if->dev, mii_data->phy_id,<br /> mii_data->reg_num, val);<br />break;<br />}</p><p>default:<br />rc = -EOPNOTSUPP;<br />break;<br />}</p><p>if ((rc == 0) && (duplex_chg_out) && (duplex_changed))<br />*duplex_chg_out = 1;</p><p>return rc;<br />}<br />
一個非常熟悉的switch case 結構函數,具體完成的動作這裡不細講啦~
7.eth_change_mtu
int eth_change_mtu(struct net_device *dev, int new_mtu)<br />{<br />if (new_mtu < 68 || new_mtu > ETH_DATA_LEN)<br />return -EINVAL;<br />dev->mtu = new_mtu;<br />return 0;<br />}
修改MTU值
8.eth_validate_addr
int eth_validate_addr(struct net_device *dev)<br />{<br />if (!is_valid_ether_addr(dev->dev_addr))<br />return -EADDRNOTAVAIL;</p><p>return 0;<br />}
判斷地址是否全0xff或全0,非法
9.eth_mac_addr
int eth_mac_addr(struct net_device *dev, void *p)<br />{<br />struct sockaddr *addr = p;</p><p>if (netif_running(dev))<br />return -EBUSY;<br />if (!is_valid_ether_addr(addr->sa_data))<br />return -EADDRNOTAVAIL;<br />memcpy(dev->dev_addr, addr->sa_data, ETH_ALEN);<br />return 0;<br />}
把addr的sa_data成員綁定到裝置地址
10.dm9000_poll_controller
static void dm9000_poll_controller(struct net_device *dev)<br />{<br />disable_irq(dev->irq);<br />dm9000_interrupt(dev->irq, dev);<br />enable_irq(dev->irq);<br />}
函數dm9000_interrupt如下
static irqreturn_t dm9000_interrupt(int irq, void *dev_id)<br />{<br />struct net_device *dev = dev_id;<br />board_info_t *db = netdev_priv(dev);<br />int int_status;<br />unsigned long flags;<br />u8 reg_save;</p><p>dm9000_dbg(db, 3, "entering %s/n", __func__);</p><p>/* A real interrupt coming */</p><p>/* holders of db->lock must always block IRQs */<br />spin_lock_irqsave(&db->lock, flags);</p><p>/* Save previous register address */<br />reg_save = readb(db->io_addr);</p><p>/* Disable all interrupts */<br />iow(db, DM9000_IMR, IMR_PAR); //關中斷</p><p>/* Got DM9000 interrupt status */<br />int_status = ior(db, DM9000_ISR);//擷取中斷狀態<br />iow(db, DM9000_ISR, int_status);//清除中斷狀態</p><p>if (netif_msg_intr(db))<br />dev_dbg(db->dev, "interrupt status %02x/n", int_status);</p><p> //根據狀態來判定是接收還是發送觸發的中斷<br />/* Received the coming packet */<br />if (int_status & ISR_PRS) //接收<br />dm9000_rx(dev);</p><p>/* Trnasmit Interrupt check */<br />if (int_status & ISR_PTS) //發送<br />dm9000_tx_done(dev, db);</p><p>if (db->type != TYPE_DM9000E) {<br />if (int_status & ISR_LNKCHNG) {<br />/* fire a link-change request */<br />schedule_delayed_work(&db->phy_poll, 1);<br />}<br />}</p><p>/* Re-enable interrupt mask */<br />iow(db, DM9000_IMR, db->imr_all);</p><p>/* Restore previous register address */<br />writeb(reg_save, db->io_addr);</p><p>spin_unlock_irqrestore(&db->lock, flags);</p><p>return IRQ_HANDLED;<br />}<br />
到此為止,已經粗略的介紹完dm9000_netdev_ops的各個重要的操作。本博文應該是這個系列的倒數第二篇了,最後一篇用來分析dm9000_ethtod_ops的操作,敬請關注~
本系列課程連結地址
DM9000網卡驅動(1)http://blog.csdn.net/jarvis_xian/archive/2011/06/10/6537446.aspx
DM9000網卡驅動(2)http://blog.csdn.net/jarvis_xian/archive/2011/06/12/6539931.aspx
DM9000網卡驅動(3)http://blog.csdn.net/jarvis_xian/archive/2011/06/13/6542411.aspx
DM9000網卡驅動(4)http://blog.csdn.net/jarvis_xian/archive/2011/06/15/6545109.aspx
DM9000網卡驅動(5)http://blog.csdn.net/jarvis_xian/archive/2011/06/15/6547203.aspx