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.platform_driver 등록 2.Probe 함수 실행 (ADC 필수 레지스터 초기화 ) 3.Probe 과정에서 장치 리소스를 확인 / 설정 (Clock, platform_data) 4.Miscdevice 등록 (Miscdevice name 과 같은 platform_driver 매칭 ) 5.file_operations 등록 (.fops name) 6.file_operations 에서 원하는 동작 함수들이 존재 7.User App 에서 필요한 함수들을 System Call 로 호출
알아두어야 할 구조체 Struct device_driver Struct platform_device & driver Struct miscdevice Struct file_operations
struct device_driver 구조체 #include 기타 장치 파일을 등록하는데 필요한 함수를 포함 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 struct platform_device { const char* name; //platform_driver 에 정의된 이름과 같아야 한다. intid; struct devicedev; u32num_resources; struct resource* resource; struct platform_device_id*id_entry; /* arch specific additions */ struct pdev_archdataarchdata; };
Miscdevice 구조체 #include 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 #define S3C_PA_ADCS3C_ADDR(0xE ) // 주소값 정의
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 를 가르키게 된다 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 이다.
이상입니다