// SPDX-License-Identifier: GPL-2.0
/*
* USB Attached SCSI
* Note that this is not the same as the USB Mass Storage driver
*
* Copyright Hans de Goede <hdegoede@redhat.com> for Red Hat, Inc. 2013 - 2016
* Copyright Matthew Wilcox for Intel Corp, 2010
* Copyright Sarah Sharp for Intel Corp, 2010
*/
#include <linux/blkdev.h>
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/module.h>
#include <linux/usb.h>
#include <linux/usb_usual.h>
#include <linux/usb/hcd.h>
#include <linux/usb/storage.h>
#include <linux/usb/uas.h>
#include <scsi/scsi.h>
#include <scsi/scsi_eh.h>
#include <scsi/scsi_dbg.h>
#include <scsi/scsi_cmnd.h>
#include <scsi/scsi_device.h>
#include <scsi/scsi_host.h>
#include <scsi/scsi_tcq.h>
#include "uas-detect.h"
#include "scsiglue.h"
#define MAX_CMNDS 256
struct uas_dev_info {
struct usb_interface *intf;
struct usb_device *udev;
struct usb_anchor cmd_urbs;
struct usb_anchor sense_urbs;
struct usb_anchor data_urbs;
unsigned long flags;
int qdepth, resetting;
unsigned cmd_pipe, status_pipe, data_in_pipe, data_out_pipe;
unsigned use_streams:1;
unsigned shutdown:1;
struct scsi_cmnd *cmnd[MAX_CMNDS];
spinlock_t lock;
struct work_struct work;
};
enum {
SUBMIT_STATUS_URB = BIT(1),
ALLOC_DATA_IN_URB = BIT(2),
SUBMIT_DATA_IN_URB = BIT(3),
ALLOC_DATA_OUT_URB = BIT(4),
SUBMIT_DATA_OUT_URB = BIT(5),
ALLOC_CMD_URB = BIT(6),
SUBMIT_CMD_URB = BIT(7),
COMMAND_INFLIGHT = BIT(8),
DATA_IN_URB_INFLIGHT = BIT(9),
DATA_OUT_URB_INFLIGHT = BIT(10),
COMMAND_ABORTED = BIT(11),
IS_IN_WORK_LIST = BIT(12),
};
/* Overrides scsi_pointer */
struct uas_cmd_info {
unsigned int state;
unsigned int uas_tag;
struct urb *cmd_urb;
struct urb *data_in_urb;
struct urb *data_out_urb;
};
/* I hate forward declarations, but I actually have a loop */
static int uas_submit_urbs(struct scsi_cmnd *cmnd,
struct uas_dev_info *devinfo);
static void uas_do_work(struct work_struct *work);
static int uas_try_complete(struct scsi_cmnd *cmnd, const char *caller);
static void uas_free_streams(struct uas_dev_info *devinfo);
static void uas_log_cmd_state(struct scsi_cmnd *cmnd, const char *prefix,
int status);
static void uas_do_work(struct work_struct *work)
{
struct uas_dev_info *devinfo =
container_of(work, struct uas_dev_info, work);
struct uas_cmd_info *cmdinfo;
struct scsi_cmnd *cmnd;
unsigned long flags;
int i, err;
spin_lock_irqsave(&devinfo->lock, flags);
if (devinfo->resetting)
goto out;
for (i = 0; i < devinfo->qdepth; i++) {
if (!devinfo->cmnd[i])
continue;
cmnd = devinfo->cmnd[i];
cmdinfo = (void *)&cmnd->SCp;
if (!(cmdinfo->state & IS_IN_WORK_LIST))
continue;
err = uas_submit_urbs(cmnd, cmnd->device->hostdata);
if (!err)
cmdinfo->state &= ~IS_IN_WORK_LIST;
else
schedule_work(&devinfo->work);
}
out:
spin_unlock_irqrestore(&devinfo->lock, flags);
}
static void uas_add_work(struct uas_cmd_info *cmdinfo)
{
struct scsi_pointer *scp = (void *)cmdinfo;
<