diff options
author | Ralf Baechle <ralf@linux-mips.org> | 2000-11-28 03:58:46 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 2000-11-28 03:58:46 +0000 |
commit | b63ad0882a16a5d28003e57f2b0b81dee3fb322b (patch) | |
tree | 0a343ce219e2b8b38a5d702d66032c57b83d9720 /net | |
parent | a9d7bff9a84dba79609a0002e5321b74c4d64c64 (diff) |
Merge with 2.4.0-test11.
Diffstat (limited to 'net')
78 files changed, 5346 insertions, 1456 deletions
diff --git a/net/Makefile b/net/Makefile index ff79d8584..ddfdab2a5 100644 --- a/net/Makefile +++ b/net/Makefile @@ -7,7 +7,7 @@ O_TARGET := network.o -mod-subdirs := ipv4/netfilter ipv6/netfilter ipx irda atm netlink +mod-subdirs := ipv4/netfilter ipv6/netfilter ipx irda atm netlink sched export-objs := netsyms.o subdir-y := core ethernet diff --git a/net/README b/net/README index 7490dff2b..cacc02146 100644 --- a/net/README +++ b/net/README @@ -7,7 +7,7 @@ Code Section Bug Report Contact [token ring ] p.norton@computer.org appletalk jschlst@turbolinux.com ax25 g4klx@g4klx.demon.co.uk -bridge buytenh@openrock.net +bridge buytenh@gnu.org core alan@lxorguk.ukuu.org.uk decnet SteveW@ACM.org ethernet alan@lxorguk.ukuu.org.uk diff --git a/net/atm/pvc.c b/net/atm/pvc.c index ff28ed551..c47d6ca5c 100644 --- a/net/atm/pvc.c +++ b/net/atm/pvc.c @@ -130,7 +130,7 @@ static int __init atmpvc_init(void) error = sock_register(&pvc_family_ops); if (error < 0) { printk(KERN_ERR "ATMPVC: can't register (%d)",error); - return; + return error; } #ifdef CONFIG_ATM_CLIP atm_clip_init(); diff --git a/net/atm/signaling.c b/net/atm/signaling.c index dc7998fd1..0b3d64941 100644 --- a/net/atm/signaling.c +++ b/net/atm/signaling.c @@ -50,6 +50,7 @@ static void sigd_put_skb(struct sk_buff *skb) } schedule(); } + current->state = TASK_RUNNING; remove_wait_queue(&sigd_sleep,&wait); #else if (!sigd) { diff --git a/net/atm/svc.c b/net/atm/svc.c index 7984cb22a..e53826b98 100644 --- a/net/atm/svc.c +++ b/net/atm/svc.c @@ -205,7 +205,6 @@ static int svc_connect(struct socket *sock,struct sockaddr *sockaddr, set_current_state(TASK_UNINTERRUPTIBLE); schedule(); } - remove_wait_queue(&vcc->sleep,&wait); clear_bit(ATM_VF_REGIS,&vcc->flags); clear_bit(ATM_VF_RELEASED,&vcc->flags); clear_bit(ATM_VF_CLOSE,&vcc->flags); diff --git a/net/ax25/sysctl_net_ax25.c b/net/ax25/sysctl_net_ax25.c index 2a88a9716..c01ae329c 100644 --- a/net/ax25/sysctl_net_ax25.c +++ b/net/ax25/sysctl_net_ax25.c @@ -114,15 +114,18 @@ void ax25_register_sysctl(void) memset(ax25_table, 0x00, ax25_table_size); for (n = 0, ax25_dev = ax25_dev_list; ax25_dev != NULL; ax25_dev = ax25_dev->next) { + ctl_table *child = kmalloc(sizeof(ax25_param_table), GFP_ATOMIC); + if (!child) { + while (n--) + kfree(ax25_table[n].child); + kfree(ax25_table); + return; + } + memcpy(child, ax25_param_table, sizeof(ax25_param_table)); + ax25_table[n].child = ax25_dev->systable = child; ax25_table[n].ctl_name = n + 1; ax25_table[n].procname = ax25_dev->dev->name; - ax25_table[n].data = NULL; - ax25_table[n].maxlen = 0; ax25_table[n].mode = 0555; - ax25_table[n].child = ax25_dev->systable; - ax25_table[n].proc_handler = NULL; - - memcpy(ax25_dev->systable, ax25_param_table, sizeof(ax25_dev->systable)); #ifndef CONFIG_AX25_DAMA_SLAVE /* @@ -131,13 +134,13 @@ void ax25_register_sysctl(void) * AX.25 DAMA slave code, do we? */ - ax25_dev->systable[AX25_VALUES_DS_TIMEOUT].procname = NULL; + child[AX25_VALUES_DS_TIMEOUT].procname = NULL; #endif - ax25_dev->systable[AX25_MAX_VALUES].ctl_name = 0; /* just in case... */ + child[AX25_MAX_VALUES].ctl_name = 0; /* just in case... */ for (k = 0; k < AX25_MAX_VALUES; k++) - ax25_dev->systable[k].data = &ax25_dev->values[k]; + child[k].data = &ax25_dev->values[k]; n++; } @@ -149,9 +152,11 @@ void ax25_register_sysctl(void) void ax25_unregister_sysctl(void) { + ctl_table *p; unregister_sysctl_table(ax25_table_header); - kfree(ax25_table); - ax25_dir_table[0].child = NULL; + for (p = ax25_table; p->ctl_name; p++) + kfree(p->child); + kfree(ax25_table); } diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c index be9d1867a..f942bfd45 100644 --- a/net/bridge/br_fdb.c +++ b/net/bridge/br_fdb.c @@ -5,7 +5,7 @@ * Authors: * Lennert Buytenhek <buytenh@gnu.org> * - * $Id: br_fdb.c,v 1.4 2000/02/24 06:16:45 davem Exp $ + * $Id: br_fdb.c,v 1.5 2000/11/08 05:16:40 davem Exp $ * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -43,6 +43,7 @@ static __inline__ int has_expired(struct net_bridge *br, static __inline__ void copy_fdb(struct __fdb_entry *ent, struct net_bridge_fdb_entry *f) { + memset(ent, 0, sizeof(struct __fdb_entry)); memcpy(ent->mac_addr, f->addr.addr, ETH_ALEN); ent->port_no = f->dst?f->dst->port_no:0; ent->is_local = f->is_local; diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c index 295cfc973..08c58f19f 100644 --- a/net/bridge/br_if.c +++ b/net/bridge/br_if.c @@ -5,7 +5,7 @@ * Authors: * Lennert Buytenhek <buytenh@gnu.org> * - * $Id: br_if.c,v 1.4 2000/10/05 01:58:16 davem Exp $ + * $Id: br_if.c,v 1.5 2000/11/08 05:16:40 davem Exp $ * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -279,12 +279,8 @@ int br_get_bridge_ifindices(int *indices, int num) /* called under ioctl_lock */ void br_get_port_ifindices(struct net_bridge *br, int *ifindices) { - int i; struct net_bridge_port *p; - for (i=0;i<256;i++) - ifindices[i] = 0; - p = br->port_list; while (p != NULL) { ifindices[p->port_no] = p->dev->ifindex; diff --git a/net/bridge/br_ioctl.c b/net/bridge/br_ioctl.c index 4d04aec66..840b3641e 100644 --- a/net/bridge/br_ioctl.c +++ b/net/bridge/br_ioctl.c @@ -5,7 +5,7 @@ * Authors: * Lennert Buytenhek <buytenh@gnu.org> * - * $Id: br_ioctl.c,v 1.3 2000/10/05 01:58:16 davem Exp $ + * $Id: br_ioctl.c,v 1.4 2000/11/08 05:16:40 davem Exp $ * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -53,6 +53,7 @@ static int br_ioctl_device(struct net_bridge *br, { struct __bridge_info b; + memset(&b, 0, sizeof(struct __bridge_info)); memcpy(&b.designated_root, &br->designated_root, 8); memcpy(&b.bridge_id, &br->bridge_id, 8); b.root_path_cost = br->root_path_cost; @@ -81,8 +82,12 @@ static int br_ioctl_device(struct net_bridge *br, case BRCTL_GET_PORT_LIST: { + int i; int indices[256]; + for (i=0;i<256;i++) + indices[i] = 0; + br_get_port_ifindices(br, indices); if (copy_to_user((void *)arg0, indices, 256*sizeof(int))) return -EFAULT; @@ -124,6 +129,7 @@ static int br_ioctl_device(struct net_bridge *br, if ((pt = br_get_port(br, arg1)) == NULL) return -EINVAL; + memset(&p, 0, sizeof(struct __port_info)); memcpy(&p.designated_root, &pt->designated_root, 8); memcpy(&p.designated_bridge, &pt->designated_bridge, 8); p.port_id = pt->port_id; @@ -189,8 +195,12 @@ static int br_ioctl_deviceless(unsigned int cmd, case BRCTL_GET_BRIDGES: { + int i; int indices[64]; + for (i=0;i<64;i++) + indices[i] = 0; + if (arg1 > 64) arg1 = 64; arg1 = br_get_bridge_ifindices(indices, arg1); diff --git a/net/core/datagram.c b/net/core/datagram.c index 7f85645f0..0b865f6b9 100644 --- a/net/core/datagram.c +++ b/net/core/datagram.c @@ -66,7 +66,7 @@ static int wait_for_packet(struct sock * sk, int *err, long *timeo_p) DECLARE_WAITQUEUE(wait, current); - __set_current_state(TASK_INTERRUPTIBLE|TASK_EXCLUSIVE); + __set_current_state(TASK_INTERRUPTIBLE); add_wait_queue_exclusive(sk->sleep, &wait); /* Socket errors? */ diff --git a/net/core/dev.c b/net/core/dev.c index 17fae7a1e..1e5b59c3d 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -93,6 +93,7 @@ #include <net/profile.h> #include <linux/init.h> #include <linux/kmod.h> +#include <linux/module.h> #if defined(CONFIG_NET_RADIO) || defined(CONFIG_NET_PCMCIA_RADIO) #include <linux/wireless.h> /* Note : will define WIRELESS_EXT */ #endif /* CONFIG_NET_RADIO || CONFIG_NET_PCMCIA_RADIO */ @@ -666,9 +667,15 @@ int dev_open(struct net_device *dev) /* * Call device private open method */ - - if (dev->open) - ret = dev->open(dev); + if (try_inc_mod_count(dev->owner)) { + if (dev->open) { + ret = dev->open(dev); + if (ret != 0 && dev->owner) + __MOD_DEC_USE_COUNT(dev->owner); + } + } else { + ret = -ENODEV; + } /* * If it went open OK then: @@ -784,6 +791,12 @@ int dev_close(struct net_device *dev) */ notifier_call_chain(&netdev_chain, NETDEV_DOWN, dev); + /* + * Drop the module refcount + */ + if (dev->owner) + __MOD_DEC_USE_COUNT(dev->owner); + return(0); } @@ -2056,8 +2069,9 @@ static int dev_ifsioc(struct ifreq *ifr, unsigned int cmd) */ default: - if (cmd >= SIOCDEVPRIVATE && - cmd <= SIOCDEVPRIVATE + 15) { + if ((cmd >= SIOCDEVPRIVATE && + cmd <= SIOCDEVPRIVATE + 15) || + cmd == SIOCETHTOOL) { if (dev->do_ioctl) { if (!netif_device_present(dev)) return -ENODEV; @@ -2178,6 +2192,7 @@ int dev_ioctl(unsigned int cmd, void *arg) case SIOCSIFHWBROADCAST: case SIOCSIFTXQLEN: case SIOCSIFNAME: + case SIOCETHTOOL: if (!capable(CAP_NET_ADMIN)) return -EPERM; dev_load(ifr.ifr_name); @@ -2688,3 +2703,67 @@ int __init net_dev_init(void) return 0; } + +#ifdef CONFIG_HOTPLUG + +/* Notify userspace when a netdevice event occurs, + * by running '/sbin/hotplug net' with certain + * environment variables set. + * + * Currently reported events are listed in netdev_event_names[]. + */ + +/* /sbin/hotplug ONLY executes for events named here */ +static char *netdev_event_names[] = { + [NETDEV_REGISTER] = "register", + [NETDEV_UNREGISTER] = "unregister", +}; + +static int run_sbin_hotplug(struct notifier_block *this, + unsigned long event, void *ptr) +{ + struct net_device *dev = (struct net_device *) ptr; + char *argv[3], *envp[5], ifname[12 + IFNAMSIZ], action[32]; + int i; + + if ((event >= ARRAY_SIZE(netdev_event_names)) || + !netdev_event_names[event]) + return NOTIFY_DONE; + + sprintf(ifname, "INTERFACE=%s", dev->name); + sprintf(action, "ACTION=%s", netdev_event_names[event]); + + i = 0; + argv[i++] = hotplug_path; + argv[i++] = "net"; + argv[i] = 0; + + i = 0; + /* minimal command environment */ + envp [i++] = "HOME=/"; + envp [i++] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin"; + envp [i++] = ifname; + envp [i++] = action; + envp [i] = 0; + + call_usermodehelper (argv [0], argv, envp); + + return NOTIFY_DONE; +} + +static struct notifier_block sbin_hotplug = { + notifier_call: run_sbin_hotplug, +}; + +/* + * called from init/main.c, -after- all the initcalls are complete. + * Registers a hook that calls /sbin/hotplug on every netdev + * addition and removal. + */ +void __init net_notifier_init (void) +{ + if (register_netdevice_notifier(&sbin_hotplug)) + printk (KERN_WARNING "unable to register netdev notifier\n" + KERN_WARNING "/sbin/hotplug will not be run.\n"); +} +#endif diff --git a/net/core/scm.c b/net/core/scm.c index a29c21a8a..0bd1b4004 100644 --- a/net/core/scm.c +++ b/net/core/scm.c @@ -204,12 +204,16 @@ void scm_detach_fds(struct msghdr *msg, struct scm_cookie *scm) { struct cmsghdr *cm = (struct cmsghdr*)msg->msg_control; - int fdmax = (msg->msg_controllen - sizeof(struct cmsghdr))/sizeof(int); + int fdmax = 0; int fdnum = scm->fp->count; struct file **fp = scm->fp->fp; int *cmfptr; int err = 0, i; + if (msg->msg_controllen > sizeof(struct cmsghdr)) + fdmax = ((msg->msg_controllen - sizeof(struct cmsghdr)) + / sizeof(int)); + if (fdnum < fdmax) fdmax = fdnum; @@ -245,7 +249,7 @@ void scm_detach_fds(struct msghdr *msg, struct scm_cookie *scm) msg->msg_controllen -= cmlen; } } - if (i < fdnum) + if (i < fdnum || (fdnum && fdmax <= 0)) msg->msg_flags |= MSG_CTRUNC; /* diff --git a/net/core/sock.c b/net/core/sock.c index 8503e364f..7b9484437 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -7,7 +7,7 @@ * handler for protocols to use and generic option handler. * * - * Version: $Id: sock.c,v 1.100 2000/09/18 05:59:48 davem Exp $ + * Version: $Id: sock.c,v 1.101 2000/11/10 04:02:04 davem Exp $ * * Authors: Ross Biro, <bir7@leland.Stanford.Edu> * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG> @@ -819,7 +819,7 @@ void __lock_sock(struct sock *sk) add_wait_queue_exclusive(&sk->lock.wq, &wait); for(;;) { - current->state = TASK_EXCLUSIVE | TASK_UNINTERRUPTIBLE; + current->state = TASK_UNINTERRUPTIBLE; spin_unlock_bh(&sk->lock.slock); schedule(); spin_lock_bh(&sk->lock.slock); diff --git a/net/decnet/Config.in b/net/decnet/Config.in index c019d3f59..94422e124 100644 --- a/net/decnet/Config.in +++ b/net/decnet/Config.in @@ -5,6 +5,8 @@ bool ' DECnet: SIOCGIFCONF support' CONFIG_DECNET_SIOCGIFCONF if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then bool ' DECnet: router support (EXPERIMENTAL)' CONFIG_DECNET_ROUTER if [ "$CONFIG_DECNET_ROUTER" = "y" ]; then - bool ' DECnet: use FWMARK value as routing key (EXPERIMENTAL)' CONFIG_DECNET_ROUTE_FWMARK + if [ "$CONFIG_NETFILTER" = "y" ]; then + bool ' DECnet: use FWMARK value as routing key (EXPERIMENTAL)' CONFIG_DECNET_ROUTE_FWMARK + fi fi fi diff --git a/net/decnet/TODO b/net/decnet/TODO index 72ab936a2..1607d6d1b 100644 --- a/net/decnet/TODO +++ b/net/decnet/TODO @@ -21,19 +21,11 @@ Steve's quick list of things that need finishing off: send/recvmsg() calls should simply be a vector of set/getsockopt() calls] - o check MSG_TRUNC, MSG_CTRUNC are set where they should be. + o check MSG_CTRUNC is set where it should be. o Start to hack together user level software and add more DECnet support in ifconfig for example. - o Fix conninit_rx to check out each CI before queuing it. Support code is - now in place, so this should be easy. - - o Work out which errors we can return from conninit_rx. Support code is - now in place, so this should be easy. - - o Check out receiving of errors in the light of what conninit_rx can return - o Test adding/deleting of routes o Test route lookup @@ -55,7 +47,11 @@ Steve's quick list of things that need finishing off: allow DECnet support in netstat. o Make sure that returned connect messages are generated when they should - be, and that the correct error messages are sent too. Ensure that the - conninit receiving routine does not accept conninits with parameters - that we cannot handle. + be, and that the correct error messages are sent too. + + o Add the routing message grabbing netfilter module [written, tested, + awaiting merge] + + o Add perfect socket hashing - an idea suggested by Paul Koning [part written, + awaiting debugging and merge] diff --git a/net/decnet/af_decnet.c b/net/decnet/af_decnet.c index 8de443979..ca0850b4a 100644 --- a/net/decnet/af_decnet.c +++ b/net/decnet/af_decnet.c @@ -33,6 +33,8 @@ * David S. Miller: New socket locking * Steve Whitehouse: Socket list hashing/locking * Arnaldo C. Melo: use capable, not suser + * Steve Whitehouse: Removed unused code. Fix to use sk->allocation + * when required. */ @@ -538,7 +540,7 @@ static void dn_destroy_sock(struct sock *sk) switch(scp->state) { case DN_DN: - dn_nsp_send_disc(sk, NSP_DISCCONF, NSP_REASON_DC, GFP_KERNEL); + dn_nsp_send_disc(sk, NSP_DISCCONF, NSP_REASON_DC, sk->allocation); scp->persist_fxn = dn_destroy_timer; scp->persist = dn_nsp_persist(sk); break; @@ -550,7 +552,7 @@ static void dn_destroy_sock(struct sock *sk) case DN_DI: case DN_DR: disc_reject: - dn_nsp_send_disc(sk, NSP_DISCINIT, 0, GFP_KERNEL); + dn_nsp_send_disc(sk, NSP_DISCINIT, 0, sk->allocation); case DN_NC: case DN_NR: case DN_RJ: @@ -967,7 +969,7 @@ static int dn_accept(struct socket *sock, struct socket *newsock, int flags) cb = (struct dn_skb_cb *)skb->cb; - if ((newsk = dn_alloc_sock(newsock, GFP_KERNEL)) == NULL) { + if ((newsk = dn_alloc_sock(newsock, sk->allocation)) == NULL) { release_sock(sk); kfree_skb(skb); return -ENOBUFS; @@ -1031,7 +1033,7 @@ static int dn_accept(struct socket *sock, struct socket *newsock, int flags) if (newsk->protinfo.dn.accept_mode == ACC_IMMED) { newsk->protinfo.dn.state = DN_CC; - dn_send_conn_conf(newsk, GFP_KERNEL); + dn_send_conn_conf(newsk, newsk->allocation); err = dn_wait_accept(newsock, flags); } @@ -1388,7 +1390,7 @@ static int __dn_setsockopt(struct socket *sock, int level,int optname, char *opt return -EINVAL; scp->state = DN_CC; - dn_send_conn_conf(sk, GFP_KERNEL); + dn_send_conn_conf(sk, sk->allocation); err = dn_wait_accept(sock, sock->file->f_flags); return err; @@ -1399,7 +1401,7 @@ static int __dn_setsockopt(struct socket *sock, int level,int optname, char *opt scp->state = DN_DR; sk->shutdown = SHUTDOWN_MASK; - dn_nsp_send_disc(sk, 0x38, 0, GFP_KERNEL); + dn_nsp_send_disc(sk, 0x38, 0, sk->allocation); break; default: @@ -1527,7 +1529,7 @@ static int dn_wait_run(struct sock *sk, int flags) case DN_CR: scp->state = DN_CC; - dn_send_conn_conf(sk, GFP_KERNEL); + dn_send_conn_conf(sk, sk->allocation); return dn_wait_accept(sk->socket, (flags & MSG_DONTWAIT) ? O_NONBLOCK : 0); case DN_CI: case DN_CC: @@ -2083,7 +2085,7 @@ static int __init decnet_init(void) dn_dn2eth(decnet_ether_address, dn_ntohs(decnet_address)); #endif - printk(KERN_INFO "NET4: DECnet for Linux: V.2.3.49s (C) 1995-2000 Linux DECnet Project Team\n"); + printk(KERN_INFO "NET4: DECnet for Linux: V.2.4.0-test10s (C) 1995-2000 Linux DECnet Project Team\n"); sock_register(&dn_family_ops); dev_add_pack(&dn_dix_packet_type); @@ -2102,6 +2104,14 @@ static int __init decnet_init(void) #ifdef CONFIG_SYSCTL dn_register_sysctl(); #endif /* CONFIG_SYSCTL */ + + /* + * Prevent DECnet module unloading until its fixed properly. + * Requires an audit of the code to check for memory leaks and + * initialisation problems etc. + */ + MOD_INC_USE_COUNT; + return 0; } @@ -2111,7 +2121,6 @@ static int __init decnet_setup(char *str) { unsigned short area = simple_strtoul(str, &str, 0); unsigned short node = simple_strtoul(*str > 0 ? ++str : str, &str, 0); - /* unsigned short type = simple_strtoul(*str > 0 ? ++str : str, &str, 0); */ decnet_address = dn_htons(area << 10 | node); dn_dn2eth(decnet_ether_address, dn_ntohs(decnet_address)); diff --git a/net/decnet/dn_dev.c b/net/decnet/dn_dev.c index f05568ee5..d8f91ac38 100644 --- a/net/decnet/dn_dev.c +++ b/net/decnet/dn_dev.c @@ -19,6 +19,7 @@ * Steve Whitehouse : SIOCGIFCONF is now a compile time option * Steve Whitehouse : /proc/sys/net/decnet/conf/<sys>/forwarding * Steve Whitehouse : Removed timer1 - its a user space issue now + * Patrick Caulfield : Fixed router hello message format */ #include <linux/config.h> @@ -795,13 +796,14 @@ static void dn_send_router_hello(struct net_device *dev) DN_RT_INFO_L1RT : DN_RT_INFO_L2RT; *((unsigned short *)ptr) = dn_htons(dn_db->parms.blksize); ptr += 2; - *ptr++ = 0; /* Priority */ + *ptr++ = dn_db->parms.priority; /* Priority */ *ptr++ = 0; /* Area: Reserved */ *((unsigned short *)ptr) = dn_htons((unsigned short)dn_db->parms.t3); ptr += 2; *ptr++ = 0; /* MPD: Reserved */ i1 = ptr++; memset(ptr, 0, 7); /* Name: Reserved */ + ptr += 7; i2 = ptr++; n = dn_neigh_elist(dev, ptr, n); @@ -809,7 +811,7 @@ static void dn_send_router_hello(struct net_device *dev) *i2 = 7 * n; *i1 = 8 + *i2; - skb_trim(skb, (26 + *i2)); + skb_trim(skb, (27 + *i2)); pktlen = (unsigned short *)skb_push(skb, 2); *pktlen = dn_htons(skb->len - 2); diff --git a/net/decnet/dn_nsp_in.c b/net/decnet/dn_nsp_in.c index 6155ebccf..4754cd850 100644 --- a/net/decnet/dn_nsp_in.c +++ b/net/decnet/dn_nsp_in.c @@ -1,4 +1,3 @@ - /* * DECnet An implementation of the DECnet protocol suite for the LINUX * operating system. DECnet is implemented using the BSD Socket @@ -23,6 +22,9 @@ * Steve Whitehouse: Fixed lockup when socket filtering was enabled. * Paul Koning: Fix to push CC sockets into RUN when acks are * received. + * Steve Whitehouse: + * Patrick Caulfield: Checking conninits for correctness & sending of error + * responses. */ /****************************************************************************** @@ -71,6 +73,16 @@ #include <net/dn_dev.h> #include <net/dn_route.h> +extern int decnet_log_martians; + +static void dn_log_martian(struct sk_buff *skb, const char *msg) +{ + if (decnet_log_martians && net_ratelimit()) { + char *devname = skb->rx_dev ? skb->rx_dev->name : "???"; + struct dn_skb_cb *cb = (struct dn_skb_cb *)skb->cb; + printk(KERN_INFO "DECnet: Martian packet (%s) rx_dev=%s src=0x%04hx dst=0x%04hx srcport=0x%04hx dstport=0x%04hx\n", msg, devname, cb->src, cb->dst, cb->src_port, cb->dst_port); + } +} /* * For this function we've flipped the cross-subchannel bit @@ -83,8 +95,6 @@ static void dn_ack(struct sock *sk, struct sk_buff *skb, unsigned short ack) unsigned short type = ((ack >> 12) & 0x0003); int wakeup = 0; - /* printk(KERN_DEBUG "dn_ack: %hd 0x%04hx\n", type, ack); */ - switch(type) { case 0: /* ACK - Data */ if (after(ack, scp->ackrcv_dat)) { @@ -148,50 +158,172 @@ static int dn_process_ack(struct sock *sk, struct sk_buff *skb, int oth) } +/** + * dn_check_idf - Check an image data field format is correct. + * @pptr: Pointer to pointer to image data + * @len: Pointer to length of image data + * @max: The maximum allowed length of the data in the image data field + * @follow_on: Check that this many bytes exist beyond the end of the image data + * + * Returns: 0 if ok, -1 on error + */ +static inline int dn_check_idf(unsigned char **pptr, int *len, unsigned char max, unsigned char follow_on) +{ + unsigned char *ptr = *pptr; + unsigned char flen = *ptr++; + + (*len)--; + if (flen > max) + return -1; + if ((flen + follow_on) > *len) + return -1; + + *len -= flen; + *pptr = ptr + flen; + return 0; +} + +/* + * Table of reason codes to pass back to node which sent us a badly + * formed message, plus text messages for the log. A zero entry in + * the reason field means "don't reply" otherwise a disc init is sent with + * the specified reason code. + */ +static struct { + unsigned short reason; + const char *text; +} ci_err_table[] = { + { 0, "CI: Truncated message" }, + { NSP_REASON_ID, "CI: Destination username error" }, + { NSP_REASON_ID, "CI: Destination username type" }, + { NSP_REASON_US, "CI: Source username error" }, + { 0, "CI: Truncated at menuver" }, + { 0, "CI: Truncated before access or user data" }, + { NSP_REASON_IO, "CI: Access data format error" }, + { NSP_REASON_IO, "CI: User data format error" } +}; + /* * This function uses a slightly different lookup method * to find its sockets, since it searches on object name/number - * rather than port numbers + * rather than port numbers. Various tests are done to ensure that + * the incoming data is in the correct format before it is queued to + * a socket. */ -static struct sock *dn_find_listener(struct sk_buff *skb) +static struct sock *dn_find_listener(struct sk_buff *skb, unsigned short *reason) { struct dn_skb_cb *cb = (struct dn_skb_cb *)skb->cb; struct nsp_conn_init_msg *msg = (struct nsp_conn_init_msg *)skb->data; - struct sockaddr_dn addr; + struct sockaddr_dn dstaddr; + struct sockaddr_dn srcaddr; unsigned char type = 0; + int dstlen; + int srclen; + unsigned char *ptr; + int len; + int err = 0; + unsigned char menuver; - memset(&addr, 0, sizeof(struct sockaddr_dn)); + memset(&dstaddr, 0, sizeof(struct sockaddr_dn)); + memset(&srcaddr, 0, sizeof(struct sockaddr_dn)); + /* + * 1. Decode & remove message header + */ cb->src_port = msg->srcaddr; cb->dst_port = msg->dstaddr; cb->services = msg->services; cb->info = msg->info; cb->segsize = dn_ntohs(msg->segsize); + if (skb->len < sizeof(*msg)) + goto err_out; + skb_pull(skb, sizeof(*msg)); - /* printk(KERN_DEBUG "username2sockaddr 1\n"); */ - if (dn_username2sockaddr(skb->data, skb->len, &addr, &type) < 0) + len = skb->len; + ptr = skb->data; + + /* + * 2. Check destination end username format + */ + dstlen = dn_username2sockaddr(ptr, len, &dstaddr, &type); + err++; + if (dstlen < 0) goto err_out; + err++; if (type > 1) goto err_out; - /* printk(KERN_DEBUG "looking for listener...\n"); */ - return dn_sklist_find_listener(&addr); + len -= dstlen; + ptr += dstlen; + + /* + * 3. Check source end username format + */ + srclen = dn_username2sockaddr(ptr, len, &srcaddr, &type); + err++; + if (srclen < 0) + goto err_out; + + len -= srclen; + ptr += srclen; + err++; + if (len < 1) + goto err_out; + + menuver = *ptr; + ptr++; + len--; + + /* + * 4. Check that optional data actually exists if menuver says it does + */ + err++; + if ((menuver & (DN_MENUVER_ACC | DN_MENUVER_USR)) && (len < 1)) + goto err_out; + + /* + * 5. Check optional access data format + */ + err++; + if (menuver & DN_MENUVER_ACC) { + if (dn_check_idf(&ptr, &len, 39, 1)) + goto err_out; + if (dn_check_idf(&ptr, &len, 39, 1)) + goto err_out; + if (dn_check_idf(&ptr, &len, 39, (menuver & DN_MENUVER_USR) ? 1 : 0)) + goto err_out; + } + + /* + * 6. Check optional user data format + */ + err++; + if (menuver & DN_MENUVER_USR) { + if (dn_check_idf(&ptr, &len, 16, 0)) + goto err_out; + } + + /* + * 7. Look up socket based on destination end username + */ + return dn_sklist_find_listener(&dstaddr); err_out: + dn_log_martian(skb, ci_err_table[err].text); + *reason = ci_err_table[err].reason; return NULL; } + static void dn_nsp_conn_init(struct sock *sk, struct sk_buff *skb) { - /* printk(KERN_DEBUG "checking backlog...\n"); */ if (sk->ack_backlog >= sk->max_ack_backlog) { kfree_skb(skb); return; } - /* printk(KERN_DEBUG "waking up socket...\n"); */ sk->ack_backlog++; skb_queue_tail(&sk->receive_queue, skb); sk->state_change(sk); @@ -528,13 +660,20 @@ static void dn_returned_conn_init(struct sock *sk, struct sk_buff *skb) kfree_skb(skb); } -static void dn_nsp_no_socket(struct sk_buff *skb) +static void dn_nsp_no_socket(struct sk_buff *skb, unsigned short reason) { struct dn_skb_cb *cb = (struct dn_skb_cb *)skb->cb; - switch(cb->nsp_flags) { - case 0x28: /* Connect Confirm */ - dn_nsp_return_disc(skb, NSP_DISCCONF, NSP_REASON_NL); + if ((reason != NSP_REASON_OK) && ((cb->nsp_flags & 0x0c) == 0x08)) { + switch(cb->nsp_flags & 0x70) { + case 0x10: + case 0x60: /* (Retransmitted) Connect Init */ + dn_nsp_return_disc(skb, NSP_DISCINIT, reason); + break; + case 0x20: /* Connect Confirm */ + dn_nsp_return_disc(skb, NSP_DISCCONF, reason); + break; + } } kfree_skb(skb); @@ -545,6 +684,7 @@ static int dn_nsp_rx_packet(struct sk_buff *skb) struct dn_skb_cb *cb = (struct dn_skb_cb *)skb->cb; struct sock *sk = NULL; unsigned char *ptr = (unsigned char *)skb->data; + unsigned short reason = NSP_REASON_NL; skb->h.raw = skb->data; cb->nsp_flags = *ptr++; @@ -584,7 +724,7 @@ static int dn_nsp_rx_packet(struct sk_buff *skb) goto free_out; case 0x10: case 0x60: - sk = dn_find_listener(skb); + sk = dn_find_listener(skb, &reason); goto got_it; } } @@ -632,7 +772,7 @@ got_it: return ret; } - dn_nsp_no_socket(skb); + dn_nsp_no_socket(skb, reason); return 1; free_out: @@ -664,7 +804,6 @@ int dn_nsp_backlog_rcv(struct sock *sk, struct sk_buff *skb) * Control packet. */ if ((cb->nsp_flags & 0x0c) == 0x08) { - /* printk(KERN_DEBUG "control type\n"); */ switch(cb->nsp_flags & 0x70) { case 0x10: case 0x60: diff --git a/net/decnet/dn_nsp_out.c b/net/decnet/dn_nsp_out.c index 669aeccce..6965cbf42 100644 --- a/net/decnet/dn_nsp_out.c +++ b/net/decnet/dn_nsp_out.c @@ -149,7 +149,7 @@ struct sk_buff *dn_alloc_send_skb(struct sock *sk, int *size, int noblock, int * continue; } - if ((skb = dn_alloc_skb(sk, len, GFP_KERNEL)) == NULL) + if ((skb = dn_alloc_skb(sk, len, sk->allocation)) == NULL) continue; *size = len - 11; @@ -444,7 +444,7 @@ void dn_send_conn_ack (struct sock *sk) struct sk_buff *skb = NULL; struct nsp_conn_ack_msg *msg; - if ((skb = dn_alloc_skb(sk, 3, GFP_KERNEL)) == NULL) + if ((skb = dn_alloc_skb(sk, 3, sk->allocation)) == NULL) return; msg = (struct nsp_conn_ack_msg *)skb_put(skb, 3); @@ -626,7 +626,7 @@ void dn_nsp_send_conninit(struct sock *sk, unsigned char msgflg) struct dn_skb_cb *cb; unsigned char type = 1; - if ((skb = dn_alloc_skb(sk, 200, (msgflg == NSP_CI) ? GFP_KERNEL : GFP_ATOMIC)) == NULL) + if ((skb = dn_alloc_skb(sk, 200, (msgflg == NSP_CI) ? sk->allocation : GFP_ATOMIC)) == NULL) return; cb = (struct dn_skb_cb *)skb->cb; diff --git a/net/decnet/dn_route.c b/net/decnet/dn_route.c index a2bc39398..20ec07acc 100644 --- a/net/decnet/dn_route.c +++ b/net/decnet/dn_route.c @@ -812,8 +812,8 @@ non_local_input: key.oif = 0; key.scope = RT_SCOPE_UNIVERSE; -#ifdef CONFIG_DECNET_ROUTE_FWMASK - key.fwmark = skb->fwmark; +#ifdef CONFIG_DECNET_ROUTE_FWMARK + key.fwmark = skb->nfmark; #else key.fwmark = 0; #endif @@ -890,7 +890,7 @@ int dn_route_input(struct sk_buff *skb) if ((rt->key.saddr == cb->src) && (rt->key.daddr == cb->dst) && (rt->key.oif == 0) && -#ifdef CONFIG_DECNET_ROUTE_FWMASK +#ifdef CONFIG_DECNET_ROUTE_FWMARK (rt->key.fwmark == skb->nfmark) && #endif (rt->key.iif == cb->iif)) { diff --git a/net/decnet/sysctl_net_decnet.c b/net/decnet/sysctl_net_decnet.c index c1931c867..0c9fb1f7f 100644 --- a/net/decnet/sysctl_net_decnet.c +++ b/net/decnet/sysctl_net_decnet.c @@ -32,6 +32,7 @@ int decnet_time_wait = 30; int decnet_dn_count = 1; int decnet_di_count = 3; int decnet_dr_count = 3; +int decnet_log_martians = 1; #ifdef CONFIG_SYSCTL extern int decnet_dst_gc_interval; diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c index 584814ad8..3222d25d1 100644 --- a/net/ipv4/af_inet.c +++ b/net/ipv4/af_inet.c @@ -5,7 +5,7 @@ * * PF_INET protocol family socket handler. * - * Version: $Id: af_inet.c,v 1.121 2000/10/24 21:26:18 davem Exp $ + * Version: $Id: af_inet.c,v 1.123 2000/11/10 01:42:43 davem Exp $ * * Authors: Ross Biro, <bir7@leland.Stanford.Edu> * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG> diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 0f97c2d27..6b254e2ad 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -5,7 +5,7 @@ * * Implementation of the Transmission Control Protocol(TCP). * - * Version: $Id: tcp.c,v 1.176 2000/10/06 22:45:41 davem Exp $ + * Version: $Id: tcp.c,v 1.179 2000/11/10 04:02:04 davem Exp $ * * Authors: Ross Biro, <bir7@leland.Stanford.Edu> * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG> @@ -2011,7 +2011,7 @@ static int wait_for_connect(struct sock * sk, long timeo) */ add_wait_queue_exclusive(sk->sleep, &wait); for (;;) { - current->state = TASK_EXCLUSIVE | TASK_INTERRUPTIBLE; + current->state = TASK_INTERRUPTIBLE; release_sock(sk); if (sk->tp_pinfo.af_tcp.accept_queue == NULL) timeo = schedule_timeout(timeo); diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 1a0f278b4..9f16a976c 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -5,7 +5,7 @@ * * Implementation of the Transmission Control Protocol(TCP). * - * Version: $Id: tcp_ipv4.c,v 1.218 2000/10/18 18:04:22 davem Exp $ + * Version: $Id: tcp_ipv4.c,v 1.220 2000/11/14 07:26:02 davem Exp $ * * IPv4 specific functions * @@ -301,7 +301,7 @@ void tcp_put_port(struct sock *sk) local_bh_enable(); } -/* This lock without TASK_EXCLUSIVE is good on UP and it can be very bad on SMP. +/* This lock without WQ_FLAG_EXCLUSIVE is good on UP and it can be very bad on SMP. * Look, when several writers sleep and reader wakes them up, all but one * immediately hit write lock and grab all the cpus. Exclusive sleep solves * this, _but_ remember, it adds useless work on UP machines (wake up each @@ -317,7 +317,7 @@ void tcp_listen_wlock(void) add_wait_queue_exclusive(&tcp_lhash_wait, &wait); for (;;) { - set_current_state(TASK_UNINTERRUPTIBLE|TASK_EXCLUSIVE); + set_current_state(TASK_UNINTERRUPTIBLE); if (atomic_read(&tcp_lhash_users) == 0) break; write_unlock_bh(&tcp_lhash_lock); @@ -1721,93 +1721,88 @@ static void __tcp_v4_rehash(struct sock *sk) sk->prot->hash(sk); } -int tcp_v4_rebuild_header(struct sock *sk) +static int tcp_v4_reselect_saddr(struct sock *sk) { - struct rtable *rt = (struct rtable *)__sk_dst_check(sk, 0); + int err; + struct rtable *rt; + __u32 old_saddr = sk->saddr; __u32 new_saddr; - int want_rewrite = sysctl_ip_dynaddr && sk->state == TCP_SYN_SENT && - !(sk->userlocks & SOCK_BINDADDR_LOCK); + __u32 daddr = sk->daddr; - if (rt == NULL) { - int err; + if(sk->protinfo.af_inet.opt && sk->protinfo.af_inet.opt->srr) + daddr = sk->protinfo.af_inet.opt->faddr; - u32 daddr = sk->daddr; + /* Query new route. */ + err = ip_route_connect(&rt, daddr, 0, + RT_TOS(sk->protinfo.af_inet.tos)|sk->localroute, + sk->bound_dev_if); + if (err) + return err; - if(sk->protinfo.af_inet.opt && sk->protinfo.af_inet.opt->srr) - daddr = sk->protinfo.af_inet.opt->faddr; + __sk_dst_set(sk, &rt->u.dst); + /* sk->route_caps = rt->u.dst.dev->features; */ - err = ip_route_output(&rt, daddr, sk->saddr, - RT_TOS(sk->protinfo.af_inet.tos) | RTO_CONN | sk->localroute, - sk->bound_dev_if); - if (err) { - sk->err_soft=-err; - sk->error_report(sk); - return -1; - } - __sk_dst_set(sk, &rt->u.dst); - } + new_saddr = rt->rt_src; - /* Force route checking if want_rewrite. */ - if (want_rewrite) { - int tmp; - struct rtable *new_rt; - __u32 old_saddr = rt->rt_src; - - /* Query new route using another rt buffer */ - tmp = ip_route_connect(&new_rt, rt->rt_dst, 0, - RT_TOS(sk->protinfo.af_inet.tos)|sk->localroute, - sk->bound_dev_if); - - /* Only useful if different source addrs */ - if (tmp == 0) { - /* - * Only useful if different source addrs - */ - if (new_rt->rt_src != old_saddr ) { - __sk_dst_set(sk, &new_rt->u.dst); - rt = new_rt; - goto do_rewrite; - } - dst_release(&new_rt->u.dst); - } + if (new_saddr == old_saddr) + return 0; + + if (sysctl_ip_dynaddr > 1) { + printk(KERN_INFO "tcp_v4_rebuild_header(): shifting sk->saddr " + "from %d.%d.%d.%d to %d.%d.%d.%d\n", + NIPQUAD(old_saddr), + NIPQUAD(new_saddr)); } + sk->saddr = new_saddr; + sk->rcv_saddr = new_saddr; + + /* XXX The only one ugly spot where we need to + * XXX really change the sockets identity after + * XXX it has entered the hashes. -DaveM + * + * Besides that, it does not check for connection + * uniqueness. Wait for troubles. + */ + __tcp_v4_rehash(sk); return 0; +} -do_rewrite: - new_saddr = rt->rt_src; - - /* Ouch!, this should not happen. */ - if (!sk->saddr || !sk->rcv_saddr) { - printk(KERN_WARNING "tcp_v4_rebuild_header(): not valid sock addrs: " - "saddr=%08X rcv_saddr=%08X\n", - ntohl(sk->saddr), - ntohl(sk->rcv_saddr)); - return -1; - } +int tcp_v4_rebuild_header(struct sock *sk) +{ + struct rtable *rt = (struct rtable *)__sk_dst_check(sk, 0); + u32 daddr; + int err; - if (new_saddr != sk->saddr) { - if (sysctl_ip_dynaddr > 1) { - printk(KERN_INFO "tcp_v4_rebuild_header(): shifting sk->saddr " - "from %d.%d.%d.%d to %d.%d.%d.%d\n", - NIPQUAD(sk->saddr), - NIPQUAD(new_saddr)); - } + /* Route is OK, nothing to do. */ + if (rt != NULL) + return 0; - sk->saddr = new_saddr; - sk->rcv_saddr = new_saddr; + /* Reroute. */ + daddr = sk->daddr; + if(sk->protinfo.af_inet.opt && sk->protinfo.af_inet.opt->srr) + daddr = sk->protinfo.af_inet.opt->faddr; - /* XXX The only one ugly spot where we need to - * XXX really change the sockets identity after - * XXX it has entered the hashes. -DaveM - * - * Besides that, it does not check for connection - * uniqueness. Wait for troubles. - */ - __tcp_v4_rehash(sk); - } - - return 0; + err = ip_route_output(&rt, daddr, sk->saddr, + RT_TOS(sk->protinfo.af_inet.tos) | RTO_CONN | sk->localroute, + sk->bound_dev_if); + if (!err) { + __sk_dst_set(sk, &rt->u.dst); + /* sk->route_caps = rt->u.dst.dev->features; */ + return 0; + } + + /* Routing failed... */ + /* sk->route_caps = 0; */ + + if (!sysctl_ip_dynaddr || + sk->state != TCP_SYN_SENT || + (sk->userlocks & SOCK_BINDADDR_LOCK) || + (err = tcp_v4_reselect_saddr(sk)) != 0) { + sk->err_soft=-err; + /* sk->error_report(sk); */ + } + return err; } static void v4_addr2sockaddr(struct sock *sk, struct sockaddr * uaddr) diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c index 4a9af93e1..520a3b6c1 100644 --- a/net/ipv6/ndisc.c +++ b/net/ipv6/ndisc.c @@ -660,7 +660,8 @@ static void ndisc_router_discovery(struct sk_buff *skb) if (ra_msg->reachable_time) { __u32 rtime = (ntohl(ra_msg->reachable_time)*HZ)/1000; - if (rtime != in6_dev->nd_parms->base_reachable_time) { + if (rtime && + rtime != in6_dev->nd_parms->base_reachable_time) { in6_dev->nd_parms->base_reachable_time = rtime; in6_dev->nd_parms->gc_staletime = 3 * rtime; in6_dev->nd_parms->reachable_time = neigh_rand_reach_time(rtime); diff --git a/net/ipv6/route.c b/net/ipv6/route.c index e1832fd8c..e643a39eb 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -5,7 +5,7 @@ * Authors: * Pedro Roque <roque@di.fc.ul.pt> * - * $Id: route.c,v 1.48 2000/08/10 01:17:13 davem Exp $ + * $Id: route.c,v 1.49 2000/11/03 01:11:58 davem Exp $ * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -1902,22 +1902,22 @@ ctl_table ipv6_route_table[] = { &proc_dointvec}, {NET_IPV6_ROUTE_GC_MIN_INTERVAL, "gc_min_interval", &ip6_rt_gc_min_interval, sizeof(int), 0644, NULL, - &proc_dointvec_jiffies}, + &proc_dointvec_jiffies, &sysctl_jiffies}, {NET_IPV6_ROUTE_GC_TIMEOUT, "gc_timeout", &ip6_rt_gc_timeout, sizeof(int), 0644, NULL, - &proc_dointvec_jiffies}, + &proc_dointvec_jiffies, &sysctl_jiffies}, {NET_IPV6_ROUTE_GC_INTERVAL, "gc_interval", &ip6_rt_gc_interval, sizeof(int), 0644, NULL, - &proc_dointvec_jiffies}, + &proc_dointvec_jiffies, &sysctl_jiffies}, {NET_IPV6_ROUTE_GC_ELASTICITY, "gc_elasticity", &ip6_rt_gc_elasticity, sizeof(int), 0644, NULL, - &proc_dointvec_jiffies}, + &proc_dointvec_jiffies, &sysctl_jiffies}, {NET_IPV6_ROUTE_MTU_EXPIRES, "mtu_expires", &ip6_rt_mtu_expires, sizeof(int), 0644, NULL, - &proc_dointvec_jiffies}, + &proc_dointvec_jiffies, &sysctl_jiffies}, {NET_IPV6_ROUTE_MIN_ADVMSS, "min_adv_mss", &ip6_rt_min_advmss, sizeof(int), 0644, NULL, - &proc_dointvec_jiffies}, + &proc_dointvec_jiffies, &sysctl_jiffies}, {0} }; diff --git a/net/ipx/Config.in b/net/ipx/Config.in index 0070c5813..0c798e495 100644 --- a/net/ipx/Config.in +++ b/net/ipx/Config.in @@ -3,6 +3,6 @@ # bool ' IPX: Full internal IPX network' CONFIG_IPX_INTERN -if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then - dep_tristate ' IPX: SPX networking (EXPERIMENTAL)' CONFIG_SPX $CONFIG_IPX -fi +#if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then +# dep_tristate ' IPX: SPX networking (EXPERIMENTAL)' CONFIG_SPX $CONFIG_IPX +#fi diff --git a/net/irda/Config.in b/net/irda/Config.in index 6f4f716f8..5ab40b985 100644 --- a/net/irda/Config.in +++ b/net/irda/Config.in @@ -11,6 +11,7 @@ if [ "$CONFIG_NET" != "n" ]; then if [ "$CONFIG_IRDA" != "n" ]; then comment 'IrDA protocols' source net/irda/irlan/Config.in + source net/irda/irnet/Config.in source net/irda/ircomm/Config.in bool ' Ultra (connectionless) protocol' CONFIG_IRDA_ULTRA bool ' IrDA protocol options' CONFIG_IRDA_OPTIONS diff --git a/net/irda/Makefile b/net/irda/Makefile index ac2bdc063..3ab07c88d 100644 --- a/net/irda/Makefile +++ b/net/irda/Makefile @@ -7,7 +7,7 @@ # # Note 2! The CFLAGS definition is now in the main makefile... -ALL_SUB_DIRS := irlan ircomm compressors +ALL_SUB_DIRS := irlan irnet ircomm compressors SUB_DIRS := MOD_SUB_DIRS := OX_OBJS := @@ -17,7 +17,7 @@ O_OBJS := iriap.o iriap_event.o irlmp.o irlmp_event.o irlmp_frame.o \ irlap.o irlap_event.o irlap_frame.o timer.o qos.o irqueue.o \ irttp.o irda_device.o irias_object.o crc.o wrapper.o af_irda.o \ discovery.o parameters.o -OX_OBJS := irmod.o +OX_OBJS := irsyms.o ifeq ($(CONFIG_IRDA),m) M_OBJS := $(O_TARGET) @@ -44,6 +44,15 @@ else endif endif +ifeq ($(CONFIG_IRNET),y) +SUB_DIRS += irnet +O_OBJS += irnet/irnet.o +else + ifeq ($(CONFIG_IRNET),m) + MOD_SUB_DIRS += irnet + endif +endif + ifeq ($(CONFIG_IRDA_COMPRESSION),y) SUB_DIRS += compressors MOD_IN_SUB_DIRS += compressors diff --git a/net/irda/af_irda.c b/net/irda/af_irda.c index 69db79a0e..e5504ee96 100644 --- a/net/irda/af_irda.c +++ b/net/irda/af_irda.c @@ -7,11 +7,11 @@ * Author: Dag Brattli <dagb@cs.uit.no> * Created at: Sun May 31 10:12:43 1998 * Modified at: Sat Dec 25 21:10:23 1999 - * Modified by: Dag Brattli <dagb@cs.uit.no> + * Modified by: Dag Brattli <dag@brattli.net> * Sources: af_netroom.c, af_ax25.c, af_rose.c, af_x25.c etc. * * Copyright (c) 1999 Dag Brattli <dagb@cs.uit.no> - * Copyright (c) 1999 Jean Tourrilhes <jeant@rockfort.hpl.hp.com> + * Copyright (c) 1999 Jean Tourrilhes <jt@hpl.hp.com> * All Rights Reserved. * * This program is free software; you can redistribute it and/or @@ -43,10 +43,11 @@ ********************************************************************/ #include <linux/config.h> -#include <linux/init.h> +#include <linux/module.h> #include <linux/types.h> #include <linux/socket.h> #include <linux/sockios.h> +#include <linux/init.h> #include <linux/if_arp.h> #include <linux/net.h> #include <linux/irda.h> @@ -79,11 +80,12 @@ static struct proto_ops irda_ultra_ops; #define ULTRA_MAX_DATA 382 #endif /* CONFIG_IRDA_ULTRA */ -static hashbin_t *cachelog = NULL; -static DECLARE_WAIT_QUEUE_HEAD(discovery_wait); /* Wait for discovery */ - #define IRDA_MAX_HEADER (TTP_MAX_HEADER) +#ifdef CONFIG_IRDA_DEBUG +__u32 irda_debug = IRDA_DEBUG_LEVEL; +#endif + /* * Function irda_data_indication (instance, sap, skb) * @@ -117,7 +119,7 @@ static int irda_data_indication(void *instance, void *sap, struct sk_buff *skb) /* * Function irda_disconnect_indication (instance, sap, reason, skb) * - * Connection has been closed. Chech reason to find out why + * Connection has been closed. Check reason to find out why * */ static void irda_disconnect_indication(void *instance, void *sap, @@ -141,6 +143,27 @@ static void irda_disconnect_indication(void *instance, void *sap, sk->state_change(sk); sk->dead = 1; } + + /* Close our TSAP. + * If we leave it open, IrLMP put it back into the list of + * unconnected LSAPs. The problem is that any incomming request + * can then be matched to this socket (and it will be, because + * it is at the head of the list). This would prevent any + * listening socket waiting on the same TSAP to get those requests. + * Some apps forget to close sockets, or hang to it a bit too long, + * so we may stay in this dead state long enough to be noticed... + * Note : all socket function do check sk->state, so we are safe... + * Jean II + */ + irttp_close_tsap(self->tsap); + self->tsap = NULL; + + /* Note : once we are there, there is not much you want to do + * with the socket anymore, apart from closing it. + * For example, bind() and connect() won't reset sk->err, + * sk->shutdown and sk->dead to valid values... + * Jean II + */ } /* @@ -326,19 +349,19 @@ static void irda_flow_indication(void *instance, void *sap, LOCAL_FLOW flow) /* * Function irda_getvalue_confirm (obj_id, value, priv) * - * Got answer from remote LM-IAS + * Got answer from remote LM-IAS, just pass object to requester... * + * Note : duplicate from above, but we need our own version that + * doesn't touch the dtsap_sel and save the full value structure... */ static void irda_getvalue_confirm(int result, __u16 obj_id, - struct ias_value *value, void *priv) + struct ias_value *value, void *priv) { struct irda_sock *self; IRDA_DEBUG(2, __FUNCTION__ "()\n"); - ASSERT(priv != NULL, return;); self = (struct irda_sock *) priv; - if (!self) { WARNING(__FUNCTION__ "(), lost myself!\n"); return; @@ -348,51 +371,90 @@ static void irda_getvalue_confirm(int result, __u16 obj_id, iriap_close(self->iriap); self->iriap = NULL; - self->errno = result; - /* Check if request succeeded */ if (result != IAS_SUCCESS) { - IRDA_DEBUG(0, __FUNCTION__ "(), IAS query failed!\n"); + IRDA_DEBUG(1, __FUNCTION__ "(), IAS query failed! (%d)\n", + result); + + self->errno = result; /* We really need it later */ /* Wake up any processes waiting for result */ - wake_up_interruptible(&self->ias_wait); + wake_up_interruptible(&self->query_wait); return; } - switch (value->type) { - case IAS_INTEGER: - IRDA_DEBUG(4, __FUNCTION__ "() int=%d\n", value->t.integer); - - if (value->t.integer != -1) { - self->dtsap_sel = value->t.integer; - } else - self->dtsap_sel = 0; - break; - default: - IRDA_DEBUG(0, __FUNCTION__ "(), bad type!\n"); - break; - } - irias_delete_value(value); + /* Pass the object to the caller (so the caller must delete it) */ + self->ias_result = value; + self->errno = 0; /* Wake up any processes waiting for result */ - wake_up_interruptible(&self->ias_wait); + wake_up_interruptible(&self->query_wait); } /* - * Function irda_discovery_indication (log) + * Function irda_selective_discovery_indication (discovery) * - * Got a discovery log from IrLMP, wake ut any process waiting for answer + * Got a selective discovery indication from IrLMP. * + * IrLMP is telling us that this node is matching our hint bit + * filter. Check if it's a newly discovered node (or if node changed its + * hint bits), and then wake up any process waiting for answer... */ -static void irda_discovery_indication(hashbin_t *log) +static void irda_selective_discovery_indication(discovery_t *discovery, + void *priv) { + struct irda_sock *self; + IRDA_DEBUG(2, __FUNCTION__ "()\n"); - cachelog = log; + self = (struct irda_sock *) priv; + if (!self) { + WARNING(__FUNCTION__ "(), lost myself!\n"); + return; + } + + /* Check if node is discovered is a new one or an old one. + * We check when how long ago this node was discovered, with a + * coarse timeout (we may miss some discovery events or be delayed). + * Note : by doing this test here, we avoid waking up a process ;-) + */ + if((jiffies - discovery->first_timestamp) > + (sysctl_discovery_timeout * HZ)) { + return; /* Too old, not interesting -> goodbye */ + } + + /* Pass parameter to the caller */ + self->cachediscovery = discovery; /* Wake up process if its waiting for device to be discovered */ - wake_up_interruptible(&discovery_wait); + wake_up_interruptible(&self->query_wait); +} + +/* + * Function irda_discovery_timeout (priv) + * + * Timeout in the selective discovery process + * + * We were waiting for a node to be discovered, but nothing has come up + * so far. Wake up the user and tell him that we failed... + */ +static void irda_discovery_timeout(u_long priv) +{ + struct irda_sock *self; + + IRDA_DEBUG(2, __FUNCTION__ "()\n"); + + self = (struct irda_sock *) priv; + ASSERT(self != NULL, return;); + + /* Nothing for the caller */ + self->cachelog = NULL; + self->cachediscovery = NULL; + self->errno = -ETIME; + + /* Wake up process if its still waiting... */ + wake_up_interruptible(&self->query_wait); } /* @@ -470,6 +532,11 @@ static int irda_open_lsap(struct irda_sock *self, int pid) * * Try to lookup LSAP selector in remote LM-IAS * + * Basically, we start a IAP query, and then go to sleep. When the query + * return, irda_getvalue_confirm will wake us up, and we can examine the + * result of the query... + * Note that in some case, the query fail even before we go to sleep, + * creating some races... */ static int irda_find_lsap_sel(struct irda_sock *self, char *name) { @@ -485,19 +552,53 @@ static int irda_find_lsap_sel(struct irda_sock *self, char *name) self->iriap = iriap_open(LSAP_ANY, IAS_CLIENT, self, irda_getvalue_confirm); + /* Treat unexpected signals as disconnect */ + self->errno = -EHOSTUNREACH; + /* Query remote LM-IAS */ iriap_getvaluebyclass_request(self->iriap, self->saddr, self->daddr, name, "IrDA:TinyTP:LsapSel"); - /* Wait for answer */ - interruptible_sleep_on(&self->ias_wait); + /* Wait for answer (if not already failed) */ + if(self->iriap != NULL) + interruptible_sleep_on(&self->query_wait); + + /* Check what happened */ + if (self->errno) + { + /* Requested object/attribute doesn't exist */ + if((self->errno == IAS_CLASS_UNKNOWN) || + (self->errno == IAS_ATTRIB_UNKNOWN)) + return (-EADDRNOTAVAIL); + else + return (-EHOSTUNREACH); + } + + /* Get the remote TSAP selector */ + switch (self->ias_result->type) { + case IAS_INTEGER: + IRDA_DEBUG(4, __FUNCTION__ "() int=%d\n", + self->ias_result->t.integer); + + if (self->ias_result->t.integer != -1) + self->dtsap_sel = self->ias_result->t.integer; + else + self->dtsap_sel = 0; + break; + default: + self->dtsap_sel = 0; + IRDA_DEBUG(0, __FUNCTION__ "(), bad type!\n"); + break; + } + if (self->ias_result) + irias_delete_value(self->ias_result); if (self->dtsap_sel) return 0; - return -ENETUNREACH; /* May not be true */ + return -EADDRNOTAVAIL; } - /* +/* * Function irda_discover_daddr_and_lsap_sel (self, name) * * This try to find a device with the requested service. @@ -516,71 +617,78 @@ static int irda_find_lsap_sel(struct irda_sock *self, char *name) */ static int irda_discover_daddr_and_lsap_sel(struct irda_sock *self, char *name) { - discovery_t *discovery; - int err = -ENETUNREACH; - __u32 daddr = 0x0; /* Address we found the service on */ + struct irda_device_info *discoveries; /* Copy of the discovery log */ + int number; /* Number of nodes in the log */ + int i; + int err = -ENETUNREACH; + __u32 daddr = DEV_ADDR_ANY; /* Address we found the service on */ __u8 dtsap_sel = 0x0; /* TSAP associated with it */ IRDA_DEBUG(2, __FUNCTION__ "(), name=%s\n", name); ASSERT(self != NULL, return -1;); - /* Tell IrLMP we want to be notified */ - irlmp_update_client(self->ckey, self->mask, NULL, - irda_discovery_indication); - - /* Do some discovery */ - irlmp_discovery_request(self->nslots); - + /* Ask lmp for the current discovery log + * Note : we have to use irlmp_get_discoveries(), as opposed + * to play with the cachelog directly, because while we are + * making our ias query, le log might change... */ + discoveries = irlmp_get_discoveries(&number, self->mask); /* Check if the we got some results */ - if (!cachelog) - /* Wait for answer */ - /*interruptible_sleep_on(&self->discovery_wait);*/ - return -EAGAIN; + if (discoveries == NULL) + return -ENETUNREACH; /* No nodes discovered */ /* * Now, check all discovered devices (if any), and connect * client only about the services that the client is * interested in... */ - discovery = (discovery_t *) hashbin_get_first(cachelog); - while (discovery != NULL) { - /* Mask out the ones we don't want */ - if (discovery->hints.word & self->mask) { - /* Try this address */ - self->daddr = discovery->daddr; - self->saddr = 0x0; - IRDA_DEBUG(1, __FUNCTION__ "(), trying daddr = %08x\n", - self->daddr); - - /* Query remote LM-IAS for this service */ - err = irda_find_lsap_sel(self, name); - if (err == 0) { - /* We found the requested service */ - if(daddr != 0x0) { - IRDA_DEBUG(0, __FUNCTION__ - "(), discovered service ''%s'' in two different devices !!!\n", - name); - return(-ENOTUNIQ); - } - /* First time we foun that one, save it ! */ - daddr = self->daddr; - dtsap_sel = self->dtsap_sel; + for(i = 0; i < number; i++) { + /* Try the address in the log */ + self->daddr = discoveries[i].daddr; + self->saddr = 0x0; + IRDA_DEBUG(1, __FUNCTION__ "(), trying daddr = %08x\n", + self->daddr); + + /* Query remote LM-IAS for this service */ + err = irda_find_lsap_sel(self, name); + switch (err) { + case 0: + /* We found the requested service */ + if(daddr != DEV_ADDR_ANY) { + IRDA_DEBUG(1, __FUNCTION__ + "(), discovered service ''%s'' in two different devices !!!\n", + name); + self->daddr = DEV_ADDR_ANY; + kfree(discoveries); + return(-ENOTUNIQ); } + /* First time we found that one, save it ! */ + daddr = self->daddr; + dtsap_sel = self->dtsap_sel; + break; + case -EADDRNOTAVAIL: + /* Requested service simply doesn't exist on this node */ + break; + default: + /* Something bad did happen :-( */ + IRDA_DEBUG(0, __FUNCTION__ + "(), unexpected IAS query failure\n"); + self->daddr = DEV_ADDR_ANY; + kfree(discoveries); + return(-EHOSTUNREACH); + break; } - - /* Next node, maybe we will be more lucky... */ - discovery = (discovery_t *) hashbin_get_next(cachelog); } - cachelog = NULL; + /* Cleanup our copy of the discovery log */ + kfree(discoveries); /* Check out what we found */ - if(daddr == 0x0) { - IRDA_DEBUG(0, __FUNCTION__ + if(daddr == DEV_ADDR_ANY) { + IRDA_DEBUG(1, __FUNCTION__ "(), cannot discover service ''%s'' in any device !!!\n", name); - self->daddr = 0; /* Guessing */ - return(-ENETUNREACH); + self->daddr = DEV_ADDR_ANY; + return(-EADDRNOTAVAIL); } /* Revert back to discovered device & service */ @@ -588,7 +696,7 @@ static int irda_discover_daddr_and_lsap_sel(struct irda_sock *self, char *name) self->saddr = 0x0; self->dtsap_sel = dtsap_sel; - IRDA_DEBUG(0, __FUNCTION__ + IRDA_DEBUG(1, __FUNCTION__ "(), discovered requested service ''%s'' at address %08x\n", name, self->daddr); @@ -606,25 +714,26 @@ static int irda_getname(struct socket *sock, struct sockaddr *uaddr, { struct sockaddr_irda saddr; struct sock *sk = sock->sk; + struct irda_sock *self = sk->protinfo.irda; if (peer) { if (sk->state != TCP_ESTABLISHED) return -ENOTCONN; saddr.sir_family = AF_IRDA; - saddr.sir_lsap_sel = sk->protinfo.irda->dtsap_sel; - saddr.sir_addr = sk->protinfo.irda->daddr; + saddr.sir_lsap_sel = self->dtsap_sel; + saddr.sir_addr = self->daddr; } else { saddr.sir_family = AF_IRDA; - saddr.sir_lsap_sel = sk->protinfo.irda->stsap_sel; - saddr.sir_addr = sk->protinfo.irda->saddr; + saddr.sir_lsap_sel = self->stsap_sel; + saddr.sir_addr = self->saddr; } IRDA_DEBUG(1, __FUNCTION__ "(), tsap_sel = %#x\n", saddr.sir_lsap_sel); IRDA_DEBUG(1, __FUNCTION__ "(), addr = %08x\n", saddr.sir_addr); - if (*uaddr_len > sizeof (struct sockaddr_irda)) - *uaddr_len = sizeof (struct sockaddr_irda); + /* uaddr_len come to us uninitialised */ + *uaddr_len = sizeof (struct sockaddr_irda); memcpy(uaddr, &saddr, *uaddr_len); return 0; @@ -709,7 +818,7 @@ static int irda_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) /* Register with LM-IAS */ self->ias_obj = irias_new_object(addr->sir_name, jiffies); irias_add_integer_attrib(self->ias_obj, "IrDA:TinyTP:LsapSel", - self->stsap_sel); + self->stsap_sel, IAS_KERNEL_ATTR); irias_insert_object(self->ias_obj); #if 1 /* Will be removed in near future */ @@ -821,6 +930,20 @@ static int irda_accept(struct socket *sock, struct socket *newsock, int flags) * * Connect to a IrDA device * + * The main difference with a "standard" connect is that with IrDA we need + * to resolve the service name into a TSAP selector (in TCP, port number + * doesn't have to be resolved). + * Because of this service name resoltion, we can offer "auto-connect", + * where we connect to a service without specifying a destination address. + * + * Note : by consulting "errno", the user space caller may learn the cause + * of the failure. Most of them are visible in the function, others may come + * from subroutines called and are listed here : + * o EBUSY : already processing a connect + * o EHOSTUNREACH : bad addr->sir_addr argument + * o EADDRNOTAVAIL : bad addr->sir_name argument + * o ENOTUNIQ : more than one node has addr->sir_name (auto-connect) + * o ENETUNREACH : no node found on the network (auto-connect) */ static int irda_connect(struct socket *sock, struct sockaddr *uaddr, int addr_len, int flags) @@ -858,13 +981,13 @@ static int irda_connect(struct socket *sock, struct sockaddr *uaddr, return -EINVAL; /* Check if user supplied any destination device address */ - if (!addr->sir_addr) { + if ((!addr->sir_addr) || (addr->sir_addr == DEV_ADDR_ANY)) { /* Try to find one suitable */ err = irda_discover_daddr_and_lsap_sel(self, addr->sir_name); if (err) { IRDA_DEBUG(0, __FUNCTION__ "(), auto-connect failed!\n"); - return -EINVAL; + return err; } } else { /* Use the one provided by the user */ @@ -922,6 +1045,9 @@ static int irda_connect(struct socket *sock, struct sockaddr *uaddr, sti(); + /* At this point, IrLMP has assigned our source address */ + self->saddr = irttp_get_saddr(self->tsap); + return 0; } @@ -957,7 +1083,7 @@ static int irda_create(struct socket *sock, int protocol) return -ENOMEM; memset(self, 0, sizeof(struct irda_sock)); - init_waitqueue_head(&self->ias_wait); + init_waitqueue_head(&self->query_wait); self->sk = sk; sk->protinfo.irda = self; @@ -996,14 +1122,14 @@ static int irda_create(struct socket *sock, int protocol) sk->protocol = protocol; /* Register as a client with IrLMP */ - self->ckey = irlmp_register_client(0, NULL, NULL); + self->ckey = irlmp_register_client(0, NULL, NULL, NULL); self->mask = 0xffff; self->rx_flow = self->tx_flow = FLOW_START; self->nslots = DISCOVERY_DEFAULT_SLOTS; - self->daddr = DEV_ADDR_ANY; + self->daddr = DEV_ADDR_ANY; /* Until we get connected */ + self->saddr = 0x0; /* so IrLMP assign us any link */ - /* Notify that we are using the irda module, so nobody removes it */ - irda_mod_inc_use_count(); + MOD_INC_USE_COUNT; return 0; } @@ -1025,11 +1151,15 @@ void irda_destroy_socket(struct irda_sock *self) irlmp_unregister_service(self->skey); /* Unregister with LM-IAS */ - if (self->ias_obj) + if (self->ias_obj) { irias_delete_object(self->ias_obj); + self->ias_obj = NULL; + } - if (self->iriap) + if (self->iriap) { iriap_close(self->iriap); + self->iriap = NULL; + } if (self->tsap) { irttp_disconnect_request(self->tsap, NULL, P_NORMAL); @@ -1043,10 +1173,8 @@ void irda_destroy_socket(struct irda_sock *self) } #endif /* CONFIG_IRDA_ULTRA */ kfree(self); - - /* Notify that we are not using the irda module anymore */ - irda_mod_dec_use_count(); - + MOD_DEC_USE_COUNT; + return; } @@ -1096,7 +1224,8 @@ static int irda_sendmsg(struct socket *sock, struct msghdr *msg, int len, IRDA_DEBUG(4, __FUNCTION__ "(), len=%d\n", len); - if (msg->msg_flags & ~MSG_DONTWAIT) + /* Note : socket.c set MSG_EOR on SEQPACKET sockets */ + if (msg->msg_flags & ~(MSG_DONTWAIT | MSG_EOR)) return -EINVAL; if (sk->shutdown & SEND_SHUTDOWN) { @@ -1474,15 +1603,22 @@ static int irda_shutdown(struct socket *sock, int how) sk->shutdown |= SEND_SHUTDOWN; sk->state_change(sk); - if (self->iriap) + if (self->iriap) { iriap_close(self->iriap); - + self->iriap = NULL; + } + if (self->tsap) { irttp_disconnect_request(self->tsap, NULL, P_NORMAL); irttp_close_tsap(self->tsap); self->tsap = NULL; } + /* A few cleanup so the socket look as good as new... */ + self->rx_flow = self->tx_flow = FLOW_START; /* needed ??? */ + self->daddr = DEV_ADDR_ANY; /* Until we get re-connected */ + self->saddr = 0x0; /* so IrLMP assign us any link */ + return 0; } @@ -1606,6 +1742,7 @@ static int irda_setsockopt(struct socket *sock, int level, int optname, struct irda_sock *self; struct irda_ias_set ias_opt; struct ias_object *ias_obj; + struct ias_attrib * ias_attr; /* Attribute in IAS object */ int opt; self = sk->protinfo.irda; @@ -1616,6 +1753,13 @@ static int irda_setsockopt(struct socket *sock, int level, int optname, switch (optname) { case IRLMP_IAS_SET: + /* The user want to add an attribute to an existing IAS object + * (in the IAS database) or to create a new object with this + * attribute. + * We first query IAS to know if the object exist, and then + * create the right attribute... + */ + if (optlen != sizeof(struct irda_ias_set)) return -EINVAL; @@ -1639,9 +1783,11 @@ static int irda_setsockopt(struct socket *sock, int level, int optname, switch(ias_opt.irda_attrib_type) { case IAS_INTEGER: /* Add an integer attribute */ - irias_add_integer_attrib(ias_obj, - ias_opt.irda_attrib_name, - ias_opt.attribute.irda_attrib_int); + irias_add_integer_attrib( + ias_obj, + ias_opt.irda_attrib_name, + ias_opt.attribute.irda_attrib_int, + IAS_USER_ATTR); break; case IAS_OCT_SEQ: /* Check length */ @@ -1653,7 +1799,8 @@ static int irda_setsockopt(struct socket *sock, int level, int optname, ias_obj, ias_opt.irda_attrib_name, ias_opt.attribute.irda_attrib_octet_seq.octet_seq, - ias_opt.attribute.irda_attrib_octet_seq.len); + ias_opt.attribute.irda_attrib_octet_seq.len, + IAS_USER_ATTR); break; case IAS_STRING: /* Should check charset & co */ @@ -1667,16 +1814,49 @@ static int irda_setsockopt(struct socket *sock, int level, int optname, irias_add_string_attrib( ias_obj, ias_opt.irda_attrib_name, - ias_opt.attribute.irda_attrib_string.string); + ias_opt.attribute.irda_attrib_string.string, + IAS_USER_ATTR); break; default : return -EINVAL; } irias_insert_object(ias_obj); break; + case IRLMP_IAS_DEL: + /* The user want to delete an object from our local IAS + * database. We just need to query the IAS, check is the + * object is not owned by the kernel and delete it. + */ - IRDA_DEBUG(0, __FUNCTION__ "(), sorry not impl. yet!\n"); - return -ENOPROTOOPT; + if (optlen != sizeof(struct irda_ias_set)) + return -EINVAL; + + /* Copy query to the driver. */ + if (copy_from_user(&ias_opt, (char *)optval, optlen)) + return -EFAULT; + + /* Find the object we target */ + ias_obj = irias_find_object(ias_opt.irda_class_name); + if(ias_obj == (struct ias_object *) NULL) + return -EINVAL; + + /* Find the attribute (in the object) we target */ + ias_attr = irias_find_attrib(ias_obj, + ias_opt.irda_attrib_name); + if(ias_attr == (struct ias_attrib *) NULL) + return -EINVAL; + + /* Check is the user space own the object */ + if(ias_attr->value->owner != IAS_USER_ATTR) { + IRDA_DEBUG(1, __FUNCTION__ + "(), attempting to delete a kernel attribute\n"); + return -EPERM; + } + + /* Remove the attribute (and maybe the object) */ + irias_delete_attrib(ias_obj, ias_attr); + + break; case IRLMP_MAX_SDU_SIZE: if (optlen < sizeof(int)) return -EINVAL; @@ -1709,60 +1889,31 @@ static int irda_setsockopt(struct socket *sock, int level, int optname, self->skey = irlmp_register_service((__u16) opt); break; - default: - return -ENOPROTOOPT; - } - return 0; -} - - /* - * Function irda_simple_getvalue_confirm (obj_id, value, priv) - * - * Got answer from remote LM-IAS, just copy object to requester... - * - * Note : duplicate from above, but we need our own version that - * doesn't touch the dtsap_sel and save the full value structure... - */ -static void irda_simple_getvalue_confirm(int result, __u16 obj_id, - struct ias_value *value, void *priv) -{ - struct irda_sock *self; - - IRDA_DEBUG(2, __FUNCTION__ "()\n"); - - ASSERT(priv != NULL, return;); - self = (struct irda_sock *) priv; + case IRLMP_HINT_MASK_SET: + /* As opposed to the previous case which set the hint bits + * that we advertise, this one set the filter we use when + * making a discovery (nodes which don't match any hint + * bit in the mask are not reported). + */ + if (optlen < sizeof(int)) + return -EINVAL; - if (!self) { - WARNING(__FUNCTION__ "(), lost myself!\n"); - return; - } - - /* We probably don't need to make any more queries */ - iriap_close(self->iriap); - self->iriap = NULL; - - /* Check if request succeeded */ - if (result != IAS_SUCCESS) { - IRDA_DEBUG(0, __FUNCTION__ "(), IAS query failed!\n"); - - self->errno = -EHOSTUNREACH; + if (get_user(opt, (int *)optval)) + return -EFAULT; - /* Wake up any processes waiting for result */ - wake_up_interruptible(&self->ias_wait); + /* Set the new hint mask */ + self->mask = (__u16) opt; + /* Mask out extension bits */ + self->mask &= 0x7f7f; + /* Check if no bits */ + if(!self->mask) + self->mask = 0xFFFF; - return; + break; + default: + return -ENOPROTOOPT; } - - /* Clone the object (so the requester can free it) */ - self->ias_result = kmalloc(sizeof(struct ias_value), GFP_ATOMIC); - memcpy(self->ias_result, value, sizeof(struct ias_value)); - irias_delete_value(value); - - self->errno = 0; - - /* Wake up any processes waiting for result */ - wake_up_interruptible(&self->ias_wait); + return 0; } /* @@ -1803,6 +1954,7 @@ static int irda_extract_ias_value(struct irda_ias_set *ias_opt, /* NULL terminate the string (avoid troubles) */ ias_opt->attribute.irda_attrib_string.string[ias_value->len] = '\0'; break; + case IAS_MISSING: default : return -EINVAL; } @@ -1825,11 +1977,11 @@ static int irda_getsockopt(struct socket *sock, int level, int optname, struct sock *sk = sock->sk; struct irda_sock *self; struct irda_device_list list; - struct irda_device_info *info; - discovery_t *discovery; + struct irda_device_info *discoveries; struct irda_ias_set ias_opt; /* IAS get/query params */ struct ias_object * ias_obj; /* Object in IAS */ struct ias_attrib * ias_attr; /* Attribute in IAS object */ + int daddr = DEV_ADDR_ANY; /* Dest address for IAS queries */ int val = 0; int len = 0; int err; @@ -1845,67 +1997,38 @@ static int irda_getsockopt(struct socket *sock, int level, int optname, switch (optname) { case IRLMP_ENUMDEVICES: - /* Tell IrLMP we want to be notified */ - irlmp_update_client(self->ckey, self->mask, NULL, - irda_discovery_indication); - - /* Do some discovery */ - irlmp_discovery_request(self->nslots); - + /* Ask lmp for the current discovery log */ + discoveries = irlmp_get_discoveries(&list.len, self->mask); /* Check if the we got some results */ - if (!cachelog) - return -EAGAIN; + if (discoveries == NULL) + return -EAGAIN; /* Didn't find any devices */ + err = 0; - info = &list.dev[0]; + /* Write total list length back to client */ + if (copy_to_user(optval, &list, + sizeof(struct irda_device_list) - + sizeof(struct irda_device_info))) + err = -EFAULT; /* Offset to first device entry */ offset = sizeof(struct irda_device_list) - sizeof(struct irda_device_info); - total = offset; /* Initialized to size of the device list */ - list.len = 0; /* Initialize lenght of list */ - - /* - * Now, check all discovered devices (if any), and notify - * client only about the services that the client is - * interested in - */ - discovery = (discovery_t *) hashbin_get_first(cachelog); - while (discovery != NULL) { - /* Mask out the ones we don't want */ - if (discovery->hints.word & self->mask) { - /* Check if room for this device entry */ - if (len-total<sizeof(struct irda_device_info)) - break; - - /* Copy discovery information */ - info->saddr = discovery->saddr; - info->daddr = discovery->daddr; - info->charset = discovery->charset; - info->hints[0] = discovery->hints.byte[0]; - info->hints[1] = discovery->hints.byte[1]; - strncpy(info->info, discovery->nickname, - NICKNAME_MAX_LEN); - - if (copy_to_user(optval+total, info, - sizeof(struct irda_device_info))) - return -EFAULT; - list.len++; - total += sizeof(struct irda_device_info); - } - discovery = (discovery_t *) hashbin_get_next(cachelog); - } - cachelog = NULL; + /* Copy the list itself */ + total = offset + (list.len * sizeof(struct irda_device_info)); + if (total > len) + total = len; + if (copy_to_user(optval+offset, discoveries, total - offset)) + err = -EFAULT; /* Write total number of bytes used back to client */ if (put_user(total, optlen)) - return -EFAULT; + err = -EFAULT; - /* Write total list length back to client */ - if (copy_to_user(optval, &list, - sizeof(struct irda_device_list) - - sizeof(struct irda_device_info))) - return -EFAULT; + /* Free up our buffer */ + kfree(discoveries); + if (err) + return err; break; case IRLMP_MAX_SDU_SIZE: val = self->max_data_size; @@ -1964,6 +2087,26 @@ static int irda_getsockopt(struct socket *sock, int level, int optname, if (copy_from_user((char *) &ias_opt, (char *)optval, len)) return -EFAULT; + /* At this point, there are two cases... + * 1) the socket is connected - that's the easy case, we + * just query the device we are connected to... + * 2) the socket is not connected - the user doesn't want + * to connect and/or may not have a valid service name + * (so can't create a fake connection). In this case, + * we assume that the user pass us a valid destination + * address in the requesting structure... + */ + if(self->daddr != DEV_ADDR_ANY) { + /* We are connected - reuse known daddr */ + daddr = self->daddr; + } else { + /* We are not connected, we must specify a valid + * destination address */ + daddr = ias_opt.daddr; + if((!daddr) || (daddr == DEV_ADDR_ANY)) + return -EINVAL; + } + /* Check that we can proceed with IAP */ if (self->iriap) { WARNING(__FUNCTION__ @@ -1972,26 +2115,34 @@ static int irda_getsockopt(struct socket *sock, int level, int optname, } self->iriap = iriap_open(LSAP_ANY, IAS_CLIENT, self, - irda_simple_getvalue_confirm); + irda_getvalue_confirm); /* Treat unexpected signals as disconnect */ self->errno = -EHOSTUNREACH; /* Query remote LM-IAS */ - iriap_getvaluebyclass_request(self->iriap, - self->saddr, self->daddr, + iriap_getvaluebyclass_request(self->iriap, + self->saddr, daddr, ias_opt.irda_class_name, ias_opt.irda_attrib_name); - /* Wait for answer */ - interruptible_sleep_on(&self->ias_wait); + /* Wait for answer (if not already failed) */ + if(self->iriap != NULL) + interruptible_sleep_on(&self->query_wait); /* Check what happened */ if (self->errno) - return (self->errno); + { + /* Requested object/attribute doesn't exist */ + if((self->errno == IAS_CLASS_UNKNOWN) || + (self->errno == IAS_ATTRIB_UNKNOWN)) + return (-EADDRNOTAVAIL); + else + return (-EHOSTUNREACH); + } /* Translate from internal to user structure */ err = irda_extract_ias_value(&ias_opt, self->ias_result); if (self->ias_result) - kfree(self->ias_result); + irias_delete_value(self->ias_result); if (err) return err; @@ -2001,6 +2152,76 @@ static int irda_getsockopt(struct socket *sock, int level, int optname, return -EFAULT; /* Note : don't need to put optlen, we checked it */ break; + case IRLMP_WAITDEVICE: + /* This function is just another way of seeing life ;-) + * IRLMP_ENUMDEVICES assumes that you have a static network, + * and that you just want to pick one of the devices present. + * On the other hand, in here we assume that no device is + * present and that at some point in the future a device will + * come into range. When this device arrive, we just wake + * up the caller, so that he has time to connect to it before + * the device goes away... + * Note : once the node has been discovered for more than a + * few second, it won't trigger this function, unless it + * goes away and come back changes its hint bits (so we + * might call it IRLMP_WAITNEWDEVICE). + */ + + /* Check that the user is passing us an int */ + if (len != sizeof(int)) + return -EINVAL; + /* Get timeout in ms (max time we block the caller) */ + if (get_user(val, (int *)optval)) + return -EFAULT; + + /* Tell IrLMP we want to be notified */ + irlmp_update_client(self->ckey, self->mask, + irda_selective_discovery_indication, + NULL, (void *) self); + + /* Do some discovery (and also return cached results) */ + irlmp_discovery_request(self->nslots); + + /* Wait until a node is discovered */ + if (!self->cachediscovery) { + IRDA_DEBUG(1, __FUNCTION__ + "(), nothing discovered yet, going to sleep...\n"); + + /* Set watchdog timer to expire in <val> ms. */ + self->watchdog.function = irda_discovery_timeout; + self->watchdog.data = (unsigned long) self; + self->watchdog.expires = jiffies + (val * HZ/1000); + add_timer(&(self->watchdog)); + + /* Wait for IR-LMP to call us back */ + interruptible_sleep_on(&self->query_wait); + + /* If watchdog is still activated, kill it! */ + if(timer_pending(&(self->watchdog))) + del_timer(&(self->watchdog)); + + IRDA_DEBUG(1, __FUNCTION__ + "(), ...waking up !\n"); + } + else + IRDA_DEBUG(1, __FUNCTION__ + "(), found immediately !\n"); + + /* Tell IrLMP that we have been notified */ + irlmp_update_client(self->ckey, self->mask, NULL, NULL, NULL); + + /* Check if the we got some results */ + if (!self->cachediscovery) + return -EAGAIN; /* Didn't find any devices */ + /* Cleanup */ + self->cachediscovery = NULL; + + /* Note : We don't return anything to the user. + * We could return the device that triggered the wake up, + * but it's probably better to force the user to query + * the whole discovery log and let him pick one device... + */ + break; default: return -ENOPROTOOPT; } @@ -2150,24 +2371,44 @@ static struct notifier_block irda_dev_notifier = { }; /* + * Function irda_proc_modcount (inode, fill) + * + * Use by the proc file system functions to prevent the irda module + * being removed while the use is standing in the net/irda directory + */ +void irda_proc_modcount(struct inode *inode, int fill) +{ +#ifdef MODULE +#ifdef CONFIG_PROC_FS + if (fill) + MOD_INC_USE_COUNT; + else + MOD_DEC_USE_COUNT; +#endif /* CONFIG_PROC_FS */ +#endif /* MODULE */ +} + +/* * Function irda_proto_init (pro) * * Initialize IrDA protocol layer * */ -static int __init irda_proto_init(void) +int __init irda_proto_init(void) { - sock_register(&irda_family_ops); - - irda_packet_type.type = htons(ETH_P_IRDA); + sock_register(&irda_family_ops); + + irda_packet_type.type = htons(ETH_P_IRDA); dev_add_pack(&irda_packet_type); - - register_netdevice_notifier(&irda_dev_notifier); - - irda_init(); + + register_netdevice_notifier(&irda_dev_notifier); + + irda_init(); +#ifdef MODULE + irda_device_init(); /* Called by init/main.c when non-modular */ +#endif return 0; } -module_init(irda_proto_init); /* * Function irda_proto_cleanup (void) @@ -2188,5 +2429,11 @@ void irda_proto_cleanup(void) return; } +module_init(irda_proto_init); module_exit(irda_proto_cleanup); + +MODULE_AUTHOR("Dag Brattli <dagb@cs.uit.no>"); +MODULE_DESCRIPTION("The Linux IrDA Protocol Subsystem"); +MODULE_PARM(irda_debug, "1l"); #endif /* MODULE */ + diff --git a/net/irda/compressors/irda_deflate.c b/net/irda/compressors/irda_deflate.c index 245185500..f0d2cbb0d 100644 --- a/net/irda/compressors/irda_deflate.c +++ b/net/irda/compressors/irda_deflate.c @@ -561,37 +561,37 @@ extern void irda_unregister_compressor (struct compressor *cp); * Procedures exported to if_ppp.c. */ static struct compressor irda_deflate = { - CI_DEFLATE, /* compress_proto */ - z_comp_alloc, /* comp_alloc */ - z_comp_free, /* comp_free */ - z_comp_init, /* comp_init */ - z_comp_reset, /* comp_reset */ - z_compress, /* compress */ - z_comp_stats, /* comp_stat */ - z_decomp_alloc, /* decomp_alloc */ - z_decomp_free, /* decomp_free */ - z_decomp_init, /* decomp_init */ - z_decomp_reset, /* decomp_reset */ - z_decompress, /* decompress */ - z_incomp, /* incomp */ - z_comp_stats, /* decomp_stat */ +compress_proto: CI_DEFLATE, +comp_alloc: z_comp_alloc, +comp_free: z_comp_free, +comp_init: z_comp_init, +comp_reset: z_comp_reset, +compress: z_compress, +comp_stat: z_comp_stats, +decomp_alloc: z_decomp_alloc, +decomp_free: z_decomp_free, +decomp_init: z_decomp_init, +decomp_reset: z_decomp_reset, +decompress: z_decompress, +incomp: z_incomp, +decomp_stat: z_comp_stats }; static struct compressor irda_deflate_draft = { - CI_DEFLATE_DRAFT, /* compress_proto */ - z_comp_alloc, /* comp_alloc */ - z_comp_free, /* comp_free */ - z_comp_init, /* comp_init */ - z_comp_reset, /* comp_reset */ - z_compress, /* compress */ - z_comp_stats, /* comp_stat */ - z_decomp_alloc, /* decomp_alloc */ - z_decomp_free, /* decomp_free */ - z_decomp_init, /* decomp_init */ - z_decomp_reset, /* decomp_reset */ - z_decompress, /* decompress */ - z_incomp, /* incomp */ - z_comp_stats, /* decomp_stat */ +compress_proto: CI_DEFLATE_DRAFT, +comp_alloc: z_comp_alloc, +comp_free: z_comp_free, +comp_init: z_comp_init, +comp_reset: z_comp_reset, +compress: z_compress, +comp_stat: z_comp_stats, +decomp_alloc: z_decomp_alloc, +decomp_free: z_decomp_free, +decomp_init: z_decomp_init, +decomp_reset: z_decomp_reset, +decompress: z_decompress, +incomp: z_incomp, +decomp_stat: z_comp_stats }; int __init irda_deflate_init(void) diff --git a/net/irda/discovery.c b/net/irda/discovery.c index 957426154..d2fe17ff8 100644 --- a/net/irda/discovery.c +++ b/net/irda/discovery.c @@ -43,13 +43,25 @@ * * Add a new discovery to the cachelog, and remove any old discoveries * from the same device + * + * Note : we try to preserve the time this device was *first* discovered + * (as opposed to the time of last discovery used for cleanup). This is + * used by clients waiting for discovery events to tell if the device + * discovered is "new" or just the same old one. They can't rely there + * on a binary flag (new/old), because not all discovery events are + * propagated to them, and they might not always listen, so they would + * miss some new devices popping up... + * Jean II */ void irlmp_add_discovery(hashbin_t *cachelog, discovery_t *new) { discovery_t *discovery, *node; unsigned long flags; - spin_lock_irqsave(&irlmp->lock, flags); + /* Set time of first discovery if node is new (see below) */ + new->first_timestamp = new->timestamp; + + spin_lock_irqsave(&irlmp->log_lock, flags); /* * Remove all discoveries of devices that has previously been @@ -59,27 +71,31 @@ void irlmp_add_discovery(hashbin_t *cachelog, discovery_t *new) */ discovery = (discovery_t *) hashbin_get_first(cachelog); while (discovery != NULL ) { - node = discovery; + node = discovery; - /* Be sure to stay one item ahead */ - discovery = (discovery_t *) hashbin_get_next(cachelog); - - if ((node->daddr == new->daddr) || - (strcmp(node->nickname, new->nickname) == 0)) - { - /* This discovery is a previous discovery - * from the same device, so just remove it - */ - hashbin_remove(cachelog, node->daddr, NULL); - kfree(node); - } - } + /* Be sure to stay one item ahead */ + discovery = (discovery_t *) hashbin_get_next(cachelog); + if ((node->saddr == new->saddr) && + ((node->daddr == new->daddr) || + (strcmp(node->nickname, new->nickname) == 0))) + { + /* This discovery is a previous discovery + * from the same device, so just remove it + */ + hashbin_remove_this(cachelog, (irda_queue_t *) node); + /* Check if hints bits have changed */ + if(node->hints.word == new->hints.word) + /* Set time of first discovery for this node */ + new->first_timestamp = node->first_timestamp; + kfree(node); + } + } /* Insert the new and updated version */ - hashbin_insert(cachelog, (queue_t *) new, new->daddr, NULL); + hashbin_insert(cachelog, (irda_queue_t *) new, new->daddr, NULL); - spin_unlock_irqrestore(&irlmp->lock, flags); + spin_unlock_irqrestore(&irlmp->log_lock, flags); } /* @@ -120,13 +136,18 @@ void irlmp_add_discovery_log(hashbin_t *cachelog, hashbin_t *log) * * Go through all discoveries and expire all that has stayed to long * + * Note : this assume that IrLAP won't change its saddr, which + * currently is a valid assumption... */ void irlmp_expire_discoveries(hashbin_t *log, __u32 saddr, int force) { discovery_t *discovery, *curr; + unsigned long flags; IRDA_DEBUG(4, __FUNCTION__ "()\n"); + spin_lock_irqsave(&irlmp->log_lock, flags); + discovery = (discovery_t *) hashbin_get_first(log); while (discovery != NULL) { curr = discovery; @@ -135,14 +156,20 @@ void irlmp_expire_discoveries(hashbin_t *log, __u32 saddr, int force) discovery = (discovery_t *) hashbin_get_next(log); /* Test if it's time to expire this discovery */ - if ((curr->saddr == saddr) && (force || - ((jiffies - curr->timestamp) > DISCOVERY_EXPIRE_TIMEOUT))) + if ((curr->saddr == saddr) && + (force || + ((jiffies - curr->timestamp) > DISCOVERY_EXPIRE_TIMEOUT))) { - curr = hashbin_remove(log, curr->daddr, NULL); + /* Tell IrLMP and registered clients about it */ + irlmp_discovery_expiry(curr); + /* Remove it from the log */ + curr = hashbin_remove_this(log, (irda_queue_t *) curr); if (curr) kfree(curr); } } + + spin_unlock_irqrestore(&irlmp->log_lock, flags); } /* @@ -169,6 +196,75 @@ void irlmp_dump_discoveries(hashbin_t *log) } /* + * Function irlmp_copy_discoveries (log, pn, mask) + * + * Copy all discoveries in a buffer + * + * This function implement a safe way for lmp clients to access the + * discovery log. The basic problem is that we don't want the log + * to change (add/remove) while the client is reading it. If the + * lmp client manipulate directly the hashbin, he is sure to get + * into troubles... + * The idea is that we copy all the current discovery log in a buffer + * which is specific to the client and pass this copy to him. As we + * do this operation with the spinlock grabbed, we are safe... + * Note : we don't want those clients to grab the spinlock, because + * we have no control on how long they will hold it... + * Note : we choose to copy the log in "struct irda_device_info" to + * save space... + * Note : the client must kfree himself() the log... + * Jean II + */ +struct irda_device_info *irlmp_copy_discoveries(hashbin_t *log, int *pn, __u16 mask) +{ + discovery_t * discovery; + unsigned long flags; + struct irda_device_info * buffer; + int i = 0; + int n; + + ASSERT(pn != NULL, return NULL;); + + /* Check if log is empty */ + if(log == NULL) + return NULL; + + /* Save spin lock - spinlock should be discovery specific */ + spin_lock_irqsave(&irlmp->log_lock, flags); + + /* Create the client specific buffer */ + n = HASHBIN_GET_SIZE(log); + buffer = kmalloc(n * sizeof(struct irda_device_info), GFP_ATOMIC); + if (buffer == NULL) { + spin_unlock_irqrestore(&irlmp->log_lock, flags); + return NULL; + } + + discovery = (discovery_t *) hashbin_get_first(log); + while ((discovery != NULL) && (i < n)) { + /* Mask out the ones we don't want */ + if (discovery->hints.word & mask) { + /* Copy discovery information */ + buffer[i].saddr = discovery->saddr; + buffer[i].daddr = discovery->daddr; + buffer[i].charset = discovery->charset; + buffer[i].hints[0] = discovery->hints.byte[0]; + buffer[i].hints[1] = discovery->hints.byte[1]; + strncpy(buffer[i].info, discovery->nickname, + NICKNAME_MAX_LEN); + i++; + } + discovery = (discovery_t *) hashbin_get_next(log); + } + + spin_unlock_irqrestore(&irlmp->log_lock, flags); + + /* Get the actual number of device in the buffer and return */ + *pn = i; + return(buffer); +} + +/* * Function irlmp_find_device (name, saddr) * * Look through the discovery log at each of the links and try to find @@ -180,7 +276,7 @@ __u32 irlmp_find_device(hashbin_t *cachelog, char *name, __u32 *saddr) unsigned long flags; discovery_t *d; - spin_lock_irqsave(&irlmp->lock, flags); + spin_lock_irqsave(&irlmp->log_lock, flags); /* Look at all discoveries for that link */ d = (discovery_t *) hashbin_get_first(cachelog); @@ -192,13 +288,13 @@ __u32 irlmp_find_device(hashbin_t *cachelog, char *name, __u32 *saddr) if (strcmp(name, d->nickname) == 0) { *saddr = d->saddr; - spin_unlock_irqrestore(&irlmp->lock, flags); + spin_unlock_irqrestore(&irlmp->log_lock, flags); return d->daddr; } d = (discovery_t *) hashbin_get_next(cachelog); } - spin_unlock_irqrestore(&irlmp->lock, flags); + spin_unlock_irqrestore(&irlmp->log_lock, flags); return 0; } @@ -209,23 +305,23 @@ __u32 irlmp_find_device(hashbin_t *cachelog, char *name, __u32 *saddr) * Print discovery information in /proc file system * */ -int discovery_proc_read(char *buf, char **start, off_t offset, int len, +int discovery_proc_read(char *buf, char **start, off_t offset, int length, int unused) { discovery_t *discovery; unsigned long flags; hashbin_t *cachelog = irlmp_get_cachelog(); + int len = 0; if (!irlmp) return len; len = sprintf(buf, "IrLMP: Discovery log:\n\n"); - save_flags(flags); - cli(); - + spin_lock_irqsave(&irlmp->log_lock, flags); + discovery = (discovery_t *) hashbin_get_first(cachelog); - while ( discovery != NULL) { + while (( discovery != NULL) && (len < length)) { len += sprintf(buf+len, "nickname: %s,", discovery->nickname); len += sprintf(buf+len, " hint: 0x%02x%02x", @@ -266,7 +362,7 @@ int discovery_proc_read(char *buf, char **start, off_t offset, int len, discovery = (discovery_t *) hashbin_get_next(cachelog); } - restore_flags(flags); + spin_unlock_irqrestore(&irlmp->log_lock, flags); return len; } diff --git a/net/irda/ircomm/ircomm_core.c b/net/irda/ircomm/ircomm_core.c index 805186128..ae626e08b 100644 --- a/net/irda/ircomm/ircomm_core.c +++ b/net/irda/ircomm/ircomm_core.c @@ -127,7 +127,7 @@ struct ircomm_cb *ircomm_open(notify_t *notify, __u8 service_type, int line) self->service_type = service_type; self->line = line; - hashbin_insert(ircomm, (queue_t *) self, line, NULL); + hashbin_insert(ircomm, (irda_queue_t *) self, line, NULL); ircomm_next_state(self, IRCOMM_IDLE); @@ -512,6 +512,9 @@ int ircomm_proc_read(char *buf, char **start, off_t offset, int len) #endif /* CONFIG_PROC_FS */ #ifdef MODULE +MODULE_AUTHOR("Dag Brattli <dagb@cs.uit.no>"); +MODULE_DESCRIPTION("IrCOMM protocol"); + int init_module(void) { return ircomm_init(); diff --git a/net/irda/ircomm/ircomm_tty.c b/net/irda/ircomm/ircomm_tty.c index d785983da..67925c5b5 100644 --- a/net/irda/ircomm/ircomm_tty.c +++ b/net/irda/ircomm/ircomm_tty.c @@ -29,6 +29,7 @@ * ********************************************************************/ +#include <linux/config.h> #include <linux/init.h> #include <linux/module.h> #include <linux/fs.h> @@ -69,9 +70,10 @@ static int ircomm_tty_control_indication(void *instance, void *sap, struct sk_buff *skb); static void ircomm_tty_flow_indication(void *instance, void *sap, LOCAL_FLOW cmd); +#ifdef CONFIG_PROC_FS static int ircomm_tty_read_proc(char *buf, char **start, off_t offset, int len, int *eof, void *unused); - +#endif /* CONFIG_PROC_FS */ static struct tty_driver driver; static int ircomm_tty_refcount; /* If we manage several devices */ @@ -126,8 +128,9 @@ int __init ircomm_tty_init(void) driver.start = ircomm_tty_start; driver.hangup = ircomm_tty_hangup; driver.wait_until_sent = ircomm_tty_wait_until_sent; +#ifdef CONFIG_PROC_FS driver.read_proc = ircomm_tty_read_proc; - +#endif /* CONFIG_PROC_FS */ if (tty_register_driver(&driver)) { ERROR(__FUNCTION__ "Couldn't register serial driver\n"); return -1; @@ -429,7 +432,7 @@ static int ircomm_tty_open(struct tty_struct *tty, struct file *filp) tty->termios->c_oflag = 0; /* Insert into hash */ - hashbin_insert(ircomm_tty, (queue_t *) self, line, NULL); + hashbin_insert(ircomm_tty, (irda_queue_t *) self, line, NULL); } self->open_count++; @@ -1319,6 +1322,7 @@ static int ircomm_tty_line_info(struct ircomm_tty_cb *self, char *buf) * * */ +#ifdef CONFIG_PROC_FS static int ircomm_tty_read_proc(char *buf, char **start, off_t offset, int len, int *eof, void *unused) { @@ -1349,9 +1353,12 @@ done: *start = buf + (offset-begin); return ((len < begin+count-offset) ? len : begin+count-offset); } - +#endif /* CONFIG_PROC_FS */ #ifdef MODULE +MODULE_AUTHOR("Dag Brattli <dagb@cs.uit.no>"); +MODULE_DESCRIPTION("IrCOMM serial TTY driver"); + int init_module(void) { return ircomm_tty_init(); diff --git a/net/irda/ircomm/ircomm_tty_attach.c b/net/irda/ircomm/ircomm_tty_attach.c index 94c9fee09..ad5ee4b0f 100644 --- a/net/irda/ircomm/ircomm_tty_attach.c +++ b/net/irda/ircomm/ircomm_tty_attach.c @@ -46,7 +46,8 @@ #include <net/irda/ircomm_tty_attach.h> static void ircomm_tty_ias_register(struct ircomm_tty_cb *self); -static void ircomm_tty_discovery_indication(discovery_t *discovery); +static void ircomm_tty_discovery_indication(discovery_t *discovery, + void *priv); static void ircomm_tty_getvalue_confirm(int result, __u16 obj_id, struct ias_value *value, void *priv); void ircomm_tty_start_watchdog_timer(struct ircomm_tty_cb *self, int timeout); @@ -213,7 +214,7 @@ static void ircomm_tty_ias_register(struct ircomm_tty_cb *self) /* Register IrLPT with LM-IAS */ self->obj = irias_new_object("IrLPT", IAS_IRLPT_ID); irias_add_integer_attrib(self->obj, "IrDA:IrLMP:LsapSel", - self->slsap_sel); + self->slsap_sel, IAS_KERNEL_ATTR); irias_insert_object(self->obj); } else { hints = irlmp_service_to_hint(S_COMM); @@ -221,7 +222,7 @@ static void ircomm_tty_ias_register(struct ircomm_tty_cb *self) /* Register IrCOMM with LM-IAS */ self->obj = irias_new_object("IrDA:IrCOMM", IAS_IRCOMM_ID); irias_add_integer_attrib(self->obj, "IrDA:TinyTP:LsapSel", - self->slsap_sel); + self->slsap_sel, IAS_KERNEL_ATTR); /* Code the parameters into the buffer */ irda_param_pack(oct_seq, "bbbbbb", @@ -229,12 +230,13 @@ static void ircomm_tty_ias_register(struct ircomm_tty_cb *self) IRCOMM_PORT_TYPE, 1, IRCOMM_SERIAL); /* Register parameters with LM-IAS */ - irias_add_octseq_attrib(self->obj, "Parameters", oct_seq, 6); + irias_add_octseq_attrib(self->obj, "Parameters", oct_seq, 6, + IAS_KERNEL_ATTR); irias_insert_object(self->obj); } self->skey = irlmp_register_service(hints); self->ckey = irlmp_register_client( - hints, ircomm_tty_discovery_indication, NULL); + hints, ircomm_tty_discovery_indication, NULL, (void *) self); } /* @@ -302,7 +304,8 @@ int ircomm_tty_send_initial_parameters(struct ircomm_tty_cb *self) * device it is, and which services it has. * */ -static void ircomm_tty_discovery_indication(discovery_t *discovery) +static void ircomm_tty_discovery_indication(discovery_t *discovery, + void *priv) { struct ircomm_tty_cb *self; struct ircomm_tty_info info; diff --git a/net/irda/irda_device.c b/net/irda/irda_device.c index ca2903316..c77ca6268 100644 --- a/net/irda/irda_device.c +++ b/net/irda/irda_device.c @@ -58,6 +58,8 @@ extern int irtty_init(void); extern int nsc_ircc_init(void); extern int ircc_init(void); +extern int toshoboe_init(void); +extern int litelink_init(void); extern int w83977af_init(void); extern int esi_init(void); extern int tekram_init(void); @@ -183,7 +185,7 @@ void irda_device_set_media_busy(struct net_device *dev, int status) IRDA_DEBUG( 4, "Media busy!\n"); } else { self->media_busy = FALSE; - del_timer(&self->media_busy_timer); + irlap_stop_mbusy_timer(self); } } @@ -379,7 +381,7 @@ struct irda_task *irda_task_execute(void *instance, init_timer(&task->timer); /* Register task */ - hashbin_insert(tasks, (queue_t *) task, (int) task, NULL); + hashbin_insert(tasks, (irda_queue_t *) task, (int) task, NULL); /* No time to waste, so lets get going! */ ret = irda_task_kick(task); @@ -518,7 +520,7 @@ int irda_device_register_dongle(struct dongle_reg *new) } /* Insert IrDA dongle into hashbin */ - hashbin_insert(dongles, (queue_t *) new, new->type, NULL); + hashbin_insert(dongles, (irda_queue_t *) new, new->type, NULL); return 0; } diff --git a/net/irda/iriap.c b/net/irda/iriap.c index 5f1140525..03014ac0d 100644 --- a/net/irda/iriap.c +++ b/net/irda/iriap.c @@ -107,7 +107,7 @@ int __init iriap_init(void) /* Register the Device object with LM-IAS */ obj = irias_new_object("Device", IAS_DEVICE_ID); - irias_add_string_attrib(obj, "DeviceName", "Linux"); + irias_add_string_attrib(obj, "DeviceName", "Linux", IAS_KERNEL_ATTR); oct_seq[0] = 0x01; /* Version 1 */ oct_seq[1] = 0x00; /* IAS support bits */ @@ -115,7 +115,8 @@ int __init iriap_init(void) #ifdef CONFIG_IRDA_ULTRA oct_seq[2] |= 0x04; /* Connectionless Data support */ #endif - irias_add_octseq_attrib(obj, "IrLMPSupport", oct_seq, 3); + irias_add_octseq_attrib(obj, "IrLMPSupport", oct_seq, 3, + IAS_KERNEL_ATTR); irias_insert_object(obj); /* @@ -179,7 +180,7 @@ struct iriap_cb *iriap_open(__u8 slsap_sel, int mode, void *priv, init_timer(&self->watchdog_timer); - hashbin_insert(iriap, (queue_t *) self, (int) self, NULL); + hashbin_insert(iriap, (irda_queue_t *) self, (int) self, NULL); /* Initialize state machines */ iriap_next_client_state(self, S_DISCONNECT); @@ -866,7 +867,7 @@ static int iriap_data_indication(void *instance, void *sap, iriap_getvaluebyclass_confirm(self, skb); break; case IAS_CLASS_UNKNOWN: - WARNING(__FUNCTION__ "(), No such class!\n"); + IRDA_DEBUG(1, __FUNCTION__ "(), No such class!\n"); /* Finished, close connection! */ iriap_disconnect_request(self); @@ -880,7 +881,7 @@ static int iriap_data_indication(void *instance, void *sap, dev_kfree_skb(skb); break; case IAS_ATTRIB_UNKNOWN: - WARNING(__FUNCTION__ "(), No such attribute!\n"); + IRDA_DEBUG(1, __FUNCTION__ "(), No such attribute!\n"); /* Finished, close connection! */ iriap_disconnect_request(self); @@ -889,7 +890,7 @@ static int iriap_data_indication(void *instance, void *sap, * no to use self anymore after calling confirm */ if (self->confirm) - self->confirm(IAS_CLASS_UNKNOWN, 0, NULL, + self->confirm(IAS_ATTRIB_UNKNOWN, 0, NULL, self->priv); dev_kfree_skb(skb); break; diff --git a/net/irda/irias_object.c b/net/irda/irias_object.c index 17ad3801d..4a8fbed4c 100644 --- a/net/irda/irias_object.c +++ b/net/irda/irias_object.c @@ -148,6 +148,37 @@ int irias_delete_object(struct ias_object *obj) } /* + * Function irias_delete_attrib (obj) + * + * Remove attribute from hashbin and, if it was the last attribute of + * the object, remove the object as well. + * + */ +int irias_delete_attrib(struct ias_object *obj, struct ias_attrib *attrib) +{ + struct ias_attrib *node; + + ASSERT(obj != NULL, return -1;); + ASSERT(obj->magic == IAS_OBJECT_MAGIC, return -1;); + ASSERT(attrib != NULL, return -1;); + + /* Remove atribute from object */ + node = hashbin_remove(obj->attribs, 0, attrib->name); + if (!node) + return 0; /* Already removed or non-existent */ + + /* Deallocate attribute */ + __irias_delete_attrib(node); + + /* Check if object has still some attributes */ + node = (struct ias_attrib *) hashbin_get_first(obj->attribs); + if (!node) + irias_delete_object(obj); + + return 0; +} + +/* * Function irias_insert_object (obj) * * Insert an object into the LM-IAS database @@ -158,7 +189,7 @@ void irias_insert_object(struct ias_object *obj) ASSERT(obj != NULL, return;); ASSERT(obj->magic == IAS_OBJECT_MAGIC, return;); - hashbin_insert(objects, (queue_t *) obj, 0, obj->name); + hashbin_insert(objects, (irda_queue_t *) obj, 0, obj->name); } /* @@ -201,7 +232,8 @@ struct ias_attrib *irias_find_attrib(struct ias_object *obj, char *name) * Add attribute to object * */ -void irias_add_attrib( struct ias_object *obj, struct ias_attrib *attrib) +void irias_add_attrib( struct ias_object *obj, struct ias_attrib *attrib, + int owner) { ASSERT(obj != NULL, return;); ASSERT(obj->magic == IAS_OBJECT_MAGIC, return;); @@ -209,7 +241,10 @@ void irias_add_attrib( struct ias_object *obj, struct ias_attrib *attrib) ASSERT(attrib != NULL, return;); ASSERT(attrib->magic == IAS_ATTRIB_MAGIC, return;); - hashbin_insert(obj->attribs, (queue_t *) attrib, 0, attrib->name); + /* Set if attrib is owned by kernel or user space */ + attrib->value->owner = owner; + + hashbin_insert(obj->attribs, (irda_queue_t *) attrib, 0, attrib->name); } /* @@ -262,7 +297,8 @@ int irias_object_change_attribute(char *obj_name, char *attrib_name, * Add an integer attribute to an LM-IAS object * */ -void irias_add_integer_attrib(struct ias_object *obj, char *name, int value) +void irias_add_integer_attrib(struct ias_object *obj, char *name, int value, + int owner) { struct ias_attrib *attrib; @@ -284,7 +320,7 @@ void irias_add_integer_attrib(struct ias_object *obj, char *name, int value) /* Insert value */ attrib->value = irias_new_integer_value(value); - irias_add_attrib(obj, attrib); + irias_add_attrib(obj, attrib, owner); } /* @@ -295,7 +331,7 @@ void irias_add_integer_attrib(struct ias_object *obj, char *name, int value) */ void irias_add_octseq_attrib(struct ias_object *obj, char *name, __u8 *octets, - int len) + int len, int owner) { struct ias_attrib *attrib; @@ -319,7 +355,7 @@ void irias_add_octseq_attrib(struct ias_object *obj, char *name, __u8 *octets, attrib->value = irias_new_octseq_value( octets, len); - irias_add_attrib(obj, attrib); + irias_add_attrib(obj, attrib, owner); } /* @@ -328,7 +364,8 @@ void irias_add_octseq_attrib(struct ias_object *obj, char *name, __u8 *octets, * Add a string attribute to an LM-IAS object * */ -void irias_add_string_attrib(struct ias_object *obj, char *name, char *value) +void irias_add_string_attrib(struct ias_object *obj, char *name, char *value, + int owner) { struct ias_attrib *attrib; @@ -351,7 +388,7 @@ void irias_add_string_attrib(struct ias_object *obj, char *name, char *value) attrib->value = irias_new_string_value(value); - irias_add_attrib(obj, attrib); + irias_add_attrib(obj, attrib, owner); } /* diff --git a/net/irda/irlan/irlan_client.c b/net/irda/irlan/irlan_client.c index 7c2d5b94e..e3a3ada15 100644 --- a/net/irda/irlan/irlan_client.c +++ b/net/irda/irlan/irlan_client.c @@ -104,8 +104,6 @@ void irlan_client_start_kick_timer(struct irlan_cb *self, int timeout) */ void irlan_client_wakeup(struct irlan_cb *self, __u32 saddr, __u32 daddr) { - struct irmanager_event mgr_event; - IRDA_DEBUG(1, __FUNCTION__ "()\n"); ASSERT(self != NULL, return;); @@ -117,41 +115,24 @@ void irlan_client_wakeup(struct irlan_cb *self, __u32 saddr, __u32 daddr) */ if ((self->client.state != IRLAN_IDLE) || (self->provider.access_type == ACCESS_DIRECT)) - return; + { + IRDA_DEBUG(0, __FUNCTION__ "(), already awake!\n"); + return; + } - /* saddr may have changed! */ + /* Address may have changed! */ self->saddr = saddr; - - /* Before we try to connect, we check if network device is up. If it - * is up, that means that the "user" really wants to connect. If not - * we notify the user about the possibility of an IrLAN connection - */ - if (netif_running(&self->dev)) { - /* Open TSAPs */ - irlan_client_open_ctrl_tsap(self); - irlan_open_data_tsap(self); - - irlan_do_client_event(self, IRLAN_DISCOVERY_INDICATION, NULL); - } else if (self->notify_irmanager) { - /* - * Tell irmanager that the device can now be - * configured but only if the device was not taken - * down by the user - */ - mgr_event.event = EVENT_IRLAN_START; - strcpy(mgr_event.devname, self->dev.name); - irmanager_notify(&mgr_event); - - /* - * We set this so that we only notify once, since if - * configuration of the network device fails, the user - * will have to sort it out first anyway. No need to - * try again. - */ - self->notify_irmanager = FALSE; + + if (self->disconnect_reason == LM_USER_REQUEST) { + IRDA_DEBUG(0, __FUNCTION__ "(), still stopped by user\n"); + return; } - /* Restart watchdog timer */ - irlan_start_watchdog_timer(self, IRLAN_TIMEOUT); + + /* Open TSAPs */ + irlan_client_open_ctrl_tsap(self); + irlan_open_data_tsap(self); + + irlan_do_client_event(self, IRLAN_DISCOVERY_INDICATION, NULL); /* Start kick timer */ irlan_client_start_kick_timer(self, 2*HZ); @@ -163,7 +144,7 @@ void irlan_client_wakeup(struct irlan_cb *self, __u32 saddr, __u32 daddr) * Remote device with IrLAN server support discovered * */ -void irlan_client_discovery_indication(discovery_t *discovery) +void irlan_client_discovery_indication(discovery_t *discovery, void *priv) { struct irlan_cb *self; __u32 saddr, daddr; @@ -176,29 +157,16 @@ void irlan_client_discovery_indication(discovery_t *discovery) saddr = discovery->saddr; daddr = discovery->daddr; - /* - * Check if we already dealing with this provider. - */ - self = (struct irlan_cb *) hashbin_find(irlan, daddr, NULL); - if (self) { + /* Find instance */ + self = (struct irlan_cb *) hashbin_get_first(irlan); + if (self) { ASSERT(self->magic == IRLAN_MAGIC, return;); IRDA_DEBUG(1, __FUNCTION__ "(), Found instance (%08x)!\n", daddr); irlan_client_wakeup(self, saddr, daddr); - - return; } - - /* - * We have no instance for daddr, so start a new one - */ - IRDA_DEBUG(1, __FUNCTION__ "(), starting new instance!\n"); - self = irlan_open(saddr, daddr, TRUE); - - /* Restart watchdog timer */ - irlan_start_watchdog_timer(self, IRLAN_TIMEOUT); } /* @@ -449,9 +417,7 @@ static void irlan_check_response_param(struct irlan_cb *self, char *param, ASSERT(self != NULL, return;); ASSERT(self->magic == IRLAN_MAGIC, return;); - /* - * Media type - */ + /* Media type */ if (strcmp(param, "MEDIA") == 0) { if (strcmp(value, "802.3") == 0) self->media = MEDIA_802_3; @@ -487,9 +453,7 @@ static void irlan_check_response_param(struct irlan_cb *self, char *param, IRDA_DEBUG(2, __FUNCTION__ "(), unknown access type!\n"); } } - /* - * IRLAN version - */ + /* IRLAN version */ if (strcmp(param, "IRLAN_VER") == 0) { IRDA_DEBUG(4, "IrLAN version %d.%d\n", (__u8) value[0], (__u8) value[1]); @@ -498,9 +462,7 @@ static void irlan_check_response_param(struct irlan_cb *self, char *param, self->version[1] = value[1]; return; } - /* - * Which remote TSAP to use for data channel - */ + /* Which remote TSAP to use for data channel */ if (strcmp(param, "DATA_CHAN") == 0) { self->dtsap_sel_data = value[0]; IRDA_DEBUG(4, "Data TSAP = %02x\n", self->dtsap_sel_data); @@ -521,9 +483,7 @@ static void irlan_check_response_param(struct irlan_cb *self, char *param, self->client.max_frame); } - /* - * RECONNECT_KEY, in case the link goes down! - */ + /* RECONNECT_KEY, in case the link goes down! */ if (strcmp(param, "RECONNECT_KEY") == 0) { IRDA_DEBUG(4, "Got reconnect key: "); /* for (i = 0; i < val_len; i++) */ @@ -532,9 +492,7 @@ static void irlan_check_response_param(struct irlan_cb *self, char *param, self->client.key_len = val_len; IRDA_DEBUG(4, "\n"); } - /* - * FILTER_ENTRY, have we got an ethernet address? - */ + /* FILTER_ENTRY, have we got an ethernet address? */ if (strcmp(param, "FILTER_ENTRY") == 0) { bytes = value; IRDA_DEBUG(4, "Ethernet address = %02x:%02x:%02x:%02x:%02x:%02x\n", diff --git a/net/irda/irlan/irlan_common.c b/net/irda/irlan/irlan_common.c index 5975909fe..686c163dc 100644 --- a/net/irda/irlan/irlan_common.c +++ b/net/irda/irlan/irlan_common.c @@ -50,6 +50,14 @@ #include <net/irda/irlan_eth.h> #include <net/irda/irlan_filter.h> + +/* + * Send gratuitous ARP when connected to a new AP or not. May be a clever + * thing to do, but for some reason the machine crashes if you use DHCP. So + * lets not use it by default. + */ +#undef CONFIG_IRLAN_SEND_GRATUITOUS_ARP + /* extern char sysctl_devname[]; */ /* @@ -62,6 +70,7 @@ static __u32 ckey, skey; static int eth = 0; /* Use "eth" or "irlan" name for devices */ static int access = ACCESS_PEER; /* PEER, DIRECT or HOSTED */ +#ifdef CONFIG_PROC_FS static char *irlan_state[] = { "IRLAN_IDLE", "IRLAN_QUERY", @@ -88,6 +97,7 @@ static char *irlan_media[] = { "802.3", "802.5" }; +#endif /* CONFIG_PROC_FS */ static void __irlan_close(struct irlan_cb *self); static int __irlan_insert_param(struct sk_buff *skb, char *param, int type, @@ -102,59 +112,6 @@ extern struct proc_dir_entry *proc_irda; #endif /* CONFIG_PROC_FS */ /* - * Function irlan_watchdog_timer_expired (data) - * - * Something has gone wrong during the connection establishment - * - */ -void irlan_watchdog_timer_expired(void *data) -{ - struct irmanager_event mgr_event; - struct irlan_cb *self; - - IRDA_DEBUG(0, __FUNCTION__ "()\n"); - - self = (struct irlan_cb *) data; - - ASSERT(self != NULL, return;); - ASSERT(self->magic == IRLAN_MAGIC, return;); - - /* Check if device still configured */ - if (netif_running(&self->dev)) { - IRDA_DEBUG(0, __FUNCTION__ - "(), notifying irmanager to stop irlan!\n"); - mgr_event.event = EVENT_IRLAN_STOP; - sprintf(mgr_event.devname, "%s", self->dev.name); - irmanager_notify(&mgr_event); - - /* - * We set this to false, so that irlan_dev_close known that - * notify_irmanager should actually be set to TRUE again - * instead of FALSE, since this close has not been initiated - * by the user. - */ - self->notify_irmanager = FALSE; - } else { - IRDA_DEBUG(0, __FUNCTION__ "(), closing instance!\n"); - /*irlan_close(self);*/ - } -} - -/* - * Function irlan_start_watchdog_timer (self, timeout) - * - * - * - */ -void irlan_start_watchdog_timer(struct irlan_cb *self, int timeout) -{ - IRDA_DEBUG(4, __FUNCTION__ "()\n"); - - irda_start_timer(&self->watchdog_timer, timeout, (void *) self, - irlan_watchdog_timer_expired); -} - -/* * Function irlan_init (void) * * Initialize IrLAN layer @@ -165,8 +122,7 @@ int __init irlan_init(void) struct irlan_cb *new; __u16 hints; - IRDA_DEBUG(4, __FUNCTION__"()\n"); - + IRDA_DEBUG(0, __FUNCTION__ "()\n"); /* Allocate master structure */ irlan = hashbin_new(HB_LOCAL); if (irlan == NULL) { @@ -178,22 +134,20 @@ int __init irlan_init(void) #endif /* CONFIG_PROC_FS */ IRDA_DEBUG(4, __FUNCTION__ "()\n"); - hints = irlmp_service_to_hint(S_LAN); /* Register with IrLMP as a client */ - ckey = irlmp_register_client(hints, irlan_client_discovery_indication, - NULL); + ckey = irlmp_register_client(hints, &irlan_client_discovery_indication, + NULL, NULL); /* Register with IrLMP as a service */ skey = irlmp_register_service(hints); - /* Start the master IrLAN instance */ - new = irlan_open(DEV_ADDR_ANY, DEV_ADDR_ANY, FALSE); + /* Start the master IrLAN instance (the only one for now) */ + new = irlan_open(DEV_ADDR_ANY, DEV_ADDR_ANY); /* The master will only open its (listen) control TSAP */ irlan_provider_open_ctrl_tsap(new); - new->master = TRUE; /* Do some fast discovery! */ irlmp_discovery_request(DISCOVERY_DEFAULT_SLOTS); @@ -206,7 +160,6 @@ void irlan_cleanup(void) IRDA_DEBUG(4, __FUNCTION__ "()\n"); irlmp_unregister_client(ckey); - irlmp_unregister_service(skey); #ifdef CONFIG_PROC_FS @@ -242,8 +195,6 @@ int irlan_register_netdev(struct irlan_cb *self) IRDA_DEBUG(2, __FUNCTION__ "(), register_netdev() failed!\n"); return -1; } - self->netdev_registered = TRUE; - return 0; } @@ -253,7 +204,7 @@ int irlan_register_netdev(struct irlan_cb *self) * Open new instance of a client/provider, we should only register the * network device if this instance is ment for a particular client/provider */ -struct irlan_cb *irlan_open(__u32 saddr, __u32 daddr, int netdev) +struct irlan_cb *irlan_open(__u32 saddr, __u32 daddr) { struct irlan_cb *self; @@ -287,32 +238,28 @@ struct irlan_cb *irlan_open(__u32 saddr, __u32 daddr, int netdev) /* Provider access can only be PEER, DIRECT, or HOSTED */ self->provider.access_type = access; self->media = MEDIA_802_3; - - self->notify_irmanager = TRUE; - + self->disconnect_reason = LM_USER_REQUEST; init_timer(&self->watchdog_timer); init_timer(&self->client.kick_timer); + init_waitqueue_head(&self->open_wait); - hashbin_insert(irlan, (queue_t *) self, daddr, NULL); + hashbin_insert(irlan, (irda_queue_t *) self, daddr, NULL); skb_queue_head_init(&self->client.txq); irlan_next_client_state(self, IRLAN_IDLE); irlan_next_provider_state(self, IRLAN_IDLE); - /* Register network device now, or wait until some later time? */ - if (netdev) - irlan_register_netdev(self); + irlan_register_netdev(self); return self; } /* - * Function irlan_close (self) + * Function __irlan_close (self) * * This function closes and deallocates the IrLAN client instances. Be * aware that other functions which calles client_close() must call * hashbin_remove() first!!! - * */ static void __irlan_close(struct irlan_cb *self) { @@ -333,49 +280,13 @@ static void __irlan_close(struct irlan_cb *self) iriap_close(self->client.iriap); /* Remove frames queued on the control channel */ - while ((skb = skb_dequeue(&self->client.txq))) { + while ((skb = skb_dequeue(&self->client.txq))) dev_kfree_skb(skb); - } - if (self->netdev_registered) { - unregister_netdev(&self->dev); - self->netdev_registered = FALSE; - } + unregister_netdev(&self->dev); self->magic = 0; - kfree(self); -} - -/* - * Function irlan_close (self) - * - * Close instance - * - */ -void irlan_close(struct irlan_cb *self) -{ - struct irlan_cb *entry; - - IRDA_DEBUG(0, __FUNCTION__ "()\n"); - - ASSERT(self != NULL, return;); - ASSERT(self->magic == IRLAN_MAGIC, return;); - - /* Check if device is still configured */ - if (netif_running(&self->dev)) { - IRDA_DEBUG(0, __FUNCTION__ - "(), Device still configured, closing later!\n"); - - /* Give it a chance to reconnect */ - irlan_start_watchdog_timer(self, IRLAN_TIMEOUT); - return; - } - IRDA_DEBUG(2, __FUNCTION__ "(), daddr=%08x\n", self->daddr); - entry = hashbin_remove(irlan, self->daddr, NULL); - - ASSERT(entry == self, return;); - - __irlan_close(self); + kfree(self); } /* @@ -419,7 +330,7 @@ void irlan_connect_indication(void *instance, void *sap, struct qos_info *qos, irlan_open_unicast_addr(self); } /* Ready to transfer Ethernet frames (at last) */ - netif_start_queue(&self->dev); + netif_start_queue(&self->dev); /* Clear reason */ } void irlan_connect_confirm(void *instance, void *sap, struct qos_info *qos, @@ -454,7 +365,11 @@ void irlan_connect_confirm(void *instance, void *sap, struct qos_info *qos, /* Ready to transfer Ethernet frames */ netif_start_queue(&self->dev); + self->disconnect_reason = 0; /* Clear reason */ +#ifdef CONFIG_IRLAN_SEND_GRATUITOUS_ARP irlan_eth_send_gratuitous_arp(&self->dev); +#endif + wake_up_interruptible(&self->open_wait); } /* @@ -483,28 +398,34 @@ void irlan_disconnect_indication(void *instance, void *sap, LM_REASON reason, IRDA_DEBUG(2, "IrLAN, data channel disconnected by peer!\n"); - switch(reason) { + /* Save reason so we know if we should try to reconnect or not */ + self->disconnect_reason = reason; + + switch (reason) { case LM_USER_REQUEST: /* User request */ - irlan_close(self); + IRDA_DEBUG(2, __FUNCTION__ "(), User requested\n"); break; case LM_LAP_DISCONNECT: /* Unexpected IrLAP disconnect */ - irlan_start_watchdog_timer(self, IRLAN_TIMEOUT); + IRDA_DEBUG(2, __FUNCTION__ "(), Unexpected IrLAP disconnect\n"); break; case LM_CONNECT_FAILURE: /* Failed to establish IrLAP connection */ - IRDA_DEBUG(2, __FUNCTION__ "(), LM_CONNECT_FAILURE not impl\n"); + IRDA_DEBUG(2, __FUNCTION__ "(), IrLAP connect failed\n"); break; case LM_LAP_RESET: /* IrLAP reset */ - IRDA_DEBUG(2, __FUNCTION__ "(), LM_CONNECT_FAILURE not impl\n"); + IRDA_DEBUG(2, __FUNCTION__ "(), IrLAP reset\n"); break; case LM_INIT_DISCONNECT: - IRDA_DEBUG(2, __FUNCTION__ "(), LM_CONNECT_FAILURE not impl\n"); + IRDA_DEBUG(2, __FUNCTION__ "(), IrLMP connect failed\n"); break; default: + ERROR(__FUNCTION__ "(), Unknown disconnect reason\n"); break; } irlan_do_client_event(self, IRLAN_LMP_DISCONNECT, NULL); irlan_do_provider_event(self, IRLAN_LMP_DISCONNECT, NULL); + + wake_up_interruptible(&self->open_wait); } void irlan_open_data_tsap(struct irlan_cb *self) @@ -553,9 +474,7 @@ void irlan_close_tsaps(struct irlan_cb *self) ASSERT(self != NULL, return;); ASSERT(self->magic == IRLAN_MAGIC, return;); - /* - * Disconnect and close all open TSAP connections - */ + /* Disconnect and close all open TSAP connections */ if (self->tsap_data) { irttp_disconnect_request(self->tsap_data, NULL, P_NORMAL); irttp_close_tsap(self->tsap_data); @@ -573,6 +492,7 @@ void irlan_close_tsaps(struct irlan_cb *self) irttp_close_tsap(self->provider.tsap_ctrl); self->provider.tsap_ctrl = NULL; } + self->disconnect_reason = LM_USER_REQUEST; } /* @@ -595,7 +515,8 @@ void irlan_ias_register(struct irlan_cb *self, __u8 tsap_sel) */ if (!irias_find_object("IrLAN")) { obj = irias_new_object("IrLAN", IAS_IRLAN_ID); - irias_add_integer_attrib(obj, "IrDA:TinyTP:LsapSel", tsap_sel); + irias_add_integer_attrib(obj, "IrDA:TinyTP:LsapSel", tsap_sel, + IAS_KERNEL_ATTR); irias_insert_object(obj); } else { new_value = irias_new_integer_value(tsap_sel); @@ -607,18 +528,23 @@ void irlan_ias_register(struct irlan_cb *self, __u8 tsap_sel) if (!irias_find_object("PnP")) { obj = irias_new_object("PnP", IAS_PNP_ID); #if 0 - irias_add_string_attrib(obj, "Name", sysctl_devname); + irias_add_string_attrib(obj, "Name", sysctl_devname, + IAS_KERNEL_ATTR); #else - irias_add_string_attrib(obj, "Name", "Linux"); + irias_add_string_attrib(obj, "Name", "Linux", IAS_KERNEL_ATTR); #endif - irias_add_string_attrib(obj, "DeviceID", "HWP19F0"); - irias_add_integer_attrib(obj, "CompCnt", 1); + irias_add_string_attrib(obj, "DeviceID", "HWP19F0", + IAS_KERNEL_ATTR); + irias_add_integer_attrib(obj, "CompCnt", 1, IAS_KERNEL_ATTR); if (self->provider.access_type == ACCESS_PEER) - irias_add_string_attrib(obj, "Comp#01", "PNP8389"); + irias_add_string_attrib(obj, "Comp#01", "PNP8389", + IAS_KERNEL_ATTR); else - irias_add_string_attrib(obj, "Comp#01", "PNP8294"); + irias_add_string_attrib(obj, "Comp#01", "PNP8294", + IAS_KERNEL_ATTR); - irias_add_string_attrib(obj, "Manufacturer", "Linux-IrDA Project"); + irias_add_string_attrib(obj, "Manufacturer", + "Linux-IrDA Project", IAS_KERNEL_ATTR); irias_insert_object(obj); } } @@ -633,7 +559,7 @@ int irlan_run_ctrl_tx_queue(struct irlan_cb *self) { struct sk_buff *skb; - IRDA_DEBUG(3, __FUNCTION__ "()\n"); + IRDA_DEBUG(2, __FUNCTION__ "()\n"); if (irda_lock(&self->client.tx_busy) == FALSE) return -EBUSY; @@ -652,7 +578,7 @@ int irlan_run_ctrl_tx_queue(struct irlan_cb *self) dev_kfree_skb(skb); return -1; } - IRDA_DEBUG(3, __FUNCTION__ "(), sending ...\n"); + IRDA_DEBUG(2, __FUNCTION__ "(), sending ...\n"); return irttp_data_request(self->client.tsap_ctrl, skb); } @@ -741,7 +667,6 @@ void irlan_open_data_channel(struct irlan_cb *self) /* self->use_udata = TRUE; */ - /* irttp_data_request(self->client.tsap_ctrl, skb); */ irlan_ctrl_data_request(self, skb); } @@ -810,7 +735,6 @@ void irlan_open_unicast_addr(struct irlan_cb *self) irlan_insert_string_param(skb, "FILTER_TYPE", "DIRECTED"); irlan_insert_string_param(skb, "FILTER_MODE", "FILTER"); - /* irttp_data_request(self->client.tsap_ctrl, skb); */ irlan_ctrl_data_request(self, skb); } @@ -852,7 +776,6 @@ void irlan_set_broadcast_filter(struct irlan_cb *self, int status) else irlan_insert_string_param(skb, "FILTER_MODE", "NONE"); - /* irttp_data_request(self->client.tsap_ctrl, skb); */ irlan_ctrl_data_request(self, skb); } @@ -892,7 +815,6 @@ void irlan_set_multicast_filter(struct irlan_cb *self, int status) else irlan_insert_string_param(skb, "FILTER_MODE", "NONE"); - /* irttp_data_request(self->client.tsap_ctrl, skb); */ irlan_ctrl_data_request(self, skb); } @@ -930,7 +852,6 @@ void irlan_get_unicast_addr(struct irlan_cb *self) irlan_insert_string_param(skb, "FILTER_TYPE", "DIRECTED"); irlan_insert_string_param(skb, "FILTER_OPERATION", "DYNAMIC"); - /* irttp_data_request(self->client.tsap_ctrl, skb); */ irlan_ctrl_data_request(self, skb); } @@ -965,8 +886,6 @@ void irlan_get_media_char(struct irlan_cb *self) frame[1] = 0x01; /* One parameter */ irlan_insert_string_param(skb, "MEDIA", "802.3"); - - /* irttp_data_request(self->client.tsap_ctrl, skb); */ irlan_ctrl_data_request(self, skb); } @@ -1169,35 +1088,32 @@ static int irlan_proc_read(char *buf, char **start, off_t offset, int len) while (self != NULL) { ASSERT(self->magic == IRLAN_MAGIC, return len;); - /* Don't display the master server */ - if (self->master == 0) { - len += sprintf(buf+len, "ifname: %s,\n", - self->dev.name); - len += sprintf(buf+len, "client state: %s, ", - irlan_state[ self->client.state]); - len += sprintf(buf+len, "provider state: %s,\n", - irlan_state[ self->provider.state]); - len += sprintf(buf+len, "saddr: %#08x, ", - self->saddr); - len += sprintf(buf+len, "daddr: %#08x\n", - self->daddr); - len += sprintf(buf+len, "version: %d.%d,\n", - self->version[1], self->version[0]); - len += sprintf(buf+len, "access type: %s\n", - irlan_access[self->client.access_type]); - len += sprintf(buf+len, "media: %s\n", - irlan_media[self->media]); - - len += sprintf(buf+len, "local filter:\n"); - len += sprintf(buf+len, "remote filter: "); - len += irlan_print_filter(self->client.filter_type, - buf+len); + len += sprintf(buf+len, "ifname: %s,\n", + self->dev.name); + len += sprintf(buf+len, "client state: %s, ", + irlan_state[ self->client.state]); + len += sprintf(buf+len, "provider state: %s,\n", + irlan_state[ self->provider.state]); + len += sprintf(buf+len, "saddr: %#08x, ", + self->saddr); + len += sprintf(buf+len, "daddr: %#08x\n", + self->daddr); + len += sprintf(buf+len, "version: %d.%d,\n", + self->version[1], self->version[0]); + len += sprintf(buf+len, "access type: %s\n", + irlan_access[self->client.access_type]); + len += sprintf(buf+len, "media: %s\n", + irlan_media[self->media]); + + len += sprintf(buf+len, "local filter:\n"); + len += sprintf(buf+len, "remote filter: "); + len += irlan_print_filter(self->client.filter_type, + buf+len); - len += sprintf(buf+len, "tx busy: %s\n", - netif_queue_stopped(&self->dev) ? "TRUE" : "FALSE"); + len += sprintf(buf+len, "tx busy: %s\n", + netif_queue_stopped(&self->dev) ? "TRUE" : "FALSE"); - len += sprintf(buf+len, "\n"); - } + len += sprintf(buf+len, "\n"); self = (struct irlan_cb *) hashbin_get_next(irlan); } @@ -1272,8 +1188,9 @@ MODULE_AUTHOR("Dag Brattli <dagb@cs.uit.no>"); MODULE_DESCRIPTION("The Linux IrDA LAN protocol"); MODULE_PARM(eth, "i"); +MODULE_PARM_DESC(eth, "Name devices ethX (0) or irlanX (1)"); MODULE_PARM(access, "i"); -MODULE_PARM(timeout, "i"); +MODULE_PARM_DESC(access, "Access type DIRECT=1, PEER=2, HOSTED=3"); /* * Function init_module (void) diff --git a/net/irda/irlan/irlan_eth.c b/net/irda/irlan/irlan_eth.c index 1b46ab5b8..007c4acfd 100644 --- a/net/irda/irlan/irlan_eth.c +++ b/net/irda/irlan/irlan_eth.c @@ -48,7 +48,6 @@ */ int irlan_eth_init(struct net_device *dev) { - struct irmanager_event mgr_event; struct irlan_cb *self; IRDA_DEBUG(2, __FUNCTION__"()\n"); @@ -85,22 +84,6 @@ int irlan_eth_init(struct net_device *dev) get_random_bytes(dev->dev_addr+5, 1); } - /* - * Network device has now been registered, so tell irmanager about - * it, so it can be configured with network parameters - */ - mgr_event.event = EVENT_IRLAN_START; - sprintf(mgr_event.devname, "%s", self->dev.name); - irmanager_notify(&mgr_event); - - /* - * We set this so that we only notify once, since if - * configuration of the network device fails, the user - * will have to sort it out first anyway. No need to - * try again. - */ - self->notify_irmanager = FALSE; - return 0; } @@ -123,14 +106,16 @@ int irlan_eth_open(struct net_device *dev) ASSERT(self != NULL, return -1;); /* Ready to play! */ -/* netif_start_queue(dev) */ /* Wait until data link is ready */ - - self->notify_irmanager = TRUE; + netif_stop_queue(dev); /* Wait until data link is ready */ /* We are now open, so time to do some work */ + self->disconnect_reason = 0; irlan_client_wakeup(self, self->saddr, self->daddr); irlan_mod_inc_use_count(); + + /* Make sure we have a hardware address before we return, so DHCP clients gets happy */ + interruptible_sleep_on(&self->open_wait); return 0; } @@ -146,7 +131,8 @@ int irlan_eth_open(struct net_device *dev) int irlan_eth_close(struct net_device *dev) { struct irlan_cb *self = (struct irlan_cb *) dev->priv; - + struct sk_buff *skb; + IRDA_DEBUG(2, __FUNCTION__ "()\n"); /* Stop device */ @@ -155,20 +141,17 @@ int irlan_eth_close(struct net_device *dev) irlan_mod_dec_use_count(); irlan_close_data_channel(self); - irlan_close_tsaps(self); irlan_do_client_event(self, IRLAN_LMP_DISCONNECT, NULL); irlan_do_provider_event(self, IRLAN_LMP_DISCONNECT, NULL); - irlan_start_watchdog_timer(self, IRLAN_TIMEOUT); - - /* Device closed by user! */ - if (self->notify_irmanager) - self->notify_irmanager = FALSE; - else - self->notify_irmanager = TRUE; + /* Remove frames queued on the control channel */ + while ((skb = skb_dequeue(&self->client.txq))) + dev_kfree_skb(skb); + self->client.tx_busy = 0; + return 0; } diff --git a/net/irda/irlan/irlan_provider.c b/net/irda/irlan/irlan_provider.c index aeac03f7b..e06807bd9 100644 --- a/net/irda/irlan/irlan_provider.c +++ b/net/irda/irlan/irlan_provider.c @@ -116,16 +116,16 @@ static int irlan_provider_data_indication(void *instance, void *sap, /* * Function irlan_provider_connect_indication (handle, skb, priv) * - * Got connection from peer IrLAN layer + * Got connection from peer IrLAN client * */ static void irlan_provider_connect_indication(void *instance, void *sap, struct qos_info *qos, __u32 max_sdu_size, __u8 max_header_size, - struct sk_buff *skb) + struct sk_buff *skb) { - struct irlan_cb *self, *new; + struct irlan_cb *self; struct tsap_cb *tsap; __u32 saddr, daddr; @@ -137,82 +137,24 @@ static void irlan_provider_connect_indication(void *instance, void *sap, ASSERT(self != NULL, return;); ASSERT(self->magic == IRLAN_MAGIC, return;); - self->provider.max_sdu_size = max_sdu_size; - self->provider.max_header_size = max_header_size; - ASSERT(tsap == self->provider.tsap_ctrl,return;); ASSERT(self->provider.state == IRLAN_IDLE, return;); daddr = irttp_get_daddr(tsap); saddr = irttp_get_saddr(tsap); + self->provider.max_sdu_size = max_sdu_size; + self->provider.max_header_size = max_header_size; - /* Check if we already dealing with this client or peer */ - new = (struct irlan_cb *) hashbin_find(irlan, daddr, NULL); - if (new) { - ASSERT(new->magic == IRLAN_MAGIC, return;); - IRDA_DEBUG(0, __FUNCTION__ "(), found instance!\n"); - - /* Update saddr, since client may have moved to a new link */ - new->saddr = saddr; - IRDA_DEBUG(2, __FUNCTION__ "(), saddr=%08x\n", new->saddr); - - /* Make sure that any old provider control TSAP is removed */ - if ((new != self) && new->provider.tsap_ctrl) { - irttp_disconnect_request(new->provider.tsap_ctrl, - NULL, P_NORMAL); - irttp_close_tsap(new->provider.tsap_ctrl); - new->provider.tsap_ctrl = NULL; - } - } else { - /* This must be the master instance, so start a new instance */ - IRDA_DEBUG(0, __FUNCTION__ "(), starting new provider!\n"); - - new = irlan_open(saddr, daddr, TRUE); - } - - /* - * Check if the connection came in on the master server, or the - * slave server. If it came on the slave, then everything is - * really, OK (reconnect), if not we need to dup the connection and - * hand it over to the slave. - */ - if (new != self) { - - /* Now attach up the new "socket" */ - new->provider.tsap_ctrl = irttp_dup(self->provider.tsap_ctrl, - new); - if (!new->provider.tsap_ctrl) { - IRDA_DEBUG(0, __FUNCTION__ "(), dup failed!\n"); - return; - } - - /* new->stsap_sel = new->tsap->stsap_sel; */ - new->dtsap_sel_ctrl = new->provider.tsap_ctrl->dtsap_sel; - - /* Clean up the original one to keep it in listen state */ - self->provider.tsap_ctrl->dtsap_sel = LSAP_ANY; - self->provider.tsap_ctrl->lsap->dlsap_sel = LSAP_ANY; - self->provider.tsap_ctrl->lsap->lsap_state = LSAP_DISCONNECTED; - - /* - * Use the new instance from here instead of the master - * struct! - */ - self = new; - } - /* Check if network device has been registered */ - if (!self->netdev_registered) - irlan_register_netdev(self); - irlan_do_provider_event(self, IRLAN_CONNECT_INDICATION, NULL); /* * If we are in peer mode, the client may not have got the discovery * indication it needs to make progress. If the client is still in - * IDLE state, we must kick it to + * IDLE state, we must kick it. */ if ((self->provider.access_type == ACCESS_PEER) && - (self->client.state == IRLAN_IDLE)) { + (self->client.state == IRLAN_IDLE)) + { irlan_client_wakeup(self, self->saddr, self->daddr); } } @@ -231,11 +173,6 @@ void irlan_provider_connect_response(struct irlan_cb *self, /* Just accept */ irttp_connect_response(tsap, IRLAN_MTU, NULL); - - /* Check if network device has been registered */ - if (!self->netdev_registered) - irlan_register_netdev(self); - } void irlan_provider_disconnect_indication(void *instance, void *sap, diff --git a/net/irda/irlap.c b/net/irda/irlap.c index 1c2958c4b..38d66d327 100644 --- a/net/irda/irlap.c +++ b/net/irda/irlap.c @@ -154,7 +154,7 @@ struct irlap_cb *irlap_open(struct net_device *dev, struct qos_info *qos) irlap_next_state(self, LAP_NDM); - hashbin_insert(irlap, (queue_t *) self, self->saddr, NULL); + hashbin_insert(irlap, (irda_queue_t *) self, self->saddr, NULL); irlmp_register_link(self, self->saddr, &self->notify); @@ -232,7 +232,8 @@ void irlap_connect_indication(struct irlap_cb *self, struct sk_buff *skb) ASSERT(self->magic == LAP_MAGIC, return;); irlap_init_qos_capabilities(self, NULL); /* No user QoS! */ - + + skb_get(skb); /*LEVEL4*/ irlmp_link_connect_indication(self->notify.instance, self->saddr, self->daddr, &self->qos_tx, skb); } @@ -248,6 +249,7 @@ void irlap_connect_response(struct irlap_cb *self, struct sk_buff *skb) IRDA_DEBUG(4, __FUNCTION__ "()\n"); irlap_do_event(self, CONNECT_RESPONSE, skb, NULL); + kfree_skb(skb); } /* @@ -292,6 +294,7 @@ void irlap_connect_confirm(struct irlap_cb *self, struct sk_buff *skb) ASSERT(self != NULL, return;); ASSERT(self->magic == LAP_MAGIC, return;); + skb_get(skb); /*LEVEL4*/ irlmp_link_connect_confirm(self->notify.instance, &self->qos_tx, skb); } @@ -310,6 +313,7 @@ void irlap_data_indication(struct irlap_cb *self, struct sk_buff *skb, #ifdef CONFIG_IRDA_COMPRESSION if (self->qos_tx.compression.value) { + skb_get(skb); /*LEVEL4*/ skb = irlap_decompress_frame(self, skb); if (!skb) { IRDA_DEBUG(1, __FUNCTION__ "(), Decompress error!\n"); @@ -317,6 +321,7 @@ void irlap_data_indication(struct irlap_cb *self, struct sk_buff *skb, } } #endif + skb_get(skb); /*LEVEL4*/ irlmp_link_data_indication(self->notify.instance, skb, unreliable); } @@ -373,6 +378,7 @@ void irlap_data_request(struct irlap_cb *self, struct sk_buff *skb, ASSERT(skb != NULL, return;); } irlap_do_event(self, SEND_I_CMD, skb, NULL); + kfree_skb(skb); } else skb_queue_tail(&self->txq, skb); } @@ -422,6 +428,7 @@ void irlap_unitdata_indication(struct irlap_cb *self, struct sk_buff *skb) /* Hide LAP header from IrLMP layer */ skb_pull(skb, LAP_ADDR_HEADER+LAP_CTRL_HEADER); + skb_get(skb); /*LEVEL4*/ irlmp_link_unitdata_indication(self->notify.instance, skb); } #endif /* CONFIG_IRDA_ULTRA */ @@ -610,7 +617,7 @@ void irlap_discovery_indication(struct irlap_cb *self, discovery_t *discovery) * * */ -void irlap_status_indication(int quality_of_link) +void irlap_status_indication(struct irlap_cb *self, int quality_of_link) { switch (quality_of_link) { case STATUS_NO_ACTIVITY: @@ -622,7 +629,8 @@ void irlap_status_indication(int quality_of_link) default: break; } - irlmp_status_indication(quality_of_link, LOCK_NO_CHANGE); + irlmp_status_indication(self->notify.instance, + quality_of_link, LOCK_NO_CHANGE); } /* @@ -664,11 +672,16 @@ void irlap_reset_confirm(void) */ int irlap_generate_rand_time_slot(int S, int s) { + static int rand; int slot; ASSERT((S - s) > 0, return 0;); - slot = s + jiffies % (S-s); + rand += jiffies; + rand ^= (rand << 12); + rand ^= (rand >> 20); + + slot = s + rand % (S-s); ASSERT((slot >= s) || (slot < S), return 0;); @@ -863,6 +876,8 @@ void irlap_flush_all_queues(struct irlap_cb *self) */ void irlap_change_speed(struct irlap_cb *self, __u32 speed, int now) { + struct sk_buff *skb; + IRDA_DEBUG(0, __FUNCTION__ "(), setting speed to %d\n", speed); ASSERT(self != NULL, return;); @@ -871,8 +886,11 @@ void irlap_change_speed(struct irlap_cb *self, __u32 speed, int now) self->speed = speed; /* Change speed now, or just piggyback speed on frames */ - if (now) - irda_device_change_speed(self->netdev, speed); + if (now) { + /* Send down empty frame to trigger speed change */ + skb = dev_alloc_skb(0); + irlap_queue_xmit(self, skb); + } } #ifdef CONFIG_IRDA_COMPRESSION @@ -973,8 +991,8 @@ void irlap_init_qos_capabilities(struct irlap_cb *self, /* Set data size */ /*self->qos_rx.data_size.bits &= 0x03;*/ - /* Set disconnect time */ - self->qos_rx.link_disc_time.bits &= 0x07; + /* Set disconnect time -> done properly in qos.c */ + /*self->qos_rx.link_disc_time.bits &= 0x07;*/ irda_qos_bits_to_value(&self->qos_rx); } @@ -998,7 +1016,7 @@ void irlap_apply_default_connection_parameters(struct irlap_cb *self) irda_device_set_media_busy(self->netdev, TRUE); /* Default value in NDM */ - self->bofs_count = 11; + self->bofs_count = 12; /* * Generate random connection address for this session, which must @@ -1026,8 +1044,8 @@ void irlap_apply_default_connection_parameters(struct irlap_cb *self) self->qos_rx.data_size.value = 64; self->qos_tx.window_size.value = 1; self->qos_rx.window_size.value = 1; - self->qos_tx.additional_bofs.value = 11; - self->qos_rx.additional_bofs.value = 11; + self->qos_tx.additional_bofs.value = 12; + self->qos_rx.additional_bofs.value = 12; self->qos_tx.link_disc_time.value = 0; self->qos_rx.link_disc_time.value = 0; @@ -1071,7 +1089,12 @@ void irlap_apply_connection_parameters(struct irlap_cb *self) */ ASSERT(self->qos_tx.max_turn_time.value != 0, return;); if (self->qos_tx.link_disc_time.value == 3) - self->N1 = 0; + /* + * If we set N1 to 0, it will trigger immediately, which is + * not what we want. What we really want is to disable it, + * Jean II + */ + self->N1 = -1; /* Disable */ else self->N1 = 3000 / self->qos_tx.max_turn_time.value; diff --git a/net/irda/irlap_comp.c b/net/irda/irlap_comp.c index 942949ac8..e81fc7e4a 100644 --- a/net/irda/irlap_comp.c +++ b/net/irda/irlap_comp.c @@ -63,7 +63,7 @@ int irda_register_compressor( struct compressor *cp) new->cp = cp; /* Insert IrDA compressor into hashbin */ - hashbin_insert( irlap_compressors, (queue_t *) new, cp->compress_proto, + hashbin_insert( irlap_compressors, (irda_queue_t *) new, cp->compress_proto, NULL); return 0; diff --git a/net/irda/irlap_event.c b/net/irda/irlap_event.c index 08501162e..ec5f6611c 100644 --- a/net/irda/irlap_event.c +++ b/net/irda/irlap_event.c @@ -9,8 +9,8 @@ * Modified at: Sat Dec 25 21:07:57 1999 * Modified by: Dag Brattli <dagb@cs.uit.no> * - * Copyright (c) 1998-1999 Dag Brattli <dagb@cs.uit.no>, - * Thomas Davis <ratbert@radiks.net> + * Copyright (c) 1998-2000 Dag Brattli <dag@brattli.net>, + * Copyright (c) 1998 Thomas Davis <ratbert@radiks.net> * All Rights Reserved. * * This program is free software; you can redistribute it and/or @@ -230,7 +230,7 @@ void irlap_do_event(struct irlap_cb *self, IRLAP_EVENT event, if (!self || self->magic != LAP_MAGIC) return; - + IRDA_DEBUG(3, __FUNCTION__ "(), event = %s, state = %s\n", irlap_event[event], irlap_state[self->state]); @@ -252,6 +252,7 @@ void irlap_do_event(struct irlap_cb *self, IRLAP_EVENT event, while ((skb = skb_dequeue(&self->txq)) != NULL) { ret = (*state[self->state])(self, SEND_I_CMD, skb, NULL); + kfree_skb(skb); if (ret == -EPROTO) break; /* Try again later! */ } @@ -304,6 +305,17 @@ void irlap_next_state(struct irlap_cb *self, IRLAP_STATE state) if ((state != LAP_XMIT_P) && (state != LAP_XMIT_S)) self->bytes_left = self->line_capacity; #endif /* CONFIG_IRDA_DYNAMIC_WINDOW */ +#ifdef CONFIG_IRDA_ULTRA + /* Send any pending Ultra frames if any */ + /* The higher layers may have sent a few Ultra frames while we + * were doing discovery (either query or reply). Those frames + * have been queued, but were never sent. It is now time to + * send them... + * Jean II */ + if ((state == LAP_NDM) && (!skb_queue_empty(&self->txq_ultra))) + /* Force us to listen 500 ms before sending Ultra */ + irda_device_set_media_busy(self->netdev, TRUE); +#endif /* CONFIG_IRDA_ULTRA */ } /* @@ -351,12 +363,11 @@ static int irlap_state_ndm(struct irlap_cb *self, IRLAP_EVENT event, self->caddr = info->caddr; irlap_next_state(self, LAP_CONN); - + irlap_connect_indication(self, skb); } else { IRDA_DEBUG(0, __FUNCTION__ "(), SNRM frame does not " "contain an I field!\n"); - dev_kfree_skb(skb); } break; case DISCOVERY_REQUEST: @@ -375,6 +386,7 @@ static int irlap_state_ndm(struct irlap_cb *self, IRLAP_EVENT event, self->s = info->s; irlap_send_discovery_xid_frame(self, info->S, info->s, TRUE, info->discovery); + self->frame_sent = FALSE; self->s++; irlap_start_slot_timer(self, self->slot_timeout); @@ -385,12 +397,8 @@ static int irlap_state_ndm(struct irlap_cb *self, IRLAP_EVENT event, /* Assert that this is not the final slot */ if (info->s <= info->S) { - /* self->daddr = info->daddr; */ self->slot = irlap_generate_rand_time_slot(info->S, info->s); - IRDA_DEBUG(4, "XID_CMD: S=%d, s=%d, slot %d\n", info->S, - info->s, self->slot); - if (self->slot == info->s) { discovery_rsp = irlmp_get_discovery_response(); discovery_rsp->daddr = info->daddr; @@ -410,14 +418,34 @@ static int irlap_state_ndm(struct irlap_cb *self, IRLAP_EVENT event, irlap_start_query_timer(self, QUERY_TIMEOUT*info->S); irlap_next_state(self, LAP_REPLY); } - dev_kfree_skb(skb); + else { + /* This is the final slot. How is it possible ? + * This would happen is both discoveries are just slightly + * offset (if they are in sync, all packets are lost). + * Most often, all the discovery requests will be received + * in QUERY state (see my comment there), except for the + * last frame that will come here. + * The big trouble when it happen is that active discovery + * doesn't happen, because nobody answer the discoveries + * frame of the other guy, so the log shows up empty. + * What should we do ? + * Not much. It's too late to answer those discovery frames, + * so we just pass the info to IrLMP who will put it in the + * log (and post an event). + * Jean II + */ + IRDA_DEBUG(1, __FUNCTION__ "(), Receiving final discovery request, missed the discovery slots :-(\n"); + + /* Last discovery request -> in the log */ + irlap_discovery_indication(self, info->discovery); + } break; #ifdef CONFIG_IRDA_ULTRA case SEND_UI_FRAME: /* Only allowed to repeat an operation twice */ for (i=0; ((i<2) && (self->media_busy == FALSE)); i++) { skb = skb_dequeue(&self->txq_ultra); - if (skb) + if (skb) irlap_send_ui_frame(self, skb, CBROADCAST, CMD_FRAME); else @@ -433,7 +461,6 @@ static int irlap_state_ndm(struct irlap_cb *self, IRLAP_EVENT event, if (info->caddr != CBROADCAST) { IRDA_DEBUG(0, __FUNCTION__ "(), not a broadcast frame!\n"); - dev_kfree_skb(skb); } else irlap_unitdata_indication(self, skb); break; @@ -447,19 +474,14 @@ static int irlap_state_ndm(struct irlap_cb *self, IRLAP_EVENT event, * will only be used to send out the same info as the cmd */ irlap_send_test_frame(self, CBROADCAST, info->daddr, skb); - dev_kfree_skb(skb); break; case RECV_TEST_RSP: IRDA_DEBUG(0, __FUNCTION__ "() not implemented!\n"); - dev_kfree_skb(skb); break; default: IRDA_DEBUG(2, __FUNCTION__ "(), Unknown event %s\n", irlap_event[event]); - if (skb) - dev_kfree_skb(skb); - ret = -1; break; } @@ -492,19 +514,51 @@ static int irlap_state_query(struct irlap_cb *self, IRLAP_EVENT event, WARNING(__FUNCTION__ "(), discovery log is gone! " "maybe the discovery timeout has been set to " "short?\n"); - dev_kfree_skb(skb); break; } hashbin_insert(self->discovery_log, - (queue_t *) info->discovery, + (irda_queue_t *) info->discovery, info->discovery->daddr, NULL); /* Keep state */ /* irlap_next_state(self, LAP_QUERY); */ - dev_kfree_skb(skb); + break; + case RECV_DISCOVERY_XID_CMD: + /* Yes, it is possible to receive those frames in this mode. + * Note that most often the last discovery request won't + * occur here but in NDM state (see my comment there). + * What should we do ? + * Not much. We are currently performing our own discovery, + * therefore we can't answer those frames. We don't want + * to change state either. We just pass the info to + * IrLMP who will put it in the log (and post an event). + * Jean II + */ + + ASSERT(info != NULL, return -1;); + + IRDA_DEBUG(1, __FUNCTION__ "(), Receiving discovery request (s = %d) while performing discovery :-(\n", info->s); + + /* Last discovery request ? */ + if (info->s == 0xff) + irlap_discovery_indication(self, info->discovery); break; case SLOT_TIMER_EXPIRED: + /* + * Wait a little longer if we detect an incomming frame. This + * is not mentioned in the spec, but is a good thing to do, + * since we want to work even with devices that violate the + * timing requirements. + */ + if (irda_device_is_receiving(self->netdev)) { + IRDA_DEBUG(1, __FUNCTION__ + "(), device is slow to answer, " + "waiting some more!\n"); + irlap_start_slot_timer(self, MSECS_TO_JIFFIES(10)); + return ret; + } + if (self->s < self->S) { irlap_send_discovery_xid_frame(self, self->S, self->s, TRUE, @@ -537,9 +591,6 @@ static int irlap_state_query(struct irlap_cb *self, IRLAP_EVENT event, IRDA_DEBUG(2, __FUNCTION__ "(), Unknown event %s\n", irlap_event[event]); - if (skb) - dev_kfree_skb(skb); - ret = -1; break; } @@ -572,9 +623,7 @@ static int irlap_state_reply(struct irlap_cb *self, IRLAP_EVENT event, break; case RECV_DISCOVERY_XID_CMD: ASSERT(info != NULL, return -1;); - /* - * Last frame? - */ + /* Last frame? */ if (info->s == 0xff) { del_timer(&self->query_timer); @@ -595,15 +644,11 @@ static int irlap_state_reply(struct irlap_cb *self, IRLAP_EVENT event, self->frame_sent = TRUE; irlap_next_state(self, LAP_REPLY); } - dev_kfree_skb(skb); break; default: IRDA_DEBUG(1, __FUNCTION__ "(), Unknown event %d, %s\n", event, irlap_event[event]); - if (skb) - dev_kfree_skb(skb); - ret = -1; break; } @@ -665,14 +710,12 @@ static int irlap_state_conn(struct irlap_cb *self, IRLAP_EVENT event, irlap_start_wd_timer(self, self->wd_timeout); irlap_next_state(self, LAP_NRM_S); - dev_kfree_skb(skb); break; case RECV_DISCOVERY_XID_CMD: IRDA_DEBUG(3, __FUNCTION__ "(), event RECV_DISCOVER_XID_CMD!\n"); irlap_next_state(self, LAP_NDM); - dev_kfree_skb(skb); break; case DISCONNECT_REQUEST: irlap_send_dm_frame(self); @@ -682,9 +725,6 @@ static int irlap_state_conn(struct irlap_cb *self, IRLAP_EVENT event, IRDA_DEBUG(1, __FUNCTION__ "(), Unknown event %d, %s\n", event, irlap_event[event]); - if (skb) - dev_kfree_skb(skb); - ret = -1; break; } @@ -766,7 +806,6 @@ static int irlap_state_setup(struct irlap_cb *self, IRLAP_EVENT event, irlap_start_wd_timer(self, self->wd_timeout); } else { /* We just ignore the other device! */ - dev_kfree_skb(skb); irlap_next_state(self, LAP_SETUP); } break; @@ -803,14 +842,11 @@ static int irlap_state_setup(struct irlap_cb *self, IRLAP_EVENT event, irlap_next_state(self, LAP_NDM); irlap_disconnect_indication(self, LAP_DISC_INDICATION); - dev_kfree_skb(skb); break; default: IRDA_DEBUG(1, __FUNCTION__ "(), Unknown event %d, %s\n", event, irlap_event[event]); - if (skb) - dev_kfree_skb(skb); - + ret = -1; break; } @@ -860,14 +896,13 @@ static int irlap_state_xmit_p(struct irlap_cb *self, IRLAP_EVENT event, IRDA_DEBUG(4, __FUNCTION__ "(), Not allowed to transmit more " "bytes!\n"); - skb_queue_head(&self->txq, skb); - + skb_queue_head(&self->txq, skb_get(skb)); /* * We should switch state to LAP_NRM_P, but * that is not possible since we must be sure * that we poll the other side. Since we have * used up our time, the poll timer should - * trigger anyway now,so we just wait for it + * trigger anyway now, so we just wait for it * DB */ return -EPROTO; @@ -900,7 +935,7 @@ static int irlap_state_xmit_p(struct irlap_cb *self, IRLAP_EVENT event, } else { IRDA_DEBUG(4, __FUNCTION__ "(), Unable to send! remote busy?\n"); - skb_queue_head(&self->txq, skb); + skb_queue_head(&self->txq, skb_get(skb)); /* * The next ret is important, because it tells @@ -929,9 +964,6 @@ static int irlap_state_xmit_p(struct irlap_cb *self, IRLAP_EVENT event, IRDA_DEBUG(0, __FUNCTION__ "(), Unknown event %s\n", irlap_event[event]); - if (skb) - dev_kfree_skb(skb); - ret = -EINVAL; break; } @@ -964,7 +996,6 @@ static int irlap_state_pclose(struct irlap_cb *self, IRLAP_EVENT event, irlap_next_state(self, LAP_NDM); irlap_disconnect_indication(self, LAP_DISC_INDICATION); - dev_kfree_skb(skb); break; case FINAL_TIMER_EXPIRED: if (self->retry_count < self->N3) { @@ -985,9 +1016,6 @@ static int irlap_state_pclose(struct irlap_cb *self, IRLAP_EVENT event, default: IRDA_DEBUG(1, __FUNCTION__ "(), Unknown event %d\n", event); - if (skb) - dev_kfree_skb(skb); - ret = -1; break; } @@ -1041,7 +1069,7 @@ static int irlap_state_nrm_p(struct irlap_cb *self, IRLAP_EVENT event, /* Keep state, do not move this line */ irlap_next_state(self, LAP_NRM_P); - + irlap_data_indication(self, skb, FALSE); } else { del_timer(&self->final_timer); @@ -1065,7 +1093,7 @@ static int irlap_state_nrm_p(struct irlap_cb *self, IRLAP_EVENT event, * upper layers */ irlap_next_state(self, LAP_XMIT_P); - + irlap_data_indication(self, skb, FALSE); /* This is the last frame */ @@ -1102,7 +1130,6 @@ static int irlap_state_nrm_p(struct irlap_cb *self, IRLAP_EVENT event, irlap_start_final_timer(self, self->final_timeout); irlap_next_state(self, LAP_NRM_P); } - dev_kfree_skb(skb); break; } /* @@ -1124,7 +1151,7 @@ static int irlap_state_nrm_p(struct irlap_cb *self, IRLAP_EVENT event, /* Keep state, do not move this line */ irlap_next_state(self, LAP_NRM_P); - + irlap_data_indication(self, skb, FALSE); } else { /* @@ -1142,7 +1169,7 @@ static int irlap_state_nrm_p(struct irlap_cb *self, IRLAP_EVENT event, /* Keep state, do not move this line!*/ irlap_next_state(self, LAP_NRM_P); - + irlap_data_indication(self, skb, FALSE); } break; @@ -1171,7 +1198,6 @@ static int irlap_state_nrm_p(struct irlap_cb *self, IRLAP_EVENT event, self->ack_required = FALSE; } - dev_kfree_skb(skb); break; } @@ -1193,7 +1219,6 @@ static int irlap_state_nrm_p(struct irlap_cb *self, IRLAP_EVENT event, self->xmitflag = FALSE; } - dev_kfree_skb(skb); break; } IRDA_DEBUG(1, __FUNCTION__ "(), Not implemented!\n"); @@ -1209,6 +1234,7 @@ static int irlap_state_nrm_p(struct irlap_cb *self, IRLAP_EVENT event, } else { del_timer(&self->final_timer); irlap_data_indication(self, skb, TRUE); + printk(__FUNCTION__ "(): RECV_UI_FRAME: next state %s\n", irlap_state[self->state]); irlap_start_poll_timer(self, self->poll_timeout); } break; @@ -1270,7 +1296,6 @@ static int irlap_state_nrm_p(struct irlap_cb *self, IRLAP_EVENT event, irlap_disconnect_indication(self, LAP_RESET_INDICATION); self->xmitflag = TRUE; } - dev_kfree_skb(skb); break; case RECV_RNR_RSP: ASSERT(info != NULL, return -1;); @@ -1285,14 +1310,12 @@ static int irlap_state_nrm_p(struct irlap_cb *self, IRLAP_EVENT event, /* Start poll timer */ irlap_start_poll_timer(self, self->poll_timeout); - dev_kfree_skb(skb); break; case RECV_FRMR_RSP: del_timer(&self->final_timer); self->xmitflag = TRUE; irlap_next_state(self, LAP_RESET_WAIT); irlap_reset_indication(self); - dev_kfree_skb(skb); break; case FINAL_TIMER_EXPIRED: /* @@ -1331,7 +1354,7 @@ static int irlap_state_nrm_p(struct irlap_cb *self, IRLAP_EVENT event, " retry_count=%d\n", self->retry_count); /* Keep state */ } else if (self->retry_count == self->N1) { - irlap_status_indication(STATUS_NO_ACTIVITY); + irlap_status_indication(self, STATUS_NO_ACTIVITY); irlap_wait_min_turn_around(self, &self->qos_tx); irlap_send_rr_frame(self, CMD_FRAME); @@ -1357,7 +1380,6 @@ static int irlap_state_nrm_p(struct irlap_cb *self, IRLAP_EVENT event, } else irlap_resend_rejected_frames(self, CMD_FRAME); irlap_start_final_timer(self, self->final_timeout); - dev_kfree_skb(skb); break; case RECV_SREJ_RSP: irlap_update_nr_received(self, info->nr); @@ -1367,7 +1389,6 @@ static int irlap_state_nrm_p(struct irlap_cb *self, IRLAP_EVENT event, } else irlap_resend_rejected_frame(self, CMD_FRAME); irlap_start_final_timer(self, self->final_timeout); - dev_kfree_skb(skb); break; case RECV_RD_RSP: IRDA_DEBUG(0, __FUNCTION__ "(), RECV_RD_RSP\n"); @@ -1381,8 +1402,6 @@ static int irlap_state_nrm_p(struct irlap_cb *self, IRLAP_EVENT event, default: IRDA_DEBUG(1, __FUNCTION__ "(), Unknown event %s\n", irlap_event[event]); - if (skb) - dev_kfree_skb(skb); ret = -1; break; @@ -1428,10 +1447,8 @@ static int irlap_state_reset_wait(struct irlap_cb *self, IRLAP_EVENT event, irlap_next_state( self, LAP_PCLOSE); break; default: - IRDA_DEBUG(1, __FUNCTION__ "(), Unknown event %s\n", + IRDA_DEBUG(2, __FUNCTION__ "(), Unknown event %s\n", irlap_event[event]); - if (skb) - dev_kfree_skb(skb); ret = -1; break; @@ -1467,7 +1484,6 @@ static int irlap_state_reset(struct irlap_cb *self, IRLAP_EVENT event, irlap_disconnect_indication(self, LAP_NO_RESPONSE); - dev_kfree_skb(skb); break; case RECV_UA_RSP: del_timer(&self->final_timer); @@ -1483,7 +1499,6 @@ static int irlap_state_reset(struct irlap_cb *self, IRLAP_EVENT event, irlap_start_poll_timer(self, self->poll_timeout); - dev_kfree_skb(skb); break; case FINAL_TIMER_EXPIRED: if (self->retry_count < 3) { @@ -1522,13 +1537,10 @@ static int irlap_state_reset(struct irlap_cb *self, IRLAP_EVENT event, IRDA_DEBUG(0, __FUNCTION__ "(), SNRM frame contained an I field!\n"); } - dev_kfree_skb(skb); break; default: IRDA_DEBUG(1, __FUNCTION__ "(), Unknown event %s\n", irlap_event[event]); - if (skb) - dev_kfree_skb(skb); ret = -1; break; @@ -1566,7 +1578,7 @@ static int irlap_state_xmit_s(struct irlap_cb *self, IRLAP_EVENT event, * speed and turn-around-time. */ if (skb->len > self->bytes_left) { - skb_queue_head(&self->txq, skb); + skb_queue_head(&self->txq, skb_get(skb)); /* * Switch to NRM_S, this is only possible * when we are in secondary mode, since we @@ -1600,7 +1612,7 @@ static int irlap_state_xmit_s(struct irlap_cb *self, IRLAP_EVENT event, } } else { IRDA_DEBUG(2, __FUNCTION__ "(), Unable to send!\n"); - skb_queue_head(&self->txq, skb); + skb_queue_head(&self->txq, skb_get(skb)); ret = -EPROTO; } break; @@ -1613,8 +1625,6 @@ static int irlap_state_xmit_s(struct irlap_cb *self, IRLAP_EVENT event, default: IRDA_DEBUG(2, __FUNCTION__ "(), Unknown event %s\n", irlap_event[event]); - if (skb) - dev_kfree_skb(skb); ret = -EINVAL; break; @@ -1676,7 +1686,7 @@ static int irlap_state_nrm_s(struct irlap_cb *self, IRLAP_EVENT event, #endif /* Keep state, do not move this line */ irlap_next_state(self, LAP_NRM_S); - + irlap_data_indication(self, skb, FALSE); break; } else { @@ -1739,7 +1749,6 @@ static int irlap_state_nrm_s(struct irlap_cb *self, IRLAP_EVENT event, irlap_start_wd_timer(self, self->wd_timeout); } - dev_kfree_skb(skb); break; } @@ -1778,7 +1787,7 @@ static int irlap_state_nrm_s(struct irlap_cb *self, IRLAP_EVENT event, /* Keep state, do not move this line */ irlap_next_state(self, LAP_NRM_S); - + irlap_data_indication(self, skb, FALSE); irlap_start_wd_timer(self, self->wd_timeout); } @@ -1787,11 +1796,9 @@ static int irlap_state_nrm_s(struct irlap_cb *self, IRLAP_EVENT event, if (ret == NR_INVALID) { IRDA_DEBUG(0, "NRM_S, NR_INVALID not implemented!\n"); - dev_kfree_skb(skb); } if (ret == NS_INVALID) { IRDA_DEBUG(0, "NRM_S, NS_INVALID not implemented!\n"); - dev_kfree_skb(skb); } break; case RECV_UI_FRAME: @@ -1870,7 +1877,6 @@ static int irlap_state_nrm_s(struct irlap_cb *self, IRLAP_EVENT event, IRDA_DEBUG(1, __FUNCTION__ "(), invalid nr not implemented!\n"); } - dev_kfree_skb(skb); break; case RECV_SNRM_CMD: /* SNRM frame is not allowed to contain an I-field */ @@ -1885,7 +1891,6 @@ static int irlap_state_nrm_s(struct irlap_cb *self, IRLAP_EVENT event, "(), SNRM frame contained an I-field!\n"); } - dev_kfree_skb(skb); break; case RECV_REJ_CMD: irlap_update_nr_received(self, info->nr); @@ -1895,7 +1900,6 @@ static int irlap_state_nrm_s(struct irlap_cb *self, IRLAP_EVENT event, } else irlap_resend_rejected_frames(self, CMD_FRAME); irlap_start_wd_timer(self, self->wd_timeout); - dev_kfree_skb(skb); break; case RECV_SREJ_CMD: irlap_update_nr_received(self, info->nr); @@ -1905,31 +1909,34 @@ static int irlap_state_nrm_s(struct irlap_cb *self, IRLAP_EVENT event, } else irlap_resend_rejected_frame(self, CMD_FRAME); irlap_start_wd_timer(self, self->wd_timeout); - dev_kfree_skb(skb); break; case WD_TIMER_EXPIRED: /* * Wait until retry_count * n matches negotiated threshold/ * disconnect time (note 2 in IrLAP p. 82) + * + * Note : self->wd_timeout = (self->poll_timeout * 2), + * and self->final_timeout == self->poll_timeout, + * which explain why we use (self->retry_count * 2) here !!! + * Jean II */ IRDA_DEBUG(1, __FUNCTION__ "(), retry_count = %d\n", self->retry_count); - if ((self->retry_count < (self->N2/2)) && - (self->retry_count != self->N1/2)) { + if (((self->retry_count * 2) < self->N2) && + ((self->retry_count * 2) != self->N1)) { irlap_start_wd_timer(self, self->wd_timeout); - self->retry_count++; - } else if (self->retry_count == (self->N1/2)) { - irlap_status_indication(STATUS_NO_ACTIVITY); + self->retry_count++; + } else if ((self->retry_count * 2) == self->N1) { + irlap_status_indication(self, STATUS_NO_ACTIVITY); irlap_start_wd_timer(self, self->wd_timeout); self->retry_count++; - } else if (self->retry_count >= self->N2/2) { + } else if ((self->retry_count * 2) >= self->N2) { irlap_apply_default_connection_parameters(self); /* Always switch state before calling upper layers */ irlap_next_state(self, LAP_NDM); - irlap_disconnect_indication(self, LAP_NO_RESPONSE); } break; @@ -1944,7 +1951,6 @@ static int irlap_state_nrm_s(struct irlap_cb *self, IRLAP_EVENT event, irlap_apply_default_connection_parameters(self); irlap_disconnect_indication(self, LAP_DISC_INDICATION); - dev_kfree_skb(skb); break; case RECV_DISCOVERY_XID_CMD: irlap_wait_min_turn_around(self, &self->qos_tx); @@ -1953,24 +1959,20 @@ static int irlap_state_nrm_s(struct irlap_cb *self, IRLAP_EVENT event, irlap_start_wd_timer(self, self->wd_timeout); irlap_next_state(self, LAP_NRM_S); - dev_kfree_skb(skb); break; case RECV_TEST_CMD: - /* Remove test frame header */ - skb_pull(skb, sizeof(struct test_frame)); + /* Remove test frame header (only LAP header in NRM) */ + skb_pull(skb, LAP_ADDR_HEADER + LAP_CTRL_HEADER); irlap_wait_min_turn_around(self, &self->qos_tx); irlap_start_wd_timer(self, self->wd_timeout); /* Send response (info will be copied) */ irlap_send_test_frame(self, self->caddr, info->daddr, skb); - dev_kfree_skb(skb); break; default: IRDA_DEBUG(1, __FUNCTION__ "(), Unknown event %d, (%s)\n", event, irlap_event[event]); - if (skb) - dev_kfree_skb(skb); ret = -EINVAL; break; @@ -2005,7 +2007,6 @@ static int irlap_state_sclose(struct irlap_cb *self, IRLAP_EVENT event, irlap_apply_default_connection_parameters(self); irlap_disconnect_indication(self, LAP_DISC_INDICATION); - dev_kfree_skb(skb); break; case RECV_DM_RSP: /* Always switch state before calling upper layers */ @@ -2015,7 +2016,6 @@ static int irlap_state_sclose(struct irlap_cb *self, IRLAP_EVENT event, irlap_apply_default_connection_parameters(self); irlap_disconnect_indication(self, LAP_DISC_INDICATION); - dev_kfree_skb(skb); break; case WD_TIMER_EXPIRED: irlap_apply_default_connection_parameters(self); @@ -2025,9 +2025,7 @@ static int irlap_state_sclose(struct irlap_cb *self, IRLAP_EVENT event, default: IRDA_DEBUG(1, __FUNCTION__ "(), Unknown event %d, (%s)\n", event, irlap_event[event]); - if (skb) - dev_kfree_skb(skb); - + ret = -EINVAL; break; } @@ -2064,8 +2062,6 @@ static int irlap_state_reset_check( struct irlap_cb *self, IRLAP_EVENT event, default: IRDA_DEBUG(1, __FUNCTION__ "(), Unknown event %d, (%s)\n", event, irlap_event[event]); - if (skb) - dev_kfree_skb(skb); ret = -EINVAL; break; diff --git a/net/irda/irlap_frame.c b/net/irda/irlap_frame.c index 830fe6ef7..fff5bef93 100644 --- a/net/irda/irlap_frame.c +++ b/net/irda/irlap_frame.c @@ -69,7 +69,8 @@ static inline void irlap_insert_info(struct irlap_cb *self, * Delay equals negotiated BOFs count, plus the number of BOFs to * force the negotiated minimum turnaround time */ - cb->xbofs = self->bofs_count+self->xbofs_delay; + cb->xbofs = self->bofs_count; + cb->xbofs_delay = self->xbofs_delay; /* Reset XBOF's delay (used only for getting min turn time) */ self->xbofs_delay = 0; @@ -164,7 +165,6 @@ static void irlap_recv_snrm_cmd(struct irlap_cb *self, struct sk_buff *skb, if ((info->caddr == 0x00) || (info->caddr == 0xfe)) { IRDA_DEBUG(3, __FUNCTION__ "(), invalid connection address!\n"); - dev_kfree_skb(skb); return; } @@ -175,13 +175,13 @@ static void irlap_recv_snrm_cmd(struct irlap_cb *self, struct sk_buff *skb, /* Only accept if addressed directly to us */ if (info->saddr != self->saddr) { IRDA_DEBUG(2, __FUNCTION__ "(), not addressed to us!\n"); - dev_kfree_skb(skb); return; } irlap_do_event(self, RECV_SNRM_CMD, skb, info); - } else + } else { /* Signal that this SNRM frame does not contain and I-field */ irlap_do_event(self, RECV_SNRM_CMD, skb, NULL); + } } /* @@ -408,13 +408,11 @@ static void irlap_recv_discovery_xid_rsp(struct irlap_cb *self, if ((info->saddr != self->saddr) && (info->saddr != BROADCAST)) { IRDA_DEBUG(0, __FUNCTION__ "(), frame is not addressed to us!\n"); - dev_kfree_skb(skb); return; } if ((discovery = kmalloc(sizeof(discovery_t), GFP_ATOMIC)) == NULL) { WARNING(__FUNCTION__ "(), kmalloc failed!\n"); - dev_kfree_skb(skb); return; } memset(discovery, 0, sizeof(discovery_t)); @@ -476,7 +474,6 @@ static void irlap_recv_discovery_xid_cmd(struct irlap_cb *self, if ((info->saddr != self->saddr) && (info->saddr != BROADCAST)) { IRDA_DEBUG(0, __FUNCTION__ "(), frame is not addressed to us!\n"); - dev_kfree_skb(skb); return; } @@ -506,13 +503,18 @@ static void irlap_recv_discovery_xid_cmd(struct irlap_cb *self, * Check if last frame */ if (info->s == 0xff) { + /* Check if things are sane at this point... */ + if((discovery_info == NULL) || (skb->len < 3)) { + ERROR(__FUNCTION__ "(), discovery frame to short!\n"); + return; + } + /* * We now have some discovery info to deliver! */ discovery = kmalloc(sizeof(discovery_t), GFP_ATOMIC); if (!discovery) { WARNING(__FUNCTION__ "(), unable to malloc!\n"); - dev_kfree_skb(skb); return; } @@ -541,7 +543,7 @@ static void irlap_recv_discovery_xid_cmd(struct irlap_cb *self, info->discovery = discovery; } else info->discovery = NULL; - + irlap_do_event(self, RECV_DISCOVERY_XID_CMD, skb, info); } @@ -734,7 +736,6 @@ void irlap_send_data_primary(struct irlap_cb *self, struct sk_buff *skb) /* Copy buffer */ tx_skb = skb_clone(skb, GFP_ATOMIC); if (tx_skb == NULL) { - dev_kfree_skb(skb); return; } @@ -747,7 +748,7 @@ void irlap_send_data_primary(struct irlap_cb *self, struct sk_buff *skb) /* * Insert frame in store, in case of retransmissions */ - skb_queue_tail(&self->wx_list, skb); + skb_queue_tail(&self->wx_list, skb_get(skb)); self->vs = (self->vs + 1) % 8; self->ack_required = FALSE; @@ -756,7 +757,7 @@ void irlap_send_data_primary(struct irlap_cb *self, struct sk_buff *skb) irlap_send_i_frame( self, tx_skb, CMD_FRAME); } else { IRDA_DEBUG(4, __FUNCTION__ "(), sending unreliable frame\n"); - irlap_send_ui_frame(self, skb, self->caddr, CMD_FRAME); + irlap_send_ui_frame(self, skb_get(skb), self->caddr, CMD_FRAME); self->window -= 1; } } @@ -781,7 +782,6 @@ void irlap_send_data_primary_poll(struct irlap_cb *self, struct sk_buff *skb) /* Copy buffer */ tx_skb = skb_clone(skb, GFP_ATOMIC); if (tx_skb == NULL) { - dev_kfree_skb(skb); return; } @@ -794,7 +794,7 @@ void irlap_send_data_primary_poll(struct irlap_cb *self, struct sk_buff *skb) /* * Insert frame in store, in case of retransmissions */ - skb_queue_tail(&self->wx_list, skb); + skb_queue_tail(&self->wx_list, skb_get(skb)); /* * Set poll bit if necessary. We do this to the copied @@ -819,12 +819,12 @@ void irlap_send_data_primary_poll(struct irlap_cb *self, struct sk_buff *skb) del_timer(&self->poll_timer); if (self->ack_required) { - irlap_send_ui_frame(self, skb, self->caddr, CMD_FRAME); + irlap_send_ui_frame(self, skb_get(skb), self->caddr, CMD_FRAME); irlap_send_rr_frame(self, CMD_FRAME); self->ack_required = FALSE; } else { skb->data[1] |= PF_BIT; - irlap_send_ui_frame(self, skb, self->caddr, CMD_FRAME); + irlap_send_ui_frame(self, skb_get(skb), self->caddr, CMD_FRAME); } self->window = self->window_size; irlap_start_final_timer(self, self->final_timeout); @@ -857,7 +857,6 @@ void irlap_send_data_secondary_final(struct irlap_cb *self, tx_skb = skb_clone(skb, GFP_ATOMIC); if (tx_skb == NULL) { - dev_kfree_skb(skb); return; } @@ -865,7 +864,7 @@ void irlap_send_data_secondary_final(struct irlap_cb *self, skb_set_owner_w(tx_skb, skb->sk); /* Insert frame in store */ - skb_queue_tail(&self->wx_list, skb); + skb_queue_tail(&self->wx_list, skb_get(skb)); tx_skb->data[1] |= PF_BIT; @@ -878,12 +877,12 @@ void irlap_send_data_secondary_final(struct irlap_cb *self, irlap_send_i_frame(self, tx_skb, RSP_FRAME); } else { if (self->ack_required) { - irlap_send_ui_frame(self, skb, self->caddr, RSP_FRAME); + irlap_send_ui_frame(self, skb_get(skb), self->caddr, RSP_FRAME); irlap_send_rr_frame(self, RSP_FRAME); self->ack_required = FALSE; } else { skb->data[1] |= PF_BIT; - irlap_send_ui_frame(self, skb, self->caddr, RSP_FRAME); + irlap_send_ui_frame(self, skb_get(skb), self->caddr, RSP_FRAME); } self->window = self->window_size; @@ -912,7 +911,6 @@ void irlap_send_data_secondary(struct irlap_cb *self, struct sk_buff *skb) tx_skb = skb_clone(skb, GFP_ATOMIC); if (tx_skb == NULL) { - dev_kfree_skb(skb); return; } @@ -920,7 +918,7 @@ void irlap_send_data_secondary(struct irlap_cb *self, struct sk_buff *skb) skb_set_owner_w(tx_skb, skb->sk); /* Insert frame in store */ - skb_queue_tail(&self->wx_list, skb); + skb_queue_tail(&self->wx_list, skb_get(skb)); self->vs = (self->vs + 1) % 8; self->ack_required = FALSE; @@ -928,7 +926,7 @@ void irlap_send_data_secondary(struct irlap_cb *self, struct sk_buff *skb) irlap_send_i_frame(self, tx_skb, RSP_FRAME); } else { - irlap_send_ui_frame(self, skb, self->caddr, RSP_FRAME); + irlap_send_ui_frame(self, skb_get(skb), self->caddr, RSP_FRAME); self->window -= 1; } } @@ -1023,6 +1021,7 @@ void irlap_resend_rejected_frames(struct irlap_cb *self, int command) } else { irlap_send_data_primary_poll(self, skb); } + kfree_skb(skb); } } #endif @@ -1212,7 +1211,7 @@ void irlap_send_test_frame(struct irlap_cb *self, __u8 caddr, __u32 daddr, struct test_frame *frame; __u8 *info; - skb = dev_alloc_skb(32); + skb = dev_alloc_skb(cmd->len+sizeof(struct test_frame)); if (!skb) return; @@ -1225,10 +1224,10 @@ void irlap_send_test_frame(struct irlap_cb *self, __u8 caddr, __u32 daddr, frame->saddr = cpu_to_le32(self->saddr); frame->daddr = cpu_to_le32(daddr); } else - frame = (struct test_frame *) skb_put(skb, LAP_MAX_HEADER); + frame = (struct test_frame *) skb_put(skb, LAP_ADDR_HEADER + LAP_CTRL_HEADER); frame->caddr = caddr; - frame->control = TEST_RSP; + frame->control = TEST_RSP | PF_BIT; /* Copy info */ info = skb_put(skb, cmd->len); @@ -1259,7 +1258,6 @@ static void irlap_recv_test_frame(struct irlap_cb *self, struct sk_buff *skb, if (skb->len < sizeof(struct test_frame)) { IRDA_DEBUG(0, __FUNCTION__ "() test frame to short!\n"); - dev_kfree_skb(skb); return; } @@ -1270,7 +1268,6 @@ static void irlap_recv_test_frame(struct irlap_cb *self, struct sk_buff *skb, /* Make sure frame is addressed to us */ if ((info->saddr != self->saddr) && (info->saddr != BROADCAST)) { - dev_kfree_skb(skb); return; } } @@ -1323,8 +1320,7 @@ int irlap_driver_rcv(struct sk_buff *skb, struct net_device *dev, /* First we check if this frame has a valid connection address */ if ((info.caddr != self->caddr) && (info.caddr != CBROADCAST)) { IRDA_DEBUG(0, __FUNCTION__ "(), wrong connection address!\n"); - dev_kfree_skb(skb); - return 0; + goto out; } /* * Optimize for the common case and check if the frame is an @@ -1332,7 +1328,7 @@ int irlap_driver_rcv(struct sk_buff *skb, struct net_device *dev, */ if (~control & 0x01) { irlap_recv_i_frame(self, skb, &info, command); - return 0; + goto out; } /* * We now check is the frame is an S(upervisory) frame. Only @@ -1360,10 +1356,9 @@ int irlap_driver_rcv(struct sk_buff *skb, struct net_device *dev, WARNING(__FUNCTION__ "() Unknown S-frame %02x received!\n", info.control); - dev_kfree_skb(skb); break; } - return 0; + goto out; } /* * This must be a C(ontrol) frame @@ -1399,8 +1394,9 @@ int irlap_driver_rcv(struct sk_buff *skb, struct net_device *dev, default: WARNING(__FUNCTION__ "(), Unknown frame %02x received!\n", info.control); - dev_kfree_skb(skb); break; } +out: + dev_kfree_skb(skb); return 0; } diff --git a/net/irda/irlmp.c b/net/irda/irlmp.c index 5be0298a9..8b4d47caa 100644 --- a/net/irda/irlmp.c +++ b/net/irda/irlmp.c @@ -74,6 +74,7 @@ int irlmp_proc_read(char *buf, char **start, off_t offst, int len); */ int __init irlmp_init(void) { + IRDA_DEBUG(0, __FUNCTION__ "()\n"); /* Initialize the irlmp structure. */ irlmp = kmalloc( sizeof(struct irlmp_cb), GFP_KERNEL); if (irlmp == NULL) @@ -81,7 +82,7 @@ int __init irlmp_init(void) memset(irlmp, 0, sizeof(struct irlmp_cb)); irlmp->magic = LMP_MAGIC; - spin_lock_init(&irlmp->lock); + spin_lock_init(&irlmp->log_lock); irlmp->clients = hashbin_new(HB_GLOBAL); irlmp->services = hashbin_new(HB_GLOBAL); @@ -178,7 +179,7 @@ struct lsap_cb *irlmp_open_lsap(__u8 slsap_sel, notify_t *notify, __u8 pid) irlmp_next_lsap_state(self, LSAP_DISCONNECTED); /* Insert into queue of unconnected LSAPs */ - hashbin_insert(irlmp->unconnected_lsaps, (queue_t *) self, (int) self, + hashbin_insert(irlmp->unconnected_lsaps, (irda_queue_t *) self, (int) self, NULL); return self; @@ -284,9 +285,9 @@ void irlmp_register_link(struct irlap_cb *irlap, __u32 saddr, notify_t *notify) init_timer(&lap->idle_timer); /* - * Insert into queue of unconnected LSAPs + * Insert into queue of LMP links */ - hashbin_insert(irlmp->links, (queue_t *) lap, lap->saddr, NULL); + hashbin_insert(irlmp->links, (irda_queue_t *) lap, lap->saddr, NULL); /* * We set only this variable so IrLAP can tell us on which link the @@ -395,9 +396,25 @@ int irlmp_connect_request(struct lsap_cb *self, __u8 dlsap_sel, return -EHOSTUNREACH; } + /* Check if LAP is disconnected or already connected */ if (lap->daddr == DEV_ADDR_ANY) lap->daddr = daddr; else if (lap->daddr != daddr) { + struct lsap_cb *any_lsap; + + /* Check if some LSAPs are active on this LAP */ + any_lsap = (struct lsap_cb *) hashbin_get_first(lap->lsaps); + if (any_lsap == NULL) { + /* No active connection, but LAP hasn't been + * disconnected yet (waiting for timeout in LAP). + * Maybe we could give LAP a bit of help in this case. + */ + IRDA_DEBUG(0, __FUNCTION__ "(), sorry, but I'm waiting for LAP to timeout!\n"); + return -EAGAIN; + } + + /* LAP is already connected to a different node, and LAP + * can only talk to one node at a time */ IRDA_DEBUG(0, __FUNCTION__ "(), sorry, but link is busy!\n"); return -EBUSY; } @@ -415,7 +432,7 @@ int irlmp_connect_request(struct lsap_cb *self, __u8 dlsap_sel, ASSERT(lsap->lap != NULL, return -1;); ASSERT(lsap->lap->magic == LMP_LAP_MAGIC, return -1;); - hashbin_insert(self->lap->lsaps, (queue_t *) self, (int) self, NULL); + hashbin_insert(self->lap->lsaps, (irda_queue_t *) self, (int) self, NULL); self->connected = TRUE; @@ -557,7 +574,7 @@ struct lsap_cb *irlmp_dup(struct lsap_cb *orig, void *instance) init_timer(&new->watchdog_timer); - hashbin_insert(irlmp->unconnected_lsaps, (queue_t *) new, (int) new, + hashbin_insert(irlmp->unconnected_lsaps, (irda_queue_t *) new, (int) new, NULL); /* Make sure that we invalidate the cache */ @@ -612,7 +629,7 @@ int irlmp_disconnect_request(struct lsap_cb *self, struct sk_buff *userdata) ASSERT(lsap->magic == LMP_LSAP_MAGIC, return -1;); ASSERT(lsap == self, return -1;); - hashbin_insert(irlmp->unconnected_lsaps, (queue_t *) self, (int) self, + hashbin_insert(irlmp->unconnected_lsaps, (irda_queue_t *) self, (int) self, NULL); /* Reset some values */ @@ -658,7 +675,7 @@ void irlmp_disconnect_indication(struct lsap_cb *self, LM_REASON reason, ASSERT(lsap != NULL, return;); ASSERT(lsap == self, return;); - hashbin_insert(irlmp->unconnected_lsaps, (queue_t *) lsap, (int) lsap, + hashbin_insert(irlmp->unconnected_lsaps, (irda_queue_t *) lsap, (int) lsap, NULL); self->lap = NULL; @@ -749,6 +766,18 @@ void irlmp_discovery_request(int nslots) irlmp_do_discovery(nslots); } +/* + * Function irlmp_get_discoveries (pn, mask) + * + * Return the current discovery log + * + */ +struct irda_device_info *irlmp_get_discoveries(int *pn, __u16 mask) +{ + /* Return current cached discovery log */ + return(irlmp_copy_discoveries(irlmp->cachelog, pn, mask)); +} + #if 0 /* * Function irlmp_check_services (discovery) @@ -759,7 +788,6 @@ void irlmp_discovery_request(int nslots) void irlmp_check_services(discovery_t *discovery) { struct irlmp_client *client; - struct irmanager_event event; __u8 *service_log; __u8 service; int i = 0; @@ -787,14 +815,7 @@ void irlmp_check_services(discovery_t *discovery) continue; /* * Found no clients for dealing with this service, - * so ask the user space irmanager to try to load - * the right module for us */ - event.event = EVENT_DEVICE_DISCOVERED; - event.service = service; - event.daddr = discovery->daddr; - sprintf(event.info, "%s", discovery->info); - irmanager_notify(&event); } } kfree(service_log); @@ -805,17 +826,24 @@ void irlmp_check_services(discovery_t *discovery) * * Notify all about discovered devices * + * Clients registered with IrLMP are : + * o IrComm + * o IrLAN + * o Any socket (in any state - ouch, that may be a lot !) + * The client may have defined a callback to be notified in case of + * partial/selective discovery based on the hints that it passed to IrLMP. */ -void irlmp_notify_client(irlmp_client_t *client, hashbin_t *log) +static inline void +irlmp_notify_client(irlmp_client_t *client, hashbin_t *log) { discovery_t *discovery; IRDA_DEBUG(3, __FUNCTION__ "()\n"); - /* Check if client wants the whole log */ - if (client->callback2) - client->callback2(log); - + /* Check if client wants or not partial/selective log (optimisation) */ + if (!client->disco_callback) + return; + /* * Now, check all discovered devices (if any), and notify client * only about the services that the client is interested in @@ -828,10 +856,9 @@ void irlmp_notify_client(irlmp_client_t *client, hashbin_t *log) * Any common hint bits? Remember to mask away the extension * bits ;-) */ - if (client->hint_mask & discovery->hints.word & 0x7f7f) { - if (client->callback1) - client->callback1(discovery); - } + if (client->hint_mask & discovery->hints.word & 0x7f7f) + client->disco_callback(discovery, client->priv); + discovery = (discovery_t *) hashbin_get_next(log); } } @@ -864,9 +891,40 @@ void irlmp_discovery_confirm(hashbin_t *log) } /* + * Function irlmp_discovery_expiry (expiry) + * + * This device is no longer been discovered, and therefore it is beeing + * purged from the discovery log. Inform all clients who have + * registered for this event... + * + * Note : called exclusively from discovery.c + * Note : as we are currently processing the log, the clients callback + * should *NOT* attempt to touch the log now. + */ +void irlmp_discovery_expiry(discovery_t *expiry) +{ + irlmp_client_t *client; + + IRDA_DEBUG(3, __FUNCTION__ "()\n"); + + ASSERT(expiry != NULL, return;); + + client = (irlmp_client_t *) hashbin_get_first(irlmp->clients); + while (client != NULL) { + /* Check if we should notify client */ + if ((client->expir_callback) && + (client->hint_mask & expiry->hints.word & 0x7f7f)) + client->expir_callback(expiry, client->priv); + + /* Next client */ + client = (irlmp_client_t *) hashbin_get_next(irlmp->clients); + } +} + +/* * Function irlmp_get_discovery_response () * - * Used by IrLAP to get the disocvery info it needs when answering + * Used by IrLAP to get the discovery info it needs when answering * discovery requests by other devices. */ discovery_t *irlmp_get_discovery_response() @@ -1046,9 +1104,35 @@ void irlmp_status_request(void) IRDA_DEBUG(0, __FUNCTION__ "(), Not implemented\n"); } -void irlmp_status_indication(LINK_STATUS link, LOCK_STATUS lock) +/* + * Propagate status indication from LAP to LSAPs (via LMP) + * This don't trigger any change of state in lap_cb, lmp_cb or lsap_cb, + * and the event is stateless, therefore we can bypass both state machines + * and send the event direct to the LSAP user. + * Jean II + */ +void irlmp_status_indication(struct lap_cb *self, + LINK_STATUS link, LOCK_STATUS lock) { - IRDA_DEBUG(1, __FUNCTION__ "(), Not implemented\n"); + struct lsap_cb *next; + struct lsap_cb *curr; + + /* Send status_indication to all LSAPs using this link */ + next = (struct lsap_cb *) hashbin_get_first( self->lsaps); + while (next != NULL ) { + curr = next; + next = (struct lsap_cb *) hashbin_get_next(self->lsaps); + + ASSERT(curr->magic == LMP_LSAP_MAGIC, return;); + /* + * Inform service user if he has requested it + */ + if (curr->notify.status_indication != NULL) + curr->notify.status_indication(curr->notify.instance, + link, lock); + else + IRDA_DEBUG(2, __FUNCTION__ "(), no handler\n"); + } } /* @@ -1207,7 +1291,7 @@ __u32 irlmp_register_service(__u16 hints) return 0; } service->hints = hints; - hashbin_insert(irlmp->services, (queue_t *) service, handle, NULL); + hashbin_insert(irlmp->services, (irda_queue_t *) service, handle, NULL); return handle; } @@ -1255,15 +1339,20 @@ int irlmp_unregister_service(__u32 handle) * Function irlmp_register_client (hint_mask, callback1, callback2) * * Register a local client with IrLMP + * First callback is selective discovery (based on hints) + * Second callback is for selective discovery expiries * * Returns: handle > 0 on success, 0 on error */ -__u32 irlmp_register_client(__u16 hint_mask, DISCOVERY_CALLBACK1 callback1, - DISCOVERY_CALLBACK2 callback2) +__u32 irlmp_register_client(__u16 hint_mask, DISCOVERY_CALLBACK1 disco_clb, + DISCOVERY_CALLBACK1 expir_clb, void *priv) { irlmp_client_t *client; __u32 handle; + IRDA_DEBUG(0, __FUNCTION__ "()\n"); + ASSERT(irlmp != NULL, return 0;); + /* Get a unique handle for this client */ get_random_bytes(&handle, sizeof(handle)); while (hashbin_find(irlmp->clients, handle, NULL) || !handle) @@ -1273,16 +1362,16 @@ __u32 irlmp_register_client(__u16 hint_mask, DISCOVERY_CALLBACK1 callback1, client = kmalloc(sizeof(irlmp_client_t), GFP_ATOMIC); if (!client) { IRDA_DEBUG( 1, __FUNCTION__ "(), Unable to kmalloc!\n"); - return 0; } /* Register the details */ client->hint_mask = hint_mask; - client->callback1 = callback1; - client->callback2 = callback2; + client->disco_callback = disco_clb; + client->expir_callback = expir_clb; + client->priv = priv; - hashbin_insert(irlmp->clients, (queue_t *) client, handle, NULL); + hashbin_insert(irlmp->clients, (irda_queue_t *) client, handle, NULL); return handle; } @@ -1296,8 +1385,8 @@ __u32 irlmp_register_client(__u16 hint_mask, DISCOVERY_CALLBACK1 callback1, * Returns: 0 on success, -1 on error */ int irlmp_update_client(__u32 handle, __u16 hint_mask, - DISCOVERY_CALLBACK1 callback1, - DISCOVERY_CALLBACK2 callback2) + DISCOVERY_CALLBACK1 disco_clb, + DISCOVERY_CALLBACK1 expir_clb, void *priv) { irlmp_client_t *client; @@ -1311,8 +1400,9 @@ int irlmp_update_client(__u32 handle, __u16 hint_mask, } client->hint_mask = hint_mask; - client->callback1 = callback1; - client->callback2 = callback2; + client->disco_callback = disco_clb; + client->expir_callback = expir_clb; + client->priv = priv; return 0; } diff --git a/net/irda/irlmp_event.c b/net/irda/irlmp_event.c index ace20d70e..a4646b274 100644 --- a/net/irda/irlmp_event.c +++ b/net/irda/irlmp_event.c @@ -122,7 +122,7 @@ int irlmp_do_lsap_event(struct lsap_cb *self, IRLMP_EVENT event, ASSERT(self->magic == LMP_LSAP_MAGIC, return -1;); IRDA_DEBUG(4, __FUNCTION__ "(), EVENT = %s, STATE = %s\n", - irlmp_event[event], irlmp_state[ self->lsap_state]); + irlmp_event[event], irlsap_state[ self->lsap_state]); return (*lsap_state[self->lsap_state]) (self, event, skb); } @@ -393,6 +393,14 @@ static void irlmp_state_active(struct lap_cb *self, IRLMP_EVENT event, irlmp_next_lap_state(self, LAP_STANDBY); self->refcount = 0; + /* In some case, at this point our side has already closed + * all lsaps, and we are waiting for the idle_timer to + * expire. If another device reconnect immediately, the + * idle timer will expire in the midle of the connection + * initialisation, screwing up things a lot... + * Therefore, we must stop the timer... */ + irlmp_stop_idle_timer(self); + /* * Inform all connected LSAP's using this link */ @@ -410,7 +418,8 @@ static void irlmp_state_active(struct lap_cb *self, IRLMP_EVENT event, } break; default: - IRDA_DEBUG(0, __FUNCTION__ "(), Unknown event %d\n", event); + IRDA_DEBUG(0, __FUNCTION__ "(), Unknown event %s\n", + irlmp_event[event]); if (skb) dev_kfree_skb(skb); break; @@ -514,7 +523,7 @@ static int irlmp_state_connect(struct lsap_cb *self, IRLMP_EVENT event, ASSERT(self->lap != NULL, return -1;); ASSERT(self->lap->lsaps != NULL, return -1;); - hashbin_insert(self->lap->lsaps, (queue_t *) self, (int) self, + hashbin_insert(self->lap->lsaps, (irda_queue_t *) self, (int) self, NULL); irlmp_send_lcf_pdu(self->lap, self->dlsap_sel, diff --git a/net/irda/irlmp_frame.c b/net/irda/irlmp_frame.c index c9bca1ea2..56287afbb 100644 --- a/net/irda/irlmp_frame.c +++ b/net/irda/irlmp_frame.c @@ -34,6 +34,9 @@ #include <net/irda/irlmp_frame.h> #include <net/irda/discovery.h> +#define DISCO_SMALL_DELAY 250 /* Delay for some discoveries in ms */ +struct timer_list disco_delay; /* The timer associated */ + static struct lsap_cb *irlmp_find_lsap(struct lap_cb *self, __u8 dlsap, __u8 slsap, int status, hashbin_t *); @@ -124,9 +127,11 @@ void irlmp_link_data_indication(struct lap_cb *self, struct sk_buff *skb, irlmp->unconnected_lsaps); /* Maybe LSAP was already connected, so try one more time */ - if (!lsap) + if (!lsap) { + IRDA_DEBUG(1, __FUNCTION__ "(), incoming connection for LSAP already connected\n"); lsap = irlmp_find_lsap(self, dlsap_sel, slsap_sel, 0, self->lsaps); + } } else lsap = irlmp_find_lsap(self, dlsap_sel, slsap_sel, 0, self->lsaps); @@ -338,10 +343,50 @@ void irlmp_link_connect_confirm(struct lap_cb *self, struct qos_info *qos, } /* + * Function irlmp_discovery_timeout (priv) + * + * Create a discovery event to the state machine (called after a delay) + * + * Note : irlmp_do_lap_event will handle the very rare case where the LAP + * is destroyed while we were sleeping. + */ +static void irlmp_discovery_timeout(u_long priv) +{ + struct lap_cb *self; + + IRDA_DEBUG(2, __FUNCTION__ "()\n"); + + self = (struct lap_cb *) priv; + ASSERT(self != NULL, return;); + + /* Just handle it the same way as a discovery confirm */ + irlmp_do_lap_event(self, LM_LAP_DISCOVERY_CONFIRM, NULL); +} + +/* * Function irlmp_link_discovery_indication (self, log) * * Device is discovering us * + * It's not an answer to our own discoveries, just another device trying + * to perform discovery, but we don't want to miss the opportunity + * to exploit this information, because : + * o We may not actively perform discovery (just passive discovery) + * o This type of discovery is much more reliable. In some cases, it + * seem that less than 50% of our discoveries get an answer, while + * we always get ~100% of these. + * o Make faster discovery, statistically divide time of discovery + * events by 2 (important for the latency aspect and user feel) + * However, when both devices discover each other, they might attempt to + * connect to each other, and it would create collisions on the medium. + * The trick here is to defer the event by a little delay to avoid both + * devices to jump in exactly at the same time... + * + * The delay is currently set to 0.25s, which leave enough time to perform + * a connection and don't interfer with next discovery (the lowest discovery + * period/timeout that may be set is 1s). The message triggering this + * event was the last of the discovery, so the medium is now free... + * Maybe more testing is needed to get the value right... */ void irlmp_link_discovery_indication(struct lap_cb *self, discovery_t *discovery) @@ -351,11 +396,14 @@ void irlmp_link_discovery_indication(struct lap_cb *self, irlmp_add_discovery(irlmp->cachelog, discovery); -#if 0 /* This will just cause a lot of connection collisions */ - - /* Just handle it the same way as a discovery confirm */ - irlmp_do_lap_event(self, LM_LAP_DISCOVERY_CONFIRM, NULL); -#endif + /* If delay was activated, kill it! */ + if(timer_pending(&disco_delay)) + del_timer(&disco_delay); + /* Set delay timer to expire in 0.25s. */ + disco_delay.expires = jiffies + (DISCO_SMALL_DELAY * HZ/1000); + disco_delay.function = irlmp_discovery_timeout; + disco_delay.data = (unsigned long) self; + add_timer(&disco_delay); } /* @@ -374,7 +422,12 @@ void irlmp_link_discovery_confirm(struct lap_cb *self, hashbin_t *log) ASSERT(self->magic == LMP_LAP_MAGIC, return;); irlmp_add_discovery_log(irlmp->cachelog, log); - + + /* If discovery delay was activated, kill it! */ + if(timer_pending(&disco_delay)) + del_timer(&disco_delay); + + /* Propagate event to the state machine */ irlmp_do_lap_event(self, LM_LAP_DISCOVERY_CONFIRM, NULL); } diff --git a/net/irda/irnet/Config.in b/net/irda/irnet/Config.in new file mode 100644 index 000000000..d578593f1 --- /dev/null +++ b/net/irda/irnet/Config.in @@ -0,0 +1 @@ +dep_tristate ' IrNET protocol' CONFIG_IRNET $CONFIG_IRDA diff --git a/net/irda/irnet/Makefile b/net/irda/irnet/Makefile new file mode 100644 index 000000000..50554ac72 --- /dev/null +++ b/net/irda/irnet/Makefile @@ -0,0 +1,22 @@ +# +# Makefile for the Linux IrDA IrNET protocol layer. +# +# Note! Dependencies are done automagically by 'make dep', which also +# removes any old dependencies. DON'T put your own dependencies here +# unless it's something special (ie not a .c file). +# +# Note 2! The CFLAGS definition is now in the main makefile... + +MOD_LIST_NAME := IRDA_MODULES +O_TARGET := irnet.o +#O_OBJS := irnet_ppp.o +O_OBJS := irnet_ppp.o irnet_irda.o +M_OBJS := $(O_TARGET) +MI_OBJS := + +OX_OBJS += + +include $(TOPDIR)/Rules.make + +tar: + tar -cvf /dev/f1 . diff --git a/net/irda/irnet/irnet.h b/net/irda/irnet/irnet.h new file mode 100644 index 000000000..6c9df66b3 --- /dev/null +++ b/net/irda/irnet/irnet.h @@ -0,0 +1,453 @@ +/* + * IrNET protocol module : Synchronous PPP over an IrDA socket. + * + * Jean II - HPL `00 - <jt@hpl.hp.com> + * + * This file contains definitions and declarations global to the IrNET module, + * all grouped in one place... + * This file is a private header, so other modules don't want to know + * what's in there... + * + * Note : as most part of the Linux kernel, this module is available + * under the GNU Public License (GPL). + */ + +#ifndef IRNET_H +#define IRNET_H + +/************************** DOCUMENTATION ***************************/ +/* + * What is IrNET + * ------------- + * IrNET is a protocol allowing to carry TCP/IP traffic between two + * IrDA peers in an efficient fashion. It is a thin layer, passing PPP + * packets to IrTTP and vice versa. It uses PPP in synchronous mode, + * because IrTTP offer a reliable sequenced packet service (as opposed + * to a byte stream). In fact, you could see IrNET as carrying TCP/IP + * in a IrDA socket, using PPP to provide the glue. + * + * The main difference with traditional PPP over IrCOMM is that we + * avoid the framing and serial emulation which are a performance + * bottleneck. It also allows multipoint communications in a sensible + * fashion. + * + * The main difference with IrLAN is that we use PPP for the link + * management, which is more standard, interoperable and flexible than + * the IrLAN protocol. For example, PPP adds authentication, + * encryption, compression, header compression and automated routing + * setup. And, as IrNET let PPP do the hard work, the implementation + * is much simpler than IrLAN. + * + * The Linux implementation + * ------------------------ + * IrNET is written on top of the Linux-IrDA stack, and interface with + * the generic Linux PPP driver. Because IrNET depend on recent + * changes of the PPP driver interface, IrNET will work only with very + * recent kernel (2.3.99-pre6 and up). + * + * The present implementation offer the following features : + * o simple user interface using pppd + * o efficient implementation (interface directly to PPP and IrTTP) + * o addressing (you can specify the name of the IrNET recipient) + * o multipoint operation (limited by IrLAP specification) + * o information in /proc/net/irda/irnet + * o IrNET events on /dev/irnet (for user space daemon) + * o IrNET deamon (irnetd) to automatically handle incomming requests + * o Windows 2000 compatibility (tested, but need more work) + * Currently missing : + * o Lot's of testing (that's your job) + * o Connection retries (may be too hard to do) + * o Check pppd persist mode + * o User space deamon (to automatically handle incomming requests) + * o A registered device number (comming, waiting from an answer) + * o Final integration in Linux-IrDA (up to Dag) + * + * The setup is not currently the most easy, but this should get much + * better when everything will get integrated... + * + * Acknowledgements + * ---------------- + * This module is based on : + * o The PPP driver (ppp_synctty/ppp_generic) by Paul Mackerras + * o The IrLAN protocol (irlan_common/XXX) by Dag Brattli + * o The IrSock interface (af_irda) by Dag Brattli + * o Some other bits from the kernel and my drivers... + * Infinite thanks to those brave souls for providing the infrastructure + * upon which IrNET is built. + * + * Thanks to all my collegues in HP for helping me. In particular, + * thanks to Salil Pradhan and Bill Serra for W2k testing... + * Thanks to Luiz Magalhaes for irnetd and much testing... + * + * Thanks to Alan Cox for answering lot's of my stupid questions, and + * to Paul Mackerras answering my questions on how to best integrate + * IrNET and pppd. + * + * Jean II + * + * Note on some implementations choices... + * ------------------------------------ + * 1) Direct interface vs tty/socket + * I could have used a tty interface to hook to ppp and use the full + * socket API to connect to IrDA. The code would have been easier to + * maintain, and maybe the code would have been smaller... + * Instead, we hook directly to ppp_generic and to IrTTP, which make + * things more complicated... + * + * The first reason is flexibility : this allow us to create IrNET + * instances on demand (no /dev/ircommX crap) and to allow linkname + * specification on pppd command line... + * + * Second reason is speed optimisation. If you look closely at the + * transmit and receive paths, you will notice that they are "super lean" + * (that's why they look ugly), with no function calls and as little data + * copy and modification as I could... + * + * 2) irnetd in user space + * irnetd is implemented in user space, which is necessary to call pppd. + * This also give maximum benefits in term of flexibility and customability, + * and allow to offer the event channel, useful for other stuff like debug. + * + * On the other hand, this require a loose coordination between the + * present module and irnetd. One critical area is how incomming request + * are handled. + * When irnet receive an incomming request, it send an event to irnetd and + * drop the incomming IrNET socket. + * irnetd start a pppd instance, which create a new IrNET socket. This new + * socket is then connected in the originating node to the pppd instance. + * At this point, in the originating node, the first socket is closed. + * + * I admit, this is a bit messy and waste some ressources. The alternative + * is caching incomming socket, and that's also quite messy and waste + * ressources. + * We also make connection time slower. For example, on a 115 kb/s link it + * adds 60ms to the connection time (770 ms). However, this is slower than + * the time it takes to fire up pppd on my P133... + * + * + * History : + * ------- + * + * v1 - 15/5/00 - Jean II + * o Basic IrNET (hook to ppp_generic & IrTTP - incl. multipoint) + * o control channel on /dev/irnet (set name/address) + * o event channel on /dev/irnet (for user space daemon) + * + * v2 - 5/6/00 - Jean II + * o Enable DROP_NOT_READY to avoid PPP timeouts & other weirdness... + * o Add DISCONNECT_TO event and rename DISCONNECT_FROM. + * o Set official device number alloaction on /dev/irnet + * + * v3 - 30/8/00 - Jean II + * o Update to latest Linux-IrDA changes : + * - queue_t => irda_queue_t + * o Update to ppp-2.4.0 : + * - move irda_irnet_connect from PPPIOCATTACH to TIOCSETD + * o Add EXPIRE event (depend on new IrDA-Linux patch) + * o Switch from `hashbin_remove' to `hashbin_remove_this' to fix + * a multilink bug... (depend on new IrDA-Linux patch) + * o fix a self->daddr to self->raddr in irda_irnet_connect to fix + * another multilink bug (darn !) + * o Remove LINKNAME_IOCTL cruft + * + * v3b - 31/8/00 - Jean II + * o Dump discovery log at event channel startup + * + * v4 - 28/9/00 - Jean II + * o Fix interaction between poll/select and dump discovery log + * o Add IRNET_BLOCKED_LINK event (depend on new IrDA-Linux patch) + * o Add IRNET_NOANSWER_FROM event (mostly to help support) + * o Release flow control in disconnect_indication + * o Block packets while connecting (speed up connections) + */ + +/***************************** INCLUDES *****************************/ + +#include <linux/module.h> + +#include <linux/kernel.h> +#include <linux/skbuff.h> +#include <linux/tty.h> +#include <linux/proc_fs.h> +#include <linux/devfs_fs_kernel.h> +#include <linux/netdevice.h> +#include <linux/poll.h> +#include <asm/uaccess.h> + +#include <linux/ppp_defs.h> +#include <linux/if_ppp.h> +#include <linux/ppp_channel.h> + +#include <net/irda/irda.h> +#include <net/irda/iriap.h> +#include <net/irda/irias_object.h> +#include <net/irda/irlmp.h> +#include <net/irda/irttp.h> +#include <net/irda/discovery.h> + +/***************************** OPTIONS *****************************/ +/* + * Define or undefine to compile or not some optional part of the + * IrNET driver... + * Note : the present defaults make sense, play with that at your + * own risk... + */ +/* IrDA side of the business... */ +#define DISCOVERY_NOMASK /* To enable W2k compatibility... */ +#define ADVERTISE_HINT /* Advertise IrLAN hint bit */ +#define ALLOW_SIMULT_CONNECT /* This seem to work, cross fingers... */ +#define DISCOVERY_EVENTS /* Query the discovery log to post events */ +#define INITIAL_DISCOVERY /* Dump current discovery log as events */ +#undef STREAM_COMPAT /* Not needed - potentially messy */ +#undef CONNECT_INDIC_KICK /* Might mess IrDA, not needed */ +#undef FAIL_SEND_DISCONNECT /* Might mess IrDA, not needed */ +#undef PASS_CONNECT_PACKETS /* Not needed ? Safe */ + +/* PPP side of the business */ +#define BLOCK_WHEN_CONNECT /* Block packets when connecting */ +#undef CONNECT_IN_SEND /* Will crash hard your box... */ +#undef FLUSH_TO_PPP /* Not sure about this one, let's play safe */ +#undef SECURE_DEVIRNET /* Bah... */ + +/****************************** DEBUG ******************************/ + +/* + * This set of flags enable and disable all the various warning, + * error and debug message of this driver. + * Each section can be enabled and disabled independantly + */ +/* In the PPP part */ +#define DEBUG_CTRL_TRACE 0 /* Control channel */ +#define DEBUG_CTRL_INFO 0 /* various info */ +#define DEBUG_CTRL_ERROR 1 /* problems */ +#define DEBUG_FS_TRACE 0 /* filesystem callbacks */ +#define DEBUG_FS_INFO 0 /* various info */ +#define DEBUG_FS_ERROR 1 /* problems */ +#define DEBUG_PPP_TRACE 0 /* PPP related functions */ +#define DEBUG_PPP_INFO 0 /* various info */ +#define DEBUG_PPP_ERROR 1 /* problems */ +#define DEBUG_MODULE_TRACE 0 /* module insertion/removal */ +#define DEBUG_MODULE_ERROR 1 /* problems */ + +/* In the IrDA part */ +#define DEBUG_IRDA_SR_TRACE 0 /* IRDA subroutines */ +#define DEBUG_IRDA_SR_INFO 0 /* various info */ +#define DEBUG_IRDA_SR_ERROR 1 /* problems */ +#define DEBUG_IRDA_SOCK_TRACE 0 /* IRDA main socket functions */ +#define DEBUG_IRDA_SOCK_INFO 0 /* various info */ +#define DEBUG_IRDA_SOCK_ERROR 1 /* problems */ +#define DEBUG_IRDA_SERV_TRACE 0 /* The IrNET server */ +#define DEBUG_IRDA_SERV_INFO 0 /* various info */ +#define DEBUG_IRDA_SERV_ERROR 1 /* problems */ +#define DEBUG_IRDA_TCB_TRACE 0 /* IRDA IrTTP callbacks */ +#define DEBUG_IRDA_OCB_TRACE 0 /* IRDA other callbacks */ +#define DEBUG_IRDA_CB_INFO 0 /* various info */ +#define DEBUG_IRDA_CB_ERROR 1 /* problems */ + +#define DEBUG_ASSERT 0 /* Verify all assertions */ + +/* + * These are the macros we are using to actually print the debug + * statements. Don't look at it, it's ugly... + * + * One of the trick is that, as the DEBUG_XXX are constant, the + * compiler will optimise away the if() in all cases. + */ +/* All error messages (will show up in the normal logs) */ +#define DERROR(dbg, args...) \ + {if(DEBUG_##dbg) \ + printk(KERN_INFO "irnet: " __FUNCTION__ "(): " args);} + +/* Normal debug message (will show up in /var/log/debug) */ +#define DEBUG(dbg, args...) \ + {if(DEBUG_##dbg) \ + printk(KERN_DEBUG "irnet: " __FUNCTION__ "(): " args);} + +/* Entering a function (trace) */ +#define DENTER(dbg, args...) \ + {if(DEBUG_##dbg) \ + printk(KERN_DEBUG "irnet: ->" __FUNCTION__ args);} + +/* Entering and exiting a function in one go (trace) */ +#define DPASS(dbg, args...) \ + {if(DEBUG_##dbg) \ + printk(KERN_DEBUG "irnet: <>" __FUNCTION__ args);} + +/* Exiting a function (trace) */ +#define DEXIT(dbg, args...) \ + {if(DEBUG_##dbg) \ + printk(KERN_DEBUG "irnet: <-" __FUNCTION__ "()" args);} + +/* Exit a function with debug */ +#define DRETURN(ret, dbg, args...) \ + {DEXIT(dbg, ": " args);\ + return(ret); } + +/* Exit a function on failed condition */ +#define DABORT(cond, ret, dbg, args...) \ + {if(cond) {\ + DERROR(dbg, args);\ + return(ret); }} + +/* Invalid assertion, print out an error and exit... */ +#define DASSERT(cond, ret, dbg, args...) \ + {if((DEBUG_ASSERT) && !(cond)) {\ + DERROR(dbg, "Invalid assertion: " args);\ + return ret; }} + +/************************ CONSTANTS & MACROS ************************/ + +/* Paranoia */ +#define IRNET_MAGIC 0xB00754 + +/* Number of control events in the control channel buffer... */ +#define IRNET_MAX_EVENTS 8 /* Should be more than enough... */ + +/****************************** TYPES ******************************/ + +/* + * This is the main structure where we store all the data pertaining to + * one instance of irnet. + * Note : in irnet functions, a pointer this structure is usually called + * "ap" or "self". If the code is borrowed from the IrDA stack, it tend + * to be called "self", and if it is borrowed from the PPP driver it is + * "ap". Apart from that, it's exactly the same structure ;-) + */ +typedef struct irnet_socket +{ + /* ------------------- Instance management ------------------- */ + /* We manage a linked list of IrNET socket instances */ + irda_queue_t q; /* Must be first - for hasbin */ + int magic; /* Paranoia */ + + /* --------------------- FileSystem part --------------------- */ + /* "pppd" interact directly with us on a /dev/ file */ + struct file * file; /* File descriptor of this instance */ + /* TTY stuff - to keep "pppd" happy */ + struct termios termios; /* Various tty flags */ + /* Stuff for the control channel */ + int event_index; /* Last read in the event log */ + + /* ------------------------- PPP part ------------------------- */ + /* We interface directly to the ppp_generic driver in the kernel */ + int ppp_open; /* registered with ppp_generic */ + struct ppp_channel chan; /* Interface to generic ppp layer */ + + int mru; /* Max size of PPP payload */ + u32 xaccm[8]; /* Asynchronous character map (just */ + u32 raccm; /* to please pppd - dummy) */ + unsigned int flags; /* PPP flags (compression, ...) */ + unsigned int rbits; /* Unused receive flags ??? */ + + /* ------------------------ IrTTP part ------------------------ */ + /* We create a pseudo "socket" over the IrDA tranport */ + int ttp_open; /* Set when IrTTP is ready */ + struct tsap_cb * tsap; /* IrTTP instance (the connection) */ + + char rname[NICKNAME_MAX_LEN + 1]; + /* IrDA nickname of destination */ + __u32 raddr; /* Requested peer IrDA address */ + __u32 saddr; /* my local IrDA address */ + __u32 daddr; /* actual peer IrDA address */ + __u8 dtsap_sel; /* Remote TSAP selector */ + __u8 stsap_sel; /* Local TSAP selector */ + + __u32 max_sdu_size_rx;/* Socket parameters used for IrTTP */ + __u32 max_sdu_size_tx; + __u32 max_data_size; + __u8 max_header_size; + LOCAL_FLOW tx_flow; /* State of the Tx path in IrTTP */ + + /* ------------------- IrLMP and IrIAS part ------------------- */ + /* Used for IrDA Discovery and socket name resolution */ + __u32 ckey; /* IrLMP client handle */ + __u16 mask; /* Hint bits mask (filter discov.)*/ + int nslots; /* Number of slots for discovery */ + + struct iriap_cb * iriap; /* Used to query remote IAS */ + wait_queue_head_t query_wait; /* Wait for the answer to a query */ + struct ias_value * ias_result; /* Result of remote IAS query */ + int errno; /* status of the IAS query */ + + /* ---------------------- Optional parts ---------------------- */ +#ifdef INITIAL_DISCOVERY + /* Stuff used to dump discovery log */ + struct irda_device_info *discoveries; /* Copy of the discovery log */ + int disco_index; /* Last read in the discovery log */ + int disco_number; /* Size of the discovery log */ +#endif INITIAL_DISCOVERY + +} irnet_socket; + +/* + * This is the various event that we will generate on the control channel + */ +typedef enum irnet_event +{ + IRNET_DISCOVER, /* New IrNET node discovered */ + IRNET_EXPIRE, /* IrNET node expired */ + IRNET_CONNECT_TO, /* IrNET socket has connected to other node */ + IRNET_CONNECT_FROM, /* Other node has connected to IrNET socket */ + IRNET_REQUEST_FROM, /* Non satisfied connection request */ + IRNET_NOANSWER_FROM, /* Failed connection request */ + IRNET_BLOCKED_LINK, /* Link (IrLAP) is blocked for > 3s */ + IRNET_DISCONNECT_FROM, /* IrNET socket has disconnected */ + IRNET_DISCONNECT_TO /* Closing IrNET socket */ +} irnet_event; + +/* + * This is the storage for an event and its arguments + */ +typedef struct irnet_log +{ + irnet_event event; + int unit; + __u32 addr; + char name[NICKNAME_MAX_LEN + 1]; +} irnet_log; + +/* + * This is the storage for all events and related stuff... + */ +typedef struct irnet_ctrl_channel +{ + irnet_log log[IRNET_MAX_EVENTS]; /* Event log */ + int index; /* Current index in log */ + spinlock_t spinlock; /* Serialize access to the event log */ + wait_queue_head_t rwait; /* processes blocked on read (or poll) */ +} irnet_ctrl_channel; + +/**************************** PROTOTYPES ****************************/ +/* + * Global functions of the IrNET module + * Note : we list here also functions called from one file to the other. + */ + +/* -------------------------- IRDA PART -------------------------- */ +extern int + irda_irnet_create(irnet_socket *); /* Initialise a IrNET socket */ +extern int + irda_irnet_connect(irnet_socket *); /* Try to connect over IrDA */ +extern void + irda_irnet_destroy(irnet_socket *); /* Teardown a IrNET socket */ +extern int + irda_irnet_init(void); /* Initialise IrDA part of IrNET */ +extern void + irda_irnet_cleanup(void); /* Teardown IrDA part of IrNET */ +/* --------------------------- PPP PART --------------------------- */ +extern int + ppp_irnet_init(void); /* Initialise PPP part of IrNET */ +extern void + ppp_irnet_cleanup(void); /* Teardown PPP part of IrNET */ +/* ---------------------------- MODULE ---------------------------- */ +extern int + init_module(void); /* Initialise IrNET module */ +extern void + cleanup_module(void); /* Teardown IrNET module */ + +/**************************** VARIABLES ****************************/ + +/* Control channel stuff - allocated in irnet_irda.h */ +extern struct irnet_ctrl_channel irnet_events; + +#endif IRNET_H diff --git a/net/irda/irnet/irnet_irda.c b/net/irda/irnet/irnet_irda.c new file mode 100644 index 000000000..ead7ef678 --- /dev/null +++ b/net/irda/irnet/irnet_irda.c @@ -0,0 +1,1481 @@ +/* + * IrNET protocol module : Synchronous PPP over an IrDA socket. + * + * Jean II - HPL `00 - <jt@hpl.hp.com> + * + * This file implement the IRDA interface of IrNET. + * Basically, we sit on top of IrTTP. We set up IrTTP, IrIAS properly, + * and exchange frames with IrTTP. + */ + +#include <linux/config.h> +#include "irnet_irda.h" /* Private header */ + +/************************* CONTROL CHANNEL *************************/ +/* + * When ppp is not active, /dev/irnet act as a control channel. + * Writting allow to set up the IrDA destination of the IrNET channel, + * and any application may be read events happening on IrNET... + */ + +/*------------------------------------------------------------------*/ +/* + * Post an event to the control channel... + * Put the event in the log, and then wait all process blocked on read + * so they can read the log... + */ +static void +irnet_post_event(irnet_socket * ap, + irnet_event event, + __u32 addr, + char * name) +{ + unsigned long flags; /* For spinlock */ + int index; /* In the log */ + + DENTER(CTRL_TRACE, "(ap=0x%X, event=%d, addr=%08x, name=``%s'')\n", + (unsigned int) ap, event, addr, name); + + /* Protect this section via spinlock. + * Note : as we are the only event producer, we only need to exclude + * ourself when touching the log, which is nice and easy. + */ + spin_lock_irqsave(&irnet_events.spinlock, flags); + + /* Copy the event in the log */ + index = irnet_events.index; + irnet_events.log[index].event = event; + irnet_events.log[index].addr = addr; + /* Try to copy IrDA nickname */ + if(name) + strcpy(irnet_events.log[index].name, name); + else + irnet_events.log[index].name[0] = '\0'; + /* Try to get ppp unit number */ + if((ap != (irnet_socket *) NULL) && (ap->ppp_open)) + irnet_events.log[index].unit = ppp_unit_number(&ap->chan); + else + irnet_events.log[index].unit = -1; + + /* Increment the index + * Note that we increment the index only after the event is written, + * to make sure that the readers don't get garbage... */ + irnet_events.index = (index + 1) % IRNET_MAX_EVENTS; + + DEBUG(CTRL_INFO, "New event index is %d\n", irnet_events.index); + + /* Spin lock end */ + spin_unlock_irqrestore(&irnet_events.spinlock, flags); + + /* Now : wake up everybody waiting for events... */ + wake_up_interruptible_all(&irnet_events.rwait); + + DEXIT(CTRL_TRACE, "\n"); +} + +/************************* IRDA SUBROUTINES *************************/ +/* + * These are a bunch of subroutines called from other functions + * down there, mostly common code or to improve readability... + * + * Note : we duplicate quite heavily some routines of af_irda.c, + * because our input structure (self) is quite different + * (struct irnet instead of struct irda_sock), which make sharing + * the same code impossible (at least, without templates). + */ + +/*------------------------------------------------------------------*/ +/* + * Function irda_open_tsap (self) + * + * Open local Transport Service Access Point (TSAP) + * + * Create a IrTTP instance for us and set all the IrTTP callbacks. + */ +static inline int +irnet_open_tsap(irnet_socket * self) +{ + notify_t notify; /* Callback structure */ + + DENTER(IRDA_SR_TRACE, "(self=0x%X)\n", (unsigned int) self); + + DABORT(self->tsap != NULL, -EBUSY, IRDA_SR_ERROR, "Already busy !\n"); + + /* Initialize IrTTP callbacks to be used by the IrDA stack */ + irda_notify_init(¬ify); + notify.connect_confirm = irnet_connect_confirm; + notify.connect_indication = irnet_connect_indication; + notify.disconnect_indication = irnet_disconnect_indication; + notify.data_indication = irnet_data_indication; + /*notify.udata_indication = NULL;*/ + notify.flow_indication = irnet_flow_indication; + notify.status_indication = irnet_status_indication; + notify.instance = self; + strncpy(notify.name, IRNET_NOTIFY_NAME, NOTIFY_MAX_NAME); + + /* Open an IrTTP instance */ + self->tsap = irttp_open_tsap(LSAP_ANY, DEFAULT_INITIAL_CREDIT, + ¬ify); + DABORT(self->tsap == NULL, -ENOMEM, + IRDA_SR_ERROR, "Unable to allocate TSAP !\n"); + + /* Remember which TSAP selector we actually got */ + self->stsap_sel = self->tsap->stsap_sel; + + DEXIT(IRDA_SR_TRACE, " - tsap=0x%X, sel=0x%X\n", + (unsigned int) self->tsap, self->stsap_sel); + return 0; +} + +/*------------------------------------------------------------------*/ +/* + * Function irnet_find_lsap_sel (self) + * + * Try to lookup LSAP selector in remote LM-IAS + * + * Basically, we start a IAP query, and then go to sleep. When the query + * return, irnet_getvalue_confirm will wake us up, and we can examine the + * result of the query... + * Note that in some case, the query fail even before we go to sleep, + * creating some races... + */ +static int +irnet_find_lsap_sel(irnet_socket * self) +{ + DENTER(IRDA_SR_TRACE, "(self=0x%X)\n", (unsigned int) self); + + /* This should not happen */ + DABORT(self->iriap, -EBUSY, IRDA_SR_ERROR, "busy with a previous query.\n"); + + /* Create an IAP instance, will be closed in irnet_getvalue_confirm() */ + self->iriap = iriap_open(LSAP_ANY, IAS_CLIENT, self, + irnet_getvalue_confirm); + + /* Treat unexpected signals as disconnect */ + self->errno = -EHOSTUNREACH; + + /* Query remote LM-IAS */ + iriap_getvaluebyclass_request(self->iriap, self->saddr, self->daddr, + IRNET_SERVICE_NAME, IRNET_IAS_VALUE); + /* Wait for answer (if not already failed) */ + if(self->iriap != NULL) + interruptible_sleep_on(&self->query_wait); + + /* Check what happened */ + if(self->errno) + { + DEBUG(IRDA_SR_INFO, "IAS query failed! (%d)\n", self->errno); + /* Requested object/attribute doesn't exist */ + if((self->errno == IAS_CLASS_UNKNOWN) || + (self->errno == IAS_ATTRIB_UNKNOWN)) + return (-EADDRNOTAVAIL); + else + return (-EHOSTUNREACH); + } + + /* Get the remote TSAP selector */ + switch(self->ias_result->type) + { + case IAS_INTEGER: + DEBUG(IRDA_SR_INFO, "result=%d\n", self->ias_result->t.integer); + if(self->ias_result->t.integer != -1) + self->dtsap_sel = self->ias_result->t.integer; + else + self->dtsap_sel = 0; + break; + default: + self->dtsap_sel = 0; + DERROR(IRDA_SR_ERROR, "bad type ! (0x%X)\n", self->ias_result->type); + break; + } + /* Cleanup */ + if(self->ias_result) + irias_delete_value(self->ias_result); + + DEXIT(IRDA_SR_TRACE, "\n"); + if(self->dtsap_sel) + return 0; + + return -EADDRNOTAVAIL; +} + +/*------------------------------------------------------------------*/ +/* + * Function irnet_discover_daddr_and_lsap_sel (self) + * + * This try to find a device with the requested service. + * + * It basically look into the discovery log. For each address in the list, + * it queries the LM-IAS of the device to find if this device offer + * the requested service. + * If there is more than one node supporting the service, we complain + * to the user (it should move devices around). + * The, we set both the destination address and the lsap selector to point + * on the service on the unique device we have found. + * + * Note : this function fails if there is more than one device in range, + * because IrLMP doesn't disconnect the LAP when the last LSAP is closed. + * Moreover, we would need to wait the LAP disconnection... + */ +static inline int +irnet_discover_daddr_and_lsap_sel(irnet_socket * self) +{ + struct irda_device_info *discoveries; /* Copy of the discovery log */ + int number; /* Number of nodes in the log */ + int i; + int err = -ENETUNREACH; + __u32 daddr = DEV_ADDR_ANY; /* Address we found the service on */ + __u8 dtsap_sel = 0x0; /* TSAP associated with it */ + + DENTER(IRDA_SR_TRACE, "(self=0x%X)\n", (unsigned int) self); + + /* Ask lmp for the current discovery log + * Note : we have to use irlmp_get_discoveries(), as opposed + * to play with the cachelog directly, because while we are + * making our ias query, le log might change... */ + discoveries = irlmp_get_discoveries(&number, self->mask); + /* Check if the we got some results */ + if (discoveries == NULL) + DRETURN(-ENETUNREACH, IRDA_SR_INFO, "Cachelog empty...\n"); + + /* + * Now, check all discovered devices (if any), and connect + * client only about the services that the client is + * interested in... + */ + for(i = 0; i < number; i++) + { + /* Try the address in the log */ + self->daddr = discoveries[i].daddr; + self->saddr = 0x0; + DEBUG(IRDA_SR_INFO, "trying daddr = %08x\n", self->daddr); + + /* Query remote LM-IAS for this service */ + err = irnet_find_lsap_sel(self); + switch(err) + { + case 0: + /* We found the requested service */ + if(daddr != DEV_ADDR_ANY) + { + DEBUG(IRDA_SR_INFO, "More than one device in range supports IrNET...\n"); + } + else + { + /* First time we found that one, save it ! */ + daddr = self->daddr; + dtsap_sel = self->dtsap_sel; + } + break; + case -EADDRNOTAVAIL: + /* Requested service simply doesn't exist on this node */ + break; + default: + /* Something bad did happen :-( */ + DERROR(IRDA_SR_ERROR, "unexpected IAS query failure\n"); + self->daddr = DEV_ADDR_ANY; + kfree(discoveries); + return(-EHOSTUNREACH); + break; + } + } + /* Cleanup our copy of the discovery log */ + kfree(discoveries); + + /* Check out what we found */ + if(daddr == DEV_ADDR_ANY) + { + self->daddr = DEV_ADDR_ANY; + DEXIT(IRDA_SR_INFO, "cannot discover IrNET in any device !!!\n"); + return(-EADDRNOTAVAIL); + } + + /* Revert back to discovered device & service */ + self->daddr = daddr; + self->saddr = 0x0; + self->dtsap_sel = dtsap_sel; + + DEBUG(IRDA_SR_INFO, "discovered IrNET at address %08x\n", self->daddr); + DEXIT(IRDA_SR_TRACE, "\n"); + + return 0; +} + +/*------------------------------------------------------------------*/ +/* + * Function irnet_dname_to_daddr (self) + * + * Convert an IrDA nickname to a valid IrDA address + * + * It basically look into the discovery log until there is a match. + */ +static inline int +irnet_dname_to_daddr(irnet_socket * self) +{ + struct irda_device_info *discoveries; /* Copy of the discovery log */ + int number; /* Number of nodes in the log */ + int i; + + DENTER(IRDA_SR_TRACE, "(self=0x%X)\n", (unsigned int) self); + + /* Ask lmp for the current discovery log */ + discoveries = irlmp_get_discoveries(&number, 0xffff); + /* Check if the we got some results */ + if(discoveries == NULL) + DRETURN(-ENETUNREACH, IRDA_SR_INFO, "Cachelog empty...\n"); + + /* + * Now, check all discovered devices (if any), and connect + * client only about the services that the client is + * interested in... + */ + for(i = 0; i < number; i++) + { + /* Does the name match ? */ + if(!strncmp(discoveries[i].info, self->rname, NICKNAME_MAX_LEN)) + { + /* Yes !!! Get it.. */ + self->daddr = discoveries[i].daddr; + DEBUG(IRDA_SR_INFO, "discovered device ``%s'' at address 0x%08x.\n", + self->rname, self->daddr); + kfree(discoveries); + DEXIT(IRDA_SR_TRACE, "\n"); + return 0; + } + } + /* No luck ! */ + DEBUG(IRDA_SR_INFO, "cannot discover device ``%s'' !!!\n", self->rname); + kfree(discoveries); + return(-EADDRNOTAVAIL); +} + + +/************************* SOCKET ROUTINES *************************/ +/* + * This are the main operations on IrNET sockets, basically to create + * and destroy IrNET sockets. These are called from the PPP part... + */ + +/*------------------------------------------------------------------*/ +/* + * Create a IrNET instance : just initialise some parameters... + */ +int +irda_irnet_create(irnet_socket * self) +{ + DENTER(IRDA_SOCK_TRACE, "(self=0x%X)\n", (unsigned int) self); + + self->magic = IRNET_MAGIC; /* Paranoia */ + + init_waitqueue_head(&self->query_wait); + + self->ttp_open = 0; /* Prevent higher layer from accessing IrTTP */ + self->rname[0] = '\0'; /* May be set via control channel */ + self->raddr = DEV_ADDR_ANY; /* May be set via control channel */ + self->daddr = DEV_ADDR_ANY; /* Until we get connected */ + self->saddr = 0x0; /* so IrLMP assign us any link */ + self->max_sdu_size_rx = TTP_SAR_UNBOUND; + + /* Register as a client with IrLMP */ + self->ckey = irlmp_register_client(0, NULL, NULL, NULL); +#ifdef DISCOVERY_NOMASK + self->mask = 0xffff; /* For W2k compatibility */ +#else DISCOVERY_NOMASK + self->mask = irlmp_service_to_hint(S_LAN); +#endif DISCOVERY_NOMASK + self->tx_flow = FLOW_START; /* Flow control from IrTTP */ + + DEXIT(IRDA_SOCK_TRACE, "\n"); + return(0); +} + +/*------------------------------------------------------------------*/ +/* + * Connect to the other side : + * o convert device name to an address + * o find the socket number (dlsap) + * o Establish the connection + */ +int +irda_irnet_connect(irnet_socket * self) +{ + int err; + + DENTER(IRDA_SOCK_TRACE, "(self=0x%X)\n", (unsigned int) self); + + /* Check if we have opened a local TSAP : + * If we have already opened a TSAP, it means that either we are already + * connected or in the process of doing so... */ + if(self->tsap != NULL) + DRETURN(-EBUSY, IRDA_SOCK_INFO, "Already connecting...\n"); + + /* Insert ourselves in the hashbin so that the IrNET server can find us. + * Notes : 4th arg is string of 32 char max and must be null terminated + * When 4th arg is used (string), 3rd arg isn't (int) + * Can't re-insert (MUST remove first) so check for that... */ + if((irnet_server.running) && (self->q.q_next == NULL)) + { + unsigned long flags; + spin_lock_irqsave(&irnet_server.spinlock, flags); + hashbin_insert(irnet_server.list, (irda_queue_t *) self, 0, self->rname); + spin_unlock_irqrestore(&irnet_server.spinlock, flags); + DEBUG(IRDA_SOCK_INFO, "Inserted ``%s'' in hashbin...\n", self->rname); + } + + /* If we don't have anything (no address, no name) */ + if((self->raddr == DEV_ADDR_ANY) && (self->rname[0] == '\0')) + { + /* Try to find a suitable address */ + if((err = irnet_discover_daddr_and_lsap_sel(self)) != 0) + DRETURN(err, IRDA_SOCK_INFO, "auto-connect failed!\n"); + } + else + { + /* If we have only the name (no address), try to get an address */ + if(self->raddr == DEV_ADDR_ANY) + { + if((err = irnet_dname_to_daddr(self)) != 0) + DRETURN(err, IRDA_SOCK_INFO, "name-connect failed!\n"); + } + else + /* Use the requested destination address */ + self->daddr = self->raddr; + + /* Query remote LM-IAS to find LSAP selector */ + if((err = irnet_find_lsap_sel(self)) != 0) + DRETURN(err, IRDA_SOCK_INFO, "connect failed!\n"); + } + DEBUG(IRDA_SOCK_INFO, "daddr = %08x, lsap = %d, starting IrTTP connection\n", + self->daddr, self->dtsap_sel); + + /* Open a local TSAP (an IrTTP instance) */ + err = irnet_open_tsap(self); + DABORT(err != 0, err, IRDA_SOCK_ERROR, "connect aborted!\n"); + + /* Connect to remote device */ + err = irttp_connect_request(self->tsap, self->dtsap_sel, + self->saddr, self->daddr, NULL, + self->max_sdu_size_rx, NULL); + DABORT(err != 0, err, IRDA_SOCK_ERROR, "connect aborted!\n"); + + DEXIT(IRDA_SOCK_TRACE, "\n"); + return(0); +} + +/*------------------------------------------------------------------*/ +/* + * Function irda_irnet_destroy(self) + * + * Destroy irnet instance + * + */ +void +irda_irnet_destroy(irnet_socket * self) +{ + DENTER(IRDA_SOCK_TRACE, "(self=0x%X)\n", (unsigned int) self); + if(self == NULL) + return; + + /* Remove ourselves from hashbin (if we are queued in hashbin) + * Note : `irnet_server.running' protect us from calls in hashbin_delete() */ + if((irnet_server.running) && (self->q.q_next != NULL)) + { + struct irnet_socket * entry; + unsigned long flags; + DEBUG(IRDA_SOCK_INFO, "Removing from hash..\n"); + spin_lock_irqsave(&irnet_server.spinlock, flags); + entry = hashbin_remove_this(irnet_server.list, (irda_queue_t *) self); + self->q.q_next = NULL; + spin_unlock_irqrestore(&irnet_server.spinlock, flags); + DASSERT(entry == self, , IRDA_SOCK_ERROR, "Can't remove from hash.\n"); + } + + /* Unregister with IrLMP */ + irlmp_unregister_client(self->ckey); + + /* Unregister with LM-IAS */ + if(self->iriap) + iriap_close(self->iriap); + + /* Prevent higher layer from accessing IrTTP */ + self->ttp_open = 0; + + /* Close our IrTTP connection */ + if(self->tsap) + { + DEBUG(IRDA_SOCK_INFO, "Closing our TTP connection.\n"); + irttp_disconnect_request(self->tsap, NULL, P_NORMAL); + irttp_close_tsap(self->tsap); + self->tsap = NULL; + /* Note : as the disconnect comes from ppp_generic, the unit number + * doesn't exist anymore when we post the event, so we need to pass + * NULL as the first arg... */ + irnet_post_event(NULL, IRNET_DISCONNECT_TO, self->daddr, self->rname); + } + self->stsap_sel = 0; + + DEXIT(IRDA_SOCK_TRACE, "\n"); + return; +} + + +/************************** SERVER SOCKET **************************/ +/* + * The IrNET service is composed of one server socket and a variable + * number of regular IrNET sockets. The server socket is supposed to + * handle incomming connections and redirect them to one IrNET sockets. + * It's a superset of the regular IrNET socket, but has a very distinct + * behaviour... + */ + +/*------------------------------------------------------------------*/ +/* + * Function irnet_daddr_to_dname (self) + * + * Convert an IrDA address to a IrDA nickname + * + * It basically look into the discovery log until there is a match. + */ +static inline int +irnet_daddr_to_dname(irnet_socket * self) +{ + struct irda_device_info *discoveries; /* Copy of the discovery log */ + int number; /* Number of nodes in the log */ + int i; + + DENTER(IRDA_SERV_TRACE, "(self=0x%X)\n", (unsigned int) self); + + /* Ask lmp for the current discovery log */ + discoveries = irlmp_get_discoveries(&number, 0xffff); + /* Check if the we got some results */ + if (discoveries == NULL) + DRETURN(-ENETUNREACH, IRDA_SERV_INFO, "Cachelog empty...\n"); + + /* Now, check all discovered devices (if any) */ + for(i = 0; i < number; i++) + { + /* Does the name match ? */ + if(discoveries[i].daddr == self->daddr) + { + /* Yes !!! Get it.. */ + strncpy(self->rname, discoveries[i].info, NICKNAME_MAX_LEN); + self->rname[NICKNAME_MAX_LEN + 1] = '\0'; + DEBUG(IRDA_SERV_INFO, "Device 0x%08x is in fact ``%s''.\n", + self->daddr, self->rname); + kfree(discoveries); + DEXIT(IRDA_SERV_TRACE, "\n"); + return 0; + } + } + /* No luck ! */ + DEXIT(IRDA_SERV_INFO, ": cannot discover device 0x%08x !!!\n", self->daddr); + kfree(discoveries); + return(-EADDRNOTAVAIL); +} + +/*------------------------------------------------------------------*/ +/* + * Function irda_find_socket (self) + * + * Find the correct IrNET socket + * + * Look into the list of IrNET sockets and finds one with the right + * properties... + */ +static inline irnet_socket * +irnet_find_socket(irnet_socket * self) +{ + irnet_socket * new = (irnet_socket *) NULL; + unsigned long flags; + int err; + + DENTER(IRDA_SERV_TRACE, "(self=0x%X)\n", (unsigned int) self); + + /* Get the address of the requester */ + self->daddr = irttp_get_daddr(self->tsap); + + /* Try to get the IrDA nickname of the requester */ + err = irnet_daddr_to_dname(self); + + /* Protect access to the instance list */ + spin_lock_irqsave(&irnet_server.spinlock, flags); + + /* So now, try to get an socket having specifically + * requested that nickname */ + if(err == 0) + { + new = (irnet_socket *) hashbin_find(irnet_server.list, + 0, self->rname); + if(new) + DEBUG(IRDA_SERV_INFO, "Socket 0x%X matches rname ``%s''.\n", + (unsigned int) new, new->rname); + } + + /* If no name matches, try to find an socket by the destination address */ + /* It can be either the requested destination address (set via the + * control channel), or the current destination address if the + * socket is in the middle of a connection request */ + if(new == (irnet_socket *) NULL) + { + new = (irnet_socket *) hashbin_get_first(irnet_server.list); + while(new !=(irnet_socket *) NULL) + { + /* Does it have the same address ? */ + if((new->raddr == self->daddr) || (new->daddr == self->daddr)) + { + /* Yes !!! Get it.. */ + DEBUG(IRDA_SERV_INFO, "Socket 0x%X matches daddr %#08x.\n", + (unsigned int) new, self->daddr); + break; + } + new = (irnet_socket *) hashbin_get_next(irnet_server.list); + } + } + + /* If we don't have any socket, get the first unconnected socket */ + if(new == (irnet_socket *) NULL) + { + new = (irnet_socket *) hashbin_get_first(irnet_server.list); + while(new !=(irnet_socket *) NULL) + { + /* Is it available ? */ + if(!(new->ttp_open) && (new->raddr == DEV_ADDR_ANY) && + (new->rname[0] == '\0') && (new->ppp_open)) + { + /* Yes !!! Get it.. */ + DEBUG(IRDA_SERV_INFO, "Socket 0x%X is free.\n", + (unsigned int) new); + break; + } + new = (irnet_socket *) hashbin_get_next(irnet_server.list); + } + } + + /* Spin lock end */ + spin_unlock_irqrestore(&irnet_server.spinlock, flags); + + DEXIT(IRDA_SERV_TRACE, " - new = 0x%X\n", (unsigned int) new); + return new; +} + +/*------------------------------------------------------------------*/ +/* + * Function irda_connect_socket (self) + * + * Connect an incomming connection to the socket + * + */ +static inline int +irnet_connect_socket(irnet_socket * self, + irnet_socket * new, + struct qos_info * qos, + __u32 max_sdu_size, + __u8 max_header_size) +{ + DENTER(IRDA_SERV_TRACE, "(self=0x%X, new=0x%X)\n", + (unsigned int) self, (unsigned int) new); + + /* Now attach up the new socket */ + new->tsap = irttp_dup(self->tsap, new); + DABORT(new->tsap == NULL, -1, IRDA_SERV_ERROR, "dup failed!\n"); + + /* Set up all the relevant parameters on the new socket */ + new->stsap_sel = new->tsap->stsap_sel; + new->dtsap_sel = new->tsap->dtsap_sel; + new->saddr = irttp_get_saddr(new->tsap); + new->daddr = irttp_get_daddr(new->tsap); + + new->max_header_size = max_header_size; + new->max_sdu_size_tx = max_sdu_size; + new->max_data_size = max_sdu_size; +#ifdef STREAM_COMPAT + /* If we want to receive "stream sockets" */ + if(max_sdu_size == 0) + new->max_data_size = irttp_get_max_seg_size(new->tsap); +#endif STREAM_COMPAT + + /* Clean up the original one to keep it in listen state */ + self->tsap->dtsap_sel = self->tsap->lsap->dlsap_sel = LSAP_ANY; + self->tsap->lsap->lsap_state = LSAP_DISCONNECTED; + + /* Send a connection response on the new socket */ + irttp_connect_response(new->tsap, new->max_sdu_size_rx, NULL); + + /* Allow PPP to send its junk over the new socket... */ + new->ttp_open = 1; +#ifdef CONNECT_INDIC_KICK + /* As currently we don't packets in ppp_irnet_send(), this is not needed... + * Also, not doing it give IrDA a chance to finish the setup properly + * before beeing swamped with packets... */ + ppp_output_wakeup(&new->chan); +#endif CONNECT_INDIC_KICK + + /* Notify the control channel */ + irnet_post_event(new, IRNET_CONNECT_FROM, new->daddr, self->rname); + + DEXIT(IRDA_SERV_TRACE, "\n"); + return 0; +} + +/*------------------------------------------------------------------*/ +/* + * Function irda_disconnect_server (self) + * + * Cleanup the server socket when the incomming connection abort + * + */ +static inline void +irnet_disconnect_server(irnet_socket * self, + struct sk_buff *skb) +{ + DENTER(IRDA_SERV_TRACE, "(self=0x%X)\n", (unsigned int) self); + + /* Put the received packet in the black hole */ + kfree_skb(skb); + +#ifdef FAIL_SEND_DISCONNECT + /* Tell the other party we don't want to be connected */ + /* Hum... Is it the right thing to do ? And do we need to send + * a connect response before ? It looks ok without this... */ + irttp_disconnect_request(self->tsap, NULL, P_NORMAL); +#endif FAIL_SEND_DISCONNECT + + /* Clean up the server to keep it in listen state */ + self->tsap->dtsap_sel = self->tsap->lsap->dlsap_sel = LSAP_ANY; + self->tsap->lsap->lsap_state = LSAP_DISCONNECTED; + + /* Notify the control channel */ + irnet_post_event(NULL, IRNET_REQUEST_FROM, self->daddr, self->rname); + + DEXIT(IRDA_SERV_TRACE, "\n"); + return; +} + +/*------------------------------------------------------------------*/ +/* + * Function irda_setup_server (self) + * + * Create a IrTTP server and set it up... + * + * Register the IrLAN hint bit, create a IrTTP instance for us, + * set all the IrTTP callbacks and create an IrIAS entry... + */ +static inline int +irnet_setup_server(void) +{ + __u16 hints; + + DENTER(IRDA_SERV_TRACE, "()\n"); + + /* Initialise the regular socket part of the server */ + irda_irnet_create(&irnet_server.s); + + /* Open a local TSAP (an IrTTP instance) for the server */ + irnet_open_tsap(&irnet_server.s); + + /* PPP part setup */ + irnet_server.s.ppp_open = 0; + irnet_server.s.chan.private = NULL; + irnet_server.s.file = NULL; + + /* Get the hint bit corresponding to IrLAN */ + /* Note : we overload the IrLAN hint bit. As it is only a "hint", and as + * we provide roughly the same functionality as IrLAN, this is ok. + * In fact, the situation is similar as JetSend overloading the Obex hint + */ + hints = irlmp_service_to_hint(S_LAN); + +#ifdef ADVERTISE_HINT + /* Register with IrLMP as a service (advertise our hint bit) */ + irnet_server.skey = irlmp_register_service(hints); +#endif ADVERTISE_HINT + + /* Register with LM-IAS (so that people can connect to us) */ + irnet_server.ias_obj = irias_new_object(IRNET_SERVICE_NAME, jiffies); + irias_add_integer_attrib(irnet_server.ias_obj, IRNET_IAS_VALUE, + irnet_server.s.stsap_sel, IAS_KERNEL_ATTR); + irias_insert_object(irnet_server.ias_obj); + +#ifdef DISCOVERY_EVENTS + /* Tell IrLMP we want to be notified of newly discovered nodes */ + irlmp_update_client(irnet_server.s.ckey, hints, + irnet_discovery_indication, irnet_expiry_indication, + (void *) &irnet_server.s); +#endif + + DEXIT(IRDA_SERV_TRACE, " - self=0x%X\n", (unsigned int) &irnet_server.s); + return 0; +} + +/*------------------------------------------------------------------*/ +/* + * Function irda_destroy_server (self) + * + * Destroy the IrTTP server... + * + * Reverse of the previous function... + */ +static inline void +irnet_destroy_server(void) +{ + DENTER(IRDA_SERV_TRACE, "()\n"); + +#ifdef ADVERTISE_HINT + /* Unregister with IrLMP */ + irlmp_unregister_service(irnet_server.skey); +#endif ADVERTISE_HINT + + /* Unregister with LM-IAS */ + if(irnet_server.ias_obj) + irias_delete_object(irnet_server.ias_obj); + + /* Cleanup the socket part */ + irda_irnet_destroy(&irnet_server.s); + + DEXIT(IRDA_SERV_TRACE, "\n"); + return; +} + + +/************************ IRDA-TTP CALLBACKS ************************/ +/* + * When we create a IrTTP instance, we pass to it a set of callbacks + * that IrTTP will call in case of various events. + * We take care of those events here. + */ + +/*------------------------------------------------------------------*/ +/* + * Function irnet_data_indication (instance, sap, skb) + * + * Received some data from TinyTP. Just queue it on the receive queue + * + */ +static int +irnet_data_indication(void * instance, + void * sap, + struct sk_buff *skb) +{ + irnet_socket * ap = (irnet_socket *) instance; + unsigned char * p; + int code = 0; + + DENTER(IRDA_TCB_TRACE, "(self/ap=0x%X, skb=0x%X)\n", + (unsigned int) ap,(unsigned int) skb); + DASSERT(skb != NULL, 0, IRDA_CB_ERROR, "skb is NULL !!!\n"); + + /* Check is ppp is ready to receive our packet */ + if(!ap->ppp_open) + { + DERROR(IRDA_CB_ERROR, "PPP not ready, dropping packet...\n"); + /* When we return error, TTP will need to requeue the skb and + * will stop the sender. IrTTP will stall until we send it a + * flow control request... */ + return -ENOMEM; + } + + /* strip address/control field if present */ + p = skb->data; + if((p[0] == PPP_ALLSTATIONS) && (p[1] == PPP_UI)) + { + /* chop off address/control */ + if(skb->len < 3) + goto err_exit; + p = skb_pull(skb, 2); + } + + /* decompress protocol field if compressed */ + if(p[0] & 1) + { + /* protocol is compressed */ + skb_push(skb, 1)[0] = 0; + } + else + if(skb->len < 2) + goto err_exit; + + /* pass to generic ppp layer */ + /* Note : how do I know if ppp can accept or not the packet ? This is + * essential if I want to manage flow control smoothly... */ + ppp_input(&ap->chan, skb); + + DEXIT(IRDA_TCB_TRACE, "\n"); + return 0; + + err_exit: + DERROR(IRDA_CB_ERROR, "Packet too small, dropping...\n"); + kfree_skb(skb); + ppp_input_error(&ap->chan, code); + return 0; /* Don't return an error code, only for flow control... */ +} + +/*------------------------------------------------------------------*/ +/* + * Function irnet_disconnect_indication (instance, sap, reason, skb) + * + * Connection has been closed. Chech reason to find out why + * + * Note : there are many cases where we come here : + * o attempted to connect, timeout + * o connected, link is broken, LAP has timeout + * o connected, other side close the link + * o connection request on the server no handled + */ +static void +irnet_disconnect_indication(void * instance, + void * sap, + LM_REASON reason, + struct sk_buff *skb) +{ + irnet_socket * self = (irnet_socket *) instance; + + DENTER(IRDA_TCB_TRACE, "(self=0x%X)\n", (unsigned int) self); + DASSERT(self != NULL, , IRDA_CB_ERROR, "Self is NULL !!!\n"); + + /* If we were active, notify the control channel */ + if(self->ttp_open) + irnet_post_event(self, IRNET_DISCONNECT_FROM, self->daddr, self->rname); + else + /* If we were trying to connect, notify the control channel */ + if((self->tsap) && (self != &irnet_server.s)) + irnet_post_event(self, IRNET_NOANSWER_FROM, self->daddr, self->rname); + + /* Prevent higher layer from accessing IrTTP */ + self->ttp_open = 0; + + /* Close our IrTTP connection */ + if((self->tsap) && (self != &irnet_server.s)) + { + DEBUG(IRDA_CB_INFO, "Closing our TTP connection.\n"); + irttp_disconnect_request(self->tsap, NULL, P_NORMAL); + irttp_close_tsap(self->tsap); + self->tsap = NULL; + + /* Flush (drain) ppp_generic Tx queue (most often we have blocked it) */ + if(self->ppp_open) + ppp_output_wakeup(&self->chan); + } + /* Cleanup the socket in case we want to reconnect */ + self->stsap_sel = 0; + self->daddr = DEV_ADDR_ANY; + self->tx_flow = FLOW_START; + + /* Note : what should we say to ppp ? + * It seem the ppp_generic and pppd are happy that way and will eventually + * timeout gracefully, so don't bother them... */ + + DEXIT(IRDA_TCB_TRACE, "\n"); +} + +/*------------------------------------------------------------------*/ +/* + * Function irnet_connect_confirm (instance, sap, qos, max_sdu_size, skb) + * + * Connections has been confirmed by the remote device + * + */ +static void +irnet_connect_confirm(void * instance, + void * sap, + struct qos_info *qos, + __u32 max_sdu_size, + __u8 max_header_size, + struct sk_buff *skb) +{ + irnet_socket * self = (irnet_socket *) instance; + + DENTER(IRDA_TCB_TRACE, "(self=0x%X)\n", (unsigned int) self); + + /* How much header space do we need to reserve */ + self->max_header_size = max_header_size; + + /* IrTTP max SDU size in transmit direction */ + self->max_sdu_size_tx = max_sdu_size; + self->max_data_size = max_sdu_size; +#ifdef STREAM_COMPAT + if(max_sdu_size == 0) + self->max_data_size = irttp_get_max_seg_size(self->tsap); +#endif STREAM_COMPAT + + /* At this point, IrLMP has assigned our source address */ + self->saddr = irttp_get_saddr(self->tsap); + + /* Allow higher layer to access IrTTP */ + self->ttp_open = 1; + /* Give a kick in the ass of ppp_generic so that he sends us some data */ + ppp_output_wakeup(&self->chan); + + /* Check size of received packet */ + if(skb->len > 0) + { +#ifdef PASS_CONNECT_PACKETS + DEBUG(IRDA_CB_INFO, "Passing connect packet to PPP.\n"); + /* Try to pass it to PPP */ + irnet_data_indication(instance, sap, skb); +#else PASS_CONNECT_PACKETS + DERROR(IRDA_CB_ERROR, "Dropping non empty packet.\n"); + kfree_skb(skb); /* Note : will be optimised with other kfree... */ +#endif PASS_CONNECT_PACKETS + } + else + kfree_skb(skb); + + /* Notify the control channel */ + irnet_post_event(self, IRNET_CONNECT_TO, self->daddr, self->rname); + + DEXIT(IRDA_TCB_TRACE, "\n"); +} + +/*------------------------------------------------------------------*/ +/* + * Function irnet_flow_indication (instance, sap, flow) + * + * Used by TinyTP to tell us if it can accept more data or not + * + */ +static void +irnet_flow_indication(void * instance, + void * sap, + LOCAL_FLOW flow) +{ + irnet_socket * self = (irnet_socket *) instance; + + DENTER(IRDA_TCB_TRACE, "(self=0x%X, flow=%d)\n", (unsigned int) self, flow); + + /* Update our state */ + self->tx_flow = flow; + + /* Check what IrTTP want us to do... */ + switch(flow) + { + case FLOW_START: + DEBUG(IRDA_CB_INFO, "IrTTP wants us to start again\n"); + ppp_output_wakeup(&self->chan); + break; + case FLOW_STOP: + DEBUG(IRDA_CB_INFO, "IrTTP wants us to slow down\n"); + break; + default: + DEBUG(IRDA_CB_INFO, "Unknown flow command!\n"); + break; + } + + DEXIT(IRDA_TCB_TRACE, "\n"); +} + +/*------------------------------------------------------------------*/ +/* + * Function irnet_status_indication (instance, sap, reason, skb) + * + * Link (IrLAP) status report. + * + */ +static void +irnet_status_indication(void * instance, + LINK_STATUS link, + LOCK_STATUS lock) +{ + irnet_socket * self = (irnet_socket *) instance; + + DENTER(IRDA_TCB_TRACE, "(self=0x%X)\n", (unsigned int) self); + DASSERT(self != NULL, , IRDA_CB_ERROR, "Self is NULL !!!\n"); + + /* We can only get this event if we are connected */ + switch(link) + { + case STATUS_NO_ACTIVITY: + irnet_post_event(self, IRNET_BLOCKED_LINK, self->daddr, self->rname); + break; + default: + DEBUG(IRDA_CB_INFO, "Unknown status...\n"); + } + + DEXIT(IRDA_TCB_TRACE, "\n"); +} + +/*------------------------------------------------------------------*/ +/* + * Function irnet_connect_indication(instance, sap, qos, max_sdu_size, userdata) + * + * Incomming connection + * + * In theory, this function is called only on the server socket. + * Some other node is attempting to connect to the IrNET service, and has + * sent a connection request on our server socket. + * We just redirect the connection to the relevant IrNET socket. + * + * Note : we also make sure that between 2 irnet nodes, there can + * exist only one irnet connection. + */ +static void +irnet_connect_indication(void * instance, + void * sap, + struct qos_info *qos, + __u32 max_sdu_size, + __u8 max_header_size, + struct sk_buff *skb) +{ + irnet_socket * self = &irnet_server.s; + irnet_socket * new = (irnet_socket *) NULL; + + DENTER(IRDA_TCB_TRACE, "(self=0x%X)\n", (unsigned int) self); + DASSERT(instance == &irnet_server, , IRDA_CB_ERROR, + "Invalid instance (0x%X) !!!\n", (unsigned int) instance); + DASSERT(sap == irnet_server.s.tsap, , IRDA_CB_ERROR, "Invalid sap !!!\n"); + + /* Try to find the most appropriate IrNET socket */ + new = irnet_find_socket(self); + + /* After all this hard work, do we have an socket ? */ + if(new == (irnet_socket *) NULL) + { + DEXIT(IRDA_CB_INFO, ": No socket waiting for this connection.\n"); + irnet_disconnect_server(self, skb); + return; + } + + /* Is the socket already busy ? */ + if(new->ttp_open) + { + DEXIT(IRDA_CB_INFO, ": Socket already connected.\n"); + irnet_disconnect_server(self, skb); + return; + } + + /* Socket connecting */ + if(new->tsap != NULL) + { + /* The socket has sent a IrTTP connection request and is waiting for + * a connection response (that may never come). + * Now, the pain is that the socket has open a tsap and is waiting on it, + * while the other end is trying to connect to it on another tsap. + * Argh ! We will deal with that later... + */ + DERROR(IRDA_CB_ERROR, "Socket already connecting. Ouch !\n"); +#ifdef ALLOW_SIMULT_CONNECT + /* Close the connection the new socket was attempting. + * WARNING : This need more testing ! */ + irttp_close_tsap(new->tsap); + /* Note : no return, fall through... */ +#else ALLOW_SIMULT_CONNECT + irnet_disconnect_server(self, skb); + return; +#endif ALLOW_SIMULT_CONNECT + } + + /* So : at this point, we have a socket, and it is idle. Good ! */ + irnet_connect_socket(self, new, qos, max_sdu_size, max_header_size); + + /* Check size of received packet */ + if(skb->len > 0) + { +#ifdef PASS_CONNECT_PACKETS + DEBUG(IRDA_CB_INFO, "Passing connect packet to PPP.\n"); + /* Try to pass it to PPP */ + irnet_data_indication(new, new->tsap, skb); +#else PASS_CONNECT_PACKETS + DERROR(IRDA_CB_ERROR, "Dropping non empty packet.\n"); + kfree_skb(skb); /* Note : will be optimised with other kfree... */ +#endif PASS_CONNECT_PACKETS + } + else + kfree_skb(skb); + + DEXIT(IRDA_TCB_TRACE, "\n"); +} + + +/********************** IRDA-IAS/LMP CALLBACKS **********************/ +/* + * These are the callbacks called by other layers of the IrDA stack, + * mainly LMP for discovery and IAS for name queries. + */ + +/*------------------------------------------------------------------*/ +/* + * Function irnet_getvalue_confirm (obj_id, value, priv) + * + * Got answer from remote LM-IAS, just pass object to requester... + * + */ +static void +irnet_getvalue_confirm(int result, + __u16 obj_id, + struct ias_value *value, + void * priv) +{ + irnet_socket * self = (irnet_socket *) priv; + + DENTER(IRDA_OCB_TRACE, "(self=0x%X)\n", (unsigned int) self); + DASSERT(self != NULL, , IRDA_CB_ERROR, "Self is NULL !!!\n"); + + /* We probably don't need to make any more queries */ + iriap_close(self->iriap); + self->iriap = NULL; + + /* Check if request succeeded */ + if(result != IAS_SUCCESS) + { + DEBUG(IRDA_CB_INFO, "IAS query failed! (%d)\n", result); + self->errno = result; /* We really need it later */ + } + else + { + /* Pass the object to the caller (so the caller must delete it) */ + self->ias_result = value; + self->errno = 0; + } + + /* Wake up any processes waiting for result */ + wake_up_interruptible(&self->query_wait); + + DEXIT(IRDA_OCB_TRACE, "\n"); +} + +#ifdef DISCOVERY_EVENTS +/*------------------------------------------------------------------*/ +/* + * Function irnet_discovery_indication (discovery) + * + * Got a discovery indication from IrLMP, post an event + * + * Note : IrLMP take care of matching the hint mask for us, we only + * check if it is a "new" node... + * + * As IrLMP filter on the IrLAN hint bit, we get both IrLAN and IrNET + * nodes, so it's only at connection time that we will know if the + * node support IrNET, IrLAN or both. The other solution is to check + * in IAS the PNP ids and service name. + * Note : even if a node support IrNET (or IrLAN), it's no guarantee + * that we will be able to connect to it, the node might already be + * busy... + * + * One last thing : in some case, this function will trigger duplicate + * discovery events. On the other hand, we should catch all + * discoveries properly (i.e. not miss one). Filtering duplicate here + * is to messy, so we leave that to user space... + */ +static void +irnet_discovery_indication(discovery_t *discovery, + void * priv) +{ + irnet_socket * self = &irnet_server.s; + + DENTER(IRDA_OCB_TRACE, "(self=0x%X)\n", (unsigned int) self); + DASSERT(priv == &irnet_server, , IRDA_CB_ERROR, + "Invalid instance (0x%X) !!!\n", (unsigned int) priv); + + /* Check if node is discovered is a new one or an old one. + * We check when how long ago this node was discovered, with a + * coarse timeout (we may miss some discovery events or be delayed). + */ + if((jiffies - discovery->first_timestamp) >= (sysctl_discovery_timeout * HZ)) + { + return; /* Too old, not interesting -> goodbye */ + } + + DEBUG(IRDA_CB_INFO, "Discovered new IrNET/IrLAN node %s...\n", + discovery->nickname); + + /* Notify the control channel */ + irnet_post_event(NULL, IRNET_DISCOVER, discovery->daddr, + discovery->nickname); + + DEXIT(IRDA_OCB_TRACE, "\n"); +} + +/*------------------------------------------------------------------*/ +/* + * Function irnet_expiry_indication (expiry) + * + * Got a expiry indication from IrLMP, post an event + * + * Note : IrLMP take care of matching the hint mask for us, we only + * check if it is a "new" node... + */ +static void +irnet_expiry_indication(discovery_t * expiry, + void * priv) +{ + irnet_socket * self = &irnet_server.s; + + DENTER(IRDA_OCB_TRACE, "(self=0x%X)\n", (unsigned int) self); + DASSERT(priv == &irnet_server, , IRDA_CB_ERROR, + "Invalid instance (0x%X) !!!\n", (unsigned int) priv); + + DEBUG(IRDA_CB_INFO, "IrNET/IrLAN node %s expired...\n", + expiry->nickname); + + /* Notify the control channel */ + irnet_post_event(NULL, IRNET_EXPIRE, expiry->daddr, + expiry->nickname); + + DEXIT(IRDA_OCB_TRACE, "\n"); +} +#endif DISCOVERY_EVENTS + + +/*********************** PROC ENTRY CALLBACKS ***********************/ +/* + * We create a instance in the /proc filesystem, and here we take care + * of that... + */ + +#ifdef CONFIG_PROC_FS +/*------------------------------------------------------------------*/ +/* + * Function irnet_proc_read (buf, start, offset, len, unused) + * + * Give some info to the /proc file system + */ +static int +irnet_proc_read(char * buf, + char ** start, + off_t offset, + int len) +{ + irnet_socket * self; + char * state; + unsigned long flags; + int i = 0; + + len = 0; + + /* Get the IrNET server information... */ + len += sprintf(buf+len, "IrNET server - "); + len += sprintf(buf+len, "IrDA state: %s, ", + (irnet_server.running ? "running" : "dead")); + len += sprintf(buf+len, "stsap_sel: %02x, ", irnet_server.s.stsap_sel); + len += sprintf(buf+len, "dtsap_sel: %02x\n", irnet_server.s.dtsap_sel); + + /* Do we need to continue ? */ + if(!irnet_server.running) + return len; + + /* Protect access to the instance list */ + spin_lock_irqsave(&irnet_server.spinlock, flags); + + /* Get the sockets one by one... */ + self = (irnet_socket *) hashbin_get_first(irnet_server.list); + while(self != NULL) + { + /* Start printing info about the socket. */ + len += sprintf(buf+len, "\nIrNET socket %d - ", i++); + + /* First, get the requested configuration */ + len += sprintf(buf+len, "Requested IrDA name: \"%s\", ", self->rname); + len += sprintf(buf+len, "addr: %08x\n", self->raddr); + + /* Second, get all the PPP info */ + len += sprintf(buf+len, " PPP state: %s", + (self->ppp_open ? "registered" : "unregistered")); + if(self->ppp_open) + { + len += sprintf(buf+len, ", unit: ppp%d", + ppp_unit_number(&self->chan)); + len += sprintf(buf+len, ", channel: %d", + ppp_channel_index(&self->chan)); + len += sprintf(buf+len, ", mru: %d", + self->mru); + /* Maybe add self->flags ? Later... */ + } + + /* Then, get all the IrDA specific info... */ + if(self->ttp_open) + state = "connected"; + else + if(self->tsap != NULL) + state = "connecting"; + else + state = "idle"; + len += sprintf(buf+len, "\n IrDA state: %s, ", state); + len += sprintf(buf+len, "daddr: %08x, ", self->daddr); + len += sprintf(buf+len, "stsap_sel: %02x, ", self->stsap_sel); + len += sprintf(buf+len, "dtsap_sel: %02x\n", self->dtsap_sel); + + /* Next socket, please... */ + self = (irnet_socket *) hashbin_get_next(irnet_server.list); + } + + /* Spin lock end */ + spin_unlock_irqrestore(&irnet_server.spinlock, flags); + + return len; +} +#endif /* PROC_FS */ + + +/********************** CONFIGURATION/CLEANUP **********************/ +/* + * Initialisation and teardown of the IrDA part, called at module + * insertion and removal... + */ + +/*------------------------------------------------------------------*/ +/* + * Prepare the IrNET layer for operation... + */ +int +irda_irnet_init(void) +{ + int err = 0; + + DENTER(MODULE_TRACE, "()\n"); + + /* Pure paranoia - should be redundant */ + memset(&irnet_server, 0, sizeof(struct irnet_root)); + + /* Setup start of irnet instance list */ + irnet_server.list = hashbin_new(HB_LOCAL); + DABORT(irnet_server.list == NULL, -ENOMEM, + MODULE_ERROR, "Can't allocate hashbin!\n"); + /* Init spinlock for instance list */ + spin_lock_init(&irnet_server.spinlock); + + /* Initialise control channel */ + init_waitqueue_head(&irnet_events.rwait); + irnet_events.index = 0; + /* Init spinlock for event logging */ + spin_lock_init(&irnet_events.spinlock); + +#ifdef CONFIG_PROC_FS + /* Add a /proc file for irnet infos */ + create_proc_info_entry("irnet", 0, proc_irda, irnet_proc_read); +#endif /* CONFIG_PROC_FS */ + + /* Setup the IrNET server */ + err = irnet_setup_server(); + + if(!err) + /* We are no longer functional... */ + irnet_server.running = 1; + + DEXIT(MODULE_TRACE, "\n"); + return err; +} + +/*------------------------------------------------------------------*/ +/* + * Cleanup at exit... + */ +void +irda_irnet_cleanup(void) +{ + DENTER(MODULE_TRACE, "()\n"); + + /* We are no longer there... */ + irnet_server.running = 0; + +#ifdef CONFIG_PROC_FS + /* Remove our /proc file */ + remove_proc_entry("irnet", proc_irda); +#endif CONFIG_PROC_FS + + /* Remove our IrNET server from existence */ + irnet_destroy_server(); + + /* Remove all instances of IrNET socket still present */ + hashbin_delete(irnet_server.list, (FREE_FUNC) irda_irnet_destroy); + + DEXIT(MODULE_TRACE, "\n"); +} diff --git a/net/irda/irnet/irnet_irda.h b/net/irda/irnet/irnet_irda.h new file mode 100644 index 000000000..0e5e9a36f --- /dev/null +++ b/net/irda/irnet/irnet_irda.h @@ -0,0 +1,169 @@ +/* + * IrNET protocol module : Synchronous PPP over an IrDA socket. + * + * Jean II - HPL `00 - <jt@hpl.hp.com> + * + * This file contains all definitions and declarations necessary for the + * IRDA part of the IrNET module (dealing with IrTTP, IrIAS and co). + * This file is a private header, so other modules don't want to know + * what's in there... + */ + +#ifndef IRNET_IRDA_H +#define IRNET_IRDA_H + +/***************************** INCLUDES *****************************/ + +#include <linux/config.h> +#include "irnet.h" /* Module global include */ + +/************************ CONSTANTS & MACROS ************************/ + +/* + * Name of the service (socket name) used by IrNET + */ +/* IAS object name (or part of it) */ +#define IRNET_SERVICE_NAME "IrNetv1" +/* IAS attribute */ +#define IRNET_IAS_VALUE "IrDA:TinyTP:LsapSel" +/* LMP notify name for client (only for /proc/net/irda/irlmp) */ +#define IRNET_NOTIFY_NAME "IrNET socket" +/* LMP notify name for server (only for /proc/net/irda/irlmp) */ +#define IRNET_NOTIFY_NAME_SERV "IrNET server" + +/****************************** TYPES ******************************/ + +/* + * This is the main structure where we store all the data pertaining to + * the IrNET server (listen for connection requests) and the root + * of the IrNET socket list + */ +typedef struct irnet_root +{ + irnet_socket s; /* To pretend we are a client... */ + + /* Generic stuff */ + int magic; /* Paranoia */ + int running; /* Are we operational ? */ + + /* Link list of all IrNET instances opened */ + hashbin_t * list; + spinlock_t spinlock; /* Serialize access to the list */ + /* Note : the way hashbin has been designed is absolutely not + * reentrant, beware... So, we blindly protect all with spinlock */ + + /* Handle for the hint bit advertised in IrLMP */ + __u32 skey; + + /* Server socket part */ + struct ias_object * ias_obj; /* Our service name + lsap in IAS */ + +} irnet_root; + + +/**************************** PROTOTYPES ****************************/ + +/* ----------------------- CONTROL CHANNEL ----------------------- */ +static void + irnet_post_event(irnet_socket *, + irnet_event, + __u32, + char *); +/* ----------------------- IRDA SUBROUTINES ----------------------- */ +static inline int + irnet_open_tsap(irnet_socket *); +static int + irnet_find_lsap_sel(irnet_socket *); +static inline int + irnet_discover_daddr_and_lsap_sel(irnet_socket *); +static inline int + irnet_dname_to_daddr(irnet_socket *); +/* ------------------------ SERVER SOCKET ------------------------ */ +static inline int + irnet_daddr_to_dname(irnet_socket *); +static inline irnet_socket * + irnet_find_socket(irnet_socket *); +static inline int + irnet_connect_socket(irnet_socket *, + irnet_socket *, + struct qos_info *, + __u32, + __u8); +static inline void + irnet_disconnect_server(irnet_socket *, + struct sk_buff *); +static inline int + irnet_setup_server(void); +static inline void + irnet_destroy_server(void); +/* ---------------------- IRDA-TTP CALLBACKS ---------------------- */ +static int + irnet_data_indication(void *, /* instance */ + void *, /* sap */ + struct sk_buff *); +static void + irnet_disconnect_indication(void *, + void *, + LM_REASON, + struct sk_buff *); +static void + irnet_connect_confirm(void *, + void *, + struct qos_info *, + __u32, + __u8, + struct sk_buff *); +static void + irnet_flow_indication(void *, + void *, + LOCAL_FLOW); +static void + irnet_status_indication(void *, + LINK_STATUS, + LOCK_STATUS); +static void + irnet_connect_indication(void *, + void *, + struct qos_info *, + __u32, + __u8, + struct sk_buff *); +/* -------------------- IRDA-IAS/LMP CALLBACKS -------------------- */ +static void + irnet_getvalue_confirm(int, + __u16, + struct ias_value *, + void *); +#ifdef DISCOVERY_EVENTS +static void + irnet_discovery_indication(discovery_t *, + void *); +static void + irnet_expiry_indication(discovery_t *, + void *); +#endif +/* -------------------------- PROC ENTRY -------------------------- */ +#ifdef CONFIG_PROC_FS +static int + irnet_proc_read(char *, + char **, + off_t, + int); +#endif CONFIG_PROC_FS + +/**************************** VARIABLES ****************************/ + +/* + * The IrNET server. Listen to connection requests and co... + */ +static struct irnet_root irnet_server; + +/* Control channel stuff (note : extern) */ +struct irnet_ctrl_channel irnet_events; + +/* The /proc/net/irda directory, defined elsewhere... */ +#ifdef CONFIG_PROC_FS +extern struct proc_dir_entry *proc_irda; +#endif CONFIG_PROC_FS + +#endif IRNET_IRDA_H diff --git a/net/irda/irnet/irnet_ppp.c b/net/irda/irnet/irnet_ppp.c new file mode 100644 index 000000000..96a5c4114 --- /dev/null +++ b/net/irda/irnet/irnet_ppp.c @@ -0,0 +1,1052 @@ +/* + * IrNET protocol module : Synchronous PPP over an IrDA socket. + * + * Jean II - HPL `00 - <jt@hpl.hp.com> + * + * This file implement the PPP interface and /dev/irnet character device. + * The PPP interface hook to the ppp_generic module, handle all our + * relationship to the PPP code in the kernel (and by extension to pppd), + * and exchange PPP frames with this module (send/receive). + * The /dev/irnet device is used primarily for 2 functions : + * 1) as a stub for pppd (the ppp daemon), so that we can appropriately + * generate PPP sessions (we pretend we are a tty). + * 2) as a control channel (write commands, read events) + */ + +#include "irnet_ppp.h" /* Private header */ + +/************************* CONTROL CHANNEL *************************/ +/* + * When a pppd instance is not active on /dev/irnet, it acts as a control + * channel. + * Writting allow to set up the IrDA destination of the IrNET channel, + * and any application may be read events happening in IrNET... + */ + +/*------------------------------------------------------------------*/ +/* + * Write is used to send a command to configure a IrNET channel + * before it is open by pppd. The syntax is : "command argument" + * Currently there is only two defined commands : + * o name : set the requested IrDA nickname of the IrNET peer. + * o addr : set the requested IrDA address of the IrNET peer. + * Note : the code is crude, but effective... + */ +static inline ssize_t +irnet_ctrl_write(irnet_socket * ap, + const char * buf, + size_t count) +{ + char command[5 + NICKNAME_MAX_LEN + 2]; + int length = count; + + DENTER(CTRL_TRACE, "(ap=0x%X, count=%d)\n", (unsigned int) ap, count); + + /* Check for overflow... */ + DABORT(count > (5 + NICKNAME_MAX_LEN + 1), -ENOMEM, + CTRL_ERROR, "Too much data !!!\n"); + + /* Get the data in the driver */ + if(copy_from_user(command, buf, count)) + { + DERROR(CTRL_ERROR, "Invalid user space pointer.\n"); + return -EFAULT; + } + + /* Strip out '\n' if needed, and safe terminate the string */ + if(command[length - 1] == '\0') + length--; + if(command[length - 1] == '\n') + length--; + command[length] = '\0'; + DEBUG(CTRL_INFO, "Command received is ``%s'' (%d-%d).\n", + command, length, count); + + /* Check if we recognised the command */ + /* First command : name */ + if(!strncmp(command, "name", 4)) + { + /* Copy the name only if is included and not "any" */ + if((length > 5) && (strcmp(command + 5, "any"))) + { + /* Copy the name for later reuse (including the '/0') */ + memcpy(ap->rname, command + 5, length - 5 + 1); + } + else + ap->rname[0] = '\0'; + DEXIT(CTRL_TRACE, " - rname = ``%s''\n", ap->rname); + return(count); + } + + /* Second command : addr */ + if(!strncmp(command, "addr", 4)) + { + /* Copy the address only if is included and not "any" */ + if((length > 5) && (strcmp(command + 5, "any"))) + { + char * endp; + __u32 daddr; + + /* Convert argument to a number (last arg is the base) */ + daddr = simple_strtoul(command + 5, &endp, 16); + /* Has it worked ? (endp should be command + count) */ + DABORT(endp <= (command + 5), -EINVAL, + CTRL_ERROR, "Invalid address.\n"); + /* Save it */ + ap->raddr = daddr; + } + else + ap->raddr = DEV_ADDR_ANY; + DEXIT(CTRL_TRACE, " - raddr = %08x\n", ap->raddr); + return(count); + } + + /* Other possible command : connect N (number of retries) */ + + /* Failed... */ + DABORT(1, -EINVAL, CTRL_ERROR, "Not a recognised IrNET command.\n"); +} + +#ifdef INITIAL_DISCOVERY +/*------------------------------------------------------------------*/ +/* + * Function irnet_read_discovery_log (self) + * + * Read the content on the discovery log + * + * This function dump the current content of the discovery log + * at the startup of the event channel. + * Return 1 if written on the control channel... + * + * State of the ap->disco_XXX variables : + * at socket creation : disco_index = 0 ; disco_number = 0 + * while reading : disco_index = X ; disco_number = Y + * After reading : disco_index = Y ; disco_number = -1 + */ +static inline int +irnet_read_discovery_log(irnet_socket * ap, + char * event) +{ + int done_event = 0; + + DENTER(CTRL_TRACE, "(ap=0x%X, event=0x%X)\n", + (unsigned int) ap, (unsigned int) event); + + /* Test if we have some work to do or we have already finished */ + if(ap->disco_number == -1) + { + DEBUG(CTRL_INFO, "Already done\n"); + return 0; + } + + /* Test if it's the first time and therefore we need to get the log */ + if(ap->disco_index == 0) + { + __u16 mask = irlmp_service_to_hint(S_LAN); + + /* Ask IrLMP for the current discovery log */ + ap->discoveries = irlmp_get_discoveries(&ap->disco_number, mask); + /* Check if the we got some results */ + if(ap->discoveries == NULL) + ap->disco_number = -1; + DEBUG(CTRL_INFO, "Got the log (0x%X), size is %d\n", + (unsigned int) ap->discoveries, ap->disco_number); + } + + /* Check if we have more item to dump */ + if(ap->disco_index < ap->disco_number) + { + /* Write an event */ + sprintf(event, "Found %08x (%s)\n", + ap->discoveries[ap->disco_index].daddr, + ap->discoveries[ap->disco_index].info); + DEBUG(CTRL_INFO, "Writing discovery %d : %s\n", + ap->disco_index, ap->discoveries[ap->disco_index].info); + + /* We have an event */ + done_event = 1; + /* Next discovery */ + ap->disco_index++; + } + + /* Check if we have done the last item */ + if(ap->disco_index >= ap->disco_number) + { + /* No more items : remove the log and signal termination */ + DEBUG(CTRL_INFO, "Cleaning up log (0x%X)\n", + (unsigned int) ap->discoveries); + if(ap->discoveries != NULL) + { + /* Cleanup our copy of the discovery log */ + kfree(ap->discoveries); + ap->discoveries = NULL; + } + ap->disco_number = -1; + } + + return done_event; +} +#endif INITIAL_DISCOVERY + +/*------------------------------------------------------------------*/ +/* + * Read is used to get IrNET events + */ +static inline ssize_t +irnet_ctrl_read(irnet_socket * ap, + struct file * file, + char * buf, + size_t count) +{ + DECLARE_WAITQUEUE(wait, current); + char event[64]; /* Max event is 61 char */ + ssize_t ret = 0; + + DENTER(CTRL_TRACE, "(ap=0x%X, count=%d)\n", (unsigned int) ap, count); + + /* Check if we can write an event out in one go */ + DABORT(count < sizeof(event), -EOVERFLOW, CTRL_ERROR, "Buffer to small.\n"); + +#ifdef INITIAL_DISCOVERY + /* Check if we have read the log */ + if(irnet_read_discovery_log(ap, event)) + { + /* We have an event !!! Copy it to the user */ + if(copy_to_user(buf, event, strlen(event))) + { + DERROR(CTRL_ERROR, "Invalid user space pointer.\n"); + return -EFAULT; + } + + DEXIT(CTRL_TRACE, "\n"); + return(strlen(event)); + } +#endif INITIAL_DISCOVERY + + /* Put ourselves on the wait queue to be woken up */ + add_wait_queue(&irnet_events.rwait, &wait); + current->state = TASK_INTERRUPTIBLE; + for(;;) + { + /* If there is unread events */ + ret = 0; + if(ap->event_index != irnet_events.index) + break; + ret = -EAGAIN; + if(file->f_flags & O_NONBLOCK) + break; + ret = -ERESTARTSYS; + if(signal_pending(current)) + break; + /* Yield and wait to be woken up */ + schedule(); + } + current->state = TASK_RUNNING; + remove_wait_queue(&irnet_events.rwait, &wait); + + /* Did we got it ? */ + if(ret != 0) + { + /* No, return the error code */ + DEXIT(CTRL_TRACE, " - ret %d\n", ret); + return ret; + } + + /* Which event is it ? */ + switch(irnet_events.log[ap->event_index].event) + { + case IRNET_DISCOVER: + sprintf(event, "Discovered %08x (%s)\n", + irnet_events.log[ap->event_index].addr, + irnet_events.log[ap->event_index].name); + break; + case IRNET_EXPIRE: + sprintf(event, "Expired %08x (%s)\n", + irnet_events.log[ap->event_index].addr, + irnet_events.log[ap->event_index].name); + break; + case IRNET_CONNECT_TO: + sprintf(event, "Connected to %08x (%s) on ppp%d\n", + irnet_events.log[ap->event_index].addr, + irnet_events.log[ap->event_index].name, + irnet_events.log[ap->event_index].unit); + break; + case IRNET_CONNECT_FROM: + sprintf(event, "Connection from %08x (%s) on ppp%d\n", + irnet_events.log[ap->event_index].addr, + irnet_events.log[ap->event_index].name, + irnet_events.log[ap->event_index].unit); + break; + case IRNET_REQUEST_FROM: + sprintf(event, "Request from %08x (%s)\n", + irnet_events.log[ap->event_index].addr, + irnet_events.log[ap->event_index].name); + break; + case IRNET_NOANSWER_FROM: + sprintf(event, "No-answer from %08x (%s) on ppp%d\n", + irnet_events.log[ap->event_index].addr, + irnet_events.log[ap->event_index].name, + irnet_events.log[ap->event_index].unit); + break; + case IRNET_BLOCKED_LINK: + sprintf(event, "Blocked link with %08x (%s) on ppp%d\n", + irnet_events.log[ap->event_index].addr, + irnet_events.log[ap->event_index].name, + irnet_events.log[ap->event_index].unit); + break; + case IRNET_DISCONNECT_FROM: + sprintf(event, "Disconnection from %08x (%s) on ppp%d\n", + irnet_events.log[ap->event_index].addr, + irnet_events.log[ap->event_index].name, + irnet_events.log[ap->event_index].unit); + break; + case IRNET_DISCONNECT_TO: + sprintf(event, "Disconnected to %08x (%s)\n", + irnet_events.log[ap->event_index].addr, + irnet_events.log[ap->event_index].name); + break; + default: + sprintf(event, "Bug\n"); + } + /* Increment our event index */ + ap->event_index = (ap->event_index + 1) % IRNET_MAX_EVENTS; + + DEBUG(CTRL_INFO, "Event is :%s", event); + + /* Copy it to the user */ + if(copy_to_user(buf, event, strlen(event))) + { + DERROR(CTRL_ERROR, "Invalid user space pointer.\n"); + return -EFAULT; + } + + DEXIT(CTRL_TRACE, "\n"); + return(strlen(event)); +} + +/*------------------------------------------------------------------*/ +/* + * Poll : called when someone do a select on /dev/irnet. + * Just check if there are new events... + */ +static inline unsigned int +irnet_ctrl_poll(irnet_socket * ap, + struct file * file, + poll_table * wait) +{ + unsigned int mask; + + DENTER(CTRL_TRACE, "(ap=0x%X)\n", (unsigned int) ap); + + poll_wait(file, &irnet_events.rwait, wait); + mask = POLLOUT | POLLWRNORM; + /* If there is unread events */ + if(ap->event_index != irnet_events.index) + mask |= POLLIN | POLLRDNORM; +#ifdef INITIAL_DISCOVERY + if(ap->disco_number != -1) + mask |= POLLIN | POLLRDNORM; +#endif INITIAL_DISCOVERY + + DEXIT(CTRL_TRACE, " - mask=0x%X\n", mask); + return mask; +} + + +/*********************** FILESYSTEM CALLBACKS ***********************/ +/* + * Implement the usual open, read, write functions that will be called + * by the file system when some action is performed on /dev/irnet. + * Most of those actions will in fact be performed by "pppd" or + * the control channel, we just act as a redirector... + */ + +/*------------------------------------------------------------------*/ +/* + * Open : when somebody open /dev/irnet + * We basically create a new instance of irnet and initialise it. + */ +static int +dev_irnet_open(struct inode * inode, + struct file * file) +{ + struct irnet_socket * ap; + int err; + + DENTER(FS_TRACE, "(file=0x%X)\n", (unsigned int) file); + +#ifdef SECURE_DEVIRNET + /* This could (should?) be enforced by the permissions on /dev/irnet. */ + if(!capable(CAP_NET_ADMIN)) + return -EPERM; +#endif SECURE_DEVIRNET + + /* Allocate a private structure for this IrNET instance */ + ap = kmalloc(sizeof(*ap), GFP_KERNEL); + DABORT(ap == NULL, -ENOMEM, FS_ERROR, "Can't allocate struct irnet...\n"); + + MOD_INC_USE_COUNT; + + /* initialize the irnet structure */ + memset(ap, 0, sizeof(*ap)); + ap->file = file; + + /* PPP channel setup */ + ap->ppp_open = 0; + ap->chan.private = ap; + /* PPP parameters */ + ap->mru = PPP_MRU; + ap->xaccm[0] = ~0U; + ap->xaccm[3] = 0x60000000U; + ap->raccm = ~0U; + + /* Setup the IrDA part... */ + err = irda_irnet_create(ap); + if(err) + { + DERROR(FS_ERROR, "Can't setup IrDA link...\n"); + kfree(ap); + MOD_DEC_USE_COUNT; + return err; + } + + /* For the control channel */ + ap->event_index = irnet_events.index; /* Cancel all past events */ + + /* Put our stuff where we will be able to find it later */ + file->private_data = ap; + + DEXIT(FS_TRACE, " - ap=0x%X\n", (unsigned int) ap); + return 0; +} + + +/*------------------------------------------------------------------*/ +/* + * Close : when somebody close /dev/irnet + * Destroy the instance of /dev/irnet + */ +static int +dev_irnet_close(struct inode * inode, + struct file * file) +{ + irnet_socket * ap = (struct irnet_socket *) file->private_data; + + DENTER(FS_TRACE, "(file=0x%X, ap=0x%X)\n", + (unsigned int) file, (unsigned int) ap); + DABORT(ap == NULL, 0, FS_ERROR, "ap is NULL !!!\n"); + + /* Detach ourselves */ + file->private_data = NULL; + + /* Close IrDA stuff */ + irda_irnet_destroy(ap); + + /* Disconnect from the generic PPP layer if not already done */ + if(ap->ppp_open) + { + DERROR(FS_ERROR, "Channel still registered - deregistering !\n"); + ppp_unregister_channel(&ap->chan); + ap->ppp_open = 0; + } + + kfree(ap); + MOD_DEC_USE_COUNT; + + DEXIT(FS_TRACE, "\n"); + return 0; +} + +/*------------------------------------------------------------------*/ +/* + * Write does nothing. + * (we receive packet from ppp_generic through ppp_irnet_send()) + */ +static ssize_t +dev_irnet_write(struct file * file, + const char * buf, + size_t count, + loff_t * ppos) +{ + irnet_socket * ap = (struct irnet_socket *) file->private_data; + + DPASS(FS_TRACE, "(file=0x%X, ap=0x%X, count=%d)\n", + (unsigned int) file, (unsigned int) ap, count); + DABORT(ap == NULL, -ENXIO, FS_ERROR, "ap is NULL !!!\n"); + + /* If we are connected to ppp_generic, let it handle the job */ + if(ap->ppp_open) + return ppp_channel_write(&ap->chan, buf, count); + else + return irnet_ctrl_write(ap, buf, count); +} + +/*------------------------------------------------------------------*/ +/* + * Read doesn't do much either. + * (pppd poll us, but ultimately reads through /dev/ppp) + */ +static ssize_t +dev_irnet_read(struct file * file, + char * buf, + size_t count, + loff_t * ppos) +{ + irnet_socket * ap = (struct irnet_socket *) file->private_data; + + DPASS(FS_TRACE, "(file=0x%X, ap=0x%X, count=%d)\n", + (unsigned int) file, (unsigned int) ap, count); + DABORT(ap == NULL, -ENXIO, FS_ERROR, "ap is NULL !!!\n"); + + /* If we are connected to ppp_generic, let it handle the job */ + if(ap->ppp_open) + return ppp_channel_read(&ap->chan, file, buf, count); + else + return irnet_ctrl_read(ap, file, buf, count); +} + +/*------------------------------------------------------------------*/ +/* + * Poll : called when someone do a select on /dev/irnet + */ +static unsigned int +dev_irnet_poll(struct file * file, + poll_table * wait) +{ + irnet_socket * ap = (struct irnet_socket *) file->private_data; + unsigned int mask; + + DENTER(FS_TRACE, "(file=0x%X, ap=0x%X)\n", + (unsigned int) file, (unsigned int) ap); + + mask = POLLOUT | POLLWRNORM; + DABORT(ap == NULL, mask, FS_ERROR, "ap is NULL !!!\n"); + + /* If we are connected to ppp_generic, let it handle the job */ + if(ap->ppp_open) + mask |= ppp_channel_poll(&ap->chan, file, wait); + else + mask |= irnet_ctrl_poll(ap, file, wait); + + DEXIT(FS_TRACE, " - mask=0x%X\n", mask); + return(mask); +} + +/*------------------------------------------------------------------*/ +/* + * IOCtl : Called when someone does some ioctls on /dev/irnet + * This is the way pppd configure us and control us while the PPP + * instance is active. + */ +static int +dev_irnet_ioctl(struct inode * inode, + struct file * file, + unsigned int cmd, + unsigned long arg) +{ + irnet_socket * ap = (struct irnet_socket *) file->private_data; + int err; + int val; + + DENTER(FS_TRACE, "(file=0x%X, ap=0x%X, cmd=0x%X)\n", + (unsigned int) file, (unsigned int) ap, cmd); + + /* Basic checks... */ + DASSERT(ap != NULL, -ENXIO, PPP_ERROR, "ap is NULL...\n"); +#ifdef SECURE_DEVIRNET + if(!capable(CAP_NET_ADMIN)) + return -EPERM; +#endif SECURE_DEVIRNET + + err = -EFAULT; + switch(cmd) + { + /* Set discipline (should be N_SYNC_PPP or N_TTY) */ + case TIOCSETD: + if(get_user(val, (int *) arg)) + break; + if((val == N_SYNC_PPP) || (val == N_PPP)) + { + DEBUG(FS_INFO, "Entering PPP discipline.\n"); + /* PPP channel setup */ + ap->chan.private = ap; + ap->chan.ops = &irnet_ppp_ops; + ap->chan.mtu = PPP_MRU; + err = ppp_register_channel(&ap->chan); + if(err == 0) + { + /* Our ppp side is active */ + ap->ppp_open = 1; + + DEBUG(FS_INFO, "Trying to establish a connection.\n"); + /* Setup the IrDA link now - may fail... */ + irda_irnet_connect(ap); + } + else + DERROR(FS_ERROR, "Can't setup PPP channel...\n"); + } + else + { + /* In theory, should be N_TTY */ + DEBUG(FS_INFO, "Exiting PPP discipline.\n"); + /* Disconnect from the generic PPP layer */ + if(ap->ppp_open) + ppp_unregister_channel(&ap->chan); + else + DERROR(FS_ERROR, "Channel not registered !\n"); + ap->ppp_open = 0; + err = 0; + } + break; + + /* Attach this PPP instance to the PPP driver (set it active) */ + case PPPIOCATTACH: + case PPPIOCDETACH: + if(ap->ppp_open) + err = ppp_channel_ioctl(&ap->chan, cmd, arg); + else + DERROR(FS_ERROR, "Channel not registered !\n"); + break; + + /* Query PPP channel and unit number */ + case PPPIOCGCHAN: + if(!ap->ppp_open) + break; + if(put_user(ppp_channel_index(&ap->chan), (int *) arg)) + break; + DEBUG(FS_INFO, "Query channel.\n"); + err = 0; + break; + case PPPIOCGUNIT: + if(!ap->ppp_open) + break; + if(put_user(ppp_unit_number(&ap->chan), (int *) arg)) + break; + DEBUG(FS_INFO, "Query unit number.\n"); + err = 0; + break; + + /* All these ioctls can be passed both directly and from ppp_generic, + * so we just deal with them in one place... + */ + case PPPIOCGFLAGS: + case PPPIOCSFLAGS: + case PPPIOCGASYNCMAP: + case PPPIOCSASYNCMAP: + case PPPIOCGRASYNCMAP: + case PPPIOCSRASYNCMAP: + case PPPIOCGXASYNCMAP: + case PPPIOCSXASYNCMAP: + case PPPIOCGMRU: + case PPPIOCSMRU: + DEBUG(FS_INFO, "Standard PPP ioctl.\n"); + if(!capable(CAP_NET_ADMIN)) + err = -EPERM; + else + err = ppp_irnet_ioctl(&ap->chan, cmd, arg); + break; + + /* TTY IOCTLs : Pretend that we are a tty, to keep pppd happy */ + /* Get termios */ + case TCGETS: + DEBUG(FS_INFO, "Get termios.\n"); + if(kernel_termios_to_user_termios((struct termios *)arg, &ap->termios)) + break; + err = 0; + break; + /* Set termios */ + case TCSETSF: + DEBUG(FS_INFO, "Set termios.\n"); + if(user_termios_to_kernel_termios(&ap->termios, (struct termios *) arg)) + break; + err = 0; + break; + + /* Set DTR/RTS */ + case TIOCMBIS: + case TIOCMBIC: + /* Set exclusive/non-exclusive mode */ + case TIOCEXCL: + case TIOCNXCL: + DEBUG(FS_INFO, "TTY compatibility.\n"); + err = 0; + break; + + case TCGETA: + DEBUG(FS_INFO, "TCGETA\n"); + break; + + case TCFLSH: + DEBUG(FS_INFO, "TCFLSH\n"); + /* Note : this will flush buffers in PPP, so it *must* be done + * We should also worry that we don't accept junk here and that + * we get rid of our own buffers */ +#ifdef FLUSH_TO_PPP + ppp_output_wakeup(&ap->chan); +#endif FLUSH_TO_PPP + err = 0; + break; + + case FIONREAD: + DEBUG(FS_INFO, "FIONREAD\n"); + val = 0; + if(put_user(val, (int *) arg)) + break; + err = 0; + break; + + default: + DERROR(FS_ERROR, "Unsupported ioctl (0x%X)\n", cmd); + err = -ENOIOCTLCMD; + } + + DEXIT(FS_TRACE, " - err = 0x%X\n", err); + return err; +} + +/************************** PPP CALLBACKS **************************/ +/* + * This are the functions that the generic PPP driver in the kernel + * will call to communicate to us. + */ + +/*------------------------------------------------------------------*/ +/* + * Prepare the ppp frame for transmission over the IrDA socket. + * We make sure that the header space is enough, and we change ppp header + * according to flags passed by pppd. + * This is not a callback, but just a helper function used in ppp_irnet_send() + */ +static inline struct sk_buff * +irnet_prepare_skb(irnet_socket * ap, + struct sk_buff * skb) +{ + unsigned char * data; + int proto; /* PPP protocol */ + int islcp; /* Protocol == LCP */ + int needaddr; /* Need PPP address */ + + DENTER(PPP_TRACE, "(ap=0x%X, skb=0x%X)\n", + (unsigned int) ap, (unsigned int) skb); + + /* Extract PPP protocol from the frame */ + data = skb->data; + proto = (data[0] << 8) + data[1]; + + /* LCP packets with codes between 1 (configure-request) + * and 7 (code-reject) must be sent as though no options + * have been negotiated. */ + islcp = (proto == PPP_LCP) && (1 <= data[2]) && (data[2] <= 7); + + /* compress protocol field if option enabled */ + if((data[0] == 0) && (ap->flags & SC_COMP_PROT) && (!islcp)) + skb_pull(skb,1); + + /* Check if we need address/control fields */ + needaddr = 2*((ap->flags & SC_COMP_AC) == 0 || islcp); + + /* Is the skb headroom large enough to contain all IrDA-headers? */ + if((skb_headroom(skb) < (ap->max_header_size + needaddr)) || + (skb_shared(skb))) + { + struct sk_buff * new_skb; + + DEBUG(PPP_INFO, "Reallocating skb\n"); + + /* Create a new skb */ + new_skb = skb_realloc_headroom(skb, ap->max_header_size + needaddr); + + /* We have to free the original skb anyway */ + dev_kfree_skb(skb); + + /* Did the realloc succeed ? */ + DABORT(new_skb == NULL, NULL, PPP_ERROR, "Could not realloc skb\n"); + + /* Use the new skb instead */ + skb = new_skb; + } + + /* prepend address/control fields if necessary */ + if(needaddr) + { + skb_push(skb,2); + skb->data[0] = PPP_ALLSTATIONS; + skb->data[1] = PPP_UI; + } + + DEXIT(PPP_TRACE, "\n"); + + return skb; +} + +/*------------------------------------------------------------------*/ +/* + * Send a packet to the peer over the IrTTP connection. + * Returns 1 iff the packet was accepted. + * Returns 0 iff packet was not consumed. + * If the packet was not accepted, we will call ppp_output_wakeup + * at some later time to reactivate flow control in ppp_generic. + */ +static int +ppp_irnet_send(struct ppp_channel * chan, + struct sk_buff * skb) +{ + irnet_socket * self = (struct irnet_socket *) chan->private; + int ret; + + DENTER(PPP_TRACE, "(channel=0x%X, ap/self=0x%X)\n", + (unsigned int) chan, (unsigned int) self); + + /* Check if things are somewhat valid... */ + DASSERT(self != NULL, 0, PPP_ERROR, "Self is NULL !!!\n"); + + /* Check if we are connected */ + if(self->ttp_open == 0) + { +#ifdef CONNECT_IN_SEND + /* Let's try to connect one more time... */ + /* Note : we won't connect fully yet, but we should be ready for + * next packet... */ + /* Note : we can't do that, we need to have a process context to + * go through interruptible_sleep_on() in irnet_find_lsap_sel() + * We need to find another way... */ + irda_irnet_connect(self); +#endif CONNECT_IN_SEND + + DEBUG(PPP_INFO, "IrTTP not ready ! (%d-0x%X)\n", + self->ttp_open, (unsigned int) self->tsap); + + /* Note : we can either drop the packet or block the packet. + * + * Blocking the packet allow us a better connection time, + * because by calling ppp_output_wakeup() we can have + * ppp_generic resending the LCP request immediately to us, + * rather than waiting for one of pppd periodic transmission of + * LCP request. + * + * On the other hand, if we block all packet, all those periodic + * transmissions of pppd accumulate in ppp_generic, creating a + * backlog of LCP request. When we eventually connect later on, + * we have to transmit all this backlog before we can connect + * proper (if we don't timeout before). + * + * The current strategy is as follow : + * While we are attempting to connect, we block packets to get + * a better connection time. + * If we fail to connect, we drain the queue and start dropping packets + */ +#ifdef BLOCK_WHEN_CONNECT + /* If we are attempting to connect */ + if(self->tsap) + { + /* Blocking packet, ppp_generic will retry later */ + return 0; + } +#endif BLOCK_WHEN_CONNECT + + /* Dropping packet, pppd will retry later */ + dev_kfree_skb(skb); + return 1; + } + + /* Check if the queue can accept any packet, otherwise block */ + if(self->tx_flow != FLOW_START) + DRETURN(0, PPP_INFO, "IrTTP queue full (%d skbs)...\n", + skb_queue_len(&self->tsap->tx_queue)); + + /* Prepare ppp frame for transmission */ + skb = irnet_prepare_skb(self, skb); + DABORT(skb == NULL, 1, PPP_ERROR, "Prepare skb for Tx failed.\n"); + + /* Send the packet to IrTTP */ + ret = irttp_data_request(self->tsap, skb); + if(ret < 0) + { + /* + * > IrTTPs tx queue is full, so we just have to + * > drop the frame! You might think that we should + * > just return -1 and don't deallocate the frame, + * > but that is dangerous since it's possible that + * > we have replaced the original skb with a new + * > one with larger headroom, and that would really + * > confuse do_dev_queue_xmit() in dev.c! I have + * > tried :-) DB + * Correction : we verify the flow control above (self->tx_flow), + * so we come here only if IrTTP doesn't like the packet (empty, + * too large, IrTTP not connected). In those rare cases, it's ok + * to drop it, we don't want to see it here again... + * Jean II + */ + DERROR(PPP_ERROR, "IrTTP doesn't like this packet !!! (0x%X)\n", ret); + dev_kfree_skb(skb); + } + + DEXIT(PPP_TRACE, "\n"); + return 1; /* Packet has been consumed */ +} + +/*------------------------------------------------------------------*/ +/* + * Take care of the ioctls that ppp_generic doesn't want to deal with... + * Note : we are also called from dev_irnet_ioctl(). + */ +static int +ppp_irnet_ioctl(struct ppp_channel * chan, + unsigned int cmd, + unsigned long arg) +{ + irnet_socket * ap = (struct irnet_socket *) chan->private; + int err; + int val; + u32 accm[8]; + + DENTER(PPP_TRACE, "(channel=0x%X, ap=0x%X, cmd=0x%X)\n", + (unsigned int) chan, (unsigned int) ap, cmd); + + /* Basic checks... */ + DASSERT(ap != NULL, -ENXIO, PPP_ERROR, "ap is NULL...\n"); + + err = -EFAULT; + switch(cmd) + { + /* PPP flags */ + case PPPIOCGFLAGS: + val = ap->flags | ap->rbits; + if(put_user(val, (int *) arg)) + break; + err = 0; + break; + case PPPIOCSFLAGS: + if(get_user(val, (int *) arg)) + break; + ap->flags = val & ~SC_RCV_BITS; + ap->rbits = val & SC_RCV_BITS; + err = 0; + break; + + /* Async map stuff - all dummy to please pppd */ + case PPPIOCGASYNCMAP: + if(put_user(ap->xaccm[0], (u32 *) arg)) + break; + err = 0; + break; + case PPPIOCSASYNCMAP: + if(get_user(ap->xaccm[0], (u32 *) arg)) + break; + err = 0; + break; + case PPPIOCGRASYNCMAP: + if(put_user(ap->raccm, (u32 *) arg)) + break; + err = 0; + break; + case PPPIOCSRASYNCMAP: + if(get_user(ap->raccm, (u32 *) arg)) + break; + err = 0; + break; + case PPPIOCGXASYNCMAP: + if(copy_to_user((void *) arg, ap->xaccm, sizeof(ap->xaccm))) + break; + err = 0; + break; + case PPPIOCSXASYNCMAP: + if(copy_from_user(accm, (void *) arg, sizeof(accm))) + break; + accm[2] &= ~0x40000000U; /* can't escape 0x5e */ + accm[3] |= 0x60000000U; /* must escape 0x7d, 0x7e */ + memcpy(ap->xaccm, accm, sizeof(ap->xaccm)); + err = 0; + break; + + /* Max PPP frame size */ + case PPPIOCGMRU: + if(put_user(ap->mru, (int *) arg)) + break; + err = 0; + break; + case PPPIOCSMRU: + if(get_user(val, (int *) arg)) + break; + if(val < PPP_MRU) + val = PPP_MRU; + ap->mru = val; + err = 0; + break; + + default: + DEBUG(PPP_INFO, "Unsupported ioctl (0x%X)\n", cmd); + err = -ENOIOCTLCMD; + } + + DEXIT(PPP_TRACE, " - err = 0x%X\n", err); + return err; +} + +/************************** INITIALISATION **************************/ +/* + * Module initialisation and all that jazz... + */ + +/*------------------------------------------------------------------*/ +/* + * Hook our device callbacks in the filesystem, to connect our code + * to /dev/irnet + */ +int +ppp_irnet_init(void) +{ + int err = 0; + + DENTER(MODULE_TRACE, "()\n"); + + /* Allocate ourselves as a minor in the misc range */ + err = misc_register(&irnet_misc_device); + + DEXIT(MODULE_TRACE, "\n"); + return err; +} + +/*------------------------------------------------------------------*/ +/* + * Cleanup at exit... + */ +void +ppp_irnet_cleanup(void) +{ + DENTER(MODULE_TRACE, "()\n"); + + /* De-allocate /dev/irnet minor in misc range */ + misc_deregister(&irnet_misc_device); + + DEXIT(MODULE_TRACE, "\n"); +} + +#ifdef MODULE +/*------------------------------------------------------------------*/ +/* + * Module main entry point + */ +int +init_module(void) +{ + int err; + + /* Initialise both parts... */ + err = irda_irnet_init(); + if(!err) + err = ppp_irnet_init(); + return err; +} + +/*------------------------------------------------------------------*/ +/* + * Module exit + */ +void +cleanup_module(void) +{ + irda_irnet_cleanup(); + return ppp_irnet_cleanup(); +} +#endif /* MODULE */ diff --git a/net/irda/irnet/irnet_ppp.h b/net/irda/irnet/irnet_ppp.h new file mode 100644 index 000000000..8e8365374 --- /dev/null +++ b/net/irda/irnet/irnet_ppp.h @@ -0,0 +1,130 @@ +/* + * IrNET protocol module : Synchronous PPP over an IrDA socket. + * + * Jean II - HPL `00 - <jt@hpl.hp.com> + * + * This file contains all definitions and declarations necessary for the + * PPP part of the IrNET module. + * This file is a private header, so other modules don't want to know + * what's in there... + */ + +#ifndef IRNET_PPP_H +#define IRNET_PPP_H + +/***************************** INCLUDES *****************************/ + +#include "irnet.h" /* Module global include */ + +/************************ CONSTANTS & MACROS ************************/ + +/* /dev/irnet file constants */ +#define IRNET_MAJOR 10 /* Misc range */ +#define IRNET_MINOR 187 /* Official allocation */ + +#ifdef LINKNAME_IOCTL +/* Compatibility with old ppp drivers + * Should be defined in <linux/if_ppp.h> */ +#ifndef PPPIOCSLINKNAME +#define PPPIOCSLINKNAME _IOW('t', 74, struct ppp_option_data) +#endif PPPIOCSLINKNAME +#endif LINKNAME_IOCTL + +/* PPP hardcore stuff */ + +/* Bits in rbits (PPP flags in irnet struct) */ +#define SC_RCV_BITS (SC_RCV_B7_1|SC_RCV_B7_0|SC_RCV_ODDP|SC_RCV_EVNP) + +/* Bit numbers in busy */ +#define XMIT_BUSY 0 +#define RECV_BUSY 1 +#define XMIT_WAKEUP 2 +#define XMIT_FULL 3 + +/* Queue management */ +#define PPPSYNC_MAX_RQLEN 32 /* arbitrary */ + +/****************************** TYPES ******************************/ + + +/**************************** PROTOTYPES ****************************/ + +/* ----------------------- CONTROL CHANNEL ----------------------- */ +static inline ssize_t + irnet_ctrl_write(irnet_socket *, + const char *, + size_t); +static inline ssize_t + irnet_ctrl_read(irnet_socket *, + struct file *, + char *, + size_t); +static inline unsigned int + irnet_ctrl_poll(irnet_socket *, + struct file *, + poll_table *); +/* ----------------------- CHARACTER DEVICE ----------------------- */ +static int + dev_irnet_open(struct inode *, /* fs callback : open */ + struct file *), + dev_irnet_close(struct inode *, + struct file *); +static ssize_t + dev_irnet_write(struct file *, + const char *, + size_t, + loff_t *), + dev_irnet_read(struct file *, + char *, + size_t, + loff_t *); +static unsigned int + dev_irnet_poll(struct file *, + poll_table *); +static int + dev_irnet_ioctl(struct inode *, + struct file *, + unsigned int, + unsigned long); +/* ------------------------ PPP INTERFACE ------------------------ */ +static inline struct sk_buff * + irnet_prepare_skb(irnet_socket *, + struct sk_buff *); +static int + ppp_irnet_send(struct ppp_channel *, + struct sk_buff *); +static int + ppp_irnet_ioctl(struct ppp_channel *, + unsigned int, + unsigned long); + +/**************************** VARIABLES ****************************/ + +/* Filesystem callbacks (to call us) */ +static struct file_operations irnet_device_fops = +{ + read: dev_irnet_read, + write: dev_irnet_write, + poll: dev_irnet_poll, + ioctl: dev_irnet_ioctl, + open: dev_irnet_open, + release: dev_irnet_close + /* Also : llseek, readdir, mmap, flush, fsync, fasync, lock, readv, writev */ +}; + +/* Structure so that the misc major (drivers/char/misc.c) take care of us... */ +static struct miscdevice irnet_misc_device = +{ + IRNET_MINOR, + "irnet", + &irnet_device_fops +}; + +/* Generic PPP callbacks (to call us) */ +struct ppp_channel_ops irnet_ppp_ops = +{ + ppp_irnet_send, + ppp_irnet_ioctl +}; + +#endif IRNET_PPP_H diff --git a/net/irda/irqueue.c b/net/irda/irqueue.c index 1d26d8f19..d2e4da720 100644 --- a/net/irda/irqueue.c +++ b/net/irda/irqueue.c @@ -36,7 +36,7 @@ #include <net/irda/irqueue.h> #include <net/irda/irmod.h> -static queue_t *dequeue_general( queue_t **queue, queue_t* element); +static irda_queue_t *dequeue_general( irda_queue_t **queue, irda_queue_t* element); static __u32 hash( char* name); /* @@ -79,7 +79,7 @@ hashbin_t *hashbin_new(int type) */ int hashbin_clear( hashbin_t* hashbin, FREE_FUNC free_func) { - queue_t* queue; + irda_queue_t* queue; int i; ASSERT(hashbin != NULL, return -1;); @@ -89,12 +89,12 @@ int hashbin_clear( hashbin_t* hashbin, FREE_FUNC free_func) * Free the entries in the hashbin */ for (i = 0; i < HASHBIN_SIZE; i ++ ) { - queue = dequeue_first( (queue_t**) &hashbin->hb_queue[i]); + queue = dequeue_first( (irda_queue_t**) &hashbin->hb_queue[i]); while (queue) { if (free_func) (*free_func)(queue); queue = dequeue_first( - (queue_t**) &hashbin->hb_queue[i]); + (irda_queue_t**) &hashbin->hb_queue[i]); } } hashbin->hb_size = 0; @@ -112,7 +112,7 @@ int hashbin_clear( hashbin_t* hashbin, FREE_FUNC free_func) */ int hashbin_delete( hashbin_t* hashbin, FREE_FUNC free_func) { - queue_t* queue; + irda_queue_t* queue; int i; ASSERT(hashbin != NULL, return -1;); @@ -123,12 +123,12 @@ int hashbin_delete( hashbin_t* hashbin, FREE_FUNC free_func) * it has been shown to work */ for (i = 0; i < HASHBIN_SIZE; i ++ ) { - queue = dequeue_first((queue_t**) &hashbin->hb_queue[i]); + queue = dequeue_first((irda_queue_t**) &hashbin->hb_queue[i]); while (queue ) { if (free_func) (*free_func)(queue); queue = dequeue_first( - (queue_t**) &hashbin->hb_queue[i]); + (irda_queue_t**) &hashbin->hb_queue[i]); } } @@ -210,7 +210,7 @@ void hashbin_unlock(hashbin_t* hashbin, __u32 hashv, char* name, * Insert an entry into the hashbin * */ -void hashbin_insert(hashbin_t* hashbin, queue_t* entry, __u32 hashv, char* name) +void hashbin_insert(hashbin_t* hashbin, irda_queue_t* entry, __u32 hashv, char* name) { unsigned long flags = 0; int bin; @@ -250,7 +250,7 @@ void hashbin_insert(hashbin_t* hashbin, queue_t* entry, __u32 hashv, char* name) */ if ( hashbin->hb_type & HB_SORTED) { } else { - enqueue_first( (queue_t**) &hashbin->hb_queue[ bin ], + enqueue_first( (irda_queue_t**) &hashbin->hb_queue[ bin ], entry); } hashbin->hb_size++; @@ -275,7 +275,7 @@ void* hashbin_find( hashbin_t* hashbin, __u32 hashv, char* name ) { int bin, found = FALSE; unsigned long flags = 0; - queue_t* entry; + irda_queue_t* entry; IRDA_DEBUG( 4, "hashbin_find()\n"); @@ -342,7 +342,7 @@ void* hashbin_find( hashbin_t* hashbin, __u32 hashv, char* name ) void *hashbin_remove_first( hashbin_t *hashbin) { unsigned long flags; - queue_t *entry = NULL; + irda_queue_t *entry = NULL; save_flags(flags); cli(); @@ -367,7 +367,7 @@ void* hashbin_remove( hashbin_t* hashbin, __u32 hashv, char* name) { int bin, found = FALSE; unsigned long flags = 0; - queue_t* entry; + irda_queue_t* entry; IRDA_DEBUG( 4, __FUNCTION__ "()\n"); @@ -381,6 +381,7 @@ void* hashbin_remove( hashbin_t* hashbin, __u32 hashv, char* name) hashv = hash( name ); bin = GET_HASHBIN( hashv ); + /* Synchronize */ if ( hashbin->hb_type & HB_GLOBAL ) { spin_lock_irqsave( &hashbin->hb_mutex[ bin ], flags); @@ -421,8 +422,8 @@ void* hashbin_remove( hashbin_t* hashbin, __u32 hashv, char* name) * If entry was found, dequeue it */ if ( found ) { - dequeue_general( (queue_t**) &hashbin->hb_queue[ bin ], - (queue_t*) entry ); + dequeue_general( (irda_queue_t**) &hashbin->hb_queue[ bin ], + (irda_queue_t*) entry ); hashbin->hb_size--; /* @@ -450,6 +451,75 @@ void* hashbin_remove( hashbin_t* hashbin, __u32 hashv, char* name) } +/* + * Function hashbin_remove (hashbin, hashv, name) + * + * Remove entry with the given name + * + * In some cases, the user of hashbin can't guarantee the unicity + * of either the hashv or name. + * In those cases, using the above function is guaranteed to cause troubles, + * so we use this one instead... + * And by the way, it's also faster, because we skip the search phase ;-) + */ +void* hashbin_remove_this( hashbin_t* hashbin, irda_queue_t* entry) +{ + unsigned long flags = 0; + int bin; + __u32 hashv; + + IRDA_DEBUG( 4, __FUNCTION__ "()\n"); + + ASSERT( hashbin != NULL, return NULL;); + ASSERT( hashbin->magic == HB_MAGIC, return NULL;); + ASSERT( entry != NULL, return NULL;); + + /* Check if valid and not already removed... */ + if((entry->q_next == NULL) || (entry->q_prev == NULL)) + return NULL; + + /* + * Locate hashbin + */ + hashv = entry->q_hash; + bin = GET_HASHBIN( hashv ); + + /* Synchronize */ + if ( hashbin->hb_type & HB_GLOBAL ) { + spin_lock_irqsave( &hashbin->hb_mutex[ bin ], flags); + + } else if ( hashbin->hb_type & HB_LOCAL ) { + save_flags(flags); + cli(); + } /* Default is no-lock */ + + /* + * Dequeue the entry... + */ + dequeue_general( (irda_queue_t**) &hashbin->hb_queue[ bin ], + (irda_queue_t*) entry ); + hashbin->hb_size--; + entry->q_next = NULL; + entry->q_prev = NULL; + + /* + * Check if this item is the currently selected item, and in + * that case we must reset hb_current + */ + if ( entry == hashbin->hb_current) + hashbin->hb_current = NULL; + + /* Release lock */ + if ( hashbin->hb_type & HB_GLOBAL) { + spin_unlock_irq( &hashbin->hb_mutex[ bin]); + + } else if ( hashbin->hb_type & HB_LOCAL) { + restore_flags( flags); + } + + return entry; +} + /* * Function hashbin_get_first (hashbin) * @@ -457,9 +527,9 @@ void* hashbin_remove( hashbin_t* hashbin, __u32 hashv, char* name) * called before any calls to hashbin_get_next()! * */ -queue_t *hashbin_get_first( hashbin_t* hashbin) +irda_queue_t *hashbin_get_first( hashbin_t* hashbin) { - queue_t *entry; + irda_queue_t *entry; int i; ASSERT( hashbin != NULL, return NULL;); @@ -489,9 +559,9 @@ queue_t *hashbin_get_first( hashbin_t* hashbin) * NULL when all items have been traversed * */ -queue_t *hashbin_get_next( hashbin_t *hashbin) +irda_queue_t *hashbin_get_next( hashbin_t *hashbin) { - queue_t* entry; + irda_queue_t* entry; int bin; int i; @@ -542,7 +612,7 @@ queue_t *hashbin_get_next( hashbin_t *hashbin) * Insert item into end of queue. * */ -static void __enqueue_last( queue_t **queue, queue_t* element) +static void __enqueue_last( irda_queue_t **queue, irda_queue_t* element) { IRDA_DEBUG( 4, __FUNCTION__ "()\n"); @@ -566,7 +636,7 @@ static void __enqueue_last( queue_t **queue, queue_t* element) } } -inline void enqueue_last( queue_t **queue, queue_t* element) +inline void enqueue_last( irda_queue_t **queue, irda_queue_t* element) { unsigned long flags; @@ -584,7 +654,7 @@ inline void enqueue_last( queue_t **queue, queue_t* element) * Insert item first in queue. * */ -void enqueue_first(queue_t **queue, queue_t* element) +void enqueue_first(irda_queue_t **queue, irda_queue_t* element) { IRDA_DEBUG( 4, __FUNCTION__ "()\n"); @@ -616,9 +686,9 @@ void enqueue_first(queue_t **queue, queue_t* element) * Insert a queue (list) into the start of the first queue * */ -void enqueue_queue( queue_t** queue, queue_t** list ) +void enqueue_queue( irda_queue_t** queue, irda_queue_t** list ) { - queue_t* tmp; + irda_queue_t* tmp; /* * Check if queue is empty @@ -643,7 +713,7 @@ void enqueue_queue( queue_t** queue, queue_t** list ) * */ #if 0 -static void enqueue_second(queue_t **queue, queue_t* element) +static void enqueue_second(irda_queue_t **queue, irda_queue_t* element) { IRDA_DEBUG( 0, "enqueue_second()\n"); @@ -674,9 +744,9 @@ static void enqueue_second(queue_t **queue, queue_t* element) * Remove first entry in queue * */ -queue_t *dequeue_first(queue_t **queue) +irda_queue_t *dequeue_first(irda_queue_t **queue) { - queue_t *ret; + irda_queue_t *ret; IRDA_DEBUG( 4, "dequeue_first()\n"); @@ -715,9 +785,9 @@ queue_t *dequeue_first(queue_t **queue) * * */ -static queue_t *dequeue_general(queue_t **queue, queue_t* element) +static irda_queue_t *dequeue_general(irda_queue_t **queue, irda_queue_t* element) { - queue_t *ret; + irda_queue_t *ret; IRDA_DEBUG( 4, "dequeue_general()\n"); diff --git a/net/irda/irmod.c b/net/irda/irsyms.c index 39257f87a..117f23ecc 100644 --- a/net/irda/irmod.c +++ b/net/irda/irsyms.c @@ -1,8 +1,8 @@ /********************************************************************* * - * Filename: irmod.c - * Version: 0.8 - * Description: IrDA module code and some other stuff + * Filename: irsyms.c + * Version: 0.9 + * Description: IrDA module symbols * Status: Experimental. * Author: Dag Brattli <dagb@cs.uit.no> * Created at: Mon Dec 15 13:55:39 1997 @@ -23,7 +23,7 @@ ********************************************************************/ #include <linux/config.h> -#include <linux/module.h> +#include <linux/module.h> #include <linux/init.h> #include <linux/poll.h> @@ -49,18 +49,12 @@ extern struct proc_dir_entry *proc_irda; -struct irda_cb irda; /* One global instance */ - -#ifdef CONFIG_IRDA_DEBUG -__u32 irda_debug = IRDA_DEBUG_LEVEL; -#endif - extern void irda_proc_register(void); extern void irda_proc_unregister(void); extern int irda_sysctl_register(void); extern void irda_sysctl_unregister(void); -extern void irda_proto_init(struct net_proto *pro); +extern int irda_proto_init(void); extern void irda_proto_cleanup(void); extern int irda_device_init(void); @@ -78,26 +72,6 @@ extern irda_deflate_init(); #endif /* CONFIG_IRDA_DEFLATE */ #endif /* CONFIG_IRDA_COMPRESSION */ -static int irda_open(struct inode * inode, struct file *file); -static int irda_ioctl(struct inode *inode, struct file *filp, - unsigned int cmd, unsigned long arg); -static int irda_close(struct inode *inode, struct file *file); -static ssize_t irda_read(struct file *file, char *buffer, size_t count, - loff_t *noidea); -static ssize_t irda_write(struct file *file, const char *buffer, - size_t count, loff_t *noidea); -static u_int irda_poll(struct file *file, poll_table *wait); - -static struct file_operations irda_fops = { - owner: THIS_MODULE, - read: irda_read, - write: irda_write, - poll: irda_poll, - ioctl: irda_ioctl, - open: irda_open, - release: irda_close, -}; - /* IrTTP */ EXPORT_SYMBOL(irttp_open_tsap); EXPORT_SYMBOL(irttp_close_tsap); @@ -114,7 +88,6 @@ EXPORT_SYMBOL(irttp_dup); EXPORT_SYMBOL(irda_debug); #endif EXPORT_SYMBOL(irda_notify_init); -EXPORT_SYMBOL(irmanager_notify); EXPORT_SYMBOL(irda_lock); #ifdef CONFIG_PROC_FS EXPORT_SYMBOL(proc_irda); @@ -145,6 +118,8 @@ EXPORT_SYMBOL(irias_new_octseq_value); /* IrLMP */ EXPORT_SYMBOL(irlmp_discovery_request); +EXPORT_SYMBOL(irlmp_get_discoveries); +EXPORT_SYMBOL(sysctl_discovery_timeout); EXPORT_SYMBOL(irlmp_register_client); EXPORT_SYMBOL(irlmp_unregister_client); EXPORT_SYMBOL(irlmp_update_client); @@ -168,6 +143,7 @@ EXPORT_SYMBOL(hashbin_new); EXPORT_SYMBOL(hashbin_insert); EXPORT_SYMBOL(hashbin_delete); EXPORT_SYMBOL(hashbin_remove); +EXPORT_SYMBOL(hashbin_remove_this); EXPORT_SYMBOL(hashbin_get_next); EXPORT_SYMBOL(hashbin_get_first); @@ -208,14 +184,11 @@ EXPORT_SYMBOL(irtty_set_packet_mode); int __init irda_init(void) { - MESSAGE("IrDA (tm) Protocols for Linux-2.3 (Dag Brattli)\n"); - + IRDA_DEBUG(0, __FUNCTION__ "()\n"); + irlmp_init(); irlap_init(); -#ifdef MODULE - irda_device_init(); /* Called by init/main.c when non-modular */ -#endif iriap_init(); irttp_init(); @@ -225,17 +198,6 @@ int __init irda_init(void) #ifdef CONFIG_SYSCTL irda_sysctl_register(); #endif - init_waitqueue_head(&irda.wait_queue); - irda.dev.minor = MISC_DYNAMIC_MINOR; - irda.dev.name = "irda"; - irda.dev.fops = &irda_fops; - - misc_register(&irda.dev); - - irda.in_use = FALSE; - - init_waitqueue_head(&irda.wait_queue); - /* * Initialize modules that got compiled into the kernel */ @@ -256,11 +218,8 @@ int __init irda_init(void) return 0; } -#ifdef MODULE -void irda_cleanup(void) +static void __exit irda_cleanup(void) { - misc_deregister(&irda.dev); - #ifdef CONFIG_SYSCTL irda_sysctl_unregister(); #endif @@ -279,7 +238,6 @@ void irda_cleanup(void) /* Remove middle layer */ irlmp_cleanup(); } -#endif /* MODULE */ /* * Function irda_unlock (lock) @@ -310,241 +268,8 @@ void irda_notify_init(notify_t *notify) notify->connect_indication = NULL; notify->disconnect_indication = NULL; notify->flow_indication = NULL; + notify->status_indication = NULL; notify->instance = NULL; strncpy(notify->name, "Unknown", NOTIFY_MAX_NAME); } -/* - * Function irda_execute_as_process (self, callback, param) - * - * If a layer needs to have a function executed with a process context, - * then it can register the function here, and the function will then - * be executed as fast as possible. - * - */ -void irda_execute_as_process( void *self, TODO_CALLBACK callback, __u32 param) -{ - struct irda_todo *new; - struct irmanager_event event; - - /* Make sure irmanager is running */ - if (!irda.in_use) { - return; - } - - /* Make new todo event */ - new = (struct irda_todo *) kmalloc( sizeof(struct irda_todo), - GFP_ATOMIC); - if ( new == NULL) { - return; - } - memset( new, 0, sizeof( struct irda_todo)); - - new->self = self; - new->callback = callback; - new->param = param; - - /* Queue todo */ - enqueue_last(&irda.todo_queue, (queue_t *) new); - - event.event = EVENT_NEED_PROCESS_CONTEXT; - - /* Notify the user space manager */ - irmanager_notify(&event); -} - -/* - * Function irmanger_notify (event) - * - * Send an event to the user space manager - * - */ -void irmanager_notify( struct irmanager_event *event) -{ - struct irda_event *new; - - IRDA_DEBUG(4, __FUNCTION__ "()\n"); - - /* Make sure irmanager is running */ - if (!irda.in_use) { - return; - } - - /* Make new IrDA Event */ - new = (struct irda_event *) kmalloc( sizeof(struct irda_event), - GFP_ATOMIC); - if ( new == NULL) { - return; - } - memset(new, 0, sizeof( struct irda_event)); - new->event = *event; - - /* Queue event */ - enqueue_last(&irda.event_queue, (queue_t *) new); - - /* Wake up irmanager sleeping on read */ - wake_up_interruptible(&irda.wait_queue); -} - -static int irda_open( struct inode * inode, struct file *file) -{ - IRDA_DEBUG( 4, __FUNCTION__ "()\n"); - - lock_kernel(); - if (irda.in_use) { - unlock_kernel(); - IRDA_DEBUG(0, __FUNCTION__ - "(), irmanager is already running!\n"); - return -1; - } - irda.in_use = TRUE; - unlock_kernel(); - - return 0; -} - -/* - * Function irda_ioctl (inode, filp, cmd, arg) - * - * Ioctl, used by irmanager to ... - * - */ -static int irda_ioctl(struct inode *inode, struct file *filp, - unsigned int cmd, unsigned long arg) -{ - struct irda_todo *todo; - int err = 0; - int size = _IOC_SIZE(cmd); - - IRDA_DEBUG(4, __FUNCTION__ "()\n"); - - if (_IOC_DIR(cmd) & _IOC_READ) - err = verify_area( VERIFY_WRITE, (void *) arg, size); - else if (_IOC_DIR(cmd) & _IOC_WRITE) - err = verify_area( VERIFY_READ, (void *) arg, size); - if (err) - return err; - - switch (cmd) { - case IRMGR_IOCTNPC: - /* Got process context! */ - IRDA_DEBUG(4, __FUNCTION__ "(), got process context!\n"); - - while ((todo = (struct irda_todo *) dequeue_first( - &irda.todo_queue)) != NULL) - { - todo->callback(todo->self, todo->param); - - kfree(todo); - } - break; - - default: - return -ENOIOCTLCMD; - } - - return 0; -} - -static int irda_close(struct inode *inode, struct file *file) -{ - IRDA_DEBUG(4, __FUNCTION__ "()\n"); - - lock_kernel(); - irda.in_use = FALSE; - unlock_kernel(); - - return 0; -} - -static ssize_t irda_read(struct file *file, char *buffer, size_t count, - loff_t *noidea) -{ - struct irda_event *event; - unsigned long flags; - int len; - - IRDA_DEBUG(4, __FUNCTION__ "()\n"); - - /* * Go to sleep and wait for event if there is no event to be read! */ - save_flags( flags); - cli(); - if ( !irda.event_queue) - interruptible_sleep_on( &irda.wait_queue); - restore_flags(flags); - - /* - * Ensure proper reaction to signals, and screen out - * blocked signals (page 112. linux device drivers) - */ - if (signal_pending( current)) - return -ERESTARTSYS; - - event = (struct irda_event *) dequeue_first( &irda.event_queue); - if (!event) - return 0; - - len = sizeof(struct irmanager_event); - copy_to_user(buffer, &event->event, len); - - /* Finished with event */ - kfree(event); - - return len; -} - -static ssize_t irda_write(struct file *file, const char *buffer, - size_t count, loff_t *noidea) -{ - IRDA_DEBUG(0, __FUNCTION__ "()\n"); - - return 0; -} - -static u_int irda_poll(struct file *file, poll_table *wait) -{ - IRDA_DEBUG(0, __FUNCTION__ "(), Sorry not implemented yet!\n"); - - return 0; -} - -void irda_mod_inc_use_count(void) -{ -#ifdef MODULE - MOD_INC_USE_COUNT; -#endif -} - -void irda_mod_dec_use_count(void) -{ -#ifdef MODULE - MOD_DEC_USE_COUNT; -#endif -} - -/* - * Function irda_proc_modcount (inode, fill) - * - * Use by the proc file system functions to prevent the irda module - * being removed while the use is standing in the net/irda directory - */ -void irda_proc_modcount(struct inode *inode, int fill) -{ -#ifdef MODULE -#ifdef CONFIG_PROC_FS - if (fill) - MOD_INC_USE_COUNT; - else - MOD_DEC_USE_COUNT; -#endif /* CONFIG_PROC_FS */ -#endif /* MODULE */ -} - -#ifdef MODULE - -MODULE_AUTHOR("Dag Brattli <dagb@cs.uit.no>"); -MODULE_DESCRIPTION("The Linux IrDA Protocol Subsystem"); -MODULE_PARM(irda_debug, "1l"); -module_exit(irda_proto_cleanup); -#endif /* MODULE */ - diff --git a/net/irda/irsysctl.c b/net/irda/irsysctl.c index 1efeedc69..4c6218527 100644 --- a/net/irda/irsysctl.c +++ b/net/irda/irsysctl.c @@ -29,10 +29,11 @@ #include <asm/segment.h> #include <net/irda/irda.h> +#include <net/irda/irias_object.h> #define NET_IRDA 412 /* Random number */ enum { DISCOVERY=1, DEVNAME, COMPRESSION, DEBUG, SLOTS, DISCOVERY_TIMEOUT, - SLOT_TIMEOUT }; + SLOT_TIMEOUT, MAX_BAUD_RATE, MAX_INACTIVE_TIME }; extern int sysctl_discovery; extern int sysctl_discovery_slots; @@ -41,17 +42,35 @@ extern int sysctl_slot_timeout; extern int sysctl_fast_poll_increase; int sysctl_compression = 0; extern char sysctl_devname[]; +extern int sysctl_max_baud_rate; +extern int sysctl_max_inactive_time; #ifdef CONFIG_IRDA_DEBUG extern unsigned int irda_debug; #endif +static int do_devname(ctl_table *table, int write, struct file *filp, + void *buffer, size_t *lenp) +{ + int ret; + + ret = proc_dostring(table, write, filp, buffer, lenp); + if (ret == 0 && write) { + struct ias_value *val; + + val = irias_new_string_value(sysctl_devname); + if (val) + irias_object_change_attribute("Device", "DeviceName", val); + } + return ret; +} + /* One file */ static ctl_table irda_table[] = { { DISCOVERY, "discovery", &sysctl_discovery, sizeof(int), 0644, NULL, &proc_dointvec }, { DEVNAME, "devname", sysctl_devname, - 65, 0644, NULL, &proc_dostring, &sysctl_string}, + 65, 0644, NULL, &do_devname, &sysctl_string}, { COMPRESSION, "compression", &sysctl_compression, sizeof(int), 0644, NULL, &proc_dointvec }, #ifdef CONFIG_IRDA_DEBUG @@ -68,6 +87,10 @@ static ctl_table irda_table[] = { sizeof(int), 0644, NULL, &proc_dointvec }, { SLOT_TIMEOUT, "slot_timeout", &sysctl_slot_timeout, sizeof(int), 0644, NULL, &proc_dointvec }, + { MAX_BAUD_RATE, "max_baud_rate", &sysctl_max_baud_rate, + sizeof(int), 0644, NULL, &proc_dointvec }, + { MAX_INACTIVE_TIME, "max_inactive_time", &sysctl_max_inactive_time, + sizeof(int), 0644, NULL, &proc_dointvec }, { 0 } }; diff --git a/net/irda/irttp.c b/net/irda/irttp.c index e9d120efb..645815835 100644 --- a/net/irda/irttp.c +++ b/net/irda/irttp.c @@ -156,6 +156,8 @@ struct tsap_cb *irttp_open_tsap(__u8 stsap_sel, int credit, notify_t *notify) ttp_notify.disconnect_indication = irttp_disconnect_indication; ttp_notify.data_indication = irttp_data_indication; ttp_notify.udata_indication = irttp_udata_indication; + if(notify->status_indication != NULL) + ttp_notify.status_indication = irttp_status_indication; ttp_notify.instance = self; strncpy(ttp_notify.name, notify->name, NOTIFY_MAX_NAME); @@ -185,7 +187,7 @@ struct tsap_cb *irttp_open_tsap(__u8 stsap_sel, int credit, notify_t *notify) self->notify = *notify; self->lsap = lsap; - hashbin_insert(irttp->tsaps, (queue_t *) self, (int) self, NULL); + hashbin_insert(irttp->tsaps, (irda_queue_t *) self, (int) self, NULL); if (credit > TTP_MAX_QUEUE) self->initial_credit = TTP_MAX_QUEUE; @@ -608,6 +610,34 @@ static int irttp_data_indication(void *instance, void *sap, } /* + * Function irttp_status_indication (self, reason) + * + * Status_indication, just pass to the higher layer... + * + */ +void irttp_status_indication(void *instance, + LINK_STATUS link, LOCK_STATUS lock) +{ + struct tsap_cb *self; + + IRDA_DEBUG(4, __FUNCTION__ "()\n"); + + self = (struct tsap_cb *) instance; + + ASSERT(self != NULL, return;); + ASSERT(self->magic == TTP_TSAP_MAGIC, return;); + + /* + * Inform service user if he has requested it + */ + if (self->notify.status_indication != NULL) + self->notify.status_indication(self->notify.instance, + link, lock); + else + IRDA_DEBUG(2, __FUNCTION__ "(), no handler\n"); +} + +/* * Function irttp_flow_request (self, command) * * This funtion could be used by the upper layers to tell IrTTP to stop @@ -1002,7 +1032,7 @@ struct tsap_cb *irttp_dup(struct tsap_cb *orig, void *instance) skb_queue_head_init(&new->tx_queue); skb_queue_head_init(&new->rx_fragments); - hashbin_insert(irttp->tsaps, (queue_t *) new, (int) new, NULL); + hashbin_insert(irttp->tsaps, (irda_queue_t *) new, (int) new, NULL); return new; } diff --git a/net/irda/parameters.c b/net/irda/parameters.c index b43bbd70e..2b013ff43 100644 --- a/net/irda/parameters.c +++ b/net/irda/parameters.c @@ -513,10 +513,7 @@ int irda_param_extract(void *self, __u8 *buf, int len, pi_param_info_t *info) buf[0]); /* Skip this parameter */ - n += (2 + buf[n+1]); - len -= (2 + buf[n+1]); - - return 0; /* Continue */ + return 2 + buf[n + 1]; /* Continue */ } /* Lookup the info on how to parse this parameter */ @@ -532,10 +529,7 @@ int irda_param_extract(void *self, __u8 *buf, int len, pi_param_info_t *info) if (!pi_minor_info->func) { MESSAGE(__FUNCTION__"(), no handler for pi=%#x\n", buf[n]); /* Skip this parameter */ - n += (2 + buf[n+1]); - len -= (2 + buf[n+1]); - - return 0; /* Continue */ + return 2 + buf[n + 1]; /* Continue */ } /* Parse parameter value */ diff --git a/net/irda/qos.c b/net/irda/qos.c index 10a7765cb..997b3e8ff 100644 --- a/net/irda/qos.c +++ b/net/irda/qos.c @@ -43,6 +43,21 @@ #define CI_BZIP2 27 /* Random pick */ #endif +/* + * Maximum values of the baud rate we negociate with the other end. + * Most often, you don't have to change that, because Linux-IrDA will + * use the maximum offered by the link layer, which usually works fine. + * In some very rare cases, you may want to limit it to lower speeds... + */ +int sysctl_max_baud_rate = 16000000; +/* + * Maximum value of the lap disconnect timer we negociate with the other end. + * Most often, the value below represent the best compromise, but some user + * may want to keep the LAP alive longuer or shorter in case of link failure. + * Remember that the threshold time (early warning) is fixed to 3s... + */ +int sysctl_max_inactive_time = 12; + static int irlap_param_baud_rate(void *instance, irda_param_t *param, int get); static int irlap_param_link_disconnect(void *instance, irda_param_t *parm, int get); @@ -55,6 +70,10 @@ static int irlap_param_additional_bofs(void *instance, irda_param_t *parm, int get); static int irlap_param_min_turn_time(void *instance, irda_param_t *param, int get); +static int value_index(__u32 value, __u32 *array, int size); +static __u32 byte_value(__u8 byte, __u32 *array); +static __u32 index_value(int index, __u32 *array); +static int value_lower_bits(__u32 value, __u32 *array, int size, __u16 *field); __u32 min_turn_times[] = { 10000, 5000, 1000, 500, 100, 50, 10, 0 }; /* us */ __u32 baud_rates[] = { 2400, 9600, 19200, 38400, 57600, 115200, 576000, @@ -147,19 +166,31 @@ void irda_qos_compute_intersection(struct qos_info *qos, struct qos_info *new) */ void irda_init_max_qos_capabilies(struct qos_info *qos) { + int i; /* * These are the maximum supported values as specified on pages * 39-43 in IrLAP */ + /* Use sysctl to set some configurable values... */ + /* Set configured max speed */ + i = value_lower_bits(sysctl_max_baud_rate, baud_rates, 10, + &qos->baud_rate.bits); + sysctl_max_baud_rate = index_value(i, baud_rates); + + /* Set configured max disc time */ + i = value_lower_bits(sysctl_max_inactive_time, link_disc_times, 8, + &qos->link_disc_time.bits); + sysctl_max_inactive_time = index_value(i, link_disc_times); + /* LSB is first byte, MSB is second byte */ - qos->baud_rate.bits = 0x01ff; + qos->baud_rate.bits &= 0x03ff; qos->window_size.bits = 0x7f; qos->min_turn_time.bits = 0xff; qos->max_turn_time.bits = 0x0f; qos->data_size.bits = 0x3f; - qos->link_disc_time.bits = 0xff; + qos->link_disc_time.bits &= 0xff; qos->additional_bofs.bits = 0xff; #ifdef CONFIG_IRDA_COMPRESSION @@ -197,7 +228,7 @@ void irlap_adjust_qos_settings(struct qos_info *qos) * The data size must be adjusted according to the baud rate and max * turn time */ - index = value_index(qos->data_size.value, data_sizes); + index = value_index(qos->data_size.value, data_sizes, 6); line_capacity = irlap_max_line_capacity(qos->baud_rate.value, qos->max_turn_time.value); @@ -537,8 +568,8 @@ __u32 irlap_max_line_capacity(__u32 speed, __u32 max_turn_time) IRDA_DEBUG(2, __FUNCTION__ "(), speed=%d, max_turn_time=%d\n", speed, max_turn_time); - i = value_index(speed, baud_rates); - j = value_index(max_turn_time, max_turn_times); + i = value_index(speed, baud_rates, 10); + j = value_index(max_turn_time, max_turn_times, 4); ASSERT(((i >=0) && (i <=10)), return 0;); ASSERT(((j >=0) && (j <=4)), return 0;); @@ -574,7 +605,7 @@ __u32 irlap_min_turn_time_in_bytes(__u32 speed, __u32 min_turn_time) return bytes; } -__u32 byte_value(__u8 byte, __u32 *array) +static __u32 byte_value(__u8 byte, __u32 *array) { int index; @@ -602,20 +633,19 @@ int msb_index (__u16 word) msb >>=1; index--; } - return index; } /* - * Function value_index (value, array) + * Function value_index (value, array, size) * * Returns the index to the value in the specified array */ -int value_index(__u32 value, __u32 *array) +static int value_index(__u32 value, __u32 *array, int size) { int i; - for (i=0;i<8;i++) + for (i=0; i < size; i++) if (array[i] == value) break; return i; @@ -627,11 +657,38 @@ int value_index(__u32 value, __u32 *array) * Returns value to index in array, easy! * */ -__u32 index_value(int index, __u32 *array) +static __u32 index_value(int index, __u32 *array) { return array[index]; } +/* + * Function value_lower_bits (value, array) + * + * Returns a bit field marking all possibility lower than value. + * We may need a "value_higher_bits" in the future... + */ +static int value_lower_bits(__u32 value, __u32 *array, int size, __u16 *field) +{ + int i; + __u16 mask = 0x1; + __u16 result = 0x0; + + for (i=0; i < size; i++) { + /* Add the current value to the bit field, shift mask */ + result |= mask; + mask <<= 1; + /* Finished ? */ + if (array[i] >= value) + break; + } + /* Send back a valid index */ + if(i >= size) + i = size - 1; /* Last item */ + *field = result; + return i; +} + void irda_qos_bits_to_value(struct qos_info *qos) { int index; @@ -667,10 +724,3 @@ void irda_qos_bits_to_value(struct qos_info *qos) qos->compression.value = 0; #endif } - - - - - - - diff --git a/net/irda/timer.c b/net/irda/timer.c index 7625eecfc..ac80c1a23 100644 --- a/net/irda/timer.c +++ b/net/irda/timer.c @@ -24,6 +24,7 @@ ********************************************************************/ #include <asm/system.h> +#include <linux/config.h> #include <linux/delay.h> #include <net/irda/timer.h> @@ -99,6 +100,25 @@ void irlap_start_mbusy_timer(struct irlap_cb *self) (void *) self, irlap_media_busy_expired); } +void irlap_stop_mbusy_timer(struct irlap_cb *self) +{ + /* If timer is activated, kill it! */ + if(timer_pending(&self->media_busy_timer)) + del_timer(&self->media_busy_timer); + +#ifdef CONFIG_IRDA_ULTRA + /* Send any pending Ultra frames if any */ + if (!skb_queue_empty(&self->txq_ultra)) + /* Note : we don't send the frame, just post an event. + * Frames will be sent only if we are in NDM mode (see + * irlap_event.c). + * Also, moved this code from irlap_media_busy_expired() + * to here to catch properly all cases... + * Jean II */ + irlap_do_event(self, SEND_UI_FRAME, NULL, NULL); +#endif /* CONFIG_IRDA_ULTRA */ +} + void irlmp_start_watchdog_timer(struct lsap_cb *self, int timeout) { irda_start_timer(&self->watchdog_timer, timeout, (void *) self, @@ -117,6 +137,13 @@ void irlmp_start_idle_timer(struct lap_cb *self, int timeout) irlmp_idle_timer_expired); } +void irlmp_stop_idle_timer(struct lap_cb *self) +{ + /* If timer is activated, kill it! */ + if(timer_pending(&self->idle_timer)) + del_timer(&self->idle_timer); +} + /* * Function irlap_slot_timer_expired (data) * @@ -210,8 +237,5 @@ void irlap_media_busy_expired(void* data) ASSERT(self != NULL, return;); irda_device_set_media_busy(self->netdev, FALSE); - - /* Send any pending Ultra frames if any */ - if (!skb_queue_empty(&self->txq_ultra)) - irlap_do_event(self, SEND_UI_FRAME, NULL, NULL); + /* Note : will deal with Ultra frames */ } diff --git a/net/irda/wrapper.c b/net/irda/wrapper.c index 623328af1..a9f6d7328 100644 --- a/net/irda/wrapper.c +++ b/net/irda/wrapper.c @@ -69,6 +69,7 @@ static void (*state[])(struct net_device *dev, struct net_device_stats *stats, */ int async_wrap_skb(struct sk_buff *skb, __u8 *tx_buff, int buffsize) { + struct irda_skb_cb *cb = (struct irda_skb_cb *) skb->cb; int xbofs; int i; int n; @@ -85,7 +86,8 @@ int async_wrap_skb(struct sk_buff *skb, __u8 *tx_buff, int buffsize) * Send XBOF's for required min. turn time and for the negotiated * additional XBOFS */ - if (((struct irda_skb_cb *)(skb->cb))->magic != LAP_MAGIC) { + + if (cb->magic != LAP_MAGIC) { /* * This will happen for all frames sent from user-space. * Nothing to worry about, but we set the default number of @@ -94,7 +96,7 @@ int async_wrap_skb(struct sk_buff *skb, __u8 *tx_buff, int buffsize) IRDA_DEBUG(1, __FUNCTION__ "(), wrong magic in skb!\n"); xbofs = 10; } else - xbofs = ((struct irda_skb_cb *)(skb->cb))->xbofs; + xbofs = cb->xbofs + cb->xbofs_delay; IRDA_DEBUG(4, __FUNCTION__ "(), xbofs=%d\n", xbofs); @@ -287,6 +289,8 @@ static void state_link_escape(struct net_device *dev, { switch (byte) { case BOF: /* New frame? */ + IRDA_DEBUG(1, __FUNCTION__ + "(), Discarding incomplete frame\n"); rx_buff->state = BEGIN_FRAME; irda_device_set_media_busy(dev, TRUE); break; @@ -328,6 +332,8 @@ static void state_inside_frame(struct net_device *dev, switch (byte) { case BOF: /* New frame? */ + IRDA_DEBUG(1, __FUNCTION__ + "(), Discarding incomplete frame\n"); rx_buff->state = BEGIN_FRAME; irda_device_set_media_busy(dev, TRUE); break; diff --git a/net/khttpd/README b/net/khttpd/README index 57d974051..84ccd6575 100644 --- a/net/khttpd/README +++ b/net/khttpd/README @@ -117,17 +117,6 @@ echo 1 > /proc/sys/net/khttpd/start Port 8080 - in /etc/apache/httpd.conf. For security-reasons, you can also change - - BindAddress * - - to - - BindAddress 127.0.0.1 - - (in the same file) to prevent outside users from accessing Apache - directly. - Stopping kHTTPd diff --git a/net/khttpd/datasending.c b/net/khttpd/datasending.c index a26afe191..5726dca32 100644 --- a/net/khttpd/datasending.c +++ b/net/khttpd/datasending.c @@ -58,7 +58,7 @@ It sends the data to the socket indicated by desc->buf. static int sock_send_actor(read_descriptor_t * desc, struct page *page, unsigned long offset, unsigned long size) { int written; - unsigned long kaddr; + char *kaddr; unsigned long count = desc->count; struct socket *sock = (struct socket *) desc->buf; mm_segment_t old_fs; @@ -69,7 +69,7 @@ static int sock_send_actor(read_descriptor_t * desc, struct page *page, unsigned set_fs(KERNEL_DS); kaddr = kmap(page); - written = SendBuffer_async(sock,(char *)kaddr + offset,size); + written = SendBuffer_async(sock, kaddr + offset, size); kunmap(page); set_fs(old_fs); if (written < 0) { @@ -114,7 +114,7 @@ int DataSending(const int CPUNR) inode = CurrentRequest->filp->f_dentry->d_inode; - if (inode && inode->i_mapping->a_ops->readpage) { + if (inode->i_mapping->a_ops->readpage) { /* This does the actual transfer using sendfile */ read_descriptor_t desc; loff_t *ppos; diff --git a/net/khttpd/main.c b/net/khttpd/main.c index 5c39c59b9..2ade82be2 100644 --- a/net/khttpd/main.c +++ b/net/khttpd/main.c @@ -101,15 +101,12 @@ static int MainDaemon(void *cpu_pointer) MOD_INC_USE_COUNT; - current->state |= TASK_EXCLUSIVE; - CPUNR=0; if (cpu_pointer!=NULL) CPUNR=(int)*(int*)cpu_pointer; sprintf(current->comm,"khttpd - %i",CPUNR); - lock_kernel(); /* This seems to be required for exit_mm */ - exit_mm(current); + daemonize(); init_waitqueue_head(&(DummyWQ[CPUNR])); @@ -148,7 +145,6 @@ static int MainDaemon(void *cpu_pointer) changes +=AcceptConnections(CPUNR,MainSocket); } - set_current_state(TASK_INTERRUPTIBLE|TASK_EXCLUSIVE); if (changes==0) { (void)interruptible_sleep_on_timeout(&(DummyWQ[CPUNR]),1); @@ -200,8 +196,7 @@ static int ManagementDaemon(void *unused) sprintf(current->comm,"khttpd manager"); - lock_kernel(); /* This seems to be required for exit_mm */ - exit_mm(current); + daemonize(); /* Block all signals except SIGKILL and SIGSTOP */ diff --git a/net/lapb/lapb_iface.c b/net/lapb/lapb_iface.c index bdc84d72f..1723588da 100644 --- a/net/lapb/lapb_iface.c +++ b/net/lapb/lapb_iface.c @@ -12,6 +12,7 @@ * History * LAPB 001 Jonathan Naylor Started Coding * LAPB 002 Jonathan Naylor New timer architecture. + * 2000-10-29 Henner Eisen lapb_data_indication() return status. */ #include <linux/config.h> @@ -370,14 +371,11 @@ void lapb_disconnect_indication(lapb_cb *lapb, int reason) int lapb_data_indication(lapb_cb *lapb, struct sk_buff *skb) { - int used = 0; - if (lapb->callbacks.data_indication != NULL) { - (lapb->callbacks.data_indication)(lapb->token, skb); - used = 1; + return (lapb->callbacks.data_indication)(lapb->token, skb); } - - return used; + kfree_skb(skb); + return NET_RX_CN_HIGH; /* For now; must be != NET_RX_DROP */ } int lapb_data_transmit(lapb_cb *lapb, struct sk_buff *skb) diff --git a/net/lapb/lapb_in.c b/net/lapb/lapb_in.c index 4e7a9ca4d..0b45f57f5 100644 --- a/net/lapb/lapb_in.c +++ b/net/lapb/lapb_in.c @@ -12,6 +12,7 @@ * History * LAPB 001 Jonathan Naulor Started Coding * LAPB 002 Jonathan Naylor New timer architecture. + * 2000-10-29 Henner Eisen lapb_data_indication() return status. */ #include <linux/config.h> @@ -464,8 +465,21 @@ static void lapb_state3_machine(lapb_cb *lapb, struct sk_buff *skb, struct lapb_ lapb_check_iframes_acked(lapb, frame->nr); } if (frame->ns == lapb->vr) { + int cn; + cn = lapb_data_indication(lapb, skb); + queued = 1; + /* + * If upper layer has dropped the frame, we + * basically ignore any further protocol + * processing. This will cause the peer + * to re-transmit the frame later like + * a frame lost on the wire. + */ + if(cn == NET_RX_DROP){ + printk(KERN_DEBUG "LAPB: rx congestion\n"); + break; + } lapb->vr = (lapb->vr + 1) % modulus; - queued = lapb_data_indication(lapb, skb); lapb->condition &= ~LAPB_REJECT_CONDITION; if (frame->pf) { lapb_enquiry_response(lapb); diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index 3927d9c3a..a8218d679 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c @@ -96,7 +96,7 @@ static void netlink_sock_destruct(struct sock *sk) #endif } -/* This lock without TASK_EXCLUSIVE is good on UP and it is _very_ bad on SMP. +/* This lock without WQ_FLAG_EXCLUSIVE is good on UP and it is _very_ bad on SMP. * Look, when several writers sleep and reader wakes them up, all but one * immediately hit write lock and grab all the cpus. Exclusive sleep solves * this, _but_ remember, it adds useless work on UP machines. @@ -111,7 +111,7 @@ static void netlink_table_grab(void) add_wait_queue_exclusive(&nl_table_wait, &wait); for(;;) { - set_current_state(TASK_UNINTERRUPTIBLE|TASK_EXCLUSIVE); + set_current_state(TASK_UNINTERRUPTIBLE); if (atomic_read(&nl_table_users) == 0) break; write_unlock_bh(&nl_table_lock); diff --git a/net/socket.c b/net/socket.c index 58df6d92e..5c5c5a85b 100644 --- a/net/socket.c +++ b/net/socket.c @@ -277,14 +277,12 @@ static struct super_operations sockfs_ops = { static struct super_block * sockfs_read_super(struct super_block *sb, void *data, int silent) { - struct inode *root = get_empty_inode(); + struct inode *root = new_inode(sb); if (!root) return NULL; root->i_mode = S_IFDIR | S_IRUSR | S_IWUSR; root->i_uid = root->i_gid = 0; root->i_atime = root->i_mtime = root->i_ctime = CURRENT_TIME; - root->i_sb = sb; - root->i_dev = sb->s_dev; sb->s_blocksize = 1024; sb->s_blocksize_bits = 10; sb->s_magic = SOCKFS_MAGIC; @@ -407,7 +405,7 @@ struct socket *sockfd_lookup(int fd, int *err) } inode = file->f_dentry->d_inode; - if (!inode || !inode->i_sock || !(sock = socki_lookup(inode))) + if (!inode->i_sock || !(sock = socki_lookup(inode))) { *err = -ENOTSOCK; fput(file); diff --git a/net/sunrpc/sched.c b/net/sunrpc/sched.c index de359d2f5..9eee6afe2 100644 --- a/net/sunrpc/sched.c +++ b/net/sunrpc/sched.c @@ -257,7 +257,7 @@ rpc_make_runnable(struct rpc_task *task) printk(KERN_ERR "RPC: task w/ running timer in rpc_make_runnable!!\n"); return; } - task->tk_running = 1; + rpc_set_running(task); if (RPC_IS_ASYNC(task)) { if (RPC_IS_SLEEPING(task)) { int status; @@ -265,13 +265,14 @@ rpc_make_runnable(struct rpc_task *task) if (status < 0) { printk(KERN_WARNING "RPC: failed to add task to queue: error: %d!\n", status); task->tk_status = status; - } else - task->tk_sleeping = 0; + return; + } + rpc_clear_sleeping(task); + if (waitqueue_active(&rpciod_idle)) + wake_up(&rpciod_idle); } - if (waitqueue_active(&rpciod_idle)) - wake_up(&rpciod_idle); } else { - task->tk_sleeping = 0; + rpc_clear_sleeping(task); if (waitqueue_active(&task->tk_wait)) wake_up(&task->tk_wait); } @@ -287,7 +288,7 @@ rpc_schedule_run(struct rpc_task *task) if (RPC_IS_ACTIVATED(task)) return; task->tk_active = 1; - task->tk_sleeping = 1; + rpc_set_sleeping(task); rpc_make_runnable(task); } @@ -326,7 +327,7 @@ __rpc_sleep_on(struct rpc_wait_queue *q, struct rpc_task *task, /* Mark the task as being activated if so needed */ if (!RPC_IS_ACTIVATED(task)) { task->tk_active = 1; - task->tk_sleeping = 1; + rpc_set_sleeping(task); } status = __rpc_add_wait_queue(q, task); @@ -334,7 +335,7 @@ __rpc_sleep_on(struct rpc_wait_queue *q, struct rpc_task *task, printk(KERN_WARNING "RPC: failed to add task to queue: error: %d!\n", status); task->tk_status = status; } else { - task->tk_running = 0; + rpc_clear_running(task); if (task->tk_callback) { dprintk(KERN_ERR "RPC: %4d overwrites an active callback\n", task->tk_pid); BUG(); @@ -590,21 +591,15 @@ __rpc_execute(struct rpc_task *task) /* * Check whether task is sleeping. - * Note that if the task goes to sleep in tk_action, - * and the RPC reply arrives before we get here, it will - * have state RUNNING, but will still be on schedq. - * 27/9/99: The above has been attempted fixed by - * introduction of task->tk_sleeping. */ spin_lock_bh(&rpc_queue_lock); if (!RPC_IS_RUNNING(task)) { - task->tk_sleeping = 1; + rpc_set_sleeping(task); if (RPC_IS_ASYNC(task)) { spin_unlock_bh(&rpc_queue_lock); return 0; } - } else - task->tk_sleeping = 0; + } spin_unlock_bh(&rpc_queue_lock); while (RPC_IS_SLEEPING(task)) { @@ -684,7 +679,7 @@ rpc_execute(struct rpc_task *task) } task->tk_active = 1; - task->tk_running = 1; + rpc_set_running(task); return __rpc_execute(task); out_release: rpc_release_task(task); diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index 7603a9221..e48b8549a 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c @@ -8,7 +8,7 @@ * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * - * Version: $Id: af_unix.c,v 1.107 2000/10/19 01:05:36 davem Exp $ + * Version: $Id: af_unix.c,v 1.108 2000/11/10 04:02:04 davem Exp $ * * Fixes: * Linus Torvalds : Assorted bug cures. @@ -832,7 +832,7 @@ static long unix_wait_for_peer(unix_socket *other, long timeo) int sched; DECLARE_WAITQUEUE(wait, current); - __set_current_state(TASK_INTERRUPTIBLE|TASK_EXCLUSIVE); + __set_current_state(TASK_INTERRUPTIBLE); add_wait_queue_exclusive(&other->protinfo.af_unix.peer_wait, &wait); sched = (!other->dead && diff --git a/net/unix/garbage.c b/net/unix/garbage.c index 3fcf7464c..16d46de54 100644 --- a/net/unix/garbage.c +++ b/net/unix/garbage.c @@ -100,7 +100,7 @@ extern inline unix_socket *unix_get_socket(struct file *filp) /* * Socket ? */ - if (inode && inode->i_sock) { + if (inode->i_sock) { struct socket * sock = &inode->u.socket_i; struct sock * s = sock->sk; diff --git a/net/x25/af_x25.c b/net/x25/af_x25.c index 320722c40..0242b12e5 100644 --- a/net/x25/af_x25.c +++ b/net/x25/af_x25.c @@ -16,13 +16,16 @@ * X.25 001 Jonathan Naylor Started coding. * X.25 002 Jonathan Naylor Centralised disconnect handling. * New timer architecture. - * 2000-11-03 Henner Eisen MSG_EOR handling more POSIX compliant. - * 2000-22-03 Daniela Squassoni Allowed disabling/enabling of + * 2000-03-11 Henner Eisen MSG_EOR handling more POSIX compliant. + * 2000-03-22 Daniela Squassoni Allowed disabling/enabling of * facilities negotiation and increased * the throughput upper limit. - * 2000-27-08 Arnaldo C. Melo s/suser/capable/ + micro cleanups - * 2000-04-09 Henner Eisen Set sock->state in x25_accept(). + * 2000-08-27 Arnaldo C. Melo s/suser/capable/ + micro cleanups + * 2000-09-04 Henner Eisen Set sock->state in x25_accept(). * Fixed x25_output() related skb leakage. + * 2000-10-02 Henner Eisen Made x25_kick() single threaded per socket. + * 2000-10-27 Henner Eisen MSG_DONTWAIT for fragment allocation. + * 2000-11-14 Henner Eisen Closing datalink from NETDEV_GOING_DOWN */ #include <linux/config.h> @@ -191,6 +194,7 @@ static void x25_kill_by_device(struct net_device *dev) static int x25_device_event(struct notifier_block *this, unsigned long event, void *ptr) { struct net_device *dev = (struct net_device *)ptr; + struct x25_neigh *neigh; if (dev->type == ARPHRD_X25 #if defined(CONFIG_LLC) || defined(CONFIG_LLC_MODULE) @@ -201,6 +205,10 @@ static int x25_device_event(struct notifier_block *this, unsigned long event, vo case NETDEV_UP: x25_link_device_up(dev); break; + case NETDEV_GOING_DOWN: + if ((neigh = x25_get_neigh(dev))) + x25_terminate_link(neigh); + break; case NETDEV_DOWN: x25_kill_by_device(dev); x25_route_device_down(dev); @@ -471,6 +479,7 @@ static int x25_create(struct socket *sock, int protocol) sock->ops = &x25_proto_ops; sk->protocol = protocol; + sk->backlog_rcv = x25_backlog_rcv; x25->t21 = sysctl_x25_call_request_timeout; x25->t22 = sysctl_x25_reset_request_timeout; @@ -905,6 +914,7 @@ static int x25_sendmsg(struct socket *sock, struct msghdr *msg, int len, struct if ((skb = sock_alloc_send_skb(sk, size, 0, msg->msg_flags & MSG_DONTWAIT, &err)) == NULL) return err; + X25_SKB_CB(skb)->flags = msg->msg_flags; skb_reserve(skb, X25_MAX_L2_LEN + X25_EXT_MIN_LEN); @@ -974,14 +984,31 @@ static int x25_sendmsg(struct socket *sock, struct msghdr *msg, int len, struct if (msg->msg_flags & MSG_OOB) { skb_queue_tail(&sk->protinfo.x25->interrupt_out_queue, skb); } else { - err = x25_output(sk, skb); - if(err){ - len = err; + len = x25_output(sk, skb); + if(len<0){ kfree_skb(skb); + } else { + if(sk->protinfo.x25->qbitincl) len++; } } + /* + * lock_sock() is currently only used to serialize this x25_kick() + * against input-driven x25_kick() calls. It currently only blocks + * incoming packets for this socket and does not protect against + * any other socket state changes and is not called from anywhere + * else. As x25_kick() cannot block and as long as all socket + * operations are BKL-wrapped, we don't need take to care about + * purging the backlog queue in x25_release(). + * + * Using lock_sock() to protect all socket operations entirely + * (and making the whole x25 stack SMP aware) unfortunately would + * require major changes to {send,recv}msg and skb allocation methods. + * -> 2.5 ;) + */ + lock_sock(sk); x25_kick(sk); + release_sock(sk); return len; } diff --git a/net/x25/x25_dev.c b/net/x25/x25_dev.c index d986022fb..fbc781dce 100644 --- a/net/x25/x25_dev.c +++ b/net/x25/x25_dev.c @@ -68,8 +68,17 @@ static int x25_receive_data(struct sk_buff *skb, struct x25_neigh *neigh) * Find an existing socket. */ if ((sk = x25_find_socket(lci, neigh)) != NULL) { + int queued = 1; + skb->h.raw = skb->data; - return x25_process_rx_frame(sk, skb); + bh_lock_sock(sk); + if (!sk->lock.users) { + queued = x25_process_rx_frame(sk, skb); + } else { + sk_add_backlog(sk, skb); + } + bh_unlock_sock(sk); + return queued; } /* diff --git a/net/x25/x25_in.c b/net/x25/x25_in.c index 5110d327a..bcb5f1cf4 100644 --- a/net/x25/x25_in.c +++ b/net/x25/x25_in.c @@ -16,8 +16,10 @@ * X.25 001 Jonathan Naylor Started coding. * X.25 002 Jonathan Naylor Centralised disconnection code. * New timer architecture. - * mar/20/00 Daniela Squassoni Disabling/enabling of facilities + * 2000-03-20 Daniela Squassoni Disabling/enabling of facilities * negotiation. + * 2000-11-10 Henner Eisen Check and reset for out-of-sequence + * i-frames. */ #include <linux/config.h> @@ -216,7 +218,8 @@ static int x25_state3_machine(struct sock *sk, struct sk_buff *skb, int frametyp case X25_DATA: /* XXX */ sk->protinfo.x25->condition &= ~X25_COND_PEER_RX_BUSY; - if (!x25_validate_nr(sk, nr)) { + if ((ns!=sk->protinfo.x25->vr) || + !x25_validate_nr(sk, nr)) { x25_clear_queues(sk); x25_write_internal(sk, X25_RESET_REQUEST); x25_start_t22timer(sk); @@ -357,4 +360,14 @@ int x25_process_rx_frame(struct sock *sk, struct sk_buff *skb) return queued; } +int x25_backlog_rcv(struct sock *sk, struct sk_buff *skb) +{ + int queued; + + queued = x25_process_rx_frame(sk,skb); + if(!queued) kfree_skb(skb); + + return 0; +} + #endif diff --git a/net/x25/x25_out.c b/net/x25/x25_out.c index 077f2c0b4..b3ce30477 100644 --- a/net/x25/x25_out.c +++ b/net/x25/x25_out.c @@ -15,7 +15,10 @@ * History * X.25 001 Jonathan Naylor Started coding. * X.25 002 Jonathan Naylor New timer architecture. - * 2000-09-04 Henner Eisen Prevented x25_output() skb leakage. + * 2000-09-04 Henner Eisen Prevented x25_output() skb leakage. + * 2000-10-27 Henner Eisen MSG_DONTWAIT for fragment allocation. + * 2000-11-10 Henner Eisen x25_send_iframe(): re-queued frames + * needed cleaned seq-number fields. */ #include <linux/config.h> @@ -55,13 +58,17 @@ static int x25_pacsize_to_bytes(unsigned int pacsize) } /* - * This is where all X.25 information frames pass; + * This is where all X.25 information frames pass. + * + * Returns the amount of user data bytes sent on success + * or a negative error code on failure. */ int x25_output(struct sock *sk, struct sk_buff *skb) { struct sk_buff *skbn; unsigned char header[X25_EXT_MIN_LEN]; int err, frontlen, len, header_len, max_len; + int sent=0, noblock = X25_SKB_CB(skb)->flags & MSG_DONTWAIT; header_len = (sk->protinfo.x25->neighbour->extended) ? X25_EXT_MIN_LEN : X25_STD_MIN_LEN; max_len = x25_pacsize_to_bytes(sk->protinfo.x25->facilities.pacsize_out); @@ -74,11 +81,14 @@ int x25_output(struct sock *sk, struct sk_buff *skb) frontlen = skb_headroom(skb); while (skb->len > 0) { - if ((skbn = sock_alloc_send_skb(sk, frontlen + max_len, 0, 0, &err)) == NULL){ - int unsent = skb->len - header_len; - SOCK_DEBUG(sk, "x25_output: framgent allocation failed, err=%d, %d bytes unsent\n", err, unsent); - return err; + if ((skbn = sock_alloc_send_skb(sk, frontlen + max_len, 0, noblock, &err)) == NULL){ + if(err == -EWOULDBLOCK && noblock){ + kfree_skb(skb); + return sent; } + SOCK_DEBUG(sk, "x25_output: fragment allocation failed, err=%d, %d bytes sent\n", err, sent); + return err; + } skb_reserve(skbn, frontlen); @@ -100,13 +110,15 @@ int x25_output(struct sock *sk, struct sk_buff *skb) } skb_queue_tail(&sk->write_queue, skbn); + sent += len; } kfree_skb(skb); } else { skb_queue_tail(&sk->write_queue, skb); + sent = skb->len - header_len; } - return 0; + return sent; } /* @@ -119,9 +131,11 @@ static void x25_send_iframe(struct sock *sk, struct sk_buff *skb) return; if (sk->protinfo.x25->neighbour->extended) { - skb->data[2] |= (sk->protinfo.x25->vs << 1) & 0xFE; + skb->data[2] = (sk->protinfo.x25->vs << 1) & 0xFE; + skb->data[3] &= X25_EXT_M_BIT; skb->data[3] |= (sk->protinfo.x25->vr << 1) & 0xFE; } else { + skb->data[2] &= X25_STD_M_BIT; skb->data[2] |= (sk->protinfo.x25->vs << 1) & 0x0E; skb->data[2] |= (sk->protinfo.x25->vr << 5) & 0xE0; } |