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 */
execute_set_led_command(void)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 */
execute_command(void)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 * necessary */
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 */
command_loop(void)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 }
239