162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright IBM Corp. 2001, 2009 462306a36Sopenharmony_ci * Author(s): 562306a36Sopenharmony_ci * Original CTC driver(s): 662306a36Sopenharmony_ci * Fritz Elfert (felfert@millenux.com) 762306a36Sopenharmony_ci * Dieter Wellerdiek (wel@de.ibm.com) 862306a36Sopenharmony_ci * Martin Schwidefsky (schwidefsky@de.ibm.com) 962306a36Sopenharmony_ci * Denis Joseph Barrow (barrow_dj@yahoo.com) 1062306a36Sopenharmony_ci * Jochen Roehrig (roehrig@de.ibm.com) 1162306a36Sopenharmony_ci * Cornelia Huck <cornelia.huck@de.ibm.com> 1262306a36Sopenharmony_ci * MPC additions: 1362306a36Sopenharmony_ci * Belinda Thompson (belindat@us.ibm.com) 1462306a36Sopenharmony_ci * Andy Richter (richtera@us.ibm.com) 1562306a36Sopenharmony_ci * Revived by: 1662306a36Sopenharmony_ci * Peter Tiedemann (ptiedem@de.ibm.com) 1762306a36Sopenharmony_ci */ 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#undef DEBUG 2062306a36Sopenharmony_ci#undef DEBUGDATA 2162306a36Sopenharmony_ci#undef DEBUGCCW 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#define KMSG_COMPONENT "ctcm" 2462306a36Sopenharmony_ci#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#include <linux/module.h> 2762306a36Sopenharmony_ci#include <linux/init.h> 2862306a36Sopenharmony_ci#include <linux/kernel.h> 2962306a36Sopenharmony_ci#include <linux/slab.h> 3062306a36Sopenharmony_ci#include <linux/errno.h> 3162306a36Sopenharmony_ci#include <linux/types.h> 3262306a36Sopenharmony_ci#include <linux/interrupt.h> 3362306a36Sopenharmony_ci#include <linux/timer.h> 3462306a36Sopenharmony_ci#include <linux/bitops.h> 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci#include <linux/signal.h> 3762306a36Sopenharmony_ci#include <linux/string.h> 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci#include <linux/ip.h> 4062306a36Sopenharmony_ci#include <linux/if_arp.h> 4162306a36Sopenharmony_ci#include <linux/tcp.h> 4262306a36Sopenharmony_ci#include <linux/skbuff.h> 4362306a36Sopenharmony_ci#include <linux/ctype.h> 4462306a36Sopenharmony_ci#include <net/dst.h> 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci#include <linux/io.h> 4762306a36Sopenharmony_ci#include <asm/ccwdev.h> 4862306a36Sopenharmony_ci#include <asm/ccwgroup.h> 4962306a36Sopenharmony_ci#include <linux/uaccess.h> 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci#include <asm/idals.h> 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci#include "ctcm_fsms.h" 5462306a36Sopenharmony_ci#include "ctcm_main.h" 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci/* Some common global variables */ 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci/* 5962306a36Sopenharmony_ci * The root device for ctcm group devices 6062306a36Sopenharmony_ci */ 6162306a36Sopenharmony_cistatic struct device *ctcm_root_dev; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci/* 6462306a36Sopenharmony_ci * Linked list of all detected channels. 6562306a36Sopenharmony_ci */ 6662306a36Sopenharmony_cistruct channel *channels; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci/* 6962306a36Sopenharmony_ci * Unpack a just received skb and hand it over to 7062306a36Sopenharmony_ci * upper layers. 7162306a36Sopenharmony_ci * 7262306a36Sopenharmony_ci * ch The channel where this skb has been received. 7362306a36Sopenharmony_ci * pskb The received skb. 7462306a36Sopenharmony_ci */ 7562306a36Sopenharmony_civoid ctcm_unpack_skb(struct channel *ch, struct sk_buff *pskb) 7662306a36Sopenharmony_ci{ 7762306a36Sopenharmony_ci struct net_device *dev = ch->netdev; 7862306a36Sopenharmony_ci struct ctcm_priv *priv = dev->ml_priv; 7962306a36Sopenharmony_ci __u16 len = *((__u16 *) pskb->data); 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci skb_put(pskb, 2 + LL_HEADER_LENGTH); 8262306a36Sopenharmony_ci skb_pull(pskb, 2); 8362306a36Sopenharmony_ci pskb->dev = dev; 8462306a36Sopenharmony_ci pskb->ip_summed = CHECKSUM_UNNECESSARY; 8562306a36Sopenharmony_ci while (len > 0) { 8662306a36Sopenharmony_ci struct sk_buff *skb; 8762306a36Sopenharmony_ci int skblen; 8862306a36Sopenharmony_ci struct ll_header *header = (struct ll_header *)pskb->data; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci skb_pull(pskb, LL_HEADER_LENGTH); 9162306a36Sopenharmony_ci if ((ch->protocol == CTCM_PROTO_S390) && 9262306a36Sopenharmony_ci (header->type != ETH_P_IP)) { 9362306a36Sopenharmony_ci if (!(ch->logflags & LOG_FLAG_ILLEGALPKT)) { 9462306a36Sopenharmony_ci ch->logflags |= LOG_FLAG_ILLEGALPKT; 9562306a36Sopenharmony_ci /* 9662306a36Sopenharmony_ci * Check packet type only if we stick strictly 9762306a36Sopenharmony_ci * to S/390's protocol of OS390. This only 9862306a36Sopenharmony_ci * supports IP. Otherwise allow any packet 9962306a36Sopenharmony_ci * type. 10062306a36Sopenharmony_ci */ 10162306a36Sopenharmony_ci CTCM_DBF_TEXT_(ERROR, CTC_DBF_ERROR, 10262306a36Sopenharmony_ci "%s(%s): Illegal packet type 0x%04x" 10362306a36Sopenharmony_ci " - dropping", 10462306a36Sopenharmony_ci CTCM_FUNTAIL, dev->name, header->type); 10562306a36Sopenharmony_ci } 10662306a36Sopenharmony_ci priv->stats.rx_dropped++; 10762306a36Sopenharmony_ci priv->stats.rx_frame_errors++; 10862306a36Sopenharmony_ci return; 10962306a36Sopenharmony_ci } 11062306a36Sopenharmony_ci pskb->protocol = cpu_to_be16(header->type); 11162306a36Sopenharmony_ci if ((header->length <= LL_HEADER_LENGTH) || 11262306a36Sopenharmony_ci (len <= LL_HEADER_LENGTH)) { 11362306a36Sopenharmony_ci if (!(ch->logflags & LOG_FLAG_ILLEGALSIZE)) { 11462306a36Sopenharmony_ci CTCM_DBF_TEXT_(ERROR, CTC_DBF_ERROR, 11562306a36Sopenharmony_ci "%s(%s): Illegal packet size %d(%d,%d)" 11662306a36Sopenharmony_ci "- dropping", 11762306a36Sopenharmony_ci CTCM_FUNTAIL, dev->name, 11862306a36Sopenharmony_ci header->length, dev->mtu, len); 11962306a36Sopenharmony_ci ch->logflags |= LOG_FLAG_ILLEGALSIZE; 12062306a36Sopenharmony_ci } 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci priv->stats.rx_dropped++; 12362306a36Sopenharmony_ci priv->stats.rx_length_errors++; 12462306a36Sopenharmony_ci return; 12562306a36Sopenharmony_ci } 12662306a36Sopenharmony_ci header->length -= LL_HEADER_LENGTH; 12762306a36Sopenharmony_ci len -= LL_HEADER_LENGTH; 12862306a36Sopenharmony_ci if ((header->length > skb_tailroom(pskb)) || 12962306a36Sopenharmony_ci (header->length > len)) { 13062306a36Sopenharmony_ci if (!(ch->logflags & LOG_FLAG_OVERRUN)) { 13162306a36Sopenharmony_ci CTCM_DBF_TEXT_(ERROR, CTC_DBF_ERROR, 13262306a36Sopenharmony_ci "%s(%s): Packet size %d (overrun)" 13362306a36Sopenharmony_ci " - dropping", CTCM_FUNTAIL, 13462306a36Sopenharmony_ci dev->name, header->length); 13562306a36Sopenharmony_ci ch->logflags |= LOG_FLAG_OVERRUN; 13662306a36Sopenharmony_ci } 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci priv->stats.rx_dropped++; 13962306a36Sopenharmony_ci priv->stats.rx_length_errors++; 14062306a36Sopenharmony_ci return; 14162306a36Sopenharmony_ci } 14262306a36Sopenharmony_ci skb_put(pskb, header->length); 14362306a36Sopenharmony_ci skb_reset_mac_header(pskb); 14462306a36Sopenharmony_ci len -= header->length; 14562306a36Sopenharmony_ci skb = dev_alloc_skb(pskb->len); 14662306a36Sopenharmony_ci if (!skb) { 14762306a36Sopenharmony_ci if (!(ch->logflags & LOG_FLAG_NOMEM)) { 14862306a36Sopenharmony_ci CTCM_DBF_TEXT_(ERROR, CTC_DBF_ERROR, 14962306a36Sopenharmony_ci "%s(%s): MEMORY allocation error", 15062306a36Sopenharmony_ci CTCM_FUNTAIL, dev->name); 15162306a36Sopenharmony_ci ch->logflags |= LOG_FLAG_NOMEM; 15262306a36Sopenharmony_ci } 15362306a36Sopenharmony_ci priv->stats.rx_dropped++; 15462306a36Sopenharmony_ci return; 15562306a36Sopenharmony_ci } 15662306a36Sopenharmony_ci skb_copy_from_linear_data(pskb, skb_put(skb, pskb->len), 15762306a36Sopenharmony_ci pskb->len); 15862306a36Sopenharmony_ci skb_reset_mac_header(skb); 15962306a36Sopenharmony_ci skb->dev = pskb->dev; 16062306a36Sopenharmony_ci skb->protocol = pskb->protocol; 16162306a36Sopenharmony_ci pskb->ip_summed = CHECKSUM_UNNECESSARY; 16262306a36Sopenharmony_ci skblen = skb->len; 16362306a36Sopenharmony_ci /* 16462306a36Sopenharmony_ci * reset logflags 16562306a36Sopenharmony_ci */ 16662306a36Sopenharmony_ci ch->logflags = 0; 16762306a36Sopenharmony_ci priv->stats.rx_packets++; 16862306a36Sopenharmony_ci priv->stats.rx_bytes += skblen; 16962306a36Sopenharmony_ci netif_rx(skb); 17062306a36Sopenharmony_ci if (len > 0) { 17162306a36Sopenharmony_ci skb_pull(pskb, header->length); 17262306a36Sopenharmony_ci if (skb_tailroom(pskb) < LL_HEADER_LENGTH) { 17362306a36Sopenharmony_ci CTCM_DBF_DEV_NAME(TRACE, dev, 17462306a36Sopenharmony_ci "Overrun in ctcm_unpack_skb"); 17562306a36Sopenharmony_ci ch->logflags |= LOG_FLAG_OVERRUN; 17662306a36Sopenharmony_ci return; 17762306a36Sopenharmony_ci } 17862306a36Sopenharmony_ci skb_put(pskb, LL_HEADER_LENGTH); 17962306a36Sopenharmony_ci } 18062306a36Sopenharmony_ci } 18162306a36Sopenharmony_ci} 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci/* 18462306a36Sopenharmony_ci * Release a specific channel in the channel list. 18562306a36Sopenharmony_ci * 18662306a36Sopenharmony_ci * ch Pointer to channel struct to be released. 18762306a36Sopenharmony_ci */ 18862306a36Sopenharmony_cistatic void channel_free(struct channel *ch) 18962306a36Sopenharmony_ci{ 19062306a36Sopenharmony_ci CTCM_DBF_TEXT_(SETUP, CTC_DBF_INFO, "%s(%s)", CTCM_FUNTAIL, ch->id); 19162306a36Sopenharmony_ci ch->flags &= ~CHANNEL_FLAGS_INUSE; 19262306a36Sopenharmony_ci fsm_newstate(ch->fsm, CTC_STATE_IDLE); 19362306a36Sopenharmony_ci} 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci/* 19662306a36Sopenharmony_ci * Remove a specific channel in the channel list. 19762306a36Sopenharmony_ci * 19862306a36Sopenharmony_ci * ch Pointer to channel struct to be released. 19962306a36Sopenharmony_ci */ 20062306a36Sopenharmony_cistatic void channel_remove(struct channel *ch) 20162306a36Sopenharmony_ci{ 20262306a36Sopenharmony_ci struct channel **c = &channels; 20362306a36Sopenharmony_ci char chid[CTCM_ID_SIZE+1]; 20462306a36Sopenharmony_ci int ok = 0; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci if (ch == NULL) 20762306a36Sopenharmony_ci return; 20862306a36Sopenharmony_ci else 20962306a36Sopenharmony_ci strncpy(chid, ch->id, CTCM_ID_SIZE); 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci channel_free(ch); 21262306a36Sopenharmony_ci while (*c) { 21362306a36Sopenharmony_ci if (*c == ch) { 21462306a36Sopenharmony_ci *c = ch->next; 21562306a36Sopenharmony_ci fsm_deltimer(&ch->timer); 21662306a36Sopenharmony_ci if (IS_MPC(ch)) 21762306a36Sopenharmony_ci fsm_deltimer(&ch->sweep_timer); 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci kfree_fsm(ch->fsm); 22062306a36Sopenharmony_ci clear_normalized_cda(&ch->ccw[4]); 22162306a36Sopenharmony_ci if (ch->trans_skb != NULL) { 22262306a36Sopenharmony_ci clear_normalized_cda(&ch->ccw[1]); 22362306a36Sopenharmony_ci dev_kfree_skb_any(ch->trans_skb); 22462306a36Sopenharmony_ci } 22562306a36Sopenharmony_ci if (IS_MPC(ch)) { 22662306a36Sopenharmony_ci tasklet_kill(&ch->ch_tasklet); 22762306a36Sopenharmony_ci tasklet_kill(&ch->ch_disc_tasklet); 22862306a36Sopenharmony_ci kfree(ch->discontact_th); 22962306a36Sopenharmony_ci } 23062306a36Sopenharmony_ci kfree(ch->ccw); 23162306a36Sopenharmony_ci kfree(ch->irb); 23262306a36Sopenharmony_ci kfree(ch); 23362306a36Sopenharmony_ci ok = 1; 23462306a36Sopenharmony_ci break; 23562306a36Sopenharmony_ci } 23662306a36Sopenharmony_ci c = &((*c)->next); 23762306a36Sopenharmony_ci } 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci CTCM_DBF_TEXT_(SETUP, CTC_DBF_INFO, "%s(%s) %s", CTCM_FUNTAIL, 24062306a36Sopenharmony_ci chid, ok ? "OK" : "failed"); 24162306a36Sopenharmony_ci} 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci/* 24462306a36Sopenharmony_ci * Get a specific channel from the channel list. 24562306a36Sopenharmony_ci * 24662306a36Sopenharmony_ci * type Type of channel we are interested in. 24762306a36Sopenharmony_ci * id Id of channel we are interested in. 24862306a36Sopenharmony_ci * direction Direction we want to use this channel for. 24962306a36Sopenharmony_ci * 25062306a36Sopenharmony_ci * returns Pointer to a channel or NULL if no matching channel available. 25162306a36Sopenharmony_ci */ 25262306a36Sopenharmony_cistatic struct channel *channel_get(enum ctcm_channel_types type, 25362306a36Sopenharmony_ci char *id, int direction) 25462306a36Sopenharmony_ci{ 25562306a36Sopenharmony_ci struct channel *ch = channels; 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci while (ch && (strncmp(ch->id, id, CTCM_ID_SIZE) || (ch->type != type))) 25862306a36Sopenharmony_ci ch = ch->next; 25962306a36Sopenharmony_ci if (!ch) { 26062306a36Sopenharmony_ci CTCM_DBF_TEXT_(ERROR, CTC_DBF_ERROR, 26162306a36Sopenharmony_ci "%s(%d, %s, %d) not found in channel list\n", 26262306a36Sopenharmony_ci CTCM_FUNTAIL, type, id, direction); 26362306a36Sopenharmony_ci } else { 26462306a36Sopenharmony_ci if (ch->flags & CHANNEL_FLAGS_INUSE) 26562306a36Sopenharmony_ci ch = NULL; 26662306a36Sopenharmony_ci else { 26762306a36Sopenharmony_ci ch->flags |= CHANNEL_FLAGS_INUSE; 26862306a36Sopenharmony_ci ch->flags &= ~CHANNEL_FLAGS_RWMASK; 26962306a36Sopenharmony_ci ch->flags |= (direction == CTCM_WRITE) 27062306a36Sopenharmony_ci ? CHANNEL_FLAGS_WRITE : CHANNEL_FLAGS_READ; 27162306a36Sopenharmony_ci fsm_newstate(ch->fsm, CTC_STATE_STOPPED); 27262306a36Sopenharmony_ci } 27362306a36Sopenharmony_ci } 27462306a36Sopenharmony_ci return ch; 27562306a36Sopenharmony_ci} 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_cistatic long ctcm_check_irb_error(struct ccw_device *cdev, struct irb *irb) 27862306a36Sopenharmony_ci{ 27962306a36Sopenharmony_ci if (!IS_ERR(irb)) 28062306a36Sopenharmony_ci return 0; 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci CTCM_DBF_TEXT_(ERROR, CTC_DBF_WARN, 28362306a36Sopenharmony_ci "irb error %ld on device %s\n", 28462306a36Sopenharmony_ci PTR_ERR(irb), dev_name(&cdev->dev)); 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci switch (PTR_ERR(irb)) { 28762306a36Sopenharmony_ci case -EIO: 28862306a36Sopenharmony_ci dev_err(&cdev->dev, 28962306a36Sopenharmony_ci "An I/O-error occurred on the CTCM device\n"); 29062306a36Sopenharmony_ci break; 29162306a36Sopenharmony_ci case -ETIMEDOUT: 29262306a36Sopenharmony_ci dev_err(&cdev->dev, 29362306a36Sopenharmony_ci "An adapter hardware operation timed out\n"); 29462306a36Sopenharmony_ci break; 29562306a36Sopenharmony_ci default: 29662306a36Sopenharmony_ci dev_err(&cdev->dev, 29762306a36Sopenharmony_ci "An error occurred on the adapter hardware\n"); 29862306a36Sopenharmony_ci } 29962306a36Sopenharmony_ci return PTR_ERR(irb); 30062306a36Sopenharmony_ci} 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci/* 30462306a36Sopenharmony_ci * Check sense of a unit check. 30562306a36Sopenharmony_ci * 30662306a36Sopenharmony_ci * ch The channel, the sense code belongs to. 30762306a36Sopenharmony_ci * sense The sense code to inspect. 30862306a36Sopenharmony_ci */ 30962306a36Sopenharmony_cistatic void ccw_unit_check(struct channel *ch, __u8 sense) 31062306a36Sopenharmony_ci{ 31162306a36Sopenharmony_ci CTCM_DBF_TEXT_(TRACE, CTC_DBF_DEBUG, 31262306a36Sopenharmony_ci "%s(%s): %02x", 31362306a36Sopenharmony_ci CTCM_FUNTAIL, ch->id, sense); 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci if (sense & SNS0_INTERVENTION_REQ) { 31662306a36Sopenharmony_ci if (sense & 0x01) { 31762306a36Sopenharmony_ci if (ch->sense_rc != 0x01) { 31862306a36Sopenharmony_ci pr_notice( 31962306a36Sopenharmony_ci "%s: The communication peer has " 32062306a36Sopenharmony_ci "disconnected\n", ch->id); 32162306a36Sopenharmony_ci ch->sense_rc = 0x01; 32262306a36Sopenharmony_ci } 32362306a36Sopenharmony_ci fsm_event(ch->fsm, CTC_EVENT_UC_RCRESET, ch); 32462306a36Sopenharmony_ci } else { 32562306a36Sopenharmony_ci if (ch->sense_rc != SNS0_INTERVENTION_REQ) { 32662306a36Sopenharmony_ci pr_notice( 32762306a36Sopenharmony_ci "%s: The remote operating system is " 32862306a36Sopenharmony_ci "not available\n", ch->id); 32962306a36Sopenharmony_ci ch->sense_rc = SNS0_INTERVENTION_REQ; 33062306a36Sopenharmony_ci } 33162306a36Sopenharmony_ci fsm_event(ch->fsm, CTC_EVENT_UC_RSRESET, ch); 33262306a36Sopenharmony_ci } 33362306a36Sopenharmony_ci } else if (sense & SNS0_EQUIPMENT_CHECK) { 33462306a36Sopenharmony_ci if (sense & SNS0_BUS_OUT_CHECK) { 33562306a36Sopenharmony_ci if (ch->sense_rc != SNS0_BUS_OUT_CHECK) { 33662306a36Sopenharmony_ci CTCM_DBF_TEXT_(TRACE, CTC_DBF_WARN, 33762306a36Sopenharmony_ci "%s(%s): remote HW error %02x", 33862306a36Sopenharmony_ci CTCM_FUNTAIL, ch->id, sense); 33962306a36Sopenharmony_ci ch->sense_rc = SNS0_BUS_OUT_CHECK; 34062306a36Sopenharmony_ci } 34162306a36Sopenharmony_ci fsm_event(ch->fsm, CTC_EVENT_UC_HWFAIL, ch); 34262306a36Sopenharmony_ci } else { 34362306a36Sopenharmony_ci if (ch->sense_rc != SNS0_EQUIPMENT_CHECK) { 34462306a36Sopenharmony_ci CTCM_DBF_TEXT_(TRACE, CTC_DBF_WARN, 34562306a36Sopenharmony_ci "%s(%s): remote read parity error %02x", 34662306a36Sopenharmony_ci CTCM_FUNTAIL, ch->id, sense); 34762306a36Sopenharmony_ci ch->sense_rc = SNS0_EQUIPMENT_CHECK; 34862306a36Sopenharmony_ci } 34962306a36Sopenharmony_ci fsm_event(ch->fsm, CTC_EVENT_UC_RXPARITY, ch); 35062306a36Sopenharmony_ci } 35162306a36Sopenharmony_ci } else if (sense & SNS0_BUS_OUT_CHECK) { 35262306a36Sopenharmony_ci if (ch->sense_rc != SNS0_BUS_OUT_CHECK) { 35362306a36Sopenharmony_ci CTCM_DBF_TEXT_(TRACE, CTC_DBF_WARN, 35462306a36Sopenharmony_ci "%s(%s): BUS OUT error %02x", 35562306a36Sopenharmony_ci CTCM_FUNTAIL, ch->id, sense); 35662306a36Sopenharmony_ci ch->sense_rc = SNS0_BUS_OUT_CHECK; 35762306a36Sopenharmony_ci } 35862306a36Sopenharmony_ci if (sense & 0x04) /* data-streaming timeout */ 35962306a36Sopenharmony_ci fsm_event(ch->fsm, CTC_EVENT_UC_TXTIMEOUT, ch); 36062306a36Sopenharmony_ci else /* Data-transfer parity error */ 36162306a36Sopenharmony_ci fsm_event(ch->fsm, CTC_EVENT_UC_TXPARITY, ch); 36262306a36Sopenharmony_ci } else if (sense & SNS0_CMD_REJECT) { 36362306a36Sopenharmony_ci if (ch->sense_rc != SNS0_CMD_REJECT) { 36462306a36Sopenharmony_ci CTCM_DBF_TEXT_(TRACE, CTC_DBF_WARN, 36562306a36Sopenharmony_ci "%s(%s): Command rejected", 36662306a36Sopenharmony_ci CTCM_FUNTAIL, ch->id); 36762306a36Sopenharmony_ci ch->sense_rc = SNS0_CMD_REJECT; 36862306a36Sopenharmony_ci } 36962306a36Sopenharmony_ci } else if (sense == 0) { 37062306a36Sopenharmony_ci CTCM_DBF_TEXT_(TRACE, CTC_DBF_WARN, 37162306a36Sopenharmony_ci "%s(%s): Unit check ZERO", 37262306a36Sopenharmony_ci CTCM_FUNTAIL, ch->id); 37362306a36Sopenharmony_ci fsm_event(ch->fsm, CTC_EVENT_UC_ZERO, ch); 37462306a36Sopenharmony_ci } else { 37562306a36Sopenharmony_ci CTCM_DBF_TEXT_(TRACE, CTC_DBF_WARN, 37662306a36Sopenharmony_ci "%s(%s): Unit check code %02x unknown", 37762306a36Sopenharmony_ci CTCM_FUNTAIL, ch->id, sense); 37862306a36Sopenharmony_ci fsm_event(ch->fsm, CTC_EVENT_UC_UNKNOWN, ch); 37962306a36Sopenharmony_ci } 38062306a36Sopenharmony_ci} 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ciint ctcm_ch_alloc_buffer(struct channel *ch) 38362306a36Sopenharmony_ci{ 38462306a36Sopenharmony_ci clear_normalized_cda(&ch->ccw[1]); 38562306a36Sopenharmony_ci ch->trans_skb = __dev_alloc_skb(ch->max_bufsize, GFP_ATOMIC | GFP_DMA); 38662306a36Sopenharmony_ci if (ch->trans_skb == NULL) { 38762306a36Sopenharmony_ci CTCM_DBF_TEXT_(ERROR, CTC_DBF_ERROR, 38862306a36Sopenharmony_ci "%s(%s): %s trans_skb allocation error", 38962306a36Sopenharmony_ci CTCM_FUNTAIL, ch->id, 39062306a36Sopenharmony_ci (CHANNEL_DIRECTION(ch->flags) == CTCM_READ) ? 39162306a36Sopenharmony_ci "RX" : "TX"); 39262306a36Sopenharmony_ci return -ENOMEM; 39362306a36Sopenharmony_ci } 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci ch->ccw[1].count = ch->max_bufsize; 39662306a36Sopenharmony_ci if (set_normalized_cda(&ch->ccw[1], ch->trans_skb->data)) { 39762306a36Sopenharmony_ci dev_kfree_skb(ch->trans_skb); 39862306a36Sopenharmony_ci ch->trans_skb = NULL; 39962306a36Sopenharmony_ci CTCM_DBF_TEXT_(ERROR, CTC_DBF_ERROR, 40062306a36Sopenharmony_ci "%s(%s): %s set norm_cda failed", 40162306a36Sopenharmony_ci CTCM_FUNTAIL, ch->id, 40262306a36Sopenharmony_ci (CHANNEL_DIRECTION(ch->flags) == CTCM_READ) ? 40362306a36Sopenharmony_ci "RX" : "TX"); 40462306a36Sopenharmony_ci return -ENOMEM; 40562306a36Sopenharmony_ci } 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci ch->ccw[1].count = 0; 40862306a36Sopenharmony_ci ch->trans_skb_data = ch->trans_skb->data; 40962306a36Sopenharmony_ci ch->flags &= ~CHANNEL_FLAGS_BUFSIZE_CHANGED; 41062306a36Sopenharmony_ci return 0; 41162306a36Sopenharmony_ci} 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci/* 41462306a36Sopenharmony_ci * Interface API for upper network layers 41562306a36Sopenharmony_ci */ 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci/* 41862306a36Sopenharmony_ci * Open an interface. 41962306a36Sopenharmony_ci * Called from generic network layer when ifconfig up is run. 42062306a36Sopenharmony_ci * 42162306a36Sopenharmony_ci * dev Pointer to interface struct. 42262306a36Sopenharmony_ci * 42362306a36Sopenharmony_ci * returns 0 on success, -ERRNO on failure. (Never fails.) 42462306a36Sopenharmony_ci */ 42562306a36Sopenharmony_ciint ctcm_open(struct net_device *dev) 42662306a36Sopenharmony_ci{ 42762306a36Sopenharmony_ci struct ctcm_priv *priv = dev->ml_priv; 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci CTCMY_DBF_DEV_NAME(SETUP, dev, ""); 43062306a36Sopenharmony_ci if (!IS_MPC(priv)) 43162306a36Sopenharmony_ci fsm_event(priv->fsm, DEV_EVENT_START, dev); 43262306a36Sopenharmony_ci return 0; 43362306a36Sopenharmony_ci} 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci/* 43662306a36Sopenharmony_ci * Close an interface. 43762306a36Sopenharmony_ci * Called from generic network layer when ifconfig down is run. 43862306a36Sopenharmony_ci * 43962306a36Sopenharmony_ci * dev Pointer to interface struct. 44062306a36Sopenharmony_ci * 44162306a36Sopenharmony_ci * returns 0 on success, -ERRNO on failure. (Never fails.) 44262306a36Sopenharmony_ci */ 44362306a36Sopenharmony_ciint ctcm_close(struct net_device *dev) 44462306a36Sopenharmony_ci{ 44562306a36Sopenharmony_ci struct ctcm_priv *priv = dev->ml_priv; 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci CTCMY_DBF_DEV_NAME(SETUP, dev, ""); 44862306a36Sopenharmony_ci if (!IS_MPC(priv)) 44962306a36Sopenharmony_ci fsm_event(priv->fsm, DEV_EVENT_STOP, dev); 45062306a36Sopenharmony_ci return 0; 45162306a36Sopenharmony_ci} 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci/* 45562306a36Sopenharmony_ci * Transmit a packet. 45662306a36Sopenharmony_ci * This is a helper function for ctcm_tx(). 45762306a36Sopenharmony_ci * 45862306a36Sopenharmony_ci * ch Channel to be used for sending. 45962306a36Sopenharmony_ci * skb Pointer to struct sk_buff of packet to send. 46062306a36Sopenharmony_ci * The linklevel header has already been set up 46162306a36Sopenharmony_ci * by ctcm_tx(). 46262306a36Sopenharmony_ci * 46362306a36Sopenharmony_ci * returns 0 on success, -ERRNO on failure. (Never fails.) 46462306a36Sopenharmony_ci */ 46562306a36Sopenharmony_cistatic int ctcm_transmit_skb(struct channel *ch, struct sk_buff *skb) 46662306a36Sopenharmony_ci{ 46762306a36Sopenharmony_ci unsigned long saveflags; 46862306a36Sopenharmony_ci struct ll_header header; 46962306a36Sopenharmony_ci int rc = 0; 47062306a36Sopenharmony_ci __u16 block_len; 47162306a36Sopenharmony_ci int ccw_idx; 47262306a36Sopenharmony_ci struct sk_buff *nskb; 47362306a36Sopenharmony_ci unsigned long hi; 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci /* we need to acquire the lock for testing the state 47662306a36Sopenharmony_ci * otherwise we can have an IRQ changing the state to 47762306a36Sopenharmony_ci * TXIDLE after the test but before acquiring the lock. 47862306a36Sopenharmony_ci */ 47962306a36Sopenharmony_ci spin_lock_irqsave(&ch->collect_lock, saveflags); 48062306a36Sopenharmony_ci if (fsm_getstate(ch->fsm) != CTC_STATE_TXIDLE) { 48162306a36Sopenharmony_ci int l = skb->len + LL_HEADER_LENGTH; 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci if (ch->collect_len + l > ch->max_bufsize - 2) { 48462306a36Sopenharmony_ci spin_unlock_irqrestore(&ch->collect_lock, saveflags); 48562306a36Sopenharmony_ci return -EBUSY; 48662306a36Sopenharmony_ci } else { 48762306a36Sopenharmony_ci refcount_inc(&skb->users); 48862306a36Sopenharmony_ci header.length = l; 48962306a36Sopenharmony_ci header.type = be16_to_cpu(skb->protocol); 49062306a36Sopenharmony_ci header.unused = 0; 49162306a36Sopenharmony_ci memcpy(skb_push(skb, LL_HEADER_LENGTH), &header, 49262306a36Sopenharmony_ci LL_HEADER_LENGTH); 49362306a36Sopenharmony_ci skb_queue_tail(&ch->collect_queue, skb); 49462306a36Sopenharmony_ci ch->collect_len += l; 49562306a36Sopenharmony_ci } 49662306a36Sopenharmony_ci spin_unlock_irqrestore(&ch->collect_lock, saveflags); 49762306a36Sopenharmony_ci goto done; 49862306a36Sopenharmony_ci } 49962306a36Sopenharmony_ci spin_unlock_irqrestore(&ch->collect_lock, saveflags); 50062306a36Sopenharmony_ci /* 50162306a36Sopenharmony_ci * Protect skb against beeing free'd by upper 50262306a36Sopenharmony_ci * layers. 50362306a36Sopenharmony_ci */ 50462306a36Sopenharmony_ci refcount_inc(&skb->users); 50562306a36Sopenharmony_ci ch->prof.txlen += skb->len; 50662306a36Sopenharmony_ci header.length = skb->len + LL_HEADER_LENGTH; 50762306a36Sopenharmony_ci header.type = be16_to_cpu(skb->protocol); 50862306a36Sopenharmony_ci header.unused = 0; 50962306a36Sopenharmony_ci memcpy(skb_push(skb, LL_HEADER_LENGTH), &header, LL_HEADER_LENGTH); 51062306a36Sopenharmony_ci block_len = skb->len + 2; 51162306a36Sopenharmony_ci *((__u16 *)skb_push(skb, 2)) = block_len; 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci /* 51462306a36Sopenharmony_ci * IDAL support in CTCM is broken, so we have to 51562306a36Sopenharmony_ci * care about skb's above 2G ourselves. 51662306a36Sopenharmony_ci */ 51762306a36Sopenharmony_ci hi = ((unsigned long)skb_tail_pointer(skb) + LL_HEADER_LENGTH) >> 31; 51862306a36Sopenharmony_ci if (hi) { 51962306a36Sopenharmony_ci nskb = alloc_skb(skb->len, GFP_ATOMIC | GFP_DMA); 52062306a36Sopenharmony_ci if (!nskb) { 52162306a36Sopenharmony_ci refcount_dec(&skb->users); 52262306a36Sopenharmony_ci skb_pull(skb, LL_HEADER_LENGTH + 2); 52362306a36Sopenharmony_ci ctcm_clear_busy(ch->netdev); 52462306a36Sopenharmony_ci return -ENOMEM; 52562306a36Sopenharmony_ci } else { 52662306a36Sopenharmony_ci skb_put_data(nskb, skb->data, skb->len); 52762306a36Sopenharmony_ci refcount_inc(&nskb->users); 52862306a36Sopenharmony_ci refcount_dec(&skb->users); 52962306a36Sopenharmony_ci dev_kfree_skb_irq(skb); 53062306a36Sopenharmony_ci skb = nskb; 53162306a36Sopenharmony_ci } 53262306a36Sopenharmony_ci } 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci ch->ccw[4].count = block_len; 53562306a36Sopenharmony_ci if (set_normalized_cda(&ch->ccw[4], skb->data)) { 53662306a36Sopenharmony_ci /* 53762306a36Sopenharmony_ci * idal allocation failed, try via copying to 53862306a36Sopenharmony_ci * trans_skb. trans_skb usually has a pre-allocated 53962306a36Sopenharmony_ci * idal. 54062306a36Sopenharmony_ci */ 54162306a36Sopenharmony_ci if (ctcm_checkalloc_buffer(ch)) { 54262306a36Sopenharmony_ci /* 54362306a36Sopenharmony_ci * Remove our header. It gets added 54462306a36Sopenharmony_ci * again on retransmit. 54562306a36Sopenharmony_ci */ 54662306a36Sopenharmony_ci refcount_dec(&skb->users); 54762306a36Sopenharmony_ci skb_pull(skb, LL_HEADER_LENGTH + 2); 54862306a36Sopenharmony_ci ctcm_clear_busy(ch->netdev); 54962306a36Sopenharmony_ci return -ENOMEM; 55062306a36Sopenharmony_ci } 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci skb_reset_tail_pointer(ch->trans_skb); 55362306a36Sopenharmony_ci ch->trans_skb->len = 0; 55462306a36Sopenharmony_ci ch->ccw[1].count = skb->len; 55562306a36Sopenharmony_ci skb_copy_from_linear_data(skb, 55662306a36Sopenharmony_ci skb_put(ch->trans_skb, skb->len), skb->len); 55762306a36Sopenharmony_ci refcount_dec(&skb->users); 55862306a36Sopenharmony_ci dev_kfree_skb_irq(skb); 55962306a36Sopenharmony_ci ccw_idx = 0; 56062306a36Sopenharmony_ci } else { 56162306a36Sopenharmony_ci skb_queue_tail(&ch->io_queue, skb); 56262306a36Sopenharmony_ci ccw_idx = 3; 56362306a36Sopenharmony_ci } 56462306a36Sopenharmony_ci if (do_debug_ccw) 56562306a36Sopenharmony_ci ctcmpc_dumpit((char *)&ch->ccw[ccw_idx], 56662306a36Sopenharmony_ci sizeof(struct ccw1) * 3); 56762306a36Sopenharmony_ci ch->retry = 0; 56862306a36Sopenharmony_ci fsm_newstate(ch->fsm, CTC_STATE_TX); 56962306a36Sopenharmony_ci fsm_addtimer(&ch->timer, CTCM_TIME_5_SEC, CTC_EVENT_TIMER, ch); 57062306a36Sopenharmony_ci spin_lock_irqsave(get_ccwdev_lock(ch->cdev), saveflags); 57162306a36Sopenharmony_ci ch->prof.send_stamp = jiffies; 57262306a36Sopenharmony_ci rc = ccw_device_start(ch->cdev, &ch->ccw[ccw_idx], 0, 0xff, 0); 57362306a36Sopenharmony_ci spin_unlock_irqrestore(get_ccwdev_lock(ch->cdev), saveflags); 57462306a36Sopenharmony_ci if (ccw_idx == 3) 57562306a36Sopenharmony_ci ch->prof.doios_single++; 57662306a36Sopenharmony_ci if (rc != 0) { 57762306a36Sopenharmony_ci fsm_deltimer(&ch->timer); 57862306a36Sopenharmony_ci ctcm_ccw_check_rc(ch, rc, "single skb TX"); 57962306a36Sopenharmony_ci if (ccw_idx == 3) 58062306a36Sopenharmony_ci skb_dequeue_tail(&ch->io_queue); 58162306a36Sopenharmony_ci /* 58262306a36Sopenharmony_ci * Remove our header. It gets added 58362306a36Sopenharmony_ci * again on retransmit. 58462306a36Sopenharmony_ci */ 58562306a36Sopenharmony_ci skb_pull(skb, LL_HEADER_LENGTH + 2); 58662306a36Sopenharmony_ci } else if (ccw_idx == 0) { 58762306a36Sopenharmony_ci struct net_device *dev = ch->netdev; 58862306a36Sopenharmony_ci struct ctcm_priv *priv = dev->ml_priv; 58962306a36Sopenharmony_ci priv->stats.tx_packets++; 59062306a36Sopenharmony_ci priv->stats.tx_bytes += skb->len - LL_HEADER_LENGTH; 59162306a36Sopenharmony_ci } 59262306a36Sopenharmony_cidone: 59362306a36Sopenharmony_ci ctcm_clear_busy(ch->netdev); 59462306a36Sopenharmony_ci return rc; 59562306a36Sopenharmony_ci} 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_cistatic void ctcmpc_send_sweep_req(struct channel *rch) 59862306a36Sopenharmony_ci{ 59962306a36Sopenharmony_ci struct net_device *dev = rch->netdev; 60062306a36Sopenharmony_ci struct ctcm_priv *priv; 60162306a36Sopenharmony_ci struct mpc_group *grp; 60262306a36Sopenharmony_ci struct th_sweep *header; 60362306a36Sopenharmony_ci struct sk_buff *sweep_skb; 60462306a36Sopenharmony_ci struct channel *ch; 60562306a36Sopenharmony_ci /* int rc = 0; */ 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci priv = dev->ml_priv; 60862306a36Sopenharmony_ci grp = priv->mpcg; 60962306a36Sopenharmony_ci ch = priv->channel[CTCM_WRITE]; 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci /* sweep processing is not complete until response and request */ 61262306a36Sopenharmony_ci /* has completed for all read channels in group */ 61362306a36Sopenharmony_ci if (grp->in_sweep == 0) { 61462306a36Sopenharmony_ci grp->in_sweep = 1; 61562306a36Sopenharmony_ci grp->sweep_rsp_pend_num = grp->active_channels[CTCM_READ]; 61662306a36Sopenharmony_ci grp->sweep_req_pend_num = grp->active_channels[CTCM_READ]; 61762306a36Sopenharmony_ci } 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci sweep_skb = __dev_alloc_skb(MPC_BUFSIZE_DEFAULT, GFP_ATOMIC|GFP_DMA); 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci if (sweep_skb == NULL) { 62262306a36Sopenharmony_ci /* rc = -ENOMEM; */ 62362306a36Sopenharmony_ci goto nomem; 62462306a36Sopenharmony_ci } 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci header = skb_put_zero(sweep_skb, TH_SWEEP_LENGTH); 62762306a36Sopenharmony_ci header->th.th_ch_flag = TH_SWEEP_REQ; /* 0x0f */ 62862306a36Sopenharmony_ci header->sw.th_last_seq = ch->th_seq_num; 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci netif_trans_update(dev); 63162306a36Sopenharmony_ci skb_queue_tail(&ch->sweep_queue, sweep_skb); 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci fsm_addtimer(&ch->sweep_timer, 100, CTC_EVENT_RSWEEP_TIMER, ch); 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci return; 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_cinomem: 63862306a36Sopenharmony_ci grp->in_sweep = 0; 63962306a36Sopenharmony_ci ctcm_clear_busy(dev); 64062306a36Sopenharmony_ci fsm_event(grp->fsm, MPCG_EVENT_INOP, dev); 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_ci return; 64362306a36Sopenharmony_ci} 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci/* 64662306a36Sopenharmony_ci * MPC mode version of transmit_skb 64762306a36Sopenharmony_ci */ 64862306a36Sopenharmony_cistatic int ctcmpc_transmit_skb(struct channel *ch, struct sk_buff *skb) 64962306a36Sopenharmony_ci{ 65062306a36Sopenharmony_ci struct pdu *p_header; 65162306a36Sopenharmony_ci struct net_device *dev = ch->netdev; 65262306a36Sopenharmony_ci struct ctcm_priv *priv = dev->ml_priv; 65362306a36Sopenharmony_ci struct mpc_group *grp = priv->mpcg; 65462306a36Sopenharmony_ci struct th_header *header; 65562306a36Sopenharmony_ci struct sk_buff *nskb; 65662306a36Sopenharmony_ci int rc = 0; 65762306a36Sopenharmony_ci int ccw_idx; 65862306a36Sopenharmony_ci unsigned long hi; 65962306a36Sopenharmony_ci unsigned long saveflags = 0; /* avoids compiler warning */ 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_ci CTCM_PR_DEBUG("Enter %s: %s, cp=%i ch=0x%p id=%s state=%s\n", 66262306a36Sopenharmony_ci __func__, dev->name, smp_processor_id(), ch, 66362306a36Sopenharmony_ci ch->id, fsm_getstate_str(ch->fsm)); 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci if ((fsm_getstate(ch->fsm) != CTC_STATE_TXIDLE) || grp->in_sweep) { 66662306a36Sopenharmony_ci spin_lock_irqsave(&ch->collect_lock, saveflags); 66762306a36Sopenharmony_ci refcount_inc(&skb->users); 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ci p_header = skb_push(skb, PDU_HEADER_LENGTH); 67062306a36Sopenharmony_ci p_header->pdu_offset = skb->len - PDU_HEADER_LENGTH; 67162306a36Sopenharmony_ci p_header->pdu_proto = 0x01; 67262306a36Sopenharmony_ci if (be16_to_cpu(skb->protocol) == ETH_P_SNAP) { 67362306a36Sopenharmony_ci p_header->pdu_flag = PDU_FIRST | PDU_CNTL; 67462306a36Sopenharmony_ci } else { 67562306a36Sopenharmony_ci p_header->pdu_flag = PDU_FIRST; 67662306a36Sopenharmony_ci } 67762306a36Sopenharmony_ci p_header->pdu_seq = 0; 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci CTCM_PR_DEBUG("%s(%s): Put on collect_q - skb len: %04x \n" 68062306a36Sopenharmony_ci "pdu header and data for up to 32 bytes:\n", 68162306a36Sopenharmony_ci __func__, dev->name, skb->len); 68262306a36Sopenharmony_ci CTCM_D3_DUMP((char *)skb->data, min_t(int, 32, skb->len)); 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_ci skb_queue_tail(&ch->collect_queue, skb); 68562306a36Sopenharmony_ci ch->collect_len += skb->len; 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci spin_unlock_irqrestore(&ch->collect_lock, saveflags); 68862306a36Sopenharmony_ci goto done; 68962306a36Sopenharmony_ci } 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_ci /* 69262306a36Sopenharmony_ci * Protect skb against beeing free'd by upper 69362306a36Sopenharmony_ci * layers. 69462306a36Sopenharmony_ci */ 69562306a36Sopenharmony_ci refcount_inc(&skb->users); 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ci /* 69862306a36Sopenharmony_ci * IDAL support in CTCM is broken, so we have to 69962306a36Sopenharmony_ci * care about skb's above 2G ourselves. 70062306a36Sopenharmony_ci */ 70162306a36Sopenharmony_ci hi = ((unsigned long)skb->tail + TH_HEADER_LENGTH) >> 31; 70262306a36Sopenharmony_ci if (hi) { 70362306a36Sopenharmony_ci nskb = __dev_alloc_skb(skb->len, GFP_ATOMIC | GFP_DMA); 70462306a36Sopenharmony_ci if (!nskb) { 70562306a36Sopenharmony_ci goto nomem_exit; 70662306a36Sopenharmony_ci } else { 70762306a36Sopenharmony_ci skb_put_data(nskb, skb->data, skb->len); 70862306a36Sopenharmony_ci refcount_inc(&nskb->users); 70962306a36Sopenharmony_ci refcount_dec(&skb->users); 71062306a36Sopenharmony_ci dev_kfree_skb_irq(skb); 71162306a36Sopenharmony_ci skb = nskb; 71262306a36Sopenharmony_ci } 71362306a36Sopenharmony_ci } 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_ci p_header = skb_push(skb, PDU_HEADER_LENGTH); 71662306a36Sopenharmony_ci p_header->pdu_offset = skb->len - PDU_HEADER_LENGTH; 71762306a36Sopenharmony_ci p_header->pdu_proto = 0x01; 71862306a36Sopenharmony_ci p_header->pdu_seq = 0; 71962306a36Sopenharmony_ci if (be16_to_cpu(skb->protocol) == ETH_P_SNAP) { 72062306a36Sopenharmony_ci p_header->pdu_flag = PDU_FIRST | PDU_CNTL; 72162306a36Sopenharmony_ci } else { 72262306a36Sopenharmony_ci p_header->pdu_flag = PDU_FIRST; 72362306a36Sopenharmony_ci } 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_ci if (ch->collect_len > 0) { 72662306a36Sopenharmony_ci spin_lock_irqsave(&ch->collect_lock, saveflags); 72762306a36Sopenharmony_ci skb_queue_tail(&ch->collect_queue, skb); 72862306a36Sopenharmony_ci ch->collect_len += skb->len; 72962306a36Sopenharmony_ci skb = skb_dequeue(&ch->collect_queue); 73062306a36Sopenharmony_ci ch->collect_len -= skb->len; 73162306a36Sopenharmony_ci spin_unlock_irqrestore(&ch->collect_lock, saveflags); 73262306a36Sopenharmony_ci } 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_ci p_header = (struct pdu *)skb->data; 73562306a36Sopenharmony_ci p_header->pdu_flag |= PDU_LAST; 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_ci ch->prof.txlen += skb->len - PDU_HEADER_LENGTH; 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_ci /* put the TH on the packet */ 74062306a36Sopenharmony_ci header = skb_push(skb, TH_HEADER_LENGTH); 74162306a36Sopenharmony_ci memset(header, 0, TH_HEADER_LENGTH); 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_ci header->th_ch_flag = TH_HAS_PDU; /* Normal data */ 74462306a36Sopenharmony_ci ch->th_seq_num++; 74562306a36Sopenharmony_ci header->th_seq_num = ch->th_seq_num; 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_ci CTCM_PR_DBGDATA("%s(%s) ToVTAM_th_seq= %08x\n" , 74862306a36Sopenharmony_ci __func__, dev->name, ch->th_seq_num); 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_ci CTCM_PR_DBGDATA("%s(%s): skb len: %04x\n - pdu header and data for " 75162306a36Sopenharmony_ci "up to 32 bytes sent to vtam:\n", 75262306a36Sopenharmony_ci __func__, dev->name, skb->len); 75362306a36Sopenharmony_ci CTCM_D3_DUMP((char *)skb->data, min_t(int, 32, skb->len)); 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_ci ch->ccw[4].count = skb->len; 75662306a36Sopenharmony_ci if (set_normalized_cda(&ch->ccw[4], skb->data)) { 75762306a36Sopenharmony_ci /* 75862306a36Sopenharmony_ci * idal allocation failed, try via copying to trans_skb. 75962306a36Sopenharmony_ci * trans_skb usually has a pre-allocated idal. 76062306a36Sopenharmony_ci */ 76162306a36Sopenharmony_ci if (ctcm_checkalloc_buffer(ch)) { 76262306a36Sopenharmony_ci /* 76362306a36Sopenharmony_ci * Remove our header. 76462306a36Sopenharmony_ci * It gets added again on retransmit. 76562306a36Sopenharmony_ci */ 76662306a36Sopenharmony_ci goto nomem_exit; 76762306a36Sopenharmony_ci } 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci skb_reset_tail_pointer(ch->trans_skb); 77062306a36Sopenharmony_ci ch->trans_skb->len = 0; 77162306a36Sopenharmony_ci ch->ccw[1].count = skb->len; 77262306a36Sopenharmony_ci skb_put_data(ch->trans_skb, skb->data, skb->len); 77362306a36Sopenharmony_ci refcount_dec(&skb->users); 77462306a36Sopenharmony_ci dev_kfree_skb_irq(skb); 77562306a36Sopenharmony_ci ccw_idx = 0; 77662306a36Sopenharmony_ci CTCM_PR_DBGDATA("%s(%s): trans_skb len: %04x\n" 77762306a36Sopenharmony_ci "up to 32 bytes sent to vtam:\n", 77862306a36Sopenharmony_ci __func__, dev->name, ch->trans_skb->len); 77962306a36Sopenharmony_ci CTCM_D3_DUMP((char *)ch->trans_skb->data, 78062306a36Sopenharmony_ci min_t(int, 32, ch->trans_skb->len)); 78162306a36Sopenharmony_ci } else { 78262306a36Sopenharmony_ci skb_queue_tail(&ch->io_queue, skb); 78362306a36Sopenharmony_ci ccw_idx = 3; 78462306a36Sopenharmony_ci } 78562306a36Sopenharmony_ci ch->retry = 0; 78662306a36Sopenharmony_ci fsm_newstate(ch->fsm, CTC_STATE_TX); 78762306a36Sopenharmony_ci fsm_addtimer(&ch->timer, CTCM_TIME_5_SEC, CTC_EVENT_TIMER, ch); 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_ci if (do_debug_ccw) 79062306a36Sopenharmony_ci ctcmpc_dumpit((char *)&ch->ccw[ccw_idx], 79162306a36Sopenharmony_ci sizeof(struct ccw1) * 3); 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_ci spin_lock_irqsave(get_ccwdev_lock(ch->cdev), saveflags); 79462306a36Sopenharmony_ci ch->prof.send_stamp = jiffies; 79562306a36Sopenharmony_ci rc = ccw_device_start(ch->cdev, &ch->ccw[ccw_idx], 0, 0xff, 0); 79662306a36Sopenharmony_ci spin_unlock_irqrestore(get_ccwdev_lock(ch->cdev), saveflags); 79762306a36Sopenharmony_ci if (ccw_idx == 3) 79862306a36Sopenharmony_ci ch->prof.doios_single++; 79962306a36Sopenharmony_ci if (rc != 0) { 80062306a36Sopenharmony_ci fsm_deltimer(&ch->timer); 80162306a36Sopenharmony_ci ctcm_ccw_check_rc(ch, rc, "single skb TX"); 80262306a36Sopenharmony_ci if (ccw_idx == 3) 80362306a36Sopenharmony_ci skb_dequeue_tail(&ch->io_queue); 80462306a36Sopenharmony_ci } else if (ccw_idx == 0) { 80562306a36Sopenharmony_ci priv->stats.tx_packets++; 80662306a36Sopenharmony_ci priv->stats.tx_bytes += skb->len - TH_HEADER_LENGTH; 80762306a36Sopenharmony_ci } 80862306a36Sopenharmony_ci if (ch->th_seq_num > 0xf0000000) /* Chose at random. */ 80962306a36Sopenharmony_ci ctcmpc_send_sweep_req(ch); 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_ci goto done; 81262306a36Sopenharmony_cinomem_exit: 81362306a36Sopenharmony_ci CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_CRIT, 81462306a36Sopenharmony_ci "%s(%s): MEMORY allocation ERROR\n", 81562306a36Sopenharmony_ci CTCM_FUNTAIL, ch->id); 81662306a36Sopenharmony_ci rc = -ENOMEM; 81762306a36Sopenharmony_ci refcount_dec(&skb->users); 81862306a36Sopenharmony_ci dev_kfree_skb_any(skb); 81962306a36Sopenharmony_ci fsm_event(priv->mpcg->fsm, MPCG_EVENT_INOP, dev); 82062306a36Sopenharmony_cidone: 82162306a36Sopenharmony_ci CTCM_PR_DEBUG("Exit %s(%s)\n", __func__, dev->name); 82262306a36Sopenharmony_ci return rc; 82362306a36Sopenharmony_ci} 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_ci/* 82662306a36Sopenharmony_ci * Start transmission of a packet. 82762306a36Sopenharmony_ci * Called from generic network device layer. 82862306a36Sopenharmony_ci */ 82962306a36Sopenharmony_ci/* first merge version - leaving both functions separated */ 83062306a36Sopenharmony_cistatic netdev_tx_t ctcm_tx(struct sk_buff *skb, struct net_device *dev) 83162306a36Sopenharmony_ci{ 83262306a36Sopenharmony_ci struct ctcm_priv *priv = dev->ml_priv; 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_ci if (skb == NULL) { 83562306a36Sopenharmony_ci CTCM_DBF_TEXT_(ERROR, CTC_DBF_ERROR, 83662306a36Sopenharmony_ci "%s(%s): NULL sk_buff passed", 83762306a36Sopenharmony_ci CTCM_FUNTAIL, dev->name); 83862306a36Sopenharmony_ci priv->stats.tx_dropped++; 83962306a36Sopenharmony_ci return NETDEV_TX_OK; 84062306a36Sopenharmony_ci } 84162306a36Sopenharmony_ci if (skb_headroom(skb) < (LL_HEADER_LENGTH + 2)) { 84262306a36Sopenharmony_ci CTCM_DBF_TEXT_(ERROR, CTC_DBF_ERROR, 84362306a36Sopenharmony_ci "%s(%s): Got sk_buff with head room < %ld bytes", 84462306a36Sopenharmony_ci CTCM_FUNTAIL, dev->name, LL_HEADER_LENGTH + 2); 84562306a36Sopenharmony_ci dev_kfree_skb(skb); 84662306a36Sopenharmony_ci priv->stats.tx_dropped++; 84762306a36Sopenharmony_ci return NETDEV_TX_OK; 84862306a36Sopenharmony_ci } 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_ci /* 85162306a36Sopenharmony_ci * If channels are not running, try to restart them 85262306a36Sopenharmony_ci * and throw away packet. 85362306a36Sopenharmony_ci */ 85462306a36Sopenharmony_ci if (fsm_getstate(priv->fsm) != DEV_STATE_RUNNING) { 85562306a36Sopenharmony_ci fsm_event(priv->fsm, DEV_EVENT_START, dev); 85662306a36Sopenharmony_ci dev_kfree_skb(skb); 85762306a36Sopenharmony_ci priv->stats.tx_dropped++; 85862306a36Sopenharmony_ci priv->stats.tx_errors++; 85962306a36Sopenharmony_ci priv->stats.tx_carrier_errors++; 86062306a36Sopenharmony_ci return NETDEV_TX_OK; 86162306a36Sopenharmony_ci } 86262306a36Sopenharmony_ci 86362306a36Sopenharmony_ci if (ctcm_test_and_set_busy(dev)) 86462306a36Sopenharmony_ci return NETDEV_TX_BUSY; 86562306a36Sopenharmony_ci 86662306a36Sopenharmony_ci netif_trans_update(dev); 86762306a36Sopenharmony_ci if (ctcm_transmit_skb(priv->channel[CTCM_WRITE], skb) != 0) 86862306a36Sopenharmony_ci return NETDEV_TX_BUSY; 86962306a36Sopenharmony_ci return NETDEV_TX_OK; 87062306a36Sopenharmony_ci} 87162306a36Sopenharmony_ci 87262306a36Sopenharmony_ci/* unmerged MPC variant of ctcm_tx */ 87362306a36Sopenharmony_cistatic netdev_tx_t ctcmpc_tx(struct sk_buff *skb, struct net_device *dev) 87462306a36Sopenharmony_ci{ 87562306a36Sopenharmony_ci int len = 0; 87662306a36Sopenharmony_ci struct ctcm_priv *priv = dev->ml_priv; 87762306a36Sopenharmony_ci struct mpc_group *grp = priv->mpcg; 87862306a36Sopenharmony_ci struct sk_buff *newskb = NULL; 87962306a36Sopenharmony_ci 88062306a36Sopenharmony_ci /* 88162306a36Sopenharmony_ci * Some sanity checks ... 88262306a36Sopenharmony_ci */ 88362306a36Sopenharmony_ci if (skb == NULL) { 88462306a36Sopenharmony_ci CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR, 88562306a36Sopenharmony_ci "%s(%s): NULL sk_buff passed", 88662306a36Sopenharmony_ci CTCM_FUNTAIL, dev->name); 88762306a36Sopenharmony_ci priv->stats.tx_dropped++; 88862306a36Sopenharmony_ci goto done; 88962306a36Sopenharmony_ci } 89062306a36Sopenharmony_ci if (skb_headroom(skb) < (TH_HEADER_LENGTH + PDU_HEADER_LENGTH)) { 89162306a36Sopenharmony_ci CTCM_DBF_TEXT_(MPC_TRACE, CTC_DBF_ERROR, 89262306a36Sopenharmony_ci "%s(%s): Got sk_buff with head room < %ld bytes", 89362306a36Sopenharmony_ci CTCM_FUNTAIL, dev->name, 89462306a36Sopenharmony_ci TH_HEADER_LENGTH + PDU_HEADER_LENGTH); 89562306a36Sopenharmony_ci 89662306a36Sopenharmony_ci CTCM_D3_DUMP((char *)skb->data, min_t(int, 32, skb->len)); 89762306a36Sopenharmony_ci 89862306a36Sopenharmony_ci len = skb->len + TH_HEADER_LENGTH + PDU_HEADER_LENGTH; 89962306a36Sopenharmony_ci newskb = __dev_alloc_skb(len, GFP_ATOMIC | GFP_DMA); 90062306a36Sopenharmony_ci 90162306a36Sopenharmony_ci if (!newskb) { 90262306a36Sopenharmony_ci CTCM_DBF_TEXT_(MPC_TRACE, CTC_DBF_ERROR, 90362306a36Sopenharmony_ci "%s: %s: __dev_alloc_skb failed", 90462306a36Sopenharmony_ci __func__, dev->name); 90562306a36Sopenharmony_ci 90662306a36Sopenharmony_ci dev_kfree_skb_any(skb); 90762306a36Sopenharmony_ci priv->stats.tx_dropped++; 90862306a36Sopenharmony_ci priv->stats.tx_errors++; 90962306a36Sopenharmony_ci priv->stats.tx_carrier_errors++; 91062306a36Sopenharmony_ci fsm_event(grp->fsm, MPCG_EVENT_INOP, dev); 91162306a36Sopenharmony_ci goto done; 91262306a36Sopenharmony_ci } 91362306a36Sopenharmony_ci newskb->protocol = skb->protocol; 91462306a36Sopenharmony_ci skb_reserve(newskb, TH_HEADER_LENGTH + PDU_HEADER_LENGTH); 91562306a36Sopenharmony_ci skb_put_data(newskb, skb->data, skb->len); 91662306a36Sopenharmony_ci dev_kfree_skb_any(skb); 91762306a36Sopenharmony_ci skb = newskb; 91862306a36Sopenharmony_ci } 91962306a36Sopenharmony_ci 92062306a36Sopenharmony_ci /* 92162306a36Sopenharmony_ci * If channels are not running, 92262306a36Sopenharmony_ci * notify anybody about a link failure and throw 92362306a36Sopenharmony_ci * away packet. 92462306a36Sopenharmony_ci */ 92562306a36Sopenharmony_ci if ((fsm_getstate(priv->fsm) != DEV_STATE_RUNNING) || 92662306a36Sopenharmony_ci (fsm_getstate(grp->fsm) < MPCG_STATE_XID2INITW)) { 92762306a36Sopenharmony_ci dev_kfree_skb_any(skb); 92862306a36Sopenharmony_ci CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR, 92962306a36Sopenharmony_ci "%s(%s): inactive MPCGROUP - dropped", 93062306a36Sopenharmony_ci CTCM_FUNTAIL, dev->name); 93162306a36Sopenharmony_ci priv->stats.tx_dropped++; 93262306a36Sopenharmony_ci priv->stats.tx_errors++; 93362306a36Sopenharmony_ci priv->stats.tx_carrier_errors++; 93462306a36Sopenharmony_ci goto done; 93562306a36Sopenharmony_ci } 93662306a36Sopenharmony_ci 93762306a36Sopenharmony_ci if (ctcm_test_and_set_busy(dev)) { 93862306a36Sopenharmony_ci CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR, 93962306a36Sopenharmony_ci "%s(%s): device busy - dropped", 94062306a36Sopenharmony_ci CTCM_FUNTAIL, dev->name); 94162306a36Sopenharmony_ci dev_kfree_skb_any(skb); 94262306a36Sopenharmony_ci priv->stats.tx_dropped++; 94362306a36Sopenharmony_ci priv->stats.tx_errors++; 94462306a36Sopenharmony_ci priv->stats.tx_carrier_errors++; 94562306a36Sopenharmony_ci fsm_event(grp->fsm, MPCG_EVENT_INOP, dev); 94662306a36Sopenharmony_ci goto done; 94762306a36Sopenharmony_ci } 94862306a36Sopenharmony_ci 94962306a36Sopenharmony_ci netif_trans_update(dev); 95062306a36Sopenharmony_ci if (ctcmpc_transmit_skb(priv->channel[CTCM_WRITE], skb) != 0) { 95162306a36Sopenharmony_ci CTCM_DBF_TEXT_(MPC_ERROR, CTC_DBF_ERROR, 95262306a36Sopenharmony_ci "%s(%s): device error - dropped", 95362306a36Sopenharmony_ci CTCM_FUNTAIL, dev->name); 95462306a36Sopenharmony_ci dev_kfree_skb_any(skb); 95562306a36Sopenharmony_ci priv->stats.tx_dropped++; 95662306a36Sopenharmony_ci priv->stats.tx_errors++; 95762306a36Sopenharmony_ci priv->stats.tx_carrier_errors++; 95862306a36Sopenharmony_ci ctcm_clear_busy(dev); 95962306a36Sopenharmony_ci fsm_event(grp->fsm, MPCG_EVENT_INOP, dev); 96062306a36Sopenharmony_ci goto done; 96162306a36Sopenharmony_ci } 96262306a36Sopenharmony_ci ctcm_clear_busy(dev); 96362306a36Sopenharmony_cidone: 96462306a36Sopenharmony_ci if (do_debug) 96562306a36Sopenharmony_ci MPC_DBF_DEV_NAME(TRACE, dev, "exit"); 96662306a36Sopenharmony_ci 96762306a36Sopenharmony_ci return NETDEV_TX_OK; /* handle freeing of skb here */ 96862306a36Sopenharmony_ci} 96962306a36Sopenharmony_ci 97062306a36Sopenharmony_ci 97162306a36Sopenharmony_ci/* 97262306a36Sopenharmony_ci * Sets MTU of an interface. 97362306a36Sopenharmony_ci * 97462306a36Sopenharmony_ci * dev Pointer to interface struct. 97562306a36Sopenharmony_ci * new_mtu The new MTU to use for this interface. 97662306a36Sopenharmony_ci * 97762306a36Sopenharmony_ci * returns 0 on success, -EINVAL if MTU is out of valid range. 97862306a36Sopenharmony_ci * (valid range is 576 .. 65527). If VM is on the 97962306a36Sopenharmony_ci * remote side, maximum MTU is 32760, however this is 98062306a36Sopenharmony_ci * not checked here. 98162306a36Sopenharmony_ci */ 98262306a36Sopenharmony_cistatic int ctcm_change_mtu(struct net_device *dev, int new_mtu) 98362306a36Sopenharmony_ci{ 98462306a36Sopenharmony_ci struct ctcm_priv *priv; 98562306a36Sopenharmony_ci int max_bufsize; 98662306a36Sopenharmony_ci 98762306a36Sopenharmony_ci priv = dev->ml_priv; 98862306a36Sopenharmony_ci max_bufsize = priv->channel[CTCM_READ]->max_bufsize; 98962306a36Sopenharmony_ci 99062306a36Sopenharmony_ci if (IS_MPC(priv)) { 99162306a36Sopenharmony_ci if (new_mtu > max_bufsize - TH_HEADER_LENGTH) 99262306a36Sopenharmony_ci return -EINVAL; 99362306a36Sopenharmony_ci dev->hard_header_len = TH_HEADER_LENGTH + PDU_HEADER_LENGTH; 99462306a36Sopenharmony_ci } else { 99562306a36Sopenharmony_ci if (new_mtu > max_bufsize - LL_HEADER_LENGTH - 2) 99662306a36Sopenharmony_ci return -EINVAL; 99762306a36Sopenharmony_ci dev->hard_header_len = LL_HEADER_LENGTH + 2; 99862306a36Sopenharmony_ci } 99962306a36Sopenharmony_ci dev->mtu = new_mtu; 100062306a36Sopenharmony_ci return 0; 100162306a36Sopenharmony_ci} 100262306a36Sopenharmony_ci 100362306a36Sopenharmony_ci/* 100462306a36Sopenharmony_ci * Returns interface statistics of a device. 100562306a36Sopenharmony_ci * 100662306a36Sopenharmony_ci * dev Pointer to interface struct. 100762306a36Sopenharmony_ci * 100862306a36Sopenharmony_ci * returns Pointer to stats struct of this interface. 100962306a36Sopenharmony_ci */ 101062306a36Sopenharmony_cistatic struct net_device_stats *ctcm_stats(struct net_device *dev) 101162306a36Sopenharmony_ci{ 101262306a36Sopenharmony_ci return &((struct ctcm_priv *)dev->ml_priv)->stats; 101362306a36Sopenharmony_ci} 101462306a36Sopenharmony_ci 101562306a36Sopenharmony_cistatic void ctcm_free_netdevice(struct net_device *dev) 101662306a36Sopenharmony_ci{ 101762306a36Sopenharmony_ci struct ctcm_priv *priv; 101862306a36Sopenharmony_ci struct mpc_group *grp; 101962306a36Sopenharmony_ci 102062306a36Sopenharmony_ci CTCM_DBF_TEXT_(SETUP, CTC_DBF_INFO, 102162306a36Sopenharmony_ci "%s(%s)", CTCM_FUNTAIL, dev->name); 102262306a36Sopenharmony_ci priv = dev->ml_priv; 102362306a36Sopenharmony_ci if (priv) { 102462306a36Sopenharmony_ci grp = priv->mpcg; 102562306a36Sopenharmony_ci if (grp) { 102662306a36Sopenharmony_ci if (grp->fsm) 102762306a36Sopenharmony_ci kfree_fsm(grp->fsm); 102862306a36Sopenharmony_ci dev_kfree_skb(grp->xid_skb); 102962306a36Sopenharmony_ci dev_kfree_skb(grp->rcvd_xid_skb); 103062306a36Sopenharmony_ci tasklet_kill(&grp->mpc_tasklet2); 103162306a36Sopenharmony_ci kfree(grp); 103262306a36Sopenharmony_ci priv->mpcg = NULL; 103362306a36Sopenharmony_ci } 103462306a36Sopenharmony_ci if (priv->fsm) { 103562306a36Sopenharmony_ci kfree_fsm(priv->fsm); 103662306a36Sopenharmony_ci priv->fsm = NULL; 103762306a36Sopenharmony_ci } 103862306a36Sopenharmony_ci kfree(priv->xid); 103962306a36Sopenharmony_ci priv->xid = NULL; 104062306a36Sopenharmony_ci /* 104162306a36Sopenharmony_ci * Note: kfree(priv); is done in "opposite" function of 104262306a36Sopenharmony_ci * allocator function probe_device which is remove_device. 104362306a36Sopenharmony_ci */ 104462306a36Sopenharmony_ci } 104562306a36Sopenharmony_ci#ifdef MODULE 104662306a36Sopenharmony_ci free_netdev(dev); 104762306a36Sopenharmony_ci#endif 104862306a36Sopenharmony_ci} 104962306a36Sopenharmony_ci 105062306a36Sopenharmony_cistruct mpc_group *ctcmpc_init_mpc_group(struct ctcm_priv *priv); 105162306a36Sopenharmony_ci 105262306a36Sopenharmony_cistatic const struct net_device_ops ctcm_netdev_ops = { 105362306a36Sopenharmony_ci .ndo_open = ctcm_open, 105462306a36Sopenharmony_ci .ndo_stop = ctcm_close, 105562306a36Sopenharmony_ci .ndo_get_stats = ctcm_stats, 105662306a36Sopenharmony_ci .ndo_change_mtu = ctcm_change_mtu, 105762306a36Sopenharmony_ci .ndo_start_xmit = ctcm_tx, 105862306a36Sopenharmony_ci}; 105962306a36Sopenharmony_ci 106062306a36Sopenharmony_cistatic const struct net_device_ops ctcm_mpc_netdev_ops = { 106162306a36Sopenharmony_ci .ndo_open = ctcm_open, 106262306a36Sopenharmony_ci .ndo_stop = ctcm_close, 106362306a36Sopenharmony_ci .ndo_get_stats = ctcm_stats, 106462306a36Sopenharmony_ci .ndo_change_mtu = ctcm_change_mtu, 106562306a36Sopenharmony_ci .ndo_start_xmit = ctcmpc_tx, 106662306a36Sopenharmony_ci}; 106762306a36Sopenharmony_ci 106862306a36Sopenharmony_cistatic void ctcm_dev_setup(struct net_device *dev) 106962306a36Sopenharmony_ci{ 107062306a36Sopenharmony_ci dev->type = ARPHRD_SLIP; 107162306a36Sopenharmony_ci dev->tx_queue_len = 100; 107262306a36Sopenharmony_ci dev->flags = IFF_POINTOPOINT | IFF_NOARP; 107362306a36Sopenharmony_ci dev->min_mtu = 576; 107462306a36Sopenharmony_ci dev->max_mtu = 65527; 107562306a36Sopenharmony_ci} 107662306a36Sopenharmony_ci 107762306a36Sopenharmony_ci/* 107862306a36Sopenharmony_ci * Initialize everything of the net device except the name and the 107962306a36Sopenharmony_ci * channel structs. 108062306a36Sopenharmony_ci */ 108162306a36Sopenharmony_cistatic struct net_device *ctcm_init_netdevice(struct ctcm_priv *priv) 108262306a36Sopenharmony_ci{ 108362306a36Sopenharmony_ci struct net_device *dev; 108462306a36Sopenharmony_ci struct mpc_group *grp; 108562306a36Sopenharmony_ci if (!priv) 108662306a36Sopenharmony_ci return NULL; 108762306a36Sopenharmony_ci 108862306a36Sopenharmony_ci if (IS_MPC(priv)) 108962306a36Sopenharmony_ci dev = alloc_netdev(0, MPC_DEVICE_GENE, NET_NAME_UNKNOWN, 109062306a36Sopenharmony_ci ctcm_dev_setup); 109162306a36Sopenharmony_ci else 109262306a36Sopenharmony_ci dev = alloc_netdev(0, CTC_DEVICE_GENE, NET_NAME_UNKNOWN, 109362306a36Sopenharmony_ci ctcm_dev_setup); 109462306a36Sopenharmony_ci 109562306a36Sopenharmony_ci if (!dev) { 109662306a36Sopenharmony_ci CTCM_DBF_TEXT_(ERROR, CTC_DBF_CRIT, 109762306a36Sopenharmony_ci "%s: MEMORY allocation ERROR", 109862306a36Sopenharmony_ci CTCM_FUNTAIL); 109962306a36Sopenharmony_ci return NULL; 110062306a36Sopenharmony_ci } 110162306a36Sopenharmony_ci dev->ml_priv = priv; 110262306a36Sopenharmony_ci priv->fsm = init_fsm("ctcmdev", dev_state_names, dev_event_names, 110362306a36Sopenharmony_ci CTCM_NR_DEV_STATES, CTCM_NR_DEV_EVENTS, 110462306a36Sopenharmony_ci dev_fsm, dev_fsm_len, GFP_KERNEL); 110562306a36Sopenharmony_ci if (priv->fsm == NULL) { 110662306a36Sopenharmony_ci CTCMY_DBF_DEV(SETUP, dev, "init_fsm error"); 110762306a36Sopenharmony_ci free_netdev(dev); 110862306a36Sopenharmony_ci return NULL; 110962306a36Sopenharmony_ci } 111062306a36Sopenharmony_ci fsm_newstate(priv->fsm, DEV_STATE_STOPPED); 111162306a36Sopenharmony_ci fsm_settimer(priv->fsm, &priv->restart_timer); 111262306a36Sopenharmony_ci 111362306a36Sopenharmony_ci if (IS_MPC(priv)) { 111462306a36Sopenharmony_ci /* MPC Group Initializations */ 111562306a36Sopenharmony_ci grp = ctcmpc_init_mpc_group(priv); 111662306a36Sopenharmony_ci if (grp == NULL) { 111762306a36Sopenharmony_ci MPC_DBF_DEV(SETUP, dev, "init_mpc_group error"); 111862306a36Sopenharmony_ci free_netdev(dev); 111962306a36Sopenharmony_ci return NULL; 112062306a36Sopenharmony_ci } 112162306a36Sopenharmony_ci tasklet_init(&grp->mpc_tasklet2, 112262306a36Sopenharmony_ci mpc_group_ready, (unsigned long)dev); 112362306a36Sopenharmony_ci dev->mtu = MPC_BUFSIZE_DEFAULT - 112462306a36Sopenharmony_ci TH_HEADER_LENGTH - PDU_HEADER_LENGTH; 112562306a36Sopenharmony_ci 112662306a36Sopenharmony_ci dev->netdev_ops = &ctcm_mpc_netdev_ops; 112762306a36Sopenharmony_ci dev->hard_header_len = TH_HEADER_LENGTH + PDU_HEADER_LENGTH; 112862306a36Sopenharmony_ci priv->buffer_size = MPC_BUFSIZE_DEFAULT; 112962306a36Sopenharmony_ci } else { 113062306a36Sopenharmony_ci dev->mtu = CTCM_BUFSIZE_DEFAULT - LL_HEADER_LENGTH - 2; 113162306a36Sopenharmony_ci dev->netdev_ops = &ctcm_netdev_ops; 113262306a36Sopenharmony_ci dev->hard_header_len = LL_HEADER_LENGTH + 2; 113362306a36Sopenharmony_ci } 113462306a36Sopenharmony_ci 113562306a36Sopenharmony_ci CTCMY_DBF_DEV(SETUP, dev, "finished"); 113662306a36Sopenharmony_ci 113762306a36Sopenharmony_ci return dev; 113862306a36Sopenharmony_ci} 113962306a36Sopenharmony_ci 114062306a36Sopenharmony_ci/* 114162306a36Sopenharmony_ci * Main IRQ handler. 114262306a36Sopenharmony_ci * 114362306a36Sopenharmony_ci * cdev The ccw_device the interrupt is for. 114462306a36Sopenharmony_ci * intparm interruption parameter. 114562306a36Sopenharmony_ci * irb interruption response block. 114662306a36Sopenharmony_ci */ 114762306a36Sopenharmony_cistatic void ctcm_irq_handler(struct ccw_device *cdev, 114862306a36Sopenharmony_ci unsigned long intparm, struct irb *irb) 114962306a36Sopenharmony_ci{ 115062306a36Sopenharmony_ci struct channel *ch; 115162306a36Sopenharmony_ci struct net_device *dev; 115262306a36Sopenharmony_ci struct ctcm_priv *priv; 115362306a36Sopenharmony_ci struct ccwgroup_device *cgdev; 115462306a36Sopenharmony_ci int cstat; 115562306a36Sopenharmony_ci int dstat; 115662306a36Sopenharmony_ci 115762306a36Sopenharmony_ci CTCM_DBF_TEXT_(TRACE, CTC_DBF_DEBUG, 115862306a36Sopenharmony_ci "Enter %s(%s)", CTCM_FUNTAIL, dev_name(&cdev->dev)); 115962306a36Sopenharmony_ci 116062306a36Sopenharmony_ci if (ctcm_check_irb_error(cdev, irb)) 116162306a36Sopenharmony_ci return; 116262306a36Sopenharmony_ci 116362306a36Sopenharmony_ci cgdev = dev_get_drvdata(&cdev->dev); 116462306a36Sopenharmony_ci 116562306a36Sopenharmony_ci cstat = irb->scsw.cmd.cstat; 116662306a36Sopenharmony_ci dstat = irb->scsw.cmd.dstat; 116762306a36Sopenharmony_ci 116862306a36Sopenharmony_ci /* Check for unsolicited interrupts. */ 116962306a36Sopenharmony_ci if (cgdev == NULL) { 117062306a36Sopenharmony_ci CTCM_DBF_TEXT_(TRACE, CTC_DBF_ERROR, 117162306a36Sopenharmony_ci "%s(%s) unsolicited irq: c-%02x d-%02x\n", 117262306a36Sopenharmony_ci CTCM_FUNTAIL, dev_name(&cdev->dev), cstat, dstat); 117362306a36Sopenharmony_ci dev_warn(&cdev->dev, 117462306a36Sopenharmony_ci "The adapter received a non-specific IRQ\n"); 117562306a36Sopenharmony_ci return; 117662306a36Sopenharmony_ci } 117762306a36Sopenharmony_ci 117862306a36Sopenharmony_ci priv = dev_get_drvdata(&cgdev->dev); 117962306a36Sopenharmony_ci 118062306a36Sopenharmony_ci /* Try to extract channel from driver data. */ 118162306a36Sopenharmony_ci if (priv->channel[CTCM_READ]->cdev == cdev) 118262306a36Sopenharmony_ci ch = priv->channel[CTCM_READ]; 118362306a36Sopenharmony_ci else if (priv->channel[CTCM_WRITE]->cdev == cdev) 118462306a36Sopenharmony_ci ch = priv->channel[CTCM_WRITE]; 118562306a36Sopenharmony_ci else { 118662306a36Sopenharmony_ci dev_err(&cdev->dev, 118762306a36Sopenharmony_ci "%s: Internal error: Can't determine channel for " 118862306a36Sopenharmony_ci "interrupt device %s\n", 118962306a36Sopenharmony_ci __func__, dev_name(&cdev->dev)); 119062306a36Sopenharmony_ci /* Explain: inconsistent internal structures */ 119162306a36Sopenharmony_ci return; 119262306a36Sopenharmony_ci } 119362306a36Sopenharmony_ci 119462306a36Sopenharmony_ci dev = ch->netdev; 119562306a36Sopenharmony_ci if (dev == NULL) { 119662306a36Sopenharmony_ci dev_err(&cdev->dev, 119762306a36Sopenharmony_ci "%s Internal error: net_device is NULL, ch = 0x%p\n", 119862306a36Sopenharmony_ci __func__, ch); 119962306a36Sopenharmony_ci /* Explain: inconsistent internal structures */ 120062306a36Sopenharmony_ci return; 120162306a36Sopenharmony_ci } 120262306a36Sopenharmony_ci 120362306a36Sopenharmony_ci /* Copy interruption response block. */ 120462306a36Sopenharmony_ci memcpy(ch->irb, irb, sizeof(struct irb)); 120562306a36Sopenharmony_ci 120662306a36Sopenharmony_ci /* Issue error message and return on subchannel error code */ 120762306a36Sopenharmony_ci if (irb->scsw.cmd.cstat) { 120862306a36Sopenharmony_ci fsm_event(ch->fsm, CTC_EVENT_SC_UNKNOWN, ch); 120962306a36Sopenharmony_ci CTCM_DBF_TEXT_(TRACE, CTC_DBF_WARN, 121062306a36Sopenharmony_ci "%s(%s): sub-ch check %s: cs=%02x ds=%02x", 121162306a36Sopenharmony_ci CTCM_FUNTAIL, dev->name, ch->id, cstat, dstat); 121262306a36Sopenharmony_ci dev_warn(&cdev->dev, 121362306a36Sopenharmony_ci "A check occurred on the subchannel\n"); 121462306a36Sopenharmony_ci return; 121562306a36Sopenharmony_ci } 121662306a36Sopenharmony_ci 121762306a36Sopenharmony_ci /* Check the reason-code of a unit check */ 121862306a36Sopenharmony_ci if (irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK) { 121962306a36Sopenharmony_ci if ((irb->ecw[0] & ch->sense_rc) == 0) 122062306a36Sopenharmony_ci /* print it only once */ 122162306a36Sopenharmony_ci CTCM_DBF_TEXT_(TRACE, CTC_DBF_WARN, 122262306a36Sopenharmony_ci "%s(%s): sense=%02x, ds=%02x", 122362306a36Sopenharmony_ci CTCM_FUNTAIL, ch->id, irb->ecw[0], dstat); 122462306a36Sopenharmony_ci ccw_unit_check(ch, irb->ecw[0]); 122562306a36Sopenharmony_ci return; 122662306a36Sopenharmony_ci } 122762306a36Sopenharmony_ci if (irb->scsw.cmd.dstat & DEV_STAT_BUSY) { 122862306a36Sopenharmony_ci if (irb->scsw.cmd.dstat & DEV_STAT_ATTENTION) 122962306a36Sopenharmony_ci fsm_event(ch->fsm, CTC_EVENT_ATTNBUSY, ch); 123062306a36Sopenharmony_ci else 123162306a36Sopenharmony_ci fsm_event(ch->fsm, CTC_EVENT_BUSY, ch); 123262306a36Sopenharmony_ci return; 123362306a36Sopenharmony_ci } 123462306a36Sopenharmony_ci if (irb->scsw.cmd.dstat & DEV_STAT_ATTENTION) { 123562306a36Sopenharmony_ci fsm_event(ch->fsm, CTC_EVENT_ATTN, ch); 123662306a36Sopenharmony_ci return; 123762306a36Sopenharmony_ci } 123862306a36Sopenharmony_ci if ((irb->scsw.cmd.stctl & SCSW_STCTL_SEC_STATUS) || 123962306a36Sopenharmony_ci (irb->scsw.cmd.stctl == SCSW_STCTL_STATUS_PEND) || 124062306a36Sopenharmony_ci (irb->scsw.cmd.stctl == 124162306a36Sopenharmony_ci (SCSW_STCTL_ALERT_STATUS | SCSW_STCTL_STATUS_PEND))) 124262306a36Sopenharmony_ci fsm_event(ch->fsm, CTC_EVENT_FINSTAT, ch); 124362306a36Sopenharmony_ci else 124462306a36Sopenharmony_ci fsm_event(ch->fsm, CTC_EVENT_IRQ, ch); 124562306a36Sopenharmony_ci 124662306a36Sopenharmony_ci} 124762306a36Sopenharmony_ci 124862306a36Sopenharmony_cistatic const struct device_type ctcm_devtype = { 124962306a36Sopenharmony_ci .name = "ctcm", 125062306a36Sopenharmony_ci .groups = ctcm_attr_groups, 125162306a36Sopenharmony_ci}; 125262306a36Sopenharmony_ci 125362306a36Sopenharmony_ci/* 125462306a36Sopenharmony_ci * Add ctcm specific attributes. 125562306a36Sopenharmony_ci * Add ctcm private data. 125662306a36Sopenharmony_ci * 125762306a36Sopenharmony_ci * cgdev pointer to ccwgroup_device just added 125862306a36Sopenharmony_ci * 125962306a36Sopenharmony_ci * returns 0 on success, !0 on failure. 126062306a36Sopenharmony_ci */ 126162306a36Sopenharmony_cistatic int ctcm_probe_device(struct ccwgroup_device *cgdev) 126262306a36Sopenharmony_ci{ 126362306a36Sopenharmony_ci struct ctcm_priv *priv; 126462306a36Sopenharmony_ci 126562306a36Sopenharmony_ci CTCM_DBF_TEXT_(SETUP, CTC_DBF_INFO, 126662306a36Sopenharmony_ci "%s %p", 126762306a36Sopenharmony_ci __func__, cgdev); 126862306a36Sopenharmony_ci 126962306a36Sopenharmony_ci if (!get_device(&cgdev->dev)) 127062306a36Sopenharmony_ci return -ENODEV; 127162306a36Sopenharmony_ci 127262306a36Sopenharmony_ci priv = kzalloc(sizeof(struct ctcm_priv), GFP_KERNEL); 127362306a36Sopenharmony_ci if (!priv) { 127462306a36Sopenharmony_ci CTCM_DBF_TEXT_(ERROR, CTC_DBF_ERROR, 127562306a36Sopenharmony_ci "%s: memory allocation failure", 127662306a36Sopenharmony_ci CTCM_FUNTAIL); 127762306a36Sopenharmony_ci put_device(&cgdev->dev); 127862306a36Sopenharmony_ci return -ENOMEM; 127962306a36Sopenharmony_ci } 128062306a36Sopenharmony_ci priv->buffer_size = CTCM_BUFSIZE_DEFAULT; 128162306a36Sopenharmony_ci cgdev->cdev[0]->handler = ctcm_irq_handler; 128262306a36Sopenharmony_ci cgdev->cdev[1]->handler = ctcm_irq_handler; 128362306a36Sopenharmony_ci dev_set_drvdata(&cgdev->dev, priv); 128462306a36Sopenharmony_ci cgdev->dev.type = &ctcm_devtype; 128562306a36Sopenharmony_ci 128662306a36Sopenharmony_ci return 0; 128762306a36Sopenharmony_ci} 128862306a36Sopenharmony_ci 128962306a36Sopenharmony_ci/* 129062306a36Sopenharmony_ci * Add a new channel to the list of channels. 129162306a36Sopenharmony_ci * Keeps the channel list sorted. 129262306a36Sopenharmony_ci * 129362306a36Sopenharmony_ci * cdev The ccw_device to be added. 129462306a36Sopenharmony_ci * type The type class of the new channel. 129562306a36Sopenharmony_ci * priv Points to the private data of the ccwgroup_device. 129662306a36Sopenharmony_ci * 129762306a36Sopenharmony_ci * returns 0 on success, !0 on error. 129862306a36Sopenharmony_ci */ 129962306a36Sopenharmony_cistatic int add_channel(struct ccw_device *cdev, enum ctcm_channel_types type, 130062306a36Sopenharmony_ci struct ctcm_priv *priv) 130162306a36Sopenharmony_ci{ 130262306a36Sopenharmony_ci struct channel **c = &channels; 130362306a36Sopenharmony_ci struct channel *ch; 130462306a36Sopenharmony_ci int ccw_num; 130562306a36Sopenharmony_ci int rc = 0; 130662306a36Sopenharmony_ci 130762306a36Sopenharmony_ci CTCM_DBF_TEXT_(SETUP, CTC_DBF_INFO, 130862306a36Sopenharmony_ci "%s(%s), type %d, proto %d", 130962306a36Sopenharmony_ci __func__, dev_name(&cdev->dev), type, priv->protocol); 131062306a36Sopenharmony_ci 131162306a36Sopenharmony_ci ch = kzalloc(sizeof(struct channel), GFP_KERNEL); 131262306a36Sopenharmony_ci if (ch == NULL) 131362306a36Sopenharmony_ci return -ENOMEM; 131462306a36Sopenharmony_ci 131562306a36Sopenharmony_ci ch->protocol = priv->protocol; 131662306a36Sopenharmony_ci if (IS_MPC(priv)) { 131762306a36Sopenharmony_ci ch->discontact_th = kzalloc(TH_HEADER_LENGTH, GFP_KERNEL); 131862306a36Sopenharmony_ci if (ch->discontact_th == NULL) 131962306a36Sopenharmony_ci goto nomem_return; 132062306a36Sopenharmony_ci 132162306a36Sopenharmony_ci ch->discontact_th->th_blk_flag = TH_DISCONTACT; 132262306a36Sopenharmony_ci tasklet_init(&ch->ch_disc_tasklet, 132362306a36Sopenharmony_ci mpc_action_send_discontact, (unsigned long)ch); 132462306a36Sopenharmony_ci 132562306a36Sopenharmony_ci tasklet_init(&ch->ch_tasklet, ctcmpc_bh, (unsigned long)ch); 132662306a36Sopenharmony_ci ch->max_bufsize = (MPC_BUFSIZE_DEFAULT - 35); 132762306a36Sopenharmony_ci ccw_num = 17; 132862306a36Sopenharmony_ci } else 132962306a36Sopenharmony_ci ccw_num = 8; 133062306a36Sopenharmony_ci 133162306a36Sopenharmony_ci ch->ccw = kcalloc(ccw_num, sizeof(struct ccw1), GFP_KERNEL | GFP_DMA); 133262306a36Sopenharmony_ci if (ch->ccw == NULL) 133362306a36Sopenharmony_ci goto nomem_return; 133462306a36Sopenharmony_ci 133562306a36Sopenharmony_ci ch->cdev = cdev; 133662306a36Sopenharmony_ci scnprintf(ch->id, CTCM_ID_SIZE, "ch-%s", dev_name(&cdev->dev)); 133762306a36Sopenharmony_ci ch->type = type; 133862306a36Sopenharmony_ci 133962306a36Sopenharmony_ci /* 134062306a36Sopenharmony_ci * "static" ccws are used in the following way: 134162306a36Sopenharmony_ci * 134262306a36Sopenharmony_ci * ccw[0..2] (Channel program for generic I/O): 134362306a36Sopenharmony_ci * 0: prepare 134462306a36Sopenharmony_ci * 1: read or write (depending on direction) with fixed 134562306a36Sopenharmony_ci * buffer (idal allocated once when buffer is allocated) 134662306a36Sopenharmony_ci * 2: nop 134762306a36Sopenharmony_ci * ccw[3..5] (Channel program for direct write of packets) 134862306a36Sopenharmony_ci * 3: prepare 134962306a36Sopenharmony_ci * 4: write (idal allocated on every write). 135062306a36Sopenharmony_ci * 5: nop 135162306a36Sopenharmony_ci * ccw[6..7] (Channel program for initial channel setup): 135262306a36Sopenharmony_ci * 6: set extended mode 135362306a36Sopenharmony_ci * 7: nop 135462306a36Sopenharmony_ci * 135562306a36Sopenharmony_ci * ch->ccw[0..5] are initialized in ch_action_start because 135662306a36Sopenharmony_ci * the channel's direction is yet unknown here. 135762306a36Sopenharmony_ci * 135862306a36Sopenharmony_ci * ccws used for xid2 negotiations 135962306a36Sopenharmony_ci * ch-ccw[8-14] need to be used for the XID exchange either 136062306a36Sopenharmony_ci * X side XID2 Processing 136162306a36Sopenharmony_ci * 8: write control 136262306a36Sopenharmony_ci * 9: write th 136362306a36Sopenharmony_ci * 10: write XID 136462306a36Sopenharmony_ci * 11: read th from secondary 136562306a36Sopenharmony_ci * 12: read XID from secondary 136662306a36Sopenharmony_ci * 13: read 4 byte ID 136762306a36Sopenharmony_ci * 14: nop 136862306a36Sopenharmony_ci * Y side XID Processing 136962306a36Sopenharmony_ci * 8: sense 137062306a36Sopenharmony_ci * 9: read th 137162306a36Sopenharmony_ci * 10: read XID 137262306a36Sopenharmony_ci * 11: write th 137362306a36Sopenharmony_ci * 12: write XID 137462306a36Sopenharmony_ci * 13: write 4 byte ID 137562306a36Sopenharmony_ci * 14: nop 137662306a36Sopenharmony_ci * 137762306a36Sopenharmony_ci * ccws used for double noop due to VM timing issues 137862306a36Sopenharmony_ci * which result in unrecoverable Busy on channel 137962306a36Sopenharmony_ci * 15: nop 138062306a36Sopenharmony_ci * 16: nop 138162306a36Sopenharmony_ci */ 138262306a36Sopenharmony_ci ch->ccw[6].cmd_code = CCW_CMD_SET_EXTENDED; 138362306a36Sopenharmony_ci ch->ccw[6].flags = CCW_FLAG_SLI; 138462306a36Sopenharmony_ci 138562306a36Sopenharmony_ci ch->ccw[7].cmd_code = CCW_CMD_NOOP; 138662306a36Sopenharmony_ci ch->ccw[7].flags = CCW_FLAG_SLI; 138762306a36Sopenharmony_ci 138862306a36Sopenharmony_ci if (IS_MPC(priv)) { 138962306a36Sopenharmony_ci ch->ccw[15].cmd_code = CCW_CMD_WRITE; 139062306a36Sopenharmony_ci ch->ccw[15].flags = CCW_FLAG_SLI | CCW_FLAG_CC; 139162306a36Sopenharmony_ci ch->ccw[15].count = TH_HEADER_LENGTH; 139262306a36Sopenharmony_ci ch->ccw[15].cda = virt_to_phys(ch->discontact_th); 139362306a36Sopenharmony_ci 139462306a36Sopenharmony_ci ch->ccw[16].cmd_code = CCW_CMD_NOOP; 139562306a36Sopenharmony_ci ch->ccw[16].flags = CCW_FLAG_SLI; 139662306a36Sopenharmony_ci 139762306a36Sopenharmony_ci ch->fsm = init_fsm(ch->id, ctc_ch_state_names, 139862306a36Sopenharmony_ci ctc_ch_event_names, CTC_MPC_NR_STATES, 139962306a36Sopenharmony_ci CTC_MPC_NR_EVENTS, ctcmpc_ch_fsm, 140062306a36Sopenharmony_ci mpc_ch_fsm_len, GFP_KERNEL); 140162306a36Sopenharmony_ci } else { 140262306a36Sopenharmony_ci ch->fsm = init_fsm(ch->id, ctc_ch_state_names, 140362306a36Sopenharmony_ci ctc_ch_event_names, CTC_NR_STATES, 140462306a36Sopenharmony_ci CTC_NR_EVENTS, ch_fsm, 140562306a36Sopenharmony_ci ch_fsm_len, GFP_KERNEL); 140662306a36Sopenharmony_ci } 140762306a36Sopenharmony_ci if (ch->fsm == NULL) 140862306a36Sopenharmony_ci goto nomem_return; 140962306a36Sopenharmony_ci 141062306a36Sopenharmony_ci fsm_newstate(ch->fsm, CTC_STATE_IDLE); 141162306a36Sopenharmony_ci 141262306a36Sopenharmony_ci ch->irb = kzalloc(sizeof(struct irb), GFP_KERNEL); 141362306a36Sopenharmony_ci if (ch->irb == NULL) 141462306a36Sopenharmony_ci goto nomem_return; 141562306a36Sopenharmony_ci 141662306a36Sopenharmony_ci while (*c && ctcm_less_than((*c)->id, ch->id)) 141762306a36Sopenharmony_ci c = &(*c)->next; 141862306a36Sopenharmony_ci 141962306a36Sopenharmony_ci if (*c && (!strncmp((*c)->id, ch->id, CTCM_ID_SIZE))) { 142062306a36Sopenharmony_ci CTCM_DBF_TEXT_(SETUP, CTC_DBF_INFO, 142162306a36Sopenharmony_ci "%s (%s) already in list, using old entry", 142262306a36Sopenharmony_ci __func__, (*c)->id); 142362306a36Sopenharmony_ci 142462306a36Sopenharmony_ci goto free_return; 142562306a36Sopenharmony_ci } 142662306a36Sopenharmony_ci 142762306a36Sopenharmony_ci spin_lock_init(&ch->collect_lock); 142862306a36Sopenharmony_ci 142962306a36Sopenharmony_ci fsm_settimer(ch->fsm, &ch->timer); 143062306a36Sopenharmony_ci skb_queue_head_init(&ch->io_queue); 143162306a36Sopenharmony_ci skb_queue_head_init(&ch->collect_queue); 143262306a36Sopenharmony_ci 143362306a36Sopenharmony_ci if (IS_MPC(priv)) { 143462306a36Sopenharmony_ci fsm_settimer(ch->fsm, &ch->sweep_timer); 143562306a36Sopenharmony_ci skb_queue_head_init(&ch->sweep_queue); 143662306a36Sopenharmony_ci } 143762306a36Sopenharmony_ci ch->next = *c; 143862306a36Sopenharmony_ci *c = ch; 143962306a36Sopenharmony_ci return 0; 144062306a36Sopenharmony_ci 144162306a36Sopenharmony_cinomem_return: 144262306a36Sopenharmony_ci rc = -ENOMEM; 144362306a36Sopenharmony_ci 144462306a36Sopenharmony_cifree_return: /* note that all channel pointers are 0 or valid */ 144562306a36Sopenharmony_ci kfree(ch->ccw); 144662306a36Sopenharmony_ci kfree(ch->discontact_th); 144762306a36Sopenharmony_ci kfree_fsm(ch->fsm); 144862306a36Sopenharmony_ci kfree(ch->irb); 144962306a36Sopenharmony_ci kfree(ch); 145062306a36Sopenharmony_ci return rc; 145162306a36Sopenharmony_ci} 145262306a36Sopenharmony_ci 145362306a36Sopenharmony_ci/* 145462306a36Sopenharmony_ci * Return type of a detected device. 145562306a36Sopenharmony_ci */ 145662306a36Sopenharmony_cistatic enum ctcm_channel_types get_channel_type(struct ccw_device_id *id) 145762306a36Sopenharmony_ci{ 145862306a36Sopenharmony_ci enum ctcm_channel_types type; 145962306a36Sopenharmony_ci type = (enum ctcm_channel_types)id->driver_info; 146062306a36Sopenharmony_ci 146162306a36Sopenharmony_ci if (type == ctcm_channel_type_ficon) 146262306a36Sopenharmony_ci type = ctcm_channel_type_escon; 146362306a36Sopenharmony_ci 146462306a36Sopenharmony_ci return type; 146562306a36Sopenharmony_ci} 146662306a36Sopenharmony_ci 146762306a36Sopenharmony_ci/* 146862306a36Sopenharmony_ci * 146962306a36Sopenharmony_ci * Setup an interface. 147062306a36Sopenharmony_ci * 147162306a36Sopenharmony_ci * cgdev Device to be setup. 147262306a36Sopenharmony_ci * 147362306a36Sopenharmony_ci * returns 0 on success, !0 on failure. 147462306a36Sopenharmony_ci */ 147562306a36Sopenharmony_cistatic int ctcm_new_device(struct ccwgroup_device *cgdev) 147662306a36Sopenharmony_ci{ 147762306a36Sopenharmony_ci char read_id[CTCM_ID_SIZE]; 147862306a36Sopenharmony_ci char write_id[CTCM_ID_SIZE]; 147962306a36Sopenharmony_ci int direction; 148062306a36Sopenharmony_ci enum ctcm_channel_types type; 148162306a36Sopenharmony_ci struct ctcm_priv *priv; 148262306a36Sopenharmony_ci struct net_device *dev; 148362306a36Sopenharmony_ci struct ccw_device *cdev0; 148462306a36Sopenharmony_ci struct ccw_device *cdev1; 148562306a36Sopenharmony_ci struct channel *readc; 148662306a36Sopenharmony_ci struct channel *writec; 148762306a36Sopenharmony_ci int ret; 148862306a36Sopenharmony_ci int result; 148962306a36Sopenharmony_ci 149062306a36Sopenharmony_ci priv = dev_get_drvdata(&cgdev->dev); 149162306a36Sopenharmony_ci if (!priv) { 149262306a36Sopenharmony_ci result = -ENODEV; 149362306a36Sopenharmony_ci goto out_err_result; 149462306a36Sopenharmony_ci } 149562306a36Sopenharmony_ci 149662306a36Sopenharmony_ci cdev0 = cgdev->cdev[0]; 149762306a36Sopenharmony_ci cdev1 = cgdev->cdev[1]; 149862306a36Sopenharmony_ci 149962306a36Sopenharmony_ci type = get_channel_type(&cdev0->id); 150062306a36Sopenharmony_ci 150162306a36Sopenharmony_ci scnprintf(read_id, CTCM_ID_SIZE, "ch-%s", dev_name(&cdev0->dev)); 150262306a36Sopenharmony_ci scnprintf(write_id, CTCM_ID_SIZE, "ch-%s", dev_name(&cdev1->dev)); 150362306a36Sopenharmony_ci 150462306a36Sopenharmony_ci ret = add_channel(cdev0, type, priv); 150562306a36Sopenharmony_ci if (ret) { 150662306a36Sopenharmony_ci result = ret; 150762306a36Sopenharmony_ci goto out_err_result; 150862306a36Sopenharmony_ci } 150962306a36Sopenharmony_ci ret = add_channel(cdev1, type, priv); 151062306a36Sopenharmony_ci if (ret) { 151162306a36Sopenharmony_ci result = ret; 151262306a36Sopenharmony_ci goto out_remove_channel1; 151362306a36Sopenharmony_ci } 151462306a36Sopenharmony_ci 151562306a36Sopenharmony_ci ret = ccw_device_set_online(cdev0); 151662306a36Sopenharmony_ci if (ret != 0) { 151762306a36Sopenharmony_ci CTCM_DBF_TEXT_(TRACE, CTC_DBF_NOTICE, 151862306a36Sopenharmony_ci "%s(%s) set_online rc=%d", 151962306a36Sopenharmony_ci CTCM_FUNTAIL, read_id, ret); 152062306a36Sopenharmony_ci result = -EIO; 152162306a36Sopenharmony_ci goto out_remove_channel2; 152262306a36Sopenharmony_ci } 152362306a36Sopenharmony_ci 152462306a36Sopenharmony_ci ret = ccw_device_set_online(cdev1); 152562306a36Sopenharmony_ci if (ret != 0) { 152662306a36Sopenharmony_ci CTCM_DBF_TEXT_(TRACE, CTC_DBF_NOTICE, 152762306a36Sopenharmony_ci "%s(%s) set_online rc=%d", 152862306a36Sopenharmony_ci CTCM_FUNTAIL, write_id, ret); 152962306a36Sopenharmony_ci 153062306a36Sopenharmony_ci result = -EIO; 153162306a36Sopenharmony_ci goto out_ccw1; 153262306a36Sopenharmony_ci } 153362306a36Sopenharmony_ci 153462306a36Sopenharmony_ci dev = ctcm_init_netdevice(priv); 153562306a36Sopenharmony_ci if (dev == NULL) { 153662306a36Sopenharmony_ci result = -ENODEV; 153762306a36Sopenharmony_ci goto out_ccw2; 153862306a36Sopenharmony_ci } 153962306a36Sopenharmony_ci 154062306a36Sopenharmony_ci for (direction = CTCM_READ; direction <= CTCM_WRITE; direction++) { 154162306a36Sopenharmony_ci priv->channel[direction] = 154262306a36Sopenharmony_ci channel_get(type, direction == CTCM_READ ? 154362306a36Sopenharmony_ci read_id : write_id, direction); 154462306a36Sopenharmony_ci if (priv->channel[direction] == NULL) { 154562306a36Sopenharmony_ci if (direction == CTCM_WRITE) 154662306a36Sopenharmony_ci channel_free(priv->channel[CTCM_READ]); 154762306a36Sopenharmony_ci result = -ENODEV; 154862306a36Sopenharmony_ci goto out_dev; 154962306a36Sopenharmony_ci } 155062306a36Sopenharmony_ci priv->channel[direction]->netdev = dev; 155162306a36Sopenharmony_ci priv->channel[direction]->protocol = priv->protocol; 155262306a36Sopenharmony_ci priv->channel[direction]->max_bufsize = priv->buffer_size; 155362306a36Sopenharmony_ci } 155462306a36Sopenharmony_ci /* sysfs magic */ 155562306a36Sopenharmony_ci SET_NETDEV_DEV(dev, &cgdev->dev); 155662306a36Sopenharmony_ci 155762306a36Sopenharmony_ci if (register_netdev(dev)) { 155862306a36Sopenharmony_ci result = -ENODEV; 155962306a36Sopenharmony_ci goto out_dev; 156062306a36Sopenharmony_ci } 156162306a36Sopenharmony_ci 156262306a36Sopenharmony_ci strscpy(priv->fsm->name, dev->name, sizeof(priv->fsm->name)); 156362306a36Sopenharmony_ci 156462306a36Sopenharmony_ci dev_info(&dev->dev, 156562306a36Sopenharmony_ci "setup OK : r/w = %s/%s, protocol : %d\n", 156662306a36Sopenharmony_ci priv->channel[CTCM_READ]->id, 156762306a36Sopenharmony_ci priv->channel[CTCM_WRITE]->id, priv->protocol); 156862306a36Sopenharmony_ci 156962306a36Sopenharmony_ci CTCM_DBF_TEXT_(SETUP, CTC_DBF_INFO, 157062306a36Sopenharmony_ci "setup(%s) OK : r/w = %s/%s, protocol : %d", dev->name, 157162306a36Sopenharmony_ci priv->channel[CTCM_READ]->id, 157262306a36Sopenharmony_ci priv->channel[CTCM_WRITE]->id, priv->protocol); 157362306a36Sopenharmony_ci 157462306a36Sopenharmony_ci return 0; 157562306a36Sopenharmony_ciout_dev: 157662306a36Sopenharmony_ci ctcm_free_netdevice(dev); 157762306a36Sopenharmony_ciout_ccw2: 157862306a36Sopenharmony_ci ccw_device_set_offline(cgdev->cdev[1]); 157962306a36Sopenharmony_ciout_ccw1: 158062306a36Sopenharmony_ci ccw_device_set_offline(cgdev->cdev[0]); 158162306a36Sopenharmony_ciout_remove_channel2: 158262306a36Sopenharmony_ci readc = channel_get(type, read_id, CTCM_READ); 158362306a36Sopenharmony_ci channel_remove(readc); 158462306a36Sopenharmony_ciout_remove_channel1: 158562306a36Sopenharmony_ci writec = channel_get(type, write_id, CTCM_WRITE); 158662306a36Sopenharmony_ci channel_remove(writec); 158762306a36Sopenharmony_ciout_err_result: 158862306a36Sopenharmony_ci return result; 158962306a36Sopenharmony_ci} 159062306a36Sopenharmony_ci 159162306a36Sopenharmony_ci/* 159262306a36Sopenharmony_ci * Shutdown an interface. 159362306a36Sopenharmony_ci * 159462306a36Sopenharmony_ci * cgdev Device to be shut down. 159562306a36Sopenharmony_ci * 159662306a36Sopenharmony_ci * returns 0 on success, !0 on failure. 159762306a36Sopenharmony_ci */ 159862306a36Sopenharmony_cistatic int ctcm_shutdown_device(struct ccwgroup_device *cgdev) 159962306a36Sopenharmony_ci{ 160062306a36Sopenharmony_ci struct ctcm_priv *priv; 160162306a36Sopenharmony_ci struct net_device *dev; 160262306a36Sopenharmony_ci 160362306a36Sopenharmony_ci priv = dev_get_drvdata(&cgdev->dev); 160462306a36Sopenharmony_ci if (!priv) 160562306a36Sopenharmony_ci return -ENODEV; 160662306a36Sopenharmony_ci 160762306a36Sopenharmony_ci if (priv->channel[CTCM_READ]) { 160862306a36Sopenharmony_ci dev = priv->channel[CTCM_READ]->netdev; 160962306a36Sopenharmony_ci CTCM_DBF_DEV(SETUP, dev, ""); 161062306a36Sopenharmony_ci /* Close the device */ 161162306a36Sopenharmony_ci ctcm_close(dev); 161262306a36Sopenharmony_ci dev->flags &= ~IFF_RUNNING; 161362306a36Sopenharmony_ci channel_free(priv->channel[CTCM_READ]); 161462306a36Sopenharmony_ci } else 161562306a36Sopenharmony_ci dev = NULL; 161662306a36Sopenharmony_ci 161762306a36Sopenharmony_ci if (priv->channel[CTCM_WRITE]) 161862306a36Sopenharmony_ci channel_free(priv->channel[CTCM_WRITE]); 161962306a36Sopenharmony_ci 162062306a36Sopenharmony_ci if (dev) { 162162306a36Sopenharmony_ci unregister_netdev(dev); 162262306a36Sopenharmony_ci ctcm_free_netdevice(dev); 162362306a36Sopenharmony_ci } 162462306a36Sopenharmony_ci 162562306a36Sopenharmony_ci if (priv->fsm) 162662306a36Sopenharmony_ci kfree_fsm(priv->fsm); 162762306a36Sopenharmony_ci 162862306a36Sopenharmony_ci ccw_device_set_offline(cgdev->cdev[1]); 162962306a36Sopenharmony_ci ccw_device_set_offline(cgdev->cdev[0]); 163062306a36Sopenharmony_ci channel_remove(priv->channel[CTCM_READ]); 163162306a36Sopenharmony_ci channel_remove(priv->channel[CTCM_WRITE]); 163262306a36Sopenharmony_ci priv->channel[CTCM_READ] = priv->channel[CTCM_WRITE] = NULL; 163362306a36Sopenharmony_ci 163462306a36Sopenharmony_ci return 0; 163562306a36Sopenharmony_ci 163662306a36Sopenharmony_ci} 163762306a36Sopenharmony_ci 163862306a36Sopenharmony_ci 163962306a36Sopenharmony_cistatic void ctcm_remove_device(struct ccwgroup_device *cgdev) 164062306a36Sopenharmony_ci{ 164162306a36Sopenharmony_ci struct ctcm_priv *priv = dev_get_drvdata(&cgdev->dev); 164262306a36Sopenharmony_ci 164362306a36Sopenharmony_ci CTCM_DBF_TEXT_(SETUP, CTC_DBF_INFO, 164462306a36Sopenharmony_ci "removing device %p, proto : %d", 164562306a36Sopenharmony_ci cgdev, priv->protocol); 164662306a36Sopenharmony_ci 164762306a36Sopenharmony_ci if (cgdev->state == CCWGROUP_ONLINE) 164862306a36Sopenharmony_ci ctcm_shutdown_device(cgdev); 164962306a36Sopenharmony_ci dev_set_drvdata(&cgdev->dev, NULL); 165062306a36Sopenharmony_ci kfree(priv); 165162306a36Sopenharmony_ci put_device(&cgdev->dev); 165262306a36Sopenharmony_ci} 165362306a36Sopenharmony_ci 165462306a36Sopenharmony_cistatic struct ccw_device_id ctcm_ids[] = { 165562306a36Sopenharmony_ci {CCW_DEVICE(0x3088, 0x08), .driver_info = ctcm_channel_type_parallel}, 165662306a36Sopenharmony_ci {CCW_DEVICE(0x3088, 0x1e), .driver_info = ctcm_channel_type_ficon}, 165762306a36Sopenharmony_ci {CCW_DEVICE(0x3088, 0x1f), .driver_info = ctcm_channel_type_escon}, 165862306a36Sopenharmony_ci {}, 165962306a36Sopenharmony_ci}; 166062306a36Sopenharmony_ciMODULE_DEVICE_TABLE(ccw, ctcm_ids); 166162306a36Sopenharmony_ci 166262306a36Sopenharmony_cistatic struct ccw_driver ctcm_ccw_driver = { 166362306a36Sopenharmony_ci .driver = { 166462306a36Sopenharmony_ci .owner = THIS_MODULE, 166562306a36Sopenharmony_ci .name = "ctcm", 166662306a36Sopenharmony_ci }, 166762306a36Sopenharmony_ci .ids = ctcm_ids, 166862306a36Sopenharmony_ci .probe = ccwgroup_probe_ccwdev, 166962306a36Sopenharmony_ci .remove = ccwgroup_remove_ccwdev, 167062306a36Sopenharmony_ci .int_class = IRQIO_CTC, 167162306a36Sopenharmony_ci}; 167262306a36Sopenharmony_ci 167362306a36Sopenharmony_cistatic struct ccwgroup_driver ctcm_group_driver = { 167462306a36Sopenharmony_ci .driver = { 167562306a36Sopenharmony_ci .owner = THIS_MODULE, 167662306a36Sopenharmony_ci .name = CTC_DRIVER_NAME, 167762306a36Sopenharmony_ci }, 167862306a36Sopenharmony_ci .ccw_driver = &ctcm_ccw_driver, 167962306a36Sopenharmony_ci .setup = ctcm_probe_device, 168062306a36Sopenharmony_ci .remove = ctcm_remove_device, 168162306a36Sopenharmony_ci .set_online = ctcm_new_device, 168262306a36Sopenharmony_ci .set_offline = ctcm_shutdown_device, 168362306a36Sopenharmony_ci}; 168462306a36Sopenharmony_ci 168562306a36Sopenharmony_cistatic ssize_t group_store(struct device_driver *ddrv, const char *buf, 168662306a36Sopenharmony_ci size_t count) 168762306a36Sopenharmony_ci{ 168862306a36Sopenharmony_ci int err; 168962306a36Sopenharmony_ci 169062306a36Sopenharmony_ci err = ccwgroup_create_dev(ctcm_root_dev, &ctcm_group_driver, 2, buf); 169162306a36Sopenharmony_ci return err ? err : count; 169262306a36Sopenharmony_ci} 169362306a36Sopenharmony_cistatic DRIVER_ATTR_WO(group); 169462306a36Sopenharmony_ci 169562306a36Sopenharmony_cistatic struct attribute *ctcm_drv_attrs[] = { 169662306a36Sopenharmony_ci &driver_attr_group.attr, 169762306a36Sopenharmony_ci NULL, 169862306a36Sopenharmony_ci}; 169962306a36Sopenharmony_cistatic struct attribute_group ctcm_drv_attr_group = { 170062306a36Sopenharmony_ci .attrs = ctcm_drv_attrs, 170162306a36Sopenharmony_ci}; 170262306a36Sopenharmony_cistatic const struct attribute_group *ctcm_drv_attr_groups[] = { 170362306a36Sopenharmony_ci &ctcm_drv_attr_group, 170462306a36Sopenharmony_ci NULL, 170562306a36Sopenharmony_ci}; 170662306a36Sopenharmony_ci 170762306a36Sopenharmony_ci/* 170862306a36Sopenharmony_ci * Module related routines 170962306a36Sopenharmony_ci */ 171062306a36Sopenharmony_ci 171162306a36Sopenharmony_ci/* 171262306a36Sopenharmony_ci * Prepare to be unloaded. Free IRQ's and release all resources. 171362306a36Sopenharmony_ci * This is called just before this module is unloaded. It is 171462306a36Sopenharmony_ci * not called, if the usage count is !0, so we don't need to check 171562306a36Sopenharmony_ci * for that. 171662306a36Sopenharmony_ci */ 171762306a36Sopenharmony_cistatic void __exit ctcm_exit(void) 171862306a36Sopenharmony_ci{ 171962306a36Sopenharmony_ci ccwgroup_driver_unregister(&ctcm_group_driver); 172062306a36Sopenharmony_ci ccw_driver_unregister(&ctcm_ccw_driver); 172162306a36Sopenharmony_ci root_device_unregister(ctcm_root_dev); 172262306a36Sopenharmony_ci ctcm_unregister_dbf_views(); 172362306a36Sopenharmony_ci pr_info("CTCM driver unloaded\n"); 172462306a36Sopenharmony_ci} 172562306a36Sopenharmony_ci 172662306a36Sopenharmony_ci/* 172762306a36Sopenharmony_ci * Print Banner. 172862306a36Sopenharmony_ci */ 172962306a36Sopenharmony_cistatic void print_banner(void) 173062306a36Sopenharmony_ci{ 173162306a36Sopenharmony_ci pr_info("CTCM driver initialized\n"); 173262306a36Sopenharmony_ci} 173362306a36Sopenharmony_ci 173462306a36Sopenharmony_ci/* 173562306a36Sopenharmony_ci * Initialize module. 173662306a36Sopenharmony_ci * This is called just after the module is loaded. 173762306a36Sopenharmony_ci * 173862306a36Sopenharmony_ci * returns 0 on success, !0 on error. 173962306a36Sopenharmony_ci */ 174062306a36Sopenharmony_cistatic int __init ctcm_init(void) 174162306a36Sopenharmony_ci{ 174262306a36Sopenharmony_ci int ret; 174362306a36Sopenharmony_ci 174462306a36Sopenharmony_ci channels = NULL; 174562306a36Sopenharmony_ci 174662306a36Sopenharmony_ci ret = ctcm_register_dbf_views(); 174762306a36Sopenharmony_ci if (ret) 174862306a36Sopenharmony_ci goto out_err; 174962306a36Sopenharmony_ci ctcm_root_dev = root_device_register("ctcm"); 175062306a36Sopenharmony_ci ret = PTR_ERR_OR_ZERO(ctcm_root_dev); 175162306a36Sopenharmony_ci if (ret) 175262306a36Sopenharmony_ci goto register_err; 175362306a36Sopenharmony_ci ret = ccw_driver_register(&ctcm_ccw_driver); 175462306a36Sopenharmony_ci if (ret) 175562306a36Sopenharmony_ci goto ccw_err; 175662306a36Sopenharmony_ci ctcm_group_driver.driver.groups = ctcm_drv_attr_groups; 175762306a36Sopenharmony_ci ret = ccwgroup_driver_register(&ctcm_group_driver); 175862306a36Sopenharmony_ci if (ret) 175962306a36Sopenharmony_ci goto ccwgroup_err; 176062306a36Sopenharmony_ci print_banner(); 176162306a36Sopenharmony_ci return 0; 176262306a36Sopenharmony_ci 176362306a36Sopenharmony_ciccwgroup_err: 176462306a36Sopenharmony_ci ccw_driver_unregister(&ctcm_ccw_driver); 176562306a36Sopenharmony_ciccw_err: 176662306a36Sopenharmony_ci root_device_unregister(ctcm_root_dev); 176762306a36Sopenharmony_ciregister_err: 176862306a36Sopenharmony_ci ctcm_unregister_dbf_views(); 176962306a36Sopenharmony_ciout_err: 177062306a36Sopenharmony_ci pr_err("%s / Initializing the ctcm device driver failed, ret = %d\n", 177162306a36Sopenharmony_ci __func__, ret); 177262306a36Sopenharmony_ci return ret; 177362306a36Sopenharmony_ci} 177462306a36Sopenharmony_ci 177562306a36Sopenharmony_cimodule_init(ctcm_init); 177662306a36Sopenharmony_cimodule_exit(ctcm_exit); 177762306a36Sopenharmony_ci 177862306a36Sopenharmony_ciMODULE_AUTHOR("Peter Tiedemann <ptiedem@de.ibm.com>"); 177962306a36Sopenharmony_ciMODULE_DESCRIPTION("Network driver for S/390 CTC + CTCMPC (SNA)"); 178062306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 178162306a36Sopenharmony_ci 1782