Хорошо, хотя это немного сложно, я расскажу вам, какие шаги необходимы для этого. Поскольку это действительно сложно, многие не хотели бы давать правильный ответ, тем не менее, это шаг, которому вы можете следовать.
1. Что вам нужно сделать, так это сначала написать модуль ядра. Затем вы можете скомпилировать это для создания файла .ko
. Это загружаемый модуль ядра. Напишите загружаемый модуль ядра, в котором вы можете сохранить ISR (процедура обслуживания прерываний), которая запускается, когда на какой-либо конкретный GPIO приходит прерывание. К вашему сведению, ISR также является функцией, которая автоматически запускается при получении прерывания на конкретный GPIO.
Оставьте в своем модуле ядра возможность принимать прикладной уровень Process ID
, чтобы, как только произойдет прерывание на GPIO
, модуль мог отправить SIGNAL
с уровня ядра на прикладной уровень.
2. Скопируйте это в свой ARM target processor
, а затем вставьте модуль в ядро. Вы можете вставить модуль, набрав
`insmod <your_module.ko>`
- Проверьте, вставлен ли модуль, введя команду
lsmod
на терминал вашей целевой платы AM335x
.
3. Затем вы можете написать приложение уровня пользователя, где вы можете отправить processID
процесса с уровня приложения на уровень ядра, как только ваш процесс запустится. ТАК, чтобы модуль ядра, который вы написали, мог заставить прикладной уровень ProcessID
отправить файл SIGNAL
.
ПРИМЕЧАНИЕ. Поскольку Linux по умолчанию использует первые 32
SIGNAL
, вы можете использовать для своих целей номер SIGNAL
из 33 to 64
. Здесь мы используем SIGNAL Number 44
.
Пример модуля ядра:
#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);
Вы можете прямо взять код и создать для него файл .ko. Просто позаботьтесь о настройке номера appropriate GPIO
в соответствии с вашими требованиями. Я настроил GPIO-54
Код был написан только для AM335x
процессора. Если вы не знаете, как создать файл .ko
, просто будьте спокойны и погуглите "HOW TO BUILD A KERNEL MODULE"
, вы легко его получите.
Наконец, вы можете написать фиктивное приложение для его тестирования. Приложение выглядит следующим образом:
/********************************************************************************/
/**
* @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()
— это функция на уровне вашего приложения, которая запускается, когда прерывание поступает к конкретному сконфигурированному GPIO
. Теперь вы можете запускать свое приложение самостоятельно, а также сможете получать прерывания без опроса.
"Примеры говорят сами за себя, и для вашего понимания даны соответствующие комментарии"
Надеюсь это поможет.
14.03.2016