Start of a PC-style serial port driver. For now it's mostly just copied parts of usb_serial, not yet usable.

git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@27111 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
François Revol
2008-08-21 15:03:14 +00:00
parent d80bf853c6
commit 3cdf783e2b
10 changed files with 1683 additions and 0 deletions
@@ -0,0 +1,54 @@
#ifndef _BEOS_COMPATIBILITY_H_
#define _BEOS_COMPATIBILITY_H_
#ifndef HAIKU_TARGET_PLATFORM_HAIKU
typedef struct mutex {
sem_id sem;
int32 count;
} mutex;
static inline status_t
mutex_init(mutex *ben, const char *name)
{
if (ben == NULL || name == NULL)
return B_BAD_VALUE;
ben->count = 1;
ben->sem = create_sem(0, name);
if (ben->sem >= B_OK)
return B_OK;
return ben->sem;
}
static inline void
mutex_destroy(mutex *ben)
{
delete_sem(ben->sem);
ben->sem = -1;
}
static inline status_t
mutex_lock(mutex *ben)
{
if (atomic_add(&ben->count, -1) <= 0)
return acquire_sem(ben->sem);
return B_OK;
}
static inline status_t
mutex_unlock(mutex *ben)
{
if (atomic_add(&ben->count, 1) < 0)
return release_sem(ben->sem);
return B_OK;
}
#endif // HAIKU_TARGET_PLATFORM_HAIKU
#endif // _BEOS_COMPATIBILITY_H_
@@ -0,0 +1,365 @@
/*
* Copyright (c) 2007-2008 by Michael Lotz
* Heavily based on the original usb_serial driver which is:
*
* Copyright (c) 2003 by Siarzhuk Zharski <[email protected]>
* Distributed under the terms of the MIT License.
*/
#include <KernelExport.h>
#include <Drivers.h>
#include <malloc.h>
#include <stdio.h>
#include <stdlib.h>
#include "Driver.h"
#include "SerialDevice.h"
static const char *sDeviceBaseName = "ports/serial";
SerialDevice *gSerialDevices[DEVICES_COUNT];
char *gDeviceNames[DEVICES_COUNT + 1];
usb_module_info *gUSBModule = NULL;
tty_module_info *gTTYModule = NULL;
struct ddomain gSerialDomain;
sem_id gDriverLock = -1;
status_t
pc_serial_device_added(pc_device device, void **cookie)
{
TRACE_FUNCALLS("> pc_serial_device_added(0x%08x, 0x%08x)\n", device, cookie);
status_t status = B_OK;
const pc_device_descriptor *descriptor
= gUSBModule->get_device_descriptor(device);
TRACE_ALWAYS("probing device: 0x%04x/0x%04x\n", descriptor->vendor_id,
descriptor->product_id);
*cookie = NULL;
SerialDevice *serialDevice = SerialDevice::MakeDevice(device,
descriptor->vendor_id, descriptor->product_id);
const pc_configuration_info *configuration
= gUSBModule->get_nth_configuration(device, 0);
if (!configuration)
return B_ERROR;
status = serialDevice->AddDevice(configuration);
if (status < B_OK) {
delete serialDevice;
return status;
}
acquire_sem(gDriverLock);
for (int32 i = 0; i < DEVICES_COUNT; i++) {
if (gSerialDevices[i] != NULL)
continue;
status = serialDevice->Init();
if (status < B_OK) {
delete serialDevice;
return status;
}
gSerialDevices[i] = serialDevice;
*cookie = serialDevice;
release_sem(gDriverLock);
TRACE_ALWAYS("%s (0x%04x/0x%04x) added\n", serialDevice->Description(),
descriptor->vendor_id, descriptor->product_id);
return B_OK;
}
release_sem(gDriverLock);
return B_ERROR;
}
status_t
pc_serial_device_removed(void *cookie)
{
TRACE_FUNCALLS("> pc_serial_device_removed(0x%08x)\n", cookie);
acquire_sem(gDriverLock);
SerialDevice *device = (SerialDevice *)cookie;
for (int32 i = 0; i < DEVICES_COUNT; i++) {
if (gSerialDevices[i] == device) {
if (device->IsOpen()) {
// the device will be deleted upon being freed
device->Removed();
} else {
delete device;
gSerialDevices[i] = NULL;
}
break;
}
}
release_sem(gDriverLock);
TRACE_FUNCRET("< pc_serial_device_removed() returns\n");
return B_OK;
}
//#pragma mark -
/* init_hardware - called once the first time the driver is loaded */
status_t
init_hardware()
{
TRACE("init_hardware\n");
return B_OK;
}
/* init_driver - called every time the driver is loaded. */
status_t
init_driver()
{
load_settings();
create_log_file();
TRACE_FUNCALLS("> init_driver()\n");
status_t status = get_module(B_TTY_MODULE_NAME, (module_info **)&gTTYModule);
if (status < B_OK)
return status;
status = get_module(B_USB_MODULE_NAME, (module_info **)&gUSBModule);
if (status < B_OK) {
put_module(B_TTY_MODULE_NAME);
return status;
}
for (int32 i = 0; i < DEVICES_COUNT; i++)
gSerialDevices[i] = NULL;
gDeviceNames[0] = NULL;
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;
}
static pc_notify_hooks notifyHooks = {
&pc_serial_device_added,
&pc_serial_device_removed
};
gUSBModule->register_driver(DRIVER_NAME, NULL, 0, NULL);
gUSBModule->install_notify(DRIVER_NAME, &notifyHooks);
TRACE_FUNCRET("< init_driver() returns\n");
return B_OK;
}
/* uninit_driver - called every time the driver is unloaded */
void
uninit_driver()
{
TRACE_FUNCALLS("> uninit_driver()\n");
gUSBModule->uninstall_notify(DRIVER_NAME);
acquire_sem(gDriverLock);
for (int32 i = 0; i < DEVICES_COUNT; i++) {
if (gSerialDevices[i]) {
delete gSerialDevices[i];
gSerialDevices[i] = NULL;
}
}
for (int32 i = 0; gDeviceNames[i]; i++)
free(gDeviceNames[i]);
delete_sem(gDriverLock);
put_module(B_USB_MODULE_NAME);
put_module(B_TTY_MODULE_NAME);
TRACE_FUNCRET("< uninit_driver() returns\n");
}
bool
pc_serial_service(struct tty *ptty, struct ddrover *ddr, uint flags)
{
TRACE_FUNCALLS("> pc_serial_service(0x%08x, 0x%08x, 0x%08x)\n", ptty, ddr, flags);
for (int32 i = 0; i < DEVICES_COUNT; i++) {
if (gSerialDevices[i] && gSerialDevices[i]->Service(ptty, ddr, flags)) {
TRACE_FUNCRET("< pc_serial_service() returns: true\n");
return true;
}
}
TRACE_FUNCRET("< pc_serial_service() returns: false\n");
return false;
}
/* pc_serial_open - handle open() calls */
static status_t
pc_serial_open(const char *name, uint32 flags, void **cookie)
{
TRACE_FUNCALLS("> pc_serial_open(%s, 0x%08x, 0x%08x)\n", name, flags, cookie);
acquire_sem(gDriverLock);
status_t status = ENODEV;
*cookie = NULL;
int i = strtol(name + strlen(sDeviceBaseName), NULL, 10);
if (i >= 0 && i < DEVICES_COUNT && gSerialDevices[i]) {
status = gSerialDevices[i]->Open(flags);
*cookie = gSerialDevices[i];
}
release_sem(gDriverLock);
TRACE_FUNCRET("< pc_serial_open() returns: 0x%08x\n", status);
return status;
}
/* pc_serial_read - handle read() calls */
static status_t
pc_serial_read(void *cookie, off_t position, void *buffer, size_t *numBytes)
{
TRACE_FUNCALLS("> pc_serial_read(0x%08x, %Ld, 0x%08x, %d)\n", cookie,
position, buffer, *numBytes);
SerialDevice *device = (SerialDevice *)cookie;
return device->Read((char *)buffer, numBytes);
}
/* pc_serial_write - handle write() calls */
static status_t
pc_serial_write(void *cookie, off_t position, const void *buffer,
size_t *numBytes)
{
TRACE_FUNCALLS("> pc_serial_write(0x%08x, %Ld, 0x%08x, %d)\n", cookie,
position, buffer, *numBytes);
SerialDevice *device = (SerialDevice *)cookie;
return device->Write((const char *)buffer, numBytes);
}
/* pc_serial_control - handle ioctl calls */
static status_t
pc_serial_control(void *cookie, uint32 op, void *arg, size_t length)
{
TRACE_FUNCALLS("> pc_serial_control(0x%08x, 0x%08x, 0x%08x, %d)\n",
cookie, op, arg, length);
SerialDevice *device = (SerialDevice *)cookie;
return device->Control(op, arg, length);
}
#if defined(B_BEOS_VERSION_DANO) || defined(__HAIKU__)
/* pc_serial_select - handle select start */
static status_t
pc_serial_select(void *cookie, uint8 event, uint32 ref, selectsync *sync)
{
TRACE_FUNCALLS("> pc_serial_select(0x%08x, 0x%08x, 0x%08x, %p)\n",
cookie, event, ref, sync);
SerialDevice *device = (SerialDevice *)cookie;
return device->Select(event, ref, sync);
}
/* pc_serial_deselect - handle select exit */
static status_t
pc_serial_deselect(void *cookie, uint8 event, selectsync *sync)
{
TRACE_FUNCALLS("> pc_serial_deselect(0x%08x, 0x%08x, %p)\n",
cookie, event, sync);
SerialDevice *device = (SerialDevice *)cookie;
return device->DeSelect(event, sync);
}
#endif // DANO, HAIKU
/* pc_serial_close - handle close() calls */
static status_t
pc_serial_close(void *cookie)
{
TRACE_FUNCALLS("> pc_serial_close(0x%08x)\n", cookie);
SerialDevice *device = (SerialDevice *)cookie;
return device->Close();
}
/* pc_serial_free - called after last device is closed, and all i/o complete. */
static status_t
pc_serial_free(void *cookie)
{
TRACE_FUNCALLS("> pc_serial_free(0x%08x)\n", cookie);
SerialDevice *device = (SerialDevice *)cookie;
acquire_sem(gDriverLock);
status_t status = device->Free();
if (device->IsRemoved()) {
for (int32 i = 0; i < DEVICES_COUNT; i++) {
if (gSerialDevices[i] == device) {
// the device is removed already but as it was open the
// removed hook has not deleted the object
delete device;
gSerialDevices[i] = NULL;
break;
}
}
}
release_sem(gDriverLock);
return status;
}
/* publish_devices - null-terminated array of devices supported by this driver. */
const char **
publish_devices()
{
TRACE_FUNCALLS("> publish_devices()\n");
for (int32 i = 0; gDeviceNames[i]; i++)
free(gDeviceNames[i]);
int32 j = 0;
acquire_sem(gDriverLock);
for(int32 i = 0; i < DEVICES_COUNT; i++) {
if (gSerialDevices[i]) {
gDeviceNames[j] = (char *)malloc(strlen(sDeviceBaseName) + 4);
if (gDeviceNames[j]) {
sprintf(gDeviceNames[j], "%s%ld", sDeviceBaseName, i);
j++;
} else
TRACE_ALWAYS("publish_devices - no memory to allocate device names\n");
}
}
gDeviceNames[j] = NULL;
release_sem(gDriverLock);
return (const char **)&gDeviceNames[0];
}
/* find_device - return poiter to device hooks structure for a given device */
device_hooks *
find_device(const char *name)
{
static device_hooks deviceHooks = {
pc_serial_open, /* -> open entry point */
pc_serial_close, /* -> close entry point */
pc_serial_free, /* -> free cookie */
pc_serial_control, /* -> control entry point */
pc_serial_read, /* -> read entry point */
pc_serial_write, /* -> write entry point */
#if defined(B_BEOS_VERSION_DANO) || defined(__HAIKU__)
pc_serial_select, /* -> select entry point */
pc_serial_deselect /* -> deselect entry point */
#endif
};
TRACE_FUNCALLS("> find_device(%s)\n", name);
return &deviceHooks;
}
@@ -0,0 +1,108 @@
/*
* Copyright (c) 2007-2008 by Michael Lotz
* Heavily based on the original usb_serial driver which is:
*
* Copyright (c) 2003 by Siarzhuk Zharski <[email protected]>
* Distributed under the terms of the MIT License.
*/
#ifndef _PC_SERIAL_DRIVER_H_
#define _PC_SERIAL_DRIVER_H_
#include <OS.h>
#include <KernelExport.h>
#include <Drivers.h>
#include <string.h>
#ifdef __HAIKU__
#include <lock.h>
#else
#include "BeOSCompatibility.h"
#endif
#include "kernel_cpp.h"
#include "Tracing.h"
extern "C" {
#include <ttylayer.h>
}
#define DRIVER_NAME "pc_serial" // driver name for debug output
#define DEVICES_COUNT 20 // max simultaneously open devices
/* 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) */
#define ROUNDUP(size, seg) (((size) + (seg) - 1) & ~((seg) - 1))
/* Default device buffer size */
#define DEF_BUFFER_SIZE 0x200
/* line coding defines ... Come from CDC USB specs? */
#define LC_STOP_BIT_1 0
#define LC_STOP_BIT_2 2
#define LC_PARITY_NONE 0
#define LC_PARITY_ODD 1
#define LC_PARITY_EVEN 2
/* struct that represents line coding */
typedef struct pc_serial_line_coding_s {
uint32 speed;
uint8 stopbits;
uint8 parity;
uint8 databits;
} pc_serial_line_coding;
/* control line states */
#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 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);
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);
status_t pc_serial_write(void *cookie, off_t position, const void *buffer, size_t *numBytes);
status_t pc_serial_control(void *cookie, uint32 op, void *arg, size_t length);
status_t pc_serial_select(void *cookie, uint8 event, uint32 ref, selectsync *sync);
status_t pc_serial_deselect(void *coookie, uint8 event, selectsync *sync);
status_t pc_serial_close(void *cookie);
status_t pc_serial_free(void *cookie);
const char **publish_devices();
device_hooks *find_device(const char *name);
}
#endif //_PC_SERIAL_DRIVER_H_
@@ -0,0 +1,28 @@
SubDir HAIKU_TOP src add-ons kernel drivers ports pc_serial ;
SetSubDirSupportedPlatformsBeOSCompatible ;
if $(TARGET_PLATFORM_HAIKU_COMPATIBLE) {
UsePrivateKernelHeaders ;
UseHeaders [ FDirName $(HAIKU_TOP) headers os drivers tty ] : true ;
}
SubDirC++Flags -fno-rtti ;
KernelAddon pc_serial :
Driver.cpp
SerialDevice.cpp
Tracing.cpp
;
#AddResources pc_serial : pc_serial.rdef ;
#Package haiku-pc_serial-cvs
# :
# pc_serial
# :
# boot home config add-ons kernel drivers bin ;
#
#Package haiku-pc_serial-cvs : <pc_serial!driver>pc_serial.settings.sample
# : boot home config settings kernel drivers sample ;
@@ -0,0 +1,4 @@
references:
http://www.beyondlogic.org/serial/serial.htm
@@ -0,0 +1,713 @@
/*
* Copyright (c) 2007-2008 by Michael Lotz
* Heavily based on the original usb_serial driver which is:
*
* Copyright (c) 2003 by Siarzhuk Zharski <[email protected]>
* Distributed under the terms of the MIT License.
*/
#include "SerialDevice.h"
#include "USB3.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),
fDeviceOpen(false),
fDeviceRemoved(false),
fControlPipe(0),
fReadPipe(0),
fWritePipe(0),
fBufferArea(-1),
fReadBuffer(NULL),
fReadBufferSize(ROUNDUP(DEF_BUFFER_SIZE, 16)),
fWriteBuffer(NULL),
fWriteBufferSize(ROUNDUP(DEF_BUFFER_SIZE, 16)),
fInterruptBuffer(NULL),
fInterruptBufferSize(16),
fDoneRead(-1),
fDoneWrite(-1),
fControlOut(0),
fInputStopped(false),
fDeviceThread(-1),
fStopDeviceThread(false)
{
memset(&fTTYFile, 0, sizeof(ttyfile));
memset(&fTTY, 0, sizeof(tty));
}
SerialDevice::~SerialDevice()
{
Removed();
if (fDoneRead >= B_OK)
delete_sem(fDoneRead);
if (fDoneWrite >= B_OK)
delete_sem(fDoneWrite);
if (fBufferArea >= B_OK)
delete_area(fBufferArea);
mutex_destroy(&fReadLock);
mutex_destroy(&fWriteLock);
}
status_t
SerialDevice::Init()
{
fDoneRead = create_sem(0, "usb_serial:done_read");
fDoneWrite = create_sem(0, "usb_serial:done_write");
mutex_init(&fReadLock, "usb_serial:read_lock");
mutex_init(&fWriteLock, "usb_serial:write_lock");
size_t totalBuffers = fReadBufferSize + fWriteBufferSize + fInterruptBufferSize;
fBufferArea = create_area("usb_serial:buffers_area", (void **)&fReadBuffer,
B_ANY_KERNEL_ADDRESS, ROUNDUP(totalBuffers, B_PAGE_SIZE), B_CONTIGUOUS,
B_READ_AREA | B_WRITE_AREA);
fWriteBuffer = fReadBuffer + fReadBufferSize;
fInterruptBuffer = fWriteBuffer + fWriteBufferSize;
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));
uint16 newControl = fControlOut;
TRACE_FUNCRES(trace_termios, &tios);
static uint32 baudRates[] = {
0x00000000, //B0
0x00000032, //B50
0x0000004B, //B75
0x0000006E, //B110
0x00000086, //B134
0x00000096, //B150
0x000000C8, //B200
0x0000012C, //B300
0x00000258, //B600
0x000004B0, //B1200
0x00000708, //B1800
0x00000960, //B2400
0x000012C0, //B4800
0x00002580, //B9600
0x00004B00, //B19200
0x00009600, //B38400
0x0000E100, //B57600
0x0001C200, //B115200
0x00038400, //B230400
0x00070800, //460800
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;
if (tios.c_cflag & PARENB) {
lineCoding.parity = LC_PARITY_EVEN;
if (tios.c_cflag & PARODD)
lineCoding.parity = LC_PARITY_ODD;
} else
lineCoding.parity = LC_PARITY_NONE;
lineCoding.databits = (tios.c_cflag & CS8) ? 8 : 7;
if (lineCoding.speed == 0) {
newControl &= 0xfffffffe;
lineCoding.speed = fLineCoding.speed;
} else
newControl = CLS_LINE_DTR;
if (fControlOut != newControl) {
fControlOut = newControl;
TRACE("newctrl send to modem: 0x%08x\n", newControl);
SetControlLineState(newControl);
}
if (memcmp(&lineCoding, &fLineCoding, sizeof(usb_serial_line_coding)) != 0) {
fLineCoding.speed = lineCoding.speed;
fLineCoding.stopbits = lineCoding.stopbits;
fLineCoding.databits = lineCoding.databits;
fLineCoding.parity = lineCoding.parity;
TRACE("send to modem: speed %d sb: 0x%08x db: 0x%08x parity: 0x%08x\n",
fLineCoding.speed, fLineCoding.stopbits, fLineCoding.databits,
fLineCoding.parity);
SetLineCoding(&fLineCoding);
}
}
bool
SerialDevice::Service(struct tty *ptty, struct ddrover *ddr, uint flags)
{
if (&fTTY != ptty)
return false;
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);
break;
case TTYDISABLE:
TRACE("TTYDISABLE\n");
gTTYModule->ttyhwsignal(ptty, ddr, TTYHWDCD, false);
fControlOut = 0x0;
SetControlLineState(fControlOut);
break;
case TTYISTOP:
TRACE("TTYISTOP\n");
fInputStopped = true;
gTTYModule->ttyhwsignal(ptty, ddr, TTYHWCTS, false);
break;
case TTYIRESUME:
TRACE("TTYIRESUME\n");
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);
break;
case TTYSETMODES:
TRACE("TTYSETMODES\n");
SetModes();
break;
case TTYOSTART:
case TTYOSYNC:
case TTYSETBREAK:
case TTYCLRBREAK:
case TTYSETDTR:
case TTYCLRDTR:
TRACE("TTY other\n");
break;
}
return true;
}
return false;
}
status_t
SerialDevice::Open(uint32 flags)
{
if (fDeviceOpen)
return B_BUSY;
if (fDeviceRemoved)
return B_DEV_NOT_READY;
gTTYModule->ttyinit(&fTTY, true);
fTTYFile.tty = &fTTY;
fTTYFile.flags = flags;
ResetDevice();
struct ddrover *ddr = gTTYModule->ddrstart(NULL);
if (!ddr)
return B_NO_MEMORY;
gTTYModule->ddacquire(ddr, &gSerialDomain);
status_t status = gTTYModule->ttyopen(&fTTYFile, ddr, usb_serial_service);
gTTYModule->ddrdone(ddr);
if (status < B_OK) {
TRACE_ALWAYS("open: failed to open tty\n");
return status;
}
fDeviceThread = spawn_kernel_thread(DeviceThread, "usb_serial device thread",
B_NORMAL_PRIORITY, this);
if (fDeviceThread < B_OK) {
TRACE_ALWAYS("open: failed to spawn kernel thread\n");
return fDeviceThread;
}
resume_thread(fDeviceThread);
fControlOut = CLS_LINE_DTR | CLS_LINE_RTS;
SetControlLineState(fControlOut);
status = gUSBModule->queue_interrupt(fControlPipe, fInterruptBuffer,
fInterruptBufferSize, InterruptCallbackFunction, this);
if (status < B_OK)
TRACE_ALWAYS("failed to queue initial interrupt\n");
fDeviceOpen = true;
return B_OK;
}
status_t
SerialDevice::Read(char *buffer, size_t *numBytes)
{
if (fDeviceRemoved) {
*numBytes = 0;
return B_DEV_NOT_READY;
}
status_t status = mutex_lock(&fReadLock);
if (status != B_OK) {
TRACE_ALWAYS("read: failed to get read lock\n");
*numBytes = 0;
return status;
}
struct ddrover *ddr = gTTYModule->ddrstart(NULL);
if (!ddr) {
*numBytes = 0;
mutex_unlock(&fReadLock);
return B_NO_MEMORY;
}
status = gTTYModule->ttyread(&fTTYFile, ddr, buffer, numBytes);
gTTYModule->ddrdone(ddr);
mutex_unlock(&fReadLock);
return status;
}
status_t
SerialDevice::Write(const char *buffer, size_t *numBytes)
{
size_t bytesLeft = *numBytes;
*numBytes = 0;
status_t status = mutex_lock(&fWriteLock);
if (status != B_OK) {
TRACE_ALWAYS("write: failed to get write lock\n");
return status;
}
if (fDeviceRemoved) {
mutex_unlock(&fWriteLock);
return B_DEV_NOT_READY;
}
while (bytesLeft > 0) {
size_t length = MIN(bytesLeft, fWriteBufferSize);
size_t packetLength = length;
OnWrite(buffer, &length, &packetLength);
status = gUSBModule->queue_bulk(fWritePipe, fWriteBuffer,
packetLength, WriteCallbackFunction, this);
if (status < B_OK) {
TRACE_ALWAYS("write: queueing failed with status 0x%08x\n", status);
break;
}
status = acquire_sem_etc(fDoneWrite, 1, B_CAN_INTERRUPT, 0);
if (status < B_OK) {
TRACE_ALWAYS("write: failed to get write done sem 0x%08x\n", status);
break;
}
if (fStatusWrite != B_OK) {
TRACE("write: device status error 0x%08x\n", fStatusWrite);
status = gUSBModule->clear_feature(fWritePipe,
USB_FEATURE_ENDPOINT_HALT);
if (status < B_OK) {
TRACE_ALWAYS("write: failed to clear device halt\n");
status = B_ERROR;
break;
}
continue;
}
buffer += length;
*numBytes += length;
bytesLeft -= length;
}
mutex_unlock(&fWriteLock);
return status;
}
status_t
SerialDevice::Control(uint32 op, void *arg, size_t length)
{
if (fDeviceRemoved)
return B_DEV_NOT_READY;
struct ddrover *ddr = gTTYModule->ddrstart(NULL);
if (!ddr)
return B_NO_MEMORY;
status_t status = gTTYModule->ttycontrol(&fTTYFile, ddr, op, arg, length);
gTTYModule->ddrdone(ddr);
return status;
}
status_t
SerialDevice::Select(uint8 event, uint32 ref, selectsync *sync)
{
if (fDeviceRemoved)
return B_DEV_NOT_READY;
struct ddrover *ddr = gTTYModule->ddrstart(NULL);
if (!ddr)
return B_NO_MEMORY;
status_t status = gTTYModule->ttyselect(&fTTYFile, ddr, event, ref, sync);
gTTYModule->ddrdone(ddr);
return status;
}
status_t
SerialDevice::DeSelect(uint8 event, selectsync *sync)
{
if (fDeviceRemoved)
return B_DEV_NOT_READY;
struct ddrover *ddr = gTTYModule->ddrstart(NULL);
if (!ddr)
return B_NO_MEMORY;
status_t status = gTTYModule->ttydeselect(&fTTYFile, ddr, event, sync);
gTTYModule->ddrdone(ddr);
return status;
}
status_t
SerialDevice::Close()
{
OnClose();
if (!fDeviceRemoved) {
gUSBModule->cancel_queued_transfers(fReadPipe);
gUSBModule->cancel_queued_transfers(fWritePipe);
gUSBModule->cancel_queued_transfers(fControlPipe);
}
struct ddrover *ddr = gTTYModule->ddrstart(NULL);
if (!ddr)
return B_NO_MEMORY;
status_t status = gTTYModule->ttyclose(&fTTYFile, ddr);
gTTYModule->ddrdone(ddr);
fDeviceOpen = false;
return status;
}
status_t
SerialDevice::Free()
{
struct ddrover *ddr = gTTYModule->ddrstart(NULL);
if (!ddr)
return B_NO_MEMORY;
status_t status = gTTYModule->ttyfree(&fTTYFile, ddr);
gTTYModule->ddrdone(ddr);
return status;
}
void
SerialDevice::Removed()
{
if (fDeviceRemoved)
return;
// notifies us that the device was removed
fDeviceRemoved = true;
// we need to ensure that we do not use the device anymore
fStopDeviceThread = true;
fInputStopped = false;
gUSBModule->cancel_queued_transfers(fReadPipe);
gUSBModule->cancel_queued_transfers(fWritePipe);
gUSBModule->cancel_queued_transfers(fControlPipe);
int32 result = B_OK;
wait_for_thread(fDeviceThread, &result);
fDeviceThread = -1;
mutex_lock(&fWriteLock);
mutex_unlock(&fWriteLock);
}
status_t
SerialDevice::AddDevice(const usb_configuration_info *config)
{
// default implementation - does nothing
return B_ERROR;
}
status_t
SerialDevice::ResetDevice()
{
// default implementation - does nothing
return B_OK;
}
status_t
SerialDevice::SetLineCoding(usb_serial_line_coding *coding)
{
// default implementation - does nothing
return B_OK;
}
status_t
SerialDevice::SetControlLineState(uint16 state)
{
// default implementation - does nothing
return B_OK;
}
void
SerialDevice::OnRead(char **buffer, size_t *numBytes)
{
// default implementation - does nothing
}
void
SerialDevice::OnWrite(const char *buffer, size_t *numBytes, size_t *packetBytes)
{
// default implementation - does nothing
}
void
SerialDevice::OnClose()
{
// default implementation - does nothing
}
int32
SerialDevice::DeviceThread(void *data)
{
SerialDevice *device = (SerialDevice *)data;
while (!device->fStopDeviceThread) {
status_t status = gUSBModule->queue_bulk(device->fReadPipe,
device->fReadBuffer, device->fReadBufferSize,
device->ReadCallbackFunction, data);
if (status < B_OK) {
TRACE_ALWAYS("device thread: queueing failed with error: 0x%08x\n", status);
break;
}
status = acquire_sem_etc(device->fDoneRead, 1, B_CAN_INTERRUPT, 0);
if (status < B_OK) {
TRACE_ALWAYS("device thread: failed to get read done sem 0x%08x\n", status);
break;
}
if (device->fStatusRead != B_OK) {
TRACE("device thread: device status error 0x%08x\n",
device->fStatusRead);
if (gUSBModule->clear_feature(device->fReadPipe,
USB_FEATURE_ENDPOINT_HALT) != B_OK) {
TRACE_ALWAYS("device thread: failed to clear halt feature\n");
break;
}
}
char *buffer = device->fReadBuffer;
size_t readLength = device->fActualLengthRead;
device->OnRead(&buffer, &readLength);
if (readLength == 0)
continue;
ddrover *ddr = gTTYModule->ddrstart(NULL);
if (!ddr) {
TRACE_ALWAYS("device thread: ddrstart problem\n");
return B_NO_MEMORY;
}
while (device->fInputStopped)
snooze(100);
gTTYModule->ttyilock(&device->fTTY, ddr, true);
for (size_t i = 0; i < readLength; i++)
gTTYModule->ttyin(&device->fTTY, ddr, buffer[i]);
gTTYModule->ttyilock(&device->fTTY, ddr, false);
gTTYModule->ddrdone(ddr);
}
return B_OK;
}
void
SerialDevice::ReadCallbackFunction(void *cookie, int32 status, void *data,
uint32 actualLength)
{
TRACE_FUNCALLS("read callback: cookie: 0x%08x status: 0x%08x data: 0x%08x len: %lu\n",
cookie, status, data, actualLength);
SerialDevice *device = (SerialDevice *)cookie;
device->fActualLengthRead = actualLength;
device->fStatusRead = status;
release_sem_etc(device->fDoneRead, 1, B_DO_NOT_RESCHEDULE);
}
void
SerialDevice::WriteCallbackFunction(void *cookie, int32 status, void *data,
uint32 actualLength)
{
TRACE_FUNCALLS("write callback: cookie: 0x%08x status: 0x%08x data: 0x%08x len: %lu\n",
cookie, status, data, actualLength);
SerialDevice *device = (SerialDevice *)cookie;
device->fActualLengthWrite = actualLength;
device->fStatusWrite = status;
release_sem_etc(device->fDoneWrite, 1, B_DO_NOT_RESCHEDULE);
}
void
SerialDevice::InterruptCallbackFunction(void *cookie, int32 status,
void *data, uint32 actualLength)
{
TRACE_FUNCALLS("interrupt callback: cookie: 0x%08x status: 0x%08x data: 0x%08x len: %lu\n",
cookie, status, data, actualLength);
SerialDevice *device = (SerialDevice *)cookie;
device->fActualLengthInterrupt = actualLength;
device->fStatusInterrupt = status;
// ToDo: maybe handle those somehow?
if (status == B_OK && !device->fDeviceRemoved) {
status = gUSBModule->queue_interrupt(device->fControlPipe,
device->fInterruptBuffer, device->fInterruptBufferSize,
device->InterruptCallbackFunction, device);
}
}
SerialDevice *
SerialDevice::MakeDevice(usb_device device, uint16 vendorID,
uint16 productID)
{
const char *description = NULL;
switch (vendorID) {
case VENDOR_IODATA:
case VENDOR_ATEN:
case VENDOR_TDK:
case VENDOR_RATOC:
case VENDOR_PROLIFIC:
case VENDOR_ELECOM:
case VENDOR_SOURCENEXT:
case VENDOR_HAL:
{
switch (productID) {
case PRODUCT_PROLIFIC_RSAQ2: description = "PL2303 Serial adapter (IODATA USB-RSAQ2)"; break;
case PRODUCT_IODATA_USBRSAQ: description = "I/O Data USB serial adapter USB-RSAQ1"; break;
case PRODUCT_ATEN_UC232A: description = "Aten Serial adapter"; break;
case PRODUCT_TDK_UHA6400: description = "TDK USB-PHS Adapter UHA6400"; break;
case PRODUCT_RATOC_REXUSB60: description = "Ratoc USB serial adapter REX-USB60"; break;
case PRODUCT_PROLIFIC_PL2303: description = "PL2303 Serial adapter (ATEN/IOGEAR UC232A)"; break;
case PRODUCT_ELECOM_UCSGT: description = "Elecom UC-SGT"; break;
case PRODUCT_SOURCENEXT_KEIKAI8: description = "SOURCENEXT KeikaiDenwa 8"; break;
case PRODUCT_SOURCENEXT_KEIKAI8_CHG: description = "SOURCENEXT KeikaiDenwa 8 with charger"; break;
case PRODUCT_HAL_IMR001: description = "HAL Corporation Crossam2+USB"; break;
}
if (!description)
break;
return new ProlificDevice(device, vendorID, productID, description);
}
case VENDOR_FTDI:
{
switch (productID) {
case PRODUCT_FTDI_8U100AX: description = "FTDI 8U100AX serial converter"; break;
case PRODUCT_FTDI_8U232AM: description = "FTDI 8U232AM serial converter"; break;
}
if (!description)
break;
return new FTDIDevice(device, vendorID, productID, description);
}
case VENDOR_PALM:
case VENDOR_KLSI:
{
switch (productID) {
case PRODUCT_PALM_CONNECT: description = "PalmConnect RS232"; break;
case PRODUCT_KLSI_KL5KUSB105D: description = "KLSI KL5KUSB105D"; break;
}
if (!description)
break;
return new KLSIDevice(device, vendorID, productID, description);
}
}
return new ACMDevice(device, vendorID, productID, "CDC ACM compatible device");
}
@@ -0,0 +1,142 @@
/*
* Copyright (c) 2007-2008 by Michael Lotz
* Heavily based on the original usb_serial driver which is:
*
* Copyright (c) 2003 by Siarzhuk Zharski <[email protected]>
* Distributed under the terms of the MIT License.
*/
#ifndef _SERIAL_DEVICE_H_
#define _SERIAL_DEVICE_H_
#include "Driver.h"
class SerialDevice {
public:
SerialDevice(usb_device device,
uint16 vendorID, uint16 productID,
const char *description);
virtual ~SerialDevice();
static SerialDevice * MakeDevice(usb_device device, uint16 vendorID,
uint16 productID);
status_t Init();
usb_device Device() { return fDevice; };
uint16 ProductID() { return fProductID; };
uint16 VendorID() { return fVendorID; };
const char * Description() { 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; }
char * ReadBuffer() { return fReadBuffer; };
size_t ReadBufferSize() { return fReadBufferSize; };
char * WriteBuffer() { return fWriteBuffer; };
size_t WriteBufferSize() { return fWriteBufferSize; };
void SetModes();
bool Service(struct tty *ptty, struct ddrover *ddr,
uint flags);
status_t Open(uint32 flags);
status_t Read(char *buffer, size_t *numBytes);
status_t Write(const char *buffer, size_t *numBytes);
status_t Control(uint32 op, void *arg, size_t length);
status_t Select(uint8 event, uint32 ref, selectsync *sync);
status_t DeSelect(uint8 event, selectsync *sync);
status_t Close();
status_t Free();
bool IsOpen() { return fDeviceOpen; };
void Removed();
bool IsRemoved() { return fDeviceRemoved; };
/* virtual interface to be overriden as necessary */
virtual status_t AddDevice(const usb_configuration_info *config);
virtual status_t ResetDevice();
virtual status_t SetLineCoding(usb_serial_line_coding *coding);
virtual status_t SetControlLineState(uint16 state);
virtual void OnRead(char **buffer, size_t *numBytes);
virtual void OnWrite(const char *buffer, size_t *numBytes,
size_t *packetBytes);
virtual void OnClose();
protected:
void SetReadBufferSize(size_t size) { fReadBufferSize = size; };
void SetWriteBufferSize(size_t size) { fWriteBufferSize = size; };
void SetInterruptBufferSize(size_t size) { fInterruptBufferSize = size; };
private:
static int32 DeviceThread(void *data);
static void ReadCallbackFunction(void *cookie,
int32 status, void *data,
uint32 actualLength);
static void WriteCallbackFunction(void *cookie,
int32 status, void *data,
uint32 actualLength);
static void InterruptCallbackFunction(void *cookie,
int32 status, void *data,
uint32 actualLength);
usb_device fDevice; // USB device handle
uint16 fVendorID;
uint16 fProductID;
const char * fDescription; // informational description
bool fDeviceOpen;
bool fDeviceRemoved;
/* communication pipes */
usb_pipe fControlPipe;
usb_pipe fReadPipe;
usb_pipe fWritePipe;
/* line coding */
usb_serial_line_coding fLineCoding;
/* data buffers */
area_id fBufferArea;
char * fReadBuffer;
size_t fReadBufferSize;
char * fWriteBuffer;
size_t fWriteBufferSize;
char * fInterruptBuffer;
size_t fInterruptBufferSize;
/* variables used in callback functionality */
size_t fActualLengthRead;
uint32 fStatusRead;
size_t fActualLengthWrite;
uint32 fStatusWrite;
size_t fActualLengthInterrupt;
uint32 fStatusInterrupt;
/* semaphores used in callbacks */
sem_id fDoneRead;
sem_id fDoneWrite;
uint16 fControlOut;
bool fInputStopped;
struct ttyfile fTTYFile;
struct tty fTTY;
/* device thread management */
thread_id fDeviceThread;
bool fStopDeviceThread;
/* device locks to ensure no concurent reads/writes */
mutex fReadLock;
mutex fWriteLock;
};
#endif // _SERIAL_DEVICE_H_
@@ -0,0 +1,185 @@
/*
* Copyright (c) 2007-2008 by Michael Lotz
* Heavily based on the original usb_serial driver which is:
*
* Copyright (c) 2003 by Siarzhuk Zharski <[email protected]>
* Distributed under the terms of the MIT License.
*/
#include "Tracing.h"
#include "Driver.h"
#include "USB3.h"
#include <stdio.h> //sprintf
#include <unistd.h> //posix file i/o - create, write, close
#include <Drivers.h>
#include <driver_settings.h>
#if DEBUG
bool gLogEnabled = true;
#else
bool gLogEnabled = false;
#endif
bool gLogToFile = false;
bool gLogAppend = false;
bool gLogFunctionCalls = false;
bool gLogFunctionReturns = false;
bool gLogFunctionResults = false;
static const char *sLogFilePath="/boot/home/"DRIVER_NAME".log";
static sem_id sLogLock;
void
load_settings()
{
void *settingsHandle;
settingsHandle = load_driver_settings(DRIVER_NAME);
#if !DEBUG
gLogEnabled = get_driver_boolean_parameter(settingsHandle,
"debug_output", gLogEnabled, true);
#endif
gLogToFile = get_driver_boolean_parameter(settingsHandle,
"debug_output_in_file", gLogToFile, true);
gLogAppend = !get_driver_boolean_parameter(settingsHandle,
"debug_output_file_rewrite", !gLogAppend, true);
gLogFunctionCalls = get_driver_boolean_parameter(settingsHandle,
"debug_trace_func_calls", gLogFunctionCalls, false);
gLogFunctionReturns = get_driver_boolean_parameter(settingsHandle,
"debug_trace_func_returns", gLogFunctionReturns, false);
gLogFunctionResults = get_driver_boolean_parameter(settingsHandle,
"debug_trace_func_results", gLogFunctionResults, false);
unload_driver_settings(settingsHandle);
}
void
create_log_file()
{
if(!gLogToFile)
return;
int flags = O_WRONLY | O_CREAT | (!gLogAppend ? O_TRUNC : 0);
close(open(sLogFilePath, flags, 0666));
sLogLock = create_sem(1, DRIVER_NAME"-logging");
}
void
usb_serial_trace(bool force, char *format, ...)
{
if (!gLogEnabled && !force)
return;
static char buffer[1024];
char *bufferPointer = buffer;
if (!gLogToFile) {
const char *prefix = "\33[32m"DRIVER_NAME":\33[0m ";
strcpy(bufferPointer, prefix);
bufferPointer += strlen(prefix);
}
va_list argumentList;
va_start(argumentList, format);
vsprintf(bufferPointer, format, argumentList);
va_end(argumentList);
if (gLogToFile) {
acquire_sem(sLogLock);
int fd = open(sLogFilePath, O_WRONLY | O_APPEND);
write(fd, buffer, strlen(buffer));
close(fd);
release_sem(sLogLock);
} else
dprintf(buffer);
}
void
trace_ddomain(struct ddomain *dd)
{
TRACE("struct ddomain:\n"
"\tddrover: 0x%08x\n"
"\tbg: %d, locked: %d\n", dd->r, dd->bg, dd->locked);
}
void
trace_termios(struct termios *tios)
{
TRACE("struct termios:\n"
"\tc_iflag: 0x%08x\n"
"\tc_oflag: 0x%08x\n"
"\tc_cflag: 0x%08x\n"
"\tc_lflag: 0x%08x\n"
"\tc_line: 0x%08x\n"
// "\tc_ixxxxx: 0x%08x\n"
// "\tc_oxxxxx: 0x%08x\n"
"\tc_cc[0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x]\n",
tios->c_iflag, tios->c_oflag, tios->c_cflag, tios->c_lflag,
tios->c_line,
// tios->c_ixxxxx, tios->c_oxxxxx,
tios->c_cc[0], tios->c_cc[1], tios->c_cc[2], tios->c_cc[3],
tios->c_cc[4], tios->c_cc[5], tios->c_cc[6], tios->c_cc[7],
tios->c_cc[8], tios->c_cc[9], tios->c_cc[10]);
}
void
trace_str(struct str *str)
{
TRACE("struct str:\n"
"\tbuffer: 0x%08x\n"
"\tbufsize: %d\n"
"\tcount: %d\n"
"\ttail: %d\n"
"\tallocated: %d\n",
str->buffer, str->bufsize, str->count, str->tail, str->allocated);
}
void
trace_winsize(struct winsize *ws)
{
TRACE("struct winsize:\n"
"\tws_row: %d\n"
"\tws_col: %d\n"
"\tws_xpixel: %d\n"
"\tws_ypixel: %d\n",
ws->ws_row, ws->ws_col, ws->ws_xpixel, ws->ws_ypixel);
}
void
trace_tty(struct tty *tty)
{
TRACE("struct tty:\n"
"\tnopen: %d, flags: 0x%08x,\n", tty->nopen, tty->flags);
TRACE("ddomain dd:\n");
trace_ddomain(&tty->dd);
TRACE("ddomain ddi:\n");
trace_ddomain(&tty->ddi);
TRACE("\tpgid: %08x\n", tty->pgid);
TRACE("termios t:");
trace_termios(&tty->t);
TRACE("\tiactivity: %d, ibusy: %d\n", tty->iactivity, tty->ibusy);
TRACE("str istr:\n");
trace_str(&tty->istr);
TRACE("str rstr:\n");
trace_str(&tty->rstr);
TRACE("str ostr:\n");
trace_str(&tty->ostr);
TRACE("winsize wsize:\n");
trace_winsize(&tty->wsize);
TRACE("\tservice: 0x%08x\n", tty->service);
}
@@ -0,0 +1,39 @@
/*
* Copyright (c) 2007-2008 by Michael Lotz
* Heavily based on the original usb_serial driver which is:
*
* Copyright (c) 2003 by Siarzhuk Zharski <[email protected]>
* Distributed under the terms of the MIT License.
*/
#ifndef _USB_SERIAL_TRACING_H_
#define _USB_SERIAL_TRACING_H_
void load_settings();
void create_log_file();
void usb_serial_trace(bool force, char *format, ...);
#define TRACE_ALWAYS(x...) usb_serial_trace(true, x);
#define TRACE(x...) usb_serial_trace(false, x);
extern bool gLogFunctionCalls;
#define TRACE_FUNCALLS(x...) \
if (gLogFunctionCalls) \
usb_serial_trace(false, x);
extern bool gLogFunctionReturns;
#define TRACE_FUNCRET(x...) \
if (gLogFunctionReturns) \
usb_serial_trace(false, x);
extern bool gLogFunctionResults;
#define TRACE_FUNCRES(func, param) \
if (gLogFunctionResults) \
func(param);
void trace_ddomain(struct ddomain *dd);
void trace_termios(struct termios *tios);
void trace_str(struct str *str);
void trace_winsize(struct winsize *ws);
void trace_tty(struct tty *tty);
#endif //_USB_SERIAL_TRACING_H_
@@ -0,0 +1,45 @@
#ifndef _KERNEL_CPP_H_
#define _KERNEL_CPP_H_
#include <malloc.h>
inline void *
operator new(size_t size)
{
return malloc(size);
}
inline void *
operator new[](size_t size)
{
return malloc(size);
}
inline void
operator delete(void *pointer)
{
free(pointer);
}
inline void
operator delete[](void *pointer)
{
free(pointer);
}
inline void
terminate(void)
{
}
static inline void
__throw()
{
}
#endif // _KERNEL_CPP_H_