Makefile
obj-m += mock.o all: make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules clean: make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
mock.c
#include <linux/module.h> #include <linux/proc_fs.h> #include <linux/cdev.h> #include <linux/device.h> #include <linux/init.h> #include <asm/uaccess.h> #include <linux/wait.h> #include <linux/sched.h> #include "mock.h" #define MODNAME "mock" #define MODNUM MOCKNUM #define INFOPRINT(fmt, ...) printk(KERN_INFO "[%s] %s:%d :: " fmt, MODNAME, __func__, __LINE__, ##__VA_ARGS__) #define DBGPRINT(fmt, ...) printk(KERN_DEBUG "[%s] %s:%d :: " fmt, MODNAME, __func__, __LINE__, ##__VA_ARGS__) #define ERRPRINT(fmt, ...) printk(KERN_ERR "[%s] %s:%d :: <ERROR> " fmt, MODNAME, __func__, __LINE__, ##__VA_ARGS__) static int mock_major; static struct class *mock_class; #define MAX_PACKET_NUM 10 typedef struct mock_packet_store_s { mock_packet_t packet[MAX_PACKET_NUM]; unsigned int stored_num; unsigned int cur_idx; } mock_packet_store_t; typedef struct mock_dev_s { struct cdev c_dev; dev_t dev; mock_packet_store_t store; wait_queue_head_t wait_queue; } mock_dev_t; static mock_dev_t mock_dev[MODNUM]; typedef enum mock_act_type_e { MOCK_ACT_TYPE_EXPECT = 0, MOCK_ACT_TYPE_RETURN, } mock_act_type_t; static int mock_open(struct inode *inode, struct file *filp) { mock_dev_t *dev; INFOPRINT("%s is called\n", __func__); dev = container_of(inode->i_cdev, mock_dev_t, c_dev); filp->private_data = dev; INFOPRINT("minor : %d\n", MINOR(dev->dev)); return 0; } static int mock_release(struct inode *inode, struct file *filp) { mock_dev_t *dev = filp->private_data; INFOPRINT("%s is called\n", __func__); INFOPRINT("minor : %d\n", MINOR(dev->dev)); return 0; } static ssize_t mock_wait_act(mock_dev_t *mock_dev, mock_act_t act, unsigned char *buf, mock_act_type_t act_type) { mock_packet_store_t *store = &mock_dev->store; unsigned char cmp_buf[MOCK_MAX_DATA_SIZE]; int ret; ret = wait_event_interruptible(mock_dev->wait_queue, store->packet[store->cur_idx].act == act); if (ret < 0) { ERRPRINT("wait_event_interruptible failed\n"); return -ERESTARTSYS; } if (0 < store->packet[store->cur_idx].size) { if (act_type == MOCK_ACT_TYPE_EXPECT) { copy_from_user(cmp_buf, buf, store->packet[store->cur_idx].size); ret = memcmp(store->packet[store->cur_idx].data, buf, store->packet[store->cur_idx].size); if (ret != 0) { ERRPRINT("unexpected value\n"); return -EINVAL; } } else { copy_to_user(buf, store->packet[store->cur_idx].data, store->packet[store->cur_idx].size); } } store->cur_idx++; wake_up_interruptible(&mock_dev->wait_queue); return 0; } static ssize_t mock_read(struct file *filp, char *buf, size_t count, loff_t *offset) { mock_dev_t *dev = filp->private_data; ssize_t size_ret; INFOPRINT("%s is called\n", __func__); INFOPRINT("minor : %d\n", MINOR(dev->dev)); size_ret = mock_wait_act(dev, MOCK_ACT_RETURN_READ, (unsigned char *)buf, MOCK_ACT_TYPE_RETURN); return size_ret; } static ssize_t mock_write(struct file *filp, const char *buf, size_t count, loff_t *offset) { mock_dev_t *dev = filp->private_data; INFOPRINT("%s is called\n", __func__); INFOPRINT("minor : %d\n", MINOR(dev->dev)); return 0; } static void mock_ioctl_clear(mock_dev_t *mock_dev) { mock_dev->store.stored_num = 0; mock_dev->store.cur_idx = 0; return; } static void mock_ioctl_set_act(mock_dev_t *mock_dev, unsigned long arg) { mock_packet_t packet; mock_packet_store_t *store = &mock_dev->store; copy_from_user(&packet, (void *)arg, sizeof(mock_packet_t)); { int i; DBGPRINT("act : %d\n", packet.act); DBGPRINT("ret : %d\n", packet.ret_val); for (i=0; i<packet.size; i++) { DBGPRINT("data[%d] : %u\n", i, packet.data[i]); } } store->packet[store->stored_num] = packet; store->stored_num++; return; } static long mock_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { mock_dev_t *dev = filp->private_data; INFOPRINT("%s is called\n", __func__); INFOPRINT("minor : %d\n", MINOR(dev->dev)); switch(cmd) { case MOCK_CLEAR: INFOPRINT("MOCK_CLEAR\n"); mock_ioctl_clear(dev); break; case MOCK_SET_ACT: INFOPRINT("MOCK_SET_ACT\n"); mock_ioctl_set_act(dev, arg); break; default: ERRPRINT("unknown cmd (cmd=%u)\n", cmd); return -EINVAL; } return 0; } static struct file_operations mock_fops ={ .owner = THIS_MODULE, .open = mock_open, .release = mock_release, .read = mock_read, .write = mock_write, .unlocked_ioctl = mock_ioctl, }; static int __init mock_init(void) { struct device *dev; dev_t dev_id; int cdev_init_idx = -1; int device_create_idx = -1; int i; int ret; int err; ret = alloc_chrdev_region(&dev_id, 0, MODNUM, MODNAME); if (ret < 0) { ERRPRINT("alloc_chrdev_region failed (ret=%d)\n", ret); return ret; } mock_major = MAJOR(dev_id); for (i=0; i<MODNUM; i++) { cdev_init(&mock_dev[i].c_dev, &mock_fops); ret = cdev_add(&mock_dev[i].c_dev, dev_id, i+1); if (ret < 0) { ERRPRINT("cdev_add (%d) failed (ret=%d)\n", i, ret); err = ret; goto ERR_CDEV_DEL; } mock_dev[i].dev = MKDEV(mock_major, i); init_waitqueue_head(&mock_dev[i].wait_queue); cdev_init_idx = i; } mock_class = class_create(THIS_MODULE, MODNAME); if (IS_ERR(mock_class)) { ERRPRINT("class_create failed (ret=%d)\n", ret); err = PTR_ERR(mock_class); goto ERR_CDEV_DEL; } for (i=0; i<MODNUM; i++) { dev_id = MKDEV(mock_major, i); dev = device_create(mock_class, NULL, dev_id, NULL, MODNAME"%d", i); if (IS_ERR(dev)) { ERRPRINT("device_create (%d) failed (ret=%d)\n", i, ret); err = PTR_ERR(mock_dev); goto ERR_DEVICE_DESTROY; } device_create_idx = i; } INFOPRINT("%s is loaded\n", MODNAME); return 0; ERR_DEVICE_DESTROY: for (i=0; i<device_create_idx+1; i++) { dev_id = MKDEV(mock_major, i); device_destroy(mock_class, dev_id); } class_destroy(mock_class); ERR_CDEV_DEL: for (i=0; i<cdev_init_idx+1; i++) { cdev_del(&mock_dev[i].c_dev); } unregister_chrdev_region(dev_id, MODNUM); return err; } static void __exit mock_exit(void) { int i; dev_t dev_id; for (i=0; i<MODNUM; i++) { dev_id = MKDEV(mock_major, i); device_destroy(mock_class, dev_id); } class_destroy(mock_class); for (i=0; i<MODNUM; i++) { cdev_del(&mock_dev[i].c_dev); } dev_id = MKDEV(mock_major, 0); unregister_chrdev_region(dev_id, MODNUM); INFOPRINT("%s is unloaded\n", MODNAME); } module_init(mock_init); module_exit(mock_exit); MODULE_AUTHOR("Hiroshi Sakaida"); MODULE_DESCRIPTION("Mock Module"); MODULE_LICENSE("GPL v2");
mock.h
#ifndef _MOCK_H_ #define _MOCK_H_ #ifdef __cplusplus extern "C" { #endif #include <linux/ioctl.h> #define MOCKNUM 3 #define MOCKDEV_BASE "/dev/mock" typedef enum mock_act_e { MOCK_ACT_EXPECT_WRITE = 0, MOCK_ACT_RETURN_READ, } mock_act_t; #define MOCK_MAX_DATA_SIZE 256 typedef struct mock_packet_s { mock_act_t act; int ret_val; unsigned char data[MOCK_MAX_DATA_SIZE]; size_t size; } mock_packet_t; #define MOCKDEV_MAGIC 0xAE #define MOCK_CLEAR _IOC(_IOC_NONE, MOCKDEV_MAGIC, 0, 0) #define MOCK_SET_ACT _IOC(_IOC_WRITE, MOCKDEV_MAGIC, 1, sizeof(mock_packet_t)) #ifdef __cplusplus } #endif #endif /* _MOCK_H_ */
main.c
#include <stdio.h> /* #include <sys/stat.h> */ #include <fcntl.h> #include <assert.h> #include "mock.h" #define DEVNUM 3 static int fd[DEVNUM]; #define PATHLEN 128 typedef int (*test_func)(void); #define TEST_NUM 2 static test_func tests[TEST_NUM]; static void get_dev_path(char path[], size_t pathlen, int idx) { snprintf(path, pathlen, "%s%d", MOCKDEV_BASE, idx); printf("%s\n", path); return; } static void test_setup(void) { int i; char path[PATHLEN]; int ret; for (i=0; i<DEVNUM; i++) { get_dev_path(path, PATHLEN, i); fd[i] = open(path, O_RDWR); assert(0 <= fd[i]); ret = ioctl(fd[i], MOCK_CLEAR, NULL); assert(0 <= ret); } return; } static int test_exit(void) { int i; for (i=0; i<DEVNUM; i++) { close(fd[i]); } return 0; } static int open_close_test(void) { return 0; } static int read_test(void) { mock_packet_t packet; unsigned char buf[256]; int idx = 0; int i, j; int ret; packet.data[idx++] = 7; packet.data[idx++] = 8; packet.data[idx++] = 9; packet.size = idx; for (i=0; i<DEVNUM; i++) { ret = ioctl(fd[i], MOCK_EXPECT_READ, &packet); assert(0 <= ret); ret = ioctl(fd[i], MOCK_EXPECT_READ, &packet); assert(0 <= ret); ret = read(fd[i], buf, 256); assert(ret == idx); for (j=0; j<idx; j++) { printf("buf[%d] : %u\n", j, buf[j]); assert(buf[j] == packet.data[j]); } ret = read(fd[i], buf, 256); assert(ret == idx); for (j=0; j<idx; j++) { printf("buf[%d] : %u\n", j, buf[j]); assert(buf[j] == packet.data[j]); } } return 0; } static int exec_tests(void) { int i; int test_ret = 0; int exit_ret = 0; int self_ret = 0; for (i = 0; i<TEST_NUM; i++) { printf("-----------------\n"); printf(" TEST [%d]\n", i); printf("-----------------\n\n"); test_setup(); test_ret = tests[i](); exit_ret = test_exit(); if (test_ret < 0 || exit_ret < 0) { printf("Failure\n"); self_ret = -1; } else { printf("Success\n"); } } return self_ret; } int main(int argc, char *argv[]) { int idx = 0; int ret; tests[idx++] = open_close_test; tests[idx++] = read_test; ret = exec_tests(); printf("\n\n- - - - - - - - - - -\n"); if (ret < 0) { printf("Tests Failed\n"); } else { printf("Tests Succeeded\n"); } return 0; }
共通ヘッダ com.h
#ifndef _COM_H_ #define _COM_H_ #include <pthread.h> #define SHARE_FILE "/tmp/libcmd" #define CMD_MAX_LEN 256 typedef struct _shared_data_t { pthread_mutex_t mutex; pthread_cond_t cond; char cmd[CMD_MAX_LEN]; unsigned int count; } shared_data_t; #endif /* _COM_H_ */
送信側
#include <stdio.h> #include <string.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/errno.h> #include <sys/fcntl.h> #include <sys/mman.h> #include "com.h" int main(int argc, char *argv[]) { int fd; char *name; int id; char cmd[CMD_MAX_LEN]; int memsize = sizeof(shared_data_t); shared_data_t *shared_data; if (argc < 3) { fprintf(stderr, "cmd [\"name\"] [id]\n"); return 1; } fd = open(SHARE_FILE, O_RDWR); if (fd < 0) { perror("open failed"); return 1; } shared_data = (shared_data_t *)mmap(NULL, memsize, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); if (shared_data == NULL) { perror("mmap failed"); close(fd); return 1; } name = argv[1]; id = atoi(argv[2]); printf("execute %s %d\n", name, id); pthread_mutex_lock(&shared_data->mutex); snprintf(shared_data->cmd, CMD_MAX_LEN, "%s %d", name, id); shared_data->count += 1; pthread_cond_broadcast(&shared_data->cond); pthread_mutex_unlock(&shared_data->mutex); close(fd); }
受信側
#include <stdio.h> #include <string.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/errno.h> #include <sys/fcntl.h> #include <sys/mman.h> #include "com.h" int main(int argc, char *argv[]) { int fd; char *name; int id; char cmd[CMD_MAX_LEN]; int memsize = sizeof(shared_data_t); shared_data_t *shared_data; struct stat st; int init_flg = 0; unsigned int count; if (stat(SHARE_FILE, &st) < 0) { printf("Create share file...\n"); fd = open(SHARE_FILE, O_RDWR | O_CREAT, S_IRWXU); if (fd < 0) { perror("open failed"); return 1; } if (ftruncate(fd, memsize) < 0) { perror("ftruncate failed"); close(fd); return 1; } init_flg = 1; } else { fd = open(SHARE_FILE, O_RDWR); if (fd < 0) { perror("open failed"); return 1; } } shared_data = (shared_data_t *)mmap(NULL, memsize, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); if (shared_data == NULL) { perror("mmap failed"); close(fd); return 1; } if (init_flg) { pthread_mutex_init(&shared_data->mutex, NULL); pthread_cond_init(&shared_data->cond, NULL); shared_data->count = 0; } pthread_mutex_lock(&shared_data->mutex); count = shared_data->count; while (count == shared_data->count) { pthread_cond_wait(&shared_data->cond, &shared_data->mutex); } printf("receive %s (count=%d)\n", shared_data->cmd, shared_data->count); pthread_mutex_unlock(&shared_data->mutex); close(fd); }