18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *	Copyright IBM Corp. 2004, 2007
48c2ecf20Sopenharmony_ci *	Authors:	Belinda Thompson (belindat@us.ibm.com)
58c2ecf20Sopenharmony_ci *			Andy Richter (richtera@us.ibm.com)
68c2ecf20Sopenharmony_ci *			Peter Tiedemann (ptiedem@de.ibm.com)
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci/*
108c2ecf20Sopenharmony_ci	This module exports functions to be used by CCS:
118c2ecf20Sopenharmony_ci	EXPORT_SYMBOL(ctc_mpc_alloc_channel);
128c2ecf20Sopenharmony_ci	EXPORT_SYMBOL(ctc_mpc_establish_connectivity);
138c2ecf20Sopenharmony_ci	EXPORT_SYMBOL(ctc_mpc_dealloc_ch);
148c2ecf20Sopenharmony_ci	EXPORT_SYMBOL(ctc_mpc_flow_control);
158c2ecf20Sopenharmony_ci*/
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci#undef DEBUG
188c2ecf20Sopenharmony_ci#undef DEBUGDATA
198c2ecf20Sopenharmony_ci#undef DEBUGCCW
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci#define KMSG_COMPONENT "ctcm"
228c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci#include <linux/module.h>
258c2ecf20Sopenharmony_ci#include <linux/init.h>
268c2ecf20Sopenharmony_ci#include <linux/kernel.h>
278c2ecf20Sopenharmony_ci#include <linux/slab.h>
288c2ecf20Sopenharmony_ci#include <linux/errno.h>
298c2ecf20Sopenharmony_ci#include <linux/types.h>
308c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
318c2ecf20Sopenharmony_ci#include <linux/timer.h>
328c2ecf20Sopenharmony_ci#include <linux/sched.h>
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci#include <linux/signal.h>
358c2ecf20Sopenharmony_ci#include <linux/string.h>
368c2ecf20Sopenharmony_ci#include <linux/proc_fs.h>
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci#include <linux/ip.h>
398c2ecf20Sopenharmony_ci#include <linux/if_arp.h>
408c2ecf20Sopenharmony_ci#include <linux/tcp.h>
418c2ecf20Sopenharmony_ci#include <linux/skbuff.h>
428c2ecf20Sopenharmony_ci#include <linux/ctype.h>
438c2ecf20Sopenharmony_ci#include <linux/netdevice.h>
448c2ecf20Sopenharmony_ci#include <net/dst.h>
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci#include <linux/io.h>		/* instead of <asm/io.h> ok ? */
478c2ecf20Sopenharmony_ci#include <asm/ccwdev.h>
488c2ecf20Sopenharmony_ci#include <asm/ccwgroup.h>
498c2ecf20Sopenharmony_ci#include <linux/bitops.h>	/* instead of <asm/bitops.h> ok ? */
508c2ecf20Sopenharmony_ci#include <linux/uaccess.h>	/* instead of <asm/uaccess.h> ok ? */
518c2ecf20Sopenharmony_ci#include <linux/wait.h>
528c2ecf20Sopenharmony_ci#include <linux/moduleparam.h>
538c2ecf20Sopenharmony_ci#include <asm/idals.h>
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci#include "ctcm_main.h"
568c2ecf20Sopenharmony_ci#include "ctcm_mpc.h"
578c2ecf20Sopenharmony_ci#include "ctcm_fsms.h"
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_cistatic const struct xid2 init_xid = {
608c2ecf20Sopenharmony_ci	.xid2_type_id	=	XID_FM2,
618c2ecf20Sopenharmony_ci	.xid2_len	=	0x45,
628c2ecf20Sopenharmony_ci	.xid2_adj_id	=	0,
638c2ecf20Sopenharmony_ci	.xid2_rlen	=	0x31,
648c2ecf20Sopenharmony_ci	.xid2_resv1	=	0,
658c2ecf20Sopenharmony_ci	.xid2_flag1	=	0,
668c2ecf20Sopenharmony_ci	.xid2_fmtt	=	0,
678c2ecf20Sopenharmony_ci	.xid2_flag4	=	0x80,
688c2ecf20Sopenharmony_ci	.xid2_resv2	=	0,
698c2ecf20Sopenharmony_ci	.xid2_tgnum	=	0,
708c2ecf20Sopenharmony_ci	.xid2_sender_id	=	0,
718c2ecf20Sopenharmony_ci	.xid2_flag2	=	0,
728c2ecf20Sopenharmony_ci	.xid2_option	=	XID2_0,
738c2ecf20Sopenharmony_ci	.xid2_resv3	=	"\x00",
748c2ecf20Sopenharmony_ci	.xid2_resv4	=	0,
758c2ecf20Sopenharmony_ci	.xid2_dlc_type	=	XID2_READ_SIDE,
768c2ecf20Sopenharmony_ci	.xid2_resv5	=	0,
778c2ecf20Sopenharmony_ci	.xid2_mpc_flag	=	0,
788c2ecf20Sopenharmony_ci	.xid2_resv6	=	0,
798c2ecf20Sopenharmony_ci	.xid2_buf_len	=	(MPC_BUFSIZE_DEFAULT - 35),
808c2ecf20Sopenharmony_ci};
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_cistatic const struct th_header thnorm = {
838c2ecf20Sopenharmony_ci	.th_seg		=	0x00,
848c2ecf20Sopenharmony_ci	.th_ch_flag	=	TH_IS_XID,
858c2ecf20Sopenharmony_ci	.th_blk_flag	=	TH_DATA_IS_XID,
868c2ecf20Sopenharmony_ci	.th_is_xid	=	0x01,
878c2ecf20Sopenharmony_ci	.th_seq_num	=	0x00000000,
888c2ecf20Sopenharmony_ci};
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_cistatic const struct th_header thdummy = {
918c2ecf20Sopenharmony_ci	.th_seg		=	0x00,
928c2ecf20Sopenharmony_ci	.th_ch_flag	=	0x00,
938c2ecf20Sopenharmony_ci	.th_blk_flag	=	TH_DATA_IS_XID,
948c2ecf20Sopenharmony_ci	.th_is_xid	=	0x01,
958c2ecf20Sopenharmony_ci	.th_seq_num	=	0x00000000,
968c2ecf20Sopenharmony_ci};
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci/*
998c2ecf20Sopenharmony_ci * Definition of one MPC group
1008c2ecf20Sopenharmony_ci */
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci/*
1038c2ecf20Sopenharmony_ci * Compatibility macros for busy handling
1048c2ecf20Sopenharmony_ci * of network devices.
1058c2ecf20Sopenharmony_ci */
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_cistatic void ctcmpc_unpack_skb(struct channel *ch, struct sk_buff *pskb);
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci/*
1108c2ecf20Sopenharmony_ci * MPC Group state machine actions (static prototypes)
1118c2ecf20Sopenharmony_ci */
1128c2ecf20Sopenharmony_cistatic void mpc_action_nop(fsm_instance *fsm, int event, void *arg);
1138c2ecf20Sopenharmony_cistatic void mpc_action_go_ready(fsm_instance *fsm, int event, void *arg);
1148c2ecf20Sopenharmony_cistatic void mpc_action_go_inop(fsm_instance *fi, int event, void *arg);
1158c2ecf20Sopenharmony_cistatic void mpc_action_timeout(fsm_instance *fi, int event, void *arg);
1168c2ecf20Sopenharmony_cistatic int  mpc_validate_xid(struct mpcg_info *mpcginfo);
1178c2ecf20Sopenharmony_cistatic void mpc_action_yside_xid(fsm_instance *fsm, int event, void *arg);
1188c2ecf20Sopenharmony_cistatic void mpc_action_doxid0(fsm_instance *fsm, int event, void *arg);
1198c2ecf20Sopenharmony_cistatic void mpc_action_doxid7(fsm_instance *fsm, int event, void *arg);
1208c2ecf20Sopenharmony_cistatic void mpc_action_xside_xid(fsm_instance *fsm, int event, void *arg);
1218c2ecf20Sopenharmony_cistatic void mpc_action_rcvd_xid0(fsm_instance *fsm, int event, void *arg);
1228c2ecf20Sopenharmony_cistatic void mpc_action_rcvd_xid7(fsm_instance *fsm, int event, void *arg);
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci#ifdef DEBUGDATA
1258c2ecf20Sopenharmony_ci/*-------------------------------------------------------------------*
1268c2ecf20Sopenharmony_ci* Dump buffer format						     *
1278c2ecf20Sopenharmony_ci*								     *
1288c2ecf20Sopenharmony_ci*--------------------------------------------------------------------*/
1298c2ecf20Sopenharmony_civoid ctcmpc_dumpit(char *buf, int len)
1308c2ecf20Sopenharmony_ci{
1318c2ecf20Sopenharmony_ci	__u32	ct, sw, rm, dup;
1328c2ecf20Sopenharmony_ci	char	*ptr, *rptr;
1338c2ecf20Sopenharmony_ci	char	tbuf[82], tdup[82];
1348c2ecf20Sopenharmony_ci	char	addr[22];
1358c2ecf20Sopenharmony_ci	char	boff[12];
1368c2ecf20Sopenharmony_ci	char	bhex[82], duphex[82];
1378c2ecf20Sopenharmony_ci	char	basc[40];
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	sw  = 0;
1408c2ecf20Sopenharmony_ci	rptr = ptr = buf;
1418c2ecf20Sopenharmony_ci	rm  = 16;
1428c2ecf20Sopenharmony_ci	duphex[0] = 0x00;
1438c2ecf20Sopenharmony_ci	dup = 0;
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	for (ct = 0; ct < len; ct++, ptr++, rptr++) {
1468c2ecf20Sopenharmony_ci		if (sw == 0) {
1478c2ecf20Sopenharmony_ci			sprintf(addr, "%16.16llx", (__u64)rptr);
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci			sprintf(boff, "%4.4X", (__u32)ct);
1508c2ecf20Sopenharmony_ci			bhex[0] = '\0';
1518c2ecf20Sopenharmony_ci			basc[0] = '\0';
1528c2ecf20Sopenharmony_ci		}
1538c2ecf20Sopenharmony_ci		if ((sw == 4) || (sw == 12))
1548c2ecf20Sopenharmony_ci			strcat(bhex, " ");
1558c2ecf20Sopenharmony_ci		if (sw == 8)
1568c2ecf20Sopenharmony_ci			strcat(bhex, "	");
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci		sprintf(tbuf, "%2.2llX", (__u64)*ptr);
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci		tbuf[2] = '\0';
1618c2ecf20Sopenharmony_ci		strcat(bhex, tbuf);
1628c2ecf20Sopenharmony_ci		if ((0 != isprint(*ptr)) && (*ptr >= 0x20))
1638c2ecf20Sopenharmony_ci			basc[sw] = *ptr;
1648c2ecf20Sopenharmony_ci		else
1658c2ecf20Sopenharmony_ci			basc[sw] = '.';
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci		basc[sw+1] = '\0';
1688c2ecf20Sopenharmony_ci		sw++;
1698c2ecf20Sopenharmony_ci		rm--;
1708c2ecf20Sopenharmony_ci		if (sw != 16)
1718c2ecf20Sopenharmony_ci			continue;
1728c2ecf20Sopenharmony_ci		if ((strcmp(duphex, bhex)) != 0) {
1738c2ecf20Sopenharmony_ci			if (dup != 0) {
1748c2ecf20Sopenharmony_ci				sprintf(tdup,
1758c2ecf20Sopenharmony_ci					"Duplicate as above to %s", addr);
1768c2ecf20Sopenharmony_ci				ctcm_pr_debug("		       --- %s ---\n",
1778c2ecf20Sopenharmony_ci						tdup);
1788c2ecf20Sopenharmony_ci			}
1798c2ecf20Sopenharmony_ci			ctcm_pr_debug("   %s (+%s) : %s  [%s]\n",
1808c2ecf20Sopenharmony_ci					addr, boff, bhex, basc);
1818c2ecf20Sopenharmony_ci			dup = 0;
1828c2ecf20Sopenharmony_ci			strcpy(duphex, bhex);
1838c2ecf20Sopenharmony_ci		} else
1848c2ecf20Sopenharmony_ci			dup++;
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci		sw = 0;
1878c2ecf20Sopenharmony_ci		rm = 16;
1888c2ecf20Sopenharmony_ci	}  /* endfor */
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci	if (sw != 0) {
1918c2ecf20Sopenharmony_ci		for ( ; rm > 0; rm--, sw++) {
1928c2ecf20Sopenharmony_ci			if ((sw == 4) || (sw == 12))
1938c2ecf20Sopenharmony_ci				strcat(bhex, " ");
1948c2ecf20Sopenharmony_ci			if (sw == 8)
1958c2ecf20Sopenharmony_ci				strcat(bhex, "	");
1968c2ecf20Sopenharmony_ci			strcat(bhex, "	");
1978c2ecf20Sopenharmony_ci			strcat(basc, " ");
1988c2ecf20Sopenharmony_ci		}
1998c2ecf20Sopenharmony_ci		if (dup != 0) {
2008c2ecf20Sopenharmony_ci			sprintf(tdup, "Duplicate as above to %s", addr);
2018c2ecf20Sopenharmony_ci			ctcm_pr_debug("		       --- %s ---\n", tdup);
2028c2ecf20Sopenharmony_ci		}
2038c2ecf20Sopenharmony_ci		ctcm_pr_debug("   %s (+%s) : %s  [%s]\n",
2048c2ecf20Sopenharmony_ci					addr, boff, bhex, basc);
2058c2ecf20Sopenharmony_ci	} else {
2068c2ecf20Sopenharmony_ci		if (dup >= 1) {
2078c2ecf20Sopenharmony_ci			sprintf(tdup, "Duplicate as above to %s", addr);
2088c2ecf20Sopenharmony_ci			ctcm_pr_debug("		       --- %s ---\n", tdup);
2098c2ecf20Sopenharmony_ci		}
2108c2ecf20Sopenharmony_ci		if (dup != 0) {
2118c2ecf20Sopenharmony_ci			ctcm_pr_debug("   %s (+%s) : %s  [%s]\n",
2128c2ecf20Sopenharmony_ci				addr, boff, bhex, basc);
2138c2ecf20Sopenharmony_ci		}
2148c2ecf20Sopenharmony_ci	}
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci	return;
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci}   /*	 end of ctcmpc_dumpit  */
2198c2ecf20Sopenharmony_ci#endif
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci#ifdef DEBUGDATA
2228c2ecf20Sopenharmony_ci/*
2238c2ecf20Sopenharmony_ci * Dump header and first 16 bytes of an sk_buff for debugging purposes.
2248c2ecf20Sopenharmony_ci *
2258c2ecf20Sopenharmony_ci * skb		The sk_buff to dump.
2268c2ecf20Sopenharmony_ci * offset	Offset relative to skb-data, where to start the dump.
2278c2ecf20Sopenharmony_ci */
2288c2ecf20Sopenharmony_civoid ctcmpc_dump_skb(struct sk_buff *skb, int offset)
2298c2ecf20Sopenharmony_ci{
2308c2ecf20Sopenharmony_ci	__u8 *p = skb->data;
2318c2ecf20Sopenharmony_ci	struct th_header *header;
2328c2ecf20Sopenharmony_ci	struct pdu *pheader;
2338c2ecf20Sopenharmony_ci	int bl = skb->len;
2348c2ecf20Sopenharmony_ci	int i;
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci	if (p == NULL)
2378c2ecf20Sopenharmony_ci		return;
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci	p += offset;
2408c2ecf20Sopenharmony_ci	header = (struct th_header *)p;
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	ctcm_pr_debug("dump:\n");
2438c2ecf20Sopenharmony_ci	ctcm_pr_debug("skb len=%d \n", skb->len);
2448c2ecf20Sopenharmony_ci	if (skb->len > 2) {
2458c2ecf20Sopenharmony_ci		switch (header->th_ch_flag) {
2468c2ecf20Sopenharmony_ci		case TH_HAS_PDU:
2478c2ecf20Sopenharmony_ci			break;
2488c2ecf20Sopenharmony_ci		case 0x00:
2498c2ecf20Sopenharmony_ci		case TH_IS_XID:
2508c2ecf20Sopenharmony_ci			if ((header->th_blk_flag == TH_DATA_IS_XID) &&
2518c2ecf20Sopenharmony_ci			   (header->th_is_xid == 0x01))
2528c2ecf20Sopenharmony_ci				goto dumpth;
2538c2ecf20Sopenharmony_ci		case TH_SWEEP_REQ:
2548c2ecf20Sopenharmony_ci				goto dumpth;
2558c2ecf20Sopenharmony_ci		case TH_SWEEP_RESP:
2568c2ecf20Sopenharmony_ci				goto dumpth;
2578c2ecf20Sopenharmony_ci		default:
2588c2ecf20Sopenharmony_ci			break;
2598c2ecf20Sopenharmony_ci		}
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci		pheader = (struct pdu *)p;
2628c2ecf20Sopenharmony_ci		ctcm_pr_debug("pdu->offset: %d hex: %04x\n",
2638c2ecf20Sopenharmony_ci			       pheader->pdu_offset, pheader->pdu_offset);
2648c2ecf20Sopenharmony_ci		ctcm_pr_debug("pdu->flag  : %02x\n", pheader->pdu_flag);
2658c2ecf20Sopenharmony_ci		ctcm_pr_debug("pdu->proto : %02x\n", pheader->pdu_proto);
2668c2ecf20Sopenharmony_ci		ctcm_pr_debug("pdu->seq   : %02x\n", pheader->pdu_seq);
2678c2ecf20Sopenharmony_ci					goto dumpdata;
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_cidumpth:
2708c2ecf20Sopenharmony_ci		ctcm_pr_debug("th->seg     : %02x\n", header->th_seg);
2718c2ecf20Sopenharmony_ci		ctcm_pr_debug("th->ch      : %02x\n", header->th_ch_flag);
2728c2ecf20Sopenharmony_ci		ctcm_pr_debug("th->blk_flag: %02x\n", header->th_blk_flag);
2738c2ecf20Sopenharmony_ci		ctcm_pr_debug("th->type    : %s\n",
2748c2ecf20Sopenharmony_ci			       (header->th_is_xid) ? "DATA" : "XID");
2758c2ecf20Sopenharmony_ci		ctcm_pr_debug("th->seqnum  : %04x\n", header->th_seq_num);
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci	}
2788c2ecf20Sopenharmony_cidumpdata:
2798c2ecf20Sopenharmony_ci	if (bl > 32)
2808c2ecf20Sopenharmony_ci		bl = 32;
2818c2ecf20Sopenharmony_ci	ctcm_pr_debug("data: ");
2828c2ecf20Sopenharmony_ci	for (i = 0; i < bl; i++)
2838c2ecf20Sopenharmony_ci		ctcm_pr_debug("%02x%s", *p++, (i % 16) ? " " : "\n");
2848c2ecf20Sopenharmony_ci	ctcm_pr_debug("\n");
2858c2ecf20Sopenharmony_ci}
2868c2ecf20Sopenharmony_ci#endif
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_cistatic struct net_device *ctcmpc_get_dev(int port_num)
2898c2ecf20Sopenharmony_ci{
2908c2ecf20Sopenharmony_ci	char device[20];
2918c2ecf20Sopenharmony_ci	struct net_device *dev;
2928c2ecf20Sopenharmony_ci	struct ctcm_priv *priv;
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci	sprintf(device, "%s%i", MPC_DEVICE_NAME, port_num);
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci	dev = __dev_get_by_name(&init_net, device);
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_ci	if (dev == NULL) {
2998c2ecf20Sopenharmony_ci		CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
3008c2ecf20Sopenharmony_ci			"%s: Device not found by name: %s",
3018c2ecf20Sopenharmony_ci					CTCM_FUNTAIL, device);
3028c2ecf20Sopenharmony_ci		return NULL;
3038c2ecf20Sopenharmony_ci	}
3048c2ecf20Sopenharmony_ci	priv = dev->ml_priv;
3058c2ecf20Sopenharmony_ci	if (priv == NULL) {
3068c2ecf20Sopenharmony_ci		CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
3078c2ecf20Sopenharmony_ci			"%s(%s): dev->ml_priv is NULL",
3088c2ecf20Sopenharmony_ci					CTCM_FUNTAIL, device);
3098c2ecf20Sopenharmony_ci		return NULL;
3108c2ecf20Sopenharmony_ci	}
3118c2ecf20Sopenharmony_ci	if (priv->mpcg == NULL) {
3128c2ecf20Sopenharmony_ci		CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
3138c2ecf20Sopenharmony_ci			"%s(%s): priv->mpcg is NULL",
3148c2ecf20Sopenharmony_ci					CTCM_FUNTAIL, device);
3158c2ecf20Sopenharmony_ci		return NULL;
3168c2ecf20Sopenharmony_ci	}
3178c2ecf20Sopenharmony_ci	return dev;
3188c2ecf20Sopenharmony_ci}
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci/*
3218c2ecf20Sopenharmony_ci * ctc_mpc_alloc_channel
3228c2ecf20Sopenharmony_ci *	(exported interface)
3238c2ecf20Sopenharmony_ci *
3248c2ecf20Sopenharmony_ci * Device Initialization :
3258c2ecf20Sopenharmony_ci *	ACTPATH  driven IO operations
3268c2ecf20Sopenharmony_ci */
3278c2ecf20Sopenharmony_ciint ctc_mpc_alloc_channel(int port_num, void (*callback)(int, int))
3288c2ecf20Sopenharmony_ci{
3298c2ecf20Sopenharmony_ci	struct net_device *dev;
3308c2ecf20Sopenharmony_ci	struct mpc_group *grp;
3318c2ecf20Sopenharmony_ci	struct ctcm_priv *priv;
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_ci	dev = ctcmpc_get_dev(port_num);
3348c2ecf20Sopenharmony_ci	if (dev == NULL)
3358c2ecf20Sopenharmony_ci		return 1;
3368c2ecf20Sopenharmony_ci	priv = dev->ml_priv;
3378c2ecf20Sopenharmony_ci	grp = priv->mpcg;
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci	grp->allochanfunc = callback;
3408c2ecf20Sopenharmony_ci	grp->port_num = port_num;
3418c2ecf20Sopenharmony_ci	grp->port_persist = 1;
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci	CTCM_DBF_TEXT_(MPC_SETUP, CTC_DBF_INFO,
3448c2ecf20Sopenharmony_ci			"%s(%s): state=%s",
3458c2ecf20Sopenharmony_ci			CTCM_FUNTAIL, dev->name, fsm_getstate_str(grp->fsm));
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci	switch (fsm_getstate(grp->fsm)) {
3488c2ecf20Sopenharmony_ci	case MPCG_STATE_INOP:
3498c2ecf20Sopenharmony_ci		/* Group is in the process of terminating */
3508c2ecf20Sopenharmony_ci		grp->alloc_called = 1;
3518c2ecf20Sopenharmony_ci		break;
3528c2ecf20Sopenharmony_ci	case MPCG_STATE_RESET:
3538c2ecf20Sopenharmony_ci		/* MPC Group will transition to state		  */
3548c2ecf20Sopenharmony_ci		/* MPCG_STATE_XID2INITW iff the minimum number	  */
3558c2ecf20Sopenharmony_ci		/* of 1 read and 1 write channel have successfully*/
3568c2ecf20Sopenharmony_ci		/* activated					  */
3578c2ecf20Sopenharmony_ci		/*fsm_newstate(grp->fsm, MPCG_STATE_XID2INITW);*/
3588c2ecf20Sopenharmony_ci		if (callback)
3598c2ecf20Sopenharmony_ci			grp->send_qllc_disc = 1;
3608c2ecf20Sopenharmony_ci		fallthrough;
3618c2ecf20Sopenharmony_ci	case MPCG_STATE_XID0IOWAIT:
3628c2ecf20Sopenharmony_ci		fsm_deltimer(&grp->timer);
3638c2ecf20Sopenharmony_ci		grp->outstanding_xid2 = 0;
3648c2ecf20Sopenharmony_ci		grp->outstanding_xid7 = 0;
3658c2ecf20Sopenharmony_ci		grp->outstanding_xid7_p2 = 0;
3668c2ecf20Sopenharmony_ci		grp->saved_xid2 = NULL;
3678c2ecf20Sopenharmony_ci		if (callback)
3688c2ecf20Sopenharmony_ci			ctcm_open(dev);
3698c2ecf20Sopenharmony_ci		fsm_event(priv->fsm, DEV_EVENT_START, dev);
3708c2ecf20Sopenharmony_ci		break;
3718c2ecf20Sopenharmony_ci	case MPCG_STATE_READY:
3728c2ecf20Sopenharmony_ci		/* XID exchanges completed after PORT was activated */
3738c2ecf20Sopenharmony_ci		/* Link station already active			    */
3748c2ecf20Sopenharmony_ci		/* Maybe timing issue...retry callback		    */
3758c2ecf20Sopenharmony_ci		grp->allocchan_callback_retries++;
3768c2ecf20Sopenharmony_ci		if (grp->allocchan_callback_retries < 4) {
3778c2ecf20Sopenharmony_ci			if (grp->allochanfunc)
3788c2ecf20Sopenharmony_ci				grp->allochanfunc(grp->port_num,
3798c2ecf20Sopenharmony_ci						  grp->group_max_buflen);
3808c2ecf20Sopenharmony_ci		} else {
3818c2ecf20Sopenharmony_ci			/* there are problems...bail out	    */
3828c2ecf20Sopenharmony_ci			/* there may be a state mismatch so restart */
3838c2ecf20Sopenharmony_ci			fsm_event(grp->fsm, MPCG_EVENT_INOP, dev);
3848c2ecf20Sopenharmony_ci			grp->allocchan_callback_retries = 0;
3858c2ecf20Sopenharmony_ci		}
3868c2ecf20Sopenharmony_ci		break;
3878c2ecf20Sopenharmony_ci	}
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_ci	return 0;
3908c2ecf20Sopenharmony_ci}
3918c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ctc_mpc_alloc_channel);
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_ci/*
3948c2ecf20Sopenharmony_ci * ctc_mpc_establish_connectivity
3958c2ecf20Sopenharmony_ci *	(exported interface)
3968c2ecf20Sopenharmony_ci */
3978c2ecf20Sopenharmony_civoid ctc_mpc_establish_connectivity(int port_num,
3988c2ecf20Sopenharmony_ci				void (*callback)(int, int, int))
3998c2ecf20Sopenharmony_ci{
4008c2ecf20Sopenharmony_ci	struct net_device *dev;
4018c2ecf20Sopenharmony_ci	struct mpc_group *grp;
4028c2ecf20Sopenharmony_ci	struct ctcm_priv *priv;
4038c2ecf20Sopenharmony_ci	struct channel *rch, *wch;
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_ci	dev = ctcmpc_get_dev(port_num);
4068c2ecf20Sopenharmony_ci	if (dev == NULL)
4078c2ecf20Sopenharmony_ci		return;
4088c2ecf20Sopenharmony_ci	priv = dev->ml_priv;
4098c2ecf20Sopenharmony_ci	grp = priv->mpcg;
4108c2ecf20Sopenharmony_ci	rch = priv->channel[CTCM_READ];
4118c2ecf20Sopenharmony_ci	wch = priv->channel[CTCM_WRITE];
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_ci	CTCM_DBF_TEXT_(MPC_SETUP, CTC_DBF_INFO,
4148c2ecf20Sopenharmony_ci			"%s(%s): state=%s",
4158c2ecf20Sopenharmony_ci			CTCM_FUNTAIL, dev->name, fsm_getstate_str(grp->fsm));
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_ci	grp->estconnfunc = callback;
4188c2ecf20Sopenharmony_ci	grp->port_num = port_num;
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_ci	switch (fsm_getstate(grp->fsm)) {
4218c2ecf20Sopenharmony_ci	case MPCG_STATE_READY:
4228c2ecf20Sopenharmony_ci		/* XID exchanges completed after PORT was activated */
4238c2ecf20Sopenharmony_ci		/* Link station already active			    */
4248c2ecf20Sopenharmony_ci		/* Maybe timing issue...retry callback		    */
4258c2ecf20Sopenharmony_ci		fsm_deltimer(&grp->timer);
4268c2ecf20Sopenharmony_ci		grp->estconn_callback_retries++;
4278c2ecf20Sopenharmony_ci		if (grp->estconn_callback_retries < 4) {
4288c2ecf20Sopenharmony_ci			if (grp->estconnfunc) {
4298c2ecf20Sopenharmony_ci				grp->estconnfunc(grp->port_num, 0,
4308c2ecf20Sopenharmony_ci						grp->group_max_buflen);
4318c2ecf20Sopenharmony_ci				grp->estconnfunc = NULL;
4328c2ecf20Sopenharmony_ci			}
4338c2ecf20Sopenharmony_ci		} else {
4348c2ecf20Sopenharmony_ci			/* there are problems...bail out	 */
4358c2ecf20Sopenharmony_ci			fsm_event(grp->fsm, MPCG_EVENT_INOP, dev);
4368c2ecf20Sopenharmony_ci			grp->estconn_callback_retries = 0;
4378c2ecf20Sopenharmony_ci		}
4388c2ecf20Sopenharmony_ci		break;
4398c2ecf20Sopenharmony_ci	case MPCG_STATE_INOP:
4408c2ecf20Sopenharmony_ci	case MPCG_STATE_RESET:
4418c2ecf20Sopenharmony_ci		/* MPC Group is not ready to start XID - min num of */
4428c2ecf20Sopenharmony_ci		/* 1 read and 1 write channel have not been acquired*/
4438c2ecf20Sopenharmony_ci
4448c2ecf20Sopenharmony_ci		CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
4458c2ecf20Sopenharmony_ci			"%s(%s): REJECTED - inactive channels",
4468c2ecf20Sopenharmony_ci					CTCM_FUNTAIL, dev->name);
4478c2ecf20Sopenharmony_ci		if (grp->estconnfunc) {
4488c2ecf20Sopenharmony_ci			grp->estconnfunc(grp->port_num, -1, 0);
4498c2ecf20Sopenharmony_ci			grp->estconnfunc = NULL;
4508c2ecf20Sopenharmony_ci		}
4518c2ecf20Sopenharmony_ci		break;
4528c2ecf20Sopenharmony_ci	case MPCG_STATE_XID2INITW:
4538c2ecf20Sopenharmony_ci		/* alloc channel was called but no XID exchange    */
4548c2ecf20Sopenharmony_ci		/* has occurred. initiate xside XID exchange	   */
4558c2ecf20Sopenharmony_ci		/* make sure yside XID0 processing has not started */
4568c2ecf20Sopenharmony_ci
4578c2ecf20Sopenharmony_ci		if ((fsm_getstate(rch->fsm) > CH_XID0_PENDING) ||
4588c2ecf20Sopenharmony_ci			(fsm_getstate(wch->fsm) > CH_XID0_PENDING)) {
4598c2ecf20Sopenharmony_ci			CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
4608c2ecf20Sopenharmony_ci				"%s(%s): ABORT - PASSIVE XID",
4618c2ecf20Sopenharmony_ci					CTCM_FUNTAIL, dev->name);
4628c2ecf20Sopenharmony_ci			break;
4638c2ecf20Sopenharmony_ci		}
4648c2ecf20Sopenharmony_ci		grp->send_qllc_disc = 1;
4658c2ecf20Sopenharmony_ci		fsm_newstate(grp->fsm, MPCG_STATE_XID0IOWAIT);
4668c2ecf20Sopenharmony_ci		fsm_deltimer(&grp->timer);
4678c2ecf20Sopenharmony_ci		fsm_addtimer(&grp->timer, MPC_XID_TIMEOUT_VALUE,
4688c2ecf20Sopenharmony_ci						MPCG_EVENT_TIMER, dev);
4698c2ecf20Sopenharmony_ci		grp->outstanding_xid7 = 0;
4708c2ecf20Sopenharmony_ci		grp->outstanding_xid7_p2 = 0;
4718c2ecf20Sopenharmony_ci		grp->saved_xid2 = NULL;
4728c2ecf20Sopenharmony_ci		if ((rch->in_mpcgroup) &&
4738c2ecf20Sopenharmony_ci				(fsm_getstate(rch->fsm) == CH_XID0_PENDING))
4748c2ecf20Sopenharmony_ci			fsm_event(grp->fsm, MPCG_EVENT_XID0DO, rch);
4758c2ecf20Sopenharmony_ci		else {
4768c2ecf20Sopenharmony_ci			CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
4778c2ecf20Sopenharmony_ci				"%s(%s): RX-%s not ready for ACTIVE XID0",
4788c2ecf20Sopenharmony_ci					CTCM_FUNTAIL, dev->name, rch->id);
4798c2ecf20Sopenharmony_ci			if (grp->estconnfunc) {
4808c2ecf20Sopenharmony_ci				grp->estconnfunc(grp->port_num, -1, 0);
4818c2ecf20Sopenharmony_ci				grp->estconnfunc = NULL;
4828c2ecf20Sopenharmony_ci			}
4838c2ecf20Sopenharmony_ci			fsm_deltimer(&grp->timer);
4848c2ecf20Sopenharmony_ci				goto done;
4858c2ecf20Sopenharmony_ci		}
4868c2ecf20Sopenharmony_ci		if ((wch->in_mpcgroup) &&
4878c2ecf20Sopenharmony_ci				(fsm_getstate(wch->fsm) == CH_XID0_PENDING))
4888c2ecf20Sopenharmony_ci			fsm_event(grp->fsm, MPCG_EVENT_XID0DO, wch);
4898c2ecf20Sopenharmony_ci		else {
4908c2ecf20Sopenharmony_ci			CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
4918c2ecf20Sopenharmony_ci				"%s(%s): WX-%s not ready for ACTIVE XID0",
4928c2ecf20Sopenharmony_ci					CTCM_FUNTAIL, dev->name, wch->id);
4938c2ecf20Sopenharmony_ci			if (grp->estconnfunc) {
4948c2ecf20Sopenharmony_ci				grp->estconnfunc(grp->port_num, -1, 0);
4958c2ecf20Sopenharmony_ci				grp->estconnfunc = NULL;
4968c2ecf20Sopenharmony_ci			}
4978c2ecf20Sopenharmony_ci			fsm_deltimer(&grp->timer);
4988c2ecf20Sopenharmony_ci				goto done;
4998c2ecf20Sopenharmony_ci			}
5008c2ecf20Sopenharmony_ci		break;
5018c2ecf20Sopenharmony_ci	case MPCG_STATE_XID0IOWAIT:
5028c2ecf20Sopenharmony_ci		/* already in active XID negotiations */
5038c2ecf20Sopenharmony_ci	default:
5048c2ecf20Sopenharmony_ci		break;
5058c2ecf20Sopenharmony_ci	}
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_cidone:
5088c2ecf20Sopenharmony_ci	CTCM_PR_DEBUG("Exit %s()\n", __func__);
5098c2ecf20Sopenharmony_ci	return;
5108c2ecf20Sopenharmony_ci}
5118c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ctc_mpc_establish_connectivity);
5128c2ecf20Sopenharmony_ci
5138c2ecf20Sopenharmony_ci/*
5148c2ecf20Sopenharmony_ci * ctc_mpc_dealloc_ch
5158c2ecf20Sopenharmony_ci *	(exported interface)
5168c2ecf20Sopenharmony_ci */
5178c2ecf20Sopenharmony_civoid ctc_mpc_dealloc_ch(int port_num)
5188c2ecf20Sopenharmony_ci{
5198c2ecf20Sopenharmony_ci	struct net_device *dev;
5208c2ecf20Sopenharmony_ci	struct ctcm_priv *priv;
5218c2ecf20Sopenharmony_ci	struct mpc_group *grp;
5228c2ecf20Sopenharmony_ci
5238c2ecf20Sopenharmony_ci	dev = ctcmpc_get_dev(port_num);
5248c2ecf20Sopenharmony_ci	if (dev == NULL)
5258c2ecf20Sopenharmony_ci		return;
5268c2ecf20Sopenharmony_ci	priv = dev->ml_priv;
5278c2ecf20Sopenharmony_ci	grp = priv->mpcg;
5288c2ecf20Sopenharmony_ci
5298c2ecf20Sopenharmony_ci	CTCM_DBF_TEXT_(MPC_SETUP, CTC_DBF_DEBUG,
5308c2ecf20Sopenharmony_ci			"%s: %s: refcount = %d\n",
5318c2ecf20Sopenharmony_ci			CTCM_FUNTAIL, dev->name, netdev_refcnt_read(dev));
5328c2ecf20Sopenharmony_ci
5338c2ecf20Sopenharmony_ci	fsm_deltimer(&priv->restart_timer);
5348c2ecf20Sopenharmony_ci	grp->channels_terminating = 0;
5358c2ecf20Sopenharmony_ci	fsm_deltimer(&grp->timer);
5368c2ecf20Sopenharmony_ci	grp->allochanfunc = NULL;
5378c2ecf20Sopenharmony_ci	grp->estconnfunc = NULL;
5388c2ecf20Sopenharmony_ci	grp->port_persist = 0;
5398c2ecf20Sopenharmony_ci	grp->send_qllc_disc = 0;
5408c2ecf20Sopenharmony_ci	fsm_event(grp->fsm, MPCG_EVENT_INOP, dev);
5418c2ecf20Sopenharmony_ci
5428c2ecf20Sopenharmony_ci	ctcm_close(dev);
5438c2ecf20Sopenharmony_ci	return;
5448c2ecf20Sopenharmony_ci}
5458c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ctc_mpc_dealloc_ch);
5468c2ecf20Sopenharmony_ci
5478c2ecf20Sopenharmony_ci/*
5488c2ecf20Sopenharmony_ci * ctc_mpc_flow_control
5498c2ecf20Sopenharmony_ci *	(exported interface)
5508c2ecf20Sopenharmony_ci */
5518c2ecf20Sopenharmony_civoid ctc_mpc_flow_control(int port_num, int flowc)
5528c2ecf20Sopenharmony_ci{
5538c2ecf20Sopenharmony_ci	struct ctcm_priv *priv;
5548c2ecf20Sopenharmony_ci	struct mpc_group *grp;
5558c2ecf20Sopenharmony_ci	struct net_device *dev;
5568c2ecf20Sopenharmony_ci	struct channel *rch;
5578c2ecf20Sopenharmony_ci	int mpcg_state;
5588c2ecf20Sopenharmony_ci
5598c2ecf20Sopenharmony_ci	dev = ctcmpc_get_dev(port_num);
5608c2ecf20Sopenharmony_ci	if (dev == NULL)
5618c2ecf20Sopenharmony_ci		return;
5628c2ecf20Sopenharmony_ci	priv = dev->ml_priv;
5638c2ecf20Sopenharmony_ci	grp = priv->mpcg;
5648c2ecf20Sopenharmony_ci
5658c2ecf20Sopenharmony_ci	CTCM_DBF_TEXT_(MPC_TRACE, CTC_DBF_DEBUG,
5668c2ecf20Sopenharmony_ci			"%s: %s: flowc = %d",
5678c2ecf20Sopenharmony_ci				CTCM_FUNTAIL, dev->name, flowc);
5688c2ecf20Sopenharmony_ci
5698c2ecf20Sopenharmony_ci	rch = priv->channel[CTCM_READ];
5708c2ecf20Sopenharmony_ci
5718c2ecf20Sopenharmony_ci	mpcg_state = fsm_getstate(grp->fsm);
5728c2ecf20Sopenharmony_ci	switch (flowc) {
5738c2ecf20Sopenharmony_ci	case 1:
5748c2ecf20Sopenharmony_ci		if (mpcg_state == MPCG_STATE_FLOWC)
5758c2ecf20Sopenharmony_ci			break;
5768c2ecf20Sopenharmony_ci		if (mpcg_state == MPCG_STATE_READY) {
5778c2ecf20Sopenharmony_ci			if (grp->flow_off_called == 1)
5788c2ecf20Sopenharmony_ci				grp->flow_off_called = 0;
5798c2ecf20Sopenharmony_ci			else
5808c2ecf20Sopenharmony_ci				fsm_newstate(grp->fsm, MPCG_STATE_FLOWC);
5818c2ecf20Sopenharmony_ci			break;
5828c2ecf20Sopenharmony_ci		}
5838c2ecf20Sopenharmony_ci		break;
5848c2ecf20Sopenharmony_ci	case 0:
5858c2ecf20Sopenharmony_ci		if (mpcg_state == MPCG_STATE_FLOWC) {
5868c2ecf20Sopenharmony_ci			fsm_newstate(grp->fsm, MPCG_STATE_READY);
5878c2ecf20Sopenharmony_ci			/* ensure any data that has accumulated */
5888c2ecf20Sopenharmony_ci			/* on the io_queue will now be sen t	*/
5898c2ecf20Sopenharmony_ci			tasklet_schedule(&rch->ch_tasklet);
5908c2ecf20Sopenharmony_ci		}
5918c2ecf20Sopenharmony_ci		/* possible race condition			*/
5928c2ecf20Sopenharmony_ci		if (mpcg_state == MPCG_STATE_READY) {
5938c2ecf20Sopenharmony_ci			grp->flow_off_called = 1;
5948c2ecf20Sopenharmony_ci			break;
5958c2ecf20Sopenharmony_ci		}
5968c2ecf20Sopenharmony_ci		break;
5978c2ecf20Sopenharmony_ci	}
5988c2ecf20Sopenharmony_ci
5998c2ecf20Sopenharmony_ci}
6008c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ctc_mpc_flow_control);
6018c2ecf20Sopenharmony_ci
6028c2ecf20Sopenharmony_cistatic int mpc_send_qllc_discontact(struct net_device *);
6038c2ecf20Sopenharmony_ci
6048c2ecf20Sopenharmony_ci/*
6058c2ecf20Sopenharmony_ci * helper function of ctcmpc_unpack_skb
6068c2ecf20Sopenharmony_ci*/
6078c2ecf20Sopenharmony_cistatic void mpc_rcvd_sweep_resp(struct mpcg_info *mpcginfo)
6088c2ecf20Sopenharmony_ci{
6098c2ecf20Sopenharmony_ci	struct channel	  *rch = mpcginfo->ch;
6108c2ecf20Sopenharmony_ci	struct net_device *dev = rch->netdev;
6118c2ecf20Sopenharmony_ci	struct ctcm_priv   *priv = dev->ml_priv;
6128c2ecf20Sopenharmony_ci	struct mpc_group  *grp = priv->mpcg;
6138c2ecf20Sopenharmony_ci	struct channel	  *ch = priv->channel[CTCM_WRITE];
6148c2ecf20Sopenharmony_ci
6158c2ecf20Sopenharmony_ci	CTCM_PR_DEBUG("%s: ch=0x%p id=%s\n", __func__, ch, ch->id);
6168c2ecf20Sopenharmony_ci	CTCM_D3_DUMP((char *)mpcginfo->sweep, TH_SWEEP_LENGTH);
6178c2ecf20Sopenharmony_ci
6188c2ecf20Sopenharmony_ci	grp->sweep_rsp_pend_num--;
6198c2ecf20Sopenharmony_ci
6208c2ecf20Sopenharmony_ci	if ((grp->sweep_req_pend_num == 0) &&
6218c2ecf20Sopenharmony_ci			(grp->sweep_rsp_pend_num == 0)) {
6228c2ecf20Sopenharmony_ci		fsm_deltimer(&ch->sweep_timer);
6238c2ecf20Sopenharmony_ci		grp->in_sweep = 0;
6248c2ecf20Sopenharmony_ci		rch->th_seq_num = 0x00;
6258c2ecf20Sopenharmony_ci		ch->th_seq_num = 0x00;
6268c2ecf20Sopenharmony_ci		ctcm_clear_busy_do(dev);
6278c2ecf20Sopenharmony_ci	}
6288c2ecf20Sopenharmony_ci
6298c2ecf20Sopenharmony_ci	return;
6308c2ecf20Sopenharmony_ci
6318c2ecf20Sopenharmony_ci}
6328c2ecf20Sopenharmony_ci
6338c2ecf20Sopenharmony_ci/*
6348c2ecf20Sopenharmony_ci * helper function of mpc_rcvd_sweep_req
6358c2ecf20Sopenharmony_ci * which is a helper of ctcmpc_unpack_skb
6368c2ecf20Sopenharmony_ci */
6378c2ecf20Sopenharmony_cistatic void ctcmpc_send_sweep_resp(struct channel *rch)
6388c2ecf20Sopenharmony_ci{
6398c2ecf20Sopenharmony_ci	struct net_device *dev = rch->netdev;
6408c2ecf20Sopenharmony_ci	struct ctcm_priv *priv = dev->ml_priv;
6418c2ecf20Sopenharmony_ci	struct mpc_group *grp = priv->mpcg;
6428c2ecf20Sopenharmony_ci	struct th_sweep *header;
6438c2ecf20Sopenharmony_ci	struct sk_buff *sweep_skb;
6448c2ecf20Sopenharmony_ci	struct channel *ch  = priv->channel[CTCM_WRITE];
6458c2ecf20Sopenharmony_ci
6468c2ecf20Sopenharmony_ci	CTCM_PR_DEBUG("%s: ch=0x%p id=%s\n", __func__, rch, rch->id);
6478c2ecf20Sopenharmony_ci
6488c2ecf20Sopenharmony_ci	sweep_skb = __dev_alloc_skb(MPC_BUFSIZE_DEFAULT, GFP_ATOMIC | GFP_DMA);
6498c2ecf20Sopenharmony_ci	if (sweep_skb == NULL) {
6508c2ecf20Sopenharmony_ci		CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
6518c2ecf20Sopenharmony_ci			"%s(%s): sweep_skb allocation ERROR\n",
6528c2ecf20Sopenharmony_ci			CTCM_FUNTAIL, rch->id);
6538c2ecf20Sopenharmony_ci		goto done;
6548c2ecf20Sopenharmony_ci	}
6558c2ecf20Sopenharmony_ci
6568c2ecf20Sopenharmony_ci	header = kmalloc(sizeof(struct th_sweep), gfp_type());
6578c2ecf20Sopenharmony_ci
6588c2ecf20Sopenharmony_ci	if (!header) {
6598c2ecf20Sopenharmony_ci		dev_kfree_skb_any(sweep_skb);
6608c2ecf20Sopenharmony_ci		goto done;
6618c2ecf20Sopenharmony_ci	}
6628c2ecf20Sopenharmony_ci
6638c2ecf20Sopenharmony_ci	header->th.th_seg	= 0x00 ;
6648c2ecf20Sopenharmony_ci	header->th.th_ch_flag	= TH_SWEEP_RESP;
6658c2ecf20Sopenharmony_ci	header->th.th_blk_flag	= 0x00;
6668c2ecf20Sopenharmony_ci	header->th.th_is_xid	= 0x00;
6678c2ecf20Sopenharmony_ci	header->th.th_seq_num	= 0x00;
6688c2ecf20Sopenharmony_ci	header->sw.th_last_seq	= ch->th_seq_num;
6698c2ecf20Sopenharmony_ci
6708c2ecf20Sopenharmony_ci	skb_put_data(sweep_skb, header, TH_SWEEP_LENGTH);
6718c2ecf20Sopenharmony_ci
6728c2ecf20Sopenharmony_ci	kfree(header);
6738c2ecf20Sopenharmony_ci
6748c2ecf20Sopenharmony_ci	netif_trans_update(dev);
6758c2ecf20Sopenharmony_ci	skb_queue_tail(&ch->sweep_queue, sweep_skb);
6768c2ecf20Sopenharmony_ci
6778c2ecf20Sopenharmony_ci	fsm_addtimer(&ch->sweep_timer, 100, CTC_EVENT_RSWEEP_TIMER, ch);
6788c2ecf20Sopenharmony_ci
6798c2ecf20Sopenharmony_ci	return;
6808c2ecf20Sopenharmony_ci
6818c2ecf20Sopenharmony_cidone:
6828c2ecf20Sopenharmony_ci	grp->in_sweep = 0;
6838c2ecf20Sopenharmony_ci	ctcm_clear_busy_do(dev);
6848c2ecf20Sopenharmony_ci	fsm_event(grp->fsm, MPCG_EVENT_INOP, dev);
6858c2ecf20Sopenharmony_ci
6868c2ecf20Sopenharmony_ci	return;
6878c2ecf20Sopenharmony_ci}
6888c2ecf20Sopenharmony_ci
6898c2ecf20Sopenharmony_ci/*
6908c2ecf20Sopenharmony_ci * helper function of ctcmpc_unpack_skb
6918c2ecf20Sopenharmony_ci */
6928c2ecf20Sopenharmony_cistatic void mpc_rcvd_sweep_req(struct mpcg_info *mpcginfo)
6938c2ecf20Sopenharmony_ci{
6948c2ecf20Sopenharmony_ci	struct channel	  *rch     = mpcginfo->ch;
6958c2ecf20Sopenharmony_ci	struct net_device *dev     = rch->netdev;
6968c2ecf20Sopenharmony_ci	struct ctcm_priv  *priv = dev->ml_priv;
6978c2ecf20Sopenharmony_ci	struct mpc_group  *grp  = priv->mpcg;
6988c2ecf20Sopenharmony_ci	struct channel	  *ch	   = priv->channel[CTCM_WRITE];
6998c2ecf20Sopenharmony_ci
7008c2ecf20Sopenharmony_ci	if (do_debug)
7018c2ecf20Sopenharmony_ci		CTCM_DBF_TEXT_(MPC_TRACE, CTC_DBF_DEBUG,
7028c2ecf20Sopenharmony_ci			" %s(): ch=0x%p id=%s\n", __func__, ch, ch->id);
7038c2ecf20Sopenharmony_ci
7048c2ecf20Sopenharmony_ci	if (grp->in_sweep == 0) {
7058c2ecf20Sopenharmony_ci		grp->in_sweep = 1;
7068c2ecf20Sopenharmony_ci		ctcm_test_and_set_busy(dev);
7078c2ecf20Sopenharmony_ci		grp->sweep_req_pend_num = grp->active_channels[CTCM_READ];
7088c2ecf20Sopenharmony_ci		grp->sweep_rsp_pend_num = grp->active_channels[CTCM_READ];
7098c2ecf20Sopenharmony_ci	}
7108c2ecf20Sopenharmony_ci
7118c2ecf20Sopenharmony_ci	CTCM_D3_DUMP((char *)mpcginfo->sweep, TH_SWEEP_LENGTH);
7128c2ecf20Sopenharmony_ci
7138c2ecf20Sopenharmony_ci	grp->sweep_req_pend_num--;
7148c2ecf20Sopenharmony_ci	ctcmpc_send_sweep_resp(ch);
7158c2ecf20Sopenharmony_ci	kfree(mpcginfo);
7168c2ecf20Sopenharmony_ci	return;
7178c2ecf20Sopenharmony_ci}
7188c2ecf20Sopenharmony_ci
7198c2ecf20Sopenharmony_ci/*
7208c2ecf20Sopenharmony_ci  * MPC Group Station FSM definitions
7218c2ecf20Sopenharmony_ci */
7228c2ecf20Sopenharmony_cistatic const char *mpcg_event_names[] = {
7238c2ecf20Sopenharmony_ci	[MPCG_EVENT_INOP]	= "INOP Condition",
7248c2ecf20Sopenharmony_ci	[MPCG_EVENT_DISCONC]	= "Discontact Received",
7258c2ecf20Sopenharmony_ci	[MPCG_EVENT_XID0DO]	= "Channel Active - Start XID",
7268c2ecf20Sopenharmony_ci	[MPCG_EVENT_XID2]	= "XID2 Received",
7278c2ecf20Sopenharmony_ci	[MPCG_EVENT_XID2DONE]	= "XID0 Complete",
7288c2ecf20Sopenharmony_ci	[MPCG_EVENT_XID7DONE]	= "XID7 Complete",
7298c2ecf20Sopenharmony_ci	[MPCG_EVENT_TIMER]	= "XID Setup Timer",
7308c2ecf20Sopenharmony_ci	[MPCG_EVENT_DOIO]	= "XID DoIO",
7318c2ecf20Sopenharmony_ci};
7328c2ecf20Sopenharmony_ci
7338c2ecf20Sopenharmony_cistatic const char *mpcg_state_names[] = {
7348c2ecf20Sopenharmony_ci	[MPCG_STATE_RESET]	= "Reset",
7358c2ecf20Sopenharmony_ci	[MPCG_STATE_INOP]	= "INOP",
7368c2ecf20Sopenharmony_ci	[MPCG_STATE_XID2INITW]	= "Passive XID- XID0 Pending Start",
7378c2ecf20Sopenharmony_ci	[MPCG_STATE_XID2INITX]	= "Passive XID- XID0 Pending Complete",
7388c2ecf20Sopenharmony_ci	[MPCG_STATE_XID7INITW]	= "Passive XID- XID7 Pending P1 Start",
7398c2ecf20Sopenharmony_ci	[MPCG_STATE_XID7INITX]	= "Passive XID- XID7 Pending P2 Complete",
7408c2ecf20Sopenharmony_ci	[MPCG_STATE_XID0IOWAIT]	= "Active  XID- XID0 Pending Start",
7418c2ecf20Sopenharmony_ci	[MPCG_STATE_XID0IOWAIX]	= "Active  XID- XID0 Pending Complete",
7428c2ecf20Sopenharmony_ci	[MPCG_STATE_XID7INITI]	= "Active  XID- XID7 Pending Start",
7438c2ecf20Sopenharmony_ci	[MPCG_STATE_XID7INITZ]	= "Active  XID- XID7 Pending Complete ",
7448c2ecf20Sopenharmony_ci	[MPCG_STATE_XID7INITF]	= "XID        - XID7 Complete ",
7458c2ecf20Sopenharmony_ci	[MPCG_STATE_FLOWC]	= "FLOW CONTROL ON",
7468c2ecf20Sopenharmony_ci	[MPCG_STATE_READY]	= "READY",
7478c2ecf20Sopenharmony_ci};
7488c2ecf20Sopenharmony_ci
7498c2ecf20Sopenharmony_ci/*
7508c2ecf20Sopenharmony_ci * The MPC Group Station FSM
7518c2ecf20Sopenharmony_ci *   22 events
7528c2ecf20Sopenharmony_ci */
7538c2ecf20Sopenharmony_cistatic const fsm_node mpcg_fsm[] = {
7548c2ecf20Sopenharmony_ci	{ MPCG_STATE_RESET,	MPCG_EVENT_INOP,	mpc_action_go_inop    },
7558c2ecf20Sopenharmony_ci	{ MPCG_STATE_INOP,	MPCG_EVENT_INOP,	mpc_action_nop        },
7568c2ecf20Sopenharmony_ci	{ MPCG_STATE_FLOWC,	MPCG_EVENT_INOP,	mpc_action_go_inop    },
7578c2ecf20Sopenharmony_ci
7588c2ecf20Sopenharmony_ci	{ MPCG_STATE_READY,	MPCG_EVENT_DISCONC,	mpc_action_discontact },
7598c2ecf20Sopenharmony_ci	{ MPCG_STATE_READY,	MPCG_EVENT_INOP,	mpc_action_go_inop    },
7608c2ecf20Sopenharmony_ci
7618c2ecf20Sopenharmony_ci	{ MPCG_STATE_XID2INITW,	MPCG_EVENT_XID0DO,	mpc_action_doxid0     },
7628c2ecf20Sopenharmony_ci	{ MPCG_STATE_XID2INITW,	MPCG_EVENT_XID2,	mpc_action_rcvd_xid0  },
7638c2ecf20Sopenharmony_ci	{ MPCG_STATE_XID2INITW,	MPCG_EVENT_INOP,	mpc_action_go_inop    },
7648c2ecf20Sopenharmony_ci	{ MPCG_STATE_XID2INITW,	MPCG_EVENT_TIMER,	mpc_action_timeout    },
7658c2ecf20Sopenharmony_ci	{ MPCG_STATE_XID2INITW,	MPCG_EVENT_DOIO,	mpc_action_yside_xid  },
7668c2ecf20Sopenharmony_ci
7678c2ecf20Sopenharmony_ci	{ MPCG_STATE_XID2INITX,	MPCG_EVENT_XID0DO,	mpc_action_doxid0     },
7688c2ecf20Sopenharmony_ci	{ MPCG_STATE_XID2INITX,	MPCG_EVENT_XID2,	mpc_action_rcvd_xid0  },
7698c2ecf20Sopenharmony_ci	{ MPCG_STATE_XID2INITX,	MPCG_EVENT_INOP,	mpc_action_go_inop    },
7708c2ecf20Sopenharmony_ci	{ MPCG_STATE_XID2INITX,	MPCG_EVENT_TIMER,	mpc_action_timeout    },
7718c2ecf20Sopenharmony_ci	{ MPCG_STATE_XID2INITX,	MPCG_EVENT_DOIO,	mpc_action_yside_xid  },
7728c2ecf20Sopenharmony_ci
7738c2ecf20Sopenharmony_ci	{ MPCG_STATE_XID7INITW,	MPCG_EVENT_XID2DONE,	mpc_action_doxid7     },
7748c2ecf20Sopenharmony_ci	{ MPCG_STATE_XID7INITW,	MPCG_EVENT_DISCONC,	mpc_action_discontact },
7758c2ecf20Sopenharmony_ci	{ MPCG_STATE_XID7INITW,	MPCG_EVENT_XID2,	mpc_action_rcvd_xid7  },
7768c2ecf20Sopenharmony_ci	{ MPCG_STATE_XID7INITW,	MPCG_EVENT_INOP,	mpc_action_go_inop    },
7778c2ecf20Sopenharmony_ci	{ MPCG_STATE_XID7INITW,	MPCG_EVENT_TIMER,	mpc_action_timeout    },
7788c2ecf20Sopenharmony_ci	{ MPCG_STATE_XID7INITW,	MPCG_EVENT_XID7DONE,	mpc_action_doxid7     },
7798c2ecf20Sopenharmony_ci	{ MPCG_STATE_XID7INITW,	MPCG_EVENT_DOIO,	mpc_action_yside_xid  },
7808c2ecf20Sopenharmony_ci
7818c2ecf20Sopenharmony_ci	{ MPCG_STATE_XID7INITX,	MPCG_EVENT_DISCONC,	mpc_action_discontact },
7828c2ecf20Sopenharmony_ci	{ MPCG_STATE_XID7INITX,	MPCG_EVENT_XID2,	mpc_action_rcvd_xid7  },
7838c2ecf20Sopenharmony_ci	{ MPCG_STATE_XID7INITX,	MPCG_EVENT_INOP,	mpc_action_go_inop    },
7848c2ecf20Sopenharmony_ci	{ MPCG_STATE_XID7INITX,	MPCG_EVENT_XID7DONE,	mpc_action_doxid7     },
7858c2ecf20Sopenharmony_ci	{ MPCG_STATE_XID7INITX,	MPCG_EVENT_TIMER,	mpc_action_timeout    },
7868c2ecf20Sopenharmony_ci	{ MPCG_STATE_XID7INITX,	MPCG_EVENT_DOIO,	mpc_action_yside_xid  },
7878c2ecf20Sopenharmony_ci
7888c2ecf20Sopenharmony_ci	{ MPCG_STATE_XID0IOWAIT, MPCG_EVENT_XID0DO,	mpc_action_doxid0     },
7898c2ecf20Sopenharmony_ci	{ MPCG_STATE_XID0IOWAIT, MPCG_EVENT_DISCONC,	mpc_action_discontact },
7908c2ecf20Sopenharmony_ci	{ MPCG_STATE_XID0IOWAIT, MPCG_EVENT_XID2,	mpc_action_rcvd_xid0  },
7918c2ecf20Sopenharmony_ci	{ MPCG_STATE_XID0IOWAIT, MPCG_EVENT_INOP,	mpc_action_go_inop    },
7928c2ecf20Sopenharmony_ci	{ MPCG_STATE_XID0IOWAIT, MPCG_EVENT_TIMER,	mpc_action_timeout    },
7938c2ecf20Sopenharmony_ci	{ MPCG_STATE_XID0IOWAIT, MPCG_EVENT_DOIO,	mpc_action_xside_xid  },
7948c2ecf20Sopenharmony_ci
7958c2ecf20Sopenharmony_ci	{ MPCG_STATE_XID0IOWAIX, MPCG_EVENT_XID0DO,	mpc_action_doxid0     },
7968c2ecf20Sopenharmony_ci	{ MPCG_STATE_XID0IOWAIX, MPCG_EVENT_DISCONC,	mpc_action_discontact },
7978c2ecf20Sopenharmony_ci	{ MPCG_STATE_XID0IOWAIX, MPCG_EVENT_XID2,	mpc_action_rcvd_xid0  },
7988c2ecf20Sopenharmony_ci	{ MPCG_STATE_XID0IOWAIX, MPCG_EVENT_INOP,	mpc_action_go_inop    },
7998c2ecf20Sopenharmony_ci	{ MPCG_STATE_XID0IOWAIX, MPCG_EVENT_TIMER,	mpc_action_timeout    },
8008c2ecf20Sopenharmony_ci	{ MPCG_STATE_XID0IOWAIX, MPCG_EVENT_DOIO,	mpc_action_xside_xid  },
8018c2ecf20Sopenharmony_ci
8028c2ecf20Sopenharmony_ci	{ MPCG_STATE_XID7INITI,	MPCG_EVENT_XID2DONE,	mpc_action_doxid7     },
8038c2ecf20Sopenharmony_ci	{ MPCG_STATE_XID7INITI,	MPCG_EVENT_XID2,	mpc_action_rcvd_xid7  },
8048c2ecf20Sopenharmony_ci	{ MPCG_STATE_XID7INITI,	MPCG_EVENT_DISCONC,	mpc_action_discontact },
8058c2ecf20Sopenharmony_ci	{ MPCG_STATE_XID7INITI,	MPCG_EVENT_INOP,	mpc_action_go_inop    },
8068c2ecf20Sopenharmony_ci	{ MPCG_STATE_XID7INITI,	MPCG_EVENT_TIMER,	mpc_action_timeout    },
8078c2ecf20Sopenharmony_ci	{ MPCG_STATE_XID7INITI,	MPCG_EVENT_XID7DONE,	mpc_action_doxid7     },
8088c2ecf20Sopenharmony_ci	{ MPCG_STATE_XID7INITI,	MPCG_EVENT_DOIO,	mpc_action_xside_xid  },
8098c2ecf20Sopenharmony_ci
8108c2ecf20Sopenharmony_ci	{ MPCG_STATE_XID7INITZ,	MPCG_EVENT_XID2,	mpc_action_rcvd_xid7  },
8118c2ecf20Sopenharmony_ci	{ MPCG_STATE_XID7INITZ,	MPCG_EVENT_XID7DONE,	mpc_action_doxid7     },
8128c2ecf20Sopenharmony_ci	{ MPCG_STATE_XID7INITZ,	MPCG_EVENT_DISCONC,	mpc_action_discontact },
8138c2ecf20Sopenharmony_ci	{ MPCG_STATE_XID7INITZ,	MPCG_EVENT_INOP,	mpc_action_go_inop    },
8148c2ecf20Sopenharmony_ci	{ MPCG_STATE_XID7INITZ,	MPCG_EVENT_TIMER,	mpc_action_timeout    },
8158c2ecf20Sopenharmony_ci	{ MPCG_STATE_XID7INITZ,	MPCG_EVENT_DOIO,	mpc_action_xside_xid  },
8168c2ecf20Sopenharmony_ci
8178c2ecf20Sopenharmony_ci	{ MPCG_STATE_XID7INITF,	MPCG_EVENT_INOP,	mpc_action_go_inop    },
8188c2ecf20Sopenharmony_ci	{ MPCG_STATE_XID7INITF,	MPCG_EVENT_XID7DONE,	mpc_action_go_ready   },
8198c2ecf20Sopenharmony_ci};
8208c2ecf20Sopenharmony_ci
8218c2ecf20Sopenharmony_cistatic int mpcg_fsm_len = ARRAY_SIZE(mpcg_fsm);
8228c2ecf20Sopenharmony_ci
8238c2ecf20Sopenharmony_ci/*
8248c2ecf20Sopenharmony_ci * MPC Group Station FSM action
8258c2ecf20Sopenharmony_ci * CTCM_PROTO_MPC only
8268c2ecf20Sopenharmony_ci */
8278c2ecf20Sopenharmony_cistatic void mpc_action_go_ready(fsm_instance *fsm, int event, void *arg)
8288c2ecf20Sopenharmony_ci{
8298c2ecf20Sopenharmony_ci	struct net_device *dev = arg;
8308c2ecf20Sopenharmony_ci	struct ctcm_priv *priv = dev->ml_priv;
8318c2ecf20Sopenharmony_ci	struct mpc_group *grp = priv->mpcg;
8328c2ecf20Sopenharmony_ci
8338c2ecf20Sopenharmony_ci	if (grp == NULL) {
8348c2ecf20Sopenharmony_ci		CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
8358c2ecf20Sopenharmony_ci			"%s(%s): No MPC group",
8368c2ecf20Sopenharmony_ci				CTCM_FUNTAIL, dev->name);
8378c2ecf20Sopenharmony_ci		return;
8388c2ecf20Sopenharmony_ci	}
8398c2ecf20Sopenharmony_ci
8408c2ecf20Sopenharmony_ci	fsm_deltimer(&grp->timer);
8418c2ecf20Sopenharmony_ci
8428c2ecf20Sopenharmony_ci	if (grp->saved_xid2->xid2_flag2 == 0x40) {
8438c2ecf20Sopenharmony_ci		priv->xid->xid2_flag2 = 0x00;
8448c2ecf20Sopenharmony_ci		if (grp->estconnfunc) {
8458c2ecf20Sopenharmony_ci			grp->estconnfunc(grp->port_num, 1,
8468c2ecf20Sopenharmony_ci					grp->group_max_buflen);
8478c2ecf20Sopenharmony_ci			grp->estconnfunc = NULL;
8488c2ecf20Sopenharmony_ci		} else if (grp->allochanfunc)
8498c2ecf20Sopenharmony_ci			grp->send_qllc_disc = 1;
8508c2ecf20Sopenharmony_ci
8518c2ecf20Sopenharmony_ci		fsm_event(grp->fsm, MPCG_EVENT_INOP, dev);
8528c2ecf20Sopenharmony_ci		CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
8538c2ecf20Sopenharmony_ci				"%s(%s): fails",
8548c2ecf20Sopenharmony_ci					CTCM_FUNTAIL, dev->name);
8558c2ecf20Sopenharmony_ci		return;
8568c2ecf20Sopenharmony_ci	}
8578c2ecf20Sopenharmony_ci
8588c2ecf20Sopenharmony_ci	grp->port_persist = 1;
8598c2ecf20Sopenharmony_ci	grp->out_of_sequence = 0;
8608c2ecf20Sopenharmony_ci	grp->estconn_called = 0;
8618c2ecf20Sopenharmony_ci
8628c2ecf20Sopenharmony_ci	tasklet_hi_schedule(&grp->mpc_tasklet2);
8638c2ecf20Sopenharmony_ci
8648c2ecf20Sopenharmony_ci	return;
8658c2ecf20Sopenharmony_ci}
8668c2ecf20Sopenharmony_ci
8678c2ecf20Sopenharmony_ci/*
8688c2ecf20Sopenharmony_ci * helper of ctcm_init_netdevice
8698c2ecf20Sopenharmony_ci * CTCM_PROTO_MPC only
8708c2ecf20Sopenharmony_ci */
8718c2ecf20Sopenharmony_civoid mpc_group_ready(unsigned long adev)
8728c2ecf20Sopenharmony_ci{
8738c2ecf20Sopenharmony_ci	struct net_device *dev = (struct net_device *)adev;
8748c2ecf20Sopenharmony_ci	struct ctcm_priv *priv = dev->ml_priv;
8758c2ecf20Sopenharmony_ci	struct mpc_group *grp = priv->mpcg;
8768c2ecf20Sopenharmony_ci	struct channel *ch = NULL;
8778c2ecf20Sopenharmony_ci
8788c2ecf20Sopenharmony_ci	if (grp == NULL) {
8798c2ecf20Sopenharmony_ci		CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
8808c2ecf20Sopenharmony_ci			"%s(%s): No MPC group",
8818c2ecf20Sopenharmony_ci				CTCM_FUNTAIL, dev->name);
8828c2ecf20Sopenharmony_ci		return;
8838c2ecf20Sopenharmony_ci	}
8848c2ecf20Sopenharmony_ci
8858c2ecf20Sopenharmony_ci	CTCM_DBF_TEXT_(MPC_SETUP, CTC_DBF_NOTICE,
8868c2ecf20Sopenharmony_ci		"%s: %s: GROUP TRANSITIONED TO READY, maxbuf = %d\n",
8878c2ecf20Sopenharmony_ci			CTCM_FUNTAIL, dev->name, grp->group_max_buflen);
8888c2ecf20Sopenharmony_ci
8898c2ecf20Sopenharmony_ci	fsm_newstate(grp->fsm, MPCG_STATE_READY);
8908c2ecf20Sopenharmony_ci
8918c2ecf20Sopenharmony_ci	/* Put up a read on the channel */
8928c2ecf20Sopenharmony_ci	ch = priv->channel[CTCM_READ];
8938c2ecf20Sopenharmony_ci	ch->pdu_seq = 0;
8948c2ecf20Sopenharmony_ci	CTCM_PR_DBGDATA("ctcmpc: %s() ToDCM_pdu_seq= %08x\n" ,
8958c2ecf20Sopenharmony_ci			__func__, ch->pdu_seq);
8968c2ecf20Sopenharmony_ci
8978c2ecf20Sopenharmony_ci	ctcmpc_chx_rxidle(ch->fsm, CTC_EVENT_START, ch);
8988c2ecf20Sopenharmony_ci	/* Put the write channel in idle state */
8998c2ecf20Sopenharmony_ci	ch = priv->channel[CTCM_WRITE];
9008c2ecf20Sopenharmony_ci	if (ch->collect_len > 0) {
9018c2ecf20Sopenharmony_ci		spin_lock(&ch->collect_lock);
9028c2ecf20Sopenharmony_ci		ctcm_purge_skb_queue(&ch->collect_queue);
9038c2ecf20Sopenharmony_ci		ch->collect_len = 0;
9048c2ecf20Sopenharmony_ci		spin_unlock(&ch->collect_lock);
9058c2ecf20Sopenharmony_ci	}
9068c2ecf20Sopenharmony_ci	ctcm_chx_txidle(ch->fsm, CTC_EVENT_START, ch);
9078c2ecf20Sopenharmony_ci	ctcm_clear_busy(dev);
9088c2ecf20Sopenharmony_ci
9098c2ecf20Sopenharmony_ci	if (grp->estconnfunc) {
9108c2ecf20Sopenharmony_ci		grp->estconnfunc(grp->port_num, 0,
9118c2ecf20Sopenharmony_ci				    grp->group_max_buflen);
9128c2ecf20Sopenharmony_ci		grp->estconnfunc = NULL;
9138c2ecf20Sopenharmony_ci	} else 	if (grp->allochanfunc)
9148c2ecf20Sopenharmony_ci		grp->allochanfunc(grp->port_num, grp->group_max_buflen);
9158c2ecf20Sopenharmony_ci
9168c2ecf20Sopenharmony_ci	grp->send_qllc_disc = 1;
9178c2ecf20Sopenharmony_ci	grp->changed_side = 0;
9188c2ecf20Sopenharmony_ci
9198c2ecf20Sopenharmony_ci	return;
9208c2ecf20Sopenharmony_ci
9218c2ecf20Sopenharmony_ci}
9228c2ecf20Sopenharmony_ci
9238c2ecf20Sopenharmony_ci/*
9248c2ecf20Sopenharmony_ci * Increment the MPC Group Active Channel Counts
9258c2ecf20Sopenharmony_ci * helper of dev_action (called from channel fsm)
9268c2ecf20Sopenharmony_ci */
9278c2ecf20Sopenharmony_civoid mpc_channel_action(struct channel *ch, int direction, int action)
9288c2ecf20Sopenharmony_ci{
9298c2ecf20Sopenharmony_ci	struct net_device  *dev  = ch->netdev;
9308c2ecf20Sopenharmony_ci	struct ctcm_priv   *priv = dev->ml_priv;
9318c2ecf20Sopenharmony_ci	struct mpc_group   *grp  = priv->mpcg;
9328c2ecf20Sopenharmony_ci
9338c2ecf20Sopenharmony_ci	if (grp == NULL) {
9348c2ecf20Sopenharmony_ci		CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
9358c2ecf20Sopenharmony_ci			"%s(%s): No MPC group",
9368c2ecf20Sopenharmony_ci				CTCM_FUNTAIL, dev->name);
9378c2ecf20Sopenharmony_ci		return;
9388c2ecf20Sopenharmony_ci	}
9398c2ecf20Sopenharmony_ci
9408c2ecf20Sopenharmony_ci	CTCM_PR_DEBUG("enter %s: ch=0x%p id=%s\n", __func__, ch, ch->id);
9418c2ecf20Sopenharmony_ci
9428c2ecf20Sopenharmony_ci	CTCM_DBF_TEXT_(MPC_TRACE, CTC_DBF_NOTICE,
9438c2ecf20Sopenharmony_ci		"%s: %i / Grp:%s total_channels=%i, active_channels: "
9448c2ecf20Sopenharmony_ci		"read=%i, write=%i\n", __func__, action,
9458c2ecf20Sopenharmony_ci		fsm_getstate_str(grp->fsm), grp->num_channel_paths,
9468c2ecf20Sopenharmony_ci		grp->active_channels[CTCM_READ],
9478c2ecf20Sopenharmony_ci		grp->active_channels[CTCM_WRITE]);
9488c2ecf20Sopenharmony_ci
9498c2ecf20Sopenharmony_ci	if ((action == MPC_CHANNEL_ADD) && (ch->in_mpcgroup == 0)) {
9508c2ecf20Sopenharmony_ci		grp->num_channel_paths++;
9518c2ecf20Sopenharmony_ci		grp->active_channels[direction]++;
9528c2ecf20Sopenharmony_ci		grp->outstanding_xid2++;
9538c2ecf20Sopenharmony_ci		ch->in_mpcgroup = 1;
9548c2ecf20Sopenharmony_ci
9558c2ecf20Sopenharmony_ci		if (ch->xid_skb != NULL)
9568c2ecf20Sopenharmony_ci			dev_kfree_skb_any(ch->xid_skb);
9578c2ecf20Sopenharmony_ci
9588c2ecf20Sopenharmony_ci		ch->xid_skb = __dev_alloc_skb(MPC_BUFSIZE_DEFAULT,
9598c2ecf20Sopenharmony_ci					GFP_ATOMIC | GFP_DMA);
9608c2ecf20Sopenharmony_ci		if (ch->xid_skb == NULL) {
9618c2ecf20Sopenharmony_ci			CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
9628c2ecf20Sopenharmony_ci				"%s(%s): Couldn't alloc ch xid_skb\n",
9638c2ecf20Sopenharmony_ci				CTCM_FUNTAIL, dev->name);
9648c2ecf20Sopenharmony_ci			fsm_event(grp->fsm, MPCG_EVENT_INOP, dev);
9658c2ecf20Sopenharmony_ci			return;
9668c2ecf20Sopenharmony_ci		}
9678c2ecf20Sopenharmony_ci		ch->xid_skb_data = ch->xid_skb->data;
9688c2ecf20Sopenharmony_ci		ch->xid_th = (struct th_header *)ch->xid_skb->data;
9698c2ecf20Sopenharmony_ci		skb_put(ch->xid_skb, TH_HEADER_LENGTH);
9708c2ecf20Sopenharmony_ci		ch->xid = (struct xid2 *)skb_tail_pointer(ch->xid_skb);
9718c2ecf20Sopenharmony_ci		skb_put(ch->xid_skb, XID2_LENGTH);
9728c2ecf20Sopenharmony_ci		ch->xid_id = skb_tail_pointer(ch->xid_skb);
9738c2ecf20Sopenharmony_ci		ch->xid_skb->data = ch->xid_skb_data;
9748c2ecf20Sopenharmony_ci		skb_reset_tail_pointer(ch->xid_skb);
9758c2ecf20Sopenharmony_ci		ch->xid_skb->len = 0;
9768c2ecf20Sopenharmony_ci
9778c2ecf20Sopenharmony_ci		skb_put_data(ch->xid_skb, grp->xid_skb->data,
9788c2ecf20Sopenharmony_ci			     grp->xid_skb->len);
9798c2ecf20Sopenharmony_ci
9808c2ecf20Sopenharmony_ci		ch->xid->xid2_dlc_type =
9818c2ecf20Sopenharmony_ci			((CHANNEL_DIRECTION(ch->flags) == CTCM_READ)
9828c2ecf20Sopenharmony_ci				? XID2_READ_SIDE : XID2_WRITE_SIDE);
9838c2ecf20Sopenharmony_ci
9848c2ecf20Sopenharmony_ci		if (CHANNEL_DIRECTION(ch->flags) == CTCM_WRITE)
9858c2ecf20Sopenharmony_ci			ch->xid->xid2_buf_len = 0x00;
9868c2ecf20Sopenharmony_ci
9878c2ecf20Sopenharmony_ci		ch->xid_skb->data = ch->xid_skb_data;
9888c2ecf20Sopenharmony_ci		skb_reset_tail_pointer(ch->xid_skb);
9898c2ecf20Sopenharmony_ci		ch->xid_skb->len = 0;
9908c2ecf20Sopenharmony_ci
9918c2ecf20Sopenharmony_ci		fsm_newstate(ch->fsm, CH_XID0_PENDING);
9928c2ecf20Sopenharmony_ci
9938c2ecf20Sopenharmony_ci		if ((grp->active_channels[CTCM_READ] > 0) &&
9948c2ecf20Sopenharmony_ci		    (grp->active_channels[CTCM_WRITE] > 0) &&
9958c2ecf20Sopenharmony_ci			(fsm_getstate(grp->fsm) < MPCG_STATE_XID2INITW)) {
9968c2ecf20Sopenharmony_ci			fsm_newstate(grp->fsm, MPCG_STATE_XID2INITW);
9978c2ecf20Sopenharmony_ci			CTCM_DBF_TEXT_(MPC_SETUP, CTC_DBF_NOTICE,
9988c2ecf20Sopenharmony_ci				"%s: %s: MPC GROUP CHANNELS ACTIVE\n",
9998c2ecf20Sopenharmony_ci						__func__, dev->name);
10008c2ecf20Sopenharmony_ci		}
10018c2ecf20Sopenharmony_ci	} else if ((action == MPC_CHANNEL_REMOVE) &&
10028c2ecf20Sopenharmony_ci			(ch->in_mpcgroup == 1)) {
10038c2ecf20Sopenharmony_ci		ch->in_mpcgroup = 0;
10048c2ecf20Sopenharmony_ci		grp->num_channel_paths--;
10058c2ecf20Sopenharmony_ci		grp->active_channels[direction]--;
10068c2ecf20Sopenharmony_ci
10078c2ecf20Sopenharmony_ci		if (ch->xid_skb != NULL)
10088c2ecf20Sopenharmony_ci			dev_kfree_skb_any(ch->xid_skb);
10098c2ecf20Sopenharmony_ci		ch->xid_skb = NULL;
10108c2ecf20Sopenharmony_ci
10118c2ecf20Sopenharmony_ci		if (grp->channels_terminating)
10128c2ecf20Sopenharmony_ci					goto done;
10138c2ecf20Sopenharmony_ci
10148c2ecf20Sopenharmony_ci		if (((grp->active_channels[CTCM_READ] == 0) &&
10158c2ecf20Sopenharmony_ci					(grp->active_channels[CTCM_WRITE] > 0))
10168c2ecf20Sopenharmony_ci			|| ((grp->active_channels[CTCM_WRITE] == 0) &&
10178c2ecf20Sopenharmony_ci					(grp->active_channels[CTCM_READ] > 0)))
10188c2ecf20Sopenharmony_ci			fsm_event(grp->fsm, MPCG_EVENT_INOP, dev);
10198c2ecf20Sopenharmony_ci	}
10208c2ecf20Sopenharmony_cidone:
10218c2ecf20Sopenharmony_ci	CTCM_DBF_TEXT_(MPC_TRACE, CTC_DBF_DEBUG,
10228c2ecf20Sopenharmony_ci		"exit %s: %i / Grp:%s total_channels=%i, active_channels: "
10238c2ecf20Sopenharmony_ci		"read=%i, write=%i\n", __func__, action,
10248c2ecf20Sopenharmony_ci		fsm_getstate_str(grp->fsm), grp->num_channel_paths,
10258c2ecf20Sopenharmony_ci		grp->active_channels[CTCM_READ],
10268c2ecf20Sopenharmony_ci		grp->active_channels[CTCM_WRITE]);
10278c2ecf20Sopenharmony_ci
10288c2ecf20Sopenharmony_ci	CTCM_PR_DEBUG("exit %s: ch=0x%p id=%s\n", __func__, ch, ch->id);
10298c2ecf20Sopenharmony_ci}
10308c2ecf20Sopenharmony_ci
10318c2ecf20Sopenharmony_ci/**
10328c2ecf20Sopenharmony_ci * Unpack a just received skb and hand it over to
10338c2ecf20Sopenharmony_ci * upper layers.
10348c2ecf20Sopenharmony_ci * special MPC version of unpack_skb.
10358c2ecf20Sopenharmony_ci *
10368c2ecf20Sopenharmony_ci * ch		The channel where this skb has been received.
10378c2ecf20Sopenharmony_ci * pskb		The received skb.
10388c2ecf20Sopenharmony_ci */
10398c2ecf20Sopenharmony_cistatic void ctcmpc_unpack_skb(struct channel *ch, struct sk_buff *pskb)
10408c2ecf20Sopenharmony_ci{
10418c2ecf20Sopenharmony_ci	struct net_device *dev	= ch->netdev;
10428c2ecf20Sopenharmony_ci	struct ctcm_priv *priv = dev->ml_priv;
10438c2ecf20Sopenharmony_ci	struct mpc_group *grp = priv->mpcg;
10448c2ecf20Sopenharmony_ci	struct pdu *curr_pdu;
10458c2ecf20Sopenharmony_ci	struct mpcg_info *mpcginfo;
10468c2ecf20Sopenharmony_ci	struct th_header *header = NULL;
10478c2ecf20Sopenharmony_ci	struct th_sweep *sweep = NULL;
10488c2ecf20Sopenharmony_ci	int pdu_last_seen = 0;
10498c2ecf20Sopenharmony_ci	__u32 new_len;
10508c2ecf20Sopenharmony_ci	struct sk_buff *skb;
10518c2ecf20Sopenharmony_ci	int skblen;
10528c2ecf20Sopenharmony_ci	int sendrc = 0;
10538c2ecf20Sopenharmony_ci
10548c2ecf20Sopenharmony_ci	CTCM_PR_DEBUG("ctcmpc enter: %s() %s cp:%i ch:%s\n",
10558c2ecf20Sopenharmony_ci			__func__, dev->name, smp_processor_id(), ch->id);
10568c2ecf20Sopenharmony_ci
10578c2ecf20Sopenharmony_ci	header = (struct th_header *)pskb->data;
10588c2ecf20Sopenharmony_ci	if ((header->th_seg == 0) &&
10598c2ecf20Sopenharmony_ci		(header->th_ch_flag == 0) &&
10608c2ecf20Sopenharmony_ci		(header->th_blk_flag == 0) &&
10618c2ecf20Sopenharmony_ci		(header->th_seq_num == 0))
10628c2ecf20Sopenharmony_ci		/* nothing for us */	goto done;
10638c2ecf20Sopenharmony_ci
10648c2ecf20Sopenharmony_ci	CTCM_PR_DBGDATA("%s: th_header\n", __func__);
10658c2ecf20Sopenharmony_ci	CTCM_D3_DUMP((char *)header, TH_HEADER_LENGTH);
10668c2ecf20Sopenharmony_ci	CTCM_PR_DBGDATA("%s: pskb len: %04x \n", __func__, pskb->len);
10678c2ecf20Sopenharmony_ci
10688c2ecf20Sopenharmony_ci	pskb->dev = dev;
10698c2ecf20Sopenharmony_ci	pskb->ip_summed = CHECKSUM_UNNECESSARY;
10708c2ecf20Sopenharmony_ci	skb_pull(pskb, TH_HEADER_LENGTH);
10718c2ecf20Sopenharmony_ci
10728c2ecf20Sopenharmony_ci	if (likely(header->th_ch_flag == TH_HAS_PDU)) {
10738c2ecf20Sopenharmony_ci		CTCM_PR_DBGDATA("%s: came into th_has_pdu\n", __func__);
10748c2ecf20Sopenharmony_ci		if ((fsm_getstate(grp->fsm) == MPCG_STATE_FLOWC) ||
10758c2ecf20Sopenharmony_ci		   ((fsm_getstate(grp->fsm) == MPCG_STATE_READY) &&
10768c2ecf20Sopenharmony_ci		    (header->th_seq_num != ch->th_seq_num + 1) &&
10778c2ecf20Sopenharmony_ci		    (ch->th_seq_num != 0))) {
10788c2ecf20Sopenharmony_ci			/* This is NOT the next segment		*
10798c2ecf20Sopenharmony_ci			 * we are not the correct race winner	*
10808c2ecf20Sopenharmony_ci			 * go away and let someone else win	*
10818c2ecf20Sopenharmony_ci			 * BUT..this only applies if xid negot	*
10828c2ecf20Sopenharmony_ci			 * is done				*
10838c2ecf20Sopenharmony_ci			*/
10848c2ecf20Sopenharmony_ci			grp->out_of_sequence += 1;
10858c2ecf20Sopenharmony_ci			__skb_push(pskb, TH_HEADER_LENGTH);
10868c2ecf20Sopenharmony_ci			skb_queue_tail(&ch->io_queue, pskb);
10878c2ecf20Sopenharmony_ci			CTCM_PR_DBGDATA("%s: th_seq_num expect:%08x "
10888c2ecf20Sopenharmony_ci					"got:%08x\n", __func__,
10898c2ecf20Sopenharmony_ci				ch->th_seq_num + 1, header->th_seq_num);
10908c2ecf20Sopenharmony_ci
10918c2ecf20Sopenharmony_ci			return;
10928c2ecf20Sopenharmony_ci		}
10938c2ecf20Sopenharmony_ci		grp->out_of_sequence = 0;
10948c2ecf20Sopenharmony_ci		ch->th_seq_num = header->th_seq_num;
10958c2ecf20Sopenharmony_ci
10968c2ecf20Sopenharmony_ci		CTCM_PR_DBGDATA("ctcmpc: %s() FromVTAM_th_seq=%08x\n",
10978c2ecf20Sopenharmony_ci					__func__, ch->th_seq_num);
10988c2ecf20Sopenharmony_ci
10998c2ecf20Sopenharmony_ci		if (unlikely(fsm_getstate(grp->fsm) != MPCG_STATE_READY))
11008c2ecf20Sopenharmony_ci					goto done;
11018c2ecf20Sopenharmony_ci		while ((pskb->len > 0) && !pdu_last_seen) {
11028c2ecf20Sopenharmony_ci			curr_pdu = (struct pdu *)pskb->data;
11038c2ecf20Sopenharmony_ci
11048c2ecf20Sopenharmony_ci			CTCM_PR_DBGDATA("%s: pdu_header\n", __func__);
11058c2ecf20Sopenharmony_ci			CTCM_D3_DUMP((char *)pskb->data, PDU_HEADER_LENGTH);
11068c2ecf20Sopenharmony_ci			CTCM_PR_DBGDATA("%s: pskb len: %04x \n",
11078c2ecf20Sopenharmony_ci						__func__, pskb->len);
11088c2ecf20Sopenharmony_ci
11098c2ecf20Sopenharmony_ci			skb_pull(pskb, PDU_HEADER_LENGTH);
11108c2ecf20Sopenharmony_ci
11118c2ecf20Sopenharmony_ci			if (curr_pdu->pdu_flag & PDU_LAST)
11128c2ecf20Sopenharmony_ci				pdu_last_seen = 1;
11138c2ecf20Sopenharmony_ci			if (curr_pdu->pdu_flag & PDU_CNTL)
11148c2ecf20Sopenharmony_ci				pskb->protocol = htons(ETH_P_SNAP);
11158c2ecf20Sopenharmony_ci			else
11168c2ecf20Sopenharmony_ci				pskb->protocol = htons(ETH_P_SNA_DIX);
11178c2ecf20Sopenharmony_ci
11188c2ecf20Sopenharmony_ci			if ((pskb->len <= 0) || (pskb->len > ch->max_bufsize)) {
11198c2ecf20Sopenharmony_ci				CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
11208c2ecf20Sopenharmony_ci					"%s(%s): Dropping packet with "
11218c2ecf20Sopenharmony_ci					"illegal siize %d",
11228c2ecf20Sopenharmony_ci					CTCM_FUNTAIL, dev->name, pskb->len);
11238c2ecf20Sopenharmony_ci
11248c2ecf20Sopenharmony_ci				priv->stats.rx_dropped++;
11258c2ecf20Sopenharmony_ci				priv->stats.rx_length_errors++;
11268c2ecf20Sopenharmony_ci					goto done;
11278c2ecf20Sopenharmony_ci			}
11288c2ecf20Sopenharmony_ci			skb_reset_mac_header(pskb);
11298c2ecf20Sopenharmony_ci			new_len = curr_pdu->pdu_offset;
11308c2ecf20Sopenharmony_ci			CTCM_PR_DBGDATA("%s: new_len: %04x \n",
11318c2ecf20Sopenharmony_ci						__func__, new_len);
11328c2ecf20Sopenharmony_ci			if ((new_len == 0) || (new_len > pskb->len)) {
11338c2ecf20Sopenharmony_ci				/* should never happen		    */
11348c2ecf20Sopenharmony_ci				/* pskb len must be hosed...bail out */
11358c2ecf20Sopenharmony_ci				CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
11368c2ecf20Sopenharmony_ci					"%s(%s): non valid pdu_offset: %04x",
11378c2ecf20Sopenharmony_ci					/* "data may be lost", */
11388c2ecf20Sopenharmony_ci					CTCM_FUNTAIL, dev->name, new_len);
11398c2ecf20Sopenharmony_ci				goto done;
11408c2ecf20Sopenharmony_ci			}
11418c2ecf20Sopenharmony_ci			skb = __dev_alloc_skb(new_len+4, GFP_ATOMIC);
11428c2ecf20Sopenharmony_ci
11438c2ecf20Sopenharmony_ci			if (!skb) {
11448c2ecf20Sopenharmony_ci				CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
11458c2ecf20Sopenharmony_ci					"%s(%s): MEMORY allocation error",
11468c2ecf20Sopenharmony_ci						CTCM_FUNTAIL, dev->name);
11478c2ecf20Sopenharmony_ci				priv->stats.rx_dropped++;
11488c2ecf20Sopenharmony_ci				fsm_event(grp->fsm, MPCG_EVENT_INOP, dev);
11498c2ecf20Sopenharmony_ci						goto done;
11508c2ecf20Sopenharmony_ci			}
11518c2ecf20Sopenharmony_ci			skb_put_data(skb, pskb->data, new_len);
11528c2ecf20Sopenharmony_ci
11538c2ecf20Sopenharmony_ci			skb_reset_mac_header(skb);
11548c2ecf20Sopenharmony_ci			skb->dev = pskb->dev;
11558c2ecf20Sopenharmony_ci			skb->protocol = pskb->protocol;
11568c2ecf20Sopenharmony_ci			skb->ip_summed = CHECKSUM_UNNECESSARY;
11578c2ecf20Sopenharmony_ci			*((__u32 *) skb_push(skb, 4)) = ch->pdu_seq;
11588c2ecf20Sopenharmony_ci			ch->pdu_seq++;
11598c2ecf20Sopenharmony_ci
11608c2ecf20Sopenharmony_ci			if (do_debug_data) {
11618c2ecf20Sopenharmony_ci				ctcm_pr_debug("%s: ToDCM_pdu_seq= %08x\n",
11628c2ecf20Sopenharmony_ci						__func__, ch->pdu_seq);
11638c2ecf20Sopenharmony_ci				ctcm_pr_debug("%s: skb:%0lx "
11648c2ecf20Sopenharmony_ci					"skb len: %d \n", __func__,
11658c2ecf20Sopenharmony_ci					(unsigned long)skb, skb->len);
11668c2ecf20Sopenharmony_ci				ctcm_pr_debug("%s: up to 32 bytes "
11678c2ecf20Sopenharmony_ci					"of pdu_data sent\n", __func__);
11688c2ecf20Sopenharmony_ci				ctcmpc_dump32((char *)skb->data, skb->len);
11698c2ecf20Sopenharmony_ci			}
11708c2ecf20Sopenharmony_ci
11718c2ecf20Sopenharmony_ci			skblen = skb->len;
11728c2ecf20Sopenharmony_ci			sendrc = netif_rx(skb);
11738c2ecf20Sopenharmony_ci			priv->stats.rx_packets++;
11748c2ecf20Sopenharmony_ci			priv->stats.rx_bytes += skblen;
11758c2ecf20Sopenharmony_ci			skb_pull(pskb, new_len); /* point to next PDU */
11768c2ecf20Sopenharmony_ci		}
11778c2ecf20Sopenharmony_ci	} else {
11788c2ecf20Sopenharmony_ci		mpcginfo = kmalloc(sizeof(struct mpcg_info), gfp_type());
11798c2ecf20Sopenharmony_ci		if (mpcginfo == NULL)
11808c2ecf20Sopenharmony_ci					goto done;
11818c2ecf20Sopenharmony_ci
11828c2ecf20Sopenharmony_ci		mpcginfo->ch = ch;
11838c2ecf20Sopenharmony_ci		mpcginfo->th = header;
11848c2ecf20Sopenharmony_ci		mpcginfo->skb = pskb;
11858c2ecf20Sopenharmony_ci		CTCM_PR_DEBUG("%s: Not PDU - may be control pkt\n",
11868c2ecf20Sopenharmony_ci					__func__);
11878c2ecf20Sopenharmony_ci		/*  it's a sweep?   */
11888c2ecf20Sopenharmony_ci		sweep = (struct th_sweep *)pskb->data;
11898c2ecf20Sopenharmony_ci		mpcginfo->sweep = sweep;
11908c2ecf20Sopenharmony_ci		if (header->th_ch_flag == TH_SWEEP_REQ)
11918c2ecf20Sopenharmony_ci			mpc_rcvd_sweep_req(mpcginfo);
11928c2ecf20Sopenharmony_ci		else if (header->th_ch_flag == TH_SWEEP_RESP)
11938c2ecf20Sopenharmony_ci			mpc_rcvd_sweep_resp(mpcginfo);
11948c2ecf20Sopenharmony_ci		else if (header->th_blk_flag == TH_DATA_IS_XID) {
11958c2ecf20Sopenharmony_ci			struct xid2 *thisxid = (struct xid2 *)pskb->data;
11968c2ecf20Sopenharmony_ci			skb_pull(pskb, XID2_LENGTH);
11978c2ecf20Sopenharmony_ci			mpcginfo->xid = thisxid;
11988c2ecf20Sopenharmony_ci			fsm_event(grp->fsm, MPCG_EVENT_XID2, mpcginfo);
11998c2ecf20Sopenharmony_ci		} else if (header->th_blk_flag == TH_DISCONTACT)
12008c2ecf20Sopenharmony_ci			fsm_event(grp->fsm, MPCG_EVENT_DISCONC, mpcginfo);
12018c2ecf20Sopenharmony_ci		else if (header->th_seq_num != 0) {
12028c2ecf20Sopenharmony_ci			CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
12038c2ecf20Sopenharmony_ci				"%s(%s): control pkt expected\n",
12048c2ecf20Sopenharmony_ci						CTCM_FUNTAIL, dev->name);
12058c2ecf20Sopenharmony_ci			priv->stats.rx_dropped++;
12068c2ecf20Sopenharmony_ci			/* mpcginfo only used for non-data transfers */
12078c2ecf20Sopenharmony_ci			if (do_debug_data)
12088c2ecf20Sopenharmony_ci				ctcmpc_dump_skb(pskb, -8);
12098c2ecf20Sopenharmony_ci		}
12108c2ecf20Sopenharmony_ci		kfree(mpcginfo);
12118c2ecf20Sopenharmony_ci	}
12128c2ecf20Sopenharmony_cidone:
12138c2ecf20Sopenharmony_ci
12148c2ecf20Sopenharmony_ci	dev_kfree_skb_any(pskb);
12158c2ecf20Sopenharmony_ci	if (sendrc == NET_RX_DROP) {
12168c2ecf20Sopenharmony_ci		dev_warn(&dev->dev,
12178c2ecf20Sopenharmony_ci			"The network backlog for %s is exceeded, "
12188c2ecf20Sopenharmony_ci			"package dropped\n", __func__);
12198c2ecf20Sopenharmony_ci		fsm_event(grp->fsm, MPCG_EVENT_INOP, dev);
12208c2ecf20Sopenharmony_ci	}
12218c2ecf20Sopenharmony_ci
12228c2ecf20Sopenharmony_ci	CTCM_PR_DEBUG("exit %s: %s: ch=0x%p id=%s\n",
12238c2ecf20Sopenharmony_ci			__func__, dev->name, ch, ch->id);
12248c2ecf20Sopenharmony_ci}
12258c2ecf20Sopenharmony_ci
12268c2ecf20Sopenharmony_ci/**
12278c2ecf20Sopenharmony_ci * tasklet helper for mpc's skb unpacking.
12288c2ecf20Sopenharmony_ci *
12298c2ecf20Sopenharmony_ci * ch		The channel to work on.
12308c2ecf20Sopenharmony_ci * Allow flow control back pressure to occur here.
12318c2ecf20Sopenharmony_ci * Throttling back channel can result in excessive
12328c2ecf20Sopenharmony_ci * channel inactivity and system deact of channel
12338c2ecf20Sopenharmony_ci */
12348c2ecf20Sopenharmony_civoid ctcmpc_bh(unsigned long thischan)
12358c2ecf20Sopenharmony_ci{
12368c2ecf20Sopenharmony_ci	struct channel	  *ch	= (struct channel *)thischan;
12378c2ecf20Sopenharmony_ci	struct sk_buff	  *skb;
12388c2ecf20Sopenharmony_ci	struct net_device *dev	= ch->netdev;
12398c2ecf20Sopenharmony_ci	struct ctcm_priv  *priv	= dev->ml_priv;
12408c2ecf20Sopenharmony_ci	struct mpc_group  *grp	= priv->mpcg;
12418c2ecf20Sopenharmony_ci
12428c2ecf20Sopenharmony_ci	CTCM_PR_DEBUG("%s cp:%i enter:  %s() %s\n",
12438c2ecf20Sopenharmony_ci	       dev->name, smp_processor_id(), __func__, ch->id);
12448c2ecf20Sopenharmony_ci	/* caller has requested driver to throttle back */
12458c2ecf20Sopenharmony_ci	while ((fsm_getstate(grp->fsm) != MPCG_STATE_FLOWC) &&
12468c2ecf20Sopenharmony_ci			(skb = skb_dequeue(&ch->io_queue))) {
12478c2ecf20Sopenharmony_ci		ctcmpc_unpack_skb(ch, skb);
12488c2ecf20Sopenharmony_ci		if (grp->out_of_sequence > 20) {
12498c2ecf20Sopenharmony_ci			/* assume data loss has occurred if */
12508c2ecf20Sopenharmony_ci			/* missing seq_num for extended     */
12518c2ecf20Sopenharmony_ci			/* period of time		    */
12528c2ecf20Sopenharmony_ci			grp->out_of_sequence = 0;
12538c2ecf20Sopenharmony_ci			fsm_event(grp->fsm, MPCG_EVENT_INOP, dev);
12548c2ecf20Sopenharmony_ci			break;
12558c2ecf20Sopenharmony_ci		}
12568c2ecf20Sopenharmony_ci		if (skb == skb_peek(&ch->io_queue))
12578c2ecf20Sopenharmony_ci			break;
12588c2ecf20Sopenharmony_ci	}
12598c2ecf20Sopenharmony_ci	CTCM_PR_DEBUG("exit %s: %s: ch=0x%p id=%s\n",
12608c2ecf20Sopenharmony_ci			__func__, dev->name, ch, ch->id);
12618c2ecf20Sopenharmony_ci	return;
12628c2ecf20Sopenharmony_ci}
12638c2ecf20Sopenharmony_ci
12648c2ecf20Sopenharmony_ci/*
12658c2ecf20Sopenharmony_ci *  MPC Group Initializations
12668c2ecf20Sopenharmony_ci */
12678c2ecf20Sopenharmony_cistruct mpc_group *ctcmpc_init_mpc_group(struct ctcm_priv *priv)
12688c2ecf20Sopenharmony_ci{
12698c2ecf20Sopenharmony_ci	struct mpc_group *grp;
12708c2ecf20Sopenharmony_ci
12718c2ecf20Sopenharmony_ci	CTCM_DBF_TEXT_(MPC_SETUP, CTC_DBF_INFO,
12728c2ecf20Sopenharmony_ci			"Enter %s(%p)", CTCM_FUNTAIL, priv);
12738c2ecf20Sopenharmony_ci
12748c2ecf20Sopenharmony_ci	grp = kzalloc(sizeof(struct mpc_group), GFP_KERNEL);
12758c2ecf20Sopenharmony_ci	if (grp == NULL)
12768c2ecf20Sopenharmony_ci		return NULL;
12778c2ecf20Sopenharmony_ci
12788c2ecf20Sopenharmony_ci	grp->fsm = init_fsm("mpcg", mpcg_state_names, mpcg_event_names,
12798c2ecf20Sopenharmony_ci			MPCG_NR_STATES, MPCG_NR_EVENTS, mpcg_fsm,
12808c2ecf20Sopenharmony_ci			mpcg_fsm_len, GFP_KERNEL);
12818c2ecf20Sopenharmony_ci	if (grp->fsm == NULL) {
12828c2ecf20Sopenharmony_ci		kfree(grp);
12838c2ecf20Sopenharmony_ci		return NULL;
12848c2ecf20Sopenharmony_ci	}
12858c2ecf20Sopenharmony_ci
12868c2ecf20Sopenharmony_ci	fsm_newstate(grp->fsm, MPCG_STATE_RESET);
12878c2ecf20Sopenharmony_ci	fsm_settimer(grp->fsm, &grp->timer);
12888c2ecf20Sopenharmony_ci
12898c2ecf20Sopenharmony_ci	grp->xid_skb =
12908c2ecf20Sopenharmony_ci		 __dev_alloc_skb(MPC_BUFSIZE_DEFAULT, GFP_ATOMIC | GFP_DMA);
12918c2ecf20Sopenharmony_ci	if (grp->xid_skb == NULL) {
12928c2ecf20Sopenharmony_ci		kfree_fsm(grp->fsm);
12938c2ecf20Sopenharmony_ci		kfree(grp);
12948c2ecf20Sopenharmony_ci		return NULL;
12958c2ecf20Sopenharmony_ci	}
12968c2ecf20Sopenharmony_ci	/*  base xid for all channels in group  */
12978c2ecf20Sopenharmony_ci	grp->xid_skb_data = grp->xid_skb->data;
12988c2ecf20Sopenharmony_ci	grp->xid_th = (struct th_header *)grp->xid_skb->data;
12998c2ecf20Sopenharmony_ci	skb_put_data(grp->xid_skb, &thnorm, TH_HEADER_LENGTH);
13008c2ecf20Sopenharmony_ci
13018c2ecf20Sopenharmony_ci	grp->xid = (struct xid2 *)skb_tail_pointer(grp->xid_skb);
13028c2ecf20Sopenharmony_ci	skb_put_data(grp->xid_skb, &init_xid, XID2_LENGTH);
13038c2ecf20Sopenharmony_ci	grp->xid->xid2_adj_id = jiffies | 0xfff00000;
13048c2ecf20Sopenharmony_ci	grp->xid->xid2_sender_id = jiffies;
13058c2ecf20Sopenharmony_ci
13068c2ecf20Sopenharmony_ci	grp->xid_id = skb_tail_pointer(grp->xid_skb);
13078c2ecf20Sopenharmony_ci	skb_put_data(grp->xid_skb, "VTAM", 4);
13088c2ecf20Sopenharmony_ci
13098c2ecf20Sopenharmony_ci	grp->rcvd_xid_skb =
13108c2ecf20Sopenharmony_ci		__dev_alloc_skb(MPC_BUFSIZE_DEFAULT, GFP_ATOMIC|GFP_DMA);
13118c2ecf20Sopenharmony_ci	if (grp->rcvd_xid_skb == NULL) {
13128c2ecf20Sopenharmony_ci		kfree_fsm(grp->fsm);
13138c2ecf20Sopenharmony_ci		dev_kfree_skb(grp->xid_skb);
13148c2ecf20Sopenharmony_ci		kfree(grp);
13158c2ecf20Sopenharmony_ci		return NULL;
13168c2ecf20Sopenharmony_ci	}
13178c2ecf20Sopenharmony_ci	grp->rcvd_xid_data = grp->rcvd_xid_skb->data;
13188c2ecf20Sopenharmony_ci	grp->rcvd_xid_th = (struct th_header *)grp->rcvd_xid_skb->data;
13198c2ecf20Sopenharmony_ci	skb_put_data(grp->rcvd_xid_skb, &thnorm, TH_HEADER_LENGTH);
13208c2ecf20Sopenharmony_ci	grp->saved_xid2 = NULL;
13218c2ecf20Sopenharmony_ci	priv->xid = grp->xid;
13228c2ecf20Sopenharmony_ci	priv->mpcg = grp;
13238c2ecf20Sopenharmony_ci	return grp;
13248c2ecf20Sopenharmony_ci}
13258c2ecf20Sopenharmony_ci
13268c2ecf20Sopenharmony_ci/*
13278c2ecf20Sopenharmony_ci * The MPC Group Station FSM
13288c2ecf20Sopenharmony_ci */
13298c2ecf20Sopenharmony_ci
13308c2ecf20Sopenharmony_ci/*
13318c2ecf20Sopenharmony_ci * MPC Group Station FSM actions
13328c2ecf20Sopenharmony_ci * CTCM_PROTO_MPC only
13338c2ecf20Sopenharmony_ci */
13348c2ecf20Sopenharmony_ci
13358c2ecf20Sopenharmony_ci/**
13368c2ecf20Sopenharmony_ci * NOP action for statemachines
13378c2ecf20Sopenharmony_ci */
13388c2ecf20Sopenharmony_cistatic void mpc_action_nop(fsm_instance *fi, int event, void *arg)
13398c2ecf20Sopenharmony_ci{
13408c2ecf20Sopenharmony_ci}
13418c2ecf20Sopenharmony_ci
13428c2ecf20Sopenharmony_ci/*
13438c2ecf20Sopenharmony_ci * invoked when the device transitions to dev_stopped
13448c2ecf20Sopenharmony_ci * MPC will stop each individual channel if a single XID failure
13458c2ecf20Sopenharmony_ci * occurs, or will intitiate all channels be stopped if a GROUP
13468c2ecf20Sopenharmony_ci * level failure occurs.
13478c2ecf20Sopenharmony_ci */
13488c2ecf20Sopenharmony_cistatic void mpc_action_go_inop(fsm_instance *fi, int event, void *arg)
13498c2ecf20Sopenharmony_ci{
13508c2ecf20Sopenharmony_ci	struct net_device  *dev = arg;
13518c2ecf20Sopenharmony_ci	struct ctcm_priv    *priv;
13528c2ecf20Sopenharmony_ci	struct mpc_group *grp;
13538c2ecf20Sopenharmony_ci	struct channel *wch;
13548c2ecf20Sopenharmony_ci
13558c2ecf20Sopenharmony_ci	CTCM_PR_DEBUG("Enter %s: %s\n",	__func__, dev->name);
13568c2ecf20Sopenharmony_ci
13578c2ecf20Sopenharmony_ci	priv  = dev->ml_priv;
13588c2ecf20Sopenharmony_ci	grp =  priv->mpcg;
13598c2ecf20Sopenharmony_ci	grp->flow_off_called = 0;
13608c2ecf20Sopenharmony_ci	fsm_deltimer(&grp->timer);
13618c2ecf20Sopenharmony_ci	if (grp->channels_terminating)
13628c2ecf20Sopenharmony_ci			return;
13638c2ecf20Sopenharmony_ci
13648c2ecf20Sopenharmony_ci	grp->channels_terminating = 1;
13658c2ecf20Sopenharmony_ci	grp->saved_state = fsm_getstate(grp->fsm);
13668c2ecf20Sopenharmony_ci	fsm_newstate(grp->fsm, MPCG_STATE_INOP);
13678c2ecf20Sopenharmony_ci	if (grp->saved_state > MPCG_STATE_XID7INITF)
13688c2ecf20Sopenharmony_ci		CTCM_DBF_TEXT_(MPC_TRACE, CTC_DBF_NOTICE,
13698c2ecf20Sopenharmony_ci			"%s(%s): MPC GROUP INOPERATIVE",
13708c2ecf20Sopenharmony_ci				CTCM_FUNTAIL, dev->name);
13718c2ecf20Sopenharmony_ci	if ((grp->saved_state != MPCG_STATE_RESET) ||
13728c2ecf20Sopenharmony_ci		/* dealloc_channel has been called */
13738c2ecf20Sopenharmony_ci		(grp->port_persist == 0))
13748c2ecf20Sopenharmony_ci		fsm_deltimer(&priv->restart_timer);
13758c2ecf20Sopenharmony_ci
13768c2ecf20Sopenharmony_ci	wch = priv->channel[CTCM_WRITE];
13778c2ecf20Sopenharmony_ci
13788c2ecf20Sopenharmony_ci	switch (grp->saved_state) {
13798c2ecf20Sopenharmony_ci	case MPCG_STATE_RESET:
13808c2ecf20Sopenharmony_ci	case MPCG_STATE_INOP:
13818c2ecf20Sopenharmony_ci	case MPCG_STATE_XID2INITW:
13828c2ecf20Sopenharmony_ci	case MPCG_STATE_XID0IOWAIT:
13838c2ecf20Sopenharmony_ci	case MPCG_STATE_XID2INITX:
13848c2ecf20Sopenharmony_ci	case MPCG_STATE_XID7INITW:
13858c2ecf20Sopenharmony_ci	case MPCG_STATE_XID7INITX:
13868c2ecf20Sopenharmony_ci	case MPCG_STATE_XID0IOWAIX:
13878c2ecf20Sopenharmony_ci	case MPCG_STATE_XID7INITI:
13888c2ecf20Sopenharmony_ci	case MPCG_STATE_XID7INITZ:
13898c2ecf20Sopenharmony_ci	case MPCG_STATE_XID7INITF:
13908c2ecf20Sopenharmony_ci		break;
13918c2ecf20Sopenharmony_ci	case MPCG_STATE_FLOWC:
13928c2ecf20Sopenharmony_ci	case MPCG_STATE_READY:
13938c2ecf20Sopenharmony_ci	default:
13948c2ecf20Sopenharmony_ci		tasklet_hi_schedule(&wch->ch_disc_tasklet);
13958c2ecf20Sopenharmony_ci	}
13968c2ecf20Sopenharmony_ci
13978c2ecf20Sopenharmony_ci	grp->xid2_tgnum = 0;
13988c2ecf20Sopenharmony_ci	grp->group_max_buflen = 0;  /*min of all received */
13998c2ecf20Sopenharmony_ci	grp->outstanding_xid2 = 0;
14008c2ecf20Sopenharmony_ci	grp->outstanding_xid7 = 0;
14018c2ecf20Sopenharmony_ci	grp->outstanding_xid7_p2 = 0;
14028c2ecf20Sopenharmony_ci	grp->saved_xid2 = NULL;
14038c2ecf20Sopenharmony_ci	grp->xidnogood = 0;
14048c2ecf20Sopenharmony_ci	grp->changed_side = 0;
14058c2ecf20Sopenharmony_ci
14068c2ecf20Sopenharmony_ci	grp->rcvd_xid_skb->data = grp->rcvd_xid_data;
14078c2ecf20Sopenharmony_ci	skb_reset_tail_pointer(grp->rcvd_xid_skb);
14088c2ecf20Sopenharmony_ci	grp->rcvd_xid_skb->len = 0;
14098c2ecf20Sopenharmony_ci	grp->rcvd_xid_th = (struct th_header *)grp->rcvd_xid_skb->data;
14108c2ecf20Sopenharmony_ci	skb_put_data(grp->rcvd_xid_skb, &thnorm, TH_HEADER_LENGTH);
14118c2ecf20Sopenharmony_ci
14128c2ecf20Sopenharmony_ci	if (grp->send_qllc_disc == 1) {
14138c2ecf20Sopenharmony_ci		grp->send_qllc_disc = 0;
14148c2ecf20Sopenharmony_ci		mpc_send_qllc_discontact(dev);
14158c2ecf20Sopenharmony_ci	}
14168c2ecf20Sopenharmony_ci
14178c2ecf20Sopenharmony_ci	/* DO NOT issue DEV_EVENT_STOP directly out of this code */
14188c2ecf20Sopenharmony_ci	/* This can result in INOP of VTAM PU due to halting of  */
14198c2ecf20Sopenharmony_ci	/* outstanding IO which causes a sense to be returned	 */
14208c2ecf20Sopenharmony_ci	/* Only about 3 senses are allowed and then IOS/VTAM will*/
14218c2ecf20Sopenharmony_ci	/* become unreachable without manual intervention	 */
14228c2ecf20Sopenharmony_ci	if ((grp->port_persist == 1) || (grp->alloc_called)) {
14238c2ecf20Sopenharmony_ci		grp->alloc_called = 0;
14248c2ecf20Sopenharmony_ci		fsm_deltimer(&priv->restart_timer);
14258c2ecf20Sopenharmony_ci		fsm_addtimer(&priv->restart_timer, 500, DEV_EVENT_RESTART, dev);
14268c2ecf20Sopenharmony_ci		fsm_newstate(grp->fsm, MPCG_STATE_RESET);
14278c2ecf20Sopenharmony_ci		if (grp->saved_state > MPCG_STATE_XID7INITF)
14288c2ecf20Sopenharmony_ci			CTCM_DBF_TEXT_(MPC_TRACE, CTC_DBF_ALWAYS,
14298c2ecf20Sopenharmony_ci				"%s(%s): MPC GROUP RECOVERY SCHEDULED",
14308c2ecf20Sopenharmony_ci					CTCM_FUNTAIL, dev->name);
14318c2ecf20Sopenharmony_ci	} else {
14328c2ecf20Sopenharmony_ci		fsm_deltimer(&priv->restart_timer);
14338c2ecf20Sopenharmony_ci		fsm_addtimer(&priv->restart_timer, 500, DEV_EVENT_STOP, dev);
14348c2ecf20Sopenharmony_ci		fsm_newstate(grp->fsm, MPCG_STATE_RESET);
14358c2ecf20Sopenharmony_ci		CTCM_DBF_TEXT_(MPC_TRACE, CTC_DBF_ALWAYS,
14368c2ecf20Sopenharmony_ci			"%s(%s): NO MPC GROUP RECOVERY ATTEMPTED",
14378c2ecf20Sopenharmony_ci						CTCM_FUNTAIL, dev->name);
14388c2ecf20Sopenharmony_ci	}
14398c2ecf20Sopenharmony_ci}
14408c2ecf20Sopenharmony_ci
14418c2ecf20Sopenharmony_ci/**
14428c2ecf20Sopenharmony_ci * Handle mpc group  action timeout.
14438c2ecf20Sopenharmony_ci * MPC Group Station FSM action
14448c2ecf20Sopenharmony_ci * CTCM_PROTO_MPC only
14458c2ecf20Sopenharmony_ci *
14468c2ecf20Sopenharmony_ci * fi		An instance of an mpc_group fsm.
14478c2ecf20Sopenharmony_ci * event	The event, just happened.
14488c2ecf20Sopenharmony_ci * arg		Generic pointer, casted from net_device * upon call.
14498c2ecf20Sopenharmony_ci */
14508c2ecf20Sopenharmony_cistatic void mpc_action_timeout(fsm_instance *fi, int event, void *arg)
14518c2ecf20Sopenharmony_ci{
14528c2ecf20Sopenharmony_ci	struct net_device *dev = arg;
14538c2ecf20Sopenharmony_ci	struct ctcm_priv *priv;
14548c2ecf20Sopenharmony_ci	struct mpc_group *grp;
14558c2ecf20Sopenharmony_ci	struct channel *wch;
14568c2ecf20Sopenharmony_ci	struct channel *rch;
14578c2ecf20Sopenharmony_ci
14588c2ecf20Sopenharmony_ci	priv = dev->ml_priv;
14598c2ecf20Sopenharmony_ci	grp = priv->mpcg;
14608c2ecf20Sopenharmony_ci	wch = priv->channel[CTCM_WRITE];
14618c2ecf20Sopenharmony_ci	rch = priv->channel[CTCM_READ];
14628c2ecf20Sopenharmony_ci
14638c2ecf20Sopenharmony_ci	switch (fsm_getstate(grp->fsm)) {
14648c2ecf20Sopenharmony_ci	case MPCG_STATE_XID2INITW:
14658c2ecf20Sopenharmony_ci		/* Unless there is outstanding IO on the  */
14668c2ecf20Sopenharmony_ci		/* channel just return and wait for ATTN  */
14678c2ecf20Sopenharmony_ci		/* interrupt to begin XID negotiations	  */
14688c2ecf20Sopenharmony_ci		if ((fsm_getstate(rch->fsm) == CH_XID0_PENDING) &&
14698c2ecf20Sopenharmony_ci		   (fsm_getstate(wch->fsm) == CH_XID0_PENDING))
14708c2ecf20Sopenharmony_ci			break;
14718c2ecf20Sopenharmony_ci		fallthrough;
14728c2ecf20Sopenharmony_ci	default:
14738c2ecf20Sopenharmony_ci		fsm_event(grp->fsm, MPCG_EVENT_INOP, dev);
14748c2ecf20Sopenharmony_ci	}
14758c2ecf20Sopenharmony_ci
14768c2ecf20Sopenharmony_ci	CTCM_DBF_TEXT_(MPC_TRACE, CTC_DBF_DEBUG,
14778c2ecf20Sopenharmony_ci			"%s: dev=%s exit",
14788c2ecf20Sopenharmony_ci			CTCM_FUNTAIL, dev->name);
14798c2ecf20Sopenharmony_ci	return;
14808c2ecf20Sopenharmony_ci}
14818c2ecf20Sopenharmony_ci
14828c2ecf20Sopenharmony_ci/*
14838c2ecf20Sopenharmony_ci * MPC Group Station FSM action
14848c2ecf20Sopenharmony_ci * CTCM_PROTO_MPC only
14858c2ecf20Sopenharmony_ci */
14868c2ecf20Sopenharmony_civoid mpc_action_discontact(fsm_instance *fi, int event, void *arg)
14878c2ecf20Sopenharmony_ci{
14888c2ecf20Sopenharmony_ci	struct mpcg_info   *mpcginfo   = arg;
14898c2ecf20Sopenharmony_ci	struct channel	   *ch	       = mpcginfo->ch;
14908c2ecf20Sopenharmony_ci	struct net_device  *dev;
14918c2ecf20Sopenharmony_ci	struct ctcm_priv   *priv;
14928c2ecf20Sopenharmony_ci	struct mpc_group   *grp;
14938c2ecf20Sopenharmony_ci
14948c2ecf20Sopenharmony_ci	if (ch) {
14958c2ecf20Sopenharmony_ci		dev = ch->netdev;
14968c2ecf20Sopenharmony_ci		if (dev) {
14978c2ecf20Sopenharmony_ci			priv = dev->ml_priv;
14988c2ecf20Sopenharmony_ci			if (priv) {
14998c2ecf20Sopenharmony_ci				CTCM_DBF_TEXT_(MPC_TRACE, CTC_DBF_NOTICE,
15008c2ecf20Sopenharmony_ci					"%s: %s: %s\n",
15018c2ecf20Sopenharmony_ci					CTCM_FUNTAIL, dev->name, ch->id);
15028c2ecf20Sopenharmony_ci				grp = priv->mpcg;
15038c2ecf20Sopenharmony_ci				grp->send_qllc_disc = 1;
15048c2ecf20Sopenharmony_ci				fsm_event(grp->fsm, MPCG_EVENT_INOP, dev);
15058c2ecf20Sopenharmony_ci			}
15068c2ecf20Sopenharmony_ci		}
15078c2ecf20Sopenharmony_ci	}
15088c2ecf20Sopenharmony_ci
15098c2ecf20Sopenharmony_ci	return;
15108c2ecf20Sopenharmony_ci}
15118c2ecf20Sopenharmony_ci
15128c2ecf20Sopenharmony_ci/*
15138c2ecf20Sopenharmony_ci * MPC Group Station - not part of FSM
15148c2ecf20Sopenharmony_ci * CTCM_PROTO_MPC only
15158c2ecf20Sopenharmony_ci * called from add_channel in ctcm_main.c
15168c2ecf20Sopenharmony_ci */
15178c2ecf20Sopenharmony_civoid mpc_action_send_discontact(unsigned long thischan)
15188c2ecf20Sopenharmony_ci{
15198c2ecf20Sopenharmony_ci	int rc;
15208c2ecf20Sopenharmony_ci	struct channel	*ch = (struct channel *)thischan;
15218c2ecf20Sopenharmony_ci	unsigned long	saveflags = 0;
15228c2ecf20Sopenharmony_ci
15238c2ecf20Sopenharmony_ci	spin_lock_irqsave(get_ccwdev_lock(ch->cdev), saveflags);
15248c2ecf20Sopenharmony_ci	rc = ccw_device_start(ch->cdev, &ch->ccw[15], 0, 0xff, 0);
15258c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(get_ccwdev_lock(ch->cdev), saveflags);
15268c2ecf20Sopenharmony_ci
15278c2ecf20Sopenharmony_ci	if (rc != 0) {
15288c2ecf20Sopenharmony_ci		ctcm_ccw_check_rc(ch, rc, (char *)__func__);
15298c2ecf20Sopenharmony_ci	}
15308c2ecf20Sopenharmony_ci
15318c2ecf20Sopenharmony_ci	return;
15328c2ecf20Sopenharmony_ci}
15338c2ecf20Sopenharmony_ci
15348c2ecf20Sopenharmony_ci
15358c2ecf20Sopenharmony_ci/*
15368c2ecf20Sopenharmony_ci * helper function of mpc FSM
15378c2ecf20Sopenharmony_ci * CTCM_PROTO_MPC only
15388c2ecf20Sopenharmony_ci * mpc_action_rcvd_xid7
15398c2ecf20Sopenharmony_ci*/
15408c2ecf20Sopenharmony_cistatic int mpc_validate_xid(struct mpcg_info *mpcginfo)
15418c2ecf20Sopenharmony_ci{
15428c2ecf20Sopenharmony_ci	struct channel	   *ch	 = mpcginfo->ch;
15438c2ecf20Sopenharmony_ci	struct net_device  *dev  = ch->netdev;
15448c2ecf20Sopenharmony_ci	struct ctcm_priv   *priv = dev->ml_priv;
15458c2ecf20Sopenharmony_ci	struct mpc_group   *grp  = priv->mpcg;
15468c2ecf20Sopenharmony_ci	struct xid2	   *xid  = mpcginfo->xid;
15478c2ecf20Sopenharmony_ci	int	rc	 = 0;
15488c2ecf20Sopenharmony_ci	__u64	our_id   = 0;
15498c2ecf20Sopenharmony_ci	__u64   their_id = 0;
15508c2ecf20Sopenharmony_ci	int	len = TH_HEADER_LENGTH + PDU_HEADER_LENGTH;
15518c2ecf20Sopenharmony_ci
15528c2ecf20Sopenharmony_ci	CTCM_PR_DEBUG("Enter %s: xid=%p\n", __func__, xid);
15538c2ecf20Sopenharmony_ci
15548c2ecf20Sopenharmony_ci	if (xid == NULL) {
15558c2ecf20Sopenharmony_ci		rc = 1;
15568c2ecf20Sopenharmony_ci		/* XID REJECTED: xid == NULL */
15578c2ecf20Sopenharmony_ci		CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
15588c2ecf20Sopenharmony_ci			"%s(%s): xid = NULL",
15598c2ecf20Sopenharmony_ci				CTCM_FUNTAIL, ch->id);
15608c2ecf20Sopenharmony_ci			goto done;
15618c2ecf20Sopenharmony_ci	}
15628c2ecf20Sopenharmony_ci
15638c2ecf20Sopenharmony_ci	CTCM_D3_DUMP((char *)xid, XID2_LENGTH);
15648c2ecf20Sopenharmony_ci
15658c2ecf20Sopenharmony_ci	/*the received direction should be the opposite of ours  */
15668c2ecf20Sopenharmony_ci	if (((CHANNEL_DIRECTION(ch->flags) == CTCM_READ) ? XID2_WRITE_SIDE :
15678c2ecf20Sopenharmony_ci				XID2_READ_SIDE) != xid->xid2_dlc_type) {
15688c2ecf20Sopenharmony_ci		rc = 2;
15698c2ecf20Sopenharmony_ci		/* XID REJECTED: r/w channel pairing mismatch */
15708c2ecf20Sopenharmony_ci		CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
15718c2ecf20Sopenharmony_ci			"%s(%s): r/w channel pairing mismatch",
15728c2ecf20Sopenharmony_ci				CTCM_FUNTAIL, ch->id);
15738c2ecf20Sopenharmony_ci			goto done;
15748c2ecf20Sopenharmony_ci	}
15758c2ecf20Sopenharmony_ci
15768c2ecf20Sopenharmony_ci	if (xid->xid2_dlc_type == XID2_READ_SIDE) {
15778c2ecf20Sopenharmony_ci		CTCM_PR_DEBUG("%s: grpmaxbuf:%d xid2buflen:%d\n", __func__,
15788c2ecf20Sopenharmony_ci				grp->group_max_buflen, xid->xid2_buf_len);
15798c2ecf20Sopenharmony_ci
15808c2ecf20Sopenharmony_ci		if (grp->group_max_buflen == 0 || grp->group_max_buflen >
15818c2ecf20Sopenharmony_ci						xid->xid2_buf_len - len)
15828c2ecf20Sopenharmony_ci			grp->group_max_buflen = xid->xid2_buf_len - len;
15838c2ecf20Sopenharmony_ci	}
15848c2ecf20Sopenharmony_ci
15858c2ecf20Sopenharmony_ci	if (grp->saved_xid2 == NULL) {
15868c2ecf20Sopenharmony_ci		grp->saved_xid2 =
15878c2ecf20Sopenharmony_ci			(struct xid2 *)skb_tail_pointer(grp->rcvd_xid_skb);
15888c2ecf20Sopenharmony_ci
15898c2ecf20Sopenharmony_ci		skb_put_data(grp->rcvd_xid_skb, xid, XID2_LENGTH);
15908c2ecf20Sopenharmony_ci		grp->rcvd_xid_skb->data = grp->rcvd_xid_data;
15918c2ecf20Sopenharmony_ci
15928c2ecf20Sopenharmony_ci		skb_reset_tail_pointer(grp->rcvd_xid_skb);
15938c2ecf20Sopenharmony_ci		grp->rcvd_xid_skb->len = 0;
15948c2ecf20Sopenharmony_ci
15958c2ecf20Sopenharmony_ci		/* convert two 32 bit numbers into 1 64 bit for id compare */
15968c2ecf20Sopenharmony_ci		our_id = (__u64)priv->xid->xid2_adj_id;
15978c2ecf20Sopenharmony_ci		our_id = our_id << 32;
15988c2ecf20Sopenharmony_ci		our_id = our_id + priv->xid->xid2_sender_id;
15998c2ecf20Sopenharmony_ci		their_id = (__u64)xid->xid2_adj_id;
16008c2ecf20Sopenharmony_ci		their_id = their_id << 32;
16018c2ecf20Sopenharmony_ci		their_id = their_id + xid->xid2_sender_id;
16028c2ecf20Sopenharmony_ci		/* lower id assume the xside role */
16038c2ecf20Sopenharmony_ci		if (our_id < their_id) {
16048c2ecf20Sopenharmony_ci			grp->roll = XSIDE;
16058c2ecf20Sopenharmony_ci			CTCM_DBF_TEXT_(MPC_TRACE, CTC_DBF_NOTICE,
16068c2ecf20Sopenharmony_ci				"%s(%s): WE HAVE LOW ID - TAKE XSIDE",
16078c2ecf20Sopenharmony_ci					CTCM_FUNTAIL, ch->id);
16088c2ecf20Sopenharmony_ci		} else {
16098c2ecf20Sopenharmony_ci			grp->roll = YSIDE;
16108c2ecf20Sopenharmony_ci			CTCM_DBF_TEXT_(MPC_TRACE, CTC_DBF_NOTICE,
16118c2ecf20Sopenharmony_ci				"%s(%s): WE HAVE HIGH ID - TAKE YSIDE",
16128c2ecf20Sopenharmony_ci					CTCM_FUNTAIL, ch->id);
16138c2ecf20Sopenharmony_ci		}
16148c2ecf20Sopenharmony_ci
16158c2ecf20Sopenharmony_ci	} else {
16168c2ecf20Sopenharmony_ci		if (xid->xid2_flag4 != grp->saved_xid2->xid2_flag4) {
16178c2ecf20Sopenharmony_ci			rc = 3;
16188c2ecf20Sopenharmony_ci			/* XID REJECTED: xid flag byte4 mismatch */
16198c2ecf20Sopenharmony_ci			CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
16208c2ecf20Sopenharmony_ci				"%s(%s): xid flag byte4 mismatch",
16218c2ecf20Sopenharmony_ci					CTCM_FUNTAIL, ch->id);
16228c2ecf20Sopenharmony_ci		}
16238c2ecf20Sopenharmony_ci		if (xid->xid2_flag2 == 0x40) {
16248c2ecf20Sopenharmony_ci			rc = 4;
16258c2ecf20Sopenharmony_ci			/* XID REJECTED - xid NOGOOD */
16268c2ecf20Sopenharmony_ci			CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
16278c2ecf20Sopenharmony_ci				"%s(%s): xid NOGOOD",
16288c2ecf20Sopenharmony_ci					CTCM_FUNTAIL, ch->id);
16298c2ecf20Sopenharmony_ci		}
16308c2ecf20Sopenharmony_ci		if (xid->xid2_adj_id != grp->saved_xid2->xid2_adj_id) {
16318c2ecf20Sopenharmony_ci			rc = 5;
16328c2ecf20Sopenharmony_ci			/* XID REJECTED - Adjacent Station ID Mismatch */
16338c2ecf20Sopenharmony_ci			CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
16348c2ecf20Sopenharmony_ci				"%s(%s): Adjacent Station ID Mismatch",
16358c2ecf20Sopenharmony_ci					CTCM_FUNTAIL, ch->id);
16368c2ecf20Sopenharmony_ci		}
16378c2ecf20Sopenharmony_ci		if (xid->xid2_sender_id != grp->saved_xid2->xid2_sender_id) {
16388c2ecf20Sopenharmony_ci			rc = 6;
16398c2ecf20Sopenharmony_ci			/* XID REJECTED - Sender Address Mismatch */
16408c2ecf20Sopenharmony_ci			CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
16418c2ecf20Sopenharmony_ci				"%s(%s): Sender Address Mismatch",
16428c2ecf20Sopenharmony_ci					CTCM_FUNTAIL, ch->id);
16438c2ecf20Sopenharmony_ci		}
16448c2ecf20Sopenharmony_ci	}
16458c2ecf20Sopenharmony_cidone:
16468c2ecf20Sopenharmony_ci	if (rc) {
16478c2ecf20Sopenharmony_ci		dev_warn(&dev->dev,
16488c2ecf20Sopenharmony_ci			"The XID used in the MPC protocol is not valid, "
16498c2ecf20Sopenharmony_ci			"rc = %d\n", rc);
16508c2ecf20Sopenharmony_ci		priv->xid->xid2_flag2 = 0x40;
16518c2ecf20Sopenharmony_ci		grp->saved_xid2->xid2_flag2 = 0x40;
16528c2ecf20Sopenharmony_ci	}
16538c2ecf20Sopenharmony_ci
16548c2ecf20Sopenharmony_ci	return rc;
16558c2ecf20Sopenharmony_ci}
16568c2ecf20Sopenharmony_ci
16578c2ecf20Sopenharmony_ci/*
16588c2ecf20Sopenharmony_ci * MPC Group Station FSM action
16598c2ecf20Sopenharmony_ci * CTCM_PROTO_MPC only
16608c2ecf20Sopenharmony_ci */
16618c2ecf20Sopenharmony_cistatic void mpc_action_side_xid(fsm_instance *fsm, void *arg, int side)
16628c2ecf20Sopenharmony_ci{
16638c2ecf20Sopenharmony_ci	struct channel *ch = arg;
16648c2ecf20Sopenharmony_ci	int rc = 0;
16658c2ecf20Sopenharmony_ci	int gotlock = 0;
16668c2ecf20Sopenharmony_ci	unsigned long saveflags = 0;	/* avoids compiler warning with
16678c2ecf20Sopenharmony_ci					   spin_unlock_irqrestore */
16688c2ecf20Sopenharmony_ci
16698c2ecf20Sopenharmony_ci	CTCM_PR_DEBUG("Enter %s: cp=%i ch=0x%p id=%s\n",
16708c2ecf20Sopenharmony_ci			__func__, smp_processor_id(), ch, ch->id);
16718c2ecf20Sopenharmony_ci
16728c2ecf20Sopenharmony_ci	if (ctcm_checkalloc_buffer(ch))
16738c2ecf20Sopenharmony_ci					goto done;
16748c2ecf20Sopenharmony_ci
16758c2ecf20Sopenharmony_ci	/*
16768c2ecf20Sopenharmony_ci	 * skb data-buffer referencing:
16778c2ecf20Sopenharmony_ci	 */
16788c2ecf20Sopenharmony_ci	ch->trans_skb->data = ch->trans_skb_data;
16798c2ecf20Sopenharmony_ci	skb_reset_tail_pointer(ch->trans_skb);
16808c2ecf20Sopenharmony_ci	ch->trans_skb->len = 0;
16818c2ecf20Sopenharmony_ci	/* result of the previous 3 statements is NOT always
16828c2ecf20Sopenharmony_ci	 * already set after ctcm_checkalloc_buffer
16838c2ecf20Sopenharmony_ci	 * because of possible reuse of the trans_skb
16848c2ecf20Sopenharmony_ci	 */
16858c2ecf20Sopenharmony_ci	memset(ch->trans_skb->data, 0, 16);
16868c2ecf20Sopenharmony_ci	ch->rcvd_xid_th =  (struct th_header *)ch->trans_skb_data;
16878c2ecf20Sopenharmony_ci	/* check is main purpose here: */
16888c2ecf20Sopenharmony_ci	skb_put(ch->trans_skb, TH_HEADER_LENGTH);
16898c2ecf20Sopenharmony_ci	ch->rcvd_xid = (struct xid2 *)skb_tail_pointer(ch->trans_skb);
16908c2ecf20Sopenharmony_ci	/* check is main purpose here: */
16918c2ecf20Sopenharmony_ci	skb_put(ch->trans_skb, XID2_LENGTH);
16928c2ecf20Sopenharmony_ci	ch->rcvd_xid_id = skb_tail_pointer(ch->trans_skb);
16938c2ecf20Sopenharmony_ci	/* cleanup back to startpoint */
16948c2ecf20Sopenharmony_ci	ch->trans_skb->data = ch->trans_skb_data;
16958c2ecf20Sopenharmony_ci	skb_reset_tail_pointer(ch->trans_skb);
16968c2ecf20Sopenharmony_ci	ch->trans_skb->len = 0;
16978c2ecf20Sopenharmony_ci
16988c2ecf20Sopenharmony_ci	/* non-checking rewrite of above skb data-buffer referencing: */
16998c2ecf20Sopenharmony_ci	/*
17008c2ecf20Sopenharmony_ci	memset(ch->trans_skb->data, 0, 16);
17018c2ecf20Sopenharmony_ci	ch->rcvd_xid_th =  (struct th_header *)ch->trans_skb_data;
17028c2ecf20Sopenharmony_ci	ch->rcvd_xid = (struct xid2 *)(ch->trans_skb_data + TH_HEADER_LENGTH);
17038c2ecf20Sopenharmony_ci	ch->rcvd_xid_id = ch->trans_skb_data + TH_HEADER_LENGTH + XID2_LENGTH;
17048c2ecf20Sopenharmony_ci	 */
17058c2ecf20Sopenharmony_ci
17068c2ecf20Sopenharmony_ci	ch->ccw[8].flags	= CCW_FLAG_SLI | CCW_FLAG_CC;
17078c2ecf20Sopenharmony_ci	ch->ccw[8].count	= 0;
17088c2ecf20Sopenharmony_ci	ch->ccw[8].cda		= 0x00;
17098c2ecf20Sopenharmony_ci
17108c2ecf20Sopenharmony_ci	if (!(ch->xid_th && ch->xid && ch->xid_id))
17118c2ecf20Sopenharmony_ci		CTCM_DBF_TEXT_(MPC_TRACE, CTC_DBF_INFO,
17128c2ecf20Sopenharmony_ci			"%s(%s): xid_th=%p, xid=%p, xid_id=%p",
17138c2ecf20Sopenharmony_ci			CTCM_FUNTAIL, ch->id, ch->xid_th, ch->xid, ch->xid_id);
17148c2ecf20Sopenharmony_ci
17158c2ecf20Sopenharmony_ci	if (side == XSIDE) {
17168c2ecf20Sopenharmony_ci		/* mpc_action_xside_xid */
17178c2ecf20Sopenharmony_ci		if (ch->xid_th == NULL)
17188c2ecf20Sopenharmony_ci				goto done;
17198c2ecf20Sopenharmony_ci		ch->ccw[9].cmd_code	= CCW_CMD_WRITE;
17208c2ecf20Sopenharmony_ci		ch->ccw[9].flags	= CCW_FLAG_SLI | CCW_FLAG_CC;
17218c2ecf20Sopenharmony_ci		ch->ccw[9].count	= TH_HEADER_LENGTH;
17228c2ecf20Sopenharmony_ci		ch->ccw[9].cda		= virt_to_phys(ch->xid_th);
17238c2ecf20Sopenharmony_ci
17248c2ecf20Sopenharmony_ci		if (ch->xid == NULL)
17258c2ecf20Sopenharmony_ci				goto done;
17268c2ecf20Sopenharmony_ci		ch->ccw[10].cmd_code	= CCW_CMD_WRITE;
17278c2ecf20Sopenharmony_ci		ch->ccw[10].flags	= CCW_FLAG_SLI | CCW_FLAG_CC;
17288c2ecf20Sopenharmony_ci		ch->ccw[10].count	= XID2_LENGTH;
17298c2ecf20Sopenharmony_ci		ch->ccw[10].cda		= virt_to_phys(ch->xid);
17308c2ecf20Sopenharmony_ci
17318c2ecf20Sopenharmony_ci		ch->ccw[11].cmd_code	= CCW_CMD_READ;
17328c2ecf20Sopenharmony_ci		ch->ccw[11].flags	= CCW_FLAG_SLI | CCW_FLAG_CC;
17338c2ecf20Sopenharmony_ci		ch->ccw[11].count	= TH_HEADER_LENGTH;
17348c2ecf20Sopenharmony_ci		ch->ccw[11].cda		= virt_to_phys(ch->rcvd_xid_th);
17358c2ecf20Sopenharmony_ci
17368c2ecf20Sopenharmony_ci		ch->ccw[12].cmd_code	= CCW_CMD_READ;
17378c2ecf20Sopenharmony_ci		ch->ccw[12].flags	= CCW_FLAG_SLI | CCW_FLAG_CC;
17388c2ecf20Sopenharmony_ci		ch->ccw[12].count	= XID2_LENGTH;
17398c2ecf20Sopenharmony_ci		ch->ccw[12].cda		= virt_to_phys(ch->rcvd_xid);
17408c2ecf20Sopenharmony_ci
17418c2ecf20Sopenharmony_ci		ch->ccw[13].cmd_code	= CCW_CMD_READ;
17428c2ecf20Sopenharmony_ci		ch->ccw[13].cda		= virt_to_phys(ch->rcvd_xid_id);
17438c2ecf20Sopenharmony_ci
17448c2ecf20Sopenharmony_ci	} else { /* side == YSIDE : mpc_action_yside_xid */
17458c2ecf20Sopenharmony_ci		ch->ccw[9].cmd_code	= CCW_CMD_READ;
17468c2ecf20Sopenharmony_ci		ch->ccw[9].flags	= CCW_FLAG_SLI | CCW_FLAG_CC;
17478c2ecf20Sopenharmony_ci		ch->ccw[9].count	= TH_HEADER_LENGTH;
17488c2ecf20Sopenharmony_ci		ch->ccw[9].cda		= virt_to_phys(ch->rcvd_xid_th);
17498c2ecf20Sopenharmony_ci
17508c2ecf20Sopenharmony_ci		ch->ccw[10].cmd_code	= CCW_CMD_READ;
17518c2ecf20Sopenharmony_ci		ch->ccw[10].flags	= CCW_FLAG_SLI | CCW_FLAG_CC;
17528c2ecf20Sopenharmony_ci		ch->ccw[10].count	= XID2_LENGTH;
17538c2ecf20Sopenharmony_ci		ch->ccw[10].cda		= virt_to_phys(ch->rcvd_xid);
17548c2ecf20Sopenharmony_ci
17558c2ecf20Sopenharmony_ci		if (ch->xid_th == NULL)
17568c2ecf20Sopenharmony_ci				goto done;
17578c2ecf20Sopenharmony_ci		ch->ccw[11].cmd_code	= CCW_CMD_WRITE;
17588c2ecf20Sopenharmony_ci		ch->ccw[11].flags	= CCW_FLAG_SLI | CCW_FLAG_CC;
17598c2ecf20Sopenharmony_ci		ch->ccw[11].count	= TH_HEADER_LENGTH;
17608c2ecf20Sopenharmony_ci		ch->ccw[11].cda		= virt_to_phys(ch->xid_th);
17618c2ecf20Sopenharmony_ci
17628c2ecf20Sopenharmony_ci		if (ch->xid == NULL)
17638c2ecf20Sopenharmony_ci				goto done;
17648c2ecf20Sopenharmony_ci		ch->ccw[12].cmd_code	= CCW_CMD_WRITE;
17658c2ecf20Sopenharmony_ci		ch->ccw[12].flags	= CCW_FLAG_SLI | CCW_FLAG_CC;
17668c2ecf20Sopenharmony_ci		ch->ccw[12].count	= XID2_LENGTH;
17678c2ecf20Sopenharmony_ci		ch->ccw[12].cda		= virt_to_phys(ch->xid);
17688c2ecf20Sopenharmony_ci
17698c2ecf20Sopenharmony_ci		if (ch->xid_id == NULL)
17708c2ecf20Sopenharmony_ci				goto done;
17718c2ecf20Sopenharmony_ci		ch->ccw[13].cmd_code	= CCW_CMD_WRITE;
17728c2ecf20Sopenharmony_ci		ch->ccw[13].cda		= virt_to_phys(ch->xid_id);
17738c2ecf20Sopenharmony_ci
17748c2ecf20Sopenharmony_ci	}
17758c2ecf20Sopenharmony_ci	ch->ccw[13].flags	= CCW_FLAG_SLI | CCW_FLAG_CC;
17768c2ecf20Sopenharmony_ci	ch->ccw[13].count	= 4;
17778c2ecf20Sopenharmony_ci
17788c2ecf20Sopenharmony_ci	ch->ccw[14].cmd_code	= CCW_CMD_NOOP;
17798c2ecf20Sopenharmony_ci	ch->ccw[14].flags	= CCW_FLAG_SLI;
17808c2ecf20Sopenharmony_ci	ch->ccw[14].count	= 0;
17818c2ecf20Sopenharmony_ci	ch->ccw[14].cda		= 0;
17828c2ecf20Sopenharmony_ci
17838c2ecf20Sopenharmony_ci	CTCM_CCW_DUMP((char *)&ch->ccw[8], sizeof(struct ccw1) * 7);
17848c2ecf20Sopenharmony_ci	CTCM_D3_DUMP((char *)ch->xid_th, TH_HEADER_LENGTH);
17858c2ecf20Sopenharmony_ci	CTCM_D3_DUMP((char *)ch->xid, XID2_LENGTH);
17868c2ecf20Sopenharmony_ci	CTCM_D3_DUMP((char *)ch->xid_id, 4);
17878c2ecf20Sopenharmony_ci
17888c2ecf20Sopenharmony_ci	if (!in_irq()) {
17898c2ecf20Sopenharmony_ci			 /* Such conditional locking is a known problem for
17908c2ecf20Sopenharmony_ci			  * sparse because its static undeterministic.
17918c2ecf20Sopenharmony_ci			  * Warnings should be ignored here. */
17928c2ecf20Sopenharmony_ci		spin_lock_irqsave(get_ccwdev_lock(ch->cdev), saveflags);
17938c2ecf20Sopenharmony_ci		gotlock = 1;
17948c2ecf20Sopenharmony_ci	}
17958c2ecf20Sopenharmony_ci
17968c2ecf20Sopenharmony_ci	fsm_addtimer(&ch->timer, 5000 , CTC_EVENT_TIMER, ch);
17978c2ecf20Sopenharmony_ci	rc = ccw_device_start(ch->cdev, &ch->ccw[8], 0, 0xff, 0);
17988c2ecf20Sopenharmony_ci
17998c2ecf20Sopenharmony_ci	if (gotlock)	/* see remark above about conditional locking */
18008c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(get_ccwdev_lock(ch->cdev), saveflags);
18018c2ecf20Sopenharmony_ci
18028c2ecf20Sopenharmony_ci	if (rc != 0) {
18038c2ecf20Sopenharmony_ci		ctcm_ccw_check_rc(ch, rc,
18048c2ecf20Sopenharmony_ci				(side == XSIDE) ? "x-side XID" : "y-side XID");
18058c2ecf20Sopenharmony_ci	}
18068c2ecf20Sopenharmony_ci
18078c2ecf20Sopenharmony_cidone:
18088c2ecf20Sopenharmony_ci	CTCM_PR_DEBUG("Exit %s: ch=0x%p id=%s\n",
18098c2ecf20Sopenharmony_ci				__func__, ch, ch->id);
18108c2ecf20Sopenharmony_ci	return;
18118c2ecf20Sopenharmony_ci
18128c2ecf20Sopenharmony_ci}
18138c2ecf20Sopenharmony_ci
18148c2ecf20Sopenharmony_ci/*
18158c2ecf20Sopenharmony_ci * MPC Group Station FSM action
18168c2ecf20Sopenharmony_ci * CTCM_PROTO_MPC only
18178c2ecf20Sopenharmony_ci */
18188c2ecf20Sopenharmony_cistatic void mpc_action_xside_xid(fsm_instance *fsm, int event, void *arg)
18198c2ecf20Sopenharmony_ci{
18208c2ecf20Sopenharmony_ci	mpc_action_side_xid(fsm, arg, XSIDE);
18218c2ecf20Sopenharmony_ci}
18228c2ecf20Sopenharmony_ci
18238c2ecf20Sopenharmony_ci/*
18248c2ecf20Sopenharmony_ci * MPC Group Station FSM action
18258c2ecf20Sopenharmony_ci * CTCM_PROTO_MPC only
18268c2ecf20Sopenharmony_ci */
18278c2ecf20Sopenharmony_cistatic void mpc_action_yside_xid(fsm_instance *fsm, int event, void *arg)
18288c2ecf20Sopenharmony_ci{
18298c2ecf20Sopenharmony_ci	mpc_action_side_xid(fsm, arg, YSIDE);
18308c2ecf20Sopenharmony_ci}
18318c2ecf20Sopenharmony_ci
18328c2ecf20Sopenharmony_ci/*
18338c2ecf20Sopenharmony_ci * MPC Group Station FSM action
18348c2ecf20Sopenharmony_ci * CTCM_PROTO_MPC only
18358c2ecf20Sopenharmony_ci */
18368c2ecf20Sopenharmony_cistatic void mpc_action_doxid0(fsm_instance *fsm, int event, void *arg)
18378c2ecf20Sopenharmony_ci{
18388c2ecf20Sopenharmony_ci	struct channel	   *ch   = arg;
18398c2ecf20Sopenharmony_ci	struct net_device  *dev  = ch->netdev;
18408c2ecf20Sopenharmony_ci	struct ctcm_priv   *priv = dev->ml_priv;
18418c2ecf20Sopenharmony_ci	struct mpc_group   *grp  = priv->mpcg;
18428c2ecf20Sopenharmony_ci
18438c2ecf20Sopenharmony_ci	CTCM_PR_DEBUG("Enter %s: cp=%i ch=0x%p id=%s\n",
18448c2ecf20Sopenharmony_ci			__func__, smp_processor_id(), ch, ch->id);
18458c2ecf20Sopenharmony_ci
18468c2ecf20Sopenharmony_ci	if (ch->xid == NULL) {
18478c2ecf20Sopenharmony_ci		CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
18488c2ecf20Sopenharmony_ci			"%s(%s): ch->xid == NULL",
18498c2ecf20Sopenharmony_ci				CTCM_FUNTAIL, dev->name);
18508c2ecf20Sopenharmony_ci		return;
18518c2ecf20Sopenharmony_ci	}
18528c2ecf20Sopenharmony_ci
18538c2ecf20Sopenharmony_ci	fsm_newstate(ch->fsm, CH_XID0_INPROGRESS);
18548c2ecf20Sopenharmony_ci
18558c2ecf20Sopenharmony_ci	ch->xid->xid2_option =	XID2_0;
18568c2ecf20Sopenharmony_ci
18578c2ecf20Sopenharmony_ci	switch (fsm_getstate(grp->fsm)) {
18588c2ecf20Sopenharmony_ci	case MPCG_STATE_XID2INITW:
18598c2ecf20Sopenharmony_ci	case MPCG_STATE_XID2INITX:
18608c2ecf20Sopenharmony_ci		ch->ccw[8].cmd_code = CCW_CMD_SENSE_CMD;
18618c2ecf20Sopenharmony_ci		break;
18628c2ecf20Sopenharmony_ci	case MPCG_STATE_XID0IOWAIT:
18638c2ecf20Sopenharmony_ci	case MPCG_STATE_XID0IOWAIX:
18648c2ecf20Sopenharmony_ci		ch->ccw[8].cmd_code = CCW_CMD_WRITE_CTL;
18658c2ecf20Sopenharmony_ci		break;
18668c2ecf20Sopenharmony_ci	}
18678c2ecf20Sopenharmony_ci
18688c2ecf20Sopenharmony_ci	fsm_event(grp->fsm, MPCG_EVENT_DOIO, ch);
18698c2ecf20Sopenharmony_ci
18708c2ecf20Sopenharmony_ci	return;
18718c2ecf20Sopenharmony_ci}
18728c2ecf20Sopenharmony_ci
18738c2ecf20Sopenharmony_ci/*
18748c2ecf20Sopenharmony_ci * MPC Group Station FSM action
18758c2ecf20Sopenharmony_ci * CTCM_PROTO_MPC only
18768c2ecf20Sopenharmony_ci*/
18778c2ecf20Sopenharmony_cistatic void mpc_action_doxid7(fsm_instance *fsm, int event, void *arg)
18788c2ecf20Sopenharmony_ci{
18798c2ecf20Sopenharmony_ci	struct net_device *dev = arg;
18808c2ecf20Sopenharmony_ci	struct ctcm_priv  *priv = dev->ml_priv;
18818c2ecf20Sopenharmony_ci	struct mpc_group  *grp  = NULL;
18828c2ecf20Sopenharmony_ci	int direction;
18838c2ecf20Sopenharmony_ci	int send = 0;
18848c2ecf20Sopenharmony_ci
18858c2ecf20Sopenharmony_ci	if (priv)
18868c2ecf20Sopenharmony_ci		grp = priv->mpcg;
18878c2ecf20Sopenharmony_ci	if (grp == NULL)
18888c2ecf20Sopenharmony_ci		return;
18898c2ecf20Sopenharmony_ci
18908c2ecf20Sopenharmony_ci	for (direction = CTCM_READ; direction <= CTCM_WRITE; direction++) {
18918c2ecf20Sopenharmony_ci		struct channel *ch = priv->channel[direction];
18928c2ecf20Sopenharmony_ci		struct xid2 *thisxid = ch->xid;
18938c2ecf20Sopenharmony_ci		ch->xid_skb->data = ch->xid_skb_data;
18948c2ecf20Sopenharmony_ci		skb_reset_tail_pointer(ch->xid_skb);
18958c2ecf20Sopenharmony_ci		ch->xid_skb->len = 0;
18968c2ecf20Sopenharmony_ci		thisxid->xid2_option = XID2_7;
18978c2ecf20Sopenharmony_ci		send = 0;
18988c2ecf20Sopenharmony_ci
18998c2ecf20Sopenharmony_ci		/* xid7 phase 1 */
19008c2ecf20Sopenharmony_ci		if (grp->outstanding_xid7_p2 > 0) {
19018c2ecf20Sopenharmony_ci			if (grp->roll == YSIDE) {
19028c2ecf20Sopenharmony_ci				if (fsm_getstate(ch->fsm) == CH_XID7_PENDING1) {
19038c2ecf20Sopenharmony_ci					fsm_newstate(ch->fsm, CH_XID7_PENDING2);
19048c2ecf20Sopenharmony_ci					ch->ccw[8].cmd_code = CCW_CMD_SENSE_CMD;
19058c2ecf20Sopenharmony_ci					skb_put_data(ch->xid_skb, &thdummy,
19068c2ecf20Sopenharmony_ci						     TH_HEADER_LENGTH);
19078c2ecf20Sopenharmony_ci					send = 1;
19088c2ecf20Sopenharmony_ci				}
19098c2ecf20Sopenharmony_ci			} else if (fsm_getstate(ch->fsm) < CH_XID7_PENDING2) {
19108c2ecf20Sopenharmony_ci					fsm_newstate(ch->fsm, CH_XID7_PENDING2);
19118c2ecf20Sopenharmony_ci					ch->ccw[8].cmd_code = CCW_CMD_WRITE_CTL;
19128c2ecf20Sopenharmony_ci					skb_put_data(ch->xid_skb, &thnorm,
19138c2ecf20Sopenharmony_ci						     TH_HEADER_LENGTH);
19148c2ecf20Sopenharmony_ci					send = 1;
19158c2ecf20Sopenharmony_ci			}
19168c2ecf20Sopenharmony_ci		} else {
19178c2ecf20Sopenharmony_ci			/* xid7 phase 2 */
19188c2ecf20Sopenharmony_ci			if (grp->roll == YSIDE) {
19198c2ecf20Sopenharmony_ci				if (fsm_getstate(ch->fsm) < CH_XID7_PENDING4) {
19208c2ecf20Sopenharmony_ci					fsm_newstate(ch->fsm, CH_XID7_PENDING4);
19218c2ecf20Sopenharmony_ci					skb_put_data(ch->xid_skb, &thnorm,
19228c2ecf20Sopenharmony_ci						     TH_HEADER_LENGTH);
19238c2ecf20Sopenharmony_ci					ch->ccw[8].cmd_code = CCW_CMD_WRITE_CTL;
19248c2ecf20Sopenharmony_ci					send = 1;
19258c2ecf20Sopenharmony_ci				}
19268c2ecf20Sopenharmony_ci			} else if (fsm_getstate(ch->fsm) == CH_XID7_PENDING3) {
19278c2ecf20Sopenharmony_ci				fsm_newstate(ch->fsm, CH_XID7_PENDING4);
19288c2ecf20Sopenharmony_ci				ch->ccw[8].cmd_code = CCW_CMD_SENSE_CMD;
19298c2ecf20Sopenharmony_ci				skb_put_data(ch->xid_skb, &thdummy,
19308c2ecf20Sopenharmony_ci					     TH_HEADER_LENGTH);
19318c2ecf20Sopenharmony_ci				send = 1;
19328c2ecf20Sopenharmony_ci			}
19338c2ecf20Sopenharmony_ci		}
19348c2ecf20Sopenharmony_ci
19358c2ecf20Sopenharmony_ci		if (send)
19368c2ecf20Sopenharmony_ci			fsm_event(grp->fsm, MPCG_EVENT_DOIO, ch);
19378c2ecf20Sopenharmony_ci	}
19388c2ecf20Sopenharmony_ci
19398c2ecf20Sopenharmony_ci	return;
19408c2ecf20Sopenharmony_ci}
19418c2ecf20Sopenharmony_ci
19428c2ecf20Sopenharmony_ci/*
19438c2ecf20Sopenharmony_ci * MPC Group Station FSM action
19448c2ecf20Sopenharmony_ci * CTCM_PROTO_MPC only
19458c2ecf20Sopenharmony_ci */
19468c2ecf20Sopenharmony_cistatic void mpc_action_rcvd_xid0(fsm_instance *fsm, int event, void *arg)
19478c2ecf20Sopenharmony_ci{
19488c2ecf20Sopenharmony_ci
19498c2ecf20Sopenharmony_ci	struct mpcg_info   *mpcginfo  = arg;
19508c2ecf20Sopenharmony_ci	struct channel	   *ch   = mpcginfo->ch;
19518c2ecf20Sopenharmony_ci	struct net_device  *dev  = ch->netdev;
19528c2ecf20Sopenharmony_ci	struct ctcm_priv   *priv = dev->ml_priv;
19538c2ecf20Sopenharmony_ci	struct mpc_group   *grp  = priv->mpcg;
19548c2ecf20Sopenharmony_ci
19558c2ecf20Sopenharmony_ci	CTCM_PR_DEBUG("%s: ch-id:%s xid2:%i xid7:%i xidt_p2:%i \n",
19568c2ecf20Sopenharmony_ci			__func__, ch->id, grp->outstanding_xid2,
19578c2ecf20Sopenharmony_ci			grp->outstanding_xid7, grp->outstanding_xid7_p2);
19588c2ecf20Sopenharmony_ci
19598c2ecf20Sopenharmony_ci	if (fsm_getstate(ch->fsm) < CH_XID7_PENDING)
19608c2ecf20Sopenharmony_ci		fsm_newstate(ch->fsm, CH_XID7_PENDING);
19618c2ecf20Sopenharmony_ci
19628c2ecf20Sopenharmony_ci	grp->outstanding_xid2--;
19638c2ecf20Sopenharmony_ci	grp->outstanding_xid7++;
19648c2ecf20Sopenharmony_ci	grp->outstanding_xid7_p2++;
19658c2ecf20Sopenharmony_ci
19668c2ecf20Sopenharmony_ci	/* must change state before validating xid to */
19678c2ecf20Sopenharmony_ci	/* properly handle interim interrupts received*/
19688c2ecf20Sopenharmony_ci	switch (fsm_getstate(grp->fsm)) {
19698c2ecf20Sopenharmony_ci	case MPCG_STATE_XID2INITW:
19708c2ecf20Sopenharmony_ci		fsm_newstate(grp->fsm, MPCG_STATE_XID2INITX);
19718c2ecf20Sopenharmony_ci		mpc_validate_xid(mpcginfo);
19728c2ecf20Sopenharmony_ci		break;
19738c2ecf20Sopenharmony_ci	case MPCG_STATE_XID0IOWAIT:
19748c2ecf20Sopenharmony_ci		fsm_newstate(grp->fsm, MPCG_STATE_XID0IOWAIX);
19758c2ecf20Sopenharmony_ci		mpc_validate_xid(mpcginfo);
19768c2ecf20Sopenharmony_ci		break;
19778c2ecf20Sopenharmony_ci	case MPCG_STATE_XID2INITX:
19788c2ecf20Sopenharmony_ci		if (grp->outstanding_xid2 == 0) {
19798c2ecf20Sopenharmony_ci			fsm_newstate(grp->fsm, MPCG_STATE_XID7INITW);
19808c2ecf20Sopenharmony_ci			mpc_validate_xid(mpcginfo);
19818c2ecf20Sopenharmony_ci			fsm_event(grp->fsm, MPCG_EVENT_XID2DONE, dev);
19828c2ecf20Sopenharmony_ci		}
19838c2ecf20Sopenharmony_ci		break;
19848c2ecf20Sopenharmony_ci	case MPCG_STATE_XID0IOWAIX:
19858c2ecf20Sopenharmony_ci		if (grp->outstanding_xid2 == 0) {
19868c2ecf20Sopenharmony_ci			fsm_newstate(grp->fsm, MPCG_STATE_XID7INITI);
19878c2ecf20Sopenharmony_ci			mpc_validate_xid(mpcginfo);
19888c2ecf20Sopenharmony_ci			fsm_event(grp->fsm, MPCG_EVENT_XID2DONE, dev);
19898c2ecf20Sopenharmony_ci		}
19908c2ecf20Sopenharmony_ci		break;
19918c2ecf20Sopenharmony_ci	}
19928c2ecf20Sopenharmony_ci
19938c2ecf20Sopenharmony_ci	CTCM_PR_DEBUG("ctcmpc:%s() %s xid2:%i xid7:%i xidt_p2:%i \n",
19948c2ecf20Sopenharmony_ci		__func__, ch->id, grp->outstanding_xid2,
19958c2ecf20Sopenharmony_ci		grp->outstanding_xid7, grp->outstanding_xid7_p2);
19968c2ecf20Sopenharmony_ci	CTCM_PR_DEBUG("ctcmpc:%s() %s grpstate: %s chanstate: %s \n",
19978c2ecf20Sopenharmony_ci		__func__, ch->id,
19988c2ecf20Sopenharmony_ci		fsm_getstate_str(grp->fsm), fsm_getstate_str(ch->fsm));
19998c2ecf20Sopenharmony_ci	return;
20008c2ecf20Sopenharmony_ci
20018c2ecf20Sopenharmony_ci}
20028c2ecf20Sopenharmony_ci
20038c2ecf20Sopenharmony_ci
20048c2ecf20Sopenharmony_ci/*
20058c2ecf20Sopenharmony_ci * MPC Group Station FSM action
20068c2ecf20Sopenharmony_ci * CTCM_PROTO_MPC only
20078c2ecf20Sopenharmony_ci */
20088c2ecf20Sopenharmony_cistatic void mpc_action_rcvd_xid7(fsm_instance *fsm, int event, void *arg)
20098c2ecf20Sopenharmony_ci{
20108c2ecf20Sopenharmony_ci	struct mpcg_info   *mpcginfo   = arg;
20118c2ecf20Sopenharmony_ci	struct channel	   *ch	       = mpcginfo->ch;
20128c2ecf20Sopenharmony_ci	struct net_device  *dev        = ch->netdev;
20138c2ecf20Sopenharmony_ci	struct ctcm_priv   *priv    = dev->ml_priv;
20148c2ecf20Sopenharmony_ci	struct mpc_group   *grp     = priv->mpcg;
20158c2ecf20Sopenharmony_ci
20168c2ecf20Sopenharmony_ci	CTCM_PR_DEBUG("Enter %s: cp=%i ch=0x%p id=%s\n",
20178c2ecf20Sopenharmony_ci		__func__, smp_processor_id(), ch, ch->id);
20188c2ecf20Sopenharmony_ci	CTCM_PR_DEBUG("%s: outstanding_xid7: %i, outstanding_xid7_p2: %i\n",
20198c2ecf20Sopenharmony_ci		__func__, grp->outstanding_xid7, grp->outstanding_xid7_p2);
20208c2ecf20Sopenharmony_ci
20218c2ecf20Sopenharmony_ci	grp->outstanding_xid7--;
20228c2ecf20Sopenharmony_ci	ch->xid_skb->data = ch->xid_skb_data;
20238c2ecf20Sopenharmony_ci	skb_reset_tail_pointer(ch->xid_skb);
20248c2ecf20Sopenharmony_ci	ch->xid_skb->len = 0;
20258c2ecf20Sopenharmony_ci
20268c2ecf20Sopenharmony_ci	switch (fsm_getstate(grp->fsm)) {
20278c2ecf20Sopenharmony_ci	case MPCG_STATE_XID7INITI:
20288c2ecf20Sopenharmony_ci		fsm_newstate(grp->fsm, MPCG_STATE_XID7INITZ);
20298c2ecf20Sopenharmony_ci		mpc_validate_xid(mpcginfo);
20308c2ecf20Sopenharmony_ci		break;
20318c2ecf20Sopenharmony_ci	case MPCG_STATE_XID7INITW:
20328c2ecf20Sopenharmony_ci		fsm_newstate(grp->fsm, MPCG_STATE_XID7INITX);
20338c2ecf20Sopenharmony_ci		mpc_validate_xid(mpcginfo);
20348c2ecf20Sopenharmony_ci		break;
20358c2ecf20Sopenharmony_ci	case MPCG_STATE_XID7INITZ:
20368c2ecf20Sopenharmony_ci	case MPCG_STATE_XID7INITX:
20378c2ecf20Sopenharmony_ci		if (grp->outstanding_xid7 == 0) {
20388c2ecf20Sopenharmony_ci			if (grp->outstanding_xid7_p2 > 0) {
20398c2ecf20Sopenharmony_ci				grp->outstanding_xid7 =
20408c2ecf20Sopenharmony_ci					grp->outstanding_xid7_p2;
20418c2ecf20Sopenharmony_ci				grp->outstanding_xid7_p2 = 0;
20428c2ecf20Sopenharmony_ci			} else
20438c2ecf20Sopenharmony_ci				fsm_newstate(grp->fsm, MPCG_STATE_XID7INITF);
20448c2ecf20Sopenharmony_ci
20458c2ecf20Sopenharmony_ci			mpc_validate_xid(mpcginfo);
20468c2ecf20Sopenharmony_ci			fsm_event(grp->fsm, MPCG_EVENT_XID7DONE, dev);
20478c2ecf20Sopenharmony_ci			break;
20488c2ecf20Sopenharmony_ci		}
20498c2ecf20Sopenharmony_ci		mpc_validate_xid(mpcginfo);
20508c2ecf20Sopenharmony_ci		break;
20518c2ecf20Sopenharmony_ci	}
20528c2ecf20Sopenharmony_ci	return;
20538c2ecf20Sopenharmony_ci}
20548c2ecf20Sopenharmony_ci
20558c2ecf20Sopenharmony_ci/*
20568c2ecf20Sopenharmony_ci * mpc_action helper of an MPC Group Station FSM action
20578c2ecf20Sopenharmony_ci * CTCM_PROTO_MPC only
20588c2ecf20Sopenharmony_ci */
20598c2ecf20Sopenharmony_cistatic int mpc_send_qllc_discontact(struct net_device *dev)
20608c2ecf20Sopenharmony_ci{
20618c2ecf20Sopenharmony_ci	__u32	new_len	= 0;
20628c2ecf20Sopenharmony_ci	struct sk_buff   *skb;
20638c2ecf20Sopenharmony_ci	struct qllc      *qllcptr;
20648c2ecf20Sopenharmony_ci	struct ctcm_priv *priv = dev->ml_priv;
20658c2ecf20Sopenharmony_ci	struct mpc_group *grp = priv->mpcg;
20668c2ecf20Sopenharmony_ci
20678c2ecf20Sopenharmony_ci	CTCM_PR_DEBUG("%s: GROUP STATE: %s\n",
20688c2ecf20Sopenharmony_ci		__func__, mpcg_state_names[grp->saved_state]);
20698c2ecf20Sopenharmony_ci
20708c2ecf20Sopenharmony_ci	switch (grp->saved_state) {
20718c2ecf20Sopenharmony_ci	/*
20728c2ecf20Sopenharmony_ci	 * establish conn callback function is
20738c2ecf20Sopenharmony_ci	 * preferred method to report failure
20748c2ecf20Sopenharmony_ci	 */
20758c2ecf20Sopenharmony_ci	case MPCG_STATE_XID0IOWAIT:
20768c2ecf20Sopenharmony_ci	case MPCG_STATE_XID0IOWAIX:
20778c2ecf20Sopenharmony_ci	case MPCG_STATE_XID7INITI:
20788c2ecf20Sopenharmony_ci	case MPCG_STATE_XID7INITZ:
20798c2ecf20Sopenharmony_ci	case MPCG_STATE_XID2INITW:
20808c2ecf20Sopenharmony_ci	case MPCG_STATE_XID2INITX:
20818c2ecf20Sopenharmony_ci	case MPCG_STATE_XID7INITW:
20828c2ecf20Sopenharmony_ci	case MPCG_STATE_XID7INITX:
20838c2ecf20Sopenharmony_ci		if (grp->estconnfunc) {
20848c2ecf20Sopenharmony_ci			grp->estconnfunc(grp->port_num, -1, 0);
20858c2ecf20Sopenharmony_ci			grp->estconnfunc = NULL;
20868c2ecf20Sopenharmony_ci			break;
20878c2ecf20Sopenharmony_ci		}
20888c2ecf20Sopenharmony_ci		fallthrough;
20898c2ecf20Sopenharmony_ci	case MPCG_STATE_FLOWC:
20908c2ecf20Sopenharmony_ci	case MPCG_STATE_READY:
20918c2ecf20Sopenharmony_ci		grp->send_qllc_disc = 2;
20928c2ecf20Sopenharmony_ci		new_len = sizeof(struct qllc);
20938c2ecf20Sopenharmony_ci		qllcptr = kzalloc(new_len, gfp_type() | GFP_DMA);
20948c2ecf20Sopenharmony_ci		if (qllcptr == NULL) {
20958c2ecf20Sopenharmony_ci			CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
20968c2ecf20Sopenharmony_ci				"%s(%s): qllcptr allocation error",
20978c2ecf20Sopenharmony_ci						CTCM_FUNTAIL, dev->name);
20988c2ecf20Sopenharmony_ci			return -ENOMEM;
20998c2ecf20Sopenharmony_ci		}
21008c2ecf20Sopenharmony_ci
21018c2ecf20Sopenharmony_ci		qllcptr->qllc_address = 0xcc;
21028c2ecf20Sopenharmony_ci		qllcptr->qllc_commands = 0x03;
21038c2ecf20Sopenharmony_ci
21048c2ecf20Sopenharmony_ci		skb = __dev_alloc_skb(new_len, GFP_ATOMIC);
21058c2ecf20Sopenharmony_ci
21068c2ecf20Sopenharmony_ci		if (skb == NULL) {
21078c2ecf20Sopenharmony_ci			CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
21088c2ecf20Sopenharmony_ci				"%s(%s): skb allocation error",
21098c2ecf20Sopenharmony_ci						CTCM_FUNTAIL, dev->name);
21108c2ecf20Sopenharmony_ci			priv->stats.rx_dropped++;
21118c2ecf20Sopenharmony_ci			kfree(qllcptr);
21128c2ecf20Sopenharmony_ci			return -ENOMEM;
21138c2ecf20Sopenharmony_ci		}
21148c2ecf20Sopenharmony_ci
21158c2ecf20Sopenharmony_ci		skb_put_data(skb, qllcptr, new_len);
21168c2ecf20Sopenharmony_ci		kfree(qllcptr);
21178c2ecf20Sopenharmony_ci
21188c2ecf20Sopenharmony_ci		if (skb_headroom(skb) < 4) {
21198c2ecf20Sopenharmony_ci			CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
21208c2ecf20Sopenharmony_ci				"%s(%s): skb_headroom error",
21218c2ecf20Sopenharmony_ci						CTCM_FUNTAIL, dev->name);
21228c2ecf20Sopenharmony_ci			dev_kfree_skb_any(skb);
21238c2ecf20Sopenharmony_ci			return -ENOMEM;
21248c2ecf20Sopenharmony_ci		}
21258c2ecf20Sopenharmony_ci
21268c2ecf20Sopenharmony_ci		*((__u32 *)skb_push(skb, 4)) =
21278c2ecf20Sopenharmony_ci			priv->channel[CTCM_READ]->pdu_seq;
21288c2ecf20Sopenharmony_ci		priv->channel[CTCM_READ]->pdu_seq++;
21298c2ecf20Sopenharmony_ci		CTCM_PR_DBGDATA("ctcmpc: %s ToDCM_pdu_seq= %08x\n",
21308c2ecf20Sopenharmony_ci				__func__, priv->channel[CTCM_READ]->pdu_seq);
21318c2ecf20Sopenharmony_ci
21328c2ecf20Sopenharmony_ci		/* receipt of CC03 resets anticipated sequence number on
21338c2ecf20Sopenharmony_ci		      receiving side */
21348c2ecf20Sopenharmony_ci		priv->channel[CTCM_READ]->pdu_seq = 0x00;
21358c2ecf20Sopenharmony_ci		skb_reset_mac_header(skb);
21368c2ecf20Sopenharmony_ci		skb->dev = dev;
21378c2ecf20Sopenharmony_ci		skb->protocol = htons(ETH_P_SNAP);
21388c2ecf20Sopenharmony_ci		skb->ip_summed = CHECKSUM_UNNECESSARY;
21398c2ecf20Sopenharmony_ci
21408c2ecf20Sopenharmony_ci		CTCM_D3_DUMP(skb->data, (sizeof(struct qllc) + 4));
21418c2ecf20Sopenharmony_ci
21428c2ecf20Sopenharmony_ci		netif_rx(skb);
21438c2ecf20Sopenharmony_ci		break;
21448c2ecf20Sopenharmony_ci	default:
21458c2ecf20Sopenharmony_ci		break;
21468c2ecf20Sopenharmony_ci
21478c2ecf20Sopenharmony_ci	}
21488c2ecf20Sopenharmony_ci
21498c2ecf20Sopenharmony_ci	return 0;
21508c2ecf20Sopenharmony_ci}
21518c2ecf20Sopenharmony_ci/* --- This is the END my friend --- */
21528c2ecf20Sopenharmony_ci
2153