aboutsummaryrefslogblamecommitdiffstats
path: root/drivers/scsi/dpt_i2o.c
blob: c28e3aea1c3cef83f28e3b3bccb00dac2e8ada27 (plain) (tree)
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
2953
2954
2955
2956
2957
2958
2959
2960
2961
2962
2963
2964
2965
2966
2967
2968
2969
2970
2971
2972
2973
2974
2975
2976
2977
2978
2979
2980
2981
2982
2983
2984
2985
2986
2987
2988
2989
2990
2991
2992
2993
2994
2995
2996
2997
2998
2999
3000
3001
3002
3003
3004
3005
3006
3007
3008
3009
3010
3011
3012
3013
3014
3015
3016
3017
3018
3019
3020
3021
3022
3023
3024
3025
3026
3027
3028
3029
3030
3031
3032
3033
3034
3035
3036
3037
3038
3039
3040
3041
3042
3043
3044
3045
3046
3047
3048
3049
3050
3051
3052
3053
3054
3055
3056
3057
3058
3059
3060
3061
3062
3063
3064
3065
3066
3067
3068
3069
3070
3071
3072
3073
3074
3075
3076
3077
3078
3079
3080
3081
3082
3083
3084
3085
3086
3087
3088
3089
3090
3091
3092
3093
3094
3095
3096
3097
3098
3099
3100
3101
3102
3103
3104
3105
3106
3107
3108
3109
3110
3111
3112
3113
3114
3115
3116
3117
3118
3119
3120
3121
3122
3123
3124
3125
3126
3127
3128
3129
3130
3131
3132
3133
3134
3135
3136
3137
3138
3139
3140
3141
3142
3143
3144
3145
3146
3147
3148
3149
3150
3151
3152
3153
3154
3155
3156
3157
3158
3159
3160
3161
3162
3163
3164
3165
3166
3167
3168
3169
3170
3171
3172
3173
3174
3175
3176
3177
3178
3179
3180
3181
3182
3183
3184
3185
3186
3187
3188
3189
3190
3191
3192
3193
3194
3195
3196
3197
3198
3199
3200
3201
3202
3203
3204
3205
3206
3207
3208
3209
3210
3211
3212
3213
3214
3215
3216
3217
3218
3219
3220
3221
3222
3223
3224
3225
3226
3227
3228
3229
3230
3231
3232
3233
3234
3235
3236
3237
3238
3239
3240
3241
3242
3243
3244
3245
3246
3247
3248
3249
3250
3251
3252
3253
3254
3255
3256
3257
3258
3259
3260
3261
3262
3263
3264
3265
3266
3267
3268
3269
3270
3271
3272
3273
3274
3275
3276
3277
3278
3279
3280
3281
3282
3283
3284
3285
3286
3287
3288
3289
3290
3291
3292
3293
3294
3295
3296
3297
3298
3299
3300
3301
3302
3303
3304
3305
3306
3307
3308
3309
3310
3311
3312
3313
3314
3315
3316
3317
3318
3319
3320
3321
3322
3323
3324
3325
3326
3327
3328
3329
3330
3331
3332
3333
3334
3335
3336
3337
3338
3339
3340
3341
3342
3343
3344



































                                                                                













































































                                                                              













































































































































































































































































                                                                                          


































                                                                                      









































































































































































































































































                                                                                                                                      
                                                      














                                                                                   
                                                                           




























                                                                                                                                              
                                                                








                                                                          
                                              














                                                                                                                                            










                                                      















































































































                                                                                                    




























                                                                                        
                                                   


                                                                             

                                                                     
                                          








                                                                                   
                                                  












                                                     
                                          




                                          








                                                         
                                                 































































                                                                                                  















                                                      
                                        


                                                        



                                  
























                                                                                           
                                                                          


















































                                                                                                        
                                         
 
                       
                               
 
















                                                                 
                                                 




















                                                                       
                                                    














































                                                                                                       
                                                    









































































                                                                                   
                                                    






























                                                                                 
                                                    















                                                                                                  
                                                            




























































































                                                                                                                
                                     

















































































































































































































































































































































































                                                                                                                                        


                              






































































































                                                                            

                                                             































































                                                                                              
                            























                                                                                    
                                       


                                                     
                                          







                                                                                                                
                                                          





                                                      


                                                                              






















































































































































































                                                                                                                                                         
                                                                       


























































































                                                                                                                                                          
                                                                           

















































































































                                                                                                                                          
                                                     

















































































































































































































                                                                                                                                         
                                                    
































                                                                                                 
                                                    




































                                                                                                  
                                                    




                                                                      
                              

                          
                      
 
                                































































                                                                                                          
                                                    

























                                                                         
                                                    


























































































































                                                                                                                
                       



























                                                                                             

                                                                                                 







































































































































































































































































































































































































                                                                                                                                        
/***************************************************************************
                          dpti.c  -  description
                             -------------------
    begin                : Thu Sep 7 2000
    copyright            : (C) 2000 by Adaptec

			   July 30, 2001 First version being submitted
			   for inclusion in the kernel.  V2.4

    See Documentation/scsi/dpti.txt for history, notes, license info
    and credits
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   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 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/
/***************************************************************************
 * Sat Dec 20 2003 Go Taniguchi <go@turbolinux.co.jp>
 - Support 2.6 kernel and DMA-mapping
 - ioctl fix for raid tools
 - use schedule_timeout in long long loop
 **************************************************************************/

/*#define DEBUG 1 */
/*#define UARTDELAY 1 */

/* On the real kernel ADDR32 should always be zero for 2.4. GFP_HIGH allocates
   high pages. Keep the macro around because of the broken unmerged ia64 tree */

#define ADDR32 (0)

#include <linux/module.h>

MODULE_AUTHOR("Deanna Bonds, with _lots_ of help from Mark Salyzyn");
MODULE_DESCRIPTION("Adaptec I2O RAID Driver");

////////////////////////////////////////////////////////////////

#include <linux/ioctl.h>	/* For SCSI-Passthrough */
#include <asm/uaccess.h>

#include <linux/stat.h>
#include <linux/slab.h>		/* for kmalloc() */
#include <linux/config.h>	/* for CONFIG_PCI */
#include <linux/pci.h>		/* for PCI support */
#include <linux/proc_fs.h>
#include <linux/blkdev.h>
#include <linux/delay.h>	/* for udelay */
#include <linux/interrupt.h>
#include <linux/kernel.h>	/* for printk */
#include <linux/sched.h>
#include <linux/reboot.h>
#include <linux/spinlock.h>
#include <linux/smp_lock.h>

#include <linux/timer.h>
#include <linux/string.h>
#include <linux/ioport.h>

#include <asm/processor.h>	/* for boot_cpu_data */
#include <asm/pgtable.h>
#include <asm/io.h>		/* for virt_to_bus, etc. */

#include <scsi/scsi.h>
#include <scsi/scsi_cmnd.h>
#include <scsi/scsi_device.h>
#include <scsi/scsi_host.h>
#include <scsi/scsi_tcq.h>

#include "dpt/dptsig.h"
#include "dpti.h"

/*============================================================================
 * Create a binary signature - this is read by dptsig
 * Needed for our management apps
 *============================================================================
 */
static dpt_sig_S DPTI_sig = {
	{'d', 'P', 't', 'S', 'i', 'G'}, SIG_VERSION,
#ifdef __i386__
	PROC_INTEL, PROC_386 | PROC_486 | PROC_PENTIUM | PROC_SEXIUM,
#elif defined(__ia64__)
	PROC_INTEL, PROC_IA64,
#elif defined(__sparc__)
	PROC_ULTRASPARC, PROC_ULTRASPARC,
#elif defined(__alpha__)
	PROC_ALPHA, PROC_ALPHA,
#else
	(-1),(-1),
#endif
	 FT_HBADRVR, 0, OEM_DPT, OS_LINUX, CAP_OVERLAP, DEV_ALL,
	ADF_ALL_SC5, 0, 0, DPT_VERSION, DPT_REVISION, DPT_SUBREVISION,
	DPT_MONTH, DPT_DAY, DPT_YEAR, "Adaptec Linux I2O RAID Driver"
};




/*============================================================================
 * Globals
 *============================================================================
 */

static DECLARE_MUTEX(adpt_configuration_lock);

static struct i2o_sys_tbl *sys_tbl = NULL;
static int sys_tbl_ind = 0;
static int sys_tbl_len = 0;

static adpt_hba* hba_chain = NULL;
static int hba_count = 0;

static struct file_operations adpt_fops = {
	.ioctl		= adpt_ioctl,
	.open		= adpt_open,
	.release	= adpt_close
};

#ifdef REBOOT_NOTIFIER
static struct notifier_block adpt_reboot_notifier =
{
	 adpt_reboot_event,
	 NULL,
	 0
};
#endif

/* Structures and definitions for synchronous message posting.
 * See adpt_i2o_post_wait() for description
 * */
struct adpt_i2o_post_wait_data
{
	int status;
	u32 id;
	adpt_wait_queue_head_t *wq;
	struct adpt_i2o_post_wait_data *next;
};

static struct adpt_i2o_post_wait_data *adpt_post_wait_queue = NULL;
static u32 adpt_post_wait_id = 0;
static DEFINE_SPINLOCK(adpt_post_wait_lock);


/*============================================================================
 * 				Functions
 *============================================================================
 */

static u8 adpt_read_blink_led(adpt_hba* host)
{
	if(host->FwDebugBLEDflag_P != 0) {
		if( readb(host->FwDebugBLEDflag_P) == 0xbc ){
			return readb(host->FwDebugBLEDvalue_P);
		}
	}
	return 0;
}

/*============================================================================
 * Scsi host template interface functions
 *============================================================================
 */

static struct pci_device_id dptids[] = {
	{ PCI_DPT_VENDOR_ID, PCI_DPT_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID,},
	{ PCI_DPT_VENDOR_ID, PCI_DPT_RAPTOR_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID,},
	{ 0, }
};
MODULE_DEVICE_TABLE(pci,dptids);

static int adpt_detect(struct scsi_host_template* sht)
{
	struct pci_dev *pDev = NULL;
	adpt_hba* pHba;

	adpt_init();

	PINFO("Detecting Adaptec I2O RAID controllers...\n");

        /* search for all Adatpec I2O RAID cards */
	while ((pDev = pci_find_device( PCI_DPT_VENDOR_ID, PCI_ANY_ID, pDev))) {
		if(pDev->device == PCI_DPT_DEVICE_ID ||
		   pDev->device == PCI_DPT_RAPTOR_DEVICE_ID){
			if(adpt_install_hba(sht, pDev) ){
				PERROR("Could not Init an I2O RAID device\n");
				PERROR("Will not try to detect others.\n");
				return hba_count-1;
			}
		}
	}

	/* In INIT state, Activate IOPs */
	for (pHba = hba_chain; pHba; pHba = pHba->next) {
		// Activate does get status , init outbound, and get hrt
		if (adpt_i2o_activate_hba(pHba) < 0) {
			adpt_i2o_delete_hba(pHba);
		}
	}


	/* Active IOPs in HOLD state */

rebuild_sys_tab:
	if (hba_chain == NULL) 
		return 0;

	/*
	 * If build_sys_table fails, we kill everything and bail
	 * as we can't init the IOPs w/o a system table
	 */	
	if (adpt_i2o_build_sys_table() < 0) {
		adpt_i2o_sys_shutdown();
		return 0;
	}

	PDEBUG("HBA's in HOLD state\n");

	/* If IOP don't get online, we need to rebuild the System table */
	for (pHba = hba_chain; pHba; pHba = pHba->next) {
		if (adpt_i2o_online_hba(pHba) < 0) {
			adpt_i2o_delete_hba(pHba);	
			goto rebuild_sys_tab;
		}
	}

	/* Active IOPs now in OPERATIONAL state */
	PDEBUG("HBA's in OPERATIONAL state\n");

	printk("dpti: If you have a lot of devices this could take a few minutes.\n");
	for (pHba = hba_chain; pHba; pHba = pHba->next) {
		printk(KERN_INFO"%s: Reading the hardware resource table.\n", pHba->name);
		if (adpt_i2o_lct_get(pHba) < 0){
			adpt_i2o_delete_hba(pHba);
			continue;
		}

		if (adpt_i2o_parse_lct(pHba) < 0){
			adpt_i2o_delete_hba(pHba);
			continue;
		}
		adpt_inquiry(pHba);
	}

	for (pHba = hba_chain; pHba; pHba = pHba->next) {
		if( adpt_scsi_register(pHba,sht) < 0){
			adpt_i2o_delete_hba(pHba);
			continue;
		}
		pHba->initialized = TRUE;
		pHba->state &= ~DPTI_STATE_RESET;
	}

	// Register our control device node
	// nodes will need to be created in /dev to access this
	// the nodes can not be created from within the driver
	if (hba_count && register_chrdev(DPTI_I2O_MAJOR, DPT_DRIVER, &adpt_fops)) {
		adpt_i2o_sys_shutdown();
		return 0;
	}
	return hba_count;
}


/*
 * scsi_unregister will be called AFTER we return. 
 */
static int adpt_release(struct Scsi_Host *host)
{
	adpt_hba* pHba = (adpt_hba*) host->hostdata[0];
//	adpt_i2o_quiesce_hba(pHba);
	adpt_i2o_delete_hba(pHba);
	scsi_unregister(host);
	return 0;
}


static void adpt_inquiry(adpt_hba* pHba)
{
	u32 msg[14]; 
	u32 *mptr;
	u32 *lenptr;
	int direction;
	int scsidir;
	u32 len;
	u32 reqlen;
	u8* buf;
	u8  scb[16];
	s32 rcode;

	memset(msg, 0, sizeof(msg));
	buf = (u8*)kmalloc(80,GFP_KERNEL|ADDR32);
	if(!buf){
		printk(KERN_ERR"%s: Could not allocate buffer\n",pHba->name);
		return;
	}
	memset((void*)buf, 0, 36);
	
	len = 36;
	direction = 0x00000000;	
	scsidir  =0x40000000;	// DATA IN  (iop<--dev)

	reqlen = 14;		// SINGLE SGE
	/* Stick the headers on */
	msg[0] = reqlen<<16 | SGL_OFFSET_12;
	msg[1] = (0xff<<24|HOST_TID<<12|ADAPTER_TID);
	msg[2] = 0;
	msg[3]  = 0;
	// Adaptec/DPT Private stuff 
	msg[4] = I2O_CMD_SCSI_EXEC|DPT_ORGANIZATION_ID<<16;
	msg[5] = ADAPTER_TID | 1<<16 /* Interpret*/;
	/* Direction, disconnect ok | sense data | simple queue , CDBLen */
	// I2O_SCB_FLAG_ENABLE_DISCONNECT | 
	// I2O_SCB_FLAG_SIMPLE_QUEUE_TAG | 
	// I2O_SCB_FLAG_SENSE_DATA_IN_MESSAGE;
	msg[6] = scsidir|0x20a00000| 6 /* cmd len*/;

	mptr=msg+7;

	memset(scb, 0, sizeof(scb));
	// Write SCSI command into the message - always 16 byte block 
	scb[0] = INQUIRY;
	scb[1] = 0;
	scb[2] = 0;
	scb[3] = 0;
	scb[4] = 36;
	scb[5] = 0;
	// Don't care about the rest of scb

	memcpy(mptr, scb, sizeof(scb));
	mptr+=4;
	lenptr=mptr++;		/* Remember me - fill in when we know */

	/* Now fill in the SGList and command */
	*lenptr = len;
	*mptr++ = 0xD0000000|direction|len;
	*mptr++ = virt_to_bus(buf);

	// Send it on it's way
	rcode = adpt_i2o_post_wait(pHba, msg, reqlen<<2, 120);
	if (rcode != 0) {
		sprintf(pHba->detail, "Adaptec I2O RAID");
		printk(KERN_INFO "%s: Inquiry Error (%d)\n",pHba->name,rcode);
		if (rcode != -ETIME && rcode != -EINTR)
			kfree(buf);
	} else {
		memset(pHba->detail, 0, sizeof(pHba->detail));
		memcpy(&(pHba->detail), "Vendor: Adaptec ", 16);
		memcpy(&(pHba->detail[16]), " Model: ", 8);
		memcpy(&(pHba->detail[24]), (u8*) &buf[16], 16);
		memcpy(&(pHba->detail[40]), " FW: ", 4);
		memcpy(&(pHba->detail[44]), (u8*) &buf[32], 4);
		pHba->detail[48] = '\0';	/* precautionary */
		kfree(buf);
	}
	adpt_i2o_status_get(pHba);
	return ;
}


static int adpt_slave_configure(struct scsi_device * device)
{
	struct Scsi_Host *host = device->host;
	adpt_hba* pHba;

	pHba = (adpt_hba *) host->hostdata[0];

	if (host->can_queue && device->tagged_supported) {
		scsi_adjust_queue_depth(device, MSG_SIMPLE_TAG,
				host->can_queue - 1);
	} else {
		scsi_adjust_queue_depth(device, 0, 1);
	}
	return 0;
}

static int adpt_queue(struct scsi_cmnd * cmd, void (*done) (struct scsi_cmnd *))
{
	adpt_hba* pHba = NULL;
	struct adpt_device* pDev = NULL;	/* dpt per device information */

	cmd->scsi_done = done;
	/*
	 * SCSI REQUEST_SENSE commands will be executed automatically by the 
	 * Host Adapter for any errors, so they should not be executed 
	 * explicitly unless the Sense Data is zero indicating that no error 
	 * occurred.
	 */

	if ((cmd->cmnd[0] == REQUEST_SENSE) && (cmd->sense_buffer[0] != 0)) {
		cmd->result = (DID_OK << 16);
		cmd->scsi_done(cmd);
		return 0;
	}

	pHba = (adpt_hba*)cmd->device->host->hostdata[0];
	if (!pHba) {
		return FAILED;
	}

	rmb();
	/*
	 * TODO: I need to block here if I am processing ioctl cmds
	 * but if the outstanding cmds all finish before the ioctl,
	 * the scsi-core will not know to start sending cmds to me again.
	 * I need to a way to restart the scsi-cores queues or should I block
	 * calling scsi_done on the outstanding cmds instead
	 * for now we don't set the IOCTL state
	 */
	if(((pHba->state) & DPTI_STATE_IOCTL) || ((pHba->state) & DPTI_STATE_RESET)) {
		pHba->host->last_reset = jiffies;
		pHba->host->resetting = 1;
		return 1;
	}

	// TODO if the cmd->device if offline then I may need to issue a bus rescan
	// followed by a get_lct to see if the device is there anymore
	if((pDev = (struct adpt_device*) (cmd->device->hostdata)) == NULL) {
		/*
		 * First command request for this device.  Set up a pointer
		 * to the device structure.  This should be a TEST_UNIT_READY
		 * command from scan_scsis_single.
		 */
		if ((pDev = adpt_find_device(pHba, (u32)cmd->device->channel, (u32)cmd->device->id, (u32)cmd->device->lun)) == NULL) {
			// TODO: if any luns are at this bus, scsi id then fake a TEST_UNIT_READY and INQUIRY response 
			// with type 7F (for all luns less than the max for this bus,id) so the lun scan will continue.
			cmd->result = (DID_NO_CONNECT << 16);
			cmd->scsi_done(cmd);
			return 0;
		}
		cmd->device->hostdata = pDev;
	}
	pDev->pScsi_dev = cmd->device;

	/*
	 * If we are being called from when the device is being reset, 
	 * delay processing of the command until later.
	 */
	if (pDev->state & DPTI_DEV_RESET ) {
		return FAILED;
	}
	return adpt_scsi_to_i2o(pHba, cmd, pDev);
}

static int adpt_bios_param(struct scsi_device *sdev, struct block_device *dev,
		sector_t capacity, int geom[])
{
	int heads=-1;
	int sectors=-1;
	int cylinders=-1;

	// *** First lets set the default geometry ****
	
	// If the capacity is less than ox2000
	if (capacity < 0x2000 ) {	// floppy
		heads = 18;
		sectors = 2;
	} 
	// else if between 0x2000 and 0x20000
	else if (capacity < 0x20000) {
		heads = 64;
		sectors = 32;
	}
	// else if between 0x20000 and 0x40000
	else if (capacity < 0x40000) {
		heads = 65;
		sectors = 63;
	}
	// else if between 0x4000 and 0x80000
	else if (capacity < 0x80000) {
		heads = 128;
		sectors = 63;
	}
	// else if greater than 0x80000
	else {
		heads = 255;
		sectors = 63;
	}
	cylinders = sector_div(capacity, heads * sectors);

	// Special case if CDROM
	if(sdev->type == 5) {  // CDROM
		heads = 252;
		sectors = 63;
		cylinders = 1111;
	}

	geom[0] = heads;
	geom[1] = sectors;
	geom[2] = cylinders;
	
	PDEBUG("adpt_bios_param: exit\n");
	return 0;
}


static const char *adpt_info(struct Scsi_Host *host)
{
	adpt_hba* pHba;

	pHba = (adpt_hba *) host->hostdata[0];
	return (char *) (pHba->detail);
}

static int adpt_proc_info(struct Scsi_Host *host, char *buffer, char **start, off_t offset,
		  int length, int inout)
{
	struct adpt_device* d;
	int id;
	int chan;
	int len = 0;
	int begin = 0;
	int pos = 0;
	adpt_hba* pHba;
	int unit;

	*start = buffer;
	if (inout == TRUE) {
		/*
		 * The user has done a write and wants us to take the
		 * data in the buffer and do something with it.
		 * proc_scsiwrite calls us with inout = 1
		 *
		 * Read data from buffer (writing to us) - NOT SUPPORTED
		 */
		return -EINVAL;
	}

	/*
	 * inout = 0 means the user has done a read and wants information
	 * returned, so we write information about the cards into the buffer
	 * proc_scsiread() calls us with inout = 0
	 */

	// Find HBA (host bus adapter) we are looking for
	down(&adpt_configuration_lock);
	for (pHba = hba_chain; pHba; pHba = pHba->next) {
		if (pHba->host == host) {
			break;	/* found adapter */
		}
	}
	up(&adpt_configuration_lock);
	if (pHba == NULL) {
		return 0;
	}
	host = pHba->host;

	len  = sprintf(buffer    , "Adaptec I2O RAID Driver Version: %s\n\n", DPT_I2O_VERSION);
	len += sprintf(buffer+len, "%s\n", pHba->detail);
	len += sprintf(buffer+len, "SCSI Host=scsi%d  Control Node=/dev/%s  irq=%d\n", 
			pHba->host->host_no, pHba->name, host->irq);
	len += sprintf(buffer+len, "\tpost fifo size  = %d\n\treply fifo size = %d\n\tsg table size   = %d\n\n",
			host->can_queue, (int) pHba->reply_fifo_size , host->sg_tablesize);

	pos = begin + len;

	/* CHECKPOINT */
	if(pos > offset + length) {
		goto stop_output;
	}
	if(pos <= offset) {
		/*
		 * If we haven't even written to where we last left
		 * off (the last time we were called), reset the 
		 * beginning pointer.
		 */
		len = 0;
		begin = pos;
	}
	len +=  sprintf(buffer+len, "Devices:\n");
	for(chan = 0; chan < MAX_CHANNEL; chan++) {
		for(id = 0; id < MAX_ID; id++) {
			d = pHba->channel[chan].device[id];
			while(d){
				len += sprintf(buffer+len,"\t%-24.24s", d->pScsi_dev->vendor);
				len += sprintf(buffer+len," Rev: %-8.8s\n", d->pScsi_dev->rev);
				pos = begin + len;


				/* CHECKPOINT */
				if(pos > offset + length) {
					goto stop_output;
				}
				if(pos <= offset) {
					len = 0;
					begin = pos;
				}

				unit = d->pI2o_dev->lct_data.tid;
				len += sprintf(buffer+len, "\tTID=%d, (Channel=%d, Target=%d, Lun=%d)  (%s)\n\n",
					       unit, (int)d->scsi_channel, (int)d->scsi_id, (int)d->scsi_lun,
					       scsi_device_online(d->pScsi_dev)? "online":"offline"); 
				pos = begin + len;

				/* CHECKPOINT */
				if(pos > offset + length) {
					goto stop_output;
				}
				if(pos <= offset) {
					len = 0;
					begin = pos;
				}

				d = d->next_lun;
			}
		}
	}

	/*
	 * begin is where we last checked our position with regards to offset
	 * begin is always less than offset.  len is relative to begin.  It
	 * is the number of bytes written past begin
	 *
	 */
stop_output:
	/* stop the output and calculate the correct length */
	*(buffer + len) = '\0';

	*start = buffer + (offset - begin);	/* Start of wanted data */
	len -= (offset - begin);
	if(len > length) {
		len = length;
	} else if(len < 0){
		len = 0;
		**start = '\0';
	}
	return len;
}


/*===========================================================================
 * Error Handling routines
 *===========================================================================
 */

static int adpt_abort(struct scsi_cmnd * cmd)
{
	adpt_hba* pHba = NULL;	/* host bus adapter structure */
	struct adpt_device* dptdevice;	/* dpt per device information */
	u32 msg[5];
	int rcode;

	if(cmd->serial_number == 0){
		return FAILED;
	}
	pHba = (adpt_hba*) cmd->device->host->hostdata[0];
	printk(KERN_INFO"%s: Trying to Abort cmd=%ld\n",pHba->name, cmd->serial_number);
	if ((dptdevice = (void*) (cmd->device->hostdata)) == NULL) {
		printk(KERN_ERR "%s: Unable to abort: No device in cmnd\n",pHba->name);
		return FAILED;
	}

	memset(msg, 0, sizeof(msg));
	msg[0] = FIVE_WORD_MSG_SIZE|SGL_OFFSET_0;
	msg[1] = I2O_CMD_SCSI_ABORT<<24|HOST_TID<<12|dptdevice->tid;
	msg[2] = 0;
	msg[3]= 0; 
	msg[4] = (u32)cmd;
	if( (rcode = adpt_i2o_post_wait(pHba, msg, sizeof(msg), FOREVER)) != 0){
		if(rcode == -EOPNOTSUPP ){
			printk(KERN_INFO"%s: Abort cmd not supported\n",pHba->name);
			return FAILED;
		}
		printk(KERN_INFO"%s: Abort cmd=%ld failed.\n",pHba->name, cmd->serial_number);
		return FAILED;
	} 
	printk(KERN_INFO"%s: Abort cmd=%ld complete.\n",pHba->name, cmd->serial_number);
	return SUCCESS;
}


#define I2O_DEVICE_RESET 0x27
// This is the same for BLK and SCSI devices
// NOTE this is wrong in the i2o.h definitions
// This is not currently supported by our adapter but we issue it anyway
static int adpt_device_reset(struct scsi_cmnd* cmd)
{
	adpt_hba* pHba;
	u32 msg[4];
	u32 rcode;
	int old_state;
	struct adpt_device* d = cmd->device->hostdata;

	pHba = (void*) cmd->device->host->hostdata[0];
	printk(KERN_INFO"%s: Trying to reset device\n",pHba->name);
	if (!d) {
		printk(KERN_INFO"%s: Reset Device: Device Not found\n",pHba->name);
		return FAILED;
	}
	memset(msg, 0, sizeof(msg));
	msg[0] = FOUR_WORD_MSG_SIZE|SGL_OFFSET_0;
	msg[1] = (I2O_DEVICE_RESET<<24|HOST_TID<<12|d->tid);
	msg[2] = 0;
	msg[3] = 0;

	old_state = d->state;
	d->state |= DPTI_DEV_RESET;
	if( (rcode = adpt_i2o_post_wait(pHba, msg,sizeof(msg), FOREVER)) ){
		d->state = old_state;
		if(rcode == -EOPNOTSUPP ){
			printk(KERN_INFO"%s: Device reset not supported\n",pHba->name);
			return FAILED;
		}
		printk(KERN_INFO"%s: Device reset failed\n",pHba->name);
		return FAILED;
	} else {
		d->state = old_state;
		printk(KERN_INFO"%s: Device reset successful\n",pHba->name);
		return SUCCESS;
	}
}


#define I2O_HBA_BUS_RESET 0x87
// This version of bus reset is called by the eh_error handler
static int adpt_bus_reset(struct scsi_cmnd* cmd)
{
	adpt_hba* pHba;
	u32 msg[4];

	pHba = (adpt_hba*)cmd->device->host->hostdata[0];
	memset(msg, 0, sizeof(msg));
	printk(KERN_WARNING"%s: Bus reset: SCSI Bus %d: tid: %d\n",pHba->name, cmd->device->channel,pHba->channel[cmd->device->channel].tid );
	msg[0] = FOUR_WORD_MSG_SIZE|SGL_OFFSET_0;
	msg[1] = (I2O_HBA_BUS_RESET<<24|HOST_TID<<12|pHba->channel[cmd->device->channel].tid);
	msg[2] = 0;
	msg[3] = 0;
	if(adpt_i2o_post_wait(pHba, msg,sizeof(msg), FOREVER) ){
		printk(KERN_WARNING"%s: Bus reset failed.\n",pHba->name);
		return FAILED;
	} else {
		printk(KERN_WARNING"%s: Bus reset success.\n",pHba->name);
		return SUCCESS;
	}
}

// This version of reset is called by the eh_error_handler
static int __adpt_reset(struct scsi_cmnd* cmd)
{
	adpt_hba* pHba;
	int rcode;
	pHba = (adpt_hba*)cmd->device->host->hostdata[0];
	printk(KERN_WARNING"%s: Hba Reset: scsi id %d: tid: %d\n",pHba->name,cmd->device->channel,pHba->channel[cmd->device->channel].tid );
	rcode =  adpt_hba_reset(pHba);
	if(rcode == 0){
		printk(KERN_WARNING"%s: HBA reset complete\n",pHba->name);
		return SUCCESS;
	} else {
		printk(KERN_WARNING"%s: HBA reset failed (%x)\n",pHba->name, rcode);
		return FAILED;
	}
}

static int adpt_reset(struct scsi_cmnd* cmd)
{
	int rc;

	spin_lock_irq(cmd->device->host->host_lock);
	rc = __adpt_reset(cmd);
	spin_unlock_irq(cmd->device->host->host_lock);

	return rc;
}

// This version of reset is called by the ioctls and indirectly from eh_error_handler via adpt_reset
static int adpt_hba_reset(adpt_hba* pHba)
{
	int rcode;

	pHba->state |= DPTI_STATE_RESET;

	// Activate does get status , init outbound, and get hrt
	if ((rcode=adpt_i2o_activate_hba(pHba)) < 0) {
		printk(KERN_ERR "%s: Could not activate\n", pHba->name);
		adpt_i2o_delete_hba(pHba);
		return rcode;
	}

	if ((rcode=adpt_i2o_build_sys_table()) < 0) {
		adpt_i2o_delete_hba(pHba);
		return rcode;
	}
	PDEBUG("%s: in HOLD state\n",pHba->name);

	if ((rcode=adpt_i2o_online_hba(pHba)) < 0) {
		adpt_i2o_delete_hba(pHba);	
		return rcode;
	}
	PDEBUG("%s: in OPERATIONAL state\n",pHba->name);

	if ((rcode=adpt_i2o_lct_get(pHba)) < 0){
		adpt_i2o_delete_hba(pHba);
		return rcode;
	}

	if ((rcode=adpt_i2o_reparse_lct(pHba)) < 0){
		adpt_i2o_delete_hba(pHba);
		return rcode;
	}
	pHba->state &= ~DPTI_STATE_RESET;

	adpt_fail_posted_scbs(pHba);
	return 0;	/* return success */
}

/*===========================================================================
 * 
 *===========================================================================
 */


static void adpt_i2o_sys_shutdown(void)
{
	adpt_hba *pHba, *pNext;
	struct adpt_i2o_post_wait_data *p1, *p2;

	 printk(KERN_INFO"Shutting down Adaptec I2O controllers.\n");
	 printk(KERN_INFO"   This could take a few minutes if there are many devices attached\n");
	/* Delete all IOPs from the controller chain */
	/* They should have already been released by the
	 * scsi-core
	 */
	for (pHba = hba_chain; pHba; pHba = pNext) {
		pNext = pHba->next;
		adpt_i2o_delete_hba(pHba);
	}

	/* Remove any timedout entries from the wait queue.  */
	p2 = NULL;
//	spin_lock_irqsave(&adpt_post_wait_lock, flags);
	/* Nothing should be outstanding at this point so just
	 * free them 
	 */
	for(p1 = adpt_post_wait_queue; p1; p2 = p1, p1 = p2->next) {
		kfree(p1);
	}
//	spin_unlock_irqrestore(&adpt_post_wait_lock, flags);
	adpt_post_wait_queue = NULL;

	 printk(KERN_INFO "Adaptec I2O controllers down.\n");
}

/*
 * reboot/shutdown notification.
 *
 * - Quiesce each IOP in the system
 *
 */

#ifdef REBOOT_NOTIFIER
static int adpt_reboot_event(struct notifier_block *n, ulong code, void *p)
{

	 if(code != SYS_RESTART && code != SYS_HALT && code != SYS_POWER_OFF)
		  return NOTIFY_DONE;

	 adpt_i2o_sys_shutdown();

	 return NOTIFY_DONE;
}
#endif


static int adpt_install_hba(struct scsi_host_template* sht, struct pci_dev* pDev) 
{

	adpt_hba* pHba = NULL;
	adpt_hba* p = NULL;
	ulong base_addr0_phys = 0;
	ulong base_addr1_phys = 0;
	u32 hba_map0_area_size = 0;
	u32 hba_map1_area_size = 0;
	void __iomem *base_addr_virt = NULL;
	void __iomem *msg_addr_virt = NULL;

	int raptorFlag = FALSE;

	if(pci_enable_device(pDev)) {
		return -EINVAL;
	}
	pci_set_master(pDev);
	if (pci_set_dma_mask(pDev, 0xffffffffffffffffULL) &&
	    pci_set_dma_mask(pDev, 0xffffffffULL))
		return -EINVAL;

	base_addr0_phys = pci_resource_start(pDev,0);
	hba_map0_area_size = pci_resource_len(pDev,0);

	// Check if standard PCI card or single BAR Raptor
	if(pDev->device == PCI_DPT_DEVICE_ID){
		if(pDev->subsystem_device >=0xc032 && pDev->subsystem_device <= 0xc03b){
			// Raptor card with this device id needs 4M
			hba_map0_area_size = 0x400000;
		} else { // Not Raptor - it is a PCI card
			if(hba_map0_area_size > 0x100000 ){ 
				hba_map0_area_size = 0x100000;
			}
		}
	} else {// Raptor split BAR config
		// Use BAR1 in this configuration
		base_addr1_phys = pci_resource_start(pDev,1);
		hba_map1_area_size = pci_resource_len(pDev,1);
		raptorFlag = TRUE;
	}

	if (pci_request_regions(pDev, "dpt_i2o")) {
		PERROR("dpti: adpt_config_hba: pci request region failed\n");
		return -EINVAL;
	}
	base_addr_virt = ioremap(base_addr0_phys,hba_map0_area_size);
	if (!base_addr_virt) {
		pci_release_regions(pDev);
		PERROR("dpti: adpt_config_hba: io remap failed\n");
		return -EINVAL;
	}

        if(raptorFlag == TRUE) {
		msg_addr_virt = ioremap(base_addr1_phys, hba_map1_area_size );
		if (!msg_addr_virt) {
			PERROR("dpti: adpt_config_hba: io remap failed on BAR1\n");
			iounmap(base_addr_virt);
			pci_release_regions(pDev);
			return -EINVAL;
		}
	} else {
		msg_addr_virt = base_addr_virt;
	}
	
	// Allocate and zero the data structure
	pHba = kmalloc(sizeof(adpt_hba), GFP_KERNEL);
	if( pHba == NULL) {
		if(msg_addr_virt != base_addr_virt){
			iounmap(msg_addr_virt);
		}
		iounmap(base_addr_virt);
		pci_release_regions(pDev);
		return -ENOMEM;
	}
	memset(pHba, 0, sizeof(adpt_hba));

	down(&adpt_configuration_lock);

	if(hba_chain != NULL){
		for(p = hba_chain; p->next; p = p->next);
		p->next = pHba;
	} else {
		hba_chain = pHba;
	}
	pHba->next = NULL;
	pHba->unit = hba_count;
	sprintf(pHba->name, "dpti%d", hba_count);
	hba_count++;
	
	up(&adpt_configuration_lock);

	pHba->pDev = pDev;
	pHba->base_addr_phys = base_addr0_phys;

	// Set up the Virtual Base Address of the I2O Device
	pHba->base_addr_virt = base_addr_virt;
	pHba->msg_addr_virt = msg_addr_virt;
	pHba->irq_mask = base_addr_virt+0x30;
	pHba->post_port = base_addr_virt+0x40;
	pHba->reply_port = base_addr_virt+0x44;

	pHba->hrt = NULL;
	pHba->lct = NULL;
	pHba->lct_size = 0;
	pHba->status_block = NULL;
	pHba->post_count = 0;
	pHba->state = DPTI_STATE_RESET;
	pHba->pDev = pDev;
	pHba->devices = NULL;

	// Initializing the spinlocks
	spin_lock_init(&pHba->state_lock);
	spin_lock_init(&adpt_post_wait_lock);

	if(raptorFlag == 0){
		printk(KERN_INFO"Adaptec I2O RAID controller %d at %p size=%x irq=%d\n", 
			hba_count-1, base_addr_virt, hba_map0_area_size, pDev->irq);
	} else {
		printk(KERN_INFO"Adaptec I2O RAID controller %d irq=%d\n",hba_count-1, pDev->irq);
		printk(KERN_INFO"     BAR0 %p - size= %x\n",base_addr_virt,hba_map0_area_size);
		printk(KERN_INFO"     BAR1 %p - size= %x\n",msg_addr_virt,hba_map1_area_size);
	}

	if (request_irq (pDev->irq, adpt_isr, SA_SHIRQ, pHba->name, pHba)) {
		printk(KERN_ERR"%s: Couldn't register IRQ %d\n", pHba->name, pDev->irq);
		adpt_i2o_delete_hba(pHba);
		return -EINVAL;
	}

	return 0;
}


static void adpt_i2o_delete_hba(adpt_hba* pHba)
{
	adpt_hba* p1;
	adpt_hba* p2;
	struct i2o_device* d;
	struct i2o_device* next;
	int i;
	int j;
	struct adpt_device* pDev;
	struct adpt_device* pNext;


	down(&adpt_configuration_lock);
	// scsi_unregister calls our adpt_release which
	// does a quiese
	if(pHba->host){
		free_irq(pHba->host->irq, pHba);
	}
	p2 = NULL;
	for( p1 = hba_chain; p1; p2 = p1,p1=p1->next){
		if(p1 == pHba) {
			if(p2) {
				p2->next = p1->next;
			} else {
				hba_chain = p1->next;
			}
			break;
		}
	}

	hba_count--;
	up(&adpt_configuration_lock);

	iounmap(pHba->base_addr_virt);
	pci_release_regions(pHba->pDev);
	if(pHba->msg_addr_virt != pHba->base_addr_virt){
		iounmap(pHba->msg_addr_virt);
	}
	kfree(pHba->hrt);
	kfree(pHba->lct);
	kfree(pHba->status_block);
	kfree(pHba->reply_pool);

	for(d = pHba->devices; d ; d = next){
		next = d->next;
		kfree(d);
	}
	for(i = 0 ; i < pHba->top_scsi_channel ; i++){
		for(j = 0; j < MAX_ID; j++){
			if(pHba->channel[i].device[j] != NULL){
				for(pDev = pHba->channel[i].device[j]; pDev; pDev = pNext){
					pNext = pDev->next_lun;
					kfree(pDev);
				}
			}
		}
	}
	kfree(pHba);

	if(hba_count <= 0){
		unregister_chrdev(DPTI_I2O_MAJOR, DPT_DRIVER);   
	}
}


static int adpt_init(void)
{
	printk("Loading Adaptec I2O RAID: Version " DPT_I2O_VERSION "\n");
#ifdef REBOOT_NOTIFIER
	register_reboot_notifier(&adpt_reboot_notifier);
#endif

	return 0;
}


static struct adpt_device* adpt_find_device(adpt_hba* pHba, u32 chan, u32 id, u32 lun)
{
	struct adpt_device* d;

	if(chan < 0 || chan >= MAX_CHANNEL)
		return NULL;
	
	if( pHba->channel[chan].device == NULL){
		printk(KERN_DEBUG"Adaptec I2O RAID: Trying to find device before they are allocated\n");
		return NULL;
	}

	d = pHba->channel[chan].device[id];
	if(!d || d->tid == 0) {
		return NULL;
	}

	/* If it is the only lun at that address then this should match*/
	if(d->scsi_lun == lun){
		return d;
	}

	/* else we need to look through all the luns */
	for(d=d->next_lun ; d ; d = d->next_lun){
		if(d->scsi_lun == lun){
			return d;
		}
	}
	return NULL;
}


static int adpt_i2o_post_wait(adpt_hba* pHba, u32* msg, int len, int timeout)
{
	// I used my own version of the WAIT_QUEUE_HEAD
	// to handle some version differences
	// When embedded in the kernel this could go back to the vanilla one
	ADPT_DECLARE_WAIT_QUEUE_HEAD(adpt_wq_i2o_post);
	int status = 0;
	ulong flags = 0;
	struct adpt_i2o_post_wait_data *p1, *p2;
	struct adpt_i2o_post_wait_data *wait_data =
		kmalloc(sizeof(struct adpt_i2o_post_wait_data),GFP_KERNEL);
	DECLARE_WAITQUEUE(wait, current);

	if (!wait_data)
		return -ENOMEM;

	/*
	 * The spin locking is needed to keep anyone from playing
	 * with the queue pointers and id while we do the same
	 */
	spin_lock_irqsave(&adpt_post_wait_lock, flags);
       // TODO we need a MORE unique way of getting ids
       // to support async LCT get
	wait_data->next = adpt_post_wait_queue;
	adpt_post_wait_queue = wait_data;
	adpt_post_wait_id++;
	adpt_post_wait_id &= 0x7fff;
	wait_data->id =  adpt_post_wait_id;
	spin_unlock_irqrestore(&adpt_post_wait_lock, flags);

	wait_data->wq = &adpt_wq_i2o_post;
	wait_data->status = -ETIMEDOUT;

	add_wait_queue(&adpt_wq_i2o_post, &wait);

	msg[2] |= 0x80000000 | ((u32)wait_data->id);
	timeout *= HZ;
	if((status = adpt_i2o_post_this(pHba, msg, len)) == 0){
		set_current_state(TASK_INTERRUPTIBLE);
		if(pHba->host)
			spin_unlock_irq(pHba->host->host_lock);
		if (!timeout)
			schedule();
		else{
			timeout = schedule_timeout(timeout);
			if (timeout == 0) {
				// I/O issued, but cannot get result in
				// specified time. Freeing resorces is
				// dangerous.
				status = -ETIME;
			}
		}
		if(pHba->host)
			spin_lock_irq(pHba->host->host_lock);
	}
	remove_wait_queue(&adpt_wq_i2o_post, &wait);

	if(status == -ETIMEDOUT){
		printk(KERN_INFO"dpti%d: POST WAIT TIMEOUT\n",pHba->unit);
		// We will have to free the wait_data memory during shutdown
		return status;
	}

	/* Remove the entry from the queue.  */
	p2 = NULL;
	spin_lock_irqsave(&adpt_post_wait_lock, flags);
	for(p1 = adpt_post_wait_queue; p1; p2 = p1, p1 = p1->next) {
		if(p1 == wait_data) {
			if(p1->status == I2O_DETAIL_STATUS_UNSUPPORTED_FUNCTION ) {
				status = -EOPNOTSUPP;
			}
			if(p2) {
				p2->next = p1->next;
			} else {
				adpt_post_wait_queue = p1->next;
			}
			break;
		}
	}
	spin_unlock_irqrestore(&adpt_post_wait_lock, flags);

	kfree(wait_data);

	return status;
}


static s32 adpt_i2o_post_this(adpt_hba* pHba, u32* data, int len)
{

	u32 m = EMPTY_QUEUE;
	u32 __iomem *msg;
	ulong timeout = jiffies + 30*HZ;
	do {
		rmb();
		m = readl(pHba->post_port);
		if (m != EMPTY_QUEUE) {
			break;
		}
		if(time_after(jiffies,timeout)){
			printk(KERN_WARNING"dpti%d: Timeout waiting for message frame!\n", pHba->unit);
			return -ETIMEDOUT;
		}
		schedule_timeout_uninterruptible(1);
	} while(m == EMPTY_QUEUE);
		
	msg = pHba->msg_addr_virt + m;
	memcpy_toio(msg, data, len);
	wmb();

	//post message
	writel(m, pHba->post_port);
	wmb();

	return 0;
}


static void adpt_i2o_post_wait_complete(u32 context, int status)
{
	struct adpt_i2o_post_wait_data *p1 = NULL;
	/*
	 * We need to search through the adpt_post_wait
	 * queue to see if the given message is still
	 * outstanding.  If not, it means that the IOP
	 * took longer to respond to the message than we
	 * had allowed and timer has already expired.
	 * Not much we can do about that except log
	 * it for debug purposes, increase timeout, and recompile
	 *
	 * Lock needed to keep anyone from moving queue pointers
	 * around while we're looking through them.
	 */

	context &= 0x7fff;

	spin_lock(&adpt_post_wait_lock);
	for(p1 = adpt_post_wait_queue; p1; p1 = p1->next) {
		if(p1->id == context) {
			p1->status = status;
			spin_unlock(&adpt_post_wait_lock);
			wake_up_interruptible(p1->wq);
			return;
		}
	}
	spin_unlock(&adpt_post_wait_lock);
        // If this happens we lose commands that probably really completed
	printk(KERN_DEBUG"dpti: Could Not find task %d in wait queue\n",context);
	printk(KERN_DEBUG"      Tasks in wait queue:\n");
	for(p1 = adpt_post_wait_queue; p1; p1 = p1->next) {
		printk(KERN_DEBUG"           %d\n",p1->id);
	}
	return;
}

static s32 adpt_i2o_reset_hba(adpt_hba* pHba)			
{
	u32 msg[8];
	u8* status;
	u32 m = EMPTY_QUEUE ;
	ulong timeout = jiffies + (TMOUT_IOPRESET*HZ);

	if(pHba->initialized  == FALSE) {	// First time reset should be quick
		timeout = jiffies + (25*HZ);
	} else {
		adpt_i2o_quiesce_hba(pHba);
	}

	do {
		rmb();
		m = readl(pHba->post_port);
		if (m != EMPTY_QUEUE) {
			break;
		}
		if(time_after(jiffies,timeout)){
			printk(KERN_WARNING"Timeout waiting for message!\n");
			return -ETIMEDOUT;
		}
		schedule_timeout_uninterruptible(1);
	} while (m == EMPTY_QUEUE);

	status = (u8*)kmalloc(4, GFP_KERNEL|ADDR32);
	if(status == NULL) {
		adpt_send_nop(pHba, m);
		printk(KERN_ERR"IOP reset failed - no free memory.\n");
		return -ENOMEM;
	}
	memset(status,0,4);

	msg[0]=EIGHT_WORD_MSG_SIZE|SGL_OFFSET_0;
	msg[1]=I2O_CMD_ADAPTER_RESET<<24|HOST_TID<<12|ADAPTER_TID;
	msg[2]=0;
	msg[3]=0;
	msg[4]=0;
	msg[5]=0;
	msg[6]=virt_to_bus(status);
	msg[7]=0;     

	memcpy_toio(pHba->msg_addr_virt+m, msg, sizeof(msg));
	wmb();
	writel(m, pHba->post_port);
	wmb();

	while(*status == 0){
		if(time_after(jiffies,timeout)){
			printk(KERN_WARNING"%s: IOP Reset Timeout\n",pHba->name);
			kfree(status);
			return -ETIMEDOUT;
		}
		rmb();
		schedule_timeout_uninterruptible(1);
	}

	if(*status == 0x01 /*I2O_EXEC_IOP_RESET_IN_PROGRESS*/) {
		PDEBUG("%s: Reset in progress...\n", pHba->name);
		// Here we wait for message frame to become available
		// indicated that reset has finished
		do {
			rmb();
			m = readl(pHba->post_port);
			if (m != EMPTY_QUEUE) {
				break;
			}
			if(time_after(jiffies,timeout)){
				printk(KERN_ERR "%s:Timeout waiting for IOP Reset.\n",pHba->name);
				return -ETIMEDOUT;
			}
			schedule_timeout_uninterruptible(1);
		} while (m == EMPTY_QUEUE);
		// Flush the offset
		adpt_send_nop(pHba, m);
	}
	adpt_i2o_status_get(pHba);
	if(*status == 0x02 ||
			pHba->status_block->iop_state != ADAPTER_STATE_RESET) {
		printk(KERN_WARNING"%s: Reset reject, trying to clear\n",
				pHba->name);
	} else {
		PDEBUG("%s: Reset completed.\n", pHba->name);
	}

	kfree(status);
#ifdef UARTDELAY
	// This delay is to allow someone attached to the card through the debug UART to 
	// set up the dump levels that they want before the rest of the initialization sequence
	adpt_delay(20000);
#endif
	return 0;
}


static int adpt_i2o_parse_lct(adpt_hba* pHba)
{
	int i;
	int max;
	int tid;
	struct i2o_device *d;
	i2o_lct *lct = pHba->lct;
	u8 bus_no = 0;
	s16 scsi_id;
	s16 scsi_lun;
	u32 buf[10]; // larger than 7, or 8 ...
	struct adpt_device* pDev; 
	
	if (lct == NULL) {
		printk(KERN_ERR "%s: LCT is empty???\n",pHba->name);
		return -1;
	}
	
	max = lct->table_size;	
	max -= 3;
	max /= 9;

	for(i=0;i<max;i++) {
		if( lct->lct_entry[i].user_tid != 0xfff){
			/*
			 * If we have hidden devices, we need to inform the upper layers about
			 * the possible maximum id reference to handle device access when
			 * an array is disassembled. This code has no other purpose but to
			 * allow us future access to devices that are currently hidden
			 * behind arrays, hotspares or have not been configured (JBOD mode).
			 */
			if( lct->lct_entry[i].class_id != I2O_CLASS_RANDOM_BLOCK_STORAGE &&
			    lct->lct_entry[i].class_id != I2O_CLASS_SCSI_PERIPHERAL &&
			    lct->lct_entry[i].class_id != I2O_CLASS_FIBRE_CHANNEL_PERIPHERAL ){
			    	continue;
			}
			tid = lct->lct_entry[i].tid;
			// I2O_DPT_DEVICE_INFO_GROUP_NO;
			if(adpt_i2o_query_scalar(pHba, tid, 0x8000, -1, buf, 32)<0) {
				continue;
			}
			bus_no = buf[0]>>16;
			scsi_id = buf[1];
			scsi_lun = (buf[2]>>8 )&0xff;
			if(bus_no >= MAX_CHANNEL) {	// Something wrong skip it
				printk(KERN_WARNING"%s: Channel number %d out of range \n", pHba->name, bus_no);
				continue;
			}
			if (scsi_id >= MAX_ID){
				printk(KERN_WARNING"%s: SCSI ID %d out of range \n", pHba->name, bus_no);
				continue;
			}
			if(bus_no > pHba->top_scsi_channel){
				pHba->top_scsi_channel = bus_no;
			}
			if(scsi_id > pHba->top_scsi_id){
				pHba->top_scsi_id = scsi_id;
			}
			if(scsi_lun > pHba->top_scsi_lun){
				pHba->top_scsi_lun = scsi_lun;
			}
			continue;
		}
		d = (struct i2o_device *)kmalloc(sizeof(struct i2o_device), GFP_KERNEL);
		if(d==NULL)
		{
			printk(KERN_CRIT"%s: Out of memory for I2O device data.\n",pHba->name);
			return -ENOMEM;
		}
		
		d->controller = pHba;
		d->next = NULL;

		memcpy(&d->lct_data, &lct->lct_entry[i], sizeof(i2o_lct_entry));

		d->flags = 0;
		tid = d->lct_data.tid;
		adpt_i2o_report_hba_unit(pHba, d);
		adpt_i2o_install_device(pHba, d);
	}
	bus_no = 0;
	for(d = pHba->devices; d ; d = d->next) {
		if(d->lct_data.class_id  == I2O_CLASS_BUS_ADAPTER_PORT ||
		   d->lct_data.class_id  == I2O_CLASS_FIBRE_CHANNEL_PORT){
			tid = d->lct_data.tid;
			// TODO get the bus_no from hrt-but for now they are in order
			//bus_no = 
			if(bus_no > pHba->top_scsi_channel){
				pHba->top_scsi_channel = bus_no;
			}
			pHba->channel[bus_no].type = d->lct_data.class_id;
			pHba->channel[bus_no].tid = tid;
			if(adpt_i2o_query_scalar(pHba, tid, 0x0200, -1, buf, 28)>=0)
			{
				pHba->channel[bus_no].scsi_id = buf[1];
				PDEBUG("Bus %d - SCSI ID %d.\n", bus_no, buf[1]);
			}
			// TODO remove - this is just until we get from hrt
			bus_no++;
			if(bus_no >= MAX_CHANNEL) {	// Something wrong skip it
				printk(KERN_WARNING"%s: Channel number %d out of range - LCT\n", pHba->name, bus_no);
				break;
			}
		}
	}

	// Setup adpt_device table
	for(d = pHba->devices; d ; d = d->next) {
		if(d->lct_data.class_id  == I2O_CLASS_RANDOM_BLOCK_STORAGE ||
		   d->lct_data.class_id  == I2O_CLASS_SCSI_PERIPHERAL ||
		   d->lct_data.class_id  == I2O_CLASS_FIBRE_CHANNEL_PERIPHERAL ){

			tid = d->lct_data.tid;
			scsi_id = -1;
			// I2O_DPT_DEVICE_INFO_GROUP_NO;
			if(adpt_i2o_query_scalar(pHba, tid, 0x8000, -1, buf, 32)>=0) {
				bus_no = buf[0]>>16;
				scsi_id = buf[1];
				scsi_lun = (buf[2]>>8 )&0xff;
				if(bus_no >= MAX_CHANNEL) {	// Something wrong skip it
					continue;
				}
				if (scsi_id >= MAX_ID) {
					continue;
				}
				if( pHba->channel[bus_no].device[scsi_id] == NULL){
					pDev =  kmalloc(sizeof(struct adpt_device),GFP_KERNEL);
					if(pDev == NULL) {
						return -ENOMEM;
					}
					pHba->channel[bus_no].device[scsi_id] = pDev;
					memset(pDev,0,sizeof(struct adpt_device));
				} else {
					for( pDev = pHba->channel[bus_no].device[scsi_id];	
							pDev->next_lun; pDev = pDev->next_lun){
					}
					pDev->next_lun = kmalloc(sizeof(struct adpt_device),GFP_KERNEL);
					if(pDev->next_lun == NULL) {
						return -ENOMEM;
					}
					memset(pDev->next_lun,0,sizeof(struct adpt_device));
					pDev = pDev->next_lun;
				}
				pDev->tid = tid;
				pDev->scsi_channel = bus_no;
				pDev->scsi_id = scsi_id;
				pDev->scsi_lun = scsi_lun;
				pDev->pI2o_dev = d;
				d->owner = pDev;
				pDev->type = (buf[0])&0xff;
				pDev->flags = (buf[0]>>8)&0xff;
				if(scsi_id > pHba->top_scsi_id){
					pHba->top_scsi_id = scsi_id;
				}
				if(scsi_lun > pHba->top_scsi_lun){
					pHba->top_scsi_lun = scsi_lun;
				}
			}
			if(scsi_id == -1){
				printk(KERN_WARNING"Could not find SCSI ID for %s\n",
						d->lct_data.identity_tag);
			}
		}
	}
	return 0;
}


/*
 *	Each I2O controller has a chain of devices on it - these match
 *	the useful parts of the LCT of the board.
 */
 
static int adpt_i2o_install_device(adpt_hba* pHba, struct i2o_device *d)
{
	down(&adpt_configuration_lock);
	d->controller=pHba;
	d->owner=NULL;
	d->next=pHba->devices;
	d->prev=NULL;
	if (pHba->devices != NULL){
		pHba->devices->prev=d;
	}
	pHba->devices=d;
	*d->dev_name = 0;

	up(&adpt_configuration_lock);
	return 0;
}

static int adpt_open(struct inode *inode, struct file *file)
{
	int minor;
	adpt_hba* pHba;

	//TODO check for root access
	//
	minor = iminor(inode);
	if (minor >= hba_count) {
		return -ENXIO;
	}
	down(&adpt_configuration_lock);
	for (pHba = hba_chain; pHba; pHba = pHba->next) {
		if (pHba->unit == minor) {
			break;	/* found adapter */
		}
	}
	if (pHba == NULL) {
		up(&adpt_configuration_lock);
		return -ENXIO;
	}

//	if(pHba->in_use){
	//	up(&adpt_configuration_lock);
//		return -EBUSY;
//	}

	pHba->in_use = 1;
	up(&adpt_configuration_lock);

	return 0;
}

static int adpt_close(struct inode *inode, struct file *file)
{
	int minor;
	adpt_hba* pHba;

	minor = iminor(inode);
	if (minor >= hba_count) {
		return -ENXIO;
	}
	down(&adpt_configuration_lock);
	for (pHba = hba_chain; pHba; pHba = pHba->next) {
		if (pHba->unit == minor) {
			break;	/* found adapter */
		}
	}
	up(&adpt_configuration_lock);
	if (pHba == NULL) {
		return -ENXIO;
	}

	pHba->in_use = 0;

	return 0;
}


static int adpt_i2o_passthru(adpt_hba* pHba, u32 __user *arg)
{
	u32 msg[MAX_MESSAGE_SIZE];
	u32* reply = NULL;
	u32 size = 0;
	u32 reply_size = 0;
	u32 __user *user_msg = arg;
	u32 __user * user_reply = NULL;
	void *sg_list[pHba->sg_tablesize];
	u32 sg_offset = 0;
	u32 sg_count = 0;
	int sg_index = 0;
	u32 i = 0;
	u32 rcode = 0;
	void *p = NULL;
	ulong flags = 0;

	memset(&msg, 0, MAX_MESSAGE_SIZE*4);
	// get user msg size in u32s 
	if(get_user(size, &user_msg[0])){
		return -EFAULT;
	}
	size = size>>16;

	user_reply = &user_msg[size];
	if(size > MAX_MESSAGE_SIZE){
		return -EFAULT;
	}
	size *= 4; // Convert to bytes

	/* Copy in the user's I2O command */
	if(copy_from_user(msg, user_msg, size)) {
		return -EFAULT;
	}
	get_user(reply_size, &user_reply[0]);
	reply_size = reply_size>>16;
	if(reply_size > REPLY_FRAME_SIZE){
		reply_size = REPLY_FRAME_SIZE;
	}
	reply_size *= 4;
	reply = kmalloc(REPLY_FRAME_SIZE*4, GFP_KERNEL);
	if(reply == NULL) {
		printk(KERN_WARNING"%s: Could not allocate reply buffer\n",pHba->name);
		return -ENOMEM;
	}
	memset(reply,0,REPLY_FRAME_SIZE*4);
	sg_offset = (msg[0]>>4)&0xf;
	msg[2] = 0x40000000; // IOCTL context
	msg[3] = (u32)reply;
	memset(sg_list,0, sizeof(sg_list[0])*pHba->sg_tablesize);
	if(sg_offset) {
		// TODO 64bit fix
		struct sg_simple_element *sg =  (struct sg_simple_element*) (msg+sg_offset);
		sg_count = (size - sg_offset*4) / sizeof(struct sg_simple_element);
		if (sg_count > pHba->sg_tablesize){
			printk(KERN_DEBUG"%s:IOCTL SG List too large (%u)\n", pHba->name,sg_count);
			kfree (reply);
			return -EINVAL;
		}

		for(i = 0; i < sg_count; i++) {
			int sg_size;

			if (!(sg[i].flag_count & 0x10000000 /*I2O_SGL_FLAGS_SIMPLE_ADDRESS_ELEMENT*/)) {
				printk(KERN_DEBUG"%s:Bad SG element %d - not simple (%x)\n",pHba->name,i,  sg[i].flag_count);
				rcode = -EINVAL;
				goto cleanup;
			}
			sg_size = sg[i].flag_count & 0xffffff;      
			/* Allocate memory for the transfer */
			p = kmalloc(sg_size, GFP_KERNEL|ADDR32);
			if(!p) {
				printk(KERN_DEBUG"%s: Could not allocate SG buffer - size = %d buffer number %d of %d\n",
						pHba->name,sg_size,i,sg_count);
				rcode = -ENOMEM;
				goto cleanup;
			}
			sg_list[sg_index++] = p; // sglist indexed with input frame, not our internal frame.
			/* Copy in the user's SG buffer if necessary */
			if(sg[i].flag_count & 0x04000000 /*I2O_SGL_FLAGS_DIR*/) {
				// TODO 64bit fix
				if (copy_from_user(p,(void __user *)sg[i].addr_bus, sg_size)) {
					printk(KERN_DEBUG"%s: Could not copy SG buf %d FROM user\n",pHba->name,i);
					rcode = -EFAULT;
					goto cleanup;
				}
			}
			//TODO 64bit fix
			sg[i].addr_bus = (u32)virt_to_bus(p);
		}
	}

	do {
		if(pHba->host)
			spin_lock_irqsave(pHba->host->host_lock, flags);
		// This state stops any new commands from enterring the
		// controller while processing the ioctl
//		pHba->state |= DPTI_STATE_IOCTL;
//		We can't set this now - The scsi subsystem sets host_blocked and
//		the queue empties and stops.  We need a way to restart the queue
		rcode = adpt_i2o_post_wait(pHba, msg, size, FOREVER);
		if (rcode != 0)
			printk("adpt_i2o_passthru: post wait failed %d %p\n",
					rcode, reply);
//		pHba->state &= ~DPTI_STATE_IOCTL;
		if(pHba->host)
			spin_unlock_irqrestore(pHba->host->host_lock, flags);
	} while(rcode == -ETIMEDOUT);  

	if(rcode){
		goto cleanup;
	}

	if(sg_offset) {
	/* Copy back the Scatter Gather buffers back to user space */
		u32 j;
		// TODO 64bit fix
		struct sg_simple_element* sg;
		int sg_size;

		// re-acquire the original message to handle correctly the sg copy operation
		memset(&msg, 0, MAX_MESSAGE_SIZE*4); 
		// get user msg size in u32s 
		if(get_user(size, &user_msg[0])){
			rcode = -EFAULT; 
			goto cleanup; 
		}
		size = size>>16;
		size *= 4;
		/* Copy in the user's I2O command */
		if (copy_from_user (msg, user_msg, size)) {
			rcode = -EFAULT;
			goto cleanup;
		}
		sg_count = (size - sg_offset*4) / sizeof(struct sg_simple_element);

		// TODO 64bit fix
		sg 	 = (struct sg_simple_element*)(msg + sg_offset);
		for (j = 0; j < sg_count; j++) {
			/* Copy out the SG list to user's buffer if necessary */
			if(! (sg[j].flag_count & 0x4000000 /*I2O_SGL_FLAGS_DIR*/)) {
				sg_size = sg[j].flag_count & 0xffffff; 
				// TODO 64bit fix
				if (copy_to_user((void __user *)sg[j].addr_bus,sg_list[j], sg_size)) {
					printk(KERN_WARNING"%s: Could not copy %p TO user %x\n",pHba->name, sg_list[j], sg[j].addr_bus);
					rcode = -EFAULT;
					goto cleanup;
				}
			}
		}
	} 

	/* Copy back the reply to user space */
	if (reply_size) {
		// we wrote our own values for context - now restore the user supplied ones
		if(copy_from_user(reply+2, user_msg+2, sizeof(u32)*2)) {
			printk(KERN_WARNING"%s: Could not copy message context FROM user\n",pHba->name);
			rcode = -EFAULT;
		}
		if(copy_to_user(user_reply, reply, reply_size)) {
			printk(KERN_WARNING"%s: Could not copy reply TO user\n",pHba->name);
			rcode = -EFAULT;
		}
	}


cleanup:
	if (rcode != -ETIME && rcode != -EINTR)
		kfree (reply);
	while(sg_index) {
		if(sg_list[--sg_index]) {
			if (rcode != -ETIME && rcode != -EINTR)
				kfree(sg_list[sg_index]);
		}
	}
	return rcode;
}


/*
 * This routine returns information about the system.  This does not effect
 * any logic and if the info is wrong - it doesn't matter.
 */

/* Get all the info we can not get from kernel services */
static int adpt_system_info(void __user *buffer)
{
	sysInfo_S si;

	memset(&si, 0, sizeof(si));

	si.osType = OS_LINUX;
	si.osMajorVersion = 0;
	si.osMinorVersion = 0;
	si.osRevision = 0;
	si.busType = SI_PCI_BUS;
	si.processorFamily = DPTI_sig.dsProcessorFamily;

#if defined __i386__ 
	adpt_i386_info(&si);
#elif defined (__ia64__)
	adpt_ia64_info(&si);
#elif defined(__sparc__)
	adpt_sparc_info(&si);
#elif defined (__alpha__)
	adpt_alpha_info(&si);
#else
	si.processorType = 0xff ;
#endif
	if(copy_to_user(buffer, &si, sizeof(si))){
		printk(KERN_WARNING"dpti: Could not copy buffer TO user\n");
		return -EFAULT;
	}

	return 0;
}

#if defined __ia64__ 
static void adpt_ia64_info(sysInfo_S* si)
{
	// This is all the info we need for now
	// We will add more info as our new
	// managmenent utility requires it
	si->processorType = PROC_IA64;
}
#endif


#if defined __sparc__ 
static void adpt_sparc_info(sysInfo_S* si)
{
	// This is all the info we need for now
	// We will add more info as our new
	// managmenent utility requires it
	si->processorType = PROC_ULTRASPARC;
}
#endif

#if defined __alpha__ 
static void adpt_alpha_info(sysInfo_S* si)
{
	// This is all the info we need for now
	// We will add more info as our new
	// managmenent utility requires it
	si->processorType = PROC_ALPHA;
}
#endif

#if defined __i386__

static void adpt_i386_info(sysInfo_S* si)
{
	// This is all the info we need for now
	// We will add more info as our new
	// managmenent utility requires it
	switch (boot_cpu_data.x86) {
	case CPU_386:
		si->processorType = PROC_386;
		break;
	case CPU_486:
		si->processorType = PROC_486;
		break;
	case CPU_586:
		si->processorType = PROC_PENTIUM;
		break;
	default:  // Just in case 
		si->processorType = PROC_PENTIUM;
		break;
	}
}

#endif


static int adpt_ioctl(struct inode *inode, struct file *file, uint cmd,
	      ulong arg)
{
	int minor;
	int error = 0;
	adpt_hba* pHba;
	ulong flags = 0;
	void __user *argp = (void __user *)arg;

	minor = iminor(inode);
	if (minor >= DPTI_MAX_HBA){
		return -ENXIO;
	}
	down(&adpt_configuration_lock);
	for (pHba = hba_chain; pHba; pHba = pHba->next) {
		if (pHba->unit == minor) {
			break;	/* found adapter */
		}
	}
	up(&adpt_configuration_lock);
	if(pHba == NULL){
		return -ENXIO;
	}

	while((volatile u32) pHba->state & DPTI_STATE_RESET )
		schedule_timeout_uninterruptible(2);

	switch (cmd) {
	// TODO: handle 3 cases
	case DPT_SIGNATURE:
		if (copy_to_user(argp, &DPTI_sig, sizeof(DPTI_sig))) {
			return -EFAULT;
		}
		break;
	case I2OUSRCMD:
		return adpt_i2o_passthru(pHba, argp);

	case DPT_CTRLINFO:{
		drvrHBAinfo_S HbaInfo;

#define FLG_OSD_PCI_VALID 0x0001
#define FLG_OSD_DMA	  0x0002
#define FLG_OSD_I2O	  0x0004
		memset(&HbaInfo, 0, sizeof(HbaInfo));
		HbaInfo.drvrHBAnum = pHba->unit;
		HbaInfo.baseAddr = (ulong) pHba->base_addr_phys;
		HbaInfo.blinkState = adpt_read_blink_led(pHba);
		HbaInfo.pciBusNum =  pHba->pDev->bus->number;
		HbaInfo.pciDeviceNum=PCI_SLOT(pHba->pDev->devfn); 
		HbaInfo.Interrupt = pHba->pDev->irq; 
		HbaInfo.hbaFlags = FLG_OSD_PCI_VALID | FLG_OSD_DMA | FLG_OSD_I2O;
		if(copy_to_user(argp, &HbaInfo, sizeof(HbaInfo))){
			printk(KERN_WARNING"%s: Could not copy HbaInfo TO user\n",pHba->name);
			return -EFAULT;
		}
		break;
		}
	case DPT_SYSINFO:
		return adpt_system_info(argp);
	case DPT_BLINKLED:{
		u32 value;
		value = (u32)adpt_read_blink_led(pHba);
		if (copy_to_user(argp, &value, sizeof(value))) {
			return -EFAULT;
		}
		break;
		}
	case I2ORESETCMD:
		if(pHba->host)
			spin_lock_irqsave(pHba->host->host_lock, flags);
		adpt_hba_reset(pHba);
		if(pHba->host)
			spin_unlock_irqrestore(pHba->host->host_lock, flags);
		break;
	case I2ORESCANCMD:
		adpt_rescan(pHba);
		break;
	default:
		return -EINVAL;
	}

	return error;
}


static irqreturn_t adpt_isr(int irq, void *dev_id, struct pt_regs *regs)
{
	struct scsi_cmnd* cmd;
	adpt_hba* pHba = dev_id;
	u32 m;
	void __iomem *reply;
	u32 status=0;
	u32 context;
	ulong flags = 0;
	int handled = 0;

	if (pHba == NULL){
		printk(KERN_WARNING"adpt_isr: NULL dev_id\n");
		return IRQ_NONE;
	}
	if(pHba->host)
		spin_lock_irqsave(pHba->host->host_lock, flags);

	while( readl(pHba->irq_mask) & I2O_INTERRUPT_PENDING_B) {
		m = readl(pHba->reply_port);
		if(m == EMPTY_QUEUE){
			// Try twice then give up
			rmb();
			m = readl(pHba->reply_port);
			if(m == EMPTY_QUEUE){ 
				// This really should not happen
				printk(KERN_ERR"dpti: Could not get reply frame\n");
				goto out;
			}
		}
		reply = bus_to_virt(m);

		if (readl(reply) & MSG_FAIL) {
			u32 old_m = readl(reply+28); 
			void __iomem *msg;
			u32 old_context;
			PDEBUG("%s: Failed message\n",pHba->name);
			if(old_m >= 0x100000){
				printk(KERN_ERR"%s: Bad preserved MFA (%x)- dropping frame\n",pHba->name,old_m);
				writel(m,pHba->reply_port);
				continue;
			}
			// Transaction context is 0 in failed reply frame
			msg = pHba->msg_addr_virt + old_m;
			old_context = readl(msg+12);
			writel(old_context, reply+12);
			adpt_send_nop(pHba, old_m);
		} 
		context = readl(reply+8);
		if(context & 0x40000000){ // IOCTL
			void *p = (void *)readl(reply+12);
			if( p != NULL) {
				memcpy_fromio(p, reply, REPLY_FRAME_SIZE * 4);
			}
			// All IOCTLs will also be post wait
		}
		if(context & 0x80000000){ // Post wait message
			status = readl(reply+16);
			if(status  >> 24){
				status &=  0xffff; /* Get detail status */
			} else {
				status = I2O_POST_WAIT_OK;
			}
			if(!(context & 0x40000000)) {
				cmd = (struct scsi_cmnd*) readl(reply+12); 
				if(cmd != NULL) {
					printk(KERN_WARNING"%s: Apparent SCSI cmd in Post Wait Context - cmd=%p context=%x\n", pHba->name, cmd, context);
				}
			}
			adpt_i2o_post_wait_complete(context, status);
		} else { // SCSI message
			cmd = (struct scsi_cmnd*) readl(reply+12); 
			if(cmd != NULL){
				if(cmd->serial_number != 0) { // If not timedout
					adpt_i2o_to_scsi(reply, cmd);
				}
			}
		}
		writel(m, pHba->reply_port);
		wmb();
		rmb();
	}
	handled = 1;
out:	if(pHba->host)
		spin_unlock_irqrestore(pHba->host->host_lock, flags);
	return IRQ_RETVAL(handled);
}

static s32 adpt_scsi_to_i2o(adpt_hba* pHba, struct scsi_cmnd* cmd, struct adpt_device* d)
{
	int i;
	u32 msg[MAX_MESSAGE_SIZE];
	u32* mptr;
	u32 *lenptr;
	int direction;
	int scsidir;
	u32 len;
	u32 reqlen;
	s32 rcode;

	memset(msg, 0 , sizeof(msg));
	len = cmd->request_bufflen;
	direction = 0x00000000;	
	
	scsidir = 0x00000000;			// DATA NO XFER
	if(len) {
		/*
		 * Set SCBFlags to indicate if data is being transferred
		 * in or out, or no data transfer
		 * Note:  Do not have to verify index is less than 0 since
		 * cmd->cmnd[0] is an unsigned char
		 */
		switch(cmd->sc_data_direction){
		case DMA_FROM_DEVICE:
			scsidir  =0x40000000;	// DATA IN  (iop<--dev)
			break;
		case DMA_TO_DEVICE:
			direction=0x04000000;	// SGL OUT
			scsidir  =0x80000000;	// DATA OUT (iop-->dev)
			break;
		case DMA_NONE:
			break;
		case DMA_BIDIRECTIONAL:
			scsidir  =0x40000000;	// DATA IN  (iop<--dev)
			// Assume In - and continue;
			break;
		default:
			printk(KERN_WARNING"%s: scsi opcode 0x%x not supported.\n",
			     pHba->name, cmd->cmnd[0]);
			cmd->result = (DID_OK <<16) | (INITIATOR_ERROR << 8);
			cmd->scsi_done(cmd);
			return 	0;
		}
	}
	// msg[0] is set later
	// I2O_CMD_SCSI_EXEC
	msg[1] = ((0xff<<24)|(HOST_TID<<12)|d->tid);
	msg[2] = 0;
	msg[3] = (u32)cmd;	/* We want the SCSI control block back */
	// Our cards use the transaction context as the tag for queueing
	// Adaptec/DPT Private stuff 
	msg[4] = I2O_CMD_SCSI_EXEC|(DPT_ORGANIZATION_ID<<16);
	msg[5] = d->tid;
	/* Direction, disconnect ok | sense data | simple queue , CDBLen */
	// I2O_SCB_FLAG_ENABLE_DISCONNECT | 
	// I2O_SCB_FLAG_SIMPLE_QUEUE_TAG | 
	// I2O_SCB_FLAG_SENSE_DATA_IN_MESSAGE;
	msg[6] = scsidir|0x20a00000|cmd->cmd_len;

	mptr=msg+7;

	// Write SCSI command into the message - always 16 byte block 
	memset(mptr, 0,  16);
	memcpy(mptr, cmd->cmnd, cmd->cmd_len);
	mptr+=4;
	lenptr=mptr++;		/* Remember me - fill in when we know */
	reqlen = 14;		// SINGLE SGE
	/* Now fill in the SGList and command */
	if(cmd->use_sg) {
		struct scatterlist *sg = (struct scatterlist *)cmd->request_buffer;
		int sg_count = pci_map_sg(pHba->pDev, sg, cmd->use_sg,
				cmd->sc_data_direction);


		len = 0;
		for(i = 0 ; i < sg_count; i++) {
			*mptr++ = direction|0x10000000|sg_dma_len(sg);
			len+=sg_dma_len(sg);
			*mptr++ = sg_dma_address(sg);
			sg++;
		}
		/* Make this an end of list */
		mptr[-2] = direction|0xD0000000|sg_dma_len(sg-1);
		reqlen = mptr - msg;
		*lenptr = len;
		
		if(cmd->underflow && len != cmd->underflow){
			printk(KERN_WARNING"Cmd len %08X Cmd underflow %08X\n",
				len, cmd->underflow);
		}
	} else {
		*lenptr = len = cmd->request_bufflen;
		if(len == 0) {
			reqlen = 12;
		} else {
			*mptr++ = 0xD0000000|direction|cmd->request_bufflen;
			*mptr++ = pci_map_single(pHba->pDev,
				cmd->request_buffer,
				cmd->request_bufflen,
				cmd->sc_data_direction);
		}
	}
	
	/* Stick the headers on */
	msg[0] = reqlen<<16 | ((reqlen > 12) ? SGL_OFFSET_12 : SGL_OFFSET_0);
	
	// Send it on it's way
	rcode = adpt_i2o_post_this(pHba, msg, reqlen<<2);
	if (rcode == 0) {
		return 0;
	}
	return rcode;
}


static s32 adpt_scsi_register(adpt_hba* pHba,struct scsi_host_template * sht)
{
	struct Scsi_Host *host = NULL;

	host = scsi_register(sht, sizeof(adpt_hba*));
	if (host == NULL) {
		printk ("%s: scsi_register returned NULL\n",pHba->name);
		return -1;
	}
	host->hostdata[0] = (unsigned long)pHba;
	pHba->host = host;

	host->irq = pHba->pDev->irq;
	/* no IO ports, so don't have to set host->io_port and 
	 * host->n_io_port
	 */
	host->io_port = 0;
	host->n_io_port = 0;
				/* see comments in hosts.h */
	host->max_id = 16;
	host->max_lun = 256;
	host->max_channel = pHba->top_scsi_channel + 1;
	host->cmd_per_lun = 1;
	host->unique_id = (uint) pHba;
	host->sg_tablesize = pHba->sg_tablesize;
	host->can_queue = pHba->post_fifo_size;

	return 0;
}


static s32 adpt_i2o_to_scsi(void __iomem *reply, struct scsi_cmnd* cmd)
{
	adpt_hba* pHba;
	u32 hba_status;
	u32 dev_status;
	u32 reply_flags = readl(reply) & 0xff00; // Leave it shifted up 8 bits 
	// I know this would look cleaner if I just read bytes
	// but the model I have been using for all the rest of the
	// io is in 4 byte words - so I keep that model
	u16 detailed_status = readl(reply+16) &0xffff;
	dev_status = (detailed_status & 0xff);
	hba_status = detailed_status >> 8;

	// calculate resid for sg 
	cmd->resid = cmd->request_bufflen - readl(reply+5);

	pHba = (adpt_hba*) cmd->device->host->hostdata[0];

	cmd->sense_buffer[0] = '\0';  // initialize sense valid flag to false

	if(!(reply_flags & MSG_FAIL)) {
		switch(detailed_status & I2O_SCSI_DSC_MASK) {
		case I2O_SCSI_DSC_SUCCESS:
			cmd->result = (DID_OK << 16);
			// handle underflow
			if(readl(reply+5) < cmd->underflow ) {
				cmd->result = (DID_ERROR <<16);
				printk(KERN_WARNING"%s: SCSI CMD underflow\n",pHba->name);
			}
			break;
		case I2O_SCSI_DSC_REQUEST_ABORTED:
			cmd->result = (DID_ABORT << 16);
			break;
		case I2O_SCSI_DSC_PATH_INVALID:
		case I2O_SCSI_DSC_DEVICE_NOT_PRESENT:
		case I2O_SCSI_DSC_SELECTION_TIMEOUT:
		case I2O_SCSI_DSC_COMMAND_TIMEOUT:
		case I2O_SCSI_DSC_NO_ADAPTER:
		case I2O_SCSI_DSC_RESOURCE_UNAVAILABLE:
			printk(KERN_WARNING"%s: SCSI Timeout-Device (%d,%d,%d) hba status=0x%x, dev status=0x%x, cmd=0x%x\n",
				pHba->name, (u32)cmd->device->channel, (u32)cmd->device->id, (u32)cmd->device->lun, hba_status, dev_status, cmd->cmnd[0]);
			cmd->result = (DID_TIME_OUT << 16);
			break;
		case I2O_SCSI_DSC_ADAPTER_BUSY:
		case I2O_SCSI_DSC_BUS_BUSY:
			cmd->result = (DID_BUS_BUSY << 16);
			break;
		case I2O_SCSI_DSC_SCSI_BUS_RESET:
		case I2O_SCSI_DSC_BDR_MESSAGE_SENT:
			cmd->result = (DID_RESET << 16);
			break;
		case I2O_SCSI_DSC_PARITY_ERROR_FAILURE:
			printk(KERN_WARNING"%s: SCSI CMD parity error\n",pHba->name);
			cmd->result = (DID_PARITY << 16);
			break;
		case I2O_SCSI_DSC_UNABLE_TO_ABORT:
		case I2O_SCSI_DSC_COMPLETE_WITH_ERROR:
		case I2O_SCSI_DSC_UNABLE_TO_TERMINATE:
		case I2O_SCSI_DSC_MR_MESSAGE_RECEIVED:
		case I2O_SCSI_DSC_AUTOSENSE_FAILED:
		case I2O_SCSI_DSC_DATA_OVERRUN:
		case I2O_SCSI_DSC_UNEXPECTED_BUS_FREE:
		case I2O_SCSI_DSC_SEQUENCE_FAILURE:
		case I2O_SCSI_DSC_REQUEST_LENGTH_ERROR:
		case I2O_SCSI_DSC_PROVIDE_FAILURE:
		case I2O_SCSI_DSC_REQUEST_TERMINATED:
		case I2O_SCSI_DSC_IDE_MESSAGE_SENT:
		case I2O_SCSI_DSC_UNACKNOWLEDGED_EVENT:
		case I2O_SCSI_DSC_MESSAGE_RECEIVED:
		case I2O_SCSI_DSC_INVALID_CDB:
		case I2O_SCSI_DSC_LUN_INVALID:
		case I2O_SCSI_DSC_SCSI_TID_INVALID:
		case I2O_SCSI_DSC_FUNCTION_UNAVAILABLE:
		case I2O_SCSI_DSC_NO_NEXUS:
		case I2O_SCSI_DSC_CDB_RECEIVED:
		case I2O_SCSI_DSC_LUN_ALREADY_ENABLED:
		case I2O_SCSI_DSC_QUEUE_FROZEN:
		case I2O_SCSI_DSC_REQUEST_INVALID:
		default:
			printk(KERN_WARNING"%s: SCSI error %0x-Device(%d,%d,%d) hba_status=0x%x, dev_status=0x%x, cmd=0x%x\n",
				pHba->name, detailed_status & I2O_SCSI_DSC_MASK, (u32)cmd->device->channel, (u32)cmd->device->id, (u32)cmd->device->lun,
			       hba_status, dev_status, cmd->cmnd[0]);
			cmd->result = (DID_ERROR << 16);
			break;
		}

		// copy over the request sense data if it was a check
		// condition status
		if(dev_status == 0x02 /*CHECK_CONDITION*/) {
			u32 len = sizeof(cmd->sense_buffer);
			len = (len > 40) ?  40 : len;
			// Copy over the sense data
			memcpy_fromio(cmd->sense_buffer, (reply+28) , len);
			if(cmd->sense_buffer[0] == 0x70 /* class 7 */ && 
			   cmd->sense_buffer[2] == DATA_PROTECT ){
				/* This is to handle an array failed */
				cmd->result = (DID_TIME_OUT << 16);
				printk(KERN_WARNING"%s: SCSI Data Protect-Device (%d,%d,%d) hba_status=0x%x, dev_status=0x%x, cmd=0x%x\n",
					pHba->name, (u32)cmd->device->channel, (u32)cmd->device->id, (u32)cmd->device->lun, 
					hba_status, dev_status, cmd->cmnd[0]);

			}
		}
	} else {
		/* In this condtion we could not talk to the tid
		 * the card rejected it.  We should signal a retry
		 * for a limitted number of retries.
		 */
		cmd->result = (DID_TIME_OUT << 16);
		printk(KERN_WARNING"%s: I2O MSG_FAIL - Device (%d,%d,%d) tid=%d, cmd=0x%x\n",
			pHba->name, (u32)cmd->device->channel, (u32)cmd->device->id, (u32)cmd->device->lun,
			((struct adpt_device*)(cmd->device->hostdata))->tid, cmd->cmnd[0]);
	}

	cmd->result |= (dev_status);

	if(cmd->scsi_done != NULL){
		cmd->scsi_done(cmd);
	} 
	return cmd->result;
}


static s32 adpt_rescan(adpt_hba* pHba)
{
	s32 rcode;
	ulong flags = 0;

	if(pHba->host)
		spin_lock_irqsave(pHba->host->host_lock, flags);
	if ((rcode=adpt_i2o_lct_get(pHba)) < 0)
		goto out;
	if ((rcode=adpt_i2o_reparse_lct(pHba)) < 0)
		goto out;
	rcode = 0;
out:	if(pHba->host)
		spin_unlock_irqrestore(pHba->host->host_lock, flags);
	return rcode;
}


static s32 adpt_i2o_reparse_lct(adpt_hba* pHba)
{
	int i;
	int max;
	int tid;
	struct i2o_device *d;
	i2o_lct *lct = pHba->lct;
	u8 bus_no = 0;
	s16 scsi_id;
	s16 scsi_lun;
	u32 buf[10]; // at least 8 u32's
	struct adpt_device* pDev = NULL;
	struct i2o_device* pI2o_dev = NULL;
	
	if (lct == NULL) {
		printk(KERN_ERR "%s: LCT is empty???\n",pHba->name);
		return -1;
	}
	
	max = lct->table_size;	
	max -= 3;
	max /= 9;

	// Mark each drive as unscanned
	for (d = pHba->devices; d; d = d->next) {
		pDev =(struct adpt_device*) d->owner;
		if(!pDev){
			continue;
		}
		pDev->state |= DPTI_DEV_UNSCANNED;
	}

	printk(KERN_INFO "%s: LCT has %d entries.\n", pHba->name,max);
	
	for(i=0;i<max;i++) {
		if( lct->lct_entry[i].user_tid != 0xfff){
			continue;
		}

		if( lct->lct_entry[i].class_id == I2O_CLASS_RANDOM_BLOCK_STORAGE ||
		    lct->lct_entry[i].class_id == I2O_CLASS_SCSI_PERIPHERAL ||
		    lct->lct_entry[i].class_id == I2O_CLASS_FIBRE_CHANNEL_PERIPHERAL ){
			tid = lct->lct_entry[i].tid;
			if(adpt_i2o_query_scalar(pHba, tid, 0x8000, -1, buf, 32)<0) {
				printk(KERN_ERR"%s: Could not query device\n",pHba->name);
				continue;
			}
			bus_no = buf[0]>>16;
			scsi_id = buf[1];
			scsi_lun = (buf[2]>>8 )&0xff;
			pDev = pHba->channel[bus_no].device[scsi_id];
			/* da lun */
			while(pDev) {
				if(pDev->scsi_lun == scsi_lun) {
					break;
				}
				pDev = pDev->next_lun;
			}
			if(!pDev ) { // Something new add it
				d = (struct i2o_device *)kmalloc(sizeof(struct i2o_device), GFP_KERNEL);
				if(d==NULL)
				{
					printk(KERN_CRIT "Out of memory for I2O device data.\n");
					return -ENOMEM;
				}
				
				d->controller = pHba;
				d->next = NULL;

				memcpy(&d->lct_data, &lct->lct_entry[i], sizeof(i2o_lct_entry));

				d->flags = 0;
				adpt_i2o_report_hba_unit(pHba, d);
				adpt_i2o_install_device(pHba, d);
	
				if(bus_no >= MAX_CHANNEL) {	// Something wrong skip it
					printk(KERN_WARNING"%s: Channel number %d out of range \n", pHba->name, bus_no);
					continue;
				}
				pDev = pHba->channel[bus_no].device[scsi_id];	
				if( pDev == NULL){
					pDev =  kmalloc(sizeof(struct adpt_device),GFP_KERNEL);
					if(pDev == NULL) {
						return -ENOMEM;
					}
					pHba->channel[bus_no].device[scsi_id] = pDev;
				} else {
					while (pDev->next_lun) {
						pDev = pDev->next_lun;
					}
					pDev = pDev->next_lun = kmalloc(sizeof(struct adpt_device),GFP_KERNEL);
					if(pDev == NULL) {
						return -ENOMEM;
					}
				}
				memset(pDev,0,sizeof(struct adpt_device));
				pDev->tid = d->lct_data.tid;
				pDev->scsi_channel = bus_no;
				pDev->scsi_id = scsi_id;
				pDev->scsi_lun = scsi_lun;
				pDev->pI2o_dev = d;
				d->owner = pDev;
				pDev->type = (buf[0])&0xff;
				pDev->flags = (buf[0]>>8)&0xff;
				// Too late, SCSI system has made up it's mind, but what the hey ...
				if(scsi_id > pHba->top_scsi_id){
					pHba->top_scsi_id = scsi_id;
				}
				if(scsi_lun > pHba->top_scsi_lun){
					pHba->top_scsi_lun = scsi_lun;
				}
				continue;
			} // end of new i2o device

			// We found an old device - check it
			while(pDev) {
				if(pDev->scsi_lun == scsi_lun) {
					if(!scsi_device_online(pDev->pScsi_dev)) {
						printk(KERN_WARNING"%s: Setting device (%d,%d,%d) back online\n",
								pHba->name,bus_no,scsi_id,scsi_lun);
						if (pDev->pScsi_dev) {
							scsi_device_set_state(pDev->pScsi_dev, SDEV_RUNNING);
						}
					}
					d = pDev->pI2o_dev;
					if(d->lct_data.tid != tid) { // something changed
						pDev->tid = tid;
						memcpy(&d->lct_data, &lct->lct_entry[i], sizeof(i2o_lct_entry));
						if (pDev->pScsi_dev) {
							pDev->pScsi_dev->changed = TRUE;
							pDev->pScsi_dev->removable = TRUE;
						}
					}
					// Found it - mark it scanned
					pDev->state = DPTI_DEV_ONLINE;
					break;
				}
				pDev = pDev->next_lun;
			}
		}
	}
	for (pI2o_dev = pHba->devices; pI2o_dev; pI2o_dev = pI2o_dev->next) {
		pDev =(struct adpt_device*) pI2o_dev->owner;
		if(!pDev){
			continue;
		}
		// Drive offline drives that previously existed but could not be found
		// in the LCT table
		if (pDev->state & DPTI_DEV_UNSCANNED){
			pDev->state = DPTI_DEV_OFFLINE;
			printk(KERN_WARNING"%s: Device (%d,%d,%d) offline\n",pHba->name,pDev->scsi_channel,pDev->scsi_id,pDev->scsi_lun);
			if (pDev->pScsi_dev) {
				scsi_device_set_state(pDev->pScsi_dev, SDEV_OFFLINE);
			}
		}
	}
	return 0;
}

static void adpt_fail_posted_scbs(adpt_hba* pHba)
{
	struct scsi_cmnd* 	cmd = NULL;
	struct scsi_device* 	d = NULL;

	shost_for_each_device(d, pHba->host) {
		unsigned long flags;
		spin_lock_irqsave(&d->list_lock, flags);
		list_for_each_entry(cmd, &d->cmd_list, list) {
			if(cmd->serial_number == 0){
				continue;
			}
			cmd->result = (DID_OK << 16) | (QUEUE_FULL <<1);
			cmd->scsi_done(cmd);
		}
		spin_unlock_irqrestore(&d->list_lock, flags);
	}
}


/*============================================================================
 *  Routines from i2o subsystem
 *============================================================================
 */



/*
 *	Bring an I2O controller into HOLD state. See the spec.
 */
static int adpt_i2o_activate_hba(adpt_hba* pHba)
{
	int rcode;

	if(pHba->initialized ) {
		if (adpt_i2o_status_get(pHba) < 0) {
			if((rcode = adpt_i2o_reset_hba(pHba)) != 0){
				printk(KERN_WARNING"%s: Could NOT reset.\n", pHba->name);
				return rcode;
			}
			if (adpt_i2o_status_get(pHba) < 0) {
				printk(KERN_INFO "HBA not responding.\n");
				return -1;
			}
		}

		if(pHba->status_block->iop_state == ADAPTER_STATE_FAULTED) {
			printk(KERN_CRIT "%s: hardware fault\n", pHba->name);
			return -1;
		}

		if (pHba->status_block->iop_state == ADAPTER_STATE_READY ||
		    pHba->status_block->iop_state == ADAPTER_STATE_OPERATIONAL ||
		    pHba->status_block->iop_state == ADAPTER_STATE_HOLD ||
		    pHba->status_block->iop_state == ADAPTER_STATE_FAILED) {
			adpt_i2o_reset_hba(pHba);			
			if (adpt_i2o_status_get(pHba) < 0 || pHba->status_block->iop_state != ADAPTER_STATE_RESET) {
				printk(KERN_ERR "%s: Failed to initialize.\n", pHba->name);
				return -1;
			}
		}
	} else {
		if((rcode = adpt_i2o_reset_hba(pHba)) != 0){
			printk(KERN_WARNING"%s: Could NOT reset.\n", pHba->name);
			return rcode;
		}

	}

	if (adpt_i2o_init_outbound_q(pHba) < 0) {
		return -1;
	}

	/* In HOLD state */
	
	if (adpt_i2o_hrt_get(pHba) < 0) {
		return -1;
	}

	return 0;
}

/*
 *	Bring a controller online into OPERATIONAL state. 
 */
 
static int adpt_i2o_online_hba(adpt_hba* pHba)
{
	if (adpt_i2o_systab_send(pHba) < 0) {
		adpt_i2o_delete_hba(pHba);
		return -1;
	}
	/* In READY state */

	if (adpt_i2o_enable_hba(pHba) < 0) {
		adpt_i2o_delete_hba(pHba);
		return -1;
	}

	/* In OPERATIONAL state  */
	return 0;
}

static s32 adpt_send_nop(adpt_hba*pHba,u32 m)
{
	u32 __iomem *msg;
	ulong timeout = jiffies + 5*HZ;

	while(m == EMPTY_QUEUE){
		rmb();
		m = readl(pHba->post_port);
		if(m != EMPTY_QUEUE){
			break;
		}
		if(time_after(jiffies,timeout)){
			printk(KERN_ERR "%s: Timeout waiting for message frame!\n",pHba->name);
			return 2;
		}
		schedule_timeout_uninterruptible(1);
	}
	msg = (u32 __iomem *)(pHba->msg_addr_virt + m);
	writel( THREE_WORD_MSG_SIZE | SGL_OFFSET_0,&msg[0]);
	writel( I2O_CMD_UTIL_NOP << 24 | HOST_TID << 12 | 0,&msg[1]);
	writel( 0,&msg[2]);
	wmb();

	writel(m, pHba->post_port);
	wmb();
	return 0;
}

static s32 adpt_i2o_init_outbound_q(adpt_hba* pHba)
{
	u8 *status;
	u32 __iomem *msg = NULL;
	int i;
	ulong timeout = jiffies + TMOUT_INITOUTBOUND*HZ;
	u32* ptr;
	u32 outbound_frame;  // This had to be a 32 bit address
	u32 m;

	do {
		rmb();
		m = readl(pHba->post_port);
		if (m != EMPTY_QUEUE) {
			break;
		}

		if(time_after(jiffies,timeout)){
			printk(KERN_WARNING"%s: Timeout waiting for message frame\n",pHba->name);
			return -ETIMEDOUT;
		}
		schedule_timeout_uninterruptible(1);
	} while(m == EMPTY_QUEUE);

	msg=(u32 __iomem *)(pHba->msg_addr_virt+m);

	status = kmalloc(4,GFP_KERNEL|ADDR32);
	if (status==NULL) {
		adpt_send_nop(pHba, m);
		printk(KERN_WARNING"%s: IOP reset failed - no free memory.\n",
			pHba->name);
		return -ENOMEM;
	}
	memset(status, 0, 4);

	writel(EIGHT_WORD_MSG_SIZE| SGL_OFFSET_6, &msg[0]);
	writel(I2O_CMD_OUTBOUND_INIT<<24 | HOST_TID<<12 | ADAPTER_TID, &msg[1]);
	writel(0, &msg[2]);
	writel(0x0106, &msg[3]);	/* Transaction context */
	writel(4096, &msg[4]);		/* Host page frame size */
	writel((REPLY_FRAME_SIZE)<<16|0x80, &msg[5]);	/* Outbound msg frame size and Initcode */
	writel(0xD0000004, &msg[6]);		/* Simple SG LE, EOB */
	writel(virt_to_bus(status), &msg[7]);

	writel(m, pHba->post_port);
	wmb();

	// Wait for the reply status to come back
	do {
		if (*status) {
			if (*status != 0x01 /*I2O_EXEC_OUTBOUND_INIT_IN_PROGRESS*/) {
				break;
			}
		}
		rmb();
		if(time_after(jiffies,timeout)){
			printk(KERN_WARNING"%s: Timeout Initializing\n",pHba->name);
			return -ETIMEDOUT;
		}
		schedule_timeout_uninterruptible(1);
	} while (1);

	// If the command was successful, fill the fifo with our reply
	// message packets
	if(*status != 0x04 /*I2O_EXEC_OUTBOUND_INIT_COMPLETE*/) {
		kfree(status);
		return -2;
	}
	kfree(status);

	kfree(pHba->reply_pool);

	pHba->reply_pool = (u32*)kmalloc(pHba->reply_fifo_size * REPLY_FRAME_SIZE * 4, GFP_KERNEL|ADDR32);
	if(!pHba->reply_pool){
		printk(KERN_ERR"%s: Could not allocate reply pool\n",pHba->name);
		return -1;
	}
	memset(pHba->reply_pool, 0 , pHba->reply_fifo_size * REPLY_FRAME_SIZE * 4);

	ptr = pHba->reply_pool;
	for(i = 0; i < pHba->reply_fifo_size; i++) {
		outbound_frame = (u32)virt_to_bus(ptr);
		writel(outbound_frame, pHba->reply_port);
		wmb();
		ptr +=  REPLY_FRAME_SIZE;
	}
	adpt_i2o_status_get(pHba);
	return 0;
}


/*
 * I2O System Table.  Contains information about
 * all the IOPs in the system.  Used to inform IOPs
 * about each other's existence.
 *
 * sys_tbl_ver is the CurrentChangeIndicator that is
 * used by IOPs to track changes.
 */



static s32 adpt_i2o_status_get(adpt_hba* pHba)
{
	ulong timeout;
	u32 m;
	u32 __iomem *msg;
	u8 *status_block=NULL;
	ulong status_block_bus;

	if(pHba->status_block == NULL) {
		pHba->status_block = (i2o_status_block*)
			kmalloc(sizeof(i2o_status_block),GFP_KERNEL|ADDR32);
		if(pHba->status_block == NULL) {
			printk(KERN_ERR
			"dpti%d: Get Status Block failed; Out of memory. \n", 
			pHba->unit);
			return -ENOMEM;
		}
	}
	memset(pHba->status_block, 0, sizeof(i2o_status_block));
	status_block = (u8*)(pHba->status_block);
	status_block_bus = virt_to_bus(pHba->status_block);
	timeout = jiffies+TMOUT_GETSTATUS*HZ;
	do {
		rmb();
		m = readl(pHba->post_port);
		if (m != EMPTY_QUEUE) {
			break;
		}
		if(time_after(jiffies,timeout)){
			printk(KERN_ERR "%s: Timeout waiting for message !\n",
					pHba->name);
			return -ETIMEDOUT;
		}
		schedule_timeout_uninterruptible(1);
	} while(m==EMPTY_QUEUE);

	
	msg=(u32 __iomem *)(pHba->msg_addr_virt+m);

	writel(NINE_WORD_MSG_SIZE|SGL_OFFSET_0, &msg[0]);
	writel(I2O_CMD_STATUS_GET<<24|HOST_TID<<12|ADAPTER_TID, &msg[1]);
	writel(1, &msg[2]);
	writel(0, &msg[3]);
	writel(0, &msg[4]);
	writel(0, &msg[5]);
	writel(((u32)status_block_bus)&0xffffffff, &msg[6]);
	writel(0, &msg[7]);
	writel(sizeof(i2o_status_block), &msg[8]); // 88 bytes

	//post message
	writel(m, pHba->post_port);
	wmb();

	while(status_block[87]!=0xff){
		if(time_after(jiffies,timeout)){
			printk(KERN_ERR"dpti%d: Get status timeout.\n",
				pHba->unit);
			return -ETIMEDOUT;
		}
		rmb();
		schedule_timeout_uninterruptible(1);
	}

	// Set up our number of outbound and inbound messages
	pHba->post_fifo_size = pHba->status_block->max_inbound_frames;
	if (pHba->post_fifo_size > MAX_TO_IOP_MESSAGES) {
		pHba->post_fifo_size = MAX_TO_IOP_MESSAGES;
	}

	pHba->reply_fifo_size = pHba->status_block->max_outbound_frames;
	if (pHba->reply_fifo_size > MAX_FROM_IOP_MESSAGES) {
		pHba->reply_fifo_size = MAX_FROM_IOP_MESSAGES;
	}

	// Calculate the Scatter Gather list size
	pHba->sg_tablesize = (pHba->status_block->inbound_frame_size * 4 -40)/ sizeof(struct sg_simple_element);
	if (pHba->sg_tablesize > SG_LIST_ELEMENTS) {
		pHba->sg_tablesize = SG_LIST_ELEMENTS;
	}


#ifdef DEBUG
	printk("dpti%d: State = ",pHba->unit);
	switch(pHba->status_block->iop_state) {
		case 0x01:
			printk("INIT\n");
			break;
		case 0x02:
			printk("RESET\n");
			break;
		case 0x04:
			printk("HOLD\n");
			break;
		case 0x05:
			printk("READY\n");
			break;
		case 0x08:
			printk("OPERATIONAL\n");
			break;
		case 0x10:
			printk("FAILED\n");
			break;
		case 0x11:
			printk("FAULTED\n");
			break;
		default:
			printk("%x (unknown!!)\n",pHba->status_block->iop_state);
	}
#endif
	return 0;
}

/*
 * Get the IOP's Logical Configuration Table
 */
static int adpt_i2o_lct_get(adpt_hba* pHba)
{
	u32 msg[8];
	int ret;
	u32 buf[16];

	if ((pHba->lct_size == 0) || (pHba->lct == NULL)){
		pHba->lct_size = pHba->status_block->expected_lct_size;
	}
	do {
		if (pHba->lct == NULL) {
			pHba->lct = kmalloc(pHba->lct_size, GFP_KERNEL|ADDR32);
			if(pHba->lct == NULL) {
				printk(KERN_CRIT "%s: Lct Get failed. Out of memory.\n",
					pHba->name);
				return -ENOMEM;
			}
		}
		memset(pHba->lct, 0, pHba->lct_size);

		msg[0] = EIGHT_WORD_MSG_SIZE|SGL_OFFSET_6;
		msg[1] = I2O_CMD_LCT_NOTIFY<<24 | HOST_TID<<12 | ADAPTER_TID;
		msg[2] = 0;
		msg[3] = 0;
		msg[4] = 0xFFFFFFFF;	/* All devices */
		msg[5] = 0x00000000;	/* Report now */
		msg[6] = 0xD0000000|pHba->lct_size;
		msg[7] = virt_to_bus(pHba->lct);

		if ((ret=adpt_i2o_post_wait(pHba, msg, sizeof(msg), 360))) {
			printk(KERN_ERR "%s: LCT Get failed (status=%#10x.\n", 
				pHba->name, ret);	
			printk(KERN_ERR"Adaptec: Error Reading Hardware.\n");
			return ret;
		}

		if ((pHba->lct->table_size << 2) > pHba->lct_size) {
			pHba->lct_size = pHba->lct->table_size << 2;
			kfree(pHba->lct);
			pHba->lct = NULL;
		}
	} while (pHba->lct == NULL);

	PDEBUG("%s: Hardware resource table read.\n", pHba->name);


	// I2O_DPT_EXEC_IOP_BUFFERS_GROUP_NO;
	if(adpt_i2o_query_scalar(pHba, 0 , 0x8000, -1, buf, sizeof(buf))>=0) {
		pHba->FwDebugBufferSize = buf[1];
		pHba->FwDebugBuffer_P    = pHba->base_addr_virt + buf[0];
		pHba->FwDebugFlags_P     = pHba->FwDebugBuffer_P + FW_DEBUG_FLAGS_OFFSET;
		pHba->FwDebugBLEDvalue_P = pHba->FwDebugBuffer_P + FW_DEBUG_BLED_OFFSET;
		pHba->FwDebugBLEDflag_P  = pHba->FwDebugBLEDvalue_P + 1;
		pHba->FwDebugStrLength_P = pHba->FwDebugBuffer_P + FW_DEBUG_STR_LENGTH_OFFSET;
		pHba->FwDebugBuffer_P += buf[2]; 
		pHba->FwDebugFlags = 0;
	}

	return 0;
}

static int adpt_i2o_build_sys_table(void)
{
	adpt_hba* pHba = NULL;
	int count = 0;

	sys_tbl_len = sizeof(struct i2o_sys_tbl) +	// Header + IOPs
				(hba_count) * sizeof(struct i2o_sys_tbl_entry);

	kfree(sys_tbl);

	sys_tbl = kmalloc(sys_tbl_len, GFP_KERNEL|ADDR32);
	if(!sys_tbl) {
		printk(KERN_WARNING "SysTab Set failed. Out of memory.\n");	
		return -ENOMEM;
	}
	memset(sys_tbl, 0, sys_tbl_len);

	sys_tbl->num_entries = hba_count;
	sys_tbl->version = I2OVERSION;
	sys_tbl->change_ind = sys_tbl_ind++;

	for(pHba = hba_chain; pHba; pHba = pHba->next) {
		// Get updated Status Block so we have the latest information
		if (adpt_i2o_status_get(pHba)) {
			sys_tbl->num_entries--;
			continue; // try next one	
		}

		sys_tbl->iops[count].org_id = pHba->status_block->org_id;
		sys_tbl->iops[count].iop_id = pHba->unit + 2;
		sys_tbl->iops[count].seg_num = 0;
		sys_tbl->iops[count].i2o_version = pHba->status_block->i2o_version;
		sys_tbl->iops[count].iop_state = pHba->status_block->iop_state;
		sys_tbl->iops[count].msg_type = pHba->status_block->msg_type;
		sys_tbl->iops[count].frame_size = pHba->status_block->inbound_frame_size;
		sys_tbl->iops[count].last_changed = sys_tbl_ind - 1; // ??
		sys_tbl->iops[count].iop_capabilities = pHba->status_block->iop_capabilities;
		sys_tbl->iops[count].inbound_low = (u32)virt_to_bus(pHba->post_port);
		sys_tbl->iops[count].inbound_high = (u32)((u64)virt_to_bus(pHba->post_port)>>32);

		count++;
	}

#ifdef DEBUG
{
	u32 *table = (u32*)sys_tbl;
	printk(KERN_DEBUG"sys_tbl_len=%d in 32bit words\n",(sys_tbl_len >>2));
	for(count = 0; count < (sys_tbl_len >>2); count++) {
		printk(KERN_INFO "sys_tbl[%d] = %0#10x\n", 
			count, table[count]);
	}
}
#endif

	return 0;
}


/*
 *	 Dump the information block associated with a given unit (TID)
 */
 
static void adpt_i2o_report_hba_unit(adpt_hba* pHba, struct i2o_device *d)
{
	char buf[64];
	int unit = d->lct_data.tid;

	printk(KERN_INFO "TID %3.3d ", unit);

	if(adpt_i2o_query_scalar(pHba, unit, 0xF100, 3, buf, 16)>=0)
	{
		buf[16]=0;
		printk(" Vendor: %-12.12s", buf);
	}
	if(adpt_i2o_query_scalar(pHba, unit, 0xF100, 4, buf, 16)>=0)
	{
		buf[16]=0;
		printk(" Device: %-12.12s", buf);
	}
	if(adpt_i2o_query_scalar(pHba, unit, 0xF100, 6, buf, 8)>=0)
	{
		buf[8]=0;
		printk(" Rev: %-12.12s\n", buf);
	}
#ifdef DEBUG
	 printk(KERN_INFO "\tClass: %.21s\n", adpt_i2o_get_class_name(d->lct_data.class_id));
	 printk(KERN_INFO "\tSubclass: 0x%04X\n", d->lct_data.sub_class);
	 printk(KERN_INFO "\tFlags: ");

	 if(d->lct_data.device_flags&(1<<0))
		  printk("C");	     // ConfigDialog requested
	 if(d->lct_data.device_flags&(1<<1))
		  printk("U");	     // Multi-user capable
	 if(!(d->lct_data.device_flags&(1<<4)))
		  printk("P");	     // Peer service enabled!
	 if(!(d->lct_data.device_flags&(1<<5)))
		  printk("M");	     // Mgmt service enabled!
	 printk("\n");
#endif
}

#ifdef DEBUG
/*
 *	Do i2o class name lookup
 */
static const char *adpt_i2o_get_class_name(int class)
{
	int idx = 16;
	static char *i2o_class_name[] = {
		"Executive",
		"Device Driver Module",
		"Block Device",
		"Tape Device",
		"LAN Interface",
		"WAN Interface",
		"Fibre Channel Port",
		"Fibre Channel Device",
		"SCSI Device",
		"ATE Port",
		"ATE Device",
		"Floppy Controller",
		"Floppy Device",
		"Secondary Bus Port",
		"Peer Transport Agent",
		"Peer Transport",
		"Unknown"
	};
	
	switch(class&0xFFF) {
	case I2O_CLASS_EXECUTIVE:
		idx = 0; break;
	case I2O_CLASS_DDM:
		idx = 1; break;
	case I2O_CLASS_RANDOM_BLOCK_STORAGE:
		idx = 2; break;
	case I2O_CLASS_SEQUENTIAL_STORAGE:
		idx = 3; break;
	case I2O_CLASS_LAN:
		idx = 4; break;
	case I2O_CLASS_WAN:
		idx = 5; break;
	case I2O_CLASS_FIBRE_CHANNEL_PORT:
		idx = 6; break;
	case I2O_CLASS_FIBRE_CHANNEL_PERIPHERAL:
		idx = 7; break;
	case I2O_CLASS_SCSI_PERIPHERAL:
		idx = 8; break;
	case I2O_CLASS_ATE_PORT:
		idx = 9; break;
	case I2O_CLASS_ATE_PERIPHERAL:
		idx = 10; break;
	case I2O_CLASS_FLOPPY_CONTROLLER:
		idx = 11; break;
	case I2O_CLASS_FLOPPY_DEVICE:
		idx = 12; break;
	case I2O_CLASS_BUS_ADAPTER_PORT:
		idx = 13; break;
	case I2O_CLASS_PEER_TRANSPORT_AGENT:
		idx = 14; break;
	case I2O_CLASS_PEER_TRANSPORT:
		idx = 15; break;
	}
	return i2o_class_name[idx];
}
#endif


static s32 adpt_i2o_hrt_get(adpt_hba* pHba)
{
	u32 msg[6];
	int ret, size = sizeof(i2o_hrt);

	do {
		if (pHba->hrt == NULL) {
			pHba->hrt=kmalloc(size, GFP_KERNEL|ADDR32);
			if (pHba->hrt == NULL) {
				printk(KERN_CRIT "%s: Hrt Get failed; Out of memory.\n", pHba->name);
				return -ENOMEM;
			}
		}

		msg[0]= SIX_WORD_MSG_SIZE| SGL_OFFSET_4;
		msg[1]= I2O_CMD_HRT_GET<<24 | HOST_TID<<12 | ADAPTER_TID;
		msg[2]= 0;
		msg[3]= 0;
		msg[4]= (0xD0000000 | size);    /* Simple transaction */
		msg[5]= virt_to_bus(pHba->hrt);   /* Dump it here */

		if ((ret = adpt_i2o_post_wait(pHba, msg, sizeof(msg),20))) {
			printk(KERN_ERR "%s: Unable to get HRT (status=%#10x)\n", pHba->name, ret);
			return ret;
		}

		if (pHba->hrt->num_entries * pHba->hrt->entry_len << 2 > size) {
			size = pHba->hrt->num_entries * pHba->hrt->entry_len << 2;
			kfree(pHba->hrt);
			pHba->hrt = NULL;
		}
	} while(pHba->hrt == NULL);
	return 0;
}                                                                                                                                       

/*
 *	 Query one scalar group value or a whole scalar group.
 */		    	
static int adpt_i2o_query_scalar(adpt_hba* pHba, int tid, 
			int group, int field, void *buf, int buflen)
{
	u16 opblk[] = { 1, 0, I2O_PARAMS_FIELD_GET, group, 1, field };
	u8 *resblk;

	int size;

	/* 8 bytes for header */
	resblk = kmalloc(sizeof(u8) * (8+buflen), GFP_KERNEL|ADDR32);
	if (resblk == NULL) {
		printk(KERN_CRIT "%s: query scalar failed; Out of memory.\n", pHba->name);
		return -ENOMEM;
	}

	if (field == -1)  		/* whole group */
			opblk[4] = -1;

	size = adpt_i2o_issue_params(I2O_CMD_UTIL_PARAMS_GET, pHba, tid, 
		opblk, sizeof(opblk), resblk, sizeof(u8)*(8+buflen));
	if (size == -ETIME) {
		printk(KERN_WARNING "%s: issue params failed; Timed out.\n", pHba->name);
		return -ETIME;
	} else if (size == -EINTR) {
		printk(KERN_WARNING "%s: issue params failed; Interrupted.\n", pHba->name);
		return -EINTR;
	}
			
	memcpy(buf, resblk+8, buflen);  /* cut off header */

	kfree(resblk);
	if (size < 0)
		return size;	

	return buflen;
}


/*	Issue UTIL_PARAMS_GET or UTIL_PARAMS_SET
 *
 *	This function can be used for all UtilParamsGet/Set operations.
 *	The OperationBlock is given in opblk-buffer, 
 *	and results are returned in resblk-buffer.
 *	Note that the minimum sized resblk is 8 bytes and contains
 *	ResultCount, ErrorInfoSize, BlockStatus and BlockSize.
 */
static int adpt_i2o_issue_params(int cmd, adpt_hba* pHba, int tid, 
		  void *opblk, int oplen, void *resblk, int reslen)
{
	u32 msg[9]; 
	u32 *res = (u32 *)resblk;
	int wait_status;

	msg[0] = NINE_WORD_MSG_SIZE | SGL_OFFSET_5;
	msg[1] = cmd << 24 | HOST_TID << 12 | tid; 
	msg[2] = 0;
	msg[3] = 0;
	msg[4] = 0;
	msg[5] = 0x54000000 | oplen;	/* OperationBlock */
	msg[6] = virt_to_bus(opblk);
	msg[7] = 0xD0000000 | reslen;	/* ResultBlock */
	msg[8] = virt_to_bus(resblk);

	if ((wait_status = adpt_i2o_post_wait(pHba, msg, sizeof(msg), 20))) {
		printk("adpt_i2o_issue_params: post_wait failed (%p)\n", resblk);
   		return wait_status; 	/* -DetailedStatus */
	}

	if (res[1]&0x00FF0000) { 	/* BlockStatus != SUCCESS */
		printk(KERN_WARNING "%s: %s - Error:\n  ErrorInfoSize = 0x%02x, "
			"BlockStatus = 0x%02x, BlockSize = 0x%04x\n",
			pHba->name,
			(cmd == I2O_CMD_UTIL_PARAMS_SET) ? "PARAMS_SET"
							 : "PARAMS_GET",   
			res[1]>>24, (res[1]>>16)&0xFF, res[1]&0xFFFF);
		return -((res[1] >> 16) & 0xFF); /* -BlockStatus */
	}

	 return 4 + ((res[1] & 0x0000FFFF) << 2); /* bytes used in resblk */ 
}


static s32 adpt_i2o_quiesce_hba(adpt_hba* pHba)
{
	u32 msg[4];
	int ret;

	adpt_i2o_status_get(pHba);

	/* SysQuiesce discarded if IOP not in READY or OPERATIONAL state */

	if((pHba->status_block->iop_state != ADAPTER_STATE_READY) &&
   	   (pHba->status_block->iop_state != ADAPTER_STATE_OPERATIONAL)){
		return 0;
	}

	msg[0] = FOUR_WORD_MSG_SIZE|SGL_OFFSET_0;
	msg[1] = I2O_CMD_SYS_QUIESCE<<24|HOST_TID<<12|ADAPTER_TID;
	msg[2] = 0;
	msg[3] = 0;

	if((ret = adpt_i2o_post_wait(pHba, msg, sizeof(msg), 240))) {
		printk(KERN_INFO"dpti%d: Unable to quiesce (status=%#x).\n",
				pHba->unit, -ret);
	} else {
		printk(KERN_INFO"dpti%d: Quiesced.\n",pHba->unit);
	}

	adpt_i2o_status_get(pHba);
	return ret;
}


/* 
 * Enable IOP. Allows the IOP to resume external operations.
 */
static int adpt_i2o_enable_hba(adpt_hba* pHba)
{
	u32 msg[4];
	int ret;
	
	adpt_i2o_status_get(pHba);
	if(!pHba->status_block){
		return -ENOMEM;
	}
	/* Enable only allowed on READY state */
	if(pHba->status_block->iop_state == ADAPTER_STATE_OPERATIONAL)
		return 0;

	if(pHba->status_block->iop_state != ADAPTER_STATE_READY)
		return -EINVAL;

	msg[0]=FOUR_WORD_MSG_SIZE|SGL_OFFSET_0;
	msg[1]=I2O_CMD_SYS_ENABLE<<24|HOST_TID<<12|ADAPTER_TID;
	msg[2]= 0;
	msg[3]= 0;

	if ((ret = adpt_i2o_post_wait(pHba, msg, sizeof(msg), 240))) {
		printk(KERN_WARNING"%s: Could not enable (status=%#10x).\n", 
			pHba->name, ret);
	} else {
		PDEBUG("%s: Enabled.\n", pHba->name);
	}

	adpt_i2o_status_get(pHba);
	return ret;
}


static int adpt_i2o_systab_send(adpt_hba* pHba)
{
	 u32 msg[12];
	 int ret;

	msg[0] = I2O_MESSAGE_SIZE(12) | SGL_OFFSET_6;
	msg[1] = I2O_CMD_SYS_TAB_SET<<24 | HOST_TID<<12 | ADAPTER_TID;
	msg[2] = 0;
	msg[3] = 0;
	msg[4] = (0<<16) | ((pHba->unit+2) << 12); /* Host 0 IOP ID (unit + 2) */
	msg[5] = 0;				   /* Segment 0 */

	/* 
	 * Provide three SGL-elements:
	 * System table (SysTab), Private memory space declaration and 
	 * Private i/o space declaration  
	 */
	msg[6] = 0x54000000 | sys_tbl_len;
	msg[7] = virt_to_phys(sys_tbl);
	msg[8] = 0x54000000 | 0;
	msg[9] = 0;
	msg[10] = 0xD4000000 | 0;
	msg[11] = 0;

	if ((ret=adpt_i2o_post_wait(pHba, msg, sizeof(msg), 120))) {
		printk(KERN_INFO "%s: Unable to set SysTab (status=%#10x).\n", 
			pHba->name, ret);
	}
#ifdef DEBUG
	else {
		PINFO("%s: SysTab set.\n", pHba->name);
	}
#endif

	return ret;	
 }


/*============================================================================
 *
 *============================================================================
 */


#ifdef UARTDELAY 

static static void adpt_delay(int millisec)
{
	int i;
	for (i = 0; i < millisec; i++) {
		udelay(1000);	/* delay for one millisecond */
	}
}

#endif

static struct scsi_host_template driver_template = {
	.name			= "dpt_i2o",
	.proc_name		= "dpt_i2o",
	.proc_info		= adpt_proc_info,
	.detect			= adpt_detect,	
	.release		= adpt_release,
	.info			= adpt_info,
	.queuecommand		= adpt_queue,
	.eh_abort_handler	= adpt_abort,
	.eh_device_reset_handler = adpt_device_reset,
	.eh_bus_reset_handler	= adpt_bus_reset,
	.eh_host_reset_handler	= adpt_reset,
	.bios_param		= adpt_bios_param,
	.slave_configure	= adpt_slave_configure,
	.can_queue		= MAX_TO_IOP_MESSAGES,
	.this_id		= 7,
	.cmd_per_lun		= 1,
	.use_clustering		= ENABLE_CLUSTERING,
};
#include "scsi_module.c"
MODULE_LICENSE("GPL");