Merge branch 'master' of https://github.com/texane/stlink
[fw/stlink] / src / stlink-sg.c
index 9d71df12326bf48df329285dae5949ee7c244373..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
  */
 
 
 #define __USE_GNU
+#include <assert.h>
 #include <stdio.h>
 #include <string.h>
 #include <stdarg.h>
@@ -106,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) {
@@ -127,6 +125,179 @@ void _stlink_sg_close(stlink_t *sl) {
     }
 }
 
+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;
+}
+
+static int dump_CDB_command(uint8_t *cdb, uint8_t cdb_len) {
+    char dbugblah[100];
+    char *dbugp = dbugblah;
+    dbugp += sprintf(dbugp, "Sending CDB [");
+    for (uint8_t i = 0; i < cdb_len; i++) {
+        dbugp += sprintf(dbugp, " %#02x", (unsigned int) cdb[i]);
+    }
+    sprintf(dbugp, "]\n");
+    DLOG(dbugblah);
+    return 0;
+}
+
+/**
+ * Wraps a CDB mass storage command in the appropriate gunk to get it down
+ * @param handle
+ * @param endpoint
+ * @param cdb
+ * @param cdb_length
+ * @param lun
+ * @param flags
+ * @param expected_rx_size
+ * @return 
+ */
+int send_usb_mass_storage_command(libusb_device_handle *handle, uint8_t endpoint_out,
+                              uint8_t *cdb, uint8_t cdb_length,
+                              uint8_t lun, uint8_t flags, uint32_t expected_rx_size) {
+    DLOG("Sending usb m-s cmd: cdblen:%d, rxsize=%d\n", cdb_length, expected_rx_size);
+    dump_CDB_command(cdb, cdb_length);
+
+    static uint32_t tag;
+    if (tag == 0) {
+        tag = 1;
+    }
+
+    int try = 0;
+    int ret = 0;
+    int real_transferred;
+    int i = 0;
+
+    uint8_t c_buf[STLINK_SG_SIZE];
+    // tag is allegedly ignored... TODO - verify
+    c_buf[i++] = 'U';
+    c_buf[i++] = 'S';
+    c_buf[i++] = 'B';
+    c_buf[i++] = 'C';
+    write_uint32(&c_buf[i], tag);
+    uint32_t this_tag = tag++;
+    write_uint32(&c_buf[i+4], expected_rx_size);
+    i+= 8;
+    c_buf[i++] = flags;
+    c_buf[i++] = lun;
+
+    c_buf[i++] = cdb_length;
+
+    // Now the actual CDB request
+    assert(cdb_length <= CDB_SL);
+    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) {
+            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, returning tag: %d\n", real_transferred, tag);
+    return this_tag;
+}
+
+
+/**
+ * Straight from stm8 stlink code...
+ * @param handle
+ * @param endpoint_in
+ * @param endpoint_out
+ */
+static void
+get_sense(libusb_device_handle *handle, uint8_t endpoint_in, uint8_t endpoint_out)
+{
+    DLOG("Fetching sense...\n");
+    uint8_t cdb[16];
+    memset(cdb, 0, sizeof(cdb));
+#define REQUEST_SENSE 0x03
+#define REQUEST_SENSE_LENGTH 18
+    cdb[0] = REQUEST_SENSE;
+    cdb[4] = REQUEST_SENSE_LENGTH;
+    uint32_t tag = send_usb_mass_storage_command(handle, endpoint_out, cdb, sizeof(cdb), 0,
+                                                 LIBUSB_ENDPOINT_IN, REQUEST_SENSE_LENGTH);
+    if (tag == 0) {
+        WLOG("refusing to send request sense with tag 0\n");
+        return;
+    }
+    unsigned char sense[REQUEST_SENSE_LENGTH];
+    int transferred;
+    int ret;
+    int try = 0;
+    do {
+        ret = libusb_bulk_transfer(handle, endpoint_in, sense, sizeof(sense),
+                                   &transferred, SG_TIMEOUT_MSEC);
+        if (ret == LIBUSB_ERROR_PIPE) {
+            libusb_clear_halt(handle, endpoint_in);
+        }
+        try++;
+    } while ((ret == LIBUSB_ERROR_PIPE) && (try < 3));
+    if (ret != LIBUSB_SUCCESS) {
+        WLOG("receiving sense failed: %d\n", ret);
+        return;
+    }
+    if (transferred != sizeof(sense)) {
+        WLOG("received unexpected amount of sense: %d != %d\n", transferred, sizeof(sense));
+    }
+    uint32_t received_tag;
+    int status = get_usb_mass_storage_status(handle, endpoint_in, &received_tag);
+    if (status != 0) {
+        WLOG("receiving sense failed with status: %02x\n", status);
+        return;
+    }
+    if (sense[0] != 0x70 && sense[0] != 0x71) {
+        WLOG("No sense data\n");
+    } else {
+        WLOG("Sense KCQ: %02X %02X %02X\n", sense[2] & 0x0f, sense[12], sense[13]);
+    }
+}
+
 
 //TODO rewrite/cleanup, save the error in sl
 
@@ -214,14 +385,119 @@ static void stlink_confirm_inq(stlink_t *stl, struct sg_pt_base *ptvp) {
 }
 #endif
 
-void stlink_q(stlink_t *sl) {
-#if FINISHED_WITH_SG
+/**
+ * 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;
-    DLOG("CDB[");
-    for (int i = 0; i < CDB_SL; i++)
-        DLOG(" 0x%02x", (unsigned int) sg->cdb_cmd_blk[i]);
-    DLOG("]\n");
+    //uint8_t cdb_len = 6;  // FIXME varies!!!
+    uint8_t cdb_len = 10;  // FIXME varies!!!
+    uint8_t lun = 0;  // always zero...
+    uint32_t tag = send_usb_mass_storage_command(sg->usb_handle, sg->ep_req, 
+        sg->cdb_cmd_blk, cdb_len, lun, LIBUSB_ENDPOINT_IN, sl->q_len);
+    
+    
+    // now wait for our response...
+    // length copied from stlink-usb...
+    int rx_length = sl->q_len;
+    int try = 0;
+    int real_transferred;
+    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) {
+                libusb_clear_halt(sg->usb_handle, sg->ep_req);
+            }
+            try++;
+        } while ((ret == LIBUSB_ERROR_PIPE) && (try < 3));
+
+        if (ret != LIBUSB_SUCCESS) {
+            WLOG("Receiving failed: %d\n", ret);
+            return -1;
+        }
+
+        if (real_transferred != rx_length) {
+            WLOG("received unexpected amount: %d != %d\n", real_transferred, rx_length);
+        }
+    }
+
+    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(sg->usb_handle, sg->ep_rep, &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(sg->usb_handle, sg->ep_rep, sg->ep_req);
+        return -1;
+    }
+    if (received_tag != tag) {
+        WLOG("received tag %d but expected %d\n", received_tag, tag);
+        //return -1;
+    }
+    if (rx_length > 0 && real_transferred != rx_length) {
+        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();
@@ -281,102 +557,9 @@ void _stlink_sg_version(stlink_t *stl) {
     sl->cdb_cmd_blk[0] = STLINK_GET_VERSION;
     stl->q_len = 6;
     sl->q_addr = 0;
-//    stlink_q(stl);
+    stlink_q(stl);
     // HACK use my own private version right now...
     
-    int try = 0;
-    int ret = 0;
-    int real_transferred;
-    
-/*
-    uint32_t    dCBWSignature;
-    uint32_t    dCBWTag;
-    uint32_t    dCBWDataTransferLength;
-    uint8_t     bmCBWFlags;
-    uint8_t     bCBWLUN;
-    uint8_t     bCBWCBLength;
-    uint8_t     CBWCB[16];
-  */    
-
-#if using_old_code_examples
-    /*
-     * and from old code?
-    cmd[i++] = 'U';
-    cmd[i++] = 'S';
-    cmd[i++] = 'B';
-    cmd[i++] = 'C';
-    write_uint32(&cmd[i], sg->sg_transfer_idx);
-    write_uint32(&cmd[i + 4], len);
-    i += 8;
-    cmd[i++] = (dir == SG_DXFER_FROM_DEV)?0x80:0;
-    cmd[i++] = 0; /* Logical unit */
-    cmd[i++] = 0xa; /* Command length */
-     */
-#endif
-    
-    int i = 0;
-    stl->c_buf[i++] = 'U';
-    stl->c_buf[i++] = 'S';
-    stl->c_buf[i++] = 'B';
-    stl->c_buf[i++] = 'C';
-    // tag is allegedly ignored... TODO - verify
-    write_uint32(&stl->c_buf[i], 1);
-    // TODO - Does this even matter? verify with more commands....
-    uint32_t command_length = STLINK_CMD_SIZE;
-    write_uint32(&stl->c_buf[i+4], command_length);
-    i+= 8;
-    stl->c_buf[i++] = LIBUSB_ENDPOINT_IN;
-    // assumption: lun is always 0;
-    stl->c_buf[i++] = 0;  
-    
-    stl->c_buf[i++] = sizeof(sl->cdb_cmd_blk);
-    
-    // duh, now the actual data...
-    memcpy(&(stl->c_buf[i]), sl->cdb_cmd_blk, sizeof(sl->cdb_cmd_blk));
-    
-    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(sl->usb_handle, sl->ep_req, stl->c_buf, sending_length,
-                                   &real_transferred, 3 * 1000);
-        if (ret == LIBUSB_ERROR_PIPE) {
-            libusb_clear_halt(sl->usb_handle, sl->ep_req);
-        }
-        try++;
-    } while ((ret == LIBUSB_ERROR_PIPE) && (try < 3));
-    if (ret != LIBUSB_SUCCESS) {
-        WLOG("sending failed: %d\n", ret);
-        return;
-    }
-    DLOG("Actually sent: %d\n", real_transferred);
-    
-    // now wait for our response...
-    // length copied from stlink-usb...
-    int rx_length = 6;
-    try = 0;
-    do {
-        DLOG("attempting rx\n");
-        ret = libusb_bulk_transfer(sl->usb_handle, sl->ep_rep, stl->q_buf, rx_length, 
-            &real_transferred, 3 * 1000);
-        if (ret == LIBUSB_ERROR_PIPE) {
-            libusb_clear_halt(sl->usb_handle, sl->ep_req);
-        }
-        try++;
-    } while ((ret == LIBUSB_ERROR_PIPE) && (try < 3));
-
-    if (ret != LIBUSB_SUCCESS) {
-        WLOG("Receiving failed: %d\n", ret);
-        return;
-    }
-    
-    if (real_transferred != rx_length) {
-        WLOG("received unexpected amount: %d != %d\n", real_transferred, rx_length);
-    }
-        
-    DLOG("Actually received: %d\n", real_transferred);
 }
 
 // Get stlink mode:
@@ -729,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);
 }
 
@@ -748,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);
 }
 
@@ -831,10 +1013,10 @@ stlink_backend_t _stlink_sg_backend = {
     _stlink_sg_force_debug
 };
 
-static stlink_t* stlink_open(const char *dev_name, const int verbose) {
-    DLOG("*** stlink_open [%s] ***\n", dev_name);
+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");
@@ -920,7 +1102,6 @@ static stlink_t* stlink_open(const char *dev_name, 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;
@@ -940,28 +1121,23 @@ static stlink_t* stlink_open(const char *dev_name, const int verbose) {
 
 
 
-stlink_t* stlink_v1_open(const char *dev_name, const int verbose) {
+stlink_t* stlink_v1_open(const int verbose) {
     ugly_init(verbose);
-    stlink_t *sl = stlink_open(dev_name, verbose);
+    stlink_t *sl = stlink_open(verbose);
     if (sl == NULL) {
         fputs("Error: could not open stlink device\n", stderr);
         return NULL;
     }
 
     stlink_version(sl);
-    struct stlink_libsg *sg = sl->backend_data;
 
     if ((sl->version.st_vid != USB_ST_VID) || (sl->version.stlink_pid != USB_STLINK_PID)) {
-        fprintf(stderr, "Error: the device %s is not a stlink\n",
-                dev_name);
-        fprintf(stderr, "       VID: got %04x expect %04x \n",
-                sl->version.st_vid, USB_ST_VID);
-        fprintf(stderr, "       PID: got %04x expect %04x \n",
-                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;
     }
 
-    DLOG("\n*** stlink_force_open ***\n");
+    DLOG("Reading current mode...\n");
     switch (stlink_current_mode(sl)) {
         case STLINK_DEV_MASS_MODE:
             return sl;
@@ -969,15 +1145,18 @@ stlink_t* stlink_v1_open(const char *dev_name, const int verbose) {
             // TODO go to mass?
             return sl;
     }
-    DLOG("\n*** switch the stlink to mass mode ***\n");
+
+    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);
 
-    sl = stlink_open(dev_name, verbose);
+    DLOG("Attempting to reopen the stlink...\n");
+    sl = stlink_open(verbose);
     if (sl == NULL) {
         fputs("Error: could not open stlink device\n", stderr);
         return NULL;
@@ -986,18 +1165,3 @@ stlink_t* stlink_v1_open(const char *dev_name, 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
-}