上一次做了一个kobject实例,这次做的是总线、设备、驱动的实例。这两个例子都比较虚,实际开发很难看见它们。对于一个急于开发某款硬件驱动的人来说意义不大,且对于有实际任务的你也很难静下心来看这些苍白的东西。
Ldd3的话:
许多驱动作者将不会需要这里涉及的材料。这个水平的细节通常在总线级别处理,并且很少作者需要添加一个新总线类型。这个信息是有用的,但是,对任何人好奇在 PCI, USB等层面的里面发生了什么或者谁需要在那个级别做改变。
我第一次看到这句话时就把设备模型这章跳过了,现在做一些总线的驱动才会感到很多的疑惑,疑惑当然就是总线、设备、驱动、sysfs之间的关系。虽然这些疑惑可能不会影响你开发驱动(很多人包括我认为驱动只是对系统提供的接口的填充,最后主要是对硬件的理解),但请相信我,看完设备模型后,你会感觉自己站在另一个高度去看待驱动。
开场白结束。
基本知识:略。
基本函数:网上有。
开发平台:还是那个平台。linux-3.2.36
实例结构图:
曲线表示对应关系
有个总线叫mine,它有一个驱动叫my_dev,有两个设备叫my_dev0,my_dev1。my_dev0和my_dev1对应驱动my_dev。
先贴两个.h文件
//my_bus.h
#ifndef __MY_BUS_H__
#define __MY_BUS_H__
#define CHR_LEN 20
//模仿usb,usb用这个记录一些特别的设备
#define MY_DEVICE(vend, prod) \
.idVendor = (vend), \
.idProduct = (prod)
//是否用初始化
typedef enum
{
NO = 0,
YES = 1,
}initFg;//可以用个bool
struct my_device_id
{
__u16 idVendor;
__u16 idProduct;
initFg initFg;
};
#define MATCH_ID(id1, id2) ((id1->idVendor == id2->idVendor) & (id1->idProduct == id2->idProduct))
struct my_driver
{
char const *version;
struct module *module;
struct device_driver driver;
//在usb中没用device_driver,而是自己定义了usb_driver和struct usb_device_driver等
struct driver_attribute *version_attr;
const struct my_device_id *id_table;
};
#define to_my_driver(drv) container_of(drv, struct my_driver, driver)
struct my_device
{
char const *name;
const struct my_device_id *id;
struct device_attribute *attr;
struct device dev;
struct my_driver *mydriver;
};
#define to_my_device(dev) container_of(dev, struct my_device, dev)
extern int register_my_device(struct my_device *);
extern void unregister_my_device(struct my_device *);
extern int register_my_driver(struct my_driver *);
extern void unregister_my_driver(struct my_driver *);
#endif//__MY_BUS_H__
//common.h
#ifndef __COMMON_H__
#define __COMMON_H__
#define DEBUG_EN
#define PRINT_COLOR_RESET "\33[0m"
#define PRINT_COLOR_BLACK "\33[30m"
#define PRINT_COLOR_RED "\33[31m"
#define PRINT_COLOR_GREEN "\33[32m"
#define PRINT_COLOR_YELLOW "\33[33m"
#define PRINT_COLOR_BLUE "\33[34m"
#define PRINT_COLOR_MAGENTA "\33[35m"
#define PRINT_COLOR_CYAN "\33[36m"
#define PRINT_COLOR_WHITE "\33[37m"
#define Printk(COLOR, ARGs...) \
printk(COLOR); \
printk(KERN_DEBUG ARGs); \
printk(PRINT_COLOR_RESET) \
#ifdef DEBUG_EN
#define MY_DEBUG(ARGs...) Printk(PRINT_COLOR_YELLOW, ARGs)
#else
#define MY_DEBUG(ARGs...) ;
#endif
#endif//__COMMON_H__
//my_bus.c
#include <linux/device.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include "my_bus.h"
#include "common.h"
MODULE_AUTHOR("wwxxxxll");
MODULE_LICENSE("Dual BSD/GPL");
struct my_bus_info
{
struct bus_type my_bus_type;
struct device my_bus;
struct bus_attribute *my_bus_attrs_version;
struct bus_attribute *my_bus_attrs_info;
char info[CHR_LEN];
};
#define to_my_bus(data) container_of(data, struct my_bus_info, my_bus_type)
/****************************attr***************************/
static ssize_t show_bus_version(struct bus_type *bus, char *buf)
{
return snprintf(buf, CHR_LEN, "%s\n", "my_bus-1.0.0");
}
static BUS_ATTR(version, S_IRUGO, show_bus_version, NULL);//定义一个attr
/*attr结构
struct bus_attribute {
struct attribute attr;
ssize_t (*show)(struct bus_type *bus, char *buf);
ssize_t (*store)(struct bus_type *bus, const char *buf,
size_t count);
};
*/
static ssize_t show_bus_info(struct bus_type *bus, char *buf)
{
struct my_bus_info *my_bus_info_p = to_my_bus(bus);
return snprintf(buf, CHR_LEN, "%s\n", my_bus_info_p->info);
}
static ssize_t store_bus_info(struct bus_type *bus, const char *buf, size_t count)
{
struct my_bus_info *my_bus_info_p = to_my_bus(bus);
sscanf(buf, "%s", my_bus_info_p->info);
return count;
}
//S_IRUGO | S_IWUSR 允许root 来改变参数
static BUS_ATTR(info, S_IRUGO | S_IWUSR, show_bus_info, store_bus_info);
/***********************************************************/
/****************************bus****************************/
//热插拔先不管
static int my_bus_uevent(struct device *dev, struct kobj_uevent_env *env)
{
MY_DEBUG("my_bus_uevent\n");
return 0;
}
static int my_bus_match(struct device *dev, struct device_driver *driver)
{
//platform_data会在my_dvc.c中赋值
struct my_device *mydev = (struct my_device*)dev->platform_data;
MY_DEBUG("my_bus_match\n");
/*
在device_add中
if (dev->init_name) {
dev_set_name(dev, "%s", dev->init_name);
dev->init_name = NULL;
}
dev->init_name的值传给dev->kobj中,自己变为NULL
我开始这样判断
!strncmp(dev->init_name, driver->name, strlen(driver->name));
当然会出现段错误,搞死我了!
*/
if (!strncmp(mydev->name, driver->name, strlen(driver->name)))
{
mydev->mydriver = to_my_driver(driver);
return 1;
}
return 0;
}
static void my_bus_release(struct device *dev)
{
MY_DEBUG("my_bus_release\n");
}
static struct my_bus_info my_bus_info_data =
{
.my_bus_type =
{
.name = "mine",
.match = my_bus_match,
.uevent = my_bus_uevent,
},
.my_bus =
{
.init_name = "mine0",
.release = my_bus_release,
},
.my_bus_attrs_version = &bus_attr_version,
.my_bus_attrs_info = &bus_attr_info,
.info = "Hello World",
};
/***********************************************************/
/******************************dvc**************************/
int register_my_device(struct my_device *mydev)
{
int ret = 0;
mydev->dev.bus = &my_bus_info_data.my_bus_type;
mydev->dev.parent = &my_bus_info_data.my_bus;
ret = device_register(&mydev->dev);
if (ret)
{
return ret;
}
if (mydev->attr == NULL)
{
return ret;
}
ret = device_create_file(&mydev->dev, mydev->attr);
if (ret)
{
device_unregister(&mydev->dev);
}
return ret;
}
EXPORT_SYMBOL(register_my_device);
void unregister_my_device(struct my_device *mydev)
{
device_unregister(&mydev->dev);
}
EXPORT_SYMBOL(unregister_my_device);
/***********************************************************/
/******************************dvr**************************/
int register_my_driver(struct my_driver *driver)
{
int ret = 0;
driver->driver.bus = &my_bus_info_data.my_bus_type;
ret = driver_register(&driver->driver);
if (ret)
return ret;
if (driver->version_attr == NULL)
{
return ret;
}
ret = driver_create_file(&driver->driver, driver->version_attr);\
if (ret)
{
driver_unregister(&driver->driver);
}
return ret;
}
EXPORT_SYMBOL(register_my_driver);
void unregister_my_driver(struct my_driver *driver)
{
driver_unregister(&driver->driver);
}
EXPORT_SYMBOL(unregister_my_driver);
/***********************************************************/
static int __init my_bus_init(void)
{
int ret;
ret = bus_register(&my_bus_info_data.my_bus_type);
if (ret)
return ret;
ret = bus_create_file(&my_bus_info_data.my_bus_type, my_bus_info_data.my_bus_attrs_version);
if (ret)
goto fail;
ret = bus_create_file(&my_bus_info_data.my_bus_type, my_bus_info_data.my_bus_attrs_info);
if (ret)
goto fail1;
ret = device_register(&my_bus_info_data.my_bus);
if (ret)
{
goto fail2;
}
return 0;
fail2:
bus_remove_file(&my_bus_info_data.my_bus_type, my_bus_info_data.my_bus_attrs_info);
fail1:
bus_remove_file(&my_bus_info_data.my_bus_type, my_bus_info_data.my_bus_attrs_version);
fail:
bus_unregister(&my_bus_info_data.my_bus_type);
return ret;
}
static void __exit my_bus_exit(void)
{
device_unregister(&my_bus_info_data.my_bus);
bus_remove_file(&my_bus_info_data.my_bus_type, my_bus_info_data.my_bus_attrs_info);
bus_remove_file(&my_bus_info_data.my_bus_type, my_bus_info_data.my_bus_attrs_version);
bus_unregister(&my_bus_info_data.my_bus_type);
}
module_init(my_bus_init);
module_exit(my_bus_exit);
//调试bus
# insmod my_bus.ko
# ls /sys/bus/
mine platform
有我们的mine
你可以试试version 和 info,我就不做了
//my_dvr.c
#include <linux/module.h>
#include <linux/device.h>
#include <linux/init.h>
#include <linux/kernel.h> /* printk() */
#include <linux/fs.h> /* everything... */
#include <linux/errno.h> /* error codes */
#include <linux/types.h> /* size_t */
#include "my_bus.h"
#include "common.h"
MODULE_AUTHOR("wwxxxxll");
MODULE_LICENSE("Dual BSD/GPL");
//假设一个设备需要特殊的初始化
static const struct my_device_id my_device_list[] = {
{MY_DEVICE(0x0002, 0x0002), YES},
{}
};
static ssize_t show_version(struct device_driver *driver, char *buf)
{
struct my_driver *ldriver = to_my_driver(driver);
sprintf(buf, "%s\n", ldriver->version);
return strlen(buf);
}
static DRIVER_ATTR(version, S_IRUGO, show_version, NULL);//不说了
static bool special_match(const struct my_device_id *id, const struct my_device_id *sdvc_list)
{
int i = sizeof(my_device_list);
//虽然只有一个,我们还是认为它有很多
while(i--)
{
if (MATCH_ID(sdvc_list, id))
{
return 1;
}
sdvc_list++;
}
return 0;
}
//特殊器件初始化
static void special_init(void)
{
MY_DEBUG("special_init\n");
}
/*
if ((drv->bus->probe && drv->probe) ||
(drv->bus->remove && drv->remove) ||
(drv->bus->shutdown && drv->shutdown))
printk(KERN_WARNING "Driver '%s' needs updating - please use "
"bus_type methods\n", drv->name);
这是driver_register中的一段,bus和driver的probe都有会报警告,
我用实例验证是只调用了bus的probe
*/
//这里我们做个probe的例子
static int my_dvr_probe(struct device *dev)
{
struct my_device *mydev = (struct my_device*)dev->platform_data;
MY_DEBUG("my_dvr_probe\n");//只是打印太没意思,所以做了一个特殊设备判断
if (special_match(mydev->id, mydev->mydriver->id_table))//mydev->mydriver的赋值在bus的match中
{
special_init();
}
return 0;
}
static int my_dvr_remove(struct device *dev)
{
MY_DEBUG("my_dvr_remove\n");
return 0;
}
static struct my_driver my_dvr_driver = {
.version = "mydvr: 1.0.0",
.module = THIS_MODULE,
.driver = {
.name = "my_dev",
.probe = my_dvr_probe,
.remove = my_dvr_remove,
},
.version_attr = &driver_attr_version,
.id_table = my_device_list,
};
int my_dvr_init(void)
{
return register_my_driver(&my_dvr_driver);
}
void my_dvr_cleanup(void)
{
unregister_my_driver(&my_dvr_driver);
}
module_init(my_dvr_init);
module_exit(my_dvr_cleanup);
//调试my_dvr.c
# insmod my_dvr.ko
# ls /sys/bus/mine/drivers
my_dev
# ls /sys/bus/mine/drivers/my_dev/
bind uevent unbind version
有了my_dev驱动
//my_dvc.c
#include <linux/module.h>
#include <linux/device.h>
#include <linux/init.h>
#include <linux/kernel.h> /* printk() */
#include <linux/fs.h> /* everything... */
#include <linux/errno.h> /* error codes */
#include <linux/types.h> /* size_t */
#include "my_bus.h"
#include "common.h"
MODULE_AUTHOR("wwxxxxll");
MODULE_LICENSE("Dual BSD/GPL");
//看到下面这个,是不是想起usb驱动
#define VENDOR_ID1 0x0001
#define PRODUCT_ID1 0x0001
#define VENDOR_ID2 0x0002
#define PRODUCT_ID2 0x0002
static const struct my_device_id my_dvc_table[] = {
{MY_DEVICE(VENDOR_ID1, PRODUCT_ID1), NO},
{MY_DEVICE(VENDOR_ID2, PRODUCT_ID2), NO},
{}
};
MODULE_DEVICE_TABLE(my_dvc, my_dvc_table);
static ssize_t my_dvc_show_info(struct device *devp, struct device_attribute *attr, char *buf)
{
return snprintf(buf, strlen("test device\n"), "test device\n");
}
//生成/sys/bus/mine/devices/my_dev0/info,老玩意了,不调试
static DEVICE_ATTR(info, S_IRUGO, my_dvc_show_info, NULL);
static void my_dev_release(struct device *dev)
{
struct my_device *mydev = (struct my_device*)dev->platform_data;
MY_DEBUG("%s_release\n", mydev->name);
}
static struct my_device my_dvcs[2] =
{
{
.name = "my_dev0",
.id = my_dvc_table,
.dev =
{
.platform_data = &my_dvcs[0],
.release = my_dev_release,
.init_name = "my_dev0",
},
.attr = &dev_attr_info,
},
{
.name = "my_dev1",
.id = &my_dvc_table[1],
.dev =
{
.platform_data = &my_dvcs[1],
.release = my_dev_release,
.init_name = "my_dev1",
},
},
};
int my_dvc_init(void)
{
int i = 0, ret = 0;
for (i = 0; i < 2; i++)
{
if ((ret = register_my_device(&my_dvcs[i])))
{
break;
}
}
if (ret)
{
while(--i)
{
unregister_my_device(&my_dvcs[i]);
}
}
return ret;
}
void my_dvc_cleanup(void)
{
int i=0;
for (i = 0; i < 2; i++) {
unregister_my_device(&my_dvcs[i]);
}
}
module_init(my_dvc_init);
module_exit(my_dvc_cleanup);
//调试my_dvc.c
# insmod my_dvc.ko
#dmesg
my_bus_uevent
my_bus_match
my_dvr_probe
my_bus_uevent
my_bus_match
my_dvr_probe
special_init
前三个是my_dev0
后四个是my_dev1,它是特殊设备,所以调用special_init
查看一下
# ls /sys/bus/mine/devices/
my_dev0 my_dev1
# ls /sys/bus/mine/devices/my_dev0
driver info power subsystem uevent
# ls /sys/bus/mine/devices/my_dev1
driver power subsystem uevent
看一下drivers
# ls -l /sys/bus/mine/drivers/my_dev/*
--w------- 1 0 0 4096 Jan 1 00:03 /sys/bus/mine/drivers/my_dev/bind
lrwxrwxrwx 1 0 0 0 Jan 1 00:03 /sys/bus/mine/drivers/my_dev/my_dev0 -> ../../../../devices/mine0/my_dev0
lrwxrwxrwx 1 0 0 0 Jan 1 00:03 /sys/bus/mine/drivers/my_dev/my_dev1 -> ../../../../devices/mine0/my_dev1
--w------- 1 0 0 4096 Jan 1 00:03 /sys/bus/mine/drivers/my_dev/uevent
--w------- 1 0 0 4096 Jan 1 00:03 /sys/bus/mine/drivers/my_dev/unbind
-r--r--r-- 1 0 0 4096 Jan 1 00:03 /sys/bus/mine/drivers/my_dev/version
看到连接了吧
# rmmod my_dvc
rmmod: remove 'my_bus': Resource temporarily unavailable
//my_bus没卸载成功,应该是my_dvr用到了my_bus中的
extern int register_my_driver(struct my_driver *);
extern void unregister_my_driver(struct my_driver *);
所以会报Resource temporarily unavailable,先这样
#dmesg
...
my_dvr_remove
my_bus_uevent
my_dev0_release
my_dvr_remove
my_bus_uevent
my_dev1_release
//同时卸载 my_dvr
//补一个卸载my_bus
#rmmod my_bus
#dmesg
...
my_bus_release
代码下载:
http://download.csdn.net/detail/xxxxxlllllxl/5736659
用的vs编辑,里面有^M符号,不要管它。
class不说了。
下期预告:
热插拔