天天看点

基于tiny4412开发板LED灯驱动标准的read write函数写法

简介

原来曾经写过一个led灯的驱动,调用read write函数对灯的亮灭进行操作,虽然达到了控制的目的,学过系统编程的人知道,其实标准的read write函数的用法,并不是这样的,在对文件操作时,我们读取相关文件内容时,每当我们读取一部分内容时,文件内位置指针会随着移动,在进行文件内容读取时,读取内容会在当前位置读取一定数量的内容。写操作同样。当我们写一些内容到一个文件,例如将

1234456789

写到

new.txt

中,先写

1234

,在写

56789

,打开文件我们会发现,写入内容为

123456789

,后写入的

56789

并没有将原来写入的

1234

覆盖掉(这两次写操作均是在一次文件打开中执行,关闭后重新打开会覆盖掉原来的内容)。我们写的

read

write

函数,进行例如读取灯的状态时,每次读一个灯的状态,往往读取的都是第一个灯的状态,并不会随着我们读取次数的增加,而灯的位置发生变化,驱动比较笨不会进行位置偏移,或者写的并不符合标准,就这个问题将给出标准的基于

led

read

write

函数的写法,实现像操作文件一样去操作灯。

驱动层

read

write

函数原型

ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
           

我们可以看到这两个函数与我们写上层应用时,参数是不一样的,多了一个

loff_t *

。这就是驱动层与应用层的区别,而我们实现驱动

led

灯,像操作文件一样,这个参数至关总要。这个参数是干什么的呢?这个参数记录着当前操作位置。也就是一个文件内有

123456789

内容我先读取了

1234

这几项内容,假如文件没有关闭,再继续读文件会从5开始读取,并不会从1开始读取,实现这个功能就是

loff_t *

的作用,它会把当前文件操作的位置记录下来,方便下次操作。

之前的reead write代码

ssize_t leddriver_read(struct file *file, char __user *usr, size_t size, loff_t *loft)
{
    unsigned char LED_STATUE;


 copy_from_user(&LED_STATUE,usr,);
 printk("leddriver read is %d\r\n",LED_STATUE);

  switch(LED_STATUE)
    {
 case :GPM4DAT |= (<<);break;
 case :GPM4DAT &=~(0X1<<);break;
 case :GPM4DAT |= (<<);break;
 case :GPM4DAT &=~(0X1<<);break;
 case :GPM4DAT |= (<<);break;
 case :GPM4DAT &=~(0X1<<);break;
 case :GPM4DAT |= (<<);break;
 case :GPM4DAT &=~(0X1<<);break;

  }

  return ;
}
           

只能传入参数控制某个灯亮

新的标准led read write函数

ssize_t leddriver_read(struct file *file, char __user *usr, size_t size, loff_t *loft)
{
    loff_t cur_pos=*loft;//取出当前读写位置值
  unsigned char led_statue[],i,LED_NUM=;
  //读取数据长度为0什么也不做 返回0 退出程序的执行
  if(size<=)
    {
    return ;
  }
  //读取位置在末尾 无论size是多少都不能读出数据 数据有效区域越界
  if(cur_pos>=LED_NUM)
    {
     return ;
  }
  //判断size+当前位置是大于文件大小,只读取有效位的内容
  if(cur_pos+size>LED_NUM)
    {
    size=LED_NUM-cur_pos;

  }
  for(i=;i<LED_NUM;i++)
    {
     if(GPM4DAT &(<<i))
     led_statue[i]=;
     else
        led_statue[i]=;
  }
  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[],i,LED_NUM=;
  //写入数据大小为0 什么也不操作返回0退出
  if(size<=)
    {
    return ;
  }
  //当前位置大于等于文件最大有效数据,即使写入数据也是无效,所依不进行操作 返回0退出
  if(cur_pos>=LED_NUM)
    {
   return ;
  }
  //如果当前位置加上所要读取数据的长度大于剩余有效位   只读取有效数据位的数值
  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=;i<size;i++)
    {
    if(led_statue[i+cur_pos]==)
    GPM4DAT &= ~(<<(i+cur_pos));
    else
    GPM4DAT |= (<<(i+cur_pos));    
  }
  *loft+=size;
  return size;
}
           

验证led write的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[]={,,,},statue[]={};
  led_fp = open(argv[],O_RDWR);
    write(led_fp,led_statue,);
    sleep();
    write(led_fp,led_statue,);
  close(led_fp);
}
           

运行这个app函数会发现,两个灯先亮,两秒之后,剩余两个灯再次亮起。实现箱操作文件那样,可以在原来的基础上进行操作的用法。

验证led read的app函数

因为lseek函数还没写,所以文件在上一步写完后会定位到,文件末尾,在进行读操作,什么也读不到。所以进行写操作后,需要将文件关闭,再次打开在进行读取验证,否则验证结果不正确。

#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[]={,,,},statue[]={0};
  led_fp = open(argv[],O_RDWR);
    read(led_fp,statue,);
    for(i=;i<;i++)
    {
      if(statue[i]==)
        {
        printf("第%d个灯亮\r\n",i+);
        }
      else
        {
        printf("第%d个灯灭\r\n",i+);
        }
    }

    sleep();
    read(led_fp,statue,);
    for(i=;i<;i++)
    {
      if(statue[i]==)
        {
        printf("第%d个灯亮\r\n",i+);
        }
      else
        {
        printf("第%d个灯灭\r\n",i+);
        }
    }
  close(led_fp);
}
           

运行app后会依次读取灯的状态。

总结

像操作文件一样操作led灯,就要依赖文件偏移参数。

总的驱动代码

#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=;
//定义设备类
static struct class *leddriver_class;
//定义设备结构体
static struct device *leddriver_device;
//定义错误返回类型
static int  err;
//定义设备名称
#define LEDDRIVER_NAME "myled"

#define GPM4CON_ADDR 0x110002E0 
#define GPM4DAT_ADDR 0X110002E4

static volatile unsigned long *gpm4con=NULL;  
static volatile unsigned long *gpm4dat=NULL;

#define GPM4CON *gpm4con
#define GPM4DAT *gpm4dat

ssize_t leddriver_read(struct file *file, char __user *usr, size_t size, loff_t *loft)
{
    loff_t cur_pos=*loft;//取出当前读写位置值
  unsigned char led_statue[],i,LED_NUM=;
  //读取数据长度为0什么也不做 返回0 退出程序的执行
  if(size<=)
    {
    return ;
  }
  //读取位置在末尾 无论size是多少都不能读出数据 数据有效区域越界
  if(cur_pos>=LED_NUM)
    {
     return ;
  }
  //判断size+当前位置是大于文件大小,只读取有效位的内容
  if(cur_pos+size>LED_NUM)
    {
    size=LED_NUM-cur_pos;

  }
  for(i=;i<LED_NUM;i++)
    {
     if(GPM4DAT &(<<i))
     led_statue[i]=;
     else
        led_statue[i]=;
  }
  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[],i,LED_NUM=;
  //写入数据大小为0 什么也不操作返回0退出
  if(size<=)
    {
    return ;
  }
  //当前位置大于等于文件最大有效数据,即使写入数据也是无效,所依不进行操作 返回0退出
  if(cur_pos>=LED_NUM)
    {
   return ;
  }
  //如果当前位置加上所要读取数据的长度大于剩余有效位   只读取有效数据位的数值
  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=;i<size;i++)
    {
    if(led_statue[i+cur_pos]==)
    GPM4DAT &= ~(<<(i+cur_pos));
    else
    GPM4DAT |= (<<(i+cur_pos));    
  }
  *loft+=size;
  return size;
}

int leddriver_open (struct inode *node, struct file *file)
{
  printk("files open is success\r\n");
  return ;
}

int leddriver_release (struct inode *node, struct file *file)
{
  printk("leddriver close is success\r\n");
  return ;
}

//文件操作函数结构体
static struct file_operations leddriver_fops={
    .owner=THIS_MODULE,
    .open=leddriver_open,
    .release=leddriver_release,
    .read=leddriver_read,
    .write=leddriver_write,
};

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, , , LEDDRIVER_NAME);
//错误判断
if(err<)
{
  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,);
if(err<)
{
  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, );
    gpm4dat=ioremap(GPM4DAT_ADDR, );

    GPM4CON &= ~(<<);
    GPM4CON |= (<<);
    GPM4DAT |= (<<);

printk("leddriver init is success\r\n");
return ;

err_device_create:
class_destroy(leddriver_class);
err_class_create:
 cdev_del(leddriver_cdev);
err_cdev_add:
unregister_chrdev_region(leddriver_num, );

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, );
  printk("leddriver is exit\r\n");
}


module_init(ldedriver_init);
module_exit(leddriver_exit);
MODULE_LICENSE("GPL");
           

继续阅读