summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRaviv Shvili <rshvili@codeaurora.org>2014-03-30 13:36:05 +0300
committerDavid Keitel <dkeitel@codeaurora.org>2016-03-22 10:56:52 -0700
commit125fc97a4fa5c143893f91bc8d8b1fd4ac6ba159 (patch)
tree14ffb29acdc13f2ff35f1cf30feaa7c2db67ae73
parenta7da3b29591656e60597cccc866808e3bcf6b51b (diff)
scsi: ufs: add read string descriptor api
The string descriptor may consist of up to 126 UNICODE characters. The number of UNICODE characters is calculated by (descriptor's length - 2)/2. In addition the api allows to get the result in an ascii format. Change-Id: If43453bf5f9a826900c0acc064e8def275c0f7d1 Signed-off-by: Raviv Shvili <rshvili@codeaurora.org>
-rw-r--r--drivers/scsi/ufs/ufs.h1
-rw-r--r--drivers/scsi/ufs/ufshcd.c89
-rw-r--r--drivers/scsi/ufs/ufshcd.h4
3 files changed, 94 insertions, 0 deletions
diff --git a/drivers/scsi/ufs/ufs.h b/drivers/scsi/ufs/ufs.h
index b8f68b61a7c2..a597a34b689f 100644
--- a/drivers/scsi/ufs/ufs.h
+++ b/drivers/scsi/ufs/ufs.h
@@ -44,6 +44,7 @@
#define GENERAL_UPIU_REQUEST_SIZE 32
#define QUERY_DESC_MAX_SIZE 255
#define QUERY_DESC_MIN_SIZE 2
+#define QUERY_DESC_HDR_SIZE 2
#define QUERY_OSF_SIZE (GENERAL_UPIU_REQUEST_SIZE - \
(sizeof(struct utp_upiu_header)))
diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index a238db4f305e..3481023af37c 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -40,6 +40,7 @@
#include <linux/async.h>
#include <scsi/ufs/ioctl.h>
#include <linux/devfreq.h>
+#include <linux/nls.h>
#include "ufshcd.h"
#include "unipro.h"
@@ -280,6 +281,16 @@ static inline void ufshcd_disable_irq(struct ufs_hba *hba)
}
}
+/* replace non-printable or non-ASCII characters with spaces */
+static inline void ufshcd_remove_non_printable(char *val)
+{
+ if (!val)
+ return;
+
+ if (*val < 0x20 || *val > 0x7e)
+ *val = ' ';
+}
+
static void ufshcd_print_host_regs(struct ufs_hba *hba)
{
/*
@@ -2153,6 +2164,84 @@ int ufshcd_read_device_desc(struct ufs_hba *hba, u8 *buf, u32 size)
}
/**
+ * ufshcd_read_string_desc - read string descriptor
+ * @hba: pointer to adapter instance
+ * @desc_index: descriptor index
+ * @buf: pointer to buffer where descriptor would be read
+ * @size: size of buf
+ * @ascii: if true convert from unicode to ascii characters
+ *
+ * Return 0 in case of success, non-zero otherwise
+ */
+int ufshcd_read_string_desc(struct ufs_hba *hba, int desc_index, u8 *buf,
+ u32 size, bool ascii)
+{
+ int err = 0;
+ int retries;
+
+ for (retries = 0; retries < QUERY_REQ_RETRIES; retries++) {
+ err = ufshcd_read_desc(hba,
+ QUERY_DESC_IDN_STRING, desc_index, buf, size);
+ if (!err)
+ break;
+ dev_dbg(hba->dev, "%s: error %d retrying %d\n", __func__, err,
+ retries);
+ }
+
+ if (err) {
+ dev_err(hba->dev, "%s: reading String Desc failed after %d retries. err = %d\n",
+ __func__, QUERY_REQ_RETRIES, err);
+ goto out;
+ }
+
+ if (ascii) {
+ int desc_len;
+ int ascii_len;
+ int i;
+ char *buff_ascii;
+
+ desc_len = buf[0];
+ /* remove header and divide by 2 to move from UTF16 to UTF8 */
+ ascii_len = (desc_len - QUERY_DESC_HDR_SIZE) / 2 + 1;
+ if (size < ascii_len + QUERY_DESC_HDR_SIZE) {
+ dev_err(hba->dev, "%s: buffer allocated size is too small\n",
+ __func__);
+ err = -ENOMEM;
+ goto out;
+ }
+
+ buff_ascii = kmalloc(ascii_len, GFP_KERNEL);
+ if (!buff_ascii) {
+ dev_err(hba->dev, "%s: Failed allocating %d bytes\n",
+ __func__, ascii_len);
+ err = -ENOMEM;
+ goto out_free_buff;
+ }
+
+ /*
+ * the descriptor contains string in UTF16 format
+ * we need to convert to utf-8 so it can be displayed
+ */
+ utf16s_to_utf8s((wchar_t *)&buf[QUERY_DESC_HDR_SIZE],
+ desc_len - QUERY_DESC_HDR_SIZE,
+ UTF16_BIG_ENDIAN, buff_ascii, ascii_len);
+
+ /* replace non-printable or non-ASCII characters with spaces */
+ for (i = 0; i < ascii_len; i++)
+ ufshcd_remove_non_printable(&buff_ascii[i]);
+
+ memset(buf + QUERY_DESC_HDR_SIZE, 0,
+ size - QUERY_DESC_HDR_SIZE);
+ memcpy(buf + QUERY_DESC_HDR_SIZE, buff_ascii, ascii_len);
+ buf[QUERY_DESC_LENGTH_OFFSET] = ascii_len + QUERY_DESC_HDR_SIZE;
+out_free_buff:
+ kfree(buff_ascii);
+ }
+out:
+ return err;
+}
+
+/**
* ufshcd_read_unit_desc_param - read the specified unit descriptor parameter
* @hba: Pointer to adapter instance
* @lun: lun id
diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h
index 34a9cc841217..51912dc59f76 100644
--- a/drivers/scsi/ufs/ufshcd.h
+++ b/drivers/scsi/ufs/ufshcd.h
@@ -778,6 +778,10 @@ static inline int ufshcd_dme_peer_get(struct ufs_hba *hba,
int ufshcd_read_device_desc(struct ufs_hba *hba, u8 *buf, u32 size);
+#define ASCII_STD true
+#define UTF16_STD false
+int ufshcd_read_string_desc(struct ufs_hba *hba, int desc_index, u8 *buf,
+ u32 size, bool ascii);
/* variant specific ops structures */
#ifdef CONFIG_SCSI_UFS_MSM
extern const struct ufs_hba_variant_ops ufs_hba_msm_vops;