Determine the device parameters explicit after running
[fw/stlink] / src / stlink-sg.c
index 64f182022da3cfdece24bc3951537b557d83436d..991bc878b5bb77ccb82ec8fd28bcb6ee73ed4fb6 100644 (file)
@@ -12,7 +12,6 @@
  The stlink related constants kindly provided by Oliver Spencer (OpenOCD)
  for use in a GPL compatible license.
 
- Code format ~ TAB = 8, K&R, linux kernel source, golang oriented
  Tested compatibility: linux, gcc >= 4.3.3
 
  The communication is based on standard USB mass storage device
  CBW           - Command Block Wrapper
  CSW           - Command Status Wrapper
  RFU           - Reserved for Future Use
- scsi_pt       - SCSI pass-through
- sg            - SCSI generic
 
- * usb-storage.quirks
+ Originally, this driver used scsi pass through commands, which required the
+ usb-storage module to be loaded, providing the /dev/sgX links.  The USB mass
+ storage implementation on the STLinkv1 is however terribly broken, and it can 
+ take many minutes for the kernel to give up.
+ However, in Nov 2011, the scsi pass through was replaced by raw libusb, so 
+ instead of having to let usb-storage struggle with the device, and also greatly 
+ limiting the portability of the driver, you can now tell usb-storage to simply
+ ignore this device completely.
+   
+ usb-storage.quirks
  http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=blob_plain;f=Documentation/kernel-parameters.txt
  Each entry has the form VID:PID:Flags where VID and PID are Vendor and Product
  ID values (4-digit hex numbers) and Flags is a set of characters, each corresponding
 
  Example: quirks=0419:aaf5:rl,0421:0433:rc
  http://permalink.gmane.org/gmane.linux.usb.general/35053
+ For the stlinkv1, you just want the following
 
- modprobe -r usb-storage && modprobe usb-storage quirks=483:3744:l
+ modprobe -r usb-storage && modprobe usb-storage quirks=483:3744:i
 
  Equivalently, you can add a line saying
 
- options usb-storage quirks=483:3744:l
+ options usb-storage quirks=483:3744:i
 
  to your /etc/modprobe.conf or /etc/modprobe.d/local.conf (or add the "quirks=..."
  part to an existing options line for usb-storage).
-
- https://wiki.kubuntu.org/Kernel/Debugging/USB explains the protocoll and 
- would allow to replace the sg access to pure libusb access
  */
 
 
 #define WLOG(format, args...)         ugly_log(UWARN, LOG_TAG, format, ## args)
 #define fatal(format, args...)        ugly_log(UFATAL, LOG_TAG, format, ## args)
 
-// Suspends execution of the calling process for
-// (at least) ms milliseconds.
-
-static void delay(int ms) {
-    //fprintf(stderr, "*** wait %d ms\n", ms);
-    usleep(1000 * ms);
-}
-
 static void clear_cdb(struct stlink_libsg *sl) {
     for (size_t i = 0; i < sizeof (sl->cdb_cmd_blk); i++)
         sl->cdb_cmd_blk[i] = 0;
@@ -107,113 +105,54 @@ static void clear_cdb(struct stlink_libsg *sl) {
     sl->q_data_dir = Q_DATA_IN;
 }
 
-// E.g. make the valgrind happy.
-
-static void clear_buf(stlink_t *sl) {
-    DLOG("*** clear_buf ***\n");
-    for (size_t i = 0; i < sizeof (sl->q_buf); i++)
-        sl->q_buf[i] = 0;
-
-}
-
-// close the device, free the allocated memory
-
-void _stlink_sg_close(stlink_t *sl) {
+/**
+ * Close and free any _backend_ related information...
+ * @param sl
+ */void _stlink_sg_close(stlink_t *sl) {
     if (sl) {
-#if FINISHED_WITH_SG
         struct stlink_libsg *slsg = sl->backend_data;
-        scsi_pt_close_device(slsg->sg_fd);
+        libusb_close(slsg->usb_handle);
+        libusb_exit(slsg->libusb_ctx);
         free(slsg);
-#endif
     }
 }
 
-
-//TODO rewrite/cleanup, save the error in sl
-
-#if FINISHED_WITH_SG
-static void stlink_confirm_inq(stlink_t *stl, struct sg_pt_base *ptvp) {
-    struct stlink_libsg *sl = stl->backend_data;
-    const int e = sl->do_scsi_pt_err;
-    if (e < 0) {
-        fprintf(stderr, "scsi_pt error: pass through os error: %s\n",
-                safe_strerror(-e));
-        return;
-    } else if (e == SCSI_PT_DO_BAD_PARAMS) {
-        fprintf(stderr, "scsi_pt error: bad pass through setup\n");
-        return;
-    } else if (e == SCSI_PT_DO_TIMEOUT) {
-        fprintf(stderr, "  pass through timeout\n");
-        return;
+static int get_usb_mass_storage_status(libusb_device_handle *handle, uint8_t endpoint, uint32_t *tag)
+{
+    unsigned char csw[13];
+    memset(csw, 0, sizeof(csw));
+    int transferred;
+    int ret;
+    int try = 0;
+    do {
+        ret = libusb_bulk_transfer(handle, endpoint, (unsigned char *)&csw, sizeof(csw),
+                                   &transferred, SG_TIMEOUT_MSEC);
+        if (ret == LIBUSB_ERROR_PIPE) {
+            libusb_clear_halt(handle, endpoint);
+        }
+        try++;
+    } while ((ret == LIBUSB_ERROR_PIPE) && (try < 3));
+    if (ret != LIBUSB_SUCCESS) {
+        WLOG("%s: receiving failed: %d\n", __func__, ret);
+        return -1;
     }
-    const int duration = get_scsi_pt_duration_ms(ptvp);
-    if ((stl->verbose > 1) && (duration >= 0))
-        DLOG("      duration=%d ms\n", duration);
-
-    // XXX stlink fw sends broken residue, so ignore it and use the known q_len
-    // "usb-storage quirks=483:3744:r"
-    // forces residue to be ignored and calculated, but this causes aboard if
-    // data_len = 0 and by some other data_len values.
-
-    const int resid = get_scsi_pt_resid(ptvp);
-    const int dsize = stl->q_len - resid;
-
-    const int cat = get_scsi_pt_result_category(ptvp);
-    char buf[512];
-    unsigned int slen;
-
-    switch (cat) {
-        case SCSI_PT_RESULT_GOOD:
-            if (stl->verbose && (resid > 0))
-                DLOG("      notice: requested %d bytes but "
-                    "got %d bytes, ignore [broken] residue = %d\n",
-                    stl->q_len, dsize, resid);
-            break;
-        case SCSI_PT_RESULT_STATUS:
-            if (stl->verbose) {
-                sg_get_scsi_status_str(
-                        get_scsi_pt_status_response(ptvp), sizeof (buf),
-                        buf);
-                DLOG("  scsi status: %s\n", buf);
-            }
-            return;
-        case SCSI_PT_RESULT_SENSE:
-            slen = get_scsi_pt_sense_len(ptvp);
-            if (stl->verbose) {
-                sg_get_sense_str("", sl->sense_buf, slen, (stl->verbose
-                        > 1), sizeof (buf), buf);
-                DLOG("%s", buf);
-            }
-            if (stl->verbose && (resid > 0)) {
-                if ((stl->verbose) || (stl->q_len > 0))
-                    DLOG("    requested %d bytes but "
-                        "got %d bytes\n", stl->q_len, dsize);
-            }
-            return;
-        case SCSI_PT_RESULT_TRANSPORT_ERR:
-            if (stl->verbose) {
-                get_scsi_pt_transport_err_str(ptvp, sizeof (buf), buf);
-                // http://tldp.org/HOWTO/SCSI-Generic-HOWTO/x291.html
-                // These codes potentially come from the firmware on a host adapter
-                // or from one of several hosts that an adapter driver controls.
-                // The 'host_status' field has the following values:
-                //     [0x07] Internal error detected in the host adapter.
-                // This may not be fatal (and the command may have succeeded).
-                DLOG("  transport: %s", buf);
-            }
-            return;
-        case SCSI_PT_RESULT_OS_ERR:
-            if (stl->verbose) {
-                get_scsi_pt_os_err_str(ptvp, sizeof (buf), buf);
-                DLOG("  os: %s", buf);
-            }
-            return;
-        default:
-            fprintf(stderr, "  unknown pass through result "
-                    "category (%d)\n", cat);
+    if (transferred != sizeof(csw)) {
+        WLOG("%s: received unexpected amount: %d\n", __func__, transferred);
+        return -1;
+    }
+
+    uint32_t rsig = read_uint32(csw, 0);
+    uint32_t rtag = read_uint32(csw, 4);
+    /* uint32_t residue = read_uint32(csw, 8); */
+#define USB_CSW_SIGNATURE 0x53425355  // 'U' 'S' 'B' 'S' (reversed)
+    if (rsig != USB_CSW_SIGNATURE) {
+        WLOG("status signature was invalid: %#x\n", rsig);
+        return -1;
     }
+    *tag = rtag;
+    uint8_t rstatus = csw[12];
+    return rstatus;
 }
-#endif
 
 static int dump_CDB_command(uint8_t *cdb, uint8_t cdb_len) {
     char dbugblah[100];
@@ -274,11 +213,9 @@ int send_usb_mass_storage_command(libusb_device_handle *handle, uint8_t endpoint
     memcpy(&(c_buf[i]), cdb, cdb_length);
     
     int sending_length = STLINK_SG_SIZE;
-    DLOG("sending length set to: %d\n", sending_length);
     
     // send....
     do {
-        DLOG("attempting tx...\n");
         ret = libusb_bulk_transfer(handle, endpoint_out, c_buf, sending_length,
                                    &real_transferred, SG_TIMEOUT_MSEC);
         if (ret == LIBUSB_ERROR_PIPE) {
@@ -290,50 +227,10 @@ int send_usb_mass_storage_command(libusb_device_handle *handle, uint8_t endpoint
         WLOG("sending failed: %d\n", ret);
         return -1;
     }
-    DLOG("Actually sent: %d, returning tag: %d\n", real_transferred, tag);
     return this_tag;
 }
 
 
-static int get_usb_mass_storage_status(libusb_device_handle *handle, uint8_t endpoint, uint32_t *tag)
-{
-    unsigned char csw[13];
-    memset(csw, 0, sizeof(csw));
-    int transferred;
-    int ret;
-    int try = 0;
-    do {
-        ret = libusb_bulk_transfer(handle, endpoint, (unsigned char *)&csw, sizeof(csw),
-                                   &transferred, SG_TIMEOUT_MSEC);
-        if (ret == LIBUSB_ERROR_PIPE) {
-            libusb_clear_halt(handle, endpoint);
-        }
-        try++;
-    } while ((ret == LIBUSB_ERROR_PIPE) && (try < 3));
-    if (ret != LIBUSB_SUCCESS) {
-        fprintf(stderr, "%s: receiving failed: %d\n", __func__, ret);
-        return -1;
-    }
-    if (transferred != sizeof(csw)) {
-        fprintf(stderr, "%s: received unexpected amount: %d\n", __func__, transferred);
-        return -1;
-    }
-
-    uint32_t rsig = read_uint32(csw, 0);
-    uint32_t rtag = read_uint32(csw, 4);
-    uint32_t residue = read_uint32(csw, 8);
-#define USB_CSW_SIGNATURE 0x53425355  // 'U' 'S' 'B' 'S' (reversed)
-    if (rsig != USB_CSW_SIGNATURE) {
-        WLOG("status signature was invalid: %#x\n", rsig);
-        return -1;
-    }
-    DLOG("residue was= %#x\n", residue);
-    *tag = rtag;
-    uint8_t rstatus = csw[12];
-    DLOG("rstatus = %x\n", rstatus);
-    return rstatus;
-}
-
 /**
  * Straight from stm8 stlink code...
  * @param handle
@@ -388,6 +285,54 @@ get_sense(libusb_device_handle *handle, uint8_t endpoint_in, uint8_t endpoint_ou
     }
 }
 
+/**
+ * Just send a buffer on an endpoint, no questions asked.
+ * Handles repeats, and time outs.  Also handles reading status reports and sense
+ * @param handle libusb device *
+ * @param endpoint_out sends 
+ * @param endpoint_in used to read status reports back in 
+ * @param cbuf  what to send
+ * @param length how much to send
+ * @return number of bytes actually sent, or -1 for failures.
+ */
+int send_usb_data_only(libusb_device_handle *handle, unsigned char endpoint_out,
+    unsigned char endpoint_in, unsigned char *cbuf, unsigned int length) {
+    int ret;
+    int real_transferred;
+    int try = 0;
+    do {
+        ret = libusb_bulk_transfer(handle, endpoint_out, cbuf, length,
+                                   &real_transferred, SG_TIMEOUT_MSEC);
+        if (ret == LIBUSB_ERROR_PIPE) {
+            libusb_clear_halt(handle, endpoint_out);
+        }
+        try++;
+    } while ((ret == LIBUSB_ERROR_PIPE) && (try < 3));
+    if (ret != LIBUSB_SUCCESS) {
+        WLOG("sending failed: %d\n", ret);
+        return -1;
+    }
+    
+    // now, swallow up the status, so that things behave nicely...
+    uint32_t received_tag;
+    // -ve is for my errors, 0 is good, +ve is libusb sense status bytes
+    int status = get_usb_mass_storage_status(handle, endpoint_in, &received_tag);
+    if (status < 0) {
+        WLOG("receiving status failed: %d\n", status);
+        return -1;
+    }
+    if (status != 0) {
+        WLOG("receiving status not passed :(: %02x\n", status);
+    }
+    if (status == 1) {
+        get_sense(handle, endpoint_in, endpoint_out);
+        return -1;
+    }
+    
+    return real_transferred;
+}
+
+
 int stlink_q(stlink_t *sl) {
     struct stlink_libsg* sg = sl->backend_data;
     //uint8_t cdb_len = 6;  // FIXME varies!!!
@@ -405,7 +350,6 @@ int stlink_q(stlink_t *sl) {
     int ret;
     if (rx_length > 0) {
         do {
-            DLOG("attempting rx\n");
             ret = libusb_bulk_transfer(sg->usb_handle, sg->ep_rep, sl->q_buf, rx_length, 
                 &real_transferred, SG_TIMEOUT_MSEC);
             if (ret == LIBUSB_ERROR_PIPE) {
@@ -446,40 +390,6 @@ int stlink_q(stlink_t *sl) {
         return -1;
     }
     return 0;
-
-        
-    DLOG("Actually received: %d\n", real_transferred);
-
-#if FINISHED_WITH_SG
-    // Get control command descriptor of scsi structure,
-    // (one object per command!!)
-    struct sg_pt_base *ptvp = construct_scsi_pt_obj();
-    if (NULL == ptvp) {
-        fprintf(stderr, "construct_scsi_pt_obj: out of memory\n");
-        return;
-    }
-
-    set_scsi_pt_cdb(ptvp, sg->cdb_cmd_blk, sizeof (sg->cdb_cmd_blk));
-
-    // set buffer for sense (error information) data
-    set_scsi_pt_sense(ptvp, sg->sense_buf, sizeof (sg->sense_buf));
-
-    // Set a buffer to be used for data transferred from device
-    if (sg->q_data_dir == Q_DATA_IN) {
-        //clear_buf(sl);
-        set_scsi_pt_data_in(ptvp, sl->q_buf, sl->q_len);
-    } else {
-        set_scsi_pt_data_out(ptvp, sl->q_buf, sl->q_len);
-    }
-    // Executes SCSI command (or at least forwards it to lower layers).
-    sg->do_scsi_pt_err = do_scsi_pt(ptvp, sg->sg_fd, SG_TIMEOUT_SEC,
-            sl->verbose);
-
-    // check for scsi errors
-    stlink_confirm_inq(sl, ptvp);
-    // TODO recycle: clear_scsi_pt_obj(struct sg_pt_base * objp);
-    destruct_scsi_pt_obj(ptvp);
-#endif
 }
 
 // TODO thinking, cleanup
@@ -505,14 +415,11 @@ void stlink_stat(stlink_t *stl, char *txt) {
 
 void _stlink_sg_version(stlink_t *stl) {
     struct stlink_libsg *sl = stl->backend_data;
-    DLOG("\n*** stlink_version ***\n");
     clear_cdb(sl);
     sl->cdb_cmd_blk[0] = STLINK_GET_VERSION;
     stl->q_len = 6;
     sl->q_addr = 0;
     stlink_q(stl);
-    // HACK use my own private version right now...
-    
 }
 
 // Get stlink mode:
@@ -554,7 +461,7 @@ void _stlink_sg_enter_jtag_mode(stlink_t *sl) {
 }
 
 // XXX kernel driver performs reset, the device temporally disappears
-
+// Suspect this is no longer the case when we have ignore on? RECHECK
 void _stlink_sg_exit_dfu_mode(stlink_t *sl) {
     struct stlink_libsg *sg = sl->backend_data;
     DLOG("\n*** stlink_exit_dfu_mode ***\n");
@@ -630,11 +537,23 @@ void _stlink_sg_reset(stlink_t *sl) {
     stlink_stat(sl, "core reset");
 }
 
+// Arm-core reset -> halted state.
+
+void _stlink_sg_jtag_reset(stlink_t *sl, int value) {
+    struct stlink_libsg *sg = sl->backend_data;
+    clear_cdb(sg);
+    sg->cdb_cmd_blk[1] = STLINK_JTAG_DRIVE_NRST;
+    sg->cdb_cmd_blk[2] = (value)?0:1;
+    sl->q_len = 3;
+    sg->q_addr = 2;
+    stlink_q(sl);
+    stlink_stat(sl, "core reset");
+}
+
 // Arm-core status: halted or running.
 
 void _stlink_sg_status(stlink_t *sl) {
     struct stlink_libsg *sg = sl->backend_data;
-    DLOG("\n*** stlink_status ***\n");
     clear_cdb(sg);
     sg->cdb_cmd_blk[1] = STLINK_DEBUG_GETSTATUS;
     sl->q_len = 2;
@@ -646,7 +565,6 @@ void _stlink_sg_status(stlink_t *sl) {
 
 void _stlink_sg_force_debug(stlink_t *sl) {
     struct stlink_libsg *sg = sl->backend_data;
-    DLOG("\n*** stlink_force_debug ***\n");
     clear_cdb(sg);
     sg->cdb_cmd_blk[1] = STLINK_DEBUG_FORCEDEBUG;
     sl->q_len = 2;
@@ -660,9 +578,6 @@ void _stlink_sg_force_debug(stlink_t *sl) {
 void _stlink_sg_read_all_regs(stlink_t *sl, reg *regp) {
     struct stlink_libsg *sg = sl->backend_data;
 
-    /* unused */
-    regp = regp;
-
     clear_cdb(sg);
     sg->cdb_cmd_blk[1] = STLINK_DEBUG_READALLREGS;
     sl->q_len = 84;
@@ -675,23 +590,23 @@ void _stlink_sg_read_all_regs(stlink_t *sl, reg *regp) {
     // 0-3 | 4-7 | ... | 60-63 | 64-67 | 68-71   | 72-75      | 76-79 | 80-83
     // r0  | r1  | ... | r15   | xpsr  | main_sp | process_sp | rw    | rw2
     for (int i = 0; i < 16; i++) {
-        sg->reg.r[i] = read_uint32(sl->q_buf, 4 * i);
+        regp->r[i] = read_uint32(sl->q_buf, 4 * i);
         if (sl->verbose > 1)
-            DLOG("r%2d = 0x%08x\n", i, sg->reg.r[i]);
+            DLOG("r%2d = 0x%08x\n", i, regp->r[i]);
     }
-    sg->reg.xpsr = read_uint32(sl->q_buf, 64);
-    sg->reg.main_sp = read_uint32(sl->q_buf, 68);
-    sg->reg.process_sp = read_uint32(sl->q_buf, 72);
-    sg->reg.rw = read_uint32(sl->q_buf, 76);
-    sg->reg.rw2 = read_uint32(sl->q_buf, 80);
+    regp->xpsr = read_uint32(sl->q_buf, 64);
+    regp->main_sp = read_uint32(sl->q_buf, 68);
+    regp->process_sp = read_uint32(sl->q_buf, 72);
+    regp->rw = read_uint32(sl->q_buf, 76);
+    regp->rw2 = read_uint32(sl->q_buf, 80);
     if (sl->verbose < 2)
         return;
 
-    DLOG("xpsr       = 0x%08x\n", sg->reg.xpsr);
-    DLOG("main_sp    = 0x%08x\n", sg->reg.main_sp);
-    DLOG("process_sp = 0x%08x\n", sg->reg.process_sp);
-    DLOG("rw         = 0x%08x\n", sg->reg.rw);
-    DLOG("rw2        = 0x%08x\n", sg->reg.rw2);
+    DLOG("xpsr       = 0x%08x\n", regp->xpsr);
+    DLOG("main_sp    = 0x%08x\n", regp->main_sp);
+    DLOG("process_sp = 0x%08x\n", regp->process_sp);
+    DLOG("rw         = 0x%08x\n", regp->rw);
+    DLOG("rw2        = 0x%08x\n", regp->rw2);
 }
 
 // Read an arm-core register, the index must be in the range 0..20.
@@ -776,7 +691,6 @@ void stlink_write_dreg(stlink_t *sl, uint32_t reg, uint32_t addr) {
 
 void _stlink_sg_run(stlink_t *sl) {
     struct stlink_libsg *sg = sl->backend_data;
-    DLOG("\n*** stlink_run ***\n");
     clear_cdb(sg);
     sg->cdb_cmd_blk[1] = STLINK_DEBUG_RUNCORE;
     sl->q_len = 2;
@@ -865,11 +779,10 @@ void _stlink_sg_write_mem8(stlink_t *sl, uint32_t addr, uint16_t len) {
     write_uint32(sg->cdb_cmd_blk + 2, addr);
     write_uint16(sg->cdb_cmd_blk + 6, len);
 
-    // data_out 0-len
-    sl->q_len = len;
-    sg->q_addr = addr;
-    sg->q_data_dir = Q_DATA_OUT;
-    stlink_q(sl);
+    // this sends the command...
+    send_usb_mass_storage_command(sg->usb_handle, sg->ep_req, sg->cdb_cmd_blk, CDB_SL, 0, 0, 0);
+    // This sends the data...
+    send_usb_data_only(sg->usb_handle, sg->ep_req, sg->ep_rep, sl->q_buf, len);
     stlink_print_data(sl);
 }
 
@@ -884,46 +797,41 @@ void _stlink_sg_write_mem32(stlink_t *sl, uint32_t addr, uint16_t len) {
     write_uint32(sg->cdb_cmd_blk + 2, addr);
     write_uint16(sg->cdb_cmd_blk + 6, len);
 
-    // data_out 0-0x40-...-len
-    sl->q_len = len;
-    sg->q_addr = addr;
-    sg->q_data_dir = Q_DATA_OUT;
-    stlink_q(sl);
+    // this sends the command...
+    send_usb_mass_storage_command(sg->usb_handle, sg->ep_req, sg->cdb_cmd_blk, CDB_SL, 0, 0, 0);
+    // This sends the data...
+    send_usb_data_only(sg->usb_handle, sg->ep_req, sg->ep_rep, sl->q_buf, len);
+
     stlink_print_data(sl);
 }
 
-#if 0 /* not working */
-
-static int write_flash_mem16
-(struct stlink* sl, uint32_t addr, uint16_t val) {
-    /* half word writes */
-    if (addr % 2) return -1;
-
-    /* unlock if locked */
-    unlock_flash_if(sl);
-
-    /* set flash programming chosen bit */
-    set_flash_cr_pg(sl);
+// Write one DWORD data to memory
 
-    write_uint16(sl->q_buf, val);
-    stlink_write_mem16(sl, addr, 2);
+void _stlink_sg_write_debug32(stlink_t *sl, uint32_t addr, uint32_t data) {
+    struct stlink_libsg *sg = sl->backend_data;
+    clear_cdb(sg);
+    sg->cdb_cmd_blk[1] = STLINK_JTAG_WRITEDEBUG_32BIT;
+    // 2-5: addr
+    write_uint32(sg->cdb_cmd_blk + 2, addr);
+    write_uint32(sg->cdb_cmd_blk + 6, data);
+    sl->q_len = 2;
+    stlink_q(sl);
 
-    /* wait for non business */
-    wait_flash_busy(sl);
+}
 
-    lock_flash(sl);
+// Read one DWORD data from memory
 
-    /* check the programmed value back */
-    stlink_read_mem16(sl, addr, 2);
-    if (*(const uint16_t*) sl->q_buf != val) {
-        /* values differ at i * sizeof(uint16_t) */
-        return -1;
-    }
+uint32_t _stlink_sg_read_debug32(stlink_t *sl, uint32_t addr) {
+    struct stlink_libsg *sg = sl->backend_data;
+    clear_cdb(sg);
+    sg->cdb_cmd_blk[1] = STLINK_JTAG_READDEBUG_32BIT;
+    // 2-5: addr
+    write_uint32(sg->cdb_cmd_blk + 2, addr);
+    sl->q_len = 8;
+    stlink_q(sl);
 
-    /* success */
-    return 0;
+    return read_uint32(sl->q_buf, 4);
 }
-#endif /* not working */
 
 // Exit the jtag or swd mode and enter the mass mode.
 
@@ -953,10 +861,13 @@ stlink_backend_t _stlink_sg_backend = {
     _stlink_sg_exit_dfu_mode,
     _stlink_sg_core_id,
     _stlink_sg_reset,
+    _stlink_sg_jtag_reset,
     _stlink_sg_run,
     _stlink_sg_status,
     _stlink_sg_version,
+    _stlink_sg_read_debug32,
     _stlink_sg_read_mem32,
+    _stlink_sg_write_debug32,
     _stlink_sg_write_mem32,
     _stlink_sg_write_mem8,
     _stlink_sg_read_all_regs,
@@ -970,6 +881,7 @@ stlink_backend_t _stlink_sg_backend = {
 static stlink_t* stlink_open(const int verbose) {
     
     stlink_t *sl = malloc(sizeof (stlink_t));
+    memset(sl, 0, sizeof(stlink_t));
     struct stlink_libsg *slsg = malloc(sizeof (struct stlink_libsg));
     if (sl == NULL || slsg == NULL) {
         WLOG("Couldn't malloc stlink and stlink_sg structures out of memory!\n");
@@ -989,6 +901,7 @@ static stlink_t* stlink_open(const int verbose) {
     if (slsg->usb_handle == NULL) {
         WLOG("Failed to find an stlink v1 by VID:PID\n");
         libusb_close(slsg->usb_handle);
+        libusb_exit(slsg->libusb_ctx);
         free(sl);
         free(slsg);
         return NULL;
@@ -1003,6 +916,7 @@ static stlink_t* stlink_open(const int verbose) {
         if (r < 0) {
             WLOG("libusb_detach_kernel_driver(() error %s\n", strerror(-r));
             libusb_close(slsg->usb_handle);
+            libusb_exit(slsg->libusb_ctx);
             free(sl);
             free(slsg);
             return NULL;
@@ -1015,6 +929,7 @@ static stlink_t* stlink_open(const int verbose) {
         /* this may fail for a previous configured device */
         WLOG("libusb_get_configuration()\n");
         libusb_close(slsg->usb_handle);
+        libusb_exit(slsg->libusb_ctx);
         free(sl);
         free(slsg);
         return NULL;
@@ -1029,6 +944,7 @@ static stlink_t* stlink_open(const int verbose) {
             /* this may fail for a previous configured device */
             WLOG("libusb_set_configuration() failed\n");
             libusb_close(slsg->usb_handle);
+            libusb_exit(slsg->libusb_ctx);
             free(sl);
             free(slsg);
             return NULL;
@@ -1038,6 +954,7 @@ static stlink_t* stlink_open(const int verbose) {
     if (libusb_claim_interface(slsg->usb_handle, 0)) {
         WLOG("libusb_claim_interface() failed\n");
         libusb_close(slsg->usb_handle);
+        libusb_exit(slsg->libusb_ctx);
         free(sl);
         free(slsg);
         return NULL;
@@ -1055,27 +972,12 @@ static stlink_t* stlink_open(const int verbose) {
 
     sl->core_stat = STLINK_CORE_STAT_UNKNOWN;
     slsg->q_addr = 0;
-    clear_buf(sl);
-
-    /* flash memory settings */
-    sl->flash_base = STM32_FLASH_BASE;
-    sl->flash_size = STM32_FLASH_SIZE;
-    sl->flash_pgsz = STM32_FLASH_PGSZ;
-
-    /* system memory */
-    sl->sys_base = STM32_SYSTEM_BASE;
-    sl->sys_size = STM32_SYSTEM_SIZE;
-
-    /* sram memory settings */
-    sl->sram_base = STM32_SRAM_BASE;
-    sl->sram_size = STM32_SRAM_SIZE;
 
     return sl;
 }
 
 
-
-stlink_t* stlink_v1_open(const int verbose) {
+stlink_t* stlink_v1_open_inner(const int verbose) {
     ugly_init(verbose);
     stlink_t *sl = stlink_open(verbose);
     if (sl == NULL) {
@@ -1084,7 +986,6 @@ stlink_t* stlink_v1_open(const int verbose) {
     }
 
     stlink_version(sl);
-
     if ((sl->version.st_vid != USB_ST_VID) || (sl->version.stlink_pid != USB_STLINK_PID)) {
         ugly_log(UERROR, LOG_TAG, 
             "WTF? successfully opened, but unable to read version details. BROKEN!\n");
@@ -1093,44 +994,37 @@ stlink_t* stlink_v1_open(const int verbose) {
 
     DLOG("Reading current mode...\n");
     switch (stlink_current_mode(sl)) {
-        case STLINK_DEV_MASS_MODE:
-            return sl;
-        case STLINK_DEV_DEBUG_MODE:
-            // TODO go to mass?
+    case STLINK_DEV_MASS_MODE:
             return sl;
+    case STLINK_DEV_DEBUG_MODE:
+        // TODO go to mass?
+        return sl;
+    default:
+        ILOG("Current mode unusable, trying to get back to a useful state...\n");
+        break;
     }
 
     DLOG("Attempting to exit DFU mode\n");
     _stlink_sg_exit_dfu_mode(sl);
     
-    // exit the dfu mode -> the device is gone
-    DLOG("\n*** reopen the stlink device ***\n");
-    delay(1000);
-    stlink_close(sl);
-    delay(5000);
-
-    DLOG("Attempting to reopen the stlink...\n");
-    sl = stlink_open(verbose);
-    if (sl == NULL) {
-        fputs("Error: could not open stlink device\n", stderr);
+    // re-query device info (and retest)
+    stlink_version(sl);
+    if ((sl->version.st_vid != USB_ST_VID) || (sl->version.stlink_pid != USB_STLINK_PID)) {
+        ugly_log(UERROR, LOG_TAG, 
+            "WTF? successfully opened, but unable to read version details. BROKEN!\n");
         return NULL;
     }
-    // re-query device info
-    stlink_version(sl);
     return sl;
 }
 
-static void __attribute__((unused)) mark_buf(stlink_t *sl) {
-    clear_buf(sl);
-    sl->q_buf[0] = 0x12;
-    sl->q_buf[1] = 0x34;
-    sl->q_buf[2] = 0x56;
-    sl->q_buf[3] = 0x78;
-    sl->q_buf[4] = 0x90;
-    sl->q_buf[15] = 0x42;
-    sl->q_buf[16] = 0x43;
-    sl->q_buf[63] = 0x42;
-    sl->q_buf[64] = 0x43;
-    sl->q_buf[1024 * 6 - 1] = 0x42; //6kB
-    sl->q_buf[1024 * 8 - 1] = 0x42; //8kB
+stlink_t* stlink_v1_open(const int verbose) {
+    stlink_t *sl = stlink_v1_open_inner(verbose);
+    if (sl == NULL) {
+        fputs("Error: could not open stlink device\n", stderr);
+        return NULL;
+    }
+    // by now, it _must_ be fully open and in a useful mode....
+       stlink_enter_swd_mode(sl);
+    ILOG("Successfully opened a stlink v1 debugger\n");
+    return sl;
 }