From cdd33ebe7e1c66663486c4ed43ea23b769bc4307 Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Sun, 6 Jan 2019 21:43:23 +0100 Subject: [PATCH] WIP: TITAN TestPort for accessing USB devices from a USB host --- .gitignore | 4 + src/Makefile | 219 ++++++++++++++++++ src/USB_PT.cc | 495 +++++++++++++++++++++++++++++++++++++++++ src/USB_PT.hh | 129 +++++++++++ src/USB_PortType.ttcn | 22 ++ src/USB_PortTypes.ttcn | 125 +++++++++++ 6 files changed, 994 insertions(+) create mode 100644 .gitignore create mode 100644 src/Makefile create mode 100644 src/USB_PT.cc create mode 100644 src/USB_PT.hh create mode 100644 src/USB_PortType.ttcn create mode 100644 src/USB_PortTypes.ttcn diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..45140c0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +*.d +*.o +*.so +compile diff --git a/src/Makefile b/src/Makefile new file mode 100644 index 0000000..e8ebcce --- /dev/null +++ b/src/Makefile @@ -0,0 +1,219 @@ +# This Makefile was generated by the Makefile Generator +# of the TTCN-3 Test Executor version CRL 113 200/6 R5A +# for Harald Welte (laforge@nataraja) on Sun Jan 6 20:29:51 2019 + +# Copyright (c) 2000-2018 Ericsson Telecom AB + +# The following make commands are available: +# - make, make all Builds the executable test suite. +# - make archive Archives all source files. +# - make check Checks the semantics of TTCN-3 and ASN.1modules. +# - make port Generates port skeletons. +# - make clean Removes all generated files. +# - make compile Translates TTCN-3 and ASN.1 modules to C++. +# - make dep Creates/updates dependency list. +# - make executable Builds the executable test suite. +# - make library Builds the library archive. +# - make objects Builds the object files without linking the executable. +# - make shared_objects Builds the shared object files without linking the executable. +# WARNING! This Makefile can be used with GNU make only. +# Other versions of make may report syntax errors in it. + +# +# Do NOT touch this line... +# +.PHONY: all shared_objects executable library objects check port clean dep archive + +.SUFFIXES: .d + +# +# Set these variables... +# + +ifndef TTCN3_DIR +ifneq (,$(wildcard /usr/include/titan/)) +TTCN3_DIR = /usr +TTCN3_SUBDIR = /titan +else +TTCN3_DIR = +TTCN3_SUBDIR = +endif +endif + +# Your platform: (SOLARIS, SOLARIS8, LINUX, FREEBSD or WIN32) +PLATFORM = LINUX + +# Your C++ compiler: +# (if you change the platform, you may need to change the compiler) +CXX = g++ + +# Flags for the C++ preprocessor (and makedepend as well): +CPPFLAGS = -D$(PLATFORM) -I$(TTCN3_DIR)/include$(TTCN3_SUBDIR) $(shell pkg-config --cflags libusb-1.0) + +# Flags for dependency generation +CXXDEPFLAGS = -MM + +# Flags for the C++ compiler: +CXXFLAGS = -Wall -fPIC + +# Flags for the linker: +LDFLAGS = -fPIC $(shell pkg-config --libs libusb-1.0) + +ifeq ($(PLATFORM), WIN32) +# Silence linker warnings. +LDFLAGS += -Wl,--enable-auto-import,--enable-runtime-pseudo-reloc +endif + +# Utility to create library files +AR = ar +ARFLAGS = + +# Flags for the TTCN-3 and ASN.1 compiler: +COMPILER_FLAGS = -L + +# Execution mode: (either ttcn3 or ttcn3-parallel) +TTCN3_LIB = ttcn3-parallel-dynamic + +# The path of your libxml2 installation: +# If you do not have your own one, leave it unchanged. +XMLDIR = $(TTCN3_DIR) + +# Directory to store the archived source files: +ARCHIVE_DIR = backup + +# +# You may change these variables. Add your files if necessary... +# + +# TTCN-3 modules of this project: +TTCN3_MODULES = USB_PortType.ttcn USB_PortTypes.ttcn + +# ASN.1 modules of this project: +ASN1_MODULES = + +# C++ source & header files generated from the TTCN-3 & ASN.1 modules of +# this project: +GENERATED_SOURCES = $(TTCN3_MODULES:.ttcn=.cc) $(ASN1_MODULES:.asn=.cc) +GENERATED_HEADERS = $(GENERATED_SOURCES:.cc=.hh) + +# C/C++ Source & header files of Test Ports, external functions and +# other modules: +USER_SOURCES = USB_PT.cc +USER_HEADERS = $(USER_SOURCES:.cc=.hh) + +# Shared object files of this project: +SHARED_OBJECTS = $(GENERATED_SOURCES:.cc=.so) $(USER_SOURCES:.cc=.so) + +# Object files of this project that are needed for the executable test suite: +OBJECTS = $(GENERATED_OBJECTS) $(USER_OBJECTS) + +GENERATED_OBJECTS = $(GENERATED_SOURCES:.cc=.o) + +USER_OBJECTS = $(USER_SOURCES:.cc=.o) + +DEPFILES = $(USER_OBJECTS:.o=.d) $(GENERATED_OBJECTS:.o=.d) + +# Other files of the project (Makefile, configuration files, etc.) +# that will be added to the archived source files: +OTHER_FILES = Makefile + +# The name of the executable test suite: +EXECUTABLE = USB_PortType + + + +LIBRARY = lib$(EXECUTABLE).so + +TARGET = $(EXECUTABLE) + +# +# Do not modify these unless you know what you are doing... +# Platform specific additional libraries: +# +SOLARIS_LIBS = -lsocket -lnsl -lxml2 +SOLARIS8_LIBS = -lsocket -lnsl -lxml2 +LINUX_LIBS = -lxml2 +FREEBSD_LIBS = -lxml2 +WIN32_LIBS = -lxml2 + +# +# Rules for building the executable... +# + +all: $(TARGET) ; + +shared_objects: $(SHARED_OBJECTS) ; + +executable: $(EXECUTABLE) ; + +library: $(LIBRARY) ; + +objects: $(OBJECTS) compile; + +$(EXECUTABLE): $(SHARED_OBJECTS) + if $(CXX) $(LDFLAGS) -o $@ -Wl,--no-as-needed $^ \ + -L$(TTCN3_DIR)/lib$(TTCN3_SUBDIR) -l$(TTCN3_LIB) \ + -L$(OPENSSL_DIR)/lib -lcrypto \ + -L$(XMLDIR)/lib $($(PLATFORM)_LIBS); \ + then : ; else $(TTCN3_DIR)/bin/titanver $(OBJECTS); exit 1; fi + +$(LIBRARY): $(OBJECTS) + $(CXX) -shared -o $@ $(OBJECTS) + +.cc.o .c.o: + $(CXX) -c $(CPPFLAGS) $(CXXFLAGS) -o $@ $< + +.cc.d .c.d: + @echo Creating dependency file for '$<'; set -e; \ + $(CXX) $(CXXDEPFLAGS) $(CPPFLAGS) $(CXXFLAGS) $< \ + | sed 's/\($*\)\.o[ :]*/\1.o $@ : /g' > $@; \ + [ -s $@ ] || rm -f $@ + +%.so: %.o + $(CXX) -shared -o $@ $< + +$(GENERATED_SOURCES) $(GENERATED_HEADERS): compile + @if [ ! -f $@ ]; then $(RM) compile; $(MAKE) compile; fi + +check: $(TTCN3_MODULES) $(ASN1_MODULES) + $(TTCN3_DIR)/bin/compiler -s $(COMPILER_FLAGS) $^ + +port: $(TTCN3_MODULES) $(ASN1_MODULES) + $(TTCN3_DIR)/bin/compiler -t $(COMPILER_FLAGS) $^ + +compile: $(TTCN3_MODULES) $(ASN1_MODULES) + $(TTCN3_DIR)/bin/compiler $(COMPILER_FLAGS) $^ - $? + touch $@ + +clean: + -$(RM) $(EXECUTABLE) $(LIBRARY) $(OBJECTS) $(GENERATED_HEADERS) \ + $(GENERATED_SOURCES) $(SHARED_OBJECTS) compile $(DEPFILES) \ + tags *.log + +dep: $(GENERATED_SOURCES) $(USER_SOURCES) ; + +ifeq ($(findstring n,$(MAKEFLAGS)),) +ifeq ($(filter clean check port compile archive diag,$(MAKECMDGOALS)),) +-include $(DEPFILES) +endif +endif + +archive: + mkdir -p $(ARCHIVE_DIR) + tar -cvhf - $(TTCN3_MODULES) $(ASN1_MODULES) \ + $(USER_HEADERS) $(USER_SOURCES) $(OTHER_FILES) \ + | gzip >$(ARCHIVE_DIR)/`basename $(TARGET) .exe`-`date '+%y%m%d-%H%M'`.tgz + +diag: + $(TTCN3_DIR)/bin/compiler -v 2>&1 + $(TTCN3_DIR)/bin/mctr_cli -v 2>&1 + $(CXX) -v 2>&1 + @echo TTCN3_DIR=$(TTCN3_DIR) + @echo OPENSSL_DIR=$(OPENSSL_DIR) + @echo XMLDIR=$(XMLDIR) + @echo PLATFORM=$(PLATFORM) + +# +# Add your rules here if necessary... +# + diff --git a/src/USB_PT.cc b/src/USB_PT.cc new file mode 100644 index 0000000..3747a07 --- /dev/null +++ b/src/USB_PT.cc @@ -0,0 +1,495 @@ +/* Copyright (c) 2018 Harald Welte */ + +#include "USB_PT.hh" +#include "USB_PortType.hh" + +#include +#include + +using namespace USB__PortTypes; + +namespace USB__PortType { + +/*********************************************************************** + * libusb callbacks / helpers + ***********************************************************************/ + +static enum libusb_transfer_type ttype_titan2usb(const USB__transfer__type &in) +{ + switch (in) { + case USB__transfer__type::USB__TRANSFER__TYPE__CONTROL: + return LIBUSB_TRANSFER_TYPE_CONTROL; + case USB__transfer__type::USB__TRANSFER__TYPE__ISOCHRONOUS: + return LIBUSB_TRANSFER_TYPE_ISOCHRONOUS; + case USB__transfer__type::USB__TRANSFER__TYPE__BULK: + return LIBUSB_TRANSFER_TYPE_BULK; + case USB__transfer__type::USB__TRANSFER__TYPE__INTERRUPT: + return LIBUSB_TRANSFER_TYPE_INTERRUPT; + case USB__transfer__type::USB__TRANSFER__TYPE__BULK__STREAM: + return LIBUSB_TRANSFER_TYPE_BULK_STREAM; + default: + TTCN_error("Unknown USB transfer type %d", (int)in); + } +} + +static USB__transfer__type ttype_usb2titan(enum libusb_transfer_type in) +{ + switch (in) { + case LIBUSB_TRANSFER_TYPE_CONTROL: + return USB__transfer__type::USB__TRANSFER__TYPE__CONTROL; + case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS: + return USB__transfer__type::USB__TRANSFER__TYPE__ISOCHRONOUS; + case LIBUSB_TRANSFER_TYPE_BULK: + return USB__transfer__type::USB__TRANSFER__TYPE__BULK; + case LIBUSB_TRANSFER_TYPE_INTERRUPT: + return USB__transfer__type::USB__TRANSFER__TYPE__INTERRUPT; + case LIBUSB_TRANSFER_TYPE_BULK_STREAM: + return USB__transfer__type::USB__TRANSFER__TYPE__BULK__STREAM; + default: + TTCN_error("Unknown USB transfer type %d", (int)in); + } +} + +/* call-back by libusb telling us to monitor a given fd */ +void libusb_pollfd_added(int fd, short events, void *user_data) +{ + TTCN_warning("pollfd_added(%d, %d)", fd, events); + USB__PT_PROVIDER *pt = (USB__PT_PROVIDER *)user_data; + if (events & POLLIN) + pt->Handler_Add_Fd_Read(fd); + if (events & POLLOUT) + pt->Handler_Add_Fd_Write(fd); +} + +/* call-back by libusb telling us to stop monitoring a given fd */ +void libusb_pollfd_removed(int fd, void *user_data) +{ + TTCN_warning("pollfd_removed(%d)", fd); + USB__PT_PROVIDER *pt = (USB__PT_PROVIDER *)user_data; + pt->Handler_Remove_Fd(fd); +} + +void libusb_transfer_cb(struct libusb_transfer *xfer) +{ + USB_Transfer *t = (USB_Transfer *) xfer->user_data; + t->complete(); + delete t; +} + + +/*********************************************************************** + * USB_Interface + ***********************************************************************/ + +USB_Interface::USB_Interface(USB_Device &dev, unsigned int number) + : mDev(dev), mNum(number) +{ + mDev.log(TTCN_WARNING, "creating USB interface %u\n", mNum); +} + + +USB_Interface::~USB_Interface() +{ + mDev.log(TTCN_WARNING, "releasing USB interface %u\n", mNum); + int rc = libusb_release_interface(mDev.mHandle, mNum); + if (rc != 0) + mDev.log(TTCN_WARNING, "cannot release interface"); +} +/*********************************************************************** + * USB_Device + ***********************************************************************/ + +USB_Device::USB_Device(USB__PT_PROVIDER *port, libusb_device_handle *handle, unsigned int id) + : mPort(port), mHandle(handle), mID(id) +{ + log(TTCN_WARNING, "creating"); +} + +USB_Device::USB_Device(const USB_Device &in) +{ + this->mPort = in.mPort; + this->mHandle = in.mHandle; + this->mID = in.mID; + log(TTCN_WARNING, "copying from %p", &in); +} + +USB_Device::~USB_Device() +{ + log(TTCN_WARNING, "destroying"); + + /* iterate over map of interfaces: destroy the USB_Interface objects in it; clear map */ + std::map::iterator it; + for (it = mInterfaces.begin(); it != mInterfaces.end(); it++) { + delete it->second; + } + mInterfaces.clear(); + + libusb_close(mHandle); +} + +USB_Interface *USB_Device::interface(unsigned int number) +{ + std::map::iterator it = mInterfaces.find(number); + if (it == mInterfaces.end()) + TTCN_error("Cannot find USB interface %u", number); + return it->second; +} + +void USB_Device::log(TTCN_Logger::Severity msg_severity, const char *fmt, ...) +{ + TTCN_Logger::begin_event(msg_severity); + TTCN_Logger::log_event("USB Test port (%s): Device %p(%p,%d) ", mPort->get_name(), this, mHandle, mID); + va_list args; + va_start(args, fmt); + TTCN_Logger::log_event_va_list(fmt, args); + va_end(args); + TTCN_Logger::end_event(); +} + + + + +/*********************************************************************** + * USB_Transfer + ***********************************************************************/ + +USB_Transfer::USB_Transfer(const USB_Device *dev, unsigned int id, struct libusb_transfer *lx) +{ + init(dev, id, lx); +} + +USB_Transfer::USB_Transfer(USB__PT_PROVIDER *pt, const USB__transfer& send_par) +{ + USB_Device *dev = pt->usbdev_by_hdl(send_par.device__hdl()); + + struct libusb_transfer *xfer = libusb_alloc_transfer(0); + xfer->dev_handle = dev->mHandle; + xfer->flags = 0; + xfer->type = ttype_titan2usb(send_par.ttype()); + xfer->endpoint = send_par.endpoint(); + xfer->timeout = send_par.timeout__msec(); + xfer->user_data = this; + xfer->length = send_par.data().lengthof(); + xfer->buffer = libusb_dev_mem_alloc(xfer->dev_handle, xfer->length); + memcpy(xfer->buffer, send_par.data(), xfer->length); + xfer->callback = &libusb_transfer_cb; + init(dev, send_par.transfer__hdl(), xfer); +}; + +void USB_Transfer::init(const USB_Device *dev, unsigned int id, struct libusb_transfer *lx) +{ + mDev = dev; + mID = id; + mXfer = lx; + log("transfer created\n"); +} + +void USB_Transfer::submit() +{ + int rc; + + rc = libusb_submit_transfer(mXfer); + if (rc != 0) { + TTCN_error("Error during libusb_submit_transfer(): %s", + libusb_strerror((libusb_error) rc)); + } + log("transfer submitted\n"); +} + +void USB_Transfer::complete() +{ + log("transfer completed\n"); + /* report back to TTCN-3 port user */ + mDev->mPort->transfer_completed(this); +} + +void USB_Transfer::log(const char *fmt, ...) +{ + TTCN_Logger::begin_event(TTCN_WARNING); + TTCN_Logger::log_event("USB Test port (%s): T %p ", mDev->mPort->get_name(), this); + va_list args; + va_start(args, fmt); + TTCN_Logger::log_event_va_list(fmt, args); + va_end(args); + TTCN_Logger::end_event(); +} + + +USB_Transfer::~USB_Transfer() +{ + /* last, but not least: release the buffer used */ + if (mXfer->buffer) { + libusb_dev_mem_free(mDev->mHandle, mXfer->buffer, mXfer->length); + mXfer->buffer = NULL; + } + libusb_free_transfer(mXfer); + log("transfer destroyed\n"); +} + + +/*********************************************************************** + * USB__PT_PROVIDER + ***********************************************************************/ + +USB__PT_PROVIDER::USB__PT_PROVIDER(const char *par_port_name) + : PORT(par_port_name) +{ + libusb_init(&mCtx); + libusb_set_pollfd_notifiers(mCtx, libusb_pollfd_added, libusb_pollfd_removed, this); +} + +USB__PT_PROVIDER::~USB__PT_PROVIDER() +{ +} + +void USB__PT_PROVIDER::log(const char *fmt, ...) +{ + TTCN_Logger::begin_event(TTCN_WARNING); + TTCN_Logger::log_event("USB Test port (%s): ", get_name()); + va_list args; + va_start(args, fmt); + TTCN_Logger::log_event_va_list(fmt, args); + va_end(args); + TTCN_Logger::end_event(); +} + +void USB__PT_PROVIDER::set_parameter(const char *parameter_name, const char *parameter_value) +{ + if (0) { + /* handle known/supported parameters here */ + } else + TTCN_error("Unsupported test port parameter `%s'.", parameter_name); +} + +void USB__PT_PROVIDER::Handle_Fd_Event(int fd, boolean is_readable, + boolean is_writable, boolean is_error) +{ + /* we assume that we're running Linux v2.6.27 with timerfd support here + * and hence don't have to perform manual timeout handling. See + * "Notes on time-based events" at + * http://libusb.sourceforge.net/api-1.0/group__libusb__poll.html */ + struct timeval zero_tv = { 0, 0 }; + libusb_handle_events_timeout(mCtx, &zero_tv); +} + +/*void USB__PT_PROVIDER::Handle_Timeout(double time_since_last_call) {}*/ + +void USB__PT_PROVIDER::user_map(const char * /*system_port*/) +{ + +} + +void USB__PT_PROVIDER::user_unmap(const char * /*system_port*/) +{ + /* iterate over map of devices: destroy the USB_Device objects in it; clear map */ + std::map::iterator it; + for (it = mDevices.begin(); it != mDevices.end(); it++) { + delete it->second; + } + mDevices.clear(); +} + +void USB__PT_PROVIDER::user_start() +{ +} + +void USB__PT_PROVIDER::user_stop() +{ +} + +USB_Device *USB__PT_PROVIDER::usbdev_by_hdl(unsigned int hdl) +{ + std::map::iterator search = mDevices.find(hdl); + if (search == mDevices.end()) { + TTCN_error("Cannot find USB device for handle %u\n", hdl); + } + return search->second; +} + +unsigned int USB__PT_PROVIDER::usbhdl_by_dev(libusb_device_handle *dh) +{ + std::map::iterator it = mDevices.begin(); + while (it != mDevices.end()) { + if (it->second->mHandle == dh) + return it->first; + } + + TTCN_error("Cannot find USB handle for lubusb_dev %p\n", dh); +} + + +void USB__PT_PROVIDER::outgoing_send(const USB__open__vid__pid& send_par) +{ + unsigned int device_hdl = send_par.device__hdl(); + int rc; + + libusb_device_handle *dh; + dh = libusb_open_device_with_vid_pid(mCtx, send_par.vendor__id(), send_par.product__id()); + if (!dh) { + log("Error opening VID/PID %04x:%04x", send_par.vendor__id(), send_par.product__id()); + rc = -1; + } else { + USB_Device *dev = new USB_Device(this, dh, device_hdl); + mDevices.insert(std::make_pair(device_hdl, dev)); + rc = 0; + } + incoming_message(USB__result(send_par.req__hdl(), send_par.device__hdl(), rc)); +} + +void USB__PT_PROVIDER::outgoing_send(const USB__set__configuration& send_par) +{ + USB_Device *dev = usbdev_by_hdl(send_par.device__hdl()); + int rc; + + rc = libusb_set_configuration(dev->mHandle, send_par.configuration()); + if (rc != 0) + log("Cannot set configuration %d", (int)send_par.configuration()); + incoming_message(USB__result(send_par.req__hdl(), send_par.device__hdl(), rc)); +} + + +void USB__PT_PROVIDER::outgoing_send(const USB__claim__interface& send_par) +{ + USB_Device *dev = usbdev_by_hdl(send_par.device__hdl()); + unsigned int interface_nr = send_par.interface(); + int rc; + + rc = libusb_claim_interface(dev->mHandle, interface_nr); + if (rc != 0) + log("Cannot claim USB interface %d\n", interface_nr); + else { + USB_Interface *intf = new USB_Interface(*dev, interface_nr); + dev->mInterfaces.insert(std::make_pair(interface_nr, intf)); + } + incoming_message(USB__result(send_par.req__hdl(), send_par.device__hdl(), rc)); +} + +void USB__PT_PROVIDER::outgoing_send(const USB__release__interface& send_par) +{ + USB_Device *dev = usbdev_by_hdl(send_par.device__hdl()); + unsigned int interface_nr = send_par.interface(); + int rc; + + rc = libusb_release_interface(dev->mHandle, interface_nr); + if (rc != 0) + log("Cannot release USB interface %d\n", interface_nr); + else + dev->mInterfaces.erase(interface_nr); + incoming_message(USB__result(send_par.req__hdl(), send_par.device__hdl(), rc)); +} + +void USB__PT_PROVIDER::outgoing_send(const USB__get__device__descriptor& send_par) +{ + USB_Device *dev = usbdev_by_hdl(send_par.device__hdl()); + struct libusb_device_descriptor udesc; + struct USB__descriptor resp; + OCTETSTRING data; + int rc; + + struct libusb_device *d = libusb_get_device(dev->mHandle); + rc = libusb_get_device_descriptor(d, &udesc); + if (rc != 0) + data = OCTETSTRING(); + else + data = OCTETSTRING(sizeof(udesc), (const unsigned char *) &udesc); + incoming_message(USB__descriptor(send_par.req__hdl(), send_par.device__hdl(), rc, data)); +} + +void USB__PT_PROVIDER::outgoing_send(const USB__get__config__descriptor& send_par) +{ + USB_Device *dev = usbdev_by_hdl(send_par.device__hdl()); + struct libusb_config_descriptor *udesc; + struct USB__descriptor resp; + OCTETSTRING data; + int rc; + + struct libusb_device *d = libusb_get_device(dev->mHandle); + rc = libusb_get_config_descriptor(d, send_par.config__index(), &udesc); + if (rc != 0) + data = OCTETSTRING(); + else { + data = OCTETSTRING(udesc->wTotalLength, (const unsigned char *) udesc); + libusb_free_config_descriptor(udesc); + } + incoming_message(USB__descriptor(send_par.req__hdl(), send_par.device__hdl(), rc, data)); +} + +void USB__PT_PROVIDER::outgoing_send(const USB__get__config__descriptor__by__value& send_par) +{ + USB_Device *dev = usbdev_by_hdl(send_par.device__hdl()); + struct libusb_config_descriptor *udesc; + struct USB__descriptor resp; + OCTETSTRING data; + int rc; + + struct libusb_device *d = libusb_get_device(dev->mHandle); + rc = libusb_get_config_descriptor_by_value(d, send_par.config__value(), &udesc); + if (rc != 0) + data = OCTETSTRING(); + else { + data = OCTETSTRING(udesc->wTotalLength, (const unsigned char *) udesc); + libusb_free_config_descriptor(udesc); + } + incoming_message(USB__descriptor(send_par.req__hdl(), send_par.device__hdl(), rc, data)); +} + + +void USB__PT_PROVIDER::outgoing_send(const USB__get__active__config__descriptor& send_par) +{ + USB_Device *dev = usbdev_by_hdl(send_par.device__hdl()); + struct libusb_config_descriptor *udesc; + struct USB__descriptor resp; + OCTETSTRING data; + int rc; + + struct libusb_device *d = libusb_get_device(dev->mHandle); + rc = libusb_get_active_config_descriptor(d, &udesc); + if (rc != 0) + data = OCTETSTRING(); + else { + data = OCTETSTRING(udesc->wTotalLength, (const unsigned char *) udesc); + libusb_free_config_descriptor(udesc); + } + incoming_message(USB__descriptor(send_par.req__hdl(), send_par.device__hdl(), rc, data)); +} + +void USB__PT_PROVIDER::outgoing_send(const USB__get__string__descriptor& send_par) +{ + USB_Device *dev = usbdev_by_hdl(send_par.device__hdl()); + struct USB__descriptor resp; + unsigned char buf[256]; + OCTETSTRING data; + int rc; + + rc = libusb_get_string_descriptor(dev->mHandle, send_par.index(), send_par.language__id(), + buf, sizeof(buf)); + if (rc < 0) + data = OCTETSTRING(); + else + data = OCTETSTRING(rc, buf); + incoming_message(USB__descriptor(send_par.req__hdl(), send_par.device__hdl(), rc, data)); +} + + + + +void USB__PT_PROVIDER::outgoing_send(const USB__transfer& send_par) +{ + USB_Transfer *t = new USB_Transfer(this, send_par); + t->submit(); +} + + +void USB__PT_PROVIDER::transfer_completed(USB_Transfer *t) +{ + USB__transfer__compl xfc; + xfc.device__hdl() = t->mDev->mID; + xfc.ttype() = ttype_usb2titan((enum libusb_transfer_type) t->mXfer->type); + xfc.endpoint() = t->mXfer->endpoint; + xfc.data() = OCTETSTRING(t->mXfer->length, t->mXfer->buffer); + xfc.actual__length() = t->mXfer->actual_length; + incoming_message(xfc); +} + +} /* end of namespace */ + diff --git a/src/USB_PT.hh b/src/USB_PT.hh new file mode 100644 index 0000000..41d00c7 --- /dev/null +++ b/src/USB_PT.hh @@ -0,0 +1,129 @@ +// This Test Port skeleton header file was generated by the +// TTCN-3 Compiler of the TTCN-3 Test Executor version CRL 113 200/6 R5A +// for Harald Welte (laforge@nataraja) on Sun Jan 6 18:08:12 2019 + +// Copyright (c) 2000-2018 Ericsson Telecom AB + +// You may modify this file. Add your attributes and prototypes of your +// member functions here. + +#ifndef USB__PT_HH +#define USB__PT_HH + +#include +#include + +#include +#include +#include "USB_PortTypes.hh" + +// Note: Header file USB_PortType.hh must not be included into this file! +// (because it includes this file) +// Please add the declarations of message types manually. + +namespace USB__PortType { + +using namespace USB__PortTypes; + +class USB_Device; + +class USB_Interface { +public: + USB_Interface(USB_Device &dev, unsigned int number); + ~USB_Interface(); + USB_Device &mDev; + unsigned int mNum; +}; + +class USB__PT_PROVIDER; + +class USB_Device { +public: + USB_Device(USB__PT_PROVIDER *pt, libusb_device_handle *handle, unsigned int id); + USB_Device(const USB_Device &in); + ~USB_Device(); + USB_Interface *interface(unsigned int number); + void log(TTCN_Logger::Severity msg_severity, const char *fmt, ...); + + /* the TTCN-3 port through which this device was opened */ + USB__PT_PROVIDER *mPort; + /* the actaul libusb data structure */ + libusb_device_handle *mHandle; + /* ID the user gave the transfer, returned back to user on completion */ + unsigned int mID; + /* interfaces we have claimed for this device */ + std::map mInterfaces; +}; + +class USB_Transfer { +public: + USB_Transfer(const USB_Device *dev, unsigned int id, struct libusb_transfer *lx); + USB_Transfer(USB__PT_PROVIDER *pt, const USB__transfer& send_par); + ~USB_Transfer(); + void submit(); + void complete(); + void log(const char *fmt, ...); + + /* the USB device through which the transfer is sent */ + const USB_Device *mDev; + /* ID the user gave the transfer, returned back to user on completion */ + unsigned int mID; + /* the actaul libusb data structure */ + struct libusb_transfer *mXfer; +private: + void init(const USB_Device *dev, unsigned int id, struct libusb_transfer *lx); +}; + +class USB__PT_PROVIDER : public PORT { +public: + USB__PT_PROVIDER(const char *par_port_name); + ~USB__PT_PROVIDER(); + + void set_parameter(const char *parameter_name, + const char *parameter_value); + +private: + void Handle_Fd_Event(int fd, boolean is_readable, boolean is_writable, boolean is_error); + /* void Handle_Timeout(double time_since_last_call); */ + +protected: + void user_map(const char *system_port); + void user_unmap(const char *system_port); + + void user_start(); + void user_stop(); + + void outgoing_send(const USB__open__vid__pid& send_par); + void outgoing_send(const USB__transfer& send_par); + void outgoing_send(const USB__set__configuration& send_par); + void outgoing_send(const USB__claim__interface& send_par); + void outgoing_send(const USB__release__interface& send_par); + void outgoing_send(const USB__get__device__descriptor& send_par); + void outgoing_send(const USB__get__config__descriptor& send_par); + void outgoing_send(const USB__get__config__descriptor__by__value& send_par); + void outgoing_send(const USB__get__active__config__descriptor& send_par); + void outgoing_send(const USB__get__string__descriptor& send_par); + + virtual void incoming_message(const USB__transfer__compl& incoming_par) = 0; + virtual void incoming_message(const USB__result& incoming_par) = 0; + virtual void incoming_message(const USB__descriptor& incoming_par) = 0; + +public: + void log(const char *fmt, ...); + USB_Device *usbdev_by_hdl(unsigned int hdl); + void transfer_completed(USB_Transfer *t); +private: + libusb_context *mCtx; + std::map mDevices; + + unsigned int usbhdl_by_dev(libusb_device_handle *dh); + + /* to make them access protected Handler_{Add,Remove}_Fd methods */ + friend void libusb_pollfd_added(int fd, short events, void *user_data); + friend void libusb_pollfd_removed(int fd, void *user_data); + friend void libusb_transfer_cb(struct libusb_transfer *xfer); +}; + +} /* end of namespace */ + +#endif diff --git a/src/USB_PortType.ttcn b/src/USB_PortType.ttcn new file mode 100644 index 0000000..cf2856b --- /dev/null +++ b/src/USB_PortType.ttcn @@ -0,0 +1,22 @@ +module USB_PortType { +import from USB_PortTypes all; + + type port USB_PT message { + out USB_open_vid_pid; + out USB_set_configuration; + out USB_claim_interface; + out USB_release_interface; + in USB_result; + + out USB_get_device_descriptor; + out USB_get_config_descriptor; + out USB_get_active_config_descriptor; + out USB_get_config_descriptor_by_value; + out USB_get_string_descriptor; + in USB_descriptor; + + out USB_transfer; + in USB_transfer_compl; + } with { extension "provider" }; + +}; diff --git a/src/USB_PortTypes.ttcn b/src/USB_PortTypes.ttcn new file mode 100644 index 0000000..d6262a0 --- /dev/null +++ b/src/USB_PortTypes.ttcn @@ -0,0 +1,125 @@ +module USB_PortTypes { + + type enumerated USB_transfer_status { + USB_TRANSFER_COMPLETED, + USB_TRANSFER_ERROR, + USB_TRANSFER_TIMED_OUT, + USB_TRANSFER_CANCELLED, + USB_TRANSFER_STALL, + USB_TRANSFER_NO_DEVIC, + USB_TRANSFER_OVERFLOW + }; + + type integer USB_endpoint (0..255); + type integer USB_vendor_id (0..65535); + type integer USB_product_id (0..65535); + + type enumerated USB_transfer_type { + USB_TRANSFER_TYPE_CONTROL, + USB_TRANSFER_TYPE_ISOCHRONOUS, + USB_TRANSFER_TYPE_BULK, + USB_TRANSFER_TYPE_INTERRUPT, + USB_TRANSFER_TYPE_BULK_STREAM + }; + + /* submit a USB transfer to the specified device / endpoint */ + type record USB_transfer { + /* refers to a previously opened device */ + integer device_hdl, + /* opaque value passed back in call-back */ + integer transfer_hdl, + USB_endpoint endpoint, + USB_transfer_type ttype, + octetstring data, + integer timeout_msec + }; + /* Response: USB_transfer_compl */ + + type record USB_transfer_compl { + /* refers to a previously opened device */ + integer device_hdl, + /* opaque value passed back in call-back */ + integer transfer_hdl, + USB_endpoint endpoint, + USB_transfer_type ttype, + octetstring data, + integer actual_length, + USB_transfer_status status + }; + + type record USB_result { + integer req_hdl, + integer device_hdl, + integer result_code + }; + + type record USB_open_vid_pid { + integer req_hdl, + integer device_hdl, + USB_vendor_id vendor_id, + USB_product_id product_id + }; + /* Response: USB_result */ + + type record USB_set_configuration { + integer req_hdl, + integer device_hdl, + integer configuration + }; + /* Response: USB_result */ + + type record USB_claim_interface { + integer req_hdl, + integer device_hdl, + integer interface + }; + /* Response: USB_result */ + + type record USB_release_interface { + integer req_hdl, + integer device_hdl, + integer interface + }; + /* Response: USB_result */ + + type record USB_get_device_descriptor { + integer req_hdl, + integer device_hdl + }; + /* Response: USB_descriptor */ + + type record USB_get_config_descriptor { + integer req_hdl, + integer device_hdl, + integer config_index + }; + /* Response: USB_descriptor */ + + type record USB_get_active_config_descriptor { + integer req_hdl, + integer device_hdl + }; + /* Response: USB_descriptor */ + + type record USB_get_config_descriptor_by_value { + integer req_hdl, + integer device_hdl, + integer config_value + }; + /* Response: USB_descriptor */ + + type record USB_get_string_descriptor { + integer req_hdl, + integer device_hdl, + integer index, + integer language_id + }; + /* Response: USB_descriptor */ + + type record USB_descriptor { + integer req_hdl, + integer device_hdl, + integer result_code, + octetstring data + }; +};