字符设备的注册与注销实现

一. 简介

前面文章学习了 编写字符设备驱动框架,并加载驱动模块。了解了 一组注册与注销设备的函数。

了解了字符设备号的组成以及如何分配。

本文在之前 1_chrdevbase工程代码的基础上,来学习如何注册与注销字符设备。

二.  注册设备前的准备工作

1.  注册函数与注销函数

对于字符设备驱动而言,当驱动模块加载成功以后需要注册字符设备,同样,卸载驱动模块的时候也需要注销掉字符设备。
字符设备的注册和注销函数原型如下所示 :
static inline int register_chrdev(unsigned int major, const char *name,
const struct file_operations *fops)

static inline void unregister_chrdev(unsigned int major, const char *name)

这两个函数的参数这里不做介绍,之前已经做过介绍。

2.  选定设备号与设备名

(1)选定设备号

这里使用静态分配的方式,实现字符设备的注册与注销,也就是我自己给设备设置一个设备号。

静态分配设备号需要我们检查当前系统中所有被使用了的设备号,然后挑选一个目前开发板上系统中没有使用的设备号。
所以,这里需要查看开发板上所有设备已经使用过设备号。开发板上电后进入根文件系统,查看系统上所以已使用的设备号:
/ # cat /proc/devices
Character devices:
  1 mem
  4 /dev/vc/0
  4 tty
  5 /dev/tty
  5 /dev/console
  5 /dev/ptmx
  7 vcs
 10 misc
 13 input
 29 fb
 81 video4linux
 89 i2c
 90 mtd
116 alsa
128 ptm
136 pts
180 usb
189 usb_device
207 ttymxc
226 drm
250 ttyLP
251 watchdog
252 ptp
253 pps
254 rtc

以上列出了开发板系统中已使用的所有设备号。

可以看出,使用 "cat /proc/devices" 命令可以查看系统上所以已使用的设备。上面信息可以看到字符设备已经列出,第一列数字表示主设备号。

除了上面字符设备的设备号,可以选择任何一个设备号作为这里设备的主设备号。例如,可以设置主设备号为 200,可以在 chrdevbase.c文件中设置 一个宏表示主设备号。

(2) 设置设备名

注册设备需要设置一个设备名,所设置的设备名不要与开发板系统上现有设备名重复。例如,可以设置设备名为 chrdevbase。可以在 chrdevbase.c 文件中设置 一个宏表示设备名。

(3) 定义 struct file_operations结构体
结构体 file_operations 类型指针,指向设备的操作函数集合变量。

字符设备驱动的编写,主要就是驱动对应的open、close、read等功能的实现,当应用程序调用 open,read,write,或 close函数时,对应的就是 这里结构体 file_operations对应的函数成员变量里面。

struct file_operations 这个类型结构体的定义在 /include/linux/fs.h文件下。 下面写函数接口可以参考结构体中函参类型进行编写。

三.   字符设备的注册与注销实现

这里字符设备驱动框架,可以参考 Linux内核源码中现有的驱动框架来编写。具体可以参考 之前NXP官方提供 Linux内核远源码。主要是 struct file_operations结构体中的函数的实现。

这里主要实现 open,read,write, close等功能的函数。这里参考NXP官方 Linux内核源码中 tb0219.c文件,来编写1_chrdevbase工程的编写:

#include 
#include 
#include 
#include 


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

static int chrdevbase_open(struct inode *inode, struct file *file);
static ssize_t chrdevbase_read(struct file * file, char __user * buf, size_t len, loff_t * ppos);
static ssize_t chrdevbase_write(struct file *file, const char __user *data, size_t len, loff_t *ppos);
static int chrdevbase_release(struct inode *inode, struct file *file);

/* 字符设备的操作函数集 */
static const struct file_operations chrdevbase_fops =
{
    .owner = THIS_MODULE,
    .open = chrdevbase_open,
    .read = chrdevbase_read,
    .write = chrdevbase_write,
    .release = chrdevbase_release,
};

/* 驱动模块入口函数 */
static int __init chrdevbase_init(void)
{
    int ret = 0;
    /* 注册字符设备 */
    ret = register_chrdev(CHRDEVBASE_MAJOR, CHRDEVBASE_NAME, &chrdevbase_fops);
    if(ret < 0)
    {
        printk("register_chrdev failed!\r\n");
        return ret;
    }		  

	return 0;
}

/* 驱动模块出口函数 */
static void __exit chrdevbase_exit(void)
{
    /* 注销字符设备 */
   unregister_chrdev(CHRDEVBASE_MAJOR, CHRDEVBASE_NAME);
}

/* 打开字符设备函数 */
static int chrdevbase_open(struct inode *inode, struct file *file)
{
    printk("chrdevbase_open()\r\n");
    return 0;
}

/* 读取字符设备数据 */
static ssize_t chrdevbase_read(struct file * file, char __user * buf, size_t len, loff_t * ppos) 
{
    printk("chrdevbase_read()\r\n");
    return 0;
}

/* 向字符设备写数据 */
static ssize_t chrdevbase_write(struct file *file, const char __user *data, size_t len, loff_t *ppos)                                  
{
    printk("chrdevbase_write()\r\n");
    return 0;
}

/* 释放/关闭字符设备*/
static int chrdevbase_release(struct inode *inode, struct file *file)
{
    printk("chrdevbase_release()\r\n");
    return 0;
}

/*
* 驱动模块的入口与出口函数
*/
module_init(chrdevbase_init); /* 入口 */
module_exit(chrdevbase_exit); /* 出口 */


MODULE_LICENSE("GPL");
MODULE_AUTHOR("LingXueWu");

编译 1_chrdevbase工程,编译完成后,生成 .ko文件。下一篇文章编写测试程序来对这个字符设备驱动框架进行测试。

你可能感兴趣的:(arm开发,linux)