cregit-Linux how code gets into the kernel

Release 4.11 drivers/usb/storage/usb.c

/*
 * Driver for USB Mass Storage compliant devices
 *
 * Current development and maintenance by:
 *   (c) 1999-2003 Matthew Dharm (mdharm-usb@one-eyed-alien.net)
 *
 * Developed with the assistance of:
 *   (c) 2000 David L. Brown, Jr. (usb-storage@davidb.org)
 *   (c) 2003-2009 Alan Stern (stern@rowland.harvard.edu)
 *
 * Initial work by:
 *   (c) 1999 Michael Gee (michael@linuxspecific.com)
 *
 * usb_device_id support by Adam J. Richter (adam@yggdrasil.com):
 *   (c) 2000 Yggdrasil Computing, Inc.
 *
 * This driver is based on the 'USB Mass Storage Class' document. This
 * describes in detail the protocol used to communicate with such
 * devices.  Clearly, the designers had SCSI and ATAPI commands in
 * mind when they created this document.  The commands are all very
 * similar to commands in the SCSI-II and ATAPI specifications.
 *
 * It is important to note that in a number of cases this class
 * exhibits class-specific exemptions from the USB specification.
 * Notably the usage of NAK, STALL and ACK differs from the norm, in
 * that they are used to communicate wait, failed and OK on commands.
 *
 * Also, for certain devices, the interrupt endpoint is used to convey
 * status of a command.
 *
 * Please see http://www.one-eyed-alien.net/~mdharm/linux-usb for more
 * information about this driver.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation; either version 2, or (at your option) any
 * later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#ifdef CONFIG_USB_STORAGE_DEBUG

#define DEBUG
#endif

#include <linux/sched.h>
#include <linux/errno.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/kthread.h>
#include <linux/mutex.h>
#include <linux/utsname.h>

#include <scsi/scsi.h>
#include <scsi/scsi_cmnd.h>
#include <scsi/scsi_device.h>

#include "usb.h"
#include "scsiglue.h"
#include "transport.h"
#include "protocol.h"
#include "debug.h"
#include "initializers.h"

#include "sierra_ms.h"
#include "option_ms.h"

#if IS_ENABLED(CONFIG_USB_UAS)
#include "uas-detect.h"
#endif


#define DRV_NAME "usb-storage"

/* Some informational data */
MODULE_AUTHOR("Matthew Dharm <mdharm-usb@one-eyed-alien.net>");
MODULE_DESCRIPTION("USB Mass Storage driver for Linux");
MODULE_LICENSE("GPL");


static unsigned int delay_use = 1;
module_param(delay_use, uint, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(delay_use, "seconds to delay before using a new device");


static char quirks[128];
module_param_string(quirks, quirks, sizeof(quirks), S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(quirks, "supplemental list of device IDs and their quirks");


/*
 * The entries in this table correspond, line for line,
 * with the entries in usb_storage_usb_ids[], defined in usual-tables.c.
 */

/*
 *The vendor name should be kept at eight characters or less, and
 * the product name should be kept at 16 characters or less. If a device
 * has the US_FL_FIX_INQUIRY flag, then the vendor and product names
 * normally generated by a device through the INQUIRY response will be
 * taken from this list, and this is the reason for the above size
 * restriction. However, if the flag is not present, then you
 * are free to use as many characters as you like.
 */


#define UNUSUAL_DEV(idVendor, idProduct, bcdDeviceMin, bcdDeviceMax, \
		    vendor_name, product_name, use_protocol, use_transport, \
		    init_function, Flags) \
{ \
        .vendorName = vendor_name,      \
        .productName = product_name,    \
        .useProtocol = use_protocol,    \
        .useTransport = use_transport,  \
        .initFunction = init_function,  \
}


#define COMPLIANT_DEV	UNUSUAL_DEV


#define USUAL_DEV(use_protocol, use_transport) \
{ \
        .useProtocol = use_protocol,    \
        .useTransport = use_transport,  \
}


#define UNUSUAL_VENDOR_INTF(idVendor, cl, sc, pr, \
		vendor_name, product_name, use_protocol, use_transport, \
		init_function, Flags) \
{ \
        .vendorName = vendor_name,      \
        .productName = product_name,    \
        .useProtocol = use_protocol,    \
        .useTransport = use_transport,  \
        .initFunction = init_function,  \
}


static struct us_unusual_dev us_unusual_dev_list[] = {
#	include "unusual_devs.h"
	{ }		/* Terminating entry */
};


static struct us_unusual_dev for_dynamic_ids =
		USUAL_DEV(USB_SC_SCSI, USB_PR_BULK);


#undef UNUSUAL_DEV

#undef COMPLIANT_DEV

#undef USUAL_DEV

#undef UNUSUAL_VENDOR_INTF

#ifdef CONFIG_LOCKDEP


static struct lock_class_key us_interface_key[USB_MAXINTERFACES];


static void us_set_lock_class(struct mutex *mutex, struct usb_interface *intf) { struct usb_device *udev = interface_to_usbdev(intf); struct usb_host_config *config = udev->actconfig; int i; for (i = 0; i < config->desc.bNumInterfaces; i++) { if (config->interface[i] == intf) break; } BUG_ON(i == config->desc.bNumInterfaces); lockdep_set_class(mutex, &us_interface_key[i]); }

Contributors

PersonTokensPropCommitsCommitProp
Lei Ming91100.00%1100.00%
Total91100.00%1100.00%

#else
static void us_set_lock_class(struct mutex *mutex, struct usb_interface *intf) { }

Contributors

PersonTokensPropCommitsCommitProp
Lei Ming15100.00%1100.00%
Total15100.00%1100.00%

#endif #ifdef CONFIG_PM /* Minimal support for suspend and resume */
int usb_stor_suspend(struct usb_interface *iface, pm_message_t message) { struct us_data *us = usb_get_intfdata(iface); /* Wait until no command is running */ mutex_lock(&us->dev_mutex); if (us->suspend_resume_hook) (us->suspend_resume_hook)(us, US_SUSPEND); /* * When runtime PM is working, we'll set a flag to indicate * whether we should autoresume when a SCSI request arrives. */ mutex_unlock(&us->dev_mutex); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Alan Stern3760.66%228.57%
Matthew Dharm1727.87%114.29%
Arjan van de Ven46.56%114.29%
Linus Torvalds (pre-git)11.64%114.29%
Felipe Balbi11.64%114.29%
Linus Torvalds11.64%114.29%
Total61100.00%7100.00%

EXPORT_SYMBOL_GPL(usb_stor_suspend);
int usb_stor_resume(struct usb_interface *iface) { struct us_data *us = usb_get_intfdata(iface); mutex_lock(&us->dev_mutex); if (us->suspend_resume_hook) (us->suspend_resume_hook)(us, US_RESUME); mutex_unlock(&us->dev_mutex); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Alan Stern2341.07%250.00%
Matthew Dharm1730.36%125.00%
Greg Kroah-Hartman1628.57%125.00%
Total56100.00%4100.00%

EXPORT_SYMBOL_GPL(usb_stor_resume);
int usb_stor_reset_resume(struct usb_interface *iface) { struct us_data *us = usb_get_intfdata(iface); /* Report the reset to the SCSI core */ usb_stor_report_bus_reset(us); /* * FIXME: Notify the subdrivers that they need to reinitialize * the device */ return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Alan Stern2996.67%266.67%
Felipe Balbi13.33%133.33%
Total30100.00%3100.00%

EXPORT_SYMBOL_GPL(usb_stor_reset_resume); #endif /* CONFIG_PM */ /* * The next two routines get called just before and just after * a USB port reset, whether from this driver or a different one. */
int usb_stor_pre_reset(struct usb_interface *iface) { struct us_data *us = usb_get_intfdata(iface); /* Make sure no command runs during the reset */ mutex_lock(&us->dev_mutex); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Alan Stern32100.00%3100.00%
Total32100.00%3100.00%

EXPORT_SYMBOL_GPL(usb_stor_pre_reset);
int usb_stor_post_reset(struct usb_interface *iface) { struct us_data *us = usb_get_intfdata(iface); /* Report the reset to the SCSI core */ usb_stor_report_bus_reset(us); /* * FIXME: Notify the subdrivers that they need to reinitialize * the device */ mutex_unlock(&us->dev_mutex); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Alan Stern3797.37%375.00%
Felipe Balbi12.63%125.00%
Total38100.00%4100.00%

EXPORT_SYMBOL_GPL(usb_stor_post_reset); /* * fill_inquiry_response takes an unsigned char array (which must * be at least 36 characters) and populates the vendor name, * product name, and revision fields. Then the array is copied * into the SCSI command's response buffer (oddly enough * called request_buffer). data_len contains the length of the * data array, which again must be at least 36. */
void fill_inquiry_response(struct us_data *us, unsigned char *data, unsigned int data_len) { if (data_len < 36) /* You lose. */ return; memset(data+8, ' ', 28); if (data[0]&0x20) { /* * USB device currently not connected. Return * peripheral qualifier 001b ("...however, the * physical device is not currently connected * to this logical unit") and leave vendor and * product identification empty. ("If the target * does store some of the INQUIRY data on the * device, it may return zeros or ASCII spaces * (20h) in those fields until the data is * available from the device."). */ } else { u16 bcdDevice = le16_to_cpu(us->pusb_dev->descriptor.bcdDevice); int n; n = strlen(us->unusual_dev->vendorName); memcpy(data+8, us->unusual_dev->vendorName, min(8, n)); n = strlen(us->unusual_dev->productName); memcpy(data+16, us->unusual_dev->productName, min(16, n)); data[32] = 0x30 + ((bcdDevice>>12) & 0x0F); data[33] = 0x30 + ((bcdDevice>>8) & 0x0F); data[34] = 0x30 + ((bcdDevice>>4) & 0x0F); data[35] = 0x30 + ((bcdDevice) & 0x0F); } usb_stor_set_xfer_buf(data, data_len, us->srb); }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds15071.77%218.18%
Alan Stern4019.14%327.27%
Greg Kroah-Hartman146.70%19.09%
Linus Torvalds (pre-git)20.96%218.18%
Felipe Balbi10.48%19.09%
Jeffrin Jose10.48%19.09%
Jens Axboe10.48%19.09%
Total209100.00%11100.00%

EXPORT_SYMBOL_GPL(fill_inquiry_response);
static int usb_stor_control_thread(void * __us) { struct us_data *us = (struct us_data *)__us; struct Scsi_Host *host = us_to_host(us); for (;;) { usb_stor_dbg(us, "*** thread sleeping\n"); if (wait_for_completion_interruptible(&us->cmnd_ready)) break; usb_stor_dbg(us, "*** thread awakened\n"); /* lock the device pointers */ mutex_lock(&(us->dev_mutex)); /* lock access to the state */ scsi_lock(host); /* When we are called with no command pending, we're done */ if (us->srb == NULL) { scsi_unlock(host); mutex_unlock(&us->dev_mutex); usb_stor_dbg(us, "-- exiting\n"); break; } /* has the command timed out *already* ? */ if (test_bit(US_FLIDX_TIMED_OUT, &us->dflags)) { us->srb->result = DID_ABORT << 16; goto SkipForAbort; } scsi_unlock(host); /* * reject the command if the direction indicator * is UNKNOWN */ if (us->srb->sc_data_direction == DMA_BIDIRECTIONAL) { usb_stor_dbg(us, "UNKNOWN data direction\n"); us->srb->result = DID_ERROR << 16; } /* * reject if target != 0 or if LUN is higher than * the maximum known LUN */ else if (us->srb->device->id && !(us->fflags & US_FL_SCM_MULT_TARG)) { usb_stor_dbg(us, "Bad target number (%d:%llu)\n", us->srb->device->id, us->srb->device->lun); us->srb->result = DID_BAD_TARGET << 16; } else if (us->srb->device->lun > us->max_lun) { usb_stor_dbg(us, "Bad LUN (%d:%llu)\n", us->srb->device->id, us->srb->device->lun); us->srb->result = DID_BAD_TARGET << 16; } /* * Handle those devices which need us to fake * their inquiry data */ else if ((us->srb->cmnd[0] == INQUIRY) && (us->fflags & US_FL_FIX_INQUIRY)) { unsigned char data_ptr[36] = { 0x00, 0x80, 0x02, 0x02, 0x1F, 0x00, 0x00, 0x00}; usb_stor_dbg(us, "Faking INQUIRY command\n"); fill_inquiry_response(us, data_ptr, 36); us->srb->result = SAM_STAT_GOOD; } /* we've got a command, let's do it! */ else { US_DEBUG(usb_stor_show_command(us, us->srb)); us->proto_handler(us->srb, us); usb_mark_last_busy(us->pusb_dev); } /* lock access to the state */ scsi_lock(host); /* indicate that the command is done */ if (us->srb->result != DID_ABORT << 16) { usb_stor_dbg(us, "scsi cmd done, result=0x%x\n", us->srb->result); us->srb->scsi_done(us->srb); } else { SkipForAbort: usb_stor_dbg(us, "scsi command aborted\n"); } /* * If an abort request was received we need to signal that * the abort has finished. The proper test for this is * the TIMED_OUT flag, not srb->result == DID_ABORT, because * the timeout might have occurred after the command had * already completed with a different result code. */ if (test_bit(US_FLIDX_TIMED_OUT, &us->dflags)) { complete(&(us->notify)); /* Allow USB transfers to resume */ clear_bit(US_FLIDX_ABORTING, &us->dflags); clear_bit(US_FLIDX_TIMED_OUT, &us->dflags); } /* finished working on this command */ us->srb = NULL; scsi_unlock(host); /* unlock the device pointers */ mutex_unlock(&us->dev_mutex); } /* for (;;) */ /* Wait until we are told to stop */ for (;;) { set_current_state(TASK_INTERRUPTIBLE); if (kthread_should_stop()) break; schedule(); } __set_current_state(TASK_RUNNING); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds28652.29%310.00%
Matthew Dharm12422.67%1136.67%
Alan Stern549.87%516.67%
Joe Perches315.67%13.33%
Linus Torvalds (pre-git)203.66%516.67%
Luben Tuikov152.74%13.33%
Arjan van de Ven61.10%13.33%
Manfred Spraul50.91%13.33%
Felipe Balbi40.73%13.33%
Hannes Reinecke20.37%13.33%
Total547100.00%30100.00%

/*********************************************************************** * Device probing and disconnecting ***********************************************************************/ /* Associate our private data with the USB device */
static int associate_dev(struct us_data *us, struct usb_interface *intf) { /* Fill in the device-related fields */ us->pusb_dev = interface_to_usbdev(intf); us->pusb_intf = intf; us->ifnum = intf->cur_altsetting->desc.bInterfaceNumber; usb_stor_dbg(us, "Vendor: 0x%04x, Product: 0x%04x, Revision: 0x%04x\n", le16_to_cpu(us->pusb_dev->descriptor.idVendor), le16_to_cpu(us->pusb_dev->descriptor.idProduct), le16_to_cpu(us->pusb_dev->descriptor.bcdDevice)); usb_stor_dbg(us, "Interface Subclass: 0x%02x, Protocol: 0x%02x\n", intf->cur_altsetting->desc.bInterfaceSubClass, intf->cur_altsetting->desc.bInterfaceProtocol); /* Store our private data in the interface */ usb_set_intfdata(intf, us); /* Allocate the control/setup and DMA-mapped buffers */ us->cr = kmalloc(sizeof(*us->cr), GFP_KERNEL); if (!us->cr) return -ENOMEM; us->iobuf = usb_alloc_coherent(us->pusb_dev, US_IOBUF_SIZE, GFP_KERNEL, &us->iobuf_dma); if (!us->iobuf) { usb_stor_dbg(us, "I/O buffer allocation failed\n"); return -ENOMEM; } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Matthew Dharm16588.24%444.44%
Greg Kroah-Hartman94.81%111.11%
Joe Perches94.81%111.11%
Alan Stern31.60%222.22%
Daniel Mack10.53%111.11%
Total187100.00%9100.00%

/* Works only for digits and letters, but small and fast */ #define TOLOWER(x) ((x) | 0x20) /* Adjust device flags based on the "quirks=" module parameter */
void usb_stor_adjust_quirks(struct usb_device *udev, unsigned long *fflags) { char *p; u16 vid = le16_to_cpu(udev->descriptor.idVendor); u16 pid = le16_to_cpu(udev->descriptor.idProduct); unsigned f = 0; unsigned int mask = (US_FL_SANE_SENSE | US_FL_BAD_SENSE | US_FL_FIX_CAPACITY | US_FL_IGNORE_UAS | US_FL_CAPACITY_HEURISTICS | US_FL_IGNORE_DEVICE | US_FL_NOT_LOCKABLE | US_FL_MAX_SECTORS_64 | US_FL_CAPACITY_OK | US_FL_IGNORE_RESIDUE | US_FL_SINGLE_LUN | US_FL_NO_WP_DETECT | US_FL_NO_READ_DISC_INFO | US_FL_NO_READ_CAPACITY_16 | US_FL_INITIAL_READ10 | US_FL_WRITE_CACHE | US_FL_NO_ATA_1X | US_FL_NO_REPORT_OPCODES | US_FL_MAX_SECTORS_240 | US_FL_NO_REPORT_LUNS | US_FL_ALWAYS_SYNC); p = quirks; while (*p) { /* Each entry consists of VID:PID:flags */ if (vid == simple_strtoul(p, &p, 16) && *p == ':' && pid == simple_strtoul(p+1, &p, 16) && *p == ':') break; /* Move forward to the next entry */ while (*p) { if (*p++ == ',') break; } } if (!*p) /* No match */ return; /* Collect the flags */ while (*++p && *p != ',') { switch (TOLOWER(*p)) { case 'a': f |= US_FL_SANE_SENSE; break; case 'b': f |= US_FL_BAD_SENSE; break; case 'c': f |= US_FL_FIX_CAPACITY; break; case 'd': f |= US_FL_NO_READ_DISC_INFO; break; case 'e': f |= US_FL_NO_READ_CAPACITY_16; break; case 'f': f |= US_FL_NO_REPORT_OPCODES; break; case 'g': f |= US_FL_MAX_SECTORS_240; break; case 'h': f |= US_FL_CAPACITY_HEURISTICS; break; case 'i': f |= US_FL_IGNORE_DEVICE; break; case 'j': f |= US_FL_NO_REPORT_LUNS; break; case 'l': f |= US_FL_NOT_LOCKABLE; break; case 'm': f |= US_FL_MAX_SECTORS_64; break; case 'n': f |= US_FL_INITIAL_READ10; break; case 'o': f |= US_FL_CAPACITY_OK; break; case 'p': f |= US_FL_WRITE_CACHE; break; case 'r': f |= US_FL_IGNORE_RESIDUE; break; case 's': f |= US_FL_SINGLE_LUN; break; case 't': f |= US_FL_NO_ATA_1X; break; case 'u': f |= US_FL_IGNORE_UAS; break; case 'w': f |= US_FL_NO_WP_DETECT; break; case 'y': f |= US_FL_ALWAYS_SYNC; break; /* Ignore unrecognized flag characters */ } } *fflags = (*fflags & ~mask) | f; }

Contributors

PersonTokensPropCommitsCommitProp
Alan Stern27572.75%535.71%
Hans de Goede6216.40%535.71%
Karl Relton205.29%17.14%
Oliver Neukum102.65%17.14%
Namjae Jeon102.65%17.14%
Pete Zaitcev10.26%17.14%
Total378100.00%14100.00%

EXPORT_SYMBOL_GPL(usb_stor_adjust_quirks); /* Get the unusual_devs entries and the string descriptors */
static int get_device_info(struct us_data *us, const struct usb_device_id *id, struct us_unusual_dev *unusual_dev) { struct usb_device *dev = us->pusb_dev; struct usb_interface_descriptor *idesc = &us->pusb_intf->cur_altsetting->desc; struct device *pdev = &us->pusb_intf->dev; /* Store the entries */ us->unusual_dev = unusual_dev; us->subclass = (unusual_dev->useProtocol == USB_SC_DEVICE) ? idesc->bInterfaceSubClass : unusual_dev->useProtocol; us->protocol = (unusual_dev->useTransport == USB_PR_DEVICE) ? idesc->bInterfaceProtocol : unusual_dev->useTransport; us->fflags = id->driver_info; usb_stor_adjust_quirks(us->pusb_dev, &us->fflags); if (us->fflags & US_FL_IGNORE_DEVICE) { dev_info(pdev, "device ignored\n"); return -ENODEV; } /* * This flag is only needed when we're in high-speed, so let's * disable it if we're in full-speed */ if (dev->speed != USB_SPEED_HIGH) us->fflags &= ~US_FL_GO_SLOW; if (us->fflags) dev_info(pdev, "Quirks match for vid %04x pid %04x: %lx\n", le16_to_cpu(dev->descriptor.idVendor), le16_to_cpu(dev->descriptor.idProduct), us->fflags); /* * Log a message if a non-generic unusual_dev entry contains an * unnecessary subclass or protocol override. This may stimulate * reports from users that will help us remove unneeded entries * from the unusual_devs.h table. */ if (id->idVendor || id->idProduct) { static const char *msgs[3] = { "an unneeded SubClass entry", "an unneeded Protocol entry", "unneeded SubClass and Protocol entries"}; struct usb_device_descriptor *ddesc = &dev->descriptor; int msg = -1; if (unusual_dev->useProtocol != USB_SC_DEVICE && us->subclass == idesc->bInterfaceSubClass) msg += 1; if (unusual_dev->useTransport != USB_PR_DEVICE && us->protocol == idesc->bInterfaceProtocol) msg += 2; if (msg >= 0 && !(us->fflags & US_FL_NEED_OVERRIDE)) dev_notice(pdev, "This device " "(%04x,%04x,%04x S %02x P %02x)" " has %s in unusual_devs.h (kernel" " %s)\n" " Please send a copy of this message to " "<linux-usb@vger.kernel.org> and " "<usb-storage@lists.one-eyed-alien.net>\n", le16_to_cpu(ddesc->idVendor), le16_to_cpu(ddesc->idProduct), le16_to_cpu(ddesc->bcdDevice), idesc->bInterfaceSubClass, idesc->bInterfaceProtocol, msgs[msg], utsname()->release); } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Matthew Dharm18251.70%936.00%
Felipe Contreras8724.72%28.00%
Daniel Drake205.68%14.00%
Phil Dibowitz185.11%28.00%
Alan Stern143.98%416.00%
Pete Zaitcev82.27%14.00%
Hans de Goede82.27%14.00%
Linus Torvalds (pre-git)71.99%14.00%
Michal Nazarewicz41.14%14.00%
Andrew Lunn20.57%14.00%
Arjan van de Ven10.28%14.00%
Felipe Balbi10.28%14.00%
Total352100.00%25100.00%

/* Get the transport settings */
static void get_transport(struct us_data *us) { switch (us->protocol) { case USB_PR_CB: us->transport_name = "Control/Bulk"; us->transport = usb_stor_CB_transport; us->transport_reset = usb_stor_CB_reset; us->max_lun = 7; break; case USB_PR_CBI: us->transport_name = "Control/Bulk/Interrupt"; us->transport = usb_stor_CB_transport; us->transport_reset = usb_stor_CB_reset; us->max_lun = 7; break; case USB_PR_BULK: us->transport_name = "Bulk"; us->transport = usb_stor_Bulk_transport; us->transport_reset = usb_stor_Bulk_reset; break; } }

Contributors

PersonTokensPropCommitsCommitProp
Matthew Dharm9193.81%555.56%
Michal Nazarewicz33.09%111.11%
Alan Stern22.06%222.22%
Greg Kroah-Hartman11.03%111.11%
Total97100.00%9100.00%

/* Get the protocol settings */
static void get_protocol(struct us_data *us) { switch (us->subclass) { case USB_SC_RBC: us->protocol_name = "Reduced Block Commands (RBC)"; us->proto_handler = usb_stor_transparent_scsi_command; break; case USB_SC_8020: us->protocol_name = "8020i"; us->proto_handler = usb_stor_pad12_command; us->max_lun = 0; break; case USB_SC_QIC: us->protocol_name = "QIC-157"; us->proto_handler = usb_stor_pad12_command; us->max_lun = 0; break; case USB_SC_8070: us->protocol_name = "8070i"; us->proto_handler = usb_stor_pad12_command; us->max_lun = 0; break; case USB_SC_SCSI: us->protocol_name = "Transparent SCSI"; us->proto_handler = usb_stor_transparent_scsi_command; break; case USB_SC_UFI: us->protocol_name = "Uniform Floppy Interface (UFI)"; us->proto_handler = usb_stor_ufi_command; break; } }

Contributors

PersonTokensPropCommitsCommitProp
Matthew Dharm11586.47%116.67%
Michal Nazarewicz64.51%116.67%
Linus Torvalds53.76%116.67%
Alan Stern43.01%233.33%
Linus Torvalds (pre-git)32.26%116.67%
Total133100.00%6100.00%

/* Get the pipe settings */
static int get_pipes(struct us_data *us) { struct usb_host_interface *altsetting = us->pusb_intf->cur_altsetting; int i; struct usb_endpoint_descriptor *ep; struct usb_endpoint_descriptor *ep_in = NULL; struct usb_endpoint_descriptor *ep_out = NULL; struct usb_endpoint_descriptor *ep_int = NULL; /* * Find the first endpoint of each type we need. * We are expecting a minimum of 2 endpoints - in and out (bulk). * An optional interrupt-in is OK (necessary for CBI protocol). * We will ignore any others. */ for (i = 0; i < altsetting->desc.bNumEndpoints; i++) { ep = &altsetting->endpoint[i].desc; if (usb_endpoint_xfer_bulk(ep)) { if (usb_endpoint_dir_in(ep)) { if (!ep_in) ep_in = ep; } else { if (!ep_out) ep_out = ep; } } else if (usb_endpoint_is_int_in(ep)) { if (!ep_int) ep_int = ep; } } if (!ep_in || !ep_out || (us->protocol == USB_PR_CBI && !ep_int)) { usb_stor_dbg(us, "Endpoint sanity check failed! Rejecting dev.\n"); return -EIO; } /* Calculate and store the pipe values */ us->send_ctrl_pipe = usb_sndctrlpipe(us->pusb_dev, 0); us->recv_ctrl_pipe = usb_rcvctrlpipe(us->pusb_dev, 0); us->send_bulk_pipe = usb_sndbulkpipe(us->pusb_dev, usb_endpoint_num(ep_out)); us->recv_bulk_pipe = usb_rcvbulkpipe(us->pusb_dev, usb_endpoint_num(ep_in)); if (ep_int) { us->recv_intr_pipe = usb_rcvintpipe(us->pusb_dev, usb_endpoint_num(ep_int)); us->ep_bInterval = ep_int->bInterval; } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Matthew Dharm14554.72%428.57%
Linus Torvalds (pre-git)6223.40%214.29%
Alan Stern2910.94%321.43%
Julia Lawall93.40%17.14%
Luiz Fernando N. Capitulino83.02%17.14%
David Brownell83.02%17.14%
Joe Perches31.13%17.14%
Michal Nazarewicz10.38%17.14%
Total265100.00%14100.00%

/* Initialize all the dynamic resources we need */
static int usb_stor_acquire_resources(struct us_data *us) { int p; struct task_struct *th; us->current_urb = usb_alloc_urb(0, GFP_KERNEL); if (!us->current_urb) return -ENOMEM; /* * Just before we start our control thread, initialize * the device if it needs initialization */ if (us->unusual_dev->initFunction) { p = us->unusual_dev->initFunction(us); if (p) return p; } /* Start up our control thread */ th = kthread_run(usb_stor_control_thread, us, "usb-storage"); if (IS_ERR(th)) { dev_warn(&us->pusb_intf->dev, "Unable to start control thread\n"); return PTR_ERR(th); } us->ctl_thread = th; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Matthew Dharm6857.63%646.15%
Alan Stern2823.73%323.08%
Linus Torvalds (pre-git)1210.17%215.38%
Felipe Contreras97.63%17.69%
Felipe Balbi10.85%17.69%
Total118100.00%13100.00%

/* Release all our dynamic resources */
static void usb_stor_release_resources(struct us_data *us) { /* * Tell the control thread to exit. The SCSI host must * already have been removed and the DISCONNECTING flag set * so that we won't accept any more commands. */ usb_stor_dbg(us, "-- sending exit command to thread\n"); complete(&us->cmnd_ready); if (us->ctl_thread) kthread_stop(us->ctl_thread); /* Call the destructor routine, if it exists */ if (us->extra_destructor) { usb_stor_dbg(us, "-- calling extra_destructor()\n"); us->extra_destructor(us->extra); } /* Free the extra data and the URB */ kfree(us->extra); usb_free_urb(us->current_urb); }

Contributors

PersonTokensPropCommitsCommitProp
Matthew Dharm4252.50%436.36%
Alan Stern1518.75%218.18%
Linus Torvalds810.00%19.09%
Linus Torvalds (pre-git)78.75%19.09%
Joe Perches67.50%19.09%
Adrian Bunk11.25%19.09%
Felipe Balbi11.25%19.09%
Total80100.00%11100.00%

/* Dissociate from the USB device */
static void dissociate_dev(struct us_data *us) { /* Free the buffers */ kfree(us->cr); usb_free_coherent(us->pusb_dev, US_IOBUF_SIZE, us->iobuf, us->iobuf_dma); /* Remove our private data from the interface */ usb_set_intfdata(us->pusb_intf, NULL); }

Contributors

PersonTokensPropCommitsCommitProp
Matthew Dharm4291.30%240.00%
Alan Stern24.35%120.00%
Daniel Mack12.17%120.00%
Linus Torvalds (pre-git)12.17%120.00%
Total46100.00%5100.00%

/* * First stage of disconnect processing: stop SCSI scanning, * remove the host, and stop accepting new commands */
static void quiesce_and_remove_host(struct us_data *us) { struct Scsi_Host *host = us_to_host(us); /* If the device is really gone, cut short reset delays */ if (us->pusb_dev->state == USB_STATE_NOTATTACHED) { set_bit(US_FLIDX_DISCONNECTING, &us->dflags); wake_up(&us->delay_wait); } /* * Prevent SCSI scanning (if it hasn't started yet) * or wait for the SCSI-scanning routine to stop. */ cancel_delayed_work_sync(&us->scan_dwork); /* Balance autopm calls if scanning was cancelled */ if (test_bit(US_FLIDX_SCAN_PENDING, &us->dflags)) usb_autopm_put_interface_no_suspend(us->pusb_intf); /* * Removing the host will perform an orderly shutdown: caches * synchronized, disks spun down, etc. */ scsi_remove_host(host); /* * Prevent any new commands from being accepted and cut short * reset delays. */ scsi_lock(host); set_bit(US_FLIDX_DISCONNECTING, &us->dflags); scsi_unlock(host); wake_up(&us->delay_wait); }

Contributors

PersonTokensPropCommitsCommitProp
Alan Stern6656.90%562.50%
Matthew Dharm4740.52%225.00%
Felipe Balbi32.59%112.50%
Total116100.00%8100.00%

/* Second stage of disconnect processing: deallocate all resources */
static void release_everything(struct us_data *us) { usb_stor_release_resources(us); dissociate_dev(us); /* * Drop our reference to the host; the SCSI core will free it * (and "us" along with it) when the refcount becomes 0. */ scsi_host_put(us_to_host(us)); }

Contributors

PersonTokensPropCommitsCommitProp
Matthew Dharm2996.67%150.00%
Felipe Balbi13.33%150.00%
Total30100.00%2100.00%

/* Delayed-work routine to carry out SCSI-device scanning */
static void usb_stor_scan_dwork(struct work_struct *work) { struct us_data *us = container_of(work, struct us_data, scan_dwork.work); struct device *dev = &us->pusb_intf->dev; dev_dbg(dev, "starting scan\n"); /* For bulk-only devices, determine the max LUN value */ if (us->protocol == USB_PR_BULK && !(us->fflags & US_FL_SINGLE_LUN) && !(us->fflags & US_FL_SCM_MULT_TARG)) { mutex_lock(&us->dev_mutex); us->max_lun = usb_stor_Bulk_max_lun(us); /* * Allow proper scanning of devices that present more than 8 LUNs * While not affecting other devices that may need the previous * behavior */ if (us->max_lun >= 8) us_to_host(us)->max_lun = us->max_lun+1; mutex_unlock(&us->dev_mutex); } scsi_scan_host(us_to_host(us)); dev_dbg(dev, "scan complete\n"); /* Should we unbind if no devices were detected? */ usb_autopm_put_interface(us->pusb_intf); clear_bit(US_FLIDX_SCAN_PENDING, &us->dflags); }

Contributors

PersonTokensPropCommitsCommitProp
Alan Stern6843.59%533.33%
Matthew Dharm3522.44%320.00%
Oliver Neukum2113.46%16.67%
Felipe Contreras95.77%16.67%
Mark Knibbs95.77%16.67%
Matthew Wilcox85.13%16.67%
Arjan van de Ven42.56%16.67%
Michal Nazarewicz10.64%16.67%
Felipe Balbi10.64%16.67%
Total156100.00%15100.00%


static unsigned int usb_stor_sg_tablesize(struct usb_interface *intf) { struct usb_device *usb_dev = interface_to_usbdev(intf); if (usb_dev->bus->sg_tablesize) { return usb_dev->bus->sg_tablesize; } return SG_ALL; }

Contributors

PersonTokensPropCommitsCommitProp
David Vrabel42100.00%1100.00%
Total42100.00%1100.00%

/* First part of general USB mass-storage probing */
int usb_stor_probe1(struct us_data **pus, struct usb_interface *intf, const struct usb_device_id *id, struct us_unusual_dev *unusual_dev, struct scsi_host_template *sht) { struct Scsi_Host *host; struct us_data *us; int result; dev_info(&intf->dev, "USB Mass Storage device detected\n"); /* * Ask the SCSI layer to allocate a host structure, with extra * space at the end for our private us_data structure. */ host = scsi_host_alloc(sht, sizeof(*us)); if (!host) { dev_warn(&intf->dev, "Unable to allocate the scsi host\n"); return -ENOMEM; } /* * Allow 16-byte CDBs and thus > 2TB */ host->max_cmd_len = 16; host->sg_tablesize = usb_stor_sg_tablesize(intf); *pus = us = host_to_us(host); mutex_init(&(us->dev_mutex)); us_set_lock_class(&us->dev_mutex, intf); init_completion(&us->cmnd_ready); init_completion(&(us->notify)); init_waitqueue_head(&us->delay_wait); INIT_DELAYED_WORK(&us->scan_dwork, usb_stor_scan_dwork); /* Associate the us_data structure with the USB device */ result = associate_dev(us, intf); if (result) goto BadDevice; /* Get the unusual_devs entries and the descriptors */ result = get_device_info(us, id, unusual_dev); if (result) goto BadDevice; /* Get standard transport and protocol settings */ get_transport(us); get_protocol(us); /* * Give the caller a chance to fill in specialized transport * or protocol settings. */ return 0; BadDevice: usb_stor_dbg(us, "storage_probe() failed\n"); release_everything(us); return result; }

Contributors

PersonTokensPropCommitsCommitProp
Matthew Dharm11246.67%934.62%
Alan Stern5422.50%415.38%
Linus Torvalds (pre-git)135.42%311.54%
Lei Ming104.17%13.85%
Daniel Drake93.75%13.85%
Joe Perches93.75%13.85%
David Vrabel93.75%13.85%
Felipe Contreras72.92%13.85%
Richard Sharpe72.92%13.85%
Akinobu Mita62.50%13.85%
Arjan van de Ven20.83%13.85%
Pete Zaitcev10.42%13.85%
Felipe Balbi10.42%13.85%
Total240100.00%26100.00%

EXPORT_SYMBOL_GPL(usb_stor_probe1); /* Second part of general USB mass-storage probing */
int usb_stor_probe2(struct us_data *us) { int result; struct device *dev = &us->pusb_intf->dev; /* Make sure the transport and protocol have both been set */ if (!us->transport || !us->proto_handler) { result = -ENXIO; goto BadDevice; } usb_stor_dbg(us, "Transport: %s\n", us->transport_name); usb_stor_dbg(us, "Protocol: %s\n", us->protocol_name); if (us->fflags & US_FL_SCM_MULT_TARG) { /* * SCM eUSCSI bridge devices can have different numbers * of LUNs on different targets; allow all to be probed. */ us->max_lun = 7; /* The eUSCSI itself has ID 7, so avoid scanning that */ us_to_host(us)->this_id = 7; /* max_id is 8 initially, so no need to set it here */ } else { /* In the normal case there is only a single target */ us_to_host(us)->max_id = 1; /* * Like Windows, we won't store the LUN bits in CDB[1] for * SCSI-2 devices using the Bulk-Only transport (even though * this violates the SCSI spec). */ if (us->transport == usb_stor_Bulk_transport) us_to_host(us)->no_scsi2_lun_in_cdb = 1; } /* fix for single-lun devices */ if (us->fflags & US_FL_SINGLE_LUN) us->max_lun = 0; /* Find the endpoints and calculate pipe values */ result = get_pipes(us); if (result) goto BadDevice; /* * If the device returns invalid data for the first READ(10) * command, indicate the command should be retried. */ if (us->fflags & US_FL_INITIAL_READ10) set_bit(US_FLIDX_REDO_READ10, &us->dflags); /* Acquire all the other resources and add the host */ result = usb_stor_acquire_resources(us); if (result) goto BadDevice; usb_autopm_get_interface_no_resume(us->pusb_intf); snprintf(us->scsi_name, sizeof(us->scsi_name), "usb-storage %s", dev_name(&us->pusb_intf->dev)); result = scsi_add_host(us_to_host(us), dev); if (result) { dev_warn(dev, "Unable to add the scsi host\n"); goto HostAddErr; } /* Submit the delayed_work for SCSI-device scanning */ set_bit(US_FLIDX_SCAN_PENDING, &us->dflags); if (delay_use > 0) dev_dbg(dev, "waiting for device to settle before scanning\n"); queue_delayed_work(system_freezable_wq, &us->scan_dwork, delay_use * HZ); return 0; /* We come here if there are any problems */ HostAddErr: usb_autopm_put_interface_no_suspend(us->pusb_intf); BadDevice: usb_stor_dbg(us, "storage_probe() failed\n"); release_everything(us); return result; }

Contributors

PersonTokensPropCommitsCommitProp
Alan Stern12338.20%728.00%
Matthew Dharm7322.67%832.00%
Mark Knibbs3410.56%14.00%
Matthew Wilcox268.07%14.00%
Heiner Kallweit175.28%14.00%
Felipe Contreras175.28%14.00%
Ming Lei113.42%14.00%
Linus Torvalds (pre-git)92.80%28.00%
Joe Perches92.80%14.00%
Greg Kroah-Hartman20.62%14.00%
Christoph Hellwig10.31%14.00%
Total322100.00%25100.00%

EXPORT_SYMBOL_GPL(usb_stor_probe2); /* Handle a USB mass-storage disconnect */
void usb_stor_disconnect(struct usb_interface *intf) { struct us_data *us = usb_get_intfdata(intf); quiesce_and_remove_host(us); release_everything(us); }

Contributors

PersonTokensPropCommitsCommitProp
Matthew Dharm1653.33%562.50%
Linus Torvalds (pre-git)1136.67%112.50%
Greg Kroah-Hartman26.67%112.50%
Alan Stern13.33%112.50%
Total30100.00%8100.00%

EXPORT_SYMBOL_GPL(usb_stor_disconnect); static struct scsi_host_template usb_stor_host_template; /* The main probe routine for standard devices */
static int storage_probe(struct usb_interface *intf, const struct usb_device_id *id) { struct us_unusual_dev *unusual_dev; struct us_data *us; int result; int size; /* If uas is enabled and this device can do uas then ignore it. */ #if IS_ENABLED(CONFIG_USB_UAS) if (uas_use_uas_driver(intf, id, NULL)) return -ENXIO; #endif /* * If the device isn't standard (is handled by a subdriver * module) then don't accept it. */ if (usb_usual_ignore_device(intf)) return -ENXIO; /* * Call the general probe procedures. * * The unusual_dev_list array is parallel to the usb_storage_usb_ids * table, so we use the index of the id entry to find the * corresponding unusual_devs entry. */ size = ARRAY_SIZE(us_unusual_dev_list); if (id >= usb_storage_usb_ids && id < usb_storage_usb_ids + size) { unusual_dev = (id - usb_storage_usb_ids) + us_unusual_dev_list; } else { unusual_dev = &for_dynamic_ids; dev_dbg(&intf->dev, "Use Bulk-Only transport with the Transparent SCSI protocol for dynamic id: 0x%04x 0x%04x\n", id->idVendor, id->idProduct); } result = usb_stor_probe1(&us, intf, id, unusual_dev, &usb_stor_host_template); if (result) return result; /* No special transport or protocol settings in the main module */ result = usb_stor_probe2(us); return result; }

Contributors

PersonTokensPropCommitsCommitProp
Alan Stern6841.98%114.29%
Huajun Li6037.04%114.29%
Hans de Goede2414.81%228.57%
Joe Perches63.70%114.29%
Akinobu Mita31.85%114.29%
Sebastian Andrzej Siewior10.62%114.29%
Total162100.00%7100.00%

static struct usb_driver usb_storage_driver = { .name = DRV_NAME, .probe = storage_probe, .disconnect = usb_stor_disconnect, .suspend = usb_stor_suspend, .resume = usb_stor_resume, .reset_resume = usb_stor_reset_resume, .pre_reset = usb_stor_pre_reset, .post_reset = usb_stor_post_reset, .id_table = usb_storage_usb_ids, .supports_autosuspend = 1, .soft_unbind = 1, }; module_usb_stor_driver(usb_storage_driver, usb_stor_host_template, DRV_NAME);

Overall Contributors

PersonTokensPropCommitsCommitProp
Matthew Dharm136832.09%3325.38%
Alan Stern117827.63%3023.08%
Linus Torvalds50111.75%64.62%
Linus Torvalds (pre-git)1804.22%86.15%
Lei Ming1313.07%10.77%
Felipe Contreras1293.03%21.54%
Hans de Goede1102.58%75.38%
Joe Perches731.71%10.77%
Huajun Li721.69%10.77%
David Vrabel511.20%10.77%
Greg Kroah-Hartman451.06%43.08%
Mark Knibbs431.01%10.77%
Matthew Wilcox420.99%21.54%
Oliver Neukum310.73%21.54%
Daniel Drake290.68%10.77%
fangxiaozhi290.68%10.77%
Pete Zaitcev240.56%10.77%
Akinobu Mita240.56%10.77%
Phil Dibowitz200.47%21.54%
Karl Relton200.47%10.77%
Felipe Balbi200.47%10.77%
Arjan van de Ven200.47%21.54%
Heiner Kallweit170.40%10.77%
Michal Nazarewicz150.35%10.77%
Luben Tuikov150.35%10.77%
Ming Lei110.26%10.77%
Namjae Jeon100.23%10.77%
Julia Lawall90.21%10.77%
David Brownell80.19%10.77%
Luiz Fernando N. Capitulino80.19%10.77%
Richard Sharpe70.16%10.77%
Manfred Spraul50.12%10.77%
Kevin Lloyd30.07%10.77%
Dan J Williams30.07%10.77%
Andrew Lunn20.05%10.77%
Daniel Mack20.05%10.77%
Hannes Reinecke20.05%10.77%
Sam Ravnborg10.02%10.77%
Christoph Hellwig10.02%10.77%
Jeffrin Jose10.02%10.77%
Sebastian Andrzej Siewior10.02%10.77%
Jens Axboe10.02%10.77%
Adrian Bunk10.02%10.77%
Total4263100.00%130100.00%
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with cregit.