Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
1// SPDX-License-Identifier: GPL-2.0
2/*
3 * bsg endpoint that supports UPIUs
4 *
5 * Copyright (C) 2018 Western Digital Corporation
6 */
7
8#include <linux/bsg-lib.h>
9#include <scsi/scsi.h>
10#include <scsi/scsi_host.h>
11#include "ufs_bsg.h"
12#include <ufs/ufshcd.h>
13#include "ufshcd-priv.h"
14
15static int ufs_bsg_get_query_desc_size(struct ufs_hba *hba, int *desc_len,
16 struct utp_upiu_query *qr)
17{
18 int desc_size = be16_to_cpu(qr->length);
19 int desc_id = qr->idn;
20
21 if (desc_size <= 0)
22 return -EINVAL;
23
24 ufshcd_map_desc_id_to_length(hba, desc_id, desc_len);
25 if (!*desc_len)
26 return -EINVAL;
27
28 *desc_len = min_t(int, *desc_len, desc_size);
29
30 return 0;
31}
32
33static int ufs_bsg_verify_query_size(struct ufs_hba *hba,
34 unsigned int request_len,
35 unsigned int reply_len)
36{
37 int min_req_len = sizeof(struct ufs_bsg_request);
38 int min_rsp_len = sizeof(struct ufs_bsg_reply);
39
40 if (min_req_len > request_len || min_rsp_len > reply_len) {
41 dev_err(hba->dev, "not enough space assigned\n");
42 return -EINVAL;
43 }
44
45 return 0;
46}
47
48static int ufs_bsg_alloc_desc_buffer(struct ufs_hba *hba, struct bsg_job *job,
49 uint8_t **desc_buff, int *desc_len,
50 enum query_opcode desc_op)
51{
52 struct ufs_bsg_request *bsg_request = job->request;
53 struct utp_upiu_query *qr;
54 u8 *descp;
55
56 if (desc_op != UPIU_QUERY_OPCODE_WRITE_DESC &&
57 desc_op != UPIU_QUERY_OPCODE_READ_DESC)
58 goto out;
59
60 qr = &bsg_request->upiu_req.qr;
61 if (ufs_bsg_get_query_desc_size(hba, desc_len, qr)) {
62 dev_err(hba->dev, "Illegal desc size\n");
63 return -EINVAL;
64 }
65
66 if (*desc_len > job->request_payload.payload_len) {
67 dev_err(hba->dev, "Illegal desc size\n");
68 return -EINVAL;
69 }
70
71 descp = kzalloc(*desc_len, GFP_KERNEL);
72 if (!descp)
73 return -ENOMEM;
74
75 if (desc_op == UPIU_QUERY_OPCODE_WRITE_DESC)
76 sg_copy_to_buffer(job->request_payload.sg_list,
77 job->request_payload.sg_cnt, descp,
78 *desc_len);
79
80 *desc_buff = descp;
81
82out:
83 return 0;
84}
85
86static int ufs_bsg_request(struct bsg_job *job)
87{
88 struct ufs_bsg_request *bsg_request = job->request;
89 struct ufs_bsg_reply *bsg_reply = job->reply;
90 struct ufs_hba *hba = shost_priv(dev_to_shost(job->dev->parent));
91 unsigned int req_len = job->request_len;
92 unsigned int reply_len = job->reply_len;
93 struct uic_command uc = {};
94 int msgcode;
95 uint8_t *desc_buff = NULL;
96 int desc_len = 0;
97 enum query_opcode desc_op = UPIU_QUERY_OPCODE_NOP;
98 int ret;
99
100 ret = ufs_bsg_verify_query_size(hba, req_len, reply_len);
101 if (ret)
102 goto out;
103
104 bsg_reply->reply_payload_rcv_len = 0;
105
106 ufshcd_rpm_get_sync(hba);
107
108 msgcode = bsg_request->msgcode;
109 switch (msgcode) {
110 case UPIU_TRANSACTION_QUERY_REQ:
111 desc_op = bsg_request->upiu_req.qr.opcode;
112 ret = ufs_bsg_alloc_desc_buffer(hba, job, &desc_buff,
113 &desc_len, desc_op);
114 if (ret) {
115 ufshcd_rpm_put_sync(hba);
116 goto out;
117 }
118
119 fallthrough;
120 case UPIU_TRANSACTION_NOP_OUT:
121 case UPIU_TRANSACTION_TASK_REQ:
122 ret = ufshcd_exec_raw_upiu_cmd(hba, &bsg_request->upiu_req,
123 &bsg_reply->upiu_rsp, msgcode,
124 desc_buff, &desc_len, desc_op);
125 if (ret)
126 dev_err(hba->dev,
127 "exe raw upiu: error code %d\n", ret);
128
129 break;
130 case UPIU_TRANSACTION_UIC_CMD:
131 memcpy(&uc, &bsg_request->upiu_req.uc, UIC_CMD_SIZE);
132 ret = ufshcd_send_uic_cmd(hba, &uc);
133 if (ret)
134 dev_err(hba->dev,
135 "send uic cmd: error code %d\n", ret);
136
137 memcpy(&bsg_reply->upiu_rsp.uc, &uc, UIC_CMD_SIZE);
138
139 break;
140 default:
141 ret = -ENOTSUPP;
142 dev_err(hba->dev, "unsupported msgcode 0x%x\n", msgcode);
143
144 break;
145 }
146
147 ufshcd_rpm_put_sync(hba);
148
149 if (!desc_buff)
150 goto out;
151
152 if (desc_op == UPIU_QUERY_OPCODE_READ_DESC && desc_len)
153 bsg_reply->reply_payload_rcv_len =
154 sg_copy_from_buffer(job->request_payload.sg_list,
155 job->request_payload.sg_cnt,
156 desc_buff, desc_len);
157
158 kfree(desc_buff);
159
160out:
161 bsg_reply->result = ret;
162 job->reply_len = sizeof(struct ufs_bsg_reply);
163 /* complete the job here only if no error */
164 if (ret == 0)
165 bsg_job_done(job, ret, bsg_reply->reply_payload_rcv_len);
166
167 return ret;
168}
169
170/**
171 * ufs_bsg_remove - detach and remove the added ufs-bsg node
172 * @hba: per adapter object
173 *
174 * Should be called when unloading the driver.
175 */
176void ufs_bsg_remove(struct ufs_hba *hba)
177{
178 struct device *bsg_dev = &hba->bsg_dev;
179
180 if (!hba->bsg_queue)
181 return;
182
183 bsg_remove_queue(hba->bsg_queue);
184
185 device_del(bsg_dev);
186 put_device(bsg_dev);
187}
188
189static inline void ufs_bsg_node_release(struct device *dev)
190{
191 put_device(dev->parent);
192}
193
194/**
195 * ufs_bsg_probe - Add ufs bsg device node
196 * @hba: per adapter object
197 *
198 * Called during initial loading of the driver, and before scsi_scan_host.
199 */
200int ufs_bsg_probe(struct ufs_hba *hba)
201{
202 struct device *bsg_dev = &hba->bsg_dev;
203 struct Scsi_Host *shost = hba->host;
204 struct device *parent = &shost->shost_gendev;
205 struct request_queue *q;
206 int ret;
207
208 device_initialize(bsg_dev);
209
210 bsg_dev->parent = get_device(parent);
211 bsg_dev->release = ufs_bsg_node_release;
212
213 dev_set_name(bsg_dev, "ufs-bsg%u", shost->host_no);
214
215 ret = device_add(bsg_dev);
216 if (ret)
217 goto out;
218
219 q = bsg_setup_queue(bsg_dev, dev_name(bsg_dev), ufs_bsg_request, NULL, 0);
220 if (IS_ERR(q)) {
221 ret = PTR_ERR(q);
222 goto out;
223 }
224
225 hba->bsg_queue = q;
226
227 return 0;
228
229out:
230 dev_err(bsg_dev, "fail to initialize a bsg dev %d\n", shost->host_no);
231 put_device(bsg_dev);
232 return ret;
233}