MRPT  2.0.3
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 
86  [maybe_unused]] const std::string& serialNumber)
87 {
88 #if MRPT_HAS_FTDI
90 
92 
93  // Close previous connection:
94  Close();
95 
96  // ftdi_usb_open_desc ...
97 
98  // Create a list of all the devices:
99  TFTDIDeviceList lstDevs;
100  ListAllDevices(lstDevs);
101 
102  // Look for the one we want:
103  void* myDev = nullptr;
104 
105  for (auto& lstDev : lstDevs)
106  {
107  if (lstDev.ftdi_serial == serialNumber)
108  {
109  myDev = lstDev.usb_device_struct;
110  break;
111  }
112  }
113 
114  if (!myDev)
116  "USB device with serial number '%s' not found.",
117  serialNumber.c_str());
118 
119  // Open it:
120  auto* ctx = static_cast<ftdi_context*>(m_ftdi_context);
121 
122  int ret = ftdi_usb_open_dev(
123  ctx,
124 #if MRPT_FTDI_VERSION >= 0x120
125  (struct libusb_device*)myDev
126 #else
127  (struct usb_device*)myDev
128 #endif
129  );
130 
131  if (ret) THROW_EXCEPTION(string(ftdi_get_error_string(ctx)));
132 
134 #endif
135 }
136 
137 /*-------------------------------------------------------------
138  ListAllDevices
139 -------------------------------------------------------------*/
140 void CInterfaceFTDI::ListAllDevices([[maybe_unused]] TFTDIDeviceList& outList)
141 {
143 #if MRPT_HAS_FTDI
144 
145  outList.clear();
146 
147 #if MRPT_FTDI_VERSION >= 0x120
148  // For new libftdi1-dev
149  // Use libusb-1.0
150 
151  libusb_device** list;
152  ssize_t nDevices = libusb_get_device_list(nullptr, &list);
153 
154  for (unsigned int i = 0; i < nDevices; i++)
155  {
156  libusb_device* device = list[i];
157  struct libusb_device_descriptor desc;
158  if (0 != libusb_get_device_descriptor(device, &desc)) continue;
159  if (!desc.idVendor) continue;
160 
161  TFTDIDevice newEntry;
162  newEntry.usb_device_struct = (void*)device;
163  newEntry.usb_idProduct = desc.idProduct;
164  newEntry.usb_idVendor = desc.idVendor;
165  newEntry.usb_serialNumber = desc.iSerialNumber;
166 
167  // Open the device temporally so we can get more info:
168  libusb_device_handle* handle;
169  if (0 != libusb_open(device, &handle)) continue;
170 
171  char buf[1024];
172  int ret;
173  // manufacturer
174  ret = libusb_get_string_descriptor_ascii(
175  handle, desc.iManufacturer, (unsigned char*)buf, sizeof(buf) - 1);
176  if (ret < 0) continue;
177  buf[ret] = '\0';
178  newEntry.ftdi_manufacturer = buf;
179 
180  // description
181  ret = libusb_get_string_descriptor_ascii(
182  handle, desc.iProduct, (unsigned char*)buf, sizeof(buf) - 1);
183  if (ret < 0) continue;
184  buf[ret] = '\0';
185  newEntry.ftdi_description = buf;
186 
187  // serial
188  ret = libusb_get_string_descriptor_ascii(
189  handle, desc.iSerialNumber, (unsigned char*)buf, sizeof(buf) - 1);
190  if (ret < 0) continue;
191  buf[ret] = '\0';
192  newEntry.ftdi_serial = buf;
193 
194  outList.push_back(newEntry);
195  }
196 
197 #else
198  // For old libftdi-dev
199  // Use old usb.h interface
200  struct usb_bus* bus;
201  struct usb_device* dev;
202 
203  usb_init();
204  if (usb_find_busses() < 0) THROW_EXCEPTION("usb_find_busses() failed");
205  if (usb_find_devices() < 0) THROW_EXCEPTION("usb_find_devices() failed");
206 
207  for (bus = usb_busses; bus; bus = bus->next)
208  for (dev = bus->devices; dev; dev = dev->next)
209  recursive_fill_list_devices(
210  dev, outList); // Process this node and its children:
211 
212 #endif
213  if (getenv("VERBOSE") != nullptr)
214  {
215  printf("[CInterfaceFTDI::ListAllDevices] List: \n");
216  for (auto i = outList.begin(); i != outList.end(); ++i)
217  printf(
218  "USB DEV: V=%04X P=%04X S=%s\n", i->usb_idVendor,
219  i->usb_idProduct, i->ftdi_serial.c_str());
220  }
221 
222 #endif
224 }
225 
226 void CInterfaceFTDI::recursive_fill_list_devices(
227  [[maybe_unused]] void* usb_device_structure,
228  [[maybe_unused]] TFTDIDeviceList& outList)
229 {
230 #if MRPT_HAS_FTDI
231 #if MRPT_FTDI_VERSION >= 0x120
232  // For new libftdi1-dev
233  throw std::runtime_error("Should not have got to this function!");
234 #else
235  // For old libftdi-dev
236  struct usb_device* dev = (struct usb_device*)usb_device_structure;
237 
238  if (dev->descriptor.idProduct && dev->descriptor.idVendor)
239  {
240  TFTDIDevice newEntry;
241  newEntry.usb_idProduct = dev->descriptor.idProduct;
242  newEntry.usb_idVendor = dev->descriptor.idVendor;
243  newEntry.usb_device_struct = (void*)dev;
244 
245  int strLen;
246 
247  // Open the device temporally so we can get more info:
248  usb_dev_handle* hUSB = usb_open(dev);
249 
250  if (hUSB)
251  {
252  char manufacturer[3000];
253  if ((strLen = usb_get_string_simple(
254  hUSB, dev->descriptor.iManufacturer, manufacturer,
255  sizeof(manufacturer))) <= 0)
256  {
257  cerr << "Couldn't open " << (int)dev->descriptor.iManufacturer
258  << endl;
259  // usb_close(hUSB); hUSB=nullptr;
260  }
261  else
262  {
263  manufacturer[strLen] = '\0';
264  // cout << "Manuf: " << manufacturer << endl;
265  newEntry.ftdi_manufacturer = manufacturer;
266  }
267  }
268 
269  if (hUSB)
270  {
271  char description[3000];
272  if ((strLen = usb_get_string_simple(
273  hUSB, dev->descriptor.iProduct, description,
274  sizeof(description))) <= 0)
275  {
276  // usb_close(hUSB); hUSB=nullptr;
277  }
278  else
279  {
280  description[strLen] = '\0';
281  newEntry.ftdi_description = description;
282  }
283  }
284 
285  if (hUSB)
286  {
287  char serial[300];
288  if ((strLen = usb_get_string_simple(
289  hUSB, dev->descriptor.iSerialNumber, serial,
290  sizeof(serial))) <= 0)
291  {
292  // usb_close(hUSB); hUSB=nullptr;
293  }
294  else
295  {
296  serial[strLen] = '\0';
297  newEntry.ftdi_serial = serial;
298  }
299  }
300 
301  if (hUSB)
302  {
303  outList.push_back(newEntry);
304  usb_close(hUSB);
305  }
306 
307  // And now its children:
308  // -----------------------------------
309  for (unsigned char j = 0; j < dev->num_children; j++)
310  recursive_fill_list_devices((void*)dev->children[j], outList);
311  }
312 #endif
313 #endif
314 }
315 
316 /*-------------------------------------------------------------
317  ftdi_read
318 -------------------------------------------------------------*/
320  [[maybe_unused]] void* lpvBuffer, [[maybe_unused]] unsigned long dwBuffSize,
321  [[maybe_unused]] unsigned long* lpdwBytesRead)
322 {
323 #if MRPT_HAS_FTDI
325  auto* ctx = static_cast<ftdi_context*>(m_ftdi_context);
326 
327  int ret = ftdi_read_data(ctx, (unsigned char*)lpvBuffer, dwBuffSize);
328  if (ret >= 0)
329  *lpdwBytesRead = ret;
330  else
331  {
332  if (!strcmp("usb bulk read failed", ctx->error_str))
333  {
334  *lpdwBytesRead = 0;
335  return;
336  }
337  THROW_EXCEPTION(string(ftdi_get_error_string(ctx)));
338  }
339 
341 #endif
342 }
343 
344 /*-------------------------------------------------------------
345  ftdi_write
346 -------------------------------------------------------------*/
348  [[maybe_unused]] const void* lpvBuffer,
349  [[maybe_unused]] unsigned long dwBuffSize,
350  [[maybe_unused]] unsigned long* lpdwBytes)
351 {
352 #if MRPT_HAS_FTDI
354  auto* ctx = static_cast<ftdi_context*>(m_ftdi_context);
355 
356  int ret = ftdi_write_data(ctx, (unsigned char*)lpvBuffer, dwBuffSize);
357  if (ret >= 0)
358  *lpdwBytes = ret;
359  else
360  THROW_EXCEPTION(string(ftdi_get_error_string(ctx)));
361 
363 #endif
364 }
365 
366 /*-------------------------------------------------------------
367  isOpen
368 -------------------------------------------------------------*/
370 {
371 #if MRPT_HAS_FTDI
372  auto* ctx = static_cast<ftdi_context*>(m_ftdi_context);
373  return ctx->usb_dev != nullptr;
374 #else
375  return false;
376 #endif
377 }
378 
379 /*-------------------------------------------------------------
380  Close
381 -------------------------------------------------------------*/
383 {
384 #if MRPT_HAS_FTDI
385  auto* ctx = static_cast<ftdi_context*>(m_ftdi_context);
386  if (ctx->usb_dev)
387  {
388  ftdi_usb_close(ctx);
389  ctx->usb_dev = nullptr;
390  }
391 
392  // To assure this is as a "reset", re-init the ftdi context again:
393  ftdi_deinit(ctx);
394  ftdi_init(ctx);
395 
397 #endif
398 }
399 
400 /*-------------------------------------------------------------
401  ResetDevice
402 -------------------------------------------------------------*/
404 {
405 #if MRPT_HAS_FTDI
406  auto* ctx = static_cast<ftdi_context*>(m_ftdi_context);
407  ASSERT_(ctx->usb_dev);
408 
409  if (ftdi_usb_reset(ctx) < 0) THROW_EXCEPTION("Error resetting device");
410 
412 #endif
413 }
414 
415 /*-------------------------------------------------------------
416  Purge
417 -------------------------------------------------------------*/
419 {
420 #if MRPT_HAS_FTDI
421  auto* ctx = static_cast<ftdi_context*>(m_ftdi_context);
422  ASSERT_(ctx->usb_dev);
423 
424  if (ftdi_usb_purge_buffers(ctx) < 0)
425  THROW_EXCEPTION("Error purging device buffers");
426 
428 #endif
429 }
430 
431 /*-------------------------------------------------------------
432  SetLatencyTimer
433 -------------------------------------------------------------*/
434 void CInterfaceFTDI::SetLatencyTimer([[maybe_unused]] unsigned char latency_ms)
435 {
436 #if MRPT_HAS_FTDI
437  auto* ctx = static_cast<ftdi_context*>(m_ftdi_context);
438  ASSERT_(ctx->usb_dev);
439 
440  if (ftdi_set_latency_timer(ctx, latency_ms) < 0)
441  THROW_EXCEPTION("Error setting latency timer");
442 #endif
443 }
444 
445 /*-------------------------------------------------------------
446  SetTimeouts
447 -------------------------------------------------------------*/
449  [[maybe_unused]] unsigned long dwReadTimeout_ms,
450  [[maybe_unused]] unsigned long dwWriteTimeout_ms)
451 {
452 #if MRPT_HAS_FTDI
453  auto* ctx = static_cast<ftdi_context*>(m_ftdi_context);
454  ASSERT_(ctx->usb_dev);
455 
456 // JL: It seems it works worse with timeouts...
457 // ctx->usb_read_timeout = dwReadTimeout_ms;
458 // ctx->usb_write_timeout = dwWriteTimeout_ms;
459 #endif
460 }
461 
462 /*-------------------------------------------------------------
463  OpenByDescription
464 -------------------------------------------------------------*/
465 std::ostream& mrpt::comms::operator<<(std::ostream& o, const TFTDIDevice& d)
466 {
467  o << "Manufacturer : " << d.ftdi_manufacturer << endl
468  << "Description : " << d.ftdi_description << endl
469  << "FTDI serial : " << d.ftdi_serial << endl
470  << "USB ID (Vendor/Product) : "
471  << format("%04X / %04X", d.usb_idVendor, d.usb_idProduct) << endl
472  << "USB serial : " << d.usb_serialNumber << endl;
473 
474  return o;
475 }
476 
477 #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...
void Purge()
Purge the I/O buffers.



Page generated by Doxygen 1.8.14 for MRPT 2.0.3 Git: 8e9e8af54 Wed May 13 17:41:24 2020 +0200 at miƩ may 13 17:55:54 CEST 2020