如果做到一个驱动程序管理多个硬件设备个体,通过次设备号,共享一个主设备号;
答:struct inode
{
dev_t i_rdev;
struct cdev *icdev;
...
};
作用:
描述一个文件的物理上的信息
如果文件创建,内核为这个文件创建一个inode对象来描述这个文件的物理信息
如果文件销毁,内核也会销毁对应的inode对象;
重要成员:
i_rdev:如果此文件为设备文件,此字段保存设备文件的设备号信息,
驱动可以根据此字段来获取次设备号,通过次设备号来区分各个硬件设备个体;
i_cdev:如果此文件为字符设备文件,那么此字段指向字符设备驱动注册字符设备对象,
例如led_cdev;struct file
{
const struct file_operations *f_op;
};
作用:
描述一个文件被打开以后的状态属性;
如果文件打开(open),那么内核就会创建一个file对象来描述这个文件打开以后的属性;
如果文件关闭 ( colse ),那么内核也会销毁对应的file对象
重要成员:
f_op: 指向驱动程序注册的硬件操作方法(led_fops);
inode和file存在一定的关系:参考内核源码fbmem.cstruct inode *inode =
file->f_path.dentry->d_inode;注意: 一个文件仅有一个inode对象,但是可以有多个file对象案例:
2个LED共享一个驱动,共享一个主设备号,通过次设备号来区分
明确:
2个LED设备
设备文件分别是/dev/myled1和/dev/myled2
主设备号共享:
次设备号分别是:0 和 1,个数为2
硬件设备个数为2;
cdev对象共享
file_operations操作函数也共享;注意:通过次设备号区分
实施步骤:
#include
#include
#include //struct file_operations
#include //struct cdev + 设备号
#include
#include
#include //copy_to_user
#include
//声明描述LED硬件相关的数据结构
struct led_resource {
char *name;
int gpio;
};
//定义初始化LED硬件信息
static struct led_resource led_info[] = {
[0] = {
.name = "LED1",
.gpio = S5PV210_GPC0(3)
},
[1] = {
.name = "LED2",
.gpio = S5PV210_GPC0(4)
}
};
//定义设备号
static dev_t dev;
//定义字符设备对象
static struct cdev led_cdev;
//定义设备类指针
static struct class *cls;
#define LED_ON 0x100001 //开灯命令
#define LED_OFF 0x100002 //关灯命令
static long led_ioctl(struct file *file,
unsigned int cmd,
unsigned long arg)
{
//获取inode对象
struct inode *inode =
file->f_path.dentry->d_inode;
//获取次设备号
int minor = MINOR(inode->i_rdev);
//解析处理用户命令
switch(cmd) {
case LED_ON:
gpio_set_value(led_info[minor].gpio, 1);
printk("%s:第%d个灯被打开!\n", __func__, minor+1);
break;
case LED_OFF:
gpio_set_value(led_info[minor].gpio, 0);
printk("%s:第%d个灯被关闭!\n", __func__, minor+1);
break;
default:
return -1;
}
return 0; //成功返回0,失败返回-1
}
//定义初始化硬件操作方法
static struct file_operations led_fops = {
.owner = THIS_MODULE,
.unlocked_ioctl = led_ioctl //向设备发送命令和完成读写
};
static int led_init(void)
{
int i;
//申请设备号
alloc_chrdev_region(&dev, 0, 2, "tarena");
//初始化字符设备对象
cdev_init(&led_cdev, &led_fops);
//注册字符设备对象到内核
cdev_add(&led_cdev, dev, 2);
//申请GPIO资源和配置GPIO为输出口,输出0(省电)
for (i = 0; i < ARRAY_SIZE(led_info); i++)
{
gpio_request(led_info[i].gpio, led_info[i].name);
gpio_direction_output(led_info[i].gpio, 0);
}
//创建设备类(长树枝)
cls = class_create(THIS_MODULE, "tarena");
//创建设备文件
device_create(cls, NULL, MKDEV(MAJOR(dev), 0), NULL, "myled1");
device_create(cls, NULL, MKDEV(MAJOR(dev), 1), NULL, "myled2");
return 0;
}
static void led_exit(void)
{
int i;
//删除设备文件
device_destroy(cls, MKDEV(MAJOR(dev), 0));
device_destroy(cls, MKDEV(MAJOR(dev), 1));
class_destroy(cls);
//输出0,释放GPIO资源
for (i = 0; i < ARRAY_SIZE(led_info); i++)
{
gpio_set_value(led_info[i].gpio, 0);
gpio_free(led_info[i].gpio);
}
//卸载字符设备对象
cdev_del(&led_cdev);
//释放设备号
unregister_chrdev_region(dev, 2);
}
module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");
测试代码
#include
#include
#include
#include
#define LED_ON 0x100001
#define LED_OFF 0x100002
int main(void)
{
int fd1;
int fd2;
//打开设备
//open->....->调用led_open
fd1 = open("/dev/myled1", O_RDWR);
if (fd1 < 0) {
printf("打开设备失败!\n");
return -1;
}
fd2 = open("/dev/myled2", O_RDWR);
if (fd2 < 0) {
printf("打开设备失败!\n");
return -1;
}
while (1) {
ioctl(fd1, LED_ON);
sleep(1);
ioctl(fd2, LED_ON);
sleep(1);
ioctl(fd1, LED_OFF);
sleep(1);
ioctl(fd2, LED_OFF);
sleep(1);
}
//关闭设备
//close->...->调用led_close
close(fd1);
close(fd2);
return 0;
}
---------------------
作者:ptonlix
来源:CSDN
原文:https://blog.csdn.net/ptonlix/article/details/79229938
版权声明:本文为博主原创文章,转载请附上博文链接!