Skip to content

Commit

Permalink
Add Glasgow support (untested, gateware doesn't exist yet).
Browse files Browse the repository at this point in the history
  • Loading branch information
whitequark committed Sep 5, 2018
1 parent ac0d61d commit b0c63e4
Show file tree
Hide file tree
Showing 4 changed files with 378 additions and 0 deletions.
4 changes: 4 additions & 0 deletions CMakeLists.txt
Expand Up @@ -57,6 +57,7 @@ set(JTAGHAL_SOURCES
FTDIDriver.cpp
FTDIJtagInterface.cpp
FTDISWDInterface.cpp
GlasgowSWDInterface.cpp
NetworkedJtagInterface.cpp
PipeJtagInterface.cpp

Expand Down Expand Up @@ -106,6 +107,9 @@ add_library(jtaghal SHARED
${JTAGHAL_SOURCES})

target_link_libraries(jtaghal log xptools ${PROTOBUF_LIBRARIES})
if(USB_LIB)
target_link_libraries(jtaghal usb-1.0)
endif()
if(FTD2XX_LIB)
target_link_libraries(jtaghal ftd2xx)
endif()
Expand Down
293 changes: 293 additions & 0 deletions GlasgowSWDInterface.cpp
@@ -0,0 +1,293 @@
/***********************************************************************************************************************
* *
* ANTIKERNEL v0.1 *
* *
* Copyright (c) 2012-2018 Andrew D. Zonenberg *
* 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. *
* *
* * Neither the name of the author nor the names of any contributors may be used to endorse or promote products *
* derived from this software without specific prior written permission. *
* *
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS "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 AUTHORS BE HELD 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. *
* *
***********************************************************************************************************************/

/**
@file
@author whitequark
@brief Implementation of GlasgowSWDInterface
*/

#include "jtaghal.h"

#if HAVE_LIBUSB

#define VID_QIHW 0x20b7
#define PID_GLASGOW 0x9db1

using namespace std;

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Construction / destruction

/**
@brief Connects to a Glasgow SWD interface
The Glasgow SWD applet must be configured as the sole applet in the device and use exclusive access mode.
E.g. using `glasgow run swd`.
@throw SWDException if the connection could not be establishes or the serial number is invalid
@param serial Serial number of the device to connect to
*/
GlasgowSWDInterface::GlasgowSWDInterface(const string& serial)
{
int ret;

if((ret = libusb_init(&m_context)) != 0) {
throw JtagExceptionWrapper(
"libusb_init failed",
libusb_error_name(ret));
}

ssize_t device_count;
libusb_device **device_list;
if((device_count = libusb_get_device_list(m_context, &device_list)) < 0) {
throw JtagExceptionWrapper(
"libusb_get_device_list failed",
libusb_error_name(device_count));
}

for(int i = 0; i < device_count; i++) {
libusb_device *device = device_list[i];
libusb_device_descriptor device_desc;
if((ret = libusb_get_device_descriptor(device, &device_desc)) != 0) {
LogError("Cannot get USB device descriptor for device %03d:%03d\n",
libusb_get_bus_number(device),
libusb_get_port_number(device));
continue;
}

if(device_desc.idVendor != VID_QIHW || device_desc.idProduct != PID_GLASGOW)
continue;

libusb_device_handle *device_handle;
if((ret = libusb_open(device, &device_handle)) != 0) {
LogError("Cannot open Glasgow device %03d:%03d\n",
libusb_get_bus_number(device),
libusb_get_port_number(device));
continue;
}

char device_serial[64];
if((ret = libusb_get_string_descriptor_ascii(device_handle, device_desc.iSerialNumber,
(uint8_t *)device_serial, sizeof(device_serial))) != 0) {
LogError("Cannot get serial number for Glasgow device %03d:%03d\n",
libusb_get_bus_number(device),
libusb_get_port_number(device));
libusb_close(device_handle);
continue;
}

if(serial == "" || serial == device_serial) {
m_device = libusb_ref_device(device);
m_handle = device_handle;
m_serial = device_serial;
break;
}
}

libusb_free_device_list(device_list, /*unref_devices=*/true);

if(m_handle == NULL) {
if(serial.empty()) {
throw JtagExceptionWrapper(
"No Glasgow device found",
"");
} else {
throw JtagExceptionWrapper(
"No Glasgow device found with serial " + serial,
"");
}
}

libusb_config_descriptor *config_desc;
if((ret = libusb_get_active_config_descriptor(m_device, &config_desc)) != 0) {
throw JtagExceptionWrapper(
"libusb_get_config_descriptor failed",
libusb_error_name(ret));
}

if(config_desc->bNumInterfaces < 1 ||
config_desc->interface[0].num_altsetting < 1 ||
config_desc->interface[0].altsetting[0].bNumEndpoints != 2) {
libusb_free_config_descriptor(config_desc);
throw JtagExceptionWrapper(
"Malformed USB configuration descriptor: interfaces or endpoints missing",
"");
}

const libusb_interface_descriptor *interface_desc = &config_desc->interface[0].altsetting[0];
for(int i = 0; i < interface_desc->bNumEndpoints; i++) {
const libusb_endpoint_descriptor *endpoint_desc = &interface_desc->endpoint[i];
if(endpoint_desc->bEndpointAddress & LIBUSB_ENDPOINT_IN) {
m_ep_in = endpoint_desc->bEndpointAddress;
m_packet_size_in = endpoint_desc->wMaxPacketSize;
} else {
m_ep_out = endpoint_desc->bEndpointAddress;
m_packet_size_out = endpoint_desc->wMaxPacketSize;
}
}

libusb_free_config_descriptor(config_desc);
if(m_ep_in == 0 || m_ep_out == 0) {
throw JtagExceptionWrapper(
"Malformed USB configuration descriptor: unexpected endpoints",
"");
}
}

/**
@brief Interface destructor
Closes handles and disconnects from the adapter.
*/
GlasgowSWDInterface::~GlasgowSWDInterface()
{
if(m_handle != NULL)
libusb_close(m_handle);
if(m_context != NULL)
libusb_exit(m_context);
}

void GlasgowSWDInterface::WritePacket(vector<uint8_t> packet) {
int ret, transferred;
if((ret = libusb_bulk_transfer(m_handle, m_ep_out, packet.data(), packet.size(), &transferred, 0)) != 0 ||
(size_t)transferred != packet.size()) {
throw JtagExceptionWrapper(
"libusb_bulk_transfer OUT failed",
libusb_error_name(ret));
}
}

vector<uint8_t> GlasgowSWDInterface::ReadPacket() {
vector<uint8_t> packet;
packet.resize(m_packet_size_in);

int ret, transferred;
if((ret = libusb_bulk_transfer(m_handle, m_ep_in, packet.data(), packet.size(), &transferred, 0)) != 0) {
throw JtagExceptionWrapper(
"libusb_bulk_transfer IN failed",
libusb_error_name(ret));
}

packet.resize((size_t)transferred);
return packet;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Shim overrides to push SWDInterface functions into FTDIDriver

string GlasgowSWDInterface::GetName()
{
return "Glasgow";
}

string GlasgowSWDInterface::GetSerial()
{
return m_serial;
}

string GlasgowSWDInterface::GetUserID()
{
return "";
}

int GlasgowSWDInterface::GetFrequency()
{
return -1;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Low level

/**
@brief Resets the SWD link layer
*/
void GlasgowSWDInterface::ResetInterface()
{
vector<uint8_t> command = { 0xff };
WritePacket(command);

vector<uint8_t> reply = ReadPacket();
if(reply != vector<uint8_t> { 0xff }) {
throw JtagExceptionWrapper(
string("SWD reset returned unexpected result: ") + std::to_string(reply[0]),
"");
}
}

/**
@brief Performs a SW-DP write transaction
*/
void GlasgowSWDInterface::WriteWord(uint8_t reg_addr, bool ap, uint32_t wdata)
{
vector<uint8_t> command = {
(uint8_t)(0x80 | (ap << 3) | reg_addr),
(uint8_t)(wdata >> 0),
(uint8_t)(wdata >> 8),
(uint8_t)(wdata >> 16),
(uint8_t)(wdata >> 24),
};
WritePacket(command);

vector<uint8_t> reply = ReadPacket();
if(reply != vector<uint8_t> { 0x04 }) {
throw JtagExceptionWrapper(
string("SWD write returned unexpected result: ") + std::to_string(reply[0]),
"");
}
}

/**
@brief Performs a SW-DP read transaction
*/
uint32_t GlasgowSWDInterface::ReadWord(uint8_t reg_addr, bool ap)
{
vector<uint8_t> command = {
(uint8_t)(0x80 | (ap << 3) | (1 << 2) | reg_addr),
};
WritePacket(command);

vector<uint8_t> reply = ReadPacket();
if(reply.size() != 5 || reply[0] != 0x04) {
throw JtagExceptionWrapper(
string("SWD read returned unexpected result: ") + std::to_string(reply[0]),
"");
}

uint32_t word =
(reply[1] << 0) |
(reply[2] << 8) |
(reply[3] << 16) |
(reply[4] << 24);
return word;
}

#endif
80 changes: 80 additions & 0 deletions GlasgowSWDInterface.h
@@ -0,0 +1,80 @@
/***********************************************************************************************************************
* *
* ANTIKERNEL v0.1 *
* *
* Copyright (c) 2012-2018 Andrew D. Zonenberg *
* 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. *
* *
* * Neither the name of the author nor the names of any contributors may be used to endorse or promote products *
* derived from this software without specific prior written permission. *
* *
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS "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 AUTHORS BE HELD 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. *
* *
***********************************************************************************************************************/

/**
@file
@author whitequark
@brief Declaration of GlasgowSWDInterface
*/

#ifndef GlasgowSWDInterface_h
#define GlasgowSWDInterface_h

#if HAVE_LIBUSB

#include "SWDInterface.h"
#include <libusb-1.0/libusb.h>

/**
@brief A SWD adapter using the Glasgow debug tool, accessed through libusb
\ingroup interfaces
*/
class GlasgowSWDInterface : public SWDInterface
{
public:
GlasgowSWDInterface(const std::string& serial);
virtual ~GlasgowSWDInterface();

virtual void WriteWord(uint8_t reg_addr, bool ap, uint32_t wdata);
virtual uint32_t ReadWord(uint8_t reg_addr, bool ap);
virtual void ResetInterface();

virtual std::string GetName();
virtual std::string GetSerial();
virtual std::string GetUserID();
virtual int GetFrequency();

private:
std::string m_serial;
libusb_context *m_context = NULL;
libusb_device *m_device = NULL;
libusb_device_handle *m_handle = NULL;
uint8_t m_ep_in = 0;
uint8_t m_ep_out = 0;
uint16_t m_packet_size_in = 0;
uint16_t m_packet_size_out = 0;

void WritePacket(std::vector<uint8_t> packet);
std::vector<uint8_t> ReadPacket();
};

#endif

#endif

0 comments on commit b0c63e4

Please sign in to comment.