Skip to content

Instantly share code, notes, and snippets.

@Ruinland
Created October 8, 2021 12:32
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Ruinland/362eb9dc65615b076b098ddb507f07d8 to your computer and use it in GitHub Desktop.
Save Ruinland/362eb9dc65615b076b098ddb507f07d8 to your computer and use it in GitHub Desktop.
rtc-fake.2021.c
/*
* Fake RTC whose time is settable using the command line
*
* Copyright (C) 2013 Joseph C. Lehner <joseph.c.lehner@gmail.com>
* Copyright (C) 2021 Ruinland <ruinland.please.dont.count.me.in>
*
* This program 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; version 2 of the License.
*
* This program 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 this program.
* If not, see <http://www.gnu.org/licenses/>.
*/
/*
* This driver provides a very simple RTC device, which requires the
* user to specify an initial time via a command-line option. Reading
* from the device will return that initial time plus the time elapsed
* since the device was initialized. By default, setting the time is not
* supported, as it makes little sense given the fact that the value is
* lost upon reboot/module unloading.
*
* In conjunction with CONFIG_RTC_HCTOSYS the driver may be used to specify
* the machine's boot time (may be of interest on systems lacking a builtin
* RTC). For this to work, the driver must be built as a module!
*
*/
#include <linux/platform_device.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/time.h>
#include <linux/err.h>
#include <linux/rtc.h>
#define DRVNAME "rtc-fake"
#define MOD_WARNING KERN_WARNING DRVNAME ": "
#define MOD_NOTICE KERN_NOTICE DRVNAME ": "
#define MOD_INFO KERN_INFO DRVNAME ": "
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Joseph C. Lehner");
MODULE_DESCRIPTION("Fake RTC that returns a time specified by the module/kernel command line");
static struct platform_device *pdev = NULL;
static struct timespec64 begtime;
static unsigned long time = 100;
module_param(time, ulong, S_IRUGO);
MODULE_PARM_DESC(time, "Initial time in seconds since the epoch; use 0 to disable");
static bool can_set_time = 1;
module_param(can_set_time, bool, S_IRUGO);
MODULE_PARM_DESC(can_set_time, "Allow setting the time; this usually makes no sense");
static inline unsigned long timespec_to_ulong(struct timespec64 *ts)
{
return ts->tv_nsec < NSEC_PER_SEC/2 ? ts->tv_sec : ts->tv_sec + 1;
}
static inline void get_uptime(struct timespec64* ts)
{
ktime_get_raw_ts64(ts);
ktime_get_boottime_ts64(ts);
}
static int fake_rtc_read_time(struct device *dev, struct rtc_time *tm)
{
struct timespec64 now, diff;
get_uptime(&now);
diff = timespec64_sub(now, begtime);
rtc_time64_to_tm(time + timespec_to_ulong(&diff), tm);
return rtc_valid_tm(tm);
}
static int fake_rtc_set_time(struct device *dev, struct rtc_time *tm)
{
if (!can_set_time)
return -ENOSYS;
get_uptime(&begtime);
rtc_tm_to_time64(tm);
return 0;
}
static const struct rtc_class_ops fake_rtc_ops = {
.read_time = fake_rtc_read_time,
.set_time = fake_rtc_set_time
};
static int fake_rtc_probe(struct platform_device *pdev)
{
struct rtc_device *rtc;
get_uptime(&begtime);
rtc = devm_rtc_device_register(&pdev->dev, pdev->name, &fake_rtc_ops,
THIS_MODULE);
if (IS_ERR(rtc))
return PTR_ERR(rtc);
platform_set_drvdata(pdev, rtc);
dev_info(&pdev->dev, "loaded; begtime is %lu, time is %lu\n",
timespec_to_ulong(&begtime), time);
return 0;
}
static int fake_rtc_remove(struct platform_device *pdev)
{
//rtc_device_unregister(platform_get_drvdata(pdev));
return 0;
}
static struct platform_driver fake_rtc_drv = {
.probe = fake_rtc_probe,
.remove = fake_rtc_remove,
.driver = {
.name = DRVNAME,
.owner = THIS_MODULE
},
};
static int __init fake_rtc_init(void)
{
int err;
if (!time) {
#ifdef MODULE
printk(MOD_NOTICE "missing 'time' parameter\n");
#endif
return -EINVAL;
}
if ((err = platform_driver_register(&fake_rtc_drv))) {
printk(MOD_INFO "platform_register_driver failed; errno=%d\n", err);
goto out;
}
pdev = platform_device_register_simple(DRVNAME, -1, NULL, 0);
if (IS_ERR(pdev)) {
err = PTR_ERR(pdev);
printk(MOD_INFO "platform_device_register_simple failed; errno=%d\n", err);
platform_driver_unregister(&fake_rtc_drv);
goto out;
}
out:
return err;
}
static void __exit fake_rtc_exit(void)
{
platform_device_unregister(pdev);
platform_driver_unregister(&fake_rtc_drv);
}
module_init(fake_rtc_init);
module_exit(fake_rtc_exit);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment