Suneet Times
Suneet Times

Reputation: 11

Access a GPIO AM335x board as RESET and minimum CPU usage

In my application there is a thread which takes care of RESET button that when and for how much time it was pressed; on the basis of that we take the action. Problem is to minimize CPU usage:

1.popen usage fp=popen(RESET_GPIO_VALUE,"r"); with this application have 75% CPU consumption.

  1. fopen usage fp=fopen(RESET_GPIO_VALUE,"r"); with this application have 87% CPU consumption.

  2. open usage fd = open(RESET_GPIO_VALUE,O_RDONLY); with this application have 95% CPU consumption.

Is there any method to access the GPIO with around 10-15% of CPU consumption.

Here currently in my logic I continuously check the RESET GPIO by checking its value via above define methods. As button pressed timer started and released stop the timer and calculate the difference of time. This whole action running in infinite loop.

Upvotes: 0

Views: 461

Answers (2)

Sudipta Kumar Sahoo
Sudipta Kumar Sahoo

Reputation: 1147

Okay as it is little tricky, I will tell you what are the steps required to do that.As it is really a tricky one many would not prefer to give the appropriate answer, never the less these are the step you can follow.

1.what you have to do is you can first write a kernel module. Then You can compile that to generate the .ko file. Which is a loadable kernel module. Write a loadable kernel module in which you can keep an ISR(Interrupt Service Routine) that gets executed when there is an interrupt comes to any particular GPIO. For your information ISR is also a function that automatically gets executed when there is an interrupt received to a particular GPIO.

Keep a provision in your kernel module, to accept the application layer Process ID, so that as soon as there comes an interrupt to the GPIO, the module can send a SIGNAL from Kernel layer to application Layer.

2.Copy that to your ARM target processor and then insert the module to the kernel. You can insert the module by typing

             `insmod <your_module.ko>`
  1. Check if the Module has inserted or not by typing lsmod Command on to the terminal of your AM335x target board.

3.Then you can write the user level application, where you can send the processID of the process from the application layer to the kernel layer as soon as your process starts running. SO that the kernel module you have written can get the application layer ProcessID to send the SIGNAL.

NOTE: As Linux by default uses the first 32 SIGNAL so you can use the SIGNAL number from 33 to 64 for your purpose. Here we are using SIGNAL Number 44.

Example of the Kernel Module:

#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/gpio.h>                 //! << Required for the GPIO functions
#include <linux/interrupt.h>            //! << Required for the IRQ code

#include <asm/siginfo.h>            //! << siginfo
#include <linux/rcupdate.h>         //! << rcu_read_lock
#include <linux/sched.h>            //! << find_task_by_pid_type
#include <linux/debugfs.h>
#include <linux/uaccess.h>


MODULE_LICENSE("GPL");
MODULE_AUTHOR("Sudipta Kumar Sahoo");
MODULE_DESCRIPTION("A SPI GPIO Interrupt driver for the Sitara");
MODULE_VERSION("1.0");

#define SIG_TEST 44             //! << we choose 44 as our signal number
                    //! << (real-time signals are in the range of 33 to 64)
struct dentry *file;

int gPID = 0;
int Return = 0;
struct siginfo stGInfo;
struct task_struct *stGTask;

//static unsigned int resetGPIOInterrupt = 115;   //! << hard coding the Delphino Interrupt gpio for this in BB P9_27 (GPIO115)
static unsigned int resetGPIOInterrupt = 54;
static unsigned int irqNumber;                 //! << Used to share the IRQ number within this file
static unsigned int numberPresses = 0;         //! << For information, store the number of times the SIGNAL is high.

/********************************************************************************/
/**
 * \fn    write_pid(struct file *file, const char __user *buf,
 *                                                  size_t count, loff_t *ppos)
 *
 * @brief The LKM write_pid function
 *        The static keyword restricts the visibility of the function
 *        to within this C file.
 *  
 * @return returns 0 if successful
 */
/********************************************************************************/
static ssize_t write_pid(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
{
        char mybuf[10];
        int pid = 0;
        int ret;
        struct siginfo info;
        struct task_struct *stTask;
        /* read the value from user space */
        if(count > 10)
    {
        return -EINVAL;
    }

        copy_from_user(mybuf, buf, count);      //! << Copy the Process ID from the Application Process

    /* For Global variables value assignment starts */
    /******************************************************************************************/
    //! Copy the required information to Global variables to send SIGNAL from ISR
    sscanf(mybuf, "%d", &gPID);         //! << Copy to global PID
    memset(&stGInfo, 0, sizeof(struct siginfo));
    stGInfo.si_signo = SIG_TEST;
        //! << this is bit of a trickery: SI_QUEUE is normally used by sigqueue from user space,
        //! << and kernel space should use SI_KERNEL. But if SI_KERNEL is used the real_time data
        //! << is not delivered to the user space signal handler function.
    stGInfo.si_code = SI_QUEUE;
    stGInfo.si_int = 1024;
    /*For Global variables value assignment Ends */
    /******************************************************************************************/

        /* send a SIGNAL to the process to intimate that it received the corresponding PID. */
        sscanf(mybuf, "%d", &pid);
        printk("pid = %d\n", pid);
        memset(&info, 0, sizeof(struct siginfo));
        info.si_signo = SIG_TEST;
        //! << this is bit of a trickery: SI_QUEUE is normally used by sigqueue from user space,
        //! << and kernel space should use SI_KERNEL. But if SI_KERNEL is used the real_time data
        //! << is not delivered to the user space signal handler function.
        info.si_code = SI_QUEUE;
        info.si_int = 1234;                     //! << real time signals may have 32 bits of data.

        rcu_read_lock();                //! << Get the RCU Read lock
    //t = find_task_by_pid_type(PIDTYPE_PID, pid);  //! << find the task_struct associated with this pid
        stTask = pid_task(find_pid_ns(pid, &init_pid_ns), PIDTYPE_PID);
        if(stTask == NULL)
        {
                printk("no such pid\n");
                rcu_read_unlock();
                return -ENODEV;
        }
        rcu_read_unlock();              //! << Make the RCU Read unlock

        ret = send_sig_info(SIG_TEST, &info, stTask);    //! <<send the signal
        if (ret < 0)
        {
                printk("error sending signal\n");
                return ret;
        }
        return count;
}

/********************************************************************************/
/**
 * @brief The LKM mapping user defiend functions to file_operations structure.
 */
/********************************************************************************/
static const struct file_operations my_fops = {
        .write = write_pid,
};

/********************************************************************************/
/**
 * @brief Function prototype for the custom IRQ handler function
 *        see below for the implementation.
 */
/********************************************************************************/
static irq_handler_t  ebbgpio_irq_handler(unsigned int irq, void *dev_id, struct pt_regs *regs);

/********************************************************************************/
/**
 * \fn    __init ebbgpio_init(void)
 *
 *  @brief The LKM initialization function. The static keyword restricts the
 *         visibility of the function to within this C file. The __init macro
 *         means that for a built-in driver (not a LKM) the function is only used
 *         at initialization time and that it can be discarded and its memory
 *         freed up after that point. In this example this function sets up the
 *         GPIOs and the IRQ
 *
 *  @return returns 0 if successful
 */
/********************************************************************************/
static int __init ebbgpio_init(void)
{
   int result = 0;
   printk(KERN_INFO "GPIO_TEST: Initializing the GPIO_TEST LKM\n");
   // Is the GPIO a valid GPIO number (e.g., Sitara has 4x32 but not all available)
   if (!gpio_is_valid(resetGPIOInterrupt))
   {
      printk(KERN_INFO "GPIO_TEST: invalid Button GPIO\n");
      return -ENODEV;
   }
   //Going to set up the Button. It is a GPIO in input mode and will be zero  by default
   //gpio_set_value(gpioLED, ledOn);                   //! Not required as set by line above (here for reference)
   gpio_request(resetGPIOInterrupt, "sysfs");       //! Set up the gpioButton
   gpio_direction_input(resetGPIOInterrupt);        //! Set the resetGPIOInterrupt GPIO to be an input
   gpio_set_debounce(resetGPIOInterrupt, 200);      //! Debounce the resetGPIOInterrupt with a delay of 200ms
   gpio_export(resetGPIOInterrupt, false);          //! Causes gpio115 to appear in /sys/class/gpio
                                           //! the bool argument prevents the direction from being changed

   // Perform a quick test to see that the resetGPIOInterrupt is working as expected on LKM load
   printk(KERN_INFO "GPIO_TEST: The resetGPIOInterrupt state is currently: %d\n", gpio_get_value(resetGPIOInterrupt));

   // GPIO numbers and IRQ numbers are not the same! This function performs the mapping for us
   irqNumber = gpio_to_irq(resetGPIOInterrupt);
   printk(KERN_INFO "GPIO_TEST: The resetGPIOInterrupt is mapped to IRQ: %d\n", irqNumber);

   // This next call requests an interrupt line
   result = request_irq(irqNumber,                       //! The interrupt number requested
                        (irq_handler_t) ebbgpio_irq_handler, //! The pointer to the handler function below
                        IRQF_TRIGGER_RISING,             //! Interrupt on rising edge (When The Signal is High-1 not at Low-0)
                        "ebb_gpio_handler",              //! Used in /proc/interrupts to identify the owner
                        NULL);                           //! The *dev_id for shared interrupt lines, NULL is okay

   printk(KERN_INFO "GPIO_TEST: The interrupt request result is: %d\n", result);

   /* we need to know the pid of the user space process
   * -> we use debugfs for this. As soon as a pid is written to 
   * this file, a signal is sent to that pid
   */
   /* only root can write to this file (no read) */
   file = debugfs_create_file("signalconfpid", 0200, NULL, NULL, &my_fops);

   return result;
}


/********************************************************************************/
/**
 *  \fn    __init ebbgpio_init(void)
 *
 *  @brief The LKM cleanup function Similar to the initialization function,
 *         it is static. The __exit macro notifies that if this code is used for
 *         a built-in driver (not a LKM) that this function is not required. Used
 *         to release the GPIOs and display cleanup messages.
 *
 *  @return returns NULL
 */
/********************************************************************************/
static void __exit ebbgpio_exit(void)
{
   printk(KERN_INFO "GPIO_TEST: The resetGPIOInterrupt state is currently: %d\n", gpio_get_value(resetGPIOInterrupt));
   printk(KERN_INFO "GPIO_TEST: The resetGPIOInterrupt was received %d times\n", numberPresses);

   free_irq(irqNumber, NULL);                          //! << Free the IRQ number, no *dev_id required in this case.
   gpio_unexport(resetGPIOInterrupt);               //! << Unexport the resetGPIOInterrupt GPIO.
   gpio_free(resetGPIOInterrupt);                   //! << Free the resetGPIOInterrupt GPIO.

   debugfs_remove(file);                   //! <<  Remove the debugfs file.
   printk(KERN_INFO "GPIO_TEST: Unloading the GPIO_TEST LKM module!\n");
}

/********************************************************************************/
/**
 *  \fn    ebbgpio_irq_handler(void)
 *  @brief The GPIO IRQ Handler function. This function is a custom interrupt
 *         handler that is attached to the GPIO above. The same interrupt handler
 *         cannot be invoked concurrently as the interrupt line is masked out
 *         until the function is complete. This function is static as it should
 *         not be invoked directly from outside of this file.
 *
 *  @param irq    the IRQ number that is associated with the GPIO--useful for logging.
 *
 *  @param dev_id the *dev_id that is provided -- can be used to identify which
 *                                                device caused the interrupt
 *                                      (Not used in this case as NULL is passed.)
 *
 *  @param regs   h/w specific register values -- only really ever used for debugging.
 *
 *  @return returns IRQ_HANDLED if successful -- should return IRQ_NONE otherwise.
 */
/********************************************************************************/
static irq_handler_t ebbgpio_irq_handler(unsigned int irq, void *dev_id, struct pt_regs *regs)
{
   printk(KERN_INFO "GPIO_TEST: Interrupt! (resetGPIOInterrupt state is %d)\n", gpio_get_value(resetGPIOInterrupt));
   numberPresses++;                         // Global counter, will be outputted when the module is unloaded

   /* Sending The Signal to Process Starts */
   rcu_read_lock();
   stGTask = pid_task(find_pid_ns(gPID, &init_pid_ns), PIDTYPE_PID);
   printk("sending signal From ISR\n");
   if(stGTask == NULL)
   {
      printk("no such pid\n");
      rcu_read_unlock();
      return -ENODEV;
   }
   rcu_read_unlock();

   //This is the Line Important for You. Where we are sending the Signal to the Application layer
   Return = send_sig_info(SIG_TEST, &stGInfo, stGTask);    //send the signal

   if (Return < 0)
   {
      printk("error sending signal\n");
      return Return;
   }
   /* Sending The Signal to Process Ends */

   return (irq_handler_t) IRQ_HANDLED;      // Announce that the IRQ has been handled correctly
}

/********************************************************************************/
/// This next calls are  mandatory -- they identify the initialization function
/// and the cleanup function (as above).
/********************************************************************************/
module_init(ebbgpio_init);
module_exit(ebbgpio_exit);

You can straight forward take the code and generate .ko file for that. Just take care to configure the appropriate GPIO Number as per your requirement. I have configured GPIO-54 The code has been written for AM335x processor Only. If you do not know how to create .ko file then just be cool and google "HOW TO BUILD A KERNEL MODULE", you will get it easily.

Finally you can write a dummy application to test it, The application looks as follow:

/********************************************************************************/
/**
 * @file   userApplication.c
 *
 * @author Sudipta Kumar Sahoo
 *
 * @brief  An Application layer programm implementation to connect with the
 *     kernel layer, That receives the interrupt from the kernel space
 *     once the particular interrupt is received at configured GPIO pin.
 *
 *     NOTE:: Before executing the program first mount the debugfs if it has
 *     not already mounted to the file system and then execute the application
 *     to receive the interrupt Signal in applicatio layer.
 *
 *  The copyright notice does not evidence any actual or intended publication.
*/
/********************************************************************************/

#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>

#define SIG_TEST    44 /* we define our own signal, hard coded since SIGRTMIN
                is different in user and in kernel space */ 
#define RUNNING     1
/********************************************************************************/
/**
 * \fn    void signalHandler(int n, siginfo_t *info, void *unused)
 *
 * @brief The receiveData function
 *        The function has been registered to the kernel layer debugfs file system
 *        When GPIO gets the interrupt it intimates to appliation process via
 *    signalHandler() function and the appropriate action is taken afterwards.
 *  
 * @return NULL
 */
/********************************************************************************/
void signalHandler(int n, siginfo_t *info, void *unused)
{
    printf("Application received value %i\n", info->si_int);
    /* Do what ever you want to do inside this Function upon the receival of the Interrupt */
}

int main ( int argc, char **argv )
{
    int configfd;
    char buf[10];

    /* setup the signal handler for SIG_TEST 
     * SA_SIGINFO -> we want the signal handler function with 3 arguments
     */
    struct sigaction sig;

    sig.sa_sigaction = signalHandler;
    sig.sa_flags = SA_SIGINFO;
    sigaction(SIG_TEST, &sig, NULL);

    /* kernel needs to know our pid to be able to send us a signal ->
     * we use debugfs for this -> do not forget to mount the debugfs!
     */
    configfd = open("/sys/kernel/debug/signalconfpid", O_WRONLY);
    if(configfd < 0)
    {
        printf("Could not Open the File\n");
        perror("open");
        return -1;
    }

    sprintf(buf, "%i", getpid());               //! << Get the process ID.

    if (write(configfd, buf, strlen(buf) + 1) < 0)      //! << Write the details to Kernel Space.
    {
        perror("fwrite"); 
        return -1;
    }

    /*
     * Making the Application to run independently
     */
    while(RUNNING)
    {
        printf("Waiting for Interrup Signal...\n");
        sleep(1);
    }

    return 0;
}

signalHandler() is the function in your application layer that gets executed when there an interrupt comes to the particular configured GPIO. Now you can run your application independently and also you will be able to receive the interrupt with out polling

"The examples are self explanatory and appropriate comments has been given for your understanding"

Hope this Helps.

Upvotes: 0

mfro
mfro

Reputation: 3335

With the (originally) missing information about RESET_GPIO_VALUE, we can provide a reasonable answer.

Obviously, your µC's board support package implements the Linux GPIO Sysfs Interface for Userspace (you should read the documentation about that that comes with your Linux BSP).

Basically (if the board and the driver supports it), you can make GPIO's trigger interrupts in user space. Just write rising, falling or both (literally) to /sys/class/gpio/gpioN/edge to select the signal edges your code wants to react on, then do a poll() on an open file descriptor to /sys/class/gpio/gpioN/value.

This poll will return every time value changes accordingly.

Upvotes: 2

Related Questions