text: /*
   * Driver for ST5481 USB ISDN modem
   *
   * Author       Frode Isaksen
   * Copyright    2001 by Frode Isaksen      <fisaksen@bewan.com>
   *              2001 by Kai Germaschewski  <kai.germaschewski@gmx.de>
   * 
   * This software may be used and distributed according to the terms
   * of the GNU General Public License, incorporated herein by reference.
   *
   */
  
  #include <linux/init.h>
  #include <linux/gfp.h>
  #include <linux/usb.h>
  #include <linux/netdevice.h>
  #include "st5481.h"
  
  static void ph_connect(struct st5481_adapter *adapter);
  static void ph_disconnect(struct st5481_adapter *adapter);
  
  static struct Fsm l1fsm;
  
  static char *strL1State[] =
  {
  	"ST_L1_F3",
  	"ST_L1_F4",
  	"ST_L1_F6",
  	"ST_L1_F7",
  	"ST_L1_F8",
  };
  
  static char *strL1Event[] =
  {
  	"EV_IND_DP",  
  	"EV_IND_1",   
  	"EV_IND_2",   
  	"EV_IND_3",   
  	"EV_IND_RSY", 
  	"EV_IND_5",   
  	"EV_IND_6",   
  	"EV_IND_7",   
  	"EV_IND_AP",  
  	"EV_IND_9",   
  	"EV_IND_10",  
  	"EV_IND_11",  
  	"EV_IND_AI8",
  	"EV_IND_AI10",
  	"EV_IND_AIL",
  	"EV_IND_DI",  
  	"EV_PH_ACTIVATE_REQ",
  	"EV_PH_DEACTIVATE_REQ",
  	"EV_TIMER3",
  };
  
  static inline void D_L1L2(struct st5481_adapter *adapter, int pr, void *arg)
  {
  	struct hisax_if *ifc = (struct hisax_if *) &adapter->hisax_d_if;
  
  	ifc->l1l2(ifc, pr, arg);
  }
  
  static void
  l1_go_f3(struct FsmInst *fi, int event, void *arg)
  {
  	struct st5481_adapter *adapter = fi->userdata;
  
  	if (fi->state == ST_L1_F7)
  		ph_disconnect(adapter);
  	
  	FsmChangeState(fi, ST_L1_F3);
  	D_L1L2(adapter, PH_DEACTIVATE | INDICATION, NULL);
  }
  
  static void
  l1_go_f6(struct FsmInst *fi, int event, void *arg)
  {
  	struct st5481_adapter *adapter = fi->userdata;
  
  	if (fi->state == ST_L1_F7)
  		ph_disconnect(adapter);
  
  	FsmChangeState(fi, ST_L1_F6);
  }
  
  static void
  l1_go_f7(struct FsmInst *fi, int event, void *arg)
  {
  	struct st5481_adapter *adapter = fi->userdata;
  
  	FsmDelTimer(&adapter->timer, 0);
  	ph_connect(adapter);
  	FsmChangeState(fi, ST_L1_F7);
  	D_L1L2(adapter, PH_ACTIVATE | INDICATION, NULL);
  }
  
  static void
  l1_go_f8(struct FsmInst *fi, int event, void *arg)
  {
  	struct st5481_adapter *adapter = fi->userdata;
  
  	if (fi->state == ST_L1_F7)
  		ph_disconnect(adapter);
  
  	FsmChangeState(fi, ST_L1_F8);
  }
  
  static void
  l1_timer3(struct FsmInst *fi, int event, void *arg)
  {
  	struct st5481_adapter *adapter = fi->userdata;
  
  	st5481_ph_command(adapter, ST5481_CMD_DR);
  	FsmChangeState(fi, ST_L1_F3);
  	D_L1L2(adapter, PH_DEACTIVATE | INDICATION, NULL);
  }
  
  static void
  l1_ignore(struct FsmInst *fi, int event, void *arg)
  {
  }
  
  static void
  l1_activate(struct FsmInst *fi, int event, void *arg)
  {
  	struct st5481_adapter *adapter = fi->userdata;
  
  	st5481_ph_command(adapter, ST5481_CMD_DR);
  	st5481_ph_command(adapter, ST5481_CMD_PUP);
  	FsmRestartTimer(&adapter->timer, TIMER3_VALUE, EV_TIMER3, NULL, 2);
  	st5481_ph_command(adapter, ST5481_CMD_AR8);
  	FsmChangeState(fi, ST_L1_F4);
  }
  
  static struct FsmNode L1FnList[] __initdata =
  {
  	{ST_L1_F3, EV_IND_DP,            l1_ignore},
  	{ST_L1_F3, EV_IND_AP,            l1_go_f6},
  	{ST_L1_F3, EV_IND_AI8,           l1_go_f7},
  	{ST_L1_F3, EV_IND_AI10,          l1_go_f7},
  	{ST_L1_F3, EV_PH_ACTIVATE_REQ,   l1_activate},
  
  	{ST_L1_F4, EV_TIMER3,            l1_timer3},
  	{ST_L1_F4, EV_IND_DP,            l1_go_f3},
  	{ST_L1_F4, EV_IND_AP,            l1_go_f6},
  	{ST_L1_F4, EV_IND_AI8,           l1_go_f7},
  	{ST_L1_F4, EV_IND_AI10,          l1_go_f7},
  
  	{ST_L1_F6, EV_TIMER3,            l1_timer3},
  	{ST_L1_F6, EV_IND_DP,            l1_go_f3},
  	{ST_L1_F6, EV_IND_AP,            l1_ignore},
  	{ST_L1_F6, EV_IND_AI8,           l1_go_f7},
  	{ST_L1_F6, EV_IND_AI10,          l1_go_f7},
  	{ST_L1_F7, EV_IND_RSY,           l1_go_f8},
  
  	{ST_L1_F7, EV_IND_DP,            l1_go_f3},
  	{ST_L1_F7, EV_IND_AP,            l1_go_f6},
  	{ST_L1_F7, EV_IND_AI8,           l1_ignore},
  	{ST_L1_F7, EV_IND_AI10,          l1_ignore},
  	{ST_L1_F7, EV_IND_RSY,           l1_go_f8},
  
  	{ST_L1_F8, EV_TIMER3,            l1_timer3},
  	{ST_L1_F8, EV_IND_DP,            l1_go_f3},
  	{ST_L1_F8, EV_IND_AP,            l1_go_f6},
  	{ST_L1_F8, EV_IND_AI8,           l1_go_f8},
  	{ST_L1_F8, EV_IND_AI10,          l1_go_f8},
  	{ST_L1_F8, EV_IND_RSY,           l1_ignore},
  };
  
  static void l1m_debug(struct FsmInst *fi, char *fmt, ...)
  {
  	va_list args;
  	char buf[256];
  	
  	va_start(args, fmt);
  	vsnprintf(buf, sizeof(buf), fmt, args);
  	DBG(8, "%s", buf);
  	va_end(args);
  }
  
  /* ======================================================================
   * D-Channel out
   */
  
  /*
    D OUT state machine:
    ====================
  
    Transmit short frame (< 16 bytes of encoded data):
  
    L1 FRAME    D_OUT_STATE           USB                  D CHANNEL
    --------    -----------           ---                  ---------
   
                FIXME
  
   -> [xx..xx]  SHORT_INIT            -> [7Exx..xxC1C27EFF]
                SHORT_WAIT_DEN        <> OUT_D_COUNTER=16 
                                                   
                END_OF_SHORT          <- DEN_EVENT         -> 7Exx
                                                            xxxx 
                                                            xxxx
  							  xxxx 
  							  xxxx
  							  xxxx
  							  C1C1 
  							  7EFF 
                WAIT_FOR_RESET_IDLE   <- D_UNDERRUN        <- (8ms)                        
                IDLE                  <> Reset pipe
  
                
  
    Transmit long frame (>= 16 bytes of encoded data):
  
    L1 FRAME    D_OUT_STATE           USB                  D CHANNEL
    --------    -----------           ---                  ---------
  
   -> [xx...xx] IDLE
                WAIT_FOR_STOP         <> OUT_D_COUNTER=0
                WAIT_FOR_RESET        <> Reset pipe
  	      STOP
  	      INIT_LONG_FRAME       -> [7Exx..xx]
                WAIT_DEN              <> OUT_D_COUNTER=16 
                OUT_NORMAL            <- DEN_EVENT       -> 7Exx
                END_OF_FRAME_BUSY     -> [xxxx]             xxxx 
                END_OF_FRAME_NOT_BUSY -> [xxxx]             xxxx
  				    -> [xxxx]		  xxxx 
  				    -> [C1C2]		  xxxx
  				    -> [7EFF]		  xxxx
  							  xxxx 
  							  xxxx 
                                                            ....
  							  xxxx
  							  C1C2
  							  7EFF
  	                 	    <- D_UNDERRUN      <- (> 8ms)                        
                WAIT_FOR_STOP         <> OUT_D_COUNTER=0
                WAIT_FOR_RESET        <> Reset pipe
  	      STOP
  
  */          
  
  static struct Fsm dout_fsm;
  
  static char *strDoutState[] =
  {
  	"ST_DOUT_NONE",
  
  	"ST_DOUT_SHORT_INIT",
  	"ST_DOUT_SHORT_WAIT_DEN",
  
  	"ST_DOUT_LONG_INIT",
  	"ST_DOUT_LONG_WAIT_DEN",
  	"ST_DOUT_NORMAL",
  
  	"ST_DOUT_WAIT_FOR_UNDERRUN",
          "ST_DOUT_WAIT_FOR_NOT_BUSY",
  	"ST_DOUT_WAIT_FOR_STOP",
  	"ST_DOUT_WAIT_FOR_RESET",
  };
  
  static char *strDoutEvent[] =
  {
  	"EV_DOUT_START_XMIT",
  	"EV_DOUT_COMPLETE",
  	"EV_DOUT_DEN",
  	"EV_DOUT_RESETED",
  	"EV_DOUT_STOPPED",
  	"EV_DOUT_COLL",
  	"EV_DOUT_UNDERRUN",
  };
  
  static void dout_debug(struct FsmInst *fi, char *fmt, ...)
  {
  	va_list args;
  	char buf[256];
  	
  	va_start(args, fmt);
  	vsnprintf(buf, sizeof(buf), fmt, args);
  	DBG(0x2, "%s", buf);
  	va_end(args);
  }
  
  static void dout_stop_event(void *context)
  {
  	struct st5481_adapter *adapter = context;
  
  	FsmEvent(&adapter->d_out.fsm, EV_DOUT_STOPPED, NULL);
  }
  
  /*
   * Start the transfer of a D channel frame.
   */
  static void usb_d_out(struct st5481_adapter *adapter, int buf_nr)
  {
  	struct st5481_d_out *d_out = &adapter->d_out;
  	struct urb *urb;
  	unsigned int num_packets, packet_offset;
  	int len, buf_size, bytes_sent;
  	struct sk_buff *skb;
  	struct usb_iso_packet_descriptor *desc;
  
  	if (d_out->fsm.state != ST_DOUT_NORMAL)
  		return;
  
  	if (test_and_set_bit(buf_nr, &d_out->busy)) {
  		DBG(2, "ep %d urb %d busy %#lx", EP_D_OUT, buf_nr, d_out->busy);
  		return;
  	}
  	urb = d_out->urb[buf_nr];
  
  	skb = d_out->tx_skb;
  
  	buf_size = NUM_ISO_PACKETS_D * SIZE_ISO_PACKETS_D_OUT;
  	
  	if (skb) {
  		len = isdnhdlc_encode(&d_out->hdlc_state,
  				      skb->data, skb->len, &bytes_sent,
  				      urb->transfer_buffer, buf_size);
  		skb_pull(skb,bytes_sent);
  	} else {
  		// Send flags or idle
  		len = isdnhdlc_encode(&d_out->hdlc_state,
  				      NULL, 0, &bytes_sent,
  				      urb->transfer_buffer, buf_size);
  	}
  	
  	if (len < buf_size) {
  		FsmChangeState(&d_out->fsm, ST_DOUT_WAIT_FOR_UNDERRUN);
  	}
  	if (skb && !skb->len) {
  		d_out->tx_skb = NULL;
  		D_L1L2(adapter, PH_DATA | CONFIRM, NULL);
  		dev_kfree_skb_any(skb);
  	}
  
  	// Prepare the URB
  	urb->transfer_buffer_length = len;
  	num_packets = 0;
  	packet_offset = 0;
  	while (packet_offset < len) {
  		desc = &urb->iso_frame_desc[num_packets];
  		desc->offset = packet_offset;
  		desc->length = SIZE_ISO_PACKETS_D_OUT;
  		if (len - packet_offset < desc->length)
  			desc->length = len - packet_offset;
  		num_packets++;
  		packet_offset += desc->length;
  	}
  	urb->number_of_packets = num_packets;
  
  	// Prepare the URB
  	urb->dev = adapter->usb_dev;
  	// Need to transmit the next buffer 2ms after the DEN_EVENT
  	urb->transfer_flags = 0;
  	urb->start_frame = usb_get_current_frame_number(adapter->usb_dev)+2;
  
  	DBG_ISO_PACKET(0x20,urb);
  
  	if (usb_submit_urb(urb, GFP_KERNEL) < 0) {
  		// There is another URB queued up
  		urb->transfer_flags = URB_ISO_ASAP;
  		SUBMIT_URB(urb, GFP_KERNEL);
  	}	
  }
  
  static void fifo_reseted(void *context)
  {
  	struct st5481_adapter *adapter = context;
  
  	FsmEvent(&adapter->d_out.fsm, EV_DOUT_RESETED, NULL);
  }
  
  static void usb_d_out_complete(struct urb *urb)
  {
  	struct st5481_adapter *adapter = urb->context;
  	struct st5481_d_out *d_out = &adapter->d_out;
  	long buf_nr;
  	
  	DBG(2, "");
  
  	buf_nr = get_buf_nr(d_out->urb, urb);
  	test_and_clear_bit(buf_nr, &d_out->busy);
  
  	if (unlikely(urb->status < 0)) {
  		switch (urb->status) {
  			case -ENOENT:
  			case -ESHUTDOWN:
  			case -ECONNRESET:
  				DBG(1,"urb killed status %d", urb->status);
  				break;
  			default: 
  				WARNING("urb status %d",urb->status);
  				if (d_out->busy == 0) {
  					st5481_usb_pipe_reset(adapter, EP_D_OUT | USB_DIR_OUT, fifo_reseted, adapter);
  				}
  				break;
  		}
  		return; // Give up
  	}
  
  	FsmEvent(&adapter->d_out.fsm, EV_DOUT_COMPLETE, (void *) buf_nr);
  }
  
  /* ====================================================================== */
  
  static void dout_start_xmit(struct FsmInst *fsm, int event, void *arg)
  {
  	// FIXME unify?
  	struct st5481_adapter *adapter = fsm->userdata;
  	struct st5481_d_out *d_out = &adapter->d_out;
  	struct urb *urb;
  	int len, bytes_sent;
  	struct sk_buff *skb;
  	int buf_nr = 0;
  
  	skb = d_out->tx_skb;
  
  	DBG(2,"len=%d",skb->len);
  
  	isdnhdlc_out_init(&d_out->hdlc_state, HDLC_DCHANNEL | HDLC_BITREVERSE);
  
  	if (test_and_set_bit(buf_nr, &d_out->busy)) {
  		WARNING("ep %d urb %d busy %#lx", EP_D_OUT, buf_nr, d_out->busy);
  		return;
  	}
  	urb = d_out->urb[buf_nr];
  
  	DBG_SKB(0x10, skb);
  	len = isdnhdlc_encode(&d_out->hdlc_state,
  			      skb->data, skb->len, &bytes_sent,
  			      urb->transfer_buffer, 16);
  	skb_pull(skb, bytes_sent);
  
  	if(len < 16)
  		FsmChangeState(&d_out->fsm, ST_DOUT_SHORT_INIT);
  	else
  		FsmChangeState(&d_out->fsm, ST_DOUT_LONG_INIT);
  
  	if (skb->len == 0) {
  		d_out->tx_skb = NULL;
  		D_L1L2(adapter, PH_DATA | CONFIRM, NULL);
  		dev_kfree_skb_any(skb);
  	}
  
  // Prepare the URB
  	urb->transfer_buffer_length = len;
  
  	urb->iso_frame_desc[0].offset = 0;
  	urb->iso_frame_desc[0].length = len;
  	urb->number_of_packets = 1;
  
  	// Prepare the URB
  	urb->dev = adapter->usb_dev;
  	urb->transfer_flags = URB_ISO_ASAP;
  
  	DBG_ISO_PACKET(0x20,urb);
  	SUBMIT_URB(urb, GFP_KERNEL);
  }
  
  static void dout_short_fifo(struct FsmInst *fsm, int event, void *arg)
  {
  	struct st5481_adapter *adapter = fsm->userdata;
  	struct st5481_d_out *d_out = &adapter->d_out;
  
  	FsmChangeState(&d_out->fsm, ST_DOUT_SHORT_WAIT_DEN);
  	st5481_usb_device_ctrl_msg(adapter, OUT_D_COUNTER, 16, NULL, NULL);
  }
  
  static void dout_end_short_frame(struct FsmInst *fsm, int event, void *arg)
  {
  	struct st5481_adapter *adapter = fsm->userdata;
  	struct st5481_d_out *d_out = &adapter->d_out;
  
  	FsmChangeState(&d_out->fsm, ST_DOUT_WAIT_FOR_UNDERRUN);
  }
  
  static void dout_long_enable_fifo(struct FsmInst *fsm, int event, void *arg)
  {
  	struct st5481_adapter *adapter = fsm->userdata;
  	struct st5481_d_out *d_out = &adapter->d_out;
      
  	st5481_usb_device_ctrl_msg(adapter, OUT_D_COUNTER, 16, NULL, NULL);
  	FsmChangeState(&d_out->fsm, ST_DOUT_LONG_WAIT_DEN);
  }
  
  static void dout_long_den(struct FsmInst *fsm, int event, void *arg)
  {
  	struct st5481_adapter *adapter = fsm->userdata;
  	struct st5481_d_out *d_out = &adapter->d_out;
  
  	FsmChangeState(&d_out->fsm, ST_DOUT_NORMAL);
  	usb_d_out(adapter, 0);
  	usb_d_out(adapter, 1);
  }
  
  static void dout_reset(struct FsmInst *fsm, int event, void *arg)
  {
  	struct st5481_adapter *adapter = fsm->userdata;
  	struct st5481_d_out *d_out = &adapter->d_out;
  
  	FsmChangeState(&d_out->fsm, ST_DOUT_WAIT_FOR_RESET);
  	st5481_usb_pipe_reset(adapter, EP_D_OUT | USB_DIR_OUT, fifo_reseted, adapter);
  }
  
  static void dout_stop(struct FsmInst *fsm, int event, void *arg)
  {
  	struct st5481_adapter *adapter = fsm->userdata;
  	struct st5481_d_out *d_out = &adapter->d_out;
  
  	FsmChangeState(&d_out->fsm, ST_DOUT_WAIT_FOR_STOP);
  	st5481_usb_device_ctrl_msg(adapter, OUT_D_COUNTER, 0, dout_stop_event, adapter);
  }
  
  static void dout_underrun(struct FsmInst *fsm, int event, void *arg)
  {
  	struct st5481_adapter *adapter = fsm->userdata;
  	struct st5481_d_out *d_out = &adapter->d_out;
  
  	if (test_bit(0, &d_out->busy) || test_bit(1, &d_out->busy)) {
  		FsmChangeState(&d_out->fsm, ST_DOUT_WAIT_FOR_NOT_BUSY);
  	}  else {
  		dout_stop(fsm, event, arg);
  	}
  }
  
  static void dout_check_busy(struct FsmInst *fsm, int event, void *arg)
  {
  	struct st5481_adapter *adapter = fsm->userdata;
  	struct st5481_d_out *d_out = &adapter->d_out;
  
  	if (!test_bit(0, &d_out->busy) && !test_bit(1, &d_out->busy))
  		dout_stop(fsm, event, arg);
  }
  
  static void dout_reseted(struct FsmInst *fsm, int event, void *arg)
  {
  	struct st5481_adapter *adapter = fsm->userdata;
  	struct st5481_d_out *d_out = &adapter->d_out;
  
  	FsmChangeState(&d_out->fsm, ST_DOUT_NONE);
  	// FIXME locking
  	if (d_out->tx_skb)
  		FsmEvent(&d_out->fsm, EV_DOUT_START_XMIT, NULL);
  }
  
  static void dout_complete(struct FsmInst *fsm, int event, void *arg)
  {
  	struct st5481_adapter *adapter = fsm->userdata;
  	long buf_nr = (long) arg;
  
  	usb_d_out(adapter, buf_nr);
  }
  
  static void dout_ignore(struct FsmInst *fsm, int event, void *arg)
  {
  }
  
  static struct FsmNode DoutFnList[] __initdata =
  {
  	{ST_DOUT_NONE,                   EV_DOUT_START_XMIT,   dout_start_xmit},
  
  	{ST_DOUT_SHORT_INIT,             EV_DOUT_COMPLETE,     dout_short_fifo},
  
  	{ST_DOUT_SHORT_WAIT_DEN,         EV_DOUT_DEN,          dout_end_short_frame},
  	{ST_DOUT_SHORT_WAIT_DEN,         EV_DOUT_UNDERRUN,     dout_underrun},
  
  	{ST_DOUT_LONG_INIT,              EV_DOUT_COMPLETE,     dout_long_enable_fifo},
  
  	{ST_DOUT_LONG_WAIT_DEN,          EV_DOUT_DEN,          dout_long_den},
  	{ST_DOUT_LONG_WAIT_DEN,          EV_DOUT_UNDERRUN,     dout_underrun},
  
  	{ST_DOUT_NORMAL,                 EV_DOUT_UNDERRUN,     dout_underrun},
  	{ST_DOUT_NORMAL,                 EV_DOUT_COMPLETE,     dout_complete},
  
  	{ST_DOUT_WAIT_FOR_UNDERRUN,      EV_DOUT_UNDERRUN,     dout_underrun},
  	{ST_DOUT_WAIT_FOR_UNDERRUN,      EV_DOUT_COMPLETE,     dout_ignore},
  
  	{ST_DOUT_WAIT_FOR_NOT_BUSY,      EV_DOUT_COMPLETE,     dout_check_busy},
  
  	{ST_DOUT_WAIT_FOR_STOP,          EV_DOUT_STOPPED,      dout_reset},
  
  	{ST_DOUT_WAIT_FOR_RESET,         EV_DOUT_RESETED,      dout_reseted},
  };
  
  void st5481_d_l2l1(struct hisax_if *hisax_d_if, int pr, void *arg)
  {
  	struct st5481_adapter *adapter = hisax_d_if->priv;
  	struct sk_buff *skb = arg;
  
  	switch (pr) {
  	case PH_ACTIVATE | REQUEST:
  		FsmEvent(&adapter->l1m, EV_PH_ACTIVATE_REQ, NULL);
  		break;
  	case PH_DEACTIVATE | REQUEST:
  		FsmEvent(&adapter->l1m, EV_PH_DEACTIVATE_REQ, NULL);
  		break;
  	case PH_DATA | REQUEST:
  		DBG(2, "PH_DATA REQUEST len %d", skb->len);
  		BUG_ON(adapter->d_out.tx_skb);
  		adapter->d_out.tx_skb = skb;
  		FsmEvent(&adapter->d_out.fsm, EV_DOUT_START_XMIT, NULL);
  		break;
  	default:
  		WARNING("pr %#x\n", pr);
  		break;
  	}
  }
  
  /* ======================================================================
   */
  
  /*
   * Start receiving on the D channel since entered state F7.
   */
  static void ph_connect(struct st5481_adapter *adapter)
  {
  	struct st5481_d_out *d_out = &adapter->d_out;
  	struct st5481_in *d_in = &adapter->d_in;
  
  	DBG(8,"");
  		
  	FsmChangeState(&d_out->fsm, ST_DOUT_NONE);
  
  	//	st5481_usb_device_ctrl_msg(adapter, FFMSK_D, OUT_UNDERRUN, NULL, NULL);
  	st5481_usb_device_ctrl_msg(adapter, FFMSK_D, 0xfc, NULL, NULL);
  	st5481_in_mode(d_in, L1_MODE_HDLC);
  
  #ifdef LOOPBACK
  	// Turn loopback on (data sent on B and D looped back)
  	st5481_usb_device_ctrl_msg(cs, LBB, 0x04, NULL, NULL);
  #endif
  
  	st5481_usb_pipe_reset(adapter, EP_D_OUT | USB_DIR_OUT, NULL, NULL);
  
  	// Turn on the green LED to tell that we are in state F7
  	adapter->leds |= GREEN_LED;
  	st5481_usb_device_ctrl_msg(adapter, GPIO_OUT, adapter->leds, NULL, NULL);
  }
  
  /*
   * Stop receiving on the D channel since not in state F7.
   */
  static void ph_disconnect(struct st5481_adapter *adapter)
  {
  	DBG(8,"");
  
  	st5481_in_mode(&adapter->d_in, L1_MODE_NULL);
  
  	// Turn off the green LED to tell that we left state F7
  	adapter->leds &= ~GREEN_LED;
  	st5481_usb_device_ctrl_msg(adapter, GPIO_OUT, adapter->leds, NULL, NULL);
  }
  
  static int st5481_setup_d_out(struct st5481_adapter *adapter)
  {
  	struct usb_device *dev = adapter->usb_dev;
  	struct usb_interface *intf;
  	struct usb_host_interface *altsetting = NULL;
  	struct usb_host_endpoint *endpoint;
  	struct st5481_d_out *d_out = &adapter->d_out;
  
  	DBG(2,"");
  
  	intf = usb_ifnum_to_if(dev, 0);
  	if (intf)
  		altsetting = usb_altnum_to_altsetting(intf, 3);
  	if (!altsetting)
  		return -ENXIO;
  
  	// Allocate URBs and buffers for the D channel out
  	endpoint = &altsetting->endpoint[EP_D_OUT-1];
  
  	DBG(2,"endpoint address=%02x,packet size=%d",
  	    endpoint->desc.bEndpointAddress, le16_to_cpu(endpoint->desc.wMaxPacketSize));
  
  	return st5481_setup_isocpipes(d_out->urb, dev, 
  				      usb_sndisocpipe(dev, endpoint->desc.bEndpointAddress),
  				      NUM_ISO_PACKETS_D, SIZE_ISO_PACKETS_D_OUT,
  				      NUM_ISO_PACKETS_D * SIZE_ISO_PACKETS_D_OUT,
  				      usb_d_out_complete, adapter);
  }
  
  static void st5481_release_d_out(struct st5481_adapter *adapter)
  {
  	struct st5481_d_out *d_out = &adapter->d_out;
  
  	DBG(2,"");
  
  	st5481_release_isocpipes(d_out->urb);
  }
  
  int st5481_setup_d(struct st5481_adapter *adapter)
  {
  	int retval;
  
  	DBG(2,"");
  
  	retval = st5481_setup_d_out(adapter);
  	if (retval)
  		goto err;
  	adapter->d_in.bufsize = MAX_DFRAME_LEN_L1;
  	adapter->d_in.num_packets = NUM_ISO_PACKETS_D;
  	adapter->d_in.packet_size = SIZE_ISO_PACKETS_D_IN;
  	adapter->d_in.ep = EP_D_IN | USB_DIR_IN;
  	adapter->d_in.counter = IN_D_COUNTER;
  	adapter->d_in.adapter = adapter;
  	adapter->d_in.hisax_if = &adapter->hisax_d_if.ifc;
  	retval = st5481_setup_in(&adapter->d_in);
  	if (retval)
  		goto err_d_out;
  
  	adapter->l1m.fsm = &l1fsm;
  	adapter->l1m.state = ST_L1_F3;
  	adapter->l1m.debug = st5481_debug & 0x100;
  	adapter->l1m.userdata = adapter;
  	adapter->l1m.printdebug = l1m_debug;
  	FsmInitTimer(&adapter->l1m, &adapter->timer);
  
  	adapter->d_out.fsm.fsm = &dout_fsm;
  	adapter->d_out.fsm.state = ST_DOUT_NONE;
  	adapter->d_out.fsm.debug = st5481_debug & 0x100;
  	adapter->d_out.fsm.userdata = adapter;
  	adapter->d_out.fsm.printdebug = dout_debug;
  
  	return 0;
  
   err_d_out:
  	st5481_release_d_out(adapter);
   err:
  	return retval;
  }
  
  void st5481_release_d(struct st5481_adapter *adapter)
  {
  	DBG(2,"");
  
  	st5481_release_in(&adapter->d_in);
  	st5481_release_d_out(adapter);
  }
  
  /* ======================================================================
   * init / exit
   */
  
  int __init st5481_d_init(void)
  {
  	int retval;
  
  	l1fsm.state_count = L1_STATE_COUNT;
  	l1fsm.event_count = L1_EVENT_COUNT;
  	l1fsm.strEvent = strL1Event;
  	l1fsm.strState = strL1State;
  	retval = FsmNew(&l1fsm, L1FnList, ARRAY_SIZE(L1FnList));
  	if (retval)
  		goto err;
  
  	dout_fsm.state_count = DOUT_STATE_COUNT;
  	dout_fsm.event_count = DOUT_EVENT_COUNT;
  	dout_fsm.strEvent = strDoutEvent;
  	dout_fsm.strState = strDoutState;
  	retval = FsmNew(&dout_fsm, DoutFnList, ARRAY_SIZE(DoutFnList));
  	if (retval)
  		goto err_l1;
  
  	return 0;
  
   err_l1:
  	FsmFree(&l1fsm);
   err:
  	return retval;
  }
  
  // can't be __exit
  void st5481_d_exit(void)
  {
  	FsmFree(&l1fsm);
  	FsmFree(&dout_fsm);
  }
  