liteusb: refactor software (use python instead of libftdicom in C) and provide simple example.

small modifications to fastftdi.c are also done to select our interface (A or B) and mode (synchronous, asynchronous)
This commit is contained in:
Florent Kermarrec 2015-05-01 16:15:15 +02:00
parent 603b4cdc8c
commit da0fe2ecfb
13 changed files with 511 additions and 489 deletions

View file

@ -0,0 +1,14 @@
[> Libftdicom
------------------------------
[> Windows build
--------------------------
1. Install MinGW32
2. Download libusbx windows binaries (tested version: libusbx-1.0.17-win)
3. Put libusb-1.0.dll.a in mingw lib directory
4. Download Zadig and use WinUSB driver for Interface A and Interface B
5. make all in libftdicom/win
[> Linux build
--------------------------
1. make all in libftdicom/linux

View file

@ -0,0 +1,352 @@
import platform
import ctypes
import os
import time
import queue
import threading
_lpath = (os.path.dirname(__file__))
if _lpath == '':
_lpath = '.'
if platform.system() == "Windows":
libftdicom = ctypes.cdll.LoadLibrary(_lpath + "/libftdicom.dll")
else:
libftdicom = ctypes.cdll.LoadLibrary(_lpath + "/libftdicom.so")
class FTDI_Device(ctypes.Structure):
_fields_ = [
('_1', ctypes.c_void_p),
('_2', ctypes.c_void_p),
]
pFTDI_Device = ctypes.POINTER(FTDI_Device)
# FTDIDevice_Open
FTDIDevice_Open = libftdicom.FTDIDevice_Open
FTDIDevice_Open.argtypes = [
pFTDI_Device, # Dev
ctypes.c_int # Interface
]
FTDIDevice_Open.restype = ctypes.c_int
# FTDIDevice_Close
FTDIDevice_Close = libftdicom.FTDIDevice_Close
FTDIDevice_Close.argtypes = [pFTDI_Device]
FTDIDevice_SetMode = libftdicom.FTDIDevice_SetMode
FTDIDevice_SetMode.argtypes = [
pFTDI_Device, # Dev
ctypes.c_int, # Interface
ctypes.c_int, # Mode
ctypes.c_char, # PinDirection
ctypes.c_char, # baudrate
]
FTDIDevice_Write = libftdicom.FTDIDevice_Write
FTDIDevice_Write.argtypes = [
pFTDI_Device, # Dev
ctypes.c_int, # Interface
ctypes.c_char_p, # Buf
ctypes.c_size_t, # N
ctypes.c_bool, # async
]
FTDIDevice_Write.restype = ctypes.c_int
p_cb_StreamCallback = ctypes.CFUNCTYPE(
ctypes.c_int, # retval
ctypes.POINTER(ctypes.c_uint8), # buf
ctypes.c_int, # length
ctypes.c_void_p, # progress
ctypes.c_void_p) # userdata
FTDIDevice_ReadStream = libftdicom.FTDIDevice_ReadStream
FTDIDevice_ReadStream.argtypes = [
pFTDI_Device, # dev
ctypes.c_int, # interface
p_cb_StreamCallback, # callback
ctypes.c_void_p, # userdata
ctypes.c_int, # packetsPerTransfer
ctypes.c_int, # numTransfers
]
FTDIDevice_ReadStream.restype = ctypes.c_int
FTDI_INTERFACE_A = 1
FTDI_INTERFACE_B = 2
FTDI_BITMODE_SYNC_FIFO = (1 << 6)
class FTDIDevice:
def __init__(self, interface, mode):
self.__is_open = False
self._dev = FTDI_Device()
self.interface = interface
self.mode = mode
def __del__(self):
if self.__is_open:
self.__is_open = False
FTDIDevice_Close(self._dev)
def open(self):
err = FTDIDevice_Open(self._dev, self.interface)
if err:
return err
else:
self.__is_open = True
if self.mode == "synchronous":
err = FTDIDevice_SetMode(self._dev, interface, FTDI_BITMODE_SYNC_FIFO, 0xFF, 0)
return err
def write(self, intf, buf, async=False):
if not isinstance(buf, bytes):
raise TypeError("buf must be bytes")
return FTDIDevice_Write(self._dev, intf, buf, len(buf), async)
def read(self, intf, n):
buf = []
def callback(b, prog):
buf.extend(b)
return int(len(buf) >= n)
self.read_async(intf, callback, 4, 4)
return buf
def read_async(self, intf, callback, packetsPerTransfer, numTransfers):
def callback_wrapper(buf, ll, prog, user):
if ll:
b = ctypes.string_at(buf, ll)
else:
b = b''
return callback(b, prog)
cb = p_cb_StreamCallback(callback_wrapper)
return FTDIDevice_ReadStream(self._dev, intf, cb,
None, packetsPerTransfer, numTransfers)
class ProtocolError(Exception):
pass
class TimeoutError(Exception):
pass
INCOMPLETE = -1
UNMATCHED = 0
class BaseService:
def match_identifier(self, byt):
r = True
r = r and (byt[0] == 0x5A)
r = r and (byt[1] == 0xA5)
r = r and (byt[2] == 0x5A)
r = r and (byt[3] == 0xA5)
r = r and (byt[4] == self.tag)
return r
def get_needed_size_for_identifier(self):
return self.NEEDED_FOR_SIZE
def present_bytes(self, b):
if len(b) < self.get_needed_size_for_identifier():
return INCOMPLETE
if not self.match_identifier(b):
return UNMATCHED
size = self.get_packet_size(b)
if len(b) < size:
return INCOMPLETE
self.consume(b[:size])
return size
class UART:
class __UARTService(BaseService):
NEEDED_FOR_SIZE = 9
def __init__(self, tag):
self.tag = tag
self.q = queue.Queue()
def get_packet_size(self, buf):
return 10
def consume(self, buf):
self.q.put(buf[9])
def __init__(self, tag):
self.tag = tag
self.service = UART.__UARTService(self.tag)
def do_read(self, timeout=None):
try:
resp = self.service.q.get(True, timeout)
except queue.Empty:
return -1
return resp
def do_write(self, value):
msg = [0x5A, 0xA5, 0x5A, 0xA5, self.tag, 0x00, 0x00, 0x00, 1, value&0xFF]
self.service.write(bytes(msg))
class DMA:
class __DMAService(BaseService):
NEEDED_FOR_SIZE = 9
def __init__(self, tag):
self.tag = tag
self.q = queue.Queue()
def get_packet_size(self, buf):
payload_size = buf[5] << 24
payload_size |= buf[6] << 16
payload_size |= buf[7] << 8
payload_size |= buf[8] << 0
return 9 + payload_size
def consume(self, buf):
self.q.put(buf[9:])
def __init__(self, tag):
self.tag = tag
self.service = DMA.__DMAService(self.tag)
def do_read(self, timeout=None):
try:
resp = list(self.service.q.get(True, timeout))
except queue.Empty:
raise TimeoutError("DMA read timed out")
return resp
def do_write(self, data):
length = len(data)
msg = [0x5A, 0xA5, 0x5A, 0xA5, self.tag,
(length & 0xff000000) >> 24,
(length & 0x00ff0000) >> 16,
(length & 0x0000ff00) >> 8,
(length & 0x000000ff) >> 0]
msg += data
self.service.write(bytes(msg))
class FTDIComDevice:
def __init__(self, interface, mode, uart_tag=0, dma_tag=1, verbose=False):
self.__is_open = False
self.interface = interface
self.mode = mode
self.dev = FTDIDevice(interface, mode)
self.verbose = verbose
self.uart = UART(uart_tag)
self.dma = DMA(dma_tag)
self.__services = [self.uart.service, self.dma.service]
# Inject a write function into the services
for service in self.__services:
def write(msg):
if self.verbose:
print("< %s" % " ".join("%02x" % i for i in msg))
self.dev.write(self.interface, msg, async=False)
service.write = write
def __comms(self):
self.__buf = b""
def callback(b, prog):
try:
if self.verbose and b:
print("> %s" % " ".join("%02x" % i for i in b))
self.__buf += b
incomplete = False
while self.__buf and not incomplete:
for service in self.__services:
code = service.present_bytes(self.__buf)
if code == INCOMPLETE:
incomplete = True
break
elif code:
self.__buf = self.__buf[code:]
break
else:
self.__buf = self.__buf[1:]
return int(self.__comm_term)
except Exception as e:
self.__comm_term = True
self.__comm_exc = e
return 1
while not self.__comm_term:
self.dev.read_async(self.interface, callback, 8, 16)
if self.__comm_exc:
raise self.__comm_exc
def __del__(self):
if self.__is_open:
self.close()
def open(self):
if self.__is_open:
raise ValueError("FTDICOMDevice doubly opened")
stat = self.dev.open()
if stat:
print("USB: Error opening device\n")
return stat
self.commthread = threading.Thread(target=self.__comms, daemon=True)
self.__comm_term = False
self.__comm_exc = None
self.commthread.start()
self.__comm_term = False
self.__is_open = True
def close(self):
if not self.__is_open:
raise ValueError("FTDICOMDevice doubly closed")
self.__comm_term = True
self.commthread.join()
self.__is_open = False
def uartflush(self, timeout=0.25):
while (self.uartread(timeout) != -1):
pass
def uartread(self, timeout=None):
return self.uart.do_read(timeout)
def uartwrite(self, value):
return self.uart.do_write(value)
def dmaread(self):
return self.dma.do_read()
def dmawrite(self, data):
return self.dma.do_write(data)

View file

@ -0,0 +1,81 @@
import platform
import os
import sys
import time
import threading
# XXX FTDI Communication POC
sys.path.append("../")
from ftdi import FTDIComDevice, FTDI_INTERFACE_B
def uart_console(ftdi_com):
def read():
while True:
print(chr(ftdi_com.uartread()), end="")
readthread = threading.Thread(target=read, daemon=True)
readthread.start()
def write():
while True:
for e in input():
c = ord(e)
ftdi_com.uartwrite(c)
ftdi_com.uartwrite(ord("\n"))
writethread = threading.Thread(target=write, daemon=True)
writethread.start()
def uart_virtual(ftdi_com):
import pty, serial
master, slave = pty.openpty()
s_name = os.ttyname(slave)
ser = serial.Serial(s_name)
def read():
while True:
s = ftdi_com.uartread()
s = bytes(chr(s).encode('utf-8'))
os.write(master, s)
readthread = threading.Thread(target=read, daemon=True)
readthread.start()
def write():
while True:
for c in list(os.read(master, 100)):
ftdi_com.uartwrite(c)
writethread = threading.Thread(target=write, daemon=True)
writethread.start()
return s_name
ftdi_map = {
"uart": 0,
"dma": 1
}
ftdi_com = FTDIComDevice(FTDI_INTERFACE_B,
mode="asynchronous",
uart_tag=ftdi_map["uart"],
dma_tag=ftdi_map["dma"],
verbose=False)
ftdi_com.open()
# test DMA
for i in range(256):
ftdi_com.dmawrite([i])
print("%02x" %(ftdi_com.dmaread()[0]), end="")
sys.stdout.flush()
print("")
# test UART
if platform.system() == "Windows":
uart_console(ftdi_com) # redirect uart to console since pty does not exist on Windows platforms
else:
s_name = uart_virtual(ftdi_com)
print(s_name)
while True:
time.sleep(1)

View file

@ -4,6 +4,7 @@
* synchronous FIFO mode. Requires libusb-1.0
*
* Copyright (C) 2009 Micah Elizabeth Scott
* Copyright (C) 2015 Florent Kermarrec
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@ -51,16 +52,14 @@ typedef struct {
} FTDIStreamState;
static int
DeviceInit(FTDIDevice *dev)
DeviceInit(FTDIDevice *dev, FTDIInterface interface)
{
int err, interface;
int err;
for (interface = 0; interface < 2; interface++) {
if (libusb_kernel_driver_active(dev->handle, interface) == 1) {
if ((err = libusb_detach_kernel_driver(dev->handle, interface))) {
perror("Error detaching kernel driver");
return err;
}
if (libusb_kernel_driver_active(dev->handle, (interface-1)) == 1) {
if ((err = libusb_detach_kernel_driver(dev->handle, (interface-1)))) {
perror("Error detaching kernel driver");
return err;
}
}
@ -69,11 +68,9 @@ DeviceInit(FTDIDevice *dev)
return err;
}
for (interface = 0; interface < 2; interface++) {
if ((err = libusb_claim_interface(dev->handle, interface))) {
perror("Error claiming interface");
return err;
}
if ((err = libusb_claim_interface(dev->handle, (interface-1)))) {
perror("Error claiming interface");
return err;
}
return 0;
@ -81,7 +78,7 @@ DeviceInit(FTDIDevice *dev)
int
FTDIDevice_Open(FTDIDevice *dev)
FTDIDevice_Open(FTDIDevice *dev, FTDIInterface interface)
{
int err;
@ -93,9 +90,6 @@ FTDIDevice_Open(FTDIDevice *dev)
libusb_set_debug(dev->libusb, 0);
dev->handle = libusb_open_device_with_vid_pid(dev->libusb,
LITEUSB_VENDOR,
LITEUSB_PRODUCT);
if (!dev->handle) {
dev->handle = libusb_open_device_with_vid_pid(dev->libusb,
@ -107,7 +101,7 @@ FTDIDevice_Open(FTDIDevice *dev)
return LIBUSB_ERROR_NO_DEVICE;
}
return DeviceInit(dev);
return DeviceInit(dev, interface);
}
@ -120,7 +114,7 @@ FTDIDevice_Close(FTDIDevice *dev)
int
FTDIDevice_Reset(FTDIDevice *dev)
FTDIDevice_Reset(FTDIDevice *dev, FTDIInterface interface)
{
int err;
@ -128,7 +122,7 @@ FTDIDevice_Reset(FTDIDevice *dev)
if (err)
return err;
return DeviceInit(dev);
return DeviceInit(dev, interface);
}

View file

@ -4,6 +4,7 @@
* for synchronous FIFO mode.
*
* Copyright (C) 2009 Micah Elizabeth Scott
* Copyright (C) 2015 Florent Kermarrec
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@ -79,9 +80,6 @@ typedef struct {
#define FTDI_VENDOR 0x0403
#define FTDI_PRODUCT_FT2232H 0x6010
#define LITEUSB_VENDOR 0x1d50
#define LITEUSB_PRODUCT 0x607c
#define FTDI_COMMAND_TIMEOUT 1000
#define FTDI_SET_BAUD_REQUEST 0x03
@ -102,9 +100,9 @@ typedef int (FTDIStreamCallback)(uint8_t *buffer, int length,
* Public Functions
*/
int FTDIDevice_Open(FTDIDevice *dev);
int FTDIDevice_Open(FTDIDevice *dev, FTDIInterface interface);
void FTDIDevice_Close(FTDIDevice *dev);
int FTDIDevice_Reset(FTDIDevice *dev);
int FTDIDevice_Reset(FTDIDevice *dev, FTDIInterface interface);
int FTDIDevice_SetMode(FTDIDevice *dev, FTDIInterface interface,
FTDIBitmode mode, uint8_t pinDirections,

View file

@ -1,15 +0,0 @@
CC=gcc
CFLAGS =-Wall -O0
CFLAGS_DLL =-Wall -O0 -g -shared -Wl,--out-implib,libftdicom.a
INC=-I. -I../libusb
LIBS_PATHS= -L. -L../libusb
LIBS_DLL= -lusb-1.0 -lpthreadGC2
all: libftdicom.dll
libftdicom.dll: crc32.c fastftdi.c ftdicom.c
$(CC) $(INC) -o $@ $(CFLAGS_DLL) $^ $(LIBS_PATHS) $(LIBS_DLL)
clean:
rm libftdicom.a
rm libftdicom.dll

View file

@ -1,7 +0,0 @@
#ifndef __CRC_H
#define __CRC_H
unsigned short crc16(const unsigned char *buffer, int len);
unsigned int crc32(const unsigned char *buffer, unsigned int len);
#endif

View file

@ -1,81 +0,0 @@
/* crc32.c -- compute the CRC-32 of a data stream
* Copyright (C) 1995-1998 Mark Adler
* For conditions of distribution and use, see copyright notice in zlib.h
*/
#include <crc.h>
static const unsigned int crc_table[256] = {
0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L,
0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L,
0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L,
0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL,
0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L,
0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L,
0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L,
0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL,
0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L,
0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL,
0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L,
0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L,
0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L,
0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL,
0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL,
0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L,
0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL,
0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L,
0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L,
0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L,
0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL,
0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L,
0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L,
0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL,
0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L,
0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L,
0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L,
0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L,
0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L,
0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL,
0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL,
0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L,
0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L,
0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL,
0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL,
0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L,
0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL,
0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L,
0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL,
0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L,
0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL,
0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L,
0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L,
0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL,
0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L,
0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L,
0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L,
0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L,
0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L,
0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L,
0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL,
0x2d02ef8dL
};
#define DO1(buf) crc = crc_table[((int)crc ^ (*buf++)) & 0xff] ^ (crc >> 8);
#define DO2(buf) DO1(buf); DO1(buf);
#define DO4(buf) DO2(buf); DO2(buf);
#define DO8(buf) DO4(buf); DO4(buf);
unsigned int crc32(const unsigned char *buffer, unsigned int len)
{
unsigned int crc;
crc = 0;
crc = crc ^ 0xffffffffL;
while(len >= 8) {
DO8(buffer);
len -= 8;
}
if(len) do {
DO1(buffer);
} while(--len);
return crc ^ 0xffffffffL;
}

View file

@ -1,257 +0,0 @@
/*
* ftdicom.c - Low Level USB communication interface
*
* Provides UART and DMA low level communication
* functions for FT2232H in slave fifo mode.
*
* Copyright (C) 2014 florent@enjoy-digital.fr
*
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>
#include "ftdicom.h"
#include "crc.h"
/*
* Open / close functions
*/
int ftdicom_open(FTDICom *com)
{
int err = 0;
err = FTDIDevice_Open(com->dev);
if (err)
return err;
com->raw_tx_buf = malloc(RAW_BUFFER_SIZE);
com->raw_rx_buf = malloc(RAW_BUFFER_SIZE);
com->uart_rx_buf = malloc(UART_RINGBUFFER_SIZE_RX);
err = FTDIDevice_SetMode(com->dev, FTDI_INTERFACE_A, FTDI_BITMODE_SYNC_FIFO, 0xFF, 0);
if (err)
return err;
com->raw_rx_buf_length = 0;
pthread_t thread;
com->thread = &thread;
pthread_create(com->thread, NULL, ftdicom_read_thread, com);
return 0;
}
void ftdicom_close(FTDICom *com)
{
free(com->raw_tx_buf);
free(com->raw_rx_buf);
free(com->uart_rx_buf);
FTDIDevice_Close(com->dev);
free(com->thread);
}
/*
* Write (Tx) functions
*/
int ftdicom_write(FTDICom *com, uint8_t tag, uint8_t *data, size_t length, uint8_t with_crc)
{
unsigned int computed_crc;
com->raw_tx_buf[0] = 0x5A;
com->raw_tx_buf[1] = 0xA5;
com->raw_tx_buf[2] = 0x5A;
com->raw_tx_buf[3] = 0xA5;
com->raw_tx_buf[4] = tag;
if (with_crc)
length += 4;
com->raw_tx_buf[5] = (length >> 24) & 0xff;
com->raw_tx_buf[6] = (length >> 16) & 0xff;
com->raw_tx_buf[7] = (length >> 8) & 0xff;
com->raw_tx_buf[8] = (length >> 0) & 0xff;
memcpy(com->raw_tx_buf+9, data, length);
if (with_crc) {
computed_crc = crc32(data, length-4);
com->raw_tx_buf[9+length-1] = (computed_crc >> 24) & 0xff;
com->raw_tx_buf[9+length-2] = (computed_crc >> 16) & 0xff;
com->raw_tx_buf[9+length-3] = (computed_crc >> 8) & 0xff;
com->raw_tx_buf[9+length-4] = (computed_crc >> 0) & 0xff;
}
return FTDIDevice_Write(com->dev, FTDI_INTERFACE_A, com->raw_tx_buf, 9+length, false);
}
/*
* Read (Rx) common functions
*/
int ftdicom_present_bytes(uint8_t tag, uint8_t *buffer, int length)
{
if (length < NEEDED_FOR_SIZE)
return INCOMPLETE;
if (buffer[0] != 0x5A ||
buffer[1] != 0xA5 ||
buffer[2] != 0x5A ||
buffer[3] != 0xA5 ||
buffer[4] != tag)
return UNMATCHED;
int size = NEEDED_FOR_SIZE;
size += buffer[5] << 24;
size += buffer[6] << 16;
size += buffer[7] << 8;
size += buffer[8];
if (length < size)
return INCOMPLETE;
return size;
}
int ftdicom_uart_present_bytes(uint8_t *buffer, int length)
{
return ftdicom_present_bytes(UART_TAG, buffer, length);
}
int ftdicom_dma_present_bytes(uint8_t *buffer, int length)
{
return ftdicom_present_bytes(DMA_TAG, buffer, length);
}
int ftdicom_read_callback(uint8_t *buffer, int length, FTDIProgressInfo *progress, void *userdata)
{
FTDICom *com = (FTDICom *) userdata;
// Concatenate buffer & raw_rx_buf
memcpy(com->raw_rx_buf + com->raw_rx_buf_length, buffer, length);
com->raw_rx_buf_length += length;
int code = 0;
int incomplete = 0;
int i = 0;
// Search frames in raw_rx_buf
while (i != com->raw_rx_buf_length && !incomplete)
{
code = 0;
// UART
code = ftdicom_uart_present_bytes(com->raw_rx_buf + i, com->raw_rx_buf_length-i);
if (code == INCOMPLETE)
{
incomplete = 1;
break;
} else if (code)
{
ftdicom_uart_read_callback(com, com->raw_rx_buf + i + NEEDED_FOR_SIZE, code-NEEDED_FOR_SIZE);
i += code-1;
}
// DMA
code = ftdicom_dma_present_bytes(com->raw_rx_buf + i, com->raw_rx_buf_length-i);
if (code == INCOMPLETE)
{
incomplete = 1;
break;
} else if (code)
{
ftdicom_dma_read_callback(com, com->raw_rx_buf + i + NEEDED_FOR_SIZE, code-NEEDED_FOR_SIZE);
i += code;
}
// Nothing found, increment index
if (code == UNMATCHED)
i=i+1;
}
// Prepare raw_rx_buf for next callback
if (incomplete == 1)
{
com->raw_rx_buf_length = com->raw_rx_buf_length - i;
memcpy(com->raw_rx_buf, com->raw_rx_buf + i, com->raw_rx_buf_length);
} else {
com->raw_rx_buf_length = 0;
}
return 0;
}
void *ftdicom_read_thread(void *userdata)
{
FTDICom *com = (FTDICom *) userdata;
FTDIDevice_ReadStream(com->dev, FTDI_INTERFACE_A, ftdicom_read_callback, com, 8, 16);
return 0;
}
/*
* UART functions
*/
int ftdicom_uart_write_buffer(FTDICom *com, uint8_t *data, size_t length)
{
return ftdicom_write(com, UART_TAG, data, length, 0);
}
int ftdicom_uart_write(FTDICom *com, uint8_t c)
{
return ftdicom_write(com, UART_TAG, &c, 1, 0);
}
void ftdicom_uart_read_callback(FTDICom *com, uint8_t *buffer, int length)
{
while (length > 0) {
com->uart_rx_buf[com->uart_rx_produce] = buffer[0];
com->uart_rx_produce = (com->uart_rx_produce + 1) & UART_RINGBUFFER_MASK_RX;
length -=1;
buffer +=1;
}
}
uint8_t ftdicom_uart_read(FTDICom *com)
{
uint8_t c;
while(com->uart_rx_consume == com->uart_rx_produce);
c = com->uart_rx_buf[com->uart_rx_consume];
com->uart_rx_consume = (com->uart_rx_consume + 1) & UART_RINGBUFFER_MASK_RX;
return c;
}
int ftdicom_uart_read_nonblock(FTDICom *com)
{
return (com->uart_rx_consume != com->uart_rx_produce);
}
/*
* DMA functions
*/
int ftdicom_dma_write(FTDICom *com, uint8_t *data, size_t length)
{
return ftdicom_write(com, DMA_TAG, data, length, 1);
}
void ftdicom_dma_read_set_callback(FTDICom *com, dma_read_ext_callback_t callback, void *userdata)
{
com->dma_read_ext_callback = callback;
com->userdata = userdata;
}
int ftdicom_dma_read_callback(FTDICom *com, uint8_t *buffer, int length)
{
unsigned int received_crc;
unsigned int computed_crc;
received_crc = ((unsigned int)buffer[length-1] << 24)
|((unsigned int)buffer[length-2] << 16)
|((unsigned int)buffer[length-3] << 8)
|((unsigned int)buffer[length-4]);
computed_crc = crc32(buffer, length-4);
if(received_crc != computed_crc) return -1;
if (com->dma_read_ext_callback != NULL)
return com->dma_read_ext_callback(buffer, length-4, com->userdata);
else
return -1;
}

View file

@ -1,104 +0,0 @@
/*
* ftdicom.c - Low Level USB communication interface
*
* Provides UART and DMA low level communication
* functions for FT2232H in slave fifo mode.
*
* Copyright (C) 2014 florent@enjoy-digital.fr
*
*/
#ifndef __FTDICOM_H
#define __FTDICOM_H
#include <stdint.h>
#include <stdbool.h>
#include <pthread.h>
#include "fastftdi.h"
/*
* Protocol Constants
*/
#define UART_TAG 0
#define DMA_TAG 1
#define NEEDED_FOR_SIZE 9
#define PAYLOAD_OFFSET 10
#define INCOMPLETE -1
#define UNMATCHED 0
/*
* Buffer Constants
*
* Buffer sizes must be a power of 2 so that modulos can be computed
* with logical AND.
*/
// RAW
#define RAW_BUFFER_SIZE 20*1024*1024
// UART
#define UART_RINGBUFFER_SIZE_RX 4096
#define UART_RINGBUFFER_MASK_RX (UART_RINGBUFFER_SIZE_RX-1)
// DMA
#define DMA_BUFFER_SIZE_TX 20*1024*1024
#define DMA_BUFFER_SIZE_RX 20*1024*1024
/*
* Struct
*/
typedef int (*dma_read_ext_callback_t)(uint8_t *buffer, int length, void *userdata);
typedef struct {
FTDIDevice *dev;
uint8_t *raw_tx_buf;
uint8_t *raw_rx_buf;
unsigned int raw_rx_buf_length;
char *uart_rx_buf;
volatile unsigned int uart_rx_produce;
volatile unsigned int uart_rx_consume;
pthread_t *thread;
dma_read_ext_callback_t dma_read_ext_callback;
void *userdata;
} FTDICom;
/*
* Public Functions
*/
int ftdicom_open(FTDICom *com);
void ftdicom_close(FTDICom *com);
int ftdicom_uart_write_buffer(FTDICom *com, uint8_t *data, size_t length);
int ftdicom_uart_write(FTDICom *com, uint8_t c);
uint8_t ftdicom_uart_read(FTDICom *com);
int ftdicom_uart_read_nonblock(FTDICom *com);
int ftdicom_dma_write(FTDICom *com, uint8_t *data, size_t length);
void ftdicom_dma_read_set_callback(FTDICom *com, dma_read_ext_callback_t callback, void *userdata);
/*
* Private Functions
*/
int ftdicom_write(FTDICom *com, uint8_t tag, uint8_t *data, size_t length, uint8_t with_crc);
int ftdicom_present_bytes(uint8_t tag, uint8_t *buffer, int length);
int ftdicom_uart_present_bytes(uint8_t *buffer, int length);
int ftdicom_dma_present_bytes(uint8_t *buffer, int length);
int ftdicom_read_callback(uint8_t *buffer, int length, FTDIProgressInfo *progress, void *userdata);
void *ftdicom_read_thread(void *userdata);
void ftdicom_uart_read_callback(FTDICom *com, uint8_t *buffer, int length);
int ftdicom_dma_read_callback(FTDICom *com, uint8_t *buffer, int length);
#endif /* __FTDICOM_H */

View file

@ -0,0 +1,35 @@
UNAME := $(shell uname)
LIBNAME := libftdicom
# Load libusb via pkg-config
PACKAGES := libusb-1.0
CFLAGS += $(shell pkg-config --cflags $(PACKAGES))
LDFLAGS += $(shell pkg-config --libs $(PACKAGES))
# Large file support
CFLAGS += $(shell getconf LFS_CFLAGS)
CFLAGS += -fPIC
SO := $(LIBNAME).so
SO_LDFLAGS := $(LDFLAGS) -shared
# Local headers
CFLAGS += -I../include
SO_OBJS := ../fastftdi.o
CFLAGS += -O3 -g --std=c99
all: $(SO)
cp libftdicom.so ../libftdicom.so
$(SO): $(SO_OBJS)
cc -o $@ $^ $(SO_LDFLAGS)
*.o: *.h Makefile
clean:
rm -f $(SO) $(OBJS) $(SO_OBJS)

View file

@ -0,0 +1,12 @@
CC=gcc
CFLAGS_DLL =-Wall -O0 -g -shared -Wl,--subsystem,windows -DDLL
LIBS= -lusb-1.0
all: libftdicom.dll
cp libftdicom.dll ../libftdicom.dll
libftdicom.dll: ../fastftdi.c
$(CC) -o $@ $(CFLAGS_DLL) $^ $(LIBS)
clean:
rm -f libftdicom.dll ../libftdicom.dll