2f579e15da
The GENLOCK_IOC_RELEASE ioctl is now deprecated. This was done because to avoid race conditions where the handle was released before another client tried to unlock it. (cherry picked from commit d1514357422ce130d3e2776f984294a9efeea83e) Change-Id: Iaf433a79dd0dad992cbf67180765718beec4eb1c CRs-fixed: 333141
326 lines
11 KiB
C++
326 lines
11 KiB
C++
/*
|
|
* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
|
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are
|
|
* met:
|
|
* * Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* * Redistributions in binary form must reproduce the above
|
|
* copyright notice, this list of conditions and the following
|
|
* disclaimer in the documentation and/or other materials provided
|
|
* with the distribution.
|
|
* * Neither the name of Code Aurora Forum, Inc. nor the names of its
|
|
* contributors may be used to endorse or promote products derived
|
|
* from this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
|
|
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
|
|
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
|
|
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
|
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
|
|
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
|
|
* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#include <cutils/log.h>
|
|
#include <cutils/native_handle.h>
|
|
#include <gralloc_priv.h>
|
|
#include <linux/genlock.h>
|
|
#include <fcntl.h>
|
|
#include <sys/ioctl.h>
|
|
|
|
#include "genlock.h"
|
|
|
|
#define GENLOCK_DEVICE "/dev/genlock"
|
|
|
|
namespace {
|
|
/* Internal function to map the userspace locks to the kernel lock types */
|
|
int get_kernel_lock_type(genlock_lock_type lockType)
|
|
{
|
|
int kLockType = 0;
|
|
// If the user sets both a read and write lock, higher preference is
|
|
// given to the write lock.
|
|
if (lockType & GENLOCK_WRITE_LOCK) {
|
|
kLockType = GENLOCK_WRLOCK;
|
|
} else if (lockType & GENLOCK_READ_LOCK) {
|
|
kLockType = GENLOCK_RDLOCK;
|
|
} else {
|
|
LOGE("%s: invalid lockType (lockType = %d)", __FUNCTION__, lockType);
|
|
return -1;
|
|
}
|
|
return kLockType;
|
|
}
|
|
|
|
/* Internal function to perform the actual lock/unlock operations */
|
|
genlock_status_t perform_lock_unlock_operation(native_handle_t *buffer_handle,
|
|
int lockType, int timeout)
|
|
{
|
|
if (private_handle_t::validate(buffer_handle)) {
|
|
LOGE("%s: handle is invalid", __FUNCTION__);
|
|
return GENLOCK_FAILURE;
|
|
}
|
|
|
|
private_handle_t *hnd = reinterpret_cast<private_handle_t*>(buffer_handle);
|
|
if ((hnd->flags & private_handle_t::PRIV_FLAGS_UNSYNCHRONIZED) == 0) {
|
|
if (hnd->genlockPrivFd < 0) {
|
|
LOGE("%s: the lock has not been created, or has not been attached",
|
|
__FUNCTION__);
|
|
return GENLOCK_FAILURE;
|
|
}
|
|
|
|
genlock_lock lock;
|
|
lock.op = lockType;
|
|
lock.flags = 0;
|
|
lock.timeout = timeout;
|
|
lock.fd = hnd->genlockHandle;
|
|
|
|
if (ioctl(hnd->genlockPrivFd, GENLOCK_IOC_LOCK, &lock)) {
|
|
LOGE("%s: GENLOCK_IOC_LOCK failed (lockType0x%x, err=%s fd=%d)", __FUNCTION__,
|
|
lockType, strerror(errno), hnd->fd);
|
|
if (ETIMEDOUT == errno)
|
|
return GENLOCK_TIMEDOUT;
|
|
|
|
return GENLOCK_FAILURE;
|
|
}
|
|
}
|
|
return GENLOCK_NO_ERROR;
|
|
}
|
|
|
|
/* Internal function to close the fd and release the handle */
|
|
void close_genlock_fd_and_handle(int& fd, int& handle)
|
|
{
|
|
if (fd >=0 ) {
|
|
close(fd);
|
|
fd = -1;
|
|
}
|
|
|
|
if (handle >= 0) {
|
|
close(handle);
|
|
handle = -1;
|
|
}
|
|
}
|
|
|
|
}
|
|
/*
|
|
* Create a genlock lock. The genlock lock file descriptor and the lock
|
|
* handle are stored in the buffer_handle.
|
|
*
|
|
* @param: handle of the buffer
|
|
* @return error status.
|
|
*/
|
|
genlock_status_t genlock_create_lock(native_handle_t *buffer_handle)
|
|
{
|
|
genlock_status_t ret = GENLOCK_NO_ERROR;
|
|
if (private_handle_t::validate(buffer_handle)) {
|
|
LOGE("%s: handle is invalid", __FUNCTION__);
|
|
return GENLOCK_FAILURE;
|
|
}
|
|
|
|
private_handle_t *hnd = reinterpret_cast<private_handle_t*>(buffer_handle);
|
|
#ifdef USE_GENLOCK
|
|
if ((hnd->flags & private_handle_t::PRIV_FLAGS_UNSYNCHRONIZED) == 0) {
|
|
// Open the genlock device
|
|
int fd = open(GENLOCK_DEVICE, O_RDWR);
|
|
if (fd < 0) {
|
|
LOGE("%s: open genlock device failed (err=%s)", __FUNCTION__,
|
|
strerror(errno));
|
|
return GENLOCK_FAILURE;
|
|
}
|
|
|
|
// Create a new lock
|
|
genlock_lock lock;
|
|
if (ioctl(fd, GENLOCK_IOC_NEW, NULL)) {
|
|
LOGE("%s: GENLOCK_IOC_NEW failed (error=%s)", __FUNCTION__,
|
|
strerror(errno));
|
|
close_genlock_fd_and_handle(fd, lock.fd);
|
|
ret = GENLOCK_FAILURE;
|
|
}
|
|
|
|
// Export the lock for other processes to be able to use it.
|
|
if (GENLOCK_FAILURE != ret) {
|
|
if (ioctl(fd, GENLOCK_IOC_EXPORT, &lock)) {
|
|
LOGE("%s: GENLOCK_IOC_EXPORT failed (error=%s)", __FUNCTION__,
|
|
strerror(errno));
|
|
close_genlock_fd_and_handle(fd, lock.fd);
|
|
ret = GENLOCK_FAILURE;
|
|
}
|
|
}
|
|
|
|
// Store the lock params in the handle.
|
|
hnd->genlockPrivFd = fd;
|
|
hnd->genlockHandle = lock.fd;
|
|
} else {
|
|
hnd->genlockHandle = 0;
|
|
}
|
|
#else
|
|
hnd->genlockHandle = 0;
|
|
#endif
|
|
return ret;
|
|
}
|
|
|
|
|
|
/*
|
|
* Release a genlock lock associated with the handle.
|
|
*
|
|
* @param: handle of the buffer
|
|
* @return error status.
|
|
*/
|
|
genlock_status_t genlock_release_lock(native_handle_t *buffer_handle)
|
|
{
|
|
genlock_status_t ret = GENLOCK_NO_ERROR;
|
|
#ifdef USE_GENLOCK
|
|
if (private_handle_t::validate(buffer_handle)) {
|
|
LOGE("%s: handle is invalid", __FUNCTION__);
|
|
return GENLOCK_FAILURE;
|
|
}
|
|
|
|
private_handle_t *hnd = reinterpret_cast<private_handle_t*>(buffer_handle);
|
|
if ((hnd->flags & private_handle_t::PRIV_FLAGS_UNSYNCHRONIZED) == 0) {
|
|
if (hnd->genlockPrivFd < 0) {
|
|
LOGE("%s: the lock is invalid", __FUNCTION__);
|
|
return GENLOCK_FAILURE;
|
|
}
|
|
|
|
// Close the fd and reset the parameters.
|
|
close_genlock_fd_and_handle(hnd->genlockPrivFd, hnd->genlockHandle);
|
|
}
|
|
#endif
|
|
return ret;
|
|
}
|
|
|
|
|
|
/*
|
|
* Attach a lock to the buffer handle passed via an IPC.
|
|
*
|
|
* @param: handle of the buffer
|
|
* @return error status.
|
|
*/
|
|
genlock_status_t genlock_attach_lock(native_handle_t *buffer_handle)
|
|
{
|
|
genlock_status_t ret = GENLOCK_NO_ERROR;
|
|
#ifdef USE_GENLOCK
|
|
if (private_handle_t::validate(buffer_handle)) {
|
|
LOGE("%s: handle is invalid", __FUNCTION__);
|
|
return GENLOCK_FAILURE;
|
|
}
|
|
|
|
private_handle_t *hnd = reinterpret_cast<private_handle_t*>(buffer_handle);
|
|
if ((hnd->flags & private_handle_t::PRIV_FLAGS_UNSYNCHRONIZED) == 0) {
|
|
// Open the genlock device
|
|
int fd = open(GENLOCK_DEVICE, O_RDWR);
|
|
if (fd < 0) {
|
|
LOGE("%s: open genlock device failed (err=%s)", __FUNCTION__,
|
|
strerror(errno));
|
|
return GENLOCK_FAILURE;
|
|
}
|
|
|
|
// Attach the local handle to an existing lock
|
|
genlock_lock lock;
|
|
lock.fd = hnd->genlockHandle;
|
|
if (ioctl(fd, GENLOCK_IOC_ATTACH, &lock)) {
|
|
LOGE("%s: GENLOCK_IOC_ATTACH failed (err=%s)", __FUNCTION__,
|
|
strerror(errno));
|
|
close_genlock_fd_and_handle(fd, lock.fd);
|
|
ret = GENLOCK_FAILURE;
|
|
}
|
|
|
|
// Store the relavant information in the handle
|
|
hnd->genlockPrivFd = fd;
|
|
}
|
|
#endif
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* Lock the buffer specified by the buffer handle. The lock held by the buffer
|
|
* is specified by the lockType. This function will block if a write lock is
|
|
* requested on the buffer which has previously been locked for a read or write
|
|
* operation. A buffer can be locked by multiple clients for read. An optional
|
|
* timeout value can be specified. By default, there is no timeout.
|
|
*
|
|
* @param: handle of the buffer
|
|
* @param: type of lock to be acquired by the buffer.
|
|
* @param: timeout value in ms. GENLOCK_MAX_TIMEOUT is the maximum timeout value.
|
|
* @return error status.
|
|
*/
|
|
genlock_status_t genlock_lock_buffer(native_handle_t *buffer_handle,
|
|
genlock_lock_type_t lockType,
|
|
int timeout)
|
|
{
|
|
genlock_status_t ret = GENLOCK_NO_ERROR;
|
|
#ifdef USE_GENLOCK
|
|
// Translate the locktype
|
|
int kLockType = get_kernel_lock_type(lockType);
|
|
if (-1 == kLockType) {
|
|
LOGE("%s: invalid lockType", __FUNCTION__);
|
|
return GENLOCK_FAILURE;
|
|
}
|
|
|
|
if (0 == timeout) {
|
|
LOGW("%s: trying to lock a buffer with timeout = 0", __FUNCTION__);
|
|
}
|
|
// Call the private function to perform the lock operation specified.
|
|
ret = perform_lock_unlock_operation(buffer_handle, kLockType, timeout);
|
|
#endif
|
|
return ret;
|
|
}
|
|
|
|
|
|
/*
|
|
* Unlocks a buffer that has previously been locked by the client.
|
|
*
|
|
* @param: handle of the buffer to be unlocked.
|
|
* @return: error status.
|
|
*/
|
|
genlock_status_t genlock_unlock_buffer(native_handle_t *buffer_handle)
|
|
{
|
|
genlock_status_t ret = GENLOCK_NO_ERROR;
|
|
#ifdef USE_GENLOCK
|
|
// Do the unlock operation by setting the unlock flag. Timeout is always
|
|
// 0 in this case.
|
|
ret = perform_lock_unlock_operation(buffer_handle, GENLOCK_UNLOCK, 0);
|
|
#endif
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* Blocks the calling process until the lock held on the handle is unlocked.
|
|
*
|
|
* @param: handle of the buffer
|
|
* @param: timeout value for the wait.
|
|
* return: error status.
|
|
*/
|
|
genlock_status_t genlock_wait(native_handle_t *buffer_handle, int timeout) {
|
|
#ifdef USE_GENLOCK
|
|
if (private_handle_t::validate(buffer_handle)) {
|
|
LOGE("%s: handle is invalid", __FUNCTION__);
|
|
return GENLOCK_FAILURE;
|
|
}
|
|
|
|
private_handle_t *hnd = reinterpret_cast<private_handle_t*>(buffer_handle);
|
|
if ((hnd->flags & private_handle_t::PRIV_FLAGS_UNSYNCHRONIZED) == 0) {
|
|
if (hnd->genlockPrivFd < 0) {
|
|
LOGE("%s: the lock is invalid", __FUNCTION__);
|
|
return GENLOCK_FAILURE;
|
|
}
|
|
|
|
if (0 == timeout)
|
|
LOGW("%s: timeout = 0", __FUNCTION__);
|
|
|
|
genlock_lock lock;
|
|
lock.fd = hnd->genlockHandle;
|
|
lock.timeout = timeout;
|
|
if (ioctl(hnd->genlockPrivFd, GENLOCK_IOC_WAIT, &lock)) {
|
|
LOGE("%s: GENLOCK_IOC_WAIT failed (err=%s)", __FUNCTION__, strerror(errno));
|
|
return GENLOCK_FAILURE;
|
|
}
|
|
}
|
|
#endif
|
|
return GENLOCK_NO_ERROR;
|
|
}
|