blob: 8fcb9f43a9cb707740d3ac848bc722b3e24a0218 [file] [log] [blame]
// Copyright 2017 The Chromium OS Authors. All rights reserved.
// 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/bind.h>
#include <base/logging.h>
#include <base/threading/thread.h>
#include "common/libcab_test_internal.h"
#include "cros-camera/camera_algorithm.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::Max());
break;
default:
status = -EINVAL;
}
thread_.task_runner()->PostTask(
FROM_HERE,
base::Bind(&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]);
}
}
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);
}
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_;
};
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);
}
} // 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};
}