/* * Copyright (c) 2021 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "display_gralloc.h" #include <cerrno> #include <inttypes.h> #include <pthread.h> #include <stdio.h> #include <sys/mman.h> #include <sys/shm.h> #include <securec.h> #include "buffer_handle.h" #include "display_type.h" #include "disp_common.h" #include "hdf_log.h" #include "osal_mem.h" #define DEFAULT_READ_WRITE_PERMISSIONS 0666 #define MAX_MALLOC_SIZE 0x10000000L #define SHM_MAX_KEY 10000 #define SHM_START_KEY 1 #define INVALID_SHMID -1 #define BITS_PER_BYTE 8 #define DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d)) #define ALIGN_UP(x, a) ((((x) + ((a) - 1)) / (a)) * (a)) #define HEIGHT_ALIGN 2U #define WIDTH_ALIGN 8U #define MAX_PLANES 3 #undef HDF_LOG_TAG #define HDF_LOG_TAG display_gralloc_c typedef struct { BufferHandle hdl; int32_t shmid; } PriBufferHandle; typedef struct { uint32_t numPlanes; uint32_t radio[MAX_PLANES]; } PlaneLayoutInfo; typedef struct { uint32_t format; uint32_t bitsPerPixel; // bits per pixel for first plane const PlaneLayoutInfo *planes; } FormatInfo; struct GrallocManager { pthread_mutex_t mutex; pthread_mutexattr_t mutexattr; int32_t count; }; static struct GrallocManager g_grallocManager; static const PlaneLayoutInfo g_yuv420SPLayout = { .numPlanes = 2, .radio = { 4, 2 }, }; static const PlaneLayoutInfo g_yuv420PLayout = { .numPlanes = 3, .radio = { 4, 1, 1 }, }; static void GetGrallocMgr(void) { g_grallocManager.count++; } static int32_t PutGrallocMgr(void) { g_grallocManager.count--; return g_grallocManager.count; } static void LockGrallocMgr(void) { pthread_mutex_lock(&g_grallocManager.mutex); } static void UnlockGrallocMgr(void) { pthread_mutex_unlock(&g_grallocManager.mutex); } static const FormatInfo *GetFormatInfo(uint32_t format) { static const FormatInfo fmtInfos[] = { {PIXEL_FMT_RGBX_8888, 32, NULL}, {PIXEL_FMT_RGBA_8888, 32, NULL}, {PIXEL_FMT_BGRX_8888, 32, NULL}, {PIXEL_FMT_BGRA_8888, 32, NULL}, {PIXEL_FMT_RGB_888, 24, NULL}, {PIXEL_FMT_BGR_565, 16, NULL}, {PIXEL_FMT_RGBA_5551, 16, NULL}, {PIXEL_FMT_RGB_565, 16, NULL}, {PIXEL_FMT_BGRX_4444, 16, NULL}, {PIXEL_FMT_BGRA_4444, 16, NULL}, {PIXEL_FMT_RGBA_4444, 16, NULL}, {PIXEL_FMT_RGBX_4444, 16, NULL}, {PIXEL_FMT_BGRX_5551, 16, NULL}, {PIXEL_FMT_BGRA_5551, 16, NULL}, {PIXEL_FMT_YCBCR_420_SP, 8, &g_yuv420SPLayout}, {PIXEL_FMT_YCRCB_420_SP, 8, &g_yuv420SPLayout}, {PIXEL_FMT_YCBCR_420_P, 8, &g_yuv420PLayout}, {PIXEL_FMT_YCRCB_420_P, 8, &g_yuv420PLayout}, }; for (uint32_t i = 0; i < sizeof(fmtInfos) / sizeof(FormatInfo); i++) { if (fmtInfos[i].format == format) { return &fmtInfos[i]; } } HDF_LOGE("the format can not support %d %d", format, PIXEL_FMT_RGBA_8888); return NULL; } static uint32_t AdjustStrideFromFormat(uint32_t format, uint32_t width) { const FormatInfo *fmtInfo = GetFormatInfo(format); if ((fmtInfo != NULL) && (fmtInfo->planes != NULL)) { uint32_t sum = fmtInfo->planes->radio[0]; for (uint32_t i = 1; (i < fmtInfo->planes->numPlanes) && (i < MAX_PLANES); i++) { sum += fmtInfo->planes->radio[i]; } if (sum > 0) { width = DIV_ROUND_UP((width * sum), fmtInfo->planes->radio[0]); } } return width; } static int32_t InitBufferHandle(PriBufferHandle* buffer, const AllocInfo* info) { int32_t size; int32_t stride; int32_t h = ALIGN_UP(info->height, HEIGHT_ALIGN); const FormatInfo *fmtInfo = GetFormatInfo(info->format); if (fmtInfo == NULL) { HDF_LOGE("can not get format information : %d", buffer->hdl.format); return DISPLAY_FAILURE; } stride = ALIGN_UP(AdjustStrideFromFormat(info->format, info->width), WIDTH_ALIGN) * fmtInfo->bitsPerPixel / BITS_PER_BYTE; size = h * stride; buffer->hdl.width = info->width; buffer->hdl.stride = stride; buffer->hdl.height = info->height; buffer->hdl.size = size; buffer->hdl.usage = info->usage; buffer->hdl.fd = -1; buffer->shmid = INVALID_SHMID; buffer->hdl.format = info->format; buffer->hdl.reserveInts = (sizeof(PriBufferHandle) - sizeof(BufferHandle) - buffer->hdl.reserveFds * sizeof(uint32_t)) / sizeof(uint32_t); return DISPLAY_SUCCESS; } static int32_t AllocShm(BufferHandle *buffer) { static int32_t key = SHM_START_KEY; int32_t shmid; while ((shmid = shmget(key, buffer->size, IPC_CREAT | IPC_EXCL | DEFAULT_READ_WRITE_PERMISSIONS)) < 0) { if (errno != EEXIST) { HDF_LOGE("%s: fail to alloc the shared memory, errno = %d", __func__, errno); return DISPLAY_FAILURE; } key++; if (key >= SHM_MAX_KEY) { key = SHM_START_KEY; } } void *pBase = shmat(shmid, NULL, 0); if (pBase == ((void *)-1)) { HDF_LOGE("%s: Fail to attach the shared memory, errno = %d", __func__, errno); if (shmctl(shmid, IPC_RMID, 0) == -1) { HDF_LOGE("%s: Fail to free shmid, errno = %d", __func__, errno); } return DISPLAY_FAILURE; } buffer->virAddr = pBase; buffer->fd = key; ((PriBufferHandle*)buffer)->shmid = shmid; key++; if (memset_s(pBase, buffer->size, 0x0, buffer->size) != EOK) { HDF_LOGE("memset_s failure"); if (shmctl(shmid, IPC_RMID, 0) == -1) { HDF_LOGE("%s: Fail to free shmid, errno = %d", __func__, errno); } return DISPLAY_FAILURE; } if (key >= SHM_MAX_KEY) { key = SHM_START_KEY; } return DISPLAY_SUCCESS; } static int32_t AllocMem(const AllocInfo* info, BufferHandle **buffer) { int32_t ret; DISPLAY_CHK_RETURN((buffer == NULL), DISPLAY_NULL_PTR, HDF_LOGE("%s: in buffer is null", __func__)); DISPLAY_CHK_RETURN((info == NULL), DISPLAY_NULL_PTR, HDF_LOGE("%s: in info is null", __func__)); PriBufferHandle* priBuffer = (PriBufferHandle*)calloc(1, sizeof(PriBufferHandle)); DISPLAY_CHK_RETURN((priBuffer == NULL), DISPLAY_NULL_PTR, HDF_LOGE("%s: can not calloc errno : %d", __func__, errno)); ret = InitBufferHandle(priBuffer, info); DISPLAY_CHK_RETURN((ret != DISPLAY_SUCCESS), DISPLAY_FAILURE, HDF_LOGE("%s: can not init buffe handle", __func__); goto OUT); BufferHandle *bufferHdl = &priBuffer->hdl; DISPLAY_CHK_RETURN(((bufferHdl->size > MAX_MALLOC_SIZE) || (bufferHdl->size == 0)), DISPLAY_FAILURE, HDF_LOGE("%s: size is invalid %d ", __func__, bufferHdl->size); goto OUT); LockGrallocMgr(); if (bufferHdl->usage == HBM_USE_MEM_SHARE) { ret = AllocShm(bufferHdl); } else { HDF_LOGE("%s: not support memory usage: 0x%" PRIx64 "", __func__, bufferHdl->usage); ret = DISPLAY_NOT_SUPPORT; } OUT: if ((ret != DISPLAY_SUCCESS) && (bufferHdl != NULL)) { free(bufferHdl); bufferHdl = NULL; } *buffer = bufferHdl; UnlockGrallocMgr(); return ret; } static void FreeShm(BufferHandle *buffer) { CHECK_NULLPOINTER_RETURN(buffer->virAddr); if (shmdt(buffer->virAddr) == -1) { HDF_LOGE("%s: Fail to free shared memory, errno = %d", __func__, errno); } if (shmctl(((PriBufferHandle*)buffer)->shmid, IPC_RMID, 0) == -1) { HDF_LOGE("%s: Fail to free shmid, errno = %d", __func__, errno); } } static void FreeMem(BufferHandle *buffer) { CHECK_NULLPOINTER_RETURN(buffer); if ((buffer->size > MAX_MALLOC_SIZE) || (buffer->size == 0)) { HDF_LOGE("%s: size is invalid, buffer->size = %d", __func__, buffer->size); return; } LockGrallocMgr(); if (buffer->usage == HBM_USE_MEM_SHARE) { FreeShm(buffer); } else { HDF_LOGE("%s: not support memory usage: 0x%" PRIx64 "", __func__, buffer->usage); } UnlockGrallocMgr(); } static void *MmapShm(BufferHandle *buffer) { int32_t shmid; shmid = shmget(buffer->fd, buffer->size, IPC_EXCL | DEFAULT_READ_WRITE_PERMISSIONS); if (shmid < 0) { HDF_LOGE("%s: Fail to mmap the shared memory, errno = %d", __func__, errno); return NULL; } void *pBase = shmat(shmid, NULL, 0); if (pBase == ((void *)-1)) { HDF_LOGE("%s: Fail to attach the shared memory, errno = %d", __func__, errno); return NULL; } ((PriBufferHandle*)buffer)->shmid = shmid; HDF_LOGI("%s: Mmap shared memory succeed", __func__); return pBase; } static void *Mmap(BufferHandle *buffer) { void *temp = NULL; CHECK_NULLPOINTER_RETURN_VALUE(buffer, NULL); if ((buffer->size > MAX_MALLOC_SIZE) || (buffer->size == 0)) { HDF_LOGE("%s: size is invalid, buffer->size = %d", __func__, buffer->size); return NULL; } LockGrallocMgr(); if (buffer->usage == HBM_USE_MEM_SHARE) { temp = MmapShm(buffer); } else { HDF_LOGE("%s: not support memory usage: 0x%" PRIx64 "", __func__, buffer->usage); } UnlockGrallocMgr(); return temp; } static int32_t UnmapShm(BufferHandle *buffer) { if (shmdt(buffer->virAddr) == -1) { HDF_LOGE("%s: Fail to unmap shared memory errno = %d", __func__, errno); return DISPLAY_FAILURE; } int32_t shmid = ((PriBufferHandle*)buffer)->shmid; if ((shmid != INVALID_SHMID) && (shmctl(shmid, IPC_RMID, 0) == -1)) { HDF_LOGE("%s: Fail to free shmid, errno = %d", __func__, errno); } return DISPLAY_SUCCESS; } static int32_t Unmap(BufferHandle *buffer) { int32_t ret; CHECK_NULLPOINTER_RETURN_VALUE(buffer, DISPLAY_NULL_PTR); CHECK_NULLPOINTER_RETURN_VALUE(buffer->virAddr, DISPLAY_NULL_PTR); if ((buffer->size > MAX_MALLOC_SIZE) || (buffer->size == 0)) { HDF_LOGE("%s: size is invalid, buffer->size = %d", __func__, buffer->size); return DISPLAY_FAILURE; } LockGrallocMgr(); if (buffer->usage == HBM_USE_MEM_SHARE) { ret = UnmapShm(buffer); } else { HDF_LOGE("%s: not support memory usage: 0x%" PRIx64 "", __func__, buffer->usage); ret = DISPLAY_FAILURE; } UnlockGrallocMgr(); return ret; } int32_t GrallocInitialize(GrallocFuncs **funcs) { static GrallocFuncs *gFuncs = NULL; if (funcs == NULL) { HDF_LOGE("%s: funcs is null", __func__); return DISPLAY_NULL_PTR; } if (gFuncs == NULL) { gFuncs = (GrallocFuncs *)OsalMemCalloc(sizeof(GrallocFuncs)); if (gFuncs == NULL) { HDF_LOGE("%s: gFuncs is null", __func__); return DISPLAY_NULL_PTR; } pthread_mutexattr_init(&g_grallocManager.mutexattr); pthread_mutexattr_setpshared(&g_grallocManager.mutexattr, PTHREAD_PROCESS_SHARED); pthread_mutex_init(&g_grallocManager.mutex, &g_grallocManager.mutexattr); gFuncs->AllocMem = AllocMem; gFuncs->FreeMem = FreeMem; gFuncs->Mmap = Mmap; gFuncs->Unmap = Unmap; } *funcs = gFuncs; GetGrallocMgr(); HDF_LOGI("%s: gralloc initialize success", __func__); return DISPLAY_SUCCESS; } int32_t GrallocUninitialize(GrallocFuncs *funcs) { if (funcs == NULL) { HDF_LOGE("%s: funcs is null", __func__); return DISPLAY_NULL_PTR; } if (PutGrallocMgr() == 0) { pthread_mutexattr_destroy(&g_grallocManager.mutexattr); pthread_mutex_destroy(&g_grallocManager.mutex); OsalMemFree(funcs); } HDF_LOGI("%s: gralloc uninitialize success", __func__); return DISPLAY_SUCCESS; }