/*      $NetBSD: iscsi_globals.h,v 1.28 2023/11/25 10:08:27 mlelstv Exp $       */

/*-
* Copyright (c) 2004,2005,2006,2011 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Wasabi Systems, Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
*    notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
*    notice, this list of conditions and the following disclaimer in the
*    documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef _ISCSI_GLOBALS_H
#define _ISCSI_GLOBALS_H

/*#include "opt_ddb.h" */
#define DDB 1

/* Includes we need in all files */

#include <sys/param.h>
#include <sys/mutex.h>
#include <sys/rwlock.h>
#include <sys/proc.h>
#include <sys/conf.h>
#include <sys/errno.h>
#include <sys/malloc.h>
#include <sys/scsiio.h>
#include <sys/kernel.h>
#include <sys/kthread.h>
#include <sys/systm.h>
#include <sys/device.h>

#include <dev/scsipi/scsi_all.h>
#include <dev/scsipi/scsi_message.h>
#include <dev/scsipi/scsipi_all.h>
#include <dev/scsipi/scsiconf.h>
#include <dev/scsipi/scsipiconf.h>

#include "iscsi.h"
#include "iscsi_pdu.h"
#include "iscsi_ioctl.h"

/* ------------------------ Code selection constants ------------------------ */

#define ISCSI_DEBUG      0

/* -------------------------  Global Constants  ----------------------------- */

/* Version information */

#define INTERFACE_VERSION       2
#define VERSION_MAJOR           3
#define VERSION_MINOR           1
#define VERSION_STRING          "NetBSD iSCSI Software Initiator 20110407"

/*
  NOTE: CCBS_PER_SESSION must not exceed 256 due to the way the ITT
  is constructed (it has the CCB index in its lower 8 bits). If it should ever
  be necessary to increase the number beyond that (which isn't expected),
  the corresponding ITT generation and extraction code must be rewritten.
*/
#define CCBS_PER_SESSION      32        /* ToDo: Reasonable number?? */
/*
  NOTE: CCBS_FOR_SCSPI limits the number of outstanding commands for
  SCSI commands, leaving some CCBs for keepalive and logout attempts,
  which are needed for each connection.
*/
#define CCBS_FOR_SCSIPI       16        /* ToDo: Reasonable number?? */
/*
  NOTE: PDUS_PER_CONNECTION is a number that could potentially impact
  performance if set too low, as a single command may use up a lot of PDUs for
  high values of First/MaxBurstLength and small values of
  MaxRecvDataSegmentLength of the target.
*/
#define PDUS_PER_CONNECTION   64        /* ToDo: Reasonable number?? */

/* max outstanding serial nums before we give up on the connection */
#define SERNUM_BUFFER_LENGTH  (CCBS_PER_SESSION / 2)    /* ToDo: Reasonable?? */

/* The RecvDataSegmentLength for Target->Initiator */
#define DEFAULT_MaxRecvDataSegmentLength     (64*1024)

/* Command timeout (reset on received PDU associated with the command's CCB) */
#define COMMAND_TIMEOUT         (60 * hz) /* ToDo: Reasonable? (60 seconds) */
#define MAX_CCB_TIMEOUTS        3               /* Max number of tries to resend or SNACK */
#define MAX_CCB_TRIES           9       /* Max number of total tries to recover */

/* Connectionn timeout (reset on every valid received PDU) */
#define CONNECTION_TIMEOUT       (2 * hz)       /* ToDo: Reasonable? (2 seconds) */
#define CONNECTION_IDLE_TIMEOUT  (30 * hz)      /* Adjusted to Time2Retain/2 later */
#define MAX_CONN_TIMEOUTS        4      /* Max number of tries to ping a target */

/* Maximum attempts to recover connection */
#define MAX_RECOVERY_ATTEMPTS   2       /* If two attempts don't work, something */
                                                                       /* probably is seriously broken */

/* PDU flags */

#define PDUF_BUSY       0x01    /* PDU is being sent, don't re-send */
#define PDUF_INQUEUE    0x02    /* PDU is in send queue */
#define PDUF_PRIORITY   0x04    /* Insert PDU at head of queue */
#define PDUF_NOUPDATE   0x10    /* Do not update PDU header/digest (test mode) */

/* CCB Flags */

#define CCBF_COMPLETE   0x0001  /* received status */
#define CCBF_RESENT     0x0002  /* ccb was resent */
#define CCBF_SENDTARGET 0x0004  /* SendTargets text request, not negotiation */
#define CCBF_GOT_RSP    0x0010  /* Got at least one response to this request */
#define CCBF_REASSIGN   0x0020  /* Command can be reassigned */
#define CCBF_OTHERCONN  0x0040  /* a logout for a different connection */
#define CCBF_WAITQUEUE  0x0080  /* CCB is on waiting queue */

/* ---------------------------  Global Types  ------------------------------- */

/* Connection state */

typedef enum {
       ST_SEC_NEG      = 0,    /* security negotiation phase */
       ST_SEC_FIN      = 1,    /* switch from SEC after mutual CHAP */
       ST_OP_NEG       = 2,    /* operational negotiation phase */
       ST_FULL_FEATURE = 3,    /* full feature phase */
       ST_WINDING_DOWN = 4,    /* connection termination initiated, logging out */
       ST_LOGOUT_SENT  = 5,    /* logout has been sent */
       ST_SETTLING     = 6,    /* waiting for things to settle down */
       ST_IDLE         = 7     /* connection is idle (ready to delete) */
} conn_state_t;


/* Logout state */

typedef enum {
       NOT_LOGGED_OUT,                         /* Not logged out */
       LOGOUT_SENT,                            /* Logout was sent */
       LOGOUT_SUCCESS,                         /* Logout succeeded */
       LOGOUT_FAILED                           /* Logout failed */
} logout_state_t;


/* CCB Disposition */

typedef enum {
       CCBDISP_UNUSED, /* 0 = In free pool */
       CCBDISP_BUSY,   /* This CCB is busy, don't allow rx ops */
       CCBDISP_NOWAIT, /* Not waiting for anything */
       CCBDISP_FREE,   /* Free this CCB when done */
       CCBDISP_WAIT,   /* Calling thread is waiting for completion */
       CCBDISP_SCSIPI, /* Call scsipi_done when operation completes */
       CCBDISP_DEFER   /* Defer waiting until all PDUs have been queued */
} ccb_disp_t;


/* PDU Disposition */

typedef enum {
       PDUDISP_UNUSED,         /* 0 = In free pool */
       PDUDISP_FREE,           /* Free this PDU when done */
       PDUDISP_WAIT            /* Waiting for acknowledge */
} pdu_disp_t;

/* Timeout state */

typedef enum {
       TOUT_NONE,              /* Initial */
       TOUT_ARMED,             /* callout is scheduled */
       TOUT_QUEUED,            /* put into timeout queue */
       TOUT_BUSY               /* cleanup thread working */
} tout_state_t;

typedef struct connection_s connection_t;
typedef struct session_s session_t;
typedef struct ccb_s ccb_t;
typedef struct pdu_s pdu_t;

/* the serial number management structure (a circular buffer) */

typedef struct {
       uint32_t        ExpSN;  /* ExpxxSN (Data or Stat) sent to the target */
       uint32_t        next_sn; /* next_sn (== ExpSN if no ack is pending) */
       int             top;    /* top of buffer (newest element) */
       int             bottom; /* bottom of buffer (oldest element) */
       uint32_t        sernum[SERNUM_BUFFER_LENGTH];   /* the serial numbers */
       bool            ack[SERNUM_BUFFER_LENGTH];      /* acknowledged? */
} sernum_buffer_t;


/*
  The per-PDU data structure.
*/

struct pdu_s {
       TAILQ_ENTRY(pdu_s)      pdu_chain;      /* freelist or wait list (or no list) */
       TAILQ_ENTRY(pdu_s)      pdu_send_chain;
                               /* chaining PDUs waiting to be sent */
       pdu_disp_t              pdu_disp; /* what to do with this pdu */
       uint32_t                pdu_flags;      /* various processing flags */
       pdu_header_t            pdu_hdr; /* Buffer for PDU associated with cmd */
       void                    *pdu_temp_data; /* (free after use) */
       uint32_t                pdu_temp_data_len;      /* size of temp data */

       struct uio              pdu_uio; /* UIO structure */
       struct iovec            pdu_io_vec[4];
                               /* Header + data + data-digest + padding */

       struct uio              pdu_save_uio;
                               /* UIO structure save for retransmits */
       struct iovec            pdu_save_iovec[4];
                               /* Header + data + data-digest + padding */
       uint32_t                pdu_data_digest;
                               /* holds data digest if enabled */
       ccb_t                   *pdu_owner;
                               /* the ccb this PDU belongs to (if any) */
       connection_t            *pdu_connection;
                               /* the connection this PDU belongs to */
};


/* the PDU list type */

TAILQ_HEAD(pdu_list_s, pdu_s);
typedef struct pdu_list_s pdu_list_t;

/*
  The per-command data structure. Calling it ccb in correspondence
  to other HA drivers.
*/

struct ccb_s {
       TAILQ_ENTRY(ccb_s)      ccb_chain;
       /* either freelist or waiting list (or no list) */

       uint32_t                ccb_status; /* Status gets entered here */
       ccb_disp_t              ccb_disp;       /* what to do with this ccb */

       struct callout          ccb_timeout; /* To make sure it isn't lost */
       TAILQ_ENTRY(ccb_s)      ccb_tchain;
       tout_state_t            ccb_timedout;
       int                     ccb_num_timeouts;
       /* How often we've sent out SNACK without answer */
       int                     ccb_total_tries;
       /* How often we've tried to recover */

       uint32_t                ccb_ITT;
       /* task tag: ITT counter + sess id + CCB index */
       sernum_buffer_t         ccb_DataSN_buf;
       /* Received Data Seq nums (read ops only) */

       void                    *ccb_par;
       /* misc. parameter for this request */
       struct scsipi_xfer      *ccb_xs;
       /* the scsipi_xfer for this cmd */

       void                    *ccb_temp_data;
       /* to hold state (mainly during negotiation) */
       void                    *ccb_text_data;
       /* holds accumulated text for continued PDUs */
       uint32_t                ccb_text_len;
       /* length of text data so far */

       uint64_t                ccb_lun; /* LUN */
       uint32_t                ccb_tag; /* Command tag */
       uint8_t                 *ccb_cmd; /* SCSI command block */
       uint16_t                ccb_cmdlen; /* SCSI command block length */
       bool                    ccb_data_in; /* if this is a read request */
       uint8_t                 *ccb_data_ptr; /* data pointer for read/write */
       uint32_t                ccb_data_len; /* total data length */
       uint32_t                ccb_xfer_len; /* data transferred on read */
       uint32_t                ccb_residual; /* residual data size */

       void                    *ccb_sense_ptr; /* sense data pointer */
       int                     ccb_sense_len_req; /* requested sense data length */
       int                     ccb_sense_len_got; /* actual sense data length */

       pdu_t                   *ccb_pdu_waiting; /* PDU waiting to be ack'ed */
       volatile uint32_t       ccb_CmdSN; /* CmdSN associated with waiting PDU */

       int                     ccb_flags;
       connection_t            *ccb_connection; /* connection for CCB */
       session_t               *ccb_session; /* session for CCB */
};


/* the CCB list type */

TAILQ_HEAD(ccb_list_s, ccb_s);
typedef struct ccb_list_s ccb_list_t;


/*
  Per connection data: the connection structure
*/
struct connection_s {
       TAILQ_ENTRY(connection_s)       c_connections;

       kmutex_t                        c_lock;
       kcondvar_t                      c_conn_cv;
       kcondvar_t                      c_pdu_cv;
       kcondvar_t                      c_ccb_cv;
       kcondvar_t                      c_idle_cv;

       pdu_list_t                      c_pdu_pool; /* the free PDU pool */

       ccb_list_t                      c_ccbs_waiting;
                                       /* CCBs waiting for completion */

       pdu_list_t                      c_pdus_to_send;
                                       /* the PDUs waiting to be sent */

       sernum_buffer_t                 c_StatSN_buf;
                                       /* to keep track of received StatSNs */

       uint32_t                        c_max_transfer;
               /* min(MaxRecvDataSegmentLength, MaxBurstLength) */
       uint32_t                        c_max_firstimmed;
               /* 0 if InitialR2T=Yes, else
                  min of (MaxRecvDataSegmentLength, FirstBurstLength) */
       uint32_t                        c_max_firstdata;
               /* 0 if ImmediateData=No, else min of */
               /* (MaxRecvDataSegmentLength, FirstBurstLength) */

       uint32_t                        c_MaxRecvDataSegmentLength;
                                       /* Target's value */
       uint32_t                        c_Our_MaxRecvDataSegmentLength;
                                       /* Our own value */
       bool                            c_HeaderDigest; /* TRUE if doing CRC */
       bool                            c_DataDigest;   /* TRUE if doing CRC */
       uint32_t                        c_Time2Wait;
                                       /* Negotiated default or logout value */
       uint32_t                        c_Time2Retain;
                                       /* Negotiated default or logout value */

       uint16_t                        c_id;
               /* connection ID (unique within session) */

       conn_state_t                    c_state; /* State of connection */

       struct lwp                      *c_threadobj;
               /* proc/thread pointer of socket owner */

       krwlock_t                       c_sock_rw;
       struct file                     *c_sock;        /* the connection's socket */
       session_t                       *c_session;
                                       /* back pointer to the owning session */

       struct lwp                      *c_rcvproc; /* receive thread */
       struct lwp                      *c_sendproc; /* send thread */

       uint32_t                        c_terminating;
                                       /* if closing down: status */
       int                             c_recover; /* recovery count */
               /* (reset on first successful data transfer) */
       volatile unsigned               c_usecount; /* number of active CCBs */
       unsigned                        c_pducount; /* number of active PDUs */

       bool                            c_destroy; /* conn will be destroyed */
       bool                            c_in_session;
               /* if it's linked into the session list */
       logout_state_t                  c_loggedout;
               /* status of logout (for recovery) */
       struct callout                  c_timeout;
               /* Timeout for checking if connection is dead */
       TAILQ_ENTRY(connection_s)       c_tchain;
       tout_state_t                    c_timedout;
       int                             c_num_timeouts;
               /* How often we've sent out a NOP without answer */
       uint32_t                        c_idle_timeout_val;
               /* Connection timeout value when idle */

       iscsi_login_parameters_t        *c_login_par;
                                       /* only valid during login */

       pdu_t                           c_pdu[PDUS_PER_CONNECTION]; /* PDUs */
};

/* the connection list type */

TAILQ_HEAD(connection_list_s, connection_s);
typedef struct connection_list_s connection_list_t;


/*
  Per session data: the session structure
*/

struct session_s {
       /* Interface to child drivers.
          NOTE: sc_adapter MUST be the first field in this structure so we can
          easily get from adapter to session.
        */
       struct scsipi_adapter   s_sc_adapter;
       struct scsipi_channel   s_sc_channel;

       device_t                s_child_dev;
       /* the child we're associated with - (NULL if not mapped) */

       int                     s_refcount;     /* session in use by scsipi */

       /* local stuff */
       TAILQ_ENTRY(session_s)  s_sessions;     /* the list of sessions */

       kmutex_t                s_lock;
       kcondvar_t              s_sess_cv;
       kcondvar_t              s_ccb_cv;

       ccb_list_t              s_ccb_pool;     /* The free CCB pool */

       int                     s_send_window;

       uint16_t                s_id;   /* session ID (unique within driver) */
       uint16_t                s_TSIH; /* Target assigned session ID */

       uint32_t                s_CmdSN;         /* Current CmdSN */
       uint32_t                s_ExpCmdSN; /* Current max ExpCmdSN received */
       uint32_t                s_MaxCmdSN; /* Current MaxCmdSN */

       /* negotiated values */
       uint32_t                s_ErrorRecoveryLevel;
       uint32_t                s_FirstBurstLength;
       uint32_t                s_MaxBurstLength;
       bool                    s_ImmediateData;
       bool                    s_InitialR2T;
       uint32_t                s_MaxOutstandingR2T;
       uint32_t                s_MaxConnections;
       uint32_t                s_DefaultTime2Wait;
       uint32_t                s_DefaultTime2Retain;

       iscsi_login_session_type_t s_login_type;        /* session type */

       /* for send_targets requests */
       uint8_t                 *s_target_list;
       uint32_t                s_target_list_len;

       uint32_t                s_conn_id;      /* connection ID counter */

       uint32_t                s_terminating;  /* if closing down: status */

       uint32_t                s_active_connections;
                               /* currently active connections */
       uint32_t                s_total_connections;
       /* connections associated with this session (active or winding down) */
       connection_list_t       s_conn_list;    /* the list of connections */
       connection_t            *s_mru_connection;
                               /* the most recently used connection */

       ccb_t                   s_ccb[CCBS_PER_SESSION];        /* CCBs */

       char                    s_tgtname[ISCSI_STRING_LENGTH + 1];
                               /* iSCSI target name */
};

/* the session list type */

TAILQ_HEAD(session_list_s, session_s);
typedef struct session_list_s session_list_t;


/*
  Event notification structures
*/

typedef struct event_s {
       TAILQ_ENTRY(event_s)    ev_link;        /* next event in queue */
       iscsi_event_t           ev_event_kind;  /* which event */
       uint32_t                ev_session_id;  /* affected session ID */
       uint32_t                ev_connection_id;/* affected connection ID */
       uint32_t                ev_reason;              /* event reason */
} event_t;

/* the event list entry type */

TAILQ_HEAD(event_list_s, event_s);
typedef struct event_list_s event_list_t;


typedef struct event_handler_s {
       TAILQ_ENTRY(event_handler_s)    evh_link;       /* next handler */
       uint32_t                        evh_id; /* unique ID */
       event_list_t                    evh_events;     /* list of events pending */
       iscsi_wait_event_parameters_t   *evh_waiter; /* waiting parameter */
       /* following to detect dead handlers */
       event_t                         *evh_first_in_list;
} event_handler_t;

/* the event list entry type */

TAILQ_HEAD(event_handler_list_s, event_handler_s);
typedef struct event_handler_list_s event_handler_list_t;

/* /dev/iscsi0 state */
struct iscsifd {
       TAILQ_ENTRY(iscsifd)            fd_link;
       device_t        fd_dev;
       int             fd_unit;
};

/* -------------------------  Global Variables  ----------------------------- */

/* In iscsi_main.c */

extern struct cfattach iscsi_ca;                /* the device attach structure */

extern session_list_t iscsi_sessions;           /* the list of sessions */
extern bool iscsi_detaching;                    /* signal to cleanup thread it should exit */
extern uint32_t iscsi_num_send_threads;         /* the number of active send threads */

extern uint8_t iscsi_InitiatorName[ISCSI_STRING_LENGTH];
extern uint8_t iscsi_InitiatorAlias[ISCSI_STRING_LENGTH];
extern login_isid_t iscsi_InitiatorISID;

/* Debugging stuff */

#ifndef DDB
#define Debugger() panic("should call debugger here (iscsi.c)")
#endif /* ! DDB */

#ifdef ISCSI_DEBUG

extern int iscsi_debug_level;   /* How much debug info to display */
extern bool iscsi_hex_bignums;  /* Whether to encode parameters in hex or base64 */

#define DEBOUT(x) printf x
#define DEB(lev,x) { if (iscsi_debug_level >= lev) printf x ;}
#define DEBC(conn,lev,x) { if (iscsi_debug_level >= lev) { printf("S%dC%d: ", \
                       conn && conn->c_session ? conn->c_session->s_id : -1, \
                       conn ? conn->c_id : -1); printf x ;}}

#define STATIC static

#else

#define DEBOUT(x)
#define DEB(lev,x)
#define DEBC(conn,lev,x)

#define STATIC static

#endif

/* Critical section macros */

/* misc stuff */
#define min(a, b) ((a) < (b)) ? (a) : (b)
#define max(a, b) ((a) < (b)) ? (b) : (a)


/*
  Convert unsigned int to 3-byte value (for DataSegmentLength field in PDU)
*/

static __inline void
hton3(uint32_t val, uint8_t *bytes)
{
       bytes[0] = (uint8_t) (val >> 16);
       bytes[1] = (uint8_t) (val >> 8);
       bytes[2] = (uint8_t) val;
}

/*
  Convert 3-byte value to unsigned int (for DataSegmentLength field in PDU)
*/

static __inline uint32_t
ntoh3(uint8_t *bytes)
{
       return (bytes[0] << 16) | (bytes[1] << 8) | bytes[2];
}


/*
* Convert uint64 to network byte order (for LUN field in PDU)
*/
static __inline uint64_t
htonq(uint64_t x)
{
#if BYTE_ORDER == LITTLE_ENDIAN
       uint8_t *s = (uint8_t *) & x;
       return (uint64_t) ((uint64_t) s[0] << 56 | (uint64_t) s[1] << 48 |
                       (uint64_t) s[2] << 40 | (uint64_t) s[3] << 32 |
                       (uint64_t) s[4] << 24 | (uint64_t) s[5] << 16 |
                       (uint64_t) s[6] <<  8 | (uint64_t) s[7]);
#else
       return x;
#endif
}

#define ntohq(x) htonq(x)

/*
* Serial number buffer empty?
*/

static __inline bool
sn_empty(sernum_buffer_t *buf)
{
       return buf->top == buf->bottom;
}


/*
* Serial number compare
*/

static __inline bool
sn_a_lt_b(uint32_t a, uint32_t b)
{
       return (a < b && !((b - a) & 0x80000000)) ||
              (a > b && ((a - b) & 0x80000000));
}

static __inline bool
sn_a_le_b(uint32_t a, uint32_t b)
{
       return (a <= b && !((b - a) & 0x80000000)) ||
              (a >= b && ((a - b) & 0x80000000));
}

/* in iscsi_ioctl.c */

void iscsi_init_cleanup(void);
int iscsi_destroy_cleanup(void);
void iscsi_notify_cleanup(void);


/* Parameter for logout is reason code in logout PDU, -1 for don't send logout */
#define NO_LOGOUT          -1
#define LOGOUT_SESSION     0
#define LOGOUT_CONNECTION  1
#define RECOVER_CONNECTION 2

void add_event(iscsi_event_t, uint32_t, uint32_t, uint32_t);

void kill_connection(connection_t *, uint32_t, int, bool);
void kill_session(uint32_t, uint32_t, int, bool);
int kill_all_sessions(void);
void handle_connection_error(connection_t *, uint32_t, int);
void add_connection_cleanup(connection_t *);

int iscsiioctl(struct file *, u_long, void *);

session_t *find_session(uint32_t);
connection_t *find_connection(session_t *, uint32_t);
int ref_session(session_t *);
void unref_session(session_t *);

/* in iscsi_main.c */

/*void iscsiattach(void *); */
int iscsidetach(device_t, int);

void iscsi_done(ccb_t *);
int map_session(session_t *, device_t);
int unmap_session(session_t *);

/* in iscsi_send.c */

void iscsi_send_thread(void *);

connection_t *assign_connection(session_t *session, bool waitok);
void resend_pdu(ccb_t *);
int send_login(connection_t *);
int send_logout(connection_t *, connection_t *, int, bool);
int send_data_out(connection_t *, pdu_t *, ccb_t *, ccb_disp_t, bool);
void send_run_xfer(session_t *, struct scsipi_xfer *);
int send_send_targets(session_t *, uint8_t *);
int send_task_management(connection_t *, ccb_t *, struct scsipi_xfer *, int);

void negotiate_login(connection_t *, pdu_t *, ccb_t *);
void acknowledge_text(connection_t *, pdu_t *, ccb_t *);
void start_text_negotiation(connection_t *);
void negotiate_text(connection_t *, pdu_t *, ccb_t *);
int send_nop_out(connection_t *, pdu_t *);
void snack_missing(connection_t *, ccb_t *, uint8_t, uint32_t, uint32_t);
void send_snack(connection_t *, pdu_t *, ccb_t *, uint8_t);
int send_send_targets(session_t *, uint8_t *);

void send_command(ccb_t *, ccb_disp_t, bool, bool);
#ifndef ISCSI_MINIMAL
int send_io_command(session_t *, uint64_t, scsireq_t *, bool, uint32_t);
#endif

void connection_timeout_co(void *);
void ccb_timeout_co(void *);

void connection_timeout(connection_t *);
void connection_timeout_start(connection_t *, int);
void connection_timeout_stop(connection_t *);
void ccb_timeout(ccb_t *);
void ccb_timeout_start(ccb_t *, int);
void ccb_timeout_stop(ccb_t *);

/* in iscsi_rcv.c */

void iscsi_rcv_thread(void *);

/* in iscsi_utils.c */

uint32_t gen_digest(const void *, size_t);
uint32_t gen_digest_2(const void *, size_t, const void *, size_t);

void create_ccbs(session_t *);
void destroy_ccbs(session_t *);
ccb_t *get_ccb(connection_t *, bool);
void free_ccb(ccb_t *);
void suspend_ccb(ccb_t *, bool);
void wake_ccb(ccb_t *, uint32_t);

void create_pdus(connection_t *);
pdu_t *get_pdu(connection_t *, bool);
void free_pdu(pdu_t *);

void init_sernum(sernum_buffer_t *);
int add_sernum(sernum_buffer_t *, uint32_t);
uint32_t ack_sernum(sernum_buffer_t *, uint32_t);

uint32_t get_sernum(session_t *, pdu_t *);
int sernum_in_window(session_t *);
int window_size(session_t *, int);

/* in iscsi_text.c */

int assemble_login_parameters(connection_t *, ccb_t *, pdu_t *);
int assemble_security_parameters(connection_t *, ccb_t *, pdu_t *, pdu_t *);
int assemble_negotiation_parameters(connection_t *, ccb_t *, pdu_t *, pdu_t *);
int init_text_parameters(connection_t *, ccb_t *);
int assemble_send_targets(pdu_t *, uint8_t *);
void set_negotiated_parameters(ccb_t *);

#endif /* !_ISCSI_GLOBALS_H */