Working version of the driver. At least it does seem to work with my onboard com port, but not the OxfordSemi PCI card I have here. Still requires tty_manager, and config_manager too, so won't work in Haiku yet. Needs cleanup.
git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@29328 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
@@ -14,15 +14,113 @@
|
||||
#include "Driver.h"
|
||||
#include "SerialDevice.h"
|
||||
|
||||
static const char *sDeviceBaseName = "ports/serial";
|
||||
int32 api_version = B_CUR_DRIVER_API_VERSION;
|
||||
static const char *sDeviceBaseName = DEVFS_BASE;
|
||||
SerialDevice *gSerialDevices[DEVICES_COUNT];
|
||||
char *gDeviceNames[DEVICES_COUNT + 1];
|
||||
usb_module_info *gUSBModule = NULL;
|
||||
config_manager_for_driver_module_info *gConfigManagerModule = NULL;
|
||||
isa_module_info *gISAModule = NULL;
|
||||
pci_module_info *gPCIModule = NULL;
|
||||
tty_module_info *gTTYModule = NULL;
|
||||
struct ddomain gSerialDomain;
|
||||
sem_id gDriverLock = -1;
|
||||
|
||||
// 24 MHz clock
|
||||
static const uint32 sDefaultRates[] = {
|
||||
0, //B0
|
||||
2304, //B50
|
||||
1536, //B75
|
||||
1047, //B110
|
||||
857, //B134
|
||||
768, //B150
|
||||
512, //B200
|
||||
384, //B300
|
||||
192, //B600
|
||||
0, //B1200
|
||||
0, //B1800
|
||||
48, //B2400
|
||||
24, //B4800
|
||||
12, //B9600
|
||||
6, //B19200
|
||||
3, //B38400
|
||||
2, //B57600
|
||||
1, //B115200
|
||||
0, //B230400
|
||||
4, //460800 !? B31250!
|
||||
0, //921600 !?
|
||||
};
|
||||
|
||||
// 8MHz clock on serial3 and 4 on the BeBox
|
||||
#if 0
|
||||
static const uint32 sBeBoxRates[] = {
|
||||
0, //B0
|
||||
//...
|
||||
};
|
||||
#endif
|
||||
|
||||
static const struct serial_support_descriptor sSupportedDevices[] = {
|
||||
|
||||
#ifdef HANDLE_ISA_COM
|
||||
// ISA devices
|
||||
{ B_ISA_BUS, "Generic 16550 Serial Port", sDefaultRates, NULL, { 8, 8, 8 },
|
||||
{ PCI_simple_communications, PCI_serial, PCI_serial_16550 } },
|
||||
#endif
|
||||
// PCI devices
|
||||
|
||||
// vendor/device matches first
|
||||
|
||||
/*
|
||||
{ B_PCI_BUS, "OxfordSemi 16950 Serial Port", sDefaultRates, NULL, { 32, 32, 8 },
|
||||
{ PCI_simple_communications, PCI_serial, PCI_serial_16950,
|
||||
0x1415, 0x9501 } },
|
||||
*/
|
||||
// generic fallback matches
|
||||
/*
|
||||
{ B_PCI_BUS, "Generic XT Serial Port", NULL },
|
||||
{ PCI_INVAL, PCI_INVAL, PCI_simple_communications,
|
||||
PCI_serial, PCI_serial_xt } },
|
||||
|
||||
{ B_PCI_BUS, "Generic 16450 Serial Port", NULL },
|
||||
{ PCI_INVAL, PCI_INVAL, PCI_simple_communications,
|
||||
PCI_serial, PCI_serial_16450 } },
|
||||
|
||||
*/
|
||||
{ B_PCI_BUS, "Generic 16550 Serial Port", sDefaultRates, NULL, { 8, 8, 8 },
|
||||
{ PCI_simple_communications, PCI_serial, PCI_serial_16550,
|
||||
PCI_INVAL, PCI_INVAL } },
|
||||
|
||||
//XXX
|
||||
|
||||
{ B_PCI_BUS, "Generic 16950 Serial Port", sDefaultRates, NULL, { 8, 8, 8 },
|
||||
{ PCI_simple_communications, PCI_serial, PCI_serial_16950,
|
||||
PCI_INVAL, PCI_INVAL } },
|
||||
|
||||
//XXX DEBUG! HACK HACK HACK
|
||||
//XXX for testing probing
|
||||
#if 0
|
||||
{ B_PCI_BUS, "My BadIDE controller", sDefaultRates, NULL, { 8, 32, 8 },
|
||||
{ 1, 1, 0x8a,
|
||||
0x1002, 0x434a } },
|
||||
{ B_PCI_BUS, "My GoodIDE controller", sDefaultRates, NULL, { 8, 32, 8 },
|
||||
{ 1, 1, 0x8a,
|
||||
0x1002, 0x4349 } },
|
||||
{ B_PCI_BUS, "My IDE controller", sDefaultRates, NULL, { 8, 32, 8 },
|
||||
{ 1, 1, 0x8a,
|
||||
PCI_INVAL, PCI_INVAL } },
|
||||
#endif
|
||||
|
||||
// non PCI_serial devices
|
||||
|
||||
// beos zz driver supported that one
|
||||
{ B_PCI_BUS, "Lucent Modem", sDefaultRates, NULL, { 8, 8, 8 },
|
||||
{ PCI_simple_communications, PCI_simple_communications_other, 0x00,
|
||||
0x11C1, 0x0480 } },
|
||||
|
||||
{ B_PCI_BUS, NULL, NULL, NULL, {0}, {0} }
|
||||
};
|
||||
|
||||
|
||||
#if 0
|
||||
status_t
|
||||
pc_serial_device_added(pc_device device, void **cookie)
|
||||
{
|
||||
@@ -101,6 +199,323 @@ pc_serial_device_removed(void *cookie)
|
||||
TRACE_FUNCRET("< pc_serial_device_removed() returns\n");
|
||||
return B_OK;
|
||||
}
|
||||
#endif
|
||||
|
||||
//#pragma mark -
|
||||
|
||||
status_t
|
||||
pc_serial_insert_device(SerialDevice *device)
|
||||
{
|
||||
status_t status = B_OK;
|
||||
|
||||
//XXX fix leaks!
|
||||
acquire_sem(gDriverLock);
|
||||
for (int32 i = 0; i < DEVICES_COUNT; i++) {
|
||||
if (gSerialDevices[i] != NULL)
|
||||
continue;
|
||||
|
||||
status = device->Init();
|
||||
if (status < B_OK) {
|
||||
delete device;
|
||||
//return status;
|
||||
break;
|
||||
}
|
||||
|
||||
gSerialDevices[i] = device;
|
||||
|
||||
release_sem(gDriverLock);
|
||||
TRACE_ALWAYS("%s added\n", device->Description());
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
release_sem(gDriverLock);
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
|
||||
// probe devices with config_manager
|
||||
static status_t
|
||||
scan_bus(bus_type bus)
|
||||
{
|
||||
const char *bus_name = "Unknown";
|
||||
uint64 cookie = 0;
|
||||
status_t status;
|
||||
struct {
|
||||
device_info di;
|
||||
pci_info pi;
|
||||
} big_info;
|
||||
struct device_info &dinfo = big_info.di;
|
||||
|
||||
switch (bus) {
|
||||
case B_ISA_BUS:
|
||||
bus_name = "ISA";
|
||||
break;
|
||||
case B_PCI_BUS:
|
||||
bus_name = "PCI";
|
||||
break;
|
||||
case B_PCMCIA_BUS:
|
||||
default:
|
||||
return EINVAL;
|
||||
}
|
||||
TRACE_ALWAYS("scanning %s bus...\n", bus_name);
|
||||
|
||||
//XXX: clean up this mess
|
||||
|
||||
while ((gConfigManagerModule->get_next_device_info(bus,
|
||||
&cookie, &big_info.di, sizeof(big_info)) == B_OK)) {
|
||||
// skip disabled devices
|
||||
if (dinfo.flags & B_DEVICE_INFO_ENABLED == 0)
|
||||
continue;
|
||||
// skip non configured devices
|
||||
if (dinfo.flags & B_DEVICE_INFO_CONFIGURED == 0)
|
||||
continue;
|
||||
// and devices in error
|
||||
if (dinfo.config_status < B_OK)
|
||||
continue;
|
||||
|
||||
|
||||
/*
|
||||
TRACE_ALWAYS("device: 0x%08lx 0x%08lx 0x%08lx 0x%08lx\n",
|
||||
dinfo.id[0], dinfo.id[1], dinfo.id[2], dinfo.id[3]);
|
||||
*/
|
||||
|
||||
/*
|
||||
if (bus == B_PCI_BUS) {
|
||||
pci_info *pcii = (pci_info *)(((char *)&dinfo) +
|
||||
dinfo.bus_dependent_info_offset);
|
||||
TRACE_ALWAYS("pci: %04x:%04x\n",
|
||||
pcii->vendor_id, pcii->device_id);
|
||||
if ((pcii->header_type & PCI_header_type_mask) ==
|
||||
PCI_header_type_generic) {
|
||||
TRACE_ALWAYS("subsys: %04x:%04x\n",
|
||||
pcii->u.h0.subsystem_vendor_id, pcii->u.h0.subsystem_id);
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
const struct serial_support_descriptor *supported = NULL;
|
||||
for (int i = 0; sSupportedDevices[i].name; i++) {
|
||||
if (sSupportedDevices[i].bus != bus)
|
||||
continue;
|
||||
if (sSupportedDevices[i].match.class_base != PCI_undefined &&
|
||||
sSupportedDevices[i].match.class_base != dinfo.devtype.base)
|
||||
continue;
|
||||
if (sSupportedDevices[i].match.class_sub != PCI_undefined &&
|
||||
sSupportedDevices[i].match.class_sub != dinfo.devtype.subtype)
|
||||
continue;
|
||||
if (sSupportedDevices[i].match.class_api != PCI_undefined &&
|
||||
sSupportedDevices[i].match.class_api != dinfo.devtype.interface)
|
||||
continue;
|
||||
|
||||
#if 0
|
||||
// either this way
|
||||
if (bus == B_PCI_BUS) {
|
||||
pci_info *pcii = (pci_info *)(((char *)&dinfo) +
|
||||
dinfo.bus_dependent_info_offset);
|
||||
if (sSupportedDevices[i].match.vendor_id != PCI_INVAL &&
|
||||
sSupportedDevices[i].match.vendor_id != pcii->vendor_id)
|
||||
continue;
|
||||
if (sSupportedDevices[i].match.device_id != PCI_INVAL &&
|
||||
sSupportedDevices[i].match.device_id != pcii->device_id)
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
// or this one:
|
||||
// .id[0] = vendor_id and .id[1] = device_id
|
||||
// .id[3?] = subsys_vendor_id and .id[2?] = subsys_device_id
|
||||
if (bus == B_PCI_BUS &&
|
||||
sSupportedDevices[i].match.vendor_id != PCI_INVAL &&
|
||||
sSupportedDevices[i].match.vendor_id != dinfo.id[0])
|
||||
continue;
|
||||
|
||||
if (bus == B_PCI_BUS &&
|
||||
sSupportedDevices[i].match.device_id != PCI_INVAL &&
|
||||
sSupportedDevices[i].match.device_id != dinfo.id[1])
|
||||
continue;
|
||||
|
||||
|
||||
supported = &sSupportedDevices[i];
|
||||
break;
|
||||
}
|
||||
if (supported == NULL)
|
||||
continue;
|
||||
|
||||
struct {
|
||||
struct device_configuration c;
|
||||
resource_descriptor res[16];
|
||||
} config;
|
||||
if (gConfigManagerModule->get_size_of_current_configuration_for(
|
||||
cookie) > sizeof(config)) {
|
||||
TRACE_ALWAYS("config size too big for device\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (gConfigManagerModule->get_current_configuration_for(cookie,
|
||||
&config.c, sizeof(config)) < B_OK) {
|
||||
TRACE_ALWAYS("can't get config for device\n");
|
||||
continue;
|
||||
|
||||
}
|
||||
|
||||
TRACE_ALWAYS("device %Ld resources: %d irq %d dma %d io %d mem\n",
|
||||
cookie,
|
||||
gConfigManagerModule->count_resource_descriptors_of_type(
|
||||
&config.c, B_IRQ_RESOURCE),
|
||||
gConfigManagerModule->count_resource_descriptors_of_type(
|
||||
&config.c, B_DMA_RESOURCE),
|
||||
gConfigManagerModule->count_resource_descriptors_of_type(
|
||||
&config.c, B_IO_PORT_RESOURCE),
|
||||
gConfigManagerModule->count_resource_descriptors_of_type(
|
||||
&config.c, B_MEMORY_RESOURCE));
|
||||
|
||||
|
||||
// we first need the IRQ
|
||||
resource_descriptor irqdesc;
|
||||
if (gConfigManagerModule->get_nth_resource_descriptor_of_type(
|
||||
&config.c, 0, B_IRQ_RESOURCE, &irqdesc, sizeof(irqdesc)) < B_OK) {
|
||||
TRACE_ALWAYS("can't find IRQ for device\n");
|
||||
continue;
|
||||
}
|
||||
int irq;
|
||||
// XXX: what about APIC lines ?
|
||||
for (irq = 0; irq < 32; irq++) {
|
||||
if (irqdesc.d.m.mask & (1 << irq))
|
||||
break;
|
||||
}
|
||||
TRACE_ALWAYS("irq %d\n", irq);
|
||||
//TRACE_ALWAYS("irq: %lx,%lx,%lx\n", irqdesc.d.m.mask, irqdesc.d.m.flags, irqdesc.d.m.cookie);
|
||||
|
||||
TRACE_ALWAYS("found %s device %Ld [%x|%x|%x] "
|
||||
/*"ID: '%16.16s'"*/" flags: %08lx status: %s\n",
|
||||
bus_name, cookie, dinfo.devtype.base, dinfo.devtype.subtype,
|
||||
dinfo.devtype.interface, /*dinfo.id,*/ dinfo.flags,
|
||||
strerror(dinfo.config_status));
|
||||
|
||||
// force enable I/O ports on PCI devices
|
||||
#if 0
|
||||
if (bus == B_PCI_BUS) {
|
||||
pci_info *pcii = (pci_info *)(((char *)&dinfo) +
|
||||
dinfo.bus_dependent_info_offset);
|
||||
|
||||
uint32 cmd = gPCIModule->read_pci_config(pcii->bus, pcii->device,
|
||||
pcii->function, PCI_command, 2);
|
||||
TRACE_ALWAYS("PCI_command: 0x%04lx\n", cmd);
|
||||
cmd |= PCI_command_io;
|
||||
gPCIModule->write_pci_config(pcii->bus, pcii->device,
|
||||
pcii->function, PCI_command, 2, cmd);
|
||||
}
|
||||
#endif
|
||||
|
||||
// instanciate devices on IO ports
|
||||
resource_descriptor iodesc;
|
||||
SerialDevice *master = NULL;
|
||||
for (int i = 0;
|
||||
gConfigManagerModule->get_nth_resource_descriptor_of_type(
|
||||
&config.c, i, B_IO_PORT_RESOURCE, &iodesc, sizeof(iodesc)) == B_OK;
|
||||
i++) {
|
||||
TRACE_ALWAYS("io at 0x%04lx len 0x%04lx\n", iodesc.d.r.minbase,
|
||||
iodesc.d.r.len);
|
||||
|
||||
if (iodesc.d.r.len < supported->constraints.minsize)
|
||||
continue;
|
||||
if (iodesc.d.r.len > supported->constraints.maxsize)
|
||||
continue;
|
||||
SerialDevice *device;
|
||||
uint32 ioport = iodesc.d.r.minbase;
|
||||
next_split:
|
||||
// no more to split
|
||||
if ((ioport - iodesc.d.r.minbase) >= iodesc.d.r.len)
|
||||
continue;
|
||||
|
||||
TRACE_ALWAYS("inserting device at io 0x%04lx as %s\n", ioport,
|
||||
supported->name);
|
||||
|
||||
|
||||
device = new SerialDevice(supported, ioport, irq, master);
|
||||
if (pc_serial_insert_device(device) < B_OK) {
|
||||
TRACE_ALWAYS("can't insert device\n");
|
||||
continue;
|
||||
}
|
||||
if (master == NULL)
|
||||
master = device;
|
||||
|
||||
ioport += supported->constraints.split;
|
||||
goto next_split;
|
||||
// try next part of the I/O range now
|
||||
}
|
||||
// we have at least one device
|
||||
if (master) {
|
||||
// hook up the irq
|
||||
#if 0
|
||||
status = install_io_interrupt_handler(irq, pc_serial_interrupt,
|
||||
master, 0);
|
||||
TRACE_ALWAYS("installing irq %d handler: %s\n", irq, strerror(status));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// this version doesn't use config_manager, but can't probe the IRQ yet
|
||||
status_t
|
||||
scan_pci_alt()
|
||||
{
|
||||
pci_info info;
|
||||
int ix;
|
||||
// probe PCI devices
|
||||
for (ix = 0; (*gPCIModule->get_nth_pci_info)(ix, &info) == B_OK; ix++) {
|
||||
// sanity check
|
||||
if (info.header_type & PCI_header_type_mask != PCI_header_type_generic)
|
||||
continue;
|
||||
TRACE_ALWAYS("probing PCI device %2d [%x|%x|%x] %04x:%04x\n",
|
||||
ix, info.class_base, info.class_sub, info.class_api,
|
||||
info.vendor_id, info.device_id);
|
||||
|
||||
const struct serial_support_descriptor *supported = NULL;
|
||||
for (int i = 0; sSupportedDevices[i].name; i++) {
|
||||
if (sSupportedDevices[i].bus != B_PCI_BUS)
|
||||
continue;
|
||||
if (info.class_base != sSupportedDevices[i].match.class_base)
|
||||
continue;
|
||||
if (info.class_sub != sSupportedDevices[i].match.class_sub)
|
||||
continue;
|
||||
if (info.class_api != sSupportedDevices[i].match.class_api)
|
||||
continue;
|
||||
if (sSupportedDevices[i].match.vendor_id != PCI_INVAL
|
||||
&& info.vendor_id != sSupportedDevices[i].match.vendor_id)
|
||||
continue;
|
||||
if (sSupportedDevices[i].match.device_id != PCI_INVAL
|
||||
&& info.device_id != sSupportedDevices[i].match.device_id)
|
||||
continue;
|
||||
supported = &sSupportedDevices[i];
|
||||
}
|
||||
if (supported == NULL)
|
||||
continue;
|
||||
|
||||
TRACE_ALWAYS("found PCI device %2d [%x|%x|%x] %04x:%04x as %s\n",
|
||||
ix, info.class_base, info.class_sub, info.class_api,
|
||||
info.vendor_id, info.device_id, supported->name);
|
||||
|
||||
// find I/O ports
|
||||
for (int r = 0; r < 6; r++) {
|
||||
TRACE_ALWAYS("range at 0x%08lx len 0x%lx flags 0x%02x\n",
|
||||
info.u.h0.base_registers[r], info.u.h0.base_register_sizes[r],
|
||||
info.u.h0.base_register_flags[r]);
|
||||
|
||||
// not I/O
|
||||
if (info.u.h0.base_register_flags[r] & PCI_address_space == 0)
|
||||
continue;
|
||||
//
|
||||
TRACE_ALWAYS("regs at 0x%08lx len 0x%lx\n",
|
||||
info.u.h0.base_registers[r], info.u.h0.base_register_sizes[r]);
|
||||
//&PCI_address_io_mask
|
||||
}
|
||||
// XXX: interrupt_line doesn't seem to
|
||||
TRACE_ALWAYS("irq line %d, pin %d\n",
|
||||
info.u.h0.interrupt_line, info.u.h0.interrupt_pin);
|
||||
}
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
//#pragma mark -
|
||||
@@ -119,20 +534,28 @@ init_hardware()
|
||||
status_t
|
||||
init_driver()
|
||||
{
|
||||
status_t status;
|
||||
load_settings();
|
||||
create_log_file();
|
||||
|
||||
TRACE_FUNCALLS("> init_driver()\n");
|
||||
|
||||
status_t status = get_module(B_TTY_MODULE_NAME, (module_info **)&gTTYModule);
|
||||
status = get_module(B_TTY_MODULE_NAME, (module_info **)&gTTYModule);
|
||||
if (status < B_OK)
|
||||
return status;
|
||||
goto err_tty;
|
||||
|
||||
status = get_module(B_USB_MODULE_NAME, (module_info **)&gUSBModule);
|
||||
if (status < B_OK) {
|
||||
put_module(B_TTY_MODULE_NAME);
|
||||
return status;
|
||||
}
|
||||
status = get_module(B_PCI_MODULE_NAME, (module_info **)&gPCIModule);
|
||||
if (status < B_OK)
|
||||
goto err_pci;
|
||||
|
||||
status = get_module(B_ISA_MODULE_NAME, (module_info **)&gISAModule);
|
||||
if (status < B_OK)
|
||||
goto err_isa;
|
||||
|
||||
status = get_module(B_CONFIG_MANAGER_FOR_DRIVER_MODULE_NAME,
|
||||
(module_info **)&gConfigManagerModule);
|
||||
if (status < B_OK)
|
||||
goto err_cm;
|
||||
|
||||
for (int32 i = 0; i < DEVICES_COUNT; i++)
|
||||
gSerialDevices[i] = NULL;
|
||||
@@ -141,20 +564,40 @@ init_driver()
|
||||
|
||||
gDriverLock = create_sem(1, DRIVER_NAME"_devices_table_lock");
|
||||
if (gDriverLock < B_OK) {
|
||||
put_module(B_USB_MODULE_NAME);
|
||||
put_module(B_TTY_MODULE_NAME);
|
||||
return gDriverLock;
|
||||
status = gDriverLock;
|
||||
goto err_sem;
|
||||
}
|
||||
|
||||
static pc_notify_hooks notifyHooks = {
|
||||
&pc_serial_device_added,
|
||||
&pc_serial_device_removed
|
||||
};
|
||||
status = ENOENT;
|
||||
|
||||
gUSBModule->register_driver(DRIVER_NAME, NULL, 0, NULL);
|
||||
gUSBModule->install_notify(DRIVER_NAME, ¬ifyHooks);
|
||||
memset(&gSerialDomain, 0, sizeof(gSerialDomain));
|
||||
ddbackground(&gSerialDomain);
|
||||
|
||||
|
||||
scan_bus(B_ISA_BUS);
|
||||
scan_bus(B_PCI_BUS);
|
||||
|
||||
|
||||
// XXX: ISA cards
|
||||
// XXX: pcmcia
|
||||
|
||||
|
||||
TRACE_FUNCRET("< init_driver() returns\n");
|
||||
return B_OK;
|
||||
|
||||
err_none:
|
||||
delete_sem(gDriverLock);
|
||||
err_sem:
|
||||
put_module(B_CONFIG_MANAGER_FOR_DRIVER_MODULE_NAME);
|
||||
err_cm:
|
||||
put_module(B_ISA_MODULE_NAME);
|
||||
err_isa:
|
||||
put_module(B_PCI_MODULE_NAME);
|
||||
err_pci:
|
||||
put_module(B_TTY_MODULE_NAME);
|
||||
err_tty:
|
||||
TRACE_FUNCRET("< init_driver() returns %s\n", strerror(status));
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
@@ -164,11 +607,16 @@ uninit_driver()
|
||||
{
|
||||
TRACE_FUNCALLS("> uninit_driver()\n");
|
||||
|
||||
gUSBModule->uninstall_notify(DRIVER_NAME);
|
||||
//gUSBModule->uninstall_notify(DRIVER_NAME);
|
||||
acquire_sem(gDriverLock);
|
||||
|
||||
for (int32 i = 0; i < DEVICES_COUNT; i++) {
|
||||
if (gSerialDevices[i]) {
|
||||
/*
|
||||
if (gSerialDevices[i]->Master() == gSerialDevices[i])
|
||||
remove_io_interrupt_handler(gSerialDevices[i]->IRQ(),
|
||||
pc_serial_interrupt, gSerialDevices[i]);
|
||||
*/
|
||||
delete gSerialDevices[i];
|
||||
gSerialDevices[i] = NULL;
|
||||
}
|
||||
@@ -178,7 +626,9 @@ uninit_driver()
|
||||
free(gDeviceNames[i]);
|
||||
|
||||
delete_sem(gDriverLock);
|
||||
put_module(B_USB_MODULE_NAME);
|
||||
put_module(B_CONFIG_MANAGER_FOR_DRIVER_MODULE_NAME);
|
||||
put_module(B_ISA_MODULE_NAME);
|
||||
put_module(B_PCI_MODULE_NAME);
|
||||
put_module(B_TTY_MODULE_NAME);
|
||||
|
||||
TRACE_FUNCRET("< uninit_driver() returns\n");
|
||||
@@ -202,6 +652,36 @@ pc_serial_service(struct tty *ptty, struct ddrover *ddr, uint flags)
|
||||
}
|
||||
|
||||
|
||||
int32
|
||||
pc_serial_interrupt(void *arg)
|
||||
{
|
||||
int32 ret;
|
||||
SerialDevice *master = (SerialDevice *)arg;
|
||||
TRACE_FUNCALLS("> pc_serial_interrupt(%p)\n", arg);
|
||||
|
||||
if (!master)
|
||||
return B_UNHANDLED_INTERRUPT;
|
||||
|
||||
ret = master->InterruptHandler();
|
||||
return ret;
|
||||
|
||||
|
||||
for (int32 i = 0; i < DEVICES_COUNT; i++) {
|
||||
if (gSerialDevices[i] && gSerialDevices[i]->Master() == master) {
|
||||
ret = gSerialDevices[i]->InterruptHandler();
|
||||
// XXX: handle more than 1 ?
|
||||
if (ret != B_UNHANDLED_INTERRUPT) {
|
||||
TRACE_FUNCRET("< pc_serial_interrupt() returns: true\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TRACE_FUNCRET("< pc_serial_interrupt() returns: unhandled\n");
|
||||
return B_UNHANDLED_INTERRUPT;
|
||||
}
|
||||
|
||||
|
||||
/* pc_serial_open - handle open() calls */
|
||||
static status_t
|
||||
pc_serial_open(const char *name, uint32 flags, void **cookie)
|
||||
|
||||
@@ -11,6 +11,9 @@
|
||||
#include <OS.h>
|
||||
#include <KernelExport.h>
|
||||
#include <Drivers.h>
|
||||
#include <ISA.h>
|
||||
#include <PCI.h>
|
||||
#include <config_manager.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef __HAIKU__
|
||||
@@ -25,9 +28,71 @@ extern "C" {
|
||||
#include <ttylayer.h>
|
||||
}
|
||||
|
||||
|
||||
// whether we should handle default COM ports
|
||||
#define HANDLE_ISA_COM
|
||||
|
||||
#define DRIVER_NAME "pc_serial" // driver name for debug output
|
||||
#define DEVICES_COUNT 20 // max simultaneously open devices
|
||||
|
||||
#ifdef HANDLE_ISA_COM
|
||||
#define DEVFS_BASE "ports/my_serial"
|
||||
#else
|
||||
// avoid clashing with BeOS zz driver
|
||||
#define DEVFS_BASE "ports/pc_serial"
|
||||
#endif
|
||||
|
||||
|
||||
// no user serviceable part beyond this point
|
||||
|
||||
// more PCI serial APIs
|
||||
#ifndef PCI_serial_16650
|
||||
#define PCI_serial_16650 0x03 /* 16650-compatible serial controller */
|
||||
#define PCI_serial_16750 0x04 /* 16750-compatible serial controller */
|
||||
#define PCI_serial_16850 0x05 /* 16850-compatible serial controller */
|
||||
#define PCI_serial_16950 0x06 /* 16950-compatible serial controller */
|
||||
#endif
|
||||
|
||||
class SerialDevice;
|
||||
|
||||
struct port_constraints {
|
||||
uint32 minsize;
|
||||
uint32 maxsize;
|
||||
uint32 split; // range to split I/O ports for each device
|
||||
};
|
||||
|
||||
#define PCI_INVAL 0xffff
|
||||
struct serial_support_descriptor {
|
||||
bus_type bus; // B_*_BUS
|
||||
const char *name;
|
||||
const uint32 *bauds;
|
||||
// not yet used
|
||||
SerialDevice *(*instanciator)(struct serial_support_descriptor *desc);
|
||||
// I/O port constrains (which ranges to use, how to split them)
|
||||
struct port_constraints constraints;
|
||||
// bus specific stuff here...
|
||||
struct {
|
||||
// for both ISA & PCI
|
||||
uchar class_base;
|
||||
uchar class_sub;
|
||||
uchar class_api; // or PCI_undefined
|
||||
// for PCI: if PCI_INVAL then match class
|
||||
ushort vendor_id;
|
||||
ushort device_id;
|
||||
} match;
|
||||
};
|
||||
typedef struct serial_support_descriptor serial_support_descriptor;
|
||||
|
||||
|
||||
struct serial_config_descriptor {
|
||||
bus_type bus; // B_*_BUS
|
||||
struct serial_support_descriptor *descriptor;
|
||||
union {
|
||||
struct pci_info pci;
|
||||
} d;
|
||||
};
|
||||
|
||||
|
||||
/* Some usefull helper defines ... */
|
||||
#define SIZEOF(array) (sizeof(array) / sizeof(array[0])) /* size of array */
|
||||
/* This one rounds the size to integral count of segs (segments) */
|
||||
@@ -35,6 +100,9 @@ extern "C" {
|
||||
/* Default device buffer size */
|
||||
#define DEF_BUFFER_SIZE 0x200
|
||||
|
||||
// XXX: sort up the mess in termios.h on B* !
|
||||
#define BLAST B230400
|
||||
|
||||
/* line coding defines ... Come from CDC USB specs? */
|
||||
#define LC_STOP_BIT_1 0
|
||||
#define LC_STOP_BIT_2 2
|
||||
@@ -55,42 +123,18 @@ typedef struct pc_serial_line_coding_s {
|
||||
#define CLS_LINE_DTR 0x0001
|
||||
#define CLS_LINE_RTS 0x0002
|
||||
|
||||
/* attributes etc ...*/
|
||||
#ifndef USB_EP_ADDR_DIR_IN
|
||||
#define USB_EP_ADDR_DIR_IN 0x80
|
||||
#define USB_EP_ADDR_DIR_OUT 0x00
|
||||
#endif
|
||||
|
||||
#ifndef USB_EP_ATTR_CONTROL
|
||||
#define USB_EP_ATTR_CONTROL 0x00
|
||||
#define USB_EP_ATTR_ISOCHRONOUS 0x01
|
||||
#define USB_EP_ATTR_BULK 0x02
|
||||
#define USB_EP_ATTR_INTERRUPT 0x03
|
||||
#endif
|
||||
|
||||
/* USB class - communication devices */
|
||||
#define USB_DEV_CLASS_COMM 0x02
|
||||
#define USB_INT_CLASS_CDC 0x02
|
||||
#define USB_INT_SUBCLASS_ACM 0x02
|
||||
#define USB_INT_CLASS_CDC_DATA 0x0a
|
||||
#define USB_INT_SUBCLASS_DATA 0x00
|
||||
|
||||
// communication device subtypes
|
||||
#define FUNCTIONAL_SUBTYPE_UNION 0x06
|
||||
|
||||
extern config_manager_for_driver_module_info *gConfigManagerModule;
|
||||
extern isa_module_info *gISAModule;
|
||||
extern pci_module_info *gPCIModule;
|
||||
extern tty_module_info *gTTYModule;
|
||||
extern struct ddomain gSerialDomain;
|
||||
|
||||
extern "C" {
|
||||
status_t pc_serial_device_added(pc_device device, void **cookie);
|
||||
status_t pc_serial_device_removed(void *cookie);
|
||||
|
||||
status_t init_hardware();
|
||||
void uninit_driver();
|
||||
|
||||
bool pc_serial_service(struct tty *ptty, struct ddrover *ddr, uint flags);
|
||||
int32 pc_serial_interrupt(void *arg);
|
||||
|
||||
status_t pc_serial_open(const char *name, uint32 flags, void **cookie);
|
||||
status_t pc_serial_read(void *cookie, off_t position, void *buffer, size_t *numBytes);
|
||||
|
||||
@@ -1,4 +1,10 @@
|
||||
|
||||
references:
|
||||
http://www.beyondlogic.org/serial/serial.htm
|
||||
http://jamesthornton.com/freebsd/articles/serial-uart/
|
||||
http://dugeem.net/david/work/freebsd-serial.html
|
||||
http://dugeem.net/david/work/etc/sio.c
|
||||
|
||||
if you can understand it:
|
||||
http://lxr.linux.no/linux+v2.6.28.7/drivers/serial/
|
||||
|
||||
|
||||
@@ -6,24 +6,23 @@
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
#include "SerialDevice.h"
|
||||
#include "USB3.h"
|
||||
#include "UART.h"
|
||||
|
||||
#include "ACM.h"
|
||||
#include "FTDI.h"
|
||||
#include "KLSI.h"
|
||||
#include "Prolific.h"
|
||||
|
||||
SerialDevice::SerialDevice(usb_device device, uint16 vendorID,
|
||||
uint16 productID, const char *description)
|
||||
: fDevice(device),
|
||||
fVendorID(vendorID),
|
||||
fProductID(productID),
|
||||
fDescription(description),
|
||||
SerialDevice::SerialDevice(const struct serial_support_descriptor *device,
|
||||
uint32 ioBase, uint32 irq, const SerialDevice *master)
|
||||
: /*fSupportDescriptor(device->descriptor),
|
||||
fDevice(device),
|
||||
fDescription(device->descriptor->name),*/
|
||||
fSupportDescriptor(device),
|
||||
fDevice(NULL),
|
||||
fDescription(device->name),
|
||||
//
|
||||
fDeviceOpen(false),
|
||||
fDeviceRemoved(false),
|
||||
fControlPipe(0),
|
||||
fReadPipe(0),
|
||||
fWritePipe(0),
|
||||
fBus(device->bus),
|
||||
fIOBase(ioBase),
|
||||
fIRQ(irq),
|
||||
fMaster(master),
|
||||
fBufferArea(-1),
|
||||
fReadBuffer(NULL),
|
||||
fReadBufferSize(ROUNDUP(DEF_BUFFER_SIZE, 16)),
|
||||
@@ -40,6 +39,7 @@ SerialDevice::SerialDevice(usb_device device, uint16 vendorID,
|
||||
{
|
||||
memset(&fTTYFile, 0, sizeof(ttyfile));
|
||||
memset(&fTTY, 0, sizeof(tty));
|
||||
memset(&fRover, 0, sizeof(ddrover));
|
||||
}
|
||||
|
||||
|
||||
@@ -75,38 +75,79 @@ SerialDevice::Init()
|
||||
|
||||
fWriteBuffer = fReadBuffer + fReadBufferSize;
|
||||
fInterruptBuffer = fWriteBuffer + fWriteBufferSize;
|
||||
|
||||
// disable DLAB
|
||||
WriteReg8(LCR, 0);
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
SerialDevice::SetControlPipe(usb_pipe handle)
|
||||
{
|
||||
fControlPipe = handle;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
SerialDevice::SetReadPipe(usb_pipe handle)
|
||||
{
|
||||
fReadPipe = handle;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
SerialDevice::SetWritePipe(usb_pipe handle)
|
||||
{
|
||||
fWritePipe = handle;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
SerialDevice::SetModes()
|
||||
{
|
||||
struct termios tios;
|
||||
memcpy(&tios, &fTTY.t, sizeof(struct termios));
|
||||
//TRACE_FUNCRES(trace_termios, &tios);
|
||||
spin(10000);
|
||||
uint32 baudIndex = tios.c_cflag & CBAUD;
|
||||
if (baudIndex > BLAST)
|
||||
baudIndex = BLAST;
|
||||
|
||||
uint8 lcr = 0;
|
||||
uint16 divisor = SupportDescriptor()->bauds[baudIndex];
|
||||
|
||||
switch (tios.c_cflag & CSIZE) {
|
||||
#if CS5 != CS7
|
||||
// in case someday...
|
||||
case CS5:
|
||||
lcr |= LCR_5BIT;
|
||||
break;
|
||||
case CS6:
|
||||
lcr |= LCR_6BIT;
|
||||
break;
|
||||
#endif
|
||||
case CS7:
|
||||
lcr |= LCR_7BIT;
|
||||
break;
|
||||
case CS8:
|
||||
default:
|
||||
lcr |= LCR_8BIT;
|
||||
break;
|
||||
}
|
||||
|
||||
if (tios.c_cflag & CSTOPB)
|
||||
lcr |= LCR_2STOP;
|
||||
if (tios.c_cflag & PARENB)
|
||||
lcr |= LCR_P_EN;
|
||||
if (tios.c_cflag & PARODD == 0)
|
||||
lcr |= LCR_P_EVEN;
|
||||
|
||||
if (baudIndex == B0) {
|
||||
// disable
|
||||
MaskReg8(MCR, MCR_DTR);
|
||||
} else {
|
||||
// set FCR now,
|
||||
// 16650 and later chips have another reg at 2 when DLAB=1
|
||||
uint8 fcr = FCR_ENABLE | FCR_RX_RST | FCR_TX_RST | FCR_F_8;
|
||||
// enable fifo
|
||||
//fcr = 0;
|
||||
WriteReg8(FCR, fcr);
|
||||
|
||||
// unmask the divisor latch regs
|
||||
WriteReg8(LCR, LCR_DLAB);
|
||||
// set divisor
|
||||
WriteReg8(DLLB, divisor & 0x00ff);
|
||||
WriteReg8(DLHB, divisor >> 8);
|
||||
//WriteReg8(2,0);
|
||||
|
||||
}
|
||||
// set control lines, and disable divisor latch reg
|
||||
WriteReg8(LCR, lcr);
|
||||
|
||||
|
||||
#if 0
|
||||
uint16 newControl = fControlOut;
|
||||
TRACE_FUNCRES(trace_termios, &tios);
|
||||
|
||||
static uint32 baudRates[] = {
|
||||
0x00000000, //B0
|
||||
@@ -132,11 +173,6 @@ SerialDevice::SetModes()
|
||||
0x000E1000, //921600
|
||||
};
|
||||
|
||||
uint32 baudCount = sizeof(baudRates) / sizeof(baudRates[0]);
|
||||
uint32 baudIndex = tios.c_cflag & CBAUD;
|
||||
if (baudIndex > baudCount)
|
||||
baudIndex = baudCount - 1;
|
||||
|
||||
usb_serial_line_coding lineCoding;
|
||||
lineCoding.speed = baudRates[baudIndex];
|
||||
lineCoding.stopbits = (tios.c_cflag & CSTOPB) ? LC_STOP_BIT_2 : LC_STOP_BIT_1;
|
||||
@@ -172,64 +208,103 @@ SerialDevice::SetModes()
|
||||
fLineCoding.parity);
|
||||
SetLineCoding(&fLineCoding);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
SerialDevice::Service(struct tty *ptty, struct ddrover *ddr, uint flags)
|
||||
{
|
||||
uint8 msr;
|
||||
status_t err;
|
||||
|
||||
if (&fTTY != ptty)
|
||||
return false;
|
||||
|
||||
TRACE_ALWAYS("%s(,,0x%08lx)\n", __FUNCTION__, flags);
|
||||
|
||||
if (flags <= TTYGETSIGNALS) {
|
||||
switch (flags) {
|
||||
case TTYENABLE:
|
||||
TRACE("TTYENABLE\n");
|
||||
gTTYModule->ttyhwsignal(ptty, ddr, TTYHWDCD, false);
|
||||
gTTYModule->ttyhwsignal(ptty, ddr, TTYHWCTS, true);
|
||||
fControlOut = CLS_LINE_DTR | CLS_LINE_RTS;
|
||||
SetControlLineState(fControlOut);
|
||||
|
||||
SetModes();
|
||||
err = install_io_interrupt_handler(IRQ(), pc_serial_interrupt, this, 0);
|
||||
TRACE_ALWAYS("installing irq handler for %d: %s\n", IRQ(), strerror(err));
|
||||
msr = ReadReg8(MSR);
|
||||
gTTYModule->ttyhwsignal(ptty, ddr, TTYHWDCD, msr & MSR_DCD);
|
||||
gTTYModule->ttyhwsignal(ptty, ddr, TTYHWCTS, msr & MSR_CTS);
|
||||
//
|
||||
WriteReg8(MCR, MCR_DTR | MCR_RTS | MCR_IRQ_EN /*| MCR_LOOP*//*XXXXXXX*/);
|
||||
// enable irqs
|
||||
WriteReg8(IER, IER_RLS | IER_MS | IER_RDA);
|
||||
//WriteReg8(IER, IER_RDA);
|
||||
break;
|
||||
|
||||
case TTYDISABLE:
|
||||
TRACE("TTYDISABLE\n");
|
||||
gTTYModule->ttyhwsignal(ptty, ddr, TTYHWDCD, false);
|
||||
fControlOut = 0x0;
|
||||
SetControlLineState(fControlOut);
|
||||
// remove the handler
|
||||
remove_io_interrupt_handler(IRQ(), pc_serial_interrupt, this);
|
||||
// disable IRQ
|
||||
WriteReg8(IER, 0);
|
||||
WriteReg8(MCR, 0);
|
||||
msr = ReadReg8(MSR);
|
||||
gTTYModule->ttyhwsignal(ptty, ddr, TTYHWDCD, msr & MSR_DCD);
|
||||
break;
|
||||
|
||||
case TTYISTOP:
|
||||
TRACE("TTYISTOP\n");
|
||||
fInputStopped = true;
|
||||
gTTYModule->ttyhwsignal(ptty, ddr, TTYHWCTS, false);
|
||||
MaskReg8(MCR, MCR_RTS);
|
||||
//fInputStopped = true;
|
||||
//gTTYModule->ttyhwsignal(ptty, ddr, TTYHWCTS, false);
|
||||
break;
|
||||
|
||||
case TTYIRESUME:
|
||||
TRACE("TTYIRESUME\n");
|
||||
gTTYModule->ttyhwsignal(ptty, ddr, TTYHWCTS, true);
|
||||
fInputStopped = false;
|
||||
OrReg8(MCR, MCR_RTS);
|
||||
//gTTYModule->ttyhwsignal(ptty, ddr, TTYHWCTS, true);
|
||||
//fInputStopped = false;
|
||||
break;
|
||||
|
||||
case TTYGETSIGNALS:
|
||||
TRACE("TTYGETSIGNALS\n");
|
||||
gTTYModule->ttyhwsignal(ptty, ddr, TTYHWDCD, true);
|
||||
gTTYModule->ttyhwsignal(ptty, ddr, TTYHWCTS, true);
|
||||
gTTYModule->ttyhwsignal(ptty, ddr, TTYHWDSR, false);
|
||||
gTTYModule->ttyhwsignal(ptty, ddr, TTYHWRI, false);
|
||||
msr = ReadReg8(MSR);
|
||||
gTTYModule->ttyhwsignal(ptty, ddr, TTYHWDCD, msr & MSR_DCD);
|
||||
gTTYModule->ttyhwsignal(ptty, ddr, TTYHWCTS, msr & MSR_CTS);
|
||||
gTTYModule->ttyhwsignal(ptty, ddr, TTYHWDSR, msr & MSR_DSR);
|
||||
gTTYModule->ttyhwsignal(ptty, ddr, TTYHWRI, msr & MSR_RI);
|
||||
break;
|
||||
|
||||
case TTYSETMODES:
|
||||
TRACE("TTYSETMODES\n");
|
||||
SetModes();
|
||||
//WriteReg8(IER, IER_RLS | IER_MS | IER_RDA);
|
||||
break;
|
||||
|
||||
case TTYOSTART:
|
||||
TRACE("TTYOSTART\n");
|
||||
// enable irqs
|
||||
WriteReg8(IER, IER_RLS | IER_MS | IER_RDA | IER_THRE);
|
||||
break;
|
||||
case TTYOSYNC:
|
||||
TRACE("TTYOSYNC\n");
|
||||
return (ReadReg8(LSR) & (LSR_THRE | LSR_TSRE)) == (LSR_THRE | LSR_TSRE);
|
||||
break;
|
||||
case TTYSETBREAK:
|
||||
TRACE("TTYSETBREAK\n");
|
||||
OrReg8(LCR, LCR_BREAK);
|
||||
break;
|
||||
case TTYCLRBREAK:
|
||||
TRACE("TTYCLRBREAK\n");
|
||||
MaskReg8(LCR, LCR_BREAK);
|
||||
break;
|
||||
case TTYSETDTR:
|
||||
TRACE("TTYSETDTR\n");
|
||||
OrReg8(MCR, MCR_DTR);
|
||||
break;
|
||||
case TTYCLRDTR:
|
||||
TRACE("TTY other\n");
|
||||
TRACE("TTYCLRDTR\n");
|
||||
MaskReg8(MCR, MCR_DTR);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -240,6 +315,86 @@ SerialDevice::Service(struct tty *ptty, struct ddrover *ddr, uint flags)
|
||||
}
|
||||
|
||||
|
||||
int32
|
||||
SerialDevice::InterruptHandler()
|
||||
{
|
||||
int32 ret = B_UNHANDLED_INTERRUPT;
|
||||
gTTYModule->ddrstart(&fRover);
|
||||
gTTYModule->ttyilock(&fTTY, &fRover, true);
|
||||
|
||||
uint8 iir, lsr, msr;
|
||||
int count;
|
||||
|
||||
while (((iir = ReadReg8(IIR)) & IIR_PENDING) == 0) { // 0 means yes
|
||||
int fifoavail = 1;
|
||||
|
||||
//DEBUG
|
||||
// for (count = 0; ReadReg8(LSR) & LSR_DR; count++)
|
||||
// gTTYModule->ttyin(&fTTY, &fRover, ReadReg8(RBR));
|
||||
|
||||
switch (iir & (IIR_IMASK | IIR_TO)) {
|
||||
case IIR_THRE:
|
||||
dprintf("IIR_THRE\n");
|
||||
// check how much fifo we can use
|
||||
//XXX: move to Init() ?
|
||||
if (iir & IIR_FMASK == IIR_FMASK)
|
||||
fifoavail = 16;
|
||||
if (iir & IIR_F64EN)
|
||||
fifoavail = 64;
|
||||
for (int i = 0; i < fifoavail; i++) {
|
||||
int chr = gTTYModule->ttyout(&fTTY, &fRover);
|
||||
if (chr < 0) {
|
||||
//WriteReg8(THB, (uint8)chr);
|
||||
break;
|
||||
}
|
||||
WriteReg8(THB, (uint8)chr);
|
||||
}
|
||||
break;
|
||||
case IIR_TO:
|
||||
case IIR_TO | IIR_RDA:
|
||||
// timeout: FALLTHROUGH
|
||||
case IIR_RDA:
|
||||
dprintf("IIR_TO/RDA\n");
|
||||
// while data is ready... get it
|
||||
while (ReadReg8(LSR) & LSR_DR)
|
||||
gTTYModule->ttyin(&fTTY, &fRover, ReadReg8(RBR));
|
||||
break;
|
||||
case IIR_RLS:
|
||||
dprintf("IIR_RLS\n");
|
||||
// ack
|
||||
lsr = ReadReg8(LSR);
|
||||
//XXX: handle this somehow
|
||||
break;
|
||||
case IIR_MS:
|
||||
dprintf("IIR_MS\n");
|
||||
// modem signals changed
|
||||
msr = ReadReg8(MSR);
|
||||
if (msr & MSR_DDCD)
|
||||
gTTYModule->ttyhwsignal(&fTTY, &fRover, TTYHWDCD, msr & MSR_DCD);
|
||||
if (msr & MSR_DCTS)
|
||||
gTTYModule->ttyhwsignal(&fTTY, &fRover, TTYHWCTS, msr & MSR_CTS);
|
||||
if (msr & MSR_DDSR)
|
||||
gTTYModule->ttyhwsignal(&fTTY, &fRover, TTYHWDSR, msr & MSR_DSR);
|
||||
if (msr & MSR_TERI)
|
||||
gTTYModule->ttyhwsignal(&fTTY, &fRover, TTYHWRI, msr & MSR_RI);
|
||||
break;
|
||||
default:
|
||||
dprintf("IIR_?\n");
|
||||
// something happened
|
||||
break;
|
||||
}
|
||||
ret = B_HANDLED_INTERRUPT;
|
||||
dprintf("IRQ:h\n");
|
||||
}
|
||||
|
||||
|
||||
gTTYModule->ttyilock(&fTTY, &fRover, false);
|
||||
gTTYModule->ddrdone(&fRover);
|
||||
dprintf("IRQ:r\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
SerialDevice::Open(uint32 flags)
|
||||
{
|
||||
@@ -249,7 +404,7 @@ SerialDevice::Open(uint32 flags)
|
||||
if (fDeviceRemoved)
|
||||
return B_DEV_NOT_READY;
|
||||
|
||||
gTTYModule->ttyinit(&fTTY, true);
|
||||
gTTYModule->ttyinit(&fTTY, false);
|
||||
fTTYFile.tty = &fTTY;
|
||||
fTTYFile.flags = flags;
|
||||
ResetDevice();
|
||||
@@ -259,7 +414,7 @@ SerialDevice::Open(uint32 flags)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
gTTYModule->ddacquire(ddr, &gSerialDomain);
|
||||
status_t status = gTTYModule->ttyopen(&fTTYFile, ddr, usb_serial_service);
|
||||
status_t status = gTTYModule->ttyopen(&fTTYFile, ddr, pc_serial_service);
|
||||
gTTYModule->ddrdone(ddr);
|
||||
|
||||
if (status < B_OK) {
|
||||
@@ -267,6 +422,7 @@ SerialDevice::Open(uint32 flags)
|
||||
return status;
|
||||
}
|
||||
|
||||
#if 0
|
||||
fDeviceThread = spawn_kernel_thread(DeviceThread, "usb_serial device thread",
|
||||
B_NORMAL_PRIORITY, this);
|
||||
|
||||
@@ -285,6 +441,7 @@ SerialDevice::Open(uint32 flags)
|
||||
if (status < B_OK)
|
||||
TRACE_ALWAYS("failed to queue initial interrupt\n");
|
||||
|
||||
#endif
|
||||
fDeviceOpen = true;
|
||||
return B_OK;
|
||||
}
|
||||
@@ -298,24 +455,31 @@ SerialDevice::Read(char *buffer, size_t *numBytes)
|
||||
return B_DEV_NOT_READY;
|
||||
}
|
||||
|
||||
status_t status;
|
||||
#if 0
|
||||
status_t status = mutex_lock(&fReadLock);
|
||||
if (status != B_OK) {
|
||||
TRACE_ALWAYS("read: failed to get read lock\n");
|
||||
*numBytes = 0;
|
||||
return status;
|
||||
}
|
||||
#endif
|
||||
|
||||
struct ddrover *ddr = gTTYModule->ddrstart(NULL);
|
||||
if (!ddr) {
|
||||
*numBytes = 0;
|
||||
#if 0
|
||||
mutex_unlock(&fReadLock);
|
||||
#endif
|
||||
return B_NO_MEMORY;
|
||||
}
|
||||
|
||||
status = gTTYModule->ttyread(&fTTYFile, ddr, buffer, numBytes);
|
||||
gTTYModule->ddrdone(ddr);
|
||||
|
||||
#if 0
|
||||
mutex_unlock(&fReadLock);
|
||||
#endif
|
||||
return status;
|
||||
}
|
||||
|
||||
@@ -324,8 +488,19 @@ status_t
|
||||
SerialDevice::Write(const char *buffer, size_t *numBytes)
|
||||
{
|
||||
size_t bytesLeft = *numBytes;
|
||||
*numBytes = 0;
|
||||
//*numBytes = 0;
|
||||
|
||||
status_t status = EINVAL;
|
||||
struct ddrover *ddr = gTTYModule->ddrstart(NULL);
|
||||
if (!ddr) {
|
||||
*numBytes = 0;
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
status = gTTYModule->ttywrite(&fTTYFile, ddr, buffer, numBytes);
|
||||
gTTYModule->ddrdone(ddr);
|
||||
|
||||
#if 0
|
||||
status_t status = mutex_lock(&fWriteLock);
|
||||
if (status != B_OK) {
|
||||
TRACE_ALWAYS("write: failed to get write lock\n");
|
||||
@@ -373,6 +548,7 @@ SerialDevice::Write(const char *buffer, size_t *numBytes)
|
||||
}
|
||||
|
||||
mutex_unlock(&fWriteLock);
|
||||
#endif
|
||||
return status;
|
||||
}
|
||||
|
||||
@@ -431,9 +607,11 @@ SerialDevice::Close()
|
||||
OnClose();
|
||||
|
||||
if (!fDeviceRemoved) {
|
||||
#if 0
|
||||
gUSBModule->cancel_queued_transfers(fReadPipe);
|
||||
gUSBModule->cancel_queued_transfers(fWritePipe);
|
||||
gUSBModule->cancel_queued_transfers(fControlPipe);
|
||||
#endif
|
||||
}
|
||||
|
||||
struct ddrover *ddr = gTTYModule->ddrstart(NULL);
|
||||
@@ -473,12 +651,14 @@ SerialDevice::Removed()
|
||||
// we need to ensure that we do not use the device anymore
|
||||
fStopDeviceThread = true;
|
||||
fInputStopped = false;
|
||||
#if 0
|
||||
gUSBModule->cancel_queued_transfers(fReadPipe);
|
||||
gUSBModule->cancel_queued_transfers(fWritePipe);
|
||||
gUSBModule->cancel_queued_transfers(fControlPipe);
|
||||
#endif
|
||||
|
||||
int32 result = B_OK;
|
||||
wait_for_thread(fDeviceThread, &result);
|
||||
//wait_for_thread(fDeviceThread, &result);
|
||||
fDeviceThread = -1;
|
||||
|
||||
mutex_lock(&fWriteLock);
|
||||
@@ -487,7 +667,7 @@ SerialDevice::Removed()
|
||||
|
||||
|
||||
status_t
|
||||
SerialDevice::AddDevice(const usb_configuration_info *config)
|
||||
SerialDevice::AddDevice(const serial_config_descriptor *config)
|
||||
{
|
||||
// default implementation - does nothing
|
||||
return B_ERROR;
|
||||
@@ -502,13 +682,14 @@ SerialDevice::ResetDevice()
|
||||
}
|
||||
|
||||
|
||||
#if 0
|
||||
status_t
|
||||
SerialDevice::SetLineCoding(usb_serial_line_coding *coding)
|
||||
{
|
||||
// default implementation - does nothing
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
status_t
|
||||
SerialDevice::SetControlLineState(uint16 state)
|
||||
@@ -543,6 +724,7 @@ int32
|
||||
SerialDevice::DeviceThread(void *data)
|
||||
{
|
||||
SerialDevice *device = (SerialDevice *)data;
|
||||
#if 0
|
||||
|
||||
while (!device->fStopDeviceThread) {
|
||||
status_t status = gUSBModule->queue_bulk(device->fReadPipe,
|
||||
@@ -592,6 +774,7 @@ SerialDevice::DeviceThread(void *data)
|
||||
gTTYModule->ddrdone(ddr);
|
||||
}
|
||||
|
||||
#endif
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
@@ -638,14 +821,17 @@ SerialDevice::InterruptCallbackFunction(void *cookie, int32 status,
|
||||
// ToDo: maybe handle those somehow?
|
||||
|
||||
if (status == B_OK && !device->fDeviceRemoved) {
|
||||
#if 0
|
||||
status = gUSBModule->queue_interrupt(device->fControlPipe,
|
||||
device->fInterruptBuffer, device->fInterruptBufferSize,
|
||||
device->InterruptCallbackFunction, device);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
#if 0
|
||||
SerialDevice *
|
||||
SerialDevice::MakeDevice(usb_device device, uint16 vendorID,
|
||||
uint16 productID)
|
||||
@@ -711,3 +897,66 @@ SerialDevice::MakeDevice(usb_device device, uint16 vendorID,
|
||||
|
||||
return new ACMDevice(device, vendorID, productID, "CDC ACM compatible device");
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
uint8
|
||||
SerialDevice::ReadReg8(int reg)
|
||||
{
|
||||
uint8 ret;
|
||||
switch (fBus) {
|
||||
case B_ISA_BUS:
|
||||
ret = gISAModule->read_io_8(IOBase() + reg);
|
||||
break;
|
||||
case B_PCI_BUS:
|
||||
ret = gPCIModule->read_io_8(IOBase() + reg);
|
||||
break;
|
||||
default:
|
||||
TRACE_ALWAYS("%s: unknown bus!\n", __FUNCTION__);
|
||||
ret = 0;
|
||||
//XXX:pcmcia ?
|
||||
}
|
||||
TRACE_ALWAYS("RR8(%d) = %d [%02x]\n", reg, ret, ret);
|
||||
//spin(1000);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void
|
||||
SerialDevice::WriteReg8(int reg, uint8 value)
|
||||
{
|
||||
// TRACE_ALWAYS("WR8(0x%04x+%d, %d [0x%x])\n", IOBase(), reg, value, value);
|
||||
TRACE_ALWAYS("WR8(%d, %d [0x%x])\n", reg, value, value);
|
||||
switch (fBus) {
|
||||
case B_ISA_BUS:
|
||||
gISAModule->write_io_8(IOBase() + reg, value);
|
||||
break;
|
||||
case B_PCI_BUS:
|
||||
gPCIModule->write_io_8(IOBase() + reg, value);
|
||||
break;
|
||||
default:
|
||||
TRACE_ALWAYS("%s: unknown bus!\n", __FUNCTION__);
|
||||
//XXX:pcmcia ?
|
||||
}
|
||||
//spin(10000);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
SerialDevice::OrReg8(int reg, uint8 value)
|
||||
{
|
||||
WriteReg8(reg, ReadReg8(reg) | value);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
SerialDevice::AndReg8(int reg, uint8 value)
|
||||
{
|
||||
WriteReg8(reg, ReadReg8(reg) & value);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
SerialDevice::MaskReg8(int reg, uint8 value)
|
||||
{
|
||||
WriteReg8(reg, ReadReg8(reg) & ~value);
|
||||
}
|
||||
|
||||
@@ -12,29 +12,26 @@
|
||||
|
||||
class SerialDevice {
|
||||
public:
|
||||
SerialDevice(usb_device device,
|
||||
uint16 vendorID, uint16 productID,
|
||||
const char *description);
|
||||
/* SerialDevice(struct serial_config_descriptor
|
||||
*device, uint32 ioBase, uint32 irq, SerialDevice *master=NULL);*/
|
||||
SerialDevice(const struct serial_support_descriptor
|
||||
*device, uint32 ioBase, uint32 irq, const SerialDevice *master=NULL);
|
||||
virtual ~SerialDevice();
|
||||
|
||||
static SerialDevice * MakeDevice(usb_device device, uint16 vendorID,
|
||||
uint16 productID);
|
||||
static SerialDevice * MakeDevice(struct serial_config_descriptor
|
||||
*device);
|
||||
|
||||
status_t Init();
|
||||
|
||||
usb_device Device() { return fDevice; };
|
||||
uint16 ProductID() { return fProductID; };
|
||||
uint16 VendorID() { return fVendorID; };
|
||||
const char * Description() { return fDescription; };
|
||||
const struct serial_support_descriptor *SupportDescriptor() const
|
||||
{ return fSupportDescriptor; };
|
||||
struct serial_config_descriptor *ConfigDescriptor() const
|
||||
{ return fDevice; };
|
||||
//uint16 ProductID() const { return fProductID; };
|
||||
//uint16 VendorID() const { return fVendorID; };
|
||||
const char * Description() const { return fDescription; };
|
||||
|
||||
void SetControlPipe(usb_pipe handle);
|
||||
usb_pipe ControlPipe() { return fControlPipe; };
|
||||
|
||||
void SetReadPipe(usb_pipe handle);
|
||||
usb_pipe ReadPipe() { return fReadPipe; };
|
||||
|
||||
void SetWritePipe(usb_pipe handle);
|
||||
usb_pipe WritePipe() { return fWritePipe; }
|
||||
const SerialDevice * Master() const { return fMaster ? fMaster : this; };
|
||||
|
||||
char * ReadBuffer() { return fReadBuffer; };
|
||||
size_t ReadBufferSize() { return fReadBufferSize; };
|
||||
@@ -46,6 +43,8 @@ static SerialDevice * MakeDevice(usb_device device, uint16 vendorID,
|
||||
bool Service(struct tty *ptty, struct ddrover *ddr,
|
||||
uint flags);
|
||||
|
||||
int32 InterruptHandler();
|
||||
|
||||
status_t Open(uint32 flags);
|
||||
status_t Read(char *buffer, size_t *numBytes);
|
||||
status_t Write(const char *buffer, size_t *numBytes);
|
||||
@@ -60,11 +59,11 @@ static SerialDevice * MakeDevice(usb_device device, uint16 vendorID,
|
||||
bool IsRemoved() { return fDeviceRemoved; };
|
||||
|
||||
/* virtual interface to be overriden as necessary */
|
||||
virtual status_t AddDevice(const usb_configuration_info *config);
|
||||
virtual status_t AddDevice(const struct serial_config_descriptor *device);
|
||||
|
||||
virtual status_t ResetDevice();
|
||||
|
||||
virtual status_t SetLineCoding(usb_serial_line_coding *coding);
|
||||
//virtual status_t SetLineCoding(usb_serial_line_coding *coding);
|
||||
virtual status_t SetControlLineState(uint16 state);
|
||||
|
||||
virtual void OnRead(char **buffer, size_t *numBytes);
|
||||
@@ -72,6 +71,9 @@ virtual void OnWrite(const char *buffer, size_t *numBytes,
|
||||
size_t *packetBytes);
|
||||
virtual void OnClose();
|
||||
|
||||
uint32 IOBase() const { return fIOBase; };
|
||||
uint32 IRQ() const { return fIRQ; };
|
||||
|
||||
protected:
|
||||
void SetReadBufferSize(size_t size) { fReadBufferSize = size; };
|
||||
void SetWriteBufferSize(size_t size) { fWriteBufferSize = size; };
|
||||
@@ -89,20 +91,25 @@ static void InterruptCallbackFunction(void *cookie,
|
||||
int32 status, void *data,
|
||||
uint32 actualLength);
|
||||
|
||||
usb_device fDevice; // USB device handle
|
||||
uint16 fVendorID;
|
||||
uint16 fProductID;
|
||||
uint8 ReadReg8(int reg);
|
||||
void WriteReg8(int reg, uint8 value);
|
||||
void OrReg8(int reg, uint8 value);
|
||||
void AndReg8(int reg, uint8 value);
|
||||
void MaskReg8(int reg, uint8 value);
|
||||
|
||||
const struct serial_support_descriptor *fSupportDescriptor;
|
||||
struct serial_config_descriptor *fDevice; // USB device handle
|
||||
const char * fDescription; // informational description
|
||||
bool fDeviceOpen;
|
||||
bool fDeviceRemoved;
|
||||
|
||||
/* communication pipes */
|
||||
usb_pipe fControlPipe;
|
||||
usb_pipe fReadPipe;
|
||||
usb_pipe fWritePipe;
|
||||
bus_type fBus;
|
||||
uint32 fIOBase;
|
||||
uint32 fIRQ;
|
||||
const SerialDevice * fMaster;
|
||||
|
||||
/* line coding */
|
||||
usb_serial_line_coding fLineCoding;
|
||||
//usb_serial_line_coding fLineCoding;
|
||||
|
||||
/* data buffers */
|
||||
area_id fBufferArea;
|
||||
@@ -129,6 +136,7 @@ static void InterruptCallbackFunction(void *cookie,
|
||||
bool fInputStopped;
|
||||
struct ttyfile fTTYFile;
|
||||
struct tty fTTY;
|
||||
struct ddrover fRover;
|
||||
|
||||
/* device thread management */
|
||||
thread_id fDeviceThread;
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
*/
|
||||
#include "Tracing.h"
|
||||
#include "Driver.h"
|
||||
#include "USB3.h"
|
||||
|
||||
#include <stdio.h> //sprintf
|
||||
#include <unistd.h> //posix file i/o - create, write, close
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
* Copyright (c) 2003 by Siarzhuk Zharski <imker@gmx.li>
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
#ifndef _USB_SERIAL_TRACING_H_
|
||||
#define _USB_SERIAL_TRACING_H_
|
||||
#ifndef _PC_SERIAL_TRACING_H_
|
||||
#define _PC_SERIAL_TRACING_H_
|
||||
|
||||
void load_settings();
|
||||
void create_log_file();
|
||||
@@ -36,4 +36,4 @@ void trace_str(struct str *str);
|
||||
void trace_winsize(struct winsize *ws);
|
||||
void trace_tty(struct tty *tty);
|
||||
|
||||
#endif //_USB_SERIAL_TRACING_H_
|
||||
#endif //_PC_SERIAL_TRACING_H_
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* Copyright 2009, Haiku, Inc.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* François Revol <revol@free.fr>
|
||||
*/
|
||||
#ifndef _UART_H_
|
||||
#define _UART_H_
|
||||
|
||||
// References:
|
||||
// http://www.beyondlogic.org/serial/serial.htm
|
||||
|
||||
|
||||
// 8250 UART registers
|
||||
#define THB 0
|
||||
#define RBR 0
|
||||
#define DLLB 0
|
||||
#define IER 1
|
||||
#define DLHB 1
|
||||
#define IIR 2
|
||||
#define FCR 2
|
||||
#define LCR 3
|
||||
#define MCR 4
|
||||
#define LSR 5
|
||||
#define MSR 6
|
||||
#define SR 7
|
||||
|
||||
// bits
|
||||
#define IER_RDA (1 << 0)
|
||||
#define IER_THRE (1 << 1)
|
||||
#define IER_RLS (1 << 2)
|
||||
#define IER_MS (1 << 3)
|
||||
#define IER_SM (1 << 4) // 16750
|
||||
#define IER_LPM (1 << 5) // 16750
|
||||
|
||||
#define IIR_PENDING (1 << 0)
|
||||
#define IIR_IMASK (0x03 << 1)
|
||||
#define IIR_MS (0 << 1)
|
||||
#define IIR_THRE (1 << 1)
|
||||
#define IIR_RDA (2 << 1)
|
||||
#define IIR_RLS (3 << 1)
|
||||
#define IIR_TO (1 << 3) // 16550
|
||||
#define IIR_F64EN (1 << 5) // 16750
|
||||
#define IIR_FMASK (3 << 6)
|
||||
|
||||
#define FCR_ENABLE (1 << 0)
|
||||
#define FCR_RX_RST (1 << 1)
|
||||
#define FCR_TX_RST (1 << 2)
|
||||
#define FCR_DMA_EN (1 << 3)
|
||||
#define FCR_F64EN (1 << 5)
|
||||
#define FCR_FMASK (0x03 << 6)
|
||||
#define FCR_F_1 (0 << 6)
|
||||
#define FCR_F_4 (1 << 6)
|
||||
#define FCR_F_8 (2 << 6)
|
||||
#define FCR_F_14 (3 << 6)
|
||||
|
||||
#define LCR_5BIT 0
|
||||
#define LCR_6BIT 1
|
||||
#define LCR_7BIT 2
|
||||
#define LCR_8BIT 3
|
||||
#define LCR_2STOP (1 << 2)
|
||||
#define LCR_P_EN (1 << 3)
|
||||
#define LCR_P_EVEN (1 << 4)
|
||||
#define LCR_P_MARK (1 << 5)
|
||||
#define LCR_BREAK (1 << 6)
|
||||
#define LCR_DLAB (1 << 7)
|
||||
|
||||
#define MCR_DTR (1 << 0)
|
||||
#define MCR_RTS (1 << 1)
|
||||
#define MCR_AUX1 (1 << 2)
|
||||
#define MCR_AUX2 (1 << 3)
|
||||
#define MCR_IRQ_EN (1 << 3) // ?
|
||||
#define MCR_LOOP (1 << 4)
|
||||
#define MCR_AUTOFLOW (1 << 5) // 16750
|
||||
|
||||
#define LSR_DR (1 << 0)
|
||||
#define LSR_OVERRUN (1 << 1)
|
||||
#define LSR_PARITY (1 << 2)
|
||||
#define LSR_FRAMING (1 << 3)
|
||||
#define LSR_BREAK (1 << 4)
|
||||
#define LSR_THRE (1 << 5)
|
||||
#define LSR_TSRE (1 << 6)
|
||||
#define LSR_FIFO (1 << 7)
|
||||
|
||||
#define MSR_DCTS (1 << 0)
|
||||
#define MSR_DDSR (1 << 1)
|
||||
#define MSR_TERI (1 << 2)
|
||||
#define MSR_DDCD (1 << 3)
|
||||
#define MSR_CTS (1 << 4)
|
||||
#define MSR_DSR (1 << 5)
|
||||
#define MSR_RI (1 << 6)
|
||||
#define MSR_DCD (1 << 7)
|
||||
|
||||
// speeds
|
||||
//#define BPS_50
|
||||
|
||||
|
||||
#endif // _UART_H_
|
||||
@@ -0,0 +1,36 @@
|
||||
PCI bus, device #4: Communication Device (Serial) [7|0|6]
|
||||
Current configuration: irq [18]
|
||||
io range: min b800 max b800 align 0 len 8
|
||||
io range: min bc00 max bc00 align 0 len 8
|
||||
io range: min c000 max c000 align 0 len 20
|
||||
mem range: min 0 max 0 align 0 len 80201000
|
||||
mem range: min 0 max 0 align 0 len 80201000
|
||||
1 configurations
|
||||
|
||||
Possible configuration #0: irq [18]
|
||||
io range: min b800 max b800 align 0 len 8
|
||||
io range: min bc00 max bc00 align 0 len 8
|
||||
io range: min c000 max c000 align 0 len 20
|
||||
mem range: min 0 max 0 align 0 len 80201000
|
||||
mem range: min 0 max 0 align 0 len 80201000
|
||||
|
||||
Bus-dependent information:
|
||||
vendor id: 1415, device id: 1501
|
||||
|
||||
PCI bus, device #5: Communication Device (Parallel) [7|1|1]
|
||||
Current configuration: irq [18]
|
||||
io range: min c400 max c400 align 0 len 8
|
||||
io range: min c800 max c800 align 0 len 4
|
||||
io range: min cc00 max cc00 align 0 len 20
|
||||
mem range: min 0 max 0 align 0 len 80201000
|
||||
1 configurations
|
||||
|
||||
Possible configuration #0: irq [18]
|
||||
io range: min c400 max c400 align 0 len 8
|
||||
io range: min c800 max c800 align 0 len 4
|
||||
io range: min cc00 max cc00 align 0 len 20
|
||||
mem range: min 0 max 0 align 0 len 80201000
|
||||
|
||||
Bus-dependent information:
|
||||
vendor id: 1415, device id: 1503
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
; poke script
|
||||
; dump 16C952 ID regs
|
||||
|
||||
outb b803 bf
|
||||
outb b802 10
|
||||
outb b803 00
|
||||
; serial_icr_write(off,val)
|
||||
; outb b807 off
|
||||
; outb b805 val
|
||||
; serial_icr_read(off)
|
||||
; outb b807 00
|
||||
; outb b805 40
|
||||
; outb b807 off
|
||||
; inb b805
|
||||
; outb b807 00
|
||||
; outb b805 00
|
||||
|
||||
outb b807 00
|
||||
outb b805 40
|
||||
; id1
|
||||
outb b807 08
|
||||
inb b805
|
||||
; id2
|
||||
outb b807 09
|
||||
inb b805
|
||||
; id3
|
||||
outb b807 0a
|
||||
inb b805
|
||||
; rev
|
||||
outb b807 0b
|
||||
inb b805
|
||||
|
||||
;
|
||||
outb b807 00
|
||||
inb b805
|
||||
|
||||
|
||||
outb b807 00
|
||||
outb b805 00
|
||||
|
||||
|
||||
; disable enhanced control bit
|
||||
outb b803 bf
|
||||
outb b802 00
|
||||
outb b803 00
|
||||
Reference in New Issue
Block a user