Linux字符设备驱动示例(LED)

上一篇博客以LED为例子,讲了如何将LED作为杂项设备注册并使用。

https://blog.csdn.net/qq_41495871/article/details/100523306

这篇博客则将LED作为字符设备注册并使用。

首先是将设备挂载在platfrom上,其实这一步是非必须的,后面会说到

#include 
#include 
#include 


MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("HQU_Orange");

#define DEVICE_NAME	"MyLEDS_device"

void MyLED_device_release(struct device *dev);

struct platform_device MyLED_device =
{
	.name	=	DEVICE_NAME	,
	.id		=	-1			,
	.dev	=	
	{
	.release=	MyLED_device_release ,	
	}
};



static int  Mini_Linux_Device_Module_Init (void)
{ 
	int Device_Status;

	printk(KERN_EMERG "Mini Liunx Device Module Enter ! \r\n");
	Device_Status=platform_device_register(&MyLED_device);
	printk(KERN_EMERG "Module Device Status is %d \n",Device_Status);
	return 0;
}

static void Mini_Linux_Device_Module_Exit (void)
{
	platform_device_unregister(&MyLED_device);
	printk(KERN_EMERG "Mini Linux Device Module Exit ! \r\n");
}



void MyLED_device_release(struct device *dev)
{
	printk(KERN_EMERG DEVICE_NAME "\tdevice release wolk!\r\n");
}

module_init(Mini_Linux_Device_Module_Init);
module_exit(Mini_Linux_Device_Module_Exit);



将设备挂载到platform上的代码都是一样的。

重点是驱动的编写。

MyLED_Driver.c

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 
#include 
#include 
#include 

#include "MyLED_Driver.h"


MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("HQU_Orange");


int MyLED_driver_probe(struct platform_device* MyLED_device);
int MyLED_driver_remove(struct platform_device* MyLED_device);
void MyLED_driver_shutdown(struct platform_device* MyLED_device);
int MyLED_driver_suspend(struct platform_device* MyLED_device,pm_message_t state);
int MyLED_driver_resume(struct platform_device* MyLED_device);

int MyLED_driver_fops_open(struct inode* p_inode,struct file* p_file);
int MyLED_driver_fops_release(struct inode* p_inode,struct file* p_file);
long MyLED_driver_fops_unlocked_ioctl(struct file* p_file ,unsigned int ID ,unsigned long cmd);
ssize_t MyLED_driver_fops_read(struct file *file, char __user *buf, size_t count, loff_t *f_ops);
ssize_t MyLED_driver_fops_write(struct file *file, const char __user *buf, size_t count, loff_t *f_ops);
loff_t MyLED_driver_fops_llseek(struct file *file, loff_t offset, int ence);

struct platform_driver MyLED_driver =
{
	.probe	=	MyLED_driver_probe		,
	.remove	=	MyLED_driver_remove		,
	.shutdown=	MyLED_driver_shutdown	,
	.suspend=	MyLED_driver_suspend	,
	.resume	=	MyLED_driver_resume		,
	.driver	=	
	{
	.name	=	DEVICE_NAME	,
	.owner	=	THIS_MODULE	,
	}
};


struct file_operations MyLED_driver_fops =
{
	.owner			=	THIS_MODULE						,
	.open			=	MyLED_driver_fops_open			,
	.release		=	MyLED_driver_fops_release		,
	.unlocked_ioctl	=	MyLED_driver_fops_unlocked_ioctl,
	.read 			= 	MyLED_driver_fops_read			,
	.write 			= 	MyLED_driver_fops_write			,
	.llseek 		= 	MyLED_driver_fops_llseek		,	
};


static int device_major_num;
static int device_minor_num;
static struct reg_dev *my_devices;
static struct class *myclass;
static int LED_Gpios[LED_NUM] = {EXYNOS4_GPL2(0),EXYNOS4_GPK1(1)};


module_param(device_minor_num,int,S_IRUSR);	 //输入次设备号

static int  Mini_Linux_Driver_Init (void)	//进入模块
{ 
	printk(KERN_EMERG "Mini Liunx Module Enter ! \r\n");
	platform_driver_register(&MyLED_driver);
	return 0;
}

static void Mini_Linux_Driver_Exit (void)	//退出模块
{
	int i;
	platform_driver_unregister(&MyLED_driver);
	for(i=0;i name = %s device -> id = %d remove!\r\n",MyLED_device->name,MyLED_device->id);
	
	return 0;
}

void MyLED_driver_shutdown(struct platform_device* MyLED_device)
{
	printk(KERN_EMERG "device-> name = %s device -> id = %d shutdown!\r\n",MyLED_device->name,MyLED_device->id);
}

int MyLED_driver_suspend(struct platform_device* MyLED_device,pm_message_t state)
{
	printk(KERN_EMERG "device-> name = %s device -> id = %d suspend!\r\n",MyLED_device->name,MyLED_device->id);
	return 0;
}

int MyLED_driver_resume(struct platform_device* MyLED_device)
{
	printk(KERN_EMERG "device-> name = %s device -> id = %d resume!\r\n",MyLED_device->name,MyLED_device->id);
	return 0;
}






int MyLED_driver_fops_open(struct inode* p_inode,struct file* p_file)
{			
	printk(KERN_EMERG "MyLED_file_open \r\n");
	return 0;
}

int MyLED_driver_fops_release(struct inode* p_inode,struct file* p_file)
{
	printk(KERN_EMERG "MyLED_file_close \r\n");
	return 0;
}

long MyLED_driver_fops_unlocked_ioctl(struct file* p_file ,unsigned int ID ,unsigned long cmd)
{
	printk(KERN_EMERG "ID is %d  cmd is %d \r\n" ,ID , cmd);
	if(ID>2||cmd>2)
	{
	printk(KERN_EMERG "ID and cmd must are 0 or 1\r\n");
	return 1;
	}
	if(ID)
	gpio_set_value(EXYNOS4_GPL2(0),cmd);
	else
	gpio_set_value(EXYNOS4_GPK1(1),cmd);

	return 0;
}


ssize_t MyLED_driver_fops_read(struct file *file, char __user *buf, size_t count, loff_t *f_ops)
{
	printk(KERN_EMERG "MyLED_file_read \r\n");	
	return 0;
}
ssize_t MyLED_driver_fops_write(struct file *file, const char __user *buf, size_t count, loff_t *f_ops)
{
	printk(KERN_EMERG "MyLED_file_write \r\n");
	return 0;
}

loff_t MyLED_driver_fops_llseek(struct file *file, loff_t offset, int ence)
{
	printk(KERN_EMERG "MyLED_file_llseek \r\n");	
	return 0;	
}




module_init(Mini_Linux_Driver_Init);
module_exit(Mini_Linux_Driver_Exit);



MyLED_Driver.h

#ifndef _MYLED_DRIVER_H_
#define _MYLED_DRIVER_H_

#define LED_NUM 2

#ifndef DEVICE_NAME
#define DEVICE_NAME			"MyLEDS_device"
#endif


#ifndef DEVICENODE_NAME
#define DEVICENODE_NAME		"MyLEDS"
#endif


#ifndef DEVICE_NUM
#define DEVICE_NUM 2
#endif


#ifndef DEV_MAJOR
#define DEV_MAJOR 0
#endif


#ifndef DEV_MINOR
#define DEV_MINOR 0
#endif


#ifndef REGDEV_SIZE
#define REGDEV_SIZE 3000
#endif

struct reg_dev
{
	char *data;
	unsigned long size;
	
	struct cdev cdev;
};
#endif

可以看到加载模块后,就直接将驱动往总线上一丢就什么也没做了。

Linux字符设备驱动示例(LED)_第1张图片

但其实,总线会自动匹配名字一样的驱动和设备,匹配成功后,就会自动执行probe中的程序

所以其实我们真正注册设备和驱动是在probe中完成。当然如果是像LED等设备一开始就焊在板子上了,

这种固定的设备就其实没有必要再挂载在总线上了,加载模块时可以跳过挂载总线这一步直接注册驱动。

下面分析一下注册代码,第一步是动态获得主设备号

Linux字符设备驱动示例(LED)_第2张图片

其中次设备号是由加载模块的用户设置的,默认是0

这一步是向内核申请并清空内存空间和创建设备类。

Linux字符设备驱动示例(LED)_第3张图片

Linux字符设备驱动示例(LED)_第4张图片

这一步就是分别为字符设备申请空间(这些空间用于write 、read 这种字符流的缓存),将它们与文件操作搭建联系,

注册到内核中,最后就可以创建可以使用的设备节点。

Linux字符设备驱动示例(LED)_第5张图片

这一步是对硬件做初始化,配置gpio

Linux字符设备驱动示例(LED)_第6张图片

记得在卸载模块时,将这些设备、设备节点、设备类都删除了并且释放我们上面申请的空间和引脚资源。

最后删除我们申请的主次设备号。

Linux字符设备驱动示例(LED)_第7张图片


分析完代码,可以进行实验了。编译

Linux字符设备驱动示例(LED)_第8张图片

拷贝到板子上

分别加载设备和驱动模块

Linux字符设备驱动示例(LED)_第9张图片

可以看到,我们成功注册了2个主设备号一致的字符设备,并且次设备号是我们输入的17

Linux字符设备驱动示例(LED)_第10张图片

成功生成两个设备节点。接下来试试调用它们。

Linux字符设备驱动示例(LED)_第11张图片 靠近按键的灯亮了

 

Linux字符设备驱动示例(LED)_第12张图片 靠近蜂鸣器的灯亮了

对比我们的ioctl函数:

Linux字符设备驱动示例(LED)_第13张图片

说明我们实验成功了。接下来卸载设备和驱动。

Linux字符设备驱动示例(LED)_第14张图片


总结一下,其实注册字符设备与注册杂项设备最大的不同就是,字符设备要申请设备号、一块用于缓存字符流的内存、

用于保存设备节点的设备类。其他的和杂项设备基本一致,只要调用正确的库函数就没有什么问题了。

你可能感兴趣的:(Linux字符设备驱动示例(LED))