DisplayConnector.cpp 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428
  1. /*
  2. * Copyright (c) 2022, Liav A. <liavalb@hotmail.co.il>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include <Kernel/FileSystem/SysFS/Subsystems/DeviceIdentifiers/CharacterDevicesDirectory.h>
  7. #include <Kernel/FileSystem/SysFS/Subsystems/Devices/Graphics/DisplayConnector/DeviceDirectory.h>
  8. #include <Kernel/FileSystem/SysFS/Subsystems/Devices/Graphics/DisplayConnector/Directory.h>
  9. #include <Kernel/Graphics/DisplayConnector.h>
  10. #include <Kernel/Graphics/GraphicsManagement.h>
  11. #include <Kernel/Memory/MemoryManager.h>
  12. #include <LibC/sys/ioctl_numbers.h>
  13. namespace Kernel {
  14. DisplayConnector::DisplayConnector(PhysicalAddress framebuffer_address, size_t framebuffer_resource_size, bool enable_write_combine_optimization)
  15. : CharacterDevice(226, GraphicsManagement::the().allocate_minor_device_number())
  16. , m_enable_write_combine_optimization(enable_write_combine_optimization)
  17. , m_framebuffer_at_arbitrary_physical_range(false)
  18. , m_framebuffer_address(framebuffer_address)
  19. , m_framebuffer_resource_size(framebuffer_resource_size)
  20. {
  21. }
  22. DisplayConnector::DisplayConnector(size_t framebuffer_resource_size, bool enable_write_combine_optimization)
  23. : CharacterDevice(226, GraphicsManagement::the().allocate_minor_device_number())
  24. , m_enable_write_combine_optimization(enable_write_combine_optimization)
  25. , m_framebuffer_at_arbitrary_physical_range(true)
  26. , m_framebuffer_address({})
  27. , m_framebuffer_resource_size(framebuffer_resource_size)
  28. {
  29. }
  30. ErrorOr<Memory::Region*> DisplayConnector::mmap(Process& process, OpenFileDescription&, Memory::VirtualRange const& range, u64 offset, int prot, bool shared)
  31. {
  32. VERIFY(m_shared_framebuffer_vmobject);
  33. if (offset != 0)
  34. return Error::from_errno(ENOTSUP);
  35. return process.address_space().allocate_region_with_vmobject(
  36. range,
  37. *m_shared_framebuffer_vmobject,
  38. 0,
  39. "Mapped Framebuffer"sv,
  40. prot,
  41. shared);
  42. }
  43. ErrorOr<size_t> DisplayConnector::read(OpenFileDescription&, u64, UserOrKernelBuffer&, size_t)
  44. {
  45. return Error::from_errno(ENOTIMPL);
  46. }
  47. ErrorOr<size_t> DisplayConnector::write(OpenFileDescription&, u64, UserOrKernelBuffer const&, size_t)
  48. {
  49. return Error::from_errno(ENOTIMPL);
  50. }
  51. void DisplayConnector::will_be_destroyed()
  52. {
  53. GraphicsManagement::the().detach_display_connector({}, *this);
  54. VERIFY(m_symlink_sysfs_component);
  55. VERIFY(!is_block_device());
  56. SysFSCharacterDevicesDirectory::the().m_child_components.with([&](auto& list) -> void {
  57. list.remove(*m_symlink_sysfs_component);
  58. });
  59. m_symlink_sysfs_component.clear();
  60. SysFSDisplayConnectorsDirectory::the().unplug({}, *m_sysfs_device_directory);
  61. before_will_be_destroyed_remove_from_device_management();
  62. }
  63. void DisplayConnector::after_inserting()
  64. {
  65. after_inserting_add_to_device_management();
  66. auto sysfs_display_connector_device_directory = DisplayConnectorSysFSDirectory::create(SysFSDisplayConnectorsDirectory::the(), *this);
  67. m_sysfs_device_directory = sysfs_display_connector_device_directory;
  68. SysFSDisplayConnectorsDirectory::the().plug({}, *sysfs_display_connector_device_directory);
  69. VERIFY(!m_symlink_sysfs_component);
  70. auto sys_fs_component = MUST(SysFSSymbolicLinkDeviceComponent::try_create(SysFSDeviceIdentifiersDirectory::the(), *this, *m_sysfs_device_directory));
  71. m_symlink_sysfs_component = sys_fs_component;
  72. VERIFY(!is_block_device());
  73. SysFSCharacterDevicesDirectory::the().m_child_components.with([&](auto& list) -> void {
  74. list.append(*m_symlink_sysfs_component);
  75. });
  76. auto rounded_size = MUST(Memory::page_round_up(m_framebuffer_resource_size));
  77. if (!m_framebuffer_at_arbitrary_physical_range) {
  78. VERIFY(m_framebuffer_address.value().page_base() == m_framebuffer_address.value());
  79. m_shared_framebuffer_vmobject = MUST(Memory::SharedFramebufferVMObject::try_create_for_physical_range(m_framebuffer_address.value(), rounded_size));
  80. m_framebuffer_region = MUST(MM.allocate_kernel_region(m_framebuffer_address.value().page_base(), rounded_size, "Framebuffer"sv, Memory::Region::Access::ReadWrite));
  81. } else {
  82. m_shared_framebuffer_vmobject = MUST(Memory::SharedFramebufferVMObject::try_create_at_arbitrary_physical_range(rounded_size));
  83. m_framebuffer_region = MUST(MM.allocate_kernel_region_with_vmobject(m_shared_framebuffer_vmobject->real_writes_framebuffer_vmobject(), rounded_size, "Framebuffer"sv, Memory::Region::Access::ReadWrite));
  84. }
  85. m_framebuffer_data = m_framebuffer_region->vaddr().as_ptr();
  86. m_fake_writes_framebuffer_region = MUST(MM.allocate_kernel_region_with_vmobject(m_shared_framebuffer_vmobject->fake_writes_framebuffer_vmobject(), rounded_size, "Fake Writes Framebuffer"sv, Memory::Region::Access::ReadWrite));
  87. GraphicsManagement::the().attach_new_display_connector({}, *this);
  88. if (m_enable_write_combine_optimization) {
  89. [[maybe_unused]] auto result = m_framebuffer_region->set_write_combine(true);
  90. }
  91. }
  92. bool DisplayConnector::console_mode() const
  93. {
  94. VERIFY(m_control_lock.is_locked());
  95. return m_console_mode;
  96. }
  97. void DisplayConnector::set_display_mode(Badge<GraphicsManagement>, DisplayMode mode)
  98. {
  99. SpinlockLocker locker(m_control_lock);
  100. {
  101. SpinlockLocker locker(m_modeset_lock);
  102. [[maybe_unused]] auto result = set_y_offset(0);
  103. }
  104. m_console_mode = mode == DisplayMode::Console ? true : false;
  105. if (m_console_mode) {
  106. VERIFY(m_framebuffer_region->size() == m_fake_writes_framebuffer_region->size());
  107. memcpy(m_fake_writes_framebuffer_region->vaddr().as_ptr(), m_framebuffer_region->vaddr().as_ptr(), m_framebuffer_region->size());
  108. m_shared_framebuffer_vmobject->switch_to_fake_sink_framebuffer_writes({});
  109. enable_console();
  110. } else {
  111. disable_console();
  112. m_shared_framebuffer_vmobject->switch_to_real_framebuffer_writes({});
  113. VERIFY(m_framebuffer_region->size() == m_fake_writes_framebuffer_region->size());
  114. memcpy(m_framebuffer_region->vaddr().as_ptr(), m_fake_writes_framebuffer_region->vaddr().as_ptr(), m_framebuffer_region->size());
  115. }
  116. }
  117. ErrorOr<void> DisplayConnector::initialize_edid_for_generic_monitor(Optional<Array<u8, 3>> possible_manufacturer_id_string)
  118. {
  119. u8 raw_manufacturer_id[2] = { 0x0, 0x0 };
  120. if (possible_manufacturer_id_string.has_value()) {
  121. Array<u8, 3> manufacturer_id_string = possible_manufacturer_id_string.release_value();
  122. u8 byte1 = (((static_cast<u8>(manufacturer_id_string[0]) - '@') & 0x1f) << 2) | (((static_cast<u8>(manufacturer_id_string[1]) - '@') >> 3) & 3);
  123. u8 byte2 = ((static_cast<u8>(manufacturer_id_string[2]) - '@') & 0x1f) | (((static_cast<u8>(manufacturer_id_string[1]) - '@') << 5) & 0xe0);
  124. Array<u8, 2> manufacturer_id_string_packed_bytes = { byte1, byte2 };
  125. raw_manufacturer_id[0] = manufacturer_id_string_packed_bytes[1];
  126. raw_manufacturer_id[1] = manufacturer_id_string_packed_bytes[0];
  127. }
  128. Array<u8, 128> virtual_monitor_edid = {
  129. 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, /* header */
  130. raw_manufacturer_id[1], raw_manufacturer_id[0], /* manufacturer */
  131. 0x00, 0x00, /* product code */
  132. 0x00, 0x00, 0x00, 0x00, /* serial number goes here */
  133. 0x01, /* week of manufacture */
  134. 0x00, /* year of manufacture */
  135. 0x01, 0x03, /* EDID version */
  136. 0x80, /* capabilities - digital */
  137. 0x00, /* horiz. res in cm, zero for projectors */
  138. 0x00, /* vert. res in cm */
  139. 0x78, /* display gamma (120 == 2.2). */
  140. 0xEE, /* features (standby, suspend, off, RGB, std */
  141. /* colour space, preferred timing mode) */
  142. 0xEE, 0x91, 0xA3, 0x54, 0x4C, 0x99, 0x26, 0x0F, 0x50, 0x54,
  143. /* chromaticity for standard colour space. */
  144. 0x00, 0x00, 0x00, /* no default timings */
  145. 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
  146. 0x01, 0x01,
  147. 0x01, 0x01, 0x01, 0x01, /* no standard timings */
  148. 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x02, 0x02,
  149. 0x02, 0x02,
  150. /* descriptor block 1 goes below */
  151. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  152. /* descriptor block 2, monitor ranges */
  153. 0x00, 0x00, 0x00, 0xFD, 0x00,
  154. 0x00, 0xC8, 0x00, 0xC8, 0x64, 0x00, 0x0A, 0x20, 0x20, 0x20,
  155. 0x20, 0x20,
  156. /* 0-200Hz vertical, 0-200KHz horizontal, 1000MHz pixel clock */
  157. 0x20,
  158. /* descriptor block 3, monitor name */
  159. 0x00, 0x00, 0x00, 0xFC, 0x00,
  160. 'G', 'e', 'n', 'e', 'r', 'i', 'c', 'S', 'c', 'r', 'e', 'e', 'n',
  161. /* descriptor block 4: dummy data */
  162. 0x00, 0x00, 0x00, 0x10, 0x00,
  163. 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20,
  164. 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
  165. 0x20,
  166. 0x00, /* number of extensions */
  167. 0x00 /* checksum goes here */
  168. };
  169. // Note: Fix checksum to avoid warnings about checksum mismatch.
  170. size_t checksum = 0;
  171. // Note: Read all 127 bytes to add them to the checksum. Byte 128 is zeroed so
  172. // we could technically add it to the sum result, but it could lead to an error if it contained
  173. // a non-zero value, so we are not using it.
  174. for (size_t index = 0; index < sizeof(virtual_monitor_edid) - 1; index++)
  175. checksum += virtual_monitor_edid[index];
  176. virtual_monitor_edid[127] = 0x100 - checksum;
  177. set_edid_bytes(virtual_monitor_edid);
  178. return {};
  179. }
  180. void DisplayConnector::set_edid_bytes(Array<u8, 128> const& edid_bytes)
  181. {
  182. memcpy((u8*)m_edid_bytes, edid_bytes.data(), sizeof(m_edid_bytes));
  183. if (auto parsed_edid = EDID::Parser::from_bytes({ m_edid_bytes, sizeof(m_edid_bytes) }); !parsed_edid.is_error()) {
  184. m_edid_parser = parsed_edid.release_value();
  185. m_edid_valid = true;
  186. } else {
  187. dmesgln("DisplayConnector: Print offending EDID");
  188. for (size_t x = 0; x < 128; x = x + 16) {
  189. dmesgln("{:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x}",
  190. m_edid_bytes[x], m_edid_bytes[x + 1], m_edid_bytes[x + 2], m_edid_bytes[x + 3],
  191. m_edid_bytes[x + 4], m_edid_bytes[x + 5], m_edid_bytes[x + 6], m_edid_bytes[x + 7],
  192. m_edid_bytes[x + 8], m_edid_bytes[x + 9], m_edid_bytes[x + 10], m_edid_bytes[x + 11],
  193. m_edid_bytes[x + 12], m_edid_bytes[x + 13], m_edid_bytes[x + 14], m_edid_bytes[x + 15]);
  194. }
  195. dmesgln("DisplayConnector: Parsing EDID failed: {}", parsed_edid.error());
  196. }
  197. }
  198. ErrorOr<void> DisplayConnector::flush_rectangle(size_t, FBRect const&)
  199. {
  200. return Error::from_errno(ENOTSUP);
  201. }
  202. DisplayConnector::ModeSetting DisplayConnector::current_mode_setting() const
  203. {
  204. SpinlockLocker locker(m_modeset_lock);
  205. return m_current_mode_setting;
  206. }
  207. ErrorOr<ByteBuffer> DisplayConnector::get_edid() const
  208. {
  209. if (!m_edid_valid)
  210. return Error::from_errno(ENOTIMPL);
  211. return ByteBuffer::copy(m_edid_bytes, sizeof(m_edid_bytes));
  212. }
  213. ErrorOr<void> DisplayConnector::ioctl(OpenFileDescription&, unsigned request, Userspace<void*> arg)
  214. {
  215. if (request != GRAPHICS_IOCTL_GET_HEAD_EDID) {
  216. // Allow anyone to query the EDID. Eventually we'll publish the current EDID on /sys
  217. // so it doesn't really make sense to require the video pledge to query it.
  218. TRY(Process::current().require_promise(Pledge::video));
  219. }
  220. // TODO: We really should have ioctls for destroying resources as well
  221. switch (request) {
  222. case GRAPHICS_IOCTL_GET_PROPERTIES: {
  223. auto user_properties = static_ptr_cast<GraphicsConnectorProperties*>(arg);
  224. GraphicsConnectorProperties properties {};
  225. properties.flushing_support = flush_support();
  226. properties.doublebuffer_support = double_framebuffering_capable();
  227. properties.partial_flushing_support = partial_flush_support();
  228. properties.refresh_rate_support = refresh_rate_support();
  229. return copy_to_user(user_properties, &properties);
  230. }
  231. case GRAPHICS_IOCTL_GET_HEAD_MODE_SETTING: {
  232. auto user_head_mode_setting = static_ptr_cast<GraphicsHeadModeSetting*>(arg);
  233. GraphicsHeadModeSetting head_mode_setting {};
  234. TRY(copy_from_user(&head_mode_setting, user_head_mode_setting));
  235. {
  236. SpinlockLocker control_locker(m_control_lock);
  237. head_mode_setting.horizontal_stride = m_current_mode_setting.horizontal_stride;
  238. head_mode_setting.pixel_clock_in_khz = m_current_mode_setting.pixel_clock_in_khz;
  239. head_mode_setting.horizontal_active = m_current_mode_setting.horizontal_active;
  240. head_mode_setting.horizontal_front_porch_pixels = m_current_mode_setting.horizontal_front_porch_pixels;
  241. head_mode_setting.horizontal_sync_time_pixels = m_current_mode_setting.horizontal_sync_time_pixels;
  242. head_mode_setting.horizontal_blank_pixels = m_current_mode_setting.horizontal_blank_pixels;
  243. head_mode_setting.vertical_active = m_current_mode_setting.vertical_active;
  244. head_mode_setting.vertical_front_porch_lines = m_current_mode_setting.vertical_front_porch_lines;
  245. head_mode_setting.vertical_sync_time_lines = m_current_mode_setting.vertical_sync_time_lines;
  246. head_mode_setting.vertical_blank_lines = m_current_mode_setting.vertical_blank_lines;
  247. head_mode_setting.horizontal_offset = m_current_mode_setting.horizontal_offset;
  248. head_mode_setting.vertical_offset = m_current_mode_setting.vertical_offset;
  249. }
  250. return copy_to_user(user_head_mode_setting, &head_mode_setting);
  251. }
  252. case GRAPHICS_IOCTL_GET_HEAD_EDID: {
  253. auto user_head_edid = static_ptr_cast<GraphicsHeadEDID*>(arg);
  254. GraphicsHeadEDID head_edid {};
  255. TRY(copy_from_user(&head_edid, user_head_edid));
  256. auto edid_bytes = TRY(get_edid());
  257. if (head_edid.bytes != nullptr) {
  258. // Only return the EDID if a buffer was provided. Either way,
  259. // we'll write back the bytes_size with the actual size
  260. if (head_edid.bytes_size < edid_bytes.size()) {
  261. head_edid.bytes_size = edid_bytes.size();
  262. TRY(copy_to_user(user_head_edid, &head_edid));
  263. return Error::from_errno(EOVERFLOW);
  264. }
  265. TRY(copy_to_user(head_edid.bytes, (void const*)edid_bytes.data(), edid_bytes.size()));
  266. }
  267. head_edid.bytes_size = edid_bytes.size();
  268. return copy_to_user(user_head_edid, &head_edid);
  269. }
  270. case GRAPHICS_IOCTL_SET_HEAD_MODE_SETTING: {
  271. auto user_mode_setting = static_ptr_cast<GraphicsHeadModeSetting const*>(arg);
  272. auto head_mode_setting = TRY(copy_typed_from_user(user_mode_setting));
  273. if (head_mode_setting.horizontal_stride < 0)
  274. return Error::from_errno(EINVAL);
  275. if (head_mode_setting.pixel_clock_in_khz < 0)
  276. return Error::from_errno(EINVAL);
  277. if (head_mode_setting.horizontal_active < 0)
  278. return Error::from_errno(EINVAL);
  279. if (head_mode_setting.horizontal_front_porch_pixels < 0)
  280. return Error::from_errno(EINVAL);
  281. if (head_mode_setting.horizontal_sync_time_pixels < 0)
  282. return Error::from_errno(EINVAL);
  283. if (head_mode_setting.horizontal_blank_pixels < 0)
  284. return Error::from_errno(EINVAL);
  285. if (head_mode_setting.vertical_active < 0)
  286. return Error::from_errno(EINVAL);
  287. if (head_mode_setting.vertical_front_porch_lines < 0)
  288. return Error::from_errno(EINVAL);
  289. if (head_mode_setting.vertical_sync_time_lines < 0)
  290. return Error::from_errno(EINVAL);
  291. if (head_mode_setting.vertical_blank_lines < 0)
  292. return Error::from_errno(EINVAL);
  293. if (head_mode_setting.horizontal_offset < 0)
  294. return Error::from_errno(EINVAL);
  295. if (head_mode_setting.vertical_offset < 0)
  296. return Error::from_errno(EINVAL);
  297. {
  298. SpinlockLocker control_locker(m_control_lock);
  299. ModeSetting requested_mode_setting;
  300. requested_mode_setting.horizontal_stride = 0;
  301. requested_mode_setting.pixel_clock_in_khz = head_mode_setting.pixel_clock_in_khz;
  302. requested_mode_setting.horizontal_active = head_mode_setting.horizontal_active;
  303. requested_mode_setting.horizontal_front_porch_pixels = head_mode_setting.horizontal_front_porch_pixels;
  304. requested_mode_setting.horizontal_sync_time_pixels = head_mode_setting.horizontal_sync_time_pixels;
  305. requested_mode_setting.horizontal_blank_pixels = head_mode_setting.horizontal_blank_pixels;
  306. requested_mode_setting.vertical_active = head_mode_setting.vertical_active;
  307. requested_mode_setting.vertical_front_porch_lines = head_mode_setting.vertical_front_porch_lines;
  308. requested_mode_setting.vertical_sync_time_lines = head_mode_setting.vertical_sync_time_lines;
  309. requested_mode_setting.vertical_blank_lines = head_mode_setting.vertical_blank_lines;
  310. requested_mode_setting.horizontal_offset = head_mode_setting.horizontal_offset;
  311. requested_mode_setting.vertical_offset = head_mode_setting.vertical_offset;
  312. TRY(set_mode_setting(requested_mode_setting));
  313. }
  314. return {};
  315. }
  316. case GRAPHICS_IOCTL_SET_SAFE_HEAD_MODE_SETTING: {
  317. SpinlockLocker control_locker(m_control_lock);
  318. TRY(set_safe_mode_setting());
  319. return {};
  320. }
  321. case GRAPHICS_IOCTL_SET_HEAD_VERTICAL_OFFSET_BUFFER: {
  322. // FIXME: We silently ignore the request if we are in console mode.
  323. // WindowServer is not ready yet to handle errors such as EBUSY currently.
  324. SpinlockLocker control_locker(m_control_lock);
  325. if (console_mode()) {
  326. return {};
  327. }
  328. auto user_head_vertical_buffer_offset = static_ptr_cast<GraphicsHeadVerticalOffset const*>(arg);
  329. auto head_vertical_buffer_offset = TRY(copy_typed_from_user(user_head_vertical_buffer_offset));
  330. SpinlockLocker locker(m_modeset_lock);
  331. if (head_vertical_buffer_offset.offsetted < 0 || head_vertical_buffer_offset.offsetted > 1)
  332. return Error::from_errno(EINVAL);
  333. TRY(set_y_offset(head_vertical_buffer_offset.offsetted == 0 ? 0 : m_current_mode_setting.vertical_active));
  334. if (head_vertical_buffer_offset.offsetted == 0)
  335. m_vertical_offsetted = false;
  336. else
  337. m_vertical_offsetted = true;
  338. return {};
  339. }
  340. case GRAPHICS_IOCTL_GET_HEAD_VERTICAL_OFFSET_BUFFER: {
  341. auto user_head_vertical_buffer_offset = static_ptr_cast<GraphicsHeadVerticalOffset*>(arg);
  342. GraphicsHeadVerticalOffset head_vertical_buffer_offset {};
  343. TRY(copy_from_user(&head_vertical_buffer_offset, user_head_vertical_buffer_offset));
  344. head_vertical_buffer_offset.offsetted = m_vertical_offsetted;
  345. return copy_to_user(user_head_vertical_buffer_offset, &head_vertical_buffer_offset);
  346. }
  347. case GRAPHICS_IOCTL_FLUSH_HEAD_BUFFERS: {
  348. if (console_mode())
  349. return {};
  350. if (!partial_flush_support())
  351. return Error::from_errno(ENOTSUP);
  352. MutexLocker locker(m_flushing_lock);
  353. auto user_flush_rects = static_ptr_cast<FBFlushRects const*>(arg);
  354. auto flush_rects = TRY(copy_typed_from_user(user_flush_rects));
  355. if (Checked<unsigned>::multiplication_would_overflow(flush_rects.count, sizeof(FBRect)))
  356. return Error::from_errno(EFAULT);
  357. if (flush_rects.count > 0) {
  358. for (unsigned i = 0; i < flush_rects.count; i++) {
  359. FBRect user_dirty_rect;
  360. TRY(copy_from_user(&user_dirty_rect, &flush_rects.rects[i]));
  361. {
  362. SpinlockLocker control_locker(m_control_lock);
  363. if (console_mode()) {
  364. return {};
  365. }
  366. TRY(flush_rectangle(flush_rects.buffer_index, user_dirty_rect));
  367. }
  368. }
  369. }
  370. return {};
  371. };
  372. case GRAPHICS_IOCTL_FLUSH_HEAD: {
  373. // FIXME: We silently ignore the request if we are in console mode.
  374. // WindowServer is not ready yet to handle errors such as EBUSY currently.
  375. MutexLocker locker(m_flushing_lock);
  376. SpinlockLocker control_locker(m_control_lock);
  377. if (console_mode()) {
  378. return {};
  379. }
  380. if (!flush_support())
  381. return Error::from_errno(ENOTSUP);
  382. TRY(flush_first_surface());
  383. return {};
  384. }
  385. }
  386. return EINVAL;
  387. }
  388. }