i.MX6ULL|字符设备驱动流程深究

描述

上一篇介绍了虚拟字符设备的驱动,这篇就深入学习字符驱动的流程,看看字符驱动和应用层是怎么配合使用的!

1、备份原来的驱动

字符

2、修改原来的驱动

在打印输出时,[BSP]开头表示驱动,[APP]开头表示应用,Makefile不用修改;

chrdevbase.c

 

#include 
#include 
#include 
#include 
#include 
#include 


#define CHRDEVBASE_MAJOR  200        /* 主设备号 */
#define CHRDEVBASE_NAME    "chrdevbase"   /* 设备名   */


static char readbuf[100];    /* 读缓冲区 */
static char writebuf[100];    /* 写缓冲区 */
static char kerneldata[] = {"kernel data!"};


/*
 * @description    : 打开设备
 * @param - inode   : 传递给驱动的inode
 * @param - filp   : 设备文件,file结构体有个叫做private_data的成员变量
 *             一般在open的时候将private_data指向设备结构体。
 * @return       : 0 成功;其他 失败
 */
static int chrdevbase_open(struct inode *inode, struct file *filp)
{
  //printk("chrdevbase open!
");
  return 0;
}


/*
 * @description    : 从设备读取数据 
 * @param - filp   : 要打开的设备文件(文件描述符)
 * @param - buf   : 返回给用户空间的数据缓冲区
 * @param - cnt   : 要读取的数据长度
 * @param - offt   : 相对于文件首地址的偏移
 * @return       : 读取的字节数,如果为负值,表示读取失败
 */
static ssize_t chrdevbase_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{
  int retvalue = 0;
  
  /* 向用户空间发送数据 */
  memcpy(readbuf, kerneldata, sizeof(kerneldata));
  retvalue = copy_to_user(buf, readbuf, cnt);
  if(retvalue == 0){
    printk("[BSP]kernel senddata ok!
");
  }else{
    printk("[BSP]kernel senddata failed!
");
  }
  
  //printk("chrdevbase read!
");
  return 0;
}


/*
 * @description    : 向设备写数据 
 * @param - filp   : 设备文件,表示打开的文件描述符
 * @param - buf   : 要写给设备写入的数据
 * @param - cnt   : 要写入的数据长度
 * @param - offt   : 相对于文件首地址的偏移
 * @return       : 写入的字节数,如果为负值,表示写入失败
 */
static ssize_t chrdevbase_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{
  int retvalue = 0;
  /* 接收用户空间传递给内核的数据并且打印出来 */
  retvalue = copy_from_user(writebuf, buf, cnt);
  if(retvalue == 0){
    printk("[BSP]kernel recevdata:%s
", writebuf);
  }else{
    printk("[BSP]kernel recevdata failed!
");
  }
  
  //printk("chrdevbase write!
");
  return 0;
}


/*
 * @description    : 关闭/释放设备
 * @param - filp   : 要关闭的设备文件(文件描述符)
 * @return       : 0 成功;其他 失败
 */
static int chrdevbase_release(struct inode *inode, struct file *filp)
{
  //printk("chrdevbase release!
");
  return 0;
}


/*
 * 设备操作函数结构体
 */
static struct file_operations chrdevbase_fops = {
  .owner = THIS_MODULE,  
  .open = chrdevbase_open,
  .read = chrdevbase_read,
  .write = chrdevbase_write,
  .release = chrdevbase_release,
};


/*
 * @description  : 驱动入口函数 
 * @param     : 无
 * @return     : 0 成功;其他 失败
 */
static int __init chrdevbase_init(void)
{
  int retvalue = 0;


  /* 注册字符设备驱动 */
  retvalue = register_chrdev(CHRDEVBASE_MAJOR, CHRDEVBASE_NAME, &chrdevbase_fops);
  if(retvalue < 0){
    printk("[BSP]chrdevbase driver register failed
");
  }
  printk("[BSP]chrdevbase init!
");
  return 0;
}


/*
 * @description  : 驱动出口函数
 * @param     : 无
 * @return     : 无
 */
static void __exit chrdevbase_exit(void)
{
  /* 注销字符设备驱动 */
  unregister_chrdev(CHRDEVBASE_MAJOR, CHRDEVBASE_NAME);
  printk("[BSP]chrdevbase exit!
");
}


/* 
 * 将上面两个函数指定为驱动的入口和出口函数 
 */
module_init(chrdevbase_init);
module_exit(chrdevbase_exit);


/* 
 * LICENSE和作者信息
 */
MODULE_LICENSE("GPL");
MODULE_AUTHOR("zuozhongkai");

 

chrdevbaseApp.c

 

#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "fcntl.h"
#include "stdlib.h"
#include "string.h"


static char usrdata[] = {"usr data!"};


/*
 * @description    : main主程序
 * @param - argc   : argv数组元素个数
 * @param - argv   : 具体参数
 * @return       : 0 成功;其他 失败
 */
int main(int argc, char *argv[])
{
  int fd, retvalue;
  char *filename;
  char readbuf[100], writebuf[100];


  if(argc != 3){
    printf("[APP]Error Usage!
");
    return -1;
  }


  filename = argv[1];


  /* 打开驱动文件 */
  fd  = open(filename, O_RDWR);
  if(fd < 0){
    printf("[APP]Can't open file %s
", filename);
    return -1;
  }


  if(atoi(argv[2]) == 1){ /* 从驱动文件读取数据 */
    retvalue = read(fd, readbuf, 50);
    if(retvalue < 0){
      printf("[APP]read file %s failed!
", filename);
    }else{
      /*  读取成功,打印出读取成功的数据 */
      printf("[APP]read data:%s
",readbuf);
    }
  }


  if(atoi(argv[2]) == 2){
   /* 向设备驱动写数据 */
    memcpy(writebuf, usrdata, sizeof(usrdata));
    retvalue = write(fd, writebuf, 50);
    if(retvalue < 0){
      printf("[APP]write file %s failed!
", filename);
    }
  }


  /* 关闭设备 */
  retvalue = close(fd);
  if(retvalue < 0){
    printf("[APP]Can't close file %s
", filename);
    return -1;
  }


  return 0;
}

 

3、编译驱动和应用

字符

4、复制需要的文件到根文件系统中

将 chrdevbase.ko 和 chrdevbaseAPP 复制到 rootfs/lib/modules/4.1.15 目录中:

字符

5、启动内核

在uboot界面输入下面指令启动系统,

 

tftp 80800000 zImage
tftp 83000000 imx6ull-14x14-evk.dtb
bootz 80800000 - 83000000

 

6、加载设备驱动

需要进入驱动文件目录才能加载设备驱动;

 

// 加载驱动
insmod chrdevbase.ko
// 查看驱动
lsmod
// 指令查看devices 信息
cat /proc/devices

 

效果如图:

字符

7、创建设备节点文件

输入如下命令创建/dev/chrdevbase 这个设备节点文件:

mknod /dev/chrdevbase c 200 0

 

8、验证读写

 

// 读
./chrdevbaseApp /dev/chrdevbase 1
// 写
./chrdevbaseApp /dev/chrdevbase 2


// 可以使用下面这行输出文件名称,输出/dev/chrdevbase
printf("filename:%s
",argv[1]);
// 可以使用下面这行输出参数,输出1 或者 2
printf("dat:%d
",atoi(argv[2]);

 

读的流程:

字符

写的流程:

字符

注意事项

下面这个函数的打印输出会印象到应用层的输出,看到应用层输出异常就把这个函数的输出给屏蔽就好;

字符

 

  审核编辑:汤梓红

 

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分