/******************************************************************************
 *                  QLOGIC LINUX SOFTWARE
 *
 * QLogic ISP2x00 device driver for Linux 2.6.x
 * Copyright (C) 2003 QLogic Corporation
 * (www.qlogic.com)
 *
 * 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, 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.
 *
 ******************************************************************************/

/* Management functions for various lists */

/* __add_to_done_queue()
 * 
 * Place SRB command on done queue.
 *
 * Input:
 *      ha           = host pointer
 *      sp           = srb pointer.
 * Locking:
 * 	this function assumes the ha->list_lock is already taken
 */
static inline void 
__add_to_done_queue(struct scsi_qla_host * ha, srb_t * sp)
{
	/*
        if (sp->state != SRB_NO_QUEUE_STATE && 
        	sp->state != SRB_ACTIVE_STATE)
		BUG();
	*/

        /* Place block on done queue */
        sp->cmd->host_scribble = (unsigned char *) NULL;
        sp->state = SRB_DONE_STATE;
        list_add_tail(&sp->list,&ha->done_queue);
        ha->done_q_cnt++;
	sp->ha = ha;
}

static inline void 
__add_to_retry_queue(struct scsi_qla_host * ha, srb_t * sp)
{
	/*
        if( sp->state != SRB_NO_QUEUE_STATE && 
        	sp->state != SRB_ACTIVE_STATE)
		BUG();
	*/

        /* Place block on retry queue */
        list_add_tail(&sp->list,&ha->retry_queue);
        ha->retry_q_cnt++;
        sp->flags |= SRB_WATCHDOG;
        sp->state = SRB_RETRY_STATE;
	sp->ha = ha;
}

static inline void 
__add_to_scsi_retry_queue(struct scsi_qla_host * ha, srb_t * sp)
{
	/*
        if( sp->state != SRB_NO_QUEUE_STATE && 
        	sp->state != SRB_ACTIVE_STATE)
		BUG();
	*/

        /* Place block on retry queue */
        list_add_tail(&sp->list,&ha->scsi_retry_queue);
        ha->scsi_retry_q_cnt++;
        sp->state = SRB_SCSI_RETRY_STATE;
	sp->ha = ha;
}

static inline void 
add_to_done_queue(struct scsi_qla_host * ha, srb_t * sp)
{
        unsigned long flags;

        spin_lock_irqsave(&ha->list_lock, flags);
        __add_to_done_queue(ha,sp);
        spin_unlock_irqrestore(&ha->list_lock, flags);
}

static inline void 
add_to_free_queue(struct scsi_qla_host * ha, srb_t * sp)
{
	mempool_free(sp, ha->srb_mempool);
}

static inline void 
add_to_retry_queue(struct scsi_qla_host * ha, srb_t * sp)
{
        unsigned long flags;

        spin_lock_irqsave(&ha->list_lock, flags);
        __add_to_retry_queue(ha,sp);
        spin_unlock_irqrestore(&ha->list_lock, flags);
}

static inline void 
add_to_scsi_retry_queue(struct scsi_qla_host * ha, srb_t * sp)
{
        unsigned long flags;

        spin_lock_irqsave(&ha->list_lock, flags);
        __add_to_scsi_retry_queue(ha,sp);
        spin_unlock_irqrestore(&ha->list_lock, flags);
}

/*
 * __del_from_retry_queue
 *      Function used to remove a command block from the
 *      watchdog timer queue.
 *
 *      Note: Must insure that command is on watchdog
 *            list before calling del_from_retry_queue
 *            if (sp->flags & SRB_WATCHDOG)
 *
 * Input: 
 *      ha = adapter block pointer.
 *      sp = srb pointer.
 * Locking:
 *	this function assumes the list_lock is already taken
 */
static inline void 
__del_from_retry_queue(struct scsi_qla_host * ha, srb_t * sp)
{
        list_del_init(&sp->list);

        sp->flags &= ~(SRB_WATCHDOG | SRB_BUSY);
        sp->state = SRB_NO_QUEUE_STATE;
        ha->retry_q_cnt--;
}

/*
 * __del_from_scsi_retry_queue
 *      Function used to remove a command block from the
 *      scsi retry queue.
 *
 * Input: 
 *      ha = adapter block pointer.
 *      sp = srb pointer.
 * Locking:
 *	this function assumes the list_lock is already taken
 */
static inline void 
__del_from_scsi_retry_queue(struct scsi_qla_host * ha, srb_t * sp)
{
        list_del_init(&sp->list);

        ha->scsi_retry_q_cnt--;
        sp->state = SRB_NO_QUEUE_STATE;
}

/*
 * del_from_retry_queue
 *      Function used to remove a command block from the
 *      watchdog timer queue.
 *
 *      Note: Must insure that command is on watchdog
 *            list before calling del_from_retry_queue
 *            if (sp->flags & SRB_WATCHDOG)
 *
 * Input: 
 *      ha = adapter block pointer.
 *      sp = srb pointer.
 * Locking:
 *	this function takes and releases the list_lock
 */
static inline void 
del_from_retry_queue(struct scsi_qla_host * ha, srb_t * sp)
{
        unsigned long flags;

        /*	if (unlikely(!(sp->flags & SRB_WATCHDOG)))
        		BUG();*/
        spin_lock_irqsave(&ha->list_lock, flags);

        /*	if (unlikely(list_empty(&ha->retry_queue)))
        		BUG();*/

        __del_from_retry_queue(ha,sp);

        spin_unlock_irqrestore(&ha->list_lock, flags);
}
/*
 * del_from_scsi_retry_queue
 *      Function used to remove a command block from the
 *      scsi retry queue.
 *
 * Input: 
 *      ha = adapter block pointer.
 *      sp = srb pointer.
 * Locking:
 *	this function takes and releases the list_lock
 */
static inline void 
del_from_scsi_retry_queue(struct scsi_qla_host * ha, srb_t * sp)
{
        unsigned long flags;

        spin_lock_irqsave(&ha->list_lock, flags);

        /*	if (unlikely(list_empty(&ha->scsi_retry_queue)))
        		BUG();*/

        __del_from_scsi_retry_queue(ha,sp);

        spin_unlock_irqrestore(&ha->list_lock, flags);
}

/*
 * __add_to_pending_queue
 *      Add the standard SCB job to the bottom of standard SCB commands.
 *
 * Input:
 * COMPLETE!!!
 *      q  = SCSI LU pointer.
 *      sp = srb pointer.
 *      SCSI_LU_Q lock must be already obtained.
 */
static inline int 
__add_to_pending_queue(struct scsi_qla_host *ha, srb_t * sp)
{
	int	empty;
	/*
        if( sp->state != SRB_NO_QUEUE_STATE &&
        	sp->state != SRB_FREE_STATE &&
        	sp->state != SRB_ACTIVE_STATE)
		BUG();
	*/

	empty = list_empty(&ha->pending_queue);
	list_add_tail(&sp->list, &ha->pending_queue);
	ha->qthreads++;
	sp->state = SRB_PENDING_STATE;

	return (empty);
}

static inline void 
__add_to_pending_queue_head(struct scsi_qla_host *ha, srb_t * sp)
{
	/*
        if( sp->state != SRB_NO_QUEUE_STATE && 
        	sp->state != SRB_FREE_STATE &&
        	sp->state != SRB_ACTIVE_STATE)
		BUG();
	*/

	list_add(&sp->list, &ha->pending_queue);
	ha->qthreads++;
	sp->state = SRB_PENDING_STATE;
}

static inline int
add_to_pending_queue(struct scsi_qla_host *ha, srb_t *sp)
{
	int	empty;
	unsigned long flags;

	spin_lock_irqsave(&ha->list_lock, flags);
	empty = __add_to_pending_queue(ha, sp);
	spin_unlock_irqrestore(&ha->list_lock, flags);

	return (empty);
}
static inline void
add_to_pending_queue_head(struct scsi_qla_host *ha, srb_t *sp)
{
	unsigned long flags;

	spin_lock_irqsave(&ha->list_lock, flags);
	__add_to_pending_queue_head(ha, sp);
	spin_unlock_irqrestore(&ha->list_lock, flags);
}

static inline void
__del_from_pending_queue(struct scsi_qla_host *ha, srb_t *sp)
{
	list_del_init(&sp->list);
	ha->qthreads--;
	sp->state = SRB_NO_QUEUE_STATE;
}

/*
 * Failover Stuff.
 */
static inline void
__add_to_failover_queue(struct scsi_qla_host * ha, srb_t * sp)
{
	/*
        if( sp->state != SRB_NO_QUEUE_STATE && 
        	sp->state != SRB_ACTIVE_STATE)
		BUG();
	*/

        list_add_tail(&sp->list,&ha->failover_queue);
        ha->failover_cnt++;
        sp->state = SRB_FAILOVER_STATE;
	sp->ha = ha;
}

static inline void add_to_failover_queue(struct scsi_qla_host * ha, srb_t * sp)
{
        unsigned long flags;

        spin_lock_irqsave(&ha->list_lock, flags);

        __add_to_failover_queue(ha,sp);

        spin_unlock_irqrestore(&ha->list_lock, flags);
}
static inline void __del_from_failover_queue(struct scsi_qla_host * ha, srb_t *
                sp)
{
        ha->failover_cnt--;
        list_del_init(&sp->list);
        sp->state = SRB_NO_QUEUE_STATE;
}

static inline void del_from_failover_queue(struct scsi_qla_host * ha, srb_t * sp)
{
        unsigned long flags;

        spin_lock_irqsave(&ha->list_lock, flags);

        __del_from_failover_queue(ha,sp);

        spin_unlock_irqrestore(&ha->list_lock, flags);
}

static inline void 
del_from_pending_queue(struct scsi_qla_host * ha, srb_t * sp)
{
        unsigned long flags;

        spin_lock_irqsave(&ha->list_lock, flags);

        __del_from_pending_queue(ha,sp);

        spin_unlock_irqrestore(&ha->list_lock, flags);
}
