上一篇博客以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
可以看到加载模块后,就直接将驱动往总线上一丢就什么也没做了。
但其实,总线会自动匹配名字一样的驱动和设备,匹配成功后,就会自动执行probe中的程序
所以其实我们真正注册设备和驱动是在probe中完成。当然如果是像LED等设备一开始就焊在板子上了,
这种固定的设备就其实没有必要再挂载在总线上了,加载模块时可以跳过挂载总线这一步直接注册驱动。
下面分析一下注册代码,第一步是动态获得主设备号
其中次设备号是由加载模块的用户设置的,默认是0
这一步是向内核申请并清空内存空间和创建设备类。
这一步就是分别为字符设备申请空间(这些空间用于write 、read 这种字符流的缓存),将它们与文件操作搭建联系,
注册到内核中,最后就可以创建可以使用的设备节点。
这一步是对硬件做初始化,配置gpio
记得在卸载模块时,将这些设备、设备节点、设备类都删除了并且释放我们上面申请的空间和引脚资源。
最后删除我们申请的主次设备号。
分析完代码,可以进行实验了。编译
拷贝到板子上
分别加载设备和驱动模块
可以看到,我们成功注册了2个主设备号一致的字符设备,并且次设备号是我们输入的17
成功生成两个设备节点。接下来试试调用它们。
对比我们的ioctl函数:
说明我们实验成功了。接下来卸载设备和驱动。
总结一下,其实注册字符设备与注册杂项设备最大的不同就是,字符设备要申请设备号、一块用于缓存字符流的内存、
用于保存设备节点的设备类。其他的和杂项设备基本一致,只要调用正确的库函数就没有什么问题了。