blob: 5dedb16a2c7a3be9bbe2668a091f65f897e126f4 [file] [log] [blame]
// Copyright 2017 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <unordered_map>
#include <base/functional/bind.h>
#include <base/logging.h>
#include <base/memory/writable_shared_memory_region.h>
#include <base/threading/thread.h>
#include "common/libcab_test_internal.h"
#include "cros-camera/camera_algorithm.h"
#include "cros-camera/common.h"
#include "cros-camera/future.h"
namespace libcab_test {
class CameraAlgorithmImpl {
public:
static CameraAlgorithmImpl* GetInstance() {
static CameraAlgorithmImpl impl;
return &impl;
}
int32_t Initialize(const camera_algorithm_callback_ops_t* callback_ops) {
if (!callback_ops) {
return -EINVAL;
}
callback_ops_ = callback_ops;
return 0;
}
int32_t RegisterBuffer(int buffer_fd) {
if (handles_.find(buffer_fd) != handles_.end()) {
LOGF(ERROR) << "Buffer already registered";
return -EINVAL;
}
struct stat sb;
if (fstat(buffer_fd, &sb) == -1) {
LOGF(ERROR) << "Failed to get buffer status";
return -EBADFD;
}
void* addr = mmap(0, sb.st_size, PROT_WRITE, MAP_SHARED, buffer_fd, 0);
if (!addr) {
LOGF(ERROR) << "Failed to map buffer";
return -EBADFD;
}
int32_t handle = -1;
static unsigned int seed = time(NULL) + getpid();
do {
handle = rand_r(&seed);
} while (shm_info_map_.find(handle) != shm_info_map_.end());
handles_[buffer_fd] = handle;
shm_info_map_[handle].fd = buffer_fd;
shm_info_map_[handle].addr = addr;
shm_info_map_[handle].size = sb.st_size;
return handle;
}
void Request(uint32_t req_id,
const uint8_t req_header[],
uint32_t size,
int32_t buffer_handle) {
uint32_t status = 0;
switch (req_header[0]) {
case REQUEST_TEST_COMMAND_NORMAL:
if (shm_info_map_.find(buffer_handle) == shm_info_map_.end()) {
LOGF(ERROR) << "Invalid buffer handle";
status = -EBADF;
}
break;
case REQUEST_TEST_COMMAND_VERIFY_STATUS:
status = SimpleHash(req_header, size);
break;
case REQUEST_TEST_COMMAND_DEAD_LOCK:
base::PlatformThread::Sleep(base::TimeDelta::FiniteMax());
break;
case REQUEST_TEST_COMMAND_VERIFY_UPDATE:
thread_.task_runner()->PostTask(
FROM_HERE, base::BindOnce(&CameraAlgorithmImpl::Update,
base::Unretained(this), req_id));
return;
default:
status = -EINVAL;
}
thread_.task_runner()->PostTask(
FROM_HERE,
base::BindOnce(&CameraAlgorithmImpl::ReturnCallback,
base::Unretained(this), req_id, status, buffer_handle));
}
void DeregisterBuffers(const int32_t buffer_handles[], uint32_t size) {
for (uint32_t i = 0; i < size; i++) {
if (shm_info_map_.find(buffer_handles[i]) == shm_info_map_.end()) {
LOGF(ERROR) << "Invalid buffer handle (" << buffer_handles[i] << ")";
continue;
}
handles_.erase(shm_info_map_[buffer_handles[i]].fd);
munmap(shm_info_map_[buffer_handles[i]].addr,
shm_info_map_[buffer_handles[i]].size);
close(shm_info_map_[buffer_handles[i]].fd);
shm_info_map_.erase(buffer_handles[i]);
}
}
void UpdateReturn(uint32_t upd_id, uint32_t status, int buffer_fd) {
if (buffer_fd != platform_shm_.GetPlatformHandle().fd) {
LOGF(ERROR) << "Wrong buffer returned from update";
return;
}
thread_.task_runner()->PostTask(
FROM_HERE, base::BindOnce(&CameraAlgorithmImpl::ReturnCallback,
base::Unretained(this), upd_id, status, -1));
}
private:
CameraAlgorithmImpl()
: thread_("Camera Algorithm Thread"), callback_ops_(nullptr) {
thread_.Start();
}
void ReturnCallback(uint32_t req_id, uint32_t status, int32_t buffer_handle) {
(*callback_ops_->return_callback)(callback_ops_, req_id, status,
buffer_handle);
}
void Update(uint32_t upd_id) {
const size_t kShmBufferSize = 2048;
std::vector<uint8_t> upd_header(sizeof(uint32_t));
base::WritableSharedMemoryRegion shm_region =
base::WritableSharedMemoryRegion::Create(kShmBufferSize);
if (!shm_region.IsValid()) {
LOGF(ERROR) << "Failed to create shared memory region";
return;
}
base::WritableSharedMemoryMapping shm_mapping = shm_region.Map();
if (!shm_mapping.IsValid()) {
LOGF(ERROR) << "Failed to create shared memory mapping";
}
uint8_t* write_ptr = shm_mapping.GetMemoryAs<uint8_t>();
if (write_ptr == nullptr) {
LOGF(ERROR) << "Failed to get a pointer to the shared memory";
return;
}
unsigned int seed = time(NULL) + getpid();
for (size_t size = 0; size < kShmBufferSize; size++) {
*(write_ptr + size) = rand_r(&seed);
}
uint32_t hashcode = SimpleHash(write_ptr, kShmBufferSize);
*static_cast<uint32_t*>(static_cast<void*>(upd_header.data())) = hashcode;
platform_shm_ =
base::WritableSharedMemoryRegion::TakeHandleForSerialization(
std::move(shm_region));
(*callback_ops_->update)(callback_ops_, upd_id, upd_header.data(),
upd_header.size(),
platform_shm_.GetPlatformHandle().fd);
}
base::Thread thread_;
const camera_algorithm_callback_ops_t* callback_ops_;
typedef struct {
int32_t fd;
void* addr;
size_t size;
} ShmInfo;
// Store shared memory fd and mapped address with handle as the key
std::unordered_map<int32_t, ShmInfo> shm_info_map_;
// Store handles with fd as the key
std::unordered_map<int32_t, int32_t> handles_;
base::subtle::PlatformSharedMemoryRegion platform_shm_;
};
static int32_t Initialize(const camera_algorithm_callback_ops_t* callback_ops) {
return CameraAlgorithmImpl::GetInstance()->Initialize(callback_ops);
}
static int32_t RegisterBuffer(int32_t buffer_fd) {
return CameraAlgorithmImpl::GetInstance()->RegisterBuffer(buffer_fd);
}
static void Request(uint32_t req_id,
const uint8_t req_header[],
uint32_t size,
int32_t buffer_handle) {
CameraAlgorithmImpl::GetInstance()->Request(req_id, req_header, size,
buffer_handle);
}
static void DeregisterBuffers(const int32_t buffer_handles[], uint32_t size) {
CameraAlgorithmImpl::GetInstance()->DeregisterBuffers(buffer_handles, size);
}
static void UpdateReturn(uint32_t upd_id, uint32_t status, int buffer_fd) {
CameraAlgorithmImpl::GetInstance()->UpdateReturn(upd_id, status, buffer_fd);
}
} // namespace libcab_test
extern "C" {
camera_algorithm_ops_t CAMERA_ALGORITHM_MODULE_INFO_SYM
__attribute__((__visibility__("default"))) = {
.initialize = libcab_test::Initialize,
.register_buffer = libcab_test::RegisterBuffer,
.request = libcab_test::Request,
.deregister_buffers = libcab_test::DeregisterBuffers,
.update_return = libcab_test::UpdateReturn};
}