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

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

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

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

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

#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>


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 <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/fs.h>
#include <linux/moduleparam.h>
#include <linux/stat.h>
#include <linux/kdev_t.h>
#include <linux/cdev.h>
#include <linux/slab.h>
#include <linux/device.h>

#include <linux/gpio.h>
#include <plat/gpio-cfg.h>
#include <mach/gpio.h>
#include <mach/gpio-exynos4.h>

#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<DEVICE_NUM;i++)
	{
		cdev_del(&(my_devices[i].cdev));
		device_destroy(myclass,MKDEV(device_major_num,device_minor_num+i));
		kfree(my_devices[i].data);
	}
	class_destroy(myclass);
	kfree(my_devices);
	for(i=0;i<LED_NUM;i++)
	{
		gpio_free(LED_Gpios[i]);
	}
		
	unregister_chrdev_region(MKDEV(device_major_num,device_minor_num),DEVICE_NUM);	
	printk(KERN_EMERG "Mini Linux Module Exit ! \r\n");	
	
}


int MyLED_driver_probe(struct platform_device* MyLED_device)	//总线驱动加载
{
	int res;
	int i;
	dev_t device_ID;
	
	res=alloc_chrdev_region(&device_ID,device_minor_num,DEVICE_NUM,DEVICE_NAME);	//获得动态主设备号
	if(res<0) 
	{
		printk(KERN_EMERG "%s device major num get fail !\r\n",DEVICE_NAME);
		return -1;
	}
	device_major_num = MAJOR(device_ID);
	printk("%s major num get from kernl is %d  And minor num you input is %d \r\n",DEVICE_NAME,device_major_num,device_minor_num);
	
	my_devices = kmalloc(DEVICE_NUM * sizeof(struct reg_dev),GFP_KERNEL);
	if(my_devices==NULL)
	{
		unregister_chrdev_region(device_ID,DEVICE_NUM);
		printk(KERN_EMERG "kmalloc is fail!\n");	
		return -1;		
	}
	memset(my_devices,0,DEVICE_NUM * sizeof(struct reg_dev));
	myclass = class_create(THIS_MODULE,DEVICE_NAME);	//创建设备类
	
	for(i=0;i<DEVICE_NUM;i++)
	{
		my_devices[i].data = kmalloc(REGDEV_SIZE,GFP_KERNEL);
		memset(my_devices[i].data,0,REGDEV_SIZE);
		cdev_init(&my_devices[i].cdev,&MyLED_driver_fops); //初始化设备结构体与文件搭建联系
		my_devices[i].cdev.owner = THIS_MODULE;
		my_devices[i].cdev.ops = &MyLED_driver_fops;
		res = cdev_add(&my_devices[i].cdev,MKDEV(device_major_num,device_minor_num+i),1); //注册到系统
		if(res<0) printk(KERN_EMERG "char device %s %d add fail	 \r\n ",DEVICE_NAME,i);
		else 	  printk(KERN_EMERG "char device %s %d add succees \r\n ",DEVICE_NAME,i);
		device_create(myclass,NULL,MKDEV(device_major_num,device_minor_num+i),NULL,DEVICENODE_NAME"%d",i); //创建设备节点
	}	
	
	for(i=0;i<LED_NUM;i++)
	{
		gpio_free(LED_Gpios[i]);
		res = gpio_request(LED_Gpios[i], "LED");
		if (res) 
		{
		printk("%s: request GPIO %d for LED failed", DEVICE_NAME,i);
		return -1;
		}
		else
		{
		s3c_gpio_cfgpin(LED_Gpios[i], S3C_GPIO_OUTPUT);
		gpio_set_value(LED_Gpios[i], 0);			
		}
	}	
	
	
	return 0;
}

int MyLED_driver_remove(struct platform_device* MyLED_device)
{
	printk(KERN_EMERG "device-> 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

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

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

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

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

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

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

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

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

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

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

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

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


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

拷贝到板子上

分别加载设备和驱动模块

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

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

靠近按键的灯亮了

 

靠近蜂鸣器的灯亮了

对比我们的ioctl函数:

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


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

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

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Linux字符设备驱动实验是指在Linux操作系统中编写和测试字符设备驱动程序的过程。字符设备驱动程序负责与字符设备进行交互,包括输入输出数据、控制设备和处理设备的状态等。 在进行Linux字符设备驱动实验之前,首先需要了解字符设备字符设备驱动的基本概念及其工作原理。字符设备是指以字符为单位进行输入输出的设备,如串口、打印机等。字符设备驱动是指将操作系统与字符设备进行交互的程序。 在实验中,我们通常需要编写一个字符设备驱动程序,包括初始化设备、读写数据、控制设备等功能。首先,我们需要定义字符设备驱动的数据结构,包括设备号、驱动程序打开、关闭等函数的实现。然后,我们需要实现字符设备驱动的读写函数来实现数据的输入输出。最后,我们可以进行一些附加功能的实现,如控制设备的状态、处理中断等。 在实验过程中,我们需要使用Linux内核提供的字符设备接口来进行字符设备驱动的编写和测试。可以使用一些工具和命令来加载和测试字符设备驱动程序,如insmod、rmmod等。通过这些工具和命令,我们可以加载和卸载字符设备驱动程序,并在用户空间进行数据的读写操作,来测试字符设备驱动的功能和性能。 Linux字符设备驱动实验可以帮助我们深入了解字符设备字符设备驱动的工作原理,并学习Linux内核的开发和调试技术。通过实验,我们可以更好地理解操作系统和驱动程序之间的关系,提高我们在Linux系统开发和嵌入式系统开发中的能力。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值