⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 af_econet.c

📁 嵌入式系统设计与实验教材二源码linux内核移植与编译
💻 C
📖 第 1 页 / 共 2 页
字号:
/* *	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. * */#include <linux/config.h>#include <linux/module.h>#include <linux/types.h>#include <linux/kernel.h>#include <linux/sched.h>#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>#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 <asm/uaccess.h>#include <asm/system.h>#include <asm/bitops.h>static struct proto_ops econet_ops;static struct sock *econet_sklist;/* 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_AUNUDPstatic spinlock_t aun_queue_lock;static struct socket *udpsock;#define AUN_PORT	0x8000struct 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};/* *	Pull a packet from our receive queue and hand it to the user. *	If necessary we block. */static int econet_recvmsg(struct socket *sock, struct msghdr *msg, int len,			  int flags, struct scm_cookie *scm){	struct sock *sk = sock->sk;	struct sk_buff *skb;	int copied, err;	msg->msg_namelen = sizeof(struct sockaddr_ec);	/*	 *	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);	/*	 *	An error occurred so return it. Because skb_recv_datagram() 	 *	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;	sk->stamp=skb->stamp;	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:	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;	struct sock *sk=sock->sk;		/*	 *	Check legality	 */	 	if (addr_len < sizeof(struct sockaddr_ec) ||	    sec->sec_family != AF_ECONET)		return -EINVAL;		sk->protinfo.af_econet->cb = sec->cb;	sk->protinfo.af_econet->port = sec->port;	sk->protinfo.af_econet->station = sec->addr.station;	sk->protinfo.af_econet->net = sec->addr.net;	return 0;}/* *	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.\n");		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);}#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 socket *sock, struct msghdr *msg, int len,			  struct scm_cookie *scm){	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;	struct sk_buff *skb;	struct ec_cb *eb;#ifdef CONFIG_ECONET_NATIVE	unsigned short proto = 0;#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			/*	 *	Check the flags. 	 */	if (msg->msg_flags&~MSG_DONTWAIT) 		return(-EINVAL);	/*	 *	Get and verify the address. 	 */	 	if (saddr == NULL) {		addr.station = sk->protinfo.af_econet->station;		addr.net = sk->protinfo.af_econet->net;		port = sk->protinfo.af_econet->port;		cb = sk->protinfo.af_econet->cb;	} else {		if (msg->msg_namelen < sizeof(struct sockaddr_ec)) 			return -EINVAL;		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 */	if (dev == NULL)	{		dev = net2dev_map[0];		/* No interfaces at all? */		if (dev == NULL)			return -ENETDOWN;	}	if (dev->type == ARPHRD_ECONET)	{		/* Real hardware Econet.  We're not worthy etc. */#ifdef CONFIG_ECONET_NATIVE		atomic_inc(&dev->refcnt);				skb = sock_alloc_send_skb(sk, len+dev->hard_header_len+15, 					  msg->msg_flags & MSG_DONTWAIT, &err);		if (skb==NULL)			goto out_unlock;				skb_reserve(skb, (dev->hard_header_len+15)&~15);		skb->nh.raw = skb->data;				eb = (struct ec_cb *)&skb->cb;				eb->cookie = saddr->cookie;		eb->sec = *saddr;		eb->sent = ec_tx_done;		if (dev->hard_header) {			int res;			struct ec_framehdr *fh;			err = -EINVAL;			res = dev->hard_header(skb, dev, ntohs(proto), 					       &addr, NULL, len);			/* 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) {				skb->tail = skb->data;				skb->len = 0;			} else if (res < 0)				goto out_free;		}				/* 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->priority;		if (err)			goto out_free;				err = -ENETDOWN;		if (!(dev->flags & IFF_UP))			goto out_free;				/*		 *	Now send it		 */				dev_queue_xmit(skb);		dev_put(dev);		return(len);	out_free:		kfree_skb(skb);	out_unlock:		if (dev)			dev_put(dev);#else		err = -EPROTOTYPE;#endif		return err;	}#ifdef CONFIG_ECONET_AUNUDP	/* AUN virtual Econet. */	if (udpsock == NULL)		return -ENETDOWN;		/* No socket - can't send */		/* 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 = in_dev_get(dev);		unsigned long network = 0;		if (idev) {			read_lock(&idev->lock);			if (idev->ifa_list)				network = ntohl(idev->ifa_list->ifa_address) & 					0xffffff00;		/* !!! */			read_unlock(&idev->lock);			in_dev_put(idev);		}		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);	iov[0].iov_base = (void *)&ah;	iov[0].iov_len = size;	for (i = 0; i < msg->msg_iovlen; i++) {		void *base = msg->msg_iov[i].iov_base;		size_t len = msg->msg_iov[i].iov_len;		/* Check it now since we switch to KERNEL_DS later. */		if ((err = verify_area(VERIFY_READ, base, len)) < 0)			return err;		iov[i+1].iov_base = base;		iov[i+1].iov_len = len;		size += len;	}	/* Get a skbuff (no data, just holds our cb information) */	if ((skb = sock_alloc_send_skb(sk, 0, 			     msg->msg_flags & MSG_DONTWAIT, &err)) == NULL)		return err;	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	return err;}/* *	Look up the address of a socket. */static int econet_getname(struct socket *sock, struct sockaddr *uaddr,			  int *uaddr_len, int peer){	struct sock *sk = sock->sk;	struct sockaddr_ec *sec = (struct sockaddr_ec *)uaddr;	if (peer)		return -EOPNOTSUPP;	sec->sec_family = AF_ECONET;	sec->port = sk->protinfo.af_econet->port;	sec->addr.station = sk->protinfo.af_econet->station;	sec->addr.net = sk->protinfo.af_econet->net;	*uaddr_len = sizeof(*sec);	return 0;}static void econet_destroy_timer(unsigned long data){	struct sock *sk=(struct sock *)data;	if (!atomic_read(&sk->wmem_alloc) && !atomic_read(&sk->rmem_alloc)) {		sk_free(sk);		MOD_DEC_USE_COUNT;		return;	}	sk->timer.expires=jiffies+10*HZ;	add_timer(&sk->timer);	printk(KERN_DEBUG "econet socket destroy delayed\n");}/* *	Close an econet socket. */static int econet_release(struct socket *sock){	struct sock *sk = sock->sk;	if (!sk)		return 0;	sklist_remove_socket(&econet_sklist, sk);	/*	 *	Now the socket is dead. No more input will appear.	 */	sk->state_change(sk);	/* It is useless. Just for sanity. */	sock->sk = NULL;	sk->socket = NULL;	sk->dead = 1;	/* Purge queues */	skb_queue_purge(&sk->receive_queue);	if (atomic_read(&sk->rmem_alloc) || atomic_read(&sk->wmem_alloc)) {		sk->timer.data=(unsigned long)sk;		sk->timer.expires=jiffies+HZ;		sk->timer.function=econet_destroy_timer;		add_timer(&sk->timer);		return 0;	}	sk_free(sk);	MOD_DEC_USE_COUNT;	return 0;}/* *	Create an Econet socket */static int econet_create(struct socket *sock, int protocol){	struct sock *sk;	int err;	/* Econet only provides datagram services. */	if (sock->type != SOCK_DGRAM)		return -ESOCKTNOSUPPORT;	sock->state = SS_UNCONNECTED;	MOD_INC_USE_COUNT;	err = -ENOBUFS;	sk = sk_alloc(PF_ECONET, GFP_KERNEL, 1);	if (sk == NULL)		goto out;	sk->reuse = 1;	sock->ops = &econet_ops;	sock_init_data(sock,sk);	sk->protinfo.af_econet = kmalloc(sizeof(struct econet_opt), GFP_KERNEL);	if (sk->protinfo.af_econet == NULL)		goto out_free;	memset(sk->protinfo.af_econet, 0, sizeof(struct econet_opt));	sk->zapped=0;	sk->family = PF_ECONET;	sk->num = protocol;	sklist_insert_socket(&econet_sklist, sk);	return(0);out_free:	sk_free(sk);out:	MOD_DEC_USE_COUNT;	return err;}/* *	Handle Econet specific ioctls */

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -