linux内核字符设备驱动之写操作

来源:互联网 发布:淘宝客流量过大 编辑:程序博客网 时间:2024/05/16 10:49

应用程序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_openstatic 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_closestatic 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_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\n", __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\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_openstatic 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_closestatic 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_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\n", __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 < 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_openstatic 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_closestatic 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_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\n", __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\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;}
原创粉丝点击