1763 lines · 45.3 KB · C
RawDownload
/* * IPWireless 3G PCMCIA Network Driver * * Original code *   by Stephen Blackheath <stephen@blacksapphire.com>, *      Ben Martel <benm@symmetric.co.nz> * * Copyrighted as follows: *   Copyright (C) 2004 by Symmetric Systems Ltd (NZ) * * Various driver changes and rewrites, port to new kernels *   Copyright (C) 2006-2007 Jiri Kosina * * Misc code cleanups and updates *   Copyright (C) 2007 David Sterba */ #include <linux/interrupt.h>#include <linux/io.h>#include <linux/irq.h>#include <linux/kernel.h>#include <linux/list.h>#include <linux/slab.h> #include "hardware.h"#include "setup_protocol.h"#include "network.h"#include "main.h" static void ipw_send_setup_packet(struct ipw_hardware *hw);static void handle_received_SETUP_packet(struct ipw_hardware *ipw,					 unsigned int address,					 const unsigned char *data, int len,					 int is_last);static void ipwireless_setup_timer(unsigned long data);static void handle_received_CTRL_packet(struct ipw_hardware *hw,		unsigned int channel_idx, const unsigned char *data, int len); /*#define TIMING_DIAGNOSTICS*/ #ifdef TIMING_DIAGNOSTICS static struct timing_stats {	unsigned long last_report_time;	unsigned long read_time;	unsigned long write_time;	unsigned long read_bytes;	unsigned long write_bytes;	unsigned long start_time;}; static void start_timing(void){	timing_stats.start_time = jiffies;} static void end_read_timing(unsigned length){	timing_stats.read_time += (jiffies - start_time);	timing_stats.read_bytes += length + 2;	report_timing();} static void end_write_timing(unsigned length){	timing_stats.write_time += (jiffies - start_time);	timing_stats.write_bytes += length + 2;	report_timing();} static void report_timing(void){	unsigned long since = jiffies - timing_stats.last_report_time; 	/* If it's been more than one second... */	if (since >= HZ) {		int first = (timing_stats.last_report_time == 0); 		timing_stats.last_report_time = jiffies;		if (!first)			printk(KERN_INFO IPWIRELESS_PCCARD_NAME			       ": %u us elapsed - read %lu bytes in %u us, wrote %lu bytes in %u us\n",			       jiffies_to_usecs(since),			       timing_stats.read_bytes,			       jiffies_to_usecs(timing_stats.read_time),			       timing_stats.write_bytes,			       jiffies_to_usecs(timing_stats.write_time)); 		timing_stats.read_time = 0;		timing_stats.write_time = 0;		timing_stats.read_bytes = 0;		timing_stats.write_bytes = 0;	}}#elsestatic void start_timing(void) { }static void end_read_timing(unsigned length) { }static void end_write_timing(unsigned length) { }#endif /* Imported IPW definitions */ #define LL_MTU_V1 318#define LL_MTU_V2 250#define LL_MTU_MAX (LL_MTU_V1 > LL_MTU_V2 ? LL_MTU_V1 : LL_MTU_V2) #define PRIO_DATA  2#define PRIO_CTRL  1#define PRIO_SETUP 0 /* Addresses */#define ADDR_SETUP_PROT 0 /* Protocol ids */enum {	/* Identifier for the Com Data protocol */	TL_PROTOCOLID_COM_DATA = 0, 	/* Identifier for the Com Control protocol */	TL_PROTOCOLID_COM_CTRL = 1, 	/* Identifier for the Setup protocol */	TL_PROTOCOLID_SETUP = 2}; /* Number of bytes in NL packet header (cannot do * sizeof(nl_packet_header) since it's a bitfield) */#define NL_FIRST_PACKET_HEADER_SIZE        3 /* Number of bytes in NL packet header (cannot do * sizeof(nl_packet_header) since it's a bitfield) */#define NL_FOLLOWING_PACKET_HEADER_SIZE    1 struct nl_first_packet_header {	unsigned char protocol:3;	unsigned char address:3;	unsigned char packet_rank:2;	unsigned char length_lsb;	unsigned char length_msb;}; struct nl_packet_header {	unsigned char protocol:3;	unsigned char address:3;	unsigned char packet_rank:2;}; /* Value of 'packet_rank' above */#define NL_INTERMEDIATE_PACKET    0x0#define NL_LAST_PACKET            0x1#define NL_FIRST_PACKET           0x2 union nl_packet {	/* Network packet header of the first packet (a special case) */	struct nl_first_packet_header hdr_first;	/* Network packet header of the following packets (if any) */	struct nl_packet_header hdr;	/* Complete network packet (header + data) */	unsigned char rawpkt[LL_MTU_MAX];} __attribute__ ((__packed__)); #define HW_VERSION_UNKNOWN -1#define HW_VERSION_1 1#define HW_VERSION_2 2 /* IPW I/O ports */#define IOIER 0x00		/* Interrupt Enable Register */#define IOIR  0x02		/* Interrupt Source/ACK register */#define IODCR 0x04		/* Data Control Register */#define IODRR 0x06		/* Data Read Register */#define IODWR 0x08		/* Data Write Register */#define IOESR 0x0A		/* Embedded Driver Status Register */#define IORXR 0x0C		/* Rx Fifo Register (Host to Embedded) */#define IOTXR 0x0E		/* Tx Fifo Register (Embedded to Host) */ /* I/O ports and bit definitions for version 1 of the hardware */ /* IER bits*/#define IER_RXENABLED   0x1#define IER_TXENABLED   0x2 /* ISR bits */#define IR_RXINTR       0x1#define IR_TXINTR       0x2 /* DCR bits */#define DCR_RXDONE      0x1#define DCR_TXDONE      0x2#define DCR_RXRESET     0x4#define DCR_TXRESET     0x8 /* I/O ports and bit definitions for version 2 of the hardware */ struct MEMCCR {	unsigned short reg_config_option;	/* PCCOR: Configuration Option Register */	unsigned short reg_config_and_status;	/* PCCSR: Configuration and Status Register */	unsigned short reg_pin_replacement;	/* PCPRR: Pin Replacemant Register */	unsigned short reg_socket_and_copy;	/* PCSCR: Socket and Copy Register */	unsigned short reg_ext_status;		/* PCESR: Extendend Status Register */	unsigned short reg_io_base;		/* PCIOB: I/O Base Register */}; struct MEMINFREG {	unsigned short memreg_tx_old;	/* TX Register (R/W) */	unsigned short pad1;	unsigned short memreg_rx_done;	/* RXDone Register (R/W) */	unsigned short pad2;	unsigned short memreg_rx;	/* RX Register (R/W) */	unsigned short pad3;	unsigned short memreg_pc_interrupt_ack;	/* PC intr Ack Register (W) */	unsigned short pad4;	unsigned long memreg_card_present;/* Mask for Host to check (R) for					   * CARD_PRESENT_VALUE */	unsigned short memreg_tx_new;	/* TX2 (new) Register (R/W) */}; #define CARD_PRESENT_VALUE (0xBEEFCAFEUL) #define MEMTX_TX                       0x0001#define MEMRX_RX                       0x0001#define MEMRX_RX_DONE                  0x0001#define MEMRX_PCINTACKK                0x0001 #define NL_NUM_OF_PRIORITIES       3#define NL_NUM_OF_PROTOCOLS        3#define NL_NUM_OF_ADDRESSES        NO_OF_IPW_CHANNELS struct ipw_hardware {	unsigned int base_port;	short hw_version;	unsigned short ll_mtu;	spinlock_t lock; 	int initializing;	int init_loops;	struct timer_list setup_timer; 	/* Flag if hw is ready to send next packet */	int tx_ready;	/* Count of pending packets to be sent */	int tx_queued;	struct list_head tx_queue[NL_NUM_OF_PRIORITIES]; 	int rx_bytes_queued;	struct list_head rx_queue;	/* Pool of rx_packet structures that are not currently used. */	struct list_head rx_pool;	int rx_pool_size;	/* True if reception of data is blocked while userspace processes it. */	int blocking_rx;	/* True if there is RX data ready on the hardware. */	int rx_ready;	unsigned short last_memtx_serial;	/*	 * Newer versions of the V2 card firmware send serial numbers in the	 * MemTX register. 'serial_number_detected' is set true when we detect	 * a non-zero serial number (indicating the new firmware).  Thereafter,	 * the driver can safely ignore the Timer Recovery re-sends to avoid	 * out-of-sync problems.	 */	int serial_number_detected;	struct work_struct work_rx; 	/* True if we are to send the set-up data to the hardware. */	int to_setup; 	/* Card has been removed */	int removed;	/* Saved irq value when we disable the interrupt. */	int irq;	/* True if this driver is shutting down. */	int shutting_down;	/* Modem control lines */	unsigned int control_lines[NL_NUM_OF_ADDRESSES];	struct ipw_rx_packet *packet_assembler[NL_NUM_OF_ADDRESSES]; 	struct tasklet_struct tasklet; 	/* The handle for the network layer, for the sending of events to it. */	struct ipw_network *network;	struct MEMINFREG __iomem *memory_info_regs;	struct MEMCCR __iomem *memregs_CCR;	void (*reboot_callback) (void *data);	void *reboot_callback_data; 	unsigned short __iomem *memreg_tx;}; /* * Packet info structure for tx packets. * Note: not all the fields defined here are required for all protocols */struct ipw_tx_packet {	struct list_head queue;	/* channel idx + 1 */	unsigned char dest_addr;	/* SETUP, CTRL or DATA */	unsigned char protocol;	/* Length of data block, which starts at the end of this structure */	unsigned short length;	/* Sending state */	/* Offset of where we've sent up to so far */	unsigned long offset;	/* Count of packet fragments, starting at 0 */	int fragment_count; 	/* Called after packet is sent and before is freed */	void (*packet_callback) (void *cb_data, unsigned int packet_length);	void *callback_data;}; /* Signals from DTE */#define COMCTRL_RTS	0#define COMCTRL_DTR	1 /* Signals from DCE */#define COMCTRL_CTS	2#define COMCTRL_DCD	3#define COMCTRL_DSR	4#define COMCTRL_RI	5 struct ipw_control_packet_body {	/* DTE signal or DCE signal */	unsigned char sig_no;	/* 0: set signal, 1: clear signal */	unsigned char value;} __attribute__ ((__packed__)); struct ipw_control_packet {	struct ipw_tx_packet header;	struct ipw_control_packet_body body;}; struct ipw_rx_packet {	struct list_head queue;	unsigned int capacity;	unsigned int length;	unsigned int protocol;	unsigned int channel_idx;}; static char *data_type(const unsigned char *buf, unsigned length){	struct nl_packet_header *hdr = (struct nl_packet_header *) buf; 	if (length == 0)		return "     "; 	if (hdr->packet_rank & NL_FIRST_PACKET) {		switch (hdr->protocol) {		case TL_PROTOCOLID_COM_DATA:	return "DATA ";		case TL_PROTOCOLID_COM_CTRL:	return "CTRL ";		case TL_PROTOCOLID_SETUP:	return "SETUP";		default: return "???? ";		}	} else		return "     ";} #define DUMP_MAX_BYTES 64 static void dump_data_bytes(const char *type, const unsigned char *data,			    unsigned length){	char prefix[56]; 	sprintf(prefix, IPWIRELESS_PCCARD_NAME ": %s %s ",			type, data_type(data, length));	print_hex_dump_bytes(prefix, 0, (void *)data,			length < DUMP_MAX_BYTES ? length : DUMP_MAX_BYTES);} static void swap_packet_bitfield_to_le(unsigned char *data){#ifdef __BIG_ENDIAN_BITFIELD	unsigned char tmp = *data, ret = 0; 	/*	 * transform bits from aa.bbb.ccc to ccc.bbb.aa	 */	ret |= tmp & 0xc0 >> 6;	ret |= tmp & 0x38 >> 1;	ret |= tmp & 0x07 << 5;	*data = ret & 0xff;#endif} static void swap_packet_bitfield_from_le(unsigned char *data){#ifdef __BIG_ENDIAN_BITFIELD	unsigned char tmp = *data, ret = 0; 	/*	 * transform bits from ccc.bbb.aa to aa.bbb.ccc	 */	ret |= tmp & 0xe0 >> 5;	ret |= tmp & 0x1c << 1;	ret |= tmp & 0x03 << 6;	*data = ret & 0xff;#endif} static void do_send_fragment(struct ipw_hardware *hw, unsigned char *data,			    unsigned length){	unsigned i;	unsigned long flags; 	start_timing();	BUG_ON(length > hw->ll_mtu); 	if (ipwireless_debug)		dump_data_bytes("send", data, length); 	spin_lock_irqsave(&hw->lock, flags); 	hw->tx_ready = 0;	swap_packet_bitfield_to_le(data); 	if (hw->hw_version == HW_VERSION_1) {		outw((unsigned short) length, hw->base_port + IODWR); 		for (i = 0; i < length; i += 2) {			unsigned short d = data[i];			__le16 raw_data; 			if (i + 1 < length)				d |= data[i + 1] << 8;			raw_data = cpu_to_le16(d);			outw(raw_data, hw->base_port + IODWR);		} 		outw(DCR_TXDONE, hw->base_port + IODCR);	} else if (hw->hw_version == HW_VERSION_2) {		outw((unsigned short) length, hw->base_port); 		for (i = 0; i < length; i += 2) {			unsigned short d = data[i];			__le16 raw_data; 			if (i + 1 < length)				d |= data[i + 1] << 8;			raw_data = cpu_to_le16(d);			outw(raw_data, hw->base_port);		}		while ((i & 3) != 2) {			outw((unsigned short) 0xDEAD, hw->base_port);			i += 2;		}		writew(MEMRX_RX, &hw->memory_info_regs->memreg_rx);	} 	spin_unlock_irqrestore(&hw->lock, flags); 	end_write_timing(length);} static void do_send_packet(struct ipw_hardware *hw, struct ipw_tx_packet *packet){	unsigned short fragment_data_len;	unsigned short data_left = packet->length - packet->offset;	unsigned short header_size;	union nl_packet pkt; 	header_size =	    (packet->fragment_count == 0)	    ? NL_FIRST_PACKET_HEADER_SIZE	    : NL_FOLLOWING_PACKET_HEADER_SIZE;	fragment_data_len = hw->ll_mtu - header_size;	if (data_left < fragment_data_len)		fragment_data_len = data_left; 	/*	 * hdr_first is now in machine bitfield order, which will be swapped	 * to le just before it goes to hw	 */	pkt.hdr_first.protocol = packet->protocol;	pkt.hdr_first.address = packet->dest_addr;	pkt.hdr_first.packet_rank = 0; 	/* First packet? */	if (packet->fragment_count == 0) {		pkt.hdr_first.packet_rank |= NL_FIRST_PACKET;		pkt.hdr_first.length_lsb = (unsigned char) packet->length;		pkt.hdr_first.length_msb =			(unsigned char) (packet->length >> 8);	} 	memcpy(pkt.rawpkt + header_size,	       ((unsigned char *) packet) + sizeof(struct ipw_tx_packet) +	       packet->offset, fragment_data_len);	packet->offset += fragment_data_len;	packet->fragment_count++; 	/* Last packet? (May also be first packet.) */	if (packet->offset == packet->length)		pkt.hdr_first.packet_rank |= NL_LAST_PACKET;	do_send_fragment(hw, pkt.rawpkt, header_size + fragment_data_len); 	/* If this packet has unsent data, then re-queue it. */

Lines 1–500 of 1763.