xref: /haiku/src/add-ons/kernel/drivers/graphics/s3/driver.cpp (revision 746cac055adc6ac3308c7bc2d29040fb95689cc9)
1 /*
2 	Copyright 1999, Be Incorporated.   All Rights Reserved.
3 	This file may be used under the terms of the Be Sample Code License.
4 
5 	Other 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 
16 #include "DriverInterface.h"
17 
18 
19 #undef TRACE
20 
21 #ifdef ENABLE_DEBUG_TRACE
22 #	define TRACE(x...) dprintf("S3: " x)
23 #else
24 #	define TRACE(x...) ;
25 #endif
26 
27 
28 #define get_pci(o, s)	 (*pci_bus->read_pci_config)(pcii->bus, pcii->device, pcii->function, (o), (s))
29 #define set_pci(o, s, v) (*pci_bus->write_pci_config)(pcii->bus, pcii->device, pcii->function, (o), (s), (v))
30 
31 #define SKD_HANDLER_INSTALLED 0x80000000
32 #define MAX_DEVICES		8
33 #define DEVICE_FORMAT	"%04X_%04X_%02X%02X%02X"
34 
35 int32 api_version = B_CUR_DRIVER_API_VERSION;	// revision of driver API we support
36 
37 
38 struct ChipInfo {
39 	uint16	chipID;			// PCI device id of the chip
40 	uint16	chipType;		// assigned chip type identifier
41 	char*	chipName;		// user recognizable name for chip (must be < 32 chars)
42 };
43 
44 // This table maps a PCI device ID to a chip type identifier and the chip name.
45 // Note that the Trio64 and Trio64V+ chips have the same ID, but have a different
46 // revision number.  After the revision number is examined, the Trio64V+ will
47 // have a different chip type code and name assigned.
48 
49 static const ChipInfo S3_ChipTable[] = {
50 	{ 0x8811, S3_TRIO64,		"Trio64"			},		// see comment above
51 	{ 0x8814, S3_TRIO64_UVP,	"Trio64 UV+"		},
52 	{ 0x8901, S3_TRIO64_V2,		"Trio64 V2/DX/GX"	},
53 
54 	{ 0x5631, S3_VIRGE,			"Virge"				},
55 	{ 0x883D, S3_VIRGE_VX,		"Virge VX"			},
56 	{ 0x8A01, S3_VIRGE_DXGX,	"Virge DX/GX"		},
57 	{ 0x8A10, S3_VIRGE_GX2,		"Virge GX2"			},
58 	{ 0x8C01, S3_VIRGE_MX,		"Virge MX"			},
59 	{ 0x8C03, S3_VIRGE_MXP,		"Virge MX+"			},
60 	{ 0x8904, S3_TRIO_3D,		"Trio 3D"			},
61 	{ 0x8A13, S3_TRIO_3D_2X,	"Trio 3D/2X"		},
62 
63 	{ 0x8a20, S3_SAVAGE_3D,		"Savage3D"				},
64 	{ 0x8a21, S3_SAVAGE_3D,		"Savage3D-MV" 			},
65 	{ 0x8a22, S3_SAVAGE4,		"Savage4"				},
66 	{ 0x8a25, S3_PROSAVAGE,		"ProSavage PM133"		},
67 	{ 0x8a26, S3_PROSAVAGE,		"ProSavage KM133"		},
68 	{ 0x8c10, S3_SAVAGE_MX,		"Savage/MX-MV"			},
69 	{ 0x8c11, S3_SAVAGE_MX,		"Savage/MX"				},
70 	{ 0x8c12, S3_SAVAGE_MX,		"Savage/IX-MV"			},
71 	{ 0x8c13, S3_SAVAGE_MX,		"Savage/IX"				},
72 	{ 0x8c22, S3_SUPERSAVAGE,	"SuperSavage/MX 128"	},
73 	{ 0x8c24, S3_SUPERSAVAGE,	"SuperSavage/MX 64"		},
74 	{ 0x8c26, S3_SUPERSAVAGE,	"SuperSavage/MX 64C"	},
75 	{ 0x8c2a, S3_SUPERSAVAGE,	"SuperSavage/IX 128SDR"	},
76 	{ 0x8c2b, S3_SUPERSAVAGE,	"SuperSavage/IX 128DDR"	},
77 	{ 0x8c2c, S3_SUPERSAVAGE,	"SuperSavage/IX 64SDR"	},
78 	{ 0x8c2d, S3_SUPERSAVAGE,	"SuperSavage/IX 64DDR"	},
79 	{ 0x8c2e, S3_SUPERSAVAGE,	"SuperSavage/IXC 64SDR"	},
80 	{ 0x8c2f, S3_SUPERSAVAGE,	"SuperSavage/IXC 64DDR"	},
81 	{ 0x8d01, S3_TWISTER,		"Twister PN133"			},
82 	{ 0x8d02, S3_TWISTER,		"Twister KN133"			},
83 	{ 0x8d03, S3_PROSAVAGE_DDR,	"ProSavage DDR"			},
84 	{ 0x8d04, S3_PROSAVAGE_DDR,	"ProSavage DDR-K"		},
85 	{ 0x9102, S3_SAVAGE2000,	"Savage2000"			},
86 	{ 0,	  0,				NULL				}
87 };
88 
89 
90 #define VENDOR_ID	0x5333		// S3 vendor ID
91 
92 static struct {
93 	uint16			vendorID;
94 	const ChipInfo*	devices;
95 } SupportedDevices[] = {
96 	{ VENDOR_ID, S3_ChipTable },
97 	{ 0x0000, NULL }
98 };
99 
100 
101 struct DeviceInfo {
102 	uint32			openCount;		// count of how many times device has been opened
103 	int32			flags;
104 	area_id 		sharedArea;		// area shared between driver and all accelerants
105 	SharedInfo* 	si;				// pointer to the shared area
106 	vuint8*	 		regs;			// kernel's pointer to memory mapped registers
107 	const ChipInfo*	pChipInfo;		// info about the selected chip
108 	pci_info		pcii;			// copy of pci info for this device
109 	char			name[B_OS_NAME_LENGTH]; // name of device
110 };
111 
112 
113 struct DeviceData {
114 	uint32		count;				// number of devices actually found
115 	benaphore	kernel; 			// for serializing opens/closes
116 	char*		deviceNames[MAX_DEVICES + 1];	// device name pointer storage
117 	DeviceInfo	di[MAX_DEVICES];	// device specific stuff
118 };
119 
120 
121 // Prototypes for our private functions.
122 
123 static status_t open_hook (const char* name, uint32 flags, void** cookie);
124 static status_t close_hook (void* dev);
125 static status_t free_hook (void* dev);
126 static status_t read_hook (void* dev, off_t pos, void* buf, size_t* len);
127 static status_t write_hook (void* dev, off_t pos, const void* buf, size_t* len);
128 static status_t control_hook (void* dev, uint32 msg, void* buf, size_t len);
129 static status_t map_device(DeviceInfo* di);
130 static void unmap_device(DeviceInfo* di);
131 static void probe_devices(void);
132 static int32 s3_interrupt(void* data);
133 
134 static DeviceData*		pd;
135 static pci_module_info*	pci_bus;
136 static device_hooks		graphics_device_hooks =
137 {
138 	open_hook,
139 	close_hook,
140 	free_hook,
141 	control_hook,
142 	read_hook,
143 	write_hook,
144 	NULL,
145 	NULL,
146 	NULL,
147 	NULL
148 };
149 
150 
151 
152 // Functions for dealing with Vertical Blanking Interrupts.  Currently, I do
153 // not know the commands to handle these operations;  thus, these functions
154 // currently do nothing.
155 
156 static bool
157 InterruptIsVBI()
158 {
159 	// return true only if a vertical blanking interrupt has occured
160 	return false;
161 }
162 
163 
164 static void
165 ClearVBI()
166 {
167 }
168 
169 static void
170 EnableVBI()
171 {
172 }
173 
174 static void
175 DisableVBI()
176 {
177 }
178 
179 
180 
181 static const ChipInfo*
182 FindDeviceMatch(uint16 vendorID, uint16 deviceID)
183 {
184 	// Search the table of supported devices to find a chip/device that
185 	// matches the vendor ID and device ID passed by the caller.
186 	// Return pointer to the struct containing the chip info if match
187 	// is found; else return NULL.
188 
189 	int vendor = 0;
190 
191 	while (SupportedDevices[vendor].vendorID != 0) {	// end of table?
192 		if (SupportedDevices[vendor].vendorID == vendorID) {
193 			const ChipInfo* pDevice = SupportedDevices[vendor].devices;
194 
195 			while (pDevice->chipID != 0) {			// end of table?
196 				if (pDevice->chipID == deviceID)
197 					return pDevice;			// matching device/chip found
198 
199 				pDevice++;
200 			}
201 		}
202 		vendor++;
203 	}
204 
205 	return NULL;		// no match found
206 }
207 
208 
209 status_t
210 init_hardware(void)
211 {
212 	// Return B_OK if a device supported by this driver is found; otherwise,
213 	// return B_ERROR so the driver will be unloaded.
214 
215 	long pci_index = 0;
216 	pci_info pcii;
217 	const ChipInfo* pDevice = NULL;
218 
219 	if (get_module(B_PCI_MODULE_NAME, (module_info**)&pci_bus) != B_OK)
220 		return B_ERROR;		// unable to access PCI bus
221 
222 	// Check all pci devices for a device supported by this driver.
223 
224 	while ((*pci_bus->get_nth_pci_info)(pci_index, &pcii) == B_NO_ERROR) {
225 		pDevice = FindDeviceMatch(pcii.vendor_id, pcii.device_id);
226 		if (pDevice != NULL)
227 			break;
228 
229 		pci_index++;
230 	}
231 
232 	TRACE("init_hardware() - %s\n", pDevice == NULL ? "no supported devices" : "device supported");
233 
234 	put_module(B_PCI_MODULE_NAME);		// put away the module manager
235 	return (pDevice == NULL ? B_ERROR : B_OK);
236 }
237 
238 
239 status_t  init_driver(void)
240 {
241 	// Get handle for the pci bus.
242 
243 	if (get_module(B_PCI_MODULE_NAME, (module_info**)&pci_bus) != B_OK)
244 		return B_ERROR;
245 
246 	pd = (DeviceData*)calloc(1, sizeof(DeviceData));
247 	if (NULL == pd) {
248 		put_module(B_PCI_MODULE_NAME);
249 		return B_ERROR;
250 	}
251 
252 	INIT_BEN(pd->kernel);	// initialize the benaphore
253 	probe_devices();		// find all devices supported by this driver
254 
255 	return B_OK;
256 }
257 
258 
259 const char**
260 publish_devices(void)
261 {
262 	return (const char**)pd->deviceNames;	// return list of supported devices
263 }
264 
265 
266 device_hooks*
267 find_device(const char* name)
268 {
269 	int index = 0;
270 	while (pd->deviceNames[index]) {
271 		if (strcmp(name, pd->deviceNames[index]) == 0)
272 			return &graphics_device_hooks;
273 		index++;
274 	}
275 	return NULL;
276 }
277 
278 
279 void
280  uninit_driver(void)
281 {
282 	// Free the driver data.
283 
284 	DELETE_BEN(pd->kernel);
285 	free(pd);
286 	pd = NULL;
287 
288 	put_module(B_PCI_MODULE_NAME);	// put the pci module away
289 }
290 
291 
292 static status_t
293 map_device(DeviceInfo* di)
294 {
295 	char areaName[B_OS_NAME_LENGTH];
296 	SharedInfo* si = di->si;
297 	pci_info* pcii = &(di->pcii);
298 
299 	TRACE("enter map_device()\n");
300 
301 	// enable memory mapped IO and VGA I/O
302 
303 	uint32 tmpUlong = get_pci(PCI_command, 2);
304 	tmpUlong |= PCI_command_io | PCI_command_memory | PCI_command_master;
305 	set_pci(PCI_command, 2, tmpUlong);
306 
307 	const uint32 SavageMmioRegBaseOld	= 0x1000000;	// 16 MB
308 	const uint32 SavageMmioRegBaseNew	= 0x0000000;
309 	const uint32 SavageMmioRegSize		= 0x0080000;	// 512 KB reg area size
310 
311 	const uint32 VirgeMmioRegBase		= 0x1000000;	// 16 MB
312 	const uint32 VirgeMmioRegSize		= 0x10000;		// 64 KB reg area size
313 
314 	uint32 videoRamAddr = 0;
315 	uint32 videoRamSize = 0;
316 	uint32 regsBase = 0;
317 	uint32 regAreaSize = 0;
318 
319 	// Since we do not know at this point the actual size of the video
320 	// memory, set it to the largest value that the respective chipset
321 	// family can have.
322 
323 	if (S3_SAVAGE_FAMILY(di->pChipInfo->chipType)) {
324 		if (S3_SAVAGE_3D_SERIES(di->pChipInfo->chipType)) {
325 			// Savage 3D & Savage MX chips.
326 
327 			regsBase = di->pcii.u.h0.base_registers[0] + SavageMmioRegBaseOld;
328 			regAreaSize = SavageMmioRegSize;
329 
330 	 		videoRamAddr = di->pcii.u.h0.base_registers[0];
331 			videoRamSize = 16 * 1024 * 1024;	// 16 MB is max for 3D series
332 			si->videoMemPCI = (void *)(di->pcii.u.h0.base_registers_pci[0]);
333 		} else {
334 			// All other Savage chips.
335 
336 			regsBase = di->pcii.u.h0.base_registers[0] + SavageMmioRegBaseNew;
337 			regAreaSize = SavageMmioRegSize;
338 
339 			videoRamAddr = di->pcii.u.h0.base_registers[1];
340 			videoRamSize = di->pcii.u.h0.base_register_sizes[1];
341 			si->videoMemPCI = (void *)(di->pcii.u.h0.base_registers_pci[1]);
342 		}
343 	} else {
344 		// Trio/Virge chips.
345 
346 		regsBase = di->pcii.u.h0.base_registers[0] + VirgeMmioRegBase;
347 		regAreaSize = VirgeMmioRegSize;
348 
349  		videoRamAddr = di->pcii.u.h0.base_registers[0];
350 		videoRamSize = 8 * 1024 * 1024;	// 8 MB is max for Trio/Virge chips
351 		si->videoMemPCI = (void *)(di->pcii.u.h0.base_registers_pci[0]);
352 	}
353 
354 	// Map the MMIO register area.
355 
356 	sprintf(areaName, DEVICE_FORMAT " regs",
357 		di->pcii.vendor_id, di->pcii.device_id,
358 		di->pcii.bus, di->pcii.device, di->pcii.function);
359 
360 	si->regsArea = map_physical_memory(areaName, (void*)regsBase, regAreaSize,
361 		B_ANY_KERNEL_ADDRESS,
362 		0,		// neither read nor write, to hide it from user space apps
363 		(void**)(&(di->regs)));
364 
365 	if (si->regsArea < 0)
366 		return si->regsArea;	// return error code
367 
368 	// Map the video memory.
369 
370 	sprintf(areaName, DEVICE_FORMAT " framebuffer",
371 		di->pcii.vendor_id, di->pcii.device_id,
372 		di->pcii.bus, di->pcii.device, di->pcii.function);
373 
374 	si->videoMemArea = map_physical_memory(
375 		areaName,
376 		(void*)videoRamAddr,
377 		videoRamSize,
378 		B_ANY_KERNEL_BLOCK_ADDRESS | B_MTR_WC,
379 		B_READ_AREA + B_WRITE_AREA,
380 		&(si->videoMemAddr));
381 
382 	if (si->videoMemArea < 0) {
383 		// Try to map this time without write combining.
384 		si->videoMemArea = map_physical_memory(
385 			areaName,
386 			(void*)videoRamAddr,
387 			videoRamSize,
388 			B_ANY_KERNEL_BLOCK_ADDRESS,
389 			B_READ_AREA + B_WRITE_AREA,
390 			&(si->videoMemAddr));
391 	}
392 
393 	TRACE("Video memory, area: %ld,  addr: 0x%lX, size: %ld\n", si->videoMemArea,
394 		(uint32)(si->videoMemAddr), videoRamSize);
395 
396 	// If there was an error, delete other areas.
397 	if (si->videoMemArea < 0) {
398 		delete_area(si->regsArea);
399 		si->regsArea = -1;
400 	}
401 
402 	TRACE("leave map_device(); result: %ld\n", si->videoMemArea);
403 	return si->videoMemArea;
404 }
405 
406 
407 static void
408 unmap_device(DeviceInfo* di)
409 {
410 	SharedInfo* si = di->si;
411 	pci_info* pcii = &(di->pcii);
412 
413 	TRACE("enter unmap_device()\n");
414 
415 	// Disable memory mapped IO.
416 	uint32 tmpUlong = get_pci(PCI_command, 2);
417 	tmpUlong &= ~(PCI_command_io | PCI_command_memory);
418 	set_pci(PCI_command, 2, tmpUlong);
419 
420 	if (si->regsArea >= 0)
421 		delete_area(si->regsArea);
422 	if (si->videoMemArea >= 0)
423 		delete_area(si->videoMemArea);
424 
425 	si->regsArea = si->videoMemArea = -1;
426 	si->videoMemAddr = NULL;
427 	di->regs = NULL;
428 
429 	TRACE("exit unmap_device()\n");
430 }
431 
432 
433 static void
434 probe_devices(void)
435 {
436 	uint32 pci_index = 0;
437 	uint32 count = 0;
438 	DeviceInfo* di = pd->di;
439 	const ChipInfo* pDevice;
440 
441 	while ((count < MAX_DEVICES)
442 			&& ((*pci_bus->get_nth_pci_info)(pci_index, &(di->pcii)) == B_NO_ERROR)) {
443 
444 		pDevice = FindDeviceMatch(di->pcii.vendor_id, di->pcii.device_id);
445 		if (pDevice != NULL) {
446 			// Compose device name.
447 			sprintf(di->name, "graphics/" DEVICE_FORMAT,
448 					  di->pcii.vendor_id, di->pcii.device_id,
449 					  di->pcii.bus, di->pcii.device, di->pcii.function);
450 			TRACE("probe_devices() match found; name: %s\n", di->name);
451 
452 			pd->deviceNames[count] = di->name;
453 			di->openCount = 0;			// mark driver as available for R/W open
454 			di->sharedArea = -1;		// indicate shared area not yet created
455 			di->si = NULL;
456 			di->pChipInfo = pDevice;
457 			di++;
458 			count++;
459 		}
460 
461 		pci_index++;
462 	}
463 
464 	pd->count = count;
465 	pd->deviceNames[pd->count] = NULL;	// terminate list with null pointer
466 
467 	TRACE("probe_devices() %ld supported devices\n", pd->count);
468 }
469 
470 
471 static uint32
472 thread_interrupt_work(DeviceInfo* di)
473 {
474 	SharedInfo* si = di->si;
475 	uint32 handled = B_HANDLED_INTERRUPT;
476 
477 	// Release vertical blanking semaphore.
478 	if (si->vertBlankSem >= 0) {
479 		int32 blocked;
480 		if ((get_sem_count(si->vertBlankSem, &blocked) == B_OK) && (blocked < 0)) {
481 			release_sem_etc(si->vertBlankSem, -blocked, B_DO_NOT_RESCHEDULE);
482 			handled = B_INVOKE_SCHEDULER;
483 		}
484 	}
485 	return handled;
486 }
487 
488 
489 static int32
490 s3_interrupt(void* data)
491 {
492 	int32 handled = B_UNHANDLED_INTERRUPT;
493 	DeviceInfo* di = (DeviceInfo*)data;
494 	int32* flags = &(di->flags);
495 
496 	// Is someone already handling an interrupt for this device?
497 	if (atomic_or(flags, SKD_HANDLER_INSTALLED) & SKD_HANDLER_INSTALLED)
498 		return B_UNHANDLED_INTERRUPT;
499 
500 	if (InterruptIsVBI()) {	// was interrupt a VBI?
501 		ClearVBI();			// clear interrupt
502 
503 		handled = thread_interrupt_work(di);	// release semaphore
504 	}
505 
506 	atomic_and(flags, ~SKD_HANDLER_INSTALLED);	// note we're not in handler anymore
507 
508 	return handled;
509 }
510 
511 
512 //	#pragma mark - Device Hooks
513 
514 
515 static status_t
516 open_hook(const char* name, uint32 flags, void** cookie)
517 {
518 	int32 index = 0;
519 	SharedInfo* si;
520 	thread_id	thid;
521 	thread_info thinfo;
522 	status_t	result = B_OK;
523 	char sharedName[B_OS_NAME_LENGTH];
524 
525 	(void)flags;		// avoid compiler warning for unused arg
526 
527 	TRACE("open_hook() - name: %s, cookie: 0x%08lx)\n", name, (uint32)cookie);
528 
529 	// Find the device name in the list of devices.
530 	while (pd->deviceNames[index] && (strcmp(name, pd->deviceNames[index]) != 0))
531 		index++;
532 
533 	DeviceInfo* di = &(pd->di[index]);
534 
535 	// Make sure no one else has write access to the common data.
536 	AQUIRE_BEN(pd->kernel);
537 
538 	if (di->openCount > 0)
539 		goto mark_as_open;
540 
541 	// Create the area for shared info with NO user-space read or write permissions,
542 	// to prevent accidental damage.
543 
544 	sprintf(sharedName, DEVICE_FORMAT " shared",
545 		di->pcii.vendor_id, di->pcii.device_id,
546 		di->pcii.bus, di->pcii.device, di->pcii.function);
547 
548 	di->sharedArea = create_area(sharedName, (void**) &(di->si), B_ANY_KERNEL_ADDRESS,
549 		((sizeof(SharedInfo) + (B_PAGE_SIZE - 1)) & ~(B_PAGE_SIZE - 1)),
550 		B_FULL_LOCK, 0);
551 	if (di->sharedArea < 0) {
552 		result = di->sharedArea;	// return error
553 		goto done;
554 	}
555 
556 	si = di->si;
557 
558 	si->vendorID = di->pcii.vendor_id;
559 	si->deviceID = di->pcii.device_id;
560 	si->revision = di->pcii.revision;
561 	si->chipType = di->pChipInfo->chipType;
562 	strcpy(si->chipName, di->pChipInfo->chipName);
563 
564 	// Trio64 and Trio64V+ chips have the same ID but different revision numbers.
565 	// Since the Trio64V+ supports MMIO, better performance can be obtained
566 	// from it if it is distinguished from the Trio64.
567 
568 	if (si->chipType == S3_TRIO64 && si->revision & 0x40) {
569 		si->chipType = S3_TRIO64_VP;
570 		strcpy(si->chipName, "Trio64 V+");
571 	}
572 
573 
574 	result = map_device(di);
575 	if (result < 0)
576 		goto free_shared;
577 
578 	result = B_OK;
579 
580 	DisableVBI();					// disable & clear any pending interrupts
581 	si->bInterruptAssigned = false;	// indicate interrupt not assigned yet
582 
583 	// Create a semaphore for vertical blank management.
584 	si->vertBlankSem = create_sem(0, di->name);
585 	if (si->vertBlankSem < 0)
586 		goto mark_as_open;
587 
588 	// Change the owner of the semaphores to the opener's team.
589 	// This is required because apps can't aquire kernel semaphores.
590 	thid = find_thread(NULL);
591 	get_thread_info(thid, &thinfo);
592 	set_sem_owner(si->vertBlankSem, thinfo.team);
593 
594 	// If there is a valid interrupt assigned, set up interrupts.
595 
596 	if ((di->pcii.u.h0.interrupt_pin == 0x00) ||
597 		(di->pcii.u.h0.interrupt_line == 0xff) ||	// no IRQ assigned
598 		(di->pcii.u.h0.interrupt_line <= 0x02)) {	// system IRQ assigned
599 		// Interrupt does not exist; thus delete semaphore as it won't be used.
600 		delete_sem(si->vertBlankSem);
601 		si->vertBlankSem = -1;
602 	} else {
603 		result = install_io_interrupt_handler(di->pcii.u.h0.interrupt_line, s3_interrupt, (void*)di, 0);
604 		if (result != B_OK) {
605 			// Delete semaphore as it won't be used.
606 			delete_sem(si->vertBlankSem);
607 			si->vertBlankSem = -1;
608 		} else {
609 			// Inform accelerant(s) we can use interrupt related functions.
610 			si->bInterruptAssigned = true;
611 		}
612 	}
613 
614 mark_as_open:
615 	di->openCount++;	// mark device open
616 	*cookie = di;		// send cookie to opener
617 	goto done;
618 
619 free_shared:
620 	delete_area(di->sharedArea);
621 	di->sharedArea = -1;
622 	di->si = NULL;
623 
624 done:
625 	// End of critical section.
626 	RELEASE_BEN(pd->kernel);
627 
628 	TRACE("open_hook() returning 0x%08lx\n", result);
629 	return result;
630 }
631 
632 
633 static status_t
634 read_hook(void* dev, off_t pos, void* buf, size_t* len)
635 {
636 	// Following 3 lines of code are here to eliminate "unused parameter" warnings.
637 	(void)dev;
638 	(void)pos;
639 	(void)buf;
640 
641 	*len = 0;
642 	return B_NOT_ALLOWED;
643 }
644 
645 
646 static status_t
647 write_hook(void* dev, off_t pos, const void* buf, size_t* len)
648 {
649 	// Following 3 lines of code are here to eliminate "unused parameter" warnings.
650 	(void)dev;
651 	(void)pos;
652 	(void)buf;
653 
654 	*len = 0;
655 	return B_NOT_ALLOWED;
656 }
657 
658 
659 static status_t
660 close_hook(void* dev)
661 {
662 	(void)dev;		// avoid compiler warning for unused arg
663 
664 	TRACE("close_hook()\n");
665 	return B_NO_ERROR;
666 }
667 
668 
669 static status_t
670 free_hook(void* dev)
671 {
672 	DeviceInfo* di = (DeviceInfo*)dev;
673 	SharedInfo* si = di->si;
674 
675 	TRACE("enter free_hook()\n");
676 
677 	AQUIRE_BEN(pd->kernel);		// lock driver
678 
679 	// If opened multiple times, decrement the open count and exit.
680 	if (di->openCount > 1)
681 		goto unlock_and_exit;
682 
683 	DisableVBI();		// disable & clear any pending interrupts
684 
685 	if (si->bInterruptAssigned) {
686 		remove_io_interrupt_handler(di->pcii.u.h0.interrupt_line, s3_interrupt, di);
687 	}
688 
689 	// Delete the semaphores, ignoring any errors because the owning team may have died.
690 	if (si->vertBlankSem >= 0)
691 		delete_sem(si->vertBlankSem);
692 	si->vertBlankSem = -1;
693 
694 	unmap_device(di);	// free regs and frame buffer areas
695 
696 	delete_area(di->sharedArea);
697 	di->sharedArea = -1;
698 	di->si = NULL;
699 
700 unlock_and_exit:
701 	if (di->openCount > 0)
702 		di->openCount--;		// mark device available
703 
704 	RELEASE_BEN(pd->kernel);	// unlock driver
705 
706 	TRACE("exit free_hook()\n");
707 	return B_OK;
708 }
709 
710 
711 static status_t
712 control_hook(void* dev, uint32 msg, void* buf, size_t len)
713 {
714 	DeviceInfo* di = (DeviceInfo*)dev;
715 
716 	(void)len;		// avoid compiler warning for unused arg
717 
718 	switch (msg) {
719 		case B_GET_ACCELERANT_SIGNATURE:
720 			strcpy((char*)buf, "s3.accelerant");
721 			return B_OK;
722 
723 		case S3_DEVICE_NAME:
724 			strncpy((char*)buf, di->name, B_OS_NAME_LENGTH);
725 			((char*)buf)[B_OS_NAME_LENGTH -1] = '\0';
726 			return B_OK;
727 
728 		case S3_GET_PRIVATE_DATA:
729 		{
730 			S3GetPrivateData* gpd = (S3GetPrivateData*)buf;
731 			if (gpd->magic == S3_PRIVATE_DATA_MAGIC) {
732 				gpd->sharedInfoArea = di->sharedArea;
733 				return B_OK;
734 			}
735 			break;
736 		}
737 
738 		case S3_RUN_INTERRUPTS:
739 		{
740 			S3SetBoolState* ri = (S3SetBoolState*)buf;
741 			if (ri->magic == S3_PRIVATE_DATA_MAGIC) {
742 				if (ri->bEnable)
743 					EnableVBI();
744 				else
745 					DisableVBI();
746 			}
747 			return B_OK;
748 		}
749 
750 		case S3_GET_PIO:
751 		{
752 			S3GetSetPIO* gsp = (S3GetSetPIO*)buf;
753 			if (gsp->magic == S3_PRIVATE_DATA_MAGIC) {
754 				switch (gsp->size) {
755 					case 1:
756 						gsp->value = pci_bus->read_io_8(gsp->offset);
757 						break;
758 					case 2:
759 						gsp->value = pci_bus->read_io_16(gsp->offset);
760 						break;
761 					case 4:
762 						gsp->value = pci_bus->read_io_32(gsp->offset);
763 						break;
764 					default:
765 						TRACE("control_hook() S3_GET_PIO invalid size: %ld\n", gsp->size);
766 						return B_ERROR;
767 				}
768 				return B_OK;
769 			}
770 			break;
771 		}
772 
773 		case S3_SET_PIO:
774 		{
775 			S3GetSetPIO* gsp = (S3GetSetPIO*)buf;
776 			if (gsp->magic == S3_PRIVATE_DATA_MAGIC) {
777 				switch (gsp->size) {
778 					case 1:
779 						pci_bus->write_io_8(gsp->offset, gsp->value);
780 						break;
781 					case 2:
782 						pci_bus->write_io_16(gsp->offset, gsp->value);
783 						break;
784 					case 4:
785 						pci_bus->write_io_32(gsp->offset, gsp->value);
786 						break;
787 					default:
788 						TRACE("control_hook() S3_SET_PIO invalid size: %ld\n", gsp->size);
789 						return B_ERROR;
790 				}
791 				return B_OK;
792 			}
793 			break;
794 		}
795 	}
796 
797 	return B_DEV_INVALID_IOCTL;
798 }
799 
800