📄 irlap_event.c
字号:
/********************************************************************* * * Filename: irlap_event.c * Version: 0.9 * Description: IrLAP state machine implementation * Status: Experimental. * Author: Dag Brattli <dag@brattli.net> * Created at: Sat Aug 16 00:59:29 1997 * Modified at: Sat Dec 25 21:07:57 1999 * Modified by: Dag Brattli <dag@brattli.net> * * Copyright (c) 1998-2000 Dag Brattli <dag@brattli.net>, * Copyright (c) 1998 Thomas Davis <ratbert@radiks.net> * 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. * * 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/string.h>#include <linux/kernel.h>#include <linux/delay.h>#include <linux/skbuff.h>#include <net/irda/irda.h>#include <net/irda/irlap_event.h>#include <net/irda/timer.h>#include <net/irda/irlap.h>#include <net/irda/irlap_frame.h>#include <net/irda/qos.h>#include <net/irda/parameters.h>#include <net/irda/irlmp.h> /* irlmp_flow_indication(), ... */#include <net/irda/irda_device.h>#ifdef CONFIG_IRDA_FAST_RRint sysctl_fast_poll_increase = 50;#endifstatic int irlap_state_ndm (struct irlap_cb *self, IRLAP_EVENT event, struct sk_buff *skb, struct irlap_info *info);static int irlap_state_query (struct irlap_cb *self, IRLAP_EVENT event, struct sk_buff *skb, struct irlap_info *info);static int irlap_state_reply (struct irlap_cb *self, IRLAP_EVENT event, struct sk_buff *skb, struct irlap_info *info);static int irlap_state_conn (struct irlap_cb *self, IRLAP_EVENT event, struct sk_buff *skb, struct irlap_info *info);static int irlap_state_setup (struct irlap_cb *self, IRLAP_EVENT event, struct sk_buff *skb, struct irlap_info *info);static int irlap_state_offline(struct irlap_cb *self, IRLAP_EVENT event, struct sk_buff *skb, struct irlap_info *info);static int irlap_state_xmit_p (struct irlap_cb *self, IRLAP_EVENT event, struct sk_buff *skb, struct irlap_info *info);static int irlap_state_pclose (struct irlap_cb *self, IRLAP_EVENT event, struct sk_buff *skb, struct irlap_info *info);static int irlap_state_nrm_p (struct irlap_cb *self, IRLAP_EVENT event, struct sk_buff *skb, struct irlap_info *info);static int irlap_state_reset_wait(struct irlap_cb *self, IRLAP_EVENT event, struct sk_buff *skb, struct irlap_info *info);static int irlap_state_reset (struct irlap_cb *self, IRLAP_EVENT event, struct sk_buff *skb, struct irlap_info *info);static int irlap_state_nrm_s (struct irlap_cb *self, IRLAP_EVENT event, struct sk_buff *skb, struct irlap_info *info);static int irlap_state_xmit_s (struct irlap_cb *self, IRLAP_EVENT event, struct sk_buff *skb, struct irlap_info *info);static int irlap_state_sclose (struct irlap_cb *self, IRLAP_EVENT event, struct sk_buff *skb, struct irlap_info *info);static int irlap_state_reset_check(struct irlap_cb *, IRLAP_EVENT event, struct sk_buff *, struct irlap_info *);#ifdef CONFIG_IRDA_DEBUGstatic const char *irlap_event[] = { "DISCOVERY_REQUEST", "CONNECT_REQUEST", "CONNECT_RESPONSE", "DISCONNECT_REQUEST", "DATA_REQUEST", "RESET_REQUEST", "RESET_RESPONSE", "SEND_I_CMD", "SEND_UI_FRAME", "RECV_DISCOVERY_XID_CMD", "RECV_DISCOVERY_XID_RSP", "RECV_SNRM_CMD", "RECV_TEST_CMD", "RECV_TEST_RSP", "RECV_UA_RSP", "RECV_DM_RSP", "RECV_RD_RSP", "RECV_I_CMD", "RECV_I_RSP", "RECV_UI_FRAME", "RECV_FRMR_RSP", "RECV_RR_CMD", "RECV_RR_RSP", "RECV_RNR_CMD", "RECV_RNR_RSP", "RECV_REJ_CMD", "RECV_REJ_RSP", "RECV_SREJ_CMD", "RECV_SREJ_RSP", "RECV_DISC_CMD", "SLOT_TIMER_EXPIRED", "QUERY_TIMER_EXPIRED", "FINAL_TIMER_EXPIRED", "POLL_TIMER_EXPIRED", "DISCOVERY_TIMER_EXPIRED", "WD_TIMER_EXPIRED", "BACKOFF_TIMER_EXPIRED", "MEDIA_BUSY_TIMER_EXPIRED",};#endif /* CONFIG_IRDA_DEBUG */const char *irlap_state[] = { "LAP_NDM", "LAP_QUERY", "LAP_REPLY", "LAP_CONN", "LAP_SETUP", "LAP_OFFLINE", "LAP_XMIT_P", "LAP_PCLOSE", "LAP_NRM_P", "LAP_RESET_WAIT", "LAP_RESET", "LAP_NRM_S", "LAP_XMIT_S", "LAP_SCLOSE", "LAP_RESET_CHECK",};static int (*state[])(struct irlap_cb *self, IRLAP_EVENT event, struct sk_buff *skb, struct irlap_info *info) ={ irlap_state_ndm, irlap_state_query, irlap_state_reply, irlap_state_conn, irlap_state_setup, irlap_state_offline, irlap_state_xmit_p, irlap_state_pclose, irlap_state_nrm_p, irlap_state_reset_wait, irlap_state_reset, irlap_state_nrm_s, irlap_state_xmit_s, irlap_state_sclose, irlap_state_reset_check,};/* * Function irda_poll_timer_expired (data) * * Poll timer has expired. Normally we must now send a RR frame to the * remote device */static void irlap_poll_timer_expired(void *data){ struct irlap_cb *self = (struct irlap_cb *) data; IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == LAP_MAGIC, return;); irlap_do_event(self, POLL_TIMER_EXPIRED, NULL, NULL);}/* * Calculate and set time before we will have to send back the pf bit * to the peer. Use in primary. * Make sure that state is XMIT_P/XMIT_S when calling this function * (and that nobody messed up with the state). - Jean II */static void irlap_start_poll_timer(struct irlap_cb *self, int timeout){ IRDA_ASSERT(self != NULL, return;); IRDA_ASSERT(self->magic == LAP_MAGIC, return;);#ifdef CONFIG_IRDA_FAST_RR /* * Send out the RR frames faster if our own transmit queue is empty, or * if the peer is busy. The effect is a much faster conversation */ if (skb_queue_empty(&self->txq) || self->remote_busy) { if (self->fast_RR == TRUE) { /* * Assert that the fast poll timer has not reached the * normal poll timer yet */ if (self->fast_RR_timeout < timeout) { /* * FIXME: this should be a more configurable * function */ self->fast_RR_timeout += (sysctl_fast_poll_increase * HZ/1000); /* Use this fast(er) timeout instead */ timeout = self->fast_RR_timeout; } } else { self->fast_RR = TRUE; /* Start with just 0 ms */ self->fast_RR_timeout = 0; timeout = 0; } } else self->fast_RR = FALSE; IRDA_DEBUG(3, "%s(), timeout=%d (%ld)\n", __FUNCTION__, timeout, jiffies);#endif /* CONFIG_IRDA_FAST_RR */ if (timeout == 0) irlap_do_event(self, POLL_TIMER_EXPIRED, NULL, NULL); else irda_start_timer(&self->poll_timer, timeout, self, irlap_poll_timer_expired);}/* * Function irlap_do_event (event, skb, info) * * Rushes through the state machine without any delay. If state == XMIT * then send queued data frames. */void irlap_do_event(struct irlap_cb *self, IRLAP_EVENT event, struct sk_buff *skb, struct irlap_info *info){ int ret; if (!self || self->magic != LAP_MAGIC) return; IRDA_DEBUG(3, "%s(), event = %s, state = %s\n", __FUNCTION__, irlap_event[event], irlap_state[self->state]); ret = (*state[self->state])(self, event, skb, info); /* * Check if there are any pending events that needs to be executed */ switch (self->state) { case LAP_XMIT_P: /* FALLTHROUGH */ case LAP_XMIT_S: /* * We just received the pf bit and are at the beginning * of a new LAP transmit window. * Check if there are any queued data frames, and do not * try to disconnect link if we send any data frames, since * that will change the state away form XMIT */ IRDA_DEBUG(2, "%s() : queue len = %d\n", __FUNCTION__, skb_queue_len(&self->txq)); if (!skb_queue_empty(&self->txq)) { /* Prevent race conditions with irlap_data_request() */ self->local_busy = TRUE; /* Theory of operation. * We send frames up to when we fill the window or * reach line capacity. Those frames will queue up * in the device queue, and the driver will slowly * send them. * After each frame that we send, we poll the higher * layer for more data. It's the right time to do * that because the link layer need to perform the mtt * and then send the first frame, so we can afford * to send a bit of time in kernel space. * The explicit flow indication allow to minimise * buffers (== lower latency), to avoid higher layer * polling via timers (== less context switches) and * to implement a crude scheduler - Jean II */ /* Try to send away all queued data frames */ while ((skb = skb_dequeue(&self->txq)) != NULL) { /* Send one frame */ ret = (*state[self->state])(self, SEND_I_CMD, skb, NULL); /* Drop reference count. * It will be increase as needed in * irlap_send_data_xxx() */ kfree_skb(skb); /* Poll the higher layers for one more frame */ irlmp_flow_indication(self->notify.instance, FLOW_START); if (ret == -EPROTO) break; /* Try again later! */ } /* Finished transmitting */ self->local_busy = FALSE; } else if (self->disconnect_pending) { self->disconnect_pending = FALSE; ret = (*state[self->state])(self, DISCONNECT_REQUEST, NULL, NULL); } break;/* case LAP_NDM: *//* case LAP_CONN: *//* case LAP_RESET_WAIT: *//* case LAP_RESET_CHECK: */ default: break; }}/* * Function irlap_state_ndm (event, skb, frame) * * NDM (Normal Disconnected Mode) state * */static int irlap_state_ndm(struct irlap_cb *self, IRLAP_EVENT event, struct sk_buff *skb, struct irlap_info *info){ discovery_t *discovery_rsp; int ret = 0; IRDA_ASSERT(self != NULL, return -1;); IRDA_ASSERT(self->magic == LAP_MAGIC, return -1;); switch (event) { case CONNECT_REQUEST: IRDA_ASSERT(self->netdev != NULL, return -1;); if (self->media_busy) { /* Note : this will never happen, because we test * media busy in irlap_connect_request() and * postpone the event... - Jean II */ IRDA_DEBUG(0, "%s(), CONNECT_REQUEST: media busy!\n", __FUNCTION__); /* Always switch state before calling upper layers */ irlap_next_state(self, LAP_NDM); irlap_disconnect_indication(self, LAP_MEDIA_BUSY); } else { irlap_send_snrm_frame(self, &self->qos_rx); /* Start Final-bit timer */ irlap_start_final_timer(self, self->final_timeout); self->retry_count = 0; irlap_next_state(self, LAP_SETUP); } break; case RECV_SNRM_CMD: /* Check if the frame contains and I field */ if (info) { self->daddr = info->daddr; self->caddr = info->caddr; irlap_next_state(self, LAP_CONN); irlap_connect_indication(self, skb); } else { IRDA_DEBUG(0, "%s(), SNRM frame does not " "contain an I field!\n", __FUNCTION__); } break; case DISCOVERY_REQUEST: IRDA_ASSERT(info != NULL, return -1;); if (self->media_busy) { IRDA_DEBUG(1, "%s(), DISCOVERY_REQUEST: media busy!\n", __FUNCTION__); /* irlap->log.condition = MEDIA_BUSY; */ /* This will make IrLMP try again */ irlap_discovery_confirm(self, NULL); /* Note : the discovery log is not cleaned up here, * it will be done in irlap_discovery_request() * Jean II */ return 0; } self->S = info->S; 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); irlap_next_state(self, LAP_QUERY); break; case RECV_DISCOVERY_XID_CMD: IRDA_ASSERT(info != NULL, return -1;); /* Assert that this is not the final slot */ if (info->s <= info->S) { self->slot = irlap_generate_rand_time_slot(info->S, info->s); if (self->slot == info->s) { discovery_rsp = irlmp_get_discovery_response(); discovery_rsp->data.daddr = info->daddr; irlap_send_discovery_xid_frame(self, info->S, self->slot, FALSE, discovery_rsp); self->frame_sent = TRUE; } else self->frame_sent = FALSE; /* * Go to reply state until end of discovery to * inhibit our own transmissions. Set the timer * to not stay forever there... Jean II */ irlap_start_query_timer(self, info->S, info->s); irlap_next_state(self, LAP_REPLY); } 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). * Another cause would be devices that do discovery much * slower than us, however the latest fixes should minimise * those cases... * Jean II */ IRDA_DEBUG(1, "%s(), Receiving final discovery request, missed the discovery slots :-(\n", __FUNCTION__); /* Last discovery request -> in the log */ irlap_discovery_indication(self, info->discovery); } break; case MEDIA_BUSY_TIMER_EXPIRED: /* A bunch of events may be postponed because the media is * busy (usually immediately after we close a connection), * or while we are doing discovery (state query/reply). * In all those cases, the media busy flag will be cleared * when it's OK for us to process those postponed events. * This event is not mentioned in the state machines in the * IrLAP spec. It's because they didn't consider Ultra and * postponing connection request is optional. * Jean II */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -