aboutsummaryrefslogblamecommitdiff
path: root/sys/dev/firewire/sbp_targ.c
blob: d10c894855b983a5fa14d1da0c49f8ac1e455c3e (plain) (tree)
1
2
3
4
5
6
   

                                        

                                                 
  















                                                                           
  










                                                                             
  









                       
                       
















                                     
                                  
 




                                  



                                           

                                                    







                                                                              

                                                                   




                         





                                
                                                                            





                                                                   

                                       

                                      
                         
                         
                         
                         
                                     
                                          

                          

                      



                                    
                     




                                            
                       













                                                  
                       
  

                                             


                            
                     








                                       
                              












                                       
                     




                           
                       






                           














                                      


                                  
                                     

                                    
                      






                                 
                         



                         

                                    
                        



                                                       








                                                                           
                                                      
                                                
                                                                       












                                                                      

                                                          





                                                                     





                                                    
                                                       



                                                                            

                                                                     
                                       
                            




                                                                           

                                                               
                                        
                     









                                             
                                                                         

                                              
                                                                          


         







                                       
                                     

              
                                          




                                    



                                                     
                                                          

         










                                                              
                                       







                                                                              

                                                
                                          


                                      
                                                               


                                                           
                                                 













                                                                 











                                                             

                                                                                                    


                                     


                                         
 

                                  



                                                                                      
                                          



                                                                          








                                                          


























                                                                           




                                                                                    
                                                



                                                                                
                                                                   
 







                                                                             
                                      






                                                              
                                             






                                                            

                                                    

                                                            
                                                                   














                                                                   
                                                          






                                                 



                                                                          





                                                                 

                                                                         
                                         
                              











                                                      
                                          
 
                                                      


      






                                                                                    
                    
                                                                             
 
                           
                                                          
                             










                                                                   
                               






                                                     
                                     

                              

                                            
                                                        



                                                
                                   
                                                              
                                                    





                      
                                                                


                               
                     
                                            
                                                                                   







                                                                          
                                                                                   

                                                                                     
                                                       
                                            



                                                                  
                       






                                        

                              
                                                                      
         








                                                                                              
                               
                    




                                           
                                                    



                             
                                                            

                                                           
                                                
                                                                             



                                
                                                       


         



                                              



                                                           
         
                               
      








                                         

                                       


                                        
                                                            


                                    


                                                                                
                              


                                                                          
                                        
                    


                                                      





                                                     
 



                                                                               

















                                                                            
 






                                                                        
                                                             

                              
                                                              

                              
 




                                                                               
 



                                                                   
 


















                                                                 
 


                                                                           
 


















                                                                              
                                                            
                 



                      
                                                                  


                                        


                                                                      

 








                                                            







                                           
                  
                                                             


                                                    
                                                                      
                                                   
                                                                    
                                      
                                                                  

         
                         


                                  
                                 

                                                         
                                                                       
                                                                    



                                                                                
                                                                   


                                                                             
                                               




                                                                                                            
                                                                




                                                                                                                



                                                  
                                                                       


























                                                             
                                                               









                                                       
                                        
















                                                                       
                                            


                                  






                                                     

                                                      
                                                 





                                    
                      
                                                                          







                                                              


                                                                                        
                                                            
                                                           

                                                      


                                                                                         
                                                             
                                                           

                                                      
                 
                                   
                                                             
                                                     
                                         








                                      

                                               

                                           
 

                                                 
                                                               
                                                            



                                                                                             

                                                   
                            



                                   
                                                                      
                                                   
                                                                  

                                      
                                                                  
 


                                                                                             
                                          
                                                                              
                                                   
                                        


                                   

                         
                                        







                                                                                  
         



                               
                                
                                                        




                           







































































                                                                                                  




                                        































                                                                                           
                       
         

                                                    





                                                                                                         





















                                                            

                                                                     









                                                         





                                                                      
                                                                       
                                                                           

                                                                             
                                               

                                                            












                                                                 
                                                                     




                                                                               
                                              

                                                                            
                                                                    








                                                                    
                                              



                                                                           




                                                                            

                                              


                                                                        
                                                                

                                             













                                                                           


                                                            
                                  
                                                                           








                                                                           

                      

                                                                           






















                                                                       
                                                                                

                                                                  





                                                          


                                                                 











                                                     
                                          






                                                                        
                                                                    
                                                                 





                                                            































                                                                           
                
                                                     
                                                    
                                                     

























                                                    
                      


                                    




                                           
                                                                      
                                                   
                                                                   

                                      
                                                                  

                                          
                                                                              


                                   
 

                          
                                                 
                                                  
                                                            
                                       
                                                     
                                           



                                       

                                              
                               




                                       
                                                                        

         
                                            
                                                          
                            
                                           
                                    

                                        
                                                  
                                  
                  

                                                                                    






























                                                                         
                                                                 

                                                         
                                                            

                                               
                                                     











                                   







                                                                               
 



                                                    
                                        


                                          
                                                     






                                                                          
                                                        


                              
                      










                                           



                                          
                                     
                      





                                           
                                                                      
                                                   
                                                                   

                                      
                                                                  

                                          
                                                                              


                                   


                          
                                 



                                       
                                                                      





                                       



                                       
 



                                                       
                                 

                                                                        
                      



                                                                 

                              




                                                                       
                                      


                                                                 

                              
                                                                 


                                        
                                                                  




                                                                            
                                                                 
                                                                                       
                                                                           
                                          
                                                                              


                         




                                                                   
                                                 



                                                                 
                                                




                                                   
                                                                  


                                              


                                                      
                                                       











                                                                 
                            


                                           
                                                                      




                                   
                                       
                                                          

                          
                                                                
                                                          







                                                                      
                                                                   




                              
                                                                              

                                                                              
                                                        



                            
                            



                                            
                                


                       
                                                                                        
                                                        



                                                


                                              
                             
                             
                                                      
                                                             
                                         
                                       
                                                               





                                                               

                              
                                                                              

                                                             
                                                                                        
                                                        
                                          


                                                  
                                              
                                                                                        
                                                        


                                              
                                                                









                                            
                                                        


                                               
                                   





                                                          

                                                                         
 
                                     


                                  
                                                   
                                            

                                               

                                     
                                            


                                    
                                            
         
 


                                         
                                                                            





                                                                      

                                                 
                                      


                                         
                                                                            

                                            
                                                               


                                      
                                                                         

                                                   
                                                                     
                                      

                              
                                                          



                                                                   


                                                   

                                         
                                                                            

                                                      

                                                                             

                      

                                                                          














                                                           
                                                    
                                                                           
                                         





                                         

                   

 




                                   
                    








                                                                            
                                                         



                                                   
 


                                                   

                                                                         




                                            
                                                              
                              
                                    
                                            














                                                  
                                 



                                                             

                                                      
                         
                                                             

                                                               
                                                                     



                                                               
                                                           





                                                
                     
                                                                    






                                                                              
                       


                                            


                                       

                                                                      

                                           


                 
                       








                                                 




                                                            
                     

                                                  
                                                 
                       
 
                                       









                                                    
 
                                           
                                              
 

                              


                 














                                                           
                                                         


                                           
/*-
 * SPDX-License-Identifier: BSD-4-Clause
 *
 * Copyright (C) 2003
 * 	Hidetoshi Shimokawa. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *
 *	This product includes software developed by Hidetoshi Shimokawa.
 *
 * 4. Neither the name of the author nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * $FreeBSD$
 */

#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/systm.h>
#include <sys/sysctl.h>
#include <sys/types.h>
#include <sys/conf.h>
#include <sys/malloc.h>
#include <sys/endian.h>

#include <sys/bus.h>
#include <machine/bus.h>

#include <dev/firewire/firewire.h>
#include <dev/firewire/firewirereg.h>
#include <dev/firewire/iec13213.h>
#include <dev/firewire/sbp.h>
#include <dev/firewire/fwmem.h>

#include <cam/cam.h>
#include <cam/cam_ccb.h>
#include <cam/cam_sim.h>
#include <cam/cam_xpt_sim.h>
#include <cam/cam_debug.h>
#include <cam/cam_periph.h>
#include <cam/scsi/scsi_all.h>
#include <cam/scsi/scsi_message.h>

#define SBP_TARG_RECV_LEN	8
#define MAX_INITIATORS		8
#define MAX_LUN			63
#define MAX_LOGINS		63
#define MAX_NODES		63
/*
 * management/command block agent registers
 *
 * BASE 0xffff f001 0000 management port
 * BASE 0xffff f001 0020 command port for login id 0
 * BASE 0xffff f001 0040 command port for login id 1
 *
 */
#define SBP_TARG_MGM	 0x10000	/* offset from 0xffff f000 000 */
#define SBP_TARG_BIND_HI	0xffff
#define SBP_TARG_BIND_LO(l)	(0xf0000000 + SBP_TARG_MGM + 0x20 * ((l) + 1))
#define SBP_TARG_BIND_START	(((u_int64_t)SBP_TARG_BIND_HI << 32) | \
				    SBP_TARG_BIND_LO(-1))
#define SBP_TARG_BIND_END	(((u_int64_t)SBP_TARG_BIND_HI << 32) | \
				    SBP_TARG_BIND_LO(MAX_LOGINS))
#define SBP_TARG_LOGIN_ID(lo)	(((lo) - SBP_TARG_BIND_LO(0))/0x20)

#define FETCH_MGM	0
#define FETCH_CMD	1
#define FETCH_POINTER	2

#define F_LINK_ACTIVE	(1 << 0)
#define F_ATIO_STARVED	(1 << 1)
#define F_LOGIN		(1 << 2)
#define F_HOLD		(1 << 3)
#define F_FREEZED	(1 << 4)

static MALLOC_DEFINE(M_SBP_TARG, "sbp_targ", "SBP-II/FireWire target mode");

static int debug = 0;

SYSCTL_INT(_debug, OID_AUTO, sbp_targ_debug, CTLFLAG_RW, &debug, 0,
        "SBP target mode debug flag");

struct sbp_targ_login {
	struct sbp_targ_lstate *lstate;
	struct fw_device *fwdev;
	struct sbp_login_res loginres;
	uint16_t fifo_hi;
	uint16_t last_hi;
	uint32_t fifo_lo;
	uint32_t last_lo;
	STAILQ_HEAD(, orb_info) orbs;
	STAILQ_ENTRY(sbp_targ_login) link;
	uint16_t hold_sec;
	uint16_t id;
	uint8_t flags;
	uint8_t spd;
	struct callout hold_callout;
};

struct sbp_targ_lstate {
	uint16_t lun;
	struct sbp_targ_softc *sc;
	struct cam_path *path;
	struct ccb_hdr_slist accept_tios;
	struct ccb_hdr_slist immed_notifies;
	struct crom_chunk model;
	uint32_t flags;
	STAILQ_HEAD(, sbp_targ_login) logins;
};

struct sbp_targ_softc {
        struct firewire_dev_comm fd;
	struct cam_sim *sim;
	struct cam_path *path;
	struct fw_bind fwb;
	int ndevs;
	int flags;
	struct crom_chunk unit;
	struct sbp_targ_lstate *lstate[MAX_LUN];
	struct sbp_targ_lstate *black_hole;
	struct sbp_targ_login *logins[MAX_LOGINS];
	struct mtx mtx;
};
#define SBP_LOCK(sc) mtx_lock(&(sc)->mtx)
#define SBP_UNLOCK(sc) mtx_unlock(&(sc)->mtx)

struct corb4 {
#if BYTE_ORDER == BIG_ENDIAN
	uint32_t n:1,
		  rq_fmt:2,
		  :1,
		  dir:1,
		  spd:3,
		  max_payload:4,
		  page_table_present:1,
		  page_size:3,
		  data_size:16;
#else
	uint32_t data_size:16,
		  page_size:3,
		  page_table_present:1,
		  max_payload:4,
		  spd:3,
		  dir:1,
		  :1,
		  rq_fmt:2,
		  n:1;
#endif
};

struct morb4 {
#if BYTE_ORDER == BIG_ENDIAN
	uint32_t n:1,
		  rq_fmt:2,
		  :9,
		  fun:4,
		  id:16;
#else
	uint32_t id:16,
		  fun:4,
		  :9,
		  rq_fmt:2,
		  n:1;
#endif
};

 
/*
 * Urestricted page table format 
 * states that the segment length
 * and high base addr are in the first
 * 32 bits and the base low is in 
 * the second
 */
struct unrestricted_page_table_fmt {
	uint16_t segment_len;
	uint16_t segment_base_high;
	uint32_t segment_base_low;
};


struct orb_info {
	struct sbp_targ_softc *sc;
	struct fw_device *fwdev;
	struct sbp_targ_login *login;
	union ccb *ccb;
	struct ccb_accept_tio *atio;
	uint8_t state;
#define ORBI_STATUS_NONE	0
#define ORBI_STATUS_FETCH	1
#define ORBI_STATUS_ATIO	2
#define ORBI_STATUS_CTIO	3
#define ORBI_STATUS_STATUS	4
#define ORBI_STATUS_POINTER	5
#define ORBI_STATUS_ABORTED	7
	uint8_t refcount;
	uint16_t orb_hi;
	uint32_t orb_lo;
	uint32_t data_hi;
	uint32_t data_lo;
	struct corb4 orb4;
	STAILQ_ENTRY(orb_info) link;
	uint32_t orb[8];
	struct unrestricted_page_table_fmt *page_table;
	struct unrestricted_page_table_fmt *cur_pte;
	struct unrestricted_page_table_fmt *last_pte;
	uint32_t  last_block_read;
	struct sbp_status status;
};

static char *orb_fun_name[] = {
	ORB_FUN_NAMES
};

static void sbp_targ_recv(struct fw_xfer *);
static void sbp_targ_fetch_orb(struct sbp_targ_softc *, struct fw_device *,
    uint16_t, uint32_t, struct sbp_targ_login *, int);
static void sbp_targ_xfer_pt(struct orb_info *);
static void sbp_targ_abort(struct sbp_targ_softc *, struct orb_info *);

static void
sbp_targ_identify(driver_t *driver, device_t parent)
{
	BUS_ADD_CHILD(parent, 0, "sbp_targ", device_get_unit(parent));
}

static int
sbp_targ_probe(device_t dev)
{
	device_t pa;

	pa = device_get_parent(dev);
	if (device_get_unit(dev) != device_get_unit(pa)) {
		return (ENXIO);
	}

	device_set_desc(dev, "SBP-2/SCSI over FireWire target mode");
	return (0);
}

static void
sbp_targ_dealloc_login(struct sbp_targ_login *login)
{
	struct orb_info *orbi, *next;

	if (login == NULL) {
		printf("%s: login = NULL\n", __func__);
		return;
	}
	for (orbi = STAILQ_FIRST(&login->orbs); orbi != NULL; orbi = next) {
		next = STAILQ_NEXT(orbi, link);
		if (debug)
			printf("%s: free orbi %p\n", __func__, orbi);
		free(orbi, M_SBP_TARG);
		orbi = NULL;
	}
	callout_stop(&login->hold_callout);

	STAILQ_REMOVE(&login->lstate->logins, login, sbp_targ_login, link);
	login->lstate->sc->logins[login->id] = NULL;
	if (debug)
		printf("%s: free login %p\n", __func__, login);
	free((void *)login, M_SBP_TARG);
	login = NULL;
}

static void
sbp_targ_hold_expire(void *arg)
{
	struct sbp_targ_login *login;

	login = (struct sbp_targ_login *)arg;

	if (login->flags & F_HOLD) {
		printf("%s: login_id=%d expired\n", __func__, login->id);
		sbp_targ_dealloc_login(login);
	} else {
		printf("%s: login_id=%d not hold\n", __func__, login->id);
	}
}

static void
sbp_targ_post_busreset(void *arg)
{
	struct sbp_targ_softc *sc;
	struct crom_src *src;
	struct crom_chunk *root;
	struct crom_chunk *unit;
	struct sbp_targ_lstate *lstate;
	struct sbp_targ_login *login;
	int i;

	sc = (struct sbp_targ_softc *)arg;
	src = sc->fd.fc->crom_src;
	root = sc->fd.fc->crom_root;

	unit = &sc->unit;

	if ((sc->flags & F_FREEZED) == 0) {
		sc->flags |= F_FREEZED;
		xpt_freeze_simq(sc->sim, /*count*/1);
	} else {
		printf("%s: already freezed\n", __func__);
	}

	bzero(unit, sizeof(struct crom_chunk));

	crom_add_chunk(src, root, unit, CROM_UDIR);
	crom_add_entry(unit, CSRKEY_SPEC, CSRVAL_ANSIT10);
	crom_add_entry(unit, CSRKEY_VER, CSRVAL_T10SBP2);
	crom_add_entry(unit, CSRKEY_COM_SPEC, CSRVAL_ANSIT10);
	crom_add_entry(unit, CSRKEY_COM_SET, CSRVAL_SCSI);

	crom_add_entry(unit, CROM_MGM, SBP_TARG_MGM >> 2);
	crom_add_entry(unit, CSRKEY_UNIT_CH, (10<<8) | 8);

	for (i = 0; i < MAX_LUN; i++) {
		lstate = sc->lstate[i];
		if (lstate == NULL)
			continue;
		crom_add_entry(unit, CSRKEY_FIRM_VER, 1);
		crom_add_entry(unit, CROM_LUN, i);
		crom_add_entry(unit, CSRKEY_MODEL, 1);
		crom_add_simple_text(src, unit, &lstate->model, "TargetMode");
	}

	/* Process for reconnection hold time */
	for (i = 0; i < MAX_LOGINS; i++) {
		login = sc->logins[i];
		if (login == NULL)
			continue;
		sbp_targ_abort(sc, STAILQ_FIRST(&login->orbs));
		if (login->flags & F_LOGIN) {
			login->flags |= F_HOLD;
			callout_reset(&login->hold_callout,
			    hz * login->hold_sec,
			    sbp_targ_hold_expire, (void *)login);
		}
	}
}

static void
sbp_targ_post_explore(void *arg)
{
	struct sbp_targ_softc *sc;

	sc = (struct sbp_targ_softc *)arg;
	sc->flags &= ~F_FREEZED;
	xpt_release_simq(sc->sim, /*run queue*/TRUE);
	return;
}

static cam_status
sbp_targ_find_devs(struct sbp_targ_softc *sc, union ccb *ccb,
    struct sbp_targ_lstate **lstate, int notfound_failure)
{
	u_int lun;

	/* XXX 0 is the only vaild target_id */
	if (ccb->ccb_h.target_id == CAM_TARGET_WILDCARD &&
	    ccb->ccb_h.target_lun == CAM_LUN_WILDCARD) {
		*lstate = sc->black_hole;
		if (debug)
			printf("setting black hole for this target id(%d)\n", ccb->ccb_h.target_id);
		return (CAM_REQ_CMP);
	}

	lun = ccb->ccb_h.target_lun;
	if (lun >= MAX_LUN)
		return (CAM_LUN_INVALID);

	*lstate = sc->lstate[lun];

	if (notfound_failure != 0 && *lstate == NULL) {
		if (debug)
			printf("%s: lstate for lun is invalid, target(%d), lun(%d)\n",
				__func__, ccb->ccb_h.target_id, lun);
		return (CAM_PATH_INVALID);
	} else
		if (debug)
			printf("%s: setting lstate for tgt(%d) lun(%d)\n",
				__func__,ccb->ccb_h.target_id, lun);

	return (CAM_REQ_CMP);
}

static void
sbp_targ_en_lun(struct sbp_targ_softc *sc, union ccb *ccb)
{
	struct ccb_en_lun *cel = &ccb->cel;
	struct sbp_targ_lstate *lstate;
	cam_status status;

	status = sbp_targ_find_devs(sc, ccb, &lstate, 0);
	if (status != CAM_REQ_CMP) {
		ccb->ccb_h.status = status;
		return;
	}

	if (cel->enable != 0) {
		if (lstate != NULL) {
			xpt_print_path(ccb->ccb_h.path);
			printf("Lun already enabled\n");
			ccb->ccb_h.status = CAM_LUN_ALRDY_ENA;
			return;
		}
		if (cel->grp6_len != 0 || cel->grp7_len != 0) {
			ccb->ccb_h.status = CAM_REQ_INVALID;
			printf("Non-zero Group Codes\n");
			return;
		}
		lstate = (struct sbp_targ_lstate *)
		    malloc(sizeof(*lstate), M_SBP_TARG, M_NOWAIT | M_ZERO);
		if (lstate == NULL) {
			xpt_print_path(ccb->ccb_h.path);
			printf("Couldn't allocate lstate\n");
			ccb->ccb_h.status = CAM_RESRC_UNAVAIL;
			return;
		} else {
			if (debug)
				printf("%s: malloc'd lstate %p\n",__func__, lstate);
		}	
		if (ccb->ccb_h.target_id == CAM_TARGET_WILDCARD) {
			sc->black_hole = lstate;
			if (debug)
				printf("Blackhole set due to target id == %d\n",
					ccb->ccb_h.target_id);
		} else
			sc->lstate[ccb->ccb_h.target_lun] = lstate;

		memset(lstate, 0, sizeof(*lstate));
		lstate->sc = sc;
		status = xpt_create_path(&lstate->path, /*periph*/NULL,
					 xpt_path_path_id(ccb->ccb_h.path),
					 xpt_path_target_id(ccb->ccb_h.path),
					 xpt_path_lun_id(ccb->ccb_h.path));
		if (status != CAM_REQ_CMP) {
			free(lstate, M_SBP_TARG);
			lstate = NULL;
			xpt_print_path(ccb->ccb_h.path);
			printf("Couldn't allocate path\n");
			ccb->ccb_h.status = CAM_RESRC_UNAVAIL;
			return;
		}
		SLIST_INIT(&lstate->accept_tios);
		SLIST_INIT(&lstate->immed_notifies);
		STAILQ_INIT(&lstate->logins);

		ccb->ccb_h.status = CAM_REQ_CMP;
		xpt_print_path(ccb->ccb_h.path);
		printf("Lun now enabled for target mode\n");
		/* bus reset */
		sc->fd.fc->ibr(sc->fd.fc);
	} else {
		struct sbp_targ_login *login, *next;

		if (lstate == NULL) {
			ccb->ccb_h.status = CAM_LUN_INVALID;
			printf("Invalid lstate for this target\n");
			return;
		}
		ccb->ccb_h.status = CAM_REQ_CMP;

		if (SLIST_FIRST(&lstate->accept_tios) != NULL) {
			printf("ATIOs pending\n");
			ccb->ccb_h.status = CAM_REQ_INVALID;
		}

		if (SLIST_FIRST(&lstate->immed_notifies) != NULL) {
			printf("INOTs pending\n");
			ccb->ccb_h.status = CAM_REQ_INVALID;
		}

		if (ccb->ccb_h.status != CAM_REQ_CMP) {
			printf("status != CAM_REQ_CMP\n");
			return;
		}

		xpt_print_path(ccb->ccb_h.path);
		printf("Target mode disabled\n");
		xpt_free_path(lstate->path);

		for (login = STAILQ_FIRST(&lstate->logins); login != NULL;
		    login = next) {
			next = STAILQ_NEXT(login, link);
			sbp_targ_dealloc_login(login);
		}

		if (ccb->ccb_h.target_id == CAM_TARGET_WILDCARD)
			sc->black_hole = NULL;
		else
			sc->lstate[ccb->ccb_h.target_lun] = NULL;
		if (debug)
			printf("%s: free lstate %p\n", __func__, lstate);
		free(lstate, M_SBP_TARG);
		lstate = NULL;

		/* bus reset */
		sc->fd.fc->ibr(sc->fd.fc);
	}
}

static void
sbp_targ_send_lstate_events(struct sbp_targ_softc *sc,
    struct sbp_targ_lstate *lstate)
{
#if 0
	struct ccb_hdr *ccbh;
	struct ccb_immediate_notify *inot;

	printf("%s: not implemented yet\n", __func__);
#endif
}


static __inline void
sbp_targ_remove_orb_info_locked(struct sbp_targ_login *login, struct orb_info *orbi)
{
	STAILQ_REMOVE(&login->orbs, orbi, orb_info, link);
}

static __inline void
sbp_targ_remove_orb_info(struct sbp_targ_login *login, struct orb_info *orbi)
{
	SBP_LOCK(orbi->sc);
	STAILQ_REMOVE(&login->orbs, orbi, orb_info, link);
	SBP_UNLOCK(orbi->sc);
}

/*
 * tag_id/init_id encoding
 *
 * tag_id and init_id has only 32bit for each.
 * scsi_target can handle very limited number(up to 15) of init_id.
 * we have to encode 48bit orb and 64bit EUI64 into these
 * variables.
 *
 * tag_id represents lower 32bit of ORB address.
 * init_id represents login_id.
 *
 */

static struct orb_info *
sbp_targ_get_orb_info(struct sbp_targ_lstate *lstate,
    u_int tag_id, u_int init_id)
{
	struct sbp_targ_login *login;
	struct orb_info *orbi;

	login = lstate->sc->logins[init_id];
	if (login == NULL) {
		printf("%s: no such login\n", __func__);
		return (NULL);
	}
	STAILQ_FOREACH(orbi, &login->orbs, link)
		if (orbi->orb_lo == tag_id)
			goto found;
	printf("%s: orb not found tag_id=0x%08x init_id=%d\n",
			 __func__, tag_id, init_id);
	return (NULL);
found:
	return (orbi);
}

static void
sbp_targ_abort(struct sbp_targ_softc *sc, struct orb_info *orbi)
{
	struct orb_info *norbi;

	SBP_LOCK(sc);
	for (; orbi != NULL; orbi = norbi) {
		printf("%s: status=%d ccb=%p\n", __func__, orbi->state, orbi->ccb);
		norbi = STAILQ_NEXT(orbi, link);
		if (orbi->state != ORBI_STATUS_ABORTED) {
			if (orbi->ccb != NULL) {
				orbi->ccb->ccb_h.status = CAM_REQ_ABORTED;
				xpt_done(orbi->ccb);
				orbi->ccb = NULL;
			}
			if (orbi->state <= ORBI_STATUS_ATIO) {
				sbp_targ_remove_orb_info_locked(orbi->login, orbi);
				if (debug)
					printf("%s: free orbi %p\n", __func__, orbi);
				free(orbi, M_SBP_TARG);
				orbi = NULL;
			} else
				orbi->state = ORBI_STATUS_ABORTED;
		}
	}
	SBP_UNLOCK(sc);
}

static void
sbp_targ_free_orbi(struct fw_xfer *xfer)
{
	struct orb_info *orbi;

	if (xfer->resp != 0) {
		/* XXX */
		printf("%s: xfer->resp = %d\n", __func__, xfer->resp);
	}
	orbi = (struct orb_info *)xfer->sc;
	if ( orbi->page_table != NULL ) {
		if (debug)
			printf("%s:  free orbi->page_table %p\n", __func__, orbi->page_table);
		free(orbi->page_table, M_SBP_TARG);
		orbi->page_table = NULL;
	}
	if (debug)
		printf("%s: free orbi %p\n", __func__, orbi);
	free(orbi, M_SBP_TARG);
	orbi = NULL;
	fw_xfer_free(xfer);
}

static void
sbp_targ_status_FIFO(struct orb_info *orbi,
    uint32_t fifo_hi, uint32_t fifo_lo, int dequeue)
{
	struct fw_xfer *xfer;

	if (dequeue)
		sbp_targ_remove_orb_info(orbi->login, orbi);

	xfer = fwmem_write_block(orbi->fwdev, (void *)orbi,
	    /*spd*/FWSPD_S400, fifo_hi, fifo_lo,
	    sizeof(uint32_t) * (orbi->status.len + 1), (char *)&orbi->status,
	    sbp_targ_free_orbi);

	if (xfer == NULL) {
		/* XXX */
		printf("%s: xfer == NULL\n", __func__);
	}
}

/*
 * Generate the appropriate CAM status for the
 * target.
 */
static void
sbp_targ_send_status(struct orb_info *orbi, union ccb *ccb)
{
	struct sbp_status *sbp_status;
#if	0
	struct orb_info *norbi;
#endif

	sbp_status = &orbi->status;

	orbi->state = ORBI_STATUS_STATUS;

	sbp_status->resp = 0; /* XXX */
	sbp_status->status = 0; /* XXX */
	sbp_status->dead = 0; /* XXX */

	ccb->ccb_h.status= CAM_REQ_CMP;

	switch (ccb->csio.scsi_status) {
	case SCSI_STATUS_OK:
		if (debug)
			printf("%s: STATUS_OK\n", __func__);
		sbp_status->len = 1;
		break;
	case SCSI_STATUS_CHECK_COND:
		if (debug)
			printf("%s: STATUS SCSI_STATUS_CHECK_COND\n", __func__);
		goto process_scsi_status;
	case SCSI_STATUS_BUSY:
		if (debug)
			printf("%s: STATUS SCSI_STATUS_BUSY\n", __func__);
		goto process_scsi_status;
	case SCSI_STATUS_CMD_TERMINATED:
process_scsi_status:
	{
		struct sbp_cmd_status *sbp_cmd_status;
		struct scsi_sense_data *sense;
		int error_code, sense_key, asc, ascq;
		uint8_t stream_bits;
		uint8_t sks[3];
		uint64_t info;
		int64_t sinfo;
		int sense_len;

		sbp_cmd_status = (struct sbp_cmd_status *)&sbp_status->data[0];
		sbp_cmd_status->status = ccb->csio.scsi_status;
		sense = &ccb->csio.sense_data;

#if 0		/* XXX What we should do? */
#if 0
		sbp_targ_abort(orbi->sc, STAILQ_NEXT(orbi, link));
#else
		norbi = STAILQ_NEXT(orbi, link);
		while (norbi) {
			printf("%s: status=%d\n", __func__, norbi->state);
			if (norbi->ccb != NULL) {
				norbi->ccb->ccb_h.status = CAM_REQ_ABORTED;
				xpt_done(norbi->ccb);
				norbi->ccb = NULL;
			}
			sbp_targ_remove_orb_info_locked(orbi->login, norbi);
			norbi = STAILQ_NEXT(norbi, link);
			free(norbi, M_SBP_TARG);
		}
#endif
#endif

		sense_len = ccb->csio.sense_len - ccb->csio.sense_resid;
		scsi_extract_sense_len(sense, sense_len, &error_code,
		    &sense_key, &asc, &ascq, /*show_errors*/ 0);

		switch (error_code) {
		case SSD_CURRENT_ERROR:
		case SSD_DESC_CURRENT_ERROR:
			sbp_cmd_status->sfmt = SBP_SFMT_CURR;
			break;
		default:
			sbp_cmd_status->sfmt = SBP_SFMT_DEFER;
			break;
		}

		if (scsi_get_sense_info(sense, sense_len, SSD_DESC_INFO, &info,
					&sinfo) == 0) {
			uint32_t info_trunc;
			sbp_cmd_status->valid = 1;
			info_trunc = info;

			sbp_cmd_status->info = htobe32(info_trunc);
		} else {
			sbp_cmd_status->valid = 0;
		}

		sbp_cmd_status->s_key = sense_key;

		if (scsi_get_stream_info(sense, sense_len, NULL,
					 &stream_bits) == 0) {
			sbp_cmd_status->mark =
			    (stream_bits & SSD_FILEMARK) ? 1 : 0;
			sbp_cmd_status->eom =
			    (stream_bits & SSD_EOM) ? 1 : 0;
			sbp_cmd_status->ill_len =
			    (stream_bits & SSD_ILI) ? 1 : 0;
		} else {
			sbp_cmd_status->mark = 0;
			sbp_cmd_status->eom = 0;
			sbp_cmd_status->ill_len = 0;
		}


		/* add_sense_code(_qual), info, cmd_spec_info */
		sbp_status->len = 4;

		if (scsi_get_sense_info(sense, sense_len, SSD_DESC_COMMAND,
					&info, &sinfo) == 0) {
			uint32_t cmdspec_trunc;

			cmdspec_trunc = info;

			sbp_cmd_status->cdb = htobe32(cmdspec_trunc);
		}

		sbp_cmd_status->s_code = asc;
		sbp_cmd_status->s_qlfr = ascq;

		if (scsi_get_sense_info(sense, sense_len, SSD_DESC_FRU, &info,
					&sinfo) == 0) {
			sbp_cmd_status->fru = (uint8_t)info;
			sbp_status->len = 5;
		} else {
			sbp_cmd_status->fru = 0;
		}

		if (scsi_get_sks(sense, sense_len, sks) == 0) {
			bcopy(sks, &sbp_cmd_status->s_keydep[0], sizeof(sks));
			sbp_status->len = 5;
			ccb->ccb_h.status |= CAM_SENT_SENSE;
		}

		break;
	}
	default:
		printf("%s: unknown scsi status 0x%x\n", __func__,
		    sbp_status->status);
	}


	sbp_targ_status_FIFO(orbi,
	    orbi->login->fifo_hi, orbi->login->fifo_lo, /*dequeue*/1);
}

/*
 * Invoked as a callback handler from fwmem_read/write_block
 *
 * Process read/write of initiator address space
 * completion and pass status onto the backend target.
 * If this is a partial read/write for a CCB then
 * we decrement the orbi's refcount to indicate
 * the status of the read/write is complete
 */
static void
sbp_targ_cam_done(struct fw_xfer *xfer)
{
	struct orb_info *orbi;
	union ccb *ccb;

	orbi = (struct orb_info *)xfer->sc;

	if (debug)
		printf("%s: resp=%d refcount=%d\n", __func__,
			xfer->resp, orbi->refcount);

	if (xfer->resp != 0) {
		printf("%s: xfer->resp = %d\n", __func__, xfer->resp);
		orbi->status.resp = SBP_TRANS_FAIL;
		orbi->status.status = OBJ_DATA | SBE_TIMEOUT/*XXX*/;
		orbi->status.dead = 1;
		sbp_targ_abort(orbi->sc, STAILQ_NEXT(orbi, link));
	}

	orbi->refcount--;

	ccb = orbi->ccb;
	if (orbi->refcount == 0) {
		orbi->ccb = NULL;
		if (orbi->state == ORBI_STATUS_ABORTED) {
			if (debug)
				printf("%s: orbi aborted\n", __func__);
			sbp_targ_remove_orb_info(orbi->login, orbi);
			if (orbi->page_table != NULL) {
				if (debug)
					printf("%s: free orbi->page_table %p\n",
						__func__, orbi->page_table);
				free(orbi->page_table, M_SBP_TARG);
			}
			if (debug)
				printf("%s: free orbi %p\n", __func__, orbi);
			free(orbi, M_SBP_TARG);
			orbi = NULL;
		} else if (orbi->status.resp == ORBI_STATUS_NONE) {
			if ((ccb->ccb_h.flags & CAM_SEND_STATUS) != 0) {
				if (debug) 
					printf("%s: CAM_SEND_STATUS set %0x\n", __func__, ccb->ccb_h.flags);
				sbp_targ_send_status(orbi, ccb);
			} else {
				if (debug)
					printf("%s: CAM_SEND_STATUS not set %0x\n", __func__, ccb->ccb_h.flags);
				ccb->ccb_h.status = CAM_REQ_CMP;
			}
			xpt_done(ccb);
		} else {
			orbi->status.len = 1;
			sbp_targ_status_FIFO(orbi,
		    	    orbi->login->fifo_hi, orbi->login->fifo_lo,
			    /*dequeue*/1);
			ccb->ccb_h.status = CAM_REQ_ABORTED;
			xpt_done(ccb);
		}
	}

	fw_xfer_free(xfer);
}

static cam_status
sbp_targ_abort_ccb(struct sbp_targ_softc *sc, union ccb *ccb)
{
	union ccb *accb;
	struct sbp_targ_lstate *lstate;
	struct ccb_hdr_slist *list;
	struct ccb_hdr *curelm;
	int found;
	cam_status status;

	status = sbp_targ_find_devs(sc, ccb, &lstate, 0);
	if (status != CAM_REQ_CMP)
		return (status);

	accb = ccb->cab.abort_ccb;

	if (accb->ccb_h.func_code == XPT_ACCEPT_TARGET_IO)
		list = &lstate->accept_tios;
	else if (accb->ccb_h.func_code == XPT_IMMEDIATE_NOTIFY)
		list = &lstate->immed_notifies;
	else
		return (CAM_UA_ABORT);

	curelm = SLIST_FIRST(list);
	found = 0;
	if (curelm == &accb->ccb_h) {
		found = 1;
		SLIST_REMOVE_HEAD(list, sim_links.sle);
	} else {
		while (curelm != NULL) {
			struct ccb_hdr *nextelm;

			nextelm = SLIST_NEXT(curelm, sim_links.sle);
			if (nextelm == &accb->ccb_h) {
				found = 1;
				SLIST_NEXT(curelm, sim_links.sle) =
				    SLIST_NEXT(nextelm, sim_links.sle);
				break;
			}
			curelm = nextelm;
		}
	}
	if (found) {
		accb->ccb_h.status = CAM_REQ_ABORTED;
		xpt_done(accb);
		return (CAM_REQ_CMP);
	}
	printf("%s: not found\n", __func__);
	return (CAM_PATH_INVALID);
}

/*
 * directly execute a read or write to the initiator
 * address space and set hand(sbp_targ_cam_done) to 
 * process the completion from the SIM to the target.
 * set orbi->refcount to inidicate that a read/write
 * is inflight to/from the initiator.
 */
static void
sbp_targ_xfer_buf(struct orb_info *orbi, u_int offset,
    uint16_t dst_hi, uint32_t dst_lo, u_int size,
    void (*hand)(struct fw_xfer *))
{
	struct fw_xfer *xfer;
	u_int len, ccb_dir, off = 0;
	char *ptr;

	if (debug > 1)
		printf("%s: offset=%d size=%d\n", __func__, offset, size);
	ccb_dir = orbi->ccb->ccb_h.flags & CAM_DIR_MASK;
	ptr = (char *)orbi->ccb->csio.data_ptr + offset;

	while (size > 0) {
		/* XXX assume dst_lo + off doesn't overflow */
		len = MIN(size, 2048 /* XXX */);
		size -= len;
		orbi->refcount ++;
		if (ccb_dir == CAM_DIR_OUT) {
			if (debug)
				printf("%s: CAM_DIR_OUT --> read block in?\n",__func__);
			xfer = fwmem_read_block(orbi->fwdev,
			   (void *)orbi, /*spd*/FWSPD_S400,
			    dst_hi, dst_lo + off, len,
			    ptr + off, hand);
		} else {
			if (debug)
				printf("%s: CAM_DIR_IN --> write block out?\n",__func__);
			xfer = fwmem_write_block(orbi->fwdev,
			   (void *)orbi, /*spd*/FWSPD_S400,
			    dst_hi, dst_lo + off, len,
			    ptr + off, hand);
		}
		if (xfer == NULL) {
			printf("%s: xfer == NULL", __func__);
			/* XXX what should we do?? */
			orbi->refcount--;
		}
		off += len;
	}
}

static void
sbp_targ_pt_done(struct fw_xfer *xfer)
{
	struct orb_info *orbi;
	struct unrestricted_page_table_fmt *pt;
	uint32_t i;

	orbi = (struct orb_info *)xfer->sc;

	if (orbi->state == ORBI_STATUS_ABORTED) {
		if (debug)
			printf("%s: orbi aborted\n", __func__);
		sbp_targ_remove_orb_info(orbi->login, orbi);
		if (debug) {
			printf("%s: free orbi->page_table %p\n", __func__, orbi->page_table);
			printf("%s: free orbi %p\n", __func__, orbi);
		}
		free(orbi->page_table, M_SBP_TARG);
		free(orbi, M_SBP_TARG);
		orbi = NULL;
		fw_xfer_free(xfer);
		return;
	}
	if (xfer->resp != 0) {
		printf("%s: xfer->resp = %d\n", __func__, xfer->resp);
		orbi->status.resp = SBP_TRANS_FAIL;
		orbi->status.status = OBJ_PT | SBE_TIMEOUT/*XXX*/;
		orbi->status.dead = 1;
		orbi->status.len = 1;
		sbp_targ_abort(orbi->sc, STAILQ_NEXT(orbi, link));

		if (debug)
			printf("%s: free orbi->page_table %p\n", __func__, orbi->page_table);

		sbp_targ_status_FIFO(orbi,
		    orbi->login->fifo_hi, orbi->login->fifo_lo, /*dequeue*/1);
		free(orbi->page_table, M_SBP_TARG);
		orbi->page_table = NULL;
		fw_xfer_free(xfer);
		return;
	}
	orbi->refcount++;
/*
 * Set endianness here so we don't have 
 * to deal with is later
 */
	for (i = 0, pt = orbi->page_table; i < orbi->orb4.data_size; i++, pt++) {
		pt->segment_len = ntohs(pt->segment_len);
		if (debug)
			printf("%s:segment_len = %u\n", __func__,pt->segment_len);
		pt->segment_base_high = ntohs(pt->segment_base_high);
		pt->segment_base_low = ntohl(pt->segment_base_low);
	}

	sbp_targ_xfer_pt(orbi);

	orbi->refcount--;
	if (orbi->refcount == 0)
		printf("%s: refcount == 0\n", __func__);

	fw_xfer_free(xfer);
	return;
}

static void sbp_targ_xfer_pt(struct orb_info *orbi)
{
	union ccb *ccb;
	uint32_t res, offset, len;

	ccb = orbi->ccb;
	if (debug)
		printf("%s: dxfer_len=%d\n", __func__, ccb->csio.dxfer_len);
	res = ccb->csio.dxfer_len;
	/*
	 * If the page table required multiple CTIO's to 
	 * complete, then cur_pte is non NULL 
	 * and we need to start from the last position
	 * If this is the first pass over a page table
	 * then we just start at the beginning of the page
	 * table.
	 *
	 * Parse the unrestricted page table and figure out where we need
	 * to shove the data from this read request.
	 */
	for (offset = 0, len = 0; (res != 0) && (orbi->cur_pte < orbi->last_pte); offset += len) {
		len = MIN(orbi->cur_pte->segment_len, res);
		res -= len;
		if (debug)
			printf("%s:page_table: %04x:%08x segment_len(%u) res(%u) len(%u)\n", 
				__func__, orbi->cur_pte->segment_base_high,
				orbi->cur_pte->segment_base_low,
				orbi->cur_pte->segment_len,
				res, len);
		sbp_targ_xfer_buf(orbi, offset, 
				orbi->cur_pte->segment_base_high,
				orbi->cur_pte->segment_base_low,
				len, sbp_targ_cam_done);
		/*
		 * If we have only written partially to
		 * this page table, then we need to save
		 * our position for the next CTIO.  If we
		 * have completed the page table, then we
		 * are safe to move on to the next entry.
		 */
		if (len == orbi->cur_pte->segment_len) {
			orbi->cur_pte++;
		} else {
			uint32_t saved_base_low;

			/* Handle transfers that cross a 4GB boundary. */
			saved_base_low = orbi->cur_pte->segment_base_low;
			orbi->cur_pte->segment_base_low += len;
			if (orbi->cur_pte->segment_base_low < saved_base_low)
				orbi->cur_pte->segment_base_high++;

			orbi->cur_pte->segment_len -= len;
		}
	}
	if (debug) {
		printf("%s: base_low(%08x) page_table_off(%p) last_block(%u)\n",
			__func__, orbi->cur_pte->segment_base_low, 
			orbi->cur_pte, orbi->last_block_read);  
	}
	if (res != 0)
		printf("Warning - short pt encountered.  "
			"Could not transfer all data.\n");
	return;
}

/*
 * Create page table in local memory
 * and transfer it from the initiator
 * in order to know where we are supposed
 * to put the data.
 */

static void
sbp_targ_fetch_pt(struct orb_info *orbi)
{
	struct fw_xfer *xfer;

	/*
	 * Pull in page table from initiator
	 * and setup for data from our
	 * backend device.
	 */
	if (orbi->page_table == NULL) {
		orbi->page_table = malloc(orbi->orb4.data_size*
					  sizeof(struct unrestricted_page_table_fmt),
					  M_SBP_TARG, M_NOWAIT|M_ZERO);
		if (orbi->page_table == NULL)
			goto error;
		orbi->cur_pte = orbi->page_table;
		orbi->last_pte = orbi->page_table + orbi->orb4.data_size;
		orbi->last_block_read = orbi->orb4.data_size;
		if (debug && orbi->page_table != NULL) 
			printf("%s: malloc'd orbi->page_table(%p), orb4.data_size(%u)\n",
 				__func__, orbi->page_table, orbi->orb4.data_size);

		xfer = fwmem_read_block(orbi->fwdev, (void *)orbi, /*spd*/FWSPD_S400,
					orbi->data_hi, orbi->data_lo, orbi->orb4.data_size*
					sizeof(struct unrestricted_page_table_fmt),
					(void *)orbi->page_table, sbp_targ_pt_done);

		if (xfer != NULL)
			return;
	} else {
		/*
		 * This is a CTIO for a page table we have
		 * already malloc'd, so just directly invoke
		 * the xfer function on the orbi.
		 */
		sbp_targ_xfer_pt(orbi);
		return;
	}
error:
	orbi->ccb->ccb_h.status = CAM_RESRC_UNAVAIL;
	if (debug)      
		printf("%s: free orbi->page_table %p due to xfer == NULL\n", __func__, orbi->page_table);
	if (orbi->page_table != NULL) {
		free(orbi->page_table, M_SBP_TARG);
		orbi->page_table = NULL;
	}
	xpt_done(orbi->ccb);
	return;
}

static void
sbp_targ_action1(struct cam_sim *sim, union ccb *ccb)
{
	struct sbp_targ_softc *sc;
	struct sbp_targ_lstate *lstate;
	cam_status status;
	u_int ccb_dir;

	sc =  (struct sbp_targ_softc *)cam_sim_softc(sim);

	status = sbp_targ_find_devs(sc, ccb, &lstate, TRUE);

	switch (ccb->ccb_h.func_code) {
	case XPT_CONT_TARGET_IO:
	{
		struct orb_info *orbi;

		if (debug)
			printf("%s: XPT_CONT_TARGET_IO (0x%08x)\n",
					 __func__, ccb->csio.tag_id);

		if (status != CAM_REQ_CMP) {
			ccb->ccb_h.status = status;
			xpt_done(ccb);
			break;
		}
		/* XXX transfer from/to initiator */
		orbi = sbp_targ_get_orb_info(lstate,
		    ccb->csio.tag_id, ccb->csio.init_id);
		if (orbi == NULL) {
			ccb->ccb_h.status = CAM_REQ_ABORTED; /* XXX */
			xpt_done(ccb);
			break;
		}
		if (orbi->state == ORBI_STATUS_ABORTED) {
			if (debug)
				printf("%s: ctio aborted\n", __func__);
			sbp_targ_remove_orb_info_locked(orbi->login, orbi);
			if (debug)
				printf("%s: free orbi %p\n", __func__, orbi);
			free(orbi, M_SBP_TARG);
			ccb->ccb_h.status = CAM_REQ_ABORTED;
			xpt_done(ccb);
			break;
		}
		orbi->state = ORBI_STATUS_CTIO;

		orbi->ccb = ccb;
		ccb_dir = ccb->ccb_h.flags & CAM_DIR_MASK;

		/* XXX */
		if (ccb->csio.dxfer_len == 0)
			ccb_dir = CAM_DIR_NONE;

		/* Sanity check */
		if (ccb_dir == CAM_DIR_IN && orbi->orb4.dir == 0)
			printf("%s: direction mismatch\n", __func__);

		/* check page table */
		if (ccb_dir != CAM_DIR_NONE && orbi->orb4.page_table_present) {
			if (debug)
				printf("%s: page_table_present\n",
				    __func__);
			if (orbi->orb4.page_size != 0) {
				printf("%s: unsupported pagesize %d != 0\n",
			 	    __func__, orbi->orb4.page_size);
				ccb->ccb_h.status = CAM_REQ_INVALID;
				xpt_done(ccb);
				break;
			}
			sbp_targ_fetch_pt(orbi);
			break;
		}

		/* Sanity check */
		if (ccb_dir != CAM_DIR_NONE) {
			sbp_targ_xfer_buf(orbi, 0, orbi->data_hi,
			    orbi->data_lo,
			    MIN(orbi->orb4.data_size, ccb->csio.dxfer_len),
			    sbp_targ_cam_done);
			if ( orbi->orb4.data_size > ccb->csio.dxfer_len ) {
				orbi->data_lo += ccb->csio.dxfer_len;
				orbi->orb4.data_size -= ccb->csio.dxfer_len;
			}
		}

		if (ccb_dir == CAM_DIR_NONE) {
			if ((ccb->ccb_h.flags & CAM_SEND_STATUS) != 0) {
				/* XXX */
				SBP_UNLOCK(sc);
				sbp_targ_send_status(orbi, ccb);
				SBP_LOCK(sc);
			}
			ccb->ccb_h.status = CAM_REQ_CMP;
			xpt_done(ccb);
		}
		break;
	}
	case XPT_ACCEPT_TARGET_IO:	/* Add Accept Target IO Resource */
		if (status != CAM_REQ_CMP) {
			ccb->ccb_h.status = status;
			xpt_done(ccb);
			break;
		}
		SLIST_INSERT_HEAD(&lstate->accept_tios, &ccb->ccb_h,
		    sim_links.sle);
		ccb->ccb_h.status = CAM_REQ_INPROG;
		if ((lstate->flags & F_ATIO_STARVED) != 0) {
			struct sbp_targ_login *login;

			if (debug)
				printf("%s: new atio arrived\n", __func__);
			lstate->flags &= ~F_ATIO_STARVED;
			STAILQ_FOREACH(login, &lstate->logins, link)
				if ((login->flags & F_ATIO_STARVED) != 0) {
					login->flags &= ~F_ATIO_STARVED;
					sbp_targ_fetch_orb(lstate->sc,
					    login->fwdev,
					    login->last_hi, login->last_lo,
					    login, FETCH_CMD);
				}
		}
		break;
	case XPT_NOTIFY_ACKNOWLEDGE:	/* recycle notify ack */
	case XPT_IMMEDIATE_NOTIFY:	/* Add Immediate Notify Resource */
		if (status != CAM_REQ_CMP) {
			ccb->ccb_h.status = status;
			xpt_done(ccb);
			break;
		}
		SLIST_INSERT_HEAD(&lstate->immed_notifies, &ccb->ccb_h,
		    sim_links.sle);
		ccb->ccb_h.status = CAM_REQ_INPROG;
		sbp_targ_send_lstate_events(sc, lstate);
		break;
	case XPT_EN_LUN:
		sbp_targ_en_lun(sc, ccb);
		xpt_done(ccb);
		break;
	case XPT_PATH_INQ:
	{
		struct ccb_pathinq *cpi = &ccb->cpi;

		cpi->version_num = 1; /* XXX??? */
		cpi->hba_inquiry = PI_TAG_ABLE;
		cpi->target_sprt = PIT_PROCESSOR
				 | PIT_DISCONNECT
				 | PIT_TERM_IO;
		cpi->transport = XPORT_SPI; /* FIXME add XPORT_FW type to cam */
		cpi->hba_misc = PIM_NOINITIATOR | PIM_NOBUSRESET |
		    PIM_NO_6_BYTE;
		cpi->hba_eng_cnt = 0;
		cpi->max_target = 7; /* XXX */
		cpi->max_lun = MAX_LUN - 1;
		cpi->initiator_id = 7; /* XXX */
		cpi->bus_id = sim->bus_id;
		cpi->base_transfer_speed = 400 * 1000 / 8;
		strlcpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN);
		strlcpy(cpi->hba_vid, "SBP_TARG", HBA_IDLEN);
		strlcpy(cpi->dev_name, sim->sim_name, DEV_IDLEN);
		cpi->unit_number = sim->unit_number;

		cpi->ccb_h.status = CAM_REQ_CMP;
		xpt_done(ccb);
		break;
	}
	case XPT_ABORT:
	{
		union ccb *accb = ccb->cab.abort_ccb;

		switch (accb->ccb_h.func_code) {
		case XPT_ACCEPT_TARGET_IO:
		case XPT_IMMEDIATE_NOTIFY:
			ccb->ccb_h.status = sbp_targ_abort_ccb(sc, ccb);
			break;
		case XPT_CONT_TARGET_IO:
			/* XXX */
			ccb->ccb_h.status = CAM_UA_ABORT;
			break;
		default:
			printf("%s: aborting unknown function %d\n",
				__func__, accb->ccb_h.func_code);
			ccb->ccb_h.status = CAM_REQ_INVALID;
			break;
		}
		xpt_done(ccb);
		break;
	}
#ifdef CAM_NEW_TRAN_CODE
	case XPT_SET_TRAN_SETTINGS:
		ccb->ccb_h.status = CAM_REQ_INVALID;
		xpt_done(ccb);
		break;
	case XPT_GET_TRAN_SETTINGS:
	{
		struct ccb_trans_settings *cts = &ccb->cts;
		struct ccb_trans_settings_scsi *scsi =
			&cts->proto_specific.scsi;
		struct ccb_trans_settings_spi *spi =
			&cts->xport_specific.spi;

		cts->protocol = PROTO_SCSI;
		cts->protocol_version = SCSI_REV_2;
		cts->transport = XPORT_FW;     /* should have a FireWire */
		cts->transport_version = 2;
		spi->valid = CTS_SPI_VALID_DISC;
		spi->flags = CTS_SPI_FLAGS_DISC_ENB;
		scsi->valid = CTS_SCSI_VALID_TQ;
		scsi->flags = CTS_SCSI_FLAGS_TAG_ENB;
#if 0
		printf("%s:%d:%d XPT_GET_TRAN_SETTINGS:\n",
			device_get_nameunit(sc->fd.dev),
			ccb->ccb_h.target_id, ccb->ccb_h.target_lun);
#endif
		cts->ccb_h.status = CAM_REQ_CMP;
		xpt_done(ccb);
		break;
	}
#endif

	default:
		printf("%s: unknown function 0x%x\n",
		    __func__, ccb->ccb_h.func_code);
		ccb->ccb_h.status = CAM_PROVIDE_FAIL;
		xpt_done(ccb);
		break;
	}
	return;
}

static void
sbp_targ_action(struct cam_sim *sim, union ccb *ccb)
{
	int s;

	s = splfw();
	sbp_targ_action1(sim, ccb);
	splx(s);
}

static void
sbp_targ_poll(struct cam_sim *sim)
{
	/* XXX */
	return;
}

static void
sbp_targ_cmd_handler(struct fw_xfer *xfer)
{
	uint32_t *orb;
	struct corb4 *orb4;
	struct orb_info *orbi;
	struct ccb_accept_tio *atio;
	u_char *bytes;
	int i;

	orbi = (struct orb_info *)xfer->sc;
	if (xfer->resp != 0) {
		printf("%s: xfer->resp = %d\n", __func__, xfer->resp);
		orbi->status.resp = SBP_TRANS_FAIL;
		orbi->status.status = OBJ_ORB | SBE_TIMEOUT/*XXX*/;
		orbi->status.dead = 1;
		orbi->status.len = 1;
		sbp_targ_abort(orbi->sc, STAILQ_NEXT(orbi, link));

		sbp_targ_status_FIFO(orbi,
		    orbi->login->fifo_hi, orbi->login->fifo_lo, /*dequeue*/1);
		fw_xfer_free(xfer);
		return;
	}

	atio = orbi->atio;

	if (orbi->state == ORBI_STATUS_ABORTED) {
		printf("%s: aborted\n", __func__);
		sbp_targ_remove_orb_info(orbi->login, orbi);
		free(orbi, M_SBP_TARG);
		atio->ccb_h.status = CAM_REQ_ABORTED;
		xpt_done((union ccb*)atio);
		goto done0;
	}
	orbi->state = ORBI_STATUS_ATIO;

	orb = orbi->orb;
	/* swap payload except SCSI command */
	for (i = 0; i < 5; i++)
		orb[i] = ntohl(orb[i]);

	orb4 = (struct corb4 *)&orb[4];
	if (orb4->rq_fmt != 0) {
		/* XXX */
		printf("%s: rq_fmt(%d) != 0\n", __func__, orb4->rq_fmt);
	}

	atio->ccb_h.target_id = 0; /* XXX */
	atio->ccb_h.target_lun = orbi->login->lstate->lun;
	atio->sense_len = 0;
	atio->tag_action = MSG_SIMPLE_TASK;
	atio->tag_id = orbi->orb_lo;
	atio->init_id = orbi->login->id;

	atio->ccb_h.flags |= CAM_TAG_ACTION_VALID;
	bytes = (u_char *)&orb[5];
	if (debug)
		printf("%s: %p %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
		    __func__, (void *)atio,
		    bytes[0], bytes[1], bytes[2], bytes[3], bytes[4],
		    bytes[5], bytes[6], bytes[7], bytes[8], bytes[9]);
	switch (bytes[0] >> 5) {
	case 0:
		atio->cdb_len = 6;
		break;
	case 1:
	case 2:
		atio->cdb_len = 10;
		break;
	case 4:
		atio->cdb_len = 16;
		break;
	case 5:
		atio->cdb_len = 12;
		break;
	case 3:
	default:
		/* Only copy the opcode. */
		atio->cdb_len = 1;
		printf("Reserved or VU command code type encountered\n");
		break;
	}

	memcpy(atio->cdb_io.cdb_bytes, bytes, atio->cdb_len);

	atio->ccb_h.status |= CAM_CDB_RECVD;

	/* next ORB */
	if ((orb[0] & (1<<31)) == 0) {
		if (debug)
			printf("%s: fetch next orb\n", __func__);
		orbi->status.src = SRC_NEXT_EXISTS;
		sbp_targ_fetch_orb(orbi->sc, orbi->fwdev,
		    orb[0], orb[1], orbi->login, FETCH_CMD);
	} else {
		orbi->status.src = SRC_NO_NEXT;
		orbi->login->flags &= ~F_LINK_ACTIVE;
	}

	orbi->data_hi = orb[2];
	orbi->data_lo = orb[3];
	orbi->orb4 = *orb4;

	xpt_done((union ccb*)atio);
done0:
	fw_xfer_free(xfer);
	return;
}

static struct sbp_targ_login *
sbp_targ_get_login(struct sbp_targ_softc *sc, struct fw_device *fwdev, int lun)
{
	struct sbp_targ_lstate *lstate;
	struct sbp_targ_login *login;
	int i;

	lstate = sc->lstate[lun];

	STAILQ_FOREACH(login, &lstate->logins, link)
		if (login->fwdev == fwdev)
			return (login);

	for (i = 0; i < MAX_LOGINS; i++)
		if (sc->logins[i] == NULL)
			goto found;

	printf("%s: increase MAX_LOGIN\n", __func__);
	return (NULL);

found:
	login = (struct sbp_targ_login *)malloc(
	    sizeof(struct sbp_targ_login), M_SBP_TARG, M_NOWAIT | M_ZERO);

	if (login == NULL) {
		printf("%s: malloc failed\n", __func__);
		return (NULL);
	}

	login->id = i;
	login->fwdev = fwdev;
	login->lstate = lstate;
	login->last_hi = 0xffff;
	login->last_lo = 0xffffffff;
	login->hold_sec = 1;
	STAILQ_INIT(&login->orbs);
	CALLOUT_INIT(&login->hold_callout);
	sc->logins[i] = login;
	return (login);
}

static void
sbp_targ_mgm_handler(struct fw_xfer *xfer)
{
	struct sbp_targ_lstate *lstate;
	struct sbp_targ_login *login;
	uint32_t *orb;
	struct morb4 *orb4;
	struct orb_info *orbi;
	int i;

	orbi = (struct orb_info *)xfer->sc;
	if (xfer->resp != 0) {
		printf("%s: xfer->resp = %d\n", __func__, xfer->resp);
		orbi->status.resp = SBP_TRANS_FAIL;
		orbi->status.status = OBJ_ORB | SBE_TIMEOUT/*XXX*/;
		orbi->status.dead = 1;
		orbi->status.len = 1;
		sbp_targ_abort(orbi->sc, STAILQ_NEXT(orbi, link));

		sbp_targ_status_FIFO(orbi,
		    orbi->login->fifo_hi, orbi->login->fifo_lo, /*dequeue*/0);
		fw_xfer_free(xfer);
		return;
	}

	orb = orbi->orb;
	/* swap payload */
	for (i = 0; i < 8; i++) {
		orb[i] = ntohl(orb[i]);
	}
	orb4 = (struct morb4 *)&orb[4];
	if (debug)
		printf("%s: %s\n", __func__, orb_fun_name[orb4->fun]);

	orbi->status.src = SRC_NO_NEXT;

	switch (orb4->fun << 16) {
	case ORB_FUN_LGI:
	{
		int exclusive = 0, lun;

		if (orb[4] & ORB_EXV)
			exclusive = 1;

		lun = orb4->id;
		lstate = orbi->sc->lstate[lun];

		if (lun >= MAX_LUN || lstate == NULL ||
		    (exclusive &&
		    STAILQ_FIRST(&lstate->logins) != NULL &&
		    STAILQ_FIRST(&lstate->logins)->fwdev != orbi->fwdev)
		   ) {
			/* error */
			orbi->status.dead = 1;
			orbi->status.status = STATUS_ACCESS_DENY;
			orbi->status.len = 1;
			break;
		}

		/* allocate login */
		login = sbp_targ_get_login(orbi->sc, orbi->fwdev, lun);
		if (login == NULL) {
			printf("%s: sbp_targ_get_login failed\n",
			    __func__);
			orbi->status.dead = 1;
			orbi->status.status = STATUS_RES_UNAVAIL;
			orbi->status.len = 1;
			break;
		}
		printf("%s: login id=%d\n", __func__, login->id);

		login->fifo_hi = orb[6];
		login->fifo_lo = orb[7];
		login->loginres.len = htons(sizeof(uint32_t) * 4);
		login->loginres.id = htons(login->id);
		login->loginres.cmd_hi = htons(SBP_TARG_BIND_HI);
		login->loginres.cmd_lo = htonl(SBP_TARG_BIND_LO(login->id));
		login->loginres.recon_hold = htons(login->hold_sec);

		STAILQ_INSERT_TAIL(&lstate->logins, login, link);
		fwmem_write_block(orbi->fwdev, NULL, /*spd*/FWSPD_S400, orb[2], orb[3],
		    sizeof(struct sbp_login_res), (void *)&login->loginres,
		    fw_asy_callback_free);
		/* XXX return status after loginres is successfully written */
		break;
	}
	case ORB_FUN_RCN:
		login = orbi->sc->logins[orb4->id];
		if (login != NULL && login->fwdev == orbi->fwdev) {
			login->flags &= ~F_HOLD;
			callout_stop(&login->hold_callout);
			printf("%s: reconnected id=%d\n",
			    __func__, login->id);
		} else {
			orbi->status.dead = 1;
			orbi->status.status = STATUS_ACCESS_DENY;
			printf("%s: reconnection faild id=%d\n",
			    __func__, orb4->id);
		}
		break;
	case ORB_FUN_LGO:
		login = orbi->sc->logins[orb4->id];
		if (login->fwdev != orbi->fwdev) {
			printf("%s: wrong initiator\n", __func__);
			break;
		}
		sbp_targ_dealloc_login(login);
		break;
	default:
		printf("%s: %s not implemented yet\n",
		    __func__, orb_fun_name[orb4->fun]);
		break;
	}
	orbi->status.len = 1;
	sbp_targ_status_FIFO(orbi, orb[6], orb[7], /*dequeue*/0);
	fw_xfer_free(xfer);
	return;
}

static void
sbp_targ_pointer_handler(struct fw_xfer *xfer)
{
	struct orb_info *orbi;
	uint32_t orb0, orb1;

	orbi = (struct orb_info *)xfer->sc;
	if (xfer->resp != 0) {
		printf("%s: xfer->resp = %d\n", __func__, xfer->resp);
		goto done;
	}

	orb0 = ntohl(orbi->orb[0]);
	orb1 = ntohl(orbi->orb[1]);
	if ((orb0 & (1U << 31)) != 0) {
		printf("%s: invalid pointer\n", __func__);
		goto done;
	}
	sbp_targ_fetch_orb(orbi->login->lstate->sc, orbi->fwdev,
	    (uint16_t)orb0, orb1, orbi->login, FETCH_CMD);
done:
	free(orbi, M_SBP_TARG);
	fw_xfer_free(xfer);
	return;
}

static void
sbp_targ_fetch_orb(struct sbp_targ_softc *sc, struct fw_device *fwdev,
    uint16_t orb_hi, uint32_t orb_lo, struct sbp_targ_login *login,
    int mode)
{
	struct orb_info *orbi;

	if (debug)
		printf("%s: fetch orb %04x:%08x\n", __func__, orb_hi, orb_lo);
	orbi = malloc(sizeof(struct orb_info), M_SBP_TARG, M_NOWAIT | M_ZERO);
	if (orbi == NULL) {
		printf("%s: malloc failed\n", __func__);
		return;
	}
	orbi->sc = sc;
	orbi->fwdev = fwdev;
	orbi->login = login;
	orbi->orb_hi = orb_hi;
	orbi->orb_lo = orb_lo;
	orbi->status.orb_hi = htons(orb_hi);
	orbi->status.orb_lo = htonl(orb_lo);
	orbi->page_table = NULL;

	switch (mode) {
	case FETCH_MGM:
		fwmem_read_block(fwdev, (void *)orbi, /*spd*/FWSPD_S400, orb_hi, orb_lo,
		    sizeof(uint32_t) * 8, &orbi->orb[0],
		    sbp_targ_mgm_handler);
		break;
	case FETCH_CMD:
		orbi->state = ORBI_STATUS_FETCH;
		login->last_hi = orb_hi;
		login->last_lo = orb_lo;
		login->flags |= F_LINK_ACTIVE;
		/* dequeue */
		SBP_LOCK(sc);
		orbi->atio = (struct ccb_accept_tio *)
		    SLIST_FIRST(&login->lstate->accept_tios);
		if (orbi->atio == NULL) {
			SBP_UNLOCK(sc);
			printf("%s: no free atio\n", __func__);
			login->lstate->flags |= F_ATIO_STARVED;
			login->flags |= F_ATIO_STARVED;
#if 0
			/* XXX ?? */
			login->fwdev = fwdev;
#endif
			break;
		}
		SLIST_REMOVE_HEAD(&login->lstate->accept_tios, sim_links.sle);
		STAILQ_INSERT_TAIL(&login->orbs, orbi, link);
		SBP_UNLOCK(sc);
		fwmem_read_block(fwdev, (void *)orbi, /*spd*/FWSPD_S400, orb_hi, orb_lo,
		    sizeof(uint32_t) * 8, &orbi->orb[0],
		    sbp_targ_cmd_handler);
		break;
	case FETCH_POINTER:
		orbi->state = ORBI_STATUS_POINTER;
		login->flags |= F_LINK_ACTIVE;
		fwmem_read_block(fwdev, (void *)orbi, /*spd*/FWSPD_S400, orb_hi, orb_lo,
		    sizeof(uint32_t) * 2, &orbi->orb[0],
		    sbp_targ_pointer_handler);
		break;
	default:
		printf("%s: invalid mode %d\n", __func__, mode);
	}
}

static void
sbp_targ_resp_callback(struct fw_xfer *xfer)
{
	struct sbp_targ_softc *sc;
	int s;

	if (debug)
		printf("%s: xfer=%p\n", __func__, xfer);
	sc = (struct sbp_targ_softc *)xfer->sc;
	fw_xfer_unload(xfer);
	xfer->recv.pay_len = SBP_TARG_RECV_LEN;
	xfer->hand = sbp_targ_recv;
	s = splfw();
	STAILQ_INSERT_TAIL(&sc->fwb.xferlist, xfer, link);
	splx(s);
}

static int
sbp_targ_cmd(struct fw_xfer *xfer, struct fw_device *fwdev, int login_id,
    int reg)
{
	struct sbp_targ_login *login;
	struct sbp_targ_softc *sc;
	int rtcode = 0;

	if (login_id < 0 || login_id >= MAX_LOGINS)
		return (RESP_ADDRESS_ERROR);

	sc = (struct sbp_targ_softc *)xfer->sc;
	login = sc->logins[login_id];
	if (login == NULL)
		return (RESP_ADDRESS_ERROR);

	if (login->fwdev != fwdev) {
		/* XXX */
		return (RESP_ADDRESS_ERROR);
	}

	switch (reg) {
	case 0x08:	/* ORB_POINTER */
		if (debug)
			printf("%s: ORB_POINTER(%d)\n", __func__, login_id);
		if ((login->flags & F_LINK_ACTIVE) != 0) {
			if (debug)
				printf("link active (ORB_POINTER)\n");
			break;
		}
		sbp_targ_fetch_orb(sc, fwdev,
		    ntohl(xfer->recv.payload[0]),
		    ntohl(xfer->recv.payload[1]),
		    login, FETCH_CMD);
		break;
	case 0x04:	/* AGENT_RESET */
		if (debug)
			printf("%s: AGENT RESET(%d)\n", __func__, login_id);
		login->last_hi = 0xffff;
		login->last_lo = 0xffffffff;
		sbp_targ_abort(sc, STAILQ_FIRST(&login->orbs));
		break;
	case 0x10:	/* DOORBELL */
		if (debug)
			printf("%s: DOORBELL(%d)\n", __func__, login_id);
		if (login->last_hi == 0xffff &&
		    login->last_lo == 0xffffffff) {
			printf("%s: no previous pointer(DOORBELL)\n",
			    __func__);
			break;
		}
		if ((login->flags & F_LINK_ACTIVE) != 0) {
			if (debug)
				printf("link active (DOORBELL)\n");
			break;
		}
		sbp_targ_fetch_orb(sc, fwdev,
		    login->last_hi, login->last_lo,
		    login, FETCH_POINTER);
		break;
	case 0x00:	/* AGENT_STATE */
		printf("%s: AGENT_STATE (%d:ignore)\n", __func__, login_id);
		break;
	case 0x14:	/* UNSOLICITED_STATE_ENABLE */
		printf("%s: UNSOLICITED_STATE_ENABLE (%d:ignore)\n",
							 __func__, login_id);
		break;
	default:
		printf("%s: invalid register %d(%d)\n",
						 __func__, reg, login_id);
		rtcode = RESP_ADDRESS_ERROR;
	}

	return (rtcode);
}

static int
sbp_targ_mgm(struct fw_xfer *xfer, struct fw_device *fwdev)
{
	struct sbp_targ_softc *sc;
	struct fw_pkt *fp;

	sc = (struct sbp_targ_softc *)xfer->sc;

	fp = &xfer->recv.hdr;
	if (fp->mode.wreqb.tcode != FWTCODE_WREQB) {
		printf("%s: tcode = %d\n", __func__, fp->mode.wreqb.tcode);
		return (RESP_TYPE_ERROR);
        }

	sbp_targ_fetch_orb(sc, fwdev,
	    ntohl(xfer->recv.payload[0]),
	    ntohl(xfer->recv.payload[1]),
	    NULL, FETCH_MGM);

	return (0);
}

static void
sbp_targ_recv(struct fw_xfer *xfer)
{
	struct fw_pkt *fp, *sfp;
	struct fw_device *fwdev;
	uint32_t lo;
	int s, rtcode;
	struct sbp_targ_softc *sc;

	s = splfw();
	sc = (struct sbp_targ_softc *)xfer->sc;
	fp = &xfer->recv.hdr;
	fwdev = fw_noderesolve_nodeid(sc->fd.fc, fp->mode.wreqb.src & 0x3f);
	if (fwdev == NULL) {
		printf("%s: cannot resolve nodeid=%d\n",
		    __func__, fp->mode.wreqb.src & 0x3f);
		rtcode = RESP_TYPE_ERROR; /* XXX */
		goto done;
	}
	lo = fp->mode.wreqb.dest_lo;

	if (lo == SBP_TARG_BIND_LO(-1))
		rtcode = sbp_targ_mgm(xfer, fwdev);
	else if (lo >= SBP_TARG_BIND_LO(0))
		rtcode = sbp_targ_cmd(xfer, fwdev, SBP_TARG_LOGIN_ID(lo),
		    lo % 0x20);
	else
		rtcode = RESP_ADDRESS_ERROR;

done:
	if (rtcode != 0)
		printf("%s: rtcode = %d\n", __func__, rtcode);
	sfp = &xfer->send.hdr;
	xfer->send.spd = FWSPD_S400;
	xfer->hand = sbp_targ_resp_callback;
	sfp->mode.wres.dst = fp->mode.wreqb.src;
	sfp->mode.wres.tlrt = fp->mode.wreqb.tlrt;
	sfp->mode.wres.tcode = FWTCODE_WRES;
	sfp->mode.wres.rtcode = rtcode;
	sfp->mode.wres.pri = 0;

	fw_asyreq(xfer->fc, -1, xfer);
	splx(s);
}

static int
sbp_targ_attach(device_t dev)
{
	struct sbp_targ_softc *sc;
	struct cam_devq *devq;
	struct firewire_comm *fc;

        sc = (struct sbp_targ_softc *) device_get_softc(dev);
	bzero((void *)sc, sizeof(struct sbp_targ_softc));

	mtx_init(&sc->mtx, "sbp_targ", NULL, MTX_DEF);
	sc->fd.fc = fc = device_get_ivars(dev);
	sc->fd.dev = dev;
	sc->fd.post_explore = (void *) sbp_targ_post_explore;
	sc->fd.post_busreset = (void *) sbp_targ_post_busreset;

        devq = cam_simq_alloc(/*maxopenings*/MAX_LUN*MAX_INITIATORS);
	if (devq == NULL)
		return (ENXIO);

	sc->sim = cam_sim_alloc(sbp_targ_action, sbp_targ_poll,
	    "sbp_targ", sc, device_get_unit(dev), &sc->mtx,
	    /*untagged*/ 1, /*tagged*/ 1, devq);
	if (sc->sim == NULL) {
		cam_simq_free(devq);
		return (ENXIO);
	}

	SBP_LOCK(sc);
	if (xpt_bus_register(sc->sim, dev, /*bus*/0) != CAM_SUCCESS)
		goto fail;

	if (xpt_create_path(&sc->path, /*periph*/ NULL, cam_sim_path(sc->sim),
	    CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD) != CAM_REQ_CMP) {
		xpt_bus_deregister(cam_sim_path(sc->sim));
		goto fail;
	}
	SBP_UNLOCK(sc);

	sc->fwb.start = SBP_TARG_BIND_START;
	sc->fwb.end = SBP_TARG_BIND_END;

	/* pre-allocate xfer */
	STAILQ_INIT(&sc->fwb.xferlist);
	fw_xferlist_add(&sc->fwb.xferlist, M_SBP_TARG,
	    /*send*/ 0, /*recv*/ SBP_TARG_RECV_LEN, MAX_LUN /* XXX */,
	    fc, (void *)sc, sbp_targ_recv);
	fw_bindadd(fc, &sc->fwb);
	return 0;

fail:
	SBP_UNLOCK(sc);
	cam_sim_free(sc->sim, /*free_devq*/TRUE);
	return (ENXIO);
}

static int
sbp_targ_detach(device_t dev)
{
	struct sbp_targ_softc *sc;
	struct sbp_targ_lstate *lstate;
	int i;

	sc = (struct sbp_targ_softc *)device_get_softc(dev);
	sc->fd.post_busreset = NULL;

	SBP_LOCK(sc);
	xpt_free_path(sc->path);
	xpt_bus_deregister(cam_sim_path(sc->sim));
	cam_sim_free(sc->sim, /*free_devq*/TRUE);
	SBP_UNLOCK(sc);

	for (i = 0; i < MAX_LUN; i++) {
		lstate = sc->lstate[i];
		if (lstate != NULL) {
			xpt_free_path(lstate->path);
			free(lstate, M_SBP_TARG);
		}
	}
	if (sc->black_hole != NULL) {
		xpt_free_path(sc->black_hole->path);
		free(sc->black_hole, M_SBP_TARG);
	}

	fw_bindremove(sc->fd.fc, &sc->fwb);
	fw_xferlist_remove(&sc->fwb.xferlist);

	mtx_destroy(&sc->mtx);

	return 0;
}

static device_method_t sbp_targ_methods[] = {
	/* device interface */
	DEVMETHOD(device_identify,	sbp_targ_identify),
	DEVMETHOD(device_probe,		sbp_targ_probe),
	DEVMETHOD(device_attach,	sbp_targ_attach),
	DEVMETHOD(device_detach,	sbp_targ_detach),
	{ 0, 0 }
};

static driver_t sbp_targ_driver = {
	"sbp_targ",
	sbp_targ_methods,
	sizeof(struct sbp_targ_softc),
};

DRIVER_MODULE(sbp_targ, firewire, sbp_targ_driver, 0, 0);
MODULE_VERSION(sbp_targ, 1);
MODULE_DEPEND(sbp_targ, firewire, 1, 1, 1);
MODULE_DEPEND(sbp_targ, cam, 1, 1, 1);