이전 코드의 문제점은 led를 켜고 싶으나, 잠들어 있어서 값을 읽어오지 못하는 상황이 발생하는거임.
즉, led를 켜고 싶어도 키가 안눌리면 led를 못키는 상황임.
리눅스에서 여러 장치를 open해서 어떤 data가 먼저 들어올 지 아무도 모름. 그래서 입출력 다중화를 해주는 거임.
어떨때는 키보드 값, 어떨 때는 키 값이 들어올 수 있기 때문에, 첫번째 프로세스에서 영원히 잠들지 않도록 다중 입출력을 구현해주는거임.
app.c부터 살펴보자.
polling에 사용되는 구조체의 생김새는 저렇고
44번 라인 - 폴링 구조체 Events의 사이즈만큼 메모리를 할당하면서 0으로 초기화
45~48은 short로 되어 있는 것들에 값을 넣어주는 거고
폴링 구조체에 2칸을 준 이유는 0번이 표준 입력장치(키보드)고 1번이 키 입력을 받는 거(gpio)
입출력 장치의 갯수만큼 배열 사이즈를 잡아주면 되는거임.
.events = POLLIN이 들어간 이유는 입력에 대한 event를 detecting 하겠다는거.
POLLOUT이면 출력에 대한 event인거
51번 라인의 2000값은 timeout 시간이고 2초에 한번씩 깨어나는거임.
52번은 에러처리
57번은 타임아웃인거 아래 조건 체크
62번에 들어가기 위해서는 키보드에 읽기 event가 발생해야함.
fgets로 keyStr의 크기인 80Byte에 데이터가 들어간거고
67번 - fgets는 \n이 붙어서 들어옴 그걸 \0로 바꿔주고
72번은 gpio key에 event가 발생해야되는거
device driver도 수정해줘야겠죠?
ubuntu@ubuntu8:~/pi_bsp/kernel/linux/include/linux$ vi fs.h
__poll_t 함수 원형 복사해서 붙여넣기
Makefile
APP := ledkey_app
MOD := ledkey_dev
OBJ := $(APP).o
obj-m := $(MOD).o
CROSS = ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf-
CC := arm-linux-gnueabihf-gcc
KDIR := /home/ubuntu/pi_bsp/kernel/linux
PWD := $(shell pwd)
default:$(OBJ)
$(MAKE) -C $(KDIR) M=$(PWD) modules $(CROSS)
cp $(MOD).ko /srv/nfs
%.o:%.c
$(CC) -o $(APP) $<
cp $(APP) /srv/nfs
clean:
rm -rf *.ko
rm -rf *.mod.*
rm -rf .*.cmd
rm -rf *.o
rm -rf modules.order
rm -rf Module.symvers
rm -rf $(MOD).mod
rm -rf .tmp_versions
rm -rf $(APP)
ledkey_app.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <poll.h>
#include <string.h>
#define DEVICE_FILENAME "/dev/ledkey_dev"
int main(int argc, char *argv[])
{
int dev;
char buff;
int ret;
int num = 1;
struct pollfd Events[2];
char keyStr[80];
if(argc != 2)
{
printf("Usage : %s [led_data(0x00~0xff)]\n",argv[0]);
return 1;
}
buff = (char)strtoul(argv[1],NULL,16);
if((buff < 0x00) || (0xff < buff))
{
printf("Usage : %s [led_data(0x00~0xff)]\n",argv[0]);
return 2;
}
// dev = open(DEVICE_FILENAME, O_RDWR | O_NONBLOCK);
dev = open(DEVICE_FILENAME, O_RDWR );
if(dev < 0)
{
perror("open");
return 2;
}
write(dev,&buff,sizeof(buff));
fflush(stdin);
memset( Events, 0, sizeof(Events));
Events[0].fd = fileno(stdin);
Events[0].events = POLLIN;
Events[1].fd = dev;
Events[1].events = POLLIN;
while(1)
{
ret = poll(Events, 2, 2000);
if(ret<0)
{
perror("poll");
exit(1);
}
else if(ret==0)
{
printf("poll time out : %d Sec\n",2*num++);
continue;
}
if(Events[0].revents & POLLIN) //stdin
{
fgets(keyStr,sizeof(keyStr),stdin);
if(keyStr[0] == 'q')
break;
keyStr[strlen(keyStr)-1] = '\0'; //'\n' clear
printf("STDIN : %s\n",keyStr);
buff = (char)atoi(keyStr);
write(dev,&buff,sizeof(buff));
}
else if(Events[1].revents & POLLIN) //ledkey
{
ret = read(dev,&buff,sizeof(buff));
printf("key_no : %d\n",buff);
if(buff == 8)
break;
buff = 1 << buff-1;
write(dev,&buff,sizeof(buff));
}
}
close(dev);
return 0;
}
ledkey_dev.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/fcntl.h>
#include <linux/gpio.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/wait.h>
#include <linux/poll.h>
#define DEBUG 1
#define LEDKEY_DEV_NAME "ledkey_dev"
#define LEDKEY_DEV_MAJOR 230
#define OFF 0
#define ON 1
#define GPIOLEDCNT 8
#define GPIOKEYCNT 8
static int gpioLed[GPIOLEDCNT] = {6,7,8,9,10,11,12,13};
static int gpioKey[GPIOKEYCNT] = {16,17,18,19,20,21,22,23};
typedef struct {
int key_irq[8];
int keyNumber;
} keyData;
static int gpioLedInit(void);
static void gpioLedSet(long);
static void gpioLedFree(void);
static int gpioKeyInit(void);
//static int gpioKeyGet(void);
static void gpioKeyFree(void);
static int gpioKeyIrqInit(keyData * pKeyData);
static void gpioKeyIrqFree(keyData * pKeyData);
DECLARE_WAIT_QUEUE_HEAD(WaitQueue_Read);
static irqreturn_t key_isr(int irq, void *data)
{
int i;
keyData * pKeyData = (keyData *)data;
for(i=0;i<GPIOKEYCNT;i++)
{
if(irq == pKeyData->key_irq[i])
{
pKeyData->keyNumber = i+1;
break;
}
}
#if DEBUG
printk("key_isr() irq : %d, KeyNumber : %d\n",irq, pKeyData->keyNumber);
#endif
wake_up_interruptible(&WaitQueue_Read);
return IRQ_HANDLED;
}
static int gpioLedInit(void)
{
int i;
int ret=0;
char gpioName[10];
for(i=0;i<GPIOLEDCNT;i++)
{
sprintf(gpioName,"led%d",i);
ret = gpio_request(gpioLed[i],gpioName);
if(ret < 0) {
printk("Failed gpio_request() gpio%d error \n",i);
return ret;
}
ret = gpio_direction_output(gpioLed[i],OFF);
if(ret < 0) {
printk("Failed gpio_direction_output() gpio%d error \n",i);
return ret;
}
}
return ret;
}
static void gpioLedSet(long val)
{
int i;
for(i=0;i<GPIOLEDCNT;i++)
{
gpio_set_value(gpioLed[i],(val>>i) & 0x1);
}
}
static void gpioLedFree(void)
{
int i;
for(i=0;i<GPIOLEDCNT;i++)
{
gpio_free(gpioLed[i]);
}
}
static int gpioKeyInit(void)
{
int i;
int ret=0;
char gpioName[10];
for(i=0;i<GPIOKEYCNT;i++)
{
sprintf(gpioName,"key%d",gpioKey[i]);
ret = gpio_request(gpioKey[i], gpioName);
if(ret < 0) {
printk("Failed Request gpio%d error\n", 6);
return ret;
}
}
for(i=0;i<GPIOKEYCNT;i++)
{
ret = gpio_direction_input(gpioKey[i]);
if(ret < 0) {
printk("Failed direction_output gpio%d error\n", 6);
return ret;
}
}
return ret;
}
/*
static int gpioKeyGet(void)
{
int i;
int ret;
int keyData=0;
for(i=0;i<GPIOKEYCNT;i++)
{
// ret=gpio_get_value(gpioKey[i]) << i;
// keyData |= ret;
ret=gpio_get_value(gpioKey[i]);
keyData = keyData | ( ret << i );
}
return keyData;
}
*/
static void gpioKeyFree(void)
{
int i;
for(i=0;i<GPIOKEYCNT;i++)
{
gpio_free(gpioKey[i]);
}
}
static int gpioKeyIrqInit(keyData * pKeyData)
{
int i;
int result;
char * irqName[8] = {"IrqKey0","IrqKey1","IrqKey2","IrqKey3","IrqKey4","IrqKey5","IrqKey6","IrqKey7"};
for(i=0;i<GPIOKEYCNT;i++)
{
pKeyData->key_irq[i] = gpio_to_irq(gpioKey[i]);
if(pKeyData->key_irq[i] < 0)
{
printk("gpioKeyIrq() Failed gpio %d\n",gpioKey[i]);
return pKeyData->key_irq[i];
}
}
for(i=0;i<GPIOKEYCNT;i++)
{
result = request_irq(pKeyData->key_irq[i],key_isr,IRQF_TRIGGER_RISING,irqName[i],pKeyData);
if(result < 0)
{
printk("request_irq() failed irq %d\n",pKeyData->key_irq[i]);
return result;
}
}
return 0;
}
static void gpioKeyIrqFree(keyData * pKeyData)
{
int i;
for(i=0;i<GPIOKEYCNT;i++)
{
free_irq(pKeyData->key_irq[i],pKeyData);
}
}
static int ledkey_open (struct inode *inode, struct file *filp)
{
int result;
int num0 = MAJOR(inode->i_rdev);
int num1 = MINOR(inode->i_rdev);
keyData * pKeyData = (keyData *)kmalloc(sizeof(keyData),GFP_KERNEL);
if(!pKeyData)
return -ENOMEM;
pKeyData->keyNumber = 0;
#if DEBUG
printk( "ledkey open -> major : %d\n", num0 );
printk( "ledkey open -> minor : %d\n", num1 );
#endif
try_module_get(THIS_MODULE);
result=gpioLedInit();
if(result < 0)
return result;
result=gpioKeyInit();
if(result < 0)
return result;
result = gpioKeyIrqInit(pKeyData);
if(result < 0)
return result;
filp->private_data = pKeyData;
return 0;
}
static ssize_t ledkey_read(struct file *filp, char *buf, size_t count, loff_t *f_pos)
{
// int result;
// char kbuf;
keyData * pKeyData = (keyData *)filp->private_data;
#if DEBUG
printk( "ledkey read -> buf : %08X, count : %08X \n", (unsigned int)buf, count );
#endif
// kbuf = (char)gpioKeyGet();
// kbuf = pKeyData->keyNumber;
if(pKeyData->keyNumber == 0)
{
if(!(filp->f_flags & O_NONBLOCK))
{
// wait_event_interruptible(WaitQueue_Read,pKeyData->keyNumber);
wait_event_interruptible_timeout(WaitQueue_Read,pKeyData->keyNumber,100); //100 * 1/HZ = 100 * 1/100 = 100 * 0.01 = 1Sec
}
}
put_user(pKeyData->keyNumber,buf);
// result = copy_to_user(buf, &(pKeyData->keyNumber), count);
if(pKeyData->keyNumber)
pKeyData->keyNumber = 0;
return count;
}
static ssize_t ledkey_write (struct file *filp, const char *buf, size_t count, loff_t *f_pos)
{
// int i;
// int result;
char kbuff;
/* char kbuff[10];
for(i=0;i<count;i++)
get_user(kbuff[i],buf++);
*/
/* char kbuff[10];
copy_from_user(kbuff,buf,count);
*/
#if DEBUG
printk( "ledkey write -> buf : %08X, count : %08X \n", (unsigned int)buf, count );
#endif
get_user(kbuff,buf);
// result = copy_from_user(&kbuff,buf,count);
gpioLedSet(kbuff);
return count;
}
static __poll_t ledkey_poll (struct file *filp, struct poll_table_struct *wait)
{
unsigned int mask =0;
keyData *pkeyData = (keyData*)filp->private_data;
#ifdef DEBUG
printk("_key : %u\n",(wait->_key & POLLIN));
#endif
if(wait->_key & POLLIN)
poll_wait(filp, &WaitQueue_Read, wait);
if(pkeyData->keyNumber >0)//2초가 안되더라도 눌렸다면 poll in을 넣어줘서 깨어나게 만듬.
mask= POLLIN;
return mask;
}
static long ledkey_ioctl (struct file *filp, unsigned int cmd, unsigned long arg)
{
#if DEBUG
printk( "ledkey ioctl -> cmd : %08X, arg : %08X \n", cmd, (unsigned int)arg );
#endif
return 0x53;
}
static int ledkey_release (struct inode *inode, struct file *filp)
{
keyData * pKeyData = (keyData *)filp->private_data;
#if DEBUG
printk( "ledkey release \n" );
#endif
module_put(THIS_MODULE);
gpioKeyIrqFree(pKeyData);
gpioKeyFree();
gpioLedFree();
if(pKeyData)
kfree(pKeyData);
return 0;
}
static struct file_operations ledkey_fops =
{
// .owner = THIS_MODULE,
.open = ledkey_open,
.read = ledkey_read,
.write = ledkey_write,
.poll = ledkey_poll,
.unlocked_ioctl = ledkey_ioctl,
.release = ledkey_release,
};
static int ledkey_init(void)
{
int result;
printk( "ledkey ledkey_init \n" );
result = register_chrdev( LEDKEY_DEV_MAJOR, LEDKEY_DEV_NAME, &ledkey_fops);
if (result < 0) return result;
return 0;
}
static void ledkey_exit(void)
{
printk( "ledkey ledkey_exit \n" );
unregister_chrdev( LEDKEY_DEV_MAJOR, LEDKEY_DEV_NAME );
}
module_init(ledkey_init);
module_exit(ledkey_exit);
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("KCCI-AIOT KSH");
MODULE_DESCRIPTION("led key test module");
'Linux BSP' 카테고리의 다른 글
[Linux Device Driver] 입출력 다중화(Poll)과 Blocking I/O를 구현한 디바이스 드라이버 (1) | 2024.02.28 |
---|---|
[Device Driver] 커널 타이머 GPIO제어하는 모듈 프로그램 디바이스 드라이버 프로그램으로 바꾸기 (0) | 2024.02.28 |
[Linux Device Driver] Blocking I/O (0) | 2024.02.28 |
[Linux Device Driver] 커널 메모리 동적 할당 및 irq매개변수를 통한 인터럽트 데이터 전달 (1) | 2024.02.27 |
[Linux Device Driver] Interrupt 처리 (0) | 2024.02.27 |