/* * Copyright (C) 2010 Jan Kiszka . * * Xenomai is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Xenomai is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Xenomai; if not, write to the Free Software Foundation, * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include #include MODULE_DESCRIPTION("RTDM test helper module"); MODULE_AUTHOR("Jan Kiszka "); MODULE_VERSION("0.1.0"); MODULE_LICENSE("GPL"); struct rtdm_basic_context { rtdm_timer_t close_timer; unsigned long close_counter; unsigned long close_deferral; }; struct rtdm_actor_context { rtdm_task_t actor_task; unsigned int request; rtdm_event_t run; rtdm_event_t done; union { __u32 cpu; } args; }; static void close_timer_proc(rtdm_timer_t *timer) { struct rtdm_basic_context *ctx = container_of(timer, struct rtdm_basic_context, close_timer); if (ctx->close_counter != 1) printk(XENO_ERR "rtdmtest: %s: close_counter is %lu, should be 1!\n", __FUNCTION__, ctx->close_counter); ctx->close_deferral = RTTST_RTDM_NORMAL_CLOSE; rtdm_fd_unlock(rtdm_private_to_fd(ctx)); } static int rtdm_basic_open(struct rtdm_fd *fd, int oflags) { struct rtdm_basic_context *ctx = rtdm_fd_to_private(fd); rtdm_timer_init(&ctx->close_timer, close_timer_proc, "rtdm close test"); ctx->close_counter = 0; ctx->close_deferral = RTTST_RTDM_NORMAL_CLOSE; return 0; } static void rtdm_basic_close(struct rtdm_fd *fd) { struct rtdm_basic_context *ctx = rtdm_fd_to_private(fd); ctx->close_counter++; switch (ctx->close_deferral) { case RTTST_RTDM_DEFER_CLOSE_CONTEXT: if (ctx->close_counter != 2) { printk(XENO_ERR "rtdmtest: %s: close_counter is %lu, " "should be 2!\n", __FUNCTION__, ctx->close_counter); return; } rtdm_fd_unlock(fd); break; } rtdm_timer_destroy(&ctx->close_timer); } static int rtdm_basic_ioctl_rt(struct rtdm_fd *fd, unsigned int request, void __user *arg) { int ret, magic = RTTST_RTDM_MAGIC_PRIMARY; switch (request) { case RTTST_RTIOC_RTDM_PING_PRIMARY: ret = rtdm_safe_copy_to_user(fd, arg, &magic, sizeof(magic)); break; default: ret = -ENOSYS; } return ret; } static int rtdm_basic_ioctl_nrt(struct rtdm_fd *fd, unsigned int request, void __user *arg) { struct rtdm_basic_context *ctx = rtdm_fd_to_private(fd); int ret = 0, magic = RTTST_RTDM_MAGIC_SECONDARY; switch (request) { case RTTST_RTIOC_RTDM_DEFER_CLOSE: ctx->close_deferral = (unsigned long)arg; if (ctx->close_deferral == RTTST_RTDM_DEFER_CLOSE_CONTEXT) { ++ctx->close_counter; rtdm_fd_lock(fd); rtdm_timer_start(&ctx->close_timer, 300000000ULL, 0, RTDM_TIMERMODE_RELATIVE); } break; case RTTST_RTIOC_RTDM_PING_SECONDARY: ret = rtdm_safe_copy_to_user(fd, arg, &magic, sizeof(magic)); break; default: ret = -ENOTTY; } return ret; } static void actor_handler(void *arg) { struct rtdm_actor_context *ctx = arg; int ret; for (;;) { if (rtdm_task_should_stop()) return; ret = rtdm_event_wait(&ctx->run); if (ret) break; switch (ctx->request) { case RTTST_RTIOC_RTDM_ACTOR_GET_CPU: ctx->args.cpu = task_cpu(current); break; default: printk(XENO_ERR "rtdmtest: bad request code %d\n", ctx->request); } rtdm_event_signal(&ctx->done); } } static int rtdm_actor_open(struct rtdm_fd *fd, int oflags) { struct rtdm_actor_context *ctx = rtdm_fd_to_private(fd); rtdm_event_init(&ctx->run, 0); rtdm_event_init(&ctx->done, 0); return rtdm_task_init(&ctx->actor_task, "rtdm_actor", actor_handler, ctx, RTDM_TASK_LOWEST_PRIORITY, 0); } static void rtdm_actor_close(struct rtdm_fd *fd) { struct rtdm_actor_context *ctx = rtdm_fd_to_private(fd); rtdm_task_destroy(&ctx->actor_task); rtdm_event_destroy(&ctx->run); rtdm_event_destroy(&ctx->done); } #define ACTION_TIMEOUT 50000000ULL /* 50 ms timeout on action */ static int run_action(struct rtdm_actor_context *ctx, unsigned int request) { rtdm_toseq_t toseq; rtdm_toseq_init(&toseq, ACTION_TIMEOUT); ctx->request = request; rtdm_event_signal(&ctx->run); /* * XXX: The handshake mechanism is not bullet-proof against * -EINTR received when waiting for the done event. Hopefully * we won't restart/start a request while the action task has * not yet completed the previous one we stopped waiting for * abruptly. */ return rtdm_event_timedwait(&ctx->done, ACTION_TIMEOUT, &toseq); } static int rtdm_actor_ioctl(struct rtdm_fd *fd, unsigned int request, void __user *arg) { struct rtdm_actor_context *ctx = rtdm_fd_to_private(fd); int ret; switch (request) { case RTTST_RTIOC_RTDM_ACTOR_GET_CPU: ctx->args.cpu = (__u32)-EINVAL; ret = run_action(ctx, request); if (ret) break; ret = rtdm_safe_copy_to_user(fd, arg, &ctx->args.cpu, sizeof(ctx->args.cpu)); break; default: ret = -ENOTTY; } return ret; } static struct rtdm_driver rtdm_basic_driver = { .profile_info = RTDM_PROFILE_INFO(rtdm_test_basic, RTDM_CLASS_TESTING, RTDM_SUBCLASS_RTDMTEST, RTTST_PROFILE_VER), .device_flags = RTDM_NAMED_DEVICE | RTDM_EXCLUSIVE, .device_count = 2, .context_size = sizeof(struct rtdm_basic_context), .ops = { .open = rtdm_basic_open, .close = rtdm_basic_close, .ioctl_rt = rtdm_basic_ioctl_rt, .ioctl_nrt = rtdm_basic_ioctl_nrt, }, }; static struct rtdm_driver rtdm_actor_driver = { .profile_info = RTDM_PROFILE_INFO(rtdm_test_actor, RTDM_CLASS_TESTING, RTDM_SUBCLASS_RTDMTEST, RTTST_PROFILE_VER), .device_flags = RTDM_NAMED_DEVICE | RTDM_EXCLUSIVE, .device_count = 1, .context_size = sizeof(struct rtdm_actor_context), .ops = { .open = rtdm_actor_open, .close = rtdm_actor_close, .ioctl_rt = rtdm_actor_ioctl, }, }; static struct rtdm_device device[3] = { [0 ... 1] = { .driver = &rtdm_basic_driver, .label = "rtdm%d", }, [2] = { .driver = &rtdm_actor_driver, .label = "rtdmx", } }; static int __init rtdm_test_init(void) { int i, ret; for (i = 0; i < ARRAY_SIZE(device); i++) { ret = rtdm_dev_register(device + i); if (ret) goto fail; } return 0; fail: while (i-- > 0) rtdm_dev_unregister(device + i); return ret; } static void __exit rtdm_test_exit(void) { int i; for (i = 0; i < ARRAY_SIZE(device); i++) rtdm_dev_unregister(device + i); } module_init(rtdm_test_init); module_exit(rtdm_test_exit);