본문 바로가기
Linux BSP

[Device Driver] 커널 타이머 GPIO제어하는 모듈 프로그램 디바이스 드라이버 프로그램으로 바꾸기

by TYB 2024. 2. 28.
반응형
 

[Device Driver] 어플리케이션 없이 insmod만으로 동작하는 모듈 프로그램으로 커널 타이머 GPIO제어

이전 글에서 코드를 향상시켜봅니다. [Device Driver] 어플리케이션 없이 insmod만으로 동작하는 모듈 프로그램으로 커널 타이머 인터럽트 어플리케이션 없이 insmod만으로 동작하는 모듈 프로그램으

program-developers-story.tistory.com

 

이전 두 글에서 진행한 kernel timer와 모듈 프로그램을 합쳐서 device file 위에 

 

kernel timer로 진행한 모듈프로그램을 device file로 바꿔보자구~


 

 

 

Makefile

APP := kerneltimer_app
MOD := kerneltimer_dev
SRC := $(APP).c
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:$(APP)
	$(MAKE) -C $(KDIR) M=$(PWD) modules $(CROSS)
	cp $(MOD).ko /srv/nfs
$(APP):
	$(CC) $(APP).c -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)

 

 

app.c

 

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>

#define DEVICE_FILENAME  "/dev/kerneltimer_dev"

void print_led(unsigned char);
void print_key(unsigned char);
int main(int argc,char * argv[])
{
    int dev;
    char buff = 0;
	char oldBuff = 0;
    int ret;
	if(argc < 2)
	{
		printf("USAGE : %s [ledval] \n",argv[0]);
		return 1;
	}
//	buff = atoi(argv[1]);
	buff = (char)strtoul(argv[1],0,16);

    dev = open( DEVICE_FILENAME, O_RDWR|O_NDELAY );
	if(dev<0)
	{
		perror("open()");
		return 2;
	}
    ret = write(dev,&buff,sizeof(buff));
	if(ret < 0)
	{
		perror("write()");
		return 3;
	}
	print_led(buff);
	
	buff = 0;
	do {
		read(dev,&buff,sizeof(buff));
//		buff = 1 << buff-1;
		if((buff != 0) && (oldBuff != buff))
		{
			printf("key : %#04x\n",buff);
//			print_key(buff);
    		write(dev,&buff,sizeof(buff));
			print_led(buff);
			oldBuff = buff;
			if(buff == 0x80) //key:8
				break;
		}
	} while(1);


    close(dev);
    return 0;
}
void print_led(unsigned char led)
{
	int i;
	puts("1:2:3:4:5:6:7:8");
	for(i=0;i<=7;i++)
	{
		if(led & (0x01 << i))
			putchar('O');
		else
			putchar('X');
		if(i < 7 )
			putchar(':');
		else
			putchar('\n');
	}
	return;
}

void print_key(unsigned char key)
{
	int i;
	puts("1:2:3:4:5:6:7:8");
	for(i=0;i<=7;i++)
	{
		if(i+1 == key)
			putchar('O');
		else
			putchar('X');

		if(i < 7 )
			putchar(':');
		else
			putchar('\n');
	}
	return;
}

 

 

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/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/gpio.h>

#define KERNELTIMER_DEV_NAME "kerneltimer_dev" 
#define KERNELTIMER_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};

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 ledVal =0;

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]);
	}
}
//----------------kernel timer dev---------------------
static int kerneltimer_dev_open(struct inode *inode, struct file *filp);
static loff_t kerneltimer_dev_llseek(struct file *filp, loff_t off, int whence);
static ssize_t kerneltimer_dev_read(struct file *filp, char *buf,size_t count, loff_t *f_pos);
static ssize_t kerneltimer_dev_write(struct file *filp, const char *buf,size_t count, loff_t *f_pos);
static long kerneltimer_dev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg);
static int kerneltimer_dev_release(struct inode *inode, struct file *filp);


struct file_operations kerneltimer_dev_fops = 
{
	.owner = THIS_MODULE,
	.llseek = kerneltimer_dev_llseek,
	.read = kerneltimer_dev_read,
	.write = kerneltimer_dev_write,
	.unlocked_ioctl = kerneltimer_dev_ioctl,
	.open = kerneltimer_dev_open,
	.release = kerneltimer_dev_release,
};

//insmod 할 때 호출되는 함수
static int kerneltimer_dev_open(struct inode *inode, struct file *filp)
{
	printk("kerneltimer_dev ledkey_init  \n");
    return 0;
}

static loff_t kerneltimer_dev_llseek(struct file *filp, loff_t off, int whence)
{
	printk("kerneltimer_dev llseek -> off : %08X, whence : %08X\n", (unsigned int)off, whence);
	return 0x23;
}
static ssize_t kerneltimer_dev_read(struct file *filp, char *buf,size_t count, loff_t *f_pos)
{
	printk("kerneltimer_dev read -> buf : %08X, count : %08X\n", (unsigned int)buf, count);
	static int keyVal;
	keyVal = gpioKeyGet();
	put_user(keyVal,buf);
	return 0x33;
}
static ssize_t kerneltimer_dev_write(struct file *filp, const char *buf,size_t count, loff_t *f_pos)
{
	printk("kerneltimer_dev write -> buf : %08X, count : %08X\n", (unsigned int)buf, count);
	char kbuf;
    get_user(kbuf,buf);
	ledVal = kbuf;
	return 0x43;
}
static long kerneltimer_dev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
	printk("kerneltimer_dev ioctl -> cmd : %08X, arg : %08X\n", cmd, (unsigned int)arg);
	return 0x53;
}
static int kerneltimer_dev_release(struct inode *inode, struct file *filp)
{
	printk("kerneltimer_dev ledkey_release  \n");
	return 0;
}
//----------------kernel timer-----------------------------

static int timerVal = 20;
module_param(timerVal,int ,0);
module_param(ledVal,int,0);
struct timer_list timerLed;
static void kerneltimer_dev_exit(void);
void kerneltimer_dev_func(struct timer_list *t);
void kerneltimer_registertimer(unsigned long timeover)
{
    timer_setup(&timerLed, kerneltimer_dev_func,0);//timer 생성
    timerLed.expires = get_jiffies_64() + timeover;//timer 작동 시간 설정
    //get_jiffies_64는 1초에 100씩 늘어남. timeover는 10ms *100 =1sec
    add_timer(&timerLed);
}
void kerneltimer_dev_func(struct timer_list *t)
{
#if DEBUG
    printk("ledVal : %#04x\n",(unsigned int)(ledVal));
#endif
    char ledStatus[16]=" : : : : : : : ";
    int key_data;
    int key_data_old = 0;
    int ret, i;
    ledVal = ~ledVal & 0xff;
    gpioLedSet(ledVal);
	/*
	key_data = gpioKeyGet();
	if(key_data != key_data_old)
	{
		if(key_data)
		{
			keyVal = 0;
			for(i =0; i<8; i++)
			{
				keyVal |= key_data>>i & 0x01;
			}
			ledVal = keyVal;
			gpioLedSet(key_data);
			printk("0:1:2:3:4:5:6:7\n");
			for(i=0;i<8;i++)
			{
				if((key_data >> i) & 0x01)
					ledStatus[i*2] = 'O';
				else
					ledStatus[i*2] = 'X';
			}
			printk("%s\n",ledStatus);
		}
		key_data_old = key_data;
		if (key_data == 0x80)
			kerneltimer_dev_exit();
	}
	*/
    mod_timer(t,get_jiffies_64() + timerVal);
}

//insmod 할 때 호출되는 함수
static int kerneltimer_dev_init(void)
{
#if DEBUG
    printk("timerVal : %d, sec : %d \n",timerVal,timerVal/HZ);//HZ는 0.01임. timerVal*100인거임.
#endif
    int ret;
    ret=gpioLedInit();
    if(ret < 0)
        return ret;
    ret=gpioKeyInit();
    if(ret < 0)
        return ret;
    kerneltimer_registertimer(timerVal);
    ret =register_chrdev(KERNELTIMER_DEV_MAJOR,KERNELTIMER_DEV_NAME, &kerneltimer_dev_fops);
    if(ret < 0) return ret;
    return 0;
}
//rmmod 할 때 호출되는 함수
static void kerneltimer_dev_exit(void)
{
    printk("kerneltimer_dev_exit  \n");
    gpioLedFree();
    gpioKeyFree();
    if(timer_pending(&timerLed))//만약 타이머가 등록되어있다면
        del_timer(&timerLed);//타이머 제거
    unregister_chrdev(KERNELTIMER_DEV_MAJOR,KERNELTIMER_DEV_NAME);
}

module_init(kerneltimer_dev_init);
module_exit(kerneltimer_dev_exit);
MODULE_AUTHOR("AIOT WCM");
MODULE_DESCRIPTION("kernel timer led key test module");
MODULE_LICENSE("Dual BSD/GPL");

 

 

 

반응형