MRPT  1.9.9
CInterfaceFTDI_LIN.cpp
Go to the documentation of this file.
1 /* +------------------------------------------------------------------------+
2  | Mobile Robot Programming Toolkit (MRPT) |
3  | https://www.mrpt.org/ |
4  | |
5  | Copyright (c) 2005-2020, Individual contributors, see AUTHORS file |
6  | See: https://www.mrpt.org/Authors - All rights reserved. |
7  | Released under BSD License. See: https://www.mrpt.org/License |
8  +------------------------------------------------------------------------+ */
9 
10 #include <mrpt/config.h>
11 #include <mrpt/core/exceptions.h>
12 
13 #include <cstring>
14 
15 #if defined(MRPT_OS_LINUX) || defined(MRPT_OS_APPLE)
16 
17 #if MRPT_HAS_FTDI
18 #include <ftdi.h>
19 #if MRPT_FTDI_VERSION >= 0x120
20 #include <libusb-1.0/libusb.h>
21 #else
22 #include <usb.h>
23 #endif
24 #endif
25 
27 
28 #include <iostream>
29 
30 using namespace mrpt::comms;
31 using namespace std;
32 
33 /*-------------------------------------------------------------
34  CInterfaceFTDI
35 -------------------------------------------------------------*/
36 CInterfaceFTDI::CInterfaceFTDI() : m_readBuffer(4096)
37 {
39 
40 #if MRPT_HAS_FTDI
41  // Alloc mem:
42  auto* newCtx = new ftdi_context[1];
43  ASSERT_(newCtx);
44 
45  // Init:
46  int ret = ftdi_init(newCtx);
47  if (ret) THROW_EXCEPTION("There was a problem initializing ftdi_context.");
48 
49  // Save in member:
50  m_ftdi_context = static_cast<void*>(newCtx);
51 #else
53  "MRPT has been compiled without FTDI support. Please, reconfigure and "
54  "recompile MRPT.");
55 #endif
56 
57 #if MRPT_FTDI_VERSION >= 0x120
58  libusb_init(nullptr);
59 #endif
61 }
62 
63 /*-------------------------------------------------------------
64  ~CInterfaceFTDI
65 -------------------------------------------------------------*/
67 {
68 #if MRPT_HAS_FTDI
69  // Close USB:
70  if (isOpen()) Close();
71  auto* ctx = static_cast<ftdi_context*>(m_ftdi_context);
72 
73  // Close context:
74  ftdi_deinit(ctx);
75 
76  // Free mem:
77  delete[] ctx;
78  ctx = nullptr;
79 #endif
80 #if MRPT_FTDI_VERSION >= 0x120
81  libusb_exit(nullptr);
82 #endif
83 }
84 
85 void CInterfaceFTDI::OpenBySerialNumber(const std::string& serialNumber)
86 {
87 #if MRPT_HAS_FTDI
89 
91 
92  // Close previous connection:
93  Close();
94 
95  // ftdi_usb_open_desc ...
96 
97  // Create a list of all the devices:
98  TFTDIDeviceList lstDevs;
99  ListAllDevices(lstDevs);
100 
101  // Look for the one we want:
102  void* myDev = nullptr;
103 
104  for (auto& lstDev : lstDevs)
105  {
106  if (lstDev.ftdi_serial == serialNumber)
107  {
108  myDev = lstDev.usb_device_struct;
109  break;
110  }
111  }
112 
113  if (!myDev)
115  "USB device with serial number '%s' not found.",
116  serialNumber.c_str());
117 
118  // Open it:
119  auto* ctx = static_cast<ftdi_context*>(m_ftdi_context);
120 
121  int ret = ftdi_usb_open_dev(
122  ctx,
123 #if MRPT_FTDI_VERSION >= 0x120
124  (struct libusb_device*)myDev
125 #else
126  (struct usb_device*)myDev
127 #endif
128  );
129 
130  if (ret) THROW_EXCEPTION(string(ftdi_get_error_string(ctx)));
131 
133 #else
134  MRPT_UNUSED_PARAM(serialNumber);
135 #endif
136 }
137 
138 /*-------------------------------------------------------------
139  ListAllDevices
140 -------------------------------------------------------------*/
142 {
144 #if MRPT_HAS_FTDI
145 
146  outList.clear();
147 
148 #if MRPT_FTDI_VERSION >= 0x120
149  // For new libftdi1-dev
150  // Use libusb-1.0
151 
152  libusb_device** list;
153  ssize_t nDevices = libusb_get_device_list(nullptr, &list);
154 
155  for (unsigned int i = 0; i < nDevices; i++)
156  {
157  libusb_device* device = list[i];
158  struct libusb_device_descriptor desc;
159  if (0 != libusb_get_device_descriptor(device, &desc)) continue;
160  if (!desc.idVendor) continue;
161 
162  TFTDIDevice newEntry;
163  newEntry.usb_device_struct = (void*)device;
164  newEntry.usb_idProduct = desc.idProduct;
165  newEntry.usb_idVendor = desc.idVendor;
166  newEntry.usb_serialNumber = desc.iSerialNumber;
167 
168  // Open the device temporally so we can get more info:
169  libusb_device_handle* handle;
170  if (0 != libusb_open(device, &handle)) continue;
171 
172  char buf[1024];
173  int ret;
174  // manufacturer
175  ret = libusb_get_string_descriptor_ascii(
176  handle, desc.iManufacturer, (unsigned char*)buf, sizeof(buf) - 1);
177  if (ret < 0) continue;
178  buf[ret] = '\0';
179  newEntry.ftdi_manufacturer = buf;
180 
181  // description
182  ret = libusb_get_string_descriptor_ascii(
183  handle, desc.iProduct, (unsigned char*)buf, sizeof(buf) - 1);
184  if (ret < 0) continue;
185  buf[ret] = '\0';
186  newEntry.ftdi_description = buf;
187 
188  // serial
189  ret = libusb_get_string_descriptor_ascii(
190  handle, desc.iSerialNumber, (unsigned char*)buf, sizeof(buf) - 1);
191  if (ret < 0) continue;
192  buf[ret] = '\0';
193  newEntry.ftdi_serial = buf;
194 
195  outList.push_back(newEntry);
196  }
197 
198 #else
199  // For old libftdi-dev
200  // Use old usb.h interface
201  struct usb_bus* bus;
202  struct usb_device* dev;
203 
204  usb_init();
205  if (usb_find_busses() < 0) THROW_EXCEPTION("usb_find_busses() failed");
206  if (usb_find_devices() < 0) THROW_EXCEPTION("usb_find_devices() failed");
207 
208  for (bus = usb_busses; bus; bus = bus->next)
209  for (dev = bus->devices; dev; dev = dev->next)
210  recursive_fill_list_devices(
211  dev, outList); // Process this node and its children:
212 
213 #endif
214  if (getenv("VERBOSE") != nullptr)
215  {
216  printf("[CInterfaceFTDI::ListAllDevices] List: \n");
217  for (auto i = outList.begin(); i != outList.end(); ++i)
218  printf(
219  "USB DEV: V=%04X P=%04X S=%s\n", i->usb_idVendor,
220  i->usb_idProduct, i->ftdi_serial.c_str());
221  }
222 
223 #else
224  MRPT_UNUSED_PARAM(outList);
225 #endif
227 }
228 
229 void CInterfaceFTDI::recursive_fill_list_devices(
230  void* usb_device_structure, TFTDIDeviceList& outList)
231 {
232 #if MRPT_HAS_FTDI
233 #if MRPT_FTDI_VERSION >= 0x120
234  // For new libftdi1-dev
235  throw std::runtime_error("Should not have got to this function!");
236 #else
237  // For old libftdi-dev
238  struct usb_device* dev = (struct usb_device*)usb_device_structure;
239 
240  if (dev->descriptor.idProduct && dev->descriptor.idVendor)
241  {
242  TFTDIDevice newEntry;
243  newEntry.usb_idProduct = dev->descriptor.idProduct;
244  newEntry.usb_idVendor = dev->descriptor.idVendor;
245  newEntry.usb_device_struct = (void*)dev;
246 
247  int strLen;
248 
249  // Open the device temporally so we can get more info:
250  usb_dev_handle* hUSB = usb_open(dev);
251 
252  if (hUSB)
253  {
254  char manufacturer[3000];
255  if ((strLen = usb_get_string_simple(
256  hUSB, dev->descriptor.iManufacturer, manufacturer,
257  sizeof(manufacturer))) <= 0)
258  {
259  cerr << "Couldn't open " << (int)dev->descriptor.iManufacturer
260  << endl;
261  // usb_close(hUSB); hUSB=nullptr;
262  }
263  else
264  {
265  manufacturer[strLen] = '\0';
266  // cout << "Manuf: " << manufacturer << endl;
267  newEntry.ftdi_manufacturer = manufacturer;
268  }
269  }
270 
271  if (hUSB)
272  {
273  char description[3000];
274  if ((strLen = usb_get_string_simple(
275  hUSB, dev->descriptor.iProduct, description,
276  sizeof(description))) <= 0)
277  {
278  // usb_close(hUSB); hUSB=nullptr;
279  }
280  else
281  {
282  description[strLen] = '\0';
283  newEntry.ftdi_description = description;
284  }
285  }
286 
287  if (hUSB)
288  {
289  char serial[300];
290  if ((strLen = usb_get_string_simple(
291  hUSB, dev->descriptor.iSerialNumber, serial,
292  sizeof(serial))) <= 0)
293  {
294  // usb_close(hUSB); hUSB=nullptr;
295  }
296  else
297  {
298  serial[strLen] = '\0';
299  newEntry.ftdi_serial = serial;
300  }
301  }
302 
303  if (hUSB)
304  {
305  outList.push_back(newEntry);
306  usb_close(hUSB);
307  }
308 
309  // And now its children:
310  // -----------------------------------
311  for (unsigned char j = 0; j < dev->num_children; j++)
312  recursive_fill_list_devices((void*)dev->children[j], outList);
313  }
314 #endif
315 #else
316  MRPT_UNUSED_PARAM(usb_device_structure);
317  MRPT_UNUSED_PARAM(outList);
318 #endif
319 }
320 
321 /*-------------------------------------------------------------
322  ftdi_read
323 -------------------------------------------------------------*/
325  void* lpvBuffer, unsigned long dwBuffSize, unsigned long* lpdwBytesRead)
326 {
327 #if MRPT_HAS_FTDI
329  auto* ctx = static_cast<ftdi_context*>(m_ftdi_context);
330 
331  int ret = ftdi_read_data(ctx, (unsigned char*)lpvBuffer, dwBuffSize);
332  if (ret >= 0)
333  *lpdwBytesRead = ret;
334  else
335  {
336  if (!strcmp("usb bulk read failed", ctx->error_str))
337  {
338  *lpdwBytesRead = 0;
339  return;
340  }
341  THROW_EXCEPTION(string(ftdi_get_error_string(ctx)));
342  }
343 
345 #else
346  MRPT_UNUSED_PARAM(lpvBuffer);
347  MRPT_UNUSED_PARAM(dwBuffSize);
348  MRPT_UNUSED_PARAM(lpdwBytesRead);
349 #endif
350 }
351 
352 /*-------------------------------------------------------------
353  ftdi_write
354 -------------------------------------------------------------*/
356  const void* lpvBuffer, unsigned long dwBuffSize, unsigned long* lpdwBytes)
357 {
358 #if MRPT_HAS_FTDI
360  auto* ctx = static_cast<ftdi_context*>(m_ftdi_context);
361 
362  int ret = ftdi_write_data(ctx, (unsigned char*)lpvBuffer, dwBuffSize);
363  if (ret >= 0)
364  *lpdwBytes = ret;
365  else
366  THROW_EXCEPTION(string(ftdi_get_error_string(ctx)));
367 
369 #else
370  MRPT_UNUSED_PARAM(lpvBuffer);
371  MRPT_UNUSED_PARAM(dwBuffSize);
372  MRPT_UNUSED_PARAM(lpdwBytes);
373 #endif
374 }
375 
376 /*-------------------------------------------------------------
377  isOpen
378 -------------------------------------------------------------*/
380 {
381 #if MRPT_HAS_FTDI
382  auto* ctx = static_cast<ftdi_context*>(m_ftdi_context);
383  return ctx->usb_dev != nullptr;
384 #else
385  return false;
386 #endif
387 }
388 
389 /*-------------------------------------------------------------
390  Close
391 -------------------------------------------------------------*/
393 {
394 #if MRPT_HAS_FTDI
395  auto* ctx = static_cast<ftdi_context*>(m_ftdi_context);
396  if (ctx->usb_dev)
397  {
398  ftdi_usb_close(ctx);
399  ctx->usb_dev = nullptr;
400  }
401 
402  // To assure this is as a "reset", re-init the ftdi context again:
403  ftdi_deinit(ctx);
404  ftdi_init(ctx);
405 
407 #endif
408 }
409 
410 /*-------------------------------------------------------------
411  ResetDevice
412 -------------------------------------------------------------*/
414 {
415 #if MRPT_HAS_FTDI
416  auto* ctx = static_cast<ftdi_context*>(m_ftdi_context);
417  ASSERT_(ctx->usb_dev);
418 
419  if (ftdi_usb_reset(ctx) < 0) THROW_EXCEPTION("Error resetting device");
420 
422 #endif
423 }
424 
425 /*-------------------------------------------------------------
426  Purge
427 -------------------------------------------------------------*/
429 {
430 #if MRPT_HAS_FTDI
431  auto* ctx = static_cast<ftdi_context*>(m_ftdi_context);
432  ASSERT_(ctx->usb_dev);
433 
434  if (ftdi_usb_purge_buffers(ctx) < 0)
435  THROW_EXCEPTION("Error purging device buffers");
436 
438 #endif
439 }
440 
441 /*-------------------------------------------------------------
442  SetLatencyTimer
443 -------------------------------------------------------------*/
444 void CInterfaceFTDI::SetLatencyTimer(unsigned char latency_ms)
445 {
446 #if MRPT_HAS_FTDI
447  auto* ctx = static_cast<ftdi_context*>(m_ftdi_context);
448  ASSERT_(ctx->usb_dev);
449 
450  if (ftdi_set_latency_timer(ctx, latency_ms) < 0)
451  THROW_EXCEPTION("Error setting latency timer");
452 #else
453  MRPT_UNUSED_PARAM(latency_ms);
454 #endif
455 }
456 
457 /*-------------------------------------------------------------
458  SetTimeouts
459 -------------------------------------------------------------*/
461  unsigned long dwReadTimeout_ms, unsigned long dwWriteTimeout_ms)
462 {
463 #if MRPT_HAS_FTDI
464  auto* ctx = static_cast<ftdi_context*>(m_ftdi_context);
465  ASSERT_(ctx->usb_dev);
466 
467 // JL: It seems it works worse with timeouts...
468 // ctx->usb_read_timeout = dwReadTimeout_ms;
469 // ctx->usb_write_timeout = dwWriteTimeout_ms;
470 #else
471  MRPT_UNUSED_PARAM(dwReadTimeout_ms);
472  MRPT_UNUSED_PARAM(dwWriteTimeout_ms);
473 #endif
474 }
475 
476 /*-------------------------------------------------------------
477  OpenByDescription
478 -------------------------------------------------------------*/
479 std::ostream& mrpt::comms::operator<<(std::ostream& o, const TFTDIDevice& d)
480 {
481  o << "Manufacturer : " << d.ftdi_manufacturer << endl
482  << "Description : " << d.ftdi_description << endl
483  << "FTDI serial : " << d.ftdi_serial << endl
484  << "USB ID (Vendor/Product) : "
485  << format("%04X / %04X", d.usb_idVendor, d.usb_idProduct) << endl
486  << "USB serial : " << d.usb_serialNumber << endl;
487 
488  return o;
489 }
490 
491 #endif
std::ostream & operator<<(std::ostream &o, const TFTDIDevice &d)
Print out all the information of a FTDI device in textual form.
virtual int printf(const char *fmt,...) MRPT_printf_format_check(2
Writes a string to the stream in a textual form.
Definition: CStream.cpp:30
bool isOpen()
Checks whether the chip has been successfully open.
#define MRPT_TRY_END
The end of a standard MRPT "try...catch()" block that allows tracing throw the call stack after an ex...
Definition: exceptions.h:213
void ftdi_write(const void *lpvBuffer, unsigned long dwBuffSize, unsigned long *lpdwBytes)
#define THROW_EXCEPTION(msg)
Definition: exceptions.h:67
std::string std::string format(std::string_view fmt, ARGS &&... args)
Definition: format.h:26
std::deque< TFTDIDevice > TFTDIDeviceList
Used in CInterfaceFTDI::ListAllDevices.
mrpt::containers::circular_buffer< uint8_t > m_readBuffer
Used in Read.
STL namespace.
CInterfaceFTDI()
Constructor, which loads driver interface (the DLL under Windows).
#define MRPT_TRY_START
The start of a standard MRPT "try...catch()" block that allows tracing throw the call stack after an ...
Definition: exceptions.h:206
void ListAllDevices(TFTDIDeviceList &outList)
Generates a list with all FTDI devices connected right now.
#define ASSERT_(f)
Defines an assertion mechanism.
Definition: exceptions.h:120
void Close()
Close the USB device.
void SetTimeouts(unsigned long dwReadTimeout_ms, unsigned long dwWriteTimeout_ms)
Change read & write timeouts, in milliseconds.
void OpenBySerialNumber(const std::string &serialNumber)
Open by device serial number.
void ResetDevice()
Reset the USB device.
void ftdi_read(void *lpvBuffer, unsigned long dwBuffSize, unsigned long *lpdwBytesRead)
Serial and networking devices and utilities.
#define THROW_EXCEPTION_FMT(_FORMAT_STRING,...)
Definition: exceptions.h:69
~CInterfaceFTDI() override
Destructor, which closes the connection with the chip and unloads the driver interface.
A list of FTDI devices and their descriptors.
void clear()
Delete all the stored data, if any.
void SetLatencyTimer(unsigned char latency_ms)
Change the latency timer (in milliseconds) implemented on the FTDI chip: for a few ms...
#define MRPT_UNUSED_PARAM(a)
Determines whether this is an X86 or AMD64 platform.
Definition: common.h:186
void Purge()
Purge the I/O buffers.



Page generated by Doxygen 1.8.14 for MRPT 1.9.9 Git: 3a26b90fd Wed Mar 25 20:17:03 2020 +0100 at miƩ mar 25 23:05:41 CET 2020