scsi: ufs: add ioctl interface for query request
This patch exposes the ioctl interface for UFS driver via SCSI device ioctl interface. As of now UFS driver would provide the ioctl for query interface to connected UFS device. Change-Id: I7a5cea8de925dc0ecb6643774321e2c3690c504e Signed-off-by: Dolev Raviv <draviv@codeaurora.org> [gbroner@codeaurora.org: dropping changes to the following files: include/uapi/scsi/Kbuild include/uapi/scsi/ufs/Kbuild include/uapi/scsi/ufs/ioctl.h include/uapi/scsi/ufs/ufs.h which are already included in previous change titled: 'uapi: scsi: ufs: add ioctl interface for query request'] Signed-off-by: Gilad Broner <gbroner@codeaurora.org> [subhashj@codeaurora.org: resolved trivial merge conflicts and compilation errors] Signed-off-by: Subhash Jadavani <subhashj@codeaurora.org>
This commit is contained in:
parent
3154337ccd
commit
58cf7d3fea
2 changed files with 229 additions and 43 deletions
|
@ -38,6 +38,7 @@
|
|||
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/types.h>
|
||||
#include <scsi/ufs/ufs.h>
|
||||
|
||||
#define MAX_CDB_SIZE 16
|
||||
#define GENERAL_UPIU_REQUEST_SIZE 32
|
||||
|
@ -71,6 +72,16 @@ enum {
|
|||
UFS_UPIU_RPMB_WLUN = 0xC4,
|
||||
};
|
||||
|
||||
/**
|
||||
* ufs_is_valid_unit_desc_lun - checks if the given LUN has a unit descriptor
|
||||
* @lun: LU number to check
|
||||
* @return: true if the lun has a matching unit descriptor, false otherwise
|
||||
*/
|
||||
static inline bool ufs_is_valid_unit_desc_lun(u8 lun)
|
||||
{
|
||||
return (lun == UFS_UPIU_RPMB_WLUN || (lun < UFS_UPIU_MAX_GENERAL_LUN));
|
||||
}
|
||||
|
||||
/*
|
||||
* UFS Protocol Information Unit related definitions
|
||||
*/
|
||||
|
@ -126,35 +137,6 @@ enum {
|
|||
UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST = 0x81,
|
||||
};
|
||||
|
||||
/* Flag idn for Query Requests*/
|
||||
enum flag_idn {
|
||||
QUERY_FLAG_IDN_FDEVICEINIT = 0x01,
|
||||
QUERY_FLAG_IDN_PWR_ON_WPE = 0x03,
|
||||
QUERY_FLAG_IDN_BKOPS_EN = 0x04,
|
||||
};
|
||||
|
||||
/* Attribute idn for Query requests */
|
||||
enum attr_idn {
|
||||
QUERY_ATTR_IDN_ACTIVE_ICC_LVL = 0x03,
|
||||
QUERY_ATTR_IDN_BKOPS_STATUS = 0x05,
|
||||
QUERY_ATTR_IDN_EE_CONTROL = 0x0D,
|
||||
QUERY_ATTR_IDN_EE_STATUS = 0x0E,
|
||||
};
|
||||
|
||||
/* Descriptor idn for Query requests */
|
||||
enum desc_idn {
|
||||
QUERY_DESC_IDN_DEVICE = 0x0,
|
||||
QUERY_DESC_IDN_CONFIGURAION = 0x1,
|
||||
QUERY_DESC_IDN_UNIT = 0x2,
|
||||
QUERY_DESC_IDN_RFU_0 = 0x3,
|
||||
QUERY_DESC_IDN_INTERCONNECT = 0x4,
|
||||
QUERY_DESC_IDN_STRING = 0x5,
|
||||
QUERY_DESC_IDN_RFU_1 = 0x6,
|
||||
QUERY_DESC_IDN_GEOMETRY = 0x7,
|
||||
QUERY_DESC_IDN_POWER = 0x8,
|
||||
QUERY_DESC_IDN_MAX,
|
||||
};
|
||||
|
||||
enum desc_header_offset {
|
||||
QUERY_DESC_LENGTH_OFFSET = 0x00,
|
||||
QUERY_DESC_DESC_TYPE_OFFSET = 0x01,
|
||||
|
@ -247,19 +229,6 @@ enum bkops_status {
|
|||
BKOPS_STATUS_MAX = BKOPS_STATUS_CRITICAL,
|
||||
};
|
||||
|
||||
/* UTP QUERY Transaction Specific Fields OpCode */
|
||||
enum query_opcode {
|
||||
UPIU_QUERY_OPCODE_NOP = 0x0,
|
||||
UPIU_QUERY_OPCODE_READ_DESC = 0x1,
|
||||
UPIU_QUERY_OPCODE_WRITE_DESC = 0x2,
|
||||
UPIU_QUERY_OPCODE_READ_ATTR = 0x3,
|
||||
UPIU_QUERY_OPCODE_WRITE_ATTR = 0x4,
|
||||
UPIU_QUERY_OPCODE_READ_FLAG = 0x5,
|
||||
UPIU_QUERY_OPCODE_SET_FLAG = 0x6,
|
||||
UPIU_QUERY_OPCODE_CLEAR_FLAG = 0x7,
|
||||
UPIU_QUERY_OPCODE_TOGGLE_FLAG = 0x8,
|
||||
};
|
||||
|
||||
/* Query response result code */
|
||||
enum {
|
||||
QUERY_RESULT_SUCCESS = 0x00,
|
||||
|
|
|
@ -38,6 +38,7 @@
|
|||
*/
|
||||
|
||||
#include <linux/async.h>
|
||||
#include <scsi/ufs/ioctl.h>
|
||||
#include <linux/devfreq.h>
|
||||
|
||||
#include "ufshcd.h"
|
||||
|
@ -2010,7 +2011,7 @@ static inline int ufshcd_read_unit_desc_param(struct ufs_hba *hba,
|
|||
* Unit descriptors are only available for general purpose LUs (LUN id
|
||||
* from 0 to 7) and RPMB Well known LU.
|
||||
*/
|
||||
if (lun != UFS_UPIU_RPMB_WLUN && (lun >= UFS_UPIU_MAX_GENERAL_LUN))
|
||||
if (!ufs_is_valid_unit_desc_lun(lun))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
return ufshcd_read_desc_param(hba, QUERY_DESC_IDN_UNIT, lun,
|
||||
|
@ -4465,6 +4466,221 @@ static void ufshcd_async_scan(void *data, async_cookie_t cookie)
|
|||
ufshcd_probe_hba(hba);
|
||||
}
|
||||
|
||||
/**
|
||||
* ufshcd_query_ioctl - perform user read queries
|
||||
* @hba: per-adapter instance
|
||||
* @lun: used for lun specific queries
|
||||
* @buffer: user space buffer for reading and submitting query data and params
|
||||
* @return: 0 for success negative error code otherwise
|
||||
*
|
||||
* Expected/Submitted buffer structure is struct ufs_ioctl_query_data.
|
||||
* It will read the opcode, idn and buf_length parameters, and, put the
|
||||
* response in the buffer field while updating the used size in buf_length.
|
||||
*/
|
||||
static int ufshcd_query_ioctl(struct ufs_hba *hba, u8 lun, void __user *buffer)
|
||||
{
|
||||
struct ufs_ioctl_query_data *ioct_data;
|
||||
int err = 0;
|
||||
int length = 0;
|
||||
void *data_ptr;
|
||||
bool flag;
|
||||
u32 att;
|
||||
u8 index;
|
||||
u8 *desc = NULL;
|
||||
|
||||
ioct_data = kmalloc(sizeof(struct ufs_ioctl_query_data), GFP_KERNEL);
|
||||
if (!ioct_data) {
|
||||
dev_err(hba->dev, "%s: Failed allocating %zu bytes\n", __func__,
|
||||
sizeof(struct ufs_ioctl_query_data));
|
||||
err = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* extract params from user buffer */
|
||||
err = copy_from_user(ioct_data, buffer,
|
||||
sizeof(struct ufs_ioctl_query_data));
|
||||
if (err) {
|
||||
dev_err(hba->dev,
|
||||
"%s: Failed copying buffer from user, err %d\n",
|
||||
__func__, err);
|
||||
goto out_release_mem;
|
||||
}
|
||||
|
||||
/* verify legal parameters & send query */
|
||||
switch (ioct_data->opcode) {
|
||||
case UPIU_QUERY_OPCODE_READ_DESC:
|
||||
switch (ioct_data->idn) {
|
||||
case QUERY_DESC_IDN_DEVICE:
|
||||
case QUERY_DESC_IDN_CONFIGURAION:
|
||||
case QUERY_DESC_IDN_INTERCONNECT:
|
||||
case QUERY_DESC_IDN_GEOMETRY:
|
||||
case QUERY_DESC_IDN_POWER:
|
||||
index = 0;
|
||||
break;
|
||||
case QUERY_DESC_IDN_UNIT:
|
||||
if (!ufs_is_valid_unit_desc_lun(lun)) {
|
||||
dev_err(hba->dev,
|
||||
"%s: No unit descriptor for lun 0x%x\n",
|
||||
__func__, lun);
|
||||
err = -EINVAL;
|
||||
goto out_release_mem;
|
||||
}
|
||||
index = lun;
|
||||
break;
|
||||
default:
|
||||
goto out_einval;
|
||||
}
|
||||
length = min_t(int, QUERY_DESC_MAX_SIZE,
|
||||
ioct_data->buf_size);
|
||||
desc = kmalloc(length, GFP_KERNEL);
|
||||
if (!desc) {
|
||||
dev_err(hba->dev, "%s: Failed allocating %d bytes\n",
|
||||
__func__, length);
|
||||
err = -ENOMEM;
|
||||
goto out_release_mem;
|
||||
}
|
||||
err = ufshcd_query_descriptor(hba, ioct_data->opcode,
|
||||
ioct_data->idn, index, 0, desc, &length);
|
||||
break;
|
||||
case UPIU_QUERY_OPCODE_READ_ATTR:
|
||||
switch (ioct_data->idn) {
|
||||
case QUERY_ATTR_IDN_BOOT_LU_EN:
|
||||
case QUERY_ATTR_IDN_POWER_MODE:
|
||||
case QUERY_ATTR_IDN_ACTIVE_ICC_LVL:
|
||||
case QUERY_ATTR_IDN_OOO_DATA_EN:
|
||||
case QUERY_ATTR_IDN_BKOPS_STATUS:
|
||||
case QUERY_ATTR_IDN_PURGE_STATUS:
|
||||
case QUERY_ATTR_IDN_MAX_DATA_IN:
|
||||
case QUERY_ATTR_IDN_MAX_DATA_OUT:
|
||||
case QUERY_ATTR_IDN_REF_CLK_FREQ:
|
||||
case QUERY_ATTR_IDN_CONF_DESC_LOCK:
|
||||
case QUERY_ATTR_IDN_MAX_NUM_OF_RTT:
|
||||
case QUERY_ATTR_IDN_EE_CONTROL:
|
||||
case QUERY_ATTR_IDN_EE_STATUS:
|
||||
case QUERY_ATTR_IDN_SECONDS_PASSED:
|
||||
index = 0;
|
||||
break;
|
||||
case QUERY_ATTR_IDN_DYN_CAP_NEEDED:
|
||||
case QUERY_ATTR_IDN_CORR_PRG_BLK_NUM:
|
||||
index = lun;
|
||||
break;
|
||||
default:
|
||||
goto out_einval;
|
||||
}
|
||||
err = ufshcd_query_attr(hba, ioct_data->opcode, ioct_data->idn,
|
||||
index, 0, &att);
|
||||
break;
|
||||
case UPIU_QUERY_OPCODE_READ_FLAG:
|
||||
switch (ioct_data->idn) {
|
||||
case QUERY_FLAG_IDN_FDEVICEINIT:
|
||||
case QUERY_FLAG_IDN_PERMANENT_WPE:
|
||||
case QUERY_FLAG_IDN_PWR_ON_WPE:
|
||||
case QUERY_FLAG_IDN_BKOPS_EN:
|
||||
case QUERY_FLAG_IDN_PURGE_ENABLE:
|
||||
case QUERY_FLAG_IDN_FPHYRESOURCEREMOVAL:
|
||||
case QUERY_FLAG_IDN_BUSY_RTC:
|
||||
break;
|
||||
default:
|
||||
goto out_einval;
|
||||
}
|
||||
err = ufshcd_query_flag(hba, ioct_data->opcode, ioct_data->idn,
|
||||
&flag);
|
||||
break;
|
||||
default:
|
||||
goto out_einval;
|
||||
}
|
||||
|
||||
if (err) {
|
||||
dev_err(hba->dev, "%s: Query for idn %d failed\n", __func__,
|
||||
ioct_data->idn);
|
||||
goto out_release_mem;
|
||||
}
|
||||
|
||||
/*
|
||||
* copy response data
|
||||
* As we might end up reading less data then what is specified in
|
||||
* "ioct_data->buf_size". So we are updating "ioct_data->
|
||||
* buf_size" to what exactly we have read.
|
||||
*/
|
||||
switch (ioct_data->opcode) {
|
||||
case UPIU_QUERY_OPCODE_READ_DESC:
|
||||
ioct_data->buf_size = min_t(int, ioct_data->buf_size, length);
|
||||
data_ptr = desc;
|
||||
break;
|
||||
case UPIU_QUERY_OPCODE_READ_ATTR:
|
||||
ioct_data->buf_size = sizeof(u32);
|
||||
data_ptr = &att;
|
||||
break;
|
||||
case UPIU_QUERY_OPCODE_READ_FLAG:
|
||||
ioct_data->buf_size = 1;
|
||||
data_ptr = &flag;
|
||||
break;
|
||||
default:
|
||||
BUG_ON(true);
|
||||
}
|
||||
|
||||
/* copy to user */
|
||||
err = copy_to_user(buffer, ioct_data,
|
||||
sizeof(struct ufs_ioctl_query_data));
|
||||
if (err)
|
||||
dev_err(hba->dev, "%s: Failed copying back to user.\n",
|
||||
__func__);
|
||||
err = copy_to_user(buffer + sizeof(struct ufs_ioctl_query_data),
|
||||
data_ptr, ioct_data->buf_size);
|
||||
if (err)
|
||||
dev_err(hba->dev, "%s: err %d copying back to user.\n",
|
||||
__func__, err);
|
||||
goto out_release_mem;
|
||||
|
||||
out_einval:
|
||||
dev_err(hba->dev,
|
||||
"%s: illegal ufs query ioctl data, opcode 0x%x, idn 0x%x\n",
|
||||
__func__, ioct_data->opcode, (unsigned int)ioct_data->idn);
|
||||
err = -EINVAL;
|
||||
out_release_mem:
|
||||
kfree(ioct_data);
|
||||
kfree(desc);
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* ufshcd_ioctl - ufs ioctl callback registered in scsi_host
|
||||
* @dev: scsi device required for per LUN queries
|
||||
* @cmd: command opcode
|
||||
* @buffer: user space buffer for transferring data
|
||||
*
|
||||
* Supported commands:
|
||||
* UFS_IOCTL_QUERY
|
||||
*/
|
||||
static int ufshcd_ioctl(struct scsi_device *dev, int cmd, void __user *buffer)
|
||||
{
|
||||
struct ufs_hba *hba = shost_priv(dev->host);
|
||||
int err = 0;
|
||||
|
||||
BUG_ON(!hba);
|
||||
if (!buffer) {
|
||||
dev_err(hba->dev, "%s: User buffer is NULL!\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
switch (cmd) {
|
||||
case UFS_IOCTL_QUERY:
|
||||
pm_runtime_get_sync(hba->dev);
|
||||
err = ufshcd_query_ioctl(hba, ufshcd_scsi_to_upiu_lun(dev->lun),
|
||||
buffer);
|
||||
pm_runtime_put_sync(hba->dev);
|
||||
break;
|
||||
default:
|
||||
err = -EINVAL;
|
||||
dev_err(hba->dev, "%s: Illegal ufs-IOCTL cmd %d\n", __func__,
|
||||
cmd);
|
||||
break;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static struct scsi_host_template ufshcd_driver_template = {
|
||||
.module = THIS_MODULE,
|
||||
.name = UFSHCD,
|
||||
|
@ -4477,6 +4693,7 @@ static struct scsi_host_template ufshcd_driver_template = {
|
|||
.eh_abort_handler = ufshcd_abort,
|
||||
.eh_device_reset_handler = ufshcd_eh_device_reset_handler,
|
||||
.eh_host_reset_handler = ufshcd_eh_host_reset_handler,
|
||||
.ioctl = ufshcd_ioctl,
|
||||
.this_id = -1,
|
||||
.sg_tablesize = SG_ALL,
|
||||
.cmd_per_lun = UFSHCD_CMD_PER_LUN,
|
||||
|
|
Loading…
Add table
Reference in a new issue