ctcmain.c
上传用户:lgb322
上传日期:2013-02-24
资源大小:30529k
文件大小:106k
- /*
- * $Id: ctcmain.c,v 1.55 2001/12/03 14:28:45 felfert Exp $
- *
- * 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鰄rig (roehrig@de.ibm.com)
- * Arnaldo Carvalho de Melo <acme@conectiva.com.br>
- *
- * 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鰄rig (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.
- *
- * RELEASE-TAG: CTC/ESCON network driver $Revision: 1.55 $
- *
- */
- #include <linux/version.h>
- #include <linux/module.h>
- #include <linux/init.h>
- #include <linux/kernel.h>
- #include <linux/slab.h>
- #include <linux/errno.h>
- #include <linux/types.h>
- #include <linux/interrupt.h>
- #include <linux/timer.h>
- #include <linux/sched.h>
- #include <linux/signal.h>
- #include <linux/string.h>
- #include <linux/proc_fs.h>
- #include <linux/ip.h>
- #include <linux/if_arp.h>
- #include <linux/tcp.h>
- #include <linux/skbuff.h>
- #include <linux/ctype.h>
- #include <net/dst.h>
- #include <asm/io.h>
- #include <asm/bitops.h>
- #include <asm/uaccess.h>
- #ifdef CONFIG_CHANDEV
- #define CTC_CHANDEV
- #endif
- #ifdef CTC_CHANDEV
- #include <asm/chandev.h>
- #define REQUEST_IRQ chandev_request_irq
- #define FREE_IRQ chandev_free_irq
- #else
- #define REQUEST_IRQ request_irq
- #define FREE_IRQ free_irq
- #endif
- #if LINUX_VERSION_CODE >= 0x020213
- # include <asm/idals.h>
- #else
- # define set_normalized_cda(ccw, addr) ((ccw)->cda = (addr),0)
- # define clear_normalized_cda(ccw)
- #endif
- #if LINUX_VERSION_CODE < 0x020400
- # define s390_dev_info_t dev_info_t
- # define dev_kfree_skb_irq(a) dev_kfree_skb(a)
- #endif
- #include <asm/irq.h>
- #include "ctctty.h"
- #include "fsm.h"
- #ifdef MODULE
- MODULE_AUTHOR("(C) 2000 IBM Corp. by Fritz Elfert (felfert@millenux.com)");
- MODULE_DESCRIPTION("Linux for S/390 CTC/Escon Driver");
- #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,12))
- MODULE_LICENSE("GPL");
- #endif
- #ifndef CTC_CHANDEV
- MODULE_PARM(ctc, "s");
- MODULE_PARM_DESC(ctc,
- "One or more definitions in the same format like the kernel param for ctc.n"
- "E.g.: ctc0:0x700:0x701:0:ctc1:0x702:0x703:0n");
- char *ctc = NULL;
- #endif
- #else
- /**
- * Number of devices in monolithic (not module) driver version.
- */
- #define MAX_STATIC_DEVICES 16
- #endif /* MODULE */
- #undef DEBUG
- /**
- * CCW commands, used in this driver.
- */
- #define CCW_CMD_WRITE 0x01
- #define CCW_CMD_READ 0x02
- #define CCW_CMD_SET_EXTENDED 0xc3
- #define CCW_CMD_PREPARE 0xe3
- #define CTC_PROTO_S390 0
- #define CTC_PROTO_LINUX 1
- #define CTC_PROTO_LINUX_TTY 2
- #define CTC_PROTO_OS390 3
- #define CTC_PROTO_MAX 3
- #define CTC_BUFSIZE_LIMIT 65535
- #define CTC_BUFSIZE_DEFAULT 32768
- #define CTC_TIMEOUT_5SEC 5000
- #define CTC_INITIAL_BLOCKLEN 2
- #define READ 0
- #define WRITE 1
- /**
- * Enum for classifying detected devices.
- */
- enum channel_types {
- /**
- * Device is not a channel.
- */
- channel_type_none,
- /**
- * Device is a channel, but we don't know
- * anything about it.
- */
- channel_type_unknown,
- /**
- * Device is a CTC/A.
- */
- channel_type_ctca,
- /**
- * Device is a ESCON channel.
- */
- channel_type_escon,
- /**
- * Device is an unsupported model.
- */
- channel_type_unsupported
- };
- typedef enum channel_types channel_type_t;
- #ifndef CTC_CHANDEV
- static int ctc_no_auto = 0;
- #endif
- /**
- * If running on 64 bit, this must be changed. XXX Why? (bird)
- */
- typedef unsigned long intparm_t;
- #ifndef CTC_CHANDEV
- /**
- * Definition of a per device parameter block
- */
- #define MAX_PARAM_NAME_LEN 11
- typedef struct param_t {
- struct param_t *next;
- int read_dev;
- int write_dev;
- __u16 proto;
- char name[MAX_PARAM_NAME_LEN];
- } param;
- static param *params = NULL;
- #endif
- typedef struct {
- unsigned long maxmulti;
- unsigned long maxcqueue;
- unsigned long doios_single;
- unsigned long doios_multi;
- unsigned long txlen;
- unsigned long tx_time;
- struct timeval send_stamp;
- } ctc_profile;
- /**
- * Definition of one channel
- */
- typedef struct channel_t {
- /**
- * Pointer to next channel in list.
- */
- struct channel_t *next;
- __u16 devno;
- int irq;
- /**
- * Type of this channel.
- * CTC/A or Escon for valid channels.
- */
- channel_type_t type;
- /**
- * Misc. flags. See CHANNEL_FLAGS_... below
- */
- __u32 flags;
- /**
- * The protocol of this channel
- */
- __u16 protocol;
- /**
- * I/O and irq related stuff
- */
- ccw1_t *ccw;
- devstat_t *devstat;
- /**
- * Bottom half task queue.
- */
- struct tq_struct tq;
- /**
- * RX/TX buffer size
- */
- int max_bufsize;
- /**
- * Transmit/Receive buffer.
- */
- struct sk_buff *trans_skb;
- /**
- * Universal I/O queue.
- */
- struct sk_buff_head io_queue;
- /**
- * TX queue for collecting skb's during busy.
- */
- struct sk_buff_head collect_queue;
- /**
- * Amount of data in collect_queue.
- */
- int collect_len;
- /**
- * spinlock for collect_queue and collect_len
- */
- spinlock_t collect_lock;
- /**
- * Timer for detecting unresposive
- * I/O operations.
- */
- fsm_timer timer;
- /**
- * Retry counter for misc. operations.
- */
- int retry;
- /**
- * The finite state machine of this channel
- */
- fsm_instance *fsm;
- /**
- * The corresponding net_device this channel
- * belongs to.
- */
- net_device *netdev;
- ctc_profile prof;
- unsigned char *trans_skb_data;
- } channel;
- #define CHANNEL_FLAGS_READ 0
- #define CHANNEL_FLAGS_WRITE 1
- #define CHANNEL_FLAGS_INUSE 2
- #define CHANNEL_FLAGS_BUFSIZE_CHANGED 4
- #define CHANNEL_FLAGS_RWMASK 1
- #define CHANNEL_DIRECTION(f) (f & CHANNEL_FLAGS_RWMASK)
- /**
- * Linked list of all detected channels.
- */
- static channel *channels = NULL;
- #ifdef CTC_CHANDEV
- static int activated;
- #endif
- typedef struct ctc_priv_t {
- struct net_device_stats stats;
- #if LINUX_VERSION_CODE >= 0x02032D
- unsigned long tbusy;
- #endif
- /**
- * The finite state machine of this interface.
- */
- fsm_instance *fsm;
- /**
- * The protocol of this device
- */
- __u16 protocol;
- channel *channel[2];
- struct proc_dir_entry *proc_dentry;
- struct proc_dir_entry *proc_stat_entry;
- struct proc_dir_entry *proc_ctrl_entry;
- int proc_registered;
- } ctc_priv;
- /**
- * Definition of our link level header.
- */
- typedef struct ll_header_t {
- __u16 length;
- __u16 type;
- __u16 unused;
- } ll_header;
- #define LL_HEADER_LENGTH (sizeof(ll_header))
- /**
- * Compatibility macros for busy handling
- * of network devices.
- */
- #if LINUX_VERSION_CODE < 0x02032D
- static __inline__ void ctc_clear_busy(net_device *dev)
- {
- clear_bit(0 ,(void *)&dev->tbusy);
- mark_bh(NET_BH);
- }
- static __inline__ int ctc_test_and_set_busy(net_device *dev)
- {
- return(test_and_set_bit(0, (void *)&dev->tbusy));
- }
- #define SET_DEVICE_START(device, value) dev->start = value
- #else
- static __inline__ void ctc_clear_busy(net_device *dev)
- {
- clear_bit(0, &(((ctc_priv *)dev->priv)->tbusy));
- netif_wake_queue(dev);
- }
- static __inline__ int ctc_test_and_set_busy(net_device *dev)
- {
- netif_stop_queue(dev);
- return test_and_set_bit(0, &((ctc_priv *)dev->priv)->tbusy);
- }
- #define SET_DEVICE_START(device, value)
- #endif
- /**
- * Print Banner.
- */
- static void print_banner(void) {
- static int printed = 0;
- char vbuf[] = "$Revision: 1.55 $";
- char *version = vbuf;
- if (printed)
- return;
- if ((version = strchr(version, ':'))) {
- char *p = strchr(version + 1, '$');
- if (p)
- *p = ' ';
- } else
- version = " ??? ";
- printk(KERN_INFO
- "CTC driver Version%swith"
- #ifndef CTC_CHANDEV
- "out"
- #endif
- " CHANDEV support"
- #ifdef DEBUG
- " (DEBUG-VERSION, " __DATE__ __TIME__ ")"
- #endif
- " initializedn", version);
- printed = 1;
- }
- #ifndef CTC_CHANDEV
- /**
- * Return type of a detected device.
- */
- static channel_type_t channel_type (senseid_t *id) {
- channel_type_t type = channel_type_none;
- switch (id->cu_type) {
- case 0x3088:
- switch (id->cu_model) {
- case 0x08:
- /**
- * 3088-08 = CTCA
- */
- type = channel_type_ctca;
- break;
- case 0x1F:
- /**
- * 3088-1F = ESCON channel
- */
- type = channel_type_escon;
- break;
- /**
- * 3088-01 = P390 OSA emulation
- */
- case 0x01:
- /* fall thru */
- /**
- * 3088-60 = OSA/2 adapter
- */
- case 0x60:
- /* fall thru */
- /**
- * 3088-61 = CISCO 7206 CLAW proto
- * on ESCON
- */
- case 0x61:
- /* fall thru */
- /**
- * 3088-62 = OSA/D device
- */
- case 0x62:
- type = channel_type_unsupported;
- break;
- default:
- type = channel_type_unknown;
- printk(KERN_INFO
- "channel: Unknown model found "
- "3088-%02xn", id->cu_model);
- }
- break;
- default:
- type = channel_type_none;
- }
- return type;
- }
- #endif
- /**
- * 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!!
- */
- 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,
- /**
- * MUST be always the last element!!
- */
- NR_DEV_EVENTS
- };
- static const char *dev_event_names[] = {
- "Start",
- "Stop",
- "RX up",
- "TX up",
- "RX down",
- "TX down",
- };
- /**
- * Events of the channel statemachine
- */
- enum ch_events {
- /**
- * Events, representing return code of
- * I/O operations (do_IO, halt_IO 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,
- };
- static const char *ch_event_names[] = {
- "do_IO success",
- "do_IO busy",
- "do_IO enodev",
- "do_IO ioerr",
- "do_IO 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",
- };
- /**
- * 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 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;
- ll_header *header;
- int i;
- p += offset;
- bl = *((__u16*)p);
- p += 2;
- header = (ll_header *)p;
- p -= 2;
-
- printk(KERN_DEBUG "dump:n");
- printk(KERN_DEBUG "blocklen=%d %04xn", bl, bl);
- printk(KERN_DEBUG "h->length=%d %04xn", header->length,
- header->length);
- printk(KERN_DEBUG "h->type=%04xn", header->type);
- printk(KERN_DEBUG "h->unused=%04xn", 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");
- }
- #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 __inline__ void ctc_unpack_skb(channel *ch, struct sk_buff *pskb)
- {
- net_device *dev = ch->netdev;
- ctc_priv *privptr = (ctc_priv *)dev->priv;
- __u16 len = *((__u16*)pskb->data);
- 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;
- ll_header *header = (ll_header *)pskb->data;
- skb_pull(pskb, LL_HEADER_LENGTH);
- if ((ch->protocol == CTC_PROTO_S390) &&
- (header->type != ETH_P_IP)) {
- /**
- * Check packet type only if we stick strictly
- * to S/390's protocol of OS390. This only
- * supports IP. Otherwise allow any packet
- * type.
- */
- printk(KERN_WARNING
- "%s Illegal packet type 0x%04x "
- "received, droppingn",
- dev->name, header->type);
- #ifdef DEBUG
- ctc_dump_skb(pskb, -6);
- #endif
- privptr->stats.rx_dropped++;
- privptr->stats.rx_frame_errors++;
- return;
- }
- pskb->protocol = ntohs(header->type);
- header->length -= LL_HEADER_LENGTH;
- if ((header->length == 0) ||
- (header->length > skb_tailroom(pskb)) ||
- (header->length > len)) {
- printk(KERN_WARNING
- "%s Illegal packet size %d "
- "received (MTU=%d blocklen=%d), "
- "droppingn", dev->name, header->length,
- dev->mtu, len);
- #ifdef DEBUG
- ctc_dump_skb(pskb, -6);
- #endif
- privptr->stats.rx_dropped++;
- privptr->stats.rx_length_errors++;
- return;
- }
- if (header->length > skb_tailroom(pskb)) {
- printk(KERN_WARNING
- "%s Illegal packet size %d "
- "(beyond the end of received data), "
- "droppingn", dev->name, header->length);
- #ifdef DEBUG
- ctc_dump_skb(pskb, -6);
- #endif
- privptr->stats.rx_dropped++;
- privptr->stats.rx_length_errors++;
- return;
- }
- skb_put(pskb, header->length);
- pskb->mac.raw = pskb->data;
- len -= (LL_HEADER_LENGTH + header->length);
- skb = dev_alloc_skb(pskb->len);
- if (!skb) {
- printk(KERN_WARNING
- "%s Out of memory in ctc_unpack_skbn",
- dev->name);
- privptr->stats.rx_dropped++;
- return;
- }
- memcpy(skb_put(skb, pskb->len), pskb->data, pskb->len);
- skb->mac.raw = skb->data;
- skb->dev = pskb->dev;
- skb->protocol = pskb->protocol;
- pskb->ip_summed = CHECKSUM_UNNECESSARY;
- if (ch->protocol == CTC_PROTO_LINUX_TTY)
- ctc_tty_netif_rx(skb);
- else
- netif_rx(skb);
- privptr->stats.rx_packets++;
- privptr->stats.rx_bytes += skb->len;
- if (len > 0) {
- skb_pull(pskb, header->length);
- skb_put(pskb, LL_HEADER_LENGTH);
- }
- }
- }
- /**
- * Bottom half routine.
- *
- * @param ch The channel to work on.
- */
- static void ctc_bh(channel *ch)
- {
- struct sk_buff *skb;
- while ((skb = skb_dequeue(&ch->io_queue)))
- ctc_unpack_skb(ch, skb);
- }
- /**
- * Check return code of a preceeding do_IO, halt_IO etc...
- *
- * @param ch The channel, the error belongs to.
- * @param return_code The error code to inspect.
- */
- static void inline ccw_check_return_code (channel *ch, int return_code)
- {
- switch (return_code) {
- case 0:
- fsm_event(ch->fsm, CH_EVENT_IO_SUCCESS, ch);
- break;
- case -EBUSY:
- printk(KERN_INFO "ch-%04x: Busy !n", ch->devno);
- fsm_event(ch->fsm, CH_EVENT_IO_EBUSY, ch);
- break;
- case -ENODEV:
- printk(KERN_EMERG
- "ch-%04x: Invalid device called for IOn",
- ch->devno);
- fsm_event(ch->fsm, CH_EVENT_IO_ENODEV, ch);
- break;
- case -EIO:
- printk(KERN_EMERG
- "ch-%04x: Status pending... n", ch->devno);
- fsm_event(ch->fsm, CH_EVENT_IO_EIO, ch);
- break;
- default:
- printk(KERN_EMERG
- "ch-%04x: Unknown error in do_IO %04xn",
- ch->devno, 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 inline ccw_unit_check (channel *ch, unsigned char sense) {
- if (sense & SNS0_INTERVENTION_REQ) {
- if (sense & 0x01) {
- if (ch->protocol != CTC_PROTO_LINUX_TTY)
- printk(KERN_DEBUG
- "ch-%04x: Interface disc. or Sel. reset "
- "(remote)n", ch->devno);
- fsm_event(ch->fsm, CH_EVENT_UC_RCRESET, ch);
- } else {
- printk(KERN_DEBUG "ch-%04x: System reset (remote)n",
- ch->devno);
- fsm_event(ch->fsm, CH_EVENT_UC_RSRESET, ch);
- }
- } else if (sense & SNS0_EQUIPMENT_CHECK) {
- if (sense & SNS0_BUS_OUT_CHECK) {
- printk(KERN_WARNING
- "ch-%04x: Hardware malfunction (remote)n",
- ch->devno);
- fsm_event(ch->fsm, CH_EVENT_UC_HWFAIL, ch);
- } else {
- printk(KERN_WARNING
- "ch-%04x: Read-data parity error (remote)n",
- ch->devno);
- fsm_event(ch->fsm, CH_EVENT_UC_RXPARITY, ch);
- }
- } else if (sense & SNS0_BUS_OUT_CHECK) {
- if (sense & 0x04) {
- printk(KERN_WARNING
- "ch-%04x: Data-streaming timeout)n",
- ch->devno);
- fsm_event(ch->fsm, CH_EVENT_UC_TXTIMEOUT, ch);
- } else {
- printk(KERN_WARNING
- "ch-%04x: Data-transfer parity errorn",
- ch->devno);
- fsm_event(ch->fsm, CH_EVENT_UC_TXPARITY, ch);
- }
- } else if (sense & SNS0_CMD_REJECT) {
- printk(KERN_WARNING "ch-%04x: Command rejectn",
- ch->devno);
- } else if (sense == 0) {
- printk(KERN_DEBUG "ch-%04x: Unit check ZEROn", ch->devno);
- fsm_event(ch->fsm, CH_EVENT_UC_ZERO, ch);
- } else {
- printk(KERN_WARNING
- "ch-%04x: Unit Check with sense code: %02xn",
- ch->devno, 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;
- while ((skb = skb_dequeue(q))) {
- atomic_dec(&skb->users);
- dev_kfree_skb_irq(skb);
- }
- }
- static __inline__ int ctc_checkalloc_buffer(channel *ch, int warn) {
- 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)
- printk(KERN_WARNING
- "ch-%04x: Couldn't alloc %s trans_skbn",
- ch->devno,
- (CHANNEL_DIRECTION(ch->flags) == READ) ?
- "RX" : "TX");
- return -ENOMEM;
- }
- ch->ccw[1].count = ch->max_bufsize;
- if (set_normalized_cda(&ch->ccw[1],
- virt_to_phys(ch->trans_skb->data))) {
- dev_kfree_skb(ch->trans_skb);
- ch->trans_skb = NULL;
- if (warn)
- printk(KERN_WARNING
- "ch-%04x: set_normalized_cda for %s "
- "trans_skb failed, dropping packetsn",
- ch->devno,
- (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)
- {
- channel *ch = (channel *)arg;
- net_device *dev = ch->netdev;
- ctc_priv *privptr = dev->priv;
- struct sk_buff *skb;
- int first = 1;
- int i;
- struct timeval done_stamp = xtime;
- unsigned long duration =
- (done_stamp.tv_sec - ch->prof.send_stamp.tv_sec) * 1000000 +
- done_stamp.tv_usec - ch->prof.send_stamp.tv_usec;
- if (duration > ch->prof.tx_time)
- ch->prof.tx_time = duration;
- if (ch->devstat->rescnt != 0)
- printk(KERN_DEBUG "%s: TX not complete, remaining %d bytesn",
- dev->name, ch->devstat->rescnt);
-
- 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->tail = ch->trans_skb->data = ch->trans_skb_data;
- 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))) {
- memcpy(skb_put(ch->trans_skb, skb->len), skb->data,
- 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 = xtime;
- rc = do_IO(ch->irq, &ch->ccw[0], (intparm_t)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);
- }
- } 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)
- {
- channel *ch = (channel *)arg;
- fsm_deltimer(&ch->timer);
- fsm_newstate(fi, CH_STATE_TXIDLE);
- fsm_event(((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)
- {
- channel *ch = (channel *)arg;
- net_device *dev = ch->netdev;
- ctc_priv *privptr = dev->priv;
- int len = ch->max_bufsize - ch->devstat->rescnt;
- struct sk_buff *skb = ch->trans_skb;
- __u16 block_len = *((__u16*)skb->data);
- int check_len;
- int rc;
- fsm_deltimer(&ch->timer);
- if (len < 8) {
- printk(KERN_WARNING "%s: got packet with length %d < 8n",
- dev->name, len);
- privptr->stats.rx_dropped++;
- privptr->stats.rx_length_errors++;
- goto again;
- }
- if (len > ch->max_bufsize) {
- printk(KERN_WARNING "%s: got packet with length %d > %dn",
- 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)) {
- printk(KERN_WARNING "%s: got block length %d != rx length %dn",
- 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 = skb->tail = ch->trans_skb_data;
- skb->len = 0;
- if (ctc_checkalloc_buffer(ch, 1))
- return;
- ch->ccw[1].count = ch->max_bufsize;
- rc = do_IO(ch->irq, &ch->ccw[0], (intparm_t)ch, 0xff, 0);
- if (rc != 0)
- ccw_check_return_code(ch, rc);
- }
- 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)
- {
- channel *ch = (channel *)arg;
- int rc;
- if (fsm_getstate(fi) == CH_STATE_TXIDLE)
- printk(KERN_DEBUG "ch-%04x: remote side issued READ?, "
- "init ...n", ch->devno);
- 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 {
- net_device *dev = ch->netdev;
- fsm_newstate(fi, CH_STATE_TXIDLE);
- fsm_event(((ctc_priv *)dev->priv)->fsm,
- DEV_EVENT_TXUP, dev);
- }
- return;
- }
- /**
- * Don磘 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 = do_IO(ch->irq, &ch->ccw[0], (intparm_t)ch, 0xff, 0);
- if (rc != 0) {
- fsm_deltimer(&ch->timer);
- fsm_newstate(fi, CH_STATE_SETUPWAIT);
- ccw_check_return_code(ch, rc);
- }
- /**
- * If in compatibility mode since we don磘 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磗
- * final state.
- */
- if ((CHANNEL_DIRECTION(ch->flags) == READ) &&
- (ch->protocol == CTC_PROTO_S390)) {
- net_device *dev = ch->netdev;
- fsm_event(((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)
- {
- channel *ch = (channel *)arg;
- net_device *dev = ch->netdev;
- __u16 buflen;
- int rc;
- fsm_deltimer(&ch->timer);
- buflen = *((__u16 *)ch->trans_skb->data);
- #ifdef DEBUG
- printk(KERN_DEBUG "%s: Initial RX count %dn", 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 = do_IO(ch->irq, &ch->ccw[0], (intparm_t)ch, 0xff, 0);
- if (rc != 0) {
- fsm_newstate(fi, CH_STATE_RXINIT);
- ccw_check_return_code(ch, rc);
- } else
- fsm_event(((ctc_priv *)dev->priv)->fsm,
- DEV_EVENT_RXUP, dev);
- } else {
- printk(KERN_DEBUG "%s: Initial RX count %d not %dn",
- 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)
- {
- channel *ch = (channel *)arg;
- int rc;
- unsigned long saveflags;
- fsm_deltimer(&ch->timer);
- fsm_addtimer(&ch->timer, CTC_TIMEOUT_5SEC, CH_EVENT_TIMER, ch);
- fsm_newstate(fi, CH_STATE_SETUPWAIT);
- if (event == CH_EVENT_TIMER)
- s390irq_spin_lock_irqsave(ch->irq, saveflags);
- rc = do_IO(ch->irq, &ch->ccw[6], (intparm_t)ch, 0xff, 0);
- if (event == CH_EVENT_TIMER)
- s390irq_spin_unlock_irqrestore(ch->irq, saveflags);
- if (rc != 0) {
- fsm_deltimer(&ch->timer);
- fsm_newstate(fi, CH_STATE_STARTWAIT);
- ccw_check_return_code(ch, rc);
- } 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)
- {
- channel *ch = (channel *)arg;
- unsigned long saveflags;
- int rc;
- net_device *dev;
- if (ch == NULL) {
- printk(KERN_WARNING "ch_action_start ch=NULLn");
- return;
- }
- if (ch->netdev == NULL) {
- printk(KERN_WARNING "ch_action_start dev=NULL, irq=%dn",
- ch->irq);
- return;
- }
- dev = ch->netdev;
- #ifdef DEBUG
- printk(KERN_DEBUG "%s: %s channel startn", 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))
- printk(KERN_NOTICE
- "%s: Could not allocate %s trans_skb, delaying "
- "allocation until first transfern",
- dev->name,
- (CHANNEL_DIRECTION(ch->flags) == READ) ? "RX" : "TX");
- #if LINUX_VERSION_CODE >= 0x020400
- INIT_LIST_HEAD(&ch->tq.list);
- #else
- ch->tq.next = NULL;
- #endif
- ch->tq.sync = 0;
- ch->tq.routine = (void *)(void *)ctc_bh;
- ch->tq.data = ch;
- 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(ccw1_t) * 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);
- s390irq_spin_lock_irqsave(ch->irq, saveflags);
- rc = halt_IO(ch->irq, (intparm_t)ch, 0);
- s390irq_spin_unlock_irqrestore(ch->irq, saveflags);
- if (rc != 0) {
- fsm_deltimer(&ch->timer);
- ccw_check_return_code(ch, rc);
- }
- #ifdef DEBUG
- printk(KERN_DEBUG "ctc: %s(): leavingn", __FUNCTION__);
- #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)
- {
- channel *ch = (channel *)arg;
- unsigned long saveflags;
- int rc;
- int oldstate;
- fsm_deltimer(&ch->timer);
- fsm_addtimer(&ch->timer, CTC_TIMEOUT_5SEC, CH_EVENT_TIMER, ch);
- if (event == CH_EVENT_STOP)
- s390irq_spin_lock_irqsave(ch->irq, saveflags);
- oldstate = fsm_getstate(fi);
- fsm_newstate(fi, CH_STATE_TERM);
- rc = halt_IO (ch->irq, (intparm_t)ch, 0);
- if (event == CH_EVENT_STOP)
- s390irq_spin_unlock_irqrestore(ch->irq, saveflags);
- if (rc != 0) {
- fsm_deltimer(&ch->timer);
- fsm_newstate(fi, oldstate);
- ccw_check_return_code(ch, rc);
- }
- }
- /**
- * 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)
- {
- channel *ch = (channel *)arg;
- net_device *dev = ch->netdev;
- 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(((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(((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)
- {
- channel *ch = (channel *)arg;
- net_device *dev = ch->netdev;
- fsm_deltimer(&ch->timer);
- fsm_newstate(fi, CH_STATE_NOTOP);
- if (CHANNEL_DIRECTION(ch->flags) == READ) {
- skb_queue_purge(&ch->io_queue);
- fsm_event(((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(((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)
- {
- channel *ch = (channel *)arg;
- net_device *dev = ch->netdev;
- /**
- * 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 = halt_IO (ch->irq, (intparm_t)ch, 0);
- if (rc != 0)
- ccw_check_return_code(ch, rc);
- }
- return;
- }
- printk(KERN_DEBUG "%s: Error %s during %s channel setup state=%sn",
- 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(((ctc_priv *)dev->priv)->fsm, DEV_EVENT_RXDOWN, dev);
- } else {
- fsm_newstate(fi, CH_STATE_TXERR);
- fsm_event(((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;
- channel *ch = (channel *)arg;
- net_device *dev = ch->netdev;
- fsm_deltimer(&ch->timer);
- printk(KERN_DEBUG "%s: %s channel restartn", 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);
- if (event == CH_EVENT_TIMER)
- s390irq_spin_lock_irqsave(ch->irq, saveflags);
- rc = halt_IO (ch->irq, (intparm_t)ch, 0);
- if (event == CH_EVENT_TIMER)
- s390irq_spin_unlock_irqrestore(ch->irq, saveflags);
- if (rc != 0) {
- fsm_deltimer(&ch->timer);
- fsm_newstate(fi, oldstate);
- ccw_check_return_code(ch, rc);
- }
- }
- /**
- * 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)
- {
- channel *ch = (channel *)arg;
- net_device *dev = ch->netdev;
- if (event == CH_EVENT_TIMER) {
- fsm_deltimer(&ch->timer);
- printk(KERN_DEBUG "%s: Timeout during RX init handshaken",
- dev->name);
- if (ch->retry++ < 3)
- ch_action_restart(fi, event, arg);
- else {
- fsm_newstate(fi, CH_STATE_RXERR);
- fsm_event(((ctc_priv *)dev->priv)->fsm,
- DEV_EVENT_RXDOWN, dev);
- }
- } else
- printk(KERN_WARNING "%s: Error during RX init handshaken",
- 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)
- {
- channel *ch = (channel *)arg;
- net_device *dev = ch->netdev;
- fsm_newstate(fi, CH_STATE_RXERR);
- printk(KERN_WARNING "%s: RX initialization failedn", dev->name);
- printk(KERN_WARNING "%s: RX <-> RX connection detectedn", dev->name);
- fsm_event(((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)
- {
- channel *ch = (channel *)arg;
- channel *ch2;
- net_device *dev = ch->netdev;
- fsm_deltimer(&ch->timer);
- printk(KERN_DEBUG "%s: Got remote disconnect, re-initializing ...n",
- dev->name);
- /**
- * Notify device statemachine
- */
- fsm_event(((ctc_priv *)dev->priv)->fsm, DEV_EVENT_RXDOWN, dev);
- fsm_event(((ctc_priv *)dev->priv)->fsm, DEV_EVENT_TXDOWN, dev);
- fsm_newstate(fi, CH_STATE_DTERM);
- ch2 = ((ctc_priv *)dev->priv)->channel[WRITE];
- fsm_newstate(ch2->fsm, CH_STATE_DTERM);
- halt_IO(ch->irq, (intparm_t)ch, 0);
- halt_IO(ch2->irq, (intparm_t)ch2, 0);
- }
- /**
- * 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)
- {
- channel *ch = (channel *)arg;
- net_device *dev = ch->netdev;
- if (event == CH_EVENT_TIMER) {
- fsm_deltimer(&ch->timer);
- printk(KERN_DEBUG "%s: Timeout during TX init handshaken",
- dev->name);
- if (ch->retry++ < 3)
- ch_action_restart(fi, event, arg);
- else {
- fsm_newstate(fi, CH_STATE_TXERR);
- fsm_event(((ctc_priv *)dev->priv)->fsm,
- DEV_EVENT_TXDOWN, dev);
- }
- } else
- printk(KERN_WARNING "%s: Error during TX init handshaken",
- 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)
- {
- channel *ch = (channel *)arg;
- net_device *dev = ch->netdev;
- unsigned long saveflags;
- fsm_deltimer(&ch->timer);
- if (ch->retry++ > 3) {
- printk(KERN_DEBUG "%s: TX retry failed, restarting channeln",
- dev->name);
- fsm_event(((ctc_priv *)dev->priv)->fsm, DEV_EVENT_TXDOWN, dev);
- ch_action_restart(fi, event, arg);
- } else {
- struct sk_buff *skb;
- printk(KERN_DEBUG "%s: TX retry %dn", 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],
- virt_to_phys(skb->data))) {
- printk(KERN_DEBUG "%s: IDAL alloc failed, "
- "restarting channeln", dev->name);
- fsm_event(((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);
- if (event == CH_EVENT_TIMER)
- s390irq_spin_lock_irqsave(ch->irq, saveflags);
- rc = do_IO(ch->irq, &ch->ccw[3], (intparm_t)ch, 0xff, 0);
- if (event == CH_EVENT_TIMER)
- s390irq_spin_unlock_irqrestore(ch->irq,
- saveflags);
- if (rc != 0) {
- fsm_deltimer(&ch->timer);
- ccw_check_return_code(ch, rc);
- 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)
- {
- channel *ch = (channel *)arg;
- net_device *dev = ch->netdev;
- fsm_deltimer(&ch->timer);
- if (CHANNEL_DIRECTION(ch->flags) == READ) {
- printk(KERN_DEBUG "%s: RX I/O errorn", dev->name);
- fsm_newstate(fi, CH_STATE_RXERR);
- fsm_event(((ctc_priv *)dev->priv)->fsm, DEV_EVENT_RXDOWN, dev);
- } else {
- printk(KERN_DEBUG "%s: TX I/O errorn", dev->name);
- fsm_newstate(fi, CH_STATE_TXERR);
- fsm_event(((ctc_priv *)dev->priv)->fsm, DEV_EVENT_TXDOWN, 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_iofatal },
- { 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_iofatal },
- { 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_iofatal },
- { 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_iofatal },
- { 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_iofatal },
- { 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_iofatal },
- { 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_iofatal },
- { 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.
- *****************************************************************************/
- /**
- * Add a new channel to the list of channels.
- * Keeps the channel list sorted.
- *
- * @param irq The IRQ to be used by the new channel.
- * @param devno The device number of the new channel.
- * @param type The type class of the new channel.
- *
- * @return 0 on success, !0 on error.
- */
- static int add_channel(int irq, __u16 devno, channel_type_t type)
- {
- channel **c = &channels;
- channel *ch;
- char name[10];
- if ((ch = (channel *)kmalloc(sizeof(channel), GFP_KERNEL)) == NULL) {
- printk(KERN_WARNING "ctc: Out of memory in add_channeln");
- return -1;
- }
- memset(ch, 0, sizeof(channel));
- if ((ch->ccw = (ccw1_t *)kmalloc(sizeof(ccw1_t) * 8,
- GFP_KERNEL|GFP_DMA)) == NULL) {
- kfree(ch);
- printk(KERN_WARNING "ctc: Out of memory in add_channeln");
- 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):
- * 3: set extended mode
- * 4: 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[6].count = 0;
- ch->ccw[6].cda = 0;
-
- ch->ccw[7].cmd_code = CCW_CMD_NOOP;
- ch->ccw[7].flags = CCW_FLAG_SLI;
- ch->ccw[7].count = 0;
- ch->ccw[7].cda = 0;
- ch->irq = irq;
- ch->devno = devno;
- ch->type = type;
- sprintf(name, "ch-%04x", devno);
- ch->fsm = init_fsm(name, ch_state_names,
- ch_event_names, NR_CH_STATES, NR_CH_EVENTS,
- ch_fsm, CH_FSM_LEN, GFP_KERNEL);
- if (ch->fsm == NULL) {
- printk(KERN_WARNING
- "ctc: Could not create FSM in add_channeln");
- kfree(ch);
- return -1;
- }
- fsm_newstate(ch->fsm, CH_STATE_IDLE);
- if ((ch->devstat = (devstat_t*)kmalloc(sizeof(devstat_t), GFP_KERNEL))
- == NULL) {
- printk(KERN_WARNING "ctc: Out of memory in add_channeln");
- kfree_fsm(ch->fsm);
- kfree(ch);
- return -1;
- }
- memset(ch->devstat, 0, sizeof(devstat_t));
- while (*c && ((*c)->devno < devno))
- c = &(*c)->next;
- if ((*c)->devno == devno) {
- printk(KERN_DEBUG
- "ctc: add_channel: device %04x already in list, "
- "using old entryn", (*c)->devno);
- kfree(ch->devstat);
- kfree_fsm(ch->fsm);
- kfree(ch);
- return 0;
- }
- 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;
- }
- #ifndef CTC_CHANDEV
- /**
- * scan for all channels and create an entry in the channels list
- * for every supported channel.
- */
- static void channel_scan(void)
- {
- static int print_result = 1;
- int irq;
- int nr_escon = 0;
- int nr_ctca = 0;
- s390_dev_info_t di;
- for (irq = 0; irq < NR_IRQS; irq++) {
- if (get_dev_info_by_irq(irq, &di) == 0) {
- if ((di.status == DEVSTAT_NOT_OPER) ||
- (di.status == DEVSTAT_DEVICE_OWNED))
- continue;
- switch (channel_type(&di.sid_data)) {
- case channel_type_ctca:
- /* CTC/A */
- if (!add_channel(irq, di.devno,
- channel_type_ctca))
- nr_ctca++;
- break;
- case channel_type_escon:
- /* ESCON */
- if (!add_channel(irq, di.devno,
- channel_type_escon))
- nr_escon++;
- break;
- default:
- }
- }
- }
- if (print_result) {
- if (nr_escon + nr_ctca)
- printk(KERN_INFO
- "ctc: %d CTC/A channel%s and %d ESCON "
- "channel%s found.n",
- nr_ctca, (nr_ctca == 1) ? "s" : "",
- nr_escon, (nr_escon == 1) ? "s" : "");
- else
- printk(KERN_INFO "ctc: No channel devices found.n");
- }
- print_result = 0;
- }
- #endif
- /**
- * Release a specific channel in the channel list.
- *
- * @param ch Pointer to channel struct to be released.
- */
- static void channel_free(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(channel *ch)
- {
- channel **c = &channels;
- if (ch == NULL)
- return;
- #ifndef CTC_CHANDEV
- if (ch->flags & CHANNEL_FLAGS_INUSE)
- FREE_IRQ(ch->irq, ch->devstat);
- #endif
- 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);
- return;
- }
- c = &((*c)->next);
- }
- }
- /**
- * Get a specific channel from the channel list.
- *
- * @param type Type of channel we are interested in.
- * @param devno Device number 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 channel *channel_get(channel_type_t type, int devno, int direction)
- {
- channel *ch = channels;
- #ifdef DEBUG
- printk(KERN_DEBUG
- "ctc: %s(): searching for ch with devno %d and type %dn",
- __FUNCTION__, devno, type);
- #endif
- while (ch && ((ch->devno != devno) || (ch->type != type))) {
- #ifdef DEBUG
- printk(KERN_DEBUG
- "ctc: %s(): ch=0x%p (devno=%d, type=%dn",
- __FUNCTION__, ch, ch->devno, ch->type);
- #endif
- ch = ch->next;
- }
- #ifdef DEBUG
- printk(KERN_DEBUG
- "ctc: %s(): ch=0x%pq (devno=%d, type=%dn",
- __FUNCTION__, ch, ch->devno, ch->type);
- #endif
- if (!ch) {
- printk(KERN_WARNING "ctc: %s(): channel with devno %d "
- "and type %d not found in channel listn",
- __FUNCTION__, devno, 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;
- }
- #ifndef CTC_CHANDEV
- /**
- * Get the next free channel from the channel list
- *
- * @param type Type 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 channel *channel_get_next(channel_type_t type, int direction)
- {
- channel *ch = channels;
- while (ch && (ch->type != type || (ch->flags & CHANNEL_FLAGS_INUSE)))
- ch = ch->next;
- if (ch) {
- 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;
- }
- #endif
- /**
- * Return the channel type by name.
- *
- * @param name Name of network interface.
- *
- * @return Type class of channel to be used for that interface.
- */
- static channel_type_t inline extract_channel_media(char *name)
- {
- channel_type_t ret = channel_type_unknown;
- if (name != NULL) {
- if (strncmp(name, "ctc", 3) == 0)
- ret = channel_type_ctca;
- if (strncmp(name, "escon", 5) == 0)
- ret = channel_type_escon;
- }
- return ret;
- }
- /**
- * Find a channel in the list by its IRQ.
- *
- * @param irq IRQ to search for.
- *
- * @return Pointer to channel or NULL if no matching channel found.
- */
- static channel *find_channel_by_irq(int irq)
- {
- channel *ch = channels;
- while (ch && (ch->irq != irq))
- ch = ch->next;
- return ch;
- }
- /**
- * Main IRQ handler.
- *
- * @param irq The IRQ to handle.
- * @param intparm IRQ params.
- * @param regs CPU registers.
- */
- static void ctc_irq_handler (int irq, void *intparm, struct pt_regs *regs)
- {
- devstat_t *devstat = (devstat_t *)intparm;
- channel *ch = (channel *)devstat->intparm;
- net_device *dev;
- /**
- * Check for unsolicited interrupts.
- * If intparm is NULL, then loop over all our known
- * channels and try matching the irq number.
- */
- if (ch == NULL) {
- if ((ch = find_channel_by_irq(irq)) == NULL) {
- printk(KERN_WARNING
- "ctc: Got unsolicited irq: %04x c-%02x d-%02x"
- "f-%02xn", devstat->devno, devstat->cstat,
- devstat->dstat, devstat->flag);
- goto done;
- }
- }
- dev = (net_device *)(ch->netdev);
- if (dev == NULL) {
- printk(KERN_CRIT
- "ctc: ctc_irq_handler dev = NULL irq=%d, ch=0x%pn",
- irq, ch);
- goto done;
- }
- if (intparm == NULL)
- printk(KERN_DEBUG "%s: Channel %04x found by IRQ %dn",
- dev->name, ch->devno, irq);
- #ifdef DEBUG
- printk(KERN_DEBUG
- "%s: interrupt for device: %04x received c-%02x d-%02x "
- "f-%02xn", dev->name, devstat->devno, devstat->cstat,
- devstat->dstat, devstat->flag);
- #endif
- /* Check for good subchannel return code, otherwise error message */
- if (devstat->cstat) {
- fsm_event(ch->fsm, CH_EVENT_SC_UNKNOWN, ch);
- printk(KERN_WARNING
- "%s: subchannel check for device: %04x - %02x %02x "
- "%02xn", dev->name, ch->devno, devstat->cstat,
- devstat->dstat, devstat->flag);
- goto done;
- }
- /* Check the reason-code of a unit check */
- if (devstat->dstat & DEV_STAT_UNIT_CHECK) {
- ccw_unit_check(ch, devstat->ii.sense.data[0]);
- goto done;
- }
- if (devstat->dstat & DEV_STAT_BUSY) {
- if (devstat->dstat & DEV_STAT_ATTENTION)
- fsm_event(ch->fsm, CH_EVENT_ATTNBUSY, ch);
- else
- fsm_event(ch->fsm, CH_EVENT_BUSY, ch);
- goto done;
- }
- if (devstat->dstat & DEV_STAT_ATTENTION) {
- fsm_event(ch->fsm, CH_EVENT_ATTN, ch);
- goto done;
- }
- if (devstat->flag & DEVSTAT_FINAL_STATUS)
- fsm_event(ch->fsm, CH_EVENT_FINSTAT, ch);
- else
- fsm_event(ch->fsm, CH_EVENT_IRQ, ch);
- done:
- }
- /**
- * 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 net_device * upon call.
- */
- static void dev_action_start(fsm_instance *fi, int event, void *arg)
- {
- net_device *dev = (net_device *)arg;
- ctc_priv *privptr = dev->priv;
- int direction;
- fsm_newstate(fi, DEV_STATE_STARTWAIT_RXTX);
- for (direction = READ; direction <= WRITE; direction++) {
- 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 net_device * upon call.
- */
- static void dev_action_stop(fsm_instance *fi, int event, void *arg)
- {
- net_device *dev = (net_device *)arg;
- ctc_priv *privptr = dev->priv;
- int direction;
- fsm_newstate(fi, DEV_STATE_STOPWAIT_RXTX);
- for (direction = READ; direction <= WRITE; direction++) {
- channel *ch = privptr->channel[direction];
- fsm_event(ch->fsm, CH_EVENT_STOP, ch);
- }
- }
- /**
- * 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 net_device * upon call.
- */
- static void dev_action_chup(fsm_instance *fi, int event, void *arg)
- {
- net_device *dev = (net_device *)arg;
- ctc_priv *privptr = dev->priv;
- 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);
- printk(KERN_INFO
- "%s: connected with remote siden",
- dev->name);
- if (privptr->protocol == CTC_PROTO_LINUX_TTY)
- ctc_tty_setcarrier(dev, 1);
- ctc_clear_busy(dev);
- }
- break;
- case DEV_STATE_STARTWAIT_TX:
- if (event == DEV_EVENT_TXUP) {
- fsm_newstate(fi, DEV_STATE_RUNNING);
- printk(KERN_INFO
- "%s: connected with remote siden",
- dev->name);
- if (privptr->protocol == CTC_PROTO_LINUX_TTY)
- ctc_tty_setcarrier(dev, 1);
- 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 net_device * upon call.
- */
- static void dev_action_chdown(fsm_instance *fi, int event, void *arg)
- {
- net_device *dev = (net_device *)arg;
- ctc_priv *privptr = dev->priv;
- switch (fsm_getstate(fi)) {
- case DEV_STATE_RUNNING:
- if (privptr->protocol == CTC_PROTO_LINUX_TTY)
- ctc_tty_setcarrier(dev, 0);
- 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_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_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_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_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_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_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 },
- };
- 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(channel *ch, struct sk_buff *skb) {
- unsigned long saveflags;
- ll_header header;
- int rc = 0;
- if (fsm_getstate(ch->fsm) != CH_STATE_TXIDLE) {
- int l = skb->len + LL_HEADER_LENGTH;
- spin_lock_irqsave(&ch->collect_lock, saveflags);
- if (ch->collect_len + l > ch->max_bufsize - 2)
- rc = -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;
- /**
- * 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 + 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);
- 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], virt_to_phys(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);
- return -EBUSY;
- }
- ch->trans_skb->tail = ch->trans_skb->data;
- ch->trans_skb->len = 0;
- ch->ccw[1].count = skb->len;
- memcpy(skb_put(ch->trans_skb, skb->len), skb->data,
- 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);
- s390irq_spin_lock_irqsave(ch->irq, saveflags);
- ch->prof.send_stamp = xtime;
- rc = do_IO(ch->irq, &ch->ccw[ccw_idx], (intparm_t)ch, 0xff, 0);
- s390irq_spin_unlock_irqrestore(ch->irq, saveflags);
- if (ccw_idx == 3)
- ch->prof.doios_single++;
- if (rc != 0) {
- fsm_deltimer(&ch->timer);
- ccw_check_return_code(ch, rc);
- 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) {
- net_device *dev = ch->netdev;
- ctc_priv *privptr = dev->priv;
- privptr->stats.tx_packets++;
- privptr->stats.tx_bytes +=
- skb->len - LL_HEADER_LENGTH;
- }
- }
- }
- 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(net_device *dev) {
- MOD_INC_USE_COUNT;
- fsm_event(((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(net_device *dev) {
- SET_DEVICE_START(dev, 0);
- fsm_event(((ctc_priv *)dev->priv)->fsm, DEV_EVENT_STOP, dev);
- MOD_DEC_USE_COUNT;
- 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, net_device *dev)
- {
- int rc = 0;
- ctc_priv *privptr = (ctc_priv *)dev->priv;
- /**
- * Some sanity checks ...
- */
- if (skb == NULL) {
- printk(KERN_WARNING "%s: NULL sk_buff passedn", dev->name);
- privptr->stats.tx_dropped++;
- return 0;
- }
- if (skb_headroom(skb) < (LL_HEADER_LENGTH + 2)) {
- printk(KERN_WARNING
- "%s: Got sk_buff with head room < %ld bytesn",
- 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
- * notify anybody about a link failure and throw
- * away packet.
- */
- if (fsm_getstate(privptr->fsm) != DEV_STATE_RUNNING) {
- fsm_event(privptr->fsm, DEV_EVENT_START, dev);
- if (privptr->protocol == CTC_PROTO_LINUX_TTY)
- return -EBUSY;
- dst_link_failure(skb);
- 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;
- ctc_clear_busy(dev);
- 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(net_device *dev, int new_mtu) {
- ctc_priv *privptr = (ctc_priv *)dev->priv;
- 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(net_device *dev) {
- return &((ctc_priv *)dev->priv)->stats;
- }
- /**
- * procfs related structures and routines
- *****************************************************************************/
- static net_device *find_netdev_by_ino(unsigned long ino)
- {
- channel *ch = channels;
- net_device *dev = NULL;
- ctc_priv *privptr;
- while (ch) {
- if (ch->netdev != dev) {
- dev = ch->netdev;
- privptr = (ctc_priv *)dev->priv;
- if ((privptr->proc_ctrl_entry->low_ino == ino) ||
- (privptr->proc_stat_entry->low_ino == ino))
- return dev;
- }
- ch = ch->next;
- }
- return NULL;
- }
- #if LINUX_VERSION_CODE < 0x020363
- /**
- * Lock the module, if someone changes into
- * our proc directory.
- */
- static void ctc_fill_inode(struct inode *inode, int fill)
- {
- if (fill) {
- MOD_INC_USE_COUNT;
- } else
- MOD_DEC_USE_COUNT;
- }
- #endif
- #define CTRL_BUFSIZE 40
- static int ctc_ctrl_open(struct inode *inode, struct file *file)
- {
- file->private_data = kmalloc(CTRL_BUFSIZE, GFP_KERNEL);
- if (file->private_data == NULL)
- return -ENOMEM;
- MOD_INC_USE_COUNT;
- return 0;
- }
- static int ctc_ctrl_close(struct inode *inode, struct file *file)
- {
- kfree(file->private_data);
- MOD_DEC_USE_COUNT;
- return 0;
- }
- static ssize_t ctc_ctrl_write(struct file *file, const char *buf, size_t count,
- loff_t *off)
- {
- unsigned int ino = ((struct inode *)file->f_dentry->d_inode)->i_ino;
- net_device *dev;
- ctc_priv *privptr;
- char *e;
- int bs1;
- char tmp[40];
- if (!(dev = find_netdev_by_ino(ino)))
- return -ENODEV;
- if (off != &file->f_pos)
- return -ESPIPE;
- privptr = (ctc_priv *)dev->priv;
- if (count >= 39)
- return -EINVAL;
- if (copy_from_user(tmp, buf, count))
- return -EFAULT;
- tmp[count+1] = ' ';
- bs1 = simple_strtoul(tmp, &e, 0);
- if ((bs1 > CTC_BUFSIZE_LIMIT) ||
- (e && (!isspace(*e))))
- return -EINVAL;
- if ((dev->flags & IFF_RUNNING) &&
- (bs1 < (dev->mtu + LL_HEADER_LENGTH + 2)))
- return -EINVAL;
- if (bs1 < (576 + LL_HEADER_LENGTH + 2))
- return -EINVAL;
- privptr->channel[READ]->max_bufsize =
- privptr->channel[WRITE]->max_bufsize = bs1;
- if (!(dev->flags & IFF_RUNNING))
- dev->mtu = bs1 - LL_HEADER_LENGTH - 2;
- privptr->channel[READ]->flags |= CHANNEL_FLAGS_BUFSIZE_CHANGED;
- privptr->channel[WRITE]->flags |= CHANNEL_FLAGS_BUFSIZE_CHANGED;
- return count;
- }
- static ssize_t ctc_ctrl_read(struct file *file, char *buf, size_t count,
- loff_t *off)
- {
- unsigned int ino = ((struct inode *)file->f_dentry->d_inode)->i_ino;
- char *sbuf = (char *)file->private_data;
- net_device *dev;
- ctc_priv *privptr;
- ssize_t ret = 0;
- char *p = sbuf;
- int l;
- if (!(dev = find_netdev_by_ino(ino)))
- return -ENODEV;
- if (off != &file->f_pos)
- return -ESPIPE;
- privptr = (ctc_priv *)dev->priv;
- if (file->f_pos == 0)
- sprintf(sbuf, "%dn", privptr->channel[READ]->max_bufsize);
- l = strlen(sbuf);
- p = sbuf;
- if (file->f_pos < l) {
- p += file->f_pos;
- l = strlen(p);
- ret = (count > l) ? l : count;
- if (copy_to_user(buf, p, ret))
- return -EFAULT;
- }
- file->f_pos += ret;
- return ret;
- }
- #define STATS_BUFSIZE 2048
- static int ctc_stat_open(struct inode *inode, struct file *file)
- {
- file->private_data = kmalloc(STATS_BUFSIZE, GFP_KERNEL);
- if (file->private_data == NULL)
- return -ENOMEM;
- MOD_INC_USE_COUNT;
- return 0;
- }
- static int ctc_stat_close(struct inode *inode, struct file *file)
- {
- kfree(file->private_data);
- MOD_DEC_USE_COUNT;
- return 0;
- }
- static ssize_t ctc_stat_write(struct file *file, const char *buf, size_t count,
- loff_t *off)
- {
- unsigned int ino = ((struct inode *)file->f_dentry->d_inode)->i_ino;
- net_device *dev;
- ctc_priv *privptr;
- if (!(dev = find_netdev_by_ino(ino)))
- return -ENODEV;
- privptr = (ctc_priv *)dev->priv;
- privptr->channel[WRITE]->prof.maxmulti = 0;
- privptr->channel[WRITE]->prof.maxcqueue = 0;
- privptr->channel[WRITE]->prof.doios_single = 0;
- privptr->channel[WRITE]->prof.doios_multi = 0;
- privptr->channel[WRITE]->prof.txlen = 0;
- privptr->channel[WRITE]->prof.tx_time = 0;
- return count;
- }
- static ssize_t ctc_stat_read(struct file *file, char *buf, size_t count,
- loff_t *off)
- {
- unsigned int ino = ((struct inode *)file->f_dentry->d_inode)->i_ino;
- char *sbuf = (char *)file->private_data;
- net_device *dev;
- ctc_priv *privptr;
- ssize_t ret = 0;
- char *p = sbuf;
- int l;
- if (!(dev = find_netdev_by_ino(ino)))
- return -ENODEV;
- if (off != &file->f_pos)
- return -ESPIPE;
- privptr = (ctc_priv *)dev->priv;
- if (file->f_pos == 0) {
- p += sprintf(p, "Device FSM state: %sn",
- fsm_getstate_str(privptr->fsm));
- p += sprintf(p, "RX channel FSM state: %sn",
- fsm_getstate_str(privptr->channel[READ]->fsm));
- p += sprintf(p, "TX channel FSM state: %sn",
- fsm_getstate_str(privptr->channel[WRITE]->fsm));
- p += sprintf(p, "Max. TX buffer used: %ldn",
- privptr->channel[WRITE]->prof.maxmulti);
- p += sprintf(p, "Max. chained SKBs: %ldn",
- privptr->channel[WRITE]->prof.maxcqueue);
- p += sprintf(p, "TX single write ops: %ldn",
- privptr->channel[WRITE]->prof.doios_single);
- p += sprintf(p, "TX multi write ops: %ldn",
- privptr->channel[WRITE]->prof.doios_multi);
- p += sprintf(p, "Netto bytes written: %ldn",
- privptr->channel[WRITE]->prof.txlen);
- p += sprintf(p, "Max. TX IO-time: %ldn",
- privptr->channel[WRITE]->prof.tx_time);
- }
- l = strlen(sbuf);
- p = sbuf;
- if (file->f_pos < l) {
- p += file->f_pos;
- l = strlen(p);
- ret = (count > l) ? l : count;
- if (copy_to_user(buf, p, ret))
- return -EFAULT;
- }
- file->f_pos += ret;
- return ret;
- }
- static struct file_operations ctc_stat_fops = {
- read: ctc_stat_read,
- write: ctc_stat_write,
- open: ctc_stat_open,
- release: ctc_stat_close,
- };
- static struct file_operations ctc_ctrl_fops = {
- read: ctc_ctrl_read,
- write: ctc_ctrl_write,
- open: ctc_ctrl_open,
- release: ctc_ctrl_close,
- };
- static struct inode_operations ctc_stat_iops = {
- #if LINUX_VERSION_CODE < 0x020363
- default_file_ops: &ctc_stat_fops
- #endif
- };
- static struct inode_operations ctc_ctrl_iops = {
- #if LINUX_VERSION_CODE < 0x020363
- default_file_ops: &ctc_ctrl_fops
- #endif
- };
- static struct proc_dir_entry stat_entry = {
- 0, /* low_ino */
- 10, /* namelen */
- "statistics", /* name */
- S_IFREG | S_IRUGO | S_IWUSR, /* mode */
- 1, /* nlink */
- 0, /* uid */
- 0, /* gid */
- 0, /* size */
- &ctc_stat_iops /* ops */
- };
- static struct proc_dir_entry ctrl_entry = {
- 0, /* low_ino */
- 10, /* namelen */
- "buffersize", /* name */
- S_IFREG | S_IRUSR | S_IWUSR, /* mode */
- 1, /* nlink */
- 0, /* uid */
- 0, /* gid */
- 0, /* size */
- &ctc_ctrl_iops /* ops */
- };
- #if LINUX_VERSION_CODE < 0x020363
- static struct proc_dir_entry ctc_dir = {
- 0, /* low_ino */
- 3, /* namelen */
- "ctc", /* name */
- S_IFDIR | S_IRUGO | S_IXUGO, /* mode */
- 2, /* nlink */
- 0, /* uid */
- 0, /* gid */
- 0, /* size */
- 0, /* ops */
- 0, /* get_info */
- ctc_fill_inode /* fill_ino (for locking) */
- };
- static struct proc_dir_entry ctc_template =
- {
- 0, /* low_ino */
- 0, /* namelen */
- "", /* name */
- S_IFDIR | S_IRUGO | S_IXUGO, /* mode */
- 2, /* nlink */
- 0, /* uid */
- 0, /* gid */
- 0, /* size */
- 0, /* ops */
- 0, /* get_info */
- ctc_fill_inode /* fill_ino (for locking) */
- };
- #else
- static struct proc_dir_entry *ctc_dir = NULL;
- static struct proc_dir_entry *ctc_template = NULL;
- #endif
- /**
- * Create the driver's main directory /proc/net/ctc
- */
- static void ctc_proc_create_main(void) {
- /**
- * If not registered, register main proc dir-entry now
- */
- #if LINUX_VERSION_CODE > 0x020362
- if (!ctc_dir)
- ctc_dir = proc_mkdir("ctc", proc_net);
- #else
- if (ctc_dir.low_ino == 0)
- proc_net_register(&ctc_dir);
- #endif
- }
- #ifdef MODULE
- /**
- * Destroy /proc/net/ctc
- */
- static void ctc_proc_destroy_main(void) {
- #if LINUX_VERSION_CODE > 0x020362
- remove_proc_entry("ctc", proc_net);
- #else
- proc_net_unregister(ctc_dir.low_ino);
- #endif
- }
- #endif MODULE
- /**
- * Create a device specific subdirectory in /proc/net/ctc/ with the
- * same name like the device. In that directory, create 2 entries
- * "statistics" and "buffersize".
- *
- * @param dev The device for which the subdirectory should be created.
- *
- */
- static void ctc_proc_create_sub(net_device *dev) {
- ctc_priv *privptr = dev->priv;
- #if LINUX_VERSION_CODE > 0x020362
- privptr->proc_dentry = proc_mkdir(dev->name, ctc_dir);
- privptr->proc_stat_entry =
- create_proc_entry("statistics",
- S_IFREG | S_IRUSR | S_IWUSR,
- privptr->proc_dentry);
- privptr->proc_stat_entry->proc_fops = &ctc_stat_fops;
- privptr->proc_stat_entry->proc_iops = &ctc_stat_iops;
- privptr->proc_ctrl_entry =
- create_proc_entry("buffersize",
- S_IFREG | S_IRUSR | S_IWUSR,
- privptr->proc_dentry);
- privptr->proc_ctrl_entry->proc_fops = &ctc_ctrl_fops;
- privptr->proc_ctrl_entry->proc_iops = &ctc_ctrl_iops;
- #else
- privptr->proc_dentry->name = dev->name;
- privptr->proc_dentry->namelen = strlen(dev->name);
- proc_register(&ctc_dir, privptr->proc_dentry);
- proc_register(privptr->proc_dentry, privptr->proc_stat_entry);
- proc_register(privptr->proc_dentry, privptr->proc_ctrl_entry);
- #endif
- privptr->proc_registered = 1;
- }
- /**
- * Destroy a device specific subdirectory.
- *
- * @param privptr Pointer to device private data.
- */
- static void ctc_proc_destroy_sub(ctc_priv *privptr) {
- if (!privptr->proc_registered)
- return;
- #if LINUX_VERSION_CODE > 0x020362
- remove_proc_entry("statistics", privptr->proc_dentry);
- remove_proc_entry("buffersize", privptr->proc_dentry);
- remove_proc_entry(privptr->proc_dentry->name, ctc_dir);
- #else
- proc_unregister(privptr->proc_dentry,
- privptr->proc_stat_entry->low_ino);
- proc_unregister(privptr->proc_dentry,
- privptr->proc_ctrl_entry->low_ino);
- proc_unregister(&ctc_dir,
- privptr->proc_dentry->low_ino);
- #endif
- privptr->proc_registered = 0;
- }
- #ifndef CTC_CHANDEV
- /**
- * Setup related routines
- *****************************************************************************/
- /**
- * Parse a portion of the setup string describing a single device or option
- * providing the following syntax:
- *
- * [Device/OptionName[:int1][:int2][:int3]]
- *
- *
- * @param setup Pointer to a pointer to the remainder of the parameter
- * string to be parsed. On return, the content of this
- * pointer is updated to point to the first character after
- * the parsed portion (e.g. possible start of next portion)
- * NOTE: The string pointed to must be writeable, since a
- *