3. 디바이스 드라이버 작성하기
3.1 데이터시트와 레지스터
향후 추가
3.2 디바이스 드라이버 표준 API
1. 디바이스 드라이버 작성 절차
1) 디바이스 드라이버 이름, 주번호 결정
2) 코어 함수 구현
3) 커널 연계 래퍼 작성 : 코어 함수를 커널에 등록하기 위해 리눅스 디바이스 드라이버가 참조하는 파일 오퍼레이션 구조체 (fops)에 대한 인터페이스 함수 구현
ㄴ open(), release(), read(), write(), ioctl() 인터페이스 함수 구현
ㄴ file_operations 구조체에 인터페이스 함수 등록
4) 커널에 등록 : 커널 내부 함수(register_XXXdev) 이용하여 개발한 디바이스 드라이버를 Insmod 명령을 이용하여 커널에 등록
5) 장치 파일 생성 : mknod 명령을 이용하여 디바이스 파일을 생성
2. 디바이스 드라이버 예제
#include <kernel.h>
#include <module.h>
#include <device.h>
#define DEVICE_NAME "MyTestDrv"
int result;
static const struct file_operations fops =
{
.owner = THIS_MODULE,
.open = myDrv_open,
.release = myDrv_release
};
int myDrv_open(struct inode *inode_p, struct file *fp){
}
int myDrv_release(struct inode *inode_p, struct file *fp){
}
static int myDrv_init(void)
{
result = register_chrdev(0, DEVICE_NAME, &fops);
printk("device number %d is registered", result);
return 0;
}
static void myDrv_exit(void)
{
unregister_chrdev(result, DEVICE_NAME);
printk("device number %d is unregistered", result);
}
module_init(myDrv_init);
module_exit(myDrv_exit);
3.3 디바이스 드라이버 저수준 OS API 활용하기
1. 디바이스 드라이버 데이터 전송 과정
1) 데이터 전송 과정에 필요한 별도 API
① get_user(void* x, constant void *addr)
- 사용자 공간 영역에서 커널 공간 영역으로 데이터 복사
② put_user(void* x, constant void *addr)
- 커널 공간 영역에서 사용자 공간 영역으로 데이터 복사
③ copy_to_user(void* to, void* from, unsigned long size)
- 커널 공간 영역에서 사용자 공간 영역으로 데이터 복사
④ copy_from_user(void* to, void* from, unsigned long size)
- 사용자 공간 영역에서 커널 공간 영역으로 데이터 복사
3.4 디바이스 드라이버 초기화 및 접근
1. 디바이스 드라이버 등록 / 해제
1) 모듈 초기화
- register_XXXdev() 함수 사용
ㄴ 실행 결과 성공 시 Major Number 반환
2) 모듈 커널 해제
- unregister_XXXdev() 함수 사용
3.5 Make 파일의 이해
1. Make 파일
1) make
- Unix/Linux 계열에서 빌드 자동화 지원하는 명령어
- Makefile이라는 명칭의 파일을 참조하여 빌드를 진행
2) Makefile
① Makefile 기본 규칙
<Target>: <Dependencies>
<Recipe>
- Target : 빌드 대상 이름. 최종적으로 생성하는 파일명
- Dependencies : 빌드 대상이 의존하는 대상. 여기에 명시된 라벨 명령들이 이미 완료된 상태여야 Target이 실행됨
- Recipe : 빌드 대상 생성하는 명령. 반드시 Tab 문자로 된 Indent 필요
② Makefile 옵션
- -D : #define과 같이 선언 정의
- -D__KERNEL__ : 커널의 변수와 함수를 사용한다는 정의
- -DMODULE : 모듈 형태로 로드 되어 사용한다는 정의
- -W : warning 출력
- -Wall : 모든 warning 출력
- -I : include
3.6 인터럽트 처리 절차
1. 인터럽트
1) 인터럽트 등록
① 인터럽트 등록은 request_irq 함수를 이용한다
int request_irq(unsigned int irq, irqreturn_t (*handler) (int, void *, struct pt_regs *), unsigned long flags, const char* dev_name, void* dev_id)
- unsigned int irq : 요청 인터럽트 번호
- *handler : 인터럽트를 처리할 함수 포인터
- unsigned long flags : 인터럽트 관리 옵션 비트 마스크
ㄴ IRQ_DISABLED : 인터럽트 발생 비활성화
ㄴ IRQF_SHARED : 인터럽트 공유
ㄴ IRQF_SAMPLE_RANDOM : 난수를 발생
- dev_name, dev_id : 인터럽트 소유자 정보 문자열, 포인터
2) 인터럽트 해제
void free_irq(unsigned int irq, void* dev_id)
- unsigned int irq : 요청 인터럽트 번호
- void *dev_id : request_irq에서 사용한 dev_id 값
3) 인터럽트 활성화 / 비활성화
① 모든 인터럽트
- 비활성화
ㄴ void local_irq_save(unsigned long flags)
ㄴ void local_irq_disable(void)
- 활성화
ㄴ void local_irq_restore(unsigned long flags)
ㄴ void local_irq_enable(void)
② 특정 인터럽트
- void disable_irq(int irq)
- void enable_irq(int irq)
2. 커널 타이머
1) 커널 타이머
- 사용자가 정의한 함수가 특정 시간에 실행되도록 하는 역할을 함
2) 커널 타이머 함수
- init_timer : 커널 타이머 구조체 초기화
- add_timer : 커널 타이머 수행될 함수 등록
- del_timer : 커널 타이머 목록에서 등록된 것 제거
# References
- https://wogh8732.tistory.com/304