jehanne/sys/src/cmd/usb/serial/ftdi.c

1600 lines
38 KiB
C

/* Copyright (c) 20XX 9front
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
/* Future Technology Devices International serial ports */
#include <u.h>
#include <lib9.h>
#include <thread.h>
#include <9P2000.h>
#include <9p.h>
#include "usb.h"
#include "serial.h"
enum {
/* used by devices which don't provide their own Vid */
FTVid = 0x0403,
FTSheevaVid = 0x9E88,
FTSheevaDid = 0x9E8F,
FTOpenRDUltDid = 0x9E90,
FTSIODid = 0x8372, /* Product Id SIO appl'n of 8U100AX */
FT8U232AMDid = 0x6001, /* Similar device to SIO above */
FT8U232AMALTDid = 0x6006, /* FT's alternate Did for above*/
FT8U2232CDid = 0x6010, /* Dual channel device */
FTRELAISDid = 0xFA10, /* Relais device */
/* NF reader */
FTNFRICVid = 0x0DCD,
FTNFRICDid = 0x0001,
FTACTZWAVEDid = 0xF2D0, /* www.irtrans.de device */
/*
* ACT Solutions HomePro ZWave interface
* http://www.act-solutions.com/HomePro.htm)
*/
FTIRTRANSDid = 0xFC60,
/*
* www.thoughttechnology.com/ TT-USB
*/
FTTTUSBDid = 0xFF20,
/* iPlus device */
FTIPLUSDid = 0xD070,
/* www.crystalfontz.com devices */
FTXF632Did = 0xFC08, /* 632: 16x2 Character Display */
FTXF634Did = 0xFC09, /* 634: 20x4 Character Display */
FTXF547Did = 0xFC0A, /* 547: Two line Display */
FTXF633Did = 0xFC0B, /* 633: 16x2 Character Display with Keys */
FTXF631Did = 0xFC0C, /* 631: 20x2 Character Display */
FTXF635Did = 0xFC0D, /* 635: 20x4 Character Display */
FTXF640Did = 0xFC0E, /* 640: Two line Display */
FTXF642Did = 0xFC0F, /* 642: Two line Display */
/*
* Video Networks Limited / Homechoice in the UK
* use an ftdi-based device for their 1Mb broadband
*/
FTVNHCPCUSBDDid = 0xfe38,
/*
* PCDJ use ftdi based dj-controllers
* DAC-2 device http://www.pcdjhardware.com/DAC2.asp
*/
FTPCDJDAC2Did = 0xFA88,
/*
* Matrix Orbital LCD displays,
* which are the FT232BM (similar to the 8U232AM)
*/
FTMTXORB0Did = 0xFA00,
FTMTXORB1Did = 0xFA01,
FTMTXORB2Did = 0xFA02,
FTMTXORB3Did = 0xFA03,
FTMTXORB4Did = 0xFA04,
FTMTXORB5Did = 0xFA05,
FTMTXORB6Did = 0xFA06,
/* Interbiometrics USB I/O Board */
INTERBIOMVid = 0x1209,
INTERBIOMIOBRDDid = 0x1002,
INTERBIOMMINIIOBRDDid = 0x1006,
/*
* The following are the values for the Perle Systems
* UltraPort USB serial converters
*/
FTPERLEULTRAPORTDid = 0xF0C0,
/*
* Sealevel SeaLINK+ adapters.
*/
SEALEVELVid = 0x0c52,
SEALEVEL2101Did = 0x2101, /* SeaLINK+232 (2101/2105) */
SEALEVEL2102Did = 0x2102, /* SeaLINK+485 (2102) */
SEALEVEL2103Did = 0x2103, /* SeaLINK+232I (2103) */
SEALEVEL2104Did = 0x2104, /* SeaLINK+485I (2104) */
SEALEVEL22011Did = 0x2211, /* SeaPORT+2/232 (2201) Port 1 */
SEALEVEL22012Did = 0x2221, /* SeaPORT+2/232 (2201) Port 2 */
SEALEVEL22021Did = 0x2212, /* SeaPORT+2/485 (2202) Port 1 */
SEALEVEL22022Did = 0x2222, /* SeaPORT+2/485 (2202) Port 2 */
SEALEVEL22031Did = 0x2213, /* SeaPORT+2 (2203) Port 1 */
SEALEVEL22032Did = 0x2223, /* SeaPORT+2 (2203) Port 2 */
SEALEVEL24011Did = 0x2411, /* SeaPORT+4/232 (2401) Port 1 */
SEALEVEL24012Did = 0x2421, /* SeaPORT+4/232 (2401) Port 2 */
SEALEVEL24013Did = 0x2431, /* SeaPORT+4/232 (2401) Port 3 */
SEALEVEL24014Did = 0x2441, /* SeaPORT+4/232 (2401) Port 4 */
SEALEVEL24021Did = 0x2412, /* SeaPORT+4/485 (2402) Port 1 */
SEALEVEL24022Did = 0x2422, /* SeaPORT+4/485 (2402) Port 2 */
SEALEVEL24023Did = 0x2432, /* SeaPORT+4/485 (2402) Port 3 */
SEALEVEL24024Did = 0x2442, /* SeaPORT+4/485 (2402) Port 4 */
SEALEVEL24031Did = 0x2413, /* SeaPORT+4 (2403) Port 1 */
SEALEVEL24032Did = 0x2423, /* SeaPORT+4 (2403) Port 2 */
SEALEVEL24033Did = 0x2433, /* SeaPORT+4 (2403) Port 3 */
SEALEVEL24034Did = 0x2443, /* SeaPORT+4 (2403) Port 4 */
SEALEVEL28011Did = 0x2811, /* SeaLINK+8/232 (2801) Port 1 */
SEALEVEL28012Did = 0x2821, /* SeaLINK+8/232 (2801) Port 2 */
SEALEVEL28013Did = 0x2831, /* SeaLINK+8/232 (2801) Port 3 */
SEALEVEL28014Did = 0x2841, /* SeaLINK+8/232 (2801) Port 4 */
SEALEVEL28015Did = 0x2851, /* SeaLINK+8/232 (2801) Port 5 */
SEALEVEL28016Did = 0x2861, /* SeaLINK+8/232 (2801) Port 6 */
SEALEVEL28017Did = 0x2871, /* SeaLINK+8/232 (2801) Port 7 */
SEALEVEL28018Did = 0x2881, /* SeaLINK+8/232 (2801) Port 8 */
SEALEVEL28021Did = 0x2812, /* SeaLINK+8/485 (2802) Port 1 */
SEALEVEL28022Did = 0x2822, /* SeaLINK+8/485 (2802) Port 2 */
SEALEVEL28023Did = 0x2832, /* SeaLINK+8/485 (2802) Port 3 */
SEALEVEL28024Did = 0x2842, /* SeaLINK+8/485 (2802) Port 4 */
SEALEVEL28025Did = 0x2852, /* SeaLINK+8/485 (2802) Port 5 */
SEALEVEL28026Did = 0x2862, /* SeaLINK+8/485 (2802) Port 6 */
SEALEVEL28027Did = 0x2872, /* SeaLINK+8/485 (2802) Port 7 */
SEALEVEL28028Did = 0x2882, /* SeaLINK+8/485 (2802) Port 8 */
SEALEVEL28031Did = 0x2813, /* SeaLINK+8 (2803) Port 1 */
SEALEVEL28032Did = 0x2823, /* SeaLINK+8 (2803) Port 2 */
SEALEVEL28033Did = 0x2833, /* SeaLINK+8 (2803) Port 3 */
SEALEVEL28034Did = 0x2843, /* SeaLINK+8 (2803) Port 4 */
SEALEVEL28035Did = 0x2853, /* SeaLINK+8 (2803) Port 5 */
SEALEVEL28036Did = 0x2863, /* SeaLINK+8 (2803) Port 6 */
SEALEVEL28037Did = 0x2873, /* SeaLINK+8 (2803) Port 7 */
SEALEVEL28038Did = 0x2883, /* SeaLINK+8 (2803) Port 8 */
/* KOBIL Vendor ID chipcard terminals */
KOBILVid = 0x0d46,
KOBILCONVB1Did = 0x2020, /* KOBIL Konverter for B1 */
KOBILCONVKAANDid = 0x2021, /* KOBILKonverter for KAAN */
/* Icom ID-1 digital transceiver */
ICOMID1Vid = 0x0C26,
ICOMID1Did = 0x0004,
FTASKRDR400Did = 0xC991, /* ASK RDR 400 series card reader */
FTDSS20Did = 0xFC82, /* DSS-20 Sync Station for Sony Ericsson P800 */
/*
* Home Electronics (www.home-electro.com) USB gadgets
*/
FTHETIRA1Did = 0xFA78, /* Tira-1 IR transceiver */
/*
* An infrared receiver and transmitter using the 8U232AM chip
* http://www.usbuirt.com
*/
FTUSBUIRTDid = 0xF850,
FTELVUR100Did = 0xFB58, /* USB-RS232-Umsetzer (UR 100) */
FTELVUM100Did = 0xFB5A, /* USB-Modul UM 100 */
FTELVUO100Did = 0xFB5B, /* USB-Modul UO 100 */
FTELVALC8500Did = 0xF06E, /* ALC 8500 Expert */
FTELVCLI7000Did = 0xFB59, /* Computer-Light-Interface */
FTELVPPS7330Did = 0xFB5C, /* Processor-Power-Supply (PPS 7330) */
FTELVTFM100Did = 0xFB5D, /* Temperartur-Feuchte Messgeraet (TFM 100) */
FTELVUDF77Did = 0xFB5E, /* USB DCF Funkurh (UDF 77) */
FTELVUIO88Did = 0xFB5F, /* USB-I/O Interface (UIO 88) */
FTELVUAD8Did = 0xF068, /* USB-AD-Wandler (UAD 8) */
FTELVUDA7Did = 0xF069, /* USB-DA-Wandler (UDA 7) */
FTELVUSI2Did = 0xF06A, /* USB-Schrittmotoren-Interface (USI 2) */
FTELVT1100Did = 0xF06B, /* Thermometer (T 1100) */
FTELVPCD200Did = 0xF06C, /* PC-Datenlogger (PCD 200) */
FTELVULA200Did = 0xF06D, /* USB-LCD-Ansteuerung (ULA 200) */
FTELVFHZ1000PCDid= 0xF06F, /* FHZ 1000 PC */
FTELVCSI8Did = 0xE0F0, /* Computer-Schalt-Interface (CSI 8) */
FTELVEM1000DLDid= 0xE0F1, /* PC-Datenlogger fuer Energiemonitor (EM 1000 DL) */
FTELVPCK100Did = 0xE0F2, /* PC-Kabeltester (PCK 100) */
FTELVRFP500Did = 0xE0F3, /* HF-Leistungsmesser (RFP 500) */
FTELVFS20SIGDid = 0xE0F4, /* Signalgeber (FS 20 SIG) */
FTELVWS300PCDid = 0xE0F6, /* PC-Wetterstation (WS 300 PC) */
FTELVFHZ1300PCDid= 0xE0E8, /* FHZ 1300 PC */
FTELVWS500Did = 0xE0E9, /* PC-Wetterstation (WS 500) */
/*
* Definitions for ID TECH (http://www.idt-net.com) devices
*/
IDTECHVid = 0x0ACD, /* ID TECH Vendor ID */
IDTECHIDT1221UDid= 0x0300, /* IDT1221U USB to RS-232 */
/*
* Definitions for Omnidirectional Control Technology, Inc. devices
*/
OCTVid = 0x0B39, /* OCT vendor ID */
/*
* Note: OCT US101 is also rebadged as Dick Smith Electronics
* (NZ) XH6381, Dick Smith Electronics (Aus) XH6451, and SIIG
* Inc. model US2308 hardware version 1.
*/
OCTUS101Did = 0x0421, /* OCT US101 USB to RS-232 */
/*
* infrared receiver for access control with IR tags
*/
FTPIEGROUPDid = 0xF208,
/*
* Definitions for Artemis astronomical USB based cameras
* http://www.artemisccd.co.uk/
*/
FTARTEMISDid = 0xDF28, /* All Artemis Cameras */
FTATIKATK16Did = 0xDF30, /* ATIK ATK-16 Grayscale Camera */
FTATIKATK16CDid = 0xDF32, /* ATIK ATK-16C Colour Camera */
FTATIKATK16HRDid= 0xDF31, /* ATIK ATK-16HR Grayscale */
FTATIKATK16HRCDid= 0xDF33, /* ATIK ATK-16HRC Colour Camera */
/*
* Protego products
*/
PROTEGOSPECIAL1 = 0xFC70, /* special/unknown device */
PROTEGOR2X0 = 0xFC71, /* R200-USB TRNG unit (R210, R220, and R230) */
PROTEGOSPECIAL3 = 0xFC72, /* special/unknown device */
PROTEGOSPECIAL4 = 0xFC73, /* special/unknown device */
/*
* Gude Analog- und Digitalsysteme GmbH
*/
FTGUDEADSE808Did = 0xE808,
FTGUDEADSE809Did = 0xE809,
FTGUDEADSE80ADid = 0xE80A,
FTGUDEADSE80BDid = 0xE80B,
FTGUDEADSE80CDid = 0xE80C,
FTGUDEADSE80DDid = 0xE80D,
FTGUDEADSE80EDid = 0xE80E,
FTGUDEADSE80FDid = 0xE80F,
FTGUDEADSE888Did = 0xE888, /* Expert ISDN Control USB */
FTGUDEADSE889Did = 0xE889, /* USB RS-232 OptoBridge */
FTGUDEADSE88ADid = 0xE88A,
FTGUDEADSE88BDid = 0xE88B,
FTGUDEADSE88CDid = 0xE88C,
FTGUDEADSE88DDid = 0xE88D,
FTGUDEADSE88EDid = 0xE88E,
FTGUDEADSE88FDid = 0xE88F,
/*
* Linx Technologies
*/
LINXSDMUSBQSSDid= 0xF448, /* Linx SDM-USB-QS-S */
LINXMASTERDEVEL2Did= 0xF449, /* Linx Master Development.0 */
LINXFUTURE0Did = 0xF44A, /* Linx future device */
LINXFUTURE1Did = 0xF44B, /* Linx future device */
LINXFUTURE2Did = 0xF44C, /* Linx future device */
/*
* CCS Inc. ICDU/ICDU40 - the FT232BM used in a in-circuit-debugger
* unit for PIC16's/PIC18's
*/
FTCCSICDU200Did = 0xF9D0,
FTCCSICDU401Did = 0xF9D1,
/* Inside Accesso contactless reader (http://www.insidefr.com) */
INSIDEACCESSO = 0xFAD0,
/*
* Intrepid Control Systems (http://www.intrepidcs.com/)
* ValueCAN and NeoVI
*/
INTREDidVid = 0x093C,
INTREDidVALUECANDid= 0x0601,
INTREDidNEOVIDid= 0x0701,
/*
* Falcom Wireless Communications GmbH
*/
FALCOMVid = 0x0F94,
FALCOMTWISTDid = 0x0001, /* Falcom Twist USB GPRS modem */
FALCOMSAMBADid = 0x0005, /* Falcom Samba USB GPRS modem */
/*
* SUUNTO
*/
FTSUUNTOSPORTSDid= 0xF680, /* Suunto Sports instrument */
/*
* B&B Electronics
*/
BANDBVid = 0x0856, /* B&B Electronics Vendor ID */
BANDBUSOTL4Did = 0xAC01, /* USOTL4 Isolated RS-485 */
BANDBUSTL4Did = 0xAC02, /* USTL4 RS-485 Converter */
BANDBUSO9ML2Did = 0xAC03, /* USO9ML2 Isolated RS-232 */
/*
* RM Michaelides CANview USB (http://www.rmcan.com)
* CAN fieldbus interface adapter
*/
FTRMCANVIEWDid = 0xfd60,
/*
* EVER Eco Pro UPS (http://www.ever.com.pl/)
*/
EVERECOPROCDSDid = 0xe520, /* RS-232 converter */
/*
* 4N-GALAXY.DE PIDs for CAN-USB, USB-RS232, USB-RS422, USB-RS485,
* USB-TTY activ, USB-TTY passiv. Some PIDs are used by several devices
*/
FT4NGALAXYDE0Did = 0x8372,
FT4NGALAXYDE1Did = 0xF3C0,
FT4NGALAXYDE2Did = 0xF3C1,
/*
* Mobility Electronics products.
*/
MOBILITYVid = 0x1342,
MOBILITYUSBSERIALDid= 0x0202, /* EasiDock USB 200 serial */
/*
* microHAM product IDs (http://www.microham.com)
*/
FTMHAMKWDid = 0xEEE8, /* USB-KW interface */
FTMHAMYSDid = 0xEEE9, /* USB-YS interface */
FTMHAMY6Did = 0xEEEA, /* USB-Y6 interface */
FTMHAMY8Did = 0xEEEB, /* USB-Y8 interface */
FTMHAMICDid = 0xEEEC, /* USB-IC interface */
FTMHAMDB9Did = 0xEEED, /* USB-DB9 interface */
FTMHAMRS232Did = 0xEEEE, /* USB-RS232 interface */
FTMHAMY9Did = 0xEEEF, /* USB-Y9 interface */
/*
* Active Robots product ids.
*/
FTACTIVEROBOTSDid = 0xE548, /* USB comms board */
XSENSCONVERTER0Did = 0xD388,
XSENSCONVERTER1Did = 0xD389,
XSENSCONVERTER2Did = 0xD38A,
XSENSCONVERTER3Did = 0xD38B,
XSENSCONVERTER4Did = 0xD38C,
XSENSCONVERTER5Did = 0xD38D,
XSENSCONVERTER6Did = 0xD38E,
XSENSCONVERTER7Did = 0xD38F,
/*
* Xsens Technologies BV products (http://www.xsens.com).
*/
FTTERATRONIKVCPDid = 0xEC88, /* Teratronik device */
FTTERATRONIKD2XXDid = 0xEC89, /* Teratronik device */
/*
* Evolution Robotics products (http://www.evolution.com/).
*/
EVOLUTIONVid = 0xDEEE,
EVOLUTIONER1Did = 0x0300, /* ER1 Control Module */
/* Pyramid Computer GmbH */
FTPYRAMIDDid = 0xE6C8, /* Pyramid Appliance Display */
/*
* Posiflex inc retail equipment (http://www.posiflex.com.tw)
*/
POSIFLEXVid = 0x0d3a,
POSIFLEXPP7000Did= 0x0300, /* PP-7000II thermal printer */
/*
* Westrex International devices
*/
FTWESTREXMODEL777Did = 0xDC00, /* Model 777 */
FTWESTREXMODEL8900FDid = 0xDC01, /* Model 8900F */
/*
* RR-CirKits LocoBuffer USB (http://www.rr-cirkits.com)
*/
FTRRCIRKITSLOCOBUFFERDid= 0xc7d0, /* LocoBuffer USB */
FTECLOCOM1WIREDid = 0xEA90, /* COM to 1-Wire USB */
/*
* Papouch products (http://www.papouch.com/)
*/
PAPOUCHVid = 0x5050,
PAPOUCHTMUDid = 0x0400, /* TMU USB Thermometer */
/*
* ACG Identification Technologies GmbH products http://www.acg.de/
*/
FTACGHFDUALDid = 0xDD20, /* HF Dual ISO Reader (RFID) */
/*
* new high speed devices
*/
FT4232HDid = 0x6011, /* FTDI FT4232H based device */
/*
* Amontec JTAGkey (http://www.amontec.com/)
*/
AMONKEYDid = 0xCFF8,
};
/* Commands */
enum {
FTRESET = 0, /* Reset the port */
FTSETMODEMCTRL, /* Set the modem control register */
FTSETFLOWCTRL, /* Set flow control register */
FTSETBAUDRATE, /* Set baud rate */
FTSETDATA, /* Set the parameters, parity */
FTGETMODEMSTATUS, /* Retrieve current value of modem ctl */
FTSETEVENTCHAR, /* Set the event character */
FTSETERRORCHAR, /* Set the error character */
FTUNKNOWN,
FTSETLATENCYTIMER, /* Set the latency timer */
FTGETLATENCYTIMER, /* Get the latency timer */
FTSETBITMODE, /* Set bit mode */
FTGETPINS, /* Read pins state */
FTGETE2READ = 0x90, /* Read address from 128-byte I2C EEPROM */
FTSETE2WRITE, /* Write to address on 128-byte I2C EEPROM */
FTSETE2ERASE, /* Erase address on 128-byte I2C EEPROM */
};
/* Port Identifier Table, index for interfaces */
enum {
PITDEFAULT = 0, /* SIOA */
PITA, /* SIOA jtag if there is one */
};
enum {
Rftdireq = 1<<6, /* bit for type of request */
};
/*
* Commands Data size
* Sets have wLength = 0
* Gets have wValue = 0
*/
enum {
FTMODEMSTATUSSZ = 1,
FTLATENCYTIMERSZ= 1,
FTPINSSZ = 1,
FTE2READSZ = 2,
};
/*
* bRequest: FTGETE2READ
* wIndex: Address of word to read
* Data: Will return a word (2 bytes) of data from E2Address
* Results put in the I2C 128 byte EEPROM string eeprom+(2*index)
*/
/*
* bRequest: FTSETE2WRITE
* wIndex: Address of word to read
* wValue: Value of the word
* Data: Will return a word (2 bytes) of data from E2Address
*/
/*
* bRequest: FTSETE2ERASE
* Erases the EEPROM
* wIndex: 0
*/
/*
* bRequest: FTRESET
* wValue: Ctl Val
* wIndex: Port
*/
enum {
FTRESETCTLVAL = 0,
FTRESETCTLVALPURGERX = 1,
FTRESETCTLVALPURGETX = 2,
};
/*
* BmRequestType: SET
* bRequest: FTSETBAUDRATE
* wValue: BaudDivisor value - see below
* Bits 15 to 0 of the 17-bit divisor are placed in the request value.
* Bit 16 is placed in bit 0 of the request index.
*/
/* chip type */
enum {
SIO = 1,
FT8U232AM = 2,
FT232BM = 3,
FT2232C = 4,
FTKINDR = 5,
FT2232H = 6,
FT4232H = 7,
};
enum {
FTb300 = 0,
FTb600 = 1,
FTb1200 = 2,
FTb2400 = 3,
FTb4800 = 4,
FTb9600 = 5,
FTb19200 = 6,
FTb38400 = 7,
FTb57600 = 8,
FTb115200 = 9,
};
/*
* bRequest: FTSETDATA
* wValue: Data characteristics
* bits 0-7 number of data bits
* wIndex: Port
*/
enum {
FTSETDATAParNONE = 0 << 8,
FTSETDATAParODD = 1 << 8,
FTSETDATAParEVEN = 2 << 8,
FTSETDATAParMARK = 3 << 8,
FTSETDATAParSPACE = 4 << 8,
FTSETDATASTOPBITS1 = 0 << 11,
FTSETDATASTOPBITS15 = 1 << 11,
FTSETDATASTOPBITS2 = 2 << 11,
FTSETBREAK = 1 << 14,
};
/*
* bRequest: FTSETMODEMCTRL
* wValue: ControlValue (see below)
* wIndex: Port
*/
/*
* bRequest: FTSETFLOWCTRL
* wValue: Xoff/Xon
* wIndex: Protocol/Port - hIndex is protocol; lIndex is port
*/
enum {
FTDISABLEFLOWCTRL= 0,
FTRTSCTSHS = 1 << 8,
FTDTRDSRHS = 2 << 8,
FTXONXOFFHS = 4 << 8,
};
/*
* bRequest: FTGETLATENCYTIMER
* wIndex: Port
* wLength: 0
* Data: latency (on return)
*/
/*
* bRequest: FTSETBITMODE
* wIndex: Port
* either it is big bang mode, in which case
* wValue: 1 byte L is the big bang mode BIG*
* or BM is
* wValue: 1 byte bitbang mode H, 1 byte bitmask for lines L
*/
enum {
BMSERIAL = 0, /* reset, turn off bit-bang mode */
BIGBMNORMAL = 1, /* normal bit-bang mode */
BIGBMSPI = 2, /* spi bit-bang mode */
BMABM = 1<<8, /* async mode */
BMMPSSE = 2<<8,
BMSYNCBB = 4<<8, /* sync bit-bang -- 2232x and R-type */
BMMCU = 8<<8, /* MCU Host Bus -- 2232x */
BMOPTO = 0x10<<8, /* opto-isolated<<8, 2232x */
BMCBUS = 0x20<<8, /* CBUS pins of R-type chips */
BMSYNCFF = 0x40<<8, /* Single Channel Sync FIFO, 2232H only */
};
/*
* bRequest: FTSETLATENCYTIMER
* wValue: Latency (milliseconds 1-255)
* wIndex: Port
*/
enum {
FTLATENCYDEFAULT = 2,
};
/*
* BmRequestType: SET
* bRequest: FTSETEVENTCHAR
* wValue: EventChar
* wIndex: Port
* 0-7 lower bits event char
* 8 enable
*/
enum {
FTEVCHARENAB = 1<<8,
};
/*
* BmRequestType: SET
* bRequest: FTSETERRORCHAR
* wValue: Error Char
* wIndex: Port
* 0-7 lower bits event char
* 8 enable
*/
enum {
FTERRCHARENAB = 1<<8,
};
/*
* BmRequestType: GET
* bRequest: FTGETMODEMSTATUS
* wIndex: Port
* wLength: 1
* Data: Status
*/
enum {
FTCTSMASK = 0x10,
FTDSRMASK = 0x20,
FTRIMASK = 0x40,
FTRLSDMASK = 0x80,
};
enum {
/* byte 0 of in data hdr */
FTICTS = 1 << 4,
FTIDSR = 1 << 5,
FTIRI = 1 << 6,
FTIRLSD = 1 << 7, /* receive line signal detect */
/* byte 1 of in data hdr */
FTIDR = 1<<0, /* data ready */
FTIOE = 1<<1, /* overrun error */
FTIPE = 1<<2, /* parity error */
FTIFE = 1<<3, /* framing error */
FTIBI = 1<<4, /* break interrupt */
FTITHRE = 1<<5, /* xmitter holding register */
FTITEMT = 1<<6, /* xmitter empty */
FTIFIFO = 1<<7, /* error in rcv fifo */
/* byte 0 of out data hdr len does not include byte 0 */
FTOLENMSK= 0x3F,
FTOPORT = 0x80, /* must be set */
};
/*
* BUG: This keeps growing, there has to be a better way, but without
* devices to try it... We can probably simply look for FTDI in the
* string, or use regular expressions somehow.
*/
Cinfo ftinfo[] = {
{ FTVid, FTACTZWAVEDid },
{ FTSheevaVid, FTSheevaDid },
{ FTVid, FTOpenRDUltDid},
{ FTVid, FTIRTRANSDid },
{ FTVid, FTIPLUSDid },
{ FTVid, FTSIODid },
{ FTVid, FT8U232AMDid },
{ FTVid, FT8U232AMALTDid },
{ FTVid, FT8U2232CDid },
{ FTVid, FTRELAISDid },
{ INTERBIOMVid, INTERBIOMIOBRDDid },
{ INTERBIOMVid, INTERBIOMMINIIOBRDDid },
{ FTVid, FTXF632Did },
{ FTVid, FTXF634Did },
{ FTVid, FTXF547Did },
{ FTVid, FTXF633Did },
{ FTVid, FTXF631Did },
{ FTVid, FTXF635Did },
{ FTVid, FTXF640Did },
{ FTVid, FTXF642Did },
{ FTVid, FTDSS20Did },
{ FTNFRICVid, FTNFRICDid },
{ FTVid, FTVNHCPCUSBDDid },
{ FTVid, FTMTXORB0Did },
{ FTVid, FTMTXORB1Did },
{ FTVid, FTMTXORB2Did },
{ FTVid, FTMTXORB3Did },
{ FTVid, FTMTXORB4Did },
{ FTVid, FTMTXORB5Did },
{ FTVid, FTMTXORB6Did },
{ FTVid, FTPERLEULTRAPORTDid },
{ FTVid, FTPIEGROUPDid },
{ SEALEVELVid, SEALEVEL2101Did },
{ SEALEVELVid, SEALEVEL2102Did },
{ SEALEVELVid, SEALEVEL2103Did },
{ SEALEVELVid, SEALEVEL2104Did },
{ SEALEVELVid, SEALEVEL22011Did },
{ SEALEVELVid, SEALEVEL22012Did },
{ SEALEVELVid, SEALEVEL22021Did },
{ SEALEVELVid, SEALEVEL22022Did },
{ SEALEVELVid, SEALEVEL22031Did },
{ SEALEVELVid, SEALEVEL22032Did },
{ SEALEVELVid, SEALEVEL24011Did },
{ SEALEVELVid, SEALEVEL24012Did },
{ SEALEVELVid, SEALEVEL24013Did },
{ SEALEVELVid, SEALEVEL24014Did },
{ SEALEVELVid, SEALEVEL24021Did },
{ SEALEVELVid, SEALEVEL24022Did },
{ SEALEVELVid, SEALEVEL24023Did },
{ SEALEVELVid, SEALEVEL24024Did },
{ SEALEVELVid, SEALEVEL24031Did },
{ SEALEVELVid, SEALEVEL24032Did },
{ SEALEVELVid, SEALEVEL24033Did },
{ SEALEVELVid, SEALEVEL24034Did },
{ SEALEVELVid, SEALEVEL28011Did },
{ SEALEVELVid, SEALEVEL28012Did },
{ SEALEVELVid, SEALEVEL28013Did },
{ SEALEVELVid, SEALEVEL28014Did },
{ SEALEVELVid, SEALEVEL28015Did },
{ SEALEVELVid, SEALEVEL28016Did },
{ SEALEVELVid, SEALEVEL28017Did },
{ SEALEVELVid, SEALEVEL28018Did },
{ SEALEVELVid, SEALEVEL28021Did },
{ SEALEVELVid, SEALEVEL28022Did },
{ SEALEVELVid, SEALEVEL28023Did },
{ SEALEVELVid, SEALEVEL28024Did },
{ SEALEVELVid, SEALEVEL28025Did },
{ SEALEVELVid, SEALEVEL28026Did },
{ SEALEVELVid, SEALEVEL28027Did },
{ SEALEVELVid, SEALEVEL28028Did },
{ SEALEVELVid, SEALEVEL28031Did },
{ SEALEVELVid, SEALEVEL28032Did },
{ SEALEVELVid, SEALEVEL28033Did },
{ SEALEVELVid, SEALEVEL28034Did },
{ SEALEVELVid, SEALEVEL28035Did },
{ SEALEVELVid, SEALEVEL28036Did },
{ SEALEVELVid, SEALEVEL28037Did },
{ SEALEVELVid, SEALEVEL28038Did },
{ IDTECHVid, IDTECHIDT1221UDid },
{ OCTVid, OCTUS101Did },
{ FTVid, FTHETIRA1Did }, /* special quirk div = 240 baud = B38400 rtscts = 1 */
{ FTVid, FTUSBUIRTDid }, /* special quirk div = 77, baud = B38400 */
{ FTVid, PROTEGOSPECIAL1 },
{ FTVid, PROTEGOR2X0 },
{ FTVid, PROTEGOSPECIAL3 },
{ FTVid, PROTEGOSPECIAL4 },
{ FTVid, FTGUDEADSE808Did },
{ FTVid, FTGUDEADSE809Did },
{ FTVid, FTGUDEADSE80ADid },
{ FTVid, FTGUDEADSE80BDid },
{ FTVid, FTGUDEADSE80CDid },
{ FTVid, FTGUDEADSE80DDid },
{ FTVid, FTGUDEADSE80EDid },
{ FTVid, FTGUDEADSE80FDid },
{ FTVid, FTGUDEADSE888Did },
{ FTVid, FTGUDEADSE889Did },
{ FTVid, FTGUDEADSE88ADid },
{ FTVid, FTGUDEADSE88BDid },
{ FTVid, FTGUDEADSE88CDid },
{ FTVid, FTGUDEADSE88DDid },
{ FTVid, FTGUDEADSE88EDid },
{ FTVid, FTGUDEADSE88FDid },
{ FTVid, FTELVUO100Did },
{ FTVid, FTELVUM100Did },
{ FTVid, FTELVUR100Did },
{ FTVid, FTELVALC8500Did },
{ FTVid, FTPYRAMIDDid },
{ FTVid, FTELVFHZ1000PCDid },
{ FTVid, FTELVCLI7000Did },
{ FTVid, FTELVPPS7330Did },
{ FTVid, FTELVTFM100Did },
{ FTVid, FTELVUDF77Did },
{ FTVid, FTELVUIO88Did },
{ FTVid, FTELVUAD8Did },
{ FTVid, FTELVUDA7Did },
{ FTVid, FTELVUSI2Did },
{ FTVid, FTELVT1100Did },
{ FTVid, FTELVPCD200Did },
{ FTVid, FTELVULA200Did },
{ FTVid, FTELVCSI8Did },
{ FTVid, FTELVEM1000DLDid },
{ FTVid, FTELVPCK100Did },
{ FTVid, FTELVRFP500Did },
{ FTVid, FTELVFS20SIGDid },
{ FTVid, FTELVWS300PCDid },
{ FTVid, FTELVFHZ1300PCDid },
{ FTVid, FTELVWS500Did },
{ FTVid, LINXSDMUSBQSSDid },
{ FTVid, LINXMASTERDEVEL2Did },
{ FTVid, LINXFUTURE0Did },
{ FTVid, LINXFUTURE1Did },
{ FTVid, LINXFUTURE2Did },
{ FTVid, FTCCSICDU200Did },
{ FTVid, FTCCSICDU401Did },
{ FTVid, INSIDEACCESSO },
{ INTREDidVid, INTREDidVALUECANDid },
{ INTREDidVid, INTREDidNEOVIDid },
{ FALCOMVid, FALCOMTWISTDid },
{ FALCOMVid, FALCOMSAMBADid },
{ FTVid, FTSUUNTOSPORTSDid },
{ FTVid, FTRMCANVIEWDid },
{ BANDBVid, BANDBUSOTL4Did },
{ BANDBVid, BANDBUSTL4Did },
{ BANDBVid, BANDBUSO9ML2Did },
{ FTVid, EVERECOPROCDSDid },
{ FTVid, FT4NGALAXYDE0Did },
{ FTVid, FT4NGALAXYDE1Did },
{ FTVid, FT4NGALAXYDE2Did },
{ FTVid, XSENSCONVERTER0Did },
{ FTVid, XSENSCONVERTER1Did },
{ FTVid, XSENSCONVERTER2Did },
{ FTVid, XSENSCONVERTER3Did },
{ FTVid, XSENSCONVERTER4Did },
{ FTVid, XSENSCONVERTER5Did },
{ FTVid, XSENSCONVERTER6Did },
{ FTVid, XSENSCONVERTER7Did },
{ MOBILITYVid, MOBILITYUSBSERIALDid },
{ FTVid, FTACTIVEROBOTSDid },
{ FTVid, FTMHAMKWDid },
{ FTVid, FTMHAMYSDid },
{ FTVid, FTMHAMY6Did },
{ FTVid, FTMHAMY8Did },
{ FTVid, FTMHAMICDid },
{ FTVid, FTMHAMDB9Did },
{ FTVid, FTMHAMRS232Did },
{ FTVid, FTMHAMY9Did },
{ FTVid, FTTERATRONIKVCPDid },
{ FTVid, FTTERATRONIKD2XXDid },
{ EVOLUTIONVid, EVOLUTIONER1Did },
{ FTVid, FTARTEMISDid },
{ FTVid, FTATIKATK16Did },
{ FTVid, FTATIKATK16CDid },
{ FTVid, FTATIKATK16HRDid },
{ FTVid, FTATIKATK16HRCDid },
{ KOBILVid, KOBILCONVB1Did },
{ KOBILVid, KOBILCONVKAANDid },
{ POSIFLEXVid, POSIFLEXPP7000Did },
{ FTVid, FTTTUSBDid },
{ FTVid, FTECLOCOM1WIREDid },
{ FTVid, FTWESTREXMODEL777Did },
{ FTVid, FTWESTREXMODEL8900FDid },
{ FTVid, FTPCDJDAC2Did },
{ FTVid, FTRRCIRKITSLOCOBUFFERDid },
{ FTVid, FTASKRDR400Did },
{ ICOMID1Vid, ICOMID1Did },
{ PAPOUCHVid, PAPOUCHTMUDid },
{ FTVid, FTACGHFDUALDid },
{ FT8U232AMDid, FT4232HDid },
{ FTVid, AMONKEYDid },
{ 0, 0 },
};
static Serialops ftops;
enum {
Packsz = 64, /* default size */
Maxpacksz = 512,
Bufsiz = 4 * 1024,
};
static int
ftdiread(Serialport *p, int index, int req, uint8_t *buf, int len)
{
int res;
Serial *ser;
ser = p->s;
if(req != FTGETE2READ)
index |= p->interfc + 1;
dsprint(2, "serial: ftdiread %#p [%d] req: %#x val: %#x idx:%d buf:%p len:%d\n",
p, p->interfc, req, 0, index, buf, len);
res = usbcmd(ser->dev, Rd2h | Rftdireq | Rdev, req, 0, index, buf, len);
dsprint(2, "serial: ftdiread res:%d\n", res);
return res;
}
static int
ftdiwrite(Serialport *p, int val, int index, int req)
{
int res;
Serial *ser;
ser = p->s;
if(req != FTGETE2READ || req != FTSETE2ERASE || req != FTSETBAUDRATE)
index |= p->interfc + 1;
dsprint(2, "serial: ftdiwrite %#p [%d] req: %#x val: %#x idx:%d\n",
p, p->interfc, req, val, index);
res = usbcmd(ser->dev, Rh2d | Rftdireq | Rdev, req, val, index, nil, 0);
dsprint(2, "serial: ftdiwrite res:%d\n", res);
return res;
}
static int
ftmodemctl(Serialport *p, int set)
{
if(set == 0){
p->mctl = 0;
ftdiwrite(p, 0, 0, FTSETMODEMCTRL);
return 0;
}
p->mctl = 1;
ftdiwrite(p, 0, FTRTSCTSHS, FTSETFLOWCTRL);
return 0;
}
static uint16_t
ft232ambaudbase2div(int baud, int base)
{
int divisor3;
uint16_t divisor;
divisor3 = (base / 2) / baud;
if((divisor3 & 7) == 7)
divisor3++; /* round x.7/8 up to x+1 */
divisor = divisor3 >> 3;
divisor3 &= 7;
if(divisor3 == 1)
divisor |= 0xc000; /* 0.125 */
else if(divisor3 >= 4)
divisor |= 0x4000; /* 0.5 */
else if(divisor3 != 0)
divisor |= 0x8000; /* 0.25 */
if( divisor == 1)
divisor = 0; /* special case for maximum baud rate */
return divisor;
}
enum{
ClockNew = 48000000,
ClockOld = 12000000 / 16,
HetiraDiv = 240,
UirtDiv = 77,
};
static uint16_t
ft232ambaud2div(int baud)
{
return ft232ambaudbase2div(baud, ClockNew);
}
static uint32_t divfrac[8] = { 0, 3, 2, 4, 1, 5, 6, 7};
static uint32_t
ft232bmbaudbase2div(int baud, int base)
{
int divisor3;
uint32_t divisor;
divisor3 = (base / 2) / baud;
divisor = divisor3 >> 3 | divfrac[divisor3 & 7] << 14;
/* Deal with special cases for highest baud rates. */
if( divisor == 1)
divisor = 0; /* 1.0 */
else if( divisor == 0x4001)
divisor = 1; /* 1.5 */
return divisor;
}
static uint32_t
ft232bmbaud2div (int baud)
{
return ft232bmbaudbase2div (baud, ClockNew);
}
static int
customdiv(Serial *ser)
{
if(ser->dev->usb->vid == FTVid && ser->dev->usb->did == FTHETIRA1Did)
return HetiraDiv;
else if(ser->dev->usb->vid == FTVid && ser->dev->usb->did == FTUSBUIRTDid)
return UirtDiv;
fprint(2, "serial: weird custom divisor\n");
return 0; /* shouldn't happen, break as much as I can */
}
static uint32_t
ftbaudcalcdiv(Serial *ser, int baud)
{
int cusdiv;
uint32_t divval;
if(baud == 38400 && (cusdiv = customdiv(ser)) != 0)
baud = ser->baudbase / cusdiv;
if(baud == 0)
baud = 9600;
switch(ser->type) {
case SIO:
switch(baud) {
case 300:
divval = FTb300;
break;
case 600:
divval = FTb600;
break;
case 1200:
divval = FTb1200;
break;
case 2400:
divval = FTb2400;
break;
case 4800:
divval = FTb4800;
break;
case 9600:
divval = FTb9600;
break;
case 19200:
divval = FTb19200;
break;
case 38400:
divval = FTb38400;
break;
case 57600:
divval = FTb57600;
break;
case 115200:
divval = FTb115200;
break;
default:
divval = FTb9600;
break;
}
break;
case FT8U232AM:
if(baud <= 3000000)
divval = ft232ambaud2div(baud);
else
divval = ft232ambaud2div(9600);
break;
case FT232BM:
case FT2232C:
case FTKINDR:
case FT2232H:
case FT4232H:
if(baud <= 3000000)
divval = ft232bmbaud2div(baud);
else
divval = ft232bmbaud2div(9600);
break;
default:
divval = ft232bmbaud2div(9600);
break;
}
return divval;
}
static int
ftsetparam(Serialport *p)
{
int res;
uint16_t val;
uint32_t bauddiv;
val = 0;
if(p->stop == 1)
val |= FTSETDATASTOPBITS1;
else if(p->stop == 2)
val |= FTSETDATASTOPBITS2;
else if(p->stop == 15)
val |= FTSETDATASTOPBITS15;
switch(p->parity){
case 0:
val |= FTSETDATAParNONE;
break;
case 1:
val |= FTSETDATAParODD;
break;
case 2:
val |= FTSETDATAParEVEN;
break;
case 3:
val |= FTSETDATAParMARK;
break;
case 4:
val |= FTSETDATAParSPACE;
break;
};
dsprint(2, "serial: setparam\n");
res = ftdiwrite(p, val, 0, FTSETDATA);
if(res < 0)
return res;
res = ftmodemctl(p, p->mctl);
if(res < 0)
return res;
bauddiv = ftbaudcalcdiv(p->s, p->baud);
res = ftdiwrite(p, bauddiv, (bauddiv>>16) & 1, FTSETBAUDRATE);
dsprint(2, "serial: setparam res: %d\n", res);
return res;
}
static int
hasjtag(Usbdev *udev)
{
/* no string, for now, by default we detect no jtag */
if(udev->product != nil && cistrstr(udev->product, "jtag") != nil)
return 1;
/* blank aijuboard has jtag for initial bringup */
if(udev->csp == 0xffffff)
return 1;
return 0;
}
/* ser locked */
static void
ftgettype(Serial *ser)
{
int i, outhdrsz, dno, pksz;
uint32_t baudbase;
Conf *cnf;
pksz = Packsz;
/* Assume it is not the original SIO device for now. */
baudbase = ClockNew / 2;
outhdrsz = 0;
dno = ser->dev->usb->dno;
cnf = ser->dev->usb->conf[0];
ser->nifcs = 0;
for(i = 0; i < Niface; i++)
if(cnf->iface[i] != nil)
ser->nifcs++;
if(ser->nifcs > 1) {
/*
* Multiple interfaces. default assume FT2232C,
*/
if(dno == 0x500)
ser->type = FT2232C;
else if(dno == 0x600)
ser->type = FTKINDR;
else if(dno == 0x700){
ser->type = FT2232H;
pksz = Maxpacksz;
} else if(dno == 0x800){
ser->type = FT4232H;
pksz = Maxpacksz;
} else
ser->type = FT2232C;
if(hasjtag(ser->dev->usb))
ser->jtag = 0;
/*
* BM-type devices have a bug where dno gets set
* to 0x200 when serial is 0.
*/
if(dno < 0x500)
fprint(2, "serial: warning: dno %d too low for "
"multi-interface device\n", dno);
} else if(dno < 0x200) {
/* Old device. Assume it is the original SIO. */
ser->type = SIO;
baudbase = ClockOld/16;
outhdrsz = 1;
} else if(dno < 0x400)
/*
* Assume its an FT8U232AM (or FT8U245AM)
* (It might be a BM because of the iSerialNumber bug,
* but it will still work as an AM device.)
*/
ser->type = FT8U232AM;
else /* Assume it is an FT232BM (or FT245BM) */
ser->type = FT232BM;
ser->maxrtrans = ser->maxwtrans = pksz;
ser->baudbase = baudbase;
ser->outhdrsz = outhdrsz;
ser->inhdrsz = 2;
ser->Serialops = ftops;
dsprint (2, "serial: detected type: %#x\n", ser->type);
}
int
ftprobe(Serial *ser)
{
Usbdev *ud = ser->dev->usb;
if(matchid(ftinfo, ud->vid, ud->did) == nil)
return -1;
ftgettype(ser);
return 0;
}
static int
ftuseinhdr(Serialport *p, uint8_t *b)
{
if(b[0] & FTICTS)
p->cts = 1;
else
p->cts = 0;
if(b[0] & FTIDSR)
p->dsr = 1;
else
p->dsr = 0;
if(b[0] & FTIRI)
p->ring = 1;
else
p->ring = 0;
if(b[0] & FTIRLSD)
p->rlsd = 1;
else
p->rlsd = 0;
if(b[1] & FTIOE)
p->novererr++;
if(b[1] & FTIPE)
p->nparityerr++;
if(b[1] & FTIFE)
p->nframeerr++;
if(b[1] & FTIBI)
p->nbreakerr++;
return 0;
}
static int
ftsetouthdr(Serialport *p, uint8_t *b, int len)
{
if(p->s->outhdrsz != 0)
b[0] = FTOPORT | (FTOLENMSK & len);
return p->s->outhdrsz;
}
static int
wait4data(Serialport *p, uint8_t *data, int count)
{
int d;
Serial *ser;
ser = p->s;
qunlock(&ser->ql);
d = sendul(p->w4data, 1);
qlock(&ser->ql);
if(d <= 0)
return -1;
if(p->ndata >= count)
p->ndata -= count;
else{
count = p->ndata;
p->ndata = 0;
}
memmove(data, p->data, count);
if(p->ndata != 0)
memmove(p->data, p->data+count, p->ndata);
recvul(p->gotdata);
return count;
}
static int
wait4write(Serialport *p, uint8_t *data, int count)
{
int off, fd;
uint8_t *b;
Serial *ser;
ser = p->s;
b = emallocz(count+ser->outhdrsz, 1);
off = ftsetouthdr(p, b, count);
memmove(b+off, data, count);
fd = p->epout->dfd;
qunlock(&ser->ql);
count = jehanne_write(fd, b, count+off);
qlock(&ser->ql);
free(b);
return count;
}
typedef struct Packser Packser;
struct Packser{
int nb;
uint8_t b[Bufsiz];
};
typedef struct Areader Areader;
struct Areader{
Serialport *p;
Channel *c;
};
static void
shutdownchan(Channel *c)
{
Packser *bp;
while((bp=nbrecvp(c)) != nil)
free(bp);
chanfree(c);
}
int
cpdata(Serial *ser, Serialport *port, uint8_t *out, uint8_t *in, int sz)
{
int i, ncp, ntotcp, pksz;
pksz = ser->maxrtrans;
ntotcp = 0;
for(i = 0; i < sz; i+= pksz){
ftuseinhdr(port, in + i);
if(sz - i > pksz)
ncp = pksz - ser->inhdrsz;
else
ncp = sz - i - ser->inhdrsz;
memmove(out, in + i + ser->inhdrsz, ncp);
out += ncp;
ntotcp += ncp;
}
return ntotcp;
}
static void
epreader(void *u)
{
int dfd, rcount, cl, ntries, recov;
Areader *a;
Channel *c;
Packser *pk;
Serial *ser;
Serialport *p;
threadsetname("epreader proc");
a = u;
p = a->p;
ser = p->s;
c = a->c;
free(a);
qlock(&ser->ql); /* this makes the reader wait end of initialization too */
dfd = p->epin->dfd;
qunlock(&ser->ql);
ntries = 0;
pk = nil;
for(;;) {
if (pk == nil)
pk = emallocz(sizeof(Packser), 1);
Eagain:
rcount = jehanne_read(dfd, pk->b, sizeof pk->b);
if(serialdebug > 5)
dsprint(2, "%d %#ux%#ux ", rcount, p->data[0],
p->data[1]);
if(rcount < 0){
if(ntries++ > 100)
break;
qlock(&ser->ql);
recov = serialrecover(ser, p, nil, "epreader: bulkin error");
qunlock(&ser->ql);
if(recov >= 0)
goto Eagain;
}
if(rcount == 0)
continue;
if(rcount >= ser->inhdrsz){
rcount = cpdata(ser, p, pk->b, pk->b, rcount);
if(rcount != 0){
pk->nb = rcount;
cl = sendp(c, pk);
if(cl < 0){
/*
* if it was a time-out, I don't want
* to give back an error.
*/
rcount = 0;
break;
}
}else
free(pk);
qlock(&ser->ql);
ser->recover = 0;
qunlock(&ser->ql);
ntries = 0;
pk = nil;
}
}
if(rcount < 0)
fprint(2, "%s: error reading %s: %r\n", argv0, p->name);
free(pk);
nbsendp(c, nil);
if(p->w4data != nil)
chanclose(p->w4data);
if(p->gotdata != nil)
chanclose(p->gotdata);
devctl(ser->dev, "detach");
closedev(ser->dev);
}
static void
statusreader(void *u)
{
Areader *a;
Channel *c;
Packser *pk;
Serialport *p;
Serial *ser;
int cl;
p = u;
ser = p->s;
threadsetname("statusreader thread");
/* big buffering, fewer bytes lost */
c = chancreate(sizeof(Packser *), 128);
a = emallocz(sizeof(Areader), 1);
a->p = p;
a->c = c;
incref(&ser->dev->ref);
proccreate(epreader, a, 16*1024);
while((pk = recvp(c)) != nil){
memmove(p->data, pk->b, pk->nb);
p->ndata = pk->nb;
free(pk);
/* consume it all */
while(p->ndata != 0){
cl = recvul(p->w4data);
if(cl < 0)
break;
cl = sendul(p->gotdata, 1);
if(cl < 0)
break;
}
}
shutdownchan(c);
devctl(ser->dev, "detach");
closedev(ser->dev);
}
static int
ftreset(Serial *ser, Serialport *p)
{
int i;
if(p != nil){
ftdiwrite(p, FTRESETCTLVAL, 0, FTRESET);
return 0;
}
p = ser->p;
for(i = 0; i < Maxifc; i++)
if(p[i].s != nil)
ftdiwrite(&p[i], FTRESETCTLVAL, 0, FTRESET);
return 0;
}
static int
ftinit(Serialport *p)
{
Serial *ser;
uint32_t timerval;
int res;
ser = p->s;
if(p->isjtag){
res = ftdiwrite(p, FTSETFLOWCTRL, 0, FTDISABLEFLOWCTRL);
if(res < 0)
return -1;
res = ftdiread(p, FTSETLATENCYTIMER, 0, (uint8_t *)&timerval,
FTLATENCYTIMERSZ);
if(res < 0)
return -1;
dsprint(2, "serial: jtag latency timer is %d\n", timerval);
timerval = 2;
ftdiwrite(p, FTLATENCYDEFAULT, 0, FTSETLATENCYTIMER);
res = ftdiread(p, FTSETLATENCYTIMER, 0, (uint8_t *)&timerval,
FTLATENCYTIMERSZ);
if(res < 0)
return -1;
dsprint(2, "serial: jtag latency timer set to %d\n", timerval);
/* 0xb is the mask for lines. plug dependant? */
ftdiwrite(p, BMMPSSE|0x0b, 0, FTSETBITMODE);
}
incref(&ser->dev->ref);
proccreate(statusreader, p, 8*1024);
return 0;
}
static int
ftsetbreak(Serialport *p, int val)
{
return ftdiwrite(p, (val != 0? FTSETBREAK: 0), 0, FTSETDATA);
}
static int
ftclearpipes(Serialport *p)
{
/* maybe can be done in one... */
ftdiwrite(p, FTRESETCTLVALPURGETX, 0, FTRESET);
ftdiwrite(p, FTRESETCTLVALPURGERX, 0, FTRESET);
return 0;
}
static int
setctlline(Serialport *p, uint8_t val)
{
return ftdiwrite(p, val | (val << 8), 0, FTSETMODEMCTRL);
}
static void
updatectlst(Serialport *p, int val)
{
if(p->rts)
p->ctlstate |= val;
else
p->ctlstate &= ~val;
}
static int
setctl(Serialport *p)
{
int res;
Serial *ser;
ser = p->s;
if(ser->dev->usb->vid == FTVid && ser->dev->usb->did == FTHETIRA1Did){
fprint(2, "serial: cannot set lines for this device\n");
updatectlst(p, CtlRTS|CtlDTR);
p->rts = p->dtr = 1;
return -1;
}
/* NB: you can not set DTR and RTS with one control message */
updatectlst(p, CtlRTS);
res = setctlline(p, (CtlRTS<<8)|p->ctlstate);
if(res < 0)
return res;
updatectlst(p, CtlDTR);
res = setctlline(p, (CtlDTR<<8)|p->ctlstate);
if(res < 0)
return res;
return 0;
}
static int
ftsendlines(Serialport *p)
{
int res;
dsprint(2, "serial: sendlines: %#2.2x\n", p->ctlstate);
res = setctl(p);
dsprint(2, "serial: sendlines res: %d\n", res);
return 0;
}
static int
ftseteps(Serialport *p)
{
char *s;
Serial *ser;
ser = p->s;
s = smprint("maxpkt %d", ser->maxrtrans);
devctl(p->epin, s);
free(s);
s = smprint("maxpkt %d", ser->maxwtrans);
devctl(p->epout, s);
free(s);
return 0;
}
static Serialops ftops = {
.init = ftinit,
.seteps = ftseteps,
.setparam = ftsetparam,
.clearpipes = ftclearpipes,
.reset = ftreset,
.sendlines = ftsendlines,
.modemctl = ftmodemctl,
.setbreak = ftsetbreak,
.wait4data = wait4data,
.wait4write = wait4write,
};