Drivers: Functions for Drivers & Modules

Drivers Table of Contents | Drivers Index

# Functions for Drivers & Modules

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 _KERNEL_. This will instruct the loader to dynamically locate the symbols in the current kernel when the driver is loaded.

### acquire_spinlock() , release_spinlock() , spinlock

Declared in: be/drivers/KernelExport.h

void `acquire_spinlock(`spinlock *lock`) `
void `release_spinlock(`spinlock *lock`) `
typedef vlong `spinlock `

Spinlocks are mutually exclusive locks that are used to protect sections of code that must execute atomically. Unlike semaphores, spinlocks can be safely used when interrupts are disabled (in fact, you must have interrupts disabled).

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.

### add_debugger_command() see kernel_debugger()

### add_timer() , cancel_timer() , timer_hook , qent , timer

Declared in: be/drivers/KernelExport.h

typedef int32 (*`timer_hook`)(timer *)

struct `quent` = {
int64 key;
qent *next;
qent *prev;
}

struct `timer` = {
qent entry;
uint16 flags;
uint16 cpu;
timer_hook hook;
bigtime_t period;
}

status_t `add_timer(`timer *theTimer, timer_hook hookFunction, bigtime_t period, int32 flags`) `
bool `cancel_timer(`timer_t *theTimer`) `

`add_timer()` installs a new timer interrupt. A timer interrupt causes the specified hookFunction to be called when the desired amount of time has passed. On entry, you should pass a pointer to a timer structure in theTimer; this will be filled out with data describing the new timer interrupt you've installed. The flags argument provides control over how the timer functions, which affects the meaning of the period argument as follows:

Constant | Meaning
--- | ---
`B_ONE_SHOT_ABSOLUTE_TIMER` | The timer will fire once at the system time specified by period.
`B_ONE_SHOT_RELATIVE_TIMER` | The timer will fire once in approximately period microseconds.
`B_PERIODIC_TIMER` | The timer will fire every period microseconds, starting in period microseconds.

`cancel_timer()` cancels the specified timer. If it's already fired, it returns true; otherwise false is returned. It's guaranteed that once `cancel_timer()` returns, if the timer was in the process of running when `cancel_timer()` was called, the timer function will be finished executing. The only exception to this is if `cancel_timer()` was called from inside a timer handler (in which case trying to wait for the handler to finish running would result in deadlock).

RETURN CODES

`B_OK`. The timer was installed (`add_timer()` only).

- `B_BAD_VALUE`. The timer couldn't be installed because the period was invalid (probably because a relative time or period was negative; unfortunately, Be hasn't mastered the intricacies of installing timers to fire in the past).

### call_all_cpus()

Declared in: be/drivers/KernelExport.h

void `call_all_cpus(`void (*func)(void *, int), void *cookie`) `

Calls the function specified by func on all CPUs. The cookie can be anything your needs require.

### cancel_timer() see add_timer()

### disable_interrupts() , restore_interrupts() , cpu_status

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()`

### dprintf() , set_dprintf_enabled() , panic()

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 serial port at a data rate of 19,200 bits per second. The output is sent to /dev/ports/serial4 on BeBoxes, /dev/modem on Macs, and /dev/ports/serial1 on Intel machines. 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.

### get_memory_map() , physical_entry

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_OK`.

See also: `lock_memory()`, `start_isa_dma()`

### has_signals_pending()

Declared in: be/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.

### install_io_interrupt_handler() , remove_io_interrupt_handler()

Declared in: be/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, void *data`) `

`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:

| int32 `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 must return one of the following values:

Constant | Meaning
--- | ---
`B_UNHANDLED_INTERRUPT` | The interrupt handler didn't handle the interrupt; the kernel will keep looking for someone to handle it.
`B_HANDLED_INTERRUPT` | The interrupt handler handled the interrupt. The kernel won't keep looking for a handler to handle it.
`B_INVOKE_SCHEDULER` | The interrupt handler handled the interrupt. This tells the kernel to invoke the scheduler immediately after the handler returns.

If `B_INVOKE_SCHEDULER` is returned by the interrupt handler, the kernel will immediately invoke the scheduler, to dispatch processor time to tasks that need handling. This is especially useful if your interrupt handler has released a semaphore (see `release_sem_etc()` in the Kernel Kit).

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_OK` 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_OK` if successful in removing the handler, and `B_ERROR` if not.

### io_card_version() see motherboard_version()

### kernel_debugger() , add_debugger_command() , remove_debugger_command() , load_driver_symbols() , kprintf() , parse_expression()

Declared in: be/drivers/KernelExport.h

void `kernel_debugger(`const char *string`) `
int `add_debugger_command(`char *name, int (*func)(int, char **), char *help`) `
int `remove_debugger_command(`char *name, int (*func)(int, char **)`) `
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 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.

`remove_debugger_command()` removes the specified kernel debugger command.

`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_OK` 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

### kprintf() see kernel_debugger()

### load_driver_symbols() see kernel_debugger()

### lock_memory() , unlock_memory()

Declared in: be/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_OK` 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.

### map_physical_memory()

Declared in: be/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()`

### motherboard_version() , io_card_version()

Declared in: be/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. These functions are only available on PowerPC-based systems (they're intended for use on the BeBox).

### panic() see dprintf()

### parse_expression() see kernel_debugger()

### platform()

Declared in: be/drivers/KernelExport.h

platform_type `platform(`void`) `

Returns the current platform, as defined in <kernel/OS.h>.

### register_kernel_daemon() , unregister_kernel_daemon()

Declared in: be/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.

### release_spinlock() see acquire_spinlock()

### remove_io_interrupt_handler() see install_io_interrupt_handler()

### restore_interrupts() see disable_interrupts()

### set_dprintf_enabled() see dprintf()

### send_signal_etc()

Declared in: be/drivers/KernelExport.h

int `send_signal_etc(`pid_t thid, uint sig, uint32 flags`) `

This function is a counterpart to `send_signal()` in the Posix layer, which is not exported for drivers.

thid is the thread_id of the thread the signal should be sent to, and sig is the signal type to send, just like in `send_signal()`. The flags argument can be used to specify flags to control the function:

Constant | Meaning
--- | ---
`B_CHECK_PERMISSION` | The signal will only be sent if the destination thread's uid and euid are the same as the caller's.
`B_DO_NOT_RESCHEDULE` | The kernel won't call the scheduler after sending the signal. You should specify this flag when calling `send_signal_etc()` from an interrupt handler.

RETURN CODES

`B_OK`. The signal was sent.

- `B_BAD_VALUE`. The signal type is invalid.

- `B_BAD_THREAD_ID`. The thread ID is invalid.

- `B_NOT_ALLOWED`. The permission check failed (if `B_CHECK_PERMISSION` was specified).

### spawn_kernel_thread()

Declared in: be/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's memory space.

See also: `spawn_thread()` in the Kernel Kit

### spin()

Declared in: be/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.

### unlock_memory() see lock_memory()

### unregister_kernel_daemon() see register_kernel_daemon()

Drivers Table of Contents | Drivers Index

The Be Book,
...in lovely HTML...
for BeOS Release 5.

Copyright © 2000 Be, Inc. All rights reserved..