Blame view

net/econet/af_econet.c 25.3 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
4
5
6
7
8
9
10
  /*
   *	An implementation of the Acorn Econet and AUN protocols.
   *	Philip Blundell <philb@gnu.org>
   *
   *	This program is free software; you can redistribute it and/or
   *	modify it under the terms of the GNU General Public License
   *	as published by the Free Software Foundation; either version
   *	2 of the License, or (at your option) any later version.
   *
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
11
12
13
14
  #include <linux/module.h>
  
  #include <linux/types.h>
  #include <linux/kernel.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
  #include <linux/string.h>
  #include <linux/mm.h>
  #include <linux/socket.h>
  #include <linux/sockios.h>
  #include <linux/in.h>
  #include <linux/errno.h>
  #include <linux/interrupt.h>
  #include <linux/if_ether.h>
  #include <linux/netdevice.h>
  #include <linux/inetdevice.h>
  #include <linux/route.h>
  #include <linux/inet.h>
  #include <linux/etherdevice.h>
  #include <linux/if_arp.h>
  #include <linux/wireless.h>
  #include <linux/skbuff.h>
14c850212   Arnaldo Carvalho de Melo   [INET_SOCK]: Move...
31
  #include <linux/udp.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
32
33
34
35
36
37
38
39
40
41
  #include <net/sock.h>
  #include <net/inet_common.h>
  #include <linux/stat.h>
  #include <linux/init.h>
  #include <linux/if_ec.h>
  #include <net/udp.h>
  #include <net/ip.h>
  #include <linux/spinlock.h>
  #include <linux/rcupdate.h>
  #include <linux/bitops.h>
1d1818316   David S. Miller   [ECONET]: Convert...
42
  #include <linux/mutex.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
43
44
45
  
  #include <asm/uaccess.h>
  #include <asm/system.h>
90ddc4f04   Eric Dumazet   [NET]: move struc...
46
  static const struct proto_ops econet_ops;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
47
48
  static struct hlist_head econet_sklist;
  static DEFINE_RWLOCK(econet_lock);
1d1818316   David S. Miller   [ECONET]: Convert...
49
  static DEFINE_MUTEX(econet_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
50
51
52
53
54
55
56
57
  
  /* Since there are only 256 possible network numbers (or fewer, depends
     how you count) it makes sense to use a simple lookup table. */
  static struct net_device *net2dev_map[256];
  
  #define EC_PORT_IP	0xd2
  
  #ifdef CONFIG_ECONET_AUNUDP
ca4033024   YOSHIFUJI Hideaki   [ECONET]: Use mac...
58
  static DEFINE_SPINLOCK(aun_queue_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
  static struct socket *udpsock;
  #define AUN_PORT	0x8000
  
  
  struct aunhdr
  {
  	unsigned char code;		/* AUN magic protocol byte */
  	unsigned char port;
  	unsigned char cb;
  	unsigned char pad;
  	unsigned long handle;
  };
  
  static unsigned long aun_seq;
  
  /* Queue of packets waiting to be transmitted. */
  static struct sk_buff_head aun_queue;
  static struct timer_list ab_cleanup_timer;
  
  #endif		/* CONFIG_ECONET_AUNUDP */
  
  /* Per-packet information */
  struct ec_cb
  {
  	struct sockaddr_ec sec;
  	unsigned long cookie;		/* Supplied by user. */
  #ifdef CONFIG_ECONET_AUNUDP
  	int done;
  	unsigned long seq;		/* Sequencing */
  	unsigned long timeout;		/* Timeout */
  	unsigned long start;		/* jiffies */
  #endif
  #ifdef CONFIG_ECONET_NATIVE
  	void (*sent)(struct sk_buff *, int result);
  #endif
  };
  
  static void econet_remove_socket(struct hlist_head *list, struct sock *sk)
  {
  	write_lock_bh(&econet_lock);
  	sk_del_node_init(sk);
  	write_unlock_bh(&econet_lock);
  }
  
  static void econet_insert_socket(struct hlist_head *list, struct sock *sk)
  {
  	write_lock_bh(&econet_lock);
  	sk_add_node(sk, list);
  	write_unlock_bh(&econet_lock);
  }
  
  /*
   *	Pull a packet from our receive queue and hand it to the user.
   *	If necessary we block.
   */
  
  static int econet_recvmsg(struct kiocb *iocb, struct socket *sock,
  			  struct msghdr *msg, size_t len, int flags)
  {
  	struct sock *sk = sock->sk;
  	struct sk_buff *skb;
  	size_t copied;
  	int err;
  
  	msg->msg_namelen = sizeof(struct sockaddr_ec);
1d1818316   David S. Miller   [ECONET]: Convert...
124
  	mutex_lock(&econet_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
125
126
127
128
129
130
131
132
133
134
135
136
  	/*
  	 *	Call the generic datagram receiver. This handles all sorts
  	 *	of horrible races and re-entrancy so we can forget about it
  	 *	in the protocol layers.
  	 *
  	 *	Now it will return ENETDOWN, if device have just gone down,
  	 *	but then it will block.
  	 */
  
  	skb=skb_recv_datagram(sk,flags,flags&MSG_DONTWAIT,&err);
  
  	/*
c9b6aab9c   YOSHIFUJI Hideaki   [NET] ECONET: Fix...
137
  	 *	An error occurred so return it. Because skb_recv_datagram()
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
  	 *	handles the blocking we don't see and worry about blocking
  	 *	retries.
  	 */
  
  	if(skb==NULL)
  		goto out;
  
  	/*
  	 *	You lose any data beyond the buffer you gave. If it worries a
  	 *	user program they can ask the device for its MTU anyway.
  	 */
  
  	copied = skb->len;
  	if (copied > len)
  	{
  		copied=len;
  		msg->msg_flags|=MSG_TRUNC;
  	}
  
  	/* We can't use skb_copy_datagram here */
  	err = memcpy_toiovec(msg->msg_iov, skb->data, copied);
  	if (err)
  		goto out_free;
b7aa0bf70   Eric Dumazet   [NET]: convert ne...
161
  	sk->sk_stamp = skb->tstamp;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
162
163
164
165
166
167
168
169
170
171
172
173
174
  
  	if (msg->msg_name)
  		memcpy(msg->msg_name, skb->cb, msg->msg_namelen);
  
  	/*
  	 *	Free or return the buffer as appropriate. Again this
  	 *	hides all the races and re-entrancy issues from us.
  	 */
  	err = copied;
  
  out_free:
  	skb_free_datagram(sk, skb);
  out:
1d1818316   David S. Miller   [ECONET]: Convert...
175
  	mutex_unlock(&econet_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
176
177
178
179
180
181
182
183
184
185
  	return err;
  }
  
  /*
   *	Bind an Econet socket.
   */
  
  static int econet_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
  {
  	struct sockaddr_ec *sec = (struct sockaddr_ec *)uaddr;
1d1818316   David S. Miller   [ECONET]: Convert...
186
187
  	struct sock *sk;
  	struct econet_sock *eo;
c9b6aab9c   YOSHIFUJI Hideaki   [NET] ECONET: Fix...
188

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
189
190
191
  	/*
  	 *	Check legality
  	 */
c9b6aab9c   YOSHIFUJI Hideaki   [NET] ECONET: Fix...
192

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
193
194
195
  	if (addr_len < sizeof(struct sockaddr_ec) ||
  	    sec->sec_family != AF_ECONET)
  		return -EINVAL;
c9b6aab9c   YOSHIFUJI Hideaki   [NET] ECONET: Fix...
196

1d1818316   David S. Miller   [ECONET]: Convert...
197
198
199
200
  	mutex_lock(&econet_mutex);
  
  	sk = sock->sk;
  	eo = ec_sk(sk);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
201
202
203
204
  	eo->cb	    = sec->cb;
  	eo->port    = sec->port;
  	eo->station = sec->addr.station;
  	eo->net	    = sec->addr.net;
1d1818316   David S. Miller   [ECONET]: Convert...
205
  	mutex_unlock(&econet_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
  	return 0;
  }
  
  #if defined(CONFIG_ECONET_AUNUDP) || defined(CONFIG_ECONET_NATIVE)
  /*
   *	Queue a transmit result for the user to be told about.
   */
  
  static void tx_result(struct sock *sk, unsigned long cookie, int result)
  {
  	struct sk_buff *skb = alloc_skb(0, GFP_ATOMIC);
  	struct ec_cb *eb;
  	struct sockaddr_ec *sec;
  
  	if (skb == NULL)
  	{
  		printk(KERN_DEBUG "ec: memory squeeze, transmit result dropped.
  ");
  		return;
  	}
  
  	eb = (struct ec_cb *)&skb->cb;
  	sec = (struct sockaddr_ec *)&eb->sec;
  	memset(sec, 0, sizeof(struct sockaddr_ec));
  	sec->cookie = cookie;
  	sec->type = ECTYPE_TRANSMIT_STATUS | result;
  	sec->sec_family = AF_ECONET;
  
  	if (sock_queue_rcv_skb(sk, skb) < 0)
  		kfree_skb(skb);
  }
  #endif
  
  #ifdef CONFIG_ECONET_NATIVE
  /*
   *	Called by the Econet hardware driver when a packet transmit
   *	has completed.  Tell the user.
   */
  
  static void ec_tx_done(struct sk_buff *skb, int result)
  {
  	struct ec_cb *eb = (struct ec_cb *)&skb->cb;
  	tx_result(skb->sk, eb->cookie, result);
  }
  #endif
  
  /*
   *	Send a packet.  We have to work out which device it's going out on
   *	and hence whether to use real Econet or the UDP emulation.
   */
  
  static int econet_sendmsg(struct kiocb *iocb, struct socket *sock,
  			  struct msghdr *msg, size_t len)
  {
  	struct sock *sk = sock->sk;
  	struct sockaddr_ec *saddr=(struct sockaddr_ec *)msg->msg_name;
  	struct net_device *dev;
  	struct ec_addr addr;
  	int err;
  	unsigned char port, cb;
  #if defined(CONFIG_ECONET_AUNUDP) || defined(CONFIG_ECONET_NATIVE)
  	struct sk_buff *skb;
  	struct ec_cb *eb;
  #endif
  #ifdef CONFIG_ECONET_AUNUDP
  	struct msghdr udpmsg;
  	struct iovec iov[msg->msg_iovlen+1];
  	struct aunhdr ah;
  	struct sockaddr_in udpdest;
  	__kernel_size_t size;
  	int i;
  	mm_segment_t oldfs;
  #endif
c9b6aab9c   YOSHIFUJI Hideaki   [NET] ECONET: Fix...
279

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
280
  	/*
c9b6aab9c   YOSHIFUJI Hideaki   [NET] ECONET: Fix...
281
  	 *	Check the flags.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
282
  	 */
c9b6aab9c   YOSHIFUJI Hideaki   [NET] ECONET: Fix...
283
  	if (msg->msg_flags & ~(MSG_DONTWAIT|MSG_CMSG_COMPAT))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
284
285
286
  		return -EINVAL;
  
  	/*
c9b6aab9c   YOSHIFUJI Hideaki   [NET] ECONET: Fix...
287
  	 *	Get and verify the address.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
288
  	 */
c9b6aab9c   YOSHIFUJI Hideaki   [NET] ECONET: Fix...
289

1d1818316   David S. Miller   [ECONET]: Convert...
290
  	mutex_lock(&econet_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
291
292
293
294
295
296
297
298
  	if (saddr == NULL) {
  		struct econet_sock *eo = ec_sk(sk);
  
  		addr.station = eo->station;
  		addr.net     = eo->net;
  		port	     = eo->port;
  		cb	     = eo->cb;
  	} else {
1d1818316   David S. Miller   [ECONET]: Convert...
299
300
  		if (msg->msg_namelen < sizeof(struct sockaddr_ec)) {
  			mutex_unlock(&econet_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
301
  			return -EINVAL;
1d1818316   David S. Miller   [ECONET]: Convert...
302
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
303
304
305
306
307
308
309
310
311
312
  		addr.station = saddr->addr.station;
  		addr.net = saddr->addr.net;
  		port = saddr->port;
  		cb = saddr->cb;
  	}
  
  	/* Look for a device with the right network number. */
  	dev = net2dev_map[addr.net];
  
  	/* If not directly reachable, use some default */
1d1818316   David S. Miller   [ECONET]: Convert...
313
  	if (dev == NULL) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
314
315
  		dev = net2dev_map[0];
  		/* No interfaces at all? */
1d1818316   David S. Miller   [ECONET]: Convert...
316
317
  		if (dev == NULL) {
  			mutex_unlock(&econet_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
318
  			return -ENETDOWN;
1d1818316   David S. Miller   [ECONET]: Convert...
319
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
320
  	}
1d1818316   David S. Miller   [ECONET]: Convert...
321
322
  	if (len + 15 > dev->mtu) {
  		mutex_unlock(&econet_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
323
  		return -EMSGSIZE;
1d1818316   David S. Miller   [ECONET]: Convert...
324
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
325

1d1818316   David S. Miller   [ECONET]: Convert...
326
  	if (dev->type == ARPHRD_ECONET) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
327
328
329
  		/* Real hardware Econet.  We're not worthy etc. */
  #ifdef CONFIG_ECONET_NATIVE
  		unsigned short proto = 0;
0c4e85813   Stephen Hemminger   [NET]: Wrap netde...
330
  		int res;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
331
332
  
  		dev_hold(dev);
c9b6aab9c   YOSHIFUJI Hideaki   [NET] ECONET: Fix...
333

f5184d267   Johannes Berg   net: Allow netdev...
334
  		skb = sock_alloc_send_skb(sk, len+LL_ALLOCATED_SPACE(dev),
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
335
336
337
  					  msg->msg_flags & MSG_DONTWAIT, &err);
  		if (skb==NULL)
  			goto out_unlock;
c9b6aab9c   YOSHIFUJI Hideaki   [NET] ECONET: Fix...
338

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
339
  		skb_reserve(skb, LL_RESERVED_SPACE(dev));
c1d2bbe1c   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
340
  		skb_reset_network_header(skb);
c9b6aab9c   YOSHIFUJI Hideaki   [NET] ECONET: Fix...
341

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
342
  		eb = (struct ec_cb *)&skb->cb;
c9b6aab9c   YOSHIFUJI Hideaki   [NET] ECONET: Fix...
343

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
344
345
346
347
  		/* BUG: saddr may be NULL */
  		eb->cookie = saddr->cookie;
  		eb->sec = *saddr;
  		eb->sent = ec_tx_done;
0c4e85813   Stephen Hemminger   [NET]: Wrap netde...
348
349
350
351
352
  		err = -EINVAL;
  		res = dev_hard_header(skb, dev, ntohs(proto), &addr, NULL, len);
  		if (res < 0)
  			goto out_free;
  		if (res > 0) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
353
  			struct ec_framehdr *fh;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
354
355
356
357
358
359
  			/* Poke in our control byte and
  			   port number.  Hack, hack.  */
  			fh = (struct ec_framehdr *)(skb->data);
  			fh->cb = cb;
  			fh->port = port;
  			if (sock->type != SOCK_DGRAM) {
27a884dc3   Arnaldo Carvalho de Melo   [SK_BUFF]: Conver...
360
  				skb_reset_tail_pointer(skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
361
  				skb->len = 0;
0c4e85813   Stephen Hemminger   [NET]: Wrap netde...
362
  			}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
363
  		}
c9b6aab9c   YOSHIFUJI Hideaki   [NET] ECONET: Fix...
364

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
365
366
367
368
369
370
371
  		/* Copy the data. Returns -EFAULT on error */
  		err = memcpy_fromiovec(skb_put(skb,len), msg->msg_iov, len);
  		skb->protocol = proto;
  		skb->dev = dev;
  		skb->priority = sk->sk_priority;
  		if (err)
  			goto out_free;
c9b6aab9c   YOSHIFUJI Hideaki   [NET] ECONET: Fix...
372

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
373
374
375
  		err = -ENETDOWN;
  		if (!(dev->flags & IFF_UP))
  			goto out_free;
c9b6aab9c   YOSHIFUJI Hideaki   [NET] ECONET: Fix...
376

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
377
378
379
  		/*
  		 *	Now send it
  		 */
c9b6aab9c   YOSHIFUJI Hideaki   [NET] ECONET: Fix...
380

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
381
382
  		dev_queue_xmit(skb);
  		dev_put(dev);
1d1818316   David S. Miller   [ECONET]: Convert...
383
  		mutex_unlock(&econet_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
384
385
386
387
388
389
390
391
392
393
  		return(len);
  
  	out_free:
  		kfree_skb(skb);
  	out_unlock:
  		if (dev)
  			dev_put(dev);
  #else
  		err = -EPROTOTYPE;
  #endif
1d1818316   David S. Miller   [ECONET]: Convert...
394
  		mutex_unlock(&econet_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
395
396
397
398
399
  		return err;
  	}
  
  #ifdef CONFIG_ECONET_AUNUDP
  	/* AUN virtual Econet. */
1d1818316   David S. Miller   [ECONET]: Convert...
400
401
  	if (udpsock == NULL) {
  		mutex_unlock(&econet_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
402
  		return -ENETDOWN;		/* No socket - can't send */
1d1818316   David S. Miller   [ECONET]: Convert...
403
  	}
c9b6aab9c   YOSHIFUJI Hideaki   [NET] ECONET: Fix...
404

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
405
406
407
408
409
410
411
412
413
414
415
416
417
418
  	/* Make up a UDP datagram and hand it off to some higher intellect. */
  
  	memset(&udpdest, 0, sizeof(udpdest));
  	udpdest.sin_family = AF_INET;
  	udpdest.sin_port = htons(AUN_PORT);
  
  	/* At the moment we use the stupid Acorn scheme of Econet address
  	   y.x maps to IP a.b.c.x.  This should be replaced with something
  	   more flexible and more aware of subnet masks.  */
  	{
  		struct in_device *idev;
  		unsigned long network = 0;
  
  		rcu_read_lock();
e5ed63991   Herbert Xu   [IPV4]: Replace _...
419
  		idev = __in_dev_get_rcu(dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
420
421
  		if (idev) {
  			if (idev->ifa_list)
c9b6aab9c   YOSHIFUJI Hideaki   [NET] ECONET: Fix...
422
  				network = ntohl(idev->ifa_list->ifa_address) &
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
  					0xffffff00;		/* !!! */
  		}
  		rcu_read_unlock();
  		udpdest.sin_addr.s_addr = htonl(network | addr.station);
  	}
  
  	ah.port = port;
  	ah.cb = cb & 0x7f;
  	ah.code = 2;		/* magic */
  	ah.pad = 0;
  
  	/* tack our header on the front of the iovec */
  	size = sizeof(struct aunhdr);
  	/*
  	 * XXX: that is b0rken.  We can't mix userland and kernel pointers
  	 * in iovec, since on a lot of platforms copy_from_user() will
  	 * *not* work with the kernel and userland ones at the same time,
  	 * regardless of what we do with set_fs().  And we are talking about
  	 * econet-over-ethernet here, so "it's only ARM anyway" doesn't
  	 * apply.  Any suggestions on fixing that code?		-- AV
  	 */
  	iov[0].iov_base = (void *)&ah;
  	iov[0].iov_len = size;
  	for (i = 0; i < msg->msg_iovlen; i++) {
  		void __user *base = msg->msg_iov[i].iov_base;
9e8342971   Hagen Paul Pfeifer   econet: Fix redec...
448
  		size_t iov_len = msg->msg_iov[i].iov_len;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
449
  		/* Check it now since we switch to KERNEL_DS later. */
9e8342971   Hagen Paul Pfeifer   econet: Fix redec...
450
  		if (!access_ok(VERIFY_READ, base, iov_len)) {
1d1818316   David S. Miller   [ECONET]: Convert...
451
  			mutex_unlock(&econet_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
452
  			return -EFAULT;
1d1818316   David S. Miller   [ECONET]: Convert...
453
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
454
  		iov[i+1].iov_base = base;
9e8342971   Hagen Paul Pfeifer   econet: Fix redec...
455
456
  		iov[i+1].iov_len = iov_len;
  		size += iov_len;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
457
458
459
  	}
  
  	/* Get a skbuff (no data, just holds our cb information) */
c9b6aab9c   YOSHIFUJI Hideaki   [NET] ECONET: Fix...
460
  	if ((skb = sock_alloc_send_skb(sk, 0,
1d1818316   David S. Miller   [ECONET]: Convert...
461
462
463
  				       msg->msg_flags & MSG_DONTWAIT,
  				       &err)) == NULL) {
  		mutex_unlock(&econet_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
464
  		return err;
1d1818316   David S. Miller   [ECONET]: Convert...
465
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
  
  	eb = (struct ec_cb *)&skb->cb;
  
  	eb->cookie = saddr->cookie;
  	eb->timeout = (5*HZ);
  	eb->start = jiffies;
  	ah.handle = aun_seq;
  	eb->seq = (aun_seq++);
  	eb->sec = *saddr;
  
  	skb_queue_tail(&aun_queue, skb);
  
  	udpmsg.msg_name = (void *)&udpdest;
  	udpmsg.msg_namelen = sizeof(udpdest);
  	udpmsg.msg_iov = &iov[0];
  	udpmsg.msg_iovlen = msg->msg_iovlen + 1;
  	udpmsg.msg_control = NULL;
  	udpmsg.msg_controllen = 0;
  	udpmsg.msg_flags=0;
  
  	oldfs = get_fs(); set_fs(KERNEL_DS);	/* More privs :-) */
  	err = sock_sendmsg(udpsock, &udpmsg, size);
  	set_fs(oldfs);
  #else
  	err = -EPROTOTYPE;
  #endif
1d1818316   David S. Miller   [ECONET]: Convert...
492
  	mutex_unlock(&econet_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
493
494
495
496
497
498
499
500
501
502
  	return err;
  }
  
  /*
   *	Look up the address of a socket.
   */
  
  static int econet_getname(struct socket *sock, struct sockaddr *uaddr,
  			  int *uaddr_len, int peer)
  {
1d1818316   David S. Miller   [ECONET]: Convert...
503
504
  	struct sock *sk;
  	struct econet_sock *eo;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
505
506
507
508
  	struct sockaddr_ec *sec = (struct sockaddr_ec *)uaddr;
  
  	if (peer)
  		return -EOPNOTSUPP;
80922bbb1   Eric Dumazet   econet: Fix econe...
509
  	memset(sec, 0, sizeof(*sec));
1d1818316   David S. Miller   [ECONET]: Convert...
510
511
512
513
  	mutex_lock(&econet_mutex);
  
  	sk = sock->sk;
  	eo = ec_sk(sk);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
514
515
516
517
  	sec->sec_family	  = AF_ECONET;
  	sec->port	  = eo->port;
  	sec->addr.station = eo->station;
  	sec->addr.net	  = eo->net;
1d1818316   David S. Miller   [ECONET]: Convert...
518
  	mutex_unlock(&econet_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
519
520
521
522
523
524
525
  	*uaddr_len = sizeof(*sec);
  	return 0;
  }
  
  static void econet_destroy_timer(unsigned long data)
  {
  	struct sock *sk=(struct sock *)data;
c564039fd   Eric Dumazet   net: sk_wmem_allo...
526
  	if (!sk_has_allocations(sk)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
  		sk_free(sk);
  		return;
  	}
  
  	sk->sk_timer.expires = jiffies + 10 * HZ;
  	add_timer(&sk->sk_timer);
  	printk(KERN_DEBUG "econet socket destroy delayed
  ");
  }
  
  /*
   *	Close an econet socket.
   */
  
  static int econet_release(struct socket *sock)
  {
1d1818316   David S. Miller   [ECONET]: Convert...
543
  	struct sock *sk;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
544

1d1818316   David S. Miller   [ECONET]: Convert...
545
546
547
  	mutex_lock(&econet_mutex);
  
  	sk = sock->sk;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
548
  	if (!sk)
1d1818316   David S. Miller   [ECONET]: Convert...
549
  		goto out_unlock;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
550
551
552
553
554
555
556
557
  
  	econet_remove_socket(&econet_sklist, sk);
  
  	/*
  	 *	Now the socket is dead. No more input will appear.
  	 */
  
  	sk->sk_state_change(sk);	/* It is useless. Just for sanity. */
0efffaf9d   David S. Miller   econet: Use sock_...
558
  	sock_orphan(sk);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
559
560
561
562
  
  	/* Purge queues */
  
  	skb_queue_purge(&sk->sk_receive_queue);
c564039fd   Eric Dumazet   net: sk_wmem_allo...
563
  	if (sk_has_allocations(sk)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
564
565
566
567
  		sk->sk_timer.data     = (unsigned long)sk;
  		sk->sk_timer.expires  = jiffies + HZ;
  		sk->sk_timer.function = econet_destroy_timer;
  		add_timer(&sk->sk_timer);
1d1818316   David S. Miller   [ECONET]: Convert...
568
569
  
  		goto out_unlock;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
570
571
572
  	}
  
  	sk_free(sk);
1d1818316   David S. Miller   [ECONET]: Convert...
573
574
575
  
  out_unlock:
  	mutex_unlock(&econet_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
576
577
578
579
580
581
582
583
584
585
586
587
  	return 0;
  }
  
  static struct proto econet_proto = {
  	.name	  = "ECONET",
  	.owner	  = THIS_MODULE,
  	.obj_size = sizeof(struct econet_sock),
  };
  
  /*
   *	Create an Econet socket
   */
3f378b684   Eric Paris   net: pass kern to...
588
589
  static int econet_create(struct net *net, struct socket *sock, int protocol,
  			 int kern)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
590
591
592
593
  {
  	struct sock *sk;
  	struct econet_sock *eo;
  	int err;
09ad9bc75   Octavian Purdila   net: use net_eq t...
594
  	if (!net_eq(net, &init_net))
1b8d7ae42   Eric W. Biederman   [NET]: Make socke...
595
  		return -EAFNOSUPPORT;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
596
597
598
599
600
601
602
  	/* Econet only provides datagram services. */
  	if (sock->type != SOCK_DGRAM)
  		return -ESOCKTNOSUPPORT;
  
  	sock->state = SS_UNCONNECTED;
  
  	err = -ENOBUFS;
6257ff217   Pavel Emelyanov   [NET]: Forget the...
603
  	sk = sk_alloc(net, PF_ECONET, GFP_KERNEL, &econet_proto);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
  	if (sk == NULL)
  		goto out;
  
  	sk->sk_reuse = 1;
  	sock->ops = &econet_ops;
  	sock_init_data(sock, sk);
  
  	eo = ec_sk(sk);
  	sock_reset_flag(sk, SOCK_ZAPPED);
  	sk->sk_family = PF_ECONET;
  	eo->num = protocol;
  
  	econet_insert_socket(&econet_sklist, sk);
  	return(0);
  out:
  	return err;
  }
  
  /*
   *	Handle Econet specific ioctls
   */
  
  static int ec_dev_ioctl(struct socket *sock, unsigned int cmd, void __user *arg)
  {
  	struct ifreq ifr;
  	struct ec_device *edev;
  	struct net_device *dev;
  	struct sockaddr_ec *sec;
1d1818316   David S. Miller   [ECONET]: Convert...
632
  	int err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
633
634
635
636
637
638
639
  
  	/*
  	 *	Fetch the caller's info block into kernel space
  	 */
  
  	if (copy_from_user(&ifr, arg, sizeof(struct ifreq)))
  		return -EFAULT;
881d966b4   Eric W. Biederman   [NET]: Make the d...
640
  	if ((dev = dev_get_by_name(&init_net, ifr.ifr_name)) == NULL)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
641
642
643
  		return -ENODEV;
  
  	sec = (struct sockaddr_ec *)&ifr.ifr_addr;
1d1818316   David S. Miller   [ECONET]: Convert...
644
645
646
647
  	mutex_lock(&econet_mutex);
  
  	err = 0;
  	switch (cmd) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
648
649
  	case SIOCSIFADDR:
  		edev = dev->ec_ptr;
1d1818316   David S. Miller   [ECONET]: Convert...
650
  		if (edev == NULL) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
651
  			/* Magic up a new one. */
0da974f4f   Panagiotis Issaris   [NET]: Conversion...
652
  			edev = kzalloc(sizeof(struct ec_device), GFP_KERNEL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
653
  			if (edev == NULL) {
1d1818316   David S. Miller   [ECONET]: Convert...
654
655
  				err = -ENOMEM;
  				break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
656
  			}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
657
  			dev->ec_ptr = edev;
1d1818316   David S. Miller   [ECONET]: Convert...
658
  		} else
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
659
660
661
662
663
664
  			net2dev_map[edev->net] = NULL;
  		edev->station = sec->addr.station;
  		edev->net = sec->addr.net;
  		net2dev_map[sec->addr.net] = dev;
  		if (!net2dev_map[0])
  			net2dev_map[0] = dev;
1d1818316   David S. Miller   [ECONET]: Convert...
665
  		break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
666
667
668
  
  	case SIOCGIFADDR:
  		edev = dev->ec_ptr;
1d1818316   David S. Miller   [ECONET]: Convert...
669
670
671
  		if (edev == NULL) {
  			err = -ENODEV;
  			break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
672
673
674
675
676
677
678
  		}
  		memset(sec, 0, sizeof(struct sockaddr_ec));
  		sec->addr.station = edev->station;
  		sec->addr.net = edev->net;
  		sec->sec_family = AF_ECONET;
  		dev_put(dev);
  		if (copy_to_user(arg, &ifr, sizeof(struct ifreq)))
1d1818316   David S. Miller   [ECONET]: Convert...
679
680
681
682
683
684
  			err = -EFAULT;
  		break;
  
  	default:
  		err = -EINVAL;
  		break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
685
  	}
1d1818316   David S. Miller   [ECONET]: Convert...
686
  	mutex_unlock(&econet_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
687
  	dev_put(dev);
1d1818316   David S. Miller   [ECONET]: Convert...
688
689
  
  	return err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
690
691
692
693
694
695
696
697
698
699
700
701
702
703
  }
  
  /*
   *	Handle generic ioctls
   */
  
  static int econet_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
  {
  	struct sock *sk = sock->sk;
  	void __user *argp = (void __user *)arg;
  
  	switch(cmd) {
  		case SIOCGSTAMP:
  			return sock_get_timestamp(sk, argp);
ae40eb1ef   Eric Dumazet   [NET]: Introduce ...
704
705
  		case SIOCGSTAMPNS:
  			return sock_get_timestampns(sk, argp);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
706
707
708
709
710
711
  		case SIOCSIFADDR:
  		case SIOCGIFADDR:
  			return ec_dev_ioctl(sock, cmd, argp);
  			break;
  
  		default:
b5e5fa5e0   Christoph Hellwig   [NET]: Add a dev_...
712
  			return -ENOIOCTLCMD;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
713
714
715
716
  	}
  	/*NOTREACHED*/
  	return 0;
  }
ec1b4cf74   Stephen Hemminger   net: mark net_pro...
717
  static const struct net_proto_family econet_family_ops = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
718
719
720
721
  	.family =	PF_ECONET,
  	.create =	econet_create,
  	.owner	=	THIS_MODULE,
  };
1d1818316   David S. Miller   [ECONET]: Convert...
722
  static const struct proto_ops econet_ops = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
723
724
725
726
727
728
729
  	.family =	PF_ECONET,
  	.owner =	THIS_MODULE,
  	.release =	econet_release,
  	.bind =		econet_bind,
  	.connect =	sock_no_connect,
  	.socketpair =	sock_no_socketpair,
  	.accept =	sock_no_accept,
c9b6aab9c   YOSHIFUJI Hideaki   [NET] ECONET: Fix...
730
  	.getname =	econet_getname,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
731
732
733
734
735
736
737
738
739
740
741
  	.poll =		datagram_poll,
  	.ioctl =	econet_ioctl,
  	.listen =	sock_no_listen,
  	.shutdown =	sock_no_shutdown,
  	.setsockopt =	sock_no_setsockopt,
  	.getsockopt =	sock_no_getsockopt,
  	.sendmsg =	econet_sendmsg,
  	.recvmsg =	econet_recvmsg,
  	.mmap =		sock_no_mmap,
  	.sendpage =	sock_no_sendpage,
  };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
742
743
744
745
746
747
748
749
750
751
752
753
754
  #if defined(CONFIG_ECONET_AUNUDP) || defined(CONFIG_ECONET_NATIVE)
  /*
   *	Find the listening socket, if any, for the given data.
   */
  
  static struct sock *ec_listening_socket(unsigned char port, unsigned char
  				 station, unsigned char net)
  {
  	struct sock *sk;
  	struct hlist_node *node;
  
  	sk_for_each(sk, node, &econet_sklist) {
  		struct econet_sock *opt = ec_sk(sk);
c9b6aab9c   YOSHIFUJI Hideaki   [NET] ECONET: Fix...
755
  		if ((opt->port == port || opt->port == 0) &&
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
  		    (opt->station == station || opt->station == 0) &&
  		    (opt->net == net || opt->net == 0))
  			goto found;
  	}
  	sk = NULL;
  found:
  	return sk;
  }
  
  /*
   *	Queue a received packet for a socket.
   */
  
  static int ec_queue_packet(struct sock *sk, struct sk_buff *skb,
  			   unsigned char stn, unsigned char net,
  			   unsigned char cb, unsigned char port)
  {
  	struct ec_cb *eb = (struct ec_cb *)&skb->cb;
  	struct sockaddr_ec *sec = (struct sockaddr_ec *)&eb->sec;
  
  	memset(sec, 0, sizeof(struct sockaddr_ec));
  	sec->sec_family = AF_ECONET;
  	sec->type = ECTYPE_PACKET_RECEIVED;
  	sec->port = port;
  	sec->cb = cb;
  	sec->addr.net = net;
  	sec->addr.station = stn;
  
  	return sock_queue_rcv_skb(sk, skb);
  }
  #endif
  
  #ifdef CONFIG_ECONET_AUNUDP
  /*
c9b6aab9c   YOSHIFUJI Hideaki   [NET] ECONET: Fix...
790
   *	Send an AUN protocol response.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
791
792
793
794
795
796
797
798
799
800
801
802
   */
  
  static void aun_send_response(__u32 addr, unsigned long seq, int code, int cb)
  {
  	struct sockaddr_in sin = {
  		.sin_family = AF_INET,
  		.sin_port = htons(AUN_PORT),
  		.sin_addr = {.s_addr = addr}
  	};
  	struct aunhdr ah = {.code = code, .cb = cb, .handle = seq};
  	struct kvec iov = {.iov_base = (void *)&ah, .iov_len = sizeof(ah)};
  	struct msghdr udpmsg;
c9b6aab9c   YOSHIFUJI Hideaki   [NET] ECONET: Fix...
803

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
  	udpmsg.msg_name = (void *)&sin;
  	udpmsg.msg_namelen = sizeof(sin);
  	udpmsg.msg_control = NULL;
  	udpmsg.msg_controllen = 0;
  	udpmsg.msg_flags=0;
  
  	kernel_sendmsg(udpsock, &udpmsg, &iov, 1, sizeof(ah));
  }
  
  
  /*
   *	Handle incoming AUN packets.  Work out if anybody wants them,
   *	and send positive or negative acknowledgements as appropriate.
   */
  
  static void aun_incoming(struct sk_buff *skb, struct aunhdr *ah, size_t len)
  {
eddc9ec53   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
821
  	struct iphdr *ip = ip_hdr(skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
822
823
824
825
826
827
828
829
830
831
  	unsigned char stn = ntohl(ip->saddr) & 0xff;
  	struct sock *sk;
  	struct sk_buff *newskb;
  	struct ec_device *edev = skb->dev->ec_ptr;
  
  	if (! edev)
  		goto bad;
  
  	if ((sk = ec_listening_socket(ah->port, stn, edev->net)) == NULL)
  		goto bad;		/* Nobody wants it */
c9b6aab9c   YOSHIFUJI Hideaki   [NET] ECONET: Fix...
832
  	newskb = alloc_skb((len - sizeof(struct aunhdr) + 15) & ~15,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
833
834
835
836
837
838
839
840
  			   GFP_ATOMIC);
  	if (newskb == NULL)
  	{
  		printk(KERN_DEBUG "AUN: memory squeeze, dropping packet.
  ");
  		/* Send nack and hope sender tries again */
  		goto bad;
  	}
c9b6aab9c   YOSHIFUJI Hideaki   [NET] ECONET: Fix...
841
  	memcpy(skb_put(newskb, len - sizeof(struct aunhdr)), (void *)(ah+1),
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
  	       len - sizeof(struct aunhdr));
  
  	if (ec_queue_packet(sk, newskb, stn, edev->net, ah->cb, ah->port))
  	{
  		/* Socket is bankrupt. */
  		kfree_skb(newskb);
  		goto bad;
  	}
  
  	aun_send_response(ip->saddr, ah->handle, 3, 0);
  	return;
  
  bad:
  	aun_send_response(ip->saddr, ah->handle, 4, 0);
  }
  
  /*
   *	Handle incoming AUN transmit acknowledgements.  If the sequence
   *      number matches something in our backlog then kill it and tell
   *	the user.  If the remote took too long to reply then we may have
   *	dropped the packet already.
   */
  
  static void aun_tx_ack(unsigned long seq, int result)
  {
  	struct sk_buff *skb;
  	unsigned long flags;
  	struct ec_cb *eb;
  
  	spin_lock_irqsave(&aun_queue_lock, flags);
de1033428   David S. Miller   econet: Use SKB q...
872
  	skb_queue_walk(&aun_queue, skb) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
873
874
875
  		eb = (struct ec_cb *)&skb->cb;
  		if (eb->seq == seq)
  			goto foundit;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
876
877
878
879
880
881
882
883
  	}
  	spin_unlock_irqrestore(&aun_queue_lock, flags);
  	printk(KERN_DEBUG "AUN: unknown sequence %ld
  ", seq);
  	return;
  
  foundit:
  	tx_result(skb->sk, eb->cookie, result);
8728b834b   David S. Miller   [NET]: Kill skb->...
884
  	skb_unlink(skb, &aun_queue);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
  	spin_unlock_irqrestore(&aun_queue_lock, flags);
  	kfree_skb(skb);
  }
  
  /*
   *	Deal with received AUN frames - sort out what type of thing it is
   *	and hand it to the right function.
   */
  
  static void aun_data_available(struct sock *sk, int slen)
  {
  	int err;
  	struct sk_buff *skb;
  	unsigned char *data;
  	struct aunhdr *ah;
  	struct iphdr *ip;
  	size_t len;
  
  	while ((skb = skb_recv_datagram(sk, 0, 1, &err)) == NULL) {
  		if (err == -EAGAIN) {
  			printk(KERN_ERR "AUN: no data available?!");
  			return;
  		}
  		printk(KERN_DEBUG "AUN: recvfrom() error %d
  ", -err);
  	}
9c70220b7   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
911
  	data = skb_transport_header(skb) + sizeof(struct udphdr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
912
913
  	ah = (struct aunhdr *)data;
  	len = skb->len - sizeof(struct udphdr);
eddc9ec53   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
914
  	ip = ip_hdr(skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
  
  	switch (ah->code)
  	{
  	case 2:
  		aun_incoming(skb, ah, len);
  		break;
  	case 3:
  		aun_tx_ack(ah->handle, ECTYPE_TRANSMIT_OK);
  		break;
  	case 4:
  		aun_tx_ack(ah->handle, ECTYPE_TRANSMIT_NOT_LISTENING);
  		break;
  #if 0
  		/* This isn't quite right yet. */
  	case 5:
  		aun_send_response(ip->saddr, ah->handle, 6, ah->cb);
  		break;
  #endif
  	default:
  		printk(KERN_DEBUG "unknown AUN packet (type %d)
  ", data[0]);
  	}
  
  	skb_free_datagram(sk, skb);
  }
  
  /*
   *	Called by the timer to manage the AUN transmit queue.  If a packet
   *	was sent to a dead or nonexistent host then we will never get an
   *	acknowledgement back.  After a few seconds we need to spot this and
   *	drop the packet.
   */
  
  static void ab_cleanup(unsigned long h)
  {
de1033428   David S. Miller   econet: Use SKB q...
950
  	struct sk_buff *skb, *n;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
951
952
953
  	unsigned long flags;
  
  	spin_lock_irqsave(&aun_queue_lock, flags);
de1033428   David S. Miller   econet: Use SKB q...
954
  	skb_queue_walk_safe(&aun_queue, skb, n) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
955
  		struct ec_cb *eb = (struct ec_cb *)&skb->cb;
de1033428   David S. Miller   econet: Use SKB q...
956
  		if ((jiffies - eb->start) > eb->timeout) {
c9b6aab9c   YOSHIFUJI Hideaki   [NET] ECONET: Fix...
957
  			tx_result(skb->sk, eb->cookie,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
958
  				  ECTYPE_TRANSMIT_NOT_PRESENT);
8728b834b   David S. Miller   [NET]: Kill skb->...
959
  			skb_unlink(skb, &aun_queue);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
960
961
  			kfree_skb(skb);
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
962
963
964
965
966
967
968
969
970
971
972
973
974
  	}
  	spin_unlock_irqrestore(&aun_queue_lock, flags);
  
  	mod_timer(&ab_cleanup_timer, jiffies + (HZ*2));
  }
  
  static int __init aun_udp_initialise(void)
  {
  	int error;
  	struct sockaddr_in sin;
  
  	skb_queue_head_init(&aun_queue);
  	spin_lock_init(&aun_queue_lock);
b24b8a247   Pavel Emelyanov   [NET]: Convert in...
975
  	setup_timer(&ab_cleanup_timer, ab_cleanup, 0);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
976
  	ab_cleanup_timer.expires = jiffies + (HZ*2);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
977
978
979
980
981
982
983
984
985
986
987
988
989
  	add_timer(&ab_cleanup_timer);
  
  	memset(&sin, 0, sizeof(sin));
  	sin.sin_port = htons(AUN_PORT);
  
  	/* We can count ourselves lucky Acorn machines are too dim to
  	   speak IPv6. :-) */
  	if ((error = sock_create_kern(PF_INET, SOCK_DGRAM, 0, &udpsock)) < 0)
  	{
  		printk("AUN: socket error %d
  ", -error);
  		return error;
  	}
c9b6aab9c   YOSHIFUJI Hideaki   [NET] ECONET: Fix...
990

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
991
992
993
  	udpsock->sk->sk_reuse = 1;
  	udpsock->sk->sk_allocation = GFP_ATOMIC; /* we're going to call it
  						    from interrupts */
c9b6aab9c   YOSHIFUJI Hideaki   [NET] ECONET: Fix...
994

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
  	error = udpsock->ops->bind(udpsock, (struct sockaddr *)&sin,
  				sizeof(sin));
  	if (error < 0)
  	{
  		printk("AUN: bind error %d
  ", -error);
  		goto release;
  	}
  
  	udpsock->sk->sk_data_ready = aun_data_available;
  
  	return 0;
  
  release:
  	sock_release(udpsock);
  	udpsock = NULL;
  	return error;
  }
  #endif
  
  #ifdef CONFIG_ECONET_NATIVE
  
  /*
   *	Receive an Econet frame from a device.
   */
f2ccd8fa0   David S. Miller   [NET]: Kill skb->...
1020
  static int econet_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, struct net_device *orig_dev)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1021
1022
1023
1024
  {
  	struct ec_framehdr *hdr;
  	struct sock *sk;
  	struct ec_device *edev = dev->ec_ptr;
721499e89   YOSHIFUJI Hideaki   netns: Use net_eq...
1025
  	if (!net_eq(dev_net(dev), &init_net))
e730c1551   Eric W. Biederman   [NET]: Make packe...
1026
  		goto drop;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
  	if (skb->pkt_type == PACKET_OTHERHOST)
  		goto drop;
  
  	if (!edev)
  		goto drop;
  
  	if ((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL)
  		return NET_RX_DROP;
  
  	if (!pskb_may_pull(skb, sizeof(struct ec_framehdr)))
  		goto drop;
  
  	hdr = (struct ec_framehdr *) skb->data;
  
  	/* First check for encapsulated IP */
  	if (hdr->port == EC_PORT_IP) {
  		skb->protocol = htons(ETH_P_IP);
  		skb_pull(skb, sizeof(struct ec_framehdr));
  		netif_rx(skb);
482d804cb   Mark Smith   econet: use NET_R...
1046
  		return NET_RX_SUCCESS;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1047
1048
1049
1050
1051
1052
1053
1054
1055
  	}
  
  	sk = ec_listening_socket(hdr->port, hdr->src_stn, hdr->src_net);
  	if (!sk)
  		goto drop;
  
  	if (ec_queue_packet(sk, skb, edev->net, hdr->src_stn, hdr->cb,
  			    hdr->port))
  		goto drop;
482d804cb   Mark Smith   econet: use NET_R...
1056
  	return NET_RX_SUCCESS;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1057
1058
1059
1060
1061
  
  drop:
  	kfree_skb(skb);
  	return NET_RX_DROP;
  }
7546dd97d   Stephen Hemminger   net: convert usag...
1062
  static struct packet_type econet_packet_type __read_mostly = {
09640e636   Harvey Harrison   net: replace uses...
1063
  	.type =		cpu_to_be16(ETH_P_ECONET),
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
  	.func =		econet_rcv,
  };
  
  static void econet_hw_initialise(void)
  {
  	dev_add_pack(&econet_packet_type);
  }
  
  #endif
  
  static int econet_notifier(struct notifier_block *this, unsigned long msg, void *data)
  {
  	struct net_device *dev = (struct net_device *)data;
  	struct ec_device *edev;
721499e89   YOSHIFUJI Hideaki   netns: Use net_eq...
1078
  	if (!net_eq(dev_net(dev), &init_net))
e9dc86534   Eric W. Biederman   [NET]: Make devic...
1079
  		return NOTIFY_DONE;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
  	switch (msg) {
  	case NETDEV_UNREGISTER:
  		/* A device has gone down - kill any data we hold for it. */
  		edev = dev->ec_ptr;
  		if (edev)
  		{
  			if (net2dev_map[0] == dev)
  				net2dev_map[0] = NULL;
  			net2dev_map[edev->net] = NULL;
  			kfree(edev);
  			dev->ec_ptr = NULL;
  		}
  		break;
  	}
  
  	return NOTIFY_DONE;
  }
  
  static struct notifier_block econet_netdev_notifier = {
  	.notifier_call =econet_notifier,
  };
  
  static void __exit econet_proto_exit(void)
  {
  #ifdef CONFIG_ECONET_AUNUDP
  	del_timer(&ab_cleanup_timer);
  	if (udpsock)
  		sock_release(udpsock);
  #endif
  	unregister_netdevice_notifier(&econet_netdev_notifier);
9c29a377f   Alexey Dobriyan   [ECONET]: remove ...
1110
1111
1112
  #ifdef CONFIG_ECONET_NATIVE
  	dev_remove_pack(&econet_packet_type);
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
  	sock_unregister(econet_family_ops.family);
  	proto_unregister(&econet_proto);
  }
  
  static int __init econet_proto_init(void)
  {
  	int err = proto_register(&econet_proto, 0);
  
  	if (err != 0)
  		goto out;
  	sock_register(&econet_family_ops);
  #ifdef CONFIG_ECONET_AUNUDP
  	spin_lock_init(&aun_queue_lock);
  	aun_udp_initialise();
  #endif
  #ifdef CONFIG_ECONET_NATIVE
  	econet_hw_initialise();
  #endif
  	register_netdevice_notifier(&econet_netdev_notifier);
  out:
  	return err;
  }
  
  module_init(econet_proto_init);
  module_exit(econet_proto_exit);
  
  MODULE_LICENSE("GPL");
  MODULE_ALIAS_NETPROTO(PF_ECONET);