18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *  Linux for S/390 Lan Channel Station Network Driver
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *  Copyright IBM Corp. 1999, 2009
68c2ecf20Sopenharmony_ci *  Author(s): Original Code written by
78c2ecf20Sopenharmony_ci *			DJ Barrow <djbarrow@de.ibm.com,barrow_dj@yahoo.com>
88c2ecf20Sopenharmony_ci *	       Rewritten by
98c2ecf20Sopenharmony_ci *			Frank Pavlic <fpavlic@de.ibm.com> and
108c2ecf20Sopenharmony_ci *			Martin Schwidefsky <schwidefsky@de.ibm.com>
118c2ecf20Sopenharmony_ci */
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#define KMSG_COMPONENT		"lcs"
148c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#include <linux/module.h>
178c2ecf20Sopenharmony_ci#include <linux/if.h>
188c2ecf20Sopenharmony_ci#include <linux/netdevice.h>
198c2ecf20Sopenharmony_ci#include <linux/etherdevice.h>
208c2ecf20Sopenharmony_ci#include <linux/fddidevice.h>
218c2ecf20Sopenharmony_ci#include <linux/inetdevice.h>
228c2ecf20Sopenharmony_ci#include <linux/in.h>
238c2ecf20Sopenharmony_ci#include <linux/igmp.h>
248c2ecf20Sopenharmony_ci#include <linux/delay.h>
258c2ecf20Sopenharmony_ci#include <linux/kthread.h>
268c2ecf20Sopenharmony_ci#include <linux/slab.h>
278c2ecf20Sopenharmony_ci#include <net/arp.h>
288c2ecf20Sopenharmony_ci#include <net/ip.h>
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci#include <asm/debug.h>
318c2ecf20Sopenharmony_ci#include <asm/idals.h>
328c2ecf20Sopenharmony_ci#include <asm/timex.h>
338c2ecf20Sopenharmony_ci#include <linux/device.h>
348c2ecf20Sopenharmony_ci#include <asm/ccwgroup.h>
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci#include "lcs.h"
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci#if !defined(CONFIG_ETHERNET) && !defined(CONFIG_FDDI)
408c2ecf20Sopenharmony_ci#error Cannot compile lcs.c without some net devices switched on.
418c2ecf20Sopenharmony_ci#endif
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci/**
448c2ecf20Sopenharmony_ci * initialization string for output
458c2ecf20Sopenharmony_ci */
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_cistatic char version[] __initdata = "LCS driver";
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci/**
508c2ecf20Sopenharmony_ci  * the root device for lcs group devices
518c2ecf20Sopenharmony_ci  */
528c2ecf20Sopenharmony_cistatic struct device *lcs_root_dev;
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci/**
558c2ecf20Sopenharmony_ci * Some prototypes.
568c2ecf20Sopenharmony_ci */
578c2ecf20Sopenharmony_cistatic void lcs_tasklet(unsigned long);
588c2ecf20Sopenharmony_cistatic void lcs_start_kernel_thread(struct work_struct *);
598c2ecf20Sopenharmony_cistatic void lcs_get_frames_cb(struct lcs_channel *, struct lcs_buffer *);
608c2ecf20Sopenharmony_ci#ifdef CONFIG_IP_MULTICAST
618c2ecf20Sopenharmony_cistatic int lcs_send_delipm(struct lcs_card *, struct lcs_ipm_list *);
628c2ecf20Sopenharmony_ci#endif /* CONFIG_IP_MULTICAST */
638c2ecf20Sopenharmony_cistatic int lcs_recovery(void *ptr);
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci/**
668c2ecf20Sopenharmony_ci * Debug Facility Stuff
678c2ecf20Sopenharmony_ci */
688c2ecf20Sopenharmony_cistatic char debug_buffer[255];
698c2ecf20Sopenharmony_cistatic debug_info_t *lcs_dbf_setup;
708c2ecf20Sopenharmony_cistatic debug_info_t *lcs_dbf_trace;
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci/**
738c2ecf20Sopenharmony_ci *  LCS Debug Facility functions
748c2ecf20Sopenharmony_ci */
758c2ecf20Sopenharmony_cistatic void
768c2ecf20Sopenharmony_cilcs_unregister_debug_facility(void)
778c2ecf20Sopenharmony_ci{
788c2ecf20Sopenharmony_ci	debug_unregister(lcs_dbf_setup);
798c2ecf20Sopenharmony_ci	debug_unregister(lcs_dbf_trace);
808c2ecf20Sopenharmony_ci}
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_cistatic int
838c2ecf20Sopenharmony_cilcs_register_debug_facility(void)
848c2ecf20Sopenharmony_ci{
858c2ecf20Sopenharmony_ci	lcs_dbf_setup = debug_register("lcs_setup", 2, 1, 8);
868c2ecf20Sopenharmony_ci	lcs_dbf_trace = debug_register("lcs_trace", 4, 1, 8);
878c2ecf20Sopenharmony_ci	if (lcs_dbf_setup == NULL || lcs_dbf_trace == NULL) {
888c2ecf20Sopenharmony_ci		pr_err("Not enough memory for debug facility.\n");
898c2ecf20Sopenharmony_ci		lcs_unregister_debug_facility();
908c2ecf20Sopenharmony_ci		return -ENOMEM;
918c2ecf20Sopenharmony_ci	}
928c2ecf20Sopenharmony_ci	debug_register_view(lcs_dbf_setup, &debug_hex_ascii_view);
938c2ecf20Sopenharmony_ci	debug_set_level(lcs_dbf_setup, 2);
948c2ecf20Sopenharmony_ci	debug_register_view(lcs_dbf_trace, &debug_hex_ascii_view);
958c2ecf20Sopenharmony_ci	debug_set_level(lcs_dbf_trace, 2);
968c2ecf20Sopenharmony_ci	return 0;
978c2ecf20Sopenharmony_ci}
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci/**
1008c2ecf20Sopenharmony_ci * Allocate io buffers.
1018c2ecf20Sopenharmony_ci */
1028c2ecf20Sopenharmony_cistatic int
1038c2ecf20Sopenharmony_cilcs_alloc_channel(struct lcs_channel *channel)
1048c2ecf20Sopenharmony_ci{
1058c2ecf20Sopenharmony_ci	int cnt;
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	LCS_DBF_TEXT(2, setup, "ichalloc");
1088c2ecf20Sopenharmony_ci	for (cnt = 0; cnt < LCS_NUM_BUFFS; cnt++) {
1098c2ecf20Sopenharmony_ci		/* alloc memory fo iobuffer */
1108c2ecf20Sopenharmony_ci		channel->iob[cnt].data =
1118c2ecf20Sopenharmony_ci			kzalloc(LCS_IOBUFFERSIZE, GFP_DMA | GFP_KERNEL);
1128c2ecf20Sopenharmony_ci		if (channel->iob[cnt].data == NULL)
1138c2ecf20Sopenharmony_ci			break;
1148c2ecf20Sopenharmony_ci		channel->iob[cnt].state = LCS_BUF_STATE_EMPTY;
1158c2ecf20Sopenharmony_ci	}
1168c2ecf20Sopenharmony_ci	if (cnt < LCS_NUM_BUFFS) {
1178c2ecf20Sopenharmony_ci		/* Not all io buffers could be allocated. */
1188c2ecf20Sopenharmony_ci		LCS_DBF_TEXT(2, setup, "echalloc");
1198c2ecf20Sopenharmony_ci		while (cnt-- > 0)
1208c2ecf20Sopenharmony_ci			kfree(channel->iob[cnt].data);
1218c2ecf20Sopenharmony_ci		return -ENOMEM;
1228c2ecf20Sopenharmony_ci	}
1238c2ecf20Sopenharmony_ci	return 0;
1248c2ecf20Sopenharmony_ci}
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci/**
1278c2ecf20Sopenharmony_ci * Free io buffers.
1288c2ecf20Sopenharmony_ci */
1298c2ecf20Sopenharmony_cistatic void
1308c2ecf20Sopenharmony_cilcs_free_channel(struct lcs_channel *channel)
1318c2ecf20Sopenharmony_ci{
1328c2ecf20Sopenharmony_ci	int cnt;
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	LCS_DBF_TEXT(2, setup, "ichfree");
1358c2ecf20Sopenharmony_ci	for (cnt = 0; cnt < LCS_NUM_BUFFS; cnt++) {
1368c2ecf20Sopenharmony_ci		kfree(channel->iob[cnt].data);
1378c2ecf20Sopenharmony_ci		channel->iob[cnt].data = NULL;
1388c2ecf20Sopenharmony_ci	}
1398c2ecf20Sopenharmony_ci}
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci/*
1428c2ecf20Sopenharmony_ci * Cleanup channel.
1438c2ecf20Sopenharmony_ci */
1448c2ecf20Sopenharmony_cistatic void
1458c2ecf20Sopenharmony_cilcs_cleanup_channel(struct lcs_channel *channel)
1468c2ecf20Sopenharmony_ci{
1478c2ecf20Sopenharmony_ci	LCS_DBF_TEXT(3, setup, "cleanch");
1488c2ecf20Sopenharmony_ci	/* Kill write channel tasklets. */
1498c2ecf20Sopenharmony_ci	tasklet_kill(&channel->irq_tasklet);
1508c2ecf20Sopenharmony_ci	/* Free channel buffers. */
1518c2ecf20Sopenharmony_ci	lcs_free_channel(channel);
1528c2ecf20Sopenharmony_ci}
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci/**
1558c2ecf20Sopenharmony_ci * LCS free memory for card and channels.
1568c2ecf20Sopenharmony_ci */
1578c2ecf20Sopenharmony_cistatic void
1588c2ecf20Sopenharmony_cilcs_free_card(struct lcs_card *card)
1598c2ecf20Sopenharmony_ci{
1608c2ecf20Sopenharmony_ci	LCS_DBF_TEXT(2, setup, "remcard");
1618c2ecf20Sopenharmony_ci	LCS_DBF_HEX(2, setup, &card, sizeof(void*));
1628c2ecf20Sopenharmony_ci	kfree(card);
1638c2ecf20Sopenharmony_ci}
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci/**
1668c2ecf20Sopenharmony_ci * LCS alloc memory for card and channels
1678c2ecf20Sopenharmony_ci */
1688c2ecf20Sopenharmony_cistatic struct lcs_card *
1698c2ecf20Sopenharmony_cilcs_alloc_card(void)
1708c2ecf20Sopenharmony_ci{
1718c2ecf20Sopenharmony_ci	struct lcs_card *card;
1728c2ecf20Sopenharmony_ci	int rc;
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	LCS_DBF_TEXT(2, setup, "alloclcs");
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci	card = kzalloc(sizeof(struct lcs_card), GFP_KERNEL | GFP_DMA);
1778c2ecf20Sopenharmony_ci	if (card == NULL)
1788c2ecf20Sopenharmony_ci		return NULL;
1798c2ecf20Sopenharmony_ci	card->lan_type = LCS_FRAME_TYPE_AUTO;
1808c2ecf20Sopenharmony_ci	card->pkt_seq = 0;
1818c2ecf20Sopenharmony_ci	card->lancmd_timeout = LCS_LANCMD_TIMEOUT_DEFAULT;
1828c2ecf20Sopenharmony_ci	/* Allocate io buffers for the read channel. */
1838c2ecf20Sopenharmony_ci	rc = lcs_alloc_channel(&card->read);
1848c2ecf20Sopenharmony_ci	if (rc){
1858c2ecf20Sopenharmony_ci		LCS_DBF_TEXT(2, setup, "iccwerr");
1868c2ecf20Sopenharmony_ci		lcs_free_card(card);
1878c2ecf20Sopenharmony_ci		return NULL;
1888c2ecf20Sopenharmony_ci	}
1898c2ecf20Sopenharmony_ci	/* Allocate io buffers for the write channel. */
1908c2ecf20Sopenharmony_ci	rc = lcs_alloc_channel(&card->write);
1918c2ecf20Sopenharmony_ci	if (rc) {
1928c2ecf20Sopenharmony_ci		LCS_DBF_TEXT(2, setup, "iccwerr");
1938c2ecf20Sopenharmony_ci		lcs_cleanup_channel(&card->read);
1948c2ecf20Sopenharmony_ci		lcs_free_card(card);
1958c2ecf20Sopenharmony_ci		return NULL;
1968c2ecf20Sopenharmony_ci	}
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci#ifdef CONFIG_IP_MULTICAST
1998c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&card->ipm_list);
2008c2ecf20Sopenharmony_ci#endif
2018c2ecf20Sopenharmony_ci	LCS_DBF_HEX(2, setup, &card, sizeof(void*));
2028c2ecf20Sopenharmony_ci	return card;
2038c2ecf20Sopenharmony_ci}
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci/*
2068c2ecf20Sopenharmony_ci * Setup read channel.
2078c2ecf20Sopenharmony_ci */
2088c2ecf20Sopenharmony_cistatic void
2098c2ecf20Sopenharmony_cilcs_setup_read_ccws(struct lcs_card *card)
2108c2ecf20Sopenharmony_ci{
2118c2ecf20Sopenharmony_ci	int cnt;
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci	LCS_DBF_TEXT(2, setup, "ireadccw");
2148c2ecf20Sopenharmony_ci	/* Setup read ccws. */
2158c2ecf20Sopenharmony_ci	memset(card->read.ccws, 0, sizeof (struct ccw1) * (LCS_NUM_BUFFS + 1));
2168c2ecf20Sopenharmony_ci	for (cnt = 0; cnt < LCS_NUM_BUFFS; cnt++) {
2178c2ecf20Sopenharmony_ci		card->read.ccws[cnt].cmd_code = LCS_CCW_READ;
2188c2ecf20Sopenharmony_ci		card->read.ccws[cnt].count = LCS_IOBUFFERSIZE;
2198c2ecf20Sopenharmony_ci		card->read.ccws[cnt].flags =
2208c2ecf20Sopenharmony_ci			CCW_FLAG_CC | CCW_FLAG_SLI | CCW_FLAG_PCI;
2218c2ecf20Sopenharmony_ci		/*
2228c2ecf20Sopenharmony_ci		 * Note: we have allocated the buffer with GFP_DMA, so
2238c2ecf20Sopenharmony_ci		 * we do not need to do set_normalized_cda.
2248c2ecf20Sopenharmony_ci		 */
2258c2ecf20Sopenharmony_ci		card->read.ccws[cnt].cda =
2268c2ecf20Sopenharmony_ci			(__u32) __pa(card->read.iob[cnt].data);
2278c2ecf20Sopenharmony_ci		((struct lcs_header *)
2288c2ecf20Sopenharmony_ci		 card->read.iob[cnt].data)->offset = LCS_ILLEGAL_OFFSET;
2298c2ecf20Sopenharmony_ci		card->read.iob[cnt].callback = lcs_get_frames_cb;
2308c2ecf20Sopenharmony_ci		card->read.iob[cnt].state = LCS_BUF_STATE_READY;
2318c2ecf20Sopenharmony_ci		card->read.iob[cnt].count = LCS_IOBUFFERSIZE;
2328c2ecf20Sopenharmony_ci	}
2338c2ecf20Sopenharmony_ci	card->read.ccws[0].flags &= ~CCW_FLAG_PCI;
2348c2ecf20Sopenharmony_ci	card->read.ccws[LCS_NUM_BUFFS - 1].flags &= ~CCW_FLAG_PCI;
2358c2ecf20Sopenharmony_ci	card->read.ccws[LCS_NUM_BUFFS - 1].flags |= CCW_FLAG_SUSPEND;
2368c2ecf20Sopenharmony_ci	/* Last ccw is a tic (transfer in channel). */
2378c2ecf20Sopenharmony_ci	card->read.ccws[LCS_NUM_BUFFS].cmd_code = LCS_CCW_TRANSFER;
2388c2ecf20Sopenharmony_ci	card->read.ccws[LCS_NUM_BUFFS].cda =
2398c2ecf20Sopenharmony_ci		(__u32) __pa(card->read.ccws);
2408c2ecf20Sopenharmony_ci	/* Setg initial state of the read channel. */
2418c2ecf20Sopenharmony_ci	card->read.state = LCS_CH_STATE_INIT;
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci	card->read.io_idx = 0;
2448c2ecf20Sopenharmony_ci	card->read.buf_idx = 0;
2458c2ecf20Sopenharmony_ci}
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_cistatic void
2488c2ecf20Sopenharmony_cilcs_setup_read(struct lcs_card *card)
2498c2ecf20Sopenharmony_ci{
2508c2ecf20Sopenharmony_ci	LCS_DBF_TEXT(3, setup, "initread");
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci	lcs_setup_read_ccws(card);
2538c2ecf20Sopenharmony_ci	/* Initialize read channel tasklet. */
2548c2ecf20Sopenharmony_ci	card->read.irq_tasklet.data = (unsigned long) &card->read;
2558c2ecf20Sopenharmony_ci	card->read.irq_tasklet.func = lcs_tasklet;
2568c2ecf20Sopenharmony_ci	/* Initialize waitqueue. */
2578c2ecf20Sopenharmony_ci	init_waitqueue_head(&card->read.wait_q);
2588c2ecf20Sopenharmony_ci}
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci/*
2618c2ecf20Sopenharmony_ci * Setup write channel.
2628c2ecf20Sopenharmony_ci */
2638c2ecf20Sopenharmony_cistatic void
2648c2ecf20Sopenharmony_cilcs_setup_write_ccws(struct lcs_card *card)
2658c2ecf20Sopenharmony_ci{
2668c2ecf20Sopenharmony_ci	int cnt;
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci	LCS_DBF_TEXT(3, setup, "iwritccw");
2698c2ecf20Sopenharmony_ci	/* Setup write ccws. */
2708c2ecf20Sopenharmony_ci	memset(card->write.ccws, 0, sizeof(struct ccw1) * (LCS_NUM_BUFFS + 1));
2718c2ecf20Sopenharmony_ci	for (cnt = 0; cnt < LCS_NUM_BUFFS; cnt++) {
2728c2ecf20Sopenharmony_ci		card->write.ccws[cnt].cmd_code = LCS_CCW_WRITE;
2738c2ecf20Sopenharmony_ci		card->write.ccws[cnt].count = 0;
2748c2ecf20Sopenharmony_ci		card->write.ccws[cnt].flags =
2758c2ecf20Sopenharmony_ci			CCW_FLAG_SUSPEND | CCW_FLAG_CC | CCW_FLAG_SLI;
2768c2ecf20Sopenharmony_ci		/*
2778c2ecf20Sopenharmony_ci		 * Note: we have allocated the buffer with GFP_DMA, so
2788c2ecf20Sopenharmony_ci		 * we do not need to do set_normalized_cda.
2798c2ecf20Sopenharmony_ci		 */
2808c2ecf20Sopenharmony_ci		card->write.ccws[cnt].cda =
2818c2ecf20Sopenharmony_ci			(__u32) __pa(card->write.iob[cnt].data);
2828c2ecf20Sopenharmony_ci	}
2838c2ecf20Sopenharmony_ci	/* Last ccw is a tic (transfer in channel). */
2848c2ecf20Sopenharmony_ci	card->write.ccws[LCS_NUM_BUFFS].cmd_code = LCS_CCW_TRANSFER;
2858c2ecf20Sopenharmony_ci	card->write.ccws[LCS_NUM_BUFFS].cda =
2868c2ecf20Sopenharmony_ci		(__u32) __pa(card->write.ccws);
2878c2ecf20Sopenharmony_ci	/* Set initial state of the write channel. */
2888c2ecf20Sopenharmony_ci	card->read.state = LCS_CH_STATE_INIT;
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci	card->write.io_idx = 0;
2918c2ecf20Sopenharmony_ci	card->write.buf_idx = 0;
2928c2ecf20Sopenharmony_ci}
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_cistatic void
2958c2ecf20Sopenharmony_cilcs_setup_write(struct lcs_card *card)
2968c2ecf20Sopenharmony_ci{
2978c2ecf20Sopenharmony_ci	LCS_DBF_TEXT(3, setup, "initwrit");
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci	lcs_setup_write_ccws(card);
3008c2ecf20Sopenharmony_ci	/* Initialize write channel tasklet. */
3018c2ecf20Sopenharmony_ci	card->write.irq_tasklet.data = (unsigned long) &card->write;
3028c2ecf20Sopenharmony_ci	card->write.irq_tasklet.func = lcs_tasklet;
3038c2ecf20Sopenharmony_ci	/* Initialize waitqueue. */
3048c2ecf20Sopenharmony_ci	init_waitqueue_head(&card->write.wait_q);
3058c2ecf20Sopenharmony_ci}
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_cistatic void
3088c2ecf20Sopenharmony_cilcs_set_allowed_threads(struct lcs_card *card, unsigned long threads)
3098c2ecf20Sopenharmony_ci{
3108c2ecf20Sopenharmony_ci	unsigned long flags;
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci	spin_lock_irqsave(&card->mask_lock, flags);
3138c2ecf20Sopenharmony_ci	card->thread_allowed_mask = threads;
3148c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&card->mask_lock, flags);
3158c2ecf20Sopenharmony_ci	wake_up(&card->wait_q);
3168c2ecf20Sopenharmony_ci}
3178c2ecf20Sopenharmony_cistatic int lcs_threads_running(struct lcs_card *card, unsigned long threads)
3188c2ecf20Sopenharmony_ci{
3198c2ecf20Sopenharmony_ci        unsigned long flags;
3208c2ecf20Sopenharmony_ci        int rc = 0;
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci	spin_lock_irqsave(&card->mask_lock, flags);
3238c2ecf20Sopenharmony_ci        rc = (card->thread_running_mask & threads);
3248c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&card->mask_lock, flags);
3258c2ecf20Sopenharmony_ci        return rc;
3268c2ecf20Sopenharmony_ci}
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_cistatic int
3298c2ecf20Sopenharmony_cilcs_wait_for_threads(struct lcs_card *card, unsigned long threads)
3308c2ecf20Sopenharmony_ci{
3318c2ecf20Sopenharmony_ci        return wait_event_interruptible(card->wait_q,
3328c2ecf20Sopenharmony_ci                        lcs_threads_running(card, threads) == 0);
3338c2ecf20Sopenharmony_ci}
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_cistatic int lcs_set_thread_start_bit(struct lcs_card *card, unsigned long thread)
3368c2ecf20Sopenharmony_ci{
3378c2ecf20Sopenharmony_ci        unsigned long flags;
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci	spin_lock_irqsave(&card->mask_lock, flags);
3408c2ecf20Sopenharmony_ci        if ( !(card->thread_allowed_mask & thread) ||
3418c2ecf20Sopenharmony_ci              (card->thread_start_mask & thread) ) {
3428c2ecf20Sopenharmony_ci                spin_unlock_irqrestore(&card->mask_lock, flags);
3438c2ecf20Sopenharmony_ci                return -EPERM;
3448c2ecf20Sopenharmony_ci        }
3458c2ecf20Sopenharmony_ci        card->thread_start_mask |= thread;
3468c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&card->mask_lock, flags);
3478c2ecf20Sopenharmony_ci        return 0;
3488c2ecf20Sopenharmony_ci}
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_cistatic void
3518c2ecf20Sopenharmony_cilcs_clear_thread_running_bit(struct lcs_card *card, unsigned long thread)
3528c2ecf20Sopenharmony_ci{
3538c2ecf20Sopenharmony_ci        unsigned long flags;
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci	spin_lock_irqsave(&card->mask_lock, flags);
3568c2ecf20Sopenharmony_ci        card->thread_running_mask &= ~thread;
3578c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&card->mask_lock, flags);
3588c2ecf20Sopenharmony_ci        wake_up(&card->wait_q);
3598c2ecf20Sopenharmony_ci}
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_cistatic int __lcs_do_run_thread(struct lcs_card *card, unsigned long thread)
3628c2ecf20Sopenharmony_ci{
3638c2ecf20Sopenharmony_ci        unsigned long flags;
3648c2ecf20Sopenharmony_ci        int rc = 0;
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci	spin_lock_irqsave(&card->mask_lock, flags);
3678c2ecf20Sopenharmony_ci        if (card->thread_start_mask & thread){
3688c2ecf20Sopenharmony_ci                if ((card->thread_allowed_mask & thread) &&
3698c2ecf20Sopenharmony_ci                    !(card->thread_running_mask & thread)){
3708c2ecf20Sopenharmony_ci                        rc = 1;
3718c2ecf20Sopenharmony_ci                        card->thread_start_mask &= ~thread;
3728c2ecf20Sopenharmony_ci                        card->thread_running_mask |= thread;
3738c2ecf20Sopenharmony_ci                } else
3748c2ecf20Sopenharmony_ci                        rc = -EPERM;
3758c2ecf20Sopenharmony_ci        }
3768c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&card->mask_lock, flags);
3778c2ecf20Sopenharmony_ci        return rc;
3788c2ecf20Sopenharmony_ci}
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_cistatic int
3818c2ecf20Sopenharmony_cilcs_do_run_thread(struct lcs_card *card, unsigned long thread)
3828c2ecf20Sopenharmony_ci{
3838c2ecf20Sopenharmony_ci        int rc = 0;
3848c2ecf20Sopenharmony_ci        wait_event(card->wait_q,
3858c2ecf20Sopenharmony_ci                   (rc = __lcs_do_run_thread(card, thread)) >= 0);
3868c2ecf20Sopenharmony_ci        return rc;
3878c2ecf20Sopenharmony_ci}
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_cistatic int
3908c2ecf20Sopenharmony_cilcs_do_start_thread(struct lcs_card *card, unsigned long thread)
3918c2ecf20Sopenharmony_ci{
3928c2ecf20Sopenharmony_ci        unsigned long flags;
3938c2ecf20Sopenharmony_ci        int rc = 0;
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_ci	spin_lock_irqsave(&card->mask_lock, flags);
3968c2ecf20Sopenharmony_ci        LCS_DBF_TEXT_(4, trace, "  %02x%02x%02x",
3978c2ecf20Sopenharmony_ci                        (u8) card->thread_start_mask,
3988c2ecf20Sopenharmony_ci                        (u8) card->thread_allowed_mask,
3998c2ecf20Sopenharmony_ci                        (u8) card->thread_running_mask);
4008c2ecf20Sopenharmony_ci        rc = (card->thread_start_mask & thread);
4018c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&card->mask_lock, flags);
4028c2ecf20Sopenharmony_ci        return rc;
4038c2ecf20Sopenharmony_ci}
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_ci/**
4068c2ecf20Sopenharmony_ci * Initialize channels,card and state machines.
4078c2ecf20Sopenharmony_ci */
4088c2ecf20Sopenharmony_cistatic void
4098c2ecf20Sopenharmony_cilcs_setup_card(struct lcs_card *card)
4108c2ecf20Sopenharmony_ci{
4118c2ecf20Sopenharmony_ci	LCS_DBF_TEXT(2, setup, "initcard");
4128c2ecf20Sopenharmony_ci	LCS_DBF_HEX(2, setup, &card, sizeof(void*));
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_ci	lcs_setup_read(card);
4158c2ecf20Sopenharmony_ci	lcs_setup_write(card);
4168c2ecf20Sopenharmony_ci	/* Set cards initial state. */
4178c2ecf20Sopenharmony_ci	card->state = DEV_STATE_DOWN;
4188c2ecf20Sopenharmony_ci	card->tx_buffer = NULL;
4198c2ecf20Sopenharmony_ci	card->tx_emitted = 0;
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_ci	init_waitqueue_head(&card->wait_q);
4228c2ecf20Sopenharmony_ci	spin_lock_init(&card->lock);
4238c2ecf20Sopenharmony_ci	spin_lock_init(&card->ipm_lock);
4248c2ecf20Sopenharmony_ci	spin_lock_init(&card->mask_lock);
4258c2ecf20Sopenharmony_ci#ifdef CONFIG_IP_MULTICAST
4268c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&card->ipm_list);
4278c2ecf20Sopenharmony_ci#endif
4288c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&card->lancmd_waiters);
4298c2ecf20Sopenharmony_ci}
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_cistatic void lcs_clear_multicast_list(struct lcs_card *card)
4328c2ecf20Sopenharmony_ci{
4338c2ecf20Sopenharmony_ci#ifdef	CONFIG_IP_MULTICAST
4348c2ecf20Sopenharmony_ci	struct lcs_ipm_list *ipm;
4358c2ecf20Sopenharmony_ci	unsigned long flags;
4368c2ecf20Sopenharmony_ci
4378c2ecf20Sopenharmony_ci	/* Free multicast list. */
4388c2ecf20Sopenharmony_ci	LCS_DBF_TEXT(3, setup, "clmclist");
4398c2ecf20Sopenharmony_ci	spin_lock_irqsave(&card->ipm_lock, flags);
4408c2ecf20Sopenharmony_ci	while (!list_empty(&card->ipm_list)){
4418c2ecf20Sopenharmony_ci		ipm = list_entry(card->ipm_list.next,
4428c2ecf20Sopenharmony_ci				 struct lcs_ipm_list, list);
4438c2ecf20Sopenharmony_ci		list_del(&ipm->list);
4448c2ecf20Sopenharmony_ci		if (ipm->ipm_state != LCS_IPM_STATE_SET_REQUIRED){
4458c2ecf20Sopenharmony_ci			spin_unlock_irqrestore(&card->ipm_lock, flags);
4468c2ecf20Sopenharmony_ci			lcs_send_delipm(card, ipm);
4478c2ecf20Sopenharmony_ci			spin_lock_irqsave(&card->ipm_lock, flags);
4488c2ecf20Sopenharmony_ci		}
4498c2ecf20Sopenharmony_ci		kfree(ipm);
4508c2ecf20Sopenharmony_ci	}
4518c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&card->ipm_lock, flags);
4528c2ecf20Sopenharmony_ci#endif
4538c2ecf20Sopenharmony_ci}
4548c2ecf20Sopenharmony_ci/**
4558c2ecf20Sopenharmony_ci * Cleanup channels,card and state machines.
4568c2ecf20Sopenharmony_ci */
4578c2ecf20Sopenharmony_cistatic void
4588c2ecf20Sopenharmony_cilcs_cleanup_card(struct lcs_card *card)
4598c2ecf20Sopenharmony_ci{
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_ci	LCS_DBF_TEXT(3, setup, "cleancrd");
4628c2ecf20Sopenharmony_ci	LCS_DBF_HEX(2,setup,&card,sizeof(void*));
4638c2ecf20Sopenharmony_ci
4648c2ecf20Sopenharmony_ci	if (card->dev != NULL)
4658c2ecf20Sopenharmony_ci		free_netdev(card->dev);
4668c2ecf20Sopenharmony_ci	/* Cleanup channels. */
4678c2ecf20Sopenharmony_ci	lcs_cleanup_channel(&card->write);
4688c2ecf20Sopenharmony_ci	lcs_cleanup_channel(&card->read);
4698c2ecf20Sopenharmony_ci}
4708c2ecf20Sopenharmony_ci
4718c2ecf20Sopenharmony_ci/**
4728c2ecf20Sopenharmony_ci * Start channel.
4738c2ecf20Sopenharmony_ci */
4748c2ecf20Sopenharmony_cistatic int
4758c2ecf20Sopenharmony_cilcs_start_channel(struct lcs_channel *channel)
4768c2ecf20Sopenharmony_ci{
4778c2ecf20Sopenharmony_ci	unsigned long flags;
4788c2ecf20Sopenharmony_ci	int rc;
4798c2ecf20Sopenharmony_ci
4808c2ecf20Sopenharmony_ci	LCS_DBF_TEXT_(4, trace,"ssch%s", dev_name(&channel->ccwdev->dev));
4818c2ecf20Sopenharmony_ci	spin_lock_irqsave(get_ccwdev_lock(channel->ccwdev), flags);
4828c2ecf20Sopenharmony_ci	rc = ccw_device_start(channel->ccwdev,
4838c2ecf20Sopenharmony_ci			      channel->ccws + channel->io_idx, 0, 0,
4848c2ecf20Sopenharmony_ci			      DOIO_DENY_PREFETCH | DOIO_ALLOW_SUSPEND);
4858c2ecf20Sopenharmony_ci	if (rc == 0)
4868c2ecf20Sopenharmony_ci		channel->state = LCS_CH_STATE_RUNNING;
4878c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(get_ccwdev_lock(channel->ccwdev), flags);
4888c2ecf20Sopenharmony_ci	if (rc) {
4898c2ecf20Sopenharmony_ci		LCS_DBF_TEXT_(4,trace,"essh%s",
4908c2ecf20Sopenharmony_ci			      dev_name(&channel->ccwdev->dev));
4918c2ecf20Sopenharmony_ci		dev_err(&channel->ccwdev->dev,
4928c2ecf20Sopenharmony_ci			"Starting an LCS device resulted in an error,"
4938c2ecf20Sopenharmony_ci			" rc=%d!\n", rc);
4948c2ecf20Sopenharmony_ci	}
4958c2ecf20Sopenharmony_ci	return rc;
4968c2ecf20Sopenharmony_ci}
4978c2ecf20Sopenharmony_ci
4988c2ecf20Sopenharmony_cistatic int
4998c2ecf20Sopenharmony_cilcs_clear_channel(struct lcs_channel *channel)
5008c2ecf20Sopenharmony_ci{
5018c2ecf20Sopenharmony_ci	unsigned long flags;
5028c2ecf20Sopenharmony_ci	int rc;
5038c2ecf20Sopenharmony_ci
5048c2ecf20Sopenharmony_ci	LCS_DBF_TEXT(4,trace,"clearch");
5058c2ecf20Sopenharmony_ci	LCS_DBF_TEXT_(4, trace, "%s", dev_name(&channel->ccwdev->dev));
5068c2ecf20Sopenharmony_ci	spin_lock_irqsave(get_ccwdev_lock(channel->ccwdev), flags);
5078c2ecf20Sopenharmony_ci	rc = ccw_device_clear(channel->ccwdev, 0);
5088c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(get_ccwdev_lock(channel->ccwdev), flags);
5098c2ecf20Sopenharmony_ci	if (rc) {
5108c2ecf20Sopenharmony_ci		LCS_DBF_TEXT_(4, trace, "ecsc%s",
5118c2ecf20Sopenharmony_ci			      dev_name(&channel->ccwdev->dev));
5128c2ecf20Sopenharmony_ci		return rc;
5138c2ecf20Sopenharmony_ci	}
5148c2ecf20Sopenharmony_ci	wait_event(channel->wait_q, (channel->state == LCS_CH_STATE_CLEARED));
5158c2ecf20Sopenharmony_ci	channel->state = LCS_CH_STATE_STOPPED;
5168c2ecf20Sopenharmony_ci	return rc;
5178c2ecf20Sopenharmony_ci}
5188c2ecf20Sopenharmony_ci
5198c2ecf20Sopenharmony_ci
5208c2ecf20Sopenharmony_ci/**
5218c2ecf20Sopenharmony_ci * Stop channel.
5228c2ecf20Sopenharmony_ci */
5238c2ecf20Sopenharmony_cistatic int
5248c2ecf20Sopenharmony_cilcs_stop_channel(struct lcs_channel *channel)
5258c2ecf20Sopenharmony_ci{
5268c2ecf20Sopenharmony_ci	unsigned long flags;
5278c2ecf20Sopenharmony_ci	int rc;
5288c2ecf20Sopenharmony_ci
5298c2ecf20Sopenharmony_ci	if (channel->state == LCS_CH_STATE_STOPPED)
5308c2ecf20Sopenharmony_ci		return 0;
5318c2ecf20Sopenharmony_ci	LCS_DBF_TEXT(4,trace,"haltsch");
5328c2ecf20Sopenharmony_ci	LCS_DBF_TEXT_(4, trace, "%s", dev_name(&channel->ccwdev->dev));
5338c2ecf20Sopenharmony_ci	channel->state = LCS_CH_STATE_INIT;
5348c2ecf20Sopenharmony_ci	spin_lock_irqsave(get_ccwdev_lock(channel->ccwdev), flags);
5358c2ecf20Sopenharmony_ci	rc = ccw_device_halt(channel->ccwdev, 0);
5368c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(get_ccwdev_lock(channel->ccwdev), flags);
5378c2ecf20Sopenharmony_ci	if (rc) {
5388c2ecf20Sopenharmony_ci		LCS_DBF_TEXT_(4, trace, "ehsc%s",
5398c2ecf20Sopenharmony_ci			      dev_name(&channel->ccwdev->dev));
5408c2ecf20Sopenharmony_ci		return rc;
5418c2ecf20Sopenharmony_ci	}
5428c2ecf20Sopenharmony_ci	/* Asynchronous halt initialted. Wait for its completion. */
5438c2ecf20Sopenharmony_ci	wait_event(channel->wait_q, (channel->state == LCS_CH_STATE_HALTED));
5448c2ecf20Sopenharmony_ci	lcs_clear_channel(channel);
5458c2ecf20Sopenharmony_ci	return 0;
5468c2ecf20Sopenharmony_ci}
5478c2ecf20Sopenharmony_ci
5488c2ecf20Sopenharmony_ci/**
5498c2ecf20Sopenharmony_ci * start read and write channel
5508c2ecf20Sopenharmony_ci */
5518c2ecf20Sopenharmony_cistatic int
5528c2ecf20Sopenharmony_cilcs_start_channels(struct lcs_card *card)
5538c2ecf20Sopenharmony_ci{
5548c2ecf20Sopenharmony_ci	int rc;
5558c2ecf20Sopenharmony_ci
5568c2ecf20Sopenharmony_ci	LCS_DBF_TEXT(2, trace, "chstart");
5578c2ecf20Sopenharmony_ci	/* start read channel */
5588c2ecf20Sopenharmony_ci	rc = lcs_start_channel(&card->read);
5598c2ecf20Sopenharmony_ci	if (rc)
5608c2ecf20Sopenharmony_ci		return rc;
5618c2ecf20Sopenharmony_ci	/* start write channel */
5628c2ecf20Sopenharmony_ci	rc = lcs_start_channel(&card->write);
5638c2ecf20Sopenharmony_ci	if (rc)
5648c2ecf20Sopenharmony_ci		lcs_stop_channel(&card->read);
5658c2ecf20Sopenharmony_ci	return rc;
5668c2ecf20Sopenharmony_ci}
5678c2ecf20Sopenharmony_ci
5688c2ecf20Sopenharmony_ci/**
5698c2ecf20Sopenharmony_ci * stop read and write channel
5708c2ecf20Sopenharmony_ci */
5718c2ecf20Sopenharmony_cistatic int
5728c2ecf20Sopenharmony_cilcs_stop_channels(struct lcs_card *card)
5738c2ecf20Sopenharmony_ci{
5748c2ecf20Sopenharmony_ci	LCS_DBF_TEXT(2, trace, "chhalt");
5758c2ecf20Sopenharmony_ci	lcs_stop_channel(&card->read);
5768c2ecf20Sopenharmony_ci	lcs_stop_channel(&card->write);
5778c2ecf20Sopenharmony_ci	return 0;
5788c2ecf20Sopenharmony_ci}
5798c2ecf20Sopenharmony_ci
5808c2ecf20Sopenharmony_ci/**
5818c2ecf20Sopenharmony_ci * Get empty buffer.
5828c2ecf20Sopenharmony_ci */
5838c2ecf20Sopenharmony_cistatic struct lcs_buffer *
5848c2ecf20Sopenharmony_ci__lcs_get_buffer(struct lcs_channel *channel)
5858c2ecf20Sopenharmony_ci{
5868c2ecf20Sopenharmony_ci	int index;
5878c2ecf20Sopenharmony_ci
5888c2ecf20Sopenharmony_ci	LCS_DBF_TEXT(5, trace, "_getbuff");
5898c2ecf20Sopenharmony_ci	index = channel->io_idx;
5908c2ecf20Sopenharmony_ci	do {
5918c2ecf20Sopenharmony_ci		if (channel->iob[index].state == LCS_BUF_STATE_EMPTY) {
5928c2ecf20Sopenharmony_ci			channel->iob[index].state = LCS_BUF_STATE_LOCKED;
5938c2ecf20Sopenharmony_ci			return channel->iob + index;
5948c2ecf20Sopenharmony_ci		}
5958c2ecf20Sopenharmony_ci		index = (index + 1) & (LCS_NUM_BUFFS - 1);
5968c2ecf20Sopenharmony_ci	} while (index != channel->io_idx);
5978c2ecf20Sopenharmony_ci	return NULL;
5988c2ecf20Sopenharmony_ci}
5998c2ecf20Sopenharmony_ci
6008c2ecf20Sopenharmony_cistatic struct lcs_buffer *
6018c2ecf20Sopenharmony_cilcs_get_buffer(struct lcs_channel *channel)
6028c2ecf20Sopenharmony_ci{
6038c2ecf20Sopenharmony_ci	struct lcs_buffer *buffer;
6048c2ecf20Sopenharmony_ci	unsigned long flags;
6058c2ecf20Sopenharmony_ci
6068c2ecf20Sopenharmony_ci	LCS_DBF_TEXT(5, trace, "getbuff");
6078c2ecf20Sopenharmony_ci	spin_lock_irqsave(get_ccwdev_lock(channel->ccwdev), flags);
6088c2ecf20Sopenharmony_ci	buffer = __lcs_get_buffer(channel);
6098c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(get_ccwdev_lock(channel->ccwdev), flags);
6108c2ecf20Sopenharmony_ci	return buffer;
6118c2ecf20Sopenharmony_ci}
6128c2ecf20Sopenharmony_ci
6138c2ecf20Sopenharmony_ci/**
6148c2ecf20Sopenharmony_ci * Resume channel program if the channel is suspended.
6158c2ecf20Sopenharmony_ci */
6168c2ecf20Sopenharmony_cistatic int
6178c2ecf20Sopenharmony_ci__lcs_resume_channel(struct lcs_channel *channel)
6188c2ecf20Sopenharmony_ci{
6198c2ecf20Sopenharmony_ci	int rc;
6208c2ecf20Sopenharmony_ci
6218c2ecf20Sopenharmony_ci	if (channel->state != LCS_CH_STATE_SUSPENDED)
6228c2ecf20Sopenharmony_ci		return 0;
6238c2ecf20Sopenharmony_ci	if (channel->ccws[channel->io_idx].flags & CCW_FLAG_SUSPEND)
6248c2ecf20Sopenharmony_ci		return 0;
6258c2ecf20Sopenharmony_ci	LCS_DBF_TEXT_(5, trace, "rsch%s", dev_name(&channel->ccwdev->dev));
6268c2ecf20Sopenharmony_ci	rc = ccw_device_resume(channel->ccwdev);
6278c2ecf20Sopenharmony_ci	if (rc) {
6288c2ecf20Sopenharmony_ci		LCS_DBF_TEXT_(4, trace, "ersc%s",
6298c2ecf20Sopenharmony_ci			      dev_name(&channel->ccwdev->dev));
6308c2ecf20Sopenharmony_ci		dev_err(&channel->ccwdev->dev,
6318c2ecf20Sopenharmony_ci			"Sending data from the LCS device to the LAN failed"
6328c2ecf20Sopenharmony_ci			" with rc=%d\n",rc);
6338c2ecf20Sopenharmony_ci	} else
6348c2ecf20Sopenharmony_ci		channel->state = LCS_CH_STATE_RUNNING;
6358c2ecf20Sopenharmony_ci	return rc;
6368c2ecf20Sopenharmony_ci
6378c2ecf20Sopenharmony_ci}
6388c2ecf20Sopenharmony_ci
6398c2ecf20Sopenharmony_ci/**
6408c2ecf20Sopenharmony_ci * Make a buffer ready for processing.
6418c2ecf20Sopenharmony_ci */
6428c2ecf20Sopenharmony_cistatic void __lcs_ready_buffer_bits(struct lcs_channel *channel, int index)
6438c2ecf20Sopenharmony_ci{
6448c2ecf20Sopenharmony_ci	int prev, next;
6458c2ecf20Sopenharmony_ci
6468c2ecf20Sopenharmony_ci	LCS_DBF_TEXT(5, trace, "rdybits");
6478c2ecf20Sopenharmony_ci	prev = (index - 1) & (LCS_NUM_BUFFS - 1);
6488c2ecf20Sopenharmony_ci	next = (index + 1) & (LCS_NUM_BUFFS - 1);
6498c2ecf20Sopenharmony_ci	/* Check if we may clear the suspend bit of this buffer. */
6508c2ecf20Sopenharmony_ci	if (channel->ccws[next].flags & CCW_FLAG_SUSPEND) {
6518c2ecf20Sopenharmony_ci		/* Check if we have to set the PCI bit. */
6528c2ecf20Sopenharmony_ci		if (!(channel->ccws[prev].flags & CCW_FLAG_SUSPEND))
6538c2ecf20Sopenharmony_ci			/* Suspend bit of the previous buffer is not set. */
6548c2ecf20Sopenharmony_ci			channel->ccws[index].flags |= CCW_FLAG_PCI;
6558c2ecf20Sopenharmony_ci		/* Suspend bit of the next buffer is set. */
6568c2ecf20Sopenharmony_ci		channel->ccws[index].flags &= ~CCW_FLAG_SUSPEND;
6578c2ecf20Sopenharmony_ci	}
6588c2ecf20Sopenharmony_ci}
6598c2ecf20Sopenharmony_ci
6608c2ecf20Sopenharmony_cistatic int
6618c2ecf20Sopenharmony_cilcs_ready_buffer(struct lcs_channel *channel, struct lcs_buffer *buffer)
6628c2ecf20Sopenharmony_ci{
6638c2ecf20Sopenharmony_ci	unsigned long flags;
6648c2ecf20Sopenharmony_ci	int index, rc;
6658c2ecf20Sopenharmony_ci
6668c2ecf20Sopenharmony_ci	LCS_DBF_TEXT(5, trace, "rdybuff");
6678c2ecf20Sopenharmony_ci	BUG_ON(buffer->state != LCS_BUF_STATE_LOCKED &&
6688c2ecf20Sopenharmony_ci	       buffer->state != LCS_BUF_STATE_PROCESSED);
6698c2ecf20Sopenharmony_ci	spin_lock_irqsave(get_ccwdev_lock(channel->ccwdev), flags);
6708c2ecf20Sopenharmony_ci	buffer->state = LCS_BUF_STATE_READY;
6718c2ecf20Sopenharmony_ci	index = buffer - channel->iob;
6728c2ecf20Sopenharmony_ci	/* Set length. */
6738c2ecf20Sopenharmony_ci	channel->ccws[index].count = buffer->count;
6748c2ecf20Sopenharmony_ci	/* Check relevant PCI/suspend bits. */
6758c2ecf20Sopenharmony_ci	__lcs_ready_buffer_bits(channel, index);
6768c2ecf20Sopenharmony_ci	rc = __lcs_resume_channel(channel);
6778c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(get_ccwdev_lock(channel->ccwdev), flags);
6788c2ecf20Sopenharmony_ci	return rc;
6798c2ecf20Sopenharmony_ci}
6808c2ecf20Sopenharmony_ci
6818c2ecf20Sopenharmony_ci/**
6828c2ecf20Sopenharmony_ci * Mark the buffer as processed. Take care of the suspend bit
6838c2ecf20Sopenharmony_ci * of the previous buffer. This function is called from
6848c2ecf20Sopenharmony_ci * interrupt context, so the lock must not be taken.
6858c2ecf20Sopenharmony_ci */
6868c2ecf20Sopenharmony_cistatic int
6878c2ecf20Sopenharmony_ci__lcs_processed_buffer(struct lcs_channel *channel, struct lcs_buffer *buffer)
6888c2ecf20Sopenharmony_ci{
6898c2ecf20Sopenharmony_ci	int index, prev, next;
6908c2ecf20Sopenharmony_ci
6918c2ecf20Sopenharmony_ci	LCS_DBF_TEXT(5, trace, "prcsbuff");
6928c2ecf20Sopenharmony_ci	BUG_ON(buffer->state != LCS_BUF_STATE_READY);
6938c2ecf20Sopenharmony_ci	buffer->state = LCS_BUF_STATE_PROCESSED;
6948c2ecf20Sopenharmony_ci	index = buffer - channel->iob;
6958c2ecf20Sopenharmony_ci	prev = (index - 1) & (LCS_NUM_BUFFS - 1);
6968c2ecf20Sopenharmony_ci	next = (index + 1) & (LCS_NUM_BUFFS - 1);
6978c2ecf20Sopenharmony_ci	/* Set the suspend bit and clear the PCI bit of this buffer. */
6988c2ecf20Sopenharmony_ci	channel->ccws[index].flags |= CCW_FLAG_SUSPEND;
6998c2ecf20Sopenharmony_ci	channel->ccws[index].flags &= ~CCW_FLAG_PCI;
7008c2ecf20Sopenharmony_ci	/* Check the suspend bit of the previous buffer. */
7018c2ecf20Sopenharmony_ci	if (channel->iob[prev].state == LCS_BUF_STATE_READY) {
7028c2ecf20Sopenharmony_ci		/*
7038c2ecf20Sopenharmony_ci		 * Previous buffer is in state ready. It might have
7048c2ecf20Sopenharmony_ci		 * happened in lcs_ready_buffer that the suspend bit
7058c2ecf20Sopenharmony_ci		 * has not been cleared to avoid an endless loop.
7068c2ecf20Sopenharmony_ci		 * Do it now.
7078c2ecf20Sopenharmony_ci		 */
7088c2ecf20Sopenharmony_ci		__lcs_ready_buffer_bits(channel, prev);
7098c2ecf20Sopenharmony_ci	}
7108c2ecf20Sopenharmony_ci	/* Clear PCI bit of next buffer. */
7118c2ecf20Sopenharmony_ci	channel->ccws[next].flags &= ~CCW_FLAG_PCI;
7128c2ecf20Sopenharmony_ci	return __lcs_resume_channel(channel);
7138c2ecf20Sopenharmony_ci}
7148c2ecf20Sopenharmony_ci
7158c2ecf20Sopenharmony_ci/**
7168c2ecf20Sopenharmony_ci * Put a processed buffer back to state empty.
7178c2ecf20Sopenharmony_ci */
7188c2ecf20Sopenharmony_cistatic void
7198c2ecf20Sopenharmony_cilcs_release_buffer(struct lcs_channel *channel, struct lcs_buffer *buffer)
7208c2ecf20Sopenharmony_ci{
7218c2ecf20Sopenharmony_ci	unsigned long flags;
7228c2ecf20Sopenharmony_ci
7238c2ecf20Sopenharmony_ci	LCS_DBF_TEXT(5, trace, "relbuff");
7248c2ecf20Sopenharmony_ci	BUG_ON(buffer->state != LCS_BUF_STATE_LOCKED &&
7258c2ecf20Sopenharmony_ci	       buffer->state != LCS_BUF_STATE_PROCESSED);
7268c2ecf20Sopenharmony_ci	spin_lock_irqsave(get_ccwdev_lock(channel->ccwdev), flags);
7278c2ecf20Sopenharmony_ci	buffer->state = LCS_BUF_STATE_EMPTY;
7288c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(get_ccwdev_lock(channel->ccwdev), flags);
7298c2ecf20Sopenharmony_ci}
7308c2ecf20Sopenharmony_ci
7318c2ecf20Sopenharmony_ci/**
7328c2ecf20Sopenharmony_ci * Get buffer for a lan command.
7338c2ecf20Sopenharmony_ci */
7348c2ecf20Sopenharmony_cistatic struct lcs_buffer *
7358c2ecf20Sopenharmony_cilcs_get_lancmd(struct lcs_card *card, int count)
7368c2ecf20Sopenharmony_ci{
7378c2ecf20Sopenharmony_ci	struct lcs_buffer *buffer;
7388c2ecf20Sopenharmony_ci	struct lcs_cmd *cmd;
7398c2ecf20Sopenharmony_ci
7408c2ecf20Sopenharmony_ci	LCS_DBF_TEXT(4, trace, "getlncmd");
7418c2ecf20Sopenharmony_ci	/* Get buffer and wait if none is available. */
7428c2ecf20Sopenharmony_ci	wait_event(card->write.wait_q,
7438c2ecf20Sopenharmony_ci		   ((buffer = lcs_get_buffer(&card->write)) != NULL));
7448c2ecf20Sopenharmony_ci	count += sizeof(struct lcs_header);
7458c2ecf20Sopenharmony_ci	*(__u16 *)(buffer->data + count) = 0;
7468c2ecf20Sopenharmony_ci	buffer->count = count + sizeof(__u16);
7478c2ecf20Sopenharmony_ci	buffer->callback = lcs_release_buffer;
7488c2ecf20Sopenharmony_ci	cmd = (struct lcs_cmd *) buffer->data;
7498c2ecf20Sopenharmony_ci	cmd->offset = count;
7508c2ecf20Sopenharmony_ci	cmd->type = LCS_FRAME_TYPE_CONTROL;
7518c2ecf20Sopenharmony_ci	cmd->slot = 0;
7528c2ecf20Sopenharmony_ci	return buffer;
7538c2ecf20Sopenharmony_ci}
7548c2ecf20Sopenharmony_ci
7558c2ecf20Sopenharmony_ci
7568c2ecf20Sopenharmony_cistatic void
7578c2ecf20Sopenharmony_cilcs_get_reply(struct lcs_reply *reply)
7588c2ecf20Sopenharmony_ci{
7598c2ecf20Sopenharmony_ci	refcount_inc(&reply->refcnt);
7608c2ecf20Sopenharmony_ci}
7618c2ecf20Sopenharmony_ci
7628c2ecf20Sopenharmony_cistatic void
7638c2ecf20Sopenharmony_cilcs_put_reply(struct lcs_reply *reply)
7648c2ecf20Sopenharmony_ci{
7658c2ecf20Sopenharmony_ci	if (refcount_dec_and_test(&reply->refcnt))
7668c2ecf20Sopenharmony_ci		kfree(reply);
7678c2ecf20Sopenharmony_ci}
7688c2ecf20Sopenharmony_ci
7698c2ecf20Sopenharmony_cistatic struct lcs_reply *
7708c2ecf20Sopenharmony_cilcs_alloc_reply(struct lcs_cmd *cmd)
7718c2ecf20Sopenharmony_ci{
7728c2ecf20Sopenharmony_ci	struct lcs_reply *reply;
7738c2ecf20Sopenharmony_ci
7748c2ecf20Sopenharmony_ci	LCS_DBF_TEXT(4, trace, "getreply");
7758c2ecf20Sopenharmony_ci
7768c2ecf20Sopenharmony_ci	reply = kzalloc(sizeof(struct lcs_reply), GFP_ATOMIC);
7778c2ecf20Sopenharmony_ci	if (!reply)
7788c2ecf20Sopenharmony_ci		return NULL;
7798c2ecf20Sopenharmony_ci	refcount_set(&reply->refcnt, 1);
7808c2ecf20Sopenharmony_ci	reply->sequence_no = cmd->sequence_no;
7818c2ecf20Sopenharmony_ci	reply->received = 0;
7828c2ecf20Sopenharmony_ci	reply->rc = 0;
7838c2ecf20Sopenharmony_ci	init_waitqueue_head(&reply->wait_q);
7848c2ecf20Sopenharmony_ci
7858c2ecf20Sopenharmony_ci	return reply;
7868c2ecf20Sopenharmony_ci}
7878c2ecf20Sopenharmony_ci
7888c2ecf20Sopenharmony_ci/**
7898c2ecf20Sopenharmony_ci * Notifier function for lancmd replies. Called from read irq.
7908c2ecf20Sopenharmony_ci */
7918c2ecf20Sopenharmony_cistatic void
7928c2ecf20Sopenharmony_cilcs_notify_lancmd_waiters(struct lcs_card *card, struct lcs_cmd *cmd)
7938c2ecf20Sopenharmony_ci{
7948c2ecf20Sopenharmony_ci	struct list_head *l, *n;
7958c2ecf20Sopenharmony_ci	struct lcs_reply *reply;
7968c2ecf20Sopenharmony_ci
7978c2ecf20Sopenharmony_ci	LCS_DBF_TEXT(4, trace, "notiwait");
7988c2ecf20Sopenharmony_ci	spin_lock(&card->lock);
7998c2ecf20Sopenharmony_ci	list_for_each_safe(l, n, &card->lancmd_waiters) {
8008c2ecf20Sopenharmony_ci		reply = list_entry(l, struct lcs_reply, list);
8018c2ecf20Sopenharmony_ci		if (reply->sequence_no == cmd->sequence_no) {
8028c2ecf20Sopenharmony_ci			lcs_get_reply(reply);
8038c2ecf20Sopenharmony_ci			list_del_init(&reply->list);
8048c2ecf20Sopenharmony_ci			if (reply->callback != NULL)
8058c2ecf20Sopenharmony_ci				reply->callback(card, cmd);
8068c2ecf20Sopenharmony_ci			reply->received = 1;
8078c2ecf20Sopenharmony_ci			reply->rc = cmd->return_code;
8088c2ecf20Sopenharmony_ci			wake_up(&reply->wait_q);
8098c2ecf20Sopenharmony_ci			lcs_put_reply(reply);
8108c2ecf20Sopenharmony_ci			break;
8118c2ecf20Sopenharmony_ci		}
8128c2ecf20Sopenharmony_ci	}
8138c2ecf20Sopenharmony_ci	spin_unlock(&card->lock);
8148c2ecf20Sopenharmony_ci}
8158c2ecf20Sopenharmony_ci
8168c2ecf20Sopenharmony_ci/**
8178c2ecf20Sopenharmony_ci * Emit buffer of a lan command.
8188c2ecf20Sopenharmony_ci */
8198c2ecf20Sopenharmony_cistatic void
8208c2ecf20Sopenharmony_cilcs_lancmd_timeout(struct timer_list *t)
8218c2ecf20Sopenharmony_ci{
8228c2ecf20Sopenharmony_ci	struct lcs_reply *reply = from_timer(reply, t, timer);
8238c2ecf20Sopenharmony_ci	struct lcs_reply *list_reply, *r;
8248c2ecf20Sopenharmony_ci	unsigned long flags;
8258c2ecf20Sopenharmony_ci
8268c2ecf20Sopenharmony_ci	LCS_DBF_TEXT(4, trace, "timeout");
8278c2ecf20Sopenharmony_ci	spin_lock_irqsave(&reply->card->lock, flags);
8288c2ecf20Sopenharmony_ci	list_for_each_entry_safe(list_reply, r,
8298c2ecf20Sopenharmony_ci				 &reply->card->lancmd_waiters,list) {
8308c2ecf20Sopenharmony_ci		if (reply == list_reply) {
8318c2ecf20Sopenharmony_ci			lcs_get_reply(reply);
8328c2ecf20Sopenharmony_ci			list_del_init(&reply->list);
8338c2ecf20Sopenharmony_ci			spin_unlock_irqrestore(&reply->card->lock, flags);
8348c2ecf20Sopenharmony_ci			reply->received = 1;
8358c2ecf20Sopenharmony_ci			reply->rc = -ETIME;
8368c2ecf20Sopenharmony_ci			wake_up(&reply->wait_q);
8378c2ecf20Sopenharmony_ci			lcs_put_reply(reply);
8388c2ecf20Sopenharmony_ci			return;
8398c2ecf20Sopenharmony_ci		}
8408c2ecf20Sopenharmony_ci	}
8418c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&reply->card->lock, flags);
8428c2ecf20Sopenharmony_ci}
8438c2ecf20Sopenharmony_ci
8448c2ecf20Sopenharmony_cistatic int
8458c2ecf20Sopenharmony_cilcs_send_lancmd(struct lcs_card *card, struct lcs_buffer *buffer,
8468c2ecf20Sopenharmony_ci		void (*reply_callback)(struct lcs_card *, struct lcs_cmd *))
8478c2ecf20Sopenharmony_ci{
8488c2ecf20Sopenharmony_ci	struct lcs_reply *reply;
8498c2ecf20Sopenharmony_ci	struct lcs_cmd *cmd;
8508c2ecf20Sopenharmony_ci	unsigned long flags;
8518c2ecf20Sopenharmony_ci	int rc;
8528c2ecf20Sopenharmony_ci
8538c2ecf20Sopenharmony_ci	LCS_DBF_TEXT(4, trace, "sendcmd");
8548c2ecf20Sopenharmony_ci	cmd = (struct lcs_cmd *) buffer->data;
8558c2ecf20Sopenharmony_ci	cmd->return_code = 0;
8568c2ecf20Sopenharmony_ci	cmd->sequence_no = card->sequence_no++;
8578c2ecf20Sopenharmony_ci	reply = lcs_alloc_reply(cmd);
8588c2ecf20Sopenharmony_ci	if (!reply)
8598c2ecf20Sopenharmony_ci		return -ENOMEM;
8608c2ecf20Sopenharmony_ci	reply->callback = reply_callback;
8618c2ecf20Sopenharmony_ci	reply->card = card;
8628c2ecf20Sopenharmony_ci	spin_lock_irqsave(&card->lock, flags);
8638c2ecf20Sopenharmony_ci	list_add_tail(&reply->list, &card->lancmd_waiters);
8648c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&card->lock, flags);
8658c2ecf20Sopenharmony_ci
8668c2ecf20Sopenharmony_ci	buffer->callback = lcs_release_buffer;
8678c2ecf20Sopenharmony_ci	rc = lcs_ready_buffer(&card->write, buffer);
8688c2ecf20Sopenharmony_ci	if (rc)
8698c2ecf20Sopenharmony_ci		return rc;
8708c2ecf20Sopenharmony_ci	timer_setup(&reply->timer, lcs_lancmd_timeout, 0);
8718c2ecf20Sopenharmony_ci	mod_timer(&reply->timer, jiffies + HZ * card->lancmd_timeout);
8728c2ecf20Sopenharmony_ci	wait_event(reply->wait_q, reply->received);
8738c2ecf20Sopenharmony_ci	del_timer_sync(&reply->timer);
8748c2ecf20Sopenharmony_ci	LCS_DBF_TEXT_(4, trace, "rc:%d",reply->rc);
8758c2ecf20Sopenharmony_ci	rc = reply->rc;
8768c2ecf20Sopenharmony_ci	lcs_put_reply(reply);
8778c2ecf20Sopenharmony_ci	return rc ? -EIO : 0;
8788c2ecf20Sopenharmony_ci}
8798c2ecf20Sopenharmony_ci
8808c2ecf20Sopenharmony_ci/**
8818c2ecf20Sopenharmony_ci * LCS startup command
8828c2ecf20Sopenharmony_ci */
8838c2ecf20Sopenharmony_cistatic int
8848c2ecf20Sopenharmony_cilcs_send_startup(struct lcs_card *card, __u8 initiator)
8858c2ecf20Sopenharmony_ci{
8868c2ecf20Sopenharmony_ci	struct lcs_buffer *buffer;
8878c2ecf20Sopenharmony_ci	struct lcs_cmd *cmd;
8888c2ecf20Sopenharmony_ci
8898c2ecf20Sopenharmony_ci	LCS_DBF_TEXT(2, trace, "startup");
8908c2ecf20Sopenharmony_ci	buffer = lcs_get_lancmd(card, LCS_STD_CMD_SIZE);
8918c2ecf20Sopenharmony_ci	cmd = (struct lcs_cmd *) buffer->data;
8928c2ecf20Sopenharmony_ci	cmd->cmd_code = LCS_CMD_STARTUP;
8938c2ecf20Sopenharmony_ci	cmd->initiator = initiator;
8948c2ecf20Sopenharmony_ci	cmd->cmd.lcs_startup.buff_size = LCS_IOBUFFERSIZE;
8958c2ecf20Sopenharmony_ci	return lcs_send_lancmd(card, buffer, NULL);
8968c2ecf20Sopenharmony_ci}
8978c2ecf20Sopenharmony_ci
8988c2ecf20Sopenharmony_ci/**
8998c2ecf20Sopenharmony_ci * LCS shutdown command
9008c2ecf20Sopenharmony_ci */
9018c2ecf20Sopenharmony_cistatic int
9028c2ecf20Sopenharmony_cilcs_send_shutdown(struct lcs_card *card)
9038c2ecf20Sopenharmony_ci{
9048c2ecf20Sopenharmony_ci	struct lcs_buffer *buffer;
9058c2ecf20Sopenharmony_ci	struct lcs_cmd *cmd;
9068c2ecf20Sopenharmony_ci
9078c2ecf20Sopenharmony_ci	LCS_DBF_TEXT(2, trace, "shutdown");
9088c2ecf20Sopenharmony_ci	buffer = lcs_get_lancmd(card, LCS_STD_CMD_SIZE);
9098c2ecf20Sopenharmony_ci	cmd = (struct lcs_cmd *) buffer->data;
9108c2ecf20Sopenharmony_ci	cmd->cmd_code = LCS_CMD_SHUTDOWN;
9118c2ecf20Sopenharmony_ci	cmd->initiator = LCS_INITIATOR_TCPIP;
9128c2ecf20Sopenharmony_ci	return lcs_send_lancmd(card, buffer, NULL);
9138c2ecf20Sopenharmony_ci}
9148c2ecf20Sopenharmony_ci
9158c2ecf20Sopenharmony_ci/**
9168c2ecf20Sopenharmony_ci * LCS lanstat command
9178c2ecf20Sopenharmony_ci */
9188c2ecf20Sopenharmony_cistatic void
9198c2ecf20Sopenharmony_ci__lcs_lanstat_cb(struct lcs_card *card, struct lcs_cmd *cmd)
9208c2ecf20Sopenharmony_ci{
9218c2ecf20Sopenharmony_ci	LCS_DBF_TEXT(2, trace, "statcb");
9228c2ecf20Sopenharmony_ci	memcpy(card->mac, cmd->cmd.lcs_lanstat_cmd.mac_addr, LCS_MAC_LENGTH);
9238c2ecf20Sopenharmony_ci}
9248c2ecf20Sopenharmony_ci
9258c2ecf20Sopenharmony_cistatic int
9268c2ecf20Sopenharmony_cilcs_send_lanstat(struct lcs_card *card)
9278c2ecf20Sopenharmony_ci{
9288c2ecf20Sopenharmony_ci	struct lcs_buffer *buffer;
9298c2ecf20Sopenharmony_ci	struct lcs_cmd *cmd;
9308c2ecf20Sopenharmony_ci
9318c2ecf20Sopenharmony_ci	LCS_DBF_TEXT(2,trace, "cmdstat");
9328c2ecf20Sopenharmony_ci	buffer = lcs_get_lancmd(card, LCS_STD_CMD_SIZE);
9338c2ecf20Sopenharmony_ci	cmd = (struct lcs_cmd *) buffer->data;
9348c2ecf20Sopenharmony_ci	/* Setup lanstat command. */
9358c2ecf20Sopenharmony_ci	cmd->cmd_code = LCS_CMD_LANSTAT;
9368c2ecf20Sopenharmony_ci	cmd->initiator = LCS_INITIATOR_TCPIP;
9378c2ecf20Sopenharmony_ci	cmd->cmd.lcs_std_cmd.lan_type = card->lan_type;
9388c2ecf20Sopenharmony_ci	cmd->cmd.lcs_std_cmd.portno = card->portno;
9398c2ecf20Sopenharmony_ci	return lcs_send_lancmd(card, buffer, __lcs_lanstat_cb);
9408c2ecf20Sopenharmony_ci}
9418c2ecf20Sopenharmony_ci
9428c2ecf20Sopenharmony_ci/**
9438c2ecf20Sopenharmony_ci * send stoplan command
9448c2ecf20Sopenharmony_ci */
9458c2ecf20Sopenharmony_cistatic int
9468c2ecf20Sopenharmony_cilcs_send_stoplan(struct lcs_card *card, __u8 initiator)
9478c2ecf20Sopenharmony_ci{
9488c2ecf20Sopenharmony_ci	struct lcs_buffer *buffer;
9498c2ecf20Sopenharmony_ci	struct lcs_cmd *cmd;
9508c2ecf20Sopenharmony_ci
9518c2ecf20Sopenharmony_ci	LCS_DBF_TEXT(2, trace, "cmdstpln");
9528c2ecf20Sopenharmony_ci	buffer = lcs_get_lancmd(card, LCS_STD_CMD_SIZE);
9538c2ecf20Sopenharmony_ci	cmd = (struct lcs_cmd *) buffer->data;
9548c2ecf20Sopenharmony_ci	cmd->cmd_code = LCS_CMD_STOPLAN;
9558c2ecf20Sopenharmony_ci	cmd->initiator = initiator;
9568c2ecf20Sopenharmony_ci	cmd->cmd.lcs_std_cmd.lan_type = card->lan_type;
9578c2ecf20Sopenharmony_ci	cmd->cmd.lcs_std_cmd.portno = card->portno;
9588c2ecf20Sopenharmony_ci	return lcs_send_lancmd(card, buffer, NULL);
9598c2ecf20Sopenharmony_ci}
9608c2ecf20Sopenharmony_ci
9618c2ecf20Sopenharmony_ci/**
9628c2ecf20Sopenharmony_ci * send startlan command
9638c2ecf20Sopenharmony_ci */
9648c2ecf20Sopenharmony_cistatic void
9658c2ecf20Sopenharmony_ci__lcs_send_startlan_cb(struct lcs_card *card, struct lcs_cmd *cmd)
9668c2ecf20Sopenharmony_ci{
9678c2ecf20Sopenharmony_ci	LCS_DBF_TEXT(2, trace, "srtlancb");
9688c2ecf20Sopenharmony_ci	card->lan_type = cmd->cmd.lcs_std_cmd.lan_type;
9698c2ecf20Sopenharmony_ci	card->portno = cmd->cmd.lcs_std_cmd.portno;
9708c2ecf20Sopenharmony_ci}
9718c2ecf20Sopenharmony_ci
9728c2ecf20Sopenharmony_cistatic int
9738c2ecf20Sopenharmony_cilcs_send_startlan(struct lcs_card *card, __u8 initiator)
9748c2ecf20Sopenharmony_ci{
9758c2ecf20Sopenharmony_ci	struct lcs_buffer *buffer;
9768c2ecf20Sopenharmony_ci	struct lcs_cmd *cmd;
9778c2ecf20Sopenharmony_ci
9788c2ecf20Sopenharmony_ci	LCS_DBF_TEXT(2, trace, "cmdstaln");
9798c2ecf20Sopenharmony_ci	buffer = lcs_get_lancmd(card, LCS_STD_CMD_SIZE);
9808c2ecf20Sopenharmony_ci	cmd = (struct lcs_cmd *) buffer->data;
9818c2ecf20Sopenharmony_ci	cmd->cmd_code = LCS_CMD_STARTLAN;
9828c2ecf20Sopenharmony_ci	cmd->initiator = initiator;
9838c2ecf20Sopenharmony_ci	cmd->cmd.lcs_std_cmd.lan_type = card->lan_type;
9848c2ecf20Sopenharmony_ci	cmd->cmd.lcs_std_cmd.portno = card->portno;
9858c2ecf20Sopenharmony_ci	return lcs_send_lancmd(card, buffer, __lcs_send_startlan_cb);
9868c2ecf20Sopenharmony_ci}
9878c2ecf20Sopenharmony_ci
9888c2ecf20Sopenharmony_ci#ifdef CONFIG_IP_MULTICAST
9898c2ecf20Sopenharmony_ci/**
9908c2ecf20Sopenharmony_ci * send setipm command (Multicast)
9918c2ecf20Sopenharmony_ci */
9928c2ecf20Sopenharmony_cistatic int
9938c2ecf20Sopenharmony_cilcs_send_setipm(struct lcs_card *card,struct lcs_ipm_list *ipm_list)
9948c2ecf20Sopenharmony_ci{
9958c2ecf20Sopenharmony_ci	struct lcs_buffer *buffer;
9968c2ecf20Sopenharmony_ci	struct lcs_cmd *cmd;
9978c2ecf20Sopenharmony_ci
9988c2ecf20Sopenharmony_ci	LCS_DBF_TEXT(2, trace, "cmdsetim");
9998c2ecf20Sopenharmony_ci	buffer = lcs_get_lancmd(card, LCS_MULTICAST_CMD_SIZE);
10008c2ecf20Sopenharmony_ci	cmd = (struct lcs_cmd *) buffer->data;
10018c2ecf20Sopenharmony_ci	cmd->cmd_code = LCS_CMD_SETIPM;
10028c2ecf20Sopenharmony_ci	cmd->initiator = LCS_INITIATOR_TCPIP;
10038c2ecf20Sopenharmony_ci	cmd->cmd.lcs_qipassist.lan_type = card->lan_type;
10048c2ecf20Sopenharmony_ci	cmd->cmd.lcs_qipassist.portno = card->portno;
10058c2ecf20Sopenharmony_ci	cmd->cmd.lcs_qipassist.version = 4;
10068c2ecf20Sopenharmony_ci	cmd->cmd.lcs_qipassist.num_ip_pairs = 1;
10078c2ecf20Sopenharmony_ci	memcpy(cmd->cmd.lcs_qipassist.lcs_ipass_ctlmsg.ip_mac_pair,
10088c2ecf20Sopenharmony_ci	       &ipm_list->ipm, sizeof (struct lcs_ip_mac_pair));
10098c2ecf20Sopenharmony_ci	LCS_DBF_TEXT_(2, trace, "%x",ipm_list->ipm.ip_addr);
10108c2ecf20Sopenharmony_ci	return lcs_send_lancmd(card, buffer, NULL);
10118c2ecf20Sopenharmony_ci}
10128c2ecf20Sopenharmony_ci
10138c2ecf20Sopenharmony_ci/**
10148c2ecf20Sopenharmony_ci * send delipm command (Multicast)
10158c2ecf20Sopenharmony_ci */
10168c2ecf20Sopenharmony_cistatic int
10178c2ecf20Sopenharmony_cilcs_send_delipm(struct lcs_card *card,struct lcs_ipm_list *ipm_list)
10188c2ecf20Sopenharmony_ci{
10198c2ecf20Sopenharmony_ci	struct lcs_buffer *buffer;
10208c2ecf20Sopenharmony_ci	struct lcs_cmd *cmd;
10218c2ecf20Sopenharmony_ci
10228c2ecf20Sopenharmony_ci	LCS_DBF_TEXT(2, trace, "cmddelim");
10238c2ecf20Sopenharmony_ci	buffer = lcs_get_lancmd(card, LCS_MULTICAST_CMD_SIZE);
10248c2ecf20Sopenharmony_ci	cmd = (struct lcs_cmd *) buffer->data;
10258c2ecf20Sopenharmony_ci	cmd->cmd_code = LCS_CMD_DELIPM;
10268c2ecf20Sopenharmony_ci	cmd->initiator = LCS_INITIATOR_TCPIP;
10278c2ecf20Sopenharmony_ci	cmd->cmd.lcs_qipassist.lan_type = card->lan_type;
10288c2ecf20Sopenharmony_ci	cmd->cmd.lcs_qipassist.portno = card->portno;
10298c2ecf20Sopenharmony_ci	cmd->cmd.lcs_qipassist.version = 4;
10308c2ecf20Sopenharmony_ci	cmd->cmd.lcs_qipassist.num_ip_pairs = 1;
10318c2ecf20Sopenharmony_ci	memcpy(cmd->cmd.lcs_qipassist.lcs_ipass_ctlmsg.ip_mac_pair,
10328c2ecf20Sopenharmony_ci	       &ipm_list->ipm, sizeof (struct lcs_ip_mac_pair));
10338c2ecf20Sopenharmony_ci	LCS_DBF_TEXT_(2, trace, "%x",ipm_list->ipm.ip_addr);
10348c2ecf20Sopenharmony_ci	return lcs_send_lancmd(card, buffer, NULL);
10358c2ecf20Sopenharmony_ci}
10368c2ecf20Sopenharmony_ci
10378c2ecf20Sopenharmony_ci/**
10388c2ecf20Sopenharmony_ci * check if multicast is supported by LCS
10398c2ecf20Sopenharmony_ci */
10408c2ecf20Sopenharmony_cistatic void
10418c2ecf20Sopenharmony_ci__lcs_check_multicast_cb(struct lcs_card *card, struct lcs_cmd *cmd)
10428c2ecf20Sopenharmony_ci{
10438c2ecf20Sopenharmony_ci	LCS_DBF_TEXT(2, trace, "chkmccb");
10448c2ecf20Sopenharmony_ci	card->ip_assists_supported =
10458c2ecf20Sopenharmony_ci		cmd->cmd.lcs_qipassist.ip_assists_supported;
10468c2ecf20Sopenharmony_ci	card->ip_assists_enabled =
10478c2ecf20Sopenharmony_ci		cmd->cmd.lcs_qipassist.ip_assists_enabled;
10488c2ecf20Sopenharmony_ci}
10498c2ecf20Sopenharmony_ci
10508c2ecf20Sopenharmony_cistatic int
10518c2ecf20Sopenharmony_cilcs_check_multicast_support(struct lcs_card *card)
10528c2ecf20Sopenharmony_ci{
10538c2ecf20Sopenharmony_ci	struct lcs_buffer *buffer;
10548c2ecf20Sopenharmony_ci	struct lcs_cmd *cmd;
10558c2ecf20Sopenharmony_ci	int rc;
10568c2ecf20Sopenharmony_ci
10578c2ecf20Sopenharmony_ci	LCS_DBF_TEXT(2, trace, "cmdqipa");
10588c2ecf20Sopenharmony_ci	/* Send query ipassist. */
10598c2ecf20Sopenharmony_ci	buffer = lcs_get_lancmd(card, LCS_STD_CMD_SIZE);
10608c2ecf20Sopenharmony_ci	cmd = (struct lcs_cmd *) buffer->data;
10618c2ecf20Sopenharmony_ci	cmd->cmd_code = LCS_CMD_QIPASSIST;
10628c2ecf20Sopenharmony_ci	cmd->initiator = LCS_INITIATOR_TCPIP;
10638c2ecf20Sopenharmony_ci	cmd->cmd.lcs_qipassist.lan_type = card->lan_type;
10648c2ecf20Sopenharmony_ci	cmd->cmd.lcs_qipassist.portno = card->portno;
10658c2ecf20Sopenharmony_ci	cmd->cmd.lcs_qipassist.version = 4;
10668c2ecf20Sopenharmony_ci	cmd->cmd.lcs_qipassist.num_ip_pairs = 1;
10678c2ecf20Sopenharmony_ci	rc = lcs_send_lancmd(card, buffer, __lcs_check_multicast_cb);
10688c2ecf20Sopenharmony_ci	if (rc != 0) {
10698c2ecf20Sopenharmony_ci		pr_err("Query IPAssist failed. Assuming unsupported!\n");
10708c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
10718c2ecf20Sopenharmony_ci	}
10728c2ecf20Sopenharmony_ci	if (card->ip_assists_supported & LCS_IPASS_MULTICAST_SUPPORT)
10738c2ecf20Sopenharmony_ci		return 0;
10748c2ecf20Sopenharmony_ci	return -EOPNOTSUPP;
10758c2ecf20Sopenharmony_ci}
10768c2ecf20Sopenharmony_ci
10778c2ecf20Sopenharmony_ci/**
10788c2ecf20Sopenharmony_ci * set or del multicast address on LCS card
10798c2ecf20Sopenharmony_ci */
10808c2ecf20Sopenharmony_cistatic void
10818c2ecf20Sopenharmony_cilcs_fix_multicast_list(struct lcs_card *card)
10828c2ecf20Sopenharmony_ci{
10838c2ecf20Sopenharmony_ci	struct list_head failed_list;
10848c2ecf20Sopenharmony_ci	struct lcs_ipm_list *ipm, *tmp;
10858c2ecf20Sopenharmony_ci	unsigned long flags;
10868c2ecf20Sopenharmony_ci	int rc;
10878c2ecf20Sopenharmony_ci
10888c2ecf20Sopenharmony_ci	LCS_DBF_TEXT(4,trace, "fixipm");
10898c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&failed_list);
10908c2ecf20Sopenharmony_ci	spin_lock_irqsave(&card->ipm_lock, flags);
10918c2ecf20Sopenharmony_cilist_modified:
10928c2ecf20Sopenharmony_ci	list_for_each_entry_safe(ipm, tmp, &card->ipm_list, list){
10938c2ecf20Sopenharmony_ci		switch (ipm->ipm_state) {
10948c2ecf20Sopenharmony_ci		case LCS_IPM_STATE_SET_REQUIRED:
10958c2ecf20Sopenharmony_ci			/* del from ipm_list so no one else can tamper with
10968c2ecf20Sopenharmony_ci			 * this entry */
10978c2ecf20Sopenharmony_ci			list_del_init(&ipm->list);
10988c2ecf20Sopenharmony_ci			spin_unlock_irqrestore(&card->ipm_lock, flags);
10998c2ecf20Sopenharmony_ci			rc = lcs_send_setipm(card, ipm);
11008c2ecf20Sopenharmony_ci			spin_lock_irqsave(&card->ipm_lock, flags);
11018c2ecf20Sopenharmony_ci			if (rc) {
11028c2ecf20Sopenharmony_ci				pr_info("Adding multicast address failed."
11038c2ecf20Sopenharmony_ci					" Table possibly full!\n");
11048c2ecf20Sopenharmony_ci				/* store ipm in failed list -> will be added
11058c2ecf20Sopenharmony_ci				 * to ipm_list again, so a retry will be done
11068c2ecf20Sopenharmony_ci				 * during the next call of this function */
11078c2ecf20Sopenharmony_ci				list_add_tail(&ipm->list, &failed_list);
11088c2ecf20Sopenharmony_ci			} else {
11098c2ecf20Sopenharmony_ci				ipm->ipm_state = LCS_IPM_STATE_ON_CARD;
11108c2ecf20Sopenharmony_ci				/* re-insert into ipm_list */
11118c2ecf20Sopenharmony_ci				list_add_tail(&ipm->list, &card->ipm_list);
11128c2ecf20Sopenharmony_ci			}
11138c2ecf20Sopenharmony_ci			goto list_modified;
11148c2ecf20Sopenharmony_ci		case LCS_IPM_STATE_DEL_REQUIRED:
11158c2ecf20Sopenharmony_ci			list_del(&ipm->list);
11168c2ecf20Sopenharmony_ci			spin_unlock_irqrestore(&card->ipm_lock, flags);
11178c2ecf20Sopenharmony_ci			lcs_send_delipm(card, ipm);
11188c2ecf20Sopenharmony_ci			spin_lock_irqsave(&card->ipm_lock, flags);
11198c2ecf20Sopenharmony_ci			kfree(ipm);
11208c2ecf20Sopenharmony_ci			goto list_modified;
11218c2ecf20Sopenharmony_ci		case LCS_IPM_STATE_ON_CARD:
11228c2ecf20Sopenharmony_ci			break;
11238c2ecf20Sopenharmony_ci		}
11248c2ecf20Sopenharmony_ci	}
11258c2ecf20Sopenharmony_ci	/* re-insert all entries from the failed_list into ipm_list */
11268c2ecf20Sopenharmony_ci	list_for_each_entry_safe(ipm, tmp, &failed_list, list)
11278c2ecf20Sopenharmony_ci		list_move_tail(&ipm->list, &card->ipm_list);
11288c2ecf20Sopenharmony_ci
11298c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&card->ipm_lock, flags);
11308c2ecf20Sopenharmony_ci}
11318c2ecf20Sopenharmony_ci
11328c2ecf20Sopenharmony_ci/**
11338c2ecf20Sopenharmony_ci * get mac address for the relevant Multicast address
11348c2ecf20Sopenharmony_ci */
11358c2ecf20Sopenharmony_cistatic void
11368c2ecf20Sopenharmony_cilcs_get_mac_for_ipm(__be32 ipm, char *mac, struct net_device *dev)
11378c2ecf20Sopenharmony_ci{
11388c2ecf20Sopenharmony_ci	LCS_DBF_TEXT(4,trace, "getmac");
11398c2ecf20Sopenharmony_ci	ip_eth_mc_map(ipm, mac);
11408c2ecf20Sopenharmony_ci}
11418c2ecf20Sopenharmony_ci
11428c2ecf20Sopenharmony_ci/**
11438c2ecf20Sopenharmony_ci * function called by net device to handle multicast address relevant things
11448c2ecf20Sopenharmony_ci */
11458c2ecf20Sopenharmony_cistatic void lcs_remove_mc_addresses(struct lcs_card *card,
11468c2ecf20Sopenharmony_ci				    struct in_device *in4_dev)
11478c2ecf20Sopenharmony_ci{
11488c2ecf20Sopenharmony_ci	struct ip_mc_list *im4;
11498c2ecf20Sopenharmony_ci	struct list_head *l;
11508c2ecf20Sopenharmony_ci	struct lcs_ipm_list *ipm;
11518c2ecf20Sopenharmony_ci	unsigned long flags;
11528c2ecf20Sopenharmony_ci	char buf[MAX_ADDR_LEN];
11538c2ecf20Sopenharmony_ci
11548c2ecf20Sopenharmony_ci	LCS_DBF_TEXT(4, trace, "remmclst");
11558c2ecf20Sopenharmony_ci	spin_lock_irqsave(&card->ipm_lock, flags);
11568c2ecf20Sopenharmony_ci	list_for_each(l, &card->ipm_list) {
11578c2ecf20Sopenharmony_ci		ipm = list_entry(l, struct lcs_ipm_list, list);
11588c2ecf20Sopenharmony_ci		for (im4 = rcu_dereference(in4_dev->mc_list);
11598c2ecf20Sopenharmony_ci		     im4 != NULL; im4 = rcu_dereference(im4->next_rcu)) {
11608c2ecf20Sopenharmony_ci			lcs_get_mac_for_ipm(im4->multiaddr, buf, card->dev);
11618c2ecf20Sopenharmony_ci			if ( (ipm->ipm.ip_addr == im4->multiaddr) &&
11628c2ecf20Sopenharmony_ci			     (memcmp(buf, &ipm->ipm.mac_addr,
11638c2ecf20Sopenharmony_ci				     LCS_MAC_LENGTH) == 0) )
11648c2ecf20Sopenharmony_ci				break;
11658c2ecf20Sopenharmony_ci		}
11668c2ecf20Sopenharmony_ci		if (im4 == NULL)
11678c2ecf20Sopenharmony_ci			ipm->ipm_state = LCS_IPM_STATE_DEL_REQUIRED;
11688c2ecf20Sopenharmony_ci	}
11698c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&card->ipm_lock, flags);
11708c2ecf20Sopenharmony_ci}
11718c2ecf20Sopenharmony_ci
11728c2ecf20Sopenharmony_cistatic struct lcs_ipm_list *lcs_check_addr_entry(struct lcs_card *card,
11738c2ecf20Sopenharmony_ci						 struct ip_mc_list *im4,
11748c2ecf20Sopenharmony_ci						 char *buf)
11758c2ecf20Sopenharmony_ci{
11768c2ecf20Sopenharmony_ci	struct lcs_ipm_list *tmp, *ipm = NULL;
11778c2ecf20Sopenharmony_ci	struct list_head *l;
11788c2ecf20Sopenharmony_ci	unsigned long flags;
11798c2ecf20Sopenharmony_ci
11808c2ecf20Sopenharmony_ci	LCS_DBF_TEXT(4, trace, "chkmcent");
11818c2ecf20Sopenharmony_ci	spin_lock_irqsave(&card->ipm_lock, flags);
11828c2ecf20Sopenharmony_ci	list_for_each(l, &card->ipm_list) {
11838c2ecf20Sopenharmony_ci		tmp = list_entry(l, struct lcs_ipm_list, list);
11848c2ecf20Sopenharmony_ci		if ( (tmp->ipm.ip_addr == im4->multiaddr) &&
11858c2ecf20Sopenharmony_ci		     (memcmp(buf, &tmp->ipm.mac_addr,
11868c2ecf20Sopenharmony_ci			     LCS_MAC_LENGTH) == 0) ) {
11878c2ecf20Sopenharmony_ci			ipm = tmp;
11888c2ecf20Sopenharmony_ci			break;
11898c2ecf20Sopenharmony_ci		}
11908c2ecf20Sopenharmony_ci	}
11918c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&card->ipm_lock, flags);
11928c2ecf20Sopenharmony_ci	return ipm;
11938c2ecf20Sopenharmony_ci}
11948c2ecf20Sopenharmony_ci
11958c2ecf20Sopenharmony_cistatic void lcs_set_mc_addresses(struct lcs_card *card,
11968c2ecf20Sopenharmony_ci				 struct in_device *in4_dev)
11978c2ecf20Sopenharmony_ci{
11988c2ecf20Sopenharmony_ci
11998c2ecf20Sopenharmony_ci	struct ip_mc_list *im4;
12008c2ecf20Sopenharmony_ci	struct lcs_ipm_list *ipm;
12018c2ecf20Sopenharmony_ci	char buf[MAX_ADDR_LEN];
12028c2ecf20Sopenharmony_ci	unsigned long flags;
12038c2ecf20Sopenharmony_ci
12048c2ecf20Sopenharmony_ci	LCS_DBF_TEXT(4, trace, "setmclst");
12058c2ecf20Sopenharmony_ci	for (im4 = rcu_dereference(in4_dev->mc_list); im4 != NULL;
12068c2ecf20Sopenharmony_ci	     im4 = rcu_dereference(im4->next_rcu)) {
12078c2ecf20Sopenharmony_ci		lcs_get_mac_for_ipm(im4->multiaddr, buf, card->dev);
12088c2ecf20Sopenharmony_ci		ipm = lcs_check_addr_entry(card, im4, buf);
12098c2ecf20Sopenharmony_ci		if (ipm != NULL)
12108c2ecf20Sopenharmony_ci			continue;	/* Address already in list. */
12118c2ecf20Sopenharmony_ci		ipm = kzalloc(sizeof(struct lcs_ipm_list), GFP_ATOMIC);
12128c2ecf20Sopenharmony_ci		if (ipm == NULL) {
12138c2ecf20Sopenharmony_ci			pr_info("Not enough memory to add"
12148c2ecf20Sopenharmony_ci				" new multicast entry!\n");
12158c2ecf20Sopenharmony_ci			break;
12168c2ecf20Sopenharmony_ci		}
12178c2ecf20Sopenharmony_ci		memcpy(&ipm->ipm.mac_addr, buf, LCS_MAC_LENGTH);
12188c2ecf20Sopenharmony_ci		ipm->ipm.ip_addr = im4->multiaddr;
12198c2ecf20Sopenharmony_ci		ipm->ipm_state = LCS_IPM_STATE_SET_REQUIRED;
12208c2ecf20Sopenharmony_ci		spin_lock_irqsave(&card->ipm_lock, flags);
12218c2ecf20Sopenharmony_ci		LCS_DBF_HEX(2,trace,&ipm->ipm.ip_addr,4);
12228c2ecf20Sopenharmony_ci		list_add(&ipm->list, &card->ipm_list);
12238c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&card->ipm_lock, flags);
12248c2ecf20Sopenharmony_ci	}
12258c2ecf20Sopenharmony_ci}
12268c2ecf20Sopenharmony_ci
12278c2ecf20Sopenharmony_cistatic int
12288c2ecf20Sopenharmony_cilcs_register_mc_addresses(void *data)
12298c2ecf20Sopenharmony_ci{
12308c2ecf20Sopenharmony_ci	struct lcs_card *card;
12318c2ecf20Sopenharmony_ci	struct in_device *in4_dev;
12328c2ecf20Sopenharmony_ci
12338c2ecf20Sopenharmony_ci	card = (struct lcs_card *) data;
12348c2ecf20Sopenharmony_ci
12358c2ecf20Sopenharmony_ci	if (!lcs_do_run_thread(card, LCS_SET_MC_THREAD))
12368c2ecf20Sopenharmony_ci		return 0;
12378c2ecf20Sopenharmony_ci	LCS_DBF_TEXT(4, trace, "regmulti");
12388c2ecf20Sopenharmony_ci
12398c2ecf20Sopenharmony_ci	in4_dev = in_dev_get(card->dev);
12408c2ecf20Sopenharmony_ci	if (in4_dev == NULL)
12418c2ecf20Sopenharmony_ci		goto out;
12428c2ecf20Sopenharmony_ci	rcu_read_lock();
12438c2ecf20Sopenharmony_ci	lcs_remove_mc_addresses(card,in4_dev);
12448c2ecf20Sopenharmony_ci	lcs_set_mc_addresses(card, in4_dev);
12458c2ecf20Sopenharmony_ci	rcu_read_unlock();
12468c2ecf20Sopenharmony_ci	in_dev_put(in4_dev);
12478c2ecf20Sopenharmony_ci
12488c2ecf20Sopenharmony_ci	netif_carrier_off(card->dev);
12498c2ecf20Sopenharmony_ci	netif_tx_disable(card->dev);
12508c2ecf20Sopenharmony_ci	wait_event(card->write.wait_q,
12518c2ecf20Sopenharmony_ci			(card->write.state != LCS_CH_STATE_RUNNING));
12528c2ecf20Sopenharmony_ci	lcs_fix_multicast_list(card);
12538c2ecf20Sopenharmony_ci	if (card->state == DEV_STATE_UP) {
12548c2ecf20Sopenharmony_ci		netif_carrier_on(card->dev);
12558c2ecf20Sopenharmony_ci		netif_wake_queue(card->dev);
12568c2ecf20Sopenharmony_ci	}
12578c2ecf20Sopenharmony_ciout:
12588c2ecf20Sopenharmony_ci	lcs_clear_thread_running_bit(card, LCS_SET_MC_THREAD);
12598c2ecf20Sopenharmony_ci	return 0;
12608c2ecf20Sopenharmony_ci}
12618c2ecf20Sopenharmony_ci#endif /* CONFIG_IP_MULTICAST */
12628c2ecf20Sopenharmony_ci
12638c2ecf20Sopenharmony_ci/**
12648c2ecf20Sopenharmony_ci * function called by net device to
12658c2ecf20Sopenharmony_ci * handle multicast address relevant things
12668c2ecf20Sopenharmony_ci */
12678c2ecf20Sopenharmony_cistatic void
12688c2ecf20Sopenharmony_cilcs_set_multicast_list(struct net_device *dev)
12698c2ecf20Sopenharmony_ci{
12708c2ecf20Sopenharmony_ci#ifdef CONFIG_IP_MULTICAST
12718c2ecf20Sopenharmony_ci        struct lcs_card *card;
12728c2ecf20Sopenharmony_ci
12738c2ecf20Sopenharmony_ci        LCS_DBF_TEXT(4, trace, "setmulti");
12748c2ecf20Sopenharmony_ci        card = (struct lcs_card *) dev->ml_priv;
12758c2ecf20Sopenharmony_ci
12768c2ecf20Sopenharmony_ci        if (!lcs_set_thread_start_bit(card, LCS_SET_MC_THREAD))
12778c2ecf20Sopenharmony_ci		schedule_work(&card->kernel_thread_starter);
12788c2ecf20Sopenharmony_ci#endif /* CONFIG_IP_MULTICAST */
12798c2ecf20Sopenharmony_ci}
12808c2ecf20Sopenharmony_ci
12818c2ecf20Sopenharmony_cistatic long
12828c2ecf20Sopenharmony_cilcs_check_irb_error(struct ccw_device *cdev, struct irb *irb)
12838c2ecf20Sopenharmony_ci{
12848c2ecf20Sopenharmony_ci	if (!IS_ERR(irb))
12858c2ecf20Sopenharmony_ci		return 0;
12868c2ecf20Sopenharmony_ci
12878c2ecf20Sopenharmony_ci	switch (PTR_ERR(irb)) {
12888c2ecf20Sopenharmony_ci	case -EIO:
12898c2ecf20Sopenharmony_ci		dev_warn(&cdev->dev,
12908c2ecf20Sopenharmony_ci			"An I/O-error occurred on the LCS device\n");
12918c2ecf20Sopenharmony_ci		LCS_DBF_TEXT(2, trace, "ckirberr");
12928c2ecf20Sopenharmony_ci		LCS_DBF_TEXT_(2, trace, "  rc%d", -EIO);
12938c2ecf20Sopenharmony_ci		break;
12948c2ecf20Sopenharmony_ci	case -ETIMEDOUT:
12958c2ecf20Sopenharmony_ci		dev_warn(&cdev->dev,
12968c2ecf20Sopenharmony_ci			"A command timed out on the LCS device\n");
12978c2ecf20Sopenharmony_ci		LCS_DBF_TEXT(2, trace, "ckirberr");
12988c2ecf20Sopenharmony_ci		LCS_DBF_TEXT_(2, trace, "  rc%d", -ETIMEDOUT);
12998c2ecf20Sopenharmony_ci		break;
13008c2ecf20Sopenharmony_ci	default:
13018c2ecf20Sopenharmony_ci		dev_warn(&cdev->dev,
13028c2ecf20Sopenharmony_ci			"An error occurred on the LCS device, rc=%ld\n",
13038c2ecf20Sopenharmony_ci			PTR_ERR(irb));
13048c2ecf20Sopenharmony_ci		LCS_DBF_TEXT(2, trace, "ckirberr");
13058c2ecf20Sopenharmony_ci		LCS_DBF_TEXT(2, trace, "  rc???");
13068c2ecf20Sopenharmony_ci	}
13078c2ecf20Sopenharmony_ci	return PTR_ERR(irb);
13088c2ecf20Sopenharmony_ci}
13098c2ecf20Sopenharmony_ci
13108c2ecf20Sopenharmony_cistatic int
13118c2ecf20Sopenharmony_cilcs_get_problem(struct ccw_device *cdev, struct irb *irb)
13128c2ecf20Sopenharmony_ci{
13138c2ecf20Sopenharmony_ci	int dstat, cstat;
13148c2ecf20Sopenharmony_ci	char *sense;
13158c2ecf20Sopenharmony_ci
13168c2ecf20Sopenharmony_ci	sense = (char *) irb->ecw;
13178c2ecf20Sopenharmony_ci	cstat = irb->scsw.cmd.cstat;
13188c2ecf20Sopenharmony_ci	dstat = irb->scsw.cmd.dstat;
13198c2ecf20Sopenharmony_ci
13208c2ecf20Sopenharmony_ci	if (cstat & (SCHN_STAT_CHN_CTRL_CHK | SCHN_STAT_INTF_CTRL_CHK |
13218c2ecf20Sopenharmony_ci		     SCHN_STAT_CHN_DATA_CHK | SCHN_STAT_CHAIN_CHECK |
13228c2ecf20Sopenharmony_ci		     SCHN_STAT_PROT_CHECK   | SCHN_STAT_PROG_CHECK)) {
13238c2ecf20Sopenharmony_ci		LCS_DBF_TEXT(2, trace, "CGENCHK");
13248c2ecf20Sopenharmony_ci		return 1;
13258c2ecf20Sopenharmony_ci	}
13268c2ecf20Sopenharmony_ci	if (dstat & DEV_STAT_UNIT_CHECK) {
13278c2ecf20Sopenharmony_ci		if (sense[LCS_SENSE_BYTE_1] &
13288c2ecf20Sopenharmony_ci		    LCS_SENSE_RESETTING_EVENT) {
13298c2ecf20Sopenharmony_ci			LCS_DBF_TEXT(2, trace, "REVIND");
13308c2ecf20Sopenharmony_ci			return 1;
13318c2ecf20Sopenharmony_ci		}
13328c2ecf20Sopenharmony_ci		if (sense[LCS_SENSE_BYTE_0] &
13338c2ecf20Sopenharmony_ci		    LCS_SENSE_CMD_REJECT) {
13348c2ecf20Sopenharmony_ci			LCS_DBF_TEXT(2, trace, "CMDREJ");
13358c2ecf20Sopenharmony_ci			return 0;
13368c2ecf20Sopenharmony_ci		}
13378c2ecf20Sopenharmony_ci		if ((!sense[LCS_SENSE_BYTE_0]) &&
13388c2ecf20Sopenharmony_ci		    (!sense[LCS_SENSE_BYTE_1]) &&
13398c2ecf20Sopenharmony_ci		    (!sense[LCS_SENSE_BYTE_2]) &&
13408c2ecf20Sopenharmony_ci		    (!sense[LCS_SENSE_BYTE_3])) {
13418c2ecf20Sopenharmony_ci			LCS_DBF_TEXT(2, trace, "ZEROSEN");
13428c2ecf20Sopenharmony_ci			return 0;
13438c2ecf20Sopenharmony_ci		}
13448c2ecf20Sopenharmony_ci		LCS_DBF_TEXT(2, trace, "DGENCHK");
13458c2ecf20Sopenharmony_ci		return 1;
13468c2ecf20Sopenharmony_ci	}
13478c2ecf20Sopenharmony_ci	return 0;
13488c2ecf20Sopenharmony_ci}
13498c2ecf20Sopenharmony_ci
13508c2ecf20Sopenharmony_cistatic void
13518c2ecf20Sopenharmony_cilcs_schedule_recovery(struct lcs_card *card)
13528c2ecf20Sopenharmony_ci{
13538c2ecf20Sopenharmony_ci	LCS_DBF_TEXT(2, trace, "startrec");
13548c2ecf20Sopenharmony_ci	if (!lcs_set_thread_start_bit(card, LCS_RECOVERY_THREAD))
13558c2ecf20Sopenharmony_ci		schedule_work(&card->kernel_thread_starter);
13568c2ecf20Sopenharmony_ci}
13578c2ecf20Sopenharmony_ci
13588c2ecf20Sopenharmony_ci/**
13598c2ecf20Sopenharmony_ci * IRQ Handler for LCS channels
13608c2ecf20Sopenharmony_ci */
13618c2ecf20Sopenharmony_cistatic void
13628c2ecf20Sopenharmony_cilcs_irq(struct ccw_device *cdev, unsigned long intparm, struct irb *irb)
13638c2ecf20Sopenharmony_ci{
13648c2ecf20Sopenharmony_ci	struct lcs_card *card;
13658c2ecf20Sopenharmony_ci	struct lcs_channel *channel;
13668c2ecf20Sopenharmony_ci	int rc, index;
13678c2ecf20Sopenharmony_ci	int cstat, dstat;
13688c2ecf20Sopenharmony_ci
13698c2ecf20Sopenharmony_ci	if (lcs_check_irb_error(cdev, irb))
13708c2ecf20Sopenharmony_ci		return;
13718c2ecf20Sopenharmony_ci
13728c2ecf20Sopenharmony_ci	card = CARD_FROM_DEV(cdev);
13738c2ecf20Sopenharmony_ci	if (card->read.ccwdev == cdev)
13748c2ecf20Sopenharmony_ci		channel = &card->read;
13758c2ecf20Sopenharmony_ci	else
13768c2ecf20Sopenharmony_ci		channel = &card->write;
13778c2ecf20Sopenharmony_ci
13788c2ecf20Sopenharmony_ci	cstat = irb->scsw.cmd.cstat;
13798c2ecf20Sopenharmony_ci	dstat = irb->scsw.cmd.dstat;
13808c2ecf20Sopenharmony_ci	LCS_DBF_TEXT_(5, trace, "Rint%s", dev_name(&cdev->dev));
13818c2ecf20Sopenharmony_ci	LCS_DBF_TEXT_(5, trace, "%4x%4x", irb->scsw.cmd.cstat,
13828c2ecf20Sopenharmony_ci		      irb->scsw.cmd.dstat);
13838c2ecf20Sopenharmony_ci	LCS_DBF_TEXT_(5, trace, "%4x%4x", irb->scsw.cmd.fctl,
13848c2ecf20Sopenharmony_ci		      irb->scsw.cmd.actl);
13858c2ecf20Sopenharmony_ci
13868c2ecf20Sopenharmony_ci	/* Check for channel and device errors presented */
13878c2ecf20Sopenharmony_ci	rc = lcs_get_problem(cdev, irb);
13888c2ecf20Sopenharmony_ci	if (rc || (dstat & DEV_STAT_UNIT_EXCEP)) {
13898c2ecf20Sopenharmony_ci		dev_warn(&cdev->dev,
13908c2ecf20Sopenharmony_ci			"The LCS device stopped because of an error,"
13918c2ecf20Sopenharmony_ci			" dstat=0x%X, cstat=0x%X \n",
13928c2ecf20Sopenharmony_ci			    dstat, cstat);
13938c2ecf20Sopenharmony_ci		if (rc) {
13948c2ecf20Sopenharmony_ci			channel->state = LCS_CH_STATE_ERROR;
13958c2ecf20Sopenharmony_ci		}
13968c2ecf20Sopenharmony_ci	}
13978c2ecf20Sopenharmony_ci	if (channel->state == LCS_CH_STATE_ERROR) {
13988c2ecf20Sopenharmony_ci		lcs_schedule_recovery(card);
13998c2ecf20Sopenharmony_ci		wake_up(&card->wait_q);
14008c2ecf20Sopenharmony_ci		return;
14018c2ecf20Sopenharmony_ci	}
14028c2ecf20Sopenharmony_ci	/* How far in the ccw chain have we processed? */
14038c2ecf20Sopenharmony_ci	if ((channel->state != LCS_CH_STATE_INIT) &&
14048c2ecf20Sopenharmony_ci	    (irb->scsw.cmd.fctl & SCSW_FCTL_START_FUNC) &&
14058c2ecf20Sopenharmony_ci	    (irb->scsw.cmd.cpa != 0)) {
14068c2ecf20Sopenharmony_ci		index = (struct ccw1 *) __va((addr_t) irb->scsw.cmd.cpa)
14078c2ecf20Sopenharmony_ci			- channel->ccws;
14088c2ecf20Sopenharmony_ci		if ((irb->scsw.cmd.actl & SCSW_ACTL_SUSPENDED) ||
14098c2ecf20Sopenharmony_ci		    (irb->scsw.cmd.cstat & SCHN_STAT_PCI))
14108c2ecf20Sopenharmony_ci			/* Bloody io subsystem tells us lies about cpa... */
14118c2ecf20Sopenharmony_ci			index = (index - 1) & (LCS_NUM_BUFFS - 1);
14128c2ecf20Sopenharmony_ci		while (channel->io_idx != index) {
14138c2ecf20Sopenharmony_ci			__lcs_processed_buffer(channel,
14148c2ecf20Sopenharmony_ci					       channel->iob + channel->io_idx);
14158c2ecf20Sopenharmony_ci			channel->io_idx =
14168c2ecf20Sopenharmony_ci				(channel->io_idx + 1) & (LCS_NUM_BUFFS - 1);
14178c2ecf20Sopenharmony_ci		}
14188c2ecf20Sopenharmony_ci	}
14198c2ecf20Sopenharmony_ci
14208c2ecf20Sopenharmony_ci	if ((irb->scsw.cmd.dstat & DEV_STAT_DEV_END) ||
14218c2ecf20Sopenharmony_ci	    (irb->scsw.cmd.dstat & DEV_STAT_CHN_END) ||
14228c2ecf20Sopenharmony_ci	    (irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK))
14238c2ecf20Sopenharmony_ci		/* Mark channel as stopped. */
14248c2ecf20Sopenharmony_ci		channel->state = LCS_CH_STATE_STOPPED;
14258c2ecf20Sopenharmony_ci	else if (irb->scsw.cmd.actl & SCSW_ACTL_SUSPENDED)
14268c2ecf20Sopenharmony_ci		/* CCW execution stopped on a suspend bit. */
14278c2ecf20Sopenharmony_ci		channel->state = LCS_CH_STATE_SUSPENDED;
14288c2ecf20Sopenharmony_ci	if (irb->scsw.cmd.fctl & SCSW_FCTL_HALT_FUNC) {
14298c2ecf20Sopenharmony_ci		if (irb->scsw.cmd.cc != 0) {
14308c2ecf20Sopenharmony_ci			ccw_device_halt(channel->ccwdev, 0);
14318c2ecf20Sopenharmony_ci			return;
14328c2ecf20Sopenharmony_ci		}
14338c2ecf20Sopenharmony_ci		/* The channel has been stopped by halt_IO. */
14348c2ecf20Sopenharmony_ci		channel->state = LCS_CH_STATE_HALTED;
14358c2ecf20Sopenharmony_ci	}
14368c2ecf20Sopenharmony_ci	if (irb->scsw.cmd.fctl & SCSW_FCTL_CLEAR_FUNC)
14378c2ecf20Sopenharmony_ci		channel->state = LCS_CH_STATE_CLEARED;
14388c2ecf20Sopenharmony_ci	/* Do the rest in the tasklet. */
14398c2ecf20Sopenharmony_ci	tasklet_schedule(&channel->irq_tasklet);
14408c2ecf20Sopenharmony_ci}
14418c2ecf20Sopenharmony_ci
14428c2ecf20Sopenharmony_ci/**
14438c2ecf20Sopenharmony_ci * Tasklet for IRQ handler
14448c2ecf20Sopenharmony_ci */
14458c2ecf20Sopenharmony_cistatic void
14468c2ecf20Sopenharmony_cilcs_tasklet(unsigned long data)
14478c2ecf20Sopenharmony_ci{
14488c2ecf20Sopenharmony_ci	unsigned long flags;
14498c2ecf20Sopenharmony_ci	struct lcs_channel *channel;
14508c2ecf20Sopenharmony_ci	struct lcs_buffer *iob;
14518c2ecf20Sopenharmony_ci	int buf_idx;
14528c2ecf20Sopenharmony_ci
14538c2ecf20Sopenharmony_ci	channel = (struct lcs_channel *) data;
14548c2ecf20Sopenharmony_ci	LCS_DBF_TEXT_(5, trace, "tlet%s", dev_name(&channel->ccwdev->dev));
14558c2ecf20Sopenharmony_ci
14568c2ecf20Sopenharmony_ci	/* Check for processed buffers. */
14578c2ecf20Sopenharmony_ci	iob = channel->iob;
14588c2ecf20Sopenharmony_ci	buf_idx = channel->buf_idx;
14598c2ecf20Sopenharmony_ci	while (iob[buf_idx].state == LCS_BUF_STATE_PROCESSED) {
14608c2ecf20Sopenharmony_ci		/* Do the callback thing. */
14618c2ecf20Sopenharmony_ci		if (iob[buf_idx].callback != NULL)
14628c2ecf20Sopenharmony_ci			iob[buf_idx].callback(channel, iob + buf_idx);
14638c2ecf20Sopenharmony_ci		buf_idx = (buf_idx + 1) & (LCS_NUM_BUFFS - 1);
14648c2ecf20Sopenharmony_ci	}
14658c2ecf20Sopenharmony_ci	channel->buf_idx = buf_idx;
14668c2ecf20Sopenharmony_ci
14678c2ecf20Sopenharmony_ci	if (channel->state == LCS_CH_STATE_STOPPED)
14688c2ecf20Sopenharmony_ci		lcs_start_channel(channel);
14698c2ecf20Sopenharmony_ci	spin_lock_irqsave(get_ccwdev_lock(channel->ccwdev), flags);
14708c2ecf20Sopenharmony_ci	if (channel->state == LCS_CH_STATE_SUSPENDED &&
14718c2ecf20Sopenharmony_ci	    channel->iob[channel->io_idx].state == LCS_BUF_STATE_READY)
14728c2ecf20Sopenharmony_ci		__lcs_resume_channel(channel);
14738c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(get_ccwdev_lock(channel->ccwdev), flags);
14748c2ecf20Sopenharmony_ci
14758c2ecf20Sopenharmony_ci	/* Something happened on the channel. Wake up waiters. */
14768c2ecf20Sopenharmony_ci	wake_up(&channel->wait_q);
14778c2ecf20Sopenharmony_ci}
14788c2ecf20Sopenharmony_ci
14798c2ecf20Sopenharmony_ci/**
14808c2ecf20Sopenharmony_ci * Finish current tx buffer and make it ready for transmit.
14818c2ecf20Sopenharmony_ci */
14828c2ecf20Sopenharmony_cistatic void
14838c2ecf20Sopenharmony_ci__lcs_emit_txbuffer(struct lcs_card *card)
14848c2ecf20Sopenharmony_ci{
14858c2ecf20Sopenharmony_ci	LCS_DBF_TEXT(5, trace, "emittx");
14868c2ecf20Sopenharmony_ci	*(__u16 *)(card->tx_buffer->data + card->tx_buffer->count) = 0;
14878c2ecf20Sopenharmony_ci	card->tx_buffer->count += 2;
14888c2ecf20Sopenharmony_ci	lcs_ready_buffer(&card->write, card->tx_buffer);
14898c2ecf20Sopenharmony_ci	card->tx_buffer = NULL;
14908c2ecf20Sopenharmony_ci	card->tx_emitted++;
14918c2ecf20Sopenharmony_ci}
14928c2ecf20Sopenharmony_ci
14938c2ecf20Sopenharmony_ci/**
14948c2ecf20Sopenharmony_ci * Callback for finished tx buffers.
14958c2ecf20Sopenharmony_ci */
14968c2ecf20Sopenharmony_cistatic void
14978c2ecf20Sopenharmony_cilcs_txbuffer_cb(struct lcs_channel *channel, struct lcs_buffer *buffer)
14988c2ecf20Sopenharmony_ci{
14998c2ecf20Sopenharmony_ci	struct lcs_card *card;
15008c2ecf20Sopenharmony_ci
15018c2ecf20Sopenharmony_ci	LCS_DBF_TEXT(5, trace, "txbuffcb");
15028c2ecf20Sopenharmony_ci	/* Put buffer back to pool. */
15038c2ecf20Sopenharmony_ci	lcs_release_buffer(channel, buffer);
15048c2ecf20Sopenharmony_ci	card = container_of(channel, struct lcs_card, write);
15058c2ecf20Sopenharmony_ci	if (netif_queue_stopped(card->dev) && netif_carrier_ok(card->dev))
15068c2ecf20Sopenharmony_ci		netif_wake_queue(card->dev);
15078c2ecf20Sopenharmony_ci	spin_lock(&card->lock);
15088c2ecf20Sopenharmony_ci	card->tx_emitted--;
15098c2ecf20Sopenharmony_ci	if (card->tx_emitted <= 0 && card->tx_buffer != NULL)
15108c2ecf20Sopenharmony_ci		/*
15118c2ecf20Sopenharmony_ci		 * Last running tx buffer has finished. Submit partially
15128c2ecf20Sopenharmony_ci		 * filled current buffer.
15138c2ecf20Sopenharmony_ci		 */
15148c2ecf20Sopenharmony_ci		__lcs_emit_txbuffer(card);
15158c2ecf20Sopenharmony_ci	spin_unlock(&card->lock);
15168c2ecf20Sopenharmony_ci}
15178c2ecf20Sopenharmony_ci
15188c2ecf20Sopenharmony_ci/**
15198c2ecf20Sopenharmony_ci * Packet transmit function called by network stack
15208c2ecf20Sopenharmony_ci */
15218c2ecf20Sopenharmony_cistatic netdev_tx_t __lcs_start_xmit(struct lcs_card *card, struct sk_buff *skb,
15228c2ecf20Sopenharmony_ci				    struct net_device *dev)
15238c2ecf20Sopenharmony_ci{
15248c2ecf20Sopenharmony_ci	struct lcs_header *header;
15258c2ecf20Sopenharmony_ci	int rc = NETDEV_TX_OK;
15268c2ecf20Sopenharmony_ci
15278c2ecf20Sopenharmony_ci	LCS_DBF_TEXT(5, trace, "hardxmit");
15288c2ecf20Sopenharmony_ci	if (skb == NULL) {
15298c2ecf20Sopenharmony_ci		card->stats.tx_dropped++;
15308c2ecf20Sopenharmony_ci		card->stats.tx_errors++;
15318c2ecf20Sopenharmony_ci		return NETDEV_TX_OK;
15328c2ecf20Sopenharmony_ci	}
15338c2ecf20Sopenharmony_ci	if (card->state != DEV_STATE_UP) {
15348c2ecf20Sopenharmony_ci		dev_kfree_skb(skb);
15358c2ecf20Sopenharmony_ci		card->stats.tx_dropped++;
15368c2ecf20Sopenharmony_ci		card->stats.tx_errors++;
15378c2ecf20Sopenharmony_ci		card->stats.tx_carrier_errors++;
15388c2ecf20Sopenharmony_ci		return NETDEV_TX_OK;
15398c2ecf20Sopenharmony_ci	}
15408c2ecf20Sopenharmony_ci	if (skb->protocol == htons(ETH_P_IPV6)) {
15418c2ecf20Sopenharmony_ci		dev_kfree_skb(skb);
15428c2ecf20Sopenharmony_ci		return NETDEV_TX_OK;
15438c2ecf20Sopenharmony_ci	}
15448c2ecf20Sopenharmony_ci	netif_stop_queue(card->dev);
15458c2ecf20Sopenharmony_ci	spin_lock(&card->lock);
15468c2ecf20Sopenharmony_ci	if (card->tx_buffer != NULL &&
15478c2ecf20Sopenharmony_ci	    card->tx_buffer->count + sizeof(struct lcs_header) +
15488c2ecf20Sopenharmony_ci	    skb->len + sizeof(u16) > LCS_IOBUFFERSIZE)
15498c2ecf20Sopenharmony_ci		/* skb too big for current tx buffer. */
15508c2ecf20Sopenharmony_ci		__lcs_emit_txbuffer(card);
15518c2ecf20Sopenharmony_ci	if (card->tx_buffer == NULL) {
15528c2ecf20Sopenharmony_ci		/* Get new tx buffer */
15538c2ecf20Sopenharmony_ci		card->tx_buffer = lcs_get_buffer(&card->write);
15548c2ecf20Sopenharmony_ci		if (card->tx_buffer == NULL) {
15558c2ecf20Sopenharmony_ci			card->stats.tx_dropped++;
15568c2ecf20Sopenharmony_ci			rc = NETDEV_TX_BUSY;
15578c2ecf20Sopenharmony_ci			goto out;
15588c2ecf20Sopenharmony_ci		}
15598c2ecf20Sopenharmony_ci		card->tx_buffer->callback = lcs_txbuffer_cb;
15608c2ecf20Sopenharmony_ci		card->tx_buffer->count = 0;
15618c2ecf20Sopenharmony_ci	}
15628c2ecf20Sopenharmony_ci	header = (struct lcs_header *)
15638c2ecf20Sopenharmony_ci		(card->tx_buffer->data + card->tx_buffer->count);
15648c2ecf20Sopenharmony_ci	card->tx_buffer->count += skb->len + sizeof(struct lcs_header);
15658c2ecf20Sopenharmony_ci	header->offset = card->tx_buffer->count;
15668c2ecf20Sopenharmony_ci	header->type = card->lan_type;
15678c2ecf20Sopenharmony_ci	header->slot = card->portno;
15688c2ecf20Sopenharmony_ci	skb_copy_from_linear_data(skb, header + 1, skb->len);
15698c2ecf20Sopenharmony_ci	spin_unlock(&card->lock);
15708c2ecf20Sopenharmony_ci	card->stats.tx_bytes += skb->len;
15718c2ecf20Sopenharmony_ci	card->stats.tx_packets++;
15728c2ecf20Sopenharmony_ci	dev_kfree_skb(skb);
15738c2ecf20Sopenharmony_ci	netif_wake_queue(card->dev);
15748c2ecf20Sopenharmony_ci	spin_lock(&card->lock);
15758c2ecf20Sopenharmony_ci	if (card->tx_emitted <= 0 && card->tx_buffer != NULL)
15768c2ecf20Sopenharmony_ci		/* If this is the first tx buffer emit it immediately. */
15778c2ecf20Sopenharmony_ci		__lcs_emit_txbuffer(card);
15788c2ecf20Sopenharmony_ciout:
15798c2ecf20Sopenharmony_ci	spin_unlock(&card->lock);
15808c2ecf20Sopenharmony_ci	return rc;
15818c2ecf20Sopenharmony_ci}
15828c2ecf20Sopenharmony_ci
15838c2ecf20Sopenharmony_cistatic netdev_tx_t lcs_start_xmit(struct sk_buff *skb, struct net_device *dev)
15848c2ecf20Sopenharmony_ci{
15858c2ecf20Sopenharmony_ci	struct lcs_card *card;
15868c2ecf20Sopenharmony_ci	int rc;
15878c2ecf20Sopenharmony_ci
15888c2ecf20Sopenharmony_ci	LCS_DBF_TEXT(5, trace, "pktxmit");
15898c2ecf20Sopenharmony_ci	card = (struct lcs_card *) dev->ml_priv;
15908c2ecf20Sopenharmony_ci	rc = __lcs_start_xmit(card, skb, dev);
15918c2ecf20Sopenharmony_ci	return rc;
15928c2ecf20Sopenharmony_ci}
15938c2ecf20Sopenharmony_ci
15948c2ecf20Sopenharmony_ci/**
15958c2ecf20Sopenharmony_ci * send startlan and lanstat command to make LCS device ready
15968c2ecf20Sopenharmony_ci */
15978c2ecf20Sopenharmony_cistatic int
15988c2ecf20Sopenharmony_cilcs_startlan_auto(struct lcs_card *card)
15998c2ecf20Sopenharmony_ci{
16008c2ecf20Sopenharmony_ci	int rc;
16018c2ecf20Sopenharmony_ci
16028c2ecf20Sopenharmony_ci	LCS_DBF_TEXT(2, trace, "strtauto");
16038c2ecf20Sopenharmony_ci#ifdef CONFIG_ETHERNET
16048c2ecf20Sopenharmony_ci	card->lan_type = LCS_FRAME_TYPE_ENET;
16058c2ecf20Sopenharmony_ci	rc = lcs_send_startlan(card, LCS_INITIATOR_TCPIP);
16068c2ecf20Sopenharmony_ci	if (rc == 0)
16078c2ecf20Sopenharmony_ci		return 0;
16088c2ecf20Sopenharmony_ci
16098c2ecf20Sopenharmony_ci#endif
16108c2ecf20Sopenharmony_ci#ifdef CONFIG_FDDI
16118c2ecf20Sopenharmony_ci	card->lan_type = LCS_FRAME_TYPE_FDDI;
16128c2ecf20Sopenharmony_ci	rc = lcs_send_startlan(card, LCS_INITIATOR_TCPIP);
16138c2ecf20Sopenharmony_ci	if (rc == 0)
16148c2ecf20Sopenharmony_ci		return 0;
16158c2ecf20Sopenharmony_ci#endif
16168c2ecf20Sopenharmony_ci	return -EIO;
16178c2ecf20Sopenharmony_ci}
16188c2ecf20Sopenharmony_ci
16198c2ecf20Sopenharmony_cistatic int
16208c2ecf20Sopenharmony_cilcs_startlan(struct lcs_card *card)
16218c2ecf20Sopenharmony_ci{
16228c2ecf20Sopenharmony_ci	int rc, i;
16238c2ecf20Sopenharmony_ci
16248c2ecf20Sopenharmony_ci	LCS_DBF_TEXT(2, trace, "startlan");
16258c2ecf20Sopenharmony_ci	rc = 0;
16268c2ecf20Sopenharmony_ci	if (card->portno != LCS_INVALID_PORT_NO) {
16278c2ecf20Sopenharmony_ci		if (card->lan_type == LCS_FRAME_TYPE_AUTO)
16288c2ecf20Sopenharmony_ci			rc = lcs_startlan_auto(card);
16298c2ecf20Sopenharmony_ci		else
16308c2ecf20Sopenharmony_ci			rc = lcs_send_startlan(card, LCS_INITIATOR_TCPIP);
16318c2ecf20Sopenharmony_ci	} else {
16328c2ecf20Sopenharmony_ci                for (i = 0; i <= 16; i++) {
16338c2ecf20Sopenharmony_ci                        card->portno = i;
16348c2ecf20Sopenharmony_ci                        if (card->lan_type != LCS_FRAME_TYPE_AUTO)
16358c2ecf20Sopenharmony_ci                                rc = lcs_send_startlan(card,
16368c2ecf20Sopenharmony_ci                                                       LCS_INITIATOR_TCPIP);
16378c2ecf20Sopenharmony_ci                        else
16388c2ecf20Sopenharmony_ci                                /* autodetecting lan type */
16398c2ecf20Sopenharmony_ci                                rc = lcs_startlan_auto(card);
16408c2ecf20Sopenharmony_ci                        if (rc == 0)
16418c2ecf20Sopenharmony_ci                                break;
16428c2ecf20Sopenharmony_ci                }
16438c2ecf20Sopenharmony_ci        }
16448c2ecf20Sopenharmony_ci	if (rc == 0)
16458c2ecf20Sopenharmony_ci		return lcs_send_lanstat(card);
16468c2ecf20Sopenharmony_ci	return rc;
16478c2ecf20Sopenharmony_ci}
16488c2ecf20Sopenharmony_ci
16498c2ecf20Sopenharmony_ci/**
16508c2ecf20Sopenharmony_ci * LCS detect function
16518c2ecf20Sopenharmony_ci * setup channels and make them I/O ready
16528c2ecf20Sopenharmony_ci */
16538c2ecf20Sopenharmony_cistatic int
16548c2ecf20Sopenharmony_cilcs_detect(struct lcs_card *card)
16558c2ecf20Sopenharmony_ci{
16568c2ecf20Sopenharmony_ci	int rc = 0;
16578c2ecf20Sopenharmony_ci
16588c2ecf20Sopenharmony_ci	LCS_DBF_TEXT(2, setup, "lcsdetct");
16598c2ecf20Sopenharmony_ci	/* start/reset card */
16608c2ecf20Sopenharmony_ci	if (card->dev)
16618c2ecf20Sopenharmony_ci		netif_stop_queue(card->dev);
16628c2ecf20Sopenharmony_ci	rc = lcs_stop_channels(card);
16638c2ecf20Sopenharmony_ci	if (rc == 0) {
16648c2ecf20Sopenharmony_ci		rc = lcs_start_channels(card);
16658c2ecf20Sopenharmony_ci		if (rc == 0) {
16668c2ecf20Sopenharmony_ci			rc = lcs_send_startup(card, LCS_INITIATOR_TCPIP);
16678c2ecf20Sopenharmony_ci			if (rc == 0)
16688c2ecf20Sopenharmony_ci				rc = lcs_startlan(card);
16698c2ecf20Sopenharmony_ci		}
16708c2ecf20Sopenharmony_ci	}
16718c2ecf20Sopenharmony_ci	if (rc == 0) {
16728c2ecf20Sopenharmony_ci		card->state = DEV_STATE_UP;
16738c2ecf20Sopenharmony_ci	} else {
16748c2ecf20Sopenharmony_ci		card->state = DEV_STATE_DOWN;
16758c2ecf20Sopenharmony_ci		card->write.state = LCS_CH_STATE_INIT;
16768c2ecf20Sopenharmony_ci		card->read.state =  LCS_CH_STATE_INIT;
16778c2ecf20Sopenharmony_ci	}
16788c2ecf20Sopenharmony_ci	return rc;
16798c2ecf20Sopenharmony_ci}
16808c2ecf20Sopenharmony_ci
16818c2ecf20Sopenharmony_ci/**
16828c2ecf20Sopenharmony_ci * LCS Stop card
16838c2ecf20Sopenharmony_ci */
16848c2ecf20Sopenharmony_cistatic int
16858c2ecf20Sopenharmony_cilcs_stopcard(struct lcs_card *card)
16868c2ecf20Sopenharmony_ci{
16878c2ecf20Sopenharmony_ci	int rc;
16888c2ecf20Sopenharmony_ci
16898c2ecf20Sopenharmony_ci	LCS_DBF_TEXT(3, setup, "stopcard");
16908c2ecf20Sopenharmony_ci
16918c2ecf20Sopenharmony_ci	if (card->read.state != LCS_CH_STATE_STOPPED &&
16928c2ecf20Sopenharmony_ci	    card->write.state != LCS_CH_STATE_STOPPED &&
16938c2ecf20Sopenharmony_ci	    card->read.state != LCS_CH_STATE_ERROR &&
16948c2ecf20Sopenharmony_ci	    card->write.state != LCS_CH_STATE_ERROR &&
16958c2ecf20Sopenharmony_ci	    card->state == DEV_STATE_UP) {
16968c2ecf20Sopenharmony_ci		lcs_clear_multicast_list(card);
16978c2ecf20Sopenharmony_ci		rc = lcs_send_stoplan(card,LCS_INITIATOR_TCPIP);
16988c2ecf20Sopenharmony_ci		rc = lcs_send_shutdown(card);
16998c2ecf20Sopenharmony_ci	}
17008c2ecf20Sopenharmony_ci	rc = lcs_stop_channels(card);
17018c2ecf20Sopenharmony_ci	card->state = DEV_STATE_DOWN;
17028c2ecf20Sopenharmony_ci
17038c2ecf20Sopenharmony_ci	return rc;
17048c2ecf20Sopenharmony_ci}
17058c2ecf20Sopenharmony_ci
17068c2ecf20Sopenharmony_ci/**
17078c2ecf20Sopenharmony_ci * Kernel Thread helper functions for LGW initiated commands
17088c2ecf20Sopenharmony_ci */
17098c2ecf20Sopenharmony_cistatic void
17108c2ecf20Sopenharmony_cilcs_start_kernel_thread(struct work_struct *work)
17118c2ecf20Sopenharmony_ci{
17128c2ecf20Sopenharmony_ci	struct lcs_card *card = container_of(work, struct lcs_card, kernel_thread_starter);
17138c2ecf20Sopenharmony_ci	LCS_DBF_TEXT(5, trace, "krnthrd");
17148c2ecf20Sopenharmony_ci	if (lcs_do_start_thread(card, LCS_RECOVERY_THREAD))
17158c2ecf20Sopenharmony_ci		kthread_run(lcs_recovery, card, "lcs_recover");
17168c2ecf20Sopenharmony_ci#ifdef CONFIG_IP_MULTICAST
17178c2ecf20Sopenharmony_ci	if (lcs_do_start_thread(card, LCS_SET_MC_THREAD))
17188c2ecf20Sopenharmony_ci		kthread_run(lcs_register_mc_addresses, card, "regipm");
17198c2ecf20Sopenharmony_ci#endif
17208c2ecf20Sopenharmony_ci}
17218c2ecf20Sopenharmony_ci
17228c2ecf20Sopenharmony_ci/**
17238c2ecf20Sopenharmony_ci * Process control frames.
17248c2ecf20Sopenharmony_ci */
17258c2ecf20Sopenharmony_cistatic void
17268c2ecf20Sopenharmony_cilcs_get_control(struct lcs_card *card, struct lcs_cmd *cmd)
17278c2ecf20Sopenharmony_ci{
17288c2ecf20Sopenharmony_ci	LCS_DBF_TEXT(5, trace, "getctrl");
17298c2ecf20Sopenharmony_ci	if (cmd->initiator == LCS_INITIATOR_LGW) {
17308c2ecf20Sopenharmony_ci		switch(cmd->cmd_code) {
17318c2ecf20Sopenharmony_ci		case LCS_CMD_STARTUP:
17328c2ecf20Sopenharmony_ci		case LCS_CMD_STARTLAN:
17338c2ecf20Sopenharmony_ci			lcs_schedule_recovery(card);
17348c2ecf20Sopenharmony_ci			break;
17358c2ecf20Sopenharmony_ci		case LCS_CMD_STOPLAN:
17368c2ecf20Sopenharmony_ci			if (card->dev) {
17378c2ecf20Sopenharmony_ci				pr_warn("Stoplan for %s initiated by LGW\n",
17388c2ecf20Sopenharmony_ci					card->dev->name);
17398c2ecf20Sopenharmony_ci				netif_carrier_off(card->dev);
17408c2ecf20Sopenharmony_ci			}
17418c2ecf20Sopenharmony_ci			break;
17428c2ecf20Sopenharmony_ci		default:
17438c2ecf20Sopenharmony_ci			LCS_DBF_TEXT(5, trace, "noLGWcmd");
17448c2ecf20Sopenharmony_ci			break;
17458c2ecf20Sopenharmony_ci		}
17468c2ecf20Sopenharmony_ci	} else
17478c2ecf20Sopenharmony_ci		lcs_notify_lancmd_waiters(card, cmd);
17488c2ecf20Sopenharmony_ci}
17498c2ecf20Sopenharmony_ci
17508c2ecf20Sopenharmony_ci/**
17518c2ecf20Sopenharmony_ci * Unpack network packet.
17528c2ecf20Sopenharmony_ci */
17538c2ecf20Sopenharmony_cistatic void
17548c2ecf20Sopenharmony_cilcs_get_skb(struct lcs_card *card, char *skb_data, unsigned int skb_len)
17558c2ecf20Sopenharmony_ci{
17568c2ecf20Sopenharmony_ci	struct sk_buff *skb;
17578c2ecf20Sopenharmony_ci
17588c2ecf20Sopenharmony_ci	LCS_DBF_TEXT(5, trace, "getskb");
17598c2ecf20Sopenharmony_ci	if (card->dev == NULL ||
17608c2ecf20Sopenharmony_ci	    card->state != DEV_STATE_UP)
17618c2ecf20Sopenharmony_ci		/* The card isn't up. Ignore the packet. */
17628c2ecf20Sopenharmony_ci		return;
17638c2ecf20Sopenharmony_ci
17648c2ecf20Sopenharmony_ci	skb = dev_alloc_skb(skb_len);
17658c2ecf20Sopenharmony_ci	if (skb == NULL) {
17668c2ecf20Sopenharmony_ci		dev_err(&card->dev->dev,
17678c2ecf20Sopenharmony_ci			" Allocating a socket buffer to interface %s failed\n",
17688c2ecf20Sopenharmony_ci			  card->dev->name);
17698c2ecf20Sopenharmony_ci		card->stats.rx_dropped++;
17708c2ecf20Sopenharmony_ci		return;
17718c2ecf20Sopenharmony_ci	}
17728c2ecf20Sopenharmony_ci	skb_put_data(skb, skb_data, skb_len);
17738c2ecf20Sopenharmony_ci	skb->protocol =	card->lan_type_trans(skb, card->dev);
17748c2ecf20Sopenharmony_ci	card->stats.rx_bytes += skb_len;
17758c2ecf20Sopenharmony_ci	card->stats.rx_packets++;
17768c2ecf20Sopenharmony_ci	if (skb->protocol == htons(ETH_P_802_2))
17778c2ecf20Sopenharmony_ci		*((__u32 *)skb->cb) = ++card->pkt_seq;
17788c2ecf20Sopenharmony_ci	netif_rx(skb);
17798c2ecf20Sopenharmony_ci}
17808c2ecf20Sopenharmony_ci
17818c2ecf20Sopenharmony_ci/**
17828c2ecf20Sopenharmony_ci * LCS main routine to get packets and lancmd replies from the buffers
17838c2ecf20Sopenharmony_ci */
17848c2ecf20Sopenharmony_cistatic void
17858c2ecf20Sopenharmony_cilcs_get_frames_cb(struct lcs_channel *channel, struct lcs_buffer *buffer)
17868c2ecf20Sopenharmony_ci{
17878c2ecf20Sopenharmony_ci	struct lcs_card *card;
17888c2ecf20Sopenharmony_ci	struct lcs_header *lcs_hdr;
17898c2ecf20Sopenharmony_ci	__u16 offset;
17908c2ecf20Sopenharmony_ci
17918c2ecf20Sopenharmony_ci	LCS_DBF_TEXT(5, trace, "lcsgtpkt");
17928c2ecf20Sopenharmony_ci	lcs_hdr = (struct lcs_header *) buffer->data;
17938c2ecf20Sopenharmony_ci	if (lcs_hdr->offset == LCS_ILLEGAL_OFFSET) {
17948c2ecf20Sopenharmony_ci		LCS_DBF_TEXT(4, trace, "-eiogpkt");
17958c2ecf20Sopenharmony_ci		return;
17968c2ecf20Sopenharmony_ci	}
17978c2ecf20Sopenharmony_ci	card = container_of(channel, struct lcs_card, read);
17988c2ecf20Sopenharmony_ci	offset = 0;
17998c2ecf20Sopenharmony_ci	while (lcs_hdr->offset != 0) {
18008c2ecf20Sopenharmony_ci		if (lcs_hdr->offset <= 0 ||
18018c2ecf20Sopenharmony_ci		    lcs_hdr->offset > LCS_IOBUFFERSIZE ||
18028c2ecf20Sopenharmony_ci		    lcs_hdr->offset < offset) {
18038c2ecf20Sopenharmony_ci			/* Offset invalid. */
18048c2ecf20Sopenharmony_ci			card->stats.rx_length_errors++;
18058c2ecf20Sopenharmony_ci			card->stats.rx_errors++;
18068c2ecf20Sopenharmony_ci			return;
18078c2ecf20Sopenharmony_ci		}
18088c2ecf20Sopenharmony_ci		/* What kind of frame is it? */
18098c2ecf20Sopenharmony_ci		if (lcs_hdr->type == LCS_FRAME_TYPE_CONTROL)
18108c2ecf20Sopenharmony_ci			/* Control frame. */
18118c2ecf20Sopenharmony_ci			lcs_get_control(card, (struct lcs_cmd *) lcs_hdr);
18128c2ecf20Sopenharmony_ci		else if (lcs_hdr->type == LCS_FRAME_TYPE_ENET ||
18138c2ecf20Sopenharmony_ci			 lcs_hdr->type == LCS_FRAME_TYPE_TR ||
18148c2ecf20Sopenharmony_ci			 lcs_hdr->type == LCS_FRAME_TYPE_FDDI)
18158c2ecf20Sopenharmony_ci			/* Normal network packet. */
18168c2ecf20Sopenharmony_ci			lcs_get_skb(card, (char *)(lcs_hdr + 1),
18178c2ecf20Sopenharmony_ci				    lcs_hdr->offset - offset -
18188c2ecf20Sopenharmony_ci				    sizeof(struct lcs_header));
18198c2ecf20Sopenharmony_ci		else
18208c2ecf20Sopenharmony_ci			/* Unknown frame type. */
18218c2ecf20Sopenharmony_ci			; // FIXME: error message ?
18228c2ecf20Sopenharmony_ci		/* Proceed to next frame. */
18238c2ecf20Sopenharmony_ci		offset = lcs_hdr->offset;
18248c2ecf20Sopenharmony_ci		lcs_hdr->offset = LCS_ILLEGAL_OFFSET;
18258c2ecf20Sopenharmony_ci		lcs_hdr = (struct lcs_header *) (buffer->data + offset);
18268c2ecf20Sopenharmony_ci	}
18278c2ecf20Sopenharmony_ci	/* The buffer is now empty. Make it ready again. */
18288c2ecf20Sopenharmony_ci	lcs_ready_buffer(&card->read, buffer);
18298c2ecf20Sopenharmony_ci}
18308c2ecf20Sopenharmony_ci
18318c2ecf20Sopenharmony_ci/**
18328c2ecf20Sopenharmony_ci * get network statistics for ifconfig and other user programs
18338c2ecf20Sopenharmony_ci */
18348c2ecf20Sopenharmony_cistatic struct net_device_stats *
18358c2ecf20Sopenharmony_cilcs_getstats(struct net_device *dev)
18368c2ecf20Sopenharmony_ci{
18378c2ecf20Sopenharmony_ci	struct lcs_card *card;
18388c2ecf20Sopenharmony_ci
18398c2ecf20Sopenharmony_ci	LCS_DBF_TEXT(4, trace, "netstats");
18408c2ecf20Sopenharmony_ci	card = (struct lcs_card *) dev->ml_priv;
18418c2ecf20Sopenharmony_ci	return &card->stats;
18428c2ecf20Sopenharmony_ci}
18438c2ecf20Sopenharmony_ci
18448c2ecf20Sopenharmony_ci/**
18458c2ecf20Sopenharmony_ci * stop lcs device
18468c2ecf20Sopenharmony_ci * This function will be called by user doing ifconfig xxx down
18478c2ecf20Sopenharmony_ci */
18488c2ecf20Sopenharmony_cistatic int
18498c2ecf20Sopenharmony_cilcs_stop_device(struct net_device *dev)
18508c2ecf20Sopenharmony_ci{
18518c2ecf20Sopenharmony_ci	struct lcs_card *card;
18528c2ecf20Sopenharmony_ci	int rc;
18538c2ecf20Sopenharmony_ci
18548c2ecf20Sopenharmony_ci	LCS_DBF_TEXT(2, trace, "stopdev");
18558c2ecf20Sopenharmony_ci	card   = (struct lcs_card *) dev->ml_priv;
18568c2ecf20Sopenharmony_ci	netif_carrier_off(dev);
18578c2ecf20Sopenharmony_ci	netif_tx_disable(dev);
18588c2ecf20Sopenharmony_ci	dev->flags &= ~IFF_UP;
18598c2ecf20Sopenharmony_ci	wait_event(card->write.wait_q,
18608c2ecf20Sopenharmony_ci		(card->write.state != LCS_CH_STATE_RUNNING));
18618c2ecf20Sopenharmony_ci	rc = lcs_stopcard(card);
18628c2ecf20Sopenharmony_ci	if (rc)
18638c2ecf20Sopenharmony_ci		dev_err(&card->dev->dev,
18648c2ecf20Sopenharmony_ci			" Shutting down the LCS device failed\n");
18658c2ecf20Sopenharmony_ci	return rc;
18668c2ecf20Sopenharmony_ci}
18678c2ecf20Sopenharmony_ci
18688c2ecf20Sopenharmony_ci/**
18698c2ecf20Sopenharmony_ci * start lcs device and make it runnable
18708c2ecf20Sopenharmony_ci * This function will be called by user doing ifconfig xxx up
18718c2ecf20Sopenharmony_ci */
18728c2ecf20Sopenharmony_cistatic int
18738c2ecf20Sopenharmony_cilcs_open_device(struct net_device *dev)
18748c2ecf20Sopenharmony_ci{
18758c2ecf20Sopenharmony_ci	struct lcs_card *card;
18768c2ecf20Sopenharmony_ci	int rc;
18778c2ecf20Sopenharmony_ci
18788c2ecf20Sopenharmony_ci	LCS_DBF_TEXT(2, trace, "opendev");
18798c2ecf20Sopenharmony_ci	card = (struct lcs_card *) dev->ml_priv;
18808c2ecf20Sopenharmony_ci	/* initialize statistics */
18818c2ecf20Sopenharmony_ci	rc = lcs_detect(card);
18828c2ecf20Sopenharmony_ci	if (rc) {
18838c2ecf20Sopenharmony_ci		pr_err("Error in opening device!\n");
18848c2ecf20Sopenharmony_ci
18858c2ecf20Sopenharmony_ci	} else {
18868c2ecf20Sopenharmony_ci		dev->flags |= IFF_UP;
18878c2ecf20Sopenharmony_ci		netif_carrier_on(dev);
18888c2ecf20Sopenharmony_ci		netif_wake_queue(dev);
18898c2ecf20Sopenharmony_ci		card->state = DEV_STATE_UP;
18908c2ecf20Sopenharmony_ci	}
18918c2ecf20Sopenharmony_ci	return rc;
18928c2ecf20Sopenharmony_ci}
18938c2ecf20Sopenharmony_ci
18948c2ecf20Sopenharmony_ci/**
18958c2ecf20Sopenharmony_ci * show function for portno called by cat or similar things
18968c2ecf20Sopenharmony_ci */
18978c2ecf20Sopenharmony_cistatic ssize_t
18988c2ecf20Sopenharmony_cilcs_portno_show (struct device *dev, struct device_attribute *attr, char *buf)
18998c2ecf20Sopenharmony_ci{
19008c2ecf20Sopenharmony_ci        struct lcs_card *card;
19018c2ecf20Sopenharmony_ci
19028c2ecf20Sopenharmony_ci	card = dev_get_drvdata(dev);
19038c2ecf20Sopenharmony_ci
19048c2ecf20Sopenharmony_ci        if (!card)
19058c2ecf20Sopenharmony_ci                return 0;
19068c2ecf20Sopenharmony_ci
19078c2ecf20Sopenharmony_ci        return sprintf(buf, "%d\n", card->portno);
19088c2ecf20Sopenharmony_ci}
19098c2ecf20Sopenharmony_ci
19108c2ecf20Sopenharmony_ci/**
19118c2ecf20Sopenharmony_ci * store the value which is piped to file portno
19128c2ecf20Sopenharmony_ci */
19138c2ecf20Sopenharmony_cistatic ssize_t
19148c2ecf20Sopenharmony_cilcs_portno_store (struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
19158c2ecf20Sopenharmony_ci{
19168c2ecf20Sopenharmony_ci        struct lcs_card *card;
19178c2ecf20Sopenharmony_ci	int rc;
19188c2ecf20Sopenharmony_ci	s16 value;
19198c2ecf20Sopenharmony_ci
19208c2ecf20Sopenharmony_ci	card = dev_get_drvdata(dev);
19218c2ecf20Sopenharmony_ci
19228c2ecf20Sopenharmony_ci        if (!card)
19238c2ecf20Sopenharmony_ci                return 0;
19248c2ecf20Sopenharmony_ci
19258c2ecf20Sopenharmony_ci	rc = kstrtos16(buf, 0, &value);
19268c2ecf20Sopenharmony_ci	if (rc)
19278c2ecf20Sopenharmony_ci		return -EINVAL;
19288c2ecf20Sopenharmony_ci        /* TODO: sanity checks */
19298c2ecf20Sopenharmony_ci        card->portno = value;
19308c2ecf20Sopenharmony_ci	if (card->dev)
19318c2ecf20Sopenharmony_ci		card->dev->dev_port = card->portno;
19328c2ecf20Sopenharmony_ci
19338c2ecf20Sopenharmony_ci        return count;
19348c2ecf20Sopenharmony_ci
19358c2ecf20Sopenharmony_ci}
19368c2ecf20Sopenharmony_ci
19378c2ecf20Sopenharmony_cistatic DEVICE_ATTR(portno, 0644, lcs_portno_show, lcs_portno_store);
19388c2ecf20Sopenharmony_ci
19398c2ecf20Sopenharmony_cistatic const char *lcs_type[] = {
19408c2ecf20Sopenharmony_ci	"not a channel",
19418c2ecf20Sopenharmony_ci	"2216 parallel",
19428c2ecf20Sopenharmony_ci	"2216 channel",
19438c2ecf20Sopenharmony_ci	"OSA LCS card",
19448c2ecf20Sopenharmony_ci	"unknown channel type",
19458c2ecf20Sopenharmony_ci	"unsupported channel type",
19468c2ecf20Sopenharmony_ci};
19478c2ecf20Sopenharmony_ci
19488c2ecf20Sopenharmony_cistatic ssize_t
19498c2ecf20Sopenharmony_cilcs_type_show(struct device *dev, struct device_attribute *attr, char *buf)
19508c2ecf20Sopenharmony_ci{
19518c2ecf20Sopenharmony_ci	struct ccwgroup_device *cgdev;
19528c2ecf20Sopenharmony_ci
19538c2ecf20Sopenharmony_ci	cgdev = to_ccwgroupdev(dev);
19548c2ecf20Sopenharmony_ci	if (!cgdev)
19558c2ecf20Sopenharmony_ci		return -ENODEV;
19568c2ecf20Sopenharmony_ci
19578c2ecf20Sopenharmony_ci	return sprintf(buf, "%s\n", lcs_type[cgdev->cdev[0]->id.driver_info]);
19588c2ecf20Sopenharmony_ci}
19598c2ecf20Sopenharmony_ci
19608c2ecf20Sopenharmony_cistatic DEVICE_ATTR(type, 0444, lcs_type_show, NULL);
19618c2ecf20Sopenharmony_ci
19628c2ecf20Sopenharmony_cistatic ssize_t
19638c2ecf20Sopenharmony_cilcs_timeout_show(struct device *dev, struct device_attribute *attr, char *buf)
19648c2ecf20Sopenharmony_ci{
19658c2ecf20Sopenharmony_ci	struct lcs_card *card;
19668c2ecf20Sopenharmony_ci
19678c2ecf20Sopenharmony_ci	card = dev_get_drvdata(dev);
19688c2ecf20Sopenharmony_ci
19698c2ecf20Sopenharmony_ci	return card ? sprintf(buf, "%u\n", card->lancmd_timeout) : 0;
19708c2ecf20Sopenharmony_ci}
19718c2ecf20Sopenharmony_ci
19728c2ecf20Sopenharmony_cistatic ssize_t
19738c2ecf20Sopenharmony_cilcs_timeout_store (struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
19748c2ecf20Sopenharmony_ci{
19758c2ecf20Sopenharmony_ci        struct lcs_card *card;
19768c2ecf20Sopenharmony_ci	unsigned int value;
19778c2ecf20Sopenharmony_ci	int rc;
19788c2ecf20Sopenharmony_ci
19798c2ecf20Sopenharmony_ci	card = dev_get_drvdata(dev);
19808c2ecf20Sopenharmony_ci
19818c2ecf20Sopenharmony_ci        if (!card)
19828c2ecf20Sopenharmony_ci                return 0;
19838c2ecf20Sopenharmony_ci
19848c2ecf20Sopenharmony_ci	rc = kstrtouint(buf, 0, &value);
19858c2ecf20Sopenharmony_ci	if (rc)
19868c2ecf20Sopenharmony_ci		return -EINVAL;
19878c2ecf20Sopenharmony_ci        /* TODO: sanity checks */
19888c2ecf20Sopenharmony_ci        card->lancmd_timeout = value;
19898c2ecf20Sopenharmony_ci
19908c2ecf20Sopenharmony_ci        return count;
19918c2ecf20Sopenharmony_ci
19928c2ecf20Sopenharmony_ci}
19938c2ecf20Sopenharmony_ci
19948c2ecf20Sopenharmony_cistatic DEVICE_ATTR(lancmd_timeout, 0644, lcs_timeout_show, lcs_timeout_store);
19958c2ecf20Sopenharmony_ci
19968c2ecf20Sopenharmony_cistatic ssize_t
19978c2ecf20Sopenharmony_cilcs_dev_recover_store(struct device *dev, struct device_attribute *attr,
19988c2ecf20Sopenharmony_ci		      const char *buf, size_t count)
19998c2ecf20Sopenharmony_ci{
20008c2ecf20Sopenharmony_ci	struct lcs_card *card = dev_get_drvdata(dev);
20018c2ecf20Sopenharmony_ci	char *tmp;
20028c2ecf20Sopenharmony_ci	int i;
20038c2ecf20Sopenharmony_ci
20048c2ecf20Sopenharmony_ci	if (!card)
20058c2ecf20Sopenharmony_ci		return -EINVAL;
20068c2ecf20Sopenharmony_ci	if (card->state != DEV_STATE_UP)
20078c2ecf20Sopenharmony_ci		return -EPERM;
20088c2ecf20Sopenharmony_ci	i = simple_strtoul(buf, &tmp, 16);
20098c2ecf20Sopenharmony_ci	if (i == 1)
20108c2ecf20Sopenharmony_ci		lcs_schedule_recovery(card);
20118c2ecf20Sopenharmony_ci	return count;
20128c2ecf20Sopenharmony_ci}
20138c2ecf20Sopenharmony_ci
20148c2ecf20Sopenharmony_cistatic DEVICE_ATTR(recover, 0200, NULL, lcs_dev_recover_store);
20158c2ecf20Sopenharmony_ci
20168c2ecf20Sopenharmony_cistatic struct attribute * lcs_attrs[] = {
20178c2ecf20Sopenharmony_ci	&dev_attr_portno.attr,
20188c2ecf20Sopenharmony_ci	&dev_attr_type.attr,
20198c2ecf20Sopenharmony_ci	&dev_attr_lancmd_timeout.attr,
20208c2ecf20Sopenharmony_ci	&dev_attr_recover.attr,
20218c2ecf20Sopenharmony_ci	NULL,
20228c2ecf20Sopenharmony_ci};
20238c2ecf20Sopenharmony_cistatic struct attribute_group lcs_attr_group = {
20248c2ecf20Sopenharmony_ci	.attrs = lcs_attrs,
20258c2ecf20Sopenharmony_ci};
20268c2ecf20Sopenharmony_cistatic const struct attribute_group *lcs_attr_groups[] = {
20278c2ecf20Sopenharmony_ci	&lcs_attr_group,
20288c2ecf20Sopenharmony_ci	NULL,
20298c2ecf20Sopenharmony_ci};
20308c2ecf20Sopenharmony_cistatic const struct device_type lcs_devtype = {
20318c2ecf20Sopenharmony_ci	.name = "lcs",
20328c2ecf20Sopenharmony_ci	.groups = lcs_attr_groups,
20338c2ecf20Sopenharmony_ci};
20348c2ecf20Sopenharmony_ci
20358c2ecf20Sopenharmony_ci/**
20368c2ecf20Sopenharmony_ci * lcs_probe_device is called on establishing a new ccwgroup_device.
20378c2ecf20Sopenharmony_ci */
20388c2ecf20Sopenharmony_cistatic int
20398c2ecf20Sopenharmony_cilcs_probe_device(struct ccwgroup_device *ccwgdev)
20408c2ecf20Sopenharmony_ci{
20418c2ecf20Sopenharmony_ci	struct lcs_card *card;
20428c2ecf20Sopenharmony_ci
20438c2ecf20Sopenharmony_ci	if (!get_device(&ccwgdev->dev))
20448c2ecf20Sopenharmony_ci		return -ENODEV;
20458c2ecf20Sopenharmony_ci
20468c2ecf20Sopenharmony_ci	LCS_DBF_TEXT(2, setup, "add_dev");
20478c2ecf20Sopenharmony_ci        card = lcs_alloc_card();
20488c2ecf20Sopenharmony_ci        if (!card) {
20498c2ecf20Sopenharmony_ci		LCS_DBF_TEXT_(2, setup, "  rc%d", -ENOMEM);
20508c2ecf20Sopenharmony_ci		put_device(&ccwgdev->dev);
20518c2ecf20Sopenharmony_ci                return -ENOMEM;
20528c2ecf20Sopenharmony_ci        }
20538c2ecf20Sopenharmony_ci	dev_set_drvdata(&ccwgdev->dev, card);
20548c2ecf20Sopenharmony_ci	ccwgdev->cdev[0]->handler = lcs_irq;
20558c2ecf20Sopenharmony_ci	ccwgdev->cdev[1]->handler = lcs_irq;
20568c2ecf20Sopenharmony_ci	card->gdev = ccwgdev;
20578c2ecf20Sopenharmony_ci	INIT_WORK(&card->kernel_thread_starter, lcs_start_kernel_thread);
20588c2ecf20Sopenharmony_ci	card->thread_start_mask = 0;
20598c2ecf20Sopenharmony_ci	card->thread_allowed_mask = 0;
20608c2ecf20Sopenharmony_ci	card->thread_running_mask = 0;
20618c2ecf20Sopenharmony_ci	ccwgdev->dev.type = &lcs_devtype;
20628c2ecf20Sopenharmony_ci
20638c2ecf20Sopenharmony_ci	return 0;
20648c2ecf20Sopenharmony_ci}
20658c2ecf20Sopenharmony_ci
20668c2ecf20Sopenharmony_cistatic int
20678c2ecf20Sopenharmony_cilcs_register_netdev(struct ccwgroup_device *ccwgdev)
20688c2ecf20Sopenharmony_ci{
20698c2ecf20Sopenharmony_ci	struct lcs_card *card;
20708c2ecf20Sopenharmony_ci
20718c2ecf20Sopenharmony_ci	LCS_DBF_TEXT(2, setup, "regnetdv");
20728c2ecf20Sopenharmony_ci	card = dev_get_drvdata(&ccwgdev->dev);
20738c2ecf20Sopenharmony_ci	if (card->dev->reg_state != NETREG_UNINITIALIZED)
20748c2ecf20Sopenharmony_ci		return 0;
20758c2ecf20Sopenharmony_ci	SET_NETDEV_DEV(card->dev, &ccwgdev->dev);
20768c2ecf20Sopenharmony_ci	return register_netdev(card->dev);
20778c2ecf20Sopenharmony_ci}
20788c2ecf20Sopenharmony_ci
20798c2ecf20Sopenharmony_ci/**
20808c2ecf20Sopenharmony_ci * lcs_new_device will be called by setting the group device online.
20818c2ecf20Sopenharmony_ci */
20828c2ecf20Sopenharmony_cistatic const struct net_device_ops lcs_netdev_ops = {
20838c2ecf20Sopenharmony_ci	.ndo_open		= lcs_open_device,
20848c2ecf20Sopenharmony_ci	.ndo_stop		= lcs_stop_device,
20858c2ecf20Sopenharmony_ci	.ndo_get_stats		= lcs_getstats,
20868c2ecf20Sopenharmony_ci	.ndo_start_xmit		= lcs_start_xmit,
20878c2ecf20Sopenharmony_ci};
20888c2ecf20Sopenharmony_ci
20898c2ecf20Sopenharmony_cistatic const struct net_device_ops lcs_mc_netdev_ops = {
20908c2ecf20Sopenharmony_ci	.ndo_open		= lcs_open_device,
20918c2ecf20Sopenharmony_ci	.ndo_stop		= lcs_stop_device,
20928c2ecf20Sopenharmony_ci	.ndo_get_stats		= lcs_getstats,
20938c2ecf20Sopenharmony_ci	.ndo_start_xmit		= lcs_start_xmit,
20948c2ecf20Sopenharmony_ci	.ndo_set_rx_mode	= lcs_set_multicast_list,
20958c2ecf20Sopenharmony_ci};
20968c2ecf20Sopenharmony_ci
20978c2ecf20Sopenharmony_cistatic int
20988c2ecf20Sopenharmony_cilcs_new_device(struct ccwgroup_device *ccwgdev)
20998c2ecf20Sopenharmony_ci{
21008c2ecf20Sopenharmony_ci	struct  lcs_card *card;
21018c2ecf20Sopenharmony_ci	struct net_device *dev=NULL;
21028c2ecf20Sopenharmony_ci	enum lcs_dev_states recover_state;
21038c2ecf20Sopenharmony_ci	int rc;
21048c2ecf20Sopenharmony_ci
21058c2ecf20Sopenharmony_ci	card = dev_get_drvdata(&ccwgdev->dev);
21068c2ecf20Sopenharmony_ci	if (!card)
21078c2ecf20Sopenharmony_ci		return -ENODEV;
21088c2ecf20Sopenharmony_ci
21098c2ecf20Sopenharmony_ci	LCS_DBF_TEXT(2, setup, "newdev");
21108c2ecf20Sopenharmony_ci	LCS_DBF_HEX(3, setup, &card, sizeof(void*));
21118c2ecf20Sopenharmony_ci	card->read.ccwdev  = ccwgdev->cdev[0];
21128c2ecf20Sopenharmony_ci	card->write.ccwdev = ccwgdev->cdev[1];
21138c2ecf20Sopenharmony_ci
21148c2ecf20Sopenharmony_ci	recover_state = card->state;
21158c2ecf20Sopenharmony_ci	rc = ccw_device_set_online(card->read.ccwdev);
21168c2ecf20Sopenharmony_ci	if (rc)
21178c2ecf20Sopenharmony_ci		goto out_err;
21188c2ecf20Sopenharmony_ci	rc = ccw_device_set_online(card->write.ccwdev);
21198c2ecf20Sopenharmony_ci	if (rc)
21208c2ecf20Sopenharmony_ci		goto out_werr;
21218c2ecf20Sopenharmony_ci
21228c2ecf20Sopenharmony_ci	LCS_DBF_TEXT(3, setup, "lcsnewdv");
21238c2ecf20Sopenharmony_ci
21248c2ecf20Sopenharmony_ci	lcs_setup_card(card);
21258c2ecf20Sopenharmony_ci	rc = lcs_detect(card);
21268c2ecf20Sopenharmony_ci	if (rc) {
21278c2ecf20Sopenharmony_ci		LCS_DBF_TEXT(2, setup, "dtctfail");
21288c2ecf20Sopenharmony_ci		dev_err(&ccwgdev->dev,
21298c2ecf20Sopenharmony_ci			"Detecting a network adapter for LCS devices"
21308c2ecf20Sopenharmony_ci			" failed with rc=%d (0x%x)\n", rc, rc);
21318c2ecf20Sopenharmony_ci		lcs_stopcard(card);
21328c2ecf20Sopenharmony_ci		goto out;
21338c2ecf20Sopenharmony_ci	}
21348c2ecf20Sopenharmony_ci	if (card->dev) {
21358c2ecf20Sopenharmony_ci		LCS_DBF_TEXT(2, setup, "samedev");
21368c2ecf20Sopenharmony_ci		LCS_DBF_HEX(3, setup, &card, sizeof(void*));
21378c2ecf20Sopenharmony_ci		goto netdev_out;
21388c2ecf20Sopenharmony_ci	}
21398c2ecf20Sopenharmony_ci	switch (card->lan_type) {
21408c2ecf20Sopenharmony_ci#ifdef CONFIG_ETHERNET
21418c2ecf20Sopenharmony_ci	case LCS_FRAME_TYPE_ENET:
21428c2ecf20Sopenharmony_ci		card->lan_type_trans = eth_type_trans;
21438c2ecf20Sopenharmony_ci		dev = alloc_etherdev(0);
21448c2ecf20Sopenharmony_ci		break;
21458c2ecf20Sopenharmony_ci#endif
21468c2ecf20Sopenharmony_ci#ifdef CONFIG_FDDI
21478c2ecf20Sopenharmony_ci	case LCS_FRAME_TYPE_FDDI:
21488c2ecf20Sopenharmony_ci		card->lan_type_trans = fddi_type_trans;
21498c2ecf20Sopenharmony_ci		dev = alloc_fddidev(0);
21508c2ecf20Sopenharmony_ci		break;
21518c2ecf20Sopenharmony_ci#endif
21528c2ecf20Sopenharmony_ci	default:
21538c2ecf20Sopenharmony_ci		LCS_DBF_TEXT(3, setup, "errinit");
21548c2ecf20Sopenharmony_ci		pr_err(" Initialization failed\n");
21558c2ecf20Sopenharmony_ci		goto out;
21568c2ecf20Sopenharmony_ci	}
21578c2ecf20Sopenharmony_ci	if (!dev)
21588c2ecf20Sopenharmony_ci		goto out;
21598c2ecf20Sopenharmony_ci	card->dev = dev;
21608c2ecf20Sopenharmony_ci	card->dev->ml_priv = card;
21618c2ecf20Sopenharmony_ci	card->dev->netdev_ops = &lcs_netdev_ops;
21628c2ecf20Sopenharmony_ci	card->dev->dev_port = card->portno;
21638c2ecf20Sopenharmony_ci	memcpy(card->dev->dev_addr, card->mac, LCS_MAC_LENGTH);
21648c2ecf20Sopenharmony_ci#ifdef CONFIG_IP_MULTICAST
21658c2ecf20Sopenharmony_ci	if (!lcs_check_multicast_support(card))
21668c2ecf20Sopenharmony_ci		card->dev->netdev_ops = &lcs_mc_netdev_ops;
21678c2ecf20Sopenharmony_ci#endif
21688c2ecf20Sopenharmony_cinetdev_out:
21698c2ecf20Sopenharmony_ci	lcs_set_allowed_threads(card,0xffffffff);
21708c2ecf20Sopenharmony_ci	if (recover_state == DEV_STATE_RECOVER) {
21718c2ecf20Sopenharmony_ci		lcs_set_multicast_list(card->dev);
21728c2ecf20Sopenharmony_ci		card->dev->flags |= IFF_UP;
21738c2ecf20Sopenharmony_ci		netif_carrier_on(card->dev);
21748c2ecf20Sopenharmony_ci		netif_wake_queue(card->dev);
21758c2ecf20Sopenharmony_ci		card->state = DEV_STATE_UP;
21768c2ecf20Sopenharmony_ci	} else {
21778c2ecf20Sopenharmony_ci		lcs_stopcard(card);
21788c2ecf20Sopenharmony_ci	}
21798c2ecf20Sopenharmony_ci
21808c2ecf20Sopenharmony_ci	if (lcs_register_netdev(ccwgdev) != 0)
21818c2ecf20Sopenharmony_ci		goto out;
21828c2ecf20Sopenharmony_ci
21838c2ecf20Sopenharmony_ci	/* Print out supported assists: IPv6 */
21848c2ecf20Sopenharmony_ci	pr_info("LCS device %s %s IPv6 support\n", card->dev->name,
21858c2ecf20Sopenharmony_ci		(card->ip_assists_supported & LCS_IPASS_IPV6_SUPPORT) ?
21868c2ecf20Sopenharmony_ci		"with" : "without");
21878c2ecf20Sopenharmony_ci	/* Print out supported assist: Multicast */
21888c2ecf20Sopenharmony_ci	pr_info("LCS device %s %s Multicast support\n", card->dev->name,
21898c2ecf20Sopenharmony_ci		(card->ip_assists_supported & LCS_IPASS_MULTICAST_SUPPORT) ?
21908c2ecf20Sopenharmony_ci		"with" : "without");
21918c2ecf20Sopenharmony_ci	return 0;
21928c2ecf20Sopenharmony_ciout:
21938c2ecf20Sopenharmony_ci
21948c2ecf20Sopenharmony_ci	ccw_device_set_offline(card->write.ccwdev);
21958c2ecf20Sopenharmony_ciout_werr:
21968c2ecf20Sopenharmony_ci	ccw_device_set_offline(card->read.ccwdev);
21978c2ecf20Sopenharmony_ciout_err:
21988c2ecf20Sopenharmony_ci	return -ENODEV;
21998c2ecf20Sopenharmony_ci}
22008c2ecf20Sopenharmony_ci
22018c2ecf20Sopenharmony_ci/**
22028c2ecf20Sopenharmony_ci * lcs_shutdown_device, called when setting the group device offline.
22038c2ecf20Sopenharmony_ci */
22048c2ecf20Sopenharmony_cistatic int
22058c2ecf20Sopenharmony_ci__lcs_shutdown_device(struct ccwgroup_device *ccwgdev, int recovery_mode)
22068c2ecf20Sopenharmony_ci{
22078c2ecf20Sopenharmony_ci	struct lcs_card *card;
22088c2ecf20Sopenharmony_ci	enum lcs_dev_states recover_state;
22098c2ecf20Sopenharmony_ci	int ret = 0, ret2 = 0, ret3 = 0;
22108c2ecf20Sopenharmony_ci
22118c2ecf20Sopenharmony_ci	LCS_DBF_TEXT(3, setup, "shtdndev");
22128c2ecf20Sopenharmony_ci	card = dev_get_drvdata(&ccwgdev->dev);
22138c2ecf20Sopenharmony_ci	if (!card)
22148c2ecf20Sopenharmony_ci		return -ENODEV;
22158c2ecf20Sopenharmony_ci	if (recovery_mode == 0) {
22168c2ecf20Sopenharmony_ci		lcs_set_allowed_threads(card, 0);
22178c2ecf20Sopenharmony_ci		if (lcs_wait_for_threads(card, LCS_SET_MC_THREAD))
22188c2ecf20Sopenharmony_ci			return -ERESTARTSYS;
22198c2ecf20Sopenharmony_ci	}
22208c2ecf20Sopenharmony_ci	LCS_DBF_HEX(3, setup, &card, sizeof(void*));
22218c2ecf20Sopenharmony_ci	recover_state = card->state;
22228c2ecf20Sopenharmony_ci
22238c2ecf20Sopenharmony_ci	ret = lcs_stop_device(card->dev);
22248c2ecf20Sopenharmony_ci	ret2 = ccw_device_set_offline(card->read.ccwdev);
22258c2ecf20Sopenharmony_ci	ret3 = ccw_device_set_offline(card->write.ccwdev);
22268c2ecf20Sopenharmony_ci	if (!ret)
22278c2ecf20Sopenharmony_ci		ret = (ret2) ? ret2 : ret3;
22288c2ecf20Sopenharmony_ci	if (ret)
22298c2ecf20Sopenharmony_ci		LCS_DBF_TEXT_(3, setup, "1err:%d", ret);
22308c2ecf20Sopenharmony_ci	if (recover_state == DEV_STATE_UP) {
22318c2ecf20Sopenharmony_ci		card->state = DEV_STATE_RECOVER;
22328c2ecf20Sopenharmony_ci	}
22338c2ecf20Sopenharmony_ci	return 0;
22348c2ecf20Sopenharmony_ci}
22358c2ecf20Sopenharmony_ci
22368c2ecf20Sopenharmony_cistatic int
22378c2ecf20Sopenharmony_cilcs_shutdown_device(struct ccwgroup_device *ccwgdev)
22388c2ecf20Sopenharmony_ci{
22398c2ecf20Sopenharmony_ci	return __lcs_shutdown_device(ccwgdev, 0);
22408c2ecf20Sopenharmony_ci}
22418c2ecf20Sopenharmony_ci
22428c2ecf20Sopenharmony_ci/**
22438c2ecf20Sopenharmony_ci * drive lcs recovery after startup and startlan initiated by Lan Gateway
22448c2ecf20Sopenharmony_ci */
22458c2ecf20Sopenharmony_cistatic int
22468c2ecf20Sopenharmony_cilcs_recovery(void *ptr)
22478c2ecf20Sopenharmony_ci{
22488c2ecf20Sopenharmony_ci	struct lcs_card *card;
22498c2ecf20Sopenharmony_ci	struct ccwgroup_device *gdev;
22508c2ecf20Sopenharmony_ci        int rc;
22518c2ecf20Sopenharmony_ci
22528c2ecf20Sopenharmony_ci	card = (struct lcs_card *) ptr;
22538c2ecf20Sopenharmony_ci
22548c2ecf20Sopenharmony_ci	LCS_DBF_TEXT(4, trace, "recover1");
22558c2ecf20Sopenharmony_ci	if (!lcs_do_run_thread(card, LCS_RECOVERY_THREAD))
22568c2ecf20Sopenharmony_ci		return 0;
22578c2ecf20Sopenharmony_ci	LCS_DBF_TEXT(4, trace, "recover2");
22588c2ecf20Sopenharmony_ci	gdev = card->gdev;
22598c2ecf20Sopenharmony_ci	dev_warn(&gdev->dev,
22608c2ecf20Sopenharmony_ci		"A recovery process has been started for the LCS device\n");
22618c2ecf20Sopenharmony_ci	rc = __lcs_shutdown_device(gdev, 1);
22628c2ecf20Sopenharmony_ci	rc = lcs_new_device(gdev);
22638c2ecf20Sopenharmony_ci	if (!rc)
22648c2ecf20Sopenharmony_ci		pr_info("Device %s successfully recovered!\n",
22658c2ecf20Sopenharmony_ci			card->dev->name);
22668c2ecf20Sopenharmony_ci	else
22678c2ecf20Sopenharmony_ci		pr_info("Device %s could not be recovered!\n",
22688c2ecf20Sopenharmony_ci			card->dev->name);
22698c2ecf20Sopenharmony_ci	lcs_clear_thread_running_bit(card, LCS_RECOVERY_THREAD);
22708c2ecf20Sopenharmony_ci	return 0;
22718c2ecf20Sopenharmony_ci}
22728c2ecf20Sopenharmony_ci
22738c2ecf20Sopenharmony_ci/**
22748c2ecf20Sopenharmony_ci * lcs_remove_device, free buffers and card
22758c2ecf20Sopenharmony_ci */
22768c2ecf20Sopenharmony_cistatic void
22778c2ecf20Sopenharmony_cilcs_remove_device(struct ccwgroup_device *ccwgdev)
22788c2ecf20Sopenharmony_ci{
22798c2ecf20Sopenharmony_ci	struct lcs_card *card;
22808c2ecf20Sopenharmony_ci
22818c2ecf20Sopenharmony_ci	card = dev_get_drvdata(&ccwgdev->dev);
22828c2ecf20Sopenharmony_ci	if (!card)
22838c2ecf20Sopenharmony_ci		return;
22848c2ecf20Sopenharmony_ci
22858c2ecf20Sopenharmony_ci	LCS_DBF_TEXT(3, setup, "remdev");
22868c2ecf20Sopenharmony_ci	LCS_DBF_HEX(3, setup, &card, sizeof(void*));
22878c2ecf20Sopenharmony_ci	if (ccwgdev->state == CCWGROUP_ONLINE) {
22888c2ecf20Sopenharmony_ci		lcs_shutdown_device(ccwgdev);
22898c2ecf20Sopenharmony_ci	}
22908c2ecf20Sopenharmony_ci	if (card->dev)
22918c2ecf20Sopenharmony_ci		unregister_netdev(card->dev);
22928c2ecf20Sopenharmony_ci	lcs_cleanup_card(card);
22938c2ecf20Sopenharmony_ci	lcs_free_card(card);
22948c2ecf20Sopenharmony_ci	dev_set_drvdata(&ccwgdev->dev, NULL);
22958c2ecf20Sopenharmony_ci	put_device(&ccwgdev->dev);
22968c2ecf20Sopenharmony_ci}
22978c2ecf20Sopenharmony_ci
22988c2ecf20Sopenharmony_cistatic struct ccw_device_id lcs_ids[] = {
22998c2ecf20Sopenharmony_ci	{CCW_DEVICE(0x3088, 0x08), .driver_info = lcs_channel_type_parallel},
23008c2ecf20Sopenharmony_ci	{CCW_DEVICE(0x3088, 0x1f), .driver_info = lcs_channel_type_2216},
23018c2ecf20Sopenharmony_ci	{CCW_DEVICE(0x3088, 0x60), .driver_info = lcs_channel_type_osa2},
23028c2ecf20Sopenharmony_ci	{},
23038c2ecf20Sopenharmony_ci};
23048c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(ccw, lcs_ids);
23058c2ecf20Sopenharmony_ci
23068c2ecf20Sopenharmony_cistatic struct ccw_driver lcs_ccw_driver = {
23078c2ecf20Sopenharmony_ci	.driver = {
23088c2ecf20Sopenharmony_ci		.owner	= THIS_MODULE,
23098c2ecf20Sopenharmony_ci		.name	= "lcs",
23108c2ecf20Sopenharmony_ci	},
23118c2ecf20Sopenharmony_ci	.ids	= lcs_ids,
23128c2ecf20Sopenharmony_ci	.probe	= ccwgroup_probe_ccwdev,
23138c2ecf20Sopenharmony_ci	.remove	= ccwgroup_remove_ccwdev,
23148c2ecf20Sopenharmony_ci	.int_class = IRQIO_LCS,
23158c2ecf20Sopenharmony_ci};
23168c2ecf20Sopenharmony_ci
23178c2ecf20Sopenharmony_ci/**
23188c2ecf20Sopenharmony_ci * LCS ccwgroup driver registration
23198c2ecf20Sopenharmony_ci */
23208c2ecf20Sopenharmony_cistatic struct ccwgroup_driver lcs_group_driver = {
23218c2ecf20Sopenharmony_ci	.driver = {
23228c2ecf20Sopenharmony_ci		.owner	= THIS_MODULE,
23238c2ecf20Sopenharmony_ci		.name	= "lcs",
23248c2ecf20Sopenharmony_ci	},
23258c2ecf20Sopenharmony_ci	.ccw_driver  = &lcs_ccw_driver,
23268c2ecf20Sopenharmony_ci	.setup	     = lcs_probe_device,
23278c2ecf20Sopenharmony_ci	.remove      = lcs_remove_device,
23288c2ecf20Sopenharmony_ci	.set_online  = lcs_new_device,
23298c2ecf20Sopenharmony_ci	.set_offline = lcs_shutdown_device,
23308c2ecf20Sopenharmony_ci};
23318c2ecf20Sopenharmony_ci
23328c2ecf20Sopenharmony_cistatic ssize_t group_store(struct device_driver *ddrv, const char *buf,
23338c2ecf20Sopenharmony_ci			   size_t count)
23348c2ecf20Sopenharmony_ci{
23358c2ecf20Sopenharmony_ci	int err;
23368c2ecf20Sopenharmony_ci	err = ccwgroup_create_dev(lcs_root_dev, &lcs_group_driver, 2, buf);
23378c2ecf20Sopenharmony_ci	return err ? err : count;
23388c2ecf20Sopenharmony_ci}
23398c2ecf20Sopenharmony_cistatic DRIVER_ATTR_WO(group);
23408c2ecf20Sopenharmony_ci
23418c2ecf20Sopenharmony_cistatic struct attribute *lcs_drv_attrs[] = {
23428c2ecf20Sopenharmony_ci	&driver_attr_group.attr,
23438c2ecf20Sopenharmony_ci	NULL,
23448c2ecf20Sopenharmony_ci};
23458c2ecf20Sopenharmony_cistatic struct attribute_group lcs_drv_attr_group = {
23468c2ecf20Sopenharmony_ci	.attrs = lcs_drv_attrs,
23478c2ecf20Sopenharmony_ci};
23488c2ecf20Sopenharmony_cistatic const struct attribute_group *lcs_drv_attr_groups[] = {
23498c2ecf20Sopenharmony_ci	&lcs_drv_attr_group,
23508c2ecf20Sopenharmony_ci	NULL,
23518c2ecf20Sopenharmony_ci};
23528c2ecf20Sopenharmony_ci
23538c2ecf20Sopenharmony_ci/**
23548c2ecf20Sopenharmony_ci *  LCS Module/Kernel initialization function
23558c2ecf20Sopenharmony_ci */
23568c2ecf20Sopenharmony_cistatic int
23578c2ecf20Sopenharmony_ci__init lcs_init_module(void)
23588c2ecf20Sopenharmony_ci{
23598c2ecf20Sopenharmony_ci	int rc;
23608c2ecf20Sopenharmony_ci
23618c2ecf20Sopenharmony_ci	pr_info("Loading %s\n", version);
23628c2ecf20Sopenharmony_ci	rc = lcs_register_debug_facility();
23638c2ecf20Sopenharmony_ci	LCS_DBF_TEXT(0, setup, "lcsinit");
23648c2ecf20Sopenharmony_ci	if (rc)
23658c2ecf20Sopenharmony_ci		goto out_err;
23668c2ecf20Sopenharmony_ci	lcs_root_dev = root_device_register("lcs");
23678c2ecf20Sopenharmony_ci	rc = PTR_ERR_OR_ZERO(lcs_root_dev);
23688c2ecf20Sopenharmony_ci	if (rc)
23698c2ecf20Sopenharmony_ci		goto register_err;
23708c2ecf20Sopenharmony_ci	rc = ccw_driver_register(&lcs_ccw_driver);
23718c2ecf20Sopenharmony_ci	if (rc)
23728c2ecf20Sopenharmony_ci		goto ccw_err;
23738c2ecf20Sopenharmony_ci	lcs_group_driver.driver.groups = lcs_drv_attr_groups;
23748c2ecf20Sopenharmony_ci	rc = ccwgroup_driver_register(&lcs_group_driver);
23758c2ecf20Sopenharmony_ci	if (rc)
23768c2ecf20Sopenharmony_ci		goto ccwgroup_err;
23778c2ecf20Sopenharmony_ci	return 0;
23788c2ecf20Sopenharmony_ci
23798c2ecf20Sopenharmony_ciccwgroup_err:
23808c2ecf20Sopenharmony_ci	ccw_driver_unregister(&lcs_ccw_driver);
23818c2ecf20Sopenharmony_ciccw_err:
23828c2ecf20Sopenharmony_ci	root_device_unregister(lcs_root_dev);
23838c2ecf20Sopenharmony_ciregister_err:
23848c2ecf20Sopenharmony_ci	lcs_unregister_debug_facility();
23858c2ecf20Sopenharmony_ciout_err:
23868c2ecf20Sopenharmony_ci	pr_err("Initializing the lcs device driver failed\n");
23878c2ecf20Sopenharmony_ci	return rc;
23888c2ecf20Sopenharmony_ci}
23898c2ecf20Sopenharmony_ci
23908c2ecf20Sopenharmony_ci
23918c2ecf20Sopenharmony_ci/**
23928c2ecf20Sopenharmony_ci *  LCS module cleanup function
23938c2ecf20Sopenharmony_ci */
23948c2ecf20Sopenharmony_cistatic void
23958c2ecf20Sopenharmony_ci__exit lcs_cleanup_module(void)
23968c2ecf20Sopenharmony_ci{
23978c2ecf20Sopenharmony_ci	pr_info("Terminating lcs module.\n");
23988c2ecf20Sopenharmony_ci	LCS_DBF_TEXT(0, trace, "cleanup");
23998c2ecf20Sopenharmony_ci	ccwgroup_driver_unregister(&lcs_group_driver);
24008c2ecf20Sopenharmony_ci	ccw_driver_unregister(&lcs_ccw_driver);
24018c2ecf20Sopenharmony_ci	root_device_unregister(lcs_root_dev);
24028c2ecf20Sopenharmony_ci	lcs_unregister_debug_facility();
24038c2ecf20Sopenharmony_ci}
24048c2ecf20Sopenharmony_ci
24058c2ecf20Sopenharmony_cimodule_init(lcs_init_module);
24068c2ecf20Sopenharmony_cimodule_exit(lcs_cleanup_module);
24078c2ecf20Sopenharmony_ci
24088c2ecf20Sopenharmony_ciMODULE_AUTHOR("Frank Pavlic <fpavlic@de.ibm.com>");
24098c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
24108c2ecf20Sopenharmony_ci
2411