VxKernel 0.6.0-7 : First version of the timer API + update INTC API
@add <> include/vhex/driver/mpu/sh/sh7305/intc | add interruption priority primitives | add all interruption configuration available <> src/driver/mpu/sh/sh7305/intc | remove the install.c source file (merge its content in driver definition) | add proper priority primitive | add priority information @update <> include/vhex/timer | force TIMER_CONTINUE at 0 | force TIMER_STOP at 1 <> src/driver/mpu/sh/sh7305/keysc | use the new interrupt priority primitives @fix <> src/driver/mpu/sh/sh7305/tmu | fix interrupt handler assembly crash for ETMUx | fix timer ID check security | fix timer available check | fix ETMUx interrupt handlers installation runtime patch | add timer interrupt level (enable interrupt) <> src/modules/display/text/dtext | fix string cache reallocation <> src/module/timer | fix driver flag check | fix module definition
This commit is contained in:
parent
0c50357b7d
commit
7aada07d23
|
@ -412,9 +412,57 @@ struct sh7305_intc
|
|||
/* Provided by intc/intc.c */
|
||||
extern struct sh7305_intc SH7305_INTC;
|
||||
|
||||
/* provide hardware-specific primitive */
|
||||
|
||||
//---
|
||||
// Interrupt names
|
||||
//---
|
||||
|
||||
/* Because of SH3/SH4 differences and the different ways interrupts are mapped
|
||||
to bits of IPR and IMR registers, we define the following names for
|
||||
interrupt signals, and their corresponding IPR and IMR bits are defined in
|
||||
the INTC driver. */
|
||||
enum {
|
||||
/* Timer Unit [TMU] */
|
||||
INTC_TMU_TUNI0,
|
||||
INTC_TMU_TUNI1,
|
||||
INTC_TMU_TUNI2,
|
||||
/* Extra Timer Unit [TMU]; SH7305-only except for ETMU_TUNI0 */
|
||||
INTC_ETMU_TUNI0,
|
||||
INTC_ETMU_TUNI1,
|
||||
INTC_ETMU_TUNI2,
|
||||
INTC_ETMU_TUNI3,
|
||||
INTC_ETMU_TUNI4,
|
||||
INTC_ETMU_TUNI5,
|
||||
/* DMA Controller [DMA0]; a single IPR is used for DEI0..3, and another
|
||||
IPR is used for DEI4..5 and DADERR together */
|
||||
INTC_DMA_DEI0,
|
||||
INTC_DMA_DEI1,
|
||||
INTC_DMA_DEI2,
|
||||
INTC_DMA_DEI3,
|
||||
INTC_DMA_DEI4,
|
||||
INTC_DMA_DEI5,
|
||||
INTC_DMA_DADERR,
|
||||
/* Real-Time Clock [RTC]; a single IPR covers all 3 interrupts */
|
||||
INTC_RTC_ATI,
|
||||
INTC_RTC_PRI,
|
||||
INTC_RTC_CUI,
|
||||
/* SPU; interrupts from the DSPs and the SPU-bound DMA */
|
||||
INTC_SPU_DSP0,
|
||||
INTC_SPU_DSP1,
|
||||
/* USB communication */
|
||||
INTC_USB,
|
||||
/* Kay Scan Interface [KEYSC]; SH7305-only */
|
||||
INTC_KEYSC,
|
||||
};
|
||||
|
||||
//---
|
||||
// hardware-specific primitives
|
||||
//---
|
||||
|
||||
/* sh7305_intc_install_inth() : install interrupt gate */
|
||||
extern void *sh7305_intc_install_inth(int blockid, void *gate, size_t size);
|
||||
|
||||
/* sh7305_intc_priority() : set the interrupt name priority */
|
||||
extern int sh7305_intc_priority(int intname, int level);
|
||||
|
||||
#endif /* __VHEX_ARCH_SH7305_INTC__ */
|
||||
|
|
|
@ -10,8 +10,8 @@
|
|||
|
||||
/* returned status from a callback */
|
||||
enum {
|
||||
TIMER_STOP,
|
||||
TIMER_CONTINUE
|
||||
TIMER_CONTINUE = 0,
|
||||
TIMER_STOP = 1,
|
||||
};
|
||||
|
||||
/* timer_configure(): Reserve and configure a timer
|
||||
|
|
|
@ -2,14 +2,4 @@
|
|||
#include <vhex/defs/types.h>
|
||||
#include <string.h>
|
||||
|
||||
/* sh7305_intc_install_inth() : install interrupt gate */
|
||||
void *sh7305_intc_install_inth(int event_code, void *gate, size_t size)
|
||||
{
|
||||
extern uintptr_t vhex_vbr;
|
||||
|
||||
return memcpy(
|
||||
(void*)((uintptr_t)&vhex_vbr + 0x600 + 0x40 + (event_code - 0x400)),
|
||||
gate,
|
||||
size
|
||||
);
|
||||
}
|
||||
|
|
|
@ -36,7 +36,94 @@ struct intc_ctx {
|
|||
sh7305_intc_nmifcr_t nmifcr;
|
||||
};
|
||||
|
||||
/* hardware configuration call */
|
||||
//---
|
||||
// hardware-level API
|
||||
//---
|
||||
|
||||
/* Interrupt IPR and IMR positions. The order of entries is as in the named
|
||||
list of interrupt signals in <gint/intc.h>. */
|
||||
|
||||
/* Friendly names for IPR and IMR register numbers */
|
||||
enum{ IPRA, IPRB, IPRC, IPRD, IPRE, IPRF, IPRG, IPRH, IPRI, IPRJ, IPRK, IPRL };
|
||||
enum{ IMR0, IMR1, IMR2, IMR3, IMR4, IMR5, IMR6, IMR7, IMR8, IMR9, IMR10 };
|
||||
#define _ 0,0
|
||||
|
||||
static struct info {
|
||||
uint16_t IPR4, IPR4bits, IMR, IMRbits;
|
||||
} const info[] = {
|
||||
/* Standard TMU */
|
||||
{ IPRA, 0xf000, IMR4, 0x10 },
|
||||
{ IPRA, 0x0f00, IMR4, 0x20 },
|
||||
{ IPRA, 0x00f0, IMR4, 0x40 },
|
||||
/* ETMU */
|
||||
{ IPRJ, 0xf000, IMR6, 0x08 },
|
||||
{ IPRG, 0x0f00, IMR5, 0x02 },
|
||||
{ IPRG, 0x00f0, IMR5, 0x04 },
|
||||
{ IPRE, 0x00f0, IMR2, 0x01 },
|
||||
{ IPRI, 0xf000, IMR6, 0x10 },
|
||||
{ IPRL, 0xf000, IMR8, 0x02 },
|
||||
/* DMA */
|
||||
{ IPRE, 0xf000, IMR1, 0x01 },
|
||||
{ IPRE, 0xf000, IMR1, 0x02 },
|
||||
{ IPRE, 0xf000, IMR1, 0x04 },
|
||||
{ IPRE, 0xf000, IMR1, 0x08 },
|
||||
{ IPRF, 0x0f00, IMR5, 0x10 },
|
||||
{ IPRF, 0x0f00, IMR5, 0x20 },
|
||||
{ IPRF, 0x0f00, IMR5, 0x40 },
|
||||
/* RTC */
|
||||
{ IPRK, 0xf000, IMR10, 0x04 },
|
||||
{ IPRK, 0xf000, IMR10, 0x02 },
|
||||
{ IPRK, 0xf000, IMR10, 0x01 },
|
||||
/* SPU */
|
||||
{ IPRC, 0x000f, IMR3, 0x04 },
|
||||
{ IPRC, 0x000f, IMR4, 0x08 },
|
||||
/* USB */
|
||||
{ IPRF, 0x00f0, IMR9, 0x02 },
|
||||
/* KEYSC */
|
||||
{ IPRF, 0xf000, IMR5, 0x80 }
|
||||
};
|
||||
|
||||
/* sh7305_intc_priority() : set the interruption level */
|
||||
int sh7305_intc_priority(int intname, int level)
|
||||
{
|
||||
struct info const *i = &info[intname];
|
||||
int IPRn = i->IPR4, IPRbits = i->IPR4bits;
|
||||
|
||||
/* Bit-shift for the mask */
|
||||
int shift = 0;
|
||||
while(IPRbits >>= 4) shift += 4;
|
||||
|
||||
uint16_t volatile *IPR = &SH7305_INTC.IPR[IPRn << 1];
|
||||
|
||||
int oldlevel = (*IPR >> shift) & 0xf;
|
||||
*IPR = (*IPR & ~(0xf << shift)) | (level << shift);
|
||||
|
||||
if(level > 0) {
|
||||
uint8_t volatile *MSKCLR = SH7305_INTC.MSKCLR;
|
||||
MSKCLR[4*i->IMR] = i->IMRbits;
|
||||
} else {
|
||||
uint8_t volatile *MSK = SH7305_INTC.MSK;
|
||||
MSK[4*i->IMR] = i->IMRbits;
|
||||
}
|
||||
|
||||
return oldlevel;
|
||||
}
|
||||
|
||||
/* sh7305_intc_install_inth() : install interrupt gate */
|
||||
void *sh7305_intc_install_inth(int event_code, void *gate, size_t size)
|
||||
{
|
||||
extern uintptr_t vhex_vbr;
|
||||
|
||||
return memcpy(
|
||||
(void*)((uintptr_t)&vhex_vbr + 0x600 + 0x40 + (event_code - 0x400)),
|
||||
gate,
|
||||
size
|
||||
);
|
||||
}
|
||||
|
||||
//---
|
||||
// hardware configuration call
|
||||
//---
|
||||
|
||||
/* __intc_configure() : configure the Interrupt Controller
|
||||
|
||||
|
@ -97,7 +184,7 @@ static void __intc_configure(struct intc_ctx *state)
|
|||
state->intmsk00.IRQ2 = 1;
|
||||
state->intmsk00.IRQ3 = 1;
|
||||
|
||||
/* NMI Interrupt flagsi (clear) */
|
||||
/* NMI Interrupt flags (clear) */
|
||||
state->nmifcr.NMIFL = 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -79,7 +79,7 @@ static void __keysc_configure(struct keysc_ctx *ctx)
|
|||
ctx->keysc.KYINDR.KYDIR0 = 1;
|
||||
|
||||
/* set the interrupt level (max) */
|
||||
ctx->iprf = 0b1111;
|
||||
sh7305_intc_priority(INTC_KEYSC, 15);
|
||||
|
||||
/* intall the KEYSC interupt handler */
|
||||
extern void sh7305_keysc_inth(void);
|
||||
|
@ -97,7 +97,6 @@ static void __keysc_hsave(struct keysc_ctx *ctx)
|
|||
ctx->keysc.KIUINTERVALREG = SH7305_KEYSC.KIUINTERVALREG;
|
||||
ctx->keysc.KYOUTDR.word = SH7305_KEYSC.KYOUTDR.word;
|
||||
ctx->keysc.KYINDR.word = SH7305_KEYSC.KYINDR.word;
|
||||
ctx->iprf = SH7305_INTC._IPRX->IPRF.KEYSC;
|
||||
}
|
||||
|
||||
/* __keysc_hrestore() : restore hadware information */
|
||||
|
@ -105,7 +104,6 @@ static void __keysc_hrestore(struct keysc_ctx *ctx)
|
|||
{
|
||||
/* force mask the SH7305_KEYSC module, otherwise the module will crash */
|
||||
SH7305_INTC._MSK->IMR5.KEYSC = 1;
|
||||
SH7305_INTC._IPRX->IPRF.KEYSC = 0b0000;
|
||||
|
||||
/* restore registers */
|
||||
SH7305_KEYSC.KIUCNTREG.word = ctx->keysc.KIUCNTREG.word;
|
||||
|
@ -118,7 +116,6 @@ static void __keysc_hrestore(struct keysc_ctx *ctx)
|
|||
SH7305_KEYSC.KYINDR.word = ctx->keysc.KYINDR.word;
|
||||
|
||||
/* enable keysc interuption */
|
||||
SH7305_INTC._IPRX->IPRF.KEYSC = ctx->iprf;
|
||||
SH7305_INTC._MSKCLR->IMR5.KEYSC = 1;
|
||||
}
|
||||
|
||||
|
|
|
@ -36,36 +36,37 @@ _sh7305_inth_etmu4:
|
|||
/* Clear interrupt flag in TCR using slow write */
|
||||
mov r0, r1
|
||||
mov.l @(4, r1), r3
|
||||
1: mov.b @r3, r0
|
||||
TCR_clear:
|
||||
mov.b @r3, r0
|
||||
tst #0x02, r0
|
||||
and #0xfd, r0
|
||||
bf/s 1b
|
||||
bf/s TCR_clear
|
||||
mov.b r0, @r3
|
||||
|
||||
/* Invoke callback */
|
||||
mov.l .sh7305_inth_callback, r8
|
||||
mov.l @r8, r8
|
||||
mov.l sh7305_inth_callback, r8
|
||||
jsr @r8
|
||||
mov.l @r1, r4
|
||||
tst r0, r0
|
||||
bt 2f
|
||||
bt ETMUx_shared_exit
|
||||
|
||||
/* If return value is non-zero, stop the timer with another callback */
|
||||
mov.l .timer_stop, r0
|
||||
mov.l timer_stop, r0
|
||||
mov.l r0, @r15
|
||||
jsr @r8
|
||||
mov r15, r4
|
||||
|
||||
2: add #20, r15
|
||||
ETMUx_shared_exit:
|
||||
add #20, r15
|
||||
lds.l @r15+, pr
|
||||
rts
|
||||
mov.l @r15+, r8
|
||||
|
||||
.zero 26
|
||||
.zero 28
|
||||
|
||||
.timer_stop:
|
||||
timer_stop:
|
||||
.long _sh7305_tmu_stop
|
||||
.sh7305_inth_callback:
|
||||
sh7305_inth_callback:
|
||||
.long _sh7305_inth_callback
|
||||
.storage_etmu4:
|
||||
.long _sh7305_tmu_callbacks + 140
|
||||
|
@ -82,24 +83,25 @@ _sh7305_inth_etmu4:
|
|||
! address during runtime
|
||||
! <> the timer ID is set during the installation of the handler
|
||||
_sh7305_inth_etmux:
|
||||
/* Dynamically compute the target of the jump */
|
||||
stc vbr, r3
|
||||
mov.l 1f, r2
|
||||
mov.l etmu4_inth_addr, r3
|
||||
mov.w etmu4_inth_soff, r2
|
||||
add r2, r3
|
||||
nop
|
||||
|
||||
mova .storage_etmux, r0
|
||||
mov.w .id_etmux, r2
|
||||
mova storage_etmux, r0
|
||||
mov.w etmux_id, r2
|
||||
jmp @r3
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
|
||||
.id_etmux:
|
||||
.word 0 /* Timer ID */
|
||||
etmux_id:
|
||||
.word 0x0000 ! Timer ID */
|
||||
|
||||
/* Offset from VBR where ETMU4 is located; set during configure */
|
||||
1: .long (.shared - _sh7305_inth_etmu4)
|
||||
etmu4_inth_soff:
|
||||
.word (.shared - _sh7305_inth_etmu4) ! ETMU4 shared code off
|
||||
|
||||
.storage_etmux:
|
||||
.long _sh7305_tmu_callbacks
|
||||
.long 0 /* TCR address */
|
||||
etmu4_inth_addr:
|
||||
.long 0x00000000 ! ETMU4 shared code addr
|
||||
|
||||
storage_etmux:
|
||||
.long 0x00000000 ! _sh7305_tmu_callbacks
|
||||
.long 0x00000000 ! TCR address
|
||||
|
|
|
@ -11,8 +11,8 @@
|
|||
timer_call_t sh7305_tmu_callbacks[9];
|
||||
|
||||
/* Arrays of standard and extra timers */
|
||||
static tmu_t *TMU = SH7305_TMU.TMU;
|
||||
static etmu_t *ETMU = SH7305_ETMU;
|
||||
tmu_t *TMU = SH7305_TMU.TMU;
|
||||
etmu_t *ETMU = SH7305_ETMU;
|
||||
|
||||
/* TSTR register for standard timers */
|
||||
static volatile uint8_t *TSTR = &SH7305_TMU.TSTR;
|
||||
|
@ -73,7 +73,7 @@ static int conf(tid_t id, uint32_t delay, int clock, timer_call_t call)
|
|||
@state 0 to start the timer, 1 to stop it (nothing else!) */
|
||||
static int sh7305_tmu_control(tid_t id, int state)
|
||||
{
|
||||
if (id < 0 || id > 9)
|
||||
if (id < 0 || id >= 9)
|
||||
return (-1);
|
||||
|
||||
if(id < 3) {
|
||||
|
@ -87,16 +87,15 @@ static int sh7305_tmu_control(tid_t id, int state)
|
|||
/* available(): Check if a timer is available (UNIE cleared, not running) */
|
||||
static int available(int id)
|
||||
{
|
||||
if (id < 0 || id > 9)
|
||||
if (id < 0 || id >= 9)
|
||||
return (-1);
|
||||
|
||||
if(id < 3) {
|
||||
tmu_t *T = &TMU[id];
|
||||
return !T->TCR.UNIE && !(*TSTR & (1 << id));
|
||||
} else {
|
||||
etmu_t *T = &ETMU[id-3];
|
||||
return !T->TCR.UNIE && !T->TSTR;
|
||||
}
|
||||
etmu_t *T = &ETMU[id-3];
|
||||
return !T->TCR.UNIE && !T->TSTR;
|
||||
}
|
||||
|
||||
/* sh7305_tmu_delay() - compute a delay constant from a duration in seconds */
|
||||
|
@ -131,18 +130,18 @@ static int stop_callback(void)
|
|||
// Public timer-API
|
||||
//---
|
||||
|
||||
int sh7305_tmu_configure(uint64_t delay, timer_call_t call)
|
||||
tid_t sh7305_tmu_configure(uint64_t delay, timer_call_t call)
|
||||
{
|
||||
int clock = 0;
|
||||
|
||||
/* Default behavior for the callback */
|
||||
if(!call.function) call = TIMER_CALL(stop_callback);
|
||||
if(!call.function) call = TIMER_CALL(&stop_callback);
|
||||
|
||||
/* Find a matching timer, starting from the slowest timers with the
|
||||
smallest interrupt priorities all the way up to TMU0 */
|
||||
for(int id = 8; id >= 0; id--)
|
||||
{
|
||||
if(available(id) != 0)
|
||||
if(available(id) == 0)
|
||||
continue;
|
||||
|
||||
/* If ID is a TMU, choose a timer prescaler. Assuming the worst
|
||||
|
@ -166,7 +165,6 @@ int sh7305_tmu_configure(uint64_t delay, timer_call_t call)
|
|||
|
||||
return conf(id, delay, clock, call);
|
||||
}
|
||||
|
||||
return (-1);
|
||||
}
|
||||
|
||||
|
@ -180,7 +178,7 @@ int sh7305_tmu_start(tid_t id)
|
|||
int sh7305_tmu_reload(tid_t id, uint64_t delay)
|
||||
{
|
||||
//FIXME
|
||||
if (id < 0 || id > 9)
|
||||
if (id < 0 || id >= 9)
|
||||
return (-1);
|
||||
|
||||
if(id < 3) {
|
||||
|
@ -200,7 +198,7 @@ int sh7305_tmu_pause(tid_t id)
|
|||
/* sh7305_tmu_stop() - stop and free a timer */
|
||||
int sh7305_tmu_stop(tid_t id)
|
||||
{
|
||||
if (id < 0 || id > 9)
|
||||
if (id < 0 || id >= 9)
|
||||
return (-1);
|
||||
|
||||
/* Stop the timer and disable UNIE to indicate that it's free */
|
||||
|
@ -226,7 +224,7 @@ int sh7305_tmu_stop(tid_t id)
|
|||
/* sh7305_tmu_wait(): Wait for a timer to stop */
|
||||
int sh7305_tmu_wait(tid_t id)
|
||||
{
|
||||
if (id < 0 || id > 9)
|
||||
if (id < 0 || id >= 9)
|
||||
return (-1);
|
||||
|
||||
if(id < 3) {
|
||||
|
@ -242,7 +240,7 @@ int sh7305_tmu_wait(tid_t id)
|
|||
/* sh7305_tmu_spinwait(): Start a timer and actively wait for UNF */
|
||||
int sh7305_tmu_spinwait(tid_t id)
|
||||
{
|
||||
if (id < 0 || id > 9)
|
||||
if (id < 0 || id >= 9)
|
||||
return (-1);
|
||||
|
||||
if(id < 3) {
|
||||
|
@ -307,15 +305,28 @@ static void __tmu_configure(struct tmu_ctx *s)
|
|||
/* install the default interrupt handler */
|
||||
h = sh7305_intc_install_inth(etmu_evt[i], &sh7305_inth_etmux, 32);
|
||||
|
||||
/* Distance from VBR handler to ETMU4, used to jump */
|
||||
*(uint32_t *)(h + 20) += (uintptr_t)h4 - (uintptr_t)cpu_get_vbr();
|
||||
/* Timer ID, used for sh7305_tmu_stop() after the callback */
|
||||
*(uint16_t *)(h + 18) = i;
|
||||
*(uint16_t *)(h + 16) = i + 3;
|
||||
/* ETMU4 interrupt handler address, used to jump */
|
||||
*(uint32_t *)(h + 20) = (uintptr_t)h4;
|
||||
/* Pointer to the callback */
|
||||
*(void **)(h + 24) += i;
|
||||
*(uint32_t *)(h + 24) = (uintptr_t)&sh7305_tmu_callbacks[i + 3];
|
||||
/* TCR address to acknowledge the interrupt */
|
||||
*(void volatile **)(h + 28) = &ETMU[i].TCR;
|
||||
*(uint32_t *)(h + 28) = (uintptr_t)&ETMU[i].TCR;
|
||||
}
|
||||
|
||||
/* Enable TMU0 at level 13, TMU1 at level 11, TMU2 at level 9 */
|
||||
sh7305_intc_priority(INTC_TMU_TUNI0, 13);
|
||||
sh7305_intc_priority(INTC_TMU_TUNI1, 11);
|
||||
sh7305_intc_priority(INTC_TMU_TUNI2, 9);
|
||||
|
||||
/* Enable the extra TMUs at level 7 */
|
||||
sh7305_intc_priority(INTC_ETMU_TUNI0, 7);
|
||||
sh7305_intc_priority(INTC_ETMU_TUNI1, 7);
|
||||
sh7305_intc_priority(INTC_ETMU_TUNI2, 7);
|
||||
sh7305_intc_priority(INTC_ETMU_TUNI3, 7);
|
||||
sh7305_intc_priority(INTC_ETMU_TUNI4, 7);
|
||||
sh7305_intc_priority(INTC_ETMU_TUNI5, 7);
|
||||
}
|
||||
|
||||
/* __tmu_hsave() : save hardware information */
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
VINLINE void r61524_clear_surface(struct dshader_surface *surface)
|
||||
{
|
||||
//TODO: check cache behaviour:
|
||||
// > ILRAM !!
|
||||
// > full xram then yram
|
||||
// > xram and yram
|
||||
// > yram and xram
|
||||
|
|
|
@ -81,15 +81,17 @@ static char *dtext_info_register(char const * const str)
|
|||
dtext_info.pool.text[i].size = 32;
|
||||
}
|
||||
}
|
||||
|
||||
len = strlen(str);
|
||||
if (len >= dtext_info.pool.text[dtext_info.pool.idx].size) {
|
||||
dtext_info.pool.text[dtext_info.pool.idx].size *= 2;
|
||||
dtext_info.pool.text[dtext_info.pool.idx].size = len;
|
||||
dtext_info.pool.text[dtext_info.pool.idx].raw = reallocarray(
|
||||
dtext_info.pool.text[dtext_info.pool.idx].raw,
|
||||
dtext_info.pool.text[dtext_info.pool.idx].size,
|
||||
sizeof(char)
|
||||
);
|
||||
}
|
||||
|
||||
strcpy(dtext_info.pool.text[dtext_info.pool.idx].raw, str);
|
||||
return dtext_info.pool.text[dtext_info.pool.idx].raw;
|
||||
}
|
||||
|
@ -98,6 +100,7 @@ static char *dtext_info_register(char const * const str)
|
|||
// Public API
|
||||
//---
|
||||
|
||||
|
||||
/* dtext_opt(): Display a string of text */
|
||||
did_t dtext_opt(
|
||||
int x, int y,
|
||||
|
|
|
@ -84,7 +84,7 @@ static void __timer_init(void)
|
|||
|
||||
struct vhex_driver *driver = vhex_driver_table();
|
||||
for (int i = 0; i < vhex_driver_count(); ++i) {
|
||||
if (driver[i].flags.DISPLAY) {
|
||||
if (driver[i].flags.TIMER) {
|
||||
memcpy(
|
||||
&timer_info.driver,
|
||||
driver[i].module_data,
|
||||
|
@ -110,3 +110,4 @@ struct vhex_module mod_timer = {
|
|||
.init = &__timer_init,
|
||||
.quit = &__timer_quit,
|
||||
};
|
||||
VHEX_DECLARE_MODULE(02, mod_timer);
|
||||
|
|
Loading…
Reference in New Issue