【1:引言: linux字符设备驱动的基本编程流程】
1.实现模块加载函数
a.申请主设备号
register_chrdev(major,name,file_operations);
b.创建字符设备cdev,注册字符设备
cdev_alloc cdev_init cdev_add
c.创建设备文件
class_create device_create
d.注册中断
ret =request_irq(中断号,...,...,...,...);
e.映射
虚拟地址=ioremap(物理地址,大小)
f.初始化(初始化等待队列头,初始化tasklet,初始化工作队列)
...
2.实现模块卸载函数
3.构建file_operations结构体变量
4.实现操作硬件的方法
xxx_open xxx_write xxx_read
为了提高驱动的可移植性,减少驱动开发周期,最好将跟硬件/平台相关的东西分离出来,以便增强驱动的可移植性
中断号,物理地址----->归为设备资源
最好将设备资源与设备驱动分离开来
平台设备驱动机制
platform_device platform_driver
设备资源(设备)
<
--
--
--
> 设备驱动
【2:设备总线驱动模型:内核用来管理设备与驱动的一种方式】
设备总线驱动模型:以对象的思想来实现的
每一个设备对应唯一的一个驱动
每个驱动则可能服务多个设备
系统中有很多总线:1)实际的物理总线(如:i2c总线,usb总线,SDIO总线,SPI总线...)
2)虚拟总线(只有一条:平台总线)
【对象思想】
/*1.在系统用来表示一个设备*/
struct
device {
struct device_driver
*driver;
//设备驱动
struct bus_type
*bus;
//所属总线
struct device
*parent;
//父设备
dev_t devt;
//设备号
void
*platform_data;
//私有数据
....
}
/*2.在系统中用来表示设备驱动*/
struct
device_driver {
const
char
*name;
//驱动名字
struct bus_type
*bus;
//所属总线
struct module
*owner;
//拥有者
int (
*probe) (
struct device
*dev);
//probe函数
}
/*3.在系统中用来表示总线*/
struct
bus_type {
const
char
*name;
//总线名字
/*mach函数,匹配函数,每条总线里面都会有自己的
*mach,但不同总线的mach的匹配方法可能会不同
*/
int (
*match)(
struct device
*dev,
struct device_driver
*drv);
/*当设备与驱动匹配成功的时候,便会调用probe函数
*不过,一般总线中不会实现probe函数,在匹配成功
*的时候,会直接调用设备驱动中的probe函数
*/
int (
*probe)(
struct device
*dev);
int (
*remove)(
struct device
*dev);
}
【设备总线驱动模型里面的操作函数】
/*1.总线注册*/
int bus_register(
struct bus_type
*bus)
void bus_unregister(
struct bus_type
*bus)
/*2.设备注册*/
int device_register(
struct device
*dev)
void device_unregister(
struct device
*dev)
/*3.设备驱动注册*/
int driver_register(
struct device_driver
*drv)
void driver_unregister(
struct device_driver
*drv)
【3:平台设备驱动机制】
借助于设备总线驱动模型,虚拟出一条总线,用来实现设备资源与设备驱动的匹配
===============================================
平台设备驱动机制采用了:分离的思想,对象的思想
分离的思想:设备资源与设备驱动分离开
【对象思想】
/*1.平台设备结构体*/
struct
platform_device {
const
char
* name;
//名字
int id;
struct device dev;
//设备结构体
u32 num_resources;
//资源数量
struct resource
* resource;
//设备资源
const
struct platform_device_id
*id_entry;
/* arch specific additions */
struct pdev_archdata archdata;
};
/*2.平台驱动结构体*/
struct
platform_driver {
int (
*probe)(
struct platform_device
*);
//probe函数
int (
*remove)(
struct platform_device
*);
void (
*shutdown)(
struct platform_device
*);
int (
*suspend)(
struct platform_device
*, pm_message_t state);
int (
*resume)(
struct platform_device
*);
struct device_driver driver;
//设备驱动
const
struct platform_device_id
*id_table;
};
/*3.虚拟总线:平台总线结构体*/
struct bus_type
platform_bus_type
= {
.name
=
"platform",
//总线名字
.dev_attrs
= platform_dev_attrs,
.match
= platform_match,
//匹配函数
.uevent
= platform_uevent,
.pm
=
&platform_dev_pm_ops,
};
/*4.资源结构体*/
struct
resource {
resource_size_t start;
//起始
resource_size_t end;
//结束
const
char
*name;
//名字
unsigned
long flags;
//标号
struct resource
*parent,
*sibling,
*child;
};
//平台设备驱动机制中如何来实现设备与驱动的匹配
static
int
platform_match(
struct device
*dev,
struct device_driver
*drv)
{
struct platform_device
*pdev
= to_platform_device(dev);
struct platform_driver
*pdrv
= to_platform_driver(drv);
/* match against the id table first :可能支持多个设备*/
if (pdrv
-
>id_table)
return platform_match_id(pdrv
-
>id_table, pdev)
!= NULL;
/*平台设备里面的名字,与设备驱动里面的名字匹配*/
return (strcmp(pdev
-
>name, drv
-
>name)
==
0);
}
【总线注册】
/*内核启动时的第一个C语言入口函数*/
start_kernel
//init/main.c
rest_init
/*创建一个内核线程*/
kernel_thread(kernel_init, NULL, CLONE_FS
| CLONE_SIGHAND);
kernel_init
do_basic_setup
//init/main.c
driver_init
platform_bus_init
/*1.注册平台总线*/
bus_register(
&platform_bus_type);
【平台设备驱动模型的关键接口函数】
/*1.注册平台设备*/
int
platform_device_register(
struct platform_device
*pdev)
void platform_device_unregister(
struct platform_device
*pdev)
/*2.注册平台驱动*/
int
platform_driver_register(
struct platform_driver
*drv)
void platform_driver_unregister(
struct platform_driver
*drv)
/*3.获取平台资源*/
platform_get_resource(
struct platform_device
* dev,
unsigned
int type,
unsigned
int num)
【linux设备驱动之设备总线驱动模型】
【设备总线驱动模型】
@成鹏致远([email protected])