patch-2.2.3 linux/net/irda/irlan/irlan_client.c
Next file: linux/net/irda/irlan/irlan_client_event.c
Previous file: linux/net/irda/irlan/irlan_cli_event.c
Back to the patch index
Back to the overall index
- Lines: 618
- Date:
Sun Mar 7 15:26:44 1999
- Orig file:
v2.2.2/linux/net/irda/irlan/irlan_client.c
- Orig date:
Wed Dec 31 16:00:00 1969
diff -u --recursive --new-file v2.2.2/linux/net/irda/irlan/irlan_client.c linux/net/irda/irlan/irlan_client.c
@@ -0,0 +1,617 @@
+/*********************************************************************
+ *
+ * Filename: irlan_client.c
+ * Version: 0.9
+ * Description: IrDA LAN Access Protocol (IrLAN) Client
+ * Status: Experimental.
+ * Author: Dag Brattli <dagb@cs.uit.no>
+ * Created at: Sun Aug 31 20:14:37 1997
+ * Modified at: Wed Feb 17 23:29:34 1999
+ * Modified by: Dag Brattli <dagb@cs.uit.no>
+ * Sources: skeleton.c by Donald Becker <becker@CESDIS.gsfc.nasa.gov>
+ * slip.c by Laurence Culhane, <loz@holmes.demon.co.uk>
+ * Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+ *
+ * Copyright (c) 1998 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/kernel.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/netdevice.h>
+#include <linux/inetdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/if_arp.h>
+#include <net/arp.h>
+
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <asm/byteorder.h>
+
+#include <net/irda/irda.h>
+#include <net/irda/irttp.h>
+#include <net/irda/irlmp.h>
+#include <net/irda/irias_object.h>
+#include <net/irda/iriap.h>
+#include <net/irda/timer.h>
+
+#include <net/irda/irlan_common.h>
+#include <net/irda/irlan_event.h>
+#include <net/irda/irlan_eth.h>
+#include <net/irda/irlan_provider.h>
+#include <net/irda/irlan_client.h>
+
+#undef CONFIG_IRLAN_GRATUITOUS_ARP
+
+static void irlan_client_ctrl_disconnect_indication(void *instance, void *sap,
+ LM_REASON reason,
+ struct sk_buff *);
+
+static void irlan_client_ctrl_data_indication(void *instance, void *sap,
+ struct sk_buff *skb);
+
+static void irlan_client_ctrl_connect_confirm(void *instance, void *sap,
+ struct qos_info *qos,
+ int max_sdu_size,
+ struct sk_buff *);
+
+static void irlan_check_response_param(struct irlan_cb *self, char *param,
+ char *value, int val_len);
+
+static void irlan_client_kick_timer_expired(unsigned long data)
+{
+ struct irlan_cb *self = (struct irlan_cb *) data;
+
+ DEBUG(2, __FUNCTION__ "()\n");
+
+ ASSERT(self != NULL, return;);
+ ASSERT(self->magic == IRLAN_MAGIC, return;);
+
+ /*
+ * 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, but only if the provider is not IDLE
+ */
+ if ((self->access_type == ACCESS_PEER) &&
+ (self->client.state == IRLAN_IDLE) &&
+ (self->provider.state != IRLAN_IDLE)) {
+ irlan_client_wakeup(self, self->saddr, self->daddr);
+ }
+}
+
+void irlan_client_start_kick_timer(struct irlan_cb *self, int timeout)
+{
+ DEBUG(4, __FUNCTION__ "()\n");
+
+ irda_start_timer(&self->client.kick_timer, timeout,
+ (unsigned long) self,
+ irlan_client_kick_timer_expired);
+}
+
+void irlan_client_wakeup(struct irlan_cb *self, __u32 saddr, __u32 daddr)
+{
+ struct irmanager_event mgr_event;
+
+ DEBUG(2, __FUNCTION__ "()\n");
+
+ ASSERT(self != NULL, return;);
+ ASSERT(self->magic == IRLAN_MAGIC, return;);
+
+ if (self->client.state == IRLAN_IDLE) {
+ /* saddr may have changed! */
+ self->saddr = saddr;
+
+ /* Check if network device is up */
+ if (self->dev.start) {
+ /* Open TSAPs */
+ irlan_client_open_ctrl_tsap(self);
+ irlan_provider_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;
+ sprintf(mgr_event.devname, "%s", self->ifname);
+ 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;
+ }
+ /* Restart watchdog timer */
+ irlan_start_watchdog_timer(self, IRLAN_TIMEOUT);
+
+ /* Start kick timer */
+ irlan_client_start_kick_timer(self, 200);
+ } else {
+ DEBUG(2, __FUNCTION__ "(), state=%s\n",
+ irlan_state[ self->client.state]);
+ /*
+ * If we get here, it's obvious that the last
+ * connection attempt has failed, so its best
+ * to go back to idle!
+ */
+ irlan_do_client_event(self, IRLAN_LMP_DISCONNECT, NULL);
+ }
+}
+
+/*
+ * Function irlan_discovery_indication (daddr)
+ *
+ * Remote device with IrLAN server support discovered
+ *
+ */
+void irlan_client_discovery_indication(DISCOVERY *discovery)
+{
+ struct irlan_cb *self, *entry;
+ __u32 saddr, daddr;
+
+ DEBUG(2, __FUNCTION__"()\n");
+
+ ASSERT(irlan != NULL, return;);
+ ASSERT(discovery != NULL, return;);
+
+ saddr = discovery->saddr;
+ daddr = discovery->daddr;
+
+ /*
+ * Check if we already have an instance for dealing with this
+ * provider.
+ */
+ self = (struct irlan_cb *) hashbin_find(irlan, daddr, NULL);
+ if (self) {
+ ASSERT(self->magic == IRLAN_MAGIC, return;);
+
+ DEBUG(2, __FUNCTION__ "(), Found instance!\n");
+
+ irlan_client_wakeup(self, saddr, daddr);
+
+ return;
+ }
+
+ /*
+ * We have no instance for daddr, so try and find an unused one
+ */
+ self = hashbin_find(irlan, DEV_ADDR_ANY, NULL);
+ if (self) {
+ DEBUG(2, __FUNCTION__ "(), Found instance with DEV_ADDR_ANY!\n");
+ /*
+ * Rehash instance, now we have a client (daddr) to serve.
+ */
+ entry = hashbin_remove(irlan, self->daddr, NULL);
+ ASSERT( entry == self, return;);
+
+ self->daddr = daddr;
+ self->saddr = saddr;
+
+ DEBUG(2, __FUNCTION__ "(), daddr=%08x\n", self->daddr);
+ hashbin_insert(irlan, (QUEUE*) self, self->daddr, NULL);
+
+ /* Check if network device has been registered */
+ if (!self->netdev_registered)
+ irlan_register_netdev(self);
+
+ /* Remember that we might have to start a new provider */
+ self->client.start_new_provider = TRUE;
+ } else {
+ DEBUG(2, __FUNCTION__ "(), Found none, starting new one!\n");
+ /* No instance available, so we have to start one! */
+ self = irlan_open(saddr, daddr, TRUE);
+ if (!self) {
+ DEBUG(2, __FUNCTION__ "(), irlan_open failed!\n");
+ return;
+ }
+ ASSERT( self != NULL, return;);
+ }
+ /* Restart watchdog timer */
+ irlan_start_watchdog_timer(self, IRLAN_TIMEOUT);
+}
+
+/*
+ * Function irlan_client_data_indication (handle, skb)
+ *
+ * This function gets the data that is received on the control channel
+ *
+ */
+static void irlan_client_ctrl_data_indication(void *instance, void *sap,
+ struct sk_buff *skb)
+{
+ struct irlan_cb *self;
+
+ DEBUG(4, __FUNCTION__ "()\n");
+
+ self = (struct irlan_cb *) instance;
+
+ ASSERT(self != NULL, return;);
+ ASSERT(self->magic == IRLAN_MAGIC, return;);
+ ASSERT(skb != NULL, return;);
+
+ irlan_do_client_event(self, IRLAN_DATA_INDICATION, skb);
+}
+
+static void irlan_client_ctrl_disconnect_indication(void *instance, void *sap,
+ LM_REASON reason,
+ struct sk_buff *userdata)
+{
+ struct irlan_cb *self;
+ struct tsap_cb *tsap;
+
+ DEBUG(4, __FUNCTION__ "(), reason=%d\n", reason);
+
+ self = (struct irlan_cb *) instance;
+ tsap = (struct tsap_cb *) sap;
+
+ ASSERT(self != NULL, return;);
+ ASSERT(self->magic == IRLAN_MAGIC, return;);
+ ASSERT(tsap != NULL, return;);
+ ASSERT(tsap->magic == TTP_TSAP_MAGIC, return;);
+
+ ASSERT(tsap == self->client.tsap_ctrl, return;);
+
+ irlan_do_client_event(self, IRLAN_LMP_DISCONNECT, NULL);
+}
+
+/*
+ * Function irlan_client_open_tsaps (self)
+ *
+ * Initialize callbacks and open IrTTP TSAPs
+ *
+ */
+void irlan_client_open_ctrl_tsap(struct irlan_cb *self)
+{
+ struct notify_t notify;
+ struct tsap_cb *tsap;
+
+ DEBUG(4, __FUNCTION__ "()\n");
+
+ ASSERT(self != NULL, return;);
+ ASSERT(self->magic == IRLAN_MAGIC, return;);
+
+ /* Check if already open */
+ if (self->client.tsap_ctrl)
+ return;
+
+ irda_notify_init(¬ify);
+
+ /* Set up callbacks */
+ notify.data_indication = irlan_client_ctrl_data_indication;
+ notify.connect_confirm = irlan_client_ctrl_connect_confirm;
+ notify.disconnect_indication = irlan_client_ctrl_disconnect_indication;
+ notify.instance = self;
+ strncpy(notify.name, "IrLAN ctrl (c)", NOTIFY_MAX_NAME);
+
+ tsap = irttp_open_tsap(LSAP_ANY, DEFAULT_INITIAL_CREDIT, ¬ify);
+ if (!tsap) {
+ DEBUG(2, __FUNCTION__ "(), Got no tsap!\n");
+ return;
+ }
+ self->client.tsap_ctrl = tsap;
+}
+
+/*
+ * Function irlan_client_connect_confirm (handle, skb)
+ *
+ * Connection to peer IrLAN laye confirmed
+ *
+ */
+static void irlan_client_ctrl_connect_confirm(void *instance, void *sap,
+ struct qos_info *qos,
+ int max_sdu_size,
+ struct sk_buff *skb)
+{
+ struct irlan_cb *self;
+
+ DEBUG(4, __FUNCTION__ "()\n");
+
+ self = (struct irlan_cb *) instance;
+
+ ASSERT(self != NULL, return;);
+ ASSERT(self->magic == IRLAN_MAGIC, return;);
+
+ /* TODO: we could set the MTU depending on the max_sdu_size */
+
+ irlan_do_client_event(self, IRLAN_CONNECT_COMPLETE, NULL);
+}
+
+/*
+ * Function irlan_client_reconnect_data_channel (self)
+ *
+ * Try to reconnect data channel (currently not used)
+ *
+ */
+void irlan_client_reconnect_data_channel(struct irlan_cb *self)
+{
+ struct sk_buff *skb;
+ __u8 *frame;
+
+ DEBUG(4, __FUNCTION__ "()\n");
+
+ ASSERT(self != NULL, return;);
+ ASSERT(self->magic == IRLAN_MAGIC, return;);
+
+ skb = dev_alloc_skb(128);
+ if (!skb)
+ return;
+
+ /* Reserve space for TTP, LMP, and LAP header */
+ skb_reserve(skb, TTP_HEADER+LMP_HEADER+LAP_HEADER);
+ skb_put(skb, 2);
+
+ frame = skb->data;
+
+ frame[0] = CMD_RECONNECT_DATA_CHAN;
+ frame[1] = 0x01;
+ irlan_insert_array_param(skb, "RECONNECT_KEY",
+ self->client.reconnect_key,
+ self->client.key_len);
+
+ irttp_data_request(self->client.tsap_ctrl, skb);
+}
+
+/*
+ * Function irlan_client_extract_params (skb)
+ *
+ * Extract all parameters from received buffer, then feed them to
+ * check_params for parsing
+ *
+ */
+void irlan_client_extract_params(struct irlan_cb *self, struct sk_buff *skb)
+{
+ __u8 *frame;
+ __u8 *ptr;
+ int count;
+ int ret;
+ __u16 val_len;
+ int i;
+ char *name;
+ char *value;
+
+ ASSERT(skb != NULL, return;);
+
+ DEBUG(4, __FUNCTION__ "() skb->len=%d\n", (int) skb->len);
+
+ ASSERT(self != NULL, return;);
+ ASSERT(self->magic == IRLAN_MAGIC, return;);
+
+ if (!skb) {
+ DEBUG(2, __FUNCTION__ "(), Got NULL skb!\n");
+ return;
+ }
+ frame = skb->data;
+
+ /*
+ * Check return code and print it if not success
+ */
+ if (frame[0]) {
+ print_ret_code(frame[0]);
+ return;
+ }
+
+ name = kmalloc(255, GFP_ATOMIC);
+ if (!name)
+ return;
+ value = kmalloc(1016, GFP_ATOMIC);
+ if (!value) {
+ kfree(name);
+ return;
+ }
+
+ /* How many parameters? */
+ count = frame[1];
+
+ DEBUG(4, __FUNCTION__ "(), got %d parameters\n", count);
+
+ ptr = frame+2;
+
+ /* For all parameters */
+ for (i=0; i<count;i++) {
+ ret = irlan_get_param(ptr, name, value, &val_len);
+ if (ret == -1) {
+ DEBUG(2, __FUNCTION__ "(), IrLAN, Error!\n");
+ break;
+ }
+ ptr+=ret;
+ irlan_check_response_param(self, name, value, val_len);
+ }
+ /* Cleanup */
+ kfree(name);
+ kfree(value);
+}
+
+/*
+ * Function check_param (param, value)
+ *
+ * Check which parameter is received and update local variables
+ *
+ */
+static void irlan_check_response_param(struct irlan_cb *self, char *param,
+ char *value, int val_len)
+{
+ struct in_device *in_dev;
+ __u8 *bytes;
+ int i;
+
+ DEBUG(4, __FUNCTION__ "(), parm=%s\n", param);
+
+ ASSERT(self != NULL, return;);
+ ASSERT(self->magic == IRLAN_MAGIC, return;);
+
+ /*
+ * Media type
+ */
+ if (strcmp(param, "MEDIA") == 0) {
+ if (strcmp(value, "802.3") == 0)
+ self->media = MEDIA_802_3;
+ else
+ self->media = MEDIA_802_5;
+ return;
+ }
+ if (strcmp(param, "FILTER_TYPE") == 0) {
+ if (strcmp(value, "DIRECTED") == 0)
+ self->client.filter_type |= IRLAN_DIRECTED;
+ else if (strcmp(value, "FUNCTIONAL") == 0)
+ self->client.filter_type |= IRLAN_FUNCTIONAL;
+ else if (strcmp(value, "GROUP") == 0)
+ self->client.filter_type |= IRLAN_GROUP;
+ else if (strcmp(value, "MAC_FRAME") == 0)
+ self->client.filter_type |= IRLAN_MAC_FRAME;
+ else if (strcmp(value, "MULTICAST") == 0)
+ self->client.filter_type |= IRLAN_MULTICAST;
+ else if (strcmp(value, "BROADCAST") == 0)
+ self->client.filter_type |= IRLAN_BROADCAST;
+ else if (strcmp(value, "IPX_SOCKET") == 0)
+ self->client.filter_type |= IRLAN_IPX_SOCKET;
+
+ }
+ if (strcmp(param, "ACCESS_TYPE") == 0) {
+ if (strcmp(value, "DIRECT") == 0)
+ self->access_type = ACCESS_DIRECT;
+ else if (strcmp(value, "PEER") == 0)
+ self->access_type = ACCESS_PEER;
+ else if (strcmp(value, "HOSTED") == 0)
+ self->access_type = ACCESS_HOSTED;
+ else {
+ DEBUG(2, __FUNCTION__ "(), unknown access type!\n");
+ }
+ }
+ /*
+ * IRLAN version
+ */
+ if (strcmp(param, "IRLAN_VER") == 0) {
+ DEBUG(4, "IrLAN version %d.%d\n", (__u8) value[0],
+ (__u8) value[1]);
+
+ self->version[0] = value[0];
+ self->version[1] = value[1];
+ return;
+ }
+ /*
+ * Which remote TSAP to use for data channel
+ */
+ if (strcmp(param, "DATA_CHAN") == 0) {
+ self->dtsap_sel_data = value[0];
+ DEBUG(4, "Data TSAP = %02x\n", self->dtsap_sel_data);
+ return;
+ }
+ if (strcmp(param, "CON_ARB") == 0) {
+ self->client.recv_arb_val = le16_to_cpup(value);
+ DEBUG(2, __FUNCTION__ "(), receive arb val=%d\n",
+ self->client.recv_arb_val);
+ }
+ if (strcmp(param, "MAX_FRAME") == 0) {
+ self->client.max_frame = le16_to_cpup(value);
+ DEBUG(4, __FUNCTION__ "(), max frame=%d\n",
+ self->client.max_frame);
+ }
+
+ /*
+ * RECONNECT_KEY, in case the link goes down!
+ */
+ if (strcmp(param, "RECONNECT_KEY") == 0) {
+ DEBUG(4, "Got reconnect key: ");
+ /* for (i = 0; i < val_len; i++) */
+/* printk("%02x", value[i]); */
+ memcpy(self->client.reconnect_key, value, val_len);
+ self->client.key_len = val_len;
+ DEBUG(4, "\n");
+ }
+ /*
+ * FILTER_ENTRY, have we got an ethernet address?
+ */
+ if (strcmp(param, "FILTER_ENTRY") == 0) {
+ bytes = value;
+ DEBUG(4, "Ethernet address = %02x:%02x:%02x:%02x:%02x:%02x\n",
+ bytes[0], bytes[1], bytes[2], bytes[3], bytes[4],
+ bytes[5]);
+ for (i = 0; i < 6; i++)
+ self->dev.dev_addr[i] = bytes[i];
+
+#ifdef CONFIG_IRLAN_GRATUITOUS_ARP
+ /*
+ * When we get a new MAC address do a gratuitous ARP. This
+ * is useful if we have changed access points on the same
+ * subnet.
+ */
+ DEBUG(4, "IrLAN: Sending gratuitous ARP\n");
+ in_dev = self->dev.ip_ptr;
+ arp_send(ARPOP_REQUEST, ETH_P_ARP,
+ in_dev->ifa_list->ifa_address,
+ &self->dev,
+ in_dev->ifa_list->ifa_address,
+ NULL, self->dev.dev_addr, NULL);
+#endif
+ }
+}
+
+/*
+ * Function irlan_client_get_value_confirm (obj_id, value)
+ *
+ * Got results from remote LM-IAS
+ *
+ */
+void irlan_client_get_value_confirm(__u16 obj_id, struct ias_value *value,
+ void *priv)
+{
+ struct irlan_cb *self;
+
+ DEBUG(4, __FUNCTION__ "()\n");
+
+ ASSERT(priv != NULL, return;);
+
+ self = (struct irlan_cb *) priv;
+ ASSERT(self->magic == IRLAN_MAGIC, return;);
+
+ /* Check if request succeeded */
+ if (!value) {
+ DEBUG(2, __FUNCTION__ "(), got NULL value!\n");
+ irlan_do_client_event(self, IRLAN_IAS_PROVIDER_NOT_AVAIL,
+ NULL);
+ return;
+ }
+
+ switch (value->type) {
+ case IAS_INTEGER:
+ self->dtsap_sel_ctrl = value->t.integer;
+
+ if (value->t.integer != -1) {
+ irlan_do_client_event(self, IRLAN_IAS_PROVIDER_AVAIL,
+ NULL);
+ return;
+ }
+ break;
+ case IAS_STRING:
+ DEBUG(2, __FUNCTION__ "(), got string %s\n", value->t.string);
+ break;
+ case IAS_OCT_SEQ:
+ DEBUG(2, __FUNCTION__ "(), OCT_SEQ not implemented\n");
+ break;
+ case IAS_MISSING:
+ DEBUG(2, __FUNCTION__ "(), MISSING not implemented\n");
+ break;
+ default:
+ DEBUG(2, __FUNCTION__ "(), unknown type!\n");
+ break;
+ }
+ irlan_do_client_event(self, IRLAN_IAS_PROVIDER_NOT_AVAIL, NULL);
+}
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)