aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/storage/uas.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/storage/uas.c')
-rw-r--r--drivers/usb/storage/uas.c90
1 files changed, 75 insertions, 15 deletions
diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c
index f98ba40352c..8ec8a6e66f5 100644
--- a/drivers/usb/storage/uas.c
+++ b/drivers/usb/storage/uas.c
@@ -58,6 +58,9 @@ enum {
58 SUBMIT_DATA_OUT_URB = (1 << 5), 58 SUBMIT_DATA_OUT_URB = (1 << 5),
59 ALLOC_CMD_URB = (1 << 6), 59 ALLOC_CMD_URB = (1 << 6),
60 SUBMIT_CMD_URB = (1 << 7), 60 SUBMIT_CMD_URB = (1 << 7),
61 COMPLETED_DATA_IN = (1 << 8),
62 COMPLETED_DATA_OUT = (1 << 9),
63 DATA_COMPLETES_CMD = (1 << 10),
61}; 64};
62 65
63/* Overrides scsi_pointer */ 66/* Overrides scsi_pointer */
@@ -111,6 +114,7 @@ static void uas_sense(struct urb *urb, struct scsi_cmnd *cmnd)
111{ 114{
112 struct sense_iu *sense_iu = urb->transfer_buffer; 115 struct sense_iu *sense_iu = urb->transfer_buffer;
113 struct scsi_device *sdev = cmnd->device; 116 struct scsi_device *sdev = cmnd->device;
117 struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp;
114 118
115 if (urb->actual_length > 16) { 119 if (urb->actual_length > 16) {
116 unsigned len = be16_to_cpup(&sense_iu->len); 120 unsigned len = be16_to_cpup(&sense_iu->len);
@@ -128,13 +132,15 @@ static void uas_sense(struct urb *urb, struct scsi_cmnd *cmnd)
128 } 132 }
129 133
130 cmnd->result = sense_iu->status; 134 cmnd->result = sense_iu->status;
131 cmnd->scsi_done(cmnd); 135 if (!(cmdinfo->state & DATA_COMPLETES_CMD))
136 cmnd->scsi_done(cmnd);
132} 137}
133 138
134static void uas_sense_old(struct urb *urb, struct scsi_cmnd *cmnd) 139static void uas_sense_old(struct urb *urb, struct scsi_cmnd *cmnd)
135{ 140{
136 struct sense_iu_old *sense_iu = urb->transfer_buffer; 141 struct sense_iu_old *sense_iu = urb->transfer_buffer;
137 struct scsi_device *sdev = cmnd->device; 142 struct scsi_device *sdev = cmnd->device;
143 struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp;
138 144
139 if (urb->actual_length > 8) { 145 if (urb->actual_length > 8) {
140 unsigned len = be16_to_cpup(&sense_iu->len) - 2; 146 unsigned len = be16_to_cpup(&sense_iu->len) - 2;
@@ -152,7 +158,8 @@ static void uas_sense_old(struct urb *urb, struct scsi_cmnd *cmnd)
152 } 158 }
153 159
154 cmnd->result = sense_iu->status; 160 cmnd->result = sense_iu->status;
155 cmnd->scsi_done(cmnd); 161 if (!(cmdinfo->state & DATA_COMPLETES_CMD))
162 cmnd->scsi_done(cmnd);
156} 163}
157 164
158static void uas_xfer_data(struct urb *urb, struct scsi_cmnd *cmnd, 165static void uas_xfer_data(struct urb *urb, struct scsi_cmnd *cmnd,
@@ -177,6 +184,7 @@ static void uas_stat_cmplt(struct urb *urb)
177 struct Scsi_Host *shost = urb->context; 184 struct Scsi_Host *shost = urb->context;
178 struct uas_dev_info *devinfo = (void *)shost->hostdata[0]; 185 struct uas_dev_info *devinfo = (void *)shost->hostdata[0];
179 struct scsi_cmnd *cmnd; 186 struct scsi_cmnd *cmnd;
187 struct uas_cmd_info *cmdinfo;
180 u16 tag; 188 u16 tag;
181 int ret; 189 int ret;
182 190
@@ -202,12 +210,32 @@ static void uas_stat_cmplt(struct urb *urb)
202 dev_err(&urb->dev->dev, "failed submit status urb\n"); 210 dev_err(&urb->dev->dev, "failed submit status urb\n");
203 return; 211 return;
204 } 212 }
213 cmdinfo = (void *)&cmnd->SCp;
205 214
206 switch (iu->iu_id) { 215 switch (iu->iu_id) {
207 case IU_ID_STATUS: 216 case IU_ID_STATUS:
208 if (devinfo->cmnd == cmnd) 217 if (devinfo->cmnd == cmnd)
209 devinfo->cmnd = NULL; 218 devinfo->cmnd = NULL;
210 219
220 if (!(cmdinfo->state & COMPLETED_DATA_IN) &&
221 cmdinfo->data_in_urb) {
222 if (devinfo->use_streams) {
223 cmdinfo->state |= DATA_COMPLETES_CMD;
224 usb_unlink_urb(cmdinfo->data_in_urb);
225 } else {
226 usb_free_urb(cmdinfo->data_in_urb);
227 }
228 }
229 if (!(cmdinfo->state & COMPLETED_DATA_OUT) &&
230 cmdinfo->data_out_urb) {
231 if (devinfo->use_streams) {
232 cmdinfo->state |= DATA_COMPLETES_CMD;
233 usb_unlink_urb(cmdinfo->data_in_urb);
234 } else {
235 usb_free_urb(cmdinfo->data_out_urb);
236 }
237 }
238
211 if (urb->actual_length < 16) 239 if (urb->actual_length < 16)
212 devinfo->uas_sense_old = 1; 240 devinfo->uas_sense_old = 1;
213 if (devinfo->uas_sense_old) 241 if (devinfo->uas_sense_old)
@@ -236,27 +264,59 @@ static void uas_stat_cmplt(struct urb *urb)
236 dev_err(&urb->dev->dev, "failed submit status urb\n"); 264 dev_err(&urb->dev->dev, "failed submit status urb\n");
237} 265}
238 266
239static void uas_data_cmplt(struct urb *urb) 267static void uas_data_out_cmplt(struct urb *urb)
268{
269 struct scsi_cmnd *cmnd = urb->context;
270 struct scsi_data_buffer *sdb = scsi_out(cmnd);
271 struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp;
272
273 cmdinfo->state |= COMPLETED_DATA_OUT;
274
275 sdb->resid = sdb->length - urb->actual_length;
276 usb_free_urb(urb);
277
278 if (cmdinfo->state & DATA_COMPLETES_CMD)
279 cmnd->scsi_done(cmnd);
280}
281
282static void uas_data_in_cmplt(struct urb *urb)
240{ 283{
241 struct scsi_data_buffer *sdb = urb->context; 284 struct scsi_cmnd *cmnd = urb->context;
285 struct scsi_data_buffer *sdb = scsi_in(cmnd);
286 struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp;
287
288 cmdinfo->state |= COMPLETED_DATA_IN;
289
242 sdb->resid = sdb->length - urb->actual_length; 290 sdb->resid = sdb->length - urb->actual_length;
243 usb_free_urb(urb); 291 usb_free_urb(urb);
292
293 if (cmdinfo->state & DATA_COMPLETES_CMD)
294 cmnd->scsi_done(cmnd);
244} 295}
245 296
246static struct urb *uas_alloc_data_urb(struct uas_dev_info *devinfo, gfp_t gfp, 297static struct urb *uas_alloc_data_urb(struct uas_dev_info *devinfo, gfp_t gfp,
247 unsigned int pipe, u16 stream_id, 298 unsigned int pipe, struct scsi_cmnd *cmnd,
248 struct scsi_data_buffer *sdb, 299 enum dma_data_direction dir)
249 enum dma_data_direction dir)
250{ 300{
301 struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp;
251 struct usb_device *udev = devinfo->udev; 302 struct usb_device *udev = devinfo->udev;
252 struct urb *urb = usb_alloc_urb(0, gfp); 303 struct urb *urb = usb_alloc_urb(0, gfp);
304 struct scsi_data_buffer *sdb;
305 usb_complete_t complete_fn;
306 u16 stream_id = cmdinfo->stream;
253 307
254 if (!urb) 308 if (!urb)
255 goto out; 309 goto out;
256 usb_fill_bulk_urb(urb, udev, pipe, NULL, sdb->length, uas_data_cmplt, 310 if (dir == DMA_FROM_DEVICE) {
257 sdb); 311 sdb = scsi_in(cmnd);
258 if (devinfo->use_streams) 312 complete_fn = uas_data_in_cmplt;
259 urb->stream_id = stream_id; 313 } else {
314 sdb = scsi_out(cmnd);
315 complete_fn = uas_data_out_cmplt;
316 }
317 usb_fill_bulk_urb(urb, udev, pipe, NULL, sdb->length,
318 complete_fn, cmnd);
319 urb->stream_id = stream_id;
260 urb->num_sgs = udev->bus->sg_tablesize ? sdb->table.nents : 0; 320 urb->num_sgs = udev->bus->sg_tablesize ? sdb->table.nents : 0;
261 urb->sg = sdb->table.sgl; 321 urb->sg = sdb->table.sgl;
262 out: 322 out:
@@ -358,8 +418,8 @@ static int uas_submit_urbs(struct scsi_cmnd *cmnd,
358 418
359 if (cmdinfo->state & ALLOC_DATA_IN_URB) { 419 if (cmdinfo->state & ALLOC_DATA_IN_URB) {
360 cmdinfo->data_in_urb = uas_alloc_data_urb(devinfo, gfp, 420 cmdinfo->data_in_urb = uas_alloc_data_urb(devinfo, gfp,
361 devinfo->data_in_pipe, cmdinfo->stream, 421 devinfo->data_in_pipe, cmnd,
362 scsi_in(cmnd), DMA_FROM_DEVICE); 422 DMA_FROM_DEVICE);
363 if (!cmdinfo->data_in_urb) 423 if (!cmdinfo->data_in_urb)
364 return SCSI_MLQUEUE_DEVICE_BUSY; 424 return SCSI_MLQUEUE_DEVICE_BUSY;
365 cmdinfo->state &= ~ALLOC_DATA_IN_URB; 425 cmdinfo->state &= ~ALLOC_DATA_IN_URB;
@@ -376,8 +436,8 @@ static int uas_submit_urbs(struct scsi_cmnd *cmnd,
376 436
377 if (cmdinfo->state & ALLOC_DATA_OUT_URB) { 437 if (cmdinfo->state & ALLOC_DATA_OUT_URB) {
378 cmdinfo->data_out_urb = uas_alloc_data_urb(devinfo, gfp, 438 cmdinfo->data_out_urb = uas_alloc_data_urb(devinfo, gfp,
379 devinfo->data_out_pipe, cmdinfo->stream, 439 devinfo->data_out_pipe, cmnd,
380 scsi_out(cmnd), DMA_TO_DEVICE); 440 DMA_TO_DEVICE);
381 if (!cmdinfo->data_out_urb) 441 if (!cmdinfo->data_out_urb)
382 return SCSI_MLQUEUE_DEVICE_BUSY; 442 return SCSI_MLQUEUE_DEVICE_BUSY;
383 cmdinfo->state &= ~ALLOC_DATA_OUT_URB; 443 cmdinfo->state &= ~ALLOC_DATA_OUT_URB;