Driver Source Code
The following implementation utilizes the platform bus architecture to manage GPIO-based LEDs. It establihses a character device interface and parses device tree information during the probe phase.
#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/of_gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/slab.h>
#include "led_control.h"
static struct gpio_desc *led_pins[3];
static struct timer_list blink_timer;
static struct device_node *tree_node;
static unsigned int dev_major = 0;
static unsigned int dev_minor = 0;
static dev_t device_id;
static struct cdev *led_cdev;
static char kernel_buffer[128];
static struct class *led_class;
static struct device *led_device;
static struct resource *mem_resource;
static unsigned int interrupt_number;
static int led_device_open(struct inode *inode, struct file *filp)
{
dev_t dev_id = inode->i_rdev;
filp->private_data = (void *)(MINOR(dev_id));
printk(KERN_INFO "%s:%s:%d\n", __FILE__, __func__, __LINE__);
return 0;
}
static ssize_t led_device_read(struct file *filp, char __user *buf, size_t count, loff_t *ppos)
{
printk(KERN_INFO "%s:%s:%d\n", __FILE__, __func__, __LINE__);
unsigned long missing;
if (count > sizeof(kernel_buffer))
count = sizeof(kernel_buffer);
missing = copy_to_user(buf, kernel_buffer, count);
if (missing) {
printk(KERN_ERR "copy_to_user failed\n");
return -EFAULT;
}
return 0;
}
static ssize_t led_device_write(struct file *filp, const char __user *buf, size_t count, loff_t *ppos)
{
unsigned long missing;
if (count > sizeof(kernel_buffer))
count = sizeof(kernel_buffer);
missing = copy_from_user(kernel_buffer, buf, count);
if (missing) {
printk(KERN_ERR "copy_from_user failed\n");
return -EFAULT;
}
return 0;
}
static long led_device_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
int led_index;
int ret = copy_from_user(&led_index, (void __user *)arg, sizeof(int));
if (ret) return -EFAULT;
if (led_index < 0 || led_index > 2) return -EINVAL;
switch (cmd) {
case CMD_LED_ON:
gpiod_set_value(led_pins[led_index], 1);
break;
case CMD_LED_OFF:
gpiod_set_value(led_pins[led_index], 0);
break;
default:
return -EINVAL;
}
return 0;
}
static int led_device_release(struct inode *inode, struct file *filp)
{
printk(KERN_INFO "%s:%s:%d\n", __FILE__, __func__, __LINE__);
return 0;
}
static const struct file_operations led_fops = {
.unlocked_ioctl = led_device_ioctl,
.open = led_device_open,
.read = led_device_read,
.write = led_device_write,
.release = led_device_release,
};
static int platform_led_probe(struct platform_device *pdev)
{
printk(KERN_INFO "%s:%s:%d\n", __FILE__, __func__, __LINE__);
mem_resource = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!mem_resource) {
printk(KERN_ERR "Failed to get MEM resource\n");
return -EFAULT;
}
printk(KERN_INFO "MEM resource start: %x\n", mem_resource->start);
interrupt_number = platform_get_irq(pdev, 0);
if (interrupt_number < 0) {
printk(KERN_ERR "Failed to get IRQ resource\n");
return interrupt_number;
}
printk(KERN_INFO "IRQ number: %d\n", interrupt_number);
int ret;
led_cdev = cdev_alloc();
if (!led_cdev) return -ENOMEM;
printk(KERN_INFO "cdev allocated\n");
cdev_init(led_cdev, &led_fops);
ret = alloc_chrdev_region(&device_id, 0, 3, "platform_led");
if (ret) {
printk(KERN_ERR "Failed to allocate char dev region\n");
goto fail_alloc;
}
dev_major = MAJOR(device_id);
dev_minor = MINOR(device_id);
printk(KERN_INFO "Char dev region allocated\n");
ret = cdev_add(led_cdev, device_id, 3);
if (ret) {
printk(KERN_ERR "Failed to add cdev\n");
goto fail_add;
}
printk(KERN_INFO "cdev added\n");
led_class = class_create(THIS_MODULE, "led_class");
if (IS_ERR(led_class)) {
ret = -PTR_ERR(led_class);
printk(KERN_ERR "Failed to create class\n");
goto fail_class;
}
printk(KERN_INFO "Class created\n");
for (int i = 0; i < 3; i++) {
led_device = device_create(led_class, NULL, MKDEV(dev_major, i), NULL, "platform_led%d", i);
if (IS_ERR(led_device)) {
ret = -PTR_ERR(led_device);
printk(KERN_ERR "Failed to create device %d\n", i);
goto fail_device;
}
}
printk(KERN_INFO "Devices created\n");
// Initialize GPIOs
for (int i = 0; i < 3; i++) {
char prop_name[16];
snprintf(prop_name, sizeof(prop_name), "led%d-gpio", i);
led_pins[i] = gpiod_get_from_of_node(pdev->dev.of_node, prop_name, 0, GPIOD_OUT_LOW, NULL);
if (IS_ERR(led_pins[i])) {
printk(KERN_ERR "Failed to get GPIO for %s\n", prop_name);
ret = -ENXIO;
goto fail_gpio;
}
}
printk(KERN_INFO "GPIOs initialized\n");
return 0;
fail_gpio:
while (--i >= 0) gpiod_put(led_pins[i]);
fail_device:
while (--i >= 0) device_destroy(led_class, MKDEV(dev_major, i));
class_destroy(led_class);
fail_class:
cdev_del(led_cdev);
fail_add:
unregister_chrdev_region(device_id, 3);
fail_alloc:
kfree(led_cdev);
return ret;
}
static int platform_led_remove(struct platform_device *pdev)
{
printk(KERN_INFO "%s:%s:%d\n", __FILE__, __func__, __LINE__);
for (int i = 0; i < 3; i++) {
gpiod_put(led_pins[i]);
device_destroy(led_class, MKDEV(dev_major, i));
}
class_destroy(led_class);
cdev_del(led_cdev);
unregister_chrdev_region(device_id, 3);
kfree(led_cdev);
del_timer(&blink_timer);
return 0;
}
static const struct of_device_id led_of_match[] = {
{ .compatible = "vendor,platform-led" },
{ }
};
static struct platform_driver platform_led_driver = {
.probe = platform_led_probe,
.remove = platform_led_remove,
.driver = {
.name = "platform_led_drv",
.of_match_table = led_of_match,
},
};
module_platform_driver(platform_led_driver);
MODULE_LICENSE("GPL");
Header File Definitions
#ifndef _LED_CONTROL_H
#define _LED_CONTROL_H
#include <linux/ioctl.h>
#define CMD_LED_ON _IOW('L', 1, int)
#define CMD_LED_OFF _IOW('L', 0, int)
#endif
User Space Application
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include "led_control.h"
int main(int argc, char *argv[])
{
int fd, choice, led_id;
fd = open("/dev/platform_led0", O_RDWR);
if (fd < 0) {
perror("Failed to open device");
exit(EXIT_FAILURE);
}
while (1) {
printf("Select Action: 0 (OFF), 1 (ON)\n> ");
scanf("%d", &choice);
printf("Select LED ID: 0, 1, 2\n> ");
scanf("%d", &led_id);
if (choice == 1) {
ioctl(fd, CMD_LED_ON, &led_id);
} else {
ioctl(fd, CMD_LED_OFF, &led_id);
}
}
close(fd);
return 0;
}