目的
这一节将点灯驱动程序通过总线设备驱动模型来实现,人为地进行拆分,这只是提供了一个参考的机制。
1. 介绍
上一节讲了输入子系统,在这个概念里讲到了分层分离思想。“一切都可以利用分层来实现”。
对于输入子系统,有一个核心层,下面两个层,一个是纯软件部分,内核已经做好了;还有一个是代表设备层,需要用户自己添加驱动代码。这是和硬件相关的部分,需要改变。这样分层的好处就是,对于软件层来说,不需要改变;而硬件层,需要提供不同的操作方法,用户可以根据自己使用硬件的不同而改变驱动程序,仅此而已。
输入子系统中:将核心层与设备相关层分层,每层专注自己的功能。
将硬件层和纯软件层分离出来,把稳定的代码保留,用户只需修改与硬件相关的部分。
分离分层思想
(1)分层:核心层和设备相关层分开
这种思想的优点就是能把很多文件共用的代码抽离集中起来成为一个或者多个核心文件供设备相关层调用,每一层专注于自己的功能。
(2)分离:
把硬件相关的代码(对于某个CPU来说是固定的,如板子的网卡、中断地址)和驱动(会根据程序作变动,如点哪一个灯)分离开来,即要编写两个文件:dev.c和drv.c。
2. 代码分析
bus_drv_dev模型: 参考gpio_keys.c文件。
(0)几个重要的数据结构
bus_type:总线的类型
-
struct bus_type {
-
const char * name;
-
struct module * owner;
-
-
struct kset subsys;
-
-
// 含有driver 和 device 链表
-
struct kset drivers; // drv链表
-
struct kset devices; // dev链表
-
struct klist klist_devices;
-
struct klist klist_drivers;
-
-
struct blocking_notifier_head bus_notifier;
-
-
struct bus_attribute * bus_attrs;
-
struct device_attribute * dev_attrs;
-
struct driver_attribute * drv_attrs;
-
struct bus_attribute drivers_autoprobe_attr;
-
struct bus_attribute drivers_probe_attr;
-
-
int (*match)(struct device * dev, struct device_driver * drv); // 用来比较drv和dev的ID是否一致,匹配。
-
int (*uevent)(struct device *dev, char **envp,
-
int num_envp, char *buffer, int buffer_size);
-
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 (*suspend_late)(struct device * dev, pm_message_t state);
-
int (*resume_early)(struct device * dev);
-
int (*resume)(struct device * dev);
-
-
unsigned int drivers_autoprobe:1;
-
};
struct device
-
struct device {
-
struct klist klist_children;
-
struct klist_node knode_parent; /* node in sibling list */
-
struct klist_node knode_driver;
-
struct klist_node knode_bus;
-
struct device *parent;
-
-
struct kobject kobj;
-
char bus_id[BUS_ID_SIZE]; /* position on parent bus */
-
struct device_type *type;
-
unsigned is_registered:1;
-
unsigned uevent_suppress:1;
-
struct device_attribute uevent_attr;
-
struct device_attribute *devt_attr;
-
-
struct semaphore sem; /* semaphore to synchronize calls to
-
* its driver.
-
*/
-
-
struct bus_type * bus; /* type of bus device is on, 定义在那一根bus上面 */
-
struct device_driver *driver; /* which driver has allocated this device, 和该dev对应的driver */
-
-
void *driver_data; /* data private to the driver */
-
void *platform_data; /* Platform specific data, device
-
core doesn't touch it */
-
struct dev_pm_info power;
-
-
#ifdef CONFIG_NUMA
-
int numa_node; /* NUMA node this device is close to */
-
#endif
-
u64 *dma_mask; /* dma mask (if dma'able device) */
-
u64 coherent_dma_mask;/* Like dma_mask, but for
-
alloc_coherent mappings as
-
not all hardware supports
-
64 bit addresses for consistent
-
allocations such descriptors. */
-
-
struct list_head dma_pools; /* dma pools (if dma'ble) */
-
-
struct dma_coherent_mem *dma_mem; /* internal for coherent mem
-
override */
-
/* arch specific additions */
-
struct dev_archdata archdata;
-
-
spinlock_t devres_lock;
-
struct list_head devres_head;
-
-
/* class_device migration path */
-
struct list_head node;
-
struct class *class;
-
dev_t devt; /* dev_t, creates the sysfs "dev" */
-
struct attribute_group **groups; /* optional groups */
-
-
void (*release)(struct device * dev); // 需要显示提供
-
};
struct device_driver
-
struct device_driver {
-
const char * name;
-
struct bus_type * bus; // dev在哪一个bus上面
-
-
struct kobject kobj;
-
struct klist klist_devices;
-
struct klist_node knode_bus;
-
-
struct module * owner;
-
const char * mod_name; /* used for built-in modules */
-
struct module_kobject * mkobj;
-
-
int (*probe) (struct device * dev); // 如果dev和drv匹配,则调用该probe函数。
-
int (*remove) (struct device * dev);
-
void (*shutdown) (struct device * dev);
-
int (*suspend) (struct device * dev, pm_message_t state);
-
int (*resume) (struct device * dev);
-
};
(1) 平台驱动注册
platform_driver_register(struct platform_driver *drv)
driver_register(struct device_driver * drv)
bus_add_driver(struct device_driver *drv)
driver_attach(struct device_driver * drv)
bus_for_each_dev(struct bus_type * bus, struct device * start, void * data, int (*fn)(struct device *, void *)) // 从dev链表取出每个dev,一一和drv进行比较。
__driver_attach(struct device * dev, void * data)
driver_probe_device(struct device_driver * drv, struct device * dev)
if (drv->bus->match && !drv->bus->match(dev, drv)) // 这里调用bus总线上的match函数来判断drv的ID 和 dev的ID 是否一致,如果一致,则match。
// 对应platform_bus总线的match函数已经定义好了。
-
struct bus_type platform_bus_type = {
-
.name = "platform",
-
.dev_attrs = platform_dev_attrs,
-
.match = platform_match,
-
.uevent = platform_uevent,
-
.suspend = platform_suspend,
-
.suspend_late = platform_suspend_late,
-
.resume_early = platform_resume_early,
-
.resume = platform_resume,
-
};
|
(2)平台设备注册
platform_device_register(struct platform_device * pdev)
platform_device_add(struct platform_device *pdev)
device_add(struct device *dev)
bus_attach_device(struct device * dev)
device_attach(struct device * dev)
bus_for_each_drv(struct bus_type * bus, struct device_driver * start, void * data, int (*fn)(struct device_driver *, void *)) // 这里从drv链表,取出drv一一和dev比较
__device_attach(struct device_driver * drv, void * data)
driver_probe_device(struct device_driver * drv, struct device * dev)
if (drv->bus->match && !drv->bus->match(dev, drv)) //调用bus上定义的match函数进行比较,如果drv和dev的ID相同的话,则匹配
|
(3)总结
3. 驱动代码
dev这边放置的需要经常改变的代码(根据用户使用的外设不同而不同); drv这边放置的是稳定的代码。
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/init.h>
-
#include <linux/serial_core.h>
-
#include <linux/platform_device.h>
-
-
-
/* 分配/设置/注册一个platform_device */
-
-
static struct resource led_resource[] = {
-
[0] = {
-
.start = 0x56000050, // 起始 地址GPFCON
-
.end = 0x56000050 + 8 - 1, // 结束地址,定义了两个32位的寄存器。 GPFCON GPFDAT
-
.flags = IORESOURCE_MEM, // 资源类型: 内存类
-
},
-
[1] = {
-
.start = 5, // pin 脚值
-
.end = 5, // pin 脚值
-
.flags = IORESOURCE_IRQ, // 中断资源
-
}
-
-
};
-
-
static void led_release(struct device * dev) // 显示提供,可以不做任何事
-
{
-
}
-
-
-
-
static struct platform_device led_dev = {
-
.name = "myled",
-
.id = -1,
-
.num_resources = ARRAY_SIZE(led_resource),
-
.resource = led_resource,
-
.dev = {
-
.release = led_release, // 必须显示提供,不然卸载会报错
-
},
-
};
-
-
static int led_dev_init(void)
-
{
-
platform_device_register(&led_dev); // 注册平台设备
-
return 0;
-
}
-
-
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
-
/* 分配/设置/注册一个platform_driver */
-
-
#include <linux/module.h>
-
#include <linux/version.h>
-
-
#include <linux/init.h>
-
#include <linux/fs.h>
-
#include <linux/interrupt.h>
-
#include <linux/irq.h>
-
#include <linux/sched.h>
-
#include <linux/pm.h>
-
#include <linux/sysctl.h>
-
#include <linux/proc_fs.h>
-
#include <linux/delay.h>
-
#include <linux/platform_device.h>
-
#include <linux/input.h>
-
#include <linux/irq.h>
-
#include <asm/uaccess.h>
-
#include <asm/io.h>
-
-
static int major;
-
-
-
static struct class *cls;
-
static volatile unsigned long *gpio_con;
-
static volatile unsigned long *gpio_dat;
-
static int pin;
-
-
static int led_open(struct inode *inode, struct file *file)
-
{
-
//printk("first_drv_open\n");
-
/* 配置为输出 */
-
*gpio_con &= ~(0x3<<(pin*2));
-
*gpio_con |= (0x1<<(pin*2));
-
return 0;
-
}
-
-
static ssize_t led_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos)
-
{
-
int val;
-
-
//printk("first_drv_write\n");
-
-
copy_from_user(&val, buf, count); // copy_to_user();
-
-
if (val == 1)
-
{
-
// 点灯
-
*gpio_dat &= ~(1<<pin);
-
}
-
else
-
{
-
// 灭灯
-
*gpio_dat |= (1<<pin);
-
}
-
-
return 0;
-
}
-
-
-
static struct file_operations led_fops = {
-
.owner = THIS_MODULE, /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */
-
.open = led_open,
-
.write = led_write,
-
};
-
-
static int led_probe(struct platform_device *pdev) // probe函数里面你可以做你想做的事情,这里只是提供了这样一种机制。
-
{
-
struct resource *res;
-
-
/* 根据platform_device的资源进行ioremap */
-
res = platform_get_resource(pdev, IORESOURCE_MEM, 0); // 最后一个参数0:表示从资源的最开始地址获取。
-
gpio_con = ioremap(res->start, res->end - res->start + 1);
-
gpio_dat = gpio_con + 1;
-
-
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
-
pin = res->start; //获取pin脚值
-
-
-
/* 注册字符设备驱动程序 */
-
printk("led_probe, found led\n");
-
-
major = register_chrdev(0, "myled", &led_fops);
-
cls = class_create(THIS_MODULE, "myled");
-
class_device_create(cls, NULL, MKDEV(major, 0), NULL, "led"); /* /dev/led */
-
-
return 0;
-
}
-
-
static int led_remove(struct platform_device *pdev)
-
{
-
/* 卸载字符设备驱动程序 */
-
/* iounmap */
-
printk("led_remove, remove led\n");
-
-
class_device_destroy(cls, MKDEV(major, 0));
-
class_destroy(cls);
-
unregister_chrdev(major, "myled");
-
iounmap(gpio_con);
-
-
return 0;
-
}
-
-
-
struct platform_driver led_drv = {
-
.probe = led_probe,
-
.remove = led_remove,
-
.driver = {
-
.name = "myled",
-
}
-
};
-
-
-
static int led_drv_init(void)
-
{
-
platform_driver_register(&led_drv);
-
return 0;
-
}
-
-
static void led_drv_exit(void)
-
{
-
platform_driver_unregister(&led_drv);
-
}
-
-
module_init(led_drv_init);
-
module_exit(led_drv_exit);
-
-
MODULE_LICENSE("GPL");
4. 测试代码
-
#include <sys/types.h>
-
#include <sys/stat.h>
-
#include <fcntl.h>
-
#include <stdio.h>
-
-
/* led_test on
-
* led_test off
-
*/
-
int main(int argc, char **argv)
-
{
-
int fd;
-
int val = 1;
-
fd = open("/dev/led", O_RDWR);
-
if (fd < 0)
-
{
-
printf("can't open!\n");
-
}
-
if (argc != 2)
-
{
-
printf("Usage :\n");
-
printf("%s \n", argv[0]);
-
return 0;
-
}
-
-
if (strcmp(argv[1], "on") == 0)
-
{
-
val = 1;
-
}
-
else
-
{
-
val = 0;
-
}
-
-
write(fd, &val, 4);
-
return 0;
-
}
5. 测试
小结
阅读(560) | 评论(0) | 转发(0) |