디바이스 타입정보, 주번호, 부번호
File I/O system calls
- open()
- close()
- read()
- write()
- lseek()
- ioctl()
# mknod /dev/ttyS4 c 4 68
모듈 관련 명령어
- insmod : 모듈을 커널에 적재
- rmmod : 커널에서 모듈을 제거
- lsmod : 커널에 적재된 모듈 목록을 출력
- depmod : 모듈간 의존성 정보를 생성
- modprobe : 모듈을 커널에 적재하거나 제거
< Device Driver Application -> digit_app.c >
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <fcntl.h>
#include <linux/kdev_t.h>
#define _LED_PATH_ "dev/led_dd"
int main(int argc, char **argv){
int fd = 0;
// parameter 개수가 안맞을때
if(argc!=2){
printf("Usage: %s [LED binary]\n", argv[0]);
exit(1);
}
// fd 번호가 음수인 경우 error 발생
if((fd = open(_LED_PATH_, _O_RDWR | O_NONBLOCK)) < 0){
perror("open()");
exit(1);
}
// fd에 argv[1] write연산 수행
write(fd, argv[1], strlen(argv[1])));
// fd close
close(fd);
return 0;
}
< Device Driver -> led_dd.c >
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/io.h>
#include <linux/delay.h>
#include <asm/io.h>
#include <asm/uaccess.h>
// Special Device File을 만들기 위한 파리미터 정의
#define DRIVER_AUTHOR "KHU"
#define DEVICE_DESC "Led Driver"
#define DEVICE_NAME "led_dd"
#define MAJOR_NUMBER 222 -> 주번호
#define GPIO_BASE 0x3F200000 // physical address
#define GPIO_SIZE 0xc0
#define INPUT 0
#define OUTPUT 1
#define LOW 0
#define HIGH 1
void __iomem *gpioAddr;
volatile unsigned int gpioToGPFSEL[] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
};
volatile unsigned int gpioToShift[] = {
0, 3, 6, 9, 12, 15, 18, 21, 24, 27,
0, 3, 6, 9, 12, 15, 18, 21, 24, 27,
0, 3, 6, 9, 12, 15, 18, 21, 24, 27,
};
volatile unsigned int gpioToGPSET = 0x1C; // Set bit
volatile unsigned int gpioToGPCLR = 0x28; // CLEAR bit
const int Led[16] = {4, 17, 18, 27, 22, 23, 24, 25, 6, 12, 13, 16, 19, 20, 26, 21};
// Gpio의 주소값을 초기화하는 함수
// 기존에는 mmap이라는 systemcall을 이용했으나, kernel에서는 다음과 같이 한다.
static volatile void initGpioAddr(void){
if(gpioAddr == NULL) // gpioAddr가 Null인경우 BASE의 pointer를 가져온다
gpioAddr = ioremap(GPIO_BASE, GPIO_SIZE); // Physical주소를 이용하여 virtual-memory로 ioremap이 mapping해준다
}
// wiringPi는 application library 때문이므로 kernel에서는 사용할 수 없다
// 따라서 kernel에서 동작하도록 직접 구현해준다.
static void pinMode(int pin, int mode){
volatile unsigned int *gpio = (volatile unsigned int *)gpioAddr;
unsigned int fsel = gpioToGPFSEL[pin] / sizeof(unsigned int); // GPSEL을 고른다
unsigned int shift = gpioToShift[pin];
if(mode) // HIGH이면
gpio[fsel] |= (1<<shift); // 1비트를 index위치로 shift해준다
else // LOW 이면
gpio[fsel] |= (0<<shift); // 0비트를 index위치로 shift해준다
}
static void digitalWrite(int pin, int value){
volatile unsigned int* gpio = (volatile unsigned int *)gpioAddr;
unsigned int set = gpioToGPSET/sizeof(unsigned int);
unsigned int clr = gpioToGPCLR/sizeof(unsigned int);
if(value)
gpio[set] = (1<<pin); // pin 위치에 1 bit을 줌으로써 on
else // pin 위치에 clear 비트를 줌으로써 off
gpio[clr] = (1<<pin):
}
// 구조체를 이용하여 open/close/write에 해당하는 함수를 매핑해준다
static struct file_operations_fops={
.owner = THIS_MODULE,
.open = led_open,
.release = led_release,
.write = led_write
};
// led 전체를 다 끄게 초기화하는 함수
static int led_open(struct inode *inode, struct file *flip){
int i;
initGpioAddr(); // gpioaddr 초기화
for(i=0; i<16; i++){
pinMode(Led[i], OUTPUT);
digitalWrite(Led[i], LOW);
}
printk("[led_dd] led_open\n");
return 0;
}
// led 파일을 닫을때 실행되는 함수
static int led_release(struct inode *inode, struct file *flip){
iounmap(gpioAddr); // gpio 초기화를 해제
printk("[led_dd] led_release\n");
return 0;
}
// led special file에 쓰는 함수. 즉 LED 동작을 하도록 하는 함수
static int led_write(struct file *flip, const char *buf, size_t len, loft_t *fops){
int i;
char state;
for(i=0; i<len; i++){
copy_from_user(&state, &buf[i], 1); // 현재 led의 상태를 읽어와 buffer에 저장
if (state=='0')
digitalWrite(Led[3-i], LOW);
else
digitalWrite(Led[3-i], HIGH);
}
printk("[led_dd] led_write\n");
return len;
}
// 모듈이 시작할 때와 종료될 떄 실행되는 함수 정의
static int led_init(void){
printk("[led_dd] led_init() \n");
// 주번호에 해당하는 장치와 디바이스 장치를 /dev에 등록
// 구조체에 해당하는 연산에 작동하는 레지스터를 등록
register_chrdev(MAJOR_NUMBER, DEVICE_NAME, &fops);
return 0;
}
static void led_exit(void){
printk("[led_dd] led_exit()\n");
unregister_chrdev(MAJOR_NUMBER, DEVICE_NAME); // /dev에서 등록을 삭제
}
module_init(led_init); // 모듈 초기화시 led_init이 실행
module_exit(led_exit); // 모듈 종료시 led_exit이 실행
MODULE_LICENSE("Dual BSD/GPL"); // 라이센스 관련