blob: 1616193596c390bb2837de24bf48400d71ae06fd [file] [log] [blame]
/* Copyright 2023 The ChromiumOS Authors
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "clock_chip.h"
#include "cmsis-dap.h"
#include "common.h"
#include "consumer.h"
#include "gpio.h"
#include "panic.h"
#include "producer.h"
#include "queue.h"
#include "queue_policies.h"
#include "registers.h"
#include "task.h"
#include "timer.h"
#include "usb-stream.h"
static const uint32_t DEFAULT_JTAG_CLOCK_HZ = 100000;
static const uint32_t OVERHEAD_CLOCK_CYCLES = 50;
/*
* The CMSIS-DAP specification calls for identifying the USB interface by
* looking for "CMSIS-DAP" in the string name, not by subclass/protocol.
*/
#define USB_SUBCLASS_CMSIS_DAP 0x00
#define USB_PROTOCOL_CMSIS_DAP 0x00
/* CMSIS-DAP command bytes */
enum cmsis_dap_command_t {
/* General commands */
DAP_Info = 0x00,
DAP_HostStatus = 0x01,
DAP_Connect = 0x02,
DAP_Disconnect = 0x03,
DAP_TransferConfigure = 0x04,
DAP_Transfer = 0x05,
DAP_TransferBlock = 0x06,
DAP_TransferAbort = 0x07,
DAP_WriteAbort = 0x08,
DAP_Delay = 0x09,
DAP_ResetTarget = 0x0A,
/* Commands used both for SWD and JTAG */
DAP_SWJ_Pins = 0x10,
DAP_SWJ_Clock = 0x11,
DAP_SWJ_Sequence = 0x12,
/* Commands used only with SWD */
DAP_SWD_Configure = 0x13,
/* Commands used only with JTAG */
DAP_JTAG_Sequence = 0x14,
DAP_JTAG_Configure = 0x15,
DAP_JTAG_IdCode = 0x16,
/* Commands used for UART tunnelling */
DAP_SWO_Transport = 0x17,
DAP_SWO_Mode = 0x18,
DAP_SWO_Baudrate = 0x19,
DAP_SWO_Control = 0x1A,
DAP_SWO_Status = 0x1B,
DAP_SWO_Data = 0x1C,
/* Commands used to group other commands */
DAP_QueueCommands = 0x7E,
DAP_ExecuteCommands = 0x7F,
/* Vendor-specific commands (reserved range 0x80 - 0x9F) */
DAP_GOOG_Info = 0x80,
DAP_GOOG_I2c = 0x81,
DAP_GOOG_I2cDevice = 0x82,
DAP_GOOG_Gpio = 0x83,
};
/* DAP Status Code */
enum cmsis_dap_status_t {
STATUS_Ok = 0x00,
STATUS_Error = 0xFF,
};
/* Parameter for info command */
enum cmsis_dap_info_subcommand_t {
INFO_Vendor = 0x01,
INFO_Product = 0x02,
INFO_Serial = 0x03,
INFO_Version = 0x04,
INFO_DeviceVendor = 0x05,
INFO_DeviceName = 0x06,
INFO_Capabilities = 0xF0,
INFO_SwoBufferSize = 0xFD,
INFO_PacketCount = 0xFE,
INFO_PacketSize = 0xFF,
};
/* Bitfield response to INFO_Capabilities */
const uint16_t CAP_Swd = BIT(0);
const uint16_t CAP_Jtag = BIT(1);
const uint16_t CAP_SwoUart = BIT(2);
const uint16_t CAP_SwoManchester = BIT(3);
const uint16_t CAP_AtomicCommands = BIT(4);
const uint16_t CAP_TestDomainTimer = BIT(5);
const uint16_t CAP_SwoStreamingTrace = BIT(6);
const uint16_t CAP_UartCommunicationPort = BIT(7);
const uint16_t CAP_UsbComPort = BIT(8);
enum connect_req_t {
CONN_REQ_Default = 0,
CONN_REQ_Swd = 1,
CONN_REQ_Jtag = 2,
};
enum connect_resp_t {
CONN_RESP_Failed = 0,
CONN_RESP_Swd = 1,
CONN_RESP_Jtag = 2,
};
/* Parameter for vendor (Google) info command */
enum goog_info_subcommand_t {
GOOG_INFO_Capabilities = 0x00,
};
/* Bitfield response to vendor (Google) capabities request */
const uint32_t GOOG_CAP_I2c = BIT(0);
const uint32_t GOOG_CAP_I2cDevice = BIT(1);
const uint32_t GOOG_CAP_GpioMonitoring = BIT(2);
const uint32_t GOOG_CAP_GpioBitbanging = BIT(3);
/* This bit indicates support for a particular UART USB control request */
const uint32_t GOOG_CAP_UartClearQueue = BIT(4);
/* This bit indicates support for SPI and I2C polling for TPM ready. */
const uint32_t GOOG_CAP_TpmPoll = BIT(5);
/* Bitfield used in DAP_SWJ_Pins request */
const uint8_t PIN_SwClk_Tck = 0x01;
const uint8_t PIN_SwDio_Tms = 0x02;
const uint8_t PIN_Tdi = 0x04;
const uint8_t PIN_Tdo = 0x08;
const uint8_t PIN_Trst = 0x20;
const uint8_t PIN_Reset = 0x80;
/* Bitfield used in DAP_JTAG_Sequence request */
const uint8_t SEQ_NumBits = 0x3F;
const uint8_t SEQ_Tms = 0x40;
const uint8_t SEQ_CaptureTdo = 0x80;
/*
* Incoming and outgoing byte streams.
*/
struct queue const cmsis_dap_tx_queue;
struct queue const cmsis_dap_rx_queue;
uint8_t rx_buffer[256];
uint8_t tx_buffer[256];
/*
* JTAG state
*/
enum jtag_signal_t {
JTAG_TCLK = 0,
JTAG_TMS,
JTAG_TDI,
JTAG_TDO,
JTAG_TRSTn,
JTAG_INVALID
};
static int jtag_pins[JTAG_INVALID] = {
GPIO_CN7_1, /* TCLK */
GPIO_CN7_7, /* TMS */
GPIO_CN7_3, /* TDI */
GPIO_CN7_5, /* TDO */
GPIO_CN7_16, /* TRSTn */
};
static int saved_pin_flags[JTAG_INVALID];
static bool jtag_enabled = false;
static uint16_t jtag_half_period_count;
void queue_blocking_add(struct queue const *q, const void *src, size_t count)
{
while (!cmsis_dap_unwind_requested()) {
size_t progress = queue_add_units(q, src, count);
src += progress;
if (progress >= count)
return;
count -= progress;
/*
* Wait for queue consumer to wake up this task, when there is
* more room in the queue.
*/
task_wait_event(0);
}
}
void queue_blocking_remove(struct queue const *q, void *dest, size_t count)
{
while (!cmsis_dap_unwind_requested()) {
size_t progress = queue_remove_units(q, dest, count);
dest += progress;
if (progress >= count)
return;
count -= progress;
/*
* Wait for queue producer to wake up this task, when there is
* more data in the queue.
*/
task_wait_event(0);
}
}
/*
* Implementation of handler routines for each CMSIS-DAP command.
*/
/* Info command, used to discover which other commands are supported. */
static void dap_info(size_t peek_c)
{
const char *CMSIS_DAP_VERSION_STR = "2.1.1";
const uint16_t CAPABILITIES = CAP_Jtag;
struct usb_string_desc *sd = usb_serialno_desc;
int i;
if (peek_c < 2)
return;
queue_remove_units(&cmsis_dap_rx_queue, rx_buffer, 2);
switch (rx_buffer[1]) {
case INFO_Serial:
for (i = 0; i < CONFIG_SERIALNO_LEN && sd->_data[i]; i++)
tx_buffer[2 + i] = sd->_data[i];
tx_buffer[1] = i;
queue_add_units(&cmsis_dap_tx_queue, tx_buffer, 2 + i);
break;
case INFO_Version:
tx_buffer[1] = strlen(CMSIS_DAP_VERSION_STR) + 1;
memcpy(tx_buffer + 2, CMSIS_DAP_VERSION_STR, tx_buffer[1]);
queue_add_units(&cmsis_dap_tx_queue, tx_buffer,
2 + tx_buffer[1]);
break;
case INFO_Capabilities:
tx_buffer[1] = sizeof(CAPABILITIES);
memcpy(tx_buffer + 2, &CAPABILITIES, sizeof(CAPABILITIES));
queue_add_units(&cmsis_dap_tx_queue, tx_buffer,
2 + tx_buffer[1]);
break;
default:
tx_buffer[1] = 0;
queue_add_units(&cmsis_dap_tx_queue, tx_buffer, 2);
break;
}
}
/* Informational command, to allow debugging device to indicate status. */
static void dap_host_status(size_t peek_c)
{
if (peek_c < 3)
return;
queue_remove_units(&cmsis_dap_rx_queue, rx_buffer, 3);
tx_buffer[1] = STATUS_Ok;
queue_add_units(&cmsis_dap_tx_queue, tx_buffer, 2);
}
/* Establish JTAG connection, take control of JTAG pins. */
static void dap_connect(size_t peek_c)
{
if (peek_c < 2)
return;
queue_remove_units(&cmsis_dap_rx_queue, rx_buffer, 2);
switch (rx_buffer[1]) {
case CONN_REQ_Default:
case CONN_REQ_Jtag:
tx_buffer[1] = CONN_RESP_Jtag;
if (!jtag_enabled) {
jtag_enabled = true;
for (size_t i = 0; i < JTAG_INVALID; i++) {
saved_pin_flags[i] =
gpio_get_flags(jtag_pins[i]);
}
gpio_set_flags(jtag_pins[JTAG_TMS], GPIO_OUT_LOW);
gpio_set_flags(jtag_pins[JTAG_TDI], GPIO_OUT_LOW);
gpio_set_flags(jtag_pins[JTAG_TCLK], GPIO_OUT_LOW);
gpio_set_flags(jtag_pins[JTAG_TRSTn],
GPIO_ODR_HIGH | GPIO_PULL_UP);
gpio_set_flags(jtag_pins[JTAG_TDO],
GPIO_INPUT | GPIO_PULL_UP);
}
break;
default:
tx_buffer[1] = CONN_RESP_Failed;
}
queue_add_units(&cmsis_dap_tx_queue, tx_buffer, 2);
}
/* Restore JTAG pins to previous configuration. */
static void dap_disconnect(size_t peek_c)
{
queue_remove_units(&cmsis_dap_rx_queue, rx_buffer, 1);
if (jtag_enabled) {
jtag_enabled = false;
for (size_t i = 0; i < JTAG_INVALID; i++) {
gpio_set_flags(jtag_pins[i], saved_pin_flags[i]);
}
}
tx_buffer[1] = STATUS_Ok;
queue_add_units(&cmsis_dap_tx_queue, tx_buffer, 2);
}
/* Configure parameters for DAP_Transfer family of requests. */
static void dap_transfer_configure(size_t peek_c)
{
if (peek_c < 6)
return;
queue_remove_units(&cmsis_dap_rx_queue, rx_buffer, 6);
/*
* This file does not offer support for the DAP_Transfer family of
* requests, and OpenOCD does not seem to issue any requests (at least
* not when operating on a RISC-V OpenTitan code.
*
* OpenOCD still sends this configuration request as part of its setup
* sequence, we can safely ignore the parameters given, and report
* success to the caller.
*/
tx_buffer[1] = STATUS_Ok;
queue_add_units(&cmsis_dap_tx_queue, tx_buffer, 2);
}
/* Reset the GSC (using same pin as if blue button was pressed). */
static void dap_reset_target(size_t peek_c)
{
queue_remove_units(&cmsis_dap_rx_queue, rx_buffer, 1);
if (shield_reset_pin != GPIO_COUNT) {
gpio_set_level(shield_reset_pin, false);
crec_usleep(100000);
gpio_set_level(shield_reset_pin, true);
tx_buffer[2] = 1;
} else {
tx_buffer[2] = 0;
}
tx_buffer[1] = STATUS_Ok;
queue_add_units(&cmsis_dap_tx_queue, tx_buffer, 3);
}
/* One-time setting of the output level of each JTAG signal. */
static void dap_swj_pins(size_t peek_c)
{
if (peek_c < 7)
return;
queue_remove_units(&cmsis_dap_rx_queue, rx_buffer, 7);
uint8_t pin_value = rx_buffer[1];
uint8_t pin_mask = rx_buffer[2];
uint32_t wait_us;
memcpy(&wait_us, rx_buffer + 3, sizeof(wait_us));
if ((pin_mask & PIN_SwClk_Tck))
gpio_set_level(jtag_pins[JTAG_TCLK],
!!(pin_value & PIN_SwClk_Tck));
if ((pin_mask & PIN_SwDio_Tms))
gpio_set_level(jtag_pins[JTAG_TMS],
!!(pin_value & PIN_SwDio_Tms));
if ((pin_mask & PIN_Tdi))
gpio_set_level(jtag_pins[JTAG_TDI], !!(pin_value & PIN_Tdi));
if ((pin_mask & PIN_Trst))
gpio_set_level(jtag_pins[JTAG_TRSTn], !!(pin_value & PIN_Trst));
if ((pin_mask & PIN_Reset) && shield_reset_pin != GPIO_COUNT)
gpio_set_level(shield_reset_pin, !!(pin_value & PIN_Reset));
crec_usleep(wait_us);
tx_buffer[1] = 0;
queue_add_units(&cmsis_dap_tx_queue, tx_buffer, 2);
}
/* Set JTAG clock frequency. */
static void dap_swj_clock(size_t peek_c)
{
uint32_t new_clock_hz, new_half_period_count;
if (peek_c < 5)
return;
queue_remove_units(&cmsis_dap_rx_queue, rx_buffer, 5);
memcpy(&new_clock_hz, rx_buffer + 1, sizeof(new_clock_hz));
if (!new_clock_hz) {
tx_buffer[1] = STATUS_Error;
} else {
new_half_period_count =
clock_get_timer_freq() / new_clock_hz / 2;
/*
* At this point, new_half_period_count contains the number of
* timer clock cycles for a half JTAG clock period. This will
* be used in a wait loop in the bit banging logic.
*
* Empirically, it has been stablished that at least 50 timer
* clock cycles are used by execution of GPIO manipulations
* involved in clock toggling and data shifting, so we subtract
* that from the number of cycles that will be "burned" in each
* clock phase while generating the waveform.
*/
if (new_half_period_count <= OVERHEAD_CLOCK_CYCLES) {
/*
* Requested speed as at or above the limit, run with no
* delay at all.
*/
new_half_period_count = 0;
} else {
new_half_period_count -= OVERHEAD_CLOCK_CYCLES;
}
if (new_half_period_count >= 0x8000) {
/*
* Requested clock is too slow. Out of range for a
* signed 16-bit countdown timer.
*/
tx_buffer[1] = STATUS_Error;
} else {
jtag_half_period_count = new_half_period_count;
tx_buffer[1] = STATUS_Ok;
}
}
queue_add_units(&cmsis_dap_tx_queue, tx_buffer, 2);
}
/* Busy-wait half a JTAG clock cycle. */
static inline __attribute__((always_inline)) void half_clock_delay(void)
{
/* Calculate the future timer value, that we want to wait for. */
uint16_t until = STM32_TIM_CNT(JTAG_TIMER) + jtag_half_period_count;
/*
* Busy-wait until counter is past the value (taking care around
* wrapping).
*/
while (((int16_t)(STM32_TIM_CNT(JTAG_TIMER) - until)) < 0)
;
}
/* Clock data out on TMS. */
static void dap_swj_sequence(size_t peek_c)
{
if (peek_c < 2)
return;
unsigned int bit_count = rx_buffer[1] == 0 ? 256 : rx_buffer[1];
unsigned c = queue_count(&cmsis_dap_rx_queue);
if (c < 2 + (bit_count + 7) / 8)
return;
queue_remove_units(&cmsis_dap_rx_queue, rx_buffer, c);
for (unsigned int i = 0; i < bit_count; i++) {
gpio_set_level(jtag_pins[JTAG_TMS],
!!(rx_buffer[2 + i / 8] & (1 << (i % 8))));
half_clock_delay();
gpio_set_level(jtag_pins[JTAG_TCLK], true);
half_clock_delay();
gpio_set_level(jtag_pins[JTAG_TCLK], false);
}
tx_buffer[1] = STATUS_Ok;
queue_add_units(&cmsis_dap_tx_queue, tx_buffer, 2);
}
/*
* Do a JTAG transaction, consisting of one or more sequences of clocking data
* on TDI (between 1 and 64 bits), while keeping TMS at a particular level.
*/
static void dap_jtag_sequence(size_t peek_c)
{
if (peek_c < 3)
return;
int c = queue_count(&cmsis_dap_rx_queue);
/* Check whether a complete request is in queue. */
queue_peek_units(&cmsis_dap_rx_queue, rx_buffer, 0, c);
size_t num_sequences = rx_buffer[1];
size_t offset = 2;
for (size_t i = 0; i < num_sequences; i++) {
uint8_t header = rx_buffer[offset];
unsigned int bit_count = header & 0x3F;
if (bit_count == 0)
bit_count = 0x40;
offset += 1 + (bit_count + 7) / 8;
if (offset > c) {
/* We do not yet have all bytes of the request. */
return;
}
}
/* We have a complete request, mark as removed from the queue. */
queue_advance_head(&cmsis_dap_rx_queue, offset);
/* Prepare output buffer for being populated one bit at a time. */
memset(tx_buffer + 1, 0, sizeof(tx_buffer) - 1);
/*
* As an optimization, resolve the IO port addresses and masks of
* frequently used GPIOs.
*/
volatile uint32_t *const jtag_clk_bsrr =
&STM32_GPIO_BSRR(gpio_list[jtag_pins[JTAG_TCLK]].port);
const uint32_t jtag_clk_mask_set = gpio_list[jtag_pins[JTAG_TCLK]].mask;
const uint32_t jtag_clk_mask_clear =
gpio_list[jtag_pins[JTAG_TCLK]].mask << 16;
volatile uint32_t *const jtag_tms_bsrr =
&STM32_GPIO_BSRR(gpio_list[jtag_pins[JTAG_TMS]].port);
const uint32_t jtag_tms_mask_set = gpio_list[jtag_pins[JTAG_TMS]].mask;
const uint32_t jtag_tms_mask_clear = gpio_list[jtag_pins[JTAG_TMS]].mask
<< 16;
volatile uint32_t *const jtag_tdi_bsrr =
&STM32_GPIO_BSRR(gpio_list[jtag_pins[JTAG_TDI]].port);
const uint32_t jtag_tdi_mask_set = gpio_list[jtag_pins[JTAG_TDI]].mask;
const uint32_t jtag_tdi_mask_clear = gpio_list[jtag_pins[JTAG_TDI]].mask
<< 16;
volatile uint16_t *const jtag_tdo_idr =
&STM32_GPIO_IDR(gpio_list[jtag_pins[JTAG_TDO]].port);
const uint16_t jtag_tdo_mask = gpio_list[jtag_pins[JTAG_TDO]].mask;
/* Clock should be low already, but make sure. */
*jtag_clk_bsrr = jtag_clk_mask_clear;
/*
* Iterate over the list of "sequences", each having a one-byte header
* specifying how many bits in the sequence, what the value of TMS
* during this sequence, and whether to record TDO during this sequence.
*/
const uint8_t *ptr = rx_buffer + 2;
uint8_t *tx_ptr = tx_buffer + 2;
const uint8_t *const end = rx_buffer + offset;
while (ptr < end) {
/* Consume and decode header byte for this one "sequence". */
uint8_t header = *ptr++;
*jtag_tms_bsrr = header & SEQ_Tms ? jtag_tms_mask_set :
jtag_tms_mask_clear;
bool capture_tdo = !!(header & SEQ_CaptureTdo);
unsigned int bit_count = (((header - 1) & SEQ_NumBits) + 1);
/*
* With TMS set at a given value, clock 1 - 64 bits of data on
* TDI/TDO.
*/
for (unsigned int i = 0; i < bit_count; i++) {
*jtag_tdi_bsrr = ptr[i / 8] & (1 << (i % 8)) ?
jtag_tdi_mask_set :
jtag_tdi_mask_clear;
half_clock_delay();
*jtag_clk_bsrr = jtag_clk_mask_set;
uint32_t tdo_val = !!(*jtag_tdo_idr & jtag_tdo_mask);
if (capture_tdo) {
tx_ptr[i / 8] |= tdo_val << (i % 8);
} else {
/*
* Spend time comparable to memory access above.
*
* Statement below clears all the bits in the
* current output byte which are "ahead" of
* where we would be placing the next sampled
* bits, that is, into the bit range that was
* already zero'ed by memset().
*/
tx_ptr[i / 8] &= ~(0xFF << (i % 8));
}
half_clock_delay();
*jtag_clk_bsrr = jtag_clk_mask_clear;
}
/* Consume the data bytes of this one "sequence". */
ptr += (bit_count + 7) / 8;
if (capture_tdo)
tx_ptr += (bit_count + 7) / 8;
}
tx_buffer[1] = STATUS_Ok;
queue_add_units(&cmsis_dap_tx_queue, tx_buffer, tx_ptr - tx_buffer);
}
/* Vendor command (HyperDebug): Discover Google-specific capabilities. */
static void dap_goog_info(size_t peek_c)
{
const uint16_t CAPABILITIES =
GOOG_CAP_I2c | GOOG_CAP_I2cDevice | GOOG_CAP_GpioMonitoring |
GOOG_CAP_GpioBitbanging | GOOG_CAP_UartClearQueue |
GOOG_CAP_TpmPoll;
if (peek_c < 2)
return;
queue_remove_units(&cmsis_dap_rx_queue, rx_buffer, 2);
switch (rx_buffer[1]) {
case GOOG_INFO_Capabilities:
tx_buffer[1] = sizeof(CAPABILITIES);
memcpy(tx_buffer + 2, &CAPABILITIES, sizeof(CAPABILITIES));
queue_add_units(&cmsis_dap_tx_queue, tx_buffer,
2 + tx_buffer[1]);
break;
}
}
/* Map from CMSIS-DAP command byte to handler routine. */
static void (*dispatch_table[256])(size_t peek_c) = {
[DAP_Info] = dap_info,
[DAP_GOOG_Info] = dap_goog_info,
[DAP_GOOG_I2c] = dap_goog_i2c,
[DAP_GOOG_I2cDevice] = dap_goog_i2c_device,
[DAP_GOOG_Gpio] = dap_goog_gpio,
[DAP_HostStatus] = dap_host_status,
[DAP_Connect] = dap_connect,
[DAP_Disconnect] = dap_disconnect,
[DAP_TransferConfigure] = dap_transfer_configure,
[DAP_ResetTarget] = dap_reset_target,
[DAP_SWJ_Pins] = dap_swj_pins,
[DAP_SWJ_Clock] = dap_swj_clock,
[DAP_SWJ_Sequence] = dap_swj_sequence,
[DAP_JTAG_Sequence] = dap_jtag_sequence,
};
/* Dispatch incoming request according to table above. */
static void cmsis_dap_dispatch(void)
{
/* Peek at the incoming data. */
size_t peek_c = queue_peek_units(&cmsis_dap_rx_queue, rx_buffer, 0, 8);
if (peek_c < 1) {
/* Not enough data to start decoding request. */
return;
}
if (dispatch_table[rx_buffer[0]]) {
/* First byte of response is always same as command byte. */
tx_buffer[0] = rx_buffer[0];
/* Invoke handler routine. */
dispatch_table[rx_buffer[0]](peek_c);
/* Trigger sending of response. */
queue_flush(&cmsis_dap_tx_queue);
} else {
/*
* Unrecognized command. The CMSIS-DAP protocol does not allow
* us to know the size of the data of a command in general, nor
* is there any command-independent means for sending "not
* understood". The code below discards all queued incoming
* data, and sends no reply. */
queue_advance_head(&cmsis_dap_rx_queue,
queue_count(&cmsis_dap_rx_queue));
}
}
/*
* If cmsis_dap_unwind_requested_by is any value other than TASK_ID_INVALID,
* it means that the given task (typically HOOKS or CONSOLE) has requested
* that the CMSIS_DAP task abort any partially received request or partially
* sent response, and return to its main loop ASAP. Before setting
* cmsis_dap_unwind_requested_by, the unwind_mutex must be locked by the
* requesting task must, and it must be held until the CMSIS_DAP task has
* acknowledged by writing TASK_ID_INVALID again.
*
* The CMSIS_DAP task only writes to cmsis_dap_unwind_requested_by if it sees
* a value other than TASK_ID_INVALID, and in that case, we know that no other
* task could be overwriting, due to the convention above.
*/
static volatile task_id_t cmsis_dap_unwind_requested_by = TASK_ID_INVALID;
static K_MUTEX_DEFINE(unwind_mutex);
bool cmsis_dap_unwind_requested(void)
{
return cmsis_dap_unwind_requested_by != TASK_ID_INVALID;
}
/*
* Main entry point for handling CMSIS-DAP requests received via USB.
*/
void cmsis_dap_task(void *unused)
{
/*
* Signal that the consumer is allowed to buffer characters
* indefinitely. `queue_flush()` will be invoked by
* `cmsis_dap_dispatch()` after processing each command, to ensure that
* the complete response is sent via USB.
*/
queue_enable_buffered_mode(&cmsis_dap_tx_queue);
while (true) {
/*
* If another task has requested unwinding, we can now report
* that the CMSIS task has returned to main loop.
*/
if (cmsis_dap_unwind_requested()) {
task_id_t requesting_task =
cmsis_dap_unwind_requested_by;
cmsis_dap_unwind_requested_by = TASK_ID_INVALID;
task_wake(requesting_task);
}
/* Wait for cmsis_dap_written() to wake up this task. */
task_wait_event(0);
if (cmsis_dap_unwind_requested())
continue;
/* Dispatch CMSIS request, if fully received. */
cmsis_dap_dispatch();
}
}
static int command_jtag_set_pins(int argc, const char **argv)
{
int new_pins[JTAG_INVALID];
if (argc < 7)
return EC_ERROR_PARAM_COUNT;
for (int i = 0; i < JTAG_INVALID; i++) {
new_pins[i] = gpio_find_by_name(argv[2 + i]);
if (new_pins[i] == GPIO_COUNT)
return EC_ERROR_PARAM2 + i;
/* Check if same pin listed twice. */
for (int j = 0; j < i; j++) {
if (new_pins[i] == new_pins[j]) {
ccprintf("Error: Pin %s listed twice\n",
gpio_list[jtag_pins[i]].name);
return EC_ERROR_PARAM2 + i;
}
}
}
/* No errors parsing command line, now apply the new settings. */
if (jtag_enabled) {
/*
* JTAG left enabled, disable current pins before proceeding.
* This will ensure that the next call to dap_connect() will
* result in the new set of pins being configured for
* input/output as appropriate for JTAG.
*/
jtag_enabled = false;
for (size_t i = 0; i < JTAG_INVALID; i++) {
gpio_set_flags(jtag_pins[i], saved_pin_flags[i]);
}
}
for (int i = 0; i < JTAG_INVALID; i++)
jtag_pins[i] = new_pins[i];
return EC_SUCCESS;
}
static int command_jtag(int argc, const char **argv)
{
if (argc < 2)
return EC_ERROR_PARAM_COUNT;
if (!strcasecmp(argv[1], "set-pins"))
return command_jtag_set_pins(argc, argv);
return 0;
}
DECLARE_CONSOLE_COMMAND_FLAGS(jtag, command_jtag, "",
"set-pins <TCLK> <TMS> <TDI> <TDO> <TRSTn>",
CMD_FLAG_RESTRICTED);
/*
* Declare USB interface for CMSIS-DAP.
*/
USB_STREAM_CONFIG_FULL(cmsis_dap_usb, USB_IFACE_CMSIS_DAP,
USB_CLASS_VENDOR_SPEC, USB_SUBCLASS_CMSIS_DAP,
USB_PROTOCOL_CMSIS_DAP, USB_STR_CMSIS_DAP_NAME,
USB_EP_CMSIS_DAP, USB_MAX_PACKET_SIZE,
USB_MAX_PACKET_SIZE, cmsis_dap_rx_queue,
cmsis_dap_tx_queue, 0, 1);
static void cmsis_dap_init(void)
{
jtag_half_period_count =
clock_get_timer_freq() / DEFAULT_JTAG_CLOCK_HZ / 2 -
OVERHEAD_CLOCK_CYCLES;
}
DECLARE_HOOK(HOOK_REINIT, cmsis_dap_init, HOOK_PRIO_DEFAULT);
static void cmsis_dap_reinit(void)
{
mutex_lock(&unwind_mutex);
/* Discard any partial data in the inbound queue. */
usb_stream_clear_rx(&cmsis_dap_usb);
/*
* Cause the CMSIS task to unwind any method blocked on receiving or
* sending more data. Then wait for the task to finish unwinding.
*/
cmsis_dap_unwind_requested_by = task_get_current();
task_wake(TASK_ID_CMSIS_DAP);
do {
if (TASK_EVENT_TIMER & task_wait_event(10000)) {
panic("CMSIS-DAP task is stuck");
}
} while (cmsis_dap_unwind_requested_by != TASK_ID_INVALID);
/* Discard any partial responses in the outgoing queue. */
usb_stream_clear_tx(&cmsis_dap_usb);
/*
* In case JTAG was enabled in dap_connect(), but not properly disabled
* with dap_disconnect(), the affected GPIO pins will be restored to
* default input setting by hook in `gpio.c`. In order for next
* dap_connect() to have proper effect, below we record the fact that
* JTAG connection has been disabled.
*/
jtag_enabled = false;
jtag_half_period_count =
clock_get_timer_freq() / DEFAULT_JTAG_CLOCK_HZ / 2 -
OVERHEAD_CLOCK_CYCLES;
mutex_unlock(&unwind_mutex);
}
/*
* Runs this hook before DEFAULT such as if the CMSIS_DAP task is blocked in any
* dap_xxx() methods in gpio.c or i2c.c, it will be unwound before the hook in
* these files are executed to reset their state.
*/
DECLARE_HOOK(HOOK_REINIT, cmsis_dap_reinit, HOOK_PRIO_PRE_DEFAULT);
static void cmsis_dap_written(struct consumer const *consumer, size_t count)
{
task_wake(TASK_ID_CMSIS_DAP);
}
struct consumer_ops const cmsis_dap_consumer_ops = {
.written = cmsis_dap_written,
};
struct consumer const cmsis_dap_consumer = {
.queue = &cmsis_dap_rx_queue,
.ops = &cmsis_dap_consumer_ops,
};
static void cmsis_dap_read(struct producer const *producer, size_t count)
{
task_wake(TASK_ID_CMSIS_DAP);
}
struct producer_ops const cmsis_dap_producer_ops = {
.read = cmsis_dap_read,
};
struct producer const cmsis_dap_producer = {
.queue = &cmsis_dap_tx_queue,
.ops = &cmsis_dap_producer_ops,
};
struct queue const cmsis_dap_tx_queue = QUEUE_DIRECT(
sizeof(tx_buffer), uint8_t, cmsis_dap_producer, cmsis_dap_usb.consumer);
struct queue const cmsis_dap_rx_queue = QUEUE_DIRECT(
sizeof(rx_buffer), uint8_t, cmsis_dap_usb.producer, cmsis_dap_consumer);