Ubuntu 에 서 는 Android 시스템 에 Linux 커 널 드라이버 구현 방법 을 작성 합 니 다.

스마트 폰 시대 에는 브랜드 마다 개성 있 는 휴대 전화 가 있 었 다.바로 이런 남 다른 개성 으로 사용 자 를 끌 어 들 이 고 브랜드 응집력 과 사용자 충성 도 를 조성 하 는 것 이다.전형 적 인 대 표 는 아이 폰 이 아 닐 수 없다.2011 년 5 월 현재 앱 스토어 의 앱 수 는 3 만 8 천 1062 개 로 1 위 를 차 지 했 고,안 드 로 이 드 마켓 의 앱 수 는 2 만 9 천 4738 개 로 앱 스토어 뒤 를 이 어 8 월 에 앱 스토어 를 넘 어 설 전망 이다.안 드 로 이 드 시스템 이 점차 시장 점유 율 을 확대 함 에 따라 단말기 의 다양성 은 더 많은 모 바 일 개발 자의 참여 가 시 급 하 다.업계 통계 에 따 르 면 안 드 로 이 드 연구개 발 인재 의 부족 은 최소 30 만 명 이다.현재 안 드 로 이 드 인재 에 대한 수 요 는 하드웨어 구동 에 치 우 친 안 드 로 이 드 인재 수요 이 고 하 나 는 소프트웨어 응용 에 치 우 친 안 드 로 이 드 인재 수요 이다.전반적 으로 안 드 로 이 드 하드웨어 구동 에 뜻 을 둔 개발 엔지니어 에 게 지금 은 주먹 과 발 을 크게 펼 칠 기회 다.그렇다면 안 드 로 이 드 시스템 을 위해 커 널 드라이버 를 만 드 는 방법 을 살 펴 보 자.
        여기 서 우 리 는 실제 하드웨어 장 치 를 위해 커 널 드라이버 를 만 들 지 않 을 것 이다.안 드 로 이 드 시스템 이 커 널 드라이버 를 만 드 는 과정 을 편리 하 게 설명 하기 위해 서 우 리 는 가상 하드웨어 장 치 를 사용 합 니 다.이 장 치 는 4 바이트 의 레지스터 만 있 습 니 다.읽 고 쓸 수 있 습 니 다.우리 가 프로그램 언어 를 처음 배 웠 을 때'Hello,World'를 예 로 들 었 던 것 을 생각하면 우 리 는 이 가상 장 치 를'hello'라 고 부 르 고 이 커 널 드라이버 도 hello 드라이버 라 고 부른다.사실,안 드 로 이 드 커 널 드라이버 는 일반 리 눅 스 커 널 드라이버 의 작성 방법 과 같 습 니 다.모두 리 눅 스 모듈 형식 으로 이 루어 졌 습 니 다.구체 적 으로 앞의 안 드 로 이 드 학습 시작 편 에서 언급 한 리 눅 스 Device Drivers 라 는 책 을 참고 할 수 있 습 니 다.그러나 여기 서 우 리 는 안 드 로 이 드 시스템 의 측면 에서 안 드 로 이 드 커 널 드라이버 의 작성 과 컴 파일 과정 을 묘사 합 니 다.
       1.앞의 두 글Android 원본 은 Ubuntu 에서 다운로드,컴 파일,설치과 재Android 커 널 소스 는 Ubuntu 에서 다운로드,컴 파일,설치(Linux Kernel)를 참조 하여 안 드 로 이 드 커 널 드라이버 개발 환경 을 준비 합 니 다.
       2.kernel/common/drivers 디 렉 터 리 에 들 어가 서 hello 디 렉 터 리 를 새로 만 듭 니 다.
             USER-NAME@MACHINE-NAME:~/Android$ cd kernel/common/drivers
             USER-NAME@MACHINE-NAME:~/Android/kernel/common/drivers$ mkdir hello
      3.hello 디 렉 터 리 에 hello.h 파일 추가:

#ifndef _HELLO_ANDROID_H_
#define _HELLO_ANDROID_H_

#include <linux/cdev.h>
#include <linux/semaphore.h>

#define HELLO_DEVICE_NODE_NAME "hello"
#define HELLO_DEVICE_FILE_NAME "hello"
#define HELLO_DEVICE_PROC_NAME "hello"
#define HELLO_DEVICE_CLASS_NAME "hello"

struct hello_android_dev {
	int val;
	struct semaphore sem;
	struct cdev dev;
};

#endif
        이 헤더 파일 은 문자열 상수 매크로 를 정의 합 니 다.뒤에서 사용 해 야 합 니 다.그 밖 에 문자 장치 구조 체 hello 도 정의 했다.android_dev,이것 이 바로 우리 의 가상 하드웨어 장치 입 니 다.val 구성원 변 수 는 장치 안의 레지스터 를 대표 합 니 다.그의 유형 은 int 이 고 sem 구성원 변 수 는 신 호 량 입 니 다.레지스터 val 에 동기 적 으로 접근 하 는 것 입 니 다.dev 구성원 변 수 는 내 장 된 문자 장치 입 니 다.이 Linux 드라이버 는 문자 장치 구조 체 를 사용자 정의 하 는 표준 방법 입 니 다.
        4.hello 디 렉 터 리 에 hello.c 파일 을 추가 합 니 다.이것 은 드라이버 의 실현 부분 입 니 다.
        드라이버 의 기능 은 읽 기와 쓰 기 를 포함 하여 상부 에 접근 장치 의 레지스터 값 을 제공 하 는 것 이다.장치 레지스터 에 접근 하 는 세 가지 방법 을 제공 합 니 다.하 나 는 proc 파일 시스템 을 통 해 접근 하 는 것 이 고,다른 하 나 는 전통 적 인 장치 파일 방법 으로 접근 하 는 것 입 니 다.다른 하 나 는 devfs 파일 시스템 을 통 해 접근 하 는 것 입 니 다.다음 단락 은 이 드라이버 의 실현 을 설명 한다.
         우선 필요 한 헤더 파일 과 세 가지 접근 장 치 를 정의 하 는 방법 을 포함 합 니 다.

#include <linux/init.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/proc_fs.h>
#include <linux/device.h>
#include <asm/uaccess.h>

#include "hello.h"

/*          */
static int hello_major = 0;
static int hello_minor = 0;

/*         */
static struct class* hello_class = NULL;
static struct hello_android_dev* hello_dev = NULL;

/*           */
static int hello_open(struct inode* inode, struct file* filp);
static int hello_release(struct inode* inode, struct file* filp);
static ssize_t hello_read(struct file* filp, char __user *buf, size_t count, loff_t* f_pos);
static ssize_t hello_write(struct file* filp, const char __user *buf, size_t count, loff_t* f_pos);

/*         */
static struct file_operations hello_fops = {
	.owner = THIS_MODULE,
	.open = hello_open,
	.release = hello_release,
	.read = hello_read,
	.write = hello_write, 
};

/*        */
static ssize_t hello_val_show(struct device* dev, struct device_attribute* attr, char* buf);
static ssize_t hello_val_store(struct device* dev, struct device_attribute* attr, const char* buf, size_t count);

/*      */
static DEVICE_ATTR(val, S_IRUGO | S_IWUSR, hello_val_show, hello_val_store);
 
   전통 적 인 장치 파일 접근 방법 을 정의 합 니 다.주로 hello 를 정의 합 니 다.open、hello_release、hello_read 와 hellowrite 이 네 가지 장치 파일 을 열 고,풀 고,읽 고,쓰 는 방법:

/*      */
static int hello_open(struct inode* inode, struct file* filp) {
	struct hello_android_dev* dev; 
	
	/*                       ,          */
	dev = container_of(inode->i_cdev, struct hello_android_dev, dev);
	filp->private_data = dev;
	
	return 0;
}

/*         ,   */
static int hello_release(struct inode* inode, struct file* filp) {
	return 0;
}

/*        val  */
static ssize_t hello_read(struct file* filp, char __user *buf, size_t count, loff_t* f_pos) {
	ssize_t err = 0;
	struct hello_android_dev* dev = filp->private_data; 

	/*    */
	if(down_interruptible(&(dev->sem))) {
		return -ERESTARTSYS;
	}

	if(count < sizeof(dev->val)) {
		goto out;
	} 

	/*    val             */
	if(copy_to_user(buf, &(dev->val), sizeof(dev->val))) {
		err = -EFAULT;
		goto out;
	}

	err = sizeof(dev->val);

out:
	up(&(dev->sem));
	return err;
}

/*        val*/
static ssize_t hello_write(struct file* filp, const char __user *buf, size_t count, loff_t* f_pos) {
	struct hello_android_dev* dev = filp->private_data;
	ssize_t err = 0; 

	/*    */
	if(down_interruptible(&(dev->sem))) {
		return -ERESTARTSYS; 
	} 

	if(count != sizeof(dev->val)) {
		goto out; 
	} 

	/*                   */
	if(copy_from_user(&(dev->val), buf, count)) {
		err = -EFAULT;
		goto out;
	}

	err = sizeof(dev->val);

out:
	up(&(dev->sem));
	return err;
}
devfs 파일 시스템 을 통 해 접근 하 는 방법 을 정의 합 니 다.장치 의 레지스터 val 을 장치 의 속성 으로 보고 이 속성 을 읽 고 쓰기 로 장 치 를 방문 합 니 다.주로 hello 를 실현 합 니 다.val_show 와 helloval_store 두 가지 방법,동시에 두 내부 에서 사용 하 는 접근 val 값 을 정의 하 는 방법hello_get_val 와hello_set_val:

/*     val      buf ,    */
static ssize_t __hello_get_val(struct hello_android_dev* dev, char* buf) {
	int val = 0; 

	/*    */
	if(down_interruptible(&(dev->sem))) { 
		return -ERESTARTSYS; 
	} 

	val = dev->val; 
	up(&(dev->sem)); 

	return snprintf(buf, PAGE_SIZE, "%d
", val); } /* buf val , */ static ssize_t __hello_set_val(struct hello_android_dev* dev, const char* buf, size_t count) { int val = 0; /* */ val = simple_strtol(buf, NULL, 10); /* */ if(down_interruptible(&(dev->sem))) { return -ERESTARTSYS; } dev->val = val; up(&(dev->sem)); return count; } /* val*/ static ssize_t hello_val_show(struct device* dev, struct device_attribute* attr, char* buf) { struct hello_android_dev* hdev = (struct hello_android_dev*)dev_get_drvdata(dev); return __hello_get_val(hdev, buf); } /* val*/ static ssize_t hello_val_store(struct device* dev, struct device_attribute* attr, const char* buf, size_t count) { struct hello_android_dev* hdev = (struct hello_android_dev*)dev_get_drvdata(dev); return __hello_set_val(hdev, buf, count); }
  proc 파일 시스템 을 통 해 접근 하 는 방법 을 정의 합 니 다.주로 helloproc_read 와 helloproc_write 두 가지 방법 은 proc 파일 시스템 에서 파일 을 만 들 고 삭제 하 는 방법 을 동시에 정의 합 니 다 hellocreate_proc 와 helloremove_proc:

/*       val  ,   page    */
static ssize_t hello_proc_read(char* page, char** start, off_t off, int count, int* eof, void* data) {
	if(off > 0) {
		*eof = 1;
		return 0;
	}

	return __hello_get_val(hello_dev, page);
}

/*      buff        val  */
static ssize_t hello_proc_write(struct file* filp, const char __user *buff, unsigned long len, void* data) {
	int err = 0;
	char* page = NULL;

	if(len > PAGE_SIZE) {
		printk(KERN_ALERT"The buff is too large: %lu.
", len); return -EFAULT; } page = (char*)__get_free_page(GFP_KERNEL); if(!page) { printk(KERN_ALERT"Failed to alloc page.
"); return -ENOMEM; } /* */ if(copy_from_user(page, buff, len)) { printk(KERN_ALERT"Failed to copy buff from user.
"); err = -EFAULT; goto out; } err = __hello_set_val(hello_dev, page, len); out: free_page((unsigned long)page); return err; } /* /proc/hello */ static void hello_create_proc(void) { struct proc_dir_entry* entry; entry = create_proc_entry(HELLO_DEVICE_PROC_NAME, 0, NULL); if(entry) { entry->owner = THIS_MODULE; entry->read_proc = hello_proc_read; entry->write_proc = hello_proc_write; } } /* /proc/hello */ static void hello_remove_proc(void) { remove_proc_entry(HELLO_DEVICE_PROC_NAME, NULL); }
 마지막 으로 모듈 로 딩 과 마 운 트 해제 방법 을 정의 합 니 다.장치 등록 과 초기 화 작업 만 수행 하면 됩 니 다.

static int __hello_setup_dev(struct hello_android_dev* dev) {
	int err;
	dev_t devno = MKDEV(hello_major, hello_minor);

	memset(dev, 0, sizeof(struct hello_android_dev));

	cdev_init(&(dev->dev), &hello_fops);
	dev->dev.owner = THIS_MODULE;
	dev->dev.ops = &hello_fops; 

	/*      */
	err = cdev_add(&(dev->dev),devno, 1);
	if(err) {
		return err;
	} 

	/*          val  */
	init_MUTEX(&(dev->sem));
	dev->val = 0;

	return 0;
}

/*      */
static int __init hello_init(void){ 
	int err = -1;
	dev_t dev = 0;
	struct device* temp = NULL;

	printk(KERN_ALERT"Initializing hello device.
"); /* */ err = alloc_chrdev_region(&dev, 0, 1, HELLO_DEVICE_NODE_NAME); if(err < 0) { printk(KERN_ALERT"Failed to alloc char dev region.
"); goto fail; } hello_major = MAJOR(dev); hello_minor = MINOR(dev); /* helo */ hello_dev = kmalloc(sizeof(struct hello_android_dev), GFP_KERNEL); if(!hello_dev) { err = -ENOMEM; printk(KERN_ALERT"Failed to alloc hello_dev.
"); goto unregister; } /* */ err = __hello_setup_dev(hello_dev); if(err) { printk(KERN_ALERT"Failed to setup dev: %d.
", err); goto cleanup; } /* /sys/class/ hello*/ hello_class = class_create(THIS_MODULE, HELLO_DEVICE_CLASS_NAME); if(IS_ERR(hello_class)) { err = PTR_ERR(hello_class); printk(KERN_ALERT"Failed to create hello class.
"); goto destroy_cdev; } /* /dev/ /sys/class/hello hello*/ temp = device_create(hello_class, NULL, dev, "%s", HELLO_DEVICE_FILE_NAME); if(IS_ERR(temp)) { err = PTR_ERR(temp); printk(KERN_ALERT"Failed to create hello device."); goto destroy_class; } /* /sys/class/hello/hello val*/ err = device_create_file(temp, &dev_attr_val); if(err < 0) { printk(KERN_ALERT"Failed to create attribute val."); goto destroy_device; } dev_set_drvdata(temp, hello_dev); /* /proc/hello */ hello_create_proc(); printk(KERN_ALERT"Succedded to initialize hello device.
"); return 0; destroy_device: device_destroy(hello_class, dev); destroy_class: class_destroy(hello_class); destroy_cdev: cdev_del(&(hello_dev->dev)); cleanup: kfree(hello_dev); unregister: unregister_chrdev_region(MKDEV(hello_major, hello_minor), 1); fail: return err; } /* */ static void __exit hello_exit(void) { dev_t devno = MKDEV(hello_major, hello_minor); printk(KERN_ALERT"Destroy hello device.
"); /* /proc/hello */ hello_remove_proc(); /* */ if(hello_class) { device_destroy(hello_class, MKDEV(hello_major, hello_minor)); class_destroy(hello_class); } /* */ if(hello_dev) { cdev_del(&(hello_dev->dev)); kfree(hello_dev); } /* */ unregister_chrdev_region(devno, 1); } MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("First Android Driver"); module_init(hello_init); module_exit(hello_exit);
       5.hello 디 렉 터 리 에 KConfig 와 Makefile 두 개의 파일 을 추가 합 니 다.그 중에서 KConfig 는 컴 파일 하기 전에 설정 명령 make menuconfig 를 실행 할 때 사 용 됩 니 다.Makefile 은 컴 파일 명령 make 를 실행 할 때 사 용 됩 니 다.
       Kconfig 파일 의 내용
           config HELLO
           tristate "First Android Driver"
           default n
           help
           This is the first android driver.
       Makefile 파일 의 내용     
       obj-$(CONFIG_HELLO) += hello.o
      Kconfig 파일 에서 tristate 는 컴 파일 옵션 HELLO 가 커 널 을 컴 파일 할 때 hello 모듈 은 모듈,내장,컴 파일 하지 않 는 세 가지 컴 파일 방법 을 지원 합 니 다.기본 값 은 컴 파일 하지 않 습 니 다.따라서 커 널 을 컴 파일 하기 전에 make menuconfig 명령 을 실행 하여 컴 파일 옵션 을 설정 하여 hello 가 모듈 이나 내장 방법 으로 컴 파일 할 수 있 도록 해 야 합 니 다.
      Makefile 파일 에 서 는 HELLO 의 값 에 따라 컴 파일 방법 이 다 릅 니 다.
      6.arch/arm/Kconfig 와 drivers/kconfig 두 파일 을 수정 하고 menu"Device Drivers"와 endmenu 사이 에 한 줄 을 추가 합 니 다. 
           source "drivers/hello/Kconfig"   
       이렇게 하면 make menuconfig 를 실행 할 때 hello 모듈 의 컴 파일 옵션 을 설정 할 수 있 습 니 다.
      7.drivers/Makefile 파일 을 수정 하고 줄 을 추가 합 니 다.    
           obj-$(CONFIG_HELLO) += hello/
       8.컴 파일 옵션 설정:
        USER-NAME@MACHINE-NAME:~/Android/kernel/common$ make menuconfig     
        "Device Drivers"=>"First Android Drivers"옵션 을 찾 아 y 로 설정 합 니 다.       
커 널 이 동적 로드 모듈 을 지원 하지 않 는 다 면 m 를 선택 할 수 없습니다.KConfig 파일 에 HELLO 옵션 을 tristate 로 설 정 했 지만.동적 로드 모듈 옵션 을 지원 하려 면 설정 메뉴 에서 Enable loadable module support 옵션 을 선택해 야 합 니 다.동적 마 운 트 해제 모듈 옵션 을 지원 하려 면 Enable loadable module support 메뉴 항목 에서 Module unloading 옵션 을 선택해 야 합 니 다.
       9.컴 파일:     
      USER-NAME@MACHINE-NAME:~/Android/kernel/common$ make       
      컴 파일 에 성공 하면 hello 디 렉 터 리 에서 hello.o 파일 을 볼 수 있 습 니 다.이때 컴 파일 된 zImage 에는 hello 드라이브 가 포함 되 어 있 습 니 다.    
      10.Ubuntu 에서 Android 최신 커 널 코드(Linux Kernel)를 다운로드,컴 파일,설치 한 것 을 참조 하여 새로 컴 파일 된 커 널 파일 을 실행 하고 hello 드라이버 가 정상적으로 설치 되 었 는 지 확인 합 니 다.      
        USER-NAME@MACHINE-NAME:~/Android$ emulator -kernel ./kernel/common/arch/arm/boot/zImage &
        USER-NAME@MACHINE-NAME:~/Android$ adb shell      
        dev 디 렉 터 리 에 들 어가 면 hello 장치 파일 을 볼 수 있 습 니 다:      
        root@android:/ # cd dev
        root@android:/dev # ls      
        proc 디 렉 터 리 에 들 어가 면 hello 파일 을 볼 수 있 습 니 다:   
        root@android:/ # cd proc
        root@android:/proc # ls    
        hello 파일 에 접근 하 는 값:    
        root@android:/proc # cat hello
        0
        root@android:/proc # echo '5' > hello
        root@android:/proc # cat hello
        5
        sys/class 디 렉 터 리 에 들 어가 면 hello 디 렉 터 리 를 볼 수 있 습 니 다.
        root@android:/ # cd sys/class
        root@android:/sys/class # ls
        hello 디 렉 터 리 에 들 어가 면 hello 디 렉 터 리 를 볼 수 있 습 니 다.
        root@android:/sys/class # cd hello
        root@android:/sys/class/hello # ls
        다음 hello 디 렉 터 리 로 들 어가 면 val 파일 을 볼 수 있 습 니 다:
        root@android:/sys/class/hello # cd hello
        root@android:/sys/class/hello/hello # ls
        속성 파일 val 에 접근 하 는 값:
        root@android:/sys/class/hello/hello # cat val
        5
        root@android:/sys/class/hello/hello # echo '0'  > val
        root@android:/sys/class/hello/hello # cat val
        0    
  이로써 우리 의 hello 커 널 드라이버 가 완성 되 었 고 모든 것 이 정상 임 을 검증 했다.여기 서 저 희 는 시스템 이 제공 하 는 방법 과 드라이버 가 상호작용 을 하 는 것 입 니 다.즉,proc 파일 시스템 과 devfs 파일 시스템 을 통 해 다음 글 에서 저 희 는 자신 이 컴 파일 한 C 언어 프로그램 을 통 해/dev/hello 파일 에 접근 하여 hello 드라이버 와 상호작용 을 할 것 입 니 다.기대 하 시기 바 랍 니 다.
 후속 적 으로 관련 글 자 료 를 계속 정리 하고 안 드 로 이 드 소스 코드 를 연구 하 는 친구 에 게 도움 이 되 기 를 바 랍 니 다.여러분 의 지지 에 감 사 드 립 니 다!

좋은 웹페이지 즐겨찾기