| /* |
| * Copyright (C) 2020 The Android Open Source Project |
| * |
| * 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 <android/hardware/graphics/mapper/3.0/IMapper.h> |
| #include <cutils/native_handle.h> |
| #include <sync/sync.h> |
| |
| #include "cb_handle_30.h" |
| #include "host_connection_session.h" |
| #include "FormatConversions.h" |
| #include "debug.h" |
| |
| #include "aemu/base/Tracing.h" |
| |
| #define ATRACE_TAG ATRACE_TAG_GRAPHICS |
| |
| const int kOMX_COLOR_FormatYUV420Planar = 19; |
| |
| using ::android::hardware::hidl_handle; |
| using ::android::hardware::hidl_vec; |
| using ::android::hardware::Return; |
| using ::android::hardware::Void; |
| |
| using ::android::hardware::graphics::common::V1_2::PixelFormat; |
| using ::android::hardware::graphics::common::V1_0::BufferUsage; |
| |
| namespace MapperV3 = ::android::hardware::graphics::mapper::V3_0; |
| |
| using IMapper3 = MapperV3::IMapper; |
| using Error3 = MapperV3::Error; |
| using YCbCrLayout3 = MapperV3::YCbCrLayout; |
| |
| namespace { |
| size_t align(const size_t v, const size_t a) { return (v + a - 1) & ~(a - 1); } |
| |
| static int waitFenceFd(const int fd, const char* logname) { |
| const int warningTimeout = 5000; |
| if (sync_wait(fd, warningTimeout) < 0) { |
| if (errno == ETIME) { |
| ALOGW("%s: fence %d didn't signal in %d ms", logname, fd, warningTimeout); |
| if (sync_wait(fd, -1) < 0) { |
| RETURN_ERROR(errno); |
| } else { |
| RETURN(0); |
| } |
| } else { |
| RETURN_ERROR(errno); |
| } |
| } else { |
| RETURN(0); |
| } |
| } |
| |
| int waitHidlFence(const hidl_handle& hidlHandle, const char* logname) { |
| const native_handle_t* nativeHandle = hidlHandle.getNativeHandle(); |
| |
| if (!nativeHandle) { |
| RETURN(0); |
| } |
| if (nativeHandle->numFds > 1) { |
| RETURN_ERROR(-EINVAL); |
| } |
| if (nativeHandle->numInts != 0) { |
| RETURN_ERROR(-EINVAL); |
| } |
| |
| return waitFenceFd(nativeHandle->data[0], logname); |
| } |
| |
| bool needGpuBuffer(const uint32_t usage) { |
| return usage & (BufferUsage::GPU_TEXTURE |
| | BufferUsage::GPU_RENDER_TARGET |
| | BufferUsage::COMPOSER_OVERLAY |
| | BufferUsage::COMPOSER_CLIENT_TARGET |
| | BufferUsage::GPU_DATA_BUFFER); |
| } |
| |
| constexpr uint64_t one64 = 1; |
| |
| constexpr uint64_t ones(int from, int to) { |
| return ((one64 << (to - from + 1)) - 1) << from; |
| } |
| |
| class GoldfishMapper : public IMapper3 { |
| public: |
| GoldfishMapper() : m_hostConn(HostConnection::createUnique()) { |
| GoldfishAddressSpaceHostMemoryAllocator host_memory_allocator(false); |
| CRASH_IF(!host_memory_allocator.is_opened(), |
| "GoldfishAddressSpaceHostMemoryAllocator failed to open"); |
| |
| GoldfishAddressSpaceBlock bufferBits; |
| CRASH_IF(host_memory_allocator.hostMalloc(&bufferBits, 256), |
| "hostMalloc failed"); |
| |
| m_physAddrToOffset = bufferBits.physAddr() - bufferBits.offset(); |
| |
| host_memory_allocator.hostFree(&bufferBits); |
| } |
| |
| Return<void> importBuffer(const hidl_handle& hh, |
| importBuffer_cb hidl_cb) { |
| native_handle_t* imported = nullptr; |
| const Error3 e = importBufferImpl(hh.getNativeHandle(), &imported); |
| if (e == Error3::NONE) { |
| hidl_cb(Error3::NONE, imported); |
| } else { |
| hidl_cb(e, nullptr); |
| } |
| return {}; |
| } |
| |
| Return<Error3> freeBuffer(void* raw) { |
| if (!raw) { |
| RETURN_ERROR(Error3::BAD_BUFFER); |
| } |
| cb_handle_30_t* cb = cb_handle_30_t::from(raw); |
| if (!cb) { |
| RETURN_ERROR(Error3::BAD_BUFFER); |
| } |
| |
| if (cb->mmapedSize > 0) { |
| GoldfishAddressSpaceBlock::memoryUnmap(cb->getBufferPtr(), cb->mmapedSize); |
| } |
| |
| native_handle_close(cb); |
| native_handle_delete(cb); |
| |
| RETURN(Error3::NONE); |
| } |
| |
| Return<void> lock(void* raw, |
| uint64_t cpuUsage, |
| const Rect& accessRegion, |
| const hidl_handle& acquireFence, |
| lock_cb hidl_cb) { |
| void* ptr = nullptr; |
| int32_t bytesPerPixel = 0; |
| int32_t bytesPerStride = 0; |
| |
| const Error3 e = lockImpl(raw, cpuUsage, accessRegion, acquireFence, |
| &ptr, &bytesPerPixel, &bytesPerStride); |
| if (e == Error3::NONE) { |
| hidl_cb(Error3::NONE, ptr, bytesPerPixel, bytesPerStride); |
| } else { |
| hidl_cb(e, nullptr, 0, 0); |
| } |
| return {}; |
| } |
| |
| Return<void> lockYCbCr(void* raw, |
| uint64_t cpuUsage, |
| const Rect& accessRegion, |
| const hidl_handle& acquireFence, |
| lockYCbCr_cb hidl_cb) { |
| YCbCrLayout3 ycbcr = {}; |
| const Error3 e = lockYCbCrImpl(raw, cpuUsage, accessRegion, acquireFence, |
| &ycbcr); |
| if (e == Error3::NONE) { |
| hidl_cb(Error3::NONE, ycbcr); |
| } else { |
| hidl_cb(e, {}); |
| } |
| return {}; |
| } |
| |
| Return<void> unlock(void* raw, unlock_cb hidl_cb) { |
| hidl_cb(unlockImpl(raw), {}); |
| return {}; |
| |
| } |
| |
| Return<void> createDescriptor(const BufferDescriptorInfo& description, |
| createDescriptor_cb hidl_cb) { |
| hidl_vec<uint32_t> raw; |
| encodeBufferDescriptorInfo(description, &raw); |
| hidl_cb(Error3::NONE, raw); |
| return {}; |
| } |
| |
| Return<void> isSupported(const IMapper::BufferDescriptorInfo& description, |
| isSupported_cb hidl_cb) { |
| hidl_cb(Error3::NONE, isSupportedImpl(description)); |
| return {}; |
| } |
| |
| Return<Error3> validateBufferSize(void* buffer, |
| const BufferDescriptorInfo& descriptor, |
| uint32_t stride) { |
| const cb_handle_30_t* cb = cb_handle_30_t::from(buffer); |
| if (cb) { |
| return validateBufferSizeImpl(*cb, descriptor, stride); |
| } else { |
| RETURN_ERROR(Error3::BAD_BUFFER); |
| } |
| } |
| |
| Return<void> getTransportSize(void* buffer, |
| getTransportSize_cb hidl_cb) { |
| const cb_handle_30_t* cb = cb_handle_30_t::from(buffer); |
| if (cb) { |
| hidl_cb(Error3::NONE, cb->numFds, cb->numInts); |
| } else { |
| hidl_cb(Error3::BAD_BUFFER, 0, 0); |
| } |
| |
| return {}; |
| } |
| |
| private: // **** impl **** |
| Error3 importBufferImpl(const native_handle_t* nh, native_handle_t** phandle) { |
| if (!nh) { |
| RETURN_ERROR(Error3::BAD_BUFFER); |
| } |
| native_handle_t* imported = native_handle_clone(nh); |
| if (!imported) { |
| RETURN_ERROR(Error3::BAD_BUFFER); |
| } |
| cb_handle_30_t* cb = cb_handle_30_t::from(imported); |
| if (!cb) { |
| native_handle_close(imported); |
| native_handle_delete(imported); |
| RETURN_ERROR(Error3::BAD_BUFFER); |
| } |
| |
| if (cb->mmapedSize > 0) { |
| LOG_ALWAYS_FATAL_IF(cb->bufferFdIndex < 0); |
| void* ptr; |
| const int res = GoldfishAddressSpaceBlock::memoryMap( |
| cb->getBufferPtr(), |
| cb->mmapedSize, |
| cb->fds[cb->bufferFdIndex], |
| cb->getMmapedOffset(), |
| &ptr); |
| if (res) { |
| native_handle_close(imported); |
| native_handle_delete(imported); |
| RETURN_ERROR(Error3::NO_RESOURCES); |
| } |
| cb->setBufferPtr(ptr); |
| } |
| |
| *phandle = imported; |
| RETURN(Error3::NONE); |
| } |
| |
| void setLocked(cb_handle_30_t* cb, const uint8_t checkedUsage, |
| const Rect& accessRegion) { |
| if (checkedUsage & BufferUsage::CPU_WRITE_MASK) { |
| cb->lockedLeft = accessRegion.left; |
| cb->lockedTop = accessRegion.top; |
| cb->lockedWidth = accessRegion.width; |
| cb->lockedHeight = accessRegion.height; |
| } else { |
| cb->lockedLeft = 0; |
| cb->lockedTop = 0; |
| cb->lockedWidth = cb->width; |
| cb->lockedHeight = cb->height; |
| } |
| cb->lockedUsage = checkedUsage; |
| } |
| |
| Error3 lockImpl(void* raw, |
| const uint64_t uncheckedUsage, |
| const Rect& accessRegion, |
| const hidl_handle& acquireFence, |
| void** pptr, |
| int32_t* pBytesPerPixel, |
| int32_t* pBytesPerStride) { |
| if (!raw) { |
| RETURN_ERROR(Error3::BAD_BUFFER); |
| } |
| cb_handle_30_t* cb = cb_handle_30_t::from(raw); |
| if (!cb) { |
| RETURN_ERROR(Error3::BAD_BUFFER); |
| } |
| if (cb->lockedUsage) { |
| RETURN_ERROR(Error3::BAD_VALUE); |
| } |
| const uint8_t checkedUsage = uncheckedUsage & cb->usage & |
| (BufferUsage::CPU_READ_MASK | BufferUsage::CPU_WRITE_MASK); |
| if (checkedUsage == 0) { |
| RETURN_ERROR(Error3::BAD_VALUE); |
| } |
| if (!cb->bufferSize) { |
| RETURN_ERROR(Error3::BAD_BUFFER); |
| } |
| char* const bufferBits = static_cast<char*>(cb->getBufferPtr()); |
| if (!bufferBits) { |
| RETURN_ERROR(Error3::BAD_BUFFER); |
| } |
| if (waitHidlFence(acquireFence, __func__)) { |
| RETURN_ERROR(Error3::BAD_VALUE); |
| } |
| |
| if (cb->hostHandle) { |
| const Error3 e = lockHostImpl(*cb, checkedUsage, accessRegion, bufferBits); |
| if (e != Error3::NONE) { |
| return e; |
| } |
| } |
| |
| setLocked(cb, checkedUsage, accessRegion); |
| |
| *pptr = bufferBits; |
| *pBytesPerPixel = cb->bytesPerPixel; |
| *pBytesPerStride = cb->bytesPerPixel * cb->stride; |
| RETURN(Error3::NONE); |
| } |
| |
| Error3 lockYCbCrImpl(void* raw, |
| const uint64_t uncheckedUsage, |
| const Rect& accessRegion, |
| const hidl_handle& acquireFence, |
| YCbCrLayout3* pYcbcr) { |
| if (!raw) { |
| RETURN_ERROR(Error3::BAD_BUFFER); |
| } |
| cb_handle_30_t* cb = cb_handle_30_t::from(raw); |
| if (!cb) { |
| RETURN_ERROR(Error3::BAD_BUFFER); |
| } |
| if (cb->lockedUsage) { |
| RETURN_ERROR(Error3::BAD_VALUE); |
| } |
| const uint8_t checkedUsage = uncheckedUsage & cb->usage & |
| (BufferUsage::CPU_READ_MASK | BufferUsage::CPU_WRITE_MASK); |
| if (checkedUsage == 0) { |
| RETURN_ERROR(Error3::BAD_VALUE); |
| } |
| if (!cb->bufferSize) { |
| RETURN_ERROR(Error3::BAD_BUFFER); |
| } |
| char* const bufferBits = static_cast<char*>(cb->getBufferPtr()); |
| if (!bufferBits) { |
| RETURN_ERROR(Error3::BAD_BUFFER); |
| } |
| if (waitHidlFence(acquireFence, __func__)) { |
| RETURN_ERROR(Error3::BAD_VALUE); |
| } |
| |
| size_t uOffset; |
| size_t vOffset; |
| size_t yStride; |
| size_t cStride; |
| size_t cStep; |
| switch (static_cast<PixelFormat>(cb->format)) { |
| case PixelFormat::YCRCB_420_SP: |
| yStride = cb->width; |
| cStride = yStride; |
| vOffset = yStride * cb->height; |
| uOffset = vOffset + 1; |
| cStep = 2; |
| break; |
| |
| case PixelFormat::YV12: |
| // https://developer.android.com/reference/android/graphics/ImageFormat.html#YV12 |
| yStride = align(cb->width, 16); |
| cStride = align(yStride / 2, 16); |
| vOffset = yStride * cb->height; |
| uOffset = vOffset + (cStride * cb->height / 2); |
| cStep = 1; |
| break; |
| |
| case PixelFormat::YCBCR_420_888: |
| yStride = cb->width; |
| cStride = yStride / 2; |
| uOffset = cb->height * yStride; |
| vOffset = uOffset + cStride * cb->height / 2; |
| cStep = 1; |
| break; |
| |
| case PixelFormat::YCBCR_P010: |
| yStride = cb->width * 2; |
| cStride = yStride; |
| uOffset = cb->height * yStride; |
| vOffset = uOffset + 2; |
| cStep = 4; |
| break; |
| |
| default: |
| ALOGE("%s:%d unexpected format (%d)", __func__, __LINE__, cb->format); |
| RETURN_ERROR(Error3::BAD_BUFFER); |
| } |
| |
| if (cb->hostHandle) { |
| const Error3 e = lockHostImpl(*cb, checkedUsage, accessRegion, bufferBits); |
| if (e != Error3::NONE) { |
| return e; |
| } |
| } |
| |
| setLocked(cb, checkedUsage, accessRegion); |
| |
| pYcbcr->y = bufferBits; |
| pYcbcr->cb = bufferBits + uOffset; |
| pYcbcr->cr = bufferBits + vOffset; |
| pYcbcr->yStride = yStride; |
| pYcbcr->cStride = cStride; |
| pYcbcr->chromaStep = cStep; |
| |
| RETURN(Error3::NONE); |
| } |
| |
| Error3 lockHostImpl(cb_handle_30_t& cb, |
| const uint8_t checkedUsage, |
| const Rect& accessRegion, |
| char* const bufferBits) { |
| const HostConnectionSession conn = getHostConnectionSession(); |
| ExtendedRCEncoderContext *const rcEnc = conn.getRcEncoder(); |
| const bool usageSwRead = (checkedUsage & BufferUsage::CPU_READ_MASK) != 0; |
| |
| const int res = rcEnc->rcColorBufferCacheFlush( |
| rcEnc, cb.hostHandle, 0, usageSwRead); |
| if (res < 0) { |
| RETURN_ERROR(Error3::NO_RESOURCES); |
| } |
| |
| if (usageSwRead) { |
| if (gralloc_is_yuv_format(cb.format)) { |
| if (rcEnc->hasYUVCache()) { |
| uint32_t bufferSize; |
| switch (static_cast<PixelFormat>(cb.format)) { |
| case PixelFormat::YV12: |
| get_yv12_offsets(cb.width, cb.height, |
| nullptr, nullptr, &bufferSize); |
| break; |
| case PixelFormat::YCBCR_420_888: |
| get_yuv420p_offsets(cb.width, cb.height, |
| nullptr, nullptr, &bufferSize); |
| break; |
| default: |
| CRASH("Unexpected format, switch is out of sync with gralloc_is_yuv_format"); |
| break; |
| } |
| |
| rcEnc->rcReadColorBufferYUV(rcEnc, cb.hostHandle, |
| 0, 0, cb.width, cb.height, |
| bufferBits, bufferSize); |
| } else { |
| // We are using RGB888 |
| std::vector<char> tmpBuf(cb.width * cb.height * 3); |
| rcEnc->rcReadColorBuffer(rcEnc, cb.hostHandle, |
| 0, 0, cb.width, cb.height, |
| cb.glFormat, cb.glType, |
| tmpBuf.data()); |
| switch (static_cast<PixelFormat>(cb.format)) { |
| case PixelFormat::YV12: |
| rgb888_to_yv12(bufferBits, tmpBuf.data(), |
| cb.width, cb.height, |
| accessRegion.left, |
| accessRegion.top, |
| accessRegion.left + accessRegion.width - 1, |
| accessRegion.top + accessRegion.height - 1); |
| break; |
| case PixelFormat::YCBCR_420_888: |
| rgb888_to_yuv420p(bufferBits, tmpBuf.data(), |
| cb.width, cb.height, |
| accessRegion.left, |
| accessRegion.top, |
| accessRegion.left + accessRegion.width - 1, |
| accessRegion.top + accessRegion.height - 1); |
| break; |
| default: |
| CRASH("Unexpected format, switch is out of sync with gralloc_is_yuv_format"); |
| break; |
| } |
| } |
| } else { |
| if (rcEnc->featureInfo()->hasReadColorBufferDma) { |
| { |
| AEMU_SCOPED_TRACE("bindDmaDirectly"); |
| rcEnc->bindDmaDirectly(bufferBits, |
| getMmapedPhysAddr(cb.getMmapedOffset())); |
| } |
| rcEnc->rcReadColorBufferDMA(rcEnc, |
| cb.hostHandle, |
| 0, 0, cb.width, cb.height, |
| cb.glFormat, cb.glType, |
| bufferBits, cb.width * cb.height * cb.bytesPerPixel); |
| } else { |
| rcEnc->rcReadColorBuffer(rcEnc, |
| cb.hostHandle, |
| 0, 0, cb.width, cb.height, |
| cb.glFormat, cb.glType, |
| bufferBits); |
| } |
| } |
| } |
| |
| RETURN(Error3::NONE); |
| } |
| |
| Error3 unlockImpl(void* raw) { |
| AEMU_SCOPED_TRACE("unlockImpl body"); |
| if (!raw) { |
| RETURN_ERROR(Error3::BAD_BUFFER); |
| } |
| cb_handle_30_t* cb = cb_handle_30_t::from(raw); |
| if (!cb) { |
| RETURN_ERROR(Error3::BAD_BUFFER); |
| } |
| if (cb->lockedUsage == 0) { |
| RETURN_ERROR(Error3::BAD_VALUE); |
| } |
| if (!cb->bufferSize) { |
| RETURN_ERROR(Error3::BAD_BUFFER); |
| } |
| char* const bufferBits = static_cast<char*>(cb->getBufferPtr()); |
| if (!bufferBits) { |
| RETURN_ERROR(Error3::BAD_BUFFER); |
| } |
| |
| if (cb->hostHandle) { |
| unlockHostImpl(*cb, bufferBits); |
| } |
| |
| cb->lockedLeft = 0; |
| cb->lockedTop = 0; |
| cb->lockedWidth = 0; |
| cb->lockedHeight = 0; |
| cb->lockedUsage = 0; |
| |
| RETURN(Error3::NONE); |
| } |
| |
| void unlockHostImpl(cb_handle_30_t& cb, char* const bufferBits) { |
| AEMU_SCOPED_TRACE("unlockHostImpl body"); |
| if (cb.lockedUsage & BufferUsage::CPU_WRITE_MASK) { |
| const int bpp = glUtilsPixelBitSize(cb.glFormat, cb.glType) >> 3; |
| const uint32_t rgbSize = cb.width * cb.height * bpp; |
| const char* bitsToSend; |
| uint32_t sizeToSend; |
| |
| if (gralloc_is_yuv_format(cb.format)) { |
| bitsToSend = bufferBits; |
| switch (static_cast<PixelFormat>(cb.format)) { |
| case PixelFormat::YV12: |
| get_yv12_offsets(cb.width, cb.height, nullptr, nullptr, &sizeToSend); |
| break; |
| case PixelFormat::YCBCR_420_888: |
| get_yuv420p_offsets(cb.width, cb.height, nullptr, nullptr, &sizeToSend); |
| break; |
| default: |
| CRASH("Unexpected format, switch is out of sync with gralloc_is_yuv_format"); |
| break; |
| } |
| } else { |
| bitsToSend = bufferBits; |
| sizeToSend = rgbSize; |
| } |
| |
| { |
| const HostConnectionSession conn = getHostConnectionSession(); |
| ExtendedRCEncoderContext *const rcEnc = conn.getRcEncoder(); |
| { |
| AEMU_SCOPED_TRACE("bindDmaDirectly"); |
| rcEnc->bindDmaDirectly(bufferBits, |
| getMmapedPhysAddr(cb.getMmapedOffset())); |
| } |
| { |
| AEMU_SCOPED_TRACE("updateColorBuffer"); |
| rcEnc->rcUpdateColorBufferDMA(rcEnc, cb.hostHandle, |
| 0, 0, cb.width, cb.height, |
| cb.glFormat, cb.glType, |
| const_cast<char*>(bitsToSend), |
| sizeToSend); |
| } |
| } |
| } |
| } |
| |
| /* BufferUsage bits that must be zero */ |
| static constexpr uint64_t kReservedUsage = |
| (one64 << 10) |
| | (one64 << 13) |
| | (one64 << 19) |
| | (one64 << 21) |
| | ones(25, 27) /* bits 25-27 must be zero and are reserved for future versions */ |
| | ones(32, 47); /* bits 32-47 must be zero and are reserved for future versions */ |
| |
| bool isSupportedImpl(const IMapper::BufferDescriptorInfo& descriptor) const { |
| if (!descriptor.width) { RETURN(false); } |
| if (!descriptor.height) { RETURN(false); } |
| if (descriptor.layerCount != 1) { RETURN(false); } |
| |
| const uint64_t usage64 = descriptor.usage; |
| if (usage64 & kReservedUsage) { |
| RETURN(false); |
| } |
| |
| const uint32_t usage = usage64; |
| |
| switch (descriptor.format) { |
| case PixelFormat::RGBA_8888: |
| case PixelFormat::RGBX_8888: |
| case PixelFormat::BGRA_8888: |
| case PixelFormat::RGB_565: |
| case PixelFormat::RGBA_FP16: |
| case PixelFormat::RGBA_1010102: |
| case PixelFormat::YV12: |
| case PixelFormat::YCBCR_420_888: |
| RETURN(true); |
| |
| case PixelFormat::IMPLEMENTATION_DEFINED: |
| RETURN(false); |
| |
| case PixelFormat::RGB_888: |
| case PixelFormat::YCRCB_420_SP: |
| case PixelFormat::RAW16: |
| case PixelFormat::Y16: |
| case PixelFormat::BLOB: |
| RETURN(!needGpuBuffer(usage)); |
| |
| default: |
| if (static_cast<int>(descriptor.format) == kOMX_COLOR_FormatYUV420Planar) { |
| return (usage & BufferUsage::VIDEO_DECODER) != 0; |
| } |
| |
| RETURN(false); |
| } |
| } |
| |
| Error3 validateBufferSizeImpl(const cb_handle_t& /*cb*/, |
| const BufferDescriptorInfo& /*descriptor*/, |
| uint32_t /*stride*/) { |
| RETURN(Error3::NONE); |
| } |
| |
| HostConnectionSession getHostConnectionSession() const { |
| return HostConnectionSession(m_hostConn.get()); |
| } |
| |
| static void encodeBufferDescriptorInfo(const BufferDescriptorInfo& d, |
| hidl_vec<uint32_t>* raw) { |
| raw->resize(5); |
| |
| (*raw)[0] = d.width; |
| (*raw)[1] = d.height; |
| (*raw)[2] = d.layerCount; |
| (*raw)[3] = static_cast<uint32_t>(d.format); |
| (*raw)[4] = d.usage & UINT32_MAX; |
| } |
| |
| uint64_t getMmapedPhysAddr(uint64_t offset) const { |
| return m_physAddrToOffset + offset; |
| } |
| |
| std::unique_ptr<HostConnection> m_hostConn; |
| uint64_t m_physAddrToOffset; |
| }; |
| } // namespace |
| |
| extern "C" IMapper3* HIDL_FETCH_IMapper(const char* /*name*/) { |
| return new GoldfishMapper; |
| } |