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);
}