xref: /haiku/src/add-ons/kernel/drivers/graphics/neomagic/driver.c (revision 9a6a20d4689307142a7ed26a1437ba47e244e73f)
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 	Mark Watson;
7 	Apsed;
8 	Rudolf Cornelissen 5/2002-4/2006.
9 */
10 
11 /* standard kernel driver stuff */
12 #include <KernelExport.h>
13 #include <ISA.h>
14 #include <PCI.h>
15 #include <OS.h>
16 #include <directories.h>
17 #include <driver_settings.h>
18 #include <malloc.h>
19 #include <stdlib.h> // for strtoXX
20 
21 /* this is for the standardized portion of the driver API */
22 /* currently only one operation is defined: B_GET_ACCELERANT_SIGNATURE */
23 #include <graphic_driver.h>
24 
25 /* this is for sprintf() */
26 #include <stdio.h>
27 
28 /* this is for string compares */
29 #include <string.h>
30 
31 /* The private interface between the accelerant and the kernel driver. */
32 #include "DriverInterface.h"
33 #include "nm_macros.h"
34 
35 #define get_pci(o, s) (*pci_bus->read_pci_config)(pcii->bus, pcii->device, pcii->function, (o), (s))
36 #define set_pci(o, s, v) (*pci_bus->write_pci_config)(pcii->bus, pcii->device, pcii->function, (o), (s), (v))
37 #define KISAGRPHW(A,B)(isa_bus->write_io_16(NMISA16_GRPHIND, ((NMGRPHX_##A) | ((B) << 8))))
38 #define KISAGRPHR(A)  (isa_bus->write_io_8(NMISA8_GRPHIND, (NMGRPHX_##A)), isa_bus->read_io_8(NMISA8_GRPHDAT))
39 #define KISASEQW(A,B)(isa_bus->write_io_16(NMISA16_SEQIND, ((NMSEQX_##A) | ((B) << 8))))
40 
41 #define MAX_DEVICES	  8
42 
43 #define DEVICE_FORMAT "%04x_%04x_%02x%02x%02x" // apsed
44 
45 /* Tell the kernel what revision of the driver API we support */
46 int32	api_version = B_CUR_DRIVER_API_VERSION; // apsed, was 2, is 2 in R5
47 
48 /* these structures are private to the kernel driver */
49 typedef struct device_info device_info;
50 
51 typedef struct {
52 	timer		te;				/* timer entry for add_timer() */
53 	device_info	*di;			/* pointer to the owning device */
54 	bigtime_t	when_target;	/* when we're supposed to wake up */
55 } timer_info;
56 
57 struct device_info {
58 	uint32		is_open;			/* a count of how many times the devices has been opened */
59 	area_id		shared_area;		/* the area shared between the driver and all of the accelerants */
60 	shared_info	*si;				/* a pointer to the shared area, for convenience */
61 	vuint32		*regs, *regs2;		/* kernel's pointer to memory mapped registers */
62 	pci_info	pcii;					/* a convenience copy of the pci info for this device */
63 	char		name[B_OS_NAME_LENGTH];	/* where we keep the name of the device for publishing and comparing */
64 };
65 
66 typedef struct {
67 	uint32		count;				/* number of devices actually found */
68 	benaphore	kernel;				/* for serializing opens/closes */
69 	char		*device_names[MAX_DEVICES+1];	/* device name pointer storage */
70 	device_info	di[MAX_DEVICES];	/* device specific stuff */
71 } DeviceData;
72 
73 /* prototypes for our private functions */
74 static status_t open_hook (const char* name, uint32 flags, void** cookie);
75 static status_t close_hook (void* dev);
76 static status_t free_hook (void* dev);
77 static status_t read_hook (void* dev, off_t pos, void* buf, size_t* len);
78 static status_t write_hook (void* dev, off_t pos, const void* buf, size_t* len);
79 static status_t control_hook (void* dev, uint32 msg, void *buf, size_t len);
80 static status_t map_device(device_info *di);
81 static void unmap_device(device_info *di);
82 static void probe_devices(void);
83 static int32 nm_interrupt(void *data);
84 void drv_program_bes_ISA(nm_bes_data *bes);
85 
86 static DeviceData		*pd;
87 static pci_module_info	*pci_bus;
88 static isa_module_info	*isa_bus;
89 
90 static device_hooks graphics_device_hooks = {
91 	open_hook,
92 	close_hook,
93 	free_hook,
94 	control_hook,
95 	read_hook,
96 	write_hook,
97 	NULL,
98 	NULL,
99 	NULL,
100 	NULL
101 };
102 
103 #define VENDOR_ID			0x10c8	/* Neomagic inc. */
104 
105 static uint16 nm_device_list[] = {
106 	0x0001,	/* MagicGraph 128 (NM2070) */
107 	0x0002,	/* MagicGraph 128V (NM2090)	0x42 */
108 	0x0003,	/* MagicGraph 128ZV (NM2093) 0x43 */
109 	0x0083, /* MagicGraph 128ZV+ (NM2097) */
110 	0x0004, /* MagicGraph 128XD (NM2160) 0x44 */
111 	0x0005,	/* MagicMedia 256AV (NM2200) 0x45 */
112 	0x0025,	/* MagicMedia 256AV+ (NM2230) */
113 	0x0006,	/* MagicMedia 256ZX (NM2360) */
114 	0x0016,	/* MagicMedia 256XL+ (NM2380) */
115 	0
116 };
117 
118 static struct {
119 	uint16	vendor;
120 	uint16	*devices;
121 } SupportedDevices[] = {
122 	{VENDOR_ID, nm_device_list},
123 	{0x0000, NULL}
124 };
125 
126 static settings current_settings = { // see comments in nm.settings
127 	// for driver
128 	DRIVER_PREFIX ".accelerant",
129 	false,      // dumprom
130 	// for accelerant
131 	0x00000000, // logmask
132 	0,          // memory
133 	true,      // usebios
134 	true,      // hardcursor
135 };
136 
137 static void dumprom (void *rom, uint32 size)
138 {
139 	int fd;
140 	uint32 cnt;
141 
142 	fd = open (kUserDirectory "/" DRIVER_PREFIX ".rom",
143 		O_WRONLY | O_CREAT, 0666);
144 	if (fd < 0) return;
145 
146 	/* apparantly max. 32kb may be written at once;
147 	 * the ROM size is a multiple of that anyway. */
148 	for (cnt = 0; (cnt < size); cnt += 32768)
149 		write (fd, ((void *)(((uint8 *)rom) + cnt)), 32768);
150 	close (fd);
151 }
152 
153 /*return 1, is interrupt has occured*/
154 static int caused_vbi(vuint32 * regs, vuint32 * regs2)
155 {
156 //	return (ACCR(STATUS)&0x20);
157 return 0;
158 }
159 
160 /*clear the interrupt*/
161 static void clear_vbi(vuint32 * regs, vuint32 * regs2)
162 {
163 //	ACCW(ICLEAR,0x20);
164 }
165 
166 static void enable_vbi(vuint32 * regs, vuint32 * regs2)
167 {
168 //	ACCW(IEN,ACCR(IEN)|0x20);
169 }
170 
171 static void disable_vbi(vuint32 * regs, vuint32 * regs2)
172 {
173 //	ACCW(IEN,(ACCR(IEN)&~0x20));
174 //	ACCW(ICLEAR,0x20);
175 }
176 
177 
178 /*
179 	init_hardware() - Returns B_OK if one is
180 	found, otherwise returns B_ERROR so the driver will be unloaded.
181 */
182 status_t
183 init_hardware(void) {
184 	long		pci_index = 0;
185 	pci_info	pcii;
186 	bool		found_one = FALSE;
187 
188 	/* choke if we can't find the PCI bus */
189 	if (get_module(B_PCI_MODULE_NAME, (module_info **)&pci_bus) != B_OK)
190 		return B_ERROR;
191 
192 	/* choke if we can't find the ISA bus */
193 	if (get_module(B_ISA_MODULE_NAME, (module_info **)&isa_bus) != B_OK)
194 	{
195 		put_module(B_PCI_MODULE_NAME);
196 		return B_ERROR;
197 	}
198 
199 	/* while there are more pci devices */
200 	while ((*pci_bus->get_nth_pci_info)(pci_index, &pcii) == B_NO_ERROR) {
201 		int vendor = 0;
202 
203 		/* if we match a supported vendor */
204 		while (SupportedDevices[vendor].vendor) {
205 			if (SupportedDevices[vendor].vendor == pcii.vendor_id) {
206 				uint16 *devices = SupportedDevices[vendor].devices;
207 				/* while there are more supported devices */
208 				while (*devices) {
209 					/* if we match a supported device */
210 					if (*devices == pcii.device_id ) {
211 
212 						found_one = TRUE;
213 						goto done;
214 					}
215 					/* next supported device */
216 					devices++;
217 				}
218 			}
219 			vendor++;
220 		}
221 		/* next pci_info struct, please */
222 		pci_index++;
223 	}
224 
225 done:
226 	/* put away the module managers */
227 	put_module(B_PCI_MODULE_NAME);
228 	put_module(B_ISA_MODULE_NAME);
229 
230 	return (found_one ? B_OK : B_ERROR);
231 }
232 
233 status_t
234 init_driver(void) {
235 	void *settings_handle;
236 
237 	// get driver/accelerant settings, apsed
238 	settings_handle  = load_driver_settings (DRIVER_PREFIX ".settings");
239 	if (settings_handle != NULL) {
240 		const char *item;
241 		char       *end;
242 		uint32      value;
243 
244 		// for driver
245 		item = get_driver_parameter (settings_handle, "accelerant", "", "");
246 		if ((strlen (item) > 0) && (strlen (item) < sizeof (current_settings.accelerant) - 1)) {
247 			strcpy (current_settings.accelerant, item);
248 		}
249 		current_settings.dumprom = get_driver_boolean_parameter (settings_handle, "dumprom", false, false);
250 
251 		// for accelerant
252 		item = get_driver_parameter (settings_handle, "logmask", "0x00000000", "0x00000000");
253 		value = strtoul (item, &end, 0);
254 		if (*end == '\0') current_settings.logmask = value;
255 
256 		item = get_driver_parameter (settings_handle, "memory", "0", "0");
257 		value = strtoul (item, &end, 0);
258 		if (*end == '\0') current_settings.memory = value;
259 
260 		current_settings.hardcursor = get_driver_boolean_parameter (settings_handle, "hardcursor", false, false);
261 		current_settings.usebios = get_driver_boolean_parameter (settings_handle, "usebios", false, false);
262 
263 		unload_driver_settings (settings_handle);
264 	}
265 
266 	/* get a handle for the pci bus */
267 	if (get_module(B_PCI_MODULE_NAME, (module_info **)&pci_bus) != B_OK)
268 		return B_ERROR;
269 
270 	/* get a handle for the isa bus */
271 	if (get_module(B_ISA_MODULE_NAME, (module_info **)&isa_bus) != B_OK) {
272 		put_module(B_PCI_MODULE_NAME);
273 		return B_ERROR;
274 	}
275 
276 	/* driver private data */
277 	pd = (DeviceData *)calloc(1, sizeof(DeviceData));
278 	if (!pd) {
279 		put_module(B_PCI_MODULE_NAME);
280 		put_module(B_ISA_MODULE_NAME);
281 		return B_ERROR;
282 	}
283 	/* initialize the benaphore */
284 	INIT_BEN(pd->kernel);
285 	/* find all of our supported devices */
286 	probe_devices();
287 	return B_OK;
288 }
289 
290 const char **
291 publish_devices(void) {
292 	/* return the list of supported devices */
293 	return (const char **)pd->device_names;
294 }
295 
296 device_hooks *
297 find_device(const char *name) {
298 	int index = 0;
299 	while (pd->device_names[index]) {
300 		if (strcmp(name, pd->device_names[index]) == 0)
301 			return &graphics_device_hooks;
302 		index++;
303 	}
304 	return NULL;
305 
306 }
307 
308 void uninit_driver(void) {
309 
310 	/* free the driver data */
311 	DELETE_BEN(pd->kernel);
312 	free(pd);
313 	pd = NULL;
314 
315 	/* put the pci modules away */
316 	put_module(B_PCI_MODULE_NAME);
317 	put_module(B_ISA_MODULE_NAME);
318 }
319 
320 static status_t map_device(device_info *di)
321 {
322 	char buffer[B_OS_NAME_LENGTH]; /*memory for device name*/
323 	shared_info *si = di->si;
324 	uint32	tmpUlong;
325 	pci_info *pcii = &(di->pcii);
326 	system_info sysinfo;
327 
328 	/* variables for making copy of ROM */
329 	uint8* rom_temp;
330 	area_id rom_area;
331 
332 	int frame_buffer = 0;
333 	/* only NM2097 and later cards have two register area's:
334 	 * older cards have their PCI registers in the frame_buffer space */
335 	int registers = 1;
336 	int registers2 = 2;
337 
338 	/* enable memory mapped IO, disable VGA I/O - this is standard*/
339 	tmpUlong = get_pci(PCI_command, 4);
340 	/* make sure ISA access is enabled: all Neomagic cards still partly rely on this */
341 	tmpUlong |= PCI_command_io;
342 	/* make sure PCI access is enabled */
343 	tmpUlong |= PCI_command_memory;
344 	tmpUlong |= PCI_command_master;
345 	set_pci(PCI_command, 4, tmpUlong);
346 
347  	/*work out which version of BeOS is running*/
348  	get_system_info(&sysinfo);
349  	if (0)//sysinfo.kernel_build_date[0]=='J')/*FIXME - better ID version*/
350  	{
351  		si->use_clone_bugfix = 1;
352  	}
353  	else
354  	{
355  		si->use_clone_bugfix = 0;
356  	}
357 
358 	/* map card's register area's */
359 	switch (di->pcii.device_id)
360 	{
361 	case 0x0001:
362 	case 0x0002:
363 	case 0x0003:
364 		/* NM2070, NM2090 and NM2093 have no seperate register area's,
365 		 * so do nothing here except notify accelerant. */
366 		si->regs_in_fb = true;
367 		break;
368 	default:
369 		/* we map the registers seperately from the framebuffer */
370 		si->regs_in_fb = false;
371 		/* work out a name for the register mapping */
372 		sprintf(buffer, DEVICE_FORMAT " regs",
373 			di->pcii.vendor_id, di->pcii.device_id,
374 			di->pcii.bus, di->pcii.device, di->pcii.function);
375 
376 		/* get a virtual memory address for the registers*/
377 		si->regs_area = map_physical_memory(
378 			buffer,
379 			di->pcii.u.h0.base_registers[registers],
380 			di->pcii.u.h0.base_register_sizes[registers],
381 			B_ANY_KERNEL_ADDRESS,
382 			B_CLONEABLE_AREA | (si->use_clone_bugfix ? B_READ_AREA|B_WRITE_AREA : 0),
383 			(void **)&(di->regs));
384  		si->clone_bugfix_regs = (uint32 *) di->regs;
385 
386 		/* if mapping registers to vmem failed then pass on error */
387 		if (si->regs_area < 0) return si->regs_area;
388 
389 		/* work out a name for the register mapping */
390 		sprintf(buffer, DEVICE_FORMAT " regs2",
391 			di->pcii.vendor_id, di->pcii.device_id,
392 			di->pcii.bus, di->pcii.device, di->pcii.function);
393 
394 		si->regs2_area = map_physical_memory(
395 			buffer,
396 			di->pcii.u.h0.base_registers[registers2],
397 			di->pcii.u.h0.base_register_sizes[registers2],
398 			B_ANY_KERNEL_ADDRESS,
399 			B_CLONEABLE_AREA | (si->use_clone_bugfix ? B_READ_AREA|B_WRITE_AREA : 0),
400 			(void **)&(di->regs2));
401 	 	si->clone_bugfix_regs2 = (uint32 *) di->regs2;
402 
403 		/* if mapping registers to vmem failed then pass on error */
404 		if (si->regs2_area < 0)
405 		{
406 			delete_area(si->regs_area);
407 			si->regs_area = -1;
408 
409 			return si->regs2_area;
410 		}
411 		break;
412 	}
413 
414 	/* work out a name for the ROM mapping*/
415 	sprintf(buffer, DEVICE_FORMAT " rom",
416 		di->pcii.vendor_id, di->pcii.device_id,
417 		di->pcii.bus, di->pcii.device, di->pcii.function);
418 
419 	/* get ROM memory mapped base adress - this is defined in the PCI standard */
420 	tmpUlong = get_pci(PCI_rom_base, 4);
421 	if (tmpUlong)
422 	{
423 		/* ROM was assigned an adress, so enable ROM decoding - see PCI standard */
424 		tmpUlong |= 0x00000001;
425 		set_pci(PCI_rom_base, 4, tmpUlong);
426 
427 		rom_area = map_physical_memory(
428 			buffer,
429 			di->pcii.u.h0.rom_base_pci,
430 			di->pcii.u.h0.rom_size,
431 			B_ANY_KERNEL_ADDRESS,
432 			B_READ_AREA,
433 			(void **)&(rom_temp)
434 		);
435 
436 		/* check if we got the BIOS signature (might fail on laptops..) */
437 		if (rom_temp[0]!=0x55 || rom_temp[1]!=0xaa)
438 		{
439 			/* apparantly no ROM is mapped here */
440 			delete_area(rom_area);
441 			rom_area = -1;
442 			/* force using ISA legacy map as fall-back */
443 			tmpUlong = 0x00000000;
444 		}
445 	}
446 
447 	if (!tmpUlong)
448 	{
449 		/* ROM was not assigned an adress, fetch it from ISA legacy memory map! */
450 		rom_area = map_physical_memory(
451 			buffer,
452 			0x000c0000,
453 			65536,
454 			B_ANY_KERNEL_ADDRESS,
455 			B_READ_AREA,
456 			(void **)&(rom_temp)
457 		);
458 	}
459 
460 	/* if mapping ROM to vmem failed then clean up and pass on error */
461 	if (rom_area < 0) {
462 		if (si->regs_area >= 0)
463 		{
464 			delete_area(si->regs_area);
465 			si->regs_area = -1;
466 		}
467 		if (si->regs2_area >= 0)
468 		{
469 			delete_area(si->regs2_area);
470 			si->regs2_area = -1;
471 		}
472 		return rom_area;
473 	}
474 
475 	/* dump ROM to file if selected in nm.settings
476 	 * (ROM should always fit in 64Kb) */
477 	if (current_settings.dumprom) dumprom (rom_temp, 65536);
478 	/* make a copy of ROM for future reference */
479 	memcpy (si->rom_mirror, rom_temp, 65536);
480 
481 	/* disable ROM decoding - this is defined in the PCI standard, and delete the area */
482 	tmpUlong = get_pci(PCI_rom_base, 4);
483 	tmpUlong &= 0xfffffffe;
484 	set_pci(PCI_rom_base, 4, tmpUlong);
485 	delete_area(rom_area);
486 
487 	/* work out a name for the framebuffer mapping*/
488 	sprintf(buffer, DEVICE_FORMAT " framebuffer",
489 		di->pcii.vendor_id, di->pcii.device_id,
490 		di->pcii.bus, di->pcii.device, di->pcii.function);
491 
492 	/* map the framebuffer into vmem, using Write Combining*/
493 	si->fb_area = map_physical_memory(
494 		buffer,
495 		di->pcii.u.h0.base_registers[frame_buffer],
496 		di->pcii.u.h0.base_register_sizes[frame_buffer],
497 		B_ANY_KERNEL_BLOCK_ADDRESS | B_WRITE_COMBINING_MEMORY,
498 		B_READ_AREA | B_WRITE_AREA | B_CLONEABLE_AREA,
499 		&(si->framebuffer));
500 
501 	/*if failed with write combining try again without*/
502 	if (si->fb_area < 0) {
503 		si->fb_area = map_physical_memory(
504 			buffer,
505 			di->pcii.u.h0.base_registers[frame_buffer],
506 			di->pcii.u.h0.base_register_sizes[frame_buffer],
507 			B_ANY_KERNEL_BLOCK_ADDRESS,
508 			B_READ_AREA | B_WRITE_AREA | B_CLONEABLE_AREA,
509 			&(si->framebuffer));
510 	}
511 
512 	/* if there was an error, delete our other areas and pass on error*/
513 	if (si->fb_area < 0)
514 	{
515 		if (si->regs_area >= 0)
516 		{
517 			delete_area(si->regs_area);
518 			si->regs_area = -1;
519 		}
520 		if (si->regs2_area >= 0)
521 		{
522 			delete_area(si->regs2_area);
523 			si->regs2_area = -1;
524 		}
525 		return si->fb_area;
526 	}
527 
528 	/* older cards have their registers in the framebuffer area */
529 	switch (di->pcii.device_id)
530 	{
531 	case 0x0001:
532 		/* NM2070 cards */
533 		di->regs = (uint32 *)((uint8 *)si->framebuffer + 0x100000);
534  		si->clone_bugfix_regs = (uint32 *) di->regs;
535 		break;
536 	case 0x0002:
537 	case 0x0003:
538 		/* NM2090 and NM2093 cards */
539 		di->regs = (uint32 *)((uint8 *)si->framebuffer + 0x200000);
540  		si->clone_bugfix_regs = (uint32 *) di->regs;
541 		break;
542 	}
543 
544 	/* remember the DMA address of the frame buffer for BDirectWindow?? purposes */
545 	si->framebuffer_pci = (void *) di->pcii.u.h0.base_registers_pci[frame_buffer];
546 
547 	// remember settings for use here and in accelerant
548 	si->settings = current_settings;
549 
550 	/* in any case, return the result */
551 	return si->fb_area;
552 }
553 
554 static void unmap_device(device_info *di) {
555 	shared_info *si = di->si;
556 	uint32	tmpUlong;
557 	pci_info *pcii = &(di->pcii);
558 
559 	/* disable memory mapped IO */
560 	tmpUlong = get_pci(PCI_command, 4);
561 	tmpUlong &= 0xfffffffc;
562 	set_pci(PCI_command, 4, tmpUlong);
563 	/* delete the areas */
564 	if (si->regs2_area >= 0) delete_area(si->regs2_area);
565 	if (si->regs_area >= 0) delete_area(si->regs_area);
566 	if (si->fb_area >= 0) delete_area(si->fb_area);
567 	si->regs2_area = si->regs_area = si->fb_area = -1;
568 	si->framebuffer = NULL;
569 	di->regs = NULL;
570 }
571 
572 static void probe_devices(void) {
573 	uint32 pci_index = 0;
574 	uint32 count = 0;
575 	device_info *di = pd->di;
576 
577 	/* while there are more pci devices */
578 	while ((count < MAX_DEVICES) && ((*pci_bus->get_nth_pci_info)(pci_index, &(di->pcii)) == B_NO_ERROR)) {
579 		int vendor = 0;
580 
581 		/* if we match a supported vendor */
582 		while (SupportedDevices[vendor].vendor) {
583 			if (SupportedDevices[vendor].vendor == di->pcii.vendor_id) {
584 				uint16 *devices = SupportedDevices[vendor].devices;
585 				/* while there are more supported devices */
586 				while (*devices) {
587 					/* if we match a supported device */
588 					if (*devices == di->pcii.device_id ) {
589 						/* publish the device name */
590 						sprintf(di->name, "graphics/" DEVICE_FORMAT,
591 							di->pcii.vendor_id, di->pcii.device_id,
592 							di->pcii.bus, di->pcii.device, di->pcii.function);
593 
594 						/* remember the name */
595 						pd->device_names[count] = di->name;
596 						/* mark the driver as available for R/W open */
597 						di->is_open = 0;
598 						/* mark areas as not yet created */
599 						di->shared_area = -1;
600 						/* mark pointer to shared data as invalid */
601 						di->si = NULL;
602 						/* inc pointer to device info */
603 						di++;
604 						/* inc count */
605 						count++;
606 						/* break out of these while loops */
607 						goto next_device;
608 					}
609 					/* next supported device */
610 					devices++;
611 				}
612 			}
613 			vendor++;
614 		}
615 next_device:
616 		/* next pci_info struct, please */
617 		pci_index++;
618 	}
619 	/* propagate count */
620 	pd->count = count;
621 	/* terminate list of device names with a null pointer */
622 	pd->device_names[pd->count] = NULL;
623 }
624 
625 static uint32 thread_interrupt_work(int32 *flags, vuint32 *regs, vuint32 *regs2, shared_info *si) {
626 	uint32 handled = B_HANDLED_INTERRUPT;
627 	/* release the vblank semaphore */
628 	if (si->vblank >= 0) {
629 		int32 blocked;
630 		if ((get_sem_count(si->vblank, &blocked) == B_OK) && (blocked < 0)) {
631 			release_sem_etc(si->vblank, -blocked, B_DO_NOT_RESCHEDULE);
632 			handled = B_INVOKE_SCHEDULER;
633 		}
634 	}
635 	return handled;
636 }
637 
638 static int32
639 nm_interrupt(void *data)
640 {
641 	int32 handled = B_UNHANDLED_INTERRUPT;
642 	device_info *di = (device_info *)data;
643 	shared_info *si = di->si;
644 	int32 *flags = &(si->flags);
645 	vuint32 *regs, *regs2;
646 
647 	/* is someone already handling an interrupt for this device? */
648 	if (atomic_or(flags, SKD_HANDLER_INSTALLED) & SKD_HANDLER_INSTALLED) {
649 		goto exit0;
650 	}
651 	/* get regs */
652 	regs = di->regs;
653 	regs2 = di->regs2;
654 
655 	/* was it a VBI? */
656 	if (caused_vbi(regs, regs2)) {
657 		/*clear the interrupt*/
658 		clear_vbi(regs, regs2);
659 		/*release the semaphore*/
660 		handled = thread_interrupt_work(flags, regs, regs2, si);
661 	}
662 
663 	/* note that we're not in the handler any more */
664 	atomic_and(flags, ~SKD_HANDLER_INSTALLED);
665 
666 exit0:
667 	return handled;
668 }
669 
670 static status_t open_hook (const char* name, uint32 flags, void** cookie) {
671 	int32 index = 0;
672 	device_info *di;
673 	shared_info *si;
674 	thread_id	thid;
675 	thread_info	thinfo;
676 	status_t	result = B_OK;
677 	char shared_name[B_OS_NAME_LENGTH];
678 
679 	/* find the device name in the list of devices */
680 	/* we're never passed a name we didn't publish */
681 	while (pd->device_names[index] && (strcmp(name, pd->device_names[index]) != 0)) index++;
682 
683 	/* for convienience */
684 	di = &(pd->di[index]);
685 
686 	/* make sure no one else has write access to the common data */
687 	AQUIRE_BEN(pd->kernel);
688 
689 	/* if it's already open for writing */
690 	if (di->is_open) {
691 		/* mark it open another time */
692 		goto mark_as_open;
693 	}
694 	/* create the shared area */
695 	sprintf(shared_name, DEVICE_FORMAT " shared",
696 		di->pcii.vendor_id, di->pcii.device_id,
697 		di->pcii.bus, di->pcii.device, di->pcii.function);
698 	/* create this area with NO user-space read or write permissions, to prevent accidental dammage */
699 	di->shared_area = create_area(shared_name, (void **)&(di->si), B_ANY_KERNEL_ADDRESS, ((sizeof(shared_info) + (B_PAGE_SIZE - 1)) & ~(B_PAGE_SIZE - 1)), B_FULL_LOCK,
700 		B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA | B_CLONEABLE_AREA);
701 	if (di->shared_area < 0) {
702 		/* return the error */
703 		result = di->shared_area;
704 		goto done;
705 	}
706 
707 	/* save a few dereferences */
708 	si = di->si;
709 
710 	/* save the vendor and device IDs */
711 	si->vendor_id = di->pcii.vendor_id;
712 	si->device_id = di->pcii.device_id;
713 	si->revision = di->pcii.revision;
714 
715 	/* ensure that the accelerant's INIT_ACCELERANT function can be executed */
716 	si->accelerant_in_use = false;
717 
718 	/* map the device */
719 	result = map_device(di);
720 	if (result < 0) goto free_shared;
721 
722 	/* we will be returning OK status for sure now */
723 	result = B_OK;
724 
725 	/* disable and clear any pending interrupts */
726 	disable_vbi(di->regs, di->regs2);
727 
728 	/* preset we can't use INT related functions */
729 	si->ps.int_assigned = false;
730 
731 	/* create a semaphore for vertical blank management */
732 	si->vblank = create_sem(0, di->name);
733 	if (si->vblank < 0) goto mark_as_open;
734 
735 	/* change the owner of the semaphores to the opener's team */
736 	/* this is required because apps can't aquire kernel semaphores */
737 	thid = find_thread(NULL);
738 	get_thread_info(thid, &thinfo);
739 	set_sem_owner(si->vblank, thinfo.team);
740 
741 	/* If there is a valid interrupt line assigned then set up interrupts */
742 	if ((di->pcii.u.h0.interrupt_pin == 0x00) ||
743 	    (di->pcii.u.h0.interrupt_line == 0xff) || /* no IRQ assigned */
744 	    (di->pcii.u.h0.interrupt_line <= 0x02))   /* system IRQ assigned */
745 	{
746 		/* delete the semaphore as it won't be used */
747 		delete_sem(si->vblank);
748 		si->vblank = -1;
749 	}
750 	else
751 	{
752 		/* otherwise install our interrupt handler */
753 		result = install_io_interrupt_handler(di->pcii.u.h0.interrupt_line, nm_interrupt, (void *)di, 0);
754 		/* bail if we couldn't install the handler */
755 		if (result != B_OK)
756 		{
757 			/* delete the semaphore as it won't be used */
758 			delete_sem(si->vblank);
759 			si->vblank = -1;
760 		}
761 		else
762 		{
763 			/* inform accelerant(s) we can use INT related functions */
764 			si->ps.int_assigned = true;
765 		}
766 	}
767 
768 mark_as_open:
769 	/* mark the device open */
770 	di->is_open++;
771 
772 	/* send the cookie to the opener */
773 	*cookie = di;
774 
775 	goto done;
776 
777 
778 free_shared:
779 	/* clean up our shared area */
780 	delete_area(di->shared_area);
781 	di->shared_area = -1;
782 	di->si = NULL;
783 
784 done:
785 	/* end of critical section */
786 	RELEASE_BEN(pd->kernel);
787 
788 	/* all done, return the status */
789 	return result;
790 }
791 
792 /* ----------
793 	read_hook - does nothing, gracefully
794 ----- */
795 static status_t
796 read_hook (void* dev, off_t pos, void* buf, size_t* len)
797 {
798 	*len = 0;
799 	return B_NOT_ALLOWED;
800 }
801 
802 
803 /* ----------
804 	write_hook - does nothing, gracefully
805 ----- */
806 static status_t
807 write_hook (void* dev, off_t pos, const void* buf, size_t* len)
808 {
809 	*len = 0;
810 	return B_NOT_ALLOWED;
811 }
812 
813 /* ----------
814 	close_hook - does nothing, gracefully
815 ----- */
816 static status_t
817 close_hook (void* dev)
818 {
819 	/* we don't do anything on close: there might be dup'd fd */
820 	return B_NO_ERROR;
821 }
822 
823 /* -----------
824 	free_hook - close down the device
825 ----------- */
826 static status_t
827 free_hook (void* dev) {
828 	device_info *di = (device_info *)dev;
829 	shared_info	*si = di->si;
830 	vuint32 *regs = di->regs;
831 	vuint32 *regs2 = di->regs2;
832 
833 	/* lock the driver */
834 	AQUIRE_BEN(pd->kernel);
835 
836 	/* if opened multiple times, decrement the open count and exit */
837 	if (di->is_open > 1)
838 		goto unlock_and_exit;
839 
840 	/* disable and clear any pending interrupts */
841 	disable_vbi(regs, regs2);
842 
843 	if (si->ps.int_assigned)
844 	{
845 		/* remove interrupt handler */
846 		remove_io_interrupt_handler(di->pcii.u.h0.interrupt_line, nm_interrupt, di);
847 
848 		/* delete the semaphores, ignoring any errors ('cause the owning team may have died on us) */
849 		delete_sem(si->vblank);
850 		si->vblank = -1;
851 	}
852 
853 	/* free regs and framebuffer areas */
854 	unmap_device(di);
855 
856 	/* clean up our shared area */
857 	delete_area(di->shared_area);
858 	di->shared_area = -1;
859 	di->si = NULL;
860 
861 unlock_and_exit:
862 	/* mark the device available */
863 	di->is_open--;
864 	/* unlock the driver */
865 	RELEASE_BEN(pd->kernel);
866 	/* all done */
867 	return B_OK;
868 }
869 
870 /* -----------
871 	control_hook - where the real work is done
872 ----------- */
873 static status_t
874 control_hook (void* dev, uint32 msg, void *buf, size_t len) {
875 	device_info *di = (device_info *)dev;
876 	status_t result = B_DEV_INVALID_IOCTL;
877 
878 	switch (msg) {
879 		/* the only PUBLIC ioctl */
880 		case B_GET_ACCELERANT_SIGNATURE: {
881 			char *sig = (char *)buf;
882 			strcpy(sig, current_settings.accelerant);
883 			result = B_OK;
884 		} break;
885 
886 		/* PRIVATE ioctl from here on */
887 		case NM_GET_PRIVATE_DATA: {
888 			nm_get_private_data *gpd = (nm_get_private_data *)buf;
889 			if (gpd->magic == NM_PRIVATE_DATA_MAGIC) {
890 				gpd->shared_info_area = di->shared_area;
891 				result = B_OK;
892 			}
893 		} break;
894 		case NM_GET_PCI: {
895 			nm_get_set_pci *gsp = (nm_get_set_pci *)buf;
896 			if (gsp->magic == NM_PRIVATE_DATA_MAGIC) {
897 				pci_info *pcii = &(di->pcii);
898 				gsp->value = get_pci(gsp->offset, gsp->size);
899 				result = B_OK;
900 			}
901 		} break;
902 		case NM_SET_PCI: {
903 			nm_get_set_pci *gsp = (nm_get_set_pci *)buf;
904 			if (gsp->magic == NM_PRIVATE_DATA_MAGIC) {
905 				pci_info *pcii = &(di->pcii);
906 				set_pci(gsp->offset, gsp->size, gsp->value);
907 				result = B_OK;
908 			}
909 		} break;
910 		case NM_DEVICE_NAME: {
911 			nm_device_name *dn = (nm_device_name *)buf;
912 			if (dn->magic == NM_PRIVATE_DATA_MAGIC) {
913 				strcpy(dn->name, di->name);
914 				result = B_OK;
915 			}
916 		} break;
917 		case NM_RUN_INTERRUPTS: {
918 			nm_set_bool_state *ri = (nm_set_bool_state *)buf;
919 			if (ri->magic == NM_PRIVATE_DATA_MAGIC) {
920 				vuint32 *regs = di->regs;
921 				vuint32 *regs2 = di->regs2;
922 				if (ri->do_it) {
923 					enable_vbi(regs, regs2);
924 				} else {
925 					disable_vbi(regs, regs2);
926 				}
927 				result = B_OK;
928 			}
929 		} break;
930 		case NM_ISA_OUT: {
931 			nm_in_out_isa *io_isa = (nm_in_out_isa *)buf;
932 			if (io_isa->magic == NM_PRIVATE_DATA_MAGIC) {
933 				if (io_isa->size == 1)
934   					isa_bus->write_io_8(io_isa->adress, (uint8)io_isa->data);
935    				else
936    					isa_bus->write_io_16(io_isa->adress, io_isa->data);
937   				result = B_OK;
938    			}
939 		} break;
940 		case NM_ISA_IN: {
941 			nm_in_out_isa *io_isa = (nm_in_out_isa *)buf;
942 			if (io_isa->magic == NM_PRIVATE_DATA_MAGIC) {
943 				if (io_isa->size == 1)
944 	   				io_isa->data = isa_bus->read_io_8(io_isa->adress);
945 	   			else
946 	   				io_isa->data = isa_bus->read_io_16(io_isa->adress);
947    				result = B_OK;
948    			}
949 		} break;
950 		case NM_PGM_BES: {
951 			nm_bes_data *bes_isa = (nm_bes_data *)buf;
952 			if (bes_isa->magic == NM_PRIVATE_DATA_MAGIC) {
953 				drv_program_bes_ISA(bes_isa);
954   				result = B_OK;
955    			}
956 		} break;
957 	}
958 	return result;
959 }
960 
961 void drv_program_bes_ISA(nm_bes_data *bes)
962 {
963 	uint8 temp;
964 
965 	/* helper: some cards use pixels to define buffer pitch, others use bytes */
966 	uint16 buf_pitch = bes->ob_width;
967 
968 	/* ISA card */
969 	/* unlock card overlay sequencer registers (b5 = 1) */
970 	temp = (KISAGRPHR(GENLOCK) | 0x20);
971 	/* we need to wait a bit or the card will mess-up it's register values.. (NM2160) */
972 	snooze(10);
973 	KISAGRPHW(GENLOCK, temp);
974 	/* destination rectangle #1 (output window position and size) */
975 	KISAGRPHW(HD1COORD1L, ((bes->moi.hcoordv >> 16) & 0xff));
976 	KISAGRPHW(HD1COORD2L, (bes->moi.hcoordv & 0xff));
977 	KISAGRPHW(HD1COORD21H, (((bes->moi.hcoordv >> 4) & 0xf0) | ((bes->moi.hcoordv >> 24) & 0x0f)));
978 	KISAGRPHW(VD1COORD1L, ((bes->moi.vcoordv >> 16) & 0xff));
979 	KISAGRPHW(VD1COORD2L, (bes->moi.vcoordv & 0xff));
980 	KISAGRPHW(VD1COORD21H, (((bes->moi.vcoordv >> 4) & 0xf0) | ((bes->moi.vcoordv >> 24) & 0x0f)));
981 	if (!bes->move_only)
982 	{
983 		/* scaling */
984 		KISAGRPHW(XSCALEL, (bes->hiscalv & 0xff));
985 		KISAGRPHW(XSCALEH, ((bes->hiscalv >> 8) & 0xff));
986 		KISAGRPHW(YSCALEL, (bes->viscalv & 0xff));
987 		KISAGRPHW(YSCALEH, ((bes->viscalv >> 8) & 0xff));
988 	}
989 	/* inputbuffer #1 origin */
990 	/* (we don't program buffer #2 as it's unused.) */
991 	if (bes->card_type < NM2200)
992 	{
993 		bes->moi.a1orgv >>= 1;
994 		/* horizontal source end does not use subpixelprecision: granularity is 8 pixels */
995 		/* notes:
996 		 * - correctly programming horizontal source end minimizes used bandwidth;
997 		 * - adding 9 below is in fact:
998 		 *   - adding 1 to round-up to the nearest whole source-end value
999 		       (making SURE we NEVER are a (tiny) bit too low);
1000 		     - adding 1 to convert 'last used position' to 'number of used pixels';
1001 		     - adding 7 to round-up to the nearest higher (or equal) valid register
1002 		       value (needed because of it's 8-pixel granularity). */
1003 		KISAGRPHW(0xbc, ((((bes->moi.hsrcendv >> 16) + 9) >> 3) - 1));
1004 	}
1005 	else
1006 	{
1007 		/* NM2200 and later cards use bytes to define buffer pitch */
1008 		buf_pitch <<= 1;
1009 		/* horizontal source end does not use subpixelprecision: granularity is 16 pixels */
1010 		/* notes:
1011 		 * - programming this register just a tiny bit too low messes up vertical
1012 		 *   scaling badly (also distortion stripes and flickering are reported)!
1013 		 * - not programming this register correctly will mess-up the picture when
1014 		 *   it's partly clipping on the right side of the screen...
1015 		 * - make absolutely sure the engine can fetch the last pixel needed from
1016 		 *   the sourcebitmap even if only to generate a tiny subpixel from it!
1017 		 *   (see remarks for < NM2200 cards regarding programming this register) */
1018 		KISAGRPHW(0xbc, ((((bes->moi.hsrcendv >> 16) + 17) >> 4) - 1));
1019 	}
1020 	KISAGRPHW(BUF1ORGL, (bes->moi.a1orgv & 0xff));
1021 	KISAGRPHW(BUF1ORGM, ((bes->moi.a1orgv >> 8) & 0xff));
1022 	KISAGRPHW(BUF1ORGH, ((bes->moi.a1orgv >> 16) & 0xff));
1023 	/* ??? */
1024 	KISAGRPHW(0xbd, 0x02);
1025 	KISAGRPHW(0xbe, 0x00);
1026 	/* b2 = 0: don't use horizontal mirroring (NM2160) */
1027 	/* other bits do ??? */
1028 	KISAGRPHW(0xbf, 0x02);
1029 	/* ??? */
1030     KISASEQW(0x1c, 0xfb);
1031    	KISASEQW(0x1d, 0x00);
1032 	KISASEQW(0x1e, 0xe2);
1033    	KISASEQW(0x1f, 0x02);
1034 	/* b1 = 0: disable alternating hardware buffers (NM2160) */
1035 	/* other bits do ??? */
1036 	KISASEQW(0x09, 0x11);
1037 	/* we don't use PCMCIA Zoomed Video port capturing, set 1:1 scale just in case */
1038 	/* (b6-4 = Y downscale = 100%, b2-0 = X downscale = 100%;
1039 	 *  downscaling selectable in 12.5% steps on increasing setting by 1) */
1040 	KISASEQW(ZVCAP_DSCAL, 0x00);
1041 	if (!bes->move_only)
1042 	{
1043 		/* global BES control */
1044 		KISAGRPHW(BESCTRL1, (bes->globctlv & 0xff));
1045 		KISASEQW(BESCTRL2, ((bes->globctlv >> 8) & 0xff));
1046 
1047 
1048 		/**************************
1049 		 *** setup color keying ***
1050 		 **************************/
1051 
1052 		KISAGRPHW(COLKEY_R, bes->colkey_r);
1053 		KISAGRPHW(COLKEY_G, bes->colkey_g);
1054 		KISAGRPHW(COLKEY_B, bes->colkey_b);
1055 
1056 
1057 		/*************************
1058 		 *** setup misc. stuff ***
1059 		 *************************/
1060 
1061 		/* setup brightness to be 'neutral' (two's complement number) */
1062 		KISAGRPHW(BRIGHTNESS, 0x00);
1063 
1064 		/* setup inputbuffer #1 pitch including slopspace */
1065 		/* (we don't program the pitch for inputbuffer #2 as it's unused.) */
1066 		KISAGRPHW(BUF1PITCHL, (buf_pitch & 0xff));
1067 		KISAGRPHW(BUF1PITCHH, ((buf_pitch >> 8) & 0xff));
1068 	}
1069 }
1070