162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * IPWireless 3G PCMCIA Network Driver
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Original code
662306a36Sopenharmony_ci *   by Stephen Blackheath <stephen@blacksapphire.com>,
762306a36Sopenharmony_ci *      Ben Martel <benm@symmetric.co.nz>
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci * Copyrighted as follows:
1062306a36Sopenharmony_ci *   Copyright (C) 2004 by Symmetric Systems Ltd (NZ)
1162306a36Sopenharmony_ci *
1262306a36Sopenharmony_ci * Various driver changes and rewrites, port to new kernels
1362306a36Sopenharmony_ci *   Copyright (C) 2006-2007 Jiri Kosina
1462306a36Sopenharmony_ci *
1562306a36Sopenharmony_ci * Misc code cleanups and updates
1662306a36Sopenharmony_ci *   Copyright (C) 2007 David Sterba
1762306a36Sopenharmony_ci */
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#include <linux/interrupt.h>
2062306a36Sopenharmony_ci#include <linux/io.h>
2162306a36Sopenharmony_ci#include <linux/irq.h>
2262306a36Sopenharmony_ci#include <linux/kernel.h>
2362306a36Sopenharmony_ci#include <linux/list.h>
2462306a36Sopenharmony_ci#include <linux/slab.h>
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci#include "hardware.h"
2762306a36Sopenharmony_ci#include "setup_protocol.h"
2862306a36Sopenharmony_ci#include "network.h"
2962306a36Sopenharmony_ci#include "main.h"
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_cistatic void ipw_send_setup_packet(struct ipw_hardware *hw);
3262306a36Sopenharmony_cistatic void handle_received_SETUP_packet(struct ipw_hardware *ipw,
3362306a36Sopenharmony_ci					 unsigned int address,
3462306a36Sopenharmony_ci					 const unsigned char *data, int len,
3562306a36Sopenharmony_ci					 int is_last);
3662306a36Sopenharmony_cistatic void ipwireless_setup_timer(struct timer_list *t);
3762306a36Sopenharmony_cistatic void handle_received_CTRL_packet(struct ipw_hardware *hw,
3862306a36Sopenharmony_ci		unsigned int channel_idx, const unsigned char *data, int len);
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci/*#define TIMING_DIAGNOSTICS*/
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci#ifdef TIMING_DIAGNOSTICS
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_cistatic struct timing_stats {
4562306a36Sopenharmony_ci	unsigned long last_report_time;
4662306a36Sopenharmony_ci	unsigned long read_time;
4762306a36Sopenharmony_ci	unsigned long write_time;
4862306a36Sopenharmony_ci	unsigned long read_bytes;
4962306a36Sopenharmony_ci	unsigned long write_bytes;
5062306a36Sopenharmony_ci	unsigned long start_time;
5162306a36Sopenharmony_ci};
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_cistatic void start_timing(void)
5462306a36Sopenharmony_ci{
5562306a36Sopenharmony_ci	timing_stats.start_time = jiffies;
5662306a36Sopenharmony_ci}
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_cistatic void end_read_timing(unsigned length)
5962306a36Sopenharmony_ci{
6062306a36Sopenharmony_ci	timing_stats.read_time += (jiffies - start_time);
6162306a36Sopenharmony_ci	timing_stats.read_bytes += length + 2;
6262306a36Sopenharmony_ci	report_timing();
6362306a36Sopenharmony_ci}
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_cistatic void end_write_timing(unsigned length)
6662306a36Sopenharmony_ci{
6762306a36Sopenharmony_ci	timing_stats.write_time += (jiffies - start_time);
6862306a36Sopenharmony_ci	timing_stats.write_bytes += length + 2;
6962306a36Sopenharmony_ci	report_timing();
7062306a36Sopenharmony_ci}
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_cistatic void report_timing(void)
7362306a36Sopenharmony_ci{
7462306a36Sopenharmony_ci	unsigned long since = jiffies - timing_stats.last_report_time;
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	/* If it's been more than one second... */
7762306a36Sopenharmony_ci	if (since >= HZ) {
7862306a36Sopenharmony_ci		int first = (timing_stats.last_report_time == 0);
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci		timing_stats.last_report_time = jiffies;
8162306a36Sopenharmony_ci		if (!first)
8262306a36Sopenharmony_ci			printk(KERN_INFO IPWIRELESS_PCCARD_NAME
8362306a36Sopenharmony_ci			       ": %u us elapsed - read %lu bytes in %u us, wrote %lu bytes in %u us\n",
8462306a36Sopenharmony_ci			       jiffies_to_usecs(since),
8562306a36Sopenharmony_ci			       timing_stats.read_bytes,
8662306a36Sopenharmony_ci			       jiffies_to_usecs(timing_stats.read_time),
8762306a36Sopenharmony_ci			       timing_stats.write_bytes,
8862306a36Sopenharmony_ci			       jiffies_to_usecs(timing_stats.write_time));
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci		timing_stats.read_time = 0;
9162306a36Sopenharmony_ci		timing_stats.write_time = 0;
9262306a36Sopenharmony_ci		timing_stats.read_bytes = 0;
9362306a36Sopenharmony_ci		timing_stats.write_bytes = 0;
9462306a36Sopenharmony_ci	}
9562306a36Sopenharmony_ci}
9662306a36Sopenharmony_ci#else
9762306a36Sopenharmony_cistatic void start_timing(void) { }
9862306a36Sopenharmony_cistatic void end_read_timing(unsigned length) { }
9962306a36Sopenharmony_cistatic void end_write_timing(unsigned length) { }
10062306a36Sopenharmony_ci#endif
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci/* Imported IPW definitions */
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci#define LL_MTU_V1 318
10562306a36Sopenharmony_ci#define LL_MTU_V2 250
10662306a36Sopenharmony_ci#define LL_MTU_MAX (LL_MTU_V1 > LL_MTU_V2 ? LL_MTU_V1 : LL_MTU_V2)
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci#define PRIO_DATA  2
10962306a36Sopenharmony_ci#define PRIO_CTRL  1
11062306a36Sopenharmony_ci#define PRIO_SETUP 0
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci/* Addresses */
11362306a36Sopenharmony_ci#define ADDR_SETUP_PROT 0
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci/* Protocol ids */
11662306a36Sopenharmony_cienum {
11762306a36Sopenharmony_ci	/* Identifier for the Com Data protocol */
11862306a36Sopenharmony_ci	TL_PROTOCOLID_COM_DATA = 0,
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	/* Identifier for the Com Control protocol */
12162306a36Sopenharmony_ci	TL_PROTOCOLID_COM_CTRL = 1,
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	/* Identifier for the Setup protocol */
12462306a36Sopenharmony_ci	TL_PROTOCOLID_SETUP = 2
12562306a36Sopenharmony_ci};
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci/* Number of bytes in NL packet header (cannot do
12862306a36Sopenharmony_ci * sizeof(nl_packet_header) since it's a bitfield) */
12962306a36Sopenharmony_ci#define NL_FIRST_PACKET_HEADER_SIZE        3
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci/* Number of bytes in NL packet header (cannot do
13262306a36Sopenharmony_ci * sizeof(nl_packet_header) since it's a bitfield) */
13362306a36Sopenharmony_ci#define NL_FOLLOWING_PACKET_HEADER_SIZE    1
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_cistruct nl_first_packet_header {
13662306a36Sopenharmony_ci	unsigned char protocol:3;
13762306a36Sopenharmony_ci	unsigned char address:3;
13862306a36Sopenharmony_ci	unsigned char packet_rank:2;
13962306a36Sopenharmony_ci	unsigned char length_lsb;
14062306a36Sopenharmony_ci	unsigned char length_msb;
14162306a36Sopenharmony_ci};
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_cistruct nl_packet_header {
14462306a36Sopenharmony_ci	unsigned char protocol:3;
14562306a36Sopenharmony_ci	unsigned char address:3;
14662306a36Sopenharmony_ci	unsigned char packet_rank:2;
14762306a36Sopenharmony_ci};
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci/* Value of 'packet_rank' above */
15062306a36Sopenharmony_ci#define NL_INTERMEDIATE_PACKET    0x0
15162306a36Sopenharmony_ci#define NL_LAST_PACKET            0x1
15262306a36Sopenharmony_ci#define NL_FIRST_PACKET           0x2
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ciunion nl_packet {
15562306a36Sopenharmony_ci	/* Network packet header of the first packet (a special case) */
15662306a36Sopenharmony_ci	struct nl_first_packet_header hdr_first;
15762306a36Sopenharmony_ci	/* Network packet header of the following packets (if any) */
15862306a36Sopenharmony_ci	struct nl_packet_header hdr;
15962306a36Sopenharmony_ci	/* Complete network packet (header + data) */
16062306a36Sopenharmony_ci	unsigned char rawpkt[LL_MTU_MAX];
16162306a36Sopenharmony_ci} __attribute__ ((__packed__));
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci#define HW_VERSION_UNKNOWN -1
16462306a36Sopenharmony_ci#define HW_VERSION_1 1
16562306a36Sopenharmony_ci#define HW_VERSION_2 2
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci/* IPW I/O ports */
16862306a36Sopenharmony_ci#define IOIER 0x00		/* Interrupt Enable Register */
16962306a36Sopenharmony_ci#define IOIR  0x02		/* Interrupt Source/ACK register */
17062306a36Sopenharmony_ci#define IODCR 0x04		/* Data Control Register */
17162306a36Sopenharmony_ci#define IODRR 0x06		/* Data Read Register */
17262306a36Sopenharmony_ci#define IODWR 0x08		/* Data Write Register */
17362306a36Sopenharmony_ci#define IOESR 0x0A		/* Embedded Driver Status Register */
17462306a36Sopenharmony_ci#define IORXR 0x0C		/* Rx Fifo Register (Host to Embedded) */
17562306a36Sopenharmony_ci#define IOTXR 0x0E		/* Tx Fifo Register (Embedded to Host) */
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci/* I/O ports and bit definitions for version 1 of the hardware */
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci/* IER bits*/
18062306a36Sopenharmony_ci#define IER_RXENABLED   0x1
18162306a36Sopenharmony_ci#define IER_TXENABLED   0x2
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci/* ISR bits */
18462306a36Sopenharmony_ci#define IR_RXINTR       0x1
18562306a36Sopenharmony_ci#define IR_TXINTR       0x2
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci/* DCR bits */
18862306a36Sopenharmony_ci#define DCR_RXDONE      0x1
18962306a36Sopenharmony_ci#define DCR_TXDONE      0x2
19062306a36Sopenharmony_ci#define DCR_RXRESET     0x4
19162306a36Sopenharmony_ci#define DCR_TXRESET     0x8
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci/* I/O ports and bit definitions for version 2 of the hardware */
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_cistruct MEMCCR {
19662306a36Sopenharmony_ci	unsigned short reg_config_option;	/* PCCOR: Configuration Option Register */
19762306a36Sopenharmony_ci	unsigned short reg_config_and_status;	/* PCCSR: Configuration and Status Register */
19862306a36Sopenharmony_ci	unsigned short reg_pin_replacement;	/* PCPRR: Pin Replacemant Register */
19962306a36Sopenharmony_ci	unsigned short reg_socket_and_copy;	/* PCSCR: Socket and Copy Register */
20062306a36Sopenharmony_ci	unsigned short reg_ext_status;		/* PCESR: Extendend Status Register */
20162306a36Sopenharmony_ci	unsigned short reg_io_base;		/* PCIOB: I/O Base Register */
20262306a36Sopenharmony_ci};
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_cistruct MEMINFREG {
20562306a36Sopenharmony_ci	unsigned short memreg_tx_old;	/* TX Register (R/W) */
20662306a36Sopenharmony_ci	unsigned short pad1;
20762306a36Sopenharmony_ci	unsigned short memreg_rx_done;	/* RXDone Register (R/W) */
20862306a36Sopenharmony_ci	unsigned short pad2;
20962306a36Sopenharmony_ci	unsigned short memreg_rx;	/* RX Register (R/W) */
21062306a36Sopenharmony_ci	unsigned short pad3;
21162306a36Sopenharmony_ci	unsigned short memreg_pc_interrupt_ack;	/* PC intr Ack Register (W) */
21262306a36Sopenharmony_ci	unsigned short pad4;
21362306a36Sopenharmony_ci	unsigned long memreg_card_present;/* Mask for Host to check (R) for
21462306a36Sopenharmony_ci					   * CARD_PRESENT_VALUE */
21562306a36Sopenharmony_ci	unsigned short memreg_tx_new;	/* TX2 (new) Register (R/W) */
21662306a36Sopenharmony_ci};
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci#define CARD_PRESENT_VALUE (0xBEEFCAFEUL)
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci#define MEMTX_TX                       0x0001
22162306a36Sopenharmony_ci#define MEMRX_RX                       0x0001
22262306a36Sopenharmony_ci#define MEMRX_RX_DONE                  0x0001
22362306a36Sopenharmony_ci#define MEMRX_PCINTACKK                0x0001
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci#define NL_NUM_OF_PRIORITIES       3
22662306a36Sopenharmony_ci#define NL_NUM_OF_PROTOCOLS        3
22762306a36Sopenharmony_ci#define NL_NUM_OF_ADDRESSES        NO_OF_IPW_CHANNELS
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_cistruct ipw_hardware {
23062306a36Sopenharmony_ci	unsigned int base_port;
23162306a36Sopenharmony_ci	short hw_version;
23262306a36Sopenharmony_ci	unsigned short ll_mtu;
23362306a36Sopenharmony_ci	spinlock_t lock;
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	int initializing;
23662306a36Sopenharmony_ci	int init_loops;
23762306a36Sopenharmony_ci	struct timer_list setup_timer;
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci	/* Flag if hw is ready to send next packet */
24062306a36Sopenharmony_ci	int tx_ready;
24162306a36Sopenharmony_ci	/* Count of pending packets to be sent */
24262306a36Sopenharmony_ci	int tx_queued;
24362306a36Sopenharmony_ci	struct list_head tx_queue[NL_NUM_OF_PRIORITIES];
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci	int rx_bytes_queued;
24662306a36Sopenharmony_ci	struct list_head rx_queue;
24762306a36Sopenharmony_ci	/* Pool of rx_packet structures that are not currently used. */
24862306a36Sopenharmony_ci	struct list_head rx_pool;
24962306a36Sopenharmony_ci	int rx_pool_size;
25062306a36Sopenharmony_ci	/* True if reception of data is blocked while userspace processes it. */
25162306a36Sopenharmony_ci	int blocking_rx;
25262306a36Sopenharmony_ci	/* True if there is RX data ready on the hardware. */
25362306a36Sopenharmony_ci	int rx_ready;
25462306a36Sopenharmony_ci	unsigned short last_memtx_serial;
25562306a36Sopenharmony_ci	/*
25662306a36Sopenharmony_ci	 * Newer versions of the V2 card firmware send serial numbers in the
25762306a36Sopenharmony_ci	 * MemTX register. 'serial_number_detected' is set true when we detect
25862306a36Sopenharmony_ci	 * a non-zero serial number (indicating the new firmware).  Thereafter,
25962306a36Sopenharmony_ci	 * the driver can safely ignore the Timer Recovery re-sends to avoid
26062306a36Sopenharmony_ci	 * out-of-sync problems.
26162306a36Sopenharmony_ci	 */
26262306a36Sopenharmony_ci	int serial_number_detected;
26362306a36Sopenharmony_ci	struct work_struct work_rx;
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	/* True if we are to send the set-up data to the hardware. */
26662306a36Sopenharmony_ci	int to_setup;
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	/* Card has been removed */
26962306a36Sopenharmony_ci	int removed;
27062306a36Sopenharmony_ci	/* Saved irq value when we disable the interrupt. */
27162306a36Sopenharmony_ci	int irq;
27262306a36Sopenharmony_ci	/* True if this driver is shutting down. */
27362306a36Sopenharmony_ci	int shutting_down;
27462306a36Sopenharmony_ci	/* Modem control lines */
27562306a36Sopenharmony_ci	unsigned int control_lines[NL_NUM_OF_ADDRESSES];
27662306a36Sopenharmony_ci	struct ipw_rx_packet *packet_assembler[NL_NUM_OF_ADDRESSES];
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci	struct tasklet_struct tasklet;
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci	/* The handle for the network layer, for the sending of events to it. */
28162306a36Sopenharmony_ci	struct ipw_network *network;
28262306a36Sopenharmony_ci	struct MEMINFREG __iomem *memory_info_regs;
28362306a36Sopenharmony_ci	struct MEMCCR __iomem *memregs_CCR;
28462306a36Sopenharmony_ci	void (*reboot_callback) (void *data);
28562306a36Sopenharmony_ci	void *reboot_callback_data;
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci	unsigned short __iomem *memreg_tx;
28862306a36Sopenharmony_ci};
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci/*
29162306a36Sopenharmony_ci * Packet info structure for tx packets.
29262306a36Sopenharmony_ci * Note: not all the fields defined here are required for all protocols
29362306a36Sopenharmony_ci */
29462306a36Sopenharmony_cistruct ipw_tx_packet {
29562306a36Sopenharmony_ci	struct list_head queue;
29662306a36Sopenharmony_ci	/* channel idx + 1 */
29762306a36Sopenharmony_ci	unsigned char dest_addr;
29862306a36Sopenharmony_ci	/* SETUP, CTRL or DATA */
29962306a36Sopenharmony_ci	unsigned char protocol;
30062306a36Sopenharmony_ci	/* Length of data block, which starts at the end of this structure */
30162306a36Sopenharmony_ci	unsigned short length;
30262306a36Sopenharmony_ci	/* Sending state */
30362306a36Sopenharmony_ci	/* Offset of where we've sent up to so far */
30462306a36Sopenharmony_ci	unsigned long offset;
30562306a36Sopenharmony_ci	/* Count of packet fragments, starting at 0 */
30662306a36Sopenharmony_ci	int fragment_count;
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci	/* Called after packet is sent and before is freed */
30962306a36Sopenharmony_ci	void (*packet_callback) (void *cb_data, unsigned int packet_length);
31062306a36Sopenharmony_ci	void *callback_data;
31162306a36Sopenharmony_ci};
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci/* Signals from DTE */
31462306a36Sopenharmony_ci#define COMCTRL_RTS	0
31562306a36Sopenharmony_ci#define COMCTRL_DTR	1
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci/* Signals from DCE */
31862306a36Sopenharmony_ci#define COMCTRL_CTS	2
31962306a36Sopenharmony_ci#define COMCTRL_DCD	3
32062306a36Sopenharmony_ci#define COMCTRL_DSR	4
32162306a36Sopenharmony_ci#define COMCTRL_RI	5
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_cistruct ipw_control_packet_body {
32462306a36Sopenharmony_ci	/* DTE signal or DCE signal */
32562306a36Sopenharmony_ci	unsigned char sig_no;
32662306a36Sopenharmony_ci	/* 0: set signal, 1: clear signal */
32762306a36Sopenharmony_ci	unsigned char value;
32862306a36Sopenharmony_ci} __attribute__ ((__packed__));
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_cistruct ipw_control_packet {
33162306a36Sopenharmony_ci	struct ipw_tx_packet header;
33262306a36Sopenharmony_ci	struct ipw_control_packet_body body;
33362306a36Sopenharmony_ci};
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_cistruct ipw_rx_packet {
33662306a36Sopenharmony_ci	struct list_head queue;
33762306a36Sopenharmony_ci	unsigned int capacity;
33862306a36Sopenharmony_ci	unsigned int length;
33962306a36Sopenharmony_ci	unsigned int protocol;
34062306a36Sopenharmony_ci	unsigned int channel_idx;
34162306a36Sopenharmony_ci};
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_cistatic char *data_type(const unsigned char *buf, unsigned length)
34462306a36Sopenharmony_ci{
34562306a36Sopenharmony_ci	struct nl_packet_header *hdr = (struct nl_packet_header *) buf;
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci	if (length == 0)
34862306a36Sopenharmony_ci		return "     ";
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci	if (hdr->packet_rank & NL_FIRST_PACKET) {
35162306a36Sopenharmony_ci		switch (hdr->protocol) {
35262306a36Sopenharmony_ci		case TL_PROTOCOLID_COM_DATA:	return "DATA ";
35362306a36Sopenharmony_ci		case TL_PROTOCOLID_COM_CTRL:	return "CTRL ";
35462306a36Sopenharmony_ci		case TL_PROTOCOLID_SETUP:	return "SETUP";
35562306a36Sopenharmony_ci		default: return "???? ";
35662306a36Sopenharmony_ci		}
35762306a36Sopenharmony_ci	} else
35862306a36Sopenharmony_ci		return "     ";
35962306a36Sopenharmony_ci}
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_ci#define DUMP_MAX_BYTES 64
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_cistatic void dump_data_bytes(const char *type, const unsigned char *data,
36462306a36Sopenharmony_ci			    unsigned length)
36562306a36Sopenharmony_ci{
36662306a36Sopenharmony_ci	char prefix[56];
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_ci	sprintf(prefix, IPWIRELESS_PCCARD_NAME ": %s %s ",
36962306a36Sopenharmony_ci			type, data_type(data, length));
37062306a36Sopenharmony_ci	print_hex_dump_bytes(prefix, 0, (void *)data,
37162306a36Sopenharmony_ci			length < DUMP_MAX_BYTES ? length : DUMP_MAX_BYTES);
37262306a36Sopenharmony_ci}
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_cistatic void swap_packet_bitfield_to_le(unsigned char *data)
37562306a36Sopenharmony_ci{
37662306a36Sopenharmony_ci#ifdef __BIG_ENDIAN_BITFIELD
37762306a36Sopenharmony_ci	unsigned char tmp = *data, ret = 0;
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci	/*
38062306a36Sopenharmony_ci	 * transform bits from aa.bbb.ccc to ccc.bbb.aa
38162306a36Sopenharmony_ci	 */
38262306a36Sopenharmony_ci	ret |= (tmp & 0xc0) >> 6;
38362306a36Sopenharmony_ci	ret |= (tmp & 0x38) >> 1;
38462306a36Sopenharmony_ci	ret |= (tmp & 0x07) << 5;
38562306a36Sopenharmony_ci	*data = ret & 0xff;
38662306a36Sopenharmony_ci#endif
38762306a36Sopenharmony_ci}
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_cistatic void swap_packet_bitfield_from_le(unsigned char *data)
39062306a36Sopenharmony_ci{
39162306a36Sopenharmony_ci#ifdef __BIG_ENDIAN_BITFIELD
39262306a36Sopenharmony_ci	unsigned char tmp = *data, ret = 0;
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci	/*
39562306a36Sopenharmony_ci	 * transform bits from ccc.bbb.aa to aa.bbb.ccc
39662306a36Sopenharmony_ci	 */
39762306a36Sopenharmony_ci	ret |= (tmp & 0xe0) >> 5;
39862306a36Sopenharmony_ci	ret |= (tmp & 0x1c) << 1;
39962306a36Sopenharmony_ci	ret |= (tmp & 0x03) << 6;
40062306a36Sopenharmony_ci	*data = ret & 0xff;
40162306a36Sopenharmony_ci#endif
40262306a36Sopenharmony_ci}
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_cistatic void do_send_fragment(struct ipw_hardware *hw, unsigned char *data,
40562306a36Sopenharmony_ci			    unsigned length)
40662306a36Sopenharmony_ci{
40762306a36Sopenharmony_ci	unsigned i;
40862306a36Sopenharmony_ci	unsigned long flags;
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci	start_timing();
41162306a36Sopenharmony_ci	BUG_ON(length > hw->ll_mtu);
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ci	if (ipwireless_debug)
41462306a36Sopenharmony_ci		dump_data_bytes("send", data, length);
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_ci	spin_lock_irqsave(&hw->lock, flags);
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci	hw->tx_ready = 0;
41962306a36Sopenharmony_ci	swap_packet_bitfield_to_le(data);
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_ci	if (hw->hw_version == HW_VERSION_1) {
42262306a36Sopenharmony_ci		outw((unsigned short) length, hw->base_port + IODWR);
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_ci		for (i = 0; i < length; i += 2) {
42562306a36Sopenharmony_ci			unsigned short d = data[i];
42662306a36Sopenharmony_ci			__le16 raw_data;
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci			if (i + 1 < length)
42962306a36Sopenharmony_ci				d |= data[i + 1] << 8;
43062306a36Sopenharmony_ci			raw_data = cpu_to_le16(d);
43162306a36Sopenharmony_ci			outw(raw_data, hw->base_port + IODWR);
43262306a36Sopenharmony_ci		}
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci		outw(DCR_TXDONE, hw->base_port + IODCR);
43562306a36Sopenharmony_ci	} else if (hw->hw_version == HW_VERSION_2) {
43662306a36Sopenharmony_ci		outw((unsigned short) length, hw->base_port);
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_ci		for (i = 0; i < length; i += 2) {
43962306a36Sopenharmony_ci			unsigned short d = data[i];
44062306a36Sopenharmony_ci			__le16 raw_data;
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ci			if (i + 1 < length)
44362306a36Sopenharmony_ci				d |= data[i + 1] << 8;
44462306a36Sopenharmony_ci			raw_data = cpu_to_le16(d);
44562306a36Sopenharmony_ci			outw(raw_data, hw->base_port);
44662306a36Sopenharmony_ci		}
44762306a36Sopenharmony_ci		while ((i & 3) != 2) {
44862306a36Sopenharmony_ci			outw((unsigned short) 0xDEAD, hw->base_port);
44962306a36Sopenharmony_ci			i += 2;
45062306a36Sopenharmony_ci		}
45162306a36Sopenharmony_ci		writew(MEMRX_RX, &hw->memory_info_regs->memreg_rx);
45262306a36Sopenharmony_ci	}
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci	spin_unlock_irqrestore(&hw->lock, flags);
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci	end_write_timing(length);
45762306a36Sopenharmony_ci}
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_cistatic void do_send_packet(struct ipw_hardware *hw, struct ipw_tx_packet *packet)
46062306a36Sopenharmony_ci{
46162306a36Sopenharmony_ci	unsigned short fragment_data_len;
46262306a36Sopenharmony_ci	unsigned short data_left = packet->length - packet->offset;
46362306a36Sopenharmony_ci	unsigned short header_size;
46462306a36Sopenharmony_ci	union nl_packet pkt;
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_ci	header_size =
46762306a36Sopenharmony_ci	    (packet->fragment_count == 0)
46862306a36Sopenharmony_ci	    ? NL_FIRST_PACKET_HEADER_SIZE
46962306a36Sopenharmony_ci	    : NL_FOLLOWING_PACKET_HEADER_SIZE;
47062306a36Sopenharmony_ci	fragment_data_len = hw->ll_mtu - header_size;
47162306a36Sopenharmony_ci	if (data_left < fragment_data_len)
47262306a36Sopenharmony_ci		fragment_data_len = data_left;
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ci	/*
47562306a36Sopenharmony_ci	 * hdr_first is now in machine bitfield order, which will be swapped
47662306a36Sopenharmony_ci	 * to le just before it goes to hw
47762306a36Sopenharmony_ci	 */
47862306a36Sopenharmony_ci	pkt.hdr_first.protocol = packet->protocol;
47962306a36Sopenharmony_ci	pkt.hdr_first.address = packet->dest_addr;
48062306a36Sopenharmony_ci	pkt.hdr_first.packet_rank = 0;
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_ci	/* First packet? */
48362306a36Sopenharmony_ci	if (packet->fragment_count == 0) {
48462306a36Sopenharmony_ci		pkt.hdr_first.packet_rank |= NL_FIRST_PACKET;
48562306a36Sopenharmony_ci		pkt.hdr_first.length_lsb = (unsigned char) packet->length;
48662306a36Sopenharmony_ci		pkt.hdr_first.length_msb =
48762306a36Sopenharmony_ci			(unsigned char) (packet->length >> 8);
48862306a36Sopenharmony_ci	}
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_ci	memcpy(pkt.rawpkt + header_size,
49162306a36Sopenharmony_ci	       ((unsigned char *) packet) + sizeof(struct ipw_tx_packet) +
49262306a36Sopenharmony_ci	       packet->offset, fragment_data_len);
49362306a36Sopenharmony_ci	packet->offset += fragment_data_len;
49462306a36Sopenharmony_ci	packet->fragment_count++;
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci	/* Last packet? (May also be first packet.) */
49762306a36Sopenharmony_ci	if (packet->offset == packet->length)
49862306a36Sopenharmony_ci		pkt.hdr_first.packet_rank |= NL_LAST_PACKET;
49962306a36Sopenharmony_ci	do_send_fragment(hw, pkt.rawpkt, header_size + fragment_data_len);
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_ci	/* If this packet has unsent data, then re-queue it. */
50262306a36Sopenharmony_ci	if (packet->offset < packet->length) {
50362306a36Sopenharmony_ci		/*
50462306a36Sopenharmony_ci		 * Re-queue it at the head of the highest priority queue so
50562306a36Sopenharmony_ci		 * it goes before all other packets
50662306a36Sopenharmony_ci		 */
50762306a36Sopenharmony_ci		unsigned long flags;
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_ci		spin_lock_irqsave(&hw->lock, flags);
51062306a36Sopenharmony_ci		list_add(&packet->queue, &hw->tx_queue[0]);
51162306a36Sopenharmony_ci		hw->tx_queued++;
51262306a36Sopenharmony_ci		spin_unlock_irqrestore(&hw->lock, flags);
51362306a36Sopenharmony_ci	} else {
51462306a36Sopenharmony_ci		if (packet->packet_callback)
51562306a36Sopenharmony_ci			packet->packet_callback(packet->callback_data,
51662306a36Sopenharmony_ci					packet->length);
51762306a36Sopenharmony_ci		kfree(packet);
51862306a36Sopenharmony_ci	}
51962306a36Sopenharmony_ci}
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_cistatic void ipw_setup_hardware(struct ipw_hardware *hw)
52262306a36Sopenharmony_ci{
52362306a36Sopenharmony_ci	unsigned long flags;
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ci	spin_lock_irqsave(&hw->lock, flags);
52662306a36Sopenharmony_ci	if (hw->hw_version == HW_VERSION_1) {
52762306a36Sopenharmony_ci		/* Reset RX FIFO */
52862306a36Sopenharmony_ci		outw(DCR_RXRESET, hw->base_port + IODCR);
52962306a36Sopenharmony_ci		/* SB: Reset TX FIFO */
53062306a36Sopenharmony_ci		outw(DCR_TXRESET, hw->base_port + IODCR);
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_ci		/* Enable TX and RX interrupts. */
53362306a36Sopenharmony_ci		outw(IER_TXENABLED | IER_RXENABLED, hw->base_port + IOIER);
53462306a36Sopenharmony_ci	} else {
53562306a36Sopenharmony_ci		/*
53662306a36Sopenharmony_ci		 * Set INTRACK bit (bit 0), which means we must explicitly
53762306a36Sopenharmony_ci		 * acknowledge interrupts by clearing bit 2 of reg_config_and_status.
53862306a36Sopenharmony_ci		 */
53962306a36Sopenharmony_ci		unsigned short csr = readw(&hw->memregs_CCR->reg_config_and_status);
54062306a36Sopenharmony_ci
54162306a36Sopenharmony_ci		csr |= 1;
54262306a36Sopenharmony_ci		writew(csr, &hw->memregs_CCR->reg_config_and_status);
54362306a36Sopenharmony_ci	}
54462306a36Sopenharmony_ci	spin_unlock_irqrestore(&hw->lock, flags);
54562306a36Sopenharmony_ci}
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci/*
54862306a36Sopenharmony_ci * If 'packet' is NULL, then this function allocates a new packet, setting its
54962306a36Sopenharmony_ci * length to 0 and ensuring it has the specified minimum amount of free space.
55062306a36Sopenharmony_ci *
55162306a36Sopenharmony_ci * If 'packet' is not NULL, then this function enlarges it if it doesn't
55262306a36Sopenharmony_ci * have the specified minimum amount of free space.
55362306a36Sopenharmony_ci *
55462306a36Sopenharmony_ci */
55562306a36Sopenharmony_cistatic struct ipw_rx_packet *pool_allocate(struct ipw_hardware *hw,
55662306a36Sopenharmony_ci					   struct ipw_rx_packet *packet,
55762306a36Sopenharmony_ci					   int minimum_free_space)
55862306a36Sopenharmony_ci{
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_ci	if (!packet) {
56162306a36Sopenharmony_ci		unsigned long flags;
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_ci		spin_lock_irqsave(&hw->lock, flags);
56462306a36Sopenharmony_ci		if (!list_empty(&hw->rx_pool)) {
56562306a36Sopenharmony_ci			packet = list_first_entry(&hw->rx_pool,
56662306a36Sopenharmony_ci					struct ipw_rx_packet, queue);
56762306a36Sopenharmony_ci			hw->rx_pool_size--;
56862306a36Sopenharmony_ci			spin_unlock_irqrestore(&hw->lock, flags);
56962306a36Sopenharmony_ci			list_del(&packet->queue);
57062306a36Sopenharmony_ci		} else {
57162306a36Sopenharmony_ci			const int min_capacity =
57262306a36Sopenharmony_ci				ipwireless_ppp_mru(hw->network) + 2;
57362306a36Sopenharmony_ci			int new_capacity;
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_ci			spin_unlock_irqrestore(&hw->lock, flags);
57662306a36Sopenharmony_ci			new_capacity =
57762306a36Sopenharmony_ci				(minimum_free_space > min_capacity
57862306a36Sopenharmony_ci				 ? minimum_free_space
57962306a36Sopenharmony_ci				 : min_capacity);
58062306a36Sopenharmony_ci			packet = kmalloc(sizeof(struct ipw_rx_packet)
58162306a36Sopenharmony_ci					+ new_capacity, GFP_ATOMIC);
58262306a36Sopenharmony_ci			if (!packet)
58362306a36Sopenharmony_ci				return NULL;
58462306a36Sopenharmony_ci			packet->capacity = new_capacity;
58562306a36Sopenharmony_ci		}
58662306a36Sopenharmony_ci		packet->length = 0;
58762306a36Sopenharmony_ci	}
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_ci	if (packet->length + minimum_free_space > packet->capacity) {
59062306a36Sopenharmony_ci		struct ipw_rx_packet *old_packet = packet;
59162306a36Sopenharmony_ci
59262306a36Sopenharmony_ci		packet = kmalloc(sizeof(struct ipw_rx_packet) +
59362306a36Sopenharmony_ci				old_packet->length + minimum_free_space,
59462306a36Sopenharmony_ci				GFP_ATOMIC);
59562306a36Sopenharmony_ci		if (!packet) {
59662306a36Sopenharmony_ci			kfree(old_packet);
59762306a36Sopenharmony_ci			return NULL;
59862306a36Sopenharmony_ci		}
59962306a36Sopenharmony_ci		memcpy(packet, old_packet,
60062306a36Sopenharmony_ci				sizeof(struct ipw_rx_packet)
60162306a36Sopenharmony_ci					+ old_packet->length);
60262306a36Sopenharmony_ci		packet->capacity = old_packet->length + minimum_free_space;
60362306a36Sopenharmony_ci		kfree(old_packet);
60462306a36Sopenharmony_ci	}
60562306a36Sopenharmony_ci
60662306a36Sopenharmony_ci	return packet;
60762306a36Sopenharmony_ci}
60862306a36Sopenharmony_ci
60962306a36Sopenharmony_cistatic void pool_free(struct ipw_hardware *hw, struct ipw_rx_packet *packet)
61062306a36Sopenharmony_ci{
61162306a36Sopenharmony_ci	if (hw->rx_pool_size > 6)
61262306a36Sopenharmony_ci		kfree(packet);
61362306a36Sopenharmony_ci	else {
61462306a36Sopenharmony_ci		hw->rx_pool_size++;
61562306a36Sopenharmony_ci		list_add(&packet->queue, &hw->rx_pool);
61662306a36Sopenharmony_ci	}
61762306a36Sopenharmony_ci}
61862306a36Sopenharmony_ci
61962306a36Sopenharmony_cistatic void queue_received_packet(struct ipw_hardware *hw,
62062306a36Sopenharmony_ci				  unsigned int protocol,
62162306a36Sopenharmony_ci				  unsigned int address,
62262306a36Sopenharmony_ci				  const unsigned char *data, int length,
62362306a36Sopenharmony_ci				  int is_last)
62462306a36Sopenharmony_ci{
62562306a36Sopenharmony_ci	unsigned int channel_idx = address - 1;
62662306a36Sopenharmony_ci	struct ipw_rx_packet *packet = NULL;
62762306a36Sopenharmony_ci	unsigned long flags;
62862306a36Sopenharmony_ci
62962306a36Sopenharmony_ci	/* Discard packet if channel index is out of range. */
63062306a36Sopenharmony_ci	if (channel_idx >= NL_NUM_OF_ADDRESSES) {
63162306a36Sopenharmony_ci		printk(KERN_INFO IPWIRELESS_PCCARD_NAME
63262306a36Sopenharmony_ci		       ": data packet has bad address %u\n", address);
63362306a36Sopenharmony_ci		return;
63462306a36Sopenharmony_ci	}
63562306a36Sopenharmony_ci
63662306a36Sopenharmony_ci	/*
63762306a36Sopenharmony_ci	 * ->packet_assembler is safe to touch unlocked, this is the only place
63862306a36Sopenharmony_ci	 */
63962306a36Sopenharmony_ci	if (protocol == TL_PROTOCOLID_COM_DATA) {
64062306a36Sopenharmony_ci		struct ipw_rx_packet **assem =
64162306a36Sopenharmony_ci			&hw->packet_assembler[channel_idx];
64262306a36Sopenharmony_ci
64362306a36Sopenharmony_ci		/*
64462306a36Sopenharmony_ci		 * Create a new packet, or assembler already contains one
64562306a36Sopenharmony_ci		 * enlarge it by 'length' bytes.
64662306a36Sopenharmony_ci		 */
64762306a36Sopenharmony_ci		(*assem) = pool_allocate(hw, *assem, length);
64862306a36Sopenharmony_ci		if (!(*assem)) {
64962306a36Sopenharmony_ci			printk(KERN_ERR IPWIRELESS_PCCARD_NAME
65062306a36Sopenharmony_ci				": no memory for incoming data packet, dropped!\n");
65162306a36Sopenharmony_ci			return;
65262306a36Sopenharmony_ci		}
65362306a36Sopenharmony_ci		(*assem)->protocol = protocol;
65462306a36Sopenharmony_ci		(*assem)->channel_idx = channel_idx;
65562306a36Sopenharmony_ci
65662306a36Sopenharmony_ci		/* Append this packet data onto existing data. */
65762306a36Sopenharmony_ci		memcpy((unsigned char *)(*assem) +
65862306a36Sopenharmony_ci			       sizeof(struct ipw_rx_packet)
65962306a36Sopenharmony_ci				+ (*assem)->length, data, length);
66062306a36Sopenharmony_ci		(*assem)->length += length;
66162306a36Sopenharmony_ci		if (is_last) {
66262306a36Sopenharmony_ci			packet = *assem;
66362306a36Sopenharmony_ci			*assem = NULL;
66462306a36Sopenharmony_ci			/* Count queued DATA bytes only */
66562306a36Sopenharmony_ci			spin_lock_irqsave(&hw->lock, flags);
66662306a36Sopenharmony_ci			hw->rx_bytes_queued += packet->length;
66762306a36Sopenharmony_ci			spin_unlock_irqrestore(&hw->lock, flags);
66862306a36Sopenharmony_ci		}
66962306a36Sopenharmony_ci	} else {
67062306a36Sopenharmony_ci		/* If it's a CTRL packet, don't assemble, just queue it. */
67162306a36Sopenharmony_ci		packet = pool_allocate(hw, NULL, length);
67262306a36Sopenharmony_ci		if (!packet) {
67362306a36Sopenharmony_ci			printk(KERN_ERR IPWIRELESS_PCCARD_NAME
67462306a36Sopenharmony_ci				": no memory for incoming ctrl packet, dropped!\n");
67562306a36Sopenharmony_ci			return;
67662306a36Sopenharmony_ci		}
67762306a36Sopenharmony_ci		packet->protocol = protocol;
67862306a36Sopenharmony_ci		packet->channel_idx = channel_idx;
67962306a36Sopenharmony_ci		memcpy((unsigned char *)packet + sizeof(struct ipw_rx_packet),
68062306a36Sopenharmony_ci				data, length);
68162306a36Sopenharmony_ci		packet->length = length;
68262306a36Sopenharmony_ci	}
68362306a36Sopenharmony_ci
68462306a36Sopenharmony_ci	/*
68562306a36Sopenharmony_ci	 * If this is the last packet, then send the assembled packet on to the
68662306a36Sopenharmony_ci	 * network layer.
68762306a36Sopenharmony_ci	 */
68862306a36Sopenharmony_ci	if (packet) {
68962306a36Sopenharmony_ci		spin_lock_irqsave(&hw->lock, flags);
69062306a36Sopenharmony_ci		list_add_tail(&packet->queue, &hw->rx_queue);
69162306a36Sopenharmony_ci		/* Block reception of incoming packets if queue is full. */
69262306a36Sopenharmony_ci		hw->blocking_rx =
69362306a36Sopenharmony_ci			(hw->rx_bytes_queued >= IPWIRELESS_RX_QUEUE_SIZE);
69462306a36Sopenharmony_ci
69562306a36Sopenharmony_ci		spin_unlock_irqrestore(&hw->lock, flags);
69662306a36Sopenharmony_ci		schedule_work(&hw->work_rx);
69762306a36Sopenharmony_ci	}
69862306a36Sopenharmony_ci}
69962306a36Sopenharmony_ci
70062306a36Sopenharmony_ci/*
70162306a36Sopenharmony_ci * Workqueue callback
70262306a36Sopenharmony_ci */
70362306a36Sopenharmony_cistatic void ipw_receive_data_work(struct work_struct *work_rx)
70462306a36Sopenharmony_ci{
70562306a36Sopenharmony_ci	struct ipw_hardware *hw =
70662306a36Sopenharmony_ci	    container_of(work_rx, struct ipw_hardware, work_rx);
70762306a36Sopenharmony_ci	unsigned long flags;
70862306a36Sopenharmony_ci
70962306a36Sopenharmony_ci	spin_lock_irqsave(&hw->lock, flags);
71062306a36Sopenharmony_ci	while (!list_empty(&hw->rx_queue)) {
71162306a36Sopenharmony_ci		struct ipw_rx_packet *packet =
71262306a36Sopenharmony_ci			list_first_entry(&hw->rx_queue,
71362306a36Sopenharmony_ci					struct ipw_rx_packet, queue);
71462306a36Sopenharmony_ci
71562306a36Sopenharmony_ci		if (hw->shutting_down)
71662306a36Sopenharmony_ci			break;
71762306a36Sopenharmony_ci		list_del(&packet->queue);
71862306a36Sopenharmony_ci
71962306a36Sopenharmony_ci		/*
72062306a36Sopenharmony_ci		 * Note: ipwireless_network_packet_received must be called in a
72162306a36Sopenharmony_ci		 * process context (i.e. via schedule_work) because the tty
72262306a36Sopenharmony_ci		 * output code can sleep in the tty_flip_buffer_push call.
72362306a36Sopenharmony_ci		 */
72462306a36Sopenharmony_ci		if (packet->protocol == TL_PROTOCOLID_COM_DATA) {
72562306a36Sopenharmony_ci			if (hw->network != NULL) {
72662306a36Sopenharmony_ci				/* If the network hasn't been disconnected. */
72762306a36Sopenharmony_ci				spin_unlock_irqrestore(&hw->lock, flags);
72862306a36Sopenharmony_ci				/*
72962306a36Sopenharmony_ci				 * This must run unlocked due to tty processing
73062306a36Sopenharmony_ci				 * and mutex locking
73162306a36Sopenharmony_ci				 */
73262306a36Sopenharmony_ci				ipwireless_network_packet_received(
73362306a36Sopenharmony_ci						hw->network,
73462306a36Sopenharmony_ci						packet->channel_idx,
73562306a36Sopenharmony_ci						(unsigned char *)packet
73662306a36Sopenharmony_ci						+ sizeof(struct ipw_rx_packet),
73762306a36Sopenharmony_ci						packet->length);
73862306a36Sopenharmony_ci				spin_lock_irqsave(&hw->lock, flags);
73962306a36Sopenharmony_ci			}
74062306a36Sopenharmony_ci			/* Count queued DATA bytes only */
74162306a36Sopenharmony_ci			hw->rx_bytes_queued -= packet->length;
74262306a36Sopenharmony_ci		} else {
74362306a36Sopenharmony_ci			/*
74462306a36Sopenharmony_ci			 * This is safe to be called locked, callchain does
74562306a36Sopenharmony_ci			 * not block
74662306a36Sopenharmony_ci			 */
74762306a36Sopenharmony_ci			handle_received_CTRL_packet(hw, packet->channel_idx,
74862306a36Sopenharmony_ci					(unsigned char *)packet
74962306a36Sopenharmony_ci					+ sizeof(struct ipw_rx_packet),
75062306a36Sopenharmony_ci					packet->length);
75162306a36Sopenharmony_ci		}
75262306a36Sopenharmony_ci		pool_free(hw, packet);
75362306a36Sopenharmony_ci		/*
75462306a36Sopenharmony_ci		 * Unblock reception of incoming packets if queue is no longer
75562306a36Sopenharmony_ci		 * full.
75662306a36Sopenharmony_ci		 */
75762306a36Sopenharmony_ci		hw->blocking_rx =
75862306a36Sopenharmony_ci			hw->rx_bytes_queued >= IPWIRELESS_RX_QUEUE_SIZE;
75962306a36Sopenharmony_ci		if (hw->shutting_down)
76062306a36Sopenharmony_ci			break;
76162306a36Sopenharmony_ci	}
76262306a36Sopenharmony_ci	spin_unlock_irqrestore(&hw->lock, flags);
76362306a36Sopenharmony_ci}
76462306a36Sopenharmony_ci
76562306a36Sopenharmony_cistatic void handle_received_CTRL_packet(struct ipw_hardware *hw,
76662306a36Sopenharmony_ci					unsigned int channel_idx,
76762306a36Sopenharmony_ci					const unsigned char *data, int len)
76862306a36Sopenharmony_ci{
76962306a36Sopenharmony_ci	const struct ipw_control_packet_body *body =
77062306a36Sopenharmony_ci		(const struct ipw_control_packet_body *) data;
77162306a36Sopenharmony_ci	unsigned int changed_mask;
77262306a36Sopenharmony_ci
77362306a36Sopenharmony_ci	if (len != sizeof(struct ipw_control_packet_body)) {
77462306a36Sopenharmony_ci		printk(KERN_INFO IPWIRELESS_PCCARD_NAME
77562306a36Sopenharmony_ci		       ": control packet was %d bytes - wrong size!\n",
77662306a36Sopenharmony_ci		       len);
77762306a36Sopenharmony_ci		return;
77862306a36Sopenharmony_ci	}
77962306a36Sopenharmony_ci
78062306a36Sopenharmony_ci	switch (body->sig_no) {
78162306a36Sopenharmony_ci	case COMCTRL_CTS:
78262306a36Sopenharmony_ci		changed_mask = IPW_CONTROL_LINE_CTS;
78362306a36Sopenharmony_ci		break;
78462306a36Sopenharmony_ci	case COMCTRL_DCD:
78562306a36Sopenharmony_ci		changed_mask = IPW_CONTROL_LINE_DCD;
78662306a36Sopenharmony_ci		break;
78762306a36Sopenharmony_ci	case COMCTRL_DSR:
78862306a36Sopenharmony_ci		changed_mask = IPW_CONTROL_LINE_DSR;
78962306a36Sopenharmony_ci		break;
79062306a36Sopenharmony_ci	case COMCTRL_RI:
79162306a36Sopenharmony_ci		changed_mask = IPW_CONTROL_LINE_RI;
79262306a36Sopenharmony_ci		break;
79362306a36Sopenharmony_ci	default:
79462306a36Sopenharmony_ci		changed_mask = 0;
79562306a36Sopenharmony_ci	}
79662306a36Sopenharmony_ci
79762306a36Sopenharmony_ci	if (changed_mask != 0) {
79862306a36Sopenharmony_ci		if (body->value)
79962306a36Sopenharmony_ci			hw->control_lines[channel_idx] |= changed_mask;
80062306a36Sopenharmony_ci		else
80162306a36Sopenharmony_ci			hw->control_lines[channel_idx] &= ~changed_mask;
80262306a36Sopenharmony_ci		if (hw->network)
80362306a36Sopenharmony_ci			ipwireless_network_notify_control_line_change(
80462306a36Sopenharmony_ci					hw->network,
80562306a36Sopenharmony_ci					channel_idx,
80662306a36Sopenharmony_ci					hw->control_lines[channel_idx],
80762306a36Sopenharmony_ci					changed_mask);
80862306a36Sopenharmony_ci	}
80962306a36Sopenharmony_ci}
81062306a36Sopenharmony_ci
81162306a36Sopenharmony_cistatic void handle_received_packet(struct ipw_hardware *hw,
81262306a36Sopenharmony_ci				   const union nl_packet *packet,
81362306a36Sopenharmony_ci				   unsigned short len)
81462306a36Sopenharmony_ci{
81562306a36Sopenharmony_ci	unsigned int protocol = packet->hdr.protocol;
81662306a36Sopenharmony_ci	unsigned int address = packet->hdr.address;
81762306a36Sopenharmony_ci	unsigned int header_length;
81862306a36Sopenharmony_ci	const unsigned char *data;
81962306a36Sopenharmony_ci	unsigned int data_len;
82062306a36Sopenharmony_ci	int is_last = packet->hdr.packet_rank & NL_LAST_PACKET;
82162306a36Sopenharmony_ci
82262306a36Sopenharmony_ci	if (packet->hdr.packet_rank & NL_FIRST_PACKET)
82362306a36Sopenharmony_ci		header_length = NL_FIRST_PACKET_HEADER_SIZE;
82462306a36Sopenharmony_ci	else
82562306a36Sopenharmony_ci		header_length = NL_FOLLOWING_PACKET_HEADER_SIZE;
82662306a36Sopenharmony_ci
82762306a36Sopenharmony_ci	data = packet->rawpkt + header_length;
82862306a36Sopenharmony_ci	data_len = len - header_length;
82962306a36Sopenharmony_ci	switch (protocol) {
83062306a36Sopenharmony_ci	case TL_PROTOCOLID_COM_DATA:
83162306a36Sopenharmony_ci	case TL_PROTOCOLID_COM_CTRL:
83262306a36Sopenharmony_ci		queue_received_packet(hw, protocol, address, data, data_len,
83362306a36Sopenharmony_ci				is_last);
83462306a36Sopenharmony_ci		break;
83562306a36Sopenharmony_ci	case TL_PROTOCOLID_SETUP:
83662306a36Sopenharmony_ci		handle_received_SETUP_packet(hw, address, data, data_len,
83762306a36Sopenharmony_ci				is_last);
83862306a36Sopenharmony_ci		break;
83962306a36Sopenharmony_ci	}
84062306a36Sopenharmony_ci}
84162306a36Sopenharmony_ci
84262306a36Sopenharmony_cistatic void acknowledge_data_read(struct ipw_hardware *hw)
84362306a36Sopenharmony_ci{
84462306a36Sopenharmony_ci	if (hw->hw_version == HW_VERSION_1)
84562306a36Sopenharmony_ci		outw(DCR_RXDONE, hw->base_port + IODCR);
84662306a36Sopenharmony_ci	else
84762306a36Sopenharmony_ci		writew(MEMRX_PCINTACKK,
84862306a36Sopenharmony_ci				&hw->memory_info_regs->memreg_pc_interrupt_ack);
84962306a36Sopenharmony_ci}
85062306a36Sopenharmony_ci
85162306a36Sopenharmony_ci/*
85262306a36Sopenharmony_ci * Retrieve a packet from the IPW hardware.
85362306a36Sopenharmony_ci */
85462306a36Sopenharmony_cistatic void do_receive_packet(struct ipw_hardware *hw)
85562306a36Sopenharmony_ci{
85662306a36Sopenharmony_ci	unsigned len;
85762306a36Sopenharmony_ci	unsigned i;
85862306a36Sopenharmony_ci	unsigned char pkt[LL_MTU_MAX];
85962306a36Sopenharmony_ci
86062306a36Sopenharmony_ci	start_timing();
86162306a36Sopenharmony_ci
86262306a36Sopenharmony_ci	if (hw->hw_version == HW_VERSION_1) {
86362306a36Sopenharmony_ci		len = inw(hw->base_port + IODRR);
86462306a36Sopenharmony_ci		if (len > hw->ll_mtu) {
86562306a36Sopenharmony_ci			printk(KERN_INFO IPWIRELESS_PCCARD_NAME
86662306a36Sopenharmony_ci			       ": received a packet of %u bytes - longer than the MTU!\n", len);
86762306a36Sopenharmony_ci			outw(DCR_RXDONE | DCR_RXRESET, hw->base_port + IODCR);
86862306a36Sopenharmony_ci			return;
86962306a36Sopenharmony_ci		}
87062306a36Sopenharmony_ci
87162306a36Sopenharmony_ci		for (i = 0; i < len; i += 2) {
87262306a36Sopenharmony_ci			__le16 raw_data = inw(hw->base_port + IODRR);
87362306a36Sopenharmony_ci			unsigned short data = le16_to_cpu(raw_data);
87462306a36Sopenharmony_ci
87562306a36Sopenharmony_ci			pkt[i] = (unsigned char) data;
87662306a36Sopenharmony_ci			pkt[i + 1] = (unsigned char) (data >> 8);
87762306a36Sopenharmony_ci		}
87862306a36Sopenharmony_ci	} else {
87962306a36Sopenharmony_ci		len = inw(hw->base_port);
88062306a36Sopenharmony_ci		if (len > hw->ll_mtu) {
88162306a36Sopenharmony_ci			printk(KERN_INFO IPWIRELESS_PCCARD_NAME
88262306a36Sopenharmony_ci			       ": received a packet of %u bytes - longer than the MTU!\n", len);
88362306a36Sopenharmony_ci			writew(MEMRX_PCINTACKK,
88462306a36Sopenharmony_ci				&hw->memory_info_regs->memreg_pc_interrupt_ack);
88562306a36Sopenharmony_ci			return;
88662306a36Sopenharmony_ci		}
88762306a36Sopenharmony_ci
88862306a36Sopenharmony_ci		for (i = 0; i < len; i += 2) {
88962306a36Sopenharmony_ci			__le16 raw_data = inw(hw->base_port);
89062306a36Sopenharmony_ci			unsigned short data = le16_to_cpu(raw_data);
89162306a36Sopenharmony_ci
89262306a36Sopenharmony_ci			pkt[i] = (unsigned char) data;
89362306a36Sopenharmony_ci			pkt[i + 1] = (unsigned char) (data >> 8);
89462306a36Sopenharmony_ci		}
89562306a36Sopenharmony_ci
89662306a36Sopenharmony_ci		while ((i & 3) != 2) {
89762306a36Sopenharmony_ci			inw(hw->base_port);
89862306a36Sopenharmony_ci			i += 2;
89962306a36Sopenharmony_ci		}
90062306a36Sopenharmony_ci	}
90162306a36Sopenharmony_ci
90262306a36Sopenharmony_ci	acknowledge_data_read(hw);
90362306a36Sopenharmony_ci
90462306a36Sopenharmony_ci	swap_packet_bitfield_from_le(pkt);
90562306a36Sopenharmony_ci
90662306a36Sopenharmony_ci	if (ipwireless_debug)
90762306a36Sopenharmony_ci		dump_data_bytes("recv", pkt, len);
90862306a36Sopenharmony_ci
90962306a36Sopenharmony_ci	handle_received_packet(hw, (union nl_packet *) pkt, len);
91062306a36Sopenharmony_ci
91162306a36Sopenharmony_ci	end_read_timing(len);
91262306a36Sopenharmony_ci}
91362306a36Sopenharmony_ci
91462306a36Sopenharmony_cistatic int get_current_packet_priority(struct ipw_hardware *hw)
91562306a36Sopenharmony_ci{
91662306a36Sopenharmony_ci	/*
91762306a36Sopenharmony_ci	 * If we're initializing, don't send anything of higher priority than
91862306a36Sopenharmony_ci	 * PRIO_SETUP.  The network layer therefore need not care about
91962306a36Sopenharmony_ci	 * hardware initialization - any of its stuff will simply be queued
92062306a36Sopenharmony_ci	 * until setup is complete.
92162306a36Sopenharmony_ci	 */
92262306a36Sopenharmony_ci	return (hw->to_setup || hw->initializing
92362306a36Sopenharmony_ci			? PRIO_SETUP + 1 : NL_NUM_OF_PRIORITIES);
92462306a36Sopenharmony_ci}
92562306a36Sopenharmony_ci
92662306a36Sopenharmony_ci/*
92762306a36Sopenharmony_ci * return 1 if something has been received from hw
92862306a36Sopenharmony_ci */
92962306a36Sopenharmony_cistatic int get_packets_from_hw(struct ipw_hardware *hw)
93062306a36Sopenharmony_ci{
93162306a36Sopenharmony_ci	int received = 0;
93262306a36Sopenharmony_ci	unsigned long flags;
93362306a36Sopenharmony_ci
93462306a36Sopenharmony_ci	spin_lock_irqsave(&hw->lock, flags);
93562306a36Sopenharmony_ci	while (hw->rx_ready && !hw->blocking_rx) {
93662306a36Sopenharmony_ci		received = 1;
93762306a36Sopenharmony_ci		hw->rx_ready--;
93862306a36Sopenharmony_ci		spin_unlock_irqrestore(&hw->lock, flags);
93962306a36Sopenharmony_ci
94062306a36Sopenharmony_ci		do_receive_packet(hw);
94162306a36Sopenharmony_ci
94262306a36Sopenharmony_ci		spin_lock_irqsave(&hw->lock, flags);
94362306a36Sopenharmony_ci	}
94462306a36Sopenharmony_ci	spin_unlock_irqrestore(&hw->lock, flags);
94562306a36Sopenharmony_ci
94662306a36Sopenharmony_ci	return received;
94762306a36Sopenharmony_ci}
94862306a36Sopenharmony_ci
94962306a36Sopenharmony_ci/*
95062306a36Sopenharmony_ci * Send pending packet up to given priority, prioritize SETUP data until
95162306a36Sopenharmony_ci * hardware is fully setup.
95262306a36Sopenharmony_ci *
95362306a36Sopenharmony_ci * return 1 if more packets can be sent
95462306a36Sopenharmony_ci */
95562306a36Sopenharmony_cistatic int send_pending_packet(struct ipw_hardware *hw, int priority_limit)
95662306a36Sopenharmony_ci{
95762306a36Sopenharmony_ci	int more_to_send = 0;
95862306a36Sopenharmony_ci	unsigned long flags;
95962306a36Sopenharmony_ci
96062306a36Sopenharmony_ci	spin_lock_irqsave(&hw->lock, flags);
96162306a36Sopenharmony_ci	if (hw->tx_queued && hw->tx_ready) {
96262306a36Sopenharmony_ci		int priority;
96362306a36Sopenharmony_ci		struct ipw_tx_packet *packet = NULL;
96462306a36Sopenharmony_ci
96562306a36Sopenharmony_ci		/* Pick a packet */
96662306a36Sopenharmony_ci		for (priority = 0; priority < priority_limit; priority++) {
96762306a36Sopenharmony_ci			if (!list_empty(&hw->tx_queue[priority])) {
96862306a36Sopenharmony_ci				packet = list_first_entry(
96962306a36Sopenharmony_ci						&hw->tx_queue[priority],
97062306a36Sopenharmony_ci						struct ipw_tx_packet,
97162306a36Sopenharmony_ci						queue);
97262306a36Sopenharmony_ci
97362306a36Sopenharmony_ci				hw->tx_queued--;
97462306a36Sopenharmony_ci				list_del(&packet->queue);
97562306a36Sopenharmony_ci
97662306a36Sopenharmony_ci				break;
97762306a36Sopenharmony_ci			}
97862306a36Sopenharmony_ci		}
97962306a36Sopenharmony_ci		if (!packet) {
98062306a36Sopenharmony_ci			hw->tx_queued = 0;
98162306a36Sopenharmony_ci			spin_unlock_irqrestore(&hw->lock, flags);
98262306a36Sopenharmony_ci			return 0;
98362306a36Sopenharmony_ci		}
98462306a36Sopenharmony_ci
98562306a36Sopenharmony_ci		spin_unlock_irqrestore(&hw->lock, flags);
98662306a36Sopenharmony_ci
98762306a36Sopenharmony_ci		/* Send */
98862306a36Sopenharmony_ci		do_send_packet(hw, packet);
98962306a36Sopenharmony_ci
99062306a36Sopenharmony_ci		/* Check if more to send */
99162306a36Sopenharmony_ci		spin_lock_irqsave(&hw->lock, flags);
99262306a36Sopenharmony_ci		for (priority = 0; priority < priority_limit; priority++)
99362306a36Sopenharmony_ci			if (!list_empty(&hw->tx_queue[priority])) {
99462306a36Sopenharmony_ci				more_to_send = 1;
99562306a36Sopenharmony_ci				break;
99662306a36Sopenharmony_ci			}
99762306a36Sopenharmony_ci
99862306a36Sopenharmony_ci		if (!more_to_send)
99962306a36Sopenharmony_ci			hw->tx_queued = 0;
100062306a36Sopenharmony_ci	}
100162306a36Sopenharmony_ci	spin_unlock_irqrestore(&hw->lock, flags);
100262306a36Sopenharmony_ci
100362306a36Sopenharmony_ci	return more_to_send;
100462306a36Sopenharmony_ci}
100562306a36Sopenharmony_ci
100662306a36Sopenharmony_ci/*
100762306a36Sopenharmony_ci * Send and receive all queued packets.
100862306a36Sopenharmony_ci */
100962306a36Sopenharmony_cistatic void ipwireless_do_tasklet(struct tasklet_struct *t)
101062306a36Sopenharmony_ci{
101162306a36Sopenharmony_ci	struct ipw_hardware *hw = from_tasklet(hw, t, tasklet);
101262306a36Sopenharmony_ci	unsigned long flags;
101362306a36Sopenharmony_ci
101462306a36Sopenharmony_ci	spin_lock_irqsave(&hw->lock, flags);
101562306a36Sopenharmony_ci	if (hw->shutting_down) {
101662306a36Sopenharmony_ci		spin_unlock_irqrestore(&hw->lock, flags);
101762306a36Sopenharmony_ci		return;
101862306a36Sopenharmony_ci	}
101962306a36Sopenharmony_ci
102062306a36Sopenharmony_ci	if (hw->to_setup == 1) {
102162306a36Sopenharmony_ci		/*
102262306a36Sopenharmony_ci		 * Initial setup data sent to hardware
102362306a36Sopenharmony_ci		 */
102462306a36Sopenharmony_ci		hw->to_setup = 2;
102562306a36Sopenharmony_ci		spin_unlock_irqrestore(&hw->lock, flags);
102662306a36Sopenharmony_ci
102762306a36Sopenharmony_ci		ipw_setup_hardware(hw);
102862306a36Sopenharmony_ci		ipw_send_setup_packet(hw);
102962306a36Sopenharmony_ci
103062306a36Sopenharmony_ci		send_pending_packet(hw, PRIO_SETUP + 1);
103162306a36Sopenharmony_ci		get_packets_from_hw(hw);
103262306a36Sopenharmony_ci	} else {
103362306a36Sopenharmony_ci		int priority_limit = get_current_packet_priority(hw);
103462306a36Sopenharmony_ci		int again;
103562306a36Sopenharmony_ci
103662306a36Sopenharmony_ci		spin_unlock_irqrestore(&hw->lock, flags);
103762306a36Sopenharmony_ci
103862306a36Sopenharmony_ci		do {
103962306a36Sopenharmony_ci			again = send_pending_packet(hw, priority_limit);
104062306a36Sopenharmony_ci			again |= get_packets_from_hw(hw);
104162306a36Sopenharmony_ci		} while (again);
104262306a36Sopenharmony_ci	}
104362306a36Sopenharmony_ci}
104462306a36Sopenharmony_ci
104562306a36Sopenharmony_ci/*
104662306a36Sopenharmony_ci * return true if the card is physically present.
104762306a36Sopenharmony_ci */
104862306a36Sopenharmony_cistatic int is_card_present(struct ipw_hardware *hw)
104962306a36Sopenharmony_ci{
105062306a36Sopenharmony_ci	if (hw->hw_version == HW_VERSION_1)
105162306a36Sopenharmony_ci		return inw(hw->base_port + IOIR) != 0xFFFF;
105262306a36Sopenharmony_ci	else
105362306a36Sopenharmony_ci		return readl(&hw->memory_info_regs->memreg_card_present) ==
105462306a36Sopenharmony_ci		    CARD_PRESENT_VALUE;
105562306a36Sopenharmony_ci}
105662306a36Sopenharmony_ci
105762306a36Sopenharmony_cistatic irqreturn_t ipwireless_handle_v1_interrupt(int irq,
105862306a36Sopenharmony_ci						  struct ipw_hardware *hw)
105962306a36Sopenharmony_ci{
106062306a36Sopenharmony_ci	unsigned short irqn;
106162306a36Sopenharmony_ci
106262306a36Sopenharmony_ci	irqn = inw(hw->base_port + IOIR);
106362306a36Sopenharmony_ci
106462306a36Sopenharmony_ci	/* Check if card is present */
106562306a36Sopenharmony_ci	if (irqn == 0xFFFF)
106662306a36Sopenharmony_ci		return IRQ_NONE;
106762306a36Sopenharmony_ci	else if (irqn != 0) {
106862306a36Sopenharmony_ci		unsigned short ack = 0;
106962306a36Sopenharmony_ci		unsigned long flags;
107062306a36Sopenharmony_ci
107162306a36Sopenharmony_ci		/* Transmit complete. */
107262306a36Sopenharmony_ci		if (irqn & IR_TXINTR) {
107362306a36Sopenharmony_ci			ack |= IR_TXINTR;
107462306a36Sopenharmony_ci			spin_lock_irqsave(&hw->lock, flags);
107562306a36Sopenharmony_ci			hw->tx_ready = 1;
107662306a36Sopenharmony_ci			spin_unlock_irqrestore(&hw->lock, flags);
107762306a36Sopenharmony_ci		}
107862306a36Sopenharmony_ci		/* Received data */
107962306a36Sopenharmony_ci		if (irqn & IR_RXINTR) {
108062306a36Sopenharmony_ci			ack |= IR_RXINTR;
108162306a36Sopenharmony_ci			spin_lock_irqsave(&hw->lock, flags);
108262306a36Sopenharmony_ci			hw->rx_ready++;
108362306a36Sopenharmony_ci			spin_unlock_irqrestore(&hw->lock, flags);
108462306a36Sopenharmony_ci		}
108562306a36Sopenharmony_ci		if (ack != 0) {
108662306a36Sopenharmony_ci			outw(ack, hw->base_port + IOIR);
108762306a36Sopenharmony_ci			tasklet_schedule(&hw->tasklet);
108862306a36Sopenharmony_ci		}
108962306a36Sopenharmony_ci		return IRQ_HANDLED;
109062306a36Sopenharmony_ci	}
109162306a36Sopenharmony_ci	return IRQ_NONE;
109262306a36Sopenharmony_ci}
109362306a36Sopenharmony_ci
109462306a36Sopenharmony_cistatic void acknowledge_pcmcia_interrupt(struct ipw_hardware *hw)
109562306a36Sopenharmony_ci{
109662306a36Sopenharmony_ci	unsigned short csr = readw(&hw->memregs_CCR->reg_config_and_status);
109762306a36Sopenharmony_ci
109862306a36Sopenharmony_ci	csr &= 0xfffd;
109962306a36Sopenharmony_ci	writew(csr, &hw->memregs_CCR->reg_config_and_status);
110062306a36Sopenharmony_ci}
110162306a36Sopenharmony_ci
110262306a36Sopenharmony_cistatic irqreturn_t ipwireless_handle_v2_v3_interrupt(int irq,
110362306a36Sopenharmony_ci						     struct ipw_hardware *hw)
110462306a36Sopenharmony_ci{
110562306a36Sopenharmony_ci	int tx = 0;
110662306a36Sopenharmony_ci	int rx = 0;
110762306a36Sopenharmony_ci	int rx_repeat = 0;
110862306a36Sopenharmony_ci	int try_mem_tx_old;
110962306a36Sopenharmony_ci	unsigned long flags;
111062306a36Sopenharmony_ci
111162306a36Sopenharmony_ci	do {
111262306a36Sopenharmony_ci
111362306a36Sopenharmony_ci	unsigned short memtx = readw(hw->memreg_tx);
111462306a36Sopenharmony_ci	unsigned short memtx_serial;
111562306a36Sopenharmony_ci	unsigned short memrxdone =
111662306a36Sopenharmony_ci		readw(&hw->memory_info_regs->memreg_rx_done);
111762306a36Sopenharmony_ci
111862306a36Sopenharmony_ci	try_mem_tx_old = 0;
111962306a36Sopenharmony_ci
112062306a36Sopenharmony_ci	/* check whether the interrupt was generated by ipwireless card */
112162306a36Sopenharmony_ci	if (!(memtx & MEMTX_TX) && !(memrxdone & MEMRX_RX_DONE)) {
112262306a36Sopenharmony_ci
112362306a36Sopenharmony_ci		/* check if the card uses memreg_tx_old register */
112462306a36Sopenharmony_ci		if (hw->memreg_tx == &hw->memory_info_regs->memreg_tx_new) {
112562306a36Sopenharmony_ci			memtx = readw(&hw->memory_info_regs->memreg_tx_old);
112662306a36Sopenharmony_ci			if (memtx & MEMTX_TX) {
112762306a36Sopenharmony_ci				printk(KERN_INFO IPWIRELESS_PCCARD_NAME
112862306a36Sopenharmony_ci					": Using memreg_tx_old\n");
112962306a36Sopenharmony_ci				hw->memreg_tx =
113062306a36Sopenharmony_ci					&hw->memory_info_regs->memreg_tx_old;
113162306a36Sopenharmony_ci			} else {
113262306a36Sopenharmony_ci				return IRQ_NONE;
113362306a36Sopenharmony_ci			}
113462306a36Sopenharmony_ci		} else
113562306a36Sopenharmony_ci			return IRQ_NONE;
113662306a36Sopenharmony_ci	}
113762306a36Sopenharmony_ci
113862306a36Sopenharmony_ci	/*
113962306a36Sopenharmony_ci	 * See if the card is physically present. Note that while it is
114062306a36Sopenharmony_ci	 * powering up, it appears not to be present.
114162306a36Sopenharmony_ci	 */
114262306a36Sopenharmony_ci	if (!is_card_present(hw)) {
114362306a36Sopenharmony_ci		acknowledge_pcmcia_interrupt(hw);
114462306a36Sopenharmony_ci		return IRQ_HANDLED;
114562306a36Sopenharmony_ci	}
114662306a36Sopenharmony_ci
114762306a36Sopenharmony_ci	memtx_serial = memtx & (unsigned short) 0xff00;
114862306a36Sopenharmony_ci	if (memtx & MEMTX_TX) {
114962306a36Sopenharmony_ci		writew(memtx_serial, hw->memreg_tx);
115062306a36Sopenharmony_ci
115162306a36Sopenharmony_ci		if (hw->serial_number_detected) {
115262306a36Sopenharmony_ci			if (memtx_serial != hw->last_memtx_serial) {
115362306a36Sopenharmony_ci				hw->last_memtx_serial = memtx_serial;
115462306a36Sopenharmony_ci				spin_lock_irqsave(&hw->lock, flags);
115562306a36Sopenharmony_ci				hw->rx_ready++;
115662306a36Sopenharmony_ci				spin_unlock_irqrestore(&hw->lock, flags);
115762306a36Sopenharmony_ci				rx = 1;
115862306a36Sopenharmony_ci			} else
115962306a36Sopenharmony_ci				/* Ignore 'Timer Recovery' duplicates. */
116062306a36Sopenharmony_ci				rx_repeat = 1;
116162306a36Sopenharmony_ci		} else {
116262306a36Sopenharmony_ci			/*
116362306a36Sopenharmony_ci			 * If a non-zero serial number is seen, then enable
116462306a36Sopenharmony_ci			 * serial number checking.
116562306a36Sopenharmony_ci			 */
116662306a36Sopenharmony_ci			if (memtx_serial != 0) {
116762306a36Sopenharmony_ci				hw->serial_number_detected = 1;
116862306a36Sopenharmony_ci				printk(KERN_DEBUG IPWIRELESS_PCCARD_NAME
116962306a36Sopenharmony_ci					": memreg_tx serial num detected\n");
117062306a36Sopenharmony_ci
117162306a36Sopenharmony_ci				spin_lock_irqsave(&hw->lock, flags);
117262306a36Sopenharmony_ci				hw->rx_ready++;
117362306a36Sopenharmony_ci				spin_unlock_irqrestore(&hw->lock, flags);
117462306a36Sopenharmony_ci			}
117562306a36Sopenharmony_ci			rx = 1;
117662306a36Sopenharmony_ci		}
117762306a36Sopenharmony_ci	}
117862306a36Sopenharmony_ci	if (memrxdone & MEMRX_RX_DONE) {
117962306a36Sopenharmony_ci		writew(0, &hw->memory_info_regs->memreg_rx_done);
118062306a36Sopenharmony_ci		spin_lock_irqsave(&hw->lock, flags);
118162306a36Sopenharmony_ci		hw->tx_ready = 1;
118262306a36Sopenharmony_ci		spin_unlock_irqrestore(&hw->lock, flags);
118362306a36Sopenharmony_ci		tx = 1;
118462306a36Sopenharmony_ci	}
118562306a36Sopenharmony_ci	if (tx)
118662306a36Sopenharmony_ci		writew(MEMRX_PCINTACKK,
118762306a36Sopenharmony_ci				&hw->memory_info_regs->memreg_pc_interrupt_ack);
118862306a36Sopenharmony_ci
118962306a36Sopenharmony_ci	acknowledge_pcmcia_interrupt(hw);
119062306a36Sopenharmony_ci
119162306a36Sopenharmony_ci	if (tx || rx)
119262306a36Sopenharmony_ci		tasklet_schedule(&hw->tasklet);
119362306a36Sopenharmony_ci	else if (!rx_repeat) {
119462306a36Sopenharmony_ci		if (hw->memreg_tx == &hw->memory_info_regs->memreg_tx_new) {
119562306a36Sopenharmony_ci			if (hw->serial_number_detected)
119662306a36Sopenharmony_ci				printk(KERN_WARNING IPWIRELESS_PCCARD_NAME
119762306a36Sopenharmony_ci					": spurious interrupt - new_tx mode\n");
119862306a36Sopenharmony_ci			else {
119962306a36Sopenharmony_ci				printk(KERN_WARNING IPWIRELESS_PCCARD_NAME
120062306a36Sopenharmony_ci					": no valid memreg_tx value - switching to the old memreg_tx\n");
120162306a36Sopenharmony_ci				hw->memreg_tx =
120262306a36Sopenharmony_ci					&hw->memory_info_regs->memreg_tx_old;
120362306a36Sopenharmony_ci				try_mem_tx_old = 1;
120462306a36Sopenharmony_ci			}
120562306a36Sopenharmony_ci		} else
120662306a36Sopenharmony_ci			printk(KERN_WARNING IPWIRELESS_PCCARD_NAME
120762306a36Sopenharmony_ci					": spurious interrupt - old_tx mode\n");
120862306a36Sopenharmony_ci	}
120962306a36Sopenharmony_ci
121062306a36Sopenharmony_ci	} while (try_mem_tx_old == 1);
121162306a36Sopenharmony_ci
121262306a36Sopenharmony_ci	return IRQ_HANDLED;
121362306a36Sopenharmony_ci}
121462306a36Sopenharmony_ci
121562306a36Sopenharmony_ciirqreturn_t ipwireless_interrupt(int irq, void *dev_id)
121662306a36Sopenharmony_ci{
121762306a36Sopenharmony_ci	struct ipw_dev *ipw = dev_id;
121862306a36Sopenharmony_ci
121962306a36Sopenharmony_ci	if (ipw->hardware->hw_version == HW_VERSION_1)
122062306a36Sopenharmony_ci		return ipwireless_handle_v1_interrupt(irq, ipw->hardware);
122162306a36Sopenharmony_ci	else
122262306a36Sopenharmony_ci		return ipwireless_handle_v2_v3_interrupt(irq, ipw->hardware);
122362306a36Sopenharmony_ci}
122462306a36Sopenharmony_ci
122562306a36Sopenharmony_cistatic void flush_packets_to_hw(struct ipw_hardware *hw)
122662306a36Sopenharmony_ci{
122762306a36Sopenharmony_ci	int priority_limit;
122862306a36Sopenharmony_ci	unsigned long flags;
122962306a36Sopenharmony_ci
123062306a36Sopenharmony_ci	spin_lock_irqsave(&hw->lock, flags);
123162306a36Sopenharmony_ci	priority_limit = get_current_packet_priority(hw);
123262306a36Sopenharmony_ci	spin_unlock_irqrestore(&hw->lock, flags);
123362306a36Sopenharmony_ci
123462306a36Sopenharmony_ci	while (send_pending_packet(hw, priority_limit));
123562306a36Sopenharmony_ci}
123662306a36Sopenharmony_ci
123762306a36Sopenharmony_cistatic void send_packet(struct ipw_hardware *hw, int priority,
123862306a36Sopenharmony_ci			struct ipw_tx_packet *packet)
123962306a36Sopenharmony_ci{
124062306a36Sopenharmony_ci	unsigned long flags;
124162306a36Sopenharmony_ci
124262306a36Sopenharmony_ci	spin_lock_irqsave(&hw->lock, flags);
124362306a36Sopenharmony_ci	list_add_tail(&packet->queue, &hw->tx_queue[priority]);
124462306a36Sopenharmony_ci	hw->tx_queued++;
124562306a36Sopenharmony_ci	spin_unlock_irqrestore(&hw->lock, flags);
124662306a36Sopenharmony_ci
124762306a36Sopenharmony_ci	flush_packets_to_hw(hw);
124862306a36Sopenharmony_ci}
124962306a36Sopenharmony_ci
125062306a36Sopenharmony_ci/* Create data packet, non-atomic allocation */
125162306a36Sopenharmony_cistatic void *alloc_data_packet(int data_size,
125262306a36Sopenharmony_ci				unsigned char dest_addr,
125362306a36Sopenharmony_ci				unsigned char protocol)
125462306a36Sopenharmony_ci{
125562306a36Sopenharmony_ci	struct ipw_tx_packet *packet = kzalloc(
125662306a36Sopenharmony_ci			sizeof(struct ipw_tx_packet) + data_size,
125762306a36Sopenharmony_ci			GFP_ATOMIC);
125862306a36Sopenharmony_ci
125962306a36Sopenharmony_ci	if (!packet)
126062306a36Sopenharmony_ci		return NULL;
126162306a36Sopenharmony_ci
126262306a36Sopenharmony_ci	INIT_LIST_HEAD(&packet->queue);
126362306a36Sopenharmony_ci	packet->dest_addr = dest_addr;
126462306a36Sopenharmony_ci	packet->protocol = protocol;
126562306a36Sopenharmony_ci	packet->length = data_size;
126662306a36Sopenharmony_ci
126762306a36Sopenharmony_ci	return packet;
126862306a36Sopenharmony_ci}
126962306a36Sopenharmony_ci
127062306a36Sopenharmony_cistatic void *alloc_ctrl_packet(int header_size,
127162306a36Sopenharmony_ci			       unsigned char dest_addr,
127262306a36Sopenharmony_ci			       unsigned char protocol,
127362306a36Sopenharmony_ci			       unsigned char sig_no)
127462306a36Sopenharmony_ci{
127562306a36Sopenharmony_ci	/*
127662306a36Sopenharmony_ci	 * sig_no is located right after ipw_tx_packet struct in every
127762306a36Sopenharmony_ci	 * CTRL or SETUP packets, we can use ipw_control_packet as a
127862306a36Sopenharmony_ci	 * common struct
127962306a36Sopenharmony_ci	 */
128062306a36Sopenharmony_ci	struct ipw_control_packet *packet = kzalloc(header_size, GFP_ATOMIC);
128162306a36Sopenharmony_ci
128262306a36Sopenharmony_ci	if (!packet)
128362306a36Sopenharmony_ci		return NULL;
128462306a36Sopenharmony_ci
128562306a36Sopenharmony_ci	INIT_LIST_HEAD(&packet->header.queue);
128662306a36Sopenharmony_ci	packet->header.dest_addr = dest_addr;
128762306a36Sopenharmony_ci	packet->header.protocol = protocol;
128862306a36Sopenharmony_ci	packet->header.length = header_size - sizeof(struct ipw_tx_packet);
128962306a36Sopenharmony_ci	packet->body.sig_no = sig_no;
129062306a36Sopenharmony_ci
129162306a36Sopenharmony_ci	return packet;
129262306a36Sopenharmony_ci}
129362306a36Sopenharmony_ci
129462306a36Sopenharmony_ciint ipwireless_send_packet(struct ipw_hardware *hw, unsigned int channel_idx,
129562306a36Sopenharmony_ci			    const u8 *data, unsigned int length,
129662306a36Sopenharmony_ci			    void (*callback) (void *cb, unsigned int length),
129762306a36Sopenharmony_ci			    void *callback_data)
129862306a36Sopenharmony_ci{
129962306a36Sopenharmony_ci	struct ipw_tx_packet *packet;
130062306a36Sopenharmony_ci
130162306a36Sopenharmony_ci	packet = alloc_data_packet(length, (channel_idx + 1),
130262306a36Sopenharmony_ci			TL_PROTOCOLID_COM_DATA);
130362306a36Sopenharmony_ci	if (!packet)
130462306a36Sopenharmony_ci		return -ENOMEM;
130562306a36Sopenharmony_ci	packet->packet_callback = callback;
130662306a36Sopenharmony_ci	packet->callback_data = callback_data;
130762306a36Sopenharmony_ci	memcpy((unsigned char *) packet + sizeof(struct ipw_tx_packet), data,
130862306a36Sopenharmony_ci			length);
130962306a36Sopenharmony_ci
131062306a36Sopenharmony_ci	send_packet(hw, PRIO_DATA, packet);
131162306a36Sopenharmony_ci	return 0;
131262306a36Sopenharmony_ci}
131362306a36Sopenharmony_ci
131462306a36Sopenharmony_cistatic int set_control_line(struct ipw_hardware *hw, int prio,
131562306a36Sopenharmony_ci			   unsigned int channel_idx, int line, int state)
131662306a36Sopenharmony_ci{
131762306a36Sopenharmony_ci	struct ipw_control_packet *packet;
131862306a36Sopenharmony_ci	int protocolid = TL_PROTOCOLID_COM_CTRL;
131962306a36Sopenharmony_ci
132062306a36Sopenharmony_ci	if (prio == PRIO_SETUP)
132162306a36Sopenharmony_ci		protocolid = TL_PROTOCOLID_SETUP;
132262306a36Sopenharmony_ci
132362306a36Sopenharmony_ci	packet = alloc_ctrl_packet(sizeof(struct ipw_control_packet),
132462306a36Sopenharmony_ci			(channel_idx + 1), protocolid, line);
132562306a36Sopenharmony_ci	if (!packet)
132662306a36Sopenharmony_ci		return -ENOMEM;
132762306a36Sopenharmony_ci	packet->header.length = sizeof(struct ipw_control_packet_body);
132862306a36Sopenharmony_ci	packet->body.value = (state == 0 ? 0 : 1);
132962306a36Sopenharmony_ci	send_packet(hw, prio, &packet->header);
133062306a36Sopenharmony_ci	return 0;
133162306a36Sopenharmony_ci}
133262306a36Sopenharmony_ci
133362306a36Sopenharmony_ci
133462306a36Sopenharmony_cistatic int set_DTR(struct ipw_hardware *hw, int priority,
133562306a36Sopenharmony_ci		   unsigned int channel_idx, int state)
133662306a36Sopenharmony_ci{
133762306a36Sopenharmony_ci	if (state != 0)
133862306a36Sopenharmony_ci		hw->control_lines[channel_idx] |= IPW_CONTROL_LINE_DTR;
133962306a36Sopenharmony_ci	else
134062306a36Sopenharmony_ci		hw->control_lines[channel_idx] &= ~IPW_CONTROL_LINE_DTR;
134162306a36Sopenharmony_ci
134262306a36Sopenharmony_ci	return set_control_line(hw, priority, channel_idx, COMCTRL_DTR, state);
134362306a36Sopenharmony_ci}
134462306a36Sopenharmony_ci
134562306a36Sopenharmony_cistatic int set_RTS(struct ipw_hardware *hw, int priority,
134662306a36Sopenharmony_ci		   unsigned int channel_idx, int state)
134762306a36Sopenharmony_ci{
134862306a36Sopenharmony_ci	if (state != 0)
134962306a36Sopenharmony_ci		hw->control_lines[channel_idx] |= IPW_CONTROL_LINE_RTS;
135062306a36Sopenharmony_ci	else
135162306a36Sopenharmony_ci		hw->control_lines[channel_idx] &= ~IPW_CONTROL_LINE_RTS;
135262306a36Sopenharmony_ci
135362306a36Sopenharmony_ci	return set_control_line(hw, priority, channel_idx, COMCTRL_RTS, state);
135462306a36Sopenharmony_ci}
135562306a36Sopenharmony_ci
135662306a36Sopenharmony_ciint ipwireless_set_DTR(struct ipw_hardware *hw, unsigned int channel_idx,
135762306a36Sopenharmony_ci		       int state)
135862306a36Sopenharmony_ci{
135962306a36Sopenharmony_ci	return set_DTR(hw, PRIO_CTRL, channel_idx, state);
136062306a36Sopenharmony_ci}
136162306a36Sopenharmony_ci
136262306a36Sopenharmony_ciint ipwireless_set_RTS(struct ipw_hardware *hw, unsigned int channel_idx,
136362306a36Sopenharmony_ci		       int state)
136462306a36Sopenharmony_ci{
136562306a36Sopenharmony_ci	return set_RTS(hw, PRIO_CTRL, channel_idx, state);
136662306a36Sopenharmony_ci}
136762306a36Sopenharmony_ci
136862306a36Sopenharmony_cistruct ipw_setup_get_version_query_packet {
136962306a36Sopenharmony_ci	struct ipw_tx_packet header;
137062306a36Sopenharmony_ci	struct tl_setup_get_version_qry body;
137162306a36Sopenharmony_ci};
137262306a36Sopenharmony_ci
137362306a36Sopenharmony_cistruct ipw_setup_config_packet {
137462306a36Sopenharmony_ci	struct ipw_tx_packet header;
137562306a36Sopenharmony_ci	struct tl_setup_config_msg body;
137662306a36Sopenharmony_ci};
137762306a36Sopenharmony_ci
137862306a36Sopenharmony_cistruct ipw_setup_config_done_packet {
137962306a36Sopenharmony_ci	struct ipw_tx_packet header;
138062306a36Sopenharmony_ci	struct tl_setup_config_done_msg body;
138162306a36Sopenharmony_ci};
138262306a36Sopenharmony_ci
138362306a36Sopenharmony_cistruct ipw_setup_open_packet {
138462306a36Sopenharmony_ci	struct ipw_tx_packet header;
138562306a36Sopenharmony_ci	struct tl_setup_open_msg body;
138662306a36Sopenharmony_ci};
138762306a36Sopenharmony_ci
138862306a36Sopenharmony_cistruct ipw_setup_info_packet {
138962306a36Sopenharmony_ci	struct ipw_tx_packet header;
139062306a36Sopenharmony_ci	struct tl_setup_info_msg body;
139162306a36Sopenharmony_ci};
139262306a36Sopenharmony_ci
139362306a36Sopenharmony_cistruct ipw_setup_reboot_msg_ack {
139462306a36Sopenharmony_ci	struct ipw_tx_packet header;
139562306a36Sopenharmony_ci	struct TlSetupRebootMsgAck body;
139662306a36Sopenharmony_ci};
139762306a36Sopenharmony_ci
139862306a36Sopenharmony_ci/* This handles the actual initialization of the card */
139962306a36Sopenharmony_cistatic void __handle_setup_get_version_rsp(struct ipw_hardware *hw)
140062306a36Sopenharmony_ci{
140162306a36Sopenharmony_ci	struct ipw_setup_config_packet *config_packet;
140262306a36Sopenharmony_ci	struct ipw_setup_config_done_packet *config_done_packet;
140362306a36Sopenharmony_ci	struct ipw_setup_open_packet *open_packet;
140462306a36Sopenharmony_ci	struct ipw_setup_info_packet *info_packet;
140562306a36Sopenharmony_ci	int port;
140662306a36Sopenharmony_ci	unsigned int channel_idx;
140762306a36Sopenharmony_ci
140862306a36Sopenharmony_ci	/* generate config packet */
140962306a36Sopenharmony_ci	for (port = 1; port <= NL_NUM_OF_ADDRESSES; port++) {
141062306a36Sopenharmony_ci		config_packet = alloc_ctrl_packet(
141162306a36Sopenharmony_ci				sizeof(struct ipw_setup_config_packet),
141262306a36Sopenharmony_ci				ADDR_SETUP_PROT,
141362306a36Sopenharmony_ci				TL_PROTOCOLID_SETUP,
141462306a36Sopenharmony_ci				TL_SETUP_SIGNO_CONFIG_MSG);
141562306a36Sopenharmony_ci		if (!config_packet)
141662306a36Sopenharmony_ci			goto exit_nomem;
141762306a36Sopenharmony_ci		config_packet->header.length = sizeof(struct tl_setup_config_msg);
141862306a36Sopenharmony_ci		config_packet->body.port_no = port;
141962306a36Sopenharmony_ci		config_packet->body.prio_data = PRIO_DATA;
142062306a36Sopenharmony_ci		config_packet->body.prio_ctrl = PRIO_CTRL;
142162306a36Sopenharmony_ci		send_packet(hw, PRIO_SETUP, &config_packet->header);
142262306a36Sopenharmony_ci	}
142362306a36Sopenharmony_ci	config_done_packet = alloc_ctrl_packet(
142462306a36Sopenharmony_ci			sizeof(struct ipw_setup_config_done_packet),
142562306a36Sopenharmony_ci			ADDR_SETUP_PROT,
142662306a36Sopenharmony_ci			TL_PROTOCOLID_SETUP,
142762306a36Sopenharmony_ci			TL_SETUP_SIGNO_CONFIG_DONE_MSG);
142862306a36Sopenharmony_ci	if (!config_done_packet)
142962306a36Sopenharmony_ci		goto exit_nomem;
143062306a36Sopenharmony_ci	config_done_packet->header.length = sizeof(struct tl_setup_config_done_msg);
143162306a36Sopenharmony_ci	send_packet(hw, PRIO_SETUP, &config_done_packet->header);
143262306a36Sopenharmony_ci
143362306a36Sopenharmony_ci	/* generate open packet */
143462306a36Sopenharmony_ci	for (port = 1; port <= NL_NUM_OF_ADDRESSES; port++) {
143562306a36Sopenharmony_ci		open_packet = alloc_ctrl_packet(
143662306a36Sopenharmony_ci				sizeof(struct ipw_setup_open_packet),
143762306a36Sopenharmony_ci				ADDR_SETUP_PROT,
143862306a36Sopenharmony_ci				TL_PROTOCOLID_SETUP,
143962306a36Sopenharmony_ci				TL_SETUP_SIGNO_OPEN_MSG);
144062306a36Sopenharmony_ci		if (!open_packet)
144162306a36Sopenharmony_ci			goto exit_nomem;
144262306a36Sopenharmony_ci		open_packet->header.length = sizeof(struct tl_setup_open_msg);
144362306a36Sopenharmony_ci		open_packet->body.port_no = port;
144462306a36Sopenharmony_ci		send_packet(hw, PRIO_SETUP, &open_packet->header);
144562306a36Sopenharmony_ci	}
144662306a36Sopenharmony_ci	for (channel_idx = 0;
144762306a36Sopenharmony_ci			channel_idx < NL_NUM_OF_ADDRESSES; channel_idx++) {
144862306a36Sopenharmony_ci		int ret;
144962306a36Sopenharmony_ci
145062306a36Sopenharmony_ci		ret = set_DTR(hw, PRIO_SETUP, channel_idx,
145162306a36Sopenharmony_ci			(hw->control_lines[channel_idx] &
145262306a36Sopenharmony_ci			 IPW_CONTROL_LINE_DTR) != 0);
145362306a36Sopenharmony_ci		if (ret) {
145462306a36Sopenharmony_ci			printk(KERN_ERR IPWIRELESS_PCCARD_NAME
145562306a36Sopenharmony_ci					": error setting DTR (%d)\n", ret);
145662306a36Sopenharmony_ci			return;
145762306a36Sopenharmony_ci		}
145862306a36Sopenharmony_ci
145962306a36Sopenharmony_ci		ret = set_RTS(hw, PRIO_SETUP, channel_idx,
146062306a36Sopenharmony_ci			(hw->control_lines [channel_idx] &
146162306a36Sopenharmony_ci			 IPW_CONTROL_LINE_RTS) != 0);
146262306a36Sopenharmony_ci		if (ret) {
146362306a36Sopenharmony_ci			printk(KERN_ERR IPWIRELESS_PCCARD_NAME
146462306a36Sopenharmony_ci					": error setting RTS (%d)\n", ret);
146562306a36Sopenharmony_ci			return;
146662306a36Sopenharmony_ci		}
146762306a36Sopenharmony_ci	}
146862306a36Sopenharmony_ci	/*
146962306a36Sopenharmony_ci	 * For NDIS we assume that we are using sync PPP frames, for COM async.
147062306a36Sopenharmony_ci	 * This driver uses NDIS mode too. We don't bother with translation
147162306a36Sopenharmony_ci	 * from async -> sync PPP.
147262306a36Sopenharmony_ci	 */
147362306a36Sopenharmony_ci	info_packet = alloc_ctrl_packet(sizeof(struct ipw_setup_info_packet),
147462306a36Sopenharmony_ci			ADDR_SETUP_PROT,
147562306a36Sopenharmony_ci			TL_PROTOCOLID_SETUP,
147662306a36Sopenharmony_ci			TL_SETUP_SIGNO_INFO_MSG);
147762306a36Sopenharmony_ci	if (!info_packet)
147862306a36Sopenharmony_ci		goto exit_nomem;
147962306a36Sopenharmony_ci	info_packet->header.length = sizeof(struct tl_setup_info_msg);
148062306a36Sopenharmony_ci	info_packet->body.driver_type = NDISWAN_DRIVER;
148162306a36Sopenharmony_ci	info_packet->body.major_version = NDISWAN_DRIVER_MAJOR_VERSION;
148262306a36Sopenharmony_ci	info_packet->body.minor_version = NDISWAN_DRIVER_MINOR_VERSION;
148362306a36Sopenharmony_ci	send_packet(hw, PRIO_SETUP, &info_packet->header);
148462306a36Sopenharmony_ci
148562306a36Sopenharmony_ci	/* Initialization is now complete, so we clear the 'to_setup' flag */
148662306a36Sopenharmony_ci	hw->to_setup = 0;
148762306a36Sopenharmony_ci
148862306a36Sopenharmony_ci	return;
148962306a36Sopenharmony_ci
149062306a36Sopenharmony_ciexit_nomem:
149162306a36Sopenharmony_ci	printk(KERN_ERR IPWIRELESS_PCCARD_NAME
149262306a36Sopenharmony_ci			": not enough memory to alloc control packet\n");
149362306a36Sopenharmony_ci	hw->to_setup = -1;
149462306a36Sopenharmony_ci}
149562306a36Sopenharmony_ci
149662306a36Sopenharmony_cistatic void handle_setup_get_version_rsp(struct ipw_hardware *hw,
149762306a36Sopenharmony_ci		unsigned char vers_no)
149862306a36Sopenharmony_ci{
149962306a36Sopenharmony_ci	del_timer(&hw->setup_timer);
150062306a36Sopenharmony_ci	hw->initializing = 0;
150162306a36Sopenharmony_ci	printk(KERN_INFO IPWIRELESS_PCCARD_NAME ": card is ready.\n");
150262306a36Sopenharmony_ci
150362306a36Sopenharmony_ci	if (vers_no == TL_SETUP_VERSION)
150462306a36Sopenharmony_ci		__handle_setup_get_version_rsp(hw);
150562306a36Sopenharmony_ci	else
150662306a36Sopenharmony_ci		printk(KERN_ERR IPWIRELESS_PCCARD_NAME
150762306a36Sopenharmony_ci				": invalid hardware version no %u\n",
150862306a36Sopenharmony_ci				(unsigned int) vers_no);
150962306a36Sopenharmony_ci}
151062306a36Sopenharmony_ci
151162306a36Sopenharmony_cistatic void ipw_send_setup_packet(struct ipw_hardware *hw)
151262306a36Sopenharmony_ci{
151362306a36Sopenharmony_ci	struct ipw_setup_get_version_query_packet *ver_packet;
151462306a36Sopenharmony_ci
151562306a36Sopenharmony_ci	ver_packet = alloc_ctrl_packet(
151662306a36Sopenharmony_ci			sizeof(struct ipw_setup_get_version_query_packet),
151762306a36Sopenharmony_ci			ADDR_SETUP_PROT, TL_PROTOCOLID_SETUP,
151862306a36Sopenharmony_ci			TL_SETUP_SIGNO_GET_VERSION_QRY);
151962306a36Sopenharmony_ci	if (!ver_packet)
152062306a36Sopenharmony_ci		return;
152162306a36Sopenharmony_ci	ver_packet->header.length = sizeof(struct tl_setup_get_version_qry);
152262306a36Sopenharmony_ci
152362306a36Sopenharmony_ci	/*
152462306a36Sopenharmony_ci	 * Response is handled in handle_received_SETUP_packet
152562306a36Sopenharmony_ci	 */
152662306a36Sopenharmony_ci	send_packet(hw, PRIO_SETUP, &ver_packet->header);
152762306a36Sopenharmony_ci}
152862306a36Sopenharmony_ci
152962306a36Sopenharmony_cistatic void handle_received_SETUP_packet(struct ipw_hardware *hw,
153062306a36Sopenharmony_ci					 unsigned int address,
153162306a36Sopenharmony_ci					 const unsigned char *data, int len,
153262306a36Sopenharmony_ci					 int is_last)
153362306a36Sopenharmony_ci{
153462306a36Sopenharmony_ci	const union ipw_setup_rx_msg *rx_msg = (const union ipw_setup_rx_msg *) data;
153562306a36Sopenharmony_ci
153662306a36Sopenharmony_ci	if (address != ADDR_SETUP_PROT) {
153762306a36Sopenharmony_ci		printk(KERN_INFO IPWIRELESS_PCCARD_NAME
153862306a36Sopenharmony_ci		       ": setup packet has bad address %d\n", address);
153962306a36Sopenharmony_ci		return;
154062306a36Sopenharmony_ci	}
154162306a36Sopenharmony_ci
154262306a36Sopenharmony_ci	switch (rx_msg->sig_no) {
154362306a36Sopenharmony_ci	case TL_SETUP_SIGNO_GET_VERSION_RSP:
154462306a36Sopenharmony_ci		if (hw->to_setup)
154562306a36Sopenharmony_ci			handle_setup_get_version_rsp(hw,
154662306a36Sopenharmony_ci					rx_msg->version_rsp_msg.version);
154762306a36Sopenharmony_ci		break;
154862306a36Sopenharmony_ci
154962306a36Sopenharmony_ci	case TL_SETUP_SIGNO_OPEN_MSG:
155062306a36Sopenharmony_ci		if (ipwireless_debug) {
155162306a36Sopenharmony_ci			unsigned int channel_idx = rx_msg->open_msg.port_no - 1;
155262306a36Sopenharmony_ci
155362306a36Sopenharmony_ci			printk(KERN_INFO IPWIRELESS_PCCARD_NAME
155462306a36Sopenharmony_ci			       ": OPEN_MSG [channel %u] reply received\n",
155562306a36Sopenharmony_ci			       channel_idx);
155662306a36Sopenharmony_ci		}
155762306a36Sopenharmony_ci		break;
155862306a36Sopenharmony_ci
155962306a36Sopenharmony_ci	case TL_SETUP_SIGNO_INFO_MSG_ACK:
156062306a36Sopenharmony_ci		if (ipwireless_debug)
156162306a36Sopenharmony_ci			printk(KERN_DEBUG IPWIRELESS_PCCARD_NAME
156262306a36Sopenharmony_ci			       ": card successfully configured as NDISWAN\n");
156362306a36Sopenharmony_ci		break;
156462306a36Sopenharmony_ci
156562306a36Sopenharmony_ci	case TL_SETUP_SIGNO_REBOOT_MSG:
156662306a36Sopenharmony_ci		if (hw->to_setup)
156762306a36Sopenharmony_ci			printk(KERN_DEBUG IPWIRELESS_PCCARD_NAME
156862306a36Sopenharmony_ci			       ": Setup not completed - ignoring reboot msg\n");
156962306a36Sopenharmony_ci		else {
157062306a36Sopenharmony_ci			struct ipw_setup_reboot_msg_ack *packet;
157162306a36Sopenharmony_ci
157262306a36Sopenharmony_ci			printk(KERN_DEBUG IPWIRELESS_PCCARD_NAME
157362306a36Sopenharmony_ci			       ": Acknowledging REBOOT message\n");
157462306a36Sopenharmony_ci			packet = alloc_ctrl_packet(
157562306a36Sopenharmony_ci					sizeof(struct ipw_setup_reboot_msg_ack),
157662306a36Sopenharmony_ci					ADDR_SETUP_PROT, TL_PROTOCOLID_SETUP,
157762306a36Sopenharmony_ci					TL_SETUP_SIGNO_REBOOT_MSG_ACK);
157862306a36Sopenharmony_ci			if (!packet) {
157962306a36Sopenharmony_ci				pr_err(IPWIRELESS_PCCARD_NAME
158062306a36Sopenharmony_ci				       ": Not enough memory to send reboot packet");
158162306a36Sopenharmony_ci				break;
158262306a36Sopenharmony_ci			}
158362306a36Sopenharmony_ci			packet->header.length =
158462306a36Sopenharmony_ci				sizeof(struct TlSetupRebootMsgAck);
158562306a36Sopenharmony_ci			send_packet(hw, PRIO_SETUP, &packet->header);
158662306a36Sopenharmony_ci			if (hw->reboot_callback)
158762306a36Sopenharmony_ci				hw->reboot_callback(hw->reboot_callback_data);
158862306a36Sopenharmony_ci		}
158962306a36Sopenharmony_ci		break;
159062306a36Sopenharmony_ci
159162306a36Sopenharmony_ci	default:
159262306a36Sopenharmony_ci		printk(KERN_INFO IPWIRELESS_PCCARD_NAME
159362306a36Sopenharmony_ci		       ": unknown setup message %u received\n",
159462306a36Sopenharmony_ci		       (unsigned int) rx_msg->sig_no);
159562306a36Sopenharmony_ci	}
159662306a36Sopenharmony_ci}
159762306a36Sopenharmony_ci
159862306a36Sopenharmony_cistatic void do_close_hardware(struct ipw_hardware *hw)
159962306a36Sopenharmony_ci{
160062306a36Sopenharmony_ci	unsigned int irqn;
160162306a36Sopenharmony_ci
160262306a36Sopenharmony_ci	if (hw->hw_version == HW_VERSION_1) {
160362306a36Sopenharmony_ci		/* Disable TX and RX interrupts. */
160462306a36Sopenharmony_ci		outw(0, hw->base_port + IOIER);
160562306a36Sopenharmony_ci
160662306a36Sopenharmony_ci		/* Acknowledge any outstanding interrupt requests */
160762306a36Sopenharmony_ci		irqn = inw(hw->base_port + IOIR);
160862306a36Sopenharmony_ci		if (irqn & IR_TXINTR)
160962306a36Sopenharmony_ci			outw(IR_TXINTR, hw->base_port + IOIR);
161062306a36Sopenharmony_ci		if (irqn & IR_RXINTR)
161162306a36Sopenharmony_ci			outw(IR_RXINTR, hw->base_port + IOIR);
161262306a36Sopenharmony_ci
161362306a36Sopenharmony_ci		synchronize_irq(hw->irq);
161462306a36Sopenharmony_ci	}
161562306a36Sopenharmony_ci}
161662306a36Sopenharmony_ci
161762306a36Sopenharmony_cistruct ipw_hardware *ipwireless_hardware_create(void)
161862306a36Sopenharmony_ci{
161962306a36Sopenharmony_ci	int i;
162062306a36Sopenharmony_ci	struct ipw_hardware *hw =
162162306a36Sopenharmony_ci		kzalloc(sizeof(struct ipw_hardware), GFP_KERNEL);
162262306a36Sopenharmony_ci
162362306a36Sopenharmony_ci	if (!hw)
162462306a36Sopenharmony_ci		return NULL;
162562306a36Sopenharmony_ci
162662306a36Sopenharmony_ci	hw->irq = -1;
162762306a36Sopenharmony_ci	hw->initializing = 1;
162862306a36Sopenharmony_ci	hw->tx_ready = 1;
162962306a36Sopenharmony_ci	hw->rx_bytes_queued = 0;
163062306a36Sopenharmony_ci	hw->rx_pool_size = 0;
163162306a36Sopenharmony_ci	hw->last_memtx_serial = (unsigned short) 0xffff;
163262306a36Sopenharmony_ci	for (i = 0; i < NL_NUM_OF_PRIORITIES; i++)
163362306a36Sopenharmony_ci		INIT_LIST_HEAD(&hw->tx_queue[i]);
163462306a36Sopenharmony_ci
163562306a36Sopenharmony_ci	INIT_LIST_HEAD(&hw->rx_queue);
163662306a36Sopenharmony_ci	INIT_LIST_HEAD(&hw->rx_pool);
163762306a36Sopenharmony_ci	spin_lock_init(&hw->lock);
163862306a36Sopenharmony_ci	tasklet_setup(&hw->tasklet, ipwireless_do_tasklet);
163962306a36Sopenharmony_ci	INIT_WORK(&hw->work_rx, ipw_receive_data_work);
164062306a36Sopenharmony_ci	timer_setup(&hw->setup_timer, ipwireless_setup_timer, 0);
164162306a36Sopenharmony_ci
164262306a36Sopenharmony_ci	return hw;
164362306a36Sopenharmony_ci}
164462306a36Sopenharmony_ci
164562306a36Sopenharmony_civoid ipwireless_init_hardware_v1(struct ipw_hardware *hw,
164662306a36Sopenharmony_ci		unsigned int base_port,
164762306a36Sopenharmony_ci		void __iomem *attr_memory,
164862306a36Sopenharmony_ci		void __iomem *common_memory,
164962306a36Sopenharmony_ci		int is_v2_card,
165062306a36Sopenharmony_ci		void (*reboot_callback) (void *data),
165162306a36Sopenharmony_ci		void *reboot_callback_data)
165262306a36Sopenharmony_ci{
165362306a36Sopenharmony_ci	if (hw->removed) {
165462306a36Sopenharmony_ci		hw->removed = 0;
165562306a36Sopenharmony_ci		enable_irq(hw->irq);
165662306a36Sopenharmony_ci	}
165762306a36Sopenharmony_ci	hw->base_port = base_port;
165862306a36Sopenharmony_ci	hw->hw_version = (is_v2_card ? HW_VERSION_2 : HW_VERSION_1);
165962306a36Sopenharmony_ci	hw->ll_mtu = (hw->hw_version == HW_VERSION_1 ? LL_MTU_V1 : LL_MTU_V2);
166062306a36Sopenharmony_ci	hw->memregs_CCR = (struct MEMCCR __iomem *)
166162306a36Sopenharmony_ci			((unsigned short __iomem *) attr_memory + 0x200);
166262306a36Sopenharmony_ci	hw->memory_info_regs = (struct MEMINFREG __iomem *) common_memory;
166362306a36Sopenharmony_ci	hw->memreg_tx = &hw->memory_info_regs->memreg_tx_new;
166462306a36Sopenharmony_ci	hw->reboot_callback = reboot_callback;
166562306a36Sopenharmony_ci	hw->reboot_callback_data = reboot_callback_data;
166662306a36Sopenharmony_ci}
166762306a36Sopenharmony_ci
166862306a36Sopenharmony_civoid ipwireless_init_hardware_v2_v3(struct ipw_hardware *hw)
166962306a36Sopenharmony_ci{
167062306a36Sopenharmony_ci	hw->initializing = 1;
167162306a36Sopenharmony_ci	hw->init_loops = 0;
167262306a36Sopenharmony_ci	printk(KERN_INFO IPWIRELESS_PCCARD_NAME
167362306a36Sopenharmony_ci	       ": waiting for card to start up...\n");
167462306a36Sopenharmony_ci	ipwireless_setup_timer(&hw->setup_timer);
167562306a36Sopenharmony_ci}
167662306a36Sopenharmony_ci
167762306a36Sopenharmony_cistatic void ipwireless_setup_timer(struct timer_list *t)
167862306a36Sopenharmony_ci{
167962306a36Sopenharmony_ci	struct ipw_hardware *hw = from_timer(hw, t, setup_timer);
168062306a36Sopenharmony_ci
168162306a36Sopenharmony_ci	hw->init_loops++;
168262306a36Sopenharmony_ci
168362306a36Sopenharmony_ci	if (hw->init_loops == TL_SETUP_MAX_VERSION_QRY &&
168462306a36Sopenharmony_ci			hw->hw_version == HW_VERSION_2 &&
168562306a36Sopenharmony_ci			hw->memreg_tx == &hw->memory_info_regs->memreg_tx_new) {
168662306a36Sopenharmony_ci		printk(KERN_INFO IPWIRELESS_PCCARD_NAME
168762306a36Sopenharmony_ci				": failed to startup using TX2, trying TX\n");
168862306a36Sopenharmony_ci
168962306a36Sopenharmony_ci		hw->memreg_tx = &hw->memory_info_regs->memreg_tx_old;
169062306a36Sopenharmony_ci		hw->init_loops = 0;
169162306a36Sopenharmony_ci	}
169262306a36Sopenharmony_ci	/* Give up after a certain number of retries */
169362306a36Sopenharmony_ci	if (hw->init_loops == TL_SETUP_MAX_VERSION_QRY) {
169462306a36Sopenharmony_ci		printk(KERN_INFO IPWIRELESS_PCCARD_NAME
169562306a36Sopenharmony_ci		       ": card failed to start up!\n");
169662306a36Sopenharmony_ci		hw->initializing = 0;
169762306a36Sopenharmony_ci	} else {
169862306a36Sopenharmony_ci		/* Do not attempt to write to the board if it is not present. */
169962306a36Sopenharmony_ci		if (is_card_present(hw)) {
170062306a36Sopenharmony_ci			unsigned long flags;
170162306a36Sopenharmony_ci
170262306a36Sopenharmony_ci			spin_lock_irqsave(&hw->lock, flags);
170362306a36Sopenharmony_ci			hw->to_setup = 1;
170462306a36Sopenharmony_ci			hw->tx_ready = 1;
170562306a36Sopenharmony_ci			spin_unlock_irqrestore(&hw->lock, flags);
170662306a36Sopenharmony_ci			tasklet_schedule(&hw->tasklet);
170762306a36Sopenharmony_ci		}
170862306a36Sopenharmony_ci
170962306a36Sopenharmony_ci		mod_timer(&hw->setup_timer,
171062306a36Sopenharmony_ci			jiffies + msecs_to_jiffies(TL_SETUP_VERSION_QRY_TMO));
171162306a36Sopenharmony_ci	}
171262306a36Sopenharmony_ci}
171362306a36Sopenharmony_ci
171462306a36Sopenharmony_ci/*
171562306a36Sopenharmony_ci * Stop any interrupts from executing so that, once this function returns,
171662306a36Sopenharmony_ci * other layers of the driver can be sure they won't get any more callbacks.
171762306a36Sopenharmony_ci * Thus must be called on a proper process context.
171862306a36Sopenharmony_ci */
171962306a36Sopenharmony_civoid ipwireless_stop_interrupts(struct ipw_hardware *hw)
172062306a36Sopenharmony_ci{
172162306a36Sopenharmony_ci	if (!hw->shutting_down) {
172262306a36Sopenharmony_ci		/* Tell everyone we are going down. */
172362306a36Sopenharmony_ci		hw->shutting_down = 1;
172462306a36Sopenharmony_ci		del_timer(&hw->setup_timer);
172562306a36Sopenharmony_ci
172662306a36Sopenharmony_ci		/* Prevent the hardware from sending any more interrupts */
172762306a36Sopenharmony_ci		do_close_hardware(hw);
172862306a36Sopenharmony_ci	}
172962306a36Sopenharmony_ci}
173062306a36Sopenharmony_ci
173162306a36Sopenharmony_civoid ipwireless_hardware_free(struct ipw_hardware *hw)
173262306a36Sopenharmony_ci{
173362306a36Sopenharmony_ci	int i;
173462306a36Sopenharmony_ci	struct ipw_rx_packet *rp, *rq;
173562306a36Sopenharmony_ci	struct ipw_tx_packet *tp, *tq;
173662306a36Sopenharmony_ci
173762306a36Sopenharmony_ci	ipwireless_stop_interrupts(hw);
173862306a36Sopenharmony_ci
173962306a36Sopenharmony_ci	flush_work(&hw->work_rx);
174062306a36Sopenharmony_ci
174162306a36Sopenharmony_ci	for (i = 0; i < NL_NUM_OF_ADDRESSES; i++)
174262306a36Sopenharmony_ci		kfree(hw->packet_assembler[i]);
174362306a36Sopenharmony_ci
174462306a36Sopenharmony_ci	for (i = 0; i < NL_NUM_OF_PRIORITIES; i++)
174562306a36Sopenharmony_ci		list_for_each_entry_safe(tp, tq, &hw->tx_queue[i], queue) {
174662306a36Sopenharmony_ci			list_del(&tp->queue);
174762306a36Sopenharmony_ci			kfree(tp);
174862306a36Sopenharmony_ci		}
174962306a36Sopenharmony_ci
175062306a36Sopenharmony_ci	list_for_each_entry_safe(rp, rq, &hw->rx_queue, queue) {
175162306a36Sopenharmony_ci		list_del(&rp->queue);
175262306a36Sopenharmony_ci		kfree(rp);
175362306a36Sopenharmony_ci	}
175462306a36Sopenharmony_ci
175562306a36Sopenharmony_ci	list_for_each_entry_safe(rp, rq, &hw->rx_pool, queue) {
175662306a36Sopenharmony_ci		list_del(&rp->queue);
175762306a36Sopenharmony_ci		kfree(rp);
175862306a36Sopenharmony_ci	}
175962306a36Sopenharmony_ci	kfree(hw);
176062306a36Sopenharmony_ci}
176162306a36Sopenharmony_ci
176262306a36Sopenharmony_ci/*
176362306a36Sopenharmony_ci * Associate the specified network with this hardware, so it will receive events
176462306a36Sopenharmony_ci * from it.
176562306a36Sopenharmony_ci */
176662306a36Sopenharmony_civoid ipwireless_associate_network(struct ipw_hardware *hw,
176762306a36Sopenharmony_ci				  struct ipw_network *network)
176862306a36Sopenharmony_ci{
176962306a36Sopenharmony_ci	hw->network = network;
177062306a36Sopenharmony_ci}
1771