1 总线型驱动
原理
总线分两边
- driver
- 分配,设置,注册,填充 file_operations
- 根据 device 的硬件资源操作硬件
- device
- 指定硬件资源
总线上会有一个匹配函数,在注册 driver 时,会在 device 中对比两边结构体中的 name,如果一样,就采用 device 的硬件资源。
匹配规则
-
最先比较:
platform_device. driver_override
和platform_driver.driver.name
可以设置 platform_device 的 driver_override,强制选择某个 platform_driver。 -
然后比较:
platform_device. name
和platform_driver.id_table[i].name
Platform_driver.id_table
是“platform_device_id”指针,表示该 drv 支持若干个 device,它 里面列出了各个 device 的{.name, .driver_data}
,其中的 “name” 表示该 drv 支持的设备的名 字,driver_data 是些提供给该 device 的私有数据。 -
最后比较:
platform_device.name
和platform_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 的中断号:
怎么写程序
- 分配/设置/注册 platform_device 结构体 在里面定义所用资源,指定设备名字。
- 分配/设置/注册 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;
}