Go to most recent revision | Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
4453 | clevermous | 1 | ; RDC M2010/M2012 video driver. |
2 | |||
3 | ; Standard driver stuff |
||
4 | format PE DLL native at 0 |
||
5 | entry start |
||
6 | __DEBUG__ equ 1 |
||
7 | __DEBUG_LEVEL__ equ 1 |
||
8 | section '.flat' readable writable executable |
||
9 | include '../proc32.inc' |
||
10 | include '../struct.inc' |
||
11 | include '../macros.inc' |
||
12 | include '../fdo.inc' |
||
13 | |||
14 | ; Display-specific driver stuff |
||
15 | ; Kernel passes to init_cursor cursors with fixed size 32x32 |
||
16 | KERNEL_CURSOR_WIDTH = 32 |
||
17 | KERNEL_CURSOR_HEIGHT = 32 |
||
18 | |||
19 | ; Constants for IOCTL codes |
||
20 | SRV_GETVERSION = 0 |
||
21 | SRV_ENUM_MODES = 1 |
||
22 | SRV_SET_MODE = 2 |
||
23 | |||
24 | ; Constants for SRV_GETVERSION result |
||
25 | CURRENT_API = 0x0200 |
||
26 | COMPATIBLE_API = 0x0100 |
||
27 | API_VERSION = (COMPATIBLE_API shl 16) + CURRENT_API |
||
28 | |||
29 | ; Some structures |
||
30 | struct display_t |
||
31 | x dd ? |
||
32 | y dd ? |
||
33 | width dd ? |
||
34 | height dd ? |
||
35 | bpp dd ? |
||
36 | vrefresh dd ? |
||
37 | pitch dd ? |
||
38 | lfb dd ? |
||
39 | |||
40 | modes dd ? |
||
41 | ddev dd ? |
||
42 | connector dd ? |
||
43 | crtc dd ? |
||
44 | |||
45 | cr_list.next dd ? |
||
46 | cr_list.prev dd ? |
||
47 | |||
48 | cursor dd ? |
||
49 | |||
50 | init_cursor dd ? |
||
51 | select_cursor dd ? |
||
52 | show_cursor dd ? |
||
53 | move_cursor dd ? |
||
54 | restore_cursor dd ? |
||
55 | disable_mouse dd ? |
||
56 | mask_seqno dd ? |
||
57 | check_mouse dd ? |
||
58 | check_m_pixel dd ? |
||
59 | ends |
||
60 | |||
61 | struct APPOBJ ; common object header |
||
62 | magic dd ? ; |
||
63 | destroy dd ? ; internal destructor |
||
64 | fd dd ? ; next object in list |
||
65 | bk dd ? ; prev object in list |
||
66 | pid dd ? ; owner id |
||
67 | ends |
||
68 | |||
69 | struct CURSOR APPOBJ |
||
70 | base dd ? ;allocated memory |
||
71 | hot_x dd ? ;hotspot coords |
||
72 | hot_y dd ? |
||
73 | |||
74 | list_next dd ? ;next cursor in cursor list |
||
75 | list_prev dd ? ;prev cursor in cursor list |
||
76 | dev_obj dd ? ;device depended data |
||
77 | ends |
||
78 | |||
79 | ; Constants specific to our drivers |
||
80 | ; We are handling two videocards: M2010 and M2012 |
||
81 | PCI_VENDOR_RDC = 0x17F3 |
||
82 | ; I like this approach to select device IDs! |
||
83 | PCI_CHIP_M2010 = 0x2010 |
||
84 | PCI_CHIP_M2012 = 0x2012 |
||
85 | |||
86 | ; I/O ports for CRT registers |
||
87 | COLOR_CRTC_INDEX = 0x3D4 |
||
88 | COLOR_CRTC_DATA = 0x3D5 |
||
89 | ; The value that unlocks extended CRT registers: |
||
90 | ; index 0x80, value 0xA8 |
||
91 | ENABLE_EXTENDED_REGS = 0xA880 |
||
92 | |||
93 | ; Hardware cursors have size 64x64 |
||
94 | HW_CURSOR_WIDTH = 64 |
||
95 | HW_CURSOR_HEIGHT = 64 |
||
96 | ; Multiplication to powers of two can be replaced with shifts, |
||
97 | ; x*HW_CURSOR_WIDTH = x shl HW_CURSOR_WIDTH_SHIFT |
||
98 | HW_CURSOR_WIDTH_SHIFT = 6 |
||
99 | HW_CURSOR_HEIGHT_SHIFT = 6 |
||
100 | |||
101 | ; MMIO registers responsible for hardware cursor, see PRM |
||
102 | HWC_MMIO_CTRL = 0x580 |
||
103 | HWC_MMIO_OFFSET = 0x584 |
||
104 | HWC_MMIO_POSITION = 0x588 |
||
105 | HWC_MMIO_ADDRESS = 0x58C |
||
106 | |||
107 | ; Data for hardware cursors must be stored in videomemory, |
||
108 | ; so we need an allocator for objects inside videomemory. |
||
109 | ; Currently, we just reserve a fixed amount of memory for cursors |
||
110 | ; (cursor size is the same for all cursors) and keep a bitfield |
||
111 | ; that describes free blocks. 32 bits fit nicely in one dword. |
||
112 | MAX_CURSORS = 32 |
||
113 | |||
114 | ; === Entry points for external code === |
||
115 | |||
116 | ; Called once when driver is loading and once at shutdown. |
||
117 | ; When loading, must initialize itself, register itself in the system |
||
118 | ; and return eax = value obtained when registering. |
||
119 | ; Cdecl with two parameters. |
||
120 | proc start |
||
121 | push ebx esi ; save used registers to be stdcall |
||
122 | virtual at esp |
||
123 | rd 2 ; saved registers |
||
124 | dd ? ; return address |
||
125 | .reason dd ? ; DRV_ENTRY or DRV_EXIT |
||
126 | .cmdline dd ? ; normally NULL |
||
127 | end virtual |
||
128 | ; 1. Check the reason for the call, do nothing unless initializing. |
||
129 | cmp [.reason], DRV_ENTRY |
||
130 | jnz .nothing |
||
131 | ; 2. Find the PCI device for our videocard. |
||
132 | ; If not found, just return zero. |
||
133 | invoke GetPCIList |
||
134 | mov ebx, eax |
||
135 | .look_pcidev_loop: |
||
136 | mov ebx, [ebx+PCIDEV.fd] |
||
137 | cmp ebx, eax |
||
138 | jz .pcidev_notfound |
||
139 | cmp [ebx+PCIDEV.vendor_device_id], PCI_VENDOR_RDC + (PCI_CHIP_M2010 shl 16) |
||
140 | jz .pcidev_found |
||
141 | cmp [ebx+PCIDEV.vendor_device_id], PCI_VENDOR_RDC + (PCI_CHIP_M2012 shl 16) |
||
142 | jnz .look_pcidev_loop |
||
143 | .pcidev_found: |
||
144 | ; 3. Get addresses, sizes and pointers from the hardware. |
||
145 | ; 3a. Create mapping for MMIO. |
||
146 | invoke PciRead32, dword [ebx+PCIDEV.bus], dword [ebx+PCIDEV.devfn], 14h |
||
147 | and al, not 0xF |
||
148 | invoke MapIoMem, eax, 10000h, PG_NOCACHE+PG_SW |
||
149 | test eax, eax |
||
150 | jz .nothing |
||
151 | mov [mmio], eax |
||
152 | ; 3b. Get videomemory size. It is stored in 3 lower bits of 0xAA extended register |
||
153 | ; logarithmically started with 8Mb. |
||
154 | mov dx, COLOR_CRTC_INDEX |
||
155 | mov ax, ENABLE_EXTENDED_REGS |
||
156 | out dx, ax |
||
157 | mov al, 0xAA |
||
158 | out dx, al |
||
159 | inc edx |
||
160 | in al, dx |
||
161 | and eax, 7 |
||
162 | mov ecx, eax |
||
163 | mov eax, 8 shl 20 |
||
164 | shl eax, cl |
||
165 | mov [video_mem_size], eax |
||
166 | ; 3c. Reserve area for cursors in the last part of videomemory. |
||
167 | sub eax, MAX_CURSORS * HW_CURSOR_WIDTH * HW_CURSOR_HEIGHT * 4 |
||
168 | mov [cursors_base_offset], eax |
||
169 | ; 3d. Create mapping for part of videomemory that we have reserved for cursors. |
||
170 | ; Note: we can't just use system-wide mapping at 0xFE000000, it is too short. |
||
171 | invoke PciRead32, dword [ebx+PCIDEV.bus], dword [ebx+PCIDEV.devfn], 10h |
||
172 | and al, not 0xF |
||
173 | add eax, [cursors_base_offset] |
||
174 | invoke MapIoMem, eax, MAX_CURSORS * HW_CURSOR_WIDTH * HW_CURSOR_HEIGHT * 4, PG_SW |
||
175 | test eax, eax |
||
176 | jz .nothing |
||
177 | mov [cursors_base_va], eax |
||
178 | ; 4. Install cursor handlers. |
||
179 | ; 4a. Get pointer to the structure that keeps everything display-related. |
||
180 | invoke GetDisplay |
||
181 | mov ebx, eax |
||
182 | ; 4b. Make sure that no one tries to use partially-changed structure. |
||
183 | pushf |
||
184 | cli |
||
185 | ; 4c. Ask the previous handler to restore image hidden beyond cursor. |
||
186 | stdcall [ebx+display_t.restore_cursor], 0, 0 |
||
187 | ; 4d. Store pointers to our functions. |
||
188 | mov [ebx+display_t.init_cursor], init_cursor |
||
189 | mov [ebx+display_t.select_cursor], select_cursor |
||
190 | mov [ebx+display_t.show_cursor], 0 |
||
191 | mov [ebx+display_t.move_cursor], move_cursor |
||
192 | mov [ebx+display_t.restore_cursor], restore_cursor |
||
193 | mov [ebx+display_t.disable_mouse], disable_mouse |
||
194 | ; 4e. The kernel will pass all new cursors to our init_cursor, |
||
195 | ; but we must process already created cursors ourselves to be able to |
||
196 | ; select_cursor them when requested. Do it now: pass every cursor |
||
197 | ; in the list display_t.cr_list to init_cursor. |
||
198 | add ebx, display_t.cr_list.next |
||
199 | mov esi, ebx |
||
200 | .init_old_cursors_loop: |
||
201 | mov esi, [esi] |
||
202 | cmp esi, ebx |
||
203 | jz .init_old_cursors_done |
||
204 | lea eax, [esi-CURSOR.list_next] |
||
205 | push eax |
||
206 | call init_cursor |
||
207 | pop eax |
||
208 | jmp .init_old_cursors_loop |
||
209 | .init_old_cursors_done: |
||
210 | ; 4f. Setup the current cursor. |
||
211 | stdcall move_cursor, [ebx+display_t.cursor-display_t.cr_list.next], 0, 0 |
||
212 | stdcall select_cursor, [ebx+display_t.cursor-display_t.cr_list.next] |
||
213 | ; 4g. It is safe now to work with display structure; restore after 4b. |
||
214 | popf |
||
215 | ; 5. Say something happy to the (curious) user. |
||
216 | mov esi, success_msg |
||
217 | invoke SysMsgBoardStr |
||
218 | ; 6. Register ourselves as a service. |
||
219 | ; Note: not really needed currently as we don't do any useful in ioctl_handler, |
||
220 | ; but do it nevertheless for future expansion. |
||
221 | invoke RegService, rdc_name, ioctl_handler |
||
222 | .nothing: |
||
223 | pop esi ebx ; restore used registers to be stdcall |
||
224 | ret |
||
225 | .pcidev_notfound: |
||
226 | xor eax, eax |
||
227 | jmp .nothing |
||
228 | endp |
||
229 | |||
230 | ; Service procedure for the driver - handle all IOCTL requests for the driver. |
||
231 | ; Stdcall with one parameter. |
||
232 | proc ioctl_handler |
||
233 | virtual at esp |
||
234 | dd ? ; return address |
||
235 | .ioctl dd ? |
||
236 | end virtual |
||
237 | ; Not very useful currently - just return API_VERSION as a response to SRV_GETVERSION = 0. |
||
238 | mov edx, [.ioctl] |
||
239 | mov eax, [edx+IOCTL.io_code] |
||
240 | test eax, eax |
||
241 | jz .getversion |
||
242 | .error: |
||
243 | or eax, -1 ; fail everything unknown |
||
244 | retn 4 |
||
245 | .getversion: |
||
246 | cmp [edx+IOCTL.out_size], 4 |
||
247 | jnz .error |
||
248 | mov eax, [edx+IOCTL.output] |
||
249 | mov dword [eax], API_VERSION |
||
250 | xor eax, eax |
||
251 | retn 4 |
||
252 | endp |
||
253 | |||
254 | ; === Cursors === |
||
255 | |||
256 | ; This function is called when an application registers a new cursor. |
||
257 | ; Cdecl with one parameter, return value ignored. |
||
258 | proc init_cursor |
||
259 | push esi edi |
||
260 | virtual at esp |
||
261 | rd 2 ; saved registers |
||
262 | dd ? ; return address |
||
263 | .cursor dd ? |
||
264 | end virtual |
||
265 | ; We store one specific dword in CURSOR.dev_obj, |
||
266 | ; index in cursors area from 0 to NUM_CURSORS-1, or -1 for error. |
||
267 | ; 1. Prepare: store -1 to CURSOR.dev_obj and pointer to destroy function. |
||
268 | mov edx, [.cursor] |
||
269 | mov [edx+CURSOR.dev_obj], -1 |
||
270 | mov [edx+CURSOR.destroy], destroy_cursor |
||
271 | ; 2. Allocate videomemory. |
||
272 | bsr edi, [free_cursors] |
||
273 | jz .nocopy |
||
274 | btr [free_cursors], edi |
||
275 | ; 3. Store the allocated item to CURSOR.dev_obj. |
||
276 | mov [edx+CURSOR.dev_obj], edi |
||
277 | ; 4. Copy data from kernel-provided cursor to videomemory, |
||
278 | ; transforming KERNEL_CURSOR_WIDTH*KERNEL_CURSOR_HEIGHT*(4 bytes RGBA) to |
||
279 | ; HW_CURSOR_WIDTH*HW_CURSOR_HEIGHT*(4 bytes RGBA). |
||
280 | shl edi, HW_CURSOR_WIDTH_SHIFT + HW_CURSOR_HEIGHT_SHIFT + 2 |
||
281 | add edi, [cursors_base_va] |
||
282 | mov esi, [edx+CURSOR.base] |
||
283 | push KERNEL_CURSOR_HEIGHT |
||
284 | xor eax, eax |
||
285 | @@: |
||
286 | mov ecx, KERNEL_CURSOR_WIDTH |
||
287 | rep movsd |
||
288 | mov ecx, HW_CURSOR_WIDTH - KERNEL_CURSOR_WIDTH |
||
289 | rep stosd |
||
290 | dec dword [esp] |
||
291 | jnz @b |
||
292 | pop ecx |
||
293 | mov ecx, HW_CURSOR_WIDTH * (HW_CURSOR_HEIGHT - KERNEL_CURSOR_HEIGHT) |
||
294 | rep stosd |
||
295 | .nocopy: |
||
296 | ; 5. We don't need kernel-provided data anymore; free it now. |
||
297 | invoke KernelFree, [edx+CURSOR.base] |
||
298 | pop edi esi |
||
299 | ret |
||
300 | endp |
||
301 | |||
302 | ; This function is called when a thread that has created a cursor |
||
303 | ; is terminating and we need to free the cursor. |
||
304 | proc destroy_cursor |
||
305 | ; 1. Free allocated videomemory. |
||
306 | mov edx, [eax+CURSOR.dev_obj] |
||
307 | test edx, edx |
||
308 | js .nofree |
||
309 | bts [free_cursors], edx |
||
310 | .nofree: |
||
311 | ; 2. Remove the cursor from the overall list at display_t.cr_list. |
||
312 | pushf |
||
313 | cli |
||
314 | mov ecx, [eax+CURSOR.list_next] |
||
315 | mov edx, [eax+CURSOR.list_prev] |
||
316 | mov [ecx+4], edx |
||
317 | mov [edx], ecx |
||
318 | popf |
||
319 | ; 3. Free memory allocated for kernel object. |
||
320 | jmp [DestroyObject] |
||
321 | endp |
||
322 | |||
323 | ; This function is called when cursor shape needs to be changed, |
||
324 | ; either due to explicit request from application |
||
325 | ; or due to moving from one window to another. |
||
326 | ; Stdcall with one parameter, return value ignored. |
||
327 | proc select_cursor |
||
328 | virtual at esp |
||
329 | dd ? ; return address |
||
330 | .cursor dd ? |
||
331 | end virtual |
||
332 | mov eax, [.cursor] |
||
333 | mov eax, [eax+CURSOR.dev_obj] |
||
334 | cmp eax, -1 |
||
335 | jz .nothing |
||
336 | ; Setup base address of cursor, relative to videomemory, |
||
337 | ; and enable hardware cursor. |
||
338 | ; See PRM for details. |
||
339 | shl eax, HW_CURSOR_WIDTH_SHIFT + HW_CURSOR_HEIGHT_SHIFT + 2 |
||
340 | add eax, [cursors_base_offset] |
||
341 | shr eax, 3 |
||
342 | mov edx, [mmio] |
||
343 | mov [edx+HWC_MMIO_ADDRESS], eax |
||
344 | mov dword [edx+HWC_MMIO_CTRL], (1 shl 31) + (1 shl 1) + (1 shl 0) |
||
345 | .nothing: |
||
346 | ret 4 |
||
347 | endp |
||
348 | |||
349 | ; This function is called when cursor is moved to a new place. |
||
350 | ; Stdcall with three parameters, return value ignored. |
||
351 | proc move_cursor |
||
352 | virtual at esp |
||
353 | dd ? ; return address |
||
354 | .cursor dd ? |
||
355 | .x dd ? |
||
356 | .y dd ? |
||
357 | .xoffset dd ? |
||
358 | .yoffset dd ? |
||
359 | end virtual |
||
360 | ; If cursor x is smaller than hotspot x, |
||
361 | ; only part of cursor is shown at position x=0 |
||
362 | ; with x offset = (hotspot x) - (cursor x). |
||
363 | ; Otherwise, the entire cursor is shown (x offset = 0) |
||
364 | ; at position x = (cursor x) - (hotspot x). |
||
365 | ; Similar for y. Refer to PRM for details. |
||
366 | xor ecx, ecx |
||
367 | mov edx, [.cursor] |
||
368 | mov eax, [.x] |
||
369 | sub eax, [edx+CURSOR.hot_x] |
||
370 | jae @f |
||
371 | sub ecx, eax |
||
372 | xor eax, eax |
||
373 | @@: |
||
374 | mov [.x], eax |
||
375 | mov eax, [.y] |
||
376 | sub eax, [edx+CURSOR.hot_y] |
||
377 | jae @f |
||
378 | shl eax, 8 |
||
379 | sub ecx, eax |
||
380 | xor eax, eax |
||
381 | @@: |
||
382 | mov [.y], eax |
||
383 | mov edx, [mmio] |
||
384 | mov eax, [.y] |
||
385 | add eax, HW_CURSOR_HEIGHT - 1 ; no scaling |
||
386 | shl eax, 16 |
||
387 | add eax, ecx |
||
388 | mov [edx+HWC_MMIO_OFFSET], eax |
||
389 | mov eax, [.y] |
||
390 | shl eax, 16 |
||
391 | add eax, [.x] |
||
392 | mov [edx+HWC_MMIO_POSITION], eax |
||
393 | mov eax, [edx+HWC_MMIO_CTRL] |
||
394 | mov [edx+HWC_MMIO_CTRL], eax |
||
395 | ret 12 |
||
396 | endp |
||
397 | |||
398 | ; Stdcall with two parameters, return value ignored. |
||
399 | proc restore_cursor |
||
400 | ; No-operation for hardware cursors. |
||
401 | ret 8 |
||
402 | endp |
||
403 | |||
404 | ; No parameters, return value ignored. |
||
405 | proc disable_mouse |
||
406 | ; No-operation for hardware cursors. |
||
407 | ret |
||
408 | endp |
||
409 | |||
410 | ; === Data === |
||
411 | rdc_name db 'DISPLAY',0 |
||
412 | success_msg db 'RDC: using hardware cursors',13,10,0 |
||
413 | |||
414 | align 4 |
||
415 | ; Look at the comment before definition of NUM_CURSORS. |
||
416 | free_cursors dd 0xFFFFFFFF |
||
417 | |||
418 | data fixups |
||
419 | end data |
||
420 | |||
421 | include '../peimport.inc' |
||
422 | ;include_debug_strings |
||
423 | IncludeIGlobals |
||
424 | IncludeUGlobals |
||
425 | align 4 |
||
426 | mmio dd ? ; virtual address of MMIO for our device |
||
427 | video_mem_size dd ? ; total size of video memory, in bytes |
||
428 | cursors_base_offset dd ? ; base of cursor data, relative to video memory start |
||
429 | cursors_base_va dd ? ; mapped virtual address of cursor data |