xref: /haiku/src/add-ons/kernel/drivers/graphics/matrox/driver.c (revision 9eb55bc1d104b8fda80898f8b25c94d8000c8255)
1 /*
2 	Copyright 1999, Be Incorporated.   All Rights Reserved.
3 	This file may be used under the terms of the Be Sample Code License.
4 
5 	Other authors:
6 	Mark Watson;
7 	Rudolf Cornelissen 3/2002-11/2003.
8 */
9 
10 /* standard kernel driver stuff */
11 #include <KernelExport.h>
12 #include <PCI.h>
13 #include <OS.h>
14 #include <driver_settings.h>
15 #include <malloc.h>
16 #include <stdlib.h> // for strtoXX
17 
18 /* this is for the standardized portion of the driver API */
19 /* currently only one operation is defined: B_GET_ACCELERANT_SIGNATURE */
20 #include <graphic_driver.h>
21 
22 /* this is for sprintf() */
23 #include <stdio.h>
24 
25 /* this is for string compares */
26 #include <string.h>
27 
28 /* The private interface between the accelerant and the kernel driver. */
29 #include "DriverInterface.h"
30 #include "mga_macros.h"
31 
32 #define get_pci(o, s) (*pci_bus->read_pci_config)(pcii->bus, pcii->device, pcii->function, (o), (s))
33 #define set_pci(o, s, v) (*pci_bus->write_pci_config)(pcii->bus, pcii->device, pcii->function, (o), (s), (v))
34 
35 #define MAX_DEVICES	  8
36 
37 #define DEVICE_FORMAT "%04X_%04X_%02X%02X%02X" // apsed
38 
39 /* Tell the kernel what revision of the driver API we support */
40 int32	api_version = B_CUR_DRIVER_API_VERSION; // apsed, was 2, is 2 in R5
41 
42 /* these structures are private to the kernel driver */
43 typedef struct device_info device_info;
44 
45 typedef struct {
46 	timer		te;				/* timer entry for add_timer() */
47 	device_info	*di;			/* pointer to the owning device */
48 	bigtime_t	when_target;	/* when we're supposed to wake up */
49 } timer_info;
50 
51 struct device_info {
52 	uint32		is_open;			/* a count of how many times the devices has been opened */
53 	area_id		shared_area;		/* the area shared between the driver and all of the accelerants */
54 	shared_info	*si;				/* a pointer to the shared area, for convenience */
55 	vuint32		*regs;				/* kernel's pointer to memory mapped registers */
56 	pci_info	pcii;					/* a convenience copy of the pci info for this device */
57 	char		name[B_OS_NAME_LENGTH];	/* where we keep the name of the device for publishing and comparing */
58 };
59 
60 typedef struct {
61 	uint32		count;				/* number of devices actually found */
62 	benaphore	kernel;				/* for serializing opens/closes */
63 	char		*device_names[MAX_DEVICES+1];	/* device name pointer storage */
64 	device_info	di[MAX_DEVICES];	/* device specific stuff */
65 } DeviceData;
66 
67 /* prototypes for our private functions */
68 static status_t open_hook (const char* name, uint32 flags, void** cookie);
69 static status_t close_hook (void* dev);
70 static status_t free_hook (void* dev);
71 static status_t read_hook (void* dev, off_t pos, void* buf, size_t* len);
72 static status_t write_hook (void* dev, off_t pos, const void* buf, size_t* len);
73 static status_t control_hook (void* dev, uint32 msg, void *buf, size_t len);
74 static status_t map_device(device_info *di);
75 static void unmap_device(device_info *di);
76 static void probe_devices(void);
77 static int32 gx00_interrupt(void *data);
78 
79 static DeviceData		*pd;
80 static pci_module_info	*pci_bus;
81 static device_hooks graphics_device_hooks = {
82 	open_hook,
83 	close_hook,
84 	free_hook,
85 	control_hook,
86 	read_hook,
87 	write_hook,
88 	NULL,
89 	NULL,
90 	NULL,
91 	NULL
92 };
93 
94 #define VENDOR_ID			0x102b	/* Matrox graphics inc. */
95 
96 static uint16 gx00_device_list[] = {
97 	0x2527, /* G450AGP, G550AGP */
98 	0x0525, /* G400AGP */
99 	0x0520, /* G200PCI */
100 	0x0521, /* G200AGP */
101 	0x1000, /* G100PCI */
102 	0x1001, /* G100AGP */
103 	0x051B, /* MGA-2164 PCI Millennium 2 */
104 	0x051F, /* MGA-2164 AGP Millennium 2 */
105 	0x0519, /* MGA-2064 PCI Millennium 1 */
106 //fixme? only support Mystique once we are sure we actually support it...
107 //	0x051A, /* MGA-1064 PCI Mystique 170/220 */
108 	0
109 };
110 
111 static struct {
112 	uint16	vendor;
113 	uint16	*devices;
114 } SupportedDevices[] = {
115 	{VENDOR_ID, gx00_device_list},
116 	{0x0000, NULL}
117 };
118 
119 static settings current_settings = { // see comments in mga.settings
120 	// for driver
121 	DRIVER_PREFIX ".accelerant",
122 	false,      // dumprom
123 	// for accelerant
124 	0x00000000, // logmask
125 	0,          // memory
126 	false,      // usebios
127 	false,      // hardcursor
128 	false,		// greensync
129 };
130 
131 static void dumprom (void *rom, size_t size)
132 {
133 	int fd = open ("/boot/home/" DRIVER_PREFIX ".rom", O_WRONLY | O_CREAT, 0666);
134 	if (fd < 0) return;
135 	write (fd, rom, size);
136 	close (fd);
137 }
138 
139 /*return 1, is interrupt has occured*/
140 static int caused_vbi(vuint32 * regs)
141 {
142 	return (ACCR(STATUS)&0x20);
143 }
144 
145 /*clear the interrupt*/
146 static void clear_vbi(vuint32 * regs)
147 {
148 	ACCW(ICLEAR,0x20);
149 }
150 
151 static void enable_vbi(vuint32 * regs)
152 {
153 	ACCW(IEN,ACCR(IEN)|0x20);
154 }
155 
156 static void disable_vbi(vuint32 * regs)
157 {
158 	ACCW(IEN,(ACCR(IEN)&~0x20));
159 	ACCW(ICLEAR,0x20);
160 }
161 
162 
163 /*
164 	init_hardware() - Returns B_OK if one is
165 	found, otherwise returns B_ERROR so the driver will be unloaded.
166 */
167 status_t
168 init_hardware(void) {
169 	long		pci_index = 0;
170 	pci_info	pcii;
171 	bool		found_one = FALSE;
172 
173 	/* choke if we can't find the PCI bus */
174 	if (get_module(B_PCI_MODULE_NAME, (module_info **)&pci_bus) != B_OK)
175 		return B_ERROR;
176 
177 	/* while there are more pci devices */
178 	while ((*pci_bus->get_nth_pci_info)(pci_index, &pcii) == B_NO_ERROR) {
179 		int vendor = 0;
180 
181 		/* if we match a supported vendor */
182 		while (SupportedDevices[vendor].vendor) {
183 			if (SupportedDevices[vendor].vendor == pcii.vendor_id) {
184 				uint16 *devices = SupportedDevices[vendor].devices;
185 				/* while there are more supported devices */
186 				while (*devices) {
187 					/* if we match a supported device */
188 					if (*devices == pcii.device_id ) {
189 
190 						found_one = TRUE;
191 						goto done;
192 					}
193 					/* next supported device */
194 					devices++;
195 				}
196 			}
197 			vendor++;
198 		}
199 		/* next pci_info struct, please */
200 		pci_index++;
201 	}
202 
203 done:
204 	/* put away the module manager */
205 	put_module(B_PCI_MODULE_NAME);
206 	return (found_one ? B_OK : B_ERROR);
207 }
208 
209 status_t
210 init_driver(void) {
211 	void *settings_handle;
212 
213 	// get driver/accelerant settings, apsed
214 	settings_handle  = load_driver_settings (DRIVER_PREFIX ".settings");
215 	if (settings_handle != NULL) {
216 		const char *item;
217 		char       *end;
218 		uint32      value;
219 
220 		// for driver
221 		item = get_driver_parameter (settings_handle, "accelerant", "", "");
222 		if ((strlen (item) > 0) && (strlen (item) < sizeof (current_settings.accelerant) - 1)) {
223 			strcpy (current_settings.accelerant, item);
224 		}
225 		current_settings.dumprom = get_driver_boolean_parameter (settings_handle, "dumprom", false, false);
226 
227 		// for accelerant
228 		item = get_driver_parameter (settings_handle, "logmask", "0x00000000", "0x00000000");
229 		value = strtoul (item, &end, 0);
230 		if (*end == '\0') current_settings.logmask = value;
231 
232 		item = get_driver_parameter (settings_handle, "memory", "0", "0");
233 		value = strtoul (item, &end, 0);
234 		if (*end == '\0') current_settings.memory = value;
235 
236 		current_settings.hardcursor = get_driver_boolean_parameter (settings_handle, "hardcursor", false, false);
237 		current_settings.usebios = get_driver_boolean_parameter (settings_handle, "usebios", false, false);
238 		current_settings.greensync = get_driver_boolean_parameter (settings_handle, "greensync", false, false);
239 
240 		unload_driver_settings (settings_handle);
241 	}
242 
243 	/* get a handle for the pci bus */
244 	if (get_module(B_PCI_MODULE_NAME, (module_info **)&pci_bus) != B_OK)
245 		return B_ERROR;
246 
247 	/* driver private data */
248 	pd = (DeviceData *)calloc(1, sizeof(DeviceData));
249 	if (!pd) {
250 		put_module(B_PCI_MODULE_NAME);
251 		return B_ERROR;
252 	}
253 	/* initialize the benaphore */
254 	INIT_BEN(pd->kernel);
255 	/* find all of our supported devices */
256 	probe_devices();
257 	return B_OK;
258 }
259 
260 const char **
261 publish_devices(void) {
262 	/* return the list of supported devices */
263 	return (const char **)pd->device_names;
264 }
265 
266 device_hooks *
267 find_device(const char *name) {
268 	int index = 0;
269 	while (pd->device_names[index]) {
270 		if (strcmp(name, pd->device_names[index]) == 0)
271 			return &graphics_device_hooks;
272 		index++;
273 	}
274 	return NULL;
275 
276 }
277 
278 void uninit_driver(void) {
279 
280 	/* free the driver data */
281 	DELETE_BEN(pd->kernel);
282 	free(pd);
283 	pd = NULL;
284 
285 	/* put the pci module away */
286 	put_module(B_PCI_MODULE_NAME);
287 }
288 
289 static status_t map_device(device_info *di)
290 {
291 	char buffer[B_OS_NAME_LENGTH]; /*memory for device name*/
292 	shared_info *si = di->si;
293 	uint32	tmpUlong;
294 	pci_info *pcii = &(di->pcii);
295 	system_info sysinfo;
296 
297 	/*storage for the physical to virtual table (used for dma buffer)*/
298 //	physical_entry physical_memory[2];
299 //	#define G400_DMA_BUFFER_SIZE 1024*1024
300 
301 	/*variables for making copy of ROM*/
302 	char * rom_temp;
303 	area_id rom_area;
304 
305 	/* MIL1 has frame_buffer in [1], control_regs in [0], and nothing in [2], while
306 	 * MIL2 and later have frame_buffer in [0], control_regs in [1], pseudo_dma in [2] */
307 	int frame_buffer = 0;
308 	int registers = 1;
309 	int pseudo_dma = 2;
310 
311 	/* correct layout for MIL1 */
312 	//fixme: checkout Mystique 170 and 220...
313 	if (di->pcii.device_id == 0x0519)
314 	{
315 		frame_buffer = 1;
316 		registers = 0;
317 	}
318 
319 	/* enable memory mapped IO, disable VGA I/O - this is standard*/
320 	tmpUlong = get_pci(PCI_command, 4);
321 	tmpUlong |= 0x00000002;
322 	tmpUlong &= 0xfffffffe;
323 	set_pci(PCI_command, 4, tmpUlong);
324 
325  	/*work out which version of BeOS is running*/
326  	get_system_info(&sysinfo);
327  	if (sysinfo.kernel_build_date[0]=='J')/*FIXME - better ID version*/
328  	{
329  		si->use_clone_bugfix = 1;
330  	}
331  	else
332  	{
333  		si->use_clone_bugfix = 0;
334  	}
335 
336 	/* work out a name for the register mapping */
337 	sprintf(buffer, DEVICE_FORMAT " regs",
338 		di->pcii.vendor_id, di->pcii.device_id,
339 		di->pcii.bus, di->pcii.device, di->pcii.function);
340 
341 	/* get a virtual memory address for the registers*/
342 	si->regs_area = map_physical_memory(
343 		buffer,
344 		(void *) di->pcii.u.h0.base_registers[registers],
345 		di->pcii.u.h0.base_register_sizes[registers],
346 		B_ANY_KERNEL_ADDRESS,
347  		(si->use_clone_bugfix ? B_READ_AREA|B_WRITE_AREA : 0),
348 		(void **)&(di->regs));
349  	si->clone_bugfix_regs = (uint32 *) di->regs;
350 
351 	/* if mapping registers to vmem failed then pass on error */
352 	if (si->regs_area < 0) return si->regs_area;
353 
354 	/* work out a name for the ROM mapping*/
355 	sprintf(buffer, DEVICE_FORMAT " rom",
356 		di->pcii.vendor_id, di->pcii.device_id,
357 		di->pcii.bus, di->pcii.device, di->pcii.function);
358 
359 	/*place ROM over the fbspace (this is definately safe)*/
360 	tmpUlong = di->pcii.u.h0.base_registers[frame_buffer];
361 	tmpUlong |= 0x00000001;
362 	set_pci(PCI_rom_base, 4, tmpUlong);
363 
364 	rom_area = map_physical_memory(
365 		buffer,
366 		(void *)di->pcii.u.h0.base_registers[frame_buffer],
367 		32768,
368 		B_ANY_KERNEL_ADDRESS,
369 		B_READ_AREA,
370 		(void **)&(rom_temp)
371 	);
372 
373 	/* if mapping ROM to vmem failed then clean up and pass on error */
374 	if (rom_area < 0) {
375 		delete_area(si->regs_area);
376 		si->regs_area = -1;
377 		return rom_area;
378 	}
379 
380 	/* make a copy of ROM for future reference*/
381 	memcpy (si->rom_mirror, rom_temp, 32768);
382 	if (current_settings.dumprom) dumprom (rom_temp, 32768);
383 
384 	/*disable ROM and delete the area*/
385 	set_pci(PCI_rom_base,4,0);
386 	delete_area(rom_area);
387 
388 	/* (pseudo)DMA does not exist on MIL1 */
389 	//fixme: checkout Mystique 170 and 220...
390 	if (di->pcii.device_id != 0x0519)
391 	{
392 		/* work out a name for the pseudo dma mapping*/
393 		sprintf(buffer, DEVICE_FORMAT " pseudodma",
394 			di->pcii.vendor_id, di->pcii.device_id,
395 			di->pcii.bus, di->pcii.device, di->pcii.function);
396 
397 		/* map the pseudo dma into vmem (write-only)*/
398 		si->pseudo_dma_area = map_physical_memory(
399 			buffer,
400 			(void *) di->pcii.u.h0.base_registers[pseudo_dma],
401 			di->pcii.u.h0.base_register_sizes[pseudo_dma],
402 			B_ANY_KERNEL_ADDRESS,
403 			B_WRITE_AREA,
404 			&(si->pseudo_dma));
405 
406 		/* if there was an error, delete our other areas and pass on error*/
407 		if (si->pseudo_dma_area < 0) {
408 			delete_area(si->regs_area);
409 			si->regs_area = -1;
410 			return si->pseudo_dma_area;
411 		}
412 
413 		/* work out a name for the a dma buffer*/
414 //		sprintf(buffer, DEVICE_FORMAT " dmabuffer",
415 //			di->pcii.vendor_id, di->pcii.device_id,
416 //			di->pcii.bus, di->pcii.device, di->pcii.function);
417 
418 		/* create an area for the dma buffer*/
419 //		si->dma_buffer_area = create_area(
420 //			buffer,
421 //			&si->dma_buffer,
422 //			B_ANY_ADDRESS,
423 //			G400_DMA_BUFFER_SIZE,
424 //			B_FULL_LOCK|B_CONTIGUOUS,
425 //			B_READ_AREA|B_WRITE_AREA);
426 
427 		/* if there was an error, delete our other areas and pass on error*/
428 //		if (si->dma_buffer_area < 0) {
429 //			delete_area(si->pseudo_dma_area);
430 //			si->pseudo_dma_area = -1;
431 //			delete_area(si->regs_area);
432 //			si->regs_area = -1;
433 //			return si->dma_buffer_area;
434 //		}
435 
436 		/*find where it is in real memory*/
437 //		get_memory_map(si->dma_buffer,4,physical_memory,1);
438 //		si->dma_buffer_pci = physical_memory[0].address; /*addr from PCI space*/
439 	}
440 
441 	/* work out a name for the framebuffer mapping*/
442 	sprintf(buffer, DEVICE_FORMAT " framebuffer",
443 		di->pcii.vendor_id, di->pcii.device_id,
444 		di->pcii.bus, di->pcii.device, di->pcii.function);
445 
446 	/* map the framebuffer into vmem, using Write Combining*/
447 	si->fb_area = map_physical_memory(
448 		buffer,
449 		(void *) di->pcii.u.h0.base_registers[frame_buffer],
450 		di->pcii.u.h0.base_register_sizes[frame_buffer],
451 		B_ANY_KERNEL_BLOCK_ADDRESS | B_MTR_WC,
452 		B_READ_AREA + B_WRITE_AREA,
453 		&(si->framebuffer));
454 
455 	/*if failed with write combining try again without*/
456 	if (si->fb_area < 0) {
457 		si->fb_area = map_physical_memory(
458 			buffer,
459 			(void *) di->pcii.u.h0.base_registers[frame_buffer],
460 			di->pcii.u.h0.base_register_sizes[frame_buffer],
461 			B_ANY_KERNEL_BLOCK_ADDRESS,
462 			B_READ_AREA + B_WRITE_AREA,
463 			&(si->framebuffer));
464 	}
465 
466 	/* if there was an error, delete our other areas and pass on error*/
467 	if (si->fb_area < 0)
468 	{
469 		/* (pseudo)DMA does not exist on MIL1 */
470 		//fixme: checkout Mystique 170 and 220...
471 		if (di->pcii.device_id != 0x0519)
472 		{
473 			delete_area(si->dma_buffer_area);
474 			si->dma_buffer_area = -1;
475 			delete_area(si->pseudo_dma_area);
476 			si->pseudo_dma_area = -1;
477 		}
478 		delete_area(si->regs_area);
479 		si->regs_area = -1;
480 		return si->fb_area;
481 	}
482 	/* remember the DMA address of the frame buffer for BDirectWindow?? purposes */
483 	si->framebuffer_pci = (void *) di->pcii.u.h0.base_registers_pci[frame_buffer];
484 
485 	// remember settings for use here and in accelerant
486 	si->settings = current_settings;
487 
488 	/* in any case, return the result */
489 	return si->fb_area;
490 }
491 
492 static void unmap_device(device_info *di) {
493 	shared_info *si = di->si;
494 	uint32	tmpUlong;
495 	pci_info *pcii = &(di->pcii);
496 
497 	/* disable memory mapped IO */
498 	tmpUlong = get_pci(PCI_command, 4);
499 	tmpUlong &= 0xfffffffc;
500 	set_pci(PCI_command, 4, tmpUlong);
501 	/* delete the areas */
502 	if (si->regs_area >= 0) delete_area(si->regs_area);
503 	if (si->fb_area >= 0) delete_area(si->fb_area);
504 	si->regs_area = si->fb_area = -1;
505 
506 	/* (pseudo)DMA does not exist on MIL1 */
507 	//fixme: checkout Mystique 170 and 220...
508 	if (di->pcii.device_id != 0x0519)
509 	{
510 		delete_area(si->dma_buffer_area);
511 		si->dma_buffer_area = -1;
512 		delete_area(si->pseudo_dma_area);
513 		si->pseudo_dma_area = -1;
514 	}
515 
516 	si->framebuffer = NULL;
517 	di->regs = NULL;
518 }
519 
520 static void probe_devices(void) {
521 	uint32 pci_index = 0;
522 	uint32 count = 0;
523 	device_info *di = pd->di;
524 
525 	/* while there are more pci devices */
526 	while ((count < MAX_DEVICES) && ((*pci_bus->get_nth_pci_info)(pci_index, &(di->pcii)) == B_NO_ERROR)) {
527 		int vendor = 0;
528 
529 		/* if we match a supported vendor */
530 		while (SupportedDevices[vendor].vendor) {
531 			if (SupportedDevices[vendor].vendor == di->pcii.vendor_id) {
532 				uint16 *devices = SupportedDevices[vendor].devices;
533 				/* while there are more supported devices */
534 				while (*devices) {
535 					/* if we match a supported device */
536 					if (*devices == di->pcii.device_id ) {
537 						/* publish the device name */
538 						sprintf(di->name, "graphics/" DEVICE_FORMAT,
539 							di->pcii.vendor_id, di->pcii.device_id,
540 							di->pcii.bus, di->pcii.device, di->pcii.function);
541 
542 						/* remember the name */
543 						pd->device_names[count] = di->name;
544 						/* mark the driver as available for R/W open */
545 						di->is_open = 0;
546 						/* mark areas as not yet created */
547 						di->shared_area = -1;
548 						/* mark pointer to shared data as invalid */
549 						di->si = NULL;
550 						/* inc pointer to device info */
551 						di++;
552 						/* inc count */
553 						count++;
554 						/* break out of these while loops */
555 						goto next_device;
556 					}
557 					/* next supported device */
558 					devices++;
559 				}
560 			}
561 			vendor++;
562 		}
563 next_device:
564 		/* next pci_info struct, please */
565 		pci_index++;
566 	}
567 	/* propagate count */
568 	pd->count = count;
569 	/* terminate list of device names with a null pointer */
570 	pd->device_names[pd->count] = NULL;
571 }
572 
573 static uint32 thread_interrupt_work(int32 *flags, vuint32 *regs, shared_info *si) {
574 	uint32 handled = B_HANDLED_INTERRUPT;
575 	/* release the vblank semaphore */
576 	if (si->vblank >= 0) {
577 		int32 blocked;
578 		if ((get_sem_count(si->vblank, &blocked) == B_OK) && (blocked < 0)) {
579 			release_sem_etc(si->vblank, -blocked, B_DO_NOT_RESCHEDULE);
580 			handled = B_INVOKE_SCHEDULER;
581 		}
582 	}
583 	return handled;
584 }
585 
586 static int32
587 gx00_interrupt(void *data)
588 {
589 	int32 handled = B_UNHANDLED_INTERRUPT;
590 	device_info *di = (device_info *)data;
591 	shared_info *si = di->si;
592 	int32 *flags = &(si->flags);
593 	vuint32 *regs;
594 
595 	/* is someone already handling an interrupt for this device? */
596 	if (atomic_or(flags, SKD_HANDLER_INSTALLED) & SKD_HANDLER_INSTALLED) {
597 		goto exit0;
598 	}
599 	/* get regs */
600 	regs = di->regs;
601 
602 	/* was it a VBI? */
603 	if (caused_vbi(regs)) {
604 		/*clear the interrupt*/
605 		clear_vbi(regs);
606 		/*release the semaphore*/
607 		handled = thread_interrupt_work(flags, regs, si);
608 	}
609 
610 	/* note that we're not in the handler any more */
611 	atomic_and(flags, ~SKD_HANDLER_INSTALLED);
612 
613 exit0:
614 	return handled;
615 }
616 
617 static status_t open_hook (const char* name, uint32 flags, void** cookie) {
618 	int32 index = 0;
619 	device_info *di;
620 	shared_info *si;
621 	thread_id	thid;
622 	thread_info	thinfo;
623 	status_t	result = B_OK;
624 	vuint32		*regs;
625 	char shared_name[B_OS_NAME_LENGTH];
626 
627 	/* find the device name in the list of devices */
628 	/* we're never passed a name we didn't publish */
629 	while (pd->device_names[index] && (strcmp(name, pd->device_names[index]) != 0)) index++;
630 
631 	/* for convienience */
632 	di = &(pd->di[index]);
633 
634 	/* make sure no one else has write access to the common data */
635 	AQUIRE_BEN(pd->kernel);
636 
637 	/* if it's already open for writing */
638 	if (di->is_open) {
639 		/* mark it open another time */
640 		goto mark_as_open;
641 	}
642 	/* create the shared area */
643 	sprintf(shared_name, DEVICE_FORMAT " shared",
644 		di->pcii.vendor_id, di->pcii.device_id,
645 		di->pcii.bus, di->pcii.device, di->pcii.function);
646 	/* create this area with NO user-space read or write permissions, to prevent accidental dammage */
647 	di->shared_area = create_area(shared_name, (void **)&(di->si), B_ANY_KERNEL_ADDRESS, ((sizeof(shared_info) + (B_PAGE_SIZE - 1)) & ~(B_PAGE_SIZE - 1)), B_FULL_LOCK, 0);
648 	if (di->shared_area < 0) {
649 		/* return the error */
650 		result = di->shared_area;
651 		goto done;
652 	}
653 
654 	/* save a few dereferences */
655 	si = di->si;
656 
657 	/* save the vendor and device IDs */
658 	si->vendor_id = di->pcii.vendor_id;
659 	si->device_id = di->pcii.device_id;
660 	si->revision = di->pcii.revision;
661 
662 	/* map the device */
663 	result = map_device(di);
664 	if (result < 0) goto free_shared;
665 	result = B_OK;
666 
667 	/* create a semaphore for vertical blank management */
668 	si->vblank = create_sem(0, di->name);
669 	if (si->vblank < 0) {
670 		result = si->vblank;
671 		goto unmap;
672 	}
673 
674 	/* change the owner of the semaphores to the opener's team */
675 	/* this is required because apps can't aquire kernel semaphores */
676 	thid = find_thread(NULL);
677 	get_thread_info(thid, &thinfo);
678 	set_sem_owner(si->vblank, thinfo.team);
679 
680 	/* assign local regs pointer for SAMPLExx() macros */
681 	regs = di->regs;
682 
683 	/* disable and clear any pending interrupts */
684 	disable_vbi(regs);
685 
686 	/* If there is a valid interrupt line assigned then set up interrupts */
687 	if ((di->pcii.u.h0.interrupt_pin == 0x00) ||
688 	    (di->pcii.u.h0.interrupt_line == 0xff) || /* no IRQ assigned */
689 	    (di->pcii.u.h0.interrupt_line <= 0x02))   /* system IRQ assigned */
690 	{
691 		/* we are aborting! */
692 		/* Note: the R4 graphics driver kit lacks this statement!! */
693 		result = B_ERROR;
694 		/* interrupt does not exist so exit without installing our handler */
695 		goto delete_the_sem;
696 	}
697 	else
698 	{
699 		/* otherwise install our interrupt handler */
700 		result = install_io_interrupt_handler(di->pcii.u.h0.interrupt_line, gx00_interrupt, (void *)di, 0);
701 		/* bail if we couldn't install the handler */
702 		if (result != B_OK) goto delete_the_sem;
703 	}
704 
705 mark_as_open:
706 	/* mark the device open */
707 	di->is_open++;
708 
709 	/* send the cookie to the opener */
710 	*cookie = di;
711 
712 	goto done;
713 
714 
715 delete_the_sem:
716 	delete_sem(si->vblank);
717 
718 unmap:
719 	unmap_device(di);
720 
721 free_shared:
722 	/* clean up our shared area */
723 	delete_area(di->shared_area);
724 	di->shared_area = -1;
725 	di->si = NULL;
726 
727 done:
728 	/* end of critical section */
729 	RELEASE_BEN(pd->kernel);
730 
731 	/* all done, return the status */
732 	return result;
733 }
734 
735 /* ----------
736 	read_hook - does nothing, gracefully
737 ----- */
738 static status_t
739 read_hook (void* dev, off_t pos, void* buf, size_t* len)
740 {
741 	*len = 0;
742 	return B_NOT_ALLOWED;
743 }
744 
745 
746 /* ----------
747 	write_hook - does nothing, gracefully
748 ----- */
749 static status_t
750 write_hook (void* dev, off_t pos, const void* buf, size_t* len)
751 {
752 	*len = 0;
753 	return B_NOT_ALLOWED;
754 }
755 
756 /* ----------
757 	close_hook - does nothing, gracefully
758 ----- */
759 static status_t
760 close_hook (void* dev)
761 {
762 	/* we don't do anything on close: there might be dup'd fd */
763 	return B_NO_ERROR;
764 }
765 
766 /* -----------
767 	free_hook - close down the device
768 ----------- */
769 static status_t
770 free_hook (void* dev) {
771 	device_info *di = (device_info *)dev;
772 	shared_info	*si = di->si;
773 	vuint32 *regs = di->regs;
774 
775 	/* lock the driver */
776 	AQUIRE_BEN(pd->kernel);
777 
778 	/* if opened multiple times, decrement the open count and exit */
779 	if (di->is_open > 1)
780 		goto unlock_and_exit;
781 
782 	/* disable and clear any pending interrupts */
783 	disable_vbi(regs);
784 
785 	/* remove interrupt handler */
786 	remove_io_interrupt_handler(di->pcii.u.h0.interrupt_line, gx00_interrupt, di);
787 
788 	/* delete the semaphores, ignoring any errors ('cause the owning team may have died on us) */
789 	delete_sem(si->vblank);
790 	si->vblank = -1;
791 
792 	/* free regs and framebuffer areas */
793 	unmap_device(di);
794 
795 	/* clean up our shared area */
796 	delete_area(di->shared_area);
797 	di->shared_area = -1;
798 	di->si = NULL;
799 
800 unlock_and_exit:
801 	/* mark the device available */
802 	di->is_open--;
803 	/* unlock the driver */
804 	RELEASE_BEN(pd->kernel);
805 	/* all done */
806 	return B_OK;
807 }
808 
809 /* -----------
810 	control_hook - where the real work is done
811 ----------- */
812 static status_t
813 control_hook (void* dev, uint32 msg, void *buf, size_t len) {
814 	device_info *di = (device_info *)dev;
815 	status_t result = B_DEV_INVALID_IOCTL;
816 
817 	switch (msg) {
818 		/* the only PUBLIC ioctl */
819 		case B_GET_ACCELERANT_SIGNATURE: {
820 			char *sig = (char *)buf;
821 			strcpy(sig, current_settings.accelerant);
822 			result = B_OK;
823 		} break;
824 
825 		/* PRIVATE ioctl from here on */
826 		case GX00_GET_PRIVATE_DATA: {
827 			gx00_get_private_data *gpd = (gx00_get_private_data *)buf;
828 			if (gpd->magic == GX00_PRIVATE_DATA_MAGIC) {
829 				gpd->shared_info_area = di->shared_area;
830 				result = B_OK;
831 			}
832 		} break;
833 		case GX00_GET_PCI: {
834 			gx00_get_set_pci *gsp = (gx00_get_set_pci *)buf;
835 			if (gsp->magic == GX00_PRIVATE_DATA_MAGIC) {
836 				pci_info *pcii = &(di->pcii);
837 				gsp->value = get_pci(gsp->offset, gsp->size);
838 				result = B_OK;
839 			}
840 		} break;
841 		case GX00_SET_PCI: {
842 			gx00_get_set_pci *gsp = (gx00_get_set_pci *)buf;
843 			if (gsp->magic == GX00_PRIVATE_DATA_MAGIC) {
844 				pci_info *pcii = &(di->pcii);
845 				set_pci(gsp->offset, gsp->size, gsp->value);
846 				result = B_OK;
847 			}
848 		} break;
849 		case GX00_DEVICE_NAME: { // apsed
850 			gx00_device_name *dn = (gx00_device_name *)buf;
851 			if (dn->magic == GX00_PRIVATE_DATA_MAGIC) {
852 				strcpy(dn->name, di->name);
853 				result = B_OK;
854 			}
855 		} break;
856 		case GX00_RUN_INTERRUPTS: {
857 			gx00_set_bool_state *ri = (gx00_set_bool_state *)buf;
858 			if (ri->magic == GX00_PRIVATE_DATA_MAGIC) {
859 				vuint32 *regs = di->regs;
860 				if (ri->do_it) {
861 					enable_vbi(regs);
862 				} else {
863 					disable_vbi(regs);
864 				}
865 				result = B_OK;
866 			}
867 		} break;
868 	}
869 	return result;
870 }
871 
872