linux内核字符设备驱动之写操作
2017-09-15 15:46
471 查看
应用程序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;
切记:对于write接口的第二个参数buf,这个buf指针保存的是用户缓冲区的首地址,在内核空间不能直接访问操作,需要利用内核的内存拷贝函数,将用户数据拷贝到内核空间,这个内存拷贝函数:
unsigned long copy_from_user(void *to, void __user *from, unsigned long n) 作用:将用户缓冲区的数据拷贝到内核缓冲区中 参数: to:目的地址,传递内核缓冲区的首地址 from:源地址,传递用户缓冲区的首地址(buf) n:要拷贝的字节数 将来只要看到__user修饰的指针,就不能在驱动中直接访问操作,必须利用内存拷贝函数!
案例:编写字符设备驱动,提供write接口,将用户空间的数据写入到内核空间
int udata = 0x5555;write(fd, &udata, sizeof(udata));
#include <linux/init.h> #include <linux/module.h> #include <linux/fs.h> //struct file_operations #include <linux/cdev.h> //struct cdev + 设备号 #include <asm/gpio.h> #include <plat/gpio-cfg.h> #include <linux/uaccess.h> //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_open static int led_open(struct inode *inode, struct file *file) { int i; for(i = 0; i < ARRAY_SIZE(led_info); i++) gpio_set_value(led_info[i].gpio, 1); printk("%s\n", __func__); return 0; //执行成功返回0,执行失败返回负值 } //调用关系:应用程序close->...->led_close static int led_close(struct inode *inode, struct file *file) { int i; for(i = 0; i < ARRAY_SIZE(led_info); i++) gpio_set_value(led_info[i].gpio, 0); printk("%s\n", __func__); return 0; //执行成功返回0,执行失败返回负值 } //调用关系:应用程序read->...->led_read static 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\n", __func__); return sizeof(kdata); //失败返回负值,成功返回实际读取的字节数 } //调用关系:应用程序write->...->最终调用led_write static 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\n", __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 < ARRAY_SIZE(led_info); i++) { gpio_set_value(led_info[i].gpio, 0); gpio_free(led_info[i].gpio); } //卸载字符设备对象 cdev_del(&led_cdev); //释放设备号 unregister_chrdev_region(dev, 1); } module_init(led_init); module_exit(led_exit); MODULE_LICENSE("GPL");
#include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> int main(void) { int fd; int udata = 0x5555; //定义用户缓冲区 //打开设备 //open->....->调用led_open fd = open("/dev/myled", O_RDWR); if (fd < 0) { printf("打开设备失败!\n"); return -1; } //write->...->调用led_write write(fd, &udata, sizeof(udata)); //关闭设备 //close->...->调用led_close close(fd); return 0; }
案例:用户写1,开所有的灯;用户写0,关所有的灯;
#include <linux/init.h> #include <linux/module.h> #include <linux/fs.h> //struct file_operations #include <linux/cdev.h> //struct cdev + 设备号 #include <asm/gpio.h> #include <plat/gpio-cfg.h> #include <linux/uaccess.h> //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_open static int led_open(struct inode *inode, struct file *file) { int i; for(i = 0; i < ARRAY_SIZE(led_info); i++) gpio_set_value(led_info[i].gpio, 1); printk("%s\n", __func__); return 0; //执行成功返回0,执行失败返回负值 } //调用关系:应用程序close->...->led_close static int led_close(struct inode *inode, struct file *file) { int i; for(i = 0; i < ARRAY_SIZE(led_info); i++) gpio_set_value(led_info[i].gpio, 0); printk("%s\n", __func__); return 0; //执行成功返回0,执行失败返回负值 } //调用关系:应用程序read->...->led_read static 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\n", __func__); return sizeof(kdata); //失败返回负值,成功返回实际读取的字节数 } //调用关系:应用程序write->...->最终调用led_write static 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 < ARRAY_SIZE(led_info); i++) { gpio_set_value(led_info[i].gpio, 0); gpio_free(led_info[i].gpio); } //卸载字符设备对象 cdev_del(&led_cdev); //释放设备号 unregister_chrdev_region(dev, 1); } module_init(led_init); module_exit(led_exit); MODULE_LICENSE("GPL");
#include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> int main(void) { int fd; int udata; //定义用户缓冲区 //打开设备 //open->....->调用led_open fd = open("/dev/myled", O_RDWR); if (fd < 0) { printf("打开设备失败!\n"); return -1; } //write->...->调用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; }
案例:用户能够指定其中某个灯的开关状态;
提示:
用户不仅仅要告诉灯的开关状态,还要告诉驱动用户现在要想操作哪个灯;#include <linux/init.h> #include <linux/module.h> #include <linux/fs.h> //struct file_operations #include <linux/cdev.h> //struct cdev + 设备号 #include <asm/gpio.h> #include <plat/gpio-cfg.h> #include <linux/uaccess.h> //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_open static int led_open(struct inode *inode, struct file *file) { int i; for(i = 0; i < ARRAY_SIZE(led_info); i++) gpio_set_value(led_info[i].gpio, 1); printk("%s\n", __func__); return 0; //执行成功返回0,执行失败返回负值 } //调用关系:应用程序close->...->led_close static int led_close(struct inode *inode, struct file *file) { int i; for(i = 0; i < ARRAY_SIZE(led_info); i++) gpio_set_value(led_info[i].gpio, 0); printk("%s\n", __func__); return 0; //执行成功返回0,执行失败返回负值 } //调用关系:应用程序read->...->led_read static 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\n", __func__); return sizeof(kdata); //失败返回负值,成功返回实际读取的字节数 } //调用关系:应用程序write->...->最终调用led_write static 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\n", __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 < ARRAY_SIZE(led_info); i++) { gpio_set_value(led_info[i].gpio, 0); gpio_free(led_info[i].gpio); } //卸载字符设备对象 cdev_del(&led_cdev); //释放设备号 unregister_chrdev_region(dev, 1); } module_init(led_init); module_exit(led_exit); MODULE_LICENSE("GPL");
#include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> //声明LED操作的数据结构 struct led_cmd { int index; //指定灯的编号 int cmd; //开关命令 }; int main(void) { int fd; struct led_cmd udata; //定义用户缓冲区 //打开设备 //open->....->调用led_open fd = open("/dev/myled", O_RDWR); if (fd < 0) { printf("打开设备失败!\n"); return -1; } //write->...->调用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; }
相关文章推荐
- Linux设备驱动程式学习(5)-高级字符驱动程式操作[(2)阻塞型I/O和休眠]
- Linux内核开发之简单字符设备驱动(下)
- linux驱动开发之字符设备--内核和用户空间数据的交换(read write)
- linux驱动开发--字符设备:原子操作
- Linux设备驱动程式学习(6)-高级字符驱动程式操作[(3)设备文档的访问控制]
- linux内核之字符设备驱动图解
- Linux字符设备驱动对IO操作有三种方式
- linux 高级字符设备驱动 ioctl操作介绍 例程分析实现
- linux 2.6内核 字符设备驱动 相关函数
- Linux设备驱动之mmap设备操作(memdev.c字符设备驱动分析)
- Linux 内核设备驱动之GPIO驱动之GPIO GPIO字符设备初始化
- linux高级字符设备驱动之 二 内核等待队列
- linux4.10.8 内核移植(四)---字符设备驱动_led驱动程序
- linux驱动开发之字符设备--内核和用户空间数据的交换(ioctl)
- Linux内核开发之简单字符设备驱动(下)
- linux设备驱动开发-高级字符设备操作poll
- 简单的LINUX字符设备驱动及编译进Linux内核…
- linux内核字符设备驱动之发送命令接口
- 07-S3C2440驱动学习(一)嵌入式linux字符设备驱动-按键驱动程序之异步通知机制+原子操作+互斥信号量+阻塞与非阻塞+定时器去抖
- tony之linux driver_LDD3_scull字符设备驱动编译在新内核编译问题