llc_station.c revision 025e36332573177ecd7c12730e18a4390f994f05
1/* 2 * llc_station.c - station component of LLC 3 * 4 * Copyright (c) 1997 by Procom Technology, Inc. 5 * 2001-2003 by Arnaldo Carvalho de Melo <acme@conectiva.com.br> 6 * 7 * This program can be redistributed or modified under the terms of the 8 * GNU General Public License as published by the Free Software Foundation. 9 * This program is distributed without any warranty or implied warranty 10 * of merchantability or fitness for a particular purpose. 11 * 12 * See the GNU General Public License for more details. 13 */ 14#include <linux/init.h> 15#include <linux/module.h> 16#include <linux/slab.h> 17#include <net/llc.h> 18#include <net/llc_sap.h> 19#include <net/llc_conn.h> 20#include <net/llc_c_ac.h> 21#include <net/llc_s_ac.h> 22#include <net/llc_c_ev.h> 23#include <net/llc_c_st.h> 24#include <net/llc_s_ev.h> 25#include <net/llc_s_st.h> 26#include <net/llc_pdu.h> 27 28/** 29 * struct llc_station - LLC station component 30 * 31 * SAP and connection resource manager, one per adapter. 32 * 33 * @mac_sa: MAC source address 34 * @sap_list: list of related SAPs 35 * @ev_q: events entering state mach. 36 * @mac_pdu_q: PDUs ready to send to MAC 37 */ 38struct llc_station { 39 struct { 40 struct sk_buff_head list; 41 spinlock_t lock; 42 } ev_q; 43 struct sk_buff_head mac_pdu_q; 44}; 45 46typedef int (*llc_station_ev_t)(struct sk_buff *skb); 47 48typedef int (*llc_station_action_t)(struct sk_buff *skb); 49 50/* Station component state table structure */ 51struct llc_station_state_trans { 52 llc_station_ev_t ev; 53 llc_station_action_t *ev_actions; 54}; 55 56static struct llc_station llc_main_station; 57 58static int llc_stat_ev_rx_null_dsap_xid_c(struct sk_buff *skb) 59{ 60 struct llc_pdu_un *pdu = llc_pdu_un_hdr(skb); 61 62 return LLC_PDU_IS_CMD(pdu) && /* command PDU */ 63 LLC_PDU_TYPE_IS_U(pdu) && /* U type PDU */ 64 LLC_U_PDU_CMD(pdu) == LLC_1_PDU_CMD_XID && 65 !pdu->dsap ? 0 : 1; /* NULL DSAP value */ 66} 67 68static int llc_stat_ev_rx_null_dsap_test_c(struct sk_buff *skb) 69{ 70 struct llc_pdu_un *pdu = llc_pdu_un_hdr(skb); 71 72 return LLC_PDU_IS_CMD(pdu) && /* command PDU */ 73 LLC_PDU_TYPE_IS_U(pdu) && /* U type PDU */ 74 LLC_U_PDU_CMD(pdu) == LLC_1_PDU_CMD_TEST && 75 !pdu->dsap ? 0 : 1; /* NULL DSAP */ 76} 77 78/** 79 * llc_station_send_pdu - queues PDU to send 80 * @skb: Address of the PDU 81 * 82 * Queues a PDU to send to the MAC layer. 83 */ 84static void llc_station_send_pdu(struct sk_buff *skb) 85{ 86 skb_queue_tail(&llc_main_station.mac_pdu_q, skb); 87 while ((skb = skb_dequeue(&llc_main_station.mac_pdu_q)) != NULL) 88 if (dev_queue_xmit(skb)) 89 break; 90} 91 92static int llc_station_ac_send_xid_r(struct sk_buff *skb) 93{ 94 u8 mac_da[ETH_ALEN], dsap; 95 int rc = 1; 96 struct sk_buff *nskb = llc_alloc_frame(NULL, skb->dev, LLC_PDU_TYPE_U, 97 sizeof(struct llc_xid_info)); 98 99 if (!nskb) 100 goto out; 101 rc = 0; 102 llc_pdu_decode_sa(skb, mac_da); 103 llc_pdu_decode_ssap(skb, &dsap); 104 llc_pdu_header_init(nskb, LLC_PDU_TYPE_U, 0, dsap, LLC_PDU_RSP); 105 llc_pdu_init_as_xid_rsp(nskb, LLC_XID_NULL_CLASS_2, 127); 106 rc = llc_mac_hdr_init(nskb, skb->dev->dev_addr, mac_da); 107 if (unlikely(rc)) 108 goto free; 109 llc_station_send_pdu(nskb); 110out: 111 return rc; 112free: 113 kfree_skb(nskb); 114 goto out; 115} 116 117static int llc_station_ac_send_test_r(struct sk_buff *skb) 118{ 119 u8 mac_da[ETH_ALEN], dsap; 120 int rc = 1; 121 u32 data_size; 122 struct sk_buff *nskb; 123 124 /* The test request command is type U (llc_len = 3) */ 125 data_size = ntohs(eth_hdr(skb)->h_proto) - 3; 126 nskb = llc_alloc_frame(NULL, skb->dev, LLC_PDU_TYPE_U, data_size); 127 128 if (!nskb) 129 goto out; 130 rc = 0; 131 llc_pdu_decode_sa(skb, mac_da); 132 llc_pdu_decode_ssap(skb, &dsap); 133 llc_pdu_header_init(nskb, LLC_PDU_TYPE_U, 0, dsap, LLC_PDU_RSP); 134 llc_pdu_init_as_test_rsp(nskb, skb); 135 rc = llc_mac_hdr_init(nskb, skb->dev->dev_addr, mac_da); 136 if (unlikely(rc)) 137 goto free; 138 llc_station_send_pdu(nskb); 139out: 140 return rc; 141free: 142 kfree_skb(nskb); 143 goto out; 144} 145 146/* state transition for LLC_STATION_EV_RX_NULL_DSAP_XID_C event */ 147static llc_station_action_t llc_stat_up_state_actions_2[] = { 148 [0] = llc_station_ac_send_xid_r, 149 [1] = NULL, 150}; 151 152static struct llc_station_state_trans llc_stat_up_state_trans_2 = { 153 .ev = llc_stat_ev_rx_null_dsap_xid_c, 154 .ev_actions = llc_stat_up_state_actions_2, 155}; 156 157/* state transition for LLC_STATION_EV_RX_NULL_DSAP_TEST_C event */ 158static llc_station_action_t llc_stat_up_state_actions_3[] = { 159 [0] = llc_station_ac_send_test_r, 160 [1] = NULL, 161}; 162 163static struct llc_station_state_trans llc_stat_up_state_trans_3 = { 164 .ev = llc_stat_ev_rx_null_dsap_test_c, 165 .ev_actions = llc_stat_up_state_actions_3, 166}; 167 168/* array of pointers; one to each transition */ 169static struct llc_station_state_trans *llc_stat_up_state_trans [] = { 170 &llc_stat_up_state_trans_2, 171 &llc_stat_up_state_trans_3, 172 NULL, 173}; 174 175/** 176 * llc_exec_station_trans_actions - executes actions for transition 177 * @trans: Address of the transition 178 * @skb: Address of the event that caused the transition 179 * 180 * Executes actions of a transition of the station state machine. Returns 181 * 0 if all actions complete successfully, nonzero otherwise. 182 */ 183static u16 llc_exec_station_trans_actions(struct llc_station_state_trans *trans, 184 struct sk_buff *skb) 185{ 186 u16 rc = 0; 187 llc_station_action_t *next_action = trans->ev_actions; 188 189 for (; next_action && *next_action; next_action++) 190 if ((*next_action)(skb)) 191 rc = 1; 192 return rc; 193} 194 195/** 196 * llc_find_station_trans - finds transition for this event 197 * @skb: Address of the event 198 * 199 * Search thru events of the current state of the station until list 200 * exhausted or it's obvious that the event is not valid for the current 201 * state. Returns the address of the transition if cound, %NULL otherwise. 202 */ 203static struct llc_station_state_trans * 204 llc_find_station_trans(struct sk_buff *skb) 205{ 206 int i = 0; 207 struct llc_station_state_trans *rc = NULL; 208 struct llc_station_state_trans **next_trans; 209 210 for (next_trans = llc_stat_up_state_trans; next_trans[i]; i++) 211 if (!next_trans[i]->ev(skb)) { 212 rc = next_trans[i]; 213 break; 214 } 215 return rc; 216} 217 218/** 219 * llc_station_free_ev - frees an event 220 * @skb: Address of the event 221 * 222 * Frees an event. 223 */ 224static void llc_station_free_ev(struct sk_buff *skb) 225{ 226 kfree_skb(skb); 227} 228 229/** 230 * llc_station_next_state - processes event and goes to the next state 231 * @skb: Address of the event 232 * 233 * Processes an event, executes any transitions related to that event and 234 * updates the state of the station. 235 */ 236static u16 llc_station_next_state(struct sk_buff *skb) 237{ 238 u16 rc = 1; 239 struct llc_station_state_trans *trans; 240 241 trans = llc_find_station_trans(skb); 242 if (trans) 243 /* got the state to which we next transition; perform the 244 * actions associated with this transition before actually 245 * transitioning to the next state 246 */ 247 rc = llc_exec_station_trans_actions(trans, skb); 248 else 249 /* event not recognized in current state; re-queue it for 250 * processing again at a later time; return failure 251 */ 252 rc = 0; 253 llc_station_free_ev(skb); 254 return rc; 255} 256 257/** 258 * llc_station_service_events - service events in the queue 259 * 260 * Get an event from the station event queue (if any); attempt to service 261 * the event; if event serviced, get the next event (if any) on the event 262 * queue; if event not service, re-queue the event on the event queue and 263 * attempt to service the next event; when serviced all events in queue, 264 * finished; if don't transition to different state, just service all 265 * events once; if transition to new state, service all events again. 266 * Caller must hold llc_main_station.ev_q.lock. 267 */ 268static void llc_station_service_events(void) 269{ 270 struct sk_buff *skb; 271 272 while ((skb = skb_dequeue(&llc_main_station.ev_q.list)) != NULL) 273 llc_station_next_state(skb); 274} 275 276/** 277 * llc_station_state_process - queue event and try to process queue. 278 * @skb: Address of the event 279 * 280 * Queues an event (on the station event queue) for handling by the 281 * station state machine and attempts to process any queued-up events. 282 */ 283static void llc_station_state_process(struct sk_buff *skb) 284{ 285 spin_lock_bh(&llc_main_station.ev_q.lock); 286 skb_queue_tail(&llc_main_station.ev_q.list, skb); 287 llc_station_service_events(); 288 spin_unlock_bh(&llc_main_station.ev_q.lock); 289} 290 291/** 292 * llc_station_rcv - send received pdu to the station state machine 293 * @skb: received frame. 294 * 295 * Sends data unit to station state machine. 296 */ 297static void llc_station_rcv(struct sk_buff *skb) 298{ 299 llc_station_state_process(skb); 300} 301 302void __init llc_station_init(void) 303{ 304 skb_queue_head_init(&llc_main_station.mac_pdu_q); 305 skb_queue_head_init(&llc_main_station.ev_q.list); 306 spin_lock_init(&llc_main_station.ev_q.lock); 307 llc_set_station_handler(llc_station_rcv); 308} 309 310void llc_station_exit(void) 311{ 312 llc_set_station_handler(NULL); 313} 314