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:
François Revol
2009-02-27 00:15:20 +00:00
parent b5aca78a04
commit bbc84914d4
10 changed files with 1107 additions and 141 deletions
@@ -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, &notifyHooks);
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