* 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;
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;
}