I’m trying to make a kernel module, using a Raspberry PI 2, to simply control a led as part of a school project.
“echo 0 > /dev/gpio-driver” -> Led OFF
“echo 1 > /dev/gpio-driver” -> Led On
However, my code isn’t 100% correct, because I load the module and the led is always on, when I do
“echo 0 > /dev/gpio-driver” the led turn off but after a few seconds turn on again.
I don’t know where the error is so I posted what I have so far.
Thanks a lot,
Eddy
/*
* Kernel modules for Raspberry PI
* Creation/Analysis of kernel modules for Raspberry PI
*/
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/gpio.h>
MODULE_LICENSE ("GPL");
MODULE_AUTHOR ("");
MODULE_DESCRIPTION ("Simple kernel module to controle an LED");
#define DEVICE_NAME "gpio-chrdev"
#define DEVICE_FILE_NAME "gpio-file"
#define CLASS "gpio-project"
#define MAX_FILE_SIZE 3
static int size; //Initialize to 0 in module_init
static char data[MAX_FILE_SIZE];
static int __init gpio_driver_init (void);
static void __exit gpio_driver_exit (void);
static int gpio_driver_open (struct inode *inode, struct file *filp);
static int gpio_driver_release (struct inode *inode, struct file *filp);
static ssize_t gpio_driver_read (struct file *filp, char __user * buf,
size_t count, loff_t * offset);
static ssize_t gpio_driver_write (struct file *filp, const char __user * buf,
size_t count, loff_t * offset);
static dev_t devnr;
//static struct device *device;
static struct cdev LED_cdev;
/* Notify udev from the module - first create a virtual device class */
static struct class *gpio_class;
static int deviceopen = 0;
module_init(gpio_driver_init);
module_exit(gpio_driver_exit);
static struct file_operations LED_fops = {
.owner = THIS_MODULE,
.read = gpio_driver_read,
.write = gpio_driver_write,
.open = gpio_driver_open,
.release = gpio_driver_release,
};
/* This function is called when this module is loaded into the kernel */
static int __init gpio_driver_init(void){
/* Device file creation */
/* Alocate device number */
/* The major number will be chosen dynamically and returned in dev
* Returns zero or a negative erro code */
if(alloc_chrdev_region(&devnr, 0, 1, DEVICE_FILE_NAME) < 0) {
printk("Device class can't be created\n");
return -1;
}
/* Create device class */
/* Create a new class for this device. It will be visible in /sys/class */
if ((gpio_class = class_create(THIS_MODULE, CLASS)) == NULL) {
printk("Device class can't be created\n");
goto classerror;
}
/* Inicialize device*/
cdev_init (&LED_cdev, &LED_fops);
/* Activate the device */
if ((cdev_add (&LED_cdev, devnr, 1)) == -1){
printk("Unable to register the device\n");
goto validerror;
}
/* Create device file */
/* Device info can be viewd under /sys/class/arcom-project */
if ((device_create (gpio_class, NULL, devnr, NULL, DEVICE_NAME)) == NULL){
printk("Can't create device file\n");
goto fileerror;
}
/*
* GPIO configurations
* ********************/
/* Check if GPIO is valid */
if(gpio_is_valid(4) == false){
printk("GPIO is not valid\n");
goto validerror;
}
/* GPIO 4 Request */
if(gpio_request(4, "gpio-4") < 0 ){
printk("Can't request GPIO 4\n");
goto gpio4requesterror;
}
/* Configure GPIO as output*/
gpio_direction_output(4, 0);
//gpio_export(4, false);
return 0;
validerror:
device_destroy(gpio_class, devnr);
gpio4requesterror:
gpio_free(4);
fileerror:
class_destroy(gpio_class);
classerror:
unregister_chrdev_region(devnr, 1);
return -1;
}
static int gpio_driver_open(struct inode *inode, struct file *filp)
{
if (deviceopen)
{
printk(KERN_WARNING "Already open\n");
return -EBUSY;
}
try_module_get(THIS_MODULE);
deviceopen = 1;
return 0;
}
int gpio_driver_release(struct inode *inode, struct file *filp)
{
deviceopen = 0;
module_put(THIS_MODULE);
return 0;
}
/* Read data out of the buffer */
static ssize_t gpio_driver_read(struct file *filp, char __user *buf, size_t count, loff_t * offset)
{
int i;
int last = *offset + count;
if (last > size)
{
last = size;
}
i = last - *offset;
if (i == 0)
{
return 0;
}
if (copy_to_user (buf, data + *offset, i))
{
return -EFAULT;
}
return i;
}
/* Write data to buffer */
static ssize_t gpio_driver_write(struct file *filp, const char __user *buf, size_t count, loff_t * offset)
{
int i, n;
int last = *offset + count;
if (last > MAX_FILE_SIZE)
last = MAX_FILE_SIZE;
i = last - *offset;
n = copy_from_user (data + *offset, buf, i);
if (n != 0)
{
pr_warn ("Could not copy %d bytes\n", n);
return -EFAULT;
}
*offset += i;
if (size < *offset) //keep data from previous accesses
size = *offset;
/* Setting the LED - 0 or 1*/
if (data[0] == '0'){
gpio_set_value(4,0);
}
else if (data[0] == '1'){
gpio_set_value(4,1);
}
else {
printk("Invalid input\n");
}
return i;
}
/*
static void my_timer_func(struct timer_list *unused){
led_status = ~led_status;
outb(led_status, 0x378);
my_timer.expires += blink_delay;
add_timer(&my_timer);
}
*/
void gpio_driver_exit(void){
//gpio_unexport(4);
gpio_free(4);
cdev_del(&LED_cdev);
device_destroy(gpio_class, devnr);
class_destroy(gpio_class);
unregister_chrdev_region(devnr, 1);
printk("Live long and prosper\n");
}
EDIT:
Hello,
I’ve redone the program, also the module w1-gpio was loaded and messing with the module I tried to implement, so after the changes in the code and remove the module everything worked fine
/*
* Kernel modules for Raspberry PI
* Creation/Analysis of kernel modules for Raspberry PI
*/
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/gpio.h>
MODULE_LICENSE ("GPL");
MODULE_AUTHOR ("");
MODULE_DESCRIPTION ("Simple kernel module to controle an LED");
#define MAX_FILE_SIZE 3
static int size; //Initialize to 0 in module_init
//static char data[MAX_FILE_SIZE];
static int __init gpio_driver_init (void);
static void __exit gpio_driver_exit (void);
static int gpio_driver_open (struct inode *inode, struct file *filp);
static int gpio_driver_release (struct inode *inode, struct file *filp);
static ssize_t gpio_driver_read (struct file *filp, char __user * buf,
size_t count, loff_t * offset);
static ssize_t gpio_driver_write (struct file *filp, const char __user * buf,
size_t count, loff_t * offset);
static dev_t devnr;
static struct cdev LED_cdev;
/* Notify udev from the module - first create a virtual device class */
static struct class *gpio_class;
static int deviceopen = 0;
module_init(gpio_driver_init);
module_exit(gpio_driver_exit);
static struct file_operations LED_fops = {
.owner = THIS_MODULE,
.read = gpio_driver_read,
.write = gpio_driver_write,
.open = gpio_driver_open,
.release = gpio_driver_release,
};
/* This function is called when this module is loaded into the kernel */
static int __init gpio_driver_init(void){
/* Device file creation */
/* Alocate device number */
/* The major number will be chosen dynamically and returned in dev
* Returns zero or a negative erro code */
if((alloc_chrdev_region(&devnr, 0, 1, "project_dev")) < 0) {
printk("Device class can't be created\n");
goto err_alloc;
}
/* Create cdev structure */
cdev_init (&LED_cdev, &LED_fops);
/* Add character device to the system */
if ((cdev_add (&LED_cdev, devnr, 1)) == -1){
printk("Unable to register the device\n");
device_destroy(gpio_class, devnr);
}
/* Create device class */
/* Create a new class for this device. It will be visible in /sys/class */
if ((gpio_class = class_create(THIS_MODULE, "project_class")) == NULL) {
printk("Device class can't be created\n");
goto err_class;
}
/* Create device file */
/* Device info can be viewd under /sys/class/arcom-project */
if ((device_create (gpio_class, NULL, devnr, NULL, "project_device")) == NULL){
printk("Can't create device file\n");
goto err_device;
}
size = 0;
/* GPIO configuration */
/* Checking if GPIO is valid */
if(gpio_is_valid(4) == false){
printk("GPIO is no valid\n");
goto err_device;
}
/* Requesting the GPIO */
if(gpio_request(4, "gpio-4") < 0){
printk("GPIO not requested\n");
goto err_gpio;
}
/* Configure GPIO as output */
if(gpio_direction_output(4,0)){
printk("Can not set GPIO to output\n");
goto err_gpio;
}
/* For debugging purposes we can export the GPIO which is allocated
* usig the gpio_request - /sys/class/gpio/gpio* */
gpio_export(4, false);
printk("Sao 4.30 da manha... e eu ainda aqui!\n");
return 0;
err_gpio:
gpio_free(4);
err_device:
device_destroy (gpio_class, devnr);
err_class:
class_destroy(gpio_class);
err_alloc:
unregister_chrdev_region(devnr, 1);
return -1;
}
static int gpio_driver_open(struct inode *inode, struct file *filp)
{
if (deviceopen)
{
printk(KERN_WARNING "Already open\n");
return -EBUSY;
}
try_module_get(THIS_MODULE);
deviceopen = 1;
return 0;
}
int gpio_driver_release(struct inode *inode, struct file *filp)
{
deviceopen = 0;
module_put(THIS_MODULE);
return 0;
}
/* Read data out of the buffer */
static ssize_t gpio_driver_read(struct file *filp, char __user *buf, size_t count, loff_t * offset)
{
uint8_t gpio_value = 0;
int i;
int last = *offset + count;
/* Read GPIO value */
gpio_value = gpio_get_value(4);
if (last > size)
{
last = size;
}
i = last - *offset;
if (i == 0)
{
return 0;
}
if (copy_to_user (buf, &gpio_value + *offset, i)) //VERIFICAR
{
return -EFAULT;
}
return i;
}
/* Write data to buffer */
static ssize_t gpio_driver_write(struct file *filp, const char __user *buf, size_t count, loff_t * offset)
{
char gpio_read;
int i, n;
int last = *offset + count;
if (last > MAX_FILE_SIZE)
last = MAX_FILE_SIZE;
i = last - *offset;
n = copy_from_user (&gpio_read + *offset, buf, i);
if (n != 0)
{
pr_warn ("Could not copy %d bytes\n", n);
return -EFAULT;
}
*offset += i;
if (size < *offset) //keep data from previous accesses
size = *offset;
/* Setting the LED - 0 or 1*/
if (n == '0'){
gpio_set_value(4,0);
}
else if (n == '1'){
gpio_set_value(4,1);
}
else {
printk("Invalid input\n");
}
return i;
}
/*
static void my_timer_func(struct timer_list *unused){
led_status = ~led_status;
outb(led_status, 0x378);
my_timer.expires += blink_delay;
add_timer(&my_timer);
}
*/
void gpio_driver_exit(void){
//gpio_unexport(4);
gpio_free(4);
device_destroy(gpio_class, devnr);
class_destroy(gpio_class);
cdev_del(&LED_cdev);
unregister_chrdev_region(devnr, 1);
printk("Live long and prosper\n");
}