xref: /haiku/src/add-ons/kernel/drivers/graphics/s3/driver.cpp (revision fc7456e9b1ec38c941134ed6d01c438cf289381e)
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 		B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA | B_CLONEABLE_AREA,
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_WRITE_COMBINING_MEMORY,
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%" B_PRIXADDR "\n",
280 		si.videoMemArea, (addr_t)(si.videoMemAddr));
281 
282 	// If there was an error, delete other areas.
283 	if (si.videoMemArea < 0) {
284 		delete_area(si.regsArea);
285 		si.regsArea = -1;
286 	}
287 
288 	TRACE("leave MapDevice(); result: %ld\n", si.videoMemArea);
289 	return si.videoMemArea;
290 }
291 
292 
293 static void
294 UnmapDevice(DeviceInfo& di)
295 {
296 	SharedInfo& si = *(di.sharedInfo);
297 
298 	TRACE("enter UnmapDevice()\n");
299 
300 	if (si.regsArea >= 0)
301 		delete_area(si.regsArea);
302 	if (si.videoMemArea >= 0)
303 		delete_area(si.videoMemArea);
304 
305 	si.regsArea = si.videoMemArea = -1;
306 	si.videoMemAddr = NULL;
307 	di.regs = NULL;
308 
309 	TRACE("exit UnmapDevice()\n");
310 }
311 
312 
313 static int32
314 InterruptHandler(void* data)
315 {
316 	int32 handled = B_UNHANDLED_INTERRUPT;
317 	DeviceInfo& di = *((DeviceInfo*)data);
318 	int32* flags = &(di.flags);
319 
320 	// Is someone already handling an interrupt for this device?
321 	if (atomic_or(flags, SKD_HANDLER_INSTALLED) & SKD_HANDLER_INSTALLED)
322 		return B_UNHANDLED_INTERRUPT;
323 
324 	if (InterruptIsVBI()) {	// was interrupt a VBI?
325 		ClearVBI();			// clear interrupt
326 
327 		handled = B_HANDLED_INTERRUPT;
328 
329 		// Release vertical blanking semaphore.
330 		sem_id& sem = di.sharedInfo->vertBlankSem;
331 
332 		if (sem >= 0) {
333 			int32 blocked;
334 			if ((get_sem_count(sem, &blocked) == B_OK) && (blocked < 0)) {
335 				release_sem_etc(sem, -blocked, B_DO_NOT_RESCHEDULE);
336 				handled = B_INVOKE_SCHEDULER;
337 			}
338 		}
339 	}
340 
341 	atomic_and(flags, ~SKD_HANDLER_INSTALLED);	// note we're not in handler anymore
342 
343 	return handled;
344 }
345 
346 
347 static void
348 InitInterruptHandler(DeviceInfo& di)
349 {
350 	SharedInfo& si = *(di.sharedInfo);
351 
352 	TRACE("enter InitInterruptHandler()\n");
353 
354 	DisableVBI();					// disable & clear any pending interrupts
355 	si.bInterruptAssigned = false;	// indicate interrupt not assigned yet
356 
357 	// Create a semaphore for vertical blank management.
358 	si.vertBlankSem = create_sem(0, di.name);
359 	if (si.vertBlankSem < 0)
360 		return;
361 
362 	// Change the owner of the semaphores to the calling team (usually the
363 	// app_server).  This is required because apps can't aquire kernel
364 	// semaphores.
365 
366 	thread_id threadID = find_thread(NULL);
367 	thread_info threadInfo;
368 	status_t status = get_thread_info(threadID, &threadInfo);
369 	if (status == B_OK)
370 		status = set_sem_owner(si.vertBlankSem, threadInfo.team);
371 
372 	// If there is a valid interrupt assigned, set up interrupts.
373 
374 	if (status == B_OK && di.pciInfo.u.h0.interrupt_pin != 0x00
375 		&& di.pciInfo.u.h0.interrupt_line != 0xff) {
376 		// We have a interrupt line to use.
377 
378 		status = install_io_interrupt_handler(di.pciInfo.u.h0.interrupt_line,
379 			InterruptHandler, (void*)(&di), 0);
380 
381 		if (status == B_OK)
382 			si.bInterruptAssigned = true;	// we can use interrupt related functions
383 	}
384 
385 	if (status != B_OK) {
386 		// Interrupt does not exist; thus delete semaphore as it won't be used.
387 		delete_sem(si.vertBlankSem);
388 		si.vertBlankSem = -1;
389 	}
390 }
391 
392 
393 static status_t
394 InitDevice(DeviceInfo& di)
395 {
396 	// Perform initialization and mapping of the device, and return B_OK if
397 	// sucessful;  else, return error code.
398 
399 	// Create the area for shared info with NO user-space read or write
400 	// permissions, to prevent accidental damage.
401 
402 	TRACE("enter InitDevice()\n");
403 
404 	pci_info& pciInfo = di.pciInfo;
405 	char sharedName[B_OS_NAME_LENGTH];
406 
407 	sprintf(sharedName, DEVICE_FORMAT " shared",
408 		pciInfo.vendor_id, pciInfo.device_id,
409 		pciInfo.bus, pciInfo.device, pciInfo.function);
410 
411 	di.sharedArea = create_area(sharedName, (void**) &(di.sharedInfo),
412 		B_ANY_KERNEL_ADDRESS,
413 		((sizeof(SharedInfo) + (B_PAGE_SIZE - 1)) & ~(B_PAGE_SIZE - 1)),
414 		B_FULL_LOCK,
415 		B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA | B_CLONEABLE_AREA);
416 	if (di.sharedArea < 0)
417 		return di.sharedArea;	// return error code
418 
419 	SharedInfo& si = *(di.sharedInfo);
420 
421 	si.vendorID = pciInfo.vendor_id;
422 	si.deviceID = pciInfo.device_id;
423 	si.revision = pciInfo.revision;
424 	si.chipType = di.pChipInfo->chipType;
425 	strcpy(si.chipName, di.pChipInfo->chipName);
426 
427 	// Trio64 and Trio64V+ chips have the same ID but different revision numbers.
428 	// Since the Trio64V+ supports MMIO, better performance can be obtained
429 	// from it if it is distinguished from the Trio64.
430 
431 	if (si.chipType == S3_TRIO64 && si.revision & 0x40) {
432 		si.chipType = S3_TRIO64_VP;
433 		strcpy(si.chipName, "Trio64 V+");
434 	}
435 
436 	status_t status = MapDevice(di);
437 	if (status < 0) {
438 		delete_area(di.sharedArea);
439 		di.sharedArea = -1;
440 		di.sharedInfo = NULL;
441 		return status;		// return error code
442 	}
443 
444 	InitInterruptHandler(di);
445 
446 	TRACE("Interrupt assigned:  %s\n", si.bInterruptAssigned ? "yes" : "no");
447 	return B_OK;
448 }
449 
450 
451 static const ChipInfo*
452 GetNextSupportedDevice(uint32& pciIndex, pci_info& pciInfo)
453 {
454 	// Search the PCI devices for a device that is supported by this driver.
455 	// The search starts at the device specified by argument pciIndex, and
456 	// continues until a supported device is found or there are no more devices
457 	// to examine.  Argument pciIndex is incremented after each device is
458 	// examined.
459 
460 	// If a supported device is found, return a pointer to the struct containing
461 	// the chip info; else return NULL.
462 
463 	while (gPCI->get_nth_pci_info(pciIndex, &pciInfo) == B_OK) {
464 		if (pciInfo.vendor_id == VENDOR_ID) {
465 			// Search the table of supported devices to find a chip/device that
466 			// matches device ID of the current PCI device.
467 
468 			const ChipInfo* pDevice = chipTable;
469 
470 			while (pDevice->chipID != 0) {		// end of table?
471 				if (pDevice->chipID == pciInfo.device_id) {
472 					pciIndex++;
473 					return pDevice;	// matching device/chip found
474 				}
475 
476 				pDevice++;
477 			}
478 		}
479 
480 		pciIndex++;
481 	}
482 
483 	return NULL;		// no supported device found found
484 }
485 
486 
487 
488 #ifdef __HAIKU__
489 
490 static status_t
491 GetEdidFromBIOS(edid1_raw& edidRaw)
492 {
493 	// Get the EDID info from the video BIOS, and return B_OK if successful.
494 
495 #define ADDRESS_SEGMENT(address) ((addr_t)(address) >> 4)
496 #define ADDRESS_OFFSET(address) ((addr_t)(address) & 0xf)
497 
498 	bios_module_info* biosModule;
499 	status_t status = get_module(B_BIOS_MODULE_NAME, (module_info**)&biosModule);
500 	if (status != B_OK) {
501 		TRACE("GetEdidFromBIOS(): failed to get BIOS module: 0x%" B_PRIx32 "\n",
502 			status);
503 		return status;
504 	}
505 
506 	bios_state* state;
507 	status = biosModule->prepare(&state);
508 	if (status != B_OK) {
509 		TRACE("GetEdidFromBIOS(): bios_prepare() failed: 0x%" B_PRIx32 "\n",
510 			status);
511 		put_module(B_BIOS_MODULE_NAME);
512 		return status;
513 	}
514 
515 	bios_regs regs = {};
516 	regs.eax = 0x4f15;
517 	regs.ebx = 0;			// 0 = report DDC service
518 	regs.ecx = 0;
519 	regs.es = 0;
520 	regs.edi = 0;
521 
522 	status = biosModule->interrupt(state, 0x10, &regs);
523 	if (status == B_OK) {
524 		// AH contains the error code, and AL determines whether or not the
525 		// function is supported.
526 		if (regs.eax != 0x4f)
527 			status = B_NOT_SUPPORTED;
528 
529 		// Test if DDC is supported by the monitor.
530 		if ((regs.ebx & 3) == 0)
531 			status = B_NOT_SUPPORTED;
532 	}
533 
534 	if (status == B_OK) {
535 		edid1_raw* edid = (edid1_raw*)biosModule->allocate_mem(state,
536 			sizeof(edid1_raw));
537 		if (edid == NULL) {
538 			status = B_NO_MEMORY;
539 			goto out;
540 		}
541 
542 		regs.eax = 0x4f15;
543 		regs.ebx = 1;		// 1 = read EDID
544 		regs.ecx = 0;
545 		regs.edx = 0;
546 		regs.es  = ADDRESS_SEGMENT(edid);
547 		regs.edi = ADDRESS_OFFSET(edid);
548 
549 		status = biosModule->interrupt(state, 0x10, &regs);
550 		if (status == B_OK) {
551 			if (regs.eax != 0x4f) {
552 				status = B_NOT_SUPPORTED;
553 			} else {
554 				// Copy the EDID info to the caller's location, and compute the
555 				// checksum of the EDID info while copying.
556 
557 				uint8 sum = 0;
558 				uint8 allOr = 0;
559 				uint8* dest = (uint8*)&edidRaw;
560 				uint8* src = (uint8*)edid;
561 
562 				for (uint32 j = 0; j < sizeof(edidRaw); j++) {
563 					sum += *src;
564 					allOr |= *src;
565 					*dest++ = *src++;
566 				}
567 
568 				if (allOr == 0) {
569 					TRACE("GetEdidFromBIOS(); EDID info contains only zeros\n");
570 					status = B_ERROR;
571 				} else if (sum != 0) {
572 					TRACE("GetEdidFromBIOS(); Checksum error in EDID info\n");
573 					status = B_ERROR;
574 				}
575 			}
576 		}
577 	}
578 
579 out:
580 	biosModule->finish(state);
581 	put_module(B_BIOS_MODULE_NAME);
582 
583 	TRACE("GetEdidFromBIOS() status: 0x%" B_PRIx32 "\n", status);
584 	return status;
585 }
586 
587 #endif	// __HAIKU__
588 
589 
590 
591 //	#pragma mark - Kernel Interface
592 
593 
594 status_t
595 init_hardware(void)
596 {
597 	// Return B_OK if a device supported by this driver is found; otherwise,
598 	// return B_ERROR so the driver will be unloaded.
599 
600 	if (get_module(B_PCI_MODULE_NAME, (module_info**)&gPCI) != B_OK)
601 		return B_ERROR;		// unable to access PCI bus
602 
603 	// Check pci devices for a device supported by this driver.
604 
605 	uint32 pciIndex = 0;
606 	pci_info pciInfo;
607 	const ChipInfo* pDevice = GetNextSupportedDevice(pciIndex, pciInfo);
608 
609 	TRACE("init_hardware() - %s\n", pDevice == NULL ? "no supported devices" : "device supported");
610 
611 	put_module(B_PCI_MODULE_NAME);		// put away the module manager
612 
613 	return (pDevice == NULL ? B_ERROR : B_OK);
614 }
615 
616 
617 status_t  init_driver(void)
618 {
619 	// Get handle for the pci bus.
620 
621 	if (get_module(B_PCI_MODULE_NAME, (module_info**)&gPCI) != B_OK)
622 		return B_ERROR;
623 
624 	status_t status = gLock.Init("S3 driver lock");
625 	if (status < B_OK)
626 		return status;
627 
628 	// Get info about all the devices supported by this driver.
629 
630 	uint32 pciIndex = 0;
631 	uint32 count = 0;
632 
633 	while (count < MAX_DEVICES) {
634 		DeviceInfo& di = gDeviceInfo[count];
635 
636 		const ChipInfo* pDevice = GetNextSupportedDevice(pciIndex, di.pciInfo);
637 		if (pDevice == NULL)
638 			break;			// all supported devices have been obtained
639 
640 		// Compose device name.
641 		sprintf(di.name, "graphics/" DEVICE_FORMAT,
642 				  di.pciInfo.vendor_id, di.pciInfo.device_id,
643 				  di.pciInfo.bus, di.pciInfo.device, di.pciInfo.function);
644 		TRACE("init_driver() match found; name: %s\n", di.name);
645 
646 		gDeviceNames[count] = di.name;
647 		di.openCount = 0;		// mark driver as available for R/W open
648 		di.sharedArea = -1;		// indicate shared area not yet created
649 		di.sharedInfo = NULL;
650 		di.pChipInfo = pDevice;
651 		count++;
652 	}
653 
654 	gDeviceNames[count] = NULL;	// terminate list with null pointer
655 
656 	TRACE("init_driver() %ld supported devices\n", count);
657 
658 	return B_OK;
659 }
660 
661 
662 void
663 uninit_driver(void)
664 {
665 	// Free the driver data.
666 
667 	gLock.Delete();
668 	put_module(B_PCI_MODULE_NAME);	// put the pci module away
669 }
670 
671 
672 const char**
673 publish_devices(void)
674 {
675 	return (const char**)gDeviceNames;	// return list of supported devices
676 }
677 
678 
679 device_hooks*
680 find_device(const char* name)
681 {
682 	int index = 0;
683 	while (gDeviceNames[index] != NULL) {
684 		if (strcmp(name, gDeviceNames[index]) == 0)
685 			return &gDeviceHooks;
686 		index++;
687 	}
688 
689 	return NULL;
690 }
691 
692 
693 
694 //	#pragma mark - Device Hooks
695 
696 
697 static status_t
698 device_open(const char* name, uint32 /*flags*/, void** cookie)
699 {
700 	status_t status = B_OK;
701 
702 	TRACE("device_open() - name: %s, cookie: 0x%" B_PRIXADDR "\n", name,
703 		(addr_t)cookie);
704 
705 	// Find the device name in the list of devices.
706 
707 	int32 index = 0;
708 	while (gDeviceNames[index] != NULL && (strcmp(name, gDeviceNames[index]) != 0))
709 		index++;
710 
711 	if (gDeviceNames[index] == NULL)
712 		return B_BAD_VALUE;		// device name not found in list of devices
713 
714 	DeviceInfo& di = gDeviceInfo[index];
715 
716 	gLock.Acquire();	// make sure no one else has write access to common data
717 
718 	if (di.openCount == 0)
719 		status = InitDevice(di);
720 
721 	gLock.Release();
722 
723 	if (status == B_OK) {
724 		di.openCount++;		// mark device open
725 		*cookie = &di;		// send cookie to opener
726 	}
727 
728 	TRACE("device_open() returning 0x%lx,  open count: %ld\n", status, di.openCount);
729 	return status;
730 }
731 
732 
733 static status_t
734 device_read(void* dev, off_t pos, void* buf, size_t* len)
735 {
736 	// Following 3 lines of code are here to eliminate "unused parameter" warnings.
737 	(void)dev;
738 	(void)pos;
739 	(void)buf;
740 
741 	*len = 0;
742 	return B_NOT_ALLOWED;
743 }
744 
745 
746 static status_t
747 device_write(void* dev, off_t pos, const void* buf, size_t* len)
748 {
749 	// Following 3 lines of code are here to eliminate "unused parameter" warnings.
750 	(void)dev;
751 	(void)pos;
752 	(void)buf;
753 
754 	*len = 0;
755 	return B_NOT_ALLOWED;
756 }
757 
758 
759 static status_t
760 device_close(void* dev)
761 {
762 	(void)dev;		// avoid compiler warning for unused arg
763 
764 	TRACE("device_close()\n");
765 	return B_NO_ERROR;
766 }
767 
768 
769 static status_t
770 device_free(void* dev)
771 {
772 	DeviceInfo& di = *((DeviceInfo*)dev);
773 	SharedInfo& si = *(di.sharedInfo);
774 	pci_info& pciInfo = di.pciInfo;
775 
776 	TRACE("enter device_free()\n");
777 
778 	gLock.Acquire();		// lock driver
779 
780 	// If opened multiple times, merely decrement the open count and exit.
781 
782 	if (di.openCount <= 1) {
783 		DisableVBI();		// disable & clear any pending interrupts
784 
785 		if (si.bInterruptAssigned) {
786 			remove_io_interrupt_handler(pciInfo.u.h0.interrupt_line, InterruptHandler, &di);
787 		}
788 
789 		// Delete the semaphores, ignoring any errors because the owning team may have died.
790 		if (si.vertBlankSem >= 0)
791 			delete_sem(si.vertBlankSem);
792 		si.vertBlankSem = -1;
793 
794 		UnmapDevice(di);	// free regs and frame buffer areas
795 
796 		delete_area(di.sharedArea);
797 		di.sharedArea = -1;
798 		di.sharedInfo = NULL;
799 	}
800 
801 	if (di.openCount > 0)
802 		di.openCount--;		// mark device available
803 
804 	gLock.Release();	// unlock driver
805 
806 	TRACE("exit device_free() openCount: %ld\n", di.openCount);
807 	return B_OK;
808 }
809 
810 
811 static status_t
812 device_ioctl(void* dev, uint32 msg, void* buf, size_t len)
813 {
814 	DeviceInfo& di = *((DeviceInfo*)dev);
815 
816 	(void)len;		// avoid compiler warning for unused arg
817 
818 //	TRACE("device_ioctl(); ioctl: %lu, buf: 0x%08lx, len: %lu\n", msg, (uint32)buf, len);
819 
820 	switch (msg) {
821 		case B_GET_ACCELERANT_SIGNATURE:
822 			strcpy((char*)buf, "s3.accelerant");
823 			return B_OK;
824 
825 		case S3_DEVICE_NAME:
826 			strncpy((char*)buf, di.name, B_OS_NAME_LENGTH);
827 			((char*)buf)[B_OS_NAME_LENGTH -1] = '\0';
828 			return B_OK;
829 
830 		case S3_GET_PRIVATE_DATA:
831 		{
832 			S3GetPrivateData* gpd = (S3GetPrivateData*)buf;
833 			if (gpd->magic == S3_PRIVATE_DATA_MAGIC) {
834 				gpd->sharedInfoArea = di.sharedArea;
835 				return B_OK;
836 			}
837 			break;
838 		}
839 
840 		case S3_GET_EDID:
841 		{
842 #ifdef __HAIKU__
843 			S3GetEDID* ged = (S3GetEDID*)buf;
844 			if (ged->magic == S3_PRIVATE_DATA_MAGIC) {
845 				edid1_raw rawEdid;
846 				status_t status = GetEdidFromBIOS(rawEdid);
847 				if (status == B_OK)
848 					user_memcpy(&ged->rawEdid, &rawEdid, sizeof(rawEdid));
849 				return status;
850 			}
851 #else
852 			return B_UNSUPPORTED;
853 #endif
854 			break;
855 		}
856 
857 		case S3_GET_PIO:
858 		{
859 			S3GetSetPIO* gsp = (S3GetSetPIO*)buf;
860 			if (gsp->magic == S3_PRIVATE_DATA_MAGIC) {
861 				switch (gsp->size) {
862 					case 1:
863 						gsp->value = gPCI->read_io_8(gsp->offset);
864 						break;
865 					case 2:
866 						gsp->value = gPCI->read_io_16(gsp->offset);
867 						break;
868 					case 4:
869 						gsp->value = gPCI->read_io_32(gsp->offset);
870 						break;
871 					default:
872 						TRACE("device_ioctl() S3_GET_PIO invalid size: %ld\n", gsp->size);
873 						return B_ERROR;
874 				}
875 				return B_OK;
876 			}
877 			break;
878 		}
879 
880 		case S3_SET_PIO:
881 		{
882 			S3GetSetPIO* gsp = (S3GetSetPIO*)buf;
883 			if (gsp->magic == S3_PRIVATE_DATA_MAGIC) {
884 				switch (gsp->size) {
885 					case 1:
886 						gPCI->write_io_8(gsp->offset, gsp->value);
887 						break;
888 					case 2:
889 						gPCI->write_io_16(gsp->offset, gsp->value);
890 						break;
891 					case 4:
892 						gPCI->write_io_32(gsp->offset, gsp->value);
893 						break;
894 					default:
895 						TRACE("device_ioctl() S3_SET_PIO invalid size: %ld\n", gsp->size);
896 						return B_ERROR;
897 				}
898 				return B_OK;
899 			}
900 			break;
901 		}
902 
903 		case S3_RUN_INTERRUPTS:
904 		{
905 			S3SetBoolState* ri = (S3SetBoolState*)buf;
906 			if (ri->magic == S3_PRIVATE_DATA_MAGIC) {
907 				if (ri->bEnable)
908 					EnableVBI();
909 				else
910 					DisableVBI();
911 			}
912 			return B_OK;
913 		}
914 	}
915 
916 	return B_DEV_INVALID_IOCTL;
917 }
918