Commit ba97e35a authored by Linus Torvalds's avatar Linus Torvalds

Import 1.1.60

parent c3b8d39c
......@@ -11,6 +11,13 @@ N: Werner Almesberger
E: almesber@bernina.ethz.ch
D: dosfs, LILO, some fd features, various other hacks here and there
N: John Aycock
E: aycock@cpsc.ucalgary.ca
D: Adaptec 274x driver
S: Department of Computer Science
S: University of Calgary
S: Calgary, Alberta, Canada
N: Krishna Balasubramanian
E: balasub@cis.ohio-state.edu
D: Wrote SYS V IPC (part of standard kernel since 0.99.10)
......@@ -376,6 +383,14 @@ S: 2200 Monroe Street #1509
S: Santa Clara, California 95050-3452
S: USA
N: Alain L. Knaff
E: Alain.Knaff@imag.fr
D: floppy driver
S: Appartement 310B
S: 11, rue General Mangin
S: 38100 Grenoble
S: France
N: Rudolf Koenig
E: rfkoenig@immd4.informatik.uni-erlangen.de
D: The Linux Support Team Erlangen
......
VERSION = 1
PATCHLEVEL = 1
SUBLEVEL = 59
SUBLEVEL = 60
ARCH = i386
......
......@@ -51,7 +51,9 @@ comment 'SCSI low-level drivers'
bool 'Adaptec AHA152X support' CONFIG_SCSI_AHA152X n
bool 'Adaptec AHA1542 support' CONFIG_SCSI_AHA1542 y
bool 'Adaptec AHA1740 support' CONFIG_SCSI_AHA1740 n
bool 'Adaptec AHA274X/284X support' CONFIG_SCSI_AHA274X n
bool 'BusLogic SCSI support' CONFIG_SCSI_BUSLOGIC n
bool 'UltraStor 14F/34F support' CONFIG_SCSI_U14_34F n
bool 'Future Domain 16xx SCSI support' CONFIG_SCSI_FUTURE_DOMAIN n
bool 'Generic NCR5380 SCSI support' CONFIG_SCSI_GENERIC_NCR5380 n
bool 'NCR53c7,8xx SCSI support' CONFIG_SCSI_NCR53C7xx n
......
......@@ -35,17 +35,53 @@
* asynchronous events from the drive informing the driver that a disk
* has been inserted, removed, etc.
*
* NEWS FLASH - The driver now supports interrupts and DMA, but they are
* NEWS FLASH - The driver now supports interrupts but they are
* turned off by default. Use of interrupts is highly encouraged, it
* cuts CPU usage down to a reasonable level. For a single-speed drive,
* DMA is ok, but the 8-bit DMA cannot keep up with the double speed
* drives.
* cuts CPU usage down to a reasonable level. I had DMA in for a while
* but PC DMA is just too slow. Better to just insb() it.
*
* One thing about these drives: They talk in MSF (Minute Second Frame) format.
* There are 75 frames a second, 60 seconds a minute, and up to 75 minutes on a
* disk. The funny thing is that these are sent to the drive in BCD, but the
* interface wants to see them in decimal. A lot of conversion goes on.
*
* DRIVER SPECIAL FEATURES
* -----------------------
*
* This section describes features beyond the normal audio and CD-ROM
* functions of the drive.
*
* 2048 byte buffer mode
*
* If a disk is mounted with -o block=2048, data is copied straight
* from the drive data port to the buffer. Otherwise, the readahead
* buffer must be involved to hold the other 1K of data when a 1K
* block operation is done. Note that with 2048 byte blocks you
* cannot execute files from the CD.
*
* XA compatability
*
* The driver should support XA disks for both the CDU31A and CDU33A.
* It does this transparently, the using program doesn't need to set it.
*
* Multi-Session
*
* A multi-session disk is treated like a partitioned disk, each session
* has it's own minor device number, starting with 0. The support is
* pretty transparent, music, TOC operations, and read operations should
* all work transparently on any session. Note that since the driver
* writer doesn't have a multi-session disk, this is all theoritical.
* Also, music operation will obviously only work on one session at a
* time.
*
* Raw sector I/O
*
* Using the CDROMREADAUDIO it is possible to read raw audio and data
* tracks. Both operations return 2352 bytes per sector. On the data
* tracks, the first 12 bytes is not returned by the drive and the value
* of that data is indeterminate.
*
*
* Copyright (C) 1993 Corey Minyard
*
* This program is free software; you can redistribute it and/or modify
......@@ -70,50 +106,50 @@
* You have another card, you are on your own.
*
* +----------+-----------------+----------------------+
* | JP1 | 34 Pin Conn | |
* | JP2 +-----------------+ |
* | JP3 |
* | JP4 |
* | +--+
* | | +-+
* | | | | External
* | | | | Connector
* | | | |
* | | +-+
* | +--+
* | |
* | +--------+
* | |
* | JP1 | 34 Pin Conn | |
* | JP2 +-----------------+ |
* | JP3 |
* | JP4 |
* | +--+
* | | +-+
* | | | | External
* | | | | Connector
* | | | |
* | | +-+
* | +--+
* | |
* | +--------+
* | |
* +------------------------------------------+
*
* JP1 sets the Base Address, using the following settings:
*
* Address Pin 1 Pin 2
* ------- ----- -----
* 0x320 Short Short
* 0x330 Short Open
* 0x340 Open Short
* 0x360 Open Open
* Address Pin 1 Pin 2
* ------- ----- -----
* 0x320 Short Short
* 0x330 Short Open
* 0x340 Open Short
* 0x360 Open Open
*
* JP2 and JP3 configure the DMA channel; they must be set the same.
*
* DMA Pin 1 Pin 2 Pin 3
* --- ----- ----- -----
* 1 On Off On
* 2 Off On Off
* 3 Off Off On
* DMA Pin 1 Pin 2 Pin 3
* --- ----- ----- -----
* 1 On Off On
* 2 Off On Off
* 3 Off Off On
*
* JP4 Configures the IRQ:
*
* IRQ Pin 1 Pin 2 Pin 3 Pin 4
* --- ----- ----- ----- -----
* 3 Off Off On Off
* 4 Off Off* Off On
* 5 On Off Off Off
* 6 Off On Off Off
* IRQ Pin 1 Pin 2 Pin 3 Pin 4
* --- ----- ----- ----- -----
* 3 Off Off On Off
* 4 Off Off* Off On
* 5 On Off Off Off
* 6 Off On Off Off
*
* * The documentation states to set this for interrupt
* 4, but I think that is a mistake.
* * The documentation states to set this for interrupt
* 4, but I think that is a mistake.
*/
#include <linux/errno.h>
......@@ -126,6 +162,7 @@
#include <linux/genhd.h>
#include <linux/ioport.h>
#include <linux/string.h>
#include <linux/malloc.h>
#include <asm/system.h>
#include <asm/io.h>
......@@ -138,6 +175,9 @@
#define MAJOR_NR CDU31A_CDROM_MAJOR
#include "blk.h"
#define DEBUG 0
#define CDU31A_READAHEAD 64 /* 64 sector, 32kB, 16 reads read-ahead */
#define CDU31A_MAX_CONSECUTIVE_ATTENTIONS 10
/* Define the following if you have data corruption problems. */
......@@ -150,26 +190,25 @@
*/
static struct
{
unsigned short base; /* I/O Base Address */
short dma_num; /* DMA Number (-1 means no DMA) */
short int_num; /* Interrupt Number (-1 means scan for it,
unsigned short base; /* I/O Base Address */
short int_num; /* Interrupt Number (-1 means scan for it,
0 means don't use) */
} cdu31a_addresses[] =
{
{ 0x340, -1, 0 }, /* Standard configuration Sony Interface */
{ 0x1f88, -1, 0 }, /* Fusion CD-16 */
{ 0x230, -1, 0 }, /* SoundBlaster 16 card */
{ 0x360, -1, 0 }, /* Secondary standard Sony Interface */
{ 0x320, -1, 0 }, /* Secondary standard Sony Interface */
{ 0x330, -1, 0 }, /* Secondary standard Sony Interface */
{ 0x634, -1, 0 }, /* Sound FX SC400 */
{ 0x654, -1, 0 }, /* Sound FX SC400 */
{ 0x340, 0 }, /* Standard configuration Sony Interface */
{ 0x1f88, 0 }, /* Fusion CD-16 */
{ 0x230, 0 }, /* SoundBlaster 16 card */
{ 0x360, 0 }, /* Secondary standard Sony Interface */
{ 0x320, 0 }, /* Secondary standard Sony Interface */
{ 0x330, 0 }, /* Secondary standard Sony Interface */
{ 0x634, 0 }, /* Sound FX SC400 */
{ 0x654, 0 }, /* Sound FX SC400 */
{ 0 }
};
static int handle_sony_cd_attention(void);
static int read_subcode(void);
static void sony_get_toc(void);
static void sony_get_toc(int dev);
static int scd_open(struct inode *inode, struct file *filp);
static void do_sony_cd_cmd(unsigned char cmd,
unsigned char *params,
......@@ -179,6 +218,11 @@ static void do_sony_cd_cmd(unsigned char cmd,
static void size_to_buf(unsigned int size,
unsigned char *buf);
/* Parameters for the read-ahead. */
static unsigned int sony_next_block; /* Next 512 byte block offset */
static unsigned int sony_blocks_left = 0; /* Number of 512 byte blocks left
in the current read command. */
/* The base I/O address of the Sony Interface. This is a variable (not a
#define) so it can be easily changed via some future ioctl() */
......@@ -198,37 +242,32 @@ static volatile unsigned short sony_cd_read_reg;
static volatile unsigned short sony_cd_fifost_reg;
static int sony_disc_changed = 1; /* Has the disk been changed
since the last check? */
static int sony_toc_read = 0; /* Has the table of contents been
read? */
static int sony_spun_up = 0; /* Has the drive been spun up? */
static unsigned int sony_buffer_size; /* Size in bytes of the read-ahead
buffer. */
static unsigned int sony_buffer_sectors; /* Size (in 2048 byte records) of
the read-ahead buffer. */
static int sony_xa_mode = 0; /* Is an XA disk in the drive
and the drive a CDU31A? */
static int sony_raw_data_mode = 1; /* 1 if data tracks, 0 if audio.
For raw data reads. */
static unsigned int sony_usage = 0; /* How many processes have the
drive open. */
static volatile int sony_first_block = -1; /* First OS block (512 byte) in
the read-ahead buffer */
static volatile int sony_last_block = -1; /* Last OS block (512 byte) in
the read-ahead buffer */
static struct s_sony_session_toc *(ses_tocs[MAX_TRACKS]); /* Points to the
table of
contents. */
static struct s_sony_toc *sony_toc; /* Points to the table of
contents. */
static struct s_sony_subcode * volatile last_sony_subcode; /* Points to the last
subcode address read */
static unsigned char * volatile sony_buffer; /* Points to the read-ahead
buffer */
static volatile int sony_inuse = 0; /* Is the drive in use? Only one operation at a time
allowed */
static volatile int sony_inuse = 0; /* Is the drive in use? Only one operation
at a time allowed */
static struct wait_queue * sony_wait = NULL;
static struct wait_queue * sony_wait = NULL; /* Things waiting for the drive */
static struct task_struct *has_cd_task = NULL; /* The task that is currently using the
CDROM drive, or NULL if none. */
static struct task_struct *has_cd_task = NULL; /* The task that is currently
using the CDROM drive, or
NULL if none. */
static int is_double_speed = 0; /* Is the drive a CDU33A? */
......@@ -248,12 +287,29 @@ static volatile int sony_audio_status = CDROM_AUDIO_NO_STATUS;
static unsigned volatile char cur_pos_msf[3] = { 0, 0, 0 };
static unsigned volatile char final_pos_msf[3] = { 0, 0, 0 };
static int irq_used = -1;
static int dma_channel = -1;
/* What IRQ is the drive using? 0 if none. */
static int irq_used = 0;
/* The interrupt handler will wake this queue up when it gets an
interrupts. */
static struct wait_queue *cdu31a_irq_wait = NULL;
static int curr_control_reg = 0; /* Current value of the control register */
/* A disk changed variable for every possible session. When a disk change
is detected, these will all be set to TRUE. As the upper layers ask
for disk_changed status for individual minor numbers, they will be
cleared. */
static char disk_changed[MAX_TRACKS];
/* Variable for using the readahead buffer. The readahead buffer
is used for raw sector reads and for blocksizes that are smaller
than 2048 bytes. */
static char *readahead_buffer = NULL; /* Used for 1024 byte blocksize. */
static int readahead_dataleft = 0;
static int readahead_bad = 0;
/*
* This routine returns 1 if the disk has been changed since the last
* check or 0 if it hasn't.
......@@ -266,13 +322,13 @@ scd_disk_change(dev_t full_dev)
target = MINOR(full_dev);
if (target > 0) {
if (target >= MAX_TRACKS) {
printk("Sony CD-ROM request error: invalid device.\n");
return 0;
}
retval = sony_disc_changed;
sony_disc_changed = 0;
retval = disk_changed[target];
disk_changed[target] = 0;
return retval;
}
......@@ -299,7 +355,14 @@ static void
cdu31a_interrupt(int unused)
{
disable_interrupts();
wake_up(&cdu31a_irq_wait);
if (cdu31a_irq_wait != NULL)
{
wake_up(&cdu31a_irq_wait);
}
else
{
printk("CDU31A: Got an interrupt but nothing was waiting\n");
}
}
/*
......@@ -365,6 +428,12 @@ is_param_write_rdy(void)
return((inb(sony_cd_fifost_reg) & SONY_PARAM_WRITE_RDY_BIT) != 0);
}
static inline int
is_result_reg_not_empty(void)
{
return((inb(sony_cd_fifost_reg) & SONY_RES_REG_NOT_EMP_BIT) != 0);
}
static inline void
reset_drive(void)
{
......@@ -434,7 +503,7 @@ write_cmd(unsigned char cmd)
static void
set_drive_params(void)
{
unsigned char res_reg[2];
unsigned char res_reg[12];
unsigned int res_size;
unsigned char params[3];
......@@ -450,7 +519,7 @@ set_drive_params(void)
2,
res_reg,
&res_size);
if ((res_size < 2) || ((res_reg[0] & 0x20) == 0x20))
if ((res_size < 2) || ((res_reg[0] & 0xf0) == 0x20))
{
printk(" Unable to set mechanical parameters: 0x%2.2x\n", res_reg[1]);
}
......@@ -462,7 +531,7 @@ set_drive_params(void)
static void
restart_on_error(void)
{
unsigned char res_reg[2];
unsigned char res_reg[12];
unsigned int res_size;
unsigned int retry_count;
......@@ -476,7 +545,7 @@ restart_on_error(void)
}
set_drive_params();
do_sony_cd_cmd(SONY_SPIN_UP_CMD, NULL, 0, res_reg, &res_size);
if ((res_size < 2) || ((res_reg[0] & 0x20) == 0x20))
if ((res_size < 2) || ((res_reg[0] & 0xf0) == 0x20))
{
printk("cdu31a: Unable to spin up drive: 0x%2.2x\n", res_reg[1]);
}
......@@ -486,15 +555,10 @@ restart_on_error(void)
schedule();
do_sony_cd_cmd(SONY_READ_TOC_CMD, NULL, 0, res_reg, &res_size);
if ((res_size < 2) || ((res_reg[0] & 0x20) == 0x20))
if ((res_size < 2) || ((res_reg[0] & 0xf0) == 0x20))
{
printk("cdu31a: Unable to read TOC: 0x%2.2x\n", res_reg[1]);
}
sony_get_toc();
if (!sony_toc_read)
{
printk("cdu31a: Unable to get TOC data\n");
}
}
/*
......@@ -557,6 +621,9 @@ get_result(unsigned char *result_buffer,
}
if (is_busy() || (!(is_result_ready())))
{
#if DEBUG
printk("CDU31A timeout out %d\n", __LINE__);
#endif
result_buffer[0] = 0x20;
result_buffer[1] = SONY_TIMEOUT_OP_ERR;
*result_size = 2;
......@@ -571,6 +638,14 @@ get_result(unsigned char *result_buffer,
a = read_result_register();
*result_buffer = a;
result_buffer++;
/* Check for block error status result. */
if ((a & 0xf0) == 0x50)
{
*result_size = 1;
return;
}
b = read_result_register();
*result_buffer = b;
result_buffer++;
......@@ -605,6 +680,9 @@ get_result(unsigned char *result_buffer,
}
if (!is_result_ready())
{
#if DEBUG
printk("CDU31A timeout out %d\n", __LINE__);
#endif
result_buffer[0] = 0x20;
result_buffer[1] = SONY_TIMEOUT_OP_ERR;
*result_size = 2;
......@@ -631,6 +709,9 @@ get_result(unsigned char *result_buffer,
}
if (!is_result_ready())
{
#if DEBUG
printk("CDU31A timeout out %d\n", __LINE__);
#endif
result_buffer[0] = 0x20;
result_buffer[1] = SONY_TIMEOUT_OP_ERR;
*result_size = 2;
......@@ -649,252 +730,6 @@ get_result(unsigned char *result_buffer,
}
}
static void
read_data_dma(unsigned char *data,
unsigned int data_size,
unsigned char *result_buffer,
unsigned int *result_size)
{
unsigned int retry_count;
cli();
disable_dma(dma_channel);
clear_dma_ff(dma_channel);
set_dma_mode(dma_channel, DMA_MODE_READ);
set_dma_addr(dma_channel, (int) data);
set_dma_count(dma_channel, data_size);
enable_dma(dma_channel);
sti();
retry_count = jiffies + SONY_JIFFIES_TIMEOUT;
while ( (retry_count > jiffies)
&& (!is_data_ready())
&& (!is_result_ready()))
{
while (handle_sony_cd_attention())
;
sony_sleep();
}
if (!is_data_requested())
{
result_buffer[0] = 0x20;
result_buffer[1] = SONY_TIMEOUT_OP_ERR;
*result_size = 2;
return;
}
}
/*
* Read in a 2048 byte block of data.
*/
static void
read_data_block(unsigned char *data,
unsigned int data_size,
unsigned char *result_buffer,
unsigned int *result_size)
{
#ifdef SONY_POLL_EACH_BYTE
int i;
unsigned int retry_count;
for (i=0; i<data_size; i++)
{
retry_count = jiffies + SONY_JIFFIES_TIMEOUT;
while ((retry_count > jiffies) && (!is_data_requested()))
{
while (handle_sony_cd_attention())
;
sony_sleep();
}
if (!is_data_requested())
{
result_buffer[0] = 0x20;
result_buffer[1] = SONY_TIMEOUT_OP_ERR;
*result_size = 2;
return;
}
*data = read_data_register();
data++;
}
#else
insb(sony_cd_read_reg, data, data_size);
#endif
}
/*
* This routine issues a read data command and gets the data. I don't
* really like the way this is done (I would prefer for do_sony_cmd() to
* handle it automatically) but I found that the drive returns status
* when it finishes reading (not when the host has read all the data)
* or after it gets an error. This means that the status can be
* received at any time and should be handled immediately (at least
* between every 2048 byte block) to check for errors, we can't wait
* until all the data is read.
*
* This routine returns the total number of sectors read. It will
* not return an error if it reads at least one sector successfully.
*/
static unsigned int
get_data(unsigned char *orig_data,
unsigned char *params, /* 6 bytes with the MSF start address
and number of sectors to read. */
unsigned int orig_data_size,
unsigned char *result_buffer,
unsigned int *result_size)
{
unsigned int cur_offset;
unsigned int retry_count;
int result_read;
int num_retries;
unsigned int num_sectors_read = 0;
unsigned char *data = orig_data;
unsigned int data_size = orig_data_size;
num_retries = 0;
retry_data_operation:
result_buffer[0] = 0;
result_buffer[1] = 0;
/*
* Clear any outstanding attentions and wait for the drive to
* complete any pending operations.
*/
while (handle_sony_cd_attention())
;
retry_count = jiffies + SONY_JIFFIES_TIMEOUT;
while ((retry_count > jiffies) && (is_busy()))
{
sony_sleep();
while (handle_sony_cd_attention())
;
}
if (is_busy())
{
result_buffer[0] = 0x20;
result_buffer[1] = SONY_TIMEOUT_OP_ERR;
*result_size = 2;
}
else
{
/* Issue the command */
clear_result_ready();
clear_param_reg();
write_params(params, 6);
write_cmd(SONY_READ_CMD);
/*
* Read the data from the drive one 2048 byte sector at a time. Handle
* any results received between sectors, if an error result is returned
* terminate the operation immediately.
*/
cur_offset = 0;
result_read = 0;
while ((data_size > 0) && (result_buffer[0] == 0))
{
/* Wait for the drive to tell us we have something */
retry_count = jiffies + SONY_JIFFIES_TIMEOUT;
while ((retry_count > jiffies) && (!(is_result_ready() || is_data_ready())))
{
while (handle_sony_cd_attention())
;
sony_sleep();
}
if (!(is_result_ready() || is_data_ready()))
{
result_buffer[0] = 0x20;
result_buffer[1] = SONY_TIMEOUT_OP_ERR;
*result_size = 2;
}
/* Handle results first */
else if (is_result_ready())
{
result_read = 1;
get_result(result_buffer, result_size);
}
/* Handle data next */
else if (dma_channel > 0)
{
clear_data_ready();
read_data_dma(data, 2048, result_buffer, result_size);
data += 2048;
data_size -= 2048;
cur_offset = cur_offset + 2048;
num_sectors_read++;
}
else
{
/*
* The drive has to be polled for status on a byte-by-byte basis
* to know if the data is ready. Yuck. I really wish I could use
* DMA all the time.
*
* NEWS FLASH - I am no longer polling on a byte-by-byte basis.
* It seems to work ok, but the spec says you shouldn't.
*/
clear_data_ready();
read_data_block(data, 2048, result_buffer, result_size);
data += 2048;
data_size -= 2048;
cur_offset = cur_offset + 2048;
num_sectors_read++;
}
}
/* Make sure the result has been read */
if (!result_read)
{
get_result(result_buffer, result_size);
}
}
if ( ((result_buffer[0] & 0x20) == 0x20)
&& (result_buffer[1] != SONY_NOT_SPIN_ERR) /* No retry when not spin */
&& (num_retries < MAX_CDU31A_RETRIES))
{
/*
* If an error occurs, go back and only read one sector at the
* given location. Hopefully the error occurred on an unused
* sector after the first one. It is hard to say which sector
* the error occurred on because the drive returns status before
* the data transfer is finished and doesn't say which sector.
*/
data_size = 2048;
data = orig_data;
num_sectors_read = 0;
size_to_buf(1, &params[3]);
num_retries++;
/* Issue a reset on an error (the second time), otherwise just delay */
if (num_retries == 2)
{
restart_on_error();
}
else
{
current->state = TASK_INTERRUPTIBLE;
current->timeout = jiffies + 10;
schedule();
}
/* Restart the operation. */
goto retry_data_operation;
}
return(num_sectors_read);
}
/*
* Do a command that does not involve data transfer. This routine must
* be re-entrant from the same task to support being called from the
......@@ -952,6 +787,9 @@ do_sony_cd_cmd(unsigned char cmd,
}
if (is_busy())
{
#if DEBUG
printk("CDU31A timeout out %d\n", __LINE__);
#endif
result_buffer[0] = 0x20;
result_buffer[1] = SONY_TIMEOUT_OP_ERR;
*result_size = 2;
......@@ -967,7 +805,7 @@ do_sony_cd_cmd(unsigned char cmd,
get_result(result_buffer, result_size);
}
if ( ((result_buffer[0] & 0x20) == 0x20)
if ( ((result_buffer[0] & 0xf0) == 0x20)
&& (num_retries < MAX_CDU31A_RETRIES))
{
num_retries++;
......@@ -1000,6 +838,7 @@ handle_sony_cd_attention(void)
{
unsigned char atten_code;
static int num_consecutive_attentions = 0;
int i;
if (is_attention())
......@@ -1019,11 +858,22 @@ handle_sony_cd_attention(void)
{
/* Someone changed the CD. Mark it as changed */
case SONY_MECH_LOADED_ATTN:
sony_disc_changed = 1;
sony_toc_read = 0;
for (i=0; i<MAX_TRACKS; i++)
{
disk_changed[i] = 1;
if (ses_tocs[i] != NULL)
{
kfree_s(ses_tocs[i], sizeof(struct s_sony_session_toc));
ses_tocs[i] = NULL;
}
}
sony_audio_status = CDROM_AUDIO_NO_STATUS;
sony_first_block = -1;
sony_last_block = -1;
sony_blocks_left = 0;
break;
case SONY_SPIN_DOWN_COMPLETE_ATTN:
/* Mark the disk as spun down. */
sony_spun_up = 0;
break;
case SONY_AUDIO_PLAY_DONE_ATTN:
......@@ -1120,70 +970,485 @@ size_to_buf(unsigned int size,
buf[2] = size % 256;
}
/*
* The OS calls this to perform a read or write operation to the drive.
* Write obviously fail. Reads to a read ahead of sony_buffer_size
* bytes to help speed operations. This especially helps since the OS
* uses 1024 byte blocks and the drive uses 2048 byte blocks. Since most
* data access on a CD is done sequentially, this saves a lot of operations.
*/
static void
do_cdu31a_request(void)
/* Starts a read operation. Returns 0 on success and 1 on failure.
The read operation used here allows multiple sequential sectors
to be read and status returned for each sector. The driver will
read the out one at a time as the requests come and abort the
operation if the requested sector is not the next one from the
drive. */
static int
start_request(unsigned int sector,
unsigned int nsect,
int read_nsect_only,
int dev)
{
int block;
unsigned int dev;
int nsect;
unsigned char params[10];
unsigned char res_reg[12];
unsigned int res_size;
int copyoff;
int spin_up_retry;
unsigned char params[6];
unsigned int read_size;
unsigned int retry_count;
/*
* Make sure no one else is using the driver; wait for them
* to finish if it is so.
log_to_msf(sector, params);
/* If requested, read exactly what was asked. */
if (read_nsect_only)
{
read_size = nsect;
}
/*
* If the full read-ahead would go beyond the end of the media, trim
* it back to read just till the end of the media.
*/
cli();
while (sony_inuse)
else if ((sector + nsect) >= ses_tocs[dev]->lead_out_start_lba)
{
interruptible_sleep_on(&sony_wait);
if (current->signal & ~current->blocked)
{
return;
}
read_size = ses_tocs[dev]->lead_out_start_lba - sector;
}
sony_inuse = 1;
has_cd_task = current;
sti();
/* Read the full readahead amount. */
else
{
read_size = CDU31A_READAHEAD;
}
size_to_buf(read_size, &params[3]);
if (!sony_spun_up)
/*
* Clear any outstanding attentions and wait for the drive to
* complete any pending operations.
*/
while (handle_sony_cd_attention())
;
retry_count = jiffies + SONY_JIFFIES_TIMEOUT;
while ((retry_count > jiffies) && (is_busy()))
{
scd_open (NULL,NULL);
sony_sleep();
while (handle_sony_cd_attention())
;
}
while (1)
if (is_busy())
{
cdu31a_request_startover:
/*
* The beginning here is stolen from the hard disk driver. I hope
* its right.
*/
if (!(CURRENT) || CURRENT->dev < 0)
{
goto end_do_cdu31a_request;
}
printk("CDU31A: Timeout while waiting to issue command\n");
return(1);
}
else
{
/* Issue the command */
clear_result_ready();
clear_param_reg();
INIT_REQUEST;
dev = MINOR(CURRENT->dev);
block = CURRENT->sector;
nsect = CURRENT->nr_sectors;
if (dev != 0)
{
end_request(0);
goto cdu31a_request_startover;
write_params(params, 6);
write_cmd(SONY_READ_BLKERR_STAT_CMD);
sony_blocks_left = read_size * 4;
sony_next_block = sector * 4;
readahead_dataleft = 0;
readahead_bad = 0;
return(0);
}
}
/* Abort a pending read operation. Clear all the drive status and
readahead variables. */
static void
abort_read(void)
{
unsigned char result_reg[2];
int result_size;
volatile int val;
do_sony_cd_cmd(SONY_ABORT_CMD, NULL, 0, result_reg, &result_size);
if ((result_reg[0] & 0xf0) == 0x20)
{
printk("CDU31A: Error aborting read, error = 0x%2.2x\n",
result_reg[1]);
}
while (is_result_reg_not_empty())
{
val = read_result_register();
}
clear_data_ready();
clear_result_ready();
/* Clear out the data */
while (is_data_requested())
{
val = read_data_register();
}
sony_blocks_left = 0;
readahead_dataleft = 0;
readahead_bad = 0;
}
/* Actually get data and status from the drive. */
static void
input_data(char *buffer,
unsigned int bytesleft,
unsigned int nblocks,
unsigned int offset,
unsigned int skip)
{
int i;
volatile unsigned char val;
/* If an XA disk on a CDU31A, skip the first 12 bytes of data from
the disk. The real data is after that. */
if (sony_xa_mode)
{
for(i=0; i<CD_XA_HEAD; i++)
{
val = read_data_register();
}
}
clear_data_ready();
if (bytesleft == 2048) /* 2048 byte direct buffer transfer */
{
insb(sony_cd_read_reg, buffer, 2048);
readahead_dataleft = 0;
}
else
{
/* If the input read did not align with the beginning of the block,
skip the necessary bytes. */
if (skip != 0)
{
insb(sony_cd_read_reg, readahead_buffer, skip);
}
/* Get the data into the buffer. */
insb(sony_cd_read_reg, &buffer[offset], bytesleft);
/* Get the rest of the data into the readahead buffer at the
proper location. */
readahead_dataleft = (2048 - skip) - bytesleft;
insb(sony_cd_read_reg,
readahead_buffer + bytesleft,
readahead_dataleft);
}
sony_blocks_left -= nblocks;
sony_next_block += nblocks;
/* If an XA disk, we have to clear out the rest of the unused
error correction data. */
if (sony_xa_mode)
{
for(i=0; i<CD_XA_TAIL; i++)
{
val = read_data_register();
}
}
}
/* read data from the drive. Note the nsect must be <= 4. */
static void
read_data_block(char *buffer,
unsigned int block,
unsigned int nblocks,
unsigned char res_reg[],
int *res_size)
{
unsigned int retry_count;
unsigned int bytesleft;
unsigned int offset;
unsigned int skip;
res_reg[0] = 0;
res_reg[1] = 0;
*res_size = 0;
bytesleft = nblocks * 512;
offset = 0;
/* If the data in the read-ahead does not match the block offset,
then fix things up. */
if (((block % 4) * 512) != ((2048 - readahead_dataleft) % 2048))
{
sony_next_block += block % 4;
sony_blocks_left -= block % 4;
skip = (block % 4) * 512;
}
else
{
skip = 0;
}
/* We have readahead data in the buffer, get that first before we
decide if a read is necessary. */
if (readahead_dataleft != 0)
{
if (bytesleft > readahead_dataleft)
{
/* The readahead will not fill the requested buffer, but
get the data out of the readahead into the buffer. */
memcpy(buffer,
readahead_buffer + (2048 - readahead_dataleft),
readahead_dataleft);
readahead_dataleft = 0;
bytesleft -= readahead_dataleft;
offset += readahead_dataleft;
}
else
{
/* The readahead will fill the whole buffer, get the data
and return. */
memcpy(buffer,
readahead_buffer + (2048 - readahead_dataleft),
bytesleft);
readahead_dataleft -= bytesleft;
bytesleft = 0;
sony_blocks_left -= nblocks;
sony_next_block += nblocks;
/* If the data in the readahead is bad, return an error so the
driver will abort the buffer. */
if (readahead_bad)
{
res_reg[0] = 0x20;
res_reg[1] = SONY_BAD_DATA_ERR;
*res_size = 2;
}
if (readahead_dataleft == 0)
{
readahead_bad = 0;
}
/* Final transfer is done for read command, get final result. */
if (sony_blocks_left == 0)
{
get_result(res_reg, res_size);
}
return;
}
}
/* Wait for the drive to tell us we have something */
retry_count = jiffies + SONY_JIFFIES_TIMEOUT;
while ((retry_count > jiffies) && !(is_data_ready()))
{
while (handle_sony_cd_attention())
;
sony_sleep();
}
if (!(is_data_ready()))
{
if (is_result_ready())
{
get_result(res_reg, res_size);
if ((res_reg[0] & 0xf0) != 0x20)
{
printk("CDU31A: Got result that should have been error: %d\n",
res_reg[0]);
res_reg[0] = 0x20;
res_reg[1] = SONY_BAD_DATA_ERR;
*res_size = 2;
}
abort_read();
}
else
{
#if DEBUG
printk("CDU31A timeout out %d\n", __LINE__);
#endif
res_reg[0] = 0x20;
res_reg[1] = SONY_TIMEOUT_OP_ERR;
*res_size = 2;
abort_read();
}
}
else
{
input_data(buffer, bytesleft, nblocks, offset, skip);
/* Wait for the status from the drive. */
retry_count = jiffies + SONY_JIFFIES_TIMEOUT;
while ((retry_count > jiffies) && !(is_result_ready()))
{
while (handle_sony_cd_attention())
;
sony_sleep();
}
if (!is_result_ready())
{
#if DEBUG
printk("CDU31A timeout out %d\n", __LINE__);
#endif
res_reg[0] = 0x20;
res_reg[1] = SONY_TIMEOUT_OP_ERR;
*res_size = 2;
abort_read();
}
else
{
get_result(res_reg, res_size);
/* If we got a buffer status, handle that. */
if ((res_reg[0] & 0xf0) == 0x50)
{
if ( (res_reg[0] == SONY_NO_CIRC_ERR_BLK_STAT)
|| (res_reg[0] == SONY_NO_LECC_ERR_BLK_STAT)
|| (res_reg[0] == SONY_RECOV_LECC_ERR_BLK_STAT))
{
/* The data was successful, but if data was read from
the readahead and it was bad, set the whole
buffer as bad. */
if (readahead_bad)
{
readahead_bad = 0;
res_reg[0] = 0x20;
res_reg[1] = SONY_BAD_DATA_ERR;
*res_size = 2;
}
}
else
{
printk("CDU31A: Data block error: 0x%x\n", res_reg[0]);
res_reg[0] = 0x20;
res_reg[1] = SONY_BAD_DATA_ERR;
*res_size = 2;
/* Data is in the readahead buffer but an error was returned.
Make sure future requests don't use the data. */
if (bytesleft != 2048)
{
readahead_bad = 1;
}
}
/* Final transfer is done for read command, get final result. */
if (sony_blocks_left == 0)
{
get_result(res_reg, res_size);
}
}
else if ((res_reg[0] & 0xf0) != 0x20)
{
/* The drive gave me bad status, I don't know what to do.
Reset the driver and return an error. */
printk("CDU31A: Invalid block status: 0x%x\n", res_reg[0]);
restart_on_error();
res_reg[0] = 0x20;
res_reg[1] = SONY_BAD_DATA_ERR;
*res_size = 2;
}
}
}
}
/*
* The OS calls this to perform a read or write operation to the drive.
* Write obviously fail. Reads to a read ahead of sony_buffer_size
* bytes to help speed operations. This especially helps since the OS
* uses 1024 byte blocks and the drive uses 2048 byte blocks. Since most
* data access on a CD is done sequentially, this saves a lot of operations.
*/
static void
do_cdu31a_request(void)
{
int block;
unsigned int dev;
int nblock;
unsigned char res_reg[12];
unsigned int res_size;
int num_retries;
/*
* Make sure no one else is using the driver; wait for them
* to finish if it is so.
*/
cli();
while (sony_inuse)
{
interruptible_sleep_on(&sony_wait);
if (current->signal & ~current->blocked)
{
return;
}
}
sony_inuse = 1;
has_cd_task = current;
sti();
/* Get drive status before doing anything. */
while (handle_sony_cd_attention())
;
while (1)
{
cdu31a_request_startover:
/*
* The beginning here is stolen from the hard disk driver. I hope
* its right.
*/
if (!(CURRENT) || CURRENT->dev < 0)
{
goto end_do_cdu31a_request;
}
if (!sony_spun_up)
{
struct inode in;
/* This is a kludge to get a valid dev in an inode that
scd_open can take. That's the only thing scd_open()
uses the inode for. */
in.i_rdev = CURRENT->dev;
scd_open(&in,NULL);
}
/* I don't use INIT_REQUEST because it calls return, which would
return without unlocking the device. It shouldn't matter,
but just to be safe... */
if (MAJOR(CURRENT->dev) != MAJOR_NR)
{
panic(DEVICE_NAME ": request list destroyed");
}
if (CURRENT->bh)
{
if (!CURRENT->bh->b_lock)
{
panic(DEVICE_NAME ": block not locked");
}
}
dev = MINOR(CURRENT->dev);
block = CURRENT->sector;
nblock = CURRENT->nr_sectors;
/* Check for multi-session disks. */
if (dev > MAX_TRACKS)
{
printk("CDU31A: Invalid device request: %d\n", dev);
end_request(0);
goto cdu31a_request_startover;
}
else if (ses_tocs[dev] == NULL)
{
printk("CDU31A: session TOC not read: %d\n", dev);
end_request(0);
goto cdu31a_request_startover;
}
/* Check for base read of multi-session disk. */
if ((dev != 0) && (block == 64))
{
if (ses_tocs[dev]->first_track_num == ses_tocs[dev]->last_track_num)
{
printk("CDU31A: Not a multi-session disk: %d\n", dev);
end_request(0);
goto cdu31a_request_startover;
}
/* Offset the request into the session. */
block += (ses_tocs[dev]->start_track_lba * 4);
}
switch(CURRENT->cmd)
......@@ -1193,92 +1458,96 @@ do_cdu31a_request(void)
* If the block address is invalid or the request goes beyond the end of
* the media, return an error.
*/
if ((block / 4) >= sony_toc->lead_out_start_lba)
if ((block / 4) < ses_tocs[dev]->start_track_lba)
{
printk("CDU31A: Request before beginning of media\n");
end_request(0);
goto cdu31a_request_startover;
}
if ((block / 4) >= ses_tocs[dev]->lead_out_start_lba)
{
printk("CDU31A: Request past end of media\n");
end_request(0);
goto cdu31a_request_startover;
}
if (((block + nsect) / 4) >= sony_toc->lead_out_start_lba)
if (((block + nblock) / 4) >= ses_tocs[dev]->lead_out_start_lba)
{
printk("CDU31A: Request past end of media\n");
end_request(0);
goto cdu31a_request_startover;
}
while (nsect > 0)
num_retries = 0;
try_read_again:
while (handle_sony_cd_attention())
;
if (ses_tocs[dev] == NULL)
{
printk("CDU31A: session TOC not read: %d\n", dev);
end_request(0);
goto cdu31a_request_startover;
}
/* If no data is left to be read from the drive, start the
next request. */
if (sony_blocks_left == 0)
{
/*
* If the requested sector is not currently in the read-ahead buffer,
* it must be read in.
*/
if ((block < sony_first_block) || (block > sony_last_block))
if (start_request(block / 4, CDU31A_READAHEAD / 4, 0, dev))
{
sony_first_block = (block / 4) * 4;
log_to_msf(block/4, params);
/*
* If the full read-ahead would go beyond the end of the media, trim
* it back to read just till the end of the media.
*/
if (((block / 4) + sony_buffer_sectors) >= sony_toc->lead_out_start_lba)
{
read_size = sony_toc->lead_out_start_lba - (block / 4);
}
else
{
read_size = sony_buffer_sectors;
}
size_to_buf(read_size, &params[3]);
end_request(0);
goto cdu31a_request_startover;
}
}
/* If the requested block is not the next one waiting in
the driver, abort the current operation and start a
new one. */
else if (block != sony_next_block)
{
#if DEBUG
printk("CDU31A Warning: Read for block %d, expected %d\n",
block,
sony_next_block);
#endif
abort_read();
if (ses_tocs[dev] == NULL)
{
printk("CDU31A: session TOC not read: %d\n", dev);
end_request(0);
goto cdu31a_request_startover;
}
else if (start_request(block / 4, CDU31A_READAHEAD / 4, 0, dev))
{
end_request(0);
goto cdu31a_request_startover;
}
}
/*
* Read the data. If the drive was not spinning, spin it up and try
* once more. I know, the goto is ugly, but I am too lazy to fix it.
*/
spin_up_retry = 0;
try_read_again:
sony_last_block = sony_first_block
+ (get_data(sony_buffer,
params,
(read_size * 2048),
res_reg,
&res_size) * 4) - 1;
if ((res_size < 2) || (res_reg[0] != 0))
{
if ((res_reg[1] == SONY_NOT_SPIN_ERR) && (!spin_up_retry))
{
do_sony_cd_cmd(SONY_SPIN_UP_CMD, NULL, 0, res_reg, &res_size);
spin_up_retry = 1;
goto try_read_again;
}
printk("Sony CDROM Read error: 0x%2.2x\n", res_reg[1]);
sony_first_block = -1;
sony_last_block = -1;
end_request(0);
goto cdu31a_request_startover;
}
read_data_block(CURRENT->buffer, block, nblock, res_reg, &res_size);
if (res_reg[0] == 0x20)
{
if (num_retries > MAX_CDU31A_RETRIES)
{
end_request(0);
goto cdu31a_request_startover;
}
/*
* The data is in memory now, copy it to the buffer and advance to the
* next block to read.
*/
copyoff = (block - sony_first_block) * 512;
/*
* Bugfix: get_data calls handle_sony_cd_attention
* there the buffer may be declared invalid
* if the CD ist changed by setting sony_first_block = -1
* This would cause a segfault in memcpy
*/
if(sony_first_block <0) goto cdu31a_request_startover;
memcpy(CURRENT->buffer, sony_buffer+copyoff, 512);
block += 1;
nsect -= 1;
CURRENT->buffer += 512;
num_retries++;
if (res_reg[1] == SONY_NOT_SPIN_ERR)
{
do_sony_cd_cmd(SONY_SPIN_UP_CMD, NULL, 0, res_reg, &res_size);
}
else
{
printk("CDU31A: Read error: 0x%2.2x\n", res_reg[1]);
}
goto try_read_again;
}
else
{
end_request(1);
}
end_request(1);
break;
case WRITE:
......@@ -1286,40 +1555,130 @@ do_cdu31a_request(void)
break;
default:
panic("Unknown SONY CD cmd");
panic("CDU31A: Unknown cmd");
}
}
end_do_cdu31a_request:
#if 0
/* After finished, cancel any pending operations. */
abort_read();
#endif
has_cd_task = NULL;
sony_inuse = 0;
wake_up_interruptible(&sony_wait);
}
/* Copy overlapping buffers. */
static void
mcovlp(char *dst,
char *src,
int size)
{
src += (size - 1);
dst += (size - 1);
while (size > 0)
{
*dst = *src;
size--;
dst--;
src--;
}
}
/*
* Read the table of contents from the drive and set sony_toc_read if
* Read the table of contents from the drive and set up TOC if
* successful.
*/
static void
sony_get_toc(void)
sony_get_toc(int dev)
{
unsigned int res_size;
unsigned char parms[1];
if (!sony_toc_read)
if (dev >= MAX_TRACKS)
{
do_sony_cd_cmd(SONY_REQ_TOC_DATA_CMD,
NULL,
0,
(unsigned char *) sony_toc,
&res_size);
if ((res_size < 2) || ((sony_toc->exec_status[0] & 0x20) == 0x20))
printk("CDU31A: Request for invalid TOC track: %d\n", dev);
}
else if (ses_tocs[dev] == NULL)
{
ses_tocs[dev] = kmalloc(sizeof(struct s_sony_session_toc), 0);
if (ses_tocs[dev] == NULL)
{
return;
printk("CDU31A: Unable to alloc mem for TOC\n");
}
else
{
parms[0] = dev+1;
do_sony_cd_cmd(SONY_REQ_TOC_DATA_SPEC_CMD,
parms,
1,
(unsigned char *) ses_tocs[dev],
&res_size);
if ((res_size < 2) || ((ses_tocs[dev]->exec_status[0] & 0xf0) == 0x20))
{
kfree_s(ses_tocs[dev], sizeof(struct s_sony_session_toc));
ses_tocs[dev] = NULL;
return;
}
/* For points that do not exist, move the data over them
to the right location. */
if (ses_tocs[dev]->pointb0 != 0xb0)
{
mcovlp(((char *) ses_tocs[dev]) + 27,
((char *) ses_tocs[dev]) + 18,
res_size - 18);
res_size += 9;
}
if (ses_tocs[dev]->pointb1 != 0xb1)
{
mcovlp(((char *) ses_tocs[dev]) + 36,
((char *) ses_tocs[dev]) + 27,
res_size - 27);
res_size += 9;
}
if (ses_tocs[dev]->pointb2 != 0xb2)
{
mcovlp(((char *) ses_tocs[dev]) + 45,
((char *) ses_tocs[dev]) + 36,
res_size - 36);
res_size += 9;
}
if (ses_tocs[dev]->pointb3 != 0xb3)
{
mcovlp(((char *) ses_tocs[dev]) + 54,
((char *) ses_tocs[dev]) + 45,
res_size - 45);
res_size += 9;
}
if (ses_tocs[dev]->pointb4 != 0xb4)
{
mcovlp(((char *) ses_tocs[dev]) + 63,
((char *) ses_tocs[dev]) + 54,
res_size - 54);
res_size += 9;
}
if (ses_tocs[dev]->pointc0 != 0xc0)
{
mcovlp(((char *) ses_tocs[dev]) + 72,
((char *) ses_tocs[dev]) + 63,
res_size - 63);
res_size += 9;
}
ses_tocs[dev]->start_track_lba = msf_to_log(ses_tocs[dev]->tracks[0].track_start_msf);
ses_tocs[dev]->lead_out_start_lba = msf_to_log(ses_tocs[dev]->lead_out_start_msf);
#if DEBUG
printk("Disk session %d, start track: %d, stop track: %d\n",
dev,
ses_tocs[dev]->start_track_lba,
ses_tocs[dev]->lead_out_start_lba);
#endif
}
sony_toc->lead_out_start_lba = msf_to_log(sony_toc->lead_out_start_msf);
sony_toc_read = 1;
}
}
......@@ -1328,16 +1687,17 @@ sony_get_toc(void)
* Search for a specific track in the table of contents.
*/
static int
find_track(int track)
find_track(int track,
int dev)
{
int i;
int num_tracks;
num_tracks = sony_toc->last_track_num + sony_toc->first_track_num + 1;
num_tracks = ses_tocs[dev]->last_track_num - ses_tocs[dev]->first_track_num + 1;
for (i = 0; i < num_tracks; i++)
{
if (sony_toc->tracks[i].track == track)
if (ses_tocs[dev]->tracks[i].track == track)
{
return i;
}
......@@ -1361,7 +1721,7 @@ read_subcode(void)
0,
(unsigned char *) last_sony_subcode,
&res_size);
if ((res_size < 2) || ((last_sony_subcode->exec_status[0] & 0x20) == 0x20))
if ((res_size < 2) || ((last_sony_subcode->exec_status[0] & 0xf0) == 0x20))
{
printk("Sony CDROM error 0x%2.2x (read_subcode)\n",
last_sony_subcode->exec_status[1]);
......@@ -1380,76 +1740,399 @@ read_subcode(void)
* (not BCD), so all the conversions are done.
*/
static int
sony_get_subchnl_info(long arg)
sony_get_subchnl_info(long arg,
int dev)
{
struct cdrom_subchnl schi;
/* Get attention stuff */
while (handle_sony_cd_attention())
;
sony_get_toc(dev);
if (ses_tocs[dev] == NULL)
{
return -EIO;
}
verify_area(VERIFY_READ, (char *) arg, sizeof(schi));
verify_area(VERIFY_WRITE, (char *) arg, sizeof(schi));
memcpy_fromfs(&schi, (char *) arg, sizeof(schi));
switch (sony_audio_status)
{
case CDROM_AUDIO_PLAY:
if (read_subcode() < 0)
{
return -EIO;
}
break;
case CDROM_AUDIO_PAUSED:
case CDROM_AUDIO_COMPLETED:
break;
case CDROM_AUDIO_NO_STATUS:
schi.cdsc_audiostatus = sony_audio_status;
memcpy_tofs((char *) arg, &schi, sizeof(schi));
return 0;
break;
case CDROM_AUDIO_INVALID:
case CDROM_AUDIO_ERROR:
default:
return -EIO;
}
schi.cdsc_audiostatus = sony_audio_status;
schi.cdsc_adr = last_sony_subcode->address;
schi.cdsc_ctrl = last_sony_subcode->control;
schi.cdsc_trk = bcd_to_int(last_sony_subcode->track_num);
schi.cdsc_ind = bcd_to_int(last_sony_subcode->index_num);
if (schi.cdsc_format == CDROM_MSF)
{
schi.cdsc_absaddr.msf.minute = bcd_to_int(last_sony_subcode->abs_msf[0]);
schi.cdsc_absaddr.msf.second = bcd_to_int(last_sony_subcode->abs_msf[1]);
schi.cdsc_absaddr.msf.frame = bcd_to_int(last_sony_subcode->abs_msf[2]);
schi.cdsc_reladdr.msf.minute = bcd_to_int(last_sony_subcode->rel_msf[0]);
schi.cdsc_reladdr.msf.second = bcd_to_int(last_sony_subcode->rel_msf[1]);
schi.cdsc_reladdr.msf.frame = bcd_to_int(last_sony_subcode->rel_msf[2]);
}
else if (schi.cdsc_format == CDROM_LBA)
{
schi.cdsc_absaddr.lba = msf_to_log(last_sony_subcode->abs_msf);
schi.cdsc_reladdr.lba = msf_to_log(last_sony_subcode->rel_msf);
}
memcpy_tofs((char *) arg, &schi, sizeof(schi));
return 0;
}
/* Get audio data from the drive. This is fairly complex because I
am looking for status and data at the same time, but if I get status
then I just look for data. I need to get the status immediately so
the switch from audio to data tracks will happen quickly. */
static void
read_audio_data(char *buffer,
unsigned char res_reg[],
int *res_size)
{
unsigned int retry_count;
int result_read;
res_reg[0] = 0;
res_reg[1] = 0;
*res_size = 0;
result_read = 0;
/* Wait for the drive to tell us we have something */
retry_count = jiffies + SONY_JIFFIES_TIMEOUT;
continue_read_audio_wait:
while ( (retry_count > jiffies)
&& !(is_data_ready())
&& !(is_result_ready() || result_read))
{
while (handle_sony_cd_attention())
;
sony_sleep();
}
if (!(is_data_ready()))
{
if (is_result_ready() && !result_read)
{
get_result(res_reg, res_size);
/* Read block status and continue waiting for data. */
if ((res_reg[0] & 0xf0) == 0x50)
{
result_read = 1;
goto continue_read_audio_wait;
}
/* Invalid data from the drive. Shut down the operation. */
else if ((res_reg[0] & 0xf0) != 0x20)
{
printk("CDU31A: Got result that should have been error: %d\n",
res_reg[0]);
res_reg[0] = 0x20;
res_reg[1] = SONY_BAD_DATA_ERR;
*res_size = 2;
}
abort_read();
}
else
{
#if DEBUG
printk("CDU31A timeout out %d\n", __LINE__);
#endif
res_reg[0] = 0x20;
res_reg[1] = SONY_TIMEOUT_OP_ERR;
*res_size = 2;
abort_read();
}
}
else
{
clear_data_ready();
/* If data block, then get 2340 bytes offset by 12. */
if (sony_raw_data_mode)
{
insb(sony_cd_read_reg, buffer + CD_XA_HEAD, CD_FRAMESIZE_XA);
}
else
{
/* Audio gets the whole 2352 bytes. */
insb(sony_cd_read_reg, buffer, CD_FRAMESIZE_RAW);
}
/* If I haven't already gotten the result, get it now. */
if (!result_read)
{
/* Wait for the drive to tell us we have something */
retry_count = jiffies + SONY_JIFFIES_TIMEOUT;
while ((retry_count > jiffies) && !(is_result_ready()))
{
while (handle_sony_cd_attention())
;
sony_sleep();
}
if (!is_result_ready())
{
#if DEBUG
printk("CDU31A timeout out %d\n", __LINE__);
#endif
res_reg[0] = 0x20;
res_reg[1] = SONY_TIMEOUT_OP_ERR;
*res_size = 2;
abort_read();
return;
}
else
{
get_result(res_reg, res_size);
}
}
if ((res_reg[0] & 0xf0) == 0x50)
{
if ( (res_reg[0] == SONY_NO_CIRC_ERR_BLK_STAT)
|| (res_reg[0] == SONY_NO_LECC_ERR_BLK_STAT)
|| (res_reg[0] == SONY_RECOV_LECC_ERR_BLK_STAT)
|| (res_reg[0] == SONY_NO_ERR_DETECTION_STAT))
{
/* Ok, nothing to do. */
}
else
{
printk("CDU31A: Data block error: 0x%x\n", res_reg[0]);
res_reg[0] = 0x20;
res_reg[1] = SONY_BAD_DATA_ERR;
*res_size = 2;
}
}
else if ((res_reg[0] & 0xf0) != 0x20)
{
/* The drive gave me bad status, I don't know what to do.
Reset the driver and return an error. */
printk("CDU31A: Invalid block status: 0x%x\n", res_reg[0]);
restart_on_error();
res_reg[0] = 0x20;
res_reg[1] = SONY_BAD_DATA_ERR;
*res_size = 2;
}
}
}
/* Perform a raw data read. This will automatically detect the
track type and read the proper data (audio or data). */
static int
read_audio(struct cdrom_read_audio *ra,
struct inode *inode)
{
struct cdrom_subchnl schi;
int retval;
unsigned char params[2];
unsigned char res_reg[12];
unsigned int res_size;
unsigned int cframe;
/*
* Make sure no one else is using the driver; wait for them
* to finish if it is so.
*/
cli();
while (sony_inuse)
{
interruptible_sleep_on(&sony_wait);
if (current->signal & ~current->blocked)
{
return -EAGAIN;
}
}
sony_inuse = 1;
has_cd_task = current;
sti();
/* Get attention stuff */
while (handle_sony_cd_attention())
;
if (!sony_spun_up)
{
scd_open (inode, NULL);
}
sony_get_toc();
if (!sony_toc_read)
/* Set the drive to do raw operations. */
params[0] = SONY_SD_DECODE_PARAM;
params[1] = 0x06 | sony_raw_data_mode;
do_sony_cd_cmd(SONY_SET_DRIVE_PARAM_CMD,
params,
2,
res_reg,
&res_size);
if ((res_size < 2) || ((res_reg[0] & 0xf0) == 0x20))
{
printk("CDU31A: Unable to set decode params: 0x%2.2x\n", res_reg[1]);
return -EIO;
}
verify_area(VERIFY_READ, (char *) arg, sizeof(schi));
verify_area(VERIFY_WRITE, (char *) arg, sizeof(schi));
/* From here down, we have to goto exit_read_audio instead of returning
because the drive parameters have to be set back to data before
return. */
memcpy_fromfs(&schi, (char *) arg, sizeof(schi));
switch (sony_audio_status)
retval = 0;
/* start_request clears out any readahead data, so it should be safe. */
if (start_request(ra->addr.lba, ra->nframes, 1, 0))
{
case CDROM_AUDIO_PLAY:
if (read_subcode() < 0)
retval = -EIO;
goto exit_read_audio;
}
/* For every requested frame. */
cframe = 0;
while (cframe < ra->nframes)
{
read_audio_data(readahead_buffer, res_reg, &res_size);
if ((res_reg[0] & 0xf0) == 0x20)
{
return -EIO;
}
break;
if (res_reg[1] == SONY_BAD_DATA_ERR)
{
printk("CDU31A: Data error on audio sector %d\n",
ra->addr.lba + cframe);
}
else if (res_reg[1] == SONY_ILL_TRACK_R_ERR)
{
/* Illegal track type, change track types and start over. */
sony_raw_data_mode = (sony_raw_data_mode) ? 0 : 1;
/* Set the drive mode. */
params[0] = SONY_SD_DECODE_PARAM;
params[1] = 0x06 | sony_raw_data_mode;
do_sony_cd_cmd(SONY_SET_DRIVE_PARAM_CMD,
params,
2,
res_reg,
&res_size);
if ((res_size < 2) || ((res_reg[0] & 0xf0) == 0x20))
{
printk("CDU31A: Unable to set decode params: 0x%2.2x\n", res_reg[1]);
retval = -EIO;
goto exit_read_audio;
}
case CDROM_AUDIO_PAUSED:
case CDROM_AUDIO_COMPLETED:
break;
/* Restart the request on the current frame. */
if (start_request(ra->addr.lba + cframe, ra->nframes - cframe, 1, 0))
{
retval = -EIO;
goto exit_read_audio;
}
case CDROM_AUDIO_NO_STATUS:
schi.cdsc_audiostatus = sony_audio_status;
memcpy_tofs((char *) arg, &schi, sizeof(schi));
return 0;
break;
/* Don't go back to the top because don't want to get into
and infinite loop. A lot of code gets duplicated, but
that's no big deal, I don't guess. */
read_audio_data(readahead_buffer, res_reg, &res_size);
if ((res_reg[0] & 0xf0) == 0x20)
{
if (res_reg[1] == SONY_BAD_DATA_ERR)
{
printk("CDU31A: Data error on audio sector %d\n",
ra->addr.lba + cframe);
}
else
{
printk("CDU31A: Error reading audio data on sector %d: 0x%x\n",
ra->addr.lba + cframe,
res_reg[1]);
retval = -EIO;
goto exit_read_audio;
}
}
else
{
memcpy_tofs((char *) (ra->buf + (CD_FRAMESIZE_RAW * cframe)),
(char *) readahead_buffer,
CD_FRAMESIZE_RAW);
}
}
else
{
printk("CDU31A: Error reading audio data on sector %d: 0x%x\n",
ra->addr.lba + cframe,
res_reg[1]);
retval = -EIO;
goto exit_read_audio;
}
}
else
{
memcpy_tofs((char *) (ra->buf + (CD_FRAMESIZE_RAW * cframe)),
(char *) readahead_buffer,
CD_FRAMESIZE_RAW);
}
case CDROM_AUDIO_INVALID:
case CDROM_AUDIO_ERROR:
default:
return -EIO;
cframe++;
}
schi.cdsc_audiostatus = sony_audio_status;
schi.cdsc_adr = last_sony_subcode->address;
schi.cdsc_ctrl = last_sony_subcode->control;
schi.cdsc_trk = bcd_to_int(last_sony_subcode->track_num);
schi.cdsc_ind = bcd_to_int(last_sony_subcode->index_num);
if (schi.cdsc_format == CDROM_MSF)
get_result(res_reg, &res_size);
if ((res_reg[0] & 0xf0) == 0x20)
{
schi.cdsc_absaddr.msf.minute = bcd_to_int(last_sony_subcode->abs_msf[0]);
schi.cdsc_absaddr.msf.second = bcd_to_int(last_sony_subcode->abs_msf[1]);
schi.cdsc_absaddr.msf.frame = bcd_to_int(last_sony_subcode->abs_msf[2]);
printk("CDU31A: Error return from audio read: 0x%x\n",
res_reg[1]);
retval = -EIO;
goto exit_read_audio;
}
schi.cdsc_reladdr.msf.minute = bcd_to_int(last_sony_subcode->rel_msf[0]);
schi.cdsc_reladdr.msf.second = bcd_to_int(last_sony_subcode->rel_msf[1]);
schi.cdsc_reladdr.msf.frame = bcd_to_int(last_sony_subcode->rel_msf[2]);
exit_read_audio:
/* Set the drive mode back to the proper one for the disk. */
params[0] = SONY_SD_DECODE_PARAM;
if (!sony_xa_mode)
{
params[1] = 0x0f;
}
else if (schi.cdsc_format == CDROM_LBA)
else
{
schi.cdsc_absaddr.lba = msf_to_log(last_sony_subcode->abs_msf);
schi.cdsc_reladdr.lba = msf_to_log(last_sony_subcode->rel_msf);
params[1] = 0x07;
}
memcpy_tofs((char *) arg, &schi, sizeof(schi));
return 0;
}
do_sony_cd_cmd(SONY_SET_DRIVE_PARAM_CMD,
params,
2,
res_reg,
&res_size);
if ((res_size < 2) || ((res_reg[0] & 0xf0) == 0x20))
{
printk("CDU31A: Unable to reset decode params: 0x%2.2x\n", res_reg[1]);
return -EIO;
}
has_cd_task = NULL;
sony_inuse = 0;
wake_up_interruptible(&sony_wait);
return(retval);
}
/*
* The big ugly ioctl handler.
......@@ -1461,7 +2144,7 @@ scd_ioctl(struct inode *inode,
unsigned long arg)
{
unsigned int dev;
unsigned char res_reg[2];
unsigned char res_reg[12];
unsigned int res_size;
unsigned char params[7];
int i;
......@@ -1471,8 +2154,8 @@ scd_ioctl(struct inode *inode,
{
return -EINVAL;
}
dev = MINOR(inode->i_rdev) >> 6;
if (dev != 0)
dev = MINOR(inode->i_rdev);
if (dev > MAX_TRACKS)
{
return -EINVAL;
}
......@@ -1481,7 +2164,7 @@ scd_ioctl(struct inode *inode,
{
case CDROMSTART: /* Spin up the drive */
do_sony_cd_cmd(SONY_SPIN_UP_CMD, NULL, 0, res_reg, &res_size);
if ((res_size < 2) || ((res_reg[0] & 0x20) == 0x20))
if ((res_size < 2) || ((res_reg[0] & 0xf0) == 0x20))
{
printk("Sony CDROM error 0x%2.2x (CDROMSTART)\n", res_reg[1]);
return -EIO;
......@@ -1498,7 +2181,7 @@ scd_ioctl(struct inode *inode,
*/
sony_audio_status = CDROM_AUDIO_NO_STATUS;
do_sony_cd_cmd(SONY_SPIN_DOWN_CMD, NULL, 0, res_reg, &res_size);
if ( ((res_size < 2) || ((res_reg[0] & 0x20) == 0x20))
if ( ((res_size < 2) || ((res_reg[0] & 0xf0) == 0x20))
&& (res_reg[1] != SONY_NOT_SPIN_ERR))
{
printk("Sony CDROM error 0x%2.2x (CDROMSTOP)\n", res_reg[1]);
......@@ -1510,7 +2193,7 @@ scd_ioctl(struct inode *inode,
case CDROMPAUSE: /* Pause the drive */
do_sony_cd_cmd(SONY_AUDIO_STOP_CMD, NULL, 0, res_reg, &res_size);
if ((res_size < 2) || ((res_reg[0] & 0x20) == 0x20))
if ((res_size < 2) || ((res_reg[0] & 0xf0) == 0x20))
{
printk("Sony CDROM error 0x%2.2x (CDROMPAUSE)\n", res_reg[1]);
return -EIO;
......@@ -1545,7 +2228,7 @@ scd_ioctl(struct inode *inode,
params[6] = final_pos_msf[2];
params[0] = 0x03;
do_sony_cd_cmd(SONY_AUDIO_PLAYBACK_CMD, params, 7, res_reg, &res_size);
if ((res_size < 2) || ((res_reg[0] & 0x20) == 0x20))
if ((res_size < 2) || ((res_reg[0] & 0xf0) == 0x20))
{
printk("Sony CDROM error 0x%2.2x (CDROMRESUME)\n", res_reg[1]);
return -EIO;
......@@ -1566,7 +2249,7 @@ scd_ioctl(struct inode *inode,
}
params[0] = 0x03;
do_sony_cd_cmd(SONY_AUDIO_PLAYBACK_CMD, params, 7, res_reg, &res_size);
if ((res_size < 2) || ((res_reg[0] & 0x20) == 0x20))
if ((res_size < 2) || ((res_reg[0] & 0xf0) == 0x20))
{
printk("Sony CDROM error 0x%2.2x (CDROMPLAYMSF)\n", res_reg[1]);
return -EIO;
......@@ -1585,16 +2268,16 @@ scd_ioctl(struct inode *inode,
struct cdrom_tochdr *hdr;
struct cdrom_tochdr loc_hdr;
sony_get_toc();
if (!sony_toc_read)
sony_get_toc(dev);
if (ses_tocs[dev] == NULL)
{
return -EIO;
}
hdr = (struct cdrom_tochdr *) arg;
verify_area(VERIFY_WRITE, hdr, sizeof(*hdr));
loc_hdr.cdth_trk0 = bcd_to_int(sony_toc->first_track_num);
loc_hdr.cdth_trk1 = bcd_to_int(sony_toc->last_track_num);
loc_hdr.cdth_trk0 = bcd_to_int(ses_tocs[dev]->first_track_num);
loc_hdr.cdth_trk1 = bcd_to_int(ses_tocs[dev]->last_track_num);
memcpy_tofs(hdr, &loc_hdr, sizeof(*hdr));
}
return 0;
......@@ -1607,8 +2290,8 @@ scd_ioctl(struct inode *inode,
int track_idx;
unsigned char *msf_val = NULL;
sony_get_toc();
if (!sony_toc_read)
sony_get_toc(dev);
if (ses_tocs[dev] == NULL)
{
return -EIO;
}
......@@ -1622,21 +2305,21 @@ scd_ioctl(struct inode *inode,
/* Lead out is handled separately since it is special. */
if (loc_entry.cdte_track == CDROM_LEADOUT)
{
loc_entry.cdte_adr = sony_toc->address2;
loc_entry.cdte_ctrl = sony_toc->control2;
msf_val = sony_toc->lead_out_start_msf;
loc_entry.cdte_adr = ses_tocs[dev]->address2;
loc_entry.cdte_ctrl = ses_tocs[dev]->control2;
msf_val = ses_tocs[dev]->lead_out_start_msf;
}
else
{
track_idx = find_track(int_to_bcd(loc_entry.cdte_track));
track_idx = find_track(int_to_bcd(loc_entry.cdte_track), dev);
if (track_idx < 0)
{
return -EINVAL;
}
loc_entry.cdte_adr = sony_toc->tracks[track_idx].address;
loc_entry.cdte_ctrl = sony_toc->tracks[track_idx].control;
msf_val = sony_toc->tracks[track_idx].track_start_msf;
loc_entry.cdte_adr = ses_tocs[dev]->tracks[track_idx].address;
loc_entry.cdte_ctrl = ses_tocs[dev]->tracks[track_idx].control;
msf_val = ses_tocs[dev]->tracks[track_idx].track_start_msf;
}
/* Logical buffer address or MSF format requested? */
......@@ -1660,8 +2343,8 @@ scd_ioctl(struct inode *inode,
struct cdrom_ti ti;
int track_idx;
sony_get_toc();
if (!sony_toc_read)
sony_get_toc(dev);
if (ses_tocs[dev] == NULL)
{
return -EIO;
}
......@@ -1669,39 +2352,39 @@ scd_ioctl(struct inode *inode,
verify_area(VERIFY_READ, (char *) arg, sizeof(ti));
memcpy_fromfs(&ti, (char *) arg, sizeof(ti));
if ( (ti.cdti_trk0 < sony_toc->first_track_num)
|| (ti.cdti_trk0 > sony_toc->last_track_num)
if ( (ti.cdti_trk0 < ses_tocs[dev]->first_track_num)
|| (ti.cdti_trk0 > ses_tocs[dev]->last_track_num)
|| (ti.cdti_trk1 < ti.cdti_trk0))
{
return -EINVAL;
}
track_idx = find_track(int_to_bcd(ti.cdti_trk0));
track_idx = find_track(int_to_bcd(ti.cdti_trk0), dev);
if (track_idx < 0)
{
return -EINVAL;
}
params[1] = sony_toc->tracks[track_idx].track_start_msf[0];
params[2] = sony_toc->tracks[track_idx].track_start_msf[1];
params[3] = sony_toc->tracks[track_idx].track_start_msf[2];
params[1] = ses_tocs[dev]->tracks[track_idx].track_start_msf[0];
params[2] = ses_tocs[dev]->tracks[track_idx].track_start_msf[1];
params[3] = ses_tocs[dev]->tracks[track_idx].track_start_msf[2];
/*
* If we want to stop after the last track, use the lead-out
* MSF to do that.
*/
if (ti.cdti_trk1 >= bcd_to_int(sony_toc->last_track_num))
if (ti.cdti_trk1 >= bcd_to_int(ses_tocs[dev]->last_track_num))
{
log_to_msf(msf_to_log(sony_toc->lead_out_start_msf)-1,
log_to_msf(msf_to_log(ses_tocs[dev]->lead_out_start_msf)-1,
&(params[4]));
}
else
{
track_idx = find_track(int_to_bcd(ti.cdti_trk1+1));
track_idx = find_track(int_to_bcd(ti.cdti_trk1+1), dev);
if (track_idx < 0)
{
return -EINVAL;
}
log_to_msf(msf_to_log(sony_toc->tracks[track_idx].track_start_msf)-1,
log_to_msf(msf_to_log(ses_tocs[dev]->tracks[track_idx].track_start_msf)-1,
&(params[4]));
}
params[0] = 0x03;
......@@ -1709,7 +2392,7 @@ scd_ioctl(struct inode *inode,
do_sony_cd_cmd(SONY_SPIN_UP_CMD, NULL, 0, res_reg, &res_size);
do_sony_cd_cmd(SONY_AUDIO_PLAYBACK_CMD, params, 7, res_reg, &res_size);
if ((res_size < 2) || ((res_reg[0] & 0x20) == 0x20))
if ((res_size < 2) || ((res_reg[0] & 0xf0) == 0x20))
{
printk("Params: %x %x %x %x %x %x %x\n", params[0], params[1],
params[2], params[3], params[4], params[5], params[6]);
......@@ -1726,7 +2409,7 @@ scd_ioctl(struct inode *inode,
}
case CDROMSUBCHNL: /* Get subchannel info */
return sony_get_subchnl_info(arg);
return sony_get_subchnl_info(arg, dev);
case CDROMVOLCTRL: /* Volume control. What volume does this change, anyway? */
{
......@@ -1739,7 +2422,7 @@ scd_ioctl(struct inode *inode,
params[1] = volctrl.channel0;
params[2] = volctrl.channel1;
do_sony_cd_cmd(SONY_SET_DRIVE_PARAM_CMD, params, 3, res_reg, &res_size);
if ((res_size < 2) || ((res_reg[0] & 0x20) == 0x20))
if ((res_size < 2) || ((res_reg[0] & 0xf0) == 0x20))
{
printk("Sony CDROM error 0x%2.2x (CDROMVOLCTRL)\n", res_reg[1]);
return -EIO;
......@@ -1753,7 +2436,7 @@ scd_ioctl(struct inode *inode,
sony_audio_status = CDROM_AUDIO_INVALID;
do_sony_cd_cmd(SONY_EJECT_CMD, NULL, 0, res_reg, &res_size);
if ((res_size < 2) || ((res_reg[0] & 0x20) == 0x20))
if ((res_size < 2) || ((res_reg[0] & 0xf0) == 0x20))
{
printk("Sony CDROM error 0x%2.2x (CDROMEJECT)\n", res_reg[1]);
return -EIO;
......@@ -1761,6 +2444,64 @@ scd_ioctl(struct inode *inode,
return 0;
break;
case CDROMREADAUDIO: /* Read 2352 byte audio tracks and 2340 byte
raw data tracks. */
{
struct cdrom_read_audio ra;
sony_get_toc(dev);
if (ses_tocs[dev] == NULL)
{
return -EIO;
}
verify_area(VERIFY_READ, (char *) arg, sizeof(ra));
memcpy_fromfs(&ra, (char *) arg, sizeof(ra));
verify_area(VERIFY_WRITE, ra.buf, CD_FRAMESIZE_RAW * ra.nframes);
if (ra.addr_format == CDROM_LBA)
{
if ( (ra.addr.lba >= ses_tocs[dev]->lead_out_start_lba)
|| (ra.addr.lba + ra.nframes >= ses_tocs[dev]->lead_out_start_lba))
{
return -EINVAL;
}
}
else if (ra.addr_format == CDROM_MSF)
{
if ( (ra.addr.msf.minute >= 75)
|| (ra.addr.msf.second >= 60)
|| (ra.addr.msf.frame >= 75))
{
return -EINVAL;
}
ra.addr.lba = ( (ra.addr.msf.minute * 4500)
+ (ra.addr.msf.second * 75)
+ ra.addr.msf.frame);
if ( (ra.addr.lba >= ses_tocs[dev]->lead_out_start_lba)
|| (ra.addr.lba + ra.nframes >= ses_tocs[dev]->lead_out_start_lba))
{
return -EINVAL;
}
/* I know, this can go negative on an unsigned. However,
the first thing done to the data is to add this value,
so this should compensate and allow direct msf access. */
ra.addr.lba -= LOG_START_OFFSET;
}
else
{
return -EINVAL;
}
return(read_audio(&ra, inode));
}
return 0;
break;
default:
return -EINVAL;
}
......@@ -1775,12 +2516,14 @@ static int
scd_open(struct inode *inode,
struct file *filp)
{
unsigned char res_reg[2];
unsigned char res_reg[12];
unsigned int res_size;
int num_spin_ups;
unsigned char params[2];
int dev;
if (filp->f_mode & 2)
if ((filp) && filp->f_mode & 2)
return -EROFS;
if (!sony_spun_up)
......@@ -1825,19 +2568,56 @@ scd_open(struct inode *inode,
return -EIO;
}
sony_get_toc();
if (!sony_toc_read)
dev = MINOR(inode->i_rdev);
sony_get_toc(dev);
if (ses_tocs[dev] == NULL)
{
do_sony_cd_cmd(SONY_SPIN_DOWN_CMD, NULL, 0, res_reg, &res_size);
return -EIO;
}
/* For XA on the CDU31A only, we have to do special reads.
The CDU33A handles XA automagically. */
if ( (ses_tocs[dev]->disk_type == SONY_XA_DISK_TYPE)
&& (!is_double_speed))
{
params[0] = SONY_SD_DECODE_PARAM;
params[1] = 0x07;
do_sony_cd_cmd(SONY_SET_DRIVE_PARAM_CMD,
params,
2,
res_reg,
&res_size);
if ((res_size < 2) || ((res_reg[0] & 0xf0) == 0x20))
{
printk("CDU31A: Unable to set XA params: 0x%2.2x\n", res_reg[1]);
}
sony_xa_mode = 1;
}
/* A non-XA disk. Set the parms back if necessary. */
else if (sony_xa_mode)
{
params[0] = SONY_SD_DECODE_PARAM;
params[1] = 0x0f;
do_sony_cd_cmd(SONY_SET_DRIVE_PARAM_CMD,
params,
2,
res_reg,
&res_size);
if ((res_size < 2) || ((res_reg[0] & 0xf0) == 0x20))
{
printk("CDU31A: Unable to reset XA params: 0x%2.2x\n", res_reg[1]);
}
sony_xa_mode = 0;
}
sony_spun_up = 1;
}
drive_spinning:
if (inode)
/* If filp is not NULL (standard open), try a disk change. */
if (filp)
{
check_disk_change(inode->i_rdev);
}
......@@ -1856,7 +2636,7 @@ static void
scd_release(struct inode *inode,
struct file *filp)
{
unsigned char res_reg[2];
unsigned char res_reg[12];
unsigned int res_size;
......@@ -1885,20 +2665,16 @@ static struct file_operations scd_fops = {
scd_open, /* open */
scd_release, /* release */
NULL, /* fsync */
NULL, /* fasync */
scd_disk_change, /* media_change */
NULL /* revalidate */
NULL, /* fasync */
scd_disk_change, /* media_change */
NULL /* revalidate */
};
/* The different types of disc loading mechanisms supported */
static char *load_mech[] = { "caddy", "tray", "pop-up", "unknown" };
/* Read-ahead buffer sizes for different drives. These are just arbitrary
values, I don't know what is really optimum. */
static unsigned int mem_size[] = { 16384, 16384, 16384, 2048 };
void
static void
get_drive_configuration(unsigned short base_io,
unsigned char res_reg[],
unsigned int *res_size)
......@@ -1937,12 +2713,14 @@ get_drive_configuration(unsigned short base_io,
sony_sleep();
}
#if 0
/* If attention is never seen probably not a CDU31a present */
if (!is_attention())
{
res_reg[0] = 0x20;
return;
}
#endif
/*
* Get the drive configuration.
......@@ -1959,6 +2737,22 @@ get_drive_configuration(unsigned short base_io,
res_reg[0] = 0x20;
}
/*
* Set up base I/O and interrupts, called from main.c.
*/
void
cdu31a_setup(char *strings,
int *ints)
{
if (ints[0] > 0)
{
sony_cd_base_io = ints[1];
}
if (ints[0] > 1)
{
irq_used = ints[2];
}
}
static int cdu31a_block_size;
......@@ -1985,143 +2779,141 @@ cdu31a_init(unsigned long mem_start, unsigned long mem_end)
outb(0xbc, 0x9a01);
outb(0xe2, 0x9a01);
i = 0;
drive_found = 0;
while ( (cdu31a_addresses[i].base != 0)
&& (!drive_found))
/* Setting the base I/O address to 0xffff will disable it. */
if (sony_cd_base_io == 0xffff)
{
}
else if (sony_cd_base_io != 0)
{
if (check_region(cdu31a_addresses[i].base, 4)) {
i++;
continue;
get_drive_configuration(sony_cd_base_io,
drive_config.exec_status,
&res_size);
if ((res_size > 2) && ((drive_config.exec_status[0] & 0xf0) == 0x00))
{
drive_found = 1;
}
get_drive_configuration(cdu31a_addresses[i].base,
drive_config.exec_status,
&res_size);
if ((res_size > 2) && ((drive_config.exec_status[0] & 0x20) == 0x00))
}
else
{
i = 0;
while ( (cdu31a_addresses[i].base != 0)
&& (!drive_found))
{
drive_found = 1;
snarf_region(cdu31a_addresses[i].base, 4);
if (register_blkdev(MAJOR_NR,"cdu31a",&scd_fops))
{
printk("Unable to get major %d for CDU-31a\n", MAJOR_NR);
return mem_start;
}
if (SONY_HWC_DOUBLE_SPEED(drive_config))
{
is_double_speed = 1;
if (check_region(cdu31a_addresses[i].base, 4)) {
i++;
continue;
}
tmp_irq = cdu31a_addresses[i].int_num;
if (tmp_irq < 0)
{
autoirq_setup(0);
enable_interrupts();
reset_drive();
tmp_irq = autoirq_report(10);
disable_interrupts();
set_drive_params();
irq_used = tmp_irq;
get_drive_configuration(cdu31a_addresses[i].base,
drive_config.exec_status,
&res_size);
if ((res_size > 2) && ((drive_config.exec_status[0] & 0xf0) == 0x00))
{
drive_found = 1;
irq_used = cdu31a_addresses[i].int_num;
}
else
{
set_drive_params();
irq_used = tmp_irq;
}
if (irq_used > 0)
{
if (request_irq(irq_used, cdu31a_interrupt, SA_INTERRUPT, "cdu31a"))
{
irq_used = 0;
printk("Unable to grab IRQ%d for the CDU31A driver\n", irq_used);
}
}
dma_channel = cdu31a_addresses[i].dma_num;
if (dma_channel > 0)
else
{
if (request_dma(dma_channel,"cdu31a"))
{
dma_channel = -1;
printk("Unable to grab DMA%d for the CDU31A driver\n",
dma_channel);
}
}
i++;
}
}
}
sony_buffer_size = mem_size[SONY_HWC_GET_BUF_MEM_SIZE(drive_config)];
sony_buffer_sectors = sony_buffer_size / 2048;
if (drive_found)
{
snarf_region(sony_cd_base_io, 4);
if (register_blkdev(MAJOR_NR,"cdu31a",&scd_fops))
{
printk("Unable to get major %d for CDU-31a\n", MAJOR_NR);
return mem_start;
}
printk("Sony I/F CDROM : %8.8s %16.16s %8.8s with %s load mechanism\n",
drive_config.vendor_id,
drive_config.product_id,
drive_config.product_rev_level,
load_mech[SONY_HWC_GET_LOAD_MECH(drive_config)]);
printk(" using %d byte buffer", sony_buffer_size);
if (SONY_HWC_AUDIO_PLAYBACK(drive_config))
{
printk(", audio");
}
if (SONY_HWC_EJECT(drive_config))
{
printk(", eject");
}
if (SONY_HWC_LED_SUPPORT(drive_config))
{
printk(", LED");
}
if (SONY_HWC_ELECTRIC_VOLUME(drive_config))
{
printk(", elec. Vol");
}
if (SONY_HWC_ELECTRIC_VOLUME_CTL(drive_config))
{
printk(", sep. Vol");
}
if (is_double_speed)
{
printk(", double speed");
}
if (irq_used > 0)
{
printk(", irq %d", irq_used);
}
if (dma_channel > 0)
if (SONY_HWC_DOUBLE_SPEED(drive_config))
{
is_double_speed = 1;
}
/* A negative irq_used will attempt an autoirq. */
if (irq_used < 0)
{
autoirq_setup(0);
enable_interrupts();
reset_drive();
tmp_irq = autoirq_report(10);
disable_interrupts();
set_drive_params();
irq_used = tmp_irq;
}
else
{
set_drive_params();
}
if (irq_used > 0)
{
if (request_irq(irq_used, cdu31a_interrupt, SA_INTERRUPT, "cdu31a"))
{
printk(", drq %d", dma_channel);
irq_used = 0;
printk("Unable to grab IRQ%d for the CDU31A driver\n", irq_used);
}
printk("\n");
blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
read_ahead[MAJOR_NR] = 32; /* 32 sector (16kB) read-ahead */
cdu31a_block_size = 2048; /* 2kB block size */
/* use 'mount -o block=2048' */
blksize_size[MAJOR_NR] = &cdu31a_block_size;
sony_toc = (struct s_sony_toc *) mem_start;
mem_start += sizeof(*sony_toc);
last_sony_subcode = (struct s_sony_subcode *) mem_start;
mem_start += sizeof(*last_sony_subcode);
/* If memory will not fit into the current 64KB block, align it
so the block will not cross a 64KB boundary. This is
because DMA cannot cross 64KB boundaries. */
if ( (dma_channel > 0)
&& ( ((mem_start) & (~0xffff))
!= (((mem_start) + sony_buffer_size) & (~0xffff))))
{
mem_start = (((int)mem_start) + 0x10000) & (~0xffff);
}
sony_buffer = (unsigned char *) mem_start;
mem_start += sony_buffer_size;
}
printk("Sony I/F CDROM : %8.8s %16.16s %8.8s\n",
drive_config.vendor_id,
drive_config.product_id,
drive_config.product_rev_level);
printk(" Capabilities: %s",
load_mech[SONY_HWC_GET_LOAD_MECH(drive_config)]);
if (SONY_HWC_AUDIO_PLAYBACK(drive_config))
{
printk(", audio");
}
if (SONY_HWC_EJECT(drive_config))
{
printk(", eject");
}
if (SONY_HWC_LED_SUPPORT(drive_config))
{
printk(", LED");
}
if (SONY_HWC_ELECTRIC_VOLUME(drive_config))
{
printk(", elec. Vol");
}
if (SONY_HWC_ELECTRIC_VOLUME_CTL(drive_config))
{
printk(", sep. Vol");
}
if (is_double_speed)
{
printk(", double speed");
}
if (irq_used > 0)
{
printk(", irq %d", irq_used);
}
printk("\n");
blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
read_ahead[MAJOR_NR] = CDU31A_READAHEAD;
cdu31a_block_size = 1024; /* 1kB default block size */
/* use 'mount -o block=2048' */
blksize_size[MAJOR_NR] = &cdu31a_block_size;
last_sony_subcode = (struct s_sony_subcode *) mem_start;
mem_start += sizeof(*last_sony_subcode);
readahead_buffer = (unsigned char *) mem_start;
mem_start += CD_FRAMESIZE_RAW;
}
i++;
for (i=0; i<MAX_TRACKS; i++)
{
disk_changed[i] = 1;
}
return mem_start;
}
Sat Oct 29 18:17:34 1994 Theodore Y. Ts'o (tytso@rt-11)
* serial.c (rs_ioctl, get_lsr_info): Added patch suggested by Arne
Riiber so that user mode programs can tell when the
transmitter shift register is empty.
Thu Oct 27 23:14:29 1994 Theodore Y. Ts'o (tytso@rt-11)
* tty_ioctl.c (wait_until_sent): Added debugging printk statements
(under the #ifdef TTY_DEBUG_WAIT_UNTL_SENT)
* serial.c (rs_interrupt, rs_interrupt_single, receive_chars,
change_speed, rs_close): rs_close now disables receiver
interrupts when closing the serial port. This allows the
serial port to close quickly when Linux and a modem (or a
mouse) are engaged in an echo war; when closing the serial
port, we now first stop listening to incoming characters,
and *then* wait for the transmit buffer to drain.
In order to make this change, the info->read_status_mask
is now used to control what bits of the line status
register are looked at in the interrupt routine in all
cases; previously it was only used in receive_chars to
select a few of the status bits.
Mon Oct 24 23:36:21 1994 Theodore Y. Ts'o (tytso@rt-11)
* serial.c (rs_close): Add a timeout to the transmitter flush
......
......@@ -373,27 +373,27 @@ static _INLINE_ void receive_chars(struct async_struct *info,
do {
ch = serial_inp(info, UART_RX);
if (*status & info->ignore_status_mask)
continue;
goto ignore_char;
if (tty->flip.count >= TTY_FLIPBUF_SIZE)
break;
tty->flip.count++;
if (*status & info->read_status_mask) {
if (*status & (UART_LSR_BI)) {
*tty->flip.flag_buf_ptr++ = TTY_BREAK;
if (info->flags & ASYNC_SAK)
do_SAK(tty);
} else if (*status & UART_LSR_PE)
*tty->flip.flag_buf_ptr++ = TTY_PARITY;
else if (*status & UART_LSR_FE)
*tty->flip.flag_buf_ptr++ = TTY_FRAME;
else if (*status & UART_LSR_OE)
*tty->flip.flag_buf_ptr++ = TTY_OVERRUN;
else
*tty->flip.flag_buf_ptr++ = 0;
} else
if (*status & (UART_LSR_BI)) {
printk("handling break....");
*tty->flip.flag_buf_ptr++ = TTY_BREAK;
if (info->flags & ASYNC_SAK)
do_SAK(tty);
} else if (*status & UART_LSR_PE)
*tty->flip.flag_buf_ptr++ = TTY_PARITY;
else if (*status & UART_LSR_FE)
*tty->flip.flag_buf_ptr++ = TTY_FRAME;
else if (*status & UART_LSR_OE)
*tty->flip.flag_buf_ptr++ = TTY_OVERRUN;
else
*tty->flip.flag_buf_ptr++ = 0;
*tty->flip.char_buf_ptr++ = ch;
} while ((*status = serial_inp(info, UART_LSR)) & UART_LSR_DR);
ignore_char:
*status = serial_inp(info, UART_LSR) & info->read_status_mask;
} while (*status & UART_LSR_DR);
queue_task_irq_off(&tty->flip.tqueue, &tq_timer);
#ifdef SERIAL_DEBUG_INTR
printk("DR...");
......@@ -525,7 +525,10 @@ static void rs_interrupt(int irq)
info->last_active = jiffies;
status = serial_inp(info, UART_LSR);
status = serial_inp(info, UART_LSR) & info->read_status_mask;
#ifdef SERIAL_DEBUG_INTR
printk("status = %x...", status);
#endif
if (status & UART_LSR_DR)
receive_chars(info, &status);
check_modem_status(info);
......@@ -545,6 +548,9 @@ static void rs_interrupt(int irq)
continue;
}
} while (end_mark != info);
#ifdef SERIAL_DEBUG_INTR
printk("end.\n");
#endif
}
/*
......@@ -565,7 +571,10 @@ static void rs_interrupt_single(int irq)
return;
do {
status = serial_inp(info, UART_LSR);
status = serial_inp(info, UART_LSR) & info->read_status_mask;
#ifdef SERIAL_DEBUG_INTR
printk("status = %x...", status);
#endif
if (status & UART_LSR_DR)
receive_chars(info, &status);
check_modem_status(info);
......@@ -579,6 +588,9 @@ static void rs_interrupt_single(int irq)
}
} while (!(serial_in(info, UART_IIR) & UART_IIR_NO_INT));
info->last_active = jiffies;
#ifdef SERIAL_DEBUG_INTR
printk("end.\n");
#endif
}
#else /* CONFIG_SERIAL_NEW_ISR */
......@@ -606,7 +618,7 @@ static void rs_interrupt(int irq)
goto next;
serial_outp(info, UART_IER, 0);
status = serial_inp(info, UART_LSR);
status = serial_inp(info, UART_LSR) & info->read_status_mask;
if (status & UART_LSR_DR) {
receive_chars(info, &status);
done = 0;
......@@ -658,7 +670,7 @@ static void rs_interrupt_single(int irq)
return;
serial_outp(info, UART_IER, 0);
status = serial_inp(info, UART_LSR);
status = serial_inp(info, UART_LSR) & info->read_status_mask;
if (status & UART_LSR_DR)
receive_chars(info, &status);
check_modem_status(info);
......@@ -1140,23 +1152,28 @@ static void change_speed(struct async_struct *info)
/*
* Set up parity check flag
*/
info->read_status_mask = UART_LSR_OE;
info->read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR;
if (I_INPCK(info->tty))
info->read_status_mask |= UART_LSR_FE | UART_LSR_PE;
if (I_BRKINT(info->tty) || I_PARMRK(info->tty))
info->read_status_mask |= UART_LSR_BI;
info->ignore_status_mask = 0;
if (I_IGNPAR(info->tty))
if (I_IGNPAR(info->tty)) {
info->ignore_status_mask |= UART_LSR_PE | UART_LSR_FE;
info->read_status_mask |= UART_LSR_PE | UART_LSR_FE;
}
if (I_IGNBRK(info->tty)) {
info->ignore_status_mask |= UART_LSR_BI;
info->read_status_mask |= UART_LSR_BI;
/*
* If we're ignore parity and break indicators, ignore
* overruns too. (For real raw support).
*/
if (I_IGNPAR(info->tty))
if (I_IGNPAR(info->tty)) {
info->ignore_status_mask |= UART_LSR_OE;
info->read_status_mask |= UART_LSR_OE;
}
}
cli();
......@@ -1462,6 +1479,31 @@ static int set_serial_info(struct async_struct * info,
return retval;
}
/*
* get_lsr_info - get line status register info
*
* Purpose: Let user call ioctl() to get info when the UART physically
* is emptied. On bus types like RS485, the transmitter must
* release the bus after transmitting. This must be done when
* the transmit shift register is empty, not be done when the
* transmit holding register is empty. This functionality
* allows RS485 driver to be written in user space.
*/
static int get_lsr_info(struct async_struct * info, unsigned int *value)
{
unsigned char status;
unsigned int result;
cli();
status = serial_in(info, UART_LSR);
sti();
result = ((status & UART_LSR_TEMT) ? TIOCSER_TEMT : 0);
put_fs_long(result,(unsigned long *) value);
return 0;
}
static int get_modem_info(struct async_struct * info, unsigned int *value)
{
unsigned char control, status;
......@@ -1678,6 +1720,14 @@ static int rs_ioctl(struct tty_struct *tty, struct file * file,
put_fs_long(rs_wild_int_mask, (unsigned long *) arg);
return 0;
case TIOCSERGETLSR: /* Get line status register */
error = verify_area(VERIFY_WRITE, (void *) arg,
sizeof(unsigned int));
if (error)
return error;
else
return get_lsr_info(info, (unsigned int *) arg);
case TIOCSERSWILD:
if (!suser())
return -EPERM;
......@@ -1780,6 +1830,15 @@ static void rs_close(struct tty_struct *tty, struct file * filp)
info->normal_termios = *tty->termios;
if (info->flags & ASYNC_CALLOUT_ACTIVE)
info->callout_termios = *tty->termios;
/*
* At this point we stop accepting input. To do this, we
* disable the receive line status interrupts, and tell the
* interrut driver to stop checking the data ready bit in the
* line status register.
*/
info->IER &= ~UART_IER_RLSI;
serial_out(info, UART_IER, info->IER);
info->read_status_mask &= ~UART_LSR_DR;
if (info->flags & ASYNC_INITIALIZED) {
wait_until_sent(tty, 3000); /* 30 seconds timeout */
/*
......
......@@ -24,6 +24,8 @@
#include <asm/segment.h>
#include <asm/system.h>
#undef TTY_DEBUG_WAIT_UNTIL_SENT
#undef DEBUG
#ifdef DEBUG
# define PRINTK(x) printk (x)
......@@ -42,6 +44,9 @@ void wait_until_sent(struct tty_struct * tty, int timeout)
{
struct wait_queue wait = { current, NULL };
#ifdef TTY_DEBUG_WAIT_UNTIL_SENT
printk("%s wait until sent...\n", tty_name(tty));
#endif
if (!tty->driver.chars_in_buffer ||
!tty->driver.chars_in_buffer(tty))
return;
......@@ -52,6 +57,9 @@ void wait_until_sent(struct tty_struct * tty, int timeout)
else
current->timeout = (unsigned) -1;
do {
#ifdef TTY_DEBUG_WAIT_UNTIL_SENT
printk("waiting %s...(%d)\n", tty_name(tty), tty->driver.chars_in_buffer(tty));
#endif
current->state = TASK_INTERRUPTIBLE;
if (current->signal & ~current->blocked)
break;
......
......@@ -62,11 +62,21 @@ SCSI_OBJS := $(SCSI_OBJS) aha1740.o
SCSI_SRCS := $(SCSI_SRCS) aha1740.c
endif
ifdef CONFIG_SCSI_AHA274X
SCSI_OBJS := $(SCSI_OBJS) aha274x.o
SCSI_SRCS := $(SCSI_SRCS) aha274x.c
endif
ifdef CONFIG_SCSI_BUSLOGIC
SCSI_OBJS := $(SCSI_OBJS) buslogic.o
SCSI_SRCS := $(SCSI_SRCS) buslogic.c
endif
ifdef CONFIG_SCSI_U14_34F
SCSI_OBJS := $(SCSI_OBJS) u14-34f.o
SCSI_SRCS := $(SCSI_SRCS) u14-34f.c
endif
ifdef CONFIG_SCSI_DEBUG
SCSI_OBJS := $(SCSI_OBJS) scsi_debug.o
SCSI_SRCS := $(SCSI_SRCS) scsi_debug.c
......@@ -132,6 +142,11 @@ scsi.a: $(SCSI_OBJS)
aha152x.o: aha152x.c
$(CC) $(CFLAGS) $(AHA152X) -c aha152x.c
aic7770: aic7770.c
$(CC) $(CFLAGS) -o $@ aic7770.c
aha274x_seq.h: aic7770 aha274x.seq
./aic7770 -o $@ aha274x.seq
seagate.o: seagate.c
$(CC) $(CFLAGS) -DARBITRATE -DSLOW_HANDSHAKE -DFAST32 -c seagate.c
......
@(#)README 1.15 94/10/29 jda
AHA274x/284x DRIVER
*** THIS SHOULD BE CONSIDERED BETA SOFTWARE ***
BACKGROUND & LIMITATIONS
For various reasons, we ended up with one of these cards under the
impression that support was soon forthcoming. In mid-May, I asked
Scott Ferris (the official person who's supposed to be writing this
driver) what documentation he used, _finally_ got it from Adaptec,
and started writing this driver. It is now at what I would consider
a stable state - it runs our news server and is battered by SCSI
requests 24 hours a day without dying. There are a few devices it
reportedly doesn't like working with - those are being sorted out. Due
to some unexpected equipment loans, I am able to support this at least
for the time being.
YOU MUST HAVE THE BIOS ENABLED OR THIS WILL NOT WORK. The BIOS extracts
some configuration information that I cannot get to portably yet, as
well as provides some self-tests which this driver does not attempt to
duplicate.
Scott's driver development is stalled for now, and after discussions
with him, this is now officially out of "pre-alpha" status and into
beta until the remaining device problems can be resolved. The latest
patches can be obtained via anonymous ftp from ftp.cpsc.ucalgary.ca in
/pub/systems/linux/aha274x.
It supports both EISA 274x and VL-bus 284x, either single or twin-bus cards
(but not the second SCSI bus of twin cards - see aha274x.c), and supports
disconnection, synchronous SCSI, and scatter-gather. Unlike previous
versions, abort() and reset() are now implemented, and both hosts.c and
aha274x.c should give a clean compile. Code is now present to detect parity
errors, but has not been tested.
I wrote this using a 1.0.9 kernel. Unfortunately, I'm getting tired of
#ifdef'ing everything to handle two or three different evolutionary steps
in the SCSI kernel code, so I've upgraded my system to 1.1.49, and will
only leave in code to support versions from about 1.1.45 onward.
Thanks to patches supplied by Mark Olson <molson@tricord.com>, this driver
will now work with the 284x series (the VL-bus version of this card). The
294x (PCI-bus) is being worked on, and initial support for it will be ready
soon.
Under protest, this driver is subject to the GPL - see the file
COPYING for details.
Thanks to the following people for bug fixes/code improvements (also
thanks to the people who have sent me feedback):
"David F. Carlson" <dave@ee.rochester.edu>
Jimen Ching <jiching@wiliki.eng.hawaii.edu>
mday@artisoft.com (Matt Day)
"Dean W. Gehnert" <deang@ims.com>
Darcy Grant <darcy@cpsc.ucalgary.ca>
Alan Hourihane <alanh@fairlite.demon.co.uk>
isely@fncrd8.fnal.gov (Mike Isely)
Mike Jerger <jerger@ux1.cso.uiuc.edu>
tm@netcom.com (Toshiyasu Morita)
neal@interact.org (Neal Norwitz)
Mark Olson <molson@tricord.com>
map@europa.ecn.uoknor.edu (Michael A. Parker)
Thomas Scheunemann <thomas@dagobert.uni-duisburg.de>
Special thanks to Drew Eckhardt <drew@kinglear.cs.Colorado.EDU> for
fielding my questions about synchronous negotiation. Steffen Moeller
<smoe0024@rz.uni-hildesheim.de> sent me installation instructions which
were previously included in this README.
David Pirie <pirie@cpsc.ucalgary.ca> was nice enough to loan me his
2842 card for a week so I could track down one bug, as well as his
CD-ROM drive later, and also thanks to Doug Fortune at Riley's Data Share
in Calgary, who arranged a long-term loan of a 2842 board for further work.
Many thanks to the fearless prerelease testers! Dean Gehnert has been
building Slackware boot disks for the driver, which are available from
ftp.cpsc.ucalgary.ca in /pub/systems/linux/aha274x/slackware_boot.
Carl Riches <cgr@poplar1.cfr.washington.edu> has set up a mailing list
for aic7xxx driver development. To subscribe, send a message to
aic7770-list@poplar1.cfr.washington.edu with a message body of:
subscribe AIC7770-LIST <your name here, without the angle brackets>
Please direct questions and discussions to that list instead of me. When
sending bug reports, please include a description of your hardware, the
release numbers displayed by the driver at boot time, and as accurate a
facsimilie of any error message you're mailing about.
John Aycock
aycock@cpsc.ucalgary.ca
/*
* @(#)aha274x.c 1.29 94/10/29 jda
*
* Adaptec 274x device driver for Linux.
* Copyright (c) 1994 The University of Calgary Department of Computer Science.
*
* 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 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* Sources include the Adaptec 1740 driver (aha1740.c), the
* Ultrastor 24F driver (ultrastor.c), various Linux kernel
* source, the Adaptec EISA config file (!adp7771.cfg), the
* Adaptec AHA-2740A Series User's Guide, the Linux Kernel
* Hacker's Guide, Writing a SCSI Device Driver for Linux,
* the Adaptec 1542 driver (aha1542.c), the Adaptec EISA
* overlay file (adp7770.ovl), the Adaptec AHA-2740 Series
* Technical Reference Manual, the Adaptec AIC-7770 Data
* Book, the ANSI SCSI specification, the ANSI SCSI-2
* specification (draft 10c), ...
*
* On a twin-bus adapter card, channel B is ignored. Rationale:
* it would greatly complicate the sequencer and host driver code,
* and both busses are multiplexed on to the EISA bus anyway. So
* I don't really see any technical advantage to supporting both.
*
* As well, multiple adapter card using the same IRQ level are
* not supported. It doesn't make sense to configure the cards
* this way from a performance standpoint. Not to mention that
* the kernel would have to support two devices per registered IRQ.
*/
#include <stdarg.h>
#include <asm/io.h>
#include <linux/string.h>
#include <linux/kernel.h>
#include <linux/ioport.h>
#include <linux/delay.h>
#include "../block/blk.h"
#include "sd.h"
#include "scsi.h"
#include "hosts.h"
#include "aha274x.h"
/*
* There should be a specific return value for this in scsi.h, but
* it seems that most drivers ignore it.
*/
#define DID_UNDERFLOW DID_ERROR
/* EISA stuff */
#define MINEISA 1
#define MAXEISA 15
#define SLOTBASE(x) ((x) << 12)
#define MAXIRQ 15
/* AIC-7770 offset definitions */
#define O_MINREG(x) ((x) + 0xc00) /* i/o range to reserve */
#define O_MAXREG(x) ((x) + 0xcbf)
#define O_SCSISEQ(x) ((x) + 0xc00) /* scsi sequence control */
#define O_SCSISIGI(x) ((x) + 0xc03) /* scsi control signal read */
#define O_SCSISIGO(x) ((x) + 0xc03) /* scsi control signal write */
#define O_SCSIID(x) ((x) + 0xc05) /* scsi id */
#define O_SSTAT0(x) ((x) + 0xc0b) /* scsi status register 0 */
#define O_CLRSINT1(x) ((x) + 0xc0c) /* clear scsi interrupt 1 */
#define O_SSTAT1(x) ((x) + 0xc0c) /* scsi status register 1 */
#define O_SELID(x) ((x) + 0xc19) /* [re]selection id */
#define O_SBLKCTL(x) ((x) + 0xc1f) /* scsi block control */
#define O_SEQCTL(x) ((x) + 0xc60) /* sequencer control */
#define O_SEQRAM(x) ((x) + 0xc61) /* sequencer ram data */
#define O_SEQADDR(x) ((x) + 0xc62) /* sequencer address (W) */
#define O_BIDx(x) ((x) + 0xc80) /* board id */
#define O_BCTL(x) ((x) + 0xc84) /* board control */
#define O_HCNTRL(x) ((x) + 0xc87) /* host control */
#define O_SCBPTR(x) ((x) + 0xc90) /* scb pointer */
#define O_INTSTAT(x) ((x) + 0xc91) /* interrupt status */
#define O_ERROR(x) ((x) + 0xc92) /* hard error */
#define O_CLRINT(x) ((x) + 0xc92) /* clear interrupt status */
#define O_SCBCNT(x) ((x) + 0xc9a) /* scb auto increment */
#define O_QINFIFO(x) ((x) + 0xc9b) /* queue in fifo */
#define O_QINCNT(x) ((x) + 0xc9c) /* queue in count */
#define O_QOUTFIFO(x) ((x) + 0xc9d) /* queue out fifo */
#define O_QOUTCNT(x) ((x) + 0xc9e) /* queue out count */
#define O_SCBARRAY(x) ((x) + 0xca0) /* scb array start */
/* host adapter offset definitions */
#define HA_REJBYTE(x) ((x) + 0xc31) /* 1st message in byte */
#define HA_MSG_FLAGS(x) ((x) + 0xc35) /* outgoing message flag */
#define HA_MSG_LEN(x) ((x) + 0xc36) /* outgoing message length */
#define HA_MSG_START(x) ((x) + 0xc37) /* outgoing message body */
#define HA_ARG_1(x) ((x) + 0xc4c) /* sdtr <-> rate parameters */
#define HA_ARG_2(x) ((x) + 0xc4d)
#define HA_RETURN_1(x) ((x) + 0xc4c)
#define HA_RETURN_2(x) ((x) + 0xc4d)
#define HA_SIGSTATE(x) ((x) + 0xc4e) /* value in SCSISIGO */
#define HA_NEEDSDTR(x) ((x) + 0xc4f) /* synchronous negotiation? */
#define HA_SCSICONF(x) ((x) + 0xc5a) /* SCSI config register */
#define HA_INTDEF(x) ((x) + 0xc5c) /* interrupt def'n register */
#define HA_HOSTCONF(x) ((x) + 0xc5d) /* host config def'n register */
/* debugging code */
#define AHA274X_DEBUG
/*
* If a parity error occurs during a data transfer phase, run the
* command to completion - it's easier that way - making a note
* of the error condition in this location. This then will modify
* a DID_OK status into a DID_PARITY one for the higher-level SCSI
* code.
*/
#define aha274x_parity(cmd) ((cmd)->SCp.Status)
/*
* Since the sequencer code DMAs the scatter-gather structures
* directly from memory, we use this macro to assert that the
* kernel structure hasn't changed.
*/
#define SG_STRUCT_CHECK(sg) \
((char *)&(sg).address - (char *)&(sg) != 0 || \
(char *)&(sg).length - (char *)&(sg) != 8 || \
sizeof((sg).address) != 4 || \
sizeof((sg).length) != 4 || \
sizeof(sg) != 12)
/*
* "Static" structures. Note that these are NOT initialized
* to zero inside the kernel - we have to initialize them all
* explicitly.
*
* We support a maximum of one adapter card per IRQ level (see the
* rationale for this above). On an interrupt, use the IRQ as an
* index into aha274x_boards[] to locate the card information.
*/
static struct Scsi_Host *aha274x_boards[MAXIRQ + 1];
struct aha274x_host {
int base; /* card base address */
int startup; /* intr type check */
volatile int unpause; /* value for HCNTRL */
volatile Scsi_Cmnd *SCB_array[AHA274X_MAXSCB]; /* active commands */
};
struct aha274x_scb {
unsigned char control;
unsigned char target_channel_lun; /* 4/1/3 bits */
unsigned char SG_segment_count;
unsigned char SG_list_pointer[4];
unsigned char SCSI_cmd_pointer[4];
unsigned char SCSI_cmd_length;
unsigned char RESERVED[2]; /* must be zero */
unsigned char target_status;
unsigned char residual_data_count[3];
unsigned char residual_SG_segment_count;
unsigned char data_pointer[4];
unsigned char data_count[3];
#if 0
/*
* No real point in transferring this to the
* SCB registers.
*/
unsigned char RESERVED[6];
#endif
};
/*
* NB. This table MUST be ordered shortest period first.
*/
static struct {
short period;
short rate;
char *english;
} aha274x_synctab[] = {
100, 0, "10.0",
125, 1, "8.0",
150, 2, "6.67",
175, 3, "5.7",
200, 4, "5.0",
225, 5, "4.4",
250, 6, "4.0",
275, 7, "3.6"
};
static int aha274x_synctab_max =
sizeof(aha274x_synctab) / sizeof(aha274x_synctab[0]);
enum aha_type {
T_NONE,
T_274X,
T_284X,
T_MAX
};
#ifdef AHA274X_DEBUG
extern int vsprintf(char *, const char *, va_list);
static
void debug(const char *fmt, ...)
{
va_list ap;
char buf[256];
va_start(ap, fmt);
vsprintf(buf, fmt, ap);
printk(buf);
va_end(ap);
}
static
void debug_config(enum aha_type type, int base)
{
int ioport2, ioport3, ioport4;
static char *BRT[T_MAX][16] = {
{ }, /* T_NONE */
{
"2", "???", "???", "12", /* T_274X */
"???", "???", "???", "28",
"???", "???", "???", "44",
"???", "???", "???", "60"
},
{
"2", "4", "8", "12", /* T_284X */
"16", "20", "24", "28",
"32", "36", "40", "44",
"48", "52", "56", "60"
}
};
static int DFT[4] = {
0, 50, 75, 100
};
static int SST[4] = {
256, 128, 64, 32
};
ioport2 = inb(HA_HOSTCONF(base));
ioport3 = inb(HA_SCSICONF(base));
ioport4 = inb(HA_INTDEF(base));
if (type == T_284X)
printk("AHA284X AT SLOT %d:\n", base >> 12);
else
printk("AHA274X AT EISA SLOT %d:\n", base >> 12);
printk(" irq %d\n"
" bus release time %s bclks\n"
" data fifo threshold %d%%\n",
ioport4 & 0xf,
BRT[type][(ioport2 >> 2) & 0xf],
DFT[(ioport2 >> 6) & 0x3]);
printk(" SCSI CHANNEL A:\n"
" scsi id %d\n"
" scsi bus parity check %sabled\n"
" scsi selection timeout %d ms\n"
" scsi bus reset at power-on %sabled\n",
ioport3 & 0x7,
(ioport3 & 0x20) ? "en" : "dis",
SST[(ioport3 >> 3) & 0x3],
(ioport3 & 0x40) ? "en" : "dis");
if (type == T_274X) {
printk(" scsi bus termination %sabled\n",
(ioport3 & 0x80) ? "en" : "dis");
}
}
static
void debug_rate(int base, int rate)
{
int target = inb(O_SCSIID(base)) >> 4;
if (rate) {
printk("aha274x: target %d now synchronous at %sMb/s\n",
target,
aha274x_synctab[(rate >> 4) & 0x7].english);
} else {
printk("aha274x: target %d using asynchronous mode\n",
target);
}
}
#else
# define debug(fmt, args...)
# define debug_config(x)
# define debug_rate(x,y)
#endif AHA274X_DEBUG
/*
* XXX - these options apply unilaterally to _all_ 274x/284x
* cards in the system. This should be fixed, but then,
* does anyone really have more than one in a machine?
*/
static int aha274x_extended = 0; /* extended translation on? */
void aha274x_setup(char *s, int *dummy)
{
int i;
char *p;
static struct {
char *name;
int *flag;
} options[] = {
"extended", &aha274x_extended,
NULL
};
for (p = strtok(s, ","); p; p = strtok(NULL, ",")) {
for (i = 0; options[i].name; i++)
if (!strcmp(options[i].name, p))
*(options[i].flag) = !0;
}
}
static
void aha274x_getscb(int base, struct aha274x_scb *scb)
{
/*
* This is almost identical to aha274x_putscb().
*/
outb(0x80, O_SCBCNT(base)); /* SCBAUTO */
asm volatile("cld\n\t"
"rep\n\t"
"insb"
: /* no output */
:"D" (scb), "c" (sizeof(*scb)), "d" (O_SCBARRAY(base))
:"di", "cx", "dx");
outb(0, O_SCBCNT(base));
}
/*
* How much data should be transferred for this SCSI command? Stop
* at segment sg_last if it's a scatter-gather command so we can
* compute underflow easily.
*/
static
unsigned aha274x_length(Scsi_Cmnd *cmd, int sg_last)
{
int i, segments;
unsigned length;
struct scatterlist *sg;
segments = cmd->use_sg - sg_last;
sg = (struct scatterlist *)cmd->buffer;
if (cmd->use_sg) {
for (i = length = 0;
i < cmd->use_sg && i < segments;
i++)
{
length += sg[i].length;
}
} else
length = cmd->request_bufflen;
return(length);
}
static
void aha274x_sg_check(Scsi_Cmnd *cmd)
{
int i;
struct scatterlist *sg = (struct scatterlist *)cmd->buffer;
if (cmd->use_sg) {
for (i = 0; i < cmd->use_sg; i++)
if ((unsigned)sg[i].length > 0xffff)
panic("aha274x_sg_check: s/g segment > 64k\n");
}
}
static
void aha274x_to_scsirate(unsigned char *rate,
unsigned char transfer,
unsigned char offset)
{
int i;
transfer *= 4;
for (i = 0; i < aha274x_synctab_max-1; i++) {
if (transfer == aha274x_synctab[i].period) {
*rate = (aha274x_synctab[i].rate << 4) | (offset & 0xf);
return;
}
if (transfer > aha274x_synctab[i].period &&
transfer < aha274x_synctab[i+1].period)
{
*rate = (aha274x_synctab[i+1].rate << 4) |
(offset & 0xf);
return;
}
}
*rate = 0;
}
/*
* Pause the sequencer and wait for it to actually stop - this
* is important since the sequencer can disable pausing for critical
* sections.
*/
#define PAUSE_SEQUENCER(p) \
do { \
outb(0xe, O_HCNTRL(p->base)); /* IRQMS|PAUSE|INTEN */ \
\
while ((inb(O_HCNTRL(p->base)) & 0x4) == 0) \
; \
} while (0)
/*
* Unpause the sequencer. Unremarkable, yet done often enough to
* warrant an easy way to do it.
*/
#define UNPAUSE_SEQUENCER(p) \
outb(p->unpause, O_HCNTRL(p->base)) /* IRQMS|INTEN */
/*
* See comments in aha274x_loadram() wrt this.
*/
#define RESTART_SEQUENCER(p) \
do { \
do { \
outb(0x2, O_SEQCTL(p->base)); \
} while (inw(O_SEQADDR(p->base)) != 0); \
\
UNPAUSE_SEQUENCER(p); \
} while (0)
/*
* Since we declared this using SA_INTERRUPT, interrupts should
* be disabled all through this function unless we say otherwise.
*/
static
void aha274x_isr(int irq)
{
int base, intstat;
struct aha274x_host *p;
p = (struct aha274x_host *)aha274x_boards[irq]->hostdata;
base = p->base;
/*
* Check the startup flag - if no commands have been queued,
* we probably have the interrupt type set wrong. Reverse
* the stored value and the active one in the host control
* register.
*/
if (p->startup) {
p->unpause ^= 0x8;
outb(inb(O_HCNTRL(p->base)) ^ 0x8, O_HCNTRL(p->base));
return;
}
/*
* Handle all the interrupt sources - especially for SCSI
* interrupts, we won't get a second chance at them.
*/
intstat = inb(O_INTSTAT(base));
if (intstat & 0x8) { /* BRKADRINT */
panic("aha274x_isr: brkadrint, error = 0x%x, seqaddr = 0x%x\n",
inb(O_ERROR(base)), inw(O_SEQADDR(base)));
}
if (intstat & 0x4) { /* SCSIINT */
int scbptr = inb(O_SCBPTR(base));
int status = inb(O_SSTAT1(base));
Scsi_Cmnd *cmd;
cmd = (Scsi_Cmnd *)p->SCB_array[scbptr];
if (!cmd) {
printk("aha274x_isr: no command for scb (scsiint)\n");
/*
* Turn off the interrupt and set status
* to zero, so that it falls through the
* reset of the SCSIINT code.
*/
outb(status, O_CLRSINT1(base));
UNPAUSE_SEQUENCER(p);
outb(0x4, O_CLRINT(base)); /* undocumented */
status = 0;
}
p->SCB_array[scbptr] = NULL;
/*
* Only the SCSI Status 1 register has information
* about exceptional conditions that we'd have a
* SCSIINT about; anything in SSTAT0 will be handled
* by the sequencer. Note that there can be multiple
* bits set.
*/
if (status & 0x80) { /* SELTO */
/*
* Hardware selection timer has expired. Turn
* off SCSI selection sequence.
*/
outb(0, O_SCSISEQ(base));
cmd->result = DID_TIME_OUT << 16;
/*
* If there's an active message, it belongs to the
* command that is getting punted - remove it.
*/
outb(0, HA_MSG_FLAGS(base));
/*
* Shut off the offending interrupt sources, reset
* the sequencer address to zero and unpause it,
* then call the high-level SCSI completion routine.
*
* WARNING! This is a magic sequence! After many
* hours of guesswork, turning off the SCSI interrupts
* in CLRSINT? does NOT clear the SCSIINT bit in
* INTSTAT. By writing to the (undocumented, unused
* according to the AIC-7770 manual) third bit of
* CLRINT, you can clear INTSTAT. But, if you do it
* while the sequencer is paused, you get a BRKADRINT
* with an Illegal Host Address status, so the
* sequencer has to be restarted first.
*/
outb(0x80, O_CLRSINT1(base)); /* CLRSELTIMO */
RESTART_SEQUENCER(p);
outb(0x4, O_CLRINT(base)); /* undocumented */
cmd->scsi_done(cmd);
}
if (status & 0x4) { /* SCSIPERR */
/*
* A parity error has occurred during a data
* transfer phase. Flag it and continue.
*/
printk("aha274x: parity error on target %d, lun %d\n",
cmd->target,
cmd->lun);
aha274x_parity(cmd) = DID_PARITY;
/*
* Clear interrupt and resume as above.
*/
outb(0x4, O_CLRSINT1(base)); /* CLRSCSIPERR */
UNPAUSE_SEQUENCER(p);
outb(0x4, O_CLRINT(base)); /* undocumented */
}
if ((status & (0x8|0x4)) == 0 && status) {
/*
* We don't know what's going on. Turn off the
* interrupt source and try to continue.
*/
printk("aha274x_isr: sstat1 = 0x%x\n", status);
outb(status, O_CLRSINT1(base));
UNPAUSE_SEQUENCER(p);
outb(0x4, O_CLRINT(base)); /* undocumented */
}
}
if (intstat & 0x2) { /* CMDCMPLT */
int complete, old_scbptr;
struct aha274x_scb scb;
unsigned actual;
Scsi_Cmnd *cmd;
/*
* The sequencer will continue running when it
* issues this interrupt. There may be >1 commands
* finished, so loop until we've processed them all.
*/
do {
complete = inb(O_QOUTFIFO(base));
cmd = (Scsi_Cmnd *)p->SCB_array[complete];
if (!cmd) {
printk("aha274x warning: "
"no command for scb (cmdcmplt)\n");
continue;
}
p->SCB_array[complete] = NULL;
PAUSE_SEQUENCER(p);
/*
* After pausing the sequencer (and waiting
* for it to stop), save its SCB pointer, then
* write in our completed one and read the SCB
* registers. Afterwards, restore the saved
* pointer, unpause the sequencer and call the
* higher-level completion function - unpause
* first since we have no idea how long done()
* will take.
*/
old_scbptr = inb(O_SCBPTR(base));
outb(complete, O_SCBPTR(base));
aha274x_getscb(base, &scb);
outb(old_scbptr, O_SCBPTR(base));
UNPAUSE_SEQUENCER(p);
cmd->result = scb.target_status |
(aha274x_parity(cmd) << 16);
/*
* Did we underflow? At this time, there's only
* one other driver that bothers to check for this,
* and cmd->underflow seems to be set rather half-
* heartedly in the higher-level SCSI code.
*/
actual = aha274x_length(cmd,
scb.residual_SG_segment_count);
actual -= ((scb.residual_data_count[2] << 16) |
(scb.residual_data_count[1] << 8) |
(scb.residual_data_count[0]));
if (actual < cmd->underflow) {
printk("aha274x: target %d underflow - "
"wanted (at least) %u, got %u\n",
cmd->target, cmd->underflow, actual);
cmd->result = scb.target_status |
(DID_UNDERFLOW << 16);
}
cmd->scsi_done(cmd);
/*
* Clear interrupt status before checking
* the output queue again. This eliminates
* a race condition whereby a command could
* complete between the queue poll and the
* interrupt clearing, so notification of the
* command being complete never made it back
* up to the kernel.
*/
outb(0x2, O_CLRINT(base)); /* CLRCMDINT */
} while (inb(O_QOUTCNT(base)));
}
if (intstat & 0x1) { /* SEQINT */
unsigned char transfer, offset, rate;
/*
* Although the sequencer is paused immediately on
* a SEQINT, an interrupt for a SCSIINT or a CMDCMPLT
* condition will have unpaused the sequencer before
* this point.
*/
PAUSE_SEQUENCER(p);
switch (intstat & 0xf0) {
case 0x00:
panic("aha274x_isr: unknown scsi bus phase\n");
case 0x10:
debug("aha274x_isr warning: "
"issuing message reject, 1st byte 0x%x\n",
inb(HA_REJBYTE(base)));
break;
case 0x20:
panic("aha274x_isr: reconnecting target %d "
"didn't issue IDENTIFY message\n",
(inb(O_SELID(base)) >> 4) & 0xf);
case 0x30:
debug("aha274x_isr: sequencer couldn't find match "
"for reconnecting target %d - issuing ABORT\n",
(inb(O_SELID(base)) >> 4) & 0xf);
break;
case 0x40:
transfer = inb(HA_ARG_1(base));
offset = inb(HA_ARG_2(base));
aha274x_to_scsirate(&rate, transfer, offset);
outb(rate, HA_RETURN_1(base));
debug_rate(base, rate);
break;
default:
debug("aha274x_isr: seqint, "
"intstat = 0x%x, scsisigi = 0x%x\n",
intstat, inb(O_SCSISIGI(base)));
break;
}
outb(0x1, O_CLRINT(base)); /* CLRSEQINT */
UNPAUSE_SEQUENCER(p);
}
}
/*
* Probing for EISA boards: it looks like the first two bytes
* are a manufacturer code - three characters, five bits each:
*
* BYTE 0 BYTE 1 BYTE 2 BYTE 3
* ?1111122 22233333 PPPPPPPP RRRRRRRR
*
* The characters are baselined off ASCII '@', so add that value
* to each to get the real ASCII code for it. The next two bytes
* appear to be a product and revision number, probably vendor-
* specific. This is what is being searched for at each port,
* and what should probably correspond to the ID= field in the
* ECU's .cfg file for the card - if your card is not detected,
* make sure your signature is listed in the array.
*
* The fourth byte's lowest bit seems to be an enabled/disabled
* flag (rest of the bits are reserved?).
*/
static
enum aha_type aha274x_probe(int slot, int s_base)
{
int i;
unsigned char buf[4];
static struct {
int n;
unsigned char signature[sizeof(buf)];
enum aha_type type;
} S[] = {
4, { 0x04, 0x90, 0x77, 0x71 }, T_274X, /* host adapter 274x */
4, { 0x04, 0x90, 0x77, 0x70 }, T_274X, /* motherboard 274x */
4, { 0x04, 0x90, 0x77, 0x56 }, T_284X, /* 284x, BIOS enabled */
};
for (i = 0; i < sizeof(buf); i++) {
/*
* The VL-bus cards need to be primed by
* writing before a signature check.
*/
outb(0x80 + i, s_base);
buf[i] = inb(s_base + i);
}
for (i = 0; i < sizeof(S)/sizeof(S[0]); i++) {
if (!memcmp(buf, S[i].signature, S[i].n)) {
/*
* Signature match on enabled card?
*/
if (inb(s_base + 4) & 1)
return(S[i].type);
printk("aha274x disabled at slot %d, ignored\n", slot);
}
}
return(T_NONE);
}
/*
* Return ' ' for plain 274x, 'T' for twin-channel, 'W' for
* wide channel, '?' for anything else.
*/
static
char aha274x_type(int base)
{
/*
* The AIC-7770 can be wired so that, on chip reset,
* the SCSI Block Control register indicates how many
* busses the chip is configured for.
*/
switch (inb(O_SBLKCTL(base))) {
case 0:
return(' ');
case 2:
return('W');
case 8:
return('T');
default:
printk("aha274x has unknown bus configuration\n");
return('?');
}
}
static
void aha274x_loadram(int base)
{
static unsigned char seqprog[] = {
/*
* Each sequencer instruction is 29 bits
* long (fill in the excess with zeroes)
* and has to be loaded from least -> most
* significant byte, so this table has the
* byte ordering reversed.
*/
# include "aha274x_seq.h"
};
/*
* When the AIC-7770 is paused (as on chip reset), the
* sequencer address can be altered and a sequencer
* program can be loaded by writing it, byte by byte, to
* the sequencer RAM port - the Adaptec documentation
* recommends using REP OUTSB to do this, hence the inline
* assembly. Since the address autoincrements as we load
* the program, reset it back to zero afterward. Disable
* sequencer RAM parity error detection while loading, and
* make sure the LOADRAM bit is enabled for loading.
*/
outb(0x83, O_SEQCTL(base)); /* PERRORDIS|SEQRESET|LOADRAM */
asm volatile("cld\n\t"
"rep\n\t"
"outsb"
: /* no output */
:"S" (seqprog), "c" (sizeof(seqprog)), "d" (O_SEQRAM(base))
:"si", "cx", "dx");
/*
* WARNING! This is a magic sequence! After extensive
* experimentation, it seems that you MUST turn off the
* LOADRAM bit before you play with SEQADDR again, else
* you will end up with parity errors being flagged on
* your sequencer program. (You would also think that
* turning off LOADRAM and setting SEQRESET to reset the
* address to zero would work, but you need to do it twice
* for it to take effect on the address. Timing problem?)
*/
outb(0, O_SEQCTL(base));
do {
/*
* Actually, reset it until
* the address shows up as
* zero just to be safe..
*/
outb(0x2, O_SEQCTL(base)); /* SEQRESET */
} while (inw(O_SEQADDR(base)) != 0);
}
static
int aha274x_register(Scsi_Host_Template *template,
enum aha_type type,
int base)
{
int i, irq, scsi_id;
struct Scsi_Host *host;
struct aha274x_host *p;
/*
* Give the AIC-7770 a reset - reading the 274x's registers
* returns zeroes unless you do. This forces a pause of the
* Sequencer.
*/
outb(1, O_HCNTRL(base)); /* CHIPRST */
/*
* The IRQ level in i/o port 4 maps directly onto the real
* IRQ number. If it's ok, register it with the kernel.
*
* NB. the Adaptec documentation says the IRQ number is only
* in the lower four bits; the ECU information shows the
* high bit being used as well. Which is correct?
*/
irq = inb(HA_INTDEF(base)) & 0xf;
if (irq < 9 || irq > 15) {
printk("aha274x uses unsupported IRQ level, ignoring\n");
return(0);
}
/*
* Lock out other contenders for our i/o space.
*/
snarf_region(O_MINREG(base), O_MAXREG(base)-O_MINREG(base));
/*
* Any card-type-specific adjustments before we register
* the scsi host(s).
*/
scsi_id = inb(HA_SCSICONF(base)) & 0x7;
switch (aha274x_type(base)) {
case 'T':
printk("aha274x warning: ignoring channel B of 274x-twin\n");
break;
case ' ':
break;
default:
printk("aha274x is an unsupported type, ignoring\n");
return(0);
}
/*
* Before registry, make sure that the offsets of the
* struct scatterlist are what the sequencer will expect,
* otherwise disable scatter-gather altogether until someone
* can fix it. This is important since the sequencer will
* DMA elements of the SG array in while executing commands.
*/
if (template->sg_tablesize != SG_NONE) {
struct scatterlist sg;
if (SG_STRUCT_CHECK(sg)) {
printk("aha274x warning: kernel scatter-gather "
"structures changed, disabling it\n");
template->sg_tablesize = SG_NONE;
}
}
/*
* Register each "host" and fill in the returned Scsi_Host
* structure as best we can. Some of the parameters aren't
* really relevant for EISA, and none of the high-level SCSI
* code looks at it anyway.. why are the fields there? Also
* save the pointer so that we can find the information when
* an IRQ is triggered.
*/
host = scsi_register(template, sizeof(struct aha274x_host));
host->this_id = scsi_id;
host->irq = irq;
aha274x_boards[irq] = host;
p = (struct aha274x_host *)host->hostdata;
for (i = 0; i < AHA274X_MAXSCB; i++)
p->SCB_array[i] = NULL;
p->base = base;
/*
* The interrupt trigger is different depending
* on whether the card is EISA or VL-bus - sometimes.
* The startup variable will be cleared once the first
* command is queued, and is checked in the isr to
* try and detect when the interrupt type is set
* incorrectly, triggering an interrupt immediately.
*/
p->unpause = (type != T_274X ? 0x2 : 0xa);
p->startup = !0;
/*
* Register IRQ with the kernel _after_ the host information
* is set up, in case we take an interrupt right away, due to
* the interrupt type being set wrong.
*/
if (request_irq(irq, aha274x_isr, SA_INTERRUPT, "AHA274x/284x")) {
printk("aha274x couldn't register irq %d, ignoring\n", irq);
return(0);
}
/*
* A reminder until this can be detected automatically.
*/
printk("aha274x: extended translation %sabled\n",
aha274x_extended ? "en" : "dis");
/*
* Print out debugging information before re-enabling
* the card - a lot of registers on it can't be read
* when the sequencer is active.
*/
debug_config(type, base);
/*
* Load the sequencer program, then re-enable the board -
* resetting the AIC-7770 disables it, leaving the lights
* on with nobody home.
*/
aha274x_loadram(base);
outb(1, O_BCTL(base)); /* ENABLE */
/*
* Set the host adapter registers to indicate that synchronous
* negotiation should be attempted the first time the targets
* are communicated with. Also initialize the active message
* flag to indicate that there is no message.
*/
outb(0xff, HA_NEEDSDTR(base));
outb(0, HA_MSG_FLAGS(base));
/*
* Unpause the sequencer before returning and enable
* interrupts - we shouldn't get any until the first
* command is sent to us by the high-level SCSI code.
*/
UNPAUSE_SEQUENCER(p);
return(1);
}
int aha274x_detect(Scsi_Host_Template *template)
{
enum aha_type type;
int found = 0, slot, base;
for (slot = MINEISA; slot <= MAXEISA; slot++) {
base = SLOTBASE(slot);
if (check_region(O_MINREG(base),
O_MAXREG(base)-O_MINREG(base)))
{
/*
* Some other driver has staked a
* claim to this i/o region already.
*/
continue;
}
type = aha274x_probe(slot, O_BIDx(base));
if (type != T_NONE) {
/*
* We "find" a 274x if we locate the card
* signature and we can set it up and register
* it with the kernel without incident.
*/
found += aha274x_register(template, type, base);
}
}
template->name = (char *)aha274x_info();
return(found);
}
const char *aha274x_info(void)
{
return("Adaptec AHA274x/284x (EISA/VL-bus -> Fast SCSI) "
AHA274X_SEQ_VERSION "/"
AHA274X_H_VERSION "/"
"1.29");
}
int aha274x_command(Scsi_Cmnd *cmd)
{
/*
* This is a relic of non-interrupt-driven SCSI
* drivers. With the can_queue variable set, this
* should never be called.
*/
panic("aha274x_command was called\n");
}
static
void aha274x_buildscb(struct aha274x_host *p,
Scsi_Cmnd *cmd,
struct aha274x_scb *scb)
{
void *addr;
unsigned length;
memset(scb, 0, sizeof(*scb));
/*
* NB. channel selection (bit 3) is always zero.
*/
scb->target_channel_lun = ((cmd->target << 4) & 0xf0) |
(cmd->lun & 0x7);
/*
* The interpretation of request_buffer and request_bufflen
* changes depending on whether or not use_sg is zero; a
* non-zero use_sg indicates the number of elements in the
* scatter-gather array.
*
* The AIC-7770 can't support transfers of any sort larger
* than 2^24 (three-byte count) without backflips. For what
* the kernel is doing, this shouldn't occur. I hope.
*/
length = aha274x_length(cmd, 0);
/*
* The sequencer code cannot yet handle scatter-gather segments
* larger than 64k (two-byte length). The 1.1.x kernels, however,
* have a four-byte length field in the struct scatterlist, so
* make sure we don't exceed 64k on these kernels for now.
*/
aha274x_sg_check(cmd);
if (length > 0xffffff) {
panic("aha274x_buildscb: can't transfer > 2^24 - 1 bytes\n");
}
/*
* XXX - this relies on the host data being stored in a
* little-endian format.
*/
addr = cmd->cmnd;
scb->SCSI_cmd_length = COMMAND_SIZE(cmd->cmnd[0]);
memcpy(scb->SCSI_cmd_pointer, &addr, sizeof(scb->SCSI_cmd_pointer));
if (cmd->use_sg) {
#if 0
debug("aha274x_buildscb: SG used, %d segments, length %u\n",
cmd->use_sg,
length);
#endif
scb->SG_segment_count = cmd->use_sg;
memcpy(scb->SG_list_pointer,
&cmd->request_buffer,
sizeof(scb->SG_list_pointer));
} else {
scb->SG_segment_count = 0;
memcpy(scb->data_pointer,
&cmd->request_buffer,
sizeof(scb->data_pointer));
memcpy(scb->data_count,
&cmd->request_bufflen,
sizeof(scb->data_count));
}
}
static
void aha274x_putscb(int base, struct aha274x_scb *scb)
{
/*
* By turning on the SCB auto increment, any reference
* to the SCB I/O space postincrements the SCB address
* we're looking at. So turn this on and dump the relevant
* portion of the SCB to the card.
*/
outb(0x80, O_SCBCNT(base)); /* SCBAUTO */
asm volatile("cld\n\t"
"rep\n\t"
"outsb"
: /* no output */
:"S" (scb), "c" (sizeof(*scb)), "d" (O_SCBARRAY(base))
:"si", "cx", "dx");
outb(0, O_SCBCNT(base));
}
int aha274x_queue(Scsi_Cmnd *cmd, void (*fn)(Scsi_Cmnd *))
{
long flags;
int empty, old_scbptr;
struct aha274x_host *p;
struct aha274x_scb scb;
#if 0
debug("aha274x_queue: cmd 0x%x (size %u), target %d, lun %d\n",
cmd->cmnd[0],
COMMAND_SIZE(cmd->cmnd[0]),
cmd->target,
cmd->lun);
#endif
p = (struct aha274x_host *)cmd->host->hostdata;
/*
* Construct the SCB beforehand, so the sequencer is
* paused a minimal amount of time.
*/
aha274x_buildscb(p, cmd, &scb);
/*
* Clear the startup flag - we can now legitimately
* expect interrupts.
*/
p->startup = 0;
/*
* This is a critical section, since we don't want the
* interrupt routine mucking with the host data or the
* card. Since the kernel documentation is vague on
* whether or not we are in a cli/sti pair already, save
* the flags to be on the safe side.
*/
save_flags(flags);
cli();
/*
* Find a free slot in the SCB array to load this command
* into. Since can_queue is set to AHA274X_MAXSCB, we
* should always find one.
*/
for (empty = 0; empty < AHA274X_MAXSCB; empty++)
if (!p->SCB_array[empty])
break;
if (empty == AHA274X_MAXSCB)
panic("aha274x_queue: couldn't find a free scb\n");
/*
* Pause the sequencer so we can play with its registers -
* wait for it to acknowledge the pause.
*
* XXX - should the interrupts be left on while doing this?
*/
PAUSE_SEQUENCER(p);
/*
* Save the SCB pointer and put our own pointer in - this
* selects one of the four banks of SCB registers. Load
* the SCB, then write its pointer into the queue in FIFO
* and restore the saved SCB pointer.
*/
old_scbptr = inb(O_SCBPTR(p->base));
outb(empty, O_SCBPTR(p->base));
aha274x_putscb(p->base, &scb);
outb(empty, O_QINFIFO(p->base));
outb(old_scbptr, O_SCBPTR(p->base));
/*
* Make sure the Scsi_Cmnd pointer is saved, the struct it
* points to is set up properly, and the parity error flag
* is reset, then unpause the sequencer and watch the fun
* begin.
*/
cmd->scsi_done = fn;
p->SCB_array[empty] = cmd;
aha274x_parity(cmd) = DID_OK;
UNPAUSE_SEQUENCER(p);
restore_flags(flags);
return(0);
}
/* return values from aha274x_kill */
enum k_state {
k_ok, /* scb found and message sent */
k_busy, /* message already present */
k_absent, /* couldn't locate scb */
k_disconnect, /* scb found, but disconnected */
};
/*
* This must be called with interrupts disabled - it's going to
* be messing around with the host data, and an interrupt being
* fielded in the middle could get ugly.
*
* Since so much of the abort and reset code is shared, this
* function performs more magic than it really should. If the
* command completes ok, then it will call scsi_done with the
* result code passed in. The unpause parameter controls whether
* or not the sequencer gets unpaused - the reset function, for
* instance, may want to do something more aggressive.
*
* Note that the command is checked for in our SCB_array first
* before the sequencer is paused, so if k_absent is returned,
* then the sequencer is NOT paused.
*/
static
enum k_state aha274x_kill(Scsi_Cmnd *cmd, unsigned char message,
unsigned int result, int unpause)
{
struct aha274x_host *p;
int i, scb, found, queued;
unsigned char scbsave[AHA274X_MAXSCB];
p = (struct aha274x_host *)cmd->host->hostdata;
/*
* If we can't find the command, assume it just completed
* and shrug it away.
*/
for (scb = 0; scb < AHA274X_MAXSCB; scb++)
if (p->SCB_array[scb] == cmd)
break;
if (scb == AHA274X_MAXSCB)
return(k_absent);
PAUSE_SEQUENCER(p);
/*
* This is the best case, really. Check to see if the
* command is still in the sequencer's input queue. If
* so, simply remove it. Reload the queue afterward.
*/
queued = inb(O_QINCNT(p->base));
for (i = found = 0; i < queued; i++) {
scbsave[i] = inb(O_QINFIFO(p->base));
if (scbsave[i] == scb) {
found = 1;
i -= 1;
}
}
queued -= found;
for (i = 0; i < queued; i++)
outb(scbsave[i], O_QINFIFO(p->base));
if (found)
goto complete;
/*
* Check the current SCB bank. If it's not the one belonging
* to the command we want to kill, assume that the command
* is disconnected. It's rather a pain to force a reconnect
* and send a message to the target, so we abdicate responsibility
* in this case.
*/
if (inb(O_SCBPTR(p->base)) != scb) {
if (unpause)
UNPAUSE_SEQUENCER(p);
return(k_disconnect);
}
/*
* Presumably at this point our target command is active. Check
* to see if there's a message already in effect. If not, place
* our message in and assert ATN so the target goes into MESSAGE
* OUT phase.
*/
if (inb(HA_MSG_FLAGS(p->base)) & 0x80) {
if (unpause)
UNPAUSE_SEQUENCER(p);
return(k_busy);
}
outb(0x80, HA_MSG_FLAGS(p->base)); /* active message */
outb(1, HA_MSG_LEN(p->base)); /* length = 1 */
outb(message, HA_MSG_START(p->base)); /* message body */
/*
* Assert ATN. Use the value of SCSISIGO saved by the
* sequencer code so we don't alter its contents radically
* in the middle of something critical.
*/
outb(inb(HA_SIGSTATE(p->base)) | 0x10, O_SCSISIGO(p->base));
/*
* The command has been killed. Do the bookkeeping, unpause
* the sequencer, and notify the higher-level SCSI code.
*/
complete:
p->SCB_array[scb] = NULL;
if (unpause)
UNPAUSE_SEQUENCER(p);
cmd->result = result << 16;
cmd->scsi_done(cmd);
return(k_ok);
}
int aha274x_abort(Scsi_Cmnd *cmd)
{
int rv;
long flags;
save_flags(flags);
cli();
switch (aha274x_kill(cmd, ABORT, DID_ABORT, !0)) {
case k_ok: rv = SCSI_ABORT_SUCCESS; break;
case k_busy: rv = SCSI_ABORT_BUSY; break;
case k_absent: rv = SCSI_ABORT_NOT_RUNNING; break;
case k_disconnect: rv = SCSI_ABORT_SNOOZE; break;
default:
panic("aha274x_do_abort: internal error\n");
}
restore_flags(flags);
return(rv);
}
/*
* Resetting the bus always succeeds - is has to, otherwise the
* kernel will panic! Try a surgical technique - sending a BUS
* DEVICE RESET message - on the offending target before pulling
* the SCSI bus reset line.
*/
int aha274x_reset(Scsi_Cmnd *cmd)
{
int i;
long flags;
Scsi_Cmnd *reset;
struct aha274x_host *p;
p = (struct aha274x_host *)cmd->host->hostdata;
save_flags(flags);
cli();
switch (aha274x_kill(cmd, BUS_DEVICE_RESET, DID_RESET, 0)) {
case k_ok:
/*
* The RESET message was sent to the target
* with no problems. Flag that target as
* needing a SDTR negotiation on the next
* connection and restart the sequencer.
*/
outb((1 << cmd->target), HA_NEEDSDTR(p->base));
UNPAUSE_SEQUENCER(p);
break;
case k_absent:
/*
* The sequencer will not be paused if aha274x_kill()
* couldn't find the command.
*/
PAUSE_SEQUENCER(p);
/* falls through */
case k_busy:
case k_disconnect:
/*
* Do a hard reset of the SCSI bus. According to the
* SCSI-2 draft specification, reset has to be asserted
* for at least 25us. I'm invoking the kernel delay
* function for 30us since I'm not totally trusting of
* the busy loop timing.
*
* XXX - I'm not convinced this works. I tried resetting
* the bus before, trying to get the devices on the
* bus to revert to asynchronous transfer, and it
* never seemed to work.
*/
debug("aha274x: attempting to reset scsi bus and card\n");
outb(1, O_SCSISEQ(p->base)); /* SCSIRSTO */
udelay(30);
outb(0, O_SCSISEQ(p->base)); /* !SCSIRSTO */
outb(0xff, HA_NEEDSDTR(p->base));
UNPAUSE_SEQUENCER(p);
/*
* Locate the command and return a "reset" status
* for it. This is not completely correct and will
* probably return to haunt me later.
*/
for (i = 0; i < AHA274X_MAXSCB; i++) {
if (cmd == p->SCB_array[i]) {
reset = (Scsi_Cmnd *)p->SCB_array[i];
p->SCB_array[i] = NULL;
reset->result = DID_RESET << 16;
reset->scsi_done(reset);
break;
}
}
break;
default:
panic("aha274x_reset: internal error\n");
}
restore_flags(flags);
return(SCSI_RESET_SUCCESS);
}
int aha274x_biosparam(Disk *disk, int devno, int geom[])
{
int heads, sectors, cylinders;
/*
* XXX - if I could portably find the card's configuration
* information, then this could be autodetected instead
* of left to a boot-time switch.
*/
heads = 64;
sectors = 32;
cylinders = disk->capacity / (heads * sectors);
if (aha274x_extended && cylinders > 1024) {
heads = 255;
sectors = 63;
cylinders = disk->capacity / (255 * 63);
}
geom[0] = heads;
geom[1] = sectors;
geom[2] = cylinders;
return(0);
}
/* @(#)aha274x.h 1.11 94/09/06 jda */
/*
* Adaptec 274x device driver for Linux.
* Copyright (c) 1994 The University of Calgary Department of Computer Science.
*
* 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 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef aha274x_h
#define aha274x_h
#define AHA274X_MAXSCB 4
#define AHA274X_H_VERSION "1.11"
/*
* Scsi_Host_Template (see hosts.h) for 274x - some fields
* to do with card config are filled in after the card is
* detected.
*/
#define AHA274X { \
NULL, \
"", \
aha274x_detect, \
NULL, \
aha274x_info, \
aha274x_command, \
aha274x_queue, \
aha274x_abort, \
aha274x_reset, \
NULL, \
aha274x_biosparam, \
AHA274X_MAXSCB, /* max simultaneous cmds */\
-1, /* scsi id of host adapter */\
SG_ALL, /* max scatter-gather cmds */\
1, /* cmds per lun (linked cmds) */\
0, /* number of 274x's present */\
0, /* no memory DMA restrictions */\
DISABLE_CLUSTERING \
}
extern int aha274x_queue(Scsi_Cmnd *, void (*)(Scsi_Cmnd *));
extern int aha274x_biosparam(Disk *, int, int[]);
extern int aha274x_detect(Scsi_Host_Template *);
extern int aha274x_command(Scsi_Cmnd *);
extern int aha274x_abort(Scsi_Cmnd *);
extern int aha274x_reset(Scsi_Cmnd *);
extern const char *aha274x_info(void);
#endif
# @(#)aha274x.seq 1.28 94/10/04 jda
#
# Adaptec 274x device driver for Linux.
# Copyright (c) 1994 The University of Calgary Department of Computer Science.
#
# 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 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
VERSION AHA274X_SEQ_VERSION 1.28
MAXSCB = 4
SCSISEQ = 0x00
SXFRCTL0 = 0x01
SXFRCTL1 = 0x02
SCSISIGI = 0x03
SCSISIGO = 0x03
SCSIRATE = 0x04
SCSIID = 0x05
SCSIDATL = 0x06
STCNT = 0x08
STCNT+0 = 0x08
STCNT+1 = 0x09
STCNT+2 = 0x0a
SSTAT0 = 0x0b
CLRSINT1 = 0x0c
SSTAT1 = 0x0c
SIMODE1 = 0x11
SCSIBUSL = 0x12
SHADDR = 0x14
SELID = 0x19
SBLKCTL = 0x1f
SEQCTL = 0x60
A = 0x64 # == ACCUM
SINDEX = 0x65
DINDEX = 0x66
ALLZEROS = 0x6a
NONE = 0x6a
SINDIR = 0x6c
DINDIR = 0x6d
FUNCTION1 = 0x6e
HADDR = 0x88
HCNT = 0x8c
HCNT+0 = 0x8c
HCNT+1 = 0x8d
HCNT+2 = 0x8e
SCBPTR = 0x90
INTSTAT = 0x91
DFCNTRL = 0x93
DFSTATUS = 0x94
DFDAT = 0x99
QINFIFO = 0x9b
QINCNT = 0x9c
QOUTFIFO = 0x9d
SCSICONF = 0x5a
# The two reserved bytes at SCBARRAY+1[23] are expected to be set to
# zero, and the reserved bit in SCBARRAY+0 is used as an internal flag
# to indicate whether or not to reload scatter-gather parameters after
# a disconnect.
#
SCBARRAY+0 = 0xa0
SCBARRAY+1 = 0xa1
SCBARRAY+2 = 0xa2
SCBARRAY+3 = 0xa3
SCBARRAY+7 = 0xa7
SCBARRAY+11 = 0xab
SCBARRAY+14 = 0xae
SCBARRAY+15 = 0xaf
SCBARRAY+16 = 0xb0
SCBARRAY+17 = 0xb1
SCBARRAY+18 = 0xb2
SCBARRAY+19 = 0xb3
SCBARRAY+20 = 0xb4
SCBARRAY+21 = 0xb5
SCBARRAY+22 = 0xb6
SCBARRAY+23 = 0xb7
SCBARRAY+24 = 0xb8
SCBARRAY+25 = 0xb9
SIGNAL_0 = 0x01 # unknown scsi bus phase
SIGNAL_1 = 0x11 # message reject
SIGNAL_2 = 0x21 # no IDENTIFY after reconnect
SIGNAL_3 = 0x31 # no cmd match for reconnect
SIGNAL_4 = 0x41 # SDTR -> SCSIRATE conversion
# The host adapter card (at least the BIOS) uses 20-2f for SCSI
# device information, 32-33 and 5a-5f as well. Since we don't support
# wide or twin-bus SCSI, 28-2f can be reclaimed. As it turns out, the
# BIOS trashes 20-27 anyway, writing the synchronous negotiation results
# on top of the BIOS values, so we re-use those for our per-target
# scratchspace (actually a value that can be copied directly into
# SCSIRATE). This implies, since we can't get the BIOS config values,
# that all targets will be negotiated with for synchronous transfer.
# NEEDSDTR has one bit per target indicating if an SDTR message is
# needed for that device - this will be set initially, as well as
# after a bus reset condition.
#
# The high bit of DROPATN is set if ATN should be dropped before the ACK
# when outb is called. REJBYTE contains the first byte of a MESSAGE IN
# message, so the driver can report an intelligible error if a message is
# rejected.
#
# RESELECT's high bit is true if we are currently handling a reselect;
# its next-highest bit is true ONLY IF we've seen an IDENTIFY message
# from the reselecting target. If we haven't had IDENTIFY, then we have
# no idea what the lun is, and we can't select the right SCB register
# bank, so force a kernel panic if the target attempts a data in/out or
# command phase instead of corrupting something.
#
# Note that SG_NEXT occupies four bytes.
#
SYNCNEG = 0x20
DISC_DSB_A = 0x32
DROPATN = 0x30
REJBYTE = 0x31
RESELECT = 0x34
MSG_FLAGS = 0x35
MSG_LEN = 0x36
MSG_START+0 = 0x37
MSG_START+1 = 0x38
MSG_START+2 = 0x39
MSG_START+3 = 0x3a
MSG_START+4 = 0x3b
MSG_START+5 = 0x3c
-MSG_START+0 = 0xc9 # 2's complement of MSG_START+0
ARG_1 = 0x4c # sdtr conversion args & return
ARG_2 = 0x4d
RETURN_1 = 0x4c
SIGSTATE = 0x4e # value written to SCSISIGO
NEEDSDTR = 0x4f # send SDTR message, 1 bit/trgt
SG_SIZEOF = 12 # sizeof(struct scatterlist)
SG_NOLOAD = 0x50 # load SG pointer/length?
SG_COUNT = 0x51 # working value of SG count
SG_NEXT = 0x52 # working value of SG pointer
SG_NEXT+0 = 0x52
SG_NEXT+1 = 0x53
SG_NEXT+2 = 0x54
SG_NEXT+3 = 0x55
# Poll QINCNT for work - the lower three bits contain
# the number of entries in the Queue In FIFO.
#
start:
test SCSISIGI,0x4 jnz reselect # BSYI
test QINCNT,0x7 jz start
# We have at least one queued SCB now. Set the SCB pointer
# from the FIFO so we see the right bank of SCB registers,
# then set SCSI options and set the initiator and target
# SCSI IDs.
#
mov SCBPTR,QINFIFO
mov SCBARRAY+1 call initialize
clr SG_NOLOAD
clr RESELECT
# As soon as we get a successful selection, the target should go
# into the message out phase since we have ATN asserted. Prepare
# the message to send, locking out the device driver. If the device
# driver hasn't beaten us with an ABORT or RESET message, then tack
# on a SDTR negotation if required.
#
# Messages are stored in scratch RAM starting with a flag byte (high bit
# set means active message), one length byte, and then the message itself.
#
mov SCBARRAY+1 call disconnect # disconnect ok?
and SINDEX,0x7,SCBARRAY+1 # lun
or SINDEX,A # return value from disconnect
or SINDEX,0x80 call mk_mesg # IDENTIFY message
mov A,SINDEX
cmp MSG_START+0,A jne !message # did driver beat us?
mvi MSG_START+1 call mk_sdtr # build SDTR message if needed
!message:
# Enable selection phase as an initiator, and do automatic ATN
# after the selection.
#
mvi SCSISEQ,0x48 # ENSELO|ENAUTOATNO
# Wait for successful arbitration. The AIC-7770 documentation says
# that SELINGO indicates successful arbitration, and that it should
# be used to look for SELDO. However, if the sequencer is paused at
# just the right time - a parallel fsck(8) on two drives did it for
# me - then SELINGO can flip back to false before we've seen it. This
# makes the sequencer sit in the arbitration loop forever. This is
# Not Good.
#
# Therefore, I've added a check in the arbitration loop for SELDO
# too. This could arguably be made a critical section by disabling
# pauses, but I don't want to make a potentially infinite loop a CS.
# I suppose you could fold it into the select loop, too, but since
# I've been hunting this bug for four days it's kinda like a trophy.
#
arbitrate:
test SSTAT0,0x40 jnz *select # SELDO
test SSTAT0,0x10 jz arbitrate # SELINGO
# Wait for a successful selection. If the hardware selection
# timer goes off, then the driver gets the interrupt, so we don't
# need to worry about it.
#
select:
test SSTAT0,0x40 jz select # SELDO
jmp *select
# Reselection is being initiated by a target - we've seen the BSY
# line driven active, and we didn't do it! Enable the reselection
# hardware, and wait for it to finish. Make a note that we've been
# reselected, but haven't seen an IDENTIFY message from the target
# yet.
#
reselect:
mvi SCSISEQ,0x10 # ENRSELI
reselect1:
test SSTAT0,0x20 jz reselect1 # SELDI
mov SELID call initialize
mvi RESELECT,0x80 # reselected, no IDENTIFY
# After the [re]selection, make sure that the [re]selection enable
# bit is off. This chip is flaky enough without extra things
# turned on. Also clear the BUSFREE bit in SSTAT1 since we'll be
# using it shortly.
#
*select:
clr SCSISEQ
mvi CLRSINT1,0x8 # CLRBUSFREE
# Main loop for information transfer phases. If BSY is false, then
# we have a bus free condition, expected or not. Otherwise, wait
# for the target to assert REQ before checking MSG, C/D and I/O
# for the bus phase.
#
# We can't simply look at the values of SCSISIGI here (if we want
# to do synchronous data transfer), because the target won't assert
# REQ if it's already sent us some data that we haven't acknowledged
# yet.
#
ITloop:
test SSTAT1,0x8 jnz p_busfree # BUSFREE
test SSTAT1,0x1 jz ITloop # REQINIT
and A,0xe0,SCSISIGI # CDI|IOI|MSGI
cmp ALLZEROS,A je p_dataout
cmp A,0x40 je p_datain
cmp A,0x80 je p_command
cmp A,0xc0 je p_status
cmp A,0xa0 je p_mesgout
cmp A,0xe0 je p_mesgin
mvi INTSTAT,SIGNAL_0 # unknown - signal driver
p_dataout:
mvi 0 call scsisig # !CDO|!IOO|!MSGO
call assert
call sg_load
mvi A,3
mvi DINDEX,HCNT
mvi SCBARRAY+23 call bcopy
mvi A,3
mvi DINDEX,STCNT
mvi SCBARRAY+23 call bcopy
mvi A,4
mvi DINDEX,HADDR
mvi SCBARRAY+19 call bcopy
mvi 0x3d call dma # SCSIEN|SDMAEN|HDMAEN|
# DIRECTION|FIFORESET
# After a DMA finishes, save the final transfer pointer and count
# back into the SCB, in case a device disconnects in the middle of
# a transfer. Use SHADDR and STCNT instead of HADDR and HCNT, since
# it's a reflection of how many bytes were transferred on the SCSI
# (as opposed to the host) bus.
#
mvi A,3
mvi DINDEX,SCBARRAY+23
mvi STCNT call bcopy
mvi A,4
mvi DINDEX,SCBARRAY+19
mvi SHADDR call bcopy
call sg_advance
mov SCBARRAY+18,SG_COUNT # residual S/G count
jmp ITloop
p_datain:
mvi 0x40 call scsisig # !CDO|IOO|!MSGO
call assert
call sg_load
mvi A,3
mvi DINDEX,HCNT
mvi SCBARRAY+23 call bcopy
mvi A,3
mvi DINDEX,STCNT
mvi SCBARRAY+23 call bcopy
mvi A,4
mvi DINDEX,HADDR
mvi SCBARRAY+19 call bcopy
mvi 0x39 call dma # SCSIEN|SDMAEN|HDMAEN|
# !DIRECTION|FIFORESET
mvi A,3
mvi DINDEX,SCBARRAY+23
mvi STCNT call bcopy
mvi A,4
mvi DINDEX,SCBARRAY+19
mvi SHADDR call bcopy
call sg_advance
mov SCBARRAY+18,SG_COUNT # residual S/G count
jmp ITloop
# Command phase. Set up the DMA registers and let 'er rip - the
# two bytes after the SCB SCSI_cmd_length are zeroed by the driver,
# so we can copy those three bytes directly into HCNT.
#
p_command:
mvi 0x80 call scsisig # CDO|!IOO|!MSGO
call assert
mvi A,3
mvi DINDEX,HCNT
mvi SCBARRAY+11 call bcopy
mvi A,3
mvi DINDEX,STCNT
mvi SCBARRAY+11 call bcopy
mvi A,4
mvi DINDEX,HADDR
mvi SCBARRAY+7 call bcopy
mvi 0x3d call dma # SCSIEN|SDMAEN|HDMAEN|
# DIRECTION|FIFORESET
jmp ITloop
# Status phase. Wait for the data byte to appear, then read it
# and store it into the SCB.
#
p_status:
mvi 0xc0 call scsisig # CDO|IOO|!MSGO
mvi SCBARRAY+14 call inb
jmp ITloop
# Message out phase. If there is no active message, but the target
# took us into this phase anyway, build a no-op message and send it.
#
p_mesgout:
mvi 0xa0 call scsisig # CDO|!IOO|MSGO
mvi 0x8 call mk_mesg # build NOP message
# Set up automatic PIO transfer from MSG_START. Bit 3 in
# SXFRCTL0 (SPIOEN) is already on.
#
mvi SINDEX,MSG_START+0
mov DINDEX,MSG_LEN
clr A
# When target asks for a byte, drop ATN if it's the last one in
# the message. Otherwise, keep going until the message is exhausted.
# (We can't use outb for this since it wants the input in SINDEX.)
#
# Keep an eye out for a phase change, in case the target issues
# a MESSAGE REJECT.
#
p_mesgout2:
test SSTAT0,0x2 jz p_mesgout2 # SPIORDY
test SSTAT1,0x10 jnz p_mesgout6 # PHASEMIS
cmp DINDEX,1 jne p_mesgout3 # last byte?
mvi CLRSINT1,0x40 # CLRATNO - drop ATN
# Write a byte to the SCSI bus. The AIC-7770 refuses to automatically
# send ACKs in automatic PIO or DMA mode unless you make sure that the
# "expected" bus phase in SCSISIGO matches the actual bus phase. This
# behaviour is completely undocumented and caused me several days of
# grief.
#
# After plugging in different drives to test with and using a longer
# SCSI cable, I found that I/O in Automatic PIO mode ceased to function,
# especially when transferring >1 byte. It seems to be much more stable
# if STCNT is set to one before the transfer, and SDONE (in SSTAT0) is
# polled for transfer completion - for both output _and_ input. The
# only theory I have is that SPIORDY doesn't drop right away when SCSIDATL
# is accessed (like the documentation says it does), and that on a longer
# cable run, the sequencer code was fast enough to loop back and see
# an SPIORDY that hadn't dropped yet.
#
p_mesgout3:
call one_stcnt
mov SCSIDATL,SINDIR
p_mesgout4:
test SSTAT0,0x4 jz p_mesgout4 # SDONE
dec DINDEX
inc A
cmp MSG_LEN,A jne p_mesgout2
# If the next bus phase after ATN drops is a message out, it means
# that the target is requesting that the last message(s) be resent.
#
p_mesgout5:
test SSTAT1,0x8 jnz p_mesgout6 # BUSFREE
test SSTAT1,0x1 jz p_mesgout5 # REQINIT
and A,0xe0,SCSISIGI # CDI|IOI|MSGI
cmp A,0xa0 jne p_mesgout6
mvi 0x10 call scsisig # ATNO - re-assert ATN
jmp ITloop
p_mesgout6:
mvi CLRSINT1,0x40 # CLRATNO - in case of PHASEMIS
clr MSG_FLAGS # no active msg
jmp ITloop
# Message in phase. Bytes are read using Automatic PIO mode, but not
# using inb. This alleviates a race condition, namely that if ATN had
# to be asserted under Automatic PIO mode, it had to beat the SCSI
# circuitry sending an ACK to the target. This showed up under heavy
# loads and really confused things, since ABORT commands wouldn't be
# seen by the drive after an IDENTIFY message in until it had changed
# to a data I/O phase.
#
p_mesgin:
mvi 0xe0 call scsisig # CDO|IOO|MSGO
mvi A call inb_first # read the 1st message byte
mvi REJBYTE,A # save it for the driver
cmp ALLZEROS,A jne p_mesgin1
# We got a "command complete" message, so put the SCB pointer
# into the Queue Out, and trigger a completion interrupt.
#
mov QOUTFIFO,SCBPTR
mvi INTSTAT,0x2 # CMDCMPLT
jmp p_mesgin_done
# Is it an extended message? We only support the synchronous data
# transfer request message, which will probably be in response to
# an SDTR message out from us. If it's not an SDTR, reject it -
# apparently this can be done after any message in byte, according
# to the SCSI-2 spec.
#
# XXX - we should really reject this if we didn't initiate the SDTR
# negotiation; this may cause problems with unusual devices.
#
p_mesgin1:
cmp A,1 jne p_mesgin2 # extended message code?
mvi A call inb_next
cmp A,3 jne p_mesginN # extended mesg length = 3
mvi A call inb_next
cmp A,1 jne p_mesginN # SDTR code
mvi ARG_1 call inb_next # xfer period
mvi ARG_2 call inb_next # REQ/ACK offset
mvi INTSTAT,SIGNAL_4 # call driver to convert
call ndx_sdtr # index sync config for target
mov DINDEX,SINDEX
mov DINDIR,RETURN_1 # save returned value
not A # turn off "need sdtr" flag
and NEEDSDTR,A
# Even though the SCSI-2 specification says that a device responding
# to our SDTR message should honor our parameters for transmitting
# to us, it doesn't seem to work too well in real life. In particular,
# a lot of CD-ROM and tape units don't function: try using the SDTR
# parameters the device sent us for both transmitting and receiving.
#
mov SCSIRATE,RETURN_1
jmp p_mesgin_done
# Is it a disconnect message? Set a flag in the SCB to remind us
# and await the bus going free.
#
p_mesgin2:
cmp A,4 jne p_mesgin3 # disconnect code?
or SCBARRAY+0,0x4 # set "disconnected" bit
jmp p_mesgin_done
# Save data pointers message? Copy working values into the SCB,
# usually in preparation for a disconnect.
#
p_mesgin3:
cmp A,2 jne p_mesgin4 # save data pointers code?
call sg_ram2scb
jmp p_mesgin_done
# Restore pointers message? Data pointers are recopied from the
# SCB anyway at the start of any DMA operation, so the only thing
# to copy is the scatter-gather values.
#
p_mesgin4:
cmp A,3 jne p_mesgin5 # restore pointers code?
call sg_scb2ram
jmp p_mesgin_done
# Identify message? For a reconnecting target, this tells us the lun
# that the reconnection is for - find the correct SCB and switch to it,
# clearing the "disconnected" bit so we don't "find" it by accident later.
#
p_mesgin5:
test A,0x80 jz p_mesgin6 # identify message?
test A,0x78 jnz p_mesginN # !DiscPriv|!LUNTAR|!Reserved
mov A call findSCB # switch to correct SCB
# If a active message is present after calling findSCB, then either it
# or the driver is trying to abort the command. Either way, something
# untoward has happened and we should just leave it alone.
#
test MSG_FLAGS,0x80 jnz p_mesgin_done
xor SCBARRAY+0,0x4 # clear disconnect bit in SCB
mvi RESELECT,0xc0 # make note of IDENTIFY
call sg_scb2ram # implied restore pointers
# required on reselect
jmp p_mesgin_done
# Message reject? If we have an outstanding SDTR negotiation, assume
# that it's a response from the target selecting asynchronous transfer,
# otherwise just ignore it since we have no clue what it pertains to.
#
# XXX - I don't have a device that responds this way. Does this code
# actually work?
#
p_mesgin6:
cmp A,7 jne p_mesgin7 # message reject code?
and FUNCTION1,0x70,SCSIID # outstanding SDTR message?
mov A,FUNCTION1
test NEEDSDTR,A jz p_mesgin_done # no - ignore rejection
call ndx_sdtr # note use of asynch xfer
mov DINDEX,SINDEX
clr DINDIR
not A # turn off "active sdtr" flag
and NEEDSDTR,A
clr SCSIRATE # select asynch xfer
jmp p_mesgin_done
# [ ADD MORE MESSAGE HANDLING HERE ]
#
p_mesgin7:
# We have no idea what this message in is, and there's no way
# to pass it up to the kernel, so we issue a message reject and
# hope for the best. Since we're now using manual PIO mode to
# read in the message, there should no longer be a race condition
# present when we assert ATN. In any case, rejection should be a
# rare occurrence - signal the driver when it happens.
#
p_mesginN:
or SINDEX,0x10,SIGSTATE # turn on ATNO
call scsisig
mvi INTSTAT,SIGNAL_1 # let driver know
mvi 0x7 call mk_mesg # MESSAGE REJECT message
p_mesgin_done:
call inb_last # ack & turn auto PIO back on
jmp ITloop
# Bus free phase. It might be useful to interrupt the device
# driver if we aren't expecting this. For now, make sure that
# ATN isn't being asserted and look for a new command.
#
p_busfree:
mvi CLRSINT1,0x40 # CLRATNO
clr SIGSTATE
jmp start
# Bcopy: number of bytes to transfer should be in A, DINDEX should
# contain the destination address, and SINDEX should contain the
# source address. All input parameters are trashed on return.
#
bcopy:
mov DINDIR,SINDIR
dec A
cmp ALLZEROS,A jne bcopy
ret
# Locking the driver out, build a one-byte message passed in SINDEX
# if there is no active message already. SINDEX is returned intact.
#
mk_mesg:
mvi SEQCTL,0x40 # PAUSEDIS
test MSG_FLAGS,0x80 jnz mk_mesg1 # active message?
mvi MSG_FLAGS,0x80 # if not, there is now
mvi MSG_LEN,1 # length = 1
mov MSG_START+0,SINDEX # 1-byte message
mk_mesg1:
clr SEQCTL # !PAUSEDIS
ret
# Input byte in Automatic PIO mode. The address to store the byte
# in should be in SINDEX. DINDEX will be used by this routine.
#
inb:
test SSTAT0,0x2 jz inb # SPIORDY
mov DINDEX,SINDEX
call one_stcnt # xfer one byte
mov DINDIR,SCSIDATL
inb1:
test SSTAT0,0x4 jz inb1 # SDONE - wait to "finish"
ret
# Carefully read data in Automatic PIO mode. I first tried this using
# Manual PIO mode, but it gave me continual underrun errors, probably
# indicating that I did something wrong, but I feel more secure leaving
# Automatic PIO on all the time.
#
# According to Adaptec's documentation, an ACK is not sent on input from
# the target until SCSIDATL is read from. So we wait until SCSIDATL is
# latched (the usual way), then read the data byte directly off the bus
# using SCSIBUSL. When we have pulled the ATN line, or we just want to
# acknowledge the byte, then we do a dummy read from SCISDATL. The SCSI
# spec guarantees that the target will hold the data byte on the bus until
# we send our ACK.
#
# The assumption here is that these are called in a particular sequence,
# and that REQ is already set when inb_first is called. inb_{first,next}
# use the same calling convention as inb.
#
inb_first:
mov DINDEX,SINDEX
mov DINDIR,SCSIBUSL ret # read byte directly from bus
inb_next:
mov DINDEX,SINDEX # save SINDEX
call one_stcnt # xfer one byte
mov NONE,SCSIDATL # dummy read from latch to ACK
inb_next1:
test SSTAT0,0x4 jz inb_next1 # SDONE
inb_next2:
test SSTAT0,0x2 jz inb_next2 # SPIORDY - wait for next byte
mov DINDIR,SCSIBUSL ret # read byte directly from bus
inb_last:
call one_stcnt # ACK with dummy read
mov NONE,SCSIDATL
inb_last1:
test SSTAT0,0x4 jz inb_last1 # wait for completion
ret
# Output byte in Automatic PIO mode. The byte to output should be
# in SINDEX. If DROPATN's high bit is set, then ATN will be dropped
# before the byte is output.
#
outb:
test SSTAT0,0x2 jz outb # SPIORDY
call one_stcnt # xfer one byte
test DROPATN,0x80 jz outb1
mvi CLRSINT1,0x40 # CLRATNO
clr DROPATN
outb1:
mov SCSIDATL,SINDEX
outb2:
test SSTAT0,0x4 jz outb2 # SDONE
ret
# Write the value "1" into the STCNT registers, for Automatic PIO
# transfers.
#
one_stcnt:
clr STCNT+2
clr STCNT+1
mvi STCNT+0,1 ret
# DMA data transfer. HADDR and HCNT must be loaded first, and
# SINDEX should contain the value to load DFCNTRL with - 0x3d for
# host->scsi, or 0x39 for scsi->host. The SCSI channel is cleared
# during initialization.
#
dma:
mov DFCNTRL,SINDEX
dma1:
dma2:
test SSTAT0,0x1 jnz dma3 # DMADONE
test SSTAT1,0x10 jz dma1 # PHASEMIS, ie. underrun
# We will be "done" DMAing when the transfer count goes to zero, or
# the target changes the phase (in light of this, it makes sense that
# the DMA circuitry doesn't ACK when PHASEMIS is active). If we are
# doing a SCSI->Host transfer, flush the data FIFO.
#
dma3:
test SINDEX,0x4 jnz dma5 # DIRECTION
and SINDEX,0xfe # mask out FIFORESET
or DFCNTRL,0x2,SINDEX # FIFOFLUSH
dma4:
test DFCNTRL,0x2 jnz dma4 # FIFOFLUSHACK
# Now shut the DMA enables off, and copy STCNT (ie. the underrun
# amount, if any) to the SCB registers; SG_COUNT will get copied to
# the SCB's residual S/G count field after sg_advance is called. Make
# sure that the DMA enables are actually off first lest we get an ILLSADDR.
#
dma5:
clr DFCNTRL # disable DMA
dma6:
test DFCNTRL,0x38 jnz dma6 # SCSIENACK|SDMAENACK|HDMAENACK
mvi A,3
mvi DINDEX,SCBARRAY+15
mvi STCNT call bcopy
ret
# Common SCSI initialization for selection and reselection. Expects
# the target SCSI ID to be in the upper four bits of SINDEX, and A's
# contents are stomped on return.
#
initialize:
clr SBLKCTL # channel A, !wide
and SCSIID,0xf0,SINDEX # target ID
and A,0x7,SCSICONF # SCSI_ID_A[210]
or SCSIID,A
# Esundry initialization.
#
clr DROPATN
clr SIGSTATE
# Turn on Automatic PIO mode now, before we expect to see an REQ
# from the target. It shouldn't hurt anything to leave it on. Set
# CLRCHN here before the target has entered a data transfer mode -
# with synchronous SCSI, if you do it later, you blow away some
# data in the SCSI FIFO that the target has already sent to you.
#
mvi SXFRCTL0,0xa # SPIOEN|CLRCHN
# Set SCSI bus parity checking and the selection timeout value,
# and enable the hardware selection timer. Set the SELTO interrupt
# to signal the driver.
#
and A,0x38,SCSICONF # PARITY_ENB_A|SEL_TIM_A[10]
or SXFRCTL1,0x4,A # ENSTIMER
mvi SIMODE1,0x84 # ENSELTIMO|ENSCSIPERR
# Initialize scatter-gather pointers by setting up the working copy
# in scratch RAM.
#
call sg_scb2ram
# Initialize SCSIRATE with the appropriate value for this target.
#
call ndx_sdtr
mov SCSIRATE,SINDIR
ret
# Assert that if we've been reselected, then we've seen an IDENTIFY
# message.
#
assert:
test RESELECT,0x80 jz assert1 # reselected?
test RESELECT,0x40 jnz assert1 # seen IDENTIFY?
mvi INTSTAT,SIGNAL_2 # no - cause a kernel panic
assert1:
ret
# Find out if disconnection is ok from the information the BIOS has left
# us. The target ID should be in the upper four bits of SINDEX; A will
# contain either 0x40 (disconnection ok) or 0x00 (diconnection not ok)
# on exit.
#
# This is the only place the target ID is limited to three bits, so we
# can use the FUNCTION1 register.
#
disconnect:
and FUNCTION1,0x70,SINDEX # strip off extra just in case
mov A,FUNCTION1
test DISC_DSB_A,A jz disconnect1 # bit nonzero if DISabled
clr A ret
disconnect1:
mvi A,0x40 ret
# Locate the SCB matching the target ID in SELID and the lun in the lower
# three bits of SINDEX, and switch the SCB to it. Have the kernel print
# a warning message if it can't be found - this seems to happen occasionally
# under high loads. Also, if not found, generate an ABORT message to the
# target.
#
findSCB:
and A,0x7,SINDEX # lun in lower three bits
or A,A,SELID # can I do this?
and A,0xf7 # only channel A implemented
clr SINDEX
findSCB1:
mov SCBPTR,SINDEX # switch to new SCB
cmp SCBARRAY+1,A jne findSCB2 # target ID/channel/lun match?
test SCBARRAY+0,0x4 jz findSCB2 # should be disconnected
ret
findSCB2:
inc SINDEX
cmp SINDEX,MAXSCB jne findSCB1
mvi INTSTAT,SIGNAL_3 # not found - signal kernel
mvi 0x6 call mk_mesg # ABORT message
or SINDEX,0x10,SIGSTATE # assert ATNO
call scsisig
ret
# Make a working copy of the scatter-gather parameters in the SCB.
#
sg_scb2ram:
mov SG_COUNT,SCBARRAY+2
mvi A,4
mvi DINDEX,SG_NEXT
mvi SCBARRAY+3 call bcopy
mvi SG_NOLOAD,0x80
test SCBARRAY+0,0x10 jnz sg_scb2ram1 # don't reload s/g?
clr SG_NOLOAD
sg_scb2ram1:
ret
# Copying RAM values back to SCB, for Save Data Pointers message.
#
sg_ram2scb:
mov SCBARRAY+2,SG_COUNT
mvi A,4
mvi DINDEX,SCBARRAY+3
mvi SG_NEXT call bcopy
and SCBARRAY+0,0xef,SCBARRAY+0
test SG_NOLOAD,0x80 jz sg_ram2scb1 # reload s/g?
or SCBARRAY+0,0x10
sg_ram2scb1:
ret
# Load a struct scatter if needed and set up the data address and
# length. If the working value of the SG count is nonzero, then
# we need to load a new set of values.
#
# This, like the above DMA, assumes a little-endian host data storage.
#
sg_load:
test SG_COUNT,0xff jz sg_load3 # SG being used?
test SG_NOLOAD,0x80 jnz sg_load3 # don't reload s/g?
clr HCNT+2
clr HCNT+1
mvi HCNT+0,SG_SIZEOF
mvi A,4
mvi DINDEX,HADDR
mvi SG_NEXT call bcopy
mvi DFCNTRL,0xd # HDMAEN|DIRECTION|FIFORESET
# Wait for DMA from host memory to data FIFO to complete, then disable
# DMA and wait for it to acknowledge that it's off.
#
sg_load1:
test DFSTATUS,0x8 jz sg_load1 # HDONE
clr DFCNTRL # disable DMA
sg_load2:
test DFCNTRL,0x8 jnz sg_load2 # HDMAENACK
# Copy data from FIFO into SCB data pointer and data count. This assumes
# that the struct scatterlist has this structure (this and sizeof(struct
# scatterlist) == 12 are asserted in aha274x.c):
#
# struct scatterlist {
# char *address; /* four bytes, little-endian order */
# ... /* four bytes, ignored */
# unsigned short length; /* two bytes, little-endian order */
# }
#
mov SCBARRAY+19,DFDAT # new data address
mov SCBARRAY+20,DFDAT
mov SCBARRAY+21,DFDAT
mov SCBARRAY+22,DFDAT
mov NONE,DFDAT # throw away four bytes
mov NONE,DFDAT
mov NONE,DFDAT
mov NONE,DFDAT
mov SCBARRAY+23,DFDAT
mov SCBARRAY+24,DFDAT
clr SCBARRAY+25
sg_load3:
ret
# Advance the scatter-gather pointers only IF NEEDED. If SG is enabled,
# and the SCSI transfer count is zero (note that this should be called
# right after a DMA finishes), then move the working copies of the SG
# pointer/length along. If the SCSI transfer count is not zero, then
# presumably the target is disconnecting - do not reload the SG values
# next time.
#
sg_advance:
test SG_COUNT,0xff jz sg_advance2 # s/g enabled?
test STCNT+0,0xff jnz sg_advance1 # SCSI transfer count nonzero?
test STCNT+1,0xff jnz sg_advance1
test STCNT+2,0xff jnz sg_advance1
clr SG_NOLOAD # reload s/g next time
dec SG_COUNT # one less segment to go
clr A # add sizeof(struct scatter)
add SG_NEXT+0,SG_SIZEOF,SG_NEXT+0
adc SG_NEXT+1,A,SG_NEXT+1
adc SG_NEXT+2,A,SG_NEXT+2
adc SG_NEXT+3,A,SG_NEXT+3
ret
sg_advance1:
mvi SG_NOLOAD,0x80 # don't reload s/g next time
sg_advance2:
ret
# Add the array base SYNCNEG to the target offset (the target address
# is in SCSIID), and return the result in SINDEX. The accumulator
# contains the 3->8 decoding of the target ID on return.
#
ndx_sdtr:
shr A,SCSIID,4
and A,0x7
add SINDEX,SYNCNEG,A
and FUNCTION1,0x70,SCSIID # 3-bit target address decode
mov A,FUNCTION1 ret
# If we need to negotiate transfer parameters, build the SDTR message
# starting at the address passed in SINDEX. DINDEX is modified on return.
#
mk_sdtr:
mov DINDEX,SINDEX # save SINDEX
call ndx_sdtr
test NEEDSDTR,A jnz mk_sdtr1 # do we need negotiation?
ret
mk_sdtr1:
mvi DINDIR,1 # extended message
mvi DINDIR,3 # extended message length = 3
mvi DINDIR,1 # SDTR code
mvi DINDIR,25 # REQ/ACK transfer period
mvi DINDIR,15 # REQ/ACK offset
add MSG_LEN,-MSG_START+0,DINDEX # update message length
ret
# Set SCSI bus control signal state. This also saves the last-written
# value into a location where the higher-level driver can read it - if
# it has to send an ABORT or RESET message, then it needs to know this
# so it can assert ATN without upsetting SCSISIGO. The new value is
# expected in SINDEX. Change the actual state last to avoid contention
# from the driver.
#
scsisig:
mov SIGSTATE,SINDEX
mov SCSISIGO,SINDEX ret
#define AHA274X_SEQ_VERSION "1.28"
0x04, 0x03, 0x12, 0x1a,
0x07, 0x9c, 0x00, 0x1e,
0xff, 0x9b, 0x90, 0x02,
0x00, 0xa1, 0xe6, 0x16,
0xff, 0x6a, 0x50, 0x02,
0xff, 0x6a, 0x34, 0x02,
0x00, 0xa1, 0xf8, 0x16,
0x07, 0xa1, 0x65, 0x02,
0x00, 0x65, 0x65, 0x00,
0x80, 0x65, 0xb5, 0x16,
0xff, 0x65, 0x64, 0x02,
0x00, 0x37, 0x0d, 0x18,
0x38, 0x6a, 0x47, 0x17,
0x48, 0x6a, 0x00, 0x00,
0x40, 0x0b, 0x16, 0x1a,
0x10, 0x0b, 0x0e, 0x1e,
0x40, 0x0b, 0x10, 0x1e,
0x00, 0x65, 0x16, 0x10,
0x10, 0x6a, 0x00, 0x00,
0x20, 0x0b, 0x13, 0x1e,
0x00, 0x19, 0xe6, 0x16,
0x80, 0x6a, 0x34, 0x00,
0xff, 0x6a, 0x00, 0x02,
0x08, 0x6a, 0x0c, 0x00,
0x08, 0x0c, 0xae, 0x1a,
0x01, 0x0c, 0x18, 0x1e,
0xe0, 0x03, 0x64, 0x02,
0x00, 0x6a, 0x22, 0x1c,
0x40, 0x64, 0x38, 0x1c,
0x80, 0x64, 0x4e, 0x1c,
0xc0, 0x64, 0x5b, 0x1c,
0xa0, 0x64, 0x5e, 0x1c,
0xe0, 0x64, 0x76, 0x1c,
0x01, 0x6a, 0x91, 0x00,
0x00, 0x6a, 0x52, 0x17,
0x00, 0x65, 0xf4, 0x16,
0x00, 0x65, 0x1c, 0x17,
0x03, 0x6a, 0x64, 0x00,
0x8c, 0x6a, 0x66, 0x00,
0xb7, 0x6a, 0xb1, 0x16,
0x03, 0x6a, 0x64, 0x00,
0x08, 0x6a, 0x66, 0x00,
0xb7, 0x6a, 0xb1, 0x16,
0x04, 0x6a, 0x64, 0x00,
0x88, 0x6a, 0x66, 0x00,
0xb3, 0x6a, 0xb1, 0x16,
0x3d, 0x6a, 0xd9, 0x16,
0x03, 0x6a, 0x64, 0x00,
0xb7, 0x6a, 0x66, 0x00,
0x08, 0x6a, 0xb1, 0x16,
0x04, 0x6a, 0x64, 0x00,
0xb3, 0x6a, 0x66, 0x00,
0x14, 0x6a, 0xb1, 0x16,
0x00, 0x65, 0x34, 0x17,
0xff, 0x51, 0xb2, 0x02,
0x00, 0x65, 0x18, 0x10,
0x40, 0x6a, 0x52, 0x17,
0x00, 0x65, 0xf4, 0x16,
0x00, 0x65, 0x1c, 0x17,
0x03, 0x6a, 0x64, 0x00,
0x8c, 0x6a, 0x66, 0x00,
0xb7, 0x6a, 0xb1, 0x16,
0x03, 0x6a, 0x64, 0x00,
0x08, 0x6a, 0x66, 0x00,
0xb7, 0x6a, 0xb1, 0x16,
0x04, 0x6a, 0x64, 0x00,
0x88, 0x6a, 0x66, 0x00,
0xb3, 0x6a, 0xb1, 0x16,
0x39, 0x6a, 0xd9, 0x16,
0x03, 0x6a, 0x64, 0x00,
0xb7, 0x6a, 0x66, 0x00,
0x08, 0x6a, 0xb1, 0x16,
0x04, 0x6a, 0x64, 0x00,
0xb3, 0x6a, 0x66, 0x00,
0x14, 0x6a, 0xb1, 0x16,
0x00, 0x65, 0x34, 0x17,
0xff, 0x51, 0xb2, 0x02,
0x00, 0x65, 0x18, 0x10,
0x80, 0x6a, 0x52, 0x17,
0x00, 0x65, 0xf4, 0x16,
0x03, 0x6a, 0x64, 0x00,
0x8c, 0x6a, 0x66, 0x00,
0xab, 0x6a, 0xb1, 0x16,
0x03, 0x6a, 0x64, 0x00,
0x08, 0x6a, 0x66, 0x00,
0xab, 0x6a, 0xb1, 0x16,
0x04, 0x6a, 0x64, 0x00,
0x88, 0x6a, 0x66, 0x00,
0xa7, 0x6a, 0xb1, 0x16,
0x3d, 0x6a, 0xd9, 0x16,
0x00, 0x65, 0x18, 0x10,
0xc0, 0x6a, 0x52, 0x17,
0xae, 0x6a, 0xbc, 0x16,
0x00, 0x65, 0x18, 0x10,
0xa0, 0x6a, 0x52, 0x17,
0x08, 0x6a, 0xb5, 0x16,
0x37, 0x6a, 0x65, 0x00,
0xff, 0x36, 0x66, 0x02,
0xff, 0x6a, 0x64, 0x02,
0x02, 0x0b, 0x63, 0x1e,
0x10, 0x0c, 0x73, 0x1a,
0x01, 0x66, 0x67, 0x18,
0x40, 0x6a, 0x0c, 0x00,
0x00, 0x65, 0xd6, 0x16,
0xff, 0x6c, 0x06, 0x02,
0x04, 0x0b, 0x69, 0x1e,
0xff, 0x66, 0x66, 0x06,
0x01, 0x64, 0x64, 0x06,
0x00, 0x36, 0x63, 0x18,
0x08, 0x0c, 0x73, 0x1a,
0x01, 0x0c, 0x6d, 0x1e,
0xe0, 0x03, 0x64, 0x02,
0xa0, 0x64, 0x73, 0x18,
0x10, 0x6a, 0x52, 0x17,
0x00, 0x65, 0x18, 0x10,
0x40, 0x6a, 0x0c, 0x00,
0xff, 0x6a, 0x35, 0x02,
0x00, 0x65, 0x18, 0x10,
0xe0, 0x6a, 0x52, 0x17,
0x64, 0x6a, 0xc2, 0x16,
0x00, 0x6a, 0x31, 0x00,
0x00, 0x6a, 0x7d, 0x18,
0xff, 0x90, 0x9d, 0x02,
0x02, 0x6a, 0x91, 0x00,
0x00, 0x65, 0xac, 0x10,
0x01, 0x64, 0x8c, 0x18,
0x64, 0x6a, 0xc4, 0x16,
0x03, 0x64, 0xa8, 0x18,
0x64, 0x6a, 0xc4, 0x16,
0x01, 0x64, 0xa8, 0x18,
0x4c, 0x6a, 0xc4, 0x16,
0x4d, 0x6a, 0xc4, 0x16,
0x41, 0x6a, 0x91, 0x00,
0x00, 0x65, 0x42, 0x17,
0xff, 0x65, 0x66, 0x02,
0xff, 0x4c, 0x6d, 0x02,
0xff, 0x64, 0x64, 0x04,
0x00, 0x4f, 0x4f, 0x02,
0xff, 0x4c, 0x04, 0x02,
0x00, 0x65, 0xac, 0x10,
0x04, 0x64, 0x8f, 0x18,
0x04, 0xa0, 0xa0, 0x00,
0x00, 0x65, 0xac, 0x10,
0x02, 0x64, 0x92, 0x18,
0x00, 0x65, 0x14, 0x17,
0x00, 0x65, 0xac, 0x10,
0x03, 0x64, 0x95, 0x18,
0x00, 0x65, 0x0c, 0x17,
0x00, 0x65, 0xac, 0x10,
0x80, 0x64, 0x9d, 0x1e,
0x78, 0x64, 0xa8, 0x1a,
0x00, 0x64, 0xfd, 0x16,
0x80, 0x35, 0xac, 0x1a,
0x04, 0xa0, 0xa0, 0x04,
0xc0, 0x6a, 0x34, 0x00,
0x00, 0x65, 0x0c, 0x17,
0x00, 0x65, 0xac, 0x10,
0x07, 0x64, 0xa8, 0x18,
0x70, 0x05, 0x6e, 0x02,
0xff, 0x6e, 0x64, 0x02,
0x00, 0x4f, 0xac, 0x1e,
0x00, 0x65, 0x42, 0x17,
0xff, 0x65, 0x66, 0x02,
0xff, 0x6a, 0x6d, 0x02,
0xff, 0x64, 0x64, 0x04,
0x00, 0x4f, 0x4f, 0x02,
0xff, 0x6a, 0x04, 0x02,
0x00, 0x65, 0xac, 0x10,
0x10, 0x4e, 0x65, 0x00,
0x00, 0x65, 0x52, 0x17,
0x11, 0x6a, 0x91, 0x00,
0x07, 0x6a, 0xb5, 0x16,
0x00, 0x65, 0xca, 0x16,
0x00, 0x65, 0x18, 0x10,
0x40, 0x6a, 0x0c, 0x00,
0xff, 0x6a, 0x4e, 0x02,
0x00, 0x65, 0x00, 0x10,
0xff, 0x6c, 0x6d, 0x02,
0xff, 0x64, 0x64, 0x06,
0x00, 0x6a, 0xb1, 0x18,
0xff, 0x6a, 0x6a, 0x03,
0x40, 0x6a, 0x60, 0x00,
0x80, 0x35, 0xba, 0x1a,
0x80, 0x6a, 0x35, 0x00,
0x01, 0x6a, 0x36, 0x00,
0xff, 0x65, 0x37, 0x02,
0xff, 0x6a, 0x60, 0x02,
0xff, 0x6a, 0x6a, 0x03,
0x02, 0x0b, 0xbc, 0x1e,
0xff, 0x65, 0x66, 0x02,
0x00, 0x65, 0xd6, 0x16,
0xff, 0x06, 0x6d, 0x02,
0x04, 0x0b, 0xc0, 0x1e,
0xff, 0x6a, 0x6a, 0x03,
0xff, 0x65, 0x66, 0x02,
0xff, 0x12, 0x6d, 0x03,
0xff, 0x65, 0x66, 0x02,
0x00, 0x65, 0xd6, 0x16,
0xff, 0x06, 0x6a, 0x02,
0x04, 0x0b, 0xc7, 0x1e,
0x02, 0x0b, 0xc8, 0x1e,
0xff, 0x12, 0x6d, 0x03,
0x00, 0x65, 0xd6, 0x16,
0xff, 0x06, 0x6a, 0x02,
0x04, 0x0b, 0xcc, 0x1e,
0xff, 0x6a, 0x6a, 0x03,
0x02, 0x0b, 0xce, 0x1e,
0x00, 0x65, 0xd6, 0x16,
0x80, 0x30, 0xd3, 0x1e,
0x40, 0x6a, 0x0c, 0x00,
0xff, 0x6a, 0x30, 0x02,
0xff, 0x65, 0x06, 0x02,
0x04, 0x0b, 0xd4, 0x1e,
0xff, 0x6a, 0x6a, 0x03,
0xff, 0x6a, 0x0a, 0x02,
0xff, 0x6a, 0x09, 0x02,
0x01, 0x6a, 0x08, 0x01,
0xff, 0x65, 0x93, 0x02,
0x01, 0x0b, 0xdc, 0x1a,
0x10, 0x0c, 0xda, 0x1e,
0x04, 0x65, 0xe0, 0x1a,
0xfe, 0x65, 0x65, 0x02,
0x02, 0x65, 0x93, 0x00,
0x02, 0x93, 0xdf, 0x1a,
0xff, 0x6a, 0x93, 0x02,
0x38, 0x93, 0xe1, 0x1a,
0x03, 0x6a, 0x64, 0x00,
0xaf, 0x6a, 0x66, 0x00,
0x08, 0x6a, 0xb1, 0x16,
0xff, 0x6a, 0x6a, 0x03,
0xff, 0x6a, 0x1f, 0x02,
0xf0, 0x65, 0x05, 0x02,
0x07, 0x5a, 0x64, 0x02,
0x00, 0x05, 0x05, 0x00,
0xff, 0x6a, 0x30, 0x02,
0xff, 0x6a, 0x4e, 0x02,
0x0a, 0x6a, 0x01, 0x00,
0x38, 0x5a, 0x64, 0x02,
0x04, 0x64, 0x02, 0x00,
0x84, 0x6a, 0x11, 0x00,
0x00, 0x65, 0x0c, 0x17,
0x00, 0x65, 0x42, 0x17,
0xff, 0x6c, 0x04, 0x02,
0xff, 0x6a, 0x6a, 0x03,
0x80, 0x34, 0xf7, 0x1e,
0x40, 0x34, 0xf7, 0x1a,
0x21, 0x6a, 0x91, 0x00,
0xff, 0x6a, 0x6a, 0x03,
0x70, 0x65, 0x6e, 0x02,
0xff, 0x6e, 0x64, 0x02,
0x00, 0x32, 0xfc, 0x1e,
0xff, 0x6a, 0x64, 0x03,
0x40, 0x6a, 0x64, 0x01,
0x07, 0x65, 0x64, 0x02,
0x00, 0x19, 0x64, 0x00,
0xf7, 0x64, 0x64, 0x02,
0xff, 0x6a, 0x65, 0x02,
0xff, 0x65, 0x90, 0x02,
0x00, 0xa1, 0x05, 0x19,
0x04, 0xa0, 0x05, 0x1f,
0xff, 0x6a, 0x6a, 0x03,
0x01, 0x65, 0x65, 0x06,
0x04, 0x65, 0x01, 0x19,
0x31, 0x6a, 0x91, 0x00,
0x06, 0x6a, 0xb5, 0x16,
0x10, 0x4e, 0x65, 0x00,
0x00, 0x65, 0x52, 0x17,
0xff, 0x6a, 0x6a, 0x03,
0xff, 0xa2, 0x51, 0x02,
0x04, 0x6a, 0x64, 0x00,
0x52, 0x6a, 0x66, 0x00,
0xa3, 0x6a, 0xb1, 0x16,
0x80, 0x6a, 0x50, 0x00,
0x10, 0xa0, 0x13, 0x1b,
0xff, 0x6a, 0x50, 0x02,
0xff, 0x6a, 0x6a, 0x03,
0xff, 0x51, 0xa2, 0x02,
0x04, 0x6a, 0x64, 0x00,
0xa3, 0x6a, 0x66, 0x00,
0x52, 0x6a, 0xb1, 0x16,
0xef, 0xa0, 0xa0, 0x02,
0x80, 0x50, 0x1b, 0x1f,
0x10, 0xa0, 0xa0, 0x00,
0xff, 0x6a, 0x6a, 0x03,
0xff, 0x51, 0x33, 0x1f,
0x80, 0x50, 0x33, 0x1b,
0xff, 0x6a, 0x8e, 0x02,
0xff, 0x6a, 0x8d, 0x02,
0x0c, 0x6a, 0x8c, 0x00,
0x04, 0x6a, 0x64, 0x00,
0x88, 0x6a, 0x66, 0x00,
0x52, 0x6a, 0xb1, 0x16,
0x0d, 0x6a, 0x93, 0x00,
0x08, 0x94, 0x25, 0x1f,
0xff, 0x6a, 0x93, 0x02,
0x08, 0x93, 0x27, 0x1b,
0xff, 0x99, 0xb3, 0x02,
0xff, 0x99, 0xb4, 0x02,
0xff, 0x99, 0xb5, 0x02,
0xff, 0x99, 0xb6, 0x02,
0xff, 0x99, 0x6a, 0x02,
0xff, 0x99, 0x6a, 0x02,
0xff, 0x99, 0x6a, 0x02,
0xff, 0x99, 0x6a, 0x02,
0xff, 0x99, 0xb7, 0x02,
0xff, 0x99, 0xb8, 0x02,
0xff, 0x6a, 0xb9, 0x02,
0xff, 0x6a, 0x6a, 0x03,
0xff, 0x51, 0x41, 0x1f,
0xff, 0x08, 0x40, 0x1b,
0xff, 0x09, 0x40, 0x1b,
0xff, 0x0a, 0x40, 0x1b,
0xff, 0x6a, 0x50, 0x02,
0xff, 0x51, 0x51, 0x06,
0xff, 0x6a, 0x64, 0x02,
0x0c, 0x52, 0x52, 0x06,
0x00, 0x53, 0x53, 0x08,
0x00, 0x54, 0x54, 0x08,
0x00, 0x55, 0x55, 0x08,
0xff, 0x6a, 0x6a, 0x03,
0x80, 0x6a, 0x50, 0x00,
0xff, 0x6a, 0x6a, 0x03,
0x4c, 0x05, 0x64, 0x0a,
0x07, 0x64, 0x64, 0x02,
0x20, 0x64, 0x65, 0x06,
0x70, 0x05, 0x6e, 0x02,
0xff, 0x6e, 0x64, 0x03,
0xff, 0x65, 0x66, 0x02,
0x00, 0x65, 0x42, 0x17,
0x00, 0x4f, 0x4b, 0x1b,
0xff, 0x6a, 0x6a, 0x03,
0x01, 0x6a, 0x6d, 0x00,
0x03, 0x6a, 0x6d, 0x00,
0x01, 0x6a, 0x6d, 0x00,
0x19, 0x6a, 0x6d, 0x00,
0x0f, 0x6a, 0x6d, 0x00,
0xc9, 0x66, 0x36, 0x06,
0xff, 0x6a, 0x6a, 0x03,
0xff, 0x65, 0x4e, 0x02,
0xff, 0x65, 0x03, 0x03,
/*
* Adaptec 274x device driver for Linux.
* Copyright (c) 1994 The University of Calgary Department of Computer Science.
*
* 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 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* Comments are started by `#' and continue to the end of the line; lines
* may be of the form:
*
* <label>*
* <label>* <undef-sym> = <value>
* <label>* <opcode> <operand>*
*
* A <label> is an <undef-sym> ending in a colon. Spaces, tabs, and commas
* are token separators.
*/
#define _POSIX_SOURCE 1
#define _POSIX_C_SOURCE 2
#include <ctype.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#define MEMORY 512 /* 2^9 29-bit words */
#define MAXLINE 1024
#define MAXTOKEN 32
#define ADOTOUT "a.out"
#define NOVALUE -1
/*
* AIC-7770 register definitions
*/
#define R_SINDEX 0x65
#define R_ALLONES 0x69
#define R_ALLZEROS 0x6a
#define R_NONE 0x6a
static
char sccsid[] =
"@(#)aic7770.c 1.10 94/07/22 jda";
int debug;
int lineno, LC;
char *filename;
FILE *ifp, *ofp;
unsigned char M[MEMORY][4];
void error(char *s)
{
fprintf(stderr, "%s: %s at line %d\n", filename, s, lineno);
exit(EXIT_FAILURE);
}
void *Malloc(size_t size)
{
void *p = malloc(size);
if (!p)
error("out of memory");
return(p);
}
void *Realloc(void *ptr, size_t size)
{
void *p = realloc(ptr, size);
if (!p)
error("out of memory");
return(p);
}
char *Strdup(char *s)
{
char *p = (char *)Malloc(strlen(s) + 1);
strcpy(p, s);
return(p);
}
typedef struct sym_t {
struct sym_t *next; /* MUST BE FIRST */
char *name;
int value;
int npatch, *patch;
} sym_t;
sym_t *head;
void define(char *name, int value)
{
sym_t *p, *q;
for (p = head, q = (sym_t *)&head; p; p = p->next) {
if (!strcmp(p->name, name))
error("redefined symbol");
q = p;
}
p = q->next = (sym_t *)Malloc(sizeof(sym_t));
p->next = NULL;
p->name = Strdup(name);
p->value = value;
p->npatch = 0;
p->patch = NULL;
if (debug) {
fprintf(stderr, "\"%s\" ", p->name);
if (p->value != NOVALUE)
fprintf(stderr, "defined as 0x%x\n", p->value);
else
fprintf(stderr, "undefined\n");
}
}
sym_t *lookup(char *name)
{
sym_t *p;
for (p = head; p; p = p->next)
if (!strcmp(p->name, name))
return(p);
return(NULL);
}
void patch(sym_t *p, int location)
{
p->npatch += 1;
p->patch = (int *)Realloc(p->patch, p->npatch * sizeof(int *));
p->patch[p->npatch - 1] = location;
}
void backpatch(void)
{
int i;
sym_t *p;
for (p = head; p; p = p->next) {
if (p->value == NOVALUE) {
fprintf(stderr,
"%s: undefined symbol \"%s\"\n",
filename, p->name);
exit(EXIT_FAILURE);
}
if (p->npatch) {
if (debug)
fprintf(stderr,
"\"%s\" (0x%x) patched at",
p->name, p->value);
for (i = 0; i < p->npatch; i++) {
M[p->patch[i]][0] &= ~1;
M[p->patch[i]][0] |= ((p->value >> 8) & 1);
M[p->patch[i]][1] = p->value & 0xff;
if (debug)
fprintf(stderr, " 0x%x", p->patch[i]);
}
if (debug)
fputc('\n', stderr);
}
}
}
/*
* Output words in byte-reversed order (least significant first)
* since the sequencer RAM is loaded that way.
*/
void output(FILE *fp)
{
int i;
for (i = 0; i < LC; i++)
fprintf(fp, "\t0x%02x, 0x%02x, 0x%02x, 0x%02x,\n",
M[i][3],
M[i][2],
M[i][1],
M[i][0]);
}
char **getl(int *n)
{
int i;
char *p;
static char buf[MAXLINE];
static char *a[MAXTOKEN];
i = 0;
while (fgets(buf, sizeof(buf), ifp)) {
lineno += 1;
if (buf[strlen(buf)-1] != '\n')
error("line too long");
p = strchr(buf, '#');
if (p)
*p = '\0';
for (p = strtok(buf, ", \t\n"); p; p = strtok(NULL, ", \t\n"))
if (i < MAXTOKEN-1)
a[i++] = p;
else
error("too many tokens");
if (i) {
*n = i;
return(a);
}
}
return(NULL);
}
#define A 0x8000 /* `A'ccumulator ok */
#define I 0x4000 /* use as immediate value */
#define SL 0x2000 /* shift left */
#define SR 0x1000 /* shift right */
#define RL 0x0800 /* rotate left */
#define RR 0x0400 /* rotate right */
#define LO 0x8000 /* lookup: ori-{jmp,jc,jnc,call} */
#define LA 0x4000 /* lookup: and-{jz,jnz} */
#define LX 0x2000 /* lookup: xor-{je,jne} */
#define NA -1 /* not applicable */
struct {
char *name;
int n; /* number of operands, including opcode */
unsigned int op; /* immediate or L?|pos_from_0 */
unsigned int dest; /* NA, pos_from_0, or I|immediate */
unsigned int src; /* NA, pos_from_0, or I|immediate */
unsigned int imm; /* pos_from_0, A|pos_from_0, or I|immediate */
unsigned int addr; /* NA or pos_from_0 */
int fmt; /* instruction format - 1, 2, or 3 */
} instr[] = {
/*
* N OP DEST SRC IMM ADDR FMT
*/
"mov", 3, 1, 1, 2, I|0xff, NA, 1,
"mov", 4, LO|2, NA, 1, I|0, 3, 3,
"mvi", 3, 0, 1, I|R_ALLZEROS, A|2, NA, 1,
"mvi", 4, LO|2, NA, I|R_ALLZEROS, 1, 3, 3,
"not", 2, 2, 1, 1, I|0xff, NA, 1,
"not", 3, 2, 1, 2, I|0xff, NA, 1,
"and", 3, 1, 1, 1, A|2, NA, 1,
"and", 4, 1, 1, 3, A|2, NA, 1,
"or", 3, 0, 1, 1, A|2, NA, 1,
"or", 4, 0, 1, 3, A|2, NA, 1,
"or", 5, LO|3, NA, 1, 2, 4, 3,
"xor", 3, 2, 1, 1, A|2, NA, 1,
"xor", 4, 2, 1, 3, A|2, NA, 1,
"nop", 1, 1, I|R_NONE, I|R_ALLZEROS, I|0xff, NA, 1,
"inc", 2, 3, 1, 1, I|1, NA, 1,
"inc", 3, 3, 1, 2, I|1, NA, 1,
"dec", 2, 3, 1, 1, I|0xff, NA, 1,
"dec", 3, 3, 1, 2, I|0xff, NA, 1,
"jmp", 2, LO|0, NA, I|R_SINDEX, I|0, 1, 3,
"jc", 2, LO|0, NA, I|R_SINDEX, I|0, 1, 3,
"jnc", 2, LO|0, NA, I|R_SINDEX, I|0, 1, 3,
"call", 2, LO|0, NA, I|R_SINDEX, I|0, 1, 3,
"test", 5, LA|3, NA, 1, A|2, 4, 3,
"cmp", 5, LX|3, NA, 1, A|2, 4, 3,
"ret", 1, 1, I|R_NONE, I|R_ALLZEROS, I|0xff, NA, 1,
"clc", 1, 3, I|R_NONE, I|R_ALLZEROS, I|1, NA, 1,
"clc", 4, 3, 2, I|R_ALLZEROS, A|3, NA, 1,
"stc", 1, 3, I|R_NONE, I|R_ALLONES, I|1, NA, 1,
"stc", 2, 3, 1, I|R_ALLONES, I|1, NA, 1,
"add", 3, 3, 1, 1, A|2, NA, 1,
"add", 4, 3, 1, 3, A|2, NA, 1,
"adc", 3, 4, 1, 1, A|2, NA, 1,
"adc", 4, 4, 1, 3, A|2, NA, 1,
"shl", 3, 5, 1, 1, SL|2, NA, 2,
"shl", 4, 5, 1, 2, SL|3, NA, 2,
"shr", 3, 5, 1, 1, SR|2, NA, 2,
"shr", 4, 5, 1, 2, SR|3, NA, 2,
"rol", 3, 5, 1, 1, RL|2, NA, 2,
"rol", 4, 5, 1, 2, RL|3, NA, 2,
"ror", 3, 5, 1, 1, RR|2, NA, 2,
"ror", 4, 5, 1, 2, RR|3, NA, 2,
/*
* Extensions (note also that mvi allows A)
*/
"clr", 2, 1, 1, I|R_ALLZEROS, I|0xff, NA, 1,
0
};
int eval_operand(char **a, int spec)
{
int i;
unsigned int want = spec & (LO|LA|LX);
static struct {
unsigned int what;
char *name;
int value;
} jmptab[] = {
LO, "jmp", 8,
LO, "jc", 9,
LO, "jnc", 10,
LO, "call", 11,
LA, "jz", 15,
LA, "jnz", 13,
LX, "je", 14,
LX, "jne", 12,
};
spec &= ~(LO|LA|LX);
for (i = 0; i < sizeof(jmptab)/sizeof(jmptab[0]); i++)
if (jmptab[i].what == want &&
!strcmp(jmptab[i].name, a[spec]))
{
return(jmptab[i].value);
}
if (want)
error("invalid jump");
return(spec); /* "case 0" - no flags set */
}
int eval_sdi(char **a, int spec)
{
sym_t *p;
unsigned val;
if (spec == NA)
return(NA);
switch (spec & (A|I|SL|SR|RL|RR)) {
case SL:
case SR:
case RL:
case RR:
if (isdigit(*a[spec &~ (SL|SR|RL|RR)]))
val = strtol(a[spec &~ (SL|SR|RL|RR)], NULL, 0);
else {
p = lookup(a[spec &~ (SL|SR|RL|RR)]);
if (!p)
error("undefined symbol used");
val = p->value;
}
switch (spec & (SL|SR|RL|RR)) { /* blech */
case SL:
if (val > 7)
return(0xf0);
return(((val % 8) << 4) |
(val % 8));
case SR:
if (val > 7)
return(0xf0);
return(((val % 8) << 4) |
(1 << 3) |
((8 - (val % 8)) % 8));
case RL:
return(val % 8);
case RR:
return((8 - (val % 8)) % 8);
}
case I:
return(spec &~ I);
case A:
/*
* An immediate field of zero selects
* the accumulator. Vigorously object
* if zero is given otherwise - it's
* most likely an error.
*/
spec &= ~A;
if (!strcmp("A", a[spec]))
return(0);
if (isdigit(*a[spec]) &&
strtol(a[spec], NULL, 0) == 0)
{
error("immediate value of zero selects accumulator");
}
/* falls through */
case 0:
if (isdigit(*a[spec]))
return(strtol(a[spec], NULL, 0));
p = lookup(a[spec]);
if (p)
return(p->value);
error("undefined symbol used");
}
return(NA); /* shut the compiler up */
}
int eval_addr(char **a, int spec)
{
sym_t *p;
if (spec == NA)
return(NA);
if (isdigit(*a[spec]))
return(strtol(a[spec], NULL, 0));
p = lookup(a[spec]);
if (p) {
if (p->value != NOVALUE)
return(p->value);
patch(p, LC);
} else {
define(a[spec], NOVALUE);
p = lookup(a[spec]);
patch(p, LC);
}
return(NA); /* will be patched in later */
}
int crack(char **a, int n)
{
int i;
int I_imm, I_addr;
int I_op, I_dest, I_src, I_ret;
/*
* Check for "ret" at the end of the line; remove
* it unless it's "ret" alone - we still want to
* look it up in the table.
*/
I_ret = (strcmp(a[n-1], "ret") ? 0 : !0);
if (I_ret && n > 1)
n -= 1;
for (i = 0; instr[i].name; i++) {
/*
* Look for match in table given constraints,
* currently just the name and the number of
* operands.
*/
if (!strcmp(instr[i].name, *a) && instr[i].n == n)
break;
}
if (!instr[i].name)
error("unknown opcode or wrong number of operands");
I_op = eval_operand(a, instr[i].op);
I_src = eval_sdi(a, instr[i].src);
I_imm = eval_sdi(a, instr[i].imm);
I_dest = eval_sdi(a, instr[i].dest);
I_addr = eval_addr(a, instr[i].addr);
switch (instr[i].fmt) {
case 1:
case 2:
M[LC][0] = (I_op << 1) | I_ret;
M[LC][1] = I_dest;
M[LC][2] = I_src;
M[LC][3] = I_imm;
break;
case 3:
if (I_ret)
error("illegal use of \"ret\"");
M[LC][0] = (I_op << 1) | ((I_addr >> 8) & 1);
M[LC][1] = I_addr & 0xff;
M[LC][2] = I_src;
M[LC][3] = I_imm;
break;
}
return(1); /* no two-byte instructions yet */
}
#undef SL
#undef SR
#undef RL
#undef RR
#undef LX
#undef LA
#undef LO
#undef I
#undef A
void assemble(void)
{
int n;
char **a;
sym_t *p;
while ((a = getl(&n))) {
while (a[0][strlen(*a)-1] == ':') {
a[0][strlen(*a)-1] = '\0';
p = lookup(*a);
if (p)
p->value = LC;
else
define(*a, LC);
a += 1;
n -= 1;
}
if (!n) /* line was all labels */
continue;
if (n == 3 && !strcmp("VERSION", *a))
fprintf(ofp, "#define %s \"%s\"\n", a[1], a[2]);
else {
if (n == 3 && !strcmp("=", a[1]))
define(*a, strtol(a[2], NULL, 0));
else
LC += crack(a, n);
}
}
backpatch();
output(ofp);
if (debug)
output(stderr);
}
int main(int argc, char **argv)
{
int c;
while ((c = getopt(argc, argv, "dho:")) != EOF) {
switch (c) {
case 'd':
debug = !0;
break;
case 'o':
ofp = fopen(optarg, "w");
if (!ofp) {
perror(optarg);
exit(EXIT_FAILURE);
}
break;
case 'h':
printf("usage: %s [-d] [-ooutput] input\n", *argv);
exit(EXIT_SUCCESS);
case NULL:
/*
* An impossible option to shut the compiler
* up about sccsid[].
*/
exit((int)sccsid);
default:
exit(EXIT_FAILURE);
}
}
if (argc - optind != 1) {
fprintf(stderr, "%s: must have one input file\n", *argv);
exit(EXIT_FAILURE);
}
filename = argv[optind];
ifp = fopen(filename, "r");
if (!ifp) {
perror(filename);
exit(EXIT_FAILURE);
}
if (!ofp) {
ofp = fopen(ADOTOUT, "w");
if (!ofp) {
perror(ADOTOUT);
exit(EXIT_FAILURE);
}
}
assemble();
exit(EXIT_SUCCESS);
}
......@@ -39,10 +39,18 @@
#include "aha1740.h"
#endif
#ifdef CONFIG_SCSI_AHA274X
#include "aha274x.h"
#endif
#ifdef CONFIG_SCSI_BUSLOGIC
#include "buslogic.h"
#endif
#ifdef CONFIG_SCSI_U14_34F
#include "u14-34f.h"
#endif
#ifdef CONFIG_SCSI_FUTURE_DOMAIN
#include "fdomain.h"
#endif
......@@ -116,6 +124,9 @@ Scsi_Host_Template * scsi_hosts = NULL;
static Scsi_Host_Template builtin_scsi_hosts[] =
{
#ifdef CONFIG_SCSI_U14_34F
ULTRASTOR_14_34F,
#endif
#ifdef CONFIG_SCSI_ULTRASTOR
ULTRASTOR_14F,
#endif
......@@ -132,6 +143,9 @@ static Scsi_Host_Template builtin_scsi_hosts[] =
#ifdef CONFIG_SCSI_AHA1740
AHA1740,
#endif
#ifdef CONFIG_SCSI_AHA274X
AHA274X,
#endif
#ifdef CONFIG_SCSI_FUTURE_DOMAIN
FDOMAIN_16X0,
#endif
......
/*
* u14-34f.c - Low-level SCSI driver for UltraStor 14F/34F
*
* 28 Oct 1994 rev. 1.09 for linux 1.1.58
*
* This driver is a total replacement of the original UltraStor
* scsi driver, but it supports ONLY the 14F and 34F boards.
* It can be configured in the same kernel in which the original
* ultrastor driver is configured to allow the original U24F
* support.
*
* Multiple U14F and/or U34F host adapters are supported.
*
* Released by Dario Ballabio (Dario_Ballabio@milano.europe.dg.com)
*
* WARNING: if your 14F board has old firmware revision (see below)
* keep the following statement, otherwise comment it.
*/
#if 0
#define HAVE_OLD_U14F_FIRMWARE
#endif
/*
* The UltraStor 14F, 24F, and 34F are a family of intelligent, high
* performance SCSI-2 host adapters.
* Here is the scoop on the various models:
*
* 14F - ISA first-party DMA HA with floppy support and WD1003 emulation.
* 24F - EISA Bus Master HA with floppy support and WD1003 emulation.
* 34F - VL-Bus Bus Master HA with floppy support (no WD1003 emulation).
*
* This code has been tested with up to two U14F on boards, using both
* firmware 28004-005/38004-004 (BIOS rev. 2.00) and the latest firmware
* 28004-006/38004-005 (BIOS rev. 2.01).
*
* The latest firmware is required in order to get reliable operations when
* clustering is enabled. ENABLE_CLUSTERING provides a performance increase
* up to 50% on sequential access.
*
* Since the Scsi_Host_Template structure is shared among all 14F and 34F,
* the last setting of use_clustering is in effect for all of these boards.
*
* Here a sample configuration using two U14F boards:
*
U14F0: PORT 0x330, BIOS 0xc8000, IRQ 11, DMA 5, SG 33, Mbox 16, CmdLun 2, C1.
U14F1: PORT 0x340, BIOS 0x00000, IRQ 10, DMA 6, SG 33, Mbox 16, CmdLun 2, C1.
*
* The boot controller must have its BIOS enabled, while other boards can
* have their BIOS disabled, or enabled to an higher address.
* Boards are named Ux4F0, Ux4F1..., according to the port address order in
* the isa_io_port[] array.
*
* The following facts are based on real testing results (not on
* documentation) on the above U14F board.
*
* - The U14F board should be jumpered for bus on time less or equal to 7
* microseconds, while the default is 11 microseconds. This is order to
* get acceptable performance while using floppy drive and hard disk
* together. The jumpering for 7 microseconds is: JP13 pin 15-16,
* JP14 pin 7-8 and pin 9-10.
* The reduction has a little impact on scsi performance.
*
* - If scsi bus length exceeds 3m., the scsi bus speed needs to be reduced
* from 10Mhz to 5Mhz (do this by inserting a jumper on JP13 pin 7-8).
*
* - If U14F on board firmware is older than 28004-006/38004-005,
* the U14F board is unable to provide reliable operations if the scsi
* request length exceeds 16Kbyte. When this length is exceeded the
* behavior is:
* - adapter_status equal 0x96 or 0xa3 or 0x93 or 0x94;
* - adapter_status equal 0 and target_status equal 2 on for all targets
* in the next operation following the reset.
* This sequence takes a long time (>3 seconds), so in the mantime
* the SD_TIMEOUT in sd.c could expire giving rise to scsi aborts
* (SD_TIMEOUT has been increased from 3 to 6 seconds in 1.1.31).
* Because of this I had to DISABLE_CLUSTERING and to work around the
* bus reset in the interrupt service routine, returning DID_BUS_BUSY
* so that the operations are retried without complains from the scsi.c
* code.
* Any reset of the scsi bus is going to kill tape operations, since
* no retry is allowed for tapes. Bus resest are more likely when the
* scsi bus is under heavy load.
* Requests using scatter/gather have a maximum length of 16 x 1024 bytes
* when DISABLE_CLUSTERING is in effect, but unscattered requests could be
* larger than 16Kbyte.
*
* The new firmware has fixed all the above problems and has been tested
* with up to 33 scatter/gather lists.
*
*/
#include <linux/string.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/ioport.h>
#include <asm/io.h>
#include <asm/system.h>
#include "../block/blk.h"
#include "scsi.h"
#include "hosts.h"
#include "sd.h"
#include <asm/dma.h>
#include <asm/irq.h>
#include "u14-34f.h"
/* Values for the PRODUCT_ID ports for the 14F */
#define U14F_PRODUCT_ID1 0x56
#define U14F_PRODUCT_ID2 0x40 /* NOTE: Only upper nibble is used */
/* Subversion values */
#define U14F 0
#define U34F 1
#define OP_HOST_ADAPTER 0x1
#define OP_SCSI 0x2
#define OP_RESET 0x4
#define DTD_SCSI 0x0
#define DTD_IN 0x1
#define DTD_OUT 0x2
#define DTD_NONE 0x3
#define HA_CMD_INQUIRY 0x1
#define HA_CMD_SELF_DIAG 0x2
#define HA_CMD_READ_BUFF 0x3
#define HA_CMD_WRITE_BUFF 0x4
#if defined (HAVE_OLD_U14F_FIRMWARE)
#define U14F_MAX_SGLIST 16
#define U14F_CLUSTERING DISABLE_CLUSTERING
#else
#define U14F_MAX_SGLIST 33
#define U14F_CLUSTERING ENABLE_CLUSTERING
#endif
#define U34F_MAX_SGLIST 33
#define U34F_CLUSTERING ENABLE_CLUSTERING
#define DO_BUS_RESET /* Reset SCSI bus on error */
#define NO_DEBUG_DETECT
#define NO_DEBUG_INTERRUPT
#define DEBUG_STATISTICS
#define MAX_TARGET 8
#define MAX_IRQ 16
#define MAX_BOARDS 4
#define MAX_MAILBOXES 16
#define MAX_SGLIST U34F_MAX_SGLIST
#define MAX_CMD_PER_LUN 2
#define FALSE 0
#define TRUE 1
#define FREE 0
#define IN_USE 1
#define LOCKED 2
#define IN_RESET 3
#define NO_IRQ 0xff
#define MAXLOOP 20000
#define REG_LCL_MASK 0
#define REG_LCL_INTR 1
#define REG_SYS_MASK 2
#define REG_SYS_INTR 3
#define REG_PRODUCT_ID1 4
#define REG_PRODUCT_ID2 5
#define REG_CONFIG1 6
#define REG_CONFIG2 7
#define REG_OGM 8
#define REG_ICM 12
#define REG_REGION 0x0c
#define BSY_ASSERTED 0x01
#define INTR_ASSERTED 0x01
#define CMD_RESET 0xc0
#define CMD_OGM_INTR 0x01
#define CMD_CLR_INTR 0x01
#define CMD_ENA_INTR 0x81
#define ASOK 0x00
#define ASST 0x91
#define PACKED __attribute__((packed))
/* MailBox SCSI Command Packet */
struct mscp {
unsigned char opcode: 3; /* type of command */
unsigned char xdir: 2; /* data transfer direction */
unsigned char dcn: 1; /* disable disconnect */
unsigned char ca: 1; /* use cache (if available) */
unsigned char sg: 1; /* scatter/gather operation */
unsigned char target: 3; /* target SCSI id */
unsigned char ch_no: 2; /* SCSI channel (always 0 for 14f) */
unsigned char lun: 3; /* logical unit number */
unsigned int data_address PACKED; /* transfer data pointer */
unsigned int data_len PACKED; /* length in bytes */
unsigned int command_link PACKED; /* for linking command chains */
unsigned char scsi_command_link_id; /* identifies command in chain */
unsigned char use_sg; /* (if sg is set) 8 bytes per list */
unsigned char sense_len;
unsigned char scsi_cdbs_len; /* 6, 10, or 12 */
unsigned char scsi_cdbs[12]; /* SCSI commands */
unsigned char adapter_status; /* non-zero indicates HA error */
unsigned char target_status; /* non-zero indicates target error */
unsigned int sense_addr PACKED;
Scsi_Cmnd *SCpnt;
struct sg_list {
unsigned int address; /* Segment Address */
unsigned int num_bytes; /* Segment Length */
} sglist[MAX_SGLIST];
unsigned int index; /* cp index */
};
struct hostdata {
struct mscp cp[MAX_MAILBOXES]; /* Mailboxes for this board */
unsigned int cp_stat[MAX_MAILBOXES]; /* FREE, IN_USE, LOCKED, IN_RESET */
unsigned int last_cp_used; /* Index of last mailbox used */
unsigned int iocount; /* Total i/o done for this board */
unsigned int multicount; /* Total ... in second ihdlr loop */
int board_number; /* Number of this board */
char board_name[16]; /* Name of this board */
int in_reset; /* True if board is doing a reset */
int target_time_out[MAX_TARGET]; /* N. of timeout errors on target */
int target_reset[MAX_TARGET]; /* If TRUE redo operation on target */
unsigned char bios_drive_number: 1;
unsigned char heads;
unsigned char sectors;
unsigned char subversion: 4;
/* slot != 0 for the U24F, slot == 0 for both the U14F and U34F */
unsigned char slot;
};
static struct Scsi_Host * sh[MAX_BOARDS + 1];
static char* driver_name = "Ux4F";
static unsigned int irqlist[MAX_IRQ], calls[MAX_IRQ];
#define HD(board) ((struct hostdata *) &sh[board]->hostdata)
#define BN(board) (HD(board)->board_name)
static void u14_34f_interrupt_handler(int);
static int do_trace = FALSE;
static inline unchar wait_on_busy(ushort iobase) {
unsigned int loop = MAXLOOP;
while (inb(iobase + REG_LCL_INTR) & BSY_ASSERTED)
if (--loop == 0) return TRUE;
return FALSE;
}
static inline int port_detect(ushort *port_base, unsigned int j,
Scsi_Host_Template * tpnt) {
unsigned char irq, dma_channel, in_byte, subversion, sys_mask, lcl_mask;
/* Allowed BIOS base addresses (NULL indicates reserved) */
void *bios_segment_table[8] = {
NULL,
(void *) 0xc4000, (void *) 0xc8000, (void *) 0xcc000, (void *) 0xd0000,
(void *) 0xd4000, (void *) 0xd8000, (void *) 0xdc000
};
/* Allowed IRQs */
unsigned char interrupt_table_14f[4] = { 15, 14, 11, 10 };
/* Allowed DMA channels for 14f (0 indicates reserved) */
unsigned char dma_channel_table_14f[4] = { 5, 6, 7, 0 };
/* Head/sector mappings */
struct {
unsigned char heads;
unsigned char sectors;
} mapping_table[4] = {
{ 16, 63 }, { 64, 32 }, { 64, 63 }, { 64, 32 }
};
struct config_1 {
unsigned char bios_segment: 3;
unsigned char removable_disks_as_fixed: 1;
unsigned char interrupt: 2;
unsigned char dma_channel: 2;
} config_1;
struct config_2 {
unsigned char ha_scsi_id: 3;
unsigned char mapping_mode: 2;
unsigned char bios_drive_number: 1;
unsigned char tfr_port: 2;
} config_2;
char name[16];
sprintf(name, "%s%d", driver_name, j);
if(check_region(*port_base, REG_REGION)) return FALSE;
if (inb(*port_base + REG_PRODUCT_ID1) != U14F_PRODUCT_ID1) return FALSE;
in_byte = inb(*port_base + REG_PRODUCT_ID2);
if ((in_byte & 0xf0) != U14F_PRODUCT_ID2) return FALSE;
*(char *)&config_1 = inb(*port_base + REG_CONFIG1);
*(char *)&config_2 = inb(*port_base + REG_CONFIG2);
irq = interrupt_table_14f[config_1.interrupt];
dma_channel = dma_channel_table_14f[config_1.dma_channel];
subversion = (in_byte & 0x0f);
if ((irq >= MAX_IRQ) || ((irqlist[irq] == NO_IRQ) && request_irq
(irq, u14_34f_interrupt_handler, SA_INTERRUPT, driver_name))) {
printk("%s: unable to allocate IRQ %u, detaching.\n", name, irq);
return FALSE;
}
if (subversion == U14F && request_dma(dma_channel, driver_name)) {
printk("%s: unable to allocate DMA channel %u, detaching.\n",
name, dma_channel);
free_irq(irq);
return FALSE;
}
sh[j] = scsi_register(tpnt, sizeof(struct hostdata));
sh[j]->io_port = *port_base;
sh[j]->base = bios_segment_table[config_1.bios_segment];
sh[j]->irq = irq;
sh[j]->this_id = config_2.ha_scsi_id;
sh[j]->hostt->can_queue = MAX_MAILBOXES;
sh[j]->hostt->cmd_per_lun = MAX_CMD_PER_LUN;
sys_mask = inb(sh[j]->io_port + REG_SYS_MASK);
lcl_mask = inb(sh[j]->io_port + REG_LCL_MASK);
#if defined(DEBUG_DETECT)
printk("SYS_MASK 0x%x, LCL_MASK 0x%x.\n", sys_mask, lcl_mask);
#endif
/* If BIOS is disabled, force enable interrupts */
if (sh[j]->base == 0) outb(CMD_ENA_INTR, sh[j]->io_port + REG_SYS_MASK);
#if defined (DO_BUS_RESET)
outb(lcl_mask | 0x40, sh[j]->io_port + REG_LCL_MASK);
#else
outb(lcl_mask & ~0x40, sh[j]->io_port + REG_LCL_MASK);
#endif
/* Register the I/O space that we use */
snarf_region(sh[j]->io_port, REG_REGION);
memset(HD(j), 0, sizeof(struct hostdata));
HD(j)->heads = mapping_table[config_2.mapping_mode].heads;
HD(j)->sectors = mapping_table[config_2.mapping_mode].sectors;
HD(j)->bios_drive_number = config_2.bios_drive_number;
HD(j)->subversion = subversion;
HD(j)->board_number = j;
irqlist[irq] = j;
if (HD(j)->subversion == U34F) {
sh[j]->dma_channel = 0;
sh[j]->unchecked_isa_dma = FALSE;
sh[j]->sg_tablesize = U34F_MAX_SGLIST;
sh[j]->hostt->use_clustering = U34F_CLUSTERING;
sprintf(BN(j), "U34F%d", j);
}
else {
sh[j]->dma_channel = dma_channel;
sh[j]->unchecked_isa_dma = TRUE;
sh[j]->sg_tablesize = U14F_MAX_SGLIST;
/*if (j > 0) sh[j]->sg_tablesize = 0;*/
sh[j]->hostt->use_clustering = U14F_CLUSTERING;
sprintf(BN(j), "U14F%d", j);
disable_dma(dma_channel);
clear_dma_ff(dma_channel);
set_dma_mode(dma_channel, DMA_MODE_CASCADE);
enable_dma(dma_channel);
}
printk("%s: PORT 0x%03x, BIOS 0x%05x, IRQ %u, DMA %u, SG %d, "\
"Mbox %d, CmdLun %d, C%d.\n", BN(j), sh[j]->io_port,
(int)sh[j]->base, sh[j]->irq,
sh[j]->dma_channel, sh[j]->sg_tablesize,
sh[j]->hostt->can_queue, sh[j]->hostt->cmd_per_lun,
sh[j]->hostt->use_clustering);
return TRUE;
}
int u14_34f_detect (Scsi_Host_Template * tpnt) {
unsigned int j = 0, k, flags;
ushort isa_io_port[] = {
0x330, 0x340, 0x230, 0x240, 0x210, 0x130, 0x140, 0x0
};
ushort *port_base = isa_io_port;
save_flags(flags);
cli();
for (k = 0; k < MAX_IRQ; k++) {
irqlist[k] = NO_IRQ;
calls[k] = 0;
}
for (k = 0; k < MAX_BOARDS + 1; k++) sh[k] = NULL;
while (*port_base) {
if(j < MAX_BOARDS && port_detect(port_base, j, tpnt)) j++;
port_base++;
}
restore_flags(flags);
return j;
}
const char *u14_34f_info(void) {
return driver_name;
}
static inline void build_sg_list(struct mscp *cpp, Scsi_Cmnd *SCpnt) {
unsigned int k, data_len = 0;
struct scatterlist * sgpnt;
sgpnt = (struct scatterlist *) SCpnt->request_buffer;
for(k = 0; k < SCpnt->use_sg; k++) {
cpp->sglist[k].address = (unsigned int) sgpnt[k].address;
cpp->sglist[k].num_bytes = sgpnt[k].length;
data_len += sgpnt[k].length;
}
cpp->use_sg = SCpnt->use_sg;
cpp->data_address = (unsigned int) cpp->sglist;
cpp->data_len = data_len;
}
int u14_34f_queuecommand(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)) {
unsigned int i, j, k, flags;
struct mscp *cpp;
save_flags(flags);
cli();
/* j is the board number */
j = ((struct hostdata *) SCpnt->host->hostdata)->board_number;
if (!done) panic("%s: qcomm, pid %ld, null done.\n", BN(j), SCpnt->pid);
/* i is the mailbox number, look for the first free mailbox
starting from last_cp_used */
i = HD(j)->last_cp_used + 1;
for(k = 0; k < sh[j]->hostt->can_queue; k++, i++) {
if (i >= sh[j]->hostt->can_queue) i = 0;
if (HD(j)->cp_stat[i] == FREE) {
HD(j)->last_cp_used = i;
break;
}
}
if (k == sh[j]->hostt->can_queue) {
printk("%s: qcomm, no free mailbox, reseting.\n", BN(j));
if (HD(j)->in_reset)
printk("%s: qcomm, already in reset.\n", BN(j));
else if (u14_34f_reset(SCpnt) == SCSI_RESET_SUCCESS)
panic("%s: qcomm, SCSI_RESET_SUCCESS.\n", BN(j));
SCpnt->result = DID_BUS_BUSY << 16;
SCpnt->host_scribble = NULL;
printk("%s: qcomm, pid %ld, DID_BUS_BUSY, done.\n", BN(j), SCpnt->pid);
restore_flags(flags);
done(SCpnt);
return 0;
}
/* Set pointer to control packet structure */
cpp = &HD(j)->cp[i];
memset(cpp, 0, sizeof(struct mscp));
SCpnt->scsi_done = done;
cpp->index = i;
SCpnt->host_scribble = (unsigned char *) &cpp->index;
if (do_trace) printk("%s: qcomm, mbox %d, target %d, pid %ld.\n",
BN(j), i, SCpnt->target, SCpnt->pid);
cpp->opcode = OP_SCSI;
cpp->xdir = DTD_SCSI;
cpp->target = SCpnt->target;
cpp->lun = SCpnt->lun;
cpp->SCpnt = SCpnt;
cpp->sense_addr = (unsigned int) SCpnt->sense_buffer;
cpp->sense_len = sizeof SCpnt->sense_buffer;
if (SCpnt->use_sg) {
cpp->sg = TRUE;
build_sg_list(cpp, SCpnt);
}
else {
cpp->data_address = (unsigned int)SCpnt->request_buffer;
cpp->data_len = SCpnt->request_bufflen;
}
cpp->scsi_cdbs_len = COMMAND_SIZE(*(unsigned char *)SCpnt->cmnd);
memcpy(cpp->scsi_cdbs, SCpnt->cmnd, cpp->scsi_cdbs_len);
if (wait_on_busy(sh[j]->io_port)) {
SCpnt->result = DID_ERROR << 16;
SCpnt->host_scribble = NULL;
printk("%s: qcomm, target %d, pid %ld, adapter busy, DID_ERROR, done.\n",
BN(j), SCpnt->target, SCpnt->pid);
restore_flags(flags);
done(SCpnt);
return 0;
}
/* Store pointer in OGM address bytes */
outl((unsigned int)cpp, sh[j]->io_port + REG_OGM);
/* Issue OGM interrupt */
outb(CMD_OGM_INTR, sh[j]->io_port + REG_LCL_INTR);
HD(j)->cp_stat[i] = IN_USE;
restore_flags(flags);
return 0;
}
int u14_34f_abort(Scsi_Cmnd *SCarg) {
unsigned int i, j, flags;
save_flags(flags);
cli();
j = ((struct hostdata *) SCarg->host->hostdata)->board_number;
if (SCarg->host_scribble == NULL) {
printk("%s: abort, target %d, pid %ld inactive.\n",
BN(j), SCarg->target, SCarg->pid);
return SCSI_ABORT_NOT_RUNNING;
}
i = *(unsigned int *)SCarg->host_scribble;
printk("%s: abort, mbox %d, target %d, pid %ld.\n",
BN(j), i, SCarg->target, SCarg->pid);
if (i >= sh[j]->hostt->can_queue)
panic("%s: abort, invalid SCarg->host_scribble.\n", BN(j));
if (wait_on_busy(sh[j]->io_port)) {
printk("%s: abort, timeout error.\n", BN(j));
restore_flags(flags);
return SCSI_ABORT_ERROR;
}
if (HD(j)->cp_stat[i] == FREE) {
printk("%s: abort, mbox %d is free.\n", BN(j), i);
restore_flags(flags);
return SCSI_ABORT_NOT_RUNNING;
}
if (HD(j)->cp_stat[i] == IN_USE) {
printk("%s: abort, mbox %d is in use.\n", BN(j), i);
if (SCarg != HD(j)->cp[i].SCpnt)
panic("%s: abort, mbox %d, SCarg %p, cp SCpnt %p.\n",
BN(j), i, SCarg, HD(j)->cp[i].SCpnt);
restore_flags(flags);
return SCSI_ABORT_SNOOZE;
}
if (HD(j)->cp_stat[i] == IN_RESET) {
printk("%s: abort, mbox %d is in reset.\n", BN(j), i);
restore_flags(flags);
return SCSI_ABORT_ERROR;
}
if (HD(j)->cp_stat[i] == LOCKED) {
printk("%s: abort, mbox %d is locked.\n", BN(j), i);
restore_flags(flags);
return SCSI_ABORT_NOT_RUNNING;
}
else
panic("%s: abort, mbox %d, invalid cp_stat.\n", BN(j), i);
}
int u14_34f_reset(Scsi_Cmnd * SCarg) {
unsigned int i, j, flags, time, k, limit = 0;
int arg_done = FALSE;
Scsi_Cmnd *SCpnt;
save_flags(flags);
cli();
j = ((struct hostdata *) SCarg->host->hostdata)->board_number;
printk("%s: reset, enter, target %d, pid %ld.\n",
BN(j), SCarg->target, SCarg->pid);
if (SCarg->host_scribble == NULL)
printk("%s: reset, pid %ld inactive.\n", BN(j), SCarg->pid);
if (HD(j)->in_reset) {
printk("%s: reset, exit, already in reset.\n", BN(j));
restore_flags(flags);
return SCSI_RESET_ERROR;
}
if (wait_on_busy(sh[j]->io_port)) {
printk("%s: reset, exit, timeout error.\n", BN(j));
restore_flags(flags);
return SCSI_RESET_ERROR;
}
for (k = 0; k < MAX_TARGET; k++) HD(j)->target_reset[k] = TRUE;
for (i = 0; i < sh[j]->hostt->can_queue; i++) {
if (HD(j)->cp_stat[i] == FREE) continue;
if (HD(j)->cp_stat[i] == LOCKED) {
HD(j)->cp_stat[i] = FREE;
printk("%s: reset, locked mbox %d forced free.\n", BN(j), i);
continue;
}
SCpnt = HD(j)->cp[i].SCpnt;
HD(j)->cp_stat[i] = IN_RESET;
printk("%s: reset, mbox %d in reset, pid %ld.\n",
BN(j), i, SCpnt->pid);
if (SCpnt == NULL)
panic("%s: reset, mbox %d, SCpnt == NULL.\n", BN(j), i);
if (SCpnt->host_scribble == NULL)
panic("%s: reset, mbox %d, garbled SCpnt.\n", BN(j), i);
if (*(unsigned int *)SCpnt->host_scribble != i)
panic("%s: reset, mbox %d, index mismatch.\n", BN(j), i);
if (SCpnt->scsi_done == NULL)
panic("%s: reset, mbox %d, SCpnt->scsi_done == NULL.\n", BN(j), i);
if (SCpnt == SCarg) arg_done = TRUE;
}
if (wait_on_busy(sh[j]->io_port)) {
printk("%s: reset, cannot reset, timeout error.\n", BN(j));
restore_flags(flags);
return SCSI_RESET_ERROR;
}
outb(CMD_RESET, sh[j]->io_port + REG_LCL_INTR);
printk("%s: reset, board reset done, enabling interrupts.\n", BN(j));
do_trace = TRUE;
HD(j)->in_reset = TRUE;
sti();
time = jiffies;
while (jiffies < (time + 200) && limit++ < 100000000) sti();
cli();
printk("%s: reset, interrupts disabled, loops %d.\n", BN(j), limit);
for (i = 0; i < sh[j]->hostt->can_queue; i++) {
/* Skip mailboxes already set free by interrupt */
if (HD(j)->cp_stat[i] != IN_RESET) continue;
SCpnt = HD(j)->cp[i].SCpnt;
SCpnt->result = DID_RESET << 16;
SCpnt->host_scribble = NULL;
/* This mailbox is still waiting for its interrupt */
HD(j)->cp_stat[i] = LOCKED;
printk("%s, reset, mbox %d locked, DID_RESET, pid %ld done.\n",
BN(j), i, SCpnt->pid);
restore_flags(flags);
SCpnt->scsi_done(SCpnt);
cli();
}
HD(j)->in_reset = FALSE;
do_trace = FALSE;
restore_flags(flags);
if (arg_done) {
printk("%s: reset, exit, success.\n", BN(j));
return SCSI_RESET_SUCCESS;
}
else {
printk("%s: reset, exit, wakeup.\n", BN(j));
return SCSI_RESET_PUNT;
}
}
int u14_34f_biosparam(Disk * disk, int dev, int * dkinfo) {
unsigned int j = 0;
int size = disk->capacity;
dkinfo[0] = HD(j)->heads;
dkinfo[1] = HD(j)->sectors;
dkinfo[2] = size / (HD(j)->heads * HD(j)->sectors);
return 0;
}
static void u14_34f_interrupt_handler(int irq) {
Scsi_Cmnd *SCpnt;
unsigned int i, j, k, flags, status, loops, total_loops = 0;
struct mscp *spp;
save_flags(flags);
cli();
if (irqlist[irq] == NO_IRQ) {
printk("%s, ihdlr, irq %d, unexpected interrupt.\n", driver_name, irq);
restore_flags(flags);
return;
}
if (do_trace) printk("%s: ihdlr, enter, irq %d, calls %d.\n",
driver_name, irq, calls[irq]);
/* Service all the boards configured on this irq */
for (j = 0; sh[j] != NULL; j++) {
if (sh[j]->irq != irq) continue;
loops = 0;
/* Loop until all interrupts for a board are serviced */
while (inb(sh[j]->io_port + REG_SYS_INTR) & INTR_ASSERTED) {
total_loops++;
loops++;
if (do_trace) printk("%s: ihdlr, start service, count %d.\n",
BN(j), HD(j)->iocount);
spp = (struct mscp *)inl(sh[j]->io_port + REG_ICM);
/* Clear interrupt pending flag */
outb(CMD_CLR_INTR, sh[j]->io_port + REG_SYS_INTR);
i = spp - HD(j)->cp;
if (i >= sh[j]->hostt->can_queue)
panic("%s: ihdlr, invalid mscp address.\n", BN(j));
if (HD(j)->cp_stat[i] == LOCKED) {
HD(j)->cp_stat[i] = FREE;
printk("%s: ihdlr, mbox %d unlocked, count %d.\n",
BN(j), i, HD(j)->iocount);
continue;
}
else if (HD(j)->cp_stat[i] == FREE) {
printk("%s: ihdlr, mbox %d is free, count %d.\n",
BN(j), i, HD(j)->iocount);
continue;
}
else if (HD(j)->cp_stat[i] == IN_RESET)
printk("%s: ihdlr, mbox %d is in reset.\n", BN(j), i);
else if (HD(j)->cp_stat[i] != IN_USE)
panic("%s: ihdlr, mbox %d, invalid cp_stat.\n", BN(j), i);
HD(j)->cp_stat[i] = FREE;
SCpnt = spp->SCpnt;
if (SCpnt == NULL)
panic("%s: ihdlr, mbox %d, SCpnt == NULL.\n", BN(j), i);
if (SCpnt->host_scribble == NULL)
panic("%s: ihdlr, mbox %d, pid %ld, SCpnt %p garbled.\n",
BN(j), i, SCpnt->pid, SCpnt);
if (*(unsigned int *)SCpnt->host_scribble != i)
panic("%s: ihdlr, mbox %d, pid %ld, index mismatch %d,"\
" irq %d.\n", BN(j), i, SCpnt->pid,
*(unsigned int *)SCpnt->host_scribble, irq);
switch (spp->adapter_status) {
case ASOK: /* status OK */
/* Fix a "READ CAPACITY failed" error on some disk drives */
if (spp->target_status == INTERMEDIATE_GOOD
&& SCpnt->device->type != TYPE_TAPE)
status = DID_ERROR << 16;
/* If there was a bus reset, redo operation on each target */
else if (spp->target_status == CONDITION_GOOD
&& SCpnt->device->type != TYPE_TAPE
&& HD(j)->target_reset[SCpnt->target])
status = DID_BUS_BUSY << 16;
else
status = DID_OK << 16;
if (spp->target_status == 0)
HD(j)->target_reset[SCpnt->target] = FALSE;
HD(j)->target_time_out[SCpnt->target] = 0;
break;
case ASST: /* SCSI bus selection time out */
if (HD(j)->target_time_out[SCpnt->target] > 1)
status = DID_ERROR << 16;
else {
status = DID_TIME_OUT << 16;
HD(j)->target_time_out[SCpnt->target]++;
}
break;
case 0x92: /* Data over/under-run */
case 0x93: /* Unexpected bus free */
case 0x94: /* Target bus phase sequence failure */
case 0x96: /* Illegal SCSI command */
case 0xa3: /* SCSI bus reset error */
if (SCpnt->device->type != TYPE_TAPE)
status = DID_BUS_BUSY << 16;
else
status = DID_ERROR << 16;
for (k = 0; k < MAX_TARGET; k++)
HD(j)->target_reset[k] = TRUE;
break;
case 0x01: /* Invalid command */
case 0x02: /* Invalid parameters */
case 0x03: /* Invalid data list */
case 0x84: /* SCSI bus abort error */
case 0x9b: /* Auto request sense error */
case 0x9f: /* Unexpected command complete message error */
case 0xff: /* Invalid parameter in the S/G list */
default:
status = DID_ERROR << 16;
break;
}
SCpnt->result = status | spp->target_status;
HD(j)->iocount++;
if (loops > 1) HD(j)->multicount++;
#if defined (DEBUG_INTERRUPT)
if (SCpnt->result || do_trace)
#else
if ((spp->adapter_status != ASOK && HD(j)->iocount > 1000) ||
(spp->adapter_status != ASOK &&
spp->adapter_status != ASST && HD(j)->iocount <= 1000) ||
do_trace)
#endif
printk("%s: ihdlr, mbox %d, err 0x%x:%x,"\
" target %d:%d, pid %ld, count %d.\n",
BN(j), i, spp->adapter_status, spp->target_status,
SCpnt->target, SCpnt->lun, SCpnt->pid, HD(j)->iocount);
/* Set the command state to inactive */
SCpnt->host_scribble = NULL;
restore_flags(flags);
SCpnt->scsi_done(SCpnt);
cli();
} /* Multiple command loop */
} /* Boards loop */
calls[irq]++;
if (total_loops == 0)
printk("%s: ihdlr, irq %d, no command completed, calls %d.\n",
driver_name, irq, calls[irq]);
if (do_trace) printk("%s: ihdlr, exit, irq %d, calls %d.\n",
driver_name, irq, calls[irq]);
#if defined (DEBUG_STATISTICS)
if ((calls[irq] % 100000) == 10000)
for (j = 0; sh[j] != NULL; j++)
printk("%s: ihdlr, calls %d, count %d, multi %d.\n", BN(j),
calls[(sh[j]->irq)], HD(j)->iocount, HD(j)->multicount);
#endif
restore_flags(flags);
return;
}
/*
* u14-34f.h - used by low-level scsi driver for UltraStor 14F/34F
*/
#ifndef _U14_34F_H
#define _U14_34F_H
int u14_34f_detect(Scsi_Host_Template *);
const char *u14_34f_info(void);
int u14_34f_queuecommand(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
int u14_34f_abort(Scsi_Cmnd *);
int u14_34f_reset(Scsi_Cmnd *);
int u14_34f_biosparam(Disk *, int, int *);
#define U14_34F_VERSION "1.09.01"
#define ULTRASTOR_14_34F { \
NULL, \
"UltraStor 14F/34F rev. " U14_34F_VERSION " by " \
"Dario_Ballabio@milano.europe.dg.com.",\
u14_34f_detect, \
NULL, \
u14_34f_info, \
NULL, \
u14_34f_queuecommand, \
u14_34f_abort, \
u14_34f_reset, \
NULL, \
u14_34f_biosparam, \
0, /* can_queue, reset by detect */ \
7, /* this_id, reset by detect */ \
0, /* sg_tablesize, reset by detect */ \
0, /* cmd_per_lun, reset by detect */ \
0, /* number of boards present */ \
0, /* unchecked isa dma, reset by detect */ \
0, /* use_clustering, reset by detect */ \
}
#endif
......@@ -20,6 +20,8 @@ FS_SUBDIRS := $(FS_SUBDIRS) ext2
endif
ifdef CONFIG_MSDOS_FS
FS_SUBDIRS := $(FS_SUBDIRS) msdos
else
MODULE_FS_SUBDIRS := $(MODULE_FS_SUBDIRS) msdos
endif
ifdef CONFIG_PROC_FS
FS_SUBDIRS := $(FS_SUBDIRS) proc
......@@ -35,6 +37,8 @@ FS_SUBDIRS := $(FS_SUBDIRS) xiafs
endif
ifdef CONFIG_UMSDOS_FS
FS_SUBDIRS := $(FS_SUBDIRS) umsdos
else
MODULE_FS_SUBDIRS := $(MODULE_FS_SUBDIRS) umsdos
endif
ifdef CONFIG_SYSV_FS
FS_SUBDIRS := $(FS_SUBDIRS) sysv
......@@ -76,6 +80,10 @@ ifdef MODULES
modules:
$(MAKE) CFLAGS="$(CFLAGS) -DMODULE" $(MODULES)
(cd ../modules;for i in $(MODULES); do ln -sf ../fs/$$i .; done)
set -e; for i in $(MODULE_FS_SUBDIRS); do \
test ! -d $$i || \
{ $(MAKE) -C $$i; }; done
else
......
......@@ -139,7 +139,7 @@ asmlinkage int sys_fcntl(unsigned int fd, unsigned int cmd, unsigned long arg)
* Changed to make the security checks more
* liberal. -- TYT
*/
if (current->pgrp == -arg || current->pid != arg)
if (current->pgrp == -arg || current->pid == arg)
goto fasync_ok;
for_each_task(p) {
......
......@@ -284,6 +284,7 @@ void inode_setattr(struct inode *inode, struct iattr *attr)
if (!fsuser() && !in_group_p(inode->i_gid))
inode->i_mode &= ~S_ISGID;
}
inode->i_dirt = 1;
}
/*
......
......@@ -90,7 +90,7 @@ static int parse_options(char *options, struct iso9660_options * popt)
else return 0;
}
else if (!strcmp(this_char,"conv") && value) {
if (value[0] && !value[1] && strchr("bta",*value))
if (value[0] && !value[1] && strchr("btma",*value))
popt->conversion = *value;
else if (!strcmp(value,"binary")) popt->conversion = 'b';
else if (!strcmp(value,"text")) popt->conversion = 't';
......@@ -116,10 +116,10 @@ static int parse_options(char *options, struct iso9660_options * popt)
if (ivalue != 1024 && ivalue != 2048) return 0;
popt->blocksize = ivalue;
break;
case 'g':
case 'u':
popt->uid = ivalue;
break;
case 'u':
case 'g':
popt->gid = ivalue;
break;
}
......
......@@ -7,6 +7,10 @@
#
# Note 2! The CFLAGS definitions are now in the main makefile...
ifndef CONFIG_MSDOS_FS
CFLAGS := $(CFLAGS) -DMODULE
endif
.c.s:
$(CC) $(CFLAGS) -S $<
.c.o:
......@@ -14,7 +18,7 @@
.s.o:
$(AS) -o $*.o $<
OBJS= namei.o inode.o file.o dir.o misc.o fat.o mmap.o
OBJS= namei.o inode.o file.o dir.o misc.o fat.o
msdos.o: $(OBJS)
$(LD) -r -o msdos.o $(OBJS)
......
......@@ -18,6 +18,8 @@
#define ROUND_UP(x) (((x)+3) & ~3)
#define PRINTK(X)
static int msdos_dir_read(struct inode * inode,struct file * filp, char * buf,int count)
{
return -EISDIR;
......@@ -112,7 +114,9 @@ int msdos_readdir(
put_fs_long(ino,&dirent->d_ino);
memcpy_tofs(dirent->d_name,bufname,i+1);
put_fs_word(i,&dirent->d_reclen);
PRINTK (("readdir avant brelse\n"));
brelse(bh);
PRINTK (("readdir retourne %d\n",i));
return ROUND_UP(NAME_OFFSET(dirent) + i + 1);
}
}
......
......@@ -10,6 +10,7 @@
#include <asm/system.h>
#include <linux/sched.h>
#include <linux/locks.h>
#include <linux/fs.h>
#include <linux/msdos_fs.h>
#include <linux/errno.h>
......@@ -20,6 +21,8 @@
#define MIN(a,b) (((a) < (b)) ? (a) : (b))
#define MAX(a,b) (((a) > (b)) ? (a) : (b))
#define PRINTK(x)
static struct file_operations msdos_file_operations = {
NULL, /* lseek - default */
msdos_file_read, /* read */
......@@ -27,7 +30,7 @@ static struct file_operations msdos_file_operations = {
NULL, /* readdir - bad */
NULL, /* select - default */
NULL, /* ioctl - default */
msdos_mmap, /* mmap */
generic_mmap, /* mmap */
NULL, /* no special open is needed */
NULL, /* release */
file_fsync /* fsync */
......@@ -49,31 +52,9 @@ struct inode_operations msdos_file_inode_operations = {
msdos_bmap, /* bmap */
msdos_truncate, /* truncate */
NULL, /* permission */
msdos_smap /* smap */
};
/* No bmap for MS-DOS FS' that don't align data at kByte boundaries. */
struct inode_operations msdos_file_inode_operations_no_bmap = {
&msdos_file_operations, /* default file operations */
NULL, /* create */
NULL, /* lookup */
NULL, /* link */
NULL, /* unlink */
NULL, /* symlink */
NULL, /* mkdir */
NULL, /* rmdir */
NULL, /* mknod */
NULL, /* rename */
NULL, /* readlink */
NULL, /* follow_link */
NULL, /* bmap */
msdos_truncate, /* truncate */
NULL, /* permission */
msdos_smap /* smap */
NULL /* smap */
};
/*
Read a file into user space
*/
......@@ -84,12 +65,18 @@ int msdos_file_read(
int count)
{
char *start;
int left,offset,size,sector,cnt;
char ch;
struct buffer_head *bh;
void *data;
int left,offset,size,cnt;
struct {
int to_reada; /* How many block to read all at once */
struct buffer_head *bhreq[64]; /* Buffers not already read */
int nbreq; /* Number of buffers to read */
struct buffer_head *bhlist[64]; /* All buffers needed */
int nblist; /* Number of buffers in bhlist */
int nolist;
}pre;
int i;
/* printk("msdos_file_read\n"); */
if (!inode) {
printk("msdos_file_read: inode = NULL\n");
return -EINVAL;
......@@ -100,18 +87,67 @@ int msdos_file_read(
return -EINVAL;
}
if (filp->f_pos >= inode->i_size || count <= 0) return 0;
/*
Tell the buffer cache which block we expect to read in advance
Since we are limited with the stack, we preread only 64
because we have to keep the result into the local
arrays pre.bhlist and pre.bhreq.
*/
{
int file_sector = filp->f_pos >> SECTOR_BITS;
pre.to_reada = count / SECTOR_SIZE;
if (filp->f_reada){
int min_read = read_ahead[MAJOR(inode->i_dev)];
if (min_read > pre.to_reada) pre.to_reada = min_read;
}
if (pre.to_reada > 64) pre.to_reada = 64;
pre.nbreq = pre.nblist = 0;
for (i=0; i<pre.to_reada; i++){
int sector;
struct buffer_head *bh;
if (!(sector = msdos_smap(inode,file_sector++))) break;
bh = getblk(inode->i_dev,sector,SECTOR_SIZE);
if (bh == NULL) break;
pre.bhlist[pre.nblist++] = bh;
if (!bh->b_uptodate){
pre.bhreq[pre.nbreq++] = bh;
}
}
if (pre.nbreq > 0) ll_rw_block (READ,pre.nbreq,pre.bhreq);
}
start = buf;
pre.nolist = 0;
while ((left = MIN(inode->i_size-filp->f_pos,count-(buf-start))) > 0){
if (!(sector = msdos_smap(inode,filp->f_pos >> SECTOR_BITS)))
break;
struct buffer_head *bh;
void *data;
PRINTK (("file_read pos %d\n",filp->f_pos));
if (pre.nolist >= pre.nblist){
/* This code is executed when more than 64 sectors */
/* are request at once */
int sector;
if (!(sector = msdos_smap(inode,filp->f_pos >> SECTOR_BITS)))
break;
if (!(bh = msdos_sread(inode->i_dev,sector,&data)))
break;
}else{
bh = pre.bhlist[pre.nolist];
pre.bhlist[pre.nolist++] = NULL;
data = bh->b_data;
wait_on_buffer(bh);
if (!bh->b_uptodate){
/* read error ? */
brelse (bh);
break;
}
}
offset = filp->f_pos & (SECTOR_SIZE-1);
if (!(bh = msdos_sread(inode->i_dev,sector,&data))) break;
filp->f_pos += (size = MIN(SECTOR_SIZE-offset,left));
if (MSDOS_I(inode)->i_binary) {
memcpy_tofs(buf,data+offset,size);
buf += size;
}
else for (cnt = size; cnt; cnt--) {
char ch;
if ((ch = *((char *) data+offset++)) == '\r')
size--;
else {
......@@ -119,19 +155,18 @@ int msdos_file_read(
else {
filp->f_pos = inode->i_size;
brelse(bh);
if (start != buf
&& !IS_RDONLY(inode))
inode->i_atime
= CURRENT_TIME;
return buf-start;
break;
}
}
}
brelse(bh);
}
for (i=0; i<pre.nblist; i++) brelse (pre.bhlist[i]);
if (start == buf) return -EIO;
if (!IS_RDONLY(inode))
inode->i_atime = CURRENT_TIME;
PRINTK (("file_read ret %d\n",(buf-start)));
filp->f_reada = 1; /* Will be reset if a lseek is done */
return buf-start;
}
......@@ -149,6 +184,7 @@ int msdos_file_write(
char *start,*to,ch;
struct buffer_head *bh;
void *data;
int binary_mode = MSDOS_I(inode)->i_binary;
if (!inode) {
printk("msdos_file_write: inode = NULL\n");
......@@ -175,13 +211,20 @@ int msdos_file_write(
}
offset = filp->f_pos & (SECTOR_SIZE-1);
size = MIN(SECTOR_SIZE-offset,MAX(carry,count));
if (!(bh = msdos_sread(inode->i_dev,sector,&data))) {
if (binary_mode && offset == 0 && size == SECTOR_SIZE){
/* No need to read the block first since we will */
/* completely overwrite it */
if (!(bh = getblk(inode->i_dev,sector,SECTOR_SIZE))){
error = -EIO;
break;
}
data = bh->b_data;
}else if (!(bh = msdos_sread(inode->i_dev,sector,&data))) {
error = -EIO;
break;
}
if (MSDOS_I(inode)->i_binary) {
memcpy_fromfs(data+(filp->f_pos & (SECTOR_SIZE-1)),
buf,written = size);
if (binary_mode) {
memcpy_fromfs(data+offset,buf,written = size);
buf += size;
}
else {
......@@ -210,6 +253,7 @@ int msdos_file_write(
inode->i_size = filp->f_pos;
inode->i_dirt = 1;
}
bh->b_uptodate = 1;
mark_buffer_dirty(bh, 0);
brelse(bh);
}
......
......@@ -10,11 +10,19 @@
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/ctype.h>
#include <linux/major.h>
#include <linux/fs.h>
#include <linux/stat.h>
#include <linux/locks.h>
#ifdef MODULE
#include <linux/module.h>
#include "../../tools/version.h"
#endif
#include <asm/segment.h>
extern int *blksize_size[];
void msdos_put_inode(struct inode *inode)
{
......@@ -47,9 +55,13 @@ void msdos_put_inode(struct inode *inode)
void msdos_put_super(struct super_block *sb)
{
cache_inval_dev(sb->s_dev);
set_blocksize (sb->s_dev,BLOCK_SIZE);
lock_super(sb);
sb->s_dev = 0;
unlock_super(sb);
#ifdef MODULE
MOD_DEC_USE_COUNT;
#endif
return;
}
......@@ -160,7 +172,21 @@ struct super_block *msdos_read_super(struct super_block *s,void *data,
}
cache_init();
lock_super(s);
bh = bread(s->s_dev, 0, BLOCK_SIZE);
if (MAJOR(s->s_dev) == FLOPPY_MAJOR){
/* Patch for floppy which lacks a table ??? */
static int tbdef[]={
1024,1024,1024,1024,1024,
1024,1024,1024,1024,1024,
1024,1024,1024,1024,1024,
1024,1024,1024,1024,1024,
1024,1024,1024,1024,1024,
1024,1024,1024,1024,1024,
1024,1024,1024,1024,1024,
};
blksize_size[FLOPPY_MAJOR] = tbdef;
}
set_blocksize (s->s_dev,SECTOR_SIZE);
bh = bread(s->s_dev, 0, SECTOR_SIZE);
unlock_super(s);
if (bh == NULL) {
s->s_dev = 0;
......@@ -168,9 +194,11 @@ struct super_block *msdos_read_super(struct super_block *s,void *data,
return NULL;
}
b = (struct msdos_boot_sector *) bh->b_data;
s->s_blocksize = 1024; /* we cannot handle anything else yet */
s->s_blocksize_bits = 10; /* we cannot handle anything else yet */
s->s_blocksize = 512; /* Using this small block size solve the */
/* the misfit with buffer cache and cluster */
/* because cluster (DOS) are often aligned */
/* on odd sector */
s->s_blocksize_bits = 9; /* we cannot handle anything else yet */
/*
* The DOS3 partition size limit is *not* 32M as many people think.
* Instead, it is 64K sectors (with the usual sector size being
......@@ -219,6 +247,7 @@ struct super_block *msdos_read_super(struct super_block *s,void *data,
}
brelse(bh);
if (error || debug) {
/* The MSDOS_CAN_BMAP is obsolete, but left just to remember */
printk("[MS-DOS FS Rel. 12,FAT %d,check=%c,conv=%c,"
"uid=%d,gid=%d,umask=%03o%s]\n",MSDOS_SB(s)->fat_bits,check,
conversion,uid,gid,umask,MSDOS_CAN_BMAP(MSDOS_SB(s)) ?
......@@ -255,6 +284,9 @@ struct super_block *msdos_read_super(struct super_block *s,void *data,
printk("get root inode failed\n");
return NULL;
}
#ifdef MODULE
MOD_INC_USE_COUNT;
#endif
return s;
}
......@@ -290,15 +322,13 @@ int msdos_bmap(struct inode *inode,int block)
int cluster,offset;
sb = MSDOS_SB(inode->i_sb);
if ((sb->cluster_size & 1) || (sb->data_start & 1)) return 0;
if (inode->i_ino == MSDOS_ROOT_INO) {
if (sb->dir_start & 1) return 0;
return (sb->dir_start >> 1)+block;
return sb->dir_start + block;
}
cluster = (block*2)/sb->cluster_size;
offset = (block*2) % sb->cluster_size;
cluster = block/sb->cluster_size;
offset = block % sb->cluster_size;
if (!(cluster = get_cluster(inode,cluster))) return 0;
return ((cluster-2)*sb->cluster_size+sb->data_start+offset) >> 1;
return (cluster-2)*sb->cluster_size+sb->data_start+offset;
}
......@@ -332,7 +362,7 @@ void msdos_read_inode(struct inode *inode)
return;
}
if (!(bh = bread(inode->i_dev,inode->i_ino >> MSDOS_DPB_BITS,
BLOCK_SIZE))) {
SECTOR_SIZE))) {
printk("dev = 0x%04X, ino = %ld\n",inode->i_dev,inode->i_ino);
panic("msdos_read_inode: unable to read i-node block");
}
......@@ -367,9 +397,7 @@ void msdos_read_inode(struct inode *inode)
inode->i_mode = MSDOS_MKMODE(raw_entry->attr,(IS_NOEXEC(inode)
? S_IRUGO|S_IWUGO : S_IRWXUGO) & ~MSDOS_SB(inode->i_sb)->fs_umask) |
S_IFREG;
inode->i_op = MSDOS_CAN_BMAP(MSDOS_SB(inode->i_sb)) ?
&msdos_file_inode_operations :
&msdos_file_inode_operations_no_bmap;
inode->i_op = &msdos_file_inode_operations; /* Now can always bmap */
MSDOS_I(inode)->i_start = CF_LE_W(raw_entry->start);
inode->i_nlink = 1;
inode->i_size = CF_LE_L(raw_entry->size);
......@@ -395,7 +423,7 @@ void msdos_write_inode(struct inode *inode)
inode->i_dirt = 0;
if (inode->i_ino == MSDOS_ROOT_INO || !inode->i_nlink) return;
if (!(bh = bread(inode->i_dev,inode->i_ino >> MSDOS_DPB_BITS,
BLOCK_SIZE))) {
SECTOR_SIZE))) {
printk("dev = 0x%04X, ino = %ld\n",inode->i_dev,inode->i_ino);
panic("msdos_write_inode: unable to read i-node block");
}
......@@ -451,3 +479,32 @@ int msdos_notify_change(struct inode * inode,struct iattr * attr)
~MSDOS_SB(inode->i_sb)->fs_umask;
return 0;
}
#ifdef MODULE
char kernel_version[] = UTS_RELEASE;
static struct file_system_type msdos_fs_type = {
msdos_read_super, "msdos", 1, NULL
};
int init_module(void)
{
register_filesystem(&msdos_fs_type);
return 0;
}
void cleanup_module(void)
{
if (MOD_IN_USE)
printk("ne: device busy, remove delayed\n");
else
{
unregister_filesystem(&msdos_fs_type);
/* This is not clear why the floppy drivers does not initialise */
/* the table, but we left it the way we saw it first */
blksize_size[FLOPPY_MAJOR] = NULL;
}
}
#endif
......@@ -12,6 +12,8 @@
#include <linux/string.h>
#include <linux/stat.h>
#define PRINTK(x)
/* Well-known binary file extensions */
static char bin_extensions[] =
......@@ -109,9 +111,9 @@ void unlock_fat(struct super_block *sb)
int msdos_add_cluster(struct inode *inode)
{
int count,nr,limit,last,current,sector;
void *data;
int count,nr,limit,last,current,sector,last_sector;
struct buffer_head *bh;
int cluster_size = MSDOS_SB(inode->i_sb)->cluster_size;
if (inode->i_ino == MSDOS_ROOT_INO) return -ENOSPC;
if (!MSDOS_SB(inode->i_sb)->free_clusters) return -ENOSPC;
......@@ -161,44 +163,29 @@ printk("last = %d\n",last);
#ifdef DEBUG
if (last) printk("next set to %d\n",fat_access(inode->i_sb,last,-1));
#endif
for (current = 0; current < MSDOS_SB(inode->i_sb)->cluster_size;
current++) {
sector = MSDOS_SB(inode->i_sb)->data_start+(nr-2)*
MSDOS_SB(inode->i_sb)->cluster_size+current;
#ifdef DEBUG
printk("zeroing sector %d\n",sector);
#endif
if (current < MSDOS_SB(inode->i_sb)->cluster_size-1 &&
!(sector & 1)) {
if (!(bh = getblk(inode->i_dev,sector >> 1,
BLOCK_SIZE)))
printk("getblk failed\n");
else {
memset(bh->b_data,0,BLOCK_SIZE);
bh->b_uptodate = 1;
}
current++;
}
sector = MSDOS_SB(inode->i_sb)->data_start+(nr-2)*cluster_size;
last_sector = sector + cluster_size;
for ( ; sector < last_sector; sector++) {
#ifdef DEBUG
printk("zeroing sector %d\n",sector);
#endif
if (!(bh = getblk(inode->i_dev,sector,SECTOR_SIZE)))
printk("getblk failed\n");
else {
if (!(bh = msdos_sread(inode->i_dev,sector,
&data)))
printk("msdos_sread failed\n");
else memset(data,0,SECTOR_SIZE);
}
if (bh) {
memset(bh->b_data,0,SECTOR_SIZE);
bh->b_uptodate = 1;
mark_buffer_dirty(bh, 1);
brelse(bh);
}
}
inode->i_blocks += MSDOS_SB(inode->i_sb)->cluster_size;
inode->i_blocks += cluster_size;
if (S_ISDIR(inode->i_mode)) {
if (inode->i_size & (SECTOR_SIZE-1)) {
fs_panic(inode->i_sb,"Odd directory size");
inode->i_size = (inode->i_size+SECTOR_SIZE) &
~(SECTOR_SIZE-1);
}
inode->i_size += SECTOR_SIZE*MSDOS_SB(inode->i_sb)->
cluster_size;
inode->i_size += SECTOR_SIZE*cluster_size;
#ifdef DEBUG
printk("size is %d now (%x)\n",inode->i_size,inode);
#endif
......@@ -273,17 +260,21 @@ int msdos_get_entry(struct inode *dir, loff_t *pos,struct buffer_head **bh,
while (1) {
offset = *pos;
PRINTK (("get_entry offset %d\n",offset));
if ((sector = msdos_smap(dir,offset >> SECTOR_BITS)) == -1)
return -1;
PRINTK (("get_entry sector %d %p\n",sector,*bh));
if (!sector)
return -1; /* beyond EOF */
*pos += sizeof(struct msdos_dir_entry);
if (*bh)
brelse(*bh);
PRINTK (("get_entry sector apres brelse\n"));
if (!(*bh = msdos_sread(dir->i_dev,sector,&data))) {
printk("Directory sread (sector %d) failed\n",sector);
continue;
}
PRINTK (("get_entry apres sread\n"));
*de = (struct msdos_dir_entry *) (data+(offset &
(SECTOR_SIZE-1)));
return (sector << MSDOS_DPS_BITS)+((offset & (SECTOR_SIZE-1)) >>
......
......@@ -13,6 +13,8 @@
#include <linux/string.h>
#include <linux/stat.h>
#define PRINTK(x)
/* MS-DOS "device special files" */
static char *reserved_names[] = {
......@@ -109,6 +111,8 @@ int msdos_lookup(struct inode *dir,const char *name,int len,
struct msdos_dir_entry *de;
struct buffer_head *bh;
struct inode *next;
PRINTK (("msdos_lookup\n"));
*result = NULL;
if (!dir) return -ENOENT;
......@@ -116,6 +120,7 @@ int msdos_lookup(struct inode *dir,const char *name,int len,
iput(dir);
return -ENOENT;
}
PRINTK (("msdos_lookup 2\n"));
if (len == 1 && name[0] == '.') {
*result = dir;
return 0;
......@@ -127,21 +132,26 @@ int msdos_lookup(struct inode *dir,const char *name,int len,
if (!(*result = iget(dir->i_sb,ino))) return -EACCES;
return 0;
}
PRINTK (("msdos_lookup 3\n"));
if ((res = msdos_find(dir,name,len,&bh,&de,&ino)) < 0) {
iput(dir);
return res;
}
PRINTK (("msdos_lookup 4\n"));
if (bh) brelse(bh);
PRINTK (("msdos_lookup 4.5\n"));
/* printk("lookup: ino=%d\n",ino); */
if (!(*result = iget(dir->i_sb,ino))) {
iput(dir);
return -EACCES;
}
PRINTK (("msdos_lookup 5\n"));
if (MSDOS_I(*result)->i_busy) { /* mkdir in progress */
iput(*result);
iput(dir);
return -ENOENT;
}
PRINTK (("msdos_lookup 6\n"));
while (MSDOS_I(*result)->i_old) {
next = MSDOS_I(*result)->i_old;
iput(*result);
......@@ -151,7 +161,9 @@ int msdos_lookup(struct inode *dir,const char *name,int len,
return -ENOENT;
}
}
PRINTK (("msdos_lookup 7\n"));
iput(dir);
PRINTK (("msdos_lookup 8\n"));
return 0;
}
......
......@@ -94,11 +94,11 @@ static int proc_lookuproot(struct inode * dir,const char * name, int len,
/* nothing */;
if (i >= 0) {
ino = root_dir[i].low_ino;
if (ino == 1) {
if (ino == PROC_ROOT_INO) {
*result = dir;
return 0;
}
if (ino == 7) /* self modifying inode ... */
if (ino == PROC_SELF) /* self modifying inode ... */
ino = (current->pid << 16) + 2;
} else {
pid = 0;
......
......@@ -64,8 +64,8 @@ struct inode_operations sysv_dir_inode_operations = {
NULL /* permission */
};
static int sysv_readdir(struct inode * inode, struct file * filp,
struct dirent * dirent, int count)
static int sysv_readdir1 (struct inode * inode, struct file * filp,
struct dirent * dirent)
{
struct super_block * sb;
unsigned int offset,i;
......@@ -90,16 +90,23 @@ static int sysv_readdir(struct inode * inode, struct file * filp,
offset += SYSV_DIRSIZE;
filp->f_pos += SYSV_DIRSIZE;
if (de->inode) {
struct sysv_dir_entry sde;
/* Copy the directory entry first, because the directory
* might be modified while we sleep in put_fs_byte...
*/
memcpy(&sde, de, sizeof(struct sysv_dir_entry));
for (i = 0; i < SYSV_NAMELEN; i++)
if ((c = de->name[i]) != 0)
if ((c = sde.name[i]) != 0)
put_fs_byte(c,i+dirent->d_name);
else
break;
if (i) {
if (de->inode > inode->i_sb->sv_ninodes)
if (sde.inode > inode->i_sb->sv_ninodes)
printk("sysv_readdir: Bad inode number on dev 0x%04x, ino %ld, offset 0x%04lx: %d is out of range\n",
inode->i_dev, inode->i_ino, (off_t) filp->f_pos - SYSV_DIRSIZE, de->inode);
put_fs_long(de->inode,&dirent->d_ino);
inode->i_dev, inode->i_ino, (off_t) filp->f_pos - SYSV_DIRSIZE, sde.inode);
put_fs_long(sde.inode,&dirent->d_ino);
put_fs_byte(0,i+dirent->d_name);
put_fs_word(i,&dirent->d_reclen);
brelse(bh);
......@@ -111,3 +118,30 @@ static int sysv_readdir(struct inode * inode, struct file * filp,
}
return 0;
}
static int sysv_readdir(struct inode * inode, struct file * filp,
struct dirent * dirent, int count)
{
int retval, stored;
/* compatibility */
if (count==1)
return sysv_readdir1(inode,filp,dirent);
retval = verify_area(VERIFY_WRITE, dirent, count);
if (retval)
return retval;
stored = 0;
while (count >= sizeof(struct dirent)) {
retval = sysv_readdir1(inode,filp,dirent);
if (retval < 0)
return retval;
if (!retval)
return stored;
dirent = (struct dirent *)((char *) dirent + retval);
stored += retval;
count -= retval;
}
return stored;
}
......@@ -722,14 +722,13 @@ extern int sysv_notify_change(struct inode *inode, struct iattr *attr)
if ((error = inode_change_ok(inode, attr)) != 0)
return error;
inode_setattr(inode, attr);
if (attr->ia_valid & ATTR_MODE)
if (inode->i_sb->sv_kludge_symlinks)
if (inode->i_mode == COH_KLUDGE_SYMLINK_MODE) {
inode->i_mode = COH_KLUDGE_NOT_SYMLINK;
inode->i_dirt = 1;
}
if (attr->ia_mode == COH_KLUDGE_SYMLINK_MODE)
attr->ia_mode = COH_KLUDGE_NOT_SYMLINK;
inode_setattr(inode, attr);
return 0;
}
......
......@@ -7,6 +7,10 @@
#
# Note 2! The CFLAGS definitions are now in the main makefile...
ifndef CONFIG_UMSDOS_FS
CFLAGS := $(CFLAGS) -DMODULE
endif
.c.s:
$(CC) $(CFLAGS) -S $<
.c.o:
......
......@@ -350,6 +350,7 @@ int umsdos_inode2entry (
if (emddir == NULL){
/* This is a DOS directory */
struct file filp;
filp.f_reada = 1;
filp.f_pos = 0;
while (1){
struct dirent dirent;
......@@ -370,6 +371,7 @@ int umsdos_inode2entry (
}else{
/* skip . and .. see umsdos_readdir_x() */
struct file filp;
filp.f_reada = 1;
filp.f_pos = UMSDOS_SPECIAL_DIRFPOS;
while (1){
struct dirent dirent;
......@@ -623,6 +625,7 @@ int umsdos_hlink2inode (struct inode *hlink, struct inode **result)
iput (hlink);
}else{
struct file filp;
filp.f_reada = 1;
filp.f_pos = 0;
PRINTK (("hlink2inode "));
if (umsdos_file_read_kmem (hlink,&filp,path,hlink->i_size)
......
......@@ -197,6 +197,7 @@ int umsdos_writeentry (
memset (entry->spare,0,sizeof(entry->spare));
}
filp.f_pos = info->f_pos;
filp.f_reada = 0;
ret = umsdos_emd_dir_write(emd_dir,&filp,(char*)entry,info->recsize);
if (ret != 0){
printk ("UMSDOS: problem with EMD file. Can't write\n");
......@@ -296,6 +297,7 @@ static int umsdos_find (
buf.pos = 0;
buf.size = 0;
buf.filp.f_pos = 0;
buf.filp.f_reada = 1;
empty.found = 0;
empty.posok = emd_dir->i_size;
empty.onesize = 0;
......@@ -454,6 +456,7 @@ int umsdos_isempty (struct inode *dir)
struct file filp;
/* Find an empty slot */
filp.f_pos = 0;
filp.f_reada = 1;
filp.f_flags = O_RDONLY;
ret = 1;
while (filp.f_pos < emd_dir->i_size){
......
......@@ -75,7 +75,7 @@ struct file_operations umsdos_file_operations = {
NULL, /* readdir - bad */
NULL, /* select - default */
NULL, /* ioctl - default */
msdos_mmap, /* mmap */
generic_mmap, /* mmap */
NULL, /* no special open is needed */
NULL, /* release */
file_fsync /* fsync */
......
......@@ -18,6 +18,11 @@
#include <linux/stat.h>
#include <linux/umsdos_fs.h>
#ifdef MODULE
#include <linux/module.h>
#include "../../tools/version.h"
#endif
struct inode *pseudo_root=NULL; /* Useful to simulate the pseudo DOS */
/* directory. See UMSDOS_readdir_x() */
......@@ -48,6 +53,9 @@ void UMSDOS_put_inode(struct inode *inode)
void UMSDOS_put_super(struct super_block *sb)
{
msdos_put_super(sb);
#ifdef MODULE
MOD_DEC_USE_COUNT;
#endif
}
......@@ -395,7 +403,7 @@ struct super_block *UMSDOS_read_super(
msdos directory, with all limitation of msdos.
*/
struct super_block *sb = msdos_read_super(s,data,silent);
printk ("UMSDOS Alpha 0.4 (compatibility level %d.%d)\n"
printk ("UMSDOS Alpha 0.5 (compatibility level %d.%d, fast msdos)\n"
,UMSDOS_VERSION,UMSDOS_RELEASE);
if (sb != NULL){
sb->s_op = &umsdos_sops;
......@@ -463,8 +471,37 @@ struct super_block *UMSDOS_read_super(
}
iput (pseudo);
}
#ifdef MODULE
MOD_INC_USE_COUNT;
#endif
}
return sb;
}
#ifdef MODULE
char kernel_version[] = UTS_RELEASE;
static struct file_system_type umsdos_fs_type = {
UMSDOS_read_super, "umsdos", 1, NULL
};
int init_module(void)
{
register_filesystem(&umsdos_fs_type);
return 0;
}
void cleanup_module(void)
{
if (MOD_IN_USE)
printk("Umsdos: file system in use, remove delayed\n");
else
{
unregister_filesystem(&umsdos_fs_type);
}
}
#endif
......@@ -34,6 +34,7 @@ static int umsdos_readlink_x (
int ret = inode->i_size;
struct file filp;
filp.f_pos = 0;
filp.f_reada = 0;
if (ret > bufsiz) ret = bufsiz;
if ((*msdos_read) (inode, &filp, buffer,ret) != ret){
ret = -EIO;
......
#ifndef __ALPHA_SYSTEM_H
#define __ALPHA_SYSTEM_H
/*
* Common PAL-code
*/
#define PAL_halt 0
#define PAL_cflush 1
#define PAL_draina 2
#define PAL_cobratt 9
#define PAL_bpt 128
#define PAL_bugchk 129
#define PAL_chmk 131
#define PAL_callsys 131
#define PAL_imb 134
#define PAL_rduniq 158
#define PAL_wruniq 159
#define PAL_gentrap 170
#define PAL_nphalt 190
/*
* OSF specific PAL-code
*/
#define PAL_mtpr_mces 17
#define PAL_wrfen 43
#define PAL_wrvptptr 45
#define PAL_jtopal 46
#define PAL_swpctx 48
#define PAL_wrval 49
#define PAL_rdval 50
#define PAL_tbi 51
#define PAL_wrent 52
#define PAL_swpipl 53
#define PAL_rdps 54
#define PAL_wrkgp 55
#define PAL_wrusp 56
#define PAL_wrperfmon 57
#define PAL_rdusp 58
#define PAL_whami 60
#define PAL_rtsys 61
#define PAL_rti 63
#define invalidate_all() \
__asm__ __volatile__( \
"lda $16,-2($31)\n\t" \
".long 51" \
: : :"$1", "$16", "$17", "$22","$23","$24","$25")
#define invalidate() \
__asm__ __volatile__( \
"lda $16,-1($31)\n\t" \
".long 51" \
: : :"$1", "$16", "$17", "$22","$23","$24","$25")
#define swpipl(__new_ipl) \
({ unsigned long __old_ipl; \
__asm__ __volatile__( \
"bis %1,%1,$16\n\t" \
".long 53\n\t" \
"bis $0,$0,%0" \
: "=r" (__old_ipl) \
: "r" (__new_ipl) \
: "$0", "$1", "$16", "$22", "$23", "$24", "$25"); \
__old_ipl; })
#endif
......@@ -21,135 +21,140 @@
*
*/
/*
* General defines.
*/
#define SONY_XA_DISK_TYPE 0x20
/*
* Offsets (from the base address) and bits for the various write registers
* of the drive.
*/
#define SONY_CMD_REG_OFFSET 0
#define SONY_PARAM_REG_OFFSET 1
#define SONY_WRITE_REG_OFFSET 2
#define SONY_CONTROL_REG_OFFSET 3
# define SONY_ATTN_CLR_BIT 0x01
# define SONY_RES_RDY_CLR_BIT 0x02
# define SONY_DATA_RDY_CLR_BIT 0x04
# define SONY_ATTN_INT_EN_BIT 0x08
# define SONY_RES_RDY_INT_EN_BIT 0x10
# define SONY_DATA_RDY_INT_EN_BIT 0x20
# define SONY_PARAM_CLR_BIT 0x40
# define SONY_DRIVE_RESET_BIT 0x80
#define SONY_CMD_REG_OFFSET 0
#define SONY_PARAM_REG_OFFSET 1
#define SONY_WRITE_REG_OFFSET 2
#define SONY_CONTROL_REG_OFFSET 3
# define SONY_ATTN_CLR_BIT 0x01
# define SONY_RES_RDY_CLR_BIT 0x02
# define SONY_DATA_RDY_CLR_BIT 0x04
# define SONY_ATTN_INT_EN_BIT 0x08
# define SONY_RES_RDY_INT_EN_BIT 0x10
# define SONY_DATA_RDY_INT_EN_BIT 0x20
# define SONY_PARAM_CLR_BIT 0x40
# define SONY_DRIVE_RESET_BIT 0x80
/*
* Offsets (from the base address) and bits for the various read registers
* of the drive.
*/
#define SONY_STATUS_REG_OFFSET 0
# define SONY_ATTN_BIT 0x01
# define SONY_RES_RDY_BIT 0x02
# define SONY_DATA_RDY_BIT 0x04
# define SONY_ATTN_INT_ST_BIT 0x08
# define SONY_RES_RDY_INT_ST_BIT 0x10
# define SONY_DATA_RDY_INT_ST_BIT 0x20
# define SONY_DATA_REQUEST_BIT 0x40
# define SONY_BUSY_BIT 0x80
#define SONY_RESULT_REG_OFFSET 1
#define SONY_READ_REG_OFFSET 2
#define SONY_FIFOST_REG_OFFSET 3
# define SONY_PARAM_WRITE_RDY_BIT 0x01
# define SONY_PARAM_REG_EMPTY_BIT 0x02
# define SONY_RES_REG_NOT_EMP_BIT 0x04
# define SONY_RES_REG_FULL_BIT 0x08
#define LOG_START_OFFSET 150 /* Offset of first logical sector */
#define SONY_DETECT_TIMEOUT 80 /* Maximum amount of time
that drive detection code
will wait for response
from drive (in 1/100th's
of seconds). */
#define SONY_STATUS_REG_OFFSET 0
# define SONY_ATTN_BIT 0x01
# define SONY_RES_RDY_BIT 0x02
# define SONY_DATA_RDY_BIT 0x04
# define SONY_ATTN_INT_ST_BIT 0x08
# define SONY_RES_RDY_INT_ST_BIT 0x10
# define SONY_DATA_RDY_INT_ST_BIT 0x20
# define SONY_DATA_REQUEST_BIT 0x40
# define SONY_BUSY_BIT 0x80
#define SONY_RESULT_REG_OFFSET 1
#define SONY_READ_REG_OFFSET 2
#define SONY_FIFOST_REG_OFFSET 3
# define SONY_PARAM_WRITE_RDY_BIT 0x01
# define SONY_PARAM_REG_EMPTY_BIT 0x02
# define SONY_RES_REG_NOT_EMP_BIT 0x04
# define SONY_RES_REG_FULL_BIT 0x08
#define LOG_START_OFFSET 150 /* Offset of first logical sector */
#define SONY_DETECT_TIMEOUT 80 /* Maximum amount of time
that drive detection code
will wait for response
from drive (in 1/100th's
of seconds). */
#define SONY_JIFFIES_TIMEOUT 500 /* Maximum number of times the
drive will wait/try for an
operation */
#define SONY_RESET_TIMEOUT 100 /* Maximum number of times the
drive will wait/try a reset
operation */
#define SONY_READY_RETRIES 20000 /* How many times to retry a
spin waiting for a register
to come ready */
#define MAX_CDU31A_RETRIES 3 /* How many times to retry an
operation */
#define SONY_JIFFIES_TIMEOUT 1000 /* Maximum number of times the
drive will wait/try for an
operation */
#define SONY_RESET_TIMEOUT 100 /* Maximum number of times the
drive will wait/try a reset
operation */
#define SONY_READY_RETRIES 20000 /* How many times to retry a
spin waiting for a register
to come ready */
#define MAX_CDU31A_RETRIES 3 /* How many times to retry an
operation */
/* Commands to request or set drive control parameters and disc information */
#define SONY_REQ_DRIVE_CONFIG_CMD 0x00 /* Returns s_sony_drive_config */
#define SONY_REQ_DRIVE_MODE_CMD 0x01
#define SONY_REQ_DRIVE_PARAM_CMD 0x02
#define SONY_REQ_MECH_STATUS_CMD 0x03
#define SONY_REQ_AUDIO_STATUS_CMD 0x04
#define SONY_SET_DRIVE_PARAM_CMD 0x10
#define SONY_REQ_TOC_DATA_CMD 0x20 /* Returns s_sony_toc */
#define SONY_REQ_SUBCODE_ADDRESS_CMD 0x21 /* Returns s_sony_subcode */
#define SONY_REQ_UPC_EAN_CMD 0x22
#define SONY_REQ_ISRC_CMD 0x23
#define SONY_REQ_TOC_DATA_SPEC_CMD 0x24
#define SONY_REQ_DRIVE_CONFIG_CMD 0x00 /* Returns s_sony_drive_config */
#define SONY_REQ_DRIVE_MODE_CMD 0x01
#define SONY_REQ_DRIVE_PARAM_CMD 0x02
#define SONY_REQ_MECH_STATUS_CMD 0x03
#define SONY_REQ_AUDIO_STATUS_CMD 0x04
#define SONY_SET_DRIVE_PARAM_CMD 0x10
#define SONY_REQ_TOC_DATA_CMD 0x20 /* Returns s_sony_toc */
#define SONY_REQ_SUBCODE_ADDRESS_CMD 0x21 /* Returns s_sony_subcode */
#define SONY_REQ_UPC_EAN_CMD 0x22
#define SONY_REQ_ISRC_CMD 0x23
#define SONY_REQ_TOC_DATA_SPEC_CMD 0x24
/* Commands to request information from the drive */
#define SONY_READ_TOC_CMD 0x30
#define SONY_SEEK_CMD 0x31
#define SONY_READ_CMD 0x32
#define SONY_READ_BLKERR_STAT_CMD 0x34
#define SONY_ABORT_CMD 0x35
#define SONY_READ_TOC_SPEC_CMD 0x36
#define SONY_READ_TOC_CMD 0x30
#define SONY_SEEK_CMD 0x31
#define SONY_READ_CMD 0x32
#define SONY_READ_BLKERR_STAT_CMD 0x34
#define SONY_ABORT_CMD 0x35
#define SONY_READ_TOC_SPEC_CMD 0x36
/* Commands to control audio */
#define SONY_AUDIO_PLAYBACK_CMD 0x40
#define SONY_AUDIO_STOP_CMD 0x41
#define SONY_AUDIO_SCAN_CMD 0x42
#define SONY_AUDIO_PLAYBACK_CMD 0x40
#define SONY_AUDIO_STOP_CMD 0x41
#define SONY_AUDIO_SCAN_CMD 0x42
/* Miscellaneous control commands */
#define SONY_EJECT_CMD 0x50
#define SONY_SPIN_UP_CMD 0x51
#define SONY_SPIN_DOWN_CMD 0x52
#define SONY_EJECT_CMD 0x50
#define SONY_SPIN_UP_CMD 0x51
#define SONY_SPIN_DOWN_CMD 0x52
/* Diagnostic commands */
#define SONY_WRITE_BUFFER_CMD 0x60
#define SONY_READ_BUFFER_CMD 0x61
#define SONY_DIAGNOSTICS_CMD 0x62
#define SONY_WRITE_BUFFER_CMD 0x60
#define SONY_READ_BUFFER_CMD 0x61
#define SONY_DIAGNOSTICS_CMD 0x62
/*
* The following are command parameters for the set drive parameter command
*/
#define SONY_SD_DECODE_PARAM 0x00
#define SONY_SD_INTERFACE_PARAM 0x01
#define SONY_SD_BUFFERING_PARAM 0x02
#define SONY_SD_AUDIO_PARAM 0x03
#define SONY_SD_AUDIO_VOLUME 0x04
#define SONY_SD_MECH_CONTROL 0x05
#define SONY_SD_AUTO_SPIN_DOWN_TIME 0x06
#define SONY_SD_DECODE_PARAM 0x00
#define SONY_SD_INTERFACE_PARAM 0x01
#define SONY_SD_BUFFERING_PARAM 0x02
#define SONY_SD_AUDIO_PARAM 0x03
#define SONY_SD_AUDIO_VOLUME 0x04
#define SONY_SD_MECH_CONTROL 0x05
#define SONY_SD_AUTO_SPIN_DOWN_TIME 0x06
/*
* The following extract information from the drive configuration about
* the drive itself.
*/
#define SONY_HWC_GET_LOAD_MECH(c) (c.hw_config[0] & 0x03)
#define SONY_HWC_EJECT(c) (c.hw_config[0] & 0x04)
#define SONY_HWC_LED_SUPPORT(c) (c.hw_config[0] & 0x08)
#define SONY_HWC_DOUBLE_SPEED(c) (c.hw_config[0] & 0x10)
#define SONY_HWC_GET_BUF_MEM_SIZE(c) ((c.hw_config[0] & 0xc0) >> 6)
#define SONY_HWC_AUDIO_PLAYBACK(c) (c.hw_config[1] & 0x01)
#define SONY_HWC_ELECTRIC_VOLUME(c) (c.hw_config[1] & 0x02)
#define SONY_HWC_ELECTRIC_VOLUME_CTL(c) (c.hw_config[1] & 0x04)
#define SONY_HWC_CADDY_LOAD_MECH 0x00
#define SONY_HWC_TRAY_LOAD_MECH 0x01
#define SONY_HWC_POPUP_LOAD_MECH 0x02
#define SONY_HWC_UNKWN_LOAD_MECH 0x03
#define SONY_HWC_8KB_BUFFER 0x00
#define SONY_HWC_32KB_BUFFER 0x01
#define SONY_HWC_64KB_BUFFER 0x02
#define SONY_HWC_UNKWN_BUFFER 0x03
#define SONY_HWC_GET_LOAD_MECH(c) (c.hw_config[0] & 0x03)
#define SONY_HWC_EJECT(c) (c.hw_config[0] & 0x04)
#define SONY_HWC_LED_SUPPORT(c) (c.hw_config[0] & 0x08)
#define SONY_HWC_DOUBLE_SPEED(c) (c.hw_config[0] & 0x10)
#define SONY_HWC_GET_BUF_MEM_SIZE(c) ((c.hw_config[0] & 0xc0) >> 6)
#define SONY_HWC_AUDIO_PLAYBACK(c) (c.hw_config[1] & 0x01)
#define SONY_HWC_ELECTRIC_VOLUME(c) (c.hw_config[1] & 0x02)
#define SONY_HWC_ELECTRIC_VOLUME_CTL(c) (c.hw_config[1] & 0x04)
#define SONY_HWC_CADDY_LOAD_MECH 0x00
#define SONY_HWC_TRAY_LOAD_MECH 0x01
#define SONY_HWC_POPUP_LOAD_MECH 0x02
#define SONY_HWC_UNKWN_LOAD_MECH 0x03
#define SONY_HWC_8KB_BUFFER 0x00
#define SONY_HWC_32KB_BUFFER 0x01
#define SONY_HWC_64KB_BUFFER 0x02
#define SONY_HWC_UNKWN_BUFFER 0x03
/*
* This is the complete status returned from the drive configuration request
......@@ -168,8 +173,8 @@ struct s_sony_drive_config
struct s_sony_subcode
{
unsigned char exec_status[2];
unsigned char address :4;
unsigned char control :4;
unsigned char address :4;
unsigned char control :4;
unsigned char track_num;
unsigned char index_num;
unsigned char rel_msf[3];
......@@ -177,6 +182,7 @@ struct s_sony_subcode
unsigned char abs_msf[3];
};
#define MAX_TRACKS 100 /* The maximum tracks a disk may have. */
/*
* The following is returned from the request TOC (Table Of Contents) command.
* (last_track_num-first_track_num+1) values are valid in tracks.
......@@ -184,30 +190,91 @@ struct s_sony_subcode
struct s_sony_toc
{
unsigned char exec_status[2];
unsigned char address0 :4;
unsigned char control0 :4;
unsigned char address0 :4;
unsigned char control0 :4;
unsigned char point0;
unsigned char first_track_num;
unsigned char disk_type;
unsigned char dummy0;
unsigned char address1 :4;
unsigned char control1 :4;
unsigned char point1;
unsigned char last_track_num;
unsigned char dummy1;
unsigned char dummy2;
unsigned char address2 :4;
unsigned char control2 :4;
unsigned char point2;
unsigned char lead_out_start_msf[3];
struct
{
unsigned char address :4;
unsigned char control :4;
unsigned char track;
unsigned char track_start_msf[3];
} tracks[MAX_TRACKS];
unsigned int lead_out_start_lba;
};
struct s_sony_session_toc
{
unsigned char exec_status[2];
unsigned char session_number;
unsigned char address0 :4;
unsigned char control0 :4;
unsigned char point0;
unsigned char first_track_num;
unsigned char disk_type;
unsigned char dummy0;
unsigned char address1 :4;
unsigned char control1 :4;
unsigned char address1 :4;
unsigned char control1 :4;
unsigned char point1;
unsigned char last_track_num;
unsigned char dummy1;
unsigned char dummy2;
unsigned char address2 :4;
unsigned char control2 :4;
unsigned char address2 :4;
unsigned char control2 :4;
unsigned char point2;
unsigned char lead_out_start_msf[3];
unsigned char addressb0 :4;
unsigned char controlb0 :4;
unsigned char pointb0;
unsigned char next_poss_prog_area_msf[3];
unsigned char num_mode_5_pointers;
unsigned char max_start_outer_leadout_msf[3];
unsigned char addressb1 :4;
unsigned char controlb1 :4;
unsigned char pointb1;
unsigned char dummyb0_1[4];
unsigned char num_skip_interval_pointers;
unsigned char num_skip_track_assignments;
unsigned char dummyb0_2;
unsigned char addressb2 :4;
unsigned char controlb2 :4;
unsigned char pointb2;
unsigned char tracksb2[7];
unsigned char addressb3 :4;
unsigned char controlb3 :4;
unsigned char pointb3;
unsigned char tracksb3[7];
unsigned char addressb4 :4;
unsigned char controlb4 :4;
unsigned char pointb4;
unsigned char tracksb4[7];
unsigned char addressc0 :4;
unsigned char controlc0 :4;
unsigned char pointc0;
unsigned char dummyc0[7];
struct
{
unsigned char address :4;
unsigned char control :4;
unsigned char address :4;
unsigned char control :4;
unsigned char track;
unsigned char track_start_msf[3];
} tracks[100];
} tracks[MAX_TRACKS];
unsigned int start_track_lba;
unsigned int lead_out_start_lba;
};
......@@ -217,70 +284,71 @@ struct s_sony_toc
*/
/* Command error group */
#define SONY_ILL_CMD_ERR 0x10
#define SONY_ILL_PARAM_ERR 0x11
#define SONY_ILL_CMD_ERR 0x10
#define SONY_ILL_PARAM_ERR 0x11
/* Mechanism group */
#define SONY_NOT_LOAD_ERR 0x20
#define SONY_NO_DISK_ERR 0x21
#define SONY_NOT_SPIN_ERR 0x22
#define SONY_SPIN_ERR 0x23
#define SONY_SPINDLE_SERVO_ERR 0x25
#define SONY_FOCUS_SERVO_ERR 0x26
#define SONY_EJECT_MECH_ERR 0x29
#define SONY_AUDIO_PLAYING_ERR 0x2a
#define SONY_EMERGENCY_EJECT_ERR 0x2c
#define SONY_NOT_LOAD_ERR 0x20
#define SONY_NO_DISK_ERR 0x21
#define SONY_NOT_SPIN_ERR 0x22
#define SONY_SPIN_ERR 0x23
#define SONY_SPINDLE_SERVO_ERR 0x25
#define SONY_FOCUS_SERVO_ERR 0x26
#define SONY_EJECT_MECH_ERR 0x29
#define SONY_AUDIO_PLAYING_ERR 0x2a
#define SONY_EMERGENCY_EJECT_ERR 0x2c
/* Seek error group */
#define SONY_FOCUS_ERR 0x30
#define SONY_FRAME_SYNC_ERR 0x31
#define SONY_SUBCODE_ADDR_ERR 0x32
#define SONY_BLOCK_SYNC_ERR 0x33
#define SONY_HEADER_ADDR_ERR 0x34
#define SONY_FOCUS_ERR 0x30
#define SONY_FRAME_SYNC_ERR 0x31
#define SONY_SUBCODE_ADDR_ERR 0x32
#define SONY_BLOCK_SYNC_ERR 0x33
#define SONY_HEADER_ADDR_ERR 0x34
/* Read error group */
#define SONY_ILL_TRACK_R_ERR 0x40
#define SONY_MODE_0_R_ERR 0x41
#define SONY_ILL_MODE_R_ERR 0x42
#define SONY_ILL_BLOCK_SIZE_R_ERR 0x43
#define SONY_MODE_R_ERR 0x44
#define SONY_FORM_R_ERR 0x45
#define SONY_LEAD_OUT_R_ERR 0x46
#define SONY_BUFFER_OVERRUN_R_ERR 0x47
#define SONY_ILL_TRACK_R_ERR 0x40
#define SONY_MODE_0_R_ERR 0x41
#define SONY_ILL_MODE_R_ERR 0x42
#define SONY_ILL_BLOCK_SIZE_R_ERR 0x43
#define SONY_MODE_R_ERR 0x44
#define SONY_FORM_R_ERR 0x45
#define SONY_LEAD_OUT_R_ERR 0x46
#define SONY_BUFFER_OVERRUN_R_ERR 0x47
/* Data error group */
#define SONY_UNREC_CIRC_ERR 0x53
#define SONY_UNREC_LECC_ERR 0x57
#define SONY_UNREC_CIRC_ERR 0x53
#define SONY_UNREC_LECC_ERR 0x57
/* Subcode error group */
#define SONY_NO_TOC_ERR 0x60
#define SONY_SUBCODE_DATA_NVAL_ERR 0x61
#define SONY_FOCUS_ON_TOC_READ_ERR 0x63
#define SONY_FRAME_SYNC_ON_TOC_READ_ERR 0x64
#define SONY_TOC_DATA_ERR 0x65
#define SONY_NO_TOC_ERR 0x60
#define SONY_SUBCODE_DATA_NVAL_ERR 0x61
#define SONY_FOCUS_ON_TOC_READ_ERR 0x63
#define SONY_FRAME_SYNC_ON_TOC_READ_ERR 0x64
#define SONY_TOC_DATA_ERR 0x65
/* Hardware failure group */
#define SONY_HW_FAILURE_ERR 0x70
#define SONY_LEAD_IN_A_ERR 0x91
#define SONY_LEAD_OUT_A_ERR 0x92
#define SONY_DATA_TRACK_A_ERR 0x93
#define SONY_HW_FAILURE_ERR 0x70
#define SONY_LEAD_IN_A_ERR 0x91
#define SONY_LEAD_OUT_A_ERR 0x92
#define SONY_DATA_TRACK_A_ERR 0x93
/*
* The following are returned from the Read With Block Error Status command.
* They are not errors but information (Errors from the 0x5x group above may
* also be returned
*/
#define SONY_NO_CIRC_ERR_BLK_STAT 0x50
#define SONY_NO_LECC_ERR_BLK_STAT 0x54
#define SONY_RECOV_LECC_ERR_BLK_STAT 0x55
#define SONY_NO_ERR_DETECTION_STAT 0x59
#define SONY_NO_CIRC_ERR_BLK_STAT 0x50
#define SONY_NO_LECC_ERR_BLK_STAT 0x54
#define SONY_RECOV_LECC_ERR_BLK_STAT 0x55
#define SONY_NO_ERR_DETECTION_STAT 0x59
/*
* The following is not an error returned by the drive, but by the code
* that talks to the drive. It is returned because of a timeout.
*/
#define SONY_TIMEOUT_OP_ERR 0x01
#define SONY_SIGNAL_OP_ERR 0x02
#define SONY_TIMEOUT_OP_ERR 0x01
#define SONY_SIGNAL_OP_ERR 0x02
#define SONY_BAD_DATA_ERR 0x03
/*
......@@ -288,27 +356,27 @@ struct s_sony_toc
*/
/* Standard attention group */
#define SONY_EMER_EJECT_ATTN 0x2c
#define SONY_HW_FAILURE_ATTN 0x70
#define SONY_MECH_LOADED_ATTN 0x80
#define SONY_EJECT_PUSHED_ATTN 0x81
#define SONY_EMER_EJECT_ATTN 0x2c
#define SONY_HW_FAILURE_ATTN 0x70
#define SONY_MECH_LOADED_ATTN 0x80
#define SONY_EJECT_PUSHED_ATTN 0x81
/* Audio attention group */
#define SONY_AUDIO_PLAY_DONE_ATTN 0x90
#define SONY_LEAD_IN_ERR_ATTN 0x91
#define SONY_LEAD_OUT_ERR_ATTN 0x92
#define SONY_DATA_TRACK_ERR_ATTN 0x93
#define SONY_AUDIO_PLAYBACK_ERR_ATTN 0x94
#define SONY_AUDIO_PLAY_DONE_ATTN 0x90
#define SONY_LEAD_IN_ERR_ATTN 0x91
#define SONY_LEAD_OUT_ERR_ATTN 0x92
#define SONY_DATA_TRACK_ERR_ATTN 0x93
#define SONY_AUDIO_PLAYBACK_ERR_ATTN 0x94
/* Auto spin up group */
#define SONY_SPIN_UP_COMPLETE_ATTN 0x24
#define SONY_SPINDLE_SERVO_ERR_ATTN 0x25
#define SONY_FOCUS_SERVO_ERR_ATTN 0x26
#define SONY_TOC_READ_DONE_ATTN 0x62
#define SONY_FOCUS_ON_TOC_READ_ERR_ATTN 0x63
#define SONY_SYNC_ON_TOC_READ_ERR_ATTN 0x65
#define SONY_SPIN_UP_COMPLETE_ATTN 0x24
#define SONY_SPINDLE_SERVO_ERR_ATTN 0x25
#define SONY_FOCUS_SERVO_ERR_ATTN 0x26
#define SONY_TOC_READ_DONE_ATTN 0x62
#define SONY_FOCUS_ON_TOC_READ_ERR_ATTN 0x63
#define SONY_SYNC_ON_TOC_READ_ERR_ATTN 0x65
/* Auto eject group */
#define SONY_SPIN_DOWN_COMPLETE_ATTN 0x27
#define SONY_EJECT_COMPLETE_ATTN 0x28
#define SONY_EJECT_MECH_ERR_ATTN 0x29
#define SONY_SPIN_DOWN_COMPLETE_ATTN 0x27
#define SONY_EJECT_COMPLETE_ATTN 0x28
#define SONY_EJECT_MECH_ERR_ATTN 0x29
......@@ -117,7 +117,6 @@ struct page_info {
extern int nr_swap_pages;
extern int nr_free_pages;
#define MAX_SECONDARY_PAGES 20
#define NR_MEM_LISTS 6
struct mem_list {
......
......@@ -11,8 +11,8 @@
#define MSDOS_ROOT_INO 1 /* == MINIX_ROOT_INO */
#define SECTOR_SIZE 512 /* sector size (bytes) */
#define SECTOR_BITS 9 /* log2(SECTOR_SIZE) */
#define MSDOS_DPB (MSDOS_DPS*2) /* dir entries per block */
#define MSDOS_DPB_BITS 5 /* log2(MSDOS_DPB) */
#define MSDOS_DPB (MSDOS_DPS) /* dir entries per block */
#define MSDOS_DPB_BITS 4 /* log2(MSDOS_DPB) */
#define MSDOS_DPS (SECTOR_SIZE/sizeof(struct msdos_dir_entry))
#define MSDOS_DPS_BITS 4 /* log2(MSDOS_DPS) */
#define MSDOS_DIR_BITS 5 /* log2(sizeof(struct msdos_dir_entry)) */
......@@ -112,11 +112,10 @@ struct fat_cache {
static inline struct buffer_head *msdos_sread(int dev,int sector,void **start)
{
struct buffer_head *bh;
if (!(bh = bread(dev,sector >> 1, 1024)))
return NULL;
*start = bh->b_data+((sector & 1) << SECTOR_BITS);
struct buffer_head *bh = bread(dev,sector, 512);
if (bh != NULL){
*start = bh->b_data; /* From the time of 1024 bytes block */
}
return bh;
}
......
......@@ -52,6 +52,7 @@
#define TIOCGLCKTRMIOS 0x5456
#define TIOCSLCKTRMIOS 0x5457
#define TIOCSERGSTRUCT 0x5458 /* For debugging only */
#define TIOCSERGETLSR 0x5459 /* Get line status register */
/* Used for packet mode */
#define TIOCPKT_DATA 0
......@@ -221,6 +222,10 @@ struct termios {
#define TIOCM_CD TIOCM_CAR
#define TIOCM_RI TIOCM_RNG
/* ioctl (fd, TIOCSERGTLSR, &result) where result may be as below */
#define TIOCSER_TEMT 0x01 /* Transmitter physically empty */
/* tcflow() and TCXONC use these */
#define TCOOFF 0
#define TCOON 1
......
......@@ -93,11 +93,15 @@ extern void t128_setup(char *str, int *ints);
extern void pas16_setup(char *str, int *ints);
extern void generic_NCR5380_setup(char *str, int *intr);
extern void aha152x_setup(char *str, int *ints);
extern void aha274x_setup(char *str, int *ints);
extern void scsi_luns_setup(char *str, int *ints);
extern void sound_setup(char *str, int *ints);
#ifdef CONFIG_SBPCD
extern void sbpcd_setup(char *str, int *ints);
#endif CONFIG_SBPCD
#ifdef CONFIG_CDU31A
extern void cdu31a_setup(char *str, int *ints);
#endif CONFIG_SBPCD
void ramdisk_setup(char *str, int *ints);
#ifdef CONFIG_SYSVIPC
......@@ -208,6 +212,9 @@ struct {
#ifdef CONFIG_SCSI_AHA152X
{ "aha152x=", aha152x_setup},
#endif
#ifdef CONFIG_SCSI_AHA274X
{ "aha274x=", aha274x_setup},
#endif
#ifdef CONFIG_BLK_DEV_XD
{ "xd=", xd_setup },
#endif
......@@ -220,6 +227,9 @@ struct {
#ifdef CONFIG_SBPCD
{ "sbpcd=", sbpcd_setup },
#endif CONFIG_SBPCD
#ifdef CONFIG_CDU31A
{ "cdu31a=", cdu31a_setup },
#endif CONFIG_CDU31A
{ 0, 0 }
};
......
......@@ -27,6 +27,7 @@
#include <linux/tqueue.h>
#include <linux/tty.h>
#include <linux/serial.h>
#include <linux/locks.h>
#ifdef CONFIG_INET
#include <linux/net.h>
#include <linux/netdevice.h>
......@@ -44,6 +45,8 @@ extern char * ftape_big_buffer;
extern void (*do_floppy)(void);
#endif
extern int sys_tz;
extern int ___strtok;
extern int request_dma(unsigned int dmanr, char * deviceID);
extern void free_dma(unsigned int dmanr);
......@@ -201,7 +204,29 @@ struct symbol_table symbol_table = { 0, 0, 0, /* for stacked module support */
X(dev_queue_xmit),
X(dev_base),
#endif
/* Added to make file system as module */
X(set_writetime),
X(getblk),
X(inode_setattr),
X(sys_tz),
X(inode_change_ok),
X(__wait_on_super),
X(file_fsync),
X(simple_strtoul),
X(generic_mmap),
X(set_blocksize),
X(clear_inode),
X(refile_buffer),
X(___strtok),
X(brelse),
X(bread),
X(init_fifo),
X(super_blocks),
X(chrdev_inode_operations),
X(blkdev_inode_operations),
X(ll_rw_block),
X(__wait_on_buffer),
X(read_ahead),
/********************************************************
* Do not add anything below this line,
* as the stacked modules depend on this!
......
......@@ -31,6 +31,8 @@
#define SWP_OFFSET(entry) ((entry) >> PAGE_SHIFT)
#define SWP_ENTRY(type,offset) (((type) << 1) | ((offset) << PAGE_SHIFT))
static int min_free_pages = 20;
static int nr_swapfiles = 0;
static struct wait_queue * lock_queue = NULL;
......@@ -626,7 +628,7 @@ unsigned long __get_free_pages(int priority, unsigned long order)
save_flags(flags);
repeat:
cli();
if ((priority==GFP_ATOMIC) || nr_free_pages > MAX_SECONDARY_PAGES) {
if ((priority==GFP_ATOMIC) || nr_free_pages > min_free_pages) {
RMQUEUE(order);
restore_flags(flags);
return 0;
......@@ -958,6 +960,14 @@ unsigned long free_area_init(unsigned long start_mem, unsigned long end_mem)
unsigned long mask = PAGE_MASK;
int i;
/*
* select nr of pages we try to keep free for important stuff
* with a minimum of 16 pages. This is totally arbitrary
*/
i = end_mem >> (PAGE_SHIFT+6);
if (i < 16)
i = 16;
min_free_pages = i;
start_mem = init_swap_cache(start_mem, end_mem);
mem_map = (unsigned short *) start_mem;
p = mem_map + MAP_NR(end_mem);
......
......@@ -1288,14 +1288,14 @@ struct sock *get_sock(struct proto *prot, unsigned short num,
continue;
if(s->dead && (s->state == TCP_CLOSE))
continue;
if(ip_addr_match(s->saddr,laddr) == 0)
continue;
if(prot == &udp_prot)
return s;
if(ip_addr_match(s->daddr,raddr)==0)
continue;
if (s->dummy_th.dest != rnum && s->dummy_th.dest != 0)
continue;
if(ip_addr_match(s->saddr,laddr) == 0)
continue;
return(s);
}
return(NULL);
......
......@@ -543,8 +543,9 @@ struct rtable * ip_rt_route(unsigned long daddr, struct options *opt, unsigned l
* broadcast addresses can be special cases..
*/
if ((rt->rt_dev->flags & IFF_BROADCAST) &&
rt->rt_dev->pa_brdaddr == daddr)
if ((rt->rt_gateway == 0) &&
(rt->rt_dev->flags & IFF_BROADCAST) &&
(rt->rt_dev->pa_brdaddr == daddr))
break;
}
......
......@@ -549,7 +549,7 @@ static int sock_fasync(struct inode *inode, struct file *filp, int on)
int sock_wake_async(struct socket *sock)
{
if(sock->fasync_list==NULL)
if (!sock || !sock->fasync_list)
return -1;
kill_fasync(sock->fasync_list, SIGIO);
return 0;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment