linux驱动开发之platform平台总线的编程(一)
总线分成三部分:
bus
driver
device
什么时候用平台总线
1, 只要有设备的地址和中断都可以用平台总线
2, 如果写的驱动需要在多个平台中升级使用
3, 平台总线只是一个功能代码:将操作方法和操作资源进行了分离。
注意:平台总线不属于子系统,只是一个功能。
图1 platform总线模型
(1)平台总线:struct bus_type总线对象
struct bus_type platform_bus_type = {
.name = "platform",
.dev_attrs = platform_dev_attrs,
.match = platform_match, //匹配方法
.uevent = platform_uevent,
.pm = &platform_dev_pm_ops,
};
(2)pdev
// 描述一个设备的信息
struct platform_device {
const char * name; // 名字,用于匹配
int id; // 表示不同寄存器组的编号, 一般可以填-1
struct device dev; //父类
u32 num_resources; //资源的个数
struct resource * resource; //资源的详细信息--描述中断和内存资源
};
struct resource {
resource_size_t start;
resource_size_t end;
const char *name;
unsigned long flags;
struct resource *parent, *sibling, *child;
};
注册:
int platform_device_register(struct platform_device *);
注销:
void platform_device_unregister(struct platform_device *);
(3)pdrv
struct platform_driver {
int (*probe)(struct platform_device *); //表示匹配之后的函数
int (*remove)(struct platform_device *); //解除匹配
struct device_driver driver; //父类
const struct platform_device_id *id_table; //可以匹配列表
};
注册和注销:
extern int platform_driver_register(struct platform_driver *);
extern void platform_driver_unregister(struct platform_driver *);
下面写设备程序plat_led_dev.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
//设备程序
#define LED_GPC0_CONF 0xE0200060
#define LED_GPC0_SIZE 8
//这里描述多个设备
struct resource led_resource[] = {
[0] = {
.start = LED_GPC0_CONF,
.end = LED_GPC0_CONF + LED_GPC0_SIZE -1,//寄存器从0开始计算,所以要减1
.flags = IORESOURCE_MEM, //表示内存资源
},
// 以下部分为演示部分
[1] = {
.start = 8888,
.end = 8888, //中断是没有连续性的
.flags = IORESOURCE_IRQ, //表示中断资源
},
[2] = {
.start = 0xE0200160,
.end = 0xE0200160 + 8 -1,
.flags = IORESOURCE_MEM,
},
};
struct platform_device led_pdev = {
.name = "s5pv210_led", //只要保证与pdrv一致就行
.id = -1,
.num_resources = ARRAY_SIZE(led_resource), //求数组个数
.resource = led_resource,
};
static int __init plat_led_dev_init(void)
{
printk("-------%s-----------------\n", __FUNCTION__);
//注册一个pdev
//int platform_device_register(struct platform_device *); //注册到总线上
return platform_device_register(&led_pdev); //正常情况返回0
}
static void __exit plat_led_dev_exit(void)
{
printk("-------%s-----------------\n", __FUNCTION__);
//void platform_device_unregister(struct platform_device *);//从总线上注销出来
platform_device_unregister(&led_pdev); //一般卸载类的没有返回值
}
module_init(plat_led_dev_init);
module_exit(plat_led_dev_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("752800373@qq.com");
接下来写驱动程序plat_led_drv.c
其中,platform_driver结构体中,
probe中要做事情:(下一个博客里面写)
//为用户提供接口
0, 实例化全局的设备对象– kzalloc
1, 申请主设备号—register_chrdev
2, 自动创建设备节点—class_create, device_create
3, 初始化硬件–ioremap
4,实现 file_operation
5,拿到pdev中的资源对硬件进行初始化
#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <asm/io.h>
//驱动程序
volatile unsigned long *gpc0_conf;
volatile unsigned long *gpc0_data;
//led_drv_probe中主要任务是拿到资源
//匹配成功第一个要执行的函数
int led_drv_probe(struct platform_device *pdev)//这里的pdev指针是总线bus传过来的
{
printk("-------%s-----------------\n", __FUNCTION__);
/* 编写驱动的套路(下次文件里面写) 0, 实例化全局的设备对象-- kzalloc 1, 申请主设备号---register_chrdev 2, 自动创建设备节点---class_create, device_create 3, 初始化硬件--ioremap 4,实现 file_operation */
// 肯定拿到pdev中的资源对硬件进行初始化
//获取到内存资源
//参数2--拿到资源的类型
//参数3--相同资源中第几个,这里是第0个,记住是同一类的累加数字
//platform_get_resource_byname(struct platform_device *, unsigned int, const char *)
struct resource *addr_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
//ioremap第二个参数等价于addr_res->end - addr_res->start + 1
gpc0_conf = ioremap(addr_res->start, resource_size(addr_res));
gpc0_data = gpc0_conf + 1;
//如果要对硬件进行初始化
*gpc0_conf &= ~(0xff<<12);
*gpc0_conf |= (0x11<<12);
*gpc0_data |= (0x3<<3); //亮灯
//如果要获取到中断资源(按键)
//参数3--相同资源中第几个,这里是第0个,记住是同一类的累加数字
//struct resource *irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
//printk("irqno = %d\n", irq_res->start);//打印出起始地址
int irqno = platform_get_irq(pdev, 0);
printk("irqno = %d\n", irqno);
return 0;
}
int led_drv_remove(struct platform_device *pdev)
{
printk("-------%s-----------------\n", __FUNCTION__);
return 0;
}
const struct platform_device_id led_id_table[] = {
{
"s5pv210_led", 0x5210},//一定要和pdev里面的name一致,这里优先匹配
{
"s3c2410_led", 0x4444},
{
"s3c6410_led", 0x4444},
};
#if 0
//用来参考的
struct platform_driver {
int (*probe)(struct platform_device *);
int (*remove)(struct platform_device *);
struct device_driver driver;
const struct platform_device_id *id_table;
};
#endif
struct platform_driver led_pdrv = {
.probe = led_drv_probe, //表示匹配之后的函数
.remove = led_drv_remove, //解除匹配
.driver = {
.name = "samsung_led_drv", //随便写,但是一定要有
//可以用于和pdev进行匹配的,优先级较id_table低
},
.id_table =led_id_table , //一定是用于和pdev进行匹配, 优先进行匹配
};
static int __init plat_led_drv_init(void)
{
printk("-------%s-----------------\n", __FUNCTION__);
//注册一个pdrv
//extern int platform_driver_register(struct platform_driver *);
return platform_driver_register(&led_pdrv);
}
static void __exit plat_led_drv_exit(void)
{
printk("-------%s-----------------\n", __FUNCTION__);
platform_driver_unregister(&led_pdrv);
}
module_init(plat_led_drv_init);
module_exit(plat_led_drv_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("752800373@qq.com");
Makefile
#指定内核源码的绝对路径
KERNEL_DIR = /home/ubuntu/s5pv210/kernel/linux-3.0.8
CUR_DIR = $(shell pwd)
MYMODULE = plat_led_dev
MYMODULE2 = plat_led_drv
#MYAPP = buttons_v5
all:
#make进入到内涵源码目录,将当前目录下的源程序作为内核模块一起编译
make -C $(KERNEL_DIR) M=$(CUR_DIR) modules
#arm-none-linux-gnueabi-gcc -o $(MYAPP) $(MYAPP).c
clean:
#将编译生成的文件删除
make -C $(KERNEL_DIR) M=$(CUR_DIR) clean
rm -rf (MYAPP)
install:
cp -raf $(MYAPP) *.ko /opt/rootfs/drv_module
#指定编译哪个源文件
obj-m = $(MYMODULE).o
obj-m += $(MYMODULE2).o
这里,在开发板转载程序,可以不分先后顺序。
insmod plat_led_dev.ko
insmod plat_led_drv.ko
并且可以看到开发板上的两个LED灯亮了。
可在/sys/bus/platform/中查看程序是否成功加载到总线
程序代码:led_plat_v1.rar(链接:https://pan.baidu.com/s/1i5XRNHr 密码:qbx2)
还没有评论,来说两句吧...