diff options
Diffstat (limited to 'drivers/usb/storage')
-rw-r--r-- | drivers/usb/storage/uas.c | 90 |
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 | ||
134 | static void uas_sense_old(struct urb *urb, struct scsi_cmnd *cmnd) | 139 | static 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 | ||
158 | static void uas_xfer_data(struct urb *urb, struct scsi_cmnd *cmnd, | 165 | static 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 | ||
239 | static void uas_data_cmplt(struct urb *urb) | 267 | static 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 | |||
282 | static 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 | ||
246 | static struct urb *uas_alloc_data_urb(struct uas_dev_info *devinfo, gfp_t gfp, | 297 | static 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; |