如何在Linux内核模块中访问GPIO?

时间:2017-07-05 15:30:23

标签: linux linux-kernel linux-device-driver embedded-linux gpio

我很难找到从linux内核模块访问gpio-pin所需的步骤。 也许有人可以通过一个简单的例子向我解释。我喜欢使用引脚4(输入)和33(输出)。到目前为止我的步骤:

1。)设备树(dts):我保持dts文件不变 - 我是否需要通过引脚控制来设置引脚4和33?

2。)内核模块:一些伪代码

gpio_is_valid(4)
gpio_request_one(4, GPIOF_DIR_IN | GPIOF_EXPORT_DIR_FIXED , "myPin4")
gpio_export(4, false)
gpio_get_value(4)

gpio_is_valid(33)
gpio_request_one(33, GPIOF_DIR_OUT | GPIOF_INIT_LOW | GPIOF_OPEN_SOURCE | GPIOF_EXPORT_DIR_FIXED , "myPin33")
gpio_export(33, false)
gpio_set_value(33, 1)

如何以正确的方式做到这一点?

1 个答案:

答案 0 :(得分:3)

我建议组合自己的设备树文件+平台驱动程序+字符驱动程序

0。)RTF

检查device trees(dts)的工作方式

检查platform device的工作原理

检查character device的工作原理

获得关于gpios和dts的一些知识

@gpio mappings

@subsystems using gpios

@Specifying GPIO information for devices

阅读SOC制造商提供的信息。

访问gpios的state-of-the-art方式是通过struct gpio_desc个变量。它们是从设备树中创建的。

1。)方法

要在linux下切换一个引脚,你需要设置shure,这3个单元正在工作。

引脚控制器(pinctrl)定义输出的驱动方式。开源,拉起等。

引脚多路复用器(pinmux)为引脚定义了不同的功能。

gpio-controller(gpioctrl)翻译gpio编号。 p.E。:44 - > GPIO A 11

这些组件由SOC制造商实施。每个平台都有差异。以下是SAMA5D35xxx的示例。

@the device tree

定义引脚控制器

pinctrl@fffff200 {
    pinctrl_myPins: myPins {
        atmel,pins = <AT91_PIOA 2 AT91_PERIPH_GPIO AT91_PINCTRL_NONE  // pin 1
            AT91_PIOD 19 AT91_PERIPH_GPIO AT91_PINCTRL_NONE>; // pin 2
    };
};

创建一个链接到自己的平台设备的节点:

myPins {
    compatible = "myPlatformDevice";

    pinctrl-names = "default";
    pinctrl-0 = <&pinctrl_myPins>;

    pin1 = <&pioA 2 GPIO_ACTIVE_HIGH>;
    pin2 = <&pioD 19 GPIO_ACTIVE_HIGH>;
};

@create platform + char driver(伪代码):

// ----------------------
// kernel message support(via dmesg)
// ----------------------
#define KMSG_DEBUG(fmt,args...) printk(KERN_DEBUG "myDrv" ": "fmt"\n", ##args)
#define KMSG_PERR(fmt,args...) printk(KERN_ERR "myDrv" ": "fmt"\n", ##args)
#define KMSG_PINFO(fmt,args...) printk(KERN_INFO "myDrv" ": "fmt"\n", ##args)
// ----------------------
// trace support via defining dMyDrvTrace
// ----------------------
#ifndef dMyDrvTrace
    #define TRACE(...)
#else
    #define TRACE(fmt,args...) printk(KERN_INFO "myDrv" ": [%s] "fmt"\n", __FUNCTION__, ##args)
#endif


typedef struct SMyDrvDrvData {
    struct platform_device *pdev; //!< next device
    // here goes the local/private data
    int gpiod_pin1;
    int gpiod_pin2;

    u32 pin1;
    u32 pin2;
} TMyDrvDrvData;

static struct dentry * gmyPlattformDrvDebugfsRootDir; //!< root dir at debugfs

static int myPlattformDrv_probe(struct platform_device *pdev);
static int myPlattformDrv_remove(struct platform_device *pdev);

#if defined(CONFIG_OF)
    //! filter for the device tree class
    static struct of_device_id gMyPlattformDrvdtsFilter[] = {
        {.compatible = "myPlatformDevice"},
        {}
    };
    MODULE_DEVICE_TABLE(of, gMyPlattformDrvdtsFilter);
#else
    #define gmyPlattformDrvdtsFilter (NULL)
#endif

static struct platform_device *MyPlattformDrv_devs[] = {
};

static struct platform_driver myPlattformDrv_driver = {
        .driver = {
            .name   = dMyPlattformDrvdriver,
            .owner  = THIS_MODULE,
            .of_match_table =  of_match_ptr(gMyPlattformDrvdtsFilter),
        },
        .probe      = myPlattformDrv_probe,
        .remove     = myPlattformDrv_remove,
};


// char device

static dev_t gMyCharDev;
static struct class *gMyCharDevClass;
static struct cdev gMyCharDev_cdev;

static int dev_open      (struct inode *, struct file *);
static int dev_release   (struct inode *, struct file *);
static ssize_t dev_read  (struct file *, char *, size_t, loff_t *);
static ssize_t dev_write (struct file *, const char *, size_t, loff_t *);

static const struct file_operations gMyCharDevOps =
{
    .read    = dev_read,
    .open    = dev_open,
    .write   = dev_write,
    .release = dev_release
};


//! looks up for the gpio name and request it
static int get_gpio(struct platform_device *pdev, const char * name, int * pGPIOnum)
{
    int n,i;
    int r;
    struct device_node * pDN;

    TRACE("look at %s for %s ...", pdev->name, name);
    // reset return value
    *pGPIOnum = 0;

    // parse device tree

    // get device tree entries associated with the device
    pDN = of_find_node_by_name(NULL, pdev->name);

    // parse pins
    n = of_gpio_named_count(pDN, name);
    if (n <= 0) {
        TRACE("no gpios found");
        return -1;
    }
    for (i = 0; i < n; i++) {
        // get pin number
        *pGPIOnum = of_get_named_gpio(pDN,name, i);
        if (*pGPIOnum == -EPROBE_DEFER) {
            return r;
        }
        // check if pin number is valid
        if (gpio_is_valid(*pGPIOnum)) {
            // yes
            // request pin
            r = devm_gpio_request(&pdev->dev, *pGPIOnum, name);
            if (r) {
                return r;
            } else {
                r = gpio_direction_output(*pGPIOnum, 0);
                }
                if (r) return r;
            }
        }
    }
    return 0;
}

//! probes the platform driver
static int myPlattformDrv_probe(struct platform_device *pdev)
{
    struct TMyDrvDrvData *priv;
    int i,j,r,gpioNum, ret;

    KMSG_PINFO("probe my driver ...");

    priv = kzalloc(sizeof(*priv), GFP_KERNEL);
    if (!priv) {
        KMSG_PERR("Failed to allocate memory for the private data structure");
        return -ENOMEM;
    }

    priv->pdev = pdev;
    platform_set_drvdata(pdev, priv);

    TRACE("setup gpios ...");
    r = get_gpio(pdev, "pin1", &gpioNum);
    if (r) {
        KMSG_PERR("Failed to find gpio \"pin1\" in device tree");
    }
    // save number
    priv->gpiod_pin1 = gpioNum;

    // create "pin1" debugfs entry
    debugfs_create_u32("pin1", S_IRUGO, gmyPlattformDrvDebugfsRootDir, &priv->Pin1);

    r = get_gpio(pdev, "pin2", &gpioNum);
    if (r) {
        KMSG_PERR("Failed to find gpio \"pin2\" in device tree");
    }
    // save number
    priv->gpiod_pin2 = gpioNum;

    // create "pin2" debugfs entry
    debugfs_create_u32("pin1", S_IRUGO, gmyPlattformDrvDebugfsRootDir, &priv->Pin2);


    // create device class
    TRACE("create myCharDev char device class");

    // create char dev region
    ret = alloc_chrdev_region(&gMyCharDev, 0, 1, "myCharDev");
    if( ret < 0) {
        KMSG_PERR("alloc_chrdev_region error %i", ret);
        goto error;
    }

    // create device class
    if((gMyCharDevClass = class_create(THIS_MODULE, dSEK4DevClass)) == NULL)
    {
        KMSG_PERR("class_create error");
        goto error_classCreate;
    }

    if(NULL == device_create(gMyCharDevClass, NULL, gMyCharDev, NULL, "myCharDev"))
    {
        KMSG_PERR("device_create error");
        goto error_deviceCreate;
    }

    cdev_init(&gMyCharDev_cdev, &gMyCharDevOps);
    ret = cdev_add(&gMyCharDev_cdev, gMyCharDev, 1);
    if(-1 == ret) {
        KMSG_PERR("cdev_add error %i", ret);
        goto error_device_add;
        return -1;
    }
    TRACE("added myCharDev char device");

    return 0;
// error handling block
error_std:
error_device_add:
    device_destroy(gMyCharDevClass, gMyCharDev);
error_deviceCreate:
    class_destroy(gMyCharDevClass);
error_classCreate:
    unregister_chrdev_region(gMyCharDev, 1);
error:
    return -1;
}