1/*
2// Copyright (c) 2014 Intel Corporation 
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8//      http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15*/
16#include <common/utils/HwcTrace.h>
17#include <DrmConfig.h>
18#include <Hwcomposer.h>
19#include <DisplayQuery.h>
20#include <ips/common/DrmControl.h>
21#include <ips/common/HdcpControl.h>
22#include <cutils/properties.h>
23
24
25namespace android {
26namespace intel {
27
28HdcpControl::HdcpControl()
29    : mCallback(NULL),
30      mUserData(NULL),
31      mCallbackState(CALLBACK_PENDING),
32      mMutex(),
33      mStoppedCondition(),
34      mCompletedCondition(),
35      mWaitForCompletion(false),
36      mStopped(true),
37      mAuthenticated(false),
38      mActionDelay(0),
39      mAuthRetryCount(0),
40      mEnableAuthenticationLog(true)
41{
42}
43
44HdcpControl::~HdcpControl()
45{
46}
47
48bool HdcpControl::startHdcp()
49{
50    // this is a blocking and synchronous call
51    Mutex::Autolock lock(mMutex);
52
53    char prop[PROPERTY_VALUE_MAX];
54    if (property_get("debug.hwc.hdcp.enable", prop, "1") > 0) {
55        if (atoi(prop) == 0) {
56            WLOGTRACE("HDCP is disabled");
57            return false;
58        }
59    }
60
61    if (!mStopped) {
62        WLOGTRACE("HDCP has been started");
63        return true;
64    }
65
66    mStopped = false;
67    mAuthenticated = false;
68    mWaitForCompletion = false;
69
70    mThread = new HdcpControlThread(this);
71    if (!mThread.get()) {
72        ELOGTRACE("failed to create hdcp control thread");
73        return false;
74    }
75
76    if (!runHdcp()) {
77        ELOGTRACE("failed to run HDCP");
78        mStopped = true;
79        mThread = NULL;
80        return false;
81    }
82
83    mAuthRetryCount = 0;
84    mWaitForCompletion = !mAuthenticated;
85    if (mAuthenticated) {
86        mActionDelay = HDCP_VERIFICATION_DELAY_MS;
87    } else {
88        mActionDelay = HDCP_AUTHENTICATION_SHORT_DELAY_MS;
89    }
90
91    mThread->run("HdcpControl", PRIORITY_NORMAL);
92
93    if (!mWaitForCompletion) {
94        // HDCP is authenticated.
95        return true;
96    }
97    status_t err = mCompletedCondition.waitRelative(mMutex, milliseconds(HDCP_AUTHENTICATION_TIMEOUT_MS));
98    if (err == -ETIMEDOUT) {
99        WLOGTRACE("timeout waiting for completion");
100    }
101    mWaitForCompletion = false;
102    return mAuthenticated;
103}
104
105bool HdcpControl::startHdcpAsync(HdcpStatusCallback cb, void *userData)
106{
107    char prop[PROPERTY_VALUE_MAX];
108    if (property_get("debug.hwc.hdcp.enable", prop, "1") > 0) {
109        if (atoi(prop) == 0) {
110            WLOGTRACE("HDCP is disabled");
111            return false;
112        }
113    }
114
115    if (cb == NULL || userData == NULL) {
116        ELOGTRACE("invalid callback or user data");
117        return false;
118    }
119
120    Mutex::Autolock lock(mMutex);
121
122    if (!mStopped) {
123        WLOGTRACE("HDCP has been started");
124        return true;
125    }
126
127    mThread = new HdcpControlThread(this);
128    if (!mThread.get()) {
129        ELOGTRACE("failed to create hdcp control thread");
130        return false;
131    }
132
133    mAuthRetryCount = 0;
134    mCallback = cb;
135    mUserData = userData;
136    mCallbackState = CALLBACK_PENDING;
137    mWaitForCompletion = false;
138    mAuthenticated = false;
139    mStopped = false;
140    mActionDelay = HDCP_ASYNC_START_DELAY_MS;
141    mThread->run("HdcpControl", PRIORITY_NORMAL);
142
143    return true;
144}
145
146bool HdcpControl::stopHdcp()
147{
148    do {
149        Mutex::Autolock lock(mMutex);
150        if (mStopped) {
151            return true;
152        }
153
154        mStopped = true;
155        mStoppedCondition.signal();
156
157        mAuthenticated = false;
158        mWaitForCompletion = false;
159        mCallback = NULL;
160        mUserData = NULL;
161        disableAuthentication();
162    } while (0);
163
164    if (mThread.get()) {
165        mThread->requestExitAndWait();
166        mThread = NULL;
167    }
168
169    return true;
170}
171
172bool HdcpControl::enableAuthentication()
173{
174    int fd = Hwcomposer::getInstance().getDrm()->getDrmFd();
175    int ret = drmCommandNone(fd, DRM_PSB_ENABLE_HDCP);
176    if (ret != 0) {
177        if (mEnableAuthenticationLog) {
178            ELOGTRACE("failed to enable HDCP authentication");
179        } else {
180            VLOGTRACE("failed to enable HDCP authentication");
181        }
182
183        mEnableAuthenticationLog = false;
184        return false;
185    }
186
187    mEnableAuthenticationLog = true;
188    return true;
189}
190
191bool HdcpControl::disableAuthentication()
192{
193    int fd = Hwcomposer::getInstance().getDrm()->getDrmFd();
194    int ret = drmCommandNone(fd, DRM_PSB_DISABLE_HDCP);
195    if (ret != 0) {
196        ELOGTRACE("failed to stop disable authentication");
197        return false;
198    }
199    return true;
200}
201
202bool HdcpControl::enableOverlay()
203{
204    return true;
205}
206
207bool HdcpControl::disableOverlay()
208{
209    return true;
210}
211
212bool HdcpControl::enableDisplayIED()
213{
214    int fd = Hwcomposer::getInstance().getDrm()->getDrmFd();
215    int ret = drmCommandNone(fd, DRM_PSB_HDCP_DISPLAY_IED_ON);
216    if (ret != 0) {
217        ELOGTRACE("failed to enable overlay IED");
218        return false;
219    }
220    return true;
221}
222
223bool HdcpControl::disableDisplayIED()
224{
225    int fd = Hwcomposer::getInstance().getDrm()->getDrmFd();
226    int ret = drmCommandNone(fd, DRM_PSB_HDCP_DISPLAY_IED_OFF);
227    if (ret != 0) {
228        ELOGTRACE("failed to disable overlay IED");
229        return false;
230    }
231    return true;
232}
233
234bool HdcpControl::isHdcpSupported()
235{
236    int fd = Hwcomposer::getInstance().getDrm()->getDrmFd();
237    unsigned int caps = 0;
238    int ret = drmCommandRead(fd, DRM_PSB_QUERY_HDCP, &caps, sizeof(caps));
239    if (ret != 0) {
240        ELOGTRACE("failed to query HDCP capability");
241        return false;
242    }
243    if (caps == 0) {
244        WLOGTRACE("HDCP is not supported");
245        return false;
246    } else {
247        ILOGTRACE("HDCP is supported");
248        return true;
249    }
250}
251
252bool HdcpControl::checkAuthenticated()
253{
254    int fd = Hwcomposer::getInstance().getDrm()->getDrmFd();
255    unsigned int match = 0;
256    int ret = drmCommandRead(fd, DRM_PSB_GET_HDCP_LINK_STATUS, &match, sizeof(match));
257    if (ret != 0) {
258        ELOGTRACE("failed to get hdcp link status");
259        return false;
260    }
261    if (match) {
262        VLOGTRACE("HDCP is authenticated");
263        mAuthenticated = true;
264    } else {
265        ELOGTRACE("HDCP is not authenticated");
266        mAuthenticated = false;
267    }
268    return mAuthenticated;
269}
270
271bool HdcpControl::runHdcp()
272{
273    // Default return value is true so HDCP can be re-authenticated in the working thread
274    bool ret = true;
275
276    preRunHdcp();
277
278    for (int i = 0; i < HDCP_INLOOP_RETRY_NUMBER; i++) {
279        VLOGTRACE("enable and verify HDCP, iteration# %d", i);
280        if (mStopped) {
281            WLOGTRACE("HDCP authentication has been stopped");
282            ret = false;
283            break;
284        }
285
286        if (!enableAuthentication()) {
287            if (mAuthenticated)
288                ELOGTRACE("HDCP authentication failed. Retry");
289            else
290                VLOGTRACE("HDCP authentication failed. Retry");
291
292            mAuthenticated = false;
293            ret = true;
294        } else {
295            ILOGTRACE("HDCP is authenticated");
296            mAuthenticated = true;
297            ret = true;
298            break;
299        }
300
301        if (mStopped) {
302            WLOGTRACE("HDCP authentication has been stopped");
303            ret = false;
304            break;
305        }
306
307        if (i < HDCP_INLOOP_RETRY_NUMBER - 1) {
308            // Adding delay to make sure panel receives video signal so it can start HDCP authentication.
309            // (HDCP spec 1.3, section 2.3)
310            usleep(HDCP_INLOOP_RETRY_DELAY_US);
311        }
312    }
313
314    postRunHdcp();
315
316    return ret;
317}
318
319bool HdcpControl::preRunHdcp()
320{
321    // TODO: for CTP platform, IED needs to be disabled during HDCP authentication.
322    return true;
323}
324
325bool HdcpControl::postRunHdcp()
326{
327    // TODO: for CTP platform, IED needs to be disabled during HDCP authentication.
328    return true;
329}
330
331
332void HdcpControl::signalCompletion()
333{
334    if (mWaitForCompletion) {
335        ILOGTRACE("signal HDCP authentication completed, status = %d", mAuthenticated);
336        mCompletedCondition.signal();
337        mWaitForCompletion = false;
338    }
339}
340
341bool HdcpControl::threadLoop()
342{
343    Mutex::Autolock lock(mMutex);
344    status_t err = mStoppedCondition.waitRelative(mMutex, milliseconds(mActionDelay));
345    if (err != -ETIMEDOUT) {
346        ILOGTRACE("Hdcp is stopped.");
347        signalCompletion();
348        return false;
349    }
350
351    // default is to keep thread active
352    bool ret = true;
353    if (!mAuthenticated) {
354        ret = runHdcp();
355        mAuthRetryCount++;
356    } else {
357        mAuthRetryCount = 0;
358        checkAuthenticated();
359    }
360
361    // set next action delay
362    if (mAuthenticated) {
363        mActionDelay = HDCP_VERIFICATION_DELAY_MS;
364    } else {
365        // If HDCP can not authenticate after "HDCP_RETRY_LIMIT" attempts
366        // reduce HDCP retry frequency to 2 sec
367        if (mAuthRetryCount >= HDCP_RETRY_LIMIT) {
368            mActionDelay = HDCP_AUTHENTICATION_LONG_DELAY_MS;
369        } else {
370            mActionDelay = HDCP_AUTHENTICATION_SHORT_DELAY_MS;
371        }
372    }
373
374    // TODO: move out of lock?
375    if (!ret || mAuthenticated) {
376        signalCompletion();
377    }
378
379    if (mCallback) {
380         if ((mAuthenticated && mCallbackState == CALLBACK_AUTHENTICATED) ||
381            (!mAuthenticated && mCallbackState == CALLBACK_NOT_AUTHENTICATED)) {
382            // ignore callback as state is not changed
383        } else {
384            mCallbackState =
385                mAuthenticated ? CALLBACK_AUTHENTICATED : CALLBACK_NOT_AUTHENTICATED;
386            (*mCallback)(mAuthenticated, mUserData);
387        }
388    }
389    return ret;
390}
391
392} // namespace intel
393} // namespace android
394