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