openocd/src/jtag/drivers/OpenULINK/src/protocol.c

241 lines
7.4 KiB
C

/***************************************************************************
* Copyright (C) 2011 by Martin Schmoelzer *
* <martin.schmoelzer@student.tuwien.ac.at> *
* *
* 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., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#include "protocol.h"
#include "jtag.h"
#include "delay.h"
#include "usb.h"
#include "io.h"
#include "msgtypes.h"
#include "reg_ezusb.h"
/**
* @file
* Implementation of the OpenULINK communication protocol.
*
* The OpenULINK protocol uses one OUT and one IN endpoint. These two endpoints
* are configured to use the maximum packet size for full-speed transfers,
* 64 bytes. Commands always start with a command ID (see msgtypes.h for
* command ID definitions) and contain zero or more payload data bytes in both
* transfer directions (IN and OUT). The payload
*
* Almost all commands contain a fixed number of payload data bytes. The number
* of payload data bytes for the IN and OUT direction does not need to be the
* same.
*
* Multiple commands may be sent in one EP2 Bulk-OUT packet. Because the
* OpenULINK firmware does not perform bounds checking for EP2 Bulk-IN packets,
* the host MUST ensure that the commands sent in the OUT packet require a
* maximum of 64 bytes of IN data.
*/
/** Index in EP2 Bulk-OUT data buffer that contains the current command ID */
volatile uint8_t cmd_id_index;
/** Number of data bytes already in EP2 Bulk-IN buffer */
volatile uint8_t payload_index_in;
/**
* Execute a SET_LEDS command.
*/
void execute_set_led_command(void)
{
uint8_t led_state = OUT2BUF[cmd_id_index + 1];
if (led_state & RUN_LED_ON)
SET_RUN_LED();
if (led_state & COM_LED_ON)
SET_COM_LED();
if (led_state & RUN_LED_OFF)
CLEAR_RUN_LED();
if (led_state & COM_LED_OFF)
CLEAR_COM_LED();
}
/**
* Executes one command and updates global command indexes.
*
* @return true if this command was the last command.
* @return false if there are more commands within the current contents of the
* Bulk EP2-OUT data buffer.
*/
bool execute_command(void)
{
uint8_t usb_out_bytecount, usb_in_bytecount;
uint16_t signal_state;
uint16_t count;
/* Most commands do not transfer IN data. To save code space, we write 0 to
* usb_in_bytecount here, then modify it in the switch statement below where
* neccessary */
usb_in_bytecount = 0;
switch (OUT2BUF[cmd_id_index] /* Command ID */) {
case CMD_SCAN_IN:
usb_out_bytecount = 5;
usb_in_bytecount = OUT2BUF[cmd_id_index + 1];
jtag_scan_in(cmd_id_index + 1, payload_index_in);
break;
case CMD_SCAN_OUT:
usb_out_bytecount = OUT2BUF[cmd_id_index + 1] + 5;
jtag_scan_out(cmd_id_index + 1);
break;
case CMD_SCAN_IO:
usb_in_bytecount = OUT2BUF[cmd_id_index + 1];
usb_out_bytecount = usb_in_bytecount + 5;
jtag_scan_io(cmd_id_index + 1, payload_index_in);
break;
case CMD_CLOCK_TMS:
usb_out_bytecount = 2;
jtag_clock_tms(OUT2BUF[cmd_id_index + 1], OUT2BUF[cmd_id_index + 2]);
break;
case CMD_CLOCK_TCK:
usb_out_bytecount = 2;
count = (uint16_t)OUT2BUF[cmd_id_index + 1];
count |= ((uint16_t)OUT2BUF[cmd_id_index + 2]) << 8;
jtag_clock_tck(count);
break;
case CMD_SLOW_SCAN_IN:
usb_out_bytecount = 5;
usb_in_bytecount = OUT2BUF[cmd_id_index + 1];
jtag_slow_scan_in(cmd_id_index + 1, payload_index_in);
break;
case CMD_SLOW_SCAN_OUT:
usb_out_bytecount = OUT2BUF[cmd_id_index + 1] + 5;
jtag_slow_scan_out(cmd_id_index + 1);
break;
case CMD_SLOW_SCAN_IO:
usb_in_bytecount = OUT2BUF[cmd_id_index + 1];
usb_out_bytecount = usb_in_bytecount + 5;
jtag_slow_scan_io(cmd_id_index + 1, payload_index_in);
break;
case CMD_SLOW_CLOCK_TMS:
usb_out_bytecount = 2;
jtag_slow_clock_tms(OUT2BUF[cmd_id_index + 1], OUT2BUF[cmd_id_index + 2]);
break;
case CMD_SLOW_CLOCK_TCK:
usb_out_bytecount = 2;
count = (uint16_t)OUT2BUF[cmd_id_index + 1];
count |= ((uint16_t)OUT2BUF[cmd_id_index + 2]) << 8;
jtag_slow_clock_tck(count);
break;
case CMD_SLEEP_US:
usb_out_bytecount = 2;
count = (uint16_t)OUT2BUF[cmd_id_index + 1];
count |= ((uint16_t)OUT2BUF[cmd_id_index + 2]) << 8;
delay_us(count);
break;
case CMD_SLEEP_MS:
usb_out_bytecount = 2;
count = (uint16_t)OUT2BUF[cmd_id_index + 1];
count |= ((uint16_t)OUT2BUF[cmd_id_index + 2]) << 8;
delay_ms(count);
break;
case CMD_GET_SIGNALS:
usb_out_bytecount = 0;
usb_in_bytecount = 2;
signal_state = jtag_get_signals();
IN2BUF[payload_index_in] = (signal_state >> 8) & 0x00FF;
IN2BUF[payload_index_in + 1] = signal_state & 0x00FF;
break;
case CMD_SET_SIGNALS:
usb_out_bytecount = 2;
jtag_set_signals(OUT2BUF[cmd_id_index + 1], OUT2BUF[cmd_id_index + 2]);
break;
case CMD_CONFIGURE_TCK_FREQ:
usb_out_bytecount = 5;
jtag_configure_tck_delay(
OUT2BUF[cmd_id_index + 1], /* scan_in */
OUT2BUF[cmd_id_index + 2], /* scan_out */
OUT2BUF[cmd_id_index + 3], /* scan_io */
OUT2BUF[cmd_id_index + 4], /* clock_tck */
OUT2BUF[cmd_id_index + 5]); /* clock_tms */
break;
case CMD_SET_LEDS:
usb_out_bytecount = 1;
execute_set_led_command();
break;
case CMD_TEST:
usb_out_bytecount = 1;
/* Do nothing... This command is only used to test if the device is ready
* to accept new commands */
break;
default:
/* Should never be reached */
usb_out_bytecount = 0;
break;
}
/* Update EP2 Bulk-IN data byte count */
payload_index_in += usb_in_bytecount;
/* Determine if this was the last command */
if ((cmd_id_index + usb_out_bytecount + 1) >= OUT2BC)
return true;
else {
/* Not the last command, update cmd_id_index */
cmd_id_index += (usb_out_bytecount + 1);
return false;
}
}
/**
* Forever wait for commands and execute them as they arrive.
*/
void command_loop(void)
{
bool last_command;
while (1) {
cmd_id_index = 0;
payload_index_in = 0;
/* Wait until host sends EP2 Bulk-OUT packet */
while (!EP2_out)
;
EP2_out = 0;
/* Turn on COM LED to indicate command execution */
SET_COM_LED();
/* Execute the commands */
last_command = false;
while (last_command == false)
last_command = execute_command();
CLEAR_COM_LED();
/* Send back EP2 Bulk-IN packet if required */
if (payload_index_in > 0) {
IN2BC = payload_index_in;
while (!EP2_in)
;
EP2_in = 0;
}
/* Re-arm EP2-OUT after command execution */
OUT2BC = 0;
}
}