1/*
2 * Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
3
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
7 *   * Redistributions of source code must retain the above copyright
8 *     notice, this list of conditions and the following disclaimer.
9 *   * Redistributions in binary form must reproduce the above
10 *     copyright notice, this list of conditions and the following
11 *     disclaimer in the documentation and/or other materials provided
12 *     with the distribution.
13 *   * Neither the name of The Linux Foundation nor the names of its
14 *     contributors may be used to endorse or promote products derived
15 *     from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
18 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
20 * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
21 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
24 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
25 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
26 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
27 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30#include <cutils/log.h>
31#include <cutils/native_handle.h>
32#include <gralloc_priv.h>
33#ifdef USE_GENLOCK
34#include <linux/genlock.h>
35#endif
36#include <fcntl.h>
37#include <sys/ioctl.h>
38
39#include "genlock.h"
40
41#define GENLOCK_DEVICE "/dev/genlock"
42
43namespace {
44/* Internal function to map the userspace locks to the kernel lock types */
45    int get_kernel_lock_type(genlock_lock_type lockType)
46    {
47        int kLockType = 0;
48#ifdef USE_GENLOCK
49        // If the user sets both a read and write lock, higher preference is
50        // given to the write lock.
51        if (lockType & GENLOCK_WRITE_LOCK) {
52            kLockType = GENLOCK_WRLOCK;
53        } else if (lockType & GENLOCK_READ_LOCK) {
54            kLockType = GENLOCK_RDLOCK;
55        } else {
56            ALOGE("%s: invalid lockType (lockType = %d)",
57                  __FUNCTION__, lockType);
58            return -1;
59        }
60#endif
61        return kLockType;
62    }
63
64    /* Internal function to perform the actual lock/unlock operations */
65    genlock_status_t perform_lock_unlock_operation(native_handle_t *buffer_handle,
66                                                   int lockType, int timeout,
67                                                   int flags)
68    {
69#ifdef USE_GENLOCK
70        if (private_handle_t::validate(buffer_handle)) {
71            ALOGE("%s: handle is invalid", __FUNCTION__);
72            return GENLOCK_FAILURE;
73        }
74
75        private_handle_t *hnd = reinterpret_cast<private_handle_t*>
76                                (buffer_handle);
77        if ((hnd->flags & private_handle_t::PRIV_FLAGS_UNSYNCHRONIZED) == 0) {
78            if (hnd->genlockPrivFd < 0) {
79                ALOGE("%s: the lock has not been created,"
80                      "or has not been attached", __FUNCTION__);
81                return GENLOCK_FAILURE;
82            }
83
84            genlock_lock lock;
85            lock.op = lockType;
86            lock.flags = flags;
87            lock.timeout = timeout;
88            lock.fd = hnd->genlockHandle;
89
90#ifdef GENLOCK_IOC_DREADLOCK
91            if (ioctl(hnd->genlockPrivFd, GENLOCK_IOC_DREADLOCK, &lock)) {
92                ALOGE("%s: GENLOCK_IOC_DREADLOCK failed (lockType0x%x,"
93                       "err=%s fd=%d)", __FUNCTION__,
94                      lockType, strerror(errno), hnd->fd);
95                if (ETIMEDOUT == errno)
96                    return GENLOCK_TIMEDOUT;
97
98                return GENLOCK_FAILURE;
99            }
100#else
101            // depreciated
102            if (ioctl(hnd->genlockPrivFd, GENLOCK_IOC_LOCK, &lock)) {
103                ALOGE("%s: GENLOCK_IOC_LOCK failed (lockType0x%x, err=%s fd=%d)"
104                      ,__FUNCTION__, lockType, strerror(errno), hnd->fd);
105                if (ETIMEDOUT == errno)
106                    return GENLOCK_TIMEDOUT;
107
108                return GENLOCK_FAILURE;
109            }
110#endif
111        }
112#endif
113        return GENLOCK_NO_ERROR;
114    }
115
116    /* Internal function to close the fd and release the handle */
117    void close_genlock_fd_and_handle(int& fd, int& handle)
118    {
119        if (fd >=0 ) {
120            close(fd);
121            fd = -1;
122        }
123
124        if (handle >= 0) {
125            close(handle);
126            handle = -1;
127        }
128    }
129}
130/*
131 * Create a genlock lock. The genlock lock file descriptor and the lock
132 * handle are stored in the buffer_handle.
133 *
134 * @param: handle of the buffer
135 * @return error status.
136 */
137genlock_status_t genlock_create_lock(native_handle_t *buffer_handle)
138{
139    genlock_status_t ret = GENLOCK_NO_ERROR;
140#ifdef USE_GENLOCK
141    if (private_handle_t::validate(buffer_handle)) {
142        ALOGE("%s: handle is invalid", __FUNCTION__);
143        return GENLOCK_FAILURE;
144    }
145
146    private_handle_t *hnd = reinterpret_cast<private_handle_t*>(buffer_handle);
147    if ((hnd->flags & private_handle_t::PRIV_FLAGS_UNSYNCHRONIZED) == 0) {
148        // Open the genlock device
149        int fd = open(GENLOCK_DEVICE, O_RDWR);
150        if (fd < 0) {
151            ALOGE("%s: open genlock device failed (err=%s)", __FUNCTION__,
152                  strerror(errno));
153            return GENLOCK_FAILURE;
154        }
155
156        // Create a new lock
157        genlock_lock lock;
158        if (ioctl(fd, GENLOCK_IOC_NEW, NULL)) {
159            ALOGE("%s: GENLOCK_IOC_NEW failed (error=%s)", __FUNCTION__,
160                  strerror(errno));
161            close_genlock_fd_and_handle(fd, lock.fd);
162            ret = GENLOCK_FAILURE;
163        }
164
165        // Export the lock for other processes to be able to use it.
166        if (GENLOCK_FAILURE != ret) {
167            if (ioctl(fd, GENLOCK_IOC_EXPORT, &lock)) {
168                ALOGE("%s: GENLOCK_IOC_EXPORT failed (error=%s)", __FUNCTION__,
169                      strerror(errno));
170                close_genlock_fd_and_handle(fd, lock.fd);
171                ret = GENLOCK_FAILURE;
172            }
173        }
174
175        // Store the lock params in the handle.
176        hnd->genlockPrivFd = fd;
177        hnd->genlockHandle = lock.fd;
178    } else {
179        hnd->genlockHandle = 0;
180    }
181#endif
182    return ret;
183}
184
185
186/*
187 * Release a genlock lock associated with the handle.
188 *
189 * @param: handle of the buffer
190 * @return error status.
191 */
192genlock_status_t genlock_release_lock(native_handle_t *buffer_handle)
193{
194    genlock_status_t ret = GENLOCK_NO_ERROR;
195#ifdef USE_GENLOCK
196    if (private_handle_t::validate(buffer_handle)) {
197        ALOGE("%s: handle is invalid", __FUNCTION__);
198        return GENLOCK_FAILURE;
199    }
200
201    private_handle_t *hnd = reinterpret_cast<private_handle_t*>(buffer_handle);
202    if ((hnd->flags & private_handle_t::PRIV_FLAGS_UNSYNCHRONIZED) == 0) {
203        if (hnd->genlockPrivFd < 0) {
204            ALOGE("%s: the lock is invalid", __FUNCTION__);
205            return GENLOCK_FAILURE;
206        }
207
208        // Close the fd and reset the parameters.
209        close_genlock_fd_and_handle(hnd->genlockPrivFd, hnd->genlockHandle);
210    }
211#endif
212    return ret;
213}
214
215
216/*
217 * Attach a lock to the buffer handle passed via an IPC.
218 *
219 * @param: handle of the buffer
220 * @return error status.
221 */
222genlock_status_t genlock_attach_lock(native_handle_t *buffer_handle)
223{
224    genlock_status_t ret = GENLOCK_NO_ERROR;
225#ifdef USE_GENLOCK
226    if (private_handle_t::validate(buffer_handle)) {
227        ALOGE("%s: handle is invalid", __FUNCTION__);
228        return GENLOCK_FAILURE;
229    }
230
231    private_handle_t *hnd = reinterpret_cast<private_handle_t*>(buffer_handle);
232    if ((hnd->flags & private_handle_t::PRIV_FLAGS_UNSYNCHRONIZED) == 0) {
233        // Open the genlock device
234        int fd = open(GENLOCK_DEVICE, O_RDWR);
235        if (fd < 0) {
236            ALOGE("%s: open genlock device failed (err=%s)", __FUNCTION__,
237                  strerror(errno));
238            return GENLOCK_FAILURE;
239        }
240
241        // Attach the local handle to an existing lock
242        genlock_lock lock;
243        lock.fd = hnd->genlockHandle;
244        if (ioctl(fd, GENLOCK_IOC_ATTACH, &lock)) {
245            ALOGE("%s: GENLOCK_IOC_ATTACH failed (err=%s)", __FUNCTION__,
246                  strerror(errno));
247            close_genlock_fd_and_handle(fd, lock.fd);
248            ret = GENLOCK_FAILURE;
249        }
250
251        // Store the relavant information in the handle
252        hnd->genlockPrivFd = fd;
253    }
254#endif
255    return ret;
256}
257
258/*
259 * Lock the buffer specified by the buffer handle. The lock held by the buffer
260 * is specified by the lockType. This function will block if a write lock is
261 * requested on the buffer which has previously been locked for a read or write
262 * operation. A buffer can be locked by multiple clients for read. An optional
263 * timeout value can be specified. By default, there is no timeout.
264 *
265 * @param: handle of the buffer
266 * @param: type of lock to be acquired by the buffer.
267 * @param: timeout value in ms. GENLOCK_MAX_TIMEOUT is the maximum timeout value.
268 * @return error status.
269 */
270genlock_status_t genlock_lock_buffer(native_handle_t *buffer_handle,
271                                     genlock_lock_type_t lockType,
272                                     int timeout)
273{
274    genlock_status_t ret = GENLOCK_NO_ERROR;
275#ifdef USE_GENLOCK
276    // Translate the locktype
277    int kLockType = get_kernel_lock_type(lockType);
278    if (-1 == kLockType) {
279        ALOGE("%s: invalid lockType", __FUNCTION__);
280        return GENLOCK_FAILURE;
281    }
282
283    if (0 == timeout) {
284        ALOGW("%s: trying to lock a buffer with timeout = 0", __FUNCTION__);
285    }
286    // Call the private function to perform the lock operation specified.
287    ret = perform_lock_unlock_operation(buffer_handle, kLockType, timeout, 0);
288#endif
289    return ret;
290}
291
292
293/*
294 * Unlocks a buffer that has previously been locked by the client.
295 *
296 * @param: handle of the buffer to be unlocked.
297 * @return: error status.
298 */
299genlock_status_t genlock_unlock_buffer(native_handle_t *buffer_handle)
300{
301    genlock_status_t ret = GENLOCK_NO_ERROR;
302#ifdef USE_GENLOCK
303    // Do the unlock operation by setting the unlock flag. Timeout is always
304    // 0 in this case.
305    ret = perform_lock_unlock_operation(buffer_handle, GENLOCK_UNLOCK, 0, 0);
306#endif
307    return ret;
308}
309
310/*
311 * Blocks the calling process until the lock held on the handle is unlocked.
312 *
313 * @param: handle of the buffer
314 * @param: timeout value for the wait.
315 * return: error status.
316 */
317genlock_status_t genlock_wait(native_handle_t *buffer_handle, int timeout) {
318#ifdef USE_GENLOCK
319    if (private_handle_t::validate(buffer_handle)) {
320        ALOGE("%s: handle is invalid", __FUNCTION__);
321        return GENLOCK_FAILURE;
322    }
323
324    private_handle_t *hnd = reinterpret_cast<private_handle_t*>(buffer_handle);
325    if ((hnd->flags & private_handle_t::PRIV_FLAGS_UNSYNCHRONIZED) == 0) {
326        if (hnd->genlockPrivFd < 0) {
327            ALOGE("%s: the lock is invalid", __FUNCTION__);
328            return GENLOCK_FAILURE;
329        }
330
331        if (0 == timeout)
332            ALOGW("%s: timeout = 0", __FUNCTION__);
333
334        genlock_lock lock;
335        lock.fd = hnd->genlockHandle;
336        lock.timeout = timeout;
337        if (ioctl(hnd->genlockPrivFd, GENLOCK_IOC_WAIT, &lock)) {
338            ALOGE("%s: GENLOCK_IOC_WAIT failed (err=%s)",  __FUNCTION__,
339                  strerror(errno));
340            return GENLOCK_FAILURE;
341        }
342    }
343#endif
344    return GENLOCK_NO_ERROR;
345}
346
347/*
348 * Convert a write lock that we own to a read lock
349 *
350 * @param: handle of the buffer
351 * @param: timeout value for the wait.
352 * return: error status.
353 */
354genlock_status_t genlock_write_to_read(native_handle_t *buffer_handle,
355                                       int timeout) {
356    genlock_status_t ret = GENLOCK_NO_ERROR;
357#ifdef USE_GENLOCK
358    if (0 == timeout) {
359        ALOGW("%s: trying to lock a buffer with timeout = 0", __FUNCTION__);
360    }
361    // Call the private function to perform the lock operation specified.
362#ifdef GENLOCK_IOC_DREADLOCK
363    ret = perform_lock_unlock_operation(buffer_handle, GENLOCK_RDLOCK, timeout,
364                                        GENLOCK_WRITE_TO_READ);
365#else
366    // depreciated
367    ret = perform_lock_unlock_operation(buffer_handle, GENLOCK_RDLOCK,
368                                        timeout, 0);
369#endif
370#endif
371    return ret;
372}
373