aic94xx: aic94xx_tmf.c Implements the Task Management Funcions defined by SAS
PATCH 20/20 aic94xx: aic94xx_tmf.c Implements the Task Management Funcions defined by SAS
Signed-off-by Luben Tuikov <[email protected]>
drivers/scsi/aic94xx/aic94xx_tmf.c +634 -0
@@ -0,0 +1,634 @@ +/* +* Aic94xx Task Management Functions +* +* Copyright (C) 2005 Adaptec, Inc. All rights reserved. +* Copyright (C) 2005 Luben Tuikov <[email protected]> +* +* This file is licensed under GPLv2. +* +* This file is part of the aic94xx driver. +* +* The aic94xx driver 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; version 2 of the +* License. +* +* The aic94xx driver 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 the aic94xx driver; if not, write to the Free Software +* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +* +* $Id: //depot/aic94xx/aic94xx_tmf.c#34 $ +*/ + +#include <linux/spinlock.h> +#include <scsi/sas/sas_task.h> +#include "aic94xx.h" +#include "aic94xx_sas.h" +#include "aic94xx_hwi.h" + +/* ---------- Internal enqueue ---------- */ + +static int asd_enqueue_internal (struct asd_ascb *ascb, +void (*tasklet_complete)(struct asd_ascb *, +struct done_list_struct *), +void (*timed_out)(unsigned long )) +{ +int res; + + ascb->tasklet_complete = tasklet_complete; + ascb->uldd_timer =1 ; + + ascb->timer.data = (unsigned long ) ascb; + ascb->timer.function = timed_out; + ascb->timer.expires = jiffies +AIC94XX_SCB_TIMEOUT ; + +add_timer (&ascb->timer); + + res =asd_post_ascb_list (ascb->ha, ascb,1 ); +if (unlikely (res)) +del_timer (&ascb->timer); +return res; +} + +static inline void asd_timedout_common (unsigned long data) +{ +struct asd_ascb *ascb = (void *) data; +struct asd_seq_data *seq = &ascb->ha->seq; +unsigned long flags; + +spin_lock_irqsave (&seq->pend_q_lock, flags); + seq->pending--; +list_del_init (&ascb->list); +spin_unlock_irqrestore (&seq->pend_q_lock, flags); +} + +/* ---------- CLEAR NEXUS ---------- */ + +static void asd_clear_nexus_tasklet_complete (struct asd_ascb *ascb, +struct done_list_struct *dl) +{ +ASD_DPRINTK ("%s: here\n" , __FUNCTION__); +if (!del_timer (&ascb->timer)) { +ASD_DPRINTK ("%s: couldn't delete timer\n" , __FUNCTION__); +return ; + } +ASD_DPRINTK ("%s: opcode: 0x%x\n" , __FUNCTION__, dl->opcode); + ascb->uldd_task = (void *) (unsigned long ) dl->opcode; +complete (&ascb->completion); +} + +static void asd_clear_nexus_timedout (unsigned long data) +{ +struct asd_ascb *ascb = (void *) data; + +ASD_DPRINTK ("%s: here\n" , __FUNCTION__); +asd_timedout_common (data); + ascb->uldd_task = (void *)TMF_RESP_FUNC_FAILED ; +complete (&ascb->completion); +} + +#define CLEAR_NEXUS_PRE \ + ASD_DPRINTK("%s: PRE\n", __FUNCTION__); \ + res = 1; \ + ascb = asd_ascb_alloc_list(asd_ha, &res, GFP_KERNEL); \ + if (!ascb) \ + return -ENOMEM; \ + \ + scb = ascb->scb; \ + scb->header.opcode = CLEAR_NEXUS + +#define CLEAR_NEXUS_POST \ + ASD_DPRINTK("%s: POST\n", __FUNCTION__); \ + res = asd_enqueue_internal(ascb, asd_clear_nexus_tasklet_complete, \ + asd_clear_nexus_timedout); \ + if (res) \ + goto out_err; \ + ASD_DPRINTK("%s: clear nexus posted, waiting...\n", __FUNCTION__); \ + wait_for_completion(&ascb->completion); \ + res = (int) (unsigned long) ascb->uldd_task; \ + if (res == TC_NO_ERROR) \ + res = TMF_RESP_FUNC_COMPLETE; \ +out_err: \ + asd_ascb_free(ascb); \ + return res + +int asd_clear_nexus_ha (struct sas_ha_struct *sas_ha) +{ +struct asd_ha_struct *asd_ha = sas_ha->lldd_ha; +struct asd_ascb *ascb; +struct scb *scb; +int res; + +CLEAR_NEXUS_PRE ; + scb->clear_nexus.nexus =NEXUS_ADAPTER ; +CLEAR_NEXUS_POST ; +} + +int asd_clear_nexus_port (struct sas_port *port) +{ +struct asd_ha_struct *asd_ha = port->ha->lldd_ha; +struct asd_ascb *ascb; +struct scb *scb; +int res; + +CLEAR_NEXUS_PRE ; + scb->clear_nexus.nexus =NEXUS_PORT ; + scb->clear_nexus.conn_mask = port->phy_mask; +CLEAR_NEXUS_POST ; +} + +#if 0 +static int asd_clear_nexus_I_T (struct domain_device *dev) +{ +struct asd_ha_struct *asd_ha = dev->port->ha->lldd_ha; +struct asd_ascb *ascb; +struct scb *scb; +int res; + +CLEAR_NEXUS_PRE ; + scb->clear_nexus.nexus =NEXUS_I_T ; + scb->clear_nexus.flags =SEND_Q |EXEC_Q |NOTINQ ; +if (dev->tproto) + scb->clear_nexus.flags |=SUSPEND_TX ; + scb->clear_nexus.conn_handle =cpu_to_le16 ((u16 )(unsigned long ) + dev->lldd_dev); +CLEAR_NEXUS_POST ; +} +#endif + +static int asd_clear_nexus_I_T_L (struct domain_device *dev,u8 *lun) +{ +struct asd_ha_struct *asd_ha = dev->port->ha->lldd_ha; +struct asd_ascb *ascb; +struct scb *scb; +int res; + +CLEAR_NEXUS_PRE ; + scb->clear_nexus.nexus =NEXUS_I_T_L ; + scb->clear_nexus.flags =SEND_Q |EXEC_Q |NOTINQ ; +if (dev->tproto) + scb->clear_nexus.flags |=SUSPEND_TX ; +memcpy (scb->clear_nexus.ssp_task.lun, lun,8 ); + scb->clear_nexus.conn_handle =cpu_to_le16 ((u16 )(unsigned long ) + dev->lldd_dev); +CLEAR_NEXUS_POST ; +} + +static int asd_clear_nexus_tag (struct sas_task *task) +{ +struct asd_ha_struct *asd_ha = task->dev->port->ha->lldd_ha; +struct asd_ascb *tascb = task->lldd_task; +struct asd_ascb *ascb; +struct scb *scb; +int res; + +CLEAR_NEXUS_PRE ; + scb->clear_nexus.nexus =NEXUS_TAG ; +memcpy (scb->clear_nexus.ssp_task.lun, task->ssp_task.LUN,8 ); + scb->clear_nexus.ssp_task.tag = tascb->tag; +if (task->dev->tproto) + scb->clear_nexus.conn_handle =cpu_to_le16 ((u16 )(unsigned long ) + task->dev->lldd_dev); +CLEAR_NEXUS_POST ; +} + +static int asd_clear_nexus_index (struct sas_task *task) +{ +struct asd_ha_struct *asd_ha = task->dev->port->ha->lldd_ha; +struct asd_ascb *tascb = task->lldd_task; +struct asd_ascb *ascb; +struct scb *scb; +int res; + +CLEAR_NEXUS_PRE ; + scb->clear_nexus.nexus =NEXUS_TRANS_CX ; +if (task->dev->tproto) + scb->clear_nexus.conn_handle =cpu_to_le16 ((u16 )(unsigned long ) + task->dev->lldd_dev); + scb->clear_nexus.index =cpu_to_le16 (tascb->tc_index); +CLEAR_NEXUS_POST ; +} + +/* ---------- TMFs ---------- */ + +static void asd_tmf_timedout (unsigned long data) +{ +struct asd_ascb *ascb = (void *) data; + +ASD_DPRINTK ("tmf timed out\n" ); +asd_timedout_common (data); + ascb->uldd_task = (void *)TMF_RESP_FUNC_FAILED ; +complete (&ascb->completion); +} + +static int asd_get_tmf_resp_tasklet (struct asd_ascb *ascb, +struct done_list_struct *dl) +{ +struct asd_ha_struct *asd_ha = ascb->ha; +unsigned long flags; +struct tc_resp_sb_struct { +__le16 index_escb; +u8 len_lsb; +u8 flags; + } __attribute__ ((packed)) *resp_sb = (void *) dl->status_block; + +int edb_id = ((resp_sb->flags &0x70 ) >>4 )-1 ; +struct asd_ascb *escb; +struct asd_dma_tok *edb; +struct ssp_frame_hdr *fh; +struct ssp_response_iu *ru; +int res =TMF_RESP_FUNC_FAILED ; + +ASD_DPRINTK ("tmf resp tasklet\n" ); + +spin_lock_irqsave (&asd_ha->seq.tc_index_lock, flags); + escb =asd_tc_index_find (&asd_ha->seq, + (int )le16_to_cpu (resp_sb->index_escb)); +spin_unlock_irqrestore (&asd_ha->seq.tc_index_lock, flags); + +if (!escb) { +ASD_DPRINTK ("Uh-oh! No escb for this dl?!\n" ); +return res; + } + + edb = asd_ha->seq.edb_arr[edb_id + escb->edb_index]; + ascb->tag = *(__be16 *)(edb->vaddr+4 ); + fh = edb->vaddr +16 ; + ru = edb->vaddr +16 +sizeof (*fh); + res = ru->status; +if (ru->datapres ==1 )/* Response data present */ + res = ru->resp_data[3 ]; +#if 0 + ascb->tag = fh->tag; +#endif + ascb->tag_valid =1 ; + +asd_invalidate_edb (escb, edb_id); +return res; +} + +static void asd_tmf_tasklet_complete (struct asd_ascb *ascb, +struct done_list_struct *dl) +{ +if (!del_timer (&ascb->timer)) +return ; + +ASD_DPRINTK ("tmf tasklet complete\n" ); + +if (dl->opcode ==TC_SSP_RESP ) + ascb->uldd_task = (void *) (unsigned long ) +asd_get_tmf_resp_tasklet (ascb, dl); +else + ascb->uldd_task = (void *)0xFF00 + (unsigned long ) dl->opcode; + +complete (&ascb->completion); +} + +static inline int asd_clear_nexus (struct sas_task *task) +{ +int res =TMF_RESP_FUNC_FAILED ; +struct asd_ascb *tascb = task->lldd_task; +unsigned long flags; + +ASD_DPRINTK ("task not done, clearing nexus\n" ); +if (tascb->tag_valid) + res =asd_clear_nexus_tag (task); +else + res =asd_clear_nexus_index (task); +wait_for_completion_timeout (&tascb->completion, +AIC94XX_SCB_TIMEOUT ); +ASD_DPRINTK ("came back from clear nexus\n" ); +spin_lock_irqsave (&task->task_state_lock, flags); +if (task->task_state_flags &SAS_TASK_STATE_DONE ) + res =TMF_RESP_FUNC_COMPLETE ; +spin_unlock_irqrestore (&task->task_state_lock, flags); + +return res; +} + +/** +* asd_abort_task -- ABORT TASK TMF +* @task: the task to be aborted +* +* Before calling ABORT TASK the task state flags should be ORed with +* SAS_TASK_STATE_ABORTED (unless SAS_TASK_STATE_DONE is set) under +* the task_state_lock IRQ spinlock, then ABORT TASK *must* be called. +* +* Implements the ABORT TASK TMF, I_T_L_Q nexus. +* Returns: SAS TMF responses (see sas_task.h), +* -ENOMEM, +* -SAS_QUEUE_FULL. +* +* When ABORT TASK returns, the caller of ABORT TASK checks first the +* task->task_state_flags, and then the return value of ABORT TASK. +* +* If the task has task state bit SAS_TASK_STATE_DONE set, then the +* task was completed successfully prior to it being aborted. The +* caller of ABORT TASK has responsibility to call task->task_done() +* xor free the task, depending on their framework. The return code +* is TMF_RESP_FUNC_FAILED in this case. +* +* Else the SAS_TASK_STATE_DONE bit is not set, +* If the return code is TMF_RESP_FUNC_COMPLETE, then +* the task was aborted successfully. The caller of +* ABORT TASK has responsibility to call task->task_done() +* to finish the task, xor free the task depending on their +* framework. +* else +* the ABORT TASK returned some kind of error. The task +* was _not_ cancelled. Nothing can be assumed. +* The caller of ABORT TASK may wish to retry. +*/ +int asd_abort_task (struct sas_task *task) +{ +struct asd_ascb *tascb = task->lldd_task; +struct asd_ha_struct *asd_ha = tascb->ha; +int res =1 ; +unsigned long flags; +struct asd_ascb *ascb =NULL ; +struct scb *scb; + +spin_lock_irqsave (&task->task_state_lock, flags); +if (task->task_state_flags &SAS_TASK_STATE_DONE ) { +spin_unlock_irqrestore (&task->task_state_lock, flags); + res =TMF_RESP_FUNC_COMPLETE ; +ASD_DPRINTK ("%s: task 0x%p done\n" , __FUNCTION__, task); + goto out_done; + } +spin_unlock_irqrestore (&task->task_state_lock, flags); + + ascb =asd_ascb_alloc_list (asd_ha, &res,GFP_KERNEL ); +if (!ascb) +return -ENOMEM ; + scb = ascb->scb; + + scb->header.opcode =ABORT_TASK ; + +switch (task->task_proto) { +case SATA_PROTO : +case SAS_PROTO_STP : + scb->abort_task.proto_conn_rate = (1 <<5 );/* STP */ +break ; +case SAS_PROTO_SSP : + scb->abort_task.proto_conn_rate = (1 <<4 );/* SSP */ + scb->abort_task.proto_conn_rate |= task->dev->linkrate; +break ; +case SAS_PROTO_SMP : +break ; +default : +break ; + } + +if (task->task_proto ==SAS_PROTO_SSP ) { + scb->abort_task.ssp_frame.frame_type =SSP_TASK ; +memcpy (scb->abort_task.ssp_frame.hashed_dest_addr, + task->dev->hashed_sas_addr,HASHED_SAS_ADDR_SIZE ); +memcpy (scb->abort_task.ssp_frame.hashed_src_addr, + task->dev->port->ha->hashed_sas_addr, +HASHED_SAS_ADDR_SIZE ); + scb->abort_task.ssp_frame.tptt =cpu_to_be16 (0xFFFF ); + +memcpy (scb->abort_task.ssp_task.lun, task->ssp_task.LUN,8 ); + scb->abort_task.ssp_task.tmf =TMF_ABORT_TASK ; + scb->abort_task.ssp_task.tag =cpu_to_be16 (0xFFFF ); + } + + scb->abort_task.sister_scb =cpu_to_le16 (0xFFFF ); + scb->abort_task.conn_handle =cpu_to_le16 ( + (u16 )(unsigned long )task->dev->lldd_dev); + scb->abort_task.retry_count =1 ; + scb->abort_task.index =cpu_to_le16 ((u16 )tascb->tc_index); + scb->abort_task.itnl_to =cpu_to_le16 (ITNL_TIMEOUT_CONST ); + + res =asd_enqueue_internal (ascb, asd_tmf_tasklet_complete, + asd_tmf_timedout); +if (res) + goto out; +wait_for_completion (&ascb->completion); +ASD_DPRINTK ("tmf came back\n" ); + + res = (int ) (unsigned long ) ascb->uldd_task; + tascb->tag = ascb->tag; + tascb->tag_valid = ascb->tag_valid; + +spin_lock_irqsave (&task->task_state_lock, flags); +if (task->task_state_flags &SAS_TASK_STATE_DONE ) { +spin_unlock_irqrestore (&task->task_state_lock, flags); + res =TMF_RESP_FUNC_COMPLETE ; +ASD_DPRINTK ("%s: task 0x%p done\n" , __FUNCTION__, task); + goto out_done; + } +spin_unlock_irqrestore (&task->task_state_lock, flags); + +switch (res) { +/* The task to be aborted has been sent to the device. +* We got a Response IU for the ABORT TASK TMF. */ +case TC_NO_ERROR +0xFF00 : +case TMF_RESP_FUNC_COMPLETE : +case TMF_RESP_FUNC_FAILED : + res =asd_clear_nexus (task); +break ; +case TMF_RESP_INVALID_FRAME : +case TMF_RESP_OVERLAPPED_TAG : +case TMF_RESP_FUNC_ESUPP : +case TMF_RESP_NO_LUN : + goto out_done;break ; + } +/* In the following we assume that the managing layer +* will _never_ make a mistake, when issuing ABORT TASK. +*/ +switch (res) { +default : + res =asd_clear_nexus (task); +/* fallthrough */ +case TC_NO_ERROR +0xFF00 : +case TMF_RESP_FUNC_COMPLETE : +break ; +/* The task hasn't been sent to the device xor we never got +* a (sane) Response IU for the ABORT TASK TMF. +*/ +case TF_NAK_RECV +0xFF00 : + res =TMF_RESP_INVALID_FRAME ; +break ; +case TF_TMF_TASK_DONE +0xFF00 :/* done but not reported yet */ + res =TMF_RESP_FUNC_FAILED ; +wait_for_completion_timeout (&tascb->completion, +AIC94XX_SCB_TIMEOUT ); +spin_lock_irqsave (&task->task_state_lock, flags); +if (task->task_state_flags &SAS_TASK_STATE_DONE ) + res =TMF_RESP_FUNC_COMPLETE ; +spin_unlock_irqrestore (&task->task_state_lock, flags); + goto out_done;break ; +case TF_TMF_NO_TAG +0xFF00 : +case TF_TMF_TAG_FREE +0xFF00 :/* the tag is in the free list */ +case TF_TMF_NO_CTX +0xFF00 :/* not in seq, or proto != SSP */ +case TF_TMF_NO_CONN_HANDLE +0xFF00 :/* no such device */ + res =TMF_RESP_FUNC_COMPLETE ; + goto out_done;break ; + } +out_done: +if (res ==TMF_RESP_FUNC_COMPLETE ) { + task->lldd_task =NULL ; +mb (); +asd_ascb_free (tascb); + } +out: +asd_ascb_free (ascb); +ASD_DPRINTK ("task 0x%p aborted, res: 0x%x\n" , task, res); +return res; +} + +/** +* asd_initiate_ssp_tmf -- send a TMF to an I_T_L or I_T_L_Q nexus +* @dev: pointer to struct domain_device of interest +* @lun: pointer to u8[8] which is the LUN +* @tmf: the TMF to be performed (see sas_task.h or the SAS spec) +* @index: the transaction context of the task to be queried if QT TMF +* +* This function is used to send ABORT TASK SET, CLEAR ACA, +* CLEAR TASK SET, LU RESET and QUERY TASK TMFs. +* +* No SCBs should be queued to the I_T_L nexus when this SCB is +* pending. +* +* Returns: TMF response code (see sas_task.h or the SAS spec) +*/ +static int asd_initiate_ssp_tmf (struct domain_device *dev,u8 *lun, +int tmf,int index) +{ +struct asd_ha_struct *asd_ha = dev->port->ha->lldd_ha; +struct asd_ascb *ascb; +int res =1 ; +struct scb *scb; + +if (!(dev->tproto &SAS_PROTO_SSP )) +return TMF_RESP_FUNC_ESUPP ; + + ascb =asd_ascb_alloc_list (asd_ha, &res,GFP_KERNEL ); +if (!ascb) +return -ENOMEM ; + scb = ascb->scb; + +if (tmf ==TMF_QUERY_TASK ) + scb->header.opcode =QUERY_SSP_TASK ; +else + scb->header.opcode =INITIATE_SSP_TMF ; + + scb->ssp_tmf.proto_conn_rate = (1 <<4 );/* SSP */ + scb->ssp_tmf.proto_conn_rate |= dev->linkrate; +/* SSP frame header */ + scb->ssp_tmf.ssp_frame.frame_type =SSP_TASK ; +memcpy (scb->ssp_tmf.ssp_frame.hashed_dest_addr, + dev->hashed_sas_addr,HASHED_SAS_ADDR_SIZE ); +memcpy (scb->ssp_tmf.ssp_frame.hashed_src_addr, + dev->port->ha->hashed_sas_addr,HASHED_SAS_ADDR_SIZE ); + scb->ssp_tmf.ssp_frame.tptt =cpu_to_be16 (0xFFFF ); +/* SSP Task IU */ +memcpy (scb->ssp_tmf.ssp_task.lun, lun,8 ); + scb->ssp_tmf.ssp_task.tmf = tmf; + + scb->ssp_tmf.sister_scb =cpu_to_le16 (0xFFFF ); + scb->ssp_tmf.conn_handle=cpu_to_le16 ((u16 )(unsigned long ) + dev->lldd_dev); + scb->ssp_tmf.retry_count =1 ; + scb->ssp_tmf.itnl_to =cpu_to_le16 (ITNL_TIMEOUT_CONST ); +if (tmf ==TMF_QUERY_TASK ) + scb->ssp_tmf.index =cpu_to_le16 (index); + + res =asd_enqueue_internal (ascb, asd_tmf_tasklet_complete, + asd_tmf_timedout); +if (res) + goto out_err; +wait_for_completion (&ascb->completion); + res = (int ) (unsigned long ) ascb->uldd_task; + +switch (res) { +case TC_NO_ERROR +0xFF00 : + res =TMF_RESP_FUNC_COMPLETE ; +break ; +case TF_NAK_RECV +0xFF00 : + res =TMF_RESP_INVALID_FRAME ; +break ; +case TF_TMF_TASK_DONE +0xFF00 : + res =TMF_RESP_FUNC_FAILED ; +break ; +case TF_TMF_NO_TAG +0xFF00 : +case TF_TMF_TAG_FREE +0xFF00 :/* the tag is in the free list */ +case TF_TMF_NO_CTX +0xFF00 :/* not in seq, or proto != SSP */ +case TF_TMF_NO_CONN_HANDLE +0xFF00 :/* no such device */ + res =TMF_RESP_FUNC_COMPLETE ; +break ; +default : +ASD_DPRINTK ("%s: converting result 0x%x to TMF_RESP_FUNC_FAILED\n" , + __FUNCTION__, res); + res =TMF_RESP_FUNC_FAILED ; +break ; + } +out_err: +asd_ascb_free (ascb); +return res; +} + +int asd_abort_task_set (struct domain_device *dev,u8 *lun) +{ +int res =asd_initiate_ssp_tmf (dev, lun,TMF_ABORT_TASK_SET ,0 ); + +if (res ==TMF_RESP_FUNC_COMPLETE ) +asd_clear_nexus_I_T_L (dev, lun); +return res; +} + +int asd_clear_aca (struct domain_device *dev,u8 *lun) +{ +int res =asd_initiate_ssp_tmf (dev, lun,TMF_CLEAR_ACA ,0 ); + +if (res ==TMF_RESP_FUNC_COMPLETE ) +asd_clear_nexus_I_T_L (dev, lun); +return res; +} + +int asd_clear_task_set (struct domain_device *dev,u8 *lun) +{ +int res =asd_initiate_ssp_tmf (dev, lun,TMF_CLEAR_TASK_SET ,0 ); + +if (res ==TMF_RESP_FUNC_COMPLETE ) +asd_clear_nexus_I_T_L (dev, lun); +return res; +} + +int asd_lu_reset (struct domain_device *dev,u8 *lun) +{ +int res =asd_initiate_ssp_tmf (dev, lun,TMF_LU_RESET ,0 ); + +if (res ==TMF_RESP_FUNC_COMPLETE ) +asd_clear_nexus_I_T_L (dev, lun); +return res; +} + +/** +* asd_query_task -- send a QUERY TASK TMF to an I_T_L_Q nexus +* task: pointer to sas_task struct of interest +* +* Returns: TMF_RESP_FUNC_COMPLETE if the task is not in the task set, +* or TMF_RESP_FUNC_SUCC if the task is in the task set. +* +* Normally the management layer sets the task to aborted state, +* and then calls query task and then abort task. +*/ +int asd_query_task (struct sas_task *task) +{ +struct asd_ascb *ascb = task->lldd_task; +int index; + +if (ascb) { + index = ascb->tc_index; +return asd_initiate_ssp_tmf (task->dev, task->ssp_task.LUN, +TMF_QUERY_TASK , index); + } +return TMF_RESP_FUNC_COMPLETE ; +}