📄 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) * -- ZIP does "ub: resid 18 len 0 act 0" and whole transport quits (toggles?) * -- 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) * -- support pphaneuf's SDDR-75 with two LUNs (also broken capacity...) * -- special case some senses, e.g. 3a/0 -> no media present, reduce retries * -- do something about spin-down devices, they are extremely dangerous * (ZIP is one. Needs spin-up command as well.) * -- verify the 13 conditions and do bulk resets * -- normal pool of commands instead of cmdv[]? * -- kill last_pipe and simply do two-state clearing on both pipes * -- verify protocol (bulk) from USB descriptors (maybe...) * -- highmem and sg * -- move top_sense and work_bcs into separate allocations (if they survive) * for cache purists and esoteric architectures. * -- prune comments, they are too volumnous * -- Exterminate P3 printks * -- Resove XXX's */#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 125 /* Stolen from Experimental range for a week - XXX *//* * 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 *//* */#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 { u32 Signature; /* contains 'USBC' */ u32 Tag; /* unique per command id */ u32 DataTransferLength; /* size of data */ u8 Flags; /* direction in bit 0 */ u8 Lun; /* LUN normally 0 */ 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 { u32 Signature; /* should = 'USBS' */ u32 Tag; /* same as original command */ u32 Residue; /* amount not transferred */ u8 Status; /* see below */};#define US_BULK_CS_WRAP_LEN 13#define US_BULK_CS_SIGN 0x53425355 /* spells out 'USBS' *//* This is for Olympus Camedia digital cameras */#define US_BULK_CS_OLYMPUS_SIGN 0x55425355 /* spells out 'USBU' */#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 1#define UB_MAX_SECTORS 64/* * A second ought to be enough for a 32K transfer (UB_MAX_SECTORS) * even if a webcam hogs the bus (famous last words). * Some CDs need a second to spin up though. * ZIP drive rejects commands when it's not spinning, * so it does not need long timeouts either. */#define UB_URB_TIMEOUT (HZ*2)#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_SENSE, /* Sending Request Sense */ UB_CMDST_DONE /* Final state */};static char *ub_scsi_cmd_stname[] = { ". ", "Cmd", "dat", "c2s", "sts", "clr", "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 */ int act_len; /* Return size */ int stat_count; /* Retries getting status. */ /* * We do not support transfers from highmem pages * because the underlying USB framework does not do what we need. */ char *data; /* Requested buffer */ unsigned int len; /* Requested length */ // struct scatterlist sgv[UB_MAX_REQ_SG]; 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 15 /* No more than 256 (trace_index) */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 UB device instance. */struct ub_dev { spinlock_t lock; int id; /* Number among ub's */ atomic_t poison; /* The USB device is disconnected */ int openc; /* protected by ub_lock! */ /* kref is too implicit for our taste */ unsigned int tagcnt; int changed; /* Media was changed */ int removable; int readonly; char name[8]; struct usb_device *dev; struct usb_interface *intf; struct ub_capacity capacity; struct gendisk *disk; 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; /* XXX Use Ingo's mempool (once we have more than one) */ int cmda[1]; struct ub_scsi_cmd cmdv[1]; 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 */ struct bulk_cb_wrap work_bcb; struct bulk_cs_wrap work_bcs; struct usb_ctrlrequest work_cr; struct ub_scsi_trace tr;};/* */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_state_done(struct ub_dev *sc, struct ub_scsi_cmd *cmd, int rc);static void ub_state_stat(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);static int ub_sync_read_cap(struct ub_dev *sc, struct ub_capacity *ret);/* */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 spinlock_t ub_lock = SPIN_LOCK_UNLOCKED; /* 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]; }}static ssize_t ub_diag_show(struct device *dev, char *page){ struct usb_interface *intf; struct ub_dev *sc; int cnt; unsigned long flags; int nc, nh; int i, j; struct ub_scsi_cmd_trace *t; intf = to_usb_interface(dev); sc = usb_get_intfdata(intf); if (sc == NULL) return 0; cnt = 0; spin_lock_irqsave(&sc->lock, flags); cnt += sprintf(page + cnt, "qlen %d qmax %d changed %d removable %d readonly %d\n", sc->cmd_queue.qlen, sc->cmd_queue.qmax, sc->changed, sc->removable, sc->readonly); if ((nc = sc->tr.cur + 1) == SCMD_TRACE_SZ) nc = 0; for (j = 0; j < SCMD_TRACE_SZ; j++) { t = &sc->tr.vec[nc]; cnt += sprintf(page + cnt, "%08x %02x", t->tag, t->op); if (t->op == REQUEST_SENSE) { cnt += sprintf(page + cnt, " [sense %x %02x %02x]", t->key, t->asc, t->ascq); } else { cnt += sprintf(page + cnt, " %c", UB_DIR_CHAR(t->dir)); cnt += sprintf(page + cnt, " [%5d %5d]", t->req_size, t->act_size); } if ((nh = t->hcur + 1) == SCMD_ST_HIST_SZ) nh = 0; for (i = 0; i < SCMD_ST_HIST_SZ; i++) { cnt += sprintf(page + cnt, " %s", ub_scsi_cmd_stname[(int)t->st_hst[nh]]); if (++nh == SCMD_ST_HIST_SZ) nh = 0; } cnt += sprintf(page + cnt, "\n"); if (++nc == SCMD_TRACE_SZ) nc = 0; } spin_unlock_irqrestore(&sc->lock, flags); return cnt;}static DEVICE_ATTR(diag, S_IRUGO, ub_diag_show, NULL); /* N.B. World readable *//* * The id allocator. * * This also stores the host for indexing by minor, which is somewhat dirty. */static int ub_id_get(void){ unsigned long flags; int i; spin_lock_irqsave(&ub_lock, flags); for (i = 0; i < UB_MAX_HOSTS; i++) { if (ub_hostv[i] == 0) { ub_hostv[i] = 1; spin_unlock_irqrestore(&ub_lock, flags); return i; } } spin_unlock_irqrestore(&ub_lock, flags); return -1;}static void ub_id_put(int id){ if (id < 0 || id >= UB_MAX_HOSTS) { printk(KERN_ERR DRV_NAME ": bad host ID %d\n", id); return; } if (ub_hostv[id] == 0) { printk(KERN_ERR DRV_NAME ": freeing free host ID %d\n", id); return; } ub_hostv[id] = 0;}/* * Final cleanup and deallocation. * This must be called with ub_lock taken. */static void ub_cleanup(struct ub_dev *sc){ ub_id_put(sc->id); kfree(sc);}/* * The "command allocator". */static struct ub_scsi_cmd *ub_get_cmd(struct ub_dev *sc){ struct ub_scsi_cmd *ret; if (sc->cmda[0]) return NULL; ret = &sc->cmdv[0]; sc->cmda[0] = 1; return ret;}static void ub_put_cmd(struct ub_dev *sc, struct ub_scsi_cmd *cmd){ if (cmd != &sc->cmdv[0]) { printk(KERN_WARNING "%s: releasing a foreign cmd %p\n", sc->name, cmd); return; } if (!sc->cmda[0]) { printk(KERN_WARNING "%s: releasing a free cmd\n", sc->name); return; } sc->cmda[0] = 0;}/*
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -