字符块设备驱动程序框架---已测试程序hello为例

简介: 字符块设备驱动程序框架---已测试程序hello为例

怎么编写驱动程序

1.确定主设备号。

2.定义自己的file_operations结构体。

3.实现对应的open/read/write等函数,填入file_operations结构体。

4.把file_operations结构体告诉内核:注册驱动程序。

5.谁来注册驱动函数=呢?得有一个人口函数:安装驱动程序时,就会去调用这个出口函数。

6有人口函数就应该有出口函数:卸载驱动程序是,就会去调用这个出口函数。

7.其他完善:提供设备信息,自动创建设备节点。


app调用的系统调用函数open,read,write等系统调用函数时,会先调用sys_open,sys_read,sys_write,再根据设备判断是普通文件,还是设备文件,进而调用drv_open,drv_read,drv_write等驱动程序。那么驱动程序怎么提前写出来呢?也就是前面的七点

struct file {
  union {
    struct llist_node fu_llist;
    struct rcu_head   fu_rcuhead;
  } f_u;
  struct path   f_path;
  struct inode    *f_inode; /* cached value */
  const struct file_operations  *f_op;
  /*
   * Protects f_ep_links, f_flags.
   * Must not be taken from IRQ context.
   */
  spinlock_t    f_lock;
  atomic_long_t   f_count;
  unsigned int    f_flags;
  fmode_t     f_mode;
  struct mutex    f_pos_lock;
  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;
} __attribute__((aligned(4)));  /* lest something weird decides that 2 is OK */

struct file 这个结构体也就是我们使用open函数的返回值,是一个整数,可以暂时当做设备描述符。const struct file_operations *f_op;结构体指针,指向file_operations结构体,这个结构体里面就有驱动程序

struct file_operations {
  struct module *owner;
  loff_t (*llseek) (struct file *, loff_t, int);
  ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
  ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
  ssize_t (*read_iter) (struct kiocb *, struct iov_iter *);
  ssize_t (*write_iter) (struct kiocb *, struct iov_iter *);
  int (*iterate) (struct file *, struct dir_context *);
  int (*iterate_shared) (struct file *, struct dir_context *);
  unsigned int (*poll) (struct file *, struct poll_table_struct *);
  long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
  long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
  int (*mmap) (struct file *, struct vm_area_struct *);
  int (*open) (struct inode *, struct file *);
  int (*flush) (struct file *, fl_owner_t id);
  int (*release) (struct inode *, struct file *);
  int (*fsync) (struct file *, loff_t, loff_t, int datasync);
  int (*fasync) (int, struct file *, int);
  int (*lock) (struct file *, int, struct file_lock *);
  ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
  unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
  int (*check_flags)(int);
  int (*flock) (struct file *, int, struct file_lock *);
  ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);
  ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);
  int (*setlease)(struct file *, long, struct file_lock **, void **);
  long (*fallocate)(struct file *file, int mode, loff_t offset,
        loff_t len);
  void (*show_fdinfo)(struct seq_file *m, struct file *f);
#ifndef CONFIG_MMU
  unsigned (*mmap_capabilities)(struct file *);
#endif
  ssize_t (*copy_file_range)(struct file *, loff_t, struct file *,
      loff_t, size_t, unsigned int);
  int (*clone_file_range)(struct file *, loff_t, struct file *, loff_t,
      u64);
  ssize_t (*dedupe_file_range)(struct file *, u64, u64, struct file *,
      u64);
};

将open,write,read等函数填入到file_operations结构体中。而file_operations由register_chrdev(主设备号,file_operations)将设备注册到chrdev【】数组中。注册之后就可以通过之前系统分配的设备号或者自己指定的设备号就可以调用这个设备的驱动函数了。


开始编写驱动程序

参照liunx/char/misc.c进行编写

首先添加头文件

/*
总体流程
1.确定主设备号。
2.定义自己的file_operations结构体。
3.实现对应的open/read/write等函数,填入file_operations结构体。
4.把file_operations结构体告诉内核:注册驱动程序。
5.谁来注册驱动函数=呢?得有一个人口函数:安装驱动程序时,就会去调用这个出口函数。
6有人口函数就应该有出口函数:卸载驱动程序是,就会去调用这个出口函数。
7.其他完善:提供设备信息,自动创建设备节点。
*/
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>
/* 1.确定主设备号。*/
static int major = 0;
static char kernel_buf[1024];
static struct class *hello_class;
#define MIN(a,b)(a<b?a:b)
/*3.实现对应的open/read/write等函数,填入file_operations结构体。*/
static ssize_t hello_drv_read (struct file *file, char __user *buf, size_t size, loff_t *offset)
{
  int err;
  printk("%s %s line %d\n",__FILE__,__FUNCTION__,__LINE__);
  err = copy_to_user(buf, kernel_buf, MIN(1024,size));
  return MIN(1024, size);
}
static ssize_t hello_drv_write (struct file *file, const char __user *buf, size_t size, loff_t *offset)
{
  int err;
  printk("%s %s line %d\n",__FILE__,__FUNCTION__,__LINE__);
  err = copy_from_user(kernel_buf, buf, MIN(1024,size));
  return MIN(1024,size);
}
static int hello_drv_open (struct inode *node, struct file *file)
{
  printk("%s %s line %d\n",__FILE__,__FUNCTION__,__LINE__);
  return 0;
}
static int hello_drv_release (struct inode *node, struct file *file)
{
  printk("%s %s line %d\n",__FILE__,__FUNCTION__,__LINE__);
  return 0;
}
/* 2.定义自己的file_operations结构体。*/
static const struct file_operations hello_drv = {
  .owner   = THIS_MODULE,
  .open    = hello_drv_open,
  .read    = hello_drv_read,
  .write   = hello_drv_write,
  .release = hello_drv_release,
};
/*4.把file_operations结构体告诉内核:注册驱动程序。*/
/*5.谁来注册驱动函数=呢?得有一个人口函数:安装驱动程序时,就会去调用这个出口函数。*/
static int __init hello_init(void)
{
  int err;
  printk("%s %s line %d\n",__FILE__,__FUNCTION__,__LINE__);
  register_chrdev(0, "hello", &hello_drv);//应用程序访问某一个驱动程序需要根据某一个设备节点才能访问,那么如何才能设置设备节点那??
  hello_class = class_create(THIS_MODULE, "hello");
  err = PTR_ERR(hello_class);
  if (IS_ERR(hello_class))
  {
    printk("%s %s line %d\n",__FILE__,__FUNCTION__,__LINE__);
    unregister_chrdev(major, "hello");
    return -1;
  }
  device_create(hello_class, NULL, MKDEV(major, 0), NULL, "hello"); 
  return 0;
}
/*6有人口函数就应该有出口函数:卸载驱动程序是,就会去调用这个出口函数。*/
static void __exit hello_exit(void)
{
  printk("%s %s line %d\n",__FILE__,__FUNCTION__,__LINE__);
  device_destroy(hello_class,MKDEV(major, 0));
  class_destroy(hello_class);
  unregister_chrdev(major, "hello");
}
/*7.其他完善:提供设备信息,自动创建设备节点。*/
module_init(hello_init);//将hello_init修饰为入口函数
module_exit(hello_exit);//将hello_exit修饰为出口函数
MODULE_LICENSE("GPL");//表示同意加入GPL协议,开源协议

注意在liunx上编译后放在6ull开发板上运行时会出现

ls: cannot access ‘/dev/hello’: No such file or directory

但是hello这个驱动程序又已经加载进了内核中。这里只是没有dev设备节点,所以这里手动添加设备节点。

mknod /dev/hello c 240 0

添加之后就可以进行测试程序的操作了。


目录
相关文章
|
7天前
|
Java 测试技术 Python
《手把手教你》系列基础篇(八十)-java+ selenium自动化测试-框架设计基础-TestNG依赖测试-番外篇(详解教程)
【6月更文挑战第21天】本文介绍了TestNG中测试方法的依赖执行顺序。作者通过一个实际的自动化测试场景展示了如何设计测试用例:依次打开百度、搜索“selenium”、再搜索“selenium+java”。代码示例中,`@Test`注解的`dependsOnMethods`属性用于指定方法间的依赖,确保执行顺序。如果不设置依赖,TestNG会按方法名首字母排序执行。通过运行代码,验证了依赖关系的正确性。
30 4
|
13天前
|
测试技术 持续交付 UED
探索自动化测试框架:提高软件质量的利器
在软件开发周期中,自动化测试框架扮演着至关重要的角色。本文将深入探讨自动化测试框架如何提升测试效率、确保软件质量和促进持续集成/持续部署(CI/CD)的实施。文章将分析自动化测试的优势、挑战以及实施策略,为读者提供一份全面的自动化测试指南。
|
4天前
|
机器学习/深度学习 人工智能 测试技术
自动化测试框架的演进与实践
【6月更文挑战第23天】在软件工程领域,自动化测试框架的发展不断推动着质量保证的效率和效果。本文将探讨自动化测试框架从简单脚本到复杂集成系统的演变过程,并分析当前流行的框架如Selenium、Appium以及新兴的AI驱动测试工具。我们将通过具体案例,展示如何在现代软件开发实践中有效应用这些框架以提升测试覆盖率和准确性。
|
5天前
|
Java 测试技术 Python
《手把手教你》系列基础篇(八十一)-java+ selenium自动化测试-框架设计基础-TestNG如何暂停执行一些case(详解教程)
【6月更文挑战第22天】本文介绍了如何在TestNG中不执行特定测试用例。当部分模块未准备好时,可以通过以下方式暂停测试:③使用`@Test(enabled=false)`注解来禁用测试用例。作者提供了一个Java Selenium自动化测试的示例,展示如何通过修改`enabled`参数控制测试方法的执行。代码中,`testSearch2()`方法被禁用,因此在测试运行时不执行。文章还包含了测试报告和执行过程的截图。
36 7
|
8天前
|
Java 测试技术 Python
《手把手教你》系列基础篇(七十九)-java+ selenium自动化测试-框架设计基础-TestNG依赖测试-下篇(详解教程)
【6月更文挑战第20天】TestNG是一个Java测试框架,提供两种测试方法依赖机制:强依赖(所有前置方法成功后才运行)和弱依赖(即使前置方法失败,后置方法仍运行)。文中通过代码示例展示了这两种依赖如何实现,并解释了当依赖方法失败时,如何影响后续方法的执行。文章还包含了TestNG Suite的运行结果截图来辅助说明。
31 8
|
4天前
|
XML Java 测试技术
《手把手教你》系列基础篇(八十二)-java+ selenium自动化测试-框架设计基础-TestNG测试报告-上篇(详解教程)
【6月更文挑战第23天】TestNG 是一个用于自动化测试的 Java 框架,它自动生成测试报告,包括 HTML 和 XML 格式。报告可在 `test-output` 文件夹中找到。要创建测试用例,可创建一个实现了 `@Test` 注解的方法。通过 `testng.xml` 配置文件来组织和执行测试。默认报告包括测试结果、失败点和原因。用户还能实现 `ITestListener` 和 `IReporter` 接口来自定义报告和记录器。
22 2
|
10天前
|
Java 测试技术 Python
《手把手教你》系列基础篇(七十七)-java+ selenium自动化测试-框架设计基础-TestNG依赖测试- 上篇(详解教程)
【6月更文挑战第18天】TestNG是一个Java测试框架,它允许在测试方法间定义执行顺序和依赖关系。当不指定依赖时,TestNG默认按方法名首字母排序执行。`@Test`注解的`dependsOnMethods`属性用于指定方法依赖,如`test1`依赖`test4`,则实际执行顺序为`test4`、`test2`、`test3`、`test1`。如果依赖的方法失败,后续依赖的方法将被跳过。此外,`dependsOnGroups`属性通过组名指定依赖,方便管理多个相关测试方法。通过`groups`定义方法所属组,然后在其他方法中用`dependsOnGroups`引用这些组。
25 5
|
9天前
|
数据可视化 前端开发 Java
自动化测试框架的选择与实践: Selenium vs. TestComplete
【6月更文挑战第18天】在软件开发的海洋中,自动化测试是一艘能够确保产品质量和效率的坚固船只。本文将深入探讨两种流行的自动化测试框架——Selenium和TestComplete,从它们的优势、局限性到适用场景进行对比分析。我们将通过实际案例来揭示如何根据项目需求选择最合适的测试工具,并提供一些实用的实施建议。文章旨在为读者提供清晰的指导,帮助他们在自动化测试的旅程中做出明智的决定。
13 3
|
9天前
|
XML Web App开发 测试技术
《手把手教你》系列基础篇(七十八)-java+ selenium自动化测试-框架设计基础-TestNG依赖测试- 中篇(详解教程)
【6月更文挑战第19天】本文介绍了使用TestNG框架配置XML文件来管理测试用例的分组和依赖关系。
37 2
|
14天前
|
Web App开发 XML 安全
《手把手教你》系列基础篇(七十三)-java+ selenium自动化测试-框架设计基础-TestNG实现启动不同浏览器(详解教程)
【6月更文挑战第14天】本文介绍了如何使用TestNg进行自动化测试,特别是通过变量参数启动不同浏览器的步骤。
24 5