本文是基于韦东山视频的学习笔记
自动创建设备节点mdev
在 最简单的驱动程序 这篇文章里,设备号是自己确定的,如果要用驱动程序,还需要自己创建一个设备节点/dev/xxx
,才能open("/dev/xxx", O_RDWR)
。如果我们让系统自己去创建,岂不美哉。
首先把111
改成0
,系统将自动分配设备号。
/* 初始化(注册)驱动 */
static int __init first_drv_init(void)
{
major = register_chrdev(0, "first_drv", &first_drv);
return 0;
}
/* 卸载驱动 */
static void __exit first_drv_exit(void)
{
unregister_chrdev(major, "first_drv");
}
使用mdev的话,需要创建一个类,在这个类下面创建一个设备。
static struct class *first_class;
static struct class_device *first_drv_class;
...
static int __init first_drv_init(void)
{
first_class = class_create(THIS_MODULE, "first_drv");
first_drv_class = class_device_create(first_class, NULL, MKDEV(major, 0), NULL, "first_drv"); /* /dev/hello */
...
}
...
static void __exit first_drv_exit(void)
{
...
class_device_unregister(first_drv_class);
class_destroy(first_class);
...
}
测试程序也得把/dev/xxx
改成 /dev/first_drv
,和刚刚创建的设备名对应。
...
fd = open("/dev/xxx", O_RDWR);
...
led驱动程序
前文提到用户怎么控制开发板上的led灯的呢?
用户控制应用程序,应用程序调用应用程序里的
open、read、write
函数,通过库函数发出swi异常指令,再调用驱动程序里面的open、read、write
等函数去操作硬件设备led。
那么我们就在驱动程序里的open
函数做硬件初始化
int first_drv_open (struct inode *node, struct file *file)
{
printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
*pGPFCON &= ~((3<<8)| (3<<10) | (3<<12)); /* 把GPFCON 8~13位清零 */
*pGPFCON |= ((1<<8)| (1<<10) | (1<<12)); /* 把GPFCON 9、11、13位置1,即设置为输出模式*/
return 0;
}
在驱动程序里的write
函数做相应的操作,用copy_from_user
函数读取应用程序传过来的参数。
ssize_t first_drv_write(struct file *file, const char __user *buf, size_t size, loff_t *offset)
{
int val;
printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
copy_from_user(&val, buf, size); //把用户空间的数据保存到kernel_buf
if (val == 1)
{
/* leds on */
*pGPFDAT = ~((1<<4)| (1<<5) | (1<<6));
}
else if (val == 0)
{
/* leds off */
*pGPFDAT = (1<<4)| (1<<5) | (1<<6);
}
return 0;
}
应用程序
int main(int argc, char **argv)
{
int val = 1;
int fd = 0;
fd = open("/dev/xxx", O_RDWR);
if (fd<0) printf("can't open file first_drv\n");
write(fd, &val, 4);
if (argc!=2)
{
printf("Usage: first_drv <on/off>\n");
}
else
{
if (strcmp(argv[1], "on") == 0)
{
val = 1;
}
else
{
val = 0;
}
write(fd, &val, 4);
}
return 0;
}
总结
附上完整代码
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>
#include <linux/mm.h>
#include <linux/highmem.h>
#include <linux/blkdev.h>
#include <linux/module.h>
#include <linux/backing-dev.h>
#include <linux/interrupt.h>
#include <asm/uaccess.h>
unsigned int *pGPFCON = NULL; //指针变量pGPFCON设置为地址0x56000050
unsigned int *pGPFDAT = NULL; //指针变量pGPFDAT设置为地址0x56000054
static int major = 0;
static struct class *first_class;
static struct class_device *first_drv_class;
ssize_t first_drv_write(struct file *file, const char __user *buf, size_t size, loff_t *offset)
{
int val;
printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
copy_from_user(&val, buf, size); //把用户空间的数据保存到kernel_buf
if (val == 1)
{
/* leds on */
*pGPFDAT = ~((1<<4)| (1<<5) | (1<<6));
}
else if (val == 0)
{
/* leds off */
*pGPFDAT = (1<<4)| (1<<5) | (1<<6);
}
return 0;
}
int first_drv_open (struct inode *node, struct file *file)
{
printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
*pGPFCON &= ~((3<<8)| (3<<10) | (3<<12)); /* 把GPFCON 8~13位清零 */
*pGPFCON |= ((1<<8)| (1<<10) | (1<<12)); /* 把GPFCON 9、11、13位置1,即设置为输出模式*/
return 0;
}
/* 定义结构体,传参给内核 */
static const struct file_operations first_drv = {
.owner = THIS_MODULE,
.open = first_drv_open,
.write = first_drv_write,
};
/* 初始化(注册)驱动 */
static int __init first_drv_init(void)
{
// int err;
major = register_chrdev(0, "first_drv", &first_drv);
first_class = class_create(THIS_MODULE, "first_drv");
first_drv_class = class_device_create(first_class, NULL, MKDEV(major, 0), NULL, "first_drv"); /* /dev/hello */
pGPFCON = (unsigned int *)ioremap(0x56000050, 16);
pGPFDAT = pGPFCON + 1;
return 0;
}
/* 卸载驱动 */
static void __exit first_drv_exit(void)
{
printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
class_device_unregister(first_drv_class);
class_destroy(first_class);
unregister_chrdev(major, "first_drv");
iounmap(pGPFCON);
}
/* 7. 其他完善:提供设备信息,自动创建设备节点 */
module_init(first_drv_init);
module_exit(first_drv_exit);
MODULE_LICENSE("GPL");