(一)mini2440网卡驱动dm9000之dm9000_probe分析

2023-10-15 07:18

本文主要是介绍(一)mini2440网卡驱动dm9000之dm9000_probe分析,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!


2012-04-07 02:05:48|  分类: 跟着国嵌学arm|举报|字号 订阅

/*首先我们必须知道probe函数什么时候调用。其实在平台设备驱动注册的时候,内核会在平台设备总线上去遍历所有的设备,做匹配操作,匹配函数为platform_match(内核源码在driver/base/platform.c中)那么这个函数做了什么呢,不妨来看一下:

static int platform_match(struct device *dev, struct device_driver *drv)

{

struct platform_device *pdev;


pdev = container_of(dev, struct platform_device, dev);//获取platform_device指针

return (strncmp(pdev->name, drv->name, BUS_ID_SIZE) == 0);

}

由最后一句可以看出,当pdev->name与drv->name名字相同的时候,匹配成功。这就提醒我们,在编写平台设备驱动的时候,一定要保证设备的name和驱动的name相同。

*/

static int __devinit dm9000_probe(struct platform_device *pdev)
{
 struct dm9000_plat_data *pdata = pdev->dev.platform_data;/*关于这个结构体请见注释一*/

 struct board_info *db; /* 指向一个单板信息结构体,具体关于这个结构体的具体信息请见注释二*/
 struct net_device *ndev; //网络设备结构体
 const unsigned char *mac_src;//硬件资源
 int ret = 0;
 int iosize;
 int i;
 u32 id_val;

# if defined(CONFIG_ARCH_S3C2410)
 unsigned int oldval_bwscon = *(volatile unsigned int *)S3C2410_BWSCON;/*保存位宽等待寄存器??*/
 unsigned int oldval_bankcon4 = *(volatile unsigned int *)S3C2410_BANKCON4;/*保存控制寄存器??*/
# endif

 /* Init network device */
 ndev = alloc_etherdev(sizeof(struct board_info));/*用于分配一个struct net_device结构体,成功则返回指向struct net_device结构体

                                                                               的指针,否则返回NULL*/
 if (!ndev) {
  dev_err(&pdev->dev, "could not allocate device.\n");
  return -ENOMEM;
 }    //出错处理


/*SET_NETDEV_DEV定义在netdevice.h,其原型为:#define SET_NETDEV_DEV(net, pdev) ((net)->dev.parent = (pdev))*/

/*从上面可以看出,这行代码的实际上等于:ndev->dev.parent=&pdev->dev,关于这个问题,我百思不得其解,因为在net_device结构体中,根本就不存在一个名为dev的struct device结构体,那么我的理解就是,不存在就不写,直接来个ndev->pdev->dev,这样就将挂载在平台设备总线上的设备交给了网络设备,也就是说,这时的网路设备就是挂载在平台设备驱动上的设备。
  */ 

 SET_NETDEV_DEV(ndev, &pdev->dev);

 /*定义在device.h,详见注释三*/
 dev_dbg(&pdev->dev, "dm9000_probe()\n");

#if defined(CONFIG_ARCH_S3C2410)
 *((volatile unsigned int *)S3C2410_BWSCON) =
   (oldval_bwscon & ~(3<<16)) | S3C2410_BWSCON_DW4_16 | S3C2410_BWSCON_WS4 | S3C2410_BWSCON_ST4;
 *((volatile unsigned int *)S3C2410_BANKCON4) = 0x1f7c;
#endif

 /*下面都是设置board_info结构体*/
 
 db = netdev_priv(ndev);//获取网络设备私有信息指针
 memset(db, 0, sizeof(*db));//清零

 db->dev = &pdev->dev;//是哪一个父设备????????????????????????????
 db->ndev = ndev;//是哪一个网络设备??????????????????????????????

 spin_lock_init(&db->lock);/*初始化自旋锁,并把自旋锁的lock_raw_lock置1(未锁)详见注释四*/
 mutex_init(&db->addr_lock);/*这个函数定义在include\linux\mutex.h中,用于初始化互斥锁*/

 /*用于初始化一个任务,之后如果希望调用dm9000_poll_work这个函数,只需要用一句schedule_delayed_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;
 }

 iosize = res_size(db->addr_res);//计算资源大小
 /*request_mem_region用于申请I\O内存,申请了I\O内存之后还需要ioremap来映射到内存空间,以便操作,详见注释四*/
 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;
 }
 /*设的 I/O 端口的物理地址就被映射到内存地址空间中,这个内存空间就是上面用request_mem_region申请的空间*/
 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 = res_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;
 }
 
 /* 设置结构体board_info结束 */

 /*填充net_device 结构体*/
 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  根据芯片的外部总线带宽,指定对应的输入输出函数*/
 dm9000_set_io(db, iosize);

 /* check to see if anything is being over-ridden */
 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_reset(db);/*复位DM9000芯片*/

 /* try multiple times, DM9000 sometimes gets the read wrong */
 /*读一下生产商和制造商的ID,应该是0x90000A46。 关键函数:ior()*/
 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;
 }

 /* from this point we assume that we have found a DM9000 */

 /* driver system function */
 /* 借助ether_setup()函数来部分初始化ndev。因为对以太
    网设备来讲,很多操作与属性是固定的,内核可以帮助完成*/

 ether_setup(ndev);

 /*手动初始化ndev的ops和db的mii部分*/
 ndev->open   = &dm9000_open;
 ndev->hard_start_xmit    = &dm9000_start_xmit;
 ndev->tx_timeout         = &dm9000_timeout;
 ndev->watchdog_timeo = msecs_to_jiffies(watchdog);
 ndev->stop   = &dm9000_stop;
 ndev->set_multicast_list = &dm9000_hash_table;
 ndev->ethtool_ops  = &dm9000_ethtool_ops;
 ndev->do_ioctl   = &dm9000_ioctl;

#ifdef CONFIG_NET_POLL_CONTROLLER
 ndev->poll_controller  = &dm9000_poll_controller;
#endif

 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;

#if defined(CONFIG_ARCH_S3C2410)
 printk("Now use the default MAC address: 08:90:90:90:90:90\n");
 mac_src = "friendly-arm";
 ndev->dev_addr[0] = 0x08;
 ndev->dev_addr[1] = 0x90;
 ndev->dev_addr[2] = 0x90;
 ndev->dev_addr[3] = 0x90;
 ndev->dev_addr[4] = 0x90;
 ndev->dev_addr[5] = 0x90;
#else
 mac_src = "eeprom";

 /* try reading the node address from the attached EEPROM */
/* 
  (如果有的话)从EEPROM中读取节点地址。这里可以看到mini2440这个板子
  上没有为DM9000外挂EEPROM,所以读取出来的全部是0xff。
  见函数dm9000_read_eeprom。 关于外挂EEPROM,可以参考datasheet上的7.EEPROM Format一节
*/

 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);
#endif

/*
 很显然ndev是我们在probe函数中定义的局部变量,如果我想在其他
 地方使用它怎么办呢? 这就需要把它保存起来。内核提供了这个方
 法,使用函数platform_set_drvdata()可以将ndev保存成平台总线设备的
 私有数据。以后再要使用它时只需调用platform_get_drvdata()就可以了
*/

 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:
#if defined(CONFIG_ARCH_S3C2410)
    *(volatile unsigned int *)S3C2410_BWSCON   = oldval_bwscon;
    *(volatile unsigned int *)S3C2410_BANKCON4 = oldval_bankcon4;
#endif
 dev_err(db->dev, "not found (%d).\n", ret);

 dm9000_release_board(pdev, db);
 free_netdev(ndev);

 return ret;
}

 

注释一:

struct dm9000_plat_data {

unsigned int flags;

unsigned char dev_addr[6];


/* allow replacement IO routines */


void (*inblk)(void __iomem *reg, void *data, int len);

void (*outblk)(void __iomem *reg, void *data, int len);

void (*dumpblk)(void __iomem *reg, int len);

};

注释二:(定义在dm9000.c中)

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 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;

} board_info_t;

注释三:这个函数的实质是调用 printk(KERN_DEBUG )来输出打印信息
  可以参考:http://www.cnblogs.com/cute/archive/2011/05/16/2047868.html

注释四:自旋锁http://blog.csdn.net/unbutun/article/details/5730068
 
注释五:http://hi.baidu.com/312860519/blog/item/78a82b60695ba75beaf8f870.html

注释六:http://www.cnblogs.com/cute/archive/2011/03/23/1992651.html

注释七:首先我们来规范两个概念和一个事实。这两个概念就是io端口和io内存,当一个寄存器或内存位于io空间是,称为io端口,而当他们处于内核空间时,我们称之为io内存。一个事实就是ARM处理器只支持内存空间。好,接下来我们正式分析:

我们知道s3c2440将系统的存储空间分为八组,共1G。bank0--bank5的开始地址是固定的,用于ROM和SRAM。band6--bank7用于ROM、SRAM、SDRAM,这两组可编程并且大小相同。如下图所示:

(一)mini2440网卡驱动dm9000之dm9000_probe分析 - 航天 - 航天的博客
 

 然后再让我们来看一看dm9000与cpu的连接情况:

(一)mini2440网卡驱动dm9000之dm9000_probe分析 - 航天 - 航天的博客
从dm9000的连接图上我们看出,使用的片选信号是NGCS4(低电平有效),也就是说我们的网卡是挂在cpu的bank4空间的 ,bank4的基地址是:0x20000000。如果是物理地址直接寻址的话,很简单,就是发送地址,然后读或者写。但是linux系统只能访问内存空间,不能访问io空间,所以我们要将物理地址映射成内存空间的虚拟地址才能访问,ioremap就完成了这一工作。

我们以实例来分析一下,db->io_data= ioremap(db->data_res->start, iosize);其中db->data_res->start为0x20000000。这行代码的意思就是,将db->data_res->start开始,iosize大小的io空间映射到了内核空间,现在内核空间db->io_addr----db->io_addr之间的空间就相当于外设存储空间,之后才可以通过这些虚拟地址对io内存空间进行访问,但i要注意的是,现在对io内存的访问只能使用readb,writeb此类的函数,来进行。(关于这个问题目前也只是明白到这个地步,也不知到底对不对,今后还会更深一步分析)

小结:(1)申请并初始化一个net_device结构体,用到alloc_etherdev(sizeof(struct board_info))

           (2)使net_device与platform_device建立联系,用到SET_NETDEV_DEV

           (3)设置board_info结构体。这一步中,最主要的是将I\O内存映射到内存空间,以便操作。用到

                     request_mem_region和ioremap,前者用来申请I\O内存,后者用于将申请到的I\O内存映射到

                     内存空间。

            (4)设置net_device 结构体。初始化net_device结构体可分为用ether_setup()函数来部分初始化和手动初始化两步。

            (5)网络设备注册。使用函数register_netdev(ndev)。

这篇关于(一)mini2440网卡驱动dm9000之dm9000_probe分析的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/216195

相关文章

性能分析之MySQL索引实战案例

文章目录 一、前言二、准备三、MySQL索引优化四、MySQL 索引知识回顾五、总结 一、前言 在上一讲性能工具之 JProfiler 简单登录案例分析实战中已经发现SQL没有建立索引问题,本文将一起从代码层去分析为什么没有建立索引? 开源ERP项目地址:https://gitee.com/jishenghua/JSH_ERP 二、准备 打开IDEA找到登录请求资源路径位置

Linux_kernel驱动开发11

一、改回nfs方式挂载根文件系统         在产品将要上线之前,需要制作不同类型格式的根文件系统         在产品研发阶段,我们还是需要使用nfs的方式挂载根文件系统         优点:可以直接在上位机中修改文件系统内容,延长EMMC的寿命         【1】重启上位机nfs服务         sudo service nfs-kernel-server resta

SWAP作物生长模型安装教程、数据制备、敏感性分析、气候变化影响、R模型敏感性分析与贝叶斯优化、Fortran源代码分析、气候数据降尺度与变化影响分析

查看原文>>>全流程SWAP农业模型数据制备、敏感性分析及气候变化影响实践技术应用 SWAP模型是由荷兰瓦赫宁根大学开发的先进农作物模型,它综合考虑了土壤-水分-大气以及植被间的相互作用;是一种描述作物生长过程的一种机理性作物生长模型。它不但运用Richard方程,使其能够精确的模拟土壤中水分的运动,而且耦合了WOFOST作物模型使作物的生长描述更为科学。 本文让更多的科研人员和农业工作者

MOLE 2.5 分析分子通道和孔隙

软件介绍 生物大分子通道和孔隙在生物学中发挥着重要作用,例如在分子识别和酶底物特异性方面。 我们介绍了一种名为 MOLE 2.5 的高级软件工具,该工具旨在分析分子通道和孔隙。 与其他可用软件工具的基准测试表明,MOLE 2.5 相比更快、更强大、功能更丰富。作为一项新功能,MOLE 2.5 可以估算已识别通道的物理化学性质。 软件下载 https://pan.quark.cn/s/57

衡石分析平台使用手册-单机安装及启动

单机安装及启动​ 本文讲述如何在单机环境下进行 HENGSHI SENSE 安装的操作过程。 在安装前请确认网络环境,如果是隔离环境,无法连接互联网时,请先按照 离线环境安装依赖的指导进行依赖包的安装,然后按照本文的指导继续操作。如果网络环境可以连接互联网,请直接按照本文的指导进行安装。 准备工作​ 请参考安装环境文档准备安装环境。 配置用户与安装目录。 在操作前请检查您是否有 sud

线性因子模型 - 独立分量分析(ICA)篇

序言 线性因子模型是数据分析与机器学习中的一类重要模型,它们通过引入潜变量( latent variables \text{latent variables} latent variables)来更好地表征数据。其中,独立分量分析( ICA \text{ICA} ICA)作为线性因子模型的一种,以其独特的视角和广泛的应用领域而备受关注。 ICA \text{ICA} ICA旨在将观察到的复杂信号

【软考】希尔排序算法分析

目录 1. c代码2. 运行截图3. 运行解析 1. c代码 #include <stdio.h>#include <stdlib.h> void shellSort(int data[], int n){// 划分的数组,例如8个数则为[4, 2, 1]int *delta;int k;// i控制delta的轮次int i;// 临时变量,换值int temp;in

三相直流无刷电机(BLDC)控制算法实现:BLDC有感启动算法思路分析

一枚从事路径规划算法、运动控制算法、BLDC/FOC电机控制算法、工控、物联网工程师,爱吃土豆。如有需要技术交流或者需要方案帮助、需求:以下为联系方式—V 方案1:通过霍尔传感器IO中断触发换相 1.1 整体执行思路 霍尔传感器U、V、W三相通过IO+EXIT中断的方式进行霍尔传感器数据的读取。将IO口配置为上升沿+下降沿中断触发的方式。当霍尔传感器信号发生发生信号的变化就会触发中断在中断

kubelet组件的启动流程源码分析

概述 摘要: 本文将总结kubelet的作用以及原理,在有一定基础认识的前提下,通过阅读kubelet源码,对kubelet组件的启动流程进行分析。 正文 kubelet的作用 这里对kubelet的作用做一个简单总结。 节点管理 节点的注册 节点状态更新 容器管理(pod生命周期管理) 监听apiserver的容器事件 容器的创建、删除(CRI) 容器的网络的创建与删除

PostgreSQL核心功能特性与使用领域及场景分析

PostgreSQL有什么优点? 开源和免费 PostgreSQL是一个开源的数据库管理系统,可以免费使用和修改。这降低了企业的成本,并为开发者提供了一个活跃的社区和丰富的资源。 高度兼容 PostgreSQL支持多种操作系统(如Linux、Windows、macOS等)和编程语言(如C、C++、Java、Python、Ruby等),并提供了多种接口(如JDBC、ODBC、ADO.NET等