반응형
Makefile
APP := minor_app
MOD := minor_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)
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 <asm/uaccess.h>
#include <asm/io.h>
#include <linux/moduleparam.h>
#include <linux/gpio.h>
#define GPIOLEDCNT 8
#define GPIOKEYCNT 8
#define OFF 0
#define ON 1
#define MINOR_DEV_NAME "minordev"
#define MINOR_DEV_MAJOR 230
int gpioLed[GPIOLEDCNT] = {6,7,8,9,10,11,12,13};
int gpioKey[GPIOKEYCNT] = {16,17,18,19,20,21,22,23};
static int onevalue = 1;
static char * twostring = NULL;
module_param(onevalue, int ,0);
module_param(twostring,charp,0);
int gpioLedInit(void);
void gpioLedSet(long);
void gpioLedFree(void);
int gpioKeyInit(void);
int gpioKeyGet(void);
void gpioKeyFree(void);
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 Request gpio%d error\n", 6);
return ret;
}
}
for(i=0;i<GPIOLEDCNT;i++)
{
ret = gpio_direction_output(gpioLed[i], OFF);
if(ret < 0) {
printk("Failed direction_output gpio%d error\n", 6);
return ret;
}
}
return ret;
}
void gpioLedSet(long val)
{
int i;
for(i=0;i<GPIOLEDCNT;i++)
{
gpio_set_value(gpioLed[i], (val>>i) & 0x01);
}
}
void gpioLedFree(void)
{
int i;
for(i=0;i<GPIOLEDCNT;i++)
{
gpio_free(gpioLed[i]);
}
}
int gpioKeyInit(void)
{
int i;
int ret=0;
char gpioName[10];
for(i=0;i<GPIOKEYCNT;i++)
{
sprintf(gpioName,"key%d",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;
}
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;
}
return keyData;
}
void gpioKeyFree(void)
{
int i;
for(i=0;i<GPIOKEYCNT;i++)
{
gpio_free(gpioKey[i]);
}
}
int minor0_open (struct inode *inode, struct file *filp)
{
printk( "call minor0_open\n" );
return 0;
}
ssize_t minor0_write (struct file *filp, const char *buf, size_t count, loff_t *f_pos)
{
unsigned char status;
int ret;
// get_user(status,buf);
ret=copy_from_user(&status,buf,count);
gpioLedSet(status);
return count;
}
int minor0_release (struct inode *inode, struct file *filp)
{
printk( "call minor0_release\n" );
return 0;
}
int minor1_open (struct inode *inode, struct file *filp)
{
printk( "call minor1_open\n" );
return 0;
}
ssize_t minor1_read(struct file *filp, char *buf, size_t count, loff_t *f_pos)
{
unsigned char status;
int ret;
status = gpioKeyGet();
// put_user(status,buf);
ret=copy_to_user(buf,&status,count);
return count;
}
int minor1_release (struct inode *inode, struct file *filp)
{
printk( "call minor1_release\n" );
return 0;
}
struct file_operations minor0_fops =
{
.owner = THIS_MODULE,
.write = minor0_write,
.open = minor0_open,
.release = minor0_release,
};
struct file_operations minor1_fops =
{
.owner = THIS_MODULE,
.read = minor1_read,
.open = minor1_open,
.release = minor1_release,
};
int minor_open (struct inode *inode, struct file *filp)
{
printk( "call minor_open\n" );
switch (MINOR(inode->i_rdev))
{
case 0: filp->f_op = &minor0_fops; break;
case 1: filp->f_op = &minor1_fops; break;
default : return -ENXIO;
}
if (filp->f_op && filp->f_op->open)
return filp->f_op->open(inode,filp);
return 0;
}
struct file_operations minor_fops =
{
.owner = THIS_MODULE,
.open = minor_open,
};
int minor_init(void)
{
int result;
result = gpioLedInit();
if(result < 0)
// return result;
return -EBUSY;
result = gpioKeyInit();
if(result < 0)
return result;
result = register_chrdev( MINOR_DEV_MAJOR, MINOR_DEV_NAME, &minor_fops);
if (result < 0) return result;
return 0;
}
void minor_exit(void)
{
unregister_chrdev( MINOR_DEV_MAJOR, MINOR_DEV_NAME );
gpioLedSet(0);
gpioLedFree();
gpioKeyFree();
}
module_init(minor_init);
module_exit(minor_exit);
MODULE_AUTHOR("KCCI-AIOT KSH");
MODULE_DESCRIPTION("led key test module");
MODULE_LICENSE("Dual BSD/GPL");
file operation을 해야하기 때문에 부번호 처리하는 부분이 조금 바뀜.
처음에 등록되있던 주번호 주소는 필요가 없어진거임. 부번호에 대한 부분만 남으면 됨.
195번 줄에 보면 해당 file operation 파일 포인터에 open 함수가 있으면,
196번줄에서 open 함수를 실행하는 걸 볼 수있음.
app.c
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <unistd.h>
#define WRITE_DEVICE_FILENAME "/dev/minor_write" //major:230, minor:0
#define READ_DEVICE_FILENAME "/dev/minor_read" //major:230, minor:1
int main()
{
int read_dev;
int write_dev;
char buff[128];
int loop;
read_dev = open( READ_DEVICE_FILENAME, O_RDWR|O_NDELAY );
if( read_dev < 0 )
{
perror("open()");
printf( READ_DEVICE_FILENAME " open error\n" );
exit(1);
}
write_dev = open( WRITE_DEVICE_FILENAME, O_RDWR|O_NDELAY );
if( write_dev < 0 )
{
perror("open()");
printf( WRITE_DEVICE_FILENAME " open error\n" );
close( read_dev );
exit(1);
}
printf( "wait... input\n" );
while(1)
{
if( read(read_dev,buff,1 ) == 1 && buff[0] != 0)
{
printf( "read data [%02X]\n", buff[0] & 0xFF );
// if( !(buff[0] & 0x10) ) break;
if( buff[0] & 0x80) break;
}
}
printf( "input ok...\n");
printf( "led flashing...\n");
for( loop=0; loop<5; loop++ )
{
buff[0] = 0xFF;
write(write_dev,buff,1 );
sleep(1);
buff[0] = 0x00;
write(write_dev,buff,1 );
sleep(1);
}
close(read_dev);
close(write_dev);
return 0;
}
pi@pi08:/mnt/ubuntu_nfs $ sudo rmmod ledkey_array_dev.ko
pi@pi08:/mnt/ubuntu_nfs $ sudo mknod /dev/minor_write c 230 0
pi@pi08:/mnt/ubuntu_nfs $ sudo mknod /dev/minor_read c 230 1
pi@pi08:/mnt/ubuntu_nfs $ ls -l /dev/minor_*
crw-r--r-- 1 root root 230, 1 Feb 26 10:39 /dev/minor_read
crw-r--r-- 1 root root 230, 0 Feb 26 10:39 /dev/minor_write
pi@pi08:/mnt/ubuntu_nfs $ sudo ./minor_app
버튼을 하나씩 눌러보면 01, 02,04 08,10 ,20 , 40 , 80 쭈루루루룩 나옴.
폴링방식이라 그렇고
8번째 키를 누르면 led가 5번 깜빡이다가 끝남.
이 상태일 때 dmesg 커널메시지는 우측과 같음. |
커널 메시지를 보면 주번호 파일포인터 열고, 부번호1 파일포인터 열고/ 주번호 파일포인터 열고, 부번호0 파일포인터 열고 순으로 진행됨.
결론: app.c에 부번호에 따라 다른 디바이스 드라이버를 호출하도록 설정되어 있고, 다른 함수를 쓰는거임.
고로 같은 장치는 같은 주번호를 쓰고, 다른 동작을 수행하는 거는 부번호로 분리하는거임.
그래서 app이 실행되면서 open을 하는데
open을 하면 dev.c로 넘어감.
dev.c에서 minor_open으로 넘어가는데,
넘어가서 부번호의 file 포인터로 값이 바뀌게 되고 부번호에 맞는 함수를 실행함.
이런식으로 동일 장치가 여러개일 경우 동일한 주번호를 사용하고 부번호를 나눠서 부여해준다.
아래는 주번호 별로 사전에 정의되어 있는 사용용도임.
반응형
'Linux BSP' 카테고리의 다른 글
[Device Driver] 어플리케이션 없이 insmod만으로 동작하는 모듈 프로그램으로 커널 타이머 인터럽트 구현 (0) | 2024.02.26 |
---|---|
[Device Driver] ioctl 함수 (1) | 2024.02.26 |
[Linux Device Driver] application-> 디바이스 드라이버 8바이트 매개변수 입력 (0) | 2024.02.26 |
[Linux Device Driver] Device Driver와 application으로 led, 버튼 제어 (0) | 2024.02.23 |
[Linux Device Driver] 디바이스 드라이버 (0) | 2024.02.23 |