1 2 /* 3 * Copyright (C) 2010 The Android Open Source Project 4 * Copyright (C) 2012-14, The Linux Foundation. All rights reserved. 5 * 6 * Not a Contribution, Apache license notifications and license are 7 * retained for attribution purposes only. 8 9 * Licensed under the Apache License, Version 2.0 (the "License"); 10 * you may not use this file except in compliance with the License. 11 * You may obtain a copy of the License at 12 * 13 * http://www.apache.org/licenses/LICENSE-2.0 14 * 15 * Unless required by applicable law or agreed to in writing, software 16 * distributed under the License is distributed on an "AS IS" BASIS, 17 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 * See the License for the specific language governing permissions and 19 * limitations under the License. 20 */ 21 #define UEVENT_DEBUG 0 22 #include <hardware_legacy/uevent.h> 23 #include <utils/Log.h> 24 #include <sys/resource.h> 25 #include <sys/prctl.h> 26 #include <string.h> 27 #include <stdlib.h> 28 #include "hwc_utils.h" 29 #include "hwc_fbupdate.h" 30 #include "hwc_mdpcomp.h" 31 #include "hwc_copybit.h" 32 #include "comptype.h" 33 #include "external.h" 34 #include "virtual.h" 35 #include "hwc_virtual.h" 36 #include "mdp_version.h" 37 using namespace overlay; 38 namespace qhwc { 39 #define HWC_UEVENT_SWITCH_STR "change@/devices/virtual/switch/" 40 #define HWC_UEVENT_THREAD_NAME "hwcUeventThread" 41 42 static void setup(hwc_context_t* ctx, int dpy) 43 { 44 ctx->mFBUpdate[dpy] = IFBUpdate::getObject(ctx, dpy); 45 ctx->mMDPComp[dpy] = MDPComp::getObject(ctx, dpy); 46 } 47 48 static void clear(hwc_context_t* ctx, int dpy) 49 { 50 if(ctx->mFBUpdate[dpy]) { 51 delete ctx->mFBUpdate[dpy]; 52 ctx->mFBUpdate[dpy] = NULL; 53 } 54 if(ctx->mMDPComp[dpy]) { 55 delete ctx->mMDPComp[dpy]; 56 ctx->mMDPComp[dpy] = NULL; 57 } 58 } 59 60 /* Parse uevent data for devices which we are interested */ 61 static int getConnectedDisplay(const char* strUdata) 62 { 63 if(strcasestr("change@/devices/virtual/switch/hdmi", strUdata)) 64 return HWC_DISPLAY_EXTERNAL; 65 if(strcasestr("change@/devices/virtual/switch/wfd", strUdata)) 66 return HWC_DISPLAY_VIRTUAL; 67 return -1; 68 } 69 70 static bool getPanelResetStatus(hwc_context_t* ctx, const char* strUdata, int len) 71 { 72 const char* iter_str = strUdata; 73 if (strcasestr("change@/devices/virtual/graphics/fb0", strUdata)) { 74 while(((iter_str - strUdata) <= len) && (*iter_str)) { 75 char* pstr = strstr(iter_str, "PANEL_ALIVE=0"); 76 if (pstr != NULL) { 77 ALOGI("%s: got change event in fb0 with PANEL_ALIVE=0", 78 __FUNCTION__); 79 ctx->mPanelResetStatus = true; 80 return true; 81 } 82 iter_str += strlen(iter_str)+1; 83 } 84 } 85 return false; 86 } 87 88 /* Parse uevent data for action requested for the display */ 89 static int getConnectedState(const char* strUdata, int len) 90 { 91 const char* iter_str = strUdata; 92 while(((iter_str - strUdata) <= len) && (*iter_str)) { 93 char* pstr = strstr(iter_str, "SWITCH_STATE="); 94 if (pstr != NULL) { 95 return (atoi(pstr + strlen("SWITCH_STATE="))); 96 } 97 iter_str += strlen(iter_str)+1; 98 } 99 return -1; 100 } 101 102 void handle_pause(hwc_context_t* ctx, int dpy) { 103 if(ctx->mHWCVirtual) { 104 ctx->mHWCVirtual->pause(ctx, dpy); 105 } 106 return; 107 } 108 109 void handle_resume(hwc_context_t* ctx, int dpy) { 110 if(ctx->mHWCVirtual) { 111 ctx->mHWCVirtual->resume(ctx, dpy); 112 } 113 return; 114 } 115 116 static void teardownWfd(hwc_context_t* ctx) { 117 // Teardown WFD display 118 ALOGD_IF(UEVENT_DEBUG,"Received HDMI connection request when WFD is " 119 "active"); 120 { 121 Locker::Autolock _l(ctx->mDrawLock); 122 clear(ctx, HWC_DISPLAY_VIRTUAL); 123 ctx->dpyAttr[HWC_DISPLAY_VIRTUAL].connected = false; 124 ctx->dpyAttr[HWC_DISPLAY_VIRTUAL].isActive = false; 125 } 126 127 ctx->mVirtualDisplay->teardown(); 128 129 /* Need to send hotplug only when connected WFD in proprietary path */ 130 if(ctx->mVirtualonExtActive) { 131 ALOGE_IF(UEVENT_DEBUG,"%s: Sending EXTERNAL OFFLINE" 132 "hotplug event for wfd display", __FUNCTION__); 133 ctx->proc->hotplug(ctx->proc, HWC_DISPLAY_EXTERNAL, 134 EXTERNAL_OFFLINE); 135 { 136 Locker::Autolock _l(ctx->mDrawLock); 137 ctx->mVirtualonExtActive = false; 138 } 139 } 140 141 ctx->mWfdSyncLock.lock(); 142 ALOGD_IF(HWC_WFDDISPSYNC_LOG, 143 "%s: Waiting for wfd-teardown to be signalled",__FUNCTION__); 144 ctx->mWfdSyncLock.wait(); 145 ALOGD_IF(HWC_WFDDISPSYNC_LOG, 146 "%s: Teardown signalled. Completed waiting in uevent thread", 147 __FUNCTION__); 148 ctx->mWfdSyncLock.unlock(); 149 } 150 151 static void handle_uevent(hwc_context_t* ctx, const char* udata, int len) 152 { 153 bool bpanelReset = getPanelResetStatus(ctx, udata, len); 154 if (bpanelReset) { 155 ctx->proc->invalidate(ctx->proc); 156 return; 157 } 158 159 int dpy = getConnectedDisplay(udata); 160 if(dpy < 0) { 161 ALOGD_IF(UEVENT_DEBUG, "%s: Not disp Event ", __FUNCTION__); 162 return; 163 } 164 165 int switch_state = getConnectedState(udata, len); 166 167 ALOGE_IF(UEVENT_DEBUG,"%s: uevent received: %s switch state: %d", 168 __FUNCTION__,udata, switch_state); 169 170 switch(switch_state) { 171 case EXTERNAL_OFFLINE: 172 { 173 /* Display not connected */ 174 if(!ctx->dpyAttr[dpy].connected){ 175 ALOGE_IF(UEVENT_DEBUG,"%s: Ignoring EXTERNAL_OFFLINE event" 176 "for display: %d", __FUNCTION__, dpy); 177 break; 178 } 179 180 Locker::Autolock _l(ctx->mDrawLock); 181 clear(ctx, dpy); 182 ctx->dpyAttr[dpy].connected = false; 183 ctx->dpyAttr[dpy].isActive = false; 184 185 if(dpy == HWC_DISPLAY_EXTERNAL) { 186 ctx->mExtDisplay->teardown(); 187 } else { 188 ctx->mVirtualDisplay->teardown(); 189 } 190 191 /* We need to send hotplug to SF only when we are disconnecting 192 * (1) HDMI OR (2) proprietary WFD session */ 193 if(dpy == HWC_DISPLAY_EXTERNAL || 194 ctx->mVirtualonExtActive) { 195 ALOGE_IF(UEVENT_DEBUG,"%s:Sending EXTERNAL OFFLINE hotplug" 196 "event", __FUNCTION__); 197 ctx->proc->hotplug(ctx->proc, HWC_DISPLAY_EXTERNAL, 198 EXTERNAL_OFFLINE); 199 ctx->mVirtualonExtActive = false; 200 ctx->mQService->onHdmiHotplug((int)ctx->dpyAttr[dpy].connected); 201 } 202 break; 203 } 204 case EXTERNAL_ONLINE: 205 { 206 /* Display already connected */ 207 if(ctx->dpyAttr[dpy].connected) { 208 ALOGE_IF(UEVENT_DEBUG,"%s: Ignoring EXTERNAL_ONLINE event" 209 "for display: %d", __FUNCTION__, dpy); 210 break; 211 } 212 { 213 //Force composition to give up resources like pipes and 214 //close fb. For example if assertive display is going on, 215 //fb2 could be open, thus connecting Layer Mixer#0 to 216 //WriteBack module. If HDMI attempts to open fb1, the driver 217 //will try to attach Layer Mixer#0 to HDMI INT, which will 218 //fail, since Layer Mixer#0 is still connected to WriteBack. 219 //This block will force composition to close fb2 in above 220 //example. 221 Locker::Autolock _l(ctx->mDrawLock); 222 ctx->dpyAttr[dpy].isConfiguring = true; 223 ctx->proc->invalidate(ctx->proc); 224 } 225 //2 cycles for slower content 226 usleep(ctx->dpyAttr[HWC_DISPLAY_PRIMARY].vsync_period 227 * 2 / 1000); 228 229 if(dpy == HWC_DISPLAY_EXTERNAL) { 230 if(ctx->dpyAttr[HWC_DISPLAY_VIRTUAL].connected) { 231 // Triple Display is supported on 8084 target 232 // WFD can be initiated by Wfd-client or Settings app 233 // 1. wfd-client use hdmi hotplug mechanism. 234 // If wfd is connected via wfd-client and if HDMI is 235 // connected, we have to teardown wfd session. 236 // (as SF support only one active External display 237 // at a given time). 238 // (ToDo: Once wfd-client migrates using virtual display 239 // apis, second condition is redundant). 240 // 2. Settings app use virtual display mechanism. 241 // In this approach, there is no limitation of supporting 242 // triple display. 243 if(!(qdutils::MDPVersion::getInstance().is8084() && 244 !ctx->mVirtualonExtActive)) { 245 teardownWfd(ctx); 246 } 247 } 248 ctx->mExtDisplay->configure(); 249 } else { 250 { 251 Locker::Autolock _l(ctx->mDrawLock); 252 /* TRUE only when we are on proprietary WFD session */ 253 ctx->mVirtualonExtActive = true; 254 char property[PROPERTY_VALUE_MAX]; 255 if((property_get("persist.sys.wfd.virtual", 256 property, NULL) > 0) && 257 (!strncmp(property, "1", PROPERTY_VALUE_MAX ) || 258 (!strncasecmp(property,"true", PROPERTY_VALUE_MAX )))) { 259 // This means we are on Google's WFD session 260 ctx->mVirtualonExtActive = false; 261 } 262 } 263 ctx->mVirtualDisplay->configure(); 264 } 265 266 Locker::Autolock _l(ctx->mDrawLock); 267 setup(ctx, dpy); 268 ctx->dpyAttr[dpy].isPause = false; 269 ctx->dpyAttr[dpy].connected = true; 270 ctx->dpyAttr[dpy].isConfiguring = true; 271 272 if(dpy == HWC_DISPLAY_EXTERNAL || 273 ctx->mVirtualonExtActive) { 274 /* External display is HDMI or non-hybrid WFD solution */ 275 ALOGE_IF(UEVENT_DEBUG, "%s: Sending EXTERNAL_OFFLINE ONLINE" 276 "hotplug event", __FUNCTION__); 277 ctx->proc->hotplug(ctx->proc,HWC_DISPLAY_EXTERNAL, 278 EXTERNAL_ONLINE); 279 ctx->mQService->onHdmiHotplug(ctx->dpyAttr[dpy].connected); 280 } else { 281 /* We wont be getting unblank for VIRTUAL DISPLAY and its 282 * always guaranteed from WFD stack that CONNECT uevent for 283 * VIRTUAL DISPLAY will be triggered before creating 284 * surface for the same. */ 285 ctx->dpyAttr[HWC_DISPLAY_VIRTUAL].isActive = true; 286 } 287 break; 288 } 289 case EXTERNAL_PAUSE: 290 { // pause case 291 ALOGD("%s Received Pause event",__FUNCTION__); 292 handle_pause(ctx, dpy); 293 break; 294 } 295 case EXTERNAL_RESUME: 296 { // resume case 297 ALOGD("%s Received resume event",__FUNCTION__); 298 //Treat Resume as Online event 299 //Since external didnt have any pipes, force primary to give up 300 //its pipes; we don't allow inter-mixer pipe transfers. 301 handle_resume(ctx, dpy); 302 break; 303 } 304 default: 305 { 306 ALOGE("%s: Invalid state to swtich:%d", __FUNCTION__, switch_state); 307 break; 308 } 309 } 310 } 311 312 static void *uevent_loop(void *param) 313 { 314 int len = 0; 315 static char udata[PAGE_SIZE]; 316 hwc_context_t * ctx = reinterpret_cast<hwc_context_t *>(param); 317 char thread_name[64] = HWC_UEVENT_THREAD_NAME; 318 prctl(PR_SET_NAME, (unsigned long) &thread_name, 0, 0, 0); 319 setpriority(PRIO_PROCESS, 0, HAL_PRIORITY_URGENT_DISPLAY); 320 if(!uevent_init()) { 321 ALOGE("%s: failed to init uevent ",__FUNCTION__); 322 return NULL; 323 } 324 325 while(1) { 326 len = uevent_next_event(udata, (int)sizeof(udata) - 2); 327 handle_uevent(ctx, udata, len); 328 } 329 330 return NULL; 331 } 332 333 void init_uevent_thread(hwc_context_t* ctx) 334 { 335 pthread_t uevent_thread; 336 int ret; 337 338 ALOGI("Initializing UEVENT Thread"); 339 ret = pthread_create(&uevent_thread, NULL, uevent_loop, (void*) ctx); 340 if (ret) { 341 ALOGE("%s: failed to create %s: %s", __FUNCTION__, 342 HWC_UEVENT_THREAD_NAME, strerror(ret)); 343 } 344 } 345 346 }; //namespace 347