📄 usb.c
字号:
/* Driver for USB Mass Storage compliant devices * * $Id: usb.c,v 1.57 2000/11/21 02:56:41 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) * * 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 <linux/config.h>#include "usb.h"#include "scsiglue.h"#include "transport.h"#include "protocol.h"#include "debug.h"#include "initializers.h"#ifdef CONFIG_USB_STORAGE_HP8200e#include "shuttle_usbat.h"#endif#ifdef CONFIG_USB_STORAGE_SDDR09#include "sddr09.h"#endif#ifdef CONFIG_USB_STORAGE_DPCM#include "dpcm.h"#endif#ifdef CONFIG_USB_STORAGE_FREECOM#include "freecom.h"#endif#include <linux/module.h>#include <linux/sched.h>#include <linux/errno.h>#include <linux/init.h>#include <linux/malloc.h>/* Some informational data */MODULE_AUTHOR("Matthew Dharm <mdharm-usb@one-eyed-alien.net>");MODULE_DESCRIPTION("USB Mass Storage driver for Linux");/* * Per device data */static int my_host_number;/* * 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/* The list of structures and the protective lock for them */struct us_data *us_list;struct semaphore us_list_semaphore;static void * storage_probe(struct usb_device *dev, unsigned int ifnum, const struct usb_device_id *id);static void storage_disconnect(struct usb_device *dev, void *ptr);struct usb_driver usb_storage_driver = { name: "usb-storage", probe: storage_probe, disconnect: storage_disconnect,};/* * 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) { int i; struct scatterlist *sg; int len = us->srb->request_bufflen > data_len ? data_len : us->srb->request_bufflen; int transferred; int amt; if (data_len<36) // You lose. return; memcpy(data+8, us->unusual_dev->vendorName, strlen(us->unusual_dev->vendorName) > 8 ? 8 : strlen(us->unusual_dev->vendorName)); memcpy(data+16, us->unusual_dev->productName, strlen(us->unusual_dev->productName) > 16 ? 16 : strlen(us->unusual_dev->productName)); data[32] = 0x30 + ((us->pusb_dev->descriptor.bcdDevice>>12) & 0x0F); data[33] = 0x30 + ((us->pusb_dev->descriptor.bcdDevice>>8) & 0x0F); data[34] = 0x30 + ((us->pusb_dev->descriptor.bcdDevice>>4) & 0x0F); data[35] = 0x30 + ((us->pusb_dev->descriptor.bcdDevice) & 0x0F); if (us->srb->use_sg) { sg = (struct scatterlist *)us->srb->request_buffer; for (i=0; i<us->srb->use_sg; i++) memset(sg[i].address, 0, sg[i].length); for (i=0, transferred=0; i<us->srb->use_sg && transferred < len; i++) { amt = sg[i].length > len-transferred ? len-transferred : sg[i].length; memcpy(sg[i].address, data+transferred, amt); transferred -= amt; } } else { memset(us->srb->request_buffer, 0, us->srb->request_bufflen); memcpy(us->srb->request_buffer, data, len); }}static int usb_stor_control_thread(void * __us){ wait_queue_t wait; struct us_data *us = (struct us_data *)__us; int action; lock_kernel(); /* * This thread doesn't need any user-level access, * so get rid of all our resources.. */ exit_files(current); current->files = init_task.files; atomic_inc(¤t->files->count); daemonize(); /* set our name for identification purposes */ sprintf(current->comm, "usb-storage-%d", us->host_number); unlock_kernel(); /* set up for wakeups by new commands */ init_waitqueue_entry(&wait, current); init_waitqueue_head(&(us->wqh)); add_wait_queue(&(us->wqh), &wait); /* signal that we've started the thread */ up(&(us->notify)); set_current_state(TASK_INTERRUPTIBLE); for(;;) { US_DEBUGP("*** thread sleeping.\n"); schedule(); US_DEBUGP("*** thread awakened.\n"); /* lock access to the queue element */ down(&(us->queue_exclusion)); /* take the command off the queue */ action = us->action; us->action = 0; us->srb = us->queue_srb; /* release the queue lock as fast as possible */ up(&(us->queue_exclusion)); switch (action) { case US_ACT_COMMAND: /* reject the command if the direction indicator * is UNKNOWN */ if (us->srb->sc_data_direction == SCSI_DATA_UNKNOWN) { US_DEBUGP("UNKNOWN data direction\n"); us->srb->result = DID_ERROR << 16; set_current_state(TASK_INTERRUPTIBLE); us->srb->scsi_done(us->srb); us->srb = NULL; break; } /* reject if target != 0 or if LUN is higher than * the maximum known LUN */ if (us->srb->target && !(us->flags & US_FL_SCM_MULT_TARG)) { US_DEBUGP("Bad target number (%d/%d)\n", us->srb->target, us->srb->lun); us->srb->result = DID_BAD_TARGET << 16; set_current_state(TASK_INTERRUPTIBLE); us->srb->scsi_done(us->srb); us->srb = NULL; break; } if (us->srb->lun > us->max_lun) { US_DEBUGP("Bad LUN (%d/%d)\n", us->srb->target, us->srb->lun); us->srb->result = DID_BAD_TARGET << 16; set_current_state(TASK_INTERRUPTIBLE); us->srb->scsi_done(us->srb); us->srb = NULL; break; } /* handle those devices which can't do a START_STOP */ if ((us->srb->cmnd[0] == START_STOP) && (us->flags & US_FL_START_STOP)) { US_DEBUGP("Skipping START_STOP command\n"); us->srb->result = GOOD << 1; set_current_state(TASK_INTERRUPTIBLE); us->srb->scsi_done(us->srb); us->srb = NULL; break; } /* lock the device pointers */ down(&(us->dev_semaphore)); /* our device has gone - pretend not ready */ if (!us->pusb_dev) { US_DEBUGP("Request is for removed device\n"); /* For REQUEST_SENSE, it's the data. But * for anything else, it should look like * we auto-sensed for it. */ if (us->srb->cmnd[0] == REQUEST_SENSE) { memcpy(us->srb->request_buffer, usb_stor_sense_notready, sizeof(usb_stor_sense_notready)); us->srb->result = GOOD << 1; } else { memcpy(us->srb->sense_buffer, usb_stor_sense_notready, sizeof(usb_stor_sense_notready)); us->srb->result = CHECK_CONDITION << 1; } } else { /* !us->pusb_dev */ /* we've got a command, let's do it! */ US_DEBUG(usb_stor_show_command(us->srb)); us->proto_handler(us->srb, us); } /* unlock the device pointers */ up(&(us->dev_semaphore)); /* indicate that the command is done */ if (us->srb->result != DID_ABORT << 16) { US_DEBUGP("scsi cmd done, result=0x%x\n", us->srb->result); set_current_state(TASK_INTERRUPTIBLE); us->srb->scsi_done(us->srb); } else { US_DEBUGP("scsi command aborted\n"); set_current_state(TASK_INTERRUPTIBLE); up(&(us->notify)); } us->srb = NULL; break; case US_ACT_DEVICE_RESET: break; case US_ACT_BUS_RESET: break; case US_ACT_HOST_RESET: break; } /* end switch on action */ /* exit if we get a signal to exit */ if (action == US_ACT_EXIT) { US_DEBUGP("-- US_ACT_EXIT command recieved\n"); break; } } /* for (;;) */ /* clean up after ourselves */ set_current_state(TASK_INTERRUPTIBLE); remove_wait_queue(&(us->wqh), &wait); /* notify the exit routine that we're actually exiting now */ up(&(us->notify)); return 0;} /* This is the list of devices we recognize, along with their flag data *//* 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_DUMMY_INQUIRY flag, then the vendor and product names * normally generated by a device thorugh 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. */static struct us_unusual_dev us_unusual_dev_list[] = { { 0x03ee, 0x0000, 0x0000, 0x0245, "Mitsumi", "CD-R/RW Drive", US_SC_8020, US_PR_CBI, NULL, 0}, { 0x03f0, 0x0107, 0x0200, 0x0200, "HP", "CD-Writer+", US_SC_8070, US_PR_CB, NULL, 0}, #ifdef CONFIG_USB_STORAGE_HP8200e { 0x03f0, 0x0207, 0x0001, 0x0001, "HP", "CD-Writer+ 8200e", US_SC_8070, US_PR_SCM_ATAPI, init_8200e, 0}, #endif { 0x04e6, 0x0001, 0x0200, 0x0200, "Matshita", "LS-120", US_SC_8020, US_PR_CB, NULL, 0}, { 0x04e6, 0x0002, 0x0100, 0x0100, "Shuttle", "eUSCSI Bridge", US_SC_SCSI, US_PR_BULK, usb_stor_euscsi_init, US_FL_SCM_MULT_TARG }, #ifdef CONFIG_USB_STORAGE_SDDR09 { 0x04e6, 0x0003, 0x0000, 0x9999, "Sandisk", "ImageMate SDDR09", US_SC_SCSI, US_PR_EUSB_SDDR09, NULL, US_FL_SINGLE_LUN | US_FL_START_STOP },#endif#ifdef CONFIG_USB_STORAGE_DPCM { 0x0436, 0x0005, 0x0100, 0x0100, "Microtech", "CameraMate (DPCM_USB)", US_SC_SCSI, US_PR_DPCM_USB, NULL, US_FL_START_STOP },#endif { 0x04e6, 0x0006, 0x0100, 0x0200, "Shuttle", "eUSB MMC Adapter", US_SC_SCSI, US_PR_CB, NULL, US_FL_SINGLE_LUN}, { 0x04e6, 0x0007, 0x0100, 0x0200, "Sony", "Hifd", US_SC_SCSI, US_PR_CB, NULL, US_FL_SINGLE_LUN}, { 0x04e6, 0x0009, 0x0200, 0x0200, "Shuttle",
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -