Device Driver
커널프로그램 Kernel program 목차 Kernel program 유의사항 Kernel module 디바이스드라이버 Character/Block device driver Device driver 구조 Device driver 작성절차및 module 등록 LED 디바이스드라이버
Kernel Program (1) 커널프로그램과응용프로그램과비교 : 실행 (execution) 방식의차이 응용프로그램 : 순차적 (sequential) 프로그램 커널프로그램 : 커널에서제공하는 system call 혹은 interrupt에의한비동기적프로그램 ( 응용프로그램에서호출 ) system call interrupt 응용프로그램 커널프로그램
Kernel Program (2) Library 응용프로그램 : 대부분의 library 를링크하여사용가능 커널프로그램 커널에서 export 한 function 을사용 printf( ) printk( ), malloc( ) kmalloc( ) Address space 응용프로그램과커널프로그램은서로다른어드레스영역을사용하여각각의프로그램코드에영향을주지않음. 커널공간 1GByte 사용자공간 3GByte
Kernel Program (3) User space 와 kernel space 응용프로그램 : 사용자공간에서실행되며직접적으로하드웨어장치나메모리에접근불가능 커널프로그램 : 커널공간에서실행되며, 직접적으로하드웨어장치나메모리에접근가능 Name space pollution 응용프로그램 : 현재개발하는프로그램에서만함수와변수이름등을구별 커널프로그램 : 현재개발중인모듈외에도커널전반적인함수와변수의이름에충돌하지않도록프로그래밍
커널프로그램주의사항 (1) Library 응용프로그램에서사용하던 stdio.h 와같은헤더파일의중복성유의 커널에서포함하고있는헤더파일을사용 : linux-2.4.19-rmk4- pxa2-empx1/include/ Namespace pollution 외부파일과링크를하지않을모든 symbol을 static으로선언혹은외부파일과링크할 symbol table에등록 EXPORT_NO_SYMBOLS; EXPORT_SYMOL(name); 전역변수는잘정의된 prefix를사용 : sys_open() 부동소수형수치연산 커널프로그램에서부동소수형수치연산을수행되지않는관계로유의
커널프로그램주의사항 (2) Address space 커널이사용하는 stack 의크기는제한 Interrupt handler 도동일한스택을사용하므로큰배열을사용 recursion 이많이일어나지않도록주의 응용프로그램과 data 를주고받기위해 (call by reference) 특별한함수를사용 Fault Handling 커널은하드웨어접근에어떠한제한이없기때문에커널에서오류는시스템에치명적인결과를발생 함수호출등의작업에는에러코드를검사해주고처리
커널프로그램함수 커널인터페이스함수 커널프로그램은일반적인라이브러리를사용하지못하고커널이 export 해준함수만을사용할수있다. 커널인터페이스함수분류 : 커널에서제공하는함수중커널프로그램에서자주사용하는함수 Port I/O Interrupt Memory Kernel message Device driver register 커널모듈프로그램
Kernel Module Monolithic 커널 : 사용하고자하는모듈을로딩하여사용 임베디드시스템에이용함에따라시스템에필요한기능을적재하여활용 다양한인터페이스를타켓보드의목적와구성형태에따라입출력디바이스를설정 커널설정시모듈을설치가능 : 정적로딩 커널이실행되는중간에모듈을설치 : 동적로딩 사용자는시스템호출함수 (system function call) 를이용 디바이스드라이버모듈 /proc/modules, include/linux/init.h, include/linux/ module.h, kernel/ksyms.c, /drivers 커널내의별도의데이터구조 하나의파일에구현된함수가다른파일에있는데이터를참조하는경우, 주소결정에어려움 : 가상메모리접속방식
Kernel Module Interface (1): reading 디바이스 I/O 장치와데이터전송 Reading functions unsigned inb(unsigned port) : port 에서 1byte reading unsigned inw(unsigned port) : port 에서 2byte reading unsigned inl(unsigned port) : port 에서 4byte reading void insb(unsignedport, void *addr, unsigned long count): port 에서 count bytes 를읽어서메모리의 address 부터저장 void insw(unsignedport, void *addr, unsigned long count): port 에서 16bit * count 만큼읽어서메모리의 address 부터저장 void insl(unsignedport, void *addr, unsigned long count): port 에서 32bit * count 만큼읽어서메모리의 address 부터저장
Kernel Module Interface (2): writing Writing functions unsigned outb(charvalue, unsigned port) : port 에 1byte value 를 write unsigned outw(shortintvalue, unsigned port) : port 에 2byte value 를 write unsigned outl(longintvalue, unsigned port) : port 에 4byte value를 write void outsb(unsignedport, void *addr, unsigned long count): memory 의 address 에서부터 count bytes 를읽어서 port 에 write void outsw(unsignedport, void *addr, unsigned long count): memory 의 address 에서부터 count * 16bit 를읽어서 port 에 write void outsl(unsignedport, void *addr, unsigned long count): memory 의 address 에서부터 count * 32bit 를읽어서 port 에 write
Kernel Module Interface (3): Interrupt 인터럽트의설정및처리에관한함수 (or 매크로 ) cli() / sti() : 인터럽트금지및가능 save_flags(unsigned long flag), restore_flags(unsig ned long flag): status register의내용을저장하고복원, save_flags(), restore_flags() 이두매크로는같은함수안에존재 intrequst_irq(unsigned int irq, void (*handler)(int), unsigned long flags, const char *device): 커널로부터 IRQ 를요청하고, IRQ 에대한 interrupt handler 를커널에등록 void free_irq(unsigned int irq) : request_irq() 에서획득한 IRQ 를반납
Memory Function(1): memory allocation Kernel 에서동적메모리를할당할때사용하는함수 get_free_page(), free_page() 한페이지의메모리를할당하고해제하는데사용 kmolloc(), kfree() 물리적으로연속된메모리를할당하고해제하는데사용 한번에 128kb 까지할당가능 vmalloc(), vfree() 가상공간에서연속적인메모리를할당하고해제하는데사용 kmolloc 보다큰메모리할당가능
Memory Space 리눅스시스템에서는 4Gbyte 메모리의전체주소영역을사용 커널공간영역 (1Gbytes), 사용자공간영역 ( 응용프로그래밍, 3Gbytes) PAGE_OFFSET(0xc000 0000) 분할기준을정의하여커널공간영역은지역 offset 위에위치 커널과사용자영역 0x0000 0000 PAGE_OFFSET: 0xc000 0000 커널공간 1GByte 사용자공간 3GByte 0xFFFF FFFF
Virtual Memory Map: EMPOS-II Memory Map: /linux-2.4.9-rmk4-pxq2-empx1/arch/arm/machpxa/empos_x255.c /* virtual physical length domain r w c b */ {0xf0000000, 0x00080000, 0x00040000, DOMAIN_IO, 0, 1, 0, 0}, /* empos status flash block*/ {0xf1000000, 0x10000000, 0x00010000, DOMAIN_IO, 0, 1, 0, 0 }, /* PCMCIA status */ {0xf1100000, 0x10100000, 0x00010000, DOMAIN_IO, 0, 1, 0, 0 }, /* nport */ {0xf1300000, 0x10300000, 0x00010000, DOMAIN_IO, 1, 1, 0, 0}, /* Segment */ {0xf1400000, 0x10400000, 0x00010000, DOMAIN_IO, 1, 1, 0, 0}, /* Segment */ {0xf1500000, 0x10500000, 0x00010000, DOMAIN_IO, 0, 1, 0, 0}, /* P button */ {0xf1600000, 0x10600000, 0x00010000, DOMAIN_IO, 1, 1, 0, 0}, /* LED */ {0xf1700000, 0x10700000, 0x00010000, DOMAIN_IO, 1, 1, 0, 0}, /* Text LED */ {0xf4000000, 0x04000000, 0x00100000, DOMAIN_IO, 0, 1, 0, 0}, /* Primary Ethernet */ { 0xf5000000, 0x08000000, 0x00100000, DOMAIN_IO, 0, 1, 0, 0 }, /* Secondary Ethernet*/
Memory Function(2): sharing 사용자공간과커널공간사이에데이터를공유하기위한함수 get_user(void *x, const void *addr) *addr 의값을커널영역인 x 로 sizeof(addr) 만큼복사 put_user(void *x, const void *addr) *x 의값을 user 영역인 addr 로 sizeof(addr) 만큼복사 copy_to_user(void *to, void *from, unsigned long size) form 주소에서 to 의주소 ( 사용자공간 ) 까지 size 만큼주소의값을복사 copy_from_user(void *to, void *from, unsigned long size) form 주소에서 to 의주소 ( 커널공간 ) 까지 size 만큼주소의값을복사
Kernel Message Standard out 으로메시지를출력 : printk() printk(constchar *fmt,.) printf() 의커널버전 printk(log_level_ message) LOG_LEVEL:KERN_EMERG, KERN_ALERT, KERN_ERR,KERN_WARNING, KER_INFO, KERN_DEBUG Example printk( <1>Hello, World ); printk( kernel value = 0x%X\n,value); printk(kern_warning warning \n );
Device Driver Register 디바이스드라이버를커널에등록하는함수 int register_chrdev(unsigned int major, const char *name, struct file_operations *fops); 문자디바이스드라이버를커널에 (chrdev[major]) 등록 int unregister_chrdev(unsigned int major, const char *name); 커널 (chrdevs[major]) 에등록되어있는문자디바이스드라이버를제거 int register_blkdev(unsigned int major, const char *name, struct file_operations *fops); int unregister_blkdev(unsigned int major, const char *name);
디바이스 Device Driver 네트워크어댑터, LCD 디스플레이, PCMCIA, audio, 터미널, 키보드, 하드디스크, 플로피디스크, 프린터등의주변장치 디바이스의구동에필요한프로그램 : 디바이스드라이버가필수적으로요구 Device Driver 실제장치를추상화시켜사용자프로그램이정형화된인터페이스를통해디바이스를접근할수있도록해주는프로그램 디바이스관리에필요한정형화된인터페이스구현에요구되는함수와자료구조의집합체 표준적으로동일서비스제공을목적으로커널의일부분으로내장 응용프로그램이 H/W 를제어할수있도록인터페이스제공 하드웨어독립적인프로그램을작성을가능
Linux Device Driver (1) 사용자관점에서의디바이스드라이버 사용자는디바이스자체에대한자세한정보를알필요없음 Device는하나의파일로인식됨 File( 디바이스파일 ) 에대한접근을통하여 real device에접근가능 User Program Device file. VFS Device Driver Real Device
Linux Device Driver (2) 리눅스에서디바이스 Linux 에서 device 는특별한하나의파일처럼취급되고, access 가가능 : 사용자는 file operation 을적용할수있음 각디바이스는 major number 와 minor number 로구분 Device Driver 종류 Character device driver Block device driver Network device driver
Character Device Driver 문자디바이스의특징 자료의순차성을지닌장치 buffer cache 를사용하지않음 장치의 raw data 를사용자에게제공 Terminal, Serial/Parallel, Keyboard, Sound Card, Scanner, Printer 리눅스에서의문자디바이스 : /dev crw-r--r-- 1 root root 11, 1 Oct 7 2003 touch1 crw-r--r-- 1 root root 204, 5 Oct 7 2003 ttysa0 crw-r--r-- - 1 root root 204, 6 Oct 7 2003 ttysa1 crw-r--r-- 1 root root 180, 0 Oct 7 2003 usb
Block Device Driver Block device 특징 Random access 가능 블록단위의입출력이가능한장치 버퍼 cache 에의한내부장치표현 파일시스템에의해 mount 되어관리되는장치 디스크, RAM Disk, CD-ROM 등 리눅스에서 block device brw-r----- 1 root 1, 0 Oct 7 2003 ram0 brw-r--r-- 1 root 24 1, 1 Oct 7 2003 mmca1 brw-r--r-- 1 root 3 1, 0 Oct 7 2003 mtdblock0 brw-r--r-- 1 root 3 1, 1 Oct 7 2003 mtdblock1
Network Device Driver Network device 특징 대응하는장치파일이없음 네트워크통신을통해패킷을송수신할수있는장치 응용프로그램과의통신은표준파일의 system call 대신 socket(), bind() 등의시스템콜사용 Ethernet, PPP, ATM, ISDN device driver
Major/Minor Number (1) Major number( 주번호 ) 커널에서디바이스드라이버를구분 / 연결하는데사용 같은 Device 의종류를지칭, 1Byte (0~255 사이의값 ) Minor number( 부번호 ) 디바이스드라이버내에서장치를구분하기위해사용 각 Device 의부가적인정보를나타냄, 2Byte ( 부번호 ) 하나의디바이스드라이버가여러개의디바이스제어가능 $ ls -al /dev/ttys* crw------- 1 root root 4, 64 Jan 1 00 : 00 ttys0 crw------- 1 root root 4, 65 Oct 7 2003 ttys1 crw-r--r-- 1 root root 4, 74 Oct 7 2003 ttys10 crw-r--r-- 1 root root 4, 75 Oct 7 2003 ttys11
Major/Minor Number (2) 커널에서논리적인디바이스와 Major /Minor Number 를이용하여접근 Major Number 리눅스커널에서사용할각각의디바이스드라이버와연관, 0-255 숫자를할당 0-100 까지기본표준고정장치에할당 : 예 ) 21: SCSI, 3: IDE HDD include/linux/major.h, /proc/devices Minor Number 16 비트숫자로할당하여동일한디바이스드라이버를사용하는서로다른장치를구분 : 상위 8 비트는디바이스타입, 하위 8 비트는서로다른디바이스를설정 예 ) IDE Hard Disk 시스템에서하드디스크드라이버 Major number 3, 1 st Hard Disk Minor number 0, 2 nd Hard Disk Minor number 1
Linux Device Driver 구조 Application area Application System Call Interface VFS Kernel area Buffer Cache Network Subsystem Char Device Driver Block D/D Network D/D Device Interface Hardware Hardware
Typical module type (1) 모듈형태 커널에서수행될때필요한헤더파일 모듈에필요한헤더파일 매크로정의 : module_init(), module_exit() 모듈이설치될때초기화를수행하는코드 모듈이제거될때반환작업을수행하는코드
Typical module type (2) 기본적인디바이스드라이버의형태 #include <linux/kernel.h> #include <linux/module.h> #include <linux/init.h> int device_open( ) { } int device_release( ) { } ssize_t device_write( ) { } ssize_t device_read( ) { } static struct file_operations device_fops = { read : device_read, write : device_write, open : device_open, release : device_relese,}; int init_module(void) { } void cleanup_module(void) { } Header Files Function Prototypes File Operation 모듈설치시초기화수행모듈제거시반환작업수행
Character Device Driver: Basic init_module() 디바이스드라이버등록 메모리할당및초기화 cleanup_module() 디바이스드라이버제거 할당된 I/O memory 영역반환 open(), release() 디바이스카운트증가및감소 usage 카운트체크및증가감소 read(), write(), ioctl(), function 디바이스드라이버의목적에따른일을함 사용자공간과커널공간의테이타전송 메모리에값을쓰거나읽음
Device Driver 작성절차 1. 프로그램소스코딩 2. 디바이스드라이버모듈적재 : insmod (major number 값은커널에서할당 ) 3. mknod 을이용하여할당된 major number 로디바이스를등록 4. 커널버퍼디바이스드라이버의등록유무확인 : lsmod 5. 커널버퍼의오퍼레이션을수행 : cat & redirection 6. 디바이스드라이버모듈해지 : rmmod
Insert/Remove module /sbin/inmod [option] [-o module_name] object_file /sbin/rmmod [opion] module_name /sbin/lsmod or /proc/modules 확인 inmod init_module( ) register( ) Core Kernel Module Capability( ) printk( ) rmmod cleanup_ module( ) unregister( )
Kernel Action 1. 커널의주소공간에새로운모듈이추가 : create_module( ) 2. get_kernel_syms( ) 커널함수가모듈이참조하고있는커널내데이터위치를탐색 3. create_module( ) 함수가모듈을위한메모리공간을할당 4. init_module( ) 시스템호출을통해모듈이커널에적재되고, 모듈안에서 export되는심볼들이다른모듈에서사용할수있도록커널내부에서정의 5. insmod 명령을수행하는프로세스가새로이적재된모듈안의함수를호출
Make module 컴파일환경설정 커널모듈은독립적으로실행하는것이아님 : 커널에링크되어실행되기때문에 c flag( 링크호출제거 ) 잘못동작되면시스템전체에영향을주는바, 오류혹은경보메시지표시을위한 Wall flag 최적화옵션선정 O flag: 컴파일시에 in-line 함수가아닌일반함수로인식함에따라 export 되는심볼로인식가능 심볼정의 _KERNEL: 커널모들에서실행을의미 MODULE: 헤더파일에게모듈에서사용하는코드를인식 LINUX: 라눅스시스템의 OS 를인식 (kernel_src/include/linux/config.h)
Device Driver Insertion 드라이버를커널에등록하고, 파일연산을정의하는등의초기화작업수행이필요 모듈의형태에서는 init_module() 함수에서초기화수행 드라이버의등록함수 커널에지정되어있는 chrdevs 구조에새로운 char device 등록 major number : 주번호, 0 을주면사용하지않는값을반환 name : 디바이스이름으로 /proc/devices 에표시 fops: 디바이스와연관된파일연산구조체포인터 음수가반환되면, 오류발생 형태 int register_chrdev( unsigned int major, const * name, struct file_operations * fops);
Device Driver Remove 더이상사용되지않는드라이버의제거 rmmod 하는명령을이용하여제거 : 드라이버내의 cleanup_module 이호출, 루틴안에서다음의 system call 을호출 드라이버제거함수 int unregister_chrdev( unsigned int major, const * name);
File Operation 디바이스드라이버를일반적인파일과유사한인터페이스를이용하여관리 각디바이스는파일형태로존재하고, 커널은파일연산을이용하여 I/O 연산을수행하도록인터페이스구성 디바이스드라이버를구현한다는것은파일연산구조체에서요구되는기능들을프로그래밍한다는것을의미 textlcd character device 의파일연산구조체예제 static struct file_operations textlcd_fops = { write : textlcdport_write, ioctl : textlcdport_ioctl, open : textlcdport_open, release : textlcdport_release, };
File Operation Structure (1) struct file_operations { struct module *owner; loff_t (*llseek) (struct file *, loff_t, int); ssize_t (*read) (struct file *, char *, size_t, loff_t *); ssize_t (*write) (struct file *, const char *, size_t,loff_t *); int (*readdir) (struct file *, void *, filldir_t); unsigned int (*poll) (struct file *,struct poll_table_struct *); int (*ioctl) (struct inode *,struct file *,unsigned int, unsigned long); int (*mmap) (struct file *, struct vm_area_struct *); int (*open) (struct inode *, struct file *); int (*flush) (struct file *); int (*release) (struct inode *, struct file *);
File Operation Structure (2) }; int (*fsync) (struct file *, struct dentry *, int datasync); int (*fasync) (int, struct file *, int); int (*lock) (struct file *, int, struct file_lock *); ssize_t (*readv) (struct file *,const struct iovec *,unsigned long,loff_t *); ssize_t (*writev) (struct file*,const struct iovec *,unsigned long,loff_t *); ssize_t (*sendpage) (struct file *,struct page *, int, size_t, loff_t *, int); unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
file_operations 구조체의함수 Fuction Name llseek( ) read( ) write( ) readdir ( ) poll ( ) ioctl ( ) mmap ( ) open ( ) flush ( ) release ( ) fsync ( ) fasync ( ) check_media_change ( ) revalidate ( ) lock ( ) readv ( ) writev ( ) sendpage ( ) get_unmapped_area ( ) descriptions 파일구조체내에서위치를변경한다. ( 즉 file >f_pos를변경한다.) 장치의데이터를읽어서응용프로그램에게넘겨준다. 응용프로그램에서입력받은데이터값을장치에입력한다. Readdir은디렉토리의내용을찾기위해파일체계에의해직접적으로사용된다. Poll은응용프로그램이장치로부터특정이벤트를통지받게해준다. 응용프로그램이동작을제어하거나 ioctl 시스템호출을통한장치로부터데이터를구하게해준다. 사용자공간에대한장치주소공간의메모리멥핑을구현한다. 응용프로그램에서장치를열을때사용한다. 버퍼의데이터를삭제한다. 장치를닫을때사용한다. 모든나머지데이터를출력버퍼에기록한다. 응용프로그램이 fcntl을통해동작을변경할때호출된다. 최근의접근후매체가변경되었는지검사한다. 위의함수와연관되어있고, 디스크변경이감시되면필요에따라내부장치정보를갱신해야한다. 사용자가파일을잠그게해준다. 파일체계에서만유효하다. 파일로부터자료블록을읽어벡터에데이터를써넣는다. 벡터로부터자료블록을읽어파일에쓴다. 파일로부터자료를읽어다른파일로전송한다. 파일맵을검사해사용하지않은선형주소를찾는다.
Device Driver 작성방법 Application 에서디바이스드라이버작성 1. open 함수를이용하여특수장치파일을연다. dev = open( /dev/device,o_wronly); 2. 디바이스드라이버에서제공하는함수를이용하여디바이스를제어한다. write(dev,&buff,1); 3. close 함수를이용하여특수장치파일을닫는다. close(dev);
Hello Device Driver (1) Program Architecture init_module(), cleanup_module(): printk 문을이용하여콘솔에어떤함수가실행되는지알려준다. hello.c Source #include <linux/module> #include <linux/kernel.h> #include <linux/init.h> int init_module(void) { printk( Hello, Kernel!\n ); } void cleanup_module(void) { printk( Good-bye, Kernel!\n ); }
Hello Device Driver (2) Makefile CC = arm-linux-gcc KERNELDIR = /working/kernel/linux-2.4.19-rmk4-pax2-empx1 INCLUDEDIR = -I$(KERNELDIR)/include I./ CFLAGS = -D KERNEL -DMODULE Wall O2 I$(INCLUDEDIR) MODULE_OBJS = hello.o MODULE_SRCS = hello.c $(MODULE_OBJS) : $(CC) $(CFLAGS) c $(MODULE_SRCS) clean: rm f *.o
Hello Device Driver (3) 드라이버컴파일 $make 드라이버수행 $insmod hello.o Using hello.o Hello, Kernel! $lsmod Module size Used by hello 216 0 (unsed) $rmmod hello Good-bye, Kernel!