// 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
*/
struct ipw_network { /* Hardware context, used for calls to hardware layer. */ struct ipw_hardware *hardware; /* Context for kernel 'generic_ppp' functionality */ struct ppp_channel *ppp_channel; /* tty context connected with IPW console */ struct ipw_tty *associated_ttys[NO_OF_IPW_CHANNELS][MAX_ASSOCIATED_TTYS]; /* True if ppp needs waking up once we're ready to xmit */ int ppp_blocked; /* Number of packets queued up in hardware module. */ int outgoing_packets_queued; /* Spinlock to avoid interrupts during shutdown */
spinlock_t lock; struct mutex close_lock;
/* PPP ioctl data, not actually used anywere */ unsignedint flags; unsignedint rbits;
u32 xaccm[8];
u32 raccm; int mru;
/* * Called by the ppp system when it has a packet to send to the hardware.
*/ staticint ipwireless_ppp_start_xmit(struct ppp_channel *ppp_channel, struct sk_buff *skb)
{ struct ipw_network *network = ppp_channel->private; unsignedlong flags;
/* * If we have the requested amount of headroom in the skb we * were handed, then we can add the header efficiently.
*/ if (skb_headroom(skb) >= 2) {
memcpy(skb_push(skb, 2), header, 2);
ret = ipwireless_send_packet(network->hardware,
IPW_CHANNEL_RAS, skb->data,
skb->len,
notify_packet_sent,
network); if (ret < 0) {
skb_pull(skb, 2); return 0;
}
} else { /* Otherwise (rarely) we do it inefficiently. */
buf = kmalloc(skb->len + 2, GFP_ATOMIC); if (!buf) return 0;
memcpy(buf + 2, skb->data, skb->len);
memcpy(buf, header, 2);
ret = ipwireless_send_packet(network->hardware,
IPW_CHANNEL_RAS, buf,
skb->len + 2,
notify_packet_sent,
network);
kfree(buf); if (ret < 0) return 0;
}
kfree_skb(skb); return 1;
} else { /* * Otherwise reject the packet, and flag that the ppp system * needs to be unblocked once we are ready to send.
*/
network->ppp_blocked = 1;
spin_unlock_irqrestore(&network->lock, flags); if (ipwireless_debug)
printk(KERN_DEBUG IPWIRELESS_PCCARD_NAME ": ppp blocked\n"); return 0;
}
}
/* Handle an ioctl call that has come in via ppp. (copy of ppp_async_ioctl() */ staticint ipwireless_ppp_ioctl(struct ppp_channel *ppp_channel, unsignedint cmd, unsignedlong arg)
{ struct ipw_network *network = ppp_channel->private; int err, val;
u32 accm[8]; int __user *user_arg = (int __user *) arg;
err = -EFAULT; switch (cmd) { case PPPIOCGFLAGS:
val = network->flags | network->rbits; if (put_user(val, user_arg)) break;
err = 0; break;
case PPPIOCSFLAGS: if (get_user(val, user_arg)) break;
network->flags = val & ~SC_RCV_BITS;
network->rbits = val & SC_RCV_BITS;
err = 0; break;
case PPPIOCGASYNCMAP: if (put_user(network->xaccm[0], user_arg)) break;
err = 0; break;
case PPPIOCSASYNCMAP: if (get_user(network->xaccm[0], user_arg)) break;
err = 0; break;
case PPPIOCGRASYNCMAP: if (put_user(network->raccm, user_arg)) break;
err = 0; break;
case PPPIOCSRASYNCMAP: if (get_user(network->raccm, user_arg)) break;
err = 0; break;
case PPPIOCGXASYNCMAP: if (copy_to_user((void __user *) arg, network->xaccm, sizeof(network->xaccm))) break;
err = 0; break;
if (channel_idx == IPW_CHANNEL_RAS)
network->ras_control_lines = control_lines;
for (i = 0; i < MAX_ASSOCIATED_TTYS; i++) { struct ipw_tty *tty =
network->associated_ttys[channel_idx][i];
/* * If it's associated with a tty (other than the RAS channel * when we're online), then send the data to that tty. The RAS * channel's data is handled above - it always goes through * ppp_generic.
*/ if (tty)
ipwireless_tty_notify_control_line_change(tty,
channel_idx,
control_lines,
changed_mask);
}
}
/* * Some versions of firmware stuff packets with 0xff 0x03 (PPP: ALLSTATIONS, UI) * bytes, which are required on sent packet, but not always present on received * packets
*/ staticstruct sk_buff *ipw_packet_received_skb(unsignedchar *data, unsignedint length)
{ struct sk_buff *skb;
if (length > 2 && data[0] == PPP_ALLSTATIONS && data[1] == PPP_UI) {
length -= 2;
data += 2;
}
for (i = 0; i < MAX_ASSOCIATED_TTYS; i++) { struct ipw_tty *tty = network->associated_ttys[channel_idx][i];
if (!tty) continue;
/* * If it's associated with a tty (other than the RAS channel * when we're online), then send the data to that tty. The RAS * channel's data is handled above - it always goes through * ppp_generic.
*/ if (channel_idx == IPW_CHANNEL_RAS
&& (network->ras_control_lines &
IPW_CONTROL_LINE_DCD) != 0
&& ipwireless_tty_is_modem(tty)) { /* * If data came in on the RAS channel and this tty is * the modem tty, and we are online, then we send it to * the PPP layer.
*/
mutex_lock(&network->close_lock);
spin_lock_irqsave(&network->lock, flags); if (network->ppp_channel != NULL) { struct sk_buff *skb;
spin_unlock_irqrestore(&network->lock,
flags);
/* Send the data to the ppp_generic module. */
skb = ipw_packet_received_skb(data, length); if (skb)
ppp_input(network->ppp_channel, skb);
} else
spin_unlock_irqrestore(&network->lock,
flags);
mutex_unlock(&network->close_lock);
} /* Otherwise we send it out the tty. */ else
ipwireless_tty_received(tty, data, length);
}
}
for (i = 0; i < MAX_ASSOCIATED_TTYS; i++) if (network->associated_ttys[channel_idx][i] == NULL) {
network->associated_ttys[channel_idx][i] = tty; break;
}
}
void ipwireless_disassociate_network_ttys(struct ipw_network *network, unsignedint channel_idx)
{ int i;
for (i = 0; i < MAX_ASSOCIATED_TTYS; i++)
network->associated_ttys[channel_idx][i] = NULL;
}
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.