기본적인 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 |