应用程序write函数的使用:
char *p = “hello,world”;
write(fd, p, 12); //将数据写入到设备
底层驱动write接口
struct file_operations { ssize_t (*write) (struct file *file, const char __user *buf, size_t count, loff_t *ppos);};write接口作用:用于写设备,将数据写入到设备中与应用程序write的调用关系:应用程序调用write->...->调用驱动write接口参数:file:文件指针buf:保存用户缓冲区的首地址(p),在驱动程序中不能直接访问这个buf,如果驱动程序要向从用户空间将数据从buf拷贝到内核空间,必须利用内核提供的内存拷贝函数count:用户要写入的字节数,例如12字节 ppos:保存写的位置信息,例如 获取上一次的写位置: loff_t pos = *ppos; 假如这次成功写了12字节; 最后要更新写位置信息: *ppos = pos + 12;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
切记:对于write接口的第二个参数buf,这个buf指针保存的是用户缓冲区的首地址,在内核空间不能直接访问操作,需要利用内核的内存拷贝函数,将用户数据拷贝到内核空间,这个内存拷贝函数:
unsigned long copy_from_user(void *to, void __user *from, unsigned long n)作用:将用户缓冲区的数据拷贝到内核缓冲区中参数:to:目的地址,传递内核缓冲区的首地址from:源地址,传递用户缓冲区的首地址(buf)n:要拷贝的字节数将来只要看到__user修饰的指针,就不能在驱动中直接访问操作,必须利用内存拷贝函数!
1
2
3
4
5
6
7
8
9
案例:编写字符设备驱动,提供write接口,将用户空间的数据写入到内核空间
int udata = 0x5555; write(fd, &udata, sizeof(udata));
#include #include #include //struct file_operations#include //struct cdev + 设备号#include #include #include //copy_to_user//声明描述led硬件相关的数据结构struct led_resource { char *name; int gpio;};//定义初始化led硬件信息static struct led_resource led_info[] = { [0] = { .name = led1, .gpio = s5pv210_gpc0(3) }, [1] = { .name = led2, .gpio = s5pv210_gpc0(4) }};//定义设备号static dev_t dev;//定义字符设备对象static struct cdev led_cdev;//调用关系:应用程序open->....->led_openstatic int led_open(struct inode *inode, struct file *file){ int i; for(i = 0; i ...->led_closestatic int led_close(struct inode *inode, struct file *file){ int i; for(i = 0; i ...->led_readstatic ssize_t led_read(struct file *file, char __user *buf, size_t count, loff_t *ppos){ //定义初始化内核缓冲区(存储空间再后1g虚拟内存中) int kdata = 0x5555; //将内核数据上报给用户 //切记:buf虽然保存的用户缓冲区的首地址,但不能直接访问 //*(int *)buf = kdata;错误 copy_to_user(buf, &kdata, sizeof(kdata)); printk(%s, __func__); return sizeof(kdata); //失败返回负值,成功返回实际读取的字节数}//调用关系:应用程序write->...->最终调用led_writestatic ssize_t led_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos){ //定义内核缓冲区 int kdata; //拷贝用户数据到内核 copy_from_user(&kdata, buf, sizeof(kdata)); printk(%s:从用户写入的数据 kdata = %#x, __func__, kdata); return count; //失败返回负值,成功返回写入的字节数}//定义初始化硬件操作方法static struct file_operations led_fops = { .owner = this_module, .open = led_open, //打开设备 .release = led_close, //关闭设备 .read = led_read, //读取设备 .write = led_write //写设备};static int led_init(void){ int i; //申请设备号 alloc_chrdev_region(&dev, 0, 1, tarena); //初始化字符设备对象 cdev_init(&led_cdev, &led_fops); //注册字符设备对象到内核 cdev_add(&led_cdev, dev, 1); //申请gpio资源和配置gpio为输出口,输出0(省电) for (i = 0; i < array_size(led_info); i++) { gpio_request(led_info[i].gpio, led_info[i].name); gpio_direction_output(led_info[i].gpio, 0); } return 0;}static void led_exit(void){ int i; //输出0,释放gpio资源 for (i = 0; i ....->调用led_open fd = open(/dev/myled, o_rdwr); if (fd ...->调用led_write write(fd, &udata, sizeof(udata)); //关闭设备 //close->...->调用led_close close(fd); return 0;}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
案例:用户写1,开所有的灯;用户写0,关所有的灯;
#include #include #include //struct file_operations#include //struct cdev + 设备号#include #include #include //copy_to_user//声明描述led硬件相关的数据结构struct led_resource { char *name; int gpio;};//定义初始化led硬件信息static struct led_resource led_info[] = { [0] = { .name = led1, .gpio = s5pv210_gpc0(3) }, [1] = { .name = led2, .gpio = s5pv210_gpc0(4) }};//定义设备号static dev_t dev;//定义字符设备对象static struct cdev led_cdev;//调用关系:应用程序open->....->led_openstatic int led_open(struct inode *inode, struct file *file){ int i; for(i = 0; i ...->led_closestatic int led_close(struct inode *inode, struct file *file){ int i; for(i = 0; i ...->led_readstatic ssize_t led_read(struct file *file, char __user *buf, size_t count, loff_t *ppos){ //定义初始化内核缓冲区(存储空间再后1g虚拟内存中) int kdata = 0x5555; //将内核数据上报给用户 //切记:buf虽然保存的用户缓冲区的首地址,但不能直接访问 //*(int *)buf = kdata;错误 copy_to_user(buf, &kdata, sizeof(kdata)); printk(%s, __func__); return sizeof(kdata); //失败返回负值,成功返回实际读取的字节数}//调用关系:应用程序write->...->最终调用led_writestatic ssize_t led_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos){ int i; //定义内核缓冲区 int kdata; //拷贝用户数据到内核 copy_from_user(&kdata, buf, sizeof(kdata)); //开或者关灯 for (i = 0; i < array_size(led_info); i++) gpio_set_value(led_info[i].gpio, kdata); return count; //失败返回负值,成功返回写入的字节数}//定义初始化硬件操作方法static struct file_operations led_fops = { .owner = this_module, .open = led_open, //打开设备 .release = led_close, //关闭设备 .read = led_read, //读取设备 .write = led_write //写设备};static int led_init(void){ int i; //申请设备号 alloc_chrdev_region(&dev, 0, 1, tarena); //初始化字符设备对象 cdev_init(&led_cdev, &led_fops); //注册字符设备对象到内核 cdev_add(&led_cdev, dev, 1); //申请gpio资源和配置gpio为输出口,输出0(省电) for (i = 0; i < array_size(led_info); i++) { gpio_request(led_info[i].gpio, led_info[i].name); gpio_direction_output(led_info[i].gpio, 0); } return 0;}static void led_exit(void){ int i; //输出0,释放gpio资源 for (i = 0; i ....->调用led_open fd = open(/dev/myled, o_rdwr); if (fd ...->调用led_write while (1) { udata = 1; //开 write(fd, &udata, sizeof(udata)); sleep(1); udata = 0; //关 write(fd, &udata, sizeof(udata)); sleep(1); } //关闭设备 //close->...->调用led_close close(fd); return 0;}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
案例:用户能够指定其中某个灯的开关状态;
提示:
用户不仅仅要告诉灯的开关状态,还要告诉驱动用户现在要想操作哪个灯;
#include #include #include //struct file_operations#include //struct cdev + 设备号#include #include #include //copy_to_user//声明led操作的数据结构struct led_cmd { int index; int cmd;};//声明描述led硬件相关的数据结构struct led_resource { char *name; int gpio;};//定义初始化led硬件信息static struct led_resource led_info[] = { [0] = { .name = led1, .gpio = s5pv210_gpc0(3) }, [1] = { .name = led2, .gpio = s5pv210_gpc0(4) }};//定义设备号static dev_t dev;//定义字符设备对象static struct cdev led_cdev;//调用关系:应用程序open->....->led_openstatic int led_open(struct inode *inode, struct file *file){ int i; for(i = 0; i ...->led_closestatic int led_close(struct inode *inode, struct file *file){ int i; for(i = 0; i ...->led_readstatic ssize_t led_read(struct file *file, char __user *buf, size_t count, loff_t *ppos){ //定义初始化内核缓冲区(存储空间再后1g虚拟内存中) int kdata = 0x5555; //将内核数据上报给用户 //切记:buf虽然保存的用户缓冲区的首地址,但不能直接访问 //*(int *)buf = kdata;错误 copy_to_user(buf, &kdata, sizeof(kdata)); printk(%s, __func__); return sizeof(kdata); //失败返回负值,成功返回实际读取的字节数}//调用关系:应用程序write->...->最终调用led_writestatic ssize_t led_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos){ //定义内核缓冲区 struct led_cmd kdata; //拷贝用户数据到内核 copy_from_user(&kdata, buf, sizeof(kdata)); //开或者关灯 gpio_set_value(led_info[kdata.index - 1].gpio, kdata.cmd); printk(%s:第%d灯被%s, __func__, kdata.index, kdata.cmd?打开:关闭); return count; //失败返回负值,成功返回写入的字节数}//定义初始化硬件操作方法static struct file_operations led_fops = { .owner = this_module, .open = led_open, //打开设备 .release = led_close, //关闭设备 .read = led_read, //读取设备 .write = led_write //写设备};static int led_init(void){ int i; //申请设备号 alloc_chrdev_region(&dev, 0, 1, tarena); //初始化字符设备对象 cdev_init(&led_cdev, &led_fops); //注册字符设备对象到内核 cdev_add(&led_cdev, dev, 1); //申请gpio资源和配置gpio为输出口,输出0(省电) for (i = 0; i < array_size(led_info); i++) { gpio_request(led_info[i].gpio, led_info[i].name); gpio_direction_output(led_info[i].gpio, 0); } return 0;}static void led_exit(void){ int i; //输出0,释放gpio资源 for (i = 0; i ....->调用led_open fd = open(/dev/myled, o_rdwr); if (fd ...->调用led_write while (1) { udata.index = 1; //第一个灯 udata.cmd = 1; //开 write(fd, &udata, sizeof(udata)); sleep(1); udata.index = 2; //第二个灯 write(fd, &udata, sizeof(udata)); sleep(1); } //关闭设备 //close->...->调用led_close close(fd); return 0;}
工业以太网串口网关ES-301A/B的特点以及技术特性
数据科学家和数据工程师能合二为一吗?
Nordic nRF9160 SiP LTE-M/NB-IoT模块成功通过一系列主要资格和认证
为什么IT系统更换甚至比空中换引擎还要危险?
中软国际与华为在GITEX Global 2023签署合作备忘录,加速海外业务布局
你知道Linux内核字符设备驱动的写操作?
三相调压器原理 三相调压器接线方法
油田财务机器人如何来降本
江苏润石车规级芯片通过AEC-Q100 Grade1 满足MSL 1湿敏等级认证
你以为你了解的USB就是你了解的USB吗?
涡街流量计安装注意事项
vivo新机获得3C认证,支持120W快充,骁龙888旗舰
一文看懂电源噪声滤波器的基本原理与应用方法
南方电网松山湖智慧能源汽车体验中心正式落成
2分钟快速教你如何在华为模拟器ensp上进行抓包?
RFID标签可用于乳腺癌治疗,你敢信?
嵌入式软件工程师的发展路径
魅族MX7最新消息:魅族MX7将是梦想系列最后一代!骁龙845走高端价格4000+?
在Linux操作系统上实现无线接入点AP的设计
为什么开过SUV的人都换轿车了?