18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * IPWireless 3G PCMCIA Network Driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Original code 68c2ecf20Sopenharmony_ci * by Stephen Blackheath <stephen@blacksapphire.com>, 78c2ecf20Sopenharmony_ci * Ben Martel <benm@symmetric.co.nz> 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * Copyrighted as follows: 108c2ecf20Sopenharmony_ci * Copyright (C) 2004 by Symmetric Systems Ltd (NZ) 118c2ecf20Sopenharmony_ci * 128c2ecf20Sopenharmony_ci * Various driver changes and rewrites, port to new kernels 138c2ecf20Sopenharmony_ci * Copyright (C) 2006-2007 Jiri Kosina 148c2ecf20Sopenharmony_ci * 158c2ecf20Sopenharmony_ci * Misc code cleanups and updates 168c2ecf20Sopenharmony_ci * Copyright (C) 2007 David Sterba 178c2ecf20Sopenharmony_ci */ 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 208c2ecf20Sopenharmony_ci#include <linux/kernel.h> 218c2ecf20Sopenharmony_ci#include <linux/mutex.h> 228c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 238c2ecf20Sopenharmony_ci#include <linux/ppp_channel.h> 248c2ecf20Sopenharmony_ci#include <linux/ppp_defs.h> 258c2ecf20Sopenharmony_ci#include <linux/slab.h> 268c2ecf20Sopenharmony_ci#include <linux/ppp-ioctl.h> 278c2ecf20Sopenharmony_ci#include <linux/skbuff.h> 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#include "network.h" 308c2ecf20Sopenharmony_ci#include "hardware.h" 318c2ecf20Sopenharmony_ci#include "main.h" 328c2ecf20Sopenharmony_ci#include "tty.h" 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci#define MAX_ASSOCIATED_TTYS 2 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci#define SC_RCV_BITS (SC_RCV_B7_1|SC_RCV_B7_0|SC_RCV_ODDP|SC_RCV_EVNP) 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_cistruct ipw_network { 398c2ecf20Sopenharmony_ci /* Hardware context, used for calls to hardware layer. */ 408c2ecf20Sopenharmony_ci struct ipw_hardware *hardware; 418c2ecf20Sopenharmony_ci /* Context for kernel 'generic_ppp' functionality */ 428c2ecf20Sopenharmony_ci struct ppp_channel *ppp_channel; 438c2ecf20Sopenharmony_ci /* tty context connected with IPW console */ 448c2ecf20Sopenharmony_ci struct ipw_tty *associated_ttys[NO_OF_IPW_CHANNELS][MAX_ASSOCIATED_TTYS]; 458c2ecf20Sopenharmony_ci /* True if ppp needs waking up once we're ready to xmit */ 468c2ecf20Sopenharmony_ci int ppp_blocked; 478c2ecf20Sopenharmony_ci /* Number of packets queued up in hardware module. */ 488c2ecf20Sopenharmony_ci int outgoing_packets_queued; 498c2ecf20Sopenharmony_ci /* Spinlock to avoid interrupts during shutdown */ 508c2ecf20Sopenharmony_ci spinlock_t lock; 518c2ecf20Sopenharmony_ci struct mutex close_lock; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci /* PPP ioctl data, not actually used anywere */ 548c2ecf20Sopenharmony_ci unsigned int flags; 558c2ecf20Sopenharmony_ci unsigned int rbits; 568c2ecf20Sopenharmony_ci u32 xaccm[8]; 578c2ecf20Sopenharmony_ci u32 raccm; 588c2ecf20Sopenharmony_ci int mru; 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci int shutting_down; 618c2ecf20Sopenharmony_ci unsigned int ras_control_lines; 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci struct work_struct work_go_online; 648c2ecf20Sopenharmony_ci struct work_struct work_go_offline; 658c2ecf20Sopenharmony_ci}; 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_cistatic void notify_packet_sent(void *callback_data, unsigned int packet_length) 688c2ecf20Sopenharmony_ci{ 698c2ecf20Sopenharmony_ci struct ipw_network *network = callback_data; 708c2ecf20Sopenharmony_ci unsigned long flags; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci spin_lock_irqsave(&network->lock, flags); 738c2ecf20Sopenharmony_ci network->outgoing_packets_queued--; 748c2ecf20Sopenharmony_ci if (network->ppp_channel != NULL) { 758c2ecf20Sopenharmony_ci if (network->ppp_blocked) { 768c2ecf20Sopenharmony_ci network->ppp_blocked = 0; 778c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&network->lock, flags); 788c2ecf20Sopenharmony_ci ppp_output_wakeup(network->ppp_channel); 798c2ecf20Sopenharmony_ci if (ipwireless_debug) 808c2ecf20Sopenharmony_ci printk(KERN_DEBUG IPWIRELESS_PCCARD_NAME 818c2ecf20Sopenharmony_ci ": ppp unblocked\n"); 828c2ecf20Sopenharmony_ci } else 838c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&network->lock, flags); 848c2ecf20Sopenharmony_ci } else 858c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&network->lock, flags); 868c2ecf20Sopenharmony_ci} 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci/* 898c2ecf20Sopenharmony_ci * Called by the ppp system when it has a packet to send to the hardware. 908c2ecf20Sopenharmony_ci */ 918c2ecf20Sopenharmony_cistatic int ipwireless_ppp_start_xmit(struct ppp_channel *ppp_channel, 928c2ecf20Sopenharmony_ci struct sk_buff *skb) 938c2ecf20Sopenharmony_ci{ 948c2ecf20Sopenharmony_ci struct ipw_network *network = ppp_channel->private; 958c2ecf20Sopenharmony_ci unsigned long flags; 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci spin_lock_irqsave(&network->lock, flags); 988c2ecf20Sopenharmony_ci if (network->outgoing_packets_queued < ipwireless_out_queue) { 998c2ecf20Sopenharmony_ci unsigned char *buf; 1008c2ecf20Sopenharmony_ci static unsigned char header[] = { 1018c2ecf20Sopenharmony_ci PPP_ALLSTATIONS, /* 0xff */ 1028c2ecf20Sopenharmony_ci PPP_UI, /* 0x03 */ 1038c2ecf20Sopenharmony_ci }; 1048c2ecf20Sopenharmony_ci int ret; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci network->outgoing_packets_queued++; 1078c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&network->lock, flags); 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci /* 1108c2ecf20Sopenharmony_ci * If we have the requested amount of headroom in the skb we 1118c2ecf20Sopenharmony_ci * were handed, then we can add the header efficiently. 1128c2ecf20Sopenharmony_ci */ 1138c2ecf20Sopenharmony_ci if (skb_headroom(skb) >= 2) { 1148c2ecf20Sopenharmony_ci memcpy(skb_push(skb, 2), header, 2); 1158c2ecf20Sopenharmony_ci ret = ipwireless_send_packet(network->hardware, 1168c2ecf20Sopenharmony_ci IPW_CHANNEL_RAS, skb->data, 1178c2ecf20Sopenharmony_ci skb->len, 1188c2ecf20Sopenharmony_ci notify_packet_sent, 1198c2ecf20Sopenharmony_ci network); 1208c2ecf20Sopenharmony_ci if (ret < 0) { 1218c2ecf20Sopenharmony_ci skb_pull(skb, 2); 1228c2ecf20Sopenharmony_ci return 0; 1238c2ecf20Sopenharmony_ci } 1248c2ecf20Sopenharmony_ci } else { 1258c2ecf20Sopenharmony_ci /* Otherwise (rarely) we do it inefficiently. */ 1268c2ecf20Sopenharmony_ci buf = kmalloc(skb->len + 2, GFP_ATOMIC); 1278c2ecf20Sopenharmony_ci if (!buf) 1288c2ecf20Sopenharmony_ci return 0; 1298c2ecf20Sopenharmony_ci memcpy(buf + 2, skb->data, skb->len); 1308c2ecf20Sopenharmony_ci memcpy(buf, header, 2); 1318c2ecf20Sopenharmony_ci ret = ipwireless_send_packet(network->hardware, 1328c2ecf20Sopenharmony_ci IPW_CHANNEL_RAS, buf, 1338c2ecf20Sopenharmony_ci skb->len + 2, 1348c2ecf20Sopenharmony_ci notify_packet_sent, 1358c2ecf20Sopenharmony_ci network); 1368c2ecf20Sopenharmony_ci kfree(buf); 1378c2ecf20Sopenharmony_ci if (ret < 0) 1388c2ecf20Sopenharmony_ci return 0; 1398c2ecf20Sopenharmony_ci } 1408c2ecf20Sopenharmony_ci kfree_skb(skb); 1418c2ecf20Sopenharmony_ci return 1; 1428c2ecf20Sopenharmony_ci } else { 1438c2ecf20Sopenharmony_ci /* 1448c2ecf20Sopenharmony_ci * Otherwise reject the packet, and flag that the ppp system 1458c2ecf20Sopenharmony_ci * needs to be unblocked once we are ready to send. 1468c2ecf20Sopenharmony_ci */ 1478c2ecf20Sopenharmony_ci network->ppp_blocked = 1; 1488c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&network->lock, flags); 1498c2ecf20Sopenharmony_ci if (ipwireless_debug) 1508c2ecf20Sopenharmony_ci printk(KERN_DEBUG IPWIRELESS_PCCARD_NAME ": ppp blocked\n"); 1518c2ecf20Sopenharmony_ci return 0; 1528c2ecf20Sopenharmony_ci } 1538c2ecf20Sopenharmony_ci} 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci/* Handle an ioctl call that has come in via ppp. (copy of ppp_async_ioctl() */ 1568c2ecf20Sopenharmony_cistatic int ipwireless_ppp_ioctl(struct ppp_channel *ppp_channel, 1578c2ecf20Sopenharmony_ci unsigned int cmd, unsigned long arg) 1588c2ecf20Sopenharmony_ci{ 1598c2ecf20Sopenharmony_ci struct ipw_network *network = ppp_channel->private; 1608c2ecf20Sopenharmony_ci int err, val; 1618c2ecf20Sopenharmony_ci u32 accm[8]; 1628c2ecf20Sopenharmony_ci int __user *user_arg = (int __user *) arg; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci err = -EFAULT; 1658c2ecf20Sopenharmony_ci switch (cmd) { 1668c2ecf20Sopenharmony_ci case PPPIOCGFLAGS: 1678c2ecf20Sopenharmony_ci val = network->flags | network->rbits; 1688c2ecf20Sopenharmony_ci if (put_user(val, user_arg)) 1698c2ecf20Sopenharmony_ci break; 1708c2ecf20Sopenharmony_ci err = 0; 1718c2ecf20Sopenharmony_ci break; 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci case PPPIOCSFLAGS: 1748c2ecf20Sopenharmony_ci if (get_user(val, user_arg)) 1758c2ecf20Sopenharmony_ci break; 1768c2ecf20Sopenharmony_ci network->flags = val & ~SC_RCV_BITS; 1778c2ecf20Sopenharmony_ci network->rbits = val & SC_RCV_BITS; 1788c2ecf20Sopenharmony_ci err = 0; 1798c2ecf20Sopenharmony_ci break; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci case PPPIOCGASYNCMAP: 1828c2ecf20Sopenharmony_ci if (put_user(network->xaccm[0], user_arg)) 1838c2ecf20Sopenharmony_ci break; 1848c2ecf20Sopenharmony_ci err = 0; 1858c2ecf20Sopenharmony_ci break; 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci case PPPIOCSASYNCMAP: 1888c2ecf20Sopenharmony_ci if (get_user(network->xaccm[0], user_arg)) 1898c2ecf20Sopenharmony_ci break; 1908c2ecf20Sopenharmony_ci err = 0; 1918c2ecf20Sopenharmony_ci break; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci case PPPIOCGRASYNCMAP: 1948c2ecf20Sopenharmony_ci if (put_user(network->raccm, user_arg)) 1958c2ecf20Sopenharmony_ci break; 1968c2ecf20Sopenharmony_ci err = 0; 1978c2ecf20Sopenharmony_ci break; 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci case PPPIOCSRASYNCMAP: 2008c2ecf20Sopenharmony_ci if (get_user(network->raccm, user_arg)) 2018c2ecf20Sopenharmony_ci break; 2028c2ecf20Sopenharmony_ci err = 0; 2038c2ecf20Sopenharmony_ci break; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci case PPPIOCGXASYNCMAP: 2068c2ecf20Sopenharmony_ci if (copy_to_user((void __user *) arg, network->xaccm, 2078c2ecf20Sopenharmony_ci sizeof(network->xaccm))) 2088c2ecf20Sopenharmony_ci break; 2098c2ecf20Sopenharmony_ci err = 0; 2108c2ecf20Sopenharmony_ci break; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci case PPPIOCSXASYNCMAP: 2138c2ecf20Sopenharmony_ci if (copy_from_user(accm, (void __user *) arg, sizeof(accm))) 2148c2ecf20Sopenharmony_ci break; 2158c2ecf20Sopenharmony_ci accm[2] &= ~0x40000000U; /* can't escape 0x5e */ 2168c2ecf20Sopenharmony_ci accm[3] |= 0x60000000U; /* must escape 0x7d, 0x7e */ 2178c2ecf20Sopenharmony_ci memcpy(network->xaccm, accm, sizeof(network->xaccm)); 2188c2ecf20Sopenharmony_ci err = 0; 2198c2ecf20Sopenharmony_ci break; 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci case PPPIOCGMRU: 2228c2ecf20Sopenharmony_ci if (put_user(network->mru, user_arg)) 2238c2ecf20Sopenharmony_ci break; 2248c2ecf20Sopenharmony_ci err = 0; 2258c2ecf20Sopenharmony_ci break; 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci case PPPIOCSMRU: 2288c2ecf20Sopenharmony_ci if (get_user(val, user_arg)) 2298c2ecf20Sopenharmony_ci break; 2308c2ecf20Sopenharmony_ci if (val < PPP_MRU) 2318c2ecf20Sopenharmony_ci val = PPP_MRU; 2328c2ecf20Sopenharmony_ci network->mru = val; 2338c2ecf20Sopenharmony_ci err = 0; 2348c2ecf20Sopenharmony_ci break; 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci default: 2378c2ecf20Sopenharmony_ci err = -ENOTTY; 2388c2ecf20Sopenharmony_ci } 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci return err; 2418c2ecf20Sopenharmony_ci} 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_cistatic const struct ppp_channel_ops ipwireless_ppp_channel_ops = { 2448c2ecf20Sopenharmony_ci .start_xmit = ipwireless_ppp_start_xmit, 2458c2ecf20Sopenharmony_ci .ioctl = ipwireless_ppp_ioctl 2468c2ecf20Sopenharmony_ci}; 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_cistatic void do_go_online(struct work_struct *work_go_online) 2498c2ecf20Sopenharmony_ci{ 2508c2ecf20Sopenharmony_ci struct ipw_network *network = 2518c2ecf20Sopenharmony_ci container_of(work_go_online, struct ipw_network, 2528c2ecf20Sopenharmony_ci work_go_online); 2538c2ecf20Sopenharmony_ci unsigned long flags; 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci spin_lock_irqsave(&network->lock, flags); 2568c2ecf20Sopenharmony_ci if (!network->ppp_channel) { 2578c2ecf20Sopenharmony_ci struct ppp_channel *channel; 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&network->lock, flags); 2608c2ecf20Sopenharmony_ci channel = kzalloc(sizeof(struct ppp_channel), GFP_KERNEL); 2618c2ecf20Sopenharmony_ci if (!channel) { 2628c2ecf20Sopenharmony_ci printk(KERN_ERR IPWIRELESS_PCCARD_NAME 2638c2ecf20Sopenharmony_ci ": unable to allocate PPP channel\n"); 2648c2ecf20Sopenharmony_ci return; 2658c2ecf20Sopenharmony_ci } 2668c2ecf20Sopenharmony_ci channel->private = network; 2678c2ecf20Sopenharmony_ci channel->mtu = 16384; /* Wild guess */ 2688c2ecf20Sopenharmony_ci channel->hdrlen = 2; 2698c2ecf20Sopenharmony_ci channel->ops = &ipwireless_ppp_channel_ops; 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci network->flags = 0; 2728c2ecf20Sopenharmony_ci network->rbits = 0; 2738c2ecf20Sopenharmony_ci network->mru = PPP_MRU; 2748c2ecf20Sopenharmony_ci memset(network->xaccm, 0, sizeof(network->xaccm)); 2758c2ecf20Sopenharmony_ci network->xaccm[0] = ~0U; 2768c2ecf20Sopenharmony_ci network->xaccm[3] = 0x60000000U; 2778c2ecf20Sopenharmony_ci network->raccm = ~0U; 2788c2ecf20Sopenharmony_ci if (ppp_register_channel(channel) < 0) { 2798c2ecf20Sopenharmony_ci printk(KERN_ERR IPWIRELESS_PCCARD_NAME 2808c2ecf20Sopenharmony_ci ": unable to register PPP channel\n"); 2818c2ecf20Sopenharmony_ci kfree(channel); 2828c2ecf20Sopenharmony_ci return; 2838c2ecf20Sopenharmony_ci } 2848c2ecf20Sopenharmony_ci spin_lock_irqsave(&network->lock, flags); 2858c2ecf20Sopenharmony_ci network->ppp_channel = channel; 2868c2ecf20Sopenharmony_ci } 2878c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&network->lock, flags); 2888c2ecf20Sopenharmony_ci} 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_cistatic void do_go_offline(struct work_struct *work_go_offline) 2918c2ecf20Sopenharmony_ci{ 2928c2ecf20Sopenharmony_ci struct ipw_network *network = 2938c2ecf20Sopenharmony_ci container_of(work_go_offline, struct ipw_network, 2948c2ecf20Sopenharmony_ci work_go_offline); 2958c2ecf20Sopenharmony_ci unsigned long flags; 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci mutex_lock(&network->close_lock); 2988c2ecf20Sopenharmony_ci spin_lock_irqsave(&network->lock, flags); 2998c2ecf20Sopenharmony_ci if (network->ppp_channel != NULL) { 3008c2ecf20Sopenharmony_ci struct ppp_channel *channel = network->ppp_channel; 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci network->ppp_channel = NULL; 3038c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&network->lock, flags); 3048c2ecf20Sopenharmony_ci mutex_unlock(&network->close_lock); 3058c2ecf20Sopenharmony_ci ppp_unregister_channel(channel); 3068c2ecf20Sopenharmony_ci } else { 3078c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&network->lock, flags); 3088c2ecf20Sopenharmony_ci mutex_unlock(&network->close_lock); 3098c2ecf20Sopenharmony_ci } 3108c2ecf20Sopenharmony_ci} 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_civoid ipwireless_network_notify_control_line_change(struct ipw_network *network, 3138c2ecf20Sopenharmony_ci unsigned int channel_idx, 3148c2ecf20Sopenharmony_ci unsigned int control_lines, 3158c2ecf20Sopenharmony_ci unsigned int changed_mask) 3168c2ecf20Sopenharmony_ci{ 3178c2ecf20Sopenharmony_ci int i; 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci if (channel_idx == IPW_CHANNEL_RAS) 3208c2ecf20Sopenharmony_ci network->ras_control_lines = control_lines; 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci for (i = 0; i < MAX_ASSOCIATED_TTYS; i++) { 3238c2ecf20Sopenharmony_ci struct ipw_tty *tty = 3248c2ecf20Sopenharmony_ci network->associated_ttys[channel_idx][i]; 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci /* 3278c2ecf20Sopenharmony_ci * If it's associated with a tty (other than the RAS channel 3288c2ecf20Sopenharmony_ci * when we're online), then send the data to that tty. The RAS 3298c2ecf20Sopenharmony_ci * channel's data is handled above - it always goes through 3308c2ecf20Sopenharmony_ci * ppp_generic. 3318c2ecf20Sopenharmony_ci */ 3328c2ecf20Sopenharmony_ci if (tty) 3338c2ecf20Sopenharmony_ci ipwireless_tty_notify_control_line_change(tty, 3348c2ecf20Sopenharmony_ci channel_idx, 3358c2ecf20Sopenharmony_ci control_lines, 3368c2ecf20Sopenharmony_ci changed_mask); 3378c2ecf20Sopenharmony_ci } 3388c2ecf20Sopenharmony_ci} 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci/* 3418c2ecf20Sopenharmony_ci * Some versions of firmware stuff packets with 0xff 0x03 (PPP: ALLSTATIONS, UI) 3428c2ecf20Sopenharmony_ci * bytes, which are required on sent packet, but not always present on received 3438c2ecf20Sopenharmony_ci * packets 3448c2ecf20Sopenharmony_ci */ 3458c2ecf20Sopenharmony_cistatic struct sk_buff *ipw_packet_received_skb(unsigned char *data, 3468c2ecf20Sopenharmony_ci unsigned int length) 3478c2ecf20Sopenharmony_ci{ 3488c2ecf20Sopenharmony_ci struct sk_buff *skb; 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci if (length > 2 && data[0] == PPP_ALLSTATIONS && data[1] == PPP_UI) { 3518c2ecf20Sopenharmony_ci length -= 2; 3528c2ecf20Sopenharmony_ci data += 2; 3538c2ecf20Sopenharmony_ci } 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci skb = dev_alloc_skb(length + 4); 3568c2ecf20Sopenharmony_ci if (skb == NULL) 3578c2ecf20Sopenharmony_ci return NULL; 3588c2ecf20Sopenharmony_ci skb_reserve(skb, 2); 3598c2ecf20Sopenharmony_ci skb_put_data(skb, data, length); 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci return skb; 3628c2ecf20Sopenharmony_ci} 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_civoid ipwireless_network_packet_received(struct ipw_network *network, 3658c2ecf20Sopenharmony_ci unsigned int channel_idx, 3668c2ecf20Sopenharmony_ci unsigned char *data, 3678c2ecf20Sopenharmony_ci unsigned int length) 3688c2ecf20Sopenharmony_ci{ 3698c2ecf20Sopenharmony_ci int i; 3708c2ecf20Sopenharmony_ci unsigned long flags; 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci for (i = 0; i < MAX_ASSOCIATED_TTYS; i++) { 3738c2ecf20Sopenharmony_ci struct ipw_tty *tty = network->associated_ttys[channel_idx][i]; 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci if (!tty) 3768c2ecf20Sopenharmony_ci continue; 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci /* 3798c2ecf20Sopenharmony_ci * If it's associated with a tty (other than the RAS channel 3808c2ecf20Sopenharmony_ci * when we're online), then send the data to that tty. The RAS 3818c2ecf20Sopenharmony_ci * channel's data is handled above - it always goes through 3828c2ecf20Sopenharmony_ci * ppp_generic. 3838c2ecf20Sopenharmony_ci */ 3848c2ecf20Sopenharmony_ci if (channel_idx == IPW_CHANNEL_RAS 3858c2ecf20Sopenharmony_ci && (network->ras_control_lines & 3868c2ecf20Sopenharmony_ci IPW_CONTROL_LINE_DCD) != 0 3878c2ecf20Sopenharmony_ci && ipwireless_tty_is_modem(tty)) { 3888c2ecf20Sopenharmony_ci /* 3898c2ecf20Sopenharmony_ci * If data came in on the RAS channel and this tty is 3908c2ecf20Sopenharmony_ci * the modem tty, and we are online, then we send it to 3918c2ecf20Sopenharmony_ci * the PPP layer. 3928c2ecf20Sopenharmony_ci */ 3938c2ecf20Sopenharmony_ci mutex_lock(&network->close_lock); 3948c2ecf20Sopenharmony_ci spin_lock_irqsave(&network->lock, flags); 3958c2ecf20Sopenharmony_ci if (network->ppp_channel != NULL) { 3968c2ecf20Sopenharmony_ci struct sk_buff *skb; 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&network->lock, 3998c2ecf20Sopenharmony_ci flags); 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci /* Send the data to the ppp_generic module. */ 4028c2ecf20Sopenharmony_ci skb = ipw_packet_received_skb(data, length); 4038c2ecf20Sopenharmony_ci if (skb) 4048c2ecf20Sopenharmony_ci ppp_input(network->ppp_channel, skb); 4058c2ecf20Sopenharmony_ci } else 4068c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&network->lock, 4078c2ecf20Sopenharmony_ci flags); 4088c2ecf20Sopenharmony_ci mutex_unlock(&network->close_lock); 4098c2ecf20Sopenharmony_ci } 4108c2ecf20Sopenharmony_ci /* Otherwise we send it out the tty. */ 4118c2ecf20Sopenharmony_ci else 4128c2ecf20Sopenharmony_ci ipwireless_tty_received(tty, data, length); 4138c2ecf20Sopenharmony_ci } 4148c2ecf20Sopenharmony_ci} 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_cistruct ipw_network *ipwireless_network_create(struct ipw_hardware *hw) 4178c2ecf20Sopenharmony_ci{ 4188c2ecf20Sopenharmony_ci struct ipw_network *network = 4198c2ecf20Sopenharmony_ci kzalloc(sizeof(struct ipw_network), GFP_KERNEL); 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci if (!network) 4228c2ecf20Sopenharmony_ci return NULL; 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci spin_lock_init(&network->lock); 4258c2ecf20Sopenharmony_ci mutex_init(&network->close_lock); 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci network->hardware = hw; 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci INIT_WORK(&network->work_go_online, do_go_online); 4308c2ecf20Sopenharmony_ci INIT_WORK(&network->work_go_offline, do_go_offline); 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci ipwireless_associate_network(hw, network); 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci return network; 4358c2ecf20Sopenharmony_ci} 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_civoid ipwireless_network_free(struct ipw_network *network) 4388c2ecf20Sopenharmony_ci{ 4398c2ecf20Sopenharmony_ci network->shutting_down = 1; 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci ipwireless_ppp_close(network); 4428c2ecf20Sopenharmony_ci flush_work(&network->work_go_online); 4438c2ecf20Sopenharmony_ci flush_work(&network->work_go_offline); 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci ipwireless_stop_interrupts(network->hardware); 4468c2ecf20Sopenharmony_ci ipwireless_associate_network(network->hardware, NULL); 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci kfree(network); 4498c2ecf20Sopenharmony_ci} 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_civoid ipwireless_associate_network_tty(struct ipw_network *network, 4528c2ecf20Sopenharmony_ci unsigned int channel_idx, 4538c2ecf20Sopenharmony_ci struct ipw_tty *tty) 4548c2ecf20Sopenharmony_ci{ 4558c2ecf20Sopenharmony_ci int i; 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci for (i = 0; i < MAX_ASSOCIATED_TTYS; i++) 4588c2ecf20Sopenharmony_ci if (network->associated_ttys[channel_idx][i] == NULL) { 4598c2ecf20Sopenharmony_ci network->associated_ttys[channel_idx][i] = tty; 4608c2ecf20Sopenharmony_ci break; 4618c2ecf20Sopenharmony_ci } 4628c2ecf20Sopenharmony_ci} 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_civoid ipwireless_disassociate_network_ttys(struct ipw_network *network, 4658c2ecf20Sopenharmony_ci unsigned int channel_idx) 4668c2ecf20Sopenharmony_ci{ 4678c2ecf20Sopenharmony_ci int i; 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci for (i = 0; i < MAX_ASSOCIATED_TTYS; i++) 4708c2ecf20Sopenharmony_ci network->associated_ttys[channel_idx][i] = NULL; 4718c2ecf20Sopenharmony_ci} 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_civoid ipwireless_ppp_open(struct ipw_network *network) 4748c2ecf20Sopenharmony_ci{ 4758c2ecf20Sopenharmony_ci if (ipwireless_debug) 4768c2ecf20Sopenharmony_ci printk(KERN_DEBUG IPWIRELESS_PCCARD_NAME ": online\n"); 4778c2ecf20Sopenharmony_ci schedule_work(&network->work_go_online); 4788c2ecf20Sopenharmony_ci} 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_civoid ipwireless_ppp_close(struct ipw_network *network) 4818c2ecf20Sopenharmony_ci{ 4828c2ecf20Sopenharmony_ci /* Disconnect from the wireless network. */ 4838c2ecf20Sopenharmony_ci if (ipwireless_debug) 4848c2ecf20Sopenharmony_ci printk(KERN_DEBUG IPWIRELESS_PCCARD_NAME ": offline\n"); 4858c2ecf20Sopenharmony_ci schedule_work(&network->work_go_offline); 4868c2ecf20Sopenharmony_ci} 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ciint ipwireless_ppp_channel_index(struct ipw_network *network) 4898c2ecf20Sopenharmony_ci{ 4908c2ecf20Sopenharmony_ci int ret = -1; 4918c2ecf20Sopenharmony_ci unsigned long flags; 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci spin_lock_irqsave(&network->lock, flags); 4948c2ecf20Sopenharmony_ci if (network->ppp_channel != NULL) 4958c2ecf20Sopenharmony_ci ret = ppp_channel_index(network->ppp_channel); 4968c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&network->lock, flags); 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci return ret; 4998c2ecf20Sopenharmony_ci} 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ciint ipwireless_ppp_unit_number(struct ipw_network *network) 5028c2ecf20Sopenharmony_ci{ 5038c2ecf20Sopenharmony_ci int ret = -1; 5048c2ecf20Sopenharmony_ci unsigned long flags; 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci spin_lock_irqsave(&network->lock, flags); 5078c2ecf20Sopenharmony_ci if (network->ppp_channel != NULL) 5088c2ecf20Sopenharmony_ci ret = ppp_unit_number(network->ppp_channel); 5098c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&network->lock, flags); 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci return ret; 5128c2ecf20Sopenharmony_ci} 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ciint ipwireless_ppp_mru(const struct ipw_network *network) 5158c2ecf20Sopenharmony_ci{ 5168c2ecf20Sopenharmony_ci return network->mru; 5178c2ecf20Sopenharmony_ci} 518