网络设备驱动的分层

网络协议接口层 ------------------ 数据链路层
网络接口层 ------------------------ 数据链路层
设备驱动功能层 ------------------ 数据链路层
网络媒介层 ------------------------ 物理层

网络协议接口层

网络协议接口层给上层协议提供统一的数据包收发接口,无论上层是ARP协议还是IP协议,都通过dev_queue_xmit()函数发送数据,通过netif_rx函数接收数据。此层使得上层协议独立于具体的设备。

为网络驱动提供一些列netif开头的函数:
激活设备发送队列:void netif_start_queue(struct net_device *dev)
停止设备传输包:void netif_stop_queue(struct net_device *dev)
重新启动设备发送队列:void netif_awake_queue(struct net_device *dev)

网络设备接口层

为各种各样的网络设备定义同一的、抽象的数据结构net_device结构体,实现多种硬件在软件层次上的统一。
net_device结构体在内核中指代一个网络设备,网络设备驱动只要填充其结构体就可以实现内核与具体硬件操作函数的挂接。
net_device定义的位置:include/linux/netdevice.h

 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   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 网络设备I/O基地址 base_addr;   /* device I/O address   */  //irq 设备使用的中断号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 long       state;  struct list_head    dev_list;   struct list_head    napi_list;  struct list_head    unreg_list;     /* currently active device features */netdev_features_t features;   /* user-changeable features */  netdev_features_t   hw_features;    /* user-requested features */   netdev_features_t   wanted_features;/* mask of features inheritable by VLAN devices */  netdev_features_t   vlan_features;  /* Interface index. Unique device identifier    */  int         ifindex;    int         iflink;     struct net_device_stats stats;  atomic_long_t       rx_dropped; /* dropped packets by core network                       * Do not use this in drivers.                       */#ifdef CONFIG_WIRELESS_EXT/* List of functions to handle Wireless Extensions (instead of ioctl). * See <net/iw_handler.h> for details. Jean II */const struct iw_handler_def * wireless_handlers;  /* Instance data managed by the core of Wireless Extensions. */ struct iw_public_data * wireless_data;#endif    /* Management operations */ const struct net_device_ops *netdev_ops;    const struct ethtool_ops *ethtool_ops;  /* Hardware header description */   const struct header_ops *header_ops;        //网络接口标志    unsigned int        flags;  /* interface flags (a la BSD)   */  unsigned int        priv_flags; /* Like 'flags' but invisible to userspace.                        * See if.h for definitions. */unsigned short       gflags; unsigned short      padded; /* How much padding added by alloc_netdev() */  unsigned char       operstate; /* RFC2863 operstate */  unsigned char       link_mode; /* mapping policy to operstate */        //多端口设备中端口选择    unsigned char       if_port;    /* Selectable AUI, TP,..*/  //分配给设备的dma通道   unsigned char       dma;        /* DMA channel      */      //最大传输单元    unsigned int        mtu;    /* interface MTU value      */  //接口的硬件类型   unsigned short      type;   /* interface hardware type  */  //网络设备硬件头长度,在以太网设备初始化函数中,赋值为ETH_HLENunsigned 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;    /* 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 char       neigh_priv_len; 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 */   bool            uc_promisc; unsigned int        promiscuity;    unsigned int        allmulti;   /* Protocol specific pointers */
#if IS_ENABLED(CONFIG_VLAN_8021Q)   struct vlan_info __rcu  *vlan_info; /* VLAN info */
#endif
#if IS_ENABLED(CONFIG_NET_DSA)  struct dsa_switch_tree  *dsa_ptr;   /* dsa specific data */
#endif  void            *atalk_ptr; /* AppleTalk link   */  struct in_device __rcu  *ip_ptr;    /* IPv4 specific data   */  struct dn_dev __rcu     *dn_ptr;        /* DECnet specific data */  struct inet6_dev __rcu  *ip6_ptr;       /* IPv6 specific data */    void            *ax25_ptr;  /* AX.25 specific data */   struct wireless_dev *ieee80211_ptr; /* IEEE 802.11 specific data,   assign before registering *//* * Cache lines mostly used on receive path (including eth_type_trans()) */    unsigned long       last_rx;    /* Time of last Rx                       * This should not be set in                         * drivers, unless really needed,                        * because network stack (bonding)                       * use it if/when necessary, to                      * avoid dirtying this cache line.                       */     struct net_device   *master; /* Pointer to master device of a group,     which this device is member of.*/  /* Interface address info used in eth_type_trans() */   //设备的硬件地址 mac地址 unsigned char       *dev_addr;  /* hw address, (before bcast    because most packets are unicast) */        struct netdev_hw_addr_list  dev_addrs; /* list of device hw addresses */    //设备的广播地址   unsigned char       broadcast[MAX_ADDR_LEN];    /* hw bcast add */
#ifdef CONFIG_SYSFS struct kset     *queues_kset;
#endif
#ifdef CONFIG_RPS   struct netdev_rx_queue  *_rx;   /* Number of RX queues allocated at register_netdev() time */   unsigned int        num_rx_queues;  /* Number of RX queues currently active in device */    unsigned int        real_num_rx_queues; #ifdef CONFIG_RFS_ACCEL /* CPU reverse-mapping for RX completion interrupts, indexed     * by RX queue number.  Assigned by driver.  This must only be   * set if the ndo_rx_flow_steer operation is defined. */    struct cpu_rmap     *rx_cpu_rmap;
#endif
#endif  rx_handler_func_t __rcu *rx_handler;    void __rcu      *rx_handler_data;   struct netdev_queue __rcu *ingress_queue;/* Cache lines mostly used on transmit path */ struct netdev_queue *_tx ____cacheline_aligned_in_smp;  /* Number of TX queues allocated at alloc_netdev_mq() time  */  unsigned int        num_tx_queues; /* Number of TX queues currently active in device  */    unsigned int        real_num_tx_queues; /* root qdisc from userspace point of view */   struct Qdisc        *qdisc;     unsigned long       tx_queue_len;   /* Max frames per queue allowed */  spinlock_t      tx_global_lock;
#ifdef CONFIG_XPS   struct xps_dev_maps __rcu *xps_maps
;#endif     /* These may be needed for future network-power-down code. */   /*   * trans_start here is expensive for high speed devices on SMP,  * please use netdev_queue->trans_start instead.  */ unsigned long       trans_start;    /* Time (in jiffies) of last Tx */  int         watchdog_timeo; /* used by dev_watchdog() */    struct timer_list   watchdog_timer; /* Number of references to this device */   int __percpu        *pcpu_refcnt;   /* delayed register/unregister */   struct list_head    todo_list;  /* device index hash chain */   struct hlist_node   index_hlist;    struct list_head    link_watch_list;    /* register/unregister state machine */ enum { NETREG_UNINITIALIZED=0,       NETREG_REGISTERED,    /* completed register_netdevice */        NETREG_UNREGISTERING, /* called unregister_netdevice */         NETREG_UNREGISTERED,  /* completed unregister todo */        NETREG_RELEASED,     /* called free_netdev */           NETREG_DUMMY,        /* dummy device for NAPI poll */    } reg_state:8;  bool dismantle; /* device is going do be freed */   enum {      RTNL_LINK_INITIALIZED,      RTNL_LINK_INITIALIZING, } rtnl_link_state:16;   /* Called from unregister, can be used to call free_netdev */void (*destructor)(struct net_device *dev);#ifdef CONFIG_NETPOLL   struct netpoll_info *npinfo;
#endif #ifdef CONFIG_NET_NS /* Network namespace this network device is inside */   struct net      *nd_net;
#endif  /* mid-layer private */ union {     void                *ml_priv;       struct pcpu_lstats __percpu *lstats; /* loopback stats */       struct pcpu_tstats __percpu *tstats; /* tunnel stats */     struct pcpu_dstats __percpu *dstats; /* dummy stats */  };  /* GARP */  struct garp_port __rcu  *garp_port; /* class/net/name entry */  struct device       dev;    /* space for optional device, statistics, and wireless sysfs groups */  const struct attribute_group *sysfs_groups[4];  /* rtnetlink link ops */    const struct rtnl_link_ops *rtnl_link_ops;  /* for setting kernel sock attribute on TCP connection setup */#define GSO_MAX_SIZE     65536   unsigned int        gso_max_size; #ifdef CONFIG_DCB /* Data Center Bridging netlink ops */  const struct dcbnl_rtnl_ops *dcbnl_ops;
#endif  u8 num_tc;  struct netdev_tc_txq tc_to_txq[TC_MAX_QUEUE];   u8 prio_tc_map[TC_BITMASK + 1];#if IS_ENABLED(CONFIG_FCOE) /* max exchange id for FCoE LRO by ddp */   unsigned int        fcoe_ddp_xid;
#endif#if IS_ENABLED(CONFIG_NETPRIO_CGROUP) struct netprio_map __rcu *priomap;
#endif  /* phy device may attach itself for hardware timestamping */    struct phy_device *phydev;  /* group the device belongs to */   int group;
};

设备驱动功能层

net_device中的成员函数是一些函数指针,这些函数的实现是在设备驱动功能层实现。设备驱动功能层是Linux驱动开发要做的主要工作。

网络媒介层

主要和具体硬件大交道的一层,实现一些访问网络设备内部寄存器的操作。

重要的结构体sk_buff

sk_buff结构体是套接字缓冲区,详细记录了一个数据包的组成、时间、网络设备、各层的首部及首部的长度和数据的首尾指针。

sk_buff结构体的定义:

struct sk_buff {/* These two members must be first. */struct sk_buff                *next;struct sk_buff                *prev;ktime_t                        tstamp; struct sock                *sk;struct net_device        *dev;/* * This is the control buffer. It is free to use for every * 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. */char                        cb[48] __aligned(8); unsigned long                _skb_refdst;#ifdef CONFIG_XFRMstruct        sec_path        *sp;
#endifunsigned int                len,data_len;__u16                        mac_len,hdr_len;union {__wsum                csum;struct {__u16        csum_start;__u16        csum_offset;};};__u32                        priority;kmemcheck_bitfield_begin(flags1);__u8                        local_df:1,cloned:1,ip_summed:2,nohdr:1,nfctinfo:3;__u8                        pkt_type:3,fclone:2,ipvs_property:1,peeked:1,nf_trace:1;kmemcheck_bitfield_end(flags1);__be16       protocol; void                        (*destructor)(struct sk_buff *skb
#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE)struct nf_conntrack        *nfct;
#endif#ifdef NET_SKBUFF_NF_DEFRAG_NEEDEDstruct sk_buff                *nfct_reasm;
#endif#ifdef CONFIG_BRIDGE_NETFILTERstruct nf_bridge_info        *nf_bridge;
#endif int                        skb_iif; __u32                        rxhash;             __u16                        vlan_tci;#ifdef CONFIG_NET_SCHED__u16                        tc_index;        /* traffic control index */#ifdef CONFIG_NET_CLS_ACT__u16                        tc_verd;        /* traffic control verdict */#endif#endif__u16                        queue_mapping;kmemcheck_bitfield_begin(flags2);
#ifdef CONFIG_IPV6_NDISC_NODETYPE__u8                        ndisc_nodetype:2;
#endif__u8                        ooo_okay:1;__u8                        l4_rxhash:1;__u8                        wifi_acked_valid:1;__u8                        wifi_acked:1;__u8                        no_fcs:1;__u8                        head_frag:1;/* 8/10 bit hole (depending on ndisc_nodetype presence) */kmemcheck_bitfield_end(flags2);
#ifdef CONFIG_NET_DMAdma_cookie_t                dma_cookie;
#endif
#ifdef CONFIG_NETWORK_SECMARK__u32                        secmark;
#endifunion {__u32                mark;__u32                dropcount;__u32                avail_size;}; sk_buff_data_t               transport_header;sk_buff_data_t                network_header;sk_buff_data_t                mac_header;/* These elements must be at the end, see alloc_skb() for details.  */sk_buff_data_t                tail;sk_buff_data_t                end;unsigned char                *head,*data;unsigned int                truesize;atomic_t                users;
};

sk_buff结构体中的4个重要成员:
sk_buff_data_t tail;
sk_buff_data_t end;
unsigned char *head,*data;

和sk_buff相关的函数:
alloc_skb()函数,该函数是在上层协议要发送数据包的时候或网络设备准备接收数据包的时候分配sk_buff结构体。

static inline struct sk_buff *alloc_skb(unsigned int size,  gfp_t priority)
{return __alloc_skb(size, priority, 0, NUMA_NO_NODE);
}struct sk_buff *__alloc_skb(unsigned int size, gfp_t gfp_mask,             int fclone, int node)
{   struct kmem_cache *cache;   struct skb_shared_info *shinfo; struct sk_buff *skb;    u8 *data;   cache = fclone ? skbuff_fclone_cache : skbuff_head_cache;  /* Get the HEAD */  skb = kmem_cache_alloc_node(cache, gfp_mask & ~__GFP_DMA, node);   if (!skb)       goto out;   prefetchw(skb);     /* We do our best to align skb_shared_info on a separate cache   * line. It usually works because kmalloc(X > SMP_CACHE_BYTES) gives  * aligned memory blocks, unless SLUB/SLAB debug is enabled.     * Both skb->head and skb_shared_info are cache line aligned.     */ size = SKB_DATA_ALIGN(size);   size += SKB_DATA_ALIGN(sizeof(struct skb_shared_info));   data = kmalloc_node_track_caller(size, gfp_mask, node);    if (!data)      goto nodata;    /* kmalloc(size) might give us more room than requested.     * Put skb_shared_info exactly at the end of allocated zone,     * to allow max possible filling before reallocation.       */  size = SKB_WITH_OVERHEAD(ksize(data)); prefetchw(data + size); /*  * Only clear those fields we need to clear, not those that we will  * actually initialise below. Hence, don't put any more fields after    * the tail pointer in struct sk_buff!   */ memset(skb, 0, offsetof(struct sk_buff, tail)); /* Account for allocated memory : skb + skb->head */    skb->truesize = SKB_TRUESIZE(size); atomic_set(&skb->users, 1);  skb->head = data;   skb->data = data;   skb_reset_tail_pointer(skb);    skb->end = skb->tail + size;#ifdef NET_SKBUFF_DATA_USES_OFFSET  skb->mac_header = ~0U;
#endif  /* make sure we initialize shinfo sequentially */   shinfo = skb_shinfo(skb);  memset(shinfo, 0, offsetof(struct skb_shared_info, dataref));   atomic_set(&shinfo->dataref, 1); kmemcheck_annotate_variable(shinfo->destructor_arg);     if (fclone) {       struct sk_buff *child = skb + 1;      atomic_t *fclone_ref = (atomic_t *) (child + 1);      kmemcheck_annotate_bitfield(child, flags1);     kmemcheck_annotate_bitfield(child, flags2);     skb->fclone = SKB_FCLONE_ORIG;      atomic_set(fclone_ref, 1);      child->fclone = SKB_FCLONE_UNAVAILABLE; }out:   return skb;nodata:  kmem_cache_free(cache, skb);    skb = NULL;    goto out;}

在alloc_skb中执行了关于head、data、tail、end的操作:
skb->head = data;
skb->data = data;
skb_reset_tail_pointer(skb);
skb->end = skb->tail + size;

得到如下的一张关系图
void kfree_skb(struct sk_buff *skb) 函数,释放不被使用的sk_buff结构体

处理skb_buff数据区的相应函数:
unsigned char *skb_put(struct sk_buff *skb, unsigned int len)函数,在数据取的末端添加某协议的尾部

unsigned char *skb_push(struct sk_buff *skb, unsigned int len)函数,在数据区的前端添加某协议的头部

unsigned char *skb_pull(struct sk_buff *skb, unsigned int len)函数,在前端去掉某协议的头部

void skb_trim(struct sk_buff *skb, unsigned int len)函数,在末端去掉某协议的尾部

skb_buff缓冲区链表的操作函数:
static inline void skb_orphan(struct sk_buff *skb)
函数,将一个缓冲结构体变成孤立的skb

static inline void skb_queue_head_init(struct sk_buff_head *list)函数,初始化sk_buff_head结构体

void skb_queue_head(struct sk_buff_head *list, struct sk_buff *newsk)函数,在链表头部添加一个sk_buff结构体

void skb_queue_tail(struct sk_buff_head *list, struct sk_buff *newsk)函数,在链表尾部添加新的skb_buff结构体

struct sk_buff *skb_dequeue(struct sk_buff_head *list)函数,在链表头部移走一个skb
skb_dequeue_tail,在链表尾部移走一个skb

void skb_queue_purge(struct sk_buff_head *list) ,清空一个由sk_buff_head管理的缓冲区链表

void skb_append(struct sk_buff *old, struct sk_buff *newsk, struct sk_buff_head *list)函数,在指定的skb后附加一个skb

网络设备DM9000驱动实例分析

内核版本号Linux3.5.0
开发板型号smdkv210
从arch/arm/mach-s5pv210/mach-smdkv210.c入手:找到关于dm9000的资源分配

#define DEFINE_RES_MEM_NAMED(_start, _size, _name)                       \DEFINE_RES_NAMED((_start), (_size), (_name), IORESOURCE_MEM)#define DEFINE_RES_MEM(_start, _size)                                       \DEFINE_RES_MEM_NAMED((_start), (_size), NULL)  static struct resource smdkv210_dm9000_resources[] = {[0] = DEFINE_RES_MEM(S5PV210_PA_SROM_BANK5, 1),[1] = DEFINE_RES_MEM(S5PV210_PA_SROM_BANK5 + 2, 1),[2] = DEFINE_RES_NAMED(IRQ_EINT(9), 1, NULL, IORESOURCE_IRQ \| IORESOURCE_IRQ_HIGHLEVEL),
}; //平台设备私有数据
static struct dm9000_plat_data smdkv210_dm9000_platdata = {.flags                = DM9000_PLATF_16BITONLY | M9000_PLATF_NO_EEPROM,.dev_addr        = { 0x00, 0x09, 0xc0, 0xff, 0xec, 0x48 }, //网卡mac地址
}; static struct platform_device smdkv210_dm9000 = {.name                = "dm9000",.id                = -1,     //表明只有1块网卡.num_resources        = ARRAY_SIZE(smdkv210_dm9000_resources),.resource        = smdkv210_dm9000_resources,.dev                = {.platform_data        = &smdkv210_dm9000_platdata,},
};

dm9000平台设备被定义在smdkv210的设备列表里:

static struct platform_device *smdkv210_devices[] __initdata = {    &s3c_device_adc,    &s3c_device_cfcon,  &s3c_device_fb, &s3c_device_hsmmc0,&s3c_device_hsmmc1,&s3c_device_hsmmc2,   &s3c_device_hsmmc3, &s3c_device_i2c0,   &s3c_device_i2c1,   &s3c_device_i2c2,   &s3c_device_rtc,    &s3c_device_ts, &s3c_device_usb_hsotg,  &s3c_device_wdt,    &s5p_device_fimc0,  &s5p_device_fimc1,  &s5p_device_fimc2,  &s5p_device_fimc_md,    &s5p_device_jpeg,   &s5p_device_mfc,    &s5p_device_mfc_l,  &s5p_device_mfc_r,  &s5pv210_device_ac97,   &s5pv210_device_iis0,   &s5pv210_device_spdif,  &samsung_asoc_dma,  &samsung_asoc_idma, &samsung_device_keypad, &smdkv210_dm9000,       //dm9000 platform device&smdkv210_lcd_lte480wv,
};

分析dm9000驱动代码,drivers/net/ethernet/davicom/dm9000.c

static int __initdm9000_init(void){printk(KERN_INFO "%s Ethernet Driver, V%s\n", CARDNAME, DRV_VERSION); return platform_driver_register(&dm9000_driver);
}
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),
};

dm9000_driver的重点就是dm9000_probe函数:

/* * Search DM9000 board, allocate space and register it */
static int __devinitdm9000_probe(struct platform_device *pdev){ struct dm9000_plat_data *pdata = pdev->dev.platform_data; /*获取dev里的platform数据*/ /*dm9000设备 自有的独特的信息*/   struct board_info *db;  /* Point a board information structure */   struct net_device *ndev; /*Point a net device structure */  const unsigned char *mac_src;   int ret = 0;   int iosize; int i;  u32 id_val;     /* Init network device */   ndev = alloc_etherdev(sizeof(struct board_info)); //分配以太网设备结构体 if (!ndev)      return -ENOMEM;     //(net)->dev.parent = (pdev) 网络设备和平台设备的关系   SET_NETDEV_DEV(ndev, &pdev->dev);    dev_dbg(&pdev->dev, "dm9000_probe()\n");   /* setup board info structure */        //返回net_device结构末端地址,也就是网卡私有数据结构的起始地址    db = netdev_priv(ndev);    db->dev = &pdev->dev;    db->ndev = ndev;        spin_lock_init(&db->lock); //初始化自旋锁  mutex_init(&db->addr_lock); //初始化互斥锁     //挂入dm9000_poll_work函数:dm9000_poll_work用于载波检测   INIT_DELAYED_WORK(&db->phy_poll, dm9000_poll_work);      db->addr_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); //地址资源   db->data_res = platform_get_resource(pdev, IORESOURCE_MEM, 1); //数据资源   db->irq_res  = platform_get_resource(pdev, IORESOURCE_IRQ, 0); //中断资源   //判断资源获取是否成功    if (db->addr_res == NULL || db->data_res == NULL ||       db->irq_res == NULL) {     dev_err(db->dev, "insufficient resources\n");      ret = -ENOENT;     goto out;   }   //获得中断号 db->irq_wake = platform_get_irq(pdev, 1);   if (db->irq_wake >= 0) {     dev_dbg(db->dev, "wakeup irq %d\n", db->irq_wake);      ret = request_irq(db->irq_wake, dm9000_wol_interrupt,                       IRQF_SHARED, dev_name(db->dev), ndev);       if (ret) {          dev_err(db->dev, "cannot get wakeup irq (%d)\n", ret);     } else {            /* test to see if irq is really wakeup capable */               ret = irq_set_irq_wake(db->irq_wake, 1);            if (ret) {              dev_err(db->dev, "irq %d cannot set wakeup (%d)\n",                                db->irq_wake, ret);              ret = 0;           } else {                irq_set_irq_wake(db->irq_wake, 0);                                       db->wake_supported = 1;         }       }   }   //需要分配的IO资源大小   iosize = resource_size(db->addr_res); //地址区资源   /*申请存储空间*/  db->addr_req = request_mem_region(db->addr_res->start, iosize,                      pdev->name);   if (db->addr_req == NULL) {        dev_err(db->dev, "cannot claim address reg area\n");       ret = -EIO;        goto out;   }   /*ioremap物理地址映射后才能使用*/  db->io_addr = ioremap(db->addr_res->start, iosize);   if (db->io_addr == NULL) {     dev_err(db->dev, "failed to ioremap address reg\n");       ret = -EINVAL;     goto out;   }   iosize = resource_size(db->data_res); //数据区资源   db->data_req = request_mem_region(db->data_res->start, iosize,                      pdev->name);   if (db->data_req == NULL) {        dev_err(db->dev, "cannot claim data reg area\n");      ret = -EIO;        goto out;}  db->io_data = ioremap(db->data_res->start, iosize); if (db->io_data == NULL) {       dev_err(db->dev, "failed to ioremap data reg\n");      ret = -EINVAL;     goto out;   }   /* fill in parameters for net-dev structure */  ndev->base_addr = (unsigned long)db->io_addr;    ndev->irq    = db->irq_res->start; //中断号  /* ensure at least we have a default set of IO routines */  dm9000_set_io(db, iosize); //set io width   /* check to see if anything is being over-ridden */ //pdata来自于mach-smkdv210中定义的smdkv210_dm9000_platdata if (pdata != NULL) {       /* check to see if the driver wants to over-ride the         * default IO width */      if (pdata->flags & DM9000_PLATF_8BITONLY)            dm9000_set_io(db, 1);       if (pdata->flags & DM9000_PLATF_16BITONLY)           dm9000_set_io(db, 2);       if (pdata->flags & DM9000_PLATF_32BITONLY)           dm9000_set_io(db, 4);       /* check to see if there are any IO routine over-rides */       if (pdata->inblk != NULL)           db->inblk = pdata->inblk;        if (pdata->outblk != NULL)          db->outblk = pdata->outblk;      if (pdata->dumpblk != NULL)         db->dumpblk = pdata->dumpblk;        db->flags = pdata->flags;    }
#ifdef CONFIG_DM9000_FORCE_SIMPLE_PHY_POLL  db->flags |= DM9000_PLATF_SIMPLE_PHY;
#endif /*dm9000 重置*/    dm9000_reset(db); /* try multiple times, DM9000 sometimes gets the read wrong */    //多次尝试读取dm9000 ID 测试读取是否正确  for (i = 0; i < 8; i++) {     id_val  = ior(db, DM9000_VIDL);        id_val |= (u32)ior(db, DM9000_VIDH) << 8;        id_val |= (u32)ior(db, DM9000_PIDL) << 16;       id_val |= (u32)ior(db, DM9000_PIDH) << 24;       if (id_val == DM9000_ID)          break;      dev_err(db->dev, "read wrong id 0x%08x\n", id_val);    }   if (id_val != DM9000_ID) {     dev_err(db->dev, "wrong id: 0x%08x\n", id_val);        ret = -ENODEV;     goto out;   }   /* Identify what type of DM9000 we are working on */    /*确认DM9000的具体型号*/   id_val = ior(db, DM9000_CHIPR);    dev_dbg(db->dev, "dm9000 revision 0x%02x\n", id_val);  switch (id_val) {   case CHIPR_DM9000A:     db->type = TYPE_DM9000A;        break;  case CHIPR_DM9000B:     db->type = TYPE_DM9000B;        break;  default:        dev_dbg(db->dev, "ID %02x => defaulting to DM9000E\n", id_val);                db->type = TYPE_DM9000E;    }   /* dm9000a/b are capable of hardware checksum offload */    if (db->type == TYPE_DM9000A || db->type == TYPE_DM9000B) {                       ndev->hw_features = NETIF_F_RXCSUM | NETIF_F_IP_CSUM;                       ndev->features |= ndev->hw_features; }   /* from this point we assume that we have found a DM9000 */     /* driver system function */    /*对ndev进行初始化*/  ether_setup(ndev);  ndev->netdev_ops = &dm9000_netdev_ops;  ndev->watchdog_timeo = msecs_to_jiffies(watchdog);  ndev->ethtool_ops    = &dm9000_ethtool_ops;     /*媒介层*/ db->msg_enable       = NETIF_MSG_LINK;  db->mii.phy_id_mask  = 0x1f;    db->mii.reg_num_mask = 0x1f;    db->mii.force_media  = 0;   db->mii.full_duplex  = 0;   db->mii.dev       = ndev;   db->mii.mdio_read    = dm9000_phy_read; db->mii.mdio_write   = dm9000_phy_write;    mac_src = "eeprom";  /* try reading the node address from the attached EEPROM */ for (i = 0; i < 6; i += 2)        dm9000_read_eeprom(db, i / 2, ndev->dev_addr+i);    if (!is_valid_ether_addr(ndev->dev_addr) && pdata != NULL) {                        mac_src = "platform data";       memcpy(ndev->dev_addr, pdata->dev_addr, 6);   }   if (!is_valid_ether_addr(ndev->dev_addr)) {      /* try reading from mac */              mac_src = "chip";        for (i = 0; i < 6; i++)           ndev->dev_addr[i] = ior(db, i+DM9000_PAR); }   if (!is_valid_ether_addr(ndev->dev_addr)) {      dev_warn(db->dev, "%s: Invalid ethernet MAC address. Please "                      "set using ifconfig\n", ndev->name);       eth_hw_addr_random(ndev);       mac_src = "random";}     //把ndev作为平台设备pdev的私有数据  platform_set_drvdata(pdev, ndev);/*进行注册*/   ret = register_netdev(ndev);   if (ret == 0)     printk(KERN_INFO "%s: dm9000%c at %p,%p IRQ %d MAC: %pM (%s)\n",              ndev->name, dm9000_type_to_char(db->type, db->io_addr,                         db->io_data, ndev->irq, ndev->dev_addr, mac_src);  return 0; out:  dev_err(db->dev, "not found (%d).\n", ret);    dm9000_release_board(pdev, db); free_netdev(ndev);  return ret;}

Linux 网络设备驱动(dm9000)相关推荐

  1. Linux网络设备驱动结构概述

    2019独角兽企业重金招聘Python工程师标准>>> 网络设备驱动相比字符型设备的驱动要复杂一些,除了总体上驱动的框架有一些相似外,有很多地方都是不同,但网络设备驱动有一个很大的特 ...

  2. STM32MP157驱动开发——Linux 网络设备驱动

    STM32MP157驱动开发--Linux 网络设备驱动 一.简介 STM32MP1 GMAC 接口简介 YT8511C 详解 二.驱动开发 1.网络外设的设备树 2.设备驱动 三.测试 网速测试 参 ...

  3. linux pci 网卡驱动,linux网络设备驱动_pci网卡

    <linux网络设备驱动_pci网卡>由会员分享,可在线阅读,更多相关<linux网络设备驱动_pci网卡(12页珍藏版)>请在技术文库上搜索. 1. LinuxLinux 网 ...

  4. Linux网络设备驱动-以太网驱动分析

    1.概述 网络上数据包的发送和接收由网络设备完成.网络设备与字符设备.块设备不同,其并不对应于/dev目录下的文件,也不能使用常规的操作方法操作网络设备.现在比较通用的做法是通过套接字访问网络设备.网 ...

  5. 编写Linux网络设备驱动(上)

    编写Linux网络设备驱动(上) <编写Linux网络设备驱动(上)> 来自:刘建文 | 学术半·IT歌·文 作者:刘建文 关键字:Linux 驱动程序 永久链接地址:http://art ...

  6. 《Linux设备驱动开发详解》——第16章 Linux网络设备驱动一

    Linux系统对网络设备驱动定义了4各层次:网络协议接口层,网络设备接口层,提供实际功能的设备驱动功能层,网络设备与媒介层 16.1 Linux网络设备驱动的结构 (1)网络协议接口层:提供统一的数据 ...

  7. Linux 网络设备驱动开发(一) —— linux内核网络分层结构

    Linux内核对网络驱动程序使用统一的接口,并且对于网络设备采用面向对象的思想设计. Linux内核采用分层结构处理网络数据包.分层结构与网络协议的结构匹配,既能简化数据包处理流程,又便于扩展和维护. ...

  8. 《linux设备驱动开发详解》笔记——14 linux网络设备驱动

    14.1 网络设备驱动结构 网络协议接口层:硬件无关,标准收发函数dev_queue_xmit()和netif_rx();  注意,netif_rx是将接收到的数据给上层,有时也在驱动收到数据以后调用 ...

  9. linux网络设备驱动结构体,Linux网络设备驱动之设备驱动的注册与注销(二)

    网络设备驱动的注册与注销由 register_netdev( ) 和 unregister_netdev( ) 函数完成,这两个函数的原型为: int register_netdev(struct n ...

最新文章

  1. CSDN博客生成目录
  2. asp.net panel 加html,ASP.NET 页面中动态增加的控件、添加事件
  3. 迁移svn项目到git
  4. 通过一般处理程序实现【文件上传】
  5. Controlling Execution
  6. C和C++混合编程(__cplusplus 与 external c 的使用)
  7. MFC开发——MFC项目创建
  8. wpf 修改输入框 光标_WPF中鼠标光标的设置
  9. 2022年iOS最新面试(底层基础)问题答案
  10. 网络与社会导论之幂律与富者更富及其与长尾、齐普夫定律等的关系
  11. 【2020-CVPR-3D人体姿态估计】MetaFuse :A Pre-trained Fusion Model for Human Pose Estimation
  12. Jenkins的windows10 从节点经常性掉线问题解决
  13. 【C语言典例】——day6:猴子吃桃
  14. Android模似器硬件加速
  15. 【OSATE学习笔记】AADL 样例 ARP4761 介绍
  16. jsp+ssm计算机毕业设计亿上汽车在线销售管理系统【附源码】
  17. MODIS数据介绍——波段、产品
  18. 计算机误删了文件回收站找不到,电脑的文件误删除,回收站也没有,可以在哪找到...
  19. 第 5 章 book / article
  20. 步进电机噪声和振动过大

热门文章

  1. android中TextView设置字体竖直排列
  2. 外汇套期保值交易策略
  3. Win32病毒入门(一)
  4. Loup Verlet计算统计力学的创始人
  5. 系统分析与设计-项目总结(一)
  6. 打印机自动打印之前的页面的解决方法
  7. 海外抖音如何引流到独立站或者其他电商平台渠道
  8. 将mqm用户的环境变量写入配置文件
  9. 研报复现:backtrader实现改进金叉策略(附代码)
  10. 【过关斩将7】面试谈薪资时,HR压价怎么办?