File:  [Early Linux] / linux / kernel / blk_drv / scsi / scsi_ioctl.c
Revision 1.1.1.2 (vendor branch): download - view: text, annotated - select for diffs
Tue Apr 24 18:13:28 2018 UTC (8 years, 3 months ago) by root
Branches: linus, MAIN
CVS tags: linux098, linux097, HEAD
linux 0.97

#include <linux/config.h>
#ifdef CONFIG_SCSI 

#include <asm/io.h>
#include <asm/segment.h>
#include <asm/system.h>

#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/string.h>

#include "scsi.h"
#include "hosts.h"
#include "scsi_ioctl.h"

#define MAX_RETRIES 5	
#define MAX_TIMEOUT 200
#define MAX_BUF 8192  	

#define max(a,b) (((a) > (b)) ? (a) : (b))

/*
 * If we are told to probe a host, we will return 0 if  the host is not
 * present, 1 if the host is present, and will return an identifying
 * string at *arg, if arg is non null, filling to the length stored at
 * (int *) arg
 */

static int ioctl_probe(int dev, void *buffer)
{
	int temp;
	int len;
	
	if ((temp = scsi_hosts[dev].present) && buffer) {
		len = get_fs_long ((int *) buffer);
		memcpy_tofs (buffer, scsi_hosts[dev].info(), len);
	}
	return temp;
}

/*
 * 
 * The SCSI_IOCTL_SEND_COMMAND ioctl sends a command out to the SCSI host.
 * The MAX_TIMEOUT and MAX_RETRIES  variables are used.  
 * 
 * dev is the SCSI device struct ptr, *(int *) arg is the length of the
 * input data, if any, not including the command string & counts, 
 * *((int *)arg + 1) is the output buffer size in bytes.
 * 
 * *(char *) ((int *) arg)[2] the actual command byte.   
 * 
 * Note that no more than MAX_BUF data bytes will be transfered.  Since
 * SCSI block device size is 512 bytes, I figured 1K was good.
 * but (WDE) changed it to 8192 to handle large bad track buffers.
 * 
 * This size *does not* include the initial lengths that were passed.
 * 
 * The SCSI command is read from the memory location immediately after the
 * length words, and the input data is right after the command.  The SCSI
 * routines know the command size based on the opcode decode.  
 * 
 * The output area is then filled in starting from the command byte. 
 */

static int the_result[MAX_SCSI_HOSTS];

static void scsi_ioctl_done (int host, int result)
{
	the_result[host] = result;	
}	
	
static int ioctl_command(Scsi_Device *dev, void *buffer)
{
	char buf[MAX_BUF];
	char cmd[10];
	char * cmd_in;
	unsigned char opcode;
	int inlen, outlen, cmdlen, temp, host;

	if (!buffer)
		return -EINVAL;
	
	inlen = get_fs_long((int *) buffer);
	outlen = get_fs_long( ((int *) buffer) + 1);

	cmd_in = (char *) ( ((int *)buffer) + 2);
	opcode = get_fs_byte(cmd_in); 

	memcpy_fromfs ((void *) cmd,  cmd_in,  cmdlen = COMMAND_SIZE (opcode));
	memcpy_fromfs ((void *) buf,  (void *) (cmd_in + cmdlen),  inlen);
	host = dev->host_no;

#ifndef DEBUG_NO_CMD
	do {
		cli();
		if (the_result[host]) {
			sti();
			while(the_result[host])
				/* nothing */;
		} else {
			the_result[host]=-1;
			sti();
			break;
		}
	} while (1);
	
	scsi_do_cmd(host,  dev->id,  cmd,  buf,  ((outlen > MAX_BUF) ? 
			MAX_BUF : outlen),  scsi_ioctl_done,  MAX_TIMEOUT, 
			buf,  MAX_RETRIES);

	while (the_result[host] == -1)
		/* nothing */;
	temp = the_result[host];
	the_result[host] = 0;
	memcpy_tofs ((void *) cmd_in,  buf,  (outlen > MAX_BUF) ? MAX_BUF  : outlen);
	return temp;
#else
	{
	int i;
	printk("scsi_ioctl : device %d.  command = ", dev->id);
	for (i = 0; i < 10; ++i)
		printk("%02x ", cmd[i]);
	printk("\r\nbuffer =");
	for (i = 0; i < 20; ++i)
		printk("%02x ", buf[i]);
	printk("\r\n");
	printk("inlen = %d, outlen = %d, cmdlen = %d\n",
		inlen, outlen, cmdlen);
	printk("buffer = %d, cmd_in = %d\n", buffer, cmd_in);
	}
	return 0;
#endif
}

	
/*
	the scsi_ioctl() function differs from most ioctls in that it does
	not take a major/minor number as the dev filed.  Rather, it takes
	a pointer to a scsi_devices[] element, a structure. 
*/
int scsi_ioctl (Scsi_Device *dev, int cmd, void *arg)
{
	if ((cmd != 0 && dev->id > NR_SCSI_DEVICES))
		return -ENODEV;
	if ((cmd == 0 && dev->host_no > MAX_SCSI_HOSTS))
		return -ENODEV;
	
	switch (cmd) {
		case SCSI_IOCTL_PROBE_HOST:
			return ioctl_probe(dev->host_no, arg);
		case SCSI_IOCTL_SEND_COMMAND:
			return ioctl_command((Scsi_Device *) dev, arg);
		default :			
			return -EINVAL;
	}
}
#endif

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.