📄 irlap.c
字号:
/********************************************************************* * * Filename: irlap.c * Version: 1.0 * Description: IrLAP implementation for Linux * Status: Stable * Author: Dag Brattli <dagb@cs.uit.no> * Created at: Mon Aug 4 20:40:53 1997 * Modified at: Tue Dec 14 09:26:44 1999 * Modified by: Dag Brattli <dagb@cs.uit.no> * * Copyright (c) 1998-1999 Dag Brattli, All Rights Reserved. * Copyright (c) 2000-2003 Jean Tourrilhes <jt@hpl.hp.com> * * 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. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, * MA 02111-1307 USA * ********************************************************************/#include <linux/slab.h>#include <linux/string.h>#include <linux/skbuff.h>#include <linux/delay.h>#include <linux/proc_fs.h>#include <linux/init.h>#include <linux/random.h>#include <linux/module.h>#include <linux/seq_file.h>#include <net/irda/irda.h>#include <net/irda/irda_device.h>#include <net/irda/irqueue.h>#include <net/irda/irlmp.h>#include <net/irda/irlmp_frame.h>#include <net/irda/irlap_frame.h>#include <net/irda/irlap.h>#include <net/irda/timer.h>#include <net/irda/qos.h>static hashbin_t *irlap = NULL;int sysctl_slot_timeout = SLOT_TIMEOUT * 1000 / HZ;/* This is the delay of missed pf period before generating an event * to the application. The spec mandate 3 seconds, but in some cases * it's way too long. - Jean II */int sysctl_warn_noreply_time = 3;extern void irlap_queue_xmit(struct irlap_cb *self, struct sk_buff *skb);static void __irlap_close(struct irlap_cb *self);static void irlap_init_qos_capabilities(struct irlap_cb *self, struct qos_info *qos_user);#ifdef CONFIG_IRDA_DEBUGstatic char *lap_reasons[] = { "ERROR, NOT USED", "LAP_DISC_INDICATION", "LAP_NO_RESPONSE", "LAP_RESET_INDICATION", "LAP_FOUND_NONE", "LAP_MEDIA_BUSY", "LAP_PRIMARY_CONFLICT", "ERROR, NOT USED",};#endif /* CONFIG_IRDA_DEBUG */int __init irlap_init(void){ /* Check if the compiler did its job properly. * May happen on some ARM configuration, check with Russell King. */ IRDA_ASSERT(sizeof(struct xid_frame) == 14, ;); IRDA_ASSERT(sizeof(struct test_frame) == 10, ;); IRDA_ASSERT(sizeof(struct ua_frame) == 10, ;); IRDA_ASSERT(sizeof(struct snrm_frame) == 11, ;); /* Allocate master array */ irlap = hashbin_new(HB_LOCK); if (irlap == NULL) { IRDA_ERROR("%s: can't allocate irlap hashbin!\n", __FUNCTION__); return -ENOMEM; } return 0;}void irlap_cleanup(void){ IRDA_ASSERT(irlap != NULL, return;); hashbin_delete(irlap, (FREE_FUNC) __irlap_close);}/* * Function irlap_open (driver) * * Initialize IrLAP layer * */struct irlap_cb *irlap_open(struct net_device *dev, struct qos_info *qos, const char *hw_name){ struct irlap_cb *self; IRDA_DEBUG(4, "%s()\n", __FUNCTION__); /* Initialize the irlap structure. */ self = kzalloc(sizeof(struct irlap_cb), GFP_KERNEL); if (self == NULL) return NULL; self->magic = LAP_MAGIC; /* Make a binding between the layers */ self->netdev = dev; self->qos_dev = qos; /* Copy hardware name */ if(hw_name != NULL) { strlcpy(self->hw_name, hw_name, sizeof(self->hw_name)); } else { self->hw_name[0] = '\0'; } /* FIXME: should we get our own field? */ dev->atalk_ptr = self; self->state = LAP_OFFLINE; /* Initialize transmit queue */ skb_queue_head_init(&self->txq); skb_queue_head_init(&self->txq_ultra); skb_queue_head_init(&self->wx_list); /* My unique IrLAP device address! */ /* We don't want the broadcast address, neither the NULL address * (most often used to signify "invalid"), and we don't want an * address already in use (otherwise connect won't be able * to select the proper link). - Jean II */ do { get_random_bytes(&self->saddr, sizeof(self->saddr)); } while ((self->saddr == 0x0) || (self->saddr == BROADCAST) || (hashbin_lock_find(irlap, self->saddr, NULL)) ); /* Copy to the driver */ memcpy(dev->dev_addr, &self->saddr, 4); init_timer(&self->slot_timer); init_timer(&self->query_timer); init_timer(&self->discovery_timer); init_timer(&self->final_timer); init_timer(&self->poll_timer); init_timer(&self->wd_timer); init_timer(&self->backoff_timer); init_timer(&self->media_busy_timer); irlap_apply_default_connection_parameters(self); self->N3 = 3; /* # connections attemts to try before giving up */ self->state = LAP_NDM; hashbin_insert(irlap, (irda_queue_t *) self, self->saddr, NULL); irlmp_register_link(self, self->saddr, &self->notify); return self;}EXPORT_SYMBOL(irlap_open);/* * Function __irlap_close (self) * * Remove IrLAP and all allocated memory. Stop any pending timers. * */static void __irlap_close(struct irlap_cb *self){ IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == LAP_MAGIC, return;); /* Stop timers */ del_timer(&self->slot_timer); del_timer(&self->query_timer); del_timer(&self->discovery_timer); del_timer(&self->final_timer); del_timer(&self->poll_timer); del_timer(&self->wd_timer); del_timer(&self->backoff_timer); del_timer(&self->media_busy_timer); irlap_flush_all_queues(self); self->magic = 0; kfree(self);}/* * Function irlap_close (self) * * Remove IrLAP instance * */void irlap_close(struct irlap_cb *self){ struct irlap_cb *lap; IRDA_DEBUG(4, "%s()\n", __FUNCTION__); IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == LAP_MAGIC, return;); /* We used to send a LAP_DISC_INDICATION here, but this was * racy. This has been move within irlmp_unregister_link() * itself. Jean II */ /* Kill the LAP and all LSAPs on top of it */ irlmp_unregister_link(self->saddr); self->notify.instance = NULL; /* Be sure that we manage to remove ourself from the hash */ lap = hashbin_remove(irlap, self->saddr, NULL); if (!lap) { IRDA_DEBUG(1, "%s(), Didn't find myself!\n", __FUNCTION__); return; } __irlap_close(lap);}EXPORT_SYMBOL(irlap_close);/* * Function irlap_connect_indication (self, skb) * * Another device is attempting to make a connection * */void irlap_connect_indication(struct irlap_cb *self, struct sk_buff *skb){ IRDA_DEBUG(4, "%s()\n", __FUNCTION__); IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == LAP_MAGIC, return;); irlap_init_qos_capabilities(self, NULL); /* No user QoS! */ irlmp_link_connect_indication(self->notify.instance, self->saddr, self->daddr, &self->qos_tx, skb);}/* * Function irlap_connect_response (self, skb) * * Service user has accepted incoming connection * */void irlap_connect_response(struct irlap_cb *self, struct sk_buff *userdata){ IRDA_DEBUG(4, "%s()\n", __FUNCTION__); irlap_do_event(self, CONNECT_RESPONSE, userdata, NULL);}/* * Function irlap_connect_request (self, daddr, qos_user, sniff) * * Request connection with another device, sniffing is not implemented * yet. * */void irlap_connect_request(struct irlap_cb *self, __u32 daddr, struct qos_info *qos_user, int sniff){ IRDA_DEBUG(3, "%s(), daddr=0x%08x\n", __FUNCTION__, daddr); IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == LAP_MAGIC, return;); self->daddr = daddr; /* * If the service user specifies QoS values for this connection, * then use them */ irlap_init_qos_capabilities(self, qos_user); if ((self->state == LAP_NDM) && !self->media_busy) irlap_do_event(self, CONNECT_REQUEST, NULL, NULL); else self->connect_pending = TRUE;}/* * Function irlap_connect_confirm (self, skb) * * Connection request has been accepted * */void irlap_connect_confirm(struct irlap_cb *self, struct sk_buff *skb){ IRDA_DEBUG(4, "%s()\n", __FUNCTION__); IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == LAP_MAGIC, return;); irlmp_link_connect_confirm(self->notify.instance, &self->qos_tx, skb);}/* * Function irlap_data_indication (self, skb) * * Received data frames from IR-port, so we just pass them up to * IrLMP for further processing * */void irlap_data_indication(struct irlap_cb *self, struct sk_buff *skb, int unreliable){ /* Hide LAP header from IrLMP layer */ skb_pull(skb, LAP_ADDR_HEADER+LAP_CTRL_HEADER); irlmp_link_data_indication(self->notify.instance, skb, unreliable);}/* * Function irlap_data_request (self, skb) * * Queue data for transmission, must wait until XMIT state * */void irlap_data_request(struct irlap_cb *self, struct sk_buff *skb, int unreliable){ IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == LAP_MAGIC, return;); IRDA_DEBUG(3, "%s()\n", __FUNCTION__); IRDA_ASSERT(skb_headroom(skb) >= (LAP_ADDR_HEADER+LAP_CTRL_HEADER), return;); skb_push(skb, LAP_ADDR_HEADER+LAP_CTRL_HEADER); /* * Must set frame format now so that the rest of the code knows * if its dealing with an I or an UI frame */ if (unreliable) skb->data[1] = UI_FRAME; else skb->data[1] = I_FRAME; /* Don't forget to refcount it - see irlmp_connect_request(). */ skb_get(skb); /* Add at the end of the queue (keep ordering) - Jean II */ skb_queue_tail(&self->txq, skb); /* * Send event if this frame only if we are in the right state * FIXME: udata should be sent first! (skb_queue_head?) */ if ((self->state == LAP_XMIT_P) || (self->state == LAP_XMIT_S)) { /* If we are not already processing the Tx queue, trigger * transmission immediately - Jean II */ if((skb_queue_len(&self->txq) <= 1) && (!self->local_busy)) irlap_do_event(self, DATA_REQUEST, skb, NULL); /* Otherwise, the packets will be sent normally at the * next pf-poll - Jean II */ }}/* * Function irlap_unitdata_request (self, skb) * * Send Ultra data. This is data that must be sent outside any connection * */#ifdef CONFIG_IRDA_ULTRAvoid irlap_unitdata_request(struct irlap_cb *self, struct sk_buff *skb){ IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == LAP_MAGIC, return;); IRDA_DEBUG(3, "%s()\n", __FUNCTION__); IRDA_ASSERT(skb_headroom(skb) >= (LAP_ADDR_HEADER+LAP_CTRL_HEADER), return;); skb_push(skb, LAP_ADDR_HEADER+LAP_CTRL_HEADER); skb->data[0] = CBROADCAST; skb->data[1] = UI_FRAME; /* Don't need to refcount, see irlmp_connless_data_request() */ skb_queue_tail(&self->txq_ultra, skb); irlap_do_event(self, SEND_UI_FRAME, NULL, NULL);}#endif /*CONFIG_IRDA_ULTRA *//* * Function irlap_udata_indication (self, skb) *
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -