@@ -18,6 +18,7 @@ Contents
4. UFSHCD User Interface
4.1 UFS Query IOCTL
4.2 UFS Auto-Hibern8 IOCTL
+ 4.3 UFS Task Management IOCTL
1. Overview
@@ -220,6 +221,40 @@ host and device using IOCTL interface.
return error;
}
+4.3 UFS Task Management IOCTL
+
+ This interface enables user to manage tasks on the device. It's meant
+ mainly for test and troubleshooting purposes. You can use following
+ snippet to comunicate with this interface:
+
+ #include <sys/ioctl.h>
+ #include <scsi/ufs/ioctl.h>
+ #include <scsi/ufs/ufs.h>
+
+ static int handleTask(int fd, uint8_t task_id, uint8_t task_func,
+ uint8_t *response)
+ {
+ ufs_ioctl_task_mgmt_data task_data;
+ int error;
+
+ /* Task ID (normally in range 0-F) */
+ task_data.task_id = task_id;
+
+ /* Task Function (check <scsi/ufs/ufs.h>) */
+ task_data.task_func = task_func;
+
+ /* [fd] used here shall be opened UFS device */
+ error = ioctl(fd, UFS_IOCTL_TASK_MANAGEMENT, &task_data);
+
+ if (!error)
+ /* This will return task specific response */
+ *response = task_data.response;
+
+ return error;
+ }
+
+ This one should be used with extreme care as it's more of a testing/vaidation
+ interface.
UFS Specifications can be found at,
@@ -88,16 +88,6 @@ static inline bool ufs_is_valid_unit_desc_lun(u8 lun)
* UFS Protocol Information Unit related definitions
*/
-/* Task management functions */
-enum {
- UFS_ABORT_TASK = 0x01,
- UFS_ABORT_TASK_SET = 0x02,
- UFS_CLEAR_TASK_SET = 0x04,
- UFS_LOGICAL_RESET = 0x08,
- UFS_QUERY_TASK = 0x80,
- UFS_QUERY_TASK_SET = 0x81,
-};
-
/* UTP UPIU Transaction Codes Initiator to Target */
enum {
UPIU_TRANSACTION_NOP_OUT = 0x00,
@@ -180,6 +180,7 @@ static int ufshcd_query_ioctl(struct ufs_hba *hba, u8 lun, void __user *buffer)
goto out_release_mem;
}
+ /* Prepare to handle query */
switch (ioctl_data->opcode) {
case UPIU_QUERY_OPCODE_WRITE_DESC:
write = true;
@@ -329,6 +330,51 @@ static int ufshcd_auto_hibern8_ioctl(struct ufs_hba *hba, void __user *buffer)
return err;
}
+static int ufshcd_task_mgmt_ioctl(struct ufs_hba *hba, u8 lun,
+ void __user *buffer)
+{
+ struct ufs_ioctl_task_mgmt_data *ioctl_data;
+ int err = 0;
+
+ if (!buffer)
+ return -EINVAL;
+
+ if (!ufs_is_valid_unit_desc_lun(lun))
+ return -EINVAL;
+
+ ioctl_data = kzalloc(sizeof(struct ufs_ioctl_task_mgmt_data),
+ GFP_KERNEL);
+ if (!ioctl_data) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ /* Extract params from user buffer */
+ if (copy_from_user(ioctl_data, buffer, sizeof(*ioctl_data))) {
+ err = -EFAULT;
+ goto out_release_mem;
+ }
+
+ err = ufshcd_issue_tm_cmd(hba, lun, ioctl_data->task_id,
+ ioctl_data->task_func, &ioctl_data->response);
+
+ if (err)
+ goto out_release_mem;
+
+ /* Copy response to user */
+ if (copy_to_user(buffer, ioctl_data, sizeof(*ioctl_data)))
+ err = -EFAULT;
+
+out_release_mem:
+ kfree(ioctl_data);
+out:
+ if (err)
+ dev_err(hba->dev, "User Task Management failed (error: %d)",
+ err);
+
+ return err;
+}
+
/**
* ufshcd_ioctl - ufs ioctl callback registered in scsi_host
* @dev: scsi device required for per LUN queries
@@ -338,6 +384,7 @@ static int ufshcd_auto_hibern8_ioctl(struct ufs_hba *hba, void __user *buffer)
* Supported commands:
* UFS_IOCTL_QUERY
* UFS_IOCTL_AUTO_HIBERN8
+ * UFS_IOCTL_TASK_MANAGEMENT
*/
int ufshcd_ioctl(struct scsi_device *dev, int cmd, void __user *buffer)
{
@@ -359,6 +405,12 @@ int ufshcd_ioctl(struct scsi_device *dev, int cmd, void __user *buffer)
err = ufshcd_auto_hibern8_ioctl(hba, buffer);
pm_runtime_put_sync(hba->dev);
break;
+ case UFS_IOCTL_TASK_MANAGEMENT:
+ pm_runtime_get_sync(hba->dev);
+ err = ufshcd_task_mgmt_ioctl(hba,
+ ufshcd_scsi_to_upiu_lun(dev->lun), buffer);
+ pm_runtime_put_sync(hba->dev);
+ break;
default:
err = -EOPNOTSUPP;
break;
@@ -5437,7 +5437,7 @@ static int ufshcd_clear_tm_cmd(struct ufs_hba *hba, int tag)
*
* Returns non-zero value on error, zero on success.
*/
-static int ufshcd_issue_tm_cmd(struct ufs_hba *hba, int lun_id, int task_id,
+int ufshcd_issue_tm_cmd(struct ufs_hba *hba, int lun_id, int task_id,
u8 tm_function, u8 *tm_response)
{
struct utp_task_req_desc *task_req_descp;
@@ -864,6 +864,10 @@ int ufshcd_map_desc_id_to_length(struct ufs_hba *hba, enum desc_idn desc_id,
void ufshcd_setup_auto_hibern8(struct ufs_hba *hba, u8 scale, u16 timer_val);
u32 ufshcd_read_auto_hibern8_state(struct ufs_hba *hba);
+/* Expose Task Management API */
+int ufshcd_issue_tm_cmd(struct ufs_hba *hba, int lun_id, int task_id,
+ u8 tm_function, u8 *tm_response);
+
/* Wrapper functions for safely calling variant operations */
static inline const char *ufshcd_get_var_name(struct ufs_hba *hba)
{
@@ -255,7 +255,7 @@ static inline int scsi_is_wlun(u64 lun)
* Here are some scsi specific ioctl commands which are sometimes useful.
*
* Note that include/linux/cdrom.h also defines IOCTL 0x5300 - 0x5395
- * include/uapi/scsi/ufs/ioctl.h defines 0x53A0 - 0x53A1
+ * include/uapi/scsi/ufs/ioctl.h defines 0x53A0 - 0x53A2
*/
/* Used to obtain PUN and LUN info. Conflicts with CDROMAUDIOBUFSIZ */
@@ -9,6 +9,7 @@
*/
#define UFS_IOCTL_QUERY 0x53A0
#define UFS_IOCTL_AUTO_HIBERN8 0x53A1
+#define UFS_IOCTL_TASK_MANAGEMENT 0x53A2
/**
* struct ufs_ioctl_query_data - used to transfer data to and from user via
@@ -89,4 +90,21 @@ struct ufs_ioctl_auto_hibern8_data {
__u16 timer_val;
};
+/**
+ * struct ufs_ioctl_task_mgmt_data - used to perform Task Management specific
+ * functions
+ *
+ * @task_id: ID of a task to be managed
+ * @task_func: function to perform on managed task
+ * @response: Task Management response
+ *
+ * Submitted: task_id, task_func
+ * Received: response
+ */
+struct ufs_ioctl_task_mgmt_data {
+ __u8 task_id;
+ __u8 task_func;
+ __u8 response;
+};
+
#endif /* UAPI_UFS_IOCTL_H_ */
@@ -66,4 +66,14 @@ enum query_opcode {
UPIU_QUERY_OPCODE_TOGGLE_FLAG = 0x8,
};
+/* Task management functions */
+enum {
+ UFS_ABORT_TASK = 0x01,
+ UFS_ABORT_TASK_SET = 0x02,
+ UFS_CLEAR_TASK_SET = 0x04,
+ UFS_LOGICAL_RESET = 0x08,
+ UFS_QUERY_TASK = 0x80,
+ UFS_QUERY_TASK_SET = 0x81,
+};
+
#endif /* UAPI_UFS_H_ */