⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 ub.c

📁 Linux Kernel 2.6.9 for OMAP1710
💻 C
📖 第 1 页 / 共 4 页
字号:
/* * 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 + -