You may need to write a callout that deals with a device that may appear in different locations on different boards. You can do this by "patching" the callout code as it is copied to its final position. The third parameter of the CALLOUT_START macro is either a zero or the address of a patcher() routine. This routine has the following prototype:
void patcher(paddr_t paddr, paddr_t vaddr, unsigned rtn_offset, unsigned rw_offset, void *data, struct callout_rtn *src );
This routine is invoked immediately after the callout has been copied to its final resting place. The parameters are as follows:
Here's an example of a patcher routine for an x86 processor:
patch_debug_8250: movl 0x4(%esp),%eax // get paddr of routine addl 0xc(%esp),%eax // ... movl 0x14(%esp),%edx // get base info movl DDI_BASE(%edx),%ecx // patch code with real serial port movl %ecx,0x1(%eax) movl DDI_SHIFT(%edx),%ecx // patch code with register shift movl $REG_LS,%edx shll %cl,%edx movl %edx,0x6(%eax) ret CALLOUT_START(display_char_8250, 0, patch_debug_8250) movl $0x12345678,%edx // get serial port base (patched) movl $0x12345678,%ecx // get serial port shift (patched) .... CALLOUT_END(display_char_8250)
After the display_char_8250() routine has been copied, the patch_debug_8250() routine is invoked, where it modifies the constants in the first two instructions to the appropriate I/O port location and register spacing for the particular board. The patcher routines don't have to be written in assembler, but they typically are to keep them in the same source file as the code they're patching. By arranging the first instructions in a group of related callouts all the same (e.g. debug_char_*(), poll_key_*(), break_detect_*()), the same patcher routine can be used for all of them.