| /* | |
| * 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 __printf(2, 3) | |
| 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 __printf(2, 3) | |
| 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); | |
| } |