sg.c

来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 2,278 行 · 第 1/5 页

C
2,278
字号
/* *  History: *  Started: Aug 9 by Lawrence Foard (entropy@world.std.com), *           to allow user process control of SCSI devices. *  Development Sponsored by Killy Corp. NY NY * * Original driver (sg.c): *        Copyright (C) 1992 Lawrence Foard * Version 2 and 3 extensions to driver: *        Copyright (C) 1998 - 2004 Douglas Gilbert * *  Modified  19-JAN-1998  Richard Gooch <rgooch@atnf.csiro.au>  Devfs support * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * */static int sg_version_num = 30531;	/* 2 digits for each component */#define SG_VERSION_STR "3.5.31"/* *  D. P. Gilbert (dgilbert@interlog.com, dougg@triode.net.au), notes: *      - scsi logging is available via SCSI_LOG_TIMEOUT macros. First *        the kernel/module needs to be built with CONFIG_SCSI_LOGGING *        (otherwise the macros compile to empty statements). * */#include <linux/config.h>#include <linux/module.h>#include <linux/fs.h>#include <linux/kernel.h>#include <linux/sched.h>#include <linux/string.h>#include <linux/mm.h>#include <linux/errno.h>#include <linux/mtio.h>#include <linux/ioctl.h>#include <linux/fcntl.h>#include <linux/init.h>#include <linux/poll.h>#include <linux/smp_lock.h>#include <linux/moduleparam.h>#include <linux/devfs_fs_kernel.h>#include <linux/cdev.h>#include <linux/seq_file.h>#include <linux/blkdev.h>#include <linux/delay.h>#include "scsi.h"#include <scsi/scsi_host.h>#include <scsi/scsi_driver.h>#include <scsi/scsi_ioctl.h>#include <scsi/sg.h>#include "scsi_logging.h"#ifdef CONFIG_SCSI_PROC_FS#include <linux/proc_fs.h>static char *sg_version_date = "20040516";static int sg_proc_init(void);static void sg_proc_cleanup(void);#endif#ifndef LINUX_VERSION_CODE#include <linux/version.h>#endif				/* LINUX_VERSION_CODE */#define SG_ALLOW_DIO_DEF 0#define SG_ALLOW_DIO_CODE /* compile out by commenting this define */#define SG_MAX_DEVS 32768/* * Suppose you want to calculate the formula muldiv(x,m,d)=int(x * m / d) * Then when using 32 bit integers x * m may overflow during the calculation. * Replacing muldiv(x) by muldiv(x)=((x % d) * m) / d + int(x / d) * m * calculates the same, but prevents the overflow when both m and d * are "small" numbers (like HZ and USER_HZ). * Of course an overflow is inavoidable if the result of muldiv doesn't fit * in 32 bits. */#define MULDIV(X,MUL,DIV) ((((X % DIV) * MUL) / DIV) + ((X / DIV) * MUL))#define SG_DEFAULT_TIMEOUT MULDIV(SG_DEFAULT_TIMEOUT_USER, HZ, USER_HZ)int sg_big_buff = SG_DEF_RESERVED_SIZE;/* N.B. This variable is readable and writeable via   /proc/scsi/sg/def_reserved_size . Each time sg_open() is called a buffer   of this size (or less if there is not enough memory) will be reserved   for use by this file descriptor. [Deprecated usage: this variable is also   readable via /proc/sys/kernel/sg-big-buff if the sg driver is built into   the kernel (i.e. it is not a module).] */static int def_reserved_size = -1;	/* picks up init parameter */static int sg_allow_dio = SG_ALLOW_DIO_DEF;#define SG_SECTOR_SZ 512#define SG_SECTOR_MSK (SG_SECTOR_SZ - 1)#define SG_DEV_ARR_LUMP 32	/* amount to over allocate sg_dev_arr by */static int sg_add(struct class_device *);static void sg_remove(struct class_device *);static Scsi_Request *dummy_cmdp;	/* only used for sizeof */static rwlock_t sg_dev_arr_lock = RW_LOCK_UNLOCKED;	/* Also used to lock							   file descriptor list for device */static struct class_interface sg_interface = {	.add		= sg_add,	.remove		= sg_remove,};typedef struct sg_scatter_hold { /* holding area for scsi scatter gather info */	unsigned short k_use_sg; /* Count of kernel scatter-gather pieces */	unsigned short sglist_len; /* size of malloc'd scatter-gather list ++ */	unsigned bufflen;	/* Size of (aggregate) data buffer */	unsigned b_malloc_len;	/* actual len malloc'ed in buffer */	void *buffer;		/* Data buffer or scatter list (k_use_sg>0) */	char dio_in_use;	/* 0->indirect IO (or mmap), 1->dio */	unsigned char cmd_opcode; /* first byte of command */} Sg_scatter_hold;struct sg_device;		/* forward declarations */struct sg_fd;typedef struct sg_request {	/* SG_MAX_QUEUE requests outstanding per file */	Scsi_Request *my_cmdp;	/* != 0  when request with lower levels */	struct sg_request *nextrp;	/* NULL -> tail request (slist) */	struct sg_fd *parentfp;	/* NULL -> not in use */	Sg_scatter_hold data;	/* hold buffer, perhaps scatter list */	sg_io_hdr_t header;	/* scsi command+info, see <scsi/sg.h> */	unsigned char sense_b[sizeof (dummy_cmdp->sr_sense_buffer)];	char res_used;		/* 1 -> using reserve buffer, 0 -> not ... */	char orphan;		/* 1 -> drop on sight, 0 -> normal */	char sg_io_owned;	/* 1 -> packet belongs to SG_IO */	volatile char done;	/* 0->before bh, 1->before read, 2->read */} Sg_request;typedef struct sg_fd {		/* holds the state of a file descriptor */	struct sg_fd *nextfp;	/* NULL when last opened fd on this device */	struct sg_device *parentdp;	/* owning device */	wait_queue_head_t read_wait;	/* queue read until command done */	rwlock_t rq_list_lock;	/* protect access to list in req_arr */	int timeout;		/* defaults to SG_DEFAULT_TIMEOUT      */	int timeout_user;	/* defaults to SG_DEFAULT_TIMEOUT_USER */	Sg_scatter_hold reserve;	/* buffer held for this file descriptor */	unsigned save_scat_len;	/* original length of trunc. scat. element */	Sg_request *headrp;	/* head of request slist, NULL->empty */	struct fasync_struct *async_qp;	/* used by asynchronous notification */	Sg_request req_arr[SG_MAX_QUEUE];	/* used as singly-linked list */	char low_dma;		/* as in parent but possibly overridden to 1 */	char force_packid;	/* 1 -> pack_id input to read(), 0 -> ignored */	volatile char closed;	/* 1 -> fd closed but request(s) outstanding */	char cmd_q;		/* 1 -> allow command queuing, 0 -> don't */	char next_cmd_len;	/* 0 -> automatic (def), >0 -> use on next write() */	char keep_orphan;	/* 0 -> drop orphan (def), 1 -> keep for read() */	char mmap_called;	/* 0 -> mmap() never called on this fd */} Sg_fd;typedef struct sg_device { /* holds the state of each scsi generic device */	struct scsi_device *device;	wait_queue_head_t o_excl_wait;	/* queue open() when O_EXCL in use */	int sg_tablesize;	/* adapter's max scatter-gather table size */	Sg_fd *headfp;		/* first open fd belonging to this device */	volatile char detached;	/* 0->attached, 1->detached pending removal */	volatile char exclude;	/* opened for exclusive access */	char sgdebug;		/* 0->off, 1->sense, 9->dump dev, 10-> all devs */	struct gendisk *disk;	struct cdev * cdev;	/* char_dev [sysfs: /sys/cdev/major/sg<n>] */} Sg_device;static int sg_fasync(int fd, struct file *filp, int mode);static void sg_cmd_done(Scsi_Cmnd * SCpnt);	/* tasklet or soft irq callback */static int sg_start_req(Sg_request * srp);static void sg_finish_rem_req(Sg_request * srp);static int sg_build_indirect(Sg_scatter_hold * schp, Sg_fd * sfp, int buff_size);static int sg_build_sgat(Sg_scatter_hold * schp, const Sg_fd * sfp,			 int tablesize);static ssize_t sg_new_read(Sg_fd * sfp, char __user *buf, size_t count,			   Sg_request * srp);static ssize_t sg_new_write(Sg_fd * sfp, const char __user *buf, size_t count,			    int blocking, int read_only, Sg_request ** o_srp);static int sg_common_write(Sg_fd * sfp, Sg_request * srp,			   unsigned char *cmnd, int timeout, int blocking);static int sg_u_iovec(sg_io_hdr_t * hp, int sg_num, int ind,		      int wr_xf, int *countp, unsigned char __user **up);static int sg_write_xfer(Sg_request * srp);static int sg_read_xfer(Sg_request * srp);static int sg_read_oxfer(Sg_request * srp, char __user *outp, int num_read_xfer);static void sg_remove_scat(Sg_scatter_hold * schp);static void sg_build_reserve(Sg_fd * sfp, int req_size);static void sg_link_reserve(Sg_fd * sfp, Sg_request * srp, int size);static void sg_unlink_reserve(Sg_fd * sfp, Sg_request * srp);static char *sg_page_malloc(int rqSz, int lowDma, int *retSzp);static void sg_page_free(char *buff, int size);static Sg_fd *sg_add_sfp(Sg_device * sdp, int dev);static int sg_remove_sfp(Sg_device * sdp, Sg_fd * sfp);static void __sg_remove_sfp(Sg_device * sdp, Sg_fd * sfp);static Sg_request *sg_get_rq_mark(Sg_fd * sfp, int pack_id);static Sg_request *sg_add_request(Sg_fd * sfp);static int sg_remove_request(Sg_fd * sfp, Sg_request * srp);static int sg_res_in_use(Sg_fd * sfp);static int sg_allow_access(unsigned char opcode, char dev_type);static int sg_build_direct(Sg_request * srp, Sg_fd * sfp, int dxfer_len);static Sg_device *sg_get_dev(int dev);static inline unsigned char *sg_scatg2virt(const struct scatterlist *sclp);#ifdef CONFIG_SCSI_PROC_FSstatic int sg_last_dev(void);#endifstatic Sg_device **sg_dev_arr = NULL;static int sg_dev_max;static int sg_nr_dev;#define SZ_SG_HEADER sizeof(struct sg_header)#define SZ_SG_IO_HDR sizeof(sg_io_hdr_t)#define SZ_SG_IOVEC sizeof(sg_iovec_t)#define SZ_SG_REQ_INFO sizeof(sg_req_info_t)static intsg_open(struct inode *inode, struct file *filp){	int dev = iminor(inode);	int flags = filp->f_flags;	Sg_device *sdp;	Sg_fd *sfp;	int res;	int retval;	nonseekable_open(inode, filp);	SCSI_LOG_TIMEOUT(3, printk("sg_open: dev=%d, flags=0x%x\n", dev, flags));	sdp = sg_get_dev(dev);	if ((!sdp) || (!sdp->device))		return -ENXIO;	if (sdp->detached)		return -ENODEV;	/* This driver's module count bumped by fops_get in <linux/fs.h> */	/* Prevent the device driver from vanishing while we sleep */	retval = scsi_device_get(sdp->device);	if (retval)		return retval;	if (!((flags & O_NONBLOCK) ||	      scsi_block_when_processing_errors(sdp->device))) {		retval = -ENXIO;		/* we are in error recovery for this device */		goto error_out;	}	if (flags & O_EXCL) {		if (O_RDONLY == (flags & O_ACCMODE)) {			retval = -EPERM; /* Can't lock it with read only access */			goto error_out;		}		if (sdp->headfp && (flags & O_NONBLOCK)) {			retval = -EBUSY;			goto error_out;		}		res = 0;		__wait_event_interruptible(sdp->o_excl_wait,			((sdp->headfp || sdp->exclude) ? 0 : (sdp->exclude = 1)), res);		if (res) {			retval = res;	/* -ERESTARTSYS because signal hit process */			goto error_out;		}	} else if (sdp->exclude) {	/* some other fd has an exclusive lock on dev */		if (flags & O_NONBLOCK) {			retval = -EBUSY;			goto error_out;		}		res = 0;		__wait_event_interruptible(sdp->o_excl_wait, (!sdp->exclude),					   res);		if (res) {			retval = res;	/* -ERESTARTSYS because signal hit process */			goto error_out;		}	}	if (sdp->detached) {		retval = -ENODEV;		goto error_out;	}	if (!sdp->headfp) {	/* no existing opens on this device */		sdp->sgdebug = 0;		sdp->sg_tablesize = sdp->device->host->sg_tablesize;	}	if ((sfp = sg_add_sfp(sdp, dev)))		filp->private_data = sfp;	else {		if (flags & O_EXCL)			sdp->exclude = 0;	/* undo if error */		retval = -ENOMEM;		goto error_out;	}	return 0;      error_out:	scsi_device_put(sdp->device);	return retval;}/* Following function was formerly called 'sg_close' */static intsg_release(struct inode *inode, struct file *filp){	Sg_device *sdp;	Sg_fd *sfp;	if ((!(sfp = (Sg_fd *) filp->private_data)) || (!(sdp = sfp->parentdp)))		return -ENXIO;	SCSI_LOG_TIMEOUT(3, printk("sg_release: %s\n", sdp->disk->disk_name));	sg_fasync(-1, filp, 0);	/* remove filp from async notification list */	if (0 == sg_remove_sfp(sdp, sfp)) {	/* Returns 1 when sdp gone */		if (!sdp->detached) {			scsi_device_put(sdp->device);		}		sdp->exclude = 0;		wake_up_interruptible(&sdp->o_excl_wait);	}	return 0;}static ssize_tsg_read(struct file *filp, char __user *buf, size_t count, loff_t * ppos){	int k, res;	Sg_device *sdp;	Sg_fd *sfp;	Sg_request *srp;	int req_pack_id = -1;	struct sg_header old_hdr;	sg_io_hdr_t new_hdr;	sg_io_hdr_t *hp;	if ((!(sfp = (Sg_fd *) filp->private_data)) || (!(sdp = sfp->parentdp)))		return -ENXIO;	SCSI_LOG_TIMEOUT(3, printk("sg_read: %s, count=%d\n",				   sdp->disk->disk_name, (int) count));	if ((k = verify_area(VERIFY_WRITE, buf, count)))		return k;	if (sfp->force_packid && (count >= SZ_SG_HEADER)) {		if (__copy_from_user(&old_hdr, buf, SZ_SG_HEADER))			return -EFAULT;		if (old_hdr.reply_len < 0) {			if (count >= SZ_SG_IO_HDR) {				if (__copy_from_user				    (&new_hdr, buf, SZ_SG_IO_HDR))					return -EFAULT;				req_pack_id = new_hdr.pack_id;			}		} else			req_pack_id = old_hdr.pack_id;	}	srp = sg_get_rq_mark(sfp, req_pack_id);	if (!srp) {		/* now wait on packet to arrive */		if (sdp->detached)			return -ENODEV;		if (filp->f_flags & O_NONBLOCK)			return -EAGAIN;		while (1) {			res = 0;	/* following is a macro that beats race condition */			__wait_event_interruptible(sfp->read_wait,				(sdp->detached || (srp = sg_get_rq_mark(sfp, req_pack_id))), 						   res);			if (sdp->detached)				return -ENODEV;			if (0 == res)				break;			return res;	/* -ERESTARTSYS because signal hit process */		}	}	if (srp->header.interface_id != '\0')		return sg_new_read(sfp, buf, count, srp);	hp = &srp->header;	memset(&old_hdr, 0, SZ_SG_HEADER);	old_hdr.reply_len = (int) hp->timeout;	old_hdr.pack_len = old_hdr.reply_len; /* very old, strange behaviour */	old_hdr.pack_id = hp->pack_id;	old_hdr.twelve_byte =	    ((srp->data.cmd_opcode >= 0xc0) && (12 == hp->cmd_len)) ? 1 : 0;	old_hdr.target_status = hp->masked_status;	old_hdr.host_status = hp->host_status;	old_hdr.driver_status = hp->driver_status;	if ((CHECK_CONDITION & hp->masked_status) ||	    (DRIVER_SENSE & hp->driver_status))		memcpy(old_hdr.sense_buffer, srp->sense_b,		       sizeof (old_hdr.sense_buffer));	switch (hp->host_status) {	/* This setup of 'result' is for backward compatibility and is best	   ignored by the user who should use target, host + driver status */	case DID_OK:	case DID_PASSTHROUGH:	case DID_SOFT_ERROR:		old_hdr.result = 0;		break;	case DID_NO_CONNECT:	case DID_BUS_BUSY:	case DID_TIME_OUT:		old_hdr.result = EBUSY;		break;	case DID_BAD_TARGET:	case DID_ABORT:	case DID_PARITY:	case DID_RESET:	case DID_BAD_INTR:		old_hdr.result = EIO;		break;	case DID_ERROR:		old_hdr.result = (srp->sense_b[0] == 0 && 				  hp->masked_status == GOOD) ? 0 : EIO;		break;	default:		old_hdr.result = EIO;		break;	}	/* Now copy the result back to the user buffer.  */	if (count >= SZ_SG_HEADER) {		if (__copy_to_user(buf, &old_hdr, SZ_SG_HEADER))			return -EFAULT;		buf += SZ_SG_HEADER;		if (count > old_hdr.reply_len)			count = old_hdr.reply_len;		if (count > SZ_SG_HEADER) {			if ((res =			     sg_read_oxfer(srp, buf, count - SZ_SG_HEADER)))				return -EFAULT;		}	} else		count = (old_hdr.result == 0) ? 0 : -EIO;	sg_finish_rem_req(srp);	return count;}static ssize_tsg_new_read(Sg_fd * sfp, char __user *buf, size_t count, Sg_request * srp){	sg_io_hdr_t *hp = &srp->header;	int err = 0;	int len;	if (count < SZ_SG_IO_HDR) {		err = -EINVAL;		goto err_out;	}	hp->sb_len_wr = 0;	if ((hp->mx_sb_len > 0) && hp->sbp) {		if ((CHECK_CONDITION & hp->masked_status) ||

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?