diff options
author | Yaniv Gardi <ygardi@codeaurora.org> | 2016-03-10 10:37:09 -0500 |
---|---|---|
committer | Martin K. Petersen <martin.petersen@oracle.com> | 2016-03-14 21:04:45 -0400 |
commit | b573d484e4ff33b60b1ef95ca30f199e749ff7c9 (patch) | |
tree | c0152a854e9af929f97643d2d37ee6025cab0017 | |
parent | 596585a285a41993f26efab686e1ef20fc16a04a (diff) |
scsi: ufs: add support to read device and string descriptors
This change adds support to read device descriptor and string descriptor
from a UFS device
Reviewed-by: Gilad Broner <gbroner@codeaurora.org>
Reviewed-by: Hannes Reinecke <hare@suse.com>
Signed-off-by: Raviv Shvili <rshvili@codeaurora.org>
Signed-off-by: Yaniv Gardi <ygardi@codeaurora.org>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
-rw-r--r-- | drivers/scsi/ufs/ufs.h | 1 | ||||
-rw-r--r-- | drivers/scsi/ufs/ufshcd.c | 88 | ||||
-rw-r--r-- | drivers/scsi/ufs/ufshcd.h | 7 |
3 files changed, 95 insertions, 1 deletions
diff --git a/drivers/scsi/ufs/ufs.h b/drivers/scsi/ufs/ufs.h index 54a16cef0367..aacb23521ef0 100644 --- a/drivers/scsi/ufs/ufs.h +++ b/drivers/scsi/ufs/ufs.h | |||
@@ -43,6 +43,7 @@ | |||
43 | #define GENERAL_UPIU_REQUEST_SIZE 32 | 43 | #define GENERAL_UPIU_REQUEST_SIZE 32 |
44 | #define QUERY_DESC_MAX_SIZE 255 | 44 | #define QUERY_DESC_MAX_SIZE 255 |
45 | #define QUERY_DESC_MIN_SIZE 2 | 45 | #define QUERY_DESC_MIN_SIZE 2 |
46 | #define QUERY_DESC_HDR_SIZE 2 | ||
46 | #define QUERY_OSF_SIZE (GENERAL_UPIU_REQUEST_SIZE - \ | 47 | #define QUERY_OSF_SIZE (GENERAL_UPIU_REQUEST_SIZE - \ |
47 | (sizeof(struct utp_upiu_header))) | 48 | (sizeof(struct utp_upiu_header))) |
48 | 49 | ||
diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 80031e6a63f5..e2ed41587f56 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c | |||
@@ -39,7 +39,7 @@ | |||
39 | 39 | ||
40 | #include <linux/async.h> | 40 | #include <linux/async.h> |
41 | #include <linux/devfreq.h> | 41 | #include <linux/devfreq.h> |
42 | 42 | #include <linux/nls.h> | |
43 | #include <linux/of.h> | 43 | #include <linux/of.h> |
44 | #include "ufshcd.h" | 44 | #include "ufshcd.h" |
45 | #include "unipro.h" | 45 | #include "unipro.h" |
@@ -232,6 +232,16 @@ static inline void ufshcd_disable_irq(struct ufs_hba *hba) | |||
232 | } | 232 | } |
233 | } | 233 | } |
234 | 234 | ||
235 | /* replace non-printable or non-ASCII characters with spaces */ | ||
236 | static inline void ufshcd_remove_non_printable(char *val) | ||
237 | { | ||
238 | if (!val) | ||
239 | return; | ||
240 | |||
241 | if (*val < 0x20 || *val > 0x7e) | ||
242 | *val = ' '; | ||
243 | } | ||
244 | |||
235 | /* | 245 | /* |
236 | * ufshcd_wait_for_register - wait for register value to change | 246 | * ufshcd_wait_for_register - wait for register value to change |
237 | * @hba - per-adapter interface | 247 | * @hba - per-adapter interface |
@@ -2021,6 +2031,82 @@ static inline int ufshcd_read_power_desc(struct ufs_hba *hba, | |||
2021 | return ufshcd_read_desc(hba, QUERY_DESC_IDN_POWER, 0, buf, size); | 2031 | return ufshcd_read_desc(hba, QUERY_DESC_IDN_POWER, 0, buf, size); |
2022 | } | 2032 | } |
2023 | 2033 | ||
2034 | int ufshcd_read_device_desc(struct ufs_hba *hba, u8 *buf, u32 size) | ||
2035 | { | ||
2036 | return ufshcd_read_desc(hba, QUERY_DESC_IDN_DEVICE, 0, buf, size); | ||
2037 | } | ||
2038 | EXPORT_SYMBOL(ufshcd_read_device_desc); | ||
2039 | |||
2040 | /** | ||
2041 | * ufshcd_read_string_desc - read string descriptor | ||
2042 | * @hba: pointer to adapter instance | ||
2043 | * @desc_index: descriptor index | ||
2044 | * @buf: pointer to buffer where descriptor would be read | ||
2045 | * @size: size of buf | ||
2046 | * @ascii: if true convert from unicode to ascii characters | ||
2047 | * | ||
2048 | * Return 0 in case of success, non-zero otherwise | ||
2049 | */ | ||
2050 | int ufshcd_read_string_desc(struct ufs_hba *hba, int desc_index, u8 *buf, | ||
2051 | u32 size, bool ascii) | ||
2052 | { | ||
2053 | int err = 0; | ||
2054 | |||
2055 | err = ufshcd_read_desc(hba, | ||
2056 | QUERY_DESC_IDN_STRING, desc_index, buf, size); | ||
2057 | |||
2058 | if (err) { | ||
2059 | dev_err(hba->dev, "%s: reading String Desc failed after %d retries. err = %d\n", | ||
2060 | __func__, QUERY_REQ_RETRIES, err); | ||
2061 | goto out; | ||
2062 | } | ||
2063 | |||
2064 | if (ascii) { | ||
2065 | int desc_len; | ||
2066 | int ascii_len; | ||
2067 | int i; | ||
2068 | char *buff_ascii; | ||
2069 | |||
2070 | desc_len = buf[0]; | ||
2071 | /* remove header and divide by 2 to move from UTF16 to UTF8 */ | ||
2072 | ascii_len = (desc_len - QUERY_DESC_HDR_SIZE) / 2 + 1; | ||
2073 | if (size < ascii_len + QUERY_DESC_HDR_SIZE) { | ||
2074 | dev_err(hba->dev, "%s: buffer allocated size is too small\n", | ||
2075 | __func__); | ||
2076 | err = -ENOMEM; | ||
2077 | goto out; | ||
2078 | } | ||
2079 | |||
2080 | buff_ascii = kmalloc(ascii_len, GFP_KERNEL); | ||
2081 | if (!buff_ascii) { | ||
2082 | err = -ENOMEM; | ||
2083 | goto out_free_buff; | ||
2084 | } | ||
2085 | |||
2086 | /* | ||
2087 | * the descriptor contains string in UTF16 format | ||
2088 | * we need to convert to utf-8 so it can be displayed | ||
2089 | */ | ||
2090 | utf16s_to_utf8s((wchar_t *)&buf[QUERY_DESC_HDR_SIZE], | ||
2091 | desc_len - QUERY_DESC_HDR_SIZE, | ||
2092 | UTF16_BIG_ENDIAN, buff_ascii, ascii_len); | ||
2093 | |||
2094 | /* replace non-printable or non-ASCII characters with spaces */ | ||
2095 | for (i = 0; i < ascii_len; i++) | ||
2096 | ufshcd_remove_non_printable(&buff_ascii[i]); | ||
2097 | |||
2098 | memset(buf + QUERY_DESC_HDR_SIZE, 0, | ||
2099 | size - QUERY_DESC_HDR_SIZE); | ||
2100 | memcpy(buf + QUERY_DESC_HDR_SIZE, buff_ascii, ascii_len); | ||
2101 | buf[QUERY_DESC_LENGTH_OFFSET] = ascii_len + QUERY_DESC_HDR_SIZE; | ||
2102 | out_free_buff: | ||
2103 | kfree(buff_ascii); | ||
2104 | } | ||
2105 | out: | ||
2106 | return err; | ||
2107 | } | ||
2108 | EXPORT_SYMBOL(ufshcd_read_string_desc); | ||
2109 | |||
2024 | /** | 2110 | /** |
2025 | * ufshcd_read_unit_desc_param - read the specified unit descriptor parameter | 2111 | * ufshcd_read_unit_desc_param - read the specified unit descriptor parameter |
2026 | * @hba: Pointer to adapter instance | 2112 | * @hba: Pointer to adapter instance |
diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h index a6d35724ccdc..54e13ccb5754 100644 --- a/drivers/scsi/ufs/ufshcd.h +++ b/drivers/scsi/ufs/ufshcd.h | |||
@@ -678,6 +678,13 @@ static inline int ufshcd_dme_peer_get(struct ufs_hba *hba, | |||
678 | return ufshcd_dme_get_attr(hba, attr_sel, mib_val, DME_PEER); | 678 | return ufshcd_dme_get_attr(hba, attr_sel, mib_val, DME_PEER); |
679 | } | 679 | } |
680 | 680 | ||
681 | int ufshcd_read_device_desc(struct ufs_hba *hba, u8 *buf, u32 size); | ||
682 | |||
683 | #define ASCII_STD true | ||
684 | |||
685 | int ufshcd_read_string_desc(struct ufs_hba *hba, int desc_index, u8 *buf, | ||
686 | u32 size, bool ascii); | ||
687 | |||
681 | /* Expose Query-Request API */ | 688 | /* Expose Query-Request API */ |
682 | int ufshcd_query_flag(struct ufs_hba *hba, enum query_opcode opcode, | 689 | int ufshcd_query_flag(struct ufs_hba *hba, enum query_opcode opcode, |
683 | enum flag_idn idn, bool *flag_res); | 690 | enum flag_idn idn, bool *flag_res); |