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
36int32 api_version = B_CUR_DRIVER_API_VERSION;	// revision of driver API we support
37
38#define VENDOR_ID 0x5333	// S3 vendor ID
39
40
41struct 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
53static 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
94struct 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
106static Benaphore		gLock;
107static DeviceInfo		gDeviceInfo[MAX_DEVICES];
108static char*			gDeviceNames[MAX_DEVICES + 1];
109static pci_module_info*	gPCI;
110
111
112// Prototypes for device hook functions.
113
114static status_t device_open(const char* name, uint32 flags, void** cookie);
115static status_t device_close(void* dev);
116static status_t device_free(void* dev);
117static status_t device_read(void* dev, off_t pos, void* buf, size_t* len);
118static status_t device_write(void* dev, off_t pos, const void* buf, size_t* len);
119static status_t device_ioctl(void* dev, uint32 msg, void* buf, size_t len);
120
121static 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
137static inline uint32
138GetPCI(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
144static inline void
145SetPCI(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
155static bool
156InterruptIsVBI()
157{
158	// return true only if a vertical blanking interrupt has occured
159	return false;
160}
161
162
163static void
164ClearVBI()
165{
166}
167
168static void
169EnableVBI()
170{
171}
172
173static void
174DisableVBI()
175{
176}
177
178
179static status_t
180MapDevice(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_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%" 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
293static void
294UnmapDevice(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
313static int32
314InterruptHandler(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
347static void
348InitInterruptHandler(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
393static status_t
394InitDevice(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
451static const ChipInfo*
452GetNextSupportedDevice(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
490static status_t
491GetEdidFromBIOS(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
579out:
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
594status_t
595init_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
617status_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
662void
663uninit_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
672const char**
673publish_devices(void)
674{
675	return (const char**)gDeviceNames;	// return list of supported devices
676}
677
678
679device_hooks*
680find_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
697static status_t
698device_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
733static status_t
734device_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
746static status_t
747device_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
759static status_t
760device_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
769static status_t
770device_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
811static status_t
812device_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