43 linux的驱动模型,实现设备驱动代码的可移植性

时间:2022-10-28 06:36:09

struct bus_type 的对象来表示一个总线, 管理挂载到总线上的设备与设备驱动, 包括它们的配对
struct device 的对象来表示一个挂在总线上的设备, 描述硬件部分的资源(包含IO口, 中断号,键码等)
struct device_driver 对象来表示一个挂在总线上的设备驱动, 描述硬件的驱动方法

注意: bus总线在linux内核里仅仅是用一个结构体对象来表示而已, 与硬件接口并没有直接的关系. 总线用于装载同一类设备的device对象和device_driver对象, 并管理它们之间的匹配关系.
////////////
bus总线, 每个struct bus_type对象表示一个总线

struct bus_type {
const char *name; //总线名
...

int (*match)(struct device *dev, struct device_driver *drv); //此函数用于匹配设备与设备驱动
//函数返回值非0表示匹配上, 0表示匹配不上.
//总线的match函数在有新的设备驱动加入同一总线,并且有尚没有匹配对象的设备才会触发.
// 一个设备对象只能与一个设备驱动匹配。 一个设备驱动可以匹配多个设备

// 总线通常不会实现这些函数的, 这些函数功能由设备驱动来实现.
// 总线如实现下面函数的功能,则设备驱动同名的功能函数则没有用了.
int (*uevent)(struct device *dev, struct kobj_uevent_env *env);
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);
...
};

int bus_register(struct bus_type *bus); //注册总线对象
void bus_unregister(struct bus_type *bus); //反注册总线对象

如果总线与设备驱动, 设备不是同一个文件里实现的话,还需要导出总线对象,以便其它源文件里使用.

EXPORT_SYMBOL(mybus);

//////////////////////////////
设备, 每个struct device对象表示一个具体的硬件

    struct device {
struct device_private *p; //里面的成员driver_data指针可用于存放设备驱动用的数据。
// dev_set_drvdata(dev, data) //把设备驱动所用数据挂到dev上
// dev_get_drvdata(dev); //获取数据
struct kobject kobj; //设备注册后,存放设备的名字(init_name), 可使用dev_name(dev)来获取名字
const char *init_name; //设备在总线上的名字, 在总线上是唯一的.设备注册后, init_name会被改为NULL
struct bus_type *bus;//属于哪个总线对象,指向总线的地址
struct device_driver *driver;//如果总线匹配成功后, 此指针指向device_driver对象的地址

void *platform_data; //可用于装载提供给设备驱动的参数,如io口,中断号等

void (*release)(struct device *dev);//设备释放函数,设备对象反注册时触发调用. 不实现会有警告.
};

device_register(&mydev);
device_unregister(&mydev);

//////////////////////
设备驱动,每个struct device_driver对象表示一个驱动的方法:

struct device_driver {
const char *name; //设备驱动在总线上的名字
struct bus_type *bus;//属于哪个总线, 指向总线对象的地址

struct module *owner;
...
const struct of_device_id *of_match_table; ////用于指定此驱动具体可匹配哪些硬件设备, 由总线的match函数里使用

int (*probe) (struct device *dev); //当总线匹配上后(也就总线的match函数返回值为非0), 设备驱动的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); //设备驱动恢复工作
...
};

driver_register(&mydrv);
driver_unregister(&mydrv);

///////////////

匹配过程:
总线的match函数 –>> 设备驱动的probe函数

测试代码:
mybus.c


#include <linux/init.h>
#include <linux/module.h>
#include <linux/device.h>

int mymatch(struct device *dev, struct device_driver *drv)
{
printk("in mymatch ...%s, %s\n", dev_name(dev), drv->name);

return !strncmp(dev_name(dev), drv->name, strlen(drv->name)); //按设备驱动的名字进行匹配, 返回值非0表示匹配上, 0表示匹配不上
}

struct bus_type mybus = {
.name = "mybus",
.match = mymatch,
};

EXPORT_SYMBOL(mybus);

static int __init test_init(void)
{
return bus_register(&mybus);
}

static void __exit test_exit(void)
{
bus_unregister(&mybus);
}

module_init(test_init);
module_exit(test_exit);

MODULE_LICENSE("GPL");

mydev.c


#include <linux/init.h>
#include <linux/module.h>
#include <linux/device.h>

extern struct bus_type mybus;

struct device mydev = {
.init_name = "mykey0",
.bus = &mybus,
};

static int __init test_init(void)
{
return device_register(&mydev);
}

static void __exit test_exit(void)
{
device_unregister(&mydev);
}

module_init(test_init);
module_exit(test_exit);

MODULE_LICENSE("GPL");

mydev2.c


#include <linux/init.h>
#include <linux/module.h>
#include <linux/device.h>

extern struct bus_type mybus;

struct device mydev = {
.init_name = "mykey1",
.bus = &mybus,
};

static int __init test_init(void)
{
return device_register(&mydev);
}

static void __exit test_exit(void)
{
device_unregister(&mydev);
}

module_init(test_init);
module_exit(test_exit);

MODULE_LICENSE("GPL");

mydrv.c


#include <linux/init.h>
#include <linux/module.h>
#include <linux/device.h>

extern struct bus_type mybus;

int myprobe(struct device *dev) //参数为则匹配上的设备对象的地址
{
printk("in myprobe ...%s\n", dev_name(dev));
return 0; //返回0表示设备驱动对刚匹配上的设备初始化成功,负数表示失败
}

int myremove(struct device *dev) //参数为需要结束驱动工作的设备对象的地址
{
printk("in myremove ...%s\n", dev_name(dev));
return 0;
}


struct device_driver mydrv = {
.name = "mykey",
.bus = &mybus,

.owner = THIS_MODULE,

.probe = myprobe,
.remove = myremove,
};

static int __init test_init(void)
{
return driver_register(&mydrv);
}

static void __exit test_exit(void)
{
driver_unregister(&mydrv);
}

module_init(test_init);
module_exit(test_exit);

MODULE_LICENSE("GPL");

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
超声波测距模块实现驱动模型的实例:
总线与上面的例子代码不变.

//封装一个头文件,表示超声波测距模块所用的两个io口
distancer.h

#ifndef __DISTANCER_H
#define __DISTANCER_H

typedef struct {
int trigger_io; //表示trigger的引脚
int echo_io; //捕捉数据的引脚
}mypdata_t;
#endif /* __DISTANCER_H */

//描述一个超声波测距模块
mydev.c

#include <linux/init.h>
#include <linux/module.h>
#include <linux/device.h>
#include <mach/gpio.h>
#include "distancer.h"

extern struct bus_type mybus;

mypdata_t pdata = {
.trigger_io = GPIOA(8), //PA8
.echo_io = GPIOA(7), //PA7
};

struct device mydev = {
.init_name = "distancer0",
.bus = &mybus,
.platform_data = &pdata,
};

static int __init test_init(void)
{
return device_register(&mydev);
}

static void __exit test_exit(void)
{
device_unregister(&mydev);
}

module_init(test_init);
module_exit(test_exit);

//设备驱动
mydrv.c


#include <linux/init.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/gpio.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/ktime.h>
#include <linux/slab.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/mutex.h>
#include "distancer.h"

#define MYMAJOR 1234

typedef struct {
u64 prev; //记录超声波测距的上升沿中断时间
int started; //表示已接收到上升沿中断
int time; //测量的时间多少us

int locked;
struct mutex mutex;

struct cdev cdev;
mypdata_t *pdata;

}mydata_t; //在设备驱动里每个超声波设备都用一个这类型的对象来记录时间

extern struct bus_type mybus;

ssize_t myread(struct file *fl, char __user *buf, size_t len, loff_t *off)
{
struct cdev *cdev = fl->f_path.dentry->d_inode->i_cdev;
mydata_t *data = container_of(cdev, mydata_t , cdev);
mypdata_t *pdata = data->pdata;
int ret;

//////// 发出开始信号///////
gpio_direction_output(pdata->trigger_io, 0);
gpio_set_value(pdata->trigger_io, 1);
msleep(1);
gpio_set_value(pdata->trigger_io, 0);

//上锁
data->locked = 1;
ret = mutex_lock_interruptible(&data->mutex);
if (ret < 0)
return -ERESTART;

data->locked = 0;
//复制数据
sprintf(buf, "last time: %dus\n", data->time);

return strlen(buf);
}


struct file_operations fops = {
.read = myread,
};


irqreturn_t irq_func(int irqno, void *arg) //多个设备共用这个中断处理函数
{
struct device *dev = (struct device *)arg;
mydata_t *data = dev_get_drvdata(dev);
mypdata_t *pdata = dev->platform_data;
u64 now = ktime_to_us(ktime_get());

if (gpio_get_value(pdata->echo_io)) //高电平表示上升沿的中断
{
data->started = 1;
data->prev = now;
}
else if(data->started) //下降沿中断, 测量结束
{
data->started = 0;
// printk("%lldus\n", now - data->prev);
data->time = (now - data->prev)>>1;
if (data->locked)
mutex_unlock(&data->mutex);
}
return IRQ_HANDLED;
}

static struct class *mycls;
int myprobe(struct device *dev) //参数为则匹配上的设备对象的地址
{
mypdata_t *pdata = dev->platform_data;
int ret, irqno;
mydata_t *data;
dev_t devid;
static int mi = 0;

if (NULL == pdata)
return -EINVAL;

ret = gpio_request(pdata->trigger_io, dev_name(dev));
if (ret < 0)
goto err0;
ret = gpio_request(pdata->echo_io, dev_name(dev));
if (ret < 0)
goto err1;

irqno = gpio_to_irq(pdata->echo_io);
ret = request_irq(irqno, irq_func, IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING, dev_name(dev), dev);
if (ret < 0)
goto err2;

data = kzalloc(sizeof(*data), GFP_KERNEL);//每个设备都分配一个记录时间的对象
dev_set_drvdata(dev, data);

data->pdata = pdata;
mutex_init(&data->mutex);
mutex_lock(&data->mutex);

devid = MKDEV(MYMAJOR, mi++);
ret = register_chrdev_region(devid, 1, dev_name(dev));
if (ret < 0)
goto err3;

cdev_init(&data->cdev, &fops);
data->cdev.owner = THIS_MODULE;
ret = cdev_add(&data->cdev, devid, 1);
if (ret < 0)
goto err4;

device_create(mycls, NULL, data->cdev.dev, NULL, dev_name(dev));
printk("in myprobe ...%s\n", dev_name(dev));

return 0; //返回0表示设备驱动对刚匹配上的设备初始化成功,负数表示失败
err4:
unregister_chrdev_region(devid, 1);
err3:
kfree(data);
free_irq(irqno, dev);
err2:
gpio_free(pdata->echo_io);
err1:
gpio_free(pdata->trigger_io);
err0:
return ret;
}

int myremove(struct device *dev) //参数为需要结束驱动工作的设备对象的地址
{
mypdata_t *pdata = dev->platform_data;
int irqno = gpio_to_irq(pdata->echo_io);
mydata_t *data = dev_get_drvdata(dev);

unregister_chrdev_region(data->cdev.dev, 1);
cdev_del(&data->cdev);

device_destroy(mycls, data->cdev.dev);

free_irq(irqno, dev);
gpio_free(pdata->trigger_io);
gpio_free(pdata->echo_io);
kfree(data);
printk("in myremove ...%s\n", dev_name(dev));

return 0;
}


struct device_driver mydrv = {
.name = "distancer",
.bus = &mybus,

.owner = THIS_MODULE,

.probe = myprobe,
.remove = myremove,
};

static int __init test_init(void)
{
mycls = class_create(THIS_MODULE, "distancer");
return driver_register(&mydrv);
}

static void __exit test_exit(void)
{
driver_unregister(&mydrv);
class_destroy(mycls);
}

module_init(test_init);
module_exit(test_exit);

MODULE_LICENSE("GPL");

//当再接入一个超声波模块时,设备驱动完全无需改成,只需再描述一个设备即可
mydev.c


#include <linux/init.h>
#include <linux/module.h>
#include <linux/device.h>
#include <mach/gpio.h>
#include "distancer.h"

extern struct bus_type mybus;

mypdata_t pdata = {
.trigger_io = GPIOA(10), //PA10
.echo_io = GPIOA(9), //PA9
};

struct device mydev = {
.init_name = "distancer1",
.bus = &mybus,
.platform_data = &pdata,
};

static int __init test_init(void)
{
return device_register(&mydev);
}

static void __exit test_exit(void)
{
device_unregister(&mydev);
}

module_init(test_init);
module_exit(test_exit);

MODULE_LICENSE("GPL");