In some cases, kernel callouts need access to some static read/write storage, in
particular to be able to share information with other kernel callouts.
Because kernel callout code is position-independent (see Common characteristics), it cannot
have static read/write storage. However, you can assign a small amount a memory at
the end of the system page to a kernel callout (or multiple kernel callouts) to use as
read/write storage.
You can use the CALLOUT_START macro's second argument to specify the
address of a four-byte variable that contains the amount of read/write storage (in
bytes) the kernel callout needs. For example, in the code snippet below:
- rw_interrupt has the address of the location with the number
of bytes of storage required.
- Since the contents at the location referenced by rw_interrupt
is 4, the startup library allocates four bytes at the end of
the system page.
- The library uses the patcher routine's rw_offset argument to
pass to the patcher the offset to this allocated memory (see Patcher routines).
- The patcher routine then modifies the initial instruction of the kernel callout
to the appropriate offset, so that while the callout is executing, the
t3 register contains a pointer to the read/write
storage.
Here is the code snippet:
rw_interrupt:
.long 4
patch_interrupt:
add a1,a1,a2
j ra
sh a3,0+LOW16(a1)
/*
* Mask the specified interrupt
*/
CALLOUT_START(interrupt_mask_my_board, rw_interrupt, patch_interrupt)
/*
* Input Parameters :
* a0 - syspage_ptr
* a1 - Interrupt Number
* Returns:
* v0 - error status
*/
...
CALLOUT_END(interrupt_mask_my_board)
Sharing data between kernel callouts
If you set aside read/write static storage at the end of the system page for one
kernel callout, you can use this area to pass data between kernel callouts. The
startup library passes the same rw_offset value to the patchers
for all the kernel callouts that share the same address in their
CALLOUT_START macro's second argument.
For example, because the patchers for the following kernel callouts have the same
rw_offset argument value passed to
patch_interrupt(), they share the same read/write storage,
which they can use to share data as needed:
CALLOUT_START(interrupt_mask_my_board, rw_interrupt, patch_interrupt)
....
CALLOUT_END(interrupt_mask_my_board)
CALLOUT_START(interrupt_unmask_my_board, rw_interrupt, patch_interrupt)
....
CALLOUT_END(interrupt_unmask_my_board)
CALLOUT_START(interrupt_eoi_my_board, rw_interrupt, patch_interrupt)
....
CALLOUT_END(interrupt_eoi_my_board)
CALLOUT_START(interrupt_id_my_board, rw_interrupt, patch_interrupt)
....
CALLOUT_END(interrupt_id_my_board)