app단에서 사용하는 헤더를 커널형으로 변환해서 사용할 수 있다.
#include <string.h> => #include <linux/string.h>
와 같이 변경해서 사용하면, app에서 사용한 라이버러리와 동일하게 사용가능하다.
insmod
를 통해 원하는 파라미터를 집어넣을 수 있다.
kernel.c
static char *whom = "world";
static int num = 1;
module_param(num, int, S_IRUGO); // 숫자형 변수에 파라미터를 받을 수 있다.
module_param(whom, charp, S_IRUGO); // 문자형 변수에 파라미터를 받을 수 있다.
insmod kernel c 100 0 num=10 whom = "Jaehoon"
으로 파라미터 받기!
커널에서 사용 가능하며, GPIO에 쉽게 접근 가능한 API
헤더파일 : #include <linux/gpio.h>
사용 API :
gpio_direction_output(번호, 0)
gpio_set_value(번호, 0 또는 1)
app.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <signal.h>
void go_alert(int signum)
{
for(int i=0; i<5; i++)
{
printf("WARNING!!\n");
}
}
int main()
{
signal(SIGIO, go_alert);
printf("OPEN FILE!!\n");
int fd = open("/dev/nobrand", O_RDWR);
if (fd == -1) {
printf("FILE OPEN ERROR!!!\n");
close(fd);
return 0;
}
int pid = getpid();
ioctl(fd, _IO(0,4), pid);
while(1)
{
ioctl(fd, _IO(0,3), 0);
usleep(300 * 1000);
}
// ioctl(fd, _IO(0, 3), 0);
// ioctl(fd, _IO(0, 4), 0);
printf("CLOSE FILE!!\n");
close(fd);
return 0;
}
kernel.c
#include <linux/module.h>
#include <linux/fs.h>
#include <asm/io.h>
#include <linux/interrupt.h>
#include <linux/gpio.h>
#define NOD_NAME "nobrand"
#define NOD_MAJOR 100
MODULE_LICENSE("GPL");
static uint32_t *BASE, *GPSEL2;
static uint32_t *GPIO_PUP_PDN_CNTRL_REG1;
static uint32_t *GPLEV0;
static int irq_num;
static int app_pid;
static struct kernel_siginfo sig_info;
static struct task_struct *task;
static int nobrand_open(struct inode *inode, struct file *filp) {
printk( KERN_INFO "OPEN!!!\n");
return 0;
}
static int nobrand_release(struct inode *inode, struct file *filp) {
printk( KERN_INFO "CLOSE!!!\n");
return 0;
}
static long nobrand_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
int lev;
switch (cmd)
{
case _IO(0, 3) :
printk(KERN_INFO "IO 3\n");
lev = (*GPLEV0 >> 21) & 0x1;
printk( KERN_INFO "GPIO21 LEV = %d\n", lev);
break;
case _IO(0, 4) :
printk(KERN_INFO "IO 4\n");
app_pid = (int)arg;
break;
}
printk( KERN_ALERT "%d\n", cmd);
return 0;
}
static struct file_operations fops = {
.open = nobrand_open,
.release = nobrand_release,
.unlocked_ioctl = nobrand_ioctl
};
static irqreturn_t lets_go(int irq, void *data)
{
printk( KERN_ALERT "IT's ME\n");
if(app_pid > 0)
{
sig_info.si_signo = SIGIO;
task = pid_task(find_vpid(app_pid), PIDTYPE_PID); // task 변수에 pid 정보 집어넣기
send_sig_info(SIGIO, &sig_info, task); // 신호를 app에 보낸다.
}
return IRQ_HANDLED;
}
static int nobrand_init(void) {
int ret;
BASE = (uint32_t *)ioremap(0xFE200000, 256);
GPSEL2 = BASE + (0x08 / 4);
*GPSEL2 = *GPSEL2 & ~(0x7 << 3);
GPIO_PUP_PDN_CNTRL_REG1 = BASE + (0xe8 / 4);
*GPIO_PUP_PDN_CNTRL_REG1 &= ~(0x3 << 10);
*GPIO_PUP_PDN_CNTRL_REG1 |= (0x1 << 10);
GPLEV0 = BASE + (0x34 / 4);
irq_num = gpio_to_irq(21); // 21번의 interrupt 감시
// 버튼을 땔 때 lets_go라는 함수 콜백한다.
ret = request_irq(irq_num, lets_go, IRQF_TRIGGER_RISING, "interrupt", NULL);
printk( KERN_INFO "OK HELLO NOBRAND!!\n");
if (register_chrdev(NOD_MAJOR, NOD_NAME, &fops) < 0) {
printk( KERN_INFO "ERROR!!! register error\n");
}
return 0;
}
static void nobrand_exit(void) {
free_irq(irq_num, NULL);
unregister_chrdev(NOD_MAJOR, NOD_NAME);
printk( KERN_INFO "BYE BYE\n\n");
}
module_init(nobrand_init);
module_exit(nobrand_exit);