驱动程序之_1_字符设备_1_基本框架和Led实例

本文深入讲解Linux字符设备驱动的基本框架,包括驱动的加载、卸载流程,以及open、read、write等函数的作用。通过LED驱动实例,展示了如何定义和注册文件操作结构体,实现设备的读写功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

驱动程序之_1_字符设备_1_基本框架和Led实例

Linux设备驱动分三种,包括字符设备驱动、块设备驱动和网络设备驱动
其中本文讲的字符设备(如LCD、触摸屏等)只能按字节流先后顺序访问设备内存,不能随机访问
字符设备的基本框架比较简单

加载驱动时,调用入口函数
卸载驱动时,调用出口函数
应用程序打开驱动设备时,调用open函数
应用程序读写驱动设备时,调用read、write函数
……

open、read、write等函数是在linux内核中的统一接口,使用open打开设备会返回一个文件标识符,通过文件标识符使用read、write等函数会调用到设备自己的read、write函数,从而操作到硬件,这些设备自己的函数都被存放在一个文件结构体中

编写字符设备驱动的基本流程:
1、定义、设置文件结构体
2、编写入口函数,注册结构体
3、编写出口函数,销毁结构体
4、在设备打开时,初始化设备硬件
5、编写read、write等功能函数
6、包含一些头文件(参考其他驱动程序即可)

如:
入口函数

static int __init LedsInit(void)
{
}

出口函数

static void __exit LedsExit(void)
{
}

入口、出口函数需要修饰:

module_init(LedsInit);
module_exit(LedsExit);

结构体定义

static struct file_operations g_tLedsFops = 
{
	.owner = THIS_MODULE,
	.open   = LedsOpen,
	.read    = LedsRead,
	.write   = LedsWrite,	
};

open、read、write函数声明如下

static int LedsOpen(struct inode *ino, struct file *filep);
static ssize_t LedsWrite(struct file *fd, const char __user *buf, size_t cnt, loff_t * lof);
ssize_t LedsRead(struct file *file, char __user *buf, size_t size, loff_t * ppos);

注册结构体所用函数原型

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

可以人为创建设备,分配主设备号,然后在major中输入相应值,但一般来说都会使用linux内核所自带的mdev机制,自动分配设备号,创建设备,具体操作如下:

定义class、class_device指针变量

static struct class *g_ptLedsClass;
static struct class_device *g_ptLedsClassDevice;

创建class和class_device所用函数原型

extern struct class *class_create(struct module *owner, const char *name);

extern struct class_device *class_device_create(struct class *cls,
						struct class_device *parent,
						dev_t devt,
						struct device *device,
						const char *fmt, ...)
					__attribute__((format(printf,5,6)));

销毁class和class_device所用函数原型

extern void class_unregister(struct class *);

extern void class_device_destroy(struct class *cls, dev_t devt);

使用mdev机制需要包含GPL协议

MODULE_LICENSE("GPL");

附上一个关于led的驱动程序和测试用的应用程序,代码仅起示例作用

驱动程序

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/irq.h>
#include <asm/uaccess.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <asm/arch/regs-gpio.h>
#include <asm/hardware.h>
#include <linux/poll.h>
#include <linux/device.h>

static volatile unsigned long *g_dwGpfCon,*g_dwGpfDat;
static struct class *g_ptLedsClass;
static struct class_device *g_ptLedsClassDevice;

static int LedOpen(struct inode *ino, struct file *filep)
{
	*g_dwGpfCon&= ~(3<<8 | 3<<10 | 3<<12);
	*g_dwGpfCon |= (1<<8 | 1<<10 | 1<<12);	
	
	return 0;
}

static ssize_t LedWrite(struct file *fd, const char __user *buf, size_t cnt, loff_t * lof)
{
	int iVal;
	int iStat;
	int iNum;
	int iError;
	
	iError = copy_from_user(&iVal,buf,cnt);

	iStat = iVal % 10;
	iNum= iVal / 10;

	printk("iStat : %d , iNum : %d \r\n",iStat,iNum);	

	if(iStat)	
	{
		if(iNum != 7)			
			*g_dwGpfDat &= ~(1<<iNum);
		else
			*g_dwGpfDat &= ~((1<<(iNum-3)) | (1<<(iNum-2)) | (1<<(iNum-1)));
	}
	else
	{
		if(iNum != 7)
			*g_dwGpfDat |= (1<<iNum);
		else
			*g_dwGpfDat |= ((1<<(iNum-3)) | (1<<(iNum-2)) | (1<<(iNum-1)));
	}
	
	return 0;
}

static struct file_operations g_tLedFops = 
{
	.owner   = THIS_MODULE,
	.open    = LedOpen,
	.write   = LedWrite,
};

int g_iMajor;
static int LedInit(void)
{
	g_iMajor = register_chrdev(0,"Leds",&g_tLedFops);

	g_ptLedsClass = class_create(THIS_MODULE,"leds_class");
	g_ptLedsClassDevice = class_device_create(g_ptLedsClass,0,MKDEV(g_iMajor,0),0,"Leds");
	
	g_dwGpfCon = ioremap(0x56000050,16);
	g_dwGpfDat = g_dwGpfCon + 1;

	return 0;
}

static void LedExit(void)
{
	*g_dwGpfDat |= ((1<<4) | (1<<5) | (1<<6));	
	iounmap(g_dwGpfCon);

	class_device_destroy(g_ptLedsClass,MKDEV(g_iMajor,0));
	class_unregister(g_ptLedsClass);

	unregister_chrdev(g_iMajor,"Leds");
}

module_init(LedInit);
module_exit(LedExit);
MODULE_LICENSE("GPL");

应用程序

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

int main(int argv,char* argc[])
{
	int iFd;
	int iVal = 0;
	int iError;

	if(argv < 3)
	{
		printf("usage : ./filename <on|off> <1|2|3|all>");
		return -1;
	}

	if(!strcmp(argc[1],"on"))
	{
		iVal = 1;
	}
	else if(!strcmp(argc[1],"off"))
	{
		iVal = 0;
	}
	else
	{
		printf("usage : ./filename <on|off> <1|2|3|all>");
		return -1;
	}
	
	if(!strcmp(argc[2],"1"))
	{
		iVal += 40;
	}
	else if(!strcmp(argc[2],"2"))
	{
		iVal += 50;
	}
	else if(!strcmp(argc[2],"3"))
	{
		iVal += 60;
	}
	else if(!strcmp(argc[2],"all"))
	{
		iVal += 70;
	}
	else
	{
		printf("usage : ./filename <on|off> <1|2|3|all>\r\n");
		return -1;
	}
	
	iFd = open("/dev/Leds",O_RDWR);
	if(iFd < 0)	
	{
		printf("open error\r\n");
		return -1;
	}
	
	iError = write(iFd,&iVal,sizeof(iVal));

	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值