2005-04-17 00:20:36 +02:00
|
|
|
/*
|
|
|
|
* 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)
|
2005-05-02 01:05:40 +02:00
|
|
|
* -- Kill first_open (Al Viro fixed the block layer now)
|
2005-04-17 00:20:36 +02:00
|
|
|
* -- 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...)
|
2005-07-31 07:38:30 +02:00
|
|
|
* -- highmem
|
2005-04-17 00:20:36 +02:00
|
|
|
* -- move top_sense and work_bcs into separate allocations (if they survive)
|
|
|
|
* for cache purists and esoteric architectures.
|
2005-07-31 07:38:30 +02:00
|
|
|
* -- Allocate structure for LUN 0 before the first ub_sync_tur, avoid NULL. ?
|
2005-04-17 00:20:36 +02:00
|
|
|
* -- 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=?
|
2005-07-27 20:43:51 +02:00
|
|
|
* -- CLEAR, CLR2STS, CLRRS seem to be ripe for refactoring.
|
2005-04-17 00:20:36 +02:00
|
|
|
*/
|
|
|
|
#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
|
|
|
|
|
2005-07-27 20:43:51 +02:00
|
|
|
/*
|
|
|
|
* 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 !
|
|
|
|
* +--------+
|
|
|
|
*/
|
|
|
|
|
2005-04-17 00:20:36 +02:00
|
|
|
/*
|
|
|
|
* 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 */
|
|
|
|
|
|
|
|
/*
|
2005-05-02 01:05:40 +02:00
|
|
|
* This many LUNs per USB device.
|
|
|
|
* Every one of them takes a host, see UB_MAX_HOSTS.
|
2005-04-17 00:20:36 +02:00
|
|
|
*/
|
2005-06-06 22:54:59 +02:00
|
|
|
#define UB_MAX_LUNS 9
|
2005-05-02 01:05:40 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
*/
|
|
|
|
|
2005-04-17 00:20:36 +02:00
|
|
|
#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 */
|
2005-05-02 01:05:40 +02:00
|
|
|
u8 Lun; /* LUN */
|
2005-04-17 00:20:36 +02:00
|
|
|
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;
|
|
|
|
|
2005-07-31 07:38:30 +02:00
|
|
|
#define UB_MAX_REQ_SG 4
|
2005-04-17 00:20:36 +02:00
|
|
|
#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) */
|
2005-07-27 20:43:51 +02:00
|
|
|
UB_CMDST_CLRRS, /* Clearing before retrying status */
|
2005-04-17 00:20:36 +02:00
|
|
|
UB_CMDST_SENSE, /* Sending Request Sense */
|
|
|
|
UB_CMDST_DONE /* Final state */
|
|
|
|
};
|
|
|
|
|
|
|
|
static char *ub_scsi_cmd_stname[] = {
|
|
|
|
". ",
|
|
|
|
"Cmd",
|
|
|
|
"dat",
|
|
|
|
"c2s",
|
|
|
|
"sts",
|
|
|
|
"clr",
|
2005-07-27 20:43:51 +02:00
|
|
|
"crs",
|
2005-04-17 00:20:36 +02:00
|
|
|
"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 */
|
2005-08-15 06:16:03 +02:00
|
|
|
unsigned int current_sg;
|
|
|
|
unsigned int nsg; /* sgv[nsg] */
|
|
|
|
struct scatterlist sgv[UB_MAX_REQ_SG];
|
2005-04-17 00:20:36 +02:00
|
|
|
|
2005-05-02 01:05:40 +02:00
|
|
|
struct ub_lun *lun;
|
2005-04-17 00:20:36 +02:00
|
|
|
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;
|
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
2005-05-02 01:05:40 +02:00
|
|
|
* 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.
|
2005-04-17 00:20:36 +02:00
|
|
|
*/
|
|
|
|
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;
|
2005-05-02 01:05:40 +02:00
|
|
|
char name[12];
|
2005-04-17 00:20:36 +02:00
|
|
|
struct usb_device *dev;
|
|
|
|
struct usb_interface *intf;
|
|
|
|
|
2005-05-02 01:05:40 +02:00
|
|
|
struct list_head luns;
|
2005-04-17 00:20:36 +02:00
|
|
|
|
|
|
|
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 */
|
2005-07-27 20:43:51 +02:00
|
|
|
__le32 signature; /* Learned signature */
|
2005-04-17 00:20:36 +02:00
|
|
|
struct bulk_cb_wrap work_bcb;
|
|
|
|
struct bulk_cs_wrap work_bcs;
|
|
|
|
struct usb_ctrlrequest work_cr;
|
|
|
|
|
2005-07-31 07:38:30 +02:00
|
|
|
int sg_stat[UB_MAX_REQ_SG+1];
|
2005-04-17 00:20:36 +02:00
|
|
|
struct ub_scsi_trace tr;
|
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
*/
|
|
|
|
static void ub_cleanup(struct ub_dev *sc);
|
2005-07-31 07:51:52 +02:00
|
|
|
static int ub_request_fn_1(struct ub_lun *lun, struct request *rq);
|
2005-05-02 01:05:40 +02:00
|
|
|
static int ub_cmd_build_block(struct ub_dev *sc, struct ub_lun *lun,
|
|
|
|
struct ub_scsi_cmd *cmd, struct request *rq);
|
2005-07-31 07:38:30 +02:00
|
|
|
static int ub_cmd_build_packet(struct ub_dev *sc, struct ub_lun *lun,
|
|
|
|
struct ub_scsi_cmd *cmd, struct request *rq);
|
2005-04-17 00:20:36 +02:00
|
|
|
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);
|
2005-08-15 06:16:03 +02:00
|
|
|
static void ub_data_start(struct ub_dev *sc, struct ub_scsi_cmd *cmd);
|
2005-04-17 00:20:36 +02:00
|
|
|
static void ub_state_done(struct ub_dev *sc, struct ub_scsi_cmd *cmd, int rc);
|
2005-07-27 20:43:51 +02:00
|
|
|
static int __ub_state_stat(struct ub_dev *sc, struct ub_scsi_cmd *cmd);
|
2005-04-17 00:20:36 +02:00
|
|
|
static void ub_state_stat(struct ub_dev *sc, struct ub_scsi_cmd *cmd);
|
2005-07-27 20:43:51 +02:00
|
|
|
static void ub_state_stat_counted(struct ub_dev *sc, struct ub_scsi_cmd *cmd);
|
2005-04-17 00:20:36 +02:00
|
|
|
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);
|
2005-05-02 01:05:40 +02:00
|
|
|
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);
|
2005-04-17 00:20:36 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
*/
|
|
|
|
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 26
|
|
|
|
static char ub_hostv[UB_MAX_HOSTS];
|
2005-05-02 01:05:40 +02:00
|
|
|
|
2005-04-17 00:20:36 +02:00
|
|
|
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];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-07-31 07:51:45 +02:00
|
|
|
static ssize_t ub_diag_show(struct device *dev, struct device_attribute *attr,
|
|
|
|
char *page)
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
|
|
|
struct usb_interface *intf;
|
|
|
|
struct ub_dev *sc;
|
2005-05-02 01:05:40 +02:00
|
|
|
struct list_head *p;
|
|
|
|
struct ub_lun *lun;
|
2005-04-17 00:20:36 +02:00
|
|
|
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,
|
2005-05-02 01:05:40 +02:00
|
|
|
"qlen %d qmax %d\n",
|
|
|
|
sc->cmd_queue.qlen, sc->cmd_queue.qmax);
|
2005-07-31 07:38:30 +02:00
|
|
|
cnt += sprintf(page + cnt,
|
|
|
|
"sg %d %d %d %d %d\n",
|
|
|
|
sc->sg_stat[0],
|
|
|
|
sc->sg_stat[1],
|
|
|
|
sc->sg_stat[2],
|
|
|
|
sc->sg_stat[3],
|
|
|
|
sc->sg_stat[4]);
|
2005-05-02 01:05:40 +02:00
|
|
|
|
|
|
|
list_for_each (p, &sc->luns) {
|
|
|
|
lun = list_entry(p, struct ub_lun, link);
|
|
|
|
cnt += sprintf(page + cnt,
|
|
|
|
"lun %u changed %d removable %d readonly %d\n",
|
|
|
|
lun->num, lun->changed, lun->removable, lun->readonly);
|
|
|
|
}
|
2005-04-17 00:20:36 +02:00
|
|
|
|
|
|
|
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)
|
|
|
|
{
|
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
if (id < 0 || id >= UB_MAX_HOSTS) {
|
|
|
|
printk(KERN_ERR DRV_NAME ": bad host ID %d\n", id);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
spin_lock_irqsave(&ub_lock, flags);
|
|
|
|
if (ub_hostv[id] == 0) {
|
|
|
|
spin_unlock_irqrestore(&ub_lock, flags);
|
|
|
|
printk(KERN_ERR DRV_NAME ": freeing free host ID %d\n", id);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
ub_hostv[id] = 0;
|
|
|
|
spin_unlock_irqrestore(&ub_lock, flags);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Downcount for deallocation. This rides on two assumptions:
|
|
|
|
* - once something is poisoned, its refcount cannot grow
|
|
|
|
* - opens cannot happen at this time (del_gendisk was done)
|
|
|
|
* If the above is true, we can drop the lock, which we need for
|
|
|
|
* blk_cleanup_queue(): the silly thing may attempt to sleep.
|
|
|
|
* [Actually, it never needs to sleep for us, but it calls might_sleep()]
|
|
|
|
*/
|
|
|
|
static void ub_put(struct ub_dev *sc)
|
|
|
|
{
|
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
spin_lock_irqsave(&ub_lock, flags);
|
|
|
|
--sc->openc;
|
|
|
|
if (sc->openc == 0 && atomic_read(&sc->poison)) {
|
|
|
|
spin_unlock_irqrestore(&ub_lock, flags);
|
|
|
|
ub_cleanup(sc);
|
|
|
|
} else {
|
|
|
|
spin_unlock_irqrestore(&ub_lock, flags);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Final cleanup and deallocation.
|
|
|
|
*/
|
|
|
|
static void ub_cleanup(struct ub_dev *sc)
|
|
|
|
{
|
2005-05-02 01:05:40 +02:00
|
|
|
struct list_head *p;
|
|
|
|
struct ub_lun *lun;
|
2005-04-17 00:20:36 +02:00
|
|
|
request_queue_t *q;
|
|
|
|
|
2005-05-02 01:05:40 +02:00
|
|
|
while (!list_empty(&sc->luns)) {
|
|
|
|
p = sc->luns.next;
|
|
|
|
lun = list_entry(p, struct ub_lun, link);
|
|
|
|
list_del(p);
|
2005-04-17 00:20:36 +02:00
|
|
|
|
2005-05-02 01:05:40 +02:00
|
|
|
/* I don't think queue can be NULL. But... Stolen from sx8.c */
|
|
|
|
if ((q = lun->disk->queue) != NULL)
|
|
|
|
blk_cleanup_queue(q);
|
|
|
|
/*
|
|
|
|
* If we zero disk->private_data BEFORE put_disk, we have
|
|
|
|
* to check for NULL all over the place in open, release,
|
|
|
|
* check_media and revalidate, because the block level
|
|
|
|
* semaphore is well inside the put_disk.
|
|
|
|
* But we cannot zero after the call, because *disk is gone.
|
|
|
|
* The sd.c is blatantly racy in this area.
|
|
|
|
*/
|
|
|
|
/* disk->private_data = NULL; */
|
|
|
|
put_disk(lun->disk);
|
|
|
|
lun->disk = NULL;
|
|
|
|
|
|
|
|
ub_id_put(lun->id);
|
|
|
|
kfree(lun);
|
|
|
|
}
|
2005-04-17 00:20:36 +02:00
|
|
|
|
|
|
|
kfree(sc);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The "command allocator".
|
|
|
|
*/
|
2005-05-02 01:05:40 +02:00
|
|
|
static struct ub_scsi_cmd *ub_get_cmd(struct ub_lun *lun)
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
|
|
|
struct ub_scsi_cmd *ret;
|
|
|
|
|
2005-05-02 01:05:40 +02:00
|
|
|
if (lun->cmda[0])
|
2005-04-17 00:20:36 +02:00
|
|
|
return NULL;
|
2005-05-02 01:05:40 +02:00
|
|
|
ret = &lun->cmdv[0];
|
|
|
|
lun->cmda[0] = 1;
|
2005-04-17 00:20:36 +02:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2005-05-02 01:05:40 +02:00
|
|
|
static void ub_put_cmd(struct ub_lun *lun, struct ub_scsi_cmd *cmd)
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
2005-05-02 01:05:40 +02:00
|
|
|
if (cmd != &lun->cmdv[0]) {
|
2005-04-17 00:20:36 +02:00
|
|
|
printk(KERN_WARNING "%s: releasing a foreign cmd %p\n",
|
2005-05-02 01:05:40 +02:00
|
|
|
lun->name, cmd);
|
2005-04-17 00:20:36 +02:00
|
|
|
return;
|
|
|
|
}
|
2005-05-02 01:05:40 +02:00
|
|
|
if (!lun->cmda[0]) {
|
|
|
|
printk(KERN_WARNING "%s: releasing a free cmd\n", lun->name);
|
2005-04-17 00:20:36 +02:00
|
|
|
return;
|
|
|
|
}
|
2005-05-02 01:05:40 +02:00
|
|
|
lun->cmda[0] = 0;
|
2005-04-17 00:20:36 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The command queue.
|
|
|
|
*/
|
|
|
|
static void ub_cmdq_add(struct ub_dev *sc, struct ub_scsi_cmd *cmd)
|
|
|
|
{
|
|
|
|
struct ub_scsi_cmd_queue *t = &sc->cmd_queue;
|
|
|
|
|
|
|
|
if (t->qlen++ == 0) {
|
|
|
|
t->head = cmd;
|
|
|
|
t->tail = cmd;
|
|
|
|
} else {
|
|
|
|
t->tail->next = cmd;
|
|
|
|
t->tail = cmd;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (t->qlen > t->qmax)
|
|
|
|
t->qmax = t->qlen;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ub_cmdq_insert(struct ub_dev *sc, struct ub_scsi_cmd *cmd)
|
|
|
|
{
|
|
|
|
struct ub_scsi_cmd_queue *t = &sc->cmd_queue;
|
|
|
|
|
|
|
|
if (t->qlen++ == 0) {
|
|
|
|
t->head = cmd;
|
|
|
|
t->tail = cmd;
|
|
|
|
} else {
|
|
|
|
cmd->next = t->head;
|
|
|
|
t->head = cmd;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (t->qlen > t->qmax)
|
|
|
|
t->qmax = t->qlen;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct ub_scsi_cmd *ub_cmdq_pop(struct ub_dev *sc)
|
|
|
|
{
|
|
|
|
struct ub_scsi_cmd_queue *t = &sc->cmd_queue;
|
|
|
|
struct ub_scsi_cmd *cmd;
|
|
|
|
|
|
|
|
if (t->qlen == 0)
|
|
|
|
return NULL;
|
|
|
|
if (--t->qlen == 0)
|
|
|
|
t->tail = NULL;
|
|
|
|
cmd = t->head;
|
|
|
|
t->head = cmd->next;
|
|
|
|
cmd->next = NULL;
|
|
|
|
return cmd;
|
|
|
|
}
|
|
|
|
|
|
|
|
#define ub_cmdq_peek(sc) ((sc)->cmd_queue.head)
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The request function is our main entry point
|
|
|
|
*/
|
|
|
|
|
2005-07-31 07:51:52 +02:00
|
|
|
static void ub_request_fn(request_queue_t *q)
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
2005-05-02 01:05:40 +02:00
|
|
|
struct ub_lun *lun = q->queuedata;
|
2005-04-17 00:20:36 +02:00
|
|
|
struct request *rq;
|
|
|
|
|
|
|
|
while ((rq = elv_next_request(q)) != NULL) {
|
2005-07-31 07:51:52 +02:00
|
|
|
if (ub_request_fn_1(lun, rq) != 0) {
|
2005-04-17 00:20:36 +02:00
|
|
|
blk_stop_queue(q);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-07-31 07:51:52 +02:00
|
|
|
static int ub_request_fn_1(struct ub_lun *lun, struct request *rq)
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
2005-05-02 01:05:40 +02:00
|
|
|
struct ub_dev *sc = lun->udev;
|
2005-04-17 00:20:36 +02:00
|
|
|
struct ub_scsi_cmd *cmd;
|
|
|
|
int rc;
|
|
|
|
|
2005-05-02 01:05:40 +02:00
|
|
|
if (atomic_read(&sc->poison) || lun->changed) {
|
2005-04-17 00:20:36 +02:00
|
|
|
blkdev_dequeue_request(rq);
|
|
|
|
ub_end_rq(rq, 0);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-05-02 01:05:40 +02:00
|
|
|
if ((cmd = ub_get_cmd(lun)) == NULL)
|
2005-04-17 00:20:36 +02:00
|
|
|
return -1;
|
|
|
|
memset(cmd, 0, sizeof(struct ub_scsi_cmd));
|
|
|
|
|
|
|
|
blkdev_dequeue_request(rq);
|
|
|
|
if (blk_pc_request(rq)) {
|
2005-07-31 07:38:30 +02:00
|
|
|
rc = ub_cmd_build_packet(sc, lun, cmd, rq);
|
2005-04-17 00:20:36 +02:00
|
|
|
} else {
|
2005-05-02 01:05:40 +02:00
|
|
|
rc = ub_cmd_build_block(sc, lun, cmd, rq);
|
2005-04-17 00:20:36 +02:00
|
|
|
}
|
|
|
|
if (rc != 0) {
|
2005-05-02 01:05:40 +02:00
|
|
|
ub_put_cmd(lun, cmd);
|
2005-04-17 00:20:36 +02:00
|
|
|
ub_end_rq(rq, 0);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
cmd->state = UB_CMDST_INIT;
|
2005-05-02 01:05:40 +02:00
|
|
|
cmd->lun = lun;
|
2005-04-17 00:20:36 +02:00
|
|
|
cmd->done = ub_rw_cmd_done;
|
2005-08-15 06:16:03 +02:00
|
|
|
cmd->back = rq;
|
2005-04-17 00:20:36 +02:00
|
|
|
|
|
|
|
cmd->tag = sc->tagcnt++;
|
2005-07-31 07:38:30 +02:00
|
|
|
if (ub_submit_scsi(sc, cmd) != 0) {
|
2005-05-02 01:05:40 +02:00
|
|
|
ub_put_cmd(lun, cmd);
|
2005-04-17 00:20:36 +02:00
|
|
|
ub_end_rq(rq, 0);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-05-02 01:05:40 +02:00
|
|
|
static int ub_cmd_build_block(struct ub_dev *sc, struct ub_lun *lun,
|
|
|
|
struct ub_scsi_cmd *cmd, struct request *rq)
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
|
|
|
int ub_dir;
|
|
|
|
int n_elem;
|
2005-08-15 06:16:03 +02:00
|
|
|
unsigned int block, nblks;
|
2005-04-17 00:20:36 +02:00
|
|
|
|
|
|
|
if (rq_data_dir(rq) == WRITE)
|
|
|
|
ub_dir = UB_DIR_WRITE;
|
|
|
|
else
|
|
|
|
ub_dir = UB_DIR_READ;
|
2005-08-15 06:16:03 +02:00
|
|
|
cmd->dir = ub_dir;
|
2005-04-17 00:20:36 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* get scatterlist from block layer
|
|
|
|
*/
|
2005-08-15 06:16:03 +02:00
|
|
|
n_elem = blk_rq_map_sg(lun->disk->queue, rq, &cmd->sgv[0]);
|
2005-04-17 00:20:36 +02:00
|
|
|
if (n_elem <= 0) {
|
2005-07-31 07:38:30 +02:00
|
|
|
printk(KERN_INFO "%s: failed request map (%d)\n",
|
|
|
|
sc->name, n_elem); /* P3 */
|
|
|
|
return -1; /* request with no s/g entries? */
|
2005-04-17 00:20:36 +02:00
|
|
|
}
|
2005-07-31 07:38:30 +02:00
|
|
|
if (n_elem > UB_MAX_REQ_SG) { /* Paranoia */
|
2005-04-17 00:20:36 +02:00
|
|
|
printk(KERN_WARNING "%s: request with %d segments\n",
|
|
|
|
sc->name, n_elem);
|
|
|
|
return -1;
|
|
|
|
}
|
2005-08-15 06:16:03 +02:00
|
|
|
cmd->nsg = n_elem;
|
2005-07-31 07:38:30 +02:00
|
|
|
sc->sg_stat[n_elem]++;
|
2005-04-17 00:20:36 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* build the command
|
|
|
|
*
|
|
|
|
* The call to blk_queue_hardsect_size() guarantees that request
|
|
|
|
* is aligned, but it is given in terms of 512 byte units, always.
|
|
|
|
*/
|
2005-08-15 06:16:03 +02:00
|
|
|
block = rq->sector >> lun->capacity.bshift;
|
|
|
|
nblks = rq->nr_sectors >> lun->capacity.bshift;
|
2005-07-31 07:38:30 +02:00
|
|
|
|
2005-08-15 06:16:03 +02:00
|
|
|
cmd->cdb[0] = (ub_dir == UB_DIR_READ)? READ_10: WRITE_10;
|
2005-04-17 00:20:36 +02:00
|
|
|
/* 10-byte uses 4 bytes of LBA: 2147483648KB, 2097152MB, 2048GB */
|
|
|
|
cmd->cdb[2] = block >> 24;
|
|
|
|
cmd->cdb[3] = block >> 16;
|
|
|
|
cmd->cdb[4] = block >> 8;
|
|
|
|
cmd->cdb[5] = block;
|
|
|
|
cmd->cdb[7] = nblks >> 8;
|
|
|
|
cmd->cdb[8] = nblks;
|
|
|
|
cmd->cdb_len = 10;
|
|
|
|
|
2005-08-15 06:16:03 +02:00
|
|
|
cmd->len = rq->nr_sectors * 512;
|
|
|
|
|
|
|
|
return 0;
|
2005-04-17 00:20:36 +02:00
|
|
|
}
|
|
|
|
|
2005-07-31 07:38:30 +02:00
|
|
|
static int ub_cmd_build_packet(struct ub_dev *sc, struct ub_lun *lun,
|
|
|
|
struct ub_scsi_cmd *cmd, struct request *rq)
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
2005-08-15 06:16:03 +02:00
|
|
|
int n_elem;
|
2005-04-17 00:20:36 +02:00
|
|
|
|
|
|
|
if (rq->data_len == 0) {
|
|
|
|
cmd->dir = UB_DIR_NONE;
|
|
|
|
} else {
|
|
|
|
if (rq_data_dir(rq) == WRITE)
|
|
|
|
cmd->dir = UB_DIR_WRITE;
|
|
|
|
else
|
|
|
|
cmd->dir = UB_DIR_READ;
|
2005-08-15 06:16:03 +02:00
|
|
|
|
2005-04-17 00:20:36 +02:00
|
|
|
}
|
2005-08-15 06:16:03 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* get scatterlist from block layer
|
|
|
|
*/
|
|
|
|
n_elem = blk_rq_map_sg(lun->disk->queue, rq, &cmd->sgv[0]);
|
|
|
|
if (n_elem < 0) {
|
|
|
|
printk(KERN_INFO "%s: failed request map (%d)\n",
|
|
|
|
sc->name, n_elem); /* P3 */
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (n_elem > UB_MAX_REQ_SG) { /* Paranoia */
|
|
|
|
printk(KERN_WARNING "%s: request with %d segments\n",
|
|
|
|
sc->name, n_elem);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
cmd->nsg = n_elem;
|
|
|
|
sc->sg_stat[n_elem]++;
|
|
|
|
|
|
|
|
memcpy(&cmd->cdb, rq->cmd, rq->cmd_len);
|
|
|
|
cmd->cdb_len = rq->cmd_len;
|
|
|
|
|
2005-04-17 00:20:36 +02:00
|
|
|
cmd->len = rq->data_len;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ub_rw_cmd_done(struct ub_dev *sc, struct ub_scsi_cmd *cmd)
|
|
|
|
{
|
2005-08-15 06:16:03 +02:00
|
|
|
struct request *rq = cmd->back;
|
2005-05-02 01:05:40 +02:00
|
|
|
struct ub_lun *lun = cmd->lun;
|
2005-04-17 00:20:36 +02:00
|
|
|
int uptodate;
|
|
|
|
|
2005-08-15 06:16:03 +02:00
|
|
|
if (cmd->error == 0) {
|
2005-04-17 00:20:36 +02:00
|
|
|
uptodate = 1;
|
|
|
|
|
2005-08-15 06:16:03 +02:00
|
|
|
if (blk_pc_request(rq)) {
|
|
|
|
if (cmd->act_len >= rq->data_len)
|
|
|
|
rq->data_len = 0;
|
|
|
|
else
|
|
|
|
rq->data_len -= cmd->act_len;
|
2005-07-31 07:38:30 +02:00
|
|
|
}
|
2005-08-15 06:16:03 +02:00
|
|
|
} else {
|
2005-07-31 07:38:30 +02:00
|
|
|
uptodate = 0;
|
|
|
|
|
2005-08-15 06:16:03 +02:00
|
|
|
if (blk_pc_request(rq)) {
|
|
|
|
/* UB_SENSE_SIZE is smaller than SCSI_SENSE_BUFFERSIZE */
|
|
|
|
memcpy(rq->sense, sc->top_sense, UB_SENSE_SIZE);
|
|
|
|
rq->sense_len = UB_SENSE_SIZE;
|
|
|
|
if (sc->top_sense[0] != 0)
|
|
|
|
rq->errors = SAM_STAT_CHECK_CONDITION;
|
|
|
|
else
|
|
|
|
rq->errors = DID_ERROR << 16;
|
|
|
|
}
|
|
|
|
}
|
2005-07-31 07:38:30 +02:00
|
|
|
|
2005-05-02 01:05:40 +02:00
|
|
|
ub_put_cmd(lun, cmd);
|
2005-04-17 00:20:36 +02:00
|
|
|
ub_end_rq(rq, uptodate);
|
2005-07-31 07:38:30 +02:00
|
|
|
blk_start_queue(lun->disk->queue);
|
2005-04-17 00:20:36 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static void ub_end_rq(struct request *rq, int uptodate)
|
|
|
|
{
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
rc = end_that_request_first(rq, uptodate, rq->hard_nr_sectors);
|
|
|
|
// assert(rc == 0);
|
|
|
|
end_that_request_last(rq);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Submit a regular SCSI operation (not an auto-sense).
|
|
|
|
*
|
|
|
|
* The Iron Law of Good Submit Routine is:
|
|
|
|
* Zero return - callback is done, Nonzero return - callback is not done.
|
|
|
|
* No exceptions.
|
|
|
|
*
|
|
|
|
* Host is assumed locked.
|
|
|
|
*
|
|
|
|
* XXX We only support Bulk for the moment.
|
|
|
|
*/
|
|
|
|
static int ub_submit_scsi(struct ub_dev *sc, struct ub_scsi_cmd *cmd)
|
|
|
|
{
|
|
|
|
|
|
|
|
if (cmd->state != UB_CMDST_INIT ||
|
|
|
|
(cmd->dir != UB_DIR_NONE && cmd->len == 0)) {
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
ub_cmdq_add(sc, cmd);
|
|
|
|
/*
|
|
|
|
* We can call ub_scsi_dispatch(sc) right away here, but it's a little
|
|
|
|
* safer to jump to a tasklet, in case upper layers do something silly.
|
|
|
|
*/
|
|
|
|
tasklet_schedule(&sc->tasklet);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Submit the first URB for the queued command.
|
|
|
|
* This function does not deal with queueing in any way.
|
|
|
|
*/
|
|
|
|
static int ub_scsi_cmd_start(struct ub_dev *sc, struct ub_scsi_cmd *cmd)
|
|
|
|
{
|
|
|
|
struct bulk_cb_wrap *bcb;
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
bcb = &sc->work_bcb;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* ``If the allocation length is eighteen or greater, and a device
|
|
|
|
* server returns less than eithteen bytes of data, the application
|
|
|
|
* client should assume that the bytes not transferred would have been
|
|
|
|
* zeroes had the device server returned those bytes.''
|
|
|
|
*
|
|
|
|
* We zero sense for all commands so that when a packet request
|
|
|
|
* fails it does not return a stale sense.
|
|
|
|
*/
|
|
|
|
memset(&sc->top_sense, 0, UB_SENSE_SIZE);
|
|
|
|
|
|
|
|
/* set up the command wrapper */
|
|
|
|
bcb->Signature = cpu_to_le32(US_BULK_CB_SIGN);
|
|
|
|
bcb->Tag = cmd->tag; /* Endianness is not important */
|
|
|
|
bcb->DataTransferLength = cpu_to_le32(cmd->len);
|
|
|
|
bcb->Flags = (cmd->dir == UB_DIR_READ) ? 0x80 : 0;
|
2005-05-02 01:05:40 +02:00
|
|
|
bcb->Lun = (cmd->lun != NULL) ? cmd->lun->num : 0;
|
2005-04-17 00:20:36 +02:00
|
|
|
bcb->Length = cmd->cdb_len;
|
|
|
|
|
|
|
|
/* copy the command payload */
|
|
|
|
memcpy(bcb->CDB, cmd->cdb, UB_MAX_CDB_SIZE);
|
|
|
|
|
|
|
|
UB_INIT_COMPLETION(sc->work_done);
|
|
|
|
|
|
|
|
sc->last_pipe = sc->send_bulk_pipe;
|
|
|
|
usb_fill_bulk_urb(&sc->work_urb, sc->dev, sc->send_bulk_pipe,
|
|
|
|
bcb, US_BULK_CB_WRAP_LEN, ub_urb_complete, sc);
|
[PATCH] USB: URB_ASYNC_UNLINK flag removed from the kernel
29 July 2005, Cambridge, MA:
This afternoon Alan Stern submitted a patch to remove the URB_ASYNC_UNLINK
flag from the Linux kernel. Mr. Stern explained, "This flag is a relic
from an earlier, less-well-designed system. For over a year it hasn't
been used for anything other than printing warning messages."
An anonymous spokesman for the Linux kernel development community
commented, "This is exactly the sort of thing we see happening all the
time. As the kernel evolves, support for old techniques and old code can
be jettisoned and replaced by newer, better approaches. Proprietary
operating systems do not have the freedom or flexibility to change so
quickly."
Mr. Stern, a staff member at Harvard University's Rowland Institute who
works on Linux only as a hobby, noted that the patch (labelled as548) did
not update two files, keyspan.c and option.c, in the USB drivers' "serial"
subdirectory. "Those files need more extensive changes," he remarked.
"They examine the status field of several URBs at times when they're not
supposed to. That will need to be fixed before the URB_ASYNC_UNLINK flag
is removed."
Greg Kroah-Hartman, the kernel maintainer responsible for overseeing all
of Linux's USB drivers, did not respond to our inquiries or return our
calls. His only comment was "Applied, thanks."
Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
2005-07-29 22:11:07 +02:00
|
|
|
sc->work_urb.transfer_flags = 0;
|
2005-04-17 00:20:36 +02:00
|
|
|
|
|
|
|
/* Fill what we shouldn't be filling, because usb-storage did so. */
|
|
|
|
sc->work_urb.actual_length = 0;
|
|
|
|
sc->work_urb.error_count = 0;
|
|
|
|
sc->work_urb.status = 0;
|
|
|
|
|
|
|
|
if ((rc = usb_submit_urb(&sc->work_urb, GFP_ATOMIC)) != 0) {
|
|
|
|
/* XXX Clear stalls */
|
|
|
|
printk("ub: cmd #%d start failed (%d)\n", cmd->tag, rc); /* P3 */
|
|
|
|
ub_complete(&sc->work_done);
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
sc->work_timer.expires = jiffies + UB_URB_TIMEOUT;
|
|
|
|
add_timer(&sc->work_timer);
|
|
|
|
|
|
|
|
cmd->state = UB_CMDST_CMD;
|
|
|
|
ub_cmdtr_state(sc, cmd);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Timeout handler.
|
|
|
|
*/
|
|
|
|
static void ub_urb_timeout(unsigned long arg)
|
|
|
|
{
|
|
|
|
struct ub_dev *sc = (struct ub_dev *) arg;
|
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
spin_lock_irqsave(&sc->lock, flags);
|
|
|
|
usb_unlink_urb(&sc->work_urb);
|
|
|
|
spin_unlock_irqrestore(&sc->lock, flags);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Completion routine for the work URB.
|
|
|
|
*
|
|
|
|
* This can be called directly from usb_submit_urb (while we have
|
|
|
|
* the sc->lock taken) and from an interrupt (while we do NOT have
|
|
|
|
* the sc->lock taken). Therefore, bounce this off to a tasklet.
|
|
|
|
*/
|
|
|
|
static void ub_urb_complete(struct urb *urb, struct pt_regs *pt)
|
|
|
|
{
|
|
|
|
struct ub_dev *sc = urb->context;
|
|
|
|
|
|
|
|
ub_complete(&sc->work_done);
|
|
|
|
tasklet_schedule(&sc->tasklet);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ub_scsi_action(unsigned long _dev)
|
|
|
|
{
|
|
|
|
struct ub_dev *sc = (struct ub_dev *) _dev;
|
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
spin_lock_irqsave(&sc->lock, flags);
|
|
|
|
del_timer(&sc->work_timer);
|
|
|
|
ub_scsi_dispatch(sc);
|
|
|
|
spin_unlock_irqrestore(&sc->lock, flags);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ub_scsi_dispatch(struct ub_dev *sc)
|
|
|
|
{
|
|
|
|
struct ub_scsi_cmd *cmd;
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
while ((cmd = ub_cmdq_peek(sc)) != NULL) {
|
|
|
|
if (cmd->state == UB_CMDST_DONE) {
|
|
|
|
ub_cmdq_pop(sc);
|
|
|
|
(*cmd->done)(sc, cmd);
|
|
|
|
} else if (cmd->state == UB_CMDST_INIT) {
|
|
|
|
ub_cmdtr_new(sc, cmd);
|
|
|
|
if ((rc = ub_scsi_cmd_start(sc, cmd)) == 0)
|
|
|
|
break;
|
|
|
|
cmd->error = rc;
|
|
|
|
cmd->state = UB_CMDST_DONE;
|
|
|
|
ub_cmdtr_state(sc, cmd);
|
|
|
|
} else {
|
|
|
|
if (!ub_is_completed(&sc->work_done))
|
|
|
|
break;
|
|
|
|
ub_scsi_urb_compl(sc, cmd);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ub_scsi_urb_compl(struct ub_dev *sc, struct ub_scsi_cmd *cmd)
|
|
|
|
{
|
|
|
|
struct urb *urb = &sc->work_urb;
|
|
|
|
struct bulk_cs_wrap *bcs;
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
if (atomic_read(&sc->poison)) {
|
|
|
|
/* A little too simplistic, I feel... */
|
|
|
|
goto Bad_End;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cmd->state == UB_CMDST_CLEAR) {
|
|
|
|
if (urb->status == -EPIPE) {
|
|
|
|
/*
|
|
|
|
* STALL while clearning STALL.
|
|
|
|
* The control pipe clears itself - nothing to do.
|
|
|
|
* XXX Might try to reset the device here and retry.
|
|
|
|
*/
|
2005-05-02 01:05:40 +02:00
|
|
|
printk(KERN_NOTICE "%s: stall on control pipe\n",
|
|
|
|
sc->name);
|
2005-04-17 00:20:36 +02:00
|
|
|
goto Bad_End;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We ignore the result for the halt clear.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* reset the endpoint toggle */
|
|
|
|
usb_settoggle(sc->dev, usb_pipeendpoint(sc->last_pipe),
|
|
|
|
usb_pipeout(sc->last_pipe), 0);
|
|
|
|
|
|
|
|
ub_state_sense(sc, cmd);
|
|
|
|
|
|
|
|
} else if (cmd->state == UB_CMDST_CLR2STS) {
|
|
|
|
if (urb->status == -EPIPE) {
|
|
|
|
/*
|
|
|
|
* STALL while clearning STALL.
|
|
|
|
* The control pipe clears itself - nothing to do.
|
|
|
|
* XXX Might try to reset the device here and retry.
|
|
|
|
*/
|
2005-05-02 01:05:40 +02:00
|
|
|
printk(KERN_NOTICE "%s: stall on control pipe\n",
|
|
|
|
sc->name);
|
2005-04-17 00:20:36 +02:00
|
|
|
goto Bad_End;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We ignore the result for the halt clear.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* reset the endpoint toggle */
|
|
|
|
usb_settoggle(sc->dev, usb_pipeendpoint(sc->last_pipe),
|
|
|
|
usb_pipeout(sc->last_pipe), 0);
|
|
|
|
|
|
|
|
ub_state_stat(sc, cmd);
|
|
|
|
|
2005-07-27 20:43:51 +02:00
|
|
|
} else if (cmd->state == UB_CMDST_CLRRS) {
|
|
|
|
if (urb->status == -EPIPE) {
|
|
|
|
/*
|
|
|
|
* STALL while clearning STALL.
|
|
|
|
* The control pipe clears itself - nothing to do.
|
|
|
|
* XXX Might try to reset the device here and retry.
|
|
|
|
*/
|
|
|
|
printk(KERN_NOTICE "%s: stall on control pipe\n",
|
|
|
|
sc->name);
|
|
|
|
goto Bad_End;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We ignore the result for the halt clear.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* reset the endpoint toggle */
|
|
|
|
usb_settoggle(sc->dev, usb_pipeendpoint(sc->last_pipe),
|
|
|
|
usb_pipeout(sc->last_pipe), 0);
|
|
|
|
|
|
|
|
ub_state_stat_counted(sc, cmd);
|
|
|
|
|
2005-04-17 00:20:36 +02:00
|
|
|
} else if (cmd->state == UB_CMDST_CMD) {
|
|
|
|
if (urb->status == -EPIPE) {
|
|
|
|
rc = ub_submit_clear_stall(sc, cmd, sc->last_pipe);
|
|
|
|
if (rc != 0) {
|
|
|
|
printk(KERN_NOTICE "%s: "
|
2005-05-02 01:05:40 +02:00
|
|
|
"unable to submit clear (%d)\n",
|
|
|
|
sc->name, rc);
|
2005-04-17 00:20:36 +02:00
|
|
|
/*
|
|
|
|
* This is typically ENOMEM or some other such shit.
|
|
|
|
* Retrying is pointless. Just do Bad End on it...
|
|
|
|
*/
|
|
|
|
goto Bad_End;
|
|
|
|
}
|
|
|
|
cmd->state = UB_CMDST_CLEAR;
|
|
|
|
ub_cmdtr_state(sc, cmd);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (urb->status != 0) {
|
|
|
|
printk("ub: cmd #%d cmd status (%d)\n", cmd->tag, urb->status); /* P3 */
|
|
|
|
goto Bad_End;
|
|
|
|
}
|
|
|
|
if (urb->actual_length != US_BULK_CB_WRAP_LEN) {
|
|
|
|
printk("ub: cmd #%d xferred %d\n", cmd->tag, urb->actual_length); /* P3 */
|
|
|
|
/* XXX Must do reset here to unconfuse the device */
|
|
|
|
goto Bad_End;
|
|
|
|
}
|
|
|
|
|
2005-08-15 06:16:03 +02:00
|
|
|
if (cmd->dir == UB_DIR_NONE || cmd->nsg < 1) {
|
2005-04-17 00:20:36 +02:00
|
|
|
ub_state_stat(sc, cmd);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2005-08-15 06:16:03 +02:00
|
|
|
// udelay(125); // usb-storage has this
|
|
|
|
ub_data_start(sc, cmd);
|
2005-04-17 00:20:36 +02:00
|
|
|
|
|
|
|
} else if (cmd->state == UB_CMDST_DATA) {
|
|
|
|
if (urb->status == -EPIPE) {
|
|
|
|
rc = ub_submit_clear_stall(sc, cmd, sc->last_pipe);
|
|
|
|
if (rc != 0) {
|
|
|
|
printk(KERN_NOTICE "%s: "
|
2005-05-02 01:05:40 +02:00
|
|
|
"unable to submit clear (%d)\n",
|
|
|
|
sc->name, rc);
|
2005-04-17 00:20:36 +02:00
|
|
|
/*
|
|
|
|
* This is typically ENOMEM or some other such shit.
|
|
|
|
* Retrying is pointless. Just do Bad End on it...
|
|
|
|
*/
|
|
|
|
goto Bad_End;
|
|
|
|
}
|
|
|
|
cmd->state = UB_CMDST_CLR2STS;
|
|
|
|
ub_cmdtr_state(sc, cmd);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (urb->status == -EOVERFLOW) {
|
|
|
|
/*
|
|
|
|
* A babble? Failure, but we must transfer CSW now.
|
2005-08-15 06:16:03 +02:00
|
|
|
* XXX This is going to end in perpetual babble. Reset.
|
2005-04-17 00:20:36 +02:00
|
|
|
*/
|
|
|
|
cmd->error = -EOVERFLOW; /* A cheap trick... */
|
2005-08-15 06:16:03 +02:00
|
|
|
ub_state_stat(sc, cmd);
|
|
|
|
return;
|
2005-04-17 00:20:36 +02:00
|
|
|
}
|
2005-08-15 06:16:03 +02:00
|
|
|
if (urb->status != 0)
|
|
|
|
goto Bad_End;
|
2005-04-17 00:20:36 +02:00
|
|
|
|
2005-08-15 06:16:03 +02:00
|
|
|
cmd->act_len += urb->actual_length;
|
2005-04-17 00:20:36 +02:00
|
|
|
ub_cmdtr_act_len(sc, cmd);
|
|
|
|
|
2005-08-15 06:16:03 +02:00
|
|
|
if (++cmd->current_sg < cmd->nsg) {
|
|
|
|
ub_data_start(sc, cmd);
|
|
|
|
return;
|
|
|
|
}
|
2005-04-17 00:20:36 +02:00
|
|
|
ub_state_stat(sc, cmd);
|
|
|
|
|
|
|
|
} else if (cmd->state == UB_CMDST_STAT) {
|
|
|
|
if (urb->status == -EPIPE) {
|
|
|
|
rc = ub_submit_clear_stall(sc, cmd, sc->last_pipe);
|
|
|
|
if (rc != 0) {
|
|
|
|
printk(KERN_NOTICE "%s: "
|
2005-05-02 01:05:40 +02:00
|
|
|
"unable to submit clear (%d)\n",
|
|
|
|
sc->name, rc);
|
2005-04-17 00:20:36 +02:00
|
|
|
/*
|
|
|
|
* This is typically ENOMEM or some other such shit.
|
|
|
|
* Retrying is pointless. Just do Bad End on it...
|
|
|
|
*/
|
|
|
|
goto Bad_End;
|
|
|
|
}
|
2005-07-27 20:43:51 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Having a stall when getting CSW is an error, so
|
|
|
|
* make sure uppper levels are not oblivious to it.
|
|
|
|
*/
|
|
|
|
cmd->error = -EIO; /* A cheap trick... */
|
|
|
|
|
|
|
|
cmd->state = UB_CMDST_CLRRS;
|
2005-04-17 00:20:36 +02:00
|
|
|
ub_cmdtr_state(sc, cmd);
|
|
|
|
return;
|
|
|
|
}
|
2005-07-27 20:43:51 +02:00
|
|
|
if (urb->status == -EOVERFLOW) {
|
|
|
|
/*
|
|
|
|
* XXX We are screwed here. Retrying is pointless,
|
|
|
|
* because the pipelined data will not get in until
|
|
|
|
* we read with a big enough buffer. We must reset XXX.
|
|
|
|
*/
|
|
|
|
goto Bad_End;
|
|
|
|
}
|
2005-04-17 00:20:36 +02:00
|
|
|
if (urb->status != 0)
|
|
|
|
goto Bad_End;
|
|
|
|
|
|
|
|
if (urb->actual_length == 0) {
|
2005-07-27 20:43:51 +02:00
|
|
|
ub_state_stat_counted(sc, cmd);
|
2005-04-17 00:20:36 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Check the returned Bulk protocol status.
|
2005-07-27 20:43:51 +02:00
|
|
|
* The status block has to be validated first.
|
2005-04-17 00:20:36 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
bcs = &sc->work_bcs;
|
2005-07-27 20:43:51 +02:00
|
|
|
|
|
|
|
if (sc->signature == cpu_to_le32(0)) {
|
2005-04-17 00:20:36 +02:00
|
|
|
/*
|
2005-07-27 20:43:51 +02:00
|
|
|
* This is the first reply, so do not perform the check.
|
|
|
|
* Instead, remember the signature the device uses
|
|
|
|
* for future checks. But do not allow a nul.
|
2005-04-17 00:20:36 +02:00
|
|
|
*/
|
2005-07-27 20:43:51 +02:00
|
|
|
sc->signature = bcs->Signature;
|
|
|
|
if (sc->signature == cpu_to_le32(0)) {
|
|
|
|
ub_state_stat_counted(sc, cmd);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (bcs->Signature != sc->signature) {
|
|
|
|
ub_state_stat_counted(sc, cmd);
|
|
|
|
return;
|
|
|
|
}
|
2005-04-17 00:20:36 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (bcs->Tag != cmd->tag) {
|
|
|
|
/*
|
|
|
|
* This usually happens when we disagree with the
|
|
|
|
* device's microcode about something. For instance,
|
|
|
|
* a few of them throw this after timeouts. They buffer
|
|
|
|
* commands and reply at commands we timed out before.
|
|
|
|
* Without flushing these replies we loop forever.
|
|
|
|
*/
|
2005-07-27 20:43:51 +02:00
|
|
|
ub_state_stat_counted(sc, cmd);
|
2005-04-17 00:20:36 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2005-07-27 20:43:51 +02:00
|
|
|
rc = le32_to_cpu(bcs->Residue);
|
|
|
|
if (rc != cmd->len - cmd->act_len) {
|
|
|
|
/*
|
|
|
|
* It is all right to transfer less, the caller has
|
|
|
|
* to check. But it's not all right if the device
|
|
|
|
* counts disagree with our counts.
|
|
|
|
*/
|
|
|
|
/* P3 */ printk("%s: resid %d len %d act %d\n",
|
|
|
|
sc->name, rc, cmd->len, cmd->act_len);
|
|
|
|
goto Bad_End;
|
|
|
|
}
|
|
|
|
|
2005-04-17 00:20:36 +02:00
|
|
|
switch (bcs->Status) {
|
|
|
|
case US_BULK_STAT_OK:
|
|
|
|
break;
|
|
|
|
case US_BULK_STAT_FAIL:
|
|
|
|
ub_state_sense(sc, cmd);
|
|
|
|
return;
|
|
|
|
case US_BULK_STAT_PHASE:
|
|
|
|
/* XXX We must reset the transport here */
|
|
|
|
/* P3 */ printk("%s: status PHASE\n", sc->name);
|
|
|
|
goto Bad_End;
|
|
|
|
default:
|
|
|
|
printk(KERN_INFO "%s: unknown CSW status 0x%x\n",
|
|
|
|
sc->name, bcs->Status);
|
|
|
|
goto Bad_End;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Not zeroing error to preserve a babble indicator */
|
2005-07-27 20:43:51 +02:00
|
|
|
if (cmd->error != 0) {
|
|
|
|
ub_state_sense(sc, cmd);
|
|
|
|
return;
|
|
|
|
}
|
2005-04-17 00:20:36 +02:00
|
|
|
cmd->state = UB_CMDST_DONE;
|
|
|
|
ub_cmdtr_state(sc, cmd);
|
|
|
|
ub_cmdq_pop(sc);
|
|
|
|
(*cmd->done)(sc, cmd);
|
|
|
|
|
|
|
|
} else if (cmd->state == UB_CMDST_SENSE) {
|
|
|
|
ub_state_done(sc, cmd, -EIO);
|
|
|
|
|
|
|
|
} else {
|
|
|
|
printk(KERN_WARNING "%s: "
|
2005-05-02 01:05:40 +02:00
|
|
|
"wrong command state %d\n",
|
|
|
|
sc->name, cmd->state);
|
2005-04-17 00:20:36 +02:00
|
|
|
goto Bad_End;
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
|
|
|
|
Bad_End: /* Little Excel is dead */
|
|
|
|
ub_state_done(sc, cmd, -EIO);
|
|
|
|
}
|
|
|
|
|
2005-08-15 06:16:03 +02:00
|
|
|
/*
|
|
|
|
* Factorization helper for the command state machine:
|
|
|
|
* Initiate a data segment transfer.
|
|
|
|
*/
|
|
|
|
static void ub_data_start(struct ub_dev *sc, struct ub_scsi_cmd *cmd)
|
|
|
|
{
|
|
|
|
struct scatterlist *sg = &cmd->sgv[cmd->current_sg];
|
|
|
|
int pipe;
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
UB_INIT_COMPLETION(sc->work_done);
|
|
|
|
|
|
|
|
if (cmd->dir == UB_DIR_READ)
|
|
|
|
pipe = sc->recv_bulk_pipe;
|
|
|
|
else
|
|
|
|
pipe = sc->send_bulk_pipe;
|
|
|
|
sc->last_pipe = pipe;
|
|
|
|
usb_fill_bulk_urb(&sc->work_urb, sc->dev, pipe,
|
|
|
|
page_address(sg->page) + sg->offset, sg->length,
|
|
|
|
ub_urb_complete, sc);
|
[PATCH] USB: URB_ASYNC_UNLINK flag removed from the kernel
29 July 2005, Cambridge, MA:
This afternoon Alan Stern submitted a patch to remove the URB_ASYNC_UNLINK
flag from the Linux kernel. Mr. Stern explained, "This flag is a relic
from an earlier, less-well-designed system. For over a year it hasn't
been used for anything other than printing warning messages."
An anonymous spokesman for the Linux kernel development community
commented, "This is exactly the sort of thing we see happening all the
time. As the kernel evolves, support for old techniques and old code can
be jettisoned and replaced by newer, better approaches. Proprietary
operating systems do not have the freedom or flexibility to change so
quickly."
Mr. Stern, a staff member at Harvard University's Rowland Institute who
works on Linux only as a hobby, noted that the patch (labelled as548) did
not update two files, keyspan.c and option.c, in the USB drivers' "serial"
subdirectory. "Those files need more extensive changes," he remarked.
"They examine the status field of several URBs at times when they're not
supposed to. That will need to be fixed before the URB_ASYNC_UNLINK flag
is removed."
Greg Kroah-Hartman, the kernel maintainer responsible for overseeing all
of Linux's USB drivers, did not respond to our inquiries or return our
calls. His only comment was "Applied, thanks."
Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
2005-07-29 22:11:07 +02:00
|
|
|
sc->work_urb.transfer_flags = 0;
|
2005-08-15 06:16:03 +02:00
|
|
|
sc->work_urb.actual_length = 0;
|
|
|
|
sc->work_urb.error_count = 0;
|
|
|
|
sc->work_urb.status = 0;
|
|
|
|
|
|
|
|
if ((rc = usb_submit_urb(&sc->work_urb, GFP_ATOMIC)) != 0) {
|
|
|
|
/* XXX Clear stalls */
|
|
|
|
printk("ub: data #%d submit failed (%d)\n", cmd->tag, rc); /* P3 */
|
|
|
|
ub_complete(&sc->work_done);
|
|
|
|
ub_state_done(sc, cmd, rc);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
sc->work_timer.expires = jiffies + UB_DATA_TIMEOUT;
|
|
|
|
add_timer(&sc->work_timer);
|
|
|
|
|
|
|
|
cmd->state = UB_CMDST_DATA;
|
|
|
|
ub_cmdtr_state(sc, cmd);
|
|
|
|
}
|
|
|
|
|
2005-04-17 00:20:36 +02:00
|
|
|
/*
|
|
|
|
* Factorization helper for the command state machine:
|
|
|
|
* Finish the command.
|
|
|
|
*/
|
|
|
|
static void ub_state_done(struct ub_dev *sc, struct ub_scsi_cmd *cmd, int rc)
|
|
|
|
{
|
|
|
|
|
|
|
|
cmd->error = rc;
|
|
|
|
cmd->state = UB_CMDST_DONE;
|
|
|
|
ub_cmdtr_state(sc, cmd);
|
|
|
|
ub_cmdq_pop(sc);
|
|
|
|
(*cmd->done)(sc, cmd);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Factorization helper for the command state machine:
|
|
|
|
* Submit a CSW read.
|
|
|
|
*/
|
2005-07-27 20:43:51 +02:00
|
|
|
static int __ub_state_stat(struct ub_dev *sc, struct ub_scsi_cmd *cmd)
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
UB_INIT_COMPLETION(sc->work_done);
|
|
|
|
|
|
|
|
sc->last_pipe = sc->recv_bulk_pipe;
|
|
|
|
usb_fill_bulk_urb(&sc->work_urb, sc->dev, sc->recv_bulk_pipe,
|
|
|
|
&sc->work_bcs, US_BULK_CS_WRAP_LEN, ub_urb_complete, sc);
|
[PATCH] USB: URB_ASYNC_UNLINK flag removed from the kernel
29 July 2005, Cambridge, MA:
This afternoon Alan Stern submitted a patch to remove the URB_ASYNC_UNLINK
flag from the Linux kernel. Mr. Stern explained, "This flag is a relic
from an earlier, less-well-designed system. For over a year it hasn't
been used for anything other than printing warning messages."
An anonymous spokesman for the Linux kernel development community
commented, "This is exactly the sort of thing we see happening all the
time. As the kernel evolves, support for old techniques and old code can
be jettisoned and replaced by newer, better approaches. Proprietary
operating systems do not have the freedom or flexibility to change so
quickly."
Mr. Stern, a staff member at Harvard University's Rowland Institute who
works on Linux only as a hobby, noted that the patch (labelled as548) did
not update two files, keyspan.c and option.c, in the USB drivers' "serial"
subdirectory. "Those files need more extensive changes," he remarked.
"They examine the status field of several URBs at times when they're not
supposed to. That will need to be fixed before the URB_ASYNC_UNLINK flag
is removed."
Greg Kroah-Hartman, the kernel maintainer responsible for overseeing all
of Linux's USB drivers, did not respond to our inquiries or return our
calls. His only comment was "Applied, thanks."
Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
2005-07-29 22:11:07 +02:00
|
|
|
sc->work_urb.transfer_flags = 0;
|
2005-04-17 00:20:36 +02:00
|
|
|
sc->work_urb.actual_length = 0;
|
|
|
|
sc->work_urb.error_count = 0;
|
|
|
|
sc->work_urb.status = 0;
|
|
|
|
|
|
|
|
if ((rc = usb_submit_urb(&sc->work_urb, GFP_ATOMIC)) != 0) {
|
|
|
|
/* XXX Clear stalls */
|
|
|
|
ub_complete(&sc->work_done);
|
|
|
|
ub_state_done(sc, cmd, rc);
|
2005-07-27 20:43:51 +02:00
|
|
|
return -1;
|
2005-04-17 00:20:36 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
sc->work_timer.expires = jiffies + UB_STAT_TIMEOUT;
|
|
|
|
add_timer(&sc->work_timer);
|
2005-07-27 20:43:51 +02:00
|
|
|
return 0;
|
2005-04-17 00:20:36 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Factorization helper for the command state machine:
|
|
|
|
* Submit a CSW read and go to STAT state.
|
|
|
|
*/
|
|
|
|
static void ub_state_stat(struct ub_dev *sc, struct ub_scsi_cmd *cmd)
|
|
|
|
{
|
2005-07-27 20:43:51 +02:00
|
|
|
|
|
|
|
if (__ub_state_stat(sc, cmd) != 0)
|
|
|
|
return;
|
2005-04-17 00:20:36 +02:00
|
|
|
|
|
|
|
cmd->stat_count = 0;
|
|
|
|
cmd->state = UB_CMDST_STAT;
|
2005-07-27 20:43:51 +02:00
|
|
|
ub_cmdtr_state(sc, cmd);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Factorization helper for the command state machine:
|
|
|
|
* Submit a CSW read and go to STAT state with counter (along [C] path).
|
|
|
|
*/
|
|
|
|
static void ub_state_stat_counted(struct ub_dev *sc, struct ub_scsi_cmd *cmd)
|
|
|
|
{
|
|
|
|
|
|
|
|
if (++cmd->stat_count >= 4) {
|
|
|
|
ub_state_sense(sc, cmd);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (__ub_state_stat(sc, cmd) != 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
cmd->state = UB_CMDST_STAT;
|
2005-04-17 00:20:36 +02:00
|
|
|
ub_cmdtr_state(sc, cmd);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Factorization helper for the command state machine:
|
|
|
|
* Submit a REQUEST SENSE and go to SENSE state.
|
|
|
|
*/
|
|
|
|
static void ub_state_sense(struct ub_dev *sc, struct ub_scsi_cmd *cmd)
|
|
|
|
{
|
|
|
|
struct ub_scsi_cmd *scmd;
|
2005-08-15 06:16:03 +02:00
|
|
|
struct scatterlist *sg;
|
2005-04-17 00:20:36 +02:00
|
|
|
int rc;
|
|
|
|
|
|
|
|
if (cmd->cdb[0] == REQUEST_SENSE) {
|
|
|
|
rc = -EPIPE;
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
scmd = &sc->top_rqs_cmd;
|
2005-08-15 06:16:03 +02:00
|
|
|
memset(scmd, 0, sizeof(struct ub_scsi_cmd));
|
2005-04-17 00:20:36 +02:00
|
|
|
scmd->cdb[0] = REQUEST_SENSE;
|
|
|
|
scmd->cdb[4] = UB_SENSE_SIZE;
|
|
|
|
scmd->cdb_len = 6;
|
|
|
|
scmd->dir = UB_DIR_READ;
|
|
|
|
scmd->state = UB_CMDST_INIT;
|
2005-08-15 06:16:03 +02:00
|
|
|
scmd->nsg = 1;
|
|
|
|
sg = &scmd->sgv[0];
|
|
|
|
sg->page = virt_to_page(sc->top_sense);
|
|
|
|
sg->offset = (unsigned int)sc->top_sense & (PAGE_SIZE-1);
|
|
|
|
sg->length = UB_SENSE_SIZE;
|
2005-04-17 00:20:36 +02:00
|
|
|
scmd->len = UB_SENSE_SIZE;
|
2005-05-02 01:05:40 +02:00
|
|
|
scmd->lun = cmd->lun;
|
2005-04-17 00:20:36 +02:00
|
|
|
scmd->done = ub_top_sense_done;
|
|
|
|
scmd->back = cmd;
|
|
|
|
|
|
|
|
scmd->tag = sc->tagcnt++;
|
|
|
|
|
|
|
|
cmd->state = UB_CMDST_SENSE;
|
|
|
|
ub_cmdtr_state(sc, cmd);
|
|
|
|
|
|
|
|
ub_cmdq_insert(sc, scmd);
|
|
|
|
return;
|
|
|
|
|
|
|
|
error:
|
|
|
|
ub_state_done(sc, cmd, rc);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* A helper for the command's state machine:
|
|
|
|
* Submit a stall clear.
|
|
|
|
*/
|
|
|
|
static int ub_submit_clear_stall(struct ub_dev *sc, struct ub_scsi_cmd *cmd,
|
|
|
|
int stalled_pipe)
|
|
|
|
{
|
|
|
|
int endp;
|
|
|
|
struct usb_ctrlrequest *cr;
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
endp = usb_pipeendpoint(stalled_pipe);
|
|
|
|
if (usb_pipein (stalled_pipe))
|
|
|
|
endp |= USB_DIR_IN;
|
|
|
|
|
|
|
|
cr = &sc->work_cr;
|
|
|
|
cr->bRequestType = USB_RECIP_ENDPOINT;
|
|
|
|
cr->bRequest = USB_REQ_CLEAR_FEATURE;
|
|
|
|
cr->wValue = cpu_to_le16(USB_ENDPOINT_HALT);
|
|
|
|
cr->wIndex = cpu_to_le16(endp);
|
|
|
|
cr->wLength = cpu_to_le16(0);
|
|
|
|
|
|
|
|
UB_INIT_COMPLETION(sc->work_done);
|
|
|
|
|
|
|
|
usb_fill_control_urb(&sc->work_urb, sc->dev, sc->send_ctrl_pipe,
|
|
|
|
(unsigned char*) cr, NULL, 0, ub_urb_complete, sc);
|
[PATCH] USB: URB_ASYNC_UNLINK flag removed from the kernel
29 July 2005, Cambridge, MA:
This afternoon Alan Stern submitted a patch to remove the URB_ASYNC_UNLINK
flag from the Linux kernel. Mr. Stern explained, "This flag is a relic
from an earlier, less-well-designed system. For over a year it hasn't
been used for anything other than printing warning messages."
An anonymous spokesman for the Linux kernel development community
commented, "This is exactly the sort of thing we see happening all the
time. As the kernel evolves, support for old techniques and old code can
be jettisoned and replaced by newer, better approaches. Proprietary
operating systems do not have the freedom or flexibility to change so
quickly."
Mr. Stern, a staff member at Harvard University's Rowland Institute who
works on Linux only as a hobby, noted that the patch (labelled as548) did
not update two files, keyspan.c and option.c, in the USB drivers' "serial"
subdirectory. "Those files need more extensive changes," he remarked.
"They examine the status field of several URBs at times when they're not
supposed to. That will need to be fixed before the URB_ASYNC_UNLINK flag
is removed."
Greg Kroah-Hartman, the kernel maintainer responsible for overseeing all
of Linux's USB drivers, did not respond to our inquiries or return our
calls. His only comment was "Applied, thanks."
Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
2005-07-29 22:11:07 +02:00
|
|
|
sc->work_urb.transfer_flags = 0;
|
2005-04-17 00:20:36 +02:00
|
|
|
sc->work_urb.actual_length = 0;
|
|
|
|
sc->work_urb.error_count = 0;
|
|
|
|
sc->work_urb.status = 0;
|
|
|
|
|
|
|
|
if ((rc = usb_submit_urb(&sc->work_urb, GFP_ATOMIC)) != 0) {
|
|
|
|
ub_complete(&sc->work_done);
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
sc->work_timer.expires = jiffies + UB_CTRL_TIMEOUT;
|
|
|
|
add_timer(&sc->work_timer);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
*/
|
|
|
|
static void ub_top_sense_done(struct ub_dev *sc, struct ub_scsi_cmd *scmd)
|
|
|
|
{
|
2005-08-15 06:16:03 +02:00
|
|
|
unsigned char *sense = sc->top_sense;
|
2005-04-17 00:20:36 +02:00
|
|
|
struct ub_scsi_cmd *cmd;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Ignoring scmd->act_len, because the buffer was pre-zeroed.
|
|
|
|
*/
|
|
|
|
ub_cmdtr_sense(sc, scmd, sense);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Find the command which triggered the unit attention or a check,
|
|
|
|
* save the sense into it, and advance its state machine.
|
|
|
|
*/
|
|
|
|
if ((cmd = ub_cmdq_peek(sc)) == NULL) {
|
|
|
|
printk(KERN_WARNING "%s: sense done while idle\n", sc->name);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (cmd != scmd->back) {
|
|
|
|
printk(KERN_WARNING "%s: "
|
2005-05-02 01:05:40 +02:00
|
|
|
"sense done for wrong command 0x%x\n",
|
|
|
|
sc->name, cmd->tag);
|
2005-04-17 00:20:36 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (cmd->state != UB_CMDST_SENSE) {
|
|
|
|
printk(KERN_WARNING "%s: "
|
2005-05-02 01:05:40 +02:00
|
|
|
"sense done with bad cmd state %d\n",
|
|
|
|
sc->name, cmd->state);
|
2005-04-17 00:20:36 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
cmd->key = sense[2] & 0x0F;
|
|
|
|
cmd->asc = sense[12];
|
|
|
|
cmd->ascq = sense[13];
|
|
|
|
|
|
|
|
ub_scsi_urb_compl(sc, cmd);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This is called from a process context.
|
|
|
|
*/
|
2005-05-02 01:05:40 +02:00
|
|
|
static void ub_revalidate(struct ub_dev *sc, struct ub_lun *lun)
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
|
|
|
|
2005-05-02 01:05:40 +02:00
|
|
|
lun->readonly = 0; /* XXX Query this from the device */
|
2005-04-17 00:20:36 +02:00
|
|
|
|
2005-05-02 01:05:40 +02:00
|
|
|
lun->capacity.nsec = 0;
|
|
|
|
lun->capacity.bsize = 512;
|
|
|
|
lun->capacity.bshift = 0;
|
2005-04-17 00:20:36 +02:00
|
|
|
|
2005-05-02 01:05:40 +02:00
|
|
|
if (ub_sync_tur(sc, lun) != 0)
|
2005-04-17 00:20:36 +02:00
|
|
|
return; /* Not ready */
|
2005-05-02 01:05:40 +02:00
|
|
|
lun->changed = 0;
|
2005-04-17 00:20:36 +02:00
|
|
|
|
2005-05-02 01:05:40 +02:00
|
|
|
if (ub_sync_read_cap(sc, lun, &lun->capacity) != 0) {
|
2005-04-17 00:20:36 +02:00
|
|
|
/*
|
|
|
|
* The retry here means something is wrong, either with the
|
|
|
|
* device, with the transport, or with our code.
|
|
|
|
* We keep this because sd.c has retries for capacity.
|
|
|
|
*/
|
2005-05-02 01:05:40 +02:00
|
|
|
if (ub_sync_read_cap(sc, lun, &lun->capacity) != 0) {
|
|
|
|
lun->capacity.nsec = 0;
|
|
|
|
lun->capacity.bsize = 512;
|
|
|
|
lun->capacity.bshift = 0;
|
2005-04-17 00:20:36 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The open funcion.
|
|
|
|
* This is mostly needed to keep refcounting, but also to support
|
|
|
|
* media checks on removable media drives.
|
|
|
|
*/
|
|
|
|
static int ub_bd_open(struct inode *inode, struct file *filp)
|
|
|
|
{
|
|
|
|
struct gendisk *disk = inode->i_bdev->bd_disk;
|
2005-05-02 01:05:40 +02:00
|
|
|
struct ub_lun *lun;
|
2005-04-17 00:20:36 +02:00
|
|
|
struct ub_dev *sc;
|
|
|
|
unsigned long flags;
|
|
|
|
int rc;
|
|
|
|
|
2005-05-02 01:05:40 +02:00
|
|
|
if ((lun = disk->private_data) == NULL)
|
2005-04-17 00:20:36 +02:00
|
|
|
return -ENXIO;
|
2005-05-02 01:05:40 +02:00
|
|
|
sc = lun->udev;
|
|
|
|
|
2005-04-17 00:20:36 +02:00
|
|
|
spin_lock_irqsave(&ub_lock, flags);
|
|
|
|
if (atomic_read(&sc->poison)) {
|
|
|
|
spin_unlock_irqrestore(&ub_lock, flags);
|
|
|
|
return -ENXIO;
|
|
|
|
}
|
|
|
|
sc->openc++;
|
|
|
|
spin_unlock_irqrestore(&ub_lock, flags);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This is a workaround for a specific problem in our block layer.
|
|
|
|
* In 2.6.9, register_disk duplicates the code from rescan_partitions.
|
|
|
|
* However, if we do add_disk with a device which persistently reports
|
|
|
|
* a changed media, add_disk calls register_disk, which does do_open,
|
|
|
|
* which will call rescan_paritions for changed media. After that,
|
|
|
|
* register_disk attempts to do it all again and causes double kobject
|
|
|
|
* registration and a eventually an oops on module removal.
|
|
|
|
*
|
|
|
|
* The bottom line is, Al Viro says that we should not allow
|
|
|
|
* bdev->bd_invalidated to be set when doing add_disk no matter what.
|
|
|
|
*/
|
2005-05-02 01:05:40 +02:00
|
|
|
if (lun->first_open) {
|
|
|
|
lun->first_open = 0;
|
|
|
|
if (lun->changed) {
|
2005-04-17 00:20:36 +02:00
|
|
|
rc = -ENOMEDIUM;
|
|
|
|
goto err_open;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-05-02 01:05:40 +02:00
|
|
|
if (lun->removable || lun->readonly)
|
2005-04-17 00:20:36 +02:00
|
|
|
check_disk_change(inode->i_bdev);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The sd.c considers ->media_present and ->changed not equivalent,
|
|
|
|
* under some pretty murky conditions (a failure of READ CAPACITY).
|
|
|
|
* We may need it one day.
|
|
|
|
*/
|
2005-05-02 01:05:40 +02:00
|
|
|
if (lun->removable && lun->changed && !(filp->f_flags & O_NDELAY)) {
|
2005-04-17 00:20:36 +02:00
|
|
|
rc = -ENOMEDIUM;
|
|
|
|
goto err_open;
|
|
|
|
}
|
|
|
|
|
2005-05-02 01:05:40 +02:00
|
|
|
if (lun->readonly && (filp->f_mode & FMODE_WRITE)) {
|
2005-04-17 00:20:36 +02:00
|
|
|
rc = -EROFS;
|
|
|
|
goto err_open;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
err_open:
|
|
|
|
ub_put(sc);
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
*/
|
|
|
|
static int ub_bd_release(struct inode *inode, struct file *filp)
|
|
|
|
{
|
|
|
|
struct gendisk *disk = inode->i_bdev->bd_disk;
|
2005-05-02 01:05:40 +02:00
|
|
|
struct ub_lun *lun = disk->private_data;
|
|
|
|
struct ub_dev *sc = lun->udev;
|
2005-04-17 00:20:36 +02:00
|
|
|
|
|
|
|
ub_put(sc);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The ioctl interface.
|
|
|
|
*/
|
|
|
|
static int ub_bd_ioctl(struct inode *inode, struct file *filp,
|
|
|
|
unsigned int cmd, unsigned long arg)
|
|
|
|
{
|
|
|
|
struct gendisk *disk = inode->i_bdev->bd_disk;
|
|
|
|
void __user *usermem = (void __user *) arg;
|
|
|
|
|
|
|
|
return scsi_cmd_ioctl(filp, disk, cmd, usermem);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This is called once a new disk was seen by the block layer or by ub_probe().
|
|
|
|
* The main onjective here is to discover the features of the media such as
|
|
|
|
* the capacity, read-only status, etc. USB storage generally does not
|
|
|
|
* need to be spun up, but if we needed it, this would be the place.
|
|
|
|
*
|
|
|
|
* This call can sleep.
|
|
|
|
*
|
|
|
|
* The return code is not used.
|
|
|
|
*/
|
|
|
|
static int ub_bd_revalidate(struct gendisk *disk)
|
|
|
|
{
|
2005-05-02 01:05:40 +02:00
|
|
|
struct ub_lun *lun = disk->private_data;
|
|
|
|
|
|
|
|
ub_revalidate(lun->udev, lun);
|
2005-04-17 00:20:36 +02:00
|
|
|
|
|
|
|
/* XXX Support sector size switching like in sr.c */
|
2005-05-02 01:05:40 +02:00
|
|
|
blk_queue_hardsect_size(disk->queue, lun->capacity.bsize);
|
|
|
|
set_capacity(disk, lun->capacity.nsec);
|
|
|
|
// set_disk_ro(sdkp->disk, lun->readonly);
|
2005-04-17 00:20:36 +02:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The check is called by the block layer to verify if the media
|
|
|
|
* is still available. It is supposed to be harmless, lightweight and
|
|
|
|
* non-intrusive in case the media was not changed.
|
|
|
|
*
|
|
|
|
* This call can sleep.
|
|
|
|
*
|
|
|
|
* The return code is bool!
|
|
|
|
*/
|
|
|
|
static int ub_bd_media_changed(struct gendisk *disk)
|
|
|
|
{
|
2005-05-02 01:05:40 +02:00
|
|
|
struct ub_lun *lun = disk->private_data;
|
2005-04-17 00:20:36 +02:00
|
|
|
|
2005-05-02 01:05:40 +02:00
|
|
|
if (!lun->removable)
|
2005-04-17 00:20:36 +02:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We clean checks always after every command, so this is not
|
|
|
|
* as dangerous as it looks. If the TEST_UNIT_READY fails here,
|
|
|
|
* the device is actually not ready with operator or software
|
|
|
|
* intervention required. One dangerous item might be a drive which
|
|
|
|
* spins itself down, and come the time to write dirty pages, this
|
|
|
|
* will fail, then block layer discards the data. Since we never
|
|
|
|
* spin drives up, such devices simply cannot be used with ub anyway.
|
|
|
|
*/
|
2005-05-02 01:05:40 +02:00
|
|
|
if (ub_sync_tur(lun->udev, lun) != 0) {
|
|
|
|
lun->changed = 1;
|
2005-04-17 00:20:36 +02:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2005-05-02 01:05:40 +02:00
|
|
|
return lun->changed;
|
2005-04-17 00:20:36 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static struct block_device_operations ub_bd_fops = {
|
|
|
|
.owner = THIS_MODULE,
|
|
|
|
.open = ub_bd_open,
|
|
|
|
.release = ub_bd_release,
|
|
|
|
.ioctl = ub_bd_ioctl,
|
|
|
|
.media_changed = ub_bd_media_changed,
|
|
|
|
.revalidate_disk = ub_bd_revalidate,
|
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Common ->done routine for commands executed synchronously.
|
|
|
|
*/
|
|
|
|
static void ub_probe_done(struct ub_dev *sc, struct ub_scsi_cmd *cmd)
|
|
|
|
{
|
|
|
|
struct completion *cop = cmd->back;
|
|
|
|
complete(cop);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Test if the device has a check condition on it, synchronously.
|
|
|
|
*/
|
2005-05-02 01:05:40 +02:00
|
|
|
static int ub_sync_tur(struct ub_dev *sc, struct ub_lun *lun)
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
|
|
|
struct ub_scsi_cmd *cmd;
|
|
|
|
enum { ALLOC_SIZE = sizeof(struct ub_scsi_cmd) };
|
|
|
|
unsigned long flags;
|
|
|
|
struct completion compl;
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
init_completion(&compl);
|
|
|
|
|
|
|
|
rc = -ENOMEM;
|
|
|
|
if ((cmd = kmalloc(ALLOC_SIZE, GFP_KERNEL)) == NULL)
|
|
|
|
goto err_alloc;
|
|
|
|
memset(cmd, 0, ALLOC_SIZE);
|
|
|
|
|
|
|
|
cmd->cdb[0] = TEST_UNIT_READY;
|
|
|
|
cmd->cdb_len = 6;
|
|
|
|
cmd->dir = UB_DIR_NONE;
|
|
|
|
cmd->state = UB_CMDST_INIT;
|
2005-05-02 01:05:40 +02:00
|
|
|
cmd->lun = lun; /* This may be NULL, but that's ok */
|
2005-04-17 00:20:36 +02:00
|
|
|
cmd->done = ub_probe_done;
|
|
|
|
cmd->back = &compl;
|
|
|
|
|
|
|
|
spin_lock_irqsave(&sc->lock, flags);
|
|
|
|
cmd->tag = sc->tagcnt++;
|
|
|
|
|
|
|
|
rc = ub_submit_scsi(sc, cmd);
|
|
|
|
spin_unlock_irqrestore(&sc->lock, flags);
|
|
|
|
|
|
|
|
if (rc != 0) {
|
|
|
|
printk("ub: testing ready: submit error (%d)\n", rc); /* P3 */
|
|
|
|
goto err_submit;
|
|
|
|
}
|
|
|
|
|
|
|
|
wait_for_completion(&compl);
|
|
|
|
|
|
|
|
rc = cmd->error;
|
|
|
|
|
|
|
|
if (rc == -EIO && cmd->key != 0) /* Retries for benh's key */
|
|
|
|
rc = cmd->key;
|
|
|
|
|
|
|
|
err_submit:
|
|
|
|
kfree(cmd);
|
|
|
|
err_alloc:
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Read the SCSI capacity synchronously (for probing).
|
|
|
|
*/
|
2005-05-02 01:05:40 +02:00
|
|
|
static int ub_sync_read_cap(struct ub_dev *sc, struct ub_lun *lun,
|
|
|
|
struct ub_capacity *ret)
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
|
|
|
struct ub_scsi_cmd *cmd;
|
2005-08-15 06:16:03 +02:00
|
|
|
struct scatterlist *sg;
|
2005-04-17 00:20:36 +02:00
|
|
|
char *p;
|
|
|
|
enum { ALLOC_SIZE = sizeof(struct ub_scsi_cmd) + 8 };
|
|
|
|
unsigned long flags;
|
|
|
|
unsigned int bsize, shift;
|
|
|
|
unsigned long nsec;
|
|
|
|
struct completion compl;
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
init_completion(&compl);
|
|
|
|
|
|
|
|
rc = -ENOMEM;
|
|
|
|
if ((cmd = kmalloc(ALLOC_SIZE, GFP_KERNEL)) == NULL)
|
|
|
|
goto err_alloc;
|
|
|
|
memset(cmd, 0, ALLOC_SIZE);
|
|
|
|
p = (char *)cmd + sizeof(struct ub_scsi_cmd);
|
|
|
|
|
|
|
|
cmd->cdb[0] = 0x25;
|
|
|
|
cmd->cdb_len = 10;
|
|
|
|
cmd->dir = UB_DIR_READ;
|
|
|
|
cmd->state = UB_CMDST_INIT;
|
2005-08-15 06:16:03 +02:00
|
|
|
cmd->nsg = 1;
|
|
|
|
sg = &cmd->sgv[0];
|
|
|
|
sg->page = virt_to_page(p);
|
|
|
|
sg->offset = (unsigned int)p & (PAGE_SIZE-1);
|
|
|
|
sg->length = 8;
|
2005-04-17 00:20:36 +02:00
|
|
|
cmd->len = 8;
|
2005-05-02 01:05:40 +02:00
|
|
|
cmd->lun = lun;
|
2005-04-17 00:20:36 +02:00
|
|
|
cmd->done = ub_probe_done;
|
|
|
|
cmd->back = &compl;
|
|
|
|
|
|
|
|
spin_lock_irqsave(&sc->lock, flags);
|
|
|
|
cmd->tag = sc->tagcnt++;
|
|
|
|
|
|
|
|
rc = ub_submit_scsi(sc, cmd);
|
|
|
|
spin_unlock_irqrestore(&sc->lock, flags);
|
|
|
|
|
|
|
|
if (rc != 0) {
|
|
|
|
printk("ub: reading capacity: submit error (%d)\n", rc); /* P3 */
|
|
|
|
goto err_submit;
|
|
|
|
}
|
|
|
|
|
|
|
|
wait_for_completion(&compl);
|
|
|
|
|
|
|
|
if (cmd->error != 0) {
|
|
|
|
printk("ub: reading capacity: error %d\n", cmd->error); /* P3 */
|
|
|
|
rc = -EIO;
|
|
|
|
goto err_read;
|
|
|
|
}
|
|
|
|
if (cmd->act_len != 8) {
|
|
|
|
printk("ub: reading capacity: size %d\n", cmd->act_len); /* P3 */
|
|
|
|
rc = -EIO;
|
|
|
|
goto err_read;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* sd.c special-cases sector size of 0 to mean 512. Needed? Safe? */
|
|
|
|
nsec = be32_to_cpu(*(__be32 *)p) + 1;
|
|
|
|
bsize = be32_to_cpu(*(__be32 *)(p + 4));
|
|
|
|
switch (bsize) {
|
|
|
|
case 512: shift = 0; break;
|
|
|
|
case 1024: shift = 1; break;
|
|
|
|
case 2048: shift = 2; break;
|
|
|
|
case 4096: shift = 3; break;
|
|
|
|
default:
|
|
|
|
printk("ub: Bad sector size %u\n", bsize); /* P3 */
|
|
|
|
rc = -EDOM;
|
|
|
|
goto err_inv_bsize;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret->bsize = bsize;
|
|
|
|
ret->bshift = shift;
|
|
|
|
ret->nsec = nsec << shift;
|
|
|
|
rc = 0;
|
|
|
|
|
|
|
|
err_inv_bsize:
|
|
|
|
err_read:
|
|
|
|
err_submit:
|
|
|
|
kfree(cmd);
|
|
|
|
err_alloc:
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
*/
|
|
|
|
static void ub_probe_urb_complete(struct urb *urb, struct pt_regs *pt)
|
|
|
|
{
|
|
|
|
struct completion *cop = urb->context;
|
|
|
|
complete(cop);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ub_probe_timeout(unsigned long arg)
|
|
|
|
{
|
|
|
|
struct completion *cop = (struct completion *) arg;
|
|
|
|
complete(cop);
|
|
|
|
}
|
|
|
|
|
2005-05-02 01:05:40 +02:00
|
|
|
/*
|
|
|
|
* Get number of LUNs by the way of Bulk GetMaxLUN command.
|
|
|
|
*/
|
|
|
|
static int ub_sync_getmaxlun(struct ub_dev *sc)
|
|
|
|
{
|
|
|
|
int ifnum = sc->intf->cur_altsetting->desc.bInterfaceNumber;
|
|
|
|
unsigned char *p;
|
|
|
|
enum { ALLOC_SIZE = 1 };
|
|
|
|
struct usb_ctrlrequest *cr;
|
|
|
|
struct completion compl;
|
|
|
|
struct timer_list timer;
|
|
|
|
int nluns;
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
init_completion(&compl);
|
|
|
|
|
|
|
|
rc = -ENOMEM;
|
|
|
|
if ((p = kmalloc(ALLOC_SIZE, GFP_KERNEL)) == NULL)
|
|
|
|
goto err_alloc;
|
|
|
|
*p = 55;
|
|
|
|
|
|
|
|
cr = &sc->work_cr;
|
|
|
|
cr->bRequestType = USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE;
|
|
|
|
cr->bRequest = US_BULK_GET_MAX_LUN;
|
|
|
|
cr->wValue = cpu_to_le16(0);
|
|
|
|
cr->wIndex = cpu_to_le16(ifnum);
|
|
|
|
cr->wLength = cpu_to_le16(1);
|
|
|
|
|
|
|
|
usb_fill_control_urb(&sc->work_urb, sc->dev, sc->recv_ctrl_pipe,
|
|
|
|
(unsigned char*) cr, p, 1, ub_probe_urb_complete, &compl);
|
|
|
|
sc->work_urb.transfer_flags = 0;
|
|
|
|
sc->work_urb.actual_length = 0;
|
|
|
|
sc->work_urb.error_count = 0;
|
|
|
|
sc->work_urb.status = 0;
|
|
|
|
|
|
|
|
if ((rc = usb_submit_urb(&sc->work_urb, GFP_KERNEL)) != 0) {
|
|
|
|
if (rc == -EPIPE) {
|
|
|
|
printk("%s: Stall at GetMaxLUN, using 1 LUN\n",
|
|
|
|
sc->name); /* P3 */
|
|
|
|
} else {
|
|
|
|
printk(KERN_WARNING
|
|
|
|
"%s: Unable to submit GetMaxLUN (%d)\n",
|
|
|
|
sc->name, rc);
|
|
|
|
}
|
|
|
|
goto err_submit;
|
|
|
|
}
|
|
|
|
|
|
|
|
init_timer(&timer);
|
|
|
|
timer.function = ub_probe_timeout;
|
|
|
|
timer.data = (unsigned long) &compl;
|
|
|
|
timer.expires = jiffies + UB_CTRL_TIMEOUT;
|
|
|
|
add_timer(&timer);
|
|
|
|
|
|
|
|
wait_for_completion(&compl);
|
|
|
|
|
|
|
|
del_timer_sync(&timer);
|
|
|
|
usb_kill_urb(&sc->work_urb);
|
|
|
|
|
|
|
|
if (sc->work_urb.actual_length != 1) {
|
|
|
|
printk("%s: GetMaxLUN returned %d bytes\n", sc->name,
|
|
|
|
sc->work_urb.actual_length); /* P3 */
|
|
|
|
nluns = 0;
|
|
|
|
} else {
|
|
|
|
if ((nluns = *p) == 55) {
|
|
|
|
nluns = 0;
|
|
|
|
} else {
|
|
|
|
/* GetMaxLUN returns the maximum LUN number */
|
|
|
|
nluns += 1;
|
|
|
|
if (nluns > UB_MAX_LUNS)
|
|
|
|
nluns = UB_MAX_LUNS;
|
|
|
|
}
|
|
|
|
printk("%s: GetMaxLUN returned %d, using %d LUNs\n", sc->name,
|
|
|
|
*p, nluns); /* P3 */
|
|
|
|
}
|
|
|
|
|
|
|
|
kfree(p);
|
|
|
|
return nluns;
|
|
|
|
|
|
|
|
err_submit:
|
|
|
|
kfree(p);
|
|
|
|
err_alloc:
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2005-04-17 00:20:36 +02:00
|
|
|
/*
|
|
|
|
* Clear initial stalls.
|
|
|
|
*/
|
|
|
|
static int ub_probe_clear_stall(struct ub_dev *sc, int stalled_pipe)
|
|
|
|
{
|
|
|
|
int endp;
|
|
|
|
struct usb_ctrlrequest *cr;
|
|
|
|
struct completion compl;
|
|
|
|
struct timer_list timer;
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
init_completion(&compl);
|
|
|
|
|
|
|
|
endp = usb_pipeendpoint(stalled_pipe);
|
|
|
|
if (usb_pipein (stalled_pipe))
|
|
|
|
endp |= USB_DIR_IN;
|
|
|
|
|
|
|
|
cr = &sc->work_cr;
|
|
|
|
cr->bRequestType = USB_RECIP_ENDPOINT;
|
|
|
|
cr->bRequest = USB_REQ_CLEAR_FEATURE;
|
|
|
|
cr->wValue = cpu_to_le16(USB_ENDPOINT_HALT);
|
|
|
|
cr->wIndex = cpu_to_le16(endp);
|
|
|
|
cr->wLength = cpu_to_le16(0);
|
|
|
|
|
|
|
|
usb_fill_control_urb(&sc->work_urb, sc->dev, sc->send_ctrl_pipe,
|
|
|
|
(unsigned char*) cr, NULL, 0, ub_probe_urb_complete, &compl);
|
|
|
|
sc->work_urb.transfer_flags = 0;
|
|
|
|
sc->work_urb.actual_length = 0;
|
|
|
|
sc->work_urb.error_count = 0;
|
|
|
|
sc->work_urb.status = 0;
|
|
|
|
|
|
|
|
if ((rc = usb_submit_urb(&sc->work_urb, GFP_KERNEL)) != 0) {
|
|
|
|
printk(KERN_WARNING
|
|
|
|
"%s: Unable to submit a probe clear (%d)\n", sc->name, rc);
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
init_timer(&timer);
|
|
|
|
timer.function = ub_probe_timeout;
|
|
|
|
timer.data = (unsigned long) &compl;
|
|
|
|
timer.expires = jiffies + UB_CTRL_TIMEOUT;
|
|
|
|
add_timer(&timer);
|
|
|
|
|
|
|
|
wait_for_completion(&compl);
|
|
|
|
|
|
|
|
del_timer_sync(&timer);
|
|
|
|
usb_kill_urb(&sc->work_urb);
|
|
|
|
|
|
|
|
/* reset the endpoint toggle */
|
|
|
|
usb_settoggle(sc->dev, endp, usb_pipeout(sc->last_pipe), 0);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Get the pipe settings.
|
|
|
|
*/
|
|
|
|
static int ub_get_pipes(struct ub_dev *sc, struct usb_device *dev,
|
|
|
|
struct usb_interface *intf)
|
|
|
|
{
|
|
|
|
struct usb_host_interface *altsetting = intf->cur_altsetting;
|
|
|
|
struct usb_endpoint_descriptor *ep_in = NULL;
|
|
|
|
struct usb_endpoint_descriptor *ep_out = NULL;
|
|
|
|
struct usb_endpoint_descriptor *ep;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Find the endpoints we need.
|
|
|
|
* We are expecting a minimum of 2 endpoints - in and out (bulk).
|
|
|
|
* We will ignore any others.
|
|
|
|
*/
|
|
|
|
for (i = 0; i < altsetting->desc.bNumEndpoints; i++) {
|
|
|
|
ep = &altsetting->endpoint[i].desc;
|
|
|
|
|
|
|
|
/* Is it a BULK endpoint? */
|
|
|
|
if ((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
|
|
|
|
== USB_ENDPOINT_XFER_BULK) {
|
|
|
|
/* BULK in or out? */
|
|
|
|
if (ep->bEndpointAddress & USB_DIR_IN)
|
|
|
|
ep_in = ep;
|
|
|
|
else
|
|
|
|
ep_out = ep;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ep_in == NULL || ep_out == NULL) {
|
2005-05-02 01:05:40 +02:00
|
|
|
printk(KERN_NOTICE "%s: failed endpoint check\n",
|
|
|
|
sc->name);
|
2005-04-17 00:20:36 +02:00
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Calculate and store the pipe values */
|
|
|
|
sc->send_ctrl_pipe = usb_sndctrlpipe(dev, 0);
|
|
|
|
sc->recv_ctrl_pipe = usb_rcvctrlpipe(dev, 0);
|
|
|
|
sc->send_bulk_pipe = usb_sndbulkpipe(dev,
|
|
|
|
ep_out->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK);
|
|
|
|
sc->recv_bulk_pipe = usb_rcvbulkpipe(dev,
|
|
|
|
ep_in->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Probing is done in the process context, which allows us to cheat
|
|
|
|
* and not to build a state machine for the discovery.
|
|
|
|
*/
|
|
|
|
static int ub_probe(struct usb_interface *intf,
|
|
|
|
const struct usb_device_id *dev_id)
|
|
|
|
{
|
|
|
|
struct ub_dev *sc;
|
2005-05-02 01:05:40 +02:00
|
|
|
int nluns;
|
2005-04-17 00:20:36 +02:00
|
|
|
int rc;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
rc = -ENOMEM;
|
|
|
|
if ((sc = kmalloc(sizeof(struct ub_dev), GFP_KERNEL)) == NULL)
|
|
|
|
goto err_core;
|
|
|
|
memset(sc, 0, sizeof(struct ub_dev));
|
|
|
|
spin_lock_init(&sc->lock);
|
2005-05-02 01:05:40 +02:00
|
|
|
INIT_LIST_HEAD(&sc->luns);
|
2005-04-17 00:20:36 +02:00
|
|
|
usb_init_urb(&sc->work_urb);
|
|
|
|
tasklet_init(&sc->tasklet, ub_scsi_action, (unsigned long)sc);
|
|
|
|
atomic_set(&sc->poison, 0);
|
|
|
|
|
|
|
|
init_timer(&sc->work_timer);
|
|
|
|
sc->work_timer.data = (unsigned long) sc;
|
|
|
|
sc->work_timer.function = ub_urb_timeout;
|
|
|
|
|
|
|
|
ub_init_completion(&sc->work_done);
|
|
|
|
sc->work_done.done = 1; /* A little yuk, but oh well... */
|
|
|
|
|
|
|
|
sc->dev = interface_to_usbdev(intf);
|
|
|
|
sc->intf = intf;
|
|
|
|
// sc->ifnum = intf->cur_altsetting->desc.bInterfaceNumber;
|
|
|
|
usb_set_intfdata(intf, sc);
|
|
|
|
usb_get_dev(sc->dev);
|
|
|
|
// usb_get_intf(sc->intf); /* Do we need this? */
|
|
|
|
|
2005-05-02 01:05:40 +02:00
|
|
|
snprintf(sc->name, 12, DRV_NAME "(%d.%d)",
|
|
|
|
sc->dev->bus->busnum, sc->dev->devnum);
|
|
|
|
|
2005-04-17 00:20:36 +02:00
|
|
|
/* XXX Verify that we can handle the device (from descriptors) */
|
|
|
|
|
|
|
|
ub_get_pipes(sc, sc->dev, intf);
|
|
|
|
|
|
|
|
if (device_create_file(&sc->intf->dev, &dev_attr_diag) != 0)
|
|
|
|
goto err_diag;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* At this point, all USB initialization is done, do upper layer.
|
|
|
|
* We really hate halfway initialized structures, so from the
|
|
|
|
* invariants perspective, this ub_dev is fully constructed at
|
|
|
|
* this point.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This is needed to clear toggles. It is a problem only if we do
|
|
|
|
* `rmmod ub && modprobe ub` without disconnects, but we like that.
|
|
|
|
*/
|
|
|
|
ub_probe_clear_stall(sc, sc->recv_bulk_pipe);
|
|
|
|
ub_probe_clear_stall(sc, sc->send_bulk_pipe);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The way this is used by the startup code is a little specific.
|
|
|
|
* A SCSI check causes a USB stall. Our common case code sees it
|
|
|
|
* and clears the check, after which the device is ready for use.
|
|
|
|
* But if a check was not present, any command other than
|
|
|
|
* TEST_UNIT_READY ends with a lockup (including REQUEST_SENSE).
|
|
|
|
*
|
|
|
|
* If we neglect to clear the SCSI check, the first real command fails
|
|
|
|
* (which is the capacity readout). We clear that and retry, but why
|
|
|
|
* causing spurious retries for no reason.
|
|
|
|
*
|
|
|
|
* Revalidation may start with its own TEST_UNIT_READY, but that one
|
|
|
|
* has to succeed, so we clear checks with an additional one here.
|
|
|
|
* In any case it's not our business how revaliadation is implemented.
|
|
|
|
*/
|
|
|
|
for (i = 0; i < 3; i++) { /* Retries for benh's key */
|
2005-05-02 01:05:40 +02:00
|
|
|
if ((rc = ub_sync_tur(sc, NULL)) <= 0) break;
|
2005-04-17 00:20:36 +02:00
|
|
|
if (rc != 0x6) break;
|
|
|
|
msleep(10);
|
|
|
|
}
|
|
|
|
|
2005-05-02 01:05:40 +02:00
|
|
|
nluns = 1;
|
|
|
|
for (i = 0; i < 3; i++) {
|
|
|
|
if ((rc = ub_sync_getmaxlun(sc)) < 0) {
|
|
|
|
/*
|
|
|
|
* Some devices (i.e. Iomega Zip100) need this --
|
|
|
|
* apparently the bulk pipes get STALLed when the
|
|
|
|
* GetMaxLUN request is processed.
|
|
|
|
* XXX I have a ZIP-100, verify it does this.
|
|
|
|
*/
|
|
|
|
if (rc == -EPIPE) {
|
|
|
|
ub_probe_clear_stall(sc, sc->recv_bulk_pipe);
|
|
|
|
ub_probe_clear_stall(sc, sc->send_bulk_pipe);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (rc != 0) {
|
|
|
|
nluns = rc;
|
|
|
|
break;
|
|
|
|
}
|
2005-06-06 22:54:59 +02:00
|
|
|
msleep(100);
|
2005-05-02 01:05:40 +02:00
|
|
|
}
|
2005-04-17 00:20:36 +02:00
|
|
|
|
2005-05-02 01:05:40 +02:00
|
|
|
for (i = 0; i < nluns; i++) {
|
|
|
|
ub_probe_lun(sc, i);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
/* device_remove_file(&sc->intf->dev, &dev_attr_diag); */
|
|
|
|
err_diag:
|
|
|
|
usb_set_intfdata(intf, NULL);
|
|
|
|
// usb_put_intf(sc->intf);
|
|
|
|
usb_put_dev(sc->dev);
|
|
|
|
kfree(sc);
|
|
|
|
err_core:
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ub_probe_lun(struct ub_dev *sc, int lnum)
|
|
|
|
{
|
|
|
|
struct ub_lun *lun;
|
|
|
|
request_queue_t *q;
|
|
|
|
struct gendisk *disk;
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
rc = -ENOMEM;
|
|
|
|
if ((lun = kmalloc(sizeof(struct ub_lun), GFP_KERNEL)) == NULL)
|
|
|
|
goto err_alloc;
|
|
|
|
memset(lun, 0, sizeof(struct ub_lun));
|
|
|
|
lun->num = lnum;
|
|
|
|
|
|
|
|
rc = -ENOSR;
|
|
|
|
if ((lun->id = ub_id_get()) == -1)
|
|
|
|
goto err_id;
|
|
|
|
|
|
|
|
lun->udev = sc;
|
|
|
|
list_add(&lun->link, &sc->luns);
|
|
|
|
|
|
|
|
snprintf(lun->name, 16, DRV_NAME "%c(%d.%d.%d)",
|
|
|
|
lun->id + 'a', sc->dev->bus->busnum, sc->dev->devnum, lun->num);
|
|
|
|
|
|
|
|
lun->removable = 1; /* XXX Query this from the device */
|
|
|
|
lun->changed = 1; /* ub_revalidate clears only */
|
|
|
|
lun->first_open = 1;
|
|
|
|
ub_revalidate(sc, lun);
|
2005-04-17 00:20:36 +02:00
|
|
|
|
|
|
|
rc = -ENOMEM;
|
|
|
|
if ((disk = alloc_disk(UB_MINORS_PER_MAJOR)) == NULL)
|
|
|
|
goto err_diskalloc;
|
|
|
|
|
2005-05-02 01:05:40 +02:00
|
|
|
lun->disk = disk;
|
|
|
|
sprintf(disk->disk_name, DRV_NAME "%c", lun->id + 'a');
|
|
|
|
sprintf(disk->devfs_name, DEVFS_NAME "/%c", lun->id + 'a');
|
2005-04-17 00:20:36 +02:00
|
|
|
disk->major = UB_MAJOR;
|
2005-05-02 01:05:40 +02:00
|
|
|
disk->first_minor = lun->id * UB_MINORS_PER_MAJOR;
|
2005-04-17 00:20:36 +02:00
|
|
|
disk->fops = &ub_bd_fops;
|
2005-05-02 01:05:40 +02:00
|
|
|
disk->private_data = lun;
|
|
|
|
disk->driverfs_dev = &sc->intf->dev; /* XXX Many to one ok? */
|
2005-04-17 00:20:36 +02:00
|
|
|
|
|
|
|
rc = -ENOMEM;
|
2005-07-31 07:51:52 +02:00
|
|
|
if ((q = blk_init_queue(ub_request_fn, &sc->lock)) == NULL)
|
2005-04-17 00:20:36 +02:00
|
|
|
goto err_blkqinit;
|
|
|
|
|
|
|
|
disk->queue = q;
|
|
|
|
|
2005-05-02 01:05:40 +02:00
|
|
|
blk_queue_bounce_limit(q, BLK_BOUNCE_HIGH);
|
2005-04-17 00:20:36 +02:00
|
|
|
blk_queue_max_hw_segments(q, UB_MAX_REQ_SG);
|
|
|
|
blk_queue_max_phys_segments(q, UB_MAX_REQ_SG);
|
2005-05-02 01:05:40 +02:00
|
|
|
blk_queue_segment_boundary(q, 0xffffffff); /* Dubious. */
|
2005-04-17 00:20:36 +02:00
|
|
|
blk_queue_max_sectors(q, UB_MAX_SECTORS);
|
2005-05-02 01:05:40 +02:00
|
|
|
blk_queue_hardsect_size(q, lun->capacity.bsize);
|
2005-04-17 00:20:36 +02:00
|
|
|
|
2005-05-02 01:05:40 +02:00
|
|
|
q->queuedata = lun;
|
2005-04-17 00:20:36 +02:00
|
|
|
|
2005-05-02 01:05:40 +02:00
|
|
|
set_capacity(disk, lun->capacity.nsec);
|
|
|
|
if (lun->removable)
|
2005-04-17 00:20:36 +02:00
|
|
|
disk->flags |= GENHD_FL_REMOVABLE;
|
|
|
|
|
|
|
|
add_disk(disk);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
err_blkqinit:
|
|
|
|
put_disk(disk);
|
|
|
|
err_diskalloc:
|
2005-05-02 01:05:40 +02:00
|
|
|
list_del(&lun->link);
|
|
|
|
ub_id_put(lun->id);
|
2005-04-17 00:20:36 +02:00
|
|
|
err_id:
|
2005-05-02 01:05:40 +02:00
|
|
|
kfree(lun);
|
|
|
|
err_alloc:
|
2005-04-17 00:20:36 +02:00
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ub_disconnect(struct usb_interface *intf)
|
|
|
|
{
|
|
|
|
struct ub_dev *sc = usb_get_intfdata(intf);
|
2005-05-02 01:05:40 +02:00
|
|
|
struct list_head *p;
|
|
|
|
struct ub_lun *lun;
|
|
|
|
struct gendisk *disk;
|
2005-04-17 00:20:36 +02:00
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Prevent ub_bd_release from pulling the rug from under us.
|
|
|
|
* XXX This is starting to look like a kref.
|
|
|
|
* XXX Why not to take this ref at probe time?
|
|
|
|
*/
|
|
|
|
spin_lock_irqsave(&ub_lock, flags);
|
|
|
|
sc->openc++;
|
|
|
|
spin_unlock_irqrestore(&ub_lock, flags);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Fence stall clearnings, operations triggered by unlinkings and so on.
|
|
|
|
* We do not attempt to unlink any URBs, because we do not trust the
|
|
|
|
* unlink paths in HC drivers. Also, we get -84 upon disconnect anyway.
|
|
|
|
*/
|
|
|
|
atomic_set(&sc->poison, 1);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Blow away queued commands.
|
|
|
|
*
|
|
|
|
* Actually, this never works, because before we get here
|
|
|
|
* the HCD terminates outstanding URB(s). It causes our
|
|
|
|
* SCSI command queue to advance, commands fail to submit,
|
|
|
|
* and the whole queue drains. So, we just use this code to
|
|
|
|
* print warnings.
|
|
|
|
*/
|
|
|
|
spin_lock_irqsave(&sc->lock, flags);
|
|
|
|
{
|
|
|
|
struct ub_scsi_cmd *cmd;
|
|
|
|
int cnt = 0;
|
|
|
|
while ((cmd = ub_cmdq_pop(sc)) != NULL) {
|
|
|
|
cmd->error = -ENOTCONN;
|
|
|
|
cmd->state = UB_CMDST_DONE;
|
|
|
|
ub_cmdtr_state(sc, cmd);
|
|
|
|
ub_cmdq_pop(sc);
|
|
|
|
(*cmd->done)(sc, cmd);
|
|
|
|
cnt++;
|
|
|
|
}
|
|
|
|
if (cnt != 0) {
|
|
|
|
printk(KERN_WARNING "%s: "
|
|
|
|
"%d was queued after shutdown\n", sc->name, cnt);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
spin_unlock_irqrestore(&sc->lock, flags);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Unregister the upper layer.
|
|
|
|
*/
|
2005-05-02 01:05:40 +02:00
|
|
|
list_for_each (p, &sc->luns) {
|
|
|
|
lun = list_entry(p, struct ub_lun, link);
|
|
|
|
disk = lun->disk;
|
|
|
|
if (disk->flags & GENHD_FL_UP)
|
|
|
|
del_gendisk(disk);
|
|
|
|
/*
|
|
|
|
* I wish I could do:
|
|
|
|
* set_bit(QUEUE_FLAG_DEAD, &q->queue_flags);
|
|
|
|
* As it is, we rely on our internal poisoning and let
|
|
|
|
* the upper levels to spin furiously failing all the I/O.
|
|
|
|
*/
|
|
|
|
}
|
2005-04-17 00:20:36 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Taking a lock on a structure which is about to be freed
|
|
|
|
* is very nonsensual. Here it is largely a way to do a debug freeze,
|
|
|
|
* and a bracket which shows where the nonsensual code segment ends.
|
|
|
|
*
|
|
|
|
* Testing for -EINPROGRESS is always a bug, so we are bending
|
|
|
|
* the rules a little.
|
|
|
|
*/
|
|
|
|
spin_lock_irqsave(&sc->lock, flags);
|
|
|
|
if (sc->work_urb.status == -EINPROGRESS) { /* janitors: ignore */
|
|
|
|
printk(KERN_WARNING "%s: "
|
|
|
|
"URB is active after disconnect\n", sc->name);
|
|
|
|
}
|
|
|
|
spin_unlock_irqrestore(&sc->lock, flags);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* There is virtually no chance that other CPU runs times so long
|
|
|
|
* after ub_urb_complete should have called del_timer, but only if HCD
|
|
|
|
* didn't forget to deliver a callback on unlink.
|
|
|
|
*/
|
|
|
|
del_timer_sync(&sc->work_timer);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* At this point there must be no commands coming from anyone
|
|
|
|
* and no URBs left in transit.
|
|
|
|
*/
|
|
|
|
|
|
|
|
device_remove_file(&sc->intf->dev, &dev_attr_diag);
|
|
|
|
usb_set_intfdata(intf, NULL);
|
|
|
|
// usb_put_intf(sc->intf);
|
|
|
|
sc->intf = NULL;
|
|
|
|
usb_put_dev(sc->dev);
|
|
|
|
sc->dev = NULL;
|
|
|
|
|
|
|
|
ub_put(sc);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct usb_driver ub_driver = {
|
|
|
|
.owner = THIS_MODULE,
|
|
|
|
.name = "ub",
|
|
|
|
.probe = ub_probe,
|
|
|
|
.disconnect = ub_disconnect,
|
|
|
|
.id_table = ub_usb_ids,
|
|
|
|
};
|
|
|
|
|
|
|
|
static int __init ub_init(void)
|
|
|
|
{
|
|
|
|
int rc;
|
|
|
|
|
2005-05-02 01:05:40 +02:00
|
|
|
/* P3 */ printk("ub: sizeof ub_scsi_cmd %zu ub_dev %zu ub_lun %zu\n",
|
|
|
|
sizeof(struct ub_scsi_cmd), sizeof(struct ub_dev), sizeof(struct ub_lun));
|
2005-04-17 00:20:36 +02:00
|
|
|
|
|
|
|
if ((rc = register_blkdev(UB_MAJOR, DRV_NAME)) != 0)
|
|
|
|
goto err_regblkdev;
|
|
|
|
devfs_mk_dir(DEVFS_NAME);
|
|
|
|
|
|
|
|
if ((rc = usb_register(&ub_driver)) != 0)
|
|
|
|
goto err_register;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
err_register:
|
|
|
|
devfs_remove(DEVFS_NAME);
|
|
|
|
unregister_blkdev(UB_MAJOR, DRV_NAME);
|
|
|
|
err_regblkdev:
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void __exit ub_exit(void)
|
|
|
|
{
|
|
|
|
usb_deregister(&ub_driver);
|
|
|
|
|
|
|
|
devfs_remove(DEVFS_NAME);
|
|
|
|
unregister_blkdev(UB_MAJOR, DRV_NAME);
|
|
|
|
}
|
|
|
|
|
|
|
|
module_init(ub_init);
|
|
|
|
module_exit(ub_exit);
|
|
|
|
|
|
|
|
MODULE_LICENSE("GPL");
|