📄 irlan_common.c
字号:
/********************************************************************* * * Filename: irlan_common.c * Version: 0.9 * Description: IrDA LAN Access Protocol Implementation * Status: Experimental. * Author: Dag Brattli <dagb@cs.uit.no> * Created at: Sun Aug 31 20:14:37 1997 * Modified at: Sun Dec 26 21:53:10 1999 * Modified by: Dag Brattli <dagb@cs.uit.no> * * Copyright (c) 1997, 1999 Dag Brattli <dagb@cs.uit.no>, * All Rights Reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * Neither Dag Brattli nor University of Tromsø admit liability nor * provide warranty for any of this software. This material is * provided "AS-IS" and at no charge. * ********************************************************************/#include <linux/module.h>#include <linux/kernel.h>#include <linux/string.h>#include <linux/init.h>#include <linux/errno.h>#include <linux/proc_fs.h>#include <linux/seq_file.h>#include <linux/random.h>#include <linux/netdevice.h>#include <linux/etherdevice.h>#include <linux/rtnetlink.h>#include <linux/moduleparam.h>#include <linux/bitops.h>#include <asm/system.h>#include <asm/byteorder.h>#include <net/irda/irda.h>#include <net/irda/irttp.h>#include <net/irda/irlmp.h>#include <net/irda/iriap.h>#include <net/irda/timer.h>#include <net/irda/irlan_common.h>#include <net/irda/irlan_client.h>#include <net/irda/irlan_provider.h>#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[]; *//* * Master structure */static LIST_HEAD(irlans);static void *ckey;static void *skey;/* Module parameters */static int eth; /* Use "eth" or "irlan" name for devices */static int access = ACCESS_PEER; /* PEER, DIRECT or HOSTED */#ifdef CONFIG_PROC_FSstatic const char *irlan_access[] = { "UNKNOWN", "DIRECT", "PEER", "HOSTED"};static const char *irlan_media[] = { "UNKNOWN", "802.3", "802.5"};extern struct proc_dir_entry *proc_irda;static int irlan_seq_open(struct inode *inode, struct file *file);static const struct file_operations irlan_fops = { .owner = THIS_MODULE, .open = irlan_seq_open, .read = seq_read, .llseek = seq_lseek, .release = seq_release,};extern struct proc_dir_entry *proc_irda;#endif /* CONFIG_PROC_FS */static struct irlan_cb *irlan_open(__u32 saddr, __u32 daddr);static void __irlan_close(struct irlan_cb *self);static int __irlan_insert_param(struct sk_buff *skb, char *param, int type, __u8 value_byte, __u16 value_short, __u8 *value_array, __u16 value_len);static void irlan_open_unicast_addr(struct irlan_cb *self);static void irlan_get_unicast_addr(struct irlan_cb *self);void irlan_close_tsaps(struct irlan_cb *self);/* * Function irlan_init (void) * * Initialize IrLAN layer * */static int __init irlan_init(void){ struct irlan_cb *new; __u16 hints; IRDA_DEBUG(2, "%s()\n", __FUNCTION__ );#ifdef CONFIG_PROC_FS { struct proc_dir_entry *proc; proc = create_proc_entry("irlan", 0, proc_irda); if (!proc) { printk(KERN_ERR "irlan_init: can't create /proc entry!\n"); return -ENODEV; } proc->proc_fops = &irlan_fops; }#endif /* CONFIG_PROC_FS */ IRDA_DEBUG(4, "%s()\n", __FUNCTION__ ); hints = irlmp_service_to_hint(S_LAN); /* Register with IrLMP as a client */ ckey = irlmp_register_client(hints, &irlan_client_discovery_indication, NULL, NULL); if (!ckey) goto err_ckey; /* Register with IrLMP as a service */ skey = irlmp_register_service(hints); if (!skey) goto err_skey; /* Start the master IrLAN instance (the only one for now) */ new = irlan_open(DEV_ADDR_ANY, DEV_ADDR_ANY); if (!new) goto err_open; /* The master will only open its (listen) control TSAP */ irlan_provider_open_ctrl_tsap(new); /* Do some fast discovery! */ irlmp_discovery_request(DISCOVERY_DEFAULT_SLOTS); return 0;err_open: irlmp_unregister_service(skey);err_skey: irlmp_unregister_client(ckey);err_ckey:#ifdef CONFIG_PROC_FS remove_proc_entry("irlan", proc_irda);#endif /* CONFIG_PROC_FS */ return -ENOMEM;}static void __exit irlan_cleanup(void){ struct irlan_cb *self, *next; IRDA_DEBUG(4, "%s()\n", __FUNCTION__ ); irlmp_unregister_client(ckey); irlmp_unregister_service(skey);#ifdef CONFIG_PROC_FS remove_proc_entry("irlan", proc_irda);#endif /* CONFIG_PROC_FS */ /* Cleanup any leftover network devices */ rtnl_lock(); list_for_each_entry_safe(self, next, &irlans, dev_list) { __irlan_close(self); } rtnl_unlock();}/* * Function irlan_open (void) * * Open new instance of a client/provider, we should only register the * network device if this instance is ment for a particular client/provider */static struct irlan_cb *irlan_open(__u32 saddr, __u32 daddr){ struct net_device *dev; struct irlan_cb *self; IRDA_DEBUG(2, "%s()\n", __FUNCTION__ ); /* Create network device with irlan */ dev = alloc_irlandev(eth ? "eth%d" : "irlan%d"); if (!dev) return NULL; self = dev->priv; self->dev = dev; /* * Initialize local device structure */ self->magic = IRLAN_MAGIC; self->saddr = saddr; self->daddr = daddr; /* Provider access can only be PEER, DIRECT, or HOSTED */ self->provider.access_type = access; if (access == ACCESS_DIRECT) { /* * Since we are emulating an IrLAN sever we will have to * give ourself an ethernet address! */ dev->dev_addr[0] = 0x40; dev->dev_addr[1] = 0x00; dev->dev_addr[2] = 0x00; dev->dev_addr[3] = 0x00; get_random_bytes(dev->dev_addr+4, 1); get_random_bytes(dev->dev_addr+5, 1); } self->media = MEDIA_802_3; self->disconnect_reason = LM_USER_REQUEST; init_timer(&self->watchdog_timer); init_timer(&self->client.kick_timer); init_waitqueue_head(&self->open_wait); skb_queue_head_init(&self->client.txq); irlan_next_client_state(self, IRLAN_IDLE); irlan_next_provider_state(self, IRLAN_IDLE); if (register_netdev(dev)) { IRDA_DEBUG(2, "%s(), register_netdev() failed!\n", __FUNCTION__ ); self = NULL; free_netdev(dev); } else { rtnl_lock(); list_add_rcu(&self->dev_list, &irlans); rtnl_unlock(); } return self;}/* * Function __irlan_close (self) * * This function closes and deallocates the IrLAN client instances. Be * aware that other functions which calls client_close() must * remove self from irlans list first. */static void __irlan_close(struct irlan_cb *self){ IRDA_DEBUG(2, "%s()\n", __FUNCTION__ ); ASSERT_RTNL(); IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); del_timer_sync(&self->watchdog_timer); del_timer_sync(&self->client.kick_timer); /* Close all open connections and remove TSAPs */ irlan_close_tsaps(self); if (self->client.iriap) iriap_close(self->client.iriap); /* Remove frames queued on the control channel */ skb_queue_purge(&self->client.txq); /* Unregister and free self via destructor */ unregister_netdevice(self->dev);}/* Find any instance of irlan, used for client discovery wakeup */struct irlan_cb *irlan_get_any(void){ struct irlan_cb *self; list_for_each_entry_rcu(self, &irlans, dev_list) { return self; } return NULL;}/* * Function irlan_connect_indication (instance, sap, qos, max_sdu_size, skb) * * Here we receive the connect indication for the data channel * */static void irlan_connect_indication(void *instance, void *sap, struct qos_info *qos, __u32 max_sdu_size, __u8 max_header_size, struct sk_buff *skb){ struct irlan_cb *self; struct tsap_cb *tsap; IRDA_DEBUG(2, "%s()\n", __FUNCTION__ ); self = (struct irlan_cb *) instance; tsap = (struct tsap_cb *) sap; IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); IRDA_ASSERT(tsap == self->tsap_data,return;); self->max_sdu_size = max_sdu_size; self->max_header_size = max_header_size; IRDA_DEBUG(0, "%s: We are now connected!\n", __FUNCTION__); del_timer(&self->watchdog_timer); /* If you want to pass the skb to *both* state machines, you will * need to skb_clone() it, so that you don't free it twice. * As the state machines don't need it, git rid of it here... * Jean II */ if (skb) dev_kfree_skb(skb); irlan_do_provider_event(self, IRLAN_DATA_CONNECT_INDICATION, NULL); irlan_do_client_event(self, IRLAN_DATA_CONNECT_INDICATION, NULL); if (self->provider.access_type == ACCESS_PEER) { /* * Data channel is open, so we are now allowed to * configure the remote filter */ irlan_get_unicast_addr(self); irlan_open_unicast_addr(self); } /* Ready to transfer Ethernet frames (at last) */ netif_start_queue(self->dev); /* Clear reason */}static void irlan_connect_confirm(void *instance, void *sap, struct qos_info *qos, __u32 max_sdu_size, __u8 max_header_size, struct sk_buff *skb){ struct irlan_cb *self; self = (struct irlan_cb *) instance; IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); self->max_sdu_size = max_sdu_size; self->max_header_size = max_header_size; /* TODO: we could set the MTU depending on the max_sdu_size */ IRDA_DEBUG(0, "%s: We are now connected!\n", __FUNCTION__); del_timer(&self->watchdog_timer); /* * Data channel is open, so we are now allowed to configure the remote * filter */ irlan_get_unicast_addr(self); irlan_open_unicast_addr(self); /* Open broadcast and multicast filter by default */ irlan_set_broadcast_filter(self, TRUE); irlan_set_multicast_filter(self, TRUE); /* 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);}/* * Function irlan_client_disconnect_indication (handle) * * Callback function for the IrTTP layer. Indicates a disconnection of * the specified connection (handle) */static void irlan_disconnect_indication(void *instance, void *sap, LM_REASON reason, struct sk_buff *userdata){ struct irlan_cb *self; struct tsap_cb *tsap;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -