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