RK3568驱动层实现GPIO工作状态控制以及输入/输出电平读取

1、应用场景

新项目中有一个8pin的输入/输入引脚,通过io命令设置工作模式,读取输入输出电平过于复杂,于是就写了一个驱动用于控制GPIO工作模式,读取GPIO输入/输出电平。

2、驱动代码

文件名gpio-rockchip-cdev.c

#include <dt-bindings/gpio/gpio.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/ioctl.h>
#include <linux/uaccess.h>
#include <linux/string.h>
#include <linux/wait.h>
#include <linux/types.h>
#include <linux/proc_fs.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/gpio.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/err.h> 

#define GPIO_OUT_HIGH       1
#define GPIO_OUT_LOW        0
#define GPIO_IN_HIGH        1
#define GPIO_IN_LOW         0
#define GPIOPIN_NAME        "gpiopin"
#define IOCTL_INPUT_MOD     3
#define IOCTL_OUNPUT_MOD    2

#define MAX_GPIO_PIN_COUNT     8

struct gpiopin_dev{
    struct cdev cdev;  //charater dev
    struct device *device; //device 
    struct gpio_desc *gpiod;     //gpio number
    int gpiostate; //gpio state
};

struct UsrMesg{
    int gpioval;
};

static struct gpiopin_dev gpiopin[8];
static struct class *gpiopin_class;
static dev_t DevMajNum = 0;

/* open gpiopin device */
static int gpiopin_open(struct inode *inode ,struct file *filp)
{
    int minor = iminor(inode);
    printk("minor = %d\n",minor);
    if (minor < 0 || minor >= MAX_GPIO_PIN_COUNT) {
        return -ENODEV;
    }
    filp->private_data = &gpiopin[minor];//device sub device id
    return 0;
}

/*read data from gpiopin input device*/
static ssize_t gpiopin_read(struct file *filp, char __user *buf,
                            size_t cnt, loff_t *offt)
{
    int ret = 0;
    int RetValue = 0,PinStat = 0;
    struct gpiopin_dev *dev=filp->private_data;
    PinStat=gpiod_get_direction(dev->gpiod);//if Pinstat equal 1,Gpio pin working at input state
    if(PinStat == 1){
        printk("wx debug gipo working input mode\n");
        RetValue=gpiod_get_value(dev->gpiod);//Get gpio pin receive value
        printk("wx debug gpio input value %d\n",RetValue);
        ret = copy_to_user(buf,&RetValue,sizeof(RetValue));
        return ret;
    }
    return PinStat;
}

/*write data to gpiopin output mode*/
static ssize_t gpiopin_write(struct file *filp,const char __user *buf,size_t cnt ,loff_t *offt) //return value mast equal cnt
{
    int ret = 0;
    unsigned char PinStat = 0,DataBuf[20];
    struct gpiopin_dev *dev=filp->private_data;
    printk("wx devug gpio driver input char number=%ld\n",cnt);
    printk("wx debug write to gpio pin\n");
    PinStat=gpiod_get_direction(dev->gpiod);//if Pinstat equal 1,Gpio pin input state
    printk("Pinstat = %d\n",PinStat);
    if(PinStat == 0){
        printk("wx debug gipo working output mode\n");
        ret = copy_from_user(DataBuf,buf,cnt);
        if(ret < 0)
        {
            printk("wx debug Kernel Read/Write Error");
            return -EFAULT;
        }
        printk("wx debug ret = %d\n",ret);
        printk("wx debug Databuf[0]=%d\n",DataBuf[0]);
        if(DataBuf[0] == GPIO_OUT_HIGH || DataBuf[0] == '1'){
            gpiod_set_value(dev->gpiod,GPIO_OUT_HIGH);
            /*
             * gpiod_set_value的有效值由设备树中决定 
             * gpios = <&gpio1 RK_PD6 GPIO_ACTIVE_HIGH>; GPIO_ACTIVE_HIGH决定了输出高电平的值
            */

            printk("wx debug set gpio output high level\n");
        }else if(DataBuf[0] == GPIO_IN_LOW || DataBuf[0] == '0'){
            gpiod_set_value(dev->gpiod,GPIO_OUT_LOW);
            printk("wx debug set gpio output low level\n");
        }
    }
    return cnt;
}

static long gpiopin_ioctl(struct file *filp, unsigned int cmd,
                            unsigned long arg)
{
    int ret = 0;
    struct UsrMesg MyUsrMesg;
    struct gpiopin_dev *dev=filp->private_data;
    printk("wx debug Start ioctl function\n");
    printk("wx debug ioctl input cmd = %d\n",cmd);
    switch (cmd){
    case IOCTL_OUNPUT_MOD :
        //struct UsrMesg MyUsrMesg;
        ret = copy_from_user(&MyUsrMesg,(struct UsrMesg __user*)arg,sizeof(MyUsrMesg));
        if(ret)
            break;
        ret = gpiod_direction_output(dev->gpiod,MyUsrMesg.gpioval);
        if(ret < 0){
            printk("can't set gpio output mode\n");
            break;
        }
        break;
    case IOCTL_INPUT_MOD :
        ret = gpiod_direction_input(dev->gpiod);
        if(ret < 0){
            printk("can't set gpio input mode\n");
        }
        break;
    default :
        printk("Input Error Cmd");
        return -EINVAL ;
    }
    return 0;
}

static struct file_operations gpiopin_fops = {
    .owner = THIS_MODULE,
    .open = gpiopin_open,
    .read = gpiopin_read,
    .write = gpiopin_write,
    .unlocked_ioctl = gpiopin_ioctl,
};

static int  gpiopin_probe(struct platform_device *pdev)
{
    int ret = 0, index = 0;
    const char *state;
    struct fwnode_handle *child;   
    struct device *dev = &pdev->dev; //pointer device
    printk("WX debug rk-gpio start probe\n");
    if(DevMajNum){  
        register_chrdev_region(DevMajNum,MAX_GPIO_PIN_COUNT,GPIOPIN_NAME);
    }else{ //auto allcate device id
        alloc_chrdev_region(&DevMajNum,0,MAX_GPIO_PIN_COUNT,GPIOPIN_NAME);
    }
    printk("wx debug gpiopin major=%d\n",DevMajNum);

    //create class
    gpiopin_class= class_create(THIS_MODULE,GPIOPIN_NAME);
    if(IS_ERR(gpiopin_class)){
        return PTR_ERR(gpiopin_class);
    }
    printk("WX debug gpiopin class create succssfully\n");
    
    device_for_each_child_node(dev, child) {
        gpiopin[index].gpiostate = GPIO_OUT_HIGH;
        /*init cdev*/
        gpiopin[index].cdev.owner = THIS_MODULE;
        cdev_init(&gpiopin[index].cdev,&gpiopin_fops);
        ret = cdev_add(&gpiopin[index].cdev,MKDEV(MAJOR(DevMajNum),index),1);
        if(ret < 0 )
            printk("wx debug cdev add failed\n");
        /*obtain gpio resource*/
        gpiopin[index].gpiod = devm_fwnode_get_gpiod_from_child(dev, NULL, child,
                                 GPIOD_ASIS,NULL);        
        if (IS_ERR(gpiopin[index].gpiod)) {
            fwnode_handle_put(child);
            return PTR_ERR(gpiopin[index].gpiod);
        }
        printk("wx debug request gpio source\n");
        /*obtain device tree default-state attribute*/
        if (!fwnode_property_read_string(child, "default-state",
                         &state)) {
            if (!strcmp(state, "on"))
                gpiopin[index].gpiostate = GPIO_OUT_HIGH;
            else
                gpiopin[index].gpiostate = GPIO_OUT_LOW;
        }
        printk("wx debug gpiopin[%d].gpiostate = %d",index,gpiopin[index].gpiostate);
        ret = gpiod_direction_output(gpiopin[index].gpiod,gpiopin[index].gpiostate);
        if(ret != 0){
            printk("ret = %d wx debug can't set gpio!\n",ret);
        }
        /*create device*/        
        gpiopin[index].device = device_create(gpiopin_class, NULL, MKDEV(MAJOR(DevMajNum), index), NULL, "gpiopin%d", index);
        if (IS_ERR(gpiopin[index].device)) {
            pr_err("wx debug Failed to create device for gpiopin%d\n", index);
            // 在这里处理创建失败的情况
        } else {
            pr_info("wx debug Device for gpiopin%d created successfully\n", index);
            // 在这里处理创建成功的情况
        }
        printk("WX debug gpiopin class  create and device bind class succssfully\n");
        index ++;
    }
    return 0;
}


static int  gpiopin_remove(struct platform_device *dev)
{
    int i;
    for (i =0 ; i < MAX_GPIO_PIN_COUNT ; i++){
        gpiod_set_value(gpiopin[i].gpiod ,0);
        device_destroy(gpiopin_class, MKDEV(MAJOR(DevMajNum), i));
        cdev_del(&gpiopin[i].cdev);
    }
    class_destroy(gpiopin_class);
    return 0;
}

static const struct  of_device_id gpiopin_of_match[] = {
    { .compatible = "rk-gpio" },
    {}
};
//platform driver struct
static struct platform_driver gpiopin_driver = {
    .driver = {
        .name = "rk-gpio",   //compatible device
        .of_match_table = gpiopin_of_match,
    },
    .probe = gpiopin_probe,
    .remove = gpiopin_remove,
};


static int __init gpiopin_driver_init(void)
{
    return platform_driver_register(&gpiopin_driver);  //source platform driver struct
}

static void __exit gpiopin_driver_exit(void)
{
    platform_driver_unregister(&gpiopin_driver);
}

module_init(gpiopin_driver_init);
module_exit(gpiopin_driver_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("wangxiang");

3、配置设备树

gpio_pins: gpio-pins{
               status = "okay";
               compatible = "rk-gpio";
               pinctrl-0 = <&decenta_gpio_pins>;
                   pinctrl-1 = <&pwr_led>;
               pinctrl-names = "default","default";
               gpio_pin1{
                   gpios = <&gpio1 RK_PB6 GPIO_ACTIVE_HIGH>;
                   default-state = "off";
               };

               gpio_pin2{
                   gpios = <&gpio1 RK_PB7 GPIO_ACTIVE_HIGH>;
                   default-state = "on";
               };

               gpio_pin3{
                   gpios = <&gpio1 RK_PD6 GPIO_ACTIVE_HIGH>;
                   default-state = "off";
               };

                gpio_pin4{
                   gpios = <&gpio1 RK_PD7 GPIO_ACTIVE_HIGH>;
                   default-state = "off";
               };

              work_led {
                    gpios = <&gpio3 RK_PB7 GPIO_ACTIVE_HIGH>;
                    default-state = "off";
            };


};//配置PINCTL

4、修改Makefile

vim RK3588_LINUX_SDK/kernel/drivers/leds/Makefile 

在最后一行添加

obj-y                           += gpio-rockchip-cdev.o

5、应用层代码

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <string.h>

#define IOCTL_INPUT_MOD 3
#define IOCTL_OUTPUT_MOD 2

struct UsrMesg {
    int gpioval;
};

int main(int argc, char *argv[]) {
    if (argc < 2) {
        printf("Usage: %s <gpio_device> [read | write <value> | ioctl <mode>]\n", argv[0]);
        printf("  <gpio_device>: Path to the device file (e.g., /dev/pingpio)\n");
        printf("  [read]: Read the current GPIO value\n");
        printf("  [write <value>]: Write a value (0 or 1) to the GPIO (output mode)\n");
        printf("  [ioctl <mode>]: Set the GPIO mode (2 for input, 3 for output)\n");
        return 1;
    }

    const char *device = argv[1];
    int fd = open(device, O_RDWR);
    if (fd == -1) {
        perror("Failed to open device");
        return 1;
    }

    if (argc >= 3 && strcmp(argv[2], "read") == 0) {
        // Read the current GPIO value using read
        int value;
        if (read(fd, &value, sizeof(value)) == -1) {
            perror("Read failed");
            close(fd);
            return 1;
        }
        printf("Current GPIO value: %d\n", value);
    } else if (argc >= 4 && strcmp(argv[2], "write") == 0) {
        // Write a value to the GPIO (output mode) using write
        if (argc < 4) {
            printf("Usage: %s <gpio_device> write <value>\n", argv[0]);
            close(fd);
            return 1;
        }
        int value = atoi(argv[3]);
        if (write(fd, &value, sizeof(value)) == -1) {
            perror("Write failed");
            close(fd);
            return 1;
        }
        printf("Write %d to GPIO\n", value);
    } else if (argc >= 4 && strcmp(argv[2], "ioctl") == 0) {
        // Set the GPIO mode using ioctl
        if (argc < 4) {
            printf("Usage: %s <gpio_device> ioctl <mode>\n", argv[0]);
            close(fd);
            return 1;
        }
        struct UsrMesg usrmesg;
        if (argc >=5 ){
            usrmesg.gpioval = atoi(argv[4]);
        }else{
            usrmesg.gpioval = 0;
        }
        int mode = atoi(argv[3]);
        printf("wx debug ioctl mode=%d\n",mode);
        printf("wx debug ioctl output value=%d\n",usrmesg.gpioval);
        if (ioctl(fd, mode,&usrmesg) == -1) {//ioctl 函数的第三个参数应该为结构体的地址,负责会报错 Invaild add
            perror("IOCTL failed");
            close(fd);
            return 1;
        }
        printf("Set GPIO mode to %d\n", mode);
    } else {
        printf("Invalid command. Use 'read' to read the GPIO value, 'write <value>' to set it, or 'ioctl <mode>' to set the GPIO mode.\n");
    }

    close(fd);
    return 0;
}

应用程序使用方法

./your_program /dev/pingpio read

./your_program /dev/pingpio write 1

./your_program /dev/pingpio ioctl 3设置GPIO为输入模式

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值