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