FrameBuffer driver analysis this article introduces the display driver framework in Linux. Each display is abstracted as a frame buffer and registered to the FrameBuffer module, create the corresponding fbX device in the/dev/graphics directory. The Android system provides a Gralloc module in the hardware abstraction layer, which encapsulates all access operations on the frame buffer. Before using the frame buffer, a user space application must first load the Gralloc module and obtain a gralloc device and an fb device. With the gralloc device, applications in the user space can apply to allocate a graphic buffer and map the graphic buffer to the address space of the application, so that you can write the content of the screen to be drawn to it. Finally, the application in the user space uses the fb device to render the prepared graphic buffer to the frame buffer, that is, to draw the content of the graphic buffer to the display. Correspondingly, when an application in a user space no longer needs to use a graphic buffer, it can be released through the gralloc device and unbound from the address space.
The source code of the Gralloc module is located at: hardware/libhardware/modules/gralloc
.
── Android. mk
├ ── Framebuffer. cpp
├ ── Gralloc. cpp
── Gralloc_priv.h
├ ── Gr. h
── Er. cpp
Android Hardware abstraction Hardware Library Loading Process source code analysis introduces the loading process of the Hardware abstraction layer module in the Android system, and points out that each Hardware Abstraction Layer module must define the HAL_MODULE_INFO_SYM symbol, it has its own unique ID, and Gralloc is no exception. The Gralloc module ID is defined:
#define GRALLOC_HARDWARE_MODULE_ID "gralloc"
At the same time, a structure with HAL_MODULE_INFO_SYM as the symbol type private_module_t is defined:
Hardware \ libhardware \ modules \ gralloc. cpp
static struct hw_module_methods_t gralloc_module_methods = { open: gralloc_device_open};struct private_module_t HAL_MODULE_INFO_SYM = { base: { common: { tag: HARDWARE_MODULE_TAG, version_major: 1, version_minor: 0, id: GRALLOC_HARDWARE_MODULE_ID, name: "Graphics Memory Allocator Module", author: "The Android Open Source Project", methods: &gralloc_module_methods }, registerBuffer: gralloc_register_buffer, unregisterBuffer: gralloc_unregister_buffer, lock: gralloc_lock, unlock: gralloc_unlock, }, framebuffer: 0, flags: 0, numBuffers: 0, bufferMask: 0, lock: PTHREAD_MUTEX_INITIALIZER, currentBuffer: 0,};
After the Gralloc module is loaded into the memory by using the source code analysis method of the Hardware Library Loading Process in Android Hardware, you can call the dlsym function to obtain the derived symbol HMI, the first member variable type of private_module_t is gralloc_module_t, so it is also the first address of gralloc_module_t. Because the first member variable type of gralloc_module_t is hw_module_t, therefore, it is also the first address of hw_module_t, so as long as you get the addresses of one of the three types of variables, you can convert them to the other two types of pointers.
Data structure definition
Before analyzing the Gralloc module, we first introduce some data structures defined by the Gralloc module. Private_module_t is used to describe the system frame buffer information under the Gralloc module.
Struct private_module_t {gralloc_module_t base; private_handle_t * framebuffer; // The handle uint32_t flags pointing to the system frame buffer; // It indicates whether the system frame buffer supports dual-buffer uint32_t numBuffers; // indicates the number of graphic buffers uint32_t bufferMask contained in the system frame buffer; // records the usage of the graphic buffer in the System Frame Buffer pthread_mutex_t lock; // A mutex lock, used to protect the structure private_module_t from parallel access to buffer_handle_t currentBuffer; // used to describe the currently rendered graphic buffer int pmem_master; void * pmem_master_base; struct fb_var_screeninfo info; // Save the dynamic attributes of the device display, struct fb_fix_screeninfo finfo; // Save the fixed attributes of the device display, float xdpi; // describe the width of the device display, float ydpi; // description of the display height of the device float fps; // used to describe the refresh frequency of the display };
Framebuffer_device_t is used to describe the information of the system frame buffer device.
Typedef struct framebuffer_device_t {struct hw_device_t common; const uint32_t flags; // The const uint32_t width used to record the system frame buffer; // It is used to describe the width const uint32_t height of; // It is used to describe the height const int stride of the device display; // It is used to describe the number of pixels in a row of the device display, const int format; // It is used to describe the pixel format const float xdpi of the system frame buffer; // It is used to describe the density of the display screen on the width const float ydpi; // It is used to describe the density of the device display at the height of const float fps; // It is used to describe the refreshing frequency of the device display. const int minSwapInterval; // used to describe the minimum time interval const int maxSwapInterval between the two image buffers before and after frame buffer swap; // used to describe the maximum time interval int reserved [8] of the two image buffers before and after frame buffer swap; // reserved // used to set the minimum and maximum time interval int (* setSwapInterval) (struct framebuffer_device_t * window, int interval) of the two image buffers before and after frame buffer swap ); // set the update area int (* setUpdateRect) (struct framebuffer_device_t * window, int left, int top, int width, int height) of the frame buffer ); // used to render the buffer content of the graphic buffer to the frame buffer to int (* post) (struct framebuffer_device_t * dev, buffer_handle_t buffer); // used to notify the fb device, the combination of graphic buffers has completed int (* compositionComplete) (struct framebuffer_device_t * dev); void (* dump) (struct framebuffer_device_t * dev, char * buff, int buff_len ); int (* enableScreen) (struct framebuffer_device_t * dev, int enable); // retain void * reserved_proc [6];} framebuffer_device_t;
Gralloc_module_t is used to describe gralloc module information.
Typedef struct gralloc_module_t {struct hw_module_t common; // map a graphic buffer to the address space of a process to int (* registerBuffer) (struct gralloc_module_t const * module, buffer_handle_t handle ); // unmap a graphic buffer to the address space of a process (* unregisterBuffer) (struct gralloc_module_t const * module, buffer_handle_t handle ); // lock a specified graphic buffer int (* lock) (struct gralloc_module_t const * module, buffer_handle_t handle, int usage, int l, int t, int w, int h, void ** vaddr); // unlock a specified graph buffer int (* unlock) (struct gralloc_module_t const * module, buffer_handle_t handle); int (* perform) (struct gralloc_module_t const * module, int operation ,...); void * reserved_proc [7];} gralloc_module_t;
Alloc_device_t is used to describe the information of the gralloc device.
Typedef struct alloc_device_t {struct hw_device_t common; // used to allocate a graphic buffer int (* alloc) (struct alloc_device_t * dev, int w, int h, int format, int usage, buffer_handle_t * handle, int * stride); // used to release the specified graphic buffer int (* free) (struct alloc_device_t * dev, buffer_handle_t handle); void (* dump) (struct alloc_device_t * dev, char * buff, int buff_len); void * reserved_proc [7];} alloc_device_t;
Typedef struct hw_module_t {uint32_t tag; // tag uint16_t version_major; // the module's main device number uint16_t version_minor; // the module's secondary device number const char * id; // module ID const char * name; // Module name const char * author; // module author struct hw_module_methods_t * methods; // module operation method void * dso; // Save the first address of the module uint32_t reserved [32-7]; // reserved position} hw_module_t;
The Gralloc module of the hardware abstraction layer defines the device fb and device gpu:
#define GRALLOC_HARDWARE_FB0 "fb0"#define GRALLOC_HARDWARE_GPU0 "gpu0"
The device gpu is used to allocate the graphic buffer while the device fb is used to render the graphic buffer. hw_module_t is used to describe the Gralloc module of the hardware abstraction layer, while hw_device_t is used to describe the Gralloc device of the hardware abstraction layer, the hardware abstraction layer device can find the corresponding hardware abstraction layer module. In the Gralloc module, both fb devices and gpu devices are used to process the graphic buffer. The following is the data structure definition of the buffer:
Private_handle_t is used to describe a buffer. Android provides two methods for defining the buffer: C and C ++. The C language compiler defines the buffer:
Struct private_handle_t {struct native_handle nativeHandle; enum {PRIV_FLAGS_FRAMEBUFFER = 0x00000001}; int fd; // points to a file descriptor, which either points to the frame buffer device, either point to an anonymous shared memory int magic; int flags; // used to describe a buffer. When the flag value of a buffer is equal to PRIV_FLAGS_FRAMEBUFFER, it indicates that it is allocated in the frame buffer. Int size; // used to describe the int offset of a buffer; // used to describe the int base of the offset address of a buffer; // used to describe the actual address int pid of a buffer; // PID used to describe the creator of a buffer };
C ++ compiler definition:
Struct private_handle_t: public native_handle {enum {PRIV_FLAGS_FRAMEBUFFER = 0x00000001}; int fd; // point to a file descriptor, which either points to the frame buffer device, either point to an anonymous shared memory int magic; // point to a magic number. Its value is specified by the static member variable sMagic to identify a private_handle_t struct. Int flags; // The flag used to describe a buffer. Its value is either 0 or PRIV_FLAGS_FRAMEBUFFER int size. // It is used to describe the size of a buffer. Int offset; // used to describe the offset address of a buffer. Int base; // used to describe the actual address of a buffer. It is calculated using the offset of the member variable. Int pid; // The PID used to describe the creator of a buffer. Static const int sNumInts = 6; // contains 6 integers static const int sNumFds = 1; // contains 1 file descriptor static const int sMagic = 0x3141592 ;};
The private_handle_t definitions under the two compilers inherit from native_handle. The definition of native_handle is as follows:
Typedef struct native_handle {int version; // It is set to the size of the native_handle_t struct to identify the int numFds version of the struct native_handle_t; // It indicates the number of file descriptors contained in the struct native_handle, these file descriptors are stored in a buffer zone pointed to by the member variable data. Int numInts; // indicates the number of integers contained in the native_handle_t struct. These integers are stored in a buffer zone pointed to by the member variable data. Int data [0]; // point to a buffer} native_handle_t; typedef const native_handle_t * buffer_handle_t;
The opening process of the Gralloc module has been analyzed in detail in the source code analysis of the Android Hardware abstraction Hardware library loading process. The following analyzes the opening process of the two devices in the Gralloc module.
Fb device opening process
The ID value of the fb device is defined as # defineGRALLOC_HARDWARE_FB0 "fb0". The fb device uses the struct framebuffer_device_t to describe it. The struct framebuffer_device_t is used to describe the information of the system frame buffer.
Hardware \ libhardware \ include \ hardware \ fb. h
static inline int framebuffer_open(const struct hw_module_t* module, struct framebuffer_device_t** device) { return module->methods->open(module,GRALLOC_HARDWARE_FB0, (struct hw_device_t**)device);}
The module points to an hw_module_t struct used to describe the Gralloc module. As mentioned above, the member function open pointing to an hw_module_methods_t struct pointed to the Gralloc module function gralloc_device_open
Hardware \ libhardware \ modules \ gralloc. cpp
int gralloc_device_open(const hw_module_t* module, const char* name, hw_device_t** device){ int status = -EINVAL; if (!strcmp(name, GRALLOC_HARDWARE_GPU0)) { ... } else { status = fb_device_open(module, name, device); } return status;}
The gralloc_device_open function enables the fb device or the gpu device. The device name is used to differentiate the opened device. For the fb device, the fb_device_open function is called to enable the device.
Hardware \ libhardware \ modules \ gralloc \ framebuffer. cpp
Int fb_device_open (hw_module_t const * module, const char * name, hw_device_t ** device) {int status =-EINVAL; // determine if (! Strcmp (name, GRALLOC_HARDWARE_FB0) {alloc_device_t * gralloc_device; // enable the gpu device status = gralloc_open (module, & gralloc_device); if (status <0) return status; // create an fb_context_t object to describe the fb device context fb_context_t * dev = (fb_context_t *) malloc (sizeof (* dev); memset (dev, 0, sizeof (* dev); // initialize the fb_context_t object dev-> device. common. tag = HARDWARE_DEVICE_TAG; dev-> device. common. version = 0; dev-> device. common. module = Const_cast
This function is mainly used to create an fb_context_t struct and initialize its member variable device. The member variable of the struct fb_context_t is of the type framebuffer_device_t, which is used to describe the fb device. The fb device is mainly used to render the graphic buffer, which is implemented by calling its member function post. The post function of the fb device opened by function fb_device_open is set to the fb_post function in the Gralloc module. In addition to obtaining information about the system frame buffer, mapFrameBuffer also maps the system frame buffer to the address space of the current process. Line_length is used to describe the total number of bytes occupied by a row of pixels on the display. bits_per_pixel is used to describe the number of digits occupied by each pixel on the display. The value of bits_per_pixel shifts three places to the right, the number of bytes occupied by each pixel on the display screen. Use the total number of bytes occupied by display pixels line_length divided by the number of bytes occupied by each pixel to obtain the number of pixels on the display line and save them in stride.
stride = line_length / ( bits_per_pixel >> 3)
static int mapFrameBuffer(struct private_module_t* module){ pthread_mutex_lock(&module->lock); int err = mapFrameBufferLocked(module); pthread_mutex_unlock(&module->lock); return err;}
Call the mapFrameBufferLocked function to execute the ing process. This function is completed under thread protection.
Int mapFrameBufferLocked (struct private_module_t * module) {// already initialized... if (module-> framebuffer) {return 0;} char const * const device_template [] = {"/dev/graphics/fb % u ", "/dev/fb % u", 0}; int fd =-1; int I = 0; char name [64]; // check whether the device file/dev/graphics/fb0 or/dev/fb0 exists. If yes, call the function open to open it and save the obtained file descriptor in the variable fd while (fd =-1) & device_template [I]). {snprintf (name, 64, device_template [I], 0); fd = open (name, O_RDWR, 0); I ++;} if (fd <0) return-errno; // use the I/O Control Command FBIOGET_FSCREENINFO to obtain the fixed information of the System Frame Buffer, which is stored in the finfo structure of fb_fix_screeninfo; if (ioctl (fd, FBIOGET_FSCREENINFO, & finfo) =-1) return-errno; // use the I/O Control Command FBIOGET_VSCREENINFO to obtain variable information about the system frame buffer., Which is stored in the structure info of fb_var_screeninfo; if (ioctl (fd, FBIOGET_VSCREENINFO, & info) =-1) return-errno; // initialize info. reserved [0] = 0; info. reserved [1] = 0; info. reserved [2] = 0; info. xoffset = 0; info. yoffset = 0; info. activate = FB_ACTIVATE_NOW; // The member variables xres and yres of fb_var_screeninfo are used to describe the visual resolution of the display, while the member variables xres_virtual and yres_virtual are used to describe the virtual resolution of the display. // Set the virtual resolution height to NUM_BUFFERS times the visual resolution height. Info. yres_virtual = info. yres * NUM_BUFFERS; // 2 uint32_t flags = PAGE_FLIP; // set the virtual resolution of the device display if (ioctl (fd, FBIOPUT_VSCREENINFO, & info) =-1) {// setting failed. Reset the virtual resolution info of the display. yres_virtual = info. yres; flags & = ~ PAGE_FLIP; ALOGW ("FBIOPUT_VSCREENINFO failed, page flipping not supported");} if (info. yres_virtual <info. yres * 2) {// we need at least 2 for page-flipping info. yres_virtual = info. yres; flags & = ~ PAGE_FLIP; ALOGW ("page flipping not supported (yres_virtual = % d, requested = % d)", info. yres_virtual, info. yres * 2);} // use the I/O Control Command FBIOGET_VSCREENINFO to obtain the variable information of the System Frame Buffer. if (ioctl (fd, FBIOGET_VSCREENINFO, & info) =-1) return-errno; // calculate the refresh frequency of the device display. uint64_t refreshQuotient = (uint64_t (info. upper_margin + info. lower_margin + info. yres) * (info. left_margin + info. right_margin + info. xres) * info. pixclock );// The info. pixclock of the simulator is 0, so the calculated refreshQuotient = 0 int refreshRate = refreshQuotient> 0? (Int) (000000000000000llu/refreshQuotient): 0; // if it is a simulator, set the refresh frequency to 60Hz if (refreshRate = 0) {refreshRate = 60*1000; // 60Hz} if (int (info. width) <= 0 | int (info. height) <= 0) {info. width = (info. xres * 25.4f)/160.0f + 0.5f); info. height = (info. yres * 25.4f)/160.0f + 0.5f);} // calculate the density float xdpi = (info. xres * 25.4f)/info. width; float ydpi = (info. yres * 25.4f)/info. height; float fps = RefreshRate/1000.0f; // obtain the fixed information of the system frame buffer using the IO control command FBIOGET_FSCREENINFO again. if (ioctl (fd, FBIOGET_FSCREENINFO, & finfo) =-1) return-errno; if (finfo. smem_len <= 0) return-errno; // obtain other information about the system frame buffer to initialize a private_module_t struct described by the parameter module. Module-> flags = flags; module-> info = info; module-> finfo = finfo; module-> xdpi = xdpi; module-> ydpi = ydpi; module-> fps = fps; int err; // the size of the entire system frame buffer = the height of the virtual resolution value info. yres_virtual * The number of bytes occupied by each row. finfo. line_length, and align the size of the entire system frame buffer to the page boundary size_t fbSize = roundUpToPageSize (finfo. line_length * info. yres_virtual); // create a private_handle_t to describe the information of the entire system frame buffer. module-> framebuffer = new private_handle_t (dup (fd), fbSize, 0 ); // calculate the number of graphic buffers that can be divided into the entire system frame buffer to use module-> numBuffers = info. yres_virtual/info. yres; // indicates that all the graphic buffers in the system frame buffer are in idle status. module-> bufferMask = 0; // map the frame buffer to the current process address space in the read/write sharing mode void * vaddr = mmap (0, fbSize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0 ); if (vaddr = MAP_FAILED) {ALOGE ("Error mapping the framebuffer (% s)", strerror (errno); return-errno ;} // Save the starting address of the system frame buffer in the address space of the current process to the base of the private_handle_t domain module-> framebuffer-> base = intptr_t (vaddr ); // clear the frame buffer memset (vaddr, 0, fbSize); return 0 ;}
Before learning about this section, you must first understand the Linux FrameBuffer driver. For more information, see the FrameBuffer driver analysis.
When a Gpu device is opened, the gralloc device uses the struct alloc_device_t to describe it. The alloc_device_t struct has two member functions: alloc and free, which are used to allocate and release the graphic buffer respectively. The ID value of the gralloc device is defined:
#defineGRALLOC_HARDWARE_GPU0"gpu0"
Hardware \ libhardware \ include \ hardware \ gralloc. h
static inline int gralloc_open(const struct hw_module_t* module, struct alloc_device_t** device) { return module->methods->open(module, GRALLOC_HARDWARE_GPU0, (struct hw_device_t**)device);}
Module points to an hw_module_t struct used to describe the Gralloc module. Its member variable methods points to the open member function of an hw_module_methods_t struct pointing to the Gralloc function gralloc_device_open. The gralloc_device_open function allows you to enable the fb device or gpu device. The input device name is GRALLOC_HARDWARE_GPU0, indicating that the gpu device is currently on.
Hardware \ libhardware \ modules \ gralloc. cpp
int gralloc_device_open(const hw_module_t* module, const char* name, hw_device_t** device){ int status = -EINVAL; if (!strcmp(name, GRALLOC_HARDWARE_GPU0)) { gralloc_context_t *dev; dev = (gralloc_context_t*)malloc(sizeof(*dev)); /* initialize our state here */ memset(dev, 0, sizeof(*dev)); /* initialize the procs */ dev->device.common.tag = HARDWARE_DEVICE_TAG; dev->device.common.version = 0; dev->device.common.module = const_castThis function is mainly used to create a gralloc_context_t struct and initialize its member variable device. The type of the device member variable of the struct gralloc_context_t is gralloc_device_t, which is used to describe a gralloc device. As mentioned above, the gralloc device is used to allocate and release the graphic buffer, which is achieved by calling its member functions alloc and free. It can be seen from this that the member functions alloc and free of the gralloc device opened by gralloc_device_open are respectively set to gralloc_alloc and gralloc_free functions in the Gralloc module.