1、什么是平台总线模型?
平台总线模型也叫 platform 总线模型。 是 Linux 内核虚拟出来的一条总线,不是真实的导线。
平台总线模型就是把原来的驱动 C 文件给分成了俩个 C 文件,一个是 device.c , 一个是 driver.c 把稳定不变的放在 driver.c 里面,需要变得就放在了 device.c 里面。把共性的放在dirver.c,变的存放在device.c中。
2、为什么会有平台总线模型?
( 1 ) 可以提高代码的重用性
( 2 ) 减少重复性代码。
设备 总线 驱动
device.c driver.c
3、 平台总线模型的优点。
(1)驱动和设备代码是区分的,重用性强,可以减少重复性!
(2)挂载到了platform总线上。
4、怎么编写以平台总线模型设计的驱动?
一个是 device.c,一个是 driver.c。然后分别注册 device.c 和 driver.c 。
平台总线是以名字来匹配的,实际上就是字符串比较。
struct platform_device {
const char *name;
平台总线进行匹配的时候用到的 name,/sys/bus......
int id ;
设备id,一般写-1
struct device dev;
内嵌的 device 结构体
u32 num_resources;
资源的个数
struct resource *resource;
device里面的硬件资源(寄存器地址和中断时钟等)。 };
struct resource {
resource_size_t start;
资源的起始
resource_size_t end;
资源的结束
const char *name;
资源的名字
unsigned long flags;
资源的类型 };
#define IORESOURCE_IO IO 的内存
#define IORESOURCE_MEM 表述一段物理内存
#define IORESOURCE_IRQ 表示中断
device部分驱动代码:
#include<linux/init.h>
#include<linux/module.h>
#include <linux/platform_device.h>
void beep_release(struct device *dev)
{
printk("beep_release\n");
}
struct resource beep_res[] =
{
[0] = {
.start = 0x020AC000,//开始 蜂鸣器地址
.end = 0x020AC003,//结束
.flags = IORESOURCE_MEM,//描述的是一段物理地址
.name = "GPIO5_DR"
}
};
struct platform_device beep_device = {
.name = "beep_test",//匹配名称
.id = -1,
.resource = beep_res,
.num_resources = ARRAY_SIZE(beep_res),
.dev = {
.release = beep_release
}
};
static int device_init(void)
{
printk("hello world");
//加载到内核
return platform_device_register(&beep_device);
}
static void device_exit(void)
{
printk("bye bye");
platform_device_unregister(&beep_device);
}
module_init(device_init);
module_exit(device_exit);
MODULE_LICENSE("GPL");
编写driver.c
首先定义一个platform_driver的结构体变量,然后实现结构体中的各个成员变量,那么当driver和device匹配成功的时候,会执行probe函数,所以匹配成功之后的重点就在于probe函数的编写。
struct platform_driver {
int (*probe)(struct platform_device *);//函数指针
匹配函数:当driver和device匹配成功之后,执行probe函数
int (*remove)(struct platform_device *);
当driver和device任意一个remove的时候,执行这个函数
void (*shutdown)(struct platform_device *);
当设备收到shutdown,会执行这个函数
int (*suspend)(struct platform_device *, pm_message_t state);
当设备收到suspend,会执行这个函数休眠
int (*resume)(struct platform_device *);
当设备收到resume,会执行这个函数唤醒
struct device_driver driver;
const struct platform_device_id *id_table;
//优先匹配id_table 的.name成员
bool prevent_deferred_probe;
};
struct device_driver {
const char *name;
//这个是我们匹配设备时候要用到的名字
struct bus_type *bus;
struct module *owner;
const char *mod_name; /* used for built-in modules */
bool suppress_bind_attrs; /* disables bind/unbind via sysfs */
const struct of_device_id *of_match_table;
const struct acpi_device_id *acpi_match_table;
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 (*resume) (struct device *dev);
const struct attribute_group **groups;
const struct dev_pm_ops *pm;
struct driver_private *p;
};
设备驱动代码编写:
#include<linux/init.h>
#include<linux/module.h>
#include <linux/platform_device.h>
int beep_probe(struct platform_device *pdev){
printk("beep_probe");
return 0;
}
int beep_remove(struct platform_device *pdev){
printk("beep_remove");
return 0;
}
//id_table 的优先级比较高
const struct platform_device_id beepid_table =
{
.name = "123"
};
struct platform_driver beep_driver = {
.probe = beep_probe,
.remove = beep_remove,
.driver = {
.owner = THIS_MODULE,
.name = "beep_test"
},
.id_table = &beepid_table
};
static int beep_driver_init(void)
{
int ret =0;
ret = platform_driver_register(&beep_driver);
if (ret<0)
{
printk("platform_driver_register is error");
return ret;
}
printk("platform_driver_register is error");
return 0;
}
static void beep_driver_exit(void)
{
platform_driver_unregister(&beep_driver);
printk("bye bye");
}
module_init(beep_driver_init);
module_exit(beep_driver_exit);
MODULE_LICENSE("GPL");
probe 函数编写思路:
(1)从 device.c 里面获取资源
方式一、直接获得,不推荐。
printk("beep_res is %s\n",pdev->resource[0].name);//直接获取,打印成员变量
方法二、使用函数获得
struct resource *platform_get_resource_byname(struct platform_device *,unsigned int,const char *);
(2)注册杂项 / 字符设备,完善file_operation 结构体,并生成设备节点。
注册之前要先登记。
#define request_mem_region(start,n,name) __request_region(&iomem_resource, (start), (n ), (name), 0)
登记使用
int beep_probe(struct platform_device *pdev){
printk("beep_probe");
//方法1、直接获取,打印成员变量
printk("beep_res is %s\n",pdev->resource[0].name);
//方法2 结构体成员 资源类型,同类中第几个
beep_mem = platform_get_resource(pdev,IORESOURCE_MEM,0);
if (beep_mem == NULL)
{
printk("platform_get_resource is error\n");
return -EBUSY;
}
printk("beep_res start is 0x%x\n",beep_mem->start);
printk("beep_res end is 0x%x\n",beep_mem->end);
//登记使用
beep_mem_temp = requset_mem_region(beep_mem->start,beep_mem->end-beep_mem->start+1,"beep");
if (beep_mem_temp == NULL)
{
printk("requset_mem_region is error\n");
goto err_region;
}
return 0;
err_region:
release_mem_region(beep_mem->start,beep_mem->end-beep_mem->start+1);
}
与原先的区别就是在probe函数中使用杂项设备和字符设备,将device和driver分开了。先前是在misc_init中注册杂项设备。
#include<linux/init.h>
#include<linux/module.h>
#include <linux/platform_device.h>
#include <linux/ioport.h>
#include <linux/miscdevice.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#define GPIO5_DR 0x020AC000
unsigned int *vir_gpio_dr;//存放映射完成的虚拟地址的首地址
int misc_open(struct inode *inode,struct file *file)
{
printk("hello misc_open\n");
return 0;
}
int misc_close(struct inode *inode,struct file *file)
{
printk("bye bye\n");
return 0;
}
int misc_read(struct file *file,char __user *ubuf,size_t size,loff_t *loff_t)
{
char kbuf[64] = "hello";
if(copy_to_user(ubuf,kbuf,sizeof(kbuf))!=0)
{
printk("copy to user error\n");
return -1;
}
return 0;
}
int misc_write(struct file *file,const char __user *ubuf,size_t size,loff_t *loff_t)
{
char kbuf[64] = {0};
if(copy_from_user(kbuf,ubuf,size)!=0) //ubuf 传送进来 保存到 kbuf中
{
printk("copy_from_user\n");
return -1;
}
printk("kbuf is %s\n",kbuf);
if (kbuf[0] == 1)
*vir_gpio_dr |= (1 <<1);//open
else if (kbuf[0] == 0)
*vir_gpio_dr &= ~(1 <<1);//close
return 0;
}
struct file_operations misc_fops ={
.owner = THIS_MODULE,
.open = misc_open,
.release = misc_close,
.read = misc_read,
.write = misc_write
};
struct miscdevice misc_dev = {
.minor = MISC_DYNAMIC_MINOR,
.name = "hello_misc",
.fops = &misc_fops
};
struct resource *beep_mem;
struct resource *beep_mem_temp;
int beep_probe(struct platform_device *pdev){
int ret = 0;
printk("beep_probe");
//方法1、直接获取,打印成员变量
printk("beep_res is %s\n",pdev->resource[0].name);
//方法2 结构体成员 资源类型,同类中第几个
beep_mem = platform_get_resource(pdev,IORESOURCE_MEM,0);
if (beep_mem == NULL)
{
printk("platform_get_resource is error\n");
return -EBUSY;
}
printk("beep_res start is 0x%x\n",beep_mem->start);
printk("beep_res end is 0x%x\n",beep_mem->end);
#if 0
//登记使用
beep_mem_temp = requset_mem_region(beep_mem->start,beep_mem->end-beep_mem->start+1,"beep");
if (beep_mem_temp == NULL)
{
printk("requset_mem_region is error\n");
goto err_region;
}
#endif
/************************************************************/
vir_gpio_dr = ioremap(beep_mem->start,4);
if(vir_gpio_dr == NULL)
{
printk("GPIO5_DR ioremap error\n");
return -EBUSY;
}
printk("GPIO5_DR ioremap ok\n");
/************************************************************/
ret = misc_register(&misc_dev);//注册
if (ret<0)
{
printk("misc_register is error\n");
return -1;
}
printk("misc_register is successful\n");
return 0;
// err_region:
// release_mem_region(beep_mem->start,beep_mem->end-beep_mem->start+1);
}
int beep_remove(struct platform_device *pdev){
printk("beep_remove");
return 0;
}
//id_table 的优先级比较高
const struct platform_device_id beepid_table =
{
.name = "123"
};
struct platform_driver beep_driver = {
.probe = beep_probe,
.remove = beep_remove,
.driver = {
.owner = THIS_MODULE,
.name = "beep_test"
},
.id_table = &beepid_table
};
static int beep_driver_init(void)
{
int ret =0;
ret = platform_driver_register(&beep_driver);
if (ret<0)
{
printk("platform_driver_register is error");
return ret;
}
printk("platform_driver_register is error");
return 0;
}
static void beep_driver_exit(void)
{
platform_driver_unregister(&beep_driver);
misc_deregister(&misc_dev);//卸载
iounmap(vir_gpio_dr);
printk("bye bye");
}
module_init(beep_driver_init);
module_exit(beep_driver_exit);
MODULE_LICENSE("GPL");
首先挂载