内核接口和多节点设备

1:内核接口

int (*open) (struct inode *, struct file *);

内核层的open 和 内核层release的参数是一致

内核层的open是可以被上层多几次调用且可以被多个文件绑定!

        四个设备原则上可以用一个内核层的open

        上层打开四个文件就会产生四次调用 内核层的open

        因为设备文件不同 -> 在内核层调用 open带来内核传参也会不同!

对应的参数:

struct inode
{

      dev_t  i_rdev;

}

struct file
{

       void * private_data;//私有数据

}

2:内核接口的read和write

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 *);


file:
        private_data
        私有数据指针 后期会用到
buf:
        在内核层的 read 里面:
                这个 buf 往往代表 上层(系统层)传递一个空间 buf
                上层调用 read ->他想读取到数据->找内核读!
                内核层的 read 应该 写入 buf 里面数据!
                原则上传递到内核层 buf 不允许直接访问
                        通过函数 copy_to_user(to,from,n);
                                to:拷贝到哪里->buf
                                from:拷贝给上层数据在哪
                                n:拷贝几个字节
                在内核层的 write 里面
                        这个 buf 往往代表 上层(系统层)传递一个空间 buf
                        上层调用 write ->本意是像往 内核写入数据
                        数据在哪->buf 里面
                        作为内核我们该 读取 buf 里面数据
                原则上也是不能直接访问
        需要通过函数:
                copy_form_user(to,from,n);
                to: 你把上层传递的数据拷贝到哪里
                from:来自上层的 buf
                n : 拷贝几个字节!

                size:
                上层传递过来的 参考数据的大小
                尤其是针对 read ,他一般是参考大小
                这个根据实际情况进行判定:
                key 就一个字节
                offt:
                一般在字符设备文件不用
                偏移量无意义。

3:多节点设备LED灯

#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");

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值