The kernel exports a number of functions that device drivers can call. The device driver accesses these functions directly in the kernel, not through a library.
Remember when writing a driver that calls one of these functions to link against either _KERNEL_.LIB (for x86) or a copy of the kernel named _KERNEL_ (for PPC). This will instruct the loader to dynamically locate the symbols in the current kernel when the driver is loaded.
Declared in: be/drivers/KernelExport.h
typedef vlong spinlock void acquire_spinlock(spinlock *lock) void release_spinlock(spinlock *lock)
Spinlocks are mutually exclusive locks that are used to protect sections of code that must execute atomically. To create a spinlock, simply declare a spinlock variable and initialize it 0:
spinlock lock = 0;
The functions acquire and release the lock spinlock. When you acquire and release a spinlock, you must have interrupts disabled; the structure of your code will look like this:
cpu_status former = disable_interrupts(); acquire_spinlock(&lock); /* critical section goes here */ release_spinlock(&lock); restore_interrupts(former);
The spinlock should be held as briefly as possible, and acquisition must not be nested within the critical section.
Spinlocks are designed for use in a multi-processor system (on a single processor system simply turning off interrupts is enough to guarantee that the critical section will be atomic). Nonetheless, you can use spinlocks on a single processor--you don't have to predicate your code based on the number of CPUs in the system.
Declared in: be/drivers/KernelExport.h
typedef ulong cpu_status cpu_status disable_interrupts(void) void restore_interrupts(cpu_status status)
These functions disable and restore interrupts on the CPU that the caller is currently running on. disable_interrupts() returns its previous state (i.e. whether or not interrupts were already disabled). restore_interrupts() restores the previous status of the CPU, which should be the value that disable_interrupts() returned:
cpu_status former = disable_interrupts(); ... restore_interrupts(former);
As long as the cpu state is properly restored (as shown here), the disable/restore functions can be nested.
See also: install_io_interrupt_handler()
Declared in: be/drivers/KernelExport.h
void dprintf(const char *format, ...) bool set_dprintf_enabled(bool enabled) void panic(const char *format, ...)
dprintf() is a debugging function that has the same syntax and behavior as standard C printf(), except that it writes its output to the fourth serial port ("/dev/serial4") at a data rate of 19,200 bits per second. By default, dprintf() is disabled.
set_dprintf_enabled() enables dprintf() if the enabled flag is true, and disables it if the flag is false. It returns the previous enabled state, thus permitting intelligent nesting:
/* Turn on dprintf */ bool former = set_dprintf_enabled(true); ... /* Now restore it to its previous state. */ set_dprintf_enabled(former);
panic() is similar to dprintf(), except it hangs the computer after printing the message.
Declared in: be/drivers/KernelExport.h
long get_memory_map(const void *address, ulong numBytes, physical_entry *table, long numEntries) typedef struct { void *address; ulong size; } physical_entry
Returns the physical memory chunks that map to the virtual memory that starts at address and extends for numBytes. Each chunk of physical memory is returned as a physical_entry structure; the series of structures is returned in the table array. (which you have to allocate yourself). numEntries is the number of elements in the array that you're passing in. As shown in the example, you should lock the memory that you're about to inspect:
physical_entry table[count]; lock_memory(addr, extent, 0); get_memory_map(addr, extent, table, count); . . . unlock_memory(someAddress, someNumberOfBytes, 0);
The end of the table array is indicated by (size == 0):
long k; while (table[k].size > 0) { /* A legitimate entry */ if (++k == count) { /* Not enough entries */ break; } }
If all of the entries have non-zero sizes, then table wasn't big enough; call get_memory_map() again with more table entries.
RETURN CODES
The function always returns B_NO_ERROR.
See also: lock_memory(), start_isa_dma()
Declared in: <drivers/KernelExport.h>
int has_signals_pending(struct thread_rec *thr)
Returns a bitmask of the currently pending signals for the current thread. thr should always be NULL; passing other values will yield meaningless results. has_signals_pending() returns 0 if no signals are pending.
Declared in: <drivers/KernelExport.h>
long install_io_interrupt_handler(long interrupt_number, interrupt_handler handler, void *data, ulong flags) long remove_io_interrupt_handler(long interrupt_number, interrupt_handler handler)
install_io_interrupt_handler() adds the handler function to the chain of functions that will be called each time the specified interrupt occurs. This function should have the following syntax:
bool handler(void *data) |
The data passed to install_io_interrupt_handler() will be passed to the handler function each time it's called. It can be anything that might be of use to the handler, or NULL. If the interrupt handler returns true, the system bypasses the remaining handlers in the interrupt chain; if it returns false, the next handler in the chain is dispatched.
The flags parameter is a bitmask of options. The only option currently defined is B_NO_ENABLE_COUNTER. By default, the OS keeps track of the number of functions handling a given interrupt. If this counter changes from 0 to 1, then the system enables the irq for that interrupt. Conversely, if the counter changes from 1 to 0, the system disables the irq. Setting the B_NO_ENABLE_COUNTER flag instructs the OS to ignore the handler for the purpose of enabling and disabling the irq.
install_io_interrupt_handler() returns B_NO_ERROR if successful in installing the handler, and B_ERROR if not. An error occurs when either the interrupt_number is out of range or there is not enough room left in the interrupt chain to add the handler.
remove_io_interrupt() removes the named interrupt from the interrupt chain. It returns B_NO_ERROR if successful in removing the handler, and B_ERROR if not.
Declared in: <drivers/KernelExport.h>
void kernel_debugger(const char *string) int add_debugger_command(char *name, int (*func)(int, char **), char *help) int load_driver_symbols(const char *driverName) void kprintf(const char *format, ...) ulong parse_expression(const char *string)
kernel_debugger() drops the calling thread into a debugger that writes its output to the fourth serial port at 19,200 bits per second, just as dprintf() does. This debugger produces string as its first message; it's not affected by set_dprintf_enabled().
kernel_debugger() is identical to the debugger() function documented in the Kernel Kit, except that it works in the kernel and engages a different debugger. Drivers should use it instead of debugger().
add_debugger_command() registers a new command with the kernel debugger. When the user types in the command name, the kernel debugger calls func with the remainder of the command line as argc/argv-style arguments. The help string for the command is set to help.
load_driver_symbols() loads symbols from the specified kernel driver into the kernel debugger. driver_name is the path-less name of the driver which must be located in one of the standard kernel driver directories. The function returns B_NO_ERROR on success and B_ERROR on failure.
kprintf() outputs messages to the serial port. It should be used instead of dprintf() from new debugger commands because dprintf() depends too much upon the state of the kernel to be reliable from within the debugger.
parse_expression() takes a C expression and returns the result. It only handles integer arithmetic. The logical and relational operations are accepted. It can also supports variables and assignments. This is useful for strings with multiple expressions, which should be separated with semicolons. Finally, the special variable "." refers to the value from the previous expression. This function is designed to help implement new debugger commands.
See also: debugger() in the Kernel Kit
Declared in: <drivers/KernelExport.h>
long lock_isa_dma_channel(long channel) long unlock_isa_dma_channel(long channel)
These functions reserve an ISA DMA channel and release a channel previously reserved. They return B_NO_ERROR if successful, and B_ERROR if not. Like semaphores, these functions work only if all participating parties adhere to the protocol.
There are 8 ISA DMA channels. In general, they're used as follows:
Channel | Use |
---|---|
0 | Unreserved, available |
1 | Unreserved, available |
2 | Reserved for the floppy disk controller |
3 | Reserved for the parallel port driver |
4 | Reserved by system, cannot be used |
5 | Reserved for IDE |
6 | Reserved for sound |
7 | Reserved for sound |
Declared in: <drivers/KernelExport.h>
long lock_memory(void *address, ulong numBytes, ulong flags) long unlock_memory(const void *address, ulong numBytes, ulong flags)
lock_memory() makes sure that all the memory beginning at the specified virtual address and extending for numBytes is resident in RAM, and locks it so that it won't be paged out until unlock_memory() is called. It pages in any of the memory that isn't resident at the time it's called. It is typically used in preparation for a DMA transaction.
The flags field contains a bitmask of options. Currently, two options, B_DMA_IO and B_READ_DEVICE, are defined. B_DMA_IO should be set if any part of the memory range will be modified by something other than the CPU while it's locked, since that change won't otherwise be noticed by the system and the modified pages may not be written to disk by the virtual memory system. Typically, this sort of change is performed through DMA. B_READ_DEVICE, if set, indicates that the caller intends to fill the memory (read from the device). If cleared, it indicates the memory will be written to the device and will not be altered.
unlock_memory() releases locked memory and should be called with the same flags as passed into the corresponding lock_memory() call.
Each of these functions returns B_NO_ERROR if successful and B_ERROR if not. The main reason that lock_memory() would fail is that you're attempting to lock more memory than can be paged in.
Declared in: <drivers/KernelExport.h>
area_id map_physical_memory(const char *areaName, void *physicalAddress, size_t numBytes, uint32 spec, uint32 protection, void **virtualAddress)
This function allows you to map the memory in physical memory starting at physicalAddress and extending for numBytes bytes into your team's address space. The kernel creates an area named areaName mapped into the memory address virtualAddress and returns its area_id to the caller. numBytes must be a multiple of B_PAGE_SIZE (4096).
spec must be either B_ANY_KERNEL_ADDRESS or B_ANY_KERNEL_BLOCK_ADDRESS. If spec is B_ANY_KERNEL_ADDRESS, the memory will begin at an arbitrary location in the kernel address space. If spec is B_ANY_KERNEL_BLOCK_ADDRESS, then the memory will be mapped into a memory location aligned on a multiple of B_PAGE_SIZE.
protection is a bitmask consisting of the fields B_READ_AREA and B_WRITE_AREA, as discussed in create_area().
create_area() returns an area_id for the newly-created memory if successful or an error code on failure. The error codes are the same as those for create_area().
See also: create_area()
Declared in: <drivers/KernelExport.h>
long motherboard_version(void) long io_card_version(void)
These functions return the current versions of the motherboard and of the I/O card.
Declared in: <drivers/KernelExport.h>
platform_type platform(void)
Returns the current platform, as defined in <kernel/OS.h>.
Declared in: <drivers/KernelExport.h>
void *ram_address(const void *physicalAddress)
Returns the address of a physical block of system memory (RAM) as viewed from the PCI bus. If passed NULL as the physicalAddress, this function returns a pointer to the first byte of RAM; otherwise it returns a pointer to the physicalAddress.
This information is needed by bus masters--components, such as the ethernet and some SCSI controllers, that can perform DMA reads and writes (directly read from and write to system memory without CPU intervention).
Memory must be locked when calling this function. For example:
physical_entry table[count]; void *where; lock_memory(someAddress, someNumberOfBytes, FALSE); get_memory_map(someAddress, someNumberOfBytes, table, count); where = ram_address(table[i].address) . . . unlock_memory(someAddress, someNumberOfBytes);
See also: get_memory_map(), lock_memory()
Declared in: <drivers/KernelExport.h>
uint8 read_io_8(int port) void write_io_8(int port, uint8 value) uint16 read_io_16(int port) void write_io_16(int port, uint16 value) uint32 read_io_32(int port) void write_io_32(int port, uint32 value)
These functions provide an interface for reading and writing from i/o ports.
Declared in: <drivers/KernelExport.h>
int register_kernel_daemon(void (*func)(void *, int), void *arg, int freq) int unregister_kernel_daemon(void (*func)(void *, int), void *arg)
Adds or removes daemons from the kernel. A kernel daemon function is executed approximately once every freq/10 seconds. The kernel calls func with the arguments arg and an iteration value that increases by freq on successive calls to the daemon function.
Declared in: <drivers/KernelExport.h>
thread_id spawn_kernel_thread(thread_entry func, const char *name, long priority, void *data)
This function is a counterpart to spawn_thread() in the Kernel Kit, which is not exported for drivers. It has the same syntax as the Kernel Kit function, but is able to spawn threads in the kernel itself.
See also: spawn_thread() in the Kernel Kit
Declared in: <drivers/KernelExport.h>
void spin(bigtime_t microseconds)
Executes a delay loop lasting at least the specified number of microseconds. It could last longer, due to rounding errors, interrupts, and context switches.
Declared in: <drivers/KernelExport.h>
long start_isa_dma(long channel, void *address, long transferCount, uchar mode, uchar eMode) long start_scattered_isa_dma(long channel, const isa_dma_entry *table, uchar mode, uchar eMode) long make_isa_dma_table(const void *address, long numBytes, ulong numTransferBits, isa_dma_entry *table, long numEntries) struct {...} isa_dma_entry;
These functions initiate ISA DMA memory transfers for the specified channel. They engage the ISA 8237 DMA controller.
start_isa_dma() starts the transfer of a contiguous block of physical memory beginning at the specified address. It requests transferCount number of transfers, which cannot be greater than B_MAX_ISA_DMA_COUNT. Each transfer will move 8 or 16 bits of memory, depending on the mode and eMode flags. These arguments correspond to the mode and extended mode flags recognized by the DMA controller.
The physical memory address that's passed to start_isa_dma() can be obtained by calling get_memory_map().
start_scattered_isa_dma() starts the transfer of a memory buffer that's physically scattered in various pieces. The separate pieces of memory are described by the table passed as a second argument and provided by make_isa_dma_table().
make_isa_dma_table() provides a description of the separate chunks of physical memory that make up the contiguous virtual buffer that begins at address and extends for numBytes. This function anticipates a subsequent call to start_scattered_isa_dma(), which initiates a DMA transfer. It ensures that the information it provides is in the format expected by the 8237 DMA controller. This depends in part on how many bits will be transferred at a time. The third argument, numTransferBits, provides this information. It can be B_8_BIT_TRANSFER or B_16_BIT_TRANSFER.
Each chunk of physical memory is described by a isa_dma_entry structure, which contains the following fields (not that its arcane details matter, since you don't have to do anything with the information except pass it to start_scattered_isa_dma()):
Field | Meaning |
---|---|
ulong address | A physical memory address (in little endian format). |
ushort transfer_count | The number of transfers it will take to move all the physical memory at that address, minus 1 (in little endian format). This value won't be greater than B_MAX_ISA_DMA_COUNT. |
int flags.end_of_list:1 | A flag that's set to mark the last chunk of physical memory corresponding to the virtual buffer. |
make_isa_dma_table() is passed a pointer to a table of isa_dma_entry structures. It fills in the table, stopping when the entire buffer of virtual memory has been described or when numEntry entries in the table have been written, whichever comes first. It returns the number of bytes from the virtual address buffer that it was able to account for in the table.
start_isa_dma() and start_scattered_isa_dma() both return B_NO_ERROR if successful in initiating the transfer, and B_ERROR if the channel isn't free.
Declared in: <drivers/CAM.h>
CCB_HEADER *xpt_ccb_alloc(void) void xpt_ccb_free(void *ccb) long xpt_action(CCB_HEADER *ccbHeader) long xpt_bus_register(CAM_SIM_ENTRY *entryPoints) long xpt_bus_deregister(long pathID)
These functions conform to the SCSI common access method (CAM) specification. See the draft ANSI standard SCSI-2 Common Access Method Transport and SCSI Interface Modules for information.
Although xpt_init() is exported by the kernel, your code never needs to call it; the BeOS does this for you at bootup.
![]() |
The current implementation doesn't support asynchronous callback functions. All CAM requests are executed synchronously in their entirety. |
The Be Book, in lovely HTML, for BeOS Release 3.
Copyright © 1998 Be, Inc. All rights reserved.
Last modified March 27, 1998.