2005-04-17 02:20:36 +04:00
/*
2008-02-02 01:09:33 +03:00
* Copyright ( C ) 2000 - 2002 Michael Cornwell < cornwell @ acm . org >
* Copyright ( C ) 2000 - 2002 Andre Hedrick < andre @ linux - ide . org >
* Copyright ( C ) 2001 - 2002 Klaus Smolin
2005-04-17 02:20:36 +04:00
* IBM Storage Technology Division
2008-02-02 01:09:33 +03:00
* Copyright ( C ) 2003 - 2004 , 2007 Bartlomiej Zolnierkiewicz
2005-04-17 02:20:36 +04:00
*
* The big the bad and the ugly .
*/
# include <linux/types.h>
# include <linux/string.h>
# include <linux/kernel.h>
2006-02-16 02:17:37 +03:00
# include <linux/sched.h>
2005-04-17 02:20:36 +04:00
# include <linux/interrupt.h>
# include <linux/errno.h>
# include <linux/slab.h>
# include <linux/delay.h>
# include <linux/hdreg.h>
# include <linux/ide.h>
2007-07-25 10:13:56 +04:00
# include <linux/scatterlist.h>
2005-04-17 02:20:36 +04:00
# include <asm/uaccess.h>
# include <asm/io.h>
2009-04-08 16:13:03 +04:00
void ide_tf_readback ( ide_drive_t * drive , struct ide_cmd * cmd )
{
ide_hwif_t * hwif = drive - > hwif ;
const struct ide_tp_ops * tp_ops = hwif - > tp_ops ;
/* Be sure we're looking at the low order bytes */
tp_ops - > write_devctl ( hwif , ATA_DEVCTL_OBS ) ;
tp_ops - > tf_read ( drive , & cmd - > tf , cmd - > valid . in . tf ) ;
if ( cmd - > tf_flags & IDE_TFLAG_LBA48 ) {
tp_ops - > write_devctl ( hwif , ATA_HOB | ATA_DEVCTL_OBS ) ;
tp_ops - > tf_read ( drive , & cmd - > hob , cmd - > valid . in . hob ) ;
}
}
2009-04-08 16:13:02 +04:00
void ide_tf_dump ( const char * s , struct ide_cmd * cmd )
2008-01-26 00:17:07 +03:00
{
2008-01-26 00:17:10 +03:00
# ifdef DEBUG
printk ( " %s: tf: feat 0x%02x nsect 0x%02x lbal 0x%02x "
" lbam 0x%02x lbah 0x%02x dev 0x%02x cmd 0x%02x \n " ,
2009-04-08 16:13:02 +04:00
s , cmd - > tf . feature , cmd - > tf . nsect ,
cmd - > tf . lbal , cmd - > tf . lbam , cmd - > tf . lbah ,
cmd - > tf . device , cmd - > tf . command ) ;
printk ( " %s: hob: nsect 0x%02x lbal 0x%02x lbam 0x%02x lbah 0x%02x \n " ,
s , cmd - > hob . nsect , cmd - > hob . lbal , cmd - > hob . lbam , cmd - > hob . lbah ) ;
2008-01-26 00:17:10 +03:00
# endif
2008-04-29 01:44:39 +04:00
}
2005-04-17 02:20:36 +04:00
int taskfile_lib_get_identify ( ide_drive_t * drive , u8 * buf )
{
2009-03-27 14:46:37 +03:00
struct ide_cmd cmd ;
2008-01-26 00:17:06 +03:00
2009-03-27 14:46:37 +03:00
memset ( & cmd , 0 , sizeof ( cmd ) ) ;
cmd . tf . nsect = 0x01 ;
2005-04-17 02:20:36 +04:00
if ( drive - > media = = ide_disk )
2009-03-27 14:46:37 +03:00
cmd . tf . command = ATA_CMD_ID_ATA ;
2005-04-17 02:20:36 +04:00
else
2009-03-27 14:46:37 +03:00
cmd . tf . command = ATA_CMD_ID_ATAPI ;
2009-04-08 16:13:01 +04:00
cmd . valid . out . tf = IDE_VALID_OUT_TF | IDE_VALID_DEVICE ;
cmd . valid . in . tf = IDE_VALID_IN_TF | IDE_VALID_DEVICE ;
2009-03-27 14:46:39 +03:00
cmd . protocol = ATA_PROT_PIO ;
2009-03-27 14:46:37 +03:00
return ide_raw_taskfile ( drive , & cmd , buf , 1 ) ;
2005-04-17 02:20:36 +04:00
}
2008-01-26 00:17:16 +03:00
static ide_startstop_t task_no_data_intr ( ide_drive_t * ) ;
2009-03-27 14:46:38 +03:00
static ide_startstop_t pre_task_out_intr ( ide_drive_t * , struct ide_cmd * ) ;
2009-03-27 14:46:39 +03:00
static ide_startstop_t task_pio_intr ( ide_drive_t * ) ;
2008-01-26 00:17:16 +03:00
2009-03-27 14:46:38 +03:00
ide_startstop_t do_rw_taskfile ( ide_drive_t * drive , struct ide_cmd * orig_cmd )
2005-04-17 02:20:36 +04:00
{
2009-01-06 19:20:52 +03:00
ide_hwif_t * hwif = drive - > hwif ;
2009-03-27 14:46:38 +03:00
struct ide_cmd * cmd = & hwif - > cmd ;
2009-03-27 14:46:37 +03:00
struct ide_taskfile * tf = & cmd - > tf ;
2008-01-26 00:17:16 +03:00
ide_handler_t * handler = NULL ;
2008-07-23 21:55:56 +04:00
const struct ide_tp_ops * tp_ops = hwif - > tp_ops ;
2008-04-27 00:25:24 +04:00
const struct ide_dma_ops * dma_ops = hwif - > dma_ops ;
2005-04-17 02:20:36 +04:00
2009-03-27 14:46:39 +03:00
if ( orig_cmd - > protocol = = ATA_PROT_PIO & &
( orig_cmd - > tf_flags & IDE_TFLAG_MULTI_PIO ) & &
drive - > mult_count = = 0 ) {
printk ( KERN_ERR " %s: multimode not set! \n " , drive - > name ) ;
return ide_stopped ;
2008-01-26 00:17:15 +03:00
}
2009-03-27 14:46:38 +03:00
if ( orig_cmd - > ftf_flags & IDE_FTFLAG_FLAGGED )
orig_cmd - > ftf_flags | = IDE_FTFLAG_SET_IN_FLAGS ;
2008-01-26 00:17:15 +03:00
2009-03-27 14:46:38 +03:00
memcpy ( cmd , orig_cmd , sizeof ( * cmd ) ) ;
2008-10-13 23:39:41 +04:00
2009-03-27 14:46:37 +03:00
if ( ( cmd - > tf_flags & IDE_TFLAG_DMA_PIO_FALLBACK ) = = 0 ) {
2009-04-08 16:13:02 +04:00
ide_tf_dump ( drive - > name , cmd ) ;
2009-03-31 22:15:30 +04:00
tp_ops - > write_devctl ( hwif , ATA_DEVCTL_OBS ) ;
2008-07-15 23:21:48 +04:00
SELECT_MASK ( drive , 0 ) ;
2009-03-31 22:15:31 +04:00
if ( cmd - > ftf_flags & IDE_FTFLAG_OUT_DATA ) {
2009-04-08 16:13:02 +04:00
u8 data [ 2 ] = { cmd - > tf . data , cmd - > hob . data } ;
2009-03-31 22:15:31 +04:00
tp_ops - > output_data ( drive , cmd , data , 2 ) ;
}
2009-04-08 16:13:02 +04:00
if ( cmd - > valid . out . tf & IDE_VALID_DEVICE ) {
u8 HIHI = ( cmd - > tf_flags & IDE_TFLAG_LBA48 ) ?
0xE0 : 0xEF ;
if ( ! ( cmd - > ftf_flags & IDE_FTFLAG_FLAGGED ) )
cmd - > tf . device & = HIHI ;
cmd - > tf . device | = drive - > select ;
}
2009-04-08 16:13:03 +04:00
tp_ops - > tf_load ( drive , & cmd - > hob , cmd - > valid . out . hob ) ;
tp_ops - > tf_load ( drive , & cmd - > tf , cmd - > valid . out . tf ) ;
2008-04-29 01:44:39 +04:00
}
2005-04-17 02:20:36 +04:00
2009-03-27 14:46:39 +03:00
switch ( cmd - > protocol ) {
case ATA_PROT_PIO :
if ( cmd - > tf_flags & IDE_TFLAG_WRITE ) {
tp_ops - > exec_command ( hwif , tf - > command ) ;
ndelay ( 400 ) ; /* FIXME */
return pre_task_out_intr ( drive , cmd ) ;
}
2009-03-27 14:46:39 +03:00
handler = task_pio_intr ;
2008-01-26 00:17:16 +03:00
/* fall-through */
2009-03-27 14:46:39 +03:00
case ATA_PROT_NODATA :
2008-01-26 00:17:16 +03:00
if ( handler = = NULL )
handler = task_no_data_intr ;
2009-03-27 14:46:47 +03:00
ide_execute_command ( drive , cmd , handler , WAIT_WORSTCASE ) ;
2005-04-17 02:20:36 +04:00
return ide_started ;
2009-03-27 14:46:46 +03:00
case ATA_PROT_DMA :
2009-03-31 22:15:20 +04:00
if ( ide_dma_prepare ( drive , cmd ) )
2008-01-26 00:17:16 +03:00
return ide_stopped ;
2009-03-27 14:46:47 +03:00
hwif - > expiry = dma_ops - > dma_timer_expiry ;
2009-03-27 14:46:47 +03:00
ide_execute_command ( drive , cmd , ide_dma_intr , 2 * WAIT_CMD ) ;
2008-04-27 00:25:24 +04:00
dma_ops - > dma_start ( drive ) ;
2009-03-27 14:46:46 +03:00
default :
2008-01-26 00:17:07 +03:00
return ide_started ;
2005-04-17 02:20:36 +04:00
}
}
2008-01-26 00:17:16 +03:00
EXPORT_SYMBOL_GPL ( do_rw_taskfile ) ;
2005-04-17 02:20:36 +04:00
2008-10-13 23:39:41 +04:00
static ide_startstop_t task_no_data_intr ( ide_drive_t * drive )
2005-04-17 02:20:36 +04:00
{
2008-07-23 21:55:52 +04:00
ide_hwif_t * hwif = drive - > hwif ;
2009-03-27 14:46:37 +03:00
struct ide_cmd * cmd = & hwif - > cmd ;
struct ide_taskfile * tf = & cmd - > tf ;
int custom = ( cmd - > tf_flags & IDE_TFLAG_CUSTOM_HANDLER ) ? 1 : 0 ;
2008-10-13 23:39:41 +04:00
int retries = ( custom & & tf - > command = = ATA_CMD_INIT_DEV_PARAMS ) ? 5 : 1 ;
2005-04-17 02:20:36 +04:00
u8 stat ;
2008-07-25 00:53:36 +04:00
local_irq_enable_in_hardirq ( ) ;
2008-07-23 21:55:56 +04:00
while ( 1 ) {
stat = hwif - > tp_ops - > read_status ( hwif ) ;
2008-10-11 00:39:21 +04:00
if ( ( stat & ATA_BUSY ) = = 0 | | retries - - = = 0 )
2008-07-23 21:55:56 +04:00
break ;
2005-04-17 02:20:36 +04:00
udelay ( 10 ) ;
2008-07-23 21:55:56 +04:00
} ;
2005-04-17 02:20:36 +04:00
2008-10-13 23:39:41 +04:00
if ( ! OK_STAT ( stat , ATA_DRDY , BAD_STAT ) ) {
if ( custom & & tf - > command = = ATA_CMD_SET_MULTI ) {
drive - > mult_req = drive - > mult_count = 0 ;
drive - > special . b . recalibrate = 1 ;
( void ) ide_dump_status ( drive , __func__ , stat ) ;
return ide_stopped ;
} else if ( custom & & tf - > command = = ATA_CMD_INIT_DEV_PARAMS ) {
if ( ( stat & ( ATA_ERR | ATA_DRQ ) ) = = 0 ) {
ide_set_handler ( drive , & task_no_data_intr ,
2009-03-27 14:46:46 +03:00
WAIT_WORSTCASE ) ;
2008-10-13 23:39:41 +04:00
return ide_started ;
}
}
2005-04-17 02:20:36 +04:00
return ide_error ( drive , " task_no_data_intr " , stat ) ;
2008-10-13 23:39:41 +04:00
}
2008-02-06 04:57:51 +03:00
2009-03-27 14:46:41 +03:00
if ( custom & & tf - > command = = ATA_CMD_SET_MULTI )
2008-10-13 23:39:41 +04:00
drive - > mult_count = drive - > mult_req ;
2005-04-17 02:20:36 +04:00
2009-03-27 14:46:42 +03:00
if ( custom = = 0 | | tf - > command = = ATA_CMD_IDLEIMMEDIATE | |
tf - > command = = ATA_CMD_CHK_POWER ) {
2009-03-27 14:46:31 +03:00
struct request * rq = hwif - > rq ;
if ( blk_pm_request ( rq ) )
ide_complete_pm_rq ( drive , rq ) ;
2009-03-27 14:46:42 +03:00
else
ide_finish_cmd ( drive , cmd , stat ) ;
2009-03-27 14:46:31 +03:00
}
2005-04-17 02:20:36 +04:00
return ide_stopped ;
}
2008-02-02 01:09:16 +03:00
static u8 wait_drive_not_busy ( ide_drive_t * drive )
2005-04-17 02:20:36 +04:00
{
2008-07-23 21:55:52 +04:00
ide_hwif_t * hwif = drive - > hwif ;
2007-07-04 00:28:34 +04:00
int retries ;
2005-04-17 02:20:36 +04:00
u8 stat ;
/*
2008-06-20 22:53:33 +04:00
* Last sector was transfered , wait until device is ready . This can
* take up to 6 ms on some ATAPI devices , so we will wait max 10 ms .
2005-04-17 02:20:36 +04:00
*/
2008-06-20 22:53:33 +04:00
for ( retries = 0 ; retries < 1000 ; retries + + ) {
2008-07-23 21:55:56 +04:00
stat = hwif - > tp_ops - > read_status ( hwif ) ;
2008-02-06 04:57:51 +03:00
2008-10-11 00:39:21 +04:00
if ( stat & ATA_BUSY )
2007-07-04 00:28:34 +04:00
udelay ( 10 ) ;
else
break ;
}
2005-04-17 02:20:36 +04:00
2008-10-11 00:39:21 +04:00
if ( stat & ATA_BUSY )
2005-04-17 02:20:36 +04:00
printk ( KERN_ERR " %s: drive still BUSY! \n " , drive - > name ) ;
return stat ;
}
2009-03-31 22:15:13 +04:00
void ide_pio_bytes ( ide_drive_t * drive , struct ide_cmd * cmd ,
unsigned int write , unsigned int len )
2005-04-17 02:20:36 +04:00
{
ide_hwif_t * hwif = drive - > hwif ;
struct scatterlist * sg = hwif - > sg_table ;
2009-03-27 14:46:38 +03:00
struct scatterlist * cursg = cmd - > cursg ;
2005-04-17 02:20:36 +04:00
struct page * page ;
unsigned long flags ;
unsigned int offset ;
u8 * buf ;
2009-03-27 14:46:38 +03:00
cursg = cmd - > cursg ;
2009-03-31 22:14:59 +04:00
if ( cursg = = NULL )
cursg = cmd - > cursg = sg ;
while ( len ) {
unsigned nr_bytes = min ( len , cursg - > length - cmd - > cursg_ofs ) ;
2007-07-25 10:13:56 +04:00
2009-03-31 22:14:59 +04:00
if ( nr_bytes > PAGE_SIZE )
nr_bytes = PAGE_SIZE ;
2005-04-17 02:20:36 +04:00
2009-03-31 22:14:59 +04:00
page = sg_page ( cursg ) ;
offset = cursg - > offset + cmd - > cursg_ofs ;
/* get the current page and offset */
page = nth_page ( page , ( offset > > PAGE_SHIFT ) ) ;
offset % = PAGE_SIZE ;
2005-04-17 02:20:36 +04:00
2009-03-31 22:14:59 +04:00
if ( PageHighMem ( page ) )
local_irq_save ( flags ) ;
2009-03-31 22:14:59 +04:00
buf = kmap_atomic ( page , KM_BIO_SRC_IRQ ) + offset ;
2005-04-17 02:20:36 +04:00
2009-03-31 22:14:59 +04:00
cmd - > nleft - = nr_bytes ;
cmd - > cursg_ofs + = nr_bytes ;
2005-04-17 02:20:36 +04:00
2009-03-31 22:14:59 +04:00
if ( cmd - > cursg_ofs = = cursg - > length ) {
cursg = cmd - > cursg = sg_next ( cmd - > cursg ) ;
cmd - > cursg_ofs = 0 ;
}
2005-04-17 02:20:36 +04:00
2009-03-31 22:14:59 +04:00
/* do the actual data transfer */
if ( write )
hwif - > tp_ops - > output_data ( drive , cmd , buf , nr_bytes ) ;
else
hwif - > tp_ops - > input_data ( drive , cmd , buf , nr_bytes ) ;
2005-04-17 02:20:36 +04:00
2009-03-31 22:14:59 +04:00
kunmap_atomic ( buf , KM_BIO_SRC_IRQ ) ;
2009-03-31 22:14:59 +04:00
if ( PageHighMem ( page ) )
local_irq_restore ( flags ) ;
2005-04-17 02:20:36 +04:00
2009-03-31 22:14:59 +04:00
len - = nr_bytes ;
}
2005-04-17 02:20:36 +04:00
}
2009-03-31 22:15:13 +04:00
EXPORT_SYMBOL_GPL ( ide_pio_bytes ) ;
2005-04-17 02:20:36 +04:00
2009-03-27 14:46:38 +03:00
static void ide_pio_datablock ( ide_drive_t * drive , struct ide_cmd * cmd ,
unsigned int write )
2005-04-17 02:20:36 +04:00
{
2009-03-31 22:14:59 +04:00
unsigned int nr_bytes ;
2008-01-26 22:13:10 +03:00
u8 saved_io_32bit = drive - > io_32bit ;
2009-03-27 14:46:38 +03:00
if ( cmd - > tf_flags & IDE_TFLAG_FS )
cmd - > rq - > errors = 0 ;
2005-04-17 02:20:36 +04:00
2009-03-27 14:46:37 +03:00
if ( cmd - > tf_flags & IDE_TFLAG_IO_16BIT )
2009-03-27 14:46:32 +03:00
drive - > io_32bit = 0 ;
2008-01-26 22:13:10 +03:00
2006-02-16 02:17:37 +03:00
touch_softlockup_watchdog ( ) ;
2009-03-27 14:46:39 +03:00
if ( cmd - > tf_flags & IDE_TFLAG_MULTI_PIO )
2009-03-31 22:14:59 +04:00
nr_bytes = min_t ( unsigned , cmd - > nleft , drive - > mult_count < < 9 ) ;
2009-03-27 14:46:39 +03:00
else
2009-03-31 22:14:59 +04:00
nr_bytes = SECTOR_SIZE ;
ide_pio_bytes ( drive , cmd , write , nr_bytes ) ;
2008-01-26 22:13:10 +03:00
drive - > io_32bit = saved_io_32bit ;
2005-04-17 02:20:36 +04:00
}
2009-03-27 14:46:41 +03:00
static void ide_error_cmd ( ide_drive_t * drive , struct ide_cmd * cmd )
2005-04-17 02:20:36 +04:00
{
2009-03-27 14:46:38 +03:00
if ( cmd - > tf_flags & IDE_TFLAG_FS ) {
2009-03-27 14:46:47 +03:00
int nr_bytes = cmd - > nbytes - cmd - > nleft ;
2005-04-17 02:20:36 +04:00
2009-03-27 14:46:39 +03:00
if ( cmd - > protocol = = ATA_PROT_PIO & &
( ( cmd - > tf_flags & IDE_TFLAG_WRITE ) | | cmd - > nleft = = 0 ) ) {
if ( cmd - > tf_flags & IDE_TFLAG_MULTI_PIO )
2009-03-27 14:46:47 +03:00
nr_bytes - = drive - > mult_count < < 9 ;
2009-03-27 14:46:39 +03:00
else
2009-03-27 14:46:47 +03:00
nr_bytes - = SECTOR_SIZE ;
2005-04-17 02:20:36 +04:00
}
2009-03-27 14:46:47 +03:00
if ( nr_bytes > 0 )
ide_complete_rq ( drive , 0 , nr_bytes ) ;
2005-04-17 02:20:36 +04:00
}
}
2009-03-27 14:46:38 +03:00
void ide_finish_cmd ( ide_drive_t * drive , struct ide_cmd * cmd , u8 stat )
2005-04-17 02:20:36 +04:00
{
2009-03-27 14:46:43 +03:00
struct request * rq = drive - > hwif - > rq ;
2009-03-27 14:46:42 +03:00
u8 err = ide_read_error ( drive ) ;
2005-04-17 02:20:36 +04:00
2009-03-27 14:46:42 +03:00
ide_complete_cmd ( drive , cmd , stat , err ) ;
2009-03-27 14:46:43 +03:00
rq - > errors = err ;
2009-03-27 14:46:44 +03:00
ide_complete_rq ( drive , err ? - EIO : 0 , blk_rq_bytes ( rq ) ) ;
2005-04-17 02:20:36 +04:00
}
/*
2009-03-27 14:46:39 +03:00
* Handler for command with PIO data phase .
2005-04-17 02:20:36 +04:00
*/
2009-03-27 14:46:39 +03:00
static ide_startstop_t task_pio_intr ( ide_drive_t * drive )
2005-04-17 02:20:36 +04:00
{
ide_hwif_t * hwif = drive - > hwif ;
2009-03-27 14:46:38 +03:00
struct ide_cmd * cmd = & drive - > hwif - > cmd ;
2008-07-23 21:55:56 +04:00
u8 stat = hwif - > tp_ops - > read_status ( hwif ) ;
2009-03-27 14:46:39 +03:00
u8 write = ! ! ( cmd - > tf_flags & IDE_TFLAG_WRITE ) ;
2005-04-17 02:20:36 +04:00
2009-03-27 14:46:39 +03:00
if ( write = = 0 ) {
/* Error? */
if ( stat & ATA_ERR )
2009-03-27 14:46:41 +03:00
goto out_err ;
2005-04-17 02:20:36 +04:00
2009-03-27 14:46:39 +03:00
/* Didn't want any data? Odd. */
2009-03-27 14:46:39 +03:00
if ( ( stat & ATA_DRQ ) = = 0 ) {
/* Command all done? */
2009-03-27 14:46:41 +03:00
if ( OK_STAT ( stat , ATA_DRDY , ATA_BUSY ) )
goto out_end ;
2009-03-27 14:46:39 +03:00
/* Assume it was a spurious irq */
2009-03-27 14:46:41 +03:00
goto out_wait ;
2009-03-27 14:46:39 +03:00
}
2009-03-27 14:46:39 +03:00
} else {
if ( ! OK_STAT ( stat , DRIVE_READY , drive - > bad_wstat ) )
2009-03-27 14:46:41 +03:00
goto out_err ;
2005-04-17 02:20:36 +04:00
2009-03-27 14:46:39 +03:00
/* Deal with unexpected ATA data phase. */
if ( ( ( stat & ATA_DRQ ) = = 0 ) ^ ( cmd - > nleft = = 0 ) )
2009-03-27 14:46:41 +03:00
goto out_err ;
2009-03-27 14:46:39 +03:00
}
2009-03-27 14:46:41 +03:00
if ( write & & cmd - > nleft = = 0 )
goto out_end ;
2005-04-17 02:20:36 +04:00
/* Still data left to transfer. */
2009-03-27 14:46:39 +03:00
ide_pio_datablock ( drive , cmd , write ) ;
2005-04-17 02:20:36 +04:00
2009-03-27 14:46:39 +03:00
/* Are we done? Check status and finish transfer. */
if ( write = = 0 & & cmd - > nleft = = 0 ) {
stat = wait_drive_not_busy ( drive ) ;
if ( ! OK_STAT ( stat , 0 , BAD_STAT ) )
2009-03-27 14:46:41 +03:00
goto out_err ;
2005-04-17 02:20:36 +04:00
2009-03-27 14:46:41 +03:00
goto out_end ;
}
out_wait :
2005-04-17 02:20:36 +04:00
/* Still data left to transfer. */
2009-03-27 14:46:46 +03:00
ide_set_handler ( drive , & task_pio_intr , WAIT_WORSTCASE ) ;
2005-04-17 02:20:36 +04:00
return ide_started ;
2009-03-27 14:46:41 +03:00
out_end :
2009-03-27 14:46:42 +03:00
if ( ( cmd - > tf_flags & IDE_TFLAG_FS ) = = 0 )
ide_finish_cmd ( drive , cmd , stat ) ;
else
2009-05-07 17:24:40 +04:00
ide_complete_rq ( drive , 0 , blk_rq_sectors ( cmd - > rq ) < < 9 ) ;
2009-03-27 14:46:41 +03:00
return ide_stopped ;
out_err :
2009-03-27 14:46:41 +03:00
ide_error_cmd ( drive , cmd ) ;
return ide_error ( drive , __func__ , stat ) ;
2005-04-17 02:20:36 +04:00
}
2009-03-27 14:46:38 +03:00
static ide_startstop_t pre_task_out_intr ( ide_drive_t * drive ,
struct ide_cmd * cmd )
2005-04-17 02:20:36 +04:00
{
ide_startstop_t startstop ;
2008-10-11 00:39:21 +04:00
if ( ide_wait_stat ( & startstop , drive , ATA_DRQ ,
2005-04-17 02:20:36 +04:00
drive - > bad_wstat , WAIT_DRQ ) ) {
printk ( KERN_ERR " %s: no DRQ after issuing %sWRITE%s \n " ,
2009-03-27 14:46:27 +03:00
drive - > name ,
2009-03-27 14:46:39 +03:00
( cmd - > tf_flags & IDE_TFLAG_MULTI_PIO ) ? " MULT " : " " ,
2008-10-13 23:39:36 +04:00
( drive - > dev_flags & IDE_DFLAG_LBA48 ) ? " _EXT " : " " ) ;
2005-04-17 02:20:36 +04:00
return startstop ;
}
2008-10-13 23:39:36 +04:00
if ( ( drive - > dev_flags & IDE_DFLAG_UNMASK ) = = 0 )
2005-04-17 02:20:36 +04:00
local_irq_disable ( ) ;
2009-03-27 14:46:46 +03:00
ide_set_handler ( drive , & task_pio_intr , WAIT_WORSTCASE ) ;
2009-03-27 14:46:39 +03:00
2009-03-27 14:46:38 +03:00
ide_pio_datablock ( drive , cmd , 1 ) ;
2005-04-17 02:20:36 +04:00
return ide_started ;
}
2009-03-27 14:46:37 +03:00
int ide_raw_taskfile ( ide_drive_t * drive , struct ide_cmd * cmd , u8 * buf ,
u16 nsect )
2005-04-17 02:20:36 +04:00
{
2008-07-15 23:21:43 +04:00
struct request * rq ;
int error ;
2005-04-17 02:20:36 +04:00
2008-07-15 23:21:43 +04:00
rq = blk_get_request ( drive - > queue , READ , __GFP_WAIT ) ;
rq - > cmd_type = REQ_TYPE_ATA_TASKFILE ;
2009-04-19 02:00:42 +04:00
if ( cmd - > tf_flags & IDE_TFLAG_WRITE )
rq - > cmd_flags | = REQ_RW ;
2005-04-17 02:20:36 +04:00
/*
* ( ks ) We transfer currently only whole sectors .
* This is suffient for now . But , it would be great ,
* if we would find a solution to transfer any size .
* To support special commands like READ LONG .
*/
2009-04-19 02:00:42 +04:00
if ( nsect ) {
error = blk_rq_map_kern ( drive - > queue , rq , buf ,
nsect * SECTOR_SIZE , __GFP_WAIT ) ;
if ( error )
goto put_req ;
}
2005-04-17 02:20:36 +04:00
2009-03-27 14:46:37 +03:00
rq - > special = cmd ;
cmd - > rq = rq ;
2005-04-17 02:20:36 +04:00
2008-07-15 23:21:43 +04:00
error = blk_execute_rq ( drive - > queue , NULL , rq , 0 ) ;
2009-04-19 02:00:42 +04:00
put_req :
blk_put_request ( rq ) ;
2008-07-15 23:21:43 +04:00
return error ;
2005-04-17 02:20:36 +04:00
}
EXPORT_SYMBOL ( ide_raw_taskfile ) ;
2009-03-27 14:46:37 +03:00
int ide_no_data_taskfile ( ide_drive_t * drive , struct ide_cmd * cmd )
2008-01-26 00:17:07 +03:00
{
2009-03-27 14:46:39 +03:00
cmd - > protocol = ATA_PROT_NODATA ;
2008-01-26 00:17:07 +03:00
2009-03-27 14:46:37 +03:00
return ide_raw_taskfile ( drive , cmd , NULL , 0 ) ;
2008-01-26 00:17:07 +03:00
}
EXPORT_SYMBOL_GPL ( ide_no_data_taskfile ) ;
2007-11-05 23:42:27 +03:00
# ifdef CONFIG_IDE_TASK_IOCTL
2009-03-27 14:46:37 +03:00
int ide_taskfile_ioctl ( ide_drive_t * drive , unsigned long arg )
2005-04-17 02:20:36 +04:00
{
ide_task_request_t * req_task ;
2009-03-27 14:46:37 +03:00
struct ide_cmd cmd ;
2005-04-17 02:20:36 +04:00
u8 * outbuf = NULL ;
u8 * inbuf = NULL ;
2008-01-26 00:17:14 +03:00
u8 * data_buf = NULL ;
2005-04-17 02:20:36 +04:00
int err = 0 ;
int tasksize = sizeof ( struct ide_task_request_s ) ;
2006-10-16 19:31:02 +04:00
unsigned int taskin = 0 ;
unsigned int taskout = 0 ;
2008-01-26 00:17:14 +03:00
u16 nsect = 0 ;
2005-04-17 02:20:36 +04:00
char __user * buf = ( char __user * ) arg ;
// printk("IDE Taskfile ...\n");
2005-11-07 12:01:25 +03:00
req_task = kzalloc ( tasksize , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( req_task = = NULL ) return - ENOMEM ;
if ( copy_from_user ( req_task , buf , tasksize ) ) {
kfree ( req_task ) ;
return - EFAULT ;
}
2006-10-16 19:31:02 +04:00
taskout = req_task - > out_size ;
taskin = req_task - > in_size ;
if ( taskin > 65536 | | taskout > 65536 ) {
err = - EINVAL ;
goto abort ;
}
2005-04-17 02:20:36 +04:00
if ( taskout ) {
int outtotal = tasksize ;
2005-11-07 12:01:25 +03:00
outbuf = kzalloc ( taskout , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( outbuf = = NULL ) {
err = - ENOMEM ;
goto abort ;
}
if ( copy_from_user ( outbuf , buf + outtotal , taskout ) ) {
err = - EFAULT ;
goto abort ;
}
}
if ( taskin ) {
int intotal = tasksize + taskout ;
2005-11-07 12:01:25 +03:00
inbuf = kzalloc ( taskin , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( inbuf = = NULL ) {
err = - ENOMEM ;
goto abort ;
}
if ( copy_from_user ( inbuf , buf + intotal , taskin ) ) {
err = - EFAULT ;
goto abort ;
}
}
2009-03-27 14:46:37 +03:00
memset ( & cmd , 0 , sizeof ( cmd ) ) ;
2005-04-17 02:20:36 +04:00
2009-04-08 16:13:02 +04:00
memcpy ( & cmd . hob , req_task - > hob_ports , HDIO_DRIVE_HOB_HDR_SIZE - 2 ) ;
memcpy ( & cmd . tf , req_task - > io_ports , HDIO_DRIVE_TASK_HDR_SIZE ) ;
2008-01-26 00:17:14 +03:00
2009-04-08 16:13:01 +04:00
cmd . valid . out . tf = IDE_VALID_DEVICE ;
cmd . valid . in . tf = IDE_VALID_DEVICE | IDE_VALID_IN_TF ;
cmd . tf_flags = IDE_TFLAG_IO_16BIT ;
2005-04-17 02:20:36 +04:00
2009-04-08 16:13:01 +04:00
if ( drive - > dev_flags & IDE_DFLAG_LBA48 ) {
cmd . tf_flags | = IDE_TFLAG_LBA48 ;
cmd . valid . in . hob = IDE_VALID_IN_HOB ;
}
2008-01-26 00:17:10 +03:00
2008-01-26 00:17:07 +03:00
if ( req_task - > out_flags . all ) {
2009-03-27 14:46:37 +03:00
cmd . ftf_flags | = IDE_FTFLAG_FLAGGED ;
2008-01-26 00:17:07 +03:00
if ( req_task - > out_flags . b . data )
2009-03-27 14:46:37 +03:00
cmd . ftf_flags | = IDE_FTFLAG_OUT_DATA ;
2008-01-26 00:17:07 +03:00
if ( req_task - > out_flags . b . nsector_hob )
2009-04-08 16:13:01 +04:00
cmd . valid . out . hob | = IDE_VALID_NSECT ;
2008-01-26 00:17:07 +03:00
if ( req_task - > out_flags . b . sector_hob )
2009-04-08 16:13:01 +04:00
cmd . valid . out . hob | = IDE_VALID_LBAL ;
2008-01-26 00:17:07 +03:00
if ( req_task - > out_flags . b . lcyl_hob )
2009-04-08 16:13:01 +04:00
cmd . valid . out . hob | = IDE_VALID_LBAM ;
2008-01-26 00:17:07 +03:00
if ( req_task - > out_flags . b . hcyl_hob )
2009-04-08 16:13:01 +04:00
cmd . valid . out . hob | = IDE_VALID_LBAH ;
2008-01-26 00:17:07 +03:00
if ( req_task - > out_flags . b . error_feature )
2009-04-08 16:13:01 +04:00
cmd . valid . out . tf | = IDE_VALID_FEATURE ;
2008-01-26 00:17:07 +03:00
if ( req_task - > out_flags . b . nsector )
2009-04-08 16:13:01 +04:00
cmd . valid . out . tf | = IDE_VALID_NSECT ;
2008-01-26 00:17:07 +03:00
if ( req_task - > out_flags . b . sector )
2009-04-08 16:13:01 +04:00
cmd . valid . out . tf | = IDE_VALID_LBAL ;
2008-01-26 00:17:07 +03:00
if ( req_task - > out_flags . b . lcyl )
2009-04-08 16:13:01 +04:00
cmd . valid . out . tf | = IDE_VALID_LBAM ;
2008-01-26 00:17:07 +03:00
if ( req_task - > out_flags . b . hcyl )
2009-04-08 16:13:01 +04:00
cmd . valid . out . tf | = IDE_VALID_LBAH ;
2008-01-26 00:17:10 +03:00
} else {
2009-04-08 16:13:01 +04:00
cmd . valid . out . tf | = IDE_VALID_OUT_TF ;
2009-03-27 14:46:37 +03:00
if ( cmd . tf_flags & IDE_TFLAG_LBA48 )
2009-04-08 16:13:01 +04:00
cmd . valid . out . hob | = IDE_VALID_OUT_HOB ;
2008-01-26 00:17:07 +03:00
}
2008-01-26 00:17:14 +03:00
if ( req_task - > in_flags . b . data )
2009-03-27 14:46:37 +03:00
cmd . ftf_flags | = IDE_FTFLAG_IN_DATA ;
2008-01-26 00:17:14 +03:00
2009-03-27 14:46:38 +03:00
if ( req_task - > req_cmd = = IDE_DRIVE_TASK_RAW_WRITE ) {
/* fixup data phase if needed */
if ( req_task - > data_phase = = TASKFILE_IN_DMAQ | |
req_task - > data_phase = = TASKFILE_IN_DMA )
2009-03-27 14:46:39 +03:00
cmd . tf_flags | = IDE_TFLAG_WRITE ;
2009-03-27 14:46:38 +03:00
}
2009-03-27 14:46:39 +03:00
cmd . protocol = ATA_PROT_DMA ;
switch ( req_task - > data_phase ) {
2005-04-17 02:20:36 +04:00
case TASKFILE_MULTI_OUT :
if ( ! drive - > mult_count ) {
/* (hs): give up if multcount is not set */
printk ( KERN_ERR " %s: %s Multimode Write " \
" multcount is not set \n " ,
2008-04-27 00:25:20 +04:00
drive - > name , __func__ ) ;
2005-04-17 02:20:36 +04:00
err = - EPERM ;
goto abort ;
}
2009-03-27 14:46:39 +03:00
cmd . tf_flags | = IDE_TFLAG_MULTI_PIO ;
2005-04-17 02:20:36 +04:00
/* fall through */
case TASKFILE_OUT :
2009-03-27 14:46:39 +03:00
cmd . protocol = ATA_PROT_PIO ;
2008-01-26 00:17:14 +03:00
/* fall through */
case TASKFILE_OUT_DMAQ :
case TASKFILE_OUT_DMA :
2009-03-27 14:46:38 +03:00
cmd . tf_flags | = IDE_TFLAG_WRITE ;
2008-01-26 00:17:14 +03:00
nsect = taskout / SECTOR_SIZE ;
data_buf = outbuf ;
2005-04-17 02:20:36 +04:00
break ;
case TASKFILE_MULTI_IN :
if ( ! drive - > mult_count ) {
/* (hs): give up if multcount is not set */
printk ( KERN_ERR " %s: %s Multimode Read failure " \
" multcount is not set \n " ,
2008-04-27 00:25:20 +04:00
drive - > name , __func__ ) ;
2005-04-17 02:20:36 +04:00
err = - EPERM ;
goto abort ;
}
2009-03-27 14:46:39 +03:00
cmd . tf_flags | = IDE_TFLAG_MULTI_PIO ;
2005-04-17 02:20:36 +04:00
/* fall through */
case TASKFILE_IN :
2009-03-27 14:46:39 +03:00
cmd . protocol = ATA_PROT_PIO ;
2008-01-26 00:17:14 +03:00
/* fall through */
case TASKFILE_IN_DMAQ :
case TASKFILE_IN_DMA :
nsect = taskin / SECTOR_SIZE ;
data_buf = inbuf ;
2005-04-17 02:20:36 +04:00
break ;
case TASKFILE_NO_DATA :
2009-03-27 14:46:39 +03:00
cmd . protocol = ATA_PROT_NODATA ;
2005-04-17 02:20:36 +04:00
break ;
default :
err = - EFAULT ;
goto abort ;
}
2008-01-26 00:17:14 +03:00
if ( req_task - > req_cmd = = IDE_DRIVE_TASK_NO_DATA )
nsect = 0 ;
else if ( ! nsect ) {
2009-04-08 16:13:02 +04:00
nsect = ( cmd . hob . nsect < < 8 ) | cmd . tf . nsect ;
2008-01-26 00:17:14 +03:00
if ( ! nsect ) {
printk ( KERN_ERR " %s: in/out command without data \n " ,
drive - > name ) ;
err = - EFAULT ;
goto abort ;
}
}
2009-03-27 14:46:37 +03:00
err = ide_raw_taskfile ( drive , & cmd , data_buf , nsect ) ;
2008-01-26 00:17:14 +03:00
2009-04-08 16:13:02 +04:00
memcpy ( req_task - > hob_ports , & cmd . hob , HDIO_DRIVE_HOB_HDR_SIZE - 2 ) ;
memcpy ( req_task - > io_ports , & cmd . tf , HDIO_DRIVE_TASK_HDR_SIZE ) ;
2008-01-26 00:17:14 +03:00
2009-03-27 14:46:37 +03:00
if ( ( cmd . ftf_flags & IDE_FTFLAG_SET_IN_FLAGS ) & &
2008-01-26 00:17:14 +03:00
req_task - > in_flags . all = = 0 ) {
req_task - > in_flags . all = IDE_TASKFILE_STD_IN_FLAGS ;
2008-10-13 23:39:36 +04:00
if ( drive - > dev_flags & IDE_DFLAG_LBA48 )
2008-01-26 00:17:14 +03:00
req_task - > in_flags . all | = ( IDE_HOB_STD_IN_FLAGS < < 8 ) ;
}
2005-04-17 02:20:36 +04:00
if ( copy_to_user ( buf , req_task , tasksize ) ) {
err = - EFAULT ;
goto abort ;
}
if ( taskout ) {
int outtotal = tasksize ;
if ( copy_to_user ( buf + outtotal , outbuf , taskout ) ) {
err = - EFAULT ;
goto abort ;
}
}
if ( taskin ) {
int intotal = tasksize + taskout ;
if ( copy_to_user ( buf + intotal , inbuf , taskin ) ) {
err = - EFAULT ;
goto abort ;
}
}
abort :
kfree ( req_task ) ;
2005-11-07 12:01:32 +03:00
kfree ( outbuf ) ;
kfree ( inbuf ) ;
2005-04-17 02:20:36 +04:00
// printk("IDE Taskfile ioctl ended. rc = %i\n", err);
return err ;
}
2007-11-05 23:42:27 +03:00
# endif