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