본문 바로가기
device driver

charter device driver - 2. char device drier 만들기

by jsh91 2023. 5. 21.

charter device file을 만든다

 

#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/fs.h>

/* device number를 할당 */
#define MY_MAJOR_NUM	202

static struct cdev char_dev;

static int open(struct inode *inode, struct file *file)
{
	pr_info("open() is called.\n");
	return 0;
}

static int close(struct inode *inode, struct file *file)
{
	pr_info("close() is called.\n");
	return 0;
}

static long ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
	pr_info("ioctl() is called. cmd = [%d], arg = [%ld]\n", cmd, arg);
	return 0;
}

/* charter device에 open, close, ioctl 명령시 호출 할 함수 등록*/
static const struct file_operations char_dev_fops = {
	.owner = THIS_MODULE,
	.open = open,
	.release = close,
	.unlocked_ioctl = ioctl,
};


static int __init hello_init(void)
{
	int ret;
	/* device number를 만든다 */
	dev_t dev = MKDEV(MY_MAJOR_NUM, 0);
	pr_info("hello_init\n");

	/* register_chrdev_region(device number, minor 갯수, device name(/proc/devices에 생성)) */
	ret = register_chrdev_region(dev, 1, "char_device");
	if (ret < 0){
		printk("Unable allocate mayor number %d\n", MY_MAJOR_NUM);
		return ret;
	}

	/* charter device 를 init */
	cdev_init(&char_dev, &char_dev_fops);

	/* charter device를 추가 */
	ret= cdev_add(&char_dev, dev, 1);
	if (ret < 0){
		/*실패시 char device 등록해제*/
		unregister_chrdev_region(dev, 1);
		pr_info("Unable to add cdev\n");
		return ret;
	}

	return 0;
}

static void __exit hello_exit(void)
{
	pr_info("hello_exit\n");
	cdev_del(&char_dev);
	unregister_chrdev_region(MKDEV(MY_MAJOR_NUM, 0), 1);
}

module_init(hello_init);
module_exit(hello_exit);

MODULE_LICENSE("GPL");

 

 

charter device file에 명령(ioctl) 할 app을 만든다

ioctl_test.c

#include <stdio.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <unistd.h>

int main(void)
{
	/* 생성한 mydev를 open */
		int my_dev = open("/dev/mydev", 0);

	if (my_dev < 0) {
		perror("Fail open device file: /dev/mydev.");
	} else {
		/* 생성한 mydev에 ioctl 명령 */
		ioctl(my_dev, 10, 20);

		/* 생성한 mydev를 close */
		close(my_dev);
	}
	
	return 0;
}

 

ioctl app을 build 하기 위한 Makefile

CC = arm-linux-gnueabihf-gcc 

ioctl_test : ioctl_test.c
	$(CC) -o $@ $^
clean :
	rm ioctl_test
deploy : ioctl_test
	scp $^ root@10.0.0.10:/home/pi

위 Makefile을 'make' 하면 ioctl_test 파일이 생성

 

'mknod /dev/mydev c 202 0' command로 charter device file을 생성한다.

'mknod {경로/파일이름} {device 종류} {major number} {nimor number}

 

이제 ioctl_test app을 실행하면 driver로 command가 전달되다.

 

root@raspberrypi:/home/board# ./ioctl_test
root@raspberrypi:/home/board# dmesg
[ 1518.516277] hello_init
[ 1933.485801] open() is called.
[ 1933.485826] ioctl() is called. cmd = [10], arg = [20]
[ 1933.485843] close() is called.

-------------

class charter module을 만들어보자 

#include <linux/module.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/cdev.h>

#define  DEVICE_NAME "mydev"
#define  CLASS_NAME  "hello_class"

static struct class*  helloClass;
static struct cdev my_dev;
dev_t dev;

static int open(struct inode *inode, struct file *file)
{
	pr_info("open() is called.\n");
	return 0;
}

static int close(struct inode *inode, struct file *file)
{
	pr_info("close() is called.\n");
	return 0;
}

static long ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
	pr_info("ioctl() is called. cmd = %d, arg = %ld\n", cmd, arg);
	return 0;
}

/* declare a file_operations structure */
static const struct file_operations my_dev_fops = {
	.owner			= THIS_MODULE,
	.open 		 	= open,
	.release 	 	= close,
	.unlocked_ioctl 	= ioctl,
};

static int __init hello_init(void)
{
	int ret;
	dev_t alloc_dev;
	int Major;

	struct device* helloDevice;

	pr_info("hello_init\n");

	/* 
	device number 주번호 동적 할당 
	alloc_chrdev_region( 동적 할당할 dev major number, first minor number
	, minor 갯수, charter device name )
	*/
	
	ret = alloc_chrdev_region(&alloc_dev, 0, 1, DEVICE_NAME);
	if (ret < 0){
		pr_info("fail to allocate Mayor number \n");
		return ret;
	}

	/* device number 생성 */
	Major = MAJOR(alloc_dev);
	dev = MKDEV(Major,0);

	pr_info("major number %d\n", Major);

	/*
	cdev init, add cdev to kernel 공간
	cdev_init(struct cdev, cdev 함수 호출시 동작할 file operation)
	*/
	cdev_init(&my_dev, &my_dev_fops);
	ret = cdev_add(&my_dev, dev, 1);
	if (ret < 0){
		unregister_chrdev_region(dev, 1);
		pr_info("fail to add cdev\n");
		return ret;
	}

	/* 
	device class 등록 
	class_create( , class file name)
	*/
	helloClass = class_create(THIS_MODULE, CLASS_NAME);
	if (IS_ERR(helloClass)){
		unregister_chrdev_region(dev, 1);
		cdev_del(&my_dev);
	    pr_info("Fail to register device class\n");
	    return PTR_ERR(helloClass);
	}

	pr_info("class device is create sucess\n");

	/*
	'/dev/'에 device node 생성
	device_create(class_device, , device number , , /dev/에 생성할 name)
	*/
	helloDevice = device_create(helloClass, NULL, dev, NULL, DEVICE_NAME);
	if (IS_ERR(helloDevice)){
	    class_destroy(helloClass);
	    cdev_del(&my_dev);
	    unregister_chrdev_region(dev, 1);
	    pr_info("Fail to create the device\n");
	    return PTR_ERR(helloDevice);
	}
	pr_info("device is create sucess\n");

	return 0;
}

static void __exit hello_exit(void)
{

	device_destroy(helloClass, dev);     
	class_destroy(helloClass);           
	cdev_del(&my_dev);
	unregister_chrdev_region(dev, 1);    
	pr_info("hello_exit\n");
}

module_init(hello_init);
module_exit(hello_exit);

MODULE_LICENSE("GPL");

 

 

'/sys/class/' 에 file을 생성하고 user 단에서 접근이 가능하다

class_create() - '/sys/class/' file 생성

class_destory() - '/sys/class/' file 삭제

 

'/dev/'에 device node를 생성한다

device_create() 함수를 사용하면 mknod를 상요하여 /dev/ 경로에 file을 수동으로 생성 하지 않아도 된다.

device_create() - '/dev/' file 생성

device_destroy() - '/dev/' file 삭제

 

register_chrdev_region() 함수를 사용하면 주번호를 입력해야된다 하지만 alloc_chrdev_region() 함수를 사용하면 

주번호를 입력하지 않고 동적으로 할당 가능하다

 

root@raspberrypi:/home/board# insmod class_driver.ko
root@raspberrypi:/home/board# dmesg
[  103.479103] class_driver: loading out-of-tree module taints kernel.
[  103.479814] hello_init
[  103.479829] major number 237
[  103.479892] class device is create sucess
[  103.484609] device is create sucess

 

 

insmod 후 '/sys/class/' 경로에  'hello_class'이 생성

root@raspberrypi:/sys/class/hello_class# pwd
/sys/class/hello_class

 

'hello_class'에 mydev 생성

root@raspberrypi:/sys/class/hello_class# ls
mydev

 

mydev 디렉토리에 아래 파일들 생성

root@raspberrypi:/sys/class/hello_class/mydev# ls
dev  power  subsystem  uevent

 

'/dev'/에 mydev 파일 생성

root@raspberrypi:/dev# ls -l /dev/mydev
crw------- 1 root root 237, 0 May 21 07:14 /dev/mydev

댓글