使用read和write的函数打开一个设备文件,当write向设备文件中写数据时未完毕时,read经过wait_event_interruptible()判断标志变量condition为假处于可中断休眠状态,等待write写完数据后,更改condition为真再调用wake_up_interruptible()唤醒。
1.实例化字符设备驱动对象 2.部分初始化字符设备驱动对象 3.注册驱动(采用自动创建设备节点) 4.注销
#include "head.h"
#include "cdev.h"
#if 0
unsigned int my_major=0;
#else
unsigned int my_major = 50;
#endif
unsigned int condition =0;
int mycdev_open(struct inode *inode, struct file *file)
{
printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
return 0;
}
ssize_t mycdev_read(struct file *file, char *ubuf, size_t size, loff_t *lof)
{
// 向用户空间读取拷贝
if (size > sizeof(cdev_buf)) // 用户空间期待读取的大小内核满足不了,那就给内核支持的最大大小
size = sizeof(cdev_buf);
//检查condition是否为真,为真往下执行,否则执行可中断休眠
wait_event_interruptible(wq_head,condition);
ret = copy_to_user(ubuf, cdev_buf, size);
if (ret) // 拷贝失败
{
printk("copy_to_user filed\n");
return ret;
}
condition=0;//事件完毕,重置标志
printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
return 0;
}
ssize_t mycdev_write(struct file *file, const char *ubuf, size_t size, loff_t *lof)
{
unsigned long ret;
// 从用户空间读取数据
if (size > sizeof(cdev_buf)) // 用户空间期待读取的大小内核满足不了,那就给内核支持的最大大小
size = sizeof(cdev_buf);
ret = copy_from_user(cdev_buf, ubuf, size);
if (ret) // 拷贝失败
{
printk("copy_to_user filed\n");
return ret;
}
//改变标志
condition=1;
//唤醒可中断进程
wake_up_interruptible(&wq_head);
return 0;
}
long mycdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
return 0;
}
int mycdev_close(struct inode *inode, struct file *file)
{
printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
return 0;
}
int all_led_init(void)
{
return 0;
}
// 定义操作方法结构体变量并赋值
struct file_operations fops = {
.open = mycdev_open,
.read = mycdev_read,
.write = mycdev_write,
.release = mycdev_close,
.unlocked_ioctl = mycdev_ioctl,
};
static int __init mycdev_init(void)
{
int i;
/****初始化区域_begin****/
// 初始化等待队列头
init_waitqueue_head(&wq_head);
/****初始化区域_end****/
/****驱动设备区域_begin****/
// 1.实例化字符设备驱动对象
cdev = cdev_alloc();
if (cdev == NULL)
{
ret = -ENOMEM;
goto OUT1;
}
// 2.部分初始化字符设备驱动对象
cdev_init(cdev, &fops);
// 3.申请设备号
if (my_major == 0) // 动态申请设备号
{
ret = alloc_chrdev_region(&devno, my_minor, 3, "mycdev"); // 成功返回0,失败返回错误码
if (ret)
{
printk("动态申请设备号失败\n");
goto OUT2;
}
printk("动态申请设备号成功\n");
my_major = MAJOR(devno); // 获取主设备号
my_minor = MINOR(devno); // 获取次设备号
}
else // 静态制定设备号
{
// 通过自定义主设备号和次设备号获取设备号
devno = MKDEV(my_major, 0);
ret = register_chrdev_region(devno, 3, "mycdev"); // 成功申请设备号返回0,失败返回错误码
if (ret)
{
printk("静态申请设备号失败\n");
goto OUT2;
}
printk("静态申请设备号成功\n");
}
// 4.将字符设备驱动对象注册进内核
ret = cdev_add(cdev, devno, 3);
if (ret)
{
printk("字符设备驱动注册进内核失败\n");
goto OUT3;
}
printk("字符设备驱动注册进内核成功\n");
// 5.自动创建设备节点
// 向上提交目录
cls = class_create(THIS_MODULE, "mycdev");
if (IS_ERR(cls)) // 为真表示cls指向4K的预留空间
{
printk("向上提交目录失败\n");
ret = -PTR_ERR(cls);
goto OUT4;
}
printk("向上提交目录成功\n");
// 自动创建设备文件
for (i = 0; i < 3; i++)
{
dev = device_create(cls, NULL, MKDEV(my_major, i), NULL, "mycdev%d", i);
if (IS_ERR(dev))
{
printk("自动创建设备文件mycdev%d失败\n", i);
ret = PTR_ERR(dev);
goto OUT5;
}
printk("自动创建设备文件mycdev%d成功\n", i);
}
/****驱动设备区域_end****/
all_led_init();
return 0;
OUT5:
// 释放提交成功的设备节点信息
for (--i; i >= 0; i--)
{
device_destroy(cls, MKDEV(my_major, i));
}
// 销毁目录
class_destroy(cls);
OUT4:
cdev_del(cdev);
OUT3:
unregister_chrdev_region(devno, 3);
OUT2:
kfree(cdev);
OUT1:
return ret;
}
static void __exit mycdev_exit(void)
{
// 销毁设备信息
device_destroy(cls, MKDEV(my_major, 0));
device_destroy(cls, MKDEV(my_major, 1));
device_destroy(cls, MKDEV(my_major, 2));
// 销毁目录
class_destroy(cls);
//注销对象
cdev_del(cdev);
//释放设备号
unregister_chrdev_region(MKDEV(my_major,my_minor),3);
//释放对象的空间
kfree(cdev);
}
module_init(mycdev_init);
module_exit(mycdev_exit);
MODULE_LICENSE("GPL");
采用memset函数清空字符串更安全
#include "user.h"
//从内核的设备文件中读取数据
int main(int argc, char const *argv[])
{
//fd_r=-1;
fd_r=open("/dev/mycdev0",O_RDONLY);//是否阻塞具体看驱动代码,应用层无所谓
if(fd_r<0)
{
printf("%d\n",fd_r);
printf("设备文件mycdev0打开失败\n");
perror("read file:");
return fd_r;
}
printf("设备文件mycdev0打开成功\n");
while(1)
{
memset(user_buf_r,'0',sizeof(user_buf_r));
read(fd_r,user_buf_r,sizeof(user_buf_r));
printf("%s\n",user_buf_r);
}
close(fd_r);
return 0;
}
#include "user.h"
//从内核的设备文件中写入数据
int main(int argc, char const *argv[])
{
fd_w=-1;
fd_w=open("/dev/mycdev0",O_WRONLY);//是否阻塞具体看驱动代码,应用层无所谓
if(fd_w<0)
{
printf("设备文件mycdev0打开失败\n");
return fd_w;
}
printf("设备文件mycdev0打开成功\n");
while(1)
{
memset(user_buf_w,'0',sizeof(user_buf_w));
printf("请输入你想说的话:>>");
scanf("%s",user_buf_w);
user_buf_w[strlen(user_buf_w)-1]='\0';
write(fd_w,user_buf_w,sizeof(user_buf_w));
}
close(fd_w);
return 0;
}