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#include <linux/genlock.h>
34#include <fcntl.h>
35#include <sys/ioctl.h>
36
37#include "genlock.h"
38
39#define GENLOCK_DEVICE "/dev/genlock"
40
41namespace {
42/* Internal function to map the userspace locks to the kernel lock types */
43    int get_kernel_lock_type(genlock_lock_type lockType)
44    {
45        int kLockType = 0;
46#ifdef USE_GENLOCK
47        // If the user sets both a read and write lock, higher preference is
48        // given to the write lock.
49        if (lockType & GENLOCK_WRITE_LOCK) {
50            kLockType = GENLOCK_WRLOCK;
51        } else if (lockType & GENLOCK_READ_LOCK) {
52            kLockType = GENLOCK_RDLOCK;
53        } else {
54            ALOGE("%s: invalid lockType (lockType = %d)",
55                  __FUNCTION__, lockType);
56            return -1;
57        }
58#endif
59        return kLockType;
60    }
61
62    /* Internal function to perform the actual lock/unlock operations */
63    genlock_status_t perform_lock_unlock_operation(native_handle_t *buffer_handle,
64                                                   int lockType, int timeout,
65                                                   int flags)
66    {
67#ifdef USE_GENLOCK
68        if (private_handle_t::validate(buffer_handle)) {
69            ALOGE("%s: handle is invalid", __FUNCTION__);
70            return GENLOCK_FAILURE;
71        }
72
73        private_handle_t *hnd = reinterpret_cast<private_handle_t*>
74                                (buffer_handle);
75        if ((hnd->flags & private_handle_t::PRIV_FLAGS_UNSYNCHRONIZED) == 0) {
76            if (hnd->genlockPrivFd < 0) {
77                ALOGE("%s: the lock has not been created,"
78                      "or has not been attached", __FUNCTION__);
79                return GENLOCK_FAILURE;
80            }
81
82            genlock_lock lock;
83            lock.op = lockType;
84            lock.flags = flags;
85            lock.timeout = timeout;
86            lock.fd = hnd->genlockHandle;
87
88#ifdef GENLOCK_IOC_DREADLOCK
89            if (ioctl(hnd->genlockPrivFd, GENLOCK_IOC_DREADLOCK, &lock)) {
90                ALOGE("%s: GENLOCK_IOC_DREADLOCK failed (lockType0x%x,"
91                       "err=%s fd=%d)", __FUNCTION__,
92                      lockType, strerror(errno), hnd->fd);
93                if (ETIMEDOUT == errno)
94                    return GENLOCK_TIMEDOUT;
95
96                return GENLOCK_FAILURE;
97            }
98#else
99            // depreciated
100            if (ioctl(hnd->genlockPrivFd, GENLOCK_IOC_LOCK, &lock)) {
101                ALOGE("%s: GENLOCK_IOC_LOCK failed (lockType0x%x, err=%s fd=%d)"
102                      ,__FUNCTION__, lockType, strerror(errno), hnd->fd);
103                if (ETIMEDOUT == errno)
104                    return GENLOCK_TIMEDOUT;
105
106                return GENLOCK_FAILURE;
107            }
108#endif
109        }
110#endif
111        return GENLOCK_NO_ERROR;
112    }
113
114    /* Internal function to close the fd and release the handle */
115    void close_genlock_fd_and_handle(int& fd, int& handle)
116    {
117        if (fd >=0 ) {
118            close(fd);
119            fd = -1;
120        }
121
122        if (handle >= 0) {
123            close(handle);
124            handle = -1;
125        }
126    }
127}
128/*
129 * Create a genlock lock. The genlock lock file descriptor and the lock
130 * handle are stored in the buffer_handle.
131 *
132 * @param: handle of the buffer
133 * @return error status.
134 */
135genlock_status_t genlock_create_lock(native_handle_t *buffer_handle)
136{
137    genlock_status_t ret = GENLOCK_NO_ERROR;
138#ifdef USE_GENLOCK
139    if (private_handle_t::validate(buffer_handle)) {
140        ALOGE("%s: handle is invalid", __FUNCTION__);
141        return GENLOCK_FAILURE;
142    }
143
144    private_handle_t *hnd = reinterpret_cast<private_handle_t*>(buffer_handle);
145    if ((hnd->flags & private_handle_t::PRIV_FLAGS_UNSYNCHRONIZED) == 0) {
146        // Open the genlock device
147        int fd = open(GENLOCK_DEVICE, O_RDWR);
148        if (fd < 0) {
149            ALOGE("%s: open genlock device failed (err=%s)", __FUNCTION__,
150                  strerror(errno));
151            return GENLOCK_FAILURE;
152        }
153
154        // Create a new lock
155        genlock_lock lock;
156        if (ioctl(fd, GENLOCK_IOC_NEW, NULL)) {
157            ALOGE("%s: GENLOCK_IOC_NEW failed (error=%s)", __FUNCTION__,
158                  strerror(errno));
159            close_genlock_fd_and_handle(fd, lock.fd);
160            ret = GENLOCK_FAILURE;
161        }
162
163        // Export the lock for other processes to be able to use it.
164        if (GENLOCK_FAILURE != ret) {
165            if (ioctl(fd, GENLOCK_IOC_EXPORT, &lock)) {
166                ALOGE("%s: GENLOCK_IOC_EXPORT failed (error=%s)", __FUNCTION__,
167                      strerror(errno));
168                close_genlock_fd_and_handle(fd, lock.fd);
169                ret = GENLOCK_FAILURE;
170            }
171        }
172
173        // Store the lock params in the handle.
174        hnd->genlockPrivFd = fd;
175        hnd->genlockHandle = lock.fd;
176    } else {
177        hnd->genlockHandle = 0;
178    }
179#endif
180    return ret;
181}
182
183
184/*
185 * Release a genlock lock associated with the handle.
186 *
187 * @param: handle of the buffer
188 * @return error status.
189 */
190genlock_status_t genlock_release_lock(native_handle_t *buffer_handle)
191{
192    genlock_status_t ret = GENLOCK_NO_ERROR;
193#ifdef USE_GENLOCK
194    if (private_handle_t::validate(buffer_handle)) {
195        ALOGE("%s: handle is invalid", __FUNCTION__);
196        return GENLOCK_FAILURE;
197    }
198
199    private_handle_t *hnd = reinterpret_cast<private_handle_t*>(buffer_handle);
200    if ((hnd->flags & private_handle_t::PRIV_FLAGS_UNSYNCHRONIZED) == 0) {
201        if (hnd->genlockPrivFd < 0) {
202            ALOGE("%s: the lock is invalid", __FUNCTION__);
203            return GENLOCK_FAILURE;
204        }
205
206        // Close the fd and reset the parameters.
207        close_genlock_fd_and_handle(hnd->genlockPrivFd, hnd->genlockHandle);
208    }
209#endif
210    return ret;
211}
212
213
214/*
215 * Attach a lock to the buffer handle passed via an IPC.
216 *
217 * @param: handle of the buffer
218 * @return error status.
219 */
220genlock_status_t genlock_attach_lock(native_handle_t *buffer_handle)
221{
222    genlock_status_t ret = GENLOCK_NO_ERROR;
223#ifdef USE_GENLOCK
224    if (private_handle_t::validate(buffer_handle)) {
225        ALOGE("%s: handle is invalid", __FUNCTION__);
226        return GENLOCK_FAILURE;
227    }
228
229    private_handle_t *hnd = reinterpret_cast<private_handle_t*>(buffer_handle);
230    if ((hnd->flags & private_handle_t::PRIV_FLAGS_UNSYNCHRONIZED) == 0) {
231        // Open the genlock device
232        int fd = open(GENLOCK_DEVICE, O_RDWR);
233        if (fd < 0) {
234            ALOGE("%s: open genlock device failed (err=%s)", __FUNCTION__,
235                  strerror(errno));
236            return GENLOCK_FAILURE;
237        }
238
239        // Attach the local handle to an existing lock
240        genlock_lock lock;
241        lock.fd = hnd->genlockHandle;
242        if (ioctl(fd, GENLOCK_IOC_ATTACH, &lock)) {
243            ALOGE("%s: GENLOCK_IOC_ATTACH failed (err=%s)", __FUNCTION__,
244                  strerror(errno));
245            close_genlock_fd_and_handle(fd, lock.fd);
246            ret = GENLOCK_FAILURE;
247        }
248
249        // Store the relavant information in the handle
250        hnd->genlockPrivFd = fd;
251    }
252#endif
253    return ret;
254}
255
256/*
257 * Lock the buffer specified by the buffer handle. The lock held by the buffer
258 * is specified by the lockType. This function will block if a write lock is
259 * requested on the buffer which has previously been locked for a read or write
260 * operation. A buffer can be locked by multiple clients for read. An optional
261 * timeout value can be specified. By default, there is no timeout.
262 *
263 * @param: handle of the buffer
264 * @param: type of lock to be acquired by the buffer.
265 * @param: timeout value in ms. GENLOCK_MAX_TIMEOUT is the maximum timeout value.
266 * @return error status.
267 */
268genlock_status_t genlock_lock_buffer(native_handle_t *buffer_handle,
269                                     genlock_lock_type_t lockType,
270                                     int timeout)
271{
272    genlock_status_t ret = GENLOCK_NO_ERROR;
273#ifdef USE_GENLOCK
274    // Translate the locktype
275    int kLockType = get_kernel_lock_type(lockType);
276    if (-1 == kLockType) {
277        ALOGE("%s: invalid lockType", __FUNCTION__);
278        return GENLOCK_FAILURE;
279    }
280
281    if (0 == timeout) {
282        ALOGW("%s: trying to lock a buffer with timeout = 0", __FUNCTION__);
283    }
284    // Call the private function to perform the lock operation specified.
285    ret = perform_lock_unlock_operation(buffer_handle, kLockType, timeout, 0);
286#endif
287    return ret;
288}
289
290
291/*
292 * Unlocks a buffer that has previously been locked by the client.
293 *
294 * @param: handle of the buffer to be unlocked.
295 * @return: error status.
296 */
297genlock_status_t genlock_unlock_buffer(native_handle_t *buffer_handle)
298{
299    genlock_status_t ret = GENLOCK_NO_ERROR;
300#ifdef USE_GENLOCK
301    // Do the unlock operation by setting the unlock flag. Timeout is always
302    // 0 in this case.
303    ret = perform_lock_unlock_operation(buffer_handle, GENLOCK_UNLOCK, 0, 0);
304#endif
305    return ret;
306}
307
308/*
309 * Blocks the calling process until the lock held on the handle is unlocked.
310 *
311 * @param: handle of the buffer
312 * @param: timeout value for the wait.
313 * return: error status.
314 */
315genlock_status_t genlock_wait(native_handle_t *buffer_handle, int timeout) {
316#ifdef USE_GENLOCK
317    if (private_handle_t::validate(buffer_handle)) {
318        ALOGE("%s: handle is invalid", __FUNCTION__);
319        return GENLOCK_FAILURE;
320    }
321
322    private_handle_t *hnd = reinterpret_cast<private_handle_t*>(buffer_handle);
323    if ((hnd->flags & private_handle_t::PRIV_FLAGS_UNSYNCHRONIZED) == 0) {
324        if (hnd->genlockPrivFd < 0) {
325            ALOGE("%s: the lock is invalid", __FUNCTION__);
326            return GENLOCK_FAILURE;
327        }
328
329        if (0 == timeout)
330            ALOGW("%s: timeout = 0", __FUNCTION__);
331
332        genlock_lock lock;
333        lock.fd = hnd->genlockHandle;
334        lock.timeout = timeout;
335        if (ioctl(hnd->genlockPrivFd, GENLOCK_IOC_WAIT, &lock)) {
336            ALOGE("%s: GENLOCK_IOC_WAIT failed (err=%s)",  __FUNCTION__,
337                  strerror(errno));
338            return GENLOCK_FAILURE;
339        }
340    }
341#endif
342    return GENLOCK_NO_ERROR;
343}
344
345/*
346 * Convert a write lock that we own to a read lock
347 *
348 * @param: handle of the buffer
349 * @param: timeout value for the wait.
350 * return: error status.
351 */
352genlock_status_t genlock_write_to_read(native_handle_t *buffer_handle,
353                                       int timeout) {
354    genlock_status_t ret = GENLOCK_NO_ERROR;
355#ifdef USE_GENLOCK
356    if (0 == timeout) {
357        ALOGW("%s: trying to lock a buffer with timeout = 0", __FUNCTION__);
358    }
359    // Call the private function to perform the lock operation specified.
360#ifdef GENLOCK_IOC_DREADLOCK
361    ret = perform_lock_unlock_operation(buffer_handle, GENLOCK_RDLOCK, timeout,
362                                        GENLOCK_WRITE_TO_READ);
363#else
364    // depreciated
365    ret = perform_lock_unlock_operation(buffer_handle, GENLOCK_RDLOCK,
366                                        timeout, 0);
367#endif
368#endif
369    return ret;
370}
371