EE/Embedded Systems

[Android][2] Device Driver 컴파일

esmJK 2021. 9. 17. 11:11

보드는 FPGA의 Memory Mapped IO Interface로 구성되어 있습니다. 이러한 특징을 이용해 특정 메모리에 값을 쓰면 LED가 켜지고, 부저가 울리게 할 수 있습니다. 이를 위해 특정 역할을 하는 디바이스 드라이버를 작성하여야 합니다. 

 


드라이버 작성 준비 

kdriver라는 Makefile Project를 생성합니다. 

 

 

만든 프로젝트를 선택 후 Miscelleneous Device Driver를 만듭니다. 드라이버 파일 명은 kdriver를 그대로 사용합니다. 

Project Explorer 에 다음과 같이 Driver를 위한 개발 환경이 IDE에서 자동으로 생성되었습니다. 

 

이제 kdriver를 컴파일하게 되면 컴파일 후 보드로 다운로드까지 실행됩니다. kdriver로 자동생성된 샘플코드는 다음과 같습니다. 

 

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>

#include <linux/fs.h>
#include <linux/miscdevice.h>
#include <linux/mutex.h>
	
static int kdriver_open(struct inode * inode, struct file * file){
	printk("kdriver_open, \n");
	
	return 0;
}

static int kdriver_release(struct inode * inode, struct file * file){
	printk("kdriver_release, \n");
	
	return 0;
}

static ssize_t kdriver_read(struct file * file, char * buf, size_t length, loff_t * ofs){
	printk("kdriver_read, \n");
	
	return 0;
}

static ssize_t kdriver_write(struct file * file, const char * buf, size_t length, loff_t * ofs){
	printk("kdriver_write, \n");
	
	return 0;
}

static DEFINE_MUTEX(kdriver_mutex);
static long kdriver_ioctl(struct file * file, unsigned int cmd, unsigned long arg){
	printk("kdriver_ioctl, \n");
	
	switch(cmd){
		default:
			mutex_unlock(&kdriver_mutex);
			return ENOTTY;
	}
	
	mutex_unlock(&kdriver_mutex);
	return 0;
}

static struct file_operations kdriver_fops = {
	.owner = THIS_MODULE,
	.open = kdriver_open,
	.release = kdriver_release,
	.read = kdriver_read,
	.write = kdriver_write,
	.unlocked_ioctl = kdriver_ioctl,
};

static struct miscdevice kdriver_driver = {
	.minor = MISC_DYNAMIC_MINOR,
	.name = "kdriver",
	.fops = &kdriver_fops,
};

static int kdriver_init(void){
	printk("kdriver_init, \n");
	
	return misc_register(&kdriver_driver);
}

static void kdriver_exit(void){
	printk("kdriver_exit, \n");

	misc_deregister(&kdriver_driver);
	
}

module_init(kdriver_init);
module_exit(kdriver_exit);

MODULE_AUTHOR("Author of the kdriver to put it here.");
MODULE_DESCRIPTION("Description of the kdriver to put it here.");
MODULE_LICENSE("Dual BSD/GPL");

 

 

 

Terminal에서 Terminal Settings 선택 후 COM Port 를 확인하고 Connect 합니다. 이후 kdriver를 다시 컴파일 하면 터미널에 성공 메시지가 뜨게 됩니다. 

 

 

 

 

 

 

 

ADB USB Driver

PC와 안드로이드는 유선연결 후 Android Debugger Bridge 라는 유틸리티를 통해 통신합니다. 아래 makefile에서 보이는 것처럼 드라이버를 빌드 후 디바이스로 커널 오브젝트 파일을 옮기고 설치하는 방식은 다음과 같습니다. 

.SILENT:

include C:/AndroXStudio/Tools/make_adb

export ARCH=arm
export CROSS_COMPILE=arm-linux-androideabi-

ifneq ($(KERNELRELEASE),)
obj-m := kdriver.o
else
KDIR := /platform/linux
all:
	$(MAKE) -C $(KDIR) M=$(shell pwd) modules

	echo 
	echo "**** Install:" /system/lib/modules/kdriver.ko "****"
	$(ADB) push kdriver.ko /system/lib/modules/
	$(ADB) shell chmod 644 /system/lib/modules/kdriver.ko
	echo 
	echo "**** Load Module:" /system/lib/modules/kdriver.ko "****"
	$(ADB) shell toolbox rmmod kdriver > /dev/null
	$(ADB) shell insmod /system/lib/modules/kdriver.ko
	$(ADB) shell lsmod | grep kdriver
	echo

endif

clean:
	rm -f *.symvers
	rm -f *.ko
	rm -f *.o
	rm -f *.mod.c
	rm -f *.order
	rm -f .*.cmd
	rm -rf .tmp_versions
  • push kdriver.ko /system/lib/modules/kdriver.ko : 커널 오브젝트 파일이 만들어지고 이것을 디바이스의 /system/lib/modules/ 디렉토리로 Copy 합니다.
  • insmod로 디바이스 드라이버를 설치합니다. (module이 install 될 때 kdriver_init 호출) 
  • grep kdriver : kdriver가 있는지 확인합니다. 

 

 


드라이버 테스트 프로그램 준비 

kdriver를 만들었는데 이것이 제대로 작동하는지 확인하기 위한 테스트를 하여야 합니다. 새로운 C Project를 준비합니다. 

 

 

이번에는 C Executable 을 도구모음에서 선택해줍니다. Want to Run? 질문에 yes 를 입력합니다.

 

 

kdriver_test.c 소스코드를 다음과 같이 작성합니다. main에서는 kdriver를 Write Only로 open 합니다. 

 

리눅스에서 디바이스는 모두 파일로 되어있고, 인식 또한 파일 단위로 합니다. 예를 들어 LED 제어의 경우, LED Device File을 열고, Write Operation을 해야합니다. 

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>

#define NODE_NAME "/dev/kdriver"

int main(int argc, char * argv[]) {
	int fd;
	
	fd = open(NODE_NAME, O_WRONLY);
	if(fd < 0){
		printf("%s open error...\n", NODE_NAME);
		return -1;
	}
	
	write(fd, NULL, 0);
	ioctl(fd,0,0);
	close(fd);
	exit(0);
}

정상적으로 컴파일 될 시 다음과 같이 출력됩니다.