平台总线设备驱动采用了设备信息和设备注册分离的方式,分别放在了device.c和driver.c文件里面,其中设备注册的方式就是杂项设备驱动的方式,详情请看 树莓派驱动开发(四)杂项设备驱动之点亮LED
直接上代码
device.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/ioport.h>
void led_release(struct device *dev)
{
printk("led_release \n");
}
struct resource led_res[] = {
[0] = {
.start = 0xfe200000,
.end = 0xfe200003,
.flags = IORESOURCE_MEM,
.name = "GPIO1_IO4DIR",
},
[1] = {
.start = 0xfe20001c,
.end = 0xfe20001f,
.flags = IORESOURCE_MEM,
.name = "GPIO1_IO4H"
},
[2] = {
.start = 0xfe200028,
.end = 0xfe20002b,
.flags = IORESOURCE_MEM,
.name = "GPIO1_IO4L"
},
};
struct platform_device led_device = {
/* name必须与static struct platform_driver.name的name字段相同 */
.name = "led_test",
.id = -1,
.resource = led_res,
.num_resources = ARRAY_SIZE(led_res),
.dev={
.release = led_release,
},
};
static int device_init(void)
{
/*
注册驱动程序并将其放入由内核维护的驱动程序列表中
以便,每当发现新的匹配时,就可以按需调用其probe()函数
*/
platform_device_register(&led_device);
printk("platform_device_register ok \n");
return 0;
}
static void device_exit(void)
{
platform_device_unregister(&led_device);
printk("goodbye! \n");
}
module_init(device_init);
module_exit(device_exit);
MODULE_LICENSE("GPL");
#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/ioport.h>
#include <linux/miscdevice.h> //注册杂项设备头文件
#include <linux/uaccess.h>
#include <linux/fs.h>
#include <linux/io.h>
unsigned int *vir_gpio4_dr=NULL;
unsigned int *vir_gpio4_h=NULL;
unsigned int *vir_gpio4_l=NULL;
struct resource *gpio4_dir;
struct resource *gpio4_h;
struct resource *gpio4_l;
struct resource *leddir_mem_test;
struct resource *ledh_mem_test;
struct resource *ledl_mem_test;
ssize_t misc_write(struct file *file, const char __user *ubuf, size_t size, loff_t *loff_t)
{
char kbuf[64] = {0};
if ( copy_from_user( kbuf, ubuf, size) != 0)
{
printk( "copy_from_user error\n ");
return -1;
}
printk( "kbuf is %s\n ", kbuf);
*vir_gpio4_dr |= (001<<(3*4));
if( kbuf[0] == 1)
{
*vir_gpio4_h |=(1<<4);
}
else if( kbuf[0]==0)
{
*vir_gpio4_l |=(1<<4);
}
return 0;
}
int misc_release( struct inode *inode, struct file *file)
{
printk( "hello misc_relaease bye bye \n ");
return 0;
}
int misc_open( struct inode *inode, struct file *file)
{
printk( "hello misc_open\n ");
return 0;
}
//文件操作集
struct file_operations misc_fops = {
.owner = THIS_MODULE,
.open = misc_open,
.release = misc_release,
.write = misc_write,
};
//miscdevice 结构体
struct miscdevice misc_dev = {
.minor = MISC_DYNAMIC_MINOR,
.name = "hello_misc",
.fops = &misc_fops,
};
int led_probe( struct platform_device *pdev)
{
int ret;
printk( "led_probe\n");
ret = misc_register( &misc_dev); //注册杂项设备
if (ret < 0)
{
printk( "misc registe is error \n");
}
printk( "misc registe is succeed \n");
gpio4_dir = platform_get_resource( pdev, IORESOURCE_MEM, 0);
gpio4_h = platform_get_resource( pdev, IORESOURCE_MEM, 1);
gpio4_l = platform_get_resource( pdev, IORESOURCE_MEM, 2);
vir_gpio4_dr = ioremap( gpio4_dir->start,4);
if( vir_gpio4_dr== NULL )
{
printk( "gpio4dr ioremap error\n");
return EBUSY;
}
vir_gpio4_h = ioremap( gpio4_h->start,4);
if( vir_gpio4_h== NULL)
{
printk( "gpio4h ioremap error\n");
return EBUSY;
}
vir_gpio4_l = ioremap( gpio4_l->start,4);
if( vir_gpio4_l == NULL)
{
printk( "gpio4l ioremap error\n");
return EBUSY;
}
printk( "gpio ioremap success\n");
return 0;
#if 0
leddir_mem_test = request_mem_region( gpio4_dir->start, gpio4_dir->end - gpio4_dir->start +1, "led_dir");
if( leddir_mem_test == NULL){
printk( "platform_get_resource iserror \n");
goto errdir_region;
}
ledh_mem_test = request_mem_region( gpio4_dir->start, gpio4_dir->end - gpio4_dir->start +1, "led_dir");
if( ledh_mem_test == NULL){
printk( "platform_get_resource iserror \n");
goto errh_region;
}
ledl_mem_test = request_mem_region( gpio4_dir->start, gpio4_dir->end - gpio4_dir->start +1, "led_dir");
if( ledl_mem_test == NULL){
printk( "platform_get_resource iserror \n");
goto errl_region;
}
return 0;
errdir_region:
release_mem_region( gpio4_dir->start, gpio4_dir->end - gpio4_dir->start +1);
return -EBUSY;
errh_region:
release_mem_region( gpio4_h->start, gpio4_h->end - gpio4_h->start +1);
return -EBUSY;
errl_region:
release_mem_region(gpio4_l->start, gpio4_l->end - gpio4_l->start +1);
return -EBUSY;
#endif
}
int led_remove( struct platform_device *pdev)
{
printk("led_remove\n");
return 0;
}
struct platform_driver led_driver ={
.probe = led_probe,
.remove = led_remove,
.driver = {
.owner = THIS_MODULE,
.name = "led_test"
},
};
static int led_driver_init( void)
{
int ret =0;
ret = platform_driver_register( &led_driver);
if( ret<0)
{
printk( "platform_driver_register error \n");
}
printk( "platform_driver_register ok \n");
return 0;
}
static void led_driver_exit(void)
{
misc_deregister( &misc_dev); //卸载杂项设备
printk( "misc gooodbye! \n");
iounmap( vir_gpio4_dr);
iounmap( vir_gpio4_h);
iounmap( vir_gpio4_l);
// platform 驱动卸载
platform_driver_unregister( &led_driver);
printk( "goodbye! \n");
}
module_init( led_driver_init);
module_exit( led_driver_exit);
MODULE_LICENSE( "GPL");
app.c
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc,char *argv[])
{
int fd;
char buf[10];
fd = open("/dev/hello_misc",O_RDWR);//打开设备节点
if(fd < 0)
{
perror("open error \n");
return fd;
}
buf[0]=atoi(argv[1]);
write(fd,buf,sizeof(buf)); //向内核层写数据
close(fd);
return 0;
}
Makefile
# 判断是否在内核构建系统内。如果没有定义 KERNELRELEASE,则表示这是从命令行调用。
ifneq ($(KERNELRELEASE),)
# 如果不是内核构建系统,则定义需要编译的模块对象文件。
# obj-m 是内核模块的编译变量,+= 表示添加模块文件(.o)
obj-m += device.o
obj-m += driver.o
else
# 定义内核头文件的位置,使用当前正在运行的内核版本。
KDIR := /home/interest/linux/lib/modules/$(shell uname -r)/build
# 定义当前的工作目录。
PWD := $(shell pwd)
# 默认目标。如果调用了 make 而没有指定目标,会执行这个部分。
# -C $(KDIR) 表示切换到内核源码目录进行编译
# M=$(PWD) 表示在当前模块的目录下执行内核模块编译
all:
$(MAKE) -C $(KDIR) M=$(PWD) modules
# 清理目标:用于清理编译产生的中间文件。
clean:
rm -f *.mod.c *.order *.ko *.o *.mod *.symvers
endif
编译
Make //编译驱动代码
gcc app.c -o app //编译测试代码
sudo insmod driver.ko //加载驱动模块
sudo ./app 1 //led亮
sudo ./app 0 //led灭
sudo rmmod driver //卸载模块