Linux设备驱动——2.4内核字符设备驱动

2.4内核的字符设备注册接口

Linux 2.4内核中字符设备驱动的注册接口是:

int register_chrdev(unsigned int major,const char* name,struct file_operations* file)

Linux 2.6内核已经改为如下接口,并增加了自动在/dev目录下添加设备文件等功能。

int cdev_add(struct cdev* p,dev_t dev,unsigned count)

2.4内核的注册接口依然可以在2.6内核中使用,虽然官方不建议再使用,但是个人感觉还是2.4的接口用得爽,哈哈。驱动程序结构如下。

驱动程序结构

宏__KERNEL__表示该模块将用于内核,也可以在编译时通过- D 选项指定。宏MODULE表示这个驱动程序将单独作为一个模块的方式编译和使用,而不是直接编译进内核。可以通过insmod和rmmod命令动态加载和卸载该模块,使用更加灵活。

#ifndef __KERNEL__
#define __KERNEL__
#endif
#ifndef MODULE
#define MODULE
#endif

声明和定义驱动程序函数,标准做法是把函数原型声明放在一个.h文件中,在文件开始处include引用,并在文件中定义。这里把声明和定义放在一起。

#include<linux/module.h>
#include<linux/init.h>
#include<linux/kernel.h>
#include<linux/fs.h>
#include<linux/errno.h>
#include<linux/types.h>
#include<linux/poll.h>

#define DEVICE_NAME "drv_f"
static char drv_buf[8];

static ssize_t demo_write(struct file* filp,const char* buffer,size_t count,loff_t* ppos)
{
	copy_from_user(drv_buf,buffer,1);
	drv_buf[0]++;
	printk(KERN_ALERT "user write to driver\n");

	return 0;
}

static ssize_t demo_read(struct file* filp,char* buffer,size_t count,loff_t* ppos)
{
	copy_to_user(buffer,drv_buf,1);
	printk("user read from  driver\n");

	return count;
}

//ioctl用于控制驱动程序本身的一些特性和参数,例如串行通讯的速率,驱动程序使用的缓冲区大小等等。
static long demo_ioctl(struct file* file,unsigned int cmd,unsigned long arg)
{
	printk(KERN_ALERT "ioctl running\n");
	switch(cmd)
	{
		case 1:
			printk(KERN_ALERT "command 1 running\n");
			break;
		case 2:
			printk(KERN_ALERT "command 2 running\n");
			break;
		default:
			printk("error command number\n");
			break;
	}

	return 0;
}

static int demo_open(struct inode* inode,struct file* file)
{
	printk(KERN_ALERT "device open success\n");
	
	return 0;
}

static int demo_release(struct inode* inode,struct file* filp)
{
	printk(KERN_ALERT "device release success\n");
	
	return 0;
}

注意,定义read和write时,由于用户空间和内核空间的映射方式完全不同,不可以用memset等函数写入内存,必须使用如下函数:

unsigned long copy_to_user(void* to,const void* from,unsigned long count);

unsigned long copy_from_user(void* to,const void* from,unsigned long count);

上述read,write,ioctl,open,release是一个字符设备驱动程序五个最基本的需要完成的函数,这五个函数对应五个不同的系统调用。定义完成后,将他们扔给file_operations结构体:

static struct file_operations demo_fops = {
	.write = demo_write,
	.read = demo_read,
	.unlocked_ioctl = demo_ioctl,
	.open = demo_open,
	.release = demo_release,
};

在init函数中调用register_chrdev()函数,主设备号传入0时,函数返回动态分配的主设备号。

static int maj = -1;
static int __init demo_init(void)
{
	int result;
	result = register_chrdev(0,DEVICE_NAME,&demo_fops);
	if(result < 0)
	{
		return result;
	}
	maj = result;
	printk(DEVICE_NAME " initialized\n");

	return 0;
}

static void __exit demo_exit(void)
{
	unregister_chrdev(maj,DEVICE_NAME);
	printk(DEVICE_NAME " unloaded\n");
}

module_init(demo_init);
module_exit(demo_exit);

MODULE_LICENSE("GPL");

驱动程序的编译与加载

驱动程序编写完成后,复制到drivers/char目录下,需要修改Kconfig以及Makefile文件,并通过make menuconfig命令打开内核配置菜单,将该模块选择为动态加载模式<M>。
在这里插入图片描述

内核配置完成后,通过make modules命令编译该模块,在drivers/char/目录下生成一个该模块的.ko文件。
在这里插入图片描述
将这个文件下载到开发板,通过insmod *.ko命令就可以将模块加载到内核了。
在这里插入图片描述

通过cat /proc/devices命令可以查看该驱动对应的主设备号,我这里是253
在这里插入图片描述
此时可以看到/dev目录下没有该设备的设备文件结点,需要手动创建一个,命令为 mknod /dev/my_device c {你的主设备号} {次设备号},这里c代表字符设备。这时候可以通过ls /dev -l查看到这个设备文件节点了。
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值