본문 바로가기

Embedded/Device Driver

[Device Driver] 대소문자 변환 Device Driver 만들기

작업 환경

-메인 OS : Windows 8.1K(Intel Core i5-4590)

-작업 OS : Ubuntu 14.04.5 LTS 64bit(VirtualBox)

-장 비 명 : Hybus-Smart4412



1. 초기설정

- mkdir device --> device 디렉토리에 examdrv.c 와 Makefile을 만든다.


examdrv.c

/*뼈대 작성*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <asm/uaccess.h>

#define DEVICE_NAME "examdrv"
#define MYDRV_MAX_LENGTH 4096
#define MIN(a, b) (((a) < (b)) ? (a) : (b))

struct class *myclass;
struct cdev *mycdev;
struct device *mydevice;
dev_t mydev; 
MODULE_LICENSE("GPL");
static char * mydrv_data;
static int mydrv_read_offset, mydrv_write_offset;
int result;

static int mydrv_open(struct inode *inode, struct file *file)
{
printk("%s\n", __FUNCTION__);
return 0;
}

static int mydrv_release(struct inode * inode, struct file *file)
{
printk("%s\n", __FUNCTION__);
return 0;
}

static ssize_t mydrv_read(struct file *file, char *buf, size_t count, loff_t *ppos)
{
if( (buf == NULL) || (count < 0 ) )// 넘어온 값이 없는경우
return -EINVAL;
if( (mydrv_write_offset - mydrv_read_offset) <= 0 )// 길이가 있는지 없는지 확인
return 0;
count = MIN( (mydrv_write_offset - mydrv_read_offset), count );
if( copy_to_user(buf, mydrv_data + mydrv_read_offset, count) ) // user에서 넘어온 변수의 주소값에 값을 담는다.
return -EFAULT;
mydrv_read_offset += count;
return count; //리턴된 count은 커널로 전달된다.
}

static ssize_t mydrv_write(struct file *file,char *buf, size_t count, loff_t *ppos)
{
int i;
if( (buf==NULL) || (count<0) )
return -EINVAL;
if( count+mydrv_write_offset >= MYDRV_MAX_LENGTH) { // 길이가 있는지 없는지 확인
return 0;
}

  //추가부분
for(i=0 ; i<count ; ++i)
{
if(buf[i]>= 65 && buf[i] <= 90)
{
buf[i] += 32;
}
else if(buf[i]>=97 && buf[i]<= 122)
{
buf[i] += 32;
}
}

if( copy_from_user(mydrv_data + mydrv_write_offset, buf, count) )
return -EFAULT;

mydrv_write_offset += count;
return count;
}

struct file_operations mydrv_fops = {
.owner = THIS_MODULE,
.read = mydrv_read,
.write = mydrv_write,
.open = mydrv_open,
.release = mydrv_release,
};

int mydrv_init(void)
{
// 케릭터 디바이스 영역을 말록함
// 두번째 인자, 세번째 인자 : 고정으로 0, 1 사용
// 네번쨰 인자 : DEVICE_NAME(mydrv)  해당하는 이름으로 공간을 잡겠다.
if ( alloc_chrdev_region(&mydev, 0, 1, DEVICE_NAME) < 0 ) {
return -EBUSY;
}

// class_create() : 구조체중에 이름이 class가 있음. 모듈에 대한 모든 정보를 가지고 있는 구조체
// THIS_MODULE : 나 자신 모듈
myclass = class_create(THIS_MODULE, "mycharclass");
if (IS_ERR(myclass)){ // 에러 판단
unregister_chrdev_region(mydev, 1); // alloc_chrdev_region()영역 잡은것을 해제
return PTR_ERR(myclass); // 에러가 발생했다고 알려줌
}
mydevice = device_create(myclass, NULL, mydev, NULL, "mydevicefile");// 디바이스 구조체 생성
if (IS_ERR(mydevice)){
class_destroy(myclass);
unregister_chrdev_region(mydev, 1);
return PTR_ERR(mydevice);
}

mycdev = cdev_alloc(); // 케릭터 디바이스 크기만큼 공간을 할당
mycdev->ops = &mydrv_fops; // ops->파일연산자
mycdev->owner = THIS_MODULE; // 자기 자신이 권한을 할당
if ( cdev_add(mycdev, mydev, 1) < 0 ) { // 추가적으로 공간을 더함
device_destroy(myclass, mydev);
class_destroy(myclass);
unregister_chrdev_region(mydev, 1);
return -EBUSY;
}
mydrv_data = (char *) kmalloc(MYDRV_MAX_LENGTH * sizeof(char), GFP_KERNEL);
mydrv_read_offset = mydrv_write_offset = 0;

printk("skeleton module init!!\n");
result = register_chrdev(0, "skeleton!!", &mydrv_fops);
printk("major number=%d\n", result);

return 0;
}

void mydrv_cleanup(void)
{
// 모든 공간 해제
kfree(mydrv_data);
cdev_del(mycdev);
device_destroy(myclass, mydev);
class_destroy(myclass);
unregister_chrdev_region(mydev, 1);
}

module_init(mydrv_init); // 모듈이 등록이 되었을 때, 한번 호출. 
module_exit(mydrv_cleanup); // 모듈이 삭제 됐을 때, 모든 공간 해제


Makefile

CC := /usr/local/CodeSourcery/Sourcery_G++_Lite/bin/arm-none-eabi-gcc

obj-m := examdrv.o

KDIR := ~/Smart4412/Development/Source/Kernel/kernel_4412  

all:

        make -C $(KDIR) SUBDIRS=$(PWD) modules

clean:

        rm -rf *.o *.mod *.ko *.cmd *.mod.c



2. 빌드

- make (device 디렉토리에서)

- examdrv.ko , examdrv.mod.c , examdrv.mod.o , examdrv.o 파일들이 만들어 진다.


3. Device Driver 등록

- Tera Term에서 포트 설정 후  설정 -> 시리얼 포트 -> 속도를 115200으로 설정

- Hybus-Smart4412 장비 부팅

- 메뉴 -> 전송 -> ZMODEM -> 보내기에서 examdrv.ko 선택

- insmod examdrv.ko (커널(모듈) 등록)

 skeleton module init!!

 major number=247 (나오면 등록 성공)

mknod /dev/examdrv c 247 0  (디바이스 등록)

(/dev/examdrv(사용하려는 디바이스이름) , c(캐릭터 디바이스) , 248(주번호) , 0(부번호)) 

커널에 등록되어진 것을 디바이스드라이버에 등록

(*중요 : 만약 examdrv.c 수정 시 Tera term 에서 rmmod examdrv.ko , rm examdrv.ko를 해준 뒤 cd /dev로 이동후 rm examdrv를 해준다)


4. examdrv Driver 사용 및 테스트

examdrv_test.c

#include <stdio.h>

#include <fcntl.h>


#define MAX_BUFFER 26

char buf_in[MAX_BUFFER];

char buf_out[MAX_BUFFER];


int main(void)

{

int fd, i, c = 65;

if( (fd = open("/dev/examdrv", O_RDWR)) < 0 ){

perror("open error");

return -1;

}

for(i = 0; i<MAX_BUFFER; i++){

buf_out[i] = c++;

buf_in[i] = 65;

}


write(fd, buf_out, MAX_BUFFER);

read(fd, buf_in, MAX_BUFFER);


for(i = 0; i<MAX_BUFFER; i++){

fprintf(stderr, "%c", buf_in[i]);

}

fprintf(stderr, "\n");


close(fd);

return 0;

}


/opt/gnueabi/opt/ext-toolchain/bin/arm-linux-gnueabihf-gcc examdrv_test.c -o examdrv_test (해당 디렉토리에서 컴파일)

- Tera term으로 이동 (http://cccding.tistory.com/58 에서 1) 참조)

- 업로드 후 ./examdrv_test 실행