Merge branch 'master' of https://github.com/texane/stlink
[fw/stlink] / src / stlink-sg.c
index 64f182022da3cfdece24bc3951537b557d83436d..2bbbaf9d957a80d060316c05e1d302d8ea7f02ca 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
  */
 
 
@@ -107,15 +113,6 @@ 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) {
@@ -128,92 +125,44 @@ void _stlink_sg_close(stlink_t *sl) {
     }
 }
 
-
-//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) {
+        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;
     }
-    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);
+    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;
 }
-#endif
 
 static int dump_CDB_command(uint8_t *cdb, uint8_t cdb_len) {
     char dbugblah[100];
@@ -295,45 +244,6 @@ int send_usb_mass_storage_command(libusb_device_handle *handle, uint8_t endpoint
 }
 
 
-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 +298,143 @@ get_sense(libusb_device_handle *handle, uint8_t endpoint_in, uint8_t endpoint_ou
     }
 }
 
+
+//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;
+    }
+    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);
+    }
+}
+#endif
+
+/**
+ * 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;
+    do {
+        DLOG("attempting tx...\n");
+        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;
+    }
+    DLOG("Actually sent: %d\n", real_transferred);
+    
+    // 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!!!
@@ -865,11 +912,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,11 +930,11 @@ 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);
 }
 
@@ -970,6 +1016,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");
@@ -1055,7 +1102,6 @@ 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;
@@ -1119,18 +1165,3 @@ stlink_t* stlink_v1_open(const int verbose) {
     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
-}