// SPDX-License-Identifier: GPL-2.0 /* * Simple heartbeat STM source driver * Copyright (c) 2016, Intel Corporation. * * Heartbeat STM source will send repetitive messages over STM devices to a * trace host. */ #include #include #include #include #include #define STM_HEARTBEAT_MAX 32 static int nr_devs = 4; static int interval_ms = 10; module_param(nr_devs, int, 0400); module_param(interval_ms, int, 0600); static struct stm_heartbeat { struct stm_source_data data; struct hrtimer hrtimer; unsigned int active; } stm_heartbeat[STM_HEARTBEAT_MAX]; static const char str[] = "heartbeat stm source driver is here to serve you"; static enum hrtimer_restart stm_heartbeat_hrtimer_handler(struct hrtimer *hr) { struct stm_heartbeat *heartbeat = container_of(hr, struct stm_heartbeat, hrtimer); stm_source_write(&heartbeat->data, 0, str, sizeof str); if (heartbeat->active) hrtimer_forward_now(hr, ms_to_ktime(interval_ms)); return heartbeat->active ? HRTIMER_RESTART : HRTIMER_NORESTART; } static int stm_heartbeat_link(struct stm_source_data *data) { struct stm_heartbeat *heartbeat = container_of(data, struct stm_heartbeat, data); heartbeat->active = 1; hrtimer_start(&heartbeat->hrtimer, ms_to_ktime(interval_ms), HRTIMER_MODE_ABS); return 0; } static void stm_heartbeat_unlink(struct stm_source_data *data) { struct stm_heartbeat *heartbeat = container_of(data, struct stm_heartbeat, data); heartbeat->active = 0; hrtimer_cancel(&heartbeat->hrtimer); } static int stm_heartbeat_init(void) { int i, ret; if (nr_devs < 0 || nr_devs > STM_HEARTBEAT_MAX) return -EINVAL; for (i = 0; i < nr_devs; i++) { stm_heartbeat[i].data.name = kasprintf(GFP_KERNEL, "heartbeat.%d", i); if (!stm_heartbeat[i].data.name) { ret = -ENOMEM; goto fail_unregister; } stm_heartbeat[i].data.nr_chans = 1; stm_heartbeat[i].data.link = stm_heartbeat_link; stm_heartbeat[i].data.unlink = stm_heartbeat_unlink; hrtimer_init(&stm_heartbeat[i].hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS); stm_heartbeat[i].hrtimer.function = stm_heartbeat_hrtimer_handler; ret = stm_source_register_device(NULL, &stm_heartbeat[i].data); if (ret) goto fail_free; } return 0; fail_unregister: for (i--; i >= 0; i--) { stm_source_unregister_device(&stm_heartbeat[i].data); fail_free: kfree(stm_heartbeat[i].data.name); } return ret; } static void stm_heartbeat_exit(void) { int i; for (i = 0; i < nr_devs; i++) { stm_source_unregister_device(&stm_heartbeat[i].data); kfree(stm_heartbeat[i].data.name); } } module_init(stm_heartbeat_init); module_exit(stm_heartbeat_exit); MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("stm_heartbeat driver"); MODULE_AUTHOR("Alexander Shishkin ");