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("chorm@lchorm.com"); 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
+++