ARM AMBA 总线设备驱动总结

最近公司裁员,本人所在的部门全部被lay off了。 呵呵,闲来无事,一边找工作,一边学习linux driver 相关的知识。最近在学习ARM AMBA 总线设备驱动的相关知识,在网上搜索了一下相关的文章,并不是很多,而且有的文章描述的并不是很清楚,无奈之下,只好硬着头皮,自己HACK一下源码了。以下是本人的一些总结,如有不妥之处,还望各位大虾们多多指教。

1:总线

    总线是处理器与一个或多个设备之间的通道,在设备模型中,所有的设备都是通过总线相连。在Linux设备模型中,用bus_type结构表示总线,它定义在<linux/device.h>中:

        struct bus_type {
    const char        * name;

    struct subsystem    subsys;
    struct kset        drivers;
    struct kset        devices;
    struct klist        klist_devices;
    struct klist        klist_drivers;

    struct blocking_notifier_head bus_notifier;

    struct bus_attribute    * bus_attrs;
    struct device_attribute    * dev_attrs;
    struct driver_attribute    * drv_attrs;

    int        (*match)(struct device * dev, struct device_driver * drv);
    int        (*uevent)(struct device *dev, char **envp,
                int num_envp, char *buffer, int buffer_size);
    int        (*probe)(struct device * dev);
    int        (*remove)(struct device * dev);
    void        (*shutdown)(struct device * dev);

    int (*suspend)(struct device * dev, pm_message_t state);
    int (*suspend_late)(struct device * dev, pm_message_t state);
    int (*resume_early)(struct device * dev);
    int (*resume)(struct device * dev);
};
    我们注意到,一个总线下面包含了两个kset,分别代表了总线的驱动程序和插入总线的所有设备。

2:  AMBA总线的声明与注册

    在<driver/amba/bus.c>中有对AMBA总线的声明,如下:

    /*
 * Primecells are part of the Advanced Microcontroller Bus Architecture,
 * so we call the bus "amba".
 */
static struct bus_type amba_bustype = {
 .name  = "amba",
 .match  = amba_match,
 .uevent  = amba_uevent,
 .suspend = amba_suspend,
 .resume  = amba_resume,
};

    注意,只有非常少的bus_type成员需要初始化,他们中大部分是由设备模型中心来控制。

    对于新的总线,我们必须调用bus_register进行注册:

    static int __init amba_init(void)
{
 return bus_register(&amba_bustype);
}

    如果注册成功,新的总线子系统就会被添加到系统中,可以在sysfs的sys/bus目录下看到它,然后我们就可以向这个总线添加设备了。

3:AMBA总线设备和驱动程序的加载

    以上AMBA总线层的驱动程序(这些方法在后面将会有所介绍)在内核中已经写好了,我们要做的工作是如何将自己的AMBA设备以及驱动程序加载到AMBA总线中去。下面,将以AMBA-PL011这个串口设备为例,解释驱动和设备是如何被添加的。

    首先我们来看看如何声明一个AMBA总线设备

    AMBA设备结构定义在文件<linux/amba/bus.h>中,如下;

struct amba_device {
 struct device  dev;
 struct resource  res;
 u64   dma_mask;
 unsigned int  periphid;
 unsigned int  irq[AMBA_NR_IRQS];
};

    例如,我们定义一个串口设备UART0:

static struct amba_device uart0_device = {   /
 .dev  = {     /
  .coherent_dma_mask = ~0,   /
  .bus_id = dev:f1,    /
  .platform_data = NULL,    /
 },       /
 .res  = {     /         //端口资源
  .start = UART0_BASE,  /    //设备起始的物理地址
  .end = (UART0_BASE) + SZ_4K - 1,/
  .flags = IORESOURCE_MEM,   /
 },       /
 .dma_mask = ~0,     /
 .irq  = UART0_IRQ,    /     //设备的中断号
 }

    在Linux系统中每一个设备都用一个device结构的一个实例来表示:

    struct device {
 struct klist  klist_children;
 struct klist_node knode_parent;  /* node in sibling list */
 struct klist_node knode_driver;
 struct klist_node knode_bus;
 struct device  * parent;

 struct kobject kobj;
 char bus_id[BUS_ID_SIZE]; /* position on parent bus */
 unsigned  is_registered:1;
 struct device_attribute uevent_attr;
 struct device_attribute *devt_attr;

 struct semaphore sem; /* semaphore to synchronize calls to
      * its driver.
      */

 struct bus_type * bus;  /* type of bus device is on */
 struct device_driver *driver; /* which driver has allocated this
        device */
 void  *driver_data; /* data private to the driver */
 void  *platform_data; /* Platform specific data, device
        core doesn't touch it */
 void  *firmware_data; /* Firmware specific data (e.g. ACPI,
        BIOS data),reserved for device core*/
 struct dev_pm_info power;

 u64  *dma_mask; /* dma mask (if dma'able device) */
 u64  coherent_dma_mask;/* Like dma_mask, but for
          alloc_coherent mappings as
          not all hardware supports
          64 bit addresses for consistent
          allocations such descriptors. */

 struct list_head dma_pools; /* dma pools (if dma'ble) */

 struct dma_coherent_mem *dma_mem; /* internal for coherent mem
          override */

 /* class_device migration path */
 struct list_head node;
 struct class  *class;  /* optional*/
 dev_t   devt;  /* dev_t, creates the sysfs "dev" */
 struct attribute_group **groups; /* optional groups */

 void (*release)(struct device * dev);
};

    上面的数据结构的成员很多,其中几个成员需要了解一下:

struct device *parent: 该设备所属的设备,如果为NULL,则表示是顶层设备;

char bus_id[BUS_ID_SIZE]: 在总线上唯一表示该设备的字符串;

struct bus_type *bus: 表示该设备连接到了何种类型的总线上;

struct device_driver: 管理该设备的驱动程序。

 

    在定义了一AMBA设备之后,我们需要将此设备注册到总线上,调用如下函数:

int amba_device_register(struct amba_device *dev, struct resource *parent)

此函数定义在<driver/amba/bus.c>中,下面对这个函数做一些详细分析,看看设备是如何挂载到总线上面去的,以上面的设备为例,我们调用接口函数如下:

    amba_device_register(&uart0_device, &iomem_resource);

    其中,iomem_resource是一IO内存资源,定义如下:

struct resource iomem_resource = {
 .name = "PCI mem",
 .start = 0,
 .end = -1,
 .flags = IORESOURCE_MEM,
};

amba_device_register具体分析如下:


/**
 * amba_device_register - register an AMBA device
 * @dev: AMBA device to register
 * @parent: parent memory resource
 *
 * Setup the AMBA device, reading the cell ID if present.
 * Claim the resource, and register the AMBA device with
 * the Linux device manager.
 */
int amba_device_register(struct amba_device *dev, struct resource *parent)
{
 u32 pid, cid;
 void __iomem *tmp;
 int i, ret;

 dev->dev.release = amba_device_release;
 dev->dev.bus = &amba_bustype;//设备挂载的总线为AMBA
 dev->dev.dma_mask = &dev->dma_mask;
 dev->res.name = dev->dev.bus_id;

 if (!dev->dev.coherent_dma_mask && dev->dma_mask)
  dev_warn(&dev->dev, "coherent dma mask is unset/n");

 ret = request_resource(parent, &dev->res); //检查设备的IO资源是否已经被占用
 if (ret == 0) {  //下面的ioremap主要是将物理地址空间转换成虚拟地址
  tmp = ioremap(dev->res.start, SZ_4K);
  if (!tmp) {
   ret = -ENOMEM;
   goto out;
  }

  for (pid = 0, i = 0; i < 4; i++)  //读取设备的PeriphID
   pid |= (readl(tmp + 0xfe0 + 4 * i) & 255) << (i * 8);
  for (cid = 0, i = 0; i < 4; i++)//读取设备的PCellID
   cid |= (readl(tmp + 0xff0 + 4 * i) & 255) << (i * 8);

//注意,上面的偏址在ARM Periph IP内都是固定的,不相信的话可以查看ARM公司提供的相关外设的IP sepc。

  iounmap(tmp);

  if (cid == 0xb105f00d)
      dev->periphid = pid;
  if (dev->periphid)//如果设备的ID不为NULL,就注册设备,否则退出。

                                   //注意,如果不是标准的 ARM Periph IP,而你又想将此设备挂载到AMBA总线上的话,你必须为自己的设备提供一个PeriphID, 并在定义设备的时候初始化这个成员变量。
     ret = device_register(&dev->dev);  //将设备注册到系统中
  else
      ret = -ENODEV;
  if (ret == 0) {
     device_create_file(&dev->dev, &dev_attr_id);
   if (dev->irq[0] != NO_IRQ)
    device_create_file(&dev->dev, &dev_attr_irq0);
   if (dev->irq[1] != NO_IRQ)
    device_create_file(&dev->dev, &dev_attr_irq1);
   device_create_file(&dev->dev, &dev_attr_resource);
  } else {
 out:
   release_resource(&dev->res);
   printfs("error at amba-register/n");
  }
 }
  return ret;
}
    设备成功注册后,酒后添加到上面提到的总线设备集合中。在设备注册的过程中,系统要搜索总线上注册的驱动,看看有没有匹配的,如果设备还没有绑定驱动程序,则为其绑定一个。

具体的调用过程如下:

        device_register-->>>device_add-->>>bus_attach_device-->>>device_attach-->>>__device_attach

/**
 * device_attach - try to attach device to a driver.
 * @dev: device.
 *
 * Walk the list of drivers that the bus has and call
 * driver_probe_device() for each pair. If a compatible
 * pair is found, break out and return.
 *
 * Returns 1 if the device was bound to a driver;
 * 0 if no matching device was found; error code otherwise.
 *
 * When called for a USB interface, @dev->parent->sem must be held.
 */
int device_attach(struct device * dev)
{
 int ret = 0;

 down(&dev->sem);
 if (dev->driver) { //如果设备已经有驱动,绑定它
  ret = device_bind_driver(dev);
  if (ret == 0)
   ret = 1;
 } else //如果没有,遍历总线上的驱动链表,查找是否有匹配的驱动
  ret = bus_for_each_drv(dev->bus, NULL, dev, __device_attach);
 up(&dev->sem);
 return ret;
};

 

/**
 * driver_probe_device - attempt to bind device & driver together
 * @drv: driver to bind a device to
 * @dev: device to try to bind to the driver
 *
 * First, we call the bus's match function, if one present, which should
 * compare the device IDs the driver supports with the device IDs of the
 * device. Note we don't do this ourselves because we don't know the
 * format of the ID structures, nor what is to be considered a match and
 * what is not.
 *
 * This function returns 1 if a match is found, an error if one occurs
 * (that is not -ENODEV or -ENXIO), and 0 otherwise.
 *
 * This function must be called with @dev->sem held.  When called for a
 * USB interface, @dev->parent->sem must be held as well.
 */
int driver_probe_device(struct device_driver * drv, struct device * dev)
{
 struct stupid_thread_structure *data;
 struct task_struct *probe_task;
 int ret = 0;
 
 if (!device_is_registered(dev))
  return -ENODEV;
 if (drv->bus->match && !drv->bus->match(dev, drv))//比较设备的ID与驱动的ID是否相等
  goto done;
  
 pr_debug("%s: Matched Device %s with Driver %s/n",
   drv->bus->name, dev->bus_id, drv->name);

 data = kmalloc(sizeof(*data), GFP_KERNEL);
 if (!data)
  return -ENOMEM;
 data->drv = drv;
 data->dev = dev;
 printfs("track driver probe/n");
 if (drv->multithread_probe) {
  probe_task = kthread_run(really_probe, data,
      "probe-%s", dev->bus_id);
  if (IS_ERR(probe_task))
   ret = really_probe(data);
 } else
  ret = really_probe(data); //如果找到了匹配的驱动,调用驱动的probe方法初始化设备

done:
 //printfs("track driver probe: go to done/n");
 return ret;
}

转载 : http://blog.csdn.net/wind_beneath_wings/article/details/3349966



  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
软件介绍:   星际单警执法仪驱动安装与电脑连接说明  单警执法视音频记录仪与电脑的连接需要通过专业的管理软件来实现,支持操作系统包括Windows2000、WindowsXP,及Windows7 32位。  第一步:  将执法记录仪开机连接电脑后,然后将配套软件从随机附带光碟拷贝至电脑,在“我的电脑”中打开“设备管理器”, 选择“其它设备”中黄色问号文件(Amba Simple Class)。  第二步:确认选择黄色问号(Amba Simple Class)第三步:在列表中选择“更新驱动程序(P)”  第四步:选择“从列表或指定位置安装(高级)(S)”  第五步:在浏览中选择“驱动”文件夹安装后,点击“下一步”,开始安装,当显示完成驱动安装后,软件安装完毕。  第六步:登录,双击文件夹“高清执法记录仪”内部的 文件(或者发送到桌面快捷方式),在双击打开电脑会显示下面界面:  输入正确的六位密码后(初始密码“000000”),点击“连接设备”,再点击“提交密码” 如果密码正常,机器和电脑通讯正常,机器会自动进入以下界面,反之密码错误会提示密码错误或者连接错误。  自动校时,点击“自动校时” 会出现校时成功,此时需要确认电脑的时间是否与本地时间相同,同时机器的时间和电脑的时间会相同。点击确定。校时成功。  注:机器进入U盘模式后,其余按键不再起作用,不能完成通讯,要想设定机器相关参数,在没有进入U盘模式之前调整。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值