int (*open) (struct inode *, struct file *);
内核层的open 和 内核层release的参数是一致
内核层的open是可以被上层多几次调用且可以被多个文件绑定!
四个设备原则上可以用一个内核层的open
上层打开四个文件就会产生四次调用 内核层的open
因为设备文件不同 -> 在内核层调用 open带来内核传参也会不同!
对应的参数:
struct inode
{
dev_t i_rdev;
}
struct file
{
void * private_data;//私有数据
}
ssize_t(*read)(
struct file *f,
char __user *buf,
size_t size,
loff_t * offt
);
ssize_t (*write)(struct file *, char __user *, size_t, loff_t *);
#include "linux/kernel.h"
#include "linux/module.h"
#include "linux/of.h"
#include "linux/cdev.h"
#include "linux/fs.h"
#include "linux/gpio.h"
#include "linux/of_gpio.h"
#include "linux/device/class.h"
#include "linux/device.h"
#include "linux/platform_device.h"
#include "linux/miscdevice.h"
struct xydled_info_s{
int gpio_num;//GPIO 编号
enum of_gpio_flags gpio_flag;//GPIO_有效电平
char name[32];//GIPO 的 LED 灯对应的设备文件名
struct xydled_info_s * next;
};
struct xydled_info_s * head = NULL;//信息结构体头指针
int xydled_count = 0;//代表当前寻找到 LED 灯数量
dev_t devnum_base;//存储申请的首设备号
struct class * cls;//类结构体
struct cdev * cdev;//Linux2.6 的核心结构体
struct file_operations * ops;//内核文件操作集合
struct device_node * node;//设备树节点
int xyd_led_open(struct inode * inod, struct file * fil)
{
//多个设备文件 共用了 一个内核层 open
struct xydled_info_s * tmpp = head;
int value;
for(int i=0;i<xydled_count;i++)
{
if(inod->i_rdev == devnum_base +i )
{
if(tmpp->gpio_flag == OF_GPIO_ACTIVE_LOW)
value = 0;
gpio_set_value(tmpp->gpio_num,value);
break;//最佳优化
}
tmpp=tmpp->next;
}
return 0;
}
int xyd_led_close(struct inode * inod, struct file * fil)
{
struct xydled_info_s * tmpp = head;
int value;
for(int i=0;i<xydled_count;i++)
{
if(inod->i_rdev == devnum_base +i )
{
if(tmpp->gpio_flag == OF_GPIO_ACTIVE_LOW)
{
value = 0;
gpio_set_value(tmpp->gpio_num,!value);
break;//最佳优化
}
tmpp=tmpp->next;
}
}
return 0;
}
//新入口
int xyd_led_probe(struct platform_device * dev)
{
//1: 先取 Get 到 设备树节点
node = dev->dev.of_node;
//2:先去判断当前设备树状态
const char * tmp;
of_property_read_string(node,"status",&tmp);
if(strcmp(tmp,"okay"))
{
printk("IS ERROR!\r\n");
return -EINVAL;
}
//3:获取 GPIO 的信息: GPIO 编号 和 有效电平
while(1)
{
//3.1: 创建了 我自己的结构体 空间
struct xydled_info_s * tmpp = kzalloc(sizeof(struct
xydled_info_s),GFP_KERNEL);
if(tmpp == NULL)
return -EIO;
//3.2: 获取 GPIO 的信息
tmpp->gpio_num = of_get_named_gpio(node,"xyd
gpios",xydled_count);
if(tmpp->gpio_num < 0)
{
//失败空间没有必要存在 ->释放
kfree(tmpp);
break;
}
//3.3:获取有效电平
of_get_named_gpio_flags(node,"xyd
gpios",xydled_count,&tmpp->gpio_flag);
//3.4:封装设备文件名字
sprintf(tmpp->name,"xyd_led_%d",xydled_count);
//printf("确定有一个引脚成功获取了");
//3.5:初始化当前这个 GPIO
gpio_request(tmpp->gpio_num,tmpp->name);
if(tmpp->gpio_flag == OF_GPIO_ACTIVE_LOW)
gpio_direction_output(tmpp->gpio_num,1);
else
gpio_direction_output(tmpp->gpio_num,0);
//3.6:创建 GPIO 信息链表的关系
if(xydled_count == 0)
{
head = tmpp;
tmpp ->next = NULL;
}else
{
//找到最后一链表尾部
struct xydled_info_s * tempp = head;
while(tempp->next !=NULL)
tempp=tempp->next;
tempp->next = tmpp;
tmpp->next = NULL;
}
//3.7: 数量标志 底加
xydled_count++;
}
//4:申请设备号
alloc_chrdev_region(&devnum_base,0,xydled_count,"xyd_led");
//5: 初始化 Linux2.6 的 核心结构体
cdev = kzalloc(sizeof(struct cdev), GFP_KERNEL);
ops = kzalloc(sizeof(struct file_operations), GFP_KERNEL);
ops->owner = THIS_MODULE;
ops->open = xyd_led_open;
ops->release = xyd_led_close;
cdev_init(cdev,ops);
//6:往内核添加/注册设备
cdev_add(cdev,devnum_base,xydled_count);
/*
我现在连续添加了多个设备-> xydled_count
这些设备 共用一个 cdev 即代表共用了 cdev->ops
共用了 open close!!!!
*/
//7:创建 类结构体
cls = class_create(THIS_MODULE, "xyd_led");
//8: 根据数量创建设备文件
struct xydled_info_s * xydtmp = head;
for(int i =0;i<xydled_count;i++)
{
device_create(cls,NULL,devnum_base+i,NULL,xydtmp->name);
xydtmp= xydtmp->next;
}
return 0;
}
//新出口
int xyd_led_remove(struct platform_device * dev)
{
//我不喜欢写卸载!
return 0;
}
struct of_device_id xyd_id_table[]={
{.compatible = "xyd_led",},
};
struct platform_driver drv={
.probe = xyd_led_probe,//新入口
.remove= xyd_led_remove,//新出口
.driver ={
.name = "xyd_led",
.of_match_table =xyd_id_table,//匹配名字
},
};
static int __init myxyd_led_init(void)
{
return platform_driver_register(&drv);
}
//卸载函数
static void __exit myxyd_led_exit(void)
{
platform_driver_unregister(&drv);
}
module_init(myxyd_led_init);
module_exit(myxyd_led_exit);
MODULE_LICENSE("GPL");