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