video.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395
  1. /*
  2. * video.c
  3. *
  4. * Copyright (C) 2016 Aleksandar Andrejevic <theflash@sdf.lonestar.org>
  5. *
  6. * This program is free software: you can redistribute it and/or modify
  7. * it under the terms of the GNU Affero General Public License as
  8. * published by the Free Software Foundation, either version 3 of the
  9. * License, or (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU Affero General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU Affero General Public License
  17. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  18. */
  19. #include <video.h>
  20. #include <exception.h>
  21. #include <pci.h>
  22. #include <heap.h>
  23. #include <memory.h>
  24. #include <process.h>
  25. #include <syscalls.h>
  26. static dword_t video_device_init(void);
  27. static dword_t video_device_cleanup(void);
  28. static dword_t video_device_read_write(device_t *device, void *buffer, size_t length, size_t *bytes_read);
  29. static dword_t video_device_ioctl(device_t *device, dword_t control_code, const void *in_buffer, size_t in_length, void *out_buffer, size_t out_length);
  30. static dword_t video_default_init(list_entry_t *video_devices);
  31. static dword_t video_default_cleanup(void);
  32. static char_dev_driver_t video_subsystem =
  33. {
  34. .init_proc = video_device_init,
  35. .cleanup_proc = video_device_cleanup,
  36. .read_proc = (char_dev_read_proc_t)video_device_read_write,
  37. .write_proc = (char_dev_write_proc_t)video_device_read_write,
  38. .ioctl_proc = video_device_ioctl,
  39. };
  40. static video_driver_t video_default_driver =
  41. {
  42. .init_proc = video_default_init,
  43. .cleanup_proc = video_default_cleanup,
  44. .control_proc = video_default_control,
  45. };
  46. static DECLARE_LIST(video_devices);
  47. static dword_t video_default_bitblt(video_device_t *device, video_bitblt_parameters_t *parameters)
  48. {
  49. dword_t i, j;
  50. dword_t *framebuffer;
  51. video_map_framebuffer_t map;
  52. if (device->current_mode.bpp > 32) return ERR_INVALID;
  53. dword_t top_x = MIN(parameters->source_x, parameters->dest_x);
  54. dword_t top_y = MIN(parameters->source_y, parameters->dest_y);
  55. dword_t bottom_x = MAX(parameters->source_x, parameters->dest_x);
  56. dword_t bottom_y = MAX(parameters->source_y, parameters->dest_y);
  57. map.address = NULL;
  58. map.offset = top_y * device->current_mode.scanline_size + ((top_x * device->current_mode.bpp) >> 3);
  59. map.size = (bottom_y * device->current_mode.scanline_size + ((bottom_x * device->current_mode.bpp + 7) >> 3)) - map.offset;
  60. processor_mode_t old_previous_mode = get_current_thread()->previous_mode;
  61. get_current_thread()->previous_mode = KERNEL_MODE;
  62. dword_t ret = device->driver->control_proc(device, IOCTL_VIDEO_MAP_FRAMEBUFFER, &map, sizeof(map), &framebuffer, sizeof(framebuffer));
  63. get_current_thread()->previous_mode = old_previous_mode;
  64. if (ret != ERR_SUCCESS) return ret;
  65. for (i = 0; i < parameters->height; i++)
  66. {
  67. dword_t sp_y = parameters->source_y >= parameters->dest_y ? i : parameters->height - i - 1;
  68. dword_t dp_y = sp_y + parameters->dest_y - parameters->source_y;
  69. for (j = 0; j < parameters->width; j++)
  70. {
  71. dword_t sp_x = parameters->source_x >= parameters->dest_x ? j : parameters->width - j - 1;
  72. dword_t dp_x = sp_x + parameters->dest_x - parameters->source_x;
  73. dword_t source_pixel = 0;
  74. dword_t dest_pixel = 0;
  75. uintptr_t source_offset = (sp_y * device->current_mode.scanline_size << 3) + sp_x * device->current_mode.bpp - (map.offset << 3);
  76. uintptr_t dest_offset = (dp_y * device->current_mode.scanline_size << 3) + dp_x * device->current_mode.bpp - (map.offset << 3);
  77. qword_t mask = ((qword_t)((1 << device->current_mode.bpp) - 1)) << (source_offset & 0x1F);
  78. dword_t low_mask = (dword_t)mask;
  79. dword_t high_mask = (dword_t)(mask >> 32);
  80. if (parameters->operation & BITBLT_SOURCE_ENABLED)
  81. {
  82. source_pixel = framebuffer[source_offset >> 5] >> (source_offset & 0x1F);
  83. if (high_mask) source_pixel |= framebuffer[(source_offset >> 5) + 1] << ((-dest_offset) & 0x1F);
  84. source_pixel &= (1 << device->current_mode.bpp) - 1;
  85. }
  86. if (parameters->operation & BITBLT_DEST_ENABLED)
  87. {
  88. dest_pixel = framebuffer[dest_offset >> 5] >> (dest_offset & 0x1F);
  89. if (high_mask) dest_pixel |= framebuffer[(dest_offset >> 5) + 1] << ((-dest_offset) & 0x1F);
  90. dest_pixel &= (1 << device->current_mode.bpp) - 1;
  91. }
  92. if (parameters->operation & BITBLT_SOURCE_INVERT) source_pixel = ~source_pixel;
  93. if (parameters->operation & BITBLT_DEST_INVERT) dest_pixel = ~dest_pixel;
  94. switch (parameters->operation & 3)
  95. {
  96. case BITBLT_OPERATION_NONE:
  97. dest_pixel = source_pixel;
  98. break;
  99. case BITBLT_OPERATION_AND:
  100. dest_pixel &= source_pixel;
  101. break;
  102. case BITBLT_OPERATION_OR:
  103. dest_pixel |= source_pixel;
  104. break;
  105. case BITBLT_OPERATION_XOR:
  106. dest_pixel ^= source_pixel;
  107. break;
  108. }
  109. if (parameters->operation & BITBLT_RESULT_INVERT) dest_pixel = ~dest_pixel;
  110. framebuffer[dest_offset >> 5] = (framebuffer[dest_offset >> 5] & ~low_mask) | (dest_pixel << (dest_offset & 0x1F));
  111. if (high_mask)
  112. {
  113. framebuffer[(dest_offset >> 5) + 1] = (framebuffer[(dest_offset >> 5) + 1] & ~high_mask) | (dest_pixel >> ((-dest_offset) & 0x1F));
  114. }
  115. }
  116. }
  117. unmap_memory(framebuffer);
  118. return ERR_SUCCESS;
  119. }
  120. static dword_t video_device_init(void)
  121. {
  122. list_entry_t *pci_device_list = get_pci_device_list_head();
  123. list_entry_t *ptr;
  124. int number = 0;
  125. for (ptr = pci_device_list->next; ptr != pci_device_list; ptr = ptr->next)
  126. {
  127. pci_device_t *device = CONTAINER_OF(ptr, pci_device_t, list);
  128. if (device->class == PCI_DISPLAY_DEVICE)
  129. {
  130. video_device_t *video = (video_device_t*)malloc(sizeof(video_device_t));
  131. ASSERT(video != NULL);
  132. video->header.driver = &video_subsystem;
  133. sprintf(video->header.name, "Video%d", number);
  134. video->header.resource = 0;
  135. video->pci_device = device;
  136. video->driver = &video_default_driver;
  137. memset(&video->current_mode, 0, sizeof(video->current_mode));
  138. video->current_mode.number = 3;
  139. video->current_mode.flags = VIDEO_MODE_ALPHANUMERIC | VIDEO_MODE_USES_PALETTE;
  140. video->current_mode.width = TEXT_WIDTH;
  141. video->current_mode.height = TEXT_HEIGHT;
  142. video->current_mode.bpp = 16;
  143. video->current_mode.scanline_size = TEXT_WIDTH * 2;
  144. video->current_mode.framebuffer_phys = TEXT_VIDEO_MEMORY;
  145. dword_t ret = register_char_device(&video->header);
  146. ASSERT(ret == ERR_SUCCESS);
  147. list_append(&video_devices, &video->list);
  148. number++;
  149. }
  150. }
  151. return ERR_SUCCESS;
  152. }
  153. static dword_t video_device_cleanup(void)
  154. {
  155. return ERR_SUCCESS;
  156. }
  157. static dword_t video_device_read_write(device_t *device, void *buffer, size_t length, size_t *bytes_read)
  158. {
  159. return ERR_INVALID;
  160. }
  161. static dword_t video_device_ioctl(device_t *device,
  162. dword_t control_code,
  163. const void *in_buffer,
  164. size_t in_length,
  165. void *out_buffer,
  166. size_t out_length)
  167. {
  168. video_device_t *video = CONTAINER_OF(device, video_device_t, header);
  169. return video->driver->control_proc(video, control_code, in_buffer, in_length, out_buffer, out_length);
  170. }
  171. static dword_t video_default_init(list_entry_t *video_devices)
  172. {
  173. UNUSED_PARAMETER(video_devices);
  174. return ERR_SUCCESS;
  175. }
  176. static dword_t video_default_cleanup(void)
  177. {
  178. return ERR_SUCCESS;
  179. }
  180. dword_t video_default_control(video_device_t *device,
  181. dword_t control_code,
  182. const void *in_buffer,
  183. size_t in_length,
  184. void *out_buffer,
  185. size_t out_length)
  186. {
  187. switch (control_code)
  188. {
  189. case IOCTL_VIDEO_GET_MODE:
  190. if (out_length >= sizeof(video_mode_t))
  191. {
  192. *(video_mode_t*)out_buffer = device->current_mode;
  193. return ERR_SUCCESS;
  194. }
  195. else
  196. {
  197. return ERR_SMALLBUF;
  198. }
  199. case IOCTL_VIDEO_SET_MODE:
  200. if (in_length >= sizeof(dword_t))
  201. {
  202. return (*(dword_t*)in_buffer == device->current_mode.number) ? ERR_SUCCESS : ERR_INVALID;
  203. }
  204. else
  205. {
  206. return ERR_SMALLBUF;
  207. }
  208. case IOCTL_VIDEO_LIST_MODES:
  209. if (out_length >= sizeof(dword_t))
  210. {
  211. dword_t *list = (dword_t*)out_buffer;
  212. list[0] = 1;
  213. if (out_length >= 2 * sizeof(dword_t))
  214. {
  215. list[1] = device->current_mode.number;
  216. return ERR_SUCCESS;
  217. }
  218. else
  219. {
  220. return ERR_SMALLBUF;
  221. }
  222. }
  223. else
  224. {
  225. return ERR_SMALLBUF;
  226. }
  227. case IOCTL_VIDEO_QUERY_MODE:
  228. if (in_length >= sizeof(dword_t) && out_length >= sizeof(video_mode_t))
  229. {
  230. if (*(dword_t*)in_buffer != device->current_mode.number) return ERR_INVALID;
  231. *(video_mode_t*)out_buffer = device->current_mode;
  232. return ERR_SUCCESS;
  233. }
  234. else
  235. {
  236. return ERR_SMALLBUF;
  237. }
  238. case IOCTL_VIDEO_MAP_FRAMEBUFFER:
  239. if (in_length >= sizeof(video_map_framebuffer_t) && out_length >= sizeof(void*))
  240. {
  241. video_map_framebuffer_t *params = (video_map_framebuffer_t*)in_buffer;
  242. void *address = params->address;
  243. dword_t size = device->current_mode.height * device->current_mode.scanline_size * (device->current_mode.bpp >> 3);
  244. if (params->offset >= size || params->size >= size || (params->offset + params->size) > size)
  245. {
  246. return ERR_INVALID;
  247. }
  248. if (get_previous_mode() == USER_MODE)
  249. {
  250. dword_t ret = map_memory_in_address_space(&get_current_process()->memory_space,
  251. (void*)(device->current_mode.framebuffer_phys + params->offset),
  252. &address,
  253. params->size,
  254. MEMORY_BLOCK_ACCESSIBLE | MEMORY_BLOCK_WRITABLE | MEMORY_BLOCK_USERMODE);
  255. if (ret != ERR_SUCCESS) return ret;
  256. }
  257. else
  258. {
  259. dword_t ret = map_memory((void*)(device->current_mode.framebuffer_phys + params->offset),
  260. &address,
  261. params->size,
  262. MEMORY_BLOCK_ACCESSIBLE | MEMORY_BLOCK_WRITABLE);
  263. if (ret != ERR_SUCCESS) return ret;
  264. }
  265. *(void**)out_buffer = address;
  266. return ERR_SUCCESS;
  267. }
  268. else
  269. {
  270. return ERR_SMALLBUF;
  271. }
  272. case IOCTL_VIDEO_BITBLT:
  273. if (in_length >= sizeof(video_bitblt_parameters_t))
  274. {
  275. return video_default_bitblt(device, (video_bitblt_parameters_t*)in_buffer);
  276. }
  277. else
  278. {
  279. return ERR_SMALLBUF;
  280. }
  281. case IOCTL_VIDEO_READ_PALETTE:
  282. case IOCTL_VIDEO_WRITE_PALETTE:
  283. case IOCTL_VIDEO_READ_FONT:
  284. case IOCTL_VIDEO_WRITE_FONT:
  285. return ERR_NOSYSCALL; // TODO
  286. case IOCTL_VIDEO_SET_TEXT_CURSOR:
  287. if (in_length >= sizeof(video_cursor_location_t))
  288. {
  289. video_cursor_location_t *location = (video_cursor_location_t*)in_buffer;
  290. word_t index = location->row * TEXT_WIDTH + location->column;
  291. outportb(VGA_CRTC_INDEX, 0x0F);
  292. outportb(VGA_CRTC_DATA, (byte_t) (index & 0xFF));
  293. outportb(VGA_CRTC_INDEX, 0x0E);
  294. outportb(VGA_CRTC_DATA, (byte_t) ((index >> 8) & 0xFF));
  295. }
  296. else
  297. {
  298. return ERR_SMALLBUF;
  299. }
  300. default:
  301. return ERR_INVALID;
  302. }
  303. }
  304. dword_t register_video_driver(video_driver_t *driver)
  305. {
  306. dword_t ret = driver->init_proc(&video_devices);
  307. return ret;
  308. }
  309. dword_t unregister_video_driver(video_driver_t *driver)
  310. {
  311. driver->cleanup_proc();
  312. return ERR_SUCCESS;
  313. }
  314. void video_init()
  315. {
  316. dword_t ret = register_char_dev_driver(&video_subsystem);
  317. ASSERT(ret == ERR_SUCCESS);
  318. video_map_framebuffer_t params = {
  319. .address = (void*)VIDEO_MEMORY,
  320. .offset = 0,
  321. .size = TEXT_WIDTH * TEXT_HEIGHT * 2
  322. };
  323. void *address;
  324. handle_t handle;
  325. ASSERT(get_previous_mode() == KERNEL_MODE);
  326. ret = syscall_open_file("CharDevices/Video0", &handle, 0, 0);
  327. ASSERT(ret == ERR_SUCCESS);
  328. ret = syscall_device_ioctl(handle, IOCTL_VIDEO_MAP_FRAMEBUFFER, &params, sizeof(params), &address, sizeof(address));
  329. ASSERT(ret == ERR_SUCCESS);
  330. syscall_close_object(handle);
  331. }