USB Host Shield 2.0
cdcftdi.cpp
Go to the documentation of this file.
1 /* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.
2 
3 This software may be distributed and modified under the terms of the GNU
4 General Public License version 2 (GPL2) as published by the Free Software
5 Foundation and appearing in the file GPL2.TXT included in the packaging of
6 this file. Please note that GPL2 Section 2[b] requires that all works based
7 on this software must also be made publicly available under the terms of
8 the GPL2 ("Copyleft").
9 
10 Contact information
11 -------------------
12 
13 Circuits At Home, LTD
14 Web : http://www.circuitsathome.com
15 e-mail : support@circuitsathome.com
16  */
17 #include "cdcftdi.h"
18 
19 const uint8_t FTDI::epDataInIndex = 1;
20 const uint8_t FTDI::epDataOutIndex = 2;
21 const uint8_t FTDI::epInterruptInIndex = 3;
22 
23 FTDI::FTDI(USB *p, FTDIAsyncOper *pasync, uint16_t idProduct) :
24 pAsync(pasync),
25 pUsb(p),
26 bAddress(0),
27 bNumEP(1),
28 wFTDIType(0),
29 wIdProduct(idProduct) {
30  for(uint8_t i = 0; i < FTDI_MAX_ENDPOINTS; i++) {
31  epInfo[i].epAddr = 0;
32  epInfo[i].maxPktSize = (i) ? 0 : 8;
33  epInfo[i].bmSndToggle = 0;
34  epInfo[i].bmRcvToggle = 0;
35  epInfo[i].bmNakPower = (i == epDataInIndex) ? USB_NAK_NOWAIT : USB_NAK_MAX_POWER;
36  }
37  if(pUsb)
38  pUsb->RegisterDeviceClass(this);
39 }
40 
41 uint8_t FTDI::Init(uint8_t parent, uint8_t port, bool lowspeed) {
42  const uint8_t constBufSize = sizeof (USB_DEVICE_DESCRIPTOR);
43 
44  uint8_t buf[constBufSize];
45  USB_DEVICE_DESCRIPTOR * udd = reinterpret_cast<USB_DEVICE_DESCRIPTOR*>(buf);
46  uint8_t rcode;
47  UsbDevice *p = NULL;
48  EpInfo *oldep_ptr = NULL;
49 
50  uint8_t num_of_conf; // number of configurations
51 
52  AddressPool &addrPool = pUsb->GetAddressPool();
53 
54  USBTRACE("FTDI Init\r\n");
55 
56  if(bAddress) {
57  USBTRACE("FTDI CLASS IN USE??\r\n");
59  }
60  // Get pointer to pseudo device with address 0 assigned
61  p = addrPool.GetUsbDevicePtr(0);
62 
63  if(!p) {
64  USBTRACE("FTDI NO ADDRESS??\r\n");
66  }
67  if(!p->epinfo) {
68  USBTRACE("epinfo\r\n");
70  }
71 
72  // Save old pointer to EP_RECORD of address 0
73  oldep_ptr = p->epinfo;
74 
75  // Temporary assign new pointer to epInfo to p->epinfo in order to avoid toggle inconsistence
76  p->epinfo = epInfo;
77 
78  p->lowspeed = lowspeed;
79 
80  // Get device descriptor
81  rcode = pUsb->getDevDescr(0, 0, sizeof (USB_DEVICE_DESCRIPTOR), buf);
82 
83  // Restore p->epinfo
84  p->epinfo = oldep_ptr;
85 
86  if(rcode) {
87  goto FailGetDevDescr;
88  }
89  if(udd->idVendor != FTDI_VID || udd->idProduct != wIdProduct) {
90  USBTRACE("FTDI Init: Product not supported\r\n");
91  USBTRACE2("Expected VID:", FTDI_VID);
92  USBTRACE2("Found VID:", udd->idVendor);
93 
94  USBTRACE2("Expected PID:", wIdProduct);
95  USBTRACE2("Found PID:", udd->idProduct);
97  }
98 
99  // Save type of FTDI chip
100  wFTDIType = udd->bcdDevice;
101 
102  // Allocate new address according to device class
103  bAddress = addrPool.AllocAddress(parent, false, port);
104 
105  if(!bAddress)
107 
108  // Extract Max Packet Size from the device descriptor
109  epInfo[0].maxPktSize = udd->bMaxPacketSize0;
110  // Some devices set endpoint lengths to zero, which is incorrect.
111  // we should check them, and if zero, set them to 64.
112  if(epInfo[0].maxPktSize == 0) epInfo[0].maxPktSize = 64;
113 
114  // Assign new address to the device
115  rcode = pUsb->setAddr(0, 0, bAddress);
116 
117  if(rcode) {
118  p->lowspeed = false;
119  addrPool.FreeAddress(bAddress);
120  bAddress = 0;
121  USBTRACE2("setAddr:", rcode);
122  return rcode;
123  }
124 
125  USBTRACE2("Addr:", bAddress);
126 
127  p->lowspeed = false;
128 
129  p = addrPool.GetUsbDevicePtr(bAddress);
130 
131  if(!p)
133 
134  p->lowspeed = lowspeed;
135 
136  num_of_conf = udd->bNumConfigurations;
137 
138  // Assign epInfo to epinfo pointer
139  rcode = pUsb->setEpInfoEntry(bAddress, 1, epInfo);
140 
141  if(rcode)
142  goto FailSetDevTblEntry;
143 
144  USBTRACE2("NC:", num_of_conf);
145 
146  for(uint8_t i = 0; i < num_of_conf; i++) {
148 
149  // This interferes with serial output, and should be opt-in for debugging.
150  //HexDumper<USBReadParser, uint16_t, uint16_t> HexDump;
151  //rcode = pUsb->getConfDescr(bAddress, 0, i, &HexDump);
152  //if(rcode)
153  // goto FailGetConfDescr;
154 
155  rcode = pUsb->getConfDescr(bAddress, 0, i, &confDescrParser);
156 
157  if(rcode)
158  goto FailGetConfDescr;
159 
160  if(bNumEP > 1)
161  break;
162  } // for
163 
164  if(bNumEP < 2)
166 
167  USBTRACE2("NumEP:", bNumEP);
168 
169  // Assign epInfo to epinfo pointer
170  rcode = pUsb->setEpInfoEntry(bAddress, bNumEP, epInfo);
171 
172  USBTRACE2("Conf:", bConfNum);
173 
174  // Set Configuration Value
175  rcode = pUsb->setConf(bAddress, 0, bConfNum);
176 
177  if(rcode)
178  goto FailSetConfDescr;
179 
180  // default latency is 16ms on-chip, reduce it to 1
181  rcode = SetLatency(1);
182  if(rcode)
183  goto FailOnLatency;
184 
185 
186  rcode = pAsync->OnInit(this);
187 
188  if(rcode)
189  goto FailOnInit;
190 
191  USBTRACE("FTDI configured\r\n");
192 
193  ready = true;
194  return 0;
195 
196 FailOnLatency:
197 #ifdef DEBUG_USB_HOST
198  USBTRACE("SetLatency: ");
199  goto Fail;
200 #endif
201 
202 FailGetDevDescr:
203 #ifdef DEBUG_USB_HOST
205  goto Fail;
206 #endif
207 
208 FailSetDevTblEntry:
209 #ifdef DEBUG_USB_HOST
211  goto Fail;
212 #endif
213 
214 FailGetConfDescr:
215 #ifdef DEBUG_USB_HOST
217  goto Fail;
218 #endif
219 
220 FailSetConfDescr:
221 #ifdef DEBUG_USB_HOST
223  goto Fail;
224 #endif
225 
226 FailOnInit:
227 #ifdef DEBUG_USB_HOST
228  USBTRACE("OnInit:");
229 
230 Fail:
231  NotifyFail(rcode);
232 #endif
233  Release();
234  return rcode;
235 }
236 
237 void FTDI::EndpointXtract(uint8_t conf, uint8_t iface, uint8_t alt, uint8_t proto __attribute__((unused)), const USB_ENDPOINT_DESCRIPTOR *pep) {
238  ErrorMessage<uint8_t > (PSTR("Conf.Val"), conf);
239  ErrorMessage<uint8_t > (PSTR("Iface Num"), iface);
240  ErrorMessage<uint8_t > (PSTR("Alt.Set"), alt);
241 
242  bConfNum = conf;
243 
244  uint8_t index;
245 
246  if((pep->bmAttributes & bmUSB_TRANSFER_TYPE) == USB_TRANSFER_TYPE_INTERRUPT && (pep->bEndpointAddress & 0x80) == 0x80)
247  index = epInterruptInIndex;
249  index = ((pep->bEndpointAddress & 0x80) == 0x80) ? epDataInIndex : epDataOutIndex;
250  else
251  return;
252 
253  // Fill in the endpoint info structure
254  epInfo[index].epAddr = (pep->bEndpointAddress & 0x0F);
255  epInfo[index].maxPktSize = (uint8_t)pep->wMaxPacketSize;
256  epInfo[index].bmSndToggle = 0;
257  epInfo[index].bmRcvToggle = 0;
258  // Some device vendors set endpoint lengths to zero, which is incorrect.
259  // Check, and if zero, set to 64.
260  if(epInfo[index].maxPktSize == 0) epInfo[index].maxPktSize = 64;
261 
262  bNumEP++;
263 
264  PrintEndpointDescriptor(pep);
265 }
266 
267 uint8_t FTDI::Release() {
268  pUsb->GetAddressPool().FreeAddress(bAddress);
269 
270  bAddress = 0;
271  bNumEP = 1;
272  qNextPollTime = 0;
273  bPollEnable = false;
274  ready = false;
275  return pAsync->OnRelease(this);
276 }
277 
278 uint8_t FTDI::Poll() {
279  uint8_t rcode = 0;
280 
281  //if (!bPollEnable)
282  // return 0;
283 
284  //if (qNextPollTime <= (uint32_t)millis())
285  //{
286  // USB_HOST_SERIAL.println(bAddress, HEX);
287 
288  // qNextPollTime = (uint32_t)millis() + 100;
289  //}
290  return rcode;
291 }
292 
293 uint8_t FTDI::SetBaudRate(uint32_t baud) {
294  uint16_t baud_value, baud_index = 0;
295  uint32_t divisor3;
296  divisor3 = 48000000 / 2 / baud; // divisor shifted 3 bits to the left
297 
298  if(wFTDIType == FT232AM) {
299  if((divisor3 & 0x7) == 7)
300  divisor3++; // round x.7/8 up to x+1
301 
302  baud_value = divisor3 >> 3;
303  divisor3 &= 0x7;
304 
305  if(divisor3 == 1) baud_value |= 0xc000;
306  else // 0.125
307  if(divisor3 >= 4) baud_value |= 0x4000;
308  else // 0.5
309  if(divisor3 != 0) baud_value |= 0x8000; // 0.25
310  if(baud_value == 1) baud_value = 0; /* special case for maximum baud rate */
311  } else {
312  static const uint8_t divfrac [8] = {0, 3, 2, 0, 1, 1, 2, 3};
313  static const uint8_t divindex[8] = {0, 0, 0, 1, 0, 1, 1, 1};
314 
315  baud_value = divisor3 >> 3;
316  baud_value |= divfrac [divisor3 & 0x7] << 14;
317  baud_index = divindex[divisor3 & 0x7];
318 
319  /* Deal with special cases for highest baud rates. */
320  if(baud_value == 1) baud_value = 0;
321  else // 1.0
322  if(baud_value == 0x4001) baud_value = 1; // 1.5
323  }
324  USBTRACE2("baud_value:", baud_value);
325  USBTRACE2("baud_index:", baud_index);
326  uint8_t rv = pUsb->ctrlReq(bAddress, 0, bmREQ_FTDI_OUT, FTDI_SIO_SET_BAUD_RATE, baud_value & 0xff, baud_value >> 8, baud_index, 0, 0, NULL, NULL);
327  if(rv && rv != hrNAK) {
328  Release();
329  }
330  return rv;
331 }
332 
333 // No docs on if this is 8 or 16 bit, so play it safe, make maximum 255ms
334 
335 uint8_t FTDI::SetLatency(uint8_t l) {
336  uint8_t rv = pUsb->ctrlReq(bAddress, 0, bmREQ_FTDI_OUT, FTDI_SIO_SET_LATENCY_TIMER, l, 0, 0, 0, 0, NULL, NULL);
337  if(rv && rv != hrNAK) {
338  Release();
339  }
340  return rv;
341 }
342 
343 // No docs on if this is 8 or 16 bit, so play it safe, make maximum 255ms
344 
345 uint8_t FTDI::GetLatency(uint8_t *l) {
346  uint8_t rv = pUsb->ctrlReq(bAddress, 0, bmREQ_FTDI_OUT, FTDI_SIO_GET_LATENCY_TIMER, 0, 0, 0, 0, 1, (uint8_t *)l, NULL);
347  if(rv && rv != hrNAK) {
348  Release();
349  }
350  return rv;
351 }
352 
353 uint8_t FTDI::SetModemControl(uint16_t signal) {
354  uint8_t rv = pUsb->ctrlReq(bAddress, 0, bmREQ_FTDI_OUT, FTDI_SIO_MODEM_CTRL, signal & 0xff, signal >> 8, 0, 0, 0, NULL, NULL);
355  if(rv && rv != hrNAK) {
356  Release();
357  }
358  return rv;
359 }
360 
361 uint8_t FTDI::SetFlowControl(uint8_t protocol, uint8_t xon, uint8_t xoff) {
362  uint8_t rv = pUsb->ctrlReq(bAddress, 0, bmREQ_FTDI_OUT, FTDI_SIO_SET_FLOW_CTRL, xon, xoff, protocol << 8, 0, 0, NULL, NULL);
363  if(rv && rv != hrNAK) {
364  Release();
365  }
366  return rv;
367 }
368 
369 uint8_t FTDI::SetData(uint16_t databm) {
370  uint8_t rv = pUsb->ctrlReq(bAddress, 0, bmREQ_FTDI_OUT, FTDI_SIO_SET_DATA, databm & 0xff, databm >> 8, 0, 0, 0, NULL, NULL);
371  if(rv && rv != hrNAK) {
372  Release();
373  }
374  return rv;
375 }
376 
377 uint8_t FTDI::RcvData(uint16_t *bytes_rcvd, uint8_t *dataptr) {
378  uint8_t rv = pUsb->inTransfer(bAddress, epInfo[epDataInIndex].epAddr, bytes_rcvd, dataptr);
379  if(rv && rv != hrNAK) {
380  Release();
381  }
382  return rv;
383 }
384 
385 uint8_t FTDI::SndData(uint16_t nbytes, uint8_t *dataptr) {
386  uint8_t rv = pUsb->outTransfer(bAddress, epInfo[epDataOutIndex].epAddr, nbytes, dataptr);
387  if(rv && rv != hrNAK) {
388  Release();
389  }
390  return rv;
391 }
392 
393 void FTDI::PrintEndpointDescriptor(const USB_ENDPOINT_DESCRIPTOR* ep_ptr) {
394  Notify(PSTR("Endpoint descriptor:"), 0x80);
395  Notify(PSTR("\r\nLength:\t\t"), 0x80);
396  D_PrintHex<uint8_t > (ep_ptr->bLength, 0x80);
397  Notify(PSTR("\r\nType:\t\t"), 0x80);
398  D_PrintHex<uint8_t > (ep_ptr->bDescriptorType, 0x80);
399  Notify(PSTR("\r\nAddress:\t"), 0x80);
400  D_PrintHex<uint8_t > (ep_ptr->bEndpointAddress, 0x80);
401  Notify(PSTR("\r\nAttributes:\t"), 0x80);
402  D_PrintHex<uint8_t > (ep_ptr->bmAttributes, 0x80);
403  Notify(PSTR("\r\nMaxPktSize:\t"), 0x80);
404  D_PrintHex<uint16_t > (ep_ptr->wMaxPacketSize, 0x80);
405  Notify(PSTR("\r\nPoll Intrv:\t"), 0x80);
406  D_PrintHex<uint8_t > (ep_ptr->bInterval, 0x80);
407  Notify(PSTR("\r\n"), 0x80);
408 }
#define USB_ERROR_OUT_OF_ADDRESS_SPACE_IN_POOL
Definition: UsbCore.h:95
#define USB_ERROR_CLASS_INSTANCE_ALREADY_IN_USE
Definition: UsbCore.h:100
#define USB_DEV_CONFIG_ERROR_DEVICE_NOT_SUPPORTED
Definition: UsbCore.h:92
#define USB_ERROR_EPINFO_IS_NULL
Definition: UsbCore.h:98
#define USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL
Definition: UsbCore.h:97
#define USB_NAK_MAX_POWER
Definition: address.h:34
#define USB_NAK_NOWAIT
Definition: address.h:36
#define FTDI_SIO_MODEM_CTRL
Definition: cdcftdi.h:38
#define FT232AM
Definition: cdcftdi.h:31
#define FTDI_SIO_GET_LATENCY_TIMER
Definition: cdcftdi.h:46
#define FTDI_VID
Definition: cdcftdi.h:28
#define bmREQ_FTDI_OUT
Definition: cdcftdi.h:22
#define FTDI_MAX_ENDPOINTS
Definition: cdcftdi.h:96
#define FTDI_SIO_SET_FLOW_CTRL
Definition: cdcftdi.h:39
#define FTDI_SIO_SET_LATENCY_TIMER
Definition: cdcftdi.h:45
#define FTDI_SIO_SET_DATA
Definition: cdcftdi.h:41
#define FTDI_SIO_SET_BAUD_RATE
Definition: cdcftdi.h:40
virtual void FreeAddress(uint8_t addr)=0
virtual uint8_t AllocAddress(uint8_t parent, bool is_hub=false, uint8_t port=0)=0
virtual UsbDevice * GetUsbDevicePtr(uint8_t addr)=0
virtual uint8_t OnInit(FTDI *pftdi)
Definition: cdcftdi.h:84
virtual uint8_t OnRelease(FTDI *pftdi)
Definition: cdcftdi.h:88
uint8_t Init(uint8_t parent, uint8_t port, bool lowspeed)
Definition: cdcftdi.cpp:41
void EndpointXtract(uint8_t conf, uint8_t iface, uint8_t alt, uint8_t proto, const USB_ENDPOINT_DESCRIPTOR *ep)
Definition: cdcftdi.cpp:237
uint8_t Poll()
Definition: cdcftdi.cpp:278
uint8_t GetLatency(uint8_t *l)
Definition: cdcftdi.cpp:345
uint8_t SetFlowControl(uint8_t protocol, uint8_t xon=0x11, uint8_t xoff=0x13)
Definition: cdcftdi.cpp:361
uint8_t SetModemControl(uint16_t control)
Definition: cdcftdi.cpp:353
uint8_t SndData(uint16_t nbytes, uint8_t *dataptr)
Definition: cdcftdi.cpp:385
uint8_t SetLatency(uint8_t l)
Definition: cdcftdi.cpp:335
uint8_t RcvData(uint16_t *bytes_rcvd, uint8_t *dataptr)
Definition: cdcftdi.cpp:377
uint8_t Release()
Definition: cdcftdi.cpp:267
uint8_t SetData(uint16_t databm)
Definition: cdcftdi.cpp:369
uint8_t SetBaudRate(uint32_t baud)
Definition: cdcftdi.cpp:293
FTDI(USB *pusb, FTDIAsyncOper *pasync, uint16_t idProduct=FTDI_PID)
Definition: cdcftdi.cpp:23
Definition: UsbCore.h:212
uint8_t getDevDescr(uint8_t addr, uint8_t ep, uint16_t nbytes, uint8_t *dataptr)
defined(USB_METHODS_INLINE)
Definition: Usb.cpp:801
uint8_t setConf(uint8_t addr, uint8_t ep, uint8_t conf_value)
Definition: Usb.cpp:845
uint8_t setAddr(uint8_t oldaddr, uint8_t ep, uint8_t newaddr)
Definition: Usb.cpp:836
uint8_t RegisterDeviceClass(USBDeviceConfig *pdev)
Definition: UsbCore.h:232
uint8_t getConfDescr(uint8_t addr, uint8_t ep, uint16_t nbytes, uint8_t conf, uint8_t *dataptr)
Definition: Usb.cpp:806
uint8_t ctrlReq(uint8_t addr, uint8_t ep, uint8_t bmReqType, uint8_t bRequest, uint8_t wValLo, uint8_t wValHi, uint16_t wInd, uint16_t total, uint16_t nbytes, uint8_t *dataptr, USBReadParser *p)
Definition: Usb.cpp:126
AddressPool & GetAddressPool()
Definition: UsbCore.h:228
uint8_t setEpInfoEntry(uint8_t addr, uint8_t epcount, EpInfo *eprecord_ptr)
Definition: Usb.cpp:64
uint8_t inTransfer(uint8_t addr, uint8_t ep, uint16_t *nbytesptr, uint8_t *data, uint8_t bInterval=0)
Definition: Usb.cpp:209
uint8_t outTransfer(uint8_t addr, uint8_t ep, uint16_t nbytes, uint8_t *data)
Definition: Usb.cpp:303
#define USBTRACE2(s, r)
Definition: macros.h:84
#define USBTRACE(s)
Definition: macros.h:82
#define hrNAK
Definition: max3421e.h:218
#define NotifyFailSetConfDescr(...)
Definition: message.h:60
#define NotifyFail(...)
Definition: message.h:62
#define Notify(...)
Definition: message.h:51
#define NotifyFailGetConfDescr(...)
Definition: message.h:59
#define NotifyFailSetDevTblEntry(...)
Definition: message.h:58
#define NotifyFailGetDevDescr(...)
Definition: message.h:57
Definition: address.h:39
uint8_t bmNakPower
Definition: address.h:49
uint8_t bmRcvToggle
Definition: address.h:48
uint8_t epAddr
Definition: address.h:40
uint8_t maxPktSize
Definition: address.h:41
uint8_t bmSndToggle
Definition: address.h:47
uint8_t bMaxPacketSize0
Definition: usb_ch9.h:112
uint16_t bcdDevice
Definition: usb_ch9.h:115
uint8_t bNumConfigurations
Definition: usb_ch9.h:119
uint16_t idProduct
Definition: usb_ch9.h:114
uint8_t bEndpointAddress
Definition: usb_ch9.h:151
uint16_t wMaxPacketSize
Definition: usb_ch9.h:153
EpInfo * epinfo
Definition: address.h:83
bool lowspeed
Definition: address.h:86
#define USB_TRANSFER_TYPE_BULK
Definition: usb_ch9.h:92
#define bmUSB_TRANSFER_TYPE
Definition: usb_ch9.h:94
#define USB_TRANSFER_TYPE_INTERRUPT
Definition: usb_ch9.h:93
#define PSTR(str)