|
Be
Driven
|
Device Drivers in the Be Os |
|
|
|
|
|
C
Functions : Kernel Export |
|
C Functions - <KernelExport.h>
When Programming device drivers, there are many features provided
by the Operating System that have not been very well documented, if
at all.
The result, you end up writing fancy little functions that don't
work as well as the Os, and might break in the next version. (Functions
like call_all_cpus() for instance).
And remember, device drivers are prone to break from one version
to the next, so exploit what custom funtionality exists for your current
release !
Spawn Kernel Thread
Please Read the chapter on Thread-An Introduction to Kernel Threads.
So, given you need to create a thread in kernel space, the following
function allows you to perform this action. It follows the same function
prototype as the normal create_thread() routine described
in the BeBook Documentation.
Kernel Threads memory access is not quite as restrictive as Interrupt
Functions. They may still 'map' into a shared memory area, like normal
teams, and may access any area in Kernel space. They may not access
user provided memory at ANY point in time. They belong to a seperate
team, and should be treated with respect.
It is difficult to predict when a kernel thread has actually terminated,
so if you need to do this, create a variable in shared memory that
is set to 0 when the thread is on it's way out. As it will no longer
be accessing any memory, you need not worry exactly when it is going
to die.
thread_id
spawn_kernel_thread (
thread_entry function,
const char *thread_name,
long priority,
void *arg
);
Interrupt Masking Functions
Read the chapter on Locking - Interrupt Masking.
typedef ulong cpu_status;
cpu_status disable_interrupts();
void restore_interrupts(cpu_status status);
Spin Locks
Read the chapter on Locking - Spin Locks.
Be carefull to always use spinlocks with interrupts disabled.
typedef vlong spinlock;
void acquire_spinlock (spinlock *lock);
void release_spinlock (spinlock *lock);
Interrupt Handling Routines
Read the chapter on Interrupts-Registering Interrupt Functions.
/* ---
An interrupt handler returns one of the following values: --- */
#define B_UNHANDLED_INTERRUPT 0 /* pass to next handler */
#define B_HANDLED_INTERRUPT 1 /* don't pass on */
#define B_INVOKE_SCHEDULER 2 /* don't pass on; invoke scheduler */
typedef int32 (*interrupt_handler) (void *data);
/* interrupt handling support for device drivers */ 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
);
Interrupt Masking Functions
Always call these functions before doing DMA operations.
Information acquired from...
Be Newsletter, Issue 89, September 3, 1997
The Dirty Little Secret of lock_memory()
By Robert Herold
These functions are used to change the behaviour of memory that
has been allocated by any method.
By Locking memory, you are always making the memory available in
RAM, and in a fixed location, so that it does not move. It does not
relocated the memory into any particular region in memory. (ie., will
not fix 16MB boundry problem with DMA ).
You may also add 2 optional characteristics to this behavior. You
normally use both B_READ_DEVICE | B_DMA_IO , together.
(safer)
B_READ_DEVICE is used to tell the kernel that
the device being read from will be depositing data directory into
memory. In the BeOS implementation of virtual memory, the kernel often
attempts to be clever and see if a range of memory has been changed
since the last time it was read in from the swap file on disk. If
it has not changed, there is no need to write it back to disk when
the memory needs to be used for something else.
The MMU has a built in mechanism for detecting changes affected
by the processor, but this mechanism does not detect DMA transactions
from other devices. The B_READ_DEVICE flag tells the
kernel that indeed the memory is going to change, so don't try to
be clever about it.
The B_DMA_IO flag is an unfortunate crutch made
necessary by what some might perceive as a flawed implementation of
the bridge between the PCI bus and the main system memory bus in certain
motherboard designs from one of our supported platforms.
Normally, when a DMA transaction is started by a device on the PCI
bus and travels though the bridge to system memory, the bridge is
responsible for notifying any and all devices on the main system bus
(e.g. the processor(s)) that the memory is being written, and that
those devices should remove any copies of that memory they may have
stashed in a local cache for fast access. This mechanism was not implemented
in a few designs -- my suspicion is that it was implemented, but did
not work reliably, and was disabled.
The work around for this lame behavior is to tell all the processors
to NEVER cache locations that may have DMA transactions coming in.
This can result in a significant performance hit if those locations
are accessed frequently.
The D_DMA_IO tells the kernel to temporarily mark the
range uncacheable until the matching lock_memory.
This turns out to be a very complex operation. Data in that range
needs to be flushed from all caches. The data structures used by the
cache to determine an address's cacheability need to be updated to
reflect the new non-cacheable status -- but if a part of the range
shares a cache line with anything that gets used in updating the data
structures, it will end up back in the cache!
/* ---
virtual memory buffer functions
--- */
#define B_DMA_IO 0x00000001
#define B_READ_DEVICE 0x00000002
typedef struct {
void *address; /* address in physical memory */
ulong size; /* size of block */
} physical_entry;
long
lock_memory (
void *buf, /* -> virtual buffer to lock (make resident) */
ulong num_bytes, /* size of virtual buffer */
ulong flags
);
long
unlock_memory (
void *buf, /* -> virtual buffer to unlock */
ulong num_bytes, /* size of virtual buffer */
ulong flags
);
Obtaining a Memory Map
Once you have your memory all locked away neat and tight, you will
need to find the physical addresses where it now resides.
The get_memory_map() functions provides this ability.
Actually, you can use this to quench your curiosity on any memory
accessible in your Virtual-Address-Space, but I would suggest locking
it first so It stays in one place!
Remember, if you do not create the memory using an Area, with B_CONTIGUOUS
characteristics, you will find the memory scattered across the Physical-Address-Space!
long get_memory_map (
const void *address, /* -> virtual buffer to translate */
ulong size, /* size of virtual buffer */
physical_entry *table, /* -> caller supplied table */
long num_entries /* # entries in table */
);
So to use it you will want to create an array of physical_entry
elements. You would normally pass in the location of the first byte
in your allocated memory, and the size of the memory allocated to
get all the page locations.
physical_entry area_phys_addr[100]; // start + 99 segments
get_memory_map( base, base_size, area_phys_addr, 99 );
Map Physical Memory into Virtual Address Space
-- Info Taken from the printed Be Advanced Topics
This function allows you to map the memory in physical-address-space
starting at physical_address and extending numBytes
into your team's address space. The kernel creates an area named area_name
mapped into the memory address mapped_address
and returns its area_id to the caller.
numBytes must be a multiple of B_PAGE_SIZE
(4096).
flags 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 , memory
will be alligned on a multiple of B_PAGE_SIZE (4096).
protection is a bitmask consisting of the fields
B_READ_AREA and B_WRITE_AREA , as discussed
in create_area() .
the error codes are the same as those for create_area() .
/* -----
address specifications for mapping physical memory
----- */
#define B_ANY_KERNEL_BLOCK_ADDRESS ((B_ANY_KERNEL_ADDRESS)+1)
/* -----
MTR attributes for mapping physical memory (Intel Architecture only)
----- */
#define B_MTR_UC 0x10000000
#define B_MTR_WC 0x20000000
#define B_MTR_WT 0x30000000
#define B_MTR_WP 0x40000000
#define B_MTR_WB 0x50000000
#define B_MTR_MASK 0xf0000000
/* -----
call to map physical memory - typically used for memory-mapped i/o
----- */
area_id
map_physical_memory (
const char *area_name,
void *physical_address,
size_t numBytes,
uint32 flags,
uint32 protection,
void **mapped_address
);
Platform Stuff
Want to get details about your platform?
typedef enum platform_types {
B_BEBOX_PLATFORM = 0,
B_MAC_PLATFORM,
B_AT_CLONE_PLATFORM
// + other silly ones..
} platform_type;
extern _IMPEXP_KERNEL platform_type platform();
#if __POWERPC__
extern _IMPEXP_KERNEL long motherboard_version (void);
extern _IMPEXP_KERNEL long io_card_version (void);
#endif
Kernel Debugging
/* ---
primitive kernel debugging facilities. Debug output is on...
bebox: serial port 4
mac: modem port
pc: com1
...at 19.2 kbaud, no parity, 8 bit, 1 stop bit.
--- */
extern void dprintf (const char *format, ...); /* just like printf */
extern bool set_dprintf_enabled( bool new_state ); /* returns old state */ extern void panic(const char *format, ...); extern void kernel_debugger (const char *message); /* enter kernel debugger */ extern void kprintf (const char *fmt, ...); /* only for debugger cmds */ extern ulong parse_expression (char *str); /* util for debugger cmds */
/* special return codes for kernel debugger */
#define B_KDEBUG_CONT 2
#define B_KDEBUG_QUIT 3
extern int add_debugger_command (char *name, /* add a cmd to debugger */ int (*func)(int argc, char **argv), char *help);
extern int remove_debugger_command (char *name, /* remove a cmd from debugger */ int (*func)(int argc, char **argv));
extern int load_driver_symbols( const char *driver_name );
Spin
"Executes a delay lasting at least the specified number of
micro-seconds. It could last longer, due to rounding errors, interrupts,
and context switches."
-- Info Taken from the printed Be Advanced Topics
When doing low-level I/O to a board, there are times when you have
to pace how fast you access the poor thing. A Task swap may be to
long to wait, or even not possible (if in an interrupt), so you need
a processor independent method for waiting some small period of time..
So, if you have to wait only a really tiny period, then possibly
you may want to block interrupts to guarantee your time frame.. Of
course doing this with anything larger than the smallest delay is
going to cripple system performance..
If you use it, execute your 2 or 3 commands that must occur near
each other, and then force a task swap using sleep(1); (if you arn't
an interrupt).
Think more like a spinlock with this type of operation.
void spin (bigtime_t num_microseconds);
Register Periodic Daemon in the Kernel
"Adds or removes daemons from the kernel. A kernel daemon function
is executed approximately once every freq/10 seconds. (10ths of a
second). The kernel calls func with the arguments arg and an iteration
value that increase by freq on successive calls to the daemon."
-- Info Taken from the printed Be Advanced Topics
I have no idea why you would want the interation value passed into
you?
Ohh well.
int register_kernel_daemon(
void (*func)(void *, int), void *arg, int freq);
int unregister_kernel_daemon(
void (*func)(void *, int), void *arg);
Signals Pending
Returns a bitmask of the current pending signals for the current
thread. thr should always be NULL; passing other values at this point
is undocumented, and thus is not much use to you.
Returns 0 if no signals are pending, and other if there are.
You may want to use this function when???
extern int has_signals_pending(struct thread_rec *);
Call All Cpus
Ha Ha Ha Ha <evil laugh>
Now, at a guess, this is Dmitriy Budko's doing ;-)
Be Newsletter, Volume II, Issue 21; May 27, 1998
Windows 95 Experience on BeOS -- Or How to Hack on BeOS
By Dmitriy Budko
"spawn a real-time priority kernel thread per CPU, then block
and simultaneously release them. Use spinlocks to synchronize threads
with interrupts disabled ... , because occasionally... the registers
have to be synchronously set up on all CPUs in the system. All interrupts
must be disabled. To do that you have to designate a thread per CPU,
which the BeOS does not support (i.e., CPU affinity for threads)."
At a guess, the call will come back to you, passes in the cookie,
and your CPU number. I can not tell you if this thread will always
remain on one processor. I would make a guess that your are in Kernel
space.
If you are going to use this function, write to Be for instructions
on exactly how to use it.
void call_all_cpus(void (*f)(void*, int), void* cookie);
Disable DMA operations on the IDE bus
This is a question function.
Will return 1 if DMA is disabled. I do not know exactly the ramifications
of this operation.
bool ide_dma_disabled(void);
Are User Add-Ons Disabled
Now, I really dont know what this is about. It could be possible
that when you boot up in safe mode, and the user selects disable user
addons, that you have to check this flag, and not load your driver
if this is the case.. This could be very usefull when you are doing
in-house development, and just happen to have code that crashes things
all the time!
bool disable_user_addons(void);
Missing Functions that you might want to use.
Looking through R4.0 kernel, the following symbols are STILL publically
available, and you may choose to use them at your own peril.
Of course, Be will tell you that you should go through either the
ISA bus or PCI bus modules, and they are probably right, but here
they are for them who have serious suicidal tendencies. This is probably
still here for compatability with the old 3.0 drivers..
Be carefull 4.5 may truly dispose of these functions...
extern _IMPEXP_KERNEL uint8 read_io_8( int port ); extern _IMPEXP_KERNEL void write_io_8( int port, uint8 value ); extern _IMPEXP_KERNEL uint16 read_io_16( int port ); extern _IMPEXP_KERNEL void write_io_16( int port, uint16 value ); extern _IMPEXP_KERNEL uint32 read_io_32( int port ); extern _IMPEXP_KERNEL void write_io_32( int port, uint32 value );
The Communal Be Documentation Site
1999 - bedriven.miffy.org |