Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
L
linux
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
linux
Commits
e75c5962
Commit
e75c5962
authored
Aug 16, 2002
by
Jens Axboe
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add in 2.4 ide-scsi
parent
7a0c753d
Changes
1
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
885 additions
and
0 deletions
+885
-0
drivers/scsi/ide-scsi.c
drivers/scsi/ide-scsi.c
+885
-0
No files found.
drivers/scsi/ide-scsi.c
0 → 100644
View file @
e75c5962
/*
* linux/drivers/scsi/ide-scsi.c Version 0.9 Jul 4, 1999
*
* Copyright (C) 1996 - 1999 Gadi Oxman <gadio@netvision.net.il>
*/
/*
* Emulation of a SCSI host adapter for IDE ATAPI devices.
*
* With this driver, one can use the Linux SCSI drivers instead of the
* native IDE ATAPI drivers.
*
* Ver 0.1 Dec 3 96 Initial version.
* Ver 0.2 Jan 26 97 Fixed bug in cleanup_module() and added emulation
* of MODE_SENSE_6/MODE_SELECT_6 for cdroms. Thanks
* to Janos Farkas for pointing this out.
* Avoid using bitfields in structures for m68k.
* Added Scatter/Gather and DMA support.
* Ver 0.4 Dec 7 97 Add support for ATAPI PD/CD drives.
* Use variable timeout for each command.
* Ver 0.5 Jan 2 98 Fix previous PD/CD support.
* Allow disabling of SCSI-6 to SCSI-10 transformation.
* Ver 0.6 Jan 27 98 Allow disabling of SCSI command translation layer
* for access through /dev/sg.
* Fix MODE_SENSE_6/MODE_SELECT_6/INQUIRY translation.
* Ver 0.7 Dec 04 98 Ignore commands where lun != 0 to avoid multiple
* detection of devices with CONFIG_SCSI_MULTI_LUN
* Ver 0.8 Feb 05 99 Optical media need translation too. Reverse 0.7.
* Ver 0.9 Jul 04 99 Fix a bug in SG_SET_TRANSFORM.
* Ver 0.91 Jun 10 02 Fix "off by one" error in transforms
*/
#define IDESCSI_VERSION "0.9"
#include <linux/module.h>
#include <linux/types.h>
#include <linux/string.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/ioport.h>
#include <linux/blkdev.h>
#include <linux/errno.h>
#include <linux/hdreg.h>
#include <linux/slab.h>
#include <linux/ide.h>
#include <asm/io.h>
#include <asm/bitops.h>
#include <asm/uaccess.h>
#include "scsi.h"
#include "hosts.h"
#include "sd.h"
#include <scsi/sg.h>
#define IDESCSI_DEBUG_LOG 0
typedef
struct
idescsi_pc_s
{
u8
c
[
12
];
/* Actual packet bytes */
int
request_transfer
;
/* Bytes to transfer */
int
actually_transferred
;
/* Bytes actually transferred */
int
buffer_size
;
/* Size of our data buffer */
struct
request
*
rq
;
/* The corresponding request */
byte
*
buffer
;
/* Data buffer */
byte
*
current_position
;
/* Pointer into the above buffer */
struct
scatterlist
*
sg
;
/* Scatter gather table */
int
b_count
;
/* Bytes transferred from current entry */
Scsi_Cmnd
*
scsi_cmd
;
/* SCSI command */
void
(
*
done
)(
Scsi_Cmnd
*
);
/* Scsi completion routine */
unsigned
long
flags
;
/* Status/Action flags */
unsigned
long
timeout
;
/* Command timeout */
}
idescsi_pc_t
;
/*
* Packet command status bits.
*/
#define PC_DMA_IN_PROGRESS 0
/* 1 while DMA in progress */
#define PC_WRITING 1
/* Data direction */
#define PC_TRANSFORM 2
/* transform SCSI commands */
/*
* SCSI command transformation layer
*/
#define IDESCSI_TRANSFORM 0
/* Enable/Disable transformation */
#define IDESCSI_SG_TRANSFORM 1
/* /dev/sg transformation */
/*
* Log flags
*/
#define IDESCSI_LOG_CMD 0
/* Log SCSI commands */
typedef
struct
{
ide_drive_t
*
drive
;
idescsi_pc_t
*
pc
;
/* Current packet command */
unsigned
long
flags
;
/* Status/Action flags */
unsigned
long
transform
;
/* SCSI cmd translation layer */
unsigned
long
log
;
/* log flags */
}
idescsi_scsi_t
;
/*
* Per ATAPI device status bits.
*/
#define IDESCSI_DRQ_INTERRUPT 0
/* DRQ interrupt device */
/*
* ide-scsi requests.
*/
#define IDESCSI_PC_RQ 90
/*
* Bits of the interrupt reason register.
*/
#define IDESCSI_IREASON_COD 0x1
/* Information transferred is command */
#define IDESCSI_IREASON_IO 0x2
/* The device requests us to read */
static
void
idescsi_discard_data
(
ide_drive_t
*
drive
,
unsigned
int
bcount
)
{
while
(
bcount
--
)
IN_BYTE
(
IDE_DATA_REG
);
}
static
void
idescsi_output_zeros
(
ide_drive_t
*
drive
,
unsigned
int
bcount
)
{
while
(
bcount
--
)
OUT_BYTE
(
0
,
IDE_DATA_REG
);
}
/*
* PIO data transfer routines using the scatter gather table.
*/
static
void
idescsi_input_buffers
(
ide_drive_t
*
drive
,
idescsi_pc_t
*
pc
,
unsigned
int
bcount
)
{
int
count
;
char
*
buf
;
while
(
bcount
)
{
if
(
pc
->
sg
-
(
struct
scatterlist
*
)
pc
->
scsi_cmd
->
request_buffer
>
pc
->
scsi_cmd
->
use_sg
)
{
printk
(
KERN_ERR
"ide-scsi: scatter gather table too small, discarding data
\n
"
);
idescsi_discard_data
(
drive
,
bcount
);
return
;
}
count
=
IDE_MIN
(
pc
->
sg
->
length
-
pc
->
b_count
,
bcount
);
buf
=
page_address
(
pc
->
sg
->
page
)
+
pc
->
sg
->
offset
;
atapi_input_bytes
(
drive
,
buf
+
pc
->
b_count
,
count
);
bcount
-=
count
;
pc
->
b_count
+=
count
;
if
(
pc
->
b_count
==
pc
->
sg
->
length
)
{
pc
->
sg
++
;
pc
->
b_count
=
0
;
}
}
}
static
void
idescsi_output_buffers
(
ide_drive_t
*
drive
,
idescsi_pc_t
*
pc
,
unsigned
int
bcount
)
{
int
count
;
char
*
buf
;
while
(
bcount
)
{
if
(
pc
->
sg
-
(
struct
scatterlist
*
)
pc
->
scsi_cmd
->
request_buffer
>
pc
->
scsi_cmd
->
use_sg
)
{
printk
(
KERN_ERR
"ide-scsi: scatter gather table too small, padding with zeros
\n
"
);
idescsi_output_zeros
(
drive
,
bcount
);
return
;
}
count
=
IDE_MIN
(
pc
->
sg
->
length
-
pc
->
b_count
,
bcount
);
buf
=
page_address
(
pc
->
sg
->
page
)
+
pc
->
sg
->
offset
;
atapi_output_bytes
(
drive
,
buf
+
pc
->
b_count
,
count
);
bcount
-=
count
;
pc
->
b_count
+=
count
;
if
(
pc
->
b_count
==
pc
->
sg
->
length
)
{
pc
->
sg
++
;
pc
->
b_count
=
0
;
}
}
}
/*
* Most of the SCSI commands are supported directly by ATAPI devices.
* idescsi_transform_pc handles the few exceptions.
*/
static
inline
void
idescsi_transform_pc1
(
ide_drive_t
*
drive
,
idescsi_pc_t
*
pc
)
{
u8
*
c
=
pc
->
c
,
*
scsi_buf
=
pc
->
buffer
,
*
sc
=
pc
->
scsi_cmd
->
cmnd
;
char
*
atapi_buf
;
if
(
!
test_bit
(
PC_TRANSFORM
,
&
pc
->
flags
))
return
;
if
(
drive
->
media
==
ide_cdrom
||
drive
->
media
==
ide_optical
)
{
if
(
c
[
0
]
==
READ_6
||
c
[
0
]
==
WRITE_6
)
{
c
[
8
]
=
c
[
4
];
c
[
5
]
=
c
[
3
];
c
[
4
]
=
c
[
2
];
c
[
3
]
=
c
[
1
]
&
0x1f
;
c
[
2
]
=
0
;
c
[
1
]
&=
0xe0
;
c
[
0
]
+=
(
READ_10
-
READ_6
);
}
if
(
c
[
0
]
==
MODE_SENSE
||
c
[
0
]
==
MODE_SELECT
)
{
unsigned
short
new_len
;
if
(
!
scsi_buf
)
return
;
if
((
atapi_buf
=
kmalloc
(
pc
->
buffer_size
+
4
,
GFP_ATOMIC
))
==
NULL
)
return
;
memset
(
atapi_buf
,
0
,
pc
->
buffer_size
+
4
);
memset
(
c
,
0
,
12
);
c
[
0
]
=
sc
[
0
]
|
0x40
;
c
[
1
]
=
sc
[
1
];
c
[
2
]
=
sc
[
2
];
new_len
=
sc
[
4
]
+
4
;
c
[
8
]
=
new_len
;
c
[
9
]
=
sc
[
5
];
c
[
7
]
=
new_len
>>
8
;
if
(
c
[
0
]
==
MODE_SELECT_10
)
{
atapi_buf
[
1
]
=
scsi_buf
[
0
];
/* Mode data length */
atapi_buf
[
2
]
=
scsi_buf
[
1
];
/* Medium type */
atapi_buf
[
3
]
=
scsi_buf
[
2
];
/* Device specific parameter */
atapi_buf
[
7
]
=
scsi_buf
[
3
];
/* Block descriptor length */
memcpy
(
atapi_buf
+
8
,
scsi_buf
+
4
,
pc
->
buffer_size
-
4
);
}
pc
->
buffer
=
atapi_buf
;
pc
->
request_transfer
+=
4
;
pc
->
buffer_size
+=
4
;
}
}
}
static
inline
void
idescsi_transform_pc2
(
ide_drive_t
*
drive
,
idescsi_pc_t
*
pc
)
{
u8
*
atapi_buf
=
pc
->
buffer
;
u8
*
sc
=
pc
->
scsi_cmd
->
cmnd
;
u8
*
scsi_buf
=
pc
->
scsi_cmd
->
request_buffer
;
if
(
!
test_bit
(
PC_TRANSFORM
,
&
pc
->
flags
))
return
;
if
(
drive
->
media
==
ide_cdrom
||
drive
->
media
==
ide_optical
)
{
if
(
pc
->
c
[
0
]
==
MODE_SENSE_10
&&
sc
[
0
]
==
MODE_SENSE
)
{
scsi_buf
[
0
]
=
atapi_buf
[
1
];
/* Mode data length */
scsi_buf
[
1
]
=
atapi_buf
[
2
];
/* Medium type */
scsi_buf
[
2
]
=
atapi_buf
[
3
];
/* Device specific parameter */
scsi_buf
[
3
]
=
atapi_buf
[
7
];
/* Block descriptor length */
memcpy
(
scsi_buf
+
4
,
atapi_buf
+
8
,
pc
->
request_transfer
-
8
);
}
if
(
pc
->
c
[
0
]
==
INQUIRY
)
{
scsi_buf
[
2
]
|=
2
;
/* ansi_revision */
scsi_buf
[
3
]
=
(
scsi_buf
[
3
]
&
0xf0
)
|
2
;
/* response data format */
}
}
if
(
atapi_buf
&&
atapi_buf
!=
scsi_buf
)
kfree
(
atapi_buf
);
}
static
inline
void
idescsi_free_bio
(
struct
bio
*
bio
)
{
struct
bio
*
bhp
;
while
(
bio
)
{
bhp
=
bio
;
bio
=
bio
->
bi_next
;
bio_put
(
bhp
);
}
}
static
void
hexdump
(
u8
*
x
,
int
len
)
{
int
i
;
printk
(
"[ "
);
for
(
i
=
0
;
i
<
len
;
i
++
)
printk
(
"%x "
,
x
[
i
]);
printk
(
"]
\n
"
);
}
static
int
idescsi_end_request
(
ide_drive_t
*
drive
,
int
uptodate
)
{
idescsi_scsi_t
*
scsi
=
drive
->
driver_data
;
struct
request
*
rq
=
HWGROUP
(
drive
)
->
rq
;
idescsi_pc_t
*
pc
=
(
idescsi_pc_t
*
)
rq
->
special
;
int
log
=
test_bit
(
IDESCSI_LOG_CMD
,
&
scsi
->
log
);
struct
Scsi_Host
*
host
;
u8
*
scsi_buf
;
unsigned
long
flags
;
if
(
!
(
rq
->
flags
&
REQ_SPECIAL
))
{
ide_end_request
(
drive
,
uptodate
);
return
0
;
}
ide_end_drive_cmd
(
drive
,
0
,
0
);
if
(
rq
->
errors
>=
ERROR_MAX
)
{
pc
->
scsi_cmd
->
result
=
DID_ERROR
<<
16
;
if
(
log
)
printk
(
"ide-scsi: %s: I/O error for %lu
\n
"
,
drive
->
name
,
pc
->
scsi_cmd
->
serial_number
);
}
else
if
(
rq
->
errors
)
{
pc
->
scsi_cmd
->
result
=
(
CHECK_CONDITION
<<
1
)
|
(
DID_OK
<<
16
);
if
(
log
)
printk
(
"ide-scsi: %s: check condition for %lu
\n
"
,
drive
->
name
,
pc
->
scsi_cmd
->
serial_number
);
}
else
{
pc
->
scsi_cmd
->
result
=
DID_OK
<<
16
;
idescsi_transform_pc2
(
drive
,
pc
);
if
(
log
)
{
printk
(
"ide-scsi: %s: suc %lu"
,
drive
->
name
,
pc
->
scsi_cmd
->
serial_number
);
if
(
!
test_bit
(
PC_WRITING
,
&
pc
->
flags
)
&&
pc
->
actually_transferred
&&
pc
->
actually_transferred
<=
1024
&&
pc
->
buffer
)
{
printk
(
", rst = "
);
scsi_buf
=
pc
->
scsi_cmd
->
request_buffer
;
hexdump
(
scsi_buf
,
IDE_MIN
(
16
,
pc
->
scsi_cmd
->
request_bufflen
));
}
else
printk
(
"
\n
"
);
}
}
host
=
pc
->
scsi_cmd
->
host
;
spin_lock_irqsave
(
host
->
host_lock
,
flags
);
pc
->
done
(
pc
->
scsi_cmd
);
spin_unlock_irqrestore
(
host
->
host_lock
,
flags
);
idescsi_free_bio
(
rq
->
bio
);
kfree
(
pc
);
kfree
(
rq
);
scsi
->
pc
=
NULL
;
return
0
;
}
static
inline
unsigned
long
get_timeout
(
idescsi_pc_t
*
pc
)
{
return
IDE_MAX
(
WAIT_CMD
,
pc
->
timeout
-
jiffies
);
}
/*
* Our interrupt handler.
*/
static
ide_startstop_t
idescsi_pc_intr
(
ide_drive_t
*
drive
)
{
idescsi_scsi_t
*
scsi
=
drive
->
driver_data
;
byte
status
,
ireason
;
int
bcount
;
idescsi_pc_t
*
pc
=
scsi
->
pc
;
struct
request
*
rq
=
pc
->
rq
;
unsigned
int
temp
;
#if IDESCSI_DEBUG_LOG
printk
(
KERN_INFO
"ide-scsi: Reached idescsi_pc_intr interrupt handler
\n
"
);
#endif
/* IDESCSI_DEBUG_LOG */
if
(
test_and_clear_bit
(
PC_DMA_IN_PROGRESS
,
&
pc
->
flags
))
{
#if IDESCSI_DEBUG_LOG
printk
(
"ide-scsi: %s: DMA complete
\n
"
,
drive
->
name
);
#endif
/* IDESCSI_DEBUG_LOG */
pc
->
actually_transferred
=
pc
->
request_transfer
;
(
void
)
(
HWIF
(
drive
)
->
dmaproc
(
ide_dma_end
,
drive
));
}
status
=
GET_STAT
();
/* Clear the interrupt */
if
((
status
&
DRQ_STAT
)
==
0
)
{
/* No more interrupts */
if
(
test_bit
(
IDESCSI_LOG_CMD
,
&
scsi
->
log
))
printk
(
KERN_INFO
"Packet command completed, %d bytes transferred
\n
"
,
pc
->
actually_transferred
);
local_irq_enable
();
if
(
status
&
ERR_STAT
)
rq
->
errors
++
;
idescsi_end_request
(
drive
,
1
);
return
ide_stopped
;
}
bcount
=
IN_BYTE
(
IDE_BCOUNTH_REG
)
<<
8
|
IN_BYTE
(
IDE_BCOUNTL_REG
);
ireason
=
IN_BYTE
(
IDE_IREASON_REG
);
if
(
ireason
&
IDESCSI_IREASON_COD
)
{
printk
(
KERN_ERR
"ide-scsi: CoD != 0 in idescsi_pc_intr
\n
"
);
return
ide_do_reset
(
drive
);
}
if
(
ireason
&
IDESCSI_IREASON_IO
)
{
temp
=
pc
->
actually_transferred
+
bcount
;
if
(
temp
>
pc
->
request_transfer
)
{
if
(
temp
>
pc
->
buffer_size
)
{
printk
(
KERN_ERR
"ide-scsi: The scsi wants to send us more data than expected - discarding data
\n
"
);
temp
=
pc
->
buffer_size
-
pc
->
actually_transferred
;
if
(
temp
)
{
clear_bit
(
PC_WRITING
,
&
pc
->
flags
);
if
(
pc
->
sg
)
idescsi_input_buffers
(
drive
,
pc
,
temp
);
else
atapi_input_bytes
(
drive
,
pc
->
current_position
,
temp
);
printk
(
KERN_ERR
"ide-scsi: transferred %d of %d bytes
\n
"
,
temp
,
bcount
);
}
pc
->
actually_transferred
+=
temp
;
pc
->
current_position
+=
temp
;
idescsi_discard_data
(
drive
,
bcount
-
temp
);
ide_set_handler
(
drive
,
&
idescsi_pc_intr
,
get_timeout
(
pc
),
NULL
);
return
ide_started
;
}
#if IDESCSI_DEBUG_LOG
printk
(
KERN_NOTICE
"ide-scsi: The scsi wants to send us more data than expected - allowing transfer
\n
"
);
#endif
/* IDESCSI_DEBUG_LOG */
}
}
if
(
ireason
&
IDESCSI_IREASON_IO
)
{
clear_bit
(
PC_WRITING
,
&
pc
->
flags
);
if
(
pc
->
sg
)
idescsi_input_buffers
(
drive
,
pc
,
bcount
);
else
atapi_input_bytes
(
drive
,
pc
->
current_position
,
bcount
);
}
else
{
set_bit
(
PC_WRITING
,
&
pc
->
flags
);
if
(
pc
->
sg
)
idescsi_output_buffers
(
drive
,
pc
,
bcount
);
else
atapi_output_bytes
(
drive
,
pc
->
current_position
,
bcount
);
}
pc
->
actually_transferred
+=
bcount
;
/* Update the current position */
pc
->
current_position
+=
bcount
;
ide_set_handler
(
drive
,
&
idescsi_pc_intr
,
get_timeout
(
pc
),
NULL
);
/* And set the interrupt handler again */
return
ide_started
;
}
static
ide_startstop_t
idescsi_transfer_pc
(
ide_drive_t
*
drive
)
{
idescsi_scsi_t
*
scsi
=
drive
->
driver_data
;
idescsi_pc_t
*
pc
=
scsi
->
pc
;
byte
ireason
;
ide_startstop_t
startstop
;
if
(
ide_wait_stat
(
&
startstop
,
drive
,
DRQ_STAT
,
BUSY_STAT
,
WAIT_READY
))
{
printk
(
KERN_ERR
"ide-scsi: Strange, packet command initiated yet DRQ isn't asserted
\n
"
);
return
startstop
;
}
ireason
=
IN_BYTE
(
IDE_IREASON_REG
);
if
((
ireason
&
(
IDESCSI_IREASON_IO
|
IDESCSI_IREASON_COD
))
!=
IDESCSI_IREASON_COD
)
{
printk
(
KERN_ERR
"ide-scsi: (IO,CoD) != (0,1) while issuing a packet command
\n
"
);
return
ide_do_reset
(
drive
);
}
ide_set_handler
(
drive
,
&
idescsi_pc_intr
,
get_timeout
(
pc
),
NULL
);
/* Set the interrupt routine */
atapi_output_bytes
(
drive
,
scsi
->
pc
->
c
,
12
);
/* Send the actual packet */
return
ide_started
;
}
/*
* Issue a packet command
*/
static
ide_startstop_t
idescsi_issue_pc
(
ide_drive_t
*
drive
,
idescsi_pc_t
*
pc
)
{
idescsi_scsi_t
*
scsi
=
drive
->
driver_data
;
int
bcount
;
struct
request
*
rq
=
pc
->
rq
;
int
dma_ok
=
0
;
scsi
->
pc
=
pc
;
/* Set the current packet command */
pc
->
actually_transferred
=
0
;
/* We haven't transferred any data yet */
pc
->
current_position
=
pc
->
buffer
;
bcount
=
IDE_MIN
(
pc
->
request_transfer
,
63
*
1024
);
/* Request to transfer the entire buffer at once */
if
(
drive
->
using_dma
&&
rq
->
bio
)
dma_ok
=!
HWIF
(
drive
)
->
dmaproc
(
test_bit
(
PC_WRITING
,
&
pc
->
flags
)
?
ide_dma_write
:
ide_dma_read
,
drive
);
SELECT_DRIVE
(
HWIF
(
drive
),
drive
);
if
(
IDE_CONTROL_REG
)
OUT_BYTE
(
drive
->
ctl
,
IDE_CONTROL_REG
);
OUT_BYTE
(
dma_ok
,
IDE_FEATURE_REG
);
OUT_BYTE
(
bcount
>>
8
,
IDE_BCOUNTH_REG
);
OUT_BYTE
(
bcount
&
0xff
,
IDE_BCOUNTL_REG
);
if
(
dma_ok
)
{
set_bit
(
PC_DMA_IN_PROGRESS
,
&
pc
->
flags
);
(
void
)
(
HWIF
(
drive
)
->
dmaproc
(
ide_dma_begin
,
drive
));
}
if
(
test_bit
(
IDESCSI_DRQ_INTERRUPT
,
&
scsi
->
flags
))
{
ide_set_handler
(
drive
,
&
idescsi_transfer_pc
,
get_timeout
(
pc
),
NULL
);
OUT_BYTE
(
WIN_PACKETCMD
,
IDE_COMMAND_REG
);
/* Issue the packet command */
return
ide_started
;
}
else
{
OUT_BYTE
(
WIN_PACKETCMD
,
IDE_COMMAND_REG
);
return
idescsi_transfer_pc
(
drive
);
}
}
/*
* idescsi_do_request is our request handling function.
*/
static
ide_startstop_t
idescsi_do_request
(
ide_drive_t
*
drive
,
struct
request
*
rq
,
unsigned
long
block
)
{
#if IDESCSI_DEBUG_LOG
printk
(
KERN_INFO
"rq_status: %d, rq_dev: %u, cmd: %d, errors: %d
\n
"
,
rq
->
rq_status
,(
unsigned
int
)
rq
->
rq_dev
,
rq
->
cmd
,
rq
->
errors
);
printk
(
KERN_INFO
"sector: %ld, nr_sectors: %ld, current_nr_sectors: %ld
\n
"
,
rq
->
sector
,
rq
->
nr_sectors
,
rq
->
current_nr_sectors
);
#endif
/* IDESCSI_DEBUG_LOG */
if
(
rq
->
flags
&
REQ_SPECIAL
)
{
return
idescsi_issue_pc
(
drive
,
(
idescsi_pc_t
*
)
rq
->
special
);
}
blk_dump_rq_flags
(
rq
,
"ide-scsi: unsup command"
);
idescsi_end_request
(
drive
,
0
);
return
ide_stopped
;
}
static
int
idescsi_open
(
struct
inode
*
inode
,
struct
file
*
filp
,
ide_drive_t
*
drive
)
{
MOD_INC_USE_COUNT
;
return
0
;
}
static
void
idescsi_ide_release
(
struct
inode
*
inode
,
struct
file
*
filp
,
ide_drive_t
*
drive
)
{
MOD_DEC_USE_COUNT
;
}
static
ide_drive_t
*
idescsi_drives
[
MAX_HWIFS
*
MAX_DRIVES
];
static
int
idescsi_initialized
=
0
;
static
void
idescsi_add_settings
(
ide_drive_t
*
drive
)
{
idescsi_scsi_t
*
scsi
=
drive
->
driver_data
;
/*
* drive setting name read/write ioctl ioctl data type min max mul_factor div_factor data pointer set function
*/
ide_add_setting
(
drive
,
"bios_cyl"
,
SETTING_RW
,
-
1
,
-
1
,
TYPE_INT
,
0
,
1023
,
1
,
1
,
&
drive
->
bios_cyl
,
NULL
);
ide_add_setting
(
drive
,
"bios_head"
,
SETTING_RW
,
-
1
,
-
1
,
TYPE_BYTE
,
0
,
255
,
1
,
1
,
&
drive
->
bios_head
,
NULL
);
ide_add_setting
(
drive
,
"bios_sect"
,
SETTING_RW
,
-
1
,
-
1
,
TYPE_BYTE
,
0
,
63
,
1
,
1
,
&
drive
->
bios_sect
,
NULL
);
ide_add_setting
(
drive
,
"transform"
,
SETTING_RW
,
-
1
,
-
1
,
TYPE_INT
,
0
,
3
,
1
,
1
,
&
scsi
->
transform
,
NULL
);
ide_add_setting
(
drive
,
"log"
,
SETTING_RW
,
-
1
,
-
1
,
TYPE_INT
,
0
,
1
,
1
,
1
,
&
scsi
->
log
,
NULL
);
}
/*
* Driver initialization.
*/
static
void
idescsi_setup
(
ide_drive_t
*
drive
,
idescsi_scsi_t
*
scsi
,
int
id
)
{
DRIVER
(
drive
)
->
busy
++
;
idescsi_drives
[
id
]
=
drive
;
drive
->
driver_data
=
scsi
;
drive
->
ready_stat
=
0
;
memset
(
scsi
,
0
,
sizeof
(
idescsi_scsi_t
));
scsi
->
drive
=
drive
;
if
(
drive
->
id
&&
(
drive
->
id
->
config
&
0x0060
)
==
0x20
)
set_bit
(
IDESCSI_DRQ_INTERRUPT
,
&
scsi
->
flags
);
set_bit
(
IDESCSI_TRANSFORM
,
&
scsi
->
transform
);
clear_bit
(
IDESCSI_SG_TRANSFORM
,
&
scsi
->
transform
);
#if IDESCSI_DEBUG_LOG
set_bit
(
IDESCSI_LOG_CMD
,
&
scsi
->
log
);
#endif
/* IDESCSI_DEBUG_LOG */
idescsi_add_settings
(
drive
);
}
static
int
idescsi_cleanup
(
ide_drive_t
*
drive
)
{
idescsi_scsi_t
*
scsi
=
drive
->
driver_data
;
if
(
ide_unregister_subdriver
(
drive
))
return
1
;
drive
->
driver_data
=
NULL
;
kfree
(
scsi
);
return
0
;
}
/*
* IDE subdriver functions, registered with ide.c
*/
static
ide_driver_t
idescsi_driver
=
{
name:
"ide-scsi"
,
version:
IDESCSI_VERSION
,
media:
ide_scsi
,
busy:
0
,
supports_dma:
1
,
supports_dsc_overlap:
0
,
cleanup:
idescsi_cleanup
,
standby:
NULL
,
flushcache:
NULL
,
do_request:
idescsi_do_request
,
end_request:
idescsi_end_request
,
ioctl:
NULL
,
open:
idescsi_open
,
release:
idescsi_ide_release
,
media_change:
NULL
,
revalidate:
NULL
,
pre_reset:
NULL
,
capacity:
NULL
,
special:
NULL
,
proc:
NULL
,
};
int
idescsi_init
(
void
);
static
ide_module_t
idescsi_module
=
{
IDE_DRIVER_MODULE
,
idescsi_init
,
&
idescsi_driver
,
NULL
};
/*
* idescsi_init will register the driver for each scsi.
*/
int
idescsi_init
(
void
)
{
ide_drive_t
*
drive
;
idescsi_scsi_t
*
scsi
;
byte
media
[]
=
{
TYPE_DISK
,
TYPE_TAPE
,
TYPE_PROCESSOR
,
TYPE_WORM
,
TYPE_ROM
,
TYPE_SCANNER
,
TYPE_MOD
,
255
};
int
i
,
failed
,
id
;
if
(
idescsi_initialized
)
return
0
;
idescsi_initialized
=
1
;
for
(
i
=
0
;
i
<
MAX_HWIFS
*
MAX_DRIVES
;
i
++
)
idescsi_drives
[
i
]
=
NULL
;
MOD_INC_USE_COUNT
;
for
(
i
=
0
;
media
[
i
]
!=
255
;
i
++
)
{
failed
=
0
;
while
((
drive
=
ide_scan_devices
(
media
[
i
],
idescsi_driver
.
name
,
NULL
,
failed
++
))
!=
NULL
)
{
if
((
scsi
=
(
idescsi_scsi_t
*
)
kmalloc
(
sizeof
(
idescsi_scsi_t
),
GFP_KERNEL
))
==
NULL
)
{
printk
(
KERN_ERR
"ide-scsi: %s: Can't allocate a scsi structure
\n
"
,
drive
->
name
);
continue
;
}
if
(
ide_register_subdriver
(
drive
,
&
idescsi_driver
,
IDE_SUBDRIVER_VERSION
))
{
printk
(
KERN_ERR
"ide-scsi: %s: Failed to register the driver with ide.c
\n
"
,
drive
->
name
);
kfree
(
scsi
);
continue
;
}
for
(
id
=
0
;
id
<
MAX_HWIFS
*
MAX_DRIVES
&&
idescsi_drives
[
id
];
id
++
);
idescsi_setup
(
drive
,
scsi
,
id
);
failed
--
;
}
}
ide_register_module
(
&
idescsi_module
);
MOD_DEC_USE_COUNT
;
return
0
;
}
int
idescsi_detect
(
Scsi_Host_Template
*
host_template
)
{
struct
Scsi_Host
*
host
;
int
id
;
int
last_lun
=
0
;
host_template
->
proc_name
=
"ide-scsi"
;
host
=
scsi_register
(
host_template
,
0
);
if
(
host
==
NULL
)
return
0
;
for
(
id
=
0
;
id
<
MAX_HWIFS
*
MAX_DRIVES
&&
idescsi_drives
[
id
];
id
++
)
last_lun
=
IDE_MAX
(
last_lun
,
idescsi_drives
[
id
]
->
last_lun
);
host
->
max_id
=
id
;
host
->
max_lun
=
last_lun
+
1
;
host
->
can_queue
=
host
->
cmd_per_lun
*
id
;
return
1
;
}
int
idescsi_release
(
struct
Scsi_Host
*
host
)
{
ide_drive_t
*
drive
;
int
id
;
for
(
id
=
0
;
id
<
MAX_HWIFS
*
MAX_DRIVES
;
id
++
)
{
drive
=
idescsi_drives
[
id
];
if
(
drive
)
DRIVER
(
drive
)
->
busy
--
;
}
return
0
;
}
const
char
*
idescsi_info
(
struct
Scsi_Host
*
host
)
{
return
"SCSI host adapter emulation for IDE ATAPI devices"
;
}
int
idescsi_ioctl
(
Scsi_Device
*
dev
,
int
cmd
,
void
*
arg
)
{
ide_drive_t
*
drive
=
idescsi_drives
[
dev
->
id
];
idescsi_scsi_t
*
scsi
=
drive
->
driver_data
;
if
(
cmd
==
SG_SET_TRANSFORM
)
{
if
(
arg
)
set_bit
(
IDESCSI_SG_TRANSFORM
,
&
scsi
->
transform
);
else
clear_bit
(
IDESCSI_SG_TRANSFORM
,
&
scsi
->
transform
);
return
0
;
}
else
if
(
cmd
==
SG_GET_TRANSFORM
)
return
put_user
(
test_bit
(
IDESCSI_SG_TRANSFORM
,
&
scsi
->
transform
),
(
int
*
)
arg
);
return
-
EINVAL
;
}
static
inline
struct
bio
*
idescsi_kmalloc_bio
(
int
count
)
{
struct
bio
*
bh
,
*
bhp
,
*
first_bh
;
if
((
first_bh
=
bhp
=
bh
=
bio_alloc
(
GFP_ATOMIC
,
1
))
==
NULL
)
goto
abort
;
bio_init
(
bh
);
bh
->
bi_vcnt
=
1
;
while
(
--
count
)
{
if
((
bh
=
bio_alloc
(
GFP_ATOMIC
,
1
))
==
NULL
)
goto
abort
;
bio_init
(
bh
);
bh
->
bi_vcnt
=
1
;
bhp
->
bi_next
=
bh
;
bhp
=
bh
;
bh
->
bi_next
=
NULL
;
}
return
first_bh
;
abort:
idescsi_free_bio
(
first_bh
);
return
NULL
;
}
static
inline
int
idescsi_set_direction
(
idescsi_pc_t
*
pc
)
{
switch
(
pc
->
c
[
0
])
{
case
READ_6
:
case
READ_10
:
case
READ_12
:
clear_bit
(
PC_WRITING
,
&
pc
->
flags
);
return
0
;
case
WRITE_6
:
case
WRITE_10
:
case
WRITE_12
:
set_bit
(
PC_WRITING
,
&
pc
->
flags
);
return
0
;
default:
return
1
;
}
}
static
inline
struct
bio
*
idescsi_dma_bio
(
ide_drive_t
*
drive
,
idescsi_pc_t
*
pc
)
{
struct
bio
*
bh
=
NULL
,
*
first_bh
=
NULL
;
int
segments
=
pc
->
scsi_cmd
->
use_sg
;
struct
scatterlist
*
sg
=
pc
->
scsi_cmd
->
request_buffer
;
if
(
!
drive
->
using_dma
||
!
pc
->
request_transfer
||
pc
->
request_transfer
%
1024
)
return
NULL
;
if
(
idescsi_set_direction
(
pc
))
return
NULL
;
if
(
segments
)
{
if
((
first_bh
=
bh
=
idescsi_kmalloc_bio
(
segments
))
==
NULL
)
return
NULL
;
#if IDESCSI_DEBUG_LOG
printk
(
"ide-scsi: %s: building DMA table, %d segments, %dkB total
\n
"
,
drive
->
name
,
segments
,
pc
->
request_transfer
>>
10
);
#endif
/* IDESCSI_DEBUG_LOG */
while
(
segments
--
)
{
bh
->
bi_io_vec
[
0
].
bv_page
=
sg
->
page
;
bh
->
bi_io_vec
[
0
].
bv_len
=
sg
->
length
;
bh
->
bi_io_vec
[
0
].
bv_offset
=
sg
->
offset
;
bh
->
bi_size
=
sg
->
length
;
bh
=
bh
->
bi_next
;
sg
++
;
}
}
else
{
if
((
first_bh
=
bh
=
idescsi_kmalloc_bio
(
1
))
==
NULL
)
return
NULL
;
#if IDESCSI_DEBUG_LOG
printk
(
"ide-scsi: %s: building DMA table for a single buffer (%dkB)
\n
"
,
drive
->
name
,
pc
->
request_transfer
>>
10
);
#endif
/* IDESCSI_DEBUG_LOG */
bh
->
bi_io_vec
[
0
].
bv_page
=
virt_to_page
(
pc
->
scsi_cmd
->
request_buffer
);
bh
->
bi_io_vec
[
0
].
bv_len
=
pc
->
request_transfer
;
bh
->
bi_io_vec
[
0
].
bv_offset
=
(
unsigned
long
)
pc
->
scsi_cmd
->
request_buffer
&
~
PAGE_MASK
;
bh
->
bi_size
=
pc
->
request_transfer
;
}
return
first_bh
;
}
static
inline
int
should_transform
(
ide_drive_t
*
drive
,
Scsi_Cmnd
*
cmd
)
{
idescsi_scsi_t
*
scsi
=
drive
->
driver_data
;
if
(
major
(
cmd
->
request
->
rq_dev
)
==
SCSI_GENERIC_MAJOR
)
return
test_bit
(
IDESCSI_SG_TRANSFORM
,
&
scsi
->
transform
);
return
test_bit
(
IDESCSI_TRANSFORM
,
&
scsi
->
transform
);
}
int
idescsi_queue
(
Scsi_Cmnd
*
cmd
,
void
(
*
done
)(
Scsi_Cmnd
*
))
{
ide_drive_t
*
drive
=
idescsi_drives
[
cmd
->
target
];
idescsi_scsi_t
*
scsi
;
struct
request
*
rq
=
NULL
;
idescsi_pc_t
*
pc
=
NULL
;
if
(
!
drive
)
{
printk
(
KERN_ERR
"ide-scsi: drive id %d not present
\n
"
,
cmd
->
target
);
goto
abort
;
}
scsi
=
drive
->
driver_data
;
pc
=
kmalloc
(
sizeof
(
idescsi_pc_t
),
GFP_ATOMIC
);
rq
=
kmalloc
(
sizeof
(
struct
request
),
GFP_ATOMIC
);
if
(
rq
==
NULL
||
pc
==
NULL
)
{
printk
(
KERN_ERR
"ide-scsi: %s: out of memory
\n
"
,
drive
->
name
);
goto
abort
;
}
memset
(
pc
->
c
,
0
,
12
);
pc
->
flags
=
0
;
pc
->
rq
=
rq
;
memcpy
(
pc
->
c
,
cmd
->
cmnd
,
cmd
->
cmd_len
);
if
(
cmd
->
use_sg
)
{
pc
->
buffer
=
NULL
;
pc
->
sg
=
cmd
->
request_buffer
;
}
else
{
pc
->
buffer
=
cmd
->
request_buffer
;
pc
->
sg
=
NULL
;
}
pc
->
b_count
=
0
;
pc
->
request_transfer
=
pc
->
buffer_size
=
cmd
->
request_bufflen
;
pc
->
scsi_cmd
=
cmd
;
pc
->
done
=
done
;
pc
->
timeout
=
jiffies
+
cmd
->
timeout_per_command
;
if
(
should_transform
(
drive
,
cmd
))
set_bit
(
PC_TRANSFORM
,
&
pc
->
flags
);
idescsi_transform_pc1
(
drive
,
pc
);
if
(
test_bit
(
IDESCSI_LOG_CMD
,
&
scsi
->
log
))
{
printk
(
"ide-scsi: %s: que %lu, cmd = "
,
drive
->
name
,
cmd
->
serial_number
);
hexdump
(
cmd
->
cmnd
,
cmd
->
cmd_len
);
if
(
memcmp
(
pc
->
c
,
cmd
->
cmnd
,
cmd
->
cmd_len
))
{
printk
(
"ide-scsi: %s: que %lu, tsl = "
,
drive
->
name
,
cmd
->
serial_number
);
hexdump
(
pc
->
c
,
12
);
}
}
ide_init_drive_cmd
(
rq
);
rq
->
special
=
(
char
*
)
pc
;
rq
->
bio
=
idescsi_dma_bio
(
drive
,
pc
);
rq
->
flags
=
REQ_SPECIAL
;
spin_unlock_irq
(
cmd
->
host
->
host_lock
);
(
void
)
ide_do_drive_cmd
(
drive
,
rq
,
ide_end
);
spin_lock_irq
(
cmd
->
host
->
host_lock
);
return
0
;
abort:
if
(
pc
)
kfree
(
pc
);
if
(
rq
)
kfree
(
rq
);
cmd
->
result
=
DID_ERROR
<<
16
;
done
(
cmd
);
return
0
;
}
int
idescsi_abort
(
Scsi_Cmnd
*
cmd
)
{
return
SCSI_ABORT_SNOOZE
;
}
int
idescsi_reset
(
Scsi_Cmnd
*
cmd
,
unsigned
int
resetflags
)
{
return
SCSI_RESET_SUCCESS
;
}
static
int
idescsi_bios
(
Disk
*
disk
,
struct
block_device
*
dev
,
int
*
parm
)
{
ide_drive_t
*
drive
=
idescsi_drives
[
disk
->
device
->
id
];
if
(
drive
->
bios_cyl
&&
drive
->
bios_head
&&
drive
->
bios_sect
)
{
parm
[
0
]
=
drive
->
bios_head
;
parm
[
1
]
=
drive
->
bios_sect
;
parm
[
2
]
=
drive
->
bios_cyl
;
}
return
0
;
}
static
Scsi_Host_Template
idescsi_template
=
{
module:
THIS_MODULE
,
name:
"idescsi"
,
detect:
idescsi_detect
,
release:
idescsi_release
,
info:
idescsi_info
,
ioctl:
idescsi_ioctl
,
queuecommand:
idescsi_queue
,
abort:
idescsi_abort
,
reset:
idescsi_reset
,
bios_param:
idescsi_bios
,
can_queue:
10
,
this_id:
-
1
,
sg_tablesize:
256
,
cmd_per_lun:
5
,
use_clustering:
DISABLE_CLUSTERING
,
emulated:
1
,
};
static
int
__init
init_idescsi_module
(
void
)
{
idescsi_init
();
scsi_register_host
(
&
idescsi_template
);
return
0
;
}
static
void
__exit
exit_idescsi_module
(
void
)
{
ide_drive_t
*
drive
;
byte
media
[]
=
{
TYPE_DISK
,
TYPE_TAPE
,
TYPE_PROCESSOR
,
TYPE_WORM
,
TYPE_ROM
,
TYPE_SCANNER
,
TYPE_MOD
,
255
};
int
i
,
failed
;
scsi_unregister_host
(
&
idescsi_template
);
for
(
i
=
0
;
media
[
i
]
!=
255
;
i
++
)
{
failed
=
0
;
while
((
drive
=
ide_scan_devices
(
media
[
i
],
idescsi_driver
.
name
,
&
idescsi_driver
,
failed
))
!=
NULL
)
if
(
idescsi_cleanup
(
drive
))
{
printk
(
"%s: exit_idescsi_module() called while still busy
\n
"
,
drive
->
name
);
failed
++
;
}
}
ide_unregister_module
(
&
idescsi_module
);
}
module_init
(
init_idescsi_module
);
module_exit
(
exit_idescsi_module
);
MODULE_LICENSE
(
"GPL"
);
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment