Liunx驱动学习【1】---hello_drv

源代码

#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_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_close (struct inode *node, struct file *file)
{
   printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
   return 0;
}

/* 2. 定义自己的file_operations结构体                                              */
static struct file_operations hello_drv = {
   .owner	 = THIS_MODULE,
   .open    = hello_drv_open,
   .read    = hello_drv_read,
   .write   = hello_drv_write,
   .release = hello_drv_close,
};

/* 4. 把file_operations结构体告诉内核:注册驱动程序                                */
/* 5. 谁来注册驱动程序啊?得有一个入口函数:安装驱动程序时,就会去调用这个入口函数 */
static int __init hello_init(void)
{
   int err;
   
   printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
   major = register_chrdev(0, "hello", &hello_drv);  /* /dev/hello */


   hello_class = class_create(THIS_MODULE, "hello_class");
   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"); /* /dev/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);
module_exit(hello_exit);

MODULE_LICENSE("GPL");

分析函数构成

1. file_operation结构体

static struct file_operations hello_drv = {
	.owner	 = THIS_MODULE,
	.open    = hello_drv_open,
	.read    = hello_drv_read,
	.write   = hello_drv_write,
	.release = hello_drv_close,
};

定义驱动自己本身的file_operations结构体,里面有具体的对于文件的操作。其每个成员都是一个函数,有自己的参数。

2. 具体操作

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);
}

该函数格式可以从内核文件中得到。

  • 在应用程序中我们一般用 printf() 函数打印, 在驱动程序中我们一般使用 printk() 函数打印。
  • 其中 `
__FILE__, __FUNCTION__, __LINE__

都是内核内置的宏,分别表示文件,函数,行号等信息

  • _user 为一个空的宏,用来表示用户空间
  • 多利用宏来表示一些算式,很方便
#define MIN(a, b)  (a < b ? a : b)
  • 注意驱动程序和应用程序之间传递数据要用 copy_from_user/copy_to_user 函数。

3. 注册函数

static int __init hello_init(void)
{
	int err;
	
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	major = register_chrdev(0, "hello", &hello_drv);  /* /dev/hello */


	hello_class = class_create(THIS_MODULE, "hello_class");
	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"); /* /dev/hello */
	
	return 0;
}

顾名思义,将file_operations结构体注册到内核中去,下面我们来看这几个函数

major = register_chrdev(0, "hello", &hello_drv); 

第一个参数:可以自己指定主设备号,也可以传入 0 ,让内核给驱动程序分配主设备号;
第二个参数:表示该类设备的名称;
第三个参数:该驱动的file_operations结构体的指针。
最后将主设备号传给 “major”

hello_class = class_create(THIS_MODULE, "hello_class");

创建一个名字叫 hello_class 的类,这个函数将创建一个名字叫 hello_class 的class 类型的结构体并传给hello_class,这个类目前我也不知道干什么的,后面补充……

device_create(hello_class, NULL, MKDEV(major, 0), NULL, "hello"); /* /dev/hello */

创建这个名字叫 hello 的设备节点 /dev/hello 文件
第一个参数:就是上面刚刚创建好的 hello_class;
第三个参数:MKDEV()函数是将 设备主/次设备号转换为 32位或其他长度的dev_t类型的书,
第五个参数:将要在 /dev 创建的文件名字叫 hello

4.卸载驱动

和注册函数反着来就对了
不过,device_distroy() 函数在 calss_distory() 函数前面。

5.其他完善

module_init(hello_init);
module_exit(hello_exit);

MODULE_LICENSE("GPL");

将 hello_init 和 hello_exit 修饰为入/出口函数

最后要声明该驱动函数遵循 GPL 协议,否则没有办法使用,需要开放源代码。【GPL 协议的传染性】

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值