diff options
| author | Raviv Shvili <rshvili@codeaurora.org> | 2014-03-30 13:36:05 +0300 |
|---|---|---|
| committer | David Keitel <dkeitel@codeaurora.org> | 2016-03-22 10:56:52 -0700 |
| commit | 125fc97a4fa5c143893f91bc8d8b1fd4ac6ba159 (patch) | |
| tree | 14ffb29acdc13f2ff35f1cf30feaa7c2db67ae73 | |
| parent | a7da3b29591656e60597cccc866808e3bcf6b51b (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.h | 1 | ||||
| -rw-r--r-- | drivers/scsi/ufs/ufshcd.c | 89 | ||||
| -rw-r--r-- | drivers/scsi/ufs/ufshcd.h | 4 |
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; |
