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