Training courses

Kernel and Embedded Linux

Bootlin training courses

Embedded Linux, kernel,
Yocto Project, Buildroot, real-time,
graphics, boot time, debugging...

Bootlin logo

Elixir Cross Referencer

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
/*	$NetBSD: iscsi_globals.h,v 1.27 2022/09/13 13:09:16 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 {
	/* first three correspond to CSG/NSG coding */
	ST_SEC_NEG	= 0,	/* security negotiation phase */
	ST_OP_NEG	= 1,	/* operational negotiation phase */
	ST_FULL_FEATURE	= 3,	/* full feature phase */
	/* rest is internal */
	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 */