/*
* SCSI Media Changer device driver for Linux 2.6
*
* (c) 1996-2003 Gerd Knorr <kraxel@bytesex.org>
*
*/
#define VERSION "0.25"
#include <linux/config.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/mm.h>
#include <linux/major.h>
#include <linux/string.h>
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/blkdev.h>
#include <linux/completion.h>
#include <linux/compat.h>
#include <linux/chio.h> /* here are all the ioctls */
#include <linux/mutex.h>
#include <scsi/scsi.h>
#include <scsi/scsi_cmnd.h>
#include <scsi/scsi_driver.h>
#include <scsi/scsi_ioctl.h>
#include <scsi/scsi_host.h>
#include <scsi/scsi_device.h>
#include <scsi/scsi_eh.h>
#include <scsi/scsi_dbg.h>
#define CH_DT_MAX 16
#define CH_TYPES 8
MODULE_DESCRIPTION("device driver for scsi media changer devices");
MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org>");
MODULE_LICENSE("GPL");
MODULE_ALIAS_CHARDEV_MAJOR(SCSI_CHANGER_MAJOR);
static int init = 1;
module_param(init, int, 0444);
MODULE_PARM_DESC(init, \
"initialize element status on driver load (default: on)");
static int timeout_move = 300;
module_param(timeout_move, int, 0644);
MODULE_PARM_DESC(timeout_move,"timeout for move commands "
"(default: 300 seconds)");
static int timeout_init = 3600;
module_param(timeout_init, int, 0644);
MODULE_PARM_DESC(timeout_init,"timeout for INITIALIZE ELEMENT STATUS "
"(default: 3600 seconds)");
static int verbose = 1;
module_param(verbose, int, 0644);
MODULE_PARM_DESC(verbose,"be verbose (default: on)");
static int debug = 0;
module_param(debug, int, 0644);
MODULE_PARM_DESC(debug,"enable/disable debug messages, also prints more "
"detailed sense codes on scsi errors (default: off)");
static int dt_id[CH_DT_MAX] = { [ 0 ... (CH_DT_MAX-1) ] = -1 };
static int dt_lun[CH_DT_MAX];
module_param_array(dt_id, int, NULL, 0444);
module_param_array(dt_lun, int, NULL, 0444);
/* tell the driver about vendor-specific slots */
static int vendor_firsts[CH_TYPES-4];
static int vendor_counts[CH_TYPES-4];
module_param_array(vendor_firsts, int, NULL, 0444);
module_param_array(vendor_counts, int, NULL, 0444);
static const char * vendor_labels[CH_TYPES-4] = {
"v0", "v1", "v2", "v3"
};
// module_param_string_array(vendor_labels, NULL, 0444);
#define dprintk(fmt, arg...) if (debug) \
printk(KERN_DEBUG "%s: " fmt, ch->name , ## arg)
#define vprintk(fmt, arg...) if (verbose) \
printk(KERN_INFO "%s: " fmt, ch->name , ## arg)
/* ------------------------------------------------------------------- */
#define MAX_RETRIES 1
static int ch_probe(struct device *);
static int ch_remove(struct device *);
static int ch_open(struct inode * inode, struct file * filp);
static int ch_release(struct inode * inode, struct file * filp);
static int ch_ioctl(struct inode * inode, struct file * filp,
unsigned int cmd, unsigned long arg);
#ifdef CONFIG_COMPAT
static long ch_ioctl_compat(struct file * filp,
unsigned int cmd, unsigned long arg);
#endif
static struct class * ch_sysfs_class;
typedef struct {
struct list_head list;
int minor;
char name[8];
struct scsi_device *device;
struct scsi_device **dt; /* ptrs to data transfer elements */
u_int firsts[CH_TYPES];
u_int counts[CH_TYPES];
u_int unit_attention;
u_int voltags;
struct mutex lock;
} scsi_changer;
static LIST_HEAD(ch_devlist);
static DEFINE_SPINLOCK(ch_devlist_lock);
static int ch_devcount;
static struct scsi_driver ch_template =
{
.owner = THIS_MODULE,
.gendrv = {
.name = "ch",
.probe = ch_probe,
.remove = ch_remove,
},
};
static struct file_operations changer_fops =
{
.owner = THIS_MODULE,
.open = ch_open,
.release = ch_release,
.ioctl = ch_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = ch_ioctl_compat,
#endif
};
static const struct {
unsigned char sense;
unsigned char asc;
unsigned char ascq;
int errno;
} err[] = {
/* Just filled in what looks right. Hav'nt checked any standard paper for