static struct file_operations结构体介绍(转)

1、 驱动与内核接口层
  驱动与内核接口层主要完成驱动模块在Linux内核的注册加载、卸载清除工作。这部分工作分别由初始化和退出函数完成。
  ① 初始化函数完成驱动模块加载:
static int __init DS18B20_init(void){
  ……
  register_chrdev(DS18B20_MAJOR,DEVICE_NAME, &DS18B20_fops);//完成设备注册
  #ifdefCONFIG_DEVFS_FS//创建设备文件系统
    devfs_mk_cdev(MKDEV(DS18B20_MAJOR,0),S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP,DEVICE_NAME);
  #endif
  ……
}
  ② 退出函数完成驱动模块卸载:
static void __exit DS18B20_exit(void) {
  #ifdef CONFIG_DEVFS_FS
    devfs_remove(DEVICE_NAME);//移除设备文件
  #endif
  unregister_chrdev(DS18B20_MAJOR,DEVICE_NAME); //完成设备注销
  ……
}

2、硬件设备接口层
  硬件设备接口层用来描述驱动程序与设备的交互。这些工作通过虚拟文件系统与设备驱动程序的接口实现。这个接口由file_operation结构定义,其结构如下:
  static struct file_operations DS18B20_fops ={
  .owner=THIS_MODULE, //指向拥有该结构的模块,内核使用该结构维护模块使用计数
  .open=DS18B20_open, //打开设备函数
  .read=DS18B20_read, //读接口函数
  .write=DS18B20_write,//写接口函数
  .fasync=DS18B20_fasync, //异步通知函数
  .poll=DS18B20_poll,//poll函数
  .release=DS18B20_release, //释放设备函数
};

  2.1  打开设备函数
  打开设备函数主要完成设备的初始化。
DS18B20_open(struct inode *inode,struct file *filp) {
  Initial_Timer( );//初始化定时器,使内核模块按一定周期读温度
  Initial_Device_DS18B20();//初始化硬件
  readtemperature();//开始读取……
}
void readtemperature(void) {
  ……Temperature=DS18B20read();//读取2个8位数据,此函数完成的硬件操作时序,由当前读通道号变量指定当前通道
  DS_SLOT_NO();//将本次读通道号放入缓冲区
  DS18B20Event();//数据放入缓冲区,唤醒等待队列并启动异步通知
  if(ReleaseFlag)
  CycleTimer_Delay_Soft(hdelay);//如果没有读停止信号,通过内核定时器延时,进行下一次读,在中断服务程序中再次启动读
  ……
}
  在使用内核定时器之前需定义一个定时器结构体 static struct timer_list CycleTimer。下面是定时器的具体操作:
static void Initial_Timer(void) {
  init_timer(&CycleTimer); );//初始化定时器结构
  CycleTimer.function=DS18B20_timer; //挂接定时中断服务程序
}
        2.2  读接口函数
  用户程序执行读操作的时候可能没有可以读取的数据,此时需要让read操作等待直到有数据可以读取。在此采用等待队列使进程在无数据读取时进入等待,数据到达时唤醒。等待队列设置成一个循环缓冲区,每放入一个新数据作为缓冲区的头,存放时间最久还未被取走的数据为缓冲区的尾。
DS18B20_read( ) {
  DECLARE_WAITQUEUE(wait,current);//声明等待队列……
Next_try:
  if(DS18B20dev.head != DS18B20dev.tail) {//等待队列不为空,即有数据
  DS18B20_ret=Read_Buffer_DS18B20(); //取走缓冲区的尾
  copy_to_user( ); //读取的数据送到用户空间
}
  else { ……//等待队列为空,即没有数据
  add_wait_queue(&queue,&wait);
  current>state=TASK_INTERRUPTIBLE;//添加等待队列,声明状态为任务可中断
  while((DS18B20dev.head==DS18B20dev.tail)&&!signal_pending(current) {//进入等待
  schedule();
  current>state=TASK_INTERRUPTIBLE;
    }//如果缓冲区为空,Linux内核调度,等待通知
  current>state = TASK_RUNNING;//得到有数据的通知,声明任务状态为运行
  remove_wait_queue(&queue,&wait);//删除等待队列
  goto Next_try;//返回到读取数据
  }
}
         2.3  fasync异步通知函数
  异步通知函数向进程发送SIGIO信号,通知访问设备的进程,表示设备已经准备好I/O读写了,避免主动查询,提高程序效率。使用异步通知需增加一个struct fasync_struct的结构指针,然后实现fasync接口函数。
static struct fasync_struct *fasync;//定义一个结构体
static int DS18B20_fasync(int fd,struct file *filp,int on) {//实现接口函数
  retval = fasync_helper(fd,filp,on,&fasync);
  if ( retval<0) return retval;return 0;
}
  最后在需要向用户空间通知的地方调用内核的kill_fasync函数。在打开设备函数中提到的DS18B20Event()功能是:将数据放入循环缓冲区,唤醒等待队列并启动异步通知,其后两项功能是这样实现的:
  wake_up_interruptible(&queue);//唤醒等待队列
  if (fasync) {
  kill_fasync(&fasync,SIGIO,POLL_IN);//发送异步通知信号
  }
         2.4  poll系统调用操作接口函数
  当程序需要进行对多个文件读写时,如果某个文件没有准备好,则系统就会处于读写阻塞的状态,影响其他文件的读写。为了避免读写阻塞,使用poll函数。如果设备无阻塞地读,就返回POLLIN; 通常的数据已经准备好,可以读了,就返回POLLRDNORM。
static unsigned int DS18B20_poll(struct file *flip, poll_table *wait) {
  poll_wait(flip,&queue,wait);
  if(DS18B20dev.head != DS18B20dev.tail) {
    return POLLIN|POLLRDNORM;
  }
  return 0;
}
       2.5  release释放设备函数
      static intDS18B20_release(struct inode *inode,struct file *filp) {
  ReleaseFlag=0//内核停止读取温度标志
  DS18B20_fasync(1,filp,0);//关闭异步通知
  module_put(THIS_MODULE);//设备计数器减1
  return 0;
}
  写接口函数用来通知驱动。例如通知驱动读取通道2的数据,在应用程序中执行写接口函数write(fileno,&SLOT2,1),驱动设置当前读通道号为2。
  至此完成驱动接口函数。此驱动属于字符设备驱动,将源程序放在driver/char目录下。同时需要修改该目录下的Kconfig配置文件并添加 Config18B20_S3C2410选项,修改driver/char/Makefile,添加obj$(CONFIG_18B20_S3C2410)+=S3C2410_18B20.O。最后重新配置内核,将驱动以模块形式添加到内核,这样就可以编译驱动了。

  • 2
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Linux内核中,文件操作是通过结构体`file_operations`来实现的,其中包括了一系列函数指针,用于实现文件的读、写、打开、关闭等操作。其中,read函数的函数指针名为`read`,其定义如下: ``` ssize_t (*read) (struct file *, char __user *, size_t, loff_t *); ``` read函数的参数包括: - `struct file * filp`:表示要读取的文件对象; - `char __user * buf`:表示读取缓冲区的地址,即将文件数据读取到哪里; - `size_t count`:表示要读取的字节数; - `loff_t *f_pos`:表示读取的起始位置,即从文件的哪个位置开始读取。 read函数的返回值为`ssize_t`类型,表示实际读取的字节数。如果返回值为0,则表示已经读取到了文件末尾;如果返回值为负数,则表示读取出错。 在Linux内核中,read函数主要用于读取设备文件的数据,因此可以通过实现read函数来实现设备文件的读取操作。通常情况下,read函数的实现涉及到文件操作锁的处理、文件指针的移动、数据的拷贝等操作。以下是一个简单的read函数的实现示例: ```c static ssize_t my_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos) { int ret = 0; /* 获取锁 */ if (down_interruptible(&dev->sem)) return -ERESTARTSYS; /* 判断读取位置是否超出文件末尾 */ if (*f_pos >= dev->size) goto out; /* 计算实际需要读取的字节数 */ if (*f_pos + count > dev->size) count = dev->size - *f_pos; /* 从设备中读取数据 */ ret = copy_to_user(buf, (void*)(dev->data + *f_pos), count); if (ret != 0) { ret = -EFAULT; goto out; } /* 更新文件指针 */ *f_pos += count; ret = count; out: /* 释放锁 */ up(&dev->sem); return ret; } ``` 该函数首先获取了文件操作锁,然后判断读取位置是否超出了文件末尾,接着计算实际需要读取的字节数。最后,从设备中读取数据,并更新文件指针,最终释放锁并返回读取的字节数。需要注意的是,该示例仅供参考,实际情况下需要根据具体需求进行修改。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值