*
* Linux for S/390 Lan Channel Station Network Driver
*
- * Copyright (C) 1999-2001 IBM Deutschland Entwicklung GmbH, IBM Corporation
- * Author(s): DJ Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com)
- * Frank Pavlic (pavlic@de.ibm.com)
+ * Copyright (C) 1999-2001 IBM Deutschland Entwicklung GmbH,
+ * IBM Corporation
+ * Author(s): Original Code written by
+ * DJ Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com)
+ * Rewritten by
+ * Frank Pavlic (pavlic@de.ibm.com) and
+ * Martin Schwidefsky <schwidefsky@de.ibm.com>
+ *
+ * $Revision: 1.41 $ $Date: 2002/12/06 12:42:01 $
*
- * $Revision: 1.128 $ $Date: 2002/03/01 16:56:47 $
- *
* 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)
*
* 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
+ * 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.
- *
*/
-/*
- Thanks to Uli Hild, Martin Schwidefsky,
- & Ingo who probably will never let it down that
- he found the braindamage with my offset calculations on
- the IO buffers.
- ( for quality of effort, advise, support & sanity checking )
- Alex, Boas & the rest of the team for the opportunity to work here.
-
-DRIVER OPERATION OVERVIEW README FIRST
-======================================
-
-The lcs driver currently supports Ethernet & Token Ring, FDDI is still
-experimental.
-
-The lan channel station(lcs) network interface has two channels one read
-channel one write channel. This is very similar to the S390 CTC interface
-
-The read channel is recognised by having an even cuu number & model 0x3088
-The write channel is recognised by the formula write cuu=read cuu number + 1
-& also a model of 0x3088 only certain cuu models are supported so as not to
-clash with a CTC control unit model.
-
-The driver always has a read outstanding on the read subchannel this is used
-to receive both command replys & network packets ( these are differenciated by
-checking the type field in the lcs header structure ), network packets may
-come in during the startup & shutdown sequence these have to be discarded.
-During normal network I/O the driver will intermittently retry reads to
-permanently keep reads outstanding on the read channel if an -EBUSY or
-similar occurs (otherwise the driver would stop receiving network packets).
-
-The channel progam used work as described below
-===============================================
-The write subchannel has a hack in /net/core/skbuff.c which makes
-sure there is enough head & tailroom to write packets directly out of the
-socket buffer (If their isn't the code will still work).
-
-If the write subchannel is busy network packets are
-aggregated together into a & queued for transmission, the reason I'm doing
-this is to minimise start subchannels & interrupts which I have been told
-& have proven to be very expensive.
-
-e.g. a ping -f -s 1400 -c 20000 <gateway_addr> >/dev/null used take 40 secs,
-on my machine & a ping -f -s 100 -c 20000 <gateway_addr> >/dev/null took 27
-secs, over token ring with a single buffer this has improved a lot with the
-queueing technique (I also made sure that the delay wasn't caused by waiting
-for the free token).
-
-The new program works as follows
-================================
-The read & channel program consists of of 8 read ccw commands
-followed by a transfer in channel back to the 1st read ccw.
-A moving suspend bit is switched on in the read
-ccw of the last buffer processed with pci's switched on in the
-remaining ccw's. This way I recieve buffers with pci's without
-having to issue excess resumes or ssch's & only
-need to issue a resume if the read program gets suspended when
-all the buffers are filled.
-
-The write program consists of 4 to 9 write ccw commands folled
-by a transfer in channel back to the 1st write ccw.
-All unfilled buffers all have the suspend bit on & the
-suspend bit is taken off when buffers are either filled or partially
-filled & the write channel is no longer busy.
-
-The lan channel is started as follows
-1) A halt subchannel is issued to the read & write subchannel
-2) A startup primitive is sent to the lan & the reply is read from the read
-subchannel.
-3) Every possible relative adapter number issued a startlan primative with
-attempts to configure it as token ring ethernet & fddi at some stage in the
-boot sequence or when the module is being inserted.
-The relative adapter number is normally equal to the low byte of the read
-channel cuu/2 & hence the reason for the hint field to speed up lcs_detect.
-5) On success of a startlan we know whether the osa card is configured as
-token ring, ethernet or fddi, a lanstat primative is issued to get the
-mac address.
-6) The device is now ready for network io.
-
-The shutdown is similar to the startup just a stoplan primative followed
-by a shutdown.
-
-The driver instance globals lcs_drvr_globals are held in the priv element of
-the device structure & these in turn point to a lcs_chan_globals structure
-for the read & write channel. The read & write chan_globals & driver
-globals have their own state machine, so that the interrupt handler can
-make decisions on whether to send packets read to the network layer, or
-whether it is should wake up a process currently issuing lan commands.
-
-*/
-
-#define TRUE 1
-#define FALSE 0
-#define ERETRY 255
-
#include <linux/version.h>
#include <linux/module.h>
-#include <linux/types.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/errno.h>
-#include <linux/init.h>
-#include <asm/system.h>
-#include <asm/io.h>
-#include <asm/irq.h>
+#include <linux/if.h>
#include <linux/netdevice.h>
-#if LINUX_VERSION_CODE<KERNEL_VERSION(2,3,0)
-#include <linux/kcomp.h>
-#endif
-
-#if CONFIG_NET_ETHERNET
#include <linux/etherdevice.h>
-extern int ethif_probe(struct net_device *dev);
-#endif
-#ifdef CONFIG_TR
#include <linux/trdevice.h>
-extern int trif_probe(struct net_device *dev);
-#endif
-#ifdef CONFIG_FDDI
#include <linux/fddidevice.h>
-extern int fddiif_probe(struct net_device *dev);
-#endif
-#include <asm/irq.h>
-#include <asm/queue.h>
-#define LCS_CHANDEV CONFIG_CHANDEV
-#if LCS_CHANDEV
-#include <asm/chandev.h>
-#endif
-#ifdef CONFIG_IP_MULTICAST
#include <linux/inetdevice.h>
-#include <net/arp.h>
-#include <net/ip.h>
#include <linux/in.h>
#include <linux/igmp.h>
-#endif
-#include <net/pkt_sched.h>
-#include <net/sock.h>
-#include <net/dst.h>
-
-#define LCS_USE_IDALS (LINUX_VERSION_CODE>KERNEL_VERSION(2,2,16))
-#if (!CONFIG_ARCH_S390X) || (LCS_USE_IDALS)
-#define lcs_dev_alloc_skb(length) dev_alloc_skb((length))
-#endif
-#if LCS_USE_IDALS
-# 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>=KERNEL_VERSION(2,2,18))
-#define LCS_DEBUG 1
-#else
-#define LCS_DEBUG 0
-#endif
-#define LCS_PRINTK_DEBUG 0
-#define LCS_ZERO_COPY_READ 0
+#include <net/arp.h>
+#include <net/ip.h>
-#if LCS_DEBUG && !LCS_PRINTK_DEBUG
#include <asm/debug.h>
-#endif
+#include <asm/idals.h>
+#include <asm/timex.h>
+#include <linux/device.h>
+#include <asm/ccwgroup.h>
-#if !defined(CONFIG_NET_ETHERNET)&&!defined(CONFIG_TR)&&!defined(CONFIG_FDDI)
-#error Cannot compile lcs.c without some net devices switched on.
-#endif
-
-#define lcs_inline inline
-
-#if LINUX_VERSION_CODE<=KERNEL_VERSION(2,2,16)
-#define lcs_daemonize(name,mask,use_init_fs) s390_daemonize(name,mask)
-#else
-#define lcs_daemonize(noargs...)
-#endif
-
-/* for the proc filesystem */
-static const char *cardname = "S390 Lan Channel Station Interface";
-
-/* lcs ccw command codes */
-
-enum {
- ccw_write = 0x1,
- ccw_read = 0x2,
- ccw_read_backward = 0xc,
- ccw_control = 0x3,
- ccw_sense = 0x4,
- ccw_sense_id = 0xe4,
- ccw_transfer_in_channel0 = 0x8,
- ccw_transfer_in_channel1 = 0x8,
- ccw_set_x_mode = 0xc3, // according to uli's lan notes
- ccw_nop = 0x3 // according to uli's notes again
- // n.b. ccw_control clashes with this
- // so I presume its a special case of
- // control
-};
+#include "lcs.h"
+#include "cu3088.h"
-/* startup & shutdown primatives */
-typedef enum {
- lcs_startlan = 1,
- lcs_stoplan = 2,
- lcs_lanstat = 4,
- lcs_startup = 7,
- lcs_shutdown = 8,
-#ifdef CONFIG_IP_MULTICAST
- lcs_qipassist = 0xb2,
- lcs_setipm = 0xb4,
- lcs_delipm = 0xb5
+#if !defined(CONFIG_NET_ETHERNET) && \
+ !defined(CONFIG_TR) && !defined(CONFIG_FDDI)
+#error Cannot compile lcs.c without some net devices switched on.
#endif
-} lcs_cmd;
-
-typedef enum {
- command_reject = 0x80,
- intervention_required = 0x40,
- bus_out_check = 0x20,
- equipment_check = 0x10,
- interface_disconnect = 0x01,
-} lcs_sense_byte_0;
-
-typedef enum {
- resetting_event = 0x0080,
- device_online = 0x0020,
-} lcs_sense_byte_1;
-
-/* The type of packet we are transmitting or receiving from the lcs */
-typedef enum {
- lcs_control_frame_type = 0, /* for startup shutdown etc */
- lcs_enet_type = 1, /* the rest are for normal network io */
- lcs_token_ring_type = 2,
- lcs_fddi_type = 7,
- lcs_autodetect_type = -1,
-} lcs_frame_type;
-
-#define LCS_ILLEGAL_OFFSET 0xffff
-
-static int use_hw_stats = 0;
-static int checksum_received_ip_pkts = 0;
-#if !LCS_CHANDEV
-/* Max number of cuu models we can detect */
-#define LCS_CURR_NUM_MODELS 4
-#define LCS_ADDITIONAL_MODELS 12
-#define LCS_ADDITIONAL_MODELS_X2_STR "24"
-#define LCS_MAX_NUM_MODELS (LCS_CURR_NUM_MODELS+LCS_ADDITIONAL_MODELS)
-/* max number of parms that can be sent to lcs_setup */
-#define LCS_MAX_NUM_PARMS ((LCS_MAX_NUM_MODELS<<1)+2)
-/* configuration parms for the lcs card */
-typedef struct {
- int cu_model;
- int max_rel_adapter_no;
-} lcs_model_info;
-
-/* The models supported currently used in probing */
-#define LCS_CUTYPE 0x3088
-
-/* default configuration parms */
-
-static lcs_model_info lcs_models[LCS_CURR_NUM_MODELS]
- __attribute__ ((section(".data"))) = {
- {0x1, 15},
- /* P390/Planter 3172 emulation assume maximum 16 to be safe. */
- {0x8, 15},
- /* 3172/2216 Paralell the 2216 allows 16 ports per card */
- /* the original 3172 only allows 4 we will assume the max of 16 */
- {0x60, 1},
- /* Only 2 ports allowed on OSA2 cards model 0x60 */
- {0x1F, 15},
- /* 3172/2216 Escon serial the 2216 allows 16 ports per card */
- /* the original 3172 only allows 4 we will assume the max of 16 */
-};
-static int additional_model_info[LCS_ADDITIONAL_MODELS << 1] = { 0, };
-/* Set this to one to stop autodetection */
-static int noauto = 0, ignore_sense = 0;
-/*
- * The lcs header
- * offset=the offset of the next cmd/network packet
- * type=command ethernet token ring fddi frame
- * slot=relative adapter no
+/**
+ * initialization string for output
*/
+#define VERSION_LCS_C "$Revision: 1.41 $"
-#define LCS_MAX_FORCED_DEVNOS 12
-#define LCS_MAX_FORCED_DEVNOS_X2_STR "24"
-typedef struct {
- int devno;
- int portno;
-} lcs_dev_portno_pair;
-static int devno_portno_pairs[LCS_MAX_FORCED_DEVNOS << 1] =
- {[0 ... ((LCS_MAX_FORCED_DEVNOS << 1) - 1)] 2, };
-static lcs_dev_portno_pair *lcs_dev_portno =
- (lcs_dev_portno_pair *) & devno_portno_pairs[0];
-#endif
-
-#define lcs_header \
-u16 offset; \
-u8 type; \
-u8 slot;
-
-typedef struct {
-lcs_header} lcs_header_type __attribute__ ((packed));
-
-enum {
- lcs_390_initiated,
- lcs_lgw_initiated
-};
+static const char *version="LCS driver ("VERSION_LCS_C "/" VERSION_LCS_H ")";
-/*
- * lcs startup & shutdown commands
+/**
+ * Some prototypes.
*/
-#define lcs_base_cmd \
-u8 cmd_code; \
-u8 initiator; \
-u16 sequence_no; \
-u16 return_code; \
-
-typedef struct {
- lcs_header lcs_base_cmd u8 lan_type;
- u8 rel_adapter_no;
- u16 parameter_count;
- u8 operator_flags[3];
- u8 reserved[3];
- u8 command_data[0];
-} lcs_std_cmd __attribute__ ((packed));
-
-typedef struct {
- lcs_header lcs_base_cmd u16 unused1;
- u16 buff_size;
- u8 unused2[6];
-} lcs_startup_cmd __attribute__ ((packed));
-
-#define LCS_ADDR_LEN 6
-
-#ifdef CONFIG_IP_MULTICAST
-typedef struct {
- u32 ip_addr;
- u8 mac_address[LCS_ADDR_LEN];
- u8 reserved[2];
-} lcs_ip_mac_addr_pair __attribute__ ((packed));
-
-typedef enum {
- ipm_set_required,
- ipm_delete_required,
- ipm_on_card
-} lcs_ipm_state;
-typedef struct lcs_ipm_list lcs_ipm_list;
-struct lcs_ipm_list {
- struct lcs_ipm_list *next;
- lcs_ip_mac_addr_pair ipm;
- lcs_ipm_state ipm_state;
-};
-
-#define MAX_IP_MAC_PAIRS 32
-typedef struct {
- lcs_header lcs_base_cmd u8 lan_type;
- u8 rel_adapter_no;
- /* OSA only will only support one IP Multicast entry at a time */
- u16 num_ipm_pairs;
- u16 ip_assists_supported; /* returned by OSA */
- u16 ip_assists_enabled; /* returned by OSA */
- u16 version; /* IP version i.e. 4 */
- lcs_ip_mac_addr_pair ip_mac_pair[MAX_IP_MAC_PAIRS];
- u32 response_data;
-} lcs_ipm_ctlmsg __attribute__ ((packed));
-typedef struct {
- lcs_header lcs_base_cmd u8 lan_type;
- u8 rel_adapter_no;
- u16 num_ip_pairs; /* should be 0 */
- u16 ip_assists_supported; /* returned by OSA */
- u16 ip_assists_enabled; /* returned by OSA */
- u16 version; /* IP version i.e. 4 */
-} lcs_qipassist_ctlmsg __attribute__ ((packed));
-
-typedef enum {
- /* Not supported by LCS */
- lcs_arp_processing = 0x0001,
- lcs_inbound_checksum_support = 0x0002,
- lcs_outbound_checksum_support = 0x0004,
- lcs_ip_frag_reassembly = 0x0008,
- lcs_ip_filtering = 0x0010,
- /* Supported by lcs 3172 */
- lcs_ip_v6_support = 0x0020,
- lcs_multicast_support = 0x0040
-} lcs_assists;
-#define LANCMD_DEFAULT_MCAST_PARMS ,0,NULL
-#else
-#define LANCMD_DEFAULT_MCAST_PARMS
-#endif
-
-typedef struct {
- lcs_header lcs_base_cmd u8 lan_type;
- u8 rel_adapter_no;
- u8 unused1[10];
- u8 mac_address[LCS_ADDR_LEN];
- u32 num_packets_deblocked;
- u32 num_packets_blocked;
- u32 num_packets_tx_on_lan;
- u32 num_tx_errors_detected;
- u32 num_tx_packets_disgarded;
- u32 num_packets_rx_from_lan;
- u32 num_rx_errors_detected;
- u32 num_rx_discarded_nobuffs_avail;
- u32 num_rx_packets_too_large;
-} lcs_lanstat_reply __attribute__ ((packed));
-
-/* This buffer sizes are used by MVS so they should be reasonable */
-#define LCS_IOBUFFSIZE 0x5000
-#define LCS_NUM_TX_BUFFS 8
-#define LCS_NUM_RX_BUFFS 8
-
-#define LCS_INVALID_LOCK_OWNER -1
-
-#if LINUX_VERSION_CODE<KERNEL_VERSION(2,2,18)
-typedef struct wait_queue *wait_queue_head_t;
-#endif
-#if LINUX_VERSION_CODE<KERNEL_VERSION(2,4,0)
-typedef dev_info_t s390_dev_info_t;
-#endif
-
-typedef enum {
- chan_dead,
- chan_idle,
- chan_busy,
- chan_starting_up,
- chan_started_successfully
-} lcs_chan_busy_state;
-
-#define LCS_MAGIC 0x05A22A05 /* OSA2 to you */
-#define LCS_CHAN_GLOBALS(num_io_buffs) \
-u32 irq_allocated_magic; \
-u16 subchannel; \
-u16 devno; \
-struct lcs_drvr_globals *drvr_globals; \
-atomic_t sleeping_on_io; \
-unsigned long flags; \
-int rc; \
-int lock_owner; \
-int lock_cnt; \
-wait_queue_head_t wait; \
-struct work_struct retry_task; \
-devstat_t devstat; \
-lcs_chan_busy_state chan_busy_state; \
-ccw1_t ccw[num_io_buffs+1]; \
-
-
-typedef struct {
- LCS_CHAN_GLOBALS(0)
-} lcs_chan_globals;
-
-typedef struct {
- LCS_CHAN_GLOBALS(LCS_NUM_RX_BUFFS)
-#if LCS_ZERO_COPY_READ
- struct sk_buff *skb_buff[LCS_NUM_RX_BUFFS];
-#else
- u8 *iobuff[LCS_NUM_RX_BUFFS];
-#endif
- lcs_std_cmd *lancmd_reply_ptr; /* only used by read channel */
- u32 rx_idx;
- u8 pad[0] __attribute__ ((aligned(8))); /* so the next structure
- will be aligned */
-} lcs_read_globals;
-
-typedef struct {
- LCS_CHAN_GLOBALS(LCS_NUM_TX_BUFFS)
- u8 *iobuff[LCS_NUM_TX_BUFFS];
- u32 prepare_idx; /* current buffer being prepared */
- u32 tx_idx; /* last buffer successfully transmitted */
- unsigned long pkts_still_being_txed;
- unsigned long bytes_still_being_txed;
- struct work_struct resume_task;
- int resume_queued;
- int resume_loopcnt;
- uint64_t last_lcs_txpacket_time;
- uint64_t last_resume_time;
- uint64_t adjusted_last_bytes_still_being_txed;
-} lcs_write_globals;
-
-#define LCS_READ_ALLOCSIZE LCS_IOBUFFSIZE
+static void lcs_tasklet(unsigned long);
+static void lcs_start_kernel_thread(struct lcs_card *card);
+static void lcs_get_frames_cb(struct lcs_channel *, struct lcs_buffer *);
-/*
- * drvr_globals main state machine
+/**
+ * Debug Facility Stuff
*/
-typedef enum {
- lcs_idle,
- lcs_halting_subchannels,
- lcs_doing_io,
- lcs_idle_requesting_channels_for_lancmds,
- lcs_requesting_channels_for_lancmds,
- lcs_got_write_channel_for_lancmds,
- lcs_doing_lancmds,
- lcs_interrupted
-} lcs_state;
-
-enum {
- lcs_invalid_adapter_no = -1
-};
+static debug_info_t *lcs_dbf_setup;
+static debug_info_t *lcs_dbf_trace;
-typedef struct lcs_drvr_globals lcs_drvr_globals;
-struct lcs_drvr_globals {
- struct lcs_drvr_globals *next;
- struct net_device *dev;
- atomic_t usage_cnt; /* used by drvr_globals_valid
- * & lcs_usage_free_drvr_globals */
- u8 lan_type;
- u8 cmd_code;
- unsigned short (*lan_type_trans) (struct sk_buff * skb,
- struct net_device * dev);
- wait_queue_head_t lanstat_wait; /* processes asleep awaiting
- * lanstat results */
- int doing_lanstat;
- int up;
- lcs_state state;
- lcs_read_globals *read;
- lcs_write_globals *write;
- u16 sequence_no;
- s16 rel_adapter_no;
- int slow_hw;
- /* To insure only one kernel thread runs at a time */
- atomic_t kernel_thread_lock;
- int (*kernel_thread_routine) (lcs_drvr_globals * drvr_globals);
- struct work_struct kernel_thread_task;
- u16 lgw_sequence_no; /* this isn't required just being thorough */
- atomic_t retry_cnt;
- struct net_device_stats stats __attribute__ ((aligned(4)));
-#ifdef CONFIG_IP_MULTICAST
- spinlock_t ipm_lock;
- lcs_ipm_list *ipm_list;
-#endif
- /* So I can use atomic atomic_t for stats if required */
- u8 pad[0] __attribute__ ((aligned(8)));
- /* CCW's need 8 byte alignment ( Thanks Martin ) */
-} __attribute__ ((aligned(8)));
-
-/*
- * A token ring header can be 40 bytes if you include the llc & rcf so we
- * leave a small bit extra.
+/**
+ * LCS Debug Facility functions
*/
-
-#define LCS_TX_HIWATERMARK LCS_IOBUFFSIZE-60
-
-#define LCS_ALLOCSIZE (sizeof(lcs_drvr_globals)+sizeof(lcs_read_globals)+sizeof(lcs_write_globals))
-
-/* Function prototypes */
-static int lcs_rxpacket(lcs_drvr_globals * drvr_globals, u8 * start_buff,
- lcs_std_cmd ** lancmd_reply
-#if LCS_ZERO_COPY_READ
- , u32 curr_idx
-#endif
- );
-static int lcs_txpacket(struct sk_buff *skb, struct net_device *dev);
-static int lcs_detect(lcs_drvr_globals * drvr_globals, s16 forced_port_no,
- u8 hint_port_no, u8 max_port_no,
- lcs_frame_type frame_type, u8 * mac_address);
-static int lcs_check_reset(lcs_chan_globals * chan_globals);
-static void lcs_restartreadio(lcs_drvr_globals * drvr_globals);
-static void lcs_restartwriteio(lcs_drvr_globals * drvr_globals);
-static void lcs_queued_restartreadio(lcs_drvr_globals * drvr_globals);
-static void lcs_queued_restartwriteio(lcs_drvr_globals * drvr_globals);
-static void lcs_resume_writetask(lcs_write_globals * write);
-#ifdef MODULE
-void cleanup_module(void);
-#endif
-
-#ifdef CONFIG_IP_MULTICAST
-static int lcs_fix_multicast_list(lcs_drvr_globals * drvr_globals);
-#endif
-
-#define lcs_debug_initmessage char *message=NULL;
-#define lcs_debug_setmessage(string) message=string
-
-#if LCS_DEBUG
-#if LCS_PRINTK_DEBUG
-#define lcs_debug_event(level,args...) printk(##args)
-#define lcs_debug_exception(level,args...) printk(##args)
-#define lcs_bad_news(args...) printk(KERN_CRIT ##args);
-#define lcs_good_news(args...) printk(##args);
-#else
-static debug_info_t *lcs_id = NULL;
-#define lcs_debug_event(args...) debug_sprintf_event(lcs_id,##args)
-#define lcs_debug_exception(args...) debug_sprintf_exception(lcs_id,##args)
-
-/* &formatstr[3] is to get over the KERN_CRIT prefix */
-#define lcs_bad_news(format,args...) \
-{ \
- char *formatstr= KERN_CRIT format; \
- lcs_debug_exception(0,&formatstr[3] ,## args); \
- printk(formatstr ,## args); \
+static void
+lcs_unregister_debug_facility(void)
+{
+ if (lcs_dbf_setup)
+ debug_unregister(lcs_dbf_setup);
+ if (lcs_dbf_trace)
+ debug_unregister(lcs_dbf_trace);
}
-#define lcs_good_news(format,args...) \
-{ \
- char *formatstr=format; \
- lcs_debug_event(0,formatstr ,## args); \
- printk(formatstr ,## args); \
+static int
+lcs_register_debug_facility(void)
+{
+ lcs_dbf_setup = debug_register("lcs_setup", 1, 1, 8);
+ lcs_dbf_trace = debug_register("lcs_trace", 1, 2, 8);
+ if (lcs_dbf_setup == NULL || lcs_dbf_trace == NULL) {
+ PRINT_ERR("Not enough memory for debug facility.\n");
+ lcs_unregister_debug_facility();
+ return -ENOMEM;
+ }
+ debug_register_view(lcs_dbf_setup, &debug_hex_ascii_view);
+ debug_set_level(lcs_dbf_setup, 5);
+ debug_register_view(lcs_dbf_trace, &debug_hex_ascii_view);
+ debug_set_level(lcs_dbf_trace, 3);
+ return 0;
}
-#endif
-
-#define lcs_debugchannel(message,chan_globals) \
-lcs_debugchannel_func((message),(lcs_chan_globals *)(chan_globals))
-static void
-lcs_debugchannel_func(char *message, lcs_chan_globals * chan_globals)
+/**
+ * Allocate io buffers.
+ */
+static int
+lcs_alloc_channel(struct lcs_channel *channel)
{
- lcs_drvr_globals *drvr_globals;
+ int cnt;
- if (message) {
- lcs_debug_event(0, "lcs: %s\n", message);
- }
- lcs_debug_event(0, "lcs irq=%04x devno=%04x %s\n",
- (int) chan_globals->subchannel,
- (int) chan_globals->devno, (message ? message : ""));
- lcs_debug_event(0, "chan globals=%p\n", chan_globals);
- if (chan_globals) {
- drvr_globals = chan_globals->drvr_globals;
- if (drvr_globals) {
- if (drvr_globals->read !=
- (lcs_read_globals *) chan_globals
- && drvr_globals->write !=
- (lcs_write_globals *) chan_globals)
- lcs_debug_event(0,
- "drvr globals inconsistent\n");
- lcs_debug_event(0, "drvr_globals=%p:state=%d\n",
- drvr_globals, drvr_globals->state);
- lcs_debug_event(0,
- "chan globals sleeping_on_io=%d"
- " busy_state=%d lock_owner=%d "
- "lock_cnt=%d\n",
- atomic_read(&chan_globals->
- sleeping_on_io),
- chan_globals->chan_busy_state,
- chan_globals->lock_owner,
- chan_globals->lock_cnt);
- }
+ LCS_DBF_TEXT(3, setup, "ichalloc");
+ for (cnt = 0; cnt < LCS_NUM_BUFFS; cnt++) {
+ /* alloc memory fo iobuffer */
+ channel->iob[cnt].data = (void *)
+ kmalloc(LCS_IOBUFFERSIZE, GFP_DMA | GFP_KERNEL);
+ if (channel->iob[cnt].data == NULL)
+ break;
+ memset(channel->iob[cnt].data, 0, LCS_IOBUFFERSIZE);
+ channel->iob[cnt].state = BUF_STATE_EMPTY;
+ }
+ if (cnt < LCS_NUM_BUFFS) {
+ /* Not all io buffers could be allocated. */
+ LCS_DBF_TEXT(3, setup, "echalloc");
+ while (cnt-- > 0)
+ kfree(channel->iob[cnt].data);
+ return -ENOMEM;
}
- lcs_debug_exception(0, "leaving lcs_debugchannel\n");
+ return 0;
}
+/**
+ * Free io buffers.
+ */
static void
-lcs_debug_display_read_buff(char *message, lcs_std_cmd * lancmd_reply_ptr)
+lcs_free_channel(struct lcs_channel *channel)
{
- int *read_buff = (int *) lancmd_reply_ptr;
+ int cnt;
- lcs_debug_event(1, "%s %08x %08x %08x\n", message,
- read_buff[0], read_buff[1], read_buff[2]);
+ LCS_DBF_TEXT(3, setup, "ichfree");
+ for (cnt = 0; cnt < LCS_NUM_BUFFS; cnt++)
+ kfree(channel->iob[cnt].data);
}
-#else
-#define lcs_debug_event(noargs...)
-#define lcs_debug_exception(noargs...)
-#define lcs_bad_news(args...) printk(KERN_CRIT ##args)
-#define lcs_good_news(args...) printk(##args)
-#define lcs_debugchannel(message,chan_globals) \
-printk("lcs irq=%04x devno=%04x %s\n",(int)chan_globals->subchannel, \
-(int)chan_globals->devno,(message ? message:""))
-#define lcs_debug_display_read_buff(noargs...)
-#endif
-
-#if (!LCS_USE_IDALS) && (CONFIG_ARCH_S390X)
-static inline struct sk_buff *
-lcs_dev_alloc_skb(unsigned int length)
+/**
+ * LCS alloc memory for card and channels
+ */
+static struct lcs_card *
+lcs_alloc_card(void)
{
- struct sk_buff *skb;
+ struct lcs_card *card;
- skb = alloc_skb(length + 16, GFP_ATOMIC | GFP_DMA);
- if (skb)
- skb_reserve(skb, 16);
- return skb;
+ LCS_DBF_TEXT(3, setup, "alloclcs");
+ card = kmalloc(sizeof(struct lcs_card), GFP_KERNEL | GFP_DMA);
+ if (card == NULL)
+ return NULL;
+ memset(card, 0, sizeof(struct lcs_card));
+ card->lan_type = LCS_FRAME_TYPE_AUTO;
+ return card;
}
-#endif
-static void lcs_inline
-lcs_resetstate(lcs_drvr_globals * drvr_globals)
+/**
+ * LCS free memory for card and channels.
+ */
+static void
+lcs_free_card(struct lcs_card *card)
{
- drvr_globals->state = lcs_idle;
+ LCS_DBF_TEXT(2, setup, "remcard");
+ kfree(card);
}
-/*
- * Primatives used to put the process to sleep or wake it up during startup
- * or shutdown.
+/*
+ * Setup read channel.
*/
-static lcs_inline void
-lcs_initqueue_func(lcs_chan_globals * chan_globals)
+static void
+lcs_setup_read_ccws(struct lcs_card *card)
{
- atomic_set(&chan_globals->sleeping_on_io, TRUE);
- chan_globals->rc = 0;
-#if LINUX_VERSION_CODE>=KERNEL_VERSION(2,3,0)
- init_waitqueue_head(&chan_globals->wait);
-#else
- chan_globals->wait = NULL;
-#endif
-}
+ int cnt;
-#define lcs_initqueue(chan_globals) \
- lcs_initqueue_func((lcs_chan_globals *)chan_globals)
+ LCS_DBF_TEXT(2, setup, "ireadccw");
+ /* Setup read ccws. */
+ memset(card->read.ccws, 0, sizeof (struct ccw1) * (LCS_NUM_BUFFS + 1));
+ for (cnt = 0; cnt < LCS_NUM_BUFFS; cnt++) {
+ card->read.ccws[cnt].cmd_code = LCS_CCW_READ;
+ card->read.ccws[cnt].count = LCS_IOBUFFERSIZE;
+ card->read.ccws[cnt].flags =
+ CCW_FLAG_CC | CCW_FLAG_SLI | CCW_FLAG_PCI;
+ /*
+ * Note: we have allocated the buffer with GFP_DMA, so
+ * we do not need to do set_normalized_cda.
+ */
+ card->read.ccws[cnt].cda =
+ (__u32) __pa(card->read.iob[cnt].data);
+ ((struct lcs_header *)
+ card->read.iob[cnt].data)->offset = LCS_ILLEGAL_OFFSET;
+ card->read.iob[cnt].callback = lcs_get_frames_cb;
+ card->read.iob[cnt].state = BUF_STATE_READY;
+ card->read.iob[cnt].count = LCS_IOBUFFERSIZE;
+ }
+ card->read.ccws[0].flags &= ~CCW_FLAG_PCI;
+ card->read.ccws[LCS_NUM_BUFFS - 1].flags &= ~CCW_FLAG_PCI;
+ card->read.ccws[LCS_NUM_BUFFS - 1].flags |= CCW_FLAG_SUSPEND;
+ /* Last ccw is a tic (transfer in channel). */
+ card->read.ccws[LCS_NUM_BUFFS].cmd_code = LCS_CCW_TRANSFER;
+ card->read.ccws[LCS_NUM_BUFFS].cda =
+ (__u32) __pa(card->read.ccws);
+ /* Setg initial state of the read channel. */
+ card->read.state = CH_STATE_INIT;
+
+ card->read.io_idx = 0;
+ card->read.buf_idx = 0;
+}
-static void
-lcs_wakeup_func(lcs_chan_globals * chan_globals, int rc)
+static int
+lcs_setup_read(struct lcs_card *card)
{
- lcs_debug_event(2, "lcs_wakeup called sch=%04x rc=%d\n,",
- (int) chan_globals->subchannel, rc);
- atomic_set(&chan_globals->sleeping_on_io, FALSE);
- if (chan_globals->rc == 0)
- chan_globals->rc = rc;
- wake_up(&chan_globals->wait);
+ int rc;
+
+ LCS_DBF_TEXT(3, setup, "readirq");
+ /* Allocate io buffers for the read channel. */
+ rc = lcs_alloc_channel(&card->read);
+ if (rc){
+ LCS_DBF_TEXT(3, setup, "iccwerr");
+ return rc;
+ }
+ lcs_setup_read_ccws(card);
+ /* Initialize read channel tasklet. */
+ card->read.irq_tasklet.data = (unsigned long) &card->read;
+ card->read.irq_tasklet.func = lcs_tasklet;
+ /* Initialize waitqueue. */
+ init_waitqueue_head(&card->read.wait_q);
+ return 0;
}
-#define TOD_SHIFT_TO_USECS 12
-static inline uint64_t
-lcs_get_tod(void)
+/*
+ * Setup write channel.
+ */
+static void
+lcs_setup_write_ccws(struct lcs_card *card)
{
- uint64_t cc;
+ int cnt;
+
+ LCS_DBF_TEXT(2, setup, "iwritccw");
+ /* Setup write ccws. */
+ memset(card->write.ccws, 0, sizeof(struct ccw1) * LCS_NUM_BUFFS + 1);
+ for (cnt = 0; cnt < LCS_NUM_BUFFS; cnt++) {
+ card->write.ccws[cnt].cmd_code = LCS_CCW_WRITE;
+ card->write.ccws[cnt].count = 0;
+ card->write.ccws[cnt].flags =
+ CCW_FLAG_SUSPEND | CCW_FLAG_CC | CCW_FLAG_SLI;
+ /*
+ * Note: we have allocated the buffer with GFP_DMA, so
+ * we do not need to do set_normalized_cda.
+ */
+ card->write.ccws[cnt].cda =
+ (__u32) __pa(card->write.iob[cnt].data);
+ }
+ /* Last ccw is a tic (transfer in channel). */
+ card->write.ccws[LCS_NUM_BUFFS].cmd_code = LCS_CCW_TRANSFER;
+ card->write.ccws[LCS_NUM_BUFFS].cda =
+ (__u32) __pa(card->write.ccws);
+ /* Set initial state of the write channel. */
+ card->read.state = CH_STATE_INIT;
- asm volatile ("STCK %0":"=m" (cc));
- return (cc);
+ card->write.io_idx = 0;
+ card->write.buf_idx = 0;
}
-#define lcs_wakeup(chan_globals,rc) \
- lcs_wakeup_func((lcs_chan_globals *)chan_globals,(rc))
static int
-lcs_sleepon_func(lcs_chan_globals * chan_globals, int extratime)
+lcs_setup_write(struct lcs_card *card)
{
- /*
- We have a miniscule chance of sleeping here for 1 second
- we have to unfortunately live with it as down_interruptible
- dosen't wake up for timers just signals.
- */
- int rc = 0;
-
- lcs_debug_event(2, "lcs_sleepon called sch=%04x\n,",
- (int) chan_globals->subchannel);
-#warning FIXME: [kj] Using sleep_on derivative, is racy. consider using wait_event instead
- sleep_on_timeout(&chan_globals->wait, HZ);
- if (atomic_read(&chan_globals->sleeping_on_io) && chan_globals->rc == 0
- && extratime) {
- lcs_bad_news
- ("lcs_sleepon network card taking time responding "
- "irq=%04x devno=%04x,\n"
- "please be patient, ctrl-c will exit if shell prompt "
- "is available.\n",
- (int) chan_globals->subchannel, (int) chan_globals->devno);
-#warning FIXME: [kj] Using sleep_on derivative, is racy. consider using wait_event instead
- interruptible_sleep_on_timeout(&chan_globals->wait,
- extratime * HZ);
- }
- if (atomic_read(&chan_globals->sleeping_on_io) && chan_globals->rc == 0) {
- chan_globals->drvr_globals->state = lcs_interrupted;
- rc = (test_thread_flag(TIF_SIGPENDING) ? -EINTR : -ETIMEDOUT);
+ int rc;
+ LCS_DBF_TEXT(3, setup, "writeirq");
+ /* Allocate io buffers for the write channel. */
+ rc = lcs_alloc_channel(&card->write);
+ if (rc) {
+ LCS_DBF_TEXT(3, setup, "iccwerr");
+ return rc;
}
- if (chan_globals->rc)
- rc = chan_globals->rc;
- lcs_debug_event(2, "lcs_sleepon sch=%04x devno=%04x rc=%d\n",
- (int) chan_globals->subchannel,
- (int) chan_globals->devno, rc);
- return (rc);
-
+ lcs_setup_write_ccws(card);
+ /* Initialize write channel tasklet. */
+ card->write.irq_tasklet.data = (unsigned long) &card->write;
+ card->write.irq_tasklet.func = lcs_tasklet;
+ /* Initialize waitqueue. */
+ init_waitqueue_head(&card->write.wait_q);
+ return 0;
}
-#define lcs_sleepon(chan_globals,extratime) \
- lcs_sleepon_func((lcs_chan_globals *)chan_globals,extratime)
-
+/*
+ * Cleanup channel.
+ */
static void
-lcs_chan_lock_func(lcs_chan_globals * chan_globals)
+lcs_cleanup_channel(struct lcs_channel *channel)
{
- eieio();
- if (chan_globals->lock_owner != smp_processor_id()) {
- s390irq_spin_lock_irqsave(chan_globals->subchannel,
- chan_globals->flags);
- chan_globals->lock_cnt = 1;
- chan_globals->lock_owner = smp_processor_id();
+ LCS_DBF_TEXT(3, setup, "cleanch");
+ /* Kill write channel tasklets. */
+ tasklet_kill(&channel->irq_tasklet);
+ /* Free channel buffers. */
+ lcs_free_channel(channel);
+}
- } else
- chan_globals->lock_cnt++;
- if (chan_globals->lock_cnt < 0 || chan_globals->lock_cnt > 100) {
- lcs_bad_news("bad lock_cnt %d in lcs_chan_lock chan_globals=%p"
- " devno=%04x returnaddrs=%p,%p cpu=%d\n",
- chan_globals->lock_cnt, chan_globals,
- (int) chan_globals->devno,
- __builtin_return_address(0),
- __builtin_return_address(1), smp_processor_id());
- chan_globals->lock_cnt = 1;
+/**
+ * Initialize channels,card and state machines.
+ */
+static int
+lcs_setup_card(struct lcs_card *card)
+{
+ int rc;
+
+ LCS_DBF_TEXT(3, setup, "initcard");
+
+ rc = lcs_setup_read(card);
+ if (rc) {
+ PRINT_ERR("Could not initialize read channel\n");
+ return rc;
+ }
+ rc = lcs_setup_write(card);
+ if (rc) {
+ PRINT_ERR("Could not initialize write channel\n");
+ lcs_cleanup_channel(&card->read);
+ return rc;
}
+ /* Set cards initial state. */
+ card->state = DEV_STATE_DOWN;
+ card->tx_buffer = NULL;
+ card->tx_emitted = 0;
+
+ /* Initialize kernel thread task used for LGW commands. */
+ INIT_WORK(&card->kernel_thread_starter,
+ (void *)lcs_start_kernel_thread,card);
+ card->thread_mask = 0;
+ spin_lock_init(&card->lock);
+ INIT_LIST_HEAD(&card->ipm_list);
+ INIT_LIST_HEAD(&card->lancmd_waiters);
+ return 0;
}
+/**
+ * Cleanup channels,card and state machines.
+ */
static void
-lcs_chan_unlock_func(lcs_chan_globals * chan_globals)
+lcs_cleanup_card(struct lcs_card *card)
{
- if (chan_globals->lock_cnt <= 0) {
- lcs_bad_news
- ("bad lock_cnt %d in lcs_chan_unlock chan_globals=%p"
- " devno=%04x returnaddrs=%p,%p cpu=%d\n",
- chan_globals->lock_cnt, chan_globals,
- (int) chan_globals->devno, __builtin_return_address(0),
- __builtin_return_address(1), smp_processor_id());
- chan_globals->lock_cnt = 1;
- }
+ struct list_head *l, *n;
+ struct lcs_ipm_list *ipm_list;
- if (--chan_globals->lock_cnt == 0) {
- chan_globals->lock_owner = LCS_INVALID_LOCK_OWNER;
- s390irq_spin_unlock_irqrestore(chan_globals->subchannel,
- chan_globals->flags);
+ LCS_DBF_TEXT(3, setup, "cleancrd");
+#ifdef CONFIG_IP_MULTICAST
+ /* Free multicast list. */
+ list_for_each_safe(l, n, &card->ipm_list) {
+ ipm_list = list_entry(l, struct lcs_ipm_list, list);
+ list_del(&ipm_list->list);
+ kfree(ipm_list);
}
-
+#endif
+ /* Cleanup channels. */
+ lcs_cleanup_channel(&card->write);
+ lcs_cleanup_channel(&card->read);
}
-#define lcs_chan_lock(chan_globals) \
- lcs_chan_lock_func((lcs_chan_globals *)chan_globals)
-#define lcs_chan_unlock(chan_globals) \
- lcs_chan_unlock_func((lcs_chan_globals *)chan_globals)
-
-static int inline
-lcs_getbuffs_filled(lcs_write_globals * write)
+/**
+ * Start channel.
+ */
+static int
+lcs_start_channel(struct lcs_channel *channel)
{
- return ((write->prepare_idx - write->tx_idx) +
- (write->prepare_idx >
- write->tx_idx ? -1 : (LCS_NUM_TX_BUFFS - 1))
- + (write->ccw[write->prepare_idx].count ? 1 : 0));
+ char dbf_text[15];
+ unsigned long flags;
+ int rc;
+
+ sprintf(dbf_text,"ssch%s", channel->ccwdev->dev.bus_id);
+ LCS_DBF_TEXT(4, trace, dbf_text);
+ spin_lock_irqsave(get_ccwdev_lock(channel->ccwdev), flags);
+ rc = ccw_device_start(channel->ccwdev,
+ channel->ccws + channel->io_idx, 0, 0,
+ DOIO_DENY_PREFETCH | DOIO_ALLOW_SUSPEND);
+ if (rc == 0)
+ channel->state = CH_STATE_RUNNING;
+ spin_unlock_irqrestore(get_ccwdev_lock(channel->ccwdev), flags);
+ if (rc) {
+ sprintf(dbf_text,"essc%s", channel->ccwdev->dev.bus_id);
+ LCS_DBF_TEXT(4, trace, dbf_text);
+ PRINT_ERR("Error in starting channel, rc=%d!\n", rc);
+ }
+ return rc;
}
-/*
- * A little utility routine to make a ccw chain.
+/**
+ * Stop channel.
*/
-static void
-initccws(ccw1_t * ccws, u8 * iobuff[], int num_ccws, u8 cmd_code, u16 count,
- u8 flags)
+static int
+lcs_stop_channel(struct lcs_channel *channel)
{
- int cnt;
- ccw1_t *origccws = ccws;
- memset(ccws, 0, sizeof (ccw1_t) * num_ccws + 1);
- for (cnt = 0; cnt < num_ccws; cnt++, ccws++) {
- ccws->cmd_code = cmd_code;
- ccws->count = count;
- ccws->flags = flags;
- /* ccw addresses are 32 bit */
- (u32) ccws->cda = (u32) virt_to_phys(iobuff[cnt]);
+ char dbf_text[15];
+ unsigned long flags;
+ int rc;
+
+ if (channel->state == CH_STATE_STOPPED)
+ return 0;
+ sprintf(dbf_text,"hsch%s", channel->ccwdev->dev.bus_id);
+ LCS_DBF_TEXT(4, trace, dbf_text);
+ spin_lock_irqsave(get_ccwdev_lock(channel->ccwdev), flags);
+ rc = ccw_device_halt(channel->ccwdev, (addr_t) channel);
+ spin_unlock_irqrestore(get_ccwdev_lock(channel->ccwdev), flags);
+ if (rc) {
+ sprintf(dbf_text,"ehsc%s", channel->ccwdev->dev.bus_id);
+ LCS_DBF_TEXT(4, trace, dbf_text);
+ return rc;
}
- ccws->cmd_code = ccw_transfer_in_channel1;
- (u32) ccws->cda = (u32) virt_to_phys(origccws);
+ /* Asynchronous halt initialted. Wait for its completion. */
+ wait_event(channel->wait_q, (channel->state == CH_STATE_HALTED));
+ return 0;
}
-/*
- * linked list used to hold all the lcs_drvr_globals so they can be
- * freed up when the driver is finished
- */
-static spinlock_t lcs_card_list_lock = SPIN_LOCK_UNLOCKED;
-static lcs_drvr_globals *lcs_card_list = NULL;
-
-/*
- This function is required for kernel threads & timers
- going off after the driver is closed & long gone & causing mischef.
- ( thanks Ingo for making me aware that this was happening :-) )
+/**
+ * start read and write channel
*/
static int
-lcs_drvr_globals_valid(lcs_drvr_globals * drvr_globals)
+lcs_start_channels(struct lcs_card *card)
{
- int in_list = FALSE;
-
- if (drvr_globals) {
- spin_lock(&lcs_card_list_lock);
- in_list =
- is_in_list((list *) lcs_card_list, (list *) drvr_globals);
- if (in_list)
- atomic_inc(&drvr_globals->usage_cnt);
- spin_unlock(&lcs_card_list_lock);
- }
- return (in_list);
+ int rc;
+
+ LCS_DBF_TEXT(2, trace, "chstart");
+ /* start read channel */
+ rc = lcs_start_channel(&card->read);
+ if (rc)
+ return rc;
+ /* start write channel */
+ rc = lcs_start_channel(&card->write);
+ if (rc)
+ lcs_stop_channel(&card->read);
+ return rc;
}
-static void *
-lcs_dma_alloc(size_t allocsize)
+/**
+ * stop read and write channel
+ */
+static int
+lcs_stop_channels(struct lcs_card *card)
{
- void *newalloc = kmalloc(allocsize, GFP_KERNEL | GFP_DMA);
- if (newalloc)
- memset(newalloc, 0, allocsize);
- return (newalloc);
+ LCS_DBF_TEXT(2, trace, "chhalt");
+ lcs_stop_channel(&card->read);
+ lcs_stop_channel(&card->write);
+ return 0;
}
-static void
-lcs_usage_free_drvr_globals(lcs_drvr_globals * drvr_globals)
+/**
+ * Get empty buffer.
+ */
+static struct lcs_buffer *
+__lcs_get_buffer(struct lcs_channel *channel)
{
-#if LCS_ZERO_COPY_READ
- struct sk_buff **skb_buff;
-#endif
- u8 **iobuff;
- int cnt, refcnt;
-
- lcs_debug_event(2, "lcs_usage_free_drvr_globals_called %p\n",
- drvr_globals);
- if (drvr_globals) {
- lcs_read_globals *read = drvr_globals->read;
- refcnt = atomic_dec_return(&drvr_globals->usage_cnt);
- if (refcnt < 0)
- lcs_bad_news("bad ref cnt drvr_globals %p ref %d\n",
- drvr_globals, refcnt);
- if (refcnt <= 0) {
- spin_lock(&lcs_card_list_lock);
- if (!remove_from_list
- ((list **) & lcs_card_list, (list *) drvr_globals))
- lcs_bad_news("drvr globals not in list %p\n",
- drvr_globals);
- spin_unlock(&lcs_card_list_lock);
- lcs_debug_event(2, "freeing drvr globals\n");
- iobuff = drvr_globals->write->iobuff;
- for (cnt = 0; cnt < LCS_NUM_TX_BUFFS; cnt++) {
- if (iobuff[cnt])
- kfree(iobuff[cnt]);
- }
-#if LCS_ZERO_COPY_READ
- skb_buff = read->skb_buff;
- for (cnt = 0; cnt < LCS_NUM_RX_BUFFS; cnt++) {
- clear_normalized_cda(&read->ccw[cnt]);
- if (skb_buff[cnt])
- dev_kfree_skb(skb_buff[cnt]);
- }
-#else
- iobuff = read->iobuff;
- for (cnt = 0; cnt < LCS_NUM_RX_BUFFS; cnt++) {
- clear_normalized_cda(&read->ccw[cnt]);
- if (iobuff[cnt])
- kfree(iobuff[cnt]);
- }
-#endif
- if (drvr_globals->dev)
- drvr_globals->dev->priv = NULL;
-#ifdef CONFIG_IP_MULTICAST
- while (drvr_globals->ipm_list) {
- spin_lock(&drvr_globals->ipm_lock);
- remove_from_list((list **) & drvr_globals->
- ipm_list,
- (list *) drvr_globals->
- ipm_list);
- spin_unlock(&drvr_globals->ipm_lock);
- kfree(drvr_globals->ipm_list);
- }
-#endif
- memset(drvr_globals, 0, LCS_ALLOCSIZE);
- kfree(drvr_globals);
+ int index;
+
+ index = channel->io_idx;
+ do {
+ if (channel->iob[index].state == BUF_STATE_EMPTY) {
+ channel->iob[index].state = BUF_STATE_LOCKED;
+ return channel->iob + index;
}
- }
+ index = (index + 1) & (LCS_NUM_BUFFS - 1);
+ } while (index != channel->io_idx);
+ return NULL;
}
-static void
-lcs_kernel_thread(lcs_drvr_globals * drvr_globals)
+static struct lcs_buffer *
+lcs_get_buffer(struct lcs_channel *channel)
{
-#if LINUX_VERSION_CODE<=KERNEL_VERSION(2,2,16)
- /* tq_scheduler sometimes leaves interrupts disabled from do
- * bottom half */
- local_irq_enable();
-#endif
- if (kernel_thread((int (*)(void *)) drvr_globals->kernel_thread_routine,
- (void *) drvr_globals, SIGCHLD) < 0) {
- atomic_set(&drvr_globals->kernel_thread_lock, 1);
- lcs_debug_exception(1,
- "lcs_kernel_thread failed to launch a new "
- "thread drvr_globals=%p routine=%p",
- drvr_globals,
- drvr_globals->kernel_thread_routine);
- }
+ struct lcs_buffer *buffer;
+ unsigned long flags;
+
+ spin_lock_irqsave(get_ccwdev_lock(channel->ccwdev), flags);
+ buffer = __lcs_get_buffer(channel);
+ spin_unlock_irqrestore(get_ccwdev_lock(channel->ccwdev), flags);
+ return buffer;
}
+/**
+ * Resume channel program if the channel is suspended.
+ */
static int
-lcs_initreadccws(lcs_read_globals * read)
+__lcs_resume_channel(struct lcs_channel *channel)
{
+ char dbf_text[15];
+ int rc;
- int cnt;
- ccw1_t *origccws, *ccws;
-#if LCS_ZERO_COPY_READ
- struct sk_buff *curr_skb;
-#else
- u8 *curr_iobuff;
-#endif
+ if (channel->state != CH_STATE_SUSPENDED)
+ return 0;
+ if (channel->ccws[channel->io_idx].flags & CCW_FLAG_SUSPEND)
+ return 0;
+ sprintf(dbf_text,"rsch%s", channel->ccwdev->dev.bus_id);
+ LCS_DBF_TEXT(4, trace, dbf_text);
+ rc = ccw_device_resume(channel->ccwdev);
+ if (rc) {
+ sprintf(dbf_text,"ersc%s", channel->ccwdev->dev.bus_id);
+ LCS_DBF_TEXT(4, trace, dbf_text);
+ PRINT_ERR("Error in lcs_resume_channel: rc=%d\n",rc);
+ } else
+ channel->state = CH_STATE_RUNNING;
+ return rc;
- origccws = ccws = read->ccw;
- memset(ccws, 0, sizeof (ccw1_t) * LCS_NUM_RX_BUFFS + 1);
- for (cnt = 0; cnt < LCS_NUM_RX_BUFFS; cnt++, ccws++) {
- ccws->cmd_code = ccw_read;
-#if LCS_ZERO_COPY_READ
- curr_skb = read->skb_buff[cnt];
- if (set_normalized_cda(ccws, curr_skb->data))
- return -ENOMEM
-#else
- curr_iobuff = read->iobuff[cnt];
- if (set_normalized_cda(ccws, curr_iobuff))
- return -ENOMEM;
-#endif
+}
+
+/**
+ * Make a buffer ready for processing.
+ */
+static inline void
+__lcs_ready_buffer_bits(struct lcs_channel *channel, int index)
+{
+ int prev, next;
+
+ prev = (index - 1) & (LCS_NUM_BUFFS - 1);
+ next = (index + 1) & (LCS_NUM_BUFFS - 1);
+ /* Check if we may clear the suspend bit of this buffer. */
+ if (channel->ccws[next].flags & CCW_FLAG_SUSPEND) {
+ /* Check if we have to set the PCI bit. */
+ if (!(channel->ccws[prev].flags & CCW_FLAG_SUSPEND))
+ /* Suspend bit of the previous buffer is not set. */
+ channel->ccws[index].flags |= CCW_FLAG_PCI;
+ /* Suspend bit of the next buffer is set. */
+ channel->ccws[index].flags &= ~CCW_FLAG_SUSPEND;
}
- ccws->cmd_code = ccw_transfer_in_channel1;
- (u32) ccws->cda = (u32) virt_to_phys(origccws);
- return 0;
+}
+
+static int
+lcs_ready_buffer(struct lcs_channel *channel, struct lcs_buffer *buffer)
+{
+ unsigned long flags;
+ int index, rc;
+
+ if (buffer->state != BUF_STATE_LOCKED &&
+ buffer->state != BUF_STATE_PROCESSED)
+ BUG();
+ spin_lock_irqsave(get_ccwdev_lock(channel->ccwdev), flags);
+ buffer->state = BUF_STATE_READY;
+ index = buffer - channel->iob;
+ /* Set length. */
+ channel->ccws[index].count = buffer->count;
+ /* Check relevant PCI/suspend bits. */
+ __lcs_ready_buffer_bits(channel, index);
+ rc = __lcs_resume_channel(channel);
+ spin_unlock_irqrestore(get_ccwdev_lock(channel->ccwdev), flags);
+ return rc;
+}
+/**
+ * Mark the buffer as processed. Take care of the suspend bit
+ * of the previous buffer. This function is called from
+ * interrupt context, so the lock must not be taken.
+ */
+static int
+__lcs_processed_buffer(struct lcs_channel *channel, struct lcs_buffer *buffer)
+{
+ int index, prevprev, prev, next;
+
+ if (buffer->state != BUF_STATE_READY)
+ BUG();
+ buffer->state = BUF_STATE_PROCESSED;
+ index = buffer - channel->iob;
+ prevprev = (index - 1) & (LCS_NUM_BUFFS - 1);
+ prev = (index - 1) & (LCS_NUM_BUFFS - 1);
+ next = (index + 1) & (LCS_NUM_BUFFS - 1);
+ /* Set the suspend bit and clear the PCI bit of this buffer. */
+ channel->ccws[index].flags |= CCW_FLAG_SUSPEND;
+ channel->ccws[index].flags &= ~CCW_FLAG_PCI;
+ /* Check the suspend bit of the previous buffer. */
+ if (channel->iob[prev].state == BUF_STATE_READY) {
+ /*
+ * Previous buffer is in state ready. It might have
+ * happened in lcs_ready_buffer that the suspend bit
+ * has not been cleared to avoid an endless loop.
+ * Do it now.
+ */
+ __lcs_ready_buffer_bits(channel, prev);
+ }
+ /* Clear PCI bit of next buffer. */
+ channel->ccws[next].flags &= ~CCW_FLAG_PCI;
+ return __lcs_resume_channel(channel);
}
-/*
- * Allocates driver instance globals
+/**
+ * Put a processed buffer back to state empty.
*/
-static lcs_drvr_globals *
-lcs_alloc_drvr_globals(void)
+static void
+lcs_release_buffer(struct lcs_channel *channel, struct lcs_buffer *buffer)
{
- lcs_drvr_globals *drvr_globals;
- lcs_write_globals *write;
- lcs_read_globals *read;
- int cnt;
-#if LINUX_VERSION_CODE<KERNEL_VERSION(2,5,41)
- struct tq_struct *lcs_tq;
-#endif
+ unsigned long flags;
- lcs_debug_event(2, "lcs_alloc_drvr_globals called\n");
- drvr_globals = lcs_dma_alloc(LCS_ALLOCSIZE);
- if (!drvr_globals)
- return (NULL);
- atomic_set(&drvr_globals->usage_cnt, 1);
- drvr_globals->rel_adapter_no = lcs_invalid_adapter_no;
- read = drvr_globals->read = (lcs_read_globals *) (drvr_globals + 1);
- write = drvr_globals->write =
- (lcs_write_globals *) ((addr_t) read + sizeof (lcs_read_globals));
- write->lock_owner = drvr_globals->read->lock_owner =
- LCS_INVALID_LOCK_OWNER;
- for (cnt = 0; cnt < LCS_NUM_TX_BUFFS; cnt++) {
- if ((write->iobuff[cnt] =
- lcs_dma_alloc(LCS_IOBUFFSIZE)) == NULL)
- goto Fail;
+ if (buffer->state != BUF_STATE_LOCKED &&
+ buffer->state != BUF_STATE_PROCESSED)
+ BUG();
+ spin_lock_irqsave(get_ccwdev_lock(channel->ccwdev), flags);
+ buffer->state = BUF_STATE_EMPTY;
+ spin_unlock_irqrestore(get_ccwdev_lock(channel->ccwdev), flags);
+}
+/**
+ * Get buffer for a lan command.
+ */
+static struct lcs_buffer *
+lcs_get_lancmd(struct lcs_card *card, int count)
+{
+ struct lcs_buffer *buffer;
+ struct lcs_cmd *cmd;
+
+ /* Get buffer and wait if none is available. */
+ wait_event(card->write.wait_q,
+ ((buffer = lcs_get_buffer(&card->write)) != NULL));
+ count += sizeof(struct lcs_header);
+ *(__u16 *)(buffer->data + count) = 0;
+ buffer->count = count + sizeof(__u16);
+ buffer->callback = lcs_release_buffer;
+ cmd = (struct lcs_cmd *) buffer->data;
+ cmd->offset = count;
+ cmd->type = LCS_FRAME_TYPE_CONTROL;
+ cmd->slot = 0;
+ return buffer;
+}
+
+/**
+ * Notifier function for lancmd replies. Called from read irq.
+ */
+static void
+lcs_notify_lancmd_waiters(struct lcs_card *card, struct lcs_cmd *cmd)
+{
+ struct list_head *l, *n;
+ struct lcs_reply *reply;
+
+ spin_lock(&card->lock);
+ list_for_each_safe(l, n, &card->lancmd_waiters) {
+ reply = list_entry(l, struct lcs_reply, list);
+ if (reply->sequence_no == cmd->sequence_no) {
+ list_del(&reply->list);
+ if (reply->callback != NULL)
+ reply->callback(card, cmd);
+ reply->received = 1;
+ reply->rc = cmd->return_code;
+ wake_up(&reply->wait_q);
+ break;
+ }
}
- for (cnt = 0; cnt < LCS_NUM_RX_BUFFS; cnt++) {
-#if LCS_ZERO_COPY_READ
- if ((read->skb_buff[cnt] =
- lcs_dev_alloc_skb(LCS_READ_ALLOCSIZE)) == NULL)
-#else
- if ((read->iobuff[cnt] =
- kmalloc(LCS_READ_ALLOCSIZE, GFP_ATOMIC | GFP_DMA)) == NULL)
-#endif
- goto Fail;
- }
- if (lcs_initreadccws(read))
- goto Fail;
- read->drvr_globals = drvr_globals;
- write->drvr_globals = drvr_globals;
- lcs_resetstate(drvr_globals);
-#if LINUX_VERSION_CODE>=KERNEL_VERSION(2,5,41)
- INIT_WORK(&drvr_globals->kernel_thread_task, lcs_kernel_thread,
- (unsigned long) drvr_globals);
- INIT_WORK(&read->retry_task, lcs_queued_restartreadio,
- (unsigned long) drvr_globals);
- INIT_WORK(&write->retry_task, lcs_queued_restartwriteio,
- (unsigned long) drvr_globals);
- INIT_WORK(&resume_task, lcs_resume_writetask, (unsigned long) write);
-#else
- lcs_tq = &drvr_globals->kernel_thread_task;
- lcs_tq->routine = (void (*)(void *)) lcs_kernel_thread;
- lcs_tq->data = (void *) drvr_globals;
-#if LINUX_VERSION_CODE>KERNEL_VERSION(2,3,0)
- lcs_tq->sync = read->retry_task.sync = write->retry_task.sync = 0;
-#endif
-#if LINUX_VERSION_CODE>=KERNEL_VERSION(2,3,0)
- INIT_LIST_HEAD(&lcs_tq->list);
- INIT_LIST_HEAD(&read->retry_task.list);
- INIT_LIST_HEAD(&write->retry_task.list);
-#endif
- read->retry_task.routine = (void (*)(void *)) lcs_queued_restartreadio;
- write->retry_task.routine =
- (void (*)(void *)) lcs_queued_restartwriteio;
- write->retry_task.data = read->retry_task.data = (void *) drvr_globals;
- write->resume_task.routine = (void (*)(void *)) lcs_resume_writetask;
- write->resume_task.data = (void *) write;
-#endif
- read->chan_busy_state = write->chan_busy_state = chan_dead;
- atomic_set(&drvr_globals->kernel_thread_lock, 1);
- spin_lock(&lcs_card_list_lock);
- add_to_list((list **) & lcs_card_list, (list *) drvr_globals);
- spin_unlock(&lcs_card_list_lock);
- drvr_globals->up = FALSE;
- return (drvr_globals);
-Fail:
- lcs_usage_free_drvr_globals(drvr_globals);
- return (NULL);
+ spin_unlock(&card->lock);
}
-static void
-lcs_retry(lcs_chan_globals * chan_globals)
+/**
+ * Emit buffer of a lan comand.
+ */
+void
+lcs_lancmd_timeout(unsigned long data)
{
- lcs_debug_event(1, "lcs_retry subchannel %04x scheduling retry\n",
- chan_globals->subchannel);
- MOD_INC_USE_COUNT;
-#if LINUX_VERSION_CODE>=KERNEL_VERSION(2,5,41)
- schedule_work(&chan_globals->retry_task);
-#else
-#if LINUX_VERSION_CODE>KERNEL_VERSION(2,2,16)
- schedule_task(&chan_globals->retry_task);
-#else
- queue_task(&chan_globals->retry_task, &tq_scheduler);
-#endif
-#endif
+ struct lcs_reply *reply;
+
+ reply = (struct lcs_reply *) data;
+ list_del(&reply->list);
+ reply->received = 1;
+ reply->rc = -ETIME;
+ wake_up(&reply->wait_q);
}
static int
-lcs_doio_func(lcs_chan_globals * chan_globals, unsigned long flags)
+lcs_send_lancmd(struct lcs_card *card, struct lcs_buffer *buffer,
+ void (*reply_callback)(struct lcs_card *, struct lcs_cmd *))
{
+ struct lcs_reply reply;
+ struct lcs_cmd *cmd;
+ struct timer_list timer;
int rc;
- lcs_debug_event(2, "lcs_doio subchannel=%x\n",
- (int) chan_globals->subchannel);
- lcs_chan_lock(chan_globals);
- rc = do_IO(chan_globals->subchannel, &chan_globals->ccw[0],
- (addr_t) chan_globals, 0, flags);
+ cmd = (struct lcs_cmd *) buffer->data;
+ cmd->sequence_no = ++card->sequence_no;
+ cmd->return_code = 0;
+ reply.sequence_no = cmd->sequence_no;
+ reply.callback = reply_callback;
+ reply.received = 0;
+ reply.rc = 0;
+ init_waitqueue_head(&reply.wait_q);
+ spin_lock(&card->lock);
+ list_add_tail(&reply.list, &card->lancmd_waiters);
+ spin_unlock(&card->lock);
+ buffer->callback = lcs_release_buffer;
+ rc = lcs_ready_buffer(&card->write, buffer);
+ if (rc)
+ return rc;
+ init_timer(&timer);
+ timer.function = lcs_lancmd_timeout;
+ timer.data = (unsigned long) &reply;
+ timer.expires = jiffies + HZ*5;
+ add_timer(&timer);
+ wait_event(reply.wait_q, reply.received);
+ del_timer(&timer);
+ return reply.rc ? -EIO : 0;
+}
+
+/**
+ * LCS startup command
+ */
+static int
+lcs_send_startup(struct lcs_card *card, __u8 initiator)
+{
+ struct lcs_buffer *buffer;
+ struct lcs_cmd *cmd;
- if (rc) {
-#if LCS_DEBUG
- lcs_debug_event(1,"lcs_doio do_IO returned %d for " \
- "subchannel %X\n",rc,chan_globals->subchannel);
-#endif
- if (rc == -ENODEV)
- chan_globals->chan_busy_state = chan_dead;
- else
- lcs_retry(chan_globals);
- } else
- chan_globals->chan_busy_state = chan_started_successfully;
- lcs_chan_unlock(chan_globals);
- return (rc);
+ LCS_DBF_TEXT(2, trace, "startup");
+ buffer = lcs_get_lancmd(card, LCS_STD_CMD_SIZE);
+ cmd = (struct lcs_cmd *) buffer->data;
+ cmd->cmd_code = LCS_CMD_STARTUP;
+ cmd->initiator = initiator;
+ cmd->cmd.lcs_startup.buff_size = LCS_IOBUFFERSIZE;
+ return lcs_send_lancmd(card, buffer, NULL);
}
-#define lcs_doio(chan_globals,flags) \
- lcs_doio_func((lcs_chan_globals *)(chan_globals),(flags))
-
-/*
- * halt subchannel wrapper used during driver startup
+/**
+ * LCS shutdown command
*/
static int
-lcs_hsch_func(lcs_chan_globals * chan_globals)
+lcs_send_shutdown(struct lcs_card *card)
{
- int rc = 0;
- lcs_debug_event(2, "lcs_hsch subchannel=%x\n",
- (int) chan_globals->subchannel);
- lcs_chan_lock(chan_globals);
- lcs_initqueue(chan_globals);
- chan_globals->drvr_globals->state = lcs_halting_subchannels;
- rc = halt_IO(chan_globals->subchannel, (addr_t) chan_globals, 0);
- lcs_chan_unlock(chan_globals);
- if (rc == 0)
- rc = lcs_sleepon_func(chan_globals, 0);
- else
- lcs_debug_event(2, "lcs_hsch subchannel=%x rc=%d\n",
- (int) chan_globals->subchannel, rc);
- return (rc);
-}
+ struct lcs_buffer *buffer;
+ struct lcs_cmd *cmd;
-#define lcs_hsch(chan_globals) \
- lcs_hsch_func((lcs_chan_globals *)(chan_globals))
+ LCS_DBF_TEXT(2, trace, "shutdown");
+ buffer = lcs_get_lancmd(card, LCS_STD_CMD_SIZE);
+ cmd = (struct lcs_cmd *) buffer->data;
+ cmd->cmd_code = LCS_CMD_SHUTDOWN;
+ cmd->initiator = LCS_INITIATOR_TCPIP;
+ return lcs_send_lancmd(card, buffer, NULL);
+}
+/**
+ * LCS lanstat command
+ */
static void
-wait_on_write(lcs_drvr_globals * drvr_globals)
+__lcs_lanstat_cb(struct lcs_card *card, struct lcs_cmd *cmd)
{
- lcs_write_globals *write = drvr_globals->write;
-
- if (drvr_globals->state == lcs_doing_io) {
- if (drvr_globals->dev)
- netif_stop_queue(drvr_globals->dev);
- lcs_chan_lock(write);
- if (drvr_globals->state == lcs_doing_io &&
- (write->chan_busy_state == chan_busy
- || write->resume_queued)) {
- lcs_initqueue(write);
- drvr_globals->state =
- lcs_requesting_channels_for_lancmds;
- lcs_chan_unlock(write);
- lcs_sleepon(write, 0);
- } else
- lcs_chan_unlock(write);
- }
- drvr_globals->state = lcs_doing_lancmds;
+ memcpy(card->mac, cmd->cmd.lcs_lanstat_cmd.mac_addr, LCS_MAC_LENGTH);
}
static int
-lcs_getcmdsize(u8 cmd_code)
+lcs_send_lanstat(struct lcs_card *card)
{
- int cmdsize;
- switch (cmd_code) {
- case lcs_startlan:
- case lcs_stoplan:
- case lcs_lanstat:
- case lcs_shutdown:
- cmdsize = sizeof (lcs_std_cmd);
- break;
- case lcs_startup:
- cmdsize = sizeof (lcs_startup_cmd);
- break;
-#ifdef CONFIG_IP_MULTICAST
- case lcs_setipm:
- case lcs_delipm:
- cmdsize = sizeof (lcs_ipm_ctlmsg);
- break;
- case lcs_qipassist:
- cmdsize = sizeof (lcs_qipassist_ctlmsg);
- break;
-#endif
- default:
- lcs_debug_event(1, "illegal lan cmd %d in lcs_getcmdsize\n",
- cmd_code);
- cmdsize = -EINVAL;
- }
- if (cmdsize != -EINVAL)
- cmdsize -= sizeof (lcs_header_type);
- return (cmdsize);
+ struct lcs_buffer *buffer;
+ struct lcs_cmd *cmd;
+
+ LCS_DBF_TEXT(2, trace, "cmdstat");
+ buffer = lcs_get_lancmd(card, LCS_STD_CMD_SIZE);
+ cmd = (struct lcs_cmd *) buffer->data;
+ /* Setup lanstat command. */
+ cmd->cmd_code = LCS_CMD_LANSTAT;
+ cmd->initiator = LCS_INITIATOR_TCPIP;
+ cmd->cmd.lcs_std_cmd.lan_type = card->lan_type;
+ cmd->cmd.lcs_std_cmd.portno = card->portno;
+ return lcs_send_lancmd(card, buffer, __lcs_lanstat_cb);
}
-static lcs_header_type *
-lcs_getfreetxbuff(lcs_write_globals * write, u8 type, u8 slot, int len)
+/**
+ * send stoplan command
+ */
+static int
+lcs_send_stoplan(struct lcs_card *card, __u8 initiator)
{
- ccw1_t *curr_ccw;
- int new_buff = FALSE;
- lcs_header_type *retbuff;
- u32 virt_cda;
-
- curr_ccw = &write->ccw[write->prepare_idx];
- if (curr_ccw->count == 0)
- new_buff = TRUE;
- else if ((curr_ccw->count + len + sizeof (lcs_header_type) +
- sizeof (u16)) > LCS_IOBUFFSIZE) {
-
- if (write->prepare_idx == write->tx_idx)
- return (NULL);
- write->prepare_idx =
- ((write->prepare_idx + 1) & (LCS_NUM_TX_BUFFS - 1));
- curr_ccw->flags &= ~CCW_FLAG_SUSPEND;
- new_buff = TRUE;
- curr_ccw = &write->ccw[write->prepare_idx];
- }
- if (new_buff) {
- if ((curr_ccw->flags & CCW_FLAG_SUSPEND) == 0) {
- lcs_bad_news
- ("Bug/Race condition detected in "
- "lcs_getfreetxbuff "
- "CCW_FLAG_SUSPEND not set for\n"
- "for ccw at %p", curr_ccw);
- return (NULL);
- }
- if (curr_ccw->count != 0) {
- lcs_bad_news
- ("Bug/Race condition detected in "
- "lcs_getfreetxbuff "
- "count!=0 in new buffer\n"
- "for ccw at %p", curr_ccw);
- return (NULL);
- }
- }
- len += sizeof (lcs_header_type);
- virt_cda = (u32) virt_to_phys((void *) ((addr_t) curr_ccw->cda));
- if (new_buff) {
- retbuff = (lcs_header_type *) ((addr_t) virt_cda);
- curr_ccw->count = len + sizeof (u16);
- } else {
- retbuff =
- (lcs_header_type *) (virt_cda + curr_ccw->count -
- sizeof (u16));
- curr_ccw->count += len;
- len = curr_ccw->count - sizeof (u16);
- }
- retbuff->offset = len;
- *((u16 *) ((addr_t) (virt_cda + len))) = 0;
- retbuff->type = type;
- retbuff->slot = slot;
- lcs_debug_event(2,
- "lcs_getfreetxbuff write=%p prepare_idx=%u "
- "tx_idx=%u len=%d\n",
- write, write->prepare_idx, write->tx_idx, len);
- return (retbuff);
+ struct lcs_buffer *buffer;
+ struct lcs_cmd *cmd;
+
+ LCS_DBF_TEXT(2, trace, "cmdstpln");
+ buffer = lcs_get_lancmd(card, LCS_STD_CMD_SIZE);
+ cmd = (struct lcs_cmd *) buffer->data;
+ cmd->cmd_code = LCS_CMD_STOPLAN;
+ cmd->initiator = initiator;
+ cmd->cmd.lcs_std_cmd.lan_type = card->lan_type;
+ cmd->cmd.lcs_std_cmd.portno = card->portno;
+ return lcs_send_lancmd(card, buffer, NULL);
}
-static lcs_std_cmd *
-lcs_prepare_lancmd(lcs_write_globals * write, u8 cmd_code, u8 rel_adapter_no,
- u8 initiator, u8 lan_type)
+/**
+ * send startlan command
+ */
+static void
+__lcs_send_startlan_cb(struct lcs_card *card, struct lcs_cmd *cmd)
{
- lcs_std_cmd *writecmd;
- int cmdsize = lcs_getcmdsize(cmd_code);
- if (cmdsize < 0)
- return (NULL);
- /* Force a fresh ccw buff */
- writecmd =
- (lcs_std_cmd *) lcs_getfreetxbuff(write, lcs_control_frame_type, 0,
- cmdsize);
- if (writecmd == NULL)
- return (NULL);
- memset((void *) (((addr_t) writecmd) + sizeof (lcs_header_type)), 0,
- cmdsize);
- writecmd->cmd_code = cmd_code;
- writecmd->initiator = initiator;
- switch (cmd_code) {
- case lcs_startlan:
- case lcs_stoplan:
- case lcs_lanstat:
-#ifdef CONFIG_IP_MULTICAST
- case lcs_setipm:
- case lcs_delipm:
- case lcs_qipassist:
-#endif
- writecmd->lan_type = lan_type;
- writecmd->rel_adapter_no = rel_adapter_no;
- break;
- case lcs_startup:
- ((lcs_startup_cmd *) writecmd)->buff_size = LCS_IOBUFFSIZE;
- break;
- case lcs_shutdown:
- break;
- default:
- break;
- }
- return (writecmd);
+ card->lan_type = cmd->cmd.lcs_std_cmd.lan_type;
+ card->portno = cmd->cmd.lcs_std_cmd.portno;
}
-#if LCS_DEBUG_RESUME
-int lcs_read_resume_cnt, lcs_write_resume_cnt;
-#endif
-
-static void
-lcs_interrupt_resume_read(lcs_read_globals * read)
+static int
+lcs_send_startlan(struct lcs_card *card, __u8 initiator)
{
- int rc;
- ccw1_t *curr_ccw;
-
- lcs_debug_event(2, "lcs_interrupt_resume_read entered\n");
- if ((read->ccw[(read->rx_idx - 1) & (LCS_NUM_RX_BUFFS - 1)].
- flags & CCW_FLAG_SUSPEND) == 0)
- lcs_bad_news
- ("lcs_interrupt_resume_read detected possible lack of "
- "suspend bug\n");
- curr_ccw = &read->ccw[read->rx_idx];
- curr_ccw->flags = CCW_FLAG_CC | CCW_FLAG_SLI | CCW_FLAG_PCI
-#if LCS_USE_IDALS
- | (curr_ccw->flags & CCW_FLAG_IDA)
-#endif
- ;
- if ((rc = resume_IO(read->subchannel)) == 0) {
-#if LCS_DEBUG_RESUME
- lcs_read_resume_cnt++;
-#endif
- read->chan_busy_state = chan_busy;
- } else
- lcs_bad_news
- ("lcs_interrupt_resume_read returned %d for "
- "subchannel %X\n",
- rc, read->subchannel);
+ struct lcs_buffer *buffer;
+ struct lcs_cmd *cmd;
+
+ LCS_DBF_TEXT(2, trace, "cmdstpln");
+ buffer = lcs_get_lancmd(card, LCS_STD_CMD_SIZE);
+ cmd = (struct lcs_cmd *) buffer->data;
+ cmd->cmd_code = LCS_CMD_STARTLAN;
+ cmd->initiator = initiator;
+ cmd->cmd.lcs_std_cmd.lan_type = card->lan_type;
+ cmd->cmd.lcs_std_cmd.portno = card->portno;
+ return lcs_send_lancmd(card, buffer, __lcs_send_startlan_cb);
}
+#ifdef CONFIG_IP_MULTICAST
+/**
+ * send setipm command (Multicast)
+ */
+static int
+lcs_send_setipm(struct lcs_card *card,struct lcs_ipm_list *ipm_list)
+{
+ struct lcs_buffer *buffer;
+ struct lcs_cmd *cmd;
+
+ LCS_DBF_TEXT(2, trace, "cmdsetim");
+ buffer = lcs_get_lancmd(card, LCS_MULTICAST_CMD_SIZE);
+ cmd = (struct lcs_cmd *) buffer->data;
+ cmd->cmd_code = LCS_CMD_SETIPM;
+ cmd->initiator = LCS_INITIATOR_TCPIP;
+ cmd->cmd.lcs_qipassist.lan_type = card->lan_type;
+ cmd->cmd.lcs_qipassist.portno = card->portno;
+ cmd->cmd.lcs_qipassist.version = 4;
+ cmd->cmd.lcs_qipassist.num_ip_pairs = 1;
+ memcpy(cmd->cmd.lcs_qipassist.lcs_ipass_ctlmsg.ip_mac_pair,
+ &ipm_list->ipm, sizeof (struct lcs_ip_mac_pair));
+ return lcs_send_lancmd(card, buffer, NULL);
+}
+
+/**
+ * send delipm command (Multicast)
+ */
+static int
+lcs_send_delipm(struct lcs_card *card,struct lcs_ipm_list *ipm_list)
+{
+ struct lcs_buffer *buffer;
+ struct lcs_cmd *cmd;
+
+ LCS_DBF_TEXT(2, trace, "cmddelim");
+ buffer = lcs_get_lancmd(card, LCS_MULTICAST_CMD_SIZE);
+ cmd = (struct lcs_cmd *) buffer->data;
+ cmd->cmd_code = LCS_CMD_DELIPM;
+ cmd->initiator = LCS_INITIATOR_TCPIP;
+ cmd->cmd.lcs_qipassist.lan_type = card->lan_type;
+ cmd->cmd.lcs_qipassist.portno = card->portno;
+ cmd->cmd.lcs_qipassist.version = 4;
+ cmd->cmd.lcs_qipassist.num_ip_pairs = 1;
+ memcpy(cmd->cmd.lcs_qipassist.lcs_ipass_ctlmsg.ip_mac_pair,
+ &ipm_list->ipm, sizeof (struct lcs_ip_mac_pair));
+ return lcs_send_lancmd(card, buffer, NULL);
+}
+
+/**
+ * check if multicast is supported by LCS
+ */
static void
-lcs_resume_read(lcs_read_globals * read)
+__lcs_check_multicast_cb(struct lcs_card *card, struct lcs_cmd *cmd)
{
- lcs_debug_event(2, "lcs_resume_read entered\n");
- lcs_chan_lock(read);
- if (read->chan_busy_state == chan_idle)
- lcs_interrupt_resume_read(read);
- read->lancmd_reply_ptr = NULL;
- lcs_chan_unlock(read);
+ card->ip_assists_supported =
+ cmd->cmd.lcs_qipassist.ip_assists_supported;
+ card->ip_assists_enabled =
+ cmd->cmd.lcs_qipassist.ip_assists_enabled;
}
static int
-lcs_resume_write(lcs_write_globals * write)
+lcs_check_multicast_support(struct lcs_card *card)
{
- int rc = 0;
-
- lcs_debug_event(2, "lcs_resume_write entered write=%p\n", write);
- lcs_chan_lock(write);
- if (write->chan_busy_state == chan_idle && lcs_getbuffs_filled(write)) {
- write->ccw[write->prepare_idx].flags &= ~CCW_FLAG_SUSPEND;
- write->prepare_idx =
- ((write->prepare_idx + 1) & (LCS_NUM_TX_BUFFS - 1));
- if ((rc = resume_IO(write->subchannel)) == 0) {
- lcs_debug_event(2,"lcs_resume_write resume_IO "
- "successful prepare_idx=%u "
- "tx_idx=%u\n",
- write->prepare_idx, write->tx_idx);
-#if LCS_DEBUG_RESUME
- lcs_write_resume_cnt++;
-#endif
- write->chan_busy_state = chan_busy;
- /* the << TOD_SHIFT_TO_USECS is to adjust
- the inequality to microseconds
- for the tod in lcs_tx_is_busy
- The extra is adjustment is
- so that the tx_is_busy will weigh
- in favour of large transmission buffers.
- */
-
- write->adjusted_last_bytes_still_being_txed =
- (uint64_t) ((write->bytes_still_being_txed +
- 500) << TOD_SHIFT_TO_USECS);
- write->last_resume_time = lcs_get_tod();
- } else
- lcs_bad_news
- ("lcs_resume_write returned %d for "
- "subchannel %X\n",rc,write->subchannel);
+ struct lcs_buffer *buffer;
+ struct lcs_cmd *cmd;
+ int rc;
- }
- lcs_chan_unlock(write);
- return rc;
+ LCS_DBF_TEXT(2, trace, "cmdqipa");
+ /* Send query ipassist. */
+ buffer = lcs_get_lancmd(card, LCS_STD_CMD_SIZE);
+ cmd = (struct lcs_cmd *) buffer->data;
+ cmd->cmd_code = LCS_CMD_QIPASSIST;
+ cmd->initiator = LCS_INITIATOR_TCPIP;
+ cmd->cmd.lcs_qipassist.lan_type = card->lan_type;
+ cmd->cmd.lcs_qipassist.portno = card->portno;
+ cmd->cmd.lcs_qipassist.version = 4;
+ cmd->cmd.lcs_qipassist.num_ip_pairs = 1;
+ rc = lcs_send_lancmd(card, buffer, __lcs_check_multicast_cb);
+ if (rc != 0) {
+ PRINT_ERR("Query IPAssist failed. Assuming unsupported!\n");
+ return -EOPNOTSUPP;
+ }
+ /* Print out supported assists: IPv6 */
+ PRINT_INFO("LCS device %s %s IPv6 support\n", card->dev->name,
+ (card->ip_assists_supported & LCS_IPASS_IPV6_SUPPORT) ?
+ "with" : "without");
+ /* Print out supported assist: Multicast */
+ PRINT_INFO("LCS device %s %s Multicast support\n", card->dev->name,
+ (card->ip_assists_supported & LCS_IPASS_MULTICAST_SUPPORT) ?
+ "with" : "without");
+ if (card->ip_assists_supported & LCS_IPASS_MULTICAST_SUPPORT)
+ return 0;
+ return -EOPNOTSUPP;
}
-/*
- Unfortunately we don't know if the lcs interface is busy
- simply by knowing we have started a transmission & not
- recieved a interrupt yet because the osa microcode simply
- copies the packet into its own internal buffers.
- I estimate whether the interface maybe busy by assuming
- an extremely optimistic 30MB/sec transmission rate ( might
- happen on a gigabit ethernet card ), i.e. if this routine
- returns true the interface is almost definitely busy.
+/**
+ * set or del multicast address on LCS card
*/
static int
-lcs_tx_is_busy(lcs_write_globals * write, uint64_t tod)
-{
- uint64_t delta_tx = tod - write->last_resume_time;
- int rc;
+lcs_fix_multicast_list(void *data)
+{
+ struct list_head *l, *n;
+ struct lcs_ipm_list *ipm;
+ struct lcs_card *card;
+
+ card = (struct lcs_card *) data;
+
+ daemonize();
+ LCS_DBF_TEXT(5, trace, "fixipm");
+ spin_lock(&card->lock);
+ list_for_each_safe(l, n, &card->ipm_list) {
+ ipm = list_entry(l, struct lcs_ipm_list, list);
+ switch (ipm->ipm_state) {
+ case LCS_IPM_STATE_SET_REQUIRED:
+ if (lcs_send_setipm(card, ipm))
+ PRINT_INFO("Adding multicast address failed."
+ "Table possibly full!\n");
+ else
+ ipm->ipm_state = LCS_IPM_STATE_ON_CARD;
+ break;
+ case LCS_IPM_STATE_DEL_REQUIRED:
+ lcs_send_delipm(card, ipm);
+ list_del(&ipm->list);
+ kfree(ipm);
+ break;
+ case LCS_IPM_STATE_ON_CARD:
+ break;
+ }
+ }
+ if (card->state == DEV_STATE_UP)
+ netif_wake_queue(card->dev);
+ spin_unlock(&card->lock);
+ return 0;
+}
- /* 30MB/sec = 30 bytes per usec */
- if (write->adjusted_last_bytes_still_being_txed < (30 * delta_tx))
- rc = FALSE;
+/**
+ * get mac address for the relevant Multicast address
+ */
+static void
+lcs_get_mac_for_ipm(__u32 ipm, char *mac, struct net_device *dev)
+{
+ if (dev->type == ARPHRD_IEEE802_TR)
+ ip_tr_mc_map(ipm, mac);
else
- rc = TRUE;
-#if 0
- lcs_debug_event(2,
- "%s being txed %08X tod %08X last resume=%08X "
- "delta_tx %08X\n",
- (rc ? "lcs busy" : "lcs not busy"),
- (unsigned) ((write->
- adjusted_last_bytes_still_being_txed >>
- TOD_SHIFT_TO_USECS)),
- (unsigned) (tod >> TOD_SHIFT_TO_USECS),
- (unsigned) (write->
- last_resume_time >> TOD_SHIFT_TO_USECS),
- (unsigned) ((delta_tx) >> TOD_SHIFT_TO_USECS));
-#endif
- return (rc);
+ ip_eth_mc_map(ipm, mac);
}
+/**
+ * function called by net device to handle multicast address relevant things
+ */
static void
-lcs_queue_write(lcs_write_globals * write)
+lcs_set_multicast_list(struct net_device *dev)
{
- /* wait on write in shutdown func should avoid this
- * needing mod usage counts */
- lcs_debug_event(2, "lcs_queue_write write=%p\n", write);
- write->resume_queued = TRUE;
-#if LINUX_VERSION_CODE>=KERNEL_VERSION(2,5,41)
- schedule_work(&write->resume_task);
-#else
- queue_task(&write->resume_task, &tq_immediate);
-#endif
- mark_bh(IMMEDIATE_BH);
+ char buf[MAX_ADDR_LEN];
+ struct list_head *l;
+ struct ip_mc_list *im4;
+ struct in_device *in4_dev;
+ struct lcs_ipm_list *ipm, *tmp;
+ struct lcs_card *card;
+
+ LCS_DBF_TEXT(5, trace, "setmulti");
+ in4_dev = in_dev_get(dev);
+ if (in4_dev == NULL)
+ return;
+ read_lock(&in4_dev->lock);
+ card = (struct lcs_card *) dev->priv;
+ spin_lock(&card->lock);
+ /* Check for multicast addresses to be removed. */
+ list_for_each(l, &card->ipm_list) {
+ ipm = list_entry(l, struct lcs_ipm_list, list);
+ for (im4 = in4_dev->mc_list; im4 != NULL; im4 = im4->next) {
+ lcs_get_mac_for_ipm(im4->multiaddr, buf, dev);
+ if (memcmp(buf, &ipm->ipm.mac_addr,
+ LCS_MAC_LENGTH) == 0 &&
+ ipm->ipm.ip_addr == im4->multiaddr)
+ break;
+ }
+ if (im4 == NULL)
+ ipm->ipm_state = LCS_IPM_STATE_DEL_REQUIRED;
+ }
+ /* Check for multicast addresses to be added. */
+ for (im4 = in4_dev->mc_list; im4; im4 = im4->next) {
+ lcs_get_mac_for_ipm(im4->multiaddr, buf, dev);
+ ipm = NULL;
+ list_for_each(l, &card->ipm_list) {
+ tmp = list_entry(l, struct lcs_ipm_list, list);
+ if (memcmp(buf, &tmp->ipm.mac_addr,
+ LCS_MAC_LENGTH) == 0 &&
+ tmp->ipm.ip_addr == im4->multiaddr) {
+ ipm = tmp;
+ break;
+ }
+ }
+ if (ipm != NULL)
+ continue; /* Address already in list. */
+ ipm = (struct lcs_ipm_list *)
+ kmalloc(sizeof(struct lcs_ipm_list), GFP_ATOMIC);
+ if (ipm == NULL) {
+ PRINT_INFO("Not enough memory to add "
+ "new multicast entry!\n");
+ break;
+ }
+ memset(ipm, 0, sizeof(struct lcs_ipm_list));
+ memcpy(&ipm->ipm.mac_addr, buf, LCS_MAC_LENGTH);
+ ipm->ipm.ip_addr = im4->multiaddr;
+ ipm->ipm_state = LCS_IPM_STATE_SET_REQUIRED;
+ list_add(&ipm->list, &card->ipm_list);
+ }
+ spin_unlock(&card->lock);
+ read_unlock(&in4_dev->lock);
+ set_bit(3, &card->thread_mask);
+ schedule_work(&card->kernel_thread_starter);
}
+#endif /* CONFIG_IP_MULTICAST */
+
+/**
+ * IRQ Handler for LCS channels
+ */
static void
-lcs_queue_write_if_necessary(lcs_write_globals * write)
+lcs_irq(struct ccw_device *cdev, unsigned long intparm, struct irb *irb)
{
- lcs_debug_event(2, "lcs_queue_write_if_necessary write=%p\n", write);
- if (!write->resume_queued) {
- write->resume_loopcnt = 0;
- lcs_queue_write(write);
+ char dbf_text[15];
+ struct lcs_card *card;
+ struct lcs_channel *channel;
+ int index;
+
+ card = (struct lcs_card *)cdev->dev.driver_data;
+ if (card->read.ccwdev == cdev)
+ channel = &card->read;
+ else
+ channel = &card->write;
+
+ sprintf(dbf_text, "Rint%s", cdev->dev.bus_id);
+ LCS_DBF_TEXT(5, trace, dbf_text);
+ sprintf(dbf_text, "%4x%4x", irb->scsw.cstat, irb->scsw.dstat);
+ LCS_DBF_TEXT(5, trace, dbf_text);
+
+ /* How far in the ccw chain have we processed? */
+ if (channel->state != CH_STATE_INIT) {
+ index = (struct ccw1 *) __va((addr_t) irb->scsw.cpa)
+ - channel->ccws;
+ if ((irb->scsw.actl & SCSW_ACTL_SUSPENDED) ||
+ (irb->scsw.cstat | SCHN_STAT_PCI))
+ /* Bloody io subsystem tells us lies about cpa... */
+ index = (index - 1) & (LCS_NUM_BUFFS - 1);
+ while (channel->io_idx != index) {
+ __lcs_processed_buffer(channel,
+ channel->iob + channel->io_idx);
+ channel->io_idx =
+ (channel->io_idx + 1) & (LCS_NUM_BUFFS - 1);
+ }
}
+
+ if ((irb->scsw.dstat & DEV_STAT_DEV_END) ||
+ (irb->scsw.dstat & DEV_STAT_CHN_END) ||
+ (irb->scsw.dstat & DEV_STAT_UNIT_CHECK))
+ /* Mark channel as stopped. */
+ channel->state = CH_STATE_STOPPED;
+ else if (irb->scsw.actl & SCSW_ACTL_SUSPENDED)
+ /* CCW execution stopped on a suspend bit. */
+ channel->state = CH_STATE_SUSPENDED;
+
+ if (irb->scsw.fctl & SCSW_FCTL_HALT_FUNC)
+ /* The channel has been stopped by halt_IO. */
+ channel->state = CH_STATE_HALTED;
+
+ /* Do the rest in the tasklet. */
+ tasklet_schedule(&channel->irq_tasklet);
}
-static int
-lcs_should_queue_write(lcs_write_globals * write, uint64_t tod)
+/**
+ * Tasklet for IRQ handler
+ */
+static void
+lcs_tasklet(unsigned long data)
{
+ char dbf_text[15];
+ unsigned long flags;
+ struct lcs_channel *channel;
+ struct lcs_buffer *iob;
+ int buf_idx, io_idx;
+ int rc;
+
+ channel = (struct lcs_channel *) data;
+ sprintf(dbf_text, "tlet%s", channel->ccwdev->dev.bus_id);
+ LCS_DBF_TEXT(5, trace, dbf_text);
- if (lcs_getbuffs_filled(write) < (LCS_NUM_TX_BUFFS - 1)
- && (lcs_tx_is_busy(write, tod) ||
- ((tod - write->last_lcs_txpacket_time) <
- (25 << TOD_SHIFT_TO_USECS))))
- return (TRUE);
- return (FALSE);
+ /* Check for processed buffers. */
+ iob = channel->iob;
+ buf_idx = channel->buf_idx;
+ io_idx = channel->io_idx;
+ while (buf_idx != io_idx &&
+ iob[buf_idx].state == BUF_STATE_PROCESSED) {
+ /* Do the callback thing. */
+ if (iob[buf_idx].callback != NULL)
+ iob[buf_idx].callback(channel, iob + buf_idx);
+ buf_idx = (buf_idx + 1) & (LCS_NUM_BUFFS - 1);
+ }
+ channel->buf_idx = buf_idx;
+
+ if (channel->state == CH_STATE_STOPPED)
+ // FIXME: what if rc != 0 ??
+ rc = lcs_start_channel(channel);
+ spin_lock_irqsave(get_ccwdev_lock(channel->ccwdev), flags);
+ if (channel->state == CH_STATE_SUSPENDED &&
+ channel->iob[channel->io_idx].state == BUF_STATE_READY) {
+ // FIXME: what if rc != 0 ??
+ rc = __lcs_resume_channel(channel);
+ }
+ spin_unlock_irqrestore(get_ccwdev_lock(channel->ccwdev), flags);
+ /* Something happened on the channel. Wake up waiters. */
+ wake_up(&channel->wait_q);
}
+/**
+ * Finish current tx buffer and make it ready for transmit.
+ */
static void
-lcs_resume_writetask(lcs_write_globals * write)
+__lcs_emit_txbuffer(struct lcs_card *card)
{
- uint64_t tod;
-
- lcs_debug_event(2, "lcs_resume_writetask write=%p\n", write);
- lcs_chan_lock(write);
- tod = lcs_get_tod();
- if (write->resume_loopcnt < 20 && lcs_should_queue_write(write, tod)) {
- write->resume_loopcnt++;
- lcs_queue_write(write);
- lcs_chan_unlock(write);
- return;
- } else {
-#if 0
- if (write->resume_loopcnt > 10)
- lcs_debug_event(1,"lcs_resume_writetask bad "
- "loopcnt %d\n",write->resume_loopcnt);
-#endif
- lcs_resume_write(write);
- }
- write->resume_queued = FALSE;
- lcs_chan_unlock(write);
+ *(__u16 *)(card->tx_buffer->data + card->tx_buffer->count) = 0;
+ card->tx_buffer->count += 2;
+ lcs_ready_buffer(&card->write, card->tx_buffer);
+ card->tx_buffer = NULL;
+ card->tx_emitted++;
}
-static int
-lcs_calcsleeptime(lcs_drvr_globals * drvr_globals, u8 cmd_code)
+/**
+ * Callback for finished tx buffers.
+ */
+static void
+lcs_txbuffer_cb(struct lcs_channel *channel, struct lcs_buffer *buffer)
{
- if ((cmd_code == lcs_startlan || cmd_code == lcs_stoplan))
- return (drvr_globals->slow_hw ? 60 : 5);
- else
- return (drvr_globals->slow_hw ? 5 : 5);
+ struct lcs_card *card;
+ /* Put buffer back to pool. */
+ lcs_release_buffer(channel, buffer);
+ card = (struct lcs_card *)
+ ((char *) channel - offsetof(struct lcs_card, write));
+ spin_lock(&card->lock);
+ card->tx_emitted--;
+ if (card->tx_emitted <= 0 && card->tx_buffer != NULL)
+ /*
+ * Last running tx buffer has finished. Submit partially
+ * filled current buffer.
+ */
+ __lcs_emit_txbuffer(card);
+ spin_unlock(&card->lock);
}
+/**
+ * Packet transmit function called by network stack
+ */
static int
-lcs_resumeandwait(lcs_write_globals * write, u8 cmd_code)
+__lcs_start_xmit(struct lcs_card *card, struct sk_buff *skb,
+ struct net_device *dev)
{
- int rc;
+ struct lcs_header *header;
- lcs_initqueue(write);
- if ((rc = lcs_resume_write(write)))
- return (rc);
- return (lcs_sleepon
- (write, lcs_calcsleeptime(write->drvr_globals, cmd_code)));
+ if (skb == NULL) {
+ card->stats.tx_dropped++;
+ card->stats.tx_errors++;
+ return -EIO;
+ }
+ if (card->state != DEV_STATE_UP) {
+ dst_link_failure(skb);
+ dev_kfree_skb(skb);
+ card->stats.tx_dropped++;
+ card->stats.tx_errors++;
+ card->stats.tx_carrier_errors++;
+ return 0;
+ }
+ if (netif_queue_stopped(dev) ) {
+ card->stats.tx_dropped++;
+ return -EBUSY;
+ }
+ if (card->tx_buffer != NULL &&
+ card->tx_buffer->count + sizeof(struct lcs_header) +
+ skb->len + sizeof(u16) > LCS_IOBUFFERSIZE)
+ /* skb too big for current tx buffer. */
+ __lcs_emit_txbuffer(card);
+ if (card->tx_buffer == NULL) {
+ /* Get new tx buffer */
+ card->tx_buffer = lcs_get_buffer(&card->write);
+ if (card->tx_buffer == NULL) {
+ netif_stop_queue(dev);
+ card->stats.tx_dropped++;
+ return -EBUSY;
+ }
+ card->tx_buffer->callback = lcs_txbuffer_cb;
+ card->tx_buffer->count = 0;
+ }
+ header = (struct lcs_header *)
+ (card->tx_buffer->data + card->tx_buffer->count);
+ card->tx_buffer->count += skb->len + sizeof(struct lcs_header);
+ header->offset = card->tx_buffer->count;
+ header->type = card->lan_type;
+ header->slot = card->portno;
+ memcpy(header + 1, skb->data, skb->len);
+ card->stats.tx_bytes += skb->len;
+ card->stats.tx_packets++;
+ dev_kfree_skb(skb);
+ if (card->tx_emitted <= 0)
+ /* If this is the first tx buffer emit it immediatly. */
+ __lcs_emit_txbuffer(card);
+ return 0;
}
static int
-lcs_lgw_common(lcs_drvr_globals * drvr_globals, u8 cmd_code)
+lcs_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
+ struct lcs_card *card;
+ int rc;
- lcs_write_globals *write = drvr_globals->write;
- lcs_std_cmd *writecmd;
- wait_on_write(drvr_globals);
- writecmd = lcs_prepare_lancmd(write, cmd_code,
- drvr_globals->rel_adapter_no,
- lcs_lgw_initiated,
- drvr_globals->lan_type);
- writecmd->sequence_no = drvr_globals->lgw_sequence_no;
- return (lcs_resumeandwait(write, cmd_code));
+ LCS_DBF_TEXT(5, trace, "pktxmit");
+ card = (struct lcs_card *) dev->priv;
+ spin_lock(&card->lock);
+ rc = __lcs_start_xmit(card, skb, dev);
+ spin_unlock(&card->lock);
+ return rc;
}
-static void
-lcs_start_doing_io(lcs_drvr_globals * drvr_globals)
+/**
+ * send startlan and lanstat command to make LCS device ready
+ */
+static int
+lcs_startlan_auto(struct lcs_card *card)
{
- netif_wake_queue(drvr_globals->dev);
- drvr_globals->state = lcs_doing_io;
+ int rc;
+
+#ifdef CONFIG_NET_ETHERNET
+ card->lan_type = LCS_FRAME_TYPE_ENET;
+ rc = lcs_send_startlan(card, LCS_INITIATOR_TCPIP);
+ if (rc == 0)
+ return 0;
+
+#endif
+#ifdef CONFIG_TR
+ card->lan_type = LCS_FRAME_TYPE_TR;
+ rc = lcs_send_startlan(card, LCS_INITIATOR_TCPIP);
+ if (rc == 0)
+ return 0;
+#endif
+#ifdef CONFIG_FDDI
+ card->lan_type = LCS_FRAME_TYPE_FDDI;
+ rc = lcs_send_startlan(card, LCS_INITIATOR_TCPIP);
+ if (rc == 0)
+ return 0;
+#endif
+ return -EIO;
}
static int
-lcs_lgw_stoplan(lcs_drvr_globals * drvr_globals)
+lcs_startlan(struct lcs_card *card)
{
- struct net_device *dev;
+ int rc, i;
- lcs_daemonize("lcs_lgw_stoplan", 0, TRUE);
- if (!lcs_drvr_globals_valid(drvr_globals))
- goto Done2;
- if (drvr_globals->state != lcs_doing_io)
- goto Done1;
- dev = drvr_globals->dev;
- lcs_bad_news("lcs problems detected %s temporarily unavailable.\n",
- dev->name);
- lcs_lgw_common(drvr_globals, lcs_stoplan);
- netif_stop_queue(dev);
- drvr_globals->state = lcs_doing_io;
-Done1:
- atomic_set(&drvr_globals->kernel_thread_lock, 1);
- lcs_usage_free_drvr_globals(drvr_globals);
-Done2:
- MOD_DEC_USE_COUNT;
- return (0);
+ LCS_DBF_TEXT(2, trace, "startlan");
+ rc = 0;
+ if (card->portno != LCS_INVALID_PORT_NO) {
+ if (card->lan_type == LCS_FRAME_TYPE_AUTO)
+ rc = lcs_startlan_auto(card);
+ else
+ rc = lcs_send_startlan(card, LCS_INITIATOR_TCPIP);
+ } else {
+ for (i = 0; i <= card->max_port_no; i++) {
+ card->portno = i;
+ if (card->lan_type != LCS_FRAME_TYPE_AUTO)
+ rc = lcs_send_startlan(card,
+ LCS_INITIATOR_TCPIP);
+ else
+ /* autodetecting lan type */
+ rc = lcs_startlan_auto(card);
+ if (rc == 0)
+ break;
+ }
+ }
+ if (rc == 0)
+ return lcs_send_lanstat(card);
+ return rc;
}
+/**
+ * LCS detect function
+ * setup channels and make them I/O ready
+ */
static int
-lcs_lgw_startlan(lcs_drvr_globals * drvr_globals)
+lcs_detect(struct lcs_card *card)
{
- struct net_device *dev;
+ int rc;
- lcs_daemonize("lcs_lgw_startlan", 0, TRUE);
- if (!lcs_drvr_globals_valid(drvr_globals))
- goto Done2;
- if (drvr_globals->state != lcs_doing_io)
- goto Done1;
- dev = drvr_globals->dev;
- lcs_good_news("lcs device %s restarted.\n", dev->name);
- lcs_lgw_common(drvr_globals, lcs_startlan);
- lcs_start_doing_io(drvr_globals);
-Done1:
- atomic_set(&drvr_globals->kernel_thread_lock, 1);
- lcs_usage_free_drvr_globals(drvr_globals);
-Done2:
- MOD_DEC_USE_COUNT;
- return (0);
+ LCS_DBF_TEXT(3, setup," lcsdetct");
+ /* start/reset card */
+ if (card->dev)
+ netif_stop_queue(card->dev);
+ card->write.state = CH_STATE_INIT;
+ card->read.state = CH_STATE_INIT;
+ rc = lcs_stop_channels(card);
+ if (rc == 0) {
+ rc = lcs_start_channels(card);
+ if (rc == 0) {
+ rc = lcs_send_startup(card, LCS_INITIATOR_TCPIP);
+ if (rc == 0)
+ rc = lcs_startlan(card);
+ }
+ }
+ if (rc == 0) {
+ card->state = DEV_STATE_UP;
+ } else {
+ card->state = DEV_STATE_DOWN;
+ card->write.state = CH_STATE_INIT;
+ card->read.state = CH_STATE_INIT;
+ }
+ return rc;
}
+/**
+ * reset card
+ */
static int
-lcs_lgw_resetlan(lcs_drvr_globals * drvr_globals)
-{
- struct net_device *dev;
- int retrycnt;
-
- lcs_daemonize("lcs_lgw_resetlan", 0, TRUE);
- if (!lcs_drvr_globals_valid(drvr_globals))
- goto Done2;
- if (drvr_globals->state != lcs_doing_io)
- goto Done1;
- dev = drvr_globals->dev;
- lcs_debug_event(0, "lcs reset %s.\n", dev->name);
- for (retrycnt = 0; retrycnt < 10; retrycnt++) {
- if (lcs_detect(drvr_globals,drvr_globals->rel_adapter_no,0,0,
- drvr_globals->lan_type,dev->dev_addr) == 0) {
- lcs_start_doing_io(drvr_globals);
- lcs_good_news("lcs reset %s successfully.\n",
- dev->name);
- goto Done1;
+lcs_resetcard(struct lcs_card *card)
+{
+ int retries;
+
+ LCS_DBF_TEXT(4, trace, "rescard");
+ for (retries = 0; retries < 10; retries++) {
+ if (lcs_detect(card) == 0) {
+ netif_wake_queue(card->dev);
+ card->state = DEV_STATE_UP;
+ PRINT_INFO("LCS device %s successfully restarted!\n",
+ card->dev->name);
+ return 0;
}
- lcs_debug_event(0, "lcs_resetlan retrycnt %d\n", retrycnt);
schedule_timeout(3 * HZ);
- atomic_set(&drvr_globals->retry_cnt, 0);
}
- lcs_resetstate(drvr_globals);
- lcs_bad_news("lcs problems occured restarting after reset %s.\n",
- dev->name);
-Done1:
- atomic_set(&drvr_globals->kernel_thread_lock, 1);
- lcs_usage_free_drvr_globals(drvr_globals);
-Done2:
- MOD_DEC_USE_COUNT;
- return (0);
+ PRINT_ERR("Error in Reseting LCS card!\n");
+ return -EIO;
}
-static void
-lcs_queue_thread(int (*routine) (lcs_drvr_globals *),
- lcs_drvr_globals * drvr_globals)
+/**
+ * LCS Stop card
+ */
+static int
+lcs_stopcard(struct lcs_card *card)
{
- if (atomic_dec_and_test(&drvr_globals->kernel_thread_lock)) {
- netif_stop_queue(drvr_globals->dev);
- MOD_INC_USE_COUNT;
- drvr_globals->kernel_thread_routine = routine;
-#if LINUX_VERSION_CODE>=KERNEL_VERSION(2,5,41)
- schedule_work(&drvr_globals->kernel_thread_task);
-#else
-#if LINUX_VERSION_CODE<=KERNEL_VERSION(2,2,16)
- queue_task(&drvr_globals->kernel_thread_task, &tq_scheduler);
-#else
- schedule_task(&drvr_globals->kernel_thread_task);
-#endif
-#endif
- } else
- lcs_debug_event(1,"lcs_queue_thread busy drvr_globals=%p "
- "routine=%p",drvr_globals,routine);
+ int rc;
+
+ LCS_DBF_TEXT(3, setup, "stopcard");
+ if (card->read.state != CH_STATE_STOPPED &&
+ card->write.state != CH_STATE_STOPPED &&
+ card->state == DEV_STATE_UP)
+ rc = lcs_send_stoplan(card,LCS_INITIATOR_TCPIP);
+ rc = lcs_send_shutdown(card);
+ rc = lcs_stop_channels(card);
+ card->state = DEV_STATE_DOWN;
+ return rc;
}
+/**
+ * LGW initiated commands
+ */
static int
-lcs_check_reset(lcs_chan_globals * chan_globals)
+lcs_lgw_startlan_thread(void *data)
{
- lcs_drvr_globals *drvr_globals = chan_globals->drvr_globals;
- devstat_t *devstat = &chan_globals->devstat;
-
- if (devstat->flag & DEVSTAT_FLAG_SENSE_AVAIL) {
- if (drvr_globals->state == lcs_doing_io &&
- (devstat->scnt >= 1) &&
- ((lcs_sense_byte_1) (devstat->ii.sense.data[1]))
- == resetting_event) {
- lcs_debug_exception(0,"lcs reseting event occuring "
- "devno=%04x\n",
- (int) chan_globals->devno);
- atomic_set(&drvr_globals->retry_cnt, 0);
- switch (drvr_globals->state) {
- case lcs_idle_requesting_channels_for_lancmds:
- case lcs_requesting_channels_for_lancmds:
- case lcs_got_write_channel_for_lancmds:
- case lcs_doing_lancmds:
- lcs_wakeup(drvr_globals->read, -ERETRY);
- lcs_wakeup(drvr_globals->write, -ERETRY);
- break;
- case lcs_doing_io:
- lcs_queue_thread(lcs_lgw_resetlan,
- drvr_globals);
- break;
- default:
- break;
- }
- return (TRUE);
- } else {
- if ((devstat->scnt >= 1) && ((lcs_sense_byte_0)
- (devstat->ii.sense.
- data[0]) ==
- (intervention_required |
- interface_disconnect)))
- lcs_debug_exception(0,
- "lcs intervention_required "
- "& interface_disconnect "
- "detected devno=0x%04x\n",
- (int) chan_globals->devno);
- else {
- lcs_debug_event(0,
- "lcs unknown sense data "
- "devno=0x%04x\n"
- "sense_cnt=%d sense_data=\n"
- "%08x %08x %08x %08x\n"
- "%08x %08x %08x %08x\n",
- (int) chan_globals->devno,
- (int) devstat->scnt,
- ((int *)&devstat->
- ii.sense.data)[0],
- ((int *)&devstat->
- ii.sense.data)[1],
- ((int *)&devstat->
- ii.sense.data)[2],
- ((int *)&devstat->
- ii.sense.data)[3],
- ((int *)&devstat->
- ii.sense.data)[4],
- ((int *)&devstat->
- ii.sense.data)[5],
- ((int *)&devstat->
- ii.sense.data)[6],
- ((int *)&devstat->
- ii.sense.data)[7]);
- if (devstat->scnt > 2
- || (devstat->ii.sense.data[0] == 0
- && devstat->ii.sense.data[1] == 0)) {
- lcs_debug_event(0,
- "lcs sense data "
- "looks like rubbish "
- "ignoring it\n");
- devstat->flag &=
- ~DEVSTAT_FLAG_SENSE_AVAIL;
- }
- }
- }
- }
- return (FALSE);
-}
+ struct lcs_card *card;
-#if LCS_ZERO_COPY_READ
-static inline void
-lcs_rxskb(lcs_drvr_globals * drvr_globals, struct sk_buff *skb, u16 pkt_len,
- u16 offset, struct net_device_stats *stats)
-{
- stats->rx_bytes += pkt_len;
- if (use_hw_stats == FALSE)
- stats->rx_packets++;
- lcs_debug_event(2, "netif_rx called\n");
- lcs_debug_event(2, "received %u bytes rx_bytes=%lu\n",
- (unsigned) pkt_len, stats->rx_bytes);
- skb_reserve(skb, sizeof (lcs_header_type) + offset);
- skb_put(skb, pkt_len);
- /* Horrible horrible kludge to fix bug in some socket code */
- /* usage of skb->truesize hopefully this bug will be fixed in */
- /* upper layers soon */
- skb->truesize = skb->len;
- skb->protocol = drvr_globals->lan_type_trans(skb, skb->dev);
- netif_rx(skb);
- skb->dev->last_rx = jiffies;
+ card = (struct lcs_card *) data;
+ daemonize();
+ LCS_DBF_TEXT(4, trace, "lgwstpln");
+ if (card->dev)
+ netif_stop_queue(card->dev);
+ if (lcs_startlan(card) == 0) {
+ netif_wake_queue(card->dev);
+ card->state = DEV_STATE_UP;
+ PRINT_INFO("LCS Startlan for device %s succeeded!\n",
+ card->dev->name);
+
+ } else
+ PRINT_ERR("LCS Startlan for device %s failed!\n",
+ card->dev->name);
+ return 0;
}
-#endif
+/**
+ * Send startup command initiated by Lan Gateway
+ */
static int
-lcs_rxpacket(lcs_drvr_globals * drvr_globals, u8 * start_lcs_buff,
- lcs_std_cmd ** lancmd_reply
-#if LCS_ZERO_COPY_READ
- , u32 curr_idx
-#endif
- )
+lcs_lgw_startup_thread(void *data)
{
- lcs_read_globals *read = NULL;
-
- struct sk_buff *curr_skb
-#if LCS_ZERO_COPY_READ
- , *prev_skb
-#endif
- ;
- struct net_device *dev = NULL;
- struct net_device_stats *stats = NULL;
- lcs_header_type *lcs_header_ptr = (lcs_header_type *) start_lcs_buff;
- u16 curr_offset, curr_pkt_len
-#if LCS_ZERO_COPY_READ
- , prev_pkt_len, curr_skb_offset, prev_skb_offset
-#endif
- ;
+ int rc;
- lcs_debug_event(2, "Entering lcs_rxpacket\n");
- if (drvr_globals == NULL) {
- lcs_bad_news("drvr_globals=NULL\n");
- return -EPERM;
+ struct lcs_card *card;
+
+ card = (struct lcs_card *) data;
+ daemonize();
+ LCS_DBF_TEXT(4, trace, "lgwstpln");
+ if (card->dev)
+ netif_stop_queue(card->dev);
+ rc = lcs_send_startup(card, LCS_INITIATOR_LGW);
+ if (rc != 0) {
+ PRINT_ERR("Startup for LCS device %s initiated " \
+ "by LGW failed!\nReseting card ...\n",
+ card->dev->name);
+ /* do a card reset */
+ rc = lcs_resetcard(card);
+ if (rc == 0)
+ goto Done;
}
-
- dev = drvr_globals->dev;
- read = drvr_globals->read;
-#if LCS_ZERO_COPY_READ
- if (atomic_read(skb_datarefp(read->skb_buff[curr_idx])) != 1) {
- lcs_bad_news("lcs_rxpacket skb->refcnt=%d start_lcs_buff=%p\n",
- atomic_read(skb_datarefp
- (read->skb_buff[curr_idx])),
- start_lcs_buff);
- return -EIO;
+ rc = lcs_startlan(card);
+ if (rc == 0) {
+ netif_wake_queue(card->dev);
+ card->state = DEV_STATE_UP;
}
-#endif
- stats = &drvr_globals->stats;
- lcs_debug_event(2, "We appear to have received a packet buff=%p\n",
- lcs_header_ptr);
-#if LCS_ZERO_COPY_READ
- curr_skb = NULL;
- curr_pkt_len = curr_skb_offset =
-#endif
- curr_offset = 0;
- while (lcs_header_ptr->offset) {
- lcs_debug_event(3, "lcs_header_ptr=%p curroffset=%d\n",
- lcs_header_ptr, curr_offset);
-#if LCS_ZERO_COPY_READ
- prev_pkt_len = curr_pkt_len;
- prev_skb = curr_skb;
- prev_skb_offset = curr_skb_offset;
-#endif
-#if 0
- static int idx = 0;
- idx++;
- if ((idx & 0x1ff) == 0) {
- lcs_header_ptr->type = lcs_control_frame_type;
- ((lcs_std_cmd *) lcs_header_ptr)->initiator =
- lcs_lgw_initiated;
- (((lcs_std_cmd *) lcs_header_ptr)->cmd_code) =
- lcs_startup;
- }
-#endif
- if (lcs_header_ptr->offset > LCS_IOBUFFSIZE ||
- lcs_header_ptr->offset < curr_offset) {
- if (use_hw_stats == FALSE) {
- stats->rx_length_errors++;
- stats->rx_errors++;
- }
- lcs_bad_news
- ("lcs whacko rx_buffer read_globals=%p "
- "curr_offset=%d new_offset=%d\n",
- read, (int) curr_offset,
- (int) lcs_header_ptr->offset);
- return -EIO;
- }
+Done:
+ if (rc == 0)
+ PRINT_INFO("LCS Startup for device %s succeeded!\n",
+ card->dev->name);
+ else
+ PRINT_ERR("LCS Startup for device %s failed!\n",
+ card->dev->name);
+ return 0;
+}
- if (lcs_header_ptr->type == lcs_control_frame_type) {
- if (((lcs_std_cmd *) lcs_header_ptr)->initiator ==
- lcs_lgw_initiated
- && drvr_globals->state == lcs_doing_io) {
- drvr_globals->lgw_sequence_no =
- ((lcs_std_cmd *) lcs_header_ptr)->sequence_no;
- lcs_debug_display_read_buff
- ("lgw_initiated_command read buff",
- ((lcs_std_cmd *) lcs_header_ptr));
- switch ((int)
- (((lcs_std_cmd *)lcs_header_ptr)->
- cmd_code)) {
- case lcs_startup:
- atomic_set(&drvr_globals->retry_cnt,
- -1);
- lcs_queue_thread(lcs_lgw_resetlan,
- drvr_globals);
- break;
- case lcs_startlan:
- lcs_queue_thread(lcs_lgw_startlan,
- drvr_globals);
- break;
- case lcs_stoplan:
- lcs_queue_thread(lcs_lgw_stoplan,
- drvr_globals);
- break;
- default:
- lcs_debug_exception(1,"lcs "
- "unrecognised lgw command "
- "received 0x%02x on "
- "devno=%04x\n",
- (int)((lcs_std_cmd *)
- lcs_header_ptr)->cmd_code,
- (int)read->devno);
- }
- } else {
- if (drvr_globals->state == lcs_doing_lancmds) {
- if (((((lcs_std_cmd *)
- lcs_header_ptr)->sequence_no ==
- drvr_globals->sequence_no))
- &&
- (((lcs_std_cmd *)
- lcs_header_ptr)->cmd_code ==
- drvr_globals->cmd_code)) {
- *lancmd_reply =
- read->lancmd_reply_ptr =
- (lcs_std_cmd *)lcs_header_ptr;
- lcs_debug_event(2,
- "got a valid "
- "lancommand %p "
- "cmd_code=%02x "
- "sequence_no=%d\n",
- read->lancmd_reply_ptr,
- (int)
- ((lcs_std_cmd *)
- lcs_header_ptr)->
- cmd_code,
- (int)
- ((lcs_std_cmd *)
- lcs_header_ptr)->
- sequence_no);
- lcs_wakeup((lcs_chan_globals *)
- read, 0);
- break;
- }
- }
- }
- } else if (dev && drvr_globals->state == lcs_doing_io) {
- /* the packet is for tcpip */
-
- /* dev_alloc_skb allocs with GFP_ATOMIC currently */
- /* so this should be okay under interrupt */
- /* hopefully .... */
-#if LCS_ZERO_COPY_READ
- if (curr_skb == NULL) {
- /* We need to detach the buffer */
- struct sk_buff *new_skb;
- ccw1_t temp_ccw = read->ccw[curr_idx];
- if (set_normalized_cda (&temp_ccw,
- new_skb->data)) {
- stats->rx_dropped++;
- lcs_bad_news
- ("%s: Memory squeeze, "
- "dropping packet.\n",
- dev->name);
- return -ENOMEM;
- }
- if ((new_skb=
- lcs_dev_alloc_skb(LCS_READ_ALLOCSIZE))==
- NULL) {
- clear_normalized_cda(&temp_ccw);
- stats->rx_dropped++;
- lcs_bad_news
- ("%s: Memory squeeze, "
- "dropping packet.\n",
- dev->name);
- return -ENOMEM;
- }
- curr_skb = read->skb_buff[curr_idx];
- curr_skb->dev = dev;
- curr_skb->ip_summed=(checksum_received_ip_pkts
- ? CHECKSUM_NONE :
- CHECKSUM_UNNECESSARY);
- clear_normalized_cda(&read->ccw[curr_idx]);
- read->skb_buff[curr_idx] = new_skb;
- read->ccw[curr_idx] = temp_ccw;
- } else {
- curr_skb = skb_clone(prev_skb, GFP_ATOMIC);
- if (curr_skb == NULL) {
- curr_skb = prev_skb;
- stats->rx_dropped++;
- lcs_bad_news("%s: Memory squeeze, "
- "dropping packet.\n",
- dev->name);
- return -ENOMEM;
- }
- curr_skb_offset = curr_offset;
- }
- if (prev_skb)
- lcs_rxskb(drvr_globals, prev_skb, prev_pkt_len,
- prev_skb_offset, stats);
- curr_pkt_len =
- lcs_header_ptr->offset - curr_skb_offset -
- sizeof (lcs_header_type);
-#else
- curr_pkt_len =
- lcs_header_ptr->offset - curr_offset -
- sizeof (lcs_header_type);
- curr_skb = lcs_dev_alloc_skb(curr_pkt_len);
- if (curr_skb) {
- curr_skb->dev = dev;
- memcpy(skb_put(curr_skb, curr_pkt_len),
- lcs_header_ptr + 1, curr_pkt_len);
- curr_skb->protocol =
- drvr_globals->lan_type_trans(curr_skb, dev);
- curr_skb->ip_summed =
- (checksum_received_ip_pkts ? CHECKSUM_NONE :
- CHECKSUM_UNNECESSARY);
- stats->rx_bytes += curr_pkt_len;
- if (use_hw_stats == FALSE)
- stats->rx_packets++;
- lcs_debug_event(2,"netif_rx called\n");
- lcs_debug_event(2,"received %u bytes " \
- "rx_bytes=%lu\n",
- (unsigned) curr_pkt_len,
- stats->rx_bytes);
- netif_rx(curr_skb);
- curr_skb->dev->last_rx = jiffies;
- } else {
- lcs_bad_news
- ("%s: Memory squeeze, dropping packet.\n",
- dev->name);
- stats->rx_dropped++;
- return -ENOMEM;
- }
-#endif
- }
- curr_offset = lcs_header_ptr->offset;
- ((addr_t) lcs_header_ptr) = start_lcs_buff + curr_offset;
- }
-#if LCS_ZERO_COPY_READ
- if (curr_skb)
- lcs_rxskb(drvr_globals, curr_skb, curr_pkt_len, curr_skb_offset,
- stats);
-#endif
+
+/**
+ * send stoplan command initiated by Lan Gateway
+ */
+static int
+lcs_lgw_stoplan_thread(void *data)
+{
+ struct lcs_card *card;
+
+ card = (struct lcs_card *) data;
+ daemonize();
+ LCS_DBF_TEXT(4, trace, "lgwstop");
+ if (card->dev)
+ netif_stop_queue(card->dev);
+ if (lcs_send_stoplan(card, LCS_INITIATOR_LGW) == 0)
+ PRINT_INFO("Stoplan for %s initiated by LGW succeeded!\n",
+ card->dev->name);
+ else
+ PRINT_ERR("Stoplan %s initiated by LGW failed!\n",
+ card->dev->name);
return 0;
}
-/*
- * This is the main interrupt handler it does a lot of sanity checking &
- * wakes up a process if doing startup or shutdown or calls
- * queues lcs_rxpacket if a network packet is received or
- * resets transmit busy a network packet has been transmitted &
- * marks the bottom half so that networking knows it can transmit more packets
- * N.B. if we decide to call ssch directly or indirectly from the irq_handler
- * we had better increment chan_globals->lock_cnt & decrement it when done to avoid
- * deadlocks ( this count can be over 2 inside the interrupt handler ),
- * but not in normal tasks.
+/**
+ * Kernel Thread helper functions for LGW initiated commands
*/
+static void
+lcs_start_kernel_thread(struct lcs_card *card)
+{
+ LCS_DBF_TEXT(5, trace, "krnthrd");
+ if (test_and_clear_bit(0, &card->thread_mask))
+ kernel_thread(lcs_lgw_startup_thread, (void *) card, SIGCHLD);
+ if (test_and_clear_bit(1, &card->thread_mask))
+ kernel_thread(lcs_lgw_startlan_thread, (void *) card, SIGCHLD);
+ if (test_and_clear_bit(2, &card->thread_mask))
+ kernel_thread(lcs_lgw_stoplan_thread, (void *) card, SIGCHLD);
+ if (test_and_clear_bit(3, &card->thread_mask))
+ kernel_thread(lcs_fix_multicast_list, (void *) card, SIGCHLD);
+}
+/**
+ * Process control frames.
+ */
static void
-lcs_handle_irq(int irq, devstat_t * devstat, struct pt_regs *regs)
+lcs_get_control(struct lcs_card *card, struct lcs_cmd *cmd)
{
- union {
- lcs_read_globals *read;
- lcs_write_globals *write;
- lcs_chan_globals *globals;
- } chan;
- struct net_device *dev = NULL;
- lcs_drvr_globals *drvr_globals = NULL;
- lcs_debug_initmessage struct net_device_stats *stats;
- ccw1_t *curr_ccw;
- lcs_header_type *lcs_hdr;
- u32 curr_ccwidx, curr_ccwidx2, curr_idx;
- int chan_globals_valid = FALSE;
-
- chan.globals = NULL;
- if (!devstat)
- goto lcs_int_spurious;
- chan.globals = (lcs_chan_globals *) devstat->intparm;
- if (!chan.globals || (irq != chan.globals->subchannel))
- goto lcs_int_spurious;
- chan_globals_valid = TRUE;
- drvr_globals = chan.globals->drvr_globals;
- lcs_debug_event(2,
- "lcs_handle_irq irq=%x chan_globals=%p drvr_globals=%p"
- " drvr_state=%d devstat=%p irb=%p cpu=%d "
- "chan_busy_state=%d\n",
- irq, chan.globals, drvr_globals, drvr_globals->state,
- &chan.globals->devstat, &chan.globals->devstat.ii.irb,
- smp_processor_id(), chan.globals->chan_busy_state);
- if (chan.globals->lock_cnt)
- lcs_bad_news("lcs_handle_irq lock owner %d lock_cnt=%d\n",
- chan.globals->lock_owner, chan.globals->lock_cnt);
- if (drvr_globals->state == lcs_interrupted) {
- lcs_wakeup(chan.globals, -EINTR);
- } else {
- if (lcs_check_reset(chan.globals))
- return;
- if (chan.globals->irq_allocated_magic != LCS_MAGIC)
- goto lcs_int_spurious;
- }
- dev = drvr_globals->dev;
- if (dev == NULL && drvr_globals->state == lcs_doing_io) {
- lcs_debug_setmessage("dev=NULL in handle irq");
- goto lcs_int_debug_info;
- }
- stats = &drvr_globals->stats;
- if (drvr_globals->state == lcs_halting_subchannels) {
- lcs_wakeup(chan.globals, 0);
- return;
- }
- chan.globals->lock_owner = smp_processor_id();
- chan.globals->lock_cnt = 1;
- if (drvr_globals->read == chan.read) {
- if (devstat->dstat != 0 ||
- (devstat->cstat & ~SCHN_STAT_PCI) != 0) {
-
- lcs_debug_event(0,
- "lcs_handle_irq dstat=%02x cstat=%02x "
- "drvr_globals_state=%d\n",
- devstat->dstat, devstat->cstat,
- drvr_globals->state);
- switch (drvr_globals->state) {
- case lcs_idle_requesting_channels_for_lancmds:
- case lcs_requesting_channels_for_lancmds:
- case lcs_got_write_channel_for_lancmds:
- case lcs_doing_lancmds:
- lcs_restartreadio(drvr_globals);
- break;
- case lcs_doing_io:
- lcs_retry(chan.globals);
- break;
- default:
- break;
- }
- goto lcs_normal_ret;
- }
- switch (chan.read->chan_busy_state) {
- case chan_busy: {
- int rxerr = 0;
- lcs_std_cmd *lancmd_reply_ptr;
-
- switch (drvr_globals->state) {
- case lcs_doing_io:
- case lcs_doing_lancmds:
- curr_idx =
- ((ccw1_t *)
- phys_to_virt(devstat->cpa) -
- &chan.read->ccw[0]);
- /* We don't need to divide by
- * sizeof(ccw1_t) here as we are
- * doing pointer arithmetic */
- curr_ccwidx = ((curr_idx - 2)
- & (LCS_NUM_RX_BUFFS - 1));
- curr_ccwidx2 = ((curr_idx - 1)
- & (LCS_NUM_RX_BUFFS - 1));
- for (curr_idx = chan.read->rx_idx;
- curr_idx != curr_ccwidx2;
- curr_idx = ((curr_idx + 1)
- & (LCS_NUM_RX_BUFFS - 1))) {
-#if LCS_ZERO_COPY_READ
- lcs_hdr = (lcs_header_type *)
- (chan.read->
- skb_buff[curr_idx]->data);
-#else
- lcs_hdr = (lcs_header_type *)
- (chan.read->
- iobuff[curr_idx]);
-#endif
-
- if (lcs_hdr->offset !=
- LCS_ILLEGAL_OFFSET) {
- /* Detect duplicate interrupts
- unfilled buffers etc. */
- lancmd_reply_ptr = NULL;
- if (rxerr != -ENOMEM)
- rxerr = lcs_rxpacket
- (drvr_globals,
- (u8 *)lcs_hdr,
- &lancmd_reply_ptr
-#if LCS_ZERO_COPY_READ
- , curr_idx
-#endif
- );
- if (lancmd_reply_ptr == NULL) {
-#if LCS_ZERO_COPY_READ
- lcs_hdr =
- (lcs_header_type *)
- (chan.read->
- skb_buff[curr_idx]->
- data);
-#endif
- lcs_hdr->offset =
- LCS_ILLEGAL_OFFSET;
- }
- } else {
- lcs_bad_news
- ("lcs_handle_irq race "
- "condition Illegal"
- " offset buffer "
- "for ccw=%p\n",
- &chan.read->
- ccw[curr_idx]);
- goto lcs_int_debug_info;
- }
- }
- if (chan.read->rx_idx != curr_ccwidx2) {
- curr_ccw =
- &chan.read->ccw[curr_ccwidx];
- curr_ccw->flags =
- CCW_FLAG_CC | CCW_FLAG_SLI |
- CCW_FLAG_SUSPEND
-#if LCS_USE_IDALS
- | (curr_ccw->flags &
- CCW_FLAG_IDA)
-#endif
- ;
- curr_idx = (chan.read->rx_idx - 1) &
- (LCS_NUM_RX_BUFFS - 1);
- curr_ccw = &chan.read->ccw[curr_idx];
- curr_ccw->flags = CCW_FLAG_CC |
- CCW_FLAG_SLI |
- CCW_FLAG_PCI
-#if LCS_USE_IDALS
- | (curr_ccw->flags &
- CCW_FLAG_IDA)
-#endif
- ;
- chan.read->rx_idx = curr_ccwidx2;
- }
- break;
- default:
- break;
- }
- }
- break;
- case chan_started_successfully:
- chan.read->chan_busy_state = chan_busy;
+ if (cmd->initiator == LCS_INITIATOR_LGW) {
+ switch(cmd->cmd_code) {
+ case LCS_CMD_STARTUP:
+ set_bit(0, &card->thread_mask);
+ schedule_work(&card->kernel_thread_starter);
+ break;
+ case LCS_CMD_STARTLAN:
+ set_bit(1, &card->thread_mask);
+ schedule_work(&card->kernel_thread_starter);
+ break;
+ case LCS_CMD_STOPLAN:
+ set_bit(2, &card->thread_mask);
+ schedule_work(&card->kernel_thread_starter);
break;
default:
+ PRINT_INFO("UNRECOGNIZED LGW COMMAND\n");
break;
}
+ } else
+ lcs_notify_lancmd_waiters(card, cmd);
+}
- /*
- We need to suspend if till the lancmd reply is consumed.
- */
- if ((chan.read->chan_busy_state == chan_busy ||
- chan.read->chan_busy_state == chan_idle) &&
-#if 0
- devstat->flag & DEVSTAT_SUSPENDED
-#else
- devstat->ii.irb.scsw.actl & SCSW_ACTL_SUSPENDED
-#endif
- ) {
- chan.read->chan_busy_state = chan_idle;
- if (chan.read->lancmd_reply_ptr == NULL) {
- lcs_debug_event(2,"resume called from "
- "interrupt handler.\n");
- lcs_interrupt_resume_read(chan.read);
- }
- }
- } else {
-
- if (devstat->dstat != 0 ||
- devstat->flag & DEVSTAT_FLAG_SENSE_AVAIL ||
- devstat->cstat != 0) {
- lcs_debug_event(0,
- "lcs_handle_irq write channel "
- "dstat=%02x flag=%x cstat=%02x "
- "drvr_globals_state=%d\n",
- (int) devstat->dstat,
- (int) devstat->flag,
- (int) devstat->cstat,
- drvr_globals->state);
- switch (drvr_globals->state) {
- case lcs_idle_requesting_channels_for_lancmds:
- case lcs_requesting_channels_for_lancmds:
- case lcs_got_write_channel_for_lancmds:
- case lcs_doing_lancmds:
- lcs_restartwriteio(drvr_globals);
- break;
- case lcs_doing_io:
- lcs_queue_thread(lcs_lgw_resetlan,
- drvr_globals);
- break;
- default:
- break;
- }
- goto lcs_normal_ret;
- }
- switch (chan.write->chan_busy_state) {
- case chan_started_successfully:
- chan.write->chan_busy_state = chan_idle;
- break;
- case chan_busy:
- chan.write->chan_busy_state = chan_idle;
-#if 0
- if (devstat->flag & DEVSTAT_SUSPENDED)
-#else
- if (devstat->ii.irb.scsw.actl & SCSW_ACTL_SUSPENDED)
-#endif
- {
- switch (drvr_globals->state) {
- case lcs_doing_io:
- case lcs_requesting_channels_for_lancmds:
- case lcs_doing_lancmds:
- curr_idx =
- ((ccw1_t *)
- phys_to_virt(devstat->cpa) -
- &chan.write->ccw[0]);
- curr_ccwidx =
- ((curr_idx - 2) &
- (LCS_NUM_TX_BUFFS - 1));
- curr_ccwidx2 =
- ((curr_idx - 1)
- & (LCS_NUM_TX_BUFFS - 1));
- if (curr_ccwidx == chan.write->tx_idx) {
- lcs_bad_news
- ("lcs_handle_irq race "
- "tx_idx=%d=curr_ccwidx "
- "prepare_idx=%u "
- "write_globals=%p\n",
- chan.write->tx_idx,
- chan.write->prepare_idx,
- chan.write);
- goto lcs_int_debug_info;
- }
-
- for (curr_idx =
- ((chan.write->tx_idx + 1)
- & (LCS_NUM_TX_BUFFS - 1));
- curr_idx != curr_ccwidx2;
- curr_idx =
- ((curr_idx + 1)
- & (LCS_NUM_TX_BUFFS - 1))) {
- curr_ccw =
- &chan.write->ccw[curr_idx];
- curr_ccw->flags |=
- CCW_FLAG_SUSPEND;
- curr_ccw->count = 0;
- }
- chan.write->tx_idx = curr_ccwidx;
- curr_ccw =
- &chan.write->ccw[chan.write->
- prepare_idx];
- if (curr_ccw !=
- &chan.write->ccw[curr_ccwidx]) {
- if (curr_ccw->count != 0) {
- /* Tx the buffer currently
- * being prepared */
- if (drvr_globals->state ==
- lcs_doing_io &&
- lcs_should_queue_write
- (chan.write, lcs_get_tod()))
- lcs_queue_write_if_necessary
- (chan.write);
- else
- lcs_resume_write
- (chan.write);
- }
- } else {
- lcs_bad_news
- ("lcs_handle_irq race "
- "tx_idx=%u=prepare_idx "
- "write_globals=%p\n",
- (int) chan.write->
- prepare_idx, chan.write);
- goto lcs_normal_ret;
- }
- break;
- default:
- break;
- }
- }
- break;
- default:
- break;
- }
- if (chan.write->chan_busy_state == chan_idle) {
- switch (drvr_globals->state) {
- case lcs_idle_requesting_channels_for_lancmds:
- case lcs_requesting_channels_for_lancmds:
- drvr_globals->state =
- lcs_got_write_channel_for_lancmds;
- case lcs_doing_lancmds:
- lcs_wakeup(chan.globals, 0);
- break;
- case lcs_doing_io:
- stats->tx_bytes +=
- chan.write->bytes_still_being_txed;
- chan.write->bytes_still_being_txed = 0;
- if (!use_hw_stats)
- stats->tx_packets +=
- chan.write->pkts_still_being_txed;
- chan.write->pkts_still_being_txed = 0;
- netif_wake_queue(dev);
- break;
- default:
- break;
- }
- }
- }
- goto lcs_normal_ret;
-lcs_int_spurious:
- lcs_debug_setmessage("received spurious interrupt");
-lcs_int_debug_info:
- lcs_debugchannel(message, chan.globals);
-lcs_normal_ret:
- lcs_debug_event(2, "lcs irq exited irq=%x\n", irq);
- if (chan_globals_valid) {
- chan.globals->lock_owner = LCS_INVALID_LOCK_OWNER;
- chan.globals->lock_cnt = 0;
- }
-}
-
-/*
- * Wrappers to allocate irqs if these fail the io channel is probably
- * being used by another device.
+/**
+ * Unpack network packet.
*/
-static int
-lcs_allocirq_func(lcs_chan_globals * chan_globals)
+static void
+lcs_get_skb(struct lcs_card *card, char *skb_data, unsigned int skb_len)
{
- int rc;
+ struct sk_buff *skb;
- lcs_debug_event(2, "lcs_allocirq called chan_globals=%p\n",
- chan_globals);
- if (chan_globals->irq_allocated_magic) {
- lcs_bad_news("lcs_allocirq irq already allocated");
- return -EBUSY;
- } else {
- chan_globals->devstat.intparm = (addr_t) chan_globals;
- rc =
-#if LCS_CHANDEV
- chandev_request_irq
-#else
- request_irq
-#endif
- (chan_globals->subchannel,
- (void (*)(int, void *, struct pt_regs *)) lcs_handle_irq,
- 0, cardname, &chan_globals->devstat);
- if (!rc)
- chan_globals->irq_allocated_magic = LCS_MAGIC;
+ if (card->dev == NULL ||
+ card->state != DEV_STATE_UP)
+ /* The card isn't up. Ignore the packet. */
+ return;
-#if LCS_DEBUG
- else
- lcs_debug_event(2,"request irq failed error code %d "
- "subchannel %2X\n",
- rc,chan_globals->subchannel);
-#endif
- return rc;
+ skb = dev_alloc_skb(skb_len);
+ if (skb == NULL) {
+ PRINT_ERR("LCS: alloc_skb failed for device=%s\n",
+ card->dev->name);
+ card->stats.rx_dropped++;
+ return;
}
+ skb->dev = card->dev;
+ memcpy(skb_put(skb, skb_len), skb_data, skb_len);
+ skb->protocol = card->lan_type_trans(skb, card->dev);
+ card->stats.rx_bytes += skb_len;
+ card->stats.rx_packets++;
+ netif_rx(skb);
}
-#define lcs_allocirq(chan_globals) \
- lcs_allocirq_func((lcs_chan_globals *)(chan_globals))
-
+/**
+ * LCS main routine to get packets and lancmd replies from the buffers
+ */
static void
-lcs_freeirq(lcs_chan_globals * chan_globals)
+lcs_get_frames_cb(struct lcs_channel *channel, struct lcs_buffer *buffer)
{
- if (chan_globals->irq_allocated_magic) {
-#if LCS_CHANDEV
- chandev_free_irq(chan_globals->subchannel,
- &chan_globals->devstat);
-#else
- free_irq(chan_globals->subchannel, &chan_globals->devstat);
-#endif
- chan_globals->irq_allocated_magic = 0;
- }
-}
+ struct lcs_card *card;
+ struct lcs_header *lcs_hdr;
+ __u16 offset;
-static lcs_inline void
-lcs_freeallirqs(lcs_drvr_globals * drvr_globals)
-{
- lcs_debug_event(2, "lcs_freeallirqs called %p\n", drvr_globals);
- lcs_freeirq((lcs_chan_globals *) drvr_globals->read);
- lcs_freeirq((lcs_chan_globals *) drvr_globals->write);
-}
-
-/*
- * Generic routine for sending all startup & shutdown command primatives
- * to lcs.
- * A unit check with a sense code of 0x42 may be presented here if this
- * happens we restart the startup procedure, currently the osa card has been
- * seen to present the unit check very late in the startup sequence
- * & resend outstanding buffers already read, the source of this problem is
- * unknown, this problem is handled basically by checking sequence
- * numbers & disgarding packets already received.
- */
-static int
-lcs_lancmd(lcs_cmd cmd_code, u8 rel_adapter_no, lcs_drvr_globals * drvr_globals
-#ifdef CONFIG_IP_MULTICAST
- , u16 num_ipm_pairs, lcs_ip_mac_addr_pair * ip_mac_pairs
-#endif
-)
-{
- lcs_std_cmd *readcmd, *writecmd;
- lcs_read_globals *read;
- lcs_write_globals *write;
- int rc = 0;
- int retry_cnt = 0;
-
- read = drvr_globals->read;
- write = drvr_globals->write;
- lcs_debug_exception(2, "lcs_lancmd cmd code=%02x drvr_globals=%p\n",
- (int) cmd_code, drvr_globals);
- wait_on_write(drvr_globals);
-Retry:
- read->lancmd_reply_ptr = NULL;
- do {
- if ((writecmd =
- lcs_prepare_lancmd(write, cmd_code, rel_adapter_no,
- lcs_390_initiated,
- drvr_globals->lan_type)) == 0) {
- lcs_debug_event(2, "lancmd2 lcs_prepare_cmd failed\n");
- return (-EIO);
- }
- lcs_debug_event(2, "lcs_lancmd writecmd=%p\n", writecmd);
- read->lancmd_reply_ptr = NULL;
- lcs_initqueue(read);
-#ifdef CONFIG_IP_MULTICAST
- if (cmd_code == lcs_setipm || cmd_code == lcs_delipm) {
- ((lcs_ipm_ctlmsg *) writecmd)->version = 4;
- ((lcs_ipm_ctlmsg *) writecmd)->num_ipm_pairs =
- num_ipm_pairs;
- memcpy(&((lcs_ipm_ctlmsg *) writecmd)->ip_mac_pair[0],
- ip_mac_pairs,
- sizeof (lcs_ip_mac_addr_pair) * num_ipm_pairs);
- }
- if (cmd_code == lcs_qipassist) {
- ((lcs_qipassist_ctlmsg *) writecmd)->num_ip_pairs = 0;
- ((lcs_qipassist_ctlmsg *) writecmd)->version = 4;
- }
-#endif
- writecmd->sequence_no = ++drvr_globals->sequence_no;
- lcs_debug_event(2, "sequence_no=%d\n",
- (int) drvr_globals->sequence_no);
- drvr_globals->cmd_code = cmd_code;
- rc = lcs_resumeandwait(write, cmd_code);
- } while (rc == -EAGAIN && retry_cnt++ < 3);
- if (rc)
- goto Done;
- rc = lcs_sleepon(read, lcs_calcsleeptime(drvr_globals, cmd_code));
- if (rc == -ERETRY)
- goto Done;
- if ((readcmd = read->lancmd_reply_ptr) == NULL || rc == -EAGAIN) {
- if (retry_cnt++ < 3) {
- lcs_debug_event(2,
- "retrying cmd_code=0x02 read_cmd=%p "
- "rc=%d\n",
- (int) cmd_code, readcmd, rc);
- goto Retry;
- } else {
- lcs_debug_event(2,"lcs_lancmd retry count "
- "exceeded giving up\n");
- goto Done;
- }
+ LCS_DBF_TEXT(5, trace, "lcsgtpkt");
+ lcs_hdr = (struct lcs_header *) buffer->data;
+ if (lcs_hdr->offset == LCS_ILLEGAL_OFFSET) {
+ LCS_DBF_TEXT(4, trace, "-eiogpkt");
+ return;
}
- if (readcmd->cmd_code != cmd_code
- || (writecmd->sequence_no - readcmd->sequence_no) > 3) {
- lcs_debug_event(1,
- "readcmd=%p read_cmd_code=%02x write_cmd_code=%02x "
- "read_seq=%04x write_seq=%04x\n",
- readcmd, (int) readcmd->cmd_code,
- (int) cmd_code, (int) readcmd->sequence_no,
- (int) writecmd->sequence_no);
- goto Fail;
- } else {
- switch (cmd_code) {
- case lcs_lanstat:
- if (readcmd->rel_adapter_no != rel_adapter_no) {
- lcs_debug_event(1, "rel adapters differ\n");
- goto Fail;
- }
- /* I got back a funny rel adapter no from stoplan
- * so I'll assume it's broke */
- case lcs_startlan:
- if (readcmd->lan_type != writecmd->lan_type) {
- lcs_debug_event(1,"lan types differ\n");
- goto Fail;
- }
- if (readcmd->return_code != 0) {
- lcs_debug_event(2,"readcmd_return_code=%02x\n",
- (int) readcmd->return_code);
- rc = -ENXIO;
- goto Done;
- }
- case lcs_stoplan:
- /* the stoplan primative should return the
- * adapter type */
- /* it dosen't appar to though */
- case lcs_startup:
- case lcs_shutdown:
-#ifdef CONFIG_IP_MULTICAST
- case lcs_setipm:
- case lcs_delipm:
- case lcs_qipassist:
-#endif
- if (readcmd->return_code != 0)
- goto Fail;
-
+ card = (struct lcs_card *)
+ ((char *) channel - offsetof(struct lcs_card, read));
+ offset = 0;
+ while (lcs_hdr->offset != 0) {
+ if (lcs_hdr->offset <= 0 ||
+ lcs_hdr->offset > LCS_IOBUFFERSIZE ||
+ lcs_hdr->offset < offset) {
+ /* Offset invalid. */
+ card->stats.rx_length_errors++;
+ card->stats.rx_errors++;
+ return;
}
+ /* What kind of frame is it? */
+ if (lcs_hdr->type == LCS_FRAME_TYPE_CONTROL)
+ /* Control frame. */
+ lcs_get_control(card, (struct lcs_cmd *) lcs_hdr);
+ else if (lcs_hdr->type == LCS_FRAME_TYPE_ENET ||
+ lcs_hdr->type == LCS_FRAME_TYPE_TR ||
+ lcs_hdr->type == LCS_FRAME_TYPE_FDDI)
+ /* Normal network packet. */
+ lcs_get_skb(card, (char *)(lcs_hdr + 1),
+ lcs_hdr->offset - offset -
+ sizeof(struct lcs_header));
+ else
+ /* Unknown frame type. */
+ ; // FIXME: error message ?
+ /* Proceed to next frame. */
+ offset = lcs_hdr->offset;
+ lcs_hdr->offset = LCS_ILLEGAL_OFFSET;
+ lcs_hdr = (struct lcs_header *) (buffer->data + offset);
}
-Done:
- lcs_debug_event(2, "lcs_lancmd cmd_code=%02x rc=%d reply_ptr=%p\n",
- (int) cmd_code, rc, read->lancmd_reply_ptr);
-#if LCS_DEBUG
- if (rc && read->lancmd_reply_ptr)
- lcs_debug_display_read_buff("reply", read->lancmd_reply_ptr);
-#endif
- drvr_globals->state = lcs_idle;
- if (rc || (cmd_code != lcs_lanstat
-#ifdef CONFIG_IP_MULTICAST
- && cmd_code != lcs_qipassist
-#endif
- ))
- lcs_resume_read(read);
- return rc;
-Fail:
- rc = -EIO;
- goto Done;
+ /* The buffer is now empty. Make it ready again. */
+ lcs_ready_buffer(&card->read, buffer);
}
-#ifdef CONFIG_IP_MULTICAST
-static int
-lcs_print_ipassists(lcs_qipassist_ctlmsg * reply, char *name,
- char *ipassist_str, lcs_assists mask)
+/**
+ * get network statistics for ifconfig and other user programs
+ */
+struct net_device_stats *
+lcs_getstats(struct net_device *dev)
{
- lcs_good_news("%s: %s supported %s enabled %s\n",
- name, ipassist_str,
- reply->ip_assists_supported & mask ? "yes" : "no",
- reply->ip_assists_enabled & mask ? "yes" : "no");
- return ((reply->ip_assists_supported & reply->
- ip_assists_enabled & mask) ? TRUE : FALSE);
-}
+ struct lcs_card *card;
-static int
-lcs_check_multicast_supported(lcs_drvr_globals * drvr_globals)
-{
- lcs_qipassist_ctlmsg *lcs_reply;
- char *name = drvr_globals->dev->name;
- int rc = FALSE;
- if (lcs_lancmd(lcs_qipassist, drvr_globals->rel_adapter_no,
- drvr_globals LANCMD_DEFAULT_MCAST_PARMS) == 0) {
- lcs_reply = (lcs_qipassist_ctlmsg *)
- drvr_globals->read->lancmd_reply_ptr;
- if (lcs_reply) {
- lcs_print_ipassists(lcs_reply, name, "ip v6",
- lcs_ip_v6_support);
- rc = lcs_print_ipassists(lcs_reply, name, "multicast",
- lcs_multicast_support);
- }
- } else
- lcs_good_news("qipassist failed, ipassists assumed "
- "unsupported for %s\n",name);
- lcs_resume_read(drvr_globals->read);
- return (rc);
+ LCS_DBF_TEXT(4, trace, "netstats");
+ card = (struct lcs_card *) dev->priv;
+ return &card->stats;
}
-#endif
-/*
- * Update network statistics from hardware.
+/**
+ * stop lcs device
+ * This function will be called by user doing ifconfig xxx down
*/
-static int
-lcs_gethwinfo(struct net_device *dev)
+int
+lcs_stop_device(struct net_device *dev)
{
- lcs_drvr_globals *drvr_globals = ((lcs_drvr_globals *) dev->priv);
- lcs_lanstat_reply *lcs_reply;
- struct net_device_stats *stats;
+ struct lcs_card *card;
int rc;
- lcs_read_globals *read = drvr_globals->read;
-
- if ((rc = lcs_lancmd(lcs_lanstat,
- drvr_globals->rel_adapter_no,
- drvr_globals LANCMD_DEFAULT_MCAST_PARMS)) == 0) {
-
- lcs_reply = (lcs_lanstat_reply *)
- read->lancmd_reply_ptr;
- if (lcs_reply) {
- stats = &drvr_globals->stats;
- stats->rx_packets = lcs_reply->num_packets_rx_from_lan;
- stats->rx_errors = lcs_reply->num_rx_errors_detected;
- stats->rx_length_errors =
- lcs_reply->num_rx_packets_too_large;
- stats->rx_missed_errors =
- lcs_reply->num_rx_discarded_nobuffs_avail;
- stats->tx_packets = lcs_reply->num_packets_tx_on_lan;
- stats->tx_errors = lcs_reply->num_tx_errors_detected;
- stats->tx_aborted_errors =
- lcs_reply->num_tx_packets_disgarded;
- }
- }
-#if LCS_DEBUG
- else
- lcs_debug_event(2, "gethwinfo failed\n");
-#endif
- lcs_resume_read(read);
- return rc;
-}
-/*
- * Displays the mac address etc. on startup.
- */
-static void
-lcs_displayinfo(struct net_device *dev)
-{
- lcs_drvr_globals *drvr_globals = ((lcs_drvr_globals *) dev->priv);
- u8 *mac_address = dev->dev_addr;
- lcs_read_globals *read = drvr_globals->read;
- lcs_write_globals *write = drvr_globals->write;
-
- lcs_good_news
- ("\nlcs: %s configured as follows read subchannel=%x "
- "write subchannel=%x\n"
- "read_devno=%04x write_devno=%04x\n"
- "hw_address=%02X:%02X:%02X:%02X:%02X:%02X rel_adapter_no=%d\n",
- dev->name, read->subchannel, write->subchannel, (int) read->devno,
- (int) write->devno, (int) mac_address[0], (int) mac_address[1],
- (int) mac_address[2], (int) mac_address[3], (int) mac_address[4],
- (int) mac_address[5], drvr_globals->rel_adapter_no);
-}
-
-/* shutdown osa card */
-
-static int
-lcs_halt_channels(lcs_drvr_globals * drvr_globals, int delay)
-{
- int rc, rc2;
- if (delay & 1) {
-/* [arnd] maybe better rethink/rewrite this whole function */
- set_current_state(TASK_INTERRUPTIBLE);
- schedule_timeout(2);
- }
- rc = lcs_hsch(drvr_globals->read);
- if (delay & 2) {
-
- set_current_state(TASK_INTERRUPTIBLE);
- schedule_timeout(2);
- }
- drvr_globals->read->chan_busy_state = chan_dead;
- rc2 = lcs_hsch(drvr_globals->write);
- if (delay & 4) {
-
- set_current_state(TASK_INTERRUPTIBLE);
- schedule_timeout(2);
- }
- drvr_globals->write->chan_busy_state = chan_dead;
- return (rc ? rc : rc2);
-}
-
-static int
-lcs_stopcard(lcs_drvr_globals * drvr_globals)
-{
- int rc1, rc2, rc3;
-
- rc1 = rc2 = rc3 = 0;
- drvr_globals->up = FALSE;
- lcs_debug_event(2, "lcs_stopcard called\n");
- if (drvr_globals->state == lcs_interrupted)
- drvr_globals->state = lcs_idle;
- /* Make sure that we don't attempt to resume a channel */
- /* that was halted or dead to avoid the -ENOTCONNECTED */
- /* errors reported from Ingo on resume_IO */
- if (drvr_globals->read->chan_busy_state != chan_dead &&
- drvr_globals->write->chan_busy_state != chan_dead) {
- rc1 = lcs_lancmd(lcs_stoplan, drvr_globals->rel_adapter_no,
- drvr_globals LANCMD_DEFAULT_MCAST_PARMS);
- /* We probably haven't got a good relative adapter no */
- if (drvr_globals->rel_adapter_no != lcs_invalid_adapter_no)
- rc2 = lcs_lancmd(lcs_shutdown, 0, drvr_globals
- LANCMD_DEFAULT_MCAST_PARMS);
- }
- lcs_resetstate(drvr_globals);
- rc3 = lcs_halt_channels(drvr_globals, 7);
- lcs_resetstate(drvr_globals);
- return (rc1 ? rc1 : (rc2 ? rc2 : rc3));
-}
-
-static void
-lcs_initwriteccws(lcs_write_globals * write)
-{
- initccws(write->ccw, write->iobuff, LCS_NUM_TX_BUFFS, ccw_write, 0,
- CCW_FLAG_CC | CCW_FLAG_SLI | CCW_FLAG_SUSPEND);
- write->prepare_idx = 0;
- write->tx_idx = LCS_NUM_TX_BUFFS - 1;
- write->resume_queued = FALSE;
- write->bytes_still_being_txed = write->pkts_still_being_txed = 0;
- write->adjusted_last_bytes_still_being_txed = 0;
- write->last_resume_time = lcs_get_tod();
- write->chan_busy_state = chan_starting_up;
-}
-
-static void
-lcs_startreadio(lcs_read_globals * read)
-{
- int cnt;
- ccw1_t *ccws;
- lcs_debug_event(0, "lcs_startreadio read_globals=%p\n", read);
- ccws = read->ccw;
- for (cnt = 0; cnt < LCS_NUM_RX_BUFFS; cnt++, ccws++) {
- ccws->count = LCS_IOBUFFSIZE;
- ccws->flags = CCW_FLAG_CC | CCW_FLAG_SLI |
- (cnt ==
- (LCS_NUM_RX_BUFFS - 1) ? CCW_FLAG_SUSPEND : CCW_FLAG_PCI)
-#if LCS_USE_IDALS
- | (ccws->flags & CCW_FLAG_IDA)
-#endif
- ;
-#if LCS_ZERO_COPY_READ
- ((lcs_header_type *) read->skb_buff[cnt]->data)->offset =
- LCS_ILLEGAL_OFFSET;
-#else
- ((lcs_header_type *) read->iobuff[cnt])->offset =
- LCS_ILLEGAL_OFFSET;
-#endif
- }
- read->lancmd_reply_ptr = NULL;
- read->rx_idx = 0;
- read->chan_busy_state = chan_starting_up;
- lcs_doio(read, DOIO_DENY_PREFETCH | DOIO_ALLOW_SUSPEND
- /*|DOIO_REPORT_ALL */ );
-
-}
-
-static void
-lcs_startwriteio(lcs_write_globals * write)
-{
- lcs_debug_event(0, "lcs_startwriteio write_globals=%p\n", write);
- lcs_initwriteccws(write);
- lcs_doio(write,DOIO_DENY_PREFETCH | DOIO_ALLOW_SUSPEND
- /* | DOIO_REPORT_ALL */
- );
-}
-
-static void
-lcs_restartreadio(lcs_drvr_globals * drvr_globals)
-{
- lcs_debug_event(2, "lcs_restartreadio\n");
- lcs_wakeup(drvr_globals->read, -EAGAIN);
- lcs_startreadio(drvr_globals->read);
-}
-
-static void
-lcs_queued_restartreadio(lcs_drvr_globals * drvr_globals)
-{
-
- if (!lcs_drvr_globals_valid(drvr_globals))
- goto Done2;
- if (drvr_globals->state == lcs_idle
- || drvr_globals->state == lcs_halting_subchannels)
- goto Done1;
- lcs_restartreadio(drvr_globals);
-Done1:
- lcs_usage_free_drvr_globals(drvr_globals);
-Done2:
- MOD_DEC_USE_COUNT;
-}
-
-static void
-lcs_restartwriteio(lcs_drvr_globals * drvr_globals)
-{
- lcs_write_globals *write;
-
- write = drvr_globals->write;
- write->bytes_still_being_txed = 0;
- drvr_globals->stats.tx_dropped += write->pkts_still_being_txed;
- if (!use_hw_stats)
- drvr_globals->stats.tx_errors += write->pkts_still_being_txed;
- write->pkts_still_being_txed = 0;
- switch (drvr_globals->state) {
- case lcs_doing_io:
- netif_wake_queue(drvr_globals->dev);
- break;
- case lcs_idle_requesting_channels_for_lancmds:
- case lcs_requesting_channels_for_lancmds:
- case lcs_got_write_channel_for_lancmds:
- lcs_wakeup(write, 0);
- break;
- case lcs_doing_lancmds:
- lcs_wakeup(write, -EAGAIN);
- break;
- default:
- break;
- }
- lcs_startwriteio(write);
-}
-
-static void
-lcs_queued_restartwriteio(lcs_drvr_globals * drvr_globals)
-{
- if (!lcs_drvr_globals_valid(drvr_globals))
- goto Done2;
- if (drvr_globals->state == lcs_idle ||
- drvr_globals->state == lcs_halting_subchannels)
- goto Done1;
- lcs_restartwriteio(drvr_globals);
-Done1:
- lcs_usage_free_drvr_globals(drvr_globals);
-Done2:
+ LCS_DBF_TEXT(2, trace, "stopdev");
+ card = (struct lcs_card *) dev->priv;
+ netif_stop_queue(dev);
+ // FIXME: really free the net_device here ?!?
+ kfree(card->dev);
+ rc = lcs_stopcard(card);
+ if (rc)
+ PRINT_ERR("Try it again!\n ");
MOD_DEC_USE_COUNT;
+ return rc;
}
-static void
-lcs_startallio(lcs_drvr_globals * drvr_globals)
-{
- if (drvr_globals->dev)
- netif_stop_queue(drvr_globals->dev);
- drvr_globals->state = lcs_idle_requesting_channels_for_lancmds;
- lcs_startreadio(drvr_globals->read);
- lcs_initqueue(drvr_globals->write);
- lcs_startwriteio(drvr_globals->write);
- lcs_sleepon(drvr_globals->write, 0);
- drvr_globals->state = lcs_doing_lancmds;
-}
-
-/*
- * The osa detection/startup routine called from lcs_probe on
- * bootup & lcs_open this is called after the irq's are allocated &
- * successful sensing of the read & write subchannels.
+/**
+ * start lcs device and make it runnable
+ * This function will be called by user doing ifconfig xxx up
*/
-
-static int
-lcs_detect(lcs_drvr_globals * drvr_globals, s16 forced_port_no, u8 hint_port_no,
- u8 max_port_no, lcs_frame_type frame_type, u8 * mac_address)
+int
+lcs_open_device(struct net_device *dev)
{
- int rc = 0, successful_startup = FALSE;
- int rel_adapter_no, rel_adapter_idx;
- lcs_read_globals *read;
- lcs_write_globals *write;
- lcs_lanstat_reply *lcs_reply;
-
- read = drvr_globals->read;
- write = drvr_globals->write;
-#ifdef CONFIG_IP_MULTICAST
- spin_lock_init(&drvr_globals->ipm_lock);
-#endif
- lcs_debug_event(1, "Entering detect read subchannel=%X\n",
- (unsigned) drvr_globals->read->subchannel);
- if (drvr_globals->dev)
- netif_stop_queue(drvr_globals->dev);
- do {
- lcs_debug_event(1, "lcs_detect retry_cnt=%d\n",
- drvr_globals->retry_cnt);
- if (atomic_read(&drvr_globals->retry_cnt) >= 0) {
- if (atomic_read(&drvr_globals->retry_cnt) > 0)
- lcs_resetstate(drvr_globals);
- if (atomic_read(&drvr_globals->retry_cnt) > 1
- && rc != -ERETRY) {
- /* Time to change tactics */
- rc = lcs_stopcard(drvr_globals);
- } else
- rc = lcs_halt_channels(drvr_globals,
- (atomic_read(&drvr_globals->retry_cnt)
- ==0) ? 0 : 7);
- if (rc)
- continue;
- lcs_startallio(drvr_globals);
- if ((rc =
- lcs_lancmd(lcs_startup, 0,
- drvr_globals
- LANCMD_DEFAULT_MCAST_PARMS)))
- continue;
- else
- successful_startup = TRUE;
- } else if ((rc = lcs_lgw_common(drvr_globals, lcs_startup)))
- continue;
- else
- successful_startup = TRUE;
- for (rel_adapter_idx = 0;
- rel_adapter_idx <= max_port_no &&
- (rc == -ENXIO || rel_adapter_idx == 0);
- rel_adapter_idx++) {
- if (forced_port_no != -1) {
- if (rel_adapter_idx > 0)
- break;
- rel_adapter_no = forced_port_no;
- } else {
- rel_adapter_no =
- (rel_adapter_idx ==
- 0 ? hint_port_no : (rel_adapter_idx ==
- hint_port_no ? 0 :
- rel_adapter_idx));
- }
- lcs_debug_event(2,
- "Trying startlan rel adapter no %u\n",
- (unsigned) rel_adapter_no);
- rc = -1;
- if (frame_type == lcs_autodetect_type) {
-#ifdef CONFIG_NET_ETHERNET
- drvr_globals->lan_type = lcs_enet_type;
- rc = lcs_lancmd(lcs_startlan, rel_adapter_no,
- drvr_globals
- LANCMD_DEFAULT_MCAST_PARMS);
- if (rc == 0)
- goto lcs_good_detect;
-#endif
-#ifdef CONFIG_TR
- drvr_globals->lan_type = lcs_token_ring_type;
- rc = lcs_lancmd(lcs_startlan, rel_adapter_no,
- drvr_globals
- LANCMD_DEFAULT_MCAST_PARMS);
- if (rc == 0)
- goto lcs_good_detect;
-#endif
-#ifdef CONFIG_FDDI
- drvr_globals->lan_type = lcs_fddi_type;
- rc = lcs_lancmd(lcs_startlan, rel_adapter_no,
- drvr_globals
- LANCMD_DEFAULT_MCAST_PARMS);
-#endif
- } else {
- drvr_globals->lan_type = frame_type;
- rc = lcs_lancmd(lcs_startlan, rel_adapter_no,
- drvr_globals
- LANCMD_DEFAULT_MCAST_PARMS);
- }
-lcs_good_detect:
- if (rc == 0) {
- lcs_debug_event(2,"Found relative adapter "
- "number %d\n",
- rel_adapter_no);
- drvr_globals->rel_adapter_no = rel_adapter_no;
-lcs_do_lanstat:
- rc = lcs_lancmd(lcs_lanstat,
- drvr_globals->rel_adapter_no,
- drvr_globals
- LANCMD_DEFAULT_MCAST_PARMS);
- if (!rc) {
- lcs_reply = (lcs_lanstat_reply *)
- drvr_globals->read->
- lancmd_reply_ptr;
- if (lcs_reply == NULL)
- goto lcs_do_lanstat;
- memcpy(mac_address,
- lcs_reply->mac_address,
- LCS_ADDR_LEN);
- lcs_resume_read(drvr_globals->read);
- drvr_globals->up = TRUE;
- return (0);
- }
- }
- }
-
- } while ((rc == -EAGAIN || rc == -ETIMEDOUT || rc == -ERETRY)
- && atomic_inc_return(&drvr_globals->retry_cnt) < 5);
- if (successful_startup && rc)
- lcs_bad_news
- ("A partially successful startup read_devno=%04x "
- "write_devno=%4x was detected rc=%d\n"
- "please check your configuration parameters,cables "
- "& connection to the network.\n",
- drvr_globals->read->devno,drvr_globals->write->devno,rc);
- return (rc == EINTR ? -EINTR : -ENODEV);
-}
+ struct lcs_card *card;
+ int rc;
-/* Called by user doing ifconfig <network_device e.g. tr0> up */
-static int
-lcs_open(struct net_device *dev)
-{
- int rc = 0;
- lcs_drvr_globals *drvr_globals;
- lcs_read_globals *read;
- lcs_write_globals *write;
+ LCS_DBF_TEXT(2, trace, "opendev");
+ card = (struct lcs_card *) dev->priv;
+ /* initialize statistics */
+ rc = lcs_detect(card);
+ if (rc) {
+ PRINT_ERR("LCS:Error in opening device!\n");
-#if LCS_DEBUG_RESUME
- lcs_read_resume_cnt = lcs_write_resume_cnt = 0;
-#endif
- drvr_globals = ((lcs_drvr_globals *) dev->priv);
- read = drvr_globals->read;
- write = drvr_globals->write;
- drvr_globals->state = lcs_idle;
- if (use_hw_stats == FALSE)
- memset(&drvr_globals->stats, 0,
- sizeof (struct net_device_stats));
- atomic_set(&drvr_globals->retry_cnt, 0);
- if ((rc = lcs_detect(drvr_globals, drvr_globals->rel_adapter_no, 0, 0,
- drvr_globals->lan_type, dev->dev_addr))) {
- lcs_debug_event(2, "Opening %s failed errcode=%d.\n",
- dev->name,rc);
- lcs_resetstate(drvr_globals);
} else {
MOD_INC_USE_COUNT;
- lcs_start_doing_io(drvr_globals);
- lcs_debug_event(2, "Successfully opened %s\n", dev->name);
-#if LCS_DEBUG && !LCS_PRINTK_DEBUG
- /* When debugging we are usually only intrested till
- * the device is open. */
- if (lcs_id && lcs_id->level != DEBUG_MAX_LEVEL)
- debug_set_level(lcs_id, 0);
-#endif
+ netif_wake_queue(dev);
+ card->state = DEV_STATE_UP;
}
return rc;
}
-/* Called by user doing ifconfig <network_device e.g. tr0> down */
-static int
-lcs_close(struct net_device *dev)
+/**
+ * show function for portno called by cat or similar things
+ */
+static ssize_t
+lcs_portno_show (struct device *dev, char *buf, size_t count,
+ loff_t off)
{
- lcs_debug_event(2, "lcs_close called\n");
- netif_stop_queue(dev);
- lcs_stopcard((lcs_drvr_globals *) dev->priv);
- MOD_DEC_USE_COUNT;
- return (0);
-}
+ struct lcs_card *card;
-static int
-lcs_txpacket(struct sk_buff *skb, struct net_device *dev)
-{
- lcs_drvr_globals *drvr_globals;
- lcs_write_globals *write;
- u16 pkt_len;
- lcs_header_type *tx_lcs_header_ptr;
- uint64_t tod, delta_tx;
- struct net_device_stats *stats;
-
- lcs_debug_event(2, "lcs_txpacket\n");
- if (dev == NULL || dev->priv == NULL) {
- lcs_debug_event(1, "lcs txpacket detected sick code\n");
- return -EINVAL;
- }
- drvr_globals = ((lcs_drvr_globals *) dev->priv);
- write = drvr_globals->write;
- tod = lcs_get_tod();
- delta_tx = tod - write->last_lcs_txpacket_time;
- write->last_lcs_txpacket_time = tod;
- stats = &drvr_globals->stats;
- if (write->chan_busy_state != chan_busy &&
- write->chan_busy_state != chan_idle) {
- dst_link_failure(skb);
- dev_kfree_skb(skb);
- stats->tx_dropped++;
- if (!use_hw_stats) {
- stats->tx_errors++;
- stats->tx_carrier_errors++;
- }
- return (0);
- }
- if (netif_queue_stopped(dev)) {
- lcs_debug_event(1, "lcs_txpacket netif_is_busy dev=%p\n", dev);
+ card = (struct lcs_card *)dev->driver_data;
- stats->tx_dropped++;
- return -EBUSY;
- }
- if (!skb) {
- lcs_debug_event(1, "lcs_txpacket skb=NULL dev=%p\n", dev);
- stats->tx_dropped++;
- if (!use_hw_stats)
- stats->tx_errors++;
- return -EIO;
- }
- lcs_debug_event(2, "skb_len %d\n", skb->len);
- pkt_len = skb->len;
-#if 0
- /* This apparently can be omitted & saves us a lot of hassle */
- pkt_len = (ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN);
-#endif
- dev->trans_start = jiffies;
- lcs_chan_lock(write);
- if ((tx_lcs_header_ptr = lcs_getfreetxbuff(write,
- drvr_globals->lan_type,
- drvr_globals->rel_adapter_no,
- pkt_len)) == NULL) {
- netif_stop_queue(dev);
- stats->tx_dropped++;
- lcs_debug_event(1,"lcs_txpacket no tx buffers "
- "available dev=%p\n",dev);
- lcs_resume_write(write);
- lcs_chan_unlock(write);
- return -EBUSY;
- }
- write->bytes_still_being_txed += pkt_len;
- write->pkts_still_being_txed++;
- memcpy(++tx_lcs_header_ptr, skb->data, pkt_len);
- dev_kfree_skb(skb);
+ if (off || !card)
+ return 0;
- if (lcs_should_queue_write(write, tod)) {
- lcs_queue_write_if_necessary(write);
- } else {
- lcs_resume_write(write);
- }
- lcs_chan_unlock(write);
- return (0);
+ return snprintf(buf, count, "%d\n", card->portno);
}
-/*
- * Used by ifconfig & the proc filesystem to receive network statistics
+/**
+ * store the value which is piped to file portno
*/
-static struct net_device_stats *
-lcs_getstats(struct net_device *dev)
+static ssize_t
+lcs_portno_store (struct device *dev, const char *buf, size_t count,
+ loff_t off)
{
- lcs_drvr_globals *drvr_globals = (lcs_drvr_globals *) dev->priv;
- lcs_debug_event(2, "lcs_enet_stats\n");
-
- if (dev == NULL || dev->priv == NULL)
- return NULL; /* shouldn't happen */
- if (use_hw_stats) {
- if (drvr_globals->doing_lanstat)
-#warning FIXME: [kj] Using sleep_on derivative, is racy. consider using wait_event instead
- interruptible_sleep_on(&drvr_globals->lanstat_wait);
- else {
- if (drvr_globals->state == lcs_doing_io) {
- drvr_globals->doing_lanstat = TRUE;
- lcs_gethwinfo(dev);
- drvr_globals->doing_lanstat = FALSE;
- netif_wake_queue(dev);
- /* Gracefully start the lan again */
- drvr_globals->state = lcs_doing_io;
- /* Wake up processes awaiting
- * lanstat results */
- wake_up_interruptible(&drvr_globals->
- lanstat_wait);
- }
- }
- }
- return &((lcs_drvr_globals *) dev->priv)->stats;
+ struct lcs_card *card;
+ int value;
-}
+ card = (struct lcs_card *)dev->driver_data;
-#if !LCS_CHANDEV
-/*
- * Used to setup kernel parameters for the osa card
- * read the printk's in the function for a description of how it
- * should be called.
- */
-static __inline__ lcs_model_info *
-lcs_get_model_info_by_idx(int idx)
-{
- return (idx < LCS_CURR_NUM_MODELS ? &lcs_models[idx] :
- (lcs_model_info *) & additional_model_info
- [(idx - LCS_CURR_NUM_MODELS) << 1]);
-}
-static void
-lcs_display_conf(void)
-{
- int cu_idx;
- lcs_model_info *model_info;
-
- lcs_good_news("\nlcs configured to use %s statistics,\n"
- "ip checksumming of received packets is %s.\n"
- "autodetection is %s ignore sense is %s.\n",
- (use_hw_stats ? "hw" : "sw"),
- (checksum_received_ip_pkts ? "on" : "off"),
- (noauto ? "off" : "on"), (ignore_sense ? "on" : "off"));
- lcs_good_news("configured to detect\n");
- for (cu_idx = 0; cu_idx < LCS_MAX_NUM_MODELS; cu_idx++) {
- model_info = lcs_get_model_info_by_idx(cu_idx);
- if ((model_info->max_rel_adapter_no == 0))
- break;
- lcs_good_news("cu_model 0x%02X,%d rel_adapter(s)\n",
- model_info->cu_model,
- model_info->max_rel_adapter_no);
- }
-}
+ if (off || !card)
+ return 0;
-MODULE_AUTHOR
-("(C) 1999 IBM Corp. by DJ Barrow <djbarrow@de.ibm.com,barrow_dj@yahoo.com>");
-MODULE_DESCRIPTION("Linux for S/390 Lan Channel Station Network Driver");
-MODULE_LICENSE("GPL");
+ sscanf(buf, "%u", &value);
+ /* TODO: sanity checks */
+ card->portno = value;
-#ifdef MODULE
-MODULE_PARM(use_hw_stats, "i");
-MODULE_PARM_DESC(use_hw_stats,
- "Get network stats from LANSTAT lcs primitive as opposed to "
- "doing it in software\n"
- "this isn't used by MVS not recommended "
- "& dosen't appear to work usually\n");
-MODULE_PARM(checksum_received_ip_pkts, "i");
-MODULE_PARM_DESC(checksum_received_ip_pkts,
- "Do ip checksumming on inbound packets");
-
-MODULE_PARM(additional_model_info, "0-" LCS_ADDITIONAL_MODELS_X2_STR "i");
-MODULE_PARM_DESC(additional_model_info,
- "This is made up of sets of model/max rel adapter no pairs\n"
- "e.g. insmod additional_model_info=0x70,3 will look for "
- "2 ports on a model 0x70.\n");
-MODULE_PARM(devno_portno_pairs, "0-" LCS_MAX_FORCED_DEVNOS_X2_STR "i");
-MODULE_PARM_DESC(devno_portno_pairs, "devno,rel_adapter_no pairs\n"
- "rel adapter no of -1 indicates don't use this adapter.");
-MODULE_PARM(noauto, "i");
-MODULE_PARM_DESC(noauto,
- "set this equal to 1 if you want autodetection off, "
- "this means you have\n"
- "explicitly configure LCS devices with the "
- "devno_portno_pairs module parameter\n.");
-MODULE_PARM(ignore_sense, "i");
-MODULE_PARM_DESC(ignore_sense,
- "set this to 1 if you wish to ignore sense information "
- "from the device layer for\n"
- "non auto detected devices.\n");
-#else
-
-#if LINUX_VERSION_CODE>=KERNEL_VERSION(2,3,0)
-static int __init
-lcs_setup_models(char *str)
-#else
-void __init
-lcs_setup_models(char *str, int *ints)
-#endif
-{
- int max_parms = LCS_MAX_NUM_PARMS - (LCS_CURR_NUM_MODELS << 1);
- int num_new_models;
-#if LINUX_VERSION_CODE>=KERNEL_VERSION(2,3,0)
- int ints[(LCS_ADDITIONAL_MODELS << 1) + 1];
- get_options(str, (LCS_ADDITIONAL_MODELS << 1), ints);
-#endif
- num_new_models = (ints[0] - 2);
- if (num_new_models < 0)
- num_new_models = 0;
- else if (num_new_models & 1)
- num_new_models = -1;
- else
- num_new_models >>= 1;
- if ((ints[0] >= 1 && ints[0] <= max_parms) && num_new_models >= 0) {
- use_hw_stats = ints[1];
- if (ints[0] >= 2)
- checksum_received_ip_pkts = ints[2];
- if (num_new_models) {
- memcpy(&additional_model_info[0], &ints[3],
- num_new_models * sizeof (lcs_model_info));
- }
- lcs_display_conf();
- } else {
- lcs_bad_news("lcs_setup: incorrect number of arguments\n");
- printk("Usage: lcs=hw-stats,ip-checksumming,additional "
- "cu model/max rel adapter no. pairs. \n");
- printk(" The driver now auto detects how the card is "
- "configured.\n");
- printk(" e.g lcs=1,1,0x60,2\n");
- printk(" Will collect network stats from LANSTAT lcs "
- "primitive as opposed\n");
- printk(" to doing it in software for ifconfig (this isn't "
- "used by MVS & doesn't\n");
- printk(" to be widely supported, ip checksum received "
- "packets\n");
- printk(" and detect 0x3088 model 0x60 devices\n");
- printk(" will look for 2 ports per card associated with "
- "this model.\n");
- printk(" The default is to use sw stats, with ip "
- "checksumming off,\n");
- printk(" this improves performance, network hw uses "
- "a crc32\n");
- printk(" ( crc64 for fddi ) which should be adequete to "
- "gaurantee\n");
- printk(" integrity for normal use\n");
- printk(" however, financial institutions etc. may like "
- "the additional\n");
- printk(" security of ip checksumming.\n");
- }
-#if LINUX_VERSION_CODE>=KERNEL_VERSION(2,3,0)
- return (1);
-#endif
-}
+ return count;
-#if LINUX_VERSION_CODE>=KERNEL_VERSION(2,3,0)
-static int __init
-lcs_setup_devnos(char *str)
-#else
-void __init
-lcs_setup_devnos(char *str, int *ints)
-#endif
-{
- int fixed_devnos;
-#if LINUX_VERSION_CODE>=KERNEL_VERSION(2,3,0)
- int ints[(LCS_MAX_FORCED_DEVNOS << 1) + 1];
- get_options(str, (LCS_MAX_FORCED_DEVNOS << 1), ints);
-#endif
- fixed_devnos = ints[0];
- if ((fixed_devnos & 1) == 0 && fixed_devnos > 0) {
- memcpy(devno_portno_pairs, &ints[1],
- sizeof (int) * fixed_devnos);
- } else {
- lcs_bad_news("lcs_setup: incorrect number of arguments\n");
- printk("usage lcs_devno=devno,rel_adapter_no pairs\n");
- printk
- ("rel adapter no of -1 indicates don't use this adapter.\n");
- }
-#if LINUX_VERSION_CODE>=KERNEL_VERSION(2,3,0)
- return (1);
-#endif
}
-#if LINUX_VERSION_CODE>=KERNEL_VERSION(2,3,0)
-static int __init
-lcs_noauto(char *str)
-#else
-void __init
-lcs_noauto(char *str, int *ints)
-#endif
-{
- noauto = 1;
-#if LINUX_VERSION_CODE>=KERNEL_VERSION(2,3,0)
- return (1);
-#endif
-}
+static DEVICE_ATTR(portno, 0644, lcs_portno_show, lcs_portno_store);
-#if LINUX_VERSION_CODE>=KERNEL_VERSION(2,3,0)
-static int __init
-lcs_ignore_sense(char *str)
-#else
-void __init
-lcs_ignore_sense(char *str, int *ints)
-#endif
+/**
+ * lcs_probe_device is called on establishing a new ccwgroup_device.
+ */
+int
+lcs_probe_device(struct ccwgroup_device *ccwgdev)
+{
+ struct lcs_card *card;
+ int ret;
+
+ if (!get_device(&ccwgdev->dev))
+ return -ENODEV;
+
+ LCS_DBF_TEXT(3, setup, "add_dev");
+ card = lcs_alloc_card();
+ if (!card) {
+ PRINT_ERR("Allocation of lcs card failed\n");
+ put_device(&ccwgdev->dev);
+ return -ENOMEM;
+ }
+ ret = device_create_file(&ccwgdev->dev, &dev_attr_portno);
+ if (ret) {
+ PRINT_ERR("Creating attributes failed");
+ lcs_free_card(card);
+ put_device(&ccwgdev->dev);
+ return ret;
+ }
+ ccwgdev->dev.driver_data = card;
+ snprintf(ccwgdev->dev.name, DEVICE_NAME_SIZE, "%s",
+ cu3088_type[ccwgdev->cdev[0]->id.driver_info]);
+ ccwgdev->cdev[0]->dev.driver_data = card;
+ ccwgdev->cdev[0]->handler = lcs_irq;
+ ccwgdev->cdev[1]->dev.driver_data = card;
+ ccwgdev->cdev[1]->handler = lcs_irq;
+ return 0;
+}
+
+/**
+ * lcs_new_device will be called by setting the group device online.
+ */
+int
+lcs_new_device(struct ccwgroup_device *ccwgdev)
{
- ignore_sense = 1;
-#if LINUX_VERSION_CODE>=KERNEL_VERSION(2,3,0)
- return (1);
-#endif
-}
+ struct lcs_card *card;
+ struct net_device *dev;
+ int rc;
-#if LINUX_VERSION_CODE>=KERNEL_VERSION(2,3,0)
-__setup("lcs_devno=", lcs_setup_devnos);
-__setup("lcs=", lcs_setup_models);
-__setup("lcs_noauto", lcs_noauto);
-__setup("lcs_ignore_sense", lcs_ignore_sense);
-#endif
-#endif
+ card = (struct lcs_card *)ccwgdev->dev.driver_data;
+ if (!card)
+ return -ENODEV;
-static lcs_inline int
-lcs_model_supported(s390_dev_info_t * dev_info_ptr, int *forced_rel_adapter_no)
-{
- int cu_idx, dev_port_idx;
- lcs_model_info *model_info;
- lcs_dev_portno_pair *dev_portno;
-
- *forced_rel_adapter_no = -1;
- for (dev_port_idx = 0; dev_port_idx < LCS_MAX_FORCED_DEVNOS;
- dev_port_idx++) {
- dev_portno = &lcs_dev_portno[dev_port_idx];
- if (dev_portno->portno == -2)
- break;
- /*
- Check are we dealing with the device passed in as
- a parameter.
- */
- if ((dev_portno->devno & 0xfffe) ==
- (dev_info_ptr->devno & 0xfffe)) {
- /* portno=-1 we want to ignore this device */
- if (dev_portno->portno == -1)
- return (-1);
- else {
- *forced_rel_adapter_no = dev_portno->portno;
- break;
- }
- }
- }
- if (noauto && *forced_rel_adapter_no == -1)
- return (-1);
- if (ignore_sense)
- return (*forced_rel_adapter_no);
- if (dev_info_ptr->sid_data.cu_type != LCS_CUTYPE)
- return (-1);
- for (cu_idx = 0; cu_idx < LCS_MAX_NUM_MODELS; cu_idx++) {
- model_info = lcs_get_model_info_by_idx(cu_idx);
- if (model_info->max_rel_adapter_no == 0)
- break;
- if (model_info->cu_model == dev_info_ptr->sid_data.cu_model) {
- if (*forced_rel_adapter_no == -1)
- return (model_info->max_rel_adapter_no);
- else
- return (*forced_rel_adapter_no);
- }
- }
- return (-1);
-}
+ card->read.ccwdev = ccwgdev->cdev[0];
+ card->write.ccwdev = ccwgdev->cdev[1];
-#endif /* LCS_CHANDEV */
+ ccw_device_set_online(card->read.ccwdev);
+ ccw_device_set_online(card->write.ccwdev);
-#if !LCS_CHANDEV
-void
-lcs_unregister(struct net_device *dev, u8 lan_type)
-{
- if (dev) {
- switch (lan_type) {
-#ifdef CONFIG_FDDI
- case lcs_fddi_type:
- unregister_netdev(dev);
- break;
-#endif
+ LCS_DBF_TEXT(3, setup, "lcsnewdv");
+ rc = lcs_setup_card(card);
+ if (rc) {
+ LCS_DBF_TEXT(3, setup, "errinit");
+ PRINT_ERR("LCS card Initialization failed\n");
+ lcs_free_card(card);
+ return rc;
+ }
+
+ rc = lcs_detect(card);
+ if (rc) {
+ lcs_stopcard(card);
+ lcs_cleanup_card(card);
+ lcs_free_card(card);
+ return -ENODEV;
+ }
+ switch (card->lan_type) {
#ifdef CONFIG_NET_ETHERNET
- case lcs_enet_type:
- unregister_netdev(dev);
- break;
+ case LCS_FRAME_TYPE_ENET:
+ card->lan_type_trans = eth_type_trans;
+ dev = init_etherdev(NULL,0);
+ break;
#endif
#ifdef CONFIG_TR
- case lcs_token_ring_type:
- unregister_trdev(dev);
- break;
+ case LCS_FRAME_TYPE_TR:
+ card->lan_type_trans = tr_type_trans;
+ dev = init_trdev(NULL,0);
+ break;
#endif
- default:
- lcs_bad_news
- ("lcs_unregister attempt to unregister %p "
- "unknown device type\n",
- dev);
- return;
- }
-
- }
-}
+#ifdef CONFIG_FDDI
+ case LCS_FRAME_TYPE_FDDI:
+ card->lan_type_trans = fddi_type_trans;
+ dev = init_fddidev(NULL,0);
+ break;
#endif
+ default:
+ LCS_DBF_TEXT(3, setup, "errinit");
+ PRINT_ERR("LCS: Initialization failed\n");
+ PRINT_ERR("LCS: No device found!\n");
+ lcs_cleanup_channel(&card->read);
+ lcs_cleanup_channel(&card->write);
+ lcs_free_card(card);
+ return -ENODEV;
+ }
+ memcpy(dev->dev_addr, card->mac, LCS_MAC_LENGTH);
+ card->dev = dev;
+ dev->priv = card;
+ dev->open = lcs_open_device;
+ dev->stop = lcs_stop_device;
+ dev->hard_start_xmit = lcs_start_xmit;
#ifdef CONFIG_IP_MULTICAST
-static int
-lcs_fix_multicast_list(lcs_drvr_globals * drvr_globals)
-{
- lcs_ipm_list *curr_lmem;
- lcs_state oldstate;
-
- lcs_daemonize("lcs_fix_multi", 0, TRUE);
- if (!lcs_drvr_globals_valid(drvr_globals))
- goto Done2;
- if (drvr_globals->up == FALSE)
- goto Done1;
- oldstate = drvr_globals->state;
- spin_lock(&drvr_globals->ipm_lock);
- for (curr_lmem = drvr_globals->ipm_list; curr_lmem;
- curr_lmem = curr_lmem->next) {
- switch (curr_lmem->ipm_state) {
- case ipm_set_required:
- spin_unlock(&drvr_globals->ipm_lock);
- if (lcs_lancmd
- (lcs_setipm, drvr_globals->rel_adapter_no,
- drvr_globals, 1, &curr_lmem->ipm)) {
- lcs_good_news("lcs_fix_multicast_list "
- "failed to add multicast "
- "entry %x multicast address "
- "table possibly full.\n",
- (u32) curr_lmem->ipm.ip_addr);
- } else
- curr_lmem->ipm_state = ipm_on_card;
- spin_lock(&drvr_globals->ipm_lock);
- break;
- case ipm_delete_required:
- spin_unlock(&drvr_globals->ipm_lock);
- lcs_lancmd(lcs_delipm, drvr_globals->rel_adapter_no,
- drvr_globals, 1, &curr_lmem->ipm);
- spin_lock(&drvr_globals->ipm_lock);
- if (remove_from_list((list **)&drvr_globals->ipm_list,
- (list *)curr_lmem))
- kfree(curr_lmem);
- else
- lcs_bad_news("lcs_fix_multicast_list "
- "detected nonexistant entry"
- " ipm_list=%p member %p\n",
- drvr_globals->ipm_list,curr_lmem);
- break;
- case ipm_on_card:
- break;
- }
- }
- spin_unlock(&drvr_globals->ipm_lock);
- drvr_globals->state = oldstate;
- if (oldstate == lcs_doing_io && drvr_globals->dev)
- netif_wake_queue(drvr_globals->dev);
-Done1:
- atomic_set(&drvr_globals->kernel_thread_lock, 1);
- lcs_usage_free_drvr_globals(drvr_globals);
-Done2:
- MOD_DEC_USE_COUNT;
- return (0);
-}
-
-static void
-lcs_get_mac_for_ipm(__u32 ipm, char *mac, struct net_device *dev)
-{
-#ifdef CONFIG_TR
- if (dev->type == ARPHRD_IEEE802_TR)
- ip_tr_mc_map(ipm, mac);
- else
-#endif
-#if defined(CONFIG_NET_ETHERNET) || defined (CONFIG_FDDI)
- ip_eth_mc_map(ipm, mac);
+ if (lcs_check_multicast_support(card))
+ dev->set_multicast_list = lcs_set_multicast_list;
#endif
+ dev->get_stats = lcs_getstats;
+ netif_stop_queue(dev);
+ lcs_stopcard(card);
+ return 0;
}
-/*
- Owing to a quirk in the osa microcode it is incompatible with its API's
- & we can only add or delete a single ipm entry at a time.
- We are also called with the spin_lock_bh held meaning we in_interrupt is true
- to add & delete multicast entries to get around this we indirectly start
- a kernel thread which executes the io commands necessary at a later
- time.
- We also need ip addresses for the setipm & delipm primatives
- to do this we use lcs_get_mac_for_ipm.
- N.B. We even need to kmalloc GFP_ATOMIC here
-
+/**
+ * lcs_shutdown_device, called when setting the group device offline.
*/
-static void
-lcs_set_multicast_list(struct net_device *dev)
+int
+lcs_shutdown_device(struct ccwgroup_device *ccwgdev)
{
- lcs_drvr_globals *drvr_globals;
- struct ip_mc_list *im4;
- struct in_device *in4_dev;
- int addr_in_list;
+ struct lcs_card *card;
-#if 0 /* LCS doesn't do IPV6 yet. */
- struct ifmcaddr6 *im6;
- struct inet6_dev *in6_dev;
-#endif
- char buf[MAX_ADDR_LEN];
- lcs_ipm_list *curr_lmem, *new_lmem;
- lcs_state oldstate;
+ LCS_DBF_TEXT(3, setup, "shtdndev");
+ card = (struct lcs_card *)ccwgdev->dev.driver_data;
+ if (!card)
+ return -ENODEV;
- if ((dev == NULL) || (dev->priv == NULL)) {
- lcs_bad_news("invalid dev=%p in lcs_set_multicast_list\n", dev);
- return;
- }
- drvr_globals = (lcs_drvr_globals *) dev->priv;
- oldstate = drvr_globals->state;
-#if LINUX_VERSION_CODE >=KERNEL_VERSION(2,3,0)
- if ((in4_dev = in_dev_get(dev)) == NULL)
- return;
- read_lock(&in4_dev->lock);
-#else
- if ((in4_dev = dev->ip_ptr) == NULL)
- return;
-#endif
- /* Pass 1 find the entries to be deleted */
- /* This is done first to avoid overflowing the cards */
- /* onboard multicast list. */
- spin_lock(&drvr_globals->ipm_lock);
- for (curr_lmem = drvr_globals->ipm_list; curr_lmem;
- curr_lmem = curr_lmem->next) {
- addr_in_list = FALSE;
- for (im4 = in4_dev->mc_list; im4; im4 = im4->next) {
- lcs_get_mac_for_ipm(im4->multiaddr, buf, dev);
- if ((memcmp(buf, &curr_lmem->ipm.mac_address,
- LCS_ADDR_LEN) == 0) &&
- (curr_lmem->ipm.ip_addr == im4->multiaddr)) {
- addr_in_list = TRUE;
- break;
- }
-
- }
- if (!addr_in_list)
- curr_lmem->ipm_state = ipm_delete_required;
- }
- /* Pass 2 find the new entries to be added */
- for (im4 = in4_dev->mc_list; im4; im4 = im4->next) {
- lcs_get_mac_for_ipm(im4->multiaddr, buf, dev);
- addr_in_list = FALSE;
- for (curr_lmem = drvr_globals->ipm_list; curr_lmem;
- curr_lmem = curr_lmem->next) {
- if ((memcmp
- (buf, &curr_lmem->ipm.mac_address,
- LCS_ADDR_LEN) == 0)
- && (curr_lmem->ipm.ip_addr == im4->multiaddr)) {
- addr_in_list = TRUE;
- break;
- }
- }
- if (!addr_in_list) {
- new_lmem =
- (lcs_ipm_list *) kmalloc(sizeof (lcs_ipm_list),
- GFP_ATOMIC);
- if (!new_lmem) {
- lcs_good_news
- ("lcs_set_multicast_list failed to add "
- "multicast entry %x not enough memory.\n",
- (u32) im4->multiaddr);
- goto done;
- }
- memset(new_lmem, 0, sizeof (lcs_ipm_list));
- memcpy(&new_lmem->ipm.mac_address, buf, LCS_ADDR_LEN);
- new_lmem->ipm.ip_addr = im4->multiaddr;
- new_lmem->ipm_state = ipm_set_required;
- add_to_list((list **) & drvr_globals->ipm_list,
- (list *) new_lmem);
- }
- }
-#if LINUX_VERSION_CODE >=KERNEL_VERSION(2,3,0)
- read_unlock(&in4_dev->lock);
- in_dev_put(in4_dev);
-#endif
-#if 0 /* LCS doesn't do IPV6 yet */
- if ((in6_dev = in6_dev_get(dev)) == NULL)
- return;
- read_lock_bh(&in6_dev->lock);
- for (im6 = in6_dev->mc_list; im6; im6 = im6->next) {
- }
- read_unlock(&in4_dev->lock);
- in6_dev_put(idev);
-#endif
-done:
- spin_unlock(&drvr_globals->ipm_lock);
- lcs_queue_thread(lcs_fix_multicast_list, drvr_globals);
+ return lcs_stop_device(card->dev);
}
-#endif
-static void
-lcs_set_slow_hw(lcs_drvr_globals * drvr_globals, u16 cu_type, u8 cu_model)
+/**
+ * lcs_remove_device, free buffers and card
+ */
+int
+lcs_remove_device(struct ccwgroup_device *ccwgdev)
{
- drvr_globals->slow_hw =
- ((cu_type == 0x3088
- && (cu_model == 0x8 || cu_model == 0x1f)) ? TRUE : FALSE);
-}
-
-#if LCS_DEBUG && !LCS_PRINTK_DEBUG
+ struct lcs_card *card;
-static void
-lcs_debug_register(void)
-{
- if (lcs_id == NULL) {
- lcs_id = debug_register("lcs", 1, 16, 16 * sizeof (long));
- debug_register_view(lcs_id, &debug_sprintf_view);
- debug_set_level(lcs_id, 0);
- }
+ LCS_DBF_TEXT(3, setup, "remdev");
+ card = (struct lcs_card *)ccwgdev->dev.driver_data;
+ if (!card)
+ return 0;
+ lcs_cleanup_card(card);
+ lcs_free_card(card);
+ put_device(&ccwgdev->dev);
+ return 0;
}
-#endif
-/*
- * The main device detection routine called on bootup or module installation
- * to do. An unfortunately unwieldly function which dosen't lend itself
- * easily to being broken into smaller functional blocks & my reason for
- * hating tabs of 8.
+/**
+ * LCS ccwgroup driver registration
*/
+struct ccwgroup_driver lcs_group_driver = {
+ .name = "lcs",
+ .max_slaves = 2,
+ .driver_id = 0xD3C3E2,
+ .probe = lcs_probe_device,
+ .remove = lcs_remove_device,
+ .set_online = lcs_new_device,
+ .set_offline = lcs_shutdown_device,
+};
-#if LCS_CHANDEV
+/**
+ * LCS Module/Kernel initialization function
+ */
static int
-lcs_probe(chandev_probeinfo * probeinfo)
+__init lcs_init_module(void)
{
- lcs_drvr_globals *drvr_globals = NULL;
- int rc = -ENODEV;
- lcs_write_globals *write;
- lcs_read_globals *read;
- u8 mac_address[LCS_ADDR_LEN];
- struct net_device *dev = NULL;
- char *basename = NULL;
- struct net_device *(*init_netdevfunc) (struct net_device * dev,
- int sizeof_priv) = NULL;
- void (*unreg_netdevfunc) (struct net_device * dev) = NULL;
-
- drvr_globals = lcs_alloc_drvr_globals();
- if (!drvr_globals) {
- lcs_bad_news("lcs_probe kmalloc failed\n");
- return -ENOMEM;
- }
- read = drvr_globals->read;
- write = drvr_globals->write;
- read->subchannel = probeinfo->read.irq;
- write->subchannel = probeinfo->write.irq;
- read->devno = probeinfo->read.devno;
- write->devno = probeinfo->write.devno;
- lcs_set_slow_hw(drvr_globals, probeinfo->read.cu_type,
- probeinfo->read.cu_model);
- if (lcs_allocirq(read) || lcs_allocirq(write)) {
- rc = -ENODEV;
- goto Fail;
- }
- checksum_received_ip_pkts = probeinfo->checksum_received_ip_pkts;
- use_hw_stats = probeinfo->use_hw_stats;
- atomic_set(&drvr_globals->retry_cnt, 0);
- rc = lcs_detect(drvr_globals,
- (probeinfo->device_forced ? probeinfo->
- port_protocol_no : -1), probeinfo->hint_port_no,
- probeinfo->max_port_no, lcs_autodetect_type,
- mac_address);
+ int rc;
+ LCS_DBF_TEXT(0, setup, "lcsinit");
+ PRINT_INFO("Loading %s\n",version);
+ rc = lcs_register_debug_facility();
if (rc) {
- lcs_stopcard(drvr_globals);
- goto Fail;
- } else {
- /*Fill in the fields of the device structure
- * with interface specific values.
- */
- switch (drvr_globals->lan_type) {
-#ifdef CONFIG_NET_ETHERNET
- case lcs_enet_type:
- init_netdevfunc = init_etherdev;
- unreg_netdevfunc = unregister_netdev;
- basename = "eth";
- drvr_globals->lan_type_trans = eth_type_trans;
- break;
-#endif
-#ifdef CONFIG_TR
- case lcs_token_ring_type:
- init_netdevfunc = init_trdev;
- unreg_netdevfunc = unregister_netdev;
- basename = "tr";
- drvr_globals->lan_type_trans = tr_type_trans;
- break;
-#endif
-#ifdef CONFIG_FDDI
- case lcs_fddi_type:
- init_netdevfunc = init_fddidev;
- unreg_netdevfunc = unregister_netdev;
- basename = "fddi";
- drvr_globals->lan_type_trans = fddi_type_trans;
- break;
-#endif
- }
- probeinfo->memory_usage_in_k =
- -(((LCS_READ_ALLOCSIZE * LCS_NUM_RX_BUFFS) +
- (LCS_NUM_TX_BUFFS * LCS_IOBUFFSIZE)) / 1024);
- if ((dev =
- chandev_initnetdevice(probeinfo,
- drvr_globals->rel_adapter_no, NULL,
- 0, basename, init_netdevfunc,
- unreg_netdevfunc)) == 0)
- goto Fail;
- memcpy(dev->dev_addr, mac_address, LCS_ADDR_LEN);
- dev->open = lcs_open;
- dev->stop = lcs_close;
- dev->hard_start_xmit = lcs_txpacket;
- dev->get_stats = lcs_getstats;
-
- drvr_globals->dev = dev;
-
- dev->priv = drvr_globals;
-#ifdef CONFIG_IP_MULTICAST
- dev->set_multicast_list =
- (lcs_check_multicast_supported(drvr_globals)
- ? lcs_set_multicast_list : NULL);
-#endif
- lcs_stopcard(drvr_globals);
- netif_stop_queue(dev);
- lcs_displayinfo(dev);
- lcs_debug_exception(0, "device structure=%p drvr globals=%p\n"
- "read=%p write=%p\n"
- "read_ccws=%p write_cwws=%p\n", dev,
- drvr_globals, drvr_globals->read,
- drvr_globals->write,
- &drvr_globals->read->ccw[0],
- &drvr_globals->write->ccw[0]);
- return 0;
- }
-Fail:
- if (drvr_globals) {
- lcs_freeallirqs(drvr_globals);
- lcs_usage_free_drvr_globals(drvr_globals);
- }
- lcs_debug_event(2, "osa_probe error %d\n", rc);
- return rc;
-}
-#else
-int __init
-lcs_probe(void)
-{
- int read_irq, write_irq;
- s390_dev_info_t read_devinfo, write_devinfo;
- lcs_drvr_globals *drvr_globals = NULL;
- int hint, detect_error;
- int rc = -ENODEV;
- int max_rel_adapter_no;
- int err;
- int forced_rel_adapter_no;
- int lcs_loopcnt = 0;
- u8 mac_address[LCS_ADDR_LEN];
- struct net_device *dev = NULL;
- lcs_frame_type lan_type = lcs_autodetect_type;
- lcs_write_globals *write;
- lcs_read_globals *read;
-
- lcs_debug_event(1, "Starting lcs_probe dev=%p\n", dev);
-
- for (read_irq = get_irq_first(); read_irq >= 0;
- read_irq = get_irq_next(read_irq)) {
- /* check read channel
- * we had to do the cu_model check also because ctc devices
- * have the same cutype & after asking some people
- * the model numbers are given out pseudo randomly so
- * we can't just take a range of them also the
- * dev_type & models are 0
- */
- lcs_loopcnt++;
- if (lcs_loopcnt > 0x10000) {
- lcs_bad_news("lcs probe detected infinite loop "
- "bug in get_irq_next\n");
- goto Fail;
- }
- if ((err = get_dev_info_by_irq(read_irq, &read_devinfo))) {
- lcs_bad_news("lcs_probe get_dev_info_by_irq "
- "reported err=%X on irq %d\n"
- "should not happen\n", err, read_irq);
- continue;
- }
- if (read_devinfo.status & DEVSTAT_DEVICE_OWNED)
- continue;
- if (read_devinfo.devno & 1)
- continue;
- if ((max_rel_adapter_no =
- lcs_model_supported(&read_devinfo,
- &forced_rel_adapter_no)) < 0)
- continue;
- if (get_dev_info_by_devno
- (read_devinfo.devno + 1, &write_devinfo))
- continue;
- if (write_devinfo.status & DEVSTAT_DEVICE_OWNED)
- continue;
- write_irq = write_devinfo.irq;
- if ((ignore_sense==FALSE && forced_rel_adapter_no != -1) &&
- ((write_devinfo.sid_data.cu_type !=
- read_devinfo.sid_data.cu_type)
- || (write_devinfo.sid_data.cu_model !=
- read_devinfo.sid_data.cu_model)))
- continue;
- if (drvr_globals == NULL) {
- drvr_globals = lcs_alloc_drvr_globals();
- if (!drvr_globals) {
- lcs_bad_news("lcs_probe kmalloc failed\n");
- return -ENOMEM;
- }
- }
- read = drvr_globals->read;
- write = drvr_globals->write;
- read->subchannel = read_irq;
- write->subchannel = write_irq;
- read->devno = read_devinfo.devno;
- write->devno = write_devinfo.devno;
- lcs_set_slow_hw(drvr_globals, read_devinfo.sid_data.cu_type,
- read_devinfo.sid_data.cu_model);
- if (lcs_allocirq(read) || lcs_allocirq(write)) {
- lcs_freeallirqs(drvr_globals);
- /* We may find another card further up the chain */
- } else {
- if (forced_rel_adapter_no == -1) {
- hint = (read_devinfo.devno & 0xFF) >> 1;
- /* The card is possibly emulated e.g P/390 */
- /* or possibly configured to use a shared */
- /* port configured by osa-sf. */
- if (hint > max_rel_adapter_no) {
- hint = 0;
- }
- } else {
- hint = -1;
- }
- detect_error =
- lcs_detect(drvr_globals, forced_rel_adapter_no,
- hint, max_rel_adapter_no, lan_type,
- mac_address, 0);
- if (detect_error) {
- lcs_stopcard(drvr_globals);
- lcs_freeallirqs(drvr_globals);
- } else {
- /*
- * Fill in the fields of the device structure
- * with interface specific values.
- */
- switch (drvr_globals->lan_type) {
-#ifdef CONFIG_NET_ETHERNET
- case lcs_enet_type:
- dev = init_etherdev(dev, 0);
- drvr_globals->lan_type_trans =
- eth_type_trans;
- break;
-#endif
-#ifdef CONFIG_TR
- case lcs_token_ring_type:
- dev = init_trdev(dev, 0);
- drvr_globals->lan_type_trans =
- tr_type_trans;
- break;
-#endif
-#ifdef CONFIG_FDDI
- case lcs_fddi_type:
- /* I'm not sure whether this
- * function is
- * in the generic kernel anymore */
- dev = init_fddidev(dev, 0);
- drvr_globals->lan_type_trans =
- fddi_type_trans;
- break;
-#endif
- }
- if (dev)
- memcpy(dev->dev_addr, mac_address,
- LCS_ADDR_LEN);
- else {
- rc = -ENOMEM;
- goto Fail;
- }
- dev->open = lcs_open;
- dev->stop = lcs_close;
- dev->hard_start_xmit = lcs_txpacket;
- dev->get_stats = lcs_getstats;
- drvr_globals->dev = dev;
- dev->priv = drvr_globals;
-#ifdef CONFIG_IP_MULTICAST
- dev->set_multicast_list =
- (lcs_check_multicast_supported(drvr_globals)
- ? lcs_set_multicast_list : NULL);
-#endif
- lcs_stopcard(drvr_globals);
- netif_stop_queue(dev);
- lcs_displayinfo(dev);
- lcs_debug_event(0,"device structure=%p "
- "drvr globals=%p\n"
- "read=%p write=%p\n"
- "read_ccws=%p write_cwws=%p\n",
- dev, drvr_globals,
- drvr_globals->read,
- drvr_globals->write,
- &drvr_globals->read->ccw[0],
- &drvr_globals->write->ccw[0]);
-
- return 0;
- }
- }
+ PRINT_ERR("Initialization failed\n");
+ return rc;
}
-Fail:
- if (drvr_globals) {
-
- lcs_freeallirqs(drvr_globals);
- lcs_usage_free_drvr_globals(drvr_globals);
+ rc = register_cu3088_discipline(&lcs_group_driver);
+ if (rc) {
+ PRINT_ERR("Initialization failed\n");
+ return rc;
}
- lcs_debug_event(2, "osa_probe error %d\n", rc);
- return rc;
-}
-#endif
-
-#if LCS_CHANDEV || MODULE
-static int
-lcs_shutdown_card(struct net_device *dev)
-{
- lcs_drvr_globals *drvr_globals = ((lcs_drvr_globals *) dev->priv);
-#if !LCS_CHANDEV
- lcs_unregister(dev, drvr_globals->lan_type);
- /* unregister_netdev calls close */
-#endif
- lcs_freeallirqs(drvr_globals);
- lcs_usage_free_drvr_globals(drvr_globals);
- kfree(dev);
- return (0);
+ return 0;
}
-#endif
-#if LCS_CHANDEV
-static void
-lcs_msck_notification_func(struct net_device *dev, int msck_irq,
- chandev_msck_status prevstatus,
- chandev_msck_status newstatus)
-{
- lcs_drvr_globals *drvr_globals = (lcs_drvr_globals *) dev->priv;
-
- lcs_debug_event(0,"lcs_msck_notifcation_func drvr_globals=%p "
- "msck_irq=%d prevstatus=%d newstatus=%d",
- drvr_globals, msck_irq, prevstatus, newstatus);
- if (!drvr_globals) {
- lcs_debug_exception(0,"lcs_msck_notification_func "
- "drvr_globals=NULL");
- return;
- }
- if (drvr_globals->state == lcs_doing_io) {
- if (newstatus == chandev_status_good
- || newstatus == chandev_status_all_chans_good) {
- switch (prevstatus) {
- case chandev_status_no_path:
- case chandev_status_gone:
- case chandev_status_not_oper:
- if (msck_irq == drvr_globals->read->subchannel)
- lcs_restartreadio(drvr_globals);
- else
- lcs_restartwriteio(drvr_globals);
- break;
- default:
- break;
- }
- } else if (msck_irq == drvr_globals->write->subchannel
- && newstatus != chandev_status_revalidate)
- drvr_globals->write->chan_busy_state = chan_dead;
- } else /* Allow lcs_detect loop a few more times */
- atomic_set(&drvr_globals->retry_cnt, 0);
-}
-#endif
-
-#if MODULE
+/**
+ * LCS module cleanup function
+ */
static void
-lcs_cleanup(void)
+__exit lcs_cleanup_module(void)
{
-#if LCS_CHANDEV
- chandev_unregister(lcs_probe, TRUE);
-#else
- while (lcs_card_list)
- lcs_shutdown_card(lcs_card_list->dev);
-#endif
+ PRINT_INFO("Terminating lcs module.\n");
+ LCS_DBF_TEXT(0, trace, "cleanup");
+ unregister_cu3088_discipline(&lcs_group_driver);
+ lcs_unregister_debug_facility();
}
-#endif
-
-#if MODULE
-int
-init_module(void)
-#else
-#if LINUX_VERSION_CODE >=KERNEL_VERSION(2,3,0)
-static
-#endif
- int __init
-lcs_init(void)
-#endif
-{
- int cardsfound = 0;
-#if MODULE && LINUX_VERSION_CODE>KERNEL_VERSION(2,4,5)
- int persist = chandev_persist(chandev_type_lcs);
-#endif
- static char initstr[] = "Starting lcs "
-#ifdef MODULE
- "module "
-#else
- "compiled into kernel "
-#endif
- " $Revision: 1.128 $ $Date: 2002/03/01 16:56:47 $ \n"
-#if LCS_CHANDEV
- "with"
-#else
- "without"
-#endif
- " chandev support,"
-#ifdef CONFIG_IP_MULTICAST
- "with"
-#else
- "without"
-#endif
- " multicast support, "
-#ifdef CONFIG_NET_ETHERNET
- "with"
-#else
- "without"
-#endif
- " ethernet support, "
-#ifdef CONFIG_TR
- "with"
-#else
- "without"
-#endif
- " token ring support"
-#if 0
- ", "
-#ifdef CONFIG_FDDI
- "with"
-#else
- "without"
-#endif
- " fddi support"
-#endif
- ".\n";
- lcs_good_news(initstr);
-#if LCS_DEBUG && !LCS_PRINTK_DEBUG
- lcs_debug_register();
-#endif
+module_init(lcs_init_module);
+module_exit(lcs_cleanup_module);
-#if LCS_CHANDEV
- cardsfound = chandev_register_and_probe(lcs_probe,
- (chandev_shutdownfunc) lcs_shutdown_card,
- (chandev_msck_notification_func)
- lcs_msck_notification_func,
- chandev_type_lcs);
-#else
- while ((lcs_probe() == 0)) {
- cardsfound++;
- }
-#endif
- if (cardsfound <= 0) {
- lcs_bad_news("No lcs capable cards found\n");
-#if MODULE
-#if LINUX_VERSION_CODE>KERNEL_VERSION(2,4,5)
- if (!persist)
-#endif
- cleanup_module();
-#endif
- }
-#if !LCS_CHANDEV
- else {
- lcs_display_conf();
- }
-#endif
- return (cardsfound ? 0 : (
-#if MODULE && LINUX_VERSION_CODE>KERNEL_VERSION(2,4,5)
- persist ? 0 :
-#endif
- -ENODEV));
-}
-
-/*
- * Code for module loading & unloading
- */
+MODULE_AUTHOR("Frank Pavlic <pavlic@de.ibm.com>");
+MODULE_LICENSE("GPL");
-#ifdef MODULE
-void
-cleanup_module(void)
-{
- lcs_good_news("Terminating lcs module.\n");
- lcs_cleanup();
-#if LCS_DEBUG && !LCS_PRINTK_DEBUG
- if (lcs_id) {
- debug_unregister_view(lcs_id, &debug_sprintf_view);
- debug_unregister(lcs_id);
- lcs_id = NULL;
- }
-#endif
-}
-#else
-#if LINUX_VERSION_CODE>=KERNEL_VERSION(2,2,18)
-__initcall(lcs_init);
-#endif
-#endif