1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147
| #include <linux/cdev.h> #include <linux/device.h> #include <linux/fs.h> #include <linux/gpio/consumer.h> #include <linux/module.h> #include <linux/of.h> #include <linux/platform_device.h> #include <linux/slab.h> #include <linux/uaccess.h>
#define TESTDEV_NAME "test"
struct test_dev { dev_t devid; struct cdev cdev; struct class *class; struct device *device; struct gpio_desc *gpiod; };
static int test_open(struct inode *inode, struct file *file) { struct test_dev *led;
led = container_of(inode->i_cdev, struct test_dev, cdev); file->private_data = led;
return 0; }
static ssize_t test_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { struct test_dev *led = file->private_data; char kbuf[16]; size_t len; int ret, value;
if (!count) return 0;
len = min(count, sizeof(kbuf) - 1); if (copy_from_user(kbuf, buf, len)) return -EFAULT;
kbuf[len] = '\0';
ret = kstrtoint(kbuf, 0, &value); if (ret) return ret;
gpiod_set_value_cansleep(led->gpiod, !!value);
return count; }
static const struct file_operations test_fops = { .owner = THIS_MODULE, .open = test_open, .write = test_write, };
static int test_probe(struct platform_device *pdev) { struct test_dev *led; int ret;
led = devm_kzalloc(&pdev->dev, sizeof(*led), GFP_KERNEL); if (!led) return -ENOMEM;
led->gpiod = devm_gpiod_get(&pdev->dev, "led", GPIOD_OUT_HIGH); if (IS_ERR(led->gpiod)) { dev_err(&pdev->dev, "failed to get led gpio\n"); return PTR_ERR(led->gpiod); }
ret = alloc_chrdev_region(&led->devid, 0, 1, TESTDEV_NAME); if (ret) return ret;
cdev_init(&led->cdev, &test_fops); led->cdev.owner = THIS_MODULE;
ret = cdev_add(&led->cdev, led->devid, 1); if (ret) goto err_unregister_chrdev;
led->class = class_create(THIS_MODULE, TESTDEV_NAME); if (IS_ERR(led->class)) { ret = PTR_ERR(led->class); goto err_cdev_del; }
led->device = device_create(led->class, NULL, led->devid, NULL, TESTDEV_NAME); if (IS_ERR(led->device)) { ret = PTR_ERR(led->device); goto err_class_destroy; }
platform_set_drvdata(pdev, led); dev_info(&pdev->dev, "test probe ok\n");
return 0;
err_class_destroy: class_destroy(led->class); err_cdev_del: cdev_del(&led->cdev); err_unregister_chrdev: unregister_chrdev_region(led->devid, 1); return ret; }
static int test_remove(struct platform_device *pdev) { struct test_dev *led = platform_get_drvdata(pdev);
device_destroy(led->class, led->devid); class_destroy(led->class); cdev_del(&led->cdev); unregister_chrdev_region(led->devid, 1);
return 0; }
static const struct of_device_id test_of_match[] = { { .compatible = "atk,test" }, { } }; MODULE_DEVICE_TABLE(of, test_of_match);
static struct platform_driver test_driver = { .probe = test_probe, .remove = test_remove, .driver = { .name = TESTDEV_NAME, .of_match_table = test_of_match, }, };
module_platform_driver(test_driver);
MODULE_LICENSE("GPL"); MODULE_AUTHOR("Jvle"); MODULE_DESCRIPTION("GPIO descriptor based test driver");
|