学了input子系统之后发现那种把硬件代码和软件代码分开的方法非常好。如果我自己也想写一个程序把容易改变的代码和比较稳定的代码分开写怎么办??自己建立联系很麻烦,内核中有没有现成的可以建立左右两边结构体联系的一种机制呢??有的,那就是bus_drv_dev模型
一、大概原理
dev.c中分配并注册platform_device结构体,platform_device结构体中有资源
drv.c中分配并注册platform_driver结构体,platform_driver结构体中函数probe实现想要实现的功能
怎么让probe函数用到device结构体中的资源呢??注册dev或drv时都会调用bus总线中的match函数,match发现如果dev与drv的name相同就会调用drv中的prob函数,并且传递platform_device结构体的指针给probe函数,这样probe函数就能调用device结构体中的资源了
二、dev.c中
分配并注册platform_device结构体
首先来看一下platform_device结构体是如何定义的
struct platform_device {
const char * name;
u32 id;
struct device dev;
u32 num_resources;
struct resource * resource;
};
我们照着platform_device的定义来填充内容
name是名字
id是
num_resources资源个数,用ARRAY_SIZE()函数求个数
resource是资源
dev干啥的我也不知道,很是复杂
static struct platform_device led_dev = {
.name = "myled",
.id = -1,
.num_resources = ARRAY_SIZE(led_resource),
.resource = led_resource,
.dev = {
.release = led_release,
},
};
再看一下resource结构体
struct resource {
resource_size_t start;
resource_size_t end;
const char *name;
unsigned long flags;
struct resource *parent, *sibling, *child;
};
这样填充
static struct resource led_resource[] = {
[0] = {
.start = 0x56000050,
.end = 0x56000050 + 8 - 1,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = 5,
.end = 5,
.flags = IORESOURCE_IRQ,
}
};
对硬件操作一般也就是对寄存器操作,所以这里的资源存储的是地址,要换一个同类型的设备只需要改一下这里的地址就可以了
注册平台设备
static int led_dev_init(void)
{
platform_device_register(&led_dev);
return 0;
}
卸载平台设备
static void led_dev_exit(void)
{
platform_device_unregister(&led_dev);
}
二、drv.c中
分配并注册platform_driver结构体
platform_driver结构体中有很多函数
其中probe函数建立连接时会被调用
remove函数卸载时会被调用
其他的没有去了解
struct platform_driver {
int (*probe)(struct platform_device *);
int (*remove)(struct platform_device *);
void (*shutdown)(struct platform_device *);
int (*suspend)(struct platform_device *, pm_message_t state);
int (*suspend_late)(struct platform_device *, pm_message_t state);
int (*resume_early)(struct platform_device *);
int (*resume)(struct platform_device *);
struct device_driver driver;
};
分配一个platform_driver结构体
struct platform_driver led_drv = {
.probe = led_probe,
.remove = led_remove,
.driver = {
.name = "myled",
}
};
在prob函数中去实现想要的功能
remove函数做卸载后的处理
注册platform_driver结构体
static int led_drv_init(void)
{
platform_driver_register(&led_drv);
return 0;
}
static void led_drv_exit(void)
{
platform_driver_unregister(&led_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>
static struct resource led_source[] = {
[0] = {
.start = 0x56000050,
.end = 0x56000050 +8 - 1,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = 5,
.end = 5,
.flags = IORESOURCE_IRQ,
},
};
static struct platform_device led_dev = {
. name = "led",
.id = -1,
.num_resources = ARRAY_SIZE(led_source),
.resource = led_source,
};
static int led_init()
{
platform_device_register(&led_dev);
return 0;
}
static void led_exit()
{
platform_device_unregister(&led_dev);
}
module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");
代码
#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 *gpfcon;
static volatile unsigned long *gpfdat;
static int pin;
static int led_open (struct inode *inode, struct file *file)
{
*gpfcon &= ~(0x3<<(pin*2));
*gpfcon |= (1<<(pin*2));
return 0;
}
static ssize_t led_write (struct file *file, const char __user *buf, size_t size, loff_t *ppos)
{
static int val;
copy_from_user(&val,buf,size);
if (val == 1)
{
// 点灯
*gpfdat &= ~(1<<pin);
}
else
{
// 灭灯
*gpfdat |= (1<<pin);
}
return 0;
}
static struct file_operations led_operation = {
.open =led_open,
.write =led_write,
};
static int led_probe(struct platform_device * dev)
{
static struct resource * res;
res = platform_get_resource(dev,IORESOURCE_MEM,0);
gpfcon = ioremap(res->start,res->end - res->start + 1);
gpfdat = gpfcon + 1;
res = platform_get_resource(dev, IORESOURCE_IRQ, 0);
pin = res->start;
major = register_chrdev(0,"led_drv",&led_operation);
printk("led_probe, found led\n");
cls = class_create(THIS_MODULE, "myled");
class_device_create(cls, NULL, MKDEV(major, 0), NULL, "led"); /* /dev/led */
return 0;
}
static int led_remove()
{
printk("led_remove, remove led\n");
class_device_destroy(cls, MKDEV(major, 0));
class_destroy(cls);
unregister_chrdev(major,"led_drv");
iounmap(gpfcon);
return 0;
}
static struct platform_driver led_drv = {
.probe = led_probe,
.remove =led_remove,
.driver ={
.name ="led",
},
};
static int led_init()
{
platform_driver_register(&led_drv);
return 0;
}
static void led_exit()
{
platform_driver_unregister(&led_drv);
}
module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");
代码led_test.c
#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 <on|off>\n", argv[0]);
return 0;
}
if (strcmp(argv[1], "on") == 0)
{
val = 1;
}
else
{
val = 0;
}
write(fd, &val, 4);
return 0;
}
编译的时候用内核顶层目录的makefile编译,
Makefile
KERN_DIR = /work/system/linux-2.6.22.6
all:
make -C $(KERN_DIR) M=`pwd` modules
clean:
make -C $(KERN_DIR) M=`pwd` modules clean
rm -rf modules.order
obj-m += led_drv.o
obj-m += led_dev.o
用户空间向内核空间传递数据
应用程序调用
write(fd, &val, 4);
把val的4字节数据写到open的文件
驱动程序调用
copy_from_user(&val, buf, count);
根据驱动程序write函数传进来的参数从buf读数据读到val的地址,大小count