Skip to content

Instantly share code, notes, and snippets.

@gnitnaw
Last active June 28, 2022 05:32
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save gnitnaw/b116f358fa688897fe00 to your computer and use it in GitHub Desktop.
Save gnitnaw/b116f358fa688897fe00 to your computer and use it in GitHub Desktop.
module to control 3 LED
/*
* kernel module for LED
*
* Author:
* Wan-Ting CHEN (wanting@gmail.com)
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h> // file_operations
#include <linux/cdev.h> // Char Device Registration
#include <linux/gpio.h> // gpio control
#include <linux/delay.h> // mdelay
#include <linux/device.h> // class_create
#include <linux/types.h> // dev_t
#include <asm/uaccess.h> // copy_from_user
#define LED_DRIVER_NAME "LED"
#define BUF_SIZE 5
static dev_t driverno ; //Default driver number
static struct cdev *gpio_cdev;
static struct class *gpio_class;
static struct gpio leds[] = {
{ 2, GPIOF_OUT_INIT_LOW, "LED_0" },
{ 3, GPIOF_OUT_INIT_LOW, "LED_1" },
{ 4, GPIOF_OUT_INIT_LOW, "LED_2" },
};
static int gpio0 = 2;
static int gpio1 = 3;
static int gpio2 = 4;
// Command line paramaters for gpio pin and driver major number
module_param(gpio0, int, S_IRUGO);
MODULE_PARM_DESC(gpio0, "GPIO-0 pin to use");
module_param(gpio1, int, S_IRUGO);
MODULE_PARM_DESC(gpio1, "GPIO-1 pin to use");
module_param(gpio2, int, S_IRUGO);
MODULE_PARM_DESC(gpio2, "GPIO-2 pin to use");
// Forward declarations
static ssize_t write_LED( struct file *, const char *,size_t,loff_t *);
//Operations that can be performed on the device
static struct file_operations fops = {
.owner = THIS_MODULE,
.write = write_LED
};
static ssize_t write_LED( struct file *filp, const char *buf,size_t count,loff_t *f_pos){
char kbuf[BUF_SIZE];
unsigned int len=1, gpio;
gpio = iminor(filp->f_path.dentry->d_inode);
printk(KERN_INFO LED_DRIVER_NAME " GPIO: LED_%d is modified. \n", gpio);
len = count < BUF_SIZE ? count-1 : BUF_SIZE-1;
if(copy_from_user(kbuf, buf, len) != 0) return -EFAULT;
kbuf[len] = '\0';
printk(KERN_INFO LED_DRIVER_NAME " Request from user: %s\n", kbuf);
if (strcmp(kbuf, "1") == 0) {
printk(KERN_ALERT LED_DRIVER_NAME " LED_%d switch On \n", gpio);
gpio_set_value(leds[gpio].gpio, 1);
} else if (strcmp(kbuf, "0") == 0) {
printk(KERN_ALERT LED_DRIVER_NAME " LED_%d switch Off \n", gpio);
gpio_set_value(leds[gpio].gpio, 0);
}
msleep(100);
return count;
}
static void blink(void)
{
int i;
printk(KERN_INFO LED_DRIVER_NAME " %s\n", __func__);
for(i = 0; i < ARRAY_SIZE(leds); i++) {
if(i - 1 >= 0) {
gpio_set_value(leds[i - 1].gpio, 0);
}
gpio_set_value(leds[i].gpio, 1);
msleep(500);
}
gpio_set_value(leds[i - 1].gpio, 0);
printk(KERN_INFO LED_DRIVER_NAME " blink ended\n");
}
static void modify_gpio(void) {
int i;
leds[0].gpio = gpio0;
leds[1].gpio = gpio1;
leds[2].gpio = gpio2;
for (i=0; i<ARRAY_SIZE(leds); ++i) {
printk(KERN_INFO LED_DRIVER_NAME " Set %s to GPIO %i \n", leds[i].label, leds[i].gpio);
}
}
static int __init LED_init_module(void)
{
int ret, i;
// Set gpio according to the parameters you give
printk(KERN_INFO LED_DRIVER_NAME " %s\n", __func__);
modify_gpio();
printk(KERN_INFO LED_DRIVER_NAME " gpio_request_array \n");
ret = gpio_request_array(leds, ARRAY_SIZE(leds));
if(ret < 0)
{
printk(KERN_ERR LED_DRIVER_NAME " Unable to request GPIOs: %d\n", ret);
goto exit_gpio_request;
}
// Get driver number
printk(KERN_INFO LED_DRIVER_NAME " alloc_chrdev_region \n");
ret=alloc_chrdev_region(&driverno,0,ARRAY_SIZE(leds),LED_DRIVER_NAME);
if (ret) {
printk(KERN_EMERG LED_DRIVER_NAME " alloc_chrdev_region failed\n");
goto exit_gpio_request;
}
printk(KERN_INFO LED_DRIVER_NAME " DRIVER No. of %s is %d\n", LED_DRIVER_NAME, MAJOR(driverno));
printk(KERN_INFO LED_DRIVER_NAME " cdev_alloc\n");
gpio_cdev = cdev_alloc();
if(gpio_cdev == NULL)
{
printk(KERN_EMERG LED_DRIVER_NAME " Cannot alloc cdev\n");
ret = -ENOMEM;
goto exit_unregister_chrdev;
}
printk(KERN_INFO LED_DRIVER_NAME " cdev_init\n");
cdev_init(gpio_cdev,&fops);
gpio_cdev->owner=THIS_MODULE;
printk(KERN_INFO LED_DRIVER_NAME " cdev_add\n");
ret=cdev_add(gpio_cdev,driverno,ARRAY_SIZE(leds));
if (ret)
{
printk(KERN_EMERG LED_DRIVER_NAME " cdev_add failed!\n");
goto exit_cdev;
}
printk(KERN_INFO LED_DRIVER_NAME " Play blink\n");
blink();
printk(KERN_INFO LED_DRIVER_NAME " Create class \n");
gpio_class = class_create(THIS_MODULE, LED_DRIVER_NAME);
if (IS_ERR(gpio_class))
{
printk(KERN_ERR LED_DRIVER_NAME " class_create failed\n");
ret = PTR_ERR(gpio_class);
goto exit_cdev;
}
printk(KERN_INFO LED_DRIVER_NAME " Create device \n");
for (i=0; i<ARRAY_SIZE(leds); ++i) {
if (device_create(gpio_class,NULL, MKDEV(MAJOR(driverno), MINOR(driverno)+i), NULL,leds[i].label)==NULL) {
printk(KERN_ERR LED_DRIVER_NAME " device_create failed\n");
ret = -1;
goto exit_cdev;
}
}
return 0;
exit_cdev:
cdev_del(gpio_cdev);
exit_unregister_chrdev:
unregister_chrdev_region(driverno, ARRAY_SIZE(leds));
exit_gpio_request:
gpio_free_array(leds, ARRAY_SIZE(leds));
return ret;
}
/*
* Module exit function
*/
static void __exit LED_exit_module(void)
{
int i;
printk(KERN_INFO LED_DRIVER_NAME " %s\n", __func__);
// turn all off
for(i = 0; i < ARRAY_SIZE(leds); i++) {
gpio_set_value(leds[i].gpio, 0);
device_destroy(gpio_class, MKDEV(MAJOR(driverno), MINOR(driverno) + i));
}
class_destroy(gpio_class);
cdev_del(gpio_cdev);
unregister_chrdev_region(driverno, ARRAY_SIZE(leds));
gpio_free_array(leds, ARRAY_SIZE(leds));
}
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Wan-Ting CHEN");
MODULE_DESCRIPTION("LED Kernel module");
module_init(LED_init_module);
module_exit(LED_exit_module);
obj-m += LED.o
EXTRA_CFLAGS += -I$(PWD)
all:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
/*
* Name : output_test.c
* Author : Vu Nguyen <quangngmetro@gmail.com>
* Version : 0.1
* Copyright : GPL
* Description : This is a test application which is used for testing
* GPIO output functionality of the raspi-gpio Linux device driver
* implemented for Raspberry Pi revision B platform. The test
* application first sets all the GPIO pins on the Raspberry Pi to
* output, then it sets all the GPIO pins to "high"/"low" logic
* level based on the options passed to the program from the command
* line
* Usage example:
* ./output_test 1 // Set all GPIO pins to output, high state
* ./output_test 0 // Set all GPIO pins to output, low state
*/
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#define NUM_GPIO_PINS 3
#define MAX_GPIO_NUMBER 4
#define BUF_SIZE 4
#define PATH_SIZE 20
int main(int argc, char **argv)
{
char path[PATH_SIZE], buf[BUF_SIZE];
int i = 0, fd = 0;
snprintf(path, sizeof(path), "/dev/LED_0");
fd = open(path, O_WRONLY);
if (fd < 0) {
perror("Error opening GPIO pin");
exit(EXIT_FAILURE);
}
printf("Set GPIO pins to output, logic level :\n");
strncpy(buf, "1", 1);
buf[1] = '\0';
if (write(fd, buf, sizeof(buf)) < 0) {
perror("write, set pin output");
exit(EXIT_FAILURE);
}
return EXIT_SUCCESS;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment