📄 scsiglue.c
字号:
/* Driver for USB Mass Storage compliant devices * SCSI layer glue code * * $Id: scsiglue.c,v 1.24 2001/11/11 03:33:58 mdharm Exp $ * * Current development and maintenance by: * (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net) * * Developed with the assistance of: * (c) 2000 David L. Brown, Jr. (usb-storage@davidb.org) * (c) 2000 Stephen J. Gowdy (SGowdy@lbl.gov) * * Initial work by: * (c) 1999 Michael Gee (michael@linuxspecific.com) * * 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. */#include "scsiglue.h"#include "usb.h"#include "debug.h"#include "transport.h"#include <linux/slab.h>/* * kernel thread actions */#define US_ACT_COMMAND 1#define US_ACT_DEVICE_RESET 2#define US_ACT_BUS_RESET 3#define US_ACT_HOST_RESET 4#define US_ACT_EXIT 5/*********************************************************************** * Host functions ***********************************************************************/static const char* host_info(struct Scsi_Host *host){ return "SCSI emulation for USB Mass Storage devices";}/* detect a virtual adapter (always works) */static int detect(struct SHT *sht){ struct us_data *us; char local_name[32]; /* Note: this function gets called with io_request_lock spinlock helt! */ /* This is not nice at all, but how else are we to get the * data here? */ us = (struct us_data *)sht->proc_dir; /* set up the name of our subdirectory under /proc/scsi/ */ sprintf(local_name, "usb-storage-%d", us->host_number); sht->proc_name = kmalloc (strlen(local_name) + 1, GFP_ATOMIC); if (!sht->proc_name) return 0; strcpy(sht->proc_name, local_name); /* we start with no /proc directory entry */ sht->proc_dir = NULL; /* register the host */ us->host = scsi_register(sht, sizeof(us)); if (us->host) { us->host->hostdata[0] = (unsigned long)us; us->host_no = us->host->host_no; return 1; } /* odd... didn't register properly. Abort and free pointers */ kfree(sht->proc_name); sht->proc_name = NULL; return 0;}/* Release all resources used by the virtual host * * NOTE: There is no contention here, because we're already deregistered * the driver and we're doing each virtual host in turn, not in parallel */static int release(struct Scsi_Host *psh){ struct us_data *us = (struct us_data *)psh->hostdata[0]; US_DEBUGP("release() called for host %s\n", us->htmplt.name); /* Kill the control threads * * Enqueue the command, wake up the thread, and wait for * notification that it's exited. */ US_DEBUGP("-- sending US_ACT_EXIT command to thread\n"); us->action = US_ACT_EXIT; up(&(us->sema)); wait_for_completion(&(us->notify)); /* remove the pointer to the data structure we were using */ psh->hostdata[0] = (unsigned long)NULL; /* we always have a successful release */ return 0;}/* run command */static int command( Scsi_Cmnd *srb ){ US_DEBUGP("Bad use of us_command\n"); return DID_BAD_TARGET << 16;}/* run command */static int queuecommand( Scsi_Cmnd *srb , void (*done)(Scsi_Cmnd *)){ struct us_data *us = (struct us_data *)srb->host->hostdata[0]; unsigned long flags; US_DEBUGP("queuecommand() called\n"); srb->host_scribble = (unsigned char *)us; /* get exclusive access to the structures we want */ spin_lock_irqsave(&(us->queue_exclusion), flags); /* enqueue the command */ us->queue_srb = srb; srb->scsi_done = done; us->action = US_ACT_COMMAND; /* release the lock on the structure */ spin_unlock_irqrestore(&(us->queue_exclusion), flags); /* wake up the process task */ up(&(us->sema)); return 0;}/*********************************************************************** * Error handling functions ***********************************************************************//* Command abort */static int command_abort( Scsi_Cmnd *srb ){ struct us_data *us = (struct us_data *)srb->host->hostdata[0]; US_DEBUGP("command_abort() called\n"); /* if we're stuck waiting for an IRQ, simulate it */ if (atomic_read(us->ip_wanted)) { US_DEBUGP("-- simulating missing IRQ\n"); up(&(us->ip_waitq)); } /* if the device has been removed, this worked */ if (!us->pusb_dev) { US_DEBUGP("-- device removed already\n"); return SUCCESS; } /* if we have an urb pending, let's wake the control thread up */ if (!us->current_done.done) { atomic_inc(&us->abortcnt); spin_unlock_irq(&io_request_lock); /* cancel the URB -- this will automatically wake the thread */ usb_unlink_urb(us->current_urb); /* wait for us to be done */ wait_for_completion(&(us->notify)); spin_lock_irq(&io_request_lock); atomic_dec(&us->abortcnt); return SUCCESS; } US_DEBUGP ("-- nothing to abort\n"); return FAILED;}/* This invokes the transport reset mechanism to reset the state of the * device */static int device_reset( Scsi_Cmnd *srb ){ struct us_data *us = (struct us_data *)srb->host->hostdata[0]; int rc; US_DEBUGP("device_reset() called\n" ); spin_unlock_irq(&io_request_lock); down(&(us->dev_semaphore)); if (!us->pusb_dev) { up(&(us->dev_semaphore)); spin_lock_irq(&io_request_lock); return SUCCESS; } rc = us->transport_reset(us); up(&(us->dev_semaphore)); spin_lock_irq(&io_request_lock); return rc;}/* This resets the device port, and simulates the device * disconnect/reconnect for all drivers which have claimed other * interfaces. */static int bus_reset( Scsi_Cmnd *srb ){ struct us_data *us = (struct us_data *)srb->host->hostdata[0]; int i; int result; /* we use the usb_reset_device() function to handle this for us */ US_DEBUGP("bus_reset() called\n"); spin_unlock_irq(&io_request_lock); down(&(us->dev_semaphore)); /* if the device has been removed, this worked */ if (!us->pusb_dev) { US_DEBUGP("-- device removed already\n"); up(&(us->dev_semaphore)); spin_lock_irq(&io_request_lock); return SUCCESS; } /* The USB subsystem doesn't handle synchronisation between * a device's several drivers. Therefore we reset only devices * with just one interface, which we of course own. */ if (us->pusb_dev->actconfig->bNumInterfaces != 1) { printk(KERN_NOTICE "usb-storage: " "Refusing to reset a multi-interface device\n"); up(&(us->dev_semaphore)); spin_lock_irq(&io_request_lock); /* XXX Don't just return success, make sure current cmd fails */ return SUCCESS; } /* release the IRQ, if we have one */ if (us->irq_urb) { US_DEBUGP("-- releasing irq URB\n"); result = usb_unlink_urb(us->irq_urb); US_DEBUGP("-- usb_unlink_urb() returned %d\n", result); } /* attempt to reset the port */ if (usb_reset_device(us->pusb_dev) < 0) { /* * Do not return errors, or else the error handler might * invoke host_reset, which is not implemented. */ goto bail_out; } /* FIXME: This needs to lock out driver probing while it's working * or we can have race conditions */ for (i = 0; i < us->pusb_dev->actconfig->bNumInterfaces; i++) { struct usb_interface *intf = &us->pusb_dev->actconfig->interface[i]; const struct usb_device_id *id; /* if this is an unclaimed interface, skip it */ if (!intf->driver) { continue; } US_DEBUGP("Examinging driver %s...", intf->driver->name); /* skip interfaces which we've claimed */ if (intf->driver == &usb_storage_driver) { US_DEBUGPX("skipping ourselves.\n"); continue; } /* simulate a disconnect and reconnect for all interfaces */ US_DEBUGPX("simulating disconnect/reconnect.\n"); down(&intf->driver->serialize); intf->driver->disconnect(us->pusb_dev, intf->private_data); id = usb_match_id(us->pusb_dev, intf, intf->driver->id_table); intf->driver->probe(us->pusb_dev, i, id); up(&intf->driver->serialize); }bail_out: /* re-allocate the IRQ URB and submit it to restore connectivity * for CBI devices */ if (us->protocol == US_PR_CBI) { us->irq_urb->dev = us->pusb_dev; result = usb_submit_urb(us->irq_urb); US_DEBUGP("usb_submit_urb() returns %d\n", result); } up(&(us->dev_semaphore)); spin_lock_irq(&io_request_lock); US_DEBUGP("bus_reset() complete\n"); return SUCCESS;}/* FIXME: This doesn't do anything right now */static int host_reset( Scsi_Cmnd *srb ){ printk(KERN_CRIT "usb-storage: host_reset() requested but not implemented\n" ); return FAILED;}/*********************************************************************** * /proc/scsi/ functions ***********************************************************************//* we use this macro to help us write into the buffer */#undef SPRINTF#define SPRINTF(args...) \ do { if (pos < buffer+length) pos += sprintf(pos, ## args); } while (0)static int proc_info (char *buffer, char **start, off_t offset, int length, int hostno, int inout){ struct us_data *us; char *pos = buffer; /* if someone is sending us data, just throw it away */ if (inout) return length; /* lock the data structures */ down(&us_list_semaphore); /* find our data from hostno */ us = us_list; while (us) { if (us->host_no == hostno) break; us = us->next; } /* release our lock on the data structures */ up(&us_list_semaphore); /* if we couldn't find it, we return an error */ if (!us) { return -ESRCH; } /* print the controller name */ SPRINTF(" Host scsi%d: usb-storage\n", hostno); /* print product, vendor, and serial number strings */ SPRINTF(" Vendor: %s\n", us->vendor); SPRINTF(" Product: %s\n", us->product); SPRINTF("Serial Number: %s\n", us->serial); /* show the protocol and transport */ SPRINTF(" Protocol: %s\n", us->protocol_name); SPRINTF(" Transport: %s\n", us->transport_name); /* show the GUID of the device */ SPRINTF(" GUID: " GUID_FORMAT "\n", GUID_ARGS(us->guid)); SPRINTF(" Attached: %s\n", us->pusb_dev ? "Yes" : "No"); /* * Calculate start of next buffer, and return value. */ *start = buffer + offset; if ((pos - buffer) < offset) return (0); else if ((pos - buffer - offset) < length) return (pos - buffer - offset); else return (length);}/* * this defines our 'host' */Scsi_Host_Template usb_stor_host_template = { name: "usb-storage", proc_info: proc_info, info: host_info, detect: detect, release: release, command: command, queuecommand: queuecommand, eh_abort_handler: command_abort, eh_device_reset_handler:device_reset, eh_bus_reset_handler: bus_reset, eh_host_reset_handler: host_reset, can_queue: 1, this_id: -1, sg_tablesize: SG_ALL, cmd_per_lun: 1, present: 0, unchecked_isa_dma: FALSE, use_clustering: TRUE, use_new_eh_code: TRUE, emulated: TRUE};unsigned char usb_stor_sense_notready[18] = { [0] = 0x70, /* current error */ [2] = 0x02, /* not ready */ [5] = 0x0a, /* additional length */ [10] = 0x04, /* not ready */ [11] = 0x03 /* manual intervention */};#define USB_STOR_SCSI_SENSE_HDRSZ 4#define USB_STOR_SCSI_SENSE_10_HDRSZ 8struct usb_stor_scsi_sense_hdr{ __u8* dataLength; __u8* mediumType; __u8* devSpecParms; __u8* blkDescLength;};typedef struct usb_stor_scsi_sense_hdr Usb_Stor_Scsi_Sense_Hdr;union usb_stor_scsi_sense_hdr_u{ Usb_Stor_Scsi_Sense_Hdr hdr; __u8* array[USB_STOR_SCSI_SENSE_HDRSZ];};
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -