zoukankan      html  css  js  c++  java
  • Mini2440 DM9000 驱动分析(一)

    Mini2440 DM9000 驱动分析(一)

    硬件特性

    Mini2440开发板上DM9000的电气连接和Mach-mini2440.c文件的关系:

    PW_RST 连接到复位按键,复位按键按下,低电平触发重新初始化,初始化完成后5us后可以使用(The DM9000 is ready after 5us when this pin deasserted )

    CMD 连接到s3c2440 的ADD2 pin

    INT 连接到s3c2440 的EINT7/GPF7,将中断控制端口

    LINK_ACT 连接到网络接口的GLEDK pin,连接到LINK LED,这样网卡上面的灯才可以亮

    LINK_O、WAKEUP、SPEED100# 这三个pin并联之后连接的网络接口的YLEDK pin

    AEN 连接到s3c2440 的nGCS4/GPA15 pin

    IOR# 连接到s3c2440 的nOE pin

    IOW# 连接到s3c2440 的 nWE pin

    IOWAIT 直接通过电阻借电源,即高电平

    TXO-、TXO+、RX-、RX+ 直接接到网络端口用于数据收发

    SD0-15 连接到s3c2440 的DATA0-15

    其中片选信号AEN使用了nGCS4,所以网卡的内存区域在BANK4,也就是从地址0x20000000开始。

    DM9000的TXD[2:0]作为strap pin在电路图中是空接的,所以IO base是300H。

    中断使用了EINT7。

    代码如下:

    /* DM9000AEP 10/100 ethernet controller */
    #define MACH_MINI2440_DM9K_BASE (S3C2410_CS4 + 0x300)
    
    static struct resource mini2440_dm9k_resource[] = {
            [0] = {
                    .start = MACH_MINI2440_DM9K_BASE,
                    .end   = MACH_MINI2440_DM9K_BASE + 3,
                    .flags = IORESOURCE_MEM
            },
            [1] = {
                    .start = MACH_MINI2440_DM9K_BASE + 4,
                    .end   = MACH_MINI2440_DM9K_BASE + 7,
                    .flags = IORESOURCE_MEM
            },
            [2] = {
                    .start = IRQ_EINT7,
                    .end   = IRQ_EINT7,
                    .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHEDGE,
            }
    };
    
    /*
     *  * The DM9000 has no eeprom, and it's MAC address is set by
     *   * the bootloader before starting the kernel.
     *    */
    static struct dm9000_plat_data mini2440_dm9k_pdata = {
            .flags          = (DM9000_PLATF_16BITONLY | DM9000_PLATF_NO_EEPROM),
    };
    
    static struct platform_device mini2440_device_eth = {
            .name           = "dm9000",
            .id             = -1,
            .num_resources  = ARRAY_SIZE(mini2440_dm9k_resource),
            .resource       = mini2440_dm9k_resource,
            .dev            = {
                    .platform_data  = &mini2440_dm9k_pdata,
            },
    };

    研究net driver首先就要先了解一下网络驱动编写的一个基本架构

    两个重要的结构体简单介绍:sk_buff和net_device

    sk_buff

    如果把网络传输看成是运送货物的话,那么sk_buff就是这个“货物”了,所有经手这个货物的人都要干点什么事儿,要么加个包装,要么印个戳儿等等。收货的时候就要拆掉这些包装,得到我们需要的货物(payload data)。没有货物你还运输什么呢?由此可见sk_buff的重要性了。

    关于sk_buff的详细介绍和几个操作它的函数,参考:“linux内核sk_buff的结构分析” http://www.linuxidc.com/Linux/2011-07/39163.htm,写得非常明白了。赞一个~

    net_device

    又是一个庞大的结构体。好吧,我承认我从来就没有看全过这个结构体。它在内核中就是指代了一个网络设备。驱动程序需要在探测的时候分配并初始化这个结构体,然后使用register_netdev来注册它,这样就可以把操作硬件的函数与内核挂接在一起。

    对于我们来说probe是一切一切的开始,看看dm9000驱动probe需要用要的结构体

    board_info

    /* 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;
    	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;
    
    	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;
    
    	int		rx_csum;
    	int		can_csum;
    	int		ip_summed;
    } board_info_t;
    

    其中有两个很重要的结构体 net_device 和 mii_if_info

    mii_if_info

    struct mii_if_info {
    	int phy_id;
    	int advertising;
    	int phy_id_mask;
    	int reg_num_mask;
    
    	unsigned int full_duplex : 1;	/* is full duplex? */
    	unsigned int force_media : 1;	/* is autoneg. disabled? */
    	unsigned int supports_gmii : 1; /* are GMII registers supported? */
    
    	struct net_device *dev;
    	int (*mdio_read) (struct net_device *dev, int phy_id, int location);
    	void (*mdio_write) (struct net_device *dev, int phy_id, int location, int val);
    };

    net_device 

    /*
     *	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
    	 * the interface.
    	 */
    	char			name[IFNAMSIZ];
    	/* 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;
    
    	/* Net device features */
    	unsigned long		features;
    #define NETIF_F_SG		1	/* Scatter/gather IO. */
    #define NETIF_F_IP_CSUM		2	/* Can checksum TCP/UDP over IPv4. */
    #define NETIF_F_NO_CSUM		4	/* Does not require checksum. F.e. loopack. */
    #define NETIF_F_HW_CSUM		8	/* Can checksum all the packets. */
    #define NETIF_F_IPV6_CSUM	16	/* Can checksum TCP/UDP over IPV6 */
    #define NETIF_F_HIGHDMA		32	/* Can DMA to high memory. */
    #define NETIF_F_FRAGLIST	64	/* Scatter/gather IO. */
    #define NETIF_F_HW_VLAN_TX	128	/* Transmit VLAN hw acceleration */
    #define NETIF_F_HW_VLAN_RX	256	/* Receive VLAN hw acceleration */
    #define NETIF_F_HW_VLAN_FILTER	512	/* Receive filtering on VLAN */
    #define NETIF_F_VLAN_CHALLENGED	1024	/* Device cannot handle VLAN packets */
    #define NETIF_F_GSO		2048	/* Enable software GSO. */
    #define NETIF_F_LLTX		4096	/* LockLess TX - deprecated. Please */
    					/* do not use LLTX in new drivers */
    #define NETIF_F_NETNS_LOCAL	8192	/* Does not change network namespaces */
    #define NETIF_F_GRO		16384	/* Generic receive offload */
    #define NETIF_F_LRO		32768	/* large receive offload */
    
    /* the GSO_MASK reserves bits 16 through 23 */
    #define NETIF_F_FCOE_CRC	(1 << 24) /* FCoE CRC32 */
    #define NETIF_F_SCTP_CSUM	(1 << 25) /* SCTP checksum offload */
    #define NETIF_F_FCOE_MTU	(1 << 26) /* Supports max FCoE MTU, 2158 bytes*/
    
    	/* Segmentation offload features */
    #define NETIF_F_GSO_SHIFT	16
    #define NETIF_F_GSO_MASK	0x00ff0000
    #define NETIF_F_TSO		(SKB_GSO_TCPV4 << NETIF_F_GSO_SHIFT)
    #define NETIF_F_UFO		(SKB_GSO_UDP << NETIF_F_GSO_SHIFT)
    #define NETIF_F_GSO_ROBUST	(SKB_GSO_DODGY << NETIF_F_GSO_SHIFT)
    #define NETIF_F_TSO_ECN		(SKB_GSO_TCP_ECN << NETIF_F_GSO_SHIFT)
    #define NETIF_F_TSO6		(SKB_GSO_TCPV6 << NETIF_F_GSO_SHIFT)
    #define NETIF_F_FSO		(SKB_GSO_FCOE << NETIF_F_GSO_SHIFT)
    
    	/* List of features with software fallbacks. */
    #define NETIF_F_GSO_SOFTWARE	(NETIF_F_TSO | NETIF_F_TSO_ECN | NETIF_F_TSO6)
    
    
    #define NETIF_F_GEN_CSUM	(NETIF_F_NO_CSUM | NETIF_F_HW_CSUM)
    #define NETIF_F_V4_CSUM		(NETIF_F_GEN_CSUM | NETIF_F_IP_CSUM)
    #define NETIF_F_V6_CSUM		(NETIF_F_GEN_CSUM | NETIF_F_IPV6_CSUM)
    #define NETIF_F_ALL_CSUM	(NETIF_F_V4_CSUM | NETIF_F_V6_CSUM)
    
    	/*
    	 * If one device supports one of these features, then enable them
    	 * for all in netdev_increment_features.
    	 */
    #define NETIF_F_ONE_FOR_ALL	(NETIF_F_GSO_SOFTWARE | NETIF_F_GSO_ROBUST | 
    				 NETIF_F_SG | NETIF_F_HIGHDMA |		
    				 NETIF_F_FRAGLIST)
    
    	/* Interface index. Unique device identifier	*/
    	int			ifindex;
    	int			iflink;
    
    	struct net_device_stats	stats;
    
    #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 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		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_len;	/* hardware address length	*/
    	unsigned short          dev_id;		/* for shared network cards */
    
    	struct netdev_hw_addr_list	uc;	/* Secondary unicast
    						   mac addresses */
    	int			uc_promisc;
    	spinlock_t		addr_list_lock;
    	struct dev_addr_list	*mc_list;	/* Multicast mac addresses	*/
    	int			mc_count;	/* Number of installed mcasts	*/
    	unsigned int		promiscuity;
    	unsigned int		allmulti;
    
    
    	/* Protocol specific pointers */
    	
    #ifdef CONFIG_NET_DSA
    	void			*dsa_ptr;	/* dsa specific data */
    #endif
    	void 			*atalk_ptr;	/* AppleTalk link 	*/
    	void			*ip_ptr;	/* IPv4 specific data	*/
    	void                    *dn_ptr;        /* DECnet specific data */
    	void                    *ip6_ptr;       /* IPv6 specific data */
    	void			*ec_ptr;	/* Econet specific data	*/
    	void			*ax25_ptr;	/* AX.25 specific data */
    	struct wireless_dev	*ieee80211_ptr;	/* IEEE 802.11 specific data,
    						   assign before registering */
    
    /*
     * Cache line mostly used on receive path (including eth_type_trans())
     */
    	unsigned long		last_rx;	/* Time of last Rx	*/
    	/* Interface address info used in eth_type_trans() */
    	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	*/
    
    	struct netdev_queue	rx_queue;
    
    	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;
    /*
     * One part is mostly used on xmit path (device)
     */
    	/* 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 */
    	atomic_t		refcnt ____cacheline_aligned_in_smp;
    
    	/* delayed register/unregister */
    	struct list_head	todo_list;
    	/* device index hash chain */
    	struct hlist_node	index_hlist;
    
    	struct net_device	*link_watch_next;
    
    	/* 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;
    
    	/* 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 */
    	void			*ml_priv;
    
    	/* bridge stuff */
    	struct net_bridge_port	*br_port;
    	/* macvlan */
    	struct macvlan_port	*macvlan_port;
    	/* GARP */
    	struct garp_port	*garp_port;
    
    	/* class/net/name entry */
    	struct device		dev;
    	/* space for optional statistics and wireless sysfs groups */
    	const struct attribute_group *sysfs_groups[3];
    
    	/* rtnetlink link ops */
    	const struct rtnl_link_ops *rtnl_link_ops;
    
    	/* VLAN feature mask */
    	unsigned long vlan_features;
    
    	/* 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 */
    	struct dcbnl_rtnl_ops *dcbnl_ops;
    #endif
    
    #if defined(CONFIG_FCOE) || defined(CONFIG_FCOE_MODULE)
    	/* max exchange id for FCoE LRO by ddp */
    	unsigned int		fcoe_ddp_xid;
    #endif
    };
    

    确实是如此庞大的一个结构体,让人畏惧的感觉,饭要一口一口吃啊O(∩_∩)O~

    另外,还是认识一下另一个上面提到的庞大结构体吧,sk_buff

    /** 
     *	struct sk_buff - socket buffer
     *	@next: Next buffer in list
     *	@prev: Previous buffer in list
     *	@sk: Socket we are owned by
     *	@tstamp: Time we arrived
     *	@dev: Device we arrived on/are leaving by
     *	@transport_header: Transport layer header
     *	@network_header: Network layer header
     *	@mac_header: Link layer header
     *	@_skb_dst: destination entry
     *	@sp: the security path, used for xfrm
     *	@cb: Control buffer. Free for use by every layer. Put private vars here
     *	@len: Length of actual data
     *	@data_len: Data length
     *	@mac_len: Length of link layer header
     *	@hdr_len: writable header length of cloned skb
     *	@csum: Checksum (must include start/offset pair)
     *	@csum_start: Offset from skb->head where checksumming should start
     *	@csum_offset: Offset from csum_start where checksum should be stored
     *	@local_df: allow local fragmentation
     *	@cloned: Head may be cloned (check refcnt to be sure)
     *	@nohdr: Payload reference only, must not modify header
     *	@pkt_type: Packet class
     *	@fclone: skbuff clone status
     *	@ip_summed: Driver fed us an IP checksum
     *	@priority: Packet queueing priority
     *	@users: User count - see {datagram,tcp}.c
     *	@protocol: Packet protocol from driver
     *	@truesize: Buffer size 
     *	@head: Head of buffer
     *	@data: Data head pointer
     *	@tail: Tail pointer
     *	@end: End pointer
     *	@destructor: Destruct function
     *	@mark: Generic packet mark
     *	@nfct: Associated connection, if any
     *	@ipvs_property: skbuff is owned by ipvs
     *	@peeked: this packet has been seen already, so stats have been
     *		done for it, don't do them again
     *	@nf_trace: netfilter packet trace flag
     *	@nfctinfo: Relationship of this skb to the connection
     *	@nfct_reasm: netfilter conntrack re-assembly pointer
     *	@nf_bridge: Saved data about a bridged frame - see br_netfilter.c
     *	@iif: ifindex of device we arrived on
     *	@queue_mapping: Queue mapping for multiqueue devices
     *	@tc_index: Traffic control index
     *	@tc_verd: traffic control verdict
     *	@ndisc_nodetype: router type (from link layer)
     *	@dma_cookie: a cookie to one of several possible DMA operations
     *		done by skb DMA functions
     *	@secmark: security marking
     *	@vlan_tci: vlan tag control information
     */
    
    struct sk_buff {
    	/* These two members must be first. */
    	struct sk_buff		*next;
    	struct sk_buff		*prev;
    
    	struct sock		*sk;
    	ktime_t			tstamp;
    	struct net_device	*dev;
    
    	unsigned long		_skb_dst;
    #ifdef CONFIG_XFRM
    	struct	sec_path	*sp;
    #endif
    	/*
    	 * 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];
    
    	unsigned 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;
    	__be16			protocol:16;
    	kmemcheck_bitfield_end(flags1);
    
    	void			(*destructor)(struct sk_buff *skb);
    #if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE)
    	struct nf_conntrack	*nfct;
    	struct sk_buff		*nfct_reasm;
    #endif
    #ifdef CONFIG_BRIDGE_NETFILTER
    	struct nf_bridge_info	*nf_bridge;
    #endif
    
    	int			iif;
    #ifdef CONFIG_NET_SCHED
    	__u16			tc_index;	/* traffic control index */
    #ifdef CONFIG_NET_CLS_ACT
    	__u16			tc_verd;	/* traffic control verdict */
    #endif
    #endif
    
    	kmemcheck_bitfield_begin(flags2);
    	__u16			queue_mapping:16;
    #ifdef CONFIG_IPV6_NDISC_NODETYPE
    	__u8			ndisc_nodetype:2;
    #endif
    	kmemcheck_bitfield_end(flags2);
    
    	/* 0/14 bit hole */
    
    #ifdef CONFIG_NET_DMA
    	dma_cookie_t		dma_cookie;
    #endif
    #ifdef CONFIG_NETWORK_SECMARK
    	__u32			secmark;
    #endif
    
    	__u32			mark;
    
    	__u16			vlan_tci;
    
    	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;
    };
    

    看到这是结构,让我深深感觉到彻底理清网卡驱动的编写还需要好好下一番功夫,莫要小看了它啊

    下面会开始具体分析网卡驱动的整个probe过程,很关键的知识点。。。。

  • 相关阅读:
    机器任务——最小点覆盖
    树的统计
    农夫约翰
    关押罪犯
    题单
    加分二叉树(递归,区间DP)
    [动态规划] 斜率优化DP
    [树形DP] 换根DP
    [期望DP][SCOI2008] 奖励关
    [数位DP][AHOI2009] Luogu P4127 同类分布
  • 原文地址:https://www.cnblogs.com/dyllove98/p/3235279.html
Copyright © 2011-2022 走看看