18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * USB Attached SCSI 48c2ecf20Sopenharmony_ci * Note that this is not the same as the USB Mass Storage driver 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Copyright Hans de Goede <hdegoede@redhat.com> for Red Hat, Inc. 2013 - 2016 78c2ecf20Sopenharmony_ci * Copyright Matthew Wilcox for Intel Corp, 2010 88c2ecf20Sopenharmony_ci * Copyright Sarah Sharp for Intel Corp, 2010 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <linux/blkdev.h> 128c2ecf20Sopenharmony_ci#include <linux/slab.h> 138c2ecf20Sopenharmony_ci#include <linux/types.h> 148c2ecf20Sopenharmony_ci#include <linux/module.h> 158c2ecf20Sopenharmony_ci#include <linux/usb.h> 168c2ecf20Sopenharmony_ci#include <linux/usb_usual.h> 178c2ecf20Sopenharmony_ci#include <linux/usb/hcd.h> 188c2ecf20Sopenharmony_ci#include <linux/usb/storage.h> 198c2ecf20Sopenharmony_ci#include <linux/usb/uas.h> 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#include <scsi/scsi.h> 228c2ecf20Sopenharmony_ci#include <scsi/scsi_eh.h> 238c2ecf20Sopenharmony_ci#include <scsi/scsi_dbg.h> 248c2ecf20Sopenharmony_ci#include <scsi/scsi_cmnd.h> 258c2ecf20Sopenharmony_ci#include <scsi/scsi_device.h> 268c2ecf20Sopenharmony_ci#include <scsi/scsi_host.h> 278c2ecf20Sopenharmony_ci#include <scsi/scsi_tcq.h> 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#include "uas-detect.h" 308c2ecf20Sopenharmony_ci#include "scsiglue.h" 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci#define MAX_CMNDS 256 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_cistruct uas_dev_info { 358c2ecf20Sopenharmony_ci struct usb_interface *intf; 368c2ecf20Sopenharmony_ci struct usb_device *udev; 378c2ecf20Sopenharmony_ci struct usb_anchor cmd_urbs; 388c2ecf20Sopenharmony_ci struct usb_anchor sense_urbs; 398c2ecf20Sopenharmony_ci struct usb_anchor data_urbs; 408c2ecf20Sopenharmony_ci unsigned long flags; 418c2ecf20Sopenharmony_ci int qdepth, resetting; 428c2ecf20Sopenharmony_ci unsigned cmd_pipe, status_pipe, data_in_pipe, data_out_pipe; 438c2ecf20Sopenharmony_ci unsigned use_streams:1; 448c2ecf20Sopenharmony_ci unsigned shutdown:1; 458c2ecf20Sopenharmony_ci struct scsi_cmnd *cmnd[MAX_CMNDS]; 468c2ecf20Sopenharmony_ci spinlock_t lock; 478c2ecf20Sopenharmony_ci struct work_struct work; 488c2ecf20Sopenharmony_ci struct work_struct scan_work; /* for async scanning */ 498c2ecf20Sopenharmony_ci}; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_cienum { 528c2ecf20Sopenharmony_ci SUBMIT_STATUS_URB = BIT(1), 538c2ecf20Sopenharmony_ci ALLOC_DATA_IN_URB = BIT(2), 548c2ecf20Sopenharmony_ci SUBMIT_DATA_IN_URB = BIT(3), 558c2ecf20Sopenharmony_ci ALLOC_DATA_OUT_URB = BIT(4), 568c2ecf20Sopenharmony_ci SUBMIT_DATA_OUT_URB = BIT(5), 578c2ecf20Sopenharmony_ci ALLOC_CMD_URB = BIT(6), 588c2ecf20Sopenharmony_ci SUBMIT_CMD_URB = BIT(7), 598c2ecf20Sopenharmony_ci COMMAND_INFLIGHT = BIT(8), 608c2ecf20Sopenharmony_ci DATA_IN_URB_INFLIGHT = BIT(9), 618c2ecf20Sopenharmony_ci DATA_OUT_URB_INFLIGHT = BIT(10), 628c2ecf20Sopenharmony_ci COMMAND_ABORTED = BIT(11), 638c2ecf20Sopenharmony_ci IS_IN_WORK_LIST = BIT(12), 648c2ecf20Sopenharmony_ci}; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci/* Overrides scsi_pointer */ 678c2ecf20Sopenharmony_cistruct uas_cmd_info { 688c2ecf20Sopenharmony_ci unsigned int state; 698c2ecf20Sopenharmony_ci unsigned int uas_tag; 708c2ecf20Sopenharmony_ci struct urb *cmd_urb; 718c2ecf20Sopenharmony_ci struct urb *data_in_urb; 728c2ecf20Sopenharmony_ci struct urb *data_out_urb; 738c2ecf20Sopenharmony_ci}; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci/* I hate forward declarations, but I actually have a loop */ 768c2ecf20Sopenharmony_cistatic int uas_submit_urbs(struct scsi_cmnd *cmnd, 778c2ecf20Sopenharmony_ci struct uas_dev_info *devinfo); 788c2ecf20Sopenharmony_cistatic void uas_do_work(struct work_struct *work); 798c2ecf20Sopenharmony_cistatic int uas_try_complete(struct scsi_cmnd *cmnd, const char *caller); 808c2ecf20Sopenharmony_cistatic void uas_free_streams(struct uas_dev_info *devinfo); 818c2ecf20Sopenharmony_cistatic void uas_log_cmd_state(struct scsi_cmnd *cmnd, const char *prefix, 828c2ecf20Sopenharmony_ci int status); 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci/* 858c2ecf20Sopenharmony_ci * This driver needs its own workqueue, as we need to control memory allocation. 868c2ecf20Sopenharmony_ci * 878c2ecf20Sopenharmony_ci * In the course of error handling and power management uas_wait_for_pending_cmnds() 888c2ecf20Sopenharmony_ci * needs to flush pending work items. In these contexts we cannot allocate memory 898c2ecf20Sopenharmony_ci * by doing block IO as we would deadlock. For the same reason we cannot wait 908c2ecf20Sopenharmony_ci * for anything allocating memory not heeding these constraints. 918c2ecf20Sopenharmony_ci * 928c2ecf20Sopenharmony_ci * So we have to control all work items that can be on the workqueue we flush. 938c2ecf20Sopenharmony_ci * Hence we cannot share a queue and need our own. 948c2ecf20Sopenharmony_ci */ 958c2ecf20Sopenharmony_cistatic struct workqueue_struct *workqueue; 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_cistatic void uas_do_work(struct work_struct *work) 988c2ecf20Sopenharmony_ci{ 998c2ecf20Sopenharmony_ci struct uas_dev_info *devinfo = 1008c2ecf20Sopenharmony_ci container_of(work, struct uas_dev_info, work); 1018c2ecf20Sopenharmony_ci struct uas_cmd_info *cmdinfo; 1028c2ecf20Sopenharmony_ci struct scsi_cmnd *cmnd; 1038c2ecf20Sopenharmony_ci unsigned long flags; 1048c2ecf20Sopenharmony_ci int i, err; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci spin_lock_irqsave(&devinfo->lock, flags); 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci if (devinfo->resetting) 1098c2ecf20Sopenharmony_ci goto out; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci for (i = 0; i < devinfo->qdepth; i++) { 1128c2ecf20Sopenharmony_ci if (!devinfo->cmnd[i]) 1138c2ecf20Sopenharmony_ci continue; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci cmnd = devinfo->cmnd[i]; 1168c2ecf20Sopenharmony_ci cmdinfo = (void *)&cmnd->SCp; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci if (!(cmdinfo->state & IS_IN_WORK_LIST)) 1198c2ecf20Sopenharmony_ci continue; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci err = uas_submit_urbs(cmnd, cmnd->device->hostdata); 1228c2ecf20Sopenharmony_ci if (!err) 1238c2ecf20Sopenharmony_ci cmdinfo->state &= ~IS_IN_WORK_LIST; 1248c2ecf20Sopenharmony_ci else 1258c2ecf20Sopenharmony_ci queue_work(workqueue, &devinfo->work); 1268c2ecf20Sopenharmony_ci } 1278c2ecf20Sopenharmony_ciout: 1288c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&devinfo->lock, flags); 1298c2ecf20Sopenharmony_ci} 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_cistatic void uas_scan_work(struct work_struct *work) 1328c2ecf20Sopenharmony_ci{ 1338c2ecf20Sopenharmony_ci struct uas_dev_info *devinfo = 1348c2ecf20Sopenharmony_ci container_of(work, struct uas_dev_info, scan_work); 1358c2ecf20Sopenharmony_ci struct Scsi_Host *shost = usb_get_intfdata(devinfo->intf); 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci dev_dbg(&devinfo->intf->dev, "starting scan\n"); 1388c2ecf20Sopenharmony_ci scsi_scan_host(shost); 1398c2ecf20Sopenharmony_ci dev_dbg(&devinfo->intf->dev, "scan complete\n"); 1408c2ecf20Sopenharmony_ci} 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_cistatic void uas_add_work(struct uas_cmd_info *cmdinfo) 1438c2ecf20Sopenharmony_ci{ 1448c2ecf20Sopenharmony_ci struct scsi_pointer *scp = (void *)cmdinfo; 1458c2ecf20Sopenharmony_ci struct scsi_cmnd *cmnd = container_of(scp, struct scsi_cmnd, SCp); 1468c2ecf20Sopenharmony_ci struct uas_dev_info *devinfo = cmnd->device->hostdata; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci lockdep_assert_held(&devinfo->lock); 1498c2ecf20Sopenharmony_ci cmdinfo->state |= IS_IN_WORK_LIST; 1508c2ecf20Sopenharmony_ci queue_work(workqueue, &devinfo->work); 1518c2ecf20Sopenharmony_ci} 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_cistatic void uas_zap_pending(struct uas_dev_info *devinfo, int result) 1548c2ecf20Sopenharmony_ci{ 1558c2ecf20Sopenharmony_ci struct uas_cmd_info *cmdinfo; 1568c2ecf20Sopenharmony_ci struct scsi_cmnd *cmnd; 1578c2ecf20Sopenharmony_ci unsigned long flags; 1588c2ecf20Sopenharmony_ci int i, err; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci spin_lock_irqsave(&devinfo->lock, flags); 1618c2ecf20Sopenharmony_ci for (i = 0; i < devinfo->qdepth; i++) { 1628c2ecf20Sopenharmony_ci if (!devinfo->cmnd[i]) 1638c2ecf20Sopenharmony_ci continue; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci cmnd = devinfo->cmnd[i]; 1668c2ecf20Sopenharmony_ci cmdinfo = (void *)&cmnd->SCp; 1678c2ecf20Sopenharmony_ci uas_log_cmd_state(cmnd, __func__, 0); 1688c2ecf20Sopenharmony_ci /* Sense urbs were killed, clear COMMAND_INFLIGHT manually */ 1698c2ecf20Sopenharmony_ci cmdinfo->state &= ~COMMAND_INFLIGHT; 1708c2ecf20Sopenharmony_ci cmnd->result = result << 16; 1718c2ecf20Sopenharmony_ci err = uas_try_complete(cmnd, __func__); 1728c2ecf20Sopenharmony_ci WARN_ON(err != 0); 1738c2ecf20Sopenharmony_ci } 1748c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&devinfo->lock, flags); 1758c2ecf20Sopenharmony_ci} 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_cistatic void uas_sense(struct urb *urb, struct scsi_cmnd *cmnd) 1788c2ecf20Sopenharmony_ci{ 1798c2ecf20Sopenharmony_ci struct sense_iu *sense_iu = urb->transfer_buffer; 1808c2ecf20Sopenharmony_ci struct scsi_device *sdev = cmnd->device; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci if (urb->actual_length > 16) { 1838c2ecf20Sopenharmony_ci unsigned len = be16_to_cpup(&sense_iu->len); 1848c2ecf20Sopenharmony_ci if (len + 16 != urb->actual_length) { 1858c2ecf20Sopenharmony_ci int newlen = min(len + 16, urb->actual_length) - 16; 1868c2ecf20Sopenharmony_ci if (newlen < 0) 1878c2ecf20Sopenharmony_ci newlen = 0; 1888c2ecf20Sopenharmony_ci sdev_printk(KERN_INFO, sdev, "%s: urb length %d " 1898c2ecf20Sopenharmony_ci "disagrees with IU sense data length %d, " 1908c2ecf20Sopenharmony_ci "using %d bytes of sense data\n", __func__, 1918c2ecf20Sopenharmony_ci urb->actual_length, len, newlen); 1928c2ecf20Sopenharmony_ci len = newlen; 1938c2ecf20Sopenharmony_ci } 1948c2ecf20Sopenharmony_ci memcpy(cmnd->sense_buffer, sense_iu->sense, len); 1958c2ecf20Sopenharmony_ci } 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci cmnd->result = sense_iu->status; 1988c2ecf20Sopenharmony_ci} 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_cistatic void uas_log_cmd_state(struct scsi_cmnd *cmnd, const char *prefix, 2018c2ecf20Sopenharmony_ci int status) 2028c2ecf20Sopenharmony_ci{ 2038c2ecf20Sopenharmony_ci struct uas_cmd_info *ci = (void *)&cmnd->SCp; 2048c2ecf20Sopenharmony_ci struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci if (status == -ENODEV) /* too late */ 2078c2ecf20Sopenharmony_ci return; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci scmd_printk(KERN_INFO, cmnd, 2108c2ecf20Sopenharmony_ci "%s %d uas-tag %d inflight:%s%s%s%s%s%s%s%s%s%s%s%s ", 2118c2ecf20Sopenharmony_ci prefix, status, cmdinfo->uas_tag, 2128c2ecf20Sopenharmony_ci (ci->state & SUBMIT_STATUS_URB) ? " s-st" : "", 2138c2ecf20Sopenharmony_ci (ci->state & ALLOC_DATA_IN_URB) ? " a-in" : "", 2148c2ecf20Sopenharmony_ci (ci->state & SUBMIT_DATA_IN_URB) ? " s-in" : "", 2158c2ecf20Sopenharmony_ci (ci->state & ALLOC_DATA_OUT_URB) ? " a-out" : "", 2168c2ecf20Sopenharmony_ci (ci->state & SUBMIT_DATA_OUT_URB) ? " s-out" : "", 2178c2ecf20Sopenharmony_ci (ci->state & ALLOC_CMD_URB) ? " a-cmd" : "", 2188c2ecf20Sopenharmony_ci (ci->state & SUBMIT_CMD_URB) ? " s-cmd" : "", 2198c2ecf20Sopenharmony_ci (ci->state & COMMAND_INFLIGHT) ? " CMD" : "", 2208c2ecf20Sopenharmony_ci (ci->state & DATA_IN_URB_INFLIGHT) ? " IN" : "", 2218c2ecf20Sopenharmony_ci (ci->state & DATA_OUT_URB_INFLIGHT) ? " OUT" : "", 2228c2ecf20Sopenharmony_ci (ci->state & COMMAND_ABORTED) ? " abort" : "", 2238c2ecf20Sopenharmony_ci (ci->state & IS_IN_WORK_LIST) ? " work" : ""); 2248c2ecf20Sopenharmony_ci scsi_print_command(cmnd); 2258c2ecf20Sopenharmony_ci} 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_cistatic void uas_free_unsubmitted_urbs(struct scsi_cmnd *cmnd) 2288c2ecf20Sopenharmony_ci{ 2298c2ecf20Sopenharmony_ci struct uas_cmd_info *cmdinfo; 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci if (!cmnd) 2328c2ecf20Sopenharmony_ci return; 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci cmdinfo = (void *)&cmnd->SCp; 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci if (cmdinfo->state & SUBMIT_CMD_URB) 2378c2ecf20Sopenharmony_ci usb_free_urb(cmdinfo->cmd_urb); 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci /* data urbs may have never gotten their submit flag set */ 2408c2ecf20Sopenharmony_ci if (!(cmdinfo->state & DATA_IN_URB_INFLIGHT)) 2418c2ecf20Sopenharmony_ci usb_free_urb(cmdinfo->data_in_urb); 2428c2ecf20Sopenharmony_ci if (!(cmdinfo->state & DATA_OUT_URB_INFLIGHT)) 2438c2ecf20Sopenharmony_ci usb_free_urb(cmdinfo->data_out_urb); 2448c2ecf20Sopenharmony_ci} 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_cistatic int uas_try_complete(struct scsi_cmnd *cmnd, const char *caller) 2478c2ecf20Sopenharmony_ci{ 2488c2ecf20Sopenharmony_ci struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp; 2498c2ecf20Sopenharmony_ci struct uas_dev_info *devinfo = (void *)cmnd->device->hostdata; 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci lockdep_assert_held(&devinfo->lock); 2528c2ecf20Sopenharmony_ci if (cmdinfo->state & (COMMAND_INFLIGHT | 2538c2ecf20Sopenharmony_ci DATA_IN_URB_INFLIGHT | 2548c2ecf20Sopenharmony_ci DATA_OUT_URB_INFLIGHT | 2558c2ecf20Sopenharmony_ci COMMAND_ABORTED)) 2568c2ecf20Sopenharmony_ci return -EBUSY; 2578c2ecf20Sopenharmony_ci devinfo->cmnd[cmdinfo->uas_tag - 1] = NULL; 2588c2ecf20Sopenharmony_ci uas_free_unsubmitted_urbs(cmnd); 2598c2ecf20Sopenharmony_ci cmnd->scsi_done(cmnd); 2608c2ecf20Sopenharmony_ci return 0; 2618c2ecf20Sopenharmony_ci} 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_cistatic void uas_xfer_data(struct urb *urb, struct scsi_cmnd *cmnd, 2648c2ecf20Sopenharmony_ci unsigned direction) 2658c2ecf20Sopenharmony_ci{ 2668c2ecf20Sopenharmony_ci struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp; 2678c2ecf20Sopenharmony_ci int err; 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci cmdinfo->state |= direction | SUBMIT_STATUS_URB; 2708c2ecf20Sopenharmony_ci err = uas_submit_urbs(cmnd, cmnd->device->hostdata); 2718c2ecf20Sopenharmony_ci if (err) { 2728c2ecf20Sopenharmony_ci uas_add_work(cmdinfo); 2738c2ecf20Sopenharmony_ci } 2748c2ecf20Sopenharmony_ci} 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_cistatic bool uas_evaluate_response_iu(struct response_iu *riu, struct scsi_cmnd *cmnd) 2778c2ecf20Sopenharmony_ci{ 2788c2ecf20Sopenharmony_ci u8 response_code = riu->response_code; 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci switch (response_code) { 2818c2ecf20Sopenharmony_ci case RC_INCORRECT_LUN: 2828c2ecf20Sopenharmony_ci set_host_byte(cmnd, DID_BAD_TARGET); 2838c2ecf20Sopenharmony_ci break; 2848c2ecf20Sopenharmony_ci case RC_TMF_SUCCEEDED: 2858c2ecf20Sopenharmony_ci set_host_byte(cmnd, DID_OK); 2868c2ecf20Sopenharmony_ci break; 2878c2ecf20Sopenharmony_ci case RC_TMF_NOT_SUPPORTED: 2888c2ecf20Sopenharmony_ci set_host_byte(cmnd, DID_TARGET_FAILURE); 2898c2ecf20Sopenharmony_ci break; 2908c2ecf20Sopenharmony_ci default: 2918c2ecf20Sopenharmony_ci uas_log_cmd_state(cmnd, "response iu", response_code); 2928c2ecf20Sopenharmony_ci set_host_byte(cmnd, DID_ERROR); 2938c2ecf20Sopenharmony_ci break; 2948c2ecf20Sopenharmony_ci } 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci return response_code == RC_TMF_SUCCEEDED; 2978c2ecf20Sopenharmony_ci} 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_cistatic void uas_stat_cmplt(struct urb *urb) 3008c2ecf20Sopenharmony_ci{ 3018c2ecf20Sopenharmony_ci struct iu *iu = urb->transfer_buffer; 3028c2ecf20Sopenharmony_ci struct Scsi_Host *shost = urb->context; 3038c2ecf20Sopenharmony_ci struct uas_dev_info *devinfo = (struct uas_dev_info *)shost->hostdata; 3048c2ecf20Sopenharmony_ci struct urb *data_in_urb = NULL; 3058c2ecf20Sopenharmony_ci struct urb *data_out_urb = NULL; 3068c2ecf20Sopenharmony_ci struct scsi_cmnd *cmnd; 3078c2ecf20Sopenharmony_ci struct uas_cmd_info *cmdinfo; 3088c2ecf20Sopenharmony_ci unsigned long flags; 3098c2ecf20Sopenharmony_ci unsigned int idx; 3108c2ecf20Sopenharmony_ci int status = urb->status; 3118c2ecf20Sopenharmony_ci bool success; 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci spin_lock_irqsave(&devinfo->lock, flags); 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci if (devinfo->resetting) 3168c2ecf20Sopenharmony_ci goto out; 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci if (status) { 3198c2ecf20Sopenharmony_ci if (status != -ENOENT && status != -ECONNRESET && status != -ESHUTDOWN) 3208c2ecf20Sopenharmony_ci dev_err(&urb->dev->dev, "stat urb: status %d\n", status); 3218c2ecf20Sopenharmony_ci goto out; 3228c2ecf20Sopenharmony_ci } 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci idx = be16_to_cpup(&iu->tag) - 1; 3258c2ecf20Sopenharmony_ci if (idx >= MAX_CMNDS || !devinfo->cmnd[idx]) { 3268c2ecf20Sopenharmony_ci dev_err(&urb->dev->dev, 3278c2ecf20Sopenharmony_ci "stat urb: no pending cmd for uas-tag %d\n", idx + 1); 3288c2ecf20Sopenharmony_ci goto out; 3298c2ecf20Sopenharmony_ci } 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci cmnd = devinfo->cmnd[idx]; 3328c2ecf20Sopenharmony_ci cmdinfo = (void *)&cmnd->SCp; 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci if (!(cmdinfo->state & COMMAND_INFLIGHT)) { 3358c2ecf20Sopenharmony_ci uas_log_cmd_state(cmnd, "unexpected status cmplt", 0); 3368c2ecf20Sopenharmony_ci goto out; 3378c2ecf20Sopenharmony_ci } 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci switch (iu->iu_id) { 3408c2ecf20Sopenharmony_ci case IU_ID_STATUS: 3418c2ecf20Sopenharmony_ci uas_sense(urb, cmnd); 3428c2ecf20Sopenharmony_ci if (cmnd->result != 0) { 3438c2ecf20Sopenharmony_ci /* cancel data transfers on error */ 3448c2ecf20Sopenharmony_ci data_in_urb = usb_get_urb(cmdinfo->data_in_urb); 3458c2ecf20Sopenharmony_ci data_out_urb = usb_get_urb(cmdinfo->data_out_urb); 3468c2ecf20Sopenharmony_ci } 3478c2ecf20Sopenharmony_ci cmdinfo->state &= ~COMMAND_INFLIGHT; 3488c2ecf20Sopenharmony_ci uas_try_complete(cmnd, __func__); 3498c2ecf20Sopenharmony_ci break; 3508c2ecf20Sopenharmony_ci case IU_ID_READ_READY: 3518c2ecf20Sopenharmony_ci if (!cmdinfo->data_in_urb || 3528c2ecf20Sopenharmony_ci (cmdinfo->state & DATA_IN_URB_INFLIGHT)) { 3538c2ecf20Sopenharmony_ci uas_log_cmd_state(cmnd, "unexpected read rdy", 0); 3548c2ecf20Sopenharmony_ci break; 3558c2ecf20Sopenharmony_ci } 3568c2ecf20Sopenharmony_ci uas_xfer_data(urb, cmnd, SUBMIT_DATA_IN_URB); 3578c2ecf20Sopenharmony_ci break; 3588c2ecf20Sopenharmony_ci case IU_ID_WRITE_READY: 3598c2ecf20Sopenharmony_ci if (!cmdinfo->data_out_urb || 3608c2ecf20Sopenharmony_ci (cmdinfo->state & DATA_OUT_URB_INFLIGHT)) { 3618c2ecf20Sopenharmony_ci uas_log_cmd_state(cmnd, "unexpected write rdy", 0); 3628c2ecf20Sopenharmony_ci break; 3638c2ecf20Sopenharmony_ci } 3648c2ecf20Sopenharmony_ci uas_xfer_data(urb, cmnd, SUBMIT_DATA_OUT_URB); 3658c2ecf20Sopenharmony_ci break; 3668c2ecf20Sopenharmony_ci case IU_ID_RESPONSE: 3678c2ecf20Sopenharmony_ci cmdinfo->state &= ~COMMAND_INFLIGHT; 3688c2ecf20Sopenharmony_ci success = uas_evaluate_response_iu((struct response_iu *)iu, cmnd); 3698c2ecf20Sopenharmony_ci if (!success) { 3708c2ecf20Sopenharmony_ci /* Error, cancel data transfers */ 3718c2ecf20Sopenharmony_ci data_in_urb = usb_get_urb(cmdinfo->data_in_urb); 3728c2ecf20Sopenharmony_ci data_out_urb = usb_get_urb(cmdinfo->data_out_urb); 3738c2ecf20Sopenharmony_ci } 3748c2ecf20Sopenharmony_ci uas_try_complete(cmnd, __func__); 3758c2ecf20Sopenharmony_ci break; 3768c2ecf20Sopenharmony_ci default: 3778c2ecf20Sopenharmony_ci uas_log_cmd_state(cmnd, "bogus IU", iu->iu_id); 3788c2ecf20Sopenharmony_ci } 3798c2ecf20Sopenharmony_ciout: 3808c2ecf20Sopenharmony_ci usb_free_urb(urb); 3818c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&devinfo->lock, flags); 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci /* Unlinking of data urbs must be done without holding the lock */ 3848c2ecf20Sopenharmony_ci if (data_in_urb) { 3858c2ecf20Sopenharmony_ci usb_unlink_urb(data_in_urb); 3868c2ecf20Sopenharmony_ci usb_put_urb(data_in_urb); 3878c2ecf20Sopenharmony_ci } 3888c2ecf20Sopenharmony_ci if (data_out_urb) { 3898c2ecf20Sopenharmony_ci usb_unlink_urb(data_out_urb); 3908c2ecf20Sopenharmony_ci usb_put_urb(data_out_urb); 3918c2ecf20Sopenharmony_ci } 3928c2ecf20Sopenharmony_ci} 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_cistatic void uas_data_cmplt(struct urb *urb) 3958c2ecf20Sopenharmony_ci{ 3968c2ecf20Sopenharmony_ci struct scsi_cmnd *cmnd = urb->context; 3978c2ecf20Sopenharmony_ci struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp; 3988c2ecf20Sopenharmony_ci struct uas_dev_info *devinfo = (void *)cmnd->device->hostdata; 3998c2ecf20Sopenharmony_ci struct scsi_data_buffer *sdb = &cmnd->sdb; 4008c2ecf20Sopenharmony_ci unsigned long flags; 4018c2ecf20Sopenharmony_ci int status = urb->status; 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci spin_lock_irqsave(&devinfo->lock, flags); 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci if (cmdinfo->data_in_urb == urb) { 4068c2ecf20Sopenharmony_ci cmdinfo->state &= ~DATA_IN_URB_INFLIGHT; 4078c2ecf20Sopenharmony_ci cmdinfo->data_in_urb = NULL; 4088c2ecf20Sopenharmony_ci } else if (cmdinfo->data_out_urb == urb) { 4098c2ecf20Sopenharmony_ci cmdinfo->state &= ~DATA_OUT_URB_INFLIGHT; 4108c2ecf20Sopenharmony_ci cmdinfo->data_out_urb = NULL; 4118c2ecf20Sopenharmony_ci } 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci if (devinfo->resetting) 4148c2ecf20Sopenharmony_ci goto out; 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci /* Data urbs should not complete before the cmd urb is submitted */ 4178c2ecf20Sopenharmony_ci if (cmdinfo->state & SUBMIT_CMD_URB) { 4188c2ecf20Sopenharmony_ci uas_log_cmd_state(cmnd, "unexpected data cmplt", 0); 4198c2ecf20Sopenharmony_ci goto out; 4208c2ecf20Sopenharmony_ci } 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci if (status) { 4238c2ecf20Sopenharmony_ci if (status != -ENOENT && status != -ECONNRESET && status != -ESHUTDOWN) 4248c2ecf20Sopenharmony_ci uas_log_cmd_state(cmnd, "data cmplt err", status); 4258c2ecf20Sopenharmony_ci /* error: no data transfered */ 4268c2ecf20Sopenharmony_ci scsi_set_resid(cmnd, sdb->length); 4278c2ecf20Sopenharmony_ci } else { 4288c2ecf20Sopenharmony_ci scsi_set_resid(cmnd, sdb->length - urb->actual_length); 4298c2ecf20Sopenharmony_ci } 4308c2ecf20Sopenharmony_ci uas_try_complete(cmnd, __func__); 4318c2ecf20Sopenharmony_ciout: 4328c2ecf20Sopenharmony_ci usb_free_urb(urb); 4338c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&devinfo->lock, flags); 4348c2ecf20Sopenharmony_ci} 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_cistatic void uas_cmd_cmplt(struct urb *urb) 4378c2ecf20Sopenharmony_ci{ 4388c2ecf20Sopenharmony_ci if (urb->status) 4398c2ecf20Sopenharmony_ci dev_err(&urb->dev->dev, "cmd cmplt err %d\n", urb->status); 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci usb_free_urb(urb); 4428c2ecf20Sopenharmony_ci} 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_cistatic struct urb *uas_alloc_data_urb(struct uas_dev_info *devinfo, gfp_t gfp, 4458c2ecf20Sopenharmony_ci struct scsi_cmnd *cmnd, 4468c2ecf20Sopenharmony_ci enum dma_data_direction dir) 4478c2ecf20Sopenharmony_ci{ 4488c2ecf20Sopenharmony_ci struct usb_device *udev = devinfo->udev; 4498c2ecf20Sopenharmony_ci struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp; 4508c2ecf20Sopenharmony_ci struct urb *urb = usb_alloc_urb(0, gfp); 4518c2ecf20Sopenharmony_ci struct scsi_data_buffer *sdb = &cmnd->sdb; 4528c2ecf20Sopenharmony_ci unsigned int pipe = (dir == DMA_FROM_DEVICE) 4538c2ecf20Sopenharmony_ci ? devinfo->data_in_pipe : devinfo->data_out_pipe; 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci if (!urb) 4568c2ecf20Sopenharmony_ci goto out; 4578c2ecf20Sopenharmony_ci usb_fill_bulk_urb(urb, udev, pipe, NULL, sdb->length, 4588c2ecf20Sopenharmony_ci uas_data_cmplt, cmnd); 4598c2ecf20Sopenharmony_ci if (devinfo->use_streams) 4608c2ecf20Sopenharmony_ci urb->stream_id = cmdinfo->uas_tag; 4618c2ecf20Sopenharmony_ci urb->num_sgs = udev->bus->sg_tablesize ? sdb->table.nents : 0; 4628c2ecf20Sopenharmony_ci urb->sg = sdb->table.sgl; 4638c2ecf20Sopenharmony_ci out: 4648c2ecf20Sopenharmony_ci return urb; 4658c2ecf20Sopenharmony_ci} 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_cistatic struct urb *uas_alloc_sense_urb(struct uas_dev_info *devinfo, gfp_t gfp, 4688c2ecf20Sopenharmony_ci struct scsi_cmnd *cmnd) 4698c2ecf20Sopenharmony_ci{ 4708c2ecf20Sopenharmony_ci struct usb_device *udev = devinfo->udev; 4718c2ecf20Sopenharmony_ci struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp; 4728c2ecf20Sopenharmony_ci struct urb *urb = usb_alloc_urb(0, gfp); 4738c2ecf20Sopenharmony_ci struct sense_iu *iu; 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci if (!urb) 4768c2ecf20Sopenharmony_ci goto out; 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci iu = kzalloc(sizeof(*iu), gfp); 4798c2ecf20Sopenharmony_ci if (!iu) 4808c2ecf20Sopenharmony_ci goto free; 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci usb_fill_bulk_urb(urb, udev, devinfo->status_pipe, iu, sizeof(*iu), 4838c2ecf20Sopenharmony_ci uas_stat_cmplt, cmnd->device->host); 4848c2ecf20Sopenharmony_ci if (devinfo->use_streams) 4858c2ecf20Sopenharmony_ci urb->stream_id = cmdinfo->uas_tag; 4868c2ecf20Sopenharmony_ci urb->transfer_flags |= URB_FREE_BUFFER; 4878c2ecf20Sopenharmony_ci out: 4888c2ecf20Sopenharmony_ci return urb; 4898c2ecf20Sopenharmony_ci free: 4908c2ecf20Sopenharmony_ci usb_free_urb(urb); 4918c2ecf20Sopenharmony_ci return NULL; 4928c2ecf20Sopenharmony_ci} 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_cistatic struct urb *uas_alloc_cmd_urb(struct uas_dev_info *devinfo, gfp_t gfp, 4958c2ecf20Sopenharmony_ci struct scsi_cmnd *cmnd) 4968c2ecf20Sopenharmony_ci{ 4978c2ecf20Sopenharmony_ci struct usb_device *udev = devinfo->udev; 4988c2ecf20Sopenharmony_ci struct scsi_device *sdev = cmnd->device; 4998c2ecf20Sopenharmony_ci struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp; 5008c2ecf20Sopenharmony_ci struct urb *urb = usb_alloc_urb(0, gfp); 5018c2ecf20Sopenharmony_ci struct command_iu *iu; 5028c2ecf20Sopenharmony_ci int len; 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci if (!urb) 5058c2ecf20Sopenharmony_ci goto out; 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci len = cmnd->cmd_len - 16; 5088c2ecf20Sopenharmony_ci if (len < 0) 5098c2ecf20Sopenharmony_ci len = 0; 5108c2ecf20Sopenharmony_ci len = ALIGN(len, 4); 5118c2ecf20Sopenharmony_ci iu = kzalloc(sizeof(*iu) + len, gfp); 5128c2ecf20Sopenharmony_ci if (!iu) 5138c2ecf20Sopenharmony_ci goto free; 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci iu->iu_id = IU_ID_COMMAND; 5168c2ecf20Sopenharmony_ci iu->tag = cpu_to_be16(cmdinfo->uas_tag); 5178c2ecf20Sopenharmony_ci iu->prio_attr = UAS_SIMPLE_TAG; 5188c2ecf20Sopenharmony_ci iu->len = len; 5198c2ecf20Sopenharmony_ci int_to_scsilun(sdev->lun, &iu->lun); 5208c2ecf20Sopenharmony_ci memcpy(iu->cdb, cmnd->cmnd, cmnd->cmd_len); 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci usb_fill_bulk_urb(urb, udev, devinfo->cmd_pipe, iu, sizeof(*iu) + len, 5238c2ecf20Sopenharmony_ci uas_cmd_cmplt, NULL); 5248c2ecf20Sopenharmony_ci urb->transfer_flags |= URB_FREE_BUFFER; 5258c2ecf20Sopenharmony_ci out: 5268c2ecf20Sopenharmony_ci return urb; 5278c2ecf20Sopenharmony_ci free: 5288c2ecf20Sopenharmony_ci usb_free_urb(urb); 5298c2ecf20Sopenharmony_ci return NULL; 5308c2ecf20Sopenharmony_ci} 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci/* 5338c2ecf20Sopenharmony_ci * Why should I request the Status IU before sending the Command IU? Spec 5348c2ecf20Sopenharmony_ci * says to, but also says the device may receive them in any order. Seems 5358c2ecf20Sopenharmony_ci * daft to me. 5368c2ecf20Sopenharmony_ci */ 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_cistatic struct urb *uas_submit_sense_urb(struct scsi_cmnd *cmnd, gfp_t gfp) 5398c2ecf20Sopenharmony_ci{ 5408c2ecf20Sopenharmony_ci struct uas_dev_info *devinfo = cmnd->device->hostdata; 5418c2ecf20Sopenharmony_ci struct urb *urb; 5428c2ecf20Sopenharmony_ci int err; 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci urb = uas_alloc_sense_urb(devinfo, gfp, cmnd); 5458c2ecf20Sopenharmony_ci if (!urb) 5468c2ecf20Sopenharmony_ci return NULL; 5478c2ecf20Sopenharmony_ci usb_anchor_urb(urb, &devinfo->sense_urbs); 5488c2ecf20Sopenharmony_ci err = usb_submit_urb(urb, gfp); 5498c2ecf20Sopenharmony_ci if (err) { 5508c2ecf20Sopenharmony_ci usb_unanchor_urb(urb); 5518c2ecf20Sopenharmony_ci uas_log_cmd_state(cmnd, "sense submit err", err); 5528c2ecf20Sopenharmony_ci usb_free_urb(urb); 5538c2ecf20Sopenharmony_ci return NULL; 5548c2ecf20Sopenharmony_ci } 5558c2ecf20Sopenharmony_ci return urb; 5568c2ecf20Sopenharmony_ci} 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_cistatic int uas_submit_urbs(struct scsi_cmnd *cmnd, 5598c2ecf20Sopenharmony_ci struct uas_dev_info *devinfo) 5608c2ecf20Sopenharmony_ci{ 5618c2ecf20Sopenharmony_ci struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp; 5628c2ecf20Sopenharmony_ci struct urb *urb; 5638c2ecf20Sopenharmony_ci int err; 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci lockdep_assert_held(&devinfo->lock); 5668c2ecf20Sopenharmony_ci if (cmdinfo->state & SUBMIT_STATUS_URB) { 5678c2ecf20Sopenharmony_ci urb = uas_submit_sense_urb(cmnd, GFP_ATOMIC); 5688c2ecf20Sopenharmony_ci if (!urb) 5698c2ecf20Sopenharmony_ci return SCSI_MLQUEUE_DEVICE_BUSY; 5708c2ecf20Sopenharmony_ci cmdinfo->state &= ~SUBMIT_STATUS_URB; 5718c2ecf20Sopenharmony_ci } 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ci if (cmdinfo->state & ALLOC_DATA_IN_URB) { 5748c2ecf20Sopenharmony_ci cmdinfo->data_in_urb = uas_alloc_data_urb(devinfo, GFP_ATOMIC, 5758c2ecf20Sopenharmony_ci cmnd, DMA_FROM_DEVICE); 5768c2ecf20Sopenharmony_ci if (!cmdinfo->data_in_urb) 5778c2ecf20Sopenharmony_ci return SCSI_MLQUEUE_DEVICE_BUSY; 5788c2ecf20Sopenharmony_ci cmdinfo->state &= ~ALLOC_DATA_IN_URB; 5798c2ecf20Sopenharmony_ci } 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ci if (cmdinfo->state & SUBMIT_DATA_IN_URB) { 5828c2ecf20Sopenharmony_ci usb_anchor_urb(cmdinfo->data_in_urb, &devinfo->data_urbs); 5838c2ecf20Sopenharmony_ci err = usb_submit_urb(cmdinfo->data_in_urb, GFP_ATOMIC); 5848c2ecf20Sopenharmony_ci if (err) { 5858c2ecf20Sopenharmony_ci usb_unanchor_urb(cmdinfo->data_in_urb); 5868c2ecf20Sopenharmony_ci uas_log_cmd_state(cmnd, "data in submit err", err); 5878c2ecf20Sopenharmony_ci return SCSI_MLQUEUE_DEVICE_BUSY; 5888c2ecf20Sopenharmony_ci } 5898c2ecf20Sopenharmony_ci cmdinfo->state &= ~SUBMIT_DATA_IN_URB; 5908c2ecf20Sopenharmony_ci cmdinfo->state |= DATA_IN_URB_INFLIGHT; 5918c2ecf20Sopenharmony_ci } 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ci if (cmdinfo->state & ALLOC_DATA_OUT_URB) { 5948c2ecf20Sopenharmony_ci cmdinfo->data_out_urb = uas_alloc_data_urb(devinfo, GFP_ATOMIC, 5958c2ecf20Sopenharmony_ci cmnd, DMA_TO_DEVICE); 5968c2ecf20Sopenharmony_ci if (!cmdinfo->data_out_urb) 5978c2ecf20Sopenharmony_ci return SCSI_MLQUEUE_DEVICE_BUSY; 5988c2ecf20Sopenharmony_ci cmdinfo->state &= ~ALLOC_DATA_OUT_URB; 5998c2ecf20Sopenharmony_ci } 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_ci if (cmdinfo->state & SUBMIT_DATA_OUT_URB) { 6028c2ecf20Sopenharmony_ci usb_anchor_urb(cmdinfo->data_out_urb, &devinfo->data_urbs); 6038c2ecf20Sopenharmony_ci err = usb_submit_urb(cmdinfo->data_out_urb, GFP_ATOMIC); 6048c2ecf20Sopenharmony_ci if (err) { 6058c2ecf20Sopenharmony_ci usb_unanchor_urb(cmdinfo->data_out_urb); 6068c2ecf20Sopenharmony_ci uas_log_cmd_state(cmnd, "data out submit err", err); 6078c2ecf20Sopenharmony_ci return SCSI_MLQUEUE_DEVICE_BUSY; 6088c2ecf20Sopenharmony_ci } 6098c2ecf20Sopenharmony_ci cmdinfo->state &= ~SUBMIT_DATA_OUT_URB; 6108c2ecf20Sopenharmony_ci cmdinfo->state |= DATA_OUT_URB_INFLIGHT; 6118c2ecf20Sopenharmony_ci } 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_ci if (cmdinfo->state & ALLOC_CMD_URB) { 6148c2ecf20Sopenharmony_ci cmdinfo->cmd_urb = uas_alloc_cmd_urb(devinfo, GFP_ATOMIC, cmnd); 6158c2ecf20Sopenharmony_ci if (!cmdinfo->cmd_urb) 6168c2ecf20Sopenharmony_ci return SCSI_MLQUEUE_DEVICE_BUSY; 6178c2ecf20Sopenharmony_ci cmdinfo->state &= ~ALLOC_CMD_URB; 6188c2ecf20Sopenharmony_ci } 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_ci if (cmdinfo->state & SUBMIT_CMD_URB) { 6218c2ecf20Sopenharmony_ci usb_anchor_urb(cmdinfo->cmd_urb, &devinfo->cmd_urbs); 6228c2ecf20Sopenharmony_ci err = usb_submit_urb(cmdinfo->cmd_urb, GFP_ATOMIC); 6238c2ecf20Sopenharmony_ci if (err) { 6248c2ecf20Sopenharmony_ci usb_unanchor_urb(cmdinfo->cmd_urb); 6258c2ecf20Sopenharmony_ci uas_log_cmd_state(cmnd, "cmd submit err", err); 6268c2ecf20Sopenharmony_ci return SCSI_MLQUEUE_DEVICE_BUSY; 6278c2ecf20Sopenharmony_ci } 6288c2ecf20Sopenharmony_ci cmdinfo->cmd_urb = NULL; 6298c2ecf20Sopenharmony_ci cmdinfo->state &= ~SUBMIT_CMD_URB; 6308c2ecf20Sopenharmony_ci cmdinfo->state |= COMMAND_INFLIGHT; 6318c2ecf20Sopenharmony_ci } 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_ci return 0; 6348c2ecf20Sopenharmony_ci} 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_cistatic int uas_queuecommand_lck(struct scsi_cmnd *cmnd, 6378c2ecf20Sopenharmony_ci void (*done)(struct scsi_cmnd *)) 6388c2ecf20Sopenharmony_ci{ 6398c2ecf20Sopenharmony_ci struct scsi_device *sdev = cmnd->device; 6408c2ecf20Sopenharmony_ci struct uas_dev_info *devinfo = sdev->hostdata; 6418c2ecf20Sopenharmony_ci struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp; 6428c2ecf20Sopenharmony_ci unsigned long flags; 6438c2ecf20Sopenharmony_ci int idx, err; 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_ci BUILD_BUG_ON(sizeof(struct uas_cmd_info) > sizeof(struct scsi_pointer)); 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_ci /* Re-check scsi_block_requests now that we've the host-lock */ 6488c2ecf20Sopenharmony_ci if (cmnd->device->host->host_self_blocked) 6498c2ecf20Sopenharmony_ci return SCSI_MLQUEUE_DEVICE_BUSY; 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_ci if ((devinfo->flags & US_FL_NO_ATA_1X) && 6528c2ecf20Sopenharmony_ci (cmnd->cmnd[0] == ATA_12 || cmnd->cmnd[0] == ATA_16)) { 6538c2ecf20Sopenharmony_ci memcpy(cmnd->sense_buffer, usb_stor_sense_invalidCDB, 6548c2ecf20Sopenharmony_ci sizeof(usb_stor_sense_invalidCDB)); 6558c2ecf20Sopenharmony_ci cmnd->result = SAM_STAT_CHECK_CONDITION; 6568c2ecf20Sopenharmony_ci cmnd->scsi_done(cmnd); 6578c2ecf20Sopenharmony_ci return 0; 6588c2ecf20Sopenharmony_ci } 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_ci spin_lock_irqsave(&devinfo->lock, flags); 6618c2ecf20Sopenharmony_ci 6628c2ecf20Sopenharmony_ci if (devinfo->resetting) { 6638c2ecf20Sopenharmony_ci set_host_byte(cmnd, DID_ERROR); 6648c2ecf20Sopenharmony_ci cmnd->scsi_done(cmnd); 6658c2ecf20Sopenharmony_ci goto zombie; 6668c2ecf20Sopenharmony_ci } 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_ci /* Find a free uas-tag */ 6698c2ecf20Sopenharmony_ci for (idx = 0; idx < devinfo->qdepth; idx++) { 6708c2ecf20Sopenharmony_ci if (!devinfo->cmnd[idx]) 6718c2ecf20Sopenharmony_ci break; 6728c2ecf20Sopenharmony_ci } 6738c2ecf20Sopenharmony_ci if (idx == devinfo->qdepth) { 6748c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&devinfo->lock, flags); 6758c2ecf20Sopenharmony_ci return SCSI_MLQUEUE_DEVICE_BUSY; 6768c2ecf20Sopenharmony_ci } 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_ci cmnd->scsi_done = done; 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_ci memset(cmdinfo, 0, sizeof(*cmdinfo)); 6818c2ecf20Sopenharmony_ci cmdinfo->uas_tag = idx + 1; /* uas-tag == usb-stream-id, so 1 based */ 6828c2ecf20Sopenharmony_ci cmdinfo->state = SUBMIT_STATUS_URB | ALLOC_CMD_URB | SUBMIT_CMD_URB; 6838c2ecf20Sopenharmony_ci 6848c2ecf20Sopenharmony_ci switch (cmnd->sc_data_direction) { 6858c2ecf20Sopenharmony_ci case DMA_FROM_DEVICE: 6868c2ecf20Sopenharmony_ci cmdinfo->state |= ALLOC_DATA_IN_URB | SUBMIT_DATA_IN_URB; 6878c2ecf20Sopenharmony_ci break; 6888c2ecf20Sopenharmony_ci case DMA_BIDIRECTIONAL: 6898c2ecf20Sopenharmony_ci cmdinfo->state |= ALLOC_DATA_IN_URB | SUBMIT_DATA_IN_URB; 6908c2ecf20Sopenharmony_ci fallthrough; 6918c2ecf20Sopenharmony_ci case DMA_TO_DEVICE: 6928c2ecf20Sopenharmony_ci cmdinfo->state |= ALLOC_DATA_OUT_URB | SUBMIT_DATA_OUT_URB; 6938c2ecf20Sopenharmony_ci case DMA_NONE: 6948c2ecf20Sopenharmony_ci break; 6958c2ecf20Sopenharmony_ci } 6968c2ecf20Sopenharmony_ci 6978c2ecf20Sopenharmony_ci if (!devinfo->use_streams) 6988c2ecf20Sopenharmony_ci cmdinfo->state &= ~(SUBMIT_DATA_IN_URB | SUBMIT_DATA_OUT_URB); 6998c2ecf20Sopenharmony_ci 7008c2ecf20Sopenharmony_ci err = uas_submit_urbs(cmnd, devinfo); 7018c2ecf20Sopenharmony_ci /* 7028c2ecf20Sopenharmony_ci * in case of fatal errors the SCSI layer is peculiar 7038c2ecf20Sopenharmony_ci * a command that has finished is a success for the purpose 7048c2ecf20Sopenharmony_ci * of queueing, no matter how fatal the error 7058c2ecf20Sopenharmony_ci */ 7068c2ecf20Sopenharmony_ci if (err == -ENODEV) { 7078c2ecf20Sopenharmony_ci set_host_byte(cmnd, DID_ERROR); 7088c2ecf20Sopenharmony_ci cmnd->scsi_done(cmnd); 7098c2ecf20Sopenharmony_ci goto zombie; 7108c2ecf20Sopenharmony_ci } 7118c2ecf20Sopenharmony_ci if (err) { 7128c2ecf20Sopenharmony_ci /* If we did nothing, give up now */ 7138c2ecf20Sopenharmony_ci if (cmdinfo->state & SUBMIT_STATUS_URB) { 7148c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&devinfo->lock, flags); 7158c2ecf20Sopenharmony_ci return SCSI_MLQUEUE_DEVICE_BUSY; 7168c2ecf20Sopenharmony_ci } 7178c2ecf20Sopenharmony_ci uas_add_work(cmdinfo); 7188c2ecf20Sopenharmony_ci } 7198c2ecf20Sopenharmony_ci 7208c2ecf20Sopenharmony_ci devinfo->cmnd[idx] = cmnd; 7218c2ecf20Sopenharmony_cizombie: 7228c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&devinfo->lock, flags); 7238c2ecf20Sopenharmony_ci return 0; 7248c2ecf20Sopenharmony_ci} 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_cistatic DEF_SCSI_QCMD(uas_queuecommand) 7278c2ecf20Sopenharmony_ci 7288c2ecf20Sopenharmony_ci/* 7298c2ecf20Sopenharmony_ci * For now we do not support actually sending an abort to the device, so 7308c2ecf20Sopenharmony_ci * this eh always fails. Still we must define it to make sure that we've 7318c2ecf20Sopenharmony_ci * dropped all references to the cmnd in question once this function exits. 7328c2ecf20Sopenharmony_ci */ 7338c2ecf20Sopenharmony_cistatic int uas_eh_abort_handler(struct scsi_cmnd *cmnd) 7348c2ecf20Sopenharmony_ci{ 7358c2ecf20Sopenharmony_ci struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp; 7368c2ecf20Sopenharmony_ci struct uas_dev_info *devinfo = (void *)cmnd->device->hostdata; 7378c2ecf20Sopenharmony_ci struct urb *data_in_urb = NULL; 7388c2ecf20Sopenharmony_ci struct urb *data_out_urb = NULL; 7398c2ecf20Sopenharmony_ci unsigned long flags; 7408c2ecf20Sopenharmony_ci 7418c2ecf20Sopenharmony_ci spin_lock_irqsave(&devinfo->lock, flags); 7428c2ecf20Sopenharmony_ci 7438c2ecf20Sopenharmony_ci uas_log_cmd_state(cmnd, __func__, 0); 7448c2ecf20Sopenharmony_ci 7458c2ecf20Sopenharmony_ci /* Ensure that try_complete does not call scsi_done */ 7468c2ecf20Sopenharmony_ci cmdinfo->state |= COMMAND_ABORTED; 7478c2ecf20Sopenharmony_ci 7488c2ecf20Sopenharmony_ci /* Drop all refs to this cmnd, kill data urbs to break their ref */ 7498c2ecf20Sopenharmony_ci devinfo->cmnd[cmdinfo->uas_tag - 1] = NULL; 7508c2ecf20Sopenharmony_ci if (cmdinfo->state & DATA_IN_URB_INFLIGHT) 7518c2ecf20Sopenharmony_ci data_in_urb = usb_get_urb(cmdinfo->data_in_urb); 7528c2ecf20Sopenharmony_ci if (cmdinfo->state & DATA_OUT_URB_INFLIGHT) 7538c2ecf20Sopenharmony_ci data_out_urb = usb_get_urb(cmdinfo->data_out_urb); 7548c2ecf20Sopenharmony_ci 7558c2ecf20Sopenharmony_ci uas_free_unsubmitted_urbs(cmnd); 7568c2ecf20Sopenharmony_ci 7578c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&devinfo->lock, flags); 7588c2ecf20Sopenharmony_ci 7598c2ecf20Sopenharmony_ci if (data_in_urb) { 7608c2ecf20Sopenharmony_ci usb_kill_urb(data_in_urb); 7618c2ecf20Sopenharmony_ci usb_put_urb(data_in_urb); 7628c2ecf20Sopenharmony_ci } 7638c2ecf20Sopenharmony_ci if (data_out_urb) { 7648c2ecf20Sopenharmony_ci usb_kill_urb(data_out_urb); 7658c2ecf20Sopenharmony_ci usb_put_urb(data_out_urb); 7668c2ecf20Sopenharmony_ci } 7678c2ecf20Sopenharmony_ci 7688c2ecf20Sopenharmony_ci return FAILED; 7698c2ecf20Sopenharmony_ci} 7708c2ecf20Sopenharmony_ci 7718c2ecf20Sopenharmony_cistatic int uas_eh_device_reset_handler(struct scsi_cmnd *cmnd) 7728c2ecf20Sopenharmony_ci{ 7738c2ecf20Sopenharmony_ci struct scsi_device *sdev = cmnd->device; 7748c2ecf20Sopenharmony_ci struct uas_dev_info *devinfo = sdev->hostdata; 7758c2ecf20Sopenharmony_ci struct usb_device *udev = devinfo->udev; 7768c2ecf20Sopenharmony_ci unsigned long flags; 7778c2ecf20Sopenharmony_ci int err; 7788c2ecf20Sopenharmony_ci 7798c2ecf20Sopenharmony_ci err = usb_lock_device_for_reset(udev, devinfo->intf); 7808c2ecf20Sopenharmony_ci if (err) { 7818c2ecf20Sopenharmony_ci shost_printk(KERN_ERR, sdev->host, 7828c2ecf20Sopenharmony_ci "%s FAILED to get lock err %d\n", __func__, err); 7838c2ecf20Sopenharmony_ci return FAILED; 7848c2ecf20Sopenharmony_ci } 7858c2ecf20Sopenharmony_ci 7868c2ecf20Sopenharmony_ci shost_printk(KERN_INFO, sdev->host, "%s start\n", __func__); 7878c2ecf20Sopenharmony_ci 7888c2ecf20Sopenharmony_ci spin_lock_irqsave(&devinfo->lock, flags); 7898c2ecf20Sopenharmony_ci devinfo->resetting = 1; 7908c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&devinfo->lock, flags); 7918c2ecf20Sopenharmony_ci 7928c2ecf20Sopenharmony_ci usb_kill_anchored_urbs(&devinfo->cmd_urbs); 7938c2ecf20Sopenharmony_ci usb_kill_anchored_urbs(&devinfo->sense_urbs); 7948c2ecf20Sopenharmony_ci usb_kill_anchored_urbs(&devinfo->data_urbs); 7958c2ecf20Sopenharmony_ci uas_zap_pending(devinfo, DID_RESET); 7968c2ecf20Sopenharmony_ci 7978c2ecf20Sopenharmony_ci err = usb_reset_device(udev); 7988c2ecf20Sopenharmony_ci 7998c2ecf20Sopenharmony_ci spin_lock_irqsave(&devinfo->lock, flags); 8008c2ecf20Sopenharmony_ci devinfo->resetting = 0; 8018c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&devinfo->lock, flags); 8028c2ecf20Sopenharmony_ci 8038c2ecf20Sopenharmony_ci usb_unlock_device(udev); 8048c2ecf20Sopenharmony_ci 8058c2ecf20Sopenharmony_ci if (err) { 8068c2ecf20Sopenharmony_ci shost_printk(KERN_INFO, sdev->host, "%s FAILED err %d\n", 8078c2ecf20Sopenharmony_ci __func__, err); 8088c2ecf20Sopenharmony_ci return FAILED; 8098c2ecf20Sopenharmony_ci } 8108c2ecf20Sopenharmony_ci 8118c2ecf20Sopenharmony_ci shost_printk(KERN_INFO, sdev->host, "%s success\n", __func__); 8128c2ecf20Sopenharmony_ci return SUCCESS; 8138c2ecf20Sopenharmony_ci} 8148c2ecf20Sopenharmony_ci 8158c2ecf20Sopenharmony_cistatic int uas_target_alloc(struct scsi_target *starget) 8168c2ecf20Sopenharmony_ci{ 8178c2ecf20Sopenharmony_ci struct uas_dev_info *devinfo = (struct uas_dev_info *) 8188c2ecf20Sopenharmony_ci dev_to_shost(starget->dev.parent)->hostdata; 8198c2ecf20Sopenharmony_ci 8208c2ecf20Sopenharmony_ci if (devinfo->flags & US_FL_NO_REPORT_LUNS) 8218c2ecf20Sopenharmony_ci starget->no_report_luns = 1; 8228c2ecf20Sopenharmony_ci 8238c2ecf20Sopenharmony_ci return 0; 8248c2ecf20Sopenharmony_ci} 8258c2ecf20Sopenharmony_ci 8268c2ecf20Sopenharmony_cistatic int uas_slave_alloc(struct scsi_device *sdev) 8278c2ecf20Sopenharmony_ci{ 8288c2ecf20Sopenharmony_ci struct uas_dev_info *devinfo = 8298c2ecf20Sopenharmony_ci (struct uas_dev_info *)sdev->host->hostdata; 8308c2ecf20Sopenharmony_ci 8318c2ecf20Sopenharmony_ci sdev->hostdata = devinfo; 8328c2ecf20Sopenharmony_ci 8338c2ecf20Sopenharmony_ci /* 8348c2ecf20Sopenharmony_ci * The protocol has no requirements on alignment in the strict sense. 8358c2ecf20Sopenharmony_ci * Controllers may or may not have alignment restrictions. 8368c2ecf20Sopenharmony_ci * As this is not exported, we use an extremely conservative guess. 8378c2ecf20Sopenharmony_ci */ 8388c2ecf20Sopenharmony_ci blk_queue_update_dma_alignment(sdev->request_queue, (512 - 1)); 8398c2ecf20Sopenharmony_ci 8408c2ecf20Sopenharmony_ci if (devinfo->flags & US_FL_MAX_SECTORS_64) 8418c2ecf20Sopenharmony_ci blk_queue_max_hw_sectors(sdev->request_queue, 64); 8428c2ecf20Sopenharmony_ci else if (devinfo->flags & US_FL_MAX_SECTORS_240) 8438c2ecf20Sopenharmony_ci blk_queue_max_hw_sectors(sdev->request_queue, 240); 8448c2ecf20Sopenharmony_ci 8458c2ecf20Sopenharmony_ci return 0; 8468c2ecf20Sopenharmony_ci} 8478c2ecf20Sopenharmony_ci 8488c2ecf20Sopenharmony_cistatic int uas_slave_configure(struct scsi_device *sdev) 8498c2ecf20Sopenharmony_ci{ 8508c2ecf20Sopenharmony_ci struct uas_dev_info *devinfo = sdev->hostdata; 8518c2ecf20Sopenharmony_ci 8528c2ecf20Sopenharmony_ci if (devinfo->flags & US_FL_NO_REPORT_OPCODES) 8538c2ecf20Sopenharmony_ci sdev->no_report_opcodes = 1; 8548c2ecf20Sopenharmony_ci 8558c2ecf20Sopenharmony_ci /* A few buggy USB-ATA bridges don't understand FUA */ 8568c2ecf20Sopenharmony_ci if (devinfo->flags & US_FL_BROKEN_FUA) 8578c2ecf20Sopenharmony_ci sdev->broken_fua = 1; 8588c2ecf20Sopenharmony_ci 8598c2ecf20Sopenharmony_ci /* UAS also needs to support FL_ALWAYS_SYNC */ 8608c2ecf20Sopenharmony_ci if (devinfo->flags & US_FL_ALWAYS_SYNC) { 8618c2ecf20Sopenharmony_ci sdev->skip_ms_page_3f = 1; 8628c2ecf20Sopenharmony_ci sdev->skip_ms_page_8 = 1; 8638c2ecf20Sopenharmony_ci sdev->wce_default_on = 1; 8648c2ecf20Sopenharmony_ci } 8658c2ecf20Sopenharmony_ci 8668c2ecf20Sopenharmony_ci /* Some disks cannot handle READ_CAPACITY_16 */ 8678c2ecf20Sopenharmony_ci if (devinfo->flags & US_FL_NO_READ_CAPACITY_16) 8688c2ecf20Sopenharmony_ci sdev->no_read_capacity_16 = 1; 8698c2ecf20Sopenharmony_ci 8708c2ecf20Sopenharmony_ci /* Some disks cannot handle WRITE_SAME */ 8718c2ecf20Sopenharmony_ci if (devinfo->flags & US_FL_NO_SAME) 8728c2ecf20Sopenharmony_ci sdev->no_write_same = 1; 8738c2ecf20Sopenharmony_ci /* 8748c2ecf20Sopenharmony_ci * Some disks return the total number of blocks in response 8758c2ecf20Sopenharmony_ci * to READ CAPACITY rather than the highest block number. 8768c2ecf20Sopenharmony_ci * If this device makes that mistake, tell the sd driver. 8778c2ecf20Sopenharmony_ci */ 8788c2ecf20Sopenharmony_ci if (devinfo->flags & US_FL_FIX_CAPACITY) 8798c2ecf20Sopenharmony_ci sdev->fix_capacity = 1; 8808c2ecf20Sopenharmony_ci 8818c2ecf20Sopenharmony_ci /* 8828c2ecf20Sopenharmony_ci * in some cases we have to guess 8838c2ecf20Sopenharmony_ci */ 8848c2ecf20Sopenharmony_ci if (devinfo->flags & US_FL_CAPACITY_HEURISTICS) 8858c2ecf20Sopenharmony_ci sdev->guess_capacity = 1; 8868c2ecf20Sopenharmony_ci 8878c2ecf20Sopenharmony_ci /* 8888c2ecf20Sopenharmony_ci * Some devices don't like MODE SENSE with page=0x3f, 8898c2ecf20Sopenharmony_ci * which is the command used for checking if a device 8908c2ecf20Sopenharmony_ci * is write-protected. Now that we tell the sd driver 8918c2ecf20Sopenharmony_ci * to do a 192-byte transfer with this command the 8928c2ecf20Sopenharmony_ci * majority of devices work fine, but a few still can't 8938c2ecf20Sopenharmony_ci * handle it. The sd driver will simply assume those 8948c2ecf20Sopenharmony_ci * devices are write-enabled. 8958c2ecf20Sopenharmony_ci */ 8968c2ecf20Sopenharmony_ci if (devinfo->flags & US_FL_NO_WP_DETECT) 8978c2ecf20Sopenharmony_ci sdev->skip_ms_page_3f = 1; 8988c2ecf20Sopenharmony_ci 8998c2ecf20Sopenharmony_ci scsi_change_queue_depth(sdev, devinfo->qdepth - 2); 9008c2ecf20Sopenharmony_ci return 0; 9018c2ecf20Sopenharmony_ci} 9028c2ecf20Sopenharmony_ci 9038c2ecf20Sopenharmony_cistatic struct scsi_host_template uas_host_template = { 9048c2ecf20Sopenharmony_ci .module = THIS_MODULE, 9058c2ecf20Sopenharmony_ci .name = "uas", 9068c2ecf20Sopenharmony_ci .queuecommand = uas_queuecommand, 9078c2ecf20Sopenharmony_ci .target_alloc = uas_target_alloc, 9088c2ecf20Sopenharmony_ci .slave_alloc = uas_slave_alloc, 9098c2ecf20Sopenharmony_ci .slave_configure = uas_slave_configure, 9108c2ecf20Sopenharmony_ci .eh_abort_handler = uas_eh_abort_handler, 9118c2ecf20Sopenharmony_ci .eh_device_reset_handler = uas_eh_device_reset_handler, 9128c2ecf20Sopenharmony_ci .this_id = -1, 9138c2ecf20Sopenharmony_ci .skip_settle_delay = 1, 9148c2ecf20Sopenharmony_ci .dma_boundary = PAGE_SIZE - 1, 9158c2ecf20Sopenharmony_ci}; 9168c2ecf20Sopenharmony_ci 9178c2ecf20Sopenharmony_ci#define UNUSUAL_DEV(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax, \ 9188c2ecf20Sopenharmony_ci vendorName, productName, useProtocol, useTransport, \ 9198c2ecf20Sopenharmony_ci initFunction, flags) \ 9208c2ecf20Sopenharmony_ci{ USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax), \ 9218c2ecf20Sopenharmony_ci .driver_info = (flags) } 9228c2ecf20Sopenharmony_ci 9238c2ecf20Sopenharmony_cistatic struct usb_device_id uas_usb_ids[] = { 9248c2ecf20Sopenharmony_ci# include "unusual_uas.h" 9258c2ecf20Sopenharmony_ci { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, USB_SC_SCSI, USB_PR_BULK) }, 9268c2ecf20Sopenharmony_ci { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, USB_SC_SCSI, USB_PR_UAS) }, 9278c2ecf20Sopenharmony_ci { } 9288c2ecf20Sopenharmony_ci}; 9298c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(usb, uas_usb_ids); 9308c2ecf20Sopenharmony_ci 9318c2ecf20Sopenharmony_ci#undef UNUSUAL_DEV 9328c2ecf20Sopenharmony_ci 9338c2ecf20Sopenharmony_cistatic int uas_switch_interface(struct usb_device *udev, 9348c2ecf20Sopenharmony_ci struct usb_interface *intf) 9358c2ecf20Sopenharmony_ci{ 9368c2ecf20Sopenharmony_ci struct usb_host_interface *alt; 9378c2ecf20Sopenharmony_ci 9388c2ecf20Sopenharmony_ci alt = uas_find_uas_alt_setting(intf); 9398c2ecf20Sopenharmony_ci if (!alt) 9408c2ecf20Sopenharmony_ci return -ENODEV; 9418c2ecf20Sopenharmony_ci 9428c2ecf20Sopenharmony_ci return usb_set_interface(udev, alt->desc.bInterfaceNumber, 9438c2ecf20Sopenharmony_ci alt->desc.bAlternateSetting); 9448c2ecf20Sopenharmony_ci} 9458c2ecf20Sopenharmony_ci 9468c2ecf20Sopenharmony_cistatic int uas_configure_endpoints(struct uas_dev_info *devinfo) 9478c2ecf20Sopenharmony_ci{ 9488c2ecf20Sopenharmony_ci struct usb_host_endpoint *eps[4] = { }; 9498c2ecf20Sopenharmony_ci struct usb_device *udev = devinfo->udev; 9508c2ecf20Sopenharmony_ci int r; 9518c2ecf20Sopenharmony_ci 9528c2ecf20Sopenharmony_ci r = uas_find_endpoints(devinfo->intf->cur_altsetting, eps); 9538c2ecf20Sopenharmony_ci if (r) 9548c2ecf20Sopenharmony_ci return r; 9558c2ecf20Sopenharmony_ci 9568c2ecf20Sopenharmony_ci devinfo->cmd_pipe = usb_sndbulkpipe(udev, 9578c2ecf20Sopenharmony_ci usb_endpoint_num(&eps[0]->desc)); 9588c2ecf20Sopenharmony_ci devinfo->status_pipe = usb_rcvbulkpipe(udev, 9598c2ecf20Sopenharmony_ci usb_endpoint_num(&eps[1]->desc)); 9608c2ecf20Sopenharmony_ci devinfo->data_in_pipe = usb_rcvbulkpipe(udev, 9618c2ecf20Sopenharmony_ci usb_endpoint_num(&eps[2]->desc)); 9628c2ecf20Sopenharmony_ci devinfo->data_out_pipe = usb_sndbulkpipe(udev, 9638c2ecf20Sopenharmony_ci usb_endpoint_num(&eps[3]->desc)); 9648c2ecf20Sopenharmony_ci 9658c2ecf20Sopenharmony_ci if (udev->speed < USB_SPEED_SUPER) { 9668c2ecf20Sopenharmony_ci devinfo->qdepth = 32; 9678c2ecf20Sopenharmony_ci devinfo->use_streams = 0; 9688c2ecf20Sopenharmony_ci } else { 9698c2ecf20Sopenharmony_ci devinfo->qdepth = usb_alloc_streams(devinfo->intf, eps + 1, 9708c2ecf20Sopenharmony_ci 3, MAX_CMNDS, GFP_NOIO); 9718c2ecf20Sopenharmony_ci if (devinfo->qdepth < 0) 9728c2ecf20Sopenharmony_ci return devinfo->qdepth; 9738c2ecf20Sopenharmony_ci devinfo->use_streams = 1; 9748c2ecf20Sopenharmony_ci } 9758c2ecf20Sopenharmony_ci 9768c2ecf20Sopenharmony_ci return 0; 9778c2ecf20Sopenharmony_ci} 9788c2ecf20Sopenharmony_ci 9798c2ecf20Sopenharmony_cistatic void uas_free_streams(struct uas_dev_info *devinfo) 9808c2ecf20Sopenharmony_ci{ 9818c2ecf20Sopenharmony_ci struct usb_device *udev = devinfo->udev; 9828c2ecf20Sopenharmony_ci struct usb_host_endpoint *eps[3]; 9838c2ecf20Sopenharmony_ci 9848c2ecf20Sopenharmony_ci eps[0] = usb_pipe_endpoint(udev, devinfo->status_pipe); 9858c2ecf20Sopenharmony_ci eps[1] = usb_pipe_endpoint(udev, devinfo->data_in_pipe); 9868c2ecf20Sopenharmony_ci eps[2] = usb_pipe_endpoint(udev, devinfo->data_out_pipe); 9878c2ecf20Sopenharmony_ci usb_free_streams(devinfo->intf, eps, 3, GFP_NOIO); 9888c2ecf20Sopenharmony_ci} 9898c2ecf20Sopenharmony_ci 9908c2ecf20Sopenharmony_cistatic int uas_probe(struct usb_interface *intf, const struct usb_device_id *id) 9918c2ecf20Sopenharmony_ci{ 9928c2ecf20Sopenharmony_ci int result = -ENOMEM; 9938c2ecf20Sopenharmony_ci struct Scsi_Host *shost = NULL; 9948c2ecf20Sopenharmony_ci struct uas_dev_info *devinfo; 9958c2ecf20Sopenharmony_ci struct usb_device *udev = interface_to_usbdev(intf); 9968c2ecf20Sopenharmony_ci unsigned long dev_flags; 9978c2ecf20Sopenharmony_ci 9988c2ecf20Sopenharmony_ci if (!uas_use_uas_driver(intf, id, &dev_flags)) 9998c2ecf20Sopenharmony_ci return -ENODEV; 10008c2ecf20Sopenharmony_ci 10018c2ecf20Sopenharmony_ci if (uas_switch_interface(udev, intf)) 10028c2ecf20Sopenharmony_ci return -ENODEV; 10038c2ecf20Sopenharmony_ci 10048c2ecf20Sopenharmony_ci shost = scsi_host_alloc(&uas_host_template, 10058c2ecf20Sopenharmony_ci sizeof(struct uas_dev_info)); 10068c2ecf20Sopenharmony_ci if (!shost) 10078c2ecf20Sopenharmony_ci goto set_alt0; 10088c2ecf20Sopenharmony_ci 10098c2ecf20Sopenharmony_ci shost->max_cmd_len = 16 + 252; 10108c2ecf20Sopenharmony_ci shost->max_id = 1; 10118c2ecf20Sopenharmony_ci shost->max_lun = 256; 10128c2ecf20Sopenharmony_ci shost->max_channel = 0; 10138c2ecf20Sopenharmony_ci shost->sg_tablesize = udev->bus->sg_tablesize; 10148c2ecf20Sopenharmony_ci 10158c2ecf20Sopenharmony_ci devinfo = (struct uas_dev_info *)shost->hostdata; 10168c2ecf20Sopenharmony_ci devinfo->intf = intf; 10178c2ecf20Sopenharmony_ci devinfo->udev = udev; 10188c2ecf20Sopenharmony_ci devinfo->resetting = 0; 10198c2ecf20Sopenharmony_ci devinfo->shutdown = 0; 10208c2ecf20Sopenharmony_ci devinfo->flags = dev_flags; 10218c2ecf20Sopenharmony_ci init_usb_anchor(&devinfo->cmd_urbs); 10228c2ecf20Sopenharmony_ci init_usb_anchor(&devinfo->sense_urbs); 10238c2ecf20Sopenharmony_ci init_usb_anchor(&devinfo->data_urbs); 10248c2ecf20Sopenharmony_ci spin_lock_init(&devinfo->lock); 10258c2ecf20Sopenharmony_ci INIT_WORK(&devinfo->work, uas_do_work); 10268c2ecf20Sopenharmony_ci INIT_WORK(&devinfo->scan_work, uas_scan_work); 10278c2ecf20Sopenharmony_ci 10288c2ecf20Sopenharmony_ci result = uas_configure_endpoints(devinfo); 10298c2ecf20Sopenharmony_ci if (result) 10308c2ecf20Sopenharmony_ci goto set_alt0; 10318c2ecf20Sopenharmony_ci 10328c2ecf20Sopenharmony_ci /* 10338c2ecf20Sopenharmony_ci * 1 tag is reserved for untagged commands + 10348c2ecf20Sopenharmony_ci * 1 tag to avoid off by one errors in some bridge firmwares 10358c2ecf20Sopenharmony_ci */ 10368c2ecf20Sopenharmony_ci shost->can_queue = devinfo->qdepth - 2; 10378c2ecf20Sopenharmony_ci 10388c2ecf20Sopenharmony_ci usb_set_intfdata(intf, shost); 10398c2ecf20Sopenharmony_ci result = scsi_add_host(shost, &intf->dev); 10408c2ecf20Sopenharmony_ci if (result) 10418c2ecf20Sopenharmony_ci goto free_streams; 10428c2ecf20Sopenharmony_ci 10438c2ecf20Sopenharmony_ci /* Submit the delayed_work for SCSI-device scanning */ 10448c2ecf20Sopenharmony_ci schedule_work(&devinfo->scan_work); 10458c2ecf20Sopenharmony_ci 10468c2ecf20Sopenharmony_ci return result; 10478c2ecf20Sopenharmony_ci 10488c2ecf20Sopenharmony_cifree_streams: 10498c2ecf20Sopenharmony_ci uas_free_streams(devinfo); 10508c2ecf20Sopenharmony_ci usb_set_intfdata(intf, NULL); 10518c2ecf20Sopenharmony_ciset_alt0: 10528c2ecf20Sopenharmony_ci usb_set_interface(udev, intf->altsetting[0].desc.bInterfaceNumber, 0); 10538c2ecf20Sopenharmony_ci if (shost) 10548c2ecf20Sopenharmony_ci scsi_host_put(shost); 10558c2ecf20Sopenharmony_ci return result; 10568c2ecf20Sopenharmony_ci} 10578c2ecf20Sopenharmony_ci 10588c2ecf20Sopenharmony_cistatic int uas_cmnd_list_empty(struct uas_dev_info *devinfo) 10598c2ecf20Sopenharmony_ci{ 10608c2ecf20Sopenharmony_ci unsigned long flags; 10618c2ecf20Sopenharmony_ci int i, r = 1; 10628c2ecf20Sopenharmony_ci 10638c2ecf20Sopenharmony_ci spin_lock_irqsave(&devinfo->lock, flags); 10648c2ecf20Sopenharmony_ci 10658c2ecf20Sopenharmony_ci for (i = 0; i < devinfo->qdepth; i++) { 10668c2ecf20Sopenharmony_ci if (devinfo->cmnd[i]) { 10678c2ecf20Sopenharmony_ci r = 0; /* Not empty */ 10688c2ecf20Sopenharmony_ci break; 10698c2ecf20Sopenharmony_ci } 10708c2ecf20Sopenharmony_ci } 10718c2ecf20Sopenharmony_ci 10728c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&devinfo->lock, flags); 10738c2ecf20Sopenharmony_ci 10748c2ecf20Sopenharmony_ci return r; 10758c2ecf20Sopenharmony_ci} 10768c2ecf20Sopenharmony_ci 10778c2ecf20Sopenharmony_ci/* 10788c2ecf20Sopenharmony_ci * Wait for any pending cmnds to complete, on usb-2 sense_urbs may temporarily 10798c2ecf20Sopenharmony_ci * get empty while there still is more work to do due to sense-urbs completing 10808c2ecf20Sopenharmony_ci * with a READ/WRITE_READY iu code, so keep waiting until the list gets empty. 10818c2ecf20Sopenharmony_ci */ 10828c2ecf20Sopenharmony_cistatic int uas_wait_for_pending_cmnds(struct uas_dev_info *devinfo) 10838c2ecf20Sopenharmony_ci{ 10848c2ecf20Sopenharmony_ci unsigned long start_time; 10858c2ecf20Sopenharmony_ci int r; 10868c2ecf20Sopenharmony_ci 10878c2ecf20Sopenharmony_ci start_time = jiffies; 10888c2ecf20Sopenharmony_ci do { 10898c2ecf20Sopenharmony_ci flush_work(&devinfo->work); 10908c2ecf20Sopenharmony_ci 10918c2ecf20Sopenharmony_ci r = usb_wait_anchor_empty_timeout(&devinfo->sense_urbs, 5000); 10928c2ecf20Sopenharmony_ci if (r == 0) 10938c2ecf20Sopenharmony_ci return -ETIME; 10948c2ecf20Sopenharmony_ci 10958c2ecf20Sopenharmony_ci r = usb_wait_anchor_empty_timeout(&devinfo->data_urbs, 500); 10968c2ecf20Sopenharmony_ci if (r == 0) 10978c2ecf20Sopenharmony_ci return -ETIME; 10988c2ecf20Sopenharmony_ci 10998c2ecf20Sopenharmony_ci if (time_after(jiffies, start_time + 5 * HZ)) 11008c2ecf20Sopenharmony_ci return -ETIME; 11018c2ecf20Sopenharmony_ci } while (!uas_cmnd_list_empty(devinfo)); 11028c2ecf20Sopenharmony_ci 11038c2ecf20Sopenharmony_ci return 0; 11048c2ecf20Sopenharmony_ci} 11058c2ecf20Sopenharmony_ci 11068c2ecf20Sopenharmony_cistatic int uas_pre_reset(struct usb_interface *intf) 11078c2ecf20Sopenharmony_ci{ 11088c2ecf20Sopenharmony_ci struct Scsi_Host *shost = usb_get_intfdata(intf); 11098c2ecf20Sopenharmony_ci struct uas_dev_info *devinfo = (struct uas_dev_info *)shost->hostdata; 11108c2ecf20Sopenharmony_ci unsigned long flags; 11118c2ecf20Sopenharmony_ci 11128c2ecf20Sopenharmony_ci if (devinfo->shutdown) 11138c2ecf20Sopenharmony_ci return 0; 11148c2ecf20Sopenharmony_ci 11158c2ecf20Sopenharmony_ci /* Block new requests */ 11168c2ecf20Sopenharmony_ci spin_lock_irqsave(shost->host_lock, flags); 11178c2ecf20Sopenharmony_ci scsi_block_requests(shost); 11188c2ecf20Sopenharmony_ci spin_unlock_irqrestore(shost->host_lock, flags); 11198c2ecf20Sopenharmony_ci 11208c2ecf20Sopenharmony_ci if (uas_wait_for_pending_cmnds(devinfo) != 0) { 11218c2ecf20Sopenharmony_ci shost_printk(KERN_ERR, shost, "%s: timed out\n", __func__); 11228c2ecf20Sopenharmony_ci scsi_unblock_requests(shost); 11238c2ecf20Sopenharmony_ci return 1; 11248c2ecf20Sopenharmony_ci } 11258c2ecf20Sopenharmony_ci 11268c2ecf20Sopenharmony_ci uas_free_streams(devinfo); 11278c2ecf20Sopenharmony_ci 11288c2ecf20Sopenharmony_ci return 0; 11298c2ecf20Sopenharmony_ci} 11308c2ecf20Sopenharmony_ci 11318c2ecf20Sopenharmony_cistatic int uas_post_reset(struct usb_interface *intf) 11328c2ecf20Sopenharmony_ci{ 11338c2ecf20Sopenharmony_ci struct Scsi_Host *shost = usb_get_intfdata(intf); 11348c2ecf20Sopenharmony_ci struct uas_dev_info *devinfo = (struct uas_dev_info *)shost->hostdata; 11358c2ecf20Sopenharmony_ci unsigned long flags; 11368c2ecf20Sopenharmony_ci int err; 11378c2ecf20Sopenharmony_ci 11388c2ecf20Sopenharmony_ci if (devinfo->shutdown) 11398c2ecf20Sopenharmony_ci return 0; 11408c2ecf20Sopenharmony_ci 11418c2ecf20Sopenharmony_ci err = uas_configure_endpoints(devinfo); 11428c2ecf20Sopenharmony_ci if (err && err != -ENODEV) 11438c2ecf20Sopenharmony_ci shost_printk(KERN_ERR, shost, 11448c2ecf20Sopenharmony_ci "%s: alloc streams error %d after reset", 11458c2ecf20Sopenharmony_ci __func__, err); 11468c2ecf20Sopenharmony_ci 11478c2ecf20Sopenharmony_ci /* we must unblock the host in every case lest we deadlock */ 11488c2ecf20Sopenharmony_ci spin_lock_irqsave(shost->host_lock, flags); 11498c2ecf20Sopenharmony_ci scsi_report_bus_reset(shost, 0); 11508c2ecf20Sopenharmony_ci spin_unlock_irqrestore(shost->host_lock, flags); 11518c2ecf20Sopenharmony_ci 11528c2ecf20Sopenharmony_ci scsi_unblock_requests(shost); 11538c2ecf20Sopenharmony_ci 11548c2ecf20Sopenharmony_ci return err ? 1 : 0; 11558c2ecf20Sopenharmony_ci} 11568c2ecf20Sopenharmony_ci 11578c2ecf20Sopenharmony_cistatic int uas_suspend(struct usb_interface *intf, pm_message_t message) 11588c2ecf20Sopenharmony_ci{ 11598c2ecf20Sopenharmony_ci struct Scsi_Host *shost = usb_get_intfdata(intf); 11608c2ecf20Sopenharmony_ci struct uas_dev_info *devinfo = (struct uas_dev_info *)shost->hostdata; 11618c2ecf20Sopenharmony_ci 11628c2ecf20Sopenharmony_ci if (uas_wait_for_pending_cmnds(devinfo) != 0) { 11638c2ecf20Sopenharmony_ci shost_printk(KERN_ERR, shost, "%s: timed out\n", __func__); 11648c2ecf20Sopenharmony_ci return -ETIME; 11658c2ecf20Sopenharmony_ci } 11668c2ecf20Sopenharmony_ci 11678c2ecf20Sopenharmony_ci return 0; 11688c2ecf20Sopenharmony_ci} 11698c2ecf20Sopenharmony_ci 11708c2ecf20Sopenharmony_cistatic int uas_resume(struct usb_interface *intf) 11718c2ecf20Sopenharmony_ci{ 11728c2ecf20Sopenharmony_ci return 0; 11738c2ecf20Sopenharmony_ci} 11748c2ecf20Sopenharmony_ci 11758c2ecf20Sopenharmony_cistatic int uas_reset_resume(struct usb_interface *intf) 11768c2ecf20Sopenharmony_ci{ 11778c2ecf20Sopenharmony_ci struct Scsi_Host *shost = usb_get_intfdata(intf); 11788c2ecf20Sopenharmony_ci struct uas_dev_info *devinfo = (struct uas_dev_info *)shost->hostdata; 11798c2ecf20Sopenharmony_ci unsigned long flags; 11808c2ecf20Sopenharmony_ci int err; 11818c2ecf20Sopenharmony_ci 11828c2ecf20Sopenharmony_ci err = uas_configure_endpoints(devinfo); 11838c2ecf20Sopenharmony_ci if (err) { 11848c2ecf20Sopenharmony_ci shost_printk(KERN_ERR, shost, 11858c2ecf20Sopenharmony_ci "%s: alloc streams error %d after reset", 11868c2ecf20Sopenharmony_ci __func__, err); 11878c2ecf20Sopenharmony_ci return -EIO; 11888c2ecf20Sopenharmony_ci } 11898c2ecf20Sopenharmony_ci 11908c2ecf20Sopenharmony_ci spin_lock_irqsave(shost->host_lock, flags); 11918c2ecf20Sopenharmony_ci scsi_report_bus_reset(shost, 0); 11928c2ecf20Sopenharmony_ci spin_unlock_irqrestore(shost->host_lock, flags); 11938c2ecf20Sopenharmony_ci 11948c2ecf20Sopenharmony_ci return 0; 11958c2ecf20Sopenharmony_ci} 11968c2ecf20Sopenharmony_ci 11978c2ecf20Sopenharmony_cistatic void uas_disconnect(struct usb_interface *intf) 11988c2ecf20Sopenharmony_ci{ 11998c2ecf20Sopenharmony_ci struct Scsi_Host *shost = usb_get_intfdata(intf); 12008c2ecf20Sopenharmony_ci struct uas_dev_info *devinfo = (struct uas_dev_info *)shost->hostdata; 12018c2ecf20Sopenharmony_ci unsigned long flags; 12028c2ecf20Sopenharmony_ci 12038c2ecf20Sopenharmony_ci spin_lock_irqsave(&devinfo->lock, flags); 12048c2ecf20Sopenharmony_ci devinfo->resetting = 1; 12058c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&devinfo->lock, flags); 12068c2ecf20Sopenharmony_ci 12078c2ecf20Sopenharmony_ci cancel_work_sync(&devinfo->work); 12088c2ecf20Sopenharmony_ci usb_kill_anchored_urbs(&devinfo->cmd_urbs); 12098c2ecf20Sopenharmony_ci usb_kill_anchored_urbs(&devinfo->sense_urbs); 12108c2ecf20Sopenharmony_ci usb_kill_anchored_urbs(&devinfo->data_urbs); 12118c2ecf20Sopenharmony_ci uas_zap_pending(devinfo, DID_NO_CONNECT); 12128c2ecf20Sopenharmony_ci 12138c2ecf20Sopenharmony_ci /* 12148c2ecf20Sopenharmony_ci * Prevent SCSI scanning (if it hasn't started yet) 12158c2ecf20Sopenharmony_ci * or wait for the SCSI-scanning routine to stop. 12168c2ecf20Sopenharmony_ci */ 12178c2ecf20Sopenharmony_ci cancel_work_sync(&devinfo->scan_work); 12188c2ecf20Sopenharmony_ci 12198c2ecf20Sopenharmony_ci scsi_remove_host(shost); 12208c2ecf20Sopenharmony_ci uas_free_streams(devinfo); 12218c2ecf20Sopenharmony_ci scsi_host_put(shost); 12228c2ecf20Sopenharmony_ci} 12238c2ecf20Sopenharmony_ci 12248c2ecf20Sopenharmony_ci/* 12258c2ecf20Sopenharmony_ci * Put the device back in usb-storage mode on shutdown, as some BIOS-es 12268c2ecf20Sopenharmony_ci * hang on reboot when the device is still in uas mode. Note the reset is 12278c2ecf20Sopenharmony_ci * necessary as some devices won't revert to usb-storage mode without it. 12288c2ecf20Sopenharmony_ci */ 12298c2ecf20Sopenharmony_cistatic void uas_shutdown(struct device *dev) 12308c2ecf20Sopenharmony_ci{ 12318c2ecf20Sopenharmony_ci struct usb_interface *intf = to_usb_interface(dev); 12328c2ecf20Sopenharmony_ci struct usb_device *udev = interface_to_usbdev(intf); 12338c2ecf20Sopenharmony_ci struct Scsi_Host *shost = usb_get_intfdata(intf); 12348c2ecf20Sopenharmony_ci struct uas_dev_info *devinfo = (struct uas_dev_info *)shost->hostdata; 12358c2ecf20Sopenharmony_ci 12368c2ecf20Sopenharmony_ci if (system_state != SYSTEM_RESTART) 12378c2ecf20Sopenharmony_ci return; 12388c2ecf20Sopenharmony_ci 12398c2ecf20Sopenharmony_ci devinfo->shutdown = 1; 12408c2ecf20Sopenharmony_ci uas_free_streams(devinfo); 12418c2ecf20Sopenharmony_ci usb_set_interface(udev, intf->altsetting[0].desc.bInterfaceNumber, 0); 12428c2ecf20Sopenharmony_ci usb_reset_device(udev); 12438c2ecf20Sopenharmony_ci} 12448c2ecf20Sopenharmony_ci 12458c2ecf20Sopenharmony_cistatic struct usb_driver uas_driver = { 12468c2ecf20Sopenharmony_ci .name = "uas", 12478c2ecf20Sopenharmony_ci .probe = uas_probe, 12488c2ecf20Sopenharmony_ci .disconnect = uas_disconnect, 12498c2ecf20Sopenharmony_ci .pre_reset = uas_pre_reset, 12508c2ecf20Sopenharmony_ci .post_reset = uas_post_reset, 12518c2ecf20Sopenharmony_ci .suspend = uas_suspend, 12528c2ecf20Sopenharmony_ci .resume = uas_resume, 12538c2ecf20Sopenharmony_ci .reset_resume = uas_reset_resume, 12548c2ecf20Sopenharmony_ci .drvwrap.driver.shutdown = uas_shutdown, 12558c2ecf20Sopenharmony_ci .id_table = uas_usb_ids, 12568c2ecf20Sopenharmony_ci}; 12578c2ecf20Sopenharmony_ci 12588c2ecf20Sopenharmony_cistatic int __init uas_init(void) 12598c2ecf20Sopenharmony_ci{ 12608c2ecf20Sopenharmony_ci int rv; 12618c2ecf20Sopenharmony_ci 12628c2ecf20Sopenharmony_ci workqueue = alloc_workqueue("uas", WQ_MEM_RECLAIM, 0); 12638c2ecf20Sopenharmony_ci if (!workqueue) 12648c2ecf20Sopenharmony_ci return -ENOMEM; 12658c2ecf20Sopenharmony_ci 12668c2ecf20Sopenharmony_ci rv = usb_register(&uas_driver); 12678c2ecf20Sopenharmony_ci if (rv) { 12688c2ecf20Sopenharmony_ci destroy_workqueue(workqueue); 12698c2ecf20Sopenharmony_ci return -ENOMEM; 12708c2ecf20Sopenharmony_ci } 12718c2ecf20Sopenharmony_ci 12728c2ecf20Sopenharmony_ci return 0; 12738c2ecf20Sopenharmony_ci} 12748c2ecf20Sopenharmony_ci 12758c2ecf20Sopenharmony_cistatic void __exit uas_exit(void) 12768c2ecf20Sopenharmony_ci{ 12778c2ecf20Sopenharmony_ci usb_deregister(&uas_driver); 12788c2ecf20Sopenharmony_ci destroy_workqueue(workqueue); 12798c2ecf20Sopenharmony_ci} 12808c2ecf20Sopenharmony_ci 12818c2ecf20Sopenharmony_cimodule_init(uas_init); 12828c2ecf20Sopenharmony_cimodule_exit(uas_exit); 12838c2ecf20Sopenharmony_ci 12848c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 12858c2ecf20Sopenharmony_ciMODULE_IMPORT_NS(USB_STORAGE); 12868c2ecf20Sopenharmony_ciMODULE_AUTHOR( 12878c2ecf20Sopenharmony_ci "Hans de Goede <hdegoede@redhat.com>, Matthew Wilcox and Sarah Sharp"); 1288