OLD | NEW |
(Empty) | |
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include "ui/ozone/platform/drm/host/drm_display_host_manager.h" |
| 6 |
| 7 #include <fcntl.h> |
| 8 #include <xf86drm.h> |
| 9 |
| 10 #include "base/files/file_enumerator.h" |
| 11 #include "base/posix/eintr_wrapper.h" |
| 12 #include "base/strings/stringprintf.h" |
| 13 #include "base/thread_task_runner_handle.h" |
| 14 #include "base/threading/worker_pool.h" |
| 15 #include "ui/display/types/display_snapshot.h" |
| 16 #include "ui/events/ozone/device/device_event.h" |
| 17 #include "ui/events/ozone/device/device_manager.h" |
| 18 #include "ui/ozone/common/display_util.h" |
| 19 #include "ui/ozone/platform/drm/common/drm_util.h" |
| 20 #include "ui/ozone/platform/drm/host/drm_device_handle.h" |
| 21 #include "ui/ozone/platform/drm/host/drm_display_host.h" |
| 22 #include "ui/ozone/platform/drm/host/drm_gpu_platform_support_host.h" |
| 23 #include "ui/ozone/platform/drm/host/drm_native_display_delegate.h" |
| 24 |
| 25 namespace ui { |
| 26 |
| 27 namespace { |
| 28 |
| 29 typedef base::Callback<void(const base::FilePath&, scoped_ptr<DrmDeviceHandle>)> |
| 30 OnOpenDeviceReplyCallback; |
| 31 |
| 32 const char kDefaultGraphicsCardPattern[] = "/dev/dri/card%d"; |
| 33 const char kVgemDevDriCardPath[] = "/dev/dri/"; |
| 34 const char kVgemSysCardPath[] = "/sys/bus/platform/devices/vgem/drm/"; |
| 35 |
| 36 const char* kDisplayActionString[] = { |
| 37 "ADD", |
| 38 "REMOVE", |
| 39 "CHANGE", |
| 40 }; |
| 41 |
| 42 void OpenDeviceOnWorkerThread( |
| 43 const base::FilePath& path, |
| 44 const scoped_refptr<base::TaskRunner>& reply_runner, |
| 45 const OnOpenDeviceReplyCallback& callback) { |
| 46 scoped_ptr<DrmDeviceHandle> handle(new DrmDeviceHandle()); |
| 47 handle->Initialize(path); |
| 48 reply_runner->PostTask( |
| 49 FROM_HERE, base::Bind(callback, path, base::Passed(handle.Pass()))); |
| 50 } |
| 51 |
| 52 base::FilePath GetPrimaryDisplayCardPath() { |
| 53 struct drm_mode_card_res res; |
| 54 for (int i = 0; /* end on first card# that does not exist */; i++) { |
| 55 std::string card_path = base::StringPrintf(kDefaultGraphicsCardPattern, i); |
| 56 |
| 57 if (access(card_path.c_str(), F_OK) != 0) { |
| 58 LOG(WARNING) << "Can't access card: " << card_path; |
| 59 break; |
| 60 } |
| 61 |
| 62 |
| 63 int fd = open(card_path.c_str(), O_RDWR | O_CLOEXEC); |
| 64 if (fd < 0) { |
| 65 VPLOG(1) << "Failed to open '" << card_path << "'"; |
| 66 continue; |
| 67 } |
| 68 |
| 69 memset(&res, 0, sizeof(struct drm_mode_card_res)); |
| 70 int ret = drmIoctl(fd, DRM_IOCTL_MODE_GETRESOURCES, &res); |
| 71 close(fd); |
| 72 if (ret == 0 && res.count_crtcs > 0) { |
| 73 return base::FilePath(card_path); |
| 74 } |
| 75 |
| 76 VPLOG_IF(1, ret) << "Failed to get DRM resources for '" << card_path << "'"; |
| 77 } |
| 78 |
| 79 return base::FilePath(); |
| 80 } |
| 81 |
| 82 base::FilePath GetVgemCardPath() { |
| 83 base::FileEnumerator file_iter(base::FilePath(kVgemSysCardPath), false, |
| 84 base::FileEnumerator::DIRECTORIES, |
| 85 FILE_PATH_LITERAL("card*")); |
| 86 |
| 87 while (!file_iter.Next().empty()) { |
| 88 // Inspect the card%d directories in the directory and extract the filename. |
| 89 std::string vgem_card_path = |
| 90 kVgemDevDriCardPath + file_iter.GetInfo().GetName().BaseName().value(); |
| 91 DVLOG(1) << "VGEM card path is " << vgem_card_path; |
| 92 return base::FilePath(vgem_card_path); |
| 93 } |
| 94 DVLOG(1) << "Don't support VGEM"; |
| 95 return base::FilePath(); |
| 96 } |
| 97 |
| 98 class FindDrmDisplayHostById { |
| 99 public: |
| 100 explicit FindDrmDisplayHostById(int64_t display_id) |
| 101 : display_id_(display_id) {} |
| 102 |
| 103 bool operator()(const DrmDisplayHost* display) const { |
| 104 return display->snapshot()->display_id() == display_id_; |
| 105 } |
| 106 |
| 107 private: |
| 108 int64_t display_id_; |
| 109 }; |
| 110 |
| 111 } // namespace |
| 112 |
| 113 DrmDisplayHostManager::DrmDisplayHostManager( |
| 114 DrmGpuPlatformSupportHost* proxy, |
| 115 DeviceManager* device_manager, |
| 116 InputControllerEvdev* input_controller) |
| 117 : proxy_(proxy), |
| 118 device_manager_(device_manager), |
| 119 input_controller_(input_controller), |
| 120 primary_graphics_card_path_(GetPrimaryDisplayCardPath()), |
| 121 weak_ptr_factory_(this) { |
| 122 { |
| 123 // First device needs to be treated specially. We need to open this |
| 124 // synchronously since the GPU process will need it to initialize the |
| 125 // graphics state. |
| 126 primary_drm_device_handle_.reset(new DrmDeviceHandle()); |
| 127 if (!primary_drm_device_handle_->Initialize(primary_graphics_card_path_)) { |
| 128 LOG(FATAL) << "Failed to open primary graphics card"; |
| 129 return; |
| 130 } |
| 131 drm_devices_.insert(primary_graphics_card_path_); |
| 132 |
| 133 vgem_card_path_ = GetVgemCardPath(); |
| 134 if (!vgem_card_path_.empty()) { |
| 135 int fd = HANDLE_EINTR( |
| 136 open(vgem_card_path_.value().c_str(), O_RDWR | O_CLOEXEC)); |
| 137 if (fd < 0) { |
| 138 PLOG(ERROR) << "Failed to open vgem: " << vgem_card_path_.value(); |
| 139 } |
| 140 vgem_card_device_file_.reset(fd); |
| 141 } |
| 142 } |
| 143 |
| 144 device_manager_->AddObserver(this); |
| 145 |
| 146 ScopedVector<HardwareDisplayControllerInfo> display_infos = |
| 147 GetAvailableDisplayControllerInfos(primary_drm_device_handle_->fd()); |
| 148 has_dummy_display_ = !display_infos.empty(); |
| 149 for (size_t i = 0; i < display_infos.size(); ++i) { |
| 150 displays_.push_back(new DrmDisplayHost( |
| 151 proxy_, CreateDisplaySnapshotParams(display_infos[i], |
| 152 primary_drm_device_handle_->fd(), i, |
| 153 gfx::Point()), |
| 154 true /* is_dummy */)); |
| 155 } |
| 156 } |
| 157 |
| 158 DrmDisplayHostManager::~DrmDisplayHostManager() { |
| 159 device_manager_->RemoveObserver(this); |
| 160 } |
| 161 |
| 162 DrmDisplayHost* DrmDisplayHostManager::GetDisplay(int64_t display_id) { |
| 163 auto it = std::find_if(displays_.begin(), displays_.end(), |
| 164 FindDrmDisplayHostById(display_id)); |
| 165 if (it == displays_.end()) |
| 166 return nullptr; |
| 167 |
| 168 return *it; |
| 169 } |
| 170 |
| 171 void DrmDisplayHostManager::AddDelegate(DrmNativeDisplayDelegate* delegate) { |
| 172 DCHECK(!delegate_); |
| 173 delegate_ = delegate; |
| 174 } |
| 175 |
| 176 void DrmDisplayHostManager::RemoveDelegate(DrmNativeDisplayDelegate* delegate) { |
| 177 DCHECK_EQ(delegate_, delegate); |
| 178 delegate_ = nullptr; |
| 179 } |
| 180 |
| 181 void DrmDisplayHostManager::TakeDisplayControl( |
| 182 const DisplayControlCallback& callback) { |
| 183 if (display_control_change_pending_) { |
| 184 LOG(ERROR) << "TakeDisplayControl called while change already pending"; |
| 185 callback.Run(false); |
| 186 return; |
| 187 } |
| 188 |
| 189 if (!display_externally_controlled_) { |
| 190 LOG(ERROR) << "TakeDisplayControl called while display already owned"; |
| 191 callback.Run(true); |
| 192 return; |
| 193 } |
| 194 |
| 195 take_display_control_callback_ = callback; |
| 196 display_control_change_pending_ = true; |
| 197 |
| 198 if (!proxy_->TakeDisplayControl()) |
| 199 OnTakeDisplayControl(false); |
| 200 } |
| 201 |
| 202 void DrmDisplayHostManager::RelinquishDisplayControl( |
| 203 const DisplayControlCallback& callback) { |
| 204 if (display_control_change_pending_) { |
| 205 LOG(ERROR) |
| 206 << "RelinquishDisplayControl called while change already pending"; |
| 207 callback.Run(false); |
| 208 return; |
| 209 } |
| 210 |
| 211 if (display_externally_controlled_) { |
| 212 LOG(ERROR) << "RelinquishDisplayControl called while display not owned"; |
| 213 callback.Run(true); |
| 214 return; |
| 215 } |
| 216 |
| 217 relinquish_display_control_callback_ = callback; |
| 218 display_control_change_pending_ = true; |
| 219 |
| 220 if (!proxy_->RelinquishDisplayControl()) |
| 221 OnRelinquishDisplayControl(false); |
| 222 } |
| 223 |
| 224 void DrmDisplayHostManager::UpdateDisplays( |
| 225 const GetDisplaysCallback& callback) { |
| 226 get_displays_callback_ = callback; |
| 227 if (!proxy_->RefreshNativeDisplays()) { |
| 228 get_displays_callback_.Reset(); |
| 229 RunUpdateDisplaysCallback(callback); |
| 230 } |
| 231 } |
| 232 |
| 233 void DrmDisplayHostManager::OnDeviceEvent(const DeviceEvent& event) { |
| 234 if (event.device_type() != DeviceEvent::DISPLAY) |
| 235 return; |
| 236 |
| 237 event_queue_.push(DisplayEvent(event.action_type(), event.path())); |
| 238 ProcessEvent(); |
| 239 } |
| 240 |
| 241 void DrmDisplayHostManager::ProcessEvent() { |
| 242 while (!event_queue_.empty() && !task_pending_) { |
| 243 DisplayEvent event = event_queue_.front(); |
| 244 event_queue_.pop(); |
| 245 VLOG(1) << "Got display event " << kDisplayActionString[event.action_type] |
| 246 << " for " << event.path.value(); |
| 247 switch (event.action_type) { |
| 248 case DeviceEvent::ADD: |
| 249 if (event.path == vgem_card_path_) |
| 250 continue; |
| 251 if (drm_devices_.find(event.path) == drm_devices_.end()) { |
| 252 task_pending_ = base::WorkerPool::PostTask( |
| 253 FROM_HERE, |
| 254 base::Bind(&OpenDeviceOnWorkerThread, event.path, |
| 255 base::ThreadTaskRunnerHandle::Get(), |
| 256 base::Bind(&DrmDisplayHostManager::OnAddGraphicsDevice, |
| 257 weak_ptr_factory_.GetWeakPtr())), |
| 258 false /* task_is_slow */); |
| 259 } |
| 260 break; |
| 261 case DeviceEvent::CHANGE: |
| 262 task_pending_ = base::ThreadTaskRunnerHandle::Get()->PostTask( |
| 263 FROM_HERE, |
| 264 base::Bind(&DrmDisplayHostManager::OnUpdateGraphicsDevice, |
| 265 weak_ptr_factory_.GetWeakPtr())); |
| 266 break; |
| 267 case DeviceEvent::REMOVE: |
| 268 DCHECK(event.path != primary_graphics_card_path_) |
| 269 << "Removing primary graphics card"; |
| 270 DCHECK(event.path != vgem_card_path_) << "Removing VGEM device"; |
| 271 auto it = drm_devices_.find(event.path); |
| 272 if (it != drm_devices_.end()) { |
| 273 task_pending_ = base::ThreadTaskRunnerHandle::Get()->PostTask( |
| 274 FROM_HERE, |
| 275 base::Bind(&DrmDisplayHostManager::OnRemoveGraphicsDevice, |
| 276 weak_ptr_factory_.GetWeakPtr(), event.path)); |
| 277 drm_devices_.erase(it); |
| 278 } |
| 279 break; |
| 280 } |
| 281 } |
| 282 } |
| 283 |
| 284 void DrmDisplayHostManager::OnAddGraphicsDevice( |
| 285 const base::FilePath& path, |
| 286 scoped_ptr<DrmDeviceHandle> handle) { |
| 287 if (handle->IsValid()) { |
| 288 drm_devices_.insert(path); |
| 289 proxy_->AddGraphicsDevice(path, base::FileDescriptor(handle->PassFD())); |
| 290 NotifyDisplayDelegate(); |
| 291 } |
| 292 |
| 293 task_pending_ = false; |
| 294 ProcessEvent(); |
| 295 } |
| 296 |
| 297 void DrmDisplayHostManager::OnUpdateGraphicsDevice() { |
| 298 NotifyDisplayDelegate(); |
| 299 task_pending_ = false; |
| 300 ProcessEvent(); |
| 301 } |
| 302 |
| 303 void DrmDisplayHostManager::OnRemoveGraphicsDevice(const base::FilePath& path) { |
| 304 proxy_->RemoveGraphicsDevice(path); |
| 305 NotifyDisplayDelegate(); |
| 306 task_pending_ = false; |
| 307 ProcessEvent(); |
| 308 } |
| 309 |
| 310 void DrmDisplayHostManager::OnChannelEstablished(int host_id) { |
| 311 // If in the middle of a configuration, just respond with the old list of |
| 312 // displays. This is fine, since after the DRM resources are initialized and |
| 313 // IPC-ed to the GPU NotifyDisplayDelegate() is called to let the display |
| 314 // delegate know that the display configuration changed and it needs to |
| 315 // update it again. |
| 316 if (!get_displays_callback_.is_null()) { |
| 317 base::ThreadTaskRunnerHandle::Get()->PostTask( |
| 318 FROM_HERE, |
| 319 base::Bind(&DrmDisplayHostManager::RunUpdateDisplaysCallback, |
| 320 weak_ptr_factory_.GetWeakPtr(), get_displays_callback_)); |
| 321 get_displays_callback_.Reset(); |
| 322 } |
| 323 |
| 324 // Signal that we're taking DRM master since we're going through the |
| 325 // initialization process again and we'll take all the available resources. |
| 326 if (!take_display_control_callback_.is_null()) |
| 327 OnTakeDisplayControl(true); |
| 328 |
| 329 if (!relinquish_display_control_callback_.is_null()) |
| 330 OnRelinquishDisplayControl(false); |
| 331 |
| 332 drm_devices_.clear(); |
| 333 drm_devices_.insert(primary_graphics_card_path_); |
| 334 scoped_ptr<DrmDeviceHandle> handle = primary_drm_device_handle_.Pass(); |
| 335 if (!handle) { |
| 336 handle.reset(new DrmDeviceHandle()); |
| 337 if (!handle->Initialize(primary_graphics_card_path_)) |
| 338 LOG(FATAL) << "Failed to open primary graphics card"; |
| 339 } |
| 340 |
| 341 // Send the primary device first since this is used to initialize graphics |
| 342 // state. |
| 343 proxy_->AddGraphicsDevice(primary_graphics_card_path_, |
| 344 base::FileDescriptor(handle->PassFD())); |
| 345 |
| 346 device_manager_->ScanDevices(this); |
| 347 NotifyDisplayDelegate(); |
| 348 } |
| 349 |
| 350 void DrmDisplayHostManager::OnChannelDestroyed(int host_id) { |
| 351 // Do nothing. |
| 352 } |
| 353 |
| 354 void DrmDisplayHostManager::OnUpdateNativeDisplays( |
| 355 const std::vector<DisplaySnapshot_Params>& params) { |
| 356 ScopedVector<DrmDisplayHost> old_displays(displays_.Pass()); |
| 357 for (size_t i = 0; i < params.size(); ++i) { |
| 358 auto it = std::find_if(old_displays.begin(), old_displays.end(), |
| 359 FindDrmDisplayHostById(params[i].display_id)); |
| 360 if (it == old_displays.end()) { |
| 361 displays_.push_back( |
| 362 new DrmDisplayHost(proxy_, params[i], false /* is_dummy */)); |
| 363 } else { |
| 364 (*it)->UpdateDisplaySnapshot(params[i]); |
| 365 displays_.push_back(*it); |
| 366 old_displays.weak_erase(it); |
| 367 } |
| 368 } |
| 369 |
| 370 if (!get_displays_callback_.is_null()) { |
| 371 base::ThreadTaskRunnerHandle::Get()->PostTask( |
| 372 FROM_HERE, |
| 373 base::Bind(&DrmDisplayHostManager::RunUpdateDisplaysCallback, |
| 374 weak_ptr_factory_.GetWeakPtr(), get_displays_callback_)); |
| 375 get_displays_callback_.Reset(); |
| 376 } |
| 377 } |
| 378 |
| 379 void DrmDisplayHostManager::OnDisplayConfigured(int64_t display_id, |
| 380 bool status) { |
| 381 DrmDisplayHost* display = GetDisplay(display_id); |
| 382 if (display) |
| 383 display->OnDisplayConfigured(status); |
| 384 else |
| 385 LOG(ERROR) << "Couldn't find display with id=" << display_id; |
| 386 } |
| 387 |
| 388 void DrmDisplayHostManager::OnHDCPStateReceived(int64_t display_id, |
| 389 bool status, |
| 390 HDCPState state) { |
| 391 DrmDisplayHost* display = GetDisplay(display_id); |
| 392 if (display) |
| 393 display->OnHDCPStateReceived(status, state); |
| 394 else |
| 395 LOG(ERROR) << "Couldn't find display with id=" << display_id; |
| 396 } |
| 397 |
| 398 void DrmDisplayHostManager::OnHDCPStateUpdated(int64_t display_id, |
| 399 bool status) { |
| 400 DrmDisplayHost* display = GetDisplay(display_id); |
| 401 if (display) |
| 402 display->OnHDCPStateUpdated(status); |
| 403 else |
| 404 LOG(ERROR) << "Couldn't find display with id=" << display_id; |
| 405 } |
| 406 |
| 407 void DrmDisplayHostManager::OnTakeDisplayControl(bool status) { |
| 408 if (take_display_control_callback_.is_null()) { |
| 409 LOG(ERROR) << "No callback for take display control"; |
| 410 return; |
| 411 } |
| 412 |
| 413 DCHECK(display_externally_controlled_); |
| 414 DCHECK(display_control_change_pending_); |
| 415 |
| 416 if (status) { |
| 417 input_controller_->SetInputDevicesEnabled(true); |
| 418 display_externally_controlled_ = false; |
| 419 } |
| 420 |
| 421 base::ThreadTaskRunnerHandle::Get()->PostTask( |
| 422 FROM_HERE, base::Bind(take_display_control_callback_, status)); |
| 423 take_display_control_callback_.Reset(); |
| 424 display_control_change_pending_ = false; |
| 425 } |
| 426 |
| 427 void DrmDisplayHostManager::OnRelinquishDisplayControl(bool status) { |
| 428 if (relinquish_display_control_callback_.is_null()) { |
| 429 LOG(ERROR) << "No callback for relinquish display control"; |
| 430 return; |
| 431 } |
| 432 |
| 433 DCHECK(!display_externally_controlled_); |
| 434 DCHECK(display_control_change_pending_); |
| 435 |
| 436 if (status) { |
| 437 input_controller_->SetInputDevicesEnabled(false); |
| 438 display_externally_controlled_ = true; |
| 439 } |
| 440 |
| 441 base::ThreadTaskRunnerHandle::Get()->PostTask( |
| 442 FROM_HERE, base::Bind(relinquish_display_control_callback_, status)); |
| 443 relinquish_display_control_callback_.Reset(); |
| 444 display_control_change_pending_ = false; |
| 445 } |
| 446 |
| 447 void DrmDisplayHostManager::RunUpdateDisplaysCallback( |
| 448 const GetDisplaysCallback& callback) const { |
| 449 std::vector<DisplaySnapshot*> snapshots; |
| 450 for (auto* display : displays_) |
| 451 snapshots.push_back(display->snapshot()); |
| 452 |
| 453 callback.Run(snapshots); |
| 454 } |
| 455 |
| 456 void DrmDisplayHostManager::NotifyDisplayDelegate() const { |
| 457 if (delegate_) |
| 458 delegate_->OnConfigurationChanged(); |
| 459 } |
| 460 |
| 461 } // namespace ui |
OLD | NEW |