基于tiny4412开发板LED灯驱动标准的llseek函数写法
来源:互联网 发布:淘宝u站在哪里进入 编辑:程序博客网 时间:2024/05/21 17:55
简介
我们写的驱动,有时候虽然也调用相关read
write
等函数,但是我们在上层操作时,可能我们写的并不符合标准API
函数接口,因此我们需要将我们的驱动函数进行标准化处理。
lseek用户空间驱动原型
off_t lseek(int fd, off_t offset, int whence);
参数:
fd:文件描述符
offset:偏移量,可证可负
whence:偏移位置,有几个重要的宏如下:
SEEK_SET:以文件初始位置为起点偏移offset字节.
SEEK_CUR :以文件当前位置为起点偏移offset字节.
SEEK_END:以文件结束位置为起点偏移offset字节.
返回值:
成功:移动文件后文件指针(以0为参考点)
失败:返回 -1,并且设置全局变量errno值为对应的错误码(来源于驱动)
驱动中对应的函数模型
loff_t (*llseek) (struct file *, loff_t, int);
实现的功能:
成功时返回当前文件指针位置,失败返回负数(错误码),应用程序中的errno值就等于错误驱动返回的错误码,只要是驱动中返回的是负数,应用程序lseek中返回都是-1.
和用户空间的lseek参数对应关系:
用户空间lseek中的fd:间接对应驱动的 file结构指针
offset: 直接对应驱动中offset参数
whence:直接对应驱动中whence参数
要解决的问题:驱动中的函数原型并没有一个指针文件读写位置的指针,如何修改
学习一个结构file:
fs.h \linux-3.5\include\linux
struct file { /* * fu_list becomes invalid after file_free is called and queued via * fu_rcuhead for RCU freeing */ union { struct list_head fu_list; struct rcu_head fu_rcuhead; } f_u; struct path f_path;#define f_dentry f_path.dentry#define f_vfsmnt f_path.mnt const struct file_operations *f_op; /* * Protects f_ep_links, f_flags, f_pos vs i_size in lseek SEEK_CUR. * Must not be taken from IRQ context. */ spinlock_t f_lock;#ifdef CONFIG_SMP int f_sb_list_cpu;#endif atomic_long_t f_count; unsigned int f_flags; fmode_t f_mode; loff_t f_pos; struct fown_struct f_owner; const struct cred *f_cred; struct file_ra_state f_ra; u64 f_version;#ifdef CONFIG_SECURITY void *f_security;#endif /* needed for tty driver, and maybe others */ void *private_data;#ifdef CONFIG_EPOLL /* Used by fs/eventpoll.c to link all the hooks to this file */ struct list_head f_ep_links; struct list_head f_tfile_llink;#endif /* #ifdef CONFIG_EPOLL */ struct address_space *f_mapping;#ifdef CONFIG_DEBUG_WRITECOUNT unsigned long f_mnt_write_state;#endif};
成员有很多,我们需要关注的是那些成员呢:
struct file {············· const struct file_operations *f_op; unsigned int f_flags; fmode_t f_mode; loff_t f_pos; //存放单钱读写位置的变量 struct fown_struct f_owner;········};
当应用程序,每调用一次open函数时,内核就会创建一个struct file
结构体,一个应用程序可以打开一个文件多次,这样内核会有多个struct file
结构体,每个结构的变量都是独立的各不相干,(例如将一个txt文件代开多次,鼠标放在不同位置,每个位置都不一样,互不影响)。那么这个file结构在哪产生的呢?我们会发现驱动中open函数原型中有一个参数struct file
,这就是open
函数对应的文件结构体。当一个文件被打开多次,文件结构怎么区分呢?很简单那就靠fd的不同了。 int (*open) (struct inode *, struct file *);
获取文件当前指针,就可以通过file
结构体 loff_t f_pos;
成员来获取
我们的任务
设计驱动程序,满足API的功能
驱动代码
loff_t leddriver_llseek(struct file *pfile, loff_t loft, int whence){ loff_t tmp;unsigned char LED_NUM=4;switch(whence){case SEEK_SET: tmp=loft; break;case SEEK_CUR: tmp=pfile->f_pos+loft; //当前位置加上调整值 break;case SEEK_END: tmp=LED_NUM+loft; break;default:return -EINVAL;//告诉程序具体错误原因 参数无效break;}//检测最后的结果是否合法if(tmp<0 || tmp>LED_NUM){return -EINVAL;}//更新文件调整后的结果到文件结构体中pfile->f_pos=tmp;//返回调整后的结果return tmp;}
完整代码
#include<linux/kernel.h>#include<linux/module.h>#include<linux/init.h>#include<asm/io.h>#include<asm/uaccess.h>#include<linux/fs.h>#include<linux/cdev.h>#include<linux/kdev_t.h>#include<linux/slab.h>#include<linux/device.h> //增加自动创建设备头文件#include<linux/uaccess.h>//定义字符设备结构体static struct cdev *leddriver_cdev;//定义设备号(包含主次)static dev_t leddriver_num=0;//定义设备类static struct class *leddriver_class;//定义设备结构体static struct device *leddriver_device;//定义错误返回类型static int err;//定义设备名称#define LEDDRIVER_NAME "myled"#define GPM4CON_ADDR 0x110002E0 #define GPM4DAT_ADDR 0X110002E4static volatile unsigned long *gpm4con=NULL; static volatile unsigned long *gpm4dat=NULL;#define GPM4CON *gpm4con#define GPM4DAT *gpm4datssize_t leddriver_read(struct file *file, char __user *usr, size_t size, loff_t *loft){ loff_t cur_pos=*loft;//取出当前读写位置值 unsigned char led_statue[10],i,LED_NUM=4; //读取数据长度为0什么也不做 返回0 退出程序的执行 if(size<=0) { return 0; } //读取位置在末尾 无论size是多少都不能读出数据 数据有效区域越界 if(cur_pos>=LED_NUM) { return 0; } //判断size+当前位置是大于文件大小,只读取有效位的内容 if(cur_pos+size>LED_NUM) { size=LED_NUM-cur_pos; } for(i=0;i<LED_NUM;i++) { if(GPM4DAT &(1<<i)) led_statue[i]=1; else led_statue[i]=0; } if(copy_to_user(usr,&led_statue[cur_pos],size)){ printk("copy to user err\r\n"); return -EFAULT; }; //指针重新定位当前位置 *loft+=size; return size;}ssize_t leddriver_write (struct file *file, const char __user *usr, size_t size, loff_t *loft){ loff_t cur_pos=*loft; unsigned char led_statue[10],i,LED_NUM=4; //写入数据大小为0 什么也不操作返回0退出 if(size<=0) { return 0; } //当前位置大于等于文件最大有效数据,即使写入数据也是无效,所依不进行操作 返回0退出 if(cur_pos>=LED_NUM) { return 0; } //如果当前位置加上所要读取数据的长度大于剩余有效位 只读取有效数据位的数值 if(size+cur_pos>LED_NUM) { size=LED_NUM-cur_pos; } if(copy_from_user(&led_statue[cur_pos],usr,size)) { printk("copy from user err\r\n"); return -EFAULT; } for(i=0;i<size;i++) { if(led_statue[i+cur_pos]==0) GPM4DAT &= ~(1<<(i+cur_pos)); else GPM4DAT |= (1<<(i+cur_pos)); } *loft+=size; return size;}int leddriver_open (struct inode *node, struct file *pfile){ printk("files open is success\r\n"); return 0;}loff_t leddriver_llseek(struct file *pfile, loff_t loft, int whence){ loff_t tmp;unsigned char LED_NUM=4;switch(whence){case SEEK_SET: tmp=loft; break;case SEEK_CUR: tmp=pfile->f_pos+loft; //当前位置加上调整值 break;case SEEK_END: tmp=LED_NUM+loft; break;default:return -EINVAL;//告诉程序具体错误原因 参数无效break;}//检测最后的结果是否合法if(tmp<0 || tmp>LED_NUM){return -EINVAL;}//更新文件调整后的结果到文件结构体中pfile->f_pos=tmp;//返回调整后的结果return tmp;}int leddriver_release (struct inode *node, struct file *file){ printk("leddriver close is success\r\n"); return 0;}//文件操作函数结构体static struct file_operations leddriver_fops={ .owner=THIS_MODULE, .open=leddriver_open, .release=leddriver_release, .read=leddriver_read, .write=leddriver_write, .llseek=leddriver_llseek,};static __init int ldedriver_init(void){//分配字符设备结构体,前面只是定义没有分配空间leddriver_cdev=cdev_alloc();//判断分配成功与否if(leddriver_cdev==NULL){ err=-ENOMEM; printk("leddriver alloc is err\r\n"); goto err_leddriver_alloc;}//动态分配设备号err=alloc_chrdev_region(&leddriver_num, 0, 1, LEDDRIVER_NAME);//错误判断if(err<0){ printk("alloc leddriver num is err\r\n"); goto err_alloc_chrdev_region;}//初始化结构体cdev_init(leddriver_cdev,&leddriver_fops);//驱动注册err=cdev_add(leddriver_cdev,leddriver_num,1);if(err<0){ printk("cdev add is err\r\n"); goto err_cdev_add;}//创建设备类leddriver_class=class_create(THIS_MODULE,"led_class"); err=PTR_ERR(leddriver_class); if(IS_ERR(leddriver_class)) {printk("leddriver creat class is err\r\n");goto err_class_create; }//创建设备 leddriver_device=device_create(leddriver_class,NULL, leddriver_num,NULL, "leddevice"); err=PTR_ERR(leddriver_device); if(IS_ERR(leddriver_device)) {printk("leddriver device creat is err \r\n");goto err_device_create; }//led灯寄存器配置 gpm4con=ioremap(GPM4CON_ADDR, 4); gpm4dat=ioremap(GPM4DAT_ADDR, 4); GPM4CON &= ~(0XFFFF<<0); GPM4CON |= (0x1111<<0); GPM4DAT |= (0XF<<0);printk("leddriver init is success\r\n");return 0;err_device_create:class_destroy(leddriver_class);err_class_create: cdev_del(leddriver_cdev);err_cdev_add:unregister_chrdev_region(leddriver_num, 1);err_alloc_chrdev_region:kfree(leddriver_cdev);err_leddriver_alloc:return err;}static __exit void leddriver_exit(void){ //取消映射 iounmap(gpm4con); iounmap(gpm4dat); device_destroy(leddriver_class,leddriver_num); class_destroy(leddriver_class); cdev_del(leddriver_cdev); unregister_chrdev_region(leddriver_num, 1); printk("leddriver is exit\r\n");}module_init(ldedriver_init);module_exit(leddriver_exit);MODULE_LICENSE("GPL");
app函数
#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <unistd.h>#include <stdio.h>#include <stdlib.h>int main(int argc,char *argv[]){ int led_fp,i; unsigned char led_statue[4]={1,0,1,0},statue[4]={0}; led_fp = open(argv[1],O_RDWR); write(led_fp,led_statue,4); lseek(led_fp,0,SEEK_SET); read(led_fp,statue,4); for(i=0;i<4;i++) { if(statue[i]==0) { printf("第%d个灯亮\r\n",i+1); } else { printf("第%d个灯灭\r\n",i+1); } } sleep(1); close(led_fp);}
测试结果
[root@ZC/zhangchao]#insmod led.ko [15827.270000] leddriver init is success[root@ZC/zhangchao]#./app /dev/leddevice [15833.320000] files open is success第1个灯灭第2个灯亮第3个灯灭第4个灯亮[15834.320000] leddriver close is success[root@ZC/zhangchao]#
- 基于tiny4412开发板LED灯驱动标准的llseek函数写法
- 基于tiny4412开发板LED灯驱动标准的read write函数写法
- tiny4412开发板LED灯驱动写法
- 基于tiny4412开发板led灯字符设备ioctl驱动写法
- Tiny4412开发板 LED灯的控制
- 基于TINY4412的Andorid开发-------简单的LED灯控制
- 基于TINY4412的Andorid开发-------简单的LED灯控制
- 基于TINY4412的Andorid开发-------简单的LED灯控制
- 基于TINY4412的Andorid开发-------简单的LED灯控制
- 基于TINY4412的Andorid开发-------简单的LED灯控制
- 基于TINY4412的Andorid开发-------简单的LED灯控制
- 基于tiny4412开发板的I2C子系统写法
- tiny4412 SDK1312B LED驱动
- 基于tiny4412按键中断驱动的poll、select函数演示
- 基于TQ2440开发的LED驱动
- 基于S3C2440开发板LED灯驱动移植
- tiny4412开发板蜂鸣器驱动
- 基于tiny4412的ts驱动简单分析
- sublime text3的用法
- Super Shell 轻松玩转多源Linux服务器的工具
- C/C++编程(四)--C语言运算符优先级
- Linux趣事 -- (2.4)学生信息管理系统
- React Native 手势响应系统
- 基于tiny4412开发板LED灯驱动标准的llseek函数写法
- c++中const用法相关总结
- 一直处于迷糊状态后学点基础知识的感想
- 完全采用缺省配置的最简Spring-boot Web应用
- 计算并输出一个三位整数的个位以及十位百位数字之和
- Android-NDK编译.so文件
- gdb 调试多进程、多线程的小栗子
- 数学基础——导数
- C++中doulbe/float/int转为CString方法