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