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:
@@ -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, ¬ifyHooks);
|
||||
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_
|
||||
Reference in New Issue
Block a user