aboutsummaryrefslogtreecommitdiff
path: root/drivers/s390/net/ctcm_mpc.c
diff options
context:
space:
mode:
authorPeter Tiedemann <ptiedem@de.ibm.com>2008-02-08 00:03:49 +0100
committerJeff Garzik <jeff@garzik.org>2008-03-17 07:49:26 -0400
commit293d984f0e3604c04dcdbf00117ddc1e5d4b1909 (patch)
tree753698fc17e33a9ce98f957eadd894d3f1d9f739 /drivers/s390/net/ctcm_mpc.c
parentf423f73506ba8e837b5fdcd8c8be50078deb123d (diff)
ctcm: infrastructure for replaced ctc driver
ctcm driver supports the channel-to-channel connections of the old ctc driver plus an additional MPC protocol to provide SNA connectivity. This new ctcm driver replaces the existing ctc driver. Signed-off-by: Peter Tiedemann <ptiedem@de.ibm.com> Signed-off-by: Ursula Braun <braunu@de.ibm.com> Signed-off-by: Jeff Garzik <jeff@garzik.org>
Diffstat (limited to 'drivers/s390/net/ctcm_mpc.c')
-rw-r--r--drivers/s390/net/ctcm_mpc.c2472
1 files changed, 2472 insertions, 0 deletions
diff --git a/drivers/s390/net/ctcm_mpc.c b/drivers/s390/net/ctcm_mpc.c
new file mode 100644
index 00000000000..044addee64a
--- /dev/null
+++ b/drivers/s390/net/ctcm_mpc.c
@@ -0,0 +1,2472 @@
+/*
+ * drivers/s390/net/ctcm_mpc.c
+ *
+ * Copyright IBM Corp. 2004, 2007
+ * Authors: Belinda Thompson (belindat@us.ibm.com)
+ * Andy Richter (richtera@us.ibm.com)
+ * Peter Tiedemann (ptiedem@de.ibm.com)
+ */
+
+/*
+ This module exports functions to be used by CCS:
+ EXPORT_SYMBOL(ctc_mpc_alloc_channel);
+ EXPORT_SYMBOL(ctc_mpc_establish_connectivity);
+ EXPORT_SYMBOL(ctc_mpc_dealloc_ch);
+ EXPORT_SYMBOL(ctc_mpc_flow_control);
+*/
+
+#undef DEBUG
+#undef DEBUGDATA
+#undef DEBUGCCW
+
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/interrupt.h>
+#include <linux/timer.h>
+#include <linux/sched.h>
+
+#include <linux/signal.h>
+#include <linux/string.h>
+#include <linux/proc_fs.h>
+
+#include <linux/ip.h>
+#include <linux/if_arp.h>
+#include <linux/tcp.h>
+#include <linux/skbuff.h>
+#include <linux/ctype.h>
+#include <linux/netdevice.h>
+#include <net/dst.h>
+
+#include <linux/io.h> /* instead of <asm/io.h> ok ? */
+#include <asm/ccwdev.h>
+#include <asm/ccwgroup.h>
+#include <linux/bitops.h> /* instead of <asm/bitops.h> ok ? */
+#include <linux/uaccess.h> /* instead of <asm/uaccess.h> ok ? */
+#include <linux/wait.h>
+#include <linux/moduleparam.h>
+#include <asm/idals.h>
+
+#include "cu3088.h"
+#include "ctcm_mpc.h"
+#include "ctcm_main.h"
+#include "ctcm_fsms.h"
+
+static const struct xid2 init_xid = {
+ .xid2_type_id = XID_FM2,
+ .xid2_len = 0x45,
+ .xid2_adj_id = 0,
+ .xid2_rlen = 0x31,
+ .xid2_resv1 = 0,
+ .xid2_flag1 = 0,
+ .xid2_fmtt = 0,
+ .xid2_flag4 = 0x80,
+ .xid2_resv2 = 0,
+ .xid2_tgnum = 0,
+ .xid2_sender_id = 0,
+ .xid2_flag2 = 0,
+ .xid2_option = XID2_0,
+ .xid2_resv3 = "\x00",
+ .xid2_resv4 = 0,
+ .xid2_dlc_type = XID2_READ_SIDE,
+ .xid2_resv5 = 0,
+ .xid2_mpc_flag = 0,
+ .xid2_resv6 = 0,
+ .xid2_buf_len = (MPC_BUFSIZE_DEFAULT - 35),
+};
+
+static const struct th_header thnorm = {
+ .th_seg = 0x00,
+ .th_ch_flag = TH_IS_XID,
+ .th_blk_flag = TH_DATA_IS_XID,
+ .th_is_xid = 0x01,
+ .th_seq_num = 0x00000000,
+};
+
+static const struct th_header thdummy = {
+ .th_seg = 0x00,
+ .th_ch_flag = 0x00,
+ .th_blk_flag = TH_DATA_IS_XID,
+ .th_is_xid = 0x01,
+ .th_seq_num = 0x00000000,
+};
+
+/*
+ * Definition of one MPC group
+ */
+
+/*
+ * Compatibility macros for busy handling
+ * of network devices.
+ */
+
+static void ctcmpc_unpack_skb(struct channel *ch, struct sk_buff *pskb);
+
+/*
+ * MPC Group state machine actions (static prototypes)
+ */
+static void mpc_action_nop(fsm_instance *fsm, int event, void *arg);
+static void mpc_action_go_ready(fsm_instance *fsm, int event, void *arg);
+static void mpc_action_go_inop(fsm_instance *fi, int event, void *arg);
+static void mpc_action_timeout(fsm_instance *fi, int event, void *arg);
+static int mpc_validate_xid(struct mpcg_info *mpcginfo);
+static void mpc_action_yside_xid(fsm_instance *fsm, int event, void *arg);
+static void mpc_action_doxid0(fsm_instance *fsm, int event, void *arg);
+static void mpc_action_doxid7(fsm_instance *fsm, int event, void *arg);
+static void mpc_action_xside_xid(fsm_instance *fsm, int event, void *arg);
+static void mpc_action_rcvd_xid0(fsm_instance *fsm, int event, void *arg);
+static void mpc_action_rcvd_xid7(fsm_instance *fsm, int event, void *arg);
+
+#ifdef DEBUGDATA
+/*-------------------------------------------------------------------*
+* Dump buffer format *
+* *
+*--------------------------------------------------------------------*/
+void ctcmpc_dumpit(char *buf, int len)
+{
+ __u32 ct, sw, rm, dup;
+ char *ptr, *rptr;
+ char tbuf[82], tdup[82];
+ #if (UTS_MACHINE == s390x)
+ char addr[22];
+ #else
+ char addr[12];
+ #endif
+ char boff[12];
+ char bhex[82], duphex[82];
+ char basc[40];
+
+ sw = 0;
+ rptr = ptr = buf;
+ rm = 16;
+ duphex[0] = 0x00;
+ dup = 0;
+
+ for (ct = 0; ct < len; ct++, ptr++, rptr++) {
+ if (sw == 0) {
+ #if (UTS_MACHINE == s390x)
+ sprintf(addr, "%16.16lx", (unsigned long)rptr);
+ #else
+ sprintf(addr, "%8.8X", (__u32)rptr);
+ #endif
+
+ sprintf(boff, "%4.4X", (__u32)ct);
+ bhex[0] = '\0';
+ basc[0] = '\0';
+ }
+ if ((sw == 4) || (sw == 12))
+ strcat(bhex, " ");
+ if (sw == 8)
+ strcat(bhex, " ");
+
+ #if (UTS_MACHINE == s390x)
+ sprintf(tbuf, "%2.2lX", (unsigned long)*ptr);
+ #else
+ sprintf(tbuf, "%2.2X", (__u32)*ptr);
+ #endif
+
+ tbuf[2] = '\0';
+ strcat(bhex, tbuf);
+ if ((0 != isprint(*ptr)) && (*ptr >= 0x20))
+ basc[sw] = *ptr;
+ else
+ basc[sw] = '.';
+
+ basc[sw+1] = '\0';
+ sw++;
+ rm--;
+ if (sw == 16) {
+ if ((strcmp(duphex, bhex)) != 0) {
+ if (dup != 0) {
+ sprintf(tdup, "Duplicate as above "
+ "to %s", addr);
+ printk(KERN_INFO " "
+ " --- %s ---\n", tdup);
+ }
+ printk(KERN_INFO " %s (+%s) : %s [%s]\n",
+ addr, boff, bhex, basc);
+ dup = 0;
+ strcpy(duphex, bhex);
+ } else
+ dup++;
+
+ sw = 0;
+ rm = 16;
+ }
+ } /* endfor */
+
+ if (sw != 0) {
+ for ( ; rm > 0; rm--, sw++) {
+ if ((sw == 4) || (sw == 12))
+ strcat(bhex, " ");
+ if (sw == 8)
+ strcat(bhex, " ");
+ strcat(bhex, " ");
+ strcat(basc, " ");
+ }
+ if (dup != 0) {
+ sprintf(tdup, "Duplicate as above to %s", addr);
+ printk(KERN_INFO " "
+ " --- %s ---\n", tdup);
+ }
+ printk(KERN_INFO " %s (+%s) : %s [%s]\n",
+ addr, boff, bhex, basc);
+ } else {
+ if (dup >= 1) {
+ sprintf(tdup, "Duplicate as above to %s", addr);
+ printk(KERN_INFO " "
+ " --- %s ---\n", tdup);
+ }
+ if (dup != 0) {
+ printk(KERN_INFO " %s (+%s) : %s [%s]\n",
+ addr, boff, bhex, basc);
+ }
+ }
+
+ return;
+
+} /* end of ctcmpc_dumpit */
+#endif
+
+#ifdef DEBUGDATA
+/*
+ * Dump header and first 16 bytes of an sk_buff for debugging purposes.
+ *
+ * skb The sk_buff to dump.
+ * offset Offset relative to skb-data, where to start the dump.
+ */
+void ctcmpc_dump_skb(struct sk_buff *skb, int offset)
+{
+ unsigned char *p = skb->data;
+ struct th_header *header;
+ struct pdu *pheader;
+ int bl = skb->len;
+ int i;
+
+ if (p == NULL)
+ return;
+
+ p += offset;
+ header = (struct th_header *)p;
+
+ printk(KERN_INFO "dump:\n");
+ printk(KERN_INFO "skb len=%d \n", skb->len);
+ if (skb->len > 2) {
+ switch (header->th_ch_flag) {
+ case TH_HAS_PDU:
+ break;
+ case 0x00:
+ case TH_IS_XID:
+ if ((header->th_blk_flag == TH_DATA_IS_XID) &&
+ (header->th_is_xid == 0x01))
+ goto dumpth;
+ case TH_SWEEP_REQ:
+ goto dumpth;
+ case TH_SWEEP_RESP:
+ goto dumpth;
+ default:
+ break;
+ }
+
+ pheader = (struct pdu *)p;
+ printk(KERN_INFO "pdu->offset: %d hex: %04x\n",
+ pheader->pdu_offset, pheader->pdu_offset);
+ printk(KERN_INFO "pdu->flag : %02x\n", pheader->pdu_flag);
+ printk(KERN_INFO "pdu->proto : %02x\n", pheader->pdu_proto);
+ printk(KERN_INFO "pdu->seq : %02x\n", pheader->pdu_seq);
+ goto dumpdata;
+
+dumpth:
+ printk(KERN_INFO "th->seg : %02x\n", header->th_seg);
+ printk(KERN_INFO "th->ch : %02x\n", header->th_ch_flag);
+ printk(KERN_INFO "th->blk_flag: %02x\n", header->th_blk_flag);
+ printk(KERN_INFO "th->type : %s\n",
+ (header->th_is_xid) ? "DATA" : "XID");
+ printk(KERN_INFO "th->seqnum : %04x\n", header->th_seq_num);
+
+ }
+dumpdata:
+ if (bl > 32)
+ bl = 32;
+ printk(KERN_INFO "data: ");
+ for (i = 0; i < bl; i++)
+ printk(KERN_INFO "%02x%s", *p++, (i % 16) ? " " : "\n<7>");
+ printk(KERN_INFO "\n");
+}
+#endif
+
+/*
+ * ctc_mpc_alloc_channel
+ * (exported interface)
+ *
+ * Device Initialization :
+ * ACTPATH driven IO operations
+ */
+int ctc_mpc_alloc_channel(int port_num, void (*callback)(int, int))
+{
+ char device[20];
+ struct net_device *dev;
+ struct mpc_group *grp;
+ struct ctcm_priv *priv;
+
+ ctcm_pr_debug("ctcmpc enter: %s()\n", __FUNCTION__);
+
+ sprintf(device, "%s%i", MPC_DEVICE_NAME, port_num);
+ dev = __dev_get_by_name(&init_net, device);
+
+ if (dev == NULL) {
+ printk(KERN_INFO "ctc_mpc_alloc_channel %s dev=NULL\n", device);
+ return 1;
+ }
+
+ priv = dev->priv;
+ grp = priv->mpcg;
+ if (!grp)
+ return 1;
+
+ grp->allochanfunc = callback;
+ grp->port_num = port_num;
+ grp->port_persist = 1;
+
+ ctcm_pr_debug("ctcmpc: %s called for device %s state=%s\n",
+ __FUNCTION__,
+ dev->name,
+ fsm_getstate_str(grp->fsm));
+
+ switch (fsm_getstate(grp->fsm)) {
+ case MPCG_STATE_INOP:
+ /* Group is in the process of terminating */
+ grp->alloc_called = 1;
+ break;
+ case MPCG_STATE_RESET:
+ /* MPC Group will transition to state */
+ /* MPCG_STATE_XID2INITW iff the minimum number */
+ /* of 1 read and 1 write channel have successfully*/
+ /* activated */
+ /*fsm_newstate(grp->fsm, MPCG_STATE_XID2INITW);*/
+ if (callback)
+ grp->send_qllc_disc = 1;
+ case MPCG_STATE_XID0IOWAIT:
+ fsm_deltimer(&grp->timer);
+ grp->outstanding_xid2 = 0;
+ grp->outstanding_xid7 = 0;
+ grp->outstanding_xid7_p2 = 0;
+ grp->saved_xid2 = NULL;
+ if (callback)
+ ctcm_open(dev);
+ fsm_event(priv->fsm, DEV_EVENT_START, dev);
+ break;
+ case MPCG_STATE_READY:
+ /* XID exchanges completed after PORT was activated */
+ /* Link station already active */
+ /* Maybe timing issue...retry callback */
+ grp->allocchan_callback_retries++;
+ if (grp->allocchan_callback_retries < 4) {
+ if (grp->allochanfunc)
+ grp->allochanfunc(grp->port_num,
+ grp->group_max_buflen);
+ } else {
+ /* there are problems...bail out */
+ /* there may be a state mismatch so restart */
+ grp->port_persist = 1;
+ fsm_event(grp->fsm, MPCG_EVENT_INOP, dev);
+ grp->allocchan_callback_retries = 0;
+ }
+ break;
+ default:
+ return 0;
+
+ }
+
+ ctcm_pr_debug("ctcmpc exit: %s()\n", __FUNCTION__);
+ return 0;
+}
+EXPORT_SYMBOL(ctc_mpc_alloc_channel);
+
+/*
+ * ctc_mpc_establish_connectivity
+ * (exported interface)
+ */
+void ctc_mpc_establish_connectivity(int port_num,
+ void (*callback)(int, int, int))
+{
+ char device[20];
+ struct net_device *dev;
+ struct mpc_group *grp;
+ struct ctcm_priv *priv;
+ struct channel *rch, *wch;
+
+ ctcm_pr_debug("ctcmpc enter: %s()\n", __FUNCTION__);
+
+ sprintf(device, "%s%i", MPC_DEVICE_NAME, port_num);
+ dev = __dev_get_by_name(&init_net, device);
+
+ if (dev == NULL) {
+ printk(KERN_INFO "ctc_mpc_establish_connectivity "
+ "%s dev=NULL\n", device);
+ return;
+ }
+ priv = dev->priv;
+ rch = priv->channel[READ];
+ wch = priv->channel[WRITE];
+
+ grp = priv->mpcg;
+
+ ctcm_pr_debug("ctcmpc: %s() called for device %s state=%s\n",
+ __FUNCTION__, dev->name,
+ fsm_getstate_str(grp->fsm));
+
+ grp->estconnfunc = callback;
+ grp->port_num = port_num;
+
+ switch (fsm_getstate(grp->fsm)) {
+ case MPCG_STATE_READY:
+ /* XID exchanges completed after PORT was activated */
+ /* Link station already active */
+ /* Maybe timing issue...retry callback */
+ fsm_deltimer(&grp->timer);
+ grp->estconn_callback_retries++;
+ if (grp->estconn_callback_retries < 4) {
+ if (grp->estconnfunc) {
+ grp->estconnfunc(grp->port_num, 0,
+ grp->group_max_buflen);
+ grp->estconnfunc = NULL;
+ }
+ } else {
+ /* there are problems...bail out */
+ fsm_event(grp->fsm, MPCG_EVENT_INOP, dev);
+ grp->estconn_callback_retries = 0;
+ }
+ break;
+ case MPCG_STATE_INOP:
+ case MPCG_STATE_RESET:
+ /* MPC Group is not ready to start XID - min num of */
+ /* 1 read and 1 write channel have not been acquired*/
+ printk(KERN_WARNING "ctcmpc: %s() REJECTED ACTIVE XID Req"
+ "uest - Channel Pair is not Active\n", __FUNCTION__);
+ if (grp->estconnfunc) {
+ grp->estconnfunc(grp->port_num, -1, 0);
+ grp->estconnfunc = NULL;
+ }
+ break;
+ case MPCG_STATE_XID2INITW:
+ /* alloc channel was called but no XID exchange */
+ /* has occurred. initiate xside XID exchange */
+ /* make sure yside XID0 processing has not started */
+ if ((fsm_getstate(rch->fsm) > CH_XID0_PENDING) ||
+ (fsm_getstate(wch->fsm) > CH_XID0_PENDING)) {
+ printk(KERN_WARNING "mpc: %s() ABORT ACTIVE XID"
+ " Request- PASSIVE XID in process\n"
+ , __FUNCTION__);
+ break;
+ }
+ grp->send_qllc_disc = 1;
+ fsm_newstate(grp->fsm, MPCG_STATE_XID0IOWAIT);
+ fsm_deltimer(&grp->timer);
+ fsm_addtimer(&grp->timer, MPC_XID_TIMEOUT_VALUE,
+ MPCG_EVENT_TIMER, dev);
+ grp->outstanding_xid7 = 0;
+ grp->outstanding_xid7_p2 = 0;
+ grp->saved_xid2 = NULL;
+ if ((rch->in_mpcgroup) &&
+ (fsm_getstate(rch->fsm) == CH_XID0_PENDING))
+ fsm_event(grp->fsm, MPCG_EVENT_XID0DO, rch);
+ else {
+ printk(KERN_WARNING "mpc: %s() Unable to start"
+ " ACTIVE XID0 on read channel\n",
+ __FUNCTION__);
+ if (grp->estconnfunc) {
+ grp->estconnfunc(grp->port_num, -1, 0);
+ grp->estconnfunc = NULL;
+ }
+ fsm_deltimer(&grp->timer);
+ goto done;
+ }
+ if ((wch->in_mpcgroup) &&
+ (fsm_getstate(wch->fsm) == CH_XID0_PENDING))
+ fsm_event(grp->fsm, MPCG_EVENT_XID0DO, wch);
+ else {
+ printk(KERN_WARNING "mpc: %s() Unable to start"
+ " ACTIVE XID0 on write channel\n",
+ __FUNCTION__);
+ if (grp->estconnfunc) {
+ grp->estconnfunc(grp->port_num, -1, 0);
+ grp->estconnfunc = NULL;
+ }
+ fsm_deltimer(&grp->timer);
+ goto done;
+ }
+ break;
+ case MPCG_STATE_XID0IOWAIT:
+ /* already in active XID negotiations */
+ default:
+ break;
+ }
+
+done:
+ ctcm_pr_debug("ctcmpc exit: %s()\n", __FUNCTION__);
+ return;
+}
+EXPORT_SYMBOL(ctc_mpc_establish_connectivity);
+
+/*
+ * ctc_mpc_dealloc_ch
+ * (exported interface)
+ */
+void ctc_mpc_dealloc_ch(int port_num)
+{
+ struct net_device *dev;
+ char device[20];
+ struct ctcm_priv *priv;
+ struct mpc_group *grp;
+
+ ctcm_pr_debug("ctcmpc enter: %s()\n", __FUNCTION__);
+ sprintf(device, "%s%i", MPC_DEVICE_NAME, port_num);
+ dev = __dev_get_by_name(&init_net, device);
+
+ if (dev == NULL) {
+ printk(KERN_INFO "%s() %s dev=NULL\n", __FUNCTION__, device);
+ goto done;
+ }
+
+ ctcm_pr_debug("ctcmpc:%s %s() called for device %s refcount=%d\n",
+ dev->name, __FUNCTION__,
+ dev->name, atomic_read(&dev->refcnt));
+
+ priv = dev->priv;
+ if (priv == NULL) {
+ printk(KERN_INFO "%s() %s priv=NULL\n",
+ __FUNCTION__, device);
+ goto done;
+ }
+ fsm_deltimer(&priv->restart_timer);
+
+ grp = priv->mpcg;
+ if (grp == NULL) {
+ printk(KERN_INFO "%s() %s dev=NULL\n", __FUNCTION__, device);
+ goto done;
+ }
+ grp->channels_terminating = 0;
+
+ fsm_deltimer(&grp->timer);
+
+ grp->allochanfunc = NULL;
+ grp->estconnfunc = NULL;
+ grp->port_persist = 0;
+ grp->send_qllc_disc = 0;
+ fsm_event(grp->fsm, MPCG_EVENT_INOP, dev);
+
+ ctcm_close(dev);
+done:
+ ctcm_pr_debug("ctcmpc exit: %s()\n", __FUNCTION__);
+ return;
+}
+EXPORT_SYMBOL(ctc_mpc_dealloc_ch);
+
+/*
+ * ctc_mpc_flow_control
+ * (exported interface)
+ */
+void ctc_mpc_flow_control(int port_num, int flowc)
+{
+ char device[20];
+ struct ctcm_priv *priv;
+ struct mpc_group *grp;
+ struct net_device *dev;
+ struct channel *rch;
+ int mpcg_state;
+
+ ctcm_pr_debug("ctcmpc enter: %s() %i\n", __FUNCTION__, flowc);
+
+ sprintf(device, "%s%i", MPC_DEVICE_NAME, port_num);
+ dev = __dev_get_by_name(&init_net, device);
+
+ if (dev == NULL) {
+ printk(KERN_INFO "ctc_mpc_flow_control %s dev=NULL\n", device);
+ return;
+ }
+
+ ctcm_pr_debug("ctcmpc: %s %s called \n", dev->name, __FUNCTION__);
+
+ priv = dev->priv;
+ if (priv == NULL) {
+ printk(KERN_INFO "ctcmpc:%s() %s priv=NULL\n",
+ __FUNCTION__, device);
+ return;
+ }
+ grp = priv->mpcg;
+ rch = priv->channel[READ];
+
+ mpcg_state = fsm_getstate(grp->fsm);
+ switch (flowc) {
+ case 1:
+ if (mpcg_state == MPCG_STATE_FLOWC)
+ break;
+ if (mpcg_state == MPCG_STATE_READY) {
+ if (grp->flow_off_called == 1)
+ grp->flow_off_called = 0;
+ else
+ fsm_newstate(grp->fsm, MPCG_STATE_FLOWC);
+ break;
+ }
+ break;
+ case 0:
+ if (mpcg_state == MPCG_STATE_FLOWC) {
+ fsm_newstate(grp->fsm, MPCG_STATE_READY);
+ /* ensure any data that has accumulated */
+ /* on the io_queue will now be sen t */
+ tasklet_schedule(&rch->ch_tasklet);
+ }
+ /* possible race condition */
+ if (mpcg_state == MPCG_STATE_READY) {
+ grp->flow_off_called = 1;
+ break;
+ }
+ break;
+ }
+
+ ctcm_pr_debug("ctcmpc exit: %s() %i\n", __FUNCTION__, flowc);
+}
+EXPORT_SYMBOL(ctc_mpc_flow_control);
+
+static int mpc_send_qllc_discontact(struct net_device *);
+
+/*
+ * helper function of ctcmpc_unpack_skb
+*/
+static void mpc_rcvd_sweep_resp(struct mpcg_info *mpcginfo)
+{
+ struct channel *rch = mpcginfo->ch;
+ struct net_device *dev = rch->netdev;
+ struct ctcm_priv *priv = dev->priv;
+ struct mpc_group *grp = priv->mpcg;
+ struct channel *ch = priv->channel[WRITE];
+
+ if (do_debug)
+ ctcm_pr_debug("ctcmpc enter: %s(): ch=0x%p id=%s\n",
+ __FUNCTION__, ch, ch->id);
+
+ if (do_debug_data)
+ ctcmpc_dumpit((char *)mpcginfo->sweep, TH_SWEEP_LENGTH);
+
+ grp->sweep_rsp_pend_num--;
+
+ if ((grp->sweep_req_pend_num == 0) &&
+ (grp->sweep_rsp_pend_num == 0)) {
+ fsm_deltimer(&ch->sweep_timer);
+ grp->in_sweep = 0;
+ rch->th_seq_num = 0x00;
+ ch->th_seq_num = 0x00;
+ ctcm_clear_busy_do(dev);
+ }
+
+ kfree(mpcginfo);
+
+ return;
+
+}
+
+/*
+ * helper function of mpc_rcvd_sweep_req
+ * which is a helper of ctcmpc_unpack_skb
+ */
+static void ctcmpc_send_sweep_resp(struct channel *rch)
+{
+ struct net_device *dev = rch->netdev;
+ struct ctcm_priv *priv = dev->priv;
+ struct mpc_group *grp = priv->mpcg;
+ int rc = 0;
+ struct th_sweep *header;
+ struct sk_buff *sweep_skb;
+ struct channel *ch = priv->channel[WRITE];
+
+ if (do_debug)
+ ctcm_pr_debug("ctcmpc exit : %s(): ch=0x%p id=%s\n",
+ __FUNCTION__, rch, rch->id);
+
+ sweep_skb = __dev_alloc_skb(MPC_BUFSIZE_DEFAULT,
+ GFP_ATOMIC|GFP_DMA);
+ if (sweep_skb == NULL) {
+ printk(KERN_INFO "Couldn't alloc sweep_skb\n");
+ rc = -ENOMEM;
+ goto done;
+ }
+
+ header = (struct th_sweep *)
+ kmalloc(sizeof(struct th_sweep), gfp_type());
+
+ if (!header) {
+ dev_kfree_skb_any(sweep_skb);
+ rc = -ENOMEM;
+ goto done;
+ }
+
+ header->th.th_seg = 0x00 ;
+ header->th.th_ch_flag = TH_SWEEP_RESP;
+ header->th.th_blk_flag = 0x00;
+ header->th.th_is_xid = 0x00;
+ header->th.th_seq_num = 0x00;
+ header->sw.th_last_seq = ch->th_seq_num;
+
+ memcpy(skb_put(sweep_skb, TH_SWEEP_LENGTH), header, TH_SWEEP_LENGTH);
+
+ kfree(header);
+
+ dev->trans_start = jiffies;
+ skb_queue_tail(&ch->sweep_queue, sweep_skb);
+
+ fsm_addtimer(&ch->sweep_timer, 100, CTC_EVENT_RSWEEP_TIMER, ch);
+
+ return;
+
+done:
+ if (rc != 0) {
+ grp->in_sweep = 0;
+ ctcm_clear_busy_do(dev);
+ fsm_event(grp->fsm, MPCG_EVENT_INOP, dev);
+ }
+
+ return;
+}
+
+/*
+ * helper function of ctcmpc_unpack_skb
+ */
+static void mpc_rcvd_sweep_req(struct mpcg_info *mpcginfo)
+{
+ struct channel *rch = mpcginfo->ch;
+ struct net_device *dev = rch->netdev;
+ struct ctcm_priv *priv = dev->priv;
+ struct mpc_group *grp = priv->mpcg;
+ struct channel *ch = priv->channel[WRITE];
+
+ if (do_debug)
+ CTCM_DBF_TEXT_(MPC_TRACE, CTC_DBF_DEBUG,
+ " %s(): ch=0x%p id=%s\n", __FUNCTION__, ch, ch->id);
+
+ if (grp->in_sweep == 0) {
+ grp->in_sweep = 1;
+ ctcm_test_and_set_busy(dev);
+ grp->sweep_req_pend_num = grp->active_channels[READ];
+ grp->sweep_rsp_pend_num = grp->active_channels[READ];
+ }
+
+ if (do_debug_data)
+ ctcmpc_dumpit((char *)mpcginfo->sweep, TH_SWEEP_LENGTH);
+
+ grp->sweep_req_pend_num--;
+ ctcmpc_send_sweep_resp(ch);
+ kfree(mpcginfo);
+ return;
+}
+
+/*
+ * MPC Group Station FSM definitions
+ */
+static const char *mpcg_event_names[] = {
+ [MPCG_EVENT_INOP] = "INOP Condition",
+ [MPCG_EVENT_DISCONC] = "Discontact Received",
+ [MPCG_EVENT_XID0DO] = "Channel Active - Start XID",
+ [MPCG_EVENT_XID2] = "XID2 Received",
+ [MPCG_EVENT_XID2DONE] = "XID0 Complete",
+ [MPCG_EVENT_XID7DONE] = "XID7 Complete",
+ [MPCG_EVENT_TIMER] = "XID Setup Timer",
+ [MPCG_EVENT_DOIO] = "XID DoIO",
+};
+
+static const char *mpcg_state_names[] = {
+ [MPCG_STATE_RESET] = "Reset",
+ [MPCG_STATE_INOP] = "INOP",
+ [MPCG_STATE_XID2INITW] = "Passive XID- XID0 Pending Start",
+ [MPCG_STATE_XID2INITX] = "Passive XID- XID0 Pending Complete",
+ [MPCG_STATE_XID7INITW] = "Passive XID- XID7 Pending P1 Start",
+ [MPCG_STATE_XID7INITX] = "Passive XID- XID7 Pending P2 Complete",
+ [MPCG_STATE_XID0IOWAIT] = "Active XID- XID0 Pending Start",
+ [MPCG_STATE_XID0IOWAIX] = "Active XID- XID0 Pending Complete",
+ [MPCG_STATE_XID7INITI] = "Active XID- XID7 Pending Start",
+ [MPCG_STATE_XID7INITZ] = "Active XID- XID7 Pending Complete ",
+ [MPCG_STATE_XID7INITF] = "XID - XID7 Complete ",
+ [MPCG_STATE_FLOWC] = "FLOW CONTROL ON",
+ [MPCG_STATE_READY] = "READY",
+};
+
+/*
+ * The MPC Group Station FSM
+ * 22 events
+ */
+static const fsm_node mpcg_fsm[] = {
+ { MPCG_STATE_RESET, MPCG_EVENT_INOP, mpc_action_go_inop },
+ { MPCG_STATE_INOP, MPCG_EVENT_INOP, mpc_action_nop },
+ { MPCG_STATE_FLOWC, MPCG_EVENT_INOP, mpc_action_go_inop },
+
+ { MPCG_STATE_READY, MPCG_EVENT_DISCONC, mpc_action_discontact },
+ { MPCG_STATE_READY, MPCG_EVENT_INOP, mpc_action_go_inop },
+
+ { MPCG_STATE_XID2INITW, MPCG_EVENT_XID0DO, mpc_action_doxid0 },
+ { MPCG_STATE_XID2INITW, MPCG_EVENT_XID2, mpc_action_rcvd_xid0 },
+ { MPCG_STATE_XID2INITW, MPCG_EVENT_INOP, mpc_action_go_inop },
+ { MPCG_STATE_XID2INITW, MPCG_EVENT_TIMER, mpc_action_timeout },
+ { MPCG_STATE_XID2INITW, MPCG_EVENT_DOIO, mpc_action_yside_xid },
+
+ { MPCG_STATE_XID2INITX, MPCG_EVENT_XID0DO, mpc_action_doxid0 },
+ { MPCG_STATE_XID2INITX, MPCG_EVENT_XID2, mpc_action_rcvd_xid0 },
+ { MPCG_STATE_XID2INITX, MPCG_EVENT_INOP, mpc_action_go_inop },
+ { MPCG_STATE_XID2INITX, MPCG_EVENT_TIMER, mpc_action_timeout },
+ { MPCG_STATE_XID2INITX, MPCG_EVENT_DOIO, mpc_action_yside_xid },
+
+ { MPCG_STATE_XID7INITW, MPCG_EVENT_XID2DONE, mpc_action_doxid7 },
+ { MPCG_STATE_XID7INITW, MPCG_EVENT_DISCONC, mpc_action_discontact },
+ { MPCG_STATE_XID7INITW, MPCG_EVENT_XID2, mpc_action_rcvd_xid7 },
+ { MPCG_STATE_XID7INITW, MPCG_EVENT_INOP, mpc_action_go_inop },
+ { MPCG_STATE_XID7INITW, MPCG_EVENT_TIMER, mpc_action_timeout },
+ { MPCG_STATE_XID7INITW, MPCG_EVENT_XID7DONE, mpc_action_doxid7 },
+ { MPCG_STATE_XID7INITW, MPCG_EVENT_DOIO, mpc_action_yside_xid },
+
+ { MPCG_STATE_XID7INITX, MPCG_EVENT_DISCONC, mpc_action_discontact },
+ { MPCG_STATE_XID7INITX, MPCG_EVENT_XID2, mpc_action_rcvd_xid7 },
+ { MPCG_STATE_XID7INITX, MPCG_EVENT_INOP, mpc_action_go_inop },
+ { MPCG_STATE_XID7INITX, MPCG_EVENT_XID7DONE, mpc_action_doxid7 },
+ { MPCG_STATE_XID7INITX, MPCG_EVENT_TIMER, mpc_action_timeout },
+ { MPCG_STATE_XID7INITX, MPCG_EVENT_DOIO, mpc_action_yside_xid },
+
+ { MPCG_STATE_XID0IOWAIT, MPCG_EVENT_XID0DO, mpc_action_doxid0 },
+ { MPCG_STATE_XID0IOWAIT, MPCG_EVENT_DISCONC, mpc_action_discontact },
+ { MPCG_STATE_XID0IOWAIT, MPCG_EVENT_XID2, mpc_action_rcvd_xid0 },
+ { MPCG_STATE_XID0IOWAIT, MPCG_EVENT_INOP, mpc_action_go_inop },
+ { MPCG_STATE_XID0IOWAIT, MPCG_EVENT_TIMER, mpc_action_timeout },
+ { MPCG_STATE_XID0IOWAIT, MPCG_EVENT_DOIO, mpc_action_xside_xid },
+
+ { MPCG_STATE_XID0IOWAIX, MPCG_EVENT_XID0DO, mpc_action_doxid0 },
+ { MPCG_STATE_XID0IOWAIX, MPCG_EVENT_DISCONC, mpc_action_discontact },
+ { MPCG_STATE_XID0IOWAIX, MPCG_EVENT_XID2, mpc_action_rcvd_xid0 },
+ { MPCG_STATE_XID0IOWAIX, MPCG_EVENT_INOP, mpc_action_go_inop },
+ { MPCG_STATE_XID0IOWAIX, MPCG_EVENT_TIMER, mpc_action_timeout },
+ { MPCG_STATE_XID0IOWAIX, MPCG_EVENT_DOIO, mpc_action_xside_xid },
+
+ { MPCG_STATE_XID7INITI, MPCG_EVENT_XID2DONE, mpc_action_doxid7 },
+ { MPCG_STATE_XID7INITI, MPCG_EVENT_XID2, mpc_action_rcvd_xid7 },
+ { MPCG_STATE_XID7INITI, MPCG_EVENT_DISCONC, mpc_action_discontact },
+ { MPCG_STATE_XID7INITI, MPCG_EVENT_INOP, mpc_action_go_inop },
+ { MPCG_STATE_XID7INITI, MPCG_EVENT_TIMER, mpc_action_timeout },
+ { MPCG_STATE_XID7INITI, MPCG_EVENT_XID7DONE, mpc_action_doxid7 },
+ { MPCG_STATE_XID7INITI, MPCG_EVENT_DOIO, mpc_action_xside_xid },
+
+ { MPCG_STATE_XID7INITZ, MPCG_EVENT_XID2, mpc_action_rcvd_xid7 },
+ { MPCG_STATE_XID7INITZ, MPCG_EVENT_XID7DONE, mpc_action_doxid7 },
+ { MPCG_STATE_XID7INITZ, MPCG_EVENT_DISCONC, mpc_action_discontact },
+ { MPCG_STATE_XID7INITZ, MPCG_EVENT_INOP, mpc_action_go_inop },
+ { MPCG_STATE_XID7INITZ, MPCG_EVENT_TIMER, mpc_action_timeout },
+ { MPCG_STATE_XID7INITZ, MPCG_EVENT_DOIO, mpc_action_xside_xid },
+
+ { MPCG_STATE_XID7INITF, MPCG_EVENT_INOP, mpc_action_go_inop },
+ { MPCG_STATE_XID7INITF, MPCG_EVENT_XID7DONE, mpc_action_go_ready },
+};
+
+static int mpcg_fsm_len = ARRAY_SIZE(mpcg_fsm);
+
+/*
+ * MPC Group Station FSM action
+ * CTCM_PROTO_MPC only
+ */
+static void mpc_action_go_ready(fsm_instance *fsm, int event, void *arg)
+{
+ struct net_device *dev = arg;
+ struct ctcm_priv *priv = NULL;
+ struct mpc_group *grp = NULL;
+
+ if (dev == NULL) {
+ printk(KERN_INFO "%s() dev=NULL\n", __FUNCTION__);
+ return;
+ }
+
+ ctcm_pr_debug("ctcmpc enter: %s %s()\n", dev->name, __FUNCTION__);
+
+ priv = dev->priv;
+ if (priv == NULL) {
+ printk(KERN_INFO "%s() priv=NULL\n", __FUNCTION__);
+ return;
+ }
+
+ grp = priv->mpcg;
+ if (grp == NULL) {
+ printk(KERN_INFO "%s() grp=NULL\n", __FUNCTION__);
+ return;
+ }
+
+ fsm_deltimer(&grp->timer);
+
+ if (grp->saved_xid2->xid2_flag2 == 0x40) {
+ priv->xid->xid2_flag2 = 0x00;
+ if (grp->estconnfunc) {
+ grp->estconnfunc(grp->port_num, 1,
+ grp->group_max_buflen);
+ grp->estconnfunc = NULL;
+ } else if (grp->allochanfunc)
+ grp->send_qllc_disc = 1;
+ goto done;
+ }
+
+ grp->port_persist = 1;
+ grp->out_of_sequence = 0;
+ grp->estconn_called = 0;
+
+ tasklet_hi_schedule(&grp->mpc_tasklet2);
+
+ ctcm_pr_debug("ctcmpc exit: %s %s()\n", dev->name, __FUNCTION__);
+ return;
+
+done:
+ fsm_event(grp->fsm, MPCG_EVENT_INOP, dev);
+
+
+ ctcm_pr_info("ctcmpc: %s()failure occurred\n", __FUNCTION__);
+}
+
+/*
+ * helper of ctcm_init_netdevice
+ * CTCM_PROTO_MPC only
+ */
+void mpc_group_ready(unsigned long adev)
+{
+ struct net_device *dev = (struct net_device *)adev;
+ struct ctcm_priv *priv = NULL;
+ struct mpc_group *grp = NULL;
+ struct channel *ch = NULL;
+
+
+ ctcm_pr_debug("ctcmpc enter: %s()\n", __FUNCTION__);
+
+ if (dev == NULL) {
+ printk(KERN_INFO "%s() dev=NULL\n", __FUNCTION__);
+ return;
+ }
+
+ priv = dev->priv;
+ if (priv == NULL) {
+ printk(KERN_INFO "%s() priv=NULL\n", __FUNCTION__);
+ return;
+ }
+
+ grp = priv->mpcg;
+ if (grp == NULL) {
+ printk(KERN_INFO "ctcmpc:%s() grp=NULL\n", __FUNCTION__);
+ return;
+ }
+
+ printk(KERN_NOTICE "ctcmpc: %s GROUP TRANSITIONED TO READY"
+ " maxbuf:%d\n",
+ dev->name, grp->group_max_buflen);
+
+ fsm_newstate(grp->fsm, MPCG_STATE_READY);
+
+ /* Put up a read on the channel */
+ ch = priv->channel[READ];
+ ch->pdu_seq = 0;
+ if (do_debug_data)
+ ctcm_pr_debug("ctcmpc: %s() ToDCM_pdu_seq= %08x\n" ,
+ __FUNCTION__, ch->pdu_seq);
+
+ ctcmpc_chx_rxidle(ch->fsm, CTC_EVENT_START, ch);
+ /* Put the write channel in idle state */
+ ch = priv->channel[WRITE];
+ if (ch->collect_len > 0) {
+ spin_lock(&ch->collect_lock);
+ ctcm_purge_skb_queue(&ch->collect_queue);
+ ch->collect_len = 0;
+ spin_unlock(&ch->collect_lock);
+ }
+ ctcm_chx_txidle(ch->fsm, CTC_EVENT_START, ch);
+
+ ctcm_clear_busy(dev);
+
+ if (grp->estconnfunc) {
+ grp->estconnfunc(grp->port_num, 0,
+ grp->group_max_buflen);
+ grp->estconnfunc = NULL;
+ } else
+ if (grp->allochanfunc)
+ grp->allochanfunc(grp->port_num,
+ grp->group_max_buflen);
+
+ grp->send_qllc_disc = 1;
+ grp->changed_side = 0;
+
+ ctcm_pr_debug("ctcmpc exit: %s()\n", __FUNCTION__);
+ return;
+
+}
+
+/*
+ * Increment the MPC Group Active Channel Counts
+ * helper of dev_action (called from channel fsm)
+ */
+int mpc_channel_action(struct channel *ch, int direction, int action)
+{
+ struct net_device *dev = ch->netdev;
+ struct ctcm_priv *priv;
+ struct mpc_group *grp = NULL;
+ int rc = 0;
+
+ if (do_debug)
+ ctcm_pr_debug("ctcmpc enter: %s(): ch=0x%p id=%s\n",
+ __FUNCTION__, ch, ch->id);
+
+ if (dev == NULL) {
+ printk(KERN_INFO "ctcmpc_channel_action %i dev=NULL\n",
+ action);
+ rc = 1;
+ goto done;
+ }
+
+ priv = dev->priv;
+ if (priv == NULL) {
+ printk(KERN_INFO
+ "ctcmpc_channel_action%i priv=NULL, dev=%s\n",
+ action, dev->name);
+ rc = 2;
+ goto done;
+ }
+
+ grp = priv->mpcg;
+
+ if (grp == NULL) {
+ printk(KERN_INFO "ctcmpc: %s()%i mpcgroup=NULL, dev=%s\n",
+ __FUNCTION__, action, dev->name);
+ rc = 3;
+ goto done;
+ }
+
+ ctcm_pr_info(
+ "ctcmpc: %s() %i(): Grp:%s total_channel_paths=%i "
+ "active_channels read=%i, write=%i\n",
+ __FUNCTION__,
+ action,
+ fsm_getstate_str(grp->fsm),
+ grp->num_channel_paths,
+ grp->active_channels[READ],
+ grp->active_channels[WRITE]);
+
+ if ((action == MPC_CHANNEL_ADD) && (ch->in_mpcgroup == 0)) {
+ grp->num_channel_paths++;
+ grp->active_channels[direction]++;
+ grp->outstanding_xid2++;
+ ch->in_mpcgroup = 1;
+
+ if (ch->xid_skb != NULL)
+ dev_kfree_skb_any(ch->xid_skb);
+
+ ch->xid_skb = __dev_alloc_skb(MPC_BUFSIZE_DEFAULT,
+ GFP_ATOMIC | GFP_DMA);
+ if (ch->xid_skb == NULL) {
+ printk(KERN_INFO "ctcmpc: %s()"
+ "Couldn't alloc ch xid_skb\n", __FUNCTION__);
+ fsm_event(grp->fsm, MPCG_EVENT_INOP, dev);
+ return 1;
+ }
+ ch->xid_skb_data = ch->xid_skb->data;
+ ch->xid_th = (struct th_header *)ch->xid_skb->data;
+ skb_put(ch->xid_skb, TH_HEADER_LENGTH);
+ ch->xid = (struct xid2 *)skb_tail_pointer(ch->xid_skb);
+ skb_put(ch->xid_skb, XID2_LENGTH);
+ ch->xid_id = skb_tail_pointer(ch->xid_skb);
+ ch->xid_skb->data = ch->xid_skb_data;
+ skb_reset_tail_pointer(ch->xid_skb);
+ ch->xid_skb->len = 0;
+
+ memcpy(skb_put(ch->xid_skb, grp->xid_skb->len),
+ grp->xid_skb->data,
+ grp->xid_skb->len);
+
+ ch->xid->xid2_dlc_type = ((CHANNEL_DIRECTION(ch->flags) == READ)
+ ? XID2_READ_SIDE : XID2_WRITE_SIDE);
+
+ if (CHANNEL_DIRECTION(ch->flags) == WRITE)
+ ch->xid->xid2_buf_len = 0x00;
+
+ ch->xid_skb->data = ch->xid_skb_data;
+ skb_reset_tail_pointer(ch->xid_skb);
+ ch->xid_skb->len = 0;
+
+ fsm_newstate(ch->fsm, CH_XID0_PENDING);
+
+ if ((grp->active_channels[READ] > 0) &&
+ (grp->active_channels[WRITE] > 0) &&
+ (fsm_getstate(grp->fsm) < MPCG_STATE_XID2INITW)) {
+ fsm_newstate(grp->fsm, MPCG_STATE_XID2INITW);
+ printk(KERN_NOTICE "ctcmpc: %s MPC GROUP "
+ "CHANNELS ACTIVE\n", dev->name);
+ }
+ } else if ((action == MPC_CHANNEL_REMOVE) &&
+ (ch->in_mpcgroup == 1)) {
+ ch->in_mpcgroup = 0;
+ grp->num_channel_paths--;
+ grp->active_channels[direction]--;
+
+ if (ch->xid_skb != NULL)
+ dev_kfree_skb_any(ch->xid_skb);
+ ch->xid_skb = NULL;
+
+ if (grp->channels_terminating)
+ goto done;
+
+ if (((grp->active_channels[READ] == 0) &&
+ (grp->active_channels[WRITE] > 0))
+ || ((grp->active_channels[WRITE] == 0) &&
+ (grp->active_channels[READ] > 0)))
+ fsm_event(grp->fsm, MPCG_EVENT_INOP, dev);
+ }
+
+done:
+
+ if (do_debug) {
+ ctcm_pr_debug(
+ "ctcmpc: %s() %i Grp:%s ttl_chan_paths=%i "
+ "active_chans read=%i, write=%i\n",
+ __FUNCTION__,
+ action,
+ fsm_getstate_str(grp->fsm),
+ grp->num_channel_paths,
+ grp->active_channels[READ],
+ grp->active_channels[WRITE]);
+
+ ctcm_pr_debug("ctcmpc exit : %s(): ch=0x%p id=%s\n",
+ __FUNCTION__, ch, ch->id);
+ }
+ return rc;
+
+}
+