1034c04f0816be821b0519e8579b1e873f23eaa1
[fw/openocd] / src / jtag / drivers / OpenULINK / src / protocol.c
1 /***************************************************************************
2  *   Copyright (C) 2011 by Martin Schmoelzer                               *
3  *   <martin.schmoelzer@student.tuwien.ac.at>                              *
4  *                                                                         *
5  *   This program is free software; you can redistribute it and/or modify  *
6  *   it under the terms of the GNU General Public License as published by  *
7  *   the Free Software Foundation; either version 2 of the License, or     *
8  *   (at your option) any later version.                                   *
9  *                                                                         *
10  *   This program is distributed in the hope that it will be useful,       *
11  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
12  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
13  *   GNU General Public License for more details.                          *
14  *                                                                         *
15  *   You should have received a copy of the GNU General Public License     *
16  *   along with this program; if not, write to the                         *
17  *   Free Software Foundation, Inc.,                                       *
18  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
19  ***************************************************************************/
20
21 #include "protocol.h"
22 #include "jtag.h"
23 #include "delay.h"
24 #include "usb.h"
25 #include "io.h"
26 #include "msgtypes.h"
27
28 #include "reg_ezusb.h"
29
30 /**
31  * @file Implementation of the OpenULINK communication protocol.
32  *
33  * The OpenULINK protocol uses one OUT and one IN endpoint. These two endpoints
34  * are configured to use the maximum packet size for full-speed transfers,
35  * 64 bytes. Commands always start with a command ID (see msgtypes.h for
36  * command ID definitions) and contain zero or more payload data bytes in both
37  * transfer directions (IN and OUT). The payload
38  *
39  * Almost all commands contain a fixed number of payload data bytes. The number
40  * of payload data bytes for the IN and OUT direction does not need to be the
41  * same.
42  *
43  * Multiple commands may be sent in one EP2 Bulk-OUT packet. Because the
44  * OpenULINK firmware does not perform bounds checking for EP2 Bulk-IN packets,
45  * the host MUST ensure that the commands sent in the OUT packet require a
46  * maximum of 64 bytes of IN data.
47  */
48
49 /** Index in EP2 Bulk-OUT data buffer that contains the current command ID */
50 volatile uint8_t cmd_id_index;
51
52 /** Number of data bytes already in EP2 Bulk-IN buffer */
53 volatile uint8_t payload_index_in;
54
55 /**
56  * Execute a SET_LEDS command.
57  */
58 void execute_set_led_command(void)
59 {
60         uint8_t led_state = OUT2BUF[cmd_id_index + 1];
61
62         if (led_state & RUN_LED_ON)
63                 SET_RUN_LED();
64
65         if (led_state & COM_LED_ON)
66                 SET_COM_LED();
67
68         if (led_state & RUN_LED_OFF)
69                 CLEAR_RUN_LED();
70
71         if (led_state & COM_LED_OFF)
72                 CLEAR_COM_LED();
73 }
74
75 /**
76  * Executes one command and updates global command indexes.
77  *
78  * @param index the index of the Bulk EP2-OUT data buffer at which the
79  *  command ID is stored.
80  * @return true if this command was the last command.
81  * @return false if there are more commands within the current contents of the
82  *  Bulk EP2-OUT data buffer.
83  */
84 bool execute_command(void)
85 {
86         uint8_t usb_out_bytecount, usb_in_bytecount;
87         uint16_t signal_state;
88         uint16_t count;
89
90         /* Most commands do not transfer IN data. To save code space, we write 0 to
91          * usb_in_bytecount here, then modify it in the switch statement below where
92          * neccessary */
93         usb_in_bytecount = 0;
94
95         switch (OUT2BUF[cmd_id_index] /* Command ID */) {
96                 case CMD_SCAN_IN:
97                         usb_out_bytecount = 5;
98                         usb_in_bytecount = OUT2BUF[cmd_id_index + 1];
99                         jtag_scan_in(cmd_id_index + 1, payload_index_in);
100                         break;
101                 case CMD_SCAN_OUT:
102                         usb_out_bytecount = OUT2BUF[cmd_id_index + 1] + 5;
103                         jtag_scan_out(cmd_id_index + 1);
104                         break;
105                 case CMD_SCAN_IO:
106                         usb_in_bytecount = OUT2BUF[cmd_id_index + 1];
107                         usb_out_bytecount = usb_in_bytecount + 5;
108                         jtag_scan_io(cmd_id_index + 1, payload_index_in);
109                         break;
110                 case CMD_CLOCK_TMS:
111                         usb_out_bytecount = 2;
112                         jtag_clock_tms(OUT2BUF[cmd_id_index + 1], OUT2BUF[cmd_id_index + 2]);
113                         break;
114                 case CMD_CLOCK_TCK:
115                         usb_out_bytecount = 2;
116                         count = (uint16_t)OUT2BUF[cmd_id_index + 1];
117                         count |= ((uint16_t)OUT2BUF[cmd_id_index + 2]) << 8;
118                         jtag_clock_tck(count);
119                         break;
120                 case CMD_SLOW_SCAN_IN:
121                         usb_out_bytecount = 5;
122                         usb_in_bytecount = OUT2BUF[cmd_id_index + 1];
123                         jtag_slow_scan_in(cmd_id_index + 1, payload_index_in);
124                         break;
125                 case CMD_SLOW_SCAN_OUT:
126                         usb_out_bytecount = OUT2BUF[cmd_id_index + 1] + 5;
127                         jtag_slow_scan_out(cmd_id_index + 1);
128                         break;
129                 case CMD_SLOW_SCAN_IO:
130                         usb_in_bytecount = OUT2BUF[cmd_id_index + 1];
131                         usb_out_bytecount = usb_in_bytecount + 5;
132                         jtag_slow_scan_io(cmd_id_index + 1, payload_index_in);
133                         break;
134                 case CMD_SLOW_CLOCK_TMS:
135                         usb_out_bytecount = 2;
136                         jtag_slow_clock_tms(OUT2BUF[cmd_id_index + 1], OUT2BUF[cmd_id_index + 2]);
137                         break;
138                 case CMD_SLOW_CLOCK_TCK:
139                         usb_out_bytecount = 2;
140                         count = (uint16_t)OUT2BUF[cmd_id_index + 1];
141                         count |= ((uint16_t)OUT2BUF[cmd_id_index + 2]) << 8;
142                         jtag_slow_clock_tck(count);
143                         break;
144                 case CMD_SLEEP_US:
145                         usb_out_bytecount = 2;
146                         count = (uint16_t)OUT2BUF[cmd_id_index + 1];
147                         count |= ((uint16_t)OUT2BUF[cmd_id_index + 2]) << 8;
148                         delay_us(count);
149                         break;
150                 case CMD_SLEEP_MS:
151                         usb_out_bytecount = 2;
152                         count = (uint16_t)OUT2BUF[cmd_id_index + 1];
153                         count |= ((uint16_t)OUT2BUF[cmd_id_index + 2]) << 8;
154                         delay_ms(count);
155                         break;
156                 case CMD_GET_SIGNALS:
157                         usb_out_bytecount = 0;
158                         usb_in_bytecount = 2;
159                         signal_state = jtag_get_signals();
160                         IN2BUF[payload_index_in] = (signal_state >> 8) & 0x00FF;
161                         IN2BUF[payload_index_in + 1] = signal_state & 0x00FF;
162                         break;
163                 case CMD_SET_SIGNALS:
164                         usb_out_bytecount = 2;
165                         jtag_set_signals(OUT2BUF[cmd_id_index + 1], OUT2BUF[cmd_id_index + 2]);
166                         break;
167                 case CMD_CONFIGURE_TCK_FREQ:
168                         usb_out_bytecount = 5;
169                         jtag_configure_tck_delay(
170                         OUT2BUF[cmd_id_index + 1],      /* scan_in */
171                         OUT2BUF[cmd_id_index + 2],      /* scan_out */
172                         OUT2BUF[cmd_id_index + 3],      /* scan_io */
173                         OUT2BUF[cmd_id_index + 4],      /* clock_tck */
174                         OUT2BUF[cmd_id_index + 5]);     /* clock_tms */
175                         break;
176                 case CMD_SET_LEDS:
177                         usb_out_bytecount = 1;
178                         execute_set_led_command();
179                         break;
180                 case CMD_TEST:
181                         usb_out_bytecount = 1;
182                         /* Do nothing... This command is only used to test if the device is ready
183                          * to accept new commands */
184                         break;
185                 default:
186                         /* Should never be reached */
187                         usb_out_bytecount = 0;
188                         break;
189         }
190
191         /* Update EP2 Bulk-IN data byte count */
192         payload_index_in += usb_in_bytecount;
193
194         /* Determine if this was the last command */
195         if ((cmd_id_index + usb_out_bytecount + 1) >= OUT2BC)
196                 return true;
197         else {
198                 /* Not the last command, update cmd_id_index */
199                 cmd_id_index += (usb_out_bytecount + 1);
200                 return false;
201         }
202 }
203
204 /**
205  * Forever wait for commands and execute them as they arrive.
206  */
207 void command_loop(void)
208 {
209         bool last_command;
210
211         while (1) {
212                 cmd_id_index = 0;
213                 payload_index_in = 0;
214
215                 /* Wait until host sends EP2 Bulk-OUT packet */
216                 while (!EP2_out)
217                         ;
218                 EP2_out = 0;
219
220                 /* Turn on COM LED to indicate command execution */
221                 SET_COM_LED();
222
223                 /* Execute the commands */
224                 last_command = false;
225                 while (last_command == false)
226                         last_command = execute_command();
227
228                 CLEAR_COM_LED();
229
230                 /* Send back EP2 Bulk-IN packet if required */
231                 if (payload_index_in > 0) {
232                         IN2BC = payload_index_in;
233                         while (!EP2_in)
234                                 ;
235                         EP2_in = 0;
236                 }
237
238                 /* Re-arm EP2-OUT after command execution */
239                 OUT2BC = 0;
240         }
241 }