字符设备驱动
1 代码
1.1 驱动代码
char_dev.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#define BUFF_SIZE 128
//定义一个宏表示设备名字
#define dev_name "char_device_name"
//定义宏表示设备数量
#define dev_count (1)
//定义字符设备号(普通定义)
static dev_t dev_number;
//定义字符设备结构体
static struct cdev chr_dev;
//数据缓冲区
static char vbuf[BUFF_SIZE];
static int chr_dev_open(struct inode *inode, struct file *filp);
static int chr_dev_release(struct inode *inode, struct file *filp);
static ssize_t chr_dev_write(struct file *filp, const char __user * buf, size_t count, loff_t *ppos);
static ssize_t chr_dev_read(struct file *filp, char __user * buf, size_t count, loff_t *ppos);
static struct file_operations chr_dev_fops =
{
.owner = THIS_MODULE,
.open = chr_dev_open,
.release = chr_dev_release,
.write = chr_dev_write,
.read = chr_dev_read,
};
static int chr_dev_open(struct inode *inode, struct file *filp)
{
printk("\nopen\n");
return 0;
}
static int chr_dev_release(struct inode *inode, struct file *filp)
{
printk("\nrelease\n");
return 0;
}
static ssize_t chr_dev_write(struct file *filp, const char __user * buf, size_t count, loff_t *ppos)
{
unsigned long p = *ppos;
int ret;
int tmp = count ;
if(p > BUFF_SIZE)
return 0;
if(tmp > BUFF_SIZE - p)
tmp = BUFF_SIZE - p;
ret = copy_from_user(vbuf, buf, tmp);
*ppos += tmp;
return tmp;
}
static ssize_t chr_dev_read(struct file *filp, char __user * buf, size_t count, loff_t *ppos)
{
unsigned long p = *ppos;
int ret;
int tmp = count ;
static int i = 0;
i++;
if(p >= BUFF_SIZE)
return 0;
if(tmp > BUFF_SIZE - p)
tmp = BUFF_SIZE - p;
ret = copy_to_user(buf, vbuf+p, tmp);
*ppos +=tmp;
return tmp;
}
static int __init char_device_init(void)
{
int ret = 0;
printk("char_device init sucess!\n");
//采用动态分配的方式,获取主设备编号,次设备号为0,设备名称和设备数量
//设备名称(char_device_name)可通过命令cat /proc/devices查看
ret = alloc_chrdev_region(&dev_number, 0, dev_count, dev_name);
if (ret < 0) {
printk("fail to alloc devno\n");
goto alloc_err;
}
//字符设备结构体cdev与文件操作结构体file_operations关联起来
cdev_init(&chr_dev, &chr_dev_fops);
//添加设备至cdev_map散列表中
ret = cdev_add(&chr_dev, dev_number, dev_count);
if (ret < 0) {
printk("fail to add cdev\n");
goto add_err;
}
return 0;
add_err:
//添加设备失败时,注销设备号
unregister_chrdev_region(dev_number, dev_count);
alloc_err:
return ret;
}
static void __exit char_device_exit(void)
{
printk("char_device exit!\n");
//注销设备号
unregister_chrdev_region(dev_number,dev_count);
//将cdev结构体从内核中移除
cdev_del(&chr_dev);
}
module_init(char_device_init);
module_exit(char_device_exit);
MODULE_LICENSE("GPL");
1.2 应用代码
chardev_test.c代码,用来测试驱动程序。
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
char *wbuf = "Hello World\n";
char rbuf[128];
int main(void)
{
printf("My char_dev test\n");
//打开文件
int fd = open("/dev/chrdev", O_RDWR);
//写入数据
write(fd, wbuf, strlen(wbuf));
//写入完毕,关闭文件
close(fd);
//打开文件
fd = open("/dev/chrdev", O_RDWR);
//读取文件内容
read(fd, rbuf, 128);
//打印读取的内容
printf("The content : %s", rbuf);
//读取完毕,关闭文件
close(fd);
return 0;
}
1.3 Makefile文件
#这里具体要根据当前文件和内核所在目录的位置设定
KERNEL_DIR=../ebf_linux_kernel/build_image/build
ARCH=arm
CROSS_COMPILE=arm-linux-gnueabihf-
export ARCH CROSS_COMPILE
obj-m := chrdev.o
out = chrdev_test
all:
$(MAKE) -C $(KERNEL_DIR) M=$(CURDIR) modules
$(CROSS_COMPILE)gcc -o $(out) chrdev_test.c
.PHONY:clean
clean:
$(MAKE) -C $(KERNEL_DIR) M=$(CURDIR) clean
rm $(out)
2 程序运行结果
2.1 编译文件
使用make命令使用Makefile编译文件
make
将编译好的chrdev_test和chrdev.ko文件,通过NFS移动到开发板
2.2 加载驱动模块
使用命令加载模块,可以看到我们驱动文件编写时设置的打印信息char_device init sucess!
sudo insmod /mnt/chrdev.ko
驱动模块加载成功后,在/proc/devices目录下,会生成一个字符设备char_device_name,主设备号为244。
cat /proc/devices
2.3 创建设备文件
根据注册到内核的设备,可以看到主设备号为244,使用mkmod命令创建设备文件
sudo mknod /dev/chrdev c 244 0
2.4 运行测试程序
运行chrdev_test可执行文件,这里我将chrdev_test文件拷贝到当前目录了。
sudo ./chrdev_test
执行效果如下
2.5 删除内核模块和设备文件
卸载内核模块
sudo rmsmod chrdev.ko
内核模块卸载后,这时候再查看,char_device_name的字符设备就没有了
cat /proc/devices
删除设备文件,这个文件是之前通过mknod命令创建的。
sudo rm /dev/chrdev