驱动内核平台移植步骤
在计算机中有这样一类设备(如I2C ,USB等),它们通过各自的设备控制器,直接和CPU连接,CPU可 以通过常规的寻址操作访问它们(或者说访问它们的控制器)。
这种连接方式,并不属于传统意义上的总线连接。但设备模型应该具备普适性,因此Linux就虚构了一条Platform Bus,供这些设备挂靠。
为了便于统一管理, linux抽象出 了资源的概念,使用时仅需get_ resource.
优点:
1隔离了硬件信息和驱动,做 到板相关的代码和驱动分离。 ----》 修改扩展
如果硬件有改动或移植,仅 需修改一下platform中关于资源的描述就可以了。
2符合 总线 驱动 设备的统设备模型, 方便管理
在层次结构上,有一个设备serial,将有一个serial的目录,serial设备里面是属性文件,可以通过修改属性文件值修改驱动
/sys sysfs
|
bus keset
|
pci keset
|
drivers keset(一组有共性的驱动集合)
|
serial kobject(一个具体的设备)
|
newid attribute
设备描述结构体:
struct device{
struct kobject kobj;
struct bus_type * bus;
struct device_driver *driver;
void *driver_data;
}
驱动描述结构体:
struct device_driver{
const char *name;
struct bus_type *bus;
int (*probe) (struct device *dev);//初始化函数需要自己实现
int (*remove) (struct device *dev);//退出函数需要自己实现
}
总线描述结构体:
struct bus_type{
const char *name;//总线类型名称
int (*match)(struct deviec *dev,struct device_driver *drv);//函数会自动通过名字去匹配设备device 和驱动driver
int (*uevent)(struct device *dev,char **envp);
int (*probe)(struct device *dev);
int (*remove)(struct device *dev);
}
硬件资源结构体:
struct resource{
resource_size_t start;//资源开始位置
resource_size_t end;//资源结束位置
const char *name;
unsigned long flags;//资源的类型 IORESOURCE_MEM(内存地址) IORESOURCE_IRQ(中断)
}
平台设备结构体:
struct platform_ device {
const char * name; //平台设备的名称
u32 id;
struct device dev; //内嵌的device实现继承
u32 num_ resources ;
struct resource * resource; // 平台设备的资源
};
平台驱动结构体:
struct platform_driver {
int (*probe)(struct platform_device *) ;
int (* remove)(struct platfbrm_device *) ;
struct device_ driver driver; // 内嵌的device_ driver实现继承
};
平台驱动具体操作:
1.在内核的设备树文件中添加自定义设备:
例:vim linux-3.14/arch/arm/boot/dts/exynos4412-origen.dts
参考模板:
myled@0x11000c40{
compatible = "farsight,myled";
reg = <0x0203F000 0x4>,<0x0203F0c5 0x4>;
}
解释说明:
compatible 表示设备描述作用是与设备进行匹配,必须保证字符串的内容完全一致。
reg:表示初始的地址,和内存空间的大小。如果有两个地址需要定义,那么写法如下:
reg = <0x0203F000 0x4>,<0x0203F0c5 0x4>;
驱动文件修改:
static int myled_probe(struct platform_device *pdev)
{
int ret = -1;
//内核相关的部分
//1.注册
devno = MKDEV(MA,MI);
ret = register_chrdev_region(devno,devNum,devName);
if(ret != 0)
return ret;
//2.初始化
cdev_init(&mydev,&myops);
//3.添加到系统
ret = cdev_add(&mydev,devno,devNum);
if(0!=ret)
{
unregister_chrdev_region(devno,devNum);
return ret;
}
//硬件相关的部分
//1.将实际的物理地址映射为虚拟地址
resource *pgx2conres = platform_get_resource(pdev,IORESOURCE_MEM,0);//将实际的内存地址存在了设备树中
resource *pgx2datres = platform_get_resource(pdev,IORESOURCE_MEM,1);
gpx2con = ioremap(pgx2conres->start,4);
gpx2dat = ioremap(pgx2datres->start,4);
//2.对硬件进行初始化操作
writel((readl(gpx2con) & ~(0xf<<28)) | (0x1<<28),gpx2con);
writel(readl(gpx2dat) & ~(0x1<<7) ,gpx2dat);
printk("mod init OK\n");
return 0;
}
static void myled_drv_remove (struct platform_device *)
{
//内核部分
//1.删除设备
cdev_del(&mydev);
//2.注销设备
unregister_chrdev_region(devno,devNum);
//硬件部分
//1.复位(不是必须操作)
writel(readl(gpx2dat) & ~(0xf<<7), gpx2dat);
//2.地址回收(必须操作)
iounmap(gpx2con);
iounmap(gpx2dat);
printk("mod exit OK\n");
return;
}
#ifdef CONFIG_OF
static const struct of_ device_ id myled_ of_ matches[] = {
{ .compatible = "farsight,myled", },//(这个名字与设备树中的compatible必须完全匹配)
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, myled_of_matches);
#endif
static struct platform_ driver myled_ driver = {
.driver = {
.name = "myled".
.owner = THIS_ MODULE,
.of_ match_ table = of_match_ptr(myled_ of_ matches),
},
.probe = myled_probe,
.remove = myled_drv_remove,
};
module_platform_driver( myled_driver);
平台驱动与纯模块驱动的最大的改动为模块注册部分,然后就是硬件操作时的地址读取部分
最后进入linux3.14目录下重新编译一下设备树文件:make dtbs
,再将设备树移动到tftp文件夹下
//在/dev下自动创建设备节点(管理设备文件方法演变mkdnod -- > devfs(动态内核) --> udev(真实用户) )
//?什么是devfs,它有何优缺点
是由Linux2.4 引入的,它使得设备驱动能管理自己,如创建删除/dev,修改权限等。
优:
1.无需手动mknod,它可devfs_ mk cdev 等自动生成
2.不需要指定主次设备号,传e的主设备号,它会动态获取。
缺:
1.不确定的设备映射,
2. /dev下文件太多,且不是表示当前系统实际的设备,
//?什么是udev,它有何优缺点
是由linux2.6 引入的用于替代devfs。
优:
1. 稳定的设备映射
2. /dev下只包含系统真实存在的设备
3.无需手动mknod,用device_ create会 自动创建
struct class *my_ class ;
//加到init函数的内核部分
/* creating your Own cLass */
my_class = class_create(THIS_MODULE, "dev_class");
if (IS_ERR(my_class)) {
printk("Err: failed in creating class.\n");
return ;
}
/* register your own device in sysfs, and this will cause udevd to create corresponding device node */
device_create(my_class, NULL, devno, NULL, "myled");
该段代码主要是动态在/sys/class目录下创建一个dev_class的文件夹,再在文件夹中创建一个myled的设备,然后系统会自动调用/dev/mydev目录,如果该目录下没有相应地设备驱动,那么系统会自动添加驱动。