Ubuntu 에 서 는 Android 시스템 에 Linux 커 널 드라이버 구현 방법 을 작성 합 니 다.
여기 서 우 리 는 실제 하드웨어 장 치 를 위해 커 널 드라이버 를 만 들 지 않 을 것 이다.안 드 로 이 드 시스템 이 커 널 드라이버 를 만 드 는 과정 을 편리 하 게 설명 하기 위해 서 우 리 는 가상 하드웨어 장 치 를 사용 합 니 다.이 장 치 는 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 드라이버 와 상호작용 을 할 것 입 니 다.기대 하 시기 바 랍 니 다.
후속 적 으로 관련 글 자 료 를 계속 정리 하고 안 드 로 이 드 소스 코드 를 연구 하 는 친구 에 게 도움 이 되 기 를 바 랍 니 다.여러분 의 지지 에 감 사 드 립 니 다!
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
PowerShell 기반 Ubuntu 시스템 사용 상세 정보본고는 주로 Ubuntu 16.04 LTS에 PowerShell을 설치하고 사용하는 방법을 소개한다.PowerShell Core는 마이크로소프트가 내놓은 크로스 플랫폼(Windows, Linux, macOS) 자동화...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.