| /********************************************************************* | |
| * | |
| * Filename: iriap_event.c | |
| * Version: 0.1 | |
| * Description: IAP Finite State Machine | |
| * Status: Experimental. | |
| * Author: Dag Brattli <dagb@cs.uit.no> | |
| * Created at: Thu Aug 21 00:02:07 1997 | |
| * Modified at: Wed Mar 1 11:28:34 2000 | |
| * Modified by: Dag Brattli <dagb@cs.uit.no> | |
| * | |
| * Copyright (c) 1997, 1999-2000 Dag Brattli <dagb@cs.uit.no>, | |
| * 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/slab.h> | |
| #include <net/irda/irda.h> | |
| #include <net/irda/irlmp.h> | |
| #include <net/irda/iriap.h> | |
| #include <net/irda/iriap_event.h> | |
| static void state_s_disconnect (struct iriap_cb *self, IRIAP_EVENT event, | |
| struct sk_buff *skb); | |
| static void state_s_connecting (struct iriap_cb *self, IRIAP_EVENT event, | |
| struct sk_buff *skb); | |
| static void state_s_call (struct iriap_cb *self, IRIAP_EVENT event, | |
| struct sk_buff *skb); | |
| static void state_s_make_call (struct iriap_cb *self, IRIAP_EVENT event, | |
| struct sk_buff *skb); | |
| static void state_s_calling (struct iriap_cb *self, IRIAP_EVENT event, | |
| struct sk_buff *skb); | |
| static void state_s_outstanding (struct iriap_cb *self, IRIAP_EVENT event, | |
| struct sk_buff *skb); | |
| static void state_s_replying (struct iriap_cb *self, IRIAP_EVENT event, | |
| struct sk_buff *skb); | |
| static void state_s_wait_for_call(struct iriap_cb *self, IRIAP_EVENT event, | |
| struct sk_buff *skb); | |
| static void state_s_wait_active (struct iriap_cb *self, IRIAP_EVENT event, | |
| struct sk_buff *skb); | |
| static void state_r_disconnect (struct iriap_cb *self, IRIAP_EVENT event, | |
| struct sk_buff *skb); | |
| static void state_r_call (struct iriap_cb *self, IRIAP_EVENT event, | |
| struct sk_buff *skb); | |
| static void state_r_waiting (struct iriap_cb *self, IRIAP_EVENT event, | |
| struct sk_buff *skb); | |
| static void state_r_wait_active (struct iriap_cb *self, IRIAP_EVENT event, | |
| struct sk_buff *skb); | |
| static void state_r_receiving (struct iriap_cb *self, IRIAP_EVENT event, | |
| struct sk_buff *skb); | |
| static void state_r_execute (struct iriap_cb *self, IRIAP_EVENT event, | |
| struct sk_buff *skb); | |
| static void state_r_returning (struct iriap_cb *self, IRIAP_EVENT event, | |
| struct sk_buff *skb); | |
| static void (*iriap_state[])(struct iriap_cb *self, IRIAP_EVENT event, | |
| struct sk_buff *skb) = { | |
| /* Client FSM */ | |
| state_s_disconnect, | |
| state_s_connecting, | |
| state_s_call, | |
| /* S-Call FSM */ | |
| state_s_make_call, | |
| state_s_calling, | |
| state_s_outstanding, | |
| state_s_replying, | |
| state_s_wait_for_call, | |
| state_s_wait_active, | |
| /* Server FSM */ | |
| state_r_disconnect, | |
| state_r_call, | |
| /* R-Connect FSM */ | |
| state_r_waiting, | |
| state_r_wait_active, | |
| state_r_receiving, | |
| state_r_execute, | |
| state_r_returning, | |
| }; | |
| void iriap_next_client_state(struct iriap_cb *self, IRIAP_STATE state) | |
| { | |
| IRDA_ASSERT(self != NULL, return;); | |
| IRDA_ASSERT(self->magic == IAS_MAGIC, return;); | |
| self->client_state = state; | |
| } | |
| void iriap_next_call_state(struct iriap_cb *self, IRIAP_STATE state) | |
| { | |
| IRDA_ASSERT(self != NULL, return;); | |
| IRDA_ASSERT(self->magic == IAS_MAGIC, return;); | |
| self->call_state = state; | |
| } | |
| void iriap_next_server_state(struct iriap_cb *self, IRIAP_STATE state) | |
| { | |
| IRDA_ASSERT(self != NULL, return;); | |
| IRDA_ASSERT(self->magic == IAS_MAGIC, return;); | |
| self->server_state = state; | |
| } | |
| void iriap_next_r_connect_state(struct iriap_cb *self, IRIAP_STATE state) | |
| { | |
| IRDA_ASSERT(self != NULL, return;); | |
| IRDA_ASSERT(self->magic == IAS_MAGIC, return;); | |
| self->r_connect_state = state; | |
| } | |
| void iriap_do_client_event(struct iriap_cb *self, IRIAP_EVENT event, | |
| struct sk_buff *skb) | |
| { | |
| IRDA_ASSERT(self != NULL, return;); | |
| IRDA_ASSERT(self->magic == IAS_MAGIC, return;); | |
| (*iriap_state[ self->client_state]) (self, event, skb); | |
| } | |
| void iriap_do_call_event(struct iriap_cb *self, IRIAP_EVENT event, | |
| struct sk_buff *skb) | |
| { | |
| IRDA_ASSERT(self != NULL, return;); | |
| IRDA_ASSERT(self->magic == IAS_MAGIC, return;); | |
| (*iriap_state[ self->call_state]) (self, event, skb); | |
| } | |
| void iriap_do_server_event(struct iriap_cb *self, IRIAP_EVENT event, | |
| struct sk_buff *skb) | |
| { | |
| IRDA_ASSERT(self != NULL, return;); | |
| IRDA_ASSERT(self->magic == IAS_MAGIC, return;); | |
| (*iriap_state[ self->server_state]) (self, event, skb); | |
| } | |
| void iriap_do_r_connect_event(struct iriap_cb *self, IRIAP_EVENT event, | |
| struct sk_buff *skb) | |
| { | |
| IRDA_ASSERT(self != NULL, return;); | |
| IRDA_ASSERT(self->magic == IAS_MAGIC, return;); | |
| (*iriap_state[ self->r_connect_state]) (self, event, skb); | |
| } | |
| /* | |
| * Function state_s_disconnect (event, skb) | |
| * | |
| * S-Disconnect, The device has no LSAP connection to a particular | |
| * remote device. | |
| */ | |
| static void state_s_disconnect(struct iriap_cb *self, IRIAP_EVENT event, | |
| struct sk_buff *skb) | |
| { | |
| IRDA_ASSERT(self != NULL, return;); | |
| IRDA_ASSERT(self->magic == IAS_MAGIC, return;); | |
| switch (event) { | |
| case IAP_CALL_REQUEST_GVBC: | |
| iriap_next_client_state(self, S_CONNECTING); | |
| IRDA_ASSERT(self->request_skb == NULL, return;); | |
| /* Don't forget to refcount it - | |
| * see iriap_getvaluebyclass_request(). */ | |
| skb_get(skb); | |
| self->request_skb = skb; | |
| iriap_connect_request(self); | |
| break; | |
| case IAP_LM_DISCONNECT_INDICATION: | |
| break; | |
| default: | |
| pr_debug("%s(), Unknown event %d\n", __func__, event); | |
| break; | |
| } | |
| } | |
| /* | |
| * Function state_s_connecting (self, event, skb) | |
| * | |
| * S-Connecting | |
| * | |
| */ | |
| static void state_s_connecting(struct iriap_cb *self, IRIAP_EVENT event, | |
| struct sk_buff *skb) | |
| { | |
| IRDA_ASSERT(self != NULL, return;); | |
| IRDA_ASSERT(self->magic == IAS_MAGIC, return;); | |
| switch (event) { | |
| case IAP_LM_CONNECT_CONFIRM: | |
| /* | |
| * Jump to S-Call FSM | |
| */ | |
| iriap_do_call_event(self, IAP_CALL_REQUEST, skb); | |
| /* iriap_call_request(self, 0,0,0); */ | |
| iriap_next_client_state(self, S_CALL); | |
| break; | |
| case IAP_LM_DISCONNECT_INDICATION: | |
| /* Abort calls */ | |
| iriap_next_call_state(self, S_MAKE_CALL); | |
| iriap_next_client_state(self, S_DISCONNECT); | |
| break; | |
| default: | |
| pr_debug("%s(), Unknown event %d\n", __func__, event); | |
| break; | |
| } | |
| } | |
| /* | |
| * Function state_s_call (self, event, skb) | |
| * | |
| * S-Call, The device can process calls to a specific remote | |
| * device. Whenever the LSAP connection is disconnected, this state | |
| * catches that event and clears up | |
| */ | |
| static void state_s_call(struct iriap_cb *self, IRIAP_EVENT event, | |
| struct sk_buff *skb) | |
| { | |
| IRDA_ASSERT(self != NULL, return;); | |
| switch (event) { | |
| case IAP_LM_DISCONNECT_INDICATION: | |
| /* Abort calls */ | |
| iriap_next_call_state(self, S_MAKE_CALL); | |
| iriap_next_client_state(self, S_DISCONNECT); | |
| break; | |
| default: | |
| pr_debug("state_s_call: Unknown event %d\n", event); | |
| break; | |
| } | |
| } | |
| /* | |
| * Function state_s_make_call (event, skb) | |
| * | |
| * S-Make-Call | |
| * | |
| */ | |
| static void state_s_make_call(struct iriap_cb *self, IRIAP_EVENT event, | |
| struct sk_buff *skb) | |
| { | |
| struct sk_buff *tx_skb; | |
| IRDA_ASSERT(self != NULL, return;); | |
| switch (event) { | |
| case IAP_CALL_REQUEST: | |
| /* Already refcounted - see state_s_disconnect() */ | |
| tx_skb = self->request_skb; | |
| self->request_skb = NULL; | |
| irlmp_data_request(self->lsap, tx_skb); | |
| iriap_next_call_state(self, S_OUTSTANDING); | |
| break; | |
| default: | |
| pr_debug("%s(), Unknown event %d\n", __func__, event); | |
| break; | |
| } | |
| } | |
| /* | |
| * Function state_s_calling (event, skb) | |
| * | |
| * S-Calling | |
| * | |
| */ | |
| static void state_s_calling(struct iriap_cb *self, IRIAP_EVENT event, | |
| struct sk_buff *skb) | |
| { | |
| pr_debug("%s(), Not implemented\n", __func__); | |
| } | |
| /* | |
| * Function state_s_outstanding (event, skb) | |
| * | |
| * S-Outstanding, The device is waiting for a response to a command | |
| * | |
| */ | |
| static void state_s_outstanding(struct iriap_cb *self, IRIAP_EVENT event, | |
| struct sk_buff *skb) | |
| { | |
| IRDA_ASSERT(self != NULL, return;); | |
| switch (event) { | |
| case IAP_RECV_F_LST: | |
| /*iriap_send_ack(self);*/ | |
| /*LM_Idle_request(idle); */ | |
| iriap_next_call_state(self, S_WAIT_FOR_CALL); | |
| break; | |
| default: | |
| pr_debug("%s(), Unknown event %d\n", __func__, event); | |
| break; | |
| } | |
| } | |
| /* | |
| * Function state_s_replying (event, skb) | |
| * | |
| * S-Replying, The device is collecting a multiple part response | |
| */ | |
| static void state_s_replying(struct iriap_cb *self, IRIAP_EVENT event, | |
| struct sk_buff *skb) | |
| { | |
| pr_debug("%s(), Not implemented\n", __func__); | |
| } | |
| /* | |
| * Function state_s_wait_for_call (event, skb) | |
| * | |
| * S-Wait-for-Call | |
| * | |
| */ | |
| static void state_s_wait_for_call(struct iriap_cb *self, IRIAP_EVENT event, | |
| struct sk_buff *skb) | |
| { | |
| pr_debug("%s(), Not implemented\n", __func__); | |
| } | |
| /* | |
| * Function state_s_wait_active (event, skb) | |
| * | |
| * S-Wait-Active | |
| * | |
| */ | |
| static void state_s_wait_active(struct iriap_cb *self, IRIAP_EVENT event, | |
| struct sk_buff *skb) | |
| { | |
| pr_debug("%s(), Not implemented\n", __func__); | |
| } | |
| /************************************************************************** | |
| * | |
| * Server FSM | |
| * | |
| **************************************************************************/ | |
| /* | |
| * Function state_r_disconnect (self, event, skb) | |
| * | |
| * LM-IAS server is disconnected (not processing any requests!) | |
| * | |
| */ | |
| static void state_r_disconnect(struct iriap_cb *self, IRIAP_EVENT event, | |
| struct sk_buff *skb) | |
| { | |
| struct sk_buff *tx_skb; | |
| switch (event) { | |
| case IAP_LM_CONNECT_INDICATION: | |
| tx_skb = alloc_skb(LMP_MAX_HEADER, GFP_ATOMIC); | |
| if (tx_skb == NULL) | |
| return; | |
| /* Reserve space for MUX_CONTROL and LAP header */ | |
| skb_reserve(tx_skb, LMP_MAX_HEADER); | |
| irlmp_connect_response(self->lsap, tx_skb); | |
| /*LM_Idle_request(idle); */ | |
| iriap_next_server_state(self, R_CALL); | |
| /* | |
| * Jump to R-Connect FSM, we skip R-Waiting since we do not | |
| * care about LM_Idle_request()! | |
| */ | |
| iriap_next_r_connect_state(self, R_RECEIVING); | |
| break; | |
| default: | |
| pr_debug("%s(), unknown event %d\n", __func__, event); | |
| break; | |
| } | |
| } | |
| /* | |
| * Function state_r_call (self, event, skb) | |
| */ | |
| static void state_r_call(struct iriap_cb *self, IRIAP_EVENT event, | |
| struct sk_buff *skb) | |
| { | |
| switch (event) { | |
| case IAP_LM_DISCONNECT_INDICATION: | |
| /* Abort call */ | |
| iriap_next_server_state(self, R_DISCONNECT); | |
| iriap_next_r_connect_state(self, R_WAITING); | |
| break; | |
| default: | |
| pr_debug("%s(), unknown event!\n", __func__); | |
| break; | |
| } | |
| } | |
| /* | |
| * R-Connect FSM | |
| */ | |
| /* | |
| * Function state_r_waiting (self, event, skb) | |
| */ | |
| static void state_r_waiting(struct iriap_cb *self, IRIAP_EVENT event, | |
| struct sk_buff *skb) | |
| { | |
| pr_debug("%s(), Not implemented\n", __func__); | |
| } | |
| static void state_r_wait_active(struct iriap_cb *self, IRIAP_EVENT event, | |
| struct sk_buff *skb) | |
| { | |
| pr_debug("%s(), Not implemented\n", __func__); | |
| } | |
| /* | |
| * Function state_r_receiving (self, event, skb) | |
| * | |
| * We are receiving a command | |
| * | |
| */ | |
| static void state_r_receiving(struct iriap_cb *self, IRIAP_EVENT event, | |
| struct sk_buff *skb) | |
| { | |
| switch (event) { | |
| case IAP_RECV_F_LST: | |
| iriap_next_r_connect_state(self, R_EXECUTE); | |
| iriap_call_indication(self, skb); | |
| break; | |
| default: | |
| pr_debug("%s(), unknown event!\n", __func__); | |
| break; | |
| } | |
| } | |
| /* | |
| * Function state_r_execute (self, event, skb) | |
| * | |
| * The server is processing the request | |
| * | |
| */ | |
| static void state_r_execute(struct iriap_cb *self, IRIAP_EVENT event, | |
| struct sk_buff *skb) | |
| { | |
| IRDA_ASSERT(skb != NULL, return;); | |
| IRDA_ASSERT(self != NULL, return;); | |
| IRDA_ASSERT(self->magic == IAS_MAGIC, return;); | |
| switch (event) { | |
| case IAP_CALL_RESPONSE: | |
| /* | |
| * Since we don't implement the Waiting state, we return | |
| * to state Receiving instead, DB. | |
| */ | |
| iriap_next_r_connect_state(self, R_RECEIVING); | |
| /* Don't forget to refcount it - see | |
| * iriap_getvaluebyclass_response(). */ | |
| skb_get(skb); | |
| irlmp_data_request(self->lsap, skb); | |
| break; | |
| default: | |
| pr_debug("%s(), unknown event!\n", __func__); | |
| break; | |
| } | |
| } | |
| static void state_r_returning(struct iriap_cb *self, IRIAP_EVENT event, | |
| struct sk_buff *skb) | |
| { | |
| pr_debug("%s(), event=%d\n", __func__, event); | |
| switch (event) { | |
| case IAP_RECV_F_LST: | |
| break; | |
| default: | |
| break; | |
| } | |
| } |