Add support for Silicon Labs CP210x to usb_serial.

Not completely tested : my device has no control lines wired. RX/TX seems to
work fine, at least.

Inspiration from the Linux driver since there isn't any documentation avilable:
http://lxr.free-electrons.com/source/drivers/usb/serial/cp210x.c

The switch/case for VID/PID identification is getting quite long. Isn't there a
better way to do it ?


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@42860 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Adrien Destugues 2011-10-16 11:46:53 +00:00
parent fd4f34da56
commit 7117b2ea25
4 changed files with 619 additions and 0 deletions

View File

@ -14,6 +14,7 @@ KernelAddon usb_serial :
FTDI.cpp
KLSI.cpp
Prolific.cpp
Silicon.cpp
;
AddResources usb_serial : usb_serial.rdef ;

View File

@ -14,6 +14,7 @@
#include "FTDI.h"
#include "KLSI.h"
#include "Prolific.h"
#include "Silicon.h"
#include <sys/ioctl.h>
@ -801,6 +802,361 @@ SerialDevice::MakeDevice(usb_device device, uint16 vendorID,
return new(std::nothrow) KLSIDevice(device, vendorID, productID,
description);
}
case VENDOR_RENESAS:
{
switch (productID) {
case 0x0053:
description = "Renesas RX610 RX-Stick";
break;
}
if (description != NULL)
goto SILICON;
break;
}
case VENDOR_AKATOM:
{
switch (productID) {
case 0x066A:
description = "AKTAKOM ACE-1001";
break;
}
if (description != NULL)
goto SILICON;
break;
}
case VENDOR_PIRELLI:
{
switch (productID) {
case 0xE000:
case 0xE003:
description = "Pirelli DP-L10 GSM Mobile";
break;
}
if (description != NULL)
goto SILICON;
break;
}
case VENDOR_CYPHERLAB:
{
switch (productID) {
case 0x1000:
description = "Cipherlab CCD Barcode Scanner";
break;
}
if (description != NULL)
goto SILICON;
break;
}
case VENDOR_GEMALTO:
{
switch (productID) {
case 0x5501:
description = "Gemalto contactless smartcard reader";
break;
}
if (description != NULL)
goto SILICON;
break;
}
case VENDOR_DIGIANSWER:
{
switch (productID) {
case 0x000A:
description = "Digianswer ZigBee MAC device";
break;
}
if (description != NULL)
goto SILICON;
break;
}
case VENDOR_MEI:
{
switch (productID) {
case 0x1100:
case 0x1101:
description = "MEI Acceptor";
break;
}
if (description != NULL)
goto SILICON;
break;
}
case VENDOR_DYNASTREAM:
{
switch (productID) {
case 0x1003:
case 0x1004:
case 0x1006:
description = "Dynastream ANT development board";
break;
}
if (description != NULL)
goto SILICON;
break;
}
case VENDOR_KNOCKOFF:
{
switch (productID) {
case 0xAA26:
description = "Knock-off DCU-11";
break;
}
if (description != NULL)
goto SILICON;
break;
}
case VENDOR_SIEMENS:
{
switch (productID) {
case 0x10C5:
description = "Siemens MC60";
break;
}
if (description != NULL)
goto SILICON;
break;
}
case VENDOR_NOKIA:
{
switch (productID) {
case 0xAC70:
description = "Nokia CA-42";
break;
}
if (description != NULL)
goto SILICON;
break;
}
case VENDOR_SILICON:
{
switch (productID) {
case 0x0F91:
case 0x1101:
case 0x1601:
case 0x800A:
case 0x803B:
case 0x8044:
case 0x804E:
case 0x8053:
case 0x8054:
case 0x8066:
case 0x806F:
case 0x807A:
case 0x80CA:
case 0x80DD:
case 0x80F6:
case 0x8115:
case 0x813D:
case 0x813F:
case 0x814A:
case 0x814B:
case 0x8156:
case 0x815E:
case 0x818B:
case 0x819F:
case 0x81A6:
case 0x81AC:
case 0x81AD:
case 0x81C8:
case 0x81E2:
case 0x81E7:
case 0x81E8:
case 0x81F2:
case 0x8218:
case 0x822B:
case 0x826B:
case 0x8293:
case 0x82F9:
case 0x8341:
case 0x8382:
case 0x83A8:
case 0x83D8:
case 0x8411:
case 0x8418:
case 0x846E:
case 0x8477:
case 0x85EA:
case 0x85EB:
case 0x8664:
case 0x8665:
case 0xEA60:
case 0xEA61:
case 0xEA71:
case 0xF001:
case 0xF002:
case 0xF003:
case 0xF004:
description = "Silicon Labs CP210x USB UART converter";
break;
}
if (description != NULL)
goto SILICON;
break;
}
case VENDOR_SILICON2:
{
switch (productID) {
case 0xEA61:
description = "Silicon Labs GPRS USB Modem";
break;
}
if (description != NULL)
goto SILICON;
break;
}
case VENDOR_SILICON3:
{
switch (productID) {
case 0xEA6A:
description = "Silicon Labs GPRS USB Modem 100EU";
break;
}
if (description != NULL)
goto SILICON;
break;
}
case VENDOR_BALTECH:
{
switch (productID) {
case 0x9999:
description = "Balteck card reader";
break;
}
if (description != NULL)
goto SILICON;
break;
}
case VENDOR_OWEN:
{
switch (productID) {
case 0x0004:
description = "Owen AC4 USB-RS485 Converter";
break;
}
if (description != NULL)
goto SILICON;
break;
}
case VENDOR_CLIPSAL:
{
switch (productID) {
case 0x0303:
description = "Clipsal 5500PCU C-Bus USB interface";
break;
}
if (description != NULL)
goto SILICON;
break;
}
case VENDOR_JABLOTRON:
{
switch (productID) {
case 0x0001:
description = "Jablotron serial interface";
break;
}
if (description != NULL)
goto SILICON;
break;
}
case VENDOR_WIENER:
{
switch (productID) {
case 0x0010:
case 0x0011:
case 0x0012:
case 0x0015:
description = "W-IE-NE-R Plein & Baus GmbH device";
break;
}
if (description != NULL)
goto SILICON;
break;
}
case VENDOR_WAVESENSE:
{
switch (productID) {
case 0xAAAA:
description = "Wavesense Jazz blood glucose meter";
break;
}
if (description != NULL)
goto SILICON;
break;
}
case VENDOR_VAISALA:
{
switch (productID) {
case 0x0200:
description = "Vaisala USB instrument";
break;
}
if (description != NULL)
goto SILICON;
break;
}
case VENDOR_ELV:
{
switch (productID) {
case 0xE00F:
description = "ELV USB I²C interface";
break;
}
if (description != NULL)
goto SILICON;
break;
}
case VENDOR_WAGO:
{
switch (productID) {
case 0x07A6:
description = "WAGO 750-923 USB Service";
break;
}
if (description != NULL)
goto SILICON;
break;
}
case VENDOR_DW700:
{
switch (productID) {
case 0x9500:
description = "DW700 GPS USB interface";
break;
}
if (description != NULL)
goto SILICON;
break;
}
SILICON:
return new(std::nothrow) SiliconDevice(device, vendorID, productID,
description);
}
return new(std::nothrow) ACMDevice(device, vendorID, productID,

View File

@ -0,0 +1,135 @@
/*
* Copyright 2011, Adrien Destugues <pulkomandy@pulkomandy.ath.cx>
* Distributed under the terms of the MIT License.
*/
#include "Silicon.h"
static const int kBaudrateGeneratorFrequency = 0x384000;
SiliconDevice::SiliconDevice(usb_device device, uint16 vendorID, uint16 productID,
const char *description)
: SerialDevice(device, vendorID, productID, description)
{
}
// Called for each configuration of the device. Return B_OK if the given
// configuration sounds like it is the usb serial one.
status_t
SiliconDevice::AddDevice(const usb_configuration_info *config)
{
status_t status = ENODEV;
if (config->interface_count > 0) {
int32 pipesSet = 0;
usb_interface_info *interface = config->interface[0].active;
for (size_t i = 0; i < interface->endpoint_count; i++) {
usb_endpoint_info *endpoint = &interface->endpoint[i];
if (endpoint->descr->attributes == USB_ENDPOINT_ATTR_BULK) {
if (endpoint->descr->endpoint_address & USB_ENDPOINT_ADDR_DIR_IN) {
SetReadPipe(endpoint->handle);
if (++pipesSet >= 3)
break;
} else {
if (endpoint->descr->endpoint_address) {
SetControlPipe(endpoint->handle);
SetWritePipe(endpoint->handle);
pipesSet += 2;
if (pipesSet >= 3)
break;
}
}
}
}
if (pipesSet >= 3) {
status = B_OK;
}
}
return status;
}
// Called on opening the device - Good time to enable the UART ?
status_t
SiliconDevice::ResetDevice()
{
uint16_t enableUart = 1;
return WriteConfig(ENABLE_UART, &enableUart, 2);
}
status_t
SiliconDevice::SetLineCoding(usb_cdc_line_coding *lineCoding)
{
uint16_t divider = kBaudrateGeneratorFrequency / lineCoding->speed ;
status_t result = WriteConfig(SET_BAUDRATE_DIVIDER, &divider, 2);
if (result != B_OK) return result;
uint16_t data = 0;
switch (lineCoding->stopbits) {
case USB_CDC_LINE_CODING_1_STOPBIT: data = 0; break;
case USB_CDC_LINE_CODING_2_STOPBITS: data = 2; break;
default:
TRACE_ALWAYS("= SiliconDevice::SetLineCoding(): Wrong stopbits param: %d\n",
lineCoding->stopbits);
break;
}
switch (lineCoding->parity) {
case USB_CDC_LINE_CODING_NO_PARITY: data |= 0 << 4; break;
case USB_CDC_LINE_CODING_EVEN_PARITY: data |= 2 << 4; break;
case USB_CDC_LINE_CODING_ODD_PARITY: data |= 1 << 4; break;
default:
TRACE_ALWAYS("= SiliconDevice::SetLineCoding(): Wrong parity param: %d\n",
lineCoding->parity);
break;
}
data |= lineCoding->databits << 8;
return WriteConfig(SET_LINE_FORMAT, &data, 2);
}
status_t
SiliconDevice::SetControlLineState(uint16 state)
{
uint16_t control = 0;
control |= 0x0300; // We are updating DTR and RTS
control |= (state & USB_CDC_CONTROL_SIGNAL_STATE_RTS) ? 2 : 0;
control |= (state & USB_CDC_CONTROL_SIGNAL_STATE_DTR) ? 1 : 0;
return WriteConfig(SET_STATUS, &control, 2);
}
status_t SiliconDevice::WriteConfig(CP210XRequest request, uint16_t* data,
size_t size)
{
size_t replyLength = 0;
status_t result;
// Small requests (16 bits and less) use the "value" field for their data.
// Bigger ones use the actual buffer.
if (size <= 2) {
result = gUSBModule->send_request(Device(),
USB_REQTYPE_VENDOR | USB_REQTYPE_DEVICE_OUT, request, data[0], 0, 0,
NULL, &replyLength);
} else {
result = gUSBModule->send_request(Device(),
USB_REQTYPE_VENDOR | USB_REQTYPE_DEVICE_OUT, request, 0x0000, 0,
size, data, &replyLength);
}
if (result != B_OK) {
TRACE_ALWAYS("= SiliconDevice request failed: 0x%08x (%s)\n",
result, strerror(result));
}
return result;
}

View File

@ -0,0 +1,127 @@
/*
* Copyright 2011, Adrien Destugues <pulkomandy@pulkomandy.ath.cx>
* Distributed under the terms of the MIT License.
*/
#ifndef _USB_SILICON_H_
#define _USB_SILICON_H_
#include "SerialDevice.h"
class SiliconDevice : public SerialDevice {
public:
SiliconDevice(usb_device device, uint16 vendorID,
uint16 productID, const char *description);
virtual status_t AddDevice(const usb_configuration_info *config);
virtual status_t ResetDevice();
virtual status_t SetLineCoding(usb_cdc_line_coding *coding);
virtual status_t SetControlLineState(uint16 state);
private:
enum CP210XRequest {
ENABLE_UART = 0,
/* 1 to enable the UART function, 0 to disable
* (some Silicon Labs chips have other functions such as GPIOs) */
SET_BAUDRATE_DIVIDER = 1,
GET_BAUDRATE_DIVIDER = 2,
/*
Baudrate base clock is 3686400
3686400 / 32 = 115200
...
3686400 / 384 = 9600
*/
SET_LINE_FORMAT = 3,
GET_LINE_FORMAT = 4,
/*
DataBits << 0x100 | Parity << 0x10 | StopBits
Databits in [5,9]
Parity :
0 = none
1 = odd
2 = even
3 = mark
4 = space
Stop bits:
0 = 1 stop bit
1 = 1.5 stop bits
2 = 2 stop bits
*/
SET_BREAK = 5,
/* 1 to enable, 0 to disable */
IMMEDIATE_CHAR = 6,
SET_STATUS = 7,
GET_STATUS = 8,
/*
bit 0 = DTR
bit 1 = RTS
bit 4 = CTS
bit 5 = DSR
bit 6 = RING
bit 7 = DCD
bit 8 = WRITE_DTR (unset to not touch DTR)
bit 9 = WRITE_RTS (unset to not touch RTS)
*/
SET_XON = 9,
SET_XOFF = 10,
SET_EVENTMASK = 11,
GET_EVENTMASK = 12,
SET_CHAR = 13,
GET_CHARS = 14,
GET_PROPS = 15,
GET_COMM_STATUS = 16,
RESET = 17,
PURGE = 18,
SET_FLOW = 19,
GET_FLOW = 20,
/* Hardware flow control setup */
EMBED_EVENTS = 21,
GET_EVENTSTATE = 22,
SET_CHARS = 0x19
};
private:
status_t WriteConfig(CP210XRequest request, uint16_t* data,
size_t size);
};
#define VENDOR_RENESAS 0x045B
#define VENDOR_AKATOM 0x0471
#define VENDOR_PIRELLI 0x0489
#define VENDOR_CYPHERLAB 0x0745
#define VENDOR_GEMALTO 0x08E6
#define VENDOR_DIGIANSWER 0x08FD
#define VENDOR_MEI 0x0BED
#define VENDOR_DYNASTREAM 0x0FCF
#define VENDOR_KNOCKOFF 0x10A6
#define VENDOR_SIEMENS 0x10AB
#define VENDOR_NOKIA 0x10B5
#define VENDOR_SILICON 0x10C4
#define VENDOR_SILICON2 0x10C5
#define VENDOR_SILICON3 0x10CE
#define VENDOR_BALTECH 0x13AD
#define VENDOR_OWEN 0x1555
#define VENDOR_CLIPSAL 0x166A
#define VENDOR_JABLOTRON 0x16D6
#define VENDOR_WIENER 0x16DC
#define VENDOR_WAVESENSE 0x17F4
#define VENDOR_VAISALA 0x1843
#define VENDOR_ELV 0x18EF
#define VENDOR_WAGO 0x1BE3
#define VENDOR_DW700 0x413C
#endif //_USB_SILICON_H_