Part I Designing Device Drivers for the Solaris Platform
1. Overview of Solaris Device Drivers
2. Solaris Kernel and Device Tree
5. Managing Events and Queueing Tasks
7. Device Access: Programmed I/O
10. Mapping Device and Kernel Memory
14. Layered Driver Interface (LDI)
Part II Designing Specific Kinds of Device Drivers
15. Drivers for Character Devices
18. SCSI Host Bus Adapter Drivers
19. Drivers for Network Devices
Part III Building a Device Driver
21. Compiling, Loading, Packaging, and Testing Drivers
22. Debugging, Testing, and Tuning Device Drivers
23. Recommended Coding Practices
B. Summary of Solaris DDI/DKI Services
C. Making a Device Driver 64-Bit Ready
D. Console Frame Buffer Drivers
Solaris Consoles and the Kernel Terminal Emulator
x86 Platform Console Communication
SPARC Platform Console Communication
Video Mode Change Callback Interface
Implementing Polled I/O in Console Frame Buffer Drivers
Frame Buffer Specific Configuration Module
The X Window System Frame Buffer Specific DDX Module
Developing, Testing, and Debugging Console Frame Buffer Drivers
Testing the I/O Control Interfaces
Testing the Polled I/O Interfaces
Testing the Video Mode Change Callback Function
Additional Suggestions for Testing Console Frame Buffer Drivers
Except for the video mode change callback, all activity between the driver and the kernel terminal emulator is initiated by the tem (terminal emulator module). This means that the tem issues all of the ioctl commands described in this document. The following sections provide implementation details for each ioctl command. For more information, see the visual_io(7I) man page and the /usr/include/sys/visual_io.h include file. See Video Mode Change Callback Interface for detailed information about the video mode change callback function.
Note - Each ioctl command should determine whether the FKIOCTL is set in the ioctl flag argument and return EPERM if that bit is not set.
The VIS_DEVINIT ioctl command initializes the frame buffer driver as the system console device. This ioctl passes the address of a vis_devinit structure.
The tem first loads the address of its video mode change callback function into the modechg_cb field of the vis_devinit structure and loads its soft state into the modechg_arg field. The tem then issues the VIS_DEVINIT ioctl command. The frame buffer driver then initializes itself and returns a summary of its configuration back to the tem by setting the version, width, height, linebytes, depth, mode, and polledio fields in the vis_devinit structure. The vis_devinit structure is shown in the following code.
struct vis_devinit { /* * This set of fields are used as parameters passed from the * layered frame buffer driver to the terminal emulator. */ int version; /* Console IO interface rev */ screen_size_t width; /* Width of the device */ screen_size_t height; /* Height of the device */ screen_size_t linebytes; /* Bytes per scan line */ int depth; /* Device depth */ short mode; /* Display mode Mode */ struct vis_polledio *polledio; /* Polled output routines */ /* * The following fields are used as parameters passed from the * terminal emulator to the underlying frame buffer driver. */ vis_modechg_cb_t modechg_cb; /* Video mode change callback */ struct vis_modechg_arg *modechg_arg; /* Mode change cb arg */ };
To implement the VIS_DEVINIT ioctl command in the console frame buffer driver, follow these general steps:
Define a struct to contain the console-specific state. This structure is private to the console frame buffer driver. This structure is referred to as consinfo in this appendix. The consinfo structure contains information such as:
Current size of the blit buffer
Pointer to the blit buffer
Color map information
Driver rendering mode information such as line pitch
Background color
Video memory address
Terminal emulator callback address
Allocate memory:
Allocate a blit buffer large enough to store a reasonable default sized rectangle of pixels at the highest video depth. Additional memory can be allocated if an incoming request exceeds the size of the buffer. The frame buffer driver's largest font is 12×22. Assuming DEFAULT_HEIGHT is 12, DEFAULT_WIDTH is 22, and the maximum video depth is 32, the buffer size should be 8448 bytes (DEFAULT_HEIGHT × DEFAULT_WIDTH × 32).
Allocate a vis_polledio structure.
Allocate a buffer to hold a cursor. This buffer should be the size of the largest character. This buffer will not change size.
Obtain the video change callback address and callback context of the tem from modechg_cb and modechg_ctx and store this information in the consinfo structure.
Populate the vis_polledio structure with entry point addresses for the polled display, copy, and cursor functions.
Provide the appropriate information in the fields of the vis_devinit structure that was passed to the driver by the tem:
Set the version field to VIS_CONS_REV, which is a constant defined in the /usr/include/sys/visual_io.h header file.
Set the mode field to VIS_PIXEL.
Set the polledio field to the address of the vis_polledio structure.
Set the height field to the video mode height in pixels.
Set the width field to the video mode width in pixels.
Set the depth field to the frame buffer pixel depth in bytes (for example, a 32-bit pixel depth would be 4 bytes).
Set the linebytes field to the value of height × width × depth.
This information is sent from the driver to the tem by using the vis_devinit structure. This information tells the terminal emulator how to render information and pass it to the graphics driver.
Whenever the console frame buffer driver changes its video mode (specifically height, width, or depth), the driver must call the video mode change callback function of the tem to update the vis_devinit structure and to pass this structure back to the terminal emulator. The terminal emulator passes its mode change callback function address in the modechg_cb field of the vis_devinit structure. The mode change callback function has the following function signature:
typedef void (*vis_modechg_cb_t) (struct vis_modechg_arg *, struct vis_devinit *);
As shown in the preceding typedef, the mode change callback function takes two arguments. The first argument is the modechg_arg and the second argument is the vis_devinit structure. The modechg_arg is sent from the tem to the driver during the VIS_DEVINIT ioctl command initialization. The driver must send the modechg_arg back to the tem with each video mode change callback.
Initialize the context of the kernel console. Specific requirements vary depending upon the capability of the graphics device. This initialization might include such steps as setting the draw engine state, initializing the palette, or locating and mapping video memory or the rendering engine so that data can be blitted onto the screen.
Return the vis_devinit structure to the caller.
The VIS_DEFINI ioctl command releases the driver's console resources and finishes the session.
To implement the VIS_DEVFINI ioctl command in the console frame buffer driver, follow these general steps:
Reset the console frame buffer driver state.
Clear the polled I/O entry points and the kernel terminal emulator video change function callback address.
Release memory.
The VIS_CONSDISPLAY ioctl command displays a rectangle of pixels at a specified location. This display is also referred to as blitting a rectangle. The vis_consdisplay structure contains the information necessary to render a rectangle at the video depth that both the driver and the tem are using. The vis_consdisplay structure is shown in the following code.
struct vis_consdisplay { screen_pos_t row; /* Row (in pixels) to display data at */ screen_pos_t col; /* Col (in pixels) to display data at */ screen_size_t width; /* Width of data (in pixels) */ screen_size_t height; /* Height of data (in pixels) */ unsigned char *data; /* Address of pixels to display */ unsigned char fg_color; /* Foreground color */ unsigned char bg_color; /* Background color */ };
To implement the VIS_CONSDISPLAY ioctl command in the console frame buffer driver, follow these general steps:
Copy the vis_consdisplay structure.
Validate the display parameters. Return an error if any of the display parameters is out of range.
Calculate the size of the rectangle to be blitted into video memory. Validate this size against the size of the blit buffer created during VIS_DEVINIT. Allocate additional memory for the blit buffer if necessary.
Retrieve the blit data. This data has been prepared by the kernel terminal emulator at the agreed upon pixel depth. That depth is the same pixel depth that was conveyed by the tem during VIS_DEVINIT. The pixel depth is updated whenever the device driver changes video modes through callback to the tem. Typical pixel depths are 8-bit color map indexed, and 32-bit TrueColor.
Invalidate any user context so that user applications cannot simultaneously access the frame buffer hardware through user memory mappings. This step is neither allowed nor necessary in polled I/O mode because user applications are not running. Be sure to hold a lock so that users cannot restore the mapping through a page fault until the VIS_CONSDISPLAY ioctl completes.
Establish the driver-specific console rendering context.
If the frame buffer is running in 8-bit color indexed mode, restore the kernel console color map that the tem set up through a previous VIS_PUTCMAP ioctl. A lazy color map loading scheme is recommended to optimize performance. In a lazy scheme, the console frame buffer only restores colors it has actually used since the VIS_DEVINIT ioctl was issued.
Display the data passed from the tem at the pixel coordinates sent by the tem. You might need to transform the RGB pixel data byte order.
The VIS_CONSCOPY ioctl command copies a rectangular region of pixels from one location to another location. One use for this ioctl is to scroll.
To implement the VIS_CONSCOPY ioctl command in the console frame buffer driver, follow these general steps:
Copy the vis_conscopy structure. The vis_conscopy structure describes the source and target rectangle sizes and locations.
Validate the display parameters. Return an error if any of the display parameters is out of range.
Invalidate any user context so that user applications cannot simultaneously access the frame buffer hardware through user memory mappings. This step is neither allowed nor necessary in polled I/O mode because user applications are not running. Be sure to hold a lock so that users cannot restore the mapping through a page fault until the VIS_CONSDISPLAY ioctl completes.
Call the function to copy the rectangle.
Note - For optimal performance, use the rendering engine of the graphic device to implement the copy function. You need to decide how to do the context management within the driver to set up the rendering engine for best performance.
The VIS_CONSCURSOR ioctl command displays or hides a cursor. The vis_conscursor structure is shown in the following code.
struct vis_conscursor { screen_pos_t row; /* Row to display cursor (in pixels) */ screen_pos_t col; /* Col to display cursor (in pixels) */ screen_size_t width; /* Width of cursor (in pixels) */ screen_size_t height; /* Height of cursor (in pixels) */ color_t fg_color; /* Foreground color */ color_t bg_color; /* Background color */ short action; /* Show or Hide cursor */ };
To implement the VIS_CONSCOPY ioctl command in the console frame buffer driver, follow these general steps:
Copy the vis_conscursor structure from the kernel terminal emulator.
Validate the display parameters. Return an error if any of the display parameters are out of range.
Invalidate any user context so that user applications cannot simultaneously access the frame buffer hardware through user memory mappings. This step is neither allowed nor necessary in polled I/O mode because user applications are not running. Be sure to hold a lock so that users cannot restore the mapping through a page fault until the VIS_CONSDISPLAY ioctl completes.
The terminal emulator can call the VIS_CONSCOPY ioctl with one of the following two actions: SHOW_CURSOR and HIDE_CURSOR. The following steps describe how to implement this functionality by reading and writing video memory. You might also be able to use the rendering engine to do this work. Whether you can use the rendering engine depends on the frame buffer hardware.
Take these steps to implement the SHOW_CURSOR functionality:
Save the pixels within the rectangle where the cursor will be drawn. These saved pixels will be needed to hide the cursor.
Scan all the pixels on the screen bounded by the rectangle where the cursor will be drawn. Within this rectangle, replace the pixels that match the specified cursor foreground color (fg_color) with white pixels. Replace the pixels that match the specified cursor background color (bg_color) with black pixels. The visual effect is of a black cursor over white text. This method works with any foreground and background color of text. Attempting to invert colors based upon color map position is not feasible. More sophisticated strategies, such as attempting color inversion using HSB coloring (Hue, Saturation, Brightness), are not necessary.
To implement the HIDE_CURSOR functionality, replace the pixels beneath the cursor rectangle with the pixels saved from the previous SHOW_CURSOR action.
The VIS_PUTCMAP ioctl command establishes the console color map. The terminal emulator calls this function to set up the color map of the kernel. The vis_cmap structure is shown in the following code. This structure only applies to 8-bit color indexed mode.
struct vis_cmap { int index; /* Index into colormap to start updating */ int count; /* Number of entries to update */ unsigned char *red; /* List of red values */ unsigned char *green; /* List of green values */ unsigned char *blue; /* List of blue values */ };
The VIS_PUTCMAP ioctl command is similar to the FBIOPUTCMAP command. The VIS_PUTCMAP command is specific to the frame buffer terminal-emulator compatible console code.
The terminal emulator calls the VIS_GETCMAP ioctl command to retrieve the console color map.