18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright IBM Corp. 2001, 2009
48c2ecf20Sopenharmony_ci * Author(s):
58c2ecf20Sopenharmony_ci *	Original CTC driver(s):
68c2ecf20Sopenharmony_ci *		Fritz Elfert (felfert@millenux.com)
78c2ecf20Sopenharmony_ci *		Dieter Wellerdiek (wel@de.ibm.com)
88c2ecf20Sopenharmony_ci *		Martin Schwidefsky (schwidefsky@de.ibm.com)
98c2ecf20Sopenharmony_ci *		Denis Joseph Barrow (barrow_dj@yahoo.com)
108c2ecf20Sopenharmony_ci *		Jochen Roehrig (roehrig@de.ibm.com)
118c2ecf20Sopenharmony_ci *		Cornelia Huck <cornelia.huck@de.ibm.com>
128c2ecf20Sopenharmony_ci *	MPC additions:
138c2ecf20Sopenharmony_ci *		Belinda Thompson (belindat@us.ibm.com)
148c2ecf20Sopenharmony_ci *		Andy Richter (richtera@us.ibm.com)
158c2ecf20Sopenharmony_ci *	Revived by:
168c2ecf20Sopenharmony_ci *		Peter Tiedemann (ptiedem@de.ibm.com)
178c2ecf20Sopenharmony_ci */
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci#undef DEBUG
208c2ecf20Sopenharmony_ci#undef DEBUGDATA
218c2ecf20Sopenharmony_ci#undef DEBUGCCW
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci#define KMSG_COMPONENT "ctcm"
248c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci#include <linux/module.h>
278c2ecf20Sopenharmony_ci#include <linux/init.h>
288c2ecf20Sopenharmony_ci#include <linux/kernel.h>
298c2ecf20Sopenharmony_ci#include <linux/slab.h>
308c2ecf20Sopenharmony_ci#include <linux/errno.h>
318c2ecf20Sopenharmony_ci#include <linux/types.h>
328c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
338c2ecf20Sopenharmony_ci#include <linux/timer.h>
348c2ecf20Sopenharmony_ci#include <linux/bitops.h>
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci#include <linux/signal.h>
378c2ecf20Sopenharmony_ci#include <linux/string.h>
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci#include <linux/ip.h>
408c2ecf20Sopenharmony_ci#include <linux/if_arp.h>
418c2ecf20Sopenharmony_ci#include <linux/tcp.h>
428c2ecf20Sopenharmony_ci#include <linux/skbuff.h>
438c2ecf20Sopenharmony_ci#include <linux/ctype.h>
448c2ecf20Sopenharmony_ci#include <net/dst.h>
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci#include <linux/io.h>
478c2ecf20Sopenharmony_ci#include <asm/ccwdev.h>
488c2ecf20Sopenharmony_ci#include <asm/ccwgroup.h>
498c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci#include <asm/idals.h>
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci#include "ctcm_fsms.h"
548c2ecf20Sopenharmony_ci#include "ctcm_main.h"
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci/* Some common global variables */
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci/**
598c2ecf20Sopenharmony_ci * The root device for ctcm group devices
608c2ecf20Sopenharmony_ci */
618c2ecf20Sopenharmony_cistatic struct device *ctcm_root_dev;
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci/*
648c2ecf20Sopenharmony_ci * Linked list of all detected channels.
658c2ecf20Sopenharmony_ci */
668c2ecf20Sopenharmony_cistruct channel *channels;
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci/**
698c2ecf20Sopenharmony_ci * Unpack a just received skb and hand it over to
708c2ecf20Sopenharmony_ci * upper layers.
718c2ecf20Sopenharmony_ci *
728c2ecf20Sopenharmony_ci *  ch		The channel where this skb has been received.
738c2ecf20Sopenharmony_ci *  pskb	The received skb.
748c2ecf20Sopenharmony_ci */
758c2ecf20Sopenharmony_civoid ctcm_unpack_skb(struct channel *ch, struct sk_buff *pskb)
768c2ecf20Sopenharmony_ci{
778c2ecf20Sopenharmony_ci	struct net_device *dev = ch->netdev;
788c2ecf20Sopenharmony_ci	struct ctcm_priv *priv = dev->ml_priv;
798c2ecf20Sopenharmony_ci	__u16 len = *((__u16 *) pskb->data);
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	skb_put(pskb, 2 + LL_HEADER_LENGTH);
828c2ecf20Sopenharmony_ci	skb_pull(pskb, 2);
838c2ecf20Sopenharmony_ci	pskb->dev = dev;
848c2ecf20Sopenharmony_ci	pskb->ip_summed = CHECKSUM_UNNECESSARY;
858c2ecf20Sopenharmony_ci	while (len > 0) {
868c2ecf20Sopenharmony_ci		struct sk_buff *skb;
878c2ecf20Sopenharmony_ci		int skblen;
888c2ecf20Sopenharmony_ci		struct ll_header *header = (struct ll_header *)pskb->data;
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci		skb_pull(pskb, LL_HEADER_LENGTH);
918c2ecf20Sopenharmony_ci		if ((ch->protocol == CTCM_PROTO_S390) &&
928c2ecf20Sopenharmony_ci		    (header->type != ETH_P_IP)) {
938c2ecf20Sopenharmony_ci			if (!(ch->logflags & LOG_FLAG_ILLEGALPKT)) {
948c2ecf20Sopenharmony_ci				ch->logflags |= LOG_FLAG_ILLEGALPKT;
958c2ecf20Sopenharmony_ci				/*
968c2ecf20Sopenharmony_ci				 * Check packet type only if we stick strictly
978c2ecf20Sopenharmony_ci				 * to S/390's protocol of OS390. This only
988c2ecf20Sopenharmony_ci				 * supports IP. Otherwise allow any packet
998c2ecf20Sopenharmony_ci				 * type.
1008c2ecf20Sopenharmony_ci				 */
1018c2ecf20Sopenharmony_ci				CTCM_DBF_TEXT_(ERROR, CTC_DBF_ERROR,
1028c2ecf20Sopenharmony_ci					"%s(%s): Illegal packet type 0x%04x"
1038c2ecf20Sopenharmony_ci					" - dropping",
1048c2ecf20Sopenharmony_ci					CTCM_FUNTAIL, dev->name, header->type);
1058c2ecf20Sopenharmony_ci			}
1068c2ecf20Sopenharmony_ci			priv->stats.rx_dropped++;
1078c2ecf20Sopenharmony_ci			priv->stats.rx_frame_errors++;
1088c2ecf20Sopenharmony_ci			return;
1098c2ecf20Sopenharmony_ci		}
1108c2ecf20Sopenharmony_ci		pskb->protocol = cpu_to_be16(header->type);
1118c2ecf20Sopenharmony_ci		if ((header->length <= LL_HEADER_LENGTH) ||
1128c2ecf20Sopenharmony_ci		    (len <= LL_HEADER_LENGTH)) {
1138c2ecf20Sopenharmony_ci			if (!(ch->logflags & LOG_FLAG_ILLEGALSIZE)) {
1148c2ecf20Sopenharmony_ci				CTCM_DBF_TEXT_(ERROR, CTC_DBF_ERROR,
1158c2ecf20Sopenharmony_ci					"%s(%s): Illegal packet size %d(%d,%d)"
1168c2ecf20Sopenharmony_ci					"- dropping",
1178c2ecf20Sopenharmony_ci					CTCM_FUNTAIL, dev->name,
1188c2ecf20Sopenharmony_ci					header->length, dev->mtu, len);
1198c2ecf20Sopenharmony_ci				ch->logflags |= LOG_FLAG_ILLEGALSIZE;
1208c2ecf20Sopenharmony_ci			}
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci			priv->stats.rx_dropped++;
1238c2ecf20Sopenharmony_ci			priv->stats.rx_length_errors++;
1248c2ecf20Sopenharmony_ci			return;
1258c2ecf20Sopenharmony_ci		}
1268c2ecf20Sopenharmony_ci		header->length -= LL_HEADER_LENGTH;
1278c2ecf20Sopenharmony_ci		len -= LL_HEADER_LENGTH;
1288c2ecf20Sopenharmony_ci		if ((header->length > skb_tailroom(pskb)) ||
1298c2ecf20Sopenharmony_ci		    (header->length > len)) {
1308c2ecf20Sopenharmony_ci			if (!(ch->logflags & LOG_FLAG_OVERRUN)) {
1318c2ecf20Sopenharmony_ci				CTCM_DBF_TEXT_(ERROR, CTC_DBF_ERROR,
1328c2ecf20Sopenharmony_ci					"%s(%s): Packet size %d (overrun)"
1338c2ecf20Sopenharmony_ci					" - dropping", CTCM_FUNTAIL,
1348c2ecf20Sopenharmony_ci						dev->name, header->length);
1358c2ecf20Sopenharmony_ci				ch->logflags |= LOG_FLAG_OVERRUN;
1368c2ecf20Sopenharmony_ci			}
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci			priv->stats.rx_dropped++;
1398c2ecf20Sopenharmony_ci			priv->stats.rx_length_errors++;
1408c2ecf20Sopenharmony_ci			return;
1418c2ecf20Sopenharmony_ci		}
1428c2ecf20Sopenharmony_ci		skb_put(pskb, header->length);
1438c2ecf20Sopenharmony_ci		skb_reset_mac_header(pskb);
1448c2ecf20Sopenharmony_ci		len -= header->length;
1458c2ecf20Sopenharmony_ci		skb = dev_alloc_skb(pskb->len);
1468c2ecf20Sopenharmony_ci		if (!skb) {
1478c2ecf20Sopenharmony_ci			if (!(ch->logflags & LOG_FLAG_NOMEM)) {
1488c2ecf20Sopenharmony_ci				CTCM_DBF_TEXT_(ERROR, CTC_DBF_ERROR,
1498c2ecf20Sopenharmony_ci					"%s(%s): MEMORY allocation error",
1508c2ecf20Sopenharmony_ci						CTCM_FUNTAIL, dev->name);
1518c2ecf20Sopenharmony_ci				ch->logflags |= LOG_FLAG_NOMEM;
1528c2ecf20Sopenharmony_ci			}
1538c2ecf20Sopenharmony_ci			priv->stats.rx_dropped++;
1548c2ecf20Sopenharmony_ci			return;
1558c2ecf20Sopenharmony_ci		}
1568c2ecf20Sopenharmony_ci		skb_copy_from_linear_data(pskb, skb_put(skb, pskb->len),
1578c2ecf20Sopenharmony_ci					  pskb->len);
1588c2ecf20Sopenharmony_ci		skb_reset_mac_header(skb);
1598c2ecf20Sopenharmony_ci		skb->dev = pskb->dev;
1608c2ecf20Sopenharmony_ci		skb->protocol = pskb->protocol;
1618c2ecf20Sopenharmony_ci		pskb->ip_summed = CHECKSUM_UNNECESSARY;
1628c2ecf20Sopenharmony_ci		skblen = skb->len;
1638c2ecf20Sopenharmony_ci		/*
1648c2ecf20Sopenharmony_ci		 * reset logflags
1658c2ecf20Sopenharmony_ci		 */
1668c2ecf20Sopenharmony_ci		ch->logflags = 0;
1678c2ecf20Sopenharmony_ci		priv->stats.rx_packets++;
1688c2ecf20Sopenharmony_ci		priv->stats.rx_bytes += skblen;
1698c2ecf20Sopenharmony_ci		netif_rx_ni(skb);
1708c2ecf20Sopenharmony_ci		if (len > 0) {
1718c2ecf20Sopenharmony_ci			skb_pull(pskb, header->length);
1728c2ecf20Sopenharmony_ci			if (skb_tailroom(pskb) < LL_HEADER_LENGTH) {
1738c2ecf20Sopenharmony_ci				CTCM_DBF_DEV_NAME(TRACE, dev,
1748c2ecf20Sopenharmony_ci					"Overrun in ctcm_unpack_skb");
1758c2ecf20Sopenharmony_ci				ch->logflags |= LOG_FLAG_OVERRUN;
1768c2ecf20Sopenharmony_ci				return;
1778c2ecf20Sopenharmony_ci			}
1788c2ecf20Sopenharmony_ci			skb_put(pskb, LL_HEADER_LENGTH);
1798c2ecf20Sopenharmony_ci		}
1808c2ecf20Sopenharmony_ci	}
1818c2ecf20Sopenharmony_ci}
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci/**
1848c2ecf20Sopenharmony_ci * Release a specific channel in the channel list.
1858c2ecf20Sopenharmony_ci *
1868c2ecf20Sopenharmony_ci *  ch		Pointer to channel struct to be released.
1878c2ecf20Sopenharmony_ci */
1888c2ecf20Sopenharmony_cistatic void channel_free(struct channel *ch)
1898c2ecf20Sopenharmony_ci{
1908c2ecf20Sopenharmony_ci	CTCM_DBF_TEXT_(SETUP, CTC_DBF_INFO, "%s(%s)", CTCM_FUNTAIL, ch->id);
1918c2ecf20Sopenharmony_ci	ch->flags &= ~CHANNEL_FLAGS_INUSE;
1928c2ecf20Sopenharmony_ci	fsm_newstate(ch->fsm, CTC_STATE_IDLE);
1938c2ecf20Sopenharmony_ci}
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci/**
1968c2ecf20Sopenharmony_ci * Remove a specific channel in the channel list.
1978c2ecf20Sopenharmony_ci *
1988c2ecf20Sopenharmony_ci *  ch		Pointer to channel struct to be released.
1998c2ecf20Sopenharmony_ci */
2008c2ecf20Sopenharmony_cistatic void channel_remove(struct channel *ch)
2018c2ecf20Sopenharmony_ci{
2028c2ecf20Sopenharmony_ci	struct channel **c = &channels;
2038c2ecf20Sopenharmony_ci	char chid[CTCM_ID_SIZE+1];
2048c2ecf20Sopenharmony_ci	int ok = 0;
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci	if (ch == NULL)
2078c2ecf20Sopenharmony_ci		return;
2088c2ecf20Sopenharmony_ci	else
2098c2ecf20Sopenharmony_ci		strncpy(chid, ch->id, CTCM_ID_SIZE);
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci	channel_free(ch);
2128c2ecf20Sopenharmony_ci	while (*c) {
2138c2ecf20Sopenharmony_ci		if (*c == ch) {
2148c2ecf20Sopenharmony_ci			*c = ch->next;
2158c2ecf20Sopenharmony_ci			fsm_deltimer(&ch->timer);
2168c2ecf20Sopenharmony_ci			if (IS_MPC(ch))
2178c2ecf20Sopenharmony_ci				fsm_deltimer(&ch->sweep_timer);
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci			kfree_fsm(ch->fsm);
2208c2ecf20Sopenharmony_ci			clear_normalized_cda(&ch->ccw[4]);
2218c2ecf20Sopenharmony_ci			if (ch->trans_skb != NULL) {
2228c2ecf20Sopenharmony_ci				clear_normalized_cda(&ch->ccw[1]);
2238c2ecf20Sopenharmony_ci				dev_kfree_skb_any(ch->trans_skb);
2248c2ecf20Sopenharmony_ci			}
2258c2ecf20Sopenharmony_ci			if (IS_MPC(ch)) {
2268c2ecf20Sopenharmony_ci				tasklet_kill(&ch->ch_tasklet);
2278c2ecf20Sopenharmony_ci				tasklet_kill(&ch->ch_disc_tasklet);
2288c2ecf20Sopenharmony_ci				kfree(ch->discontact_th);
2298c2ecf20Sopenharmony_ci			}
2308c2ecf20Sopenharmony_ci			kfree(ch->ccw);
2318c2ecf20Sopenharmony_ci			kfree(ch->irb);
2328c2ecf20Sopenharmony_ci			kfree(ch);
2338c2ecf20Sopenharmony_ci			ok = 1;
2348c2ecf20Sopenharmony_ci			break;
2358c2ecf20Sopenharmony_ci		}
2368c2ecf20Sopenharmony_ci		c = &((*c)->next);
2378c2ecf20Sopenharmony_ci	}
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci	CTCM_DBF_TEXT_(SETUP, CTC_DBF_INFO, "%s(%s) %s", CTCM_FUNTAIL,
2408c2ecf20Sopenharmony_ci			chid, ok ? "OK" : "failed");
2418c2ecf20Sopenharmony_ci}
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci/**
2448c2ecf20Sopenharmony_ci * Get a specific channel from the channel list.
2458c2ecf20Sopenharmony_ci *
2468c2ecf20Sopenharmony_ci *  type	Type of channel we are interested in.
2478c2ecf20Sopenharmony_ci *  id		Id of channel we are interested in.
2488c2ecf20Sopenharmony_ci *  direction	Direction we want to use this channel for.
2498c2ecf20Sopenharmony_ci *
2508c2ecf20Sopenharmony_ci * returns Pointer to a channel or NULL if no matching channel available.
2518c2ecf20Sopenharmony_ci */
2528c2ecf20Sopenharmony_cistatic struct channel *channel_get(enum ctcm_channel_types type,
2538c2ecf20Sopenharmony_ci					char *id, int direction)
2548c2ecf20Sopenharmony_ci{
2558c2ecf20Sopenharmony_ci	struct channel *ch = channels;
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci	while (ch && (strncmp(ch->id, id, CTCM_ID_SIZE) || (ch->type != type)))
2588c2ecf20Sopenharmony_ci		ch = ch->next;
2598c2ecf20Sopenharmony_ci	if (!ch) {
2608c2ecf20Sopenharmony_ci		CTCM_DBF_TEXT_(ERROR, CTC_DBF_ERROR,
2618c2ecf20Sopenharmony_ci				"%s(%d, %s, %d) not found in channel list\n",
2628c2ecf20Sopenharmony_ci				CTCM_FUNTAIL, type, id, direction);
2638c2ecf20Sopenharmony_ci	} else {
2648c2ecf20Sopenharmony_ci		if (ch->flags & CHANNEL_FLAGS_INUSE)
2658c2ecf20Sopenharmony_ci			ch = NULL;
2668c2ecf20Sopenharmony_ci		else {
2678c2ecf20Sopenharmony_ci			ch->flags |= CHANNEL_FLAGS_INUSE;
2688c2ecf20Sopenharmony_ci			ch->flags &= ~CHANNEL_FLAGS_RWMASK;
2698c2ecf20Sopenharmony_ci			ch->flags |= (direction == CTCM_WRITE)
2708c2ecf20Sopenharmony_ci			    ? CHANNEL_FLAGS_WRITE : CHANNEL_FLAGS_READ;
2718c2ecf20Sopenharmony_ci			fsm_newstate(ch->fsm, CTC_STATE_STOPPED);
2728c2ecf20Sopenharmony_ci		}
2738c2ecf20Sopenharmony_ci	}
2748c2ecf20Sopenharmony_ci	return ch;
2758c2ecf20Sopenharmony_ci}
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_cistatic long ctcm_check_irb_error(struct ccw_device *cdev, struct irb *irb)
2788c2ecf20Sopenharmony_ci{
2798c2ecf20Sopenharmony_ci	if (!IS_ERR(irb))
2808c2ecf20Sopenharmony_ci		return 0;
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci	CTCM_DBF_TEXT_(ERROR, CTC_DBF_WARN,
2838c2ecf20Sopenharmony_ci			"irb error %ld on device %s\n",
2848c2ecf20Sopenharmony_ci				PTR_ERR(irb), dev_name(&cdev->dev));
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci	switch (PTR_ERR(irb)) {
2878c2ecf20Sopenharmony_ci	case -EIO:
2888c2ecf20Sopenharmony_ci		dev_err(&cdev->dev,
2898c2ecf20Sopenharmony_ci			"An I/O-error occurred on the CTCM device\n");
2908c2ecf20Sopenharmony_ci		break;
2918c2ecf20Sopenharmony_ci	case -ETIMEDOUT:
2928c2ecf20Sopenharmony_ci		dev_err(&cdev->dev,
2938c2ecf20Sopenharmony_ci			"An adapter hardware operation timed out\n");
2948c2ecf20Sopenharmony_ci		break;
2958c2ecf20Sopenharmony_ci	default:
2968c2ecf20Sopenharmony_ci		dev_err(&cdev->dev,
2978c2ecf20Sopenharmony_ci			"An error occurred on the adapter hardware\n");
2988c2ecf20Sopenharmony_ci	}
2998c2ecf20Sopenharmony_ci	return PTR_ERR(irb);
3008c2ecf20Sopenharmony_ci}
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ci/**
3048c2ecf20Sopenharmony_ci * Check sense of a unit check.
3058c2ecf20Sopenharmony_ci *
3068c2ecf20Sopenharmony_ci *  ch		The channel, the sense code belongs to.
3078c2ecf20Sopenharmony_ci *  sense	The sense code to inspect.
3088c2ecf20Sopenharmony_ci */
3098c2ecf20Sopenharmony_cistatic void ccw_unit_check(struct channel *ch, __u8 sense)
3108c2ecf20Sopenharmony_ci{
3118c2ecf20Sopenharmony_ci	CTCM_DBF_TEXT_(TRACE, CTC_DBF_DEBUG,
3128c2ecf20Sopenharmony_ci			"%s(%s): %02x",
3138c2ecf20Sopenharmony_ci				CTCM_FUNTAIL, ch->id, sense);
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_ci	if (sense & SNS0_INTERVENTION_REQ) {
3168c2ecf20Sopenharmony_ci		if (sense & 0x01) {
3178c2ecf20Sopenharmony_ci			if (ch->sense_rc != 0x01) {
3188c2ecf20Sopenharmony_ci				pr_notice(
3198c2ecf20Sopenharmony_ci					"%s: The communication peer has "
3208c2ecf20Sopenharmony_ci					"disconnected\n", ch->id);
3218c2ecf20Sopenharmony_ci				ch->sense_rc = 0x01;
3228c2ecf20Sopenharmony_ci			}
3238c2ecf20Sopenharmony_ci			fsm_event(ch->fsm, CTC_EVENT_UC_RCRESET, ch);
3248c2ecf20Sopenharmony_ci		} else {
3258c2ecf20Sopenharmony_ci			if (ch->sense_rc != SNS0_INTERVENTION_REQ) {
3268c2ecf20Sopenharmony_ci				pr_notice(
3278c2ecf20Sopenharmony_ci					"%s: The remote operating system is "
3288c2ecf20Sopenharmony_ci					"not available\n", ch->id);
3298c2ecf20Sopenharmony_ci				ch->sense_rc = SNS0_INTERVENTION_REQ;
3308c2ecf20Sopenharmony_ci			}
3318c2ecf20Sopenharmony_ci			fsm_event(ch->fsm, CTC_EVENT_UC_RSRESET, ch);
3328c2ecf20Sopenharmony_ci		}
3338c2ecf20Sopenharmony_ci	} else if (sense & SNS0_EQUIPMENT_CHECK) {
3348c2ecf20Sopenharmony_ci		if (sense & SNS0_BUS_OUT_CHECK) {
3358c2ecf20Sopenharmony_ci			if (ch->sense_rc != SNS0_BUS_OUT_CHECK) {
3368c2ecf20Sopenharmony_ci				CTCM_DBF_TEXT_(TRACE, CTC_DBF_WARN,
3378c2ecf20Sopenharmony_ci					"%s(%s): remote HW error %02x",
3388c2ecf20Sopenharmony_ci						CTCM_FUNTAIL, ch->id, sense);
3398c2ecf20Sopenharmony_ci				ch->sense_rc = SNS0_BUS_OUT_CHECK;
3408c2ecf20Sopenharmony_ci			}
3418c2ecf20Sopenharmony_ci			fsm_event(ch->fsm, CTC_EVENT_UC_HWFAIL, ch);
3428c2ecf20Sopenharmony_ci		} else {
3438c2ecf20Sopenharmony_ci			if (ch->sense_rc != SNS0_EQUIPMENT_CHECK) {
3448c2ecf20Sopenharmony_ci				CTCM_DBF_TEXT_(TRACE, CTC_DBF_WARN,
3458c2ecf20Sopenharmony_ci					"%s(%s): remote read parity error %02x",
3468c2ecf20Sopenharmony_ci						CTCM_FUNTAIL, ch->id, sense);
3478c2ecf20Sopenharmony_ci				ch->sense_rc = SNS0_EQUIPMENT_CHECK;
3488c2ecf20Sopenharmony_ci			}
3498c2ecf20Sopenharmony_ci			fsm_event(ch->fsm, CTC_EVENT_UC_RXPARITY, ch);
3508c2ecf20Sopenharmony_ci		}
3518c2ecf20Sopenharmony_ci	} else if (sense & SNS0_BUS_OUT_CHECK) {
3528c2ecf20Sopenharmony_ci		if (ch->sense_rc != SNS0_BUS_OUT_CHECK) {
3538c2ecf20Sopenharmony_ci			CTCM_DBF_TEXT_(TRACE, CTC_DBF_WARN,
3548c2ecf20Sopenharmony_ci				"%s(%s): BUS OUT error %02x",
3558c2ecf20Sopenharmony_ci					CTCM_FUNTAIL, ch->id, sense);
3568c2ecf20Sopenharmony_ci			ch->sense_rc = SNS0_BUS_OUT_CHECK;
3578c2ecf20Sopenharmony_ci		}
3588c2ecf20Sopenharmony_ci		if (sense & 0x04)	/* data-streaming timeout */
3598c2ecf20Sopenharmony_ci			fsm_event(ch->fsm, CTC_EVENT_UC_TXTIMEOUT, ch);
3608c2ecf20Sopenharmony_ci		else			/* Data-transfer parity error */
3618c2ecf20Sopenharmony_ci			fsm_event(ch->fsm, CTC_EVENT_UC_TXPARITY, ch);
3628c2ecf20Sopenharmony_ci	} else if (sense & SNS0_CMD_REJECT) {
3638c2ecf20Sopenharmony_ci		if (ch->sense_rc != SNS0_CMD_REJECT) {
3648c2ecf20Sopenharmony_ci			CTCM_DBF_TEXT_(TRACE, CTC_DBF_WARN,
3658c2ecf20Sopenharmony_ci				"%s(%s): Command rejected",
3668c2ecf20Sopenharmony_ci						CTCM_FUNTAIL, ch->id);
3678c2ecf20Sopenharmony_ci			ch->sense_rc = SNS0_CMD_REJECT;
3688c2ecf20Sopenharmony_ci		}
3698c2ecf20Sopenharmony_ci	} else if (sense == 0) {
3708c2ecf20Sopenharmony_ci		CTCM_DBF_TEXT_(TRACE, CTC_DBF_WARN,
3718c2ecf20Sopenharmony_ci			"%s(%s): Unit check ZERO",
3728c2ecf20Sopenharmony_ci					CTCM_FUNTAIL, ch->id);
3738c2ecf20Sopenharmony_ci		fsm_event(ch->fsm, CTC_EVENT_UC_ZERO, ch);
3748c2ecf20Sopenharmony_ci	} else {
3758c2ecf20Sopenharmony_ci		CTCM_DBF_TEXT_(TRACE, CTC_DBF_WARN,
3768c2ecf20Sopenharmony_ci			"%s(%s): Unit check code %02x unknown",
3778c2ecf20Sopenharmony_ci					CTCM_FUNTAIL, ch->id, sense);
3788c2ecf20Sopenharmony_ci		fsm_event(ch->fsm, CTC_EVENT_UC_UNKNOWN, ch);
3798c2ecf20Sopenharmony_ci	}
3808c2ecf20Sopenharmony_ci}
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_ciint ctcm_ch_alloc_buffer(struct channel *ch)
3838c2ecf20Sopenharmony_ci{
3848c2ecf20Sopenharmony_ci	clear_normalized_cda(&ch->ccw[1]);
3858c2ecf20Sopenharmony_ci	ch->trans_skb = __dev_alloc_skb(ch->max_bufsize, GFP_ATOMIC | GFP_DMA);
3868c2ecf20Sopenharmony_ci	if (ch->trans_skb == NULL) {
3878c2ecf20Sopenharmony_ci		CTCM_DBF_TEXT_(ERROR, CTC_DBF_ERROR,
3888c2ecf20Sopenharmony_ci			"%s(%s): %s trans_skb allocation error",
3898c2ecf20Sopenharmony_ci			CTCM_FUNTAIL, ch->id,
3908c2ecf20Sopenharmony_ci			(CHANNEL_DIRECTION(ch->flags) == CTCM_READ) ?
3918c2ecf20Sopenharmony_ci				"RX" : "TX");
3928c2ecf20Sopenharmony_ci		return -ENOMEM;
3938c2ecf20Sopenharmony_ci	}
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_ci	ch->ccw[1].count = ch->max_bufsize;
3968c2ecf20Sopenharmony_ci	if (set_normalized_cda(&ch->ccw[1], ch->trans_skb->data)) {
3978c2ecf20Sopenharmony_ci		dev_kfree_skb(ch->trans_skb);
3988c2ecf20Sopenharmony_ci		ch->trans_skb = NULL;
3998c2ecf20Sopenharmony_ci		CTCM_DBF_TEXT_(ERROR, CTC_DBF_ERROR,
4008c2ecf20Sopenharmony_ci			"%s(%s): %s set norm_cda failed",
4018c2ecf20Sopenharmony_ci			CTCM_FUNTAIL, ch->id,
4028c2ecf20Sopenharmony_ci			(CHANNEL_DIRECTION(ch->flags) == CTCM_READ) ?
4038c2ecf20Sopenharmony_ci				"RX" : "TX");
4048c2ecf20Sopenharmony_ci		return -ENOMEM;
4058c2ecf20Sopenharmony_ci	}
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_ci	ch->ccw[1].count = 0;
4088c2ecf20Sopenharmony_ci	ch->trans_skb_data = ch->trans_skb->data;
4098c2ecf20Sopenharmony_ci	ch->flags &= ~CHANNEL_FLAGS_BUFSIZE_CHANGED;
4108c2ecf20Sopenharmony_ci	return 0;
4118c2ecf20Sopenharmony_ci}
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_ci/*
4148c2ecf20Sopenharmony_ci * Interface API for upper network layers
4158c2ecf20Sopenharmony_ci */
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_ci/**
4188c2ecf20Sopenharmony_ci * Open an interface.
4198c2ecf20Sopenharmony_ci * Called from generic network layer when ifconfig up is run.
4208c2ecf20Sopenharmony_ci *
4218c2ecf20Sopenharmony_ci *  dev		Pointer to interface struct.
4228c2ecf20Sopenharmony_ci *
4238c2ecf20Sopenharmony_ci * returns 0 on success, -ERRNO on failure. (Never fails.)
4248c2ecf20Sopenharmony_ci */
4258c2ecf20Sopenharmony_ciint ctcm_open(struct net_device *dev)
4268c2ecf20Sopenharmony_ci{
4278c2ecf20Sopenharmony_ci	struct ctcm_priv *priv = dev->ml_priv;
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_ci	CTCMY_DBF_DEV_NAME(SETUP, dev, "");
4308c2ecf20Sopenharmony_ci	if (!IS_MPC(priv))
4318c2ecf20Sopenharmony_ci		fsm_event(priv->fsm,	DEV_EVENT_START, dev);
4328c2ecf20Sopenharmony_ci	return 0;
4338c2ecf20Sopenharmony_ci}
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_ci/**
4368c2ecf20Sopenharmony_ci * Close an interface.
4378c2ecf20Sopenharmony_ci * Called from generic network layer when ifconfig down is run.
4388c2ecf20Sopenharmony_ci *
4398c2ecf20Sopenharmony_ci *  dev		Pointer to interface struct.
4408c2ecf20Sopenharmony_ci *
4418c2ecf20Sopenharmony_ci * returns 0 on success, -ERRNO on failure. (Never fails.)
4428c2ecf20Sopenharmony_ci */
4438c2ecf20Sopenharmony_ciint ctcm_close(struct net_device *dev)
4448c2ecf20Sopenharmony_ci{
4458c2ecf20Sopenharmony_ci	struct ctcm_priv *priv = dev->ml_priv;
4468c2ecf20Sopenharmony_ci
4478c2ecf20Sopenharmony_ci	CTCMY_DBF_DEV_NAME(SETUP, dev, "");
4488c2ecf20Sopenharmony_ci	if (!IS_MPC(priv))
4498c2ecf20Sopenharmony_ci		fsm_event(priv->fsm, DEV_EVENT_STOP, dev);
4508c2ecf20Sopenharmony_ci	return 0;
4518c2ecf20Sopenharmony_ci}
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_ci
4548c2ecf20Sopenharmony_ci/**
4558c2ecf20Sopenharmony_ci * Transmit a packet.
4568c2ecf20Sopenharmony_ci * This is a helper function for ctcm_tx().
4578c2ecf20Sopenharmony_ci *
4588c2ecf20Sopenharmony_ci *  ch		Channel to be used for sending.
4598c2ecf20Sopenharmony_ci *  skb		Pointer to struct sk_buff of packet to send.
4608c2ecf20Sopenharmony_ci *            The linklevel header has already been set up
4618c2ecf20Sopenharmony_ci *            by ctcm_tx().
4628c2ecf20Sopenharmony_ci *
4638c2ecf20Sopenharmony_ci * returns 0 on success, -ERRNO on failure. (Never fails.)
4648c2ecf20Sopenharmony_ci */
4658c2ecf20Sopenharmony_cistatic int ctcm_transmit_skb(struct channel *ch, struct sk_buff *skb)
4668c2ecf20Sopenharmony_ci{
4678c2ecf20Sopenharmony_ci	unsigned long saveflags;
4688c2ecf20Sopenharmony_ci	struct ll_header header;
4698c2ecf20Sopenharmony_ci	int rc = 0;
4708c2ecf20Sopenharmony_ci	__u16 block_len;
4718c2ecf20Sopenharmony_ci	int ccw_idx;
4728c2ecf20Sopenharmony_ci	struct sk_buff *nskb;
4738c2ecf20Sopenharmony_ci	unsigned long hi;
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_ci	/* we need to acquire the lock for testing the state
4768c2ecf20Sopenharmony_ci	 * otherwise we can have an IRQ changing the state to
4778c2ecf20Sopenharmony_ci	 * TXIDLE after the test but before acquiring the lock.
4788c2ecf20Sopenharmony_ci	 */
4798c2ecf20Sopenharmony_ci	spin_lock_irqsave(&ch->collect_lock, saveflags);
4808c2ecf20Sopenharmony_ci	if (fsm_getstate(ch->fsm) != CTC_STATE_TXIDLE) {
4818c2ecf20Sopenharmony_ci		int l = skb->len + LL_HEADER_LENGTH;
4828c2ecf20Sopenharmony_ci
4838c2ecf20Sopenharmony_ci		if (ch->collect_len + l > ch->max_bufsize - 2) {
4848c2ecf20Sopenharmony_ci			spin_unlock_irqrestore(&ch->collect_lock, saveflags);
4858c2ecf20Sopenharmony_ci			return -EBUSY;
4868c2ecf20Sopenharmony_ci		} else {
4878c2ecf20Sopenharmony_ci			refcount_inc(&skb->users);
4888c2ecf20Sopenharmony_ci			header.length = l;
4898c2ecf20Sopenharmony_ci			header.type = be16_to_cpu(skb->protocol);
4908c2ecf20Sopenharmony_ci			header.unused = 0;
4918c2ecf20Sopenharmony_ci			memcpy(skb_push(skb, LL_HEADER_LENGTH), &header,
4928c2ecf20Sopenharmony_ci			       LL_HEADER_LENGTH);
4938c2ecf20Sopenharmony_ci			skb_queue_tail(&ch->collect_queue, skb);
4948c2ecf20Sopenharmony_ci			ch->collect_len += l;
4958c2ecf20Sopenharmony_ci		}
4968c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&ch->collect_lock, saveflags);
4978c2ecf20Sopenharmony_ci				goto done;
4988c2ecf20Sopenharmony_ci	}
4998c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&ch->collect_lock, saveflags);
5008c2ecf20Sopenharmony_ci	/*
5018c2ecf20Sopenharmony_ci	 * Protect skb against beeing free'd by upper
5028c2ecf20Sopenharmony_ci	 * layers.
5038c2ecf20Sopenharmony_ci	 */
5048c2ecf20Sopenharmony_ci	refcount_inc(&skb->users);
5058c2ecf20Sopenharmony_ci	ch->prof.txlen += skb->len;
5068c2ecf20Sopenharmony_ci	header.length = skb->len + LL_HEADER_LENGTH;
5078c2ecf20Sopenharmony_ci	header.type = be16_to_cpu(skb->protocol);
5088c2ecf20Sopenharmony_ci	header.unused = 0;
5098c2ecf20Sopenharmony_ci	memcpy(skb_push(skb, LL_HEADER_LENGTH), &header, LL_HEADER_LENGTH);
5108c2ecf20Sopenharmony_ci	block_len = skb->len + 2;
5118c2ecf20Sopenharmony_ci	*((__u16 *)skb_push(skb, 2)) = block_len;
5128c2ecf20Sopenharmony_ci
5138c2ecf20Sopenharmony_ci	/*
5148c2ecf20Sopenharmony_ci	 * IDAL support in CTCM is broken, so we have to
5158c2ecf20Sopenharmony_ci	 * care about skb's above 2G ourselves.
5168c2ecf20Sopenharmony_ci	 */
5178c2ecf20Sopenharmony_ci	hi = ((unsigned long)skb_tail_pointer(skb) + LL_HEADER_LENGTH) >> 31;
5188c2ecf20Sopenharmony_ci	if (hi) {
5198c2ecf20Sopenharmony_ci		nskb = alloc_skb(skb->len, GFP_ATOMIC | GFP_DMA);
5208c2ecf20Sopenharmony_ci		if (!nskb) {
5218c2ecf20Sopenharmony_ci			refcount_dec(&skb->users);
5228c2ecf20Sopenharmony_ci			skb_pull(skb, LL_HEADER_LENGTH + 2);
5238c2ecf20Sopenharmony_ci			ctcm_clear_busy(ch->netdev);
5248c2ecf20Sopenharmony_ci			return -ENOMEM;
5258c2ecf20Sopenharmony_ci		} else {
5268c2ecf20Sopenharmony_ci			skb_put_data(nskb, skb->data, skb->len);
5278c2ecf20Sopenharmony_ci			refcount_inc(&nskb->users);
5288c2ecf20Sopenharmony_ci			refcount_dec(&skb->users);
5298c2ecf20Sopenharmony_ci			dev_kfree_skb_irq(skb);
5308c2ecf20Sopenharmony_ci			skb = nskb;
5318c2ecf20Sopenharmony_ci		}
5328c2ecf20Sopenharmony_ci	}
5338c2ecf20Sopenharmony_ci
5348c2ecf20Sopenharmony_ci	ch->ccw[4].count = block_len;
5358c2ecf20Sopenharmony_ci	if (set_normalized_cda(&ch->ccw[4], skb->data)) {
5368c2ecf20Sopenharmony_ci		/*
5378c2ecf20Sopenharmony_ci		 * idal allocation failed, try via copying to
5388c2ecf20Sopenharmony_ci		 * trans_skb. trans_skb usually has a pre-allocated
5398c2ecf20Sopenharmony_ci		 * idal.
5408c2ecf20Sopenharmony_ci		 */
5418c2ecf20Sopenharmony_ci		if (ctcm_checkalloc_buffer(ch)) {
5428c2ecf20Sopenharmony_ci			/*
5438c2ecf20Sopenharmony_ci			 * Remove our header. It gets added
5448c2ecf20Sopenharmony_ci			 * again on retransmit.
5458c2ecf20Sopenharmony_ci			 */
5468c2ecf20Sopenharmony_ci			refcount_dec(&skb->users);
5478c2ecf20Sopenharmony_ci			skb_pull(skb, LL_HEADER_LENGTH + 2);
5488c2ecf20Sopenharmony_ci			ctcm_clear_busy(ch->netdev);
5498c2ecf20Sopenharmony_ci			return -ENOMEM;
5508c2ecf20Sopenharmony_ci		}
5518c2ecf20Sopenharmony_ci
5528c2ecf20Sopenharmony_ci		skb_reset_tail_pointer(ch->trans_skb);
5538c2ecf20Sopenharmony_ci		ch->trans_skb->len = 0;
5548c2ecf20Sopenharmony_ci		ch->ccw[1].count = skb->len;
5558c2ecf20Sopenharmony_ci		skb_copy_from_linear_data(skb,
5568c2ecf20Sopenharmony_ci				skb_put(ch->trans_skb, skb->len), skb->len);
5578c2ecf20Sopenharmony_ci		refcount_dec(&skb->users);
5588c2ecf20Sopenharmony_ci		dev_kfree_skb_irq(skb);
5598c2ecf20Sopenharmony_ci		ccw_idx = 0;
5608c2ecf20Sopenharmony_ci	} else {
5618c2ecf20Sopenharmony_ci		skb_queue_tail(&ch->io_queue, skb);
5628c2ecf20Sopenharmony_ci		ccw_idx = 3;
5638c2ecf20Sopenharmony_ci	}
5648c2ecf20Sopenharmony_ci	if (do_debug_ccw)
5658c2ecf20Sopenharmony_ci		ctcmpc_dumpit((char *)&ch->ccw[ccw_idx],
5668c2ecf20Sopenharmony_ci					sizeof(struct ccw1) * 3);
5678c2ecf20Sopenharmony_ci	ch->retry = 0;
5688c2ecf20Sopenharmony_ci	fsm_newstate(ch->fsm, CTC_STATE_TX);
5698c2ecf20Sopenharmony_ci	fsm_addtimer(&ch->timer, CTCM_TIME_5_SEC, CTC_EVENT_TIMER, ch);
5708c2ecf20Sopenharmony_ci	spin_lock_irqsave(get_ccwdev_lock(ch->cdev), saveflags);
5718c2ecf20Sopenharmony_ci	ch->prof.send_stamp = jiffies;
5728c2ecf20Sopenharmony_ci	rc = ccw_device_start(ch->cdev, &ch->ccw[ccw_idx], 0, 0xff, 0);
5738c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(get_ccwdev_lock(ch->cdev), saveflags);
5748c2ecf20Sopenharmony_ci	if (ccw_idx == 3)
5758c2ecf20Sopenharmony_ci		ch->prof.doios_single++;
5768c2ecf20Sopenharmony_ci	if (rc != 0) {
5778c2ecf20Sopenharmony_ci		fsm_deltimer(&ch->timer);
5788c2ecf20Sopenharmony_ci		ctcm_ccw_check_rc(ch, rc, "single skb TX");
5798c2ecf20Sopenharmony_ci		if (ccw_idx == 3)
5808c2ecf20Sopenharmony_ci			skb_dequeue_tail(&ch->io_queue);
5818c2ecf20Sopenharmony_ci		/*
5828c2ecf20Sopenharmony_ci		 * Remove our header. It gets added
5838c2ecf20Sopenharmony_ci		 * again on retransmit.
5848c2ecf20Sopenharmony_ci		 */
5858c2ecf20Sopenharmony_ci		skb_pull(skb, LL_HEADER_LENGTH + 2);
5868c2ecf20Sopenharmony_ci	} else if (ccw_idx == 0) {
5878c2ecf20Sopenharmony_ci		struct net_device *dev = ch->netdev;
5888c2ecf20Sopenharmony_ci		struct ctcm_priv *priv = dev->ml_priv;
5898c2ecf20Sopenharmony_ci		priv->stats.tx_packets++;
5908c2ecf20Sopenharmony_ci		priv->stats.tx_bytes += skb->len - LL_HEADER_LENGTH;
5918c2ecf20Sopenharmony_ci	}
5928c2ecf20Sopenharmony_cidone:
5938c2ecf20Sopenharmony_ci	ctcm_clear_busy(ch->netdev);
5948c2ecf20Sopenharmony_ci	return rc;
5958c2ecf20Sopenharmony_ci}
5968c2ecf20Sopenharmony_ci
5978c2ecf20Sopenharmony_cistatic void ctcmpc_send_sweep_req(struct channel *rch)
5988c2ecf20Sopenharmony_ci{
5998c2ecf20Sopenharmony_ci	struct net_device *dev = rch->netdev;
6008c2ecf20Sopenharmony_ci	struct ctcm_priv *priv;
6018c2ecf20Sopenharmony_ci	struct mpc_group *grp;
6028c2ecf20Sopenharmony_ci	struct th_sweep *header;
6038c2ecf20Sopenharmony_ci	struct sk_buff *sweep_skb;
6048c2ecf20Sopenharmony_ci	struct channel *ch;
6058c2ecf20Sopenharmony_ci	/* int rc = 0; */
6068c2ecf20Sopenharmony_ci
6078c2ecf20Sopenharmony_ci	priv = dev->ml_priv;
6088c2ecf20Sopenharmony_ci	grp = priv->mpcg;
6098c2ecf20Sopenharmony_ci	ch = priv->channel[CTCM_WRITE];
6108c2ecf20Sopenharmony_ci
6118c2ecf20Sopenharmony_ci	/* sweep processing is not complete until response and request */
6128c2ecf20Sopenharmony_ci	/* has completed for all read channels in group		       */
6138c2ecf20Sopenharmony_ci	if (grp->in_sweep == 0) {
6148c2ecf20Sopenharmony_ci		grp->in_sweep = 1;
6158c2ecf20Sopenharmony_ci		grp->sweep_rsp_pend_num = grp->active_channels[CTCM_READ];
6168c2ecf20Sopenharmony_ci		grp->sweep_req_pend_num = grp->active_channels[CTCM_READ];
6178c2ecf20Sopenharmony_ci	}
6188c2ecf20Sopenharmony_ci
6198c2ecf20Sopenharmony_ci	sweep_skb = __dev_alloc_skb(MPC_BUFSIZE_DEFAULT, GFP_ATOMIC|GFP_DMA);
6208c2ecf20Sopenharmony_ci
6218c2ecf20Sopenharmony_ci	if (sweep_skb == NULL)	{
6228c2ecf20Sopenharmony_ci		/* rc = -ENOMEM; */
6238c2ecf20Sopenharmony_ci				goto nomem;
6248c2ecf20Sopenharmony_ci	}
6258c2ecf20Sopenharmony_ci
6268c2ecf20Sopenharmony_ci	header = kmalloc(TH_SWEEP_LENGTH, gfp_type());
6278c2ecf20Sopenharmony_ci
6288c2ecf20Sopenharmony_ci	if (!header) {
6298c2ecf20Sopenharmony_ci		dev_kfree_skb_any(sweep_skb);
6308c2ecf20Sopenharmony_ci		/* rc = -ENOMEM; */
6318c2ecf20Sopenharmony_ci				goto nomem;
6328c2ecf20Sopenharmony_ci	}
6338c2ecf20Sopenharmony_ci
6348c2ecf20Sopenharmony_ci	header->th.th_seg	= 0x00 ;
6358c2ecf20Sopenharmony_ci	header->th.th_ch_flag	= TH_SWEEP_REQ;  /* 0x0f */
6368c2ecf20Sopenharmony_ci	header->th.th_blk_flag	= 0x00;
6378c2ecf20Sopenharmony_ci	header->th.th_is_xid	= 0x00;
6388c2ecf20Sopenharmony_ci	header->th.th_seq_num	= 0x00;
6398c2ecf20Sopenharmony_ci	header->sw.th_last_seq	= ch->th_seq_num;
6408c2ecf20Sopenharmony_ci
6418c2ecf20Sopenharmony_ci	skb_put_data(sweep_skb, header, TH_SWEEP_LENGTH);
6428c2ecf20Sopenharmony_ci
6438c2ecf20Sopenharmony_ci	kfree(header);
6448c2ecf20Sopenharmony_ci
6458c2ecf20Sopenharmony_ci	netif_trans_update(dev);
6468c2ecf20Sopenharmony_ci	skb_queue_tail(&ch->sweep_queue, sweep_skb);
6478c2ecf20Sopenharmony_ci
6488c2ecf20Sopenharmony_ci	fsm_addtimer(&ch->sweep_timer, 100, CTC_EVENT_RSWEEP_TIMER, ch);
6498c2ecf20Sopenharmony_ci
6508c2ecf20Sopenharmony_ci	return;
6518c2ecf20Sopenharmony_ci
6528c2ecf20Sopenharmony_cinomem:
6538c2ecf20Sopenharmony_ci	grp->in_sweep = 0;
6548c2ecf20Sopenharmony_ci	ctcm_clear_busy(dev);
6558c2ecf20Sopenharmony_ci	fsm_event(grp->fsm, MPCG_EVENT_INOP, dev);
6568c2ecf20Sopenharmony_ci
6578c2ecf20Sopenharmony_ci	return;
6588c2ecf20Sopenharmony_ci}
6598c2ecf20Sopenharmony_ci
6608c2ecf20Sopenharmony_ci/*
6618c2ecf20Sopenharmony_ci * MPC mode version of transmit_skb
6628c2ecf20Sopenharmony_ci */
6638c2ecf20Sopenharmony_cistatic int ctcmpc_transmit_skb(struct channel *ch, struct sk_buff *skb)
6648c2ecf20Sopenharmony_ci{
6658c2ecf20Sopenharmony_ci	struct pdu *p_header;
6668c2ecf20Sopenharmony_ci	struct net_device *dev = ch->netdev;
6678c2ecf20Sopenharmony_ci	struct ctcm_priv *priv = dev->ml_priv;
6688c2ecf20Sopenharmony_ci	struct mpc_group *grp = priv->mpcg;
6698c2ecf20Sopenharmony_ci	struct th_header *header;
6708c2ecf20Sopenharmony_ci	struct sk_buff *nskb;
6718c2ecf20Sopenharmony_ci	int rc = 0;
6728c2ecf20Sopenharmony_ci	int ccw_idx;
6738c2ecf20Sopenharmony_ci	unsigned long hi;
6748c2ecf20Sopenharmony_ci	unsigned long saveflags = 0;	/* avoids compiler warning */
6758c2ecf20Sopenharmony_ci
6768c2ecf20Sopenharmony_ci	CTCM_PR_DEBUG("Enter %s: %s, cp=%i ch=0x%p id=%s state=%s\n",
6778c2ecf20Sopenharmony_ci			__func__, dev->name, smp_processor_id(), ch,
6788c2ecf20Sopenharmony_ci					ch->id, fsm_getstate_str(ch->fsm));
6798c2ecf20Sopenharmony_ci
6808c2ecf20Sopenharmony_ci	if ((fsm_getstate(ch->fsm) != CTC_STATE_TXIDLE) || grp->in_sweep) {
6818c2ecf20Sopenharmony_ci		spin_lock_irqsave(&ch->collect_lock, saveflags);
6828c2ecf20Sopenharmony_ci		refcount_inc(&skb->users);
6838c2ecf20Sopenharmony_ci		p_header = kmalloc(PDU_HEADER_LENGTH, gfp_type());
6848c2ecf20Sopenharmony_ci
6858c2ecf20Sopenharmony_ci		if (!p_header) {
6868c2ecf20Sopenharmony_ci			spin_unlock_irqrestore(&ch->collect_lock, saveflags);
6878c2ecf20Sopenharmony_ci				goto nomem_exit;
6888c2ecf20Sopenharmony_ci		}
6898c2ecf20Sopenharmony_ci
6908c2ecf20Sopenharmony_ci		p_header->pdu_offset = skb->len;
6918c2ecf20Sopenharmony_ci		p_header->pdu_proto = 0x01;
6928c2ecf20Sopenharmony_ci		p_header->pdu_flag = 0x00;
6938c2ecf20Sopenharmony_ci		if (be16_to_cpu(skb->protocol) == ETH_P_SNAP) {
6948c2ecf20Sopenharmony_ci			p_header->pdu_flag |= PDU_FIRST | PDU_CNTL;
6958c2ecf20Sopenharmony_ci		} else {
6968c2ecf20Sopenharmony_ci			p_header->pdu_flag |= PDU_FIRST;
6978c2ecf20Sopenharmony_ci		}
6988c2ecf20Sopenharmony_ci		p_header->pdu_seq = 0;
6998c2ecf20Sopenharmony_ci		memcpy(skb_push(skb, PDU_HEADER_LENGTH), p_header,
7008c2ecf20Sopenharmony_ci		       PDU_HEADER_LENGTH);
7018c2ecf20Sopenharmony_ci
7028c2ecf20Sopenharmony_ci		CTCM_PR_DEBUG("%s(%s): Put on collect_q - skb len: %04x \n"
7038c2ecf20Sopenharmony_ci				"pdu header and data for up to 32 bytes:\n",
7048c2ecf20Sopenharmony_ci				__func__, dev->name, skb->len);
7058c2ecf20Sopenharmony_ci		CTCM_D3_DUMP((char *)skb->data, min_t(int, 32, skb->len));
7068c2ecf20Sopenharmony_ci
7078c2ecf20Sopenharmony_ci		skb_queue_tail(&ch->collect_queue, skb);
7088c2ecf20Sopenharmony_ci		ch->collect_len += skb->len;
7098c2ecf20Sopenharmony_ci		kfree(p_header);
7108c2ecf20Sopenharmony_ci
7118c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&ch->collect_lock, saveflags);
7128c2ecf20Sopenharmony_ci			goto done;
7138c2ecf20Sopenharmony_ci	}
7148c2ecf20Sopenharmony_ci
7158c2ecf20Sopenharmony_ci	/*
7168c2ecf20Sopenharmony_ci	 * Protect skb against beeing free'd by upper
7178c2ecf20Sopenharmony_ci	 * layers.
7188c2ecf20Sopenharmony_ci	 */
7198c2ecf20Sopenharmony_ci	refcount_inc(&skb->users);
7208c2ecf20Sopenharmony_ci
7218c2ecf20Sopenharmony_ci	/*
7228c2ecf20Sopenharmony_ci	 * IDAL support in CTCM is broken, so we have to
7238c2ecf20Sopenharmony_ci	 * care about skb's above 2G ourselves.
7248c2ecf20Sopenharmony_ci	 */
7258c2ecf20Sopenharmony_ci	hi = ((unsigned long)skb->tail + TH_HEADER_LENGTH) >> 31;
7268c2ecf20Sopenharmony_ci	if (hi) {
7278c2ecf20Sopenharmony_ci		nskb = __dev_alloc_skb(skb->len, GFP_ATOMIC | GFP_DMA);
7288c2ecf20Sopenharmony_ci		if (!nskb) {
7298c2ecf20Sopenharmony_ci			goto nomem_exit;
7308c2ecf20Sopenharmony_ci		} else {
7318c2ecf20Sopenharmony_ci			skb_put_data(nskb, skb->data, skb->len);
7328c2ecf20Sopenharmony_ci			refcount_inc(&nskb->users);
7338c2ecf20Sopenharmony_ci			refcount_dec(&skb->users);
7348c2ecf20Sopenharmony_ci			dev_kfree_skb_irq(skb);
7358c2ecf20Sopenharmony_ci			skb = nskb;
7368c2ecf20Sopenharmony_ci		}
7378c2ecf20Sopenharmony_ci	}
7388c2ecf20Sopenharmony_ci
7398c2ecf20Sopenharmony_ci	p_header = kmalloc(PDU_HEADER_LENGTH, gfp_type());
7408c2ecf20Sopenharmony_ci
7418c2ecf20Sopenharmony_ci	if (!p_header)
7428c2ecf20Sopenharmony_ci		goto nomem_exit;
7438c2ecf20Sopenharmony_ci
7448c2ecf20Sopenharmony_ci	p_header->pdu_offset = skb->len;
7458c2ecf20Sopenharmony_ci	p_header->pdu_proto = 0x01;
7468c2ecf20Sopenharmony_ci	p_header->pdu_flag = 0x00;
7478c2ecf20Sopenharmony_ci	p_header->pdu_seq = 0;
7488c2ecf20Sopenharmony_ci	if (be16_to_cpu(skb->protocol) == ETH_P_SNAP) {
7498c2ecf20Sopenharmony_ci		p_header->pdu_flag |= PDU_FIRST | PDU_CNTL;
7508c2ecf20Sopenharmony_ci	} else {
7518c2ecf20Sopenharmony_ci		p_header->pdu_flag |= PDU_FIRST;
7528c2ecf20Sopenharmony_ci	}
7538c2ecf20Sopenharmony_ci	memcpy(skb_push(skb, PDU_HEADER_LENGTH), p_header, PDU_HEADER_LENGTH);
7548c2ecf20Sopenharmony_ci
7558c2ecf20Sopenharmony_ci	kfree(p_header);
7568c2ecf20Sopenharmony_ci
7578c2ecf20Sopenharmony_ci	if (ch->collect_len > 0) {
7588c2ecf20Sopenharmony_ci		spin_lock_irqsave(&ch->collect_lock, saveflags);
7598c2ecf20Sopenharmony_ci		skb_queue_tail(&ch->collect_queue, skb);
7608c2ecf20Sopenharmony_ci		ch->collect_len += skb->len;
7618c2ecf20Sopenharmony_ci		skb = skb_dequeue(&ch->collect_queue);
7628c2ecf20Sopenharmony_ci		ch->collect_len -= skb->len;
7638c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&ch->collect_lock, saveflags);
7648c2ecf20Sopenharmony_ci	}
7658c2ecf20Sopenharmony_ci
7668c2ecf20Sopenharmony_ci	p_header = (struct pdu *)skb->data;
7678c2ecf20Sopenharmony_ci	p_header->pdu_flag |= PDU_LAST;
7688c2ecf20Sopenharmony_ci
7698c2ecf20Sopenharmony_ci	ch->prof.txlen += skb->len - PDU_HEADER_LENGTH;
7708c2ecf20Sopenharmony_ci
7718c2ecf20Sopenharmony_ci	header = kmalloc(TH_HEADER_LENGTH, gfp_type());
7728c2ecf20Sopenharmony_ci	if (!header)
7738c2ecf20Sopenharmony_ci		goto nomem_exit;
7748c2ecf20Sopenharmony_ci
7758c2ecf20Sopenharmony_ci	header->th_seg = 0x00;
7768c2ecf20Sopenharmony_ci	header->th_ch_flag = TH_HAS_PDU;  /* Normal data */
7778c2ecf20Sopenharmony_ci	header->th_blk_flag = 0x00;
7788c2ecf20Sopenharmony_ci	header->th_is_xid = 0x00;          /* Just data here */
7798c2ecf20Sopenharmony_ci	ch->th_seq_num++;
7808c2ecf20Sopenharmony_ci	header->th_seq_num = ch->th_seq_num;
7818c2ecf20Sopenharmony_ci
7828c2ecf20Sopenharmony_ci	CTCM_PR_DBGDATA("%s(%s) ToVTAM_th_seq= %08x\n" ,
7838c2ecf20Sopenharmony_ci		       __func__, dev->name, ch->th_seq_num);
7848c2ecf20Sopenharmony_ci
7858c2ecf20Sopenharmony_ci	/* put the TH on the packet */
7868c2ecf20Sopenharmony_ci	memcpy(skb_push(skb, TH_HEADER_LENGTH), header, TH_HEADER_LENGTH);
7878c2ecf20Sopenharmony_ci
7888c2ecf20Sopenharmony_ci	kfree(header);
7898c2ecf20Sopenharmony_ci
7908c2ecf20Sopenharmony_ci	CTCM_PR_DBGDATA("%s(%s): skb len: %04x\n - pdu header and data for "
7918c2ecf20Sopenharmony_ci			"up to 32 bytes sent to vtam:\n",
7928c2ecf20Sopenharmony_ci				__func__, dev->name, skb->len);
7938c2ecf20Sopenharmony_ci	CTCM_D3_DUMP((char *)skb->data, min_t(int, 32, skb->len));
7948c2ecf20Sopenharmony_ci
7958c2ecf20Sopenharmony_ci	ch->ccw[4].count = skb->len;
7968c2ecf20Sopenharmony_ci	if (set_normalized_cda(&ch->ccw[4], skb->data)) {
7978c2ecf20Sopenharmony_ci		/*
7988c2ecf20Sopenharmony_ci		 * idal allocation failed, try via copying to trans_skb.
7998c2ecf20Sopenharmony_ci		 * trans_skb usually has a pre-allocated idal.
8008c2ecf20Sopenharmony_ci		 */
8018c2ecf20Sopenharmony_ci		if (ctcm_checkalloc_buffer(ch)) {
8028c2ecf20Sopenharmony_ci			/*
8038c2ecf20Sopenharmony_ci			 * Remove our header.
8048c2ecf20Sopenharmony_ci			 * It gets added again on retransmit.
8058c2ecf20Sopenharmony_ci			 */
8068c2ecf20Sopenharmony_ci				goto nomem_exit;
8078c2ecf20Sopenharmony_ci		}
8088c2ecf20Sopenharmony_ci
8098c2ecf20Sopenharmony_ci		skb_reset_tail_pointer(ch->trans_skb);
8108c2ecf20Sopenharmony_ci		ch->trans_skb->len = 0;
8118c2ecf20Sopenharmony_ci		ch->ccw[1].count = skb->len;
8128c2ecf20Sopenharmony_ci		skb_put_data(ch->trans_skb, skb->data, skb->len);
8138c2ecf20Sopenharmony_ci		refcount_dec(&skb->users);
8148c2ecf20Sopenharmony_ci		dev_kfree_skb_irq(skb);
8158c2ecf20Sopenharmony_ci		ccw_idx = 0;
8168c2ecf20Sopenharmony_ci		CTCM_PR_DBGDATA("%s(%s): trans_skb len: %04x\n"
8178c2ecf20Sopenharmony_ci				"up to 32 bytes sent to vtam:\n",
8188c2ecf20Sopenharmony_ci				__func__, dev->name, ch->trans_skb->len);
8198c2ecf20Sopenharmony_ci		CTCM_D3_DUMP((char *)ch->trans_skb->data,
8208c2ecf20Sopenharmony_ci				min_t(int, 32, ch->trans_skb->len));
8218c2ecf20Sopenharmony_ci	} else {
8228c2ecf20Sopenharmony_ci		skb_queue_tail(&ch->io_queue, skb);
8238c2ecf20Sopenharmony_ci		ccw_idx = 3;
8248c2ecf20Sopenharmony_ci	}
8258c2ecf20Sopenharmony_ci	ch->retry = 0;
8268c2ecf20Sopenharmony_ci	fsm_newstate(ch->fsm, CTC_STATE_TX);
8278c2ecf20Sopenharmony_ci	fsm_addtimer(&ch->timer, CTCM_TIME_5_SEC, CTC_EVENT_TIMER, ch);
8288c2ecf20Sopenharmony_ci
8298c2ecf20Sopenharmony_ci	if (do_debug_ccw)
8308c2ecf20Sopenharmony_ci		ctcmpc_dumpit((char *)&ch->ccw[ccw_idx],
8318c2ecf20Sopenharmony_ci					sizeof(struct ccw1) * 3);
8328c2ecf20Sopenharmony_ci
8338c2ecf20Sopenharmony_ci	spin_lock_irqsave(get_ccwdev_lock(ch->cdev), saveflags);
8348c2ecf20Sopenharmony_ci	ch->prof.send_stamp = jiffies;
8358c2ecf20Sopenharmony_ci	rc = ccw_device_start(ch->cdev, &ch->ccw[ccw_idx], 0, 0xff, 0);
8368c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(get_ccwdev_lock(ch->cdev), saveflags);
8378c2ecf20Sopenharmony_ci	if (ccw_idx == 3)
8388c2ecf20Sopenharmony_ci		ch->prof.doios_single++;
8398c2ecf20Sopenharmony_ci	if (rc != 0) {
8408c2ecf20Sopenharmony_ci		fsm_deltimer(&ch->timer);
8418c2ecf20Sopenharmony_ci		ctcm_ccw_check_rc(ch, rc, "single skb TX");
8428c2ecf20Sopenharmony_ci		if (ccw_idx == 3)
8438c2ecf20Sopenharmony_ci			skb_dequeue_tail(&ch->io_queue);
8448c2ecf20Sopenharmony_ci	} else if (ccw_idx == 0) {
8458c2ecf20Sopenharmony_ci		priv->stats.tx_packets++;
8468c2ecf20Sopenharmony_ci		priv->stats.tx_bytes += skb->len - TH_HEADER_LENGTH;
8478c2ecf20Sopenharmony_ci	}
8488c2ecf20Sopenharmony_ci	if (ch->th_seq_num > 0xf0000000)	/* Chose at random. */
8498c2ecf20Sopenharmony_ci		ctcmpc_send_sweep_req(ch);
8508c2ecf20Sopenharmony_ci
8518c2ecf20Sopenharmony_ci	goto done;
8528c2ecf20Sopenharmony_cinomem_exit:
8538c2ecf20Sopenharmony_ci	CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_CRIT,
8548c2ecf20Sopenharmony_ci			"%s(%s): MEMORY allocation ERROR\n",
8558c2ecf20Sopenharmony_ci			CTCM_FUNTAIL, ch->id);
8568c2ecf20Sopenharmony_ci	rc = -ENOMEM;
8578c2ecf20Sopenharmony_ci	refcount_dec(&skb->users);
8588c2ecf20Sopenharmony_ci	dev_kfree_skb_any(skb);
8598c2ecf20Sopenharmony_ci	fsm_event(priv->mpcg->fsm, MPCG_EVENT_INOP, dev);
8608c2ecf20Sopenharmony_cidone:
8618c2ecf20Sopenharmony_ci	CTCM_PR_DEBUG("Exit %s(%s)\n", __func__, dev->name);
8628c2ecf20Sopenharmony_ci	return rc;
8638c2ecf20Sopenharmony_ci}
8648c2ecf20Sopenharmony_ci
8658c2ecf20Sopenharmony_ci/**
8668c2ecf20Sopenharmony_ci * Start transmission of a packet.
8678c2ecf20Sopenharmony_ci * Called from generic network device layer.
8688c2ecf20Sopenharmony_ci */
8698c2ecf20Sopenharmony_ci/* first merge version - leaving both functions separated */
8708c2ecf20Sopenharmony_cistatic netdev_tx_t ctcm_tx(struct sk_buff *skb, struct net_device *dev)
8718c2ecf20Sopenharmony_ci{
8728c2ecf20Sopenharmony_ci	struct ctcm_priv *priv = dev->ml_priv;
8738c2ecf20Sopenharmony_ci
8748c2ecf20Sopenharmony_ci	if (skb == NULL) {
8758c2ecf20Sopenharmony_ci		CTCM_DBF_TEXT_(ERROR, CTC_DBF_ERROR,
8768c2ecf20Sopenharmony_ci				"%s(%s): NULL sk_buff passed",
8778c2ecf20Sopenharmony_ci					CTCM_FUNTAIL, dev->name);
8788c2ecf20Sopenharmony_ci		priv->stats.tx_dropped++;
8798c2ecf20Sopenharmony_ci		return NETDEV_TX_OK;
8808c2ecf20Sopenharmony_ci	}
8818c2ecf20Sopenharmony_ci	if (skb_headroom(skb) < (LL_HEADER_LENGTH + 2)) {
8828c2ecf20Sopenharmony_ci		CTCM_DBF_TEXT_(ERROR, CTC_DBF_ERROR,
8838c2ecf20Sopenharmony_ci			"%s(%s): Got sk_buff with head room < %ld bytes",
8848c2ecf20Sopenharmony_ci			CTCM_FUNTAIL, dev->name, LL_HEADER_LENGTH + 2);
8858c2ecf20Sopenharmony_ci		dev_kfree_skb(skb);
8868c2ecf20Sopenharmony_ci		priv->stats.tx_dropped++;
8878c2ecf20Sopenharmony_ci		return NETDEV_TX_OK;
8888c2ecf20Sopenharmony_ci	}
8898c2ecf20Sopenharmony_ci
8908c2ecf20Sopenharmony_ci	/*
8918c2ecf20Sopenharmony_ci	 * If channels are not running, try to restart them
8928c2ecf20Sopenharmony_ci	 * and throw away packet.
8938c2ecf20Sopenharmony_ci	 */
8948c2ecf20Sopenharmony_ci	if (fsm_getstate(priv->fsm) != DEV_STATE_RUNNING) {
8958c2ecf20Sopenharmony_ci		fsm_event(priv->fsm, DEV_EVENT_START, dev);
8968c2ecf20Sopenharmony_ci		dev_kfree_skb(skb);
8978c2ecf20Sopenharmony_ci		priv->stats.tx_dropped++;
8988c2ecf20Sopenharmony_ci		priv->stats.tx_errors++;
8998c2ecf20Sopenharmony_ci		priv->stats.tx_carrier_errors++;
9008c2ecf20Sopenharmony_ci		return NETDEV_TX_OK;
9018c2ecf20Sopenharmony_ci	}
9028c2ecf20Sopenharmony_ci
9038c2ecf20Sopenharmony_ci	if (ctcm_test_and_set_busy(dev))
9048c2ecf20Sopenharmony_ci		return NETDEV_TX_BUSY;
9058c2ecf20Sopenharmony_ci
9068c2ecf20Sopenharmony_ci	netif_trans_update(dev);
9078c2ecf20Sopenharmony_ci	if (ctcm_transmit_skb(priv->channel[CTCM_WRITE], skb) != 0)
9088c2ecf20Sopenharmony_ci		return NETDEV_TX_BUSY;
9098c2ecf20Sopenharmony_ci	return NETDEV_TX_OK;
9108c2ecf20Sopenharmony_ci}
9118c2ecf20Sopenharmony_ci
9128c2ecf20Sopenharmony_ci/* unmerged MPC variant of ctcm_tx */
9138c2ecf20Sopenharmony_cistatic netdev_tx_t ctcmpc_tx(struct sk_buff *skb, struct net_device *dev)
9148c2ecf20Sopenharmony_ci{
9158c2ecf20Sopenharmony_ci	int len = 0;
9168c2ecf20Sopenharmony_ci	struct ctcm_priv *priv = dev->ml_priv;
9178c2ecf20Sopenharmony_ci	struct mpc_group *grp  = priv->mpcg;
9188c2ecf20Sopenharmony_ci	struct sk_buff *newskb = NULL;
9198c2ecf20Sopenharmony_ci
9208c2ecf20Sopenharmony_ci	/*
9218c2ecf20Sopenharmony_ci	 * Some sanity checks ...
9228c2ecf20Sopenharmony_ci	 */
9238c2ecf20Sopenharmony_ci	if (skb == NULL) {
9248c2ecf20Sopenharmony_ci		CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
9258c2ecf20Sopenharmony_ci			"%s(%s): NULL sk_buff passed",
9268c2ecf20Sopenharmony_ci					CTCM_FUNTAIL, dev->name);
9278c2ecf20Sopenharmony_ci		priv->stats.tx_dropped++;
9288c2ecf20Sopenharmony_ci					goto done;
9298c2ecf20Sopenharmony_ci	}
9308c2ecf20Sopenharmony_ci	if (skb_headroom(skb) < (TH_HEADER_LENGTH + PDU_HEADER_LENGTH)) {
9318c2ecf20Sopenharmony_ci		CTCM_DBF_TEXT_(MPC_TRACE, CTC_DBF_ERROR,
9328c2ecf20Sopenharmony_ci			"%s(%s): Got sk_buff with head room < %ld bytes",
9338c2ecf20Sopenharmony_ci			CTCM_FUNTAIL, dev->name,
9348c2ecf20Sopenharmony_ci				TH_HEADER_LENGTH + PDU_HEADER_LENGTH);
9358c2ecf20Sopenharmony_ci
9368c2ecf20Sopenharmony_ci		CTCM_D3_DUMP((char *)skb->data, min_t(int, 32, skb->len));
9378c2ecf20Sopenharmony_ci
9388c2ecf20Sopenharmony_ci		len =  skb->len + TH_HEADER_LENGTH + PDU_HEADER_LENGTH;
9398c2ecf20Sopenharmony_ci		newskb = __dev_alloc_skb(len, gfp_type() | GFP_DMA);
9408c2ecf20Sopenharmony_ci
9418c2ecf20Sopenharmony_ci		if (!newskb) {
9428c2ecf20Sopenharmony_ci			CTCM_DBF_TEXT_(MPC_TRACE, CTC_DBF_ERROR,
9438c2ecf20Sopenharmony_ci				"%s: %s: __dev_alloc_skb failed",
9448c2ecf20Sopenharmony_ci						__func__, dev->name);
9458c2ecf20Sopenharmony_ci
9468c2ecf20Sopenharmony_ci			dev_kfree_skb_any(skb);
9478c2ecf20Sopenharmony_ci			priv->stats.tx_dropped++;
9488c2ecf20Sopenharmony_ci			priv->stats.tx_errors++;
9498c2ecf20Sopenharmony_ci			priv->stats.tx_carrier_errors++;
9508c2ecf20Sopenharmony_ci			fsm_event(grp->fsm, MPCG_EVENT_INOP, dev);
9518c2ecf20Sopenharmony_ci					goto done;
9528c2ecf20Sopenharmony_ci		}
9538c2ecf20Sopenharmony_ci		newskb->protocol = skb->protocol;
9548c2ecf20Sopenharmony_ci		skb_reserve(newskb, TH_HEADER_LENGTH + PDU_HEADER_LENGTH);
9558c2ecf20Sopenharmony_ci		skb_put_data(newskb, skb->data, skb->len);
9568c2ecf20Sopenharmony_ci		dev_kfree_skb_any(skb);
9578c2ecf20Sopenharmony_ci		skb = newskb;
9588c2ecf20Sopenharmony_ci	}
9598c2ecf20Sopenharmony_ci
9608c2ecf20Sopenharmony_ci	/*
9618c2ecf20Sopenharmony_ci	 * If channels are not running,
9628c2ecf20Sopenharmony_ci	 * notify anybody about a link failure and throw
9638c2ecf20Sopenharmony_ci	 * away packet.
9648c2ecf20Sopenharmony_ci	 */
9658c2ecf20Sopenharmony_ci	if ((fsm_getstate(priv->fsm) != DEV_STATE_RUNNING) ||
9668c2ecf20Sopenharmony_ci	   (fsm_getstate(grp->fsm) <  MPCG_STATE_XID2INITW)) {
9678c2ecf20Sopenharmony_ci		dev_kfree_skb_any(skb);
9688c2ecf20Sopenharmony_ci		CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
9698c2ecf20Sopenharmony_ci			"%s(%s): inactive MPCGROUP - dropped",
9708c2ecf20Sopenharmony_ci					CTCM_FUNTAIL, dev->name);
9718c2ecf20Sopenharmony_ci		priv->stats.tx_dropped++;
9728c2ecf20Sopenharmony_ci		priv->stats.tx_errors++;
9738c2ecf20Sopenharmony_ci		priv->stats.tx_carrier_errors++;
9748c2ecf20Sopenharmony_ci					goto done;
9758c2ecf20Sopenharmony_ci	}
9768c2ecf20Sopenharmony_ci
9778c2ecf20Sopenharmony_ci	if (ctcm_test_and_set_busy(dev)) {
9788c2ecf20Sopenharmony_ci		CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
9798c2ecf20Sopenharmony_ci			"%s(%s): device busy - dropped",
9808c2ecf20Sopenharmony_ci					CTCM_FUNTAIL, dev->name);
9818c2ecf20Sopenharmony_ci		dev_kfree_skb_any(skb);
9828c2ecf20Sopenharmony_ci		priv->stats.tx_dropped++;
9838c2ecf20Sopenharmony_ci		priv->stats.tx_errors++;
9848c2ecf20Sopenharmony_ci		priv->stats.tx_carrier_errors++;
9858c2ecf20Sopenharmony_ci		fsm_event(grp->fsm, MPCG_EVENT_INOP, dev);
9868c2ecf20Sopenharmony_ci					goto done;
9878c2ecf20Sopenharmony_ci	}
9888c2ecf20Sopenharmony_ci
9898c2ecf20Sopenharmony_ci	netif_trans_update(dev);
9908c2ecf20Sopenharmony_ci	if (ctcmpc_transmit_skb(priv->channel[CTCM_WRITE], skb) != 0) {
9918c2ecf20Sopenharmony_ci		CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR,
9928c2ecf20Sopenharmony_ci			"%s(%s): device error - dropped",
9938c2ecf20Sopenharmony_ci					CTCM_FUNTAIL, dev->name);
9948c2ecf20Sopenharmony_ci		dev_kfree_skb_any(skb);
9958c2ecf20Sopenharmony_ci		priv->stats.tx_dropped++;
9968c2ecf20Sopenharmony_ci		priv->stats.tx_errors++;
9978c2ecf20Sopenharmony_ci		priv->stats.tx_carrier_errors++;
9988c2ecf20Sopenharmony_ci		ctcm_clear_busy(dev);
9998c2ecf20Sopenharmony_ci		fsm_event(grp->fsm, MPCG_EVENT_INOP, dev);
10008c2ecf20Sopenharmony_ci					goto done;
10018c2ecf20Sopenharmony_ci	}
10028c2ecf20Sopenharmony_ci	ctcm_clear_busy(dev);
10038c2ecf20Sopenharmony_cidone:
10048c2ecf20Sopenharmony_ci	if (do_debug)
10058c2ecf20Sopenharmony_ci		MPC_DBF_DEV_NAME(TRACE, dev, "exit");
10068c2ecf20Sopenharmony_ci
10078c2ecf20Sopenharmony_ci	return NETDEV_TX_OK;	/* handle freeing of skb here */
10088c2ecf20Sopenharmony_ci}
10098c2ecf20Sopenharmony_ci
10108c2ecf20Sopenharmony_ci
10118c2ecf20Sopenharmony_ci/**
10128c2ecf20Sopenharmony_ci * Sets MTU of an interface.
10138c2ecf20Sopenharmony_ci *
10148c2ecf20Sopenharmony_ci *  dev		Pointer to interface struct.
10158c2ecf20Sopenharmony_ci *  new_mtu	The new MTU to use for this interface.
10168c2ecf20Sopenharmony_ci *
10178c2ecf20Sopenharmony_ci * returns 0 on success, -EINVAL if MTU is out of valid range.
10188c2ecf20Sopenharmony_ci *         (valid range is 576 .. 65527). If VM is on the
10198c2ecf20Sopenharmony_ci *         remote side, maximum MTU is 32760, however this is
10208c2ecf20Sopenharmony_ci *         not checked here.
10218c2ecf20Sopenharmony_ci */
10228c2ecf20Sopenharmony_cistatic int ctcm_change_mtu(struct net_device *dev, int new_mtu)
10238c2ecf20Sopenharmony_ci{
10248c2ecf20Sopenharmony_ci	struct ctcm_priv *priv;
10258c2ecf20Sopenharmony_ci	int max_bufsize;
10268c2ecf20Sopenharmony_ci
10278c2ecf20Sopenharmony_ci	priv = dev->ml_priv;
10288c2ecf20Sopenharmony_ci	max_bufsize = priv->channel[CTCM_READ]->max_bufsize;
10298c2ecf20Sopenharmony_ci
10308c2ecf20Sopenharmony_ci	if (IS_MPC(priv)) {
10318c2ecf20Sopenharmony_ci		if (new_mtu > max_bufsize - TH_HEADER_LENGTH)
10328c2ecf20Sopenharmony_ci			return -EINVAL;
10338c2ecf20Sopenharmony_ci		dev->hard_header_len = TH_HEADER_LENGTH + PDU_HEADER_LENGTH;
10348c2ecf20Sopenharmony_ci	} else {
10358c2ecf20Sopenharmony_ci		if (new_mtu > max_bufsize - LL_HEADER_LENGTH - 2)
10368c2ecf20Sopenharmony_ci			return -EINVAL;
10378c2ecf20Sopenharmony_ci		dev->hard_header_len = LL_HEADER_LENGTH + 2;
10388c2ecf20Sopenharmony_ci	}
10398c2ecf20Sopenharmony_ci	dev->mtu = new_mtu;
10408c2ecf20Sopenharmony_ci	return 0;
10418c2ecf20Sopenharmony_ci}
10428c2ecf20Sopenharmony_ci
10438c2ecf20Sopenharmony_ci/**
10448c2ecf20Sopenharmony_ci * Returns interface statistics of a device.
10458c2ecf20Sopenharmony_ci *
10468c2ecf20Sopenharmony_ci *  dev		Pointer to interface struct.
10478c2ecf20Sopenharmony_ci *
10488c2ecf20Sopenharmony_ci * returns Pointer to stats struct of this interface.
10498c2ecf20Sopenharmony_ci */
10508c2ecf20Sopenharmony_cistatic struct net_device_stats *ctcm_stats(struct net_device *dev)
10518c2ecf20Sopenharmony_ci{
10528c2ecf20Sopenharmony_ci	return &((struct ctcm_priv *)dev->ml_priv)->stats;
10538c2ecf20Sopenharmony_ci}
10548c2ecf20Sopenharmony_ci
10558c2ecf20Sopenharmony_cistatic void ctcm_free_netdevice(struct net_device *dev)
10568c2ecf20Sopenharmony_ci{
10578c2ecf20Sopenharmony_ci	struct ctcm_priv *priv;
10588c2ecf20Sopenharmony_ci	struct mpc_group *grp;
10598c2ecf20Sopenharmony_ci
10608c2ecf20Sopenharmony_ci	CTCM_DBF_TEXT_(SETUP, CTC_DBF_INFO,
10618c2ecf20Sopenharmony_ci			"%s(%s)", CTCM_FUNTAIL, dev->name);
10628c2ecf20Sopenharmony_ci	priv = dev->ml_priv;
10638c2ecf20Sopenharmony_ci	if (priv) {
10648c2ecf20Sopenharmony_ci		grp = priv->mpcg;
10658c2ecf20Sopenharmony_ci		if (grp) {
10668c2ecf20Sopenharmony_ci			if (grp->fsm)
10678c2ecf20Sopenharmony_ci				kfree_fsm(grp->fsm);
10688c2ecf20Sopenharmony_ci			dev_kfree_skb(grp->xid_skb);
10698c2ecf20Sopenharmony_ci			dev_kfree_skb(grp->rcvd_xid_skb);
10708c2ecf20Sopenharmony_ci			tasklet_kill(&grp->mpc_tasklet2);
10718c2ecf20Sopenharmony_ci			kfree(grp);
10728c2ecf20Sopenharmony_ci			priv->mpcg = NULL;
10738c2ecf20Sopenharmony_ci		}
10748c2ecf20Sopenharmony_ci		if (priv->fsm) {
10758c2ecf20Sopenharmony_ci			kfree_fsm(priv->fsm);
10768c2ecf20Sopenharmony_ci			priv->fsm = NULL;
10778c2ecf20Sopenharmony_ci		}
10788c2ecf20Sopenharmony_ci		kfree(priv->xid);
10798c2ecf20Sopenharmony_ci		priv->xid = NULL;
10808c2ecf20Sopenharmony_ci	/*
10818c2ecf20Sopenharmony_ci	 * Note: kfree(priv); is done in "opposite" function of
10828c2ecf20Sopenharmony_ci	 * allocator function probe_device which is remove_device.
10838c2ecf20Sopenharmony_ci	 */
10848c2ecf20Sopenharmony_ci	}
10858c2ecf20Sopenharmony_ci#ifdef MODULE
10868c2ecf20Sopenharmony_ci	free_netdev(dev);
10878c2ecf20Sopenharmony_ci#endif
10888c2ecf20Sopenharmony_ci}
10898c2ecf20Sopenharmony_ci
10908c2ecf20Sopenharmony_cistruct mpc_group *ctcmpc_init_mpc_group(struct ctcm_priv *priv);
10918c2ecf20Sopenharmony_ci
10928c2ecf20Sopenharmony_cistatic const struct net_device_ops ctcm_netdev_ops = {
10938c2ecf20Sopenharmony_ci	.ndo_open		= ctcm_open,
10948c2ecf20Sopenharmony_ci	.ndo_stop		= ctcm_close,
10958c2ecf20Sopenharmony_ci	.ndo_get_stats		= ctcm_stats,
10968c2ecf20Sopenharmony_ci	.ndo_change_mtu	   	= ctcm_change_mtu,
10978c2ecf20Sopenharmony_ci	.ndo_start_xmit		= ctcm_tx,
10988c2ecf20Sopenharmony_ci};
10998c2ecf20Sopenharmony_ci
11008c2ecf20Sopenharmony_cistatic const struct net_device_ops ctcm_mpc_netdev_ops = {
11018c2ecf20Sopenharmony_ci	.ndo_open		= ctcm_open,
11028c2ecf20Sopenharmony_ci	.ndo_stop		= ctcm_close,
11038c2ecf20Sopenharmony_ci	.ndo_get_stats		= ctcm_stats,
11048c2ecf20Sopenharmony_ci	.ndo_change_mtu	   	= ctcm_change_mtu,
11058c2ecf20Sopenharmony_ci	.ndo_start_xmit		= ctcmpc_tx,
11068c2ecf20Sopenharmony_ci};
11078c2ecf20Sopenharmony_ci
11088c2ecf20Sopenharmony_cistatic void ctcm_dev_setup(struct net_device *dev)
11098c2ecf20Sopenharmony_ci{
11108c2ecf20Sopenharmony_ci	dev->type = ARPHRD_SLIP;
11118c2ecf20Sopenharmony_ci	dev->tx_queue_len = 100;
11128c2ecf20Sopenharmony_ci	dev->flags = IFF_POINTOPOINT | IFF_NOARP;
11138c2ecf20Sopenharmony_ci	dev->min_mtu = 576;
11148c2ecf20Sopenharmony_ci	dev->max_mtu = 65527;
11158c2ecf20Sopenharmony_ci}
11168c2ecf20Sopenharmony_ci
11178c2ecf20Sopenharmony_ci/*
11188c2ecf20Sopenharmony_ci * Initialize everything of the net device except the name and the
11198c2ecf20Sopenharmony_ci * channel structs.
11208c2ecf20Sopenharmony_ci */
11218c2ecf20Sopenharmony_cistatic struct net_device *ctcm_init_netdevice(struct ctcm_priv *priv)
11228c2ecf20Sopenharmony_ci{
11238c2ecf20Sopenharmony_ci	struct net_device *dev;
11248c2ecf20Sopenharmony_ci	struct mpc_group *grp;
11258c2ecf20Sopenharmony_ci	if (!priv)
11268c2ecf20Sopenharmony_ci		return NULL;
11278c2ecf20Sopenharmony_ci
11288c2ecf20Sopenharmony_ci	if (IS_MPC(priv))
11298c2ecf20Sopenharmony_ci		dev = alloc_netdev(0, MPC_DEVICE_GENE, NET_NAME_UNKNOWN,
11308c2ecf20Sopenharmony_ci				   ctcm_dev_setup);
11318c2ecf20Sopenharmony_ci	else
11328c2ecf20Sopenharmony_ci		dev = alloc_netdev(0, CTC_DEVICE_GENE, NET_NAME_UNKNOWN,
11338c2ecf20Sopenharmony_ci				   ctcm_dev_setup);
11348c2ecf20Sopenharmony_ci
11358c2ecf20Sopenharmony_ci	if (!dev) {
11368c2ecf20Sopenharmony_ci		CTCM_DBF_TEXT_(ERROR, CTC_DBF_CRIT,
11378c2ecf20Sopenharmony_ci			"%s: MEMORY allocation ERROR",
11388c2ecf20Sopenharmony_ci			CTCM_FUNTAIL);
11398c2ecf20Sopenharmony_ci		return NULL;
11408c2ecf20Sopenharmony_ci	}
11418c2ecf20Sopenharmony_ci	dev->ml_priv = priv;
11428c2ecf20Sopenharmony_ci	priv->fsm = init_fsm("ctcmdev", dev_state_names, dev_event_names,
11438c2ecf20Sopenharmony_ci				CTCM_NR_DEV_STATES, CTCM_NR_DEV_EVENTS,
11448c2ecf20Sopenharmony_ci				dev_fsm, dev_fsm_len, GFP_KERNEL);
11458c2ecf20Sopenharmony_ci	if (priv->fsm == NULL) {
11468c2ecf20Sopenharmony_ci		CTCMY_DBF_DEV(SETUP, dev, "init_fsm error");
11478c2ecf20Sopenharmony_ci		free_netdev(dev);
11488c2ecf20Sopenharmony_ci		return NULL;
11498c2ecf20Sopenharmony_ci	}
11508c2ecf20Sopenharmony_ci	fsm_newstate(priv->fsm, DEV_STATE_STOPPED);
11518c2ecf20Sopenharmony_ci	fsm_settimer(priv->fsm, &priv->restart_timer);
11528c2ecf20Sopenharmony_ci
11538c2ecf20Sopenharmony_ci	if (IS_MPC(priv)) {
11548c2ecf20Sopenharmony_ci		/*  MPC Group Initializations  */
11558c2ecf20Sopenharmony_ci		grp = ctcmpc_init_mpc_group(priv);
11568c2ecf20Sopenharmony_ci		if (grp == NULL) {
11578c2ecf20Sopenharmony_ci			MPC_DBF_DEV(SETUP, dev, "init_mpc_group error");
11588c2ecf20Sopenharmony_ci			free_netdev(dev);
11598c2ecf20Sopenharmony_ci			return NULL;
11608c2ecf20Sopenharmony_ci		}
11618c2ecf20Sopenharmony_ci		tasklet_init(&grp->mpc_tasklet2,
11628c2ecf20Sopenharmony_ci				mpc_group_ready, (unsigned long)dev);
11638c2ecf20Sopenharmony_ci		dev->mtu = MPC_BUFSIZE_DEFAULT -
11648c2ecf20Sopenharmony_ci				TH_HEADER_LENGTH - PDU_HEADER_LENGTH;
11658c2ecf20Sopenharmony_ci
11668c2ecf20Sopenharmony_ci		dev->netdev_ops = &ctcm_mpc_netdev_ops;
11678c2ecf20Sopenharmony_ci		dev->hard_header_len = TH_HEADER_LENGTH + PDU_HEADER_LENGTH;
11688c2ecf20Sopenharmony_ci		priv->buffer_size = MPC_BUFSIZE_DEFAULT;
11698c2ecf20Sopenharmony_ci	} else {
11708c2ecf20Sopenharmony_ci		dev->mtu = CTCM_BUFSIZE_DEFAULT - LL_HEADER_LENGTH - 2;
11718c2ecf20Sopenharmony_ci		dev->netdev_ops = &ctcm_netdev_ops;
11728c2ecf20Sopenharmony_ci		dev->hard_header_len = LL_HEADER_LENGTH + 2;
11738c2ecf20Sopenharmony_ci	}
11748c2ecf20Sopenharmony_ci
11758c2ecf20Sopenharmony_ci	CTCMY_DBF_DEV(SETUP, dev, "finished");
11768c2ecf20Sopenharmony_ci
11778c2ecf20Sopenharmony_ci	return dev;
11788c2ecf20Sopenharmony_ci}
11798c2ecf20Sopenharmony_ci
11808c2ecf20Sopenharmony_ci/**
11818c2ecf20Sopenharmony_ci * Main IRQ handler.
11828c2ecf20Sopenharmony_ci *
11838c2ecf20Sopenharmony_ci *  cdev	The ccw_device the interrupt is for.
11848c2ecf20Sopenharmony_ci *  intparm	interruption parameter.
11858c2ecf20Sopenharmony_ci *  irb		interruption response block.
11868c2ecf20Sopenharmony_ci */
11878c2ecf20Sopenharmony_cistatic void ctcm_irq_handler(struct ccw_device *cdev,
11888c2ecf20Sopenharmony_ci				unsigned long intparm, struct irb *irb)
11898c2ecf20Sopenharmony_ci{
11908c2ecf20Sopenharmony_ci	struct channel		*ch;
11918c2ecf20Sopenharmony_ci	struct net_device	*dev;
11928c2ecf20Sopenharmony_ci	struct ctcm_priv	*priv;
11938c2ecf20Sopenharmony_ci	struct ccwgroup_device	*cgdev;
11948c2ecf20Sopenharmony_ci	int cstat;
11958c2ecf20Sopenharmony_ci	int dstat;
11968c2ecf20Sopenharmony_ci
11978c2ecf20Sopenharmony_ci	CTCM_DBF_TEXT_(TRACE, CTC_DBF_DEBUG,
11988c2ecf20Sopenharmony_ci		"Enter %s(%s)", CTCM_FUNTAIL, dev_name(&cdev->dev));
11998c2ecf20Sopenharmony_ci
12008c2ecf20Sopenharmony_ci	if (ctcm_check_irb_error(cdev, irb))
12018c2ecf20Sopenharmony_ci		return;
12028c2ecf20Sopenharmony_ci
12038c2ecf20Sopenharmony_ci	cgdev = dev_get_drvdata(&cdev->dev);
12048c2ecf20Sopenharmony_ci
12058c2ecf20Sopenharmony_ci	cstat = irb->scsw.cmd.cstat;
12068c2ecf20Sopenharmony_ci	dstat = irb->scsw.cmd.dstat;
12078c2ecf20Sopenharmony_ci
12088c2ecf20Sopenharmony_ci	/* Check for unsolicited interrupts. */
12098c2ecf20Sopenharmony_ci	if (cgdev == NULL) {
12108c2ecf20Sopenharmony_ci		CTCM_DBF_TEXT_(TRACE, CTC_DBF_ERROR,
12118c2ecf20Sopenharmony_ci			"%s(%s) unsolicited irq: c-%02x d-%02x\n",
12128c2ecf20Sopenharmony_ci			CTCM_FUNTAIL, dev_name(&cdev->dev), cstat, dstat);
12138c2ecf20Sopenharmony_ci		dev_warn(&cdev->dev,
12148c2ecf20Sopenharmony_ci			"The adapter received a non-specific IRQ\n");
12158c2ecf20Sopenharmony_ci		return;
12168c2ecf20Sopenharmony_ci	}
12178c2ecf20Sopenharmony_ci
12188c2ecf20Sopenharmony_ci	priv = dev_get_drvdata(&cgdev->dev);
12198c2ecf20Sopenharmony_ci
12208c2ecf20Sopenharmony_ci	/* Try to extract channel from driver data. */
12218c2ecf20Sopenharmony_ci	if (priv->channel[CTCM_READ]->cdev == cdev)
12228c2ecf20Sopenharmony_ci		ch = priv->channel[CTCM_READ];
12238c2ecf20Sopenharmony_ci	else if (priv->channel[CTCM_WRITE]->cdev == cdev)
12248c2ecf20Sopenharmony_ci		ch = priv->channel[CTCM_WRITE];
12258c2ecf20Sopenharmony_ci	else {
12268c2ecf20Sopenharmony_ci		dev_err(&cdev->dev,
12278c2ecf20Sopenharmony_ci			"%s: Internal error: Can't determine channel for "
12288c2ecf20Sopenharmony_ci			"interrupt device %s\n",
12298c2ecf20Sopenharmony_ci			__func__, dev_name(&cdev->dev));
12308c2ecf20Sopenharmony_ci			/* Explain: inconsistent internal structures */
12318c2ecf20Sopenharmony_ci		return;
12328c2ecf20Sopenharmony_ci	}
12338c2ecf20Sopenharmony_ci
12348c2ecf20Sopenharmony_ci	dev = ch->netdev;
12358c2ecf20Sopenharmony_ci	if (dev == NULL) {
12368c2ecf20Sopenharmony_ci		dev_err(&cdev->dev,
12378c2ecf20Sopenharmony_ci			"%s Internal error: net_device is NULL, ch = 0x%p\n",
12388c2ecf20Sopenharmony_ci			__func__, ch);
12398c2ecf20Sopenharmony_ci			/* Explain: inconsistent internal structures */
12408c2ecf20Sopenharmony_ci		return;
12418c2ecf20Sopenharmony_ci	}
12428c2ecf20Sopenharmony_ci
12438c2ecf20Sopenharmony_ci	/* Copy interruption response block. */
12448c2ecf20Sopenharmony_ci	memcpy(ch->irb, irb, sizeof(struct irb));
12458c2ecf20Sopenharmony_ci
12468c2ecf20Sopenharmony_ci	/* Issue error message and return on subchannel error code */
12478c2ecf20Sopenharmony_ci	if (irb->scsw.cmd.cstat) {
12488c2ecf20Sopenharmony_ci		fsm_event(ch->fsm, CTC_EVENT_SC_UNKNOWN, ch);
12498c2ecf20Sopenharmony_ci		CTCM_DBF_TEXT_(TRACE, CTC_DBF_WARN,
12508c2ecf20Sopenharmony_ci			"%s(%s): sub-ch check %s: cs=%02x ds=%02x",
12518c2ecf20Sopenharmony_ci				CTCM_FUNTAIL, dev->name, ch->id, cstat, dstat);
12528c2ecf20Sopenharmony_ci		dev_warn(&cdev->dev,
12538c2ecf20Sopenharmony_ci				"A check occurred on the subchannel\n");
12548c2ecf20Sopenharmony_ci		return;
12558c2ecf20Sopenharmony_ci	}
12568c2ecf20Sopenharmony_ci
12578c2ecf20Sopenharmony_ci	/* Check the reason-code of a unit check */
12588c2ecf20Sopenharmony_ci	if (irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK) {
12598c2ecf20Sopenharmony_ci		if ((irb->ecw[0] & ch->sense_rc) == 0)
12608c2ecf20Sopenharmony_ci			/* print it only once */
12618c2ecf20Sopenharmony_ci			CTCM_DBF_TEXT_(TRACE, CTC_DBF_WARN,
12628c2ecf20Sopenharmony_ci				"%s(%s): sense=%02x, ds=%02x",
12638c2ecf20Sopenharmony_ci				CTCM_FUNTAIL, ch->id, irb->ecw[0], dstat);
12648c2ecf20Sopenharmony_ci		ccw_unit_check(ch, irb->ecw[0]);
12658c2ecf20Sopenharmony_ci		return;
12668c2ecf20Sopenharmony_ci	}
12678c2ecf20Sopenharmony_ci	if (irb->scsw.cmd.dstat & DEV_STAT_BUSY) {
12688c2ecf20Sopenharmony_ci		if (irb->scsw.cmd.dstat & DEV_STAT_ATTENTION)
12698c2ecf20Sopenharmony_ci			fsm_event(ch->fsm, CTC_EVENT_ATTNBUSY, ch);
12708c2ecf20Sopenharmony_ci		else
12718c2ecf20Sopenharmony_ci			fsm_event(ch->fsm, CTC_EVENT_BUSY, ch);
12728c2ecf20Sopenharmony_ci		return;
12738c2ecf20Sopenharmony_ci	}
12748c2ecf20Sopenharmony_ci	if (irb->scsw.cmd.dstat & DEV_STAT_ATTENTION) {
12758c2ecf20Sopenharmony_ci		fsm_event(ch->fsm, CTC_EVENT_ATTN, ch);
12768c2ecf20Sopenharmony_ci		return;
12778c2ecf20Sopenharmony_ci	}
12788c2ecf20Sopenharmony_ci	if ((irb->scsw.cmd.stctl & SCSW_STCTL_SEC_STATUS) ||
12798c2ecf20Sopenharmony_ci	    (irb->scsw.cmd.stctl == SCSW_STCTL_STATUS_PEND) ||
12808c2ecf20Sopenharmony_ci	    (irb->scsw.cmd.stctl ==
12818c2ecf20Sopenharmony_ci	     (SCSW_STCTL_ALERT_STATUS | SCSW_STCTL_STATUS_PEND)))
12828c2ecf20Sopenharmony_ci		fsm_event(ch->fsm, CTC_EVENT_FINSTAT, ch);
12838c2ecf20Sopenharmony_ci	else
12848c2ecf20Sopenharmony_ci		fsm_event(ch->fsm, CTC_EVENT_IRQ, ch);
12858c2ecf20Sopenharmony_ci
12868c2ecf20Sopenharmony_ci}
12878c2ecf20Sopenharmony_ci
12888c2ecf20Sopenharmony_cistatic const struct device_type ctcm_devtype = {
12898c2ecf20Sopenharmony_ci	.name = "ctcm",
12908c2ecf20Sopenharmony_ci	.groups = ctcm_attr_groups,
12918c2ecf20Sopenharmony_ci};
12928c2ecf20Sopenharmony_ci
12938c2ecf20Sopenharmony_ci/**
12948c2ecf20Sopenharmony_ci * Add ctcm specific attributes.
12958c2ecf20Sopenharmony_ci * Add ctcm private data.
12968c2ecf20Sopenharmony_ci *
12978c2ecf20Sopenharmony_ci *  cgdev	pointer to ccwgroup_device just added
12988c2ecf20Sopenharmony_ci *
12998c2ecf20Sopenharmony_ci * returns 0 on success, !0 on failure.
13008c2ecf20Sopenharmony_ci */
13018c2ecf20Sopenharmony_cistatic int ctcm_probe_device(struct ccwgroup_device *cgdev)
13028c2ecf20Sopenharmony_ci{
13038c2ecf20Sopenharmony_ci	struct ctcm_priv *priv;
13048c2ecf20Sopenharmony_ci
13058c2ecf20Sopenharmony_ci	CTCM_DBF_TEXT_(SETUP, CTC_DBF_INFO,
13068c2ecf20Sopenharmony_ci			"%s %p",
13078c2ecf20Sopenharmony_ci			__func__, cgdev);
13088c2ecf20Sopenharmony_ci
13098c2ecf20Sopenharmony_ci	if (!get_device(&cgdev->dev))
13108c2ecf20Sopenharmony_ci		return -ENODEV;
13118c2ecf20Sopenharmony_ci
13128c2ecf20Sopenharmony_ci	priv = kzalloc(sizeof(struct ctcm_priv), GFP_KERNEL);
13138c2ecf20Sopenharmony_ci	if (!priv) {
13148c2ecf20Sopenharmony_ci		CTCM_DBF_TEXT_(ERROR, CTC_DBF_ERROR,
13158c2ecf20Sopenharmony_ci			"%s: memory allocation failure",
13168c2ecf20Sopenharmony_ci			CTCM_FUNTAIL);
13178c2ecf20Sopenharmony_ci		put_device(&cgdev->dev);
13188c2ecf20Sopenharmony_ci		return -ENOMEM;
13198c2ecf20Sopenharmony_ci	}
13208c2ecf20Sopenharmony_ci	priv->buffer_size = CTCM_BUFSIZE_DEFAULT;
13218c2ecf20Sopenharmony_ci	cgdev->cdev[0]->handler = ctcm_irq_handler;
13228c2ecf20Sopenharmony_ci	cgdev->cdev[1]->handler = ctcm_irq_handler;
13238c2ecf20Sopenharmony_ci	dev_set_drvdata(&cgdev->dev, priv);
13248c2ecf20Sopenharmony_ci	cgdev->dev.type = &ctcm_devtype;
13258c2ecf20Sopenharmony_ci
13268c2ecf20Sopenharmony_ci	return 0;
13278c2ecf20Sopenharmony_ci}
13288c2ecf20Sopenharmony_ci
13298c2ecf20Sopenharmony_ci/**
13308c2ecf20Sopenharmony_ci * Add a new channel to the list of channels.
13318c2ecf20Sopenharmony_ci * Keeps the channel list sorted.
13328c2ecf20Sopenharmony_ci *
13338c2ecf20Sopenharmony_ci *  cdev	The ccw_device to be added.
13348c2ecf20Sopenharmony_ci *  type	The type class of the new channel.
13358c2ecf20Sopenharmony_ci *  priv	Points to the private data of the ccwgroup_device.
13368c2ecf20Sopenharmony_ci *
13378c2ecf20Sopenharmony_ci * returns 0 on success, !0 on error.
13388c2ecf20Sopenharmony_ci */
13398c2ecf20Sopenharmony_cistatic int add_channel(struct ccw_device *cdev, enum ctcm_channel_types type,
13408c2ecf20Sopenharmony_ci				struct ctcm_priv *priv)
13418c2ecf20Sopenharmony_ci{
13428c2ecf20Sopenharmony_ci	struct channel **c = &channels;
13438c2ecf20Sopenharmony_ci	struct channel *ch;
13448c2ecf20Sopenharmony_ci	int ccw_num;
13458c2ecf20Sopenharmony_ci	int rc = 0;
13468c2ecf20Sopenharmony_ci
13478c2ecf20Sopenharmony_ci	CTCM_DBF_TEXT_(SETUP, CTC_DBF_INFO,
13488c2ecf20Sopenharmony_ci		"%s(%s), type %d, proto %d",
13498c2ecf20Sopenharmony_ci			__func__, dev_name(&cdev->dev),	type, priv->protocol);
13508c2ecf20Sopenharmony_ci
13518c2ecf20Sopenharmony_ci	ch = kzalloc(sizeof(struct channel), GFP_KERNEL);
13528c2ecf20Sopenharmony_ci	if (ch == NULL)
13538c2ecf20Sopenharmony_ci		return -ENOMEM;
13548c2ecf20Sopenharmony_ci
13558c2ecf20Sopenharmony_ci	ch->protocol = priv->protocol;
13568c2ecf20Sopenharmony_ci	if (IS_MPC(priv)) {
13578c2ecf20Sopenharmony_ci		ch->discontact_th = kzalloc(TH_HEADER_LENGTH, gfp_type());
13588c2ecf20Sopenharmony_ci		if (ch->discontact_th == NULL)
13598c2ecf20Sopenharmony_ci					goto nomem_return;
13608c2ecf20Sopenharmony_ci
13618c2ecf20Sopenharmony_ci		ch->discontact_th->th_blk_flag = TH_DISCONTACT;
13628c2ecf20Sopenharmony_ci		tasklet_init(&ch->ch_disc_tasklet,
13638c2ecf20Sopenharmony_ci			mpc_action_send_discontact, (unsigned long)ch);
13648c2ecf20Sopenharmony_ci
13658c2ecf20Sopenharmony_ci		tasklet_init(&ch->ch_tasklet, ctcmpc_bh, (unsigned long)ch);
13668c2ecf20Sopenharmony_ci		ch->max_bufsize = (MPC_BUFSIZE_DEFAULT - 35);
13678c2ecf20Sopenharmony_ci		ccw_num = 17;
13688c2ecf20Sopenharmony_ci	} else
13698c2ecf20Sopenharmony_ci		ccw_num = 8;
13708c2ecf20Sopenharmony_ci
13718c2ecf20Sopenharmony_ci	ch->ccw = kcalloc(ccw_num, sizeof(struct ccw1), GFP_KERNEL | GFP_DMA);
13728c2ecf20Sopenharmony_ci	if (ch->ccw == NULL)
13738c2ecf20Sopenharmony_ci					goto nomem_return;
13748c2ecf20Sopenharmony_ci
13758c2ecf20Sopenharmony_ci	ch->cdev = cdev;
13768c2ecf20Sopenharmony_ci	snprintf(ch->id, CTCM_ID_SIZE, "ch-%s", dev_name(&cdev->dev));
13778c2ecf20Sopenharmony_ci	ch->type = type;
13788c2ecf20Sopenharmony_ci
13798c2ecf20Sopenharmony_ci	/**
13808c2ecf20Sopenharmony_ci	 * "static" ccws are used in the following way:
13818c2ecf20Sopenharmony_ci	 *
13828c2ecf20Sopenharmony_ci	 * ccw[0..2] (Channel program for generic I/O):
13838c2ecf20Sopenharmony_ci	 *           0: prepare
13848c2ecf20Sopenharmony_ci	 *           1: read or write (depending on direction) with fixed
13858c2ecf20Sopenharmony_ci	 *              buffer (idal allocated once when buffer is allocated)
13868c2ecf20Sopenharmony_ci	 *           2: nop
13878c2ecf20Sopenharmony_ci	 * ccw[3..5] (Channel program for direct write of packets)
13888c2ecf20Sopenharmony_ci	 *           3: prepare
13898c2ecf20Sopenharmony_ci	 *           4: write (idal allocated on every write).
13908c2ecf20Sopenharmony_ci	 *           5: nop
13918c2ecf20Sopenharmony_ci	 * ccw[6..7] (Channel program for initial channel setup):
13928c2ecf20Sopenharmony_ci	 *           6: set extended mode
13938c2ecf20Sopenharmony_ci	 *           7: nop
13948c2ecf20Sopenharmony_ci	 *
13958c2ecf20Sopenharmony_ci	 * ch->ccw[0..5] are initialized in ch_action_start because
13968c2ecf20Sopenharmony_ci	 * the channel's direction is yet unknown here.
13978c2ecf20Sopenharmony_ci	 *
13988c2ecf20Sopenharmony_ci	 * ccws used for xid2 negotiations
13998c2ecf20Sopenharmony_ci	 *  ch-ccw[8-14] need to be used for the XID exchange either
14008c2ecf20Sopenharmony_ci	 *    X side XID2 Processing
14018c2ecf20Sopenharmony_ci	 *       8:  write control
14028c2ecf20Sopenharmony_ci	 *       9:  write th
14038c2ecf20Sopenharmony_ci	 *	     10: write XID
14048c2ecf20Sopenharmony_ci	 *	     11: read th from secondary
14058c2ecf20Sopenharmony_ci	 *	     12: read XID   from secondary
14068c2ecf20Sopenharmony_ci	 *	     13: read 4 byte ID
14078c2ecf20Sopenharmony_ci	 *	     14: nop
14088c2ecf20Sopenharmony_ci	 *    Y side XID Processing
14098c2ecf20Sopenharmony_ci	 *	     8:  sense
14108c2ecf20Sopenharmony_ci	 *       9:  read th
14118c2ecf20Sopenharmony_ci	 *	     10: read XID
14128c2ecf20Sopenharmony_ci	 *	     11: write th
14138c2ecf20Sopenharmony_ci	 *	     12: write XID
14148c2ecf20Sopenharmony_ci	 *	     13: write 4 byte ID
14158c2ecf20Sopenharmony_ci	 *	     14: nop
14168c2ecf20Sopenharmony_ci	 *
14178c2ecf20Sopenharmony_ci	 *  ccws used for double noop due to VM timing issues
14188c2ecf20Sopenharmony_ci	 *  which result in unrecoverable Busy on channel
14198c2ecf20Sopenharmony_ci	 *       15: nop
14208c2ecf20Sopenharmony_ci	 *       16: nop
14218c2ecf20Sopenharmony_ci	 */
14228c2ecf20Sopenharmony_ci	ch->ccw[6].cmd_code	= CCW_CMD_SET_EXTENDED;
14238c2ecf20Sopenharmony_ci	ch->ccw[6].flags	= CCW_FLAG_SLI;
14248c2ecf20Sopenharmony_ci
14258c2ecf20Sopenharmony_ci	ch->ccw[7].cmd_code	= CCW_CMD_NOOP;
14268c2ecf20Sopenharmony_ci	ch->ccw[7].flags	= CCW_FLAG_SLI;
14278c2ecf20Sopenharmony_ci
14288c2ecf20Sopenharmony_ci	if (IS_MPC(priv)) {
14298c2ecf20Sopenharmony_ci		ch->ccw[15].cmd_code = CCW_CMD_WRITE;
14308c2ecf20Sopenharmony_ci		ch->ccw[15].flags    = CCW_FLAG_SLI | CCW_FLAG_CC;
14318c2ecf20Sopenharmony_ci		ch->ccw[15].count    = TH_HEADER_LENGTH;
14328c2ecf20Sopenharmony_ci		ch->ccw[15].cda      = virt_to_phys(ch->discontact_th);
14338c2ecf20Sopenharmony_ci
14348c2ecf20Sopenharmony_ci		ch->ccw[16].cmd_code = CCW_CMD_NOOP;
14358c2ecf20Sopenharmony_ci		ch->ccw[16].flags    = CCW_FLAG_SLI;
14368c2ecf20Sopenharmony_ci
14378c2ecf20Sopenharmony_ci		ch->fsm = init_fsm(ch->id, ctc_ch_state_names,
14388c2ecf20Sopenharmony_ci				ctc_ch_event_names, CTC_MPC_NR_STATES,
14398c2ecf20Sopenharmony_ci				CTC_MPC_NR_EVENTS, ctcmpc_ch_fsm,
14408c2ecf20Sopenharmony_ci				mpc_ch_fsm_len, GFP_KERNEL);
14418c2ecf20Sopenharmony_ci	} else {
14428c2ecf20Sopenharmony_ci		ch->fsm = init_fsm(ch->id, ctc_ch_state_names,
14438c2ecf20Sopenharmony_ci				ctc_ch_event_names, CTC_NR_STATES,
14448c2ecf20Sopenharmony_ci				CTC_NR_EVENTS, ch_fsm,
14458c2ecf20Sopenharmony_ci				ch_fsm_len, GFP_KERNEL);
14468c2ecf20Sopenharmony_ci	}
14478c2ecf20Sopenharmony_ci	if (ch->fsm == NULL)
14488c2ecf20Sopenharmony_ci				goto nomem_return;
14498c2ecf20Sopenharmony_ci
14508c2ecf20Sopenharmony_ci	fsm_newstate(ch->fsm, CTC_STATE_IDLE);
14518c2ecf20Sopenharmony_ci
14528c2ecf20Sopenharmony_ci	ch->irb = kzalloc(sizeof(struct irb), GFP_KERNEL);
14538c2ecf20Sopenharmony_ci	if (ch->irb == NULL)
14548c2ecf20Sopenharmony_ci				goto nomem_return;
14558c2ecf20Sopenharmony_ci
14568c2ecf20Sopenharmony_ci	while (*c && ctcm_less_than((*c)->id, ch->id))
14578c2ecf20Sopenharmony_ci		c = &(*c)->next;
14588c2ecf20Sopenharmony_ci
14598c2ecf20Sopenharmony_ci	if (*c && (!strncmp((*c)->id, ch->id, CTCM_ID_SIZE))) {
14608c2ecf20Sopenharmony_ci		CTCM_DBF_TEXT_(SETUP, CTC_DBF_INFO,
14618c2ecf20Sopenharmony_ci				"%s (%s) already in list, using old entry",
14628c2ecf20Sopenharmony_ci				__func__, (*c)->id);
14638c2ecf20Sopenharmony_ci
14648c2ecf20Sopenharmony_ci				goto free_return;
14658c2ecf20Sopenharmony_ci	}
14668c2ecf20Sopenharmony_ci
14678c2ecf20Sopenharmony_ci	spin_lock_init(&ch->collect_lock);
14688c2ecf20Sopenharmony_ci
14698c2ecf20Sopenharmony_ci	fsm_settimer(ch->fsm, &ch->timer);
14708c2ecf20Sopenharmony_ci	skb_queue_head_init(&ch->io_queue);
14718c2ecf20Sopenharmony_ci	skb_queue_head_init(&ch->collect_queue);
14728c2ecf20Sopenharmony_ci
14738c2ecf20Sopenharmony_ci	if (IS_MPC(priv)) {
14748c2ecf20Sopenharmony_ci		fsm_settimer(ch->fsm, &ch->sweep_timer);
14758c2ecf20Sopenharmony_ci		skb_queue_head_init(&ch->sweep_queue);
14768c2ecf20Sopenharmony_ci	}
14778c2ecf20Sopenharmony_ci	ch->next = *c;
14788c2ecf20Sopenharmony_ci	*c = ch;
14798c2ecf20Sopenharmony_ci	return 0;
14808c2ecf20Sopenharmony_ci
14818c2ecf20Sopenharmony_cinomem_return:
14828c2ecf20Sopenharmony_ci	rc = -ENOMEM;
14838c2ecf20Sopenharmony_ci
14848c2ecf20Sopenharmony_cifree_return:	/* note that all channel pointers are 0 or valid */
14858c2ecf20Sopenharmony_ci	kfree(ch->ccw);
14868c2ecf20Sopenharmony_ci	kfree(ch->discontact_th);
14878c2ecf20Sopenharmony_ci	kfree_fsm(ch->fsm);
14888c2ecf20Sopenharmony_ci	kfree(ch->irb);
14898c2ecf20Sopenharmony_ci	kfree(ch);
14908c2ecf20Sopenharmony_ci	return rc;
14918c2ecf20Sopenharmony_ci}
14928c2ecf20Sopenharmony_ci
14938c2ecf20Sopenharmony_ci/*
14948c2ecf20Sopenharmony_ci * Return type of a detected device.
14958c2ecf20Sopenharmony_ci */
14968c2ecf20Sopenharmony_cistatic enum ctcm_channel_types get_channel_type(struct ccw_device_id *id)
14978c2ecf20Sopenharmony_ci{
14988c2ecf20Sopenharmony_ci	enum ctcm_channel_types type;
14998c2ecf20Sopenharmony_ci	type = (enum ctcm_channel_types)id->driver_info;
15008c2ecf20Sopenharmony_ci
15018c2ecf20Sopenharmony_ci	if (type == ctcm_channel_type_ficon)
15028c2ecf20Sopenharmony_ci		type = ctcm_channel_type_escon;
15038c2ecf20Sopenharmony_ci
15048c2ecf20Sopenharmony_ci	return type;
15058c2ecf20Sopenharmony_ci}
15068c2ecf20Sopenharmony_ci
15078c2ecf20Sopenharmony_ci/**
15088c2ecf20Sopenharmony_ci *
15098c2ecf20Sopenharmony_ci * Setup an interface.
15108c2ecf20Sopenharmony_ci *
15118c2ecf20Sopenharmony_ci *  cgdev	Device to be setup.
15128c2ecf20Sopenharmony_ci *
15138c2ecf20Sopenharmony_ci * returns 0 on success, !0 on failure.
15148c2ecf20Sopenharmony_ci */
15158c2ecf20Sopenharmony_cistatic int ctcm_new_device(struct ccwgroup_device *cgdev)
15168c2ecf20Sopenharmony_ci{
15178c2ecf20Sopenharmony_ci	char read_id[CTCM_ID_SIZE];
15188c2ecf20Sopenharmony_ci	char write_id[CTCM_ID_SIZE];
15198c2ecf20Sopenharmony_ci	int direction;
15208c2ecf20Sopenharmony_ci	enum ctcm_channel_types type;
15218c2ecf20Sopenharmony_ci	struct ctcm_priv *priv;
15228c2ecf20Sopenharmony_ci	struct net_device *dev;
15238c2ecf20Sopenharmony_ci	struct ccw_device *cdev0;
15248c2ecf20Sopenharmony_ci	struct ccw_device *cdev1;
15258c2ecf20Sopenharmony_ci	struct channel *readc;
15268c2ecf20Sopenharmony_ci	struct channel *writec;
15278c2ecf20Sopenharmony_ci	int ret;
15288c2ecf20Sopenharmony_ci	int result;
15298c2ecf20Sopenharmony_ci
15308c2ecf20Sopenharmony_ci	priv = dev_get_drvdata(&cgdev->dev);
15318c2ecf20Sopenharmony_ci	if (!priv) {
15328c2ecf20Sopenharmony_ci		result = -ENODEV;
15338c2ecf20Sopenharmony_ci		goto out_err_result;
15348c2ecf20Sopenharmony_ci	}
15358c2ecf20Sopenharmony_ci
15368c2ecf20Sopenharmony_ci	cdev0 = cgdev->cdev[0];
15378c2ecf20Sopenharmony_ci	cdev1 = cgdev->cdev[1];
15388c2ecf20Sopenharmony_ci
15398c2ecf20Sopenharmony_ci	type = get_channel_type(&cdev0->id);
15408c2ecf20Sopenharmony_ci
15418c2ecf20Sopenharmony_ci	snprintf(read_id, CTCM_ID_SIZE, "ch-%s", dev_name(&cdev0->dev));
15428c2ecf20Sopenharmony_ci	snprintf(write_id, CTCM_ID_SIZE, "ch-%s", dev_name(&cdev1->dev));
15438c2ecf20Sopenharmony_ci
15448c2ecf20Sopenharmony_ci	ret = add_channel(cdev0, type, priv);
15458c2ecf20Sopenharmony_ci	if (ret) {
15468c2ecf20Sopenharmony_ci		result = ret;
15478c2ecf20Sopenharmony_ci		goto out_err_result;
15488c2ecf20Sopenharmony_ci	}
15498c2ecf20Sopenharmony_ci	ret = add_channel(cdev1, type, priv);
15508c2ecf20Sopenharmony_ci	if (ret) {
15518c2ecf20Sopenharmony_ci		result = ret;
15528c2ecf20Sopenharmony_ci		goto out_remove_channel1;
15538c2ecf20Sopenharmony_ci	}
15548c2ecf20Sopenharmony_ci
15558c2ecf20Sopenharmony_ci	ret = ccw_device_set_online(cdev0);
15568c2ecf20Sopenharmony_ci	if (ret != 0) {
15578c2ecf20Sopenharmony_ci		CTCM_DBF_TEXT_(TRACE, CTC_DBF_NOTICE,
15588c2ecf20Sopenharmony_ci			"%s(%s) set_online rc=%d",
15598c2ecf20Sopenharmony_ci				CTCM_FUNTAIL, read_id, ret);
15608c2ecf20Sopenharmony_ci		result = -EIO;
15618c2ecf20Sopenharmony_ci		goto out_remove_channel2;
15628c2ecf20Sopenharmony_ci	}
15638c2ecf20Sopenharmony_ci
15648c2ecf20Sopenharmony_ci	ret = ccw_device_set_online(cdev1);
15658c2ecf20Sopenharmony_ci	if (ret != 0) {
15668c2ecf20Sopenharmony_ci		CTCM_DBF_TEXT_(TRACE, CTC_DBF_NOTICE,
15678c2ecf20Sopenharmony_ci			"%s(%s) set_online rc=%d",
15688c2ecf20Sopenharmony_ci				CTCM_FUNTAIL, write_id, ret);
15698c2ecf20Sopenharmony_ci
15708c2ecf20Sopenharmony_ci		result = -EIO;
15718c2ecf20Sopenharmony_ci		goto out_ccw1;
15728c2ecf20Sopenharmony_ci	}
15738c2ecf20Sopenharmony_ci
15748c2ecf20Sopenharmony_ci	dev = ctcm_init_netdevice(priv);
15758c2ecf20Sopenharmony_ci	if (dev == NULL) {
15768c2ecf20Sopenharmony_ci		result = -ENODEV;
15778c2ecf20Sopenharmony_ci		goto out_ccw2;
15788c2ecf20Sopenharmony_ci	}
15798c2ecf20Sopenharmony_ci
15808c2ecf20Sopenharmony_ci	for (direction = CTCM_READ; direction <= CTCM_WRITE; direction++) {
15818c2ecf20Sopenharmony_ci		priv->channel[direction] =
15828c2ecf20Sopenharmony_ci			channel_get(type, direction == CTCM_READ ?
15838c2ecf20Sopenharmony_ci				read_id : write_id, direction);
15848c2ecf20Sopenharmony_ci		if (priv->channel[direction] == NULL) {
15858c2ecf20Sopenharmony_ci			if (direction == CTCM_WRITE)
15868c2ecf20Sopenharmony_ci				channel_free(priv->channel[CTCM_READ]);
15878c2ecf20Sopenharmony_ci			result = -ENODEV;
15888c2ecf20Sopenharmony_ci			goto out_dev;
15898c2ecf20Sopenharmony_ci		}
15908c2ecf20Sopenharmony_ci		priv->channel[direction]->netdev = dev;
15918c2ecf20Sopenharmony_ci		priv->channel[direction]->protocol = priv->protocol;
15928c2ecf20Sopenharmony_ci		priv->channel[direction]->max_bufsize = priv->buffer_size;
15938c2ecf20Sopenharmony_ci	}
15948c2ecf20Sopenharmony_ci	/* sysfs magic */
15958c2ecf20Sopenharmony_ci	SET_NETDEV_DEV(dev, &cgdev->dev);
15968c2ecf20Sopenharmony_ci
15978c2ecf20Sopenharmony_ci	if (register_netdev(dev)) {
15988c2ecf20Sopenharmony_ci		result = -ENODEV;
15998c2ecf20Sopenharmony_ci		goto out_dev;
16008c2ecf20Sopenharmony_ci	}
16018c2ecf20Sopenharmony_ci
16028c2ecf20Sopenharmony_ci	strlcpy(priv->fsm->name, dev->name, sizeof(priv->fsm->name));
16038c2ecf20Sopenharmony_ci
16048c2ecf20Sopenharmony_ci	dev_info(&dev->dev,
16058c2ecf20Sopenharmony_ci		"setup OK : r/w = %s/%s, protocol : %d\n",
16068c2ecf20Sopenharmony_ci			priv->channel[CTCM_READ]->id,
16078c2ecf20Sopenharmony_ci			priv->channel[CTCM_WRITE]->id, priv->protocol);
16088c2ecf20Sopenharmony_ci
16098c2ecf20Sopenharmony_ci	CTCM_DBF_TEXT_(SETUP, CTC_DBF_INFO,
16108c2ecf20Sopenharmony_ci		"setup(%s) OK : r/w = %s/%s, protocol : %d", dev->name,
16118c2ecf20Sopenharmony_ci			priv->channel[CTCM_READ]->id,
16128c2ecf20Sopenharmony_ci			priv->channel[CTCM_WRITE]->id, priv->protocol);
16138c2ecf20Sopenharmony_ci
16148c2ecf20Sopenharmony_ci	return 0;
16158c2ecf20Sopenharmony_ciout_dev:
16168c2ecf20Sopenharmony_ci	ctcm_free_netdevice(dev);
16178c2ecf20Sopenharmony_ciout_ccw2:
16188c2ecf20Sopenharmony_ci	ccw_device_set_offline(cgdev->cdev[1]);
16198c2ecf20Sopenharmony_ciout_ccw1:
16208c2ecf20Sopenharmony_ci	ccw_device_set_offline(cgdev->cdev[0]);
16218c2ecf20Sopenharmony_ciout_remove_channel2:
16228c2ecf20Sopenharmony_ci	readc = channel_get(type, read_id, CTCM_READ);
16238c2ecf20Sopenharmony_ci	channel_remove(readc);
16248c2ecf20Sopenharmony_ciout_remove_channel1:
16258c2ecf20Sopenharmony_ci	writec = channel_get(type, write_id, CTCM_WRITE);
16268c2ecf20Sopenharmony_ci	channel_remove(writec);
16278c2ecf20Sopenharmony_ciout_err_result:
16288c2ecf20Sopenharmony_ci	return result;
16298c2ecf20Sopenharmony_ci}
16308c2ecf20Sopenharmony_ci
16318c2ecf20Sopenharmony_ci/**
16328c2ecf20Sopenharmony_ci * Shutdown an interface.
16338c2ecf20Sopenharmony_ci *
16348c2ecf20Sopenharmony_ci *  cgdev	Device to be shut down.
16358c2ecf20Sopenharmony_ci *
16368c2ecf20Sopenharmony_ci * returns 0 on success, !0 on failure.
16378c2ecf20Sopenharmony_ci */
16388c2ecf20Sopenharmony_cistatic int ctcm_shutdown_device(struct ccwgroup_device *cgdev)
16398c2ecf20Sopenharmony_ci{
16408c2ecf20Sopenharmony_ci	struct ctcm_priv *priv;
16418c2ecf20Sopenharmony_ci	struct net_device *dev;
16428c2ecf20Sopenharmony_ci
16438c2ecf20Sopenharmony_ci	priv = dev_get_drvdata(&cgdev->dev);
16448c2ecf20Sopenharmony_ci	if (!priv)
16458c2ecf20Sopenharmony_ci		return -ENODEV;
16468c2ecf20Sopenharmony_ci
16478c2ecf20Sopenharmony_ci	if (priv->channel[CTCM_READ]) {
16488c2ecf20Sopenharmony_ci		dev = priv->channel[CTCM_READ]->netdev;
16498c2ecf20Sopenharmony_ci		CTCM_DBF_DEV(SETUP, dev, "");
16508c2ecf20Sopenharmony_ci		/* Close the device */
16518c2ecf20Sopenharmony_ci		ctcm_close(dev);
16528c2ecf20Sopenharmony_ci		dev->flags &= ~IFF_RUNNING;
16538c2ecf20Sopenharmony_ci		channel_free(priv->channel[CTCM_READ]);
16548c2ecf20Sopenharmony_ci	} else
16558c2ecf20Sopenharmony_ci		dev = NULL;
16568c2ecf20Sopenharmony_ci
16578c2ecf20Sopenharmony_ci	if (priv->channel[CTCM_WRITE])
16588c2ecf20Sopenharmony_ci		channel_free(priv->channel[CTCM_WRITE]);
16598c2ecf20Sopenharmony_ci
16608c2ecf20Sopenharmony_ci	if (dev) {
16618c2ecf20Sopenharmony_ci		unregister_netdev(dev);
16628c2ecf20Sopenharmony_ci		ctcm_free_netdevice(dev);
16638c2ecf20Sopenharmony_ci	}
16648c2ecf20Sopenharmony_ci
16658c2ecf20Sopenharmony_ci	if (priv->fsm)
16668c2ecf20Sopenharmony_ci		kfree_fsm(priv->fsm);
16678c2ecf20Sopenharmony_ci
16688c2ecf20Sopenharmony_ci	ccw_device_set_offline(cgdev->cdev[1]);
16698c2ecf20Sopenharmony_ci	ccw_device_set_offline(cgdev->cdev[0]);
16708c2ecf20Sopenharmony_ci	channel_remove(priv->channel[CTCM_READ]);
16718c2ecf20Sopenharmony_ci	channel_remove(priv->channel[CTCM_WRITE]);
16728c2ecf20Sopenharmony_ci	priv->channel[CTCM_READ] = priv->channel[CTCM_WRITE] = NULL;
16738c2ecf20Sopenharmony_ci
16748c2ecf20Sopenharmony_ci	return 0;
16758c2ecf20Sopenharmony_ci
16768c2ecf20Sopenharmony_ci}
16778c2ecf20Sopenharmony_ci
16788c2ecf20Sopenharmony_ci
16798c2ecf20Sopenharmony_cistatic void ctcm_remove_device(struct ccwgroup_device *cgdev)
16808c2ecf20Sopenharmony_ci{
16818c2ecf20Sopenharmony_ci	struct ctcm_priv *priv = dev_get_drvdata(&cgdev->dev);
16828c2ecf20Sopenharmony_ci
16838c2ecf20Sopenharmony_ci	CTCM_DBF_TEXT_(SETUP, CTC_DBF_INFO,
16848c2ecf20Sopenharmony_ci			"removing device %p, proto : %d",
16858c2ecf20Sopenharmony_ci			cgdev, priv->protocol);
16868c2ecf20Sopenharmony_ci
16878c2ecf20Sopenharmony_ci	if (cgdev->state == CCWGROUP_ONLINE)
16888c2ecf20Sopenharmony_ci		ctcm_shutdown_device(cgdev);
16898c2ecf20Sopenharmony_ci	dev_set_drvdata(&cgdev->dev, NULL);
16908c2ecf20Sopenharmony_ci	kfree(priv);
16918c2ecf20Sopenharmony_ci	put_device(&cgdev->dev);
16928c2ecf20Sopenharmony_ci}
16938c2ecf20Sopenharmony_ci
16948c2ecf20Sopenharmony_cistatic struct ccw_device_id ctcm_ids[] = {
16958c2ecf20Sopenharmony_ci	{CCW_DEVICE(0x3088, 0x08), .driver_info = ctcm_channel_type_parallel},
16968c2ecf20Sopenharmony_ci	{CCW_DEVICE(0x3088, 0x1e), .driver_info = ctcm_channel_type_ficon},
16978c2ecf20Sopenharmony_ci	{CCW_DEVICE(0x3088, 0x1f), .driver_info = ctcm_channel_type_escon},
16988c2ecf20Sopenharmony_ci	{},
16998c2ecf20Sopenharmony_ci};
17008c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(ccw, ctcm_ids);
17018c2ecf20Sopenharmony_ci
17028c2ecf20Sopenharmony_cistatic struct ccw_driver ctcm_ccw_driver = {
17038c2ecf20Sopenharmony_ci	.driver = {
17048c2ecf20Sopenharmony_ci		.owner	= THIS_MODULE,
17058c2ecf20Sopenharmony_ci		.name	= "ctcm",
17068c2ecf20Sopenharmony_ci	},
17078c2ecf20Sopenharmony_ci	.ids	= ctcm_ids,
17088c2ecf20Sopenharmony_ci	.probe	= ccwgroup_probe_ccwdev,
17098c2ecf20Sopenharmony_ci	.remove	= ccwgroup_remove_ccwdev,
17108c2ecf20Sopenharmony_ci	.int_class = IRQIO_CTC,
17118c2ecf20Sopenharmony_ci};
17128c2ecf20Sopenharmony_ci
17138c2ecf20Sopenharmony_cistatic struct ccwgroup_driver ctcm_group_driver = {
17148c2ecf20Sopenharmony_ci	.driver = {
17158c2ecf20Sopenharmony_ci		.owner	= THIS_MODULE,
17168c2ecf20Sopenharmony_ci		.name	= CTC_DRIVER_NAME,
17178c2ecf20Sopenharmony_ci	},
17188c2ecf20Sopenharmony_ci	.ccw_driver  = &ctcm_ccw_driver,
17198c2ecf20Sopenharmony_ci	.setup	     = ctcm_probe_device,
17208c2ecf20Sopenharmony_ci	.remove      = ctcm_remove_device,
17218c2ecf20Sopenharmony_ci	.set_online  = ctcm_new_device,
17228c2ecf20Sopenharmony_ci	.set_offline = ctcm_shutdown_device,
17238c2ecf20Sopenharmony_ci};
17248c2ecf20Sopenharmony_ci
17258c2ecf20Sopenharmony_cistatic ssize_t group_store(struct device_driver *ddrv, const char *buf,
17268c2ecf20Sopenharmony_ci			   size_t count)
17278c2ecf20Sopenharmony_ci{
17288c2ecf20Sopenharmony_ci	int err;
17298c2ecf20Sopenharmony_ci
17308c2ecf20Sopenharmony_ci	err = ccwgroup_create_dev(ctcm_root_dev, &ctcm_group_driver, 2, buf);
17318c2ecf20Sopenharmony_ci	return err ? err : count;
17328c2ecf20Sopenharmony_ci}
17338c2ecf20Sopenharmony_cistatic DRIVER_ATTR_WO(group);
17348c2ecf20Sopenharmony_ci
17358c2ecf20Sopenharmony_cistatic struct attribute *ctcm_drv_attrs[] = {
17368c2ecf20Sopenharmony_ci	&driver_attr_group.attr,
17378c2ecf20Sopenharmony_ci	NULL,
17388c2ecf20Sopenharmony_ci};
17398c2ecf20Sopenharmony_cistatic struct attribute_group ctcm_drv_attr_group = {
17408c2ecf20Sopenharmony_ci	.attrs = ctcm_drv_attrs,
17418c2ecf20Sopenharmony_ci};
17428c2ecf20Sopenharmony_cistatic const struct attribute_group *ctcm_drv_attr_groups[] = {
17438c2ecf20Sopenharmony_ci	&ctcm_drv_attr_group,
17448c2ecf20Sopenharmony_ci	NULL,
17458c2ecf20Sopenharmony_ci};
17468c2ecf20Sopenharmony_ci
17478c2ecf20Sopenharmony_ci/*
17488c2ecf20Sopenharmony_ci * Module related routines
17498c2ecf20Sopenharmony_ci */
17508c2ecf20Sopenharmony_ci
17518c2ecf20Sopenharmony_ci/*
17528c2ecf20Sopenharmony_ci * Prepare to be unloaded. Free IRQ's and release all resources.
17538c2ecf20Sopenharmony_ci * This is called just before this module is unloaded. It is
17548c2ecf20Sopenharmony_ci * not called, if the usage count is !0, so we don't need to check
17558c2ecf20Sopenharmony_ci * for that.
17568c2ecf20Sopenharmony_ci */
17578c2ecf20Sopenharmony_cistatic void __exit ctcm_exit(void)
17588c2ecf20Sopenharmony_ci{
17598c2ecf20Sopenharmony_ci	ccwgroup_driver_unregister(&ctcm_group_driver);
17608c2ecf20Sopenharmony_ci	ccw_driver_unregister(&ctcm_ccw_driver);
17618c2ecf20Sopenharmony_ci	root_device_unregister(ctcm_root_dev);
17628c2ecf20Sopenharmony_ci	ctcm_unregister_dbf_views();
17638c2ecf20Sopenharmony_ci	pr_info("CTCM driver unloaded\n");
17648c2ecf20Sopenharmony_ci}
17658c2ecf20Sopenharmony_ci
17668c2ecf20Sopenharmony_ci/*
17678c2ecf20Sopenharmony_ci * Print Banner.
17688c2ecf20Sopenharmony_ci */
17698c2ecf20Sopenharmony_cistatic void print_banner(void)
17708c2ecf20Sopenharmony_ci{
17718c2ecf20Sopenharmony_ci	pr_info("CTCM driver initialized\n");
17728c2ecf20Sopenharmony_ci}
17738c2ecf20Sopenharmony_ci
17748c2ecf20Sopenharmony_ci/**
17758c2ecf20Sopenharmony_ci * Initialize module.
17768c2ecf20Sopenharmony_ci * This is called just after the module is loaded.
17778c2ecf20Sopenharmony_ci *
17788c2ecf20Sopenharmony_ci * returns 0 on success, !0 on error.
17798c2ecf20Sopenharmony_ci */
17808c2ecf20Sopenharmony_cistatic int __init ctcm_init(void)
17818c2ecf20Sopenharmony_ci{
17828c2ecf20Sopenharmony_ci	int ret;
17838c2ecf20Sopenharmony_ci
17848c2ecf20Sopenharmony_ci	channels = NULL;
17858c2ecf20Sopenharmony_ci
17868c2ecf20Sopenharmony_ci	ret = ctcm_register_dbf_views();
17878c2ecf20Sopenharmony_ci	if (ret)
17888c2ecf20Sopenharmony_ci		goto out_err;
17898c2ecf20Sopenharmony_ci	ctcm_root_dev = root_device_register("ctcm");
17908c2ecf20Sopenharmony_ci	ret = PTR_ERR_OR_ZERO(ctcm_root_dev);
17918c2ecf20Sopenharmony_ci	if (ret)
17928c2ecf20Sopenharmony_ci		goto register_err;
17938c2ecf20Sopenharmony_ci	ret = ccw_driver_register(&ctcm_ccw_driver);
17948c2ecf20Sopenharmony_ci	if (ret)
17958c2ecf20Sopenharmony_ci		goto ccw_err;
17968c2ecf20Sopenharmony_ci	ctcm_group_driver.driver.groups = ctcm_drv_attr_groups;
17978c2ecf20Sopenharmony_ci	ret = ccwgroup_driver_register(&ctcm_group_driver);
17988c2ecf20Sopenharmony_ci	if (ret)
17998c2ecf20Sopenharmony_ci		goto ccwgroup_err;
18008c2ecf20Sopenharmony_ci	print_banner();
18018c2ecf20Sopenharmony_ci	return 0;
18028c2ecf20Sopenharmony_ci
18038c2ecf20Sopenharmony_ciccwgroup_err:
18048c2ecf20Sopenharmony_ci	ccw_driver_unregister(&ctcm_ccw_driver);
18058c2ecf20Sopenharmony_ciccw_err:
18068c2ecf20Sopenharmony_ci	root_device_unregister(ctcm_root_dev);
18078c2ecf20Sopenharmony_ciregister_err:
18088c2ecf20Sopenharmony_ci	ctcm_unregister_dbf_views();
18098c2ecf20Sopenharmony_ciout_err:
18108c2ecf20Sopenharmony_ci	pr_err("%s / Initializing the ctcm device driver failed, ret = %d\n",
18118c2ecf20Sopenharmony_ci		__func__, ret);
18128c2ecf20Sopenharmony_ci	return ret;
18138c2ecf20Sopenharmony_ci}
18148c2ecf20Sopenharmony_ci
18158c2ecf20Sopenharmony_cimodule_init(ctcm_init);
18168c2ecf20Sopenharmony_cimodule_exit(ctcm_exit);
18178c2ecf20Sopenharmony_ci
18188c2ecf20Sopenharmony_ciMODULE_AUTHOR("Peter Tiedemann <ptiedem@de.ibm.com>");
18198c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Network driver for S/390 CTC + CTCMPC (SNA)");
18208c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
18218c2ecf20Sopenharmony_ci
1822