C++ API: Add support to open devices with missing product / serial strings
[libftdi] / ftdipp / ftdi.cpp
CommitLineData
cdf448f6
TJ
1/***************************************************************************
2 ftdi.cpp - C++ wraper for libftdi
3 -------------------
4 begin : Mon Oct 13 2008
79646368 5 copyright : (C) 2008-2017 by Marek Vavruša / libftdi developers
cdf448f6
TJ
6 email : opensource@intra2net.com and marek@vavrusa.com
7 ***************************************************************************/
3ab8f642 8/*
79646368 9Copyright (C) 2008-2017 by Marek Vavruša / libftdi developers
cdf448f6 10
3ab8f642
TJ
11The software in this package is distributed under the GNU General
12Public License version 2 (with a special exception described below).
13
14A copy of GNU General Public License (GPL) is included in this distribution,
15in the file COPYING.GPL.
16
17As a special exception, if other files instantiate templates or use macros
18or inline functions from this file, or you compile this file and link it
19with other works to produce a work based on this file, this file
20does not by itself cause the resulting work to be covered
21by the GNU General Public License.
22
23However the source code for this file must still be made available
24in accordance with section (3) of the GNU General Public License.
cdf448f6 25
3ab8f642
TJ
26This exception does not invalidate any other reasons why a work based
27on this file might be covered by the GNU General Public License.
28*/
fec55667 29#include <libusb.h>
20b1459a 30#include "ftdi.hpp"
b790d38e 31#include "ftdi_i.h"
20b1459a
TJ
32#include "ftdi.h"
33
34namespace Ftdi
35{
36
37class Context::Private
38{
cdf448f6
TJ
39public:
40 Private()
d5c91348 41 : open(false), ftdi(0), dev(0)
cdf448f6 42 {
cfceadbc
MV
43 ftdi = ftdi_new();
44 }
45
46 ~Private()
47 {
22d12cda 48 if (open)
cfceadbc
MV
49 ftdi_usb_close(ftdi);
50
51 ftdi_free(ftdi);
cdf448f6
TJ
52 }
53
54 bool open;
55
56 struct ftdi_context* ftdi;
579b006f 57 struct libusb_device* dev;
cdf448f6
TJ
58
59 std::string vendor;
60 std::string description;
61 std::string serial;
20b1459a
TJ
62};
63
64/*! \brief Constructor.
65 */
66Context::Context()
cdf448f6 67 : d( new Private() )
20b1459a 68{
20b1459a
TJ
69}
70
71/*! \brief Destructor.
72 */
73Context::~Context()
74{
20b1459a
TJ
75}
76
77bool Context::is_open()
78{
cdf448f6 79 return d->open;
20b1459a
TJ
80}
81
58cce2d4 82int Context::open(int vendor, int product)
20b1459a 83{
2f6b4bb6 84 // Open device
58cce2d4 85 int ret = ftdi_usb_open(d->ftdi, vendor, product);
20b1459a 86
2f6b4bb6
MV
87 if (ret < 0)
88 return ret;
20b1459a 89
5a7f320d 90 return get_strings_and_reopen(false,false,false);
58cce2d4 91}
2f6b4bb6 92
58cce2d4
GE
93int Context::open(int vendor, int product, const std::string& description, const std::string& serial, unsigned int index)
94{
95 // translate empty strings to NULL
96 // -> do not use them to find the device (vs. require an empty string to be set in the EEPROM)
97 const char* c_description=NULL;
98 const char* c_serial=NULL;
99 if (!description.empty())
100 c_description=description.c_str();
101 if (!serial.empty())
102 c_serial=serial.c_str();
20b1459a 103
58cce2d4
GE
104 int ret = ftdi_usb_open_desc_index(d->ftdi, vendor, product, c_description, c_serial, index);
105
106 if (ret < 0)
107 return ret;
108
5a7f320d 109 return get_strings_and_reopen(false,description.empty(),serial.empty());
58cce2d4
GE
110}
111
112int Context::open(const std::string& description)
113{
114 int ret = ftdi_usb_open_string(d->ftdi, description.c_str());
115
116 if (ret < 0)
117 return ret;
118
5a7f320d 119 return get_strings_and_reopen(false,true,false);
20b1459a
TJ
120}
121
579b006f 122int Context::open(struct libusb_device *dev)
20b1459a 123{
cdf448f6
TJ
124 if (dev != 0)
125 d->dev = dev;
126
127 if (d->dev == 0)
128 return -1;
20b1459a 129
58cce2d4 130 return get_strings_and_reopen();
20b1459a
TJ
131}
132
133int Context::close()
134{
cdf448f6 135 d->open = false;
9330b120 136 d->dev = 0;
cdf448f6 137 return ftdi_usb_close(d->ftdi);
20b1459a
TJ
138}
139
140int Context::reset()
141{
cdf448f6 142 return ftdi_usb_reset(d->ftdi);
20b1459a
TJ
143}
144
145int Context::flush(int mask)
146{
cdf448f6 147 int ret = 1;
20b1459a 148
cdf448f6
TJ
149 if (mask & Input)
150 ret &= ftdi_usb_purge_rx_buffer(d->ftdi);
151 if (mask & Output)
152 ret &= ftdi_usb_purge_tx_buffer(d->ftdi);
153
154 return ret;
20b1459a
TJ
155}
156
157int Context::set_interface(enum ftdi_interface interface)
158{
cdf448f6 159 return ftdi_set_interface(d->ftdi, interface);
20b1459a
TJ
160}
161
579b006f 162void Context::set_usb_device(struct libusb_device_handle *dev)
20b1459a 163{
cdf448f6 164 ftdi_set_usbdev(d->ftdi, dev);
579b006f 165 d->dev = libusb_get_device(dev);
20b1459a
TJ
166}
167
168int Context::set_baud_rate(int baudrate)
169{
cdf448f6 170 return ftdi_set_baudrate(d->ftdi, baudrate);
20b1459a
TJ
171}
172
173int Context::set_line_property(enum ftdi_bits_type bits, enum ftdi_stopbits_type sbit, enum ftdi_parity_type parity)
174{
cdf448f6 175 return ftdi_set_line_property(d->ftdi, bits, sbit, parity);
20b1459a
TJ
176}
177
178int Context::set_line_property(enum ftdi_bits_type bits, enum ftdi_stopbits_type sbit, enum ftdi_parity_type parity, enum ftdi_break_type break_type)
179{
cdf448f6 180 return ftdi_set_line_property2(d->ftdi, bits, sbit, parity, break_type);
20b1459a
TJ
181}
182
73a71502
JS
183int Context::get_usb_read_timeout() const
184{
185 return d->ftdi->usb_read_timeout;
186}
187
188void Context::set_usb_read_timeout(int usb_read_timeout)
189{
190 d->ftdi->usb_read_timeout = usb_read_timeout;
191}
192
193int Context::get_usb_write_timeout() const
194{
195 return d->ftdi->usb_write_timeout;
196}
197
198void Context::set_usb_write_timeout(int usb_write_timeout)
199{
200 d->ftdi->usb_write_timeout = usb_write_timeout;
201}
202
20b1459a
TJ
203int Context::read(unsigned char *buf, int size)
204{
cdf448f6 205 return ftdi_read_data(d->ftdi, buf, size);
20b1459a
TJ
206}
207
208int Context::set_read_chunk_size(unsigned int chunksize)
209{
cdf448f6 210 return ftdi_read_data_set_chunksize(d->ftdi, chunksize);
20b1459a
TJ
211}
212
213int Context::read_chunk_size()
214{
cdf448f6
TJ
215 unsigned chunk = -1;
216 if (ftdi_read_data_get_chunksize(d->ftdi, &chunk) < 0)
217 return -1;
20b1459a 218
cdf448f6
TJ
219 return chunk;
220}
20b1459a
TJ
221
222int Context::write(unsigned char *buf, int size)
223{
cdf448f6 224 return ftdi_write_data(d->ftdi, buf, size);
20b1459a
TJ
225}
226
227int Context::set_write_chunk_size(unsigned int chunksize)
228{
cdf448f6 229 return ftdi_write_data_set_chunksize(d->ftdi, chunksize);
20b1459a
TJ
230}
231
232int Context::write_chunk_size()
233{
cdf448f6
TJ
234 unsigned chunk = -1;
235 if (ftdi_write_data_get_chunksize(d->ftdi, &chunk) < 0)
236 return -1;
20b1459a 237
cdf448f6 238 return chunk;
20b1459a
TJ
239}
240
241int Context::set_flow_control(int flowctrl)
242{
cdf448f6 243 return ftdi_setflowctrl(d->ftdi, flowctrl);
20b1459a
TJ
244}
245
246int Context::set_modem_control(int mask)
247{
cdf448f6
TJ
248 int dtr = 0, rts = 0;
249
250 if (mask & Dtr)
251 dtr = 1;
252 if (mask & Rts)
253 rts = 1;
20b1459a 254
cdf448f6 255 return ftdi_setdtr_rts(d->ftdi, dtr, rts);
20b1459a
TJ
256}
257
258int Context::set_dtr(bool state)
259{
cdf448f6 260 return ftdi_setdtr(d->ftdi, state);
20b1459a
TJ
261}
262
263int Context::set_rts(bool state)
264{
cdf448f6 265 return ftdi_setrts(d->ftdi, state);
20b1459a
TJ
266}
267
268int Context::set_latency(unsigned char latency)
269{
cdf448f6 270 return ftdi_set_latency_timer(d->ftdi, latency);
20b1459a
TJ
271}
272
273unsigned Context::latency()
274{
cdf448f6
TJ
275 unsigned char latency = 0;
276 ftdi_get_latency_timer(d->ftdi, &latency);
277 return latency;
20b1459a
TJ
278}
279
280unsigned short Context::poll_modem_status()
281{
cdf448f6
TJ
282 unsigned short status = 0;
283 ftdi_poll_modem_status(d->ftdi, &status);
284 return status;
20b1459a
TJ
285}
286
20b1459a
TJ
287int Context::set_event_char(unsigned char eventch, unsigned char enable)
288{
cdf448f6 289 return ftdi_set_event_char(d->ftdi, eventch, enable);
20b1459a
TJ
290}
291
292int Context::set_error_char(unsigned char errorch, unsigned char enable)
293{
cdf448f6 294 return ftdi_set_error_char(d->ftdi, errorch, enable);
20b1459a
TJ
295}
296
20b1459a
TJ
297int Context::set_bitmode(unsigned char bitmask, unsigned char mode)
298{
c2ed8c4e 299 return ftdi_set_bitmode(d->ftdi, bitmask, mode);
58cce2d4
GE
300}
301
302int Context::set_bitmode(unsigned char bitmask, enum ftdi_mpsse_mode mode)
303{
cdf448f6 304 return ftdi_set_bitmode(d->ftdi, bitmask, mode);
20b1459a
TJ
305}
306
2d790e37
TJ
307int Context::bitbang_disable()
308{
309 return ftdi_disable_bitbang(d->ftdi);
310}
311
20b1459a
TJ
312int Context::read_pins(unsigned char *pins)
313{
cdf448f6 314 return ftdi_read_pins(d->ftdi, pins);
20b1459a
TJ
315}
316
c45d2630 317const char* Context::error_string()
20b1459a 318{
cdf448f6 319 return ftdi_get_error_string(d->ftdi);
20b1459a
TJ
320}
321
5a7f320d 322int Context::get_strings(bool vendor, bool description, bool serial)
20b1459a 323{
cdf448f6 324 // Prepare buffers
5a7f320d 325 char ivendor[512], idesc[512], iserial[512];
cdf448f6 326
5a7f320d 327 int ret = ftdi_usb_get_strings(d->ftdi, d->dev, vendor?ivendor:NULL, 512, description?idesc:NULL, 512, serial?iserial:NULL, 512);
cdf448f6
TJ
328
329 if (ret < 0)
330 return -1;
20b1459a 331
5a7f320d
MJ
332 d->vendor = ivendor;
333 d->description = idesc;
334 d->serial = iserial;
20b1459a 335
cdf448f6 336 return 1;
20b1459a
TJ
337}
338
5a7f320d 339int Context::get_strings_and_reopen(bool vendor, bool description, bool serial)
58cce2d4 340{
5a7f320d 341 int ret = 0;
d3af9b2c 342
5a7f320d 343 if(vendor || description || serial)
58cce2d4 344 {
5a7f320d
MJ
345 if (d->dev == 0)
346 {
347 d->dev = libusb_get_device(d->ftdi->usb_dev);
348 }
349
350 // Get device strings (closes device)
351 ret=get_strings(vendor, description, serial);
352 if (ret < 0)
353 {
354 d->open = 0;
355 return ret;
356 }
357
358 // Reattach device
359 ret = ftdi_usb_open_dev(d->ftdi, d->dev);
360 d->open = (ret >= 0);
58cce2d4
GE
361 }
362
58cce2d4
GE
363 return ret;
364}
365
1bbaf1ce 366/*! \brief Device strings properties.
20b1459a
TJ
367 */
368const std::string& Context::vendor()
369{
5a7f320d
MJ
370 if(d->vendor.empty())
371 get_strings_and_reopen(true,false,false);
cdf448f6 372 return d->vendor;
20b1459a
TJ
373}
374
1bbaf1ce
JP
375/*! \brief Device strings properties.
376 */
20b1459a
TJ
377const std::string& Context::description()
378{
5a7f320d
MJ
379 if(d->description.empty())
380 get_strings_and_reopen(false,true,false);
cdf448f6 381 return d->description;
20b1459a
TJ
382}
383
1bbaf1ce
JP
384/*! \brief Device strings properties.
385 */
20b1459a
TJ
386const std::string& Context::serial()
387{
5a7f320d
MJ
388 if(d->serial.empty())
389 get_strings_and_reopen(false,false,true);
cdf448f6 390 return d->serial;
20b1459a
TJ
391}
392
393void Context::set_context(struct ftdi_context* context)
394{
cdf448f6
TJ
395 ftdi_free(d->ftdi);
396 d->ftdi = context;
20b1459a
TJ
397}
398
579b006f 399void Context::set_usb_device(struct libusb_device *dev)
20b1459a 400{
cdf448f6 401 d->dev = dev;
20b1459a
TJ
402}
403
404struct ftdi_context* Context::context()
405{
cdf448f6 406 return d->ftdi;
20b1459a
TJ
407}
408
409class Eeprom::Private
410{
cdf448f6
TJ
411public:
412 Private()
413 : context(0)
414 {}
20b1459a 415
cdf448f6
TJ
416 struct ftdi_eeprom eeprom;
417 struct ftdi_context* context;
20b1459a
TJ
418};
419
420Eeprom::Eeprom(Context* parent)
cdf448f6 421 : d ( new Private() )
20b1459a 422{
cdf448f6 423 d->context = parent->context();
20b1459a
TJ
424}
425
426Eeprom::~Eeprom()
427{
20b1459a
TJ
428}
429
f14f84d3 430int Eeprom::init_defaults(char* manufacturer, char *product, char * serial)
20b1459a 431{
74e8e79d 432 return ftdi_eeprom_initdefaults(d->context, manufacturer, product, serial);
20b1459a
TJ
433}
434
20b1459a
TJ
435int Eeprom::chip_id(unsigned int *chipid)
436{
cdf448f6 437 return ftdi_read_chipid(d->context, chipid);
20b1459a
TJ
438}
439
440int Eeprom::build(unsigned char *output)
441{
a35aa9bd 442 return ftdi_eeprom_build(d->context);
20b1459a
TJ
443}
444
445int Eeprom::read(unsigned char *eeprom)
446{
a35aa9bd 447 return ftdi_read_eeprom(d->context);
20b1459a
TJ
448}
449
450int Eeprom::write(unsigned char *eeprom)
451{
a35aa9bd 452 return ftdi_write_eeprom(d->context);
20b1459a
TJ
453}
454
449c87a9
TJ
455int Eeprom::read_location(int eeprom_addr, unsigned short *eeprom_val)
456{
457 return ftdi_read_eeprom_location(d->context, eeprom_addr, eeprom_val);
458}
459
460int Eeprom::write_location(int eeprom_addr, unsigned short eeprom_val)
461{
462 return ftdi_write_eeprom_location(d->context, eeprom_addr, eeprom_val);
463}
464
20b1459a
TJ
465int Eeprom::erase()
466{
cdf448f6 467 return ftdi_erase_eeprom(d->context);
20b1459a
TJ
468}
469
470class List::Private
471{
cdf448f6 472public:
6b22a054
MV
473 Private(struct ftdi_device_list* _devlist)
474 : devlist(_devlist)
cdf448f6 475 {}
20b1459a 476
cfceadbc
MV
477 ~Private()
478 {
6b22a054
MV
479 if(devlist)
480 ftdi_list_free(&devlist);
cfceadbc
MV
481 }
482
6b22a054
MV
483 std::list<Context> list;
484 struct ftdi_device_list* devlist;
20b1459a
TJ
485};
486
487List::List(struct ftdi_device_list* devlist)
6b22a054 488 : d( new Private(devlist) )
20b1459a 489{
cdf448f6
TJ
490 if (devlist != 0)
491 {
492 // Iterate list
6b22a054 493 for (; devlist != 0; devlist = devlist->next)
cdf448f6 494 {
cfceadbc 495 Context c;
6b22a054 496 c.set_usb_device(devlist->dev);
cfceadbc 497 c.get_strings();
6b22a054 498 d->list.push_back(c);
cdf448f6 499 }
cdf448f6 500 }
20b1459a
TJ
501}
502
503List::~List()
504{
20b1459a
TJ
505}
506
a14193ac
TJ
507/**
508* Return begin iterator for accessing the contained list elements
509* @return Iterator
510*/
511List::iterator List::begin()
6b22a054 512{
a14193ac 513 return d->list.begin();
6b22a054
MV
514}
515
a14193ac
TJ
516/**
517* Return end iterator for accessing the contained list elements
518* @return Iterator
519*/
520List::iterator List::end()
521{
522 return d->list.end();
523}
524
525/**
526* Return begin iterator for accessing the contained list elements
527* @return Const iterator
528*/
529List::const_iterator List::begin() const
6b22a054 530{
a14193ac 531 return d->list.begin();
6b22a054
MV
532}
533
a14193ac
TJ
534/**
535* Return end iterator for accessing the contained list elements
536* @return Const iterator
537*/
538List::const_iterator List::end() const
539{
540 return d->list.end();
541}
6b22a054 542
a14193ac
TJ
543/**
544* Return begin reverse iterator for accessing the contained list elements
545* @return Reverse iterator
546*/
547List::reverse_iterator List::rbegin()
6b22a054 548{
a14193ac 549 return d->list.rbegin();
6b22a054
MV
550}
551
a14193ac
TJ
552/**
553* Return end reverse iterator for accessing the contained list elements
554* @return Reverse iterator
555*/
556List::reverse_iterator List::rend()
557{
558 return d->list.rend();
559}
560
561/**
562* Return begin reverse iterator for accessing the contained list elements
563* @return Const reverse iterator
564*/
565List::const_reverse_iterator List::rbegin() const
566{
567 return d->list.rbegin();
568}
569
570/**
571* Return end reverse iterator for accessing the contained list elements
572* @return Const reverse iterator
573*/
574List::const_reverse_iterator List::rend() const
575{
576 return d->list.rend();
577
578}
579
580/**
581* Get number of elements stored in the list
582* @return Number of elements
583*/
584List::ListType::size_type List::size() const
585{
586 return d->list.size();
587}
588
589/**
590* Check if list is empty
591* @return True if empty, false otherwise
592*/
593bool List::empty() const
594{
595 return d->list.empty();
596}
597
598/**
599 * Removes all elements. Invalidates all iterators.
600 * Do it in a non-throwing way and also make
601 * sure we really free the allocated memory.
602 */
6b22a054
MV
603void List::clear()
604{
a14193ac 605 ListType().swap(d->list);
6b22a054
MV
606
607 // Free device list
a14193ac
TJ
608 if (d->devlist)
609 {
610 ftdi_list_free(&d->devlist);
611 d->devlist = 0;
612 }
6b22a054
MV
613}
614
a14193ac
TJ
615/**
616 * Appends a copy of the element as the new last element.
617 * @param element Value to copy and append
618*/
619void List::push_back(const Context& element)
6b22a054 620{
a14193ac 621 d->list.push_back(element);
6b22a054
MV
622}
623
a14193ac
TJ
624/**
625 * Adds a copy of the element as the new first element.
626 * @param element Value to copy and add
627*/
628void List::push_front(const Context& element)
6b22a054 629{
a14193ac 630 d->list.push_front(element);
6b22a054
MV
631}
632
a14193ac
TJ
633/**
634 * Erase one element pointed by iterator
635 * @param pos Element to erase
636 * @return Position of the following element (or end())
637*/
638List::iterator List::erase(iterator pos)
639{
640 return d->list.erase(pos);
641}
642
643/**
644 * Erase a range of elements
645 * @param beg Begin of range
646 * @param end End of range
647 * @return Position of the element after the erased range (or end())
648*/
649List::iterator List::erase(iterator beg, iterator end)
650{
651 return d->list.erase(beg, end);
652}
6b22a054 653
7c21beca 654List* List::find_all(Context &context, int vendor, int product)
20b1459a 655{
cdf448f6 656 struct ftdi_device_list* dlist = 0;
7c21beca 657 ftdi_usb_find_all(context.context(), &dlist, vendor, product);
cdf448f6 658 return new List(dlist);
20b1459a
TJ
659}
660
661}