1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0 2099dc4fbSDavid Sterba /* 3099dc4fbSDavid Sterba * IPWireless 3G PCMCIA Network Driver 4099dc4fbSDavid Sterba * 5099dc4fbSDavid Sterba * Original code 6099dc4fbSDavid Sterba * by Stephen Blackheath <stephen@blacksapphire.com>, 7099dc4fbSDavid Sterba * Ben Martel <benm@symmetric.co.nz> 8099dc4fbSDavid Sterba * 9099dc4fbSDavid Sterba * Copyrighted as follows: 10099dc4fbSDavid Sterba * Copyright (C) 2004 by Symmetric Systems Ltd (NZ) 11099dc4fbSDavid Sterba * 12099dc4fbSDavid Sterba * Various driver changes and rewrites, port to new kernels 13099dc4fbSDavid Sterba * Copyright (C) 2006-2007 Jiri Kosina 14099dc4fbSDavid Sterba * 15099dc4fbSDavid Sterba * Misc code cleanups and updates 16099dc4fbSDavid Sterba * Copyright (C) 2007 David Sterba 17099dc4fbSDavid Sterba */ 18099dc4fbSDavid Sterba 19099dc4fbSDavid Sterba #include <linux/interrupt.h> 20099dc4fbSDavid Sterba #include <linux/kernel.h> 21099dc4fbSDavid Sterba #include <linux/mutex.h> 22099dc4fbSDavid Sterba #include <linux/netdevice.h> 23099dc4fbSDavid Sterba #include <linux/ppp_channel.h> 24099dc4fbSDavid Sterba #include <linux/ppp_defs.h> 255a0e3ad6STejun Heo #include <linux/slab.h> 264b32da2bSPaul Mackerras #include <linux/ppp-ioctl.h> 27099dc4fbSDavid Sterba #include <linux/skbuff.h> 28099dc4fbSDavid Sterba 29099dc4fbSDavid Sterba #include "network.h" 30099dc4fbSDavid Sterba #include "hardware.h" 31099dc4fbSDavid Sterba #include "main.h" 32099dc4fbSDavid Sterba #include "tty.h" 33099dc4fbSDavid Sterba 34099dc4fbSDavid Sterba #define MAX_ASSOCIATED_TTYS 2 35099dc4fbSDavid Sterba 36099dc4fbSDavid Sterba #define SC_RCV_BITS (SC_RCV_B7_1|SC_RCV_B7_0|SC_RCV_ODDP|SC_RCV_EVNP) 37099dc4fbSDavid Sterba 38099dc4fbSDavid Sterba struct ipw_network { 39099dc4fbSDavid Sterba /* Hardware context, used for calls to hardware layer. */ 40099dc4fbSDavid Sterba struct ipw_hardware *hardware; 41099dc4fbSDavid Sterba /* Context for kernel 'generic_ppp' functionality */ 42099dc4fbSDavid Sterba struct ppp_channel *ppp_channel; 43099dc4fbSDavid Sterba /* tty context connected with IPW console */ 44099dc4fbSDavid Sterba struct ipw_tty *associated_ttys[NO_OF_IPW_CHANNELS][MAX_ASSOCIATED_TTYS]; 45099dc4fbSDavid Sterba /* True if ppp needs waking up once we're ready to xmit */ 46099dc4fbSDavid Sterba int ppp_blocked; 47099dc4fbSDavid Sterba /* Number of packets queued up in hardware module. */ 48099dc4fbSDavid Sterba int outgoing_packets_queued; 49099dc4fbSDavid Sterba /* Spinlock to avoid interrupts during shutdown */ 5063c4dbd1SDavid Sterba spinlock_t lock; 51099dc4fbSDavid Sterba struct mutex close_lock; 52099dc4fbSDavid Sterba 53099dc4fbSDavid Sterba /* PPP ioctl data, not actually used anywere */ 54099dc4fbSDavid Sterba unsigned int flags; 55099dc4fbSDavid Sterba unsigned int rbits; 56099dc4fbSDavid Sterba u32 xaccm[8]; 57099dc4fbSDavid Sterba u32 raccm; 58099dc4fbSDavid Sterba int mru; 59099dc4fbSDavid Sterba 60099dc4fbSDavid Sterba int shutting_down; 61099dc4fbSDavid Sterba unsigned int ras_control_lines; 62099dc4fbSDavid Sterba 63099dc4fbSDavid Sterba struct work_struct work_go_online; 64099dc4fbSDavid Sterba struct work_struct work_go_offline; 65099dc4fbSDavid Sterba }; 66099dc4fbSDavid Sterba 67099dc4fbSDavid Sterba static void notify_packet_sent(void *callback_data, unsigned int packet_length) 68099dc4fbSDavid Sterba { 69099dc4fbSDavid Sterba struct ipw_network *network = callback_data; 70099dc4fbSDavid Sterba unsigned long flags; 71099dc4fbSDavid Sterba 7263c4dbd1SDavid Sterba spin_lock_irqsave(&network->lock, flags); 73099dc4fbSDavid Sterba network->outgoing_packets_queued--; 74099dc4fbSDavid Sterba if (network->ppp_channel != NULL) { 75099dc4fbSDavid Sterba if (network->ppp_blocked) { 76099dc4fbSDavid Sterba network->ppp_blocked = 0; 7763c4dbd1SDavid Sterba spin_unlock_irqrestore(&network->lock, flags); 78099dc4fbSDavid Sterba ppp_output_wakeup(network->ppp_channel); 79099dc4fbSDavid Sterba if (ipwireless_debug) 80bee9c7c0SDavid Sterba printk(KERN_DEBUG IPWIRELESS_PCCARD_NAME 81099dc4fbSDavid Sterba ": ppp unblocked\n"); 82099dc4fbSDavid Sterba } else 8363c4dbd1SDavid Sterba spin_unlock_irqrestore(&network->lock, flags); 84099dc4fbSDavid Sterba } else 8563c4dbd1SDavid Sterba spin_unlock_irqrestore(&network->lock, flags); 86099dc4fbSDavid Sterba } 87099dc4fbSDavid Sterba 88099dc4fbSDavid Sterba /* 89099dc4fbSDavid Sterba * Called by the ppp system when it has a packet to send to the hardware. 90099dc4fbSDavid Sterba */ 91099dc4fbSDavid Sterba static int ipwireless_ppp_start_xmit(struct ppp_channel *ppp_channel, 92099dc4fbSDavid Sterba struct sk_buff *skb) 93099dc4fbSDavid Sterba { 94099dc4fbSDavid Sterba struct ipw_network *network = ppp_channel->private; 95099dc4fbSDavid Sterba unsigned long flags; 96099dc4fbSDavid Sterba 9763c4dbd1SDavid Sterba spin_lock_irqsave(&network->lock, flags); 982e713165SDavid Sterba if (network->outgoing_packets_queued < ipwireless_out_queue) { 99099dc4fbSDavid Sterba unsigned char *buf; 100099dc4fbSDavid Sterba static unsigned char header[] = { 101099dc4fbSDavid Sterba PPP_ALLSTATIONS, /* 0xff */ 102099dc4fbSDavid Sterba PPP_UI, /* 0x03 */ 103099dc4fbSDavid Sterba }; 104099dc4fbSDavid Sterba int ret; 105099dc4fbSDavid Sterba 106099dc4fbSDavid Sterba network->outgoing_packets_queued++; 10763c4dbd1SDavid Sterba spin_unlock_irqrestore(&network->lock, flags); 108099dc4fbSDavid Sterba 109099dc4fbSDavid Sterba /* 110099dc4fbSDavid Sterba * If we have the requested amount of headroom in the skb we 111099dc4fbSDavid Sterba * were handed, then we can add the header efficiently. 112099dc4fbSDavid Sterba */ 113099dc4fbSDavid Sterba if (skb_headroom(skb) >= 2) { 114099dc4fbSDavid Sterba memcpy(skb_push(skb, 2), header, 2); 115099dc4fbSDavid Sterba ret = ipwireless_send_packet(network->hardware, 116099dc4fbSDavid Sterba IPW_CHANNEL_RAS, skb->data, 117099dc4fbSDavid Sterba skb->len, 118099dc4fbSDavid Sterba notify_packet_sent, 119099dc4fbSDavid Sterba network); 120*db332356STong Zhang if (ret < 0) { 121099dc4fbSDavid Sterba skb_pull(skb, 2); 122099dc4fbSDavid Sterba return 0; 123099dc4fbSDavid Sterba } 124099dc4fbSDavid Sterba } else { 125099dc4fbSDavid Sterba /* Otherwise (rarely) we do it inefficiently. */ 126099dc4fbSDavid Sterba buf = kmalloc(skb->len + 2, GFP_ATOMIC); 127099dc4fbSDavid Sterba if (!buf) 128099dc4fbSDavid Sterba return 0; 129099dc4fbSDavid Sterba memcpy(buf + 2, skb->data, skb->len); 130099dc4fbSDavid Sterba memcpy(buf, header, 2); 131099dc4fbSDavid Sterba ret = ipwireless_send_packet(network->hardware, 132099dc4fbSDavid Sterba IPW_CHANNEL_RAS, buf, 133099dc4fbSDavid Sterba skb->len + 2, 134099dc4fbSDavid Sterba notify_packet_sent, 135099dc4fbSDavid Sterba network); 136099dc4fbSDavid Sterba kfree(buf); 137*db332356STong Zhang if (ret < 0) 138099dc4fbSDavid Sterba return 0; 139099dc4fbSDavid Sterba } 140099dc4fbSDavid Sterba kfree_skb(skb); 141099dc4fbSDavid Sterba return 1; 142099dc4fbSDavid Sterba } else { 143099dc4fbSDavid Sterba /* 144099dc4fbSDavid Sterba * Otherwise reject the packet, and flag that the ppp system 145099dc4fbSDavid Sterba * needs to be unblocked once we are ready to send. 146099dc4fbSDavid Sterba */ 147099dc4fbSDavid Sterba network->ppp_blocked = 1; 14863c4dbd1SDavid Sterba spin_unlock_irqrestore(&network->lock, flags); 149bee9c7c0SDavid Sterba if (ipwireless_debug) 150bee9c7c0SDavid Sterba printk(KERN_DEBUG IPWIRELESS_PCCARD_NAME ": ppp blocked\n"); 151099dc4fbSDavid Sterba return 0; 152099dc4fbSDavid Sterba } 153099dc4fbSDavid Sterba } 154099dc4fbSDavid Sterba 155099dc4fbSDavid Sterba /* Handle an ioctl call that has come in via ppp. (copy of ppp_async_ioctl() */ 156099dc4fbSDavid Sterba static int ipwireless_ppp_ioctl(struct ppp_channel *ppp_channel, 157099dc4fbSDavid Sterba unsigned int cmd, unsigned long arg) 158099dc4fbSDavid Sterba { 159099dc4fbSDavid Sterba struct ipw_network *network = ppp_channel->private; 160099dc4fbSDavid Sterba int err, val; 161099dc4fbSDavid Sterba u32 accm[8]; 162099dc4fbSDavid Sterba int __user *user_arg = (int __user *) arg; 163099dc4fbSDavid Sterba 164099dc4fbSDavid Sterba err = -EFAULT; 165099dc4fbSDavid Sterba switch (cmd) { 166099dc4fbSDavid Sterba case PPPIOCGFLAGS: 167099dc4fbSDavid Sterba val = network->flags | network->rbits; 168099dc4fbSDavid Sterba if (put_user(val, user_arg)) 169099dc4fbSDavid Sterba break; 170099dc4fbSDavid Sterba err = 0; 171099dc4fbSDavid Sterba break; 172099dc4fbSDavid Sterba 173099dc4fbSDavid Sterba case PPPIOCSFLAGS: 174099dc4fbSDavid Sterba if (get_user(val, user_arg)) 175099dc4fbSDavid Sterba break; 176099dc4fbSDavid Sterba network->flags = val & ~SC_RCV_BITS; 177099dc4fbSDavid Sterba network->rbits = val & SC_RCV_BITS; 178099dc4fbSDavid Sterba err = 0; 179099dc4fbSDavid Sterba break; 180099dc4fbSDavid Sterba 181099dc4fbSDavid Sterba case PPPIOCGASYNCMAP: 182099dc4fbSDavid Sterba if (put_user(network->xaccm[0], user_arg)) 183099dc4fbSDavid Sterba break; 184099dc4fbSDavid Sterba err = 0; 185099dc4fbSDavid Sterba break; 186099dc4fbSDavid Sterba 187099dc4fbSDavid Sterba case PPPIOCSASYNCMAP: 188099dc4fbSDavid Sterba if (get_user(network->xaccm[0], user_arg)) 189099dc4fbSDavid Sterba break; 190099dc4fbSDavid Sterba err = 0; 191099dc4fbSDavid Sterba break; 192099dc4fbSDavid Sterba 193099dc4fbSDavid Sterba case PPPIOCGRASYNCMAP: 194099dc4fbSDavid Sterba if (put_user(network->raccm, user_arg)) 195099dc4fbSDavid Sterba break; 196099dc4fbSDavid Sterba err = 0; 197099dc4fbSDavid Sterba break; 198099dc4fbSDavid Sterba 199099dc4fbSDavid Sterba case PPPIOCSRASYNCMAP: 200099dc4fbSDavid Sterba if (get_user(network->raccm, user_arg)) 201099dc4fbSDavid Sterba break; 202099dc4fbSDavid Sterba err = 0; 203099dc4fbSDavid Sterba break; 204099dc4fbSDavid Sterba 205099dc4fbSDavid Sterba case PPPIOCGXASYNCMAP: 206099dc4fbSDavid Sterba if (copy_to_user((void __user *) arg, network->xaccm, 207099dc4fbSDavid Sterba sizeof(network->xaccm))) 208099dc4fbSDavid Sterba break; 209099dc4fbSDavid Sterba err = 0; 210099dc4fbSDavid Sterba break; 211099dc4fbSDavid Sterba 212099dc4fbSDavid Sterba case PPPIOCSXASYNCMAP: 213099dc4fbSDavid Sterba if (copy_from_user(accm, (void __user *) arg, sizeof(accm))) 214099dc4fbSDavid Sterba break; 215099dc4fbSDavid Sterba accm[2] &= ~0x40000000U; /* can't escape 0x5e */ 216099dc4fbSDavid Sterba accm[3] |= 0x60000000U; /* must escape 0x7d, 0x7e */ 217099dc4fbSDavid Sterba memcpy(network->xaccm, accm, sizeof(network->xaccm)); 218099dc4fbSDavid Sterba err = 0; 219099dc4fbSDavid Sterba break; 220099dc4fbSDavid Sterba 221099dc4fbSDavid Sterba case PPPIOCGMRU: 222099dc4fbSDavid Sterba if (put_user(network->mru, user_arg)) 223099dc4fbSDavid Sterba break; 224099dc4fbSDavid Sterba err = 0; 225099dc4fbSDavid Sterba break; 226099dc4fbSDavid Sterba 227099dc4fbSDavid Sterba case PPPIOCSMRU: 228099dc4fbSDavid Sterba if (get_user(val, user_arg)) 229099dc4fbSDavid Sterba break; 230099dc4fbSDavid Sterba if (val < PPP_MRU) 231099dc4fbSDavid Sterba val = PPP_MRU; 232099dc4fbSDavid Sterba network->mru = val; 233099dc4fbSDavid Sterba err = 0; 234099dc4fbSDavid Sterba break; 235099dc4fbSDavid Sterba 236099dc4fbSDavid Sterba default: 237099dc4fbSDavid Sterba err = -ENOTTY; 238099dc4fbSDavid Sterba } 239099dc4fbSDavid Sterba 240099dc4fbSDavid Sterba return err; 241099dc4fbSDavid Sterba } 242099dc4fbSDavid Sterba 243d7100da0Sstephen hemminger static const struct ppp_channel_ops ipwireless_ppp_channel_ops = { 244099dc4fbSDavid Sterba .start_xmit = ipwireless_ppp_start_xmit, 245099dc4fbSDavid Sterba .ioctl = ipwireless_ppp_ioctl 246099dc4fbSDavid Sterba }; 247099dc4fbSDavid Sterba 248099dc4fbSDavid Sterba static void do_go_online(struct work_struct *work_go_online) 249099dc4fbSDavid Sterba { 250099dc4fbSDavid Sterba struct ipw_network *network = 251099dc4fbSDavid Sterba container_of(work_go_online, struct ipw_network, 252099dc4fbSDavid Sterba work_go_online); 253099dc4fbSDavid Sterba unsigned long flags; 254099dc4fbSDavid Sterba 25563c4dbd1SDavid Sterba spin_lock_irqsave(&network->lock, flags); 256099dc4fbSDavid Sterba if (!network->ppp_channel) { 257099dc4fbSDavid Sterba struct ppp_channel *channel; 258099dc4fbSDavid Sterba 25963c4dbd1SDavid Sterba spin_unlock_irqrestore(&network->lock, flags); 260099dc4fbSDavid Sterba channel = kzalloc(sizeof(struct ppp_channel), GFP_KERNEL); 261099dc4fbSDavid Sterba if (!channel) { 262099dc4fbSDavid Sterba printk(KERN_ERR IPWIRELESS_PCCARD_NAME 263099dc4fbSDavid Sterba ": unable to allocate PPP channel\n"); 264099dc4fbSDavid Sterba return; 265099dc4fbSDavid Sterba } 266099dc4fbSDavid Sterba channel->private = network; 267099dc4fbSDavid Sterba channel->mtu = 16384; /* Wild guess */ 268099dc4fbSDavid Sterba channel->hdrlen = 2; 269099dc4fbSDavid Sterba channel->ops = &ipwireless_ppp_channel_ops; 270099dc4fbSDavid Sterba 271099dc4fbSDavid Sterba network->flags = 0; 272099dc4fbSDavid Sterba network->rbits = 0; 273099dc4fbSDavid Sterba network->mru = PPP_MRU; 274099dc4fbSDavid Sterba memset(network->xaccm, 0, sizeof(network->xaccm)); 275099dc4fbSDavid Sterba network->xaccm[0] = ~0U; 276099dc4fbSDavid Sterba network->xaccm[3] = 0x60000000U; 277099dc4fbSDavid Sterba network->raccm = ~0U; 278b6abc904SAlan Cox if (ppp_register_channel(channel) < 0) { 279b6abc904SAlan Cox printk(KERN_ERR IPWIRELESS_PCCARD_NAME 280b6abc904SAlan Cox ": unable to register PPP channel\n"); 281b6abc904SAlan Cox kfree(channel); 282b6abc904SAlan Cox return; 283b6abc904SAlan Cox } 28463c4dbd1SDavid Sterba spin_lock_irqsave(&network->lock, flags); 285099dc4fbSDavid Sterba network->ppp_channel = channel; 286099dc4fbSDavid Sterba } 28763c4dbd1SDavid Sterba spin_unlock_irqrestore(&network->lock, flags); 288099dc4fbSDavid Sterba } 289099dc4fbSDavid Sterba 290099dc4fbSDavid Sterba static void do_go_offline(struct work_struct *work_go_offline) 291099dc4fbSDavid Sterba { 292099dc4fbSDavid Sterba struct ipw_network *network = 293099dc4fbSDavid Sterba container_of(work_go_offline, struct ipw_network, 294099dc4fbSDavid Sterba work_go_offline); 295099dc4fbSDavid Sterba unsigned long flags; 296099dc4fbSDavid Sterba 297099dc4fbSDavid Sterba mutex_lock(&network->close_lock); 29863c4dbd1SDavid Sterba spin_lock_irqsave(&network->lock, flags); 299099dc4fbSDavid Sterba if (network->ppp_channel != NULL) { 300099dc4fbSDavid Sterba struct ppp_channel *channel = network->ppp_channel; 301099dc4fbSDavid Sterba 302099dc4fbSDavid Sterba network->ppp_channel = NULL; 30363c4dbd1SDavid Sterba spin_unlock_irqrestore(&network->lock, flags); 304099dc4fbSDavid Sterba mutex_unlock(&network->close_lock); 305099dc4fbSDavid Sterba ppp_unregister_channel(channel); 306099dc4fbSDavid Sterba } else { 30763c4dbd1SDavid Sterba spin_unlock_irqrestore(&network->lock, flags); 308099dc4fbSDavid Sterba mutex_unlock(&network->close_lock); 309099dc4fbSDavid Sterba } 310099dc4fbSDavid Sterba } 311099dc4fbSDavid Sterba 312099dc4fbSDavid Sterba void ipwireless_network_notify_control_line_change(struct ipw_network *network, 313099dc4fbSDavid Sterba unsigned int channel_idx, 314099dc4fbSDavid Sterba unsigned int control_lines, 315099dc4fbSDavid Sterba unsigned int changed_mask) 316099dc4fbSDavid Sterba { 317099dc4fbSDavid Sterba int i; 318099dc4fbSDavid Sterba 319099dc4fbSDavid Sterba if (channel_idx == IPW_CHANNEL_RAS) 320099dc4fbSDavid Sterba network->ras_control_lines = control_lines; 321099dc4fbSDavid Sterba 322099dc4fbSDavid Sterba for (i = 0; i < MAX_ASSOCIATED_TTYS; i++) { 323099dc4fbSDavid Sterba struct ipw_tty *tty = 324099dc4fbSDavid Sterba network->associated_ttys[channel_idx][i]; 325099dc4fbSDavid Sterba 326099dc4fbSDavid Sterba /* 327099dc4fbSDavid Sterba * If it's associated with a tty (other than the RAS channel 328099dc4fbSDavid Sterba * when we're online), then send the data to that tty. The RAS 329099dc4fbSDavid Sterba * channel's data is handled above - it always goes through 330099dc4fbSDavid Sterba * ppp_generic. 331099dc4fbSDavid Sterba */ 332099dc4fbSDavid Sterba if (tty) 333099dc4fbSDavid Sterba ipwireless_tty_notify_control_line_change(tty, 334099dc4fbSDavid Sterba channel_idx, 335099dc4fbSDavid Sterba control_lines, 336099dc4fbSDavid Sterba changed_mask); 337099dc4fbSDavid Sterba } 338099dc4fbSDavid Sterba } 339099dc4fbSDavid Sterba 340099dc4fbSDavid Sterba /* 341099dc4fbSDavid Sterba * Some versions of firmware stuff packets with 0xff 0x03 (PPP: ALLSTATIONS, UI) 342099dc4fbSDavid Sterba * bytes, which are required on sent packet, but not always present on received 343099dc4fbSDavid Sterba * packets 344099dc4fbSDavid Sterba */ 345099dc4fbSDavid Sterba static struct sk_buff *ipw_packet_received_skb(unsigned char *data, 346099dc4fbSDavid Sterba unsigned int length) 347099dc4fbSDavid Sterba { 348099dc4fbSDavid Sterba struct sk_buff *skb; 349099dc4fbSDavid Sterba 350099dc4fbSDavid Sterba if (length > 2 && data[0] == PPP_ALLSTATIONS && data[1] == PPP_UI) { 351099dc4fbSDavid Sterba length -= 2; 352099dc4fbSDavid Sterba data += 2; 353099dc4fbSDavid Sterba } 354099dc4fbSDavid Sterba 355099dc4fbSDavid Sterba skb = dev_alloc_skb(length + 4); 356d1519e23SAlan Cox if (skb == NULL) 357d1519e23SAlan Cox return NULL; 358099dc4fbSDavid Sterba skb_reserve(skb, 2); 35959ae1d12SJohannes Berg skb_put_data(skb, data, length); 360099dc4fbSDavid Sterba 361099dc4fbSDavid Sterba return skb; 362099dc4fbSDavid Sterba } 363099dc4fbSDavid Sterba 364099dc4fbSDavid Sterba void ipwireless_network_packet_received(struct ipw_network *network, 365099dc4fbSDavid Sterba unsigned int channel_idx, 366099dc4fbSDavid Sterba unsigned char *data, 367099dc4fbSDavid Sterba unsigned int length) 368099dc4fbSDavid Sterba { 369099dc4fbSDavid Sterba int i; 370099dc4fbSDavid Sterba unsigned long flags; 371099dc4fbSDavid Sterba 372099dc4fbSDavid Sterba for (i = 0; i < MAX_ASSOCIATED_TTYS; i++) { 373099dc4fbSDavid Sterba struct ipw_tty *tty = network->associated_ttys[channel_idx][i]; 374099dc4fbSDavid Sterba 375a51f4124SDavid Sterba if (!tty) 376a51f4124SDavid Sterba continue; 377a51f4124SDavid Sterba 378099dc4fbSDavid Sterba /* 379099dc4fbSDavid Sterba * If it's associated with a tty (other than the RAS channel 380099dc4fbSDavid Sterba * when we're online), then send the data to that tty. The RAS 381099dc4fbSDavid Sterba * channel's data is handled above - it always goes through 382099dc4fbSDavid Sterba * ppp_generic. 383099dc4fbSDavid Sterba */ 384a51f4124SDavid Sterba if (channel_idx == IPW_CHANNEL_RAS 385099dc4fbSDavid Sterba && (network->ras_control_lines & 386099dc4fbSDavid Sterba IPW_CONTROL_LINE_DCD) != 0 387099dc4fbSDavid Sterba && ipwireless_tty_is_modem(tty)) { 388099dc4fbSDavid Sterba /* 389099dc4fbSDavid Sterba * If data came in on the RAS channel and this tty is 390099dc4fbSDavid Sterba * the modem tty, and we are online, then we send it to 391099dc4fbSDavid Sterba * the PPP layer. 392099dc4fbSDavid Sterba */ 393099dc4fbSDavid Sterba mutex_lock(&network->close_lock); 39463c4dbd1SDavid Sterba spin_lock_irqsave(&network->lock, flags); 395099dc4fbSDavid Sterba if (network->ppp_channel != NULL) { 396099dc4fbSDavid Sterba struct sk_buff *skb; 397099dc4fbSDavid Sterba 39863c4dbd1SDavid Sterba spin_unlock_irqrestore(&network->lock, 399099dc4fbSDavid Sterba flags); 400099dc4fbSDavid Sterba 401099dc4fbSDavid Sterba /* Send the data to the ppp_generic module. */ 402099dc4fbSDavid Sterba skb = ipw_packet_received_skb(data, length); 403d1519e23SAlan Cox if (skb) 404099dc4fbSDavid Sterba ppp_input(network->ppp_channel, skb); 405099dc4fbSDavid Sterba } else 40663c4dbd1SDavid Sterba spin_unlock_irqrestore(&network->lock, 407099dc4fbSDavid Sterba flags); 408099dc4fbSDavid Sterba mutex_unlock(&network->close_lock); 409099dc4fbSDavid Sterba } 410099dc4fbSDavid Sterba /* Otherwise we send it out the tty. */ 411099dc4fbSDavid Sterba else 412099dc4fbSDavid Sterba ipwireless_tty_received(tty, data, length); 413099dc4fbSDavid Sterba } 414099dc4fbSDavid Sterba } 415099dc4fbSDavid Sterba 416099dc4fbSDavid Sterba struct ipw_network *ipwireless_network_create(struct ipw_hardware *hw) 417099dc4fbSDavid Sterba { 418099dc4fbSDavid Sterba struct ipw_network *network = 41913eac05bSJia-Ju Bai kzalloc(sizeof(struct ipw_network), GFP_KERNEL); 420099dc4fbSDavid Sterba 421099dc4fbSDavid Sterba if (!network) 422099dc4fbSDavid Sterba return NULL; 423099dc4fbSDavid Sterba 42463c4dbd1SDavid Sterba spin_lock_init(&network->lock); 425099dc4fbSDavid Sterba mutex_init(&network->close_lock); 426099dc4fbSDavid Sterba 427099dc4fbSDavid Sterba network->hardware = hw; 428099dc4fbSDavid Sterba 429099dc4fbSDavid Sterba INIT_WORK(&network->work_go_online, do_go_online); 430099dc4fbSDavid Sterba INIT_WORK(&network->work_go_offline, do_go_offline); 431099dc4fbSDavid Sterba 432099dc4fbSDavid Sterba ipwireless_associate_network(hw, network); 433099dc4fbSDavid Sterba 434099dc4fbSDavid Sterba return network; 435099dc4fbSDavid Sterba } 436099dc4fbSDavid Sterba 437099dc4fbSDavid Sterba void ipwireless_network_free(struct ipw_network *network) 438099dc4fbSDavid Sterba { 439099dc4fbSDavid Sterba network->shutting_down = 1; 440099dc4fbSDavid Sterba 441099dc4fbSDavid Sterba ipwireless_ppp_close(network); 44243829731STejun Heo flush_work(&network->work_go_online); 44343829731STejun Heo flush_work(&network->work_go_offline); 444099dc4fbSDavid Sterba 445099dc4fbSDavid Sterba ipwireless_stop_interrupts(network->hardware); 446099dc4fbSDavid Sterba ipwireless_associate_network(network->hardware, NULL); 447099dc4fbSDavid Sterba 448099dc4fbSDavid Sterba kfree(network); 449099dc4fbSDavid Sterba } 450099dc4fbSDavid Sterba 451099dc4fbSDavid Sterba void ipwireless_associate_network_tty(struct ipw_network *network, 452099dc4fbSDavid Sterba unsigned int channel_idx, 453099dc4fbSDavid Sterba struct ipw_tty *tty) 454099dc4fbSDavid Sterba { 455099dc4fbSDavid Sterba int i; 456099dc4fbSDavid Sterba 457099dc4fbSDavid Sterba for (i = 0; i < MAX_ASSOCIATED_TTYS; i++) 458099dc4fbSDavid Sterba if (network->associated_ttys[channel_idx][i] == NULL) { 459099dc4fbSDavid Sterba network->associated_ttys[channel_idx][i] = tty; 460099dc4fbSDavid Sterba break; 461099dc4fbSDavid Sterba } 462099dc4fbSDavid Sterba } 463099dc4fbSDavid Sterba 464099dc4fbSDavid Sterba void ipwireless_disassociate_network_ttys(struct ipw_network *network, 465099dc4fbSDavid Sterba unsigned int channel_idx) 466099dc4fbSDavid Sterba { 467099dc4fbSDavid Sterba int i; 468099dc4fbSDavid Sterba 469099dc4fbSDavid Sterba for (i = 0; i < MAX_ASSOCIATED_TTYS; i++) 470099dc4fbSDavid Sterba network->associated_ttys[channel_idx][i] = NULL; 471099dc4fbSDavid Sterba } 472099dc4fbSDavid Sterba 473099dc4fbSDavid Sterba void ipwireless_ppp_open(struct ipw_network *network) 474099dc4fbSDavid Sterba { 475099dc4fbSDavid Sterba if (ipwireless_debug) 476099dc4fbSDavid Sterba printk(KERN_DEBUG IPWIRELESS_PCCARD_NAME ": online\n"); 477099dc4fbSDavid Sterba schedule_work(&network->work_go_online); 478099dc4fbSDavid Sterba } 479099dc4fbSDavid Sterba 480099dc4fbSDavid Sterba void ipwireless_ppp_close(struct ipw_network *network) 481099dc4fbSDavid Sterba { 482099dc4fbSDavid Sterba /* Disconnect from the wireless network. */ 483099dc4fbSDavid Sterba if (ipwireless_debug) 484099dc4fbSDavid Sterba printk(KERN_DEBUG IPWIRELESS_PCCARD_NAME ": offline\n"); 485099dc4fbSDavid Sterba schedule_work(&network->work_go_offline); 486099dc4fbSDavid Sterba } 487099dc4fbSDavid Sterba 488099dc4fbSDavid Sterba int ipwireless_ppp_channel_index(struct ipw_network *network) 489099dc4fbSDavid Sterba { 490099dc4fbSDavid Sterba int ret = -1; 491099dc4fbSDavid Sterba unsigned long flags; 492099dc4fbSDavid Sterba 49363c4dbd1SDavid Sterba spin_lock_irqsave(&network->lock, flags); 494099dc4fbSDavid Sterba if (network->ppp_channel != NULL) 495099dc4fbSDavid Sterba ret = ppp_channel_index(network->ppp_channel); 49663c4dbd1SDavid Sterba spin_unlock_irqrestore(&network->lock, flags); 497099dc4fbSDavid Sterba 498099dc4fbSDavid Sterba return ret; 499099dc4fbSDavid Sterba } 500099dc4fbSDavid Sterba 501099dc4fbSDavid Sterba int ipwireless_ppp_unit_number(struct ipw_network *network) 502099dc4fbSDavid Sterba { 503099dc4fbSDavid Sterba int ret = -1; 504099dc4fbSDavid Sterba unsigned long flags; 505099dc4fbSDavid Sterba 50663c4dbd1SDavid Sterba spin_lock_irqsave(&network->lock, flags); 507099dc4fbSDavid Sterba if (network->ppp_channel != NULL) 508099dc4fbSDavid Sterba ret = ppp_unit_number(network->ppp_channel); 50963c4dbd1SDavid Sterba spin_unlock_irqrestore(&network->lock, flags); 510099dc4fbSDavid Sterba 511099dc4fbSDavid Sterba return ret; 512099dc4fbSDavid Sterba } 513a0138692SDavid Sterba 514a0138692SDavid Sterba int ipwireless_ppp_mru(const struct ipw_network *network) 515a0138692SDavid Sterba { 516a0138692SDavid Sterba return network->mru; 517a0138692SDavid Sterba } 518