ADC_디바이스드라이버_동작설명

Download Report

Transcript ADC_디바이스드라이버_동작설명

ADC
울산대 MCSL 김준수
디바이스 드라이버란?
운영체제는 사용자모드와 커널모드로 나뉨.
사용자모드에서 필요시 System Call을 이용해 커널모드 진입 가능.
디바이스 드라이버는 디바이스를 제어하는 함수들의 모음. 부팅이 된
후에 모듈로 적재되는 경우도 있기 때문에 일정한 구조로 되어있다.
디바이스 드라이버는 사용자모드에서 하드웨어 제어를 하기위해
System Call을 이용해 호출하는 파일
2.6 Kernel
- system call에 의한 커널의 구체적인 동작
1. Processor가 App을 수행하는 중에 system call 명령을 만나면
커널 영역으로 뛰어 들어간다.
2. 커널 영역에서 device로부터 data 읽기를 요청
3. 현재 수행중인 process 를 잠시 멈추기 위해 wait queue에 넣는다
4. 새로운 process를 선택
2.6 Kernel Device Driver
- 디바이스의 종류가 다양해짐에 따라 상호간의 연관성이 중요시되어
드라이버를 계층화 시켜 관리할 필요성이 점점 제기되었다.
- 따라서 2.6커널에서는 kobject라는 드라이버 객체 개념이 추가됨.
등록된 버스가 있고 버스에 새로운 디바이스가 장착되면 버스 드라
이버가 디바이스를 탐지하고 이에 맞는 드라이버를 찾아 등록하게
된다.
ADC는 여러 버스 형태 중 하나인 platform_bus_type 라는 가상의
플랫폼 버스 상에 새로운 디바이스를 등록하는 방법이다.
(이미 등록된 플랫폼 드라이버(Major)안에서 Device 등록(Minor)를
하는 방식)
ADC 디바이스 드라이버 동작
ADC 디바이스 드라이버의 동작 순서는 다음과 같다.
1.
2.
3.
4.
5.
6.
7.
platform_driver 등록
Probe함수 실행 (ADC 필수 레지스터 초기화)
Probe 과정에서 장치 리소스를 확인 / 설정(Clock, platform_data)
Miscdevice 등록 (Miscdevice name과 같은 platform_driver 매칭)
file_operations 등록(.fops name)
file_operations에서 원하는 동작 함수들이 존재
User App에서 필요한 함수들을 System Call로 호출
알아두어야 할 구조체
Struct
Struct
Struct
Struct
device_driver
platform_device & driver
miscdevice
file_operations
struct device_driver 구조체
#include <linux/device.h>
기타 장치 파일을 등록하는데 필요한 함수를 포함
struct device_driver {
const char *name;
//디바이스 드라이버의 이름(필수)
struct bus_type *bus;
//버스 네임(필수)
struct kobject kobj;
//커널이 관리
struct module *owner;
// 보통 THIS_MODULE
int (*probe) (struct device *dev);
// 초기화 루틴
int (*remove) (struct device *dev);
// 디바이스 제거 루틴
int (*suspend) (struct device *dev, pm_message_t state, u32 level);
//절전모드 (필수)
int (*resume) (struct device *dev, u32 level); //절전모드 off (필수)
…. 생략
};
Struct platform_device
#include <platform_device>
struct platform_device {
const char * name;
//platform_driver에 정의된 이름과 같아야 한다.
int
id;
struct devicedev;
u32
num_resources;
struct resource
* resource;
struct platform_device_id
*id_entry;
/* arch specific additions */
struct pdev_archdataarchdata;
};
Miscdevice 구조체
#include <linux/miscdevice.h>
struct miscdevice {
int minor;
//Device driver minor number
(지정하지 않을시 커널이 동적으로 할당)
const char *name; //device file Name
const struct file_operations *fops;
// 파일 연산을 할 때 호출될 함수 정의
… 생략
}
struct file_operations 구조체
struct file_operations {
struct module *owner;
//보통 THIS_MODULE
ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
int (*ioctl) (struct inode *, struct file *, unsigned int,
unsigned long);
// Read와 Write만으로 구현하기 곤란한 입출력 처리
int (*open) (struct inode *, struct file *);
// 응용 프로그램에서 디바이스를 처음 사용하는 경우(필수)
int (*release) (struct inode *, struct file *);
// 종료
…. 생략
};
ADC Platform_driver
static struct platform_driver s3c_adc_driver = {
.probe
= s3c_adc_probe,
.remove
= s3c_adc_remove,
.suspend
= s3c_adc_suspend,
.resume
= s3c_adc_resume,
.driver
={
.owner
= THIS_MODULE,
.name
= "s3c-adc",
},
};
Device Dirver Init
어떤 디바이스 드라이버든지 최초는 Init 함수를 통한 등록이다.
int __init s3c_adc_init(void){
printk(banner);
return platform_driver_register(&s3c_adc_driver);}
// Platform_driver 등록
}
module_init(s3c_adc_init);
Probe 함수가 호출되기 전에!
• platform_driver_register() 함수가 call된 이후에 xxxx_probe()함수가
call이 되려면 선행되야하는 작업이 있다
먼저 platform_add_devices() 함수를 통해서 platform device가
등록이 되어 있어야 한다.
이때 platform_add_devices 와 platform_driver_register에서
.name이 동일해야함을 주의 해야 한다.
Probe 함수가 호출되기 전에!
/kernel/arch/arm/plat-s5p/devs.c
static struct resource s3c_adc_resource[] = {
[0] = {
.start = S3C_PA_ADC, // ADC Register주소
.end = S3C_PA_ADC + SZ_4K - 1, // 주소 마지막 값
.flags = IORESOURCE_MEM, // resource type
},
#include <mach/map.h>
#define S3C_PA_ADC
S3C_ADDR(0xE1700000) // 주소값 정의
Probe 함수가 호출되기 전에!
struct platform_device s3c_device_adc =
{
.name
= "s3c-adc",
//platform_driver와 이름이 같아야 한다.
.id
= -1,
// 여러 개의 플랫폼 디바이스가 있을경우 id로 구분
.num_resources = ARRAY_SIZE(s3c_adc_resource),
//리소스 테이블 수
.resource
= s3c_adc_resource, 리소스 테이블 시작 주소
};
Probe 함수가 호출되기 전에!
/kernel/arch/arm/mach-s5pv210/mach-mango210.c
static struct platform_device *mango210_devices[] __initdata = {
&s3c_device_adc, // platform_device name 매칭
}
static void __init mango210_machine_init(void){
s3c_adc_set_platdata(&s3c_adc_platform);
platform_add_devices(mango210_devices,
ARRAY_SIZE(mango210_devices));
// 플랫폼 등록
}
위와같이 최종적으로 Platform 디바이스 드라이버의 등록이 되고
Probe함수가 동작된다.
ADC Probe
조금더 상세하게 들어가서 Probe함수는 어떤 동작을 하는가 보자
static int __init s3c_adc_probe(struct platform_device *pdev)
{ struct resource *res;
struct device *dev;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
//리소스(디바이스에 할당된 자원 범위) 가져오기
dev = &pdev->dev;
//// pdev 라는 플랫폼 디바이스의 멤버들을 dev 의 멤버들로 cast 시킨
다. 따라서 pdev는 dev를 가르키게 된다
<platform_device.h>
extern struct resource *platform_get_resource
(struct platform_device *, //플랫폼 디바이스 네임
unsigned int,
// flags
unsigned int);
ADC Probe
static struct resource
*adc_mem; // Global define
static void __iomem
*base_addr; // Global define
////static void __iomem 변수명 선언을 하는 이유
Writel, readl 같은 core register접근 함수에 위와같은 데이터형으로 선언이
되어있기 때문이다.
size = (res->end - res->start) + 1; // 메모리 사이즈 설정
base_addr = ioremap(res->start, size); // 가상 메모리 번지 생성
adc_clock = clk_get(&pdev->dev, "adc"); // 클럭 생산자 생성
clk_enable(adc_clock);
… 생략 (생략 내용 : prescaler, delay 등등 register init)
ret = misc_register(&s3c_adc_miscdev); //miscdevice와 매칭을 한다.
Miscdevice
static struct miscdevice s3c_adc_miscdev = {
.minor
= ADC_MINOR,
.name
= "adc",
//device file name
.fops
= &s3c_adc_fops,
};
Miscdev 등록이 끝나면 FileSystem 상에서 /dev/adc라는 디바이스
파일이 생성이 되어있다. 이것은 /etc/udev의 규칙파일에 의해서 자
동적으로 된다.
User App에서 ADC Driver 사용
디바이스 드라이버의 File_operation 구조체를 보면
Ioctl
Read
Open
등이 존재한다. 그 내용을 들여다 보면 IOCTL은 ADC PORT설정
READ는 buffer크기만큼 ADC 데이터 읽기
OPEN은 디바이스 드라이버를 쓰기위해 선행되야하는 필수 Call이다.
이상입니다