linux模塊驅動編寫示例

2023-05-04

關鍵字:


該驅動可通過dts配置屬性,可在/dev創建節點,支持對/dev下的節點進行標準IO讀寫以及ioctl讀寫。

dts如下:

ir_cut {
    status = "okay";
    compatible = "chorm, ir-cut";
    gpios = <&gpf 0 GPIO_ACTIVE_HIGH>, <&gpf 1 GPIO_ACTIVE_LOW>;
};

 

驅動代碼如下:

#include <linux/uaccess.h>
#include <linux/cdev.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/platform_device.h>

enum GPIO_FLAGS {
    ACTIVE_HIGH = 0x00,
    ACTIVE_LOW,
};

struct Pin {
    int gpio;
    enum GPIO_FLAGS flag;
};

struct IrCut {
    struct Pin in1;
    struct Pin in2;
    dev_t devid;
    struct cdev* cdev;
    struct class* clz;
    struct device* dev;
};

struct IrCut* gircut;

static int ircut_open(struct inode* node, struct file* fp)
{
    printk("ircut_open()\n");
    int major = MAJOR(node->i_rdev);
    int minor = MINOR(node->i_rdev);
    //fp->private_data = NULL;
    return 0;
}

static int ircut_release(struct inode* node, struct file* fp)
{
    printk("ircut_release()\n");
    return 0;
}

static ssize_t ircut_read(struct file* fp, char __user* buf, size_t cnt, loff_t* offset)
{
    printk("ircut_read()\n");
    int in1 = gpio_get_value(gircut->in1.gpio);
    int in2 = gpio_get_value(gircut->in2.gpio);
    if(gircut->in1.flag == ACTIVE_LOW)
    {
        in1 = !in1;
    }

    if(gircut->in2.flag == ACTIVE_LOW)
    {
        in2 = !in2;
    }

    printk("in1:%s, in2:%s\n", (in1 ? "active" : "inactive"), (in2 ? "active" : "inactive"));

    return 0;
}

static ssize_t ircut_write(struct file* fp, const char __user* buf, size_t len, loff_t* offset)
{
    printk("ircut_write()\n");
    if(len != 4)
    {
        printk("Usage:\n\t1 0\n\tactive in1, inactive in2\n");
        return len;
    }

    char kbuf[5] = {0};
    int ret = copy_from_user(kbuf, buf, len);
    if(ret)
    {
        printk("io error\n");
        return -1;
    }

    int in1, in2;
    if(kbuf[0] == '0' || kbuf[0] == '1')
    {
        in1 = kbuf[0] - '0';
        if(kbuf[1] == ' ')
        {
            if(kbuf[2] == '0' || kbuf[2] == '1')
            {
                in2 = kbuf[2] - '0';
                gpio_set_value(gircut->in1.gpio, (gircut->in1.flag == ACTIVE_LOW ? !in1 : in1));
                gpio_set_value(gircut->in2.gpio, (gircut->in2.flag == ACTIVE_LOW ? !in2 : in2));
            }
        }
    }

    return len;
}

static long ircut_ioctl(struct file* fp, unsigned int cmd, unsigned long arg)
{
    printk("ircut_ioctl()\n");

    unsigned long __user* argp = (unsigned long __user*)arg;
    void* data_struct = (void*)kzalloc(10, 1);
    int ret = copy_from_user(data_struct, argp, 10);

    ret = copy_to_user(arg, data_struct, 10);

    return 0;
}

const struct file_operations ircut_fops = {
    .owner = THIS_MODULE,
    .open = ircut_open,
    .release = ircut_release,
    .read = ircut_read,
    .write = ircut_write,
    .unlocked_ioctl = ircut_ioctl
};

static int ircut_probe(struct platform_device *pdev)
{
    int ret;
    struct device_node* node;
    struct IrCut* ircut;
    gircut = NULL;
    printk("ircut_probe()\n");

    //initialize, parse the dts
    node = pdev->dev.of_node;
    ircut = (struct IrCut*)kzalloc(sizeof(struct IrCut), GFP_KERNEL);
    if(ircut == NULL)
    {
        printk("[fail] No mem\n");
        return ENOMEM;
    }
    memset(ircut, 0, sizeof(struct IrCut));
 
    enum of_gpio_flags flg;
    ret = of_get_named_gpio_flags(node, "gpios", 0, &flg);
    if(ret < 0)
    {
        printk("Can't get gpio[0]\n");
        goto ERR1;
    }
    ircut->in1.gpio = ret;
    ircut->in1.flag = (flg == 0 ? ACTIVE_HIGH : ACTIVE_LOW);
    
    ret = of_get_named_gpio_flags(node, "gpios", 1, &flg);
    if(ret < 0)
    {
        printk("Can't get gpio[1]\n");
        goto ERR1;
    }
    ircut->in2.gpio = ret;
    ircut->in2.flag = (flg == 0 ? ACTIVE_HIGH : ACTIVE_LOW);

    //setup, request the gpios
    ret = gpio_is_valid(ircut->in1.gpio);
    if(!ret)
    {
        printk("gpio[0] invalid\n");
        goto ERR1;
    }

    ret = gpio_request(ircut->in1.gpio, "ircut-in1");
    if(ret < 0)
    {
        printk("request gpio[0] failed:%d\n", ret);
        goto ERR1;
    }

    ret = gpio_direction_output(ircut->in1.gpio, (ircut->in1.flag == ACTIVE_HIGH ? 0 : 1)); //inactive in default
    if(ret < 0)
    {
        printk("gpio[0] set failed:%d\n", ret);
        goto ERR2;
    }

    ret = gpio_is_valid(ircut->in2.gpio);
    if(!ret)
    {
        printk("gpio[1] invalid\n");
        goto ERR2;
    }

    ret = gpio_request(ircut->in2.gpio, "ircut-in2");
    if(ret < 0)
    {
        printk("request gpio[1] failed:%d\n", ret);
        goto ERR2;
    }

    ret = gpio_direction_output(ircut->in2.gpio, (ircut->in2.flag == ACTIVE_HIGH ? 0 : 1)); //inactive too
    if(ret < 0)
    {
        printk("gpio[1] set failed:%d\n", ret);
        goto ERR3;
    }

    printk("in1.gpio:%d, flag:%d\n", ircut->in1.gpio, ircut->in1.flag);
    printk("in2.gpio:%d, flag:%d\n", ircut->in2.gpio, ircut->in2.flag);

    //interface, provide operate handle
    ret = alloc_chrdev_region(&ircut->devid, 0, 1, "ir-cut");
    if(ret)
    {
        printk("alloc devid failed:%d\n", ret);
        goto ERR3;
    }

    ircut->cdev = cdev_alloc();
    if(ircut->cdev == NULL)
    {
        printk("alloc cdev failed\n");
        goto ERR4;
    }

    cdev_init(ircut->cdev, &ircut_fops);

    ret = cdev_add(ircut->cdev, ircut->devid, 1);
    if(ret)
    {
        printk("cdev add failed:%d\n", ret);
        goto ERR4;
    }

    ircut->clz = class_create(THIS_MODULE, "ircut_clz");
    if(ircut->clz == NULL)
    {
        printk("create class failed\n");
        goto ERR4;
    }

    ircut->dev = device_create(ircut->clz, NULL, ircut->devid, NULL, "ircut_dev");
    if(ircut->dev == NULL)
    {
        printk("create device failed\n");
        goto ERR5;
    }


    pdev->dev.platform_data = ircut; //record the private data
    gircut = ircut;
    printk("ircut init success\n");

    return 0;

ERR5:
    class_destroy(ircut->clz);

ERR4:
    unregister_chrdev_region(ircut->devid, 1);

ERR3:
    gpio_free(ircut->in2.gpio);

ERR2:
    gpio_free(ircut->in1.gpio);

ERR1:
    kfree(ircut);

    return EIO;
}

static int ircut_remove(struct platform_device *pdev)
{
    printk("ircut_remove()\n");

    if(pdev->dev.platform_data == NULL)
    {
        return 0;
    }

    struct IrCut* ircut = (struct IrCut*)pdev->dev.platform_data;
    gpio_free(ircut->in1.gpio);
    gpio_free(ircut->in2.gpio);
    printk("gpio freed\n");

    device_destroy(ircut->clz, ircut->devid);
    class_destroy(ircut->clz);
    cdev_del(ircut->cdev);
    unregister_chrdev_region(ircut->devid, 1);
    printk("cdev unregistered\n");

    gircut = NULL;

    return 0;
}

static const struct of_device_id ircut_of_table[] = {
    { .compatible = "chorm, ir-cut" },
    {}
};

static struct platform_driver drv_stu = {
    .probe = ircut_probe,
    .remove = ircut_remove,
    .driver = {
        .name = "ir-cut",
        .of_match_table = ircut_of_table,
    },
};
module_platform_driver(drv_stu);

MODULE_AUTHOR("[email protected]");
MODULE_DESCRIPTION("Driver for learing");
MODULE_LICENSE("GPL");

 

更多示例程序參見:

  https://files.cnblogs.com/files/chorm590/linux%E6%A8%A1%E5%9D%97%E9%A9%B1%E5%8A%A8%E7%A4%BA%E4%BE%8B%E4%BB%A3%E7%A0%81_202305041840.zip 

 


 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章