/* * 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. Load more
/* * 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; } } #else static 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. */