int read(int fd, char *buf,int n);+
从设备fd读取n个字节到空间buf中
ssize_t xxx_read(struct file *filp, char __user *pbuf, size_t count, loff_t *ppos); 完成功能:读取设备产生的数据 参数: filp:指向open产生的struct file类型的对象,表示本次read对应的那次open pbuf:指向用户空间一块内存,用来保存读到的数据 count:用户期望读取的字节数 ppos:对于需要位置指示器控制的设备操作有用,用来指示读取的起始位置,读完后也需要变更位置指示器的指示位置 ssize_t ---实际读取的字节数 size_t count--- 期望读取的字节数 返回值: 本次成功读取的字节数size,失败返回-1
return size;
put_user(x,ptr)
x:char、int类型的简单变量名
unsigned long copy_to_user (void __user * to, const void * from, unsigned long n)
void __user * to 用户空间
const void * from 内核空间
内核空间拷贝到用户空间中去
成功为返回0,失败非0
ssize_t xxx_write (struct file *filp, const char __user *pbuf, size_t count, loff_t *ppos);
完成功能:向设备写入数据
参数:
filp:指向open产生的struct file类型的对象,表示本次write对应的那次open
pbuf:指向用户空间一块内存,用来保存被写的数据
count:用户期望写入的字节数
ppos:对于需要位置指示器控制的设备操作有用,用来指示写入的起始位置,写完后也需要变更位置指示器的指示位置
返回值:
本次成功写入的字节数size,失败返回-1
return size;
get_user(x,ptr)
x:char、int类型的简单变量名
unsigned long copy_from_user (void * to, const void __user * from, unsigned long n)
成功为返回0,失败非0
验证操作步骤:
编写驱动代码mychar.c
vi mychar.c
make生成ko文件
make
insmod内核模块
sudo insmod ./mychar.ko
查阅字符设备用到的设备号(主设备号):
cat /proc/devices | grep 申请设备号时用的名字
cat /proc/devices | grep mychar
创建设备文件(设备节点) :
mknod /dev/??? c 上一步查询到的主设备号 代码中指定初始次设备号
sudo mknod /dev/mydev c 11 0
编写app验证驱动(testmychar_app.c)
将mydev赋予读写权限
sudo chmod a+w /dev/mydev
编译运行app,dmesg命令查看内核打印信息
.tca /dev/mydev
#include
#include
#include
#include
#include
#define BUF_LEN 100
int major = 11;//主设备号
int minor =0;//次设备号
int mychar_num = 1;//次设备数量
struct cdev mydev;
char mydev_buf[BUF_LEN];
int curlen = 0;//100个字节中已经存有的数据
int mychar_open(struct inode *pnode,struct file *pfile)
{
printk("mychar_open is called\n");
return 0;
}
int mychar_close(struct inode *pnode,struct file *pfile)
{
printk("mychar_close is called\n");
return 0;
}
ssize_t mychar_read(struct file *pfile,char __user *puser,size_t count,loff_t *p_pos)
{
int size = 0;
int ret = 0;
if(count >curlen)//如果期望读取的数据大小大于了原本数据大小
{
size = curlen;//读取的数据为被读取数据的大小
}
else
{
size = count;//被读取的数据大小为期望读取的数据大小
}
ret = copy_to_user(puser,mydev_buf,size);
if(ret)
{
printk("copy_to_user failed\n");
return -1;
}
//将没被读取的数据拷贝到初始读取的位置.
memcpy(mydev_buf,mydev_buf + size,curlen - size);
curlen = curlen -size;//被读取后剩余这么多字节
return size;
}
ssize_t mychar_write(struct file *pfile,const char __user *puser,size_t count,loff_t *p_pos)
{
int ret=0;
int size = 0;
if(count>BUF_LEN - curlen)//如果期望写入的数据大小大于100个字节剩余的空间
{
size = BUF_LEN - curlen;
}
else
{
size = count;
}
ret = copy_from_user(mydev_buf,puser,size);
//将内核空间中size大小的数据写入到内核空间mydev_buf为始的地址中去
if(ret)
{
printk("copy_from_user is failed\n");
return -1;
}
curlen = curlen +size;
//mydev_buf中存在的数据大小
return size;
}
struct file_operations myops = {
.owner = THIS_MODULE,
.open = mychar_open,
.release = mychar_close,
.read = mychar_read,
.write = mychar_write,//一定要记得添加上新加入的含函数
};
int __init mychar_init(void)
{
int ret = 0;
dev_t devno = MKDEV(major,minor);//组合成完整的设备号
/*申请设备号*/
ret = register_chrdev_region(devno,mychar_num,"mychar");
if(ret)//ret非0,表示失败
{
ret = alloc_chrdev_region(&devno,minor,mychar_num,"mychar");
//此设备号申请后填写到devno地址中去,从minor开始申请mychar_num个
if(ret)
{
printk("get devno failed\n");
return -1;
}
major = MAJOR(devno);//获取新的设备号,不要遗漏
//次设备号都是0,所以不用再次提取
}
//给struct_cdev对象制定操作函数集
cdev_init(&mydev,&myops);
//将struct_cdev对象添加到内核对应的数据结构里
mydev.owner = THIS_MODULE;
cdev_add(&mydev,devno,1);
return 0;
}
void __exit mychar_exit(void)
{
dev_t devno = MKDEV(major,minor);
cdev_del(&mydev);
unregister_chrdev_region(devno,mychar_num);
}
MODULE_LICENSE("GPL");
module_init(mychar_init);
module_exit(mychar_exit);
testmychar_app.c
#include
#include
#include
#include
#include
#include
int main(int argc, const char *argv[])
{
int fd = -1;
char buf[8] = "";
if(argc<2)
{
printf("the argument is too few\n");
return 1;
}
fd = open(argv[1],O_RDWR);
if(fd<0)
{
printf("open %s failed\n",argv[1]);
return 2;
}
write(fd,"hello",6);
read(fd,buf,8);
printf("buf = %s\n",buf);
close(fd);
fd = -1 ;
return 0;
}