📄 ub.c
字号:
/* * The low performance USB storage driver (ub). * * Copyright (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net) * Copyright (C) 2004 Pete Zaitcev (zaitcev@yahoo.com) * * This work is a part of Linux kernel, is derived from it, * and is not licensed separately. See file COPYING for details. * * TODO (sorted by decreasing priority) * -- Kill first_open (Al Viro fixed the block layer now) * -- Do resets with usb_device_reset (needs a thread context, use khubd) * -- set readonly flag for CDs, set removable flag for CF readers * -- do inquiry and verify we got a disk and not a tape (for LUN mismatch) * -- special case some senses, e.g. 3a/0 -> no media present, reduce retries * -- verify the 13 conditions and do bulk resets * -- kill last_pipe and simply do two-state clearing on both pipes * -- verify protocol (bulk) from USB descriptors (maybe...) * -- highmem * -- move top_sense and work_bcs into separate allocations (if they survive) * for cache purists and esoteric architectures. * -- Allocate structure for LUN 0 before the first ub_sync_tur, avoid NULL. ? * -- prune comments, they are too volumnous * -- Exterminate P3 printks * -- Resove XXX's * -- Redo "benh's retries", perhaps have spin-up code to handle them. V:D=? * -- CLEAR, CLR2STS, CLRRS seem to be ripe for refactoring. */#include <linux/kernel.h>#include <linux/module.h>#include <linux/usb.h>#include <linux/blkdev.h>#include <linux/devfs_fs_kernel.h>#include <linux/timer.h>#include <scsi/scsi.h>#define DRV_NAME "ub"#define DEVFS_NAME DRV_NAME#define UB_MAJOR 180/* * The command state machine is the key model for understanding of this driver. * * The general rule is that all transitions are done towards the bottom * of the diagram, thus preventing any loops. * * An exception to that is how the STAT state is handled. A counter allows it * to be re-entered along the path marked with [C]. * * +--------+ * ! INIT ! * +--------+ * ! * ub_scsi_cmd_start fails ->--------------------------------------\ * ! ! * V ! * +--------+ ! * ! CMD ! ! * +--------+ ! * ! +--------+ ! * was -EPIPE -->-------------------------------->! CLEAR ! ! * ! +--------+ ! * ! ! ! * was error -->------------------------------------- ! --------->\ * ! ! ! * /--<-- cmd->dir == NONE ? ! ! * ! ! ! ! * ! V ! ! * ! +--------+ ! ! * ! ! DATA ! ! ! * ! +--------+ ! ! * ! ! +---------+ ! ! * ! was -EPIPE -->--------------->! CLR2STS ! ! ! * ! ! +---------+ ! ! * ! ! ! ! ! * ! ! was error -->---- ! --------->\ * ! was error -->--------------------- ! ------------- ! --------->\ * ! ! ! ! ! * ! V ! ! ! * \--->+--------+ ! ! ! * ! STAT !<--------------------------/ ! ! * /--->+--------+ ! ! * ! ! ! ! * [C] was -EPIPE -->-----------\ ! ! * ! ! ! ! ! * +<---- len == 0 ! ! ! * ! ! ! ! ! * ! was error -->--------------------------------------!---------->\ * ! ! ! ! ! * +<---- bad CSW ! ! ! * +<---- bad tag ! ! ! * ! ! V ! ! * ! ! +--------+ ! ! * ! ! ! CLRRS ! ! ! * ! ! +--------+ ! ! * ! ! ! ! ! * \------- ! --------------------[C]--------\ ! ! * ! ! ! ! * cmd->error---\ +--------+ ! ! * ! +--------------->! SENSE !<----------/ ! * STAT_FAIL----/ +--------+ ! * ! ! V * ! V +--------+ * \--------------------------------\--------------------->! DONE ! * +--------+ *//* * Definitions which have to be scattered once we understand the layout better. *//* Transport (despite PR in the name) */#define US_PR_BULK 0x50 /* bulk only *//* Protocol */#define US_SC_SCSI 0x06 /* Transparent *//* * This many LUNs per USB device. * Every one of them takes a host, see UB_MAX_HOSTS. */#define UB_MAX_LUNS 9/* */#define UB_MINORS_PER_MAJOR 8#define UB_MAX_CDB_SIZE 16 /* Corresponds to Bulk */#define UB_SENSE_SIZE 18/* *//* command block wrapper */struct bulk_cb_wrap { __le32 Signature; /* contains 'USBC' */ u32 Tag; /* unique per command id */ __le32 DataTransferLength; /* size of data */ u8 Flags; /* direction in bit 0 */ u8 Lun; /* LUN */ u8 Length; /* of of the CDB */ u8 CDB[UB_MAX_CDB_SIZE]; /* max command */};#define US_BULK_CB_WRAP_LEN 31#define US_BULK_CB_SIGN 0x43425355 /*spells out USBC */#define US_BULK_FLAG_IN 1#define US_BULK_FLAG_OUT 0/* command status wrapper */struct bulk_cs_wrap { __le32 Signature; /* should = 'USBS' */ u32 Tag; /* same as original command */ __le32 Residue; /* amount not transferred */ u8 Status; /* see below */};#define US_BULK_CS_WRAP_LEN 13#define US_BULK_CS_SIGN 0x53425355 /* spells out 'USBS' */#define US_BULK_STAT_OK 0#define US_BULK_STAT_FAIL 1#define US_BULK_STAT_PHASE 2/* bulk-only class specific requests */#define US_BULK_RESET_REQUEST 0xff#define US_BULK_GET_MAX_LUN 0xfe/* */struct ub_dev;#define UB_MAX_REQ_SG 9 /* cdrecord requires 32KB and maybe a header */#define UB_MAX_SECTORS 64/* * A second is more than enough for a 32K transfer (UB_MAX_SECTORS) * even if a webcam hogs the bus, but some devices need time to spin up. */#define UB_URB_TIMEOUT (HZ*2)#define UB_DATA_TIMEOUT (HZ*5) /* ZIP does spin-ups in the data phase */#define UB_STAT_TIMEOUT (HZ*5) /* Same spinups and eject for a dataless cmd. */#define UB_CTRL_TIMEOUT (HZ/2) /* 500ms ought to be enough to clear a stall *//* * An instance of a SCSI command in transit. */#define UB_DIR_NONE 0#define UB_DIR_READ 1#define UB_DIR_ILLEGAL2 2#define UB_DIR_WRITE 3#define UB_DIR_CHAR(c) (((c)==UB_DIR_WRITE)? 'w': \ (((c)==UB_DIR_READ)? 'r': 'n'))enum ub_scsi_cmd_state { UB_CMDST_INIT, /* Initial state */ UB_CMDST_CMD, /* Command submitted */ UB_CMDST_DATA, /* Data phase */ UB_CMDST_CLR2STS, /* Clearing before requesting status */ UB_CMDST_STAT, /* Status phase */ UB_CMDST_CLEAR, /* Clearing a stall (halt, actually) */ UB_CMDST_CLRRS, /* Clearing before retrying status */ UB_CMDST_SENSE, /* Sending Request Sense */ UB_CMDST_DONE /* Final state */};static char *ub_scsi_cmd_stname[] = { ". ", "Cmd", "dat", "c2s", "sts", "clr", "crs", "Sen", "fin"};struct ub_scsi_cmd { unsigned char cdb[UB_MAX_CDB_SIZE]; unsigned char cdb_len; unsigned char dir; /* 0 - none, 1 - read, 3 - write. */ unsigned char trace_index; enum ub_scsi_cmd_state state; unsigned int tag; struct ub_scsi_cmd *next; int error; /* Return code - valid upon done */ unsigned int act_len; /* Return size */ unsigned char key, asc, ascq; /* May be valid if error==-EIO */ int stat_count; /* Retries getting status. */ unsigned int len; /* Requested length */ unsigned int current_sg; unsigned int nsg; /* sgv[nsg] */ struct scatterlist sgv[UB_MAX_REQ_SG]; struct ub_lun *lun; void (*done)(struct ub_dev *, struct ub_scsi_cmd *); void *back;};/* */struct ub_capacity { unsigned long nsec; /* Linux size - 512 byte sectors */ unsigned int bsize; /* Linux hardsect_size */ unsigned int bshift; /* Shift between 512 and hard sects */};/* * The SCSI command tracing structure. */#define SCMD_ST_HIST_SZ 8#define SCMD_TRACE_SZ 63 /* Less than 4KB of 61-byte lines */struct ub_scsi_cmd_trace { int hcur; unsigned int tag; unsigned int req_size, act_size; unsigned char op; unsigned char dir; unsigned char key, asc, ascq; char st_hst[SCMD_ST_HIST_SZ]; };struct ub_scsi_trace { int cur; struct ub_scsi_cmd_trace vec[SCMD_TRACE_SZ];};/* * This is a direct take-off from linux/include/completion.h * The difference is that I do not wait on this thing, just poll. * When I want to wait (ub_probe), I just use the stock completion. * * Note that INIT_COMPLETION takes no lock. It is correct. But why * in the bloody hell that thing takes struct instead of pointer to struct * is quite beyond me. I just copied it from the stock completion. */struct ub_completion { unsigned int done; spinlock_t lock;};static inline void ub_init_completion(struct ub_completion *x){ x->done = 0; spin_lock_init(&x->lock);}#define UB_INIT_COMPLETION(x) ((x).done = 0)static void ub_complete(struct ub_completion *x){ unsigned long flags; spin_lock_irqsave(&x->lock, flags); x->done++; spin_unlock_irqrestore(&x->lock, flags);}static int ub_is_completed(struct ub_completion *x){ unsigned long flags; int ret; spin_lock_irqsave(&x->lock, flags); ret = x->done; spin_unlock_irqrestore(&x->lock, flags); return ret;}/* */struct ub_scsi_cmd_queue { int qlen, qmax; struct ub_scsi_cmd *head, *tail;};/* * The block device instance (one per LUN). */struct ub_lun { struct ub_dev *udev; struct list_head link; struct gendisk *disk; int id; /* Host index */ int num; /* LUN number */ char name[16]; int changed; /* Media was changed */ int removable; int readonly; int first_open; /* Kludge. See ub_bd_open. */ /* Use Ingo's mempool if or when we have more than one command. */ /* * Currently we never need more than one command for the whole device. * However, giving every LUN a command is a cheap and automatic way * to enforce fairness between them. */ int cmda[1]; struct ub_scsi_cmd cmdv[1]; struct ub_capacity capacity; };/* * The USB device instance. */struct ub_dev { spinlock_t lock; atomic_t poison; /* The USB device is disconnected */ int openc; /* protected by ub_lock! */ /* kref is too implicit for our taste */ unsigned int tagcnt; char name[12]; struct usb_device *dev; struct usb_interface *intf; struct list_head luns; unsigned int send_bulk_pipe; /* cached pipe values */ unsigned int recv_bulk_pipe; unsigned int send_ctrl_pipe; unsigned int recv_ctrl_pipe; struct tasklet_struct tasklet; struct ub_scsi_cmd_queue cmd_queue; struct ub_scsi_cmd top_rqs_cmd; /* REQUEST SENSE */ unsigned char top_sense[UB_SENSE_SIZE]; struct ub_completion work_done; struct urb work_urb; struct timer_list work_timer; int last_pipe; /* What might need clearing */ __le32 signature; /* Learned signature */ struct bulk_cb_wrap work_bcb; struct bulk_cs_wrap work_bcs; struct usb_ctrlrequest work_cr; int sg_stat[6]; struct ub_scsi_trace tr;};/* */static void ub_cleanup(struct ub_dev *sc);static int ub_request_fn_1(struct ub_lun *lun, struct request *rq);static int ub_cmd_build_block(struct ub_dev *sc, struct ub_lun *lun, struct ub_scsi_cmd *cmd, struct request *rq);static int ub_cmd_build_packet(struct ub_dev *sc, struct ub_lun *lun, struct ub_scsi_cmd *cmd, struct request *rq);static void ub_rw_cmd_done(struct ub_dev *sc, struct ub_scsi_cmd *cmd);static void ub_end_rq(struct request *rq, int uptodate);static int ub_submit_scsi(struct ub_dev *sc, struct ub_scsi_cmd *cmd);static void ub_urb_complete(struct urb *urb, struct pt_regs *pt);static void ub_scsi_action(unsigned long _dev);static void ub_scsi_dispatch(struct ub_dev *sc);static void ub_scsi_urb_compl(struct ub_dev *sc, struct ub_scsi_cmd *cmd);static void ub_data_start(struct ub_dev *sc, struct ub_scsi_cmd *cmd);static void ub_state_done(struct ub_dev *sc, struct ub_scsi_cmd *cmd, int rc);static int __ub_state_stat(struct ub_dev *sc, struct ub_scsi_cmd *cmd);static void ub_state_stat(struct ub_dev *sc, struct ub_scsi_cmd *cmd);static void ub_state_stat_counted(struct ub_dev *sc, struct ub_scsi_cmd *cmd);static void ub_state_sense(struct ub_dev *sc, struct ub_scsi_cmd *cmd);static int ub_submit_clear_stall(struct ub_dev *sc, struct ub_scsi_cmd *cmd, int stalled_pipe);static void ub_top_sense_done(struct ub_dev *sc, struct ub_scsi_cmd *scmd);static int ub_sync_tur(struct ub_dev *sc, struct ub_lun *lun);static int ub_sync_read_cap(struct ub_dev *sc, struct ub_lun *lun, struct ub_capacity *ret);static int ub_probe_lun(struct ub_dev *sc, int lnum);/* */static struct usb_device_id ub_usb_ids[] = { // { USB_DEVICE_VER(0x0781, 0x0002, 0x0009, 0x0009) }, /* SDDR-31 */ { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_SCSI, US_PR_BULK) }, { }};MODULE_DEVICE_TABLE(usb, ub_usb_ids);/* * Find me a way to identify "next free minor" for add_disk(), * and the array disappears the next day. However, the number of * hosts has something to do with the naming and /proc/partitions. * This has to be thought out in detail before changing. * If UB_MAX_HOST was 1000, we'd use a bitmap. Or a better data structure. */#define UB_MAX_HOSTS 26static char ub_hostv[UB_MAX_HOSTS];static DEFINE_SPINLOCK(ub_lock); /* Locks globals and ->openc *//* * The SCSI command tracing procedures. */static void ub_cmdtr_new(struct ub_dev *sc, struct ub_scsi_cmd *cmd){ int n; struct ub_scsi_cmd_trace *t; if ((n = sc->tr.cur + 1) == SCMD_TRACE_SZ) n = 0; t = &sc->tr.vec[n]; memset(t, 0, sizeof(struct ub_scsi_cmd_trace)); t->tag = cmd->tag; t->op = cmd->cdb[0]; t->dir = cmd->dir; t->req_size = cmd->len; t->st_hst[0] = cmd->state; sc->tr.cur = n; cmd->trace_index = n;}static void ub_cmdtr_state(struct ub_dev *sc, struct ub_scsi_cmd *cmd){ int n; struct ub_scsi_cmd_trace *t; t = &sc->tr.vec[cmd->trace_index]; if (t->tag == cmd->tag) { if ((n = t->hcur + 1) == SCMD_ST_HIST_SZ) n = 0; t->st_hst[n] = cmd->state; t->hcur = n; }}static void ub_cmdtr_act_len(struct ub_dev *sc, struct ub_scsi_cmd *cmd){ struct ub_scsi_cmd_trace *t; t = &sc->tr.vec[cmd->trace_index]; if (t->tag == cmd->tag) t->act_size = cmd->act_len;}static void ub_cmdtr_sense(struct ub_dev *sc, struct ub_scsi_cmd *cmd, unsigned char *sense){ struct ub_scsi_cmd_trace *t; t = &sc->tr.vec[cmd->trace_index]; if (t->tag == cmd->tag) { t->key = sense[2] & 0x0F; t->asc = sense[12]; t->ascq = sense[13]; }}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -