多节点+内核文件接口

目录

一、多节点

1、什么是多节点、多节点的意义

2、多节点的实现方法

3、多节点下的流水灯

二、内核文件接口

1、open 和 close 文件接口

2、read 和 write 文件接口

3、按键的驱动控制LED灯 示例


一、多节点

1、什么是多节点、多节点的意义

多节点:一个设备对应一个节点文件(设备文件)

意义:增加硬件的独立性

2、多节点的实现方法

在一个驱动文件下实现多节点

只要是靠Linux2.6下的 cdev_add  

cdev 支持连续注册多个设备文件!

3、多节点下的流水灯

#include "linux/kernel.h"
#include "linux/module.h"
#include "linux/cdev.h"
#include "linux/fs.h"
#include "linux/gpio.h"
#include "linux/device.h"
dev_t devnum;
struct cdev cdev;
struct file_operations ops;
struct class * cls;

//四个设备文件共用了一个打开接口和关闭接口
/*
    现在你再上层调用 open 
        不管你打开的是 LED1 还是LED2 亦或是LED3
        那么他都会调用 myled_open
        请问我如何区分谁再打开造成底层的调用
*/
int myled_open(struct inode * inode, struct file * file)
{
    /*  
        inode 传参里面有个成员变量
        i_rdev -- 记录的是调用底层open
        打开设备的设备号!
    */
    if(inode->i_rdev == devnum)//LED1 
    {
        gpio_set_value(EXYNOS4X12_GPM4(0),0);
    }else if(inode->i_rdev == devnum+1 ) //LED2 
    {
        gpio_set_value(EXYNOS4X12_GPM4(1),0);
    }else if(inode->i_rdev == devnum+2 ) //LED3 
    {
        gpio_set_value(EXYNOS4X12_GPM4(2),0);
    }else if(inode->i_rdev == devnum+3 ) //LED4
    {   
        gpio_set_value(EXYNOS4X12_GPM4(3),0);
    }
       
    return 0;
}
int myled_close(struct inode * inode, struct file * file)
{
    if(inode->i_rdev == devnum)//LED1 
    {
        gpio_set_value(EXYNOS4X12_GPM4(0),1);
    }else if(inode->i_rdev == devnum+1 ) //LED2 
    {
        gpio_set_value(EXYNOS4X12_GPM4(1),1);
    }else if(inode->i_rdev == devnum+2 ) //LED3 
    {
        gpio_set_value(EXYNOS4X12_GPM4(2),1);
    }else if(inode->i_rdev == devnum+3 ) //LED4
    {   
        gpio_set_value(EXYNOS4X12_GPM4(3),1);
    }
    return 0;
}

static int __init led_init(void)
{

    //1:申请四个设备号
    int ret = alloc_chrdev_region(&devnum,0,4,"led");
    if(ret < 0)
    {
        printk("申请设备号失败\r\n");
        return -EINVAL;
    }
    //2 :Linux2.6 核心结构体初始化 
    ops.owner = THIS_MODULE;
    ops.open  = myled_open;
    ops.release = myled_close;
    cdev_init(&cdev, &ops);
    cdev_add(&cdev,devnum,4);
    /*
        由于注册设备公用一个
        cdev核心结构体
        也就是公用一个ops
        也就是他们四个设备公用
        一套文件接口!!!
    */
    //3: 生成设备文件
    cls = class_create(THIS_MODULE, "led");
    device_create(cls ,NULL, devnum,NULL,"LED1");
    device_create(cls ,NULL, devnum+1,NULL,"LED2");
    device_create(cls ,NULL, devnum+2,NULL,"LED3");
    device_create(cls ,NULL, devnum+3,NULL,"LED4");

    /*
        你现在的四个设备文件公用一套文件接口!!!
        你还是不好做流水灯
    */
    return 0;
}

static void __exit led_exit(void)
{
    //销毁设备文件
	device_destroy(cla,devnum);
	device_destroy(cla,devnum+1);
	device_destroy(cla,devnum+2);
	device_destroy(cla,devnum+3);

	//释放类结构体空间
	class_destroy(cla);

	//删除Linux2.6设备
	cdev_del(&cdev);

	//释放设备号
	unregister_chrdev_region(devnum,4);

}

module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");

二、内核文件接口

1、open 和 close 文件接口

int (*open) (

        struct inode * inode,

        struct file * file

    );

    struct inode

    {

        i_rdev;//设备的设备号!

        i_private;

            //设备节点私有数据

            //该指针可以指向任何地方

            //一般用的比较少

            //可以当作传参使用

    }

    struct file * file{

        void    *private_data;

        //此空指针的用法与 i_private差不多

        //也是提供给用户的指针

        //可以指向任何地方

        //一般多节点中当做传参使用

    }

2、read 和 write 文件接口

ssize_t (*read) (struct file * file, char __user * buf, size_t size , loff_t * offt);

ssize_t (*write) (struct file * file, const char __user * buf, size_t, loff_t * offt); 

    file:

        他就是 openclosefile指针

        主要用 private_data进行传参

        read 和 write

        大部分情况下用于多节点下

        区分不同设备的情况!

    read的buf:

        你的上层的read的目的一定是

        读内核的数据!

        那么你内核层的read一定在

        给用户层写入数据!

        而此buf就是上层read传过来的所谓的

        空间

        * 但是这个read 不能让你直接访问!

          内核专用的将数据拷贝到用户层!

         #include <linux/uaccess.h>

          copy_to_user(

            void __user *to,

            const void *from,

            unsigned long n

          );

            to-> buf

            from:从哪拷贝数据!

            n:你要拷贝过去的长度     

            顺便也解释了

                int read(

                 int fd,

                 void * buf

                 int size

                );

                read(fd,buf,999);

                读到的长度谁决定的

                内核!

    write的buf:

        用户层write一定是在给内核层写数据

        所以 内核层实际在读取用户层数据

        buf

        首先它就是用户层传过来的数据!

        还是跟read buf一样 不能直接操作

        只能通过内核函数操作

        copy_from_user(

            void *to,

            const void __user *from,

            unsigned long n

        );

        to:你现在拷贝数据放到哪!

        from:拷贝的数据来自哪--buf

        n:你要拷贝几个字节呀!

            size 内核read/write第三个参数   

    size:

        实际上内核传数据的长度!

    offt:

        偏移量仅仅适用于块设备文件!

        对于字符设备文件而言没有偏移量概念

3、按键的驱动控制LED灯 示例

#include <linux/module.h>

#include <linux/kernel.h>

#include <linux/fs.h>

#include <linux/cdev.h>

#include <linux/device.h>

#include <linux/gpio.h>

//#include <asm/uaccess.h>

#include <linux/uaccess.h>

/*

    按键

    GPX3_2 -- KEY1

    GPX3_3 -- KEY2

    GPX3_4 -- KEY3

    GPX3_5 -- KEY4

    按键按下低

    松开高

*/

dev_t devnum;

struct cdev cdev;

struct file_operations ops;

struct class * cls;

int mykey_open(struct inode * inode, struct file * file)

{

    //如果按键没有初始化一般在此处初始化!

    file->private_data = &inode->i_rdev;

    if(inode->i_rdev == devnum)

    {

        //gpio_direction_input(EXYNOS4_GPX3(2));

    }else if(inode->i_rdev == devnum+1)

    {

        //gpio_direction_input(EXYNOS4_GPX3(3));

    }else if(inode->i_rdev == devnum+2)

    {

        //gpio_direction_input(EXYNOS4_GPX3(4));

    }else if(inode->i_rdev == devnum+3)

    {

        //gpio_direction_input(EXYNOS4_GPX3(5));

    }

    return 0;

}

int mykey_close(struct inode * inode, struct file * file)

{

    file->private_data = NULL;

    return 0;
}

ssize_t mykey_read(struct file * file, char __user * buf, size_t size , loff_t * offt)

{

    uint8_t keyvaule = 0;

    dev_t * devnump = file->private_data;

    if(* devnump == devnum)

    {
        keyvaule = gpio_get_value(EXYNOS4_GPX3(2));

    }else if(* devnump == devnum+1)

    {

        keyvaule = gpio_get_value(EXYNOS4_GPX3(3));

    }else if(* devnump == devnum+2)

    {
        keyvaule = gpio_get_value(EXYNOS4_GPX3(4));

    }else if(* devnump == devnum+3)

    {
        keyvaule = gpio_get_value(EXYNOS4_GPX3(5));

    }

    copy_to_user(buf,&keyvaule,1);

    return 0;

}

static int __init key_init(void)

{

    //1:申请设备号

    alloc_chrdev_region(&devnum,0,4, "keydevnum");

    //2:初始化Linux2.6 核心结构体

    ops.open = mykey_open;

    ops.read = mykey_read;

    ops.release= mykey_close;

    cdev_init(&cdev,&ops);

    cdev_add(&cdev,devnum,4);

    //3:生成设备文件

    cls = class_create(THIS_MODULE,"keyclass");

    device_create(cls,NULL,devnum, NULL,"key1");

    device_create(cls,NULL,devnum+1, NULL,"key2");

    device_create(cls,NULL,devnum+2, NULL,"key3");

    device_create(cls,NULL,devnum+3, NULL,"key4");

    return 0;

}

static void __exit key_exit(void)

{  

}

module_init(key_init);

module_exit(key_exit);

MODULE_LICENSE("GPL");
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Andy.w

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值