|
|||
Prex Home >
Document Index >
Driver-Kernel Interface
|
The Prex kernel provides the minimum service for the device drivers. Since the driver module is separated from the kernel module, the drivers can not access other kernel functions beyond this interface. This mechanism helps to isolate the kernel from the driver codes.
This document describes the Driver-Kernel Interface (DKI) that can be used by device drivers.
The Prex driver header file provides common driver services in the kernel. A device driver must include this header file to use the driver-kernel interface.
#include <driver.h>
The following data types are defined by kernel.
Data type | Description |
---|---|
device_t | Used to identify the device object. |
irq_t | Used to identify the interrupt object. |
task_t | Used to identify the task. |
thread_t | Used to identify the thread. |
The driver-kernel service is limited at interrupt level because the kernel does not synchronize all data accesses for interrupt level access. So, the device driver can use only the following functions from the interrupt service routine.
The boot infomation keeps various system informations and they are filled by the boot loader.
The format of the boot information is as follows:
struct bootinfo { struct vidinfo video; struct physmem ram[NMEMS]; /* physical ram table */ int nr_rams; /* number of ram blocks */ struct physmem bootdisk; /* boot disk in memory */ int nr_tasks; /* number of boot tasks */ struct module kernel; /* kernel image */ struct module driver; /* driver image */ struct module tasks[1]; /* boot tasks image */ };
Each device instance has its associated device operations defined by devops structure.
The definition of device operations is as follows:
struct devops { int (*open) (device_t dev, int mode); int (*close) (device_t dev); int (*read) (device_t dev, char *buf, size_t *nbyte, int blkno); int (*write) (device_t dev, char *buf, size_t *nbyte, int blkno); int (*ioctl) (device_t dev, u_long cmd, void *arg); int (*devctl) (device_t dev, u_long cmd, void *arg); };
Each driver must define its own driver object to identify it.
The definition of drver object is as follows:
struct driver { const char *name; /* name of device driver */ struct devops *devops; /* device operations */ size_t devsz; /* size of private data */ int flags; /* state of driver */ int (*probe) (struct driver *self); int (*init) (struct driver *self); int (*unload) (struct driver *self); };
The device object is created by the driver to communicate to the application. Usually, the driver creates a device object for an existing physical device. And, it can also be used to handle logical or virtual devices.
device_t device_create(struct driver *drv, const char *name, int flags); int device_destroy(device_t dev); device_t device_lookup(const char *name); int device_control(device_t dev, u_long cmd, void *arg); int device_broadcast(u_long event, void *arg, int force); void *device_private(device_t dev);
The flags argument is the combination of the following device flags.
#define D_CHR 0x00000001 /* character device */ #define D_BLK 0x00000002 /* block device */ #define D_REM 0x00000004 /* removable device */ #define D_PROT 0x00000008 /* protected device */ #define D_TTY 0x00000010 /* tty device */
void *kmem_alloc(size_t size); void kmem_free(void *ptr); void *kmem_map(void *addr, size_t size);
Since an access to user memory may cause a page fault, the user buffer manipulation is handled by the kernel core code. The driver should not access the user buffer directly. Instead, it should use the following kernel services.
int copyin(const void *uaddr, void *kaddr, size_t len); int copyout(const void *kaddr, void *uaddr, size_t len); int copyinstr(const char *uaddr, void *kaddr, size_t len);
paddr_t page_alloc(psize_t size); void page_free(paddr_t addr, psize_t size); int page_reserve(paddr_t addr, psize_t size);
The spl() function familly controls the interrupt priority level of CPU.
int splhigh(void); int spl0(void); void splx(int level);
irq_t irq_attach(int irqno, int prio, int shared, int (*isr)(void *), void (*ist)(void *), void *data); void irq_detach(irq_t handle);
The following table shows the logical interrupt priority level for various device types. The priority value 0 is lowest priority for interrupt processing.
Priority | Name | Device Class |
---|---|---|
0 | IPL_NONE | Nothing (lowest) |
1 | IPL_COMM | Serial, parallel |
2 | IPL_BLOCK | FDD, IDE |
3 | IPL_NET | Network |
4 | IPL_DISPLAY | Screen |
5 | IPL_INPUT | Keyboard, mouse |
6 | IPL_AUDIO | Audio |
7 | IPL_BUS | USB, PcCard |
8 | IPL_RTC | RTC alarm |
9 | IPL_PROFILE | Profiling timer |
10 | IPL_CLOCK | System clock timer |
11 | IPL_HIGH | Everything |
The thread can sleep/wakeup for the specific event. The event works as the queue of the sleeping threads.
void sched_lock(void); void sched_unlock(void); int sched_tsleep(struct event *evt, u_long timeout); void sched_wakeup(struct event *evt); void sched_dpc(struct dpc *dpc, void (*func)(void *), void *arg);
The definition of the event for sleep/wakeup is as follows:
struct event { struct queue sleepq; /* Queue for waiting thread */ char *name; /* Event name */ };
The definition of the DPC object is as follows:
struct dpc { void *_data[5]; };
void timer_callout(timer_t *tmr, void (*func)(u_long), u_long arg, u_long msec); void timer_stop(timer_t *tmr); u_long timer_delay(u_long msec); u_long timer_ticks(void);
int task_capable(cap_t cap); int exception_post(task_t task, int excno); void machine_bootinfo(struct bootinfo **pbi); void machine_powerdown(int state); void sysinfo(int type, void *buf); void panic(const char *fmt, ...); void printf(const char *fmt, ...); void dbgctl(int cmd, void *data);