// SPDX-License-Identifier: GPL-2.0 /* * 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
*/
/* 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) */ unsignedchar rawpkt[LL_MTU_MAX];
} __attribute__ ((__packed__));
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; unsignedshort 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 */ unsignedint 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;
unsignedshort __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 */ unsignedchar dest_addr; /* SETUP, CTRL or DATA */ unsignedchar protocol; /* Length of data block, which starts at the end of this structure */ unsignedshort length; /* Sending state */ /* Offset of where we've sent up to so far */ unsignedlong 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, unsignedint packet_length); void *callback_data;
};
struct ipw_control_packet_body { /* DTE signal or DCE signal */ unsignedchar sig_no; /* 0: set signal, 1: clear signal */ unsignedchar value;
} __attribute__ ((__packed__));
/* * 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;
/* 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. */ if (packet->offset < packet->length) { /* * Re-queue it at the head of the highest priority queue so * it goes before all other packets
*/ unsignedlong flags;
/* Enable TX and RX interrupts. */
outw(IER_TXENABLED | IER_RXENABLED, hw->base_port + IOIER);
} else { /* * Set INTRACK bit (bit 0), which means we must explicitly * acknowledge interrupts by clearing bit 2 of reg_config_and_status.
*/ unsignedshort csr = readw(&hw->memregs_CCR->reg_config_and_status);
/* * If 'packet' is NULL, then this function allocates a new packet, setting its * length to 0 and ensuring it has the specified minimum amount of free space. * * If 'packet' is not NULL, then this function enlarges it if it doesn't * have the specified minimum amount of free space. *
*/ staticstruct ipw_rx_packet *pool_allocate(struct ipw_hardware *hw, struct ipw_rx_packet *packet, int minimum_free_space)
{
/* Discard packet if channel index is out of range. */ if (channel_idx >= NL_NUM_OF_ADDRESSES) {
printk(KERN_INFO IPWIRELESS_PCCARD_NAME ": data packet has bad address %u\n", address); return;
}
/* * ->packet_assembler is safe to touch unlocked, this is the only place
*/ if (protocol == TL_PROTOCOLID_COM_DATA) { struct ipw_rx_packet **assem =
&hw->packet_assembler[channel_idx];
/* * Create a new packet, or assembler already contains one * enlarge it by 'length' bytes.
*/
(*assem) = pool_allocate(hw, *assem, length); if (!(*assem)) {
printk(KERN_ERR IPWIRELESS_PCCARD_NAME ": no memory for incoming data packet, dropped!\n"); return;
}
(*assem)->protocol = protocol;
(*assem)->channel_idx = channel_idx;
/* Append this packet data onto existing data. */
memcpy((unsignedchar *)(*assem) + sizeof(struct ipw_rx_packet)
+ (*assem)->length, data, length);
(*assem)->length += length; if (is_last) {
packet = *assem;
*assem = NULL; /* Count queued DATA bytes only */
spin_lock_irqsave(&hw->lock, flags);
hw->rx_bytes_queued += packet->length;
spin_unlock_irqrestore(&hw->lock, flags);
}
} else { /* If it's a CTRL packet, don't assemble, just queue it. */
packet = pool_allocate(hw, NULL, length); if (!packet) {
printk(KERN_ERR IPWIRELESS_PCCARD_NAME ": no memory for incoming ctrl packet, dropped!\n"); return;
}
packet->protocol = protocol;
packet->channel_idx = channel_idx;
memcpy((unsignedchar *)packet + sizeof(struct ipw_rx_packet),
data, length);
packet->length = length;
}
/* * If this is the last packet, then send the assembled packet on to the * network layer.
*/ if (packet) {
spin_lock_irqsave(&hw->lock, flags);
list_add_tail(&packet->queue, &hw->rx_queue); /* Block reception of incoming packets if queue is full. */
hw->blocking_rx =
(hw->rx_bytes_queued >= IPWIRELESS_RX_QUEUE_SIZE);
if (hw->shutting_down) break;
list_del(&packet->queue);
/* * Note: ipwireless_network_packet_received must be called in a * process context (i.e. via schedule_work) because the tty * output code can sleep in the tty_flip_buffer_push call.
*/ if (packet->protocol == TL_PROTOCOLID_COM_DATA) { if (hw->network != NULL) { /* If the network hasn't been disconnected. */
spin_unlock_irqrestore(&hw->lock, flags); /* * This must run unlocked due to tty processing * and mutex locking
*/
ipwireless_network_packet_received(
hw->network,
packet->channel_idx,
(unsignedchar *)packet
+ sizeof(struct ipw_rx_packet),
packet->length);
spin_lock_irqsave(&hw->lock, flags);
} /* Count queued DATA bytes only */
hw->rx_bytes_queued -= packet->length;
} else { /* * This is safe to be called locked, callchain does * not block
*/
handle_received_CTRL_packet(hw, packet->channel_idx,
(unsignedchar *)packet
+ sizeof(struct ipw_rx_packet),
packet->length);
}
pool_free(hw, packet); /* * Unblock reception of incoming packets if queue is no longer * full.
*/
hw->blocking_rx =
hw->rx_bytes_queued >= IPWIRELESS_RX_QUEUE_SIZE; if (hw->shutting_down) break;
}
spin_unlock_irqrestore(&hw->lock, flags);
}
/* * Retrieve a packet from the IPW hardware.
*/ staticvoid do_receive_packet(struct ipw_hardware *hw)
{ unsigned len; unsigned i; unsignedchar pkt[LL_MTU_MAX];
start_timing();
if (hw->hw_version == HW_VERSION_1) {
len = inw(hw->base_port + IODRR); if (len > hw->ll_mtu) {
printk(KERN_INFO IPWIRELESS_PCCARD_NAME ": received a packet of %u bytes - longer than the MTU!\n", len);
outw(DCR_RXDONE | DCR_RXRESET, hw->base_port + IODCR); return;
}
for (i = 0; i < len; i += 2) {
__le16 raw_data = inw(hw->base_port + IODRR); unsignedshort data = le16_to_cpu(raw_data);
pkt[i] = (unsignedchar) data;
pkt[i + 1] = (unsignedchar) (data >> 8);
}
} else {
len = inw(hw->base_port); if (len > hw->ll_mtu) {
printk(KERN_INFO IPWIRELESS_PCCARD_NAME ": received a packet of %u bytes - longer than the MTU!\n", len);
writew(MEMRX_PCINTACKK,
&hw->memory_info_regs->memreg_pc_interrupt_ack); return;
}
for (i = 0; i < len; i += 2) {
__le16 raw_data = inw(hw->base_port); unsignedshort data = le16_to_cpu(raw_data);
staticint get_current_packet_priority(struct ipw_hardware *hw)
{ /* * If we're initializing, don't send anything of higher priority than * PRIO_SETUP. The network layer therefore need not care about * hardware initialization - any of its stuff will simply be queued * until setup is complete.
*/ return (hw->to_setup || hw->initializing
? PRIO_SETUP + 1 : NL_NUM_OF_PRIORITIES);
}
/* * return 1 if something has been received from hw
*/ staticint get_packets_from_hw(struct ipw_hardware *hw)
{ int received = 0; unsignedlong flags;
spin_lock_irqsave(&hw->lock, flags); while (hw->rx_ready && !hw->blocking_rx) {
received = 1;
hw->rx_ready--;
spin_unlock_irqrestore(&hw->lock, flags);
/* * Send pending packet up to given priority, prioritize SETUP data until * hardware is fully setup. * * return 1 if more packets can be sent
*/ staticint send_pending_packet(struct ipw_hardware *hw, int priority_limit)
{ int more_to_send = 0; unsignedlong flags;
spin_lock_irqsave(&hw->lock, flags); if (hw->tx_queued && hw->tx_ready) { int priority; struct ipw_tx_packet *packet = NULL;
/* Pick a packet */ for (priority = 0; priority < priority_limit; priority++) { if (!list_empty(&hw->tx_queue[priority])) {
packet = list_first_entry(
&hw->tx_queue[priority], struct ipw_tx_packet,
queue);
/* Check if more to send */
spin_lock_irqsave(&hw->lock, flags); for (priority = 0; priority < priority_limit; priority++) if (!list_empty(&hw->tx_queue[priority])) {
more_to_send = 1; break;
}
if (!more_to_send)
hw->tx_queued = 0;
}
spin_unlock_irqrestore(&hw->lock, flags);
/* check whether the interrupt was generated by ipwireless card */ if (!(memtx & MEMTX_TX) && !(memrxdone & MEMRX_RX_DONE)) {
/* check if the card uses memreg_tx_old register */ if (hw->memreg_tx == &hw->memory_info_regs->memreg_tx_new) {
memtx = readw(&hw->memory_info_regs->memreg_tx_old); if (memtx & MEMTX_TX) {
printk(KERN_INFO IPWIRELESS_PCCARD_NAME ": Using memreg_tx_old\n");
hw->memreg_tx =
&hw->memory_info_regs->memreg_tx_old;
} else { return IRQ_NONE;
}
} else return IRQ_NONE;
}
/* * See if the card is physically present. Note that while it is * powering up, it appears not to be present.
*/ if (!is_card_present(hw)) {
acknowledge_pcmcia_interrupt(hw); return IRQ_HANDLED;
}
if (hw->serial_number_detected) { if (memtx_serial != hw->last_memtx_serial) {
hw->last_memtx_serial = memtx_serial;
spin_lock_irqsave(&hw->lock, flags);
hw->rx_ready++;
spin_unlock_irqrestore(&hw->lock, flags);
rx = 1;
} else /* Ignore 'Timer Recovery' duplicates. */
rx_repeat = 1;
} else { /* * If a non-zero serial number is seen, then enable * serial number checking.
*/ if (memtx_serial != 0) {
hw->serial_number_detected = 1;
printk(KERN_DEBUG IPWIRELESS_PCCARD_NAME ": memreg_tx serial num detected\n");
staticvoid *alloc_ctrl_packet(int header_size, unsignedchar dest_addr, unsignedchar protocol, unsignedchar sig_no)
{ /* * sig_no is located right after ipw_tx_packet struct in every * CTRL or SETUP packets, we can use ipw_control_packet as a * common struct
*/ struct ipw_control_packet *packet = kzalloc(header_size, GFP_ATOMIC);
staticint set_control_line(struct ipw_hardware *hw, int prio, unsignedint channel_idx, int line, int state)
{ struct ipw_control_packet *packet; int protocolid = TL_PROTOCOLID_COM_CTRL;
if (prio == PRIO_SETUP)
protocolid = TL_PROTOCOLID_SETUP;
if (address != ADDR_SETUP_PROT) {
printk(KERN_INFO IPWIRELESS_PCCARD_NAME ": setup packet has bad address %d\n", address); return;
}
switch (rx_msg->sig_no) { case TL_SETUP_SIGNO_GET_VERSION_RSP: if (hw->to_setup)
handle_setup_get_version_rsp(hw,
rx_msg->version_rsp_msg.version); break;
case TL_SETUP_SIGNO_OPEN_MSG: if (ipwireless_debug) { unsignedint channel_idx = rx_msg->open_msg.port_no - 1;
case TL_SETUP_SIGNO_INFO_MSG_ACK: if (ipwireless_debug)
printk(KERN_DEBUG IPWIRELESS_PCCARD_NAME ": card successfully configured as NDISWAN\n"); break;
case TL_SETUP_SIGNO_REBOOT_MSG: if (hw->to_setup)
printk(KERN_DEBUG IPWIRELESS_PCCARD_NAME ": Setup not completed - ignoring reboot msg\n"); else { struct ipw_setup_reboot_msg_ack *packet;
printk(KERN_DEBUG IPWIRELESS_PCCARD_NAME ": Acknowledging REBOOT message\n");
packet = alloc_ctrl_packet( sizeof(struct ipw_setup_reboot_msg_ack),
ADDR_SETUP_PROT, TL_PROTOCOLID_SETUP,
TL_SETUP_SIGNO_REBOOT_MSG_ACK); if (!packet) {
pr_err(IPWIRELESS_PCCARD_NAME ": Not enough memory to send reboot packet"); break;
}
packet->header.length = sizeof(struct TlSetupRebootMsgAck);
send_packet(hw, PRIO_SETUP, &packet->header); if (hw->reboot_callback)
hw->reboot_callback(hw->reboot_callback_data);
} break;
if (hw->init_loops == TL_SETUP_MAX_VERSION_QRY &&
hw->hw_version == HW_VERSION_2 &&
hw->memreg_tx == &hw->memory_info_regs->memreg_tx_new) {
printk(KERN_INFO IPWIRELESS_PCCARD_NAME ": failed to startup using TX2, trying TX\n");
hw->memreg_tx = &hw->memory_info_regs->memreg_tx_old;
hw->init_loops = 0;
} /* Give up after a certain number of retries */ if (hw->init_loops == TL_SETUP_MAX_VERSION_QRY) {
printk(KERN_INFO IPWIRELESS_PCCARD_NAME ": card failed to start up!\n");
hw->initializing = 0;
} else { /* Do not attempt to write to the board if it is not present. */ if (is_card_present(hw)) { unsignedlong flags;
/* * Stop any interrupts from executing so that, once this function returns, * other layers of the driver can be sure they won't get any more callbacks. * Thus must be called on a proper process context.
*/ void ipwireless_stop_interrupts(struct ipw_hardware *hw)
{ if (!hw->shutting_down) { /* Tell everyone we are going down. */
hw->shutting_down = 1;
timer_delete(&hw->setup_timer);
/* Prevent the hardware from sending any more interrupts */
do_close_hardware(hw);
}
}
/* * Associate the specified network with this hardware, so it will receive events * from it.
*/ void ipwireless_associate_network(struct ipw_hardware *hw, struct ipw_network *network)
{
hw->network = network;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.