use GINT_CALL() in every API that has callbacks

* Change gint_inth_callback()
* Add intc_handler_function() to use C functions as handlers instead of
  writing assembler, and use it in the RTC and USB

* Revisit the TMU handlers, which after moving out the callbacks, now
  fit into 3 gates (great!), and adapt the ETMU handler
* Improve the timer driver (less code = better code, removed magic
  constants assuming the VBR layout on SH3/SH4, etc.)
* Remove 2 gates and a gap from the compact scheme on SH3

* Define timer_configure() to replace timer_setup(), which could not be
  cleanly updated to support GINT_CALL()
* Replace rtc_start/stop_timer with rtc_periodic_enable/disable, which
  is less confusing because of ETMU being "RTC timers"
This commit is contained in:
Lephe 2021-04-27 14:29:38 +02:00
parent 85d30fa59b
commit c37f150600
No known key found for this signature in database
GPG key ID: 1BBA026E13FC0495
19 changed files with 399 additions and 523 deletions

View file

@ -26,6 +26,7 @@ set(SOURCES_COMMON
src/dma/memcpy.c
src/dma/memset.c
src/intc/intc.c
src/intc/inth.s
src/kernel/exch.c
src/kernel/exch.s
src/kernel/hardware.c
@ -55,7 +56,6 @@ set(SOURCES_COMMON
src/render/dtext.c
src/render/dvline.c
src/render/topti.c
src/rtc/inth.s
src/rtc/rtc.c
src/rtc/rtc_ticks.c
src/spu/spu.c
@ -74,7 +74,6 @@ set(SOURCES_COMMON
src/tmu/tmu.c
src/usb/classes/ff-bulk.c
src/usb/configure.c
src/usb/inth.s
src/usb/pipes.c
src/usb/setup.c
src/usb/string.c

View file

@ -66,36 +66,27 @@ static GINLINE void *gint_inthandler(int code, void const *h, size_t size) {
return intc_handler(code, h, size);
}
/* gint_inth_callback(): Call back arbitrary code from an interrupt handler
/* gint_inth_callback(): Callback from interrupt handler to userland
Calls the specified function with the given argument after saving the user
context, enabling interrupts and going to user bank. This function is used
to call user code from interrupt handlers, typically from timer or RTC
callbacks. You can think of it as a way to escape the SR.BL=1 environment to
safely call back virtualized and interrupt-based functions during interrupt
handling.
This function performs an indirect call as with gint_call(), afters saving
the user context, enabling interrupts and going to user bank. This is useful
to call user code from interrupt handlers. You can think of it as a kernel-
space escape to virtualized userland during interrupt handling.
It is not safe to call from C code in any capacity and is mentioned here
only for documentation purposes; you should really only call this from
an interrupt handler's assembler code, typically like this:
This function can only be useful in an interrupt handler's assembler code.
It is loaded at a runtime-determined address and accessed through a function
pointer, like this:
mov.l .callback, r0
mov.l @r0, r0 # because function pointer
mov <function>, r4
mov <argument>, r5
mov <address of gint_call_t object>, r4
jsr @r0
nop
.callback:
.long _gint_inth_callback
This function is loaded to a platform-dependent address determined at
runtime; call it indirectly through the function pointer.
@callback Callback function, may take no argument in practice
@arg Argument
@call Address of a gint_call_t object
Returns the return value of the callback. */
extern int (*gint_inth_callback)(int (*function)(void *arg), void *arg);
extern int (*gint_inth_callback)(gint_call_t const *call);
#endif /* GINT_GINT */

View file

@ -117,4 +117,17 @@ int intc_priority(int intname, int level);
Returns the VBR address where the handler was installed. */
void *intc_handler(int event_code, void const *handler, size_t size);
/* intc_handler_function(): Install a function as an interrupt handler
This function can be used to install simple interrupt handlers. It installs
a pre-written interrupt handler that calls back the provided function.
Essentially it means that the interrupt handler can be written in C without
the numerous constraints of intc_handler(), at the cost of always going back
to userspace and a small time overhead.
@event_code Identifier of the interrupt block
@function Function to use as a handler
Returns true on success, false if the event code is invalid. */
bool intc_handler_function(int event_code, void (*function)(void));
#endif /* GINT_INTC */

View file

@ -6,6 +6,7 @@
#define GINT_RTC
#include <gint/defs/types.h>
#include <gint/defs/call.h>
#include <gint/timer.h>
//---
@ -43,7 +44,7 @@ void rtc_set_time(rtc_time_t const *time);
uint32_t rtc_ticks(void);
//---
// RTC timer
// RTC periodic interrupt
// The real-time clock produces a regular interrupt which may be used as a
// timer with a maximum frequency of 256 Hz. It is also useful to check
// that the clock settings (see <gint/clock.h>) are properly detected, by
@ -63,34 +64,36 @@ enum
RTC_NONE = 0,
};
/* rtc_start_timer(): Configure the RTC timer
/* rtc_periodic_enable(): Enable the periodic interrupt
This function sets up the RTC timer to invoke the provided callback function
with its argument regularly. This works like standard timers. The callback
is a function, the allowed types are listed in <gint/timer.h>. It may have
zero or one argument, and must return either TIMER_CONTINUE or TIMER_STOP.
This function sets up the periodic interrupt to invoke the provided callback
regularly. As with timers, the callback must return either TIMER_CONTINUE or
TIMER_STOP.
Do not confuse this "RTC timer" with Casio's added timers that run at
32768 Hz (which are called "RTC timers" in CPU73050.dll). These timers are
called Extra TMU in gint and are handled by <gint/timer.h>.
Do not confuse this interrupt with CASIO's extra timers that run at 32768 Hz
(which are called "RTC timers" in CPU73050.dll). These timers are called
Extra TMU or ETMU in gint and are handled by <gint/timer.h>.
Note that the timing of the first callback is always uncertain. A 1 Hz timer
set up when half of the current second is already elapsed will be called for
the first time after only 500 ms, for instance.
@freq Timer frequency
@callback Function to call back at the specified frequency
@... Up to one 4-byte argument for the callback
Fails and returns non-zero if the RTC timer is already in use. */
int rtc_start_timer(int freq, timer_callback_t callback, ...);
@frequency Periodic interrupt frequency
@callback Function to call back at the specified frequency
Returns true on success, false if the interrupt is already in use. */
bool rtc_periodic_enable(int frequency, gint_call_t callback);
/* Makes sure an argument is always provided, for va_arg() */
/* rtc_periodic_disable(): Stop the periodic interrupt
This has the same effect as returning TIMER_STOP from the callback, or
setting RTC_NONE as the parameter for rtc_periodic_enable(). */
void rtc_periodic_disable(void);
/* Deprecated versions with old-style callbacks and more confusing names */
__attribute__((deprecated("Use rtc_periodic_enable() instead")))
int rtc_start_timer(int frequency, timer_callback_t callback, ...);
#define rtc_start_timer(...) rtc_start_timer(__VA_ARGS__, 0)
/* rtc_stop_timer(): Stop the RTC timer
This function stops the RTC timer that was set up with rtc_start_timer(). If
the decision of stopping the timer comes from the callback, it is preferable
to return TIMER_STOP. */
__attribute__((deprecated("Use rtc_periodic_disable() instead")))
void rtc_stop_timer(void);
#endif /* GINT_RTC */

View file

@ -5,14 +5,15 @@
#ifndef GINT_TIMER
#define GINT_TIMER
#include <gint/defs/types.h>
#include <gint/mpu/tmu.h>
#include <gint/hardware.h>
#include <gint/defs/types.h>
#include <gint/defs/call.h>
/* Timer types and numbers
If you're new to timers, read this comment and then check timer_setup() and
timer_start(): most of the time you only need these.
If you're new to timers, read this comment and then check timer_configure()
timer_start(): most of the time you only need these.
There are two types of timers on the calculator: normal timers called TMU,
and extra timers added by Casio called ETMU. The main difference is that TMU
@ -23,7 +24,7 @@
* SH3-based fx9860g have 3 TMU (ID 0,1,2) and 1 ETMU (ID 3)
* SH4-based fx9860g and fxcg50 have 3 TMU (ID 0,1,2) and 6 ETMU (ID 3..8)
You can request "a" timer with timer_setup(), and gint will choose an
You can request "a" timer with timer_configure(), and gint will choose an
available timer depending on the precision you requested. Or, if you want
one specifically, you ask for an ID of your choice.
@ -33,13 +34,13 @@
is normally available for it unless you've used all TMUs at the same time
libprof also uses a TMU.
Most of the time you can just use timer_setup() and specify TIMER_ANY. If
you want to be sure that you have a TMU or an ETMU, you can do so by
Most of the time you can just use timer_configure() and specify TIMER_ANY.
If you want to be sure that you have a TMU or an ETMU, you can do so by
specifying TIMER_TMU or TIMER_ETMU. If you further want to have a specific
timer with specific settings, then you can:
* Set a specific ID in timer_setup(), in which case the delay is no longer
interpreter as count of µs, but as a TCOR value.
* Set a specific ID in timer_configure(), in which case the delay is no
longer interpreter as count of µs, but as a TCOR value.
* If this ID is a TMU, you can further add (with + or |) a prescaler
specification, one of TIMER_Pphi_{4,16,64,256}.
* Regardless of how the timer was obtained, you can use timer_reload() to
@ -48,7 +49,7 @@
priority levels of 13, 11, 9, and 7. The gray engine uses TMU0 to
guarantee maximum visual stability in the presence of interrupts.
In this module, timers are manipulated through their ID. timer_setup()
In this module, timers are manipulated through their ID. timer_configure()
returns the ID of a timer which was allocated to you. You can check it to
determine whether your timer is a TMU (0,1,2) or an ETMU (3 or more). */
@ -62,8 +63,8 @@
selects suitable speeds by default.
If you want something very particular, you can add (with + or |) a prescaler
value to a chosen ID in timer_setup() to request that specific value. The
default prescaler if the ID is fixed is TIMER_Pphi_4. */
value to a chosen ID in timer_configure() to request that specific value.
The default prescaler if the ID is fixed is TIMER_Pphi_4. */
enum {
TIMER_Pphi_4 = 0x00,
TIMER_Pphi_16 = 0x10,
@ -71,37 +72,13 @@ enum {
TIMER_Pphi_256 = 0x30,
};
/* Timer selection; see timer_setup() */
/* Timer selection; see timer_configure() */
enum {
TIMER_ANY = -1,
TIMER_TMU = -2,
TIMER_ETMU = -3,
};
/* Type of callback functions
This module used to require callbacks of type int (*)(volatile void *), but
callbacks are rarely this generic. Now timer_setup() accepts functions of
any of the types below. */
typedef union
{
/* No argument, returns either TIMER_CONTINUE or TIMER_STOP */
int (*v)(void);
/* Single integer argument */
int (*i)(int);
/* Single pointer argument, cv-qualified as needed */
int (*pv) (void *);
int (*pVv) (volatile void *);
int (*pCv) (const void *);
int (*pCVv)(volatile const void *);
/* Integer pointer argument, cv-qualified as needed */
int (*pi) (int *);
int (*pVi) (volatile int *);
int (*pCi) (const int *);
int (*pCVi)(volatile const int *);
} GTRANSPARENT timer_callback_t;
/* Return value for timer callbacks, indicating whether the timer should
continue running and fire again, or stop now */
enum {
@ -113,7 +90,7 @@ enum {
// Timer functions
//---
/* timer_setup(): Reserve and configure a timer
/* timer_configure(): Reserve and configure a timer
This function finds and configures a timer (without starting it). On
success, it returns the ID of the configured timer, which is used in all
@ -126,15 +103,15 @@ enum {
again after the same delay) or stop the timer.
The first argument specifies what kind of timer you want.
* TIMER_ANY will let timer_setup() choose any available timer. timer_setup()
will only use an ETMU if the delay is more than 0.1 ms to avoid resolution
issues. Most of the time this is what you need.
* TIMER_TMU or TIMER_ETMU will let timer_setup() choose an available TMU or
ETMU, respectively.
* TIMER_ANY will let timer_configure() choose any available timer.
timer_configure() will only use an ETMU if the delay is more than 0.1 ms
to avoid resolution issues. Most of the time this is what you need.
* TIMER_TMU or TIMER_ETMU will let timer_configure() choose an available TMU
or ETMU, respectively.
* Specifying an ID (0..8) will request exactly that timer. In this case, and
if the ID is a TMU (0,1,2), you may add (with + or |) a prescaler value to
specify the clock input. Otherwise the clock is set to Pphi/4.
If no timer matching the supplied settings is available, timer_setup()
If no timer matching the supplied settings is available, timer_configure()
returns -1.
The second argument is the delay. With TIMER_ANY, TIMER_TMU and TIMER_ETMU,
@ -142,28 +119,23 @@ enum {
is interpreted as a value of TCOR; see timer_delay() in this case. Note that
TCOR values are sensitive to the overclock level!
The third argument is a function to be called when the timer expires. It may
have a single 4-byte argument (int or pointer), in which case you should
provide the value to call it with as an optional argument (you can only use
a single argument). It must return an int equal to either TIMER_CONTINUE or
TIMER_STOP to control the subsequent operation of the timer. See the
definition of timer_callback_t above for the possible function types. If
this argument is NULL, a default function that stops the timer will be used.
The third argument is an indirect function call to be made when the timer
expires. You can create one with GINT_CALL(); the function must return an
int equal to either TIMER_CONTINUE or TIMER_STOP to control the subsequent
operation of the timer. Default calls are available in <gint/defs/call.h>
with common operations that are useful in timers. If GINT_CALL_NULL is
passed, a default function that stops the timer will be used.
On success, the configured timer becomes reserved; it can no longer be
returned by timer_setup() until:
returned by timer_configure() until:
* Either timer_stop() is called,
* Or the callback returns TIMER_STOP (which also stops the timer).
Remember than the returned timer is not started yet; see timer_start().
@timer Requested timer; TIMER_{ANY,TMU,ETMU} or an ID with prescaler
@delay Delay between each event, in µs unless first argument is an ID
@callback Function to be called when the timer fires
@... If the callback takes an argument, specify that value here */
int timer_setup(int timer, uint64_t delay_us, timer_callback_t callback, ...);
/* Makes sure an argument is always provided, for va_arg() */
#define timer_setup(...) timer_setup(__VA_ARGS__, 0)
@callback Function to be called when the timer fires */
int timer_configure(int timer, uint64_t delay_us, gint_call_t callback);
/* timer_start(): Start a configured timer
The specified timer will start counting down and call its callback function
@ -227,13 +199,41 @@ uint32_t timer_delay(int timer, uint64_t delay_us, int clock);
void timer_reload(int timer, uint32_t delay);
//---
// Predefined timer callbacks
// Deprecated API
//
// These types and functions were used in previous versions of gint but have
// been replaced. They are still here for compatibility until gint 3.
//---
/* Type of callback functions; a primitive predecessor to GINT_CALL() */
typedef union
{
/* No argument, returns either TIMER_CONTINUE or TIMER_STOP */
int (*v)(void);
/* Single integer argument */
int (*i)(int);
/* Single pointer argument, cv-qualified as needed */
int (*pv) (void *);
int (*pVv) (volatile void *);
int (*pCv) (const void *);
int (*pCVv)(volatile const void *);
/* Integer pointer argument, cv-qualified as needed */
int (*pi) (int *);
int (*pVi) (volatile int *);
int (*pCi) (const int *);
int (*pCVi)(volatile const int *);
} GTRANSPARENT timer_callback_t;
/* timer_setup(): Old variant of timer_configure() */
__attribute__((deprecated("Use timer_configure() instead")))
int timer_setup(int timer, uint64_t delay_us, timer_callback_t callback, ...);
/* Makes sure an argument is always provided, for va_arg() */
#define timer_setup(...) timer_setup(__VA_ARGS__, 0)
/* timer_timeout(): Callback that sets a flag and halts the timer
This predefined callback may be used when a timeout is required. It takes a
single argument which must be a pointer to int (or other integer type of 4
bytes) and increments it. */
Use GINT_CALL_SET_STOP() with timer_configure() instead. */
int timer_timeout(volatile void *arg);
#endif /* GINT_TIMER */

View file

@ -101,7 +101,8 @@ GCONSTRUCTOR static void gray_init(void)
}
/* Try to obtain the timer right away */
timer = timer_setup(GRAY_TIMER | GRAY_CLOCK, 1000, gray_int);
timer = timer_configure(GRAY_TIMER | GRAY_CLOCK, 1000,
GINT_CALL(gray_int));
/* On failure, release the resources that we obtained */
if(!gray_isinit()) gray_quit();

View file

@ -82,19 +82,16 @@ static struct info {
The _inth_remap table in src/kernel/inth.S combines the SH3-SH4 translation
with the compact translation, hence its entry for 0xf00 (the SH3 event code
for ETMU0 underflow) is the offset in this table where 0x9e0 (the SH4 event
code for the same event) is stored, which is 4. */
code for the same event) is stored, which is 3. */
static const uint16_t sh3_vbr_map[] = {
0x400, /* TMU0 underflow */
0x420, /* TMU1 underflow */
0x440, /* TMU2 underflow */
0x460, /* (gint custom: TMU helper) */
0x9e0, /* ETMU0 underflow */
0xd00, /* ETMU4 underflow (used as helper on SH3) */
0xd20, /* (gint custom: ETMU helper) */
0xd40, /* (gint custom: ETMU helper) */
0xd00, /* ETMU logic #1 (ETMU4 underflow) */
1, /* ETMU logic #2 */
1, /* ETMU logic #3 */
0xaa0, /* RTC Periodic Interrupt */
1, /* (Filler to maintain the gap between 0xaa0 and 0xae0) */
0xae0, /* (gint custom: RTC helper) */
0
};
@ -173,6 +170,19 @@ void *intc_handler(int event_code, const void *handler, size_t size)
return memcpy(dest, handler, size);
}
bool intc_handler_function(int event_code, void (*function)(void))
{
/* Install the genric handler */
extern void intc_generic_handler(void);
void *h = intc_handler(event_code, intc_generic_handler, 32);
if(!h) return false;
/* Set the function to be called */
*(void **)(h + 24) = function;
return true;
}
//---
// State and driver metadata
//---

33
src/intc/inth.s Normal file
View file

@ -0,0 +1,33 @@
/* gint.intc.inth: Standard interrupt handler
This is a generic interrupt handler that calls back into a C function,
useful for complex handling or simple drivers that benefit more from
simplicity than razor-sharp performance. */
.global _intc_generic_handler /* 32 bytes */
.section .gint.blocks, "ax"
.align 4
_intc_generic_handler:
/* Create a GINT_CALL() object with no arguments */
sts.l pr, @-r15
add #-20, r15
mov.l .function, r0
mov.l r0, @r15
/* Call back to the function in userspace */
mov.l .gint_inth_callback, r0
mov.l @r0, r0
jsr @r0
mov r15, r4
add #20, r15
lds.l @r15+, pr
rts
nop
.function:
.long 0 /* Function, set when handler is installed */
.gint_inth_callback:
.long _gint_inth_callback

View file

@ -160,9 +160,9 @@ _gint_inth_7705:
VBR offset SH3 events Description
-------------------------------------------------------------------
0x200 400 420 440 --- TMU0, TMU1, TMU2 and a helper
0x280 f00 --- --- --- ETMU0, ETMU1, ETMU2 and a helper
0x300 4a0 [ ] --- RTC Periodic Interrupt, gap and helper
0x200 400 420 440 TMU0, TMU1, TMU2
0x260 f00 --- --- --- ETMU0, 3-gate logic at ETMU4
0x2e0 4a0 RTC Periodic Interrupt
-------------------------------------------------------------------
0x600 --- --- Entry gate
-------------------------------------------------------------------
@ -171,7 +171,7 @@ _gint_inth_7705:
the interrupt entry gate at VBR + 0x640. */
_inth_remap:
.byte 0, 1, 2, 0xff, 0xff, 8, 0xff, 0xff
.byte 0, 1, 2, 0xff, 0xff, 7, 0xff, 0xff
.byte 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
.byte 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
.byte 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
@ -182,10 +182,12 @@ _inth_remap:
.byte 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
.byte 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
.byte 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
.byte 4, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
.byte 3, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
#endif
.section .gint.mapped, "ax"
/* CALLBACK HELPER
This function implements the callback with context saving. It is a general
function and does not need to reside in VBR space which is block-structured.
@ -193,15 +195,9 @@ _inth_remap:
switches back to user bank and executes the callback. It does not save other
registers (pr/mach/macl/gbr) which are managed by the handler entry. */
/* gint_inth_callback()
Calls the specified function with the given argument after saving the user
context, enabling interrupts and going to user bank.
@r4 Callback function (volatile void * -> int)
@r5 Argument (volatile void *)
Returns the return value of the callback (int). */
.section .gint.mapped, "ax"
/* gint_inth_callback: Indirect call from kernel space to userland
@r4 Address of callback function
-> Returns the return value of the callback (int). */
_gint_inth_callback_reloc:
stc.l r0_bank, @-r15
stc.l r1_bank, @-r15
@ -218,7 +214,6 @@ _gint_inth_callback_reloc:
/* Save some values to user bank; once we enable interrupts, the kernel
bank might be overwritten at any moment. */
ldc r4, r0_bank
ldc r5, r4_bank
/* Enable interrupts and go back to user bank. On SH4, SR.IMASK is set
to the level of the current interrupt, which makes sure we can only
@ -248,10 +243,15 @@ _gint_inth_callback_reloc:
.load_sr:
ldc r1, sr
/* We are now in the user bank with r0 and r4 set. Call back. We want
/* We are now in the user bank with r0 set. Perform the call. We want
to forward the return value to kernel bank, but this bank can be
changed at any moment since interrupts are enabled. */
sts.l pr, @-r15
mov.l @(4, r0), r4
mov.l @(8, r0), r5
mov.l @(12, r0), r6
mov.l @(16, r0), r7
mov.l @r0, r0
jsr @r0
nop
lds.l @r15+, pr

View file

@ -167,7 +167,8 @@ static void configure(void)
getkey_repeat(400, 40);
/* The timer will be stopped when the timer driver is unloaded */
keysc_tid = timer_setup(TIMER_ANY, scan_frequency_us, keysc_tick);
keysc_tid = timer_configure(TIMER_ANY, scan_frequency_us,
GINT_CALL(keysc_tick));
if(keysc_tid >= 0) timer_start(keysc_tid);
gint[HWKBD] = HW_LOADED | (isSH3() ? HWKBD_IO : HWKBD_KSI);

View file

@ -1,62 +0,0 @@
/*
** gint:rtc:inth - Interrupt handler for the Real-Time Clock
** This one is fairly simple, just a flag to clear and potentially a timer to
** stop if the callback returns non-zero.
*/
.global _inth_rtc_pri
.global _inth_rtc_pri_helper
.section .gint.blocks, "ax"
.align 4
/* RTC PERIODIC INTERRUPT HANDLER - 56 BYTES */
_inth_rtc_pri:
/* Invoke the callback function with its argument */
sts.l pr, @-r15
mov.l .gint_inth_callback, r0
mov.l @r0, r0
mov.l 1f, r4
mov.l 2f, r5
jsr @r0
nop
/* Jump to another gate to finish the work:
- 0xc is the size of storage below
- 0x20 is the size of the gap before next gate (alarm interrupt) */
mov #0x2c, r2
braf r2
nop
1: .long 0 /* Callback function: edited dynamically */
2: .long 0 /* Argument to callback function */
.gint_inth_callback:
.long _gint_inth_callback
_inth_rtc_pri_helper:
/* Save the return value */
mov r0, r3
.clear:
/* Clear the interrupt flag */
mov.l .RCR2, r1
mov.b @r1, r0
tst #0x80, r0
and #0x7f, r0
bf.s .clear
mov.b r0, @r1
/* Stop the timer if the return value of the callback was non-zero */
tst r3, r3
bt .end
and #0x8f, r0
mov.b r0, @r1
.end:
lds.l @r15+, pr
rts
nop
.RCR2: .long 0xa413fede /* RCR2 address, edited at startup on SH3 */

View file

@ -13,19 +13,9 @@
#include <stdarg.h>
#undef rtc_start_timer
//---
// Real-Time Clock peripheral registers
//---
/* RTC address on SH7305, adjusted at startup on SH7337 and SH7355 */
static rtc_t *RTC = &SH7305_RTC;
/* Address of interrupt handler parameters */
GBSS static struct {
void *function;
uint32_t arg;
} GPACKED(4) *timer_params;
//---
// Time management
@ -94,42 +84,67 @@ void rtc_set_time(rtc_time_t const *time)
}
//---
// RTC timer
// RTC periodic interrupt
//---
/* rtc_start_timer(): Configure the RTC timer */
int rtc_start_timer(int freq, timer_callback_t f, ...)
{
/* Refuse to override an existing callback */
if(RTC->RCR2.PES != RTC_NONE) return 1;
if(freq == RTC_NONE) return 1;
static gint_call_t rtc_periodic_callback;
/* Get the argument */
va_list args;
va_start(args, f);
uint32_t arg = va_arg(args, uint32_t);
va_end(args);
bool rtc_periodic_enable(int frequency, gint_call_t callback)
{
/* Refuse to override an existing interrupt */
if(RTC->RCR2.PES != RTC_NONE) return false;
if(frequency == RTC_NONE)
{
rtc_periodic_disable();
return true;
}
/* Temporarily disable the interrupt and set up the callback */
RTC->RCR2.PES = RTC_NONE;
timer_params->function = f.v;
timer_params->arg = arg;
rtc_periodic_callback = callback;
/* Clear the interrupt flag */
do RTC->RCR2.PEF = 0;
while(RTC->RCR2.PEF);
/* Enable the interrupt */
RTC->RCR2.PES = freq;
return 0;
RTC->RCR2.PES = frequency;
return true;
}
/* rtc_stop_timer(): Stop the RTC timer */
void rtc_stop_timer(void)
void rtc_periodic_interrupt(void)
{
int rc = gint_call(rtc_periodic_callback);
/* Clear the interrupt flag */
do RTC->RCR2.PEF = 0;
while(RTC->RCR2.PEF);
/* Stop the interrupt if the callback returns non-zero */
if(rc) rtc_periodic_disable();
}
void rtc_periodic_disable(void)
{
RTC->RCR2.PES = RTC_NONE;
}
/* Deprecated versions */
#undef rtc_start_timer
int rtc_start_timer(int frequency, timer_callback_t function, ...)
{
va_list args;
va_start(args, function);
uint32_t arg = va_arg(args, uint32_t);
va_end(args);
return !rtc_periodic_enable(frequency, GINT_CALL(function.v, arg));
}
void rtc_stop_timer(void)
{
return rtc_periodic_disable();
}
//---
// Driver initialization
//---
@ -148,19 +163,8 @@ static void configure(void)
/* Clear the periodic interrupt flag */
RTC->RCR2.PEF = 0;
/* Interrupt handlers provided by rtc/inth.s */
extern void inth_rtc_pri(void);
extern void inth_rtc_pri_helper(void);
/* Install the RTC interrupt handler */
GUNUSED void *h0, *h1;
h0 = intc_handler(0xaa0, inth_rtc_pri, 32);
h1 = intc_handler(0xae0, inth_rtc_pri_helper, 32);
timer_params = h0 + 20;
volatile uint8_t **RCR2_pointer = h1 + 28;
*RCR2_pointer = &RTC->RCR2.byte;
intc_handler_function(0xaa0, rtc_periodic_interrupt);
/* Disable the RTC interrupts for now. Give them priority 1; higher
priorities cause freezes when going back to the system on SH3

View file

@ -1,80 +1,77 @@
/*
** gint:tmu:inth-etmu - Interrupt handlers for the RTC-bound timers
** gint:tmu:inth-etmu - Interrupt handlers for the RTC-bound timers
**
** This handler uses 3 consecutive blocks like the TMU handler. However this
** time 2 empty blocks after ETMU4 (0xd20, 0xd40) are used because blocks for
** ETMU are not consecutive in memory.
**
** It would be possible to communicate between any interrupt handlers in non-
** consecutive gates. A simple way is to store at runtime a pointer to the
** desired object in one handler. But that costs a lot of space. If the
** relative position of interrupt handlers is known, the best option left is
** the unnatural @(disp,pc) addressing mode, and it doesn't even work with the
** SH3's compact VBR scheme.
*/
/* Gates for the extra timers (informally called ETMU) */
.global _inth_etmu4
.global _inth_etmux
.global _inth_etmu4 /* 96 bytes */
.global _inth_etmux /* 32 bytes */
.section .gint.blocks, "ax"
.align 4
/* EXTRA TMU INTERRUPT HANDLERS - 96 BYTES
To implement the same functionalities as the standard timers, several blocks
are once again needed. This time, 2 empty blocks after ETMU4 (0xd20, 0xd40)
are used for convenience.
It would be possible to communicate between any interrupt handlers in non-
consecutive gates. A simple way is to store at runtime a pointer to the
desired object in one handler. But that costs a lot fo space. If the
relative position of interrupt handlers is known, the best option left is
the unnatural @(disp,pc) addressing mode, and it doesn't even work with the
SH3's compact VBR scheme. */
/* FIRST GATE - ETMU4 and two empty blocks */
/* 3-block handler installed at the ETMU4 gate. */
_inth_etmu4:
mova .storage_etmu4, r0
mov #7, r2
.shared:
mov.l r2, @-r15
mov.l r8, @-r15
sts.l pr, @-r15
mov r0, r1
/* Prepare an indirect call to timer_stop(<id>) */
add #-20, r15
mov.l r2, @(4, r15)
/* Clear interrupt flag in TCR */
mov.l @(8, r1), r3
1:
mov.b @r3, r0
mov r0, r1
mov.l @(4, r1), r3
1: mov.b @r3, r0
tst #0x02, r0
and #0xfd, r0
bf/s 1b
mov.b r0, @r3
/* Prepare invoking the callback function */
/* Invoke callback */
mov.l .gint_inth_callback, r8
mov.l @r8, r8
mov.l @r1, r4
jsr @r8
mov.l @(4, r1), r5
mov.l @r1, r4
tst r0, r0
bt 2f
/* Invoke callback; if return value is non-zero, stop timer */
mov.l .timer_stop, r4
/* If return value is non-zero, stop the timer with another callback */
mov.l .timer_stop, r0
mov.l r0, @r15
jsr @r8
mov.l @(8, r15), r5
mov r15, r4
/* Clear the flag and possibly stop the timer */
2:
2: add #20, r15
lds.l @r15+, pr
mov.l @r15+, r8
rts
add #4, r15
mov.l @r15+, r8
.zero 24
.zero 26
.gint_inth_callback:
.long _gint_inth_callback
.timer_stop:
.long _timer_stop
.gint_inth_callback:
.long _gint_inth_callback
.storage_etmu4:
.long 0 /* Callback: Configured dynamically */
.long 0 /* Argument: Configured dynamically */
.long 0 /* TCR: Configured dynamically */
.long _tmu_callbacks + 140
.long 0xa44d00bc /* RTCR4 */
/* SECOND GATE - All other ETMU entries, falling back to ETMU2 */
/* Generic gate for all other ETMU handlers, falling back to ETMU4. */
_inth_etmux:
/* Dynamically compute the target of the jump */
stc vbr, r3
@ -85,20 +82,15 @@ _inth_etmux:
mov.w .id_etmux, r2
jmp @r3
nop
nop
nop
.id_etmux:
.word 0 /* Timer ID */
.word 0 /* Timer ID */
/* Offset from VBR where extra timer 2 is located:
* 0x600 to reach the interrupt handlers
* 0x040 to jump over the entry gate
* 0x900 to reach the handler of ETMU4
* Skip over the first instructions
This is different on SH3 due to the compact scheme so it's edited
dynamically at install time. */
1: .long 0xf40 + (.shared - _inth_etmu4)
/* Offset from VBR where ETMU4 is located; set during configure */
1: .long (.shared - _inth_etmu4)
.storage_etmux:
.long 0 /* Callback: Configured dynamically */
.long 0 /* Argument: Configured dynamically */
.long 0 /* TCR: Configured dynamically */
.long _tmu_callbacks
.long 0 /* TCR address */

View file

@ -1,121 +1,94 @@
/*
** gint:tmu:inth-tmu - Interrupt handlers for the timer units
** Perhaps the most technical of my interrupt handlers. They implement a
** simple kind of interrupt handler communication by letting the control flow
** from each interrupt handler to the next.
** gint:tmu:inth-tmu - Interrupt handlers for the timer units
**
** This handler consists of 3 consecutive gates that operate as a block. It
** clears the interrupt flags, invokes a GINT_CALL() in userspace, and stops
** the timer if the callback returns non-zero.
**
** It is important to notice that the code of the gates is continuous in this
** file and thus must be continuous in memory, as the assembler will use
** relative addressing methods. This "block operations" is only possible for
** handlers that are mapped to consecutive event codes.
*/
/* Gates for the standard Timer Unit (TMU) */
.global _inth_tmu /* 128 bytes */
.global _inth_tmu /* 96 bytes */
.section .gint.blocks, "ax"
.align 4
/* TMU INTERRUPT HANDLERS - 128 BYTES
Unfortunately I did not manage to write a handler that cleared the interrupt
flag and invoked a callback in less than 34 bytes data included. So I
decided to make several gates operate as a whole and add a bit more features
in them. Basically, these handlers:
- Clear the interrupt flag
- Invoke a callback function and pass it a user-provided argument
- Stop the timer if the callback returns non-zero
- Host their own callback pointers and arguments
It is important to notice that the code of the following gates looks like
they are contiguous in memory. The assembler will make that assumption, and
turn any address reference between two gates into a *relative displacement*.
If the gates don't have the same relative location at runtime, the code will
crash because we will have broken the references. This is why we can only do
it with handlers that are mapped to consecutive event codes. */
/* TMU0 entry and interrupt flag clearing. */
_inth_tmu:
mov #0, r5
mov #0, r6
mov #0, r7
/* FIRST GATE - TMU0 entry, clear underflow flag and call back */
_inth_tmu_0:
mova .storage0, r0
mov #0, r1
/*** This is the first shared section ***/
.shared1:
mov.l .TCR0, r1
add r6, r1
/* Save the timer ID on the stack */
mov.l r8, @-r15
sts.l pr, @-r15
mov.l r1, @-r15
mov.l r5, @-r15
/* Load the TCR address */
mov.l .mask, r3
not r3, r4
mov.l @(8, r0), r1
/* Clear the interrupt flag. Because r5 contains 0, 1 or 2 the 16 top
bits are 0 so we can compare without extending */
1: mov.w @r1, r5
extu.b r5, r3
cmp/eq r5, r3
bf/s 1b
mov.w r3, @r1
/* Clear the interrupt flag */
1: mov.w @r1, r2
tst r4, r2
and r3, r2
mov.w r2, @r1
bf 1b
/* Prepare callback and jump to second section */
mov.l .gint_inth_callback, r8
/* Prepare to run the callback */
mov.l .callback, r8
bra .shared2
mov.l @r8, r8
/* SECOND GATE - TMU1 entry and stop timer */
/* TMU1 entry, callback and timer stop logic. */
_inth_tmu_1:
mova .storage1, r0
mov #1, r5
mov #12, r6
bra .shared1
mov #1, r1
mov #20, r7
/*** This is the second shared section ***/
.shared2:
/* Invoke callback */
mov.l @r0, r4
mov.l .tmu_callbacks, r4
jsr @r8
mov.l @(4, r0), r5
/* Stop the timer if the return value is not zero */
mov.l @r15+, r5
add r7, r4
tst r0, r0
bt 2f
mov.l .timer_stop, r4
mov.l .timer_stop, r2
bt/s .shared3
mov.l r2, @-r15
/* Stop the timer if the return value is not zero. We use the top of
the stack as a gint_call_t object; only the function and first
argument matter, timer_stop() will ignore the rest. */
jsr @r8
mov r15, r4
bra .shared3
nop
nop
2:
/* TMU2 entry, shared exit and storage. */
_inth_tmu_2:
mov #2, r5
mov #24, r6
bra .shared1
mov #40, r7
.shared3:
add #8, r15
lds.l @r15+, pr
rts
mov.l @r15+, r8
.zero 2
/* THIRD GATE - TMU2 entry and storage for TMU0 */
_inth_tmu_2:
mova .storage2, r0
bra .shared1
mov #2, r1
.zero 10
.gint_inth_callback:
.long _gint_inth_callback
.storage0:
.long 0 /* Callback: Configured dynamically */
.long 0 /* Argument: Configured dynamically */
.long 0xa4490010 /* TCR0: Overridden at startup on SH3 */
/* FOURTH GATE - Storage for TMU1, TMU2 and other values */
_inth_tmu_storage:
.mask:
.long 0xfffffeff
.timer_stop:
.long _timer_stop
.storage1:
.long 0 /* Callback: Configured dynamically */
.long 0 /* Argument: Configured dynamically */
.long 0xa449001c /* TCR1: Overridden at startup on SH3 */
.storage2:
.long 0 /* Callback: Configured dynamically */
.long 0 /* Argument: Configured dynamically */
.long 0xa4490028 /* TCR2: Overridden at startup on SH3 */
.callback:
.long _gint_inth_callback
.TCR0:
.long 0xa4490010
.tmu_callbacks:
.long _tmu_callbacks

View file

@ -9,7 +9,8 @@ static void do_sleep(uint64_t delay_us, int spin)
{
volatile int flag = 0;
int timer = timer_setup(TIMER_ANY, delay_us, timer_timeout, &flag);
int timer = timer_configure(TIMER_ANY, delay_us,
GINT_CALL_SET_STOP(&flag));
if(timer < 0) return;
timer_start(timer);

View file

@ -7,40 +7,29 @@
#include <gint/drivers/states.h>
#include <gint/clock.h>
#include <gint/intc.h>
#include <gint/cpu.h>
#include <gint/mpu/tmu.h>
#include <stdarg.h>
#undef timer_setup
//---
// Driver storage
//---
/* inth_data_t - data storage inside interrupt handlers */
typedef struct
{
void *function; /* User-provided callback */
uint32_t arg; /* Argument for function */
volatile void *TCR; /* TCR address for TMU */
} GPACKED(4) inth_data_t;
/* This array references the storage areas of all timer handlers */
static inth_data_t *timers[9] = { NULL };
/* Callbacks for all timers */
gint_call_t tmu_callbacks[9];
/* Arrays of standard and extra timers */
static tmu_t *TMU = SH7305_TMU.TMU;
static etmu_t *ETMU = SH7305_ETMU;
/* TSTR register for standard timers */
static volatile uint8_t *TSTR = &SH7305_TMU.TSTR;
/* Shortcut to set registers that are slow to update */
#define set(lval, rval) do(lval = rval); while(rval != lval)
//---
// Local functions
//---
/* conf(): Configure a fixed timer */
static void conf(int id, uint32_t delay, int clock, void *f, uint32_t arg)
static void conf(int id, uint32_t delay, int clock, gint_call_t call)
{
if(id < 3)
{
@ -52,8 +41,7 @@ static void conf(int id, uint32_t delay, int clock, void *f, uint32_t arg)
T->TCOR = delay;
T->TCNT = delay;
T->TCR.TPSC = clock;
do T->TCR.UNF = 0;
while(T->TCR.UNF);
set(T->TCR.UNF, 0);
/* Enable interrupt and count on rising edge (SH7705) */
T->TCR.UNIE = 1;
@ -64,22 +52,14 @@ static void conf(int id, uint32_t delay, int clock, void *f, uint32_t arg)
etmu_t *T = &ETMU[id-3];
if(T->TCR.UNIE) return;
/* No clock input and clock edge here. But TCR and TCNT need
some time to execute the write */
do T->TCR.UNF = 0;
while(T->TCR.UNF);
do T->TCOR = delay;
while(T->TCOR != delay);
do T->TCNT = delay;
while(T->TCNT != delay);
/* No clock input and clock edge here */
set(T->TCR.UNF, 0);
set(T->TCOR, delay);
set(T->TCNT, delay);
T->TCR.UNIE = 1;
}
timers[id]->function = f;
timers[id]->arg = arg;
tmu_callbacks[id] = call;
}
/* matches(): Check if a timer matches the provided specification and delay */
@ -99,8 +79,7 @@ static int matches(int id, int spec, uint32_t delay)
/* available(): Check if a timer is available (UNIE cleared, not running) */
static int available(int id)
{
/* The timer should also be installed... */
if(!timers[id]) return 0;
if(id >= timer_count()) return 0;
if(id < 3)
{
@ -124,19 +103,12 @@ static int stop_callback(void)
// Timer API
//---
/* timer_setup(): Reserve and configure a timer */
int timer_setup(int spec, uint64_t delay, timer_callback_t function, ...)
int timer_configure(int spec, uint64_t delay, gint_call_t call)
{
int clock = 0;
/* Get the optional argument */
va_list va;
va_start(va, function);
uint32_t arg = va_arg(va, uint32_t);
va_end(va);
/* Default value for the callback */
if(!function.v) function.v = stop_callback;
/* Default behavior for the callback */
if(!call.function) call = GINT_CALL(stop_callback);
/* Find a matching timer, starting from the slowest timers with the
smallest interrupt priorities all the way up to TMU0 */
@ -169,7 +141,7 @@ int timer_setup(int spec, uint64_t delay, timer_callback_t function, ...)
/* Find the delay constant for that timer and clock */
if(spec < 0) delay = timer_delay(id, delay, clock);
conf(id, delay, clock, function.v, arg);
conf(id, delay, clock, call);
return id;
}
@ -196,10 +168,6 @@ uint32_t timer_delay(int id, uint64_t delay_us, int clock)
freq = 32768;
}
/* fxcg50: Calculated = 29491200 but it's too low */
/* TODO: Account for down spread spectrum in the CPG */
// uint64_t freq = 29020000 >> 2;
uint64_t product = freq * delay_us;
return product / 1000000;
}
@ -246,18 +214,13 @@ void timer_stop(int id)
}
else
{
/* Extra timers generate interrupts when TCNT=0 even if TSTR=0.
We always keep TCOR/TCNT to non-zero values when idle. */
etmu_t *T = &ETMU[id-3];
T->TCR.UNIE = 0;
/* Also clear TCOR and TCNT to avoid spurious interrupts */
do T->TCOR = 0xffffffff;
while(T->TCOR + 1);
do T->TCNT = 0xffffffff;
while(T->TCNT + 1);
do T->TCR.UNF = 0;
while(T->TCR.UNF);
set(T->TCOR, 0xffffffff);
set(T->TCNT, 0xffffffff);
set(T->TCR.UNF, 0);
}
}
@ -293,10 +256,20 @@ void timer_spinwait(int id)
}
//---
// Predefined timer callbacks
// Deprecated API
//---
/* timer_timeout() - callback that sets a flag and halts the timer */
#undef timer_setup
int timer_setup(int spec, uint64_t delay, timer_callback_t function, ...)
{
va_list va;
va_start(va, function);
uint32_t arg = va_arg(va, uint32_t);
va_end(va);
return timer_configure(spec, delay, GINT_CALL(function.v, arg));
}
int timer_timeout(void volatile *arg)
{
int volatile *x = arg;
@ -308,7 +281,7 @@ int timer_timeout(void volatile *arg)
// Driver initialization
//---
/* Interrupt handlers for standard timers (4 gates) */
/* Interrupt handlers for standard timers (3 gates) */
extern void inth_tmu(void);
/* Interrupt handlers for extra timers */
extern void inth_etmu4(void);
@ -329,65 +302,44 @@ static void configure(void)
uint16_t etmu_event[6] = { 0x9e0, 0xc20, 0xc40, 0x900, 0xd00, 0xfa0 };
*TSTR = 0;
/* Install the standard TMU's interrupt handlers */
void *h = intc_handler(0x400, inth_tmu, 128);
timers[0] = h + 84;
timers[1] = h + 104;
timers[2] = h + 116;
/* Install the TMU handlers and adjust the TCR0 value on SH3 */
void *h = intc_handler(0x400, inth_tmu, 96);
if(isSH3()) *(void volatile **)(h + 88) = &TMU[0].TCR;
/* Clear every timer to avoid surprises */
for(int id = 0; id < 3; id++)
/* Clear all timers */
for(int i = 0; i < 3; i++)
{
do TMU[id].TCR.word = 0;
while(TMU[id].TCR.word);
TMU[id].TCOR = 0xffffffff;
TMU[id].TCNT = 0xffffffff;
/* Standard timers: TCR is provided to the interrupt handler */
timers[id]->TCR = &TMU[id].TCR;
set(TMU[i].TCR.word, 0);
TMU[i].TCOR = 0xffffffff;
TMU[i].TCNT = 0xffffffff;
}
for(int id = 0; id < timer_count()-3; id++)
for(int i = 3; i < timer_count(); i++)
{
etmu_t *T = &ETMU[id];
/* Extra timers seem to generate interrupts as long as TCNT=0,
regardless of TSTR. I'm not entirely sure about this weird
behaviour, but for safety I'll set TCOR/TCNT to non-zero. */
etmu_t *T = &ETMU[i-3];
T->TSTR = 0;
do T->TCOR = 0xffffffff;
while(T->TCOR + 1);
/* Also TCNT and TCR take some time to record changes */
do T->TCNT = 0xffffffff;
while(T->TCNT + 1);
do T->TCR.byte = 0;
while(T->TCR.byte);
set(T->TCOR, 0xffffffff);
set(T->TCNT, 0xffffffff);
set(T->TCR.byte, 0);
}
/* Install the extra timers. On SH3, only ETMU0 is available */
/* Install the ETMU4 handler, which contains the logic for ETMUs */
void *h4 = intc_handler(etmu_event[4], inth_etmu4, 96);
/* Install the other ETMU handlers, and set their parameters */
for(int i = 3; i < timer_count(); i++) if(i != 7)
{
void *h = intc_handler(etmu_event[i-3], inth_etmux, 32);
timers[i] = h + 20;
/* On SH3, the ETMU handler is not at an offset of 0x900 (event
code 0xd00) but at an offset of 0xa0 */
uint32_t *etmu_offset = h + 16;
if(isSH3()) *etmu_offset = *etmu_offset - 0xf40 + 0x2a0;
uint16_t *data_id = h + 14;
*data_id = i;
uint32_t *TCR = h + 28;
*TCR = (uint32_t)&ETMU[i-3].TCR;
/* Distance from VBR handler to ETMU4, used to jump */
*(uint32_t *)(h + 20) += (uint32_t)h4 - cpu_getVBR();
/* Timer ID, used for timer_stop() after the callback */
*(uint16_t *)(h + 18) = i;
/* Pointer to the callback */
*(gint_call_t **)(h + 24) += i;
/* TCR address to acknowledge the interrupt */
*(void volatile **)(h + 28) = &ETMU[i-3].TCR;
}
/* Also install ETMU4, even on SH3, because it contains common code */
h = intc_handler(etmu_event[4], inth_etmu4, 96);
timers[7] = h + 84;
*(uint32_t *)(h + 92) = (uint32_t)&ETMU[4].TCR;
/* Enable TMU0 at level 13, TMU1 at level 11, TMU2 at level 9 */
intc_priority(INTC_TMU_TUNI0, 13);
intc_priority(INTC_TMU_TUNI1, 11);
@ -448,16 +400,10 @@ static void hrestore(tmu_state_t const *s)
struct tmu_state_stored_timer const *c = &s->t[i];
etmu_t *T = &ETMU[i-3];
do T->TCOR = c->TCOR;
while(T->TCOR != c->TCOR);
set(T->TCOR, c->TCOR);
T->TSTR = c->TSTR;
do T->TCNT = c->TCNT;
while(T->TCNT != c->TCNT);
do T->TCR.byte = c->TCR;
while(T->TCR.byte != c->TCR);
set(T->TCNT, c->TCNT);
set(T->TCR.byte, c->TCR);
}
*TSTR = s->TSTR;

View file

@ -1,29 +0,0 @@
/*
** gint:usb:inth - Interrupt handler for the USB function module
*/
.global _inth_usb
.section .gint.blocks, "ax"
.align 4
/* USB INTERRUPT HANDLER */
_inth_usb:
/* Call back into driver code, there is way too much work that needs to
be done to fit in here */
sts.l pr, @-r15
mov.l 1f, r0
mov.l @r0, r0
mov.l 2f, r4
jsr @r0
nop
lds.l @r15+, pr
rts
nop
.zero 6
1: .long _gint_inth_callback
2: .long _usb_interrupt_handler

View file

@ -10,8 +10,7 @@
#define USB SH7305_USB
/* Interrupt handler */
extern void inth_usb(void);
static void usb_interrupt_handler(void);
/* Shorthand to clear a bit in INTSTS0 */
#define INTSTS0_clear(field_name) { \
@ -165,7 +164,7 @@ int usb_open(usb_interface_t const **interfaces, gint_call_t callback)
USB.NRDYENB.word = 0x0000;
USB.BEMPENB.word = 0x0000;
intc_handler(0xa20, inth_usb, 32);
intc_handler_function(0xa20, usb_interrupt_handler);
intc_priority(INTC_USB, 15);
usb_open_status = true;
@ -191,7 +190,7 @@ void usb_close(void)
// Userspace interrupt handler
//---
void usb_interrupt_handler(void)
static void usb_interrupt_handler(void)
{
static char const * const device_st[] = {
"powered", "default", "address", "configured",

View file

@ -205,7 +205,8 @@ void usb_pipe_init_transfers(void);
/* usb_while(): A while loop with a timeout */
#define usb_while(condition) ({ \
volatile int __f = 0; \
int __t = timer_setup(TIMER_ANY, 100000 /*µs*/, timer_timeout, &__f); \
int __t = timer_configure(TIMER_ANY, 100000 /*µs*/, \
GINT_CALL_SET_STOP(&__f)); \
if(__t >= 0) timer_start(__t); \
while((condition) && __f == 0) {} \
if(__f) usb_log("%s: %d: (" #condition ") holds\n", \