본문 바로가기
Kernel/Practice

misc device 例

by 暻煥 2024. 9. 21.

기본적인 read(), write(), ioctl(), poll() 종류의 system call 작업이 가능한 misc device 예제를 작성한다.

 

Driver Source

/* file: xxx_device.c */
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/miscdevice.h>
#include <linux/poll.h>

#define DEVICE_NAME "xxx"

#define XXX_IOCTL_CMD_READ _IOR('j', 1, int)
#define XXX_IOCTL_CMD_WRITE _IOW('j', 2, int)
#define XXX_IOCTL_CMD_WAKE_UP _IO('j', 3)

int xxx_var = 1123;

atomic_t xxx_poll_ready = ATOMIC_INIT(0);
struct wait_queue_head xxx_wq = __WAIT_QUEUE_HEAD_INITIALIZER(xxx_wq); 

static ssize_t xxx_read(struct file *f, __user char *ubuf, size_t count, loff_t *ppos)
{
	size_t len_read;
	char dev_buf[32];

	len_read = snprintf(dev_buf, sizeof(dev_buf) - 1, "%d", xxx_var);

	/* count, maxinum number of byte, is PAGE_SIZE generally */
	return simple_read_from_buffer(ubuf, count, ppos, dev_buf, len_read);
}

static ssize_t xxx_write(struct file *f, __user const char *ubuf, size_t count, loff_t *ppos)
{
	char dev_buf[32];

	count = min(count, sizeof(dev_buf) - 1);
	if (copy_from_user(dev_buf, ubuf, count))
		return -EFAULT;

	dev_buf[count] = '\0';

	if (kstrtoint(dev_buf, 10, &xxx_var))
		return -EFAULT;

	return count;
}

static long xxx_ioctl(struct file *f, unsigned int cmd, unsigned long arg)
{
	int err = 0;
	
	switch (cmd) {
		case XXX_IOCTL_CMD_READ:
			if (copy_to_user((__user int *)arg, &xxx_var, sizeof(xxx_var)))
				err = -EFAULT;
			break;
		case XXX_IOCTL_CMD_WRITE:
			if (copy_from_user(&xxx_var, (__user int *)arg, sizeof(xxx_var)))
				err = -EFAULT;
			break;
		case XXX_IOCTL_CMD_WAKE_UP:
			atomic_inc(&xxx_poll_ready);
			wake_up_interruptible(&xxx_wq);
			break;
		default:
			err = -EINVAL;
	}
	return err;
}

static __poll_t xxx_poll(struct file *f, poll_table *wait)
{
	__poll_t ret = 0;

	poll_wait(f, &xxx_wq, wait);

	if (atomic_read(&xxx_poll_ready)) {
		ret = EPOLLIN | EPOLLRDNORM;
		atomic_dec(&xxx_poll_ready);
	}

	/* if not ready, need to return EPOLLERR */
	return ret;
}

static const struct file_operations xxx_fops = {
	.owner = THIS_MODULE,
	.open = simple_open,
	.llseek = default_llseek,
	.read = xxx_read,
	.write = xxx_write,
	.unlocked_ioctl = xxx_ioctl,
	.poll = xxx_poll,
};

static struct miscdevice xxx_dev = {
	.minor = MISC_DYNAMIC_MINOR,
	.name = DEVICE_NAME,
	.fops = &xxx_fops,
};

__init
static int xxx_init(void)
{
	int ret = 0;

	pr_info("%s: initialized\n", __func__);

	ret = misc_register(&xxx_dev);
	if (ret)
		pr_err("%s: fail to register device\n", __func__);

	return ret;
}

__exit
static void xxx_exit(void)
{
	pr_info("%s: destroyed\n", __func__);

	misc_deregister(&xxx_dev);
}

module_init(xxx_init);
module_exit(xxx_exit);

MODULE_AUTHOR("kh1123.jung");
MODULE_DESCRIPTION("misc device example");
MODULE_LICENSE("GPL");

 

Driver Makefile

MODULE_SOURCE=xxx_device
export KERNEL_DIR=/source/path/to/kernel/
export ARCH=arm64
export CROSS_COMPILE=aarch64-linux-gnu-

export CC = $(CROSS_COMPILE)gcc
export LD = $(CROSS_COMPILE)ld
export AR = $(CROSS_COMPILE)ar
export NM = $(CROSS_COMPILE)nm
export OBJCOPY = $(CROSS_COMPILE)objcopy
export OBJDUMP = $(CROSS_COMPILE)objdump
export READELF = $(CROSS_COMPILE)readelf
export OBJSIZE = $(CROSS_COMPILE)size
export STRIP = $(CROSS_COMPILE)strip

obj-m += ${MODULE_SOURCE}.o

all:
	make -C ${KERNEL_DIR} KCFLAGS=${KCFLAGS} ${EXTRA_CFLAGS} M=$(PWD) modules
	@${KERNEL_DIR}/scripts/sign-file sha512 ${KERNEL_DIR}/certs/signing_key.pem ${KERNEL_DIR}/certs/signing_key.x509 ${MODULE_SOURCE}.ko

clean:
	make -C ${KERNEL_DIR} M=$(PWD) clean

 

User Source

/* file: main.c */
#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <poll.h>

#define XXX_IOCTL_CMD_READ _IOR('j', 1, int)
#define XXX_IOCTL_CMD_WRITE _IOW('j', 2, int)
#define XXX_IOCTL_CMD_WAKE_UP _IO('j', 3)

int main()
{
	int fd;
	int err = 0;
	char buf[32];
	int var_w;
	int var_r;
	pid_t pid;

	/* test device open */
	fd = open("/dev/xxx", O_RDWR);
	if (fd < 0) {
		printf("fail to open device\n");
		err =  -ENOENT;
		goto out;
	}
	printf("device opened (%d)\n",fd);
	
	/* test device write */
	err = write(fd, "720\0", 4);
	if (err < 0) {
		printf("fail to write device\n");
		goto close_out;
	}

	/* test device read */
	err = read(fd, buf, 32);
	if (err < 0) {
		printf("fail to read device\n");
		goto close_out;
	}
	printf("read data : %s\n", buf);

	/* test device ioctl write */
	var_w = 810;
	err = ioctl(fd, XXX_IOCTL_CMD_WRITE, &var_w);
	if (err < 0) {
		printf("fail to write ioctl(%d)\n", err);
		goto close_out;
	}

	/* test device ioctl read */
	err = ioctl(fd, XXX_IOCTL_CMD_READ, &var_r);
	if (err < 0) {
		printf("fail to read ioctl(%d)\n", err);
		goto close_out;
	}
	printf("read ioctl data : %d\n", var_r);

	/* test poll */
	pid = fork();
	if (pid > 0) {
		/* parent */
		struct pollfd pfd;

		pfd.fd = fd;
		pfd.events = POLLIN;

		err = poll(&pfd, 1, -1);
		if ((err > 0) && (pfd.events & POLLIN)) {
			buf[0] = '\0';

			lseek(fd, 0, SEEK_SET);
			err = read(fd, buf, 32);
			if (err > 0) {
				buf[err] = '\0';
				printf("get poll event: %s\n", buf);
			} else
				printf("get poll event: but nothing to read(%d)\n", err);
		} else {
			printf("no poll event(%d)\n", err);
			goto close_out;
		}
	} else if (pid == 0) {
		/* child */
		sleep(3);
		err = ioctl(fd, XXX_IOCTL_CMD_WAKE_UP);
		if (err < 0) {
			printf("fail to wake up(%d)\n", err);
			goto close_out;
		} else
			printf("poll event\n");
	} else {
		printf("fork fail(%d)\n", pid);
		goto close_out;
	}

close_out:
	close(fd);
out:
	return err;
}

 

User Makefile

SOURCE = main.c
BIN = xxx_user.bin

export CROSS_COMPILE=aarch64-linux-gnu-
export CFLAGS = -W -Wall -O2 -g

export CC = $(CROSS_COMPILE)gcc
export LD = $(CROSS_COMPILE)ld
export AR = $(CROSS_COMPILE)ar
export NM = $(CROSS_COMPILE)nm
export OBJCOPY = $(CROSS_COMPILE)objcopy
export OBJDUMP = $(CROSS_COMPILE)objdump
export READELF = $(CROSS_COMPILE)readelf
export OBJSIZE = $(CROSS_COMPILE)size
export STRIP = $(CROSS_COMPILE)strip

all: 
	${CC} ${CFLAGS} ${LDFLAGS} ${SOURCE} ${INCLUDEPATH} ${LIBS_DIR} ${LIBS} -o ${BIN}

clean:
	@rm -rf *.bin

 


※ 참고 및 출처

https://www.kernel.org/doc/html/v4.14/driver-api/misc_devices.html

 

Miscellaneous Devices — The Linux Kernel documentation

Miscellaneous Devices int misc_register(struct miscdevice * misc) register a miscellaneous device Parameters struct miscdevice * misc device structure Description Register a miscellaneous device with the kernel. If the minor number is set to MISC_DYNAMIC_

www.kernel.org

 

.

'Kernel > Practice' 카테고리의 다른 글

kwork 사용 例  (0) 2024.02.02
hrtimer 사용 例  (0) 2024.02.02
process 생성/종료 ftrace 얻기  (0) 2024.02.02
debugfs 생성 ( /sys/kernel/debug/)  (0) 2024.02.02
함수 ftrace 얻기  (0) 2024.02.02