WARNING! This is a very big and serious change to internals
[openocd/libswd.git] / src / transport / swd_libswd.c
blob5e2a45255a7a7bb726109692c8af851ad92f6552
1 /*
2 * OpenOCD's SWD Transport Drivers for LibSWD, body file.
4 * Copyright (C) 2011-2012 Tomasz Boleslaw CEDRO
5 * cederom@tlen.pl, http://www.tomek.cedro.info
6 * All rights reserved.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions are met:
10 * 1. Redistributions of source code must retain the above copyright notice,
11 * this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright notice,
13 * this list of conditions and the following disclaimer in the documentation
14 * and/or other materials provided with the distribution.
15 * 3. Neither the name of the Tomasz Boleslaw CEDRO nor the names of its
16 * contributors may be used to endorse or promote products derived from this
17 * software without specific prior written permission.
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
24 * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
27 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
29 * OF THE POSSIBILITY OF SUCH DAMAGE.*
31 * Written by Tomasz Boleslaw CEDRO <cederom@tlen.pl>, 2011-2012;
35 /**
36 * \file transport_swd_libswd.c OpenOCD's SWD Transport Drivers for LibSWD,
37 * body file.
39 * This file implements SWD transport in OpenOCD using external LibSWD library.
40 * LibSWD makes it possible to generate and anlyze SWD bistream, which can be
41 * transported by any generic interface that provides "transfer" and "bitbang"
42 * functions (see ft2232 as example).
46 #ifdef HAVE_CONFIG_H
47 #include "config.h"
48 #endif
50 #include <interface/interface.h>
51 #include <transport/transport.h>
52 #include <transport/swd.h>
53 #include <transport/swd_libswd.h>
54 #include <target/arm.h>
55 #include <target/arm_adi_v5.h>
56 #include <helper/log.h>
58 /** OpenOCD as for now use global pointer to driver structure. */
59 extern struct jtag_interface *jtag_interface;
61 /******************************************************************************
62 * @{ oocd_transport_swd_libswd_arm_adi_v5
63 * SWD Transport definitions that use LibSWD for underlying bus operations.
66 int oocd_transport_swd_libswd_queue_idcode_read(struct adiv5_dap *dap, uint8_t *ack, uint32_t *data)
68 int retval, *pdata;
69 retval = libswd_dp_read_idcode(dap->ctx, LIBSWD_OPERATION_EXECUTE, &pdata);
70 if (retval < 0) {
71 LOG_ERROR("oocd_transport_swd_libswd_queue_idcode_read(*dap=@%p, ack=@%p, data=@%p) error (%s)", (void *)dap,
72 (void *)ack, (void *)data, libswd_error_string(retval));
73 return ERROR_FAIL;
75 if (pdata != NULL)
76 *data = (uint32_t)*pdata;
77 return ERROR_OK;
80 int oocd_transport_swd_libswd_queue_dp_read(struct adiv5_dap *dap, unsigned reg, uint32_t *data)
82 int retval, *pdata;
83 retval = libswd_dp_read((libswd_ctx_t *)dap->ctx, LIBSWD_OPERATION_EXECUTE, (char)reg, &pdata);
84 if (retval < 0) {
85 LOG_ERROR("oocd_transport_swd_libswd_queue_dp_read(dap=@%p, reg=0x%X, data=@%p) error (%s) ", (void *)dap, reg,
86 (void *)data, libswd_error_string(retval));
87 return ERROR_FAIL;
89 if (data != NULL)
90 *data = (uint32_t)*pdata;
91 return ERROR_OK;
94 int oocd_transport_swd_libswd_queue_dp_write(struct adiv5_dap *dap, unsigned reg, uint32_t data)
96 int retval;
97 retval = libswd_dp_write((libswd_ctx_t *)dap->ctx, LIBSWD_OPERATION_EXECUTE, (char)reg, (int *)&data);
98 if (retval < 0) {
99 LOG_ERROR("oocd_transport_swd_libswd_queue_dp_write(dap=@%p, reg=0x%X, data=0x%X) error (%s)", (void *)dap, reg,
100 data, libswd_error_string(retval));
101 return ERROR_FAIL;
103 return ERROR_OK;
106 int oocd_transport_swd_libswd_queue_ap_read(struct adiv5_dap *dap, unsigned reg, uint32_t *data)
108 int retval, *pdata;
109 retval = libswd_ap_read((libswd_ctx_t *)dap->ctx, LIBSWD_OPERATION_EXECUTE, (char)reg, &pdata);
110 if (retval < 0) {
111 LOG_ERROR("oocd_transport_swd_libswd_queue_ap_read(dap=@%p, reg=0x%X, data=@%p) error (%s)", (void *)dap, reg,
112 (void *)data, libswd_error_string(retval));
113 return ERROR_FAIL;
115 if (data != NULL)
116 *data = (uint32_t)*pdata;
117 return ERROR_OK;
120 int oocd_transport_swd_libswd_queue_ap_write(struct adiv5_dap *dap, unsigned reg, uint32_t data)
122 int retval;
123 retval = libswd_ap_write((libswd_ctx_t *)dap->ctx, LIBSWD_OPERATION_EXECUTE, (char) reg, (int *) &data);
124 if (retval < 0) {
125 LOG_ERROR("oocd_transport_swd_libswd_queue_ap_write(dap=@%p, reg=0x%X, data=0x%X) error (%s)", (void *)dap, reg,
126 data, libswd_error_string(retval));
127 return ERROR_FAIL;
129 return ERROR_OK;
132 int oocd_transport_swd_libswd_queue_ap_abort(struct adiv5_dap *dap, uint8_t *ack)
134 int retval;
135 int abort_flags = LIBSWD_DP_ABORT_ORUNERRCLR | LIBSWD_DP_ABORT_WDERRCLR | LIBSWD_DP_ABORT_STKERRCLR \
136 | LIBSWD_DP_ABORT_STKCMPCLR | LIBSWD_DP_ABORT_DAPABORT;
137 retval = libswd_dp_write((libswd_ctx_t *)dap->ctx, LIBSWD_OPERATION_ENQUEUE, LIBSWD_DP_ABORT_ADDR, &abort_flags);
138 if (retval < 0) {
139 LOG_ERROR("oocd_transport_swd_libswd_queue_ap_abort(dap=@%p, ack=@%p) error (%s)", (void *)dap, (void *)ack,
140 libswd_error_string(retval));
141 return ERROR_FAIL;
143 return ERROR_OK;
146 /** This function flushes all enqueued operations into a hardware interface.
147 * libswd_cmdq_flush() is called, then libswd_drv_transmit() which is using
148 * application specific drivers that are linked into target application binary.
149 * Because in SWD each operation is confirmed by Target with ACK answer
150 * we need to react on errors here. OpenOCD was constructed for use with JTAG
151 * and most functions use series of enqueue functions that are later flushed
152 * into a hardware interface with high level dap_run() / transport_run(), so
153 * this is the only sensible place to place error handling (otherwise code
154 * would need to be changed in lots of places). Caller function simply want
155 * to know if transfer succeeded, so we can perform handling such as retry
156 * on ACK=WAIT unless transfer fail with ACK={FAIL, UNKNOWN}.
159 int oocd_transport_swd_libswd_run(struct adiv5_dap *dap)
161 int retval;
162 libswd_ctx_t *libswdctx = (libswd_ctx_t *)dap->ctx;
163 retval = libswd_cmdq_flush(libswdctx, &libswdctx->cmdq, LIBSWD_OPERATION_EXECUTE);
164 if (retval < 0) {
165 LOG_ERROR("oocd_transport_swd_libswd_run(dap=@%p) error (%s)", (void *) dap, libswd_error_string(retval));
166 return ERROR_FAIL;
167 } else
168 return ERROR_OK;
173 * Select SWD transport on interface pointed by global *jtag_interface structure.
174 * Select is assumed to be called before transport init. It prepares everything,
175 * including context memory and command set for higher layers, but not hardware
176 * and does not interrogate target device (with IDCODE read that is done by
177 * transport init call). This function does not touch the hardware because
178 * hardware use signals that are not yet read from config file at this point!
180 int oocd_transport_swd_libswd_select(struct command_context *ctx)
182 int retval;
184 retval = oocd_transport_swd_libswd_register_commands(ctx);
185 if (retval != ERROR_OK) {
186 LOG_ERROR("Unable to register LibSWD commands for SWD Transport!");
187 return retval;
189 return ERROR_OK;
193 * Transport initialization routine is responsible for target initialization
194 * using previously selected transport.
195 * It talks to the hardware using functions selected by transport_select().
197 * \param *ctx is the openocd command_context.
198 * \return ERROR_OK on success, ERROR_FAIL otherwise.
200 int oocd_transport_swd_libswd_init(struct command_context *ctx)
202 LOG_DEBUG("entering function...");
203 int retval, *idcode;
205 struct target *target = get_current_target(ctx);
206 struct arm *arm = target_to_arm(target);
207 struct adiv5_dap *dap = arm->dap;
209 dap->ops = &oocd_dap_ops_swd_libswd;
211 /* Create LIBSWD_CTX if nesessary */
212 if (!dap->ctx) {
213 /** Transport was not yet initialized. */
214 dap->ctx = libswd_init();
215 if (dap->ctx == NULL) {
216 LOG_ERROR("Cannot initialize SWD context!");
217 return ERROR_FAIL;
219 LOG_INFO("New SWD context initialized at 0x%p", (void *)dap->ctx);
220 /* Now inherit the log level from OpenOCD settings. */
221 retval = libswd_log_level_inherit((libswd_ctx_t *)dap->ctx, debug_level);
222 if (retval < 0) {
223 LOG_ERROR("Unable to set log level: %s", libswd_error_string(retval));
224 return ERROR_FAIL;
226 } else
227 LOG_INFO("Working on existing transport context at 0x%p...", (void *)dap->ctx);
229 /** We enable automatic error handling on error */
230 libswd_ctx_t *libswdctx = (libswd_ctx_t *)dap->ctx;
231 libswdctx->config.autofixerrors = 0;
234 * Initialize driver and detect target working with selected transport.
235 * Because we can work on existing context there is no need to destroy it,
236 * as it can be used on next try.
238 retval = libswd_dap_detect((libswd_ctx_t *)dap->ctx, LIBSWD_OPERATION_EXECUTE, &idcode);
239 if (retval < 0) {
240 LOG_ERROR("libswd_dap_detect() error %d (%s)", retval, libswd_error_string(retval));
241 return retval;
244 LOG_INFO("SWD transport initialization complete. Found IDCODE=0x%08X.", *idcode);
245 return ERROR_OK;
249 * SWD Tranport based DAP Operations using LibSWD for underlying operations.
251 const struct dap_ops oocd_dap_ops_swd_libswd = {
252 .is_swd = true,
253 .select = oocd_transport_swd_libswd_select,
254 .init = oocd_transport_swd_libswd_init,
255 .queue_idcode_read = oocd_transport_swd_libswd_queue_idcode_read,
256 .queue_dp_read = oocd_transport_swd_libswd_queue_dp_read,
257 .queue_dp_write = oocd_transport_swd_libswd_queue_dp_write,
258 .queue_ap_read = oocd_transport_swd_libswd_queue_ap_read,
259 .queue_ap_write = oocd_transport_swd_libswd_queue_ap_write,
260 .queue_ap_abort = oocd_transport_swd_libswd_queue_ap_abort,
261 .run = oocd_transport_swd_libswd_run,
265 * Interface features adds SWD support using LibSWD as middleware.
267 oocd_feature_t oocd_transport_swd_libswd_arm_dap_feature = {
268 .name = OOCD_FEATURE_ARM_DAP,
269 .description = "ARM DAP SWD transport features based on LibSWD.",
270 .body = (void *)&oocd_dap_ops_swd_libswd,
271 .next = NULL
274 /** @} */
276 /******************************************************************************
277 * @{ oocd_transport_swd_libswd_drv
278 * Driver bridge between OpenOCD and LibSWD.
282 * Driver code to write 8-bit data (char type).
283 * MOSI (Master Output Slave Input) is a SWD Write Operation.
285 * \param *libswdctx swd context to work on.
286 * \param *cmd point to the actual command being sent.
287 * \param *data points to the char data.
288 * \bits tells how many bits to send (at most 8).
289 * \bits nLSBfirst tells the shift direction: 0 = LSB first, other MSB first.
290 * \return data count transferred, or negative LIBSWD_ERROR code on failure.
291 ar)*/
292 int libswd_drv_mosi_8(libswd_ctx_t *libswdctx, libswd_cmd_t *cmd, char *data, int bits, int nLSBfirst)
294 LOG_DEBUG("OpenOCD's libswd_drv_mosi_8(libswdctx=@%p, cmd=@%p, data=0x%02X, bits=%d, nLSBfirst=0x%02X)",
295 (void *)libswdctx, (void *)cmd, *data, bits, nLSBfirst);
296 if (data == NULL)
297 return LIBSWD_ERROR_NULLPOINTER;
298 if (bits < 0 && bits > 8)
299 return LIBSWD_ERROR_PARAM;
300 if (nLSBfirst != 0 && nLSBfirst != 1)
301 return LIBSWD_ERROR_PARAM;
303 static unsigned int i;
304 static signed int res;
305 static char misodata[8], mosidata[8];
307 /* Split output data into char array. */
308 for (i = 0; i < 8; i++)
309 mosidata[(nLSBfirst == LIBSWD_DIR_LSBFIRST) ? i : (bits - 1 - i)] = ((1 << i) & (*data)) ? 1 : 0;
310 /* Then send that array into interface hardware. */
311 res = jtag_interface->transfer(NULL, bits, mosidata, misodata, 0);
312 if (res < 0)
313 return LIBSWD_ERROR_DRIVER;
315 return res;
319 * Driver code to write 32-bit data (int type).
320 * MOSI (Master Output Slave Input) is a SWD Write Operation.
322 * \param *libswdctx swd context to work on.
323 * \param *cmd point to the actual command being sent.
324 * \param *data points to the char buffer array.
325 * \bits tells how many bits to send (at most 32).
326 * \bits nLSBfirst tells the shift direction: 0 = LSB first, other MSB first.
327 * \return data count transferred, or negative LIBSWD_ERROR code on failure.
329 int libswd_drv_mosi_32(libswd_ctx_t *libswdctx, libswd_cmd_t *cmd, int *data, int bits, int nLSBfirst)
331 LOG_DEBUG("OpenOCD's libswd_drv_mosi_32(libswdctx=@%p, cmd=@%p, data=0x%08X, bits=%d, nLSBfirst=0x%02X)",
332 (void *)libswdctx, (void *)cmd, *data, bits, nLSBfirst);
333 if (data == NULL)
334 return LIBSWD_ERROR_NULLPOINTER;
335 if (bits < 0 && bits > 8)
336 return LIBSWD_ERROR_PARAM;
337 if (nLSBfirst != 0 && nLSBfirst != 1)
338 return LIBSWD_ERROR_PARAM;
340 static unsigned int i;
341 static signed int res;
342 static char misodata[32], mosidata[32];
344 /* UrJTAG drivers shift data LSB-First. */
345 for (i = 0; i < 32; i++)
346 mosidata[(nLSBfirst == LIBSWD_DIR_LSBFIRST) ? i : (bits - 1 - i)] = ((1 << i) & (*data)) ? 1 : 0;
347 res = jtag_interface->transfer(NULL, bits, mosidata, misodata, 0);
348 if (res < 0)
349 return LIBSWD_ERROR_DRIVER;
350 return res;
354 * Use UrJTAG's driver to read 8-bit data (char type).
355 * MISO (Master Input Slave Output) is a SWD Read Operation.
357 * \param *libswdctx swd context to work on.
358 * \param *cmd point to the actual command being sent.
359 * \param *data points to the char buffer array.
360 * \bits tells how many bits to send (at most 8).
361 * \bits nLSBfirst tells the shift direction: 0 = LSB first, other MSB first.
362 * \return data count transferred, or negative LIBSWD_ERROR code on failure.
364 int libswd_drv_miso_8(libswd_ctx_t *libswdctx, libswd_cmd_t *cmd, char *data, int bits, int nLSBfirst)
366 if (data == NULL)
367 return LIBSWD_ERROR_NULLPOINTER;
368 if (bits < 0 && bits > 8)
369 return LIBSWD_ERROR_PARAM;
370 if (nLSBfirst != 0 && nLSBfirst != 1)
371 return LIBSWD_ERROR_PARAM;
373 static int i;
374 static signed int res;
375 static char misodata[8], mosidata[8];
377 res = jtag_interface->transfer(NULL, bits, mosidata, misodata, LIBSWD_DIR_LSBFIRST);
378 if (res < 0)
379 return LIBSWD_ERROR_DRIVER;
380 /* Now we need to reconstruct the data byte from shifted in LSBfirst byte array. */
381 *data = 0;
382 for (i = 0; i < bits; i++)
383 *data |= misodata[(nLSBfirst == LIBSWD_DIR_LSBFIRST) ? i : (bits - 1 - i)] ? (1 << i) : 0;
384 LOG_DEBUG("OpenOCD's libswd_drv_miso_8(libswdctx=@%p, cmd=@%p, data=@%p, bits=%d, nLSBfirst=0x%02X) reads: 0x%02X",
385 (void *)libswdctx, (void *)cmd, (void *)data, bits, nLSBfirst, *data);
386 return res;
390 * Driver code to read 32-bit data (int type).
391 * MISO (Master Input Slave Output) is a SWD Read Operation.
393 * \param *libswdctx swd context to work on.
394 * \param *cmd point to the actual command being sent.
395 * \param *data points to the char buffer array.
396 * \bits tells how many bits to send (at most 32).
397 * \bits nLSBfirst tells the shift direction: 0 = LSB first, other MSB first.
398 * \return data count transferred, or negative LIBSWD_ERROR code on failure.
400 int libswd_drv_miso_32(libswd_ctx_t *libswdctx, libswd_cmd_t *cmd, int *data, int bits, int nLSBfirst)
402 if (data == NULL)
403 return LIBSWD_ERROR_NULLPOINTER;
404 if (bits < 0 && bits > 8)
405 return LIBSWD_ERROR_PARAM;
406 if (nLSBfirst != 0 && nLSBfirst != 1)
407 return LIBSWD_ERROR_PARAM;
409 static int i;
410 static signed int res;
411 static char misodata[32], mosidata[32];
413 res = jtag_interface->transfer(NULL, bits, mosidata, misodata, LIBSWD_DIR_LSBFIRST);
414 if (res < 0)
415 return LIBSWD_ERROR_DRIVER;
416 /* Now we need to reconstruct the data byte from shifted in LSBfirst byte array. */
417 *data = 0;
418 for (i = 0; i < bits; i++)
419 *data |= (misodata[(nLSBfirst == LIBSWD_DIR_LSBFIRST) ? i : (bits - 1 - i)] ? (1 << i) : 0);
420 LOG_DEBUG("OpenOCD's libswd_drv_miso_32(libswdctx=@%p, cmd=@%p, data=@%p, bits=%d, nLSBfirst=0x%02X) reads: 0x%08X",
421 (void *)libswdctx, (void *)cmd, (void *)data, bits, nLSBfirst, *data);
422 LOG_DEBUG("OpenOCD's libswd_drv_miso_32() reads: 0x%08X\n", *data);
423 return res;
427 * This function sets interface buffers to MOSI direction.
428 * MOSI (Master Output Slave Input) is a SWD Write operation.
429 * OpenOCD use global "struct jtag_interface" pointer as interface driver.
430 * OpenOCD driver must support "RnW" signal to drive output buffers for TRN.
432 * \param *libswdctx is the swd context to work on.
433 * \param bits specify how many clock cycles must be used for TRN.
434 * \return number of bits transmitted or negative LIBSWD_ERROR code on failure.
436 int libswd_drv_mosi_trn(libswd_ctx_t *libswdctx, int bits)
438 LOG_DEBUG("OpenOCD's libswd_drv_mosi_trn(libswdctx=@%p, bits=%d)\n", (void *)libswdctx, bits);
439 if (bits < LIBSWD_TURNROUND_MIN_VAL && bits > LIBSWD_TURNROUND_MAX_VAL)
440 return LIBSWD_ERROR_TURNAROUND;
442 int res, val = 0;
443 static char buf[LIBSWD_TURNROUND_MAX_VAL];
444 /* Use driver method to set low (write) signal named RnW. */
445 res = jtag_interface->bitbang(NULL, "RnW", 0, &val);
446 if (res < 0)
447 return LIBSWD_ERROR_DRIVER;
449 /* Clock specified number of bits for proper TRN transaction. */
450 res = jtag_interface->transfer(NULL, bits, buf, buf, 0);
451 if (res < 0)
452 return LIBSWD_ERROR_DRIVER;
454 return bits;
458 * This function sets interface buffers to MISO direction.
459 * MISO (Master Input Slave Output) is a SWD Read operation.
460 * OpenOCD use global "struct jtag_interface" pointer as interface driver.
461 * OpenOCD driver must support "RnW" signal to drive output buffers for TRN.
463 * \param *libswdctx is the swd context to work on.
464 * \param bits specify how many clock cycles must be used for TRN.
465 * \return number of bits transmitted or negative LIBSWD_ERROR code on failure.
467 int libswd_drv_miso_trn(libswd_ctx_t *libswdctx, int bits)
469 LOG_DEBUG("OpenOCD's libswd_drv_miso_trn(libswdctx=@%p, bits=%d)\n", (void *)libswdctx, bits);
470 if (bits < LIBSWD_TURNROUND_MIN_VAL && bits > LIBSWD_TURNROUND_MAX_VAL)
471 return LIBSWD_ERROR_TURNAROUND;
473 static int res, val = 1;
474 static char buf[LIBSWD_TURNROUND_MAX_VAL];
476 /* Use driver method to set high (read) signal named RnW. */
477 res = jtag_interface->bitbang(NULL, "RnW", 0xFFFFFFFF, &val);
478 if (res < 0)
479 return LIBSWD_ERROR_DRIVER;
481 /* Clock specified number of bits for proper TRN transaction. */
482 res = jtag_interface->transfer(NULL, bits, buf, buf, 0);
483 if (res < 0)
484 return LIBSWD_ERROR_DRIVER;
486 return bits;
490 * Set SWD debug level according to OpenOCD settings.
492 * \param *libswdctx is the context to work on.
493 * \param loglevel is the OpenOCD numerical value of actual loglevel to force
494 * on LibSWD, or -1 to inherit from actual global settings of OpenOCD.
495 * \return LIBSWD_OK on success, negative LIBSWD_ERROR code on failure.
497 int libswd_log_level_inherit(libswd_ctx_t *libswdctx, int loglevel)
499 LOG_DEBUG("OpenOCD's libswd_log_level_inherit(libswdctx=@%p, loglevel=%d)\n", (void *)libswdctx, loglevel);
500 if (libswdctx == NULL) {
501 LOG_WARNING("libswd_log_level_inherit(): SWD Context not (yet) initialized...\n");
502 return LIBSWD_OK;
505 libswd_loglevel_t new_swdlevel;
506 switch ((loglevel == -1) ? debug_level : loglevel) {
507 case LOG_LVL_DEBUG:
508 new_swdlevel = LIBSWD_LOGLEVEL_PAYLOAD;
509 break;
510 case LOG_LVL_INFO:
511 new_swdlevel = LIBSWD_LOGLEVEL_INFO;
512 break;
513 case LOG_LVL_WARNING:
514 new_swdlevel = LIBSWD_LOGLEVEL_WARNING;
515 break;
516 case LOG_LVL_ERROR:
517 new_swdlevel = LIBSWD_LOGLEVEL_ERROR;
518 break;
519 case LOG_LVL_USER:
520 case LOG_LVL_OUTPUT:
521 new_swdlevel = LIBSWD_LOGLEVEL_NORMAL;
522 break;
523 case LOG_LVL_SILENT:
524 new_swdlevel = LIBSWD_LOGLEVEL_SILENT;
525 break;
526 default:
527 new_swdlevel = LIBSWD_LOGLEVEL_NORMAL;
530 int res = libswd_log_level_set(libswdctx, new_swdlevel);
531 if (res < 0) {
532 LOG_ERROR("libswd_log_level_set() failed (%s)\n", libswd_error_string(res));
533 return ERROR_FAIL;
535 return new_swdlevel;
538 /** We will use OpenOCD's logging mechanisms to show LibSWD messages.
539 * SWD can have different loglevel set than the OpenOCD itself, so we need to
540 * log all messages at OpenOCD level that will not block swd messages.
541 * It is also possible to 'inherit' loglevel to swd from openocd.
543 * \param *libswdctx is the pointer to the libswd context to work with.
544 * \param loglevel is the desired log level to show message at.
545 * \param *msg, ... is the printf like message to be logged.
546 * \return LIBSWD_OK on success, or error code otherwise.
548 int libswd_log(libswd_ctx_t *libswdctx, libswd_loglevel_t loglevel, char *msg, ...)
550 if (libswdctx == NULL)
551 return LIBSWD_ERROR_NULLCONTEXT;
552 if (loglevel > LIBSWD_LOGLEVEL_MAX)
553 return LIBSWD_ERROR_PARAM;
555 if (loglevel > libswdctx->config.loglevel)
556 return LIBSWD_OK;
557 va_list ap;
558 va_start(ap, msg);
559 /* Calling OpenOCD log functions here will cause program crash (va recurrent). */
560 vprintf(msg, ap);
561 va_end(ap);
562 return LIBSWD_OK;
565 /******************************************************************************
566 * @{ oocd_transport_swd_libswd_tcl
567 * TCL interface for LibSWD based SWD Transport in OpenOCD.
570 COMMAND_HANDLER(handle_oocd_transport_swd_libswd_loglevel)
572 int loglevel;
573 struct target *target = get_current_target(CMD_CTX);
574 struct arm *arm = target_to_arm(target);
575 libswd_ctx_t *swdctx = (libswd_ctx_t *)arm->dap->ctx;
577 switch (CMD_ARGC) {
578 case 0:
579 LOG_USER("Current SWD LogLevel[%d..%d] is: %d (%s)", LIBSWD_LOGLEVEL_MIN, LIBSWD_LOGLEVEL_MAX,
580 swdctx->config.loglevel, libswd_log_level_string(swdctx->config.loglevel));
581 break;
582 case 1:
583 /* We want to allow inherit current OpenOCD's debuglevel. */
584 if (strncasecmp(CMD_ARGV[0], "inherit", 7) == 0) {
585 loglevel = libswd_log_level_inherit(swdctx, debug_level);
586 if (loglevel < 0) {
587 LOG_ERROR("LogLevel inherit failed!");
588 return ERROR_FAIL;
589 } else {
590 LOG_USER("Using OpenOCD settings, SWD LogLevel[%d..%d] set to: %d (%s)", LIBSWD_LOGLEVEL_MIN,
591 LIBSWD_LOGLEVEL_MAX, loglevel, libswd_log_level_string(loglevel));
592 return ERROR_OK;
595 /* Or we want to set log level for SWD transport by hand. */
596 loglevel = atoi(CMD_ARGV[0]);
597 if (loglevel < LIBSWD_LOGLEVEL_MIN || loglevel > LIBSWD_LOGLEVEL_MAX) {
598 LOG_ERROR("Bad SWD LogLevel value!");
599 return ERROR_FAIL;
600 } else
601 LOG_USER("Setting SWD LogLevel[%d..%d] to: %d (%s)", LIBSWD_LOGLEVEL_MIN, LIBSWD_LOGLEVEL_MAX, loglevel,
602 libswd_log_level_string(loglevel));
603 if (libswd_log_level_set(swdctx, loglevel) < 0)
604 return ERROR_FAIL;
605 else
606 return ERROR_OK;
608 LOG_INFO("Available values:");
609 for (int i = 0; i <= LIBSWD_LOGLEVEL_MAX; i++)
610 LOG_INFO(" %d (%s)", i, libswd_log_level_string(i));
611 return ERROR_OK;
614 static const
615 struct command_registration oocd_transport_swd_libswd_subcommand_handlers[] = {
617 .name = "loglevel",
618 .handler = handle_oocd_transport_swd_libswd_loglevel,
619 .mode = COMMAND_ANY,
620 .help = "set/inherit/get loglevel for LibSWD-based SWD transport.",
623 COMMAND_REGISTRATION_DONE
626 static const
627 struct command_registration oocd_transport_swd_libswd_command_handlers[] = {
629 .name = "libswd",
630 .mode = COMMAND_ANY,
631 .help = "LibSWD-based SWD transport command group",
632 .chain = oocd_transport_swd_libswd_subcommand_handlers,
634 COMMAND_REGISTRATION_DONE
637 int oocd_transport_swd_libswd_register_commands(struct command_context *cmd_ctx)
639 return register_commands(cmd_ctx, NULL, oocd_transport_swd_libswd_command_handlers);
642 /** }@ */