#include <KernelExport.h>
#include <PCI.h>
#include <OS.h>
#include <malloc.h>
#include <graphic_driver.h>
#include <stdio.h>
#include <string.h>
#include "DriverInterface.h"
#include "setmode.h"
#include "acl.h"
#include "bits.h"
#if DEBUG > 0
#define ddprintf(a) dprintf a
#else
#define ddprintf(a)
#endif
#define MAX_DEVICES 8
int32 api_version = 2;
typedef struct {
uint32 isOpen;
area_id sharedArea;
ET6000SharedInfo *si;
#if DEBUG > 0
uint32 interrupt_count;
#endif
pci_info pcii;
char name[B_OS_NAME_LENGTH];
} ET6000DeviceInfo;
typedef struct {
#if DEBUG > 0
uint32 total_interrupts;
#endif
uint32 count;
benaphore kernel;
char *deviceNames[MAX_DEVICES+1];
ET6000DeviceInfo di[MAX_DEVICES];
} DeviceData;
static DeviceData *pd;
static status_t et6000OpenHook(const char* name, uint32 flags, void** cookie);
static status_t et6000CloseHook(void* dev);
static status_t et6000FreeHook(void* dev);
static status_t et6000ReadHook(void* dev, off_t pos, void* buf, size_t* len);
static status_t et6000WriteHook(void* dev, off_t pos, const void* buf, size_t* len);
static status_t et6000ControlHook(void* dev, uint32 msg, void *buf, size_t len);
static status_t et6000MapDevice(ET6000DeviceInfo *di);
static void et6000UnmapDevice(ET6000DeviceInfo *di);
static void et6000ProbeDevices(void);
static int32 et6000Interrupt(void *data);
#if DEBUG > 0
static int et6000dump(int argc, char **argv);
#endif
static pci_module_info *pci_bus;
#define get_pci(o, s) (*pci_bus->read_pci_config)(pcii->bus, pcii->device, pcii->function, (o), (s))
#define set_pci(o, s, v) (*pci_bus->write_pci_config)(pcii->bus, pcii->device, pcii->function, (o), (s), (v))
static device_hooks et6000DeviceHooks = {
et6000OpenHook,
et6000CloseHook,
et6000FreeHook,
et6000ControlHook,
et6000ReadHook,
et6000WriteHook,
NULL,
NULL,
NULL,
NULL
};
#define TSENG_VENDOR_ID 0x100C
static uint16 et6000DeviceList[] = {
0x3208,
0x4702,
0
};
static struct {
uint16 vendor;
uint16 *devices;
} supportedDevices[] = {
{TSENG_VENDOR_ID, et6000DeviceList},
{0x0000, NULL}
};
status_t init_hardware(void) {
long pciIndex = 0;
pci_info pcii;
bool foundOne = FALSE;
if (get_module(B_PCI_MODULE_NAME, (module_info **)&pci_bus) != B_OK)
return B_ERROR;
while ((*pci_bus->get_nth_pci_info)(pciIndex, &pcii) == B_NO_ERROR) {
int vendor = 0;
ddprintf(("ET6000 init_hardware(): checking pci index %ld, device 0x%04x/0x%04x\n", pciIndex, pcii.vendor_id, pcii.device_id));
while (supportedDevices[vendor].vendor) {
if (supportedDevices[vendor].vendor == pcii.vendor_id) {
uint16 *devices = supportedDevices[vendor].devices;
while (*devices) {
if (*devices == pcii.device_id) {
ddprintf(("ET6000: we support this device\n"));
foundOne = TRUE;
goto done;
}
devices++;
}
}
vendor++;
}
pciIndex++;
}
ddprintf(("ET6000: init_hardware - no supported devices\n"));
done:
put_module(B_PCI_MODULE_NAME);
return (foundOne ? B_OK : B_ERROR);
}
static void et6000ProbeDevices(void) {
uint32 pciIndex = 0;
uint32 count = 0;
ET6000DeviceInfo *di = pd->di;
while ((count < MAX_DEVICES) &&
((*pci_bus->get_nth_pci_info)(pciIndex, &(di->pcii)) == B_NO_ERROR))
{
int vendor = 0;
ddprintf(("ET6000: checking pci index %ld, device 0x%04x/0x%04x\n", pciIndex, di->pcii.vendor_id, di->pcii.device_id));
while (supportedDevices[vendor].vendor) {
if (supportedDevices[vendor].vendor == di->pcii.vendor_id) {
uint16 *devices = supportedDevices[vendor].devices;
while (*devices) {
if (*devices == di->pcii.device_id) {
sprintf(di->name, "graphics/%04X_%04X_%02X%02X%02X",
di->pcii.vendor_id, di->pcii.device_id,
di->pcii.bus, di->pcii.device, di->pcii.function);
ddprintf(("ET6000: making /dev/%s\n", di->name));
pd->deviceNames[count] = di->name;
di->isOpen = 0;
di->sharedArea = -1;
di->si = NULL;
di++;
count++;
goto next_device;
}
devices++;
}
}
vendor++;
}
next_device:
pciIndex++;
}
pd->count = count;
pd->deviceNames[pd->count] = NULL;
ddprintf(("SKD et6000ProbeDevices: %ld supported devices\n", pd->count));
}
status_t init_driver(void) {
if (get_module(B_PCI_MODULE_NAME, (module_info **)&pci_bus) != B_OK)
return B_ERROR;
pd = (DeviceData *)calloc(1, sizeof(DeviceData));
if (!pd) {
put_module(B_PCI_MODULE_NAME);
return B_ERROR;
}
INIT_BEN(pd->kernel);
et6000ProbeDevices();
#if DEBUG > 0
add_debugger_command("et6000dump", et6000dump, "dump ET6000 kernel driver persistant data");
#endif
return B_OK;
}
const char **publish_devices(void) {
return (const char **)pd->deviceNames;
}
device_hooks *find_device(const char *name) {
int index = 0;
while (pd->deviceNames[index]) {
if (strcmp(name, pd->deviceNames[index]) == 0)
return &et6000DeviceHooks;
index++;
}
return NULL;
}
void uninit_driver(void) {
#if DEBUG > 0
remove_debugger_command("et6000dump", et6000dump);
#endif
DELETE_BEN(pd->kernel);
free(pd);
pd = NULL;
put_module(B_PCI_MODULE_NAME);
}
static int32 et6000Interrupt(void *data) {
int32 handled = B_UNHANDLED_INTERRUPT;
ET6000DeviceInfo *di = (ET6000DeviceInfo *)data;
ET6000SharedInfo *si = di->si;
int32 *flags = &(si->flags);
#if DEBUG > 0
pd->total_interrupts++;
#endif
if (atomic_or(flags, ET6000_HANDLER_INSTALLED) & ET6000_HANDLER_INSTALLED) {
#if DEBUG > 0
kprintf("ET6000: Already in handler!\n");
#endif
goto exit0;
}
switch (et6000aclInterruptCause(si->mmRegs)) {
case ET6000_ACL_INT_CAUSE_NONE:
handled = B_UNHANDLED_INTERRUPT;
break;
case ET6000_ACL_INT_CAUSE_READ:
et6000aclReadInterruptClear(si->mmRegs);
handled = B_HANDLED_INTERRUPT;
break;
case ET6000_ACL_INT_CAUSE_WRITE:
et6000aclWriteInterruptClear(si->mmRegs);
handled = B_HANDLED_INTERRUPT;
break;
case ET6000_ACL_INT_CAUSE_BOTH:
et6000aclReadInterruptClear(si->mmRegs);
et6000aclWriteInterruptClear(si->mmRegs);
handled = B_HANDLED_INTERRUPT;
break;
}
#if DEBUG > 0
if (handled == B_HANDLED_INTERRUPT)
di->interrupt_count++;
#endif
atomic_and(flags, ~ET6000_HANDLER_INSTALLED);
exit0:
return handled;
}
static uint32 et6000GetOnboardMemorySize(uint16 pciConfigSpace,
volatile void *memory)
{
uint32 memSize = 0;
ioSet8(0x3d8, 0x00, 0xa0);
ioSet8(0x3b8, 0x00, 0xa0);
switch (ioGet8(0x3C2) & 0x03) {
case 0x00:
memSize = 1024*1024 * ((ioGet8(pciConfigSpace + 0x45) & 0x03) + 1);
break;
case 0x03:
memSize =
((ioGet8(pciConfigSpace + 0x47) & 0x07) + 1) * 8 * 32*1024;
if (ioGet8(pciConfigSpace + 0x45) & 0x04)
memSize *= 2;
break;
default:
memSize = 4196*1024;
}
if (memSize == 2621440) {
uint8 pci40 = ioGet8(pciConfigSpace+0x40);
et6000EnableLinearMemoryMapping(pciConfigSpace);
*(volatile uint32 *)((uint32)memory + 2359296) = 0xaa55aa55;
if (*(volatile uint32 *)((uint32)memory + 2359296) != 0xaa55aa55)
memSize = 2359296;
ioSet8(pciConfigSpace+0x40, 0x00, pci40);
}
return memSize;
}
static status_t et6000MapDevice(ET6000DeviceInfo *di) {
char buffer[B_OS_NAME_LENGTH];
ET6000SharedInfo *si = di->si;
uint32 tmpUlong;
pci_info *pcii = &(di->pcii);
tmpUlong = get_pci(PCI_command, 4);
tmpUlong |= 0x00000003;
set_pci(PCI_command, 4, tmpUlong);
tmpUlong = get_pci(PCI_rom_base, 4);
tmpUlong |= 0x00000001;
set_pci(PCI_rom_base, 4, tmpUlong);
si->pciConfigSpace = (uint16)di->pcii.u.h0.base_registers[1];
sprintf(buffer, "%04X_%04X_%02X%02X%02X videomemory",
di->pcii.vendor_id, di->pcii.device_id,
di->pcii.bus, di->pcii.device, di->pcii.function);
si->memoryArea = map_physical_memory(buffer,
di->pcii.u.h0.base_registers[0],
di->pcii.u.h0.base_register_sizes[0],
B_ANY_KERNEL_BLOCK_ADDRESS | B_UNCACHED_MEMORY,
B_READ_AREA + B_WRITE_AREA,
&(si->memory));
si->framebuffer = si->memory;
si->mmRegs = (void *)((uint32)si->memory + 0x003fff00);
si->emRegs = (void *)((uint32)si->memory + 0x003fe000);
si->physMemory = si->physFramebuffer =
(void *) di->pcii.u.h0.base_registers_pci[0];
si->memSize = et6000GetOnboardMemorySize(si->pciConfigSpace, si->memory);
return si->memoryArea;
}
static void et6000UnmapDevice(ET6000DeviceInfo *di) {
ET6000SharedInfo *si = di->si;
ddprintf(("et6000UnmapDevice(%08lx) begins...\n", (uint32)di));
ddprintf((" memoryArea: %ld\n", si->memoryArea));
if (si->memoryArea >= 0)
delete_area(si->memoryArea);
si->memoryArea = -1;
si->framebuffer = NULL;
si->physFramebuffer = NULL;
si->memory = NULL;
si->physMemory = NULL;
ddprintf(("et6000UnmapDevice() ends.\n"));
}
static status_t et6000OpenHook(const char* name, uint32 flags, void** cookie) {
int32 index = 0;
ET6000DeviceInfo *di;
ET6000SharedInfo *si;
status_t result = B_OK;
char shared_name[B_OS_NAME_LENGTH];
ddprintf(("SKD et6000OpenHook(%s, %ld, 0x%08lx)\n", name, flags, (uint32)cookie));
while(pd->deviceNames[index] &&
(strcmp(name, pd->deviceNames[index]) != 0))
{
index++;
}
di = &(pd->di[index]);
AQUIRE_BEN(pd->kernel);
if (di->isOpen) {
goto mark_as_open;
}
sprintf(shared_name, "%04X_%04X_%02X%02X%02X shared",
di->pcii.vendor_id, di->pcii.device_id,
di->pcii.bus, di->pcii.device, di->pcii.function);
di->sharedArea = create_area(shared_name, (void **)&(di->si), B_ANY_KERNEL_ADDRESS, ((sizeof(ET6000SharedInfo) + (B_PAGE_SIZE - 1)) & ~(B_PAGE_SIZE - 1)), B_FULL_LOCK,
B_FULL_LOCK, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA | B_CLONEABLE_AREA);
if (di->sharedArea < 0) {
result = di->sharedArea;
goto done;
}
si = di->si;
si->vendor_id = di->pcii.vendor_id;
si->device_id = di->pcii.device_id;
si->revision = di->pcii.revision;
si->pixelClockMax16 = 135000;
si->pixelClockMax24 = 135000;
if (si->vendor_id == 0x100C) {
switch (si->device_id) {
case 0x3208:
if (si->revision < 0x70) {
si->pixelClockMax16 = 135000;
si->pixelClockMax24 = 135000;
}
else {
si->pixelClockMax16 = 175000;
si->pixelClockMax24 = 175000;
}
break;
case 0x4702:
si->pixelClockMax16 = 220000;
si->pixelClockMax24 = 220000;
break;
}
}
result = et6000MapDevice(di);
if (result < 0)
goto free_shared;
result = B_OK;
et6000aclReadInterruptClear(si->mmRegs);
et6000aclWriteInterruptClear(si->mmRegs);
et6000aclMasterInterruptDisable(si->mmRegs);
result = install_io_interrupt_handler(di->pcii.u.h0.interrupt_line,
et6000Interrupt, (void *)di, 0);
if (result != B_OK)
goto unmap;
mark_as_open:
di->isOpen++;
*cookie = di;
goto done;
unmap:
et6000UnmapDevice(di);
free_shared:
delete_area(di->sharedArea);
di->sharedArea = -1;
di->si = NULL;
done:
RELEASE_BEN(pd->kernel);
ddprintf(("et6000OpenHook returning 0x%08lx\n", result));
return result;
}
static status_t et6000ReadHook(void* dev, off_t pos, void* buf, size_t* len)
{
*len = 0;
return B_NOT_ALLOWED;
}
static status_t et6000WriteHook(void* dev, off_t pos, const void* buf, size_t* len)
{
*len = 0;
return B_NOT_ALLOWED;
}
static status_t et6000CloseHook(void* dev)
{
ddprintf(("SKD et6000CloseHook(%08lx)\n", (uint32)dev));
return B_NO_ERROR;
}
static status_t et6000FreeHook(void* dev) {
ET6000DeviceInfo *di = (ET6000DeviceInfo *)dev;
ET6000SharedInfo *si = di->si;
ddprintf(("SKD et6000FreeHook() begins...\n"));
AQUIRE_BEN(pd->kernel);
if (di->isOpen > 1)
goto unlock_and_exit;
et6000aclReadInterruptClear(si->mmRegs);
et6000aclWriteInterruptClear(si->mmRegs);
et6000aclMasterInterruptDisable(si->mmRegs);
remove_io_interrupt_handler(di->pcii.u.h0.interrupt_line, et6000Interrupt, di);
et6000UnmapDevice(di);
delete_area(di->sharedArea);
di->sharedArea = -1;
di->si = NULL;
unlock_and_exit:
di->isOpen--;
RELEASE_BEN(pd->kernel);
ddprintf(("SKD et6000FreeHook() ends.\n"));
return B_OK;
}
static status_t et6000ControlHook(void* dev, uint32 msg, void *buf, size_t len) {
ET6000DeviceInfo *di = (ET6000DeviceInfo *)dev;
status_t result = B_DEV_INVALID_IOCTL;
switch (msg) {
case B_GET_ACCELERANT_SIGNATURE: {
char *sig = (char *)buf;
strcpy(sig, "et6000.accelerant");
result = B_OK;
} break;
case ET6000_GET_PRIVATE_DATA: {
ET6000GetPrivateData *gpd = (ET6000GetPrivateData *)buf;
if (gpd->magic == ET6000_PRIVATE_DATA_MAGIC) {
gpd->sharedInfoArea = di->sharedArea;
result = B_OK;
}
} break;
case ET6000_GET_PCI: {
ET6000GetSetPCI *gsp = (ET6000GetSetPCI *)buf;
if (gsp->magic == ET6000_PRIVATE_DATA_MAGIC) {
pci_info *pcii = &(di->pcii);
gsp->value = get_pci(gsp->offset, gsp->size);
result = B_OK;
}
} break;
case ET6000_SET_PCI: {
ET6000GetSetPCI *gsp = (ET6000GetSetPCI *)buf;
if (gsp->magic == ET6000_PRIVATE_DATA_MAGIC) {
pci_info *pcii = &(di->pcii);
set_pci(gsp->offset, gsp->size, gsp->value);
result = B_OK;
}
} break;
case ET6000_DEVICE_NAME: {
ET6000DeviceName *dn = (ET6000DeviceName *)buf;
if(dn->magic == ET6000_PRIVATE_DATA_MAGIC) {
strncpy(dn->name, di->name, B_OS_NAME_LENGTH);
result = B_OK;
}
} break;
case ET6000_PROPOSE_DISPLAY_MODE: {
ET6000DisplayMode *dm = (ET6000DisplayMode *)buf;
if(dm->magic == ET6000_PRIVATE_DATA_MAGIC) {
result = et6000ProposeMode(&dm->mode, dm->memSize);
}
} break;
case ET6000_SET_DISPLAY_MODE: {
ET6000DisplayMode *dm = (ET6000DisplayMode *)buf;
if(dm->magic == ET6000_PRIVATE_DATA_MAGIC) {
result = et6000SetMode(&dm->mode, dm->pciConfigSpace);
}
} break;
}
return result;
}
#if DEBUG > 0
static int et6000dump(int argc, char **argv) {
int i;
kprintf("ET6000 Kernel Driver Persistant Data\n\nThere are %ld card(s)\n", pd->count);
kprintf("Driver wide benahpore: %ld/%ld\n", pd->kernel.ben, pd->kernel.sem);
kprintf("Total seen interrupts: %ld\n", pd->total_interrupts);
for (i = 0; i < pd->count; i++) {
ET6000DeviceInfo *di = &(pd->di[i]);
uint16 device_id = di->pcii.device_id;
ET6000SharedInfo *si = di->si;
kprintf(" device_id: 0x%04x\n", device_id);
kprintf(" interrupt count: %ld\n", di->interrupt_count);
if (si) {
kprintf(" flags:");
}
kprintf("\n");
}
return 1;
}
#endif