1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
2282361a0SGreg Kroah-Hartman /*
3282361a0SGreg Kroah-Hartman * IPWireless 3G PCMCIA Network Driver
4282361a0SGreg Kroah-Hartman *
5282361a0SGreg Kroah-Hartman * Original code
6282361a0SGreg Kroah-Hartman * by Stephen Blackheath <stephen@blacksapphire.com>,
7282361a0SGreg Kroah-Hartman * Ben Martel <benm@symmetric.co.nz>
8282361a0SGreg Kroah-Hartman *
9282361a0SGreg Kroah-Hartman * Copyrighted as follows:
10282361a0SGreg Kroah-Hartman * Copyright (C) 2004 by Symmetric Systems Ltd (NZ)
11282361a0SGreg Kroah-Hartman *
12282361a0SGreg Kroah-Hartman * Various driver changes and rewrites, port to new kernels
13282361a0SGreg Kroah-Hartman * Copyright (C) 2006-2007 Jiri Kosina
14282361a0SGreg Kroah-Hartman *
15282361a0SGreg Kroah-Hartman * Misc code cleanups and updates
16282361a0SGreg Kroah-Hartman * Copyright (C) 2007 David Sterba
17282361a0SGreg Kroah-Hartman */
18282361a0SGreg Kroah-Hartman
19282361a0SGreg Kroah-Hartman #include <linux/interrupt.h>
20282361a0SGreg Kroah-Hartman #include <linux/kernel.h>
21282361a0SGreg Kroah-Hartman #include <linux/mutex.h>
22282361a0SGreg Kroah-Hartman #include <linux/netdevice.h>
23282361a0SGreg Kroah-Hartman #include <linux/ppp_channel.h>
24282361a0SGreg Kroah-Hartman #include <linux/ppp_defs.h>
25282361a0SGreg Kroah-Hartman #include <linux/slab.h>
264b32da2bSPaul Mackerras #include <linux/ppp-ioctl.h>
27282361a0SGreg Kroah-Hartman #include <linux/skbuff.h>
28282361a0SGreg Kroah-Hartman
29282361a0SGreg Kroah-Hartman #include "network.h"
30282361a0SGreg Kroah-Hartman #include "hardware.h"
31282361a0SGreg Kroah-Hartman #include "main.h"
32282361a0SGreg Kroah-Hartman #include "tty.h"
33282361a0SGreg Kroah-Hartman
34282361a0SGreg Kroah-Hartman #define MAX_ASSOCIATED_TTYS 2
35282361a0SGreg Kroah-Hartman
36282361a0SGreg Kroah-Hartman #define SC_RCV_BITS (SC_RCV_B7_1|SC_RCV_B7_0|SC_RCV_ODDP|SC_RCV_EVNP)
37282361a0SGreg Kroah-Hartman
38282361a0SGreg Kroah-Hartman struct ipw_network {
39282361a0SGreg Kroah-Hartman /* Hardware context, used for calls to hardware layer. */
40282361a0SGreg Kroah-Hartman struct ipw_hardware *hardware;
41282361a0SGreg Kroah-Hartman /* Context for kernel 'generic_ppp' functionality */
42282361a0SGreg Kroah-Hartman struct ppp_channel *ppp_channel;
43282361a0SGreg Kroah-Hartman /* tty context connected with IPW console */
44282361a0SGreg Kroah-Hartman struct ipw_tty *associated_ttys[NO_OF_IPW_CHANNELS][MAX_ASSOCIATED_TTYS];
45282361a0SGreg Kroah-Hartman /* True if ppp needs waking up once we're ready to xmit */
46282361a0SGreg Kroah-Hartman int ppp_blocked;
47282361a0SGreg Kroah-Hartman /* Number of packets queued up in hardware module. */
48282361a0SGreg Kroah-Hartman int outgoing_packets_queued;
49282361a0SGreg Kroah-Hartman /* Spinlock to avoid interrupts during shutdown */
50282361a0SGreg Kroah-Hartman spinlock_t lock;
51282361a0SGreg Kroah-Hartman struct mutex close_lock;
52282361a0SGreg Kroah-Hartman
53282361a0SGreg Kroah-Hartman /* PPP ioctl data, not actually used anywere */
54282361a0SGreg Kroah-Hartman unsigned int flags;
55282361a0SGreg Kroah-Hartman unsigned int rbits;
56282361a0SGreg Kroah-Hartman u32 xaccm[8];
57282361a0SGreg Kroah-Hartman u32 raccm;
58282361a0SGreg Kroah-Hartman int mru;
59282361a0SGreg Kroah-Hartman
60282361a0SGreg Kroah-Hartman int shutting_down;
61282361a0SGreg Kroah-Hartman unsigned int ras_control_lines;
62282361a0SGreg Kroah-Hartman
63282361a0SGreg Kroah-Hartman struct work_struct work_go_online;
64282361a0SGreg Kroah-Hartman struct work_struct work_go_offline;
65282361a0SGreg Kroah-Hartman };
66282361a0SGreg Kroah-Hartman
notify_packet_sent(void * callback_data,unsigned int packet_length)67282361a0SGreg Kroah-Hartman static void notify_packet_sent(void *callback_data, unsigned int packet_length)
68282361a0SGreg Kroah-Hartman {
69282361a0SGreg Kroah-Hartman struct ipw_network *network = callback_data;
70282361a0SGreg Kroah-Hartman unsigned long flags;
71282361a0SGreg Kroah-Hartman
72282361a0SGreg Kroah-Hartman spin_lock_irqsave(&network->lock, flags);
73282361a0SGreg Kroah-Hartman network->outgoing_packets_queued--;
74282361a0SGreg Kroah-Hartman if (network->ppp_channel != NULL) {
75282361a0SGreg Kroah-Hartman if (network->ppp_blocked) {
76282361a0SGreg Kroah-Hartman network->ppp_blocked = 0;
77282361a0SGreg Kroah-Hartman spin_unlock_irqrestore(&network->lock, flags);
78282361a0SGreg Kroah-Hartman ppp_output_wakeup(network->ppp_channel);
79282361a0SGreg Kroah-Hartman if (ipwireless_debug)
80282361a0SGreg Kroah-Hartman printk(KERN_DEBUG IPWIRELESS_PCCARD_NAME
81282361a0SGreg Kroah-Hartman ": ppp unblocked\n");
82282361a0SGreg Kroah-Hartman } else
83282361a0SGreg Kroah-Hartman spin_unlock_irqrestore(&network->lock, flags);
84282361a0SGreg Kroah-Hartman } else
85282361a0SGreg Kroah-Hartman spin_unlock_irqrestore(&network->lock, flags);
86282361a0SGreg Kroah-Hartman }
87282361a0SGreg Kroah-Hartman
88282361a0SGreg Kroah-Hartman /*
89282361a0SGreg Kroah-Hartman * Called by the ppp system when it has a packet to send to the hardware.
90282361a0SGreg Kroah-Hartman */
ipwireless_ppp_start_xmit(struct ppp_channel * ppp_channel,struct sk_buff * skb)91282361a0SGreg Kroah-Hartman static int ipwireless_ppp_start_xmit(struct ppp_channel *ppp_channel,
92282361a0SGreg Kroah-Hartman struct sk_buff *skb)
93282361a0SGreg Kroah-Hartman {
94282361a0SGreg Kroah-Hartman struct ipw_network *network = ppp_channel->private;
95282361a0SGreg Kroah-Hartman unsigned long flags;
96282361a0SGreg Kroah-Hartman
97282361a0SGreg Kroah-Hartman spin_lock_irqsave(&network->lock, flags);
98282361a0SGreg Kroah-Hartman if (network->outgoing_packets_queued < ipwireless_out_queue) {
99282361a0SGreg Kroah-Hartman unsigned char *buf;
100282361a0SGreg Kroah-Hartman static unsigned char header[] = {
101282361a0SGreg Kroah-Hartman PPP_ALLSTATIONS, /* 0xff */
102282361a0SGreg Kroah-Hartman PPP_UI, /* 0x03 */
103282361a0SGreg Kroah-Hartman };
104282361a0SGreg Kroah-Hartman int ret;
105282361a0SGreg Kroah-Hartman
106282361a0SGreg Kroah-Hartman network->outgoing_packets_queued++;
107282361a0SGreg Kroah-Hartman spin_unlock_irqrestore(&network->lock, flags);
108282361a0SGreg Kroah-Hartman
109282361a0SGreg Kroah-Hartman /*
110282361a0SGreg Kroah-Hartman * If we have the requested amount of headroom in the skb we
111282361a0SGreg Kroah-Hartman * were handed, then we can add the header efficiently.
112282361a0SGreg Kroah-Hartman */
113282361a0SGreg Kroah-Hartman if (skb_headroom(skb) >= 2) {
114282361a0SGreg Kroah-Hartman memcpy(skb_push(skb, 2), header, 2);
115282361a0SGreg Kroah-Hartman ret = ipwireless_send_packet(network->hardware,
116282361a0SGreg Kroah-Hartman IPW_CHANNEL_RAS, skb->data,
117282361a0SGreg Kroah-Hartman skb->len,
118282361a0SGreg Kroah-Hartman notify_packet_sent,
119282361a0SGreg Kroah-Hartman network);
120*db332356STong Zhang if (ret < 0) {
121282361a0SGreg Kroah-Hartman skb_pull(skb, 2);
122282361a0SGreg Kroah-Hartman return 0;
123282361a0SGreg Kroah-Hartman }
124282361a0SGreg Kroah-Hartman } else {
125282361a0SGreg Kroah-Hartman /* Otherwise (rarely) we do it inefficiently. */
126282361a0SGreg Kroah-Hartman buf = kmalloc(skb->len + 2, GFP_ATOMIC);
127282361a0SGreg Kroah-Hartman if (!buf)
128282361a0SGreg Kroah-Hartman return 0;
129282361a0SGreg Kroah-Hartman memcpy(buf + 2, skb->data, skb->len);
130282361a0SGreg Kroah-Hartman memcpy(buf, header, 2);
131282361a0SGreg Kroah-Hartman ret = ipwireless_send_packet(network->hardware,
132282361a0SGreg Kroah-Hartman IPW_CHANNEL_RAS, buf,
133282361a0SGreg Kroah-Hartman skb->len + 2,
134282361a0SGreg Kroah-Hartman notify_packet_sent,
135282361a0SGreg Kroah-Hartman network);
136282361a0SGreg Kroah-Hartman kfree(buf);
137*db332356STong Zhang if (ret < 0)
138282361a0SGreg Kroah-Hartman return 0;
139282361a0SGreg Kroah-Hartman }
140282361a0SGreg Kroah-Hartman kfree_skb(skb);
141282361a0SGreg Kroah-Hartman return 1;
142282361a0SGreg Kroah-Hartman } else {
143282361a0SGreg Kroah-Hartman /*
144282361a0SGreg Kroah-Hartman * Otherwise reject the packet, and flag that the ppp system
145282361a0SGreg Kroah-Hartman * needs to be unblocked once we are ready to send.
146282361a0SGreg Kroah-Hartman */
147282361a0SGreg Kroah-Hartman network->ppp_blocked = 1;
148282361a0SGreg Kroah-Hartman spin_unlock_irqrestore(&network->lock, flags);
149282361a0SGreg Kroah-Hartman if (ipwireless_debug)
150282361a0SGreg Kroah-Hartman printk(KERN_DEBUG IPWIRELESS_PCCARD_NAME ": ppp blocked\n");
151282361a0SGreg Kroah-Hartman return 0;
152282361a0SGreg Kroah-Hartman }
153282361a0SGreg Kroah-Hartman }
154282361a0SGreg Kroah-Hartman
155282361a0SGreg Kroah-Hartman /* Handle an ioctl call that has come in via ppp. (copy of ppp_async_ioctl() */
ipwireless_ppp_ioctl(struct ppp_channel * ppp_channel,unsigned int cmd,unsigned long arg)156282361a0SGreg Kroah-Hartman static int ipwireless_ppp_ioctl(struct ppp_channel *ppp_channel,
157282361a0SGreg Kroah-Hartman unsigned int cmd, unsigned long arg)
158282361a0SGreg Kroah-Hartman {
159282361a0SGreg Kroah-Hartman struct ipw_network *network = ppp_channel->private;
160282361a0SGreg Kroah-Hartman int err, val;
161282361a0SGreg Kroah-Hartman u32 accm[8];
162282361a0SGreg Kroah-Hartman int __user *user_arg = (int __user *) arg;
163282361a0SGreg Kroah-Hartman
164282361a0SGreg Kroah-Hartman err = -EFAULT;
165282361a0SGreg Kroah-Hartman switch (cmd) {
166282361a0SGreg Kroah-Hartman case PPPIOCGFLAGS:
167282361a0SGreg Kroah-Hartman val = network->flags | network->rbits;
168282361a0SGreg Kroah-Hartman if (put_user(val, user_arg))
169282361a0SGreg Kroah-Hartman break;
170282361a0SGreg Kroah-Hartman err = 0;
171282361a0SGreg Kroah-Hartman break;
172282361a0SGreg Kroah-Hartman
173282361a0SGreg Kroah-Hartman case PPPIOCSFLAGS:
174282361a0SGreg Kroah-Hartman if (get_user(val, user_arg))
175282361a0SGreg Kroah-Hartman break;
176282361a0SGreg Kroah-Hartman network->flags = val & ~SC_RCV_BITS;
177282361a0SGreg Kroah-Hartman network->rbits = val & SC_RCV_BITS;
178282361a0SGreg Kroah-Hartman err = 0;
179282361a0SGreg Kroah-Hartman break;
180282361a0SGreg Kroah-Hartman
181282361a0SGreg Kroah-Hartman case PPPIOCGASYNCMAP:
182282361a0SGreg Kroah-Hartman if (put_user(network->xaccm[0], user_arg))
183282361a0SGreg Kroah-Hartman break;
184282361a0SGreg Kroah-Hartman err = 0;
185282361a0SGreg Kroah-Hartman break;
186282361a0SGreg Kroah-Hartman
187282361a0SGreg Kroah-Hartman case PPPIOCSASYNCMAP:
188282361a0SGreg Kroah-Hartman if (get_user(network->xaccm[0], user_arg))
189282361a0SGreg Kroah-Hartman break;
190282361a0SGreg Kroah-Hartman err = 0;
191282361a0SGreg Kroah-Hartman break;
192282361a0SGreg Kroah-Hartman
193282361a0SGreg Kroah-Hartman case PPPIOCGRASYNCMAP:
194282361a0SGreg Kroah-Hartman if (put_user(network->raccm, user_arg))
195282361a0SGreg Kroah-Hartman break;
196282361a0SGreg Kroah-Hartman err = 0;
197282361a0SGreg Kroah-Hartman break;
198282361a0SGreg Kroah-Hartman
199282361a0SGreg Kroah-Hartman case PPPIOCSRASYNCMAP:
200282361a0SGreg Kroah-Hartman if (get_user(network->raccm, user_arg))
201282361a0SGreg Kroah-Hartman break;
202282361a0SGreg Kroah-Hartman err = 0;
203282361a0SGreg Kroah-Hartman break;
204282361a0SGreg Kroah-Hartman
205282361a0SGreg Kroah-Hartman case PPPIOCGXASYNCMAP:
206282361a0SGreg Kroah-Hartman if (copy_to_user((void __user *) arg, network->xaccm,
207282361a0SGreg Kroah-Hartman sizeof(network->xaccm)))
208282361a0SGreg Kroah-Hartman break;
209282361a0SGreg Kroah-Hartman err = 0;
210282361a0SGreg Kroah-Hartman break;
211282361a0SGreg Kroah-Hartman
212282361a0SGreg Kroah-Hartman case PPPIOCSXASYNCMAP:
213282361a0SGreg Kroah-Hartman if (copy_from_user(accm, (void __user *) arg, sizeof(accm)))
214282361a0SGreg Kroah-Hartman break;
215282361a0SGreg Kroah-Hartman accm[2] &= ~0x40000000U; /* can't escape 0x5e */
216282361a0SGreg Kroah-Hartman accm[3] |= 0x60000000U; /* must escape 0x7d, 0x7e */
217282361a0SGreg Kroah-Hartman memcpy(network->xaccm, accm, sizeof(network->xaccm));
218282361a0SGreg Kroah-Hartman err = 0;
219282361a0SGreg Kroah-Hartman break;
220282361a0SGreg Kroah-Hartman
221282361a0SGreg Kroah-Hartman case PPPIOCGMRU:
222282361a0SGreg Kroah-Hartman if (put_user(network->mru, user_arg))
223282361a0SGreg Kroah-Hartman break;
224282361a0SGreg Kroah-Hartman err = 0;
225282361a0SGreg Kroah-Hartman break;
226282361a0SGreg Kroah-Hartman
227282361a0SGreg Kroah-Hartman case PPPIOCSMRU:
228282361a0SGreg Kroah-Hartman if (get_user(val, user_arg))
229282361a0SGreg Kroah-Hartman break;
230282361a0SGreg Kroah-Hartman if (val < PPP_MRU)
231282361a0SGreg Kroah-Hartman val = PPP_MRU;
232282361a0SGreg Kroah-Hartman network->mru = val;
233282361a0SGreg Kroah-Hartman err = 0;
234282361a0SGreg Kroah-Hartman break;
235282361a0SGreg Kroah-Hartman
236282361a0SGreg Kroah-Hartman default:
237282361a0SGreg Kroah-Hartman err = -ENOTTY;
238282361a0SGreg Kroah-Hartman }
239282361a0SGreg Kroah-Hartman
240282361a0SGreg Kroah-Hartman return err;
241282361a0SGreg Kroah-Hartman }
242282361a0SGreg Kroah-Hartman
243282361a0SGreg Kroah-Hartman static const struct ppp_channel_ops ipwireless_ppp_channel_ops = {
244282361a0SGreg Kroah-Hartman .start_xmit = ipwireless_ppp_start_xmit,
245282361a0SGreg Kroah-Hartman .ioctl = ipwireless_ppp_ioctl
246282361a0SGreg Kroah-Hartman };
247282361a0SGreg Kroah-Hartman
do_go_online(struct work_struct * work_go_online)248282361a0SGreg Kroah-Hartman static void do_go_online(struct work_struct *work_go_online)
249282361a0SGreg Kroah-Hartman {
250282361a0SGreg Kroah-Hartman struct ipw_network *network =
251282361a0SGreg Kroah-Hartman container_of(work_go_online, struct ipw_network,
252282361a0SGreg Kroah-Hartman work_go_online);
253282361a0SGreg Kroah-Hartman unsigned long flags;
254282361a0SGreg Kroah-Hartman
255282361a0SGreg Kroah-Hartman spin_lock_irqsave(&network->lock, flags);
256282361a0SGreg Kroah-Hartman if (!network->ppp_channel) {
257282361a0SGreg Kroah-Hartman struct ppp_channel *channel;
258282361a0SGreg Kroah-Hartman
259282361a0SGreg Kroah-Hartman spin_unlock_irqrestore(&network->lock, flags);
260282361a0SGreg Kroah-Hartman channel = kzalloc(sizeof(struct ppp_channel), GFP_KERNEL);
261282361a0SGreg Kroah-Hartman if (!channel) {
262282361a0SGreg Kroah-Hartman printk(KERN_ERR IPWIRELESS_PCCARD_NAME
263282361a0SGreg Kroah-Hartman ": unable to allocate PPP channel\n");
264282361a0SGreg Kroah-Hartman return;
265282361a0SGreg Kroah-Hartman }
266282361a0SGreg Kroah-Hartman channel->private = network;
267282361a0SGreg Kroah-Hartman channel->mtu = 16384; /* Wild guess */
268282361a0SGreg Kroah-Hartman channel->hdrlen = 2;
269282361a0SGreg Kroah-Hartman channel->ops = &ipwireless_ppp_channel_ops;
270282361a0SGreg Kroah-Hartman
271282361a0SGreg Kroah-Hartman network->flags = 0;
272282361a0SGreg Kroah-Hartman network->rbits = 0;
273282361a0SGreg Kroah-Hartman network->mru = PPP_MRU;
274282361a0SGreg Kroah-Hartman memset(network->xaccm, 0, sizeof(network->xaccm));
275282361a0SGreg Kroah-Hartman network->xaccm[0] = ~0U;
276282361a0SGreg Kroah-Hartman network->xaccm[3] = 0x60000000U;
277282361a0SGreg Kroah-Hartman 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 }
284282361a0SGreg Kroah-Hartman spin_lock_irqsave(&network->lock, flags);
285282361a0SGreg Kroah-Hartman network->ppp_channel = channel;
286282361a0SGreg Kroah-Hartman }
287282361a0SGreg Kroah-Hartman spin_unlock_irqrestore(&network->lock, flags);
288282361a0SGreg Kroah-Hartman }
289282361a0SGreg Kroah-Hartman
do_go_offline(struct work_struct * work_go_offline)290282361a0SGreg Kroah-Hartman static void do_go_offline(struct work_struct *work_go_offline)
291282361a0SGreg Kroah-Hartman {
292282361a0SGreg Kroah-Hartman struct ipw_network *network =
293282361a0SGreg Kroah-Hartman container_of(work_go_offline, struct ipw_network,
294282361a0SGreg Kroah-Hartman work_go_offline);
295282361a0SGreg Kroah-Hartman unsigned long flags;
296282361a0SGreg Kroah-Hartman
297282361a0SGreg Kroah-Hartman mutex_lock(&network->close_lock);
298282361a0SGreg Kroah-Hartman spin_lock_irqsave(&network->lock, flags);
299282361a0SGreg Kroah-Hartman if (network->ppp_channel != NULL) {
300282361a0SGreg Kroah-Hartman struct ppp_channel *channel = network->ppp_channel;
301282361a0SGreg Kroah-Hartman
302282361a0SGreg Kroah-Hartman network->ppp_channel = NULL;
303282361a0SGreg Kroah-Hartman spin_unlock_irqrestore(&network->lock, flags);
304282361a0SGreg Kroah-Hartman mutex_unlock(&network->close_lock);
305282361a0SGreg Kroah-Hartman ppp_unregister_channel(channel);
306282361a0SGreg Kroah-Hartman } else {
307282361a0SGreg Kroah-Hartman spin_unlock_irqrestore(&network->lock, flags);
308282361a0SGreg Kroah-Hartman mutex_unlock(&network->close_lock);
309282361a0SGreg Kroah-Hartman }
310282361a0SGreg Kroah-Hartman }
311282361a0SGreg Kroah-Hartman
ipwireless_network_notify_control_line_change(struct ipw_network * network,unsigned int channel_idx,unsigned int control_lines,unsigned int changed_mask)312282361a0SGreg Kroah-Hartman void ipwireless_network_notify_control_line_change(struct ipw_network *network,
313282361a0SGreg Kroah-Hartman unsigned int channel_idx,
314282361a0SGreg Kroah-Hartman unsigned int control_lines,
315282361a0SGreg Kroah-Hartman unsigned int changed_mask)
316282361a0SGreg Kroah-Hartman {
317282361a0SGreg Kroah-Hartman int i;
318282361a0SGreg Kroah-Hartman
319282361a0SGreg Kroah-Hartman if (channel_idx == IPW_CHANNEL_RAS)
320282361a0SGreg Kroah-Hartman network->ras_control_lines = control_lines;
321282361a0SGreg Kroah-Hartman
322282361a0SGreg Kroah-Hartman for (i = 0; i < MAX_ASSOCIATED_TTYS; i++) {
323282361a0SGreg Kroah-Hartman struct ipw_tty *tty =
324282361a0SGreg Kroah-Hartman network->associated_ttys[channel_idx][i];
325282361a0SGreg Kroah-Hartman
326282361a0SGreg Kroah-Hartman /*
327282361a0SGreg Kroah-Hartman * If it's associated with a tty (other than the RAS channel
328282361a0SGreg Kroah-Hartman * when we're online), then send the data to that tty. The RAS
329282361a0SGreg Kroah-Hartman * channel's data is handled above - it always goes through
330282361a0SGreg Kroah-Hartman * ppp_generic.
331282361a0SGreg Kroah-Hartman */
332282361a0SGreg Kroah-Hartman if (tty)
333282361a0SGreg Kroah-Hartman ipwireless_tty_notify_control_line_change(tty,
334282361a0SGreg Kroah-Hartman channel_idx,
335282361a0SGreg Kroah-Hartman control_lines,
336282361a0SGreg Kroah-Hartman changed_mask);
337282361a0SGreg Kroah-Hartman }
338282361a0SGreg Kroah-Hartman }
339282361a0SGreg Kroah-Hartman
340282361a0SGreg Kroah-Hartman /*
341282361a0SGreg Kroah-Hartman * Some versions of firmware stuff packets with 0xff 0x03 (PPP: ALLSTATIONS, UI)
342282361a0SGreg Kroah-Hartman * bytes, which are required on sent packet, but not always present on received
343282361a0SGreg Kroah-Hartman * packets
344282361a0SGreg Kroah-Hartman */
ipw_packet_received_skb(unsigned char * data,unsigned int length)345282361a0SGreg Kroah-Hartman static struct sk_buff *ipw_packet_received_skb(unsigned char *data,
346282361a0SGreg Kroah-Hartman unsigned int length)
347282361a0SGreg Kroah-Hartman {
348282361a0SGreg Kroah-Hartman struct sk_buff *skb;
349282361a0SGreg Kroah-Hartman
350282361a0SGreg Kroah-Hartman if (length > 2 && data[0] == PPP_ALLSTATIONS && data[1] == PPP_UI) {
351282361a0SGreg Kroah-Hartman length -= 2;
352282361a0SGreg Kroah-Hartman data += 2;
353282361a0SGreg Kroah-Hartman }
354282361a0SGreg Kroah-Hartman
355282361a0SGreg Kroah-Hartman skb = dev_alloc_skb(length + 4);
356d1519e23SAlan Cox if (skb == NULL)
357d1519e23SAlan Cox return NULL;
358282361a0SGreg Kroah-Hartman skb_reserve(skb, 2);
35959ae1d12SJohannes Berg skb_put_data(skb, data, length);
360282361a0SGreg Kroah-Hartman
361282361a0SGreg Kroah-Hartman return skb;
362282361a0SGreg Kroah-Hartman }
363282361a0SGreg Kroah-Hartman
ipwireless_network_packet_received(struct ipw_network * network,unsigned int channel_idx,unsigned char * data,unsigned int length)364282361a0SGreg Kroah-Hartman void ipwireless_network_packet_received(struct ipw_network *network,
365282361a0SGreg Kroah-Hartman unsigned int channel_idx,
366282361a0SGreg Kroah-Hartman unsigned char *data,
367282361a0SGreg Kroah-Hartman unsigned int length)
368282361a0SGreg Kroah-Hartman {
369282361a0SGreg Kroah-Hartman int i;
370282361a0SGreg Kroah-Hartman unsigned long flags;
371282361a0SGreg Kroah-Hartman
372282361a0SGreg Kroah-Hartman for (i = 0; i < MAX_ASSOCIATED_TTYS; i++) {
373282361a0SGreg Kroah-Hartman struct ipw_tty *tty = network->associated_ttys[channel_idx][i];
374282361a0SGreg Kroah-Hartman
375282361a0SGreg Kroah-Hartman if (!tty)
376282361a0SGreg Kroah-Hartman continue;
377282361a0SGreg Kroah-Hartman
378282361a0SGreg Kroah-Hartman /*
379282361a0SGreg Kroah-Hartman * If it's associated with a tty (other than the RAS channel
380282361a0SGreg Kroah-Hartman * when we're online), then send the data to that tty. The RAS
381282361a0SGreg Kroah-Hartman * channel's data is handled above - it always goes through
382282361a0SGreg Kroah-Hartman * ppp_generic.
383282361a0SGreg Kroah-Hartman */
384282361a0SGreg Kroah-Hartman if (channel_idx == IPW_CHANNEL_RAS
385282361a0SGreg Kroah-Hartman && (network->ras_control_lines &
386282361a0SGreg Kroah-Hartman IPW_CONTROL_LINE_DCD) != 0
387282361a0SGreg Kroah-Hartman && ipwireless_tty_is_modem(tty)) {
388282361a0SGreg Kroah-Hartman /*
389282361a0SGreg Kroah-Hartman * If data came in on the RAS channel and this tty is
390282361a0SGreg Kroah-Hartman * the modem tty, and we are online, then we send it to
391282361a0SGreg Kroah-Hartman * the PPP layer.
392282361a0SGreg Kroah-Hartman */
393282361a0SGreg Kroah-Hartman mutex_lock(&network->close_lock);
394282361a0SGreg Kroah-Hartman spin_lock_irqsave(&network->lock, flags);
395282361a0SGreg Kroah-Hartman if (network->ppp_channel != NULL) {
396282361a0SGreg Kroah-Hartman struct sk_buff *skb;
397282361a0SGreg Kroah-Hartman
398282361a0SGreg Kroah-Hartman spin_unlock_irqrestore(&network->lock,
399282361a0SGreg Kroah-Hartman flags);
400282361a0SGreg Kroah-Hartman
401282361a0SGreg Kroah-Hartman /* Send the data to the ppp_generic module. */
402282361a0SGreg Kroah-Hartman skb = ipw_packet_received_skb(data, length);
403d1519e23SAlan Cox if (skb)
404282361a0SGreg Kroah-Hartman ppp_input(network->ppp_channel, skb);
405282361a0SGreg Kroah-Hartman } else
406282361a0SGreg Kroah-Hartman spin_unlock_irqrestore(&network->lock,
407282361a0SGreg Kroah-Hartman flags);
408282361a0SGreg Kroah-Hartman mutex_unlock(&network->close_lock);
409282361a0SGreg Kroah-Hartman }
410282361a0SGreg Kroah-Hartman /* Otherwise we send it out the tty. */
411282361a0SGreg Kroah-Hartman else
412282361a0SGreg Kroah-Hartman ipwireless_tty_received(tty, data, length);
413282361a0SGreg Kroah-Hartman }
414282361a0SGreg Kroah-Hartman }
415282361a0SGreg Kroah-Hartman
ipwireless_network_create(struct ipw_hardware * hw)416282361a0SGreg Kroah-Hartman struct ipw_network *ipwireless_network_create(struct ipw_hardware *hw)
417282361a0SGreg Kroah-Hartman {
418282361a0SGreg Kroah-Hartman struct ipw_network *network =
41913eac05bSJia-Ju Bai kzalloc(sizeof(struct ipw_network), GFP_KERNEL);
420282361a0SGreg Kroah-Hartman
421282361a0SGreg Kroah-Hartman if (!network)
422282361a0SGreg Kroah-Hartman return NULL;
423282361a0SGreg Kroah-Hartman
424282361a0SGreg Kroah-Hartman spin_lock_init(&network->lock);
425282361a0SGreg Kroah-Hartman mutex_init(&network->close_lock);
426282361a0SGreg Kroah-Hartman
427282361a0SGreg Kroah-Hartman network->hardware = hw;
428282361a0SGreg Kroah-Hartman
429282361a0SGreg Kroah-Hartman INIT_WORK(&network->work_go_online, do_go_online);
430282361a0SGreg Kroah-Hartman INIT_WORK(&network->work_go_offline, do_go_offline);
431282361a0SGreg Kroah-Hartman
432282361a0SGreg Kroah-Hartman ipwireless_associate_network(hw, network);
433282361a0SGreg Kroah-Hartman
434282361a0SGreg Kroah-Hartman return network;
435282361a0SGreg Kroah-Hartman }
436282361a0SGreg Kroah-Hartman
ipwireless_network_free(struct ipw_network * network)437282361a0SGreg Kroah-Hartman void ipwireless_network_free(struct ipw_network *network)
438282361a0SGreg Kroah-Hartman {
439282361a0SGreg Kroah-Hartman network->shutting_down = 1;
440282361a0SGreg Kroah-Hartman
441282361a0SGreg Kroah-Hartman ipwireless_ppp_close(network);
44243829731STejun Heo flush_work(&network->work_go_online);
44343829731STejun Heo flush_work(&network->work_go_offline);
444282361a0SGreg Kroah-Hartman
445282361a0SGreg Kroah-Hartman ipwireless_stop_interrupts(network->hardware);
446282361a0SGreg Kroah-Hartman ipwireless_associate_network(network->hardware, NULL);
447282361a0SGreg Kroah-Hartman
448282361a0SGreg Kroah-Hartman kfree(network);
449282361a0SGreg Kroah-Hartman }
450282361a0SGreg Kroah-Hartman
ipwireless_associate_network_tty(struct ipw_network * network,unsigned int channel_idx,struct ipw_tty * tty)451282361a0SGreg Kroah-Hartman void ipwireless_associate_network_tty(struct ipw_network *network,
452282361a0SGreg Kroah-Hartman unsigned int channel_idx,
453282361a0SGreg Kroah-Hartman struct ipw_tty *tty)
454282361a0SGreg Kroah-Hartman {
455282361a0SGreg Kroah-Hartman int i;
456282361a0SGreg Kroah-Hartman
457282361a0SGreg Kroah-Hartman for (i = 0; i < MAX_ASSOCIATED_TTYS; i++)
458282361a0SGreg Kroah-Hartman if (network->associated_ttys[channel_idx][i] == NULL) {
459282361a0SGreg Kroah-Hartman network->associated_ttys[channel_idx][i] = tty;
460282361a0SGreg Kroah-Hartman break;
461282361a0SGreg Kroah-Hartman }
462282361a0SGreg Kroah-Hartman }
463282361a0SGreg Kroah-Hartman
ipwireless_disassociate_network_ttys(struct ipw_network * network,unsigned int channel_idx)464282361a0SGreg Kroah-Hartman void ipwireless_disassociate_network_ttys(struct ipw_network *network,
465282361a0SGreg Kroah-Hartman unsigned int channel_idx)
466282361a0SGreg Kroah-Hartman {
467282361a0SGreg Kroah-Hartman int i;
468282361a0SGreg Kroah-Hartman
469282361a0SGreg Kroah-Hartman for (i = 0; i < MAX_ASSOCIATED_TTYS; i++)
470282361a0SGreg Kroah-Hartman network->associated_ttys[channel_idx][i] = NULL;
471282361a0SGreg Kroah-Hartman }
472282361a0SGreg Kroah-Hartman
ipwireless_ppp_open(struct ipw_network * network)473282361a0SGreg Kroah-Hartman void ipwireless_ppp_open(struct ipw_network *network)
474282361a0SGreg Kroah-Hartman {
475282361a0SGreg Kroah-Hartman if (ipwireless_debug)
476282361a0SGreg Kroah-Hartman printk(KERN_DEBUG IPWIRELESS_PCCARD_NAME ": online\n");
477282361a0SGreg Kroah-Hartman schedule_work(&network->work_go_online);
478282361a0SGreg Kroah-Hartman }
479282361a0SGreg Kroah-Hartman
ipwireless_ppp_close(struct ipw_network * network)480282361a0SGreg Kroah-Hartman void ipwireless_ppp_close(struct ipw_network *network)
481282361a0SGreg Kroah-Hartman {
482282361a0SGreg Kroah-Hartman /* Disconnect from the wireless network. */
483282361a0SGreg Kroah-Hartman if (ipwireless_debug)
484282361a0SGreg Kroah-Hartman printk(KERN_DEBUG IPWIRELESS_PCCARD_NAME ": offline\n");
485282361a0SGreg Kroah-Hartman schedule_work(&network->work_go_offline);
486282361a0SGreg Kroah-Hartman }
487282361a0SGreg Kroah-Hartman
ipwireless_ppp_channel_index(struct ipw_network * network)488282361a0SGreg Kroah-Hartman int ipwireless_ppp_channel_index(struct ipw_network *network)
489282361a0SGreg Kroah-Hartman {
490282361a0SGreg Kroah-Hartman int ret = -1;
491282361a0SGreg Kroah-Hartman unsigned long flags;
492282361a0SGreg Kroah-Hartman
493282361a0SGreg Kroah-Hartman spin_lock_irqsave(&network->lock, flags);
494282361a0SGreg Kroah-Hartman if (network->ppp_channel != NULL)
495282361a0SGreg Kroah-Hartman ret = ppp_channel_index(network->ppp_channel);
496282361a0SGreg Kroah-Hartman spin_unlock_irqrestore(&network->lock, flags);
497282361a0SGreg Kroah-Hartman
498282361a0SGreg Kroah-Hartman return ret;
499282361a0SGreg Kroah-Hartman }
500282361a0SGreg Kroah-Hartman
ipwireless_ppp_unit_number(struct ipw_network * network)501282361a0SGreg Kroah-Hartman int ipwireless_ppp_unit_number(struct ipw_network *network)
502282361a0SGreg Kroah-Hartman {
503282361a0SGreg Kroah-Hartman int ret = -1;
504282361a0SGreg Kroah-Hartman unsigned long flags;
505282361a0SGreg Kroah-Hartman
506282361a0SGreg Kroah-Hartman spin_lock_irqsave(&network->lock, flags);
507282361a0SGreg Kroah-Hartman if (network->ppp_channel != NULL)
508282361a0SGreg Kroah-Hartman ret = ppp_unit_number(network->ppp_channel);
509282361a0SGreg Kroah-Hartman spin_unlock_irqrestore(&network->lock, flags);
510282361a0SGreg Kroah-Hartman
511282361a0SGreg Kroah-Hartman return ret;
512282361a0SGreg Kroah-Hartman }
513282361a0SGreg Kroah-Hartman
ipwireless_ppp_mru(const struct ipw_network * network)514282361a0SGreg Kroah-Hartman int ipwireless_ppp_mru(const struct ipw_network *network)
515282361a0SGreg Kroah-Hartman {
516282361a0SGreg Kroah-Hartman return network->mru;
517282361a0SGreg Kroah-Hartman }
518