您的位置:首页 > 运维架构 > Linux

linux驱动开发之platform平台总线的编程(一)

2018-01-29 21:45 666 查看
总线分成三部分

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)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: