304 lines
7.5 KiB
C
304 lines
7.5 KiB
C
/**
|
|
* @copyright (c) 2024, MacRsh
|
|
*
|
|
* @license SPDX-License-Identifier: Apache-2.0
|
|
*
|
|
* @date 2024-09-06 MacRsh First version
|
|
*/
|
|
|
|
#include <kernel/mr_ktimer.h>
|
|
#if defined(MR_USE_KTIMER)
|
|
#include <kernel/mr_initcall.h>
|
|
#include <kernel/mr_kspinlock.h>
|
|
#include <libc/mr_malloc.h>
|
|
|
|
/* Ktimer ktype */
|
|
static mr_ktype_t ktype1, ktype2;
|
|
/* Ktimer list */
|
|
static mr_klist_t klist = MR_KLIST_INIT(&klist);
|
|
/* Ktimer lock */
|
|
static mr_kspinlock_t klock = MR_KSPINLOCK_INIT();
|
|
/* Ktimer kset */
|
|
static mr_kset_t kroot = MR_KSET_INIT(&kroot, MR_NULL);
|
|
|
|
MR_INLINE void ktimer_init(mr_ktimer_t *ktimer, mr_ptr_t entry, void *args,
|
|
mr_tick_t tick) {
|
|
/* Init ktimer */
|
|
mr_klist_init(&ktimer->list);
|
|
ktimer->init_tick = tick;
|
|
ktimer->timeout_tick = 0;
|
|
ktimer->entry = entry;
|
|
ktimer->args = args;
|
|
}
|
|
|
|
MR_INLINE mr_err_t ktimer_init_add(mr_ktimer_t *ktimer, mr_ktype_t *ktype,
|
|
const char *name, mr_ptr_t entry, void *args,
|
|
mr_tick_t tick) {
|
|
/* Init ktimer */
|
|
ktimer_init(ktimer, entry, args, tick);
|
|
|
|
/* Init kobject */
|
|
mr_kobject_init((mr_kobject_t *)ktimer, ktype);
|
|
|
|
/* Add kobject to kroot */
|
|
return mr_kobject_add((mr_kobject_t *)ktimer, (mr_kobject_t *)&kroot, name);
|
|
}
|
|
|
|
mr_err_t mr_ktimer_init(mr_ktimer_t *ktimer, const char *name,
|
|
void (*entry)(mr_ktimer_t *ktimer, void *args),
|
|
void *args, mr_tick_t tick) {
|
|
/* Check arguments */
|
|
if ((!ktimer) || (MR_KOBJECT_IS_INITED(ktimer)) || (!name) || (!entry)
|
|
|| (tick == 0)) {
|
|
return -MR_EINVAL;
|
|
}
|
|
|
|
/* Init and add ktimer */
|
|
return ktimer_init_add(ktimer, &ktype1, name, (mr_ptr_t)entry, args, tick);
|
|
}
|
|
|
|
mr_ktimer_t *mr_ktimer_create(const char *name,
|
|
void (*entry)(mr_ktimer_t *ktimer, void *args),
|
|
void *args, mr_tick_t tick) {
|
|
mr_ktimer_t *ktimer;
|
|
mr_err_t ret;
|
|
|
|
/* Check arguments */
|
|
if ((!name) || (!entry) || (tick == 0)) {
|
|
return MR_NULL;
|
|
}
|
|
|
|
/* Create ktimer */
|
|
ktimer = mr_malloc(sizeof(mr_ktimer_t));
|
|
if (!ktimer) {
|
|
return MR_NULL;
|
|
}
|
|
|
|
/* Init and add ktimer */
|
|
ret = ktimer_init_add(ktimer, &ktype2, name, (mr_ptr_t)entry, args, tick);
|
|
if (ret != 0) {
|
|
mr_free(ktimer);
|
|
return MR_NULL;
|
|
}
|
|
return ktimer;
|
|
}
|
|
|
|
MR_INLINE void ktimer_del(mr_ktimer_t *ktimer) {
|
|
int mask;
|
|
|
|
/* Lock */
|
|
mask = mr_kspinlock_lock_irqsave(&klock);
|
|
|
|
/* Remove ktimer from ktimer list */
|
|
if (!mr_klist_is_empty(&ktimer->list)) {
|
|
mr_klist_del(&ktimer->list);
|
|
}
|
|
|
|
/* Unlock */
|
|
mr_kspinlock_unlock_irqrestore(&klock, mask);
|
|
}
|
|
|
|
mr_err_t mr_ktimer_del(mr_ktimer_t *ktimer) {
|
|
/* Check arguments */
|
|
if ((!ktimer) || (!MR_KOBJECT_IS_INITED(ktimer))) {
|
|
return -MR_EINVAL;
|
|
}
|
|
|
|
/* Delete ktimer */
|
|
ktimer_del(ktimer);
|
|
|
|
/* Delete kobject */
|
|
mr_kobject_del((mr_kobject_t *)ktimer);
|
|
return 0;
|
|
}
|
|
|
|
MR_INLINE void ktimer_insert(mr_ktimer_t *ktimer) {
|
|
mr_tick_t tick;
|
|
mr_ktimer_t *t;
|
|
mr_klist_t *l;
|
|
|
|
/* Set timeout tick */
|
|
tick = ktimer->init_tick & ~MR_KTIMER_PERIODIC;
|
|
ktimer->timeout_tick = mr_kclock_tick() + tick;
|
|
|
|
/* Insert ktimer into ktimer list */
|
|
MR_KLIST_FOR_EACH(l, &klist) {
|
|
t = MR_KLIST_ENTRY(l, mr_ktimer_t, list);
|
|
if ((ktimer->timeout_tick - t->timeout_tick) < (MR_TICK_MAX / 2)) {
|
|
continue;
|
|
}
|
|
|
|
/* Move ktimer into position */
|
|
mr_klist_move(&ktimer->list, &t->list);
|
|
return;
|
|
}
|
|
|
|
/* It is the last ktimer */
|
|
mr_klist_move(&ktimer->list, &klist);
|
|
}
|
|
|
|
MR_INLINE void ktimer_start(mr_ktimer_t *ktimer) {
|
|
int mask;
|
|
|
|
/* Lock */
|
|
mask = mr_kspinlock_lock_irqsave(&klock);
|
|
|
|
/* Insert ktimer into ktimer list */
|
|
ktimer_insert(ktimer);
|
|
|
|
/* Unlock */
|
|
mr_kspinlock_unlock_irqrestore(&klock, mask);
|
|
}
|
|
|
|
mr_err_t mr_ktimer_start(mr_ktimer_t *ktimer) {
|
|
/* Check arguments */
|
|
if ((!ktimer) || (!MR_KOBJECT_IS_INITED(ktimer))) {
|
|
return -MR_EINVAL;
|
|
}
|
|
|
|
/* Start ktimer */
|
|
ktimer_start(ktimer);
|
|
return 0;
|
|
}
|
|
|
|
mr_err_t mr_ktimer_stop(mr_ktimer_t *ktimer) {
|
|
/* Check arguments */
|
|
if ((!ktimer) || (!MR_KOBJECT_IS_INITED(ktimer))) {
|
|
return -MR_EINVAL;
|
|
}
|
|
|
|
/* Delete ktimer */
|
|
ktimer_del(ktimer);
|
|
return 0;
|
|
}
|
|
|
|
MR_INLINE void ktimer_tick_set(mr_ktimer_t *ktimer, mr_tick_t tick) {
|
|
int mask;
|
|
|
|
/* Lock */
|
|
mask = mr_kspinlock_lock_irqsave(&klock);
|
|
|
|
/* Remove ktimer from ktimer list */
|
|
if (!mr_klist_is_empty(&ktimer->list)) {
|
|
mr_klist_del(&ktimer->list);
|
|
}
|
|
|
|
/* Set init tick */
|
|
ktimer->init_tick = tick;
|
|
|
|
/* Unlock */
|
|
mr_kspinlock_unlock_irqrestore(&klock, mask);
|
|
}
|
|
|
|
mr_err_t mr_ktimer_tick_set(mr_ktimer_t *ktimer, mr_tick_t tick) {
|
|
/* Check arguments */
|
|
if ((!ktimer) || (!MR_KOBJECT_IS_INITED(ktimer)) || (tick == 0)) {
|
|
return -MR_EINVAL;
|
|
}
|
|
|
|
/* Set ktimer tick */
|
|
ktimer_tick_set(ktimer, tick);
|
|
return 0;
|
|
}
|
|
|
|
mr_ktimer_t *mr_ktimer_find(const char *name) {
|
|
/* Lookup ktimer in kroot */
|
|
return (mr_ktimer_t *)mr_kobject_lookup((mr_kobject_t *)&kroot, name);
|
|
}
|
|
|
|
MR_INLINE mr_ktimer_t *ktimer_release_kobj(mr_kobject_t *kobj) {
|
|
mr_ktimer_t *ktimer;
|
|
|
|
/* Get ktimer */
|
|
ktimer = MR_CONTAINER_OF(kobj, mr_ktimer_t, parent);
|
|
|
|
/* Delete ktimer */
|
|
ktimer_del(ktimer);
|
|
return ktimer;
|
|
}
|
|
|
|
MR_INLINE void ktimer_release1_kobj(mr_kobject_t *kobj) {
|
|
/* Release ktimer */
|
|
ktimer_release_kobj(kobj);
|
|
}
|
|
|
|
MR_INLINE void ktimer_release2_kobj(mr_kobject_t *kobj) {
|
|
mr_ktimer_t *ktimer;
|
|
|
|
/* Release ktimer */
|
|
ktimer = ktimer_release_kobj(kobj);
|
|
|
|
/* Free ktimer */
|
|
mr_free(ktimer);
|
|
}
|
|
|
|
MR_INLINE void ktimer_timeout_check(void) {
|
|
mr_ktimer_t *ktimer;
|
|
mr_klist_t list;
|
|
int mask;
|
|
|
|
/* Operation list */
|
|
mr_klist_init(&list);
|
|
|
|
/* Lock */
|
|
mask = mr_kspinlock_lock_irqsave(&klock);
|
|
|
|
/* Check timeout ktimer */
|
|
while (!mr_klist_is_empty(&klist)) {
|
|
ktimer = MR_KLIST_ENTRY(klist.next, mr_ktimer_t, list);
|
|
|
|
/* Check timeout tick */
|
|
if ((mr_kclock_tick() - ktimer->timeout_tick) >= (MR_TICK_MAX / 2)) {
|
|
/* There will be no timeout ktimer after that */
|
|
break;
|
|
}
|
|
|
|
/* Move ktimer into operation list */
|
|
mr_klist_move(&ktimer->list, &list);
|
|
|
|
/* Unlock */
|
|
mr_kspinlock_unlock_irqrestore(&klock, mask);
|
|
|
|
/* Call ktimer entry */
|
|
((void (*)(mr_ktimer_t *, void *))ktimer->entry)(ktimer, ktimer->args);
|
|
|
|
/* Lock */
|
|
mask = mr_kspinlock_lock_irqsave(&klock);
|
|
|
|
/* Check that the ktimer has not been moved */
|
|
if (mr_klist_is_empty(&list)) {
|
|
/* Timer is no longer yours to operate */
|
|
continue;
|
|
}
|
|
|
|
/* Tick is periodic */
|
|
if (ktimer->init_tick & MR_KTIMER_PERIODIC) {
|
|
/* Restart ktimer */
|
|
ktimer_insert(ktimer);
|
|
}
|
|
}
|
|
|
|
/* Unlock */
|
|
mr_kspinlock_unlock_irqrestore(&klock, mask);
|
|
}
|
|
|
|
MR_INLINE void mr_ktimer_kset_init(void) {
|
|
mr_err_t ret;
|
|
|
|
/* Register kset */
|
|
ret = mr_kset_register(&kroot, "timer");
|
|
if (ret != 0) {
|
|
/* It won't entry the kclock, so it won't start */
|
|
return;
|
|
}
|
|
|
|
/* Init ktype */
|
|
mr_ktype_init(&ktype1, ktimer_release1_kobj);
|
|
mr_ktype_init(&ktype2, ktimer_release2_kobj);
|
|
|
|
/* Add kclock entry */
|
|
mr_kclock_hook_add(ktimer_timeout_check);
|
|
}
|
|
MR_INIT_EXPORT(mr_ktimer_kset_init, MR_INIT_LEVEL_KERNEL);
|
|
#endif /* defined(MR_USE_KTIMER) */
|