xref: /haiku/src/add-ons/kernel/drivers/graphics/s3/driver.cpp (revision 23d878482ed22e55dad6d1fca1df7bea42eb157c)
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 #include <malloc.h>
12 #include <stdio.h>
13 #include <string.h>
14 #include <graphic_driver.h>
15 #ifdef __HAIKU__
16 #include <arch/x86/vm86.h>
17 #endif	// __HAIKU__
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 	vm86_state vmState;
497 
498 	status_t status = vm86_prepare(&vmState, 0x2000);
499 	if (status != B_OK) {
500 		TRACE("GetEdidFromBIOS(); vm86_prepare() failed, status: %lx\n", status);
501 		return status;
502 	}
503 
504 	vmState.regs.eax = 0x4f15;
505 	vmState.regs.ebx = 0;		// 0 = report DDC service
506 	vmState.regs.ecx = 0;
507 	vmState.regs.es = 0;
508 	vmState.regs.edi = 0;
509 
510 	status = vm86_do_int(&vmState, 0x10);
511 	if (status == B_OK) {
512 		// AH contains the error code, and AL determines whether or not the
513 		// function is supported.
514 		if (vmState.regs.eax != 0x4f)
515 			status = B_NOT_SUPPORTED;
516 
517 		// Test if DDC is supported by the monitor.
518 		if ((vmState.regs.ebx & 3) == 0)
519 			status = B_NOT_SUPPORTED;
520 	}
521 
522 	if (status == B_OK) {
523 		// According to the author of the vm86 functions, the address of any
524 		// object to receive data must be >= 0x1000 and within the ram size
525 		// specified in the second argument of the vm86_prepare() call above.
526 		// Thus, the address of the struct to receive the EDID info is set to
527 		// 0x1000.
528 
529 		edid1_raw* edid = (edid1_raw*)0x1000;
530 
531 		vmState.regs.eax = 0x4f15;
532 		vmState.regs.ebx = 1;		// 1 = read EDID
533 		vmState.regs.ecx = 0;
534 		vmState.regs.edx = 0;
535 		vmState.regs.es  = ADDRESS_SEGMENT(edid);
536 		vmState.regs.edi = ADDRESS_OFFSET(edid);
537 
538 		status = vm86_do_int(&vmState, 0x10);
539 		if (status == B_OK) {
540 			if (vmState.regs.eax != 0x4f) {
541 				status = B_NOT_SUPPORTED;
542 			} else {
543 				// Copy the EDID info to the caller's location, and compute the
544 				// checksum of the EDID info while copying.
545 
546 				uint8 sum = 0;
547 				uint8 allOr = 0;
548 				uint8* dest = (uint8*)&edidRaw;
549 				uint8* src = (uint8*)edid;
550 
551 				for (uint32 j = 0; j < sizeof(edidRaw); j++) {
552 					sum += *src;
553 					allOr |= *src;
554 					*dest++ = *src++;
555 				}
556 
557 				if (allOr == 0) {
558 					TRACE("GetEdidFromBIOS(); EDID info contains only zeros\n");
559 					status = B_ERROR;
560 				} else if (sum != 0) {
561 					TRACE("GetEdidFromBIOS(); Checksum error in EDID info\n");
562 					status = B_ERROR;
563 				}
564 			}
565 		}
566 	}
567 
568 	vm86_cleanup(&vmState);
569 
570 	TRACE("GetEdidFromBIOS() status: 0x%lx\n", status);
571 	return status;
572 }
573 
574 #endif	// __HAIKU__
575 
576 
577 
578 //	#pragma mark - Kernel Interface
579 
580 
581 status_t
582 init_hardware(void)
583 {
584 	// Return B_OK if a device supported by this driver is found; otherwise,
585 	// return B_ERROR so the driver will be unloaded.
586 
587 	if (get_module(B_PCI_MODULE_NAME, (module_info**)&gPCI) != B_OK)
588 		return B_ERROR;		// unable to access PCI bus
589 
590 	// Check pci devices for a device supported by this driver.
591 
592 	uint32 pciIndex = 0;
593 	pci_info pciInfo;
594 	const ChipInfo* pDevice = GetNextSupportedDevice(pciIndex, pciInfo);
595 
596 	TRACE("init_hardware() - %s\n", pDevice == NULL ? "no supported devices" : "device supported");
597 
598 	put_module(B_PCI_MODULE_NAME);		// put away the module manager
599 
600 	return (pDevice == NULL ? B_ERROR : B_OK);
601 }
602 
603 
604 status_t  init_driver(void)
605 {
606 	// Get handle for the pci bus.
607 
608 	if (get_module(B_PCI_MODULE_NAME, (module_info**)&gPCI) != B_OK)
609 		return B_ERROR;
610 
611 	status_t status = gLock.Init("S3 driver lock");
612 	if (status < B_OK)
613 		return status;
614 
615 	// Get info about all the devices supported by this driver.
616 
617 	uint32 pciIndex = 0;
618 	uint32 count = 0;
619 
620 	while (count < MAX_DEVICES) {
621 		DeviceInfo& di = gDeviceInfo[count];
622 
623 		const ChipInfo* pDevice = GetNextSupportedDevice(pciIndex, di.pciInfo);
624 		if (pDevice == NULL)
625 			break;			// all supported devices have been obtained
626 
627 		// Compose device name.
628 		sprintf(di.name, "graphics/" DEVICE_FORMAT,
629 				  di.pciInfo.vendor_id, di.pciInfo.device_id,
630 				  di.pciInfo.bus, di.pciInfo.device, di.pciInfo.function);
631 		TRACE("init_driver() match found; name: %s\n", di.name);
632 
633 		gDeviceNames[count] = di.name;
634 		di.openCount = 0;		// mark driver as available for R/W open
635 		di.sharedArea = -1;		// indicate shared area not yet created
636 		di.sharedInfo = NULL;
637 		di.pChipInfo = pDevice;
638 		count++;
639 	}
640 
641 	gDeviceNames[count] = NULL;	// terminate list with null pointer
642 
643 	TRACE("init_driver() %ld supported devices\n", count);
644 
645 	return B_OK;
646 }
647 
648 
649 void
650 uninit_driver(void)
651 {
652 	// Free the driver data.
653 
654 	gLock.Delete();
655 	put_module(B_PCI_MODULE_NAME);	// put the pci module away
656 }
657 
658 
659 const char**
660 publish_devices(void)
661 {
662 	return (const char**)gDeviceNames;	// return list of supported devices
663 }
664 
665 
666 device_hooks*
667 find_device(const char* name)
668 {
669 	int index = 0;
670 	while (gDeviceNames[index] != NULL) {
671 		if (strcmp(name, gDeviceNames[index]) == 0)
672 			return &gDeviceHooks;
673 		index++;
674 	}
675 
676 	return NULL;
677 }
678 
679 
680 
681 //	#pragma mark - Device Hooks
682 
683 
684 static status_t
685 device_open(const char* name, uint32 /*flags*/, void** cookie)
686 {
687 	status_t status = B_OK;
688 
689 	TRACE("device_open() - name: %s, cookie: 0x%08lx)\n", name, (uint32)cookie);
690 
691 	// Find the device name in the list of devices.
692 
693 	int32 index = 0;
694 	while (gDeviceNames[index] != NULL && (strcmp(name, gDeviceNames[index]) != 0))
695 		index++;
696 
697 	if (gDeviceNames[index] == NULL)
698 		return B_BAD_VALUE;		// device name not found in list of devices
699 
700 	DeviceInfo& di = gDeviceInfo[index];
701 
702 	gLock.Acquire();	// make sure no one else has write access to common data
703 
704 	if (di.openCount == 0)
705 		status = InitDevice(di);
706 
707 	gLock.Release();
708 
709 	if (status == B_OK) {
710 		di.openCount++;		// mark device open
711 		*cookie = &di;		// send cookie to opener
712 	}
713 
714 	TRACE("device_open() returning 0x%lx,  open count: %ld\n", status, di.openCount);
715 	return status;
716 }
717 
718 
719 static status_t
720 device_read(void* dev, off_t pos, void* buf, size_t* len)
721 {
722 	// Following 3 lines of code are here to eliminate "unused parameter" warnings.
723 	(void)dev;
724 	(void)pos;
725 	(void)buf;
726 
727 	*len = 0;
728 	return B_NOT_ALLOWED;
729 }
730 
731 
732 static status_t
733 device_write(void* dev, off_t pos, const void* buf, size_t* len)
734 {
735 	// Following 3 lines of code are here to eliminate "unused parameter" warnings.
736 	(void)dev;
737 	(void)pos;
738 	(void)buf;
739 
740 	*len = 0;
741 	return B_NOT_ALLOWED;
742 }
743 
744 
745 static status_t
746 device_close(void* dev)
747 {
748 	(void)dev;		// avoid compiler warning for unused arg
749 
750 	TRACE("device_close()\n");
751 	return B_NO_ERROR;
752 }
753 
754 
755 static status_t
756 device_free(void* dev)
757 {
758 	DeviceInfo& di = *((DeviceInfo*)dev);
759 	SharedInfo& si = *(di.sharedInfo);
760 	pci_info& pciInfo = di.pciInfo;
761 
762 	TRACE("enter device_free()\n");
763 
764 	gLock.Acquire();		// lock driver
765 
766 	// If opened multiple times, merely decrement the open count and exit.
767 
768 	if (di.openCount <= 1) {
769 		DisableVBI();		// disable & clear any pending interrupts
770 
771 		if (si.bInterruptAssigned) {
772 			remove_io_interrupt_handler(pciInfo.u.h0.interrupt_line, InterruptHandler, &di);
773 		}
774 
775 		// Delete the semaphores, ignoring any errors because the owning team may have died.
776 		if (si.vertBlankSem >= 0)
777 			delete_sem(si.vertBlankSem);
778 		si.vertBlankSem = -1;
779 
780 		UnmapDevice(di);	// free regs and frame buffer areas
781 
782 		delete_area(di.sharedArea);
783 		di.sharedArea = -1;
784 		di.sharedInfo = NULL;
785 	}
786 
787 	if (di.openCount > 0)
788 		di.openCount--;		// mark device available
789 
790 	gLock.Release();	// unlock driver
791 
792 	TRACE("exit device_free() openCount: %ld\n", di.openCount);
793 	return B_OK;
794 }
795 
796 
797 static status_t
798 device_ioctl(void* dev, uint32 msg, void* buf, size_t len)
799 {
800 	DeviceInfo& di = *((DeviceInfo*)dev);
801 
802 	(void)len;		// avoid compiler warning for unused arg
803 
804 //	TRACE("device_ioctl(); ioctl: %lu, buf: 0x%08lx, len: %lu\n", msg, (uint32)buf, len);
805 
806 	switch (msg) {
807 		case B_GET_ACCELERANT_SIGNATURE:
808 			strcpy((char*)buf, "s3.accelerant");
809 			return B_OK;
810 
811 		case S3_DEVICE_NAME:
812 			strncpy((char*)buf, di.name, B_OS_NAME_LENGTH);
813 			((char*)buf)[B_OS_NAME_LENGTH -1] = '\0';
814 			return B_OK;
815 
816 		case S3_GET_PRIVATE_DATA:
817 		{
818 			S3GetPrivateData* gpd = (S3GetPrivateData*)buf;
819 			if (gpd->magic == S3_PRIVATE_DATA_MAGIC) {
820 				gpd->sharedInfoArea = di.sharedArea;
821 				return B_OK;
822 			}
823 			break;
824 		}
825 
826 		case S3_GET_EDID:
827 		{
828 #ifdef __HAIKU__
829 			S3GetEDID* ged = (S3GetEDID*)buf;
830 			if (ged->magic == S3_PRIVATE_DATA_MAGIC) {
831 				edid1_raw rawEdid;
832 				status_t status = GetEdidFromBIOS(rawEdid);
833 				if (status == B_OK)
834 					user_memcpy(&ged->rawEdid, &rawEdid, sizeof(rawEdid));
835 				return status;
836 			}
837 #else
838 			return B_UNSUPPORTED;
839 #endif
840 			break;
841 		}
842 
843 		case S3_GET_PIO:
844 		{
845 			S3GetSetPIO* gsp = (S3GetSetPIO*)buf;
846 			if (gsp->magic == S3_PRIVATE_DATA_MAGIC) {
847 				switch (gsp->size) {
848 					case 1:
849 						gsp->value = gPCI->read_io_8(gsp->offset);
850 						break;
851 					case 2:
852 						gsp->value = gPCI->read_io_16(gsp->offset);
853 						break;
854 					case 4:
855 						gsp->value = gPCI->read_io_32(gsp->offset);
856 						break;
857 					default:
858 						TRACE("device_ioctl() S3_GET_PIO invalid size: %ld\n", gsp->size);
859 						return B_ERROR;
860 				}
861 				return B_OK;
862 			}
863 			break;
864 		}
865 
866 		case S3_SET_PIO:
867 		{
868 			S3GetSetPIO* gsp = (S3GetSetPIO*)buf;
869 			if (gsp->magic == S3_PRIVATE_DATA_MAGIC) {
870 				switch (gsp->size) {
871 					case 1:
872 						gPCI->write_io_8(gsp->offset, gsp->value);
873 						break;
874 					case 2:
875 						gPCI->write_io_16(gsp->offset, gsp->value);
876 						break;
877 					case 4:
878 						gPCI->write_io_32(gsp->offset, gsp->value);
879 						break;
880 					default:
881 						TRACE("device_ioctl() S3_SET_PIO invalid size: %ld\n", gsp->size);
882 						return B_ERROR;
883 				}
884 				return B_OK;
885 			}
886 			break;
887 		}
888 
889 		case S3_RUN_INTERRUPTS:
890 		{
891 			S3SetBoolState* ri = (S3SetBoolState*)buf;
892 			if (ri->magic == S3_PRIVATE_DATA_MAGIC) {
893 				if (ri->bEnable)
894 					EnableVBI();
895 				else
896 					DisableVBI();
897 			}
898 			return B_OK;
899 		}
900 	}
901 
902 	return B_DEV_INVALID_IOCTL;
903 }
904