1.驱动:driver.c
#include <linux/module.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <asm/uaccess.h>
#include <asm/irq.h>
#include <asm/io.h>
#define DEVICENAME "myled"
static unsigned int major=0;
unsigned int pin=0;
static struct class *myclass=NULL;
static struct class_device *mydev;
static volatile unsigned long *gpfcon=NULL;
static volatile unsigned long *gpfdat=NULL;
#define GPFCON *gpfcon
#define GPFDAT *gpfdat
static int led_open(struct inode *inode, struct file *file)
{
//配置LED管脚模式
GPFCON &=~(0x3<<(pin*2));
GPFCON |= (0x1<<(pin*2));
//默认灯全部关闭
GPFDAT |= ((7<<4));
return 0;
}
static ssize_t led_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos)
{
int val;
copy_from_user(&val, buf, count); // copy_to_user();
if (val == 1)
{
// 点灯
GPFDAT &= ~(1<<pin);
}
else
{
// 灭灯
GPFDAT |= (1<<pin);
}
return 0;
}
static struct file_operations my_dev_fops = {
.owner = THIS_MODULE, /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */
.open = led_open,
.write = led_write,
};
/* 分配/设置/注册一个platform_driver */
static int __devinit led_probe(struct platform_device *pdev)
{
/* 根据platform_dev资源进行ioremap */
struct resource *res;
// 从device中获取资源 resource led_resource[]
// parms: device ,资源类型,资源索引
res = platform_get_resource(pdev,IORESOURCE_MEM,0);
gpfcon = ioremap(res->start,res->end-res->start+1);
gpfdat= gpfcon+1;
res=platform_get_resource(pdev,IORESOURCE_IRQ,0);
pin=res->start;
/* 注册字符设备 */
major = register_chrdev(0,DEVICENAME, &my_dev_fops); // 注册, 告诉内核
myclass = class_create(THIS_MODULE,DEVICENAME);
mydev=device_create(myclass, NULL, MKDEV(major,0), NULL, "led");
printk("led_probe,found led\n");
return 0;
}
static int __devexit led_remove(struct platform_device *pdev)
{
/* 根据platform_dev资源进行iounmap */
/* 注销字符设备 */
iounmap(gpfcon);
device_unregister(mydev);
class_destroy(myclass);
unregister_chrdev(major, DEVICENAME); // 卸载
printk("led_remove,remove led\n");
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");
程序的函数调用堆栈:
- insmod driver.ko
module_init(led_drv_init); // 模块初始化
--->led_drv_init
--->platform_driver_register(&led_drv); // 调用平台设备初始化
--->led_drv的probe()函数,即static int __devinit led_probe(struct platform_device *pdev)函数
---> res = platform_get_resource(pdev,IORESOURCE_MEM,0); //从device.c中的文件获取得到设备相关信息,用于设备的初始化。。。
--->接下来就是字符设备的基本初始化。
- rmmod driver.ko
module_exit(led_drv_exit); // 模块卸载
--->led_drv_exit
--->platform_driver_unregister(&led_drv); // 调用平台设备初始化
--->led_drv的remove()函数,即static int __devinit led_remove(struct platform_device *pdev)函数
---> 字符设备卸载的基本操作。
2.设备 device.c
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/delay.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/partitions.h>
#include <linux/input.h>
#include <linux/module.h>
#include <asm/mach-types.h>
#include <asm/mach/arch.h>
#include <asm/mach/flash.h>
#include <asm/mach/map.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <asm/uaccess.h>
#include <asm/irq.h>
#include <asm/io.h>
/* 分配/设置/注册一个platform_device */
void led_release(struct device *dev)
{
printk("led device release\r\n");
}
static struct resource led_resource[] = {
[0]=
{
.start = 0x56000050,
.end = 0x56000050 + 8 - 1,
.flags = IORESOURCE_MEM,
},
[1]=
{
.start = 4,
.end = 4,
.flags = IORESOURCE_IRQ,
}
};
static struct platform_device led_dev = {
.name = "myled",
.id = -1, // 无此设备
.dev = {
.release = led_release,
},
// 资源数量
.num_resources = ARRAY_SIZE(led_resource),
.resource = led_resource,
};
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");
流程与driver.c的流程类似。
注册设备--->主要是将设备相关信息注册到platform,于是drvice.c可以通过 platform_get_resource()函数获取到led_resource[]中的相关数据。所以需要先insmod device.ko
3.测试APP:
/******************firstdrvtest.c*************/
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
/* firstdrvtest on
* firstdrvtest off
*/
int main(int argc, char **argv)
{
int fd;
int val = 1;
fd = open("/dev/led", O_RDWR);//可通过ls /dev/xyz命令查看
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;
}
这就是老生常谈了,通过open/write函数调用到driver.c中的与之相匹配的my_dev_fops的.open 和 .write函数,而这两个函数的具体实现去操作寄存器,从而控制LED的点亮熄灭。
总结:通过这个例子和APP.c可以感觉出来,platform模型,其实就是一种驱动的工作模式,他完成的功能和之前的led_drive是一样的,只不过之前是设备和驱动是混在一起的,这个就是把设备和驱动完全分离开,可通过一个驱动框架,去适配这一类的所有设备。真的是很妙的思想。
4.Makefile
########################Makefile##########################
KERN_DIR =/home/zion/Desktop/JZ2440+QT/linux-3.4.2/linux-3.4.2 #这里一定要改为与代码匹配的内核的路径,否则会有各种问题
all:
make -C $(KERN_DIR) M=`pwd` modules
clean:
make -C $(KERN_DIR) M=`pwd` modules clean
rm -rf modules.order
obj-m += driver.o device.o#名字要与led_drv.c匹配
5.试验步骤:
- make 生成device.ko和driver.ko
- arm-linux-gcc app.c 生成可执行文件a.out
- insmod device.ko
- insmod driver.ko
下图的打印代表调用了 platform_driver_register(&led_drv);中led_drv->probe()函数
然后测试程序:
- ./a.out on即可看到 led的点亮
- ./a.out on即可看到 led的熄灭
- 通过demsg即可看到写在代码中的printk的打印