xref: /haiku/src/add-ons/kernel/drivers/graphics/s3/driver.cpp (revision 579f1dbca962a2a03df54f69fdc6e9423f91f20e)
1 /*
2 	Copyright 2007-2008 Haiku, Inc.  All rights reserved.
3 	Distributed under the terms of the MIT license.
4 
5 	Authors:
6 	Gerald Zajac 2007-2008
7 */
8 
9 #include <KernelExport.h>
10 #include <PCI.h>
11 #ifdef __HAIKU__
12 #include <drivers/bios.h>
13 #endif	// __HAIKU__
14 #include <malloc.h>
15 #include <stdio.h>
16 #include <string.h>
17 #include <graphic_driver.h>
18 
19 #include "DriverInterface.h"
20 
21 
22 #undef TRACE
23 
24 #ifdef ENABLE_DEBUG_TRACE
25 #	define TRACE(x...) dprintf("S3: " x)
26 #else
27 #	define TRACE(x...) ;
28 #endif
29 
30 
31 
32 #define SKD_HANDLER_INSTALLED 0x80000000
33 #define MAX_DEVICES		4
34 #define DEVICE_FORMAT	"%04X_%04X_%02X%02X%02X"
35 
36 int32 api_version = B_CUR_DRIVER_API_VERSION;	// revision of driver API we support
37 
38 #define VENDOR_ID 0x5333	// S3 vendor ID
39 
40 
41 struct ChipInfo {
42 	uint16		chipID;			// PCI device id of the chip
43 	uint16		chipType;		// assigned chip type identifier
44 	const char*	chipName;		// user recognizable name for chip (must be < 32
45 								// chars)
46 };
47 
48 // This table maps a PCI device ID to a chip type identifier and the chip name.
49 // Note that the Trio64 and Trio64V+ chips have the same ID, but have a different
50 // revision number.  After the revision number is examined, the Trio64V+ will
51 // have a different chip type code and name assigned.
52 
53 static const ChipInfo chipTable[] = {
54 	{ 0x8811, S3_TRIO64,		"Trio64"			},		// see comment above
55 	{ 0x8814, S3_TRIO64_UVP,	"Trio64 UV+"		},
56 	{ 0x8901, S3_TRIO64_V2,		"Trio64 V2/DX/GX"	},
57 
58 	{ 0x5631, S3_VIRGE,			"Virge"				},
59 	{ 0x883D, S3_VIRGE_VX,		"Virge VX"			},
60 	{ 0x8A01, S3_VIRGE_DXGX,	"Virge DX/GX"		},
61 	{ 0x8A10, S3_VIRGE_GX2,		"Virge GX2"			},
62 	{ 0x8C01, S3_VIRGE_MX,		"Virge MX"			},
63 	{ 0x8C03, S3_VIRGE_MXP,		"Virge MX+"			},
64 	{ 0x8904, S3_TRIO_3D,		"Trio 3D"			},
65 	{ 0x8A13, S3_TRIO_3D_2X,	"Trio 3D/2X"		},
66 
67 	{ 0x8a20, S3_SAVAGE_3D,		"Savage3D"				},
68 	{ 0x8a21, S3_SAVAGE_3D,		"Savage3D-MV" 			},
69 	{ 0x8a22, S3_SAVAGE4,		"Savage4"				},
70 	{ 0x8a25, S3_PROSAVAGE,		"ProSavage PM133"		},
71 	{ 0x8a26, S3_PROSAVAGE,		"ProSavage KM133"		},
72 	{ 0x8c10, S3_SAVAGE_MX,		"Savage/MX-MV"			},
73 	{ 0x8c11, S3_SAVAGE_MX,		"Savage/MX"				},
74 	{ 0x8c12, S3_SAVAGE_MX,		"Savage/IX-MV"			},
75 	{ 0x8c13, S3_SAVAGE_MX,		"Savage/IX"				},
76 	{ 0x8c22, S3_SUPERSAVAGE,	"SuperSavage/MX 128"	},
77 	{ 0x8c24, S3_SUPERSAVAGE,	"SuperSavage/MX 64"		},
78 	{ 0x8c26, S3_SUPERSAVAGE,	"SuperSavage/MX 64C"	},
79 	{ 0x8c2a, S3_SUPERSAVAGE,	"SuperSavage/IX 128SDR"	},
80 	{ 0x8c2b, S3_SUPERSAVAGE,	"SuperSavage/IX 128DDR"	},
81 	{ 0x8c2c, S3_SUPERSAVAGE,	"SuperSavage/IX 64SDR"	},
82 	{ 0x8c2d, S3_SUPERSAVAGE,	"SuperSavage/IX 64DDR"	},
83 	{ 0x8c2e, S3_SUPERSAVAGE,	"SuperSavage/IXC 64SDR"	},
84 	{ 0x8c2f, S3_SUPERSAVAGE,	"SuperSavage/IXC 64DDR"	},
85 	{ 0x8d01, S3_TWISTER,		"Twister PN133"			},
86 	{ 0x8d02, S3_TWISTER,		"Twister KN133"			},
87 	{ 0x8d03, S3_PROSAVAGE_DDR,	"ProSavage DDR"			},
88 	{ 0x8d04, S3_PROSAVAGE_DDR,	"ProSavage DDR-K"		},
89 	{ 0x9102, S3_SAVAGE2000,	"Savage2000"			},
90 	{ 0,	  0,				NULL				}
91 };
92 
93 
94 struct DeviceInfo {
95 	uint32			openCount;		// count of how many times device has been opened
96 	int32			flags;
97 	area_id 		sharedArea;		// area shared between driver and all accelerants
98 	SharedInfo* 	sharedInfo;				// pointer to shared info area memory
99 	vuint8*	 		regs;			// pointer to memory mapped registers
100 	const ChipInfo*	pChipInfo;		// info about the selected chip
101 	pci_info		pciInfo;		// copy of pci info for this device
102 	char			name[B_OS_NAME_LENGTH]; // name of device
103 };
104 
105 
106 static Benaphore		gLock;
107 static DeviceInfo		gDeviceInfo[MAX_DEVICES];
108 static char*			gDeviceNames[MAX_DEVICES + 1];
109 static pci_module_info*	gPCI;
110 
111 
112 // Prototypes for device hook functions.
113 
114 static status_t device_open(const char* name, uint32 flags, void** cookie);
115 static status_t device_close(void* dev);
116 static status_t device_free(void* dev);
117 static status_t device_read(void* dev, off_t pos, void* buf, size_t* len);
118 static status_t device_write(void* dev, off_t pos, const void* buf, size_t* len);
119 static status_t device_ioctl(void* dev, uint32 msg, void* buf, size_t len);
120 
121 static device_hooks gDeviceHooks =
122 {
123 	device_open,
124 	device_close,
125 	device_free,
126 	device_ioctl,
127 	device_read,
128 	device_write,
129 	NULL,
130 	NULL,
131 	NULL,
132 	NULL
133 };
134 
135 
136 
137 static inline uint32
138 GetPCI(pci_info& info, uint8 offset, uint8 size)
139 {
140 	return gPCI->read_pci_config(info.bus, info.device, info.function, offset, size);
141 }
142 
143 
144 static inline void
145 SetPCI(pci_info& info, uint8 offset, uint8 size, uint32 value)
146 {
147 	gPCI->write_pci_config(info.bus, info.device, info.function, offset, size, value);
148 }
149 
150 
151 // Functions for dealing with Vertical Blanking Interrupts.  Currently, I do
152 // not know the commands to handle these operations;  thus, these functions
153 // currently do nothing.
154 
155 static bool
156 InterruptIsVBI()
157 {
158 	// return true only if a vertical blanking interrupt has occured
159 	return false;
160 }
161 
162 
163 static void
164 ClearVBI()
165 {
166 }
167 
168 static void
169 EnableVBI()
170 {
171 }
172 
173 static void
174 DisableVBI()
175 {
176 }
177 
178 
179 static status_t
180 MapDevice(DeviceInfo& di)
181 {
182 	char areaName[B_OS_NAME_LENGTH];
183 	SharedInfo& si = *(di.sharedInfo);
184 	pci_info& pciInfo = di.pciInfo;
185 
186 	TRACE("enter MapDevice()\n");
187 
188 	// Enable memory mapped IO and bus master.
189 
190 	SetPCI(pciInfo, PCI_command, 2, GetPCI(pciInfo, PCI_command, 2)
191 		| PCI_command_io | PCI_command_memory | PCI_command_master);
192 
193 	const uint32 SavageMmioRegBaseOld	= 0x1000000;	// 16 MB
194 	const uint32 SavageMmioRegBaseNew	= 0x0000000;
195 	const uint32 SavageMmioRegSize		= 0x0080000;	// 512 KB reg area size
196 
197 	const uint32 VirgeMmioRegBase		= 0x1000000;	// 16 MB
198 	const uint32 VirgeMmioRegSize		= 0x10000;		// 64 KB reg area size
199 
200 	uint32 videoRamAddr = 0;
201 	uint32 videoRamSize = 0;
202 	uint32 regsBase = 0;
203 	uint32 regAreaSize = 0;
204 
205 	// Since we do not know at this point the actual size of the video
206 	// memory, set it to the largest value that the respective chipset
207 	// family can have.
208 
209 	if (S3_SAVAGE_FAMILY(di.pChipInfo->chipType)) {
210 		if (S3_SAVAGE_3D_SERIES(di.pChipInfo->chipType)) {
211 			// Savage 3D & Savage MX chips.
212 
213 			regsBase = pciInfo.u.h0.base_registers[0] + SavageMmioRegBaseOld;
214 			regAreaSize = SavageMmioRegSize;
215 
216 	 		videoRamAddr = pciInfo.u.h0.base_registers[0];
217 			videoRamSize = 16 * 1024 * 1024;	// 16 MB is max for 3D series
218 			si.videoMemPCI = (void *)(pciInfo.u.h0.base_registers_pci[0]);
219 		} else {
220 			// All other Savage chips.
221 
222 			regsBase = pciInfo.u.h0.base_registers[0] + SavageMmioRegBaseNew;
223 			regAreaSize = SavageMmioRegSize;
224 
225 			videoRamAddr = pciInfo.u.h0.base_registers[1];
226 			videoRamSize = pciInfo.u.h0.base_register_sizes[1];
227 			si.videoMemPCI = (void *)(pciInfo.u.h0.base_registers_pci[1]);
228 		}
229 	} else {
230 		// Trio/Virge chips.
231 
232 		regsBase = pciInfo.u.h0.base_registers[0] + VirgeMmioRegBase;
233 		regAreaSize = VirgeMmioRegSize;
234 
235  		videoRamAddr = pciInfo.u.h0.base_registers[0];
236 		videoRamSize = 8 * 1024 * 1024;	// 8 MB is max for Trio/Virge chips
237 		si.videoMemPCI = (void *)(pciInfo.u.h0.base_registers_pci[0]);
238 	}
239 
240 	// Map the MMIO register area.
241 
242 	sprintf(areaName, DEVICE_FORMAT " regs",
243 		pciInfo.vendor_id, pciInfo.device_id,
244 		pciInfo.bus, pciInfo.device, pciInfo.function);
245 
246 	si.regsArea = map_physical_memory(areaName, regsBase, regAreaSize,
247 		B_ANY_KERNEL_ADDRESS,
248 		0,		// neither read nor write, to hide it from user space apps
249 		(void**)(&(di.regs)));
250 
251 	if (si.regsArea < 0)
252 		return si.regsArea;	// return error code
253 
254 	// Map the video memory.
255 
256 	sprintf(areaName, DEVICE_FORMAT " framebuffer",
257 		pciInfo.vendor_id, pciInfo.device_id,
258 		pciInfo.bus, pciInfo.device, pciInfo.function);
259 
260 	si.videoMemArea = map_physical_memory(
261 		areaName,
262 		videoRamAddr,
263 		videoRamSize,
264 		B_ANY_KERNEL_BLOCK_ADDRESS | B_MTR_WC,
265 		B_READ_AREA + B_WRITE_AREA,
266 		&(si.videoMemAddr));
267 
268 	if (si.videoMemArea < 0) {
269 		// Try to map this time without write combining.
270 		si.videoMemArea = map_physical_memory(
271 			areaName,
272 			videoRamAddr,
273 			videoRamSize,
274 			B_ANY_KERNEL_BLOCK_ADDRESS,
275 			B_READ_AREA + B_WRITE_AREA,
276 			&(si.videoMemAddr));
277 	}
278 
279 	TRACE("Video memory, area: %ld,  addr: 0x%lX\n", si.videoMemArea, (uint32)(si.videoMemAddr));
280 
281 	// If there was an error, delete other areas.
282 	if (si.videoMemArea < 0) {
283 		delete_area(si.regsArea);
284 		si.regsArea = -1;
285 	}
286 
287 	TRACE("leave MapDevice(); result: %ld\n", si.videoMemArea);
288 	return si.videoMemArea;
289 }
290 
291 
292 static void
293 UnmapDevice(DeviceInfo& di)
294 {
295 	SharedInfo& si = *(di.sharedInfo);
296 
297 	TRACE("enter UnmapDevice()\n");
298 
299 	if (si.regsArea >= 0)
300 		delete_area(si.regsArea);
301 	if (si.videoMemArea >= 0)
302 		delete_area(si.videoMemArea);
303 
304 	si.regsArea = si.videoMemArea = -1;
305 	si.videoMemAddr = NULL;
306 	di.regs = NULL;
307 
308 	TRACE("exit UnmapDevice()\n");
309 }
310 
311 
312 static int32
313 InterruptHandler(void* data)
314 {
315 	int32 handled = B_UNHANDLED_INTERRUPT;
316 	DeviceInfo& di = *((DeviceInfo*)data);
317 	int32* flags = &(di.flags);
318 
319 	// Is someone already handling an interrupt for this device?
320 	if (atomic_or(flags, SKD_HANDLER_INSTALLED) & SKD_HANDLER_INSTALLED)
321 		return B_UNHANDLED_INTERRUPT;
322 
323 	if (InterruptIsVBI()) {	// was interrupt a VBI?
324 		ClearVBI();			// clear interrupt
325 
326 		handled = B_HANDLED_INTERRUPT;
327 
328 		// Release vertical blanking semaphore.
329 		sem_id& sem = di.sharedInfo->vertBlankSem;
330 
331 		if (sem >= 0) {
332 			int32 blocked;
333 			if ((get_sem_count(sem, &blocked) == B_OK) && (blocked < 0)) {
334 				release_sem_etc(sem, -blocked, B_DO_NOT_RESCHEDULE);
335 				handled = B_INVOKE_SCHEDULER;
336 			}
337 		}
338 	}
339 
340 	atomic_and(flags, ~SKD_HANDLER_INSTALLED);	// note we're not in handler anymore
341 
342 	return handled;
343 }
344 
345 
346 static void
347 InitInterruptHandler(DeviceInfo& di)
348 {
349 	SharedInfo& si = *(di.sharedInfo);
350 
351 	TRACE("enter InitInterruptHandler()\n");
352 
353 	DisableVBI();					// disable & clear any pending interrupts
354 	si.bInterruptAssigned = false;	// indicate interrupt not assigned yet
355 
356 	// Create a semaphore for vertical blank management.
357 	si.vertBlankSem = create_sem(0, di.name);
358 	if (si.vertBlankSem < 0)
359 		return;
360 
361 	// Change the owner of the semaphores to the calling team (usually the
362 	// app_server).  This is required because apps can't aquire kernel
363 	// semaphores.
364 
365 	thread_id threadID = find_thread(NULL);
366 	thread_info threadInfo;
367 	status_t status = get_thread_info(threadID, &threadInfo);
368 	if (status == B_OK)
369 		status = set_sem_owner(si.vertBlankSem, threadInfo.team);
370 
371 	// If there is a valid interrupt assigned, set up interrupts.
372 
373 	if (status == B_OK && di.pciInfo.u.h0.interrupt_pin != 0x00
374 		&& di.pciInfo.u.h0.interrupt_line != 0xff) {
375 		// We have a interrupt line to use.
376 
377 		status = install_io_interrupt_handler(di.pciInfo.u.h0.interrupt_line,
378 			InterruptHandler, (void*)(&di), 0);
379 
380 		if (status == B_OK)
381 			si.bInterruptAssigned = true;	// we can use interrupt related functions
382 	}
383 
384 	if (status != B_OK) {
385 		// Interrupt does not exist; thus delete semaphore as it won't be used.
386 		delete_sem(si.vertBlankSem);
387 		si.vertBlankSem = -1;
388 	}
389 }
390 
391 
392 static status_t
393 InitDevice(DeviceInfo& di)
394 {
395 	// Perform initialization and mapping of the device, and return B_OK if
396 	// sucessful;  else, return error code.
397 
398 	// Create the area for shared info with NO user-space read or write
399 	// permissions, to prevent accidental damage.
400 
401 	TRACE("enter InitDevice()\n");
402 
403 	pci_info& pciInfo = di.pciInfo;
404 	char sharedName[B_OS_NAME_LENGTH];
405 
406 	sprintf(sharedName, DEVICE_FORMAT " shared",
407 		pciInfo.vendor_id, pciInfo.device_id,
408 		pciInfo.bus, pciInfo.device, pciInfo.function);
409 
410 	di.sharedArea = create_area(sharedName, (void**) &(di.sharedInfo),
411 		B_ANY_KERNEL_ADDRESS,
412 		((sizeof(SharedInfo) + (B_PAGE_SIZE - 1)) & ~(B_PAGE_SIZE - 1)),
413 		B_FULL_LOCK, 0);
414 	if (di.sharedArea < 0)
415 		return di.sharedArea;	// return error code
416 
417 	SharedInfo& si = *(di.sharedInfo);
418 
419 	si.vendorID = pciInfo.vendor_id;
420 	si.deviceID = pciInfo.device_id;
421 	si.revision = pciInfo.revision;
422 	si.chipType = di.pChipInfo->chipType;
423 	strcpy(si.chipName, di.pChipInfo->chipName);
424 
425 	// Trio64 and Trio64V+ chips have the same ID but different revision numbers.
426 	// Since the Trio64V+ supports MMIO, better performance can be obtained
427 	// from it if it is distinguished from the Trio64.
428 
429 	if (si.chipType == S3_TRIO64 && si.revision & 0x40) {
430 		si.chipType = S3_TRIO64_VP;
431 		strcpy(si.chipName, "Trio64 V+");
432 	}
433 
434 	status_t status = MapDevice(di);
435 	if (status < 0) {
436 		delete_area(di.sharedArea);
437 		di.sharedArea = -1;
438 		di.sharedInfo = NULL;
439 		return status;		// return error code
440 	}
441 
442 	InitInterruptHandler(di);
443 
444 	TRACE("Interrupt assigned:  %s\n", si.bInterruptAssigned ? "yes" : "no");
445 	return B_OK;
446 }
447 
448 
449 static const ChipInfo*
450 GetNextSupportedDevice(uint32& pciIndex, pci_info& pciInfo)
451 {
452 	// Search the PCI devices for a device that is supported by this driver.
453 	// The search starts at the device specified by argument pciIndex, and
454 	// continues until a supported device is found or there are no more devices
455 	// to examine.  Argument pciIndex is incremented after each device is
456 	// examined.
457 
458 	// If a supported device is found, return a pointer to the struct containing
459 	// the chip info; else return NULL.
460 
461 	while (gPCI->get_nth_pci_info(pciIndex, &pciInfo) == B_OK) {
462 		if (pciInfo.vendor_id == VENDOR_ID) {
463 			// Search the table of supported devices to find a chip/device that
464 			// matches device ID of the current PCI device.
465 
466 			const ChipInfo* pDevice = chipTable;
467 
468 			while (pDevice->chipID != 0) {		// end of table?
469 				if (pDevice->chipID == pciInfo.device_id) {
470 					pciIndex++;
471 					return pDevice;	// matching device/chip found
472 				}
473 
474 				pDevice++;
475 			}
476 		}
477 
478 		pciIndex++;
479 	}
480 
481 	return NULL;		// no supported device found found
482 }
483 
484 
485 
486 #ifdef __HAIKU__
487 
488 static status_t
489 GetEdidFromBIOS(edid1_raw& edidRaw)
490 {
491 	// Get the EDID info from the video BIOS, and return B_OK if successful.
492 
493 #define ADDRESS_SEGMENT(address) ((addr_t)(address) >> 4)
494 #define ADDRESS_OFFSET(address) ((addr_t)(address) & 0xf)
495 
496 	bios_module_info* biosModule;
497 	status_t status = get_module(B_BIOS_MODULE_NAME, (module_info**)&biosModule);
498 	if (status != B_OK) {
499 		TRACE("GetEdidFromBIOS(): failed to get BIOS module: 0x%" B_PRIx32 "\n",
500 			status);
501 		return status;
502 	}
503 
504 	bios_state* state;
505 	status = biosModule->prepare(&state);
506 	if (status != B_OK) {
507 		TRACE("GetEdidFromBIOS(): bios_prepare() failed: 0x%" B_PRIx32 "\n",
508 			status);
509 		put_module(B_BIOS_MODULE_NAME);
510 		return status;
511 	}
512 
513 	bios_regs regs = {};
514 	regs.eax = 0x4f15;
515 	regs.ebx = 0;			// 0 = report DDC service
516 	regs.ecx = 0;
517 	regs.es = 0;
518 	regs.edi = 0;
519 
520 	status = biosModule->interrupt(state, 0x10, &regs);
521 	if (status == B_OK) {
522 		// AH contains the error code, and AL determines whether or not the
523 		// function is supported.
524 		if (regs.eax != 0x4f)
525 			status = B_NOT_SUPPORTED;
526 
527 		// Test if DDC is supported by the monitor.
528 		if ((regs.ebx & 3) == 0)
529 			status = B_NOT_SUPPORTED;
530 	}
531 
532 	if (status == B_OK) {
533 		edid1_raw* edid = (edid1_raw*)biosModule->allocate_mem(state,
534 			sizeof(edid1_raw));
535 		if (edid == NULL) {
536 			status = B_NO_MEMORY;
537 			goto out;
538 		}
539 
540 		regs.eax = 0x4f15;
541 		regs.ebx = 1;		// 1 = read EDID
542 		regs.ecx = 0;
543 		regs.edx = 0;
544 		regs.es  = ADDRESS_SEGMENT(edid);
545 		regs.edi = ADDRESS_OFFSET(edid);
546 
547 		status = biosModule->interrupt(state, 0x10, &regs);
548 		if (status == B_OK) {
549 			if (regs.eax != 0x4f) {
550 				status = B_NOT_SUPPORTED;
551 			} else {
552 				// Copy the EDID info to the caller's location, and compute the
553 				// checksum of the EDID info while copying.
554 
555 				uint8 sum = 0;
556 				uint8 allOr = 0;
557 				uint8* dest = (uint8*)&edidRaw;
558 				uint8* src = (uint8*)edid;
559 
560 				for (uint32 j = 0; j < sizeof(edidRaw); j++) {
561 					sum += *src;
562 					allOr |= *src;
563 					*dest++ = *src++;
564 				}
565 
566 				if (allOr == 0) {
567 					TRACE("GetEdidFromBIOS(); EDID info contains only zeros\n");
568 					status = B_ERROR;
569 				} else if (sum != 0) {
570 					TRACE("GetEdidFromBIOS(); Checksum error in EDID info\n");
571 					status = B_ERROR;
572 				}
573 			}
574 		}
575 	}
576 
577 out:
578 	biosModule->finish(state);
579 	put_module(B_BIOS_MODULE_NAME);
580 
581 	TRACE("GetEdidFromBIOS() status: 0x%" B_PRIx32 "\n", status);
582 	return status;
583 }
584 
585 #endif	// __HAIKU__
586 
587 
588 
589 //	#pragma mark - Kernel Interface
590 
591 
592 status_t
593 init_hardware(void)
594 {
595 	// Return B_OK if a device supported by this driver is found; otherwise,
596 	// return B_ERROR so the driver will be unloaded.
597 
598 	if (get_module(B_PCI_MODULE_NAME, (module_info**)&gPCI) != B_OK)
599 		return B_ERROR;		// unable to access PCI bus
600 
601 	// Check pci devices for a device supported by this driver.
602 
603 	uint32 pciIndex = 0;
604 	pci_info pciInfo;
605 	const ChipInfo* pDevice = GetNextSupportedDevice(pciIndex, pciInfo);
606 
607 	TRACE("init_hardware() - %s\n", pDevice == NULL ? "no supported devices" : "device supported");
608 
609 	put_module(B_PCI_MODULE_NAME);		// put away the module manager
610 
611 	return (pDevice == NULL ? B_ERROR : B_OK);
612 }
613 
614 
615 status_t  init_driver(void)
616 {
617 	// Get handle for the pci bus.
618 
619 	if (get_module(B_PCI_MODULE_NAME, (module_info**)&gPCI) != B_OK)
620 		return B_ERROR;
621 
622 	status_t status = gLock.Init("S3 driver lock");
623 	if (status < B_OK)
624 		return status;
625 
626 	// Get info about all the devices supported by this driver.
627 
628 	uint32 pciIndex = 0;
629 	uint32 count = 0;
630 
631 	while (count < MAX_DEVICES) {
632 		DeviceInfo& di = gDeviceInfo[count];
633 
634 		const ChipInfo* pDevice = GetNextSupportedDevice(pciIndex, di.pciInfo);
635 		if (pDevice == NULL)
636 			break;			// all supported devices have been obtained
637 
638 		// Compose device name.
639 		sprintf(di.name, "graphics/" DEVICE_FORMAT,
640 				  di.pciInfo.vendor_id, di.pciInfo.device_id,
641 				  di.pciInfo.bus, di.pciInfo.device, di.pciInfo.function);
642 		TRACE("init_driver() match found; name: %s\n", di.name);
643 
644 		gDeviceNames[count] = di.name;
645 		di.openCount = 0;		// mark driver as available for R/W open
646 		di.sharedArea = -1;		// indicate shared area not yet created
647 		di.sharedInfo = NULL;
648 		di.pChipInfo = pDevice;
649 		count++;
650 	}
651 
652 	gDeviceNames[count] = NULL;	// terminate list with null pointer
653 
654 	TRACE("init_driver() %ld supported devices\n", count);
655 
656 	return B_OK;
657 }
658 
659 
660 void
661 uninit_driver(void)
662 {
663 	// Free the driver data.
664 
665 	gLock.Delete();
666 	put_module(B_PCI_MODULE_NAME);	// put the pci module away
667 }
668 
669 
670 const char**
671 publish_devices(void)
672 {
673 	return (const char**)gDeviceNames;	// return list of supported devices
674 }
675 
676 
677 device_hooks*
678 find_device(const char* name)
679 {
680 	int index = 0;
681 	while (gDeviceNames[index] != NULL) {
682 		if (strcmp(name, gDeviceNames[index]) == 0)
683 			return &gDeviceHooks;
684 		index++;
685 	}
686 
687 	return NULL;
688 }
689 
690 
691 
692 //	#pragma mark - Device Hooks
693 
694 
695 static status_t
696 device_open(const char* name, uint32 /*flags*/, void** cookie)
697 {
698 	status_t status = B_OK;
699 
700 	TRACE("device_open() - name: %s, cookie: 0x%08lx)\n", name, (uint32)cookie);
701 
702 	// Find the device name in the list of devices.
703 
704 	int32 index = 0;
705 	while (gDeviceNames[index] != NULL && (strcmp(name, gDeviceNames[index]) != 0))
706 		index++;
707 
708 	if (gDeviceNames[index] == NULL)
709 		return B_BAD_VALUE;		// device name not found in list of devices
710 
711 	DeviceInfo& di = gDeviceInfo[index];
712 
713 	gLock.Acquire();	// make sure no one else has write access to common data
714 
715 	if (di.openCount == 0)
716 		status = InitDevice(di);
717 
718 	gLock.Release();
719 
720 	if (status == B_OK) {
721 		di.openCount++;		// mark device open
722 		*cookie = &di;		// send cookie to opener
723 	}
724 
725 	TRACE("device_open() returning 0x%lx,  open count: %ld\n", status, di.openCount);
726 	return status;
727 }
728 
729 
730 static status_t
731 device_read(void* dev, off_t pos, void* buf, size_t* len)
732 {
733 	// Following 3 lines of code are here to eliminate "unused parameter" warnings.
734 	(void)dev;
735 	(void)pos;
736 	(void)buf;
737 
738 	*len = 0;
739 	return B_NOT_ALLOWED;
740 }
741 
742 
743 static status_t
744 device_write(void* dev, off_t pos, const void* buf, size_t* len)
745 {
746 	// Following 3 lines of code are here to eliminate "unused parameter" warnings.
747 	(void)dev;
748 	(void)pos;
749 	(void)buf;
750 
751 	*len = 0;
752 	return B_NOT_ALLOWED;
753 }
754 
755 
756 static status_t
757 device_close(void* dev)
758 {
759 	(void)dev;		// avoid compiler warning for unused arg
760 
761 	TRACE("device_close()\n");
762 	return B_NO_ERROR;
763 }
764 
765 
766 static status_t
767 device_free(void* dev)
768 {
769 	DeviceInfo& di = *((DeviceInfo*)dev);
770 	SharedInfo& si = *(di.sharedInfo);
771 	pci_info& pciInfo = di.pciInfo;
772 
773 	TRACE("enter device_free()\n");
774 
775 	gLock.Acquire();		// lock driver
776 
777 	// If opened multiple times, merely decrement the open count and exit.
778 
779 	if (di.openCount <= 1) {
780 		DisableVBI();		// disable & clear any pending interrupts
781 
782 		if (si.bInterruptAssigned) {
783 			remove_io_interrupt_handler(pciInfo.u.h0.interrupt_line, InterruptHandler, &di);
784 		}
785 
786 		// Delete the semaphores, ignoring any errors because the owning team may have died.
787 		if (si.vertBlankSem >= 0)
788 			delete_sem(si.vertBlankSem);
789 		si.vertBlankSem = -1;
790 
791 		UnmapDevice(di);	// free regs and frame buffer areas
792 
793 		delete_area(di.sharedArea);
794 		di.sharedArea = -1;
795 		di.sharedInfo = NULL;
796 	}
797 
798 	if (di.openCount > 0)
799 		di.openCount--;		// mark device available
800 
801 	gLock.Release();	// unlock driver
802 
803 	TRACE("exit device_free() openCount: %ld\n", di.openCount);
804 	return B_OK;
805 }
806 
807 
808 static status_t
809 device_ioctl(void* dev, uint32 msg, void* buf, size_t len)
810 {
811 	DeviceInfo& di = *((DeviceInfo*)dev);
812 
813 	(void)len;		// avoid compiler warning for unused arg
814 
815 //	TRACE("device_ioctl(); ioctl: %lu, buf: 0x%08lx, len: %lu\n", msg, (uint32)buf, len);
816 
817 	switch (msg) {
818 		case B_GET_ACCELERANT_SIGNATURE:
819 			strcpy((char*)buf, "s3.accelerant");
820 			return B_OK;
821 
822 		case S3_DEVICE_NAME:
823 			strncpy((char*)buf, di.name, B_OS_NAME_LENGTH);
824 			((char*)buf)[B_OS_NAME_LENGTH -1] = '\0';
825 			return B_OK;
826 
827 		case S3_GET_PRIVATE_DATA:
828 		{
829 			S3GetPrivateData* gpd = (S3GetPrivateData*)buf;
830 			if (gpd->magic == S3_PRIVATE_DATA_MAGIC) {
831 				gpd->sharedInfoArea = di.sharedArea;
832 				return B_OK;
833 			}
834 			break;
835 		}
836 
837 		case S3_GET_EDID:
838 		{
839 #ifdef __HAIKU__
840 			S3GetEDID* ged = (S3GetEDID*)buf;
841 			if (ged->magic == S3_PRIVATE_DATA_MAGIC) {
842 				edid1_raw rawEdid;
843 				status_t status = GetEdidFromBIOS(rawEdid);
844 				if (status == B_OK)
845 					user_memcpy(&ged->rawEdid, &rawEdid, sizeof(rawEdid));
846 				return status;
847 			}
848 #else
849 			return B_UNSUPPORTED;
850 #endif
851 			break;
852 		}
853 
854 		case S3_GET_PIO:
855 		{
856 			S3GetSetPIO* gsp = (S3GetSetPIO*)buf;
857 			if (gsp->magic == S3_PRIVATE_DATA_MAGIC) {
858 				switch (gsp->size) {
859 					case 1:
860 						gsp->value = gPCI->read_io_8(gsp->offset);
861 						break;
862 					case 2:
863 						gsp->value = gPCI->read_io_16(gsp->offset);
864 						break;
865 					case 4:
866 						gsp->value = gPCI->read_io_32(gsp->offset);
867 						break;
868 					default:
869 						TRACE("device_ioctl() S3_GET_PIO invalid size: %ld\n", gsp->size);
870 						return B_ERROR;
871 				}
872 				return B_OK;
873 			}
874 			break;
875 		}
876 
877 		case S3_SET_PIO:
878 		{
879 			S3GetSetPIO* gsp = (S3GetSetPIO*)buf;
880 			if (gsp->magic == S3_PRIVATE_DATA_MAGIC) {
881 				switch (gsp->size) {
882 					case 1:
883 						gPCI->write_io_8(gsp->offset, gsp->value);
884 						break;
885 					case 2:
886 						gPCI->write_io_16(gsp->offset, gsp->value);
887 						break;
888 					case 4:
889 						gPCI->write_io_32(gsp->offset, gsp->value);
890 						break;
891 					default:
892 						TRACE("device_ioctl() S3_SET_PIO invalid size: %ld\n", gsp->size);
893 						return B_ERROR;
894 				}
895 				return B_OK;
896 			}
897 			break;
898 		}
899 
900 		case S3_RUN_INTERRUPTS:
901 		{
902 			S3SetBoolState* ri = (S3SetBoolState*)buf;
903 			if (ri->magic == S3_PRIVATE_DATA_MAGIC) {
904 				if (ri->bEnable)
905 					EnableVBI();
906 				else
907 					DisableVBI();
908 			}
909 			return B_OK;
910 		}
911 	}
912 
913 	return B_DEV_INVALID_IOCTL;
914 }
915