主页 > IT业界  > 

字符设备驱动

字符设备驱动

能够以字节流的形式进行访问的设备是字符设备,编写的对字符设备进行驱动的驱动顺序叫做字符设备驱动

用户层:open(设备文件)


操作方法:

open 方法                                                                                                           当字符设备驱动注册到内核时,会从内核申请到一个设备号(32位)一个                                                                                                                        设备有两部分组成主设备号和次设备号主设备号占据高12位次设备号:标识                                                                                                                        一类设备中的一个具体的设备,占据设备号的低20位

read 方法                                                                         

write方法


编写基本的设备驱动需要解决的问题

如何注册字符设备驱动

驱动中的操作方法和管理如何实现

如何创建设备文件

应用程序中的如何回调驱动中的操作方法

如何将硬件的物理内存映射到虚拟内存

用户和内核之间如何进行数据传递 

int register_chdev(unsigned int maior,const char *name,const struct file_operations *fops ) 功能:进行字符设备驱动的注册,申请了256个设备号(0-255) 参数 : major: >0 手动指定当前驱动的主设备号 ==0 由系统内核动态分配的主设备号 name:设备名或驱动名 fops:操作方法对象指针 void unregister_cherdev(unsigned int major,const char *name) 功能:进行字符设备的驱动的注销 参数Lmajor 祖册的时候得到的设备号 name :注册填写时候的设备名

字符设备驱动注册的测试案例

填写字符设备的驱动代码

创建一个驱动程序的mycdev.c 在其中的编写如下内容

#include <linux/init.h> #include <linux/module.h> #include <linux/fs.h> unsigned int major; 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 __user *ubuf,size_t ,loft_t *lft) { pprintk("%s,%s:%d\n",__FILE_,__FUNC_,_LINE_); return 0; } ssize_t mycdev_write(struct file *file, const char __user *ubunf, size_t size, loff_t *lft) { printk("%s:%s:%d\n",__FILE__,__func__,__LINE__); return 0; } int mycdev_close(struct inode *inode, struct file *file) { printk("%s:%s:%d\n",__FILE__,__func__,__LINE__); return 0; } //定义一个操作方法结构体变量 struct file_operations fops={ .open=mycdev_open, .read=mycdev_read, .write=mycdev_write, .release=mycdev_close, }; static int __init mycdev_init(void) { //进行字符设备驱动的注册 major=register_chrdev(0,"mycdev",&fops); if(major<0) { printk("字符设备驱动注册失败\n"); } printk("字符设备驱动注册成功 major=%d\n",major); return 0; } static void __exit mycdev_exit(void) { //进行字符设备驱动的注销 unregister_chrdev(major,"mycdev"); } module_init(mycdev_init); module_exit(mycdev_exit); MODULE_LICENSE("GPL");

安装驱动的镜像,查看/proc/devices文件中,可以发现驱动被注册

为驱动手动创建一个设备文件

创建的命令:

mknod 设备文件路径+名字 设备文件类型(c/b) 主设备号 ,次设备号

编写一个应用程序,在应用程序中操作设备文件,执行查看对应的操作是否被回调

#include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> int main(int argc, char const *argv[]) { int fd=open("/dev/mycdev",O_RDWR); if(fd<0) { printf("设备文件打开失败"); return fd; } char buf[128]={}; while(1) { write(fd,buf,sizeof(buf)); sleep(1); } close(fd); return 0; }

4 用户和内核之间的数据传递

内核和用户进行数据传递的api

#include <linux /uaccess.h> unsigned long copy_to_user(void _user *to,const void *from,unsigned long n) 功能:从内核传递一个数据给用户进程 参数 :to 用户空间保存数据buf的首地址 from: 内核空间保存数据的buf首地址 n :实际传递的数据大小 返回值 :成功返回0,失败返回没有传递成功的数据大小 unsigned long copy_from_user(void _user *to,const void *from,unsigned long n) 功能:用户传递数据给内核空间 参数: to :内核空间保存数据的buf首地址 from:用户空间保存buf的首地址 返回值 :成功返回0,失败返回没有传递成功的数据的大小 ssize_t mycdev_read(struct *file ,char _user *ubuf,size_t size,loft_t *lft) { unsigned long ret=copy_to_user(ubuf,kbuf,size); if(ret) { printk("copy_to_user filad\n" ); return ret; } return 0; } ssize_t mycdev_write(struct *file,const char _user*ubuf,size_t size,loff_t*lft) { unsigned long ret=copy_from_user(kbuf,ubuf,size); if(ret) { printf("copy_from_user filad\n"); return ret; } return 0; } int main(int argc ,char const *argv[]) { int fd=open("/dev/mycdev",O_RDWR); if(fd<0) { printf("设备文件打开失败\n"); return fd; } char buf[128]="hello world"; write(fd,buf,sizeof(buf)); memset(buf,0,sizeof(buf))); read(fd,buf,sizeof(buf)); printf("buf:%s\n",buf); close(fd); }

物理内存的内存映射API

想要完成硬件的控制直接操作硬件的寄存器,硬件的寄存器属于物理内存,我们的驱动程序会加载到虚拟内存中,我们需要寄存器的物理内存映射成虚拟内存才能完成设备的控制

void *ioremap(phys——addr_t paddr,unsigned long size) 功能将指定大小的物理内存映射为虚拟内存 参数: paddr :要映射的物理内存首地址数值 size :要映射的物理内存的大小 返回值 成功返回映射的虚拟内存的首地址 失败返回NULL; void ioumap(const void __iomem *addr) 功能:取消物理内存的映射 参数: addr :物理内存对应的首地址 返回值 :无

LED1 ->PE10

LED2->PF10

LED3->PE8

要对LED1进行控制

将PE10设置为输出模式 GPIOE_MODER[21:20]->01 0X50006000

PE10输出高低电平 GPIOE_ODR[10]  1输出高电平 

rcc_mp_ahb4enster [4]->1 使能GPIOE外设时钟 050000A28

ssize_t myled_write(struct file *file, const char __user *ubuf, size_t size, loff_t *lft) { //printk("%s:%s:%d\n",__FILE__,__func__,__LINE__); unsigned long ret= copy_from_user(kbuf,ubuf,size);//读取用户写入的数据 if(ret) { printk("copy from user filad\n"); return ret; } if(kbuf[0]=='1') { (*vir_odr)|=(0x1<<10); } else if(kbuf[0]=='0') { (*vir_odr) &=(~(0X1)<<10); } return 0; static int __init mycdev_init(void) { major =register_chrdev(0,"myled",&fops); if(major<0) { printk("字符设备驱动祖册失败\n"); } printk("字符设备驱动注册成功 major=%d\n",major); vir_moder=ioremap(PHY_GPIOE_MODER,4); if(vir_moder ==NULL) { printk("物理内存映射失败% ") } }

标签:

字符设备驱动由讯客互联IT业界栏目发布,感谢您对讯客互联的认可,以及对我们原创作品以及文章的青睐,非常欢迎各位朋友分享到个人网站或者朋友圈,但转载请说明文章出处“字符设备驱动