註冊字符設備號

chrdev.c

/*
 * @Author:topeet
 * @Description:字符設備自動創建設備節點步驟一創建類,創建設備
 */
#include <linux/init.h>	  //初始化頭文件
#include <linux/module.h> //最基本的文件,支持動態添加和卸載模塊。
#include <linux/fs.h>	  //包含了文件操作相關struct的定義,例如大名鼎鼎的struct file_operations
#include <linux/kdev_t.h>
#include <linux/cdev.h>		   //對字符設備結構cdev以及一系列的操作函數的定義。//包含了cdev 結構及相關函數的定義。
#define DEVICE_NUMBER 1		   //定義次設備號的個數
#define DEVICE_SNAME "schrdev" //定義靜態註冊設備的名稱
#define DEVICE_ANAME "achrdev" //定義動態註冊設備的名稱
#define DEVICE_MINOR_NUMBER 0  //定義次設備號的起始地址
#include <linux/device.h>	   //包含了device、class 等結構的定義
#define DEVICE_CLASS_NAME "chrdev_class"
#define DEVICE_NODE_NAME "chrdev_test" //宏定義設備節點的名字
static int major_num, minor_num;	   //定義主設備號和次設備號

struct class *class;				   /* 類 */
struct device *device;				   /* 設備 */
struct cdev cdev;					   //定義一個cdev結構體
module_param(major_num, int, S_IRUSR); //驅動模塊傳入普通參數major_num
module_param(minor_num, int, S_IRUSR); //驅動模塊傳入普通參數minor_num
dev_t dev_num;						   /* 設備號 */

/**
 * @description: 打開設備
 * @param {structinode} *inode:傳遞給驅動的 inode
 * @param {structfile} *file:設備文件,file 結構體有個叫做 private_data 的成員變量,
 *  一般在 open 的時候將 private_data 指向設備結構體。
 * @return: 0 成功;其他 失敗 
 */
int chrdev_open(struct inode *inode, struct file *file)
{
	printk("chrdev_open\n");
	return 0;
}
// 設備操作函數結構體
struct file_operations chrdev_ops = {
	.owner = THIS_MODULE,
	.open = chrdev_open};
/**
 * @description: 驅動入口函數
 * @param {*}無
 * @return {*} 0 成功;其他 失敗
 */
static int hello_init(void)
{
	int ret; //函數返回值
	if (major_num)
	{
		/*靜態註冊設備號*/
		printk("major_num = %d\n", major_num); //打印傳入進來的主設備號
		printk("minor_num = %d\n", minor_num); //打印傳入進來的次設備號

		dev_num = MKDEV(major_num, minor_num);								//MKDEV將主設備號和次設備號合併為一個設備號
		ret = register_chrdev_region(dev_num, DEVICE_NUMBER, DEVICE_SNAME); //註冊設備號
		if (ret < 0)
		{
			printk("register_chrdev_region error\n");
		}
		printk("register_chrdev_region ok\n"); //靜態註冊設備號成功
	}
	else
	{
		/*動態註冊設備號*/
		ret = alloc_chrdev_region(&dev_num, DEVICE_MINOR_NUMBER, 1, DEVICE_ANAME);
		if (ret < 0)
		{
			printk("alloc_chrdev_region error\n");
		}
		printk("alloc_chrdev_region ok\n"); //動態註冊設備號成功

		major_num = MAJOR(dev_num);			   //將主設備號取出來
		minor_num = MINOR(dev_num);			   //將次設備號取出來
		printk("major_num = %d\n", major_num); //打印傳入進來的主設備號
		printk("minor_num = %d\n", minor_num); //打印傳入進來的次設備號
	}
	// 初始化 cdev
	cdev.owner = THIS_MODULE;
	cdev_init(&cdev, &chrdev_ops);
	// 向系統註冊設備
	cdev_add(&cdev, dev_num, DEVICE_NUMBER);
	// 創建 class 類
	class = class_create(THIS_MODULE, DEVICE_CLASS_NAME);
	// 在 class 類下創建設備
	device = device_create(class, NULL, dev_num, NULL, DEVICE_NODE_NAME);
	return 0;
}
/**
 * @description: 驅動出口函數
 * @param {*}無
 * @return {*}無
 */
static void hello_exit(void)
{
	//註銷設備號
	unregister_chrdev_region(MKDEV(major_num, minor_num), DEVICE_NUMBER);
	//刪除設備
	cdev_del(&cdev);
	//註銷設備
	device_destroy(class, dev_num);
	//刪除類
	class_destroy(class);
	printk("gooodbye! \n");
}
// 將上面兩個函數指定為驅動的入口和出口函數
module_init(hello_init);
module_exit(hello_exit);
//  LICENSE 和作者信息
MODULE_LICENSE("GPL");
MODULE_AUTHOR("topeet");

Makefile

obj-m += chrdev.o  #先寫生成的中間文件的名字是什麼,-m的意思是把我們的驅動編譯成模塊
KDIR:=/home/topeet/driver/imx6ull/linux-imx-rel_imx_4.1.15_2.1.0_ga/
PWD?=$(shell pwd)   #獲取當前目錄的變量
all:
	make -C $(KDIR) M=$(PWD) modules  #make會進入內核源碼的路徑,然後把當前路徑下的代碼編譯成模塊

應用

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>


int main(int argc,char *argv[])
{
	int fd;
	char buf[64] = {0};
	fd = open("/dev/chrdev_test",O_RDWR);
	if(fd < 0)
	{
		perror("open error \n");
		return fd;
	}
	read(fd,buf,sizeof(buf));
	close(fd);
	return 0;
}