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 
25 namespace android {
26 namespace intel {
27 
HdcpControl()28 HdcpControl::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 
~HdcpControl()44 HdcpControl::~HdcpControl()
45 {
46 }
47 
startHdcp()48 bool 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 
startHdcpAsync(HdcpStatusCallback cb,void * userData)105 bool 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 
stopHdcp()146 bool 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 
enableAuthentication()172 bool 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 
disableAuthentication()191 bool 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 
enableOverlay()202 bool HdcpControl::enableOverlay()
203 {
204     return true;
205 }
206 
disableOverlay()207 bool HdcpControl::disableOverlay()
208 {
209     return true;
210 }
211 
enableDisplayIED()212 bool 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 
disableDisplayIED()223 bool 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 
isHdcpSupported()234 bool 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 
checkAuthenticated()252 bool 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 
runHdcp()271 bool 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 
preRunHdcp()319 bool HdcpControl::preRunHdcp()
320 {
321     // TODO: for CTP platform, IED needs to be disabled during HDCP authentication.
322     return true;
323 }
324 
postRunHdcp()325 bool HdcpControl::postRunHdcp()
326 {
327     // TODO: for CTP platform, IED needs to be disabled during HDCP authentication.
328     return true;
329 }
330 
331 
signalCompletion()332 void HdcpControl::signalCompletion()
333 {
334     if (mWaitForCompletion) {
335         ILOGTRACE("signal HDCP authentication completed, status = %d", mAuthenticated);
336         mCompletedCondition.signal();
337         mWaitForCompletion = false;
338     }
339 }
340 
threadLoop()341 bool 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