Move system calls to the SDK and normalize their names
[monolithium.git] / kernel / src / video.c
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
20 #include <video.h>
21 #include <exception.h>
22 #include <pci.h>
23 #include <heap.h>
24 #include <memory.h>
25 #include <process.h>
26 #include <syscalls.h>
27
28 static dword_t video_device_init(void);
29 static dword_t video_device_cleanup(void);
30 static dword_t video_device_read_write(device_t *device, void *buffer, size_t length, size_t *bytes_read);
31 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);
32
33 static dword_t video_default_init(list_entry_t *video_devices);
34 static dword_t video_default_cleanup(void);
35
36 static char_dev_driver_t video_subsystem =
37 {
38     .init_proc = video_device_init,
39     .cleanup_proc = video_device_cleanup,
40     .read_proc = (char_dev_read_proc_t)video_device_read_write,
41     .write_proc = (char_dev_write_proc_t)video_device_read_write,
42     .ioctl_proc = video_device_ioctl,
43 };
44
45 static video_driver_t video_default_driver =
46 {
47     .init_proc = video_default_init,
48     .cleanup_proc = video_default_cleanup,
49     .control_proc = video_default_control,
50 };
51
52 static DECLARE_LIST(video_devices);
53
54 static dword_t video_default_bitblt(video_device_t *device, video_bitblt_parameters_t *parameters)
55 {
56     dword_t i, j;
57     dword_t *framebuffer;
58     video_map_framebuffer_t map;
59
60     if (device->current_mode.bpp > 32) return ERR_INVALID;
61
62     dword_t top_x = MIN(parameters->source_x, parameters->dest_x);
63     dword_t top_y = MIN(parameters->source_y, parameters->dest_y);
64     dword_t bottom_x = MAX(parameters->source_x, parameters->dest_x);
65     dword_t bottom_y = MAX(parameters->source_y, parameters->dest_y);
66
67     map.address = NULL;
68     map.offset = top_y * device->current_mode.scanline_size + ((top_x * device->current_mode.bpp) >> 3);
69     map.size = (bottom_y * device->current_mode.scanline_size + ((bottom_x * device->current_mode.bpp + 7) >> 3)) - map.offset;
70
71     processor_mode_t old_previous_mode = get_current_thread()->previous_mode;
72     get_current_thread()->previous_mode = KERNEL_MODE;
73     dword_t ret = device->driver->control_proc(device, IOCTL_VIDEO_MAP_FRAMEBUFFER, &map, sizeof(map), &framebuffer, sizeof(framebuffer));
74     get_current_thread()->previous_mode = old_previous_mode;
75     if (ret != ERR_SUCCESS) return ret;
76
77     for (i = 0; i < parameters->height; i++)
78     {
79         dword_t sp_y = parameters->source_y >= parameters->dest_y ? i : parameters->height - i - 1;
80         dword_t dp_y = sp_y + parameters->dest_y - parameters->source_y;
81
82         for (j = 0; j < parameters->width; j++)
83         {
84             dword_t sp_x = parameters->source_x >= parameters->dest_x ? j : parameters->width - j - 1;
85             dword_t dp_x = sp_x + parameters->dest_x - parameters->source_x;
86
87             dword_t source_pixel = 0;
88             dword_t dest_pixel = 0;
89
90             uintptr_t source_offset = (sp_y * device->current_mode.scanline_size << 3) + sp_x * device->current_mode.bpp - (map.offset << 3);
91             uintptr_t dest_offset = (dp_y * device->current_mode.scanline_size << 3) + dp_x * device->current_mode.bpp - (map.offset << 3);
92
93             qword_t mask = ((qword_t)((1 << device->current_mode.bpp) - 1)) << (source_offset & 0x1F);
94             dword_t low_mask = (dword_t)mask;
95             dword_t high_mask = (dword_t)(mask >> 32);
96
97             if (parameters->operation & BITBLT_SOURCE_ENABLED)
98             {
99                 source_pixel = framebuffer[source_offset >> 5] >> (source_offset & 0x1F);
100                 if (high_mask) source_pixel |= framebuffer[(source_offset >> 5) + 1] << ((-dest_offset) & 0x1F);
101                 source_pixel &= (1 << device->current_mode.bpp) - 1;
102             }
103
104             if (parameters->operation & BITBLT_DEST_ENABLED)
105             {
106                 dest_pixel = framebuffer[dest_offset >> 5] >> (dest_offset & 0x1F);
107                 if (high_mask) dest_pixel |= framebuffer[(dest_offset >> 5) + 1] << ((-dest_offset) & 0x1F);
108                 dest_pixel &= (1 << device->current_mode.bpp) - 1;
109             }
110
111             if (parameters->operation & BITBLT_SOURCE_INVERT) source_pixel = ~source_pixel;
112             if (parameters->operation & BITBLT_DEST_INVERT) dest_pixel = ~dest_pixel;
113
114             switch (parameters->operation & 3)
115             {
116             case BITBLT_OPERATION_NONE:
117                 dest_pixel = source_pixel;
118                 break;
119
120             case BITBLT_OPERATION_AND:
121                 dest_pixel &= source_pixel;
122                 break;
123
124             case BITBLT_OPERATION_OR:
125                 dest_pixel |= source_pixel;
126                 break;
127
128             case BITBLT_OPERATION_XOR:
129                 dest_pixel ^= source_pixel;
130                 break;
131             }
132
133             if (parameters->operation & BITBLT_RESULT_INVERT) dest_pixel = ~dest_pixel;
134
135             framebuffer[dest_offset >> 5] = (framebuffer[dest_offset >> 5] & ~low_mask) | (dest_pixel << (dest_offset & 0x1F));
136
137             if (high_mask)
138             {
139                 framebuffer[(dest_offset >> 5) + 1] = (framebuffer[(dest_offset >> 5) + 1] & ~high_mask) | (dest_pixel >> ((-dest_offset) & 0x1F));
140             }
141         }
142     }
143
144     unmap_memory(framebuffer);
145     return ERR_SUCCESS;
146 }
147
148 static dword_t video_device_init(void)
149 {
150     list_entry_t *pci_device_list = get_pci_device_list_head();
151     list_entry_t *ptr;
152     int number = 0;
153
154     for (ptr = pci_device_list->next; ptr != pci_device_list; ptr = ptr->next)
155     {
156         pci_device_t *device = CONTAINER_OF(ptr, pci_device_t, list);
157
158         if (device->class == PCI_DISPLAY_DEVICE)
159         {
160             video_device_t *video = (video_device_t*)malloc(sizeof(video_device_t));
161             ASSERT(video != NULL);
162
163             video->header.driver = &video_subsystem;
164             sprintf(video->header.name, "Video%d", number);
165             video->header.resource = 0;
166             video->pci_device = device;
167             video->driver = &video_default_driver;
168
169             memset(&video->current_mode, 0, sizeof(video->current_mode));
170             video->current_mode.number = 3;
171             video->current_mode.flags = VIDEO_MODE_ALPHANUMERIC | VIDEO_MODE_USES_PALETTE;
172             video->current_mode.width = TEXT_WIDTH;
173             video->current_mode.height = TEXT_HEIGHT;
174             video->current_mode.bpp = 16;
175             video->current_mode.scanline_size = TEXT_WIDTH * 2;
176             video->current_mode.framebuffer_phys = TEXT_VIDEO_MEMORY;
177
178             dword_t ret = register_char_device(&video->header);
179             ASSERT(ret == ERR_SUCCESS);
180
181             list_append(&video_devices, &video->list);
182             number++;
183         }
184     }
185
186     return ERR_SUCCESS;
187 }
188
189 static dword_t video_device_cleanup(void)
190 {
191     return ERR_SUCCESS;
192 }
193
194 static dword_t video_device_read_write(device_t *device, void *buffer, size_t length, size_t *bytes_read)
195 {
196     return ERR_INVALID;
197 }
198
199 static dword_t video_device_ioctl(device_t *device,
200                                   dword_t control_code,
201                                   const void *in_buffer,
202                                   size_t in_length,
203                                   void *out_buffer,
204                                   size_t out_length)
205 {
206     video_device_t *video = CONTAINER_OF(device, video_device_t, header);
207     return video->driver->control_proc(video, control_code, in_buffer, in_length, out_buffer, out_length);
208 }
209
210 static dword_t video_default_init(list_entry_t *video_devices)
211 {
212     UNUSED_PARAMETER(video_devices);
213     return ERR_SUCCESS;
214 }
215
216 static dword_t video_default_cleanup(void)
217 {
218     return ERR_SUCCESS;
219 }
220
221 dword_t video_default_control(video_device_t *device,
222                               dword_t control_code,
223                               const void *in_buffer,
224                               size_t in_length,
225                               void *out_buffer,
226                               size_t out_length)
227 {
228     switch (control_code)
229     {
230     case IOCTL_VIDEO_GET_MODE:
231         if (out_length >= sizeof(video_mode_t))
232         {
233             *(video_mode_t*)out_buffer = device->current_mode;
234             return ERR_SUCCESS;
235         }
236         else
237         {
238             return ERR_SMALLBUF;
239         }
240
241     case IOCTL_VIDEO_SET_MODE:
242         if (in_length >= sizeof(dword_t))
243         {
244             return (*(dword_t*)in_buffer == device->current_mode.number) ? ERR_SUCCESS : ERR_INVALID;
245         }
246         else
247         {
248             return ERR_SMALLBUF;
249         }
250
251     case IOCTL_VIDEO_LIST_MODES:
252         if (out_length >= sizeof(dword_t))
253         {
254             dword_t *list = (dword_t*)out_buffer;
255             list[0] = 1;
256
257             if (out_length >= 2 * sizeof(dword_t))
258             {
259                 list[1] = device->current_mode.number;
260                 return ERR_SUCCESS;
261             }
262             else
263             {
264                 return ERR_SMALLBUF;
265             }
266         }
267         else
268         {
269             return ERR_SMALLBUF;
270         }
271
272     case IOCTL_VIDEO_QUERY_MODE:
273         if (in_length >= sizeof(dword_t) && out_length >= sizeof(video_mode_t))
274         {
275             if (*(dword_t*)in_buffer != device->current_mode.number) return ERR_INVALID;
276
277             *(video_mode_t*)out_buffer = device->current_mode;
278             return ERR_SUCCESS;
279         }
280         else
281         {
282             return ERR_SMALLBUF;
283         }
284
285     case IOCTL_VIDEO_MAP_FRAMEBUFFER:
286         if (in_length >= sizeof(video_map_framebuffer_t) && out_length >= sizeof(void*))
287         {
288             video_map_framebuffer_t *params = (video_map_framebuffer_t*)in_buffer;
289             void *address = params->address;
290             dword_t size = device->current_mode.height * device->current_mode.scanline_size * (device->current_mode.bpp >> 3);
291
292             if (params->offset >= size || params->size >= size || (params->offset + params->size) > size)
293             {
294                 return ERR_INVALID;
295             }
296
297             if (get_previous_mode() == USER_MODE)
298             {
299                 dword_t ret = map_memory_in_address_space(&get_current_process()->memory_space,
300                                                           (void*)(device->current_mode.framebuffer_phys + params->offset),
301                                                           &address,
302                                                           params->size,
303                                                           MEMORY_BLOCK_ACCESSIBLE | MEMORY_BLOCK_WRITABLE | MEMORY_BLOCK_USERMODE);
304                 if (ret != ERR_SUCCESS) return ret;
305             }
306             else
307             {
308                 dword_t ret = map_memory((void*)(device->current_mode.framebuffer_phys + params->offset),
309                                          &address,
310                                          params->size,
311                                          MEMORY_BLOCK_ACCESSIBLE | MEMORY_BLOCK_WRITABLE);
312                 if (ret != ERR_SUCCESS) return ret;
313             }
314
315             *(void**)out_buffer = address;
316             return ERR_SUCCESS;
317         }
318         else
319         {
320             return ERR_SMALLBUF;
321         }
322
323     case IOCTL_VIDEO_BITBLT:
324         if (in_length >= sizeof(video_bitblt_parameters_t))
325         {
326             return video_default_bitblt(device, (video_bitblt_parameters_t*)in_buffer);
327         }
328         else
329         {
330             return ERR_SMALLBUF;
331         }
332
333     case IOCTL_VIDEO_READ_PALETTE:
334     case IOCTL_VIDEO_WRITE_PALETTE:
335     case IOCTL_VIDEO_READ_FONT:
336     case IOCTL_VIDEO_WRITE_FONT:
337         return ERR_NOSYSCALL; // TODO
338
339     case IOCTL_VIDEO_SET_TEXT_CURSOR:
340         if (in_length >= sizeof(video_cursor_location_t))
341         {
342             video_cursor_location_t *location = (video_cursor_location_t*)in_buffer;
343             word_t index = location->row * TEXT_WIDTH + location->column;
344
345             outportb(VGA_CRTC_INDEX, 0x0F);
346             outportb(VGA_CRTC_DATA, (byte_t) (index & 0xFF));
347             outportb(VGA_CRTC_INDEX, 0x0E);
348             outportb(VGA_CRTC_DATA, (byte_t) ((index >> 8) & 0xFF));
349         }
350         else
351         {
352             return ERR_SMALLBUF;
353         }
354
355     default:
356         return ERR_INVALID;
357     }
358 }
359
360 dword_t register_video_driver(video_driver_t *driver)
361 {
362     dword_t ret = driver->init_proc(&video_devices);
363     return ret;
364 }
365
366 dword_t unregister_video_driver(video_driver_t *driver)
367 {
368     driver->cleanup_proc();
369     return ERR_SUCCESS;
370 }
371
372 void video_init()
373 {
374     dword_t ret = register_char_dev_driver(&video_subsystem);
375     ASSERT(ret == ERR_SUCCESS);
376
377     video_map_framebuffer_t params = {
378         .address = (void*)VIDEO_MEMORY,
379         .offset = 0,
380         .size = TEXT_WIDTH * TEXT_HEIGHT * 2
381     };
382
383     void *address;
384     handle_t handle;
385
386     ASSERT(get_previous_mode() == KERNEL_MODE);
387
388     ret = syscall_open_file("CharDevices/Video0", &handle, 0, 0);
389     ASSERT(ret == ERR_SUCCESS);
390
391     ret = syscall_device_ioctl(handle, IOCTL_VIDEO_MAP_FRAMEBUFFER, &params, sizeof(params), &address, sizeof(address));
392     ASSERT(ret == ERR_SUCCESS);
393
394     syscall_close_object(handle);
395 }