📄 scsi_target.c
字号:
/* @(#)scsi_target.c 1.10 *//* * SCSI Target Mode "stub" driver for Linux. * * Copyright (c) 2000, 2001 by Matthew Jacob * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions, and the following disclaimer, * without modification, immediately at the beginning of the file. * 2. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * Alternatively, this software may be distributed under the terms of the * the GNU Public License ("GPL"). * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * Matthew Jacob * Feral Software * PMB #825 * 5214-F Diamond Hts Blvd * San Francisco, CA, 94131 * mjacob@feral.com */#ifdef MODULE#define EXPORT_SYMTAB#endif#include <linux/version.h>#ifndef KERNEL_VERSION#define KERNEL_VERSION(v,p,s) (((v)<<16)+(p<<8)+s)#endif#include <linux/autoconf.h>#ifdef CONFIG_SMP#define __SMP__ 1#endif#define _KVC LINUX_VERSION_CODE#if _KVC <= KERNEL_VERSION(2,2,0)#error "Linux 2.0 and 2.1 kernels are not supported anymore"#endif#if _KVC >= KERNEL_VERSION(2,3,0) && _KVC < KERNEL_VERSION(2,4,0)#error "Linux 2.3 kernels are not supported"#endif#include <linux/module.h>#include <linux/config.h>#include <linux/init.h>#include <linux/types.h>#include <linux/blk.h>#include <linux/blkdev.h>#include <linux/delay.h>#include <linux/ioport.h>#include <linux/mm.h>#include <linux/sched.h>#include <linux/stat.h>#include <linux/pci.h>#include <linux/malloc.h>#include <asm/dma.h>#include <asm/io.h>#include <asm/irq.h>#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)#include <linux/smp.h>#include <linux/spinlock.h>#else#include <asm/spinlock.h>#endif#include <asm/scatterlist.h>#include <asm/system.h>#include <linux/miscdevice.h>#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) && defined(CONFIG_PROC_FS)#include <linux/proc_fs.h>#endif#ifdef min#undef min#endif#define min(a,b) (((a)<(b))?(a):(b))#ifndef roundup#define roundup(x, y) ((((x)+((y)-1))/(y))*(y))#endif#include "scsi_target.h"#include "isp_tpublic.h"#include "linux/smp_lock.h"#define DEFAULT_DEVICE_TYPE 3 /* PROCESSOR */#define MAX_BUS 8#define MAX_LUN 64#define N_SENSE_BUFS 64typedef struct sdata { struct sdata *next; u_int8_t sdata[QLTM_SENSELEN];} sdata_t;struct bus;typedef struct initiator { struct initiator * ini_next; struct bus * ini_bus; /* backpointer to containing bus */ struct sdata * ini_sdata; /* pending sense data list */ u_int64_t ini_iid; /* initiator identifier */} ini_t;#define HASH_WIDTH 16#define INI_HASH_LISTP(busp, ini_id) busp->list[ini_id & (HASH_WIDTH - 1)]typedef struct { hba_register_t h; /* must be first */ ini_t *list[HASH_WIDTH]; /* hash list of known initiators */ u_int8_t luns[2][MAX_LUN];} bus_t;#define SDprintk if (scsi_tdebug) printk#define SDprintk2 if (scsi_tdebug > 1) printkstatic int scsi_tdebug = 0;static int scsi_target_open(struct inode *, struct file *);static int scsi_target_close(struct inode *, struct file *);static intscsi_target_ioctl(struct inode *, struct file *, unsigned int, unsigned long);static void scsi_target_handler(qact_e, void *);static inline bus_t *bus_from_tmd(tmd_cmd_t *, int);static inline bus_t *bus_from_name(char *);static inline ini_t *ini_from_tmd(bus_t *, tmd_cmd_t *);static void add_sdata(ini_t *, void *);static void rem_sdata(ini_t *);static void scsi_target_start_cmd(tmd_cmd_t *, bus_t *);static int scsi_target_thread(void *);static int scsi_target_endis(char *, int, int, int, int);/* * Local Declarations */static u_int8_t inqdata[36] = { DEFAULT_DEVICE_TYPE, 0x0, 0x2, 0x2, 32, 0, 0, 0x32, 'L', 'I', 'N', 'U', 'X', ' ', ' ', ' ', 'S', 'C', 'S', 'I', ' ', 'B', 'L', 'A', 'C', 'K', 'H', 'O', 'L', 'E', ' ', ' ', '0', '0', '0', '1'};static u_int8_t illfld[QLTM_SENSELEN] = { 0xf0, 0, 0x5, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0x24};static u_int8_t nolun[QLTM_SENSELEN] = { 0xf0, 0, 0x5, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0x25};#if 0static u_int8_t notrdy[QLTM_SENSELEN] = { 0xf0, 0, 0x2, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0x04};#endifstatic u_int8_t ifailure[QLTM_SENSELEN] = { 0xf0, 0, 0x4, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0x44};static u_int8_t ua[QLTM_SENSELEN] = { 0xf0, 0, 0x0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0x29};static u_int8_t nosense[QLTM_SENSELEN] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};static bus_t busses[MAX_BUS];static sdata_t *sdp;DECLARE_MUTEX_LOCKED(scsi_thread_sleep_semaphore);DECLARE_MUTEX_LOCKED(scsi_thread_entry_exit_semaphore);static tmd_cmd_t *p_front = NULL, *p_last = NULL;static spinlock_t scsi_target_lock = SPIN_LOCK_UNLOCKED;static int scsi_target_thread_exit = 0;static int scsi_target_thread_notified = 0;#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) && defined(CONFIG_PROC_FS)static struct proc_dir_entry *st = NULL;extern struct proc_dir_entry *proc_scsi;static int stp(char *, char **, off_t, int);#endifstatic struct file_operations scsi_target_fops = {ioctl: scsi_target_ioctl,open: scsi_target_open, /* open */#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)release: scsi_target_close /* close */#elserelease: (void (*)(struct inode *, struct file *)) scsi_target_close#endif};static struct miscdevice scsi_target_dev = { MISC_DYNAMIC_MINOR, "scsi_target", &scsi_target_fops};static intscsi_target_open(struct inode * inode, struct file * file){ return (0);}static intscsi_target_close(struct inode * inode, struct file * file){ return (0);}static intscsi_target_ioctl(struct inode *ip, struct file *fp, unsigned int cmd, unsigned long arg){ sc_enable_t *sc; switch(cmd) { case SC_ENABLE_LUN: case SC_DISABLE_LUN: sc = (sc_enable_t *) arg; SDprintk("scsi_target_ioctl: %sable %s, chan %d, target %d, lun %d\n", (cmd == SC_ENABLE_LUN)? "en" : "dis", sc->hba_name_unit, sc->channel, sc->target, sc->lun); return (scsi_target_endis(sc->hba_name_unit, sc->channel, sc->target, sc->lun, (cmd == SC_ENABLE_LUN)? 1 : 0)); default: break; } return (-EINVAL);}#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) && defined(CONFIG_PROC_FS)static intstp(char *buf, char **start, off_t offset, int length){ int len; len = sprintf(buf, "%d\n", scsi_target_dev.minor); *start = buf + offset; len -= offset; if (len > length) len = length; if (len < 0) len = 0; return (len);}#endifstatic intscsi_target_init(void){ int r = misc_register(&scsi_target_dev); if (r == 0) { printk("scsi_target: allocated misc minor number %d (0x%x)\n", scsi_target_dev.minor, scsi_target_dev.minor); spin_lock_init(&scsi_target_lock); kernel_thread(scsi_target_thread, NULL, 0); down(&scsi_thread_entry_exit_semaphore);#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) && defined(CONFIG_PROC_FS) st = create_proc_info_entry("scsi/scsi_target_minor", 0, 0, stp);#endif } else { printk("scsi_target: unable to register with misc driver (%d)\n", r); } return (r);}static inline bus_t *bus_from_tmd(tmd_cmd_t *tmd, int doset){ bus_t *bp; if (tmd->cd_private) return (tmd->cd_private); for (bp = busses; bp < &busses[MAX_BUS]; bp++) { if (bp->h.r_action == NULL) continue; if (bp->h.r_identity == tmd->cd_hba) { if (doset) tmd->cd_private = bp; return (bp); } } return (NULL);}static inline bus_t *bus_from_name(char *name){ bus_t *bp; for (bp = busses; bp < &busses[MAX_BUS]; bp++) { char localbuf[32]; if (bp->h.r_action == NULL) continue; sprintf(localbuf, "%s%d", bp->h.r_name, bp->h.r_inst); if (strncmp(name, localbuf, 32) == 0) { return (bp); } } return (NULL);}static inline ini_t *ini_from_tmd(bus_t *bp, tmd_cmd_t *tmd){ ini_t *ptr = INI_HASH_LISTP(bp, tmd->cd_iid); if (ptr) { do { if (ptr->ini_iid == tmd->cd_iid) return (ptr); } while ((ptr = ptr->ini_next) != NULL); } return (ptr);}/* * Make an initiator structure */static intmake_ini(bus_t *bp, u_int64_t iid){ ini_t *nptr, **ptrlptr = &INI_HASH_LISTP(bp, iid); nptr = kmalloc(sizeof (ini_t), GFP_KERNEL); if (nptr == NULL) { printk("no mem to create initiator structure\n"); return (-ENOMEM); } memset(nptr, 0, sizeof (ini_t)); nptr->ini_iid = iid; nptr->ini_bus = (struct bus *) bp; nptr->ini_next = *ptrlptr; /* * Start off with a Unit Attention condition. */ add_sdata(nptr, ua); *ptrlptr = nptr; return (0);}/* * Add this sense data from the list of * sense data structures for this initiator. * We always add to the front of the list. */static voidadd_sdata(ini_t *ini, void *sd){ sdata_t *t = sdp; if (t == NULL) { printk("outta sense data structures\n"); t = kmalloc(sizeof (sdata_t), GFP_KERNEL); if (t == NULL) { printk("REALLY outta sense data structures\n"); return; } t->next = NULL; } memcpy(t->sdata, sd, sizeof (t->sdata)); t->next = ini->ini_sdata; ini->ini_sdata = t;}/* * Remove one sense data item from the list of * sense data structures for this initiator. */static voidrem_sdata(ini_t *ini){ sdata_t *t = ini->ini_sdata; if (t) { ini->ini_sdata = t->next; t->next = sdp; sdp = t; }}/* XXXX: NOT READY YET FOR NON-FC DEVICES - CONTINGENT ALLEGIANCE HANDLING */#ifndef INQUIRY#define INQUIRY 0x12#endif#ifndef REQUEST_SENSE#define REQUEST_SENSE 0x3#endif#ifndef TEST_UNIT_READY#define TEST_UNIT_READY 0#endif#define _SGS0 roundup(sizeof (struct scatterlist), sizeof (void *))#define SGS_SIZE(x) (roundup((x), sizeof (void *)) + _SGS0)#define SGS_PAYLOAD(x, ptr) (ptr) ((char *)x + _SGS0)static voidscsi_target_start_cmd(tmd_cmd_t *tmd, bus_t *bp){ unsigned long flags; ini_t *ini; spin_lock_irqsave(&scsi_target_lock, flags); ini = ini_from_tmd(bp, tmd); tmd->cd_hflags = 0; tmd->cd_scsi_status = SCSI_GOOD; tmd->cd_data = NULL; tmd->cd_resid = tmd->cd_totlen; if (ini == NULL) { if (make_ini(bp, tmd->cd_iid)) { tmd->cd_xfrlen = 0; tmd->cd_scsi_status = SCSI_BUSY; tmd->cd_hflags |= CDFH_STSVALID; spin_unlock_irqrestore(&scsi_target_lock, flags); (*bp->h.r_action)(QIN_TMD_CONT, tmd); return; } ini = ini_from_tmd(bp, tmd); } spin_unlock_irqrestore(&scsi_target_lock, flags); /* * Commands get lumped into 4 rough groups: * * + Commands which don't ever really return CHECK CONDITIONS and * always work. These are typically INQUIRY (future: Reserve/Release) * * + Commands that we accept, but also report CHECK CONDITIONS against if * we have pending contingent allegiance (e..g, TEST UNIT READY). * * + Commands that retrieve Sense Data (REQUEST SENSE) * * + All others (which we bounce with either ILLEGAL COMMAND or BAD LUN). */ if (tmd->cd_cdb[0] == INQUIRY) { if (tmd->cd_totlen == 0) tmd->cd_totlen = tmd->cd_cdb[4]; if (tmd->cd_totlen == 0) { tmd->cd_hflags |= CDFH_STSVALID; } else if ((tmd->cd_cdb[1] & 0x1f) == 0 && tmd->cd_cdb[2] == 0) { struct scatterlist *dp = NULL; dp = (struct scatterlist *)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -