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