diff options
Diffstat (limited to 'drivers/s390/net/ctcmain.c')
-rw-r--r-- | drivers/s390/net/ctcmain.c | 3062 |
1 files changed, 0 insertions, 3062 deletions
diff --git a/drivers/s390/net/ctcmain.c b/drivers/s390/net/ctcmain.c deleted file mode 100644 index 77a503139e32..000000000000 --- a/drivers/s390/net/ctcmain.c +++ /dev/null @@ -1,3062 +0,0 @@ -/* - * CTC / ESCON network driver - * - * Copyright (C) 2001 IBM Deutschland Entwicklung GmbH, IBM Corporation - * Author(s): Fritz Elfert (elfert@de.ibm.com, felfert@millenux.com) - * Fixes by : Jochen Röhrig (roehrig@de.ibm.com) - * Arnaldo Carvalho de Melo <acme@conectiva.com.br> - Peter Tiedemann (ptiedem@de.ibm.com) - * Driver Model stuff by : Cornelia Huck <cornelia.huck@de.ibm.com> - * - * Documentation used: - * - Principles of Operation (IBM doc#: SA22-7201-06) - * - Common IO/-Device Commands and Self Description (IBM doc#: SA22-7204-02) - * - Common IO/-Device Commands and Self Description (IBM doc#: SN22-5535) - * - ESCON Channel-to-Channel Adapter (IBM doc#: SA22-7203-00) - * - ESCON I/O Interface (IBM doc#: SA22-7202-029 - * - * and the source of the original CTC driver by: - * Dieter Wellerdiek (wel@de.ibm.com) - * Martin Schwidefsky (schwidefsky@de.ibm.com) - * Denis Joseph Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com) - * Jochen Röhrig (roehrig@de.ibm.com) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - */ -#undef DEBUG -#include <linux/module.h> -#include <linux/init.h> -#include <linux/kernel.h> -#include <linux/slab.h> -#include <linux/errno.h> -#include <linux/types.h> -#include <linux/interrupt.h> -#include <linux/timer.h> -#include <linux/bitops.h> - -#include <linux/signal.h> -#include <linux/string.h> - -#include <linux/ip.h> -#include <linux/if_arp.h> -#include <linux/tcp.h> -#include <linux/skbuff.h> -#include <linux/ctype.h> -#include <net/dst.h> - -#include <asm/io.h> -#include <asm/ccwdev.h> -#include <asm/ccwgroup.h> -#include <asm/uaccess.h> - -#include <asm/idals.h> - -#include "fsm.h" -#include "cu3088.h" - -#include "ctcdbug.h" -#include "ctcmain.h" - -MODULE_AUTHOR("(C) 2000 IBM Corp. by Fritz Elfert (felfert@millenux.com)"); -MODULE_DESCRIPTION("Linux for S/390 CTC/Escon Driver"); -MODULE_LICENSE("GPL"); -/** - * States of the interface statemachine. - */ -enum dev_states { - DEV_STATE_STOPPED, - DEV_STATE_STARTWAIT_RXTX, - DEV_STATE_STARTWAIT_RX, - DEV_STATE_STARTWAIT_TX, - DEV_STATE_STOPWAIT_RXTX, - DEV_STATE_STOPWAIT_RX, - DEV_STATE_STOPWAIT_TX, - DEV_STATE_RUNNING, - /** - * MUST be always the last element!! - */ - CTC_NR_DEV_STATES -}; - -static const char *dev_state_names[] = { - "Stopped", - "StartWait RXTX", - "StartWait RX", - "StartWait TX", - "StopWait RXTX", - "StopWait RX", - "StopWait TX", - "Running", -}; - -/** - * Events of the interface statemachine. - */ -enum dev_events { - DEV_EVENT_START, - DEV_EVENT_STOP, - DEV_EVENT_RXUP, - DEV_EVENT_TXUP, - DEV_EVENT_RXDOWN, - DEV_EVENT_TXDOWN, - DEV_EVENT_RESTART, - /** - * MUST be always the last element!! - */ - CTC_NR_DEV_EVENTS -}; - -static const char *dev_event_names[] = { - "Start", - "Stop", - "RX up", - "TX up", - "RX down", - "TX down", - "Restart", -}; - -/** - * Events of the channel statemachine - */ -enum ch_events { - /** - * Events, representing return code of - * I/O operations (ccw_device_start, ccw_device_halt et al.) - */ - CH_EVENT_IO_SUCCESS, - CH_EVENT_IO_EBUSY, - CH_EVENT_IO_ENODEV, - CH_EVENT_IO_EIO, - CH_EVENT_IO_UNKNOWN, - - CH_EVENT_ATTNBUSY, - CH_EVENT_ATTN, - CH_EVENT_BUSY, - - /** - * Events, representing unit-check - */ - CH_EVENT_UC_RCRESET, - CH_EVENT_UC_RSRESET, - CH_EVENT_UC_TXTIMEOUT, - CH_EVENT_UC_TXPARITY, - CH_EVENT_UC_HWFAIL, - CH_EVENT_UC_RXPARITY, - CH_EVENT_UC_ZERO, - CH_EVENT_UC_UNKNOWN, - - /** - * Events, representing subchannel-check - */ - CH_EVENT_SC_UNKNOWN, - - /** - * Events, representing machine checks - */ - CH_EVENT_MC_FAIL, - CH_EVENT_MC_GOOD, - - /** - * Event, representing normal IRQ - */ - CH_EVENT_IRQ, - CH_EVENT_FINSTAT, - - /** - * Event, representing timer expiry. - */ - CH_EVENT_TIMER, - - /** - * Events, representing commands from upper levels. - */ - CH_EVENT_START, - CH_EVENT_STOP, - - /** - * MUST be always the last element!! - */ - NR_CH_EVENTS, -}; - -/** - * States of the channel statemachine. - */ -enum ch_states { - /** - * Channel not assigned to any device, - * initial state, direction invalid - */ - CH_STATE_IDLE, - - /** - * Channel assigned but not operating - */ - CH_STATE_STOPPED, - CH_STATE_STARTWAIT, - CH_STATE_STARTRETRY, - CH_STATE_SETUPWAIT, - CH_STATE_RXINIT, - CH_STATE_TXINIT, - CH_STATE_RX, - CH_STATE_TX, - CH_STATE_RXIDLE, - CH_STATE_TXIDLE, - CH_STATE_RXERR, - CH_STATE_TXERR, - CH_STATE_TERM, - CH_STATE_DTERM, - CH_STATE_NOTOP, - - /** - * MUST be always the last element!! - */ - NR_CH_STATES, -}; - -static int loglevel = CTC_LOGLEVEL_DEFAULT; - -/** - * Linked list of all detected channels. - */ -static struct channel *channels = NULL; - -/** - * Print Banner. - */ -static void -print_banner(void) -{ - static int printed = 0; - - if (printed) - return; - - printk(KERN_INFO "CTC driver initialized\n"); - printed = 1; -} - -/** - * Return type of a detected device. - */ -static enum channel_types -get_channel_type(struct ccw_device_id *id) -{ - enum channel_types type = (enum channel_types) id->driver_info; - - if (type == channel_type_ficon) - type = channel_type_escon; - - return type; -} - -static const char *ch_event_names[] = { - "ccw_device success", - "ccw_device busy", - "ccw_device enodev", - "ccw_device ioerr", - "ccw_device unknown", - - "Status ATTN & BUSY", - "Status ATTN", - "Status BUSY", - - "Unit check remote reset", - "Unit check remote system reset", - "Unit check TX timeout", - "Unit check TX parity", - "Unit check Hardware failure", - "Unit check RX parity", - "Unit check ZERO", - "Unit check Unknown", - - "SubChannel check Unknown", - - "Machine check failure", - "Machine check operational", - - "IRQ normal", - "IRQ final", - - "Timer", - - "Start", - "Stop", -}; - -static const char *ch_state_names[] = { - "Idle", - "Stopped", - "StartWait", - "StartRetry", - "SetupWait", - "RX init", - "TX init", - "RX", - "TX", - "RX idle", - "TX idle", - "RX error", - "TX error", - "Terminating", - "Restarting", - "Not operational", -}; - -#ifdef DEBUG -/** - * Dump header and first 16 bytes of an sk_buff for debugging purposes. - * - * @param skb The sk_buff to dump. - * @param offset Offset relative to skb-data, where to start the dump. - */ -static void -ctc_dump_skb(struct sk_buff *skb, int offset) -{ - unsigned char *p = skb->data; - __u16 bl; - struct ll_header *header; - int i; - - if (!(loglevel & CTC_LOGLEVEL_DEBUG)) - return; - p += offset; - bl = *((__u16 *) p); - p += 2; - header = (struct ll_header *) p; - p -= 2; - - printk(KERN_DEBUG "dump:\n"); - printk(KERN_DEBUG "blocklen=%d %04x\n", bl, bl); - - printk(KERN_DEBUG "h->length=%d %04x\n", header->length, - header->length); - printk(KERN_DEBUG "h->type=%04x\n", header->type); - printk(KERN_DEBUG "h->unused=%04x\n", header->unused); - if (bl > 16) - bl = 16; - printk(KERN_DEBUG "data: "); - for (i = 0; i < bl; i++) - printk("%02x%s", *p++, (i % 16) ? " " : "\n<7>"); - printk("\n"); -} -#else -static inline void -ctc_dump_skb(struct sk_buff *skb, int offset) -{ -} -#endif - -/** - * Unpack a just received skb and hand it over to - * upper layers. - * - * @param ch The channel where this skb has been received. - * @param pskb The received skb. - */ -static void -ctc_unpack_skb(struct channel *ch, struct sk_buff *pskb) -{ - struct net_device *dev = ch->netdev; - struct ctc_priv *privptr = (struct ctc_priv *) dev->priv; - __u16 len = *((__u16 *) pskb->data); - - DBF_TEXT(trace, 4, __FUNCTION__); - skb_put(pskb, 2 + LL_HEADER_LENGTH); - skb_pull(pskb, 2); - pskb->dev = dev; - pskb->ip_summed = CHECKSUM_UNNECESSARY; - while (len > 0) { - struct sk_buff *skb; - struct ll_header *header = (struct ll_header *) pskb->data; - - skb_pull(pskb, LL_HEADER_LENGTH); - if ((ch->protocol == CTC_PROTO_S390) && - (header->type != ETH_P_IP)) { - -#ifndef DEBUG - if (!(ch->logflags & LOG_FLAG_ILLEGALPKT)) { -#endif - /** - * Check packet type only if we stick strictly - * to S/390's protocol of OS390. This only - * supports IP. Otherwise allow any packet - * type. - */ - ctc_pr_warn( - "%s Illegal packet type 0x%04x received, dropping\n", - dev->name, header->type); - ch->logflags |= LOG_FLAG_ILLEGALPKT; -#ifndef DEBUG - } -#endif -#ifdef DEBUG - ctc_dump_skb(pskb, -6); -#endif - privptr->stats.rx_dropped++; - privptr->stats.rx_frame_errors++; - return; - } - pskb->protocol = ntohs(header->type); - if (header->length <= LL_HEADER_LENGTH) { -#ifndef DEBUG - if (!(ch->logflags & LOG_FLAG_ILLEGALSIZE)) { -#endif - ctc_pr_warn( - "%s Illegal packet size %d " - "received (MTU=%d blocklen=%d), " - "dropping\n", dev->name, header->length, - dev->mtu, len); - ch->logflags |= LOG_FLAG_ILLEGALSIZE; -#ifndef DEBUG - } -#endif -#ifdef DEBUG - ctc_dump_skb(pskb, -6); -#endif - privptr->stats.rx_dropped++; - privptr->stats.rx_length_errors++; - return; - } - header->length -= LL_HEADER_LENGTH; - len -= LL_HEADER_LENGTH; - if ((header->length > skb_tailroom(pskb)) || - (header->length > len)) { -#ifndef DEBUG - if (!(ch->logflags & LOG_FLAG_OVERRUN)) { -#endif - ctc_pr_warn( - "%s Illegal packet size %d " - "(beyond the end of received data), " - "dropping\n", dev->name, header->length); - ch->logflags |= LOG_FLAG_OVERRUN; -#ifndef DEBUG - } -#endif -#ifdef DEBUG - ctc_dump_skb(pskb, -6); -#endif - privptr->stats.rx_dropped++; - privptr->stats.rx_length_errors++; - return; - } - skb_put(pskb, header->length); - skb_reset_mac_header(pskb); - len -= header->length; - skb = dev_alloc_skb(pskb->len); - if (!skb) { -#ifndef DEBUG - if (!(ch->logflags & LOG_FLAG_NOMEM)) { -#endif - ctc_pr_warn( - "%s Out of memory in ctc_unpack_skb\n", - dev->name); - ch->logflags |= LOG_FLAG_NOMEM; -#ifndef DEBUG - } -#endif - privptr->stats.rx_dropped++; - return; - } - skb_copy_from_linear_data(pskb, skb_put(skb, pskb->len), - pskb->len); - skb_reset_mac_header(skb); - skb->dev = pskb->dev; - skb->protocol = pskb->protocol; - pskb->ip_summed = CHECKSUM_UNNECESSARY; - /** - * reset logflags - */ - ch->logflags = 0; - privptr->stats.rx_packets++; - privptr->stats.rx_bytes += skb->len; - netif_rx_ni(skb); - dev->last_rx = jiffies; - if (len > 0) { - skb_pull(pskb, header->length); - if (skb_tailroom(pskb) < LL_HEADER_LENGTH) { -#ifndef DEBUG - if (!(ch->logflags & LOG_FLAG_OVERRUN)) { -#endif - ctc_pr_warn( - "%s Overrun in ctc_unpack_skb\n", - dev->name); - ch->logflags |= LOG_FLAG_OVERRUN; -#ifndef DEBUG - } -#endif - return; - } - skb_put(pskb, LL_HEADER_LENGTH); - } - } -} - -/** - * Check return code of a preceeding ccw_device call, halt_IO etc... - * - * @param ch The channel, the error belongs to. - * @param return_code The error code to inspect. - */ -static void -ccw_check_return_code(struct channel *ch, int return_code, char *msg) -{ - DBF_TEXT(trace, 5, __FUNCTION__); - switch (return_code) { - case 0: - fsm_event(ch->fsm, CH_EVENT_IO_SUCCESS, ch); - break; - case -EBUSY: - ctc_pr_warn("%s (%s): Busy !\n", ch->id, msg); - fsm_event(ch->fsm, CH_EVENT_IO_EBUSY, ch); - break; - case -ENODEV: - ctc_pr_emerg("%s (%s): Invalid device called for IO\n", - ch->id, msg); - fsm_event(ch->fsm, CH_EVENT_IO_ENODEV, ch); - break; - case -EIO: - ctc_pr_emerg("%s (%s): Status pending... \n", - ch->id, msg); - fsm_event(ch->fsm, CH_EVENT_IO_EIO, ch); - break; - default: - ctc_pr_emerg("%s (%s): Unknown error in do_IO %04x\n", - ch->id, msg, return_code); - fsm_event(ch->fsm, CH_EVENT_IO_UNKNOWN, ch); - } -} - -/** - * Check sense of a unit check. - * - * @param ch The channel, the sense code belongs to. - * @param sense The sense code to inspect. - */ -static void -ccw_unit_check(struct channel *ch, unsigned char sense) -{ - DBF_TEXT(trace, 5, __FUNCTION__); - if (sense & SNS0_INTERVENTION_REQ) { - if (sense & 0x01) { - ctc_pr_debug("%s: Interface disc. or Sel. reset " - "(remote)\n", ch->id); - fsm_event(ch->fsm, CH_EVENT_UC_RCRESET, ch); - } else { - ctc_pr_debug("%s: System reset (remote)\n", ch->id); - fsm_event(ch->fsm, CH_EVENT_UC_RSRESET, ch); - } - } else if (sense & SNS0_EQUIPMENT_CHECK) { - if (sense & SNS0_BUS_OUT_CHECK) { - ctc_pr_warn("%s: Hardware malfunction (remote)\n", - ch->id); - fsm_event(ch->fsm, CH_EVENT_UC_HWFAIL, ch); - } else { - ctc_pr_warn("%s: Read-data parity error (remote)\n", - ch->id); - fsm_event(ch->fsm, CH_EVENT_UC_RXPARITY, ch); - } - } else if (sense & SNS0_BUS_OUT_CHECK) { - if (sense & 0x04) { - ctc_pr_warn("%s: Data-streaming timeout)\n", ch->id); - fsm_event(ch->fsm, CH_EVENT_UC_TXTIMEOUT, ch); - } else { - ctc_pr_warn("%s: Data-transfer parity error\n", ch->id); - fsm_event(ch->fsm, CH_EVENT_UC_TXPARITY, ch); - } - } else if (sense & SNS0_CMD_REJECT) { - ctc_pr_warn("%s: Command reject\n", ch->id); - } else if (sense == 0) { - ctc_pr_debug("%s: Unit check ZERO\n", ch->id); - fsm_event(ch->fsm, CH_EVENT_UC_ZERO, ch); - } else { - ctc_pr_warn("%s: Unit Check with sense code: %02x\n", - ch->id, sense); - fsm_event(ch->fsm, CH_EVENT_UC_UNKNOWN, ch); - } -} - -static void -ctc_purge_skb_queue(struct sk_buff_head *q) -{ - struct sk_buff *skb; - - DBF_TEXT(trace, 5, __FUNCTION__); - - while ((skb = skb_dequeue(q))) { - atomic_dec(&skb->users); - dev_kfree_skb_irq(skb); - } -} - -static int -ctc_checkalloc_buffer(struct channel *ch, int warn) -{ - DBF_TEXT(trace, 5, __FUNCTION__); - if ((ch->trans_skb == NULL) || - (ch->flags & CHANNEL_FLAGS_BUFSIZE_CHANGED)) { - if (ch->trans_skb != NULL) - dev_kfree_skb(ch->trans_skb); - clear_normalized_cda(&ch->ccw[1]); - ch->trans_skb = __dev_alloc_skb(ch->max_bufsize, - GFP_ATOMIC | GFP_DMA); - if (ch->trans_skb == NULL) { - if (warn) - ctc_pr_warn( - "%s: Couldn't alloc %s trans_skb\n", - ch->id, - (CHANNEL_DIRECTION(ch->flags) == READ) ? - "RX" : "TX"); - return -ENOMEM; - } - ch->ccw[1].count = ch->max_bufsize; - if (set_normalized_cda(&ch->ccw[1], ch->trans_skb->data)) { - dev_kfree_skb(ch->trans_skb); - ch->trans_skb = NULL; - if (warn) - ctc_pr_warn( - "%s: set_normalized_cda for %s " - "trans_skb failed, dropping packets\n", - ch->id, - (CHANNEL_DIRECTION(ch->flags) == READ) ? - "RX" : "TX"); - return -ENOMEM; - } - ch->ccw[1].count = 0; - ch->trans_skb_data = ch->trans_skb->data; - ch->flags &= ~CHANNEL_FLAGS_BUFSIZE_CHANGED; - } - return 0; -} - -/** - * Dummy NOP action for statemachines - */ -static void -fsm_action_nop(fsm_instance * fi, int event, void *arg) -{ -} - -/** - * Actions for channel - statemachines. - *****************************************************************************/ - -/** - * Normal data has been send. Free the corresponding - * skb (it's in io_queue), reset dev->tbusy and - * revert to idle state. - * - * @param fi An instance of a channel statemachine. - * @param event The event, just happened. - * @param arg Generic pointer, casted from channel * upon call. - */ -static void -ch_action_txdone(fsm_instance * fi, int event, void *arg) -{ - struct channel *ch = (struct channel *) arg; - struct net_device *dev = ch->netdev; - struct ctc_priv *privptr = dev->priv; - struct sk_buff *skb; - int first = 1; - int i; - unsigned long duration; - struct timespec done_stamp = current_kernel_time(); - - DBF_TEXT(trace, 4, __FUNCTION__); - - duration = - (done_stamp.tv_sec - ch->prof.send_stamp.tv_sec) * 1000000 + - (done_stamp.tv_nsec - ch->prof.send_stamp.tv_nsec) / 1000; - if (duration > ch->prof.tx_time) - ch->prof.tx_time = duration; - - if (ch->irb->scsw.count != 0) - ctc_pr_debug("%s: TX not complete, remaining %d bytes\n", - dev->name, ch->irb->scsw.count); - fsm_deltimer(&ch->timer); - while ((skb = skb_dequeue(&ch->io_queue))) { - privptr->stats.tx_packets++; - privptr->stats.tx_bytes += skb->len - LL_HEADER_LENGTH; - if (first) { - privptr->stats.tx_bytes += 2; - first = 0; - } - atomic_dec(&skb->users); - dev_kfree_skb_irq(skb); - } - spin_lock(&ch->collect_lock); - clear_normalized_cda(&ch->ccw[4]); - if (ch->collect_len > 0) { - int rc; - - if (ctc_checkalloc_buffer(ch, 1)) { - spin_unlock(&ch->collect_lock); - return; - } - ch->trans_skb->data = ch->trans_skb_data; - skb_reset_tail_pointer(ch->trans_skb); - ch->trans_skb->len = 0; - if (ch->prof.maxmulti < (ch->collect_len + 2)) - ch->prof.maxmulti = ch->collect_len + 2; - if (ch->prof.maxcqueue < skb_queue_len(&ch->collect_queue)) - ch->prof.maxcqueue = skb_queue_len(&ch->collect_queue); - *((__u16 *) skb_put(ch->trans_skb, 2)) = ch->collect_len + 2; - i = 0; - while ((skb = skb_dequeue(&ch->collect_queue))) { - skb_copy_from_linear_data(skb, skb_put(ch->trans_skb, - skb->len), - skb->len); - privptr->stats.tx_packets++; - privptr->stats.tx_bytes += skb->len - LL_HEADER_LENGTH; - atomic_dec(&skb->users); - dev_kfree_skb_irq(skb); - i++; - } - ch->collect_len = 0; - spin_unlock(&ch->collect_lock); - ch->ccw[1].count = ch->trans_skb->len; - fsm_addtimer(&ch->timer, CTC_TIMEOUT_5SEC, CH_EVENT_TIMER, ch); - ch->prof.send_stamp = current_kernel_time(); - rc = ccw_device_start(ch->cdev, &ch->ccw[0], - (unsigned long) ch, 0xff, 0); - ch->prof.doios_multi++; - if (rc != 0) { - privptr->stats.tx_dropped += i; - privptr->stats.tx_errors += i; - fsm_deltimer(&ch->timer); - ccw_check_return_code(ch, rc, "chained TX"); - } - } else { - spin_unlock(&ch->collect_lock); - fsm_newstate(fi, CH_STATE_TXIDLE); - } - ctc_clear_busy(dev); -} - -/** - * Initial data is sent. - * Notify device statemachine that we are up and - * running. - * - * @param fi An instance of a channel statemachine. - * @param event The event, just happened. - * @param arg Generic pointer, casted from channel * upon call. - */ -static void -ch_action_txidle(fsm_instance * fi, int event, void *arg) -{ - struct channel *ch = (struct channel *) arg; - - DBF_TEXT(trace, 4, __FUNCTION__); - fsm_deltimer(&ch->timer); - fsm_newstate(fi, CH_STATE_TXIDLE); - fsm_event(((struct ctc_priv *) ch->netdev->priv)->fsm, DEV_EVENT_TXUP, - ch->netdev); -} - -/** - * Got normal data, check for sanity, queue it up, allocate new buffer - * trigger bottom half, and initiate next read. - * - * @param fi An instance of a channel statemachine. - * @param event The event, just happened. - * @param arg Generic pointer, casted from channel * upon call. - */ -static void -ch_action_rx(fsm_instance * fi, int event, void *arg) -{ - struct channel *ch = (struct channel *) arg; - struct net_device *dev = ch->netdev; - struct ctc_priv *privptr = dev->priv; - int len = ch->max_bufsize - ch->irb->scsw.count; - struct sk_buff *skb = ch->trans_skb; - __u16 block_len = *((__u16 *) skb->data); - int check_len; - int rc; - - DBF_TEXT(trace, 4, __FUNCTION__); - fsm_deltimer(&ch->timer); - if (len < 8) { - ctc_pr_debug("%s: got packet with length %d < 8\n", - dev->name, len); - privptr->stats.rx_dropped++; - privptr->stats.rx_length_errors++; - goto again; - } - if (len > ch->max_bufsize) { - ctc_pr_debug("%s: got packet with length %d > %d\n", - dev->name, len, ch->max_bufsize); - privptr->stats.rx_dropped++; - privptr->stats.rx_length_errors++; - goto again; - } - - /** - * VM TCP seems to have a bug sending 2 trailing bytes of garbage. - */ - switch (ch->protocol) { - case CTC_PROTO_S390: - case CTC_PROTO_OS390: - check_len = block_len + 2; - break; - default: - check_len = block_len; - break; - } - if ((len < block_len) || (len > check_len)) { - ctc_pr_debug("%s: got block length %d != rx length %d\n", - dev->name, block_len, len); -#ifdef DEBUG - ctc_dump_skb(skb, 0); -#endif - *((__u16 *) skb->data) = len; - privptr->stats.rx_dropped++; - privptr->stats.rx_length_errors++; - goto again; - } - block_len -= 2; - if (block_len > 0) { - *((__u16 *) skb->data) = block_len; - ctc_unpack_skb(ch, skb); - } - again: - skb->data = ch->trans_skb_data; - skb_reset_tail_pointer(skb); - skb->len = 0; - if (ctc_checkalloc_buffer(ch, 1)) - return; - ch->ccw[1].count = ch->max_bufsize; - rc = ccw_device_start(ch->cdev, &ch->ccw[0], (unsigned long) ch, 0xff, 0); - if (rc != 0) - ccw_check_return_code(ch, rc, "normal RX"); -} - -static void ch_action_rxidle(fsm_instance * fi, int event, void *arg); - -/** - * Initialize connection by sending a __u16 of value 0. - * - * @param fi An instance of a channel statemachine. - * @param event The event, just happened. - * @param arg Generic pointer, casted from channel * upon call. - */ -static void -ch_action_firstio(fsm_instance * fi, int event, void *arg) -{ - struct channel *ch = (struct channel *) arg; - int rc; - - DBF_TEXT(trace, 4, __FUNCTION__); - - if (fsm_getstate(fi) == CH_STATE_TXIDLE) - ctc_pr_debug("%s: remote side issued READ?, init ...\n", ch->id); - fsm_deltimer(&ch->timer); - if (ctc_checkalloc_buffer(ch, 1)) - return; - if ((fsm_getstate(fi) == CH_STATE_SETUPWAIT) && - (ch->protocol == CTC_PROTO_OS390)) { - /* OS/390 resp. z/OS */ - if (CHANNEL_DIRECTION(ch->flags) == READ) { - *((__u16 *) ch->trans_skb->data) = CTC_INITIAL_BLOCKLEN; - fsm_addtimer(&ch->timer, CTC_TIMEOUT_5SEC, - CH_EVENT_TIMER, ch); - ch_action_rxidle(fi, event, arg); - } else { - struct net_device *dev = ch->netdev; - fsm_newstate(fi, CH_STATE_TXIDLE); - fsm_event(((struct ctc_priv *) dev->priv)->fsm, - DEV_EVENT_TXUP, dev); - } - return; - } - - /** - * Don't setup a timer for receiving the initial RX frame - * if in compatibility mode, since VM TCP delays the initial - * frame until it has some data to send. - */ - if ((CHANNEL_DIRECTION(ch->flags) == WRITE) || - (ch->protocol != CTC_PROTO_S390)) - fsm_addtimer(&ch->timer, CTC_TIMEOUT_5SEC, CH_EVENT_TIMER, ch); - - *((__u16 *) ch->trans_skb->data) = CTC_INITIAL_BLOCKLEN; - ch->ccw[1].count = 2; /* Transfer only length */ - - fsm_newstate(fi, (CHANNEL_DIRECTION(ch->flags) == READ) - ? CH_STATE_RXINIT : CH_STATE_TXINIT); - rc = ccw_device_start(ch->cdev, &ch->ccw[0], (unsigned long) ch, 0xff, 0); - if (rc != 0) { - fsm_deltimer(&ch->timer); - fsm_newstate(fi, CH_STATE_SETUPWAIT); - ccw_check_return_code(ch, rc, "init IO"); - } - /** - * If in compatibility mode since we don't setup a timer, we - * also signal RX channel up immediately. This enables us - * to send packets early which in turn usually triggers some - * reply from VM TCP which brings up the RX channel to it's - * final state. - */ - if ((CHANNEL_DIRECTION(ch->flags) == READ) && - (ch->protocol == CTC_PROTO_S390)) { - struct net_device *dev = ch->netdev; - fsm_event(((struct ctc_priv *) dev->priv)->fsm, DEV_EVENT_RXUP, - dev); - } -} - -/** - * Got initial data, check it. If OK, - * notify device statemachine that we are up and - * running. - * - * @param fi An instance of a channel statemachine. - * @param event The event, just happened. - * @param arg Generic pointer, casted from channel * upon call. - */ -static void -ch_action_rxidle(fsm_instance * fi, int event, void *arg) -{ - struct channel *ch = (struct channel *) arg; - struct net_device *dev = ch->netdev; - __u16 buflen; - int rc; - - DBF_TEXT(trace, 4, __FUNCTION__); - fsm_deltimer(&ch->timer); - buflen = *((__u16 *) ch->trans_skb->data); -#ifdef DEBUG - ctc_pr_debug("%s: Initial RX count %d\n", dev->name, buflen); -#endif - if (buflen >= CTC_INITIAL_BLOCKLEN) { - if (ctc_checkalloc_buffer(ch, 1)) - return; - ch->ccw[1].count = ch->max_bufsize; - fsm_newstate(fi, CH_STATE_RXIDLE); - rc = ccw_device_start(ch->cdev, &ch->ccw[0], - (unsigned long) ch, 0xff, 0); - if (rc != 0) { - fsm_newstate(fi, CH_STATE_RXINIT); - ccw_check_return_code(ch, rc, "initial RX"); - } else - fsm_event(((struct ctc_priv *) dev->priv)->fsm, - DEV_EVENT_RXUP, dev); - } else { - ctc_pr_debug("%s: Initial RX count %d not %d\n", - dev->name, buflen, CTC_INITIAL_BLOCKLEN); - ch_action_firstio(fi, event, arg); - } -} - -/** - * Set channel into extended mode. - * - * @param fi An instance of a channel statemachine. - * @param event The event, just happened. - * @param arg Generic pointer, casted from channel * upon call. - */ -static void -ch_action_setmode(fsm_instance * fi, int event, void *arg) -{ - struct channel *ch = (struct channel *) arg; - int rc; - unsigned long saveflags; - - DBF_TEXT(trace, 4, __FUNCTION__); - fsm_deltimer(&ch->timer); - fsm_addtimer(&ch->timer, CTC_TIMEOUT_5SEC, CH_EVENT_TIMER, ch); - fsm_newstate(fi, CH_STATE_SETUPWAIT); - saveflags = 0; /* avoids compiler warning with - spin_unlock_irqrestore */ - if (event == CH_EVENT_TIMER) // only for timer not yet locked - spin_lock_irqsave(get_ccwdev_lock(ch->cdev), saveflags); - rc = ccw_device_start(ch->cdev, &ch->ccw[6], (unsigned long) ch, 0xff, 0); - if (event == CH_EVENT_TIMER) - spin_unlock_irqrestore(get_ccwdev_lock(ch->cdev), saveflags); - if (rc != 0) { - fsm_deltimer(&ch->timer); - fsm_newstate(fi, CH_STATE_STARTWAIT); - ccw_check_return_code(ch, rc, "set Mode"); - } else - ch->retry = 0; -} - -/** - * Setup channel. - * - * @param fi An instance of a channel statemachine. - * @param event The event, just happened. - * @param arg Generic pointer, casted from channel * upon call. - */ -static void -ch_action_start(fsm_instance * fi, int event, void *arg) -{ - struct channel *ch = (struct channel *) arg; - unsigned long saveflags; - int rc; - struct net_device *dev; - - DBF_TEXT(trace, 4, __FUNCTION__); - if (ch == NULL) { - ctc_pr_warn("ch_action_start ch=NULL\n"); - return; - } - if (ch->netdev == NULL) { - ctc_pr_warn("ch_action_start dev=NULL, id=%s\n", ch->id); - return; - } - dev = ch->netdev; - -#ifdef DEBUG - ctc_pr_debug("%s: %s channel start\n", dev->name, - (CHANNEL_DIRECTION(ch->flags) == READ) ? "RX" : "TX"); -#endif - - if (ch->trans_skb != NULL) { - clear_normalized_cda(&ch->ccw[1]); - dev_kfree_skb(ch->trans_skb); - ch->trans_skb = NULL; - } - if (CHANNEL_DIRECTION(ch->flags) == READ) { - ch->ccw[1].cmd_code = CCW_CMD_READ; - ch->ccw[1].flags = CCW_FLAG_SLI; - ch->ccw[1].count = 0; - } else { - ch->ccw[1].cmd_code = CCW_CMD_WRITE; - ch->ccw[1].flags = CCW_FLAG_SLI | CCW_FLAG_CC; - ch->ccw[1].count = 0; - } - if (ctc_checkalloc_buffer(ch, 0)) { - ctc_pr_notice( - "%s: Could not allocate %s trans_skb, delaying " - "allocation until first transfer\n", - dev->name, - (CHANNEL_DIRECTION(ch->flags) == READ) ? "RX" : "TX"); - } - - ch->ccw[0].cmd_code = CCW_CMD_PREPARE; - ch->ccw[0].flags = CCW_FLAG_SLI | CCW_FLAG_CC; - ch->ccw[0].count = 0; - ch->ccw[0].cda = 0; - ch->ccw[2].cmd_code = CCW_CMD_NOOP; /* jointed CE + DE */ - ch->ccw[2].flags = CCW_FLAG_SLI; - ch->ccw[2].count = 0; - ch->ccw[2].cda = 0; - memcpy(&ch->ccw[3], &ch->ccw[0], sizeof (struct ccw1) * 3); - ch->ccw[4].cda = 0; - ch->ccw[4].flags &= ~CCW_FLAG_IDA; - - fsm_newstate(fi, CH_STATE_STARTWAIT); - fsm_addtimer(&ch->timer, 1000, CH_EVENT_TIMER, ch); - spin_lock_irqsave(get_ccwdev_lock(ch->cdev), saveflags); - rc = ccw_device_halt(ch->cdev, (unsigned long) ch); - spin_unlock_irqrestore(get_ccwdev_lock(ch->cdev), saveflags); - if (rc != 0) { - if (rc != -EBUSY) - fsm_deltimer(&ch->timer); - ccw_check_return_code(ch, rc, "initial HaltIO"); - } -#ifdef DEBUG - ctc_pr_debug("ctc: %s(): leaving\n", __func__); -#endif -} - -/** - * Shutdown a channel. - * - * @param fi An instance of a channel statemachine. - * @param event The event, just happened. - * @param arg Generic pointer, casted from channel * upon call. - */ -static void -ch_action_haltio(fsm_instance * fi, int event, void *arg) -{ - struct channel *ch = (struct channel *) arg; - unsigned long saveflags; - int rc; - int oldstate; - - DBF_TEXT(trace, 3, __FUNCTION__); - fsm_deltimer(&ch->timer); - fsm_addtimer(&ch->timer, CTC_TIMEOUT_5SEC, CH_EVENT_TIMER, ch); - saveflags = 0; /* avoids comp warning with - spin_unlock_irqrestore */ - if (event == CH_EVENT_STOP) // only for STOP not yet locked - spin_lock_irqsave(get_ccwdev_lock(ch->cdev), saveflags); - oldstate = fsm_getstate(fi); - fsm_newstate(fi, CH_STATE_TERM); - rc = ccw_device_halt(ch->cdev, (unsigned long) ch); - if (event == CH_EVENT_STOP) - spin_unlock_irqrestore(get_ccwdev_lock(ch->cdev), saveflags); - if (rc != 0) { - if (rc != -EBUSY) { - fsm_deltimer(&ch->timer); - fsm_newstate(fi, oldstate); - } - ccw_check_return_code(ch, rc, "HaltIO in ch_action_haltio"); - } -} - -/** - * A channel has successfully been halted. - * Cleanup it's queue and notify interface statemachine. - * - * @param fi An instance of a channel statemachine. - * @param event The event, just happened. - * @param arg Generic pointer, casted from channel * upon call. - */ -static void -ch_action_stopped(fsm_instance * fi, int event, void *arg) -{ - struct channel *ch = (struct channel *) arg; - struct net_device *dev = ch->netdev; - - DBF_TEXT(trace, 3, __FUNCTION__); - fsm_deltimer(&ch->timer); - fsm_newstate(fi, CH_STATE_STOPPED); - if (ch->trans_skb != NULL) { - clear_normalized_cda(&ch->ccw[1]); - dev_kfree_skb(ch->trans_skb); - ch->trans_skb = NULL; - } - if (CHANNEL_DIRECTION(ch->flags) == READ) { - skb_queue_purge(&ch->io_queue); - fsm_event(((struct ctc_priv *) dev->priv)->fsm, - DEV_EVENT_RXDOWN, dev); - } else { - ctc_purge_skb_queue(&ch->io_queue); - spin_lock(&ch->collect_lock); - ctc_purge_skb_queue(&ch->collect_queue); - ch->collect_len = 0; - spin_unlock(&ch->collect_lock); - fsm_event(((struct ctc_priv *) dev->priv)->fsm, - DEV_EVENT_TXDOWN, dev); - } -} - -/** - * A stop command from device statemachine arrived and we are in - * not operational mode. Set state to stopped. - * - * @param fi An instance of a channel statemachine. - * @param event The event, just happened. - * @param arg Generic pointer, casted from channel * upon call. - */ -static void -ch_action_stop(fsm_instance * fi, int event, void *arg) -{ - fsm_newstate(fi, CH_STATE_STOPPED); -} - -/** - * A machine check for no path, not operational status or gone device has - * happened. - * Cleanup queue and notify interface statemachine. - * - * @param fi An instance of a channel statemachine. - * @param event The event, just happened. - * @param arg Generic pointer, casted from channel * upon call. - */ -static void -ch_action_fail(fsm_instance * fi, int event, void *arg) -{ - struct channel *ch = (struct channel *) arg; - struct net_device *dev = ch->netdev; - - DBF_TEXT(trace, 3, __FUNCTION__); - fsm_deltimer(&ch->timer); - fsm_newstate(fi, CH_STATE_NOTOP); - if (CHANNEL_DIRECTION(ch->flags) == READ) { - skb_queue_purge(&ch->io_queue); - fsm_event(((struct ctc_priv *) dev->priv)->fsm, - DEV_EVENT_RXDOWN, dev); - } else { - ctc_purge_skb_queue(&ch->io_queue); - spin_lock(&ch->collect_lock); - ctc_purge_skb_queue(&ch->collect_queue); - ch->collect_len = 0; - spin_unlock(&ch->collect_lock); - fsm_event(((struct ctc_priv *) dev->priv)->fsm, - DEV_EVENT_TXDOWN, dev); - } -} - -/** - * Handle error during setup of channel. - * - * @param fi An instance of a channel statemachine. - * @param event The event, just happened. - * @param arg Generic pointer, casted from channel * upon call. - */ -static void -ch_action_setuperr(fsm_instance * fi, int event, void *arg) -{ - struct channel *ch = (struct channel *) arg; - struct net_device *dev = ch->netdev; - - DBF_TEXT(setup, 3, __FUNCTION__); - /** - * Special case: Got UC_RCRESET on setmode. - * This means that remote side isn't setup. In this case - * simply retry after some 10 secs... - */ - if ((fsm_getstate(fi) == CH_STATE_SETUPWAIT) && - ((event == CH_EVENT_UC_RCRESET) || - (event == CH_EVENT_UC_RSRESET))) { - fsm_newstate(fi, CH_STATE_STARTRETRY); - fsm_deltimer(&ch->timer); - fsm_addtimer(&ch->timer, CTC_TIMEOUT_5SEC, CH_EVENT_TIMER, ch); - if (CHANNEL_DIRECTION(ch->flags) == READ) { - int rc = ccw_device_halt(ch->cdev, (unsigned long) ch); - if (rc != 0) - ccw_check_return_code( - ch, rc, "HaltIO in ch_action_setuperr"); - } - return; - } - - ctc_pr_debug("%s: Error %s during %s channel setup state=%s\n", - dev->name, ch_event_names[event], - (CHANNEL_DIRECTION(ch->flags) == READ) ? "RX" : "TX", - fsm_getstate_str(fi)); - if (CHANNEL_DIRECTION(ch->flags) == READ) { - fsm_newstate(fi, CH_STATE_RXERR); - fsm_event(((struct ctc_priv *) dev->priv)->fsm, - DEV_EVENT_RXDOWN, dev); - } else { - fsm_newstate(fi, CH_STATE_TXERR); - fsm_event(((struct ctc_priv *) dev->priv)->fsm, - DEV_EVENT_TXDOWN, dev); - } -} - -/** - * Restart a channel after an error. - * - * @param fi An instance of a channel statemachine. - * @param event The event, just happened. - * @param arg Generic pointer, casted from channel * upon call. - */ -static void -ch_action_restart(fsm_instance * fi, int event, void *arg) -{ - unsigned long saveflags; - int oldstate; - int rc; - - struct channel *ch = (struct channel *) arg; - struct net_device *dev = ch->netdev; - - DBF_TEXT(trace, 3, __FUNCTION__); - fsm_deltimer(&ch->timer); - ctc_pr_debug("%s: %s channel restart\n", dev->name, - (CHANNEL_DIRECTION(ch->flags) == READ) ? "RX" : "TX"); - fsm_addtimer(&ch->timer, CTC_TIMEOUT_5SEC, CH_EVENT_TIMER, ch); - oldstate = fsm_getstate(fi); - fsm_newstate(fi, CH_STATE_STARTWAIT); - saveflags = 0; /* avoids compiler warning with - spin_unlock_irqrestore */ - if (event == CH_EVENT_TIMER) // only for timer not yet locked - spin_lock_irqsave(get_ccwdev_lock(ch->cdev), saveflags); - rc = ccw_device_halt(ch->cdev, (unsigned long) ch); - if (event == CH_EVENT_TIMER) - spin_unlock_irqrestore(get_ccwdev_lock(ch->cdev), saveflags); - if (rc != 0) { - if (rc != -EBUSY) { - fsm_deltimer(&ch->timer); - fsm_newstate(fi, oldstate); - } - ccw_check_return_code(ch, rc, "HaltIO in ch_action_restart"); - } -} - -/** - * Handle error during RX initial handshake (exchange of - * 0-length block header) - * - * @param fi An instance of a channel statemachine. - * @param event The event, just happened. - * @param arg Generic pointer, casted from channel * upon call. - */ -static void -ch_action_rxiniterr(fsm_instance * fi, int event, void *arg) -{ - struct channel *ch = (struct channel *) arg; - struct net_device *dev = ch->netdev; - - DBF_TEXT(setup, 3, __FUNCTION__); - if (event == CH_EVENT_TIMER) { - fsm_deltimer(&ch->timer); - ctc_pr_debug("%s: Timeout during RX init handshake\n", dev->name); - if (ch->retry++ < 3) - ch_action_restart(fi, event, arg); - else { - fsm_newstate(fi, CH_STATE_RXERR); - fsm_event(((struct ctc_priv *) dev->priv)->fsm, - DEV_EVENT_RXDOWN, dev); - } - } else - ctc_pr_warn("%s: Error during RX init handshake\n", dev->name); -} - -/** - * Notify device statemachine if we gave up initialization - * of RX channel. - * - * @param fi An instance of a channel statemachine. - * @param event The event, just happened. - * @param arg Generic pointer, casted from channel * upon call. - */ -static void -ch_action_rxinitfail(fsm_instance * fi, int event, void *arg) -{ - struct channel *ch = (struct channel *) arg; - struct net_device *dev = ch->netdev; - - DBF_TEXT(setup, 3, __FUNCTION__); - fsm_newstate(fi, CH_STATE_RXERR); - ctc_pr_warn("%s: RX initialization failed\n", dev->name); - ctc_pr_warn("%s: RX <-> RX connection detected\n", dev->name); - fsm_event(((struct ctc_priv *) dev->priv)->fsm, DEV_EVENT_RXDOWN, dev); -} - -/** - * Handle RX Unit check remote reset (remote disconnected) - * - * @param fi An instance of a channel statemachine. - * @param event The event, just happened. - * @param arg Generic pointer, casted from channel * upon call. - */ -static void -ch_action_rxdisc(fsm_instance * fi, int event, void *arg) -{ - struct channel *ch = (struct channel *) arg; - struct channel *ch2; - struct net_device *dev = ch->netdev; - - DBF_TEXT(trace, 3, __FUNCTION__); - fsm_deltimer(&ch->timer); - ctc_pr_debug("%s: Got remote disconnect, re-initializing ...\n", - dev->name); - - /** - * Notify device statemachine - */ - fsm_event(((struct ctc_priv *) dev->priv)->fsm, DEV_EVENT_RXDOWN, dev); - fsm_event(((struct ctc_priv *) dev->priv)->fsm, DEV_EVENT_TXDOWN, dev); - - fsm_newstate(fi, CH_STATE_DTERM); - ch2 = ((struct ctc_priv *) dev->priv)->channel[WRITE]; - fsm_newstate(ch2->fsm, CH_STATE_DTERM); - - ccw_device_halt(ch->cdev, (unsigned long) ch); - ccw_device_halt(ch2->cdev, (unsigned long) ch2); -} - -/** - * Handle error during TX channel initialization. - * - * @param fi An instance of a channel statemachine. - * @param event The event, just happened. - * @param arg Generic pointer, casted from channel * upon call. - */ -static void -ch_action_txiniterr(fsm_instance * fi, int event, void *arg) -{ - struct channel *ch = (struct channel *) arg; - struct net_device *dev = ch->netdev; - - DBF_TEXT(setup, 2, __FUNCTION__); - if (event == CH_EVENT_TIMER) { - fsm_deltimer(&ch->timer); - ctc_pr_debug("%s: Timeout during TX init handshake\n", dev->name); - if (ch->retry++ < 3) - ch_action_restart(fi, event, arg); - else { - fsm_newstate(fi, CH_STATE_TXERR); - fsm_event(((struct ctc_priv *) dev->priv)->fsm, - DEV_EVENT_TXDOWN, dev); - } - } else - ctc_pr_warn("%s: Error during TX init handshake\n", dev->name); -} - -/** - * Handle TX timeout by retrying operation. - * - * @param fi An instance of a channel statemachine. - * @param event The event, just happened. - * @param arg Generic pointer, casted from channel * upon call. - */ -static void -ch_action_txretry(fsm_instance * fi, int event, void *arg) -{ - struct channel *ch = (struct channel *) arg; - struct net_device *dev = ch->netdev; - unsigned long saveflags; - - DBF_TEXT(trace, 4, __FUNCTION__); - fsm_deltimer(&ch->timer); - if (ch->retry++ > 3) { - ctc_pr_debug("%s: TX retry failed, restarting channel\n", - dev->name); - fsm_event(((struct ctc_priv *) dev->priv)->fsm, - DEV_EVENT_TXDOWN, dev); - ch_action_restart(fi, event, arg); - } else { - struct sk_buff *skb; - - ctc_pr_debug("%s: TX retry %d\n", dev->name, ch->retry); - if ((skb = skb_peek(&ch->io_queue))) { - int rc = 0; - - clear_normalized_cda(&ch->ccw[4]); - ch->ccw[4].count = skb->len; - if (set_normalized_cda(&ch->ccw[4], skb->data)) { - ctc_pr_debug( - "%s: IDAL alloc failed, chan restart\n", - dev->name); - fsm_event(((struct ctc_priv *) dev->priv)->fsm, - DEV_EVENT_TXDOWN, dev); - ch_action_restart(fi, event, arg); - return; - } - fsm_addtimer(&ch->timer, 1000, CH_EVENT_TIMER, ch); - saveflags = 0; /* avoids compiler warning with - spin_unlock_irqrestore */ - if (event == CH_EVENT_TIMER) // only for TIMER not yet locked - spin_lock_irqsave(get_ccwdev_lock(ch->cdev), - saveflags); - rc = ccw_device_start(ch->cdev, &ch->ccw[3], - (unsigned long) ch, 0xff, 0); - if (event == CH_EVENT_TIMER) - spin_unlock_irqrestore(get_ccwdev_lock(ch->cdev), - saveflags); - if (rc != 0) { - fsm_deltimer(&ch->timer); - ccw_check_return_code(ch, rc, "TX in ch_action_txretry"); - ctc_purge_skb_queue(&ch->io_queue); - } - } - } - -} - -/** - * Handle fatal errors during an I/O command. - * - * @param fi An instance of a channel statemachine. - * @param event The event, just happened. - * @param arg Generic pointer, casted from channel * upon call. - */ -static void -ch_action_iofatal(fsm_instance * fi, int event, void *arg) -{ - struct channel *ch = (struct channel *) arg; - struct net_device *dev = ch->netdev; - - DBF_TEXT(trace, 3, __FUNCTION__); - fsm_deltimer(&ch->timer); - if (CHANNEL_DIRECTION(ch->flags) == READ) { - ctc_pr_debug("%s: RX I/O error\n", dev->name); - fsm_newstate(fi, CH_STATE_RXERR); - fsm_event(((struct ctc_priv *) dev->priv)->fsm, - DEV_EVENT_RXDOWN, dev); - } else { - ctc_pr_debug("%s: TX I/O error\n", dev->name); - fsm_newstate(fi, CH_STATE_TXERR); - fsm_event(((struct ctc_priv *) dev->priv)->fsm, - DEV_EVENT_TXDOWN, dev); - } -} - -static void -ch_action_reinit(fsm_instance *fi, int event, void *arg) -{ - struct channel *ch = (struct channel *)arg; - struct net_device *dev = ch->netdev; - struct ctc_priv *privptr = dev->priv; - - DBF_TEXT(trace, 4, __FUNCTION__); - ch_action_iofatal(fi, event, arg); - fsm_addtimer(&privptr->restart_timer, 1000, DEV_EVENT_RESTART, dev); -} - -/** - * The statemachine for a channel. - */ -static const fsm_node ch_fsm[] = { - {CH_STATE_STOPPED, CH_EVENT_STOP, fsm_action_nop }, - {CH_STATE_STOPPED, CH_EVENT_START, ch_action_start }, - {CH_STATE_STOPPED, CH_EVENT_FINSTAT, fsm_action_nop }, - {CH_STATE_STOPPED, CH_EVENT_MC_FAIL, fsm_action_nop }, - - {CH_STATE_NOTOP, CH_EVENT_STOP, ch_action_stop }, - {CH_STATE_NOTOP, CH_EVENT_START, fsm_action_nop }, - {CH_STATE_NOTOP, CH_EVENT_FINSTAT, fsm_action_nop }, - {CH_STATE_NOTOP, CH_EVENT_MC_FAIL, fsm_action_nop }, - {CH_STATE_NOTOP, CH_EVENT_MC_GOOD, ch_action_start }, - - {CH_STATE_STARTWAIT, CH_EVENT_STOP, ch_action_haltio }, - {CH_STATE_STARTWAIT, CH_EVENT_START, fsm_action_nop }, - {CH_STATE_STARTWAIT, CH_EVENT_FINSTAT, ch_action_setmode }, - {CH_STATE_STARTWAIT, CH_EVENT_TIMER, ch_action_setuperr }, - {CH_STATE_STARTWAIT, CH_EVENT_IO_ENODEV, ch_action_iofatal }, - {CH_STATE_STARTWAIT, CH_EVENT_IO_EIO, ch_action_reinit }, - {CH_STATE_STARTWAIT, CH_EVENT_MC_FAIL, ch_action_fail }, - - {CH_STATE_STARTRETRY, CH_EVENT_STOP, ch_action_haltio }, - {CH_STATE_STARTRETRY, CH_EVENT_TIMER, ch_action_setmode }, - {CH_STATE_STARTRETRY, CH_EVENT_FINSTAT, fsm_action_nop }, - {CH_STATE_STARTRETRY, CH_EVENT_MC_FAIL, ch_action_fail }, - - {CH_STATE_SETUPWAIT, CH_EVENT_STOP, ch_action_haltio }, - {CH_STATE_SETUPWAIT, CH_EVENT_START, fsm_action_nop }, - {CH_STATE_SETUPWAIT, CH_EVENT_FINSTAT, ch_action_firstio }, - {CH_STATE_SETUPWAIT, CH_EVENT_UC_RCRESET, ch_action_setuperr }, - {CH_STATE_SETUPWAIT, CH_EVENT_UC_RSRESET, ch_action_setuperr }, - {CH_STATE_SETUPWAIT, CH_EVENT_TIMER, ch_action_setmode }, - {CH_STATE_SETUPWAIT, CH_EVENT_IO_ENODEV, ch_action_iofatal }, - {CH_STATE_SETUPWAIT, CH_EVENT_IO_EIO, ch_action_reinit }, - {CH_STATE_SETUPWAIT, CH_EVENT_MC_FAIL, ch_action_fail }, - - {CH_STATE_RXINIT, CH_EVENT_STOP, ch_action_haltio }, - {CH_STATE_RXINIT, CH_EVENT_START, fsm_action_nop }, - {CH_STATE_RXINIT, CH_EVENT_FINSTAT, ch_action_rxidle }, - {CH_STATE_RXINIT, CH_EVENT_UC_RCRESET, ch_action_rxiniterr }, - {CH_STATE_RXINIT, CH_EVENT_UC_RSRESET, ch_action_rxiniterr }, - {CH_STATE_RXINIT, CH_EVENT_TIMER, ch_action_rxiniterr }, - {CH_STATE_RXINIT, CH_EVENT_ATTNBUSY, ch_action_rxinitfail }, - {CH_STATE_RXINIT, CH_EVENT_IO_ENODEV, ch_action_iofatal }, - {CH_STATE_RXINIT, CH_EVENT_IO_EIO, ch_action_reinit }, - {CH_STATE_RXINIT, CH_EVENT_UC_ZERO, ch_action_firstio }, - {CH_STATE_RXINIT, CH_EVENT_MC_FAIL, ch_action_fail }, - - {CH_STATE_RXIDLE, CH_EVENT_STOP, ch_action_haltio }, - {CH_STATE_RXIDLE, CH_EVENT_START, fsm_action_nop }, - {CH_STATE_RXIDLE, CH_EVENT_FINSTAT, ch_action_rx }, - {CH_STATE_RXIDLE, CH_EVENT_UC_RCRESET, ch_action_rxdisc }, -// {CH_STATE_RXIDLE, CH_EVENT_UC_RSRESET, ch_action_rxretry }, - {CH_STATE_RXIDLE, CH_EVENT_IO_ENODEV, ch_action_iofatal }, - {CH_STATE_RXIDLE, CH_EVENT_IO_EIO, ch_action_reinit }, - {CH_STATE_RXIDLE, CH_EVENT_MC_FAIL, ch_action_fail }, - {CH_STATE_RXIDLE, CH_EVENT_UC_ZERO, ch_action_rx }, - - {CH_STATE_TXINIT, CH_EVENT_STOP, ch_action_haltio }, - {CH_STATE_TXINIT, CH_EVENT_START, fsm_action_nop }, - {CH_STATE_TXINIT, CH_EVENT_FINSTAT, ch_action_txidle }, - {CH_STATE_TXINIT, CH_EVENT_UC_RCRESET, ch_action_txiniterr }, - {CH_STATE_TXINIT, CH_EVENT_UC_RSRESET, ch_action_txiniterr }, - {CH_STATE_TXINIT, CH_EVENT_TIMER, ch_action_txiniterr }, - {CH_STATE_TXINIT, CH_EVENT_IO_ENODEV, ch_action_iofatal }, - {CH_STATE_TXINIT, CH_EVENT_IO_EIO, ch_action_reinit }, - {CH_STATE_TXINIT, CH_EVENT_MC_FAIL, ch_action_fail }, - - {CH_STATE_TXIDLE, CH_EVENT_STOP, ch_action_haltio }, - {CH_STATE_TXIDLE, CH_EVENT_START, fsm_action_nop }, - {CH_STATE_TXIDLE, CH_EVENT_FINSTAT, ch_action_firstio }, - {CH_STATE_TXIDLE, CH_EVENT_UC_RCRESET, fsm_action_nop }, - {CH_STATE_TXIDLE, CH_EVENT_UC_RSRESET, fsm_action_nop }, - {CH_STATE_TXIDLE, CH_EVENT_IO_ENODEV, ch_action_iofatal }, - {CH_STATE_TXIDLE, CH_EVENT_IO_EIO, ch_action_reinit }, - {CH_STATE_TXIDLE, CH_EVENT_MC_FAIL, ch_action_fail }, - - {CH_STATE_TERM, CH_EVENT_STOP, fsm_action_nop }, - {CH_STATE_TERM, CH_EVENT_START, ch_action_restart }, - {CH_STATE_TERM, CH_EVENT_FINSTAT, ch_action_stopped }, - {CH_STATE_TERM, CH_EVENT_UC_RCRESET, fsm_action_nop }, - {CH_STATE_TERM, CH_EVENT_UC_RSRESET, fsm_action_nop }, - {CH_STATE_TERM, CH_EVENT_MC_FAIL, ch_action_fail }, - - {CH_STATE_DTERM, CH_EVENT_STOP, ch_action_haltio }, - {CH_STATE_DTERM, CH_EVENT_START, ch_action_restart }, - {CH_STATE_DTERM, CH_EVENT_FINSTAT, ch_action_setmode }, - {CH_STATE_DTERM, CH_EVENT_UC_RCRESET, fsm_action_nop }, - {CH_STATE_DTERM, CH_EVENT_UC_RSRESET, fsm_action_nop }, - {CH_STATE_DTERM, CH_EVENT_MC_FAIL, ch_action_fail }, - - {CH_STATE_TX, CH_EVENT_STOP, ch_action_haltio }, - {CH_STATE_TX, CH_EVENT_START, fsm_action_nop }, - {CH_STATE_TX, CH_EVENT_FINSTAT, ch_action_txdone }, - {CH_STATE_TX, CH_EVENT_UC_RCRESET, ch_action_txretry }, - {CH_STATE_TX, CH_EVENT_UC_RSRESET, ch_action_txretry }, - {CH_STATE_TX, CH_EVENT_TIMER, ch_action_txretry }, - {CH_STATE_TX, CH_EVENT_IO_ENODEV, ch_action_iofatal }, - {CH_STATE_TX, CH_EVENT_IO_EIO, ch_action_reinit }, - {CH_STATE_TX, CH_EVENT_MC_FAIL, ch_action_fail }, - - {CH_STATE_RXERR, CH_EVENT_STOP, ch_action_haltio }, - {CH_STATE_TXERR, CH_EVENT_STOP, ch_action_haltio }, - {CH_STATE_TXERR, CH_EVENT_MC_FAIL, ch_action_fail }, - {CH_STATE_RXERR, CH_EVENT_MC_FAIL, ch_action_fail }, -}; - -static const int CH_FSM_LEN = sizeof (ch_fsm) / sizeof (fsm_node); - -/** - * Functions related to setup and device detection. - *****************************************************************************/ - -static inline int -less_than(char *id1, char *id2) -{ - int dev1, dev2, i; - - for (i = 0; i < 5; i++) { - id1++; - id2++; - } - dev1 = simple_strtoul(id1, &id1, 16); - dev2 = simple_strtoul(id2, &id2, 16); - - return (dev1 < dev2); -} - -/** - * Add a new channel to the list of channels. - * Keeps the channel list sorted. - * - * @param cdev The ccw_device to be added. - * @param type The type class of the new channel. - * - * @return 0 on success, !0 on error. - */ -static int -add_channel(struct ccw_device *cdev, enum channel_types type) -{ - struct channel **c = &channels; - struct channel *ch; - - DBF_TEXT(trace, 2, __FUNCTION__); - ch = kzalloc(sizeof(struct channel), GFP_KERNEL); - if (!ch) { - ctc_pr_warn("ctc: Out of memory in add_channel\n"); - return -1; - } - /* assure all flags and counters are reset */ - ch->ccw = kzalloc(8 * sizeof(struct ccw1), GFP_KERNEL | GFP_DMA); - if (!ch->ccw) { - kfree(ch); - ctc_pr_warn("ctc: Out of memory in add_channel\n"); - return -1; - } - - - /** - * "static" ccws are used in the following way: - * - * ccw[0..2] (Channel program for generic I/O): - * 0: prepare - * 1: read or write (depending on direction) with fixed - * buffer (idal allocated once when buffer is allocated) - * 2: nop - * ccw[3..5] (Channel program for direct write of packets) - * 3: prepare - * 4: write (idal allocated on every write). - * 5: nop - * ccw[6..7] (Channel program for initial channel setup): - * 6: set extended mode - * 7: nop - * - * ch->ccw[0..5] are initialized in ch_action_start because - * the channel's direction is yet unknown here. - */ - ch->ccw[6].cmd_code = CCW_CMD_SET_EXTENDED; - ch->ccw[6].flags = CCW_FLAG_SLI; - - ch->ccw[7].cmd_code = CCW_CMD_NOOP; - ch->ccw[7].flags = CCW_FLAG_SLI; - - ch->cdev = cdev; - snprintf(ch->id, CTC_ID_SIZE, "ch-%s", cdev->dev.bus_id); - ch->type = type; - ch->fsm = init_fsm(ch->id, ch_state_names, - ch_event_names, NR_CH_STATES, NR_CH_EVENTS, - ch_fsm, CH_FSM_LEN, GFP_KERNEL); - if (ch->fsm == NULL) { - ctc_pr_warn("ctc: Could not create FSM in add_channel\n"); - kfree(ch->ccw); - kfree(ch); - return -1; - } - fsm_newstate(ch->fsm, CH_STATE_IDLE); - ch->irb = kzalloc(sizeof(struct irb), GFP_KERNEL); - if (!ch->irb) { - ctc_pr_warn("ctc: Out of memory in add_channel\n"); - kfree_fsm(ch->fsm); - kfree(ch->ccw); - kfree(ch); - return -1; - } - while (*c && less_than((*c)->id, ch->id)) - c = &(*c)->next; - if (*c && (!strncmp((*c)->id, ch->id, CTC_ID_SIZE))) { - ctc_pr_debug( - "ctc: add_channel: device %s already in list, " - "using old entry\n", (*c)->id); - kfree(ch->irb); - kfree_fsm(ch->fsm); - kfree(ch->ccw); - kfree(ch); - return 0; - } - - spin_lock_init(&ch->collect_lock); - - fsm_settimer(ch->fsm, &ch->timer); - skb_queue_head_init(&ch->io_queue); - skb_queue_head_init(&ch->collect_queue); - ch->next = *c; - *c = ch; - return 0; -} - -/** - * Release a specific channel in the channel list. - * - * @param ch Pointer to channel struct to be released. - */ -static void -channel_free(struct channel *ch) -{ - ch->flags &= ~CHANNEL_FLAGS_INUSE; - fsm_newstate(ch->fsm, CH_STATE_IDLE); -} - -/** - * Remove a specific channel in the channel list. - * - * @param ch Pointer to channel struct to be released. - */ -static void -channel_remove(struct channel *ch) -{ - struct channel **c = &channels; - - DBF_TEXT(trace, 2, __FUNCTION__); - if (ch == NULL) - return; - - channel_free(ch); - while (*c) { - if (*c == ch) { - *c = ch->next; - fsm_deltimer(&ch->timer); - kfree_fsm(ch->fsm); - clear_normalized_cda(&ch->ccw[4]); - if (ch->trans_skb != NULL) { - clear_normalized_cda(&ch->ccw[1]); - dev_kfree_skb(ch->trans_skb); - } - kfree(ch->ccw); - kfree(ch->irb); - kfree(ch); - return; - } - c = &((*c)->next); - } -} - -/** - * Get a specific channel from the channel list. - * - * @param type Type of channel we are interested in. - * @param id Id of channel we are interested in. - * @param direction Direction we want to use this channel for. - * - * @return Pointer to a channel or NULL if no matching channel available. - */ -static struct channel -* -channel_get(enum channel_types type, char *id, int direction) -{ - struct channel *ch = channels; - - DBF_TEXT(trace, 3, __FUNCTION__); -#ifdef DEBUG - ctc_pr_debug("ctc: %s(): searching for ch with id %s and type %d\n", - __func__, id, type); -#endif - - while (ch && ((strncmp(ch->id, id, CTC_ID_SIZE)) || (ch->type != type))) { -#ifdef DEBUG - ctc_pr_debug("ctc: %s(): ch=0x%p (id=%s, type=%d\n", - __func__, ch, ch->id, ch->type); -#endif - ch = ch->next; - } -#ifdef DEBUG - ctc_pr_debug("ctc: %s(): ch=0x%pq (id=%s, type=%d\n", - __func__, ch, ch->id, ch->type); -#endif - if (!ch) { - ctc_pr_warn("ctc: %s(): channel with id %s " - "and type %d not found in channel list\n", - __func__, id, type); - } else { - if (ch->flags & CHANNEL_FLAGS_INUSE) - ch = NULL; - else { - ch->flags |= CHANNEL_FLAGS_INUSE; - ch->flags &= ~CHANNEL_FLAGS_RWMASK; - ch->flags |= (direction == WRITE) - ? CHANNEL_FLAGS_WRITE : CHANNEL_FLAGS_READ; - fsm_newstate(ch->fsm, CH_STATE_STOPPED); - } - } - return ch; -} - -/** - * Return the channel type by name. - * - * @param name Name of network interface. - * - * @return Type class of channel to be used for that interface. - */ -static enum channel_types inline -extract_channel_media(char *name) -{ - enum channel_types ret = channel_type_unknown; - - if (name != NULL) { - if (strncmp(name, "ctc", 3) == 0) - ret = channel_type_parallel; - if (strncmp(name, "escon", 5) == 0) - ret = channel_type_escon; - } - return ret; -} - -static long -__ctc_check_irb_error(struct ccw_device *cdev, struct irb *irb) -{ - if (!IS_ERR(irb)) - return 0; - - switch (PTR_ERR(irb)) { - case -EIO: - ctc_pr_warn("i/o-error on device %s\n", cdev->dev.bus_id); -// CTC_DBF_TEXT(trace, 2, "ckirberr"); -// CTC_DBF_TEXT_(trace, 2, " rc%d", -EIO); - break; - case -ETIMEDOUT: - ctc_pr_warn("timeout on device %s\n", cdev->dev.bus_id); -// CTC_DBF_TEXT(trace, 2, "ckirberr"); -// CTC_DBF_TEXT_(trace, 2, " rc%d", -ETIMEDOUT); - break; - default: - ctc_pr_warn("unknown error %ld on device %s\n", PTR_ERR(irb), - cdev->dev.bus_id); -// CTC_DBF_TEXT(trace, 2, "ckirberr"); -// CTC_DBF_TEXT(trace, 2, " rc???"); - } - return PTR_ERR(irb); -} - -/** - * Main IRQ handler. - * - * @param cdev The ccw_device the interrupt is for. - * @param intparm interruption parameter. - * @param irb interruption response block. - */ -static void -ctc_irq_handler(struct ccw_device *cdev, unsigned long intparm, struct irb *irb) -{ - struct channel *ch; - struct net_device *dev; - struct ctc_priv *priv; - - DBF_TEXT(trace, 5, __FUNCTION__); - if (__ctc_check_irb_error(cdev, irb)) - return; - - /* Check for unsolicited interrupts. */ - if (!cdev->dev.driver_data) { - ctc_pr_warn("ctc: Got unsolicited irq: %s c-%02x d-%02x\n", - cdev->dev.bus_id, irb->scsw.cstat, - irb->scsw.dstat); - return; - } - - priv = ((struct ccwgroup_device *)cdev->dev.driver_data) - ->dev.driver_data; - - /* Try to extract channel from driver data. */ - if (priv->channel[READ]->cdev == cdev) - ch = priv->channel[READ]; - else if (priv->channel[WRITE]->cdev == cdev) - ch = priv->channel[WRITE]; - else { - ctc_pr_err("ctc: Can't determine channel for interrupt, " - "device %s\n", cdev->dev.bus_id); - return; - } - - dev = (struct net_device *) (ch->netdev); - if (dev == NULL) { - ctc_pr_crit("ctc: ctc_irq_handler dev=NULL bus_id=%s, ch=0x%p\n", - cdev->dev.bus_id, ch); - return; - } - -#ifdef DEBUG - ctc_pr_debug("%s: interrupt for device: %s received c-%02x d-%02x\n", - dev->name, ch->id, irb->scsw.cstat, irb->scsw.dstat); -#endif - - /* Copy interruption response block. */ - memcpy(ch->irb, irb, sizeof(struct irb)); - - /* Check for good subchannel return code, otherwise error message */ - if (ch->irb->scsw.cstat) { - fsm_event(ch->fsm, CH_EVENT_SC_UNKNOWN, ch); - ctc_pr_warn("%s: subchannel check for device: %s - %02x %02x\n", - dev->name, ch->id, ch->irb->scsw.cstat, - ch->irb->scsw.dstat); - return; - } - - /* Check the reason-code of a unit check */ - if (ch->irb->scsw.dstat & DEV_STAT_UNIT_CHECK) { - ccw_unit_check(ch, ch->irb->ecw[0]); - return; - } - if (ch->irb->scsw.dstat & DEV_STAT_BUSY) { - if (ch->irb->scsw.dstat & DEV_STAT_ATTENTION) - fsm_event(ch->fsm, CH_EVENT_ATTNBUSY, ch); - else - fsm_event(ch->fsm, CH_EVENT_BUSY, ch); - return; - } - if (ch->irb->scsw.dstat & DEV_STAT_ATTENTION) { - fsm_event(ch->fsm, CH_EVENT_ATTN, ch); - return; - } - if ((ch->irb->scsw.stctl & SCSW_STCTL_SEC_STATUS) || - (ch->irb->scsw.stctl == SCSW_STCTL_STATUS_PEND) || - (ch->irb->scsw.stctl == - (SCSW_STCTL_ALERT_STATUS | SCSW_STCTL_STATUS_PEND))) - fsm_event(ch->fsm, CH_EVENT_FINSTAT, ch); - else - fsm_event(ch->fsm, CH_EVENT_IRQ, ch); - -} - -/** - * Actions for interface - statemachine. - *****************************************************************************/ - -/** - * Startup channels by sending CH_EVENT_START to each channel. - * - * @param fi An instance of an interface statemachine. - * @param event The event, just happened. - * @param arg Generic pointer, casted from struct net_device * upon call. - */ -static void -dev_action_start(fsm_instance * fi, int event, void *arg) -{ - struct net_device *dev = (struct net_device *) arg; - struct ctc_priv *privptr = dev->priv; - int direction; - - DBF_TEXT(setup, 3, __FUNCTION__); - fsm_deltimer(&privptr->restart_timer); - fsm_newstate(fi, DEV_STATE_STARTWAIT_RXTX); - for (direction = READ; direction <= WRITE; direction++) { - struct channel *ch = privptr->channel[direction]; - fsm_event(ch->fsm, CH_EVENT_START, ch); - } -} - -/** - * Shutdown channels by sending CH_EVENT_STOP to each channel. - * - * @param fi An instance of an interface statemachine. - * @param event The event, just happened. - * @param arg Generic pointer, casted from struct net_device * upon call. - */ -static void -dev_action_stop(fsm_instance * fi, int event, void *arg) -{ - struct net_device *dev = (struct net_device *) arg; - struct ctc_priv *privptr = dev->priv; - int direction; - - DBF_TEXT(trace, 3, __FUNCTION__); - fsm_newstate(fi, DEV_STATE_STOPWAIT_RXTX); - for (direction = READ; direction <= WRITE; direction++) { - struct channel *ch = privptr->channel[direction]; - fsm_event(ch->fsm, CH_EVENT_STOP, ch); - } -} -static void -dev_action_restart(fsm_instance *fi, int event, void *arg) -{ - struct net_device *dev = (struct net_device *)arg; - struct ctc_priv *privptr = dev->priv; - - DBF_TEXT(trace, 3, __FUNCTION__); - ctc_pr_debug("%s: Restarting\n", dev->name); - dev_action_stop(fi, event, arg); - fsm_event(privptr->fsm, DEV_EVENT_STOP, dev); - fsm_addtimer(&privptr->restart_timer, CTC_TIMEOUT_5SEC, - DEV_EVENT_START, dev); -} - -/** - * Called from channel statemachine - * when a channel is up and running. - * - * @param fi An instance of an interface statemachine. - * @param event The event, just happened. - * @param arg Generic pointer, casted from struct net_device * upon call. - */ -static void -dev_action_chup(fsm_instance * fi, int event, void *arg) -{ - struct net_device *dev = (struct net_device *) arg; - - DBF_TEXT(trace, 3, __FUNCTION__); - switch (fsm_getstate(fi)) { - case DEV_STATE_STARTWAIT_RXTX: - if (event == DEV_EVENT_RXUP) - fsm_newstate(fi, DEV_STATE_STARTWAIT_TX); - else - fsm_newstate(fi, DEV_STATE_STARTWAIT_RX); - break; - case DEV_STATE_STARTWAIT_RX: - if (event == DEV_EVENT_RXUP) { - fsm_newstate(fi, DEV_STATE_RUNNING); - ctc_pr_info("%s: connected with remote side\n", - dev->name); - ctc_clear_busy(dev); - } - break; - case DEV_STATE_STARTWAIT_TX: - if (event == DEV_EVENT_TXUP) { - fsm_newstate(fi, DEV_STATE_RUNNING); - ctc_pr_info("%s: connected with remote side\n", - dev->name); - ctc_clear_busy(dev); - } - break; - case DEV_STATE_STOPWAIT_TX: - if (event == DEV_EVENT_RXUP) - fsm_newstate(fi, DEV_STATE_STOPWAIT_RXTX); - break; - case DEV_STATE_STOPWAIT_RX: - if (event == DEV_EVENT_TXUP) - fsm_newstate(fi, DEV_STATE_STOPWAIT_RXTX); - break; - } -} - -/** - * Called from channel statemachine - * when a channel has been shutdown. - * - * @param fi An instance of an interface statemachine. - * @param event The event, just happened. - * @param arg Generic pointer, casted from struct net_device * upon call. - */ -static void -dev_action_chdown(fsm_instance * fi, int event, void *arg) -{ - - DBF_TEXT(trace, 3, __FUNCTION__); - switch (fsm_getstate(fi)) { - case DEV_STATE_RUNNING: - if (event == DEV_EVENT_TXDOWN) - fsm_newstate(fi, DEV_STATE_STARTWAIT_TX); - else - fsm_newstate(fi, DEV_STATE_STARTWAIT_RX); - break; - case DEV_STATE_STARTWAIT_RX: - if (event == DEV_EVENT_TXDOWN) - fsm_newstate(fi, DEV_STATE_STARTWAIT_RXTX); - break; - case DEV_STATE_STARTWAIT_TX: - if (event == DEV_EVENT_RXDOWN) - fsm_newstate(fi, DEV_STATE_STARTWAIT_RXTX); - break; - case DEV_STATE_STOPWAIT_RXTX: - if (event == DEV_EVENT_TXDOWN) - fsm_newstate(fi, DEV_STATE_STOPWAIT_RX); - else - fsm_newstate(fi, DEV_STATE_STOPWAIT_TX); - break; - case DEV_STATE_STOPWAIT_RX: - if (event == DEV_EVENT_RXDOWN) - fsm_newstate(fi, DEV_STATE_STOPPED); - break; - case DEV_STATE_STOPWAIT_TX: - if (event == DEV_EVENT_TXDOWN) - fsm_newstate(fi, DEV_STATE_STOPPED); - break; - } -} - -static const fsm_node dev_fsm[] = { - {DEV_STATE_STOPPED, DEV_EVENT_START, dev_action_start}, - - {DEV_STATE_STOPWAIT_RXTX, DEV_EVENT_START, dev_action_start }, - {DEV_STATE_STOPWAIT_RXTX, DEV_EVENT_RXDOWN, dev_action_chdown }, - {DEV_STATE_STOPWAIT_RXTX, DEV_EVENT_TXDOWN, dev_action_chdown }, - {DEV_STATE_STOPWAIT_RXTX, DEV_EVENT_RESTART, dev_action_restart }, - - {DEV_STATE_STOPWAIT_RX, DEV_EVENT_START, dev_action_start }, - {DEV_STATE_STOPWAIT_RX, DEV_EVENT_RXUP, dev_action_chup }, - {DEV_STATE_STOPWAIT_RX, DEV_EVENT_TXUP, dev_action_chup }, - {DEV_STATE_STOPWAIT_RX, DEV_EVENT_RXDOWN, dev_action_chdown }, - {DEV_STATE_STOPWAIT_RX, DEV_EVENT_RESTART, dev_action_restart }, - - {DEV_STATE_STOPWAIT_TX, DEV_EVENT_START, dev_action_start }, - {DEV_STATE_STOPWAIT_TX, DEV_EVENT_RXUP, dev_action_chup }, - {DEV_STATE_STOPWAIT_TX, DEV_EVENT_TXUP, dev_action_chup }, - {DEV_STATE_STOPWAIT_TX, DEV_EVENT_TXDOWN, dev_action_chdown }, - {DEV_STATE_STOPWAIT_TX, DEV_EVENT_RESTART, dev_action_restart }, - - {DEV_STATE_STARTWAIT_RXTX, DEV_EVENT_STOP, dev_action_stop }, - {DEV_STATE_STARTWAIT_RXTX, DEV_EVENT_RXUP, dev_action_chup }, - {DEV_STATE_STARTWAIT_RXTX, DEV_EVENT_TXUP, dev_action_chup }, - {DEV_STATE_STARTWAIT_RXTX, DEV_EVENT_RXDOWN, dev_action_chdown }, - {DEV_STATE_STARTWAIT_RXTX, DEV_EVENT_TXDOWN, dev_action_chdown }, - {DEV_STATE_STARTWAIT_RXTX, DEV_EVENT_RESTART, dev_action_restart }, - - {DEV_STATE_STARTWAIT_TX, DEV_EVENT_STOP, dev_action_stop }, - {DEV_STATE_STARTWAIT_TX, DEV_EVENT_RXUP, dev_action_chup }, - {DEV_STATE_STARTWAIT_TX, DEV_EVENT_TXUP, dev_action_chup }, - {DEV_STATE_STARTWAIT_TX, DEV_EVENT_RXDOWN, dev_action_chdown }, - {DEV_STATE_STARTWAIT_TX, DEV_EVENT_RESTART, dev_action_restart }, - - {DEV_STATE_STARTWAIT_RX, DEV_EVENT_STOP, dev_action_stop }, - {DEV_STATE_STARTWAIT_RX, DEV_EVENT_RXUP, dev_action_chup }, - {DEV_STATE_STARTWAIT_RX, DEV_EVENT_TXUP, dev_action_chup }, - {DEV_STATE_STARTWAIT_RX, DEV_EVENT_TXDOWN, dev_action_chdown }, - {DEV_STATE_STARTWAIT_RX, DEV_EVENT_RESTART, dev_action_restart }, - - {DEV_STATE_RUNNING, DEV_EVENT_STOP, dev_action_stop }, - {DEV_STATE_RUNNING, DEV_EVENT_RXDOWN, dev_action_chdown }, - {DEV_STATE_RUNNING, DEV_EVENT_TXDOWN, dev_action_chdown }, - {DEV_STATE_RUNNING, DEV_EVENT_TXUP, fsm_action_nop }, - {DEV_STATE_RUNNING, DEV_EVENT_RXUP, fsm_action_nop }, - {DEV_STATE_RUNNING, DEV_EVENT_RESTART, dev_action_restart }, -}; - -static const int DEV_FSM_LEN = sizeof (dev_fsm) / sizeof (fsm_node); - -/** - * Transmit a packet. - * This is a helper function for ctc_tx(). - * - * @param ch Channel to be used for sending. - * @param skb Pointer to struct sk_buff of packet to send. - * The linklevel header has already been set up - * by ctc_tx(). - * - * @return 0 on success, -ERRNO on failure. (Never fails.) - */ -static int -transmit_skb(struct channel *ch, struct sk_buff *skb) -{ - unsigned long saveflags; - struct ll_header header; - int rc = 0; - - DBF_TEXT(trace, 5, __FUNCTION__); - /* we need to acquire the lock for testing the state - * otherwise we can have an IRQ changing the state to - * TXIDLE after the test but before acquiring the lock. - */ - spin_lock_irqsave(&ch->collect_lock, saveflags); - if (fsm_getstate(ch->fsm) != CH_STATE_TXIDLE) { - int l = skb->len + LL_HEADER_LENGTH; - - if (ch->collect_len + l > ch->max_bufsize - 2) { - spin_unlock_irqrestore(&ch->collect_lock, saveflags); - return -EBUSY; - } else { - atomic_inc(&skb->users); - header.length = l; - header.type = skb->protocol; - header.unused = 0; - memcpy(skb_push(skb, LL_HEADER_LENGTH), &header, - LL_HEADER_LENGTH); - skb_queue_tail(&ch->collect_queue, skb); - ch->collect_len += l; - } - spin_unlock_irqrestore(&ch->collect_lock, saveflags); - } else { - __u16 block_len; - int ccw_idx; - struct sk_buff *nskb; - unsigned long hi; - spin_unlock_irqrestore(&ch->collect_lock, saveflags); - /** - * Protect skb against beeing free'd by upper - * layers. - */ - atomic_inc(&skb->users); - ch->prof.txlen += skb->len; - header.length = skb->len + LL_HEADER_LENGTH; - header.type = skb->protocol; - header.unused = 0; - memcpy(skb_push(skb, LL_HEADER_LENGTH), &header, - LL_HEADER_LENGTH); - block_len = skb->len + 2; - *((__u16 *) skb_push(skb, 2)) = block_len; - - /** - * IDAL support in CTC is broken, so we have to - * care about skb's above 2G ourselves. - */ - hi = ((unsigned long)skb_tail_pointer(skb) + - LL_HEADER_LENGTH) >> 31; - if (hi) { - nskb = alloc_skb(skb->len, GFP_ATOMIC | GFP_DMA); - if (!nskb) { - atomic_dec(&skb->users); - skb_pull(skb, LL_HEADER_LENGTH + 2); - ctc_clear_busy(ch->netdev); - return -ENOMEM; - } else { - memcpy(skb_put(nskb, skb->len), - skb->data, skb->len); - atomic_inc(&nskb->users); - atomic_dec(&skb->users); - dev_kfree_skb_irq(skb); - skb = nskb; - } - } - - ch->ccw[4].count = block_len; - if (set_normalized_cda(&ch->ccw[4], skb->data)) { - /** - * idal allocation failed, try via copying to - * trans_skb. trans_skb usually has a pre-allocated - * idal. - */ - if (ctc_checkalloc_buffer(ch, 1)) { - /** - * Remove our header. It gets added - * again on retransmit. - */ - atomic_dec(&skb->users); - skb_pull(skb, LL_HEADER_LENGTH + 2); - ctc_clear_busy(ch->netdev); - return -EBUSY; - } - - skb_reset_tail_pointer(ch->trans_skb); - ch->trans_skb->len = 0; - ch->ccw[1].count = skb->len; - skb_copy_from_linear_data(skb, skb_put(ch->trans_skb, - skb->len), - skb->len); - atomic_dec(&skb->users); - dev_kfree_skb_irq(skb); - ccw_idx = 0; - } else { - skb_queue_tail(&ch->io_queue, skb); - ccw_idx = 3; - } - ch->retry = 0; - fsm_newstate(ch->fsm, CH_STATE_TX); - fsm_addtimer(&ch->timer, CTC_TIMEOUT_5SEC, CH_EVENT_TIMER, ch); - spin_lock_irqsave(get_ccwdev_lock(ch->cdev), saveflags); - ch->prof.send_stamp = current_kernel_time(); - rc = ccw_device_start(ch->cdev, &ch->ccw[ccw_idx], - (unsigned long) ch, 0xff, 0); - spin_unlock_irqrestore(get_ccwdev_lock(ch->cdev), saveflags); - if (ccw_idx == 3) - ch->prof.doios_single++; - if (rc != 0) { - fsm_deltimer(&ch->timer); - ccw_check_return_code(ch, rc, "single skb TX"); - if (ccw_idx == 3) - skb_dequeue_tail(&ch->io_queue); - /** - * Remove our header. It gets added - * again on retransmit. - */ - skb_pull(skb, LL_HEADER_LENGTH + 2); - } else { - if (ccw_idx == 0) { - struct net_device *dev = ch->netdev; - struct ctc_priv *privptr = dev->priv; - privptr->stats.tx_packets++; - privptr->stats.tx_bytes += - skb->len - LL_HEADER_LENGTH; - } - } - } - - ctc_clear_busy(ch->netdev); - return rc; -} - -/** - * Interface API for upper network layers - *****************************************************************************/ - -/** - * Open an interface. - * Called from generic network layer when ifconfig up is run. - * - * @param dev Pointer to interface struct. - * - * @return 0 on success, -ERRNO on failure. (Never fails.) - */ -static int -ctc_open(struct net_device * dev) -{ - DBF_TEXT(trace, 5, __FUNCTION__); - fsm_event(((struct ctc_priv *) dev->priv)->fsm, DEV_EVENT_START, dev); - return 0; -} - -/** - * Close an interface. - * Called from generic network layer when ifconfig down is run. - * - * @param dev Pointer to interface struct. - * - * @return 0 on success, -ERRNO on failure. (Never fails.) - */ -static int -ctc_close(struct net_device * dev) -{ - DBF_TEXT(trace, 5, __FUNCTION__); - fsm_event(((struct ctc_priv *) dev->priv)->fsm, DEV_EVENT_STOP, dev); - return 0; -} - -/** - * Start transmission of a packet. - * Called from generic network device layer. - * - * @param skb Pointer to buffer containing the packet. - * @param dev Pointer to interface struct. - * - * @return 0 if packet consumed, !0 if packet rejected. - * Note: If we return !0, then the packet is free'd by - * the generic network layer. - */ -static int -ctc_tx(struct sk_buff *skb, struct net_device * dev) -{ - int rc = 0; - struct ctc_priv *privptr = (struct ctc_priv *) dev->priv; - - DBF_TEXT(trace, 5, __FUNCTION__); - /** - * Some sanity checks ... - */ - if (skb == NULL) { - ctc_pr_warn("%s: NULL sk_buff passed\n", dev->name); - privptr->stats.tx_dropped++; - return 0; - } - if (skb_headroom(skb) < (LL_HEADER_LENGTH + 2)) { - ctc_pr_warn("%s: Got sk_buff with head room < %ld bytes\n", - dev->name, LL_HEADER_LENGTH + 2); - dev_kfree_skb(skb); - privptr->stats.tx_dropped++; - return 0; - } - - /** - * If channels are not running, try to restart them - * and throw away packet. - */ - if (fsm_getstate(privptr->fsm) != DEV_STATE_RUNNING) { - fsm_event(privptr->fsm, DEV_EVENT_START, dev); - dev_kfree_skb(skb); - privptr->stats.tx_dropped++; - privptr->stats.tx_errors++; - privptr->stats.tx_carrier_errors++; - return 0; - } - - if (ctc_test_and_set_busy(dev)) - return -EBUSY; - - dev->trans_start = jiffies; - if (transmit_skb(privptr->channel[WRITE], skb) != 0) - rc = 1; - return rc; -} - -/** - * Sets MTU of an interface. - * - * @param dev Pointer to interface struct. - * @param new_mtu The new MTU to use for this interface. - * - * @return 0 on success, -EINVAL if MTU is out of valid range. - * (valid range is 576 .. 65527). If VM is on the - * remote side, maximum MTU is 32760, however this is - * <em>not</em> checked here. - */ -static int -ctc_change_mtu(struct net_device * dev, int new_mtu) -{ - struct ctc_priv *privptr = (struct ctc_priv *) dev->priv; - - DBF_TEXT(trace, 3, __FUNCTION__); - if ((new_mtu < 576) || (new_mtu > 65527) || - (new_mtu > (privptr->channel[READ]->max_bufsize - - LL_HEADER_LENGTH - 2))) - return -EINVAL; - dev->mtu = new_mtu; - dev->hard_header_len = LL_HEADER_LENGTH + 2; - return 0; -} - -/** - * Returns interface statistics of a device. - * - * @param dev Pointer to interface struct. - * - * @return Pointer to stats struct of this interface. - */ -static struct net_device_stats * -ctc_stats(struct net_device * dev) -{ - return &((struct ctc_priv *) dev->priv)->stats; -} - -/* - * sysfs attributes - */ - -static ssize_t -buffer_show(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct ctc_priv *priv; - - priv = dev->driver_data; - if (!priv) - return -ENODEV; - return sprintf(buf, "%d\n", - priv->buffer_size); -} - -static ssize_t -buffer_write(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) -{ - struct ctc_priv *priv; - struct net_device *ndev; - int bs1; - char buffer[16]; - - DBF_TEXT(trace, 3, __FUNCTION__); - DBF_TEXT(trace, 3, buf); - priv = dev->driver_data; - if (!priv) { - DBF_TEXT(trace, 3, "bfnopriv"); - return -ENODEV; - } - - sscanf(buf, "%u", &bs1); - if (bs1 > CTC_BUFSIZE_LIMIT) - goto einval; - if (bs1 < (576 + LL_HEADER_LENGTH + 2)) - goto einval; - priv->buffer_size = bs1; // just to overwrite the default - - ndev = priv->channel[READ]->netdev; - if (!ndev) { - DBF_TEXT(trace, 3, "bfnondev"); - return -ENODEV; - } - - if ((ndev->flags & IFF_RUNNING) && - (bs1 < (ndev->mtu + LL_HEADER_LENGTH + 2))) - goto einval; - - priv->channel[READ]->max_bufsize = bs1; - priv->channel[WRITE]->max_bufsize = bs1; - if (!(ndev->flags & IFF_RUNNING)) - ndev->mtu = bs1 - LL_HEADER_LENGTH - 2; - priv->channel[READ]->flags |= CHANNEL_FLAGS_BUFSIZE_CHANGED; - priv->channel[WRITE]->flags |= CHANNEL_FLAGS_BUFSIZE_CHANGED; - - sprintf(buffer, "%d",priv->buffer_size); - DBF_TEXT(trace, 3, buffer); - return count; - -einval: - DBF_TEXT(trace, 3, "buff_err"); - return -EINVAL; -} - -static ssize_t -loglevel_show(struct device *dev, struct device_attribute *attr, char *buf) -{ - return sprintf(buf, "%d\n", loglevel); -} - -static ssize_t -loglevel_write(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) -{ - int ll1; - - DBF_TEXT(trace, 5, __FUNCTION__); - sscanf(buf, "%i", &ll1); - - if ((ll1 > CTC_LOGLEVEL_MAX) || (ll1 < 0)) - return -EINVAL; - loglevel = ll1; - return count; -} - -static void -ctc_print_statistics(struct ctc_priv *priv) -{ - char *sbuf; - char *p; - - DBF_TEXT(trace, 4, __FUNCTION__); - if (!priv) - return; - sbuf = kmalloc(2048, GFP_KERNEL); - if (sbuf == NULL) - return; - p = sbuf; - - p += sprintf(p, " Device FSM state: %s\n", - fsm_getstate_str(priv->fsm)); - p += sprintf(p, " RX channel FSM state: %s\n", - fsm_getstate_str(priv->channel[READ]->fsm)); - p += sprintf(p, " TX channel FSM state: %s\n", - fsm_getstate_str(priv->channel[WRITE]->fsm)); - p += sprintf(p, " Max. TX buffer used: %ld\n", - priv->channel[WRITE]->prof.maxmulti); - p += sprintf(p, " Max. chained SKBs: %ld\n", - priv->channel[WRITE]->prof.maxcqueue); - p += sprintf(p, " TX single write ops: %ld\n", - priv->channel[WRITE]->prof.doios_single); - p += sprintf(p, " TX multi write ops: %ld\n", - priv->channel[WRITE]->prof.doios_multi); - p += sprintf(p, " Netto bytes written: %ld\n", - priv->channel[WRITE]->prof.txlen); - p += sprintf(p, " Max. TX IO-time: %ld\n", - priv->channel[WRITE]->prof.tx_time); - - ctc_pr_debug("Statistics for %s:\n%s", - priv->channel[WRITE]->netdev->name, sbuf); - kfree(sbuf); - return; -} - -static ssize_t -stats_show(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct ctc_priv *priv = dev->driver_data; - if (!priv) - return -ENODEV; - ctc_print_statistics(priv); - return sprintf(buf, "0\n"); -} - -static ssize_t -stats_write(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) -{ - struct ctc_priv *priv = dev->driver_data; - if (!priv) - return -ENODEV; - /* Reset statistics */ - memset(&priv->channel[WRITE]->prof, 0, - sizeof(priv->channel[WRITE]->prof)); - return count; -} - -static void -ctc_netdev_unregister(struct net_device * dev) -{ - struct ctc_priv *privptr; - - if (!dev) - return; - privptr = (struct ctc_priv *) dev->priv; - unregister_netdev(dev); -} - -static int -ctc_netdev_register(struct net_device * dev) -{ - return register_netdev(dev); -} - -static void -ctc_free_netdevice(struct net_device * dev, int free_dev) -{ - struct ctc_priv *privptr; - if (!dev) - return; - privptr = dev->priv; - if (privptr) { - if (privptr->fsm) - kfree_fsm(privptr->fsm); - kfree(privptr); - } -#ifdef MODULE - if (free_dev) - free_netdev(dev); -#endif -} - -static ssize_t -ctc_proto_show(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct ctc_priv *priv; - - priv = dev->driver_data; - if (!priv) - return -ENODEV; - - return sprintf(buf, "%d\n", priv->protocol); -} - -static ssize_t -ctc_proto_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) -{ - struct ctc_priv *priv; - int value; - - DBF_TEXT(trace, 3, __FUNCTION__); - pr_debug("%s() called\n", __FUNCTION__); - - priv = dev->driver_data; - if (!priv) - return -ENODEV; - sscanf(buf, "%u", &value); - if (!((value == CTC_PROTO_S390) || - (value == CTC_PROTO_LINUX) || - (value == CTC_PROTO_OS390))) - return -EINVAL; - priv->protocol = value; - - return count; -} - -static ssize_t -ctc_type_show(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct ccwgroup_device *cgdev; - - cgdev = to_ccwgroupdev(dev); - if (!cgdev) - return -ENODEV; - - return sprintf(buf, "%s\n", cu3088_type[cgdev->cdev[0]->id.driver_info]); -} - -static DEVICE_ATTR(buffer, 0644, buffer_show, buffer_write); -static DEVICE_ATTR(protocol, 0644, ctc_proto_show, ctc_proto_store); -static DEVICE_ATTR(type, 0444, ctc_type_show, NULL); - -static DEVICE_ATTR(loglevel, 0644, loglevel_show, loglevel_write); -static DEVICE_ATTR(stats, 0644, stats_show, stats_write); - -static struct attribute *ctc_attr[] = { - &dev_attr_protocol.attr, - &dev_attr_type.attr, - &dev_attr_buffer.attr, - NULL, -}; - -static struct attribute_group ctc_attr_group = { - .attrs = ctc_attr, -}; - -static int -ctc_add_attributes(struct device *dev) -{ - int rc; - - rc = device_create_file(dev, &dev_attr_loglevel); - if (rc) - goto out; - rc = device_create_file(dev, &dev_attr_stats); - if (!rc) - goto out; - device_remove_file(dev, &dev_attr_loglevel); -out: - return rc; -} - -static void -ctc_remove_attributes(struct device *dev) -{ - device_remove_file(dev, &dev_attr_stats); - device_remove_file(dev, &dev_attr_loglevel); -} - -static int -ctc_add_files(struct device *dev) -{ - pr_debug("%s() called\n", __FUNCTION__); - - return sysfs_create_group(&dev->kobj, &ctc_attr_group); -} - -static void -ctc_remove_files(struct device *dev) -{ - pr_debug("%s() called\n", __FUNCTION__); - - sysfs_remove_group(&dev->kobj, &ctc_attr_group); -} - -/** - * Add ctc specific attributes. - * Add ctc private data. - * - * @param cgdev pointer to ccwgroup_device just added - * - * @returns 0 on success, !0 on failure. - */ -static int -ctc_probe_device(struct ccwgroup_device *cgdev) -{ - struct ctc_priv *priv; - int rc; - char buffer[16]; - - pr_debug("%s() called\n", __FUNCTION__); - DBF_TEXT(setup, 3, __FUNCTION__); - - if (!get_device(&cgdev->dev)) - return -ENODEV; - - priv = kzalloc(sizeof(struct ctc_priv), GFP_KERNEL); - if (!priv) { - ctc_pr_err("%s: Out of memory\n", __func__); - put_device(&cgdev->dev); - return -ENOMEM; - } - - rc = ctc_add_files(&cgdev->dev); - if (rc) { - kfree(priv); - put_device(&cgdev->dev); - return rc; - } - priv->buffer_size = CTC_BUFSIZE_DEFAULT; - cgdev->cdev[0]->handler = ctc_irq_handler; - cgdev->cdev[1]->handler = ctc_irq_handler; - cgdev->dev.driver_data = priv; - - sprintf(buffer, "%p", priv); - DBF_TEXT(data, 3, buffer); - - sprintf(buffer, "%u", (unsigned int)sizeof(struct ctc_priv)); - DBF_TEXT(data, 3, buffer); - - sprintf(buffer, "%p", &channels); - DBF_TEXT(data, 3, buffer); - - sprintf(buffer, "%u", (unsigned int)sizeof(struct channel)); - DBF_TEXT(data, 3, buffer); - - return 0; -} - -/** - * Device setup function called by alloc_netdev(). - * - * @param dev Device to be setup. - */ -void ctc_init_netdevice(struct net_device * dev) -{ - DBF_TEXT(setup, 3, __FUNCTION__); - - if (dev->mtu == 0) - dev->mtu = CTC_BUFSIZE_DEFAULT - LL_HEADER_LENGTH - 2; - dev->hard_start_xmit = ctc_tx; - dev->open = ctc_open; - dev->stop = ctc_close; - dev->get_stats = ctc_stats; - dev->change_mtu = ctc_change_mtu; - dev->hard_header_len = LL_HEADER_LENGTH + 2; - dev->addr_len = 0; - dev->type = ARPHRD_SLIP; - dev->tx_queue_len = 100; - dev->flags = IFF_POINTOPOINT | IFF_NOARP; -} - - -/** - * - * Setup an interface. - * - * @param cgdev Device to be setup. - * - * @returns 0 on success, !0 on failure. - */ -static int -ctc_new_device(struct ccwgroup_device *cgdev) -{ - char read_id[CTC_ID_SIZE]; - char write_id[CTC_ID_SIZE]; - int direction; - enum channel_types type; - struct ctc_priv *privptr; - struct net_device *dev; - int ret; - char buffer[16]; - - pr_debug("%s() called\n", __FUNCTION__); - DBF_TEXT(setup, 3, __FUNCTION__); - - privptr = cgdev->dev.driver_data; - if (!privptr) - return -ENODEV; - - sprintf(buffer, "%d", privptr->buffer_size); - DBF_TEXT(setup, 3, buffer); - - type = get_channel_type(&cgdev->cdev[0]->id); - - snprintf(read_id, CTC_ID_SIZE, "ch-%s", cgdev->cdev[0]->dev.bus_id); - snprintf(write_id, CTC_ID_SIZE, "ch-%s", cgdev->cdev[1]->dev.bus_id); - - if (add_channel(cgdev->cdev[0], type)) - return -ENOMEM; - if (add_channel(cgdev->cdev[1], type)) - return -ENOMEM; - - ret = ccw_device_set_online(cgdev->cdev[0]); - if (ret != 0) { - printk(KERN_WARNING - "ccw_device_set_online (cdev[0]) failed with ret = %d\n", ret); - } - - ret = ccw_device_set_online(cgdev->cdev[1]); - if (ret != 0) { - printk(KERN_WARNING - "ccw_device_set_online (cdev[1]) failed with ret = %d\n", ret); - } - - dev = alloc_netdev(0, "ctc%d", ctc_init_netdevice); - if (!dev) { - ctc_pr_warn("ctc_init_netdevice failed\n"); - goto out; - } - dev->priv = privptr; - - privptr->fsm = init_fsm("ctcdev", dev_state_names, - dev_event_names, CTC_NR_DEV_STATES, CTC_NR_DEV_EVENTS, - dev_fsm, DEV_FSM_LEN, GFP_KERNEL); - if (privptr->fsm == NULL) { - free_netdev(dev); - goto out; - } - fsm_newstate(privptr->fsm, DEV_STATE_STOPPED); - fsm_settimer(privptr->fsm, &privptr->restart_timer); - - for (direction = READ; direction <= WRITE; direction++) { - privptr->channel[direction] = - channel_get(type, direction == READ ? read_id : write_id, - direction); - if (privptr->channel[direction] == NULL) { - if (direction == WRITE) - channel_free(privptr->channel[READ]); - - ctc_free_netdevice(dev, 1); - goto out; - } - privptr->channel[direction]->netdev = dev; - privptr->channel[direction]->protocol = privptr->protocol; - privptr->channel[direction]->max_bufsize = privptr->buffer_size; - } - /* sysfs magic */ - SET_NETDEV_DEV(dev, &cgdev->dev); - - if (ctc_netdev_register(dev) != 0) { - ctc_free_netdevice(dev, 1); - goto out; - } - - if (ctc_add_attributes(&cgdev->dev)) { - ctc_netdev_unregister(dev); - dev->priv = NULL; - ctc_free_netdevice(dev, 1); - goto out; - } - - strlcpy(privptr->fsm->name, dev->name, sizeof (privptr->fsm->name)); - - print_banner(); - - ctc_pr_info("%s: read: %s, write: %s, proto: %d\n", - dev->name, privptr->channel[READ]->id, - privptr->channel[WRITE]->id, privptr->protocol); - - return 0; -out: - ccw_device_set_offline(cgdev->cdev[1]); - ccw_device_set_offline(cgdev->cdev[0]); - - return -ENODEV; -} - -/** - * Shutdown an interface. - * - * @param cgdev Device to be shut down. - * - * @returns 0 on success, !0 on failure. - */ -static int -ctc_shutdown_device(struct ccwgroup_device *cgdev) -{ - struct ctc_priv *priv; - struct net_device *ndev; - - DBF_TEXT(setup, 3, __FUNCTION__); - pr_debug("%s() called\n", __FUNCTION__); - - - priv = cgdev->dev.driver_data; - ndev = NULL; - if (!priv) - return -ENODEV; - - if (priv->channel[READ]) { - ndev = priv->channel[READ]->netdev; - - /* Close the device */ - ctc_close(ndev); - ndev->flags &=~IFF_RUNNING; - - ctc_remove_attributes(&cgdev->dev); - - channel_free(priv->channel[READ]); - } - if (priv->channel[WRITE]) - channel_free(priv->channel[WRITE]); - - if (ndev) { - ctc_netdev_unregister(ndev); - ndev->priv = NULL; - ctc_free_netdevice(ndev, 1); - } - - if (priv->fsm) - kfree_fsm(priv->fsm); - - ccw_device_set_offline(cgdev->cdev[1]); - ccw_device_set_offline(cgdev->cdev[0]); - - if (priv->channel[READ]) - channel_remove(priv->channel[READ]); - if (priv->channel[WRITE]) - channel_remove(priv->channel[WRITE]); - priv->channel[READ] = priv->channel[WRITE] = NULL; - - return 0; - -} - -static void -ctc_remove_device(struct ccwgroup_device *cgdev) -{ - struct ctc_priv *priv; - - pr_debug("%s() called\n", __FUNCTION__); - DBF_TEXT(setup, 3, __FUNCTION__); - - priv = cgdev->dev.driver_data; - if (!priv) - return; - if (cgdev->state == CCWGROUP_ONLINE) - ctc_shutdown_device(cgdev); - ctc_remove_files(&cgdev->dev); - cgdev->dev.driver_data = NULL; - kfree(priv); - put_device(&cgdev->dev); -} - -static struct ccwgroup_driver ctc_group_driver = { - .owner = THIS_MODULE, - .name = "ctc", - .max_slaves = 2, - .driver_id = 0xC3E3C3, - .probe = ctc_probe_device, - .remove = ctc_remove_device, - .set_online = ctc_new_device, - .set_offline = ctc_shutdown_device, -}; - -/** - * Module related routines - *****************************************************************************/ - -/** - * Prepare to be unloaded. Free IRQ's and release all resources. - * This is called just before this module is unloaded. It is - * <em>not</em> called, if the usage count is !0, so we don't need to check - * for that. - */ -static void __exit -ctc_exit(void) -{ - DBF_TEXT(setup, 3, __FUNCTION__); - unregister_cu3088_discipline(&ctc_group_driver); - ctc_unregister_dbf_views(); - ctc_pr_info("CTC driver unloaded\n"); -} - -/** - * Initialize module. - * This is called just after the module is loaded. - * - * @return 0 on success, !0 on error. - */ -static int __init -ctc_init(void) -{ - int ret = 0; - - loglevel = CTC_LOGLEVEL_DEFAULT; - - DBF_TEXT(setup, 3, __FUNCTION__); - - print_banner(); - - ret = ctc_register_dbf_views(); - if (ret){ - ctc_pr_crit("ctc_init failed with ctc_register_dbf_views rc = %d\n", ret); - return ret; - } - ret = register_cu3088_discipline(&ctc_group_driver); - if (ret) { - ctc_unregister_dbf_views(); - } - return ret; -} - -module_init(ctc_init); -module_exit(ctc_exit); - -/* --- This is the END my friend --- */ |