240223

Yonggeun Park·2024년 2월 23일
0

복습

시스템 콜 함수 단점
커널 소스가 비표준임
커널 소스를 빌드하고 업그레이드하고 재부팅을 해야함
디바이스 드라이버는 필요에 따라 동적으로 적재하고 내렸다

LED를 다양한 형태로 제어하고 싶은데 제어하는 어플리케이션이 필요하다
어플리케이션과 디바이스 드라이버 간의 file_operation이 필요
등록과 해제가 가장 처음이다

프롬프트에 보이는 것은 사용자 공간이다(user mode)
커널 공간은 보이지 않고 운영체제가 관리한다(supervisor mode)

두 공간을 왔다갔다 할 수 있는 것이 시스템 콜이다
장치 파일을 모아논 것이 /dev
디바이스 드라이브도 파일처럼 제어
그런데 일반 파일처럼 정보가 저장된 것이 아니다
다양한 방법으로 디바이스 파일을 만들 수 있다

$ sudo mknod /dev/calldev c 230 32


일반 파일은 파일 사이즈가 나오지만 디바이스 파일은 주번호, 부번호가 온다
주번호는 장치(물리적인 하드디스크)를 구분하기 위해 있고, 부번호는 같은 장치에서 채널(같은 하드디스크에서 파티션)을 구분하기 위해 있다
멤버변수 3가지 - 주번호, 이름, 맵 파일

디바이스 파일을 잘못 만든 경우

$ sudo rm -rf /dev/calldev

이렇게 지운다

디바이스 파일을 만들지 않고 실행하면

파일은 만들었지만 디바이스 드라이버를 적재하지 않고 실행하면

적재할 때 register_chrdev를 호출해야하고 내릴 때 반드시 unregister_chrdev를 호출해야함
만약 안하면 GPIO request - free 안했을 때 처럼 busy가 뜬다
재부팅을 하던가 새로운 함수로 unregister 해줘야 하므로 반드시 rmmod 할 것!!

비동기 - 중간에 버퍼를 두겠다는 것
메모리가 아주 큰 버퍼가 필요할 때 사용
우리는 동기를 사용한다

O_NDELAY : 어떤 프로세스를 잠들지 않게 하겠다는 뜻
읽을 데이터가 없으면 0을 리턴
입출력 다중화 함수

파일 정보는 inode와 filp에 다 들어있다
그래서 두개만 있으면 모든 파일에 access할 수 있다


두개의 소스 make로 빌드
디바이스 파일 만들기
디바이스 드라이버 적재
애플리케이션 실행

모듈 사용횟수 관리

open에서 try_module_get을 호출하고
release에서 module_put을 호출한다

에러는 음수를 반환하므로 -를 붙여서 작성한다

커널에서 segmentation fault 출력되면 운영체제가 멈춘다
주의할 것

주변장치 메모리와 코드 메모리가 다르게 관리 됨
io관련 장치 메모리 확인
$ sudo cat /proc/ioports // 우분투
$ sudo cat /proc/iomem // pi


read, write의 두번째 매개변수로 캐릭터 배열을 준다
사용자 공간과 커널 공간에서 서로 직접 접근을 못한다
그래서 put/get_user, copy_to/from_user 함수를 사용한다

삼바의 syscall_ledkey.c 파일을
ledkey_app.c로 바꾸고 p238 폴더에 가져온다

read(dev, buf, size) : key 값 읽기
write(dev, buf, size) : led 값 쓰기

이런 식으로 사용할 예정

Makefile을 수정하고 call_dev.c 파일을 ledkey_dev.c로 변경한다

~/pi_bsp/drivers/p238_ledkey$ mv call_dev.c ledkey_dev.c


안쓸 내용은 없애주고

헤더파일을 모두 가져오고 make 한다

pi로 가서

/mnt/ubuntu_nfs $ sudo mknod /dev/ledkey c 230 0
/mnt/ubuntu_nfs $ sudo insmod ledkey_dev.ko
/mnt/ubuntu_nfs $ sudo ./ledkey_app

그런데 이렇게 하면 실패를 안해도 그냥 들어간다
예외처리가 없기 때문에

예외처리 해주고 다시 하면

무한대기가 아니다

:%s/call/ledkey/g
:%s/CALL/LEDKEY/g

call이라는 문자를 ledkey로 모두 바꾸는 명령어

포인터는 메모리 주소이면서 몇 바이트인지 크기를 가리킴
void 포인터는 주소만 가리키고 크기는 무시함

const : 상수화 하는 것 > 없으면 주소를 다른 것으로 바꿀 수 있음
앞의 매개변수는 소스를 가리켜서 const로 막아줌
read only 영역에 write를 시도하는 것을 에러로 표현해준다
만약 없으면 컴파일 시에는 넘어가지만 실행 때 segmenataion fault가 발생한다

:7,109 w gpio.txt
저장한다

:r /home/ubuntu/pi_bsp/drivers/p106_ledkey/gpio.txt
읽어온다

복사한다
이제 init을 해야하는데 해당 내용은

디바이스 드라이버 적재 전에 하면 좋을 것 같다

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

#pragma GCC diagnostic ignored "-Wunused-result"
int main(int argc, char *argv[])
{
	char buff;
	int i;
	int ledkeyfd;
	int key_data,key_data_old=0;
	unsigned long val=0;
	if(argc < 2)
	{
		printf("USAGE : %s ledVal[0x00~0xff]\n",argv[0]);
		return 1;
	}
	val = strtoul(argv[1],NULL,16);
	if(val<0 || 0xff<val)
	{
		printf("Usage : %s ledValue[0x00~0xff]\n",argv[0]);
		return 2;
	}
	ledkeyfd = open("/dev/ledkey", O_RDWR | O_NONBLOCK);
	if(ledkeyfd < 0)
	{
		perror("open()");
		return 1;
	}
	buff = (char)val;
	write(ledkeyfd, &buff, sizeof(buff));
	do {
		usleep(100000);  //100MSec
//		key_data = syscall(__NR_mysyscall,val);
		if(key_data != key_data_old)
		{
			key_data_old = key_data;
			if(key_data)
			{
				val = key_data;
				puts("0:1:2:3:4:5:6:7");
				for(i=0;i<8;i++)
				{
					if(key_data & (0x01 << i))
						putchar('O');
					else
						putchar('X');
					if(i != 7 )
						putchar(':');
					else
						putchar('\n');
				}
				putchar('\n');
			}
			if(key_data == 0x80)
				break;
		}
	}while(1);
	printf("mysyscall return value = %#04x\n",key_data);
	return 0;
}

ledkey_app.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>

#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};

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 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 ledkey_open(struct inode *inode, struct file *filp)
{
	int num0 = MAJOR(inode->i_rdev);
	int num1 = MINOR(inode->i_rdev);
	printk("ledkey open-> major : %d\n", num0);
	printk("ledkey open-> minor : %d\n", num1);
	try_module_get(THIS_MODULE);
	return 0;
}
static loff_t ledkey_llseek(struct file *filp, loff_t off, int whence)
{
	printk("ledkey llseek -> off : %08X, whence : %08X\n", (unsigned int)off, whence);
	return 0x23;
}
static ssize_t ledkey_read(struct file *filp, char *buf, size_t count, loff_t *f_pos)
{
	printk("ledkey read -> buf : %08X, count : %08X \n", (unsigned int)buf, count);
	return 0x33;
}
static ssize_t ledkey_write(struct file *filp, const char *buf, size_t count, loff_t *f_ops)
{
	printk("ledkey write -> buf : %08X, count : %08X \n", (unsigned int)buf, count);
	gpioLedSet(0xff);
	return 0x43;
}
static long ledkey_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
	printk("ledkey ioctl -> cmd : %08X, arg : %08X\n", cmd, (unsigned int)arg);
	return 0x53;
}
static int ledkey_release(struct inode *inode, struct file *filp)
{
	printk("ledkey release \n");
	module_put(THIS_MODULE);
	return 0;
}
struct file_operations ledkey_fops =
{
//	.owner = THIS_MODULE,
	.llseek = ledkey_llseek,
	.read = ledkey_read,
	.write = ledkey_write,
	.unlocked_ioctl = ledkey_ioctl,
	.open = ledkey_open,
	.release = ledkey_release,
};
static int ledkey_init(void)
{
	int result;
	printk("ledkey ledkey_init \n");
	result=gpioLedInit();
	if(result < 0) {
		return result; }
	result=gpioKeyInit();
	if(result < 0) {
		return result; }

	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);
	gpioLedFree();
	gpioKeyFree();
}
module_init(ledkey_init);
module_exit(ledkey_exit);
MODULE_LICENSE("Dual BSD/GPL");

ledkey_dev.c

write

0x0f를 커널에 넘기려고 하는데
버퍼라는 주소값으로 받았는데 이 주소가 사용자 공간이기 때문에
0x0f를 커널공간에서 받아야한다
get_user는 1바이트 받을 때 편하고 copy_from_user는 많은 데이터를 받을 때 좋다


리턴값이 있음

리턴값을 안받으면 이런 오류가 뜬다

write 부분을 다음과 같이 수정하면 된다

static ssize_t ledkey_write(struct file *filp, const char *buf, size_t count, loff_t *f_ops)
{
/*  int i;
    char kbuff[10];
    for(i=0;i<count;i++) {
        get_user(kbuff[i], buf++); }
*/
    char kbuff;
    int result;
    printk("ledkey write -> buf : %08X, count : %08X \n", (unsigned int)buf, count);
//  get_user(kbuff, buf);
    result=copy_from_user(&kbuff, buf, count);

    gpioLedSet(kbuff);
    return count;
}

데이터 타입은 적은 것을 큰 공간에 복사하기 때문에 괜찮다

read

put_user / copy_to_user 함수를 사용해야한다

ledkey_app.c을 수정한다

static ssize_t ledkey_read(struct file *filp, char *buf, size_t count, loff_t *f_pos)
{
    int result;
    char kbuf;
    printk("ledkey read -> buf : %08X, count : %08X \n", (unsigned int)buf, count);
    kbuf=(char)gpioKeyGet();
//  put_user(kbuf, buf);
    result = copy_to_user(buf, &kbuf, count);
    return 0x33;
}

이렇게 하면

스위치도 잘 먹는다

profile
Dragon_muscle

0개의 댓글

관련 채용 정보