imx6ull 驱动:总线设备驱动模型

1 总线型驱动

原理

总线分两边

  1. driver
    1. 分配,设置,注册,填充 file_operations
    2. 根据 device 的硬件资源操作硬件
  2. device
    1. 指定硬件资源

在这里插入图片描述

总线上会有一个匹配函数,在注册 driver 时,会在 device 中对比两边结构体中的 name,如果一样,就采用 device 的硬件资源。

匹配规则

  1. 最先比较:platform_device. driver_overrideplatform_driver.driver.name 可以设置 platform_device 的 driver_override,强制选择某个 platform_driver。

  2. 然后比较:platform_device. nameplatform_driver.id_table[i].name

    Platform_driver.id_table 是“platform_device_id”指针,表示该 drv 支持若干个 device,它 里面列出了各个 device 的{.name, .driver_data},其中的 “name” 表示该 drv 支持的设备的名 字,driver_data 是些提供给该 device 的私有数据。

  3. 最后比较:platform_device.nameplatform_driver.driver.name
    platform_driver.id_table 可能为空, 这时可以根据 platform_driver.driver.name 来寻找同名的 platform_device。

函数调用关系

platform_device_register 
    platform_device_add 
    	device_add 
    	bus_add_device // 放入链表 
    	bus_probe_device // probe 枚举设备,即找到匹配的(dev, drv) 
    		device_initial_probe 
    			__device_attach 
    				bus_for_each_drv(...,__device_attach_driver,...)
    					__device_attach_driver 
    						driver_match_device(drv, dev) // 是否匹配 
    						driver_probe_device	// 调用 drv 的 probe

platform_driver_register 
    __platform_driver_register 
    	driver_register bus_add_driver // 放入链表 
    		driver_attach(drv) 
    			bus_for_each_dev(drv->bus, NULL, drv, __driver_attach); 
					__driver_attach 
                        driver_match_device(drv, dev) // 是否匹配 
                        driver_probe_device		// 调用 drv 的 probe

常用函数

这些函数可查看内核源码:drivers/base/platform.c,根据函数名即可知道其含义。 下面摘取常用的几个函数。

注册 / 反注册

platform_device_register/ platform_device_unregister

platform_driver_register/ platform_driver_unregister

platform_add_devices // 注册多个 device

获得资源

返回该 dev 中某类型(type)资源中的第几个(num):

在这里插入图片描述

返回该 dev 所用的第几个(num)中断:

在这里插入图片描述

通过名字(name)返回该 dev 的某类型(type)资源:

在这里插入图片描述

通过名字(name)返回该 dev 的中断号:
在这里插入图片描述

怎么写程序

  1. 分配/设置/注册 platform_device 结构体 在里面定义所用资源,指定设备名字。
  2. 分配/设置/注册 platform_driver 结构体 在其中的 probe 函数里,分配/设置/注册 file_operations 结构体, 并从 platform_device 中确实所用硬件资源。 指定 platform_driver 的名字。

实例

在 使用 imxull alpha

不知为啥 使用 IORESOURCE_IO 时,会提示:platform myled:failed to claim resource 0

led _dev.c

头文件
#include <linux/module.h>
#include <linux/version.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/interrupt.h>
#include <linux/list.h>
#include <linux/timer.h>
#include <linux/serial_core.h>
#include <linux/platform_device.h>

#include "led_resource.h"

struct resource
static struct resource led_resource[] = {
	[0] = {// clock enable
		.start = CCM_CCGR_CG(1,13),
		.end   = CCM_CCGR_CG(1,13),
		.flags = IORESOURCE_MEM,
	},
	[1] = {// gpio pin
		.start = GROUP_PIN(1,3),
		.end   = GROUP_PIN(1,3),
		.flags = IORESOURCE_MEM,
	},
	[2] = {// gpio pad
		.start = SW_PAD(0x10b0),
		.end   = SW_PAD(0x10b0),
		.flags = IORESOURCE_MEM,
	},
	[3] = {// gpio mux
		.start = SW_MUX(5),
		.end   = SW_MUX(5),
		.flags = IORESOURCE_MEM,
	},
	[4] = {// gpio num
		.start = 1,
		.end   = 1,
		.flags = IORESOURCE_MEM,
	},
};
struct platform_device
/* create a platform device */
static struct platform_device led_dev = {
	.name			= LED_NAME,
	.id 			= -1,
	.num_resources  = ARRAY_SIZE(led_resource),
	.resource  		= led_resource,
	.dev			= {
		.release 	= led_release,
	},
};
led_release
static void led_release(struct device *dev)
{
}
led_dev_init
static int led_dev_init(void)
{
	platform_device_register(&led_dev);
	return 0;
}
led_dev_exit
static void led_dev_exit(void)
{
	platform_device_unregister(&led_dev);
}
注册
module_init(led_dev_init);
module_exit(led_dev_exit);

MODULE_LICENSE("GPL");

led _drv.c

一些定义
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/uaccess.h>
#include <linux/irq.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/cdev.h>

#include "led_resource.h"

#define LEDON		1
#define LEDOFF		0

#define CCM_CCGRx_BASE(x) 			(0x020C4068 + (0x04 * x))
#define SW_MUX_GPIO1_IO0x_BASE(x) 	(0x020E005C + (0x04 * x))
#define SW_PAD_GPIO1_IO0x_BASE(x) 	(0x020E02E8 + (0x04 * x))
#define GPIOx_GDIR_BASE 			(0X0209C004)
#define GPIOx_GD_BASE 				(0X0209C000)


/* mapped register virtual address point */
static void __iomem *IMX6U_CCM_CCGR1;
static void __iomem *SW_MUX_GPIO1_IO03;
static void __iomem *SW_PAD_GPIO1_IO03;
static void __iomem *GPIO1_DR;
static void __iomem *GPIO1_GDIR;

static volatile unsigned int led_pin;
static volatile unsigned int led_clock;
static volatile unsigned int led_mux;
static volatile unsigned int led_pad;

static struct cdev led_cdev;
static struct class *led_class;
static struct device *led_dev;
static int major;
static int minor;
static dev_t devid;
static int led_num;

led_open
/*
 * @decription  :   open device
 * @param inode :   transfer inode to driver
 * @param filp  :   device file
 * @return      :   0 success, otherwise failed
 */
 static int led_open(struct inode *node, struct file * filp)
{
	u32 val;
	int err;
	/* initialization led */
    /* mapped register address */
    IMX6U_CCM_CCGR1 = ioremap(CCM_CCGRx_BASE(CCM_CCGR(led_clock)), 4);
    SW_MUX_GPIO1_IO03 = ioremap(SW_MUX_GPIO1_IO0x_BASE(GROUP(led_mux)),4);
    SW_PAD_GPIO1_IO03 = ioremap(SW_PAD_GPIO1_IO0x_BASE(GROUP(led_pad)),4);
    GPIO1_DR = ioremap(GPIOx_GD_BASE, 4);
    GPIO1_GDIR = ioremap(GPIOx_GDIR_BASE, 4);

	/* enable clock */
	val = readl(IMX6U_CCM_CCGR1);
	val |= (3<< (CCM_CG(led_clock))*2);
	writel(val,IMX6U_CCM_CCGR1);

	/* set gpio multipex */
	writel(SW_MUX(led_mux),SW_MUX_GPIO1_IO03);

	/* set gpio pad */
	writel(SW_PAD(led_pad),SW_PAD_GPIO1_IO03);

	/* set gpio to output model */
	val = readl(GPIO1_GDIR);
	val |= (1<<PIN(led_pin));
	writel(val,GPIO1_GDIR);

	/* default set led to dark */
	val = readl(GPIO1_DR);
	val |= (1<<PIN(led_pin));
	writel(val,GPIO1_DR);

	return 0;
}
led_write
/*
 * @decription  :   led open/close
 * @param  sta :   LEDON(0) turn on led, LEDOFF(1) turn off led
 * @return      :   none
 */
void led_switch(u8 sta)
{
    u32 val = 0;
    if(sta == LEDON)
    {
        val = readl(GPIO1_DR);
        val &= ~(1<<PIN(led_pin));
        writel(val,GPIO1_DR); 
    }else if(sta == LEDOFF)
    {
        val = readl(GPIO1_DR);
        val |= (1<<PIN(led_pin));
        writel(val,GPIO1_DR);
    }
	printk("write led!\n");
}


static ssize_t led_write(struct file *filp, const char __user *buf,size_t cnt,loff_t *off)
{
	int err;
    unsigned char status;
	
    err = copy_from_user(&status,buf,cnt);/* get current led statu value */

	if(err < 0)
    {
        printk("kernel write failed!\r\n");
        return -EFAULT;
    }
    
    led_switch(status);

    return 0;
	
}
led_release
static int led_release(struct inode *node, struct file *filp)
{
	printk("iounmap!\n");
	/* cancel map */
    iounmap(IMX6U_CCM_CCGR1);
    iounmap(SW_MUX_GPIO1_IO03);
    iounmap(SW_PAD_GPIO1_IO03);
    iounmap(GPIO1_DR);
    iounmap(GPIO1_GDIR);
	
	return 0;
}
struct file_operations
static struct file_operations myled_oprs = {
	.owner		= THIS_MODULE,
	.open		= led_open,
	.write		= led_write,
	.release	= led_release,
};
led_probe
static int led_probe(struct platform_device *pdev)
{
	struct resource *res;

	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	led_clock = res->start;
	
	res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
	led_pin = res->start;
	
	res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
	led_pad = res->start;
	
	res = platform_get_resource(pdev, IORESOURCE_MEM, 3);
	led_mux = res->start;

	res = platform_get_resource(pdev, IORESOURCE_MEM, 4);
	led_num = res->start;
/*
	if(major)
	{
		devid = MKDEV(major, 0);
		register_chrdev_region(devid, led_num, LED_NAME);
	}else
	{
		alloc_chrdev_region(&devid, 0, led_num, LED_NAME);
		major = MAJOR(devid);
		minor = MINOR(devid);
	}
*/
	alloc_chrdev_region(&devid, 0, led_num, LED_NAME);
	major = MAJOR(devid);
	minor = MINOR(devid);

	printk("myled : major = %d, minor = %d\n",major,minor);

	/* initialization cdev */
	led_cdev.owner = THIS_MODULE;
	cdev_init(&led_cdev, &myled_oprs);

	/* add a cdev */
	cdev_add(&led_cdev, devid, led_num);

	/* create class */
	led_class = class_create(THIS_MODULE, LED_NAME);
	if(IS_ERR(led_class)){
		printk("class create error!\n");
		return PTR_ERR(led_class);
	}

	/* create device */
	led_dev = device_create(led_class,NULL,devid,NULL,LED_NAME);
	if(IS_ERR(led_dev)){
		printk("device create error!\n");
		return PTR_ERR(led_dev);
	}

	return 0;
}
led_remove
static int led_remove(struct platform_device *pdev)
{
	/* log out cdev, device, class */
	cdev_del(&led_cdev);
	unregister_chrdev_region(devid,led_num);
	device_destroy(led_class,devid);
	class_destroy(led_class);
	
	return 0;
}
struct platform_driver
struct platform_driver led_drv = {
	.probe 	= led_probe,
	.remove = led_remove,
	.driver = {
		.name = LED_NAME,
	}
};
myled_exit
static void myled_exit(void)
{
	platform_driver_unregister(&led_drv);
}
myled_init
static int myled_init(void)
{
	platform_driver_register(&led_drv);
	return 0;
}
注册
module_init(myled_init);
module_exit(myled_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("LUO");

led_resource.h

#ifndef __LED_RESOURCE_H
#define __LED_RESOURCE_H

#define LED_NAME	"myled"


#define GROUP(x)        (x>>16)
#define PIN(x)          (x&0xffff)
#define GROUP_PIN(g,p)  ((g<<16) | p)

#define CCM_CCGR(x)             ((x>>16))
#define CCM_CG(x)               (x&0xffff)
#define CCM_CCGR_CG(ccgr,cg)    ((ccgr<<16) | cg)

#define LED_MAJOR(x)             ((x>>16))
#define LED_MINOR(x)             (x&0xffff)
#define MAJOR_MINOR(ma,mi)    ((ma<<16) | mi)

#define SW_MUX(x)          (x&0xf)

#define SW_PAD(x)           (x&0xffff)

#define LED_NUM(x)          (x&0xffff)
#endif

Makefile

KERNELDIR := /home/luo/linux/IMX6ULL/linux_alpha/linux-imx-rel_imx_4.1.15_2.1.0_ga_alpha
CURRENT_PATH := $(shell pwd)

obj-m += led_drv.o
obj-m += led_dev.o

build: kernel_modules

kernel_modules:
	$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules
	$(CROSS_COMPILE)gcc -o ledtest ledtest.c

clean:
	$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean
	rm -rf modules.order
	rm -f ledtest

ledtest.c

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>


int main(int argc,char *argv[])
{
    int fd;
    char status;

    if(argc != 3)
    {
        printf("Usage : %s <dev> <on | off>\n",argv[0]);
        return -1;
    }
    /* open file */
    fd = open(argv[1], O_RDWR);
    if(fd == -1)
    {
        printf("can't open file %s\n",argv[1]);
        return -1;
    }

    /* write file */
    if( strcmp(argv[2], "on") == 0)
    {
        status = 1;
        write(fd,&status, 1);
    }else
    {
        status = 0;
        write(fd,&status,1);
    }

    close(fd);

    return 0;
}
  • 2
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值