Initial commit

This commit is contained in:
Paul Hollinsky
2018-09-10 20:28:29 -04:00
commit e2e5017331
133 changed files with 24597 additions and 0 deletions
+26
View File
@@ -0,0 +1,26 @@
*.ipch
*.suo
*.user
*.sdf
*.opensdf
*.exe
*.pdb
*.vs
*.VC.db
build/bin/
build/*.log
build/msvc14/*.log
build/msvc14/obj/
build/msvc12/*.log
build/msvc12/obj/
build/msvc11/*.log
build/msvc11/obj/
build/xcode/build/
tests/fuzztests/fuzztests.log
benchmarks/benchmarks.log
tests/CDSChecker/*.o
tests/CDSChecker/*.log
tests/CDSChecker/model-checker/
tests/relacy/freelist.exe
tests/relacy/spmchash.exe
tests/relacy/log.txt
+61
View File
@@ -0,0 +1,61 @@
This license file applies to everything in this repository except that which
is explicitly annotated as being written by other authors, i.e. the Boost
queue (included in the benchmarks for comparison), Intel's TBB library (ditto),
the CDSChecker tool (used for verification), the Relacy model checker (ditto),
and Jeff Preshing's semaphore implementation (used in the blocking queue) which
has a zlib license (embedded in blockingconcurrentqueue.h).
---
Simplified BSD License:
Copyright (c) 2013-2016, Cameron Desrochers.
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
- Redistributions of source code must retain the above copyright notice, this list of
conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright notice, this list of
conditions and the following disclaimer in the documentation and/or other materials
provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
---
I have also chosen to dual-license under the Boost Software License as an alternative to
the Simplified BSD license above:
Boost Software License - Version 1.0 - August 17th, 2003
Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the software and accompanying documentation covered by
this license (the "Software") to use, reproduce, display, distribute,
execute, and transmit the Software, and to prepare derivative works of the
Software, and to permit third-parties to whom the Software is furnished to
do so, all subject to the following:
The copyright notices in the Software and this entire statement, including
the above license grant, this restriction and the following disclaimer,
must be included in all copies of the Software, in whole or in part, and
all derivative works of the Software, unless such copies or derivative
works are solely in the form of machine-executable object code generated by
a source language processor.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
+486
View File
@@ -0,0 +1,486 @@
# moodycamel::ConcurrentQueue<T>
An industrial-strength lock-free queue for C++.
Note: If all you need is a single-producer, single-consumer queue, I have [one of those too][spsc].
## Features
- Knock-your-socks-off [blazing fast performance][benchmarks].
- Single-header implementation. Just drop it in your project.
- Fully thread-safe lock-free queue. Use concurrently from any number of threads.
- C++11 implementation -- elements are moved (instead of copied) where possible.
- Templated, obviating the need to deal exclusively with pointers -- memory is managed for you.
- No artificial limitations on element types or maximum count.
- Memory can be allocated once up-front, or dynamically as needed.
- Fully portable (no assembly; all is done through standard C++11 primitives).
- Supports super-fast bulk operations.
- Includes a low-overhead blocking version (BlockingConcurrentQueue).
- Exception safe.
## Reasons to use
There are not that many full-fledged lock-free queues for C++. Boost has one, but it's limited to objects with trivial
assignment operators and trivial destructors, for example.
Intel's TBB queue isn't lock-free, and requires trivial constructors too.
There're many academic papers that implement lock-free queues in C++, but usable source code is
hard to find, and tests even more so.
This queue not only has less limitations than others (for the most part), but [it's also faster][benchmarks].
It's been fairly well-tested, and offers advanced features like **bulk enqueueing/dequeueing**
(which, with my new design, is much faster than one element at a time, approaching and even surpassing
the speed of a non-concurrent queue even under heavy contention).
In short, there was a lock-free queue shaped hole in the C++ open-source universe, and I set out
to fill it with the fastest, most complete, and well-tested design and implementation I could.
The result is `moodycamel::ConcurrentQueue` :-)
## Reasons *not* to use
The fastest synchronization of all is the kind that never takes place. Fundamentally,
concurrent data structures require some synchronization, and that takes time. Every effort
was made, of course, to minimize the overhead, but if you can avoid sharing data between
threads, do so!
Why use concurrent data structures at all, then? Because they're gosh darn convenient! (And, indeed,
sometimes sharing data concurrently is unavoidable.)
My queue is **not linearizable** (see the next section on high-level design). The foundations of
its design assume that producers are independent; if this is not the case, and your producers
co-ordinate amongst themselves in some fashion, be aware that the elements won't necessarily
come out of the queue in the same order they were put in *relative to the ordering formed by that co-ordination*
(but they will still come out in the order they were put in by any *individual* producer). If this affects
your use case, you may be better off with another implementation; either way, it's an important limitation
to be aware of.
My queue is also **not NUMA aware**, and does a lot of memory re-use internally, meaning it probably doesn't
scale particularly well on NUMA architectures; however, I don't know of any other lock-free queue that *is*
NUMA aware (except for [SALSA][salsa], which is very cool, but has no publicly available implementation that I know of).
Finally, the queue is **not sequentially consistent**; there *is* a happens-before relationship between when an element is put
in the queue and when it comes out, but other things (such as pumping the queue until it's empty) require more thought
to get right in all eventualities, because explicit memory ordering may have to be done to get the desired effect. In other words,
it can sometimes be difficult to use the queue correctly. This is why it's a good idea to follow the [samples][samples.md] where possible.
On the other hand, the upside of this lack of sequential consistency is better performance.
## High-level design
Elements are stored internally using contiguous blocks instead of linked lists for better performance.
The queue is made up of a collection of sub-queues, one for each producer. When a consumer
wants to dequeue an element, it checks all the sub-queues until it finds one that's not empty.
All of this is largely transparent to the user of the queue, however -- it mostly just works<sup>TM</sup>.
One particular consequence of this design, however, (which seems to be non-intuitive) is that if two producers
enqueue at the same time, there is no defined ordering between the elements when they're later dequeued.
Normally this is fine, because even with a fully linearizable queue there'd be a race between the producer
threads and so you couldn't rely on the ordering anyway. However, if for some reason you do extra explicit synchronization
between the two producer threads yourself, thus defining a total order between enqueue operations, you might expect
that the elements would come out in the same total order, which is a guarantee my queue does not offer. At that
point, though, there semantically aren't really two separate producers, but rather one that happens to be spread
across multiple threads. In this case, you can still establish a total ordering with my queue by creating
a single producer token, and using that from both threads to enqueue (taking care to synchronize access to the token,
of course, but there was already extra synchronization involved anyway).
I've written a more detailed [overview of the internal design][blog], as well as [the full
nitty-gritty details of the design][design], on my blog. Finally, the
[source][source] itself is available for perusal for those interested in its implementation.
## Basic use
The entire queue's implementation is contained in **one header**, [`concurrentqueue.h`][concurrentqueue.h].
Simply download and include that to use the queue. The blocking version is in a separate header,
[`blockingconcurrentqueue.h`][blockingconcurrentqueue.h], that depends on the first.
The implementation makes use of certain key C++11 features, so it requires a fairly recent compiler
(e.g. VS2012+ or g++ 4.8; note that g++ 4.6 has a known bug with `std::atomic` and is thus not supported).
The algorithm implementations themselves are platform independent.
Use it like you would any other templated queue, with the exception that you can use
it from many threads at once :-)
Simple example:
#include "concurrentqueue.h"
moodycamel::ConcurrentQueue<int> q;
q.enqueue(25);
int item;
bool found = q.try_dequeue(item);
assert(found && item == 25);
Description of basic methods:
- `ConcurrentQueue(size_t initialSizeEstimate)`
Constructor which optionally accepts an estimate of the number of elements the queue will hold
- `enqueue(T&& item)`
Enqueues one item, allocating extra space if necessary
- `try_enqueue(T&& item)`
Enqueues one item, but only if enough memory is already allocated
- `try_dequeue(T& item)`
Dequeues one item, returning true if an item was found or false if the queue appeared empty
Note that it is up to the user to ensure that the queue object is completely constructed before
being used by any other threads (this includes making the memory effects of construction
visible, possibly via a memory barrier). Similarly, it's important that all threads have
finished using the queue (and the memory effects have fully propagated) before it is
destructed.
There's usually two versions of each method, one "explicit" version that takes a user-allocated per-producer or
per-consumer token, and one "implicit" version that works without tokens. Using the explicit methods is almost
always faster (though not necessarily by a huge factor). Apart from performance, the primary distinction between them
is their sub-queue allocation behaviour for enqueue operations: Using the implicit enqueue methods causes an
automatically-allocated thread-local producer sub-queue to be allocated (it is marked for reuse once the thread exits).
Explicit producers, on the other hand, are tied directly to their tokens' lifetimes (and are also recycled as needed).
Full API (pseudocode):
# Allocates more memory if necessary
enqueue(item) : bool
enqueue(prod_token, item) : bool
enqueue_bulk(item_first, count) : bool
enqueue_bulk(prod_token, item_first, count) : bool
# Fails if not enough memory to enqueue
try_enqueue(item) : bool
try_enqueue(prod_token, item) : bool
try_enqueue_bulk(item_first, count) : bool
try_enqueue_bulk(prod_token, item_first, count) : bool
# Attempts to dequeue from the queue (never allocates)
try_dequeue(item&) : bool
try_dequeue(cons_token, item&) : bool
try_dequeue_bulk(item_first, max) : size_t
try_dequeue_bulk(cons_token, item_first, max) : size_t
# If you happen to know which producer you want to dequeue from
try_dequeue_from_producer(prod_token, item&) : bool
try_dequeue_bulk_from_producer(prod_token, item_first, max) : size_t
# A not-necessarily-accurate count of the total number of elements
size_approx() : size_t
## Blocking version
As mentioned above, a full blocking wrapper of the queue is provided that adds
`wait_dequeue` and `wait_dequeue_bulk` methods in addition to the regular interface.
This wrapper is extremely low-overhead, but slightly less fast than the non-blocking
queue (due to the necessary bookkeeping involving a lightweight semaphore).
There are also timed versions that allow a timeout to be specified (either in microseconds
or with a `std::chrono` object).
The only major caveat with the blocking version is that you must be careful not to
destroy the queue while somebody is waiting on it. This generally means you need to
know for certain that another element is going to come along before you call one of
the blocking methods. (To be fair, the non-blocking version cannot be destroyed while
in use either, but it can be easier to coordinate the cleanup.)
Blocking example:
#include "blockingconcurrentqueue.h"
moodycamel::BlockingConcurrentQueue<int> q;
std::thread producer([&]() {
for (int i = 0; i != 100; ++i) {
std::this_thread::sleep_for(std::chrono::milliseconds(i % 10));
q.enqueue(i);
}
});
std::thread consumer([&]() {
for (int i = 0; i != 100; ++i) {
int item;
q.wait_dequeue(item);
assert(item == i);
if (q.wait_dequeue_timed(item, std::chrono::milliseconds(5))) {
++i;
assert(item == i);
}
}
});
producer.join();
consumer.join();
assert(q.size_approx() == 0);
## Advanced features
#### Tokens
The queue can take advantage of extra per-producer and per-consumer storage if
it's available to speed up its operations. This takes the form of "tokens":
You can create a consumer token and/or a producer token for each thread or task
(tokens themselves are not thread-safe), and use the methods that accept a token
as their first parameter:
moodycamel::ConcurrentQueue<int> q;
moodycamel::ProducerToken ptok(q);
q.enqueue(ptok, 17);
moodycamel::ConsumerToken ctok(q);
int item;
q.try_dequeue(ctok, item);
assert(item == 17);
If you happen to know which producer you want to consume from (e.g. in
a single-producer, multi-consumer scenario), you can use the `try_dequeue_from_producer`
methods, which accept a producer token instead of a consumer token, and cut some overhead.
Note that tokens work with the blocking version of the queue too.
When producing or consuming many elements, the most efficient way is to:
1. Use the bulk methods of the queue with tokens
2. Failing that, use the bulk methods without tokens
3. Failing that, use the single-item methods with tokens
4. Failing that, use the single-item methods without tokens
Having said that, don't create tokens willy-nilly -- ideally there would be
one token (of each kind) per thread. The queue will work with what it is
given, but it performs best when used with tokens.
Note that tokens aren't actually tied to any given thread; it's not technically
required that they be local to the thread, only that they be used by a single
producer/consumer at a time.
#### Bulk operations
Thanks to the [novel design][blog] of the queue, it's just as easy to enqueue/dequeue multiple
items as it is to do one at a time. This means that overhead can be cut drastically for
bulk operations. Example syntax:
moodycamel::ConcurrentQueue<int> q;
int items[] = { 1, 2, 3, 4, 5 };
q.enqueue_bulk(items, 5);
int results[5]; // Could also be any iterator
size_t count = q.try_dequeue_bulk(results, 5);
for (size_t i = 0; i != count; ++i) {
assert(results[i] == items[i]);
}
#### Preallocation (correctly using `try_enqueue`)
`try_enqueue`, unlike just plain `enqueue`, will never allocate memory. If there's not enough room in the
queue, it simply returns false. The key to using this method properly, then, is to ensure enough space is
pre-allocated for your desired maximum element count.
The constructor accepts a count of the number of elements that it should reserve space for. Because the
queue works with blocks of elements, however, and not individual elements themselves, the value to pass
in order to obtain an effective number of pre-allocated element slots is non-obvious.
First, be aware that the count passed is rounded up to the next multiple of the block size. Note that the
default block size is 32 (this can be changed via the traits). Second, once a slot in a block has been
enqueued to, that slot cannot be re-used until the rest of the block has completely been completely filled
up and then completely emptied. This affects the number of blocks you need in order to account for the
overhead of partially-filled blocks. Third, each producer (whether implicit or explicit) claims and recycles
blocks in a different manner, which again affects the number of blocks you need to account for a desired number of
usable slots.
Suppose you want the queue to be able to hold at least `N` elements at any given time. Without delving too
deep into the rather arcane implementation details, here are some simple formulas for the number of elements
to request for pre-allocation in such a case. Note the division is intended to be arithmetic division and not
integer division (in order for `ceil()` to work).
For explicit producers (using tokens to enqueue):
(ceil(N / BLOCK_SIZE) + 1) * MAX_NUM_PRODUCERS * BLOCK_SIZE
For implicit producers (no tokens):
(ceil(N / BLOCK_SIZE) - 1 + 2 * MAX_NUM_PRODUCERS) * BLOCK_SIZE
When using mixed producer types:
((ceil(N / BLOCK_SIZE) - 1) * (MAX_EXPLICIT_PRODUCERS + 1) + 2 * (MAX_IMPLICIT_PRODUCERS + MAX_EXPLICIT_PRODUCERS)) * BLOCK_SIZE
If these formulas seem rather inconvenient, you can use the constructor overload that accepts the minimum
number of elements (`N`) and the maximum number of explicit and implicit producers directly, and let it do the
computation for you.
Finally, it's important to note that because the queue is only eventually consistent and takes advantage of
weak memory ordering for speed, there's always a possibility that under contention `try_enqueue` will fail
even if the queue is correctly pre-sized for the desired number of elements. (e.g. A given thread may think that
the queue's full even when that's no longer the case.) So no matter what, you still need to handle the failure
case (perhaps looping until it succeeds), unless you don't mind dropping elements.
#### Exception safety
The queue is exception safe, and will never become corrupted if used with a type that may throw exceptions.
The queue itself never throws any exceptions (operations fail gracefully (return false) if memory allocation
fails instead of throwing `std::bad_alloc`).
It is important to note that the guarantees of exception safety only hold if the element type never throws
from its destructor, and that any iterators passed into the queue (for bulk operations) never throw either.
Note that in particular this means `std::back_inserter` iterators must be used with care, since the vector
being inserted into may need to allocate and throw a `std::bad_alloc` exception from inside the iterator;
so be sure to reserve enough capacity in the target container first if you do this.
The guarantees are presently as follows:
- Enqueue operations are rolled back completely if an exception is thrown from an element's constructor.
For bulk enqueue operations, this means that elements are copied instead of moved (in order to avoid
having only some of the objects be moved in the event of an exception). Non-bulk enqueues always use
the move constructor if one is available.
- If the assignment operator throws during a dequeue operation (both single and bulk), the element(s) are
considered dequeued regardless. In such a case, the dequeued elements are all properly destructed before
the exception is propagated, but there's no way to get the elements themselves back.
- Any exception that is thrown is propagated up the call stack, at which point the queue is in a consistent
state.
Note: If any of your type's copy constructors/move constructors/assignment operators don't throw, be sure
to annotate them with `noexcept`; this will avoid the exception-checking overhead in the queue where possible
(even with zero-cost exceptions, there's still a code size impact that has to be taken into account).
#### Traits
The queue also supports a traits template argument which defines various types, constants,
and the memory allocation and deallocation functions that are to be used by the queue. The typical pattern
to providing your own traits is to create a class that inherits from the default traits
and override only the values you wish to change. Example:
struct MyTraits : public moodycamel::ConcurrentQueueDefaultTraits
{
static const size_t BLOCK_SIZE = 256; // Use bigger blocks
};
moodycamel::ConcurrentQueue<int, MyTraits> q;
#### How to dequeue types without calling the constructor
The normal way to dequeue an item is to pass in an existing object by reference, which
is then assigned to internally by the queue (using the move-assignment operator if possible).
This can pose a problem for types that are
expensive to construct or don't have a default constructor; fortunately, there is a simple
workaround: Create a wrapper class that copies the memory contents of the object when it
is assigned by the queue (a poor man's move, essentially). Note that this only works if
the object contains no internal pointers. Example:
struct MyObjectMover {
inline void operator=(MyObject&& obj)
{
std::memcpy(data, &obj, sizeof(MyObject));
// TODO: Cleanup obj so that when it's destructed by the queue
// it doesn't corrupt the data of the object we just moved it into
}
inline MyObject& obj() { return *reinterpret_cast<MyObject*>(data); }
private:
align(alignof(MyObject)) char data[sizeof(MyObject)];
};
A less dodgy alternative, if moves are cheap but default construction is not, is to use a
wrapper that defers construction until the object is assigned, enabling use of the move
constructor:
struct MyObjectMover {
inline void operator=(MyObject&& x) {
new (data) MyObject(std::move(x));
created = true;
}
inline MyObject& obj() {
assert(created);
return *reinterpret_cast<MyObject*>(data);
}
~MyObjectMover() {
if (created)
obj().~MyObject();
}
private:
align(alignof(MyObject)) char data[sizeof(MyObject)];
bool created = false;
};
## Samples
There are some more detailed samples [here][samples.md]. The source of
the [unit tests][unittest-src] and [benchmarks][benchmark-src] are available for reference as well.
## Benchmarks
See my blog post for some [benchmark results][benchmarks] (including versus `boost::lockfree::queue` and `tbb::concurrent_queue`),
or run the benchmarks yourself (requires MinGW and certain GnuWin32 utilities to build on Windows, or a recent
g++ on Linux):
cd build
make benchmarks
bin/benchmarks
The short version of the benchmarks is that it's so fast (especially the bulk methods), that if you're actually
using the queue to *do* anything, the queue won't be your bottleneck.
## Tests (and bugs)
I've written quite a few unit tests as well as a randomized long-running fuzz tester. I also ran the
core queue algorithm through the [CDSChecker][cdschecker] C++11 memory model model checker. Some of the
inner algorithms were tested separately using the [Relacy][relacy] model checker, and full integration
tests were also performed with Relacy.
I've tested
on Linux (Fedora 19) and Windows (7), but only on x86 processors so far (Intel and AMD). The code was
written to be platform-independent, however, and should work across all processors and OSes.
Due to the complexity of the implementation and the difficult-to-test nature of lock-free code in general,
there may still be bugs. If anyone is seeing buggy behaviour, I'd like to hear about it! (Especially if
a unit test for it can be cooked up.) Just open an issue on GitHub.
## License
I'm releasing the source of this repository (with the exception of third-party code, i.e. the Boost queue
(used in the benchmarks for comparison), Intel's TBB library (ditto), CDSChecker, Relacy, and Jeff Preshing's
cross-platform semaphore, which all have their own licenses)
under a simplified BSD license. I'm also dual-licensing under the Boost Software License.
See the [LICENSE.md][license] file for more details.
Note that lock-free programming is a patent minefield, and this code may very
well violate a pending patent (I haven't looked), though it does not to my present knowledge.
I did design and implement this queue from scratch.
## Diving into the code
If you're interested in the source code itself, it helps to have a rough idea of how it's laid out. This
section attempts to describe that.
The queue is formed of several basic parts (listed here in roughly the order they appear in the source). There's the
helper functions (e.g. for rounding to a power of 2). There's the default traits of the queue, which contain the
constants and malloc/free functions used by the queue. There's the producer and consumer tokens. Then there's the queue's
public API itself, starting with the constructor, destructor, and swap/assignment methods. There's the public enqueue methods,
which are all wrappers around a small set of private enqueue methods found later on. There's the dequeue methods, which are
defined inline and are relatively straightforward.
Then there's all the main internal data structures. First, there's a lock-free free list, used for recycling spent blocks (elements
are enqueued to blocks internally). Then there's the block structure itself, which has two different ways of tracking whether
it's fully emptied or not (remember, given two parallel consumers, there's no way to know which one will finish first) depending on where it's used.
Then there's a small base class for the two types of internal SPMC producer queues (one for explicit producers that holds onto memory
but attempts to be faster, and one for implicit ones which attempt to recycle more memory back into the parent but is a little slower).
The explicit producer is defined first, then the implicit one. They both contain the same general four methods: One to enqueue, one to
dequeue, one to enqueue in bulk, and one to dequeue in bulk. (Obviously they have constructors and destructors too, and helper methods.)
The main difference between them is how the block handling is done (they both use the same blocks, but in different ways, and map indices
to them in different ways).
Finally, there's the miscellaneous internal methods: There's the ones that handle the initial block pool (populated when the queue is constructed),
and an abstract block pool that comprises the initial pool and any blocks on the free list. There's ones that handle the producer list
(a lock-free add-only linked list of all the producers in the system). There's ones that handle the implicit producer lookup table (which
is really a sort of specialized TLS lookup). And then there's some helper methods for allocating and freeing objects, and the data members
of the queue itself, followed lastly by the free-standing swap functions.
[blog]: http://moodycamel.com/blog/2014/a-fast-general-purpose-lock-free-queue-for-c++
[design]: http://moodycamel.com/blog/2014/detailed-design-of-a-lock-free-queue
[samples.md]: https://github.com/cameron314/concurrentqueue/blob/master/samples.md
[source]: https://github.com/cameron314/concurrentqueue
[concurrentqueue.h]: https://github.com/cameron314/concurrentqueue/blob/master/concurrentqueue.h
[blockingconcurrentqueue.h]: https://github.com/cameron314/concurrentqueue/blob/master/blockingconcurrentqueue.h
[unittest-src]: https://github.com/cameron314/concurrentqueue/tree/master/tests/unittests
[benchmarks]: http://moodycamel.com/blog/2014/a-fast-general-purpose-lock-free-queue-for-c++#benchmarks
[benchmark-src]: https://github.com/cameron314/concurrentqueue/tree/master/benchmarks
[license]: https://github.com/cameron314/concurrentqueue/blob/master/LICENSE.md
[cdschecker]: http://demsky.eecs.uci.edu/c11modelchecker.html
[relacy]: http://www.1024cores.net/home/relacy-race-detector
[spsc]: https://github.com/cameron314/readerwriterqueue
[salsa]: http://webee.technion.ac.il/~idish/ftp/spaa049-gidron.pdf
+981
View File
@@ -0,0 +1,981 @@
// Provides an efficient blocking version of moodycamel::ConcurrentQueue.
// ©2015-2016 Cameron Desrochers. Distributed under the terms of the simplified
// BSD license, available at the top of concurrentqueue.h.
// Uses Jeff Preshing's semaphore implementation (under the terms of its
// separate zlib license, embedded below).
#pragma once
#include "concurrentqueue.h"
#include <type_traits>
#include <cerrno>
#include <memory>
#include <chrono>
#include <ctime>
#if defined(_WIN32)
// Avoid including windows.h in a header; we only need a handful of
// items, so we'll redeclare them here (this is relatively safe since
// the API generally has to remain stable between Windows versions).
// I know this is an ugly hack but it still beats polluting the global
// namespace with thousands of generic names or adding a .cpp for nothing.
extern "C" {
struct _SECURITY_ATTRIBUTES;
__declspec(dllimport) void* __stdcall CreateSemaphoreW(_SECURITY_ATTRIBUTES* lpSemaphoreAttributes, long lInitialCount, long lMaximumCount, const wchar_t* lpName);
__declspec(dllimport) int __stdcall CloseHandle(void* hObject);
__declspec(dllimport) unsigned long __stdcall WaitForSingleObject(void* hHandle, unsigned long dwMilliseconds);
__declspec(dllimport) int __stdcall ReleaseSemaphore(void* hSemaphore, long lReleaseCount, long* lpPreviousCount);
}
#elif defined(__MACH__)
#include <mach/mach.h>
#elif defined(__unix__)
#include <semaphore.h>
#endif
namespace moodycamel
{
namespace details
{
// Code in the mpmc_sema namespace below is an adaptation of Jeff Preshing's
// portable + lightweight semaphore implementations, originally from
// https://github.com/preshing/cpp11-on-multicore/blob/master/common/sema.h
// LICENSE:
// Copyright (c) 2015 Jeff Preshing
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgement in the product documentation would be
// appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.
namespace mpmc_sema
{
#if defined(_WIN32)
class Semaphore
{
private:
void* m_hSema;
Semaphore(const Semaphore& other) MOODYCAMEL_DELETE_FUNCTION;
Semaphore& operator=(const Semaphore& other) MOODYCAMEL_DELETE_FUNCTION;
public:
Semaphore(int initialCount = 0)
{
assert(initialCount >= 0);
const long maxLong = 0x7fffffff;
m_hSema = CreateSemaphoreW(nullptr, initialCount, maxLong, nullptr);
}
~Semaphore()
{
CloseHandle(m_hSema);
}
void wait()
{
const unsigned long infinite = 0xffffffff;
WaitForSingleObject(m_hSema, infinite);
}
bool try_wait()
{
const unsigned long RC_WAIT_TIMEOUT = 0x00000102;
return WaitForSingleObject(m_hSema, 0) != RC_WAIT_TIMEOUT;
}
bool timed_wait(std::uint64_t usecs)
{
const unsigned long RC_WAIT_TIMEOUT = 0x00000102;
return WaitForSingleObject(m_hSema, (unsigned long)(usecs / 1000)) != RC_WAIT_TIMEOUT;
}
void signal(int count = 1)
{
ReleaseSemaphore(m_hSema, count, nullptr);
}
};
#elif defined(__MACH__)
//---------------------------------------------------------
// Semaphore (Apple iOS and OSX)
// Can't use POSIX semaphores due to http://lists.apple.com/archives/darwin-kernel/2009/Apr/msg00010.html
//---------------------------------------------------------
class Semaphore
{
private:
semaphore_t m_sema;
Semaphore(const Semaphore& other) MOODYCAMEL_DELETE_FUNCTION;
Semaphore& operator=(const Semaphore& other) MOODYCAMEL_DELETE_FUNCTION;
public:
Semaphore(int initialCount = 0)
{
assert(initialCount >= 0);
semaphore_create(mach_task_self(), &m_sema, SYNC_POLICY_FIFO, initialCount);
}
~Semaphore()
{
semaphore_destroy(mach_task_self(), m_sema);
}
void wait()
{
semaphore_wait(m_sema);
}
bool try_wait()
{
return timed_wait(0);
}
bool timed_wait(std::uint64_t timeout_usecs)
{
mach_timespec_t ts;
ts.tv_sec = static_cast<unsigned int>(timeout_usecs / 1000000);
ts.tv_nsec = (timeout_usecs % 1000000) * 1000;
// added in OSX 10.10: https://developer.apple.com/library/prerelease/mac/documentation/General/Reference/APIDiffsMacOSX10_10SeedDiff/modules/Darwin.html
kern_return_t rc = semaphore_timedwait(m_sema, ts);
return rc != KERN_OPERATION_TIMED_OUT && rc != KERN_ABORTED;
}
void signal()
{
semaphore_signal(m_sema);
}
void signal(int count)
{
while (count-- > 0)
{
semaphore_signal(m_sema);
}
}
};
#elif defined(__unix__)
//---------------------------------------------------------
// Semaphore (POSIX, Linux)
//---------------------------------------------------------
class Semaphore
{
private:
sem_t m_sema;
Semaphore(const Semaphore& other) MOODYCAMEL_DELETE_FUNCTION;
Semaphore& operator=(const Semaphore& other) MOODYCAMEL_DELETE_FUNCTION;
public:
Semaphore(int initialCount = 0)
{
assert(initialCount >= 0);
sem_init(&m_sema, 0, initialCount);
}
~Semaphore()
{
sem_destroy(&m_sema);
}
void wait()
{
// http://stackoverflow.com/questions/2013181/gdb-causes-sem-wait-to-fail-with-eintr-error
int rc;
do {
rc = sem_wait(&m_sema);
} while (rc == -1 && errno == EINTR);
}
bool try_wait()
{
int rc;
do {
rc = sem_trywait(&m_sema);
} while (rc == -1 && errno == EINTR);
return !(rc == -1 && errno == EAGAIN);
}
bool timed_wait(std::uint64_t usecs)
{
struct timespec ts;
const int usecs_in_1_sec = 1000000;
const int nsecs_in_1_sec = 1000000000;
clock_gettime(CLOCK_REALTIME, &ts);
ts.tv_sec += usecs / usecs_in_1_sec;
ts.tv_nsec += (usecs % usecs_in_1_sec) * 1000;
// sem_timedwait bombs if you have more than 1e9 in tv_nsec
// so we have to clean things up before passing it in
if (ts.tv_nsec >= nsecs_in_1_sec) {
ts.tv_nsec -= nsecs_in_1_sec;
++ts.tv_sec;
}
int rc;
do {
rc = sem_timedwait(&m_sema, &ts);
} while (rc == -1 && errno == EINTR);
return !(rc == -1 && errno == ETIMEDOUT);
}
void signal()
{
sem_post(&m_sema);
}
void signal(int count)
{
while (count-- > 0)
{
sem_post(&m_sema);
}
}
};
#else
#error Unsupported platform! (No semaphore wrapper available)
#endif
//---------------------------------------------------------
// LightweightSemaphore
//---------------------------------------------------------
class LightweightSemaphore
{
public:
typedef std::make_signed<std::size_t>::type ssize_t;
private:
std::atomic<ssize_t> m_count;
Semaphore m_sema;
bool waitWithPartialSpinning(std::int64_t timeout_usecs = -1)
{
ssize_t oldCount;
// Is there a better way to set the initial spin count?
// If we lower it to 1000, testBenaphore becomes 15x slower on my Core i7-5930K Windows PC,
// as threads start hitting the kernel semaphore.
int spin = 10000;
while (--spin >= 0)
{
oldCount = m_count.load(std::memory_order_relaxed);
if ((oldCount > 0) && m_count.compare_exchange_strong(oldCount, oldCount - 1, std::memory_order_acquire, std::memory_order_relaxed))
return true;
std::atomic_signal_fence(std::memory_order_acquire); // Prevent the compiler from collapsing the loop.
}
oldCount = m_count.fetch_sub(1, std::memory_order_acquire);
if (oldCount > 0)
return true;
if (timeout_usecs < 0)
{
m_sema.wait();
return true;
}
if (m_sema.timed_wait((std::uint64_t)timeout_usecs))
return true;
// At this point, we've timed out waiting for the semaphore, but the
// count is still decremented indicating we may still be waiting on
// it. So we have to re-adjust the count, but only if the semaphore
// wasn't signaled enough times for us too since then. If it was, we
// need to release the semaphore too.
while (true)
{
oldCount = m_count.load(std::memory_order_acquire);
if (oldCount >= 0 && m_sema.try_wait())
return true;
if (oldCount < 0 && m_count.compare_exchange_strong(oldCount, oldCount + 1, std::memory_order_relaxed, std::memory_order_relaxed))
return false;
}
}
ssize_t waitManyWithPartialSpinning(ssize_t max, std::int64_t timeout_usecs = -1)
{
assert(max > 0);
ssize_t oldCount;
int spin = 10000;
while (--spin >= 0)
{
oldCount = m_count.load(std::memory_order_relaxed);
if (oldCount > 0)
{
ssize_t newCount = oldCount > max ? oldCount - max : 0;
if (m_count.compare_exchange_strong(oldCount, newCount, std::memory_order_acquire, std::memory_order_relaxed))
return oldCount - newCount;
}
std::atomic_signal_fence(std::memory_order_acquire);
}
oldCount = m_count.fetch_sub(1, std::memory_order_acquire);
if (oldCount <= 0)
{
if (timeout_usecs < 0)
m_sema.wait();
else if (!m_sema.timed_wait((std::uint64_t)timeout_usecs))
{
while (true)
{
oldCount = m_count.load(std::memory_order_acquire);
if (oldCount >= 0 && m_sema.try_wait())
break;
if (oldCount < 0 && m_count.compare_exchange_strong(oldCount, oldCount + 1, std::memory_order_relaxed, std::memory_order_relaxed))
return 0;
}
}
}
if (max > 1)
return 1 + tryWaitMany(max - 1);
return 1;
}
public:
LightweightSemaphore(ssize_t initialCount = 0) : m_count(initialCount)
{
assert(initialCount >= 0);
}
bool tryWait()
{
ssize_t oldCount = m_count.load(std::memory_order_relaxed);
while (oldCount > 0)
{
if (m_count.compare_exchange_weak(oldCount, oldCount - 1, std::memory_order_acquire, std::memory_order_relaxed))
return true;
}
return false;
}
void wait()
{
if (!tryWait())
waitWithPartialSpinning();
}
bool wait(std::int64_t timeout_usecs)
{
return tryWait() || waitWithPartialSpinning(timeout_usecs);
}
// Acquires between 0 and (greedily) max, inclusive
ssize_t tryWaitMany(ssize_t max)
{
assert(max >= 0);
ssize_t oldCount = m_count.load(std::memory_order_relaxed);
while (oldCount > 0)
{
ssize_t newCount = oldCount > max ? oldCount - max : 0;
if (m_count.compare_exchange_weak(oldCount, newCount, std::memory_order_acquire, std::memory_order_relaxed))
return oldCount - newCount;
}
return 0;
}
// Acquires at least one, and (greedily) at most max
ssize_t waitMany(ssize_t max, std::int64_t timeout_usecs)
{
assert(max >= 0);
ssize_t result = tryWaitMany(max);
if (result == 0 && max > 0)
result = waitManyWithPartialSpinning(max, timeout_usecs);
return result;
}
ssize_t waitMany(ssize_t max)
{
ssize_t result = waitMany(max, -1);
assert(result > 0);
return result;
}
void signal(ssize_t count = 1)
{
assert(count >= 0);
ssize_t oldCount = m_count.fetch_add(count, std::memory_order_release);
ssize_t toRelease = -oldCount < count ? -oldCount : count;
if (toRelease > 0)
{
m_sema.signal((int)toRelease);
}
}
ssize_t availableApprox() const
{
ssize_t count = m_count.load(std::memory_order_relaxed);
return count > 0 ? count : 0;
}
};
} // end namespace mpmc_sema
} // end namespace details
// This is a blocking version of the queue. It has an almost identical interface to
// the normal non-blocking version, with the addition of various wait_dequeue() methods
// and the removal of producer-specific dequeue methods.
template<typename T, typename Traits = ConcurrentQueueDefaultTraits>
class BlockingConcurrentQueue
{
private:
typedef ::moodycamel::ConcurrentQueue<T, Traits> ConcurrentQueue;
typedef details::mpmc_sema::LightweightSemaphore LightweightSemaphore;
public:
typedef typename ConcurrentQueue::producer_token_t producer_token_t;
typedef typename ConcurrentQueue::consumer_token_t consumer_token_t;
typedef typename ConcurrentQueue::index_t index_t;
typedef typename ConcurrentQueue::size_t size_t;
typedef typename std::make_signed<size_t>::type ssize_t;
static const size_t BLOCK_SIZE = ConcurrentQueue::BLOCK_SIZE;
static const size_t EXPLICIT_BLOCK_EMPTY_COUNTER_THRESHOLD = ConcurrentQueue::EXPLICIT_BLOCK_EMPTY_COUNTER_THRESHOLD;
static const size_t EXPLICIT_INITIAL_INDEX_SIZE = ConcurrentQueue::EXPLICIT_INITIAL_INDEX_SIZE;
static const size_t IMPLICIT_INITIAL_INDEX_SIZE = ConcurrentQueue::IMPLICIT_INITIAL_INDEX_SIZE;
static const size_t INITIAL_IMPLICIT_PRODUCER_HASH_SIZE = ConcurrentQueue::INITIAL_IMPLICIT_PRODUCER_HASH_SIZE;
static const std::uint32_t EXPLICIT_CONSUMER_CONSUMPTION_QUOTA_BEFORE_ROTATE = ConcurrentQueue::EXPLICIT_CONSUMER_CONSUMPTION_QUOTA_BEFORE_ROTATE;
static const size_t MAX_SUBQUEUE_SIZE = ConcurrentQueue::MAX_SUBQUEUE_SIZE;
public:
// Creates a queue with at least `capacity` element slots; note that the
// actual number of elements that can be inserted without additional memory
// allocation depends on the number of producers and the block size (e.g. if
// the block size is equal to `capacity`, only a single block will be allocated
// up-front, which means only a single producer will be able to enqueue elements
// without an extra allocation -- blocks aren't shared between producers).
// This method is not thread safe -- it is up to the user to ensure that the
// queue is fully constructed before it starts being used by other threads (this
// includes making the memory effects of construction visible, possibly with a
// memory barrier).
explicit BlockingConcurrentQueue(size_t capacity = 6 * BLOCK_SIZE)
: inner(capacity), sema(create<LightweightSemaphore>(), &BlockingConcurrentQueue::template destroy<LightweightSemaphore>)
{
assert(reinterpret_cast<ConcurrentQueue*>((BlockingConcurrentQueue*)1) == &((BlockingConcurrentQueue*)1)->inner && "BlockingConcurrentQueue must have ConcurrentQueue as its first member");
if (!sema) {
MOODYCAMEL_THROW(std::bad_alloc());
}
}
BlockingConcurrentQueue(size_t minCapacity, size_t maxExplicitProducers, size_t maxImplicitProducers)
: inner(minCapacity, maxExplicitProducers, maxImplicitProducers), sema(create<LightweightSemaphore>(), &BlockingConcurrentQueue::template destroy<LightweightSemaphore>)
{
assert(reinterpret_cast<ConcurrentQueue*>((BlockingConcurrentQueue*)1) == &((BlockingConcurrentQueue*)1)->inner && "BlockingConcurrentQueue must have ConcurrentQueue as its first member");
if (!sema) {
MOODYCAMEL_THROW(std::bad_alloc());
}
}
// Disable copying and copy assignment
BlockingConcurrentQueue(BlockingConcurrentQueue const&) MOODYCAMEL_DELETE_FUNCTION;
BlockingConcurrentQueue& operator=(BlockingConcurrentQueue const&) MOODYCAMEL_DELETE_FUNCTION;
// Moving is supported, but note that it is *not* a thread-safe operation.
// Nobody can use the queue while it's being moved, and the memory effects
// of that move must be propagated to other threads before they can use it.
// Note: When a queue is moved, its tokens are still valid but can only be
// used with the destination queue (i.e. semantically they are moved along
// with the queue itself).
BlockingConcurrentQueue(BlockingConcurrentQueue&& other) MOODYCAMEL_NOEXCEPT
: inner(std::move(other.inner)), sema(std::move(other.sema))
{ }
inline BlockingConcurrentQueue& operator=(BlockingConcurrentQueue&& other) MOODYCAMEL_NOEXCEPT
{
return swap_internal(other);
}
// Swaps this queue's state with the other's. Not thread-safe.
// Swapping two queues does not invalidate their tokens, however
// the tokens that were created for one queue must be used with
// only the swapped queue (i.e. the tokens are tied to the
// queue's movable state, not the object itself).
inline void swap(BlockingConcurrentQueue& other) MOODYCAMEL_NOEXCEPT
{
swap_internal(other);
}
private:
BlockingConcurrentQueue& swap_internal(BlockingConcurrentQueue& other)
{
if (this == &other) {
return *this;
}
inner.swap(other.inner);
sema.swap(other.sema);
return *this;
}
public:
// Enqueues a single item (by copying it).
// Allocates memory if required. Only fails if memory allocation fails (or implicit
// production is disabled because Traits::INITIAL_IMPLICIT_PRODUCER_HASH_SIZE is 0,
// or Traits::MAX_SUBQUEUE_SIZE has been defined and would be surpassed).
// Thread-safe.
inline bool enqueue(T const& item)
{
if ((details::likely)(inner.enqueue(item))) {
sema->signal();
return true;
}
return false;
}
// Enqueues a single item (by moving it, if possible).
// Allocates memory if required. Only fails if memory allocation fails (or implicit
// production is disabled because Traits::INITIAL_IMPLICIT_PRODUCER_HASH_SIZE is 0,
// or Traits::MAX_SUBQUEUE_SIZE has been defined and would be surpassed).
// Thread-safe.
inline bool enqueue(T&& item)
{
if ((details::likely)(inner.enqueue(std::move(item)))) {
sema->signal();
return true;
}
return false;
}
// Enqueues a single item (by copying it) using an explicit producer token.
// Allocates memory if required. Only fails if memory allocation fails (or
// Traits::MAX_SUBQUEUE_SIZE has been defined and would be surpassed).
// Thread-safe.
inline bool enqueue(producer_token_t const& token, T const& item)
{
if ((details::likely)(inner.enqueue(token, item))) {
sema->signal();
return true;
}
return false;
}
// Enqueues a single item (by moving it, if possible) using an explicit producer token.
// Allocates memory if required. Only fails if memory allocation fails (or
// Traits::MAX_SUBQUEUE_SIZE has been defined and would be surpassed).
// Thread-safe.
inline bool enqueue(producer_token_t const& token, T&& item)
{
if ((details::likely)(inner.enqueue(token, std::move(item)))) {
sema->signal();
return true;
}
return false;
}
// Enqueues several items.
// Allocates memory if required. Only fails if memory allocation fails (or
// implicit production is disabled because Traits::INITIAL_IMPLICIT_PRODUCER_HASH_SIZE
// is 0, or Traits::MAX_SUBQUEUE_SIZE has been defined and would be surpassed).
// Note: Use std::make_move_iterator if the elements should be moved instead of copied.
// Thread-safe.
template<typename It>
inline bool enqueue_bulk(It itemFirst, size_t count)
{
if ((details::likely)(inner.enqueue_bulk(std::forward<It>(itemFirst), count))) {
sema->signal((LightweightSemaphore::ssize_t)(ssize_t)count);
return true;
}
return false;
}
// Enqueues several items using an explicit producer token.
// Allocates memory if required. Only fails if memory allocation fails
// (or Traits::MAX_SUBQUEUE_SIZE has been defined and would be surpassed).
// Note: Use std::make_move_iterator if the elements should be moved
// instead of copied.
// Thread-safe.
template<typename It>
inline bool enqueue_bulk(producer_token_t const& token, It itemFirst, size_t count)
{
if ((details::likely)(inner.enqueue_bulk(token, std::forward<It>(itemFirst), count))) {
sema->signal((LightweightSemaphore::ssize_t)(ssize_t)count);
return true;
}
return false;
}
// Enqueues a single item (by copying it).
// Does not allocate memory. Fails if not enough room to enqueue (or implicit
// production is disabled because Traits::INITIAL_IMPLICIT_PRODUCER_HASH_SIZE
// is 0).
// Thread-safe.
inline bool try_enqueue(T const& item)
{
if (inner.try_enqueue(item)) {
sema->signal();
return true;
}
return false;
}
// Enqueues a single item (by moving it, if possible).
// Does not allocate memory (except for one-time implicit producer).
// Fails if not enough room to enqueue (or implicit production is
// disabled because Traits::INITIAL_IMPLICIT_PRODUCER_HASH_SIZE is 0).
// Thread-safe.
inline bool try_enqueue(T&& item)
{
if (inner.try_enqueue(std::move(item))) {
sema->signal();
return true;
}
return false;
}
// Enqueues a single item (by copying it) using an explicit producer token.
// Does not allocate memory. Fails if not enough room to enqueue.
// Thread-safe.
inline bool try_enqueue(producer_token_t const& token, T const& item)
{
if (inner.try_enqueue(token, item)) {
sema->signal();
return true;
}
return false;
}
// Enqueues a single item (by moving it, if possible) using an explicit producer token.
// Does not allocate memory. Fails if not enough room to enqueue.
// Thread-safe.
inline bool try_enqueue(producer_token_t const& token, T&& item)
{
if (inner.try_enqueue(token, std::move(item))) {
sema->signal();
return true;
}
return false;
}
// Enqueues several items.
// Does not allocate memory (except for one-time implicit producer).
// Fails if not enough room to enqueue (or implicit production is
// disabled because Traits::INITIAL_IMPLICIT_PRODUCER_HASH_SIZE is 0).
// Note: Use std::make_move_iterator if the elements should be moved
// instead of copied.
// Thread-safe.
template<typename It>
inline bool try_enqueue_bulk(It itemFirst, size_t count)
{
if (inner.try_enqueue_bulk(std::forward<It>(itemFirst), count)) {
sema->signal((LightweightSemaphore::ssize_t)(ssize_t)count);
return true;
}
return false;
}
// Enqueues several items using an explicit producer token.
// Does not allocate memory. Fails if not enough room to enqueue.
// Note: Use std::make_move_iterator if the elements should be moved
// instead of copied.
// Thread-safe.
template<typename It>
inline bool try_enqueue_bulk(producer_token_t const& token, It itemFirst, size_t count)
{
if (inner.try_enqueue_bulk(token, std::forward<It>(itemFirst), count)) {
sema->signal((LightweightSemaphore::ssize_t)(ssize_t)count);
return true;
}
return false;
}
// Attempts to dequeue from the queue.
// Returns false if all producer streams appeared empty at the time they
// were checked (so, the queue is likely but not guaranteed to be empty).
// Never allocates. Thread-safe.
template<typename U>
inline bool try_dequeue(U& item)
{
if (sema->tryWait()) {
while (!inner.try_dequeue(item)) {
continue;
}
return true;
}
return false;
}
// Attempts to dequeue from the queue using an explicit consumer token.
// Returns false if all producer streams appeared empty at the time they
// were checked (so, the queue is likely but not guaranteed to be empty).
// Never allocates. Thread-safe.
template<typename U>
inline bool try_dequeue(consumer_token_t& token, U& item)
{
if (sema->tryWait()) {
while (!inner.try_dequeue(token, item)) {
continue;
}
return true;
}
return false;
}
// Attempts to dequeue several elements from the queue.
// Returns the number of items actually dequeued.
// Returns 0 if all producer streams appeared empty at the time they
// were checked (so, the queue is likely but not guaranteed to be empty).
// Never allocates. Thread-safe.
template<typename It>
inline size_t try_dequeue_bulk(It itemFirst, size_t max)
{
size_t count = 0;
max = (size_t)sema->tryWaitMany((LightweightSemaphore::ssize_t)(ssize_t)max);
while (count != max) {
count += inner.template try_dequeue_bulk<It&>(itemFirst, max - count);
}
return count;
}
// Attempts to dequeue several elements from the queue using an explicit consumer token.
// Returns the number of items actually dequeued.
// Returns 0 if all producer streams appeared empty at the time they
// were checked (so, the queue is likely but not guaranteed to be empty).
// Never allocates. Thread-safe.
template<typename It>
inline size_t try_dequeue_bulk(consumer_token_t& token, It itemFirst, size_t max)
{
size_t count = 0;
max = (size_t)sema->tryWaitMany((LightweightSemaphore::ssize_t)(ssize_t)max);
while (count != max) {
count += inner.template try_dequeue_bulk<It&>(token, itemFirst, max - count);
}
return count;
}
// Blocks the current thread until there's something to dequeue, then
// dequeues it.
// Never allocates. Thread-safe.
template<typename U>
inline void wait_dequeue(U& item)
{
sema->wait();
while (!inner.try_dequeue(item)) {
continue;
}
}
// Blocks the current thread until either there's something to dequeue
// or the timeout (specified in microseconds) expires. Returns false
// without setting `item` if the timeout expires, otherwise assigns
// to `item` and returns true.
// Using a negative timeout indicates an indefinite timeout,
// and is thus functionally equivalent to calling wait_dequeue.
// Never allocates. Thread-safe.
template<typename U>
inline bool wait_dequeue_timed(U& item, std::int64_t timeout_usecs)
{
if (!sema->wait(timeout_usecs)) {
return false;
}
while (!inner.try_dequeue(item)) {
continue;
}
return true;
}
// Blocks the current thread until either there's something to dequeue
// or the timeout expires. Returns false without setting `item` if the
// timeout expires, otherwise assigns to `item` and returns true.
// Never allocates. Thread-safe.
template<typename U, typename Rep, typename Period>
inline bool wait_dequeue_timed(U& item, std::chrono::duration<Rep, Period> const& timeout)
{
return wait_dequeue_timed(item, std::chrono::duration_cast<std::chrono::microseconds>(timeout).count());
}
// Blocks the current thread until there's something to dequeue, then
// dequeues it using an explicit consumer token.
// Never allocates. Thread-safe.
template<typename U>
inline void wait_dequeue(consumer_token_t& token, U& item)
{
sema->wait();
while (!inner.try_dequeue(token, item)) {
continue;
}
}
// Blocks the current thread until either there's something to dequeue
// or the timeout (specified in microseconds) expires. Returns false
// without setting `item` if the timeout expires, otherwise assigns
// to `item` and returns true.
// Using a negative timeout indicates an indefinite timeout,
// and is thus functionally equivalent to calling wait_dequeue.
// Never allocates. Thread-safe.
template<typename U>
inline bool wait_dequeue_timed(consumer_token_t& token, U& item, std::int64_t timeout_usecs)
{
if (!sema->wait(timeout_usecs)) {
return false;
}
while (!inner.try_dequeue(token, item)) {
continue;
}
return true;
}
// Blocks the current thread until either there's something to dequeue
// or the timeout expires. Returns false without setting `item` if the
// timeout expires, otherwise assigns to `item` and returns true.
// Never allocates. Thread-safe.
template<typename U, typename Rep, typename Period>
inline bool wait_dequeue_timed(consumer_token_t& token, U& item, std::chrono::duration<Rep, Period> const& timeout)
{
return wait_dequeue_timed(token, item, std::chrono::duration_cast<std::chrono::microseconds>(timeout).count());
}
// Attempts to dequeue several elements from the queue.
// Returns the number of items actually dequeued, which will
// always be at least one (this method blocks until the queue
// is non-empty) and at most max.
// Never allocates. Thread-safe.
template<typename It>
inline size_t wait_dequeue_bulk(It itemFirst, size_t max)
{
size_t count = 0;
max = (size_t)sema->waitMany((LightweightSemaphore::ssize_t)(ssize_t)max);
while (count != max) {
count += inner.template try_dequeue_bulk<It&>(itemFirst, max - count);
}
return count;
}
// Attempts to dequeue several elements from the queue.
// Returns the number of items actually dequeued, which can
// be 0 if the timeout expires while waiting for elements,
// and at most max.
// Using a negative timeout indicates an indefinite timeout,
// and is thus functionally equivalent to calling wait_dequeue_bulk.
// Never allocates. Thread-safe.
template<typename It>
inline size_t wait_dequeue_bulk_timed(It itemFirst, size_t max, std::int64_t timeout_usecs)
{
size_t count = 0;
max = (size_t)sema->waitMany((LightweightSemaphore::ssize_t)(ssize_t)max, timeout_usecs);
while (count != max) {
count += inner.template try_dequeue_bulk<It&>(itemFirst, max - count);
}
return count;
}
// Attempts to dequeue several elements from the queue.
// Returns the number of items actually dequeued, which can
// be 0 if the timeout expires while waiting for elements,
// and at most max.
// Never allocates. Thread-safe.
template<typename It, typename Rep, typename Period>
inline size_t wait_dequeue_bulk_timed(It itemFirst, size_t max, std::chrono::duration<Rep, Period> const& timeout)
{
return wait_dequeue_bulk_timed<It&>(itemFirst, max, std::chrono::duration_cast<std::chrono::microseconds>(timeout).count());
}
// Attempts to dequeue several elements from the queue using an explicit consumer token.
// Returns the number of items actually dequeued, which will
// always be at least one (this method blocks until the queue
// is non-empty) and at most max.
// Never allocates. Thread-safe.
template<typename It>
inline size_t wait_dequeue_bulk(consumer_token_t& token, It itemFirst, size_t max)
{
size_t count = 0;
max = (size_t)sema->waitMany((LightweightSemaphore::ssize_t)(ssize_t)max);
while (count != max) {
count += inner.template try_dequeue_bulk<It&>(token, itemFirst, max - count);
}
return count;
}
// Attempts to dequeue several elements from the queue using an explicit consumer token.
// Returns the number of items actually dequeued, which can
// be 0 if the timeout expires while waiting for elements,
// and at most max.
// Using a negative timeout indicates an indefinite timeout,
// and is thus functionally equivalent to calling wait_dequeue_bulk.
// Never allocates. Thread-safe.
template<typename It>
inline size_t wait_dequeue_bulk_timed(consumer_token_t& token, It itemFirst, size_t max, std::int64_t timeout_usecs)
{
size_t count = 0;
max = (size_t)sema->waitMany((LightweightSemaphore::ssize_t)(ssize_t)max, timeout_usecs);
while (count != max) {
count += inner.template try_dequeue_bulk<It&>(token, itemFirst, max - count);
}
return count;
}
// Attempts to dequeue several elements from the queue using an explicit consumer token.
// Returns the number of items actually dequeued, which can
// be 0 if the timeout expires while waiting for elements,
// and at most max.
// Never allocates. Thread-safe.
template<typename It, typename Rep, typename Period>
inline size_t wait_dequeue_bulk_timed(consumer_token_t& token, It itemFirst, size_t max, std::chrono::duration<Rep, Period> const& timeout)
{
return wait_dequeue_bulk_timed<It&>(token, itemFirst, max, std::chrono::duration_cast<std::chrono::microseconds>(timeout).count());
}
// Returns an estimate of the total number of elements currently in the queue. This
// estimate is only accurate if the queue has completely stabilized before it is called
// (i.e. all enqueue and dequeue operations have completed and their memory effects are
// visible on the calling thread, and no further operations start while this method is
// being called).
// Thread-safe.
inline size_t size_approx() const
{
return (size_t)sema->availableApprox();
}
// Returns true if the underlying atomic variables used by
// the queue are lock-free (they should be on most platforms).
// Thread-safe.
static bool is_lock_free()
{
return ConcurrentQueue::is_lock_free();
}
private:
template<typename U>
static inline U* create()
{
auto p = (Traits::malloc)(sizeof(U));
return p != nullptr ? new (p) U : nullptr;
}
template<typename U, typename A1>
static inline U* create(A1&& a1)
{
auto p = (Traits::malloc)(sizeof(U));
return p != nullptr ? new (p) U(std::forward<A1>(a1)) : nullptr;
}
template<typename U>
static inline void destroy(U* p)
{
if (p != nullptr) {
p->~U();
}
(Traits::free)(p);
}
private:
ConcurrentQueue inner;
std::unique_ptr<LightweightSemaphore, void (*)(LightweightSemaphore*)> sema;
};
template<typename T, typename Traits>
inline void swap(BlockingConcurrentQueue<T, Traits>& a, BlockingConcurrentQueue<T, Traits>& b) MOODYCAMEL_NOEXCEPT
{
a.swap(b);
}
} // end namespace moodycamel
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,87 @@
#pragma once
//#define MCDBGQ_TRACKMEM 1
//#define MCDBGQ_NOLOCKFREE_FREELIST 1
//#define MCDBGQ_USEDEBUGFREELIST 1
//#define MCDBGQ_NOLOCKFREE_IMPLICITPRODBLOCKINDEX 1
//#define MCDBGQ_NOLOCKFREE_IMPLICITPRODHASH 1
#if defined(_WIN32) || defined(__WINDOWS__) || defined(__WIN32__)
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
namespace moodycamel { namespace debug {
struct DebugMutex {
DebugMutex() { InitializeCriticalSectionAndSpinCount(&cs, 0x400); }
~DebugMutex() { DeleteCriticalSection(&cs); }
void lock() { EnterCriticalSection(&cs); }
void unlock() { LeaveCriticalSection(&cs); }
private:
CRITICAL_SECTION cs;
};
} }
#else
#include <mutex>
namespace moodycamel { namespace debug {
struct DebugMutex {
void lock() { m.lock(); }
void unlock() { m.unlock(); }
private:
std::mutex m;
};
} }
#define
#endif
namespace moodycamel { namespace debug {
struct DebugLock {
explicit DebugLock(DebugMutex& mutex)
: mutex(mutex)
{
mutex.lock();
}
~DebugLock()
{
mutex.unlock();
}
private:
DebugMutex& mutex;
};
template<typename N>
struct DebugFreeList {
DebugFreeList() : head(nullptr) { }
DebugFreeList(DebugFreeList&& other) : head(other.head) { other.head = nullptr; }
void swap(DebugFreeList& other) { std::swap(head, other.head); }
inline void add(N* node)
{
DebugLock lock(mutex);
node->freeListNext = head;
head = node;
}
inline N* try_get()
{
DebugLock lock(mutex);
if (head == nullptr) {
return nullptr;
}
auto prevHead = head;
head = head->freeListNext;
return prevHead;
}
N* head_unsafe() const { return head; }
private:
N* head;
DebugMutex mutex;
};
} }
+375
View File
@@ -0,0 +1,375 @@
# Samples for moodycamel::ConcurrentQueue
Here are some example usage scenarios with sample code. Note that most
use the simplest version of each available method for demonstration purposes,
but they can all be adapted to use tokens and/or the corresponding bulk methods for
extra speed.
## Hello queue
ConcurrentQueue<int> q;
for (int i = 0; i != 123; ++i)
q.enqueue(i);
int item;
for (int i = 0; i != 123; ++i) {
q.try_dequeue(item);
assert(item == i);
}
## Hello concurrency
Basic example of how to use the queue from multiple threads, with no
particular goal (i.e. it does nothing, but in an instructive way).
ConcurrentQueue<int> q;
int dequeued[100] = { 0 };
std::thread threads[20];
// Producers
for (int i = 0; i != 10; ++i) {
threads[i] = std::thread([&](int i) {
for (int j = 0; j != 10; ++j) {
q.enqueue(i * 10 + j);
}
}, i);
}
// Consumers
for (int i = 10; i != 20; ++i) {
threads[i] = std::thread([&]() {
int item;
for (int j = 0; j != 20; ++j) {
if (q.try_dequeue(item)) {
++dequeued[item];
}
}
});
}
// Wait for all threads
for (int i = 0; i != 20; ++i) {
threads[i].join();
}
// Collect any leftovers (could be some if e.g. consumers finish before producers)
int item;
while (q.try_dequeue(item)) {
++dequeued[item];
}
// Make sure everything went in and came back out!
for (int i = 0; i != 100; ++i) {
assert(dequeued[i] == 1);
}
## Bulk up
Same as previous example, but runs faster.
ConcurrentQueue<int> q;
int dequeued[100] = { 0 };
std::thread threads[20];
// Producers
for (int i = 0; i != 10; ++i) {
threads[i] = std::thread([&](int i) {
int items[10];
for (int j = 0; j != 10; ++j) {
items[j] = i * 10 + j;
}
q.enqueue_bulk(items, 10);
}, i);
}
// Consumers
for (int i = 10; i != 20; ++i) {
threads[i] = std::thread([&]() {
int items[20];
for (std::size_t count = q.try_dequeue_bulk(items, 20); count != 0; --count) {
++dequeued[items[count - 1]];
}
});
}
// Wait for all threads
for (int i = 0; i != 20; ++i) {
threads[i].join();
}
// Collect any leftovers (could be some if e.g. consumers finish before producers)
int items[10];
std::size_t count;
while ((count = q.try_dequeue_bulk(items, 10)) != 0) {
for (std::size_t i = 0; i != count; ++i) {
++dequeued[items[i]];
}
}
// Make sure everything went in and came back out!
for (int i = 0; i != 100; ++i) {
assert(dequeued[i] == 1);
}
## Producer/consumer model (simultaneous)
In this model, one set of threads is producing items,
and the other is consuming them concurrently until all of
them have been consumed. The counters are required to
ensure that all items eventually get consumed.
ConcurrentQueue<Item> q;
const int ProducerCount = 8;
const int ConsumerCount = 8;
std::thread producers[ProducerCount];
std::thread consumers[ConsumerCount];
std::atomic<int> doneProducers(0);
std::atomic<int> doneConsumers(0);
for (int i = 0; i != ProducerCount; ++i) {
producers[i] = std::thread([&]() {
while (produce) {
q.enqueue(produceItem());
}
doneProducers.fetch_add(1, std::memory_order_release);
});
}
for (int i = 0; i != ConsumerCount; ++i) {
consumers[i] = std::thread([&]() {
Item item;
bool itemsLeft;
do {
// It's important to fence (if the producers have finished) *before* dequeueing
itemsLeft = doneProducers.load(std::memory_order_acquire) != ProducerCount;
while (q.try_dequeue(item)) {
itemsLeft = true;
consumeItem(item);
}
} while (itemsLeft || doneConsumers.fetch_add(1, std::memory_order_acq_rel) + 1 == ConsumerCount);
// The condition above is a bit tricky, but it's necessary to ensure that the
// last consumer sees the memory effects of all the other consumers before it
// calls try_dequeue for the last time
});
}
for (int i = 0; i != ProducerCount; ++i) {
producers[i].join();
}
for (int i = 0; i != ConsumerCount; ++i) {
consumers[i].join();
}
## Producer/consumer model (simultaneous, blocking)
The blocking version is different, since either the number of elements being produced needs
to be known ahead of time, or some other coordination is required to tell the consumers when
to stop calling wait_dequeue (not shown here). This is necessary because otherwise a consumer
could end up blocking forever -- and destroying a queue while a consumer is blocking on it leads
to undefined behaviour.
BlockingConcurrentQueue<Item> q;
const int ProducerCount = 8;
const int ConsumerCount = 8;
std::thread producers[ProducerCount];
std::thread consumers[ConsumerCount];
std::atomic<int> promisedElementsRemaining(ProducerCount * 1000);
for (int i = 0; i != ProducerCount; ++i) {
producers[i] = std::thread([&]() {
for (int j = 0; j != 1000; ++j) {
q.enqueue(produceItem());
}
});
}
for (int i = 0; i != ConsumerCount; ++i) {
consumers[i] = std::thread([&]() {
Item item;
while (promisedElementsRemaining.fetch_sub(1, std::memory_order_relaxed)) {
q.wait_dequeue(item);
consumeItem(item);
}
});
}
for (int i = 0; i != ProducerCount; ++i) {
producers[i].join();
}
for (int i = 0; i != ConsumerCount; ++i) {
consumers[i].join();
}
## Producer/consumer model (separate stages)
ConcurrentQueue<Item> q;
// Production stage
std::thread threads[8];
for (int i = 0; i != 8; ++i) {
threads[i] = std::thread([&]() {
while (produce) {
q.enqueue(produceItem());
}
});
}
for (int i = 0; i != 8; ++i) {
threads[i].join();
}
// Consumption stage
std::atomic<int> doneConsumers(0);
for (int i = 0; i != 8; ++i) {
threads[i] = std::thread([&]() {
Item item;
do {
while (q.try_dequeue(item)) {
consumeItem(item);
}
// Loop again one last time if we're the last producer (with the acquired
// memory effects of the other producers):
} while (doneConsumers.fetch_add(1, std::memory_order_acq_rel) + 1 == 8);
});
}
for (int i = 0; i != 8; ++i) {
threads[i].join();
}
Note that there's no point trying to use the blocking queue with this model, since
there's no need to use the `wait` methods (all the elements are produced before any
are consumed), and hence the complexity would be the same but with additional overhead.
## Object pool
If you don't know what threads will be using the queue in advance,
you can't really declare any long-term tokens. The obvious solution
is to use the implicit methods (that don't take any tokens):
// A pool of 'Something' objects that can be safely accessed
// from any thread
class SomethingPool
{
public:
Something getSomething()
{
Something obj;
queue.try_dequeue(obj);
// If the dequeue succeeded, obj will be an object from the
// thread pool, otherwise it will be the default-constructed
// object as declared above
return obj;
}
void recycleSomething(Something&& obj)
{
queue.enqueue(std::move(obj));
}
};
## Threadpool task queue
BlockingConcurrentQueue<Task> q;
// To create a task from any thread:
q.enqueue(...);
// On threadpool threads:
Task task;
while (true) {
q.wait_dequeue(task);
// Process task...
}
## Multithreaded game loop
BlockingConcurrentQueue<Task> q;
std::atomic<int> pendingTasks(0);
// On threadpool threads:
Task task;
while (true) {
q.wait_dequeue(task);
// Process task...
pendingTasks.fetch_add(-1, std::memory_order_release);
}
// Whenever a new task needs to be processed for the frame:
pendingTasks.fetch_add(1, std::memory_order_release);
q.enqueue(...);
// To wait for all the frame's tasks to complete before rendering:
while (pendingTasks.load(std::memory_order_acquire) != 0)
continue;
// Alternatively you could help out the thread pool while waiting:
while (pendingTasks.load(std::memory_order_acquire) != 0) {
if (!q.try_dequeue(task)) {
continue;
}
// Process task...
pendingTasks.fetch_add(-1, std::memory_order_release);
}
## Pump until empty
This might be useful if, for example, you want to process any remaining items
in the queue before it's destroyed. Note that it is your responsibility
to ensure that the memory effects of any enqueue operations you wish to see on
the dequeue thread are visible (i.e. if you're waiting for a certain set of elements,
you need to use memory fences to ensure that those elements are visible to the dequeue
thread after they've been enqueued).
ConcurrentQueue<Item> q;
// Single-threaded pumping:
Item item;
while (q.try_dequeue(item)) {
// Process item...
}
// q is guaranteed to be empty here, unless there is another thread enqueueing still or
// there was another thread dequeueing at one point and its memory effects have not
// yet been propagated to this thread.
// Multi-threaded pumping:
std::thread threads[8];
std::atomic<int> doneConsumers(0);
for (int i = 0; i != 8; ++i) {
threads[i] = std::thread([&]() {
Item item;
do {
while (q.try_dequeue(item)) {
// Process item...
}
} while (doneConsumers.fetch_add(1, std::memory_order_acq_rel) + 1 == 8);
// If there are still enqueue operations happening on other threads,
// then the queue may not be empty at this point. However, if all enqueue
// operations completed before we finished pumping (and the propagation of
// their memory effects too), and all dequeue operations apart from those
// our threads did above completed before we finished pumping (and the
// propagation of their memory effects too), then the queue is guaranteed
// to be empty at this point.
});
}
for (int i = 0; i != 8; ++i) {
threads[i].join();
}
## Wait for a queue to become empty (without dequeueing)
You can't (robustly) :-) However, you can set up your own atomic counter and
poll that instead (see the game loop example). If you're satisfied with merely an estimate, you can use
`size_approx()`. Note that `size_approx()` may return 0 even if the queue is
not completely empty, unless the queue has already stabilized first (no threads
are enqueueing or dequeueing, and all memory effects of any previous operations
have been propagated to the thread before it calls `size_approx()`).
+53
View File
@@ -0,0 +1,53 @@
# Normal stuff
*.o
*.a
*.so
*.lo
*.la
*.pc
.deps/
.libs/
.kdev4/
build/
# kdevelop
*.kdevelop.pcs
*.kdevses
# Doxygen documentation
Doxyfile
Doxyfile.xml
doc/Doxyfile
doc/html
doc/man
doc/xml
# examples
examples/baud_test
examples/bitbang
examples/bitbang2
examples/bitbang_cbus
examples/bitbang_ft2232
examples/find_all
examples/find_all_pp
examples/serial_test
examples/simple
# Backup files and stuff from patches
*.orig
*.rej
*~
.*.swp
# libftdi specific
libftdi1-config
libftdi1.spec
# CMake
CMakeCache.txt
cmake_install.cmake
CMakeFiles
# Misc. binaries
*.dylib
opt
+79
View File
@@ -0,0 +1,79 @@
Main developers:
Intra2net AG <opensource@intra2net.com>
Contributors in alphabetical order,
see Changelog for full details:
Adam Malinowski <amalinowski75@gmail.com>
Alain Abbas <aa@libertech.fr>
Alexander Lehmann <lehmanna@in.tum.de>
Alex Harford <harford@gmail.com>
Anders Larsen <al@alarsen.net>
Andrei Errapart <a.errapart@trenz-electronic.de>
Andrew John Rogers <andrew@rogerstech.co.uk>
Arnim Läuger <arnim.laeuger@gmx.net>
Aurelien Jarno <aurelien@aurel32.net>
Benjamin Vanheuverzwijn <bvanheu@gmail.com>
Chris Morgan <chmorgan@gmail.com>
Chris Zeh <chris.w.zeh@gmail.com>
Clifford Wolf <clifford@clifford.at>
Daniel Kirkham <dk2@kirkham.id.au>
David Challis <dchallis@qsimaging.com>
Davide Michelizza <dmichelizza@gmail.com>
Denis Sirotkin <reg.libftdi@demitel.ru>
Emil <emil@datel.co.uk>
Eric Schott <eric@morningjoy.com>
Eugene Hutorny <eugene@hutorny.in.ua>
Evan Nemerson <evan@coeus-group.com>
Evgeny Sinelnikov <sin@geoft.ru>
Fahrzin Hemmati <fahhem@gmail.com>
Flynn Marquardt <ftdi@flynnux.de>
Forest Crossman <cyrozap@gmail.com>
Ian Abbott <abbotti@mev.co.uk>
Jared Boone <jared@sharebrained.com>
Jarkko Sonninen <kasper@iki.fi>
Jean-Daniel Merkli <jdmerkli@computerscience.ch>
Jochen Sprickerhof <jochen@sprickerhof.de>
Joe Zbiciak <intvnut@gmail.com>
Jon Beniston <jon@beniston.com>
Juergen Beisert <juergen.beisert@weihenstephan.org>
Lorenz Moesenlechner <lorenz@hcilab.org>
Marek Vavruša <marek@vavrusa.com>
Marius Kintel <kintel@sim.no>
Mark Hämmerling <mail@markh.de>
Matthias Janke <janke@physi.uni-heidelberg.de>
Matthias Kranz <matthias@hcilab.org>
Matthias Richter <mail.to.mr@gmx.de>
Matthijs ten Berge <m.h.tenberge@alumnus.utwente.nl>
Max <max@koeln.ccc.de>
Maxwell Dreytser <admin@mdtech.us>
Michel Zou <xantares09@hotmail.com>
Mike Frysinger <vapier.adi@gmail.com>
Nathael Pajani <nathael.pajani@ed3l.fr>
Nathan Fraser <ndf@undershorts.org>
Oleg Seiljus <oseiljus@xverve.com>
Paul Fertser <fercerpav@gmail.com>
Peter Holik <peter@holik.at>
Raphael Assenat <raph@8d.com>
Robert Cox <Robert.cox@novatechweb.com>
Robin Haberkorn <haberkorn@metratec.com>
Rodney Sinclair <rodney@sinclairrf.com>
Rogier Wolff <R.E.Wolff@harddisk-recovery.nl>
Rolf Fiedler <derRolf@gmx-topmail.de>
Salvador Eduardo Tropea <salvador@inti.gob.ar>
Stephan Linz <linz@li-pro.net>
Steven Turner <steven.turner@ftdichip.com>
Tarek Heiland <tarek@illimitable.com>
Thilo Schulz <thilo@tjps.eu>
Thimo Eichstaedt <abc@digithi.de>
Thomas Fischl <fischl@fundf.net>
Thomas Klose <thomas.klose@hiperscan.com>
Tim Ansell <mithro@mithis.com>
Tom Saunders <trsaunders@gmail.com>
Uwe Bonnes <bon@elektron.ikp.physik.tu-darmstadt.de>
Vladimir Yakovlev <nagos@inbox.ru>
Wilfried Holzke <libftdi@holzke.net>
Xiaofan Chen <xiaofanc@gmail.com>
Yegor Yefremov <yegorslists@googlemail.com>
Yi-Shin Li <ysli@araisrobo.com>
+244
View File
@@ -0,0 +1,244 @@
# Project
project(libftdi1)
set(MAJOR_VERSION 1)
set(MINOR_VERSION 4)
set(PACKAGE libftdi1)
set(VERSION_STRING ${MAJOR_VERSION}.${MINOR_VERSION})
set(VERSION ${VERSION_STRING})
set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake)
# CMake
if("${CMAKE_BUILD_TYPE}" STREQUAL "")
set(CMAKE_BUILD_TYPE RelWithDebInfo)
endif("${CMAKE_BUILD_TYPE}" STREQUAL "")
set(CMAKE_COLOR_MAKEFILE ON)
cmake_minimum_required(VERSION 2.6 FATAL_ERROR)
add_definitions(-Wall)
# Debug build
message("-- Build type: ${CMAKE_BUILD_TYPE}")
if(${CMAKE_BUILD_TYPE} STREQUAL Debug)
add_definitions(-DDEBUG)
endif(${CMAKE_BUILD_TYPE} STREQUAL Debug)
# find libusb
find_package ( USB1 REQUIRED )
include_directories ( ${LIBUSB_INCLUDE_DIR} )
# Find Boost (optional package)
find_package(Boost)
# Set components
set(CPACK_COMPONENTS_ALL sharedlibs staticlibs headers)
set(CPACK_COMPONENT_SHAREDLIBS_DISPLAY_NAME "Shared libraries")
set(CPACK_COMPONENT_STATICLIBS_DISPLAY_NAME "Static libraries")
set(CPACK_COMPONENT_HEADERS_DISPLAY_NAME "C++ Headers")
set(CPACK_COMPONENT_SHAREDLIBS_DESCRIPTION
"Shared library for general use.")
set(CPACK_COMPONENT_STATICLIBS_DESCRIPTION
"Static library, good if you want to embed libftdi1 in your application.")
set(CPACK_COMPONENT_HEADERS_DESCRIPTION
"C/C++ header files.")
set(CPACK_COMPONENT_SHAREDLIBS_GROUP "Development")
set(CPACK_COMPONENT_STATICLIBS_GROUP "Development")
set(CPACK_COMPONENT_HEADERS_GROUP "Development")
option ( STATICLIBS "Build static libraries" ON )
# guess LIB_SUFFIX, don't take debian multiarch into account
if ( NOT DEFINED LIB_SUFFIX )
if( CMAKE_SYSTEM_NAME MATCHES "Linux"
AND NOT CMAKE_CROSSCOMPILING
AND NOT EXISTS "/etc/debian_version"
AND NOT EXISTS "/etc/arch-release" )
if ( "${CMAKE_SIZEOF_VOID_P}" EQUAL "8" )
set ( LIB_SUFFIX 64 )
endif ()
endif ()
endif ()
if(NOT APPLE)
if(CMAKE_SIZEOF_VOID_P EQUAL 4)
SET(PACK_ARCH "")
else(CMAKE_SIZEOF_VOID_P EQUAL 8)
SET(PACK_ARCH .x86_64)
endif(CMAKE_SIZEOF_VOID_P EQUAL 4)
else(NOT APPLE)
SET(PACK_ARCH "")
endif(NOT APPLE)
# Package information
set(CPACK_PACKAGE_VERSION ${VERSION_STRING})
set(CPACK_PACKAGE_CONTACT "Intra2net AG <libftdi@developer.intra2net.com>")
set(CPACK_PACKAGE_DESCRIPTION "libftdi1 library.")
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY ${CPACK_PACKAGE_DESCRIPTION}
)
# Package settings
if ( UNIX )
set(CPACK_GENERATOR "DEB;RPM")
set(CPACK_CMAKE_GENERATOR "Unix Makefiles")
set(CPACK_PACKAGE_NAME ${PROJECT_NAME})
set(CPACK_PACKAGE_FILE_NAME ${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}${PACK_ARCH})
endif ()
if ( WIN32 )
set ( CPACK_GENERATOR "NSIS" )
set ( CPACK_CMAKE_GENERATOR "MinGW Makefiles" )
set ( CPACK_PACKAGE_NAME "${PROJECT_NAME}" )
set ( CPACK_PACKAGE_VENDOR "" )
set ( CPACK_PACKAGE_INSTALL_DIRECTORY "libftdi1" )
set ( CPACK_PACKAGE_FILE_NAME "${PROJECT_NAME}-${VERSION_STRING}-win32")
set ( CPACK_NSIS_DISPLAY_NAME "libftdi1" )
set ( CPACK_NSIS_MODIFY_PATH ON )
endif ()
set(CPACK_RESOURCE_FILE_LICENSE ${PROJECT_SOURCE_DIR}/LICENSE)
set(CPACK_SOURCE_GENERATOR TGZ)
set(CPACK_SOURCE_IGNORE_FILES "\\\\.git;~$;build/")
set(CPACK_SOURCE_PACKAGE_FILE_NAME ${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION})
# Subdirectories
if ( UNIX )
set ( CPACK_SET_DESTDIR ON )
endif ()
# "make dist" target
set(ARCHIVE_NAME ${CMAKE_PROJECT_NAME}-${VERSION_STRING})
add_custom_target(dist
COMMAND git archive --prefix=${ARCHIVE_NAME}/ HEAD
| bzip2 > ${CMAKE_BINARY_DIR}/${ARCHIVE_NAME}.tar.bz2
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR})
# Tests
option ( BUILD_TESTS "Build unit tests with Boost Unit Test framework" ON )
# Documentation
option ( DOCUMENTATION "Generate API documentation with Doxygen" ON )
find_package ( Doxygen )
if ( DOCUMENTATION AND DOXYGEN_FOUND )
# Find doxy config
message(STATUS "Doxygen found.")
# Copy doxy.config.in
set(top_srcdir ${PROJECT_SOURCE_DIR})
configure_file(${PROJECT_SOURCE_DIR}/doc/Doxyfile.in ${CMAKE_BINARY_DIR}/Doxyfile )
configure_file(${PROJECT_SOURCE_DIR}/doc/Doxyfile.xml.in ${CMAKE_BINARY_DIR}/Doxyfile.xml )
# Run doxygen
add_custom_command(
OUTPUT ${CMAKE_BINARY_DIR}/doc/html/index.html
COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_BINARY_DIR}/doc
COMMAND ${DOXYGEN_EXECUTABLE} ${CMAKE_BINARY_DIR}/Doxyfile
DEPENDS ${c_headers};${c_sources};${cpp_sources};${cpp_headers}
)
add_custom_target(docs ALL DEPENDS ${CMAKE_BINARY_DIR}/doc/html/index.html)
message(STATUS "Generating API documentation with Doxygen")
else(DOCUMENTATION AND DOXYGEN_FOUND)
message(STATUS "Not generating API documentation")
endif(DOCUMENTATION AND DOXYGEN_FOUND)
add_subdirectory(src)
add_subdirectory(ftdipp)
add_subdirectory(python)
add_subdirectory(ftdi_eeprom)
add_subdirectory(examples)
add_subdirectory(packages)
add_subdirectory(test)
# PkgConfig
set(prefix ${CMAKE_INSTALL_PREFIX})
set(exec_prefix ${CMAKE_INSTALL_PREFIX}/bin)
set(includedir ${CMAKE_INSTALL_PREFIX}/include/${PROJECT_NAME})
if(${UNIX})
set(libdir ${CMAKE_INSTALL_PREFIX}/lib${LIB_SUFFIX})
endif(${UNIX})
if(${WIN32})
set(libdir ${CMAKE_INSTALL_PREFIX}/bin)
endif(${WIN32})
configure_file(${PROJECT_SOURCE_DIR}/libftdi1.spec.in ${CMAKE_BINARY_DIR}/libftdi1.spec @ONLY)
configure_file(${PROJECT_SOURCE_DIR}/libftdi1.pc.in ${CMAKE_BINARY_DIR}/libftdi1.pc @ONLY)
configure_file(${PROJECT_SOURCE_DIR}/libftdipp1.pc.in ${CMAKE_BINARY_DIR}/libftdipp1.pc @ONLY)
install(FILES ${CMAKE_BINARY_DIR}/libftdi1.pc ${CMAKE_BINARY_DIR}/libftdipp1.pc
DESTINATION lib${LIB_SUFFIX}/pkgconfig)
if (UNIX OR MINGW)
configure_file ( libftdi1-config.in ${CMAKE_CURRENT_BINARY_DIR}/libftdi1-config @ONLY )
install ( PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/libftdi1-config
DESTINATION bin )
endif ()
# config script install path
if ( NOT DEFINED LIBFTDI_CMAKE_CONFIG_DIR )
set ( LIBFTDI_CMAKE_CONFIG_DIR lib${LIB_SUFFIX}/cmake/libftdi1 )
endif ()
set ( LIBFTDI_INCLUDE_DIR ${includedir} )
set ( LIBFTDI_INCLUDE_DIRS ${LIBFTDI_INCLUDE_DIR} )
set ( LIBFTDI_LIBRARY ftdi1 )
set ( LIBFTDI_LIBRARIES ${LIBFTDI_LIBRARY} )
list ( APPEND LIBFTDI_LIBRARIES ${LIBUSB_LIBRARIES} )
set ( LIBFTDI_STATIC_LIBRARY ftdi1.a )
set ( LIBFTDI_STATIC_LIBRARIES ${LIBFTDI_STATIC_LIBRARY} )
list ( APPEND LIBFTDI_STATIC_LIBRARIES ${LIBUSB_LIBRARIES} )
if (FTDI_BUILD_CPP)
set ( LIBFTDIPP_LIBRARY ftdipp1 )
set ( LIBFTDIPP_LIBRARIES ${LIBFTDIPP_LIBRARY} )
list ( APPEND LIBFTDIPP_LIBRARIES ${LIBUSB_LIBRARIES} )
endif ()
set ( LIBFTDI_LIBRARY_DIRS ${libdir} )
set ( LIBFTDI_ROOT_DIR ${prefix} )
set ( LIBFTDI_VERSION_STRING ${VERSION_STRING} )
set ( LIBFTDI_VERSION_MAJOR ${MAJOR_VERSION} )
set ( LIBFTDI_VERSION_MINOR ${MINOR_VERSION} )
set ( LIBFTDI_USE_FILE ${CMAKE_INSTALL_PREFIX}/${LIBFTDI_CMAKE_CONFIG_DIR}/UseLibFTDI1.cmake )
if(CMAKE_VERSION VERSION_LESS 2.8.8)
configure_file ( cmake/LibFTDI1Config.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/LibFTDI1Config.cmake @ONLY )
configure_file ( cmake/LibFTDI1ConfigVersion.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/LibFTDI1ConfigVersion.cmake @ONLY )
else ()
include(CMakePackageConfigHelpers)
configure_package_config_file (
cmake/LibFTDI1Config.cmake.in
${CMAKE_CURRENT_BINARY_DIR}/LibFTDI1Config.cmake
INSTALL_DESTINATION ${LIBFTDI_CMAKE_CONFIG_DIR}
PATH_VARS
LIBFTDI_USE_FILE
LIBFTDI_ROOT_DIR
LIBFTDI_INCLUDE_DIR
LIBFTDI_INCLUDE_DIRS
LIBFTDI_LIBRARY_DIRS
NO_CHECK_REQUIRED_COMPONENTS_MACRO
)
write_basic_package_version_file (
LibFTDI1ConfigVersion.cmake
VERSION ${LIBFTDI_VERSION_STRING}
COMPATIBILITY AnyNewerVersion
)
endif ()
install ( FILES
${CMAKE_CURRENT_BINARY_DIR}/LibFTDI1Config.cmake
${CMAKE_CURRENT_BINARY_DIR}/LibFTDI1ConfigVersion.cmake
cmake/UseLibFTDI1.cmake
DESTINATION ${LIBFTDI_CMAKE_CONFIG_DIR}
)
include(CPack)
+22
View File
@@ -0,0 +1,22 @@
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. The name of the author may not be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+339
View File
@@ -0,0 +1,339 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License.
+481
View File
@@ -0,0 +1,481 @@
GNU LIBRARY GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1991 Free Software Foundation, Inc.
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
[This is the first released version of the library GPL. It is
numbered 2 because it goes with version 2 of the ordinary GPL.]
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.
This license, the Library General Public License, applies to some
specially designated Free Software Foundation software, and to any
other libraries whose authors decide to use it. You can use it for
your libraries, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if
you distribute copies of the library, or if you modify it.
For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you. You must make sure that they, too, receive or can get the source
code. If you link a program with the library, you must provide
complete object files to the recipients so that they can relink them
with the library, after making changes to the library and recompiling
it. And you must show them these terms so they know their rights.
Our method of protecting your rights has two steps: (1) copyright
the library, and (2) offer you this license which gives you legal
permission to copy, distribute and/or modify the library.
Also, for each distributor's protection, we want to make certain
that everyone understands that there is no warranty for this free
library. If the library is modified by someone else and passed on, we
want its recipients to know that what they have is not the original
version, so that any problems introduced by others will not reflect on
the original authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that companies distributing free
software will individually obtain patent licenses, thus in effect
transforming the program into proprietary software. To prevent this,
we have made it clear that any patent must be licensed for everyone's
free use or not licensed at all.
Most GNU software, including some libraries, is covered by the ordinary
GNU General Public License, which was designed for utility programs. This
license, the GNU Library General Public License, applies to certain
designated libraries. This license is quite different from the ordinary
one; be sure to read it in full, and don't assume that anything in it is
the same as in the ordinary license.
The reason we have a separate public license for some libraries is that
they blur the distinction we usually make between modifying or adding to a
program and simply using it. Linking a program with a library, without
changing the library, is in some sense simply using the library, and is
analogous to running a utility program or application program. However, in
a textual and legal sense, the linked executable is a combined work, a
derivative of the original library, and the ordinary General Public License
treats it as such.
Because of this blurred distinction, using the ordinary General
Public License for libraries did not effectively promote software
sharing, because most developers did not use the libraries. We
concluded that weaker conditions might promote sharing better.
However, unrestricted linking of non-free programs would deprive the
users of those programs of all benefit from the free status of the
libraries themselves. This Library General Public License is intended to
permit developers of non-free programs to use free libraries, while
preserving your freedom as a user of such programs to change the free
libraries that are incorporated in them. (We have not seen how to achieve
this as regards changes in header files, but we have achieved it as regards
changes in the actual functions of the Library.) The hope is that this
will lead to faster development of free libraries.
The precise terms and conditions for copying, distribution and
modification follow. Pay close attention to the difference between a
"work based on the library" and a "work that uses the library". The
former contains code derived from the library, while the latter only
works together with the library.
Note that it is possible for a library to be covered by the ordinary
General Public License rather than by this special one.
GNU LIBRARY GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License Agreement applies to any software library which
contains a notice placed by the copyright holder or other authorized
party saying it may be distributed under the terms of this Library
General Public License (also called "this License"). Each licensee is
addressed as "you".
A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.
The "Library", below, refers to any such software library or work
which has been distributed under these terms. A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language. (Hereinafter, translation is
included without limitation in the term "modification".)
"Source code" for a work means the preferred form of the work for
making modifications to it. For a library, complete source code means
all the source code for all modules it contains, plus any associated
interface definition files, plus the scripts used to control compilation
and installation of the library.
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it). Whether that is true depends on what the Library does
and what the program that uses the Library does.
1. You may copy and distribute verbatim copies of the Library's
complete source code as you receive it, in any medium, provided that
you conspicuously and appropriately publish on each copy an
appropriate copyright notice and disclaimer of warranty; keep intact
all the notices that refer to this License and to the absence of any
warranty; and distribute a copy of this License along with the
Library.
You may charge a fee for the physical act of transferring a copy,
and you may at your option offer warranty protection in exchange for a
fee.
2. You may modify your copy or copies of the Library or any portion
of it, thus forming a work based on the Library, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) The modified work must itself be a software library.
b) You must cause the files modified to carry prominent notices
stating that you changed the files and the date of any change.
c) You must cause the whole of the work to be licensed at no
charge to all third parties under the terms of this License.
d) If a facility in the modified Library refers to a function or a
table of data to be supplied by an application program that uses
the facility, other than as an argument passed when the facility
is invoked, then you must make a good faith effort to ensure that,
in the event an application does not supply such function or
table, the facility still operates, and performs whatever part of
its purpose remains meaningful.
(For example, a function in a library to compute square roots has
a purpose that is entirely well-defined independent of the
application. Therefore, Subsection 2d requires that any
application-supplied function or table used by this function must
be optional: if the application does not supply it, the square
root function must still compute square roots.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Library,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Library, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote
it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Library.
In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library. To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License. (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.) Do not make any other change in
these notices.
Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.
This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.
4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you accompany
it with the complete corresponding machine-readable source code, which
must be distributed under the terms of Sections 1 and 2 above on a
medium customarily used for software interchange.
If distribution of object code is made by offering access to copy
from a designated place, then offering equivalent access to copy the
source code from the same place satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.
5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library". Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.
However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library". The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.
When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library. The
threshold for this to be true is not precisely defined by law.
If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work. (Executables containing this object code plus portions of the
Library will still fall under Section 6.)
Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.
6. As an exception to the Sections above, you may also compile or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.
You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License. You must supply a copy of this License. If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License. Also, you must do one
of these things:
a) Accompany the work with the complete corresponding
machine-readable source code for the Library including whatever
changes were used in the work (which must be distributed under
Sections 1 and 2 above); and, if the work is an executable linked
with the Library, with the complete machine-readable "work that
uses the Library", as object code and/or source code, so that the
user can modify the Library and then relink to produce a modified
executable containing the modified Library. (It is understood
that the user who changes the contents of definitions files in the
Library will not necessarily be able to recompile the application
to use the modified definitions.)
b) Accompany the work with a written offer, valid for at
least three years, to give the same user the materials
specified in Subsection 6a, above, for a charge no more
than the cost of performing this distribution.
c) If distribution of the work is made by offering access to copy
from a designated place, offer equivalent access to copy the above
specified materials from the same place.
d) Verify that the user has already received a copy of these
materials or that you have already sent this user a copy.
For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it. However, as a special exception,
the source code distributed need not include anything that is normally
distributed (in either source or binary form) with the major
components (compiler, kernel, and so on) of the operating system on
which the executable runs, unless that component itself accompanies
the executable.
It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system. Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.
7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:
a) Accompany the combined library with a copy of the same work
based on the Library, uncombined with any other library
facilities. This must be distributed under the terms of the
Sections above.
b) Give prominent notice with the combined library of the fact
that part of it is a work based on the Library, and explaining
where to find the accompanying uncombined form of the same work.
8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License. Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library is void, and will automatically terminate your
rights under this License. However, parties who have received copies,
or rights, from you under this License will not have their licenses
terminated so long as such parties remain in full compliance.
9. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Library or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.
10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
subject to these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
11. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Library at all. For example, if a patent
license would not permit royalty-free redistribution of the Library by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Library.
If any portion of this section is held invalid or unenforceable under any
particular circumstance, the balance of the section is intended to apply,
and the section as a whole is intended to apply in other circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library under this License may add
an explicit geographical distribution limitation excluding those countries,
so that distribution is permitted only in or among countries not thus
excluded. In such case, this License incorporates the limitation as if
written in the body of this License.
13. The Free Software Foundation may publish revised and/or new
versions of the Library General Public License from time to time.
Such new versions will be similar in spirit to the present version,
but may differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the Library
specifies a version number of this License which applies to it and
"any later version", you have the option of following the terms and
conditions either of that version or of any later version published by
the Free Software Foundation. If the Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.
14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
write to the author to ask for permission. For software which is
copyrighted by the Free Software Foundation, write to the Free
Software Foundation; we sometimes make exceptions for this. Our
decision will be guided by the two goals of preserving the free status
of all derivatives of our free software and of promoting the sharing
and reuse of software generally.
NO WARRANTY
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Libraries
If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change. You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).
To apply these terms, attach the following notices to the library. It is
safest to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least the
"copyright" line and a pointer to where the full notice is found.
<one line to give the library's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Also add information on how to contact you by electronic and paper mail.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the library, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the
library `Frob' (a library for tweaking knobs) written by James Random Hacker.
<signature of Ty Coon>, 1 April 1990
Ty Coon, President of Vice
That's all there is to it!
+251
View File
@@ -0,0 +1,251 @@
New in 1.4 - 2017-08-07
-----------------------
* New ftdi_usb_open_bus_addr() open function
* Use BM/R series baud rate computation for FT230X
* ftdi_get_error_string() now returns const char*
* C++ API: Ability to open devices with empty descriptor strings
* C++ API: Fix enumerations for buffer purge and modem controls
* small build fixes and improvements in the python examples
* ftdi_eeprom / eeprom handling:
* New API function: ftdi_eeprom_get_strings()
* Fix USE_SERIAL handling for 230X type chips
* Make ftdi_read_eeprom_location() endianness independent
* Fix flashing of FT245R
New in 1.3 - 2016-05-20
-----------------------
* Added ftdi_usb_get_strings2() to prevent automatic device close (Fahrzin Hemmati)
* Added ftdi_transfer_data_cancel() for cancellation of a submitted transfer,
avoided resubmittion of a canceled transfer in the callbacks,
replaced calls to libusb_handle_events with
libusb_handle_events_timeout_completed (Eugene Hutorny)
* ftdi_eeprom / eeprom handling:
* Add support for arbitrary user data (Salvador Eduardo Tropea)
* Add --build-eeprom support (Salvador Eduardo Tropea)
* Fix use_usb_version config file option (Thilo Schulz)
* Ability to include other config files in EEPROM config file (Thilo Schulz)
* Add external oscillator enable bit (Raphael Assenat)
* Support channel configuration (Stephan Linz)
* Added --device option to ftdi_eeprom to specify FTDI device (Robin Haberkorn)
* Fixed EEPROM user-area space checks for FT232R and FT245R chips (Robin Haberkorn)
* Various improvements to CBUS handling, including the EEPROM (Robin Haberkorn)
* swig wrapper: Fix handling of binary strings in ftdi_write_data()
for python 3 (xantares09)
* cbus python example code (Rodney Sinclair)
* ftdi_stream: fix timeout setting (Ларионов Даниил)
* Fixed typo in CBUS defines: CBUSG_DRIVE1 -> CBUSH_DRIVE1
New in 1.2 - 2014-11-21
-----------------------
* Support for FT230X devices (Uwe Bonnes)
* ftdi_usb_get_strings(): Don't try to open an already open device (Denis Sirotkin)
* Support for finding devices bricked by the Windows driver (Forest Crossman)
* cmake build system: New LibFTDI1ConfigVersion.cmake file (xantares09)
* Fix a typo in the MPSSE command CLK_BYTES_OR_LOW (Benjamin Vanheuverzwijn)
* Minor fixes for MSVC++ (Andrei Errapart)
* Various small code improvements (Florian Preinstorfer, Jochen Sprickerhof, xantares09)
New in 1.1 - 2014-02-05
-----------------------
* Fix FT232H eeprom suspend pulldown setting (Davide Michelizza)
* Fix FT232H eeprom user area size (Davide Michelizza)
* Improved mingw build (Paul Fertser and Michel Zou)
* C++ wrapper: Get/set functions for USB timeouts (Jochen Sprickerhof)
* Partial support for FT230X (Nathael Pajani)
* New API function: ftdi_eeprom_set_strings() (Nathael Pajani)
* Prevent possible segfault in ftdi_eeprom_decode() (Nathael Pajani)
* Save device release number in eeprom (Jarkko Sonninen)
* Fix "self powered" eeprom flag (Jarkko Sonninen)
* Improved python wrapper (Michel Zou)
* Many buildsystem improvements (Michel Zou and Mike Frysinger)
* See the git history for more changes and fixes
New in 1.0 - 2013-01-29
-----------------------
* Ported to libusb 1.x (initial work by Jie Zhang)
* Many eeprom handling improvements (Uwe Bonnes, Anders Larsen)
* Renamed pkconfig, library .so etc. files to "libftdi1" (Intra2net)
* ftdi_eeprom is part of libftdi now (Intra2net)
* New baudrate calculation code + unit tests (Uwe Bonnes and Intra2net)
* Improved python bindings including python3 support (Michel Zou)
* Switched completely to cmake build system (Intra2net)
* cmake: Easy libftdi discovery via find_package() (Michel Zou)
* eeprom handling now done via get()/set() functions (Uwe Bonnes)
* C++ wrapper: Fixed use-after-free in List::find_all() (Intra2net)
* Documentation updates (Xiaofan Chen)
* See the git history for more changes and fixes
New in 0.20 - 2012-03-19
------------------------
* Support for FT232H (Uwe Bonnes)
* Fixed install location of header files (Uwe Bonnes and Intra2net)
* Backported serial_test tool from libftdi 1.x (Uwe Bonnes)
New in 0.19 - 2011-05-23
------------------------
* Make kernel driver detach configurable (Thomas Klose)
* Correct ftdi_poll_modem_status() result code (Tom Saunders)
* cmake build system improvements (Evgeny Sinelnikov)
* Fix uninitialized memory access in async mode (Intra2net)
* Support for FT232R eeprom features (Hermann Kraus)
* Fix size returned by ftdi_read_data (Hermann Kraus)
* C++ wrapper: Fix infinite recursion in set_bitmode (Intra2net)
* Improvements to the python wrapper (Flynn Marquardt and Chris Zeh)
New in 0.18 - 2010-06-25
------------------------
* Add ftdi_eeprom_free() to free allocated memory in eeprom (Wilfried Holzke)
* More generic error message for the FTDI kernel driver (Intra2net)
* Honor CPPFLAGS in python wrapper build (Alexander Lehmann)
* cmake: Fix package creation on 32-bit machines (Uwe Bonnes)
* Fix swig argument constraints (Intra2net)
* Don't segfault if device is closed or ftdi context is invalid (Intra2net)
* Ability to disable build of examples / documentation (Mike Frysinger and Intra2net)
* Fix typo in python wrapper build (Mike Frysinger)
* Autoconf build system improvements (Mike Frysinger)
New in 0.17 - 2009-12-19
------------------------
* C++ wrapper: Reduced code duplication and small other changes (Intra2net)
* Deprecated old ftdi_enable_bitbang() function (Intra2net)
* New ftdi_usb_open_desc_index() function (Intra2net)
* Added baud rate test example code (Intra2net)
* New serial input example code (Jim Paris)
* Fix modem status byte filtering for USB high speed chips (Intra2net and Jim Paris)
* Add bitmode for synchronous fifo in FT2232H (Uwe Bonnes)
* Fix usb_set_configuration() call on Windows 64 (NIL)
* Fix usb index in ftdi_convert_baudrate() for FT2232H/FT4232H chips (Thimo Eichstaedt)
* Set initial baudrate on correct interface instead of always the first one (Thimo Eichstaedt)
* Call usb_set_configuration() on Windows only (Uwe Bonnes)
* 64 bit and other buildsystem fixes (Uwe Bonnes)
* Don't build --with-async-mode w/ libusb-compat-0.1 (Clifford Wolf)
* Functions for read/write of a single eeprom location (Oleg Seiljus)
* Protect against double close of usb device (Nathan Fraser)
* Fix out-of-tree-build in python wrapper (Aurelien Jarno)
* Autoconf and doxygen cleanup (Jim Paris)
New in 0.16 - 2009-05-08
------------------------
* C++ wrapper: Reopen the device after calling get_strings() in Context::open() (Marek Vavruša and Intra2net)
* C++ wrapper: Fixed an inheritance problem (Marek Vavruša and Intra2net)
* C++ wrapper: Relicensed under GPLv2 + linking exception (Marek Vavruša and Intra2net)
* Support for FT2232H and FT4232H (David Challis, Alex Harford and Intra2net)
* Support for mingw cross compile (Uwe Bonnes)
* Python bindings and minor autoconf cleanup (Tarek Heiland)
* Code cleanup in various places (Intra2net)
* Fixed ftdi_read_chipid in some cases (Matthias Richter)
* eeprom decode function and small cleanups (Marius Kintel)
* cmake system improvements (Marius Kintel and Intra2net)
* Fix compilation in -ansi -pedantic mode (Matthias Janke)
New in 0.15 - 2008-12-19
------------------------
* Full C++ wrapper. Needs boost (Marek Vavruša and Intra2net)
* cmake rules (Marek Vavruša)
New in 0.14 - 2008-09-09
------------------------
* Fixed flow control code for second FT2232 interface (Marek Vavruša)
* Ability to set flow control via one USB call (Marek Vavruša)
* 64 bit build support in the RPM spec file (Uwe Bonnes)
* Small fix to the RPM spec file (Uwe Bonnes)
* Ability to set RS232 break type (Intra2net)
* Grouped flow control and modem status code together (Intra2net)
New in 0.13 - 2008-06-13
------------------------
* Build .spec file via configure.in (Intra2net)
* Fixed "libusb-config --cflags" call (Mike Frysinger and Intra2net)
* Always set usb configuration (Mike Frysinger and Intra2net)
* Improved libusb-win32 support (Mike Frysinger)
New in 0.12 - 2008-04-16
------------------------
* Fix build of documentation for "out of tree" builds
* Fix USB config descriptor in the eeprom (Juergen Beisert)
* Ability to purge RX/TX buffers separately (Arnim Läuger)
* Setting of event and error character (Arnim Läuger)
* Poll modem status function (Arnim Läuger and Intra2net)
* Updated documentation and created AUTHORS file
New in 0.11 - 2008-03-01
------------------------
* Vala bindings helper functions (ftdi_new, ftdi_free, ftdi_list_free2) (Even Nermerson)
* Support for different EEPROM sizes (Andrew John Rogers, andrew@rogerstech.co.uk)
* Async write support. Linux only and no error handling.
You have to enable it via --with-async-mode.
* Detection of R-type chips
* FTDIChip-ID read support (Peter Holik)
New in 0.10 - 2007-05-08
------------------------
* Examples for libftdi_usb_find_all and CBUS mode
* Fixed ftdi_list_free
* Small cosmetic changes
New in 0.9 - 2007-02-09
-----------------------
* Fixed build without doxygen
* Correct .so file library version
New in 0.8 - 2007-02-08
-----------------------
* Complete doxygen documentation and examples
* Extended FT2232C bitbang mode example code (Max)
* ftdi_usb_get_strings function to get device ID strings (Matthijs ten Berge)
* Fix ftdi_read_pins on PowerPC systems (Thomas Fischl)
* Automatically detach ftdi_sio kernel driver (Uwe Bonnes and Intra2net)
* Configurable flow control (Lorenz Moesenlechner and Matthias Kranz)
New in 0.7 - 2005-10-11
-----------------------
* Baudrate calculation fix for FT2232C (Steven Turner/FTDI)
* Find all devices by vendor/product id (Tim Ansell and Intra2net)
* Documentation updates (Tim Ansell)
New in 0.6 - 2005-04-24
-----------------------
* Set library version on .so file again
* Configurable serial line parameters (Alain Abbas)
* Improved filtering of status bytes (Evgeny Sinelnikov)
* Extended FT2232C support (Uwe Bonnes)
* Small improvement to the baudrate calculation code (Emil)
* Error handling cleanup (Rogier Wolff and Intra2net)
New in 0.5 - 2004-09-24
-----------------------
* New autoconf suite
* pkgconfig support
* Status byte filtering now works for "big" readbuffer sizes (Thanks Evgeny!)
* Open device by description and/or serial (Evgeny Sinelnikov)
* Improved error handling (Evgeny Sinelnikov)
New in 0.4 - 2004-06-15
-----------------------
* Fixed filtering of status bytes (Readbuffer size is now 64 bytes)
* FT2232C support (Steven Turner/FTDI)
* New baudrate calculation code (Ian Abbott)
* Automatic detection of chip type
* Important: ftdi_write_data now returns the bytes written
* Fixed defaults values in ftdi_eeprom_initdefaults (Jean-Daniel Merkli)
* Reset internal readbuffer offsets for reset()/purge_buffers()
* Small typo fixes (Mark Haemmerling)
New in 0.3 - 2004-03-25
-----------------------
* Improved read function which takes arbitrary input buffer sizes
Attention: Call ftdi_deinit() on exit to free used memory
* Vastly increased read/write performance (configurable chunksize, default is 4096)
* Set/get latency timer function working (Thanks Steven Turner/FTDI)
* Increased library version because the changes require recompilation
New in 0.2 - 2004-01-03
-----------------------
* EEPROM build fix by Daniel Kirkham (Melbourne, Australia)
* Implemented basic ftdi_read_data() function
* EEPROM write fixes
New in 0.1 - 2003-06-10
-----------------------
* First public release
+38
View File
@@ -0,0 +1,38 @@
# - Try to find the freetype library
# Once done this defines
#
# LIBUSB_FOUND - system has libusb
# LIBUSB_INCLUDE_DIR - the libusb include directory
# LIBUSB_LIBRARIES - Link these to use libusb
# Copyright (c) 2006, 2008 Laurent Montel, <montel@kde.org>
#
# Redistribution and use is allowed according to the terms of the BSD license.
# For details see the accompanying COPYING-CMAKE-SCRIPTS file.
if (LIBUSB_INCLUDE_DIR AND LIBUSB_LIBRARIES)
# in cache already
set(LIBUSB_FOUND TRUE)
else (LIBUSB_INCLUDE_DIR AND LIBUSB_LIBRARIES)
IF (NOT WIN32)
# use pkg-config to get the directories and then use these values
# in the FIND_PATH() and FIND_LIBRARY() calls
find_package(PkgConfig)
pkg_check_modules(PC_LIBUSB libusb-1.0)
ENDIF(NOT WIN32)
FIND_PATH(LIBUSB_INCLUDE_DIR libusb.h
PATHS ${PC_LIBUSB_INCLUDEDIR} ${PC_LIBUSB_INCLUDE_DIRS})
FIND_LIBRARY(LIBUSB_LIBRARIES NAMES usb-1.0
PATHS ${PC_LIBUSB_LIBDIR} ${PC_LIBUSB_LIBRARY_DIRS})
include(FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(LIBUSB DEFAULT_MSG LIBUSB_LIBRARIES LIBUSB_INCLUDE_DIR)
MARK_AS_ADVANCED(LIBUSB_INCLUDE_DIR LIBUSB_LIBRARIES)
endif (LIBUSB_INCLUDE_DIR AND LIBUSB_LIBRARIES)
+25
View File
@@ -0,0 +1,25 @@
The C library "libftdi1" is distributed under the
GNU Library General Public License version 2.
A copy of the GNU Library General Public License (LGPL) is included
in this distribution, in the file COPYING.LIB.
----------------------------------------------------------------------
The C++ wrapper "ftdipp1" is distributed under the GNU General
Public License version 2 (with a special exception described below).
A copy of the GNU General Public License (GPL) is included
in this distribution, in the file COPYING.GPL.
As a special exception, if other files instantiate templates or use macros
or inline functions from this file, or you compile this file and link it
with other works to produce a work based on this file, this file
does not by itself cause the resulting work to be covered
by the GNU General Public License.
However the source code for this file must still be made available
in accordance with section (3) of the GNU General Public License.
This exception does not invalidate any other reasons why a work based
on this file might be covered by the GNU General Public License.
+52
View File
@@ -0,0 +1,52 @@
--------------------------------------------------------------------
libftdi version 1.4
--------------------------------------------------------------------
libftdi - A library (using libusb) to talk to FTDI's UART/FIFO chips
including the popular bitbang mode.
The following chips are supported:
* FT230X
- FT4232H / FT2232H
- FT232R / FT245R
- FT2232L / FT2232D / FT2232C
- FT232BM / FT245BM (and the BL/BQ variants)
- FT8U232AM / FT8U245AM
libftdi requires libusb 1.x.
The AUTHORS file contains a list of all the people
that made libftdi possible what it is today.
Changes
-------
* New ftdi_usb_open_bus_addr() open function
* Use BM/R series baud rate computation for FT230X
* ftdi_get_error_string() now returns const char*
* C++ API: Ability to open devices with empty descriptor strings
* C++ API: Fix enumerations for buffer purge and modem controls
* small build fixes and improvements in the python examples
* ftdi_eeprom / eeprom handling:
* New API function: ftdi_eeprom_get_strings()
* Fix USE_SERIAL handling for 230X type chips
* Make ftdi_read_eeprom_location() endianness independent
* Fix flashing of FT245R
You'll find the newest version of libftdi at:
https://www.intra2net.com/en/developer/libftdi
Quick start
-----------
mkdir build
cd build
cmake -DCMAKE_INSTALL_PREFIX="/usr" ../
make
make install
More verbose build instructions are in "README.build"
--------------------------------------------------------------------
www.intra2net.com 2003-2017 Intra2net AG
--------------------------------------------------------------------
+96
View File
@@ -0,0 +1,96 @@
Here is a short tutorial on how to build libftdi git under
Ubuntu 12.10, But it is similar on other Linux distros.
1) Install the build tools
sudo apt-get install build-essential (yum install make automake gcc gcc-c++ kernel-devel)
sudo apt-get install git-core (yum install git)
sudo apt-get install cmake (yum install cmake)
sudo apt-get install doxygen (for building documentations) (yum install doxygen)
2) Install dependencies
sudo apt-get install libusb-1.0-devel (yum install libusb-devel)
(if the system comes with older version like 1.0.8 or
earlier, it is recommended you build libusbx-1.0.14 or later).
sudo apt-get install libconfuse-dev (for ftdi-eeprom) (yum install libconfuse-devel)
sudo apt-get install swig python-dev (for python bindings) (yum install swig python-devel)
sudo apt-get install libboost-all-dev (for C++ binding and unit test) (yum install boost-devel)
3) Clone the git repository
mkdir libftdi
cd libftdi
git clone git://developer.intra2net.com/libftdi
If you are building the release tar ball, just extract the source
tar ball.
4) Build the git source and install
cd libftdi
mkdir build
cd build
cmake -DCMAKE_INSTALL_PREFIX="/usr" ../
make
sudo make install
5) carry out some tests
cd examples
mcuee@Ubuntu1210VM:~/Desktop/build/libftdi/libftdi/build/examples$
./find_all_pp -v 0x0403 -p 0x6001
Found devices ( VID: 0x403, PID: 0x6001 )
------------------------------------------------
FTDI (0x8730800): ftdi, usb serial converter, ftDEH51S (Open OK)
FTDI (0x8730918): FTDI, FT232R USB UART, A8007Ub5 (Open OK)
mcuee@Ubuntu1210VM:~/Desktop/build/libftdi/libftdi/build/examples$ ./eeprom
2 FTDI devices found: Only Readout on EEPROM done. Use
VID/PID/desc/serial to select device
Decoded values of device 1:
Chip type 1 ftdi_eeprom_size: 128
0x000: 00 00 03 04 01 60 00 04 a0 16 08 00 10 01 94 0a .....`.. ........
0x010: 9e 2a c8 12 0a 03 66 00 74 00 64 00 69 00 2a 03 .*....f. t.d.i.*.
0x020: 75 00 73 00 62 00 20 00 73 00 65 00 72 00 69 00 u.s.b. . s.e.r.i.
0x030: 61 00 6c 00 20 00 63 00 6f 00 6e 00 76 00 65 00 a.l. .c. o.n.v.e.
0x040: 72 00 74 00 65 00 72 00 12 03 66 00 74 00 44 00 r.t.e.r. ..f.t.D.
0x050: 45 00 48 00 35 00 31 00 53 00 02 03 00 00 00 00 E.H.5.1. S.......
0x060: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ........ ........
0x070: 00 00 00 00 00 00 00 00 00 00 00 00 01 00 16 02 ........ ........
VID: 0x0403
PID: 0x6001
Release: 0x0400
Bus Powered: 44 mA USB Remote Wake Up
Manufacturer: ftdi
Product: usb serial converter
Serial: ftDEH51S
Checksum : 0216
Enable Remote Wake Up
PNP: 1
Decoded values of device 2:
Chip type 3 ftdi_eeprom_size: 128
0x000: 00 40 03 04 01 60 00 00 a0 2d 08 00 00 00 98 0a .@...`.. .-......
0x010: a2 20 c2 12 23 10 05 00 0a 03 46 00 54 00 44 00 . ..#... ..F.T.D.
0x020: 49 00 20 03 46 00 54 00 32 00 33 00 32 00 52 00 I. .F.T. 2.3.2.R.
0x030: 20 00 55 00 53 00 42 00 20 00 55 00 41 00 52 00 .U.S.B. .U.A.R.
0x040: 54 00 12 03 41 00 38 00 30 00 30 00 37 00 55 00 T...A.8. 0.0.7.U.
0x050: 62 00 35 00 c9 bf 1c 80 00 00 00 00 00 00 00 00 b.5..... ........
0x060: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ........ ........
0x070: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0f 23 ........ .......#
0x080: 2c 04 d3 fb 00 00 c9 bf 1c 80 42 00 00 00 00 00 ,....... ..B.....
0x090: 00 00 00 00 00 00 00 00 38 41 32 52 4a 33 47 4f ........ 8A2RJ3GO
VID: 0x0403
PID: 0x6001
Release: 0x0000
Bus Powered: 90 mA USB Remote Wake Up
Manufacturer: FTDI
Product: FT232R USB UART
Serial: A8007Ub5
Checksum : 230f
Internal EEPROM
Enable Remote Wake Up
PNP: 1
Channel A has Mode UART VCP
C0 Function: TXLED
C1 Function: RXLED
C2 Function: TXDEN
C3 Function: PWREN
C4 Function: SLEEP
+38
View File
@@ -0,0 +1,38 @@
* How to cross compile libftdi-1.x for Windows? *
1 - Prepare a pkg-config wrapper according to
https://www.flameeyes.eu/autotools-mythbuster/pkgconfig/cross-compiling.html ,
additionally export PKG_CONFIG_ALLOW_SYSTEM_CFLAGS and
PKG_CONFIG_ALLOW_SYSTEM_LIBS.
2 - Write a CMake toolchain file according to
http://www.vtk.org/Wiki/CmakeMingw . Change the path to your future sysroot.
3 - Get libusb sources (either by cloning the git repo or by downloading a
tarball). Unpack, autogen.sh (when building from git), and configure like this:
./configure --build=`./config.guess` --host=i686-w64-mingw32 \
--prefix=/usr --with-sysroot=$HOME/i686-w64-mingw32-root/
4 - run
make install DESTDIR=$HOME/i686-w64-mingw32-root/
5 - go to libftdi-1.x source directory and run
cmake -DCMAKE_TOOLCHAIN_FILE=~/Toolchain-mingw.cmake \
-DCMAKE_INSTALL_PREFIX="/usr" \
-DPKG_CONFIG_EXECUTABLE=`which i686-w64-mingw32-pkg-config`
6 - run
make install DESTDIR=$HOME/i686-w64-mingw32-root/
* How to run libftdi 1.x under Windows *
On 26-Jan-2014, libusbx and libusb project were merged with the release
of libusb-1.0.18 and now the project is called libusb.
libusb Windows backend will need to rely on a proper driver to run.
Please refer to the following wiki page for proper driver installation.
https://github.com/libusb/libusb/wiki/Windows#wiki-How_to_use_libusb_on_Windows
As of 26-Jan-2014, libusb Windows backend supports WinUSB,
libusb0.sys and libusbk.sys driver. However, libusb's support of
libusb0.sys and libusbk.sys is considered to be less mature than
WinUSB. Therefore, WinUSB driver installation using Zadig
is recommended.
Take note once you replace the original FTDI driver with WinUSB driver,
you can no longer use the functionality the original FTDI driver provides
(eg. Virtual Serial Port or D2XX).
+3
View File
@@ -0,0 +1,3 @@
*** TODO for 1.0 release ***
Documentation:
- Document the new EEPROM function
+74
View File
@@ -0,0 +1,74 @@
# libConfuse is a configuration file parser library
# available at http://www.nongnu.org/confuse/
#
# The module defines the following variables:
# CONFUSE_FOUND - the system has Confuse
# CONFUSE_INCLUDE_DIR - where to find confuse.h
# CONFUSE_INCLUDE_DIRS - confuse includes
# CONFUSE_LIBRARY - where to find the Confuse library
# CONFUSE_LIBRARIES - aditional libraries
# CONFUSE_ROOT_DIR - root dir (ex. /usr/local)
#=============================================================================
# Copyright 2010-2013, Julien Schueller
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice, this
# list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
# The views and conclusions contained in the software and documentation are those
# of the authors and should not be interpreted as representing official policies,
# either expressed or implied, of the FreeBSD Project.
#=============================================================================
find_path ( CONFUSE_INCLUDE_DIR
NAMES confuse.h
)
set ( CONFUSE_INCLUDE_DIRS ${CONFUSE_INCLUDE_DIR} )
find_library ( CONFUSE_LIBRARY
NAMES confuse
)
set ( CONFUSE_LIBRARIES ${CONFUSE_LIBRARY} )
# try to guess root dir from include dir
if ( CONFUSE_INCLUDE_DIR )
string ( REGEX REPLACE "(.*)/include.*" "\\1" CONFUSE_ROOT_DIR ${CONFUSE_INCLUDE_DIR} )
# try to guess root dir from library dir
elseif ( CONFUSE_LIBRARY )
string ( REGEX REPLACE "(.*)/lib[/|32|64].*" "\\1" CONFUSE_ROOT_DIR ${CONFUSE_LIBRARY} )
endif ()
# handle the QUIETLY and REQUIRED arguments
include ( FindPackageHandleStandardArgs )
find_package_handle_standard_args( Confuse DEFAULT_MSG CONFUSE_LIBRARY CONFUSE_INCLUDE_DIR )
mark_as_advanced (
CONFUSE_LIBRARY
CONFUSE_LIBRARIES
CONFUSE_INCLUDE_DIR
CONFUSE_INCLUDE_DIRS
CONFUSE_ROOT_DIR
)
+47
View File
@@ -0,0 +1,47 @@
# Try to find Libintl functionality
# Once done this will define
#
# LIBINTL_FOUND - system has Libintl
# LIBINTL_INCLUDE_DIR - Libintl include directory
# LIBINTL_LIBRARIES - Libraries needed to use Libintl
#
# TODO: This will enable translations only if Gettext functionality is
# present in libc. Must have more robust system for release, where Gettext
# functionality can also reside in standalone Gettext library, or the one
# embedded within kdelibs (cf. gettext.m4 from Gettext source).
# Copyright (c) 2006, Chusslove Illich, <caslav.ilic@gmx.net>
# Copyright (c) 2007, Alexander Neundorf, <neundorf@kde.org>
#
# Redistribution and use is allowed according to the terms of the BSD license.
# For details see the accompanying COPYING-CMAKE-SCRIPTS file.
if(LIBINTL_INCLUDE_DIR AND LIBINTL_LIB_FOUND)
set(Libintl_FIND_QUIETLY TRUE)
endif(LIBINTL_INCLUDE_DIR AND LIBINTL_LIB_FOUND)
find_path(LIBINTL_INCLUDE_DIR libintl.h)
set(LIBINTL_LIB_FOUND FALSE)
if(LIBINTL_INCLUDE_DIR)
include(CheckFunctionExists)
check_function_exists(dgettext LIBINTL_LIBC_HAS_DGETTEXT)
if (LIBINTL_LIBC_HAS_DGETTEXT)
set(LIBINTL_LIBRARIES)
set(LIBINTL_LIB_FOUND TRUE)
else (LIBINTL_LIBC_HAS_DGETTEXT)
find_library(LIBINTL_LIBRARIES NAMES intl libintl )
if(LIBINTL_LIBRARIES)
set(LIBINTL_LIB_FOUND TRUE)
endif(LIBINTL_LIBRARIES)
endif (LIBINTL_LIBC_HAS_DGETTEXT)
endif(LIBINTL_INCLUDE_DIR)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(Libintl DEFAULT_MSG LIBINTL_INCLUDE_DIR LIBINTL_LIB_FOUND)
mark_as_advanced(LIBINTL_INCLUDE_DIR LIBINTL_LIBRARIES LIBINTL_LIBC_HAS_DGETTEXT LIBINTL_LIB_FOUND)
+37
View File
@@ -0,0 +1,37 @@
# - Try to find the freetype library
# Once done this defines
#
# LIBUSB_FOUND - system has libusb
# LIBUSB_INCLUDE_DIR - the libusb include directory
# LIBUSB_LIBRARIES - Link these to use libusb
# Copyright (c) 2006, 2008 Laurent Montel, <montel@kde.org>
#
# Redistribution and use is allowed according to the terms of the BSD license.
# For details see the accompanying COPYING-CMAKE-SCRIPTS file.
if (LIBUSB_INCLUDE_DIR AND LIBUSB_LIBRARIES)
# in cache already
set(LIBUSB_FOUND TRUE)
else (LIBUSB_INCLUDE_DIR AND LIBUSB_LIBRARIES)
# use pkg-config to get the directories and then use these values
# in the FIND_PATH() and FIND_LIBRARY() calls
find_package(PkgConfig)
pkg_check_modules(PC_LIBUSB libusb-1.0)
FIND_PATH(LIBUSB_INCLUDE_DIR libusb.h
PATH_SUFFIXES libusb-1.0
PATHS ${PC_LIBUSB_INCLUDEDIR} ${PC_LIBUSB_INCLUDE_DIRS})
FIND_LIBRARY(LIBUSB_LIBRARIES NAMES usb-1.0
PATHS ${PC_LIBUSB_LIBDIR} ${PC_LIBUSB_LIBRARY_DIRS})
include(FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(LIBUSB DEFAULT_MSG LIBUSB_LIBRARIES LIBUSB_INCLUDE_DIR)
MARK_AS_ADVANCED(LIBUSB_INCLUDE_DIR LIBUSB_LIBRARIES)
endif (LIBUSB_INCLUDE_DIR AND LIBUSB_LIBRARIES)
+53
View File
@@ -0,0 +1,53 @@
# -*- cmake -*-
#
# LibFTDI1Config.cmake(.in)
#
# Copyright (C) 2013 Intra2net AG and the libftdi developers
#
# This file is part of LibFTDI.
#
# LibFTDI is free software; you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License
# version 2.1 as published by the Free Software Foundation;
#
# Use the following variables to compile and link against LibFTDI:
# LIBFTDI_FOUND - True if LibFTDI was found on your system
# LIBFTDI_USE_FILE - The file making LibFTDI usable
# LIBFTDI_DEFINITIONS - Definitions needed to build with LibFTDI
# LIBFTDI_INCLUDE_DIRS - Directory where ftdi.h can be found
# LIBFTDI_INCLUDE_DIRS - List of directories of LibFTDI and it's dependencies
# LIBFTDI_LIBRARY - LibFTDI library location
# LIBFTDI_LIBRARIES - List of libraries to link against LibFTDI library
# LIBFTDIPP_LIBRARY - LibFTDI C++ wrapper library location
# LIBFTDIPP_LIBRARIES - List of libraries to link against LibFTDI C++ wrapper
# LIBFTDI_LIBRARY_DIRS - List of directories containing LibFTDI' libraries
# LIBFTDI_ROOT_DIR - The base directory of LibFTDI
# LIBFTDI_VERSION_STRING - A human-readable string containing the version
# LIBFTDI_VERSION_MAJOR - The major version of LibFTDI
# LIBFTDI_VERSION_MINOR - The minor version of LibFTDI
# LIBFTDI_VERSION_PATCH - The patch version of LibFTDI
# LIBFTDI_PYTHON_MODULE_PATH - Path to the python module
set ( LIBFTDI_FOUND 1 )
set ( LIBFTDI_USE_FILE "@LIBFTDI_USE_FILE@" )
set ( LIBFTDI_DEFINITIONS "@LIBFTDI_DEFINITIONS@" )
set ( LIBFTDI_INCLUDE_DIR "@LIBFTDI_INCLUDE_DIR@" )
set ( LIBFTDI_INCLUDE_DIRS "@LIBFTDI_INCLUDE_DIRS@" )
set ( LIBFTDI_LIBRARY "@LIBFTDI_LIBRARY@" )
set ( LIBFTDI_LIBRARIES "@LIBFTDI_LIBRARIES@" )
set ( LIBFTDI_STATIC_LIBRARY "@LIBFTDI_STATIC_LIBRARY@" )
set ( LIBFTDI_STATIC_LIBRARIES "@LIBFTDI_STATIC_LIBRARIES@" )
set ( LIBFTDIPP_LIBRARY "@LIBFTDIPP_LIBRARY@" )
set ( LIBFTDIPP_LIBRARIES "@LIBFTDIPP_LIBRARIES@" )
set ( LIBFTDI_LIBRARY_DIRS "@LIBFTDI_LIBRARY_DIRS@" )
set ( LIBFTDI_ROOT_DIR "@LIBFTDI_ROOT_DIR@" )
set ( LIBFTDI_VERSION_STRING "@LIBFTDI_VERSION_STRING@" )
set ( LIBFTDI_VERSION_MAJOR "@LIBFTDI_VERSION_MAJOR@" )
set ( LIBFTDI_VERSION_MINOR "@LIBFTDI_VERSION_MINOR@" )
set ( LIBFTDI_VERSION_PATCH "@LIBFTDI_VERSION_PATCH@" )
set ( LIBFTDI_PYTHON_MODULE_PATH "@LIBFTDI_PYTHON_MODULE_PATH@" )
@@ -0,0 +1,31 @@
# This is a basic version file for the Config-mode of find_package().
# It is used by write_basic_package_version_file() as input file for configure_file()
# to create a version-file which can be installed along a config.cmake file.
#
# The created file sets PACKAGE_VERSION_EXACT if the current version string and
# the requested version string are exactly the same and it sets
# PACKAGE_VERSION_COMPATIBLE if the current version is >= requested version.
# The variable CVF_VERSION must be set before calling configure_file().
set(PACKAGE_VERSION "@LIBFTDI_VERSION_STRING@")
if("${PACKAGE_VERSION}" VERSION_LESS "${PACKAGE_FIND_VERSION}" )
set(PACKAGE_VERSION_COMPATIBLE FALSE)
else()
set(PACKAGE_VERSION_COMPATIBLE TRUE)
if( "${PACKAGE_FIND_VERSION}" STREQUAL "${PACKAGE_VERSION}")
set(PACKAGE_VERSION_EXACT TRUE)
endif()
endif()
# if the installed or the using project don't have CMAKE_SIZEOF_VOID_P set, ignore it:
if("${CMAKE_SIZEOF_VOID_P}" STREQUAL "" OR "8" STREQUAL "")
return()
endif()
# check that the installed version has the same 32/64bit-ness as the one which is currently searching:
if(NOT "${CMAKE_SIZEOF_VOID_P}" STREQUAL "8")
math(EXPR installedBits "8 * 8")
set(PACKAGE_VERSION "${PACKAGE_VERSION} (${installedBits}bit)")
set(PACKAGE_VERSION_UNSUITABLE TRUE)
endif()
@@ -0,0 +1,4 @@
set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_C_COMPILER gcc -m32)
set(CMAKE_CXX_COMPILER g++ -m32)
set(CMAKE_FIND_ROOT_PATH /usr/lib)
@@ -0,0 +1,17 @@
# the name of the target operating system
SET(CMAKE_SYSTEM_NAME Windows)
# which compilers to use for C and C++
SET(CMAKE_C_COMPILER i686-w64-mingw32-gcc)
SET(CMAKE_CXX_COMPILER i686-w64-mingw32-g++)
# here is the target environment located
SET(CMAKE_FIND_ROOT_PATH /usr/i686-w64-mingw32 )
# adjust the default behaviour of the FIND_XXX() commands:
# search headers and libraries in the target environment, search
# programs in the host environment
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
set (CMAKE_RC_COMPILER i686-w64-mingw32-windres)
+16
View File
@@ -0,0 +1,16 @@
# the name of the target operating system
SET(CMAKE_SYSTEM_NAME Windows)
# which compilers to use for C and C++
SET(CMAKE_C_COMPILER i386-mingw32msvc-gcc)
SET(CMAKE_CXX_COMPILER i386-mingw32msvc-g++)
# here is the target environment located
SET(CMAKE_FIND_ROOT_PATH /opt/cross/i386-mingw32msvc )
# adjust the default behaviour of the FIND_XXX() commands:
# search headers and libraries in the target environment, search
# programs in the host environment
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
@@ -0,0 +1,17 @@
# the name of the target operating system
SET(CMAKE_SYSTEM_NAME Windows)
# which compilers to use for C and C++
SET(CMAKE_C_COMPILER x86_64-w64-mingw32-gcc)
SET(CMAKE_CXX_COMPILER x86_64-w64-mingw32-g++)
# here is the target environment located
SET(CMAKE_FIND_ROOT_PATH /usr/x86_64-w64-mingw32 )
# adjust the default behaviour of the FIND_XXX() commands:
# search headers and libraries in the target environment, search
# programs in the host environment
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
set (CMAKE_RC_COMPILER x86_64-w64-mingw32-windres)
+18
View File
@@ -0,0 +1,18 @@
# -*- cmake -*-
#
# UseLibFTDI.cmake
#
# Copyright (C) 2013 Intra2net AG and the libftdi developers
#
# This file is part of LibFTDI.
#
# LibFTDI is free software; you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License
# version 2.1 as published by the Free Software Foundation;
#
add_definitions ( ${LIBFTDI_DEFINITIONS} )
include_directories ( ${LIBFTDI_INCLUDE_DIRS} )
link_directories ( ${LIBFTDI_LIBRARY_DIRS} )
File diff suppressed because it is too large Load Diff
+26
View File
@@ -0,0 +1,26 @@
# Doxyfile 1.7.4
# xml generation only
# keep settings but shut off all other generation
@INCLUDE = Doxyfile
GENERATE_TODOLIST = NO
GENERATE_TESTLIST = NO
GENERATE_BUGLIST = NO
GENERATE_DEPRECATEDLIST= NO
GENERATE_HTML = NO
GENERATE_DOCSET = NO
GENERATE_HTMLHELP = NO
GENERATE_CHI = NO
GENERATE_QHP = NO
GENERATE_ECLIPSEHELP = NO
GENERATE_TREEVIEW = NO
GENERATE_LATEX = NO
GENERATE_RTF = NO
GENERATE_MAN = NO
GENERATE_AUTOGEN_DEF = NO
GENERATE_PERLMOD = NO
GENERATE_TAGFILE =
GENERATE_LEGEND = NO
GENERATE_XML = YES
+110
View File
@@ -0,0 +1,110 @@
Here we try to document what we know about the EEPROM Structure.
Even with a 93xx66 EEPROM, at maximum 256 Bytes are used
All important things happen in the first
0x14(FT232/245), 0x16(FT2232CD), 0x18(FT232/245R) or 0x1a (FT2232H/4432H) bytes
Type | Use extra EEPROM space
FT2XXB | No
Byte.BIT| TYPE_AM TYPE_BM TYPE_2232C TYPE_R TYPE_2232H TYPE_4232H
00.0 | 0 0 channel_a_type 232R/245R channel_a_type 0
00.1 | 0 0 channel_a_type channel_a_type 0
00.2 | 0 0 channel_a_type high_current channel_a_type 0
00.3 | 0 0 channel_a_driver channel_a_driver channel_a_driver channel_a_driver
00.4 | 0 0 high_current_a 0 0 0
00.5 | 0 0 0 0 0 0
00.6 | 0 0 0 0 0 0
00.7 | 0 0 0 0 SUSPEND_DBUS7 channel_c_driver
On TYPE_R 00.0 is set for the FT245R and cleared for the FT232R
On TYPE_R 00.3 set mean D2XX, on other devices VCP
01.0 | 0 0 channel_b_type channel_b_type 0
01.1 | 0 0 channel_b_type channel_b_type 0
01.2 | 0 0 channel_b_type 0 channel_b_type 0
01.3 | 0 0 channel_b_driver 0 channel_b_driver channel_b_driver
01.4 | 0 0 high_current_b 0 0 0
01.5 | 0 0 0 0 0 0
01.6 | 0 0 0 0 0
01.7 | 0 0 0 0 0 channel_d_driver
Fixme: Missing 4232H validation
02 | Vendor ID (VID) LSB (all)
03 | Vendor ID (VID) MSB (all)
04 | Product ID (PID) LSB (all)
05 | Product ID (PID) MSB (all)
06 | Device release number LSB (not tested on TYPE_4232H)
07 | Device release number MSB (not tested on TYPE_4232H)
|
08.4 | Battery powered
08.5 | Remote wakeup
08.6 | Self powered: 1, bus powered: 0
08.7 | Always 1
|
09 | Max power (mA/2)
|
Byte.BIT| TYPE_AM TYPE_BM TYPE_2232C TYPE_R TYPE_2232H TYPE_4232H
0a.0 | 0 IsoIn IsoIn part A 0 0 0
0a.1 | 0 IsoOut IsoOut part A 0 0 0
0a.2 | 0 suspend_pull_down suspend_pull_down suspend_pull_down suspend_pull_down
0a.3 | 0 use_serial use_serial use_serial
0a.4 | 0 change_usb_version change_usb_version
0a.5 | 0 0 IsoIn part B 0 0 0
0a.6 | 0 0 IsoOut part B 0 0 0
0a.7 | 0 - reserved
0b | TYPE_R Bitmask Invert, 0 else
Byte.BIT| TYPE_4232H
0b.4 | channel_a_rs485enable
0b.5 | channel_b_rs485enable
0b.6 | channel_c_rs485enable
0b.7 | channel_d_rs485enable
Byte | TYPE_AM TYPE_BM TYPE_2232C TYPE_R TYPE_2232H TYPE_4232H
0c | 0 USB-VER-LSB USB-VER-LSB 0 ? ?
0d | 0 USB-VER-MSB USB-VER-MSB 0 ? ?
(On several FT2232H different values were observed -> The value is unused
if change USB version is not set, so it might contain garbage)
0e | OFFSET Vendor
0f | Len VENDOR
10 | Offset Product
11 | Length Product
12 | Offset Serial
13 | Length Serial
Byte.BIT| TYPE_AM TYPE_BM TYPE_2232C TYPE_R TYPE_2232H TYPE_4232H
14.3:0 | UA UA CHIP CBUS[0] AL A
14.7:0 | UA UA CHIP CBUS[1] AH B
15.3:0 | UA UA 0 CBUS[2] BL C
15.7:0 | UA UA 0 CBUS[3] BH D
16.3:0 | UA UA UA CBUS[4] 0 0
16.7:0 | UA UA UA 0 0 0
CHIP values:
0x46: EEPROM is a 93xx46
0x56: EEPROM is a 93xx56
0x66: EEPROM is a 93xx66
17 UA UA UA 0 0 0
18 UA UA UA VENDOR CHIP CHIP
19 UA UA UA VENDOR 0 0
1a UA (all)
Additional fields after the serial string:
0x00, 0x00 - reserved for "legacy port name prefix"
0x00, 0x00 - reserved for plug and play options
(Observed values with PnP == 0:
0x02 0x03 0x01 0x00)
Note: The additional fields after the serial number string
collide with the official FTDI formula from AN_121 regarding
the start of the user area:
"Start Address = the address following the last byte of SerialNumber string."
+5
View File
@@ -0,0 +1,5 @@
#!/bin/sh
# Astyle settings used to format our source code
/usr/bin/astyle --indent=spaces=4 --indent-switches --brackets=break \
--convert-tabs --keep-one-line-statements --keep-one-line-blocks \
$*
+29
View File
@@ -0,0 +1,29 @@
*** Checklist for a new libftdi release ***
- Update ChangeLog and AUTHORS via git history
(git log --oneline latest_release..HEAD)
- Update version number in the following files:
- CMakeLists.txt
- README
- Run "make dist"
- Diff tarball to previous version, check if all
important changes are in the ChangeLog
- Ensure all modifications are checked in
- Sign tarball, build .src.rpm and sign it, too
- Create git tag:
- git tag -s -u 24F006F5 v1.XX
- git tag -d latest_release ; git tag latest_release
- git push --tags
- Website
- Upload tarball and .src.rpm
- Add ChangeLog to main page
- Update URLs in download section
- Generate API documentation and upload it
- Announce on mailinglist
+55
View File
@@ -0,0 +1,55 @@
option(EXAMPLES "Build example programs" ON)
if (EXAMPLES)
# Includes
include_directories( ${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_BINARY_DIR}
)
message(STATUS "Building example programs.")
# Targets
add_executable(simple simple.c)
add_executable(bitbang bitbang.c)
add_executable(bitbang2 bitbang2.c)
add_executable(bitbang_cbus bitbang_cbus.c)
add_executable(bitbang_ft2232 bitbang_ft2232.c)
add_executable(find_all find_all.c)
add_executable(serial_test serial_test.c)
add_executable(baud_test baud_test.c)
add_executable(stream_test stream_test.c)
add_executable(eeprom eeprom.c)
# Linkage
target_link_libraries(simple ftdi1)
target_link_libraries(bitbang ftdi1)
target_link_libraries(bitbang2 ftdi1)
target_link_libraries(bitbang_cbus ftdi1)
target_link_libraries(bitbang_ft2232 ftdi1)
target_link_libraries(find_all ftdi1)
target_link_libraries(serial_test ftdi1)
target_link_libraries(baud_test ftdi1)
target_link_libraries(stream_test ftdi1)
target_link_libraries(eeprom ftdi1)
# libftdi++ examples
if(FTDI_BUILD_CPP)
if(Boost_FOUND)
message(STATUS "Building libftdi++ examples.")
include_directories(BEFORE ${CMAKE_SOURCE_DIR}/ftdipp
${Boost_INCLUDE_DIRS})
# Target
add_executable(find_all_pp find_all_pp.cpp)
# Linkage
target_link_libraries(find_all_pp ftdipp1)
endif(Boost_FOUND)
endif(FTDI_BUILD_CPP)
# Source includes
include_directories(BEFORE ${CMAKE_SOURCE_DIR}/src)
else(EXAMPLES)
message(STATUS "Not building example programs.")
endif(EXAMPLES)
+224
View File
@@ -0,0 +1,224 @@
/* baud_test.c
*
* test setting the baudrate and compare it with the expected runtime
*
* options:
* -p <devicestring> defaults to "i:0x0403:0x6001" (this is the first FT232R with default id)
* d:<devicenode> path of bus and device-node (e.g. "003/001") within usb device tree (usually at /proc/bus/usb/)
* i:<vendor>:<product> first device with given vendor and product id,
* ids can be decimal, octal (preceded by "0") or hex (preceded by "0x")
* i:<vendor>:<product>:<index> as above with index being the number of the device (starting with 0)
* if there are more than one
* s:<vendor>:<product>:<serial> first device with given vendor id, product id and serial string
* -d <datasize to send in bytes>
* -b <baudrate> (divides by 16 if bitbang as taken from the ftdi datasheets)
* -m <mode to use> r: serial a: async bitbang s:sync bitbang
* -c <chunksize>
*
* (C) 2009 by Gerd v. Egidy <gerd.von.egidy@intra2net.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#include <sys/time.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <ftdi.h>
double get_prec_time()
{
struct timeval tv;
double res;
gettimeofday(&tv,NULL);
res=tv.tv_sec;
res+=((double)tv.tv_usec/1000000);
return res;
}
int main(int argc, char **argv)
{
struct ftdi_context *ftdi;
int i, t;
unsigned char *txbuf;
unsigned char *rxbuf;
double start, duration, plan;
int retval= 0;
// default values
int baud=9600;
int set_baud;
int datasize=100000;
char default_devicedesc[] = "i:0x0403:0x6001";
char *devicedesc=default_devicedesc;
int txchunksize=256;
enum ftdi_mpsse_mode test_mode=BITMODE_BITBANG;
while ((t = getopt (argc, argv, "b:d:p:m:c:")) != -1)
{
switch (t)
{
case 'd':
datasize = atoi (optarg);
break;
case 'm':
switch (*optarg)
{
case 'r':
// serial
test_mode=BITMODE_RESET;
break;
case 'a':
// async
test_mode=BITMODE_BITBANG;
break;
case 's':
// sync
test_mode=BITMODE_SYNCBB;
break;
}
break;
case 'b':
baud = atoi (optarg);
break;
case 'p':
devicedesc=optarg;
break;
case 'c':
txchunksize = atoi (optarg);
break;
}
}
txbuf=malloc(txchunksize);
rxbuf=malloc(txchunksize);
if (txbuf == NULL || rxbuf == NULL)
{
fprintf(stderr, "can't malloc\n");
return EXIT_FAILURE;
}
if ((ftdi = ftdi_new()) == 0)
{
fprintf(stderr, "ftdi_new failed\n");
retval = EXIT_FAILURE;
goto done;
}
if (ftdi_usb_open_string(ftdi, devicedesc) < 0)
{
fprintf(stderr,"Can't open ftdi device: %s\n",ftdi_get_error_string(ftdi));
retval = EXIT_FAILURE;
goto do_deinit;
}
set_baud=baud;
if (test_mode!=BITMODE_RESET)
{
// we do bitbang, so real baudrate / 16
set_baud=baud/16;
}
ftdi_set_baudrate(ftdi,set_baud);
printf("real baudrate used: %d\n",(test_mode==BITMODE_RESET) ? ftdi->baudrate : ftdi->baudrate*16);
if (ftdi_set_bitmode(ftdi, 0xFF,test_mode) < 0)
{
fprintf(stderr,"Can't set mode: %s\n",ftdi_get_error_string(ftdi));
retval = EXIT_FAILURE;
goto do_close;
}
if (test_mode==BITMODE_RESET)
{
// serial 8N1: 8 data bits, 1 startbit, 1 stopbit
plan=((double)(datasize*10))/baud;
}
else
{
// bitbang means 8 bits at once
plan=((double)datasize)/baud;
}
printf("this test should take %.2f seconds\n",plan);
// prepare data to send: 0 and 1 bits alternating (except for serial start/stopbit):
// maybe someone wants to look at this with a scope or logic analyzer
for (i=0; i<txchunksize; i++)
{
if (test_mode==BITMODE_RESET)
txbuf[i]=0xAA;
else
txbuf[i]=(i%2) ? 0xff : 0;
}
if (ftdi_write_data_set_chunksize(ftdi, txchunksize) < 0 ||
ftdi_read_data_set_chunksize(ftdi, txchunksize) < 0)
{
fprintf(stderr,"Can't set chunksize: %s\n",ftdi_get_error_string(ftdi));
retval = EXIT_FAILURE;
goto do_close;
}
if (test_mode==BITMODE_SYNCBB)
{
// completely clear the receive buffer before beginning
while (ftdi_read_data(ftdi, rxbuf, txchunksize)>0);
}
start=get_prec_time();
// don't wait for more data to arrive, take what we get and keep on sending
// yes, we really would like to have libusb 1.0+ with async read/write...
ftdi->usb_read_timeout=1;
i=0;
while (i < datasize)
{
int sendsize=txchunksize;
if (i+sendsize > datasize)
sendsize=datasize-i;
if ((sendsize=ftdi_write_data(ftdi, txbuf, sendsize)) < 0)
{
fprintf(stderr,"write failed at %d: %s\n",
i, ftdi_get_error_string(ftdi));
retval = EXIT_FAILURE;
goto do_close;
}
i+=sendsize;
if (test_mode==BITMODE_SYNCBB)
{
// read the same amount of data as sent
ftdi_read_data(ftdi, rxbuf, sendsize);
}
}
duration=get_prec_time()-start;
printf("and took %.4f seconds, this is %.0f baud or factor %.3f\n",duration,(plan*baud)/duration,plan/duration);
do_close:
ftdi_usb_close(ftdi);
do_deinit:
ftdi_free(ftdi);
done:
if(rxbuf)
free(rxbuf);
if(txbuf)
free(txbuf);
exit (retval);
}
+84
View File
@@ -0,0 +1,84 @@
/* This program is distributed under the GPL, version 2 */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <ftdi.h>
int main(int argc, char **argv)
{
struct ftdi_context *ftdi;
int f,i;
unsigned char buf[1];
int retval = 0;
if ((ftdi = ftdi_new()) == 0)
{
fprintf(stderr, "ftdi_new failed\n");
return EXIT_FAILURE;
}
f = ftdi_usb_open(ftdi, 0x0403, 0x6001);
if (f < 0 && f != -5)
{
fprintf(stderr, "unable to open ftdi device: %d (%s)\n", f, ftdi_get_error_string(ftdi));
retval = 1;
goto done;
}
printf("ftdi open succeeded: %d\n",f);
printf("enabling bitbang mode\n");
ftdi_set_bitmode(ftdi, 0xFF, BITMODE_BITBANG);
usleep(3 * 1000000);
buf[0] = 0x0;
printf("turning everything on\n");
f = ftdi_write_data(ftdi, buf, 1);
if (f < 0)
{
fprintf(stderr,"write failed for 0x%x, error %d (%s)\n",buf[0],f, ftdi_get_error_string(ftdi));
}
usleep(3 * 1000000);
buf[0] = 0xFF;
printf("turning everything off\n");
f = ftdi_write_data(ftdi, buf, 1);
if (f < 0)
{
fprintf(stderr,"write failed for 0x%x, error %d (%s)\n",buf[0],f, ftdi_get_error_string(ftdi));
}
usleep(3 * 1000000);
for (i = 0; i < 32; i++)
{
buf[0] = 0 | (0xFF ^ 1 << (i % 8));
if ( i > 0 && (i % 8) == 0)
{
printf("\n");
}
printf("%02hhx ",buf[0]);
fflush(stdout);
f = ftdi_write_data(ftdi, buf, 1);
if (f < 0)
{
fprintf(stderr,"write failed for 0x%x, error %d (%s)\n",buf[0],f, ftdi_get_error_string(ftdi));
}
usleep(1 * 1000000);
}
printf("\n");
printf("disabling bitbang mode\n");
ftdi_disable_bitbang(ftdi);
ftdi_usb_close(ftdi);
done:
ftdi_free(ftdi);
return retval;
}
+89
View File
@@ -0,0 +1,89 @@
/* ftdi_out.c
*
* Output a (stream of) byte(s) in bitbang mode to the
* ftdi245 chip that is (hopefully) attached.
*
* We have a little board that has a FT245BM chip and
* the 8 outputs are connected to several different
* things that we can turn on and off with this program.
*
* If you have an idea about hardware that can easily
* interface onto an FTDI chip, I'd like to collect
* ideas. If I find it worthwhile to make, I'll consider
* making it, I'll even send you a prototype (against
* cost-of-material) if you want.
*
* At "harddisk-recovery.nl" they have a little board that
* controls the power to two harddrives and two fans.
*
* -- REW R.E.Wolff@BitWizard.nl
*
*
*
* This program was based on libftdi_example_bitbang2232.c
* which doesn't carry an author or attribution header.
*
*
* This program is distributed under the GPL, version 2.
* Millions copies of the GPL float around the internet.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <ftdi.h>
void ftdi_fatal (struct ftdi_context *ftdi, char *str)
{
fprintf (stderr, "%s: %s\n",
str, ftdi_get_error_string (ftdi));
ftdi_free(ftdi);
exit (1);
}
int main(int argc, char **argv)
{
struct ftdi_context *ftdi;
int i, t;
unsigned char data;
int delay = 100000; /* 100 thousand microseconds: 1 tenth of a second */
while ((t = getopt (argc, argv, "d:")) != -1)
{
switch (t)
{
case 'd':
delay = atoi (optarg);
break;
}
}
if ((ftdi = ftdi_new()) == 0)
{
fprintf(stderr, "ftdi_bew failed\n");
return EXIT_FAILURE;
}
if (ftdi_usb_open(ftdi, 0x0403, 0x6001) < 0)
ftdi_fatal (ftdi, "Can't open ftdi device");
if (ftdi_set_bitmode(ftdi, 0xFF, BITMODE_BITBANG) < 0)
ftdi_fatal (ftdi, "Can't enable bitbang");
for (i=optind; i < argc ; i++)
{
sscanf (argv[i], "%x", &t);
data = t;
if (ftdi_write_data(ftdi, &data, 1) < 0)
{
fprintf(stderr,"write failed for 0x%x: %s\n",
data, ftdi_get_error_string(ftdi));
}
usleep(delay);
}
ftdi_usb_close(ftdi);
ftdi_free(ftdi);
exit (0);
}
+94
View File
@@ -0,0 +1,94 @@
/* bitbang_cbus.c
Example to use CBUS bitbang mode of newer chipsets.
You must enable CBUS bitbang mode in the EEPROM first.
Thanks to Steve Brown <sbrown@ewol.com> for the
the information how to do it.
The top nibble controls input/output and the bottom nibble
controls the state of the lines set to output. The datasheet isn't clear
what happens if you set a bit in the output register when that line is
conditioned for input. This is described in more detail
in the FT232R bitbang app note.
BITMASK
CBUS Bits
3210 3210
xxxx xxxx
|    |------ Output Control 0->LO, 1->HI
|----------- Input/Output   0->Input, 1->Output
Example:
All pins to output with 0 bit high: 0xF1 (11110001)
Bits 0 and 1 to input, 2 and 3 to output and masked high: 0xCC (11001100)
The input is standard "0x" hex notation.
A carriage return terminates the program.
This program is distributed under the GPL, version 2
*/
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <ftdi.h>
int main(void)
{
struct ftdi_context *ftdi;
int f;
unsigned char buf[1];
unsigned char bitmask;
char input[10];
if ((ftdi = ftdi_new()) == 0)
{
fprintf(stderr, "ftdi_new failed\n");
return EXIT_FAILURE;
}
f = ftdi_usb_open(ftdi, 0x0403, 0x6001);
if (f < 0 && f != -5)
{
fprintf(stderr, "unable to open ftdi device: %d (%s)\n", f, ftdi_get_error_string(ftdi));
ftdi_free(ftdi);
exit(-1);
}
printf("ftdi open succeeded: %d\n",f);
while (1)
{
// Set bitmask from input
fgets(input, sizeof(input) - 1, stdin);
if (input[0] == '\n') break;
bitmask = strtol(input, NULL, 0);
printf("Using bitmask 0x%02x\n", bitmask);
f = ftdi_set_bitmode(ftdi, bitmask, BITMODE_CBUS);
if (f < 0)
{
fprintf(stderr, "set_bitmode failed for 0x%x, error %d (%s)\n", bitmask, f, ftdi_get_error_string(ftdi));
ftdi_usb_close(ftdi);
ftdi_free(ftdi);
exit(-1);
}
// read CBUS
f = ftdi_read_pins(ftdi, &buf[0]);
if (f < 0)
{
fprintf(stderr, "read_pins failed, error %d (%s)\n", f, ftdi_get_error_string(ftdi));
ftdi_usb_close(ftdi);
ftdi_free(ftdi);
exit(-1);
}
printf("Read returned 0x%01x\n", buf[0] & 0x0f);
}
printf("disabling bitbang mode\n");
ftdi_disable_bitbang(ftdi);
ftdi_usb_close(ftdi);
ftdi_free(ftdi);
return 0;
}
@@ -0,0 +1,62 @@
[Basic Details]
Device Type=6
VID PID Type=0
USB VID=0403
USB PID=6001
[USB Power Options]
Bus Powered=1
Self Powered=0
Max Bus Power=44
[USB Serial Number Control]
Prefix=FT
Use Fixed Serial Number=0
Fixed Serial Number=FTDECZJT
[USB Remote WakeUp]
Enable Remote WakeUp=1
[Windows Plug and Play]
Enable Plug and Play=0
[USB String Descriptors]
Manufacturer=FTDI
Product=USB Serial Converter
[Programming Options]
Only Program Blank Devices=0
[BM Device Specific Options]
USB Version Number=1
Disable Serial Number=0
IO Pin Pull Down in Suspend=0
[Dual Device Specific Options A]
RS 232 mode=1
245 FIFO mode=0
245 CPU FIFO mode=0
OPTO Isolate mode=1
High Current Drive=0
[Dual Device Specific Options B]
RS 232 mode=1
245 FIFO mode=0
245 CPU FIFO mode=0
OPTO Isolate mode=0
High Current Drive=0
[Dual Device Driver Options A]
Virtual Com Port Driver=1
D2XX Driver=0
[Dual Device Driver Options B]
Virtual Com Port Driver=1
D2XX Driver=0
[R Device Specific Options]
Invert TXD=0
Invert RXD=0
Invert RTS#=0
Invert CTS#=0
Invert DTR#=0
Invert DSR#=0
Invert DCD#=0
Invert RI#=0
C0 Signal=10
C1 Signal=10
C2 Signal=10
C3 Signal=10
C4 Signal=5
Enable Ext Osc=0
High Current I/O=0
Load D2XX Driver=0
In EndPoint Size=0
+106
View File
@@ -0,0 +1,106 @@
/* bitbang_ft2232.c
Output some flickering in bitbang mode to the FT2232
Thanks to max@koeln.ccc.de for fixing and extending
the example for the second channel.
This program is distributed under the GPL, version 2
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <ftdi.h>
int main(int argc, char **argv)
{
struct ftdi_context *ftdi, *ftdi2;
unsigned char buf[1];
int f,i;
// Init 1. channel
if ((ftdi = ftdi_new()) == 0)
{
fprintf(stderr, "ftdi_new failed\n");
return EXIT_FAILURE;
}
ftdi_set_interface(ftdi, INTERFACE_A);
f = ftdi_usb_open(ftdi, 0x0403, 0x6001);
if (f < 0 && f != -5)
{
fprintf(stderr, "unable to open ftdi device: %d (%s)\n", f, ftdi_get_error_string(ftdi));
ftdi_free(ftdi);
exit(-1);
}
printf("ftdi open succeeded(channel 1): %d\n",f);
printf("enabling bitbang mode(channel 1)\n");
ftdi_set_bitmode(ftdi, 0xFF, BITMODE_BITBANG);
// Init 2. channel
if ((ftdi2 = ftdi_new()) == 0)
{
fprintf(stderr, "ftdi_new failed\n");
return EXIT_FAILURE;
}
ftdi_set_interface(ftdi2, INTERFACE_B);
f = ftdi_usb_open(ftdi2, 0x0403, 0x6001);
if (f < 0 && f != -5)
{
fprintf(stderr, "unable to open ftdi device: %d (%s)\n", f, ftdi_get_error_string(ftdi2));
ftdi_free(ftdi2);
exit(-1);
}
printf("ftdi open succeeded(channel 2): %d\n",f);
printf("enabling bitbang mode (channel 2)\n");
ftdi_set_bitmode(ftdi2, 0xFF, BITMODE_BITBANG);
// Write data
printf("startloop\n");
for (i = 0; i < 23; i++)
{
buf[0] = 0x1;
printf("porta: %02i: 0x%02x \n",i,buf[0]);
f = ftdi_write_data(ftdi, buf, 1);
if (f < 0)
fprintf(stderr,"write failed on channel 1 for 0x%x, error %d (%s)\n", buf[0], f, ftdi_get_error_string(ftdi));
usleep(1 * 1000000);
buf[0] = 0x2;
printf("porta: %02i: 0x%02x \n",i,buf[0]);
f = ftdi_write_data(ftdi, buf, 1);
if (f < 0)
fprintf(stderr,"write failed on channel 1 for 0x%x, error %d (%s)\n", buf[0], f, ftdi_get_error_string(ftdi));
usleep(1 * 1000000);
buf[0] = 0x1;
printf("portb: %02i: 0x%02x \n",i,buf[0]);
f = ftdi_write_data(ftdi2, buf, 1);
if (f < 0)
fprintf(stderr,"write failed on channel 2 for 0x%x, error %d (%s)\n", buf[0], f, ftdi_get_error_string(ftdi2));
usleep(1 * 1000000);
buf[0] = 0x2;
printf("portb: %02i: 0x%02x \n",i,buf[0]);
f = ftdi_write_data(ftdi2, buf, 1);
if (f < 0)
fprintf(stderr,"write failed on channel 2 for 0x%x, error %d (%s)\n", buf[0], f, ftdi_get_error_string(ftdi2));
usleep(1 * 1000000);
}
printf("\n");
printf("disabling bitbang mode(channel 1)\n");
ftdi_disable_bitbang(ftdi);
ftdi_usb_close(ftdi);
ftdi_free(ftdi);
printf("disabling bitbang mode(channel 2)\n");
ftdi_disable_bitbang(ftdi2);
ftdi_usb_close(ftdi2);
ftdi_free(ftdi2);
return 0;
}
@@ -0,0 +1,13 @@
cmake_minimum_required ( VERSION 2.8 )
project ( example C )
find_package ( LibFTDI1 NO_MODULE REQUIRED )
include ( ${LIBFTDI_USE_FILE} )
add_executable ( example main.c )
target_link_libraries( example ${LIBFTDI_LIBRARIES} )
install ( TARGETS example
DESTINATION bin )
+24
View File
@@ -0,0 +1,24 @@
/* main.c
Example for ftdi_new()
This program is distributed under the GPL, version 2
*/
#include <stdio.h>
#include <stdlib.h>
#include <ftdi.h>
int main(void)
{
struct ftdi_context *ftdi;
int retval = EXIT_SUCCESS;
if ((ftdi = ftdi_new()) == 0)
{
fprintf(stderr, "ftdi_new failed\n");
return EXIT_FAILURE;
}
return retval;
}
+299
View File
@@ -0,0 +1,299 @@
/* LIBFTDI EEPROM access example
This program is distributed under the GPL, version 2
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <unistd.h>
#include <getopt.h>
#include <ftdi.h>
int read_decode_eeprom(struct ftdi_context *ftdi)
{
int i, j, f;
int value;
int size;
unsigned char buf[256];
f = ftdi_read_eeprom(ftdi);
if (f < 0)
{
fprintf(stderr, "ftdi_read_eeprom: %d (%s)\n",
f, ftdi_get_error_string(ftdi));
return -1;
}
ftdi_get_eeprom_value(ftdi, CHIP_SIZE, & value);
if (value <0)
{
fprintf(stderr, "No EEPROM found or EEPROM empty\n");
fprintf(stderr, "On empty EEPROM, use -w option to write default values\n");
return -1;
}
fprintf(stderr, "Chip type %d ftdi_eeprom_size: %d\n", ftdi->type, value);
if (ftdi->type == TYPE_R)
size = 0xa0;
else
size = value;
ftdi_get_eeprom_buf(ftdi, buf, size);
for (i=0; i < size; i += 16)
{
fprintf(stdout,"0x%03x:", i);
for (j = 0; j< 8; j++)
fprintf(stdout," %02x", buf[i+j]);
fprintf(stdout," ");
for (; j< 16; j++)
fprintf(stdout," %02x", buf[i+j]);
fprintf(stdout," ");
for (j = 0; j< 8; j++)
fprintf(stdout,"%c", isprint(buf[i+j])?buf[i+j]:'.');
fprintf(stdout," ");
for (; j< 16; j++)
fprintf(stdout,"%c", isprint(buf[i+j])?buf[i+j]:'.');
fprintf(stdout,"\n");
}
f = ftdi_eeprom_decode(ftdi, 1);
if (f < 0)
{
fprintf(stderr, "ftdi_eeprom_decode: %d (%s)\n",
f, ftdi_get_error_string(ftdi));
return -1;
}
return 0;
}
int main(int argc, char **argv)
{
struct ftdi_context *ftdi;
int f, i;
int vid = 0;
int pid = 0;
char const *desc = 0;
char const *serial = 0;
int erase = 0;
int use_defaults = 0;
int large_chip = 0;
int do_write = 0;
int retval = 0;
int value;
if ((ftdi = ftdi_new()) == 0)
{
fprintf(stderr, "Failed to allocate ftdi structure :%s \n",
ftdi_get_error_string(ftdi));
return EXIT_FAILURE;
}
while ((i = getopt(argc, argv, "d::ev:p:l:P:S:w")) != -1)
{
switch (i)
{
case 'd':
use_defaults = 1;
if (optarg)
large_chip = 0x66;
break;
case 'e':
erase = 1;
break;
case 'v':
vid = strtoul(optarg, NULL, 0);
break;
case 'p':
pid = strtoul(optarg, NULL, 0);
break;
case 'P':
desc = optarg;
break;
case 'S':
serial = optarg;
break;
case 'w':
do_write = 1;
break;
default:
fprintf(stderr, "usage: %s [options]\n", *argv);
fprintf(stderr, "\t-d[num] Work with default valuesfor 128 Byte "
"EEPROM or for 256 Byte EEPROM if some [num] is given\n");
fprintf(stderr, "\t-w write\n");
fprintf(stderr, "\t-e erase\n");
fprintf(stderr, "\t-v verbose decoding\n");
fprintf(stderr, "\t-p <number> Search for device with PID == number\n");
fprintf(stderr, "\t-v <number> Search for device with VID == number\n");
fprintf(stderr, "\t-P <string? Search for device with given "
"product description\n");
fprintf(stderr, "\t-S <string? Search for device with given "
"serial number\n");
retval = -1;
goto done;
}
}
// Select first interface
ftdi_set_interface(ftdi, INTERFACE_ANY);
if (!vid && !pid && desc == NULL && serial == NULL)
{
struct ftdi_device_list *devlist, *curdev;
int res;
if ((res = ftdi_usb_find_all(ftdi, &devlist, 0, 0)) < 0)
{
fprintf(stderr, "No FTDI with default VID/PID found\n");
retval = EXIT_FAILURE;
goto do_deinit;
}
if (res > 1)
{
int i = 1;
fprintf(stderr, "%d FTDI devices found: Only Readout on EEPROM done. ",res);
fprintf(stderr, "Use VID/PID/desc/serial to select device\n");
for (curdev = devlist; curdev != NULL; curdev= curdev->next, i++)
{
f = ftdi_usb_open_dev(ftdi, curdev->dev);
if (f<0)
{
fprintf(stderr, "Unable to open device %d: (%s)",
i, ftdi_get_error_string(ftdi));
continue;
}
fprintf(stderr, "Decoded values of device %d:\n", i);
read_decode_eeprom(ftdi);
ftdi_usb_close(ftdi);
}
ftdi_list_free(&devlist);
retval = EXIT_SUCCESS;
goto do_deinit;
}
else if (res == 1)
{
f = ftdi_usb_open_dev(ftdi, devlist[0].dev);
if (f<0)
{
fprintf(stderr, "Unable to open device %d: (%s)",
i, ftdi_get_error_string(ftdi));
}
}
else
{
fprintf(stderr, "No devices found\n");
f = 0;
}
ftdi_list_free(&devlist);
}
else
{
// Open device
f = ftdi_usb_open_desc(ftdi, vid, pid, desc, serial);
if (f < 0)
{
fprintf(stderr, "Device VID 0x%04x PID 0x%04x", vid, pid);
if (desc)
fprintf(stderr, " Desc %s", desc);
if (serial)
fprintf(stderr, " Serial %s", serial);
fprintf(stderr, "\n");
fprintf(stderr, "unable to open ftdi device: %d (%s)\n",
f, ftdi_get_error_string(ftdi));
retval = -1;
goto done;
}
}
if (erase)
{
f = ftdi_erase_eeprom(ftdi); /* needed to determine EEPROM chip type */
if (f < 0)
{
fprintf(stderr, "Erase failed: %s",
ftdi_get_error_string(ftdi));
retval = -2;
goto done;
}
if (ftdi_get_eeprom_value(ftdi, CHIP_TYPE, & value) <0)
{
fprintf(stderr, "ftdi_get_eeprom_value: %d (%s)\n",
f, ftdi_get_error_string(ftdi));
}
if (value == -1)
fprintf(stderr, "No EEPROM\n");
else if (value == 0)
fprintf(stderr, "Internal EEPROM\n");
else
fprintf(stderr, "Found 93x%02x\n", value);
retval = 0;
goto done;
}
if (use_defaults)
{
ftdi_eeprom_initdefaults(ftdi, NULL, NULL, NULL);
if (ftdi_set_eeprom_value(ftdi, MAX_POWER, 500) <0)
{
fprintf(stderr, "ftdi_set_eeprom_value: %d (%s)\n",
f, ftdi_get_error_string(ftdi));
}
if (large_chip)
if (ftdi_set_eeprom_value(ftdi, CHIP_TYPE, 0x66) <0)
{
fprintf(stderr, "ftdi_set_eeprom_value: %d (%s)\n",
f, ftdi_get_error_string(ftdi));
}
f=(ftdi_eeprom_build(ftdi));
if (f < 0)
{
fprintf(stderr, "ftdi_eeprom_build: %d (%s)\n",
f, ftdi_get_error_string(ftdi));
retval = -1;
goto done;
}
}
else if (do_write)
{
ftdi_eeprom_initdefaults(ftdi, NULL, NULL, NULL);
f = ftdi_erase_eeprom(ftdi);
if (ftdi_set_eeprom_value(ftdi, MAX_POWER, 500) <0)
{
fprintf(stderr, "ftdi_set_eeprom_value: %d (%s)\n",
f, ftdi_get_error_string(ftdi));
}
f = ftdi_erase_eeprom(ftdi);/* needed to determine EEPROM chip type */
if (ftdi_get_eeprom_value(ftdi, CHIP_TYPE, & value) <0)
{
fprintf(stderr, "ftdi_get_eeprom_value: %d (%s)\n",
f, ftdi_get_error_string(ftdi));
}
if (value == -1)
fprintf(stderr, "No EEPROM\n");
else if (value == 0)
fprintf(stderr, "Internal EEPROM\n");
else
fprintf(stderr, "Found 93x%02x\n", value);
f=(ftdi_eeprom_build(ftdi));
if (f < 0)
{
fprintf(stderr, "Erase failed: %s",
ftdi_get_error_string(ftdi));
retval = -2;
goto done;
}
f = ftdi_write_eeprom(ftdi);
{
fprintf(stderr, "ftdi_eeprom_decode: %d (%s)\n",
f, ftdi_get_error_string(ftdi));
retval = 1;
goto done;
}
}
retval = read_decode_eeprom(ftdi);
done:
ftdi_usb_close(ftdi);
do_deinit:
ftdi_free(ftdi);
return retval;
}
+54
View File
@@ -0,0 +1,54 @@
/* find_all.c
Example for ftdi_usb_find_all()
This program is distributed under the GPL, version 2
*/
#include <stdio.h>
#include <stdlib.h>
#include <ftdi.h>
int main(void)
{
int ret, i;
struct ftdi_context *ftdi;
struct ftdi_device_list *devlist, *curdev;
char manufacturer[128], description[128];
int retval = EXIT_SUCCESS;
if ((ftdi = ftdi_new()) == 0)
{
fprintf(stderr, "ftdi_new failed\n");
return EXIT_FAILURE;
}
if ((ret = ftdi_usb_find_all(ftdi, &devlist, 0, 0)) < 0)
{
fprintf(stderr, "ftdi_usb_find_all failed: %d (%s)\n", ret, ftdi_get_error_string(ftdi));
retval = EXIT_FAILURE;
goto do_deinit;
}
printf("Number of FTDI devices found: %d\n", ret);
i = 0;
for (curdev = devlist; curdev != NULL; i++)
{
printf("Checking device: %d\n", i);
if ((ret = ftdi_usb_get_strings(ftdi, curdev->dev, manufacturer, 128, description, 128, NULL, 0)) < 0)
{
fprintf(stderr, "ftdi_usb_get_strings failed: %d (%s)\n", ret, ftdi_get_error_string(ftdi));
retval = EXIT_FAILURE;
goto done;
}
printf("Manufacturer: %s, Description: %s\n\n", manufacturer, description);
curdev = curdev->next;
}
done:
ftdi_list_free(&devlist);
do_deinit:
ftdi_free(ftdi);
return retval;
}
+72
View File
@@ -0,0 +1,72 @@
/* final_all_pp.cpp
Simple libftdi-cpp usage
This program is distributed under the GPL, version 2
*/
#include "ftdi.hpp"
#include <iostream>
#include <iomanip>
#include <cstdlib>
#include <cstring>
using namespace Ftdi;
int main(int argc, char **argv)
{
// Show help
if (argc > 1)
{
if (strcmp(argv[1],"-h") == 0 || strcmp(argv[1],"--help") == 0)
{
std::cout << "Usage: " << argv[0] << " [-v VENDOR_ID] [-p PRODUCT_ID]" << std::endl;
return EXIT_SUCCESS;
}
}
// Parse args
int vid = 0x0403, pid = 0x6010, tmp = 0;
for (int i = 0; i < (argc - 1); i++)
{
if (strcmp(argv[i], "-v") == 0)
if ((tmp = strtol(argv[++i], 0, 16)) >= 0)
vid = tmp;
if (strcmp(argv[i], "-p") == 0)
if ((tmp = strtol(argv[++i], 0, 16)) >= 0)
pid = tmp;
}
// Print header
std::cout << std::hex << std::showbase
<< "Found devices ( VID: " << vid << ", PID: " << pid << " )"
<< std::endl
<< "------------------------------------------------"
<< std::endl << std::dec;
// Print whole list
Context context;
List* list = List::find_all(context, vid, pid);
for (List::iterator it = list->begin(); it != list->end(); it++)
{
std::cout << "FTDI (" << &*it << "): "
<< it->vendor() << ", "
<< it->description() << ", "
<< it->serial();
// Open test
if(it->open() == 0)
std::cout << " (Open OK)";
else
std::cout << " (Open FAILED)";
it->close();
std::cout << std::endl;
}
delete list;
return EXIT_SUCCESS;
}
+179
View File
@@ -0,0 +1,179 @@
/* serial_test.c
Read/write data via serial I/O
This program is distributed under the GPL, version 2
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <getopt.h>
#include <signal.h>
#include <ftdi.h>
static int exitRequested = 0;
/*
* sigintHandler --
*
* SIGINT handler, so we can gracefully exit when the user hits ctrl-C.
*/
static void
sigintHandler(int signum)
{
exitRequested = 1;
}
int main(int argc, char **argv)
{
struct ftdi_context *ftdi;
unsigned char buf[1024];
int f = 0, i;
int vid = 0x403;
int pid = 0;
int baudrate = 115200;
int interface = INTERFACE_ANY;
int do_write = 0;
unsigned int pattern = 0xffff;
int retval = EXIT_FAILURE;
while ((i = getopt(argc, argv, "i:v:p:b:w::")) != -1)
{
switch (i)
{
case 'i': // 0=ANY, 1=A, 2=B, 3=C, 4=D
interface = strtoul(optarg, NULL, 0);
break;
case 'v':
vid = strtoul(optarg, NULL, 0);
break;
case 'p':
pid = strtoul(optarg, NULL, 0);
break;
case 'b':
baudrate = strtoul(optarg, NULL, 0);
break;
case 'w':
do_write = 1;
if (optarg)
pattern = strtoul(optarg, NULL, 0);
if (pattern > 0xff)
{
fprintf(stderr, "Please provide a 8 bit pattern\n");
exit(-1);
}
break;
default:
fprintf(stderr, "usage: %s [-i interface] [-v vid] [-p pid] [-b baudrate] [-w [pattern]]\n", *argv);
exit(-1);
}
}
// Init
if ((ftdi = ftdi_new()) == 0)
{
fprintf(stderr, "ftdi_new failed\n");
return EXIT_FAILURE;
}
if (!vid && !pid && (interface == INTERFACE_ANY))
{
ftdi_set_interface(ftdi, INTERFACE_ANY);
struct ftdi_device_list *devlist;
int res;
if ((res = ftdi_usb_find_all(ftdi, &devlist, 0, 0)) < 0)
{
fprintf(stderr, "No FTDI with default VID/PID found\n");
goto do_deinit;
}
if (res == 1)
{
f = ftdi_usb_open_dev(ftdi, devlist[0].dev);
if (f<0)
{
fprintf(stderr, "Unable to open device %d: (%s)",
i, ftdi_get_error_string(ftdi));
}
}
ftdi_list_free(&devlist);
if (res > 1)
{
fprintf(stderr, "%d Devices found, please select Device with VID/PID\n", res);
/* TODO: List Devices*/
goto do_deinit;
}
if (res == 0)
{
fprintf(stderr, "No Devices found with default VID/PID\n");
goto do_deinit;
}
}
else
{
// Select interface
ftdi_set_interface(ftdi, interface);
// Open device
f = ftdi_usb_open(ftdi, vid, pid);
}
if (f < 0)
{
fprintf(stderr, "unable to open ftdi device: %d (%s)\n", f, ftdi_get_error_string(ftdi));
exit(-1);
}
// Set baudrate
f = ftdi_set_baudrate(ftdi, baudrate);
if (f < 0)
{
fprintf(stderr, "unable to set baudrate: %d (%s)\n", f, ftdi_get_error_string(ftdi));
exit(-1);
}
/* Set line parameters
*
* TODO: Make these parameters settable from the command line
*
* Parameters are choosen that sending a continous stream of 0x55
* should give a square wave
*
*/
f = ftdi_set_line_property(ftdi, 8, STOP_BIT_1, NONE);
if (f < 0)
{
fprintf(stderr, "unable to set line parameters: %d (%s)\n", f, ftdi_get_error_string(ftdi));
exit(-1);
}
if (do_write)
for(i=0; i<1024; i++)
buf[i] = pattern;
signal(SIGINT, sigintHandler);
while (!exitRequested)
{
if (do_write)
f = ftdi_write_data(ftdi, buf,
(baudrate/512 >sizeof(buf))?sizeof(buf):
(baudrate/512)?baudrate/512:1);
else
f = ftdi_read_data(ftdi, buf, sizeof(buf));
if (f<0)
usleep(1 * 1000000);
else if(f> 0 && !do_write)
{
fprintf(stderr, "read %d bytes\n", f);
fwrite(buf, f, 1, stdout);
fflush(stderr);
fflush(stdout);
}
}
signal(SIGINT, SIG_DFL);
retval = EXIT_SUCCESS;
ftdi_usb_close(ftdi);
do_deinit:
ftdi_free(ftdi);
return retval;
}
+53
View File
@@ -0,0 +1,53 @@
/* simple.c
Simple libftdi usage example
This program is distributed under the GPL, version 2
*/
#include <stdio.h>
#include <stdlib.h>
#include <ftdi.h>
int main(void)
{
int ret;
struct ftdi_context *ftdi;
struct ftdi_version_info version;
if ((ftdi = ftdi_new()) == 0)
{
fprintf(stderr, "ftdi_new failed\n");
return EXIT_FAILURE;
}
version = ftdi_get_library_version();
printf("Initialized libftdi %s (major: %d, minor: %d, micro: %d, snapshot ver: %s)\n",
version.version_str, version.major, version.minor, version.micro,
version.snapshot_str);
if ((ret = ftdi_usb_open(ftdi, 0x0403, 0x6001)) < 0)
{
fprintf(stderr, "unable to open ftdi device: %d (%s)\n", ret, ftdi_get_error_string(ftdi));
ftdi_free(ftdi);
return EXIT_FAILURE;
}
// Read out FTDIChip-ID of R type chips
if (ftdi->type == TYPE_R)
{
unsigned int chipid;
printf("ftdi_read_chipid: %d\n", ftdi_read_chipid(ftdi, &chipid));
printf("FTDI chipid: %X\n", chipid);
}
if ((ret = ftdi_usb_close(ftdi)) < 0)
{
fprintf(stderr, "unable to close ftdi device: %d (%s)\n", ret, ftdi_get_error_string(ftdi));
ftdi_free(ftdi);
return EXIT_FAILURE;
}
ftdi_free(ftdi);
return EXIT_SUCCESS;
}
+358
View File
@@ -0,0 +1,358 @@
/* stream_test.c
*
* Test reading from FT2232H in synchronous FIFO mode.
*
* The FT2232H must supply data due to an appropriate circuit
*
* To check for skipped block with appended code,
* a structure as follows is assumed
* 1* uint32_t num (incremented in 0x4000 steps)
* 3* uint32_t dont_care
*
* After start, data will be read in streaming until the program is aborted
* Progess information wil be printed out
* If a filename is given on the command line, the data read will be
* written to that file
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <unistd.h>
#include <getopt.h>
#include <signal.h>
#include <errno.h>
#include <ftdi.h>
void check_outfile(char *);
static FILE *outputFile;
static int check = 1;
static int exitRequested = 0;
/*
* sigintHandler --
*
* SIGINT handler, so we can gracefully exit when the user hits ctrl-C.
*/
static void
sigintHandler(int signum)
{
exitRequested = 1;
}
static void
usage(const char *argv0)
{
fprintf(stderr,
"Usage: %s [options...] \n"
"Test streaming read from FT2232H\n"
"[-P string] only look for product with given string\n"
"[-n] don't check for special block structure\n"
"\n"
"If some filename is given, write data read to that file\n"
"Progess information is printed each second\n"
"Abort with ^C\n"
"\n"
"Options:\n"
"\n"
"Copyright (C) 2009 Micah Dowty <micah@navi.cx>\n"
"Adapted for use with libftdi (C) 2010 Uwe Bonnes <bon@elektron.ikp.physik.tu-darmstadt.de>\n",
argv0);
exit(1);
}
static uint32_t start = 0;
static uint32_t offset = 0;
static uint64_t blocks = 0;
static uint32_t skips = 0;
static uint32_t n_err = 0;
static int
readCallback(uint8_t *buffer, int length, FTDIProgressInfo *progress, void *userdata)
{
if (length)
{
if (check)
{
int i,rem;
uint32_t num;
for (i= offset; i<length-16; i+=16)
{
num = *(uint32_t*) (buffer+i);
if (start && (num != start +0x4000))
{
uint32_t delta = ((num-start)/0x4000)-1;
fprintf(stderr, "Skip %7d blocks from 0x%08x to 0x%08x at blocks %10llu\n",
delta, start -0x4000, num, (unsigned long long)blocks);
n_err++;
skips += delta;
}
blocks ++;
start = num;
}
rem = length -i;
if (rem >3)
{
num = *(uint32_t*) (buffer+i);
if (start && (num != start +0x4000))
{
uint32_t delta = ((num-start)/0x4000)-1;
fprintf(stderr, "Skip %7d blocks from 0x%08x to 0x%08x at blocks %10llu\n",
delta, start -0x4000, num, (unsigned long long) blocks);
n_err++;
skips += delta;
}
start = num;
}
else if (rem)
start += 0x4000;
if (rem != 0)
{
blocks ++;
offset = 16-rem;
}
}
if (outputFile)
{
if (fwrite(buffer, length, 1, outputFile) != 1)
{
perror("Write error");
return 1;
}
}
}
if (progress)
{
fprintf(stderr, "%10.02fs total time %9.3f MiB captured %7.1f kB/s curr rate %7.1f kB/s totalrate %d dropouts\n",
progress->totalTime,
progress->current.totalBytes / (1024.0 * 1024.0),
progress->currentRate / 1024.0,
progress->totalRate / 1024.0,
n_err);
}
return exitRequested ? 1 : 0;
}
int main(int argc, char **argv)
{
struct ftdi_context *ftdi;
int err, c;
FILE *of = NULL;
char const *outfile = 0;
outputFile =0;
exitRequested = 0;
char *descstring = NULL;
int option_index;
static struct option long_options[] = {{NULL},};
while ((c = getopt_long(argc, argv, "P:n", long_options, &option_index)) !=- 1)
switch (c)
{
case -1:
break;
case 'P':
descstring = optarg;
break;
case 'n':
check = 0;
break;
default:
usage(argv[0]);
}
if (optind == argc - 1)
{
// Exactly one extra argument- a dump file
outfile = argv[optind];
}
else if (optind < argc)
{
// Too many extra args
usage(argv[0]);
}
if ((ftdi = ftdi_new()) == 0)
{
fprintf(stderr, "ftdi_new failed\n");
return EXIT_FAILURE;
}
if (ftdi_set_interface(ftdi, INTERFACE_A) < 0)
{
fprintf(stderr, "ftdi_set_interface failed\n");
ftdi_free(ftdi);
return EXIT_FAILURE;
}
if (ftdi_usb_open_desc(ftdi, 0x0403, 0x6010, descstring, NULL) < 0)
{
fprintf(stderr,"Can't open ftdi device: %s\n",ftdi_get_error_string(ftdi));
ftdi_free(ftdi);
return EXIT_FAILURE;
}
/* A timeout value of 1 results in may skipped blocks */
if(ftdi_set_latency_timer(ftdi, 2))
{
fprintf(stderr,"Can't set latency, Error %s\n",ftdi_get_error_string(ftdi));
ftdi_usb_close(ftdi);
ftdi_free(ftdi);
return EXIT_FAILURE;
}
/* if(ftdi_usb_purge_rx_buffer(ftdi) < 0)
{
fprintf(stderr,"Can't rx purge\n",ftdi_get_error_string(ftdi));
return EXIT_FAILURE;
}*/
if (outfile)
if ((of = fopen(outfile,"w+")) == 0)
fprintf(stderr,"Can't open logfile %s, Error %s\n", outfile, strerror(errno));
if (of)
if (setvbuf(of, NULL, _IOFBF , 1<<16) == 0)
outputFile = of;
signal(SIGINT, sigintHandler);
err = ftdi_readstream(ftdi, readCallback, NULL, 8, 256);
if (err < 0 && !exitRequested)
exit(1);
if (outputFile) {
fclose(outputFile);
outputFile = NULL;
}
fprintf(stderr, "Capture ended.\n");
if (ftdi_set_bitmode(ftdi, 0xff, BITMODE_RESET) < 0)
{
fprintf(stderr,"Can't set synchronous fifo mode, Error %s\n",ftdi_get_error_string(ftdi));
ftdi_usb_close(ftdi);
ftdi_free(ftdi);
return EXIT_FAILURE;
}
ftdi_usb_close(ftdi);
ftdi_free(ftdi);
signal(SIGINT, SIG_DFL);
if (check && outfile)
{
if ((outputFile = fopen(outfile,"r")) == 0)
{
fprintf(stderr,"Can't open logfile %s, Error %s\n", outfile, strerror(errno));
ftdi_usb_close(ftdi);
ftdi_free(ftdi);
return EXIT_FAILURE;
}
check_outfile(descstring);
fclose(outputFile);
}
else if (check)
fprintf(stderr,"%d errors of %llu blocks (%Le), %d (%Le) blocks skipped\n",
n_err, (unsigned long long) blocks, (long double)n_err/(long double) blocks,
skips, (long double)skips/(long double) blocks);
exit (0);
}
void check_outfile(char *descstring)
{
if(strcmp(descstring,"FT2232HTEST") == 0)
{
char buf0[1024];
char buf1[1024];
char bufr[1024];
char *pa, *pb, *pc;
unsigned int num_lines = 0, line_num = 1;
int err_count = 0;
unsigned int num_start, num_end;
pa = buf0;
pb = buf1;
pc = buf0;
if(fgets(pa, 1023, outputFile) == NULL)
{
fprintf(stderr,"Empty output file\n");
return;
}
while(fgets(pb, 1023, outputFile) != NULL)
{
num_lines++;
unsigned int num_save = num_start;
if( sscanf(pa,"%6u%94s%6u",&num_start, bufr,&num_end) !=3)
{
fprintf(stdout,"Format doesn't match at line %8d \"%s",
num_lines, pa);
err_count++;
line_num = num_save +2;
}
else
{
if ((num_start+1)%100000 != num_end)
{
if (err_count < 20)
fprintf(stdout,"Malformed line %d \"%s\"\n",
num_lines, pa);
err_count++;
}
else if(num_start != line_num)
{
if (err_count < 20)
fprintf(stdout,"Skipping from %d to %d\n",
line_num, num_start);
err_count++;
}
line_num = num_end;
}
pa = pb;
pb = pc;
pc = pa;
}
if(err_count)
fprintf(stdout,"\n%d errors of %d data sets %f\n", err_count, num_lines, (double) err_count/(double)num_lines);
else
fprintf(stdout,"No errors for %d lines\n",num_lines);
}
else if(strcmp(descstring,"LLBBC10") == 0)
{
uint32_t block0[4];
uint32_t block1[4];
uint32_t *pa = block0;
uint32_t *pb = block1;
uint32_t *pc = block0;
uint32_t start= 0;
uint32_t nread = 0;
int n_shown = 0;
int n_errors = 0;
if (fread(pa, sizeof(uint32_t), 4,outputFile) < 4)
{
fprintf(stderr,"Empty result file\n");
return;
}
while(fread(pb, sizeof(uint32_t), 4,outputFile) != 0)
{
blocks++;
nread = pa[0];
if(start>0 && (nread != start))
{
if(n_shown < 30)
{
fprintf(stderr, "Skip %7d blocks from 0x%08x to 0x%08x at blocks %10llu \n",
(nread-start)/0x4000, start -0x4000, nread, (unsigned long long) blocks);
n_shown ++;
}
n_errors++;
}
else if (n_shown >0)
n_shown--;
start = nread + 0x4000;
pa = pb;
pb = pc;
pc = pa;
}
if(n_errors)
fprintf(stderr, "%d blocks wrong from %llu blocks read\n",
n_errors, (unsigned long long) blocks);
else
fprintf(stderr, "%llu blocks all fine\n", (unsigned long long) blocks);
}
}
+55
View File
@@ -0,0 +1,55 @@
# determine docdir
include(GNUInstallDirs)
if(NOT CMAKE_INSTALL_DOCDIR)
if(WIN32)
set(CMAKE_INSTALL_DOCDIR .)
else(WIN32)
set(CMAKE_INSTALL_DOCDIR ${CMAKE_INSTALL_DATAROOTDIR}/doc/${PROJECT_NAME})
endif(WIN32)
endif(NOT CMAKE_INSTALL_DOCDIR)
option(FTDI_EEPROM "Build ftdi_eeprom" ON)
if ( FTDI_EEPROM )
find_package ( Confuse )
find_package ( Libintl )
else(FTDI_EEPROM)
message(STATUS "ftdi_eeprom build is disabled")
endif ()
if ( CONFUSE_FOUND )
message(STATUS "Building ftdi_eeprom")
include_directories ( ${CONFUSE_INCLUDE_DIRS} )
list ( APPEND libs ${CONFUSE_LIBRARIES} )
if ( LIBINTL_FOUND )
include_directories ( ${LIBINTL_INCLUDE_DIR} )
list ( APPEND libs ${LIBINTL_LIBRARIES} )
endif ()
# Version defines
set ( EEPROM_MAJOR_VERSION 0 )
set ( EEPROM_MINOR_VERSION 17 )
set ( EEPROM_VERSION_STRING ${EEPROM_MAJOR_VERSION}.${EEPROM_MINOR_VERSION} )
include_directories ( BEFORE ${CMAKE_SOURCE_DIR}/src )
include_directories ( BEFORE ${CMAKE_CURRENT_BINARY_DIR} )
configure_file(
ftdi_eeprom_version.h.in
${CMAKE_CURRENT_BINARY_DIR}/ftdi_eeprom_version.h
)
add_executable ( ftdi_eeprom main.c )
target_link_libraries ( ftdi_eeprom ftdi1 ${CONFUSE_LIBRARIES} )
if ( LIBINTL_FOUND )
target_link_libraries ( ftdi_eeprom ${LIBINTL_LIBRARIES} )
endif ()
install ( TARGETS ftdi_eeprom DESTINATION bin )
install ( FILES example.conf DESTINATION ${CMAKE_INSTALL_DOCDIR} )
else ()
message ( STATUS "libConfuse not found, won't build ftdi_eeprom" )
endif ()
+59
View File
@@ -0,0 +1,59 @@
vendor_id=0x0403 # Vendor ID
product_id=0x6001 # Product ID
max_power=0 # Max. power consumption: value * 2 mA. Use 0 if self_powered = true.
###########
# Strings #
###########
manufacturer="ACME Inc" # Manufacturer
product="USB Serial Converter" # Product
serial="08-15" # Serial
###########
# Options #
###########
self_powered=true # Turn this off for bus powered
remote_wakeup=false # Turn this on for remote wakeup feature
use_serial=true # Use the serial number string
# Normally out don't have to change one of these flags
in_is_isochronous=false # In Endpoint is Isochronous
out_is_isochronous=false # Out Endpoint is Isochronous
suspend_pull_downs=false # Enable suspend pull downs for lower power
change_usb_version=false # Change USB Version
usb_version=0x0200 # Only used when change_usb_version is enabled
# Only used on FT-R chips (when omitted, use chip defaults)
# Possible values correspond to enum ftdi_cbus_func.
cbus0=TXLED
cbus1=RXLED
cbus2=TXDEN
cbus3=PWREN
cbus4=SLEEP
# Only used on FT232H chips (when omitted, use chip defaults)
# Possible values correspond to enum ftdi_cbush_func.
cbush0=TRISTATE
cbush1=TRISTATE
cbush2=TRISTATE
cbush3=TRISTATE
cbush4=TRISTATE
cbush5=TRISTATE
cbush6=TRISTATE
cbush7=TRISTATE
cbush8=TRISTATE
cbush9=TRISTATE
# Only used on FT230X chips (when omitted, use chip defaults)
# Possible values correspond to enum ftdi_cbusx_func.
cbusx0=TXDEN
cbusx1=RXLED
cbusx2=TXLED
cbusx3=SLEEP
########
# Misc #
########
filename="eeprom.new" # Filename, leave empty to skip file writing
@@ -0,0 +1,8 @@
#ifndef _FTDI_EEPROM_VERSION_H
#define _FTDI_EEPROM_VERSION_H
#define EEPROM_MAJOR_VERSION @EEPROM_MAJOR_VERSION@
#define EEPROM_MINOR_VERSION @EEPROM_MINOR_VERSION@
#define EEPROM_VERSION_STRING "@EEPROM_VERSION_STRING@"
#endif
+666
View File
@@ -0,0 +1,666 @@
/***************************************************************************
main.c - description
-------------------
begin : Mon Apr 7 12:05:22 CEST 2003
copyright : (C) 2003-2014 by Intra2net AG and the libftdi developers
email : opensource@intra2net.com
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License version 2 as *
* published by the Free Software Foundation. *
* *
***************************************************************************/
/*
TODO:
- Merge Uwe's eeprom tool. Current features:
- Init eeprom defaults based upon eeprom type
- Read -> Already there
- Write -> Already there
- Erase -> Already there
- Decode on stdout
- Ability to find device by PID/VID, product name or serial
TODO nice-to-have:
- Out-of-the-box compatibility with FTDI's eeprom tool configuration files
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <sys/stat.h>
#include <confuse.h>
#include <libusb.h>
#include <ftdi.h>
#include <ftdi_eeprom_version.h>
static int parse_cbus(cfg_t *cfg, cfg_opt_t *opt, const char *value, void *result)
{
static const char* options[] =
{
"TXDEN", "PWREN", "RXLED", "TXLED", "TXRXLED", "SLEEP", "CLK48",
"CLK24", "CLK12", "CLK6", "IOMODE", "BB_WR", "BB_RD"
};
int i;
for (i=0; i<sizeof(options)/sizeof(*options); i++)
{
if (!(strcmp(options[i], value)))
{
*(int *)result = i;
return 0;
}
}
cfg_error(cfg, "Invalid %s option '%s'", cfg_opt_name(opt), value);
return -1;
}
static int parse_cbush(cfg_t *cfg, cfg_opt_t *opt, const char *value, void *result)
{
static const char* options[] =
{
"TRISTATE", "TXLED", "RXLED", "TXRXLED", "PWREN", "SLEEP",
"DRIVE_0", "DRIVE1", "IOMODE", "TXDEN", "CLK30", "CLK15", "CLK7_5"
};
int i;
for (i=0; i<sizeof(options)/sizeof(*options); i++)
{
if (!(strcmp(options[i], value)))
{
*(int *)result = i;
return 0;
}
}
cfg_error(cfg, "Invalid %s option '%s'", cfg_opt_name(opt), value);
return -1;
}
static int parse_cbusx(cfg_t *cfg, cfg_opt_t *opt, const char *value, void *result)
{
static const char* options[] =
{
"TRISTATE", "TXLED", "RXLED", "TXRXLED", "PWREN", "SLEEP",
"DRIVE_0", "DRIVE1", "IOMODE", "TXDEN", "CLK24", "CLK12",
"CLK6", "BAT_DETECT", "BAT_DETECT_NEG", "I2C_TXE", "I2C_RXF", "VBUS_SENSE",
"BB_WR", "BB_RD", "TIME_STAMP", "AWAKE"
};
int i;
for (i=0; i<sizeof(options)/sizeof(*options); i++)
{
if (!(strcmp(options[i], value)))
{
*(int *)result = i;
return 0;
}
}
cfg_error(cfg, "Invalid %s option '%s'", cfg_opt_name(opt), value);
return -1;
}
static int parse_chtype(cfg_t *cfg, cfg_opt_t *opt, const char *value, void *result)
{
static const struct
{
char* key;
int opt;
} options[] =
{
{ "UART", CHANNEL_IS_UART },
{ "FIFO", CHANNEL_IS_FIFO },
{ "OPTO", CHANNEL_IS_OPTO },
{ "CPU", CHANNEL_IS_CPU },
{ "FT1284", CHANNEL_IS_FT1284}
};
int i;
for (i=0; i<sizeof(options)/sizeof(*options); i++)
{
if (!(strcmp(options[i].key, value)))
{
*(int *)result = options[i].opt;
return 0;
}
}
cfg_error(cfg, "Invalid %s option '%s'", cfg_opt_name(opt), value);
return -1;
}
/**
* @brief Set eeprom value
*
* \param ftdi pointer to ftdi_context
* \param value_name Enum of the value to set
* \param value Value to set
*
* Function will abort the program on error
**/
static void eeprom_set_value(struct ftdi_context *ftdi, enum ftdi_eeprom_value value_name, int value)
{
if (ftdi_set_eeprom_value(ftdi, value_name, value) < 0)
{
printf("Unable to set eeprom value %d: %s. Aborting\n", value_name, ftdi_get_error_string(ftdi));
exit (-1);
}
}
/**
* @brief Get eeprom value
*
* \param ftdi pointer to ftdi_context
* \param value_name Enum of the value to get
* \param value Value to get
*
* Function will abort the program on error
**/
static void eeprom_get_value(struct ftdi_context *ftdi, enum ftdi_eeprom_value value_name, int *value)
{
if (ftdi_get_eeprom_value(ftdi, value_name, value) < 0)
{
printf("Unable to get eeprom value %d: %s. Aborting\n", value_name, ftdi_get_error_string(ftdi));
exit (-1);
}
}
static void usage(const char *program)
{
fprintf(stderr, "Syntax: %s [...options...] <config-file>\n", program);
fprintf(stderr, "Valid Options:\n");
fprintf(stderr, "--device <description> Specify device to open by description string. One of:\n");
fprintf(stderr, " d:<devicenode>\n");
fprintf(stderr, " i:<vendor>:<product>\n");
fprintf(stderr, " i:<vendor>:<product>:<index>\n");
fprintf(stderr, " s:<vendor>:<product>:<serial>\n");
fprintf(stderr, "--read-eeprom Read eeprom and write to -filename- from config-file\n");
fprintf(stderr, "--build-eeprom Build eeprom image\n");
fprintf(stderr, "--erase-eeprom Erase eeprom\n");
fprintf(stderr, "--flash-eeprom Flash eeprom\n");
}
int main(int argc, char *argv[])
{
/*
configuration options
*/
cfg_opt_t opts[] =
{
CFG_INT("vendor_id", 0, 0),
CFG_INT("product_id", 0, 0),
CFG_BOOL("self_powered", cfg_true, 0),
CFG_BOOL("remote_wakeup", cfg_true, 0),
CFG_BOOL("in_is_isochronous", cfg_false, 0),
CFG_BOOL("out_is_isochronous", cfg_false, 0),
CFG_BOOL("suspend_pull_downs", cfg_false, 0),
CFG_BOOL("use_serial", cfg_false, 0),
CFG_BOOL("change_usb_version", cfg_false, 0),
CFG_INT("usb_version", 0, 0),
CFG_INT("default_pid", 0x6001, 0),
CFG_INT("max_power", 0, 0),
CFG_STR("manufacturer", "Acme Inc.", 0),
CFG_STR("product", "USB Serial Converter", 0),
CFG_STR("serial", "08-15", 0),
CFG_INT("eeprom_type", 0x00, 0),
CFG_STR("filename", "", 0),
CFG_BOOL("flash_raw", cfg_false, 0),
CFG_BOOL("high_current", cfg_false, 0),
CFG_INT_CB("cbus0", -1, 0, parse_cbus),
CFG_INT_CB("cbus1", -1, 0, parse_cbus),
CFG_INT_CB("cbus2", -1, 0, parse_cbus),
CFG_INT_CB("cbus3", -1, 0, parse_cbus),
CFG_INT_CB("cbus4", -1, 0, parse_cbus),
CFG_INT_CB("cbush0", -1, 0, parse_cbush),
CFG_INT_CB("cbush1", -1, 0, parse_cbush),
CFG_INT_CB("cbush2", -1, 0, parse_cbush),
CFG_INT_CB("cbush3", -1, 0, parse_cbush),
CFG_INT_CB("cbush4", -1, 0, parse_cbush),
CFG_INT_CB("cbush5", -1, 0, parse_cbush),
CFG_INT_CB("cbush6", -1, 0, parse_cbush),
CFG_INT_CB("cbush7", -1, 0, parse_cbush),
CFG_INT_CB("cbush8", -1, 0, parse_cbush),
CFG_INT_CB("cbush9", -1, 0, parse_cbush),
CFG_INT_CB("cbusx0", -1, 0, parse_cbusx),
CFG_INT_CB("cbusx1", -1, 0, parse_cbusx),
CFG_INT_CB("cbusx2", -1, 0, parse_cbusx),
CFG_INT_CB("cbusx3", -1, 0, parse_cbusx),
CFG_BOOL("invert_txd", cfg_false, 0),
CFG_BOOL("invert_rxd", cfg_false, 0),
CFG_BOOL("invert_rts", cfg_false, 0),
CFG_BOOL("invert_cts", cfg_false, 0),
CFG_BOOL("invert_dtr", cfg_false, 0),
CFG_BOOL("invert_dsr", cfg_false, 0),
CFG_BOOL("invert_dcd", cfg_false, 0),
CFG_BOOL("invert_ri", cfg_false, 0),
CFG_INT_CB("cha_type", -1, 0, parse_chtype),
CFG_INT_CB("chb_type", -1, 0, parse_chtype),
CFG_BOOL("cha_vcp", cfg_true, 0),
CFG_BOOL("chb_vcp", cfg_true, 0),
CFG_BOOL("chc_vcp", cfg_true, 0),
CFG_BOOL("chd_vcp", cfg_true, 0),
CFG_BOOL("cha_rs485", cfg_false, 0),
CFG_BOOL("chb_rs485", cfg_false, 0),
CFG_BOOL("chc_rs485", cfg_false, 0),
CFG_BOOL("chd_rs485", cfg_false, 0),
CFG_FUNC("include", &cfg_include),
CFG_INT("user_data_addr", 0x18, 0),
CFG_STR("user_data_file", "", 0),
CFG_END()
};
cfg_t *cfg;
/*
normal variables
*/
enum {
COMMAND_READ = 1,
COMMAND_ERASE,
COMMAND_FLASH,
COMMAND_BUILD
} command = 0;
const char *cfg_filename = NULL;
const char *device_description = NULL;
const char *user_data_file = NULL;
char *user_data_buffer = NULL;
const int max_eeprom_size = 256;
int my_eeprom_size = 0;
unsigned char *eeprom_buf = NULL;
char *filename;
int size_check;
int i;
FILE *fp;
struct ftdi_context *ftdi = NULL;
printf("\nFTDI eeprom generator v%s\n", EEPROM_VERSION_STRING);
printf ("(c) Intra2net AG and the libftdi developers <opensource@intra2net.com>\n");
for (i = 1; i < argc; i++) {
if (*argv[i] != '-')
{
cfg_filename = argv[i];
}
else if (!strcmp(argv[i], "--device"))
{
if (i+1 >= argc)
{
usage(argv[0]);
exit(-1);
}
device_description = argv[++i];
}
else if (!strcmp(argv[i], "--read-eeprom"))
{
command = COMMAND_READ;
}
else if (!strcmp(argv[i], "--erase-eeprom"))
{
command = COMMAND_ERASE;
}
else if (!strcmp(argv[i], "--flash-eeprom"))
{
command = COMMAND_FLASH;
}
else if (!strcmp(argv[i], "--build-eeprom"))
{
command = COMMAND_BUILD;
}
else
{
usage(argv[0]);
exit(-1);
}
}
if (!cfg_filename)
{
usage(argv[0]);
exit(-1);
}
if ((fp = fopen(cfg_filename, "r")) == NULL)
{
printf ("Can't open configuration file\n");
exit (-1);
}
fclose (fp);
cfg = cfg_init(opts, 0);
cfg_parse(cfg, cfg_filename);
filename = cfg_getstr(cfg, "filename");
if (cfg_getbool(cfg, "self_powered") && cfg_getint(cfg, "max_power") > 0)
printf("Hint: Self powered devices should have a max_power setting of 0.\n");
if ((ftdi = ftdi_new()) == 0)
{
fprintf(stderr, "Failed to allocate ftdi structure :%s \n",
ftdi_get_error_string(ftdi));
return EXIT_FAILURE;
}
if (device_description != NULL)
{
i = ftdi_usb_open_string(ftdi, device_description);
if (i != 0)
{
printf("Unable to find FTDI device with description: %s\n",
device_description);
printf("Error code: %d (%s)\n", i, ftdi_get_error_string(ftdi));
exit (-1);
}
}
else if (command > 0)
{
int vendor_id = cfg_getint(cfg, "vendor_id");
int product_id = cfg_getint(cfg, "product_id");
i = ftdi_usb_open(ftdi, vendor_id, product_id);
if (i != 0)
{
int default_pid = cfg_getint(cfg, "default_pid");
printf("Unable to find FTDI devices under given vendor/product id: 0x%X/0x%X\n", vendor_id, product_id);
printf("Error code: %d (%s)\n", i, ftdi_get_error_string(ftdi));
printf("Retrying with default FTDI pid=%#04x.\n", default_pid);
i = ftdi_usb_open(ftdi, 0x0403, default_pid);
if (i != 0)
{
printf("Error: %s\n", ftdi->error_str);
exit (-1);
}
}
}
ftdi_eeprom_initdefaults (ftdi, cfg_getstr(cfg, "manufacturer"),
cfg_getstr(cfg, "product"),
cfg_getstr(cfg, "serial"));
printf("FTDI read eeprom: %d\n", ftdi_read_eeprom(ftdi));
eeprom_get_value(ftdi, CHIP_SIZE, &my_eeprom_size);
printf("EEPROM size: %d\n", my_eeprom_size);
if (command == COMMAND_READ)
{
ftdi_eeprom_decode(ftdi, 0 /* debug: 1 */);
eeprom_buf = malloc(my_eeprom_size);
ftdi_get_eeprom_buf(ftdi, eeprom_buf, my_eeprom_size);
if (eeprom_buf == NULL)
{
fprintf(stderr, "Malloc failed, aborting\n");
goto cleanup;
}
if (filename != NULL && strlen(filename) > 0)
{
FILE *fp = fopen (filename, "wb");
if(fp)
{
fwrite(eeprom_buf, 1, my_eeprom_size, fp);
fclose(fp);
}
else
fprintf(stderr, "Could not open output file %s: %s\n", filename, strerror(errno));
}
else
{
printf("Warning: Not writing eeprom, you must supply a valid filename\n");
}
goto cleanup;
}
eeprom_set_value(ftdi, VENDOR_ID, cfg_getint(cfg, "vendor_id"));
eeprom_set_value(ftdi, PRODUCT_ID, cfg_getint(cfg, "product_id"));
eeprom_set_value(ftdi, SELF_POWERED, cfg_getbool(cfg, "self_powered"));
eeprom_set_value(ftdi, REMOTE_WAKEUP, cfg_getbool(cfg, "remote_wakeup"));
eeprom_set_value(ftdi, MAX_POWER, cfg_getint(cfg, "max_power"));
eeprom_set_value(ftdi, IN_IS_ISOCHRONOUS, cfg_getbool(cfg, "in_is_isochronous"));
eeprom_set_value(ftdi, OUT_IS_ISOCHRONOUS, cfg_getbool(cfg, "out_is_isochronous"));
eeprom_set_value(ftdi, SUSPEND_PULL_DOWNS, cfg_getbool(cfg, "suspend_pull_downs"));
eeprom_set_value(ftdi, USE_SERIAL, cfg_getbool(cfg, "use_serial"));
eeprom_set_value(ftdi, USE_USB_VERSION, cfg_getbool(cfg, "change_usb_version"));
eeprom_set_value(ftdi, USB_VERSION, cfg_getint(cfg, "usb_version"));
eeprom_set_value(ftdi, CHIP_TYPE, cfg_getint(cfg, "eeprom_type"));
eeprom_set_value(ftdi, HIGH_CURRENT, cfg_getbool(cfg, "high_current"));
if (ftdi->type == TYPE_R)
{
if (cfg_getint(cfg, "cbus0") != -1)
eeprom_set_value(ftdi, CBUS_FUNCTION_0, cfg_getint(cfg, "cbus0"));
if (cfg_getint(cfg, "cbus1") != -1)
eeprom_set_value(ftdi, CBUS_FUNCTION_1, cfg_getint(cfg, "cbus1"));
if (cfg_getint(cfg, "cbus2") != -1)
eeprom_set_value(ftdi, CBUS_FUNCTION_2, cfg_getint(cfg, "cbus2"));
if (cfg_getint(cfg, "cbus3") != -1)
eeprom_set_value(ftdi, CBUS_FUNCTION_3, cfg_getint(cfg, "cbus3"));
if (cfg_getint(cfg, "cbus4") != -1)
eeprom_set_value(ftdi, CBUS_FUNCTION_4, cfg_getint(cfg, "cbus4"));
}
else if (ftdi->type == TYPE_232H)
{
if (cfg_getint(cfg, "cbush0") != -1)
eeprom_set_value(ftdi, CBUS_FUNCTION_0, cfg_getint(cfg, "cbush0"));
if (cfg_getint(cfg, "cbush1") != -1)
eeprom_set_value(ftdi, CBUS_FUNCTION_1, cfg_getint(cfg, "cbush1"));
if (cfg_getint(cfg, "cbush2") != -1)
eeprom_set_value(ftdi, CBUS_FUNCTION_2, cfg_getint(cfg, "cbush2"));
if (cfg_getint(cfg, "cbush3") != -1)
eeprom_set_value(ftdi, CBUS_FUNCTION_3, cfg_getint(cfg, "cbush3"));
if (cfg_getint(cfg, "cbush4") != -1)
eeprom_set_value(ftdi, CBUS_FUNCTION_4, cfg_getint(cfg, "cbush4"));
if (cfg_getint(cfg, "cbush5") != -1)
eeprom_set_value(ftdi, CBUS_FUNCTION_5, cfg_getint(cfg, "cbush5"));
if (cfg_getint(cfg, "cbush6") != -1)
eeprom_set_value(ftdi, CBUS_FUNCTION_6, cfg_getint(cfg, "cbush6"));
if (cfg_getint(cfg, "cbush7") != -1)
eeprom_set_value(ftdi, CBUS_FUNCTION_7, cfg_getint(cfg, "cbush7"));
if (cfg_getint(cfg, "cbush8") != -1)
eeprom_set_value(ftdi, CBUS_FUNCTION_8, cfg_getint(cfg, "cbush8"));
if (cfg_getint(cfg, "cbush9") != -1)
eeprom_set_value(ftdi, CBUS_FUNCTION_9, cfg_getint(cfg, "cbush9"));
}
else if (ftdi->type == TYPE_230X)
{
if (cfg_getint(cfg, "cbusx0") != -1)
eeprom_set_value(ftdi, CBUS_FUNCTION_0, cfg_getint(cfg, "cbusx0"));
if (cfg_getint(cfg, "cbusx1") != -1)
eeprom_set_value(ftdi, CBUS_FUNCTION_1, cfg_getint(cfg, "cbusx1"));
if (cfg_getint(cfg, "cbusx2") != -1)
eeprom_set_value(ftdi, CBUS_FUNCTION_2, cfg_getint(cfg, "cbusx2"));
if (cfg_getint(cfg, "cbusx3") != -1)
eeprom_set_value(ftdi, CBUS_FUNCTION_3, cfg_getint(cfg, "cbusx3"));
}
int invert = 0;
if (cfg_getbool(cfg, "invert_rxd")) invert |= INVERT_RXD;
if (cfg_getbool(cfg, "invert_txd")) invert |= INVERT_TXD;
if (cfg_getbool(cfg, "invert_rts")) invert |= INVERT_RTS;
if (cfg_getbool(cfg, "invert_cts")) invert |= INVERT_CTS;
if (cfg_getbool(cfg, "invert_dtr")) invert |= INVERT_DTR;
if (cfg_getbool(cfg, "invert_dsr")) invert |= INVERT_DSR;
if (cfg_getbool(cfg, "invert_dcd")) invert |= INVERT_DCD;
if (cfg_getbool(cfg, "invert_ri")) invert |= INVERT_RI;
eeprom_set_value(ftdi, INVERT, invert);
if (cfg_getint(cfg, "cha_type") != -1)
eeprom_set_value(ftdi, CHANNEL_A_TYPE, cfg_getint(cfg, "cha_type"));
if (cfg_getint(cfg, "chb_type") != -1)
eeprom_set_value(ftdi, CHANNEL_B_TYPE, cfg_getint(cfg, "chb_type"));
eeprom_set_value(ftdi, CHANNEL_A_DRIVER,
cfg_getbool(cfg, "cha_vcp") ? DRIVER_VCP : 0);
eeprom_set_value(ftdi, CHANNEL_B_DRIVER,
cfg_getbool(cfg, "chb_vcp") ? DRIVER_VCP : 0);
eeprom_set_value(ftdi, CHANNEL_C_DRIVER,
cfg_getbool(cfg, "chc_vcp") ? DRIVER_VCP : 0);
eeprom_set_value(ftdi, CHANNEL_D_DRIVER,
cfg_getbool(cfg, "chd_vcp") ? DRIVER_VCP : 0);
eeprom_set_value(ftdi, CHANNEL_A_RS485, cfg_getbool(cfg, "cha_rs485"));
eeprom_set_value(ftdi, CHANNEL_B_RS485, cfg_getbool(cfg, "chb_rs485"));
eeprom_set_value(ftdi, CHANNEL_C_RS485, cfg_getbool(cfg, "chc_rs485"));
eeprom_set_value(ftdi, CHANNEL_D_RS485, cfg_getbool(cfg, "chd_rs485"));
/* Arbitrary user data */
eeprom_set_value(ftdi, USER_DATA_ADDR, cfg_getint(cfg, "user_data_addr"));
user_data_file = cfg_getstr(cfg, "user_data_file");
if (user_data_file && strlen(user_data_file) > 0)
{
int data_size;
struct stat st;
printf("User data file: %s\n", user_data_file);
/* Allocate a buffer for the user data */
user_data_buffer = (char *)malloc(max_eeprom_size);
if (user_data_buffer == NULL)
{
fprintf(stderr, "Malloc failed, aborting\n");
goto cleanup;
}
if (stat(user_data_file, &st))
{
printf ("Can't stat user data file %s.\n", user_data_file);
exit (-1);
}
if (st.st_size > max_eeprom_size)
printf("Warning: %s is too big, only reading %d bytes\n",
user_data_file, max_eeprom_size);
/* Read the user data file, no more than max_eeprom_size bytes */
FILE *fp = fopen(user_data_file, "rb");
if (fp == NULL)
{
printf ("Can't open user data file %s.\n", user_data_file);
exit (-1);
}
data_size = fread(user_data_buffer, 1, max_eeprom_size, fp);
fclose(fp);
if (data_size < 1)
{
printf ("Can't read user data file %s.\n", user_data_file);
exit (-1);
}
printf("User data size: %d\n", data_size);
ftdi_set_eeprom_user_data(ftdi, user_data_buffer, data_size);
}
if (command == COMMAND_ERASE)
{
printf("FTDI erase eeprom: %d\n", ftdi_erase_eeprom(ftdi));
}
size_check = ftdi_eeprom_build(ftdi);
eeprom_get_value(ftdi, CHIP_SIZE, &my_eeprom_size);
if (size_check == -1)
{
printf ("Sorry, the eeprom can only contain %d bytes.\n", my_eeprom_size);
goto cleanup;
}
else if (size_check < 0)
{
printf ("ftdi_eeprom_build(): error: %d\n", size_check);
goto cleanup;
}
else
{
printf ("Used eeprom space: %d bytes\n", my_eeprom_size-size_check);
}
if (command == COMMAND_FLASH)
{
if (cfg_getbool(cfg, "flash_raw"))
{
if (filename != NULL && strlen(filename) > 0)
{
eeprom_buf = malloc(max_eeprom_size);
FILE *fp = fopen(filename, "rb");
if (fp == NULL)
{
printf ("Can't open eeprom file %s.\n", filename);
exit (-1);
}
my_eeprom_size = fread(eeprom_buf, 1, max_eeprom_size, fp);
fclose(fp);
if (my_eeprom_size < 128)
{
printf ("Can't read eeprom file %s.\n", filename);
exit (-1);
}
printf("Flashing raw eeprom from file %s (%d bytes)\n",
filename, my_eeprom_size);
ftdi_set_eeprom_buf(ftdi, eeprom_buf, my_eeprom_size);
} else
{
printf ("ERROR: flash_raw mode enabled, but no eeprom filename "
"given in config file.\n");
exit (-1);
}
}
printf ("FTDI write eeprom: %d\n", ftdi_write_eeprom(ftdi));
libusb_reset_device(ftdi->usb_dev);
}
// Write to file?
if (filename != NULL && strlen(filename) > 0 && !cfg_getbool(cfg, "flash_raw"))
{
fp = fopen(filename, "w");
if (fp == NULL)
{
printf ("Can't write eeprom file.\n");
exit (-1);
}
else
printf ("Writing to file: %s\n", filename);
if (eeprom_buf == NULL)
eeprom_buf = malloc(my_eeprom_size);
ftdi_get_eeprom_buf(ftdi, eeprom_buf, my_eeprom_size);
fwrite(eeprom_buf, my_eeprom_size, 1, fp);
fclose(fp);
}
cleanup:
if (eeprom_buf)
free(eeprom_buf);
if (user_data_buffer)
free(user_data_buffer);
if (command > 0)
{
printf("FTDI close: %d\n", ftdi_usb_close(ftdi));
}
ftdi_deinit (ftdi);
ftdi_free (ftdi);
cfg_free(cfg);
printf("\n");
return 0;
}
+68
View File
@@ -0,0 +1,68 @@
# Check
set(FTDI_BUILD_CPP False PARENT_SCOPE)
option ( FTDIPP "Build C++ binding library libftdi1++" ON )
# Targets
set(cpp_sources ${CMAKE_CURRENT_SOURCE_DIR}/ftdi.cpp CACHE INTERNAL "List of cpp sources" )
set(cpp_headers ${CMAKE_CURRENT_SOURCE_DIR}/ftdi.hpp CACHE INTERNAL "List of cpp headers" )
if (FTDIPP)
if(Boost_FOUND)
# Includes
include_directories(BEFORE ${CMAKE_CURRENT_BINARY_DIR}
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_SOURCE_DIR}/src)
include_directories(${Boost_INCLUDE_DIRS})
set(FTDI_BUILD_CPP True PARENT_SCOPE)
message(STATUS "Building libftdi1++")
# Shared library
add_library(ftdipp1 SHARED ${cpp_sources})
math(EXPR VERSION_FIXUP "${MAJOR_VERSION} + 1") # Compatiblity with previous releases
set_target_properties(ftdipp1 PROPERTIES VERSION ${VERSION_FIXUP}.${MINOR_VERSION}.0 SOVERSION 3)
# Prevent clobbering each other during the build
set_target_properties(ftdipp1 PROPERTIES CLEAN_DIRECT_OUTPUT 1)
# Dependencies
target_link_libraries(ftdipp1 ftdi1 ${LIBUSB_LIBRARIES} ${BOOST_LIBRARIES})
install ( TARGETS ftdipp1
RUNTIME DESTINATION bin
LIBRARY DESTINATION lib${LIB_SUFFIX}
ARCHIVE DESTINATION lib${LIB_SUFFIX}
)
# Static library
if ( STATICLIBS )
add_library(ftdipp1-static STATIC ${cpp_sources})
set_target_properties(ftdipp1-static PROPERTIES OUTPUT_NAME "ftdipp1")
set_target_properties(ftdipp1-static PROPERTIES CLEAN_DIRECT_OUTPUT 1)
install ( TARGETS ftdipp1-static
ARCHIVE DESTINATION lib${LIB_SUFFIX}
COMPONENT staticlibs
)
endif ()
install ( FILES ${cpp_headers}
DESTINATION include/${PROJECT_NAME}
COMPONENT headers
)
else ()
message(STATUS "Boost not found, won't build libftdi1++")
endif ()
else ()
message(STATUS "Not building libftdi1++")
endif ()
+675
View File
@@ -0,0 +1,675 @@
/***************************************************************************
ftdi.cpp - C++ wraper for libftdi
-------------------
begin : Mon Oct 13 2008
copyright : (C) 2008-2017 by Marek Vavruša / libftdi developers
email : opensource@intra2net.com and marek@vavrusa.com
***************************************************************************/
/*
Copyright (C) 2008-2017 by Marek Vavruša / libftdi developers
The software in this package is distributed under the GNU General
Public License version 2 (with a special exception described below).
A copy of GNU General Public License (GPL) is included in this distribution,
in the file COPYING.GPL.
As a special exception, if other files instantiate templates or use macros
or inline functions from this file, or you compile this file and link it
with other works to produce a work based on this file, this file
does not by itself cause the resulting work to be covered
by the GNU General Public License.
However the source code for this file must still be made available
in accordance with section (3) of the GNU General Public License.
This exception does not invalidate any other reasons why a work based
on this file might be covered by the GNU General Public License.
*/
#include <libusb.h>
#include "ftdi.hpp"
#include "ftdi_i.h"
#include "ftdi.h"
namespace Ftdi
{
class Context::Private
{
public:
Private()
: open(false), ftdi(0), dev(0)
{
ftdi = ftdi_new();
}
~Private()
{
if (open)
ftdi_usb_close(ftdi);
ftdi_free(ftdi);
}
bool open;
struct ftdi_context* ftdi;
struct libusb_device* dev;
std::string vendor;
std::string description;
std::string serial;
};
/*! \brief Constructor.
*/
Context::Context()
: d( new Private() )
{
}
/*! \brief Destructor.
*/
Context::~Context()
{
}
bool Context::is_open()
{
return d->open;
}
int Context::open(int vendor, int product)
{
// Open device
int ret = ftdi_usb_open(d->ftdi, vendor, product);
if (ret < 0)
return ret;
return get_strings_and_reopen(false,false,false);
}
int Context::open(int vendor, int product, const std::string& description, const std::string& serial, unsigned int index)
{
// translate empty strings to NULL
// -> do not use them to find the device (vs. require an empty string to be set in the EEPROM)
const char* c_description=NULL;
const char* c_serial=NULL;
if (!description.empty())
c_description=description.c_str();
if (!serial.empty())
c_serial=serial.c_str();
int ret = ftdi_usb_open_desc_index(d->ftdi, vendor, product, c_description, c_serial, index);
if (ret < 0)
return ret;
return get_strings_and_reopen(false,!description.empty(),!serial.empty());
}
int Context::open(const std::string& description)
{
int ret = ftdi_usb_open_string(d->ftdi, description.c_str());
if (ret < 0)
return ret;
return get_strings_and_reopen(false,true,false);
}
int Context::open(struct libusb_device *dev)
{
if (dev != 0)
d->dev = dev;
if (d->dev == 0)
return -1;
return get_strings_and_reopen();
}
int Context::close()
{
d->open = false;
d->dev = 0;
return ftdi_usb_close(d->ftdi);
}
int Context::reset()
{
return ftdi_usb_reset(d->ftdi);
}
int Context::flush(int mask)
{
int ret;
switch (mask & (Input | Output)) {
case Input:
ret = ftdi_usb_purge_rx_buffer(d->ftdi);
break;
case Output:
ret = ftdi_usb_purge_tx_buffer(d->ftdi);
break;
case Input | Output:
ret = ftdi_usb_purge_buffers(d->ftdi);
break;
default:
// Emulate behavior of previous version.
ret = 1;
break;
}
return ret;
}
int Context::set_interface(enum ftdi_interface interface)
{
return ftdi_set_interface(d->ftdi, interface);
}
void Context::set_usb_device(struct libusb_device_handle *dev)
{
ftdi_set_usbdev(d->ftdi, dev);
d->dev = libusb_get_device(dev);
}
int Context::set_baud_rate(int baudrate)
{
return ftdi_set_baudrate(d->ftdi, baudrate);
}
int Context::set_line_property(enum ftdi_bits_type bits, enum ftdi_stopbits_type sbit, enum ftdi_parity_type parity)
{
return ftdi_set_line_property(d->ftdi, bits, sbit, parity);
}
int 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)
{
return ftdi_set_line_property2(d->ftdi, bits, sbit, parity, break_type);
}
int Context::get_usb_read_timeout() const
{
return d->ftdi->usb_read_timeout;
}
void Context::set_usb_read_timeout(int usb_read_timeout)
{
d->ftdi->usb_read_timeout = usb_read_timeout;
}
int Context::get_usb_write_timeout() const
{
return d->ftdi->usb_write_timeout;
}
void Context::set_usb_write_timeout(int usb_write_timeout)
{
d->ftdi->usb_write_timeout = usb_write_timeout;
}
int Context::read(unsigned char *buf, int size)
{
return ftdi_read_data(d->ftdi, buf, size);
}
int Context::set_read_chunk_size(unsigned int chunksize)
{
return ftdi_read_data_set_chunksize(d->ftdi, chunksize);
}
int Context::read_chunk_size()
{
unsigned chunk = -1;
if (ftdi_read_data_get_chunksize(d->ftdi, &chunk) < 0)
return -1;
return chunk;
}
int Context::write(const unsigned char *buf, int size)
{
return ftdi_write_data(d->ftdi, buf, size);
}
int Context::set_write_chunk_size(unsigned int chunksize)
{
return ftdi_write_data_set_chunksize(d->ftdi, chunksize);
}
int Context::write_chunk_size()
{
unsigned chunk = -1;
if (ftdi_write_data_get_chunksize(d->ftdi, &chunk) < 0)
return -1;
return chunk;
}
int Context::set_flow_control(int flowctrl)
{
return ftdi_setflowctrl(d->ftdi, flowctrl);
}
int Context::set_modem_control(int mask)
{
int dtr = 0, rts = 0;
if (mask & Dtr)
dtr = 1;
if (mask & Rts)
rts = 1;
return ftdi_setdtr_rts(d->ftdi, dtr, rts);
}
int Context::set_dtr(bool state)
{
return ftdi_setdtr(d->ftdi, state);
}
int Context::set_rts(bool state)
{
return ftdi_setrts(d->ftdi, state);
}
int Context::set_latency(unsigned char latency)
{
return ftdi_set_latency_timer(d->ftdi, latency);
}
unsigned Context::latency()
{
unsigned char latency = 0;
ftdi_get_latency_timer(d->ftdi, &latency);
return latency;
}
unsigned short Context::poll_modem_status()
{
unsigned short status = 0;
ftdi_poll_modem_status(d->ftdi, &status);
return status;
}
int Context::set_event_char(unsigned char eventch, unsigned char enable)
{
return ftdi_set_event_char(d->ftdi, eventch, enable);
}
int Context::set_error_char(unsigned char errorch, unsigned char enable)
{
return ftdi_set_error_char(d->ftdi, errorch, enable);
}
int Context::set_bitmode(unsigned char bitmask, unsigned char mode)
{
return ftdi_set_bitmode(d->ftdi, bitmask, mode);
}
int Context::set_bitmode(unsigned char bitmask, enum ftdi_mpsse_mode mode)
{
return ftdi_set_bitmode(d->ftdi, bitmask, mode);
}
int Context::bitbang_disable()
{
return ftdi_disable_bitbang(d->ftdi);
}
int Context::read_pins(unsigned char *pins)
{
return ftdi_read_pins(d->ftdi, pins);
}
const char* Context::error_string()
{
return ftdi_get_error_string(d->ftdi);
}
int Context::get_strings(bool vendor, bool description, bool serial)
{
// Prepare buffers
char ivendor[512], idesc[512], iserial[512];
int ret = ftdi_usb_get_strings(d->ftdi, d->dev, vendor?ivendor:NULL, 512, description?idesc:NULL, 512, serial?iserial:NULL, 512);
if (ret < 0)
return -1;
d->vendor = ivendor;
d->description = idesc;
d->serial = iserial;
return 1;
}
int Context::get_strings_and_reopen(bool vendor, bool description, bool serial)
{
int ret = 0;
if(vendor || description || serial)
{
if (d->dev == 0)
{
d->dev = libusb_get_device(d->ftdi->usb_dev);
}
// Get device strings (closes device)
ret=get_strings(vendor, description, serial);
if (ret < 0)
{
d->open = 0;
return ret;
}
// Reattach device
ret = ftdi_usb_open_dev(d->ftdi, d->dev);
d->open = (ret >= 0);
}
return ret;
}
/*! \brief Device strings properties.
*/
const std::string& Context::vendor()
{
if(d->vendor.empty())
get_strings_and_reopen(true,false,false);
return d->vendor;
}
/*! \brief Device strings properties.
*/
const std::string& Context::description()
{
if(d->description.empty())
get_strings_and_reopen(false,true,false);
return d->description;
}
/*! \brief Device strings properties.
*/
const std::string& Context::serial()
{
if(d->serial.empty())
get_strings_and_reopen(false,false,true);
return d->serial;
}
void Context::set_context(struct ftdi_context* context)
{
ftdi_free(d->ftdi);
d->ftdi = context;
}
void Context::set_usb_device(struct libusb_device *dev)
{
d->dev = dev;
}
struct ftdi_context* Context::context()
{
return d->ftdi;
}
class Eeprom::Private
{
public:
Private()
: context(0)
{}
struct ftdi_eeprom eeprom;
struct ftdi_context* context;
};
Eeprom::Eeprom(Context* parent)
: d ( new Private() )
{
d->context = parent->context();
}
Eeprom::~Eeprom()
{
}
int Eeprom::init_defaults(char* manufacturer, char *product, char * serial)
{
return ftdi_eeprom_initdefaults(d->context, manufacturer, product, serial);
}
int Eeprom::chip_id(unsigned int *chipid)
{
return ftdi_read_chipid(d->context, chipid);
}
int Eeprom::build(unsigned char *output)
{
return ftdi_eeprom_build(d->context);
}
int Eeprom::read(unsigned char *eeprom)
{
return ftdi_read_eeprom(d->context);
}
int Eeprom::write(unsigned char *eeprom)
{
return ftdi_write_eeprom(d->context);
}
int Eeprom::read_location(int eeprom_addr, unsigned short *eeprom_val)
{
return ftdi_read_eeprom_location(d->context, eeprom_addr, eeprom_val);
}
int Eeprom::write_location(int eeprom_addr, unsigned short eeprom_val)
{
return ftdi_write_eeprom_location(d->context, eeprom_addr, eeprom_val);
}
int Eeprom::erase()
{
return ftdi_erase_eeprom(d->context);
}
class List::Private
{
public:
Private(struct ftdi_device_list* _devlist)
: devlist(_devlist)
{}
~Private()
{
if(devlist)
ftdi_list_free(&devlist);
}
std::list<Context> list;
struct ftdi_device_list* devlist;
};
List::List(struct ftdi_device_list* devlist)
: d( new Private(devlist) )
{
if (devlist != 0)
{
// Iterate list
for (; devlist != 0; devlist = devlist->next)
{
Context c;
c.set_usb_device(devlist->dev);
c.get_strings();
d->list.push_back(c);
}
}
}
List::~List()
{
}
/**
* Return begin iterator for accessing the contained list elements
* @return Iterator
*/
List::iterator List::begin()
{
return d->list.begin();
}
/**
* Return end iterator for accessing the contained list elements
* @return Iterator
*/
List::iterator List::end()
{
return d->list.end();
}
/**
* Return begin iterator for accessing the contained list elements
* @return Const iterator
*/
List::const_iterator List::begin() const
{
return d->list.begin();
}
/**
* Return end iterator for accessing the contained list elements
* @return Const iterator
*/
List::const_iterator List::end() const
{
return d->list.end();
}
/**
* Return begin reverse iterator for accessing the contained list elements
* @return Reverse iterator
*/
List::reverse_iterator List::rbegin()
{
return d->list.rbegin();
}
/**
* Return end reverse iterator for accessing the contained list elements
* @return Reverse iterator
*/
List::reverse_iterator List::rend()
{
return d->list.rend();
}
/**
* Return begin reverse iterator for accessing the contained list elements
* @return Const reverse iterator
*/
List::const_reverse_iterator List::rbegin() const
{
return d->list.rbegin();
}
/**
* Return end reverse iterator for accessing the contained list elements
* @return Const reverse iterator
*/
List::const_reverse_iterator List::rend() const
{
return d->list.rend();
}
/**
* Get number of elements stored in the list
* @return Number of elements
*/
List::ListType::size_type List::size() const
{
return d->list.size();
}
/**
* Check if list is empty
* @return True if empty, false otherwise
*/
bool List::empty() const
{
return d->list.empty();
}
/**
* Removes all elements. Invalidates all iterators.
* Do it in a non-throwing way and also make
* sure we really free the allocated memory.
*/
void List::clear()
{
ListType().swap(d->list);
// Free device list
if (d->devlist)
{
ftdi_list_free(&d->devlist);
d->devlist = 0;
}
}
/**
* Appends a copy of the element as the new last element.
* @param element Value to copy and append
*/
void List::push_back(const Context& element)
{
d->list.push_back(element);
}
/**
* Adds a copy of the element as the new first element.
* @param element Value to copy and add
*/
void List::push_front(const Context& element)
{
d->list.push_front(element);
}
/**
* Erase one element pointed by iterator
* @param pos Element to erase
* @return Position of the following element (or end())
*/
List::iterator List::erase(iterator pos)
{
return d->list.erase(pos);
}
/**
* Erase a range of elements
* @param beg Begin of range
* @param end End of range
* @return Position of the element after the erased range (or end())
*/
List::iterator List::erase(iterator beg, iterator end)
{
return d->list.erase(beg, end);
}
List* List::find_all(Context &context, int vendor, int product)
{
struct ftdi_device_list* dlist = 0;
ftdi_usb_find_all(context.context(), &dlist, vendor, product);
return new List(dlist);
}
}
+221
View File
@@ -0,0 +1,221 @@
/***************************************************************************
ftdi.hpp - C++ wrapper for libftdi
-------------------
begin : Mon Oct 13 2008
copyright : (C) 2008-2017 by Marek Vavruša and libftdi developers
email : opensource@intra2net.com and marek@vavrusa.com
***************************************************************************/
/*
Copyright (C) 2008-2017 by Marek Vavruša and libftdi developers
The software in this package is distributed under the GNU General
Public License version 2 (with a special exception described below).
A copy of GNU General Public License (GPL) is included in this distribution,
in the file COPYING.GPL.
As a special exception, if other files instantiate templates or use macros
or inline functions from this file, or you compile this file and link it
with other works to produce a work based on this file, this file
does not by itself cause the resulting work to be covered
by the GNU General Public License.
However the source code for this file must still be made available
in accordance with section (3) of the GNU General Public License.
This exception does not invalidate any other reasons why a work based
on this file might be covered by the GNU General Public License.
*/
#ifndef __libftdi_hpp__
#define __libftdi_hpp__
#include <list>
#include <string>
#include <boost/shared_ptr.hpp>
#include <ftdi.h>
namespace Ftdi
{
/* Forward declarations*/
class List;
class Eeprom;
/*! \brief FTDI device context.
* Represents single FTDI device context.
*/
class Context
{
/* Friends */
friend class Eeprom;
friend class List;
public:
/*! \brief Direction flags for flush().
*/
enum Direction
{
Input = 0x2,
Output = 0x1,
};
/*! \brief Modem control flags.
*/
enum ModemCtl
{
Dtr = 0x2,
Rts = 0x1,
};
/* Constructor, Destructor */
Context();
~Context();
/* Properties */
Eeprom* eeprom();
const std::string& vendor();
const std::string& description();
const std::string& serial();
/* Device manipulators */
bool is_open();
int open(struct libusb_device *dev = 0);
int open(int vendor, int product);
int open(int vendor, int product, const std::string& description, const std::string& serial = std::string(), unsigned int index=0);
int open(const std::string& description);
int close();
int reset();
int flush(int mask = Input|Output);
int set_interface(enum ftdi_interface interface);
void set_usb_device(struct libusb_device_handle *dev);
/* Line manipulators */
int set_baud_rate(int baudrate);
int set_line_property(enum ftdi_bits_type bits, enum ftdi_stopbits_type sbit, enum ftdi_parity_type parity);
int set_line_property(enum ftdi_bits_type bits, enum ftdi_stopbits_type sbit, enum ftdi_parity_type parity, enum ftdi_break_type break_type);
int get_usb_read_timeout() const;
void set_usb_read_timeout(int usb_read_timeout);
int get_usb_write_timeout() const;
void set_usb_write_timeout(int usb_write_timeout);
/* I/O */
int read(unsigned char *buf, int size);
int write(const unsigned char *buf, int size);
int set_read_chunk_size(unsigned int chunksize);
int set_write_chunk_size(unsigned int chunksize);
int read_chunk_size();
int write_chunk_size();
/* Async IO
TODO: should wrap?
int writeAsync(const unsigned char *buf, int size);
void asyncComplete(int wait_for_more);
*/
/* Flow control */
int set_event_char(unsigned char eventch, unsigned char enable);
int set_error_char(unsigned char errorch, unsigned char enable);
int set_flow_control(int flowctrl);
int set_modem_control(int mask = Dtr|Rts);
int set_latency(unsigned char latency);
int set_dtr(bool state);
int set_rts(bool state);
unsigned short poll_modem_status();
unsigned latency();
/* BitBang mode */
int set_bitmode(unsigned char bitmask, unsigned char mode);
int set_bitmode(unsigned char bitmask, enum ftdi_mpsse_mode mode);
int bitbang_disable();
int read_pins(unsigned char *pins);
/* Misc */
const char* error_string();
protected:
int get_strings(bool vendor=true, bool description=true, bool serial=true);
int get_strings_and_reopen(bool vendor=true, bool description=true, bool serial=true);
/* Properties */
struct ftdi_context* context();
void set_context(struct ftdi_context* context);
void set_usb_device(struct libusb_device *dev);
private:
class Private;
boost::shared_ptr<Private> d;
};
/*! \brief Device EEPROM.
*/
class Eeprom
{
public:
Eeprom(Context* parent);
~Eeprom();
int init_defaults(char *manufacturer, char* product, char * serial);
int chip_id(unsigned int *chipid);
int build(unsigned char *output);
int read(unsigned char *eeprom);
int write(unsigned char *eeprom);
int read_location(int eeprom_addr, unsigned short *eeprom_val);
int write_location(int eeprom_addr, unsigned short eeprom_val);
int erase();
private:
class Private;
boost::shared_ptr<Private> d;
};
/*! \brief Device list.
*/
class List
{
public:
List(struct ftdi_device_list* devlist = 0);
~List();
static List* find_all(Context &context, int vendor, int product);
/// List type storing "Context" objects
typedef std::list<Context> ListType;
/// Iterator type for the container
typedef ListType::iterator iterator;
/// Const iterator type for the container
typedef ListType::const_iterator const_iterator;
/// Reverse iterator type for the container
typedef ListType::reverse_iterator reverse_iterator;
/// Const reverse iterator type for the container
typedef ListType::const_reverse_iterator const_reverse_iterator;
iterator begin();
iterator end();
const_iterator begin() const;
const_iterator end() const;
reverse_iterator rbegin();
reverse_iterator rend();
const_reverse_iterator rbegin() const;
const_reverse_iterator rend() const;
ListType::size_type size() const;
bool empty() const;
void clear();
void push_back(const Context& element);
void push_front(const Context& element);
iterator erase(iterator pos);
iterator erase(iterator beg, iterator end);
private:
class Private;
boost::shared_ptr<Private> d;
};
}
#endif
+3
View File
@@ -0,0 +1,3 @@
[Project]
Manager=KDevCMakeManager
Name=libftdi-1.0
+28
View File
@@ -0,0 +1,28 @@
// PC-Lint 9.00 settings
--iz:\usr\include\libusb-1.0
--i../src
--i../ftdipp
-emacro(527, ftdi_error_return) // ignore "unreachable code"
-emacro(717, ftdi_error_return)
-epu // Pointer to unsigned/signed of the same type is ok
+fie // Allow enum to int conversion
-ecall(534, usb_close) // silence ignored return value from usb_close
// Disable bogus BOOST warnings
-emacro(58,BOOST_ASSERT)
-emacro(506, BOOST_FOREACH)
-emacro(666, BOOST_FOREACH)
-esym(666, BOOST_FOREACH)
-emacro(1023, BOOST_FOREACH)
-emacro(1793, BOOST_FOREACH)
-esym(665, BOOST_FOREACH)
-e123
// Don't complain we are running with -wlib(0)
// as the boost headers can't be parsed properly
-estring(686, -wlib(0))
-wlib(0)
+79
View File
@@ -0,0 +1,79 @@
#!/bin/sh
prefix=@prefix@
exec_prefix=@exec_prefix@
exec_prefix_set=no
usage()
{
cat <<EOF
Usage: libftdi1-config [OPTIONS] [LIBRARIES]
Options:
[--prefix[=DIR]]
[--exec-prefix[=DIR]]
[--version]
[--libs]
[--cflags]
EOF
exit $1
}
if test $# -eq 0; then
usage 1 1>&2
fi
while test $# -gt 0; do
case "$1" in
-*=*) optarg=`echo "$1" | sed 's/[-_a-zA-Z0-9]*=//'` ;;
*) optarg= ;;
esac
case $1 in
--prefix=*)
prefix=$optarg
if test $exec_prefix_set = no ; then
exec_prefix=$optarg
fi
;;
--prefix)
echo_prefix=yes
;;
--exec-prefix=*)
exec_prefix=$optarg
exec_prefix_set=yes
;;
--exec-prefix)
echo_exec_prefix=yes
;;
--version)
echo @VERSION@
exit 0
;;
--cflags)
if test "@includedir@" != /usr/include ; then
includes="-I@includedir@"
fi
echo_cflags=yes
;;
--libs)
echo_libs=yes
;;
*)
usage 1 1>&2
;;
esac
shift
done
if test "$echo_prefix" = "yes"; then
echo $prefix
fi
if test "$echo_exec_prefix" = "yes"; then
echo $exec_prefix
fi
if test "$echo_cflags" = "yes"; then
echo $includes
fi
if test "$echo_libs" = "yes"; then
echo -L@libdir@ -lftdi1 @LIBS@
fi
+11
View File
@@ -0,0 +1,11 @@
prefix=@prefix@
exec_prefix=@exec_prefix@
libdir=@libdir@
includedir=@includedir@
Name: libftdi1
Description: Library to program and control the FTDI USB controller
Requires: libusb-1.0
Version: @VERSION@
Libs: -L${libdir} -lftdi1
Cflags: -I${includedir}
+100
View File
@@ -0,0 +1,100 @@
%{!?python_sitearch: %define python_sitearch %(%{__python} -c "from distutils.sysconfig import get_python_lib; print get_python_lib(1)")}
Summary: Library to program and control the FTDI USB controller
Name: libftdi1
Version: @VERSION@
Release: 1
License: LGPL for libftdi and GPLv2+linking exception for the C++ wrapper
Group: System Environment/Libraries
Vendor: Intra2net AG
Source: https://www.intra2net.com/en/developer/libftdi/download/%{name}-%{version}.tar.bz2
Buildroot: /tmp/%{name}-%{version}-root
Requires: libusb1
BuildRequires: libusb1, libusb1-devel, pkgconfig, doxygen
BuildRequires: swig python-devel
Prefix: /usr
URL: https://www.intra2net.com/en/developer/libftdi
%package devel
Summary: Header files and static libraries for libftdi1
Group: Development/Libraries
Requires: libftdi1 = %{version}, libusb1-devel
%package python
Summary: Python bindings for libftdi
License: LGPL
Group: Development/Libraries
Requires: %{name} = %{version}-%{release}
%description
Library to program and control the FTDI USB controller
%description devel
Header files and static libraries for libftdi1
%description python
Python bindings for libftdi1 generated by SWIG
%prep
%setup -q
%build
mkdir build
cd build
export CFLAGS="$RPM_OPT_FLAGS"
export CXXFLAGS="$RPM_OPT_FLAGS"
cmake -DCMAKE_INSTALL_PREFIX="%{prefix}" ../
make %{?_smp_mflags}
%install
cd build
make DESTDIR=$RPM_BUILD_ROOT install
# Remove example programs
rm -f $RPM_BUILD_ROOT/usr/bin/simple
rm -f $RPM_BUILD_ROOT/usr/bin/bitbang
rm -f $RPM_BUILD_ROOT/usr/bin/bitbang2
rm -f $RPM_BUILD_ROOT/usr/bin/bitbang_ft2232
rm -f $RPM_BUILD_ROOT/usr/bin/bitbang_cbus
rm -f $RPM_BUILD_ROOT/usr/bin/find_all
rm -f $RPM_BUILD_ROOT/usr/bin/find_all_pp
rm -f $RPM_BUILD_ROOT/usr/bin/serial_test
rm -f $RPM_BUILD_ROOT/usr/bin/baud_test
# Clean python compiled files in examples dir
find $RPM_BUILD_ROOT%{prefix}/share/libftdi/examples -name "*.pyc" -or -name "*.pyo" -exec rm -f \{\} \;
# move documentation to version specific directory
# Is there an easy way in cmake to set the DOCDIR?
mkdir -p $RPM_BUILD_ROOT%{prefix}/share/doc/%{name}-%{version}
mv $RPM_BUILD_ROOT%{prefix}/share/doc/%{name}/* $RPM_BUILD_ROOT%{prefix}/share/doc/%{name}-%{version}
%clean
rm -fr $RPM_BUILD_ROOT
%files
%defattr(-,root,root)
%doc COPYING.LIB COPYING.GPL LICENSE
%{_libdir}/libftdi1*.so*
%{_libdir}/libftdipp1*.so*
%files devel
%defattr(-,root,root)
%doc build/doc/html build/doc/man
%{_bindir}/ftdi_eeprom
%{_bindir}/libftdi1-config
%{prefix}/include/libftdi1/*.h
%{prefix}/include/libftdi1/*.hpp
%{prefix}/share/libftdi/examples/*
%{_libdir}/libftdi1*.*a
%{_libdir}/libftdipp1*.*a
%{_libdir}/pkgconfig/*.pc
%{_libdir}/cmake/libftdi1/*
%files python
%defattr(-,root,root,-)
%attr(755,root,root) %{python_sitearch}/_ftdi1.so
%{python_sitearch}/ftdi1.py*
+11
View File
@@ -0,0 +1,11 @@
prefix=@prefix@
exec_prefix=@exec_prefix@
libdir=@libdir@
includedir=@includedir@
Name: libftdipp1
Description: C++ wrapper for libftdi1
Requires: libftdi1
Version: @VERSION@
Libs: -L${libdir} -lftdipp1
Cflags: -I${includedir}
+14
View File
@@ -0,0 +1,14 @@
# FTDI Devices: FT232BM/L/Q, FT245BM/L/Q, FT232RL/Q, FT245RL/Q, VNC1L with VDPS Firmware
SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6001", MODE="0664", GROUP="plugdev"
# FTDI Devices: FT2232C/D/L, FT2232HL/Q
SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6010", MODE="0664", GROUP="plugdev"
# FTDI Devices: FT4232HL/Q
SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6011", MODE="0664", GROUP="plugdev"
# FTDI Devices: FT232H
SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6014", MODE="0664", GROUP="plugdev"
# FTDI Devices: FT230X
SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6015", MODE="0664", GROUP="plugdev"
+21
View File
@@ -0,0 +1,21 @@
# Debian
if("${PACKAGE}" STREQUAL "Debian")
# Settings
set(REVISION 0)
set(CPACK_GENERATOR "DEB" PARENT_SCOPE)
set(CPACK_PACKAGE_VERSION ${CPACK_PACKAGE_VERSION}-${REVISION} PARENT_SCOPE)
# Dependencies
set(CPACK_DEBIAN_PACKAGE_DEPENDS "libusb-1.0-0" PARENT_SCOPE)
set(DEBIAN_PACKAGE_BUILDS_DEPENDS "cmake, libusb2-dev" PARENT_SCOPE)
# Bundles
message("-- Installing udev rules to /etc/udev/rules.d")
install(FILES 99-libftdi.rules
DESTINATION /etc/udev/rules.d)
endif("${PACKAGE}" STREQUAL "Debian")
# General RPM rules
set(CPACK_RPM_PACKAGE_DEPENDS "libusb1" PARENT_SCOPE)
+81
View File
@@ -0,0 +1,81 @@
option ( PYTHON_BINDINGS "Build python bindings via swig" ON )
option ( LINK_PYTHON_LIBRARY "Link against python libraries" ON )
if ( PYTHON_BINDINGS )
# workaround for cmake bug #0013449
if ( NOT DEFINED CMAKE_FIND_ROOT_PATH )
find_package ( SWIG )
else ()
find_program ( SWIG_EXECUTABLE NAMES swig2.0 swig )
if ( SWIG_EXECUTABLE )
set ( SWIG_USE_FILE ${CMAKE_ROOT}/Modules/UseSWIG.cmake )
set ( SWIG_FOUND TRUE )
endif ()
endif ()
find_package ( PythonLibs )
find_package ( PythonInterp )
endif ()
if ( SWIG_FOUND AND PYTHONLIBS_FOUND AND PYTHONINTERP_FOUND )
include ( UseSWIG )
include_directories ( BEFORE ${CMAKE_SOURCE_DIR}/src )
include_directories ( ${PYTHON_INCLUDE_DIRS} )
link_directories ( ${CMAKE_CURRENT_BINARY_DIR}/../src )
if ( DOCUMENTATION AND DOXYGEN_FOUND )
set(CMAKE_SWIG_FLAGS -DDOXYGEN=${DOXYGEN_FOUND})
endif()
swig_add_module ( ftdi1 python ftdi1.i )
swig_link_libraries ( ftdi1 ftdi1 )
if ( LINK_PYTHON_LIBRARY )
swig_link_libraries ( ftdi1 ${PYTHON_LIBRARIES} )
elseif( APPLE )
set_target_properties ( ${SWIG_MODULE_ftdi1_REAL_NAME} PROPERTIES LINK_FLAGS "-undefined dynamic_lookup" )
endif ()
set_target_properties ( ${SWIG_MODULE_ftdi1_REAL_NAME} PROPERTIES NO_SONAME ON )
execute_process ( COMMAND ${PYTHON_EXECUTABLE} -c "from distutils import sysconfig; print( sysconfig.get_python_lib( plat_specific=True, prefix='${CMAKE_INSTALL_PREFIX}' ) )"
OUTPUT_VARIABLE _ABS_PYTHON_MODULE_PATH
OUTPUT_STRIP_TRAILING_WHITESPACE )
get_filename_component ( _ABS_PYTHON_MODULE_PATH ${_ABS_PYTHON_MODULE_PATH} ABSOLUTE )
file ( RELATIVE_PATH _REL_PYTHON_MODULE_PATH ${CMAKE_INSTALL_PREFIX} ${_ABS_PYTHON_MODULE_PATH} )
set ( PYTHON_MODULE_PATH
${_REL_PYTHON_MODULE_PATH}
)
install ( FILES ${CMAKE_CURRENT_BINARY_DIR}/ftdi1.py DESTINATION ${PYTHON_MODULE_PATH} )
install ( TARGETS ${SWIG_MODULE_ftdi1_REAL_NAME} LIBRARY DESTINATION ${PYTHON_MODULE_PATH} )
if ( DOCUMENTATION AND DOXYGEN_FOUND )
# Run doxygen to only generate the xml
add_custom_command ( OUTPUT ${CMAKE_BINARY_DIR}/doc/xml/ftdi_8c.xml
COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_BINARY_DIR}/doc
COMMAND ${DOXYGEN_EXECUTABLE} ${CMAKE_BINARY_DIR}/Doxyfile.xml
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
DEPENDS ${c_headers};${c_sources};${cpp_sources};${cpp_headers}
)
# generate .i from doxygen .xml
add_custom_command ( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/ftdi1_doc.i
COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/doxy2swig.py -n
${CMAKE_BINARY_DIR}/doc/xml/ftdi_8c.xml
${CMAKE_CURRENT_BINARY_DIR}/ftdi1_doc.i
DEPENDS ${CMAKE_BINARY_DIR}/doc/xml/ftdi_8c.xml
)
add_custom_target ( doc_i DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/ftdi1_doc.i )
add_dependencies( ${SWIG_MODULE_ftdi1_REAL_NAME} doc_i )
endif ()
set ( LIBFTDI_PYTHON_MODULE_PATH ${CMAKE_INSTALL_PREFIX}/${PYTHON_MODULE_PATH} )
set ( LIBFTDI_PYTHON_MODULE_PATH ${LIBFTDI_PYTHON_MODULE_PATH} PARENT_SCOPE ) # for ftdiconfig.cmake
message(STATUS "Building python bindings via swig. Will be installed under ${LIBFTDI_PYTHON_MODULE_PATH}")
add_subdirectory ( examples )
else ()
message(STATUS "Not building python bindings")
endif ()
+457
View File
@@ -0,0 +1,457 @@
#!/usr/bin/env python
"""Doxygen XML to SWIG docstring converter.
Usage:
doxy2swig.py [options] input.xml output.i
Converts Doxygen generated XML files into a file containing docstrings
that can be used by SWIG-1.3.x. Note that you need to get SWIG
version > 1.3.23 or use Robin Dunn's docstring patch to be able to use
the resulting output.
input.xml is your doxygen generated XML file and output.i is where the
output will be written (the file will be clobbered).
"""
#
#
# This code is implemented using Mark Pilgrim's code as a guideline:
# http://www.faqs.org/docs/diveintopython/kgp_divein.html
#
# Author: Prabhu Ramachandran
# License: BSD style
#
# Thanks:
# Johan Hake: the include_function_definition feature
# Bill Spotz: bug reports and testing.
# Sebastian Henschel: Misc. enhancements.
#
#
from xml.dom import minidom
import re
import textwrap
import sys
import os.path
import optparse
def my_open_read(source):
if hasattr(source, "read"):
return source
else:
return open(source)
def my_open_write(dest):
if hasattr(dest, "write"):
return dest
else:
return open(dest, 'w')
class Doxy2SWIG:
"""Converts Doxygen generated XML files into a file containing
docstrings that can be used by SWIG-1.3.x that have support for
feature("docstring"). Once the data is parsed it is stored in
self.pieces.
"""
def __init__(self, src, include_function_definition=True, quiet=False):
"""Initialize the instance given a source object. `src` can
be a file or filename. If you do not want to include function
definitions from doxygen then set
`include_function_definition` to `False`. This is handy since
this allows you to use the swig generated function definition
using %feature("autodoc", [0,1]).
"""
f = my_open_read(src)
self.my_dir = os.path.dirname(f.name)
self.xmldoc = minidom.parse(f).documentElement
f.close()
self.pieces = []
self.pieces.append('\n// File: %s\n' %
os.path.basename(f.name))
self.space_re = re.compile(r'\s+')
self.lead_spc = re.compile(r'^(%feature\S+\s+\S+\s*?)"\s+(\S)')
self.multi = 0
self.ignores = ['inheritancegraph', 'param', 'listofallmembers',
'innerclass', 'name', 'declname', 'incdepgraph',
'invincdepgraph', 'programlisting', 'type',
'references', 'referencedby', 'location',
'collaborationgraph', 'reimplements',
'reimplementedby', 'derivedcompoundref',
'basecompoundref']
#self.generics = []
self.include_function_definition = include_function_definition
if not include_function_definition:
self.ignores.append('argsstring')
self.quiet = quiet
def generate(self):
"""Parses the file set in the initialization. The resulting
data is stored in `self.pieces`.
"""
self.parse(self.xmldoc)
def parse(self, node):
"""Parse a given node. This function in turn calls the
`parse_<nodeType>` functions which handle the respective
nodes.
"""
pm = getattr(self, "parse_%s" % node.__class__.__name__)
pm(node)
def parse_Document(self, node):
self.parse(node.documentElement)
def parse_Text(self, node):
txt = node.data
txt = txt.replace('\\', r'\\\\')
txt = txt.replace('"', r'\"')
# ignore pure whitespace
m = self.space_re.match(txt)
if m and len(m.group()) == len(txt):
pass
else:
self.add_text(textwrap.fill(txt, break_long_words=False))
def parse_Element(self, node):
"""Parse an `ELEMENT_NODE`. This calls specific
`do_<tagName>` handers for different elements. If no handler
is available the `generic_parse` method is called. All
tagNames specified in `self.ignores` are simply ignored.
"""
name = node.tagName
ignores = self.ignores
if name in ignores:
return
attr = "do_%s" % name
if hasattr(self, attr):
handlerMethod = getattr(self, attr)
handlerMethod(node)
else:
self.generic_parse(node)
#if name not in self.generics: self.generics.append(name)
def parse_Comment(self, node):
"""Parse a `COMMENT_NODE`. This does nothing for now."""
return
def add_text(self, value):
"""Adds text corresponding to `value` into `self.pieces`."""
if isinstance(value, (list, tuple)):
self.pieces.extend(value)
else:
self.pieces.append(value)
def get_specific_nodes(self, node, names):
"""Given a node and a sequence of strings in `names`, return a
dictionary containing the names as keys and child
`ELEMENT_NODEs`, that have a `tagName` equal to the name.
"""
nodes = [(x.tagName, x) for x in node.childNodes
if x.nodeType == x.ELEMENT_NODE and
x.tagName in names]
return dict(nodes)
def generic_parse(self, node, pad=0):
"""A Generic parser for arbitrary tags in a node.
Parameters:
- node: A node in the DOM.
- pad: `int` (default: 0)
If 0 the node data is not padded with newlines. If 1 it
appends a newline after parsing the childNodes. If 2 it
pads before and after the nodes are processed. Defaults to
0.
"""
npiece = 0
if pad:
npiece = len(self.pieces)
if pad == 2:
self.add_text('\n')
for n in node.childNodes:
self.parse(n)
if pad:
if len(self.pieces) > npiece:
self.add_text('\n')
def space_parse(self, node):
self.add_text(' ')
self.generic_parse(node)
do_ref = space_parse
do_emphasis = space_parse
do_bold = space_parse
do_computeroutput = space_parse
do_formula = space_parse
def do_compoundname(self, node):
self.add_text('\n\n')
data = node.firstChild.data
self.add_text('%%feature("docstring") %s "\n' % data)
def do_compounddef(self, node):
kind = node.attributes['kind'].value
if kind in ('class', 'struct'):
prot = node.attributes['prot'].value
if prot != 'public':
return
names = ('compoundname', 'briefdescription',
'detaileddescription', 'includes')
first = self.get_specific_nodes(node, names)
for n in names:
if first.has_key(n):
self.parse(first[n])
self.add_text(['";', '\n'])
for n in node.childNodes:
if n not in first.values():
self.parse(n)
elif kind in ('file', 'namespace'):
nodes = node.getElementsByTagName('sectiondef')
for n in nodes:
self.parse(n)
def do_includes(self, node):
self.add_text('C++ includes: ')
self.generic_parse(node, pad=1)
def do_parameterlist(self, node):
text = 'unknown'
for key, val in node.attributes.items():
if key == 'kind':
if val == 'param':
text = 'Parameters'
elif val == 'exception':
text = 'Exceptions'
elif val == 'retval':
text = 'Returns'
else:
text = val
break
self.add_text(['\n', '\n', text, ':', '\n'])
self.generic_parse(node, pad=1)
def do_para(self, node):
self.add_text('\n')
self.generic_parse(node, pad=1)
def do_parametername(self, node):
self.add_text('\n')
try:
data = node.firstChild.data
except AttributeError: # perhaps a <ref> tag in it
data = node.firstChild.firstChild.data
if data.find('Exception') != -1:
self.add_text(data)
else:
self.add_text("%s: " % data)
def do_parameterdefinition(self, node):
self.generic_parse(node, pad=1)
def do_detaileddescription(self, node):
self.generic_parse(node, pad=1)
def do_briefdescription(self, node):
self.generic_parse(node, pad=1)
def do_memberdef(self, node):
prot = node.attributes['prot'].value
id = node.attributes['id'].value
kind = node.attributes['kind'].value
tmp = node.parentNode.parentNode.parentNode
compdef = tmp.getElementsByTagName('compounddef')[0]
cdef_kind = compdef.attributes['kind'].value
if prot == 'public':
first = self.get_specific_nodes(node, ('definition', 'name'))
name = first['name'].firstChild.data
if name[:8] == 'operator': # Don't handle operators yet.
return
if not 'definition' in first or \
kind in ['variable', 'typedef']:
return
if self.include_function_definition:
defn = first['definition'].firstChild.data
else:
defn = ""
self.add_text('\n')
self.add_text('%feature("docstring") ')
anc = node.parentNode.parentNode
if cdef_kind in ('file', 'namespace'):
ns_node = anc.getElementsByTagName('innernamespace')
if not ns_node and cdef_kind == 'namespace':
ns_node = anc.getElementsByTagName('compoundname')
if ns_node:
ns = ns_node[0].firstChild.data
self.add_text(' %s::%s "\n%s' % (ns, name, defn))
else:
self.add_text(' %s "\n%s' % (name, defn))
elif cdef_kind in ('class', 'struct'):
# Get the full function name.
anc_node = anc.getElementsByTagName('compoundname')
cname = anc_node[0].firstChild.data
self.add_text(' %s::%s "\n%s' % (cname, name, defn))
for n in node.childNodes:
if n not in first.values():
self.parse(n)
self.add_text(['";', '\n'])
def do_definition(self, node):
data = node.firstChild.data
self.add_text('%s "\n%s' % (data, data))
def do_sectiondef(self, node):
kind = node.attributes['kind'].value
if kind in ('public-func', 'func', 'user-defined', ''):
self.generic_parse(node)
def do_header(self, node):
"""For a user defined section def a header field is present
which should not be printed as such, so we comment it in the
output."""
data = node.firstChild.data
self.add_text('\n/*\n %s \n*/\n' % data)
# If our immediate sibling is a 'description' node then we
# should comment that out also and remove it from the parent
# node's children.
parent = node.parentNode
idx = parent.childNodes.index(node)
if len(parent.childNodes) >= idx + 2:
nd = parent.childNodes[idx + 2]
if nd.nodeName == 'description':
nd = parent.removeChild(nd)
self.add_text('\n/*')
self.generic_parse(nd)
self.add_text('\n*/\n')
def do_simplesect(self, node):
kind = node.attributes['kind'].value
if kind in ('date', 'rcs', 'version'):
pass
elif kind == 'warning':
self.add_text(['\n', 'WARNING: '])
self.generic_parse(node)
elif kind == 'see':
self.add_text('\n')
self.add_text('See: ')
self.generic_parse(node)
else:
self.generic_parse(node)
def do_argsstring(self, node):
self.generic_parse(node, pad=1)
def do_member(self, node):
kind = node.attributes['kind'].value
refid = node.attributes['refid'].value
if kind == 'function' and refid[:9] == 'namespace':
self.generic_parse(node)
def do_doxygenindex(self, node):
self.multi = 1
comps = node.getElementsByTagName('compound')
for c in comps:
refid = c.attributes['refid'].value
fname = refid + '.xml'
if not os.path.exists(fname):
fname = os.path.join(self.my_dir, fname)
if not self.quiet:
print("parsing file: %s" % fname)
p = Doxy2SWIG(fname, self.include_function_definition, self.quiet)
p.generate()
self.pieces.extend(self.clean_pieces(p.pieces))
def write(self, fname):
o = my_open_write(fname)
if self.multi:
o.write("".join(self.pieces))
else:
o.write("".join(self.clean_pieces(self.pieces)))
o.close()
def clean_pieces(self, pieces):
"""Cleans the list of strings given as `pieces`. It replaces
multiple newlines by a maximum of 2 and returns a new list.
It also wraps the paragraphs nicely.
"""
ret = []
count = 0
for i in pieces:
if i == '\n':
count = count + 1
else:
if i == '";':
if count:
ret.append('\n')
elif count > 2:
ret.append('\n\n')
elif count:
ret.append('\n' * count)
count = 0
ret.append(i)
_data = "".join(ret)
ret = []
for i in _data.split('\n\n'):
if i == 'Parameters:' or i == 'Exceptions:' or i == 'Returns:':
ret.extend([i, '\n' + '-' * len(i), '\n\n'])
elif i.find('// File:') > -1: # leave comments alone.
ret.extend([i, '\n'])
else:
_tmp = textwrap.fill(i.strip(), break_long_words=False)
_tmp = self.lead_spc.sub(r'\1"\2', _tmp)
ret.extend([_tmp, '\n\n'])
return ret
def convert(input, output, include_function_definition=True, quiet=False):
p = Doxy2SWIG(input, include_function_definition, quiet)
p.generate()
p.write(output)
def main():
usage = __doc__
parser = optparse.OptionParser(usage)
parser.add_option("-n", '--no-function-definition',
action='store_true',
default=False,
dest='func_def',
help='do not include doxygen function definitions')
parser.add_option("-q", '--quiet',
action='store_true',
default=False,
dest='quiet',
help='be quiet and minimize output')
options, args = parser.parse_args()
if len(args) != 2:
parser.error("error: no input and output specified")
convert(args[0], args[1], not options.func_def, options.quiet)
if __name__ == '__main__':
main()
+5
View File
@@ -0,0 +1,5 @@
install ( FILES simple.py complete.py cbus.py
DESTINATION share/libftdi/examples
PERMISSIONS OWNER_READ GROUP_READ WORLD_READ
)
+173
View File
@@ -0,0 +1,173 @@
#!/usr/bin/python2
"""
Copyright 2015, Sinclair R.F., Inc.
This program is distributed under the GPL, version 2.
Demonstrate how to configure the FT230X USB UART bridge as follows:
max_power 500 mA
CBUS3 Drive 1 (accomodate PCB error)
"""
import sys
# Need to install libftdi for the following to work (see README.txt)
import ftdi1 as ftdi
# Define class for displaying errors.
class ErrorMsg(Exception):
def __init__(self,message):
self.message = message
def __str__(self):
return self.message
# Function to convert CBUSX values to human-readable strings
def cbush_string(value):
if value == ftdi.CBUSX_AWAKE:
return 'AWAKE'
if value == ftdi.CBUSX_BAT_DETECT:
return 'BAT_DETECT'
if value == ftdi.CBUSX_BAT_DETECT_NEG:
return 'BAT_DETECT_NEG'
if value == ftdi.CBUSX_BB_RD:
return 'BB_RD'
if value == ftdi.CBUSX_BB_WR:
return 'BB_WR'
if value == ftdi.CBUSX_CLK24:
return 'CLK24'
if value == ftdi.CBUSX_CLK12:
return 'CLK12'
if value == ftdi.CBUSX_CLK6:
return 'CLK6'
if value == ftdi.CBUSX_DRIVE_0:
return 'DRIVE_0'
if value == ftdi.CBUSX_DRIVE1:
return 'DRIVE_1'
if value == ftdi.CBUSX_I2C_RXF:
return 'I2C_RXF'
if value == ftdi.CBUSX_I2C_TXE:
return 'I2C_TXE'
if value == ftdi.CBUSX_IOMODE:
return 'IOMODE'
if value == ftdi.CBUSX_PWREN:
return 'PWREN'
if value == ftdi.CBUSX_RXLED:
return 'RXLED'
if value == ftdi.CBUSX_SLEEP:
return 'SLEEP'
if value == ftdi.CBUSX_TIME_STAMP:
return 'TIME_STAMP'
if value == ftdi.CBUSX_TRISTATE:
return 'TRISTATE'
if value == ftdi.CBUSX_TXDEN:
return 'TXDEN'
if value == ftdi.CBUSX_TXLED:
return 'TXLED'
if value == ftdi.CBUSX_TXRXLED:
return 'TXRXLED'
if value == ftdi.CBUSX_VBUS_SENSE:
return 'VBUS_SENSE'
return 'UNKNOWN'
# Surround the program with a try ... except clause.
try:
# Allocate and inialize an ftdi context.
ftdic = ftdi.new()
if ftdic == 0:
raise ErrorMsg('ftdi.new() failed')
# List all the FT230X devices.
nDevices, devlist = ftdi.usb_find_all(ftdic, 0x0403, 0x6015)
if nDevices < 0:
raise ErrorMsg('ftdi.usb_find_all error = %s' % ftdi.get_error_string(ftdic))
elif nDevices == 0:
raise ErrorMsg('No FT230X devices found')
elif nDevices != 1:
raise ErrorMsg('More than one FT230X device found')
# Display the identified single FT230X device.
ret, manufacturer, description, serial = ftdi.usb_get_strings(ftdic, devlist.dev)
if ret < 0:
raise ErrorMsg('ftdi.usb_get_strings error = %s' % ftdi.get_error_string(ftdic))
print 'manufacturer="%s" description="%s" serial="%s"' % (manufacturer, description, serial)
# Open the identified single FT230X device.
ret = ftdi.usb_open_desc(ftdic, 0x0403, 0x6015, description, serial)
if ret < 0:
raise ErrorMsg('ftdi.usb_open_desc error = %s' % ftdi.get_error_string(ftdic))
# Read the chip id.
ret, chipid = ftdi.read_chipid(ftdic)
if ret < 0:
raise ErrorMsg('ftdi.read_chipid error = %s' % ftdi.get_error_string(ftdic))
print 'chip id=0x%08X' % (chipid % 2**32)
# Read the EEPROM
ret = ftdi.read_eeprom(ftdic)
if ret < 0:
raise ErrorMsg('ftdi.read_eeprom error = %s' % ftdi.get_error_string(ftdic))
# Get a read-only copy of the EEPROM
if True:
eeprom_size = ftdic.eeprom.size
ret, eeprom_buf = ftdi.get_eeprom_buf(ftdic, eeprom_size)
if ret < 0:
raise ErrorMsg('ftdi.get_eeprom_buf error = %s' % ftdi.get_error_string(ftdic))
for i in range(0,eeprom_size,16):
sys.stdout.write('%04x: ' % i)
for j in range(16):
sys.stdout.write('%02x ' % ord(eeprom_buf[i+j]))
if j in (7,15,):
sys.stdout.write(' ')
for j in range(16):
x = eeprom_buf[i+j]
if 32 <= ord(x) <= 0x7E:
sys.stdout.write(x)
else:
sys.stdout.write('.')
sys.stdout.write('\n')
# Read and display the EEPROM (in human readable format)
ret = ftdi.eeprom_decode(ftdic, 1)
if ret < 0:
raise ErrorMsg('ftdi.eeprom_decode error = %s' % ftdi.get_error_string(ftdic))
# Set the maximum power to 500mA.
print 'initial max_power = %dmA' % ftdic.eeprom.max_power
ftdic.eeprom.max_power = 500
print 'new max_power = %dmA' % ftdic.eeprom.max_power
# Set CBUS3 to DRIVE_1 (the board needs to be reworked to use PWREN# and BCD#)
ret, value = ftdi.get_eeprom_value(ftdic,ftdi.CBUS_FUNCTION_3)
if ret < 0:
raise ErrorMsg('ftdi.get_eeprom_value error = %s' % ftdi.get_error_string(ftdic))
print 'initial CBUS3 = %d (%s)' % (value,cbush_string(value),)
ret = ftdi.set_eeprom_value(ftdic,ftdi.CBUS_FUNCTION_3,ftdi.CBUSX_DRIVE1)
if ret < 0:
raise ErrorMsg('ftdi.set_eeprom_value error = %s' % ftdi.get_error_string(ftdic))
ret, value = ftdi.get_eeprom_value(ftdic,ftdi.CBUS_FUNCTION_3)
if ret < 0:
raise ErrorMsg('ftdi.get_eeprom_value error = %s' % ftdi.get_error_string(ftdic))
print 'new CBUS3 = %d (%s)' % (value,cbush_string(value),)
# Write the new EEPROM settings.
if False:
ret = ftdi.eeprom_build(ftdic)
if ret < 0:
raise ErrorMsg('ftdi.eeprom_build error = %s' % ftdi.get_error_string(ftdic))
ret = ftdi.write_eeprom(ftdic)
if ret < 0:
raise ErrorMsg('ftdi.write_eeprom error = %s' % ftdi.get_error_string(ftdic))
print 'EEPROM write succeeded'
else:
print 'EEPROM write not attempted'
# Close the ftdi context.
ret = ftdi.usb_close(ftdic)
if ret < 0:
raise ErrorMsg('ftdi.usb_close error = %s' % ftdi.get_error_string(ftdic))
except ErrorMsg, msg:
print >> sys.stderr, 'FATAL ERROR: ' + str(msg)
exit(1)
+121
View File
@@ -0,0 +1,121 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""Python example program.
Complete program to demonstrate the usage
of the swig generated python wrapper
You need to build and install the wrapper first"""
import os
import sys
import ftdi1 as ftdi
import time
# version
print ('version: %s\n' % ftdi.__version__)
# initialize
ftdic = ftdi.new()
if ftdic == 0:
print('new failed: %d' % ret)
os._exit(1)
# try to list ftdi devices 0x6010 or 0x6001
ret, devlist = ftdi.usb_find_all(ftdic, 0x0403, 0x6010)
if ret <= 0:
ret, devlist = ftdi.usb_find_all(ftdic, 0x0403, 0x6001)
if ret < 0:
print('ftdi_usb_find_all failed: %d (%s)' %
(ret, ftdi.get_error_string(ftdic)))
os._exit(1)
print('devices: %d' % ret)
curnode = devlist
i = 0
while(curnode != None):
ret, manufacturer, description, serial = ftdi.usb_get_strings(
ftdic, curnode.dev)
if ret < 0:
print('ftdi_usb_get_strings failed: %d (%s)' %
(ret, ftdi.get_error_string(ftdic)))
os._exit(1)
print('#%d: manufacturer="%s" description="%s" serial="%s"\n' %
(i, manufacturer, description, serial))
curnode = curnode.next
i += 1
# open usb
ret = ftdi.usb_open(ftdic, 0x0403, 0x6001)
if ret < 0:
print('unable to open ftdi device: %d (%s)' %
(ret, ftdi.get_error_string(ftdic)))
os._exit(1)
# bitbang
ret = ftdi.set_bitmode(ftdic, 0xff, ftdi.BITMODE_BITBANG)
if ret < 0:
print('Cannot enable bitbang')
os._exit(1)
print('turning everything on')
ftdi.write_data(ftdic, chr(0xff), 1)
time.sleep(1)
print('turning everything off\n')
ftdi.write_data(ftdic, chr(0x00), 1)
time.sleep(1)
for i in range(8):
val = 2 ** i
print('enabling bit #%d (0x%02x)' % (i, val))
ftdi.write_data(ftdic, chr(val), 1)
time.sleep(1)
ftdi.disable_bitbang(ftdic)
print('')
# read pins
ret, pins = ftdi.read_pins(ftdic)
if (ret == 0):
if sys.version_info[0] < 3: # python 2
pins = ord(pins)
else:
pins = pins[0]
print('pins: 0x%x' % pins)
# read chip id
ret, chipid = ftdi.read_chipid(ftdic)
if (ret == 0):
print('chip id: %x\n' % chipid)
# read eeprom
eeprom_addr = 1
ret, eeprom_val = ftdi.read_eeprom_location(ftdic, eeprom_addr)
if (ret == 0):
print('eeprom @ %d: 0x%04x\n' % (eeprom_addr, eeprom_val))
print('eeprom:')
ret = ftdi.read_eeprom(ftdic)
size = 128
ret, eeprom = ftdi.get_eeprom_buf(ftdic, size)
if (ret == 0):
for i in range(size):
octet = eeprom[i]
if sys.version_info[0] < 3: # python 2
octet = ord(octet)
sys.stdout.write('%02x ' % octet)
if (i % 8 == 7):
print('')
print('')
# close usb
ret = ftdi.usb_close(ftdic)
if ret < 0:
print('unable to close ftdi device: %d (%s)' %
(ret, ftdi.get_error_string(ftdic)))
os._exit(1)
print ('device closed')
ftdi.free(ftdic)
+34
View File
@@ -0,0 +1,34 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""Python example program.
Small program to demonstrate the usage
of the swig generated python wrapper
You need to build and install the wrapper first"""
import ftdi1 as ftdi
def main():
"""Main program"""
context = ftdi.new()
version_info = ftdi.get_library_version()
print("[FTDI version] major: %d, minor: %d, micro: %d"
", version_str: %s, snapshot_str: %s" %
(version_info.major, version_info.minor, version_info.micro,
version_info.version_str, version_info.snapshot_str))
# try to open an ftdi 0x6010 or 0x6001
ret = ftdi.usb_open(context, 0x0403, 0x6010)
if ret < 0:
ret = ftdi.usb_open(context, 0x0403, 0x6001)
print("ftdi.usb_open(): %d" % ret)
print("ftdi.set_baudrate(): %d" % ftdi.set_baudrate(context, 9600))
ftdi.free(context)
main()
+170
View File
@@ -0,0 +1,170 @@
/* File: ftdi1.i */
%module(docstring="Python interface to libftdi1") ftdi1
%feature("autodoc","1");
#ifdef DOXYGEN
%include "ftdi1_doc.i"
#endif
%{
#include "Python.h"
inline PyObject* charp2str(const char *v_, long len)
{
#if PY_MAJOR_VERSION >= 3
return PyBytes_FromStringAndSize(v_, len);
#else
return PyString_FromStringAndSize(v_, len);
#endif
}
inline char * str2charp_size(PyObject* pyObj, int * size)
{
char * v_ = 0;
#if PY_MAJOR_VERSION >= 3
PyBytes_AsStringAndSize(pyObj, &v_, (Py_ssize_t*)size);
#else
PyString_AsStringAndSize(pyObj, &v_, (Py_ssize_t*)size);
#endif
return v_;
}
%}
%include <typemaps.i>
%include <cstring.i>
%typemap(in) unsigned char* = char*;
%immutable ftdi_version_info::version_str;
%immutable ftdi_version_info::snapshot_str;
%rename("%(strip:[ftdi_])s") "";
%newobject ftdi_new;
%typemap(newfree) struct ftdi_context *ftdi "ftdi_free($1);";
%delobject ftdi_free;
%define ftdi_usb_find_all_docstring
"usb_find_all(context, vendor, product) -> (return_code, devlist)"
%enddef
%feature("autodoc", ftdi_usb_find_all_docstring) ftdi_usb_find_all;
%typemap(in,numinputs=0) SWIGTYPE** OUTPUT ($*ltype temp) %{ $1 = &temp; %}
%typemap(argout) SWIGTYPE** OUTPUT %{ $result = SWIG_Python_AppendOutput($result, SWIG_NewPointerObj((void*)*$1,$*descriptor,0)); %}
%apply SWIGTYPE** OUTPUT { struct ftdi_device_list **devlist };
int ftdi_usb_find_all(struct ftdi_context *ftdi, struct ftdi_device_list **devlist,
int vendor, int product);
%clear struct ftdi_device_list **devlist;
%define ftdi_usb_get_strings_docstring
"usb_get_strings(context, device) -> (return_code, manufacturer, description, serial)"
%enddef
%feature("autodoc", ftdi_usb_get_strings_docstring) ftdi_usb_get_strings;
%feature("autodoc", ftdi_usb_get_strings_docstring) ftdi_usb_get_strings2;
%feature("autodoc", ftdi_usb_get_strings_docstring) ftdi_eeprom_get_strings;
%apply char *OUTPUT { char * manufacturer, char * description, char * serial };
%cstring_bounded_output( char * manufacturer, 256 );
%cstring_bounded_output( char * description, 256 );
%cstring_bounded_output( char * product, 256 );
%cstring_bounded_output( char * serial, 256 );
%typemap(default,noblock=1) int mnf_len, int desc_len, int product_len, int serial_len { $1 = 256; }
int ftdi_usb_get_strings(struct ftdi_context *ftdi, struct libusb_device *dev,
char * manufacturer, int mnf_len,
char * description, int desc_len,
char * serial, int serial_len);
int ftdi_usb_get_strings2(struct ftdi_context *ftdi, struct libusb_device *dev,
char * manufacturer, int mnf_len,
char * description, int desc_len,
char * serial, int serial_len);
int ftdi_eeprom_get_strings(struct ftdi_context *ftdi,
char *manufacturer, int mnf_len,
char *product, int product_len,
char *serial, int serial_len);
%clear char * manufacturer, char * description, char * serial;
%clear char * product;
%clear int mnf_len, int desc_len, int product_len, int serial_len;
%define ftdi_read_data_docstring
"read_data(context) -> (return_code, buf)"
%enddef
%feature("autodoc", ftdi_read_data_docstring) ftdi_read_data;
%typemap(in,numinputs=1) (unsigned char *buf, int size) %{ $2 = PyInt_AsLong($input);$1 = (unsigned char*)malloc($2*sizeof(char)); %}
%typemap(argout) (unsigned char *buf, int size) %{ if(result<0) $2=0; $result = SWIG_Python_AppendOutput($result, charp2str((char*)$1, $2)); free($1); %}
int ftdi_read_data(struct ftdi_context *ftdi, unsigned char *buf, int size);
%clear (unsigned char *buf, int size);
%define ftdi_write_data_docstring
"write_data(context, data) -> return_code"
%enddef
%feature("autodoc", ftdi_write_data_docstring) ftdi_write_data;
%typemap(in,numinputs=1) (const unsigned char *buf, int size) %{ $1 = (unsigned char*)str2charp_size($input, &$2); %}
int ftdi_write_data(struct ftdi_context *ftdi, const unsigned char *buf, int size);
%clear (const unsigned char *buf, int size);
%apply int *OUTPUT { unsigned int *chunksize };
int ftdi_read_data_get_chunksize(struct ftdi_context *ftdi, unsigned int *chunksize);
int ftdi_write_data_get_chunksize(struct ftdi_context *ftdi, unsigned int *chunksize);
%clear unsigned int *chunksize;
%define ftdi_read_pins_docstring
"read_pins(context) -> (return_code, pins)"
%enddef
%feature("autodoc", ftdi_read_pins_docstring) ftdi_read_pins;
%typemap(in,numinputs=0) unsigned char *pins ($*ltype temp) %{ $1 = &temp; %}
%typemap(argout) (unsigned char *pins) %{ $result = SWIG_Python_AppendOutput($result, charp2str((char*)$1, 1)); %}
int ftdi_read_pins(struct ftdi_context *ftdi, unsigned char *pins);
%clear unsigned char *pins;
%typemap(in,numinputs=0) unsigned char *latency ($*ltype temp) %{ $1 = &temp; %}
%typemap(argout) (unsigned char *latency) %{ $result = SWIG_Python_AppendOutput($result, charp2str((char*)$1, 1)); %}
int ftdi_get_latency_timer(struct ftdi_context *ftdi, unsigned char *latency);
%clear unsigned char *latency;
%apply short *OUTPUT { unsigned short *status };
int ftdi_poll_modem_status(struct ftdi_context *ftdi, unsigned short *status);
%clear unsigned short *status;
%apply int *OUTPUT { int* value };
int ftdi_get_eeprom_value(struct ftdi_context *ftdi, enum ftdi_eeprom_value value_name, int* value);
%clear int* value;
%typemap(in,numinputs=1) (unsigned char *buf, int size) %{ $2 = PyInt_AsLong($input);$1 = (unsigned char*)malloc($2*sizeof(char)); %}
%typemap(argout) (unsigned char *buf, int size) %{ if(result<0) $2=0; $result = SWIG_Python_AppendOutput($result, charp2str((char*)$1, $2)); free($1); %}
int ftdi_get_eeprom_buf(struct ftdi_context *ftdi, unsigned char * buf, int size);
%clear (unsigned char *buf, int size);
%define ftdi_read_eeprom_location_docstring
"read_eeprom_location(context, eeprom_addr) -> (return_code, eeprom_val)"
%enddef
%feature("autodoc", ftdi_read_eeprom_location_docstring) ftdi_read_eeprom_location;
%apply short *OUTPUT { unsigned short *eeprom_val };
int ftdi_read_eeprom_location (struct ftdi_context *ftdi, int eeprom_addr, unsigned short *eeprom_val);
%clear unsigned short *eeprom_val;
%define ftdi_read_eeprom_docstring
"read_eeprom(context) -> (return_code, eeprom)"
%enddef
%feature("autodoc", ftdi_read_eeprom_docstring) ftdi_read_eeprom;
%define ftdi_read_chipid_docstring
"ftdi_read_chipid(context) -> (return_code, chipid)"
%enddef
%feature("autodoc", ftdi_read_chipid_docstring) ftdi_read_chipid;
%apply int *OUTPUT { unsigned int *chipid };
int ftdi_read_chipid(struct ftdi_context *ftdi, unsigned int *chipid);
%clear unsigned int *chipid;
%include ftdi.h
%{
#include <ftdi.h>
%}
%include ftdi_i.h
%{
#include <ftdi_i.h>
%}
%pythoncode %{
__version__ = get_library_version().version_str
%}
+55
View File
@@ -0,0 +1,55 @@
# Includes
include_directories(BEFORE ${CMAKE_CURRENT_BINARY_DIR}
${CMAKE_CURRENT_SOURCE_DIR}
)
# Version information
set(SNAPSHOT_VERSION "unknown")
execute_process(COMMAND git describe
OUTPUT_VARIABLE GIT_DESCRIBE_OUTPUT
RESULT_VARIABLE GIT_DESCRIBE_RESULT
OUTPUT_STRIP_TRAILING_WHITESPACE
)
if(${GIT_DESCRIBE_RESULT} STREQUAL 0)
set(SNAPSHOT_VERSION ${GIT_DESCRIBE_OUTPUT})
endif ()
message(STATUS "Detected git snapshot version: ${SNAPSHOT_VERSION}")
configure_file(ftdi_version_i.h.in "${CMAKE_CURRENT_BINARY_DIR}/ftdi_version_i.h" @ONLY)
# Targets
set(c_sources ${CMAKE_CURRENT_SOURCE_DIR}/ftdi.c ${CMAKE_CURRENT_SOURCE_DIR}/ftdi_stream.c CACHE INTERNAL "List of c sources" )
set(c_headers ${CMAKE_CURRENT_SOURCE_DIR}/ftdi.h CACHE INTERNAL "List of c headers" )
add_library(ftdi1 SHARED ${c_sources})
math(EXPR VERSION_FIXUP "${MAJOR_VERSION} + 1") # Compatiblity with previous releases
set_target_properties(ftdi1 PROPERTIES VERSION ${VERSION_FIXUP}.${MINOR_VERSION}.0 SOVERSION 2)
# Prevent clobbering each other during the build
set_target_properties ( ftdi1 PROPERTIES CLEAN_DIRECT_OUTPUT 1 )
# Dependencies
target_link_libraries(ftdi1 ${LIBUSB_LIBRARIES})
install ( TARGETS ftdi1
RUNTIME DESTINATION bin
LIBRARY DESTINATION lib${LIB_SUFFIX}
ARCHIVE DESTINATION lib${LIB_SUFFIX}
)
if ( STATICLIBS )
add_library(ftdi1-static STATIC ${c_sources})
target_link_libraries(ftdi1-static ${LIBUSB_LIBRARIES})
set_target_properties(ftdi1-static PROPERTIES OUTPUT_NAME "ftdi1")
set_target_properties(ftdi1-static PROPERTIES CLEAN_DIRECT_OUTPUT 1)
install ( TARGETS ftdi1-static
ARCHIVE DESTINATION lib${LIB_SUFFIX}
COMPONENT staticlibs
)
endif ()
install ( FILES ${c_headers}
DESTINATION include/${PROJECT_NAME}
COMPONENT headers
)
+4602
View File
File diff suppressed because it is too large Load Diff
+585
View File
@@ -0,0 +1,585 @@
/***************************************************************************
ftdi.h - description
-------------------
begin : Fri Apr 4 2003
copyright : (C) 2003-2017 by Intra2net AG and the libftdi developers
email : opensource@intra2net.com
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License *
* version 2.1 as published by the Free Software Foundation; *
* *
***************************************************************************/
#ifndef __libftdi_h__
#define __libftdi_h__
#include <stdint.h>
#ifndef _WIN32
#include <sys/time.h>
#endif
/* 'interface' might be defined as a macro on Windows, so we need to
* undefine it so as not to break the current libftdi API, because
* struct ftdi_context has an 'interface' member
* As this can be problematic if you include windows.h after ftdi.h
* in your sources, we force windows.h to be included first. */
#if defined(_WIN32) || defined(__CYGWIN__) || defined(_WIN32_WCE)
#include <windows.h>
#if defined(interface)
#undef interface
#endif
#endif
/** FTDI chip type */
enum ftdi_chip_type
{
TYPE_AM=0,
TYPE_BM=1,
TYPE_2232C=2,
TYPE_R=3,
TYPE_2232H=4,
TYPE_4232H=5,
TYPE_232H=6,
TYPE_230X=7,
};
/** Parity mode for ftdi_set_line_property() */
enum ftdi_parity_type { NONE=0, ODD=1, EVEN=2, MARK=3, SPACE=4 };
/** Number of stop bits for ftdi_set_line_property() */
enum ftdi_stopbits_type { STOP_BIT_1=0, STOP_BIT_15=1, STOP_BIT_2=2 };
/** Number of bits for ftdi_set_line_property() */
enum ftdi_bits_type { BITS_7=7, BITS_8=8 };
/** Break type for ftdi_set_line_property2() */
enum ftdi_break_type { BREAK_OFF=0, BREAK_ON=1 };
/** MPSSE bitbang modes */
enum ftdi_mpsse_mode
{
BITMODE_RESET = 0x00, /**< switch off bitbang mode, back to regular serial/FIFO */
BITMODE_BITBANG= 0x01, /**< classical asynchronous bitbang mode, introduced with B-type chips */
BITMODE_MPSSE = 0x02, /**< MPSSE mode, available on 2232x chips */
BITMODE_SYNCBB = 0x04, /**< synchronous bitbang mode, available on 2232x and R-type chips */
BITMODE_MCU = 0x08, /**< MCU Host Bus Emulation mode, available on 2232x chips */
/* CPU-style fifo mode gets set via EEPROM */
BITMODE_OPTO = 0x10, /**< Fast Opto-Isolated Serial Interface Mode, available on 2232x chips */
BITMODE_CBUS = 0x20, /**< Bitbang on CBUS pins of R-type chips, configure in EEPROM before */
BITMODE_SYNCFF = 0x40, /**< Single Channel Synchronous FIFO mode, available on 2232H chips */
BITMODE_FT1284 = 0x80, /**< FT1284 mode, available on 232H chips */
};
/** Port interface for chips with multiple interfaces */
enum ftdi_interface
{
INTERFACE_ANY = 0,
INTERFACE_A = 1,
INTERFACE_B = 2,
INTERFACE_C = 3,
INTERFACE_D = 4
};
/** Automatic loading / unloading of kernel modules */
enum ftdi_module_detach_mode
{
AUTO_DETACH_SIO_MODULE = 0,
DONT_DETACH_SIO_MODULE = 1
};
/* Shifting commands IN MPSSE Mode*/
#define MPSSE_WRITE_NEG 0x01 /* Write TDI/DO on negative TCK/SK edge*/
#define MPSSE_BITMODE 0x02 /* Write bits, not bytes */
#define MPSSE_READ_NEG 0x04 /* Sample TDO/DI on negative TCK/SK edge */
#define MPSSE_LSB 0x08 /* LSB first */
#define MPSSE_DO_WRITE 0x10 /* Write TDI/DO */
#define MPSSE_DO_READ 0x20 /* Read TDO/DI */
#define MPSSE_WRITE_TMS 0x40 /* Write TMS/CS */
/* FTDI MPSSE commands */
#define SET_BITS_LOW 0x80
/*BYTE DATA*/
/*BYTE Direction*/
#define SET_BITS_HIGH 0x82
/*BYTE DATA*/
/*BYTE Direction*/
#define GET_BITS_LOW 0x81
#define GET_BITS_HIGH 0x83
#define LOOPBACK_START 0x84
#define LOOPBACK_END 0x85
#define TCK_DIVISOR 0x86
/* H Type specific commands */
#define DIS_DIV_5 0x8a
#define EN_DIV_5 0x8b
#define EN_3_PHASE 0x8c
#define DIS_3_PHASE 0x8d
#define CLK_BITS 0x8e
#define CLK_BYTES 0x8f
#define CLK_WAIT_HIGH 0x94
#define CLK_WAIT_LOW 0x95
#define EN_ADAPTIVE 0x96
#define DIS_ADAPTIVE 0x97
#define CLK_BYTES_OR_HIGH 0x9c
#define CLK_BYTES_OR_LOW 0x9d
/*FT232H specific commands */
#define DRIVE_OPEN_COLLECTOR 0x9e
/* Value Low */
/* Value HIGH */ /*rate is 12000000/((1+value)*2) */
#define DIV_VALUE(rate) (rate > 6000000)?0:((6000000/rate -1) > 0xffff)? 0xffff: (6000000/rate -1)
/* Commands in MPSSE and Host Emulation Mode */
#define SEND_IMMEDIATE 0x87
#define WAIT_ON_HIGH 0x88
#define WAIT_ON_LOW 0x89
/* Commands in Host Emulation Mode */
#define READ_SHORT 0x90
/* Address_Low */
#define READ_EXTENDED 0x91
/* Address High */
/* Address Low */
#define WRITE_SHORT 0x92
/* Address_Low */
#define WRITE_EXTENDED 0x93
/* Address High */
/* Address Low */
/* Definitions for flow control */
#define SIO_RESET 0 /* Reset the port */
#define SIO_MODEM_CTRL 1 /* Set the modem control register */
#define SIO_SET_FLOW_CTRL 2 /* Set flow control register */
#define SIO_SET_BAUD_RATE 3 /* Set baud rate */
#define SIO_SET_DATA 4 /* Set the data characteristics of the port */
#define FTDI_DEVICE_OUT_REQTYPE (LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE | LIBUSB_ENDPOINT_OUT)
#define FTDI_DEVICE_IN_REQTYPE (LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE | LIBUSB_ENDPOINT_IN)
/* Requests */
#define SIO_RESET_REQUEST SIO_RESET
#define SIO_SET_BAUDRATE_REQUEST SIO_SET_BAUD_RATE
#define SIO_SET_DATA_REQUEST SIO_SET_DATA
#define SIO_SET_FLOW_CTRL_REQUEST SIO_SET_FLOW_CTRL
#define SIO_SET_MODEM_CTRL_REQUEST SIO_MODEM_CTRL
#define SIO_POLL_MODEM_STATUS_REQUEST 0x05
#define SIO_SET_EVENT_CHAR_REQUEST 0x06
#define SIO_SET_ERROR_CHAR_REQUEST 0x07
#define SIO_SET_LATENCY_TIMER_REQUEST 0x09
#define SIO_GET_LATENCY_TIMER_REQUEST 0x0A
#define SIO_SET_BITMODE_REQUEST 0x0B
#define SIO_READ_PINS_REQUEST 0x0C
#define SIO_READ_EEPROM_REQUEST 0x90
#define SIO_WRITE_EEPROM_REQUEST 0x91
#define SIO_ERASE_EEPROM_REQUEST 0x92
#define SIO_RESET_SIO 0
#define SIO_RESET_PURGE_RX 1
#define SIO_RESET_PURGE_TX 2
#define SIO_DISABLE_FLOW_CTRL 0x0
#define SIO_RTS_CTS_HS (0x1 << 8)
#define SIO_DTR_DSR_HS (0x2 << 8)
#define SIO_XON_XOFF_HS (0x4 << 8)
#define SIO_SET_DTR_MASK 0x1
#define SIO_SET_DTR_HIGH ( 1 | ( SIO_SET_DTR_MASK << 8))
#define SIO_SET_DTR_LOW ( 0 | ( SIO_SET_DTR_MASK << 8))
#define SIO_SET_RTS_MASK 0x2
#define SIO_SET_RTS_HIGH ( 2 | ( SIO_SET_RTS_MASK << 8 ))
#define SIO_SET_RTS_LOW ( 0 | ( SIO_SET_RTS_MASK << 8 ))
#define SIO_RTS_CTS_HS (0x1 << 8)
/* marker for unused usb urb structures
(taken from libusb) */
#define FTDI_URB_USERCONTEXT_COOKIE ((void *)0x1)
#ifdef __GNUC__
#define DEPRECATED(func) func __attribute__ ((deprecated))
#elif defined(_MSC_VER)
#define DEPRECATED(func) __declspec(deprecated) func
#else
#pragma message("WARNING: You need to implement DEPRECATED for this compiler")
#define DEPRECATED(func) func
#endif
struct ftdi_transfer_control
{
int completed;
unsigned char *buf;
int size;
int offset;
struct ftdi_context *ftdi;
struct libusb_transfer *transfer;
};
/**
\brief Main context structure for all libftdi functions.
Do not access directly if possible.
*/
struct ftdi_context
{
/* USB specific */
/** libusb's context */
struct libusb_context *usb_ctx;
/** libusb's usb_dev_handle */
struct libusb_device_handle *usb_dev;
/** usb read timeout */
int usb_read_timeout;
/** usb write timeout */
int usb_write_timeout;
/* FTDI specific */
/** FTDI chip type */
enum ftdi_chip_type type;
/** baudrate */
int baudrate;
/** bitbang mode state */
unsigned char bitbang_enabled;
/** pointer to read buffer for ftdi_read_data */
unsigned char *readbuffer;
/** read buffer offset */
unsigned int readbuffer_offset;
/** number of remaining data in internal read buffer */
unsigned int readbuffer_remaining;
/** read buffer chunk size */
unsigned int readbuffer_chunksize;
/** write buffer chunk size */
unsigned int writebuffer_chunksize;
/** maximum packet size. Needed for filtering modem status bytes every n packets. */
unsigned int max_packet_size;
/* FTDI FT2232C requirecments */
/** FT2232C interface number: 0 or 1 */
int interface; /* 0 or 1 */
/** FT2232C index number: 1 or 2 */
int index; /* 1 or 2 */
/* Endpoints */
/** FT2232C end points: 1 or 2 */
int in_ep;
int out_ep; /* 1 or 2 */
/** Bitbang mode. 1: (default) Normal bitbang mode, 2: FT2232C SPI bitbang mode */
unsigned char bitbang_mode;
/** Decoded eeprom structure */
struct ftdi_eeprom *eeprom;
/** String representation of last error */
const char *error_str;
/** Defines behavior in case a kernel module is already attached to the device */
enum ftdi_module_detach_mode module_detach_mode;
};
/**
List all handled EEPROM values.
Append future new values only at the end to provide API/ABI stability*/
enum ftdi_eeprom_value
{
VENDOR_ID = 0,
PRODUCT_ID = 1,
SELF_POWERED = 2,
REMOTE_WAKEUP = 3,
IS_NOT_PNP = 4,
SUSPEND_DBUS7 = 5,
IN_IS_ISOCHRONOUS = 6,
OUT_IS_ISOCHRONOUS = 7,
SUSPEND_PULL_DOWNS = 8,
USE_SERIAL = 9,
USB_VERSION = 10,
USE_USB_VERSION = 11,
MAX_POWER = 12,
CHANNEL_A_TYPE = 13,
CHANNEL_B_TYPE = 14,
CHANNEL_A_DRIVER = 15,
CHANNEL_B_DRIVER = 16,
CBUS_FUNCTION_0 = 17,
CBUS_FUNCTION_1 = 18,
CBUS_FUNCTION_2 = 19,
CBUS_FUNCTION_3 = 20,
CBUS_FUNCTION_4 = 21,
CBUS_FUNCTION_5 = 22,
CBUS_FUNCTION_6 = 23,
CBUS_FUNCTION_7 = 24,
CBUS_FUNCTION_8 = 25,
CBUS_FUNCTION_9 = 26,
HIGH_CURRENT = 27,
HIGH_CURRENT_A = 28,
HIGH_CURRENT_B = 29,
INVERT = 30,
GROUP0_DRIVE = 31,
GROUP0_SCHMITT = 32,
GROUP0_SLEW = 33,
GROUP1_DRIVE = 34,
GROUP1_SCHMITT = 35,
GROUP1_SLEW = 36,
GROUP2_DRIVE = 37,
GROUP2_SCHMITT = 38,
GROUP2_SLEW = 39,
GROUP3_DRIVE = 40,
GROUP3_SCHMITT = 41,
GROUP3_SLEW = 42,
CHIP_SIZE = 43,
CHIP_TYPE = 44,
POWER_SAVE = 45,
CLOCK_POLARITY = 46,
DATA_ORDER = 47,
FLOW_CONTROL = 48,
CHANNEL_C_DRIVER = 49,
CHANNEL_D_DRIVER = 50,
CHANNEL_A_RS485 = 51,
CHANNEL_B_RS485 = 52,
CHANNEL_C_RS485 = 53,
CHANNEL_D_RS485 = 54,
RELEASE_NUMBER = 55,
EXTERNAL_OSCILLATOR= 56,
USER_DATA_ADDR = 57,
};
/**
\brief list of usb devices created by ftdi_usb_find_all()
*/
struct ftdi_device_list
{
/** pointer to next entry */
struct ftdi_device_list *next;
/** pointer to libusb's usb_device */
struct libusb_device *dev;
};
#define FT1284_CLK_IDLE_STATE 0x01
#define FT1284_DATA_LSB 0x02 /* DS_FT232H 1.3 amd ftd2xx.h 1.0.4 disagree here*/
#define FT1284_FLOW_CONTROL 0x04
#define POWER_SAVE_DISABLE_H 0x80
#define USE_SERIAL_NUM 0x08
enum ftdi_cbus_func
{
CBUS_TXDEN = 0, CBUS_PWREN = 1, CBUS_RXLED = 2, CBUS_TXLED = 3, CBUS_TXRXLED = 4,
CBUS_SLEEP = 5, CBUS_CLK48 = 6, CBUS_CLK24 = 7, CBUS_CLK12 = 8, CBUS_CLK6 = 9,
CBUS_IOMODE = 0xa, CBUS_BB_WR = 0xb, CBUS_BB_RD = 0xc
};
enum ftdi_cbush_func
{
CBUSH_TRISTATE = 0, CBUSH_TXLED = 1, CBUSH_RXLED = 2, CBUSH_TXRXLED = 3, CBUSH_PWREN = 4,
CBUSH_SLEEP = 5, CBUSH_DRIVE_0 = 6, CBUSH_DRIVE1 = 7, CBUSH_IOMODE = 8, CBUSH_TXDEN = 9,
CBUSH_CLK30 = 10, CBUSH_CLK15 = 11, CBUSH_CLK7_5 = 12
};
enum ftdi_cbusx_func
{
CBUSX_TRISTATE = 0, CBUSX_TXLED = 1, CBUSX_RXLED = 2, CBUSX_TXRXLED = 3, CBUSX_PWREN = 4,
CBUSX_SLEEP = 5, CBUSX_DRIVE_0 = 6, CBUSX_DRIVE1 = 7, CBUSX_IOMODE = 8, CBUSX_TXDEN = 9,
CBUSX_CLK24 = 10, CBUSX_CLK12 = 11, CBUSX_CLK6 = 12, CBUSX_BAT_DETECT = 13,
CBUSX_BAT_DETECT_NEG = 14, CBUSX_I2C_TXE = 15, CBUSX_I2C_RXF = 16, CBUSX_VBUS_SENSE = 17,
CBUSX_BB_WR = 18, CBUSX_BB_RD = 19, CBUSX_TIME_STAMP = 20, CBUSX_AWAKE = 21
};
/** Invert TXD# */
#define INVERT_TXD 0x01
/** Invert RXD# */
#define INVERT_RXD 0x02
/** Invert RTS# */
#define INVERT_RTS 0x04
/** Invert CTS# */
#define INVERT_CTS 0x08
/** Invert DTR# */
#define INVERT_DTR 0x10
/** Invert DSR# */
#define INVERT_DSR 0x20
/** Invert DCD# */
#define INVERT_DCD 0x40
/** Invert RI# */
#define INVERT_RI 0x80
/** Interface Mode. */
#define CHANNEL_IS_UART 0x0
#define CHANNEL_IS_FIFO 0x1
#define CHANNEL_IS_OPTO 0x2
#define CHANNEL_IS_CPU 0x4
#define CHANNEL_IS_FT1284 0x8
#define CHANNEL_IS_RS485 0x10
#define DRIVE_4MA 0
#define DRIVE_8MA 1
#define DRIVE_12MA 2
#define DRIVE_16MA 3
#define SLOW_SLEW 4
#define IS_SCHMITT 8
/** Driver Type. */
#define DRIVER_VCP 0x08
#define DRIVER_VCPH 0x10 /* FT232H has moved the VCP bit */
#define USE_USB_VERSION_BIT 0x10
#define SUSPEND_DBUS7_BIT 0x80
/** High current drive. */
#define HIGH_CURRENT_DRIVE 0x10
#define HIGH_CURRENT_DRIVE_R 0x04
/**
\brief Progress Info for streaming read
*/
struct size_and_time
{
uint64_t totalBytes;
struct timeval time;
};
typedef struct
{
struct size_and_time first;
struct size_and_time prev;
struct size_and_time current;
double totalTime;
double totalRate;
double currentRate;
} FTDIProgressInfo;
typedef int (FTDIStreamCallback)(uint8_t *buffer, int length,
FTDIProgressInfo *progress, void *userdata);
/**
* Provide libftdi version information
* major: Library major version
* minor: Library minor version
* micro: Currently unused, ight get used for hotfixes.
* version_str: Version as (static) string
* snapshot_str: Git snapshot version if known. Otherwise "unknown" or empty string.
*/
struct ftdi_version_info
{
int major;
int minor;
int micro;
const char *version_str;
const char *snapshot_str;
};
#ifdef __cplusplus
extern "C"
{
#endif
int ftdi_init(struct ftdi_context *ftdi);
struct ftdi_context *ftdi_new(void);
int ftdi_set_interface(struct ftdi_context *ftdi, enum ftdi_interface interface);
void ftdi_deinit(struct ftdi_context *ftdi);
void ftdi_free(struct ftdi_context *ftdi);
void ftdi_set_usbdev (struct ftdi_context *ftdi, struct libusb_device_handle *usbdev);
struct ftdi_version_info ftdi_get_library_version(void);
int ftdi_usb_find_all(struct ftdi_context *ftdi, struct ftdi_device_list **devlist,
int vendor, int product);
void ftdi_list_free(struct ftdi_device_list **devlist);
void ftdi_list_free2(struct ftdi_device_list *devlist);
int ftdi_usb_get_strings(struct ftdi_context *ftdi, struct libusb_device *dev,
char *manufacturer, int mnf_len,
char *description, int desc_len,
char *serial, int serial_len);
int ftdi_usb_get_strings2(struct ftdi_context *ftdi, struct libusb_device *dev,
char *manufacturer, int mnf_len,
char *description, int desc_len,
char *serial, int serial_len);
int ftdi_eeprom_get_strings(struct ftdi_context *ftdi,
char *manufacturer, int mnf_len,
char *product, int prod_len,
char *serial, int serial_len);
int ftdi_eeprom_set_strings(struct ftdi_context *ftdi, char * manufacturer,
char * product, char * serial);
int ftdi_usb_open(struct ftdi_context *ftdi, int vendor, int product);
int ftdi_usb_open_desc(struct ftdi_context *ftdi, int vendor, int product,
const char* description, const char* serial);
int ftdi_usb_open_desc_index(struct ftdi_context *ftdi, int vendor, int product,
const char* description, const char* serial, unsigned int index);
int ftdi_usb_open_bus_addr(struct ftdi_context *ftdi, uint8_t bus, uint8_t addr);
int ftdi_usb_open_dev(struct ftdi_context *ftdi, struct libusb_device *dev);
int ftdi_usb_open_string(struct ftdi_context *ftdi, const char* description);
int ftdi_usb_close(struct ftdi_context *ftdi);
int ftdi_usb_reset(struct ftdi_context *ftdi);
int ftdi_usb_purge_rx_buffer(struct ftdi_context *ftdi);
int ftdi_usb_purge_tx_buffer(struct ftdi_context *ftdi);
int ftdi_usb_purge_buffers(struct ftdi_context *ftdi);
int ftdi_set_baudrate(struct ftdi_context *ftdi, int baudrate);
int ftdi_set_line_property(struct ftdi_context *ftdi, enum ftdi_bits_type bits,
enum ftdi_stopbits_type sbit, enum ftdi_parity_type parity);
int ftdi_set_line_property2(struct ftdi_context *ftdi, enum ftdi_bits_type bits,
enum ftdi_stopbits_type sbit, enum ftdi_parity_type parity,
enum ftdi_break_type break_type);
int ftdi_read_data(struct ftdi_context *ftdi, unsigned char *buf, int size);
int ftdi_read_data_set_chunksize(struct ftdi_context *ftdi, unsigned int chunksize);
int ftdi_read_data_get_chunksize(struct ftdi_context *ftdi, unsigned int *chunksize);
int ftdi_write_data(struct ftdi_context *ftdi, const unsigned char *buf, int size);
int ftdi_write_data_set_chunksize(struct ftdi_context *ftdi, unsigned int chunksize);
int ftdi_write_data_get_chunksize(struct ftdi_context *ftdi, unsigned int *chunksize);
int ftdi_readstream(struct ftdi_context *ftdi, FTDIStreamCallback *callback,
void *userdata, int packetsPerTransfer, int numTransfers);
struct ftdi_transfer_control *ftdi_write_data_submit(struct ftdi_context *ftdi, unsigned char *buf, int size);
struct ftdi_transfer_control *ftdi_read_data_submit(struct ftdi_context *ftdi, unsigned char *buf, int size);
int ftdi_transfer_data_done(struct ftdi_transfer_control *tc);
void ftdi_transfer_data_cancel(struct ftdi_transfer_control *tc, struct timeval * to);
int ftdi_set_bitmode(struct ftdi_context *ftdi, unsigned char bitmask, unsigned char mode);
int ftdi_disable_bitbang(struct ftdi_context *ftdi);
int ftdi_read_pins(struct ftdi_context *ftdi, unsigned char *pins);
int ftdi_set_latency_timer(struct ftdi_context *ftdi, unsigned char latency);
int ftdi_get_latency_timer(struct ftdi_context *ftdi, unsigned char *latency);
int ftdi_poll_modem_status(struct ftdi_context *ftdi, unsigned short *status);
/* flow control */
int ftdi_setflowctrl(struct ftdi_context *ftdi, int flowctrl);
int ftdi_setdtr_rts(struct ftdi_context *ftdi, int dtr, int rts);
int ftdi_setdtr(struct ftdi_context *ftdi, int state);
int ftdi_setrts(struct ftdi_context *ftdi, int state);
int ftdi_set_event_char(struct ftdi_context *ftdi, unsigned char eventch, unsigned char enable);
int ftdi_set_error_char(struct ftdi_context *ftdi, unsigned char errorch, unsigned char enable);
/* init eeprom for the given FTDI type */
int ftdi_eeprom_initdefaults(struct ftdi_context *ftdi,
char * manufacturer, char *product,
char * serial);
int ftdi_eeprom_build(struct ftdi_context *ftdi);
int ftdi_eeprom_decode(struct ftdi_context *ftdi, int verbose);
int ftdi_get_eeprom_value(struct ftdi_context *ftdi, enum ftdi_eeprom_value value_name, int* value);
int ftdi_set_eeprom_value(struct ftdi_context *ftdi, enum ftdi_eeprom_value value_name, int value);
int ftdi_get_eeprom_buf(struct ftdi_context *ftdi, unsigned char * buf, int size);
int ftdi_set_eeprom_buf(struct ftdi_context *ftdi, const unsigned char * buf, int size);
int ftdi_set_eeprom_user_data(struct ftdi_context *ftdi, const char * buf, int size);
int ftdi_read_eeprom(struct ftdi_context *ftdi);
int ftdi_read_chipid(struct ftdi_context *ftdi, unsigned int *chipid);
int ftdi_write_eeprom(struct ftdi_context *ftdi);
int ftdi_erase_eeprom(struct ftdi_context *ftdi);
int ftdi_read_eeprom_location (struct ftdi_context *ftdi, int eeprom_addr, unsigned short *eeprom_val);
int ftdi_write_eeprom_location(struct ftdi_context *ftdi, int eeprom_addr, unsigned short eeprom_val);
const char *ftdi_get_error_string(struct ftdi_context *ftdi);
#ifdef __cplusplus
}
#endif
#endif /* __libftdi_h__ */
+143
View File
@@ -0,0 +1,143 @@
/***************************************************************************
ftdi_i.h - description
-------------------
begin : Don Sep 9 2011
copyright : (C) 2003-2017 by Intra2net AG and the libftdi developers
email : opensource@intra2net.com
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License *
* version 2.1 as published by the Free Software Foundation; *
* *
***************************************************************************
Non public definitions here
*/
/* Even on 93xx66 at max 256 bytes are used (AN_121)*/
#define FTDI_MAX_EEPROM_SIZE 256
/** Max Power adjustment factor. */
#define MAX_POWER_MILLIAMP_PER_UNIT 2
/**
\brief FTDI eeprom structure
*/
struct ftdi_eeprom
{
/** vendor id */
int vendor_id;
/** product id */
int product_id;
/** Was the eeprom structure initialized for the actual
connected device? **/
int initialized_for_connected_device;
/** self powered */
int self_powered;
/** remote wakeup */
int remote_wakeup;
int is_not_pnp;
/* Suspend on DBUS7 Low */
int suspend_dbus7;
/** input in isochronous transfer mode */
int in_is_isochronous;
/** output in isochronous transfer mode */
int out_is_isochronous;
/** suspend pull downs */
int suspend_pull_downs;
/** use serial */
int use_serial;
/** usb version */
int usb_version;
/** Use usb version on FT2232 devices*/
int use_usb_version;
/** maximum power */
int max_power;
/** manufacturer name */
char *manufacturer;
/** product name */
char *product;
/** serial number */
char *serial;
/* 2232D/H specific */
/* Hardware type, 0 = RS232 Uart, 1 = 245 FIFO, 2 = CPU FIFO,
4 = OPTO Isolate */
int channel_a_type;
int channel_b_type;
/* Driver Type, 1 = VCP */
int channel_a_driver;
int channel_b_driver;
int channel_c_driver;
int channel_d_driver;
/* 4232H specific */
int channel_a_rs485enable;
int channel_b_rs485enable;
int channel_c_rs485enable;
int channel_d_rs485enable;
/* Special function of FT232R/FT232H devices (and possibly others as well) */
/** CBUS pin function. See CBUS_xxx defines. */
int cbus_function[10];
/** Select hight current drive on R devices. */
int high_current;
/** Select hight current drive on A channel (2232C */
int high_current_a;
/** Select hight current drive on B channel (2232C). */
int high_current_b;
/** Select inversion of data lines (bitmask). */
int invert;
/** Enable external oscillator. */
int external_oscillator;
/*2232H/4432H Group specific values */
/* Group0 is AL on 2322H and A on 4232H
Group1 is AH on 2232H and B on 4232H
Group2 is BL on 2322H and C on 4232H
Group3 is BH on 2232H and C on 4232H*/
int group0_drive;
int group0_schmitt;
int group0_slew;
int group1_drive;
int group1_schmitt;
int group1_slew;
int group2_drive;
int group2_schmitt;
int group2_slew;
int group3_drive;
int group3_schmitt;
int group3_slew;
int powersave;
int clock_polarity;
int data_order;
int flow_control;
/** user data **/
int user_data_addr;
int user_data_size;
const char *user_data;
/** eeprom size in bytes. This doesn't get stored in the eeprom
but is the only way to pass it to ftdi_eeprom_build. */
int size;
/* EEPROM Type 0x46 for 93xx46, 0x56 for 93xx56 and 0x66 for 93xx66*/
int chip;
unsigned char buf[FTDI_MAX_EEPROM_SIZE];
/** device release number */
int release_number;
};
+300
View File
@@ -0,0 +1,300 @@
/***************************************************************************
ftdi_stream.c - description
-------------------
copyright : (C) 2009 Micah Dowty 2010 Uwe Bonnes
email : opensource@intra2net.com
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License *
* version 2.1 as published by the Free Software Foundation; *
* *
***************************************************************************/
/* Adapted from
* fastftdi.c - A minimal FTDI FT232H interface for which supports bit-bang
* mode, but focuses on very high-performance support for
* synchronous FIFO mode. Requires libusb-1.0
*
* Copyright (C) 2009 Micah Dowty
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <stdlib.h>
#include <stdio.h>
#include <sys/time.h>
#include <libusb.h>
#include "ftdi.h"
typedef struct
{
FTDIStreamCallback *callback;
void *userdata;
int packetsize;
int activity;
int result;
FTDIProgressInfo progress;
} FTDIStreamState;
/* Handle callbacks
*
* With Exit request, free memory and release the transfer
*
* state->result is only set when some error happens
*/
static void LIBUSB_CALL
ftdi_readstream_cb(struct libusb_transfer *transfer)
{
FTDIStreamState *state = transfer->user_data;
int packet_size = state->packetsize;
state->activity++;
if (transfer->status == LIBUSB_TRANSFER_COMPLETED)
{
int i;
uint8_t *ptr = transfer->buffer;
int length = transfer->actual_length;
int numPackets = (length + packet_size - 1) / packet_size;
int res = 0;
for (i = 0; i < numPackets; i++)
{
int payloadLen;
int packetLen = length;
if (packetLen > packet_size)
packetLen = packet_size;
payloadLen = packetLen - 2;
state->progress.current.totalBytes += payloadLen;
res = state->callback(ptr + 2, payloadLen,
NULL, state->userdata);
ptr += packetLen;
length -= packetLen;
}
if (res)
{
free(transfer->buffer);
libusb_free_transfer(transfer);
}
else
{
transfer->status = -1;
state->result = libusb_submit_transfer(transfer);
}
}
else
{
fprintf(stderr, "unknown status %d\n",transfer->status);
state->result = LIBUSB_ERROR_IO;
}
}
/**
Helper function to calculate (unix) time differences
\param a timeval
\param b timeval
*/
static double
TimevalDiff(const struct timeval *a, const struct timeval *b)
{
return (a->tv_sec - b->tv_sec) + 1e-6 * (a->tv_usec - b->tv_usec);
}
/**
Streaming reading of data from the device
Use asynchronous transfers in libusb-1.0 for high-performance
streaming of data from a device interface back to the PC. This
function continuously transfers data until either an error occurs
or the callback returns a nonzero value. This function returns
a libusb error code or the callback's return value.
For every contiguous block of received data, the callback will
be invoked.
\param ftdi pointer to ftdi_context
\param callback to user supplied function for one block of data
\param userdata
\param packetsPerTransfer number of packets per transfer
\param numTransfers Number of transfers per callback
*/
int
ftdi_readstream(struct ftdi_context *ftdi,
FTDIStreamCallback *callback, void *userdata,
int packetsPerTransfer, int numTransfers)
{
struct libusb_transfer **transfers;
FTDIStreamState state = { callback, userdata, ftdi->max_packet_size, 1 };
int bufferSize = packetsPerTransfer * ftdi->max_packet_size;
int xferIndex;
int err = 0;
/* Only FT2232H and FT232H know about the synchronous FIFO Mode*/
if ((ftdi->type != TYPE_2232H) && (ftdi->type != TYPE_232H))
{
fprintf(stderr,"Device doesn't support synchronous FIFO mode\n");
return 1;
}
/* We don't know in what state we are, switch to reset*/
if (ftdi_set_bitmode(ftdi, 0xff, BITMODE_RESET) < 0)
{
fprintf(stderr,"Can't reset mode\n");
return 1;
}
/* Purge anything remaining in the buffers*/
if (ftdi_usb_purge_buffers(ftdi) < 0)
{
fprintf(stderr,"Can't Purge\n");
return 1;
}
/*
* Set up all transfers
*/
transfers = calloc(numTransfers, sizeof *transfers);
if (!transfers)
{
err = LIBUSB_ERROR_NO_MEM;
goto cleanup;
}
for (xferIndex = 0; xferIndex < numTransfers; xferIndex++)
{
struct libusb_transfer *transfer;
transfer = libusb_alloc_transfer(0);
transfers[xferIndex] = transfer;
if (!transfer)
{
err = LIBUSB_ERROR_NO_MEM;
goto cleanup;
}
libusb_fill_bulk_transfer(transfer, ftdi->usb_dev, ftdi->out_ep,
malloc(bufferSize), bufferSize,
ftdi_readstream_cb,
&state, 0);
if (!transfer->buffer)
{
err = LIBUSB_ERROR_NO_MEM;
goto cleanup;
}
transfer->status = -1;
err = libusb_submit_transfer(transfer);
if (err)
goto cleanup;
}
/* Start the transfers only when everything has been set up.
* Otherwise the transfers start stuttering and the PC not
* fetching data for several to several ten milliseconds
* and we skip blocks
*/
if (ftdi_set_bitmode(ftdi, 0xff, BITMODE_SYNCFF) < 0)
{
fprintf(stderr,"Can't set synchronous fifo mode: %s\n",
ftdi_get_error_string(ftdi));
goto cleanup;
}
/*
* Run the transfers, and periodically assess progress.
*/
gettimeofday(&state.progress.first.time, NULL);
do
{
FTDIProgressInfo *progress = &state.progress;
const double progressInterval = 1.0;
struct timeval timeout = { 0, ftdi->usb_read_timeout * 1000};
struct timeval now;
int err = libusb_handle_events_timeout(ftdi->usb_ctx, &timeout);
if (err == LIBUSB_ERROR_INTERRUPTED)
/* restart interrupted events */
err = libusb_handle_events_timeout(ftdi->usb_ctx, &timeout);
if (!state.result)
{
state.result = err;
}
if (state.activity == 0)
state.result = 1;
else
state.activity = 0;
// If enough time has elapsed, update the progress
gettimeofday(&now, NULL);
if (TimevalDiff(&now, &progress->current.time) >= progressInterval)
{
progress->current.time = now;
progress->totalTime = TimevalDiff(&progress->current.time,
&progress->first.time);
if (progress->prev.totalBytes)
{
// We have enough information to calculate rates
double currentTime;
currentTime = TimevalDiff(&progress->current.time,
&progress->prev.time);
progress->totalRate =
progress->current.totalBytes /progress->totalTime;
progress->currentRate =
(progress->current.totalBytes -
progress->prev.totalBytes) / currentTime;
}
state.callback(NULL, 0, progress, state.userdata);
progress->prev = progress->current;
}
} while (!state.result);
/*
* Cancel any outstanding transfers, and free memory.
*/
cleanup:
fprintf(stderr, "cleanup\n");
if (transfers)
free(transfers);
if (err)
return err;
else
return state.result;
}
+11
View File
@@ -0,0 +1,11 @@
#ifndef FTDI_VERSION_INTERNAL_H
#define FTDI_VERSION_INTERNAL_H
#define FTDI_MAJOR_VERSION @MAJOR_VERSION@
#define FTDI_MINOR_VERSION @MINOR_VERSION@
#define FTDI_MICRO_VERSION 0
const char FTDI_VERSION_STRING[] = "@VERSION_STRING@";
const char FTDI_SNAPSHOT_VERSION[] = "@SNAPSHOT_VERSION@";
#endif
+38
View File
@@ -0,0 +1,38 @@
# Optional unit test
if(BUILD_TESTS)
find_package(Boost COMPONENTS unit_test_framework)
if(Boost_UNIT_TEST_FRAMEWORK_FOUND)
message(STATUS "Building unit test")
enable_testing()
INCLUDE_DIRECTORIES(BEFORE ${CMAKE_SOURCE_DIR}/src ${Boost_INCLUDE_DIRS})
set(cpp_tests
basic.cpp
baudrate.cpp
)
add_executable(test_libftdi1 ${cpp_tests})
target_link_libraries(test_libftdi1 ftdi1 ${Boost_UNIT_TEST_FRAMEWORK_LIBRARIES})
add_test(test_libftdi1 test_libftdi1)
# Add custom target so we run easily run "make check"
add_custom_target(check COMMAND ${CMAKE_CTEST_COMMAND} DEPENDS test_libftdi1)
else(Boost_UNIT_TEST_FRAMEWORK_FOUND)
message(STATUS "NOT building unit test (requires boost unit test framework)")
endif(Boost_UNIT_TEST_FRAMEWORK_FOUND)
else(BUILD_TESTS)
message(STATUS "NOT building unit test")
endif(BUILD_TESTS)
+33
View File
@@ -0,0 +1,33 @@
/**@file
@brief Test basic FTDI functionality
@author Thomas Jarosch
*/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License *
* version 2.1 as published by the Free Software Foundation; *
* *
***************************************************************************/
#define BOOST_TEST_DYN_LINK
#define BOOST_TEST_MAIN
#include <boost/test/unit_test.hpp>
#include <ftdi.h>
BOOST_AUTO_TEST_SUITE(Basic)
BOOST_AUTO_TEST_CASE(SimpleInit)
{
ftdi_context ftdi;
int rtn_init = ftdi_init(&ftdi);
BOOST_REQUIRE_EQUAL(0, rtn_init);
ftdi_deinit(&ftdi);
}
BOOST_AUTO_TEST_SUITE_END()
+269
View File
@@ -0,0 +1,269 @@
/**@file
@brief Test baudrate calculator code
@author Thomas Jarosch and Uwe Bonnes
*/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License *
* version 2.1 as published by the Free Software Foundation; *
* *
***************************************************************************/
#include <ftdi.h>
#define BOOST_TEST_DYN_LINK
#include <boost/test/unit_test.hpp>
#include <boost/foreach.hpp>
#include <vector>
#include <map>
#include <math.h>
using namespace std;
extern "C" int convert_baudrate_UT_export(int baudrate, struct ftdi_context *ftdi,
unsigned short *value, unsigned short *index);
/// Basic initialization of libftdi for every test
class BaseFTDIFixture
{
protected:
ftdi_context *ftdi;
public:
BaseFTDIFixture()
: ftdi(NULL)
{
ftdi = ftdi_new();
}
virtual ~BaseFTDIFixture()
{
delete ftdi;
ftdi = NULL;
}
};
BOOST_FIXTURE_TEST_SUITE(Baudrate, BaseFTDIFixture)
/// Helper class to store the convert_baudrate_UT_export result
struct calc_result
{
int actual_baudrate;
unsigned short divisor;
unsigned short fractional_bits;
unsigned short clock;
calc_result(int actual, unsigned short my_divisor, unsigned short my_fractional_bits, unsigned short my_clock)
: actual_baudrate(actual)
, divisor(my_divisor)
, fractional_bits(my_fractional_bits)
, clock(my_clock)
{
}
calc_result()
: actual_baudrate(0)
, divisor(0)
, fractional_bits(0)
, clock(0)
{
}
};
/**
* @brief Test convert_baudrate code against a list of baud rates
*
* @param baudrates Baudrates to check
**/
static void test_baudrates(ftdi_context *ftdi, const map<int, calc_result> &baudrates)
{
typedef std::pair<int, calc_result> baudrate_type;
BOOST_FOREACH(const baudrate_type &baudrate, baudrates)
{
unsigned short calc_value = 0, calc_index = 0;
int calc_baudrate = convert_baudrate_UT_export(baudrate.first, ftdi, &calc_value, &calc_index);
const calc_result *res = &baudrate.second;
unsigned short divisor = calc_value & 0x3fff;
unsigned short fractional_bits = (calc_value >> 14);
unsigned short clock = (calc_index & 0x200) ? 120 : 48;
switch (ftdi->type)
{
case TYPE_232H:
case TYPE_2232H:
case TYPE_4232H:
fractional_bits |= (calc_index & 0x100) ? 4 : 0;
break;
case TYPE_R:
case TYPE_2232C:
case TYPE_BM:
case TYPE_230X:
fractional_bits |= (calc_index & 0x001) ? 4 : 0;
break;
default:;
}
// Aid debugging since this test is a generic function
BOOST_CHECK_MESSAGE(res->actual_baudrate == calc_baudrate && res->divisor == divisor && res->fractional_bits == fractional_bits
&& res->clock == clock,
"\n\nERROR: baudrate calculation failed for --" << baudrate.first << " baud--. Details below: ");
BOOST_CHECK_EQUAL(res->actual_baudrate, calc_baudrate);
BOOST_CHECK_EQUAL(res->divisor, divisor);
BOOST_CHECK_EQUAL(res->fractional_bits, fractional_bits);
BOOST_CHECK_EQUAL(res->clock, clock);
}
}
BOOST_AUTO_TEST_CASE(TypeAMFixedBaudrates)
{
ftdi->type = TYPE_AM;
map<int, calc_result> baudrates;
baudrates[183] = calc_result(183, 16383, 0, 48);
baudrates[300] = calc_result(300, 10000, 0, 48);
baudrates[600] = calc_result(600, 5000, 0, 48);
baudrates[1200] = calc_result(1200, 2500, 0, 48);
baudrates[2400] = calc_result(2400, 1250, 0, 48);
baudrates[4800] = calc_result(4800, 625, 0, 48);
baudrates[9600] = calc_result(9600, 312, 1, 48);
baudrates[19200] = calc_result(19200, 156, 2, 48);
baudrates[38400] = calc_result(38400, 78, 3, 48);
baudrates[57600] = calc_result(57554, 52, 3, 48);
baudrates[115200] = calc_result(115385, 26, 0, 48);
baudrates[230400] = calc_result(230769, 13, 0, 48);
baudrates[460800] = calc_result(461538, 6, 1, 48);
baudrates[921600] = calc_result(923077, 3, 2, 48);
baudrates[1000000] = calc_result(1000000, 3, 0, 48);
baudrates[1090512] = calc_result(1000000, 3, 0, 48);
baudrates[1090909] = calc_result(1000000, 3, 0, 48);
baudrates[1090910] = calc_result(1000000, 3, 0, 48);
baudrates[1200000] = calc_result(1200000, 2, 1, 48);
baudrates[1333333] = calc_result(1333333, 2, 2, 48);
baudrates[1411764] = calc_result(1411765, 2, 3, 48);
baudrates[1500000] = calc_result(1500000, 2, 0, 48);
baudrates[2000000] = calc_result(1500000, 2, 0, 48);
baudrates[3000000] = calc_result(3000000, 0, 0, 48);
test_baudrates(ftdi, baudrates);
}
BOOST_AUTO_TEST_CASE(TypeBMFixedBaudrates)
{
// Unify testing of chips behaving the same
std::vector<enum ftdi_chip_type> test_types;
test_types.push_back(TYPE_BM);
test_types.push_back(TYPE_2232C);
test_types.push_back(TYPE_R);
test_types.push_back(TYPE_230X);
map<int, calc_result> baudrates;
baudrates[183] = calc_result(183, 16383, 7, 48);
baudrates[184] = calc_result(184, 16304, 4, 48);
baudrates[300] = calc_result(300, 10000, 0, 48);
baudrates[600] = calc_result(600, 5000, 0, 48);
baudrates[1200] = calc_result(1200, 2500, 0, 48);
baudrates[2400] = calc_result(2400, 1250, 0, 48);
baudrates[4800] = calc_result(4800, 625, 0, 48);
baudrates[9600] = calc_result(9600, 312, 1, 48);
baudrates[19200] = calc_result(19200, 156, 2, 48);
baudrates[38400] = calc_result(38400, 78, 3, 48);
baudrates[57600] = calc_result(57554, 52, 3, 48);
baudrates[115200] = calc_result(115385, 26, 0, 48);
baudrates[230400] = calc_result(230769, 13, 0, 48);
baudrates[460800] = calc_result(461538, 6, 1, 48);
baudrates[921600] = calc_result(923077, 3, 2, 48);
baudrates[1000000] = calc_result(1000000, 3, 0, 48);
baudrates[1050000] = calc_result(1043478, 2, 7, 48);
baudrates[1400000] = calc_result(1411765, 2, 3, 48);
baudrates[1500000] = calc_result(1500000, 2, 0, 48);
baudrates[2000000] = calc_result(2000000, 1, 0, 48);
baudrates[3000000] = calc_result(3000000, 0, 0, 48);
baudrates[(3000000*16/(2*16+15))-1] = calc_result(round(3000000/3.000), 3, 0, 48);
baudrates[ 3000000*16/(2*16+15) ] = calc_result(round(3000000/3.000), 3, 0, 48);
baudrates[(3000000*16/(2*16+15))+1] = calc_result(round(3000000/2.875), 2, 7, 48);
baudrates[ 3000000*16/(2*16+13) ] = calc_result(round(3000000/2.875), 2, 7, 48);
baudrates[(3000000*16/(2*16+13))+1] = calc_result(round(3000000/2.750), 2, 6, 48);
baudrates[ 3000000*16/(2*16+11) ] = calc_result(round(3000000/2.750), 2, 6, 48);
baudrates[(3000000*16/(2*16+11))+1] = calc_result(round(3000000/2.625), 2, 5, 48);
baudrates[ 3000000*16/(2*16+ 9) ] = calc_result(round(3000000/2.625), 2, 5, 48);
baudrates[(3000000*16/(2*16+ 9))+1] = calc_result(round(3000000/2.500), 2, 1, 48);
baudrates[ 3000000*16/(2*16+ 7) ] = calc_result(round(3000000/2.500), 2, 1, 48);
baudrates[(3000000*16/(2*16+ 7))+1] = calc_result(round(3000000/2.375), 2, 4, 48);
baudrates[ 3000000*16/(2*16+ 5) ] = calc_result(round(3000000/2.375), 2, 4, 48);
baudrates[(3000000*16/(2*16+ 5))+1] = calc_result(round(3000000/2.250), 2, 2, 48);
baudrates[ 3000000*16/(2*16+ 3) ] = calc_result(round(3000000/2.250), 2, 2, 48);
baudrates[(3000000*16/(2*16+ 3))+1] = calc_result(round(3000000/2.125), 2, 3, 48);
baudrates[ 3000000*16/(2*16+ 1) ] = calc_result(round(3000000/2.125), 2, 3, 48);
baudrates[(3000000*16/(2*16+ 1))+1] = calc_result(round(3000000/2.000), 2, 0, 48);
BOOST_FOREACH(const enum ftdi_chip_type &test_chip_type, test_types)
{
ftdi->type = test_chip_type;
test_baudrates(ftdi, baudrates);
}
}
BOOST_AUTO_TEST_CASE(TypeHFixedBaudrates)
{
// Unify testing of chips behaving the same
std::vector<enum ftdi_chip_type> test_types;
test_types.push_back(TYPE_2232H);
test_types.push_back(TYPE_4232H);
test_types.push_back(TYPE_232H);
map<int, calc_result> baudrates;
baudrates[183] = calc_result(183, 16383, 7, 48);
baudrates[184] = calc_result(184, 16304, 4, 48);
baudrates[300] = calc_result(300, 10000, 0, 48);
baudrates[600] = calc_result(600, 5000, 0, 48);
baudrates[1200] = calc_result(1200, 10000, 0, 120);
baudrates[2400] = calc_result(2400, 5000, 0, 120);
baudrates[4800] = calc_result(4800, 2500, 0, 120);
baudrates[9600] = calc_result(9600, 1250, 0, 120);
baudrates[19200] = calc_result(19200, 625, 0, 120);
baudrates[38400] = calc_result(38400, 312, 1, 120);
baudrates[57600] = calc_result(57588, 208, 4, 120);
baudrates[115200] = calc_result(115246, 104, 3, 120);
baudrates[230400] = calc_result(230216, 52, 3, 120);
baudrates[460800] = calc_result(461538, 26, 0, 120);
baudrates[921600] = calc_result(923077, 13, 0, 120);
baudrates[1000000] = calc_result(1000000, 12, 0, 120);
baudrates[1000000] = calc_result(1000000, 12, 0, 120);
baudrates[6000000] = calc_result(6000000, 2, 0, 120);
baudrates[4173913] = calc_result(4173913, 2, 7, 120);
baudrates[8000000] = calc_result(8000000, 1, 0, 120);
baudrates[12000000] = calc_result(12000000, 0, 0, 120);
baudrates[(12000000*16/(2*16+15))-1] = calc_result(round(12000000/3.000), 3, 0, 120);
baudrates[ 12000000*16/(2*16+15) ] = calc_result(round(12000000/3.000), 3, 0, 120);
baudrates[(12000000*16/(2*16+15))+1] = calc_result(round(12000000/2.875), 2, 7, 120);
baudrates[ 12000000*16/(2*16+13) ] = calc_result(round(12000000/2.875), 2, 7, 120);
baudrates[(12000000*16/(2*16+13))+1] = calc_result(round(12000000/2.750), 2, 6, 120);
baudrates[ 12000000*16/(2*16+11) ] = calc_result(round(12000000/2.750), 2, 6, 120);
baudrates[(12000000*16/(2*16+11))+1] = calc_result(round(12000000/2.625), 2, 5, 120);
baudrates[ 12000000*16/(2*16+ 9) ] = calc_result(round(12000000/2.625), 2, 5, 120);
baudrates[(12000000*16/(2*16+ 9))+1] = calc_result(round(12000000/2.500), 2, 1, 120);
baudrates[ 12000000*16/(2*16+ 7) ] = calc_result(round(12000000/2.500), 2, 1, 120);
baudrates[(12000000*16/(2*16+ 7))+1] = calc_result(round(12000000/2.375), 2, 4, 120);
baudrates[ 12000000*16/(2*16+ 5) ] = calc_result(round(12000000/2.375), 2, 4, 120);
baudrates[(12000000*16/(2*16+ 5))+1] = calc_result(round(12000000/2.250), 2, 2, 120);
baudrates[ 12000000*16/(2*16+ 3) ] = calc_result(round(12000000/2.250), 2, 2, 120);
baudrates[(12000000*16/(2*16+ 3))+1] = calc_result(round(12000000/2.125), 2, 3, 120);
baudrates[ 12000000*16/(2*16+ 1) ] = calc_result(round(12000000/2.125), 2, 3, 120);
baudrates[(12000000*16/(2*16+ 1))+1] = calc_result(round(12000000/2.000), 2, 0, 120);
BOOST_FOREACH(const enum ftdi_chip_type &test_chip_type, test_types)
{
ftdi->type = test_chip_type;
test_baudrates(ftdi, baudrates);
}
}
BOOST_AUTO_TEST_SUITE_END()