Framebuffer Support Overview Overview eCos Support for Framebuffer Devices Description Framebuffer devices are the most common way for a computer system to display graphical output to users. There are immense variations in the implementations of such devices. CYGPKG_IO_FRAMEBUF provides an abstraction layer for use by application code and other packages. It defines an API for manipulating framebuffers, mapping this API on to functionality provided by the appropriate device driver. It also defines the interface which such device drivers should implement. For simple hardware it provides default implementations of much of this interface, greatly reducing the effort needed to write a device driver. This package does not constitute a graphics library. It does not implement functionality like drawing text or arbitrary lines, let alone any kind of windowing system. Instead it operates at the lower level of individual pixels and blocks of pixels, in addition to control operations such as hardware initialization. Some applications may use the framebuffer API directly. Others will instead use a higher-level graphics library, and it is that library which uses the framebuffer API. It is assumed that users are already familiar with the fundamentals of computer graphics, and no attempt is made here to explain terms like display depth, palette or pixel. This package is work-in-progress. The support for 1bpp, 2bpp and 4bpp display depths is incomplete. For double-buffered displays the code does not yet maintain a bounding box of the updated parts of the display. The package has also been designed to allow for expansion with new functionality. Configuration CYGPKG_IO_FRAMEBUF only contains hardware-independent code. It should be complemented by one or more framebuffer device drivers appropriate for the target platform. These drivers may be specific to the platform, or they may be more generic with platform-specific details such as the framebuffer memory base address provided by the platform HAL. When creating a configuration for a given target the device driver(s) will always be included automatically (assuming one has been written or ported). However by default this driver will be inactive and will not get built, so does not add any unnecessary size overhead for applications which do not require graphics. To activate the device driver CYGPKG_IO_FRAMEBUF must be added explicitly to the configuration, for example using ecosconfig add framebuf. After this the full framebuffer API will be available to other packages and to application code. This package contains very few configuration options. Instead it is left to device drivers or higher-level code to provide appropriate configurability. One option, CYGFUN_IO_FRAMEBUF_INSTALL_DEFAULT_PALETTE, relates to the initialization of paletted displays. There are a number of calculated and inferred configuration options and a number of interfaces. These provide information such as whether or not there is a backlight. The most important one is CYGDAT_IO_FRAMEBUF_DEVICES, which holds a list of framebuffer identifiers for use with the macro-based API. If there is a single framebuffer device driver which supports one display in either landscape or portrait mode, the configuration option may hold a value like 240x320x8 320x240x8r90. Application Programmer Interfaces Framebuffer devices require a difficult choice between flexibility and performance. On the one hand the API should be able to support multiple devices driving separate displays, or a single device operating in different modes at different times. On the other hand graphics tends to involve very large amounts of I/O: even something as simple as drawing a background image can involve setting many thousands of pixels. Efficiency requires avoiding all possible overheads including function calls. Instead the API should make extensive use of macros or inline functions. Ideally details of the framebuffer device such as the stride would be known constants at compile-time, giving the compiler as much opportunity as possible to optimize the code. Clearly this is difficult if multiple framebuffer devices are in use or if the device mode may get changed at run-time. To meet the conflicting requirements the generic framebuffer package provides two APIs: a fast macro API which requires selecting a single framebuffer device at compile or configure time; and a slower function API without this limitation. The two are very similar, for example: #include <cyg/io/framebuf.h> void clear_screen(cyg_fb* fb, cyg_fb_colour colour) { cyg_fb_fill_block(fb, 0, 0, fb->fb_width, fb->fb_height, colour); } or the equivalent macro version: #include <cyg/io/framebuf.h> #define FRAMEBUF 240x320x8 void clear_screen(cyg_fb_colour colour) { CYG_FB_FILL_BLOCK(FRAMEBUF, 0, 0, CYG_FB_WIDTH(FRAMEBUF), CYG_FB_HEIGHT(FRAMEBUF), colour); } The function-based API works in terms of cyg_fb structures, containing all the information needed to manipulate the device. Each framebuffer device driver will export one or more of these structures, for example cyg_alaia_fb_240x320x8, and the driver documentation should list the variable names. The macro API works in terms of identifiers such as 240x320x8, and by a series of substitutions the main macro gets expanded to the appropriate device-specific code, usually inline. Again the device driver documentation should list the supported identifiers. In addition the configuration option CYGDAT_IO_FRAMEBUF_DEVICES will contain the full list. By convention the identifier will be specified by a #define'd symbol such as FRAMEBUF, or in the case of graphics libraries by a configuration option. If a platform has multiple framebuffer devices connected to different displays then there will be separate cyg_fb structures and macro identifiers for each one. In addition some devices can operate in multiple modes. For example a PC VGA card can operate in a monochome 640x480 mode, an 8bpp 320x200 mode, and many other modes, but only one of these can be active at a time. The different modes are also represented by different cyg_fb structures and identifiers, effectively treating the modes as separate devices. It is the responsibility of higher-level code to ensure that only one mode is in use at a time. It is possible to use the macro API with more than one device, basically by compiling the code twice with different values of FRAMEBUF, taking appropriate care to avoid identifier name clashes. This gives the higher performance of the macros at the cost of increased code size. All of the framebuffer API, including exports of the device-specific cyg_fb structures, is available through a single header file <cyg/io/framebuf.h>. The API follows a number of conventions. Coordinates (0,0) correspond to the top-left corner of the display. All functions and macros which take a pair of coordinates have x first, y second. For block operations these coordinates are followed by width, then height. Coordinates and dimensions use cyg_ucount16 variables, which for any processor should be the most efficient unsigned data type with at least 16 bits - usually plain unsigned integers. Colours are identified by cyg_fb_colour variables, again usually unsigned integers. To allow for the different variants of the English language, the API allows for a number of alternate spellings. Colour and color can be used interchangeably, so there are data types cyg_fb_colour and cyg_fb_color, and functions cyg_fb_make_colour and cyg_fb_make_color. Similarly gray is accepted as a variant of grey so the predefined colours CYG_FB_DEFAULT_PALETTE_LIGHTGREY and CYG_FB_DEFAULT_PALETTE_LIGHTGRAY are equivalent. The API is split into the following categories: parameters getting information about a given framebuffer device such as width, height and depth. Colours management is complicated so has its own category. control operations such as switching the display on and off, and more device-specific ones such as manipulating the backlight. colours determining the colour format (monochrome, paletted, &hellip), manipulating the palette, or constructing true colours. drawing primitives for manipulating pixels and blocks of pixels. iteration efficiently iterating over blocks of pixels. Thread Safety The framebuffer API never performs any locking so is not thread-safe. Instead it assumes that higher-level code such as a graphics library performs any locking that may be needed. Adding a mutex lock and unlock around every drawing primitive, including pixel writes, would be prohibitively expensive. It is also assumed that the framebuffer will only be updated from thread context. With most hardware it will also be possible to access a framebuffer from DSR or ISR context, but this should be avoided in portable code. Framebuffer Parameters Parameters determining framebuffer capabilities #include <cyg/io/framebuf.h> typedef struct cyg_fb { cyg_ucount16 fb_depth; cyg_ucount16 fb_format; cyg_ucount16 fb_width; cyg_ucount16 fb_height; #ifdef CYGHWR_IO_FRAMEBUF_FUNCTIONALITY_VIEWPORT cyg_ucount16 fb_viewport_width; cyg_ucount16 fb_viewport_height; #endif void* fb_base; cyg_ucount16 fb_stride; cyg_uint32 fb_flags0; … } cyg_fb; cyg_fb* CYG_FB_STRUCT FRAMEBUF cyg_ucount16 CYG_FB_DEPTH FRAMEBUF cyg_ucount16 CYG_FB_FORMAT FRAMEBUF cyg_ucount16 CYG_FB_WIDTH FRAMEBUF cyg_ucount16 CYG_FB_HEIGHT FRAMEBUF cyg_ucount16 CYG_FB_VIEWPORT_WIDTH FRAMEBUF cyg_ucount16 CYG_FB_VIEWPORT_HEIGHT FRAMEBUF void* CYG_FB_BASE FRAMEBUF cyg_ucount16 CYG_FB_STRIDE FRAMEBUF cyg_uint32 CYG_FB_FLAGS0 FRAMEBUF Description When developing an application for a specific platform the various framebuffer parameters such as width and height are known, and the code can be written accordingly. However when writing code that should work on many platforms with different framebuffer devices, for example a graphics library, the code must be able to get these parameters and adapt. Code using the function API can extract the parameters from the cyg_fb structures at run-time. The macro API provides dedicated macros for each parameter. These do not follow the usual eCos convention where the result is provided via an extra argument. Instead the result is returned as normal, and is guaranteed to be a compile-time constant. This allows code like the following: #if CYG_FB_DEPTH(FRAMEBUF) < 8 … #else … #endif or alternatively: if (CYG_FB_DEPTH(FRAMEBUF) < 8) { … } else { … } or: switch (CYG_FB_DEPTH(FRAMEBUF)) { case 1 : … break; case 2 : … break; case 4 : … break; case 8 : … break; case 16 : … break; case 32 : … break; } In terms of the code actually generated by the compiler these approaches have much the same effect. The macros expand to a compile-time constant so unnecessary code can be easily eliminated. The available parameters are as follows: depth The number of bits per pixel or bpp. The common depths are 1, 2, 4, 8, 16 and 32. format How the pixel values are mapped on to visible colours, for example true colour or paletted or greyscale. width height The number of framebuffer pixels horizontally and vertically. viewport width viewport height With some devices the framebuffer height and/or width are greater than what the display can actually show. The display is said to offer a viewport into the larger framebuffer. The number of visible pixels is determined from the viewport width and height. The position of the viewport is controlled via an ioctl. Within a cyg_fb structure these fields are only present if CYGHWR_IO_FRAMEBUF_FUNCTIONALITY_VIEWPORT is defined, to avoid wasting data space on fields that are unnecessary for the current platform. For the macro API the viewport macros should only be used if CYG_FB_FLAGS0_VIEWPORT is set for the framebuffer: #if (CYG_FB_FLAGS0(FRAMEBUF) & CYG_FB_FLAGS0_VIEWPORT) … #endif base stride For linear framebuffers these parameters provide the information needed to access framebuffer memory. The stride is in bytes. flags0 This gives further information about the hardware capabilities. Some of this overlaps with other parameters, especially when it comes to colour, because it is often easier to test for a single flag than for a range of colour modes. The current flags are: CYG_FB_FLAGS0_LINEAR_FRAMEBUFFER Framebuffer memory is organized in a conventional fashion and can be accessed directly by higher-level code using the base and stride parameters. CYG_FB_FLAGS0_LE This flag is only relevant for 1bpp, 2bpp and 4bpp devices and controls how the pixels are organized within each byte. If the flag is set then the layout is little-endian: for a 1bpp device pixel (0,0) occupies bit 0 of the first byte of framebuffer memory. The more common layout is big-endian where pixel (0,0) occupies bit 7 of the first byte. CYG_FB_FLAGS0_TRUE_COLOUR The framebuffer uses a true colour format where the value of each pixel directly encodes the red, green and blue intensities. This is common for 16bpp and 32bpp devices, and is occasionally used for 8bpp devices. CYG_FB_FLAGS0_PALETTE The framebuffer uses a palette. A pixel value does not directly encode the colours, but instead acts as an index into a separate table of colour values. That table may be read-only or read-write. Paletted displays are common for 8bpp and some 4bpp displays. CYG_FB_FLAGS0_WRITEABLE_PALETTE The palette is read-write. CYG_FB_FLAGS0_DELAYED_PALETTE_UPDATE Palette updates can be synchronized to a vertical blank, in other words a brief time period when the display is not being updated, by using CYG_FB_UPDATE_VERTICAL_RETRACE as the last argument to cyg_fb_write_palette or CYG_FB_WRITE_PALETTE. With some hardware updating the palette in the middle of a screen update may result in visual noise. CYG_FB_FLAGS0_VIEWPORT The framebuffer contains more pixels than can be shown on the display. Instead the display provides a viewport into the framebuffer. An ioctl can be used to move the viewport. CYG_FB_FLAGS0_DOUBLE_BUFFER The display does not show the current contents of the framebuffer, so the results of drawing into the framebuffer are not immediately visible. Instead higher-level code needs to perform an explicit synch operation to update the display. CYG_FB_FLAGS0_PAGE_FLIPPING The hardware supports two or more pages, each of width*height pixels, only one of which is visible on the display. This allows higher-level code to update one page without disturbing what is currently visible. An ioctl is used to switch the visible page. CYG_FB_FLAGS0_BLANK The display can be blanked without affecting the framebuffer contents or settings. CYG_FB_FLAGS0_BACKLIGHT There is a backlight which can be switched on or off. Some hardware provides finer-grained control over the backlight intensity. CYG_FB_FLAGS0_MUST_BE_ON Often it is desirable to perform some initialization such as clearing the screen or setting the palette before the display is switched on, to avoid visual noise. However not all hardware allows this. If this flag is set then it is possible to access framebuffer memory and the palette before the cyg_fb_on or CYG_FB_ON operation. It may also be possible to perform some other operations such as activating the backlight, but that is implementation-defined. To allow for future expansion there are also flags1, flags2, and flags3 fields. These may get used for encoding additional ioctl functionality, support for hardware acceleration, and similar features. Linear Framebuffers There are drawing primitives for writing and reading individual pixels. However these involve a certain amount of arithmetic each time to get from a position to an address within the frame buffer, plus function call overhead if the function API is used, and this will slow down graphics operations. When the framebuffer device is known at compile-time and the macro API is used then there are additional macros specifically for iterating over parts of the frame buffer. These should prove very efficient for many graphics operations. However if the device is selected at run-time then the macros are not appropriate and code may want to manipulate framebuffer memory directly. This is possible if two conditions are satisfied: The CYG_FB_FLAGS0_LINEAR_FRAMEBUFFER flag must be set. Otherwise framebuffer memory is either not directly accessible or has a non-linear layout. The CYG_FB_FLAGS0_DOUBLE_BUFFER flag must be clear. An efficient double buffer synch operation requires knowing what part of the framebuffer have been updated, and the various drawing primitives will keep track of this. If higher-level code then starts manipulating the framebuffer directly the synch operation may perform only a partial update. The base, stride, depth, width and height parameters, plus the CYG_FB_FLAGS0_LE flag for 1bpp, 2bpp and 4bpp devices, provide all the information needed to access framebuffer memory. A linear framebuffer has pixel (0,0) at the base address. Incrementing y means adding stride bytes to the pointer. The base and stride parameters may be set even if CYG_FB_FLAGS0_LINEAR_FRAMEBUFFER is clear. This can be useful if for example the display is rotated in software from landscape to portrait mode. However the meaning of these parameters for non-linear framebuffers is implementation-defined. Framebuffer Control Operations Control Operations managing a framebuffer #include <cyg/io/framebuf.h> int cyg_fb_on cyg_fb* fbdev int cyg_fb_off cyg_fb* fbdev int cyg_fb_ioctl cyg_fb* fbdev cyg_uint16 key void* data size_t* len int CYG_FB_ON FRAMEBUF int CYG_FB_OFF FRAMEBUF int CYG_FB_IOCTL FRAMEBUF cyg_uint16 key void* data size_t* len Description The main operations on a framebuffer are drawing and colour management. However on most hardware it is also necessary to switch the display on before the user can see anything, and application code should be able to control when this happens. There are also miscellaneous operations such as manipulating the backlight or moving the viewpoint. These do not warrant dedicated functions, especially since the functionality will only be available on some hardware, so an ioctl interface is used. Switching the Display On or Off With most hardware nothing will be visible until there is a call to cyg_fb_on or an invocation of the CYG_FB_ON macro. This will initialize the framebuffer control circuitry, start sending the data signals to the display unit, and switch on the display if necessary. The exact initialization semantics are left to the framebuffer device driver. In some cases the hardware may already be partially or fully initialized by a static constructor or by boot code that ran before eCos. There are some circumstances in which initialization can fail, and this is indicated by a POSIX error code such as ENODEV. An example would be plug and play hardware where the framebuffer device is not detected at run-time. Another example is hardware which can operate in several modes, with separate cyg_fb structures for each mode, if the hardware is already in use for a different mode. A return value of 0 indicates success. Some but not all hardware allows the framebuffer memory and, if present, the palette to be manipulated before the device is switched on. That way the user does not see random noise on the screen during system startup. The flag CYG_FB_FLAGS0_MUST_BE_ON should be checked: static void init_screen(cyg_fb_colour background) { int result; #if (! (CYG_FB_FLAGS0(FRAMEBUF) & CYG_FB_FLAGS0_MUST_BE_ON)) CYG_FB_FILL_BLOCK(FRAMEBUF, 0, 0, CYG_FB_WIDTH(FRAMEBUF), CYG_FB_HEIGHT(FRAMEBUF), background); #endif result = CYG_FB_ON(FRAMEBUF); if (0 != result) { <handle unusual error condition> } #if (CYG_FB_FLAGS0(FRAMEBUF) & CYG_FB_FLAGS0_MUST_BE_ON) CYG_FB_FILL_BLOCK(FRAMEBUF, 0, 0, CYG_FB_WIDTH(FRAMEBUF), CYG_FB_HEIGHT(FRAMEBUF), background); #endif } Obviously if the application has already manipulated framebuffer memory or the palette but then the cyg_fb_on operation fails, the system is left in an undefined state. It is also possible to switch a framebuffer device off, using the function cyg_fb_off or the macro CYG_FB_OFF, although this functionality is rarely used in embedded systems. The exact semantics of switching a device off are implementation-defined, but typically it involves shutting down the display, stopping the data signals to the display, and halting the control circuitry. The framebuffer memory and the palette are left in an undefined state, and application code should assume that both need full reinitializing when the device is switched back on. Some hardware may also provide a blank operation which typically just manipulates the display, not the whole framebuffer device. Normally cyg_fb_on returns 0. The API allows for a POSIX error code as with cyg_fb_on, but switching a device off is not an operation that is likely to fail. If a framebuffer device can operate in several modes, represented by several cyg_fb structures and macro identifiers, then switching modes requires turning the current device off before turning the next one one. Miscellaneous Control Operations Some hardware functionality such as an LCD panel backlight is common but not universal. Supporting these does not warrant dedicated functions. Instead a catch-all ioctl interface is provided, with the arguments just passed straight to the device driver. This approach also allows for future expansion and for device-specific operations. cyg_fb_ioctl and CYG_FB_IOCTL take four arguments: a cyg_fb structure or framebuffer identifier; a key that specifies the operation to be performed; an arbitrary pointer, which should usually be a pointer to a data structure specific to the key; and a length field. Key values from 0 to 0x7fff are generic. Key values from 0x8000 onwards are reserved for the individual framebuffer device drivers, for device-specific functionality. The length field should be set to the size of the data structure, and may get updated by the device driver. With most ioctl operations the device can indicate whether or not it supports the functionality by one of the flags, for example: void backlight_off(cyg_fb* fb) { if (fb->fb_flags0 & CYG_FB_FLAGS0_BACKLIGHT) { cyg_fb_ioctl_backlight new_setting; size_t len = sizeof(cyg_fb_ioctl_backlight); int result; new_setting.fbbl_current = 0; result = cyg_fb_ioctl(fb, CYG_FB_IOCTL_BACKLIGHT_SET, &new_setting, &len); if (0 != result) { … } } } The operation returns zero for success or a POSIX error code on failure, for example ENOSYS if the device driver does not implement the requested functionality. Viewport # define CYG_FB_IOCTL_VIEWPORT_GET_POSITION 0x0100 # define CYG_FB_IOCTL_VIEWPORT_SET_POSITION 0x0101 typedef struct cyg_fb_ioctl_viewport { cyg_ucount16 fbvp_x; // position of top-left corner of the viewport within cyg_ucount16 fbvp_y; // the framebuffer cyg_ucount16 fbvp_when; // set-only, now or vert retrace } cyg_fb_ioctl_viewport; On some targets the framebuffer device has a higher resolution than the display. Only a subset of the pixels, the viewport, is currently visible. Application code can exploit this functionality to achieve certain effects, for example smooth scrolling. Framebuffers which support this functionality will have the CYG_FB_FLAGS0_VIEWPORT flag set. The viewport dimensions are available as additional parameters to the normal framebuffer width and height. The current position of the viewport can be obtained using an CYG_FB_IOCTL_VIEWPORT_GET_POSITION ioctl operation. The data argument should be a pointer to a cyg_fb_ioctl_viewport structure. On return the fbvp_x and fbvp_y fields will be filled in. To move the viewport use CYG_FB_IOCTL_VIEWPORT_SET_POSITION with fbvp_x and fbvp_y set to the top left corner of the new viewport within the framebuffer, and fbvp_when set to either CYG_FB_UPDATE_NOW or CYG_FB_UPDATE_VERTICAL_RETRACE. If the device driver cannot easily synchronize to a vertical retrace period then this last field is ignored. void move_viewport(cyg_fb* fb, int dx, int dy) { #ifdef CYGHWR_IO_FRAMEBUF_FUNCTIONALITY_VIEWPORT cyg_fb_ioctl_viewport viewport; int len = sizeof(cyg_fb_ioctl_viewport); int result; result = cyg_fb_ioctl(fb, CYG_FB_IOCTL_VIEWPORT_GET_POSITION, &viewport, &len); if (result != 0) { … } if (((int)viewport.fbvp_x + dx) < 0) { viewport.fbvp_x = 0; } else if ((viewport.fbvp_x + dx + fb->fb_viewport_width) > fb->fb_width) { viewport.fbvp_x = fb->fb_width - fb->fb_viewport_width; } else { viewport.fbvp_x += dx; } if (((int)viewport.fbvp_y + dy) < 0) { viewport.fbvp_y = 0; } else if ((viewport.fbvp_y + dy + fb->fb_viewport_height) > fb->fb_height) { viewport.fbvp_y = fb->fb_height - fb->fb_viewport_height; } else { viewport.fbvp_y += dy; } result = cyg_fb_ioctl(fb, CYG_FB_IOCTL_VIEWPORT_SET_POSITION, &viewport, &len); if (result != 0) { … } #else CYG_UNUSED_PARAM(cyg_fb*, fb); CYG_UNUSED_PARAM(int, dx); CYG_UNUSED_PARAM(int, dy); #endif } If an attempt is made to move the viewport beyond the boundaries of the framebuffer then the resulting behaviour is undefined. Some hardware may behave reasonably, wrapping around as appropriate, but portable code cannot assume this. The above code fragment is careful to clip the viewport to the framebuffer dimensions. Page Flipping # define CYG_FB_IOCTL_PAGE_FLIPPING_GET_PAGES 0x0200 # define CYG_FB_IOCTL_PAGE_FLIPPING_SET_PAGES 0x0201 typedef struct cyg_fb_ioctl_page_flip { cyg_uint32 fbpf_number_pages; cyg_uint32 fbpf_visible_page; cyg_uint32 fbpf_drawable_page; cyg_ucount16 fbpf_when; // set-only, now or vert retrace } cyg_fb_ioctl_page_flip; On some targets the framebuffer has enough memory for several pages, only one of which is visible at a time. This allows the application to draw into one page while displaying another. Once drawing is complete the display is flipped to the newly drawn page, and the previously displayed page is now available for updating. This technique is used for smooth animation, especially in games. The flag CYG_FB_FLAGS0_PAGE_FLIPPING indicates support for this functionality. CYG_FB_IOCTL_PAGE_FLIPPING_GET_PAGES can be used to get the current settings of the page flipping support. The data argument should be a pointer to a cyg_fb_ioctl_page_flip structure. The resulting fbpf_number_pages field indicates the total number of pages available: 2 is common, but more pages are possible. fbpf_visible_page gives the page that is currently visible to the user, and will be between 0 and (fbpf_number_pages - 1). Similarly fbpf_drawable_page gives the page that is currently visible. It is implementation-defined whether or not the visible and drawable page can be the same one. CYG_FB_IOCTL_PAGE_FLIPPING_SET_PAGES can be used to change the visible and drawable page. The fbpf_number_pages field is ignored. fbpf_visible_page and fbpf_drawable_page give the new settings. fbpf_when should be one of CYG_FB_UPDATE_NOW or CYG_FB_UPDATE_VERTICAL_RETRACE, but may be ignored by some device drivers. #if !(CYG_FB_FLAGS0(FRAMEBUF) & CYG_FB_FLAGS0_PAGE_FLIPPING) # error Current framebuffer device does not support page flipping #endif static cyg_uint32 current_visible = 0; static void page_flip_init(cyg_fb_colour background) { cyg_fb_ioctl_page_flip flip; size_t len = sizeof(cyg_fb_ioctl_page_flip); flip.fbpf_visible_page = current_visible; flip.fbpf_drawable_page = 1 - current_visible; flip.fbpf_when = CYG_FB_UPDATE_NOW; CYG_FB_IOCTL(FRAMEBUF, CYG_FB_IOCTL_PAGE_FLIPPING_SET_PAGES, &flip, &len); CYG_FB_FILL_BLOCK(FRAMEBUF, 0, 0, CYG_FB_WIDTH(FRAMEBUF), CYG_FB_HEIGHT(FRAMEBUF), background); flip.fbpf_visible_page = 1 - current_visible; flip.fbpf_drawable_page = current_visible; CYG_FB_IOCTL(FRAMEBUF, CYG_FB_IOCTL_PAGE_FLIPPING_SET_PAGES, &flip, &len); CYG_FB_FILL_BLOCK(FRAMEBUF, 0, 0, CYG_FB_WIDTH(FRAMEBUF), CYG_FB_HEIGHT(FRAMEBUF), background); current_visible = 1 - current_visible; } static void page_flip_toggle(void) { cyg_fb_ioctl_page_flip flip; size_t len = sizeof(cyg_fb_ioctl_page_flip); flip.fbpf_visible_page = 1 - current_visible; flip.fbpf_drawable_page = current_visible; CYG_FB_IOCTL(FRAMEBUF, CYG_FB_IOCTL_PAGE_FLIPPING_SET_PAGES, &flip, &len); current_visible = 1 - current_visible; } A page flip typically just changes a couple of pointers within the hardware and device driver. No attempt is made to synchronize the contents of the pages, that is left to higher-level code. Blanking the Screen # define CYG_FB_IOCTL_BLANK_GET 0x0300 # define CYG_FB_IOCTL_BLANK_SET 0x0301 typedef struct cyg_fb_ioctl_blank { cyg_bool fbbl_on; } cyg_fb_ioctl_blank; Some hardware allows the display to be switched off or blanked without shutting down the entire framebuffer device, greatly reducing power consumption. The current blanking state can be obtained using CYG_FB_IOCTL_BLANK_GET and the state can be updated using CYG_FB_IOCTL_BLANK_SET. The data argument should be a pointer to a cyg_fb_ioctl_blank structure. Support for this functionality is indicated by the CYG_FB_FLAGS0_BLANK flag. static cyg_bool display_blanked(cyg_fb_* fb) { cyg_fb_ioctl_blank blank; size_t len = sizeof(cyg_fb_ioctl_blank); if (! (fb->fb_flags0 & CYG_FB_FLAGS0_BLANK)) { return false; } (void) cyg_fb_ioctl(fb, CYG_FB_IOCTL_BLANK_GET, &blank, &len); return !blank.fbbl_on; } Controlling the Backlight # define CYG_FB_IOCTL_BACKLIGHT_GET 0x0400 # define CYG_FB_IOCTL_BACKLIGHT_SET 0x0401 typedef struct cyg_fb_ioctl_backlight { cyg_ucount32 fbbl_current; cyg_ucount32 fbbl_max; } cyg_fb_ioctl_backlight; Many LCD panels provide some sort of backlight, making the display easier to read at the cost of increased power consumption. Support for this is indicated by the CYG_FB_FLAGS0_BACKLIGHT flag. CYG_FB_IOCTL_BACKLIGHT_GET can be used to get both the current setting and the maximum value. If the maximum is 1 then the backlight can only be switched on or off. Otherwise it is possible to control the intensity. static void set_backlight_50_percent(void) { #if (CYG_FB_FLAGS0(FRAMEBUF) & CYG_FB_FLAGS0_BACKLIGHT) cyg_fb_ioctl_backlight backlight; size_t len = sizeof(cyg_fb_ioctl_backlight); CYG_FB_IOCTL(FRAMEBUF, CYG_FB_IOCTL_BACKLIGHT_GET, &backlight, &len); backlight.fbbl_current = (backlight.fbbl_max + 1) >> 1; CYG_FB_IOCTL(FRAMEBUF, CYG_FB_IOCTL_BACKLIGHT_SET, &backlight, &len); #endif } Framebuffer Colours Colours formats and palette management #include <cyg/io/framebuf.h> typedef struct cyg_fb { cyg_ucount16 fb_depth; cyg_ucount16 fb_format; cyg_uint32 fb_flags0; … } cyg_fb; extern const cyg_uint8 cyg_fb_palette_ega[16 * 3]; extern const cyg_uint8 cyg_fb_palette_vga[256 * 3]; #define CYG_FB_DEFAULT_PALETTE_BLACK 0x00 #define CYG_FB_DEFAULT_PALETTE_BLUE 0x01 #define CYG_FB_DEFAULT_PALETTE_GREEN 0x02 #define CYG_FB_DEFAULT_PALETTE_CYAN 0x03 #define CYG_FB_DEFAULT_PALETTE_RED 0x04 #define CYG_FB_DEFAULT_PALETTE_MAGENTA 0x05 #define CYG_FB_DEFAULT_PALETTE_BROWN 0x06 #define CYG_FB_DEFAULT_PALETTE_LIGHTGREY 0x07 #define CYG_FB_DEFAULT_PALETTE_LIGHTGRAY 0x07 #define CYG_FB_DEFAULT_PALETTE_DARKGREY 0x08 #define CYG_FB_DEFAULT_PALETTE_DARKGRAY 0x08 #define CYG_FB_DEFAULT_PALETTE_LIGHTBLUE 0x09 #define CYG_FB_DEFAULT_PALETTE_LIGHTGREEN 0x0A #define CYG_FB_DEFAULT_PALETTE_LIGHTCYAN 0x0B #define CYG_FB_DEFAULT_PALETTE_LIGHTRED 0x0C #define CYG_FB_DEFAULT_PALETTE_LIGHTMAGENTA 0x0D #define CYG_FB_DEFAULT_PALETTE_YELLOW 0x0E #define CYG_FB_DEFAULT_PALETTE_WHITE 0x0F cyg_ucount16 CYG_FB_FORMAT framebuf void cyg_fb_read_palette cyg_fb* fb cyg_ucount32 first cyg_ucount32 count void* data void cyg_fb_write_palette cyg_fb* fb cyg_ucount32 first cyg_ucount32 count const void* data cyg_ucount16 when cyg_fb_colour cyg_fb_make_colour cyg_fb* fb cyg_ucount8 r cyg_ucount8 g cyg_ucount8 b void cyg_fb_break_colour cyg_fb* fb cyg_fb_colour colour cyg_ucount8* r cyg_ucount8* g cyg_ucount8* b void CYG_FB_READ_PALETTE FRAMEBUF cyg_ucount32 first cyg_ucount32 count void* data void CYG_FB_WRITE_PALETTE FRAMEBUF cyg_ucount32 first cyg_ucount32 count const void* data cyg_ucount16 when cyg_fb_colour CYG_FB_MAKE_COLOUR FRAMEBUF cyg_ucount8 r cyg_ucount8 g cyg_ucount8 b void CYG_FB_BREAK_COLOUR FRAMEBUF cyg_fb_colour colour cyg_ucount8* r cyg_ucount8* g cyg_ucount8* b Description Managing colours can be one of the most difficult aspects of writing graphics code, especially if that code is intended to be portable to many different platforms. Displays can vary from 1bpp monochrome, via 2bpp and 4bpp greyscale, through 4bpp and 8bpp paletted, and up to 16bpp and 32bpp true colour - and those are just the more common scenarios. The various drawing primitives like cyg_fb_write_pixel work in terms of cyg_fb_colour values, usually an unsigned integer. Exactly how the hardware interprets a cyg_fb_colour depends on the format. Colour Formats There are a number of ways of finding out how these values will be interpreted by the hardware: The CYG_FB_FLAGS0_TRUE_COLOUR flag is set for all true colour displays. The format parameter can be examined for more details but this is not usually necessary. Instead code can use cyg_fb_make_colour or CYG_FB_MAKE_COLOUR to construct a cyg_fb_colour value from red, green and blue components. If the CYG_FB_FLAGS0_WRITEABLE_PALETTE flag is set then a cyg_fb_colour value is an index into a lookup table known as the palette, and this table contains red, green and blue components. The size of the palette is determined by the display depth, so 16 entries for a 4bpp display and 256 entries for an 8bpp display. Application code or a graphics library can install its own palette so can control exactly what colour each cyg_fb_colour value corresponds to. Alternatively there is support for installing a default palette. If CYG_FB_FLAGS0_PALETTE is set but CYG_FB_FLAGS0_WRITEABLE_PALETTE is clear then the hardware uses a fixed palette. There is no easy way for portable software to handle this case. The palette can be read at run-time, allowing the application's desired colours to be mapped to whichever palette entry provides the best match. However normally it will be necessary to write code specifically for the fixed palette. Otherwise the display is monochrome or greyscale, depending on the depth. There are still variations, for example on a monochrome display colour 0 can be either white or black. As an alternative or to provide additional information, the exact colour format is provided by the fb_format field of the cyg_fb structure or by the CYG_FB_FORMAT macro. It can be one of the following (more entries may be added in future): CYG_FB_FORMAT_1BPP_MONO_0_BLACK simple 1bpp monochrome display, with 0 as black or the darker of the two colours, and 1 as white or the ligher colour. CYG_FB_FORMAT_1BPP_MONO_0_WHITE simple 1bpp monochrome display, with 0 as white or the lighter of the two colours, and 1 as black or the darker colour. CYG_FB_FORMAT_1BPP_PAL888 a 1bpp display which cannot easily be described as monochrome. This is unusual and not readily supported by portable code. It can happen if the framebuffer normally runs at a higher depth, for example 4bpp or 8bpp paletted, but is run at only 1bpp to save memory. Hence only two of the palette entries are used, but can be set to arbitrary colours. The palette may be read-only or read-write. CYG_FB_FORMAT_2BPP_GREYSCALE_0_BLACK a 2bpp display offering four shades of grey, with 0 as black or the darkest of the four shades, and 3 as white or the lightest. CYG_FB_FORMAT_2BPP_GREYSCALE_0_WHITE a 2bpp display offering four shades of grey, with 0 as white or the lightest of the four shades, and 3 as black or the darkest. CYG_FB_FORMAT_2BPP_PAL888 a 2bpp display which cannot easily be described as greyscale, for example providing black, red, blue and white as the four colours. This is unusual and not readily supported by portable code. It can happen if the framebuffer normally runs at a higher depth, for example 4bpp or 8bpp paletted, but is run at only 2bpp to save memory. Hence only four of the palette entries are used, but can be set to arbitrary colours. The palette may be read-only or read-write. CYG_FB_FORMAT_4BPP_GREYSCALE_0_BLACK a 4bpp display offering sixteen shades of grey, with 0 as black or the darkest of the 16 shades, and 15 as white or the lighest. CYG_FB_FORMAT_4BPP_GREYSCALE_0_WHITE a 4bpp display offering sixteen shades of grey, with 0 as white or the lightest of the 16 shades, and 15 as black or the darkest. CYG_FB_FORMAT_4BPP_PAL888 a 4bpp paletted display, allowing for 16 different colours on screen at the same time. The palette may be read-only or read-write. CYG_FB_FORMAT_8BPP_PAL888 an 8bpp paletted display, allowing for 256 different colours on screen at the same time. The palette may be read-only or read-write. CYG_FB_FORMAT_8BPP_TRUE_332 an 8bpp true colour display, with three bits (eight levels) of red and green intensity and two bits (four levels) of blue intensity. CYG_FB_FORMAT_16BPP_TRUE_565 a 16bpp true colour display with 5 bits each for red and blue and 6 bits for green. CYG_FB_FORMAT_16BPP_TRUE_555 a 16bpp true colour display with five bits each for red, green and blue, and one unused bit. CYG_FB_FORMAT_32BPP_TRUE_0888 a 32bpp true colour display with eight bits each for red, green and blue and eight bits unused. For the true colour formats the format does not define exactly which bits in the pixel are used for which colour. Instead the cyg_fb_make_colour and cyg_fb_break_colour functions or the equivalent macros should be used to construct or decompose pixel values. Paletted Displays Palettes are the common way of implementing low-end colour displays. There are two variants. A read-only palette provides a fixed set of colours and it is up to application code to use these colours appropriately. A read-write palette allows the application to select its own set of colours. Displays providing a read-write palette will have the CYG_FB_FLAGS0_WRITEABLE_PALETTE flag set in addition to CYG_FB_FLAGS0_PALETTE. Even if application code can install its own palette, many applications do not exploit this functionality and instead stick with a default. There are two standard palettes: the 16-entry PC EGA for 4bpp displays; and the 256-entry PC VGA, a superset of the EGA one, for 8bpp displays. This package provides the data for both, in the form of arrays cyg_fb_palette_ega and cyg_fb_palette_vga, and 16 #define's such as CYG_FB_DEFAULT_PALETTE_BLACK for the EGA colours and the first 16 VGA colours. By default device drivers for read-write paletted displays will install the appropriate default palette, but this can be suppressed using configuration option CYGFUN_IO_FRAMEBUF_INSTALL_DEFAULT_PALETTE. If a custom palette will be used then installing the default palette involves wasting 48 or 768 bytes of memory. It should be emphasized that displays vary widely. A colour such as CYG_FB_DEFAULT_PALETTE_YELLOW may appear rather differently on two different displays, although it should always be recognizable as yellow. Developers may wish to fine-tune the palette for specific hardware. The current palette can be retrieved using cyg_fb_read_palette or CYG_FB_READ_PALETTE. The first and count arguments control which palette entries should be retrieved. For example, to retrieve just palette entry 12 first should be set to 12 and count should be set to 1. To retrieve all 256 entries for an 8bpp display, first should be set to 0 and count should be set to 256. The data argument should point at an array of bytes, allowing three bytes for every entry. Byte 0 will contain the red intensity for the first entry, byte 1 green and byte 2 blue. For read-write palettes the palette can be updated using cyg_fb_write_palette or CYG_FB_WRITE_PALETTE. The first and count arguments are the same as for cyg_fb_read_palette, and the data argument should point at a suitable byte array packed in the same way. The when argument should be one of CYG_FB_UPDATE_NOW or CYG_FB_UPDATE_VERTICAL_RETRACE. With some displays updating the palette in the middle of an update may result in visual noise, so synchronizing to the vertical retrace avoids this. However not all device drivers will support this. There is an assumption that palette entries use 8 bits for each of the red, green and blue colour intensities. This is not always the case, but the device drivers will perform appropriate adjustments. Some hardware may use only 6 bits per colour, and the device driver will ignore the bottom two bits of the supplied intensity values. Occasionally hardware may use more than 8 bits, in which case the supplied 8 bits are shifted left appropriately and zero-padded. Device drivers for such hardware may also provide device-specific routines to manipulate the palette in a non-portable fashion. True Colour displays True colour displays are often easier to manage than paletted displays. However this comes at the cost of extra memory. A 16bpp true colour display requires twice as much memory as an 8bpp paletted display, yet can offer only 32 or 64 levels of intensity for each colour as opposed to the 256 levels provided by a palette. It also requires twice as much video memory bandwidth to send all the pixel data to the display for every refresh, which may impact the performance of the rest of the system. A 32bpp true colour display offers the same colour intensities but requires four times the memory and four times the bandwidth. Exactly how the colour bits are organized in a cyg_fb_colour pixel value is not defined by the colour format. Instead code should use the cyg_fb_make_colour or CYG_FB_MAKE_COLOUR primitives. These take 8-bit intensity levels for red, green and blue, and return the corresponding cyg_fb_colour. When using the macro interface the arithmetic happens at compile-time, for example: #define BLACK CYG_FB_MAKE_COLOUR(FRAMEBUF, 0, 0, 0) #define WHITE CYG_FB_MAKE_COLOUR(FRAMEBUF, 255, 255, 255) #define RED CYG_FB_MAKE_COLOUR(FRAMEBUF, 255, 0, 0) #define GREEN CYG_FB_MAKE_COLOUR(FRAMEBUF, 0, 255, 0) #define BLUE CYG_FB_MAKE_COLOUR(FRAMEBUF, 0, 0, 255) #define YELLOW CYG_FB_MAKE_COLOUR(FRAMEBUF, 255, 255, 80) Displays vary widely so the numbers may need to be adjusted to give the exact desired colours. For symmetry there are also cyg_fb_break_colour and CYG_FB_BREAK_COLOUR primitives. These take a cyg_fb_colour value and decompose it into its red, green and blue components. Framebuffer Drawing Primitives Drawing Primitives updating the display #include <cyg/io/framebuf.h> void cyg_fb_write_pixel cyg_fb* fbdev cyg_ucount16 x cyg_ucount16 y cyg_fb_colour colour cyg_fb_colour cyg_fb_read_pixel cyg_fb* fbdev cyg_ucount16 x cyg_ucount16 y void cyg_fb_write_hline cyg_fb* fbdev cyg_ucount16 x cyg_ucount16 y cyg_ucount16 len cyg_fb_colour colour void cyg_fb_write_vline cyg_fb* fbdev cyg_ucount16 x cyg_ucount16 y cyg_ucount16 len cyg_fb_colour colour void cyg_fb_fill_block cyg_fb* fbdev cyg_ucount16 x cyg_ucount16 y cyg_ucount16 width cyg_ucount16 height cyg_fb_colour colour void cyg_fb_write_block cyg_fb* fbdev cyg_ucount16 x cyg_ucount16 y cyg_ucount16 width cyg_ucount16 height const void* data cyg_ucount16 offset cyg_ucount16 stride void cyg_fb_read_block cyg_fb* fbdev cyg_ucount16 x cyg_ucount16 y cyg_ucount16 width cyg_ucount16 height void* data cyg_ucount16 offset cyg_ucount16 stride void cyg_fb_move_block cyg_fb* fbdev cyg_ucount16 x cyg_ucount16 y cyg_ucount16 width cyg_ucount16 height cyg_ucount16 new_x cyg_ucount16 new_y void cyg_fb_synch cyg_fb* fbdev cyg_ucount16 when void CYG_FB_WRITE_PIXEL FRAMEBUF cyg_ucount16 x cyg_ucount16 y cyg_fb_colour colour cyg_fb_colour CYG_FB_READ_PIXEL FRAMEBUF cyg_ucount16 x cyg_ucount16 y void CYG_FB_WRITE_HLINE FRAMEBUF cyg_ucount16 x cyg_ucount16 y cyg_ucount16 len cyg_fb_colour colour void CYG_FB_WRITE_VLINE FRAMEBUF cyg_ucount16 x cyg_ucount16 y cyg_ucount16 len cyg_fb_colour colour void CYG_FB_FILL_BLOCK FRAMEBUF cyg_ucount16 x cyg_ucount16 y cyg_ucount16 width cyg_ucount16 height cyg_fb_colour colour void CYG_FB_WRITE_BLOCK FRAMEBUF cyg_ucount16 x cyg_ucount16 y cyg_ucount16 width cyg_ucount16 height const void* data cyg_ucount16 offset cyg_ucount16 stride void CYG_FB_READ_BLOCK FRAMEBUF cyg_ucount16 x cyg_ucount16 y cyg_ucount16 width cyg_ucount16 height void* data cyg_ucount16 offset cyg_ucount16 stride void CYG_FB_MOVE_BLOCK FRAMEBUF cyg_ucount16 x cyg_ucount16 y cyg_ucount16 width cyg_ucount16 height cyg_ucount16 new_x cyg_ucount16 new_y void CYG_FB_SYNCH FRAMEBUF cyg_ucount16 when Description The eCos framebuffer infrastructure defines a small number of drawing primitives. These are not intended to provide full graphical functionality like multiple windows, drawing text in arbitrary fonts, or anything like that. Instead they provide building blocks for higher-level graphical toolkits. The available primitives are: Manipulating individual pixels. Drawing horizontal and vertical lines. Block fills. Moving blocks between the framebuffer and main memory. Moving blocks within the framebuffer. For double-buffered devices, synchronizing the framebuffer contents with the actual display. There are two versions for each primitive: a macro and a function. The macro can be used if the desired framebuffer device is known at compile-time. Its first argument should be a framebuffer identifier, for example 320x240x16, and must be one of the entries in the configuration option CYGDAT_IO_FRAMEBUF_DEVICES. In the examples below it is assumed that FRAMEBUF has been #define'd to a suitable identifier. The function can be used if the desired framebuffer device is selected at run-time. Its first argument should be a pointer to the appropriate cyg_fb structure. The pixel, line, and block fill primitives take a cyg_fb_colour argument. For details of colour handling see . This argument should have no more bits set than are appropriate for the display depth. For example on a 4bpp only the bottom four bits of the colour may be set, otherwise the behaviour is undefined. None of the primitives will perform any run-time error checking, except possibly for some assertions in a debug build. If higher-level code provides invalid arguments, for example trying to write a block which extends past the right hand side of the screen, then the system's behaviour is undefined. It is the responsibility of higher-level code to perform clipping to the screen boundaries. Manipulating Individual Pixels The primitives for manipulating individual pixels are very simple: a pixel can be written or read back. The following example shows one way of drawing a diagonal line: void draw_diagonal(cyg_fb* fb, cyg_ucount16 x, cyg_ucount16 y, cyg_ucount16 len, cyg_fb_colour colour) { while ( len-- ) { cyg_fb_write_pixel(fb, x++, y++, colour); } } The next example shows how to draw a horizontal XOR line on a 1bpp display. void draw_horz_xor(cyg_ucount16 x, cyg_ucount16 y, cyg_ucount16 len) { cyg_fb_colour colour; while ( len--) { colour = CYG_FB_READ_PIXEL(FRAMEBUF, x, y); CYG_FB_WRITE_PIXEL(FRAMEBUF, x++, y, colour ^ 0x01); } } The pixel macros should normally be avoided. Determining the correct location within framebuffer memory corresponding to a set of coordinates for each pixel is a comparatively expensive operation. Instead there is direct support for iterating over parts of the display, avoiding unnecessary overheads. Drawing Simple Lines Higher-level graphics code often needs to draw single-pixel horizontal and vertical lines. If the application involves multiple windows then these will usually have thin borders around them. Widgets such as buttons and scrollbars also often have thin borders. cyg_fb_draw_hline and CYG_FB_DRAW_HLINE draw a horizontal line of the specified colour, starting at the x and y coordinates and extending to the right (increasing x) for a total of len pixels. A 50 pixel line starting at (100,100) will end at (149,100). cyg_fb_draw_vline and CYG_FB_DRAW_VLINE take the same arguments, but the line extends down (increasing y). These primitives do not directly support drawing lines more than one pixel thick, but block fills can be used to achieve those. There is no generic support for drawing arbitrary lines, instead that is left to higher-level graphics toolkits. Block Fills Filling a rectangular part of the screen with a solid colour is another common requirement for higher-level code. The simplest example is during initialization, to set the display's whole background to a known value. Block fills are also often used when creating new windows or drawing the bulk of a simple button or scrollbar widget. cyg_fb_fill_block and CYG_FB_FILL_BLOCK provide this functionality. The x and y arguments specify the top-left corner of the block to be filled. The width and height arguments specify the number of pixels affected, a total of width * height. The following example illustrates part of the process for initializing a framebuffer, assumed here to have a writeable palette with default settings. int display_init(void) { int result = CYG_FB_ON(FRAMEBUF); if ( result ) { return result; } CYG_FB_FILL_BLOCK(FRAMEBUF, 0, 0, CYG_FB_WIDTH(FRAMEBUF), CYG_FB_HEIGHT(FRAMEBUF), CYG_FB_DEFAULT_PALETTE_WHITE); … } Copying Blocks between the Framebuffer and Main Memory The block transfer primitives serve two main purposes: drawing images. and saving parts of the current display to be restored later. For simple linear framebuffers the primitives just implement copy operations, with no data conversion of any sort. For non-linear ones the primitives act as if the framebuffer memory was linear. For example, consider a 2bpp display where the two bits for a single pixel are split over two separate bytes in framebuffer memory, or two planes. For a block write operation the source data should still be organized with four full pixels per byte, as for a linear framebuffer of the same depth. and the block write primitive will distribute the bits over the framebuffer memory as required. Similarly a block read will combine the appropriate bits from different locations in framebuffer memory and the resulting memory block will have four full pixels per byte. Because the block transfer primitives perform no data conversion, if they are to be used for rendering images then those images should be pre-formatted appropriately for the framebuffer device. For small images this would normally happen on the host-side as part of the application build process. For larger images it will usually be better to store them in a compressed format and decompress them at run-time, trading off memory for cpu cycles. The x and y arguments specify the top-left corner of the block to be transferred, and the width and height arguments determine the size. The data, offset and stride arguments determine the location and layout of the block in main memory: data The source or destination for the transfer. For 1bpp, 2bpp and 4bpp devices the data will be packed in accordance with the framebuffer device's endianness as per the CYG_FB_FLAGS0_LE flag. Each row starts in a new byte so there may be some padding on the right. For 16bpp and 32bpp the data should be aligned to the appropriate boundary. offset Sometimes only part of an image should be written to the screen. A vertical offset can be achieved simply by adjusting data to point at the appropriate row within the image instead of the top row. For 8bpp, 16bpp and 32bpp displays an additional horizontal offset can also be achieved by adjusting data. However for 1bpp, 2bpp and 4bpp displays the starting position within the image may be in the middle of a byte. Hence the horizontal pixel offset can instead be specified with the offset argument. stride This indicates the number of bytes between rows. Usually it will be related to the width, but there are exceptions such as when drawing only part of an image. The following example fills a 4bpp display with an image held in memory and already in the right format. If the image is smaller than the display it will be centered. If the image is larger then the center portion will fill the entire display. void draw_image(const void* data, int width, int height) { cyg_ucount16 stride; cyg_ucount16 x, y, offset; #if (4 != CYG_FB_DEPTH(FRAMEBUF)) # error This code assumes a 4bpp display #endif stride = (width + 1) >> 1; // 4bpp to byte stride if (width < CYG_FB_WIDTH(FRAMEBUF)) { x = (CYG_FB_WIDTH(FRAMEBUF) - width) >> 1; offset = 0; } else { x = 0; offset = (width - CYG_FB_WIDTH(FRAMEBUF)) >> 1; width = CYG_FB_WIDTH(FRAMEBUF); } if (height < CYG_FB_HEIGHT(FRAMEBUF)) { y = (CYG_FB_HEIGHT(FRAMEBUF) - height) >> 1; } else { y = 0; data = (const void*)((const cyg_uint8*)data + (stride * ((height - CYG_FB_HEIGHT(FRAMEBUF)) >> 1)); height = CYG_FB_HEIGHT(FRAMEBUF); } CYG_FB_WRITE_BLOCK(FRAMEBUF, x, y, width, height, data, offset, stride); } Moving Blocks with the Framebuffer Sometimes it is necessary to move a block of data around the screen, especially when using a higher-level graphics toolkit that supports multiple windows. Block moves can be implemented by a read into main memory followed by a write block, but this is expensive and imposes an additional memory requirement. Instead the framebuffer infrastructure provides a generic block move primitive. It will handle all cases where the source and destination positions overlap. The x and y arguments specify the top-left corner of the block to be moved, and width and height determine the block size. new_x and new_y specify the destination. The source data will remain unchanged except in areas where it overlaps the destination. Synchronizing Double-Buffered Displays Some framebuffer devices are double-buffered: the framebuffer memory that gets manipulated by the drawing primitives is separate from what is actually displayed, and a synch operation is needed to update the display. In some cases this may be because the actual display memory is not directly accessible by the processor, for example it may instead be attached via an SPI bus. Instead drawing happens in a buffer in main memory, and then this gets transferred over the SPI bus to the actual display hardware during a synch. In other cases it may be a software artefact. Some drawing operations, especially ones involving complex curves, can take a very long time and it may be considered undesirable to have the user see this happening a few pixels at a time. Instead the drawing happens in a separate buffer in main memory and then a double buffer synch just involves a block move to framebuffer memory. Typically that block move is much faster than the drawing operation. Obviously there is a cost: an extra area of memory, and the synch operation itself can consume many cycles and much of the available memory bandwidth. It is the responsibility of the framebuffer device driver to provide the extra main memory. As far as higher-level code is concerned the only difference between an ordinary and a double-buffered display is that with the latter changes do not become visible until a synch operation has been performed. The framebuffer infrastructure provides support for a bounding box, keeping track of what has been updated since the last synch. This means only the updated part of the screen has to be transferred to the display hardware. The synch primitives take two arguments. The first identifies the framebuffer device. The second should be one of CYG_FB_UPDATE_NOW for an immediate update, or CYG_FB_UPDATE_VERTICAL_RETRACE. Some display hardware involves a lengthy vertical retrace period every 10-20 milliseconds during which nothing gets drawn to the screen, and performing the synch during this time means that the end user is unaware of the operation (assuming the synch can be completed in the time available). When the hardware supports it, specifying CYG_FB_UPDATE_VERTICAL_RETRACE means that the synch operation will block until the next vertical retrace takes place and then perform the update. This may be an expensive operation, for example it may involve polling a bit in a register. In a multi-threaded environment it may also be unreliable because the thread performing the synch may get interrupted or rescheduled in the middle of the operation. When the hardware does not involve vertical retraces, or when there is no easy way to detect them, the second argument to the synch operation will just be ignored and the update will always happen immediately. It is up to higher level code to determine when a synch operation is appropriate. One approach for typical event-driven code is to perform the synch at the start of the event loop, just before waiting for an input or timer event. This may not be optimal. For example if there two small updates to opposite corners of the screen then it would be better to make two synch calls with small bounding boxes, rather than a single synch call with a a large bounding box that requires most of the framebuffer memory to be updated. Leaving out the synch operations leads to portability problems. On hardware which does not involve double-buffering the synch operation is a no-op, usually eliminated at compile-time, so invoking synch does not add any code size or cpu cycle overhead. On double-buffered hardware, leaving out the synch means the user cannot see what has been drawn into the framebuffer. Framebuffer Pixel Manipulation Pixel Manipulation iterating over the display #include <cyg/io/framebuf.h> CYG_FB_PIXEL0_VAR FRAMEBUF void CYG_FB_PIXEL0_SET FRAMEBUF cyg_ucount16 x cyg_ucount16 y void CYG_FB_PIXEL0_GET FRAMEBUF cyg_ucount16 x cyg_ucount16 y void CYG_FB_PIXEL0_ADDX FRAMEBUF cyg_ucount16 incr void CYG_FB_PIXEL0_ADDY FRAMEBUF cyg_ucount16 incr void CYG_FB_PIXEL0_WRITE FRAMEBUF cyg_fb_colour colour cyg_fb_colour CYG_FB_PIXEL0_READ FRAMEBUF void CYG_FB_PIXEL0_FLUSHABS FRAMEBUF cyg_ucount16 x0 cyg_ucount16 y0 cyg_ucount16 width cyg_ucount16 height void CYG_FB_PIXEL0_FLUSHREL FRAMEBUF cyg_ucount16 x0 cyg_ucount16 y0 cyg_ucount16 dx cyg_ucount16 dy Description A common requirement for graphics code is to iterate over parts of the framebuffer. Drawing text typically involves iterating over a block of pixels for each character, say 8 by 8, setting each pixel to either a foreground or background colour. Drawing arbitrary lines typically involves moving to the start position and then adjusting the x and y coordinates until the end position is reached, setting a single pixel each time around the loop. Drawing images which are not in the frame buffer's native format typically involves iterating over a block of pixels, from top to bottom and left to right, setting pixels as the image is decoded. Functionality like this can be implemented in several ways. One approach is to use the pixel write primitive. Typically this involves some arithmetic to get from the x and y coordinates to a location within framebuffer memory so it is fairly expensive compared with a loop which just increments a pointer. Another approach is to write the data first to a separate buffer in memory and then use a block write primitive to move it to the framebuffer, but again this involves overhead. The eCos framebuffer support provides a third approach: a set of macros specifically for iterating over the frame buffer. Depending on the operation being performed and the details of the framebuffer implementation, these macros may be optimal or near-optimal. Obviously there are limitations. Most importantly the framebuffer device must be known at compile-time: the compiler can do a better job optimizing the code if information such as the frame buffer width are constant. Also each iteration must be performed within a single variable scope: it is not possible to do some of the iteration in one function, some in another. The Pixel Macros All the pixel macros take a framebuffer identifier as their first argument. This is the same identifier that can be used with the other macros like CYG_FB_WRITE_HLINE and CYG_FB_ON, one of the entries in the configuration option CYGDAT_IO_FRAMEBUF_DEVICES. Using an invalid identifier will result in numerous compile-time error messages which may bear little resemblance to the original code. In the examples below it is assumed that FRAMEBUF has been #define'd to a suitable identifier. Typical use of the pixel macros will look like this: CYG_FB_PIXEL0_VAR(FRAMEBUF); … CYG_FB_PIXEL0_FLUSHABS(FRAMEBUF, x, y, width, height); The VAR macro will define one or more local variables to keep track of the current pixel position, as appropriate to the framebuffer device. The other pixel macros will then use these variables. For a simple 8bpp linear framebuffer there will be just a byte pointer. For a 1bpp display there may be several variables: a byte pointer, a bit index within that byte, and possibly a cached byte; using a cached value means that the framebuffer may only get read and written once for every 8 pixels, and the compiler may well allocate a register for the cached value; on some platforms framebuffer access will bypass the processor's main cache, so reading from or writing to framebuffer memory will be slow; reducing the number of framebuffer accesses may greatly improve performance. Because the VAR macro defines one or more local variables it is normally placed at the start of a function or block, alongside other local variable definitions. One the iteration has been completed there should be a FLUSHABS or FLUSHREL macro. This serves two purposes. First, if the local variables involve a dirty cached value or similar state then this will be written back. Second, for double-buffered displays the macro sets a bounding box for the part of the screen that has been updated. This allows the double buffer synch operation to update only the part of the display that has been modified, without having to keep track of the current bounding box for every updated pixel. For FLUSHABS the x0 and y0 arguments specify the top-left corner of the bounding box, which extends for width by height pixels. For FLUSHREL x0 and y0 still specify the top-left corner, but the bottom-right corner is now determined from the current pixel position offset by dx and dy. More specifically, dx should move the current horizontal position one pixel to the right of the right-most pixel modified, such that (x + dx) - x0 gives the width of the bounding box. Similarly dy should move the current vertical position one pixel below the bottom-most pixel modified. In typical code the current pixel position will already correspond in part or in whole to the bounding box corner, as a consequence of iterating over the block of memory. If a pixel variable has been used only for reading framebuffer memory, not for modifying it, then it should still be flushed. A FLUSHABS with a width and height of 0 can be used to indicate that the bounding box is empty. If it is known that the framebuffer device being used does not support double-buffering then again it is possible to specify an empty bounding box. Otherwise portable code should specify a correct bounding box. If the framebuffer device that ends up being used does not support double buffering then the relevant macro arguments are eliminated at compile-time and do not result in any unnecessary code. In addition if there is no cached value or other state then the whole flush operation will be a no-op and no code will be generated. Failure to perform the flush may result in strange drawing artefacts on some displays which can be very hard to debug. A FLUSHABS or FLUSHREL macro only needs to be invoked once, at the end of the iteration. The SET macro sets the current position within the framebuffer. It can be used many times within an iteration. However it tends to be somewhat more expensive than ADDX or ADDY, so usually SET is only executed once at the start of an iteration. CYG_FB_PIXEL0_VAR(FRAMEBUF); CYG_FB_PIXEL0_SET(FRAMEBUF, x, y); … CYG_FB_PIXEL0_FLUSHREL(FRAMEBUF, x, y, 0, 0); The GET macro retrieves the x and y coordinates corresponding to the current position. It is provided mainly for symmetry, but can prove useful for debugging. CYG_FB_PIXEL0_VAR(FRAMEBUF); CYG_FB_PIXEL0_SET(FRAMEBUF, x, y); … #ifdef DEBUG CYG_FB_PIXEL0_GET(FRAMEBUF, new_x, new_y); diag_printf("Halfway through: x now %d, y now %d\n", new_x, new_y); #endif … CYG_FB_PIXEL0_FLUSHREL(FRAMEBUF, x, y, 0, 0); The ADDX and ADDY macros adjust the current position. The most common increments are 1 and -1, moving to the next or previous pixel horizontally or vertically, but any increment can be used. CYG_FB_PIXEL0_VAR(FRAMEBUF); CYG_FB_PIXEL0_SET(FRAMEBUF, x, y); for (rows = height; rows; rows--) { for (columns = width; columns; columns--) { <perform operation> CYG_FB_PIXEL0_ADDX(FRAMEBUF, 1); } CYG_FB_PIXEL0_ADDX(FRAMEBUF, -1 * width); CYG_FB_PIXEL0_ADDY(FRAMEBUF, 1); } CYG_FB_PIXEL0_FLUSHREL(FRAMEBUF, x, y, width, 0); Here the current position is moved one pixel to the right each time around the inner loop. In the outer loop the position is first moved back to the start of the current row, then moved one pixel down. For the final flush the current x position is off by width, but the current y position is already correct. The final two macros READ and WRITE can be used to examine or update the current pixel value. CYG_FB_PIXEL0_VAR(FRAMEBUF); CYG_FB_PIXEL0_SET(FRAMEBUF, x, y); for (rows = height; rows; rows--) { for (columns = width; columns; columns--) { cyg_fb_colour colour = CYG_FB_PIXEL0_READ(FRAMEBUF); if (colour == colour_to_replace) { CYG_FB_PIXEL0_WRITE(FRAMEBUF, replacement); } CYG_FB_PIXEL0_ADDX(FRAMEBUF, 1); } CYG_FB_PIXEL0_ADDX(FRAMEBUF, -1 * width); CYG_FB_PIXEL0_ADDY(FRAMEBUF, 1); } CYG_FB_PIXEL0_FLUSHREL(FRAMEBUF, x, y, width, 0); Concurrent Iterations Although uncommon, in some cases application code may need to iterate over two or more blocks. An example might be an advanced block move where each copied pixel requires some processing. To support this there are PIXEL1, PIXEL2 and PIXEL3 variants of all the PIXEL0 macros. For example: CYG_FB_PIXEL0_VAR(FRAMEBUF); CYG_FB_PIXEL1_VAR(FRAMEBUF); CYG_FB_PIXEL0_SET(FRAMEBUF, dest_x, dest_y_); CYG_FB_PIXEL1_SET(FRAMEBUF, source_x, source_y); for (rows = height; rows; rows--) { for (columns = width; columns; columns--) { colour = CYG_FB_PIXEL1_READ(FRAMEBUF); <do some processing on colour> CYG_FB_PIXEL0_WRITE(FRAMEBUF, colour); CYG_FB_PIXEL0_ADDX(FRAMEBUF, 1); CYG_FB_PIXEL1_ADDX(FRAMEBUF, 1); } CYG_FB_PIXEL0_ADDX(FRAMEBUF, -100); CYG_FB_PIXEL0_ADDY(FRAMEBUF, 1); CYG_FB_PIXEL1_ADDX(FRAMEBUF, -100); CYG_FB_PIXEL1_ADDY(FRAMEBUF, 1); } CYG_FB_PIXEL0_FLUSHABS(FRAMEBUF, source_x, source_y, width, height); CYG_FB_PIXEL1_FLUSHABS(FRAMEBUF, 0, 0, 0, 0); // Only used for reading The PIXEL0, PIXEL1, PIXEL2 and PIXEL3 macros all use different local variables so there are no conflicts. The variable names also depend on the framebuffer device. If the target has two displays and two active framebuffer devices then the pixel macros can be used with the two devices without conflict: CYG_FB_PIXEL0_VAR(FRAMEBUF0); CYG_FB_PIXEL0_VAR(FRAMEBUF1); … Writing a Framebuffer Device Driver Porting writing a new framebuffer device driver Description As with most device drivers, the easiest way to write a new framebuffer package is to start with an existing one. Suitable ones include the PC VGA mode13 driver, an 8bpp paletted display, and the ARM iPAQ driver, a 16bpp true colour display. This document only outlines the process. Before writing any code it is necessary to decide how many framebuffer devices should be provided by the device driver. Each such device requires a cyg_fb structure and appropriate functions, and an identifier for use with the macro API plus associated macros. There are no hard rules here. Some device drivers may support just a single device, others may support many devices which drive the hardware in different modes or orientations. Optional functionality such as viewports and page flipping may be supported by having different cyg_fb devices, or by a number of configuration options which affect a single cyg_fb device. Usually providing multiple cyg_fb structures is harmless because the unused ones will get eliminated at link-time. Configuration The CDL for a framebuffer package is usually straightforward. A framebuffer package should be a hardware package and reside in the devs/framebuf hierarchy, further organized by architecture. Generic framebuffer packages, if any, can go into a generic subdirectory, and will normally rely on the platform HAL to provide some platform-specific information such as base addresses. The package should be part of the target definition and hence loaded automatically, but should be active_if CYGPKG_IO_FRAMEBUF so that the driver only gets built if the generic framebuffer support is explicitly added to the configuration. The configuration option CYGDAT_IO_FRAMEBUF_DEVICES should hold all the valid identifiers which can be used as the first argument for the macro API. This helps application developers to select the appropriate identifier, and allows higher-level graphics library packages to check that they have been configured correctly. This is achieved using something like the following, where mode13_320x200x8 is a valid identifier for the PC VGA driver: requires { is_substr(CYGDAT_IO_FRAMEBUF_DEVICES, " mode13_320x200x8 ") } The spaces ensure that the CDL inference engine keeps the identifiers separate. CYGPKG_IO_FRAMEBUF contains a number of interfaces which should be implemented by individual device drivers when appropriate. This is used to eliminate some code or data structure fields at compile-time, keeping down memory requirements. The interfaces are CYGHWR_IO_FRAMEBUF_FUNCTIONALITY_32BPP, CYGHWR_IO_FRAMEBUF_FUNCTIONALITY_TRUE_COLOUR, CYGHWR_IO_FRAMEBUF_FUNCTIONALITY_PALETTE, CYGHWR_IO_FRAMEBUF_FUNCTIONALITY_WRITEABLE_PALETTE, CYGHWR_IO_FRAMEBUF_FUNCTIONALITY_DOUBLE_BUFFER, and CYGHWR_IO_FRAMEBUF_FUNCTIONALITY_VIEWPORT. For example if a device driver provides a true colour display but fails to implement the relevant interface then functions like cyg_fb_make_colour will be no-ops. Device drivers for paletted displays should observe the generic configuration option CYGFUN_IO_FRAMEBUF_INSTALL_DEFAULT_PALETTE and install either cyg_fb_palette_ega or cyg_fb_palette_vga as part of their cyg_fb_on implementation. Exported Header File(s) Each framebuffer device driver should export one or more header files to cyg/io/framebufs. A custom build step in CYGPKG_IO_FRAMEBUF ensures that application code can just #include cyg/io/framebuf.h and this will automatically include the device-specific headers. Drivers may export one header per cyg_fb device or a single header for all devices, without affecting any code outside the device driver. Each exported header serves two purposes. First it defines the parameters, drawing primitive macros, and iteration macros for each device. Second it declares the cyg_fb structure. Parameters The parameter section should resemble the following: #define CYG_FB_320x240x16_STRUCT cyg_ipaq_fb_320x240x16 #define CYG_FB_320x240x16_DEPTH 16 #define CYG_FB_320x240x16_FORMAT CYG_FB_FORMAT_16BPP_TRUE_565 #define CYG_FB_320x240x16_WIDTH 320 #define CYG_FB_320x240x16_HEIGHT 240 #define CYG_FB_320x240x16_VIEWPORT_WIDTH 320 #define CYG_FB_320x240x16_VIEWPORT_HEIGHT 240 #define CYG_FB_320x240x16_FLAGS0 (CYG_FB_FLAGS0_LINEAR_FRAMEBUFFER | \ CYG_FB_FLAGS0_TRUE_COLOUR | \ CYG_FB_FLAGS0_BLANK | \ CYG_FB_FLAGS0_BACKLIGHT) #define CYG_FB_320x240x16_FLAGS1 0 #define CYG_FB_320x240x16_FLAGS2 0 #define CYG_FB_320x240x16_FLAGS3 0 #define CYG_FB_320x240x16_BASE ((void*)0x01FC0020) #define CYG_FB_320x240x16_STRIDE 640 Here 320x240x16 is the framebuffer identifier for use with the macro API. Application code like: #define FRAMEBUF 320x240x16 cyg_ucount16 width = CYG_FB_WIDTH(FRAMEBUF); will end up using the CYG_FB_320x240x16_WIDTH definition. To allow for efficient portable code all parameters must be compile-time constants. If the hardware may allow some of the parameters to be varied, for example different resolutions, then this should be handled either by defining separate devices for each resolution or by configuration options. The viewport width and height should always be defined. If the device driver does not support a viewport then these will be the same as the standard width and height. To allow for future expansion there are FLAGS1, FLAGS2 and FLAGS3 parameters. No flags are defined for these at present, but device drivers should still define the parameters. Drawing Primitives For each device the exported header file should define macros for the drawing primitives, using the same naming convention as for parameters. In the case of true colour displays there should also be macros for the make-colour and break-colour primitives: #define CYG_FB_320x240x16_WRITE_PIXEL(_x_, _y_, _colour_) … #define CYG_FB_320x240x16_READ_PIXEL(_x_, _y_) … #define CYG_FB_320x240x16_WRITE_HLINE(_x_, _y_, _len_, _colour_) … #define CYG_FB_320x240x16_WRITE_VLINE(_x_, _y_, _len_, _colour_) … #define CYG_FB_320x240x16_FILL_BLOCK(_x_, _y_, _w_, _h_, _colour_) … #define CYG_FB_320x240x16_WRITE_BLOCK(_x_, _y_, _w_, _h_, _data_, _off_, _s_) … #define CYG_FB_320x240x16_READ_BLOCK(_x_, _y_, _w_, _h_, _data_, _off_, _s_) … #define CYG_FB_320x240x16_MOVE_BLOCK(_x_, _y_, _w_, _h_, _new_x_, _new_y_) … #define CYG_FB_320x240x16_MAKE_COLOUR(_r_, _g_, _b_) … #define CYG_FB_320x240x16_BREAK_COLOUR(_colour_, _r_, _g_, _b_) … For typical linear framebuffers there are default implementations of all of these primitives in the generic framebuffer package, held in the exported header cyg/io/framebuf.inl. Hence the definitions will typically look something like: #include <cyg/io/framebuf.inl> … #define CYG_FB_320x240x16_WRITE_PIXEL(_x_, _y_, _colour_) \ CYG_MACRO_START \ cyg_fb_linear_write_pixel_16_inl(CYG_FB_320x240x16_BASE, \ CYG_FB_320x240x16_STRIDE, \ _x_, _y_, _colour_); \ CYG_MACRO_END #define CYG_FB_320x240x16_READ_PIXEL(_x_, _y_) \ ({ cyg_fb_linear_read_pixel_16_inl(CYG_FB_320x240x16_BASE, \ CYG_FB_320x240x16_STRIDE, \ _x_, _y_); }) … All of the drawing primitives have variants for the common display depths and layouts: 1le, 1be, 2le, 2be, 4le, 4be, 8, 16 and 32. The inlines take the framebuffer memory base address as the first argument, and the stride in bytes as the second. Similarly there are default definitions of the true colour primitives for 8BPP_TRUE_332, 16BPP_TRUE_565, 16BPP_TRUE_555, and 32BPP_TRUE_0888: #define CYG_FB_320x240x16_MAKE_COLOUR(_r_, _g_, _b_) \ ({ CYG_FB_MAKE_COLOUR_16BPP_TRUE_565(_r_, _g_, _b_); }) #define CYG_FB_320x240x16_BREAK_COLOUR(_colour_, _r_, _g_, _b_) \ CYG_MACRO_START \ CYG_FB_BREAK_COLOUR_16BPP_TRUE_565(_colour_, _r_, _g_, _b_); \ CYG_MACRO_END These default definitions assume the most common layout of colours within a pixel value, so for example CYG_FB_MAKE_COLOUR_16BPP_TRUE_565 assumes bits 0 to 4 hold the blue intensity, bits 5 to 10 the green, and bits 11 to 15 the red. If the hardware does not implement a linear framebuffer then obviously writing the device driver will be significantly more work. The macros will have to perform the operations themselves instead of relying on generic implementations. The required functionality should be obvious, and the generic implementations can still be consulted as a reference. For complicated hardware it may be appropriate to map the macros onto function calls, rather than try to implement everything inline. At the time of writing the support for linear framebuffers is incomplete. Only 8bpp, 16bpp and 32bpp depths have full support. There may also be future extensions, for example r90, r180 and r270 variants to support rotation in software, and db variants to support double-buffered displays. Iteration Macros In addition to the drawing primitives the exported header file should define iteration macros: #define CYG_FB_320x240x16_PIXELx_VAR( _fb_, _id_) … #define CYG_FB_320x240x16_PIXELx_SET( _fb_, _id_, _x_, _y_) … #define CYG_FB_320x240x16_PIXELx_GET( _fb_, _id_, _x_, _y_) … #define CYG_FB_320x240x16_PIXELx_ADDX( _fb_, _id_, _incr_) … #define CYG_FB_320x240x16_PIXELx_ADDY( _fb_, _id_, _incr_) … #define CYG_FB_320x240x16_PIXELx_WRITE(_fb_, _id_, _colour_) … #define CYG_FB_320x240x16_PIXELx_READ( _fb_, _id_)… #define CYG_FB_320x240x16_PIXELx_FLUSHABS( _fb_, _id_, _x0_, _y0_, _w_, _h_) … #define CYG_FB_320x240x16_PIXELx_FLUSHREL( _fb_, _id_, _x0_, _y0_, _dx_, _dy_) … The _fb_ argument will be the identifier, in this case 320x240x16, and the _id_ will be a small number, 0 for a PIXEL0 iteration, 1 for PIXEL1, and so on. Together these two should allow unique local variable names to be constructed. Again there are default definitions of the macros in cyg/io/framebuf.inl for linear framebuffers: #define CYG_FB_320x240x16_PIXELx_VAR( _fb_, _id_) \ CYG_FB_PIXELx_VAR_16( _fb_, _id_) #define CYG_FB_320x240x16_PIXELx_SET( _fb_, _id_, _x_, _y_) \ CYG_MACRO_START \ CYG_FB_PIXELx_SET_16( _fb_, _id_, \ CYG_FB_320x240x16_BASE, \ 320, _x_, _y_); \ CYG_MACRO_END The linear SET and GET macros take base and stride information. The ADDX and ADDY macros only need the stride. By convention most of the macros are wrapped in CYG_MACRO_START/CYG_MACRO_END or ({/}) pairs, allowing debug code to be inserted if necessary. However the _VAR macro must not be wrapped in this way: its purpose is to define one or more local variables; wrapping the macro would declare the variables in a new scope, inaccessible to the other macros. Again for non-linear framebuffers it will be necessary to implement these macros fully rather than rely on generic implementations, but the generic versions can be consulted as a reference. The <structname>cyg_fb</structname> declaration Finally there should be an export of the cyg_fb structure or structures. Typically this uses the _STRUCT parameter, reducing the possibility of an accidental mismatch between the macro and function APIs: extern cyg_fb CYG_FB_320x240x16_STRUCT; Driver-Specific Source Code Exporting parameters and macros in a header file is not enough. It is also necessary to actually define the cyg_fb structure or structures, and to provide hardware-specific versions of the control operations. For non-linear framebuffers it will also be necessary to provide the drawing functions. There is a utility macro CYG_FB_FRAMEBUFFER for instantiating a cyg_fb structure. Drivers may ignore this macro and do the work themselves, but at an increased risk of compatibility problems with future versions of the generic code. CYG_FB_FRAMEBUFFER(CYG_FB_320x240x16_STRUCT, CYG_FB_320x240x16_DEPTH, CYG_FB_320x240x16_FORMAT, CYG_FB_320x240x16_WIDTH, CYG_FB_320x240x16_HEIGHT, CYG_FB_320x240x16_VIEWPORT_WIDTH, CYG_FB_320x240x16_VIEWPORT_HEIGHT, CYG_FB_320x240x16_BASE, CYG_FB_320x240x16_STRIDE, CYG_FB_320x240x16_FLAGS0, CYG_FB_320x240x16_FLAGS1, CYG_FB_320x240x16_FLAGS2, CYG_FB_320x240x16_FLAGS3, 0, 0, 0, 0, // fb_driver0 -> fb_driver3 &cyg_ipaq_fb_on, &cyg_ipaq_fb_off, &cyg_ipaq_fb_ioctl, &cyg_fb_nop_synch, &cyg_fb_nop_read_palette, &cyg_fb_nop_write_palette, &cyg_fb_dev_make_colour_16bpp_true_565, &cyg_fb_dev_break_colour_16bpp_true_565, &cyg_fb_linear_write_pixel_16, &cyg_fb_linear_read_pixel_16, &cyg_fb_linear_write_hline_16, &cyg_fb_linear_write_vline_16, &cyg_fb_linear_fill_block_16, &cyg_fb_linear_write_block_16, &cyg_fb_linear_read_block_16, &cyg_fb_linear_move_block_16, 0, 0, 0, 0 // fb_spare0 -> fb_spare3 ); The first 13 arguments to the macro correspond to the device parameters. The next four are arbitrary CYG_ADDRWORD values for use by the device driver. Typically these are used to share on/off/ioctl functions between multiple cyg_fb structure. They are followed by function pointers: on/off/ioctl control; double buffer synch; palette management; true colour support; and the drawing primitives. nop versions of the on, off, ioctl, synch, palette management and true colour functions are provided by the generic framebuffer package, and often these arguments to the CYG_FB_FRAMEBUFFER macro will be discarded at compile-time because the relevant CDL interface is not implemented. The final four arguments are currently unused and should be 0. They are intended for future expansion, with a value of 0 indicating that a device driver does not implement non-core functionality. As with the macros there are default implementations of the true colour primitives for 8bpp_true_332, 16bpp_true_565, 16bpp_true_555 and 32bpp_true_0888, assuming the most common layout for these colour modes. There are also default implementations of the drawing primitives for linear framebuffers, with variants for the common display depths and layouts. Obviously non-linear framebuffers will need rather more work. Typically a true colour or grey scale framebuffer device driver will have to implement just three hardware-specific functions: int cyg_ipaq_fb_on(cyg_fb* fb) { … } int cyg_ipaq_fb_off(cyg_fb* fb) { … } int cyg_ipaq_fb_ioctl(cyg_fb* fb, cyg_ucount16 key, void* data, size_t* len) { int result; switch(key) { case CYG_FB_IOCTL_BLANK_GET: … … default: result = ENOSYS; break; } return result; } These control operations are entirely hardware-specific and cannot be implemented by generic code. Paletted displays will need two more functions, again hardware-specific: void cyg_pcvga_fb_read_palette(cyg_fb* fb, cyg_ucount32 first, cyg_ucount32 len, void* data) { … } void cyg_pcvga_fb_write_palette(cyg_fb* fb, cyg_ucount32 first, cyg_ucount32 len, const void* data, cyg_ucount16 when) { … } Future Expansion As has been mentioned before framebuffer hardware varies widely. The design of a generic framebuffer API requires complicated trade-offs between efficiency, ease of use, ease of porting, and still supporting a very wide range of hardware. To some extent this requires a lowest common denominator approach, but the design allows for some future expansion and optional support for more advanced features like hardware acceleration. The most obvious route for expansion is the ioctl interface. Device drivers can define their own keys, values 0x8000 and higher, for any operation. Alternatively a device driver does not have to implement just the interface provided by the generic framebuffer package: additional functions and macros can be exported as required. Currently there are only a small number of ioctl operations. Additional ones may get added in future, for example to support a hardware mouse cursor, but only in cases where the functionality is likely to be provided by a significant number of framebuffer devices. Adding new generic functionality adds to the maintenance overhead of both code and documentation. When a new generic ioctl operation is added there will usually also be one or more new flags, so that device drivers can indicate they support the functionality. At the time of writing only 12 of the 32 FLAGS0 flags are used, and a further 96 are available in FLAGS1, FLAGS2 and FLAGS3. Another route for future expansion is the four spare arguments to the CYG_FB_FRAMEBUFFER macro. As an example of how these may get used in future, consider support for 3d hardware acceleration. One of the spare fields would become another table of function pointers to the various accelerators, or possibly a structure. A FLAGS0 flag would indicate that the device driver implements such functionality. Other forms of expansion such as defining a new standard drawing primitive would be more difficult, since this would normally involve changing the CYG_FB_FRAMEBUFFER macro. Such expansion should not be necessary because the existing primitives provide all reasonable core functionality. Instead other packages such as graphics libraries can work on top of the existing primitives.