USBUART
A library for reading/wring data via USB-UART adapters
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Modules
ftdi.cpp
Go to the documentation of this file.
1 
5 /* This file is part of USBUART Library. http://usbuart.info/
6  *
7  * Copyright (C) 2016 Eugene Hutorny <eugene@hutorny.in.ua>
8  *
9  * The USBUART Library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public License v2
11  * as published by the Free Software Foundation;
12  *
13  * The USBUART Library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
16  * See the GNU Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public License
19  * along with the USBUART Library; if not, see
20  * <http://www.gnu.org/licenses/gpl-2.0.html>.
21  */
22 
23 #include "usbuart.hpp"
24 #include <libusb.h>
25 namespace usbuart {
26 
27 //TODO sendbreak
28 
29 class ftdi : public generic {
30 public:
31  static constexpr uint8_t set_flowcontrol_req = 0x02;
32  static constexpr uint8_t set_baudrate_req = 0x03;
33  static constexpr uint8_t set_data_req = 0x04;
34 
35  static constexpr unsigned high_clk = 120*1000*1000;
36  static constexpr unsigned low_clk = 48*1000*1000;
37  enum status_bit {
38  data_ready,
39  overrun_error,
40  parity_error,
41  framing_error,
42  break_interrupt,
43  transmitter_hre, /* Transmitter Holding Register */
44  transmitter_empty, /* Transmitter Empty */
45  receiver_error /* Error in receiver FIFO */
46  };
47 
48  static constexpr uint8_t error_mask = (1<<break_interrupt) |
49  (1<<framing_error) | (1<<parity_error) | (1<<overrun_error);
50 
51  static const struct interface h_ifcs[];
52  static const struct interface l_ifc;
53  ~ftdi() noexcept { }
54 
55  void read_callback(libusb_transfer* readxfer, size_t& readpos) noexcept {
56  if( readxfer->actual_length < 2 ) {
57  log.w(__,"malformed transfer");
58  readxfer->actual_length = 0;
59  return;
60  }
61  readpos = 2;
62  if( uint8_t err = (readxfer->buffer[1] & error_mask) ) {
63  errors |= err;
64  log.w(__,"error %02x:%s%s%s%s", err,
65  (err&(1<<break_interrupt) ? " break" : ""),
66  (err&(1<<framing_error ) ? " framing" : ""),
67  (err&(1<<parity_error ) ? " parity" : ""),
68  (err&(1<<overrun_error ) ? " overrun" : "")
69  );
70  }
71  }
72 
73 
74  void compute_divisors(baudrate_t baudrate,
75  uint16_t &value, uint16_t &index) const noexcept {
76  /* FT8U232AM supports only 4 sub-integer prescalers.
77  * FT232B and newer chips support 8 sub-integer prescalers
78  * (See AN232B-05_BaudRates.pdf for details)
79  * For simplicity reason, FT8U232AM nuances are disregarded
80  * FTn232H supports clock divisors 10 or 16
81  * however, low baudrates would overflow 14 bit divisor
82  */
83  static constexpr const uint16_t mapper[8] = {
84  0x0000, 0xC000, 0x8000, 0x0100, 0x4000, 0x4100, 0x8100, 0xC100
85  };
86  static constexpr unsigned low_limit = (high_clk / 10) >> 14;
87  const unsigned clk = isH ? high_clk : low_clk;
88 
89  /* if chip supports and divisor may fit 14 bits use high speed mode */
90  const unsigned prescaler = isH && (baudrate > low_limit) ? 10 : 16;
91  unsigned divisor = (clk << 3)/baudrate + (prescaler >> 1) - 1;
92  divisor /= prescaler;
93  index = (mapper[divisor & 7] & 0x0100) | (prescaler == 10 ? 0x0200 : 0);
94  value = ((divisor >> 3) & 0x3FFF) | (mapper[divisor & 7] & 0xC000);
95  }
96 
97  void reset() const throw(error_t) {
98  write_cv(0, 0, ifcnum);
99  }
100 
101  void setbaudrate(baudrate_t baudrate) const throw(error_t) {
102  uint16_t index;
103  uint16_t value;
104  compute_divisors(baudrate, value, index);
105  log.i(__,"baudrate=%d, i=%#04X v=%#04X", baudrate, index, value);
106  write_cv(set_baudrate_req, value, index | ifcnum);
107  }
108 
109  void setup(const eia_tia_232_info& info) const throw(error_t) {
110  setbaudrate(info.baudrate);
111  setlineprops(info);
112  reset();
114  }
115 protected:
116  bool isH;
117  uint8_t errors;
118 private:
119  inline ftdi(libusb_device_handle* d, uint8_t num, bool ish) throw(error_t)
120  : generic(d, ish?h_ifcs[num]:l_ifc, num), isH(ish) {}
121  void setlineprops(const eia_tia_232_info& info) const throw(error_t) {
122  uint16_t value =
123  info.databits |
124  (((uint16_t)info.parity)<<8) |
125  (((uint16_t)info.stopbits)<<11);
126  write_cv(set_data_req, value, ifcnum);
127  write_cv(set_flowcontrol_req, info.flowcontrol, ifcnum);
128  }
129  static class factory : driver::factory {
130  driver* create(libusb_device_handle*, uint8_t) const throw(error_t);
131  } _factory;
132 };
133 
134 const struct interface ftdi::l_ifc = {
135  0x1|LIBUSB_ENDPOINT_IN, 0x2|LIBUSB_ENDPOINT_OUT, 64,
136 };
137 
138 static constexpr size_t chunk_size = 64; /* 512 causes out of band data
139  e.g. status bytes, to appear in-band
140  */
141 
142 const struct interface ftdi::h_ifcs[] = {
143  { 0x1|LIBUSB_ENDPOINT_IN, 0x2|LIBUSB_ENDPOINT_OUT, chunk_size, },
144  { 0x3|LIBUSB_ENDPOINT_IN, 0x4|LIBUSB_ENDPOINT_OUT, chunk_size, },
145  { 0x5|LIBUSB_ENDPOINT_IN, 0x6|LIBUSB_ENDPOINT_OUT, chunk_size, },
146  { 0x7|LIBUSB_ENDPOINT_IN, 0x8|LIBUSB_ENDPOINT_OUT, chunk_size, }
147 };
148 
149 ftdi::factory ftdi::_factory;
150 
151 driver* ftdi::factory::create(libusb_device_handle* handle, uint8_t num) const throw(error_t) {
152  static constexpr const uint16_t table[] = {
153  /* only original FTDI vid/pid's are supported at this time
154  * See TN_100_USB_VID-PID_Guidelines.pdf and
155  * DS_FT230X.pdf for details */
156  0x6001, 0x6010, 0x6011, 0x6014, 0x6015 };
157 
158  static constexpr const uint16_t high_speed[] = { 0x6010, 0x6011, 0x6014 };
159  /* 0x6010 pid is used for FT2232C/D/L (normal speed)
160  * and FT2232HL/Q (high speed) devices. TN_104 says:
161  * If the device type is not known then check the bcdDevice parameter to get
162  * an idea of what generation device is in use.
163  * 0x0200 = FT232/245AM
164  * 0x0400 = FT232/245BL
165  * 0x0500 = FT2232D
166  * 0x0600 = FT232R
167  * 0x0700 = FT2232H
168  * 0x0800 = FT4232H */
169 
170  bool ish = false;
171  if( num >= countof(h_ifcs) ) {
172  log.e(__,"interface #%d exceeds limit %d", num, countof(h_ifcs));
174  }
175 
176  libusb_device* dev = libusb_get_device(handle);
177  libusb_device_descriptor desc;
178  libusb_get_device_descriptor(dev, &desc);
179 
180  /* limit to FTDI VID */
181  if( desc.idVendor != 0x0403 ) return nullptr;
182 
183  bool found = false;
184  for(auto&& i : table) {
185  if( (found = (i == desc.idProduct)) )
186  break;
187  }
188  if( ! found ) {
189  ish = desc.bcdDevice == 0x0700 ||
190  desc.bcdDevice == 0x0800 ||
191  desc.bcdDevice == 0x0900;
192  } else {
193  ish =((desc.idProduct == high_speed[0] && desc.bcdDevice == 0x0700) ||
194  desc.idProduct == high_speed[1] ||
195  desc.idProduct == high_speed[2] );
196  }
197  if( ! ish && num ) {
198  log.e(__,"interface #%d exceeds limit %d", num, 0);
200  }
201 
202  //FIXME
203  // if (libusb_get_config_descriptor(dev, 0, &config0) < 0)
204  // return packet_size;
205 
206  //TODO actually probe and init driver here
207  return new ftdi(handle, ish, num);
208 }
209 } /* namespace usbuart */
uint32_t baudrate_t
Baud rate data type.
Definition: usbuart.h:35
implementation header USBUART Library
void setup(const eia_tia_232_info &) const
setup protocol on the hardware level
Definition: usbuart.hpp:143
USBUART namespace.
Definition: usbuart.h:29
invalid param passed to the API
error_t
API Error codes.
Definition: usbuart.h:177