Commit 466f66aa authored by David S. Miller's avatar David S. Miller

By hand merge.

parents 0bf34ffb 34beb106
...@@ -954,6 +954,11 @@ W: http://www.linuxppc.org/ ...@@ -954,6 +954,11 @@ W: http://www.linuxppc.org/
L: linuxppc-dev@lists.linuxppc.org L: linuxppc-dev@lists.linuxppc.org
S: Maintained S: Maintained
LLC (802.2)
P: Arnaldo Carvalho de Melo
M: acme@conectiva.com.br
S: Maintained
LINUX FOR 64BIT POWERPC LINUX FOR 64BIT POWERPC
P: David Engebretsen P: David Engebretsen
M: engebret@us.ibm.com M: engebret@us.ibm.com
......
...@@ -28,13 +28,15 @@ ...@@ -28,13 +28,15 @@
* load-locked/store-conditional cpus (ALPHA/MIPS/PPC) and * load-locked/store-conditional cpus (ALPHA/MIPS/PPC) and
* compare-and-swap cpus (Sparc64). So we control which * compare-and-swap cpus (Sparc64). So we control which
* implementation to use with a __BRLOCK_USE_ATOMICS define. -DaveM * implementation to use with a __BRLOCK_USE_ATOMICS define. -DaveM
*
* Added BR_LLC_LOCK for use in net/core/ext8022.c -acme
*/ */
/* Register bigreader lock indices here. */ /* Register bigreader lock indices here. */
enum brlock_indices { enum brlock_indices {
BR_GLOBALIRQ_LOCK, BR_GLOBALIRQ_LOCK,
BR_NETPROTO_LOCK, BR_NETPROTO_LOCK,
BR_LLC_LOCK,
__BR_END __BR_END
}; };
......
#ifndef __LINUX_LLC_H
#define __LINUX_LLC_H
/*
* IEEE 802.2 User Interface SAPs for Linux, data structures and indicators.
*
* Copyright (c) 2001 by Jay Schulist <jschlst@samba.org>
*
* This program can be redistributed or modified under the terms of the
* GNU General Public License as published by the Free Software Foundation.
* This program is distributed without any warranty or implied warranty
* of merchantability or fitness for a particular purpose.
*
* See the GNU General Public License for more details.
*/
#define __LLC_SOCK_SIZE__ 28 /* sizeof(sockaddr_llc), word align. */
struct sockaddr_llc {
sa_family_t sllc_family; /* AF_LLC */
sa_family_t sllc_arphrd; /* ARPHRD_ETHER */
unsigned char sllc_test;
unsigned char sllc_xid;
unsigned char sllc_ua; /* UA data, only for SOCK_STREAM. */
unsigned char sllc_dsap;
unsigned char sllc_ssap;
unsigned char sllc_dmac[IFHWADDRLEN];
unsigned char sllc_smac[IFHWADDRLEN];
unsigned char sllc_mmac[IFHWADDRLEN];
unsigned char __pad[__LLC_SOCK_SIZE__ - sizeof(sa_family_t) * 2 -
sizeof(unsigned char) * 5 - IFHWADDRLEN * 3];
};
/* sockopt definitions. */
enum llc_sockopts {
LLC_OPT_UNKNOWN = 0,
LLC_OPT_RETRY, /* max retrans attempts. */
LLC_OPT_SIZE, /* max PDU size (octets). */
LLC_OPT_ACK_TMR_EXP, /* ack expire time (secs). */
LLC_OPT_P_TMR_EXP, /* pf cycle expire time (secs). */
LLC_OPT_REJ_TMR_EXP, /* rej sent expire time (secs). */
LLC_OPT_BUSY_TMR_EXP, /* busy state expire time (secs). */
LLC_OPT_TX_WIN, /* tx window size. */
LLC_OPT_RX_WIN, /* rx window size. */
LLC_OPT_MAX
};
#define LLC_OPT_MAX_RETRY 100
#define LLC_OPT_MAX_SIZE 4196
#define LLC_OPT_MAX_WIN 127
#define LLC_OPT_MAX_ACK_TMR_EXP 60
#define LLC_OPT_MAX_P_TMR_EXP 60
#define LLC_OPT_MAX_REJ_TMR_EXP 60
#define LLC_OPT_MAX_BUSY_TMR_EXP 60
/* LLC SAP types. */
#define LLC_SAP_NULL 0x00 /* NULL SAP. */
#define LLC_SAP_LLC 0x02 /* LLC Sublayer Managment. */
#define LLC_SAP_SNA 0x04 /* SNA Path Control. */
#define LLC_SAP_PNM 0x0E /* Proway Network Managment. */
#define LLC_SAP_IP 0x06 /* TCP/IP. */
#define LLC_SAP_BSPAN 0x42 /* Bridge Spanning Tree Proto */
#define LLC_SAP_MMS 0x4E /* Manufacturing Message Srv. */
#define LLC_SAP_8208 0x7E /* ISO 8208 */
#define LLC_SAP_3COM 0x80 /* 3COM. */
#define LLC_SAP_PRO 0x8E /* Proway Active Station List */
#define LLC_SAP_SNAP 0xAA /* SNAP. */
#define LLC_SAP_BANYAN 0xBC /* Banyan. */
#define LLC_SAP_IPX 0xE0 /* IPX/SPX. */
#define LLC_SAP_NETBEUI 0xF0 /* NetBEUI. */
#define LLC_SAP_LANMGR 0xF4 /* LanManager. */
#define LLC_SAP_IMPL 0xF8 /* IMPL */
#define LLC_SAP_DISC 0xFC /* Discovery */
#define LLC_SAP_OSI 0xFE /* OSI Network Layers. */
#define LLC_SAP_LAR 0xDC /* LAN Address Resolution */
#define LLC_SAP_RM 0xD4 /* Resource Management */
#define LLC_SAP_GLOBAL 0xFF /* Global SAP. */
#ifdef __KERNEL__
#define LLC_SAP_DYN_START 0xC0
#define LLC_SAP_DYN_STOP 0xDE
#define LLC_SAP_DYN_TRIES 4
struct sock;
struct llc_ui_opt {
u16 link; /* network layer link number */
struct llc_sap *sap; /* pointer to parent SAP */
struct sock *core_sk;
struct net_device *dev; /* device to send to remote */
struct sockaddr_llc addr; /* address sock is bound to */
};
#define llc_ui_sk(__sk) ((struct llc_ui_opt *)(__sk)->protinfo)
#define llc_ui_skb_cb(__skb) ((struct sockaddr_llc *)&((__skb)->cb[0]))
#ifdef CONFIG_LLC_UI
extern int llc_ui_init(void);
extern void llc_ui_exit(void);
#else
#define llc_ui_init()
#define llc_ui_exit()
#endif
#endif /* __KERNEL__ */
#endif /* __LINUX_LLC_H */
...@@ -5,7 +5,16 @@ struct datalink_proto { ...@@ -5,7 +5,16 @@ struct datalink_proto {
unsigned short type_len; unsigned short type_len;
unsigned char type[8]; unsigned char type[8];
const char *string_name; const char *string_name;
union {
struct llc_pinfo *llc;
} ll_pinfo;
struct llc_sc_info *llc_sc;
struct sock *sock;
unsigned short header_length; unsigned short header_length;
int (*rcvfunc)(struct sk_buff *, struct net_device *, int (*rcvfunc)(struct sk_buff *, struct net_device *,
struct packet_type *); struct packet_type *);
void (*datalink_header)(struct datalink_proto *, struct sk_buff *, void (*datalink_header)(struct datalink_proto *, struct sk_buff *,
......
#ifndef LLC_ACTN_H
#define LLC_ACTN_H
/*
* Copyright (c) 1997 by Procom Technology,Inc.
* 2001 by Arnaldo Carvalho de Melo <acme@conectiva.com.br>
*
* This program can be redistributed or modified under the terms of the
* GNU General Public License as published by the Free Software Foundation.
* This program is distributed without any warranty or implied warranty
* of merchantability or fitness for a particular purpose.
*
* See the GNU General Public License for more details.
*/
/* Station component state transition actions */
#define LLC_STATION_AC_START_ACK_TMR 1
#define LLC_STATION_AC_SET_RETRY_CNT_0 2
#define LLC_STATION_AC_INC_RETRY_CNT_BY_1 3
#define LLC_STATION_AC_SET_XID_R_CNT_0 4
#define LLC_STATION_AC_INC_XID_R_CNT_BY_1 5
#define LLC_STATION_AC_SEND_NULL_DSAP_XID_C 6
#define LLC_STATION_AC_SEND_XID_R 7
#define LLC_STATION_AC_SEND_TEST_R 8
#define LLC_STATION_AC_REPORT_STATUS 9
/* All station state event action functions look like this */
typedef int (*llc_station_action_t)(struct llc_station *station,
struct llc_station_state_ev *ev);
extern int llc_station_ac_start_ack_timer(struct llc_station *station,
struct llc_station_state_ev *ev);
extern int llc_station_ac_set_retry_cnt_0(struct llc_station *station,
struct llc_station_state_ev *ev);
extern int llc_station_ac_inc_retry_cnt_by_1(struct llc_station *station,
struct llc_station_state_ev *ev);
extern int llc_station_ac_set_xid_r_cnt_0(struct llc_station *station,
struct llc_station_state_ev *ev);
extern int llc_station_ac_inc_xid_r_cnt_by_1(struct llc_station *station,
struct llc_station_state_ev *ev);
extern int llc_station_ac_send_null_dsap_xid_c(struct llc_station *station,
struct llc_station_state_ev *ev);
extern int llc_station_ac_send_xid_r(struct llc_station *station,
struct llc_station_state_ev *ev);
extern int llc_station_ac_send_test_r(struct llc_station *station,
struct llc_station_state_ev *ev);
extern int llc_station_ac_report_status(struct llc_station *station,
struct llc_station_state_ev *ev);
extern int llc_station_ac_report_status(struct llc_station *station,
struct llc_station_state_ev *ev);
#endif /* LLC_ACTN_H */
#ifndef LLC_C_AC_H
#define LLC_C_AC_H
/*
* Copyright (c) 1997 by Procom Technology,Inc.
* 2001 by Arnaldo Carvalho de Melo <acme@conectiva.com.br>
*
* This program can be redistributed or modified under the terms of the
* GNU General Public License as published by the Free Software Foundation.
* This program is distributed without any warranty or implied warranty
* of merchantability or fitness for a particular purpose.
*
* See the GNU General Public License for more details.
*/
/* Connection component state transition actions */
/*
* Connection state transition actions
* (Fb = F bit; Pb = P bit; Xb = X bit)
*/
#define LLC_CONN_AC_CLR_REMOTE_BUSY 1
#define LLC_CONN_AC_CONN_IND 2
#define LLC_CONN_AC_CONN_CONFIRM 3
#define LLC_CONN_AC_DATA_IND 4
#define LLC_CONN_AC_DISC_IND 5
#define LLC_CONN_AC_RESET_IND 6
#define LLC_CONN_AC_RESET_CONFIRM 7
#define LLC_CONN_AC_REPORT_STATUS 8
#define LLC_CONN_AC_CLR_REMOTE_BUSY_IF_Fb_EQ_1 9
#define LLC_CONN_AC_STOP_REJ_TMR_IF_DATA_FLAG_EQ_2 10
#define LLC_CONN_AC_SEND_DISC_CMD_Pb_SET_X 11
#define LLC_CONN_AC_SEND_DM_RSP_Fb_SET_Pb 12
#define LLC_CONN_AC_SEND_DM_RSP_Fb_SET_1 13
#define LLC_CONN_AC_SEND_DM_RSP_Fb_SET_F_FLAG 14
#define LLC_CONN_AC_SEND_FRMR_RSP_Fb_SET_X 15
#define LLC_CONN_AC_RESEND_FRMR_RSP_Fb_SET_0 16
#define LLC_CONN_AC_RESEND_FRMR_RSP_Fb_SET_Pb 17
#define LLC_CONN_AC_SEND_I_CMD_Pb_SET_1 18
#define LLC_CONN_AC_RESEND_I_CMD_Pb_SET_1 19
#define LLC_CONN_AC_RESEND_I_CMD_Pb_SET_1_OR_SEND_RR 20
#define LLC_CONN_AC_SEND_I_XXX_Xb_SET_0 21
#define LLC_CONN_AC_RESEND_I_XXX_Xb_SET_0 22
#define LLC_CONN_AC_RESEND_I_XXX_Xb_SET_0_OR_SEND_RR 23
#define LLC_CONN_AC_RESEND_I_RSP_Fb_SET_1 24
#define LLC_CONN_AC_SEND_REJ_CMD_Pb_SET_1 25
#define LLC_CONN_AC_SEND_REJ_RSP_Fb_SET_1 26
#define LLC_CONN_AC_SEND_REJ_XXX_Xb_SET_0 27
#define LLC_CONN_AC_SEND_RNR_CMD_Pb_SET_1 28
#define LLC_CONN_AC_SEND_RNR_RSP_Fb_SET_1 29
#define LLC_CONN_AC_SEND_RNR_XXX_Xb_SET_0 30
#define LLC_CONN_AC_SET_REMOTE_BUSY 31
#define LLC_CONN_AC_OPTIONAL_SEND_RNR_XXX_Xb_SET_0 32
#define LLC_CONN_AC_SEND_RR_CMD_Pb_SET_1 33
#define LLC_CONN_AC_SEND_ACK_CMD_Pb_SET_1 34
#define LLC_CONN_AC_SEND_RR_RSP_Fb_SET_1 35
#define LLC_CONN_AC_SEND_ACK_RSP_Fb_SET_1 36
#define LLC_CONN_AC_SEND_RR_XXX_Xb_SET_0 37
#define LLC_CONN_AC_SEND_ACK_XXX_Xb_SET_0 38
#define LLC_CONN_AC_SEND_SABME_CMD_Pb_SET_X 39
#define LLC_CONN_AC_SEND_UA_RSP_Fb_SET_Pb 40
#define LLC_CONN_AC_SEND_UA_RSP_Fb_SET_F_FLAG 41
#define LLC_CONN_AC_S_FLAG_SET_0 42
#define LLC_CONN_AC_S_FLAG_SET_1 43
#define LLC_CONN_AC_START_P_TMR 44
#define LLC_CONN_AC_START_ACK_TMR 45
#define LLC_CONN_AC_START_REJ_TMR 46
#define LLC_CONN_AC_START_ACK_TMR_IF_NOT_RUNNING 47
#define LLC_CONN_AC_STOP_ACK_TMR 48
#define LLC_CONN_AC_STOP_P_TMR 49
#define LLC_CONN_AC_STOP_REJ_TMR 50
#define LLC_CONN_AC_STOP_ALL_TMRS 51
#define LLC_CONN_AC_STOP_OTHER_TMRS 52
#define LLC_CONN_AC_UPDATE_Nr_RECEIVED 53
#define LLC_CONN_AC_UPDATE_P_FLAG 54
#define LLC_CONN_AC_DATA_FLAG_SET_2 55
#define LLC_CONN_AC_DATA_FLAG_SET_0 56
#define LLC_CONN_AC_DATA_FLAG_SET_1 57
#define LLC_CONN_AC_DATA_FLAG_SET_1_IF_DATA_FLAG_EQ_0 58
#define LLC_CONN_AC_P_FLAG_SET_0 59
#define LLC_CONN_AC_P_FLAG_SET_P 60
#define LLC_CONN_AC_REMOTE_BUSY_SET_0 61
#define LLC_CONN_AC_RETRY_CNT_SET_0 62
#define LLC_CONN_AC_RETRY_CNT_INC_BY_1 63
#define LLC_CONN_AC_Vr_SET_0 64
#define LLC_CONN_AC_Vr_INC_BY_1 65
#define LLC_CONN_AC_Vs_SET_0 66
#define LLC_CONN_AC_Vs_SET_Nr 67
#define LLC_CONN_AC_F_FLAG_SET_P 68
#define LLC_CONN_AC_STOP_SENDACK_TMR 70
#define LLC_CONN_AC_START_SENDACK_TMR_IF_NOT_RUNNING 71
typedef int (*llc_conn_action_t)(struct sock *sk, struct llc_conn_state_ev *ev);
extern int llc_conn_ac_clear_remote_busy(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_conn_ind(struct sock *sk, struct llc_conn_state_ev *ev);
extern int llc_conn_ac_conn_confirm(struct sock* sk, struct llc_conn_state_ev *ev);
extern int llc_conn_ac_data_ind(struct sock* sk, struct llc_conn_state_ev *ev);
extern int llc_conn_ac_disc_ind(struct sock* sk, struct llc_conn_state_ev *ev);
extern int llc_conn_ac_rst_ind(struct sock* sk, struct llc_conn_state_ev *ev);
extern int llc_conn_ac_rst_confirm(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_report_status(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_clear_remote_busy_if_f_eq_1(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_stop_rej_tmr_if_data_flag_eq_2(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_send_disc_cmd_p_set_x(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_send_dm_rsp_f_set_p(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_send_dm_rsp_f_set_1(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_send_dm_rsp_f_set_f_flag(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_send_frmr_rsp_f_set_x(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_resend_frmr_rsp_f_set_0(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_resend_frmr_rsp_f_set_p(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_send_i_cmd_p_set_1(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_send_i_cmd_p_set_0(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_resend_i_cmd_p_set_1(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_resend_i_cmd_p_set_1_or_send_rr(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_send_i_xxx_x_set_0(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_resend_i_xxx_x_set_0(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_resend_i_xxx_x_set_0_or_send_rr(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_resend_i_rsp_f_set_1(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_send_rej_cmd_p_set_1(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_send_rej_rsp_f_set_1(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_send_rej_xxx_x_set_0(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_send_rnr_cmd_p_set_1(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_send_rnr_rsp_f_set_1(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_send_rnr_xxx_x_set_0(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_set_remote_busy(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_opt_send_rnr_xxx_x_set_0(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_send_rr_cmd_p_set_1(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_send_ack_cmd_p_set_1(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_send_rr_rsp_f_set_1(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_send_ack_rsp_f_set_1(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_send_rr_xxx_x_set_0(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_send_ack_xxx_x_set_0(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_send_sabme_cmd_p_set_x(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_send_ua_rsp_f_set_f_flag(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_send_ua_rsp_f_set_p(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_set_s_flag_0(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_set_s_flag_1(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_start_p_timer(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_start_ack_timer(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_start_rej_timer(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_start_ack_tmr_if_not_running(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_stop_ack_timer(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_stop_p_timer(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_stop_rej_timer(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_stop_all_timers(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_stop_other_timers(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_upd_nr_received(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_inc_tx_win_size(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_dec_tx_win_size(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_upd_p_flag(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_set_data_flag_2(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_set_data_flag_0(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_set_data_flag_1(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_set_data_flag_1_if_data_flag_eq_0(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_set_p_flag_0(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_set_p_flag_1(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_set_remote_busy_0(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_set_retry_cnt_0(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_set_cause_flag_0(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_set_cause_flag_1(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_inc_retry_cnt_by_1(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_set_vr_0(struct sock* sk, struct llc_conn_state_ev *ev);
extern int llc_conn_ac_inc_vr_by_1(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_set_vs_0(struct sock* sk, struct llc_conn_state_ev *ev);
extern int llc_conn_ac_set_vs_nr(struct sock* sk, struct llc_conn_state_ev *ev);
extern int llc_conn_ac_rst_vs(struct sock* sk, struct llc_conn_state_ev *ev);
extern int llc_conn_ac_upd_vs(struct sock* sk, struct llc_conn_state_ev *ev);
extern int llc_conn_ac_set_f_flag_p(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_disc(struct sock* sk, struct llc_conn_state_ev *ev);
extern int llc_conn_reset(struct sock* sk, struct llc_conn_state_ev *ev);
extern int llc_conn_ac_disc_confirm(struct sock* sk, struct llc_conn_state_ev *ev);
extern u8 llc_circular_between(u8 a, u8 b, u8 c);
extern int llc_conn_ac_send_ack_if_needed(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_inc_npta_value(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_adjust_npta_by_rr(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_adjust_npta_by_rnr(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_rst_sendack_flag(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_send_rr_rsp_f_set_ackpf(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_send_i_rsp_f_set_ackpf(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_send_i_rsp_as_ack(struct sock* sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ac_send_i_as_ack(struct sock* sk,
struct llc_conn_state_ev *ev);
#endif /* LLC_C_AC_H */
#ifndef LLC_C_EV_H
#define LLC_C_EV_H
/*
* Copyright (c) 1997 by Procom Technology,Inc.
* 2001 by Arnaldo Carvalho de Melo <acme@conectiva.com.br>
*
* This program can be redistributed or modified under the terms of the
* GNU General Public License as published by the Free Software Foundation.
* This program is distributed without any warranty or implied warranty
* of merchantability or fitness for a particular purpose.
*
* See the GNU General Public License for more details.
*/
/* Connection component state transition event qualifiers */
/* Types of events (possible values in 'ev->type') */
#define LLC_CONN_EV_TYPE_SIMPLE 1
#define LLC_CONN_EV_TYPE_CONDITION 2
#define LLC_CONN_EV_TYPE_PRIM 3
#define LLC_CONN_EV_TYPE_PDU 4 /* command/response PDU */
#define LLC_CONN_EV_TYPE_ACK_TMR 5
#define LLC_CONN_EV_TYPE_P_TMR 6
#define LLC_CONN_EV_TYPE_REJ_TMR 7
#define LLC_CONN_EV_TYPE_BUSY_TMR 8
#define LLC_CONN_EV_TYPE_RPT_STATUS 9
#define LLC_CONN_EV_TYPE_SENDACK_TMR 10
#define NBR_CONN_EV 5
/* Connection events which cause state transitions when fully qualified */
#define LLC_CONN_EV_CONN_REQ 1
#define LLC_CONN_EV_CONN_RESP 2
#define LLC_CONN_EV_DATA_REQ 3
#define LLC_CONN_EV_DISC_REQ 4
#define LLC_CONN_EV_RESET_REQ 5
#define LLC_CONN_EV_RESET_RESP 6
#define LLC_CONN_EV_LOCAL_BUSY_DETECTED 7
#define LLC_CONN_EV_LOCAL_BUSY_CLEARED 8
#define LLC_CONN_EV_RX_BAD_PDU 9
#define LLC_CONN_EV_RX_DISC_CMD_Pbit_SET_X 10
#define LLC_CONN_EV_RX_DM_RSP_Fbit_SET_X 11
#define LLC_CONN_EV_RX_FRMR_RSP_Fbit_SET_X 12
#define LLC_CONN_EV_RX_I_CMD_Pbit_SET_X 13
#define LLC_CONN_EV_RX_I_CMD_Pbit_SET_X_UNEXPD_Ns 14
#define LLC_CONN_EV_RX_I_CMD_Pbit_SET_X_INVAL_Ns 15
#define LLC_CONN_EV_RX_I_RSP_Fbit_SET_X 16
#define LLC_CONN_EV_RX_I_RSP_Fbit_SET_X_UNEXPD_Ns 17
#define LLC_CONN_EV_RX_I_RSP_Fbit_SET_X_INVAL_Ns 18
#define LLC_CONN_EV_RX_REJ_CMD_Pbit_SET_X 19
#define LLC_CONN_EV_RX_REJ_RSP_Fbit_SET_X 20
#define LLC_CONN_EV_RX_RNR_CMD_Pbit_SET_X 21
#define LLC_CONN_EV_RX_RNR_RSP_Fbit_SET_X 22
#define LLC_CONN_EV_RX_RR_CMD_Pbit_SET_X 23
#define LLC_CONN_EV_RX_RR_RSP_Fbit_SET_X 24
#define LLC_CONN_EV_RX_SABME_CMD_Pbit_SET_X 25
#define LLC_CONN_EV_RX_UA_RSP_Fbit_SET_X 26
#define LLC_CONN_EV_RX_XXX_CMD_Pbit_SET_X 27
#define LLC_CONN_EV_RX_XXX_RSP_Fbit_SET_X 28
#define LLC_CONN_EV_RX_XXX_YYY 29
#define LLC_CONN_EV_RX_ZZZ_CMD_Pbit_SET_X_INVAL_Nr 30
#define LLC_CONN_EV_RX_ZZZ_RSP_Fbit_SET_X_INVAL_Nr 31
#define LLC_CONN_EV_P_TMR_EXP 32
#define LLC_CONN_EV_ACK_TMR_EXP 33
#define LLC_CONN_EV_REJ_TMR_EXP 34
#define LLC_CONN_EV_BUSY_TMR_EXP 35
#define LLC_CONN_EV_RX_XXX_CMD_Pbit_SET_1 36
#define LLC_CONN_EV_RX_XXX_CMD_Pbit_SET_0 37
#define LLC_CONN_EV_RX_I_CMD_Pbit_SET_0_UNEXPD_Ns 38
#define LLC_CONN_EV_RX_I_RSP_Fbit_SET_0_UNEXPD_Ns 39
#define LLC_CONN_EV_RX_I_RSP_Fbit_SET_1_UNEXPD_Ns 40
#define LLC_CONN_EV_RX_I_CMD_Pbit_SET_1_UNEXPD_Ns 41
#define LLC_CONN_EV_RX_I_CMD_Pbit_SET_0 42
#define LLC_CONN_EV_RX_I_RSP_Fbit_SET_0 43
#define LLC_CONN_EV_RX_I_CMD_Pbit_SET_1 44
#define LLC_CONN_EV_RX_RR_CMD_Pbit_SET_0 45
#define LLC_CONN_EV_RX_RR_RSP_Fbit_SET_0 46
#define LLC_CONN_EV_RX_RR_RSP_Fbit_SET_1 47
#define LLC_CONN_EV_RX_RR_CMD_Pbit_SET_1 48
#define LLC_CONN_EV_RX_RNR_CMD_Pbit_SET_0 49
#define LLC_CONN_EV_RX_RNR_RSP_Fbit_SET_0 50
#define LLC_CONN_EV_RX_RNR_RSP_Fbit_SET_1 51
#define LLC_CONN_EV_RX_RNR_CMD_Pbit_SET_1 52
#define LLC_CONN_EV_RX_REJ_CMD_Pbit_SET_0 53
#define LLC_CONN_EV_RX_REJ_RSP_Fbit_SET_0 54
#define LLC_CONN_EV_RX_REJ_CMD_Pbit_SET_1 55
#define LLC_CONN_EV_RX_I_RSP_Fbit_SET_1 56
#define LLC_CONN_EV_RX_REJ_RSP_Fbit_SET_1 57
#define LLC_CONN_EV_RX_XXX_RSP_Fbit_SET_1 58
#define LLC_CONN_EV_TX_BUFF_FULL 59
#define LLC_CONN_EV_INIT_P_F_CYCLE 100
/*
* Connection event qualifiers; for some events a certain combination of
* these qualifiers must be TRUE before event recognized valid for state;
* these constants act as indexes into the Event Qualifier function
* table
*/
#define LLC_CONN_EV_QFY_DATA_FLAG_EQ_1 1
#define LLC_CONN_EV_QFY_DATA_FLAG_EQ_0 2
#define LLC_CONN_EV_QFY_DATA_FLAG_EQ_2 3
#define LLC_CONN_EV_QFY_P_FLAG_EQ_1 4
#define LLC_CONN_EV_QFY_P_FLAG_EQ_0 5
#define LLC_CONN_EV_QFY_P_FLAG_EQ_Fbit 6
#define LLC_CONN_EV_QFY_REMOTE_BUSY_EQ_0 7
#define LLC_CONN_EV_QFY_RETRY_CNT_LT_N2 8
#define LLC_CONN_EV_QFY_RETRY_CNT_GTE_N2 9
#define LLC_CONN_EV_QFY_S_FLAG_EQ_1 10
#define LLC_CONN_EV_QFY_S_FLAG_EQ_0 11
#define LLC_CONN_EV_QFY_INIT_P_F_CYCLE 12
/* Event data interface; what is sent in an event package */
/* Event LLC_CONN_EV_TYPE_SIMPLE interface */
struct llc_conn_ev_simple_if {
u8 ev;
};
/* Event LLC_CONN_EV_TYPE_PRIM interface */
struct llc_conn_ev_prim_if {
u8 prim; /* connect, disconnect, reset, ... */
u8 type; /* request, indicate, response, conf */
struct llc_prim_if_block *data;
};
/* Event LLC_CONN_EV_TYPE_PDU interface */
struct llc_conn_ev_pdu_if {
u8 ev;
u8 reason;
struct sk_buff *skb;
};
/* Event interface for timer-generated events */
struct llc_conn_ev_tmr_if {
struct sock *sk;
u32 component_handle;
void *timer_specific;
};
struct llc_conn_ev_rpt_sts_if {
u8 status;
};
union llc_conn_ev_if {
struct llc_conn_ev_simple_if a; /* 'a' for simple, easy ... */
struct llc_conn_ev_prim_if prim;
struct llc_conn_ev_pdu_if pdu;
struct llc_conn_ev_tmr_if tmr;
struct llc_conn_ev_rpt_sts_if rsts; /* report status */
};
struct llc_conn_state_ev {
u8 type;
u8 status;
u8 flag;
struct llc_prim_if_block *ind_prim;
struct llc_prim_if_block *cfm_prim;
union llc_conn_ev_if data;
};
typedef int (*llc_conn_ev_t)(struct sock *sk, struct llc_conn_state_ev *ev);
typedef int (*llc_conn_ev_qfyr_t)(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_conn_req(struct sock *sk, struct llc_conn_state_ev *ev);
extern int llc_conn_ev_conn_resp(struct sock *sk, struct llc_conn_state_ev *ev);
extern int llc_conn_ev_data_req(struct sock *sk, struct llc_conn_state_ev *ev);
extern int llc_conn_ev_disc_req(struct sock *sk, struct llc_conn_state_ev *ev);
extern int llc_conn_ev_rst_req(struct sock *sk, struct llc_conn_state_ev *ev);
extern int llc_conn_ev_rst_resp(struct sock *sk, struct llc_conn_state_ev *ev);
extern int llc_conn_ev_local_busy_detected(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_local_busy_cleared(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_rx_bad_pdu(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_rx_disc_cmd_pbit_set_x(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_rx_dm_rsp_fbit_set_x(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_rx_frmr_rsp_fbit_set_x(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_rx_i_cmd_pbit_set_x_inval_ns(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_rx_i_rsp_fbit_set_x(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_rx_i_rsp_fbit_set_x_unexpd_ns(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_rx_i_rsp_fbit_set_x_inval_ns(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_rx_rej_rsp_fbit_set_x(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_rx_sabme_cmd_pbit_set_x(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_rx_ua_rsp_fbit_set_x(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_rx_xxx_cmd_pbit_set_x(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_rx_xxx_rsp_fbit_set_x(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_rx_xxx_yyy(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_rx_zzz_cmd_pbit_set_x_inval_nr(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_rx_zzz_rsp_fbit_set_x_inval_nr(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_p_tmr_exp(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_ack_tmr_exp(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_rej_tmr_exp(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_busy_tmr_exp(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_any_tmr_exp(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_sendack_tmr_exp(struct sock *sk,
struct llc_conn_state_ev *ev);
/* NOT_USED functions and their variations */
extern int llc_conn_ev_rx_xxx_cmd_pbit_set_1(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_rx_xxx_cmd_pbit_set_0(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_rx_xxx_rsp_fbit_set_1(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_rx_i_cmd_pbit_set_0_unexpd_ns(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_rx_i_cmd_pbit_set_1_unexpd_ns(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_rx_i_cmd_pbit_set_0(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_rx_i_cmd_pbit_set_1(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_rx_i_rsp_fbit_set_0_unexpd_ns(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_rx_i_rsp_fbit_set_1_unexpd_ns(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_rx_i_rsp_fbit_set_0(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_rx_i_rsp_fbit_set_1(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_rx_rr_cmd_pbit_set_0(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_rx_rr_cmd_pbit_set_1(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_rx_rr_rsp_fbit_set_0(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_rx_rr_rsp_fbit_set_1(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_rx_rnr_cmd_pbit_set_0(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_rx_rnr_cmd_pbit_set_1(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_rx_rnr_rsp_fbit_set_0(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_rx_rnr_rsp_fbit_set_1(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_rx_rej_cmd_pbit_set_0(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_rx_rej_cmd_pbit_set_1(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_rx_rej_rsp_fbit_set_0(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_rx_rej_rsp_fbit_set_1(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_rx_any_frame(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_tx_buffer_full(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_init_p_f_cycle(struct sock *sk,
struct llc_conn_state_ev *ev);
/* Available connection action qualifiers */
extern int llc_conn_ev_qlfy_data_flag_eq_1(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_qlfy_data_flag_eq_0(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_qlfy_data_flag_eq_2(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_qlfy_p_flag_eq_1(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_qlfy_last_frame_eq_1(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_qlfy_last_frame_eq_0(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_qlfy_p_flag_eq_0(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_qlfy_p_flag_eq_f(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_qlfy_remote_busy_eq_0(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_qlfy_remote_busy_eq_1(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_qlfy_retry_cnt_lt_n2(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_qlfy_retry_cnt_gte_n2(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_qlfy_s_flag_eq_1(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_qlfy_s_flag_eq_0(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_qlfy_cause_flag_eq_1(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_qlfy_cause_flag_eq_0(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_qlfy_init_p_f_cycle(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_qlfy_set_status_conn(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_qlfy_set_status_disc(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_qlfy_set_status_failed(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_qlfy_set_status_impossible(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_qlfy_set_status_remote_busy(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_qlfy_set_status_received(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_qlfy_set_status_refuse(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_qlfy_set_status_conflict(struct sock *sk,
struct llc_conn_state_ev *ev);
extern int llc_conn_ev_qlfy_set_status_rst_done(struct sock *sk,
struct llc_conn_state_ev *ev);
#endif /* LLC_C_EV_H */
#ifndef LLC_C_ST_H
#define LLC_C_ST_H
/*
* Copyright (c) 1997 by Procom Technology,Inc.
* 2001 by Arnaldo Carvalho de Melo <acme@conectiva.com.br>
*
* This program can be redistributed or modified under the terms of the
* GNU General Public License as published by the Free Software Foundation.
* This program is distributed without any warranty or implied warranty
* of merchantability or fitness for a particular purpose.
*
* See the GNU General Public License for more details.
*/
/* Connection component state management */
/* connection states */
#define LLC_CONN_OUT_OF_SVC 0 /* prior to allocation */
#define LLC_CONN_STATE_ADM 1 /* disc, initial state */
#define LLC_CONN_STATE_SETUP 2 /* disconnected state */
#define LLC_CONN_STATE_NORMAL 3 /* connected state */
#define LLC_CONN_STATE_BUSY 4 /* connected state */
#define LLC_CONN_STATE_REJ 5 /* connected state */
#define LLC_CONN_STATE_AWAIT 6 /* connected state */
#define LLC_CONN_STATE_AWAIT_BUSY 7 /* connected state */
#define LLC_CONN_STATE_AWAIT_REJ 8 /* connected state */
#define LLC_CONN_STATE_D_CONN 9 /* disconnected state */
#define LLC_CONN_STATE_RESET 10 /* disconnected state */
#define LLC_CONN_STATE_ERROR 11 /* disconnected state */
#define LLC_CONN_STATE_TEMP 12 /* disconnected state */
#define NBR_CONN_STATES 12 /* size of state table */
#define NO_STATE_CHANGE 100
/* Connection state table structure */
struct llc_conn_state_trans {
llc_conn_ev_t ev;
u8 next_state;
llc_conn_ev_qfyr_t *ev_qualifiers;
llc_conn_action_t *ev_actions;
};
struct llc_conn_state {
u8 current_state;
struct llc_conn_state_trans **transitions;
};
extern struct llc_conn_state llc_conn_state_table[];
#endif /* LLC_C_ST_H */
#ifndef LLC_CONN_H
#define LLC_CONN_H
/*
* Copyright (c) 1997 by Procom Technology, Inc.
* 2001 by Arnaldo Carvalho de Melo <acme@conectiva.com.br>
*
* This program can be redistributed or modified under the terms of the
* GNU General Public License as published by the Free Software Foundation.
* This program is distributed without any warranty or implied warranty
* of merchantability or fitness for a particular purpose.
*
* See the GNU General Public License for more details.
*/
#include <linux/timer.h>
#include <net/llc_if.h>
#undef DEBUG_LLC_CONN_ALLOC
struct llc_timer {
struct timer_list timer;
u8 running; /* timer is running or no */
u16 expire; /* timer expire time */
};
struct llc_opt {
struct list_head node; /* entry in sap->sk_list.list */
struct sock *sk; /* sock that has this llc_opt */
void *handler; /* for upper layers usage */
u8 state; /* state of connection */
struct llc_sap *sap; /* pointer to parent SAP */
struct llc_addr laddr; /* lsap/mac pair */
struct llc_addr daddr; /* dsap/mac pair */
struct net_device *dev; /* device to send to remote */
u8 retry_count; /* number of retries */
u8 ack_must_be_send;
u8 first_pdu_Ns;
u8 npta;
struct llc_timer ack_timer;
struct llc_timer pf_cycle_timer;
struct llc_timer rej_sent_timer;
struct llc_timer busy_state_timer; /* ind busy clr at remote LLC */
u8 vS; /* seq# next in-seq I-PDU tx'd*/
u8 vR; /* seq# next in-seq I-PDU rx'd*/
u32 n2; /* max nbr re-tx's for timeout*/
u32 n1; /* max nbr octets in I PDU */
u8 k; /* tx window size; max = 127 */
u8 rw; /* rx window size; max = 127 */
u8 p_flag; /* state flags */
u8 f_flag;
u8 s_flag;
u8 data_flag;
u8 remote_busy_flag;
u8 cause_flag;
struct sk_buff_head pdu_unack_q; /* PUDs sent/waiting ack */
u16 link; /* network layer link number */
u8 X; /* a temporary variable */
u8 ack_pf; /* this flag indicates what is
the P-bit of acknowledge */
u8 failed_data_req; /* recognize that already exist a
failed llc_data_req_handler
(tx_buffer_full or unacceptable
state */
u8 dec_step;
u8 inc_cntr;
u8 dec_cntr;
u8 connect_step;
u8 last_nr; /* NR of last pdu recieved */
u32 rx_pdu_hdr; /* used for saving header of last pdu
received and caused sending FRMR.
Used for resending FRMR */
#ifdef DEBUG_LLC_CONN_ALLOC
char *f_alloc, /* function that allocated this connection */
*f_free; /* function that freed this connection */
int l_alloc, /* line that allocated this connection */
l_free; /* line that freed this connection */
#endif
};
#define llc_sk(__sk) ((struct llc_opt *)(__sk)->protinfo)
struct llc_conn_state_ev;
extern struct sock *__llc_sock_alloc(void);
extern void __llc_sock_free(struct sock *sk, u8 free);
#ifdef DEBUG_LLC_CONN_ALLOC
#define dump_stack() printk(KERN_INFO "call trace: %p, %p, %p\n", \
__builtin_return_address(0), \
__builtin_return_address(1), \
__builtin_return_address(2));
#define llc_sock_alloc() ({ \
struct sock *__sk = __llc_sock_alloc(); \
if (__sk) { \
llc_sk(__sk)->f_alloc = __FUNCTION__; \
llc_sk(__sk)->l_alloc = __LINE__; \
} \
__sk;})
#define __llc_sock_assert(__sk) \
if (llc_sk(__sk)->f_free) { \
printk(KERN_ERR \
"%p conn (alloc'd @ %s(%d)) " \
"already freed @ %s(%d) " \
"being used again @ %s(%d)\n", \
llc_sk(__sk), \
llc_sk(__sk)->f_alloc, llc_sk(__sk)->l_alloc, \
llc_sk(__sk)->f_free, llc_sk(__sk)->l_free, \
__FUNCTION__, __LINE__); \
dump_stack();
#define llc_sock_free(__sk) \
{ \
__llc_sock_assert(__sk) \
} else { \
__llc_sock_free(__sk, 0); \
llc_sk(__sk)->f_free = __FUNCTION__; \
llc_sk(__sk)->l_free = __LINE__; \
} \
}
#define llc_sock_assert(__sk) \
{ \
__llc_sock_assert(__sk); \
return; } \
}
#define llc_sock_assert_ret(__sk, __ret) \
{ \
__llc_sock_assert(__sk); \
return __ret; } \
}
#else /* DEBUG_LLC_CONN_ALLOC */
#define llc_sock_alloc() __llc_sock_alloc()
#define llc_sock_free(__sk) __llc_sock_free(__sk, 1)
#define llc_sock_assert(__sk)
#define llc_sock_assert_ret(__sk)
#endif /* DEBUG_LLC_CONN_ALLOC */
extern void llc_sock_reset(struct sock *sk);
extern int llc_sock_init(struct sock *sk);
/* Access to a connection */
extern struct llc_conn_state_ev *llc_conn_alloc_ev(struct sock *sk);
extern int llc_conn_send_ev(struct sock *sk, struct llc_conn_state_ev *ev);
extern void llc_conn_send_pdu(struct sock *sk, struct sk_buff *skb);
extern void llc_conn_rtn_pdu(struct sock *sk, struct sk_buff *skb,
struct llc_conn_state_ev *ev);
extern void llc_conn_free_ev(struct llc_conn_state_ev *ev);
extern void llc_conn_resend_i_pdu_as_cmd(struct sock *sk, u8 nr,
u8 first_p_bit);
extern void llc_conn_resend_i_pdu_as_rsp(struct sock *sk, u8 nr,
u8 first_f_bit);
extern int llc_conn_remove_acked_pdus(struct sock *conn, u8 nr,
u16 *how_many_unacked);
extern struct sock *llc_find_sock(struct llc_sap *sap, struct llc_addr *daddr,
struct llc_addr *laddr);
extern u8 llc_data_accept_state(u8 state);
extern void llc_build_offset_table(void);
#endif /* LLC_CONN_H */
#ifndef LLC_EVNT_H
#define LLC_EVNT_H
/*
* Copyright (c) 1997 by Procom Technology,Inc.
* 2001 by Arnaldo Carvalho de Melo <acme@conectiva.com.br>
*
* This program can be redistributed or modified under the terms of the
* GNU General Public License as published by the Free Software Foundation.
* This program is distributed without any warranty or implied warranty
* of merchantability or fitness for a particular purpose.
*
* See the GNU General Public License for more details.
*/
/* Station component state transition events */
/* Types of events (possible values in 'ev->type') */
#define LLC_STATION_EV_TYPE_SIMPLE 1
#define LLC_STATION_EV_TYPE_CONDITION 2
#define LLC_STATION_EV_TYPE_PRIM 3
#define LLC_STATION_EV_TYPE_PDU 4 /* command/response PDU */
#define LLC_STATION_EV_TYPE_ACK_TMR 5
#define LLC_STATION_EV_TYPE_RPT_STATUS 6
/* Events */
#define LLC_STATION_EV_ENABLE_WITH_DUP_ADDR_CHECK 1
#define LLC_STATION_EV_ENABLE_WITHOUT_DUP_ADDR_CHECK 2
#define LLC_STATION_EV_ACK_TMR_EXP_LT_RETRY_CNT_MAX_RETRY 3
#define LLC_STATION_EV_ACK_TMR_EXP_EQ_RETRY_CNT_MAX_RETRY 4
#define LLC_STATION_EV_RX_NULL_DSAP_XID_C 5
#define LLC_STATION_EV_RX_NULL_DSAP_0_XID_R_XID_R_CNT_EQ 6
#define LLC_STATION_EV_RX_NULL_DSAP_1_XID_R_XID_R_CNT_EQ 7
#define LLC_STATION_EV_RX_NULL_DSAP_TEST_C 8
#define LLC_STATION_EV_DISABLE_REQ 9
/* Interfaces for various types of supported events */
struct llc_stat_ev_simple_if {
u8 ev;
};
struct llc_stat_ev_prim_if {
u8 prim; /* connect, disconnect, reset, ... */
u8 type; /* request, indicate, response, confirm */
};
struct llc_stat_ev_pdu_if {
u8 reason;
struct sk_buff *skb;
};
struct llc_stat_ev_tmr_if {
void *timer_specific;
};
struct llc_stat_ev_rpt_sts_if {
u8 status;
};
union llc_stat_ev_if {
struct llc_stat_ev_simple_if a; /* 'a' for simple, easy ... */
struct llc_stat_ev_prim_if prim;
struct llc_stat_ev_pdu_if pdu;
struct llc_stat_ev_tmr_if tmr;
struct llc_stat_ev_rpt_sts_if rsts; /* report status */
};
struct llc_station_state_ev {
u8 type;
union llc_stat_ev_if data;
struct list_head node; /* node in station->ev_q.list */
};
typedef int (*llc_station_ev_t)(struct llc_station *station,
struct llc_station_state_ev *ev);
extern int llc_stat_ev_enable_with_dup_addr_check(struct llc_station *station,
struct llc_station_state_ev *ev);
extern int llc_stat_ev_enable_without_dup_addr_check(struct llc_station *station,
struct llc_station_state_ev *ev);
extern int llc_stat_ev_ack_tmr_exp_lt_retry_cnt_max_retry(struct llc_station *
station,
struct llc_station_state_ev *ev);
extern int llc_stat_ev_ack_tmr_exp_eq_retry_cnt_max_retry(struct llc_station *station,
struct llc_station_state_ev *ev);
extern int llc_stat_ev_rx_null_dsap_xid_c(struct llc_station *station,
struct llc_station_state_ev *ev);
extern int llc_stat_ev_rx_null_dsap_0_xid_r_xid_r_cnt_eq(struct llc_station *station,
struct llc_station_state_ev *ev);
extern int llc_stat_ev_rx_null_dsap_1_xid_r_xid_r_cnt_eq(struct llc_station *station,
struct llc_station_state_ev *ev);
extern int llc_stat_ev_rx_null_dsap_test_c(struct llc_station *station,
struct llc_station_state_ev *ev);
extern int llc_stat_ev_disable_req(struct llc_station *station,
struct llc_station_state_ev *ev);
#endif /* LLC_EVNT_H */
#ifndef LLC_IF_H
#define LLC_IF_H
/*
* Copyright (c) 1997 by Procom Technology,Inc.
* 2001 by Arnaldo Carvalho de Melo <acme@conectiva.com.br>
*
* This program can be redistributed or modified under the terms of the
* GNU General Public License as published by the Free Software Foundation.
* This program is distributed without any warranty or implied warranty
* of merchantability or fitness for a particular purpose.
*
* See the GNU General Public License for more details.
*/
/* Defines LLC interface to network layer */
/* Available primitives */
#include <linux/if.h>
#define LLC_DATAUNIT_PRIM 0
#define LLC_CONN_PRIM 1
#define LLC_DATA_PRIM 2
#define LLC_DISC_PRIM 3
#define LLC_RESET_PRIM 4
#define LLC_FLOWCONTROL_PRIM 5
#define LLC_DISABLE_PRIM 6
#define LLC_XID_PRIM 7
#define LLC_TEST_PRIM 8
#define LLC_SAP_ACTIVATION 9
#define LLC_SAP_DEACTIVATION 10
#define LLC_NBR_PRIMITIVES 11
#define LLC_IND 1
#define LLC_CONFIRM 2
/* Primitive type */
#define LLC_PRIM_TYPE_REQ 1
#define LLC_PRIM_TYPE_IND 2
#define LLC_PRIM_TYPE_RESP 3
#define LLC_PRIM_TYPE_CONFIRM 4
/* Reset reasons, remote entity or local LLC */
#define LLC_RESET_REASON_REMOTE 1
#define LLC_RESET_REASON_LOCAL 2
/* Disconnect reasons */
#define LLC_DISC_REASON_RX_DM_RSP_PDU 0
#define LLC_DISC_REASON_RX_DISC_CMD_PDU 1
#define LLC_DISC_REASON_ACK_TMR_EXP 2
/* Confirm reasons */
#define LLC_STATUS_CONN 0 /* connect confirm & reset confirm */
#define LLC_STATUS_DISC 1 /* connect confirm & reset confirm */
#define LLC_STATUS_FAILED 2 /* connect confirm & reset confirm */
#define LLC_STATUS_IMPOSSIBLE 3 /* connect confirm */
#define LLC_STATUS_RECEIVED 4 /* data conn */
#define LLC_STATUS_REMOTE_BUSY 5 /* data conn */
#define LLC_STATUS_REFUSE 6 /* data conn */
#define LLC_STATUS_CONFLICT 7 /* disconnect conn */
#define LLC_STATUS_RESET_DONE 8 /* */
/* Structures and types */
/* SAP/MAC Address pair */
struct llc_addr {
u8 lsap;
u8 mac[IFHWADDRLEN];
};
/* Primitive-specific data */
struct llc_prim_conn {
struct llc_addr saddr; /* used by request only */
struct llc_addr daddr; /* used by request only */
u8 status; /* reason for failure */
u8 pri; /* service_class */
struct net_device *dev;
struct sock *sk; /* returned from REQUEST */
void *handler; /* upper layer use,
stored in llc_opt->handler */
u16 link;
struct sk_buff *skb; /* received SABME */
};
struct llc_prim_disc {
struct sock *sk;
u16 link;
u8 reason; /* not used by request */
};
struct llc_prim_reset {
struct sock *sk;
u16 link;
u8 reason; /* used only by indicate */
};
struct llc_prim_flow_ctrl {
struct sock *sk;
u16 link;
u32 amount;
};
struct llc_prim_data {
struct sock *sk;
u16 link;
u8 pri;
struct sk_buff *skb; /* pointer to frame */
u8 status; /* reason */
};
/* Sending data in conection-less mode */
struct llc_prim_unit_data {
struct llc_addr saddr;
struct llc_addr daddr;
u8 pri;
struct sk_buff *skb; /* pointer to frame */
u8 lfb; /* largest frame bit (TR) */
};
struct llc_prim_xid {
struct llc_addr saddr;
struct llc_addr daddr;
u8 pri;
struct sk_buff *skb;
};
struct llc_prim_test {
struct llc_addr saddr;
struct llc_addr daddr;
u8 pri;
struct sk_buff *skb; /* pointer to frame */
};
union llc_u_prim_data {
struct llc_prim_conn conn;
struct llc_prim_disc disc;
struct llc_prim_reset res;
struct llc_prim_flow_ctrl fc;
struct llc_prim_data data; /* data */
struct llc_prim_unit_data udata; /* unit data */
struct llc_prim_xid xid;
struct llc_prim_test test;
};
struct llc_sap;
/* Information block passed with all called primitives */
struct llc_prim_if_block {
struct llc_sap *sap;
u8 prim;
union llc_u_prim_data *data;
};
typedef int (*llc_prim_call_t)(struct llc_prim_if_block *prim_if);
extern struct llc_sap *llc_sap_open(llc_prim_call_t network_indicate,
llc_prim_call_t network_confirm, u8 lsap);
extern void llc_sap_close(struct llc_sap *sap);
#endif /* LLC_IF_H */
#ifndef LLC_MAC_H
#define LLC_MAC_H
/*
* Copyright (c) 1997 by Procom Technology, Inc.
* 2001 by Arnaldo Carvalho de Melo <acme@conectiva.com.br>
*
* This program can be redistributed or modified under the terms of the
* GNU General Public License as published by the Free Software Foundation.
* This program is distributed without any warranty or implied warranty
* of merchantability or fitness for a particular purpose.
*
* See the GNU General Public License for more details.
*/
/* Defines MAC-layer interface to LLC layer */
extern int mac_send_pdu(struct sk_buff *skb);
extern int mac_indicate(struct sk_buff *skb, struct net_device *dev,
struct packet_type *pt);
extern struct net_device *mac_dev_peer(struct net_device *current_dev,
int type, u8 *mac);
extern int llc_pdu_router(struct llc_sap *sap, struct sock *sk,
struct sk_buff *skb, u8 type);
extern u16 lan_hdrs_init(struct sk_buff *skb, u8 *sa, u8 *da);
#endif /* LLC_MAC_H */
#ifndef LLC_MAIN_H
#define LLC_MAIN_H
/*
* Copyright (c) 1997 by Procom Technology, Inc.
* 2001 by Arnaldo Carvalho de Melo <acme@conectiva.com.br>
*
* This program can be redistributed or modified under the terms of the
* GNU General Public License as published by the Free Software Foundation.
* This program is distributed without any warranty or implied warranty
* of merchantability or fitness for a particular purpose.
*
* See the GNU General Public License for more details.
*/
#define LLC_EVENT 1
#define LLC_PACKET 2
#define LLC_TYPE_1 1
#define LLC_TYPE_2 2
#define LLC_P_TIME 2
#define LLC_ACK_TIME 3
#define LLC_REJ_TIME 3
#define LLC_BUSY_TIME 3
#define LLC_SENDACK_TIME 50
#define LLC_DEST_INVALID 0 /* Invalid LLC PDU type */
#define LLC_DEST_SAP 1 /* Type 1 goes here */
#define LLC_DEST_CONN 2 /* Type 2 goes here */
/* LLC Layer global default parameters */
#define LLC_GLOBAL_DEFAULT_MAX_NBR_SAPS 4
#define LLC_GLOBAL_DEFAULT_MAX_NBR_CONNS 64
extern struct llc_prim_if_block llc_ind_prim, llc_cfm_prim;
/* LLC station component (SAP and connection resource manager) */
/* Station component; one per adapter */
struct llc_station {
u8 state; /* state of station */
u8 xid_r_count; /* XID response PDU counter */
struct timer_list ack_timer;
u8 ack_tmr_running; /* 1 or 0 */
u8 retry_count;
u8 maximum_retry;
u8 mac_sa[6]; /* MAC source address */
struct {
spinlock_t lock;
struct list_head list;
} sap_list; /* list of related SAPs */
struct {
spinlock_t lock;
struct list_head list;
} ev_q; /* events entering state mach. */
struct sk_buff_head mac_pdu_q; /* PDUs ready to send to MAC */
};
struct llc_station_state_ev;
extern struct llc_sap *llc_sap_alloc(void);
extern void llc_sap_save(struct llc_sap *sap);
extern void llc_free_sap(struct llc_sap *sap);
extern struct llc_sap *llc_sap_find(u8 lsap);
extern struct llc_station *llc_station_get(void);
extern struct llc_station_state_ev *
llc_station_alloc_ev(struct llc_station *station);
extern void llc_station_send_ev(struct llc_station *station,
struct llc_station_state_ev *ev);
extern void llc_station_send_pdu(struct llc_station *station,
struct sk_buff *skb);
extern struct sk_buff *llc_alloc_frame(void);
#endif /* LLC_MAIN_H */
#ifndef LLC_PDU_H
#define LLC_PDU_H
/*
* Copyright (c) 1997 by Procom Technology,Inc.
* 2001 by Arnaldo Carvalho de Melo <acme@conectiva.com.br>
*
* This program can be redistributed or modified under the terms of the
* GNU General Public License as published by the Free Software Foundation.
* This program is distributed without any warranty or implied warranty
* of merchantability or fitness for a particular purpose.
*
* See the GNU General Public License for more details.
*/
/* LLC PDU structure */
/* Lengths of frame formats */
#define LLC_PDU_LEN_I 4 /* header and 2 control bytes */
#define LLC_PDU_LEN_S 4
#define LLC_PDU_LEN_U 3 /* header and 1 control byte */
/* Known SAP addresses */
#define LLC_GLOBAL_SAP 0xFF
#define LLC_NULL_SAP 0x00 /* not network-layer visible */
#define LLC_MGMT_INDIV 0x02 /* station LLC mgmt indiv addr */
#define LLC_MGMT_GRP 0x03 /* station LLC mgmt group addr */
#define LLC_RDE_SAP 0xA6 /* route ... */
/* SAP field bit masks */
#define LLC_ISO_RESERVED_SAP 0x02
#define LLC_SAP_GROUP_DSAP 0x01
#define LLC_SAP_RESP_SSAP 0x01
/* Group/individual DSAP indicator is DSAP field */
#define LLC_PDU_GROUP_DSAP_MASK 0x01
#define LLC_PDU_IS_GROUP_DSAP(pdu) \
((pdu->dsap & LLC_PDU_GROUP_DSAP_MASK) ? 0 : 1)
#define LLC_PDU_IS_INDIV_DSAP(pdu) \
(!(pdu->dsap & LLC_PDU_GROUP_DSAP_MASK) ? 0 : 1)
/* Command/response PDU indicator in SSAP field */
#define LLC_PDU_CMD_RSP_MASK 0x01
#define LLC_PDU_CMD 0
#define LLC_PDU_RSP 1
#define LLC_PDU_IS_CMD(pdu) ((pdu->ssap & LLC_PDU_RSP) ? 1 : 0)
#define LLC_PDU_IS_RSP(pdu) ((pdu->ssap & LLC_PDU_RSP) ? 0 : 1)
/* Get PDU type from 2 lowest-order bits of control field first byte */
#define LLC_PDU_TYPE_I_MASK 0x01 /* 16-bit control field */
#define LLC_PDU_TYPE_S_MASK 0x03
#define LLC_PDU_TYPE_U_MASK 0x03 /* 8-bit control field */
#define LLC_PDU_TYPE_MASK 0x03
#define LLC_PDU_TYPE_I 0 /* first bit */
#define LLC_PDU_TYPE_S 1 /* first two bits */
#define LLC_PDU_TYPE_U 3 /* first two bits */
#define LLC_PDU_TYPE_IS_I(pdu) \
((!(pdu->ctrl_1 & LLC_PDU_TYPE_I_MASK)) ? 0 : 1)
#define LLC_PDU_TYPE_IS_U(pdu) \
(((pdu->ctrl_1 & LLC_PDU_TYPE_U_MASK) == LLC_PDU_TYPE_U) ? 0 : 1)
#define LLC_PDU_TYPE_IS_S(pdu) \
(((pdu->ctrl_1 & LLC_PDU_TYPE_S_MASK) == LLC_PDU_TYPE_S) ? 0 : 1)
/* U-format PDU control field masks */
#define LLC_U_PF_BIT_MASK 0x10 /* P/F bit mask */
#define LLC_U_PF_IS_1(pdu) ((pdu->ctrl_1 & LLC_U_PF_BIT_MASK) ? 0 : 1)
#define LLC_U_PF_IS_0(pdu) ((!(pdu->ctrl_1 & LLC_U_PF_BIT_MASK)) ? 0 : 1)
#define LLC_U_PDU_CMD_MASK 0xEC /* cmd/rsp mask */
#define LLC_U_PDU_CMD(pdu) (pdu->ctrl_1 & LLC_U_PDU_CMD_MASK)
#define LLC_U_PDU_RSP(pdu) (pdu->ctrl_1 & LLC_U_PDU_CMD_MASK)
#define LLC_1_PDU_CMD_UI 0x00 /* Type 1 cmds/rsps */
#define LLC_1_PDU_CMD_XID 0xAC
#define LLC_1_PDU_CMD_TEST 0xE0
#define LLC_2_PDU_CMD_SABME 0x6C /* Type 2 cmds/rsps */
#define LLC_2_PDU_CMD_DISC 0x40
#define LLC_2_PDU_RSP_UA 0x60
#define LLC_2_PDU_RSP_DM 0x0C
#define LLC_2_PDU_RSP_FRMR 0x84
/* Type 1 operations */
/* XID information field bit masks */
/* LLC format identifier (byte 1) */
#define LLC_XID_FMT_ID 0x81 /* first byte must be this */
/* LLC types/classes identifier (byte 2) */
#define LLC_XID_CLASS_ZEROS_MASK 0xE0 /* these must be zeros */
#define LLC_XID_CLASS_MASK 0x1F /* AND with byte to get below */
#define LLC_XID_NULL_CLASS_1 0x01 /* if NULL LSAP...use these */
#define LLC_XID_NULL_CLASS_2 0x03
#define LLC_XID_NULL_CLASS_3 0x05
#define LLC_XID_NULL_CLASS_4 0x07
#define LLC_XID_NNULL_TYPE_1 0x01 /* if non-NULL LSAP...use these */
#define LLC_XID_NNULL_TYPE_2 0x02
#define LLC_XID_NNULL_TYPE_3 0x04
#define LLC_XID_NNULL_TYPE_1_2 0x03
#define LLC_XID_NNULL_TYPE_1_3 0x05
#define LLC_XID_NNULL_TYPE_2_3 0x06
#define LLC_XID_NNULL_ALL 0x07
/* Sender Receive Window (byte 3) */
#define LLC_XID_RW_MASK 0xFE /* AND with value to get below */
#define LLC_XID_MIN_RW 0x02 /* lowest-order bit always zero */
/* Type 2 operations */
#define LLC_2_SEQ_NBR_MODULO ((u8) 128)
/* I-PDU masks ('ctrl' is I-PDU control word) */
#define LLC_I_GET_NS(pdu) (u8)((pdu->ctrl_1 & 0xFE) >> 1)
#define LLC_I_GET_NR(pdu) (u8)((pdu->ctrl_2 & 0xFE) >> 1)
#define LLC_I_PF_BIT_MASK 0x01
#define LLC_I_PF_IS_0(pdu) ((!(pdu->ctrl_2 & LLC_I_PF_BIT_MASK)) ? 0 : 1)
#define LLC_I_PF_IS_1(pdu) ((pdu->ctrl_2 & LLC_I_PF_BIT_MASK) ? 0 : 1)
/* S-PDU supervisory commands and responses */
#define LLC_S_PDU_CMD_MASK 0x0C
#define LLC_S_PDU_CMD(pdu) (pdu->ctrl_1 & LLC_S_PDU_CMD_MASK)
#define LLC_S_PDU_RSP(pdu) (pdu->ctrl_1 & LLC_S_PDU_CMD_MASK)
#define LLC_2_PDU_CMD_RR 0x00 /* rx ready cmd */
#define LLC_2_PDU_RSP_RR 0x00 /* rx ready rsp */
#define LLC_2_PDU_CMD_REJ 0x08 /* reject PDU cmd */
#define LLC_2_PDU_RSP_REJ 0x08 /* reject PDU rsp */
#define LLC_2_PDU_CMD_RNR 0x04 /* rx not ready cmd */
#define LLC_2_PDU_RSP_RNR 0x04 /* rx not ready rsp */
#define LLC_S_PF_BIT_MASK 0x01
#define LLC_S_PF_IS_0(pdu) ((!(pdu->ctrl_2 & LLC_S_PF_BIT_MASK)) ? 0 : 1)
#define LLC_S_PF_IS_1(pdu) ((pdu->ctrl_2 & LLC_S_PF_BIT_MASK) ? 0 : 1)
#define PDU_SUPV_GET_Nr(pdu) ((pdu->ctrl_2 & 0xFE) >> 1)
#define PDU_GET_NEXT_Vr(sn) (++sn & ~LLC_2_SEQ_NBR_MODULO)
/* FRMR information field macros */
#define FRMR_INFO_LENGTH 5 /* 5 bytes of information */
/*
* info is pointer to FRMR info field structure; 'rej_ctrl' is byte pointer
* (if U-PDU) or word pointer to rejected PDU control field
*/
#define FRMR_INFO_SET_REJ_CNTRL(info,rej_ctrl) \
info->rej_pdu_ctrl = ((*((u8 *) rej_ctrl) & \
LLC_PDU_TYPE_U) != LLC_PDU_TYPE_U ? \
(u16)*((u16 *) rej_ctrl) : \
(((u16) *((u8 *) rej_ctrl)) & 0x00FF))
/*
* Info is pointer to FRMR info field structure; 'vs' is a byte containing
* send state variable value in low-order 7 bits (insure the lowest-order
* bit remains zero (0))
*/
#define FRMR_INFO_SET_Vs(info,vs) (info->curr_ssv = (((u8) vs) << 1))
#define FRMR_INFO_SET_Vr(info,vr) (info->curr_rsv = (((u8) vr) << 1))
/*
* Info is pointer to FRMR info field structure; 'cr' is a byte containing
* the C/R bit value in the low-order bit
*/
#define FRMR_INFO_SET_C_R_BIT(info, cr) (info->curr_rsv |= (((u8) cr) & 0x01))
/*
* In the remaining five macros, 'info' is pointer to FRMR info field
* structure; 'ind' is a byte containing the bit value to set in the
* lowest-order bit)
*/
#define FRMR_INFO_SET_INVALID_PDU_CTRL_IND(info, ind) \
(info->ind_bits = ((info->ind_bits & 0xFE) | (((u8) ind) & 0x01)))
#define FRMR_INFO_SET_INVALID_PDU_INFO_IND(info, ind) \
(info->ind_bits = ( (info->ind_bits & 0xFD) | (((u8) ind) & 0x02)))
#define FRMR_INFO_SET_PDU_INFO_2LONG_IND(info, ind) \
(info->ind_bits = ( (info->ind_bits & 0xFB) | (((u8) ind) & 0x04)))
#define FRMR_INFO_SET_PDU_INVALID_Nr_IND(info, ind) \
(info->ind_bits = ( (info->ind_bits & 0xF7) | (((u8) ind) & 0x08)))
#define FRMR_INFO_SET_PDU_INVALID_Ns_IND(info, ind) \
(info->ind_bits = ( (info->ind_bits & 0xEF) | (((u8) ind) & 0x10)))
/* Sequence-numbered PDU format (4 bytes in length) */
typedef struct llc_pdu_sn {
u8 dsap;
u8 ssap;
u8 ctrl_1;
u8 ctrl_2;
} llc_pdu_sn_t;
/* Un-numbered PDU format (3 bytes in length) */
typedef struct llc_pdu_un {
u8 dsap;
u8 ssap;
u8 ctrl_1;
} llc_pdu_un_t;
/* LLC Type 1 XID command/response information fields format */
typedef struct llc_xid_info {
u8 fmt_id; /* always 0x18 for LLC */
u8 type; /* different if NULL/non-NULL LSAP */
u8 rw; /* sender receive window */
} llc_xid_info_t;
/* LLC Type 2 FRMR response information field format */
typedef struct llc_frmr_info {
u16 rej_pdu_ctrl; /* bits 1-8 if U-PDU */
u8 curr_ssv; /* current send state variable val */
u8 curr_rsv; /* current receive state variable */
u8 ind_bits; /* indicator bits set with macro */
} llc_frmr_info_t;
extern void llc_pdu_set_cmd_rsp(struct sk_buff *skb, u8 type);
extern void llc_pdu_set_pf_bit(struct sk_buff *skb, u8 bit_value);
extern int llc_pdu_decode_pf_bit(struct sk_buff *skb, u8 *pf_bit);
extern int llc_pdu_decode_cr_bit(struct sk_buff *skb, u8 *cr_bit);
extern int llc_pdu_decode_sa(struct sk_buff *skb, u8 *sa);
extern int llc_pdu_decode_da(struct sk_buff *skb, u8 *ds);
extern int llc_pdu_decode_dsap(struct sk_buff *skb, u8 *dsap);
extern int llc_pdu_decode_ssap(struct sk_buff *skb, u8 *ssap);
extern int llc_decode_pdu_type(struct sk_buff *skb, u8 *destination);
extern void llc_pdu_header_init(struct sk_buff *skb, u8 pdu_type, u8 ssap,
u8 dsap, u8 cr);
extern int llc_pdu_init_as_ui_cmd(struct sk_buff *skb);
extern int llc_pdu_init_as_xid_cmd(struct sk_buff *skb, u8 svcs_supported,
u8 rx_window);
extern int llc_pdu_init_as_test_cmd(struct sk_buff *skb);
extern int llc_pdu_init_as_disc_cmd(struct sk_buff *skb, u8 p_bit);
extern int llc_pdu_init_as_i_cmd(struct sk_buff *skb, u8 p_bit, u8 ns, u8 nr);
extern int llc_pdu_init_as_rej_cmd(struct sk_buff *skb, u8 p_bit, u8 nr);
extern int llc_pdu_init_as_rnr_cmd(struct sk_buff *skb, u8 p_bit, u8 nr);
extern int llc_pdu_init_as_rr_cmd(struct sk_buff *skb, u8 p_bit, u8 nr);
extern int llc_pdu_init_as_sabme_cmd(struct sk_buff *skb, u8 p_bit);
extern int llc_pdu_init_as_dm_rsp(struct sk_buff *skb, u8 f_bit);
extern int llc_pdu_init_as_xid_rsp(struct sk_buff *skb, u8 svcs_supported,
u8 rx_window);
extern int llc_pdu_init_as_test_rsp(struct sk_buff *skb,
struct sk_buff *ev_skb);
extern int llc_pdu_init_as_frmr_rsp(struct sk_buff *skb, llc_pdu_sn_t *prev_pdu,
u8 f_bit, u8 vs, u8 vr, u8 vzyxw);
extern int llc_pdu_init_as_rr_rsp(struct sk_buff *skb, u8 f_bit, u8 nr);
extern int llc_pdu_init_as_rej_rsp(struct sk_buff *skb, u8 f_bit, u8 nr);
extern int llc_pdu_init_as_rnr_rsp(struct sk_buff *skb, u8 f_bit, u8 nr);
extern int llc_pdu_init_as_ua_rsp(struct sk_buff *skb, u8 f_bit);
#endif /* LLC_PDU_H */
#ifndef LLC_S_AC_H
#define LLC_S_AC_H
/*
* Copyright (c) 1997 by Procom Technology,Inc.
* 2001 by Arnaldo Carvalho de Melo <acme@conectiva.com.br>
*
* This program can be redistributed or modified under the terms of the
* GNU General Public License as published by the Free Software Foundation.
* This program is distributed without any warranty or implied warranty
* of merchantability or fitness for a particular purpose.
*
* See the GNU General Public License for more details.
*/
/* SAP component actions */
#define SAP_ACT_UNITDATA_IND 1
#define SAP_ACT_SEND_UI 2
#define SAP_ACT_SEND_XID_C 3
#define SAP_ACT_SEND_XID_R 4
#define SAP_ACT_SEND_TEST_C 5
#define SAP_ACT_SEND_TEST_R 6
#define SAP_ACT_REPORT_STATUS 7
#define SAP_ACT_XID_IND 8
#define SAP_ACT_TEST_IND 9
/* All action functions must look like this */
typedef int (*llc_sap_action_t)(struct llc_sap *sap,
struct llc_sap_state_ev *ev);
extern int llc_sap_action_unitdata_ind(struct llc_sap *sap,
struct llc_sap_state_ev *ev);
extern int llc_sap_action_send_ui(struct llc_sap *sap,
struct llc_sap_state_ev *ev);
extern int llc_sap_action_send_xid_c(struct llc_sap *sap,
struct llc_sap_state_ev *ev);
extern int llc_sap_action_send_xid_r(struct llc_sap *sap,
struct llc_sap_state_ev *ev);
extern int llc_sap_action_send_test_c(struct llc_sap *sap,
struct llc_sap_state_ev *ev);
extern int llc_sap_action_send_test_r(struct llc_sap *sap,
struct llc_sap_state_ev *ev);
extern int llc_sap_action_report_status(struct llc_sap *sap,
struct llc_sap_state_ev *ev);
extern int llc_sap_action_xid_ind(struct llc_sap *sap,
struct llc_sap_state_ev *ev);
extern int llc_sap_action_test_ind(struct llc_sap *sap,
struct llc_sap_state_ev *ev);
#endif /* LLC_S_AC_H */
#ifndef LLC_S_EV_H
#define LLC_S_EV_H
/*
* Copyright (c) 1997 by Procom Technology,Inc.
* 2001 by Arnaldo Carvalho de Melo <acme@conectiva.com.br>
*
* This program can be redistributed or modified under the terms of the
* GNU General Public License as published by the Free Software Foundation.
* This program is distributed without any warranty or implied warranty
* of merchantability or fitness for a particular purpose.
*
* See the GNU General Public License for more details.
*/
/* Defines SAP component events */
/* Types of events (possible values in 'ev->type') */
#define LLC_SAP_EV_TYPE_SIMPLE 1
#define LLC_SAP_EV_TYPE_CONDITION 2
#define LLC_SAP_EV_TYPE_PRIM 3
#define LLC_SAP_EV_TYPE_PDU 4 /* command/response PDU */
#define LLC_SAP_EV_TYPE_ACK_TMR 5
#define LLC_SAP_EV_TYPE_RPT_STATUS 6
#define LLC_SAP_EV_ACTIVATION_REQ 1
#define LLC_SAP_EV_RX_UI 2
#define LLC_SAP_EV_UNITDATA_REQ 3
#define LLC_SAP_EV_XID_REQ 4
#define LLC_SAP_EV_RX_XID_C 5
#define LLC_SAP_EV_RX_XID_R 6
#define LLC_SAP_EV_TEST_REQ 7
#define LLC_SAP_EV_RX_TEST_C 8
#define LLC_SAP_EV_RX_TEST_R 9
#define LLC_SAP_EV_DEACTIVATION_REQ 10
/* Interfaces for various types of supported events */
struct llc_sap_ev_simple_if {
u8 ev;
};
struct llc_prim_if_block;
struct llc_sap_ev_prim_if {
u8 prim; /* connect, disconnect, reset, ... */
u8 type; /* request, indicate, response, conf */
struct llc_prim_if_block *data;
};
struct llc_sap_ev_pdu_if {
u8 ev;
u8 reason;
struct sk_buff *skb;
};
struct llc_sap_ev_tmr_if {
void *timer_specific;
};
struct llc_sap_ev_rpt_sts_if {
u8 status;
};
union llc_sap_ev_if {
struct llc_sap_ev_simple_if a; /* 'a' for simple, easy ... */
struct llc_sap_ev_prim_if prim;
struct llc_sap_ev_pdu_if pdu;
struct llc_sap_ev_tmr_if tmr;
struct llc_sap_ev_rpt_sts_if rsts; /* report status */
};
struct llc_prim_if_block;
struct llc_sap_state_ev {
u8 type;
u8 ind_cfm_flag;
struct llc_prim_if_block *prim;
union llc_sap_ev_if data;
};
struct llc_sap;
typedef int (*llc_sap_ev_t)(struct llc_sap *sap, struct llc_sap_state_ev *ev);
extern int llc_sap_ev_activation_req(struct llc_sap *sap,
struct llc_sap_state_ev *ev);
extern int llc_sap_ev_rx_ui(struct llc_sap *sap, struct llc_sap_state_ev *ev);
extern int llc_sap_ev_unitdata_req(struct llc_sap *sap,
struct llc_sap_state_ev *ev);
extern int llc_sap_ev_xid_req(struct llc_sap *sap,
struct llc_sap_state_ev *ev);
extern int llc_sap_ev_rx_xid_c(struct llc_sap *sap,
struct llc_sap_state_ev *ev);
extern int llc_sap_ev_rx_xid_r(struct llc_sap *sap,
struct llc_sap_state_ev *ev);
extern int llc_sap_ev_test_req(struct llc_sap *sap,
struct llc_sap_state_ev *ev);
extern int llc_sap_ev_rx_test_c(struct llc_sap *sap,
struct llc_sap_state_ev *ev);
extern int llc_sap_ev_rx_test_r(struct llc_sap *sap,
struct llc_sap_state_ev *ev);
extern int llc_sap_ev_deactivation_req(struct llc_sap *sap,
struct llc_sap_state_ev *ev);
#endif /* LLC_S_EV_H */
#ifndef LLC_S_ST_H
#define LLC_S_ST_H
/*
* Copyright (c) 1997 by Procom Technology,Inc.
* 2001 by Arnaldo Carvalho de Melo <acme@conectiva.com.br>
*
* This program can be redistributed or modified under the terms of the
* GNU General Public License as published by the Free Software Foundation.
* This program is distributed without any warranty or implied warranty
* of merchantability or fitness for a particular purpose.
*
* See the GNU General Public License for more details.
*/
/* Defines SAP component states */
#define LLC_SAP_STATE_INACTIVE 1
#define LLC_SAP_STATE_ACTIVE 2
#define LLC_NBR_SAP_STATES 2 /* size of state table */
/* structures and types */
/* SAP state table structure */
struct llc_sap_state_trans {
llc_sap_ev_t ev;
u8 next_state;
llc_sap_action_t *ev_actions;
};
struct llc_sap_state {
u8 curr_state;
struct llc_sap_state_trans **transitions;
};
/* only access to SAP state table */
extern struct llc_sap_state llc_sap_state_table[LLC_NBR_SAP_STATES];
#endif /* LLC_S_ST_H */
#ifndef LLC_SAP_H
#define LLC_SAP_H
/*
* Copyright (c) 1997 by Procom Technology,Inc.
* 2001 by Arnaldo Carvalho de Melo <acme@conectiva.com.br>
*
* This program can be redistributed or modified under the terms of the
* GNU General Public License as published by the Free Software Foundation.
* This program is distributed without any warranty or implied warranty
* of merchantability or fitness for a particular purpose.
*
* See the GNU General Public License for more details.
*/
#include <linux/skbuff.h>
/* Defines the SAP component */
struct llc_sap {
u8 state;
struct llc_station *parent_station;
u8 p_bit; /* only lowest-order bit used */
u8 f_bit; /* only lowest-order bit used */
llc_prim_call_t req; /* provided by LLC layer */
llc_prim_call_t resp; /* provided by LLC layer */
llc_prim_call_t ind; /* provided by network layer */
llc_prim_call_t conf; /* provided by network layer */
struct llc_addr laddr; /* SAP value in this 'lsap' */
struct list_head node; /* entry in station sap_list */
struct {
spinlock_t lock;
struct list_head list;
} sk_list; /* LLC sockets this one manages */
struct sk_buff_head mac_pdu_q; /* PDUs ready to send to MAC */
};
struct llc_sap_state_ev;
extern void llc_sap_assign_sock(struct llc_sap *sap, struct sock *sk);
extern void llc_sap_unassign_sock(struct llc_sap *sap, struct sock *sk);
extern void llc_sap_send_ev(struct llc_sap *sap, struct llc_sap_state_ev *ev);
extern void llc_sap_rtn_pdu(struct llc_sap *sap, struct sk_buff *skb,
struct llc_sap_state_ev *ev);
extern void llc_sap_send_pdu(struct llc_sap *sap, struct sk_buff *skb);
extern struct llc_sap_state_ev *llc_sap_alloc_ev(struct llc_sap *sap);
#endif /* LLC_SAP_H */
#ifndef LLC_STAT_H
#define LLC_STAT_H
/*
* Copyright (c) 1997 by Procom Technology,Inc.
* 2001 by Arnaldo Carvalho de Melo <acme@conectiva.com.br>
*
* This program can be redistributed or modified under the terms of the
* GNU General Public License as published by the Free Software Foundation.
* This program is distributed without any warranty or implied warranty
* of merchantability or fitness for a particular purpose.
*
* See the GNU General Public License for more details.
*/
/* Station component state table */
/* Station component states */
#define LLC_STATION_STATE_DOWN 1 /* initial state */
#define LLC_STATION_STATE_DUP_ADDR_CHK 2
#define LLC_STATION_STATE_UP 3
#define LLC_NBR_STATION_STATES 3 /* size of state table */
/* Station component state table structure */
struct llc_station_state_trans {
llc_station_ev_t ev;
u8 next_state;
llc_station_action_t *ev_actions;
};
struct llc_station_state {
u8 curr_state;
struct llc_station_state_trans **transitions;
};
extern struct llc_station_state llc_station_state_table[LLC_NBR_STATION_STATES];
#endif /* LLC_STAT_H */
#ifndef _NET_P8022_H #ifndef _NET_P8022_H
#define _NET_P8022_H #define _NET_P8022_H
extern struct datalink_proto *register_8022_client(unsigned char type,
extern struct datalink_proto *register_8022_client(unsigned char type, int (*rcvfunc)(struct sk_buff *, struct net_device *, struct packet_type *)); int (*rcvfunc)
(struct sk_buff *,
struct net_device *,
struct packet_type *));
extern void unregister_8022_client(unsigned char type); extern void unregister_8022_client(unsigned char type);
#endif #endif
...@@ -11,11 +11,10 @@ ...@@ -11,11 +11,10 @@
* matches. The control byte is ignored and handling of such items * matches. The control byte is ignored and handling of such items
* is up to the routine passed the frame. * is up to the routine passed the frame.
* *
* Unlike the 802.3 datalink we have a list of 802.2 entries as there * Unlike the 802.3 datalink we have a list of 802.2 entries as
* are multiple protocols to demux. The list is currently short (3 or * there are multiple protocols to demux. The list is currently
* 4 entries at most). The current demux assumes this. * short (3 or 4 entries at most). The current demux assumes this.
*/ */
#include <linux/module.h> #include <linux/module.h>
#include <linux/netdevice.h> #include <linux/netdevice.h>
#include <linux/skbuff.h> #include <linux/skbuff.h>
...@@ -25,8 +24,13 @@ ...@@ -25,8 +24,13 @@
#include <linux/init.h> #include <linux/init.h>
#include <net/p8022.h> #include <net/p8022.h>
static struct datalink_proto *p8022_list = NULL; extern void llc_register_sap(unsigned char sap,
int (*rcvfunc)(struct sk_buff *,
struct net_device *,
struct packet_type *));
extern void llc_unregister_sap(unsigned char sap);
static struct datalink_proto *p8022_list;
/* /*
* We don't handle the loopback SAP stuff, the extended * We don't handle the loopback SAP stuff, the extended
* 802.2 command set, multicast SAP identifiers and non UI * 802.2 command set, multicast SAP identifiers and non UI
...@@ -34,80 +38,57 @@ static struct datalink_proto *p8022_list = NULL; ...@@ -34,80 +38,57 @@ static struct datalink_proto *p8022_list = NULL;
* IP and Appletalk phase 2. See the llc_* routines for * IP and Appletalk phase 2. See the llc_* routines for
* support libraries if your protocol needs these. * support libraries if your protocol needs these.
*/ */
static struct datalink_proto *find_8022_client(unsigned char type) static struct datalink_proto *find_8022_client(unsigned char type)
{ {
struct datalink_proto *proto; struct datalink_proto *proto = p8022_list;
for (proto = p8022_list;
((proto != NULL) && (*(proto->type) != type));
proto = proto->next)
;
while (proto && *(proto->type) != type)
proto = proto->next;
return proto; return proto;
} }
int p8022_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt) int p8022_rcv(struct sk_buff *skb, struct net_device *dev,
struct packet_type *pt)
{ {
struct datalink_proto *proto; struct datalink_proto *proto;
int rc = 0;
proto = find_8022_client(*(skb->h.raw)); proto = find_8022_client(*(skb->h.raw));
if (proto != NULL) if (!proto) {
{
skb->h.raw += 3;
skb->nh.raw += 3;
skb_pull(skb,3);
return proto->rcvfunc(skb, dev, pt);
}
skb->sk = NULL; skb->sk = NULL;
kfree_skb(skb); kfree_skb(skb);
return 0; goto out;
}
skb->h.raw += 3;
skb->nh.raw += 3;
skb_pull(skb, 3);
rc = proto->rcvfunc(skb, dev, pt);
out: return rc;
} }
static void p8022_datalink_header(struct datalink_proto *dl, static void p8022_datalink_header(struct datalink_proto *dl,
struct sk_buff *skb, unsigned char *dest_node) struct sk_buff *skb, unsigned char *dest_node)
{ {
struct net_device *dev = skb->dev; struct net_device *dev = skb->dev;
unsigned char *rawp; unsigned char *rawp = skb_push(skb, 3);
rawp = skb_push(skb,3);
*rawp++ = dl->type[0]; *rawp++ = dl->type[0];
*rawp++ = dl->type[0]; *rawp++ = dl->type[0];
*rawp = 0x03; /* UI */ *rawp = 0x03; /* UI */
dev->hard_header(skb, dev, ETH_P_802_3, dest_node, NULL, skb->len); dev->hard_header(skb, dev, ETH_P_802_3, dest_node, NULL, skb->len);
} }
static struct packet_type p8022_packet_type = struct datalink_proto *register_8022_client(unsigned char type,
int (*rcvfunc)(struct sk_buff *,
struct net_device *,
struct packet_type *))
{ {
0, /* MUTTER ntohs(ETH_P_8022),*/ struct datalink_proto *proto = NULL;
NULL, /* All devices */
p8022_rcv,
NULL,
NULL,
};
EXPORT_SYMBOL(register_8022_client);
EXPORT_SYMBOL(unregister_8022_client);
static int __init p8022_init(void) if (find_8022_client(type))
{ goto out;
p8022_packet_type.type=htons(ETH_P_802_2); proto = kmalloc(sizeof(*proto), GFP_ATOMIC);
dev_add_pack(&p8022_packet_type); if (proto) {
return 0;
}
module_init(p8022_init);
struct datalink_proto *register_8022_client(unsigned char type, int (*rcvfunc)(struct sk_buff *, struct net_device *, struct packet_type *))
{
struct datalink_proto *proto;
if (find_8022_client(type) != NULL)
return NULL;
proto = (struct datalink_proto *) kmalloc(sizeof(*proto), GFP_ATOMIC);
if (proto != NULL) {
proto->type[0] = type; proto->type[0] = type;
proto->type_len = 1; proto->type_len = 1;
proto->rcvfunc = rcvfunc; proto->rcvfunc = rcvfunc;
...@@ -116,9 +97,9 @@ struct datalink_proto *register_8022_client(unsigned char type, int (*rcvfunc)(s ...@@ -116,9 +97,9 @@ struct datalink_proto *register_8022_client(unsigned char type, int (*rcvfunc)(s
proto->string_name = "802.2"; proto->string_name = "802.2";
proto->next = p8022_list; proto->next = p8022_list;
p8022_list = proto; p8022_list = proto;
llc_register_sap(type, p8022_rcv);
} }
out: return proto;
return proto;
} }
void unregister_8022_client(unsigned char type) void unregister_8022_client(unsigned char type)
...@@ -128,17 +109,18 @@ void unregister_8022_client(unsigned char type) ...@@ -128,17 +109,18 @@ void unregister_8022_client(unsigned char type)
save_flags(flags); save_flags(flags);
cli(); cli();
while (*clients) {
while ((tmp = *clients) != NULL) tmp = *clients;
{
if (tmp->type[0] == type) { if (tmp->type[0] == type) {
*clients = tmp->next; *clients = tmp->next;
kfree(tmp); kfree(tmp);
llc_unregister_sap(type);
break; break;
} else {
clients = &tmp->next;
} }
clients = &tmp->next;
} }
restore_flags(flags); restore_flags(flags);
} }
EXPORT_SYMBOL(register_8022_client);
EXPORT_SYMBOL(unregister_8022_client);
...@@ -64,11 +64,12 @@ dep_tristate '802.1d Ethernet Bridging' CONFIG_BRIDGE $CONFIG_INET ...@@ -64,11 +64,12 @@ dep_tristate '802.1d Ethernet Bridging' CONFIG_BRIDGE $CONFIG_INET
if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
tristate 'CCITT X.25 Packet Layer (EXPERIMENTAL)' CONFIG_X25 tristate 'CCITT X.25 Packet Layer (EXPERIMENTAL)' CONFIG_X25
tristate 'LAPB Data Link Driver (EXPERIMENTAL)' CONFIG_LAPB tristate 'LAPB Data Link Driver (EXPERIMENTAL)' CONFIG_LAPB
bool '802.2 LLC (EXPERIMENTAL)' CONFIG_LLC tristate 'ANSI/IEEE 802.2 Data link layer protocol' CONFIG_LLC
if [ "$CONFIG_LLC" != "n" ]; then
# When NETBEUI is added the following line will be a tristate
define_bool CONFIG_LLC_UI y
fi
bool 'Frame Diverter (EXPERIMENTAL)' CONFIG_NET_DIVERT bool 'Frame Diverter (EXPERIMENTAL)' CONFIG_NET_DIVERT
# if [ "$CONFIG_LLC" = "y" ]; then
# bool ' Netbeui (EXPERIMENTAL)' CONFIG_NETBEUI
# fi
if [ "$CONFIG_INET" = "y" ]; then if [ "$CONFIG_INET" = "y" ]; then
tristate 'Acorn Econet/AUN protocols (EXPERIMENTAL)' CONFIG_ECONET tristate 'Acorn Econet/AUN protocols (EXPERIMENTAL)' CONFIG_ECONET
if [ "$CONFIG_ECONET" != "n" ]; then if [ "$CONFIG_ECONET" != "n" ]; then
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
# Makefile for the Linux networking core. # Makefile for the Linux networking core.
# #
export-objs := netfilter.o profile.o export-objs := ext8022.o netfilter.o profile.o
obj-y := sock.o skbuff.o iovec.o datagram.o scm.o obj-y := sock.o skbuff.o iovec.o datagram.o scm.o
...@@ -16,6 +16,10 @@ obj-$(CONFIG_FILTER) += filter.o ...@@ -16,6 +16,10 @@ obj-$(CONFIG_FILTER) += filter.o
obj-$(CONFIG_NET) += dev.o dev_mcast.o dst.o neighbour.o rtnetlink.o utils.o obj-$(CONFIG_NET) += dev.o dev_mcast.o dst.o neighbour.o rtnetlink.o utils.o
ifneq ($(CONFIG_LLC),n)
obj-y += ext8022.o
endif
obj-$(CONFIG_NETFILTER) += netfilter.o obj-$(CONFIG_NETFILTER) += netfilter.o
obj-$(CONFIG_NET_DIVERT) += dv.o obj-$(CONFIG_NET_DIVERT) += dv.o
obj-$(CONFIG_NET_PROFILE) += profile.o obj-$(CONFIG_NET_PROFILE) += profile.o
......
/*
* (ext8022.c)
*
* Copyright (c) 1997 by Procom Technology, Inc.
* 2001 by Arnaldo Carvalho de Melo <acme@conectiva.com.br>
*
* This program can be redistributed or modified under the terms of the
* GNU General Public License as published by the Free Software Foundation.
* This program is distributed without any warranty or implied warranty
* of merchantability or fitness for a particular purpose.
*
* See the GNU General Public License for more details.
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/netdevice.h>
#include <linux/brlock.h>
typedef int (*func_type)(struct sk_buff *skb, struct net_device *dev,
struct packet_type *pt);
static int llc_rcv(struct sk_buff *skb, struct net_device *dev,
struct packet_type *);
static func_type llc_sap_table[128];
static int llc_users;
static struct packet_type llc_packet_type = {
type: __constant_htons(ETH_P_802_2),
func: llc_rcv,
};
static struct packet_type llc_tr_packet_type = {
type: __constant_htons(ETH_P_TR_802_2),
func: llc_rcv,
};
static int llc_rcv(struct sk_buff *skb, struct net_device *dev,
struct packet_type *pt)
{
unsigned char n = (*(skb->h.raw)) >> 1;
br_read_lock(BR_LLC_LOCK);
if (llc_sap_table[n])
llc_sap_table[n](skb, dev, pt);
else
kfree_skb(skb);
br_read_unlock(BR_LLC_LOCK);
return 0;
}
void llc_register_sap(unsigned char sap, func_type rcvfunc)
{
sap >>= 1;
br_write_lock_bh(BR_LLC_LOCK);
llc_sap_table[sap] = rcvfunc;
if (!llc_users) {
dev_add_pack(&llc_packet_type);
dev_add_pack(&llc_tr_packet_type);
}
llc_users++;
br_write_unlock_bh(BR_LLC_LOCK);
}
void llc_unregister_sap(unsigned char sap)
{
sap >>= 1;
br_write_lock_bh(BR_LLC_LOCK);
llc_sap_table[sap] = NULL;
if (!--llc_users) {
dev_remove_pack(&llc_packet_type);
dev_remove_pack(&llc_tr_packet_type);
}
br_write_unlock_bh(BR_LLC_LOCK);
}
EXPORT_SYMBOL(llc_register_sap);
EXPORT_SYMBOL(llc_unregister_sap);
###########################################################################
# Makefile for the Linux 802.2 LLC (fully-functional) layer.
#
# Note 1! Dependencies are done automagically by 'make dep', which also
# removes any old dependencies. DON'T put your own dependencies here
# unless it's something special (ie not a .c file).
#
# Note 2! The CFLAGS definition is now in the main makefile...
#
# Copyright (c) 1997 by Procom Technology,Inc.
# 2001 by Arnaldo Carvalho de Melo <acme@conectiva.com.br>
#
# This program can be redistributed or modified under the terms of the
# GNU General Public License as published by the Free Software Foundation.
# This program is distributed without any warranty or implied warranty
# of merchantability or fitness for a particular purpose.
#
# See the GNU General Public License for more details.
###########################################################################
O_TARGET := llc.o
obj-y := llc_if.o llc_c_ev.o llc_c_ac.o llc_mac.o llc_sap.o llc_s_st.o \
llc_main.o llc_s_ac.o llc_conn.o llc_c_st.o llc_stat.o llc_actn.o \
llc_s_ev.o llc_evnt.o llc_pdu.o
ifeq ($(CONFIG_LLC_UI),y)
obj-y += llc_sock.o
endif
# Objects that export symbols.
export-objs := llc_if.o
ifeq ($(CONFIG_LLC),m)
obj-m += $(O_TARGET)
endif
include $(TOPDIR)/Rules.make
/*
* llc_actn.c - Implementation of actions of station component of LLC
*
* Description :
* Functions in this module are implementation of station component actions.
* Details of actions can be found in IEEE-802.2 standard document.
* All functions have one station and one event as input argument. All of
* them return 0 On success and 1 otherwise.
*
* Copyright (c) 1997 by Procom Technology, Inc.
* 2001 by Arnaldo Carvalho de Melo <acme@conectiva.com.br>
*
* This program can be redistributed or modified under the terms of the
* GNU General Public License as published by the Free Software Foundation.
* This program is distributed without any warranty or implied warranty
* of merchantability or fitness for a particular purpose.
*
* See the GNU General Public License for more details.
*/
#include <linux/netdevice.h>
#include <net/llc_if.h>
#include <net/llc_main.h>
#include <net/llc_evnt.h>
#include <net/llc_pdu.h>
#include <net/llc_mac.h>
static void llc_station_ack_tmr_callback(unsigned long timeout_data);
int llc_station_ac_start_ack_timer(struct llc_station *station,
struct llc_station_state_ev *ev)
{
del_timer(&station->ack_timer);
station->ack_timer.expires = jiffies + LLC_ACK_TIME * HZ;
station->ack_timer.data = (unsigned long)station;
station->ack_timer.function = llc_station_ack_tmr_callback;
add_timer(&station->ack_timer);
station->ack_tmr_running = 1;
return 0;
}
int llc_station_ac_set_retry_cnt_0(struct llc_station *station,
struct llc_station_state_ev *ev)
{
station->retry_count = 0;
return 0;
}
int llc_station_ac_inc_retry_cnt_by_1(struct llc_station *station,
struct llc_station_state_ev *ev)
{
station->retry_count++;
return 0;
}
int llc_station_ac_set_xid_r_cnt_0(struct llc_station *station,
struct llc_station_state_ev *ev)
{
station->xid_r_count = 0;
return 0;
}
int llc_station_ac_inc_xid_r_cnt_by_1(struct llc_station *station,
struct llc_station_state_ev *ev)
{
station->xid_r_count++;
return 0;
}
int llc_station_ac_send_null_dsap_xid_c(struct llc_station *station,
struct llc_station_state_ev *ev)
{
int rc = 1;
struct sk_buff *skb = llc_alloc_frame();
if (!skb)
goto out;
rc = 0;
llc_pdu_header_init(skb, LLC_PDU_TYPE_U, 0, 0, LLC_PDU_CMD);
llc_pdu_init_as_xid_cmd(skb, LLC_XID_NULL_CLASS_2, 127);
lan_hdrs_init(skb, station->mac_sa, station->mac_sa);
llc_station_send_pdu(station, skb);
out:
return rc;
}
int llc_station_ac_send_xid_r(struct llc_station *station,
struct llc_station_state_ev *ev)
{
u8 mac_da[ETH_ALEN], dsap;
int rc = 1;
struct sk_buff *ev_skb;
struct sk_buff* skb = llc_alloc_frame();
if (!skb)
goto out;
rc = 0;
ev_skb = ev->data.pdu.skb;
skb->dev = ev_skb->dev;
llc_pdu_decode_sa(ev_skb, mac_da);
llc_pdu_decode_ssap(ev_skb, &dsap);
llc_pdu_header_init(skb, LLC_PDU_TYPE_U, 0, dsap, LLC_PDU_RSP);
llc_pdu_init_as_xid_rsp(skb, LLC_XID_NULL_CLASS_2, 127);
lan_hdrs_init(skb, station->mac_sa, mac_da);
llc_station_send_pdu(station, skb);
out:
return rc;
}
int llc_station_ac_send_test_r(struct llc_station *station,
struct llc_station_state_ev *ev)
{
u8 mac_da[ETH_ALEN], dsap;
int rc = 1;
struct sk_buff *ev_skb;
struct sk_buff *skb = llc_alloc_frame();
if (!skb)
goto out;
rc = 0;
ev_skb = ev->data.pdu.skb;
skb->dev = ev_skb->dev;
llc_pdu_decode_sa(ev_skb, mac_da);
llc_pdu_decode_ssap(ev_skb, &dsap);
llc_pdu_header_init(skb, LLC_PDU_TYPE_U, 0, dsap, LLC_PDU_RSP);
llc_pdu_init_as_test_rsp(skb, ev_skb);
lan_hdrs_init(skb, station->mac_sa, mac_da);
llc_station_send_pdu(station, skb);
out:
return rc;
}
int llc_station_ac_report_status(struct llc_station *station,
struct llc_station_state_ev *ev)
{
return 0;
}
static void llc_station_ack_tmr_callback(unsigned long timeout_data)
{
struct llc_station *station = (struct llc_station *)timeout_data;
struct llc_station_state_ev *ev;
station->ack_tmr_running = 0;
ev = llc_station_alloc_ev(station);
if (ev) {
ev->type = LLC_STATION_EV_TYPE_ACK_TMR;
ev->data.tmr.timer_specific = NULL;
llc_station_send_ev(station, ev);
}
}
/*
* llc_c_ac.c - actions performed during connection state transition.
*
* Description:
* Functions in this module are implementation of connection component actions
* Details of actions can be found in IEEE-802.2 standard document.
* All functions have one connection and one event as input argument. All of
* them return 0 On success and 1 otherwise.
*
* Copyright (c) 1997 by Procom Technology, Inc.
* 2001 by Arnaldo Carvalho de Melo <acme@conectiva.com.br>
*
* This program can be redistributed or modified under the terms of the
* GNU General Public License as published by the Free Software Foundation.
* This program is distributed without any warranty or implied warranty
* of merchantability or fitness for a particular purpose.
*
* See the GNU General Public License for more details.
*/
#include <linux/netdevice.h>
#include <net/llc_conn.h>
#include <net/llc_sap.h>
#include <net/sock.h>
#include <net/llc_main.h>
#include <net/llc_c_ev.h>
#include <net/llc_c_ac.h>
#include <net/llc_c_st.h>
#include <net/llc_pdu.h>
#include <net/llc_mac.h>
static void llc_conn_pf_cycle_tmr_cb(unsigned long timeout_data);
static void llc_conn_ack_tmr_cb(unsigned long timeout_data);
static void llc_conn_rej_tmr_cb(unsigned long timeout_data);
static void llc_conn_busy_tmr_cb(unsigned long timeout_data);
static int llc_conn_ac_inc_vs_by_1(struct sock *sk,
struct llc_conn_state_ev *ev);
static void llc_process_tmr_ev(struct sock *sk, struct llc_conn_state_ev *ev);
static int llc_conn_ac_data_confirm(struct sock *sk,
struct llc_conn_state_ev *ev);
#define INCORRECT 0
int llc_conn_ac_clear_remote_busy(struct sock *sk, struct llc_conn_state_ev *ev)
{
struct llc_opt *llc = llc_sk(sk);
if (llc->remote_busy_flag) {
u8 nr;
llc_pdu_sn_t *rx_pdu = (llc_pdu_sn_t *)ev->data.pdu.skb->nh.raw;
llc->remote_busy_flag = 0;
del_timer(&llc->busy_state_timer.timer);
llc->busy_state_timer.running = 0;
nr = LLC_I_GET_NR(rx_pdu);
llc_conn_resend_i_pdu_as_cmd(sk, nr, 0);
}
return 0;
}
int llc_conn_ac_conn_ind(struct sock *sk, struct llc_conn_state_ev *ev)
{
int rc = 1;
struct sk_buff *skb = ev->data.pdu.skb;
union llc_u_prim_data *prim_data = llc_ind_prim.data;
struct llc_prim_if_block *prim = &llc_ind_prim;
struct llc_sap *sap;
struct llc_opt *llc = llc_sk(sk);
llc_pdu_decode_dsap(skb, &prim_data->conn.daddr.lsap);
sap = llc_sap_find(prim_data->conn.daddr.lsap);
if (sap) {
llc_pdu_decode_sa(skb, llc->daddr.mac);
llc_pdu_decode_da(skb, llc->laddr.mac);
llc->dev = skb->dev;
prim_data->conn.pri = 0;
prim_data->conn.sk = sk;
prim_data->conn.dev = skb->dev;
memcpy(&prim_data->conn.daddr, &llc->laddr, sizeof(llc->laddr));
memcpy(&prim_data->conn.saddr, &llc->daddr, sizeof(llc->daddr));
prim->data = prim_data;
prim->prim = LLC_CONN_PRIM;
prim->sap = llc->sap;
ev->flag = 1;
ev->ind_prim = prim;
rc = 0;
}
return rc;
}
int llc_conn_ac_conn_confirm(struct sock *sk, struct llc_conn_state_ev *ev)
{
union llc_u_prim_data *prim_data = llc_cfm_prim.data;
struct sk_buff *skb = ev->data.pdu.skb;
/* FIXME: wtf, this is global, so the whole thing is really
* non reentrant...
*/
struct llc_prim_if_block *prim = &llc_cfm_prim;
struct llc_opt *llc = llc_sk(sk);
struct llc_sap *sap = llc->sap;
prim_data->conn.sk = sk;
prim_data->conn.pri = 0;
prim_data->conn.status = ev->status;
prim_data->conn.link = llc->link;
if (skb)
prim_data->conn.dev = skb->dev;
else
printk(KERN_ERR __FUNCTION__ "ev->data.pdu.skb == NULL\n");
prim->data = prim_data;
prim->prim = LLC_CONN_PRIM;
prim->sap = sap;
ev->flag = 1;
ev->cfm_prim = prim;
return 0;
}
static int llc_conn_ac_data_confirm(struct sock *sk,
struct llc_conn_state_ev *ev)
{
struct llc_prim_if_block *prim = &llc_cfm_prim;
union llc_u_prim_data *prim_data = llc_cfm_prim.data;
prim_data->data.sk = sk;
prim_data->data.pri = 0;
prim_data->data.link = llc_sk(sk)->link;
prim_data->data.status = LLC_STATUS_RECEIVED;
prim_data->data.skb = NULL;
prim->data = prim_data;
prim->prim = LLC_DATA_PRIM;
prim->sap = llc_sk(sk)->sap;
ev->flag = 1;
ev->cfm_prim = prim;
return 0;
}
int llc_conn_ac_data_ind(struct sock *sk, struct llc_conn_state_ev *ev)
{
llc_conn_rtn_pdu(sk, ev->data.pdu.skb, ev);
return 0;
}
int llc_conn_ac_disc_ind(struct sock *sk, struct llc_conn_state_ev *ev)
{
u8 reason = 0;
int rc = 1;
union llc_u_prim_data *prim_data = llc_ind_prim.data;
struct llc_prim_if_block *prim = &llc_ind_prim;
if (ev->type == LLC_CONN_EV_TYPE_PDU) {
llc_pdu_un_t *rx_pdu = (llc_pdu_un_t *)ev->data.pdu.skb->nh.raw;
if (!LLC_PDU_IS_RSP(rx_pdu) &&
!LLC_PDU_TYPE_IS_U(rx_pdu) &&
LLC_U_PDU_RSP(rx_pdu) == LLC_2_PDU_RSP_DM) {
reason = LLC_DISC_REASON_RX_DM_RSP_PDU;
rc = 0;
} else if (!LLC_PDU_IS_CMD(rx_pdu) &&
!LLC_PDU_TYPE_IS_U(rx_pdu) &&
LLC_U_PDU_CMD(rx_pdu) == LLC_2_PDU_CMD_DISC) {
reason = LLC_DISC_REASON_RX_DISC_CMD_PDU;
rc = 0;
}
} else if (ev->type == LLC_CONN_EV_TYPE_ACK_TMR) {
reason = LLC_DISC_REASON_ACK_TMR_EXP;
rc = 0;
} else {
reason = 0;
rc = 1;
}
if (!rc) {
prim_data->disc.sk = sk;
prim_data->disc.reason = reason;
prim_data->disc.link = llc_sk(sk)->link;
prim->data = prim_data;
prim->prim = LLC_DISC_PRIM;
prim->sap = llc_sk(sk)->sap;
ev->flag = 1;
ev->ind_prim = prim;
}
return rc;
}
int llc_conn_ac_disc_confirm(struct sock *sk, struct llc_conn_state_ev *ev)
{
union llc_u_prim_data *prim_data = llc_cfm_prim.data;
struct llc_prim_if_block *prim = &llc_cfm_prim;
prim_data->disc.sk = sk;
prim_data->disc.reason = ev->status;
prim_data->disc.link = llc_sk(sk)->link;
prim->data = prim_data;
prim->prim = LLC_DISC_PRIM;
prim->sap = llc_sk(sk)->sap;
ev->flag = 1;
ev->cfm_prim = prim;
return 0;
}
int llc_conn_ac_rst_ind(struct sock *sk, struct llc_conn_state_ev *ev)
{
u8 reason = 0;
int rc = 1;
llc_pdu_un_t *rx_pdu = (llc_pdu_un_t *)ev->data.pdu.skb->nh.raw;
union llc_u_prim_data *prim_data = llc_ind_prim.data;
struct llc_prim_if_block *prim = &llc_ind_prim;
struct llc_opt *llc = llc_sk(sk);
switch (ev->type) {
case LLC_CONN_EV_TYPE_PDU:
if (!LLC_PDU_IS_RSP(rx_pdu) &&
!LLC_PDU_TYPE_IS_U(rx_pdu) &&
LLC_U_PDU_RSP(rx_pdu) == LLC_2_PDU_RSP_FRMR) {
reason = LLC_RESET_REASON_LOCAL;
rc = 0;
} else if (!LLC_PDU_IS_CMD(rx_pdu) &&
!LLC_PDU_TYPE_IS_U(rx_pdu) &&
LLC_U_PDU_CMD(rx_pdu) ==
LLC_2_PDU_CMD_SABME) {
reason = LLC_RESET_REASON_REMOTE;
rc = 0;
} else {
reason = 0;
rc = 1;
}
break;
case LLC_CONN_EV_TYPE_ACK_TMR:
case LLC_CONN_EV_TYPE_P_TMR:
case LLC_CONN_EV_TYPE_REJ_TMR:
case LLC_CONN_EV_TYPE_BUSY_TMR:
if (llc->retry_count > llc->n2) {
reason = LLC_RESET_REASON_LOCAL;
rc = 0;
} else
rc = 1;
break;
}
if (!rc) {
prim_data->res.sk = sk;
prim_data->res.reason = reason;
prim_data->res.link = llc->link;
prim->data = prim_data;
prim->prim = LLC_RESET_PRIM;
prim->sap = llc->sap;
ev->flag = 1;
ev->ind_prim = prim;
}
return rc;
}
int llc_conn_ac_rst_confirm(struct sock *sk, struct llc_conn_state_ev *ev)
{
union llc_u_prim_data *prim_data = llc_cfm_prim.data;
struct llc_prim_if_block *prim = &llc_cfm_prim;
prim_data->res.sk = sk;
prim_data->res.link = llc_sk(sk)->link;
prim->data = prim_data;
prim->prim = LLC_RESET_PRIM;
prim->sap = llc_sk(sk)->sap;
ev->flag = 1;
ev->cfm_prim = prim;
return 0;
}
int llc_conn_ac_report_status(struct sock *sk, struct llc_conn_state_ev *ev)
{
return 0;
}
int llc_conn_ac_clear_remote_busy_if_f_eq_1(struct sock *sk,
struct llc_conn_state_ev *ev)
{
llc_pdu_sn_t *rx_pdu = (llc_pdu_sn_t *)ev->data.pdu.skb->nh.raw;
if (!LLC_PDU_IS_RSP(rx_pdu) &&
!LLC_PDU_TYPE_IS_I(rx_pdu) &&
!LLC_I_PF_IS_1(rx_pdu) && llc_sk(sk)->ack_pf)
llc_conn_ac_clear_remote_busy(sk, ev);
return 0;
}
int llc_conn_ac_stop_rej_tmr_if_data_flag_eq_2(struct sock *sk,
struct llc_conn_state_ev *ev)
{
if (llc_sk(sk)->data_flag == 2) {
del_timer(&llc_sk(sk)->rej_sent_timer.timer);
llc_sk(sk)->rej_sent_timer.running = 0;
}
return 0;
}
int llc_conn_ac_send_disc_cmd_p_set_x(struct sock *sk,
struct llc_conn_state_ev *ev)
{
int rc = 1;
struct sk_buff *skb = llc_alloc_frame();
if (skb) {
u8 p_bit = 1;
struct llc_opt *llc = llc_sk(sk);
struct llc_sap *sap = llc->sap;
skb->dev = llc->dev;
llc_pdu_header_init(skb, LLC_PDU_TYPE_U, sap->laddr.lsap,
llc->daddr.lsap, LLC_PDU_CMD);
llc_pdu_init_as_disc_cmd(skb, p_bit);
lan_hdrs_init(skb, llc->dev->dev_addr, llc->daddr.mac);
rc = 0;
llc_conn_send_pdu(sk, skb);
}
llc_conn_ac_set_p_flag_1(sk, ev);
return rc;
}
int llc_conn_ac_send_dm_rsp_f_set_p(struct sock *sk,
struct llc_conn_state_ev *ev)
{
int rc = 1;
struct sk_buff *skb = llc_alloc_frame();
if (skb) {
struct llc_opt *llc = llc_sk(sk);
struct llc_sap *sap = llc->sap;
struct sk_buff *rx_skb = ev->data.pdu.skb;
u8 f_bit;
skb->dev = llc->dev;
llc_pdu_decode_pf_bit(rx_skb, &f_bit);
llc_pdu_header_init(skb, LLC_PDU_TYPE_U, sap->laddr.lsap,
llc->daddr.lsap, LLC_PDU_RSP);
llc_pdu_init_as_dm_rsp(skb, f_bit);
lan_hdrs_init(skb, llc->dev->dev_addr, llc->daddr.mac);
rc = 0;
llc_conn_send_pdu(sk, skb);
}
return rc;
}
int llc_conn_ac_send_dm_rsp_f_set_1(struct sock *sk,
struct llc_conn_state_ev *ev)
{
int rc = 1;
struct sk_buff *skb = llc_alloc_frame();
if (skb) {
struct llc_opt *llc = llc_sk(sk);
struct llc_sap *sap = llc->sap;
u8 f_bit = 1;
skb->dev = llc->dev;
llc_pdu_header_init(skb, LLC_PDU_TYPE_U, sap->laddr.lsap,
llc->daddr.lsap, LLC_PDU_RSP);
llc_pdu_init_as_dm_rsp(skb, f_bit);
lan_hdrs_init(skb, llc->dev->dev_addr, llc->daddr.mac);
rc = 0;
llc_conn_send_pdu(sk, skb);
}
return rc;
}
int llc_conn_ac_send_dm_rsp_f_set_f_flag(struct sock *sk,
struct llc_conn_state_ev *ev)
{
int rc = 1;
struct sk_buff *skb = llc_alloc_frame();
if (skb) {
struct llc_opt *llc = llc_sk(sk);
struct llc_sap *sap = llc->sap;
u8 f_bit = llc->f_flag;
skb->dev = llc->dev;
llc_pdu_header_init(skb, LLC_PDU_TYPE_U, sap->laddr.lsap,
llc->daddr.lsap, LLC_PDU_RSP);
llc_pdu_init_as_dm_rsp(skb, f_bit);
lan_hdrs_init(skb, llc->dev->dev_addr, llc->daddr.mac);
rc = 0;
llc_conn_send_pdu(sk, skb);
}
return rc;
}
int llc_conn_ac_send_frmr_rsp_f_set_x(struct sock *sk,
struct llc_conn_state_ev *ev)
{
u8 f_bit;
int rc = 1;
struct sk_buff *skb, *ev_skb = ev->data.pdu.skb;
llc_pdu_sn_t *rx_pdu = (llc_pdu_sn_t *)ev_skb->nh.raw;
struct llc_opt *llc = llc_sk(sk);
llc->rx_pdu_hdr = (u32)*((u32 *)rx_pdu);
if (!LLC_PDU_IS_CMD(rx_pdu))
llc_pdu_decode_pf_bit(ev_skb, &f_bit);
else
f_bit = 0;
skb = llc_alloc_frame();
if (skb) {
struct llc_sap *sap = llc->sap;
skb->dev = llc->dev;
llc_pdu_header_init(skb, LLC_PDU_TYPE_U, sap->laddr.lsap,
llc->daddr.lsap, LLC_PDU_RSP);
llc_pdu_init_as_frmr_rsp(skb, rx_pdu, f_bit, llc->vS,
llc->vR, INCORRECT);
lan_hdrs_init(skb, llc->dev->dev_addr, llc->daddr.mac);
rc = 0;
llc_conn_send_pdu(sk, skb);
}
return rc;
}
int llc_conn_ac_resend_frmr_rsp_f_set_0(struct sock *sk,
struct llc_conn_state_ev *ev)
{
int rc = 1;
struct sk_buff *skb = llc_alloc_frame();
if (skb) {
u8 f_bit = 0;
struct llc_opt *llc = llc_sk(sk);
struct llc_sap *sap = llc->sap;
llc_pdu_sn_t *rx_pdu = (llc_pdu_sn_t *)&llc->rx_pdu_hdr;
skb->dev = llc->dev;
llc_pdu_header_init(skb, LLC_PDU_TYPE_U, sap->laddr.lsap,
llc->daddr.lsap, LLC_PDU_RSP);
llc_pdu_init_as_frmr_rsp(skb, rx_pdu, f_bit, llc->vS,
llc->vR, INCORRECT);
lan_hdrs_init(skb, llc->dev->dev_addr, llc->daddr.mac);
rc = 0;
llc_conn_send_pdu(sk, skb);
}
return rc;
}
int llc_conn_ac_resend_frmr_rsp_f_set_p(struct sock *sk,
struct llc_conn_state_ev *ev)
{
u8 f_bit;
int rc = 1;
struct sk_buff *skb;
llc_pdu_decode_pf_bit(ev->data.pdu.skb, &f_bit);
skb = llc_alloc_frame();
if (skb) {
struct llc_opt *llc = llc_sk(sk);
struct llc_sap *sap = llc->sap;
llc_pdu_sn_t *rx_pdu = (llc_pdu_sn_t *)ev->data.pdu.skb->nh.raw;
skb->dev = llc->dev;
llc_pdu_header_init(skb, LLC_PDU_TYPE_U, sap->laddr.lsap,
llc->daddr.lsap, LLC_PDU_RSP);
llc_pdu_init_as_frmr_rsp(skb, rx_pdu, f_bit, llc->vS,
llc->vR, INCORRECT);
lan_hdrs_init(skb, llc->dev->dev_addr, llc->daddr.mac);
rc = 0;
llc_conn_send_pdu(sk, skb);
}
return rc;
}
int llc_conn_ac_send_i_cmd_p_set_1(struct sock *sk,
struct llc_conn_state_ev *ev)
{
u8 p_bit = 1;
struct sk_buff *skb = ev->data.prim.data->data->data.skb;
struct llc_opt *llc = llc_sk(sk);
struct llc_sap *sap = llc->sap;
llc_pdu_header_init(skb, LLC_PDU_TYPE_I, sap->laddr.lsap,
llc->daddr.lsap, LLC_PDU_CMD);
llc_pdu_init_as_i_cmd(skb, p_bit, llc->vS, llc->vR);
lan_hdrs_init(skb, llc->dev->dev_addr, llc->daddr.mac);
llc_conn_send_pdu(sk, skb);
llc_conn_ac_inc_vs_by_1(sk, ev);
return 0;
}
int llc_conn_ac_send_i_cmd_p_set_0(struct sock *sk,
struct llc_conn_state_ev *ev)
{
u8 p_bit = 0;
struct sk_buff *skb = ev->data.prim.data->data->data.skb;
struct llc_opt *llc = llc_sk(sk);
struct llc_sap *sap = llc->sap;
llc_pdu_header_init(skb, LLC_PDU_TYPE_I, sap->laddr.lsap,
llc->daddr.lsap, LLC_PDU_CMD);
llc_pdu_init_as_i_cmd(skb, p_bit, llc->vS, llc->vR);
lan_hdrs_init(skb, llc->dev->dev_addr, llc->daddr.mac);
llc_conn_send_pdu(sk, skb);
llc_conn_ac_inc_vs_by_1(sk, ev);
return 0;
}
int llc_conn_ac_resend_i_cmd_p_set_1(struct sock *sk,
struct llc_conn_state_ev *ev)
{
llc_pdu_sn_t *rx_pdu = (llc_pdu_sn_t *)ev->data.pdu.skb->nh.raw;
u8 nr = LLC_I_GET_NR(rx_pdu);
llc_conn_resend_i_pdu_as_cmd(sk, nr, 1);
return 0;
}
int llc_conn_ac_resend_i_cmd_p_set_1_or_send_rr(struct sock *sk,
struct llc_conn_state_ev *ev)
{
llc_pdu_sn_t *rx_pdu = (llc_pdu_sn_t *)ev->data.pdu.skb->nh.raw;
u8 nr = LLC_I_GET_NR(rx_pdu);
int rc = llc_conn_ac_send_rr_cmd_p_set_1(sk, ev);
if (!rc)
llc_conn_resend_i_pdu_as_cmd(sk, nr, 0);
return rc;
}
int llc_conn_ac_send_i_xxx_x_set_0(struct sock *sk,
struct llc_conn_state_ev *ev)
{
u8 p_bit = 0;
struct sk_buff *skb = ev->data.prim.data->data->data.skb;
struct llc_opt *llc = llc_sk(sk);
struct llc_sap *sap = llc->sap;
llc_pdu_header_init(skb, LLC_PDU_TYPE_I, sap->laddr.lsap,
llc->daddr.lsap, LLC_PDU_CMD);
llc_pdu_init_as_i_cmd(skb, p_bit, llc->vS, llc->vR);
lan_hdrs_init(skb, llc->dev->dev_addr, llc->daddr.mac);
llc_conn_send_pdu(sk, skb);
llc_conn_ac_inc_vs_by_1(sk, ev);
return 0;
}
int llc_conn_ac_resend_i_xxx_x_set_0(struct sock *sk,
struct llc_conn_state_ev *ev)
{
llc_pdu_sn_t *rx_pdu = (llc_pdu_sn_t *)ev->data.pdu.skb->nh.raw;
u8 nr = LLC_I_GET_NR(rx_pdu);
llc_conn_resend_i_pdu_as_cmd(sk, nr, 0);
return 0;
}
int llc_conn_ac_resend_i_xxx_x_set_0_or_send_rr(struct sock *sk,
struct llc_conn_state_ev *ev)
{
u8 nr;
u8 f_bit = 0;
llc_pdu_sn_t *rx_pdu = (llc_pdu_sn_t *)ev->data.pdu.skb->nh.raw;
int rc = 1;
struct sk_buff *skb = llc_alloc_frame();
if (skb) {
struct llc_opt *llc = llc_sk(sk);
struct llc_sap *sap = llc->sap;
skb->dev = llc->dev;
llc_pdu_header_init(skb, LLC_PDU_TYPE_U, sap->laddr.lsap,
llc->daddr.lsap, LLC_PDU_RSP);
llc_pdu_init_as_rr_rsp(skb, f_bit, llc->vR);
lan_hdrs_init(skb, llc->dev->dev_addr, llc->daddr.mac);
rc = 0;
llc_conn_send_pdu(sk, skb);
}
if (rc) {
nr = LLC_I_GET_NR(rx_pdu);
rc = 0;
llc_conn_resend_i_pdu_as_cmd(sk, nr, f_bit);
}
return rc;
}
int llc_conn_ac_resend_i_rsp_f_set_1(struct sock *sk,
struct llc_conn_state_ev *ev)
{
llc_pdu_sn_t *rx_pdu = (llc_pdu_sn_t *)ev->data.pdu.skb->nh.raw;
u8 nr = LLC_I_GET_NR(rx_pdu);
llc_conn_resend_i_pdu_as_rsp(sk, nr, 1);
return 0;
}
int llc_conn_ac_send_rej_cmd_p_set_1(struct sock *sk,
struct llc_conn_state_ev *ev)
{
int rc = 1;
struct sk_buff *skb = llc_alloc_frame();
if (skb) {
struct llc_opt *llc = llc_sk(sk);
struct llc_sap *sap = llc->sap;
u8 p_bit = 1;
skb->dev = llc->dev;
llc_pdu_header_init(skb, LLC_PDU_TYPE_S, sap->laddr.lsap,
llc->daddr.lsap, LLC_PDU_CMD);
llc_pdu_init_as_rej_cmd(skb, p_bit, llc->vR);
lan_hdrs_init(skb, llc->dev->dev_addr, llc->daddr.mac);
rc = 0;
llc_conn_send_pdu(sk, skb);
}
return rc;
}
int llc_conn_ac_send_rej_rsp_f_set_1(struct sock *sk,
struct llc_conn_state_ev *ev)
{
int rc = 1;
struct sk_buff *skb = llc_alloc_frame();
if (skb) {
u8 f_bit = 1;
struct llc_opt *llc = llc_sk(sk);
struct llc_sap *sap = llc->sap;
skb->dev = llc->dev;
llc_pdu_header_init(skb, LLC_PDU_TYPE_S, sap->laddr.lsap,
llc->daddr.lsap, LLC_PDU_RSP);
llc_pdu_init_as_rej_rsp(skb, f_bit, llc->vR);
lan_hdrs_init(skb, llc->dev->dev_addr, llc->daddr.mac);
rc = 0;
llc_conn_send_pdu(sk, skb);
}
return rc;
}
int llc_conn_ac_send_rej_xxx_x_set_0(struct sock *sk,
struct llc_conn_state_ev *ev)
{
int rc = 1;
struct sk_buff *skb = llc_alloc_frame();
if (skb) {
struct llc_opt *llc = llc_sk(sk);
struct llc_sap *sap = llc->sap;
u8 f_bit = 0;
skb->dev = llc->dev;
llc_pdu_header_init(skb, LLC_PDU_TYPE_S, sap->laddr.lsap,
llc->daddr.lsap, LLC_PDU_RSP);
llc_pdu_init_as_rej_rsp(skb, f_bit, llc->vR);
lan_hdrs_init(skb, llc->dev->dev_addr, llc->daddr.mac);
rc = 0;
llc_conn_send_pdu(sk, skb);
}
return rc;
}
int llc_conn_ac_send_rnr_cmd_p_set_1(struct sock *sk,
struct llc_conn_state_ev *ev)
{
int rc = 1;
struct sk_buff *skb = llc_alloc_frame();
if (skb) {
struct llc_opt *llc = llc_sk(sk);
struct llc_sap *sap = llc->sap;
u8 p_bit = 1;
skb->dev = llc->dev;
llc_pdu_header_init(skb, LLC_PDU_TYPE_S, sap->laddr.lsap,
llc->daddr.lsap, LLC_PDU_CMD);
llc_pdu_init_as_rnr_cmd(skb, p_bit, llc->vR);
lan_hdrs_init(skb, llc->dev->dev_addr, llc->daddr.mac);
rc = 0;
llc_conn_send_pdu(sk, skb);
}
return rc;
}
int llc_conn_ac_send_rnr_rsp_f_set_1(struct sock *sk,
struct llc_conn_state_ev *ev)
{
int rc = 1;
struct sk_buff *skb = llc_alloc_frame();
if (skb) {
struct llc_opt *llc = llc_sk(sk);
struct llc_sap *sap = llc->sap;
u8 f_bit = 1;
skb->dev = llc->dev;
llc_pdu_header_init(skb, LLC_PDU_TYPE_S, sap->laddr.lsap,
llc->daddr.lsap, LLC_PDU_RSP);
llc_pdu_init_as_rnr_rsp(skb, f_bit, llc->vR);
lan_hdrs_init(skb, llc->dev->dev_addr, llc->daddr.mac);
rc = 0;
llc_conn_send_pdu(sk, skb);
}
return rc;
}
int llc_conn_ac_send_rnr_xxx_x_set_0(struct sock *sk,
struct llc_conn_state_ev *ev)
{
int rc = 1;
struct sk_buff *skb = llc_alloc_frame();
if (skb) {
u8 f_bit = 0;
struct llc_opt *llc = llc_sk(sk);
struct llc_sap *sap = llc->sap;
skb->dev = llc->dev;
llc_pdu_header_init(skb, LLC_PDU_TYPE_S, sap->laddr.lsap,
llc->daddr.lsap, LLC_PDU_RSP);
llc_pdu_init_as_rnr_rsp(skb, f_bit, llc->vR);
lan_hdrs_init(skb, llc->dev->dev_addr, llc->daddr.mac);
rc = 0;
llc_conn_send_pdu(sk, skb);
}
return rc;
}
int llc_conn_ac_set_remote_busy(struct sock *sk, struct llc_conn_state_ev *ev)
{
struct llc_opt *llc = llc_sk(sk);
if (!llc->remote_busy_flag) {
llc->remote_busy_flag = 1;
llc->busy_state_timer.timer.expires = jiffies +
llc->busy_state_timer.expire * HZ;
llc->busy_state_timer.timer.data = (unsigned long)sk;
llc->busy_state_timer.timer.function = llc_conn_busy_tmr_cb;
add_timer(&llc->busy_state_timer.timer);
llc->busy_state_timer.running = 1;
}
return 0;
}
int llc_conn_ac_opt_send_rnr_xxx_x_set_0(struct sock *sk,
struct llc_conn_state_ev *ev)
{
int rc = 1;
struct sk_buff *skb = llc_alloc_frame();
if (skb) {
struct llc_opt *llc = llc_sk(sk);
struct llc_sap *sap = llc->sap;
u8 f_bit = 0;
skb->dev = llc->dev;
llc_pdu_header_init(skb, LLC_PDU_TYPE_S, sap->laddr.lsap,
llc->daddr.lsap, LLC_PDU_RSP);
llc_pdu_init_as_rnr_rsp(skb, f_bit, llc->vR);
lan_hdrs_init(skb, llc->dev->dev_addr, llc->daddr.mac);
rc = 0;
llc_conn_send_pdu(sk, skb);
}
return rc;
}
int llc_conn_ac_send_rr_cmd_p_set_1(struct sock *sk,
struct llc_conn_state_ev *ev)
{
int rc = 1;
struct sk_buff *skb = llc_alloc_frame();
if (skb) {
u8 p_bit = 1;
struct llc_opt *llc = llc_sk(sk);
struct llc_sap *sap = llc->sap;
skb->dev = llc->dev;
llc_pdu_header_init(skb, LLC_PDU_TYPE_S, sap->laddr.lsap,
llc->daddr.lsap, LLC_PDU_CMD);
llc_pdu_init_as_rr_cmd(skb, p_bit, llc->vR);
lan_hdrs_init(skb, llc->dev->dev_addr, llc->daddr.mac);
rc = 0;
llc_conn_send_pdu(sk, skb);
}
return rc;
}
int llc_conn_ac_send_ack_cmd_p_set_1(struct sock *sk,
struct llc_conn_state_ev *ev)
{
int rc = 1;
struct sk_buff *skb = llc_alloc_frame();
if (skb) {
u8 p_bit = 1;
struct llc_opt *llc = llc_sk(sk);
struct llc_sap *sap = llc->sap;
skb->dev = llc->dev;
llc_pdu_header_init(skb, LLC_PDU_TYPE_S, sap->laddr.lsap,
llc->daddr.lsap, LLC_PDU_CMD);
llc_pdu_init_as_rr_cmd(skb, p_bit, llc->vR);
lan_hdrs_init(skb, llc->dev->dev_addr, llc->daddr.mac);
rc = 0;
llc_conn_send_pdu(sk, skb);
}
return rc;
}
int llc_conn_ac_send_rr_rsp_f_set_1(struct sock *sk,
struct llc_conn_state_ev *ev)
{
int rc = 1;
struct sk_buff *skb = llc_alloc_frame();
if (skb) {
struct llc_opt *llc = llc_sk(sk);
struct llc_sap *sap = llc->sap;
u8 f_bit = 1;
skb->dev = llc->dev;
llc_pdu_header_init(skb, LLC_PDU_TYPE_S, sap->laddr.lsap,
llc->daddr.lsap, LLC_PDU_RSP);
llc_pdu_init_as_rr_rsp(skb, f_bit, llc->vR);
lan_hdrs_init(skb, llc->dev->dev_addr, llc->daddr.mac);
rc = 0;
llc_conn_send_pdu(sk, skb);
}
return rc;
}
int llc_conn_ac_send_ack_rsp_f_set_1(struct sock *sk,
struct llc_conn_state_ev *ev)
{
int rc = 1;
struct sk_buff *skb = llc_alloc_frame();
if (skb) {
struct llc_opt *llc = llc_sk(sk);
struct llc_sap *sap = llc->sap;
u8 f_bit = 1;
skb->dev = llc->dev;
llc_pdu_header_init(skb, LLC_PDU_TYPE_S, sap->laddr.lsap,
llc->daddr.lsap, LLC_PDU_RSP);
llc_pdu_init_as_rr_rsp(skb, f_bit, llc->vR);
lan_hdrs_init(skb, llc->dev->dev_addr, llc->daddr.mac);
rc = 0;
llc_conn_send_pdu(sk, skb);
}
return rc;
}
int llc_conn_ac_send_rr_xxx_x_set_0(struct sock *sk,
struct llc_conn_state_ev *ev)
{
int rc = 1;
struct sk_buff *skb = llc_alloc_frame();
if (skb) {
struct llc_opt *llc = llc_sk(sk);
struct llc_sap *sap = llc->sap;
u8 f_bit = 0;
skb->dev = llc->dev;
llc_pdu_header_init(skb, LLC_PDU_TYPE_S, sap->laddr.lsap,
llc->daddr.lsap, LLC_PDU_RSP);
llc_pdu_init_as_rr_rsp(skb, f_bit, llc->vR);
lan_hdrs_init(skb, llc->dev->dev_addr, llc->daddr.mac);
rc = 0;
llc_conn_send_pdu(sk, skb);
}
return rc;
}
int llc_conn_ac_send_ack_xxx_x_set_0(struct sock *sk,
struct llc_conn_state_ev *ev)
{
int rc = 1;
struct sk_buff *skb = llc_alloc_frame();
if (skb) {
struct llc_opt *llc = llc_sk(sk);
struct llc_sap *sap = llc->sap;
u8 f_bit = 0;
skb->dev = llc->dev;
llc_pdu_header_init(skb, LLC_PDU_TYPE_S, sap->laddr.lsap,
llc->daddr.lsap, LLC_PDU_RSP);
llc_pdu_init_as_rr_rsp(skb, f_bit, llc->vR);
lan_hdrs_init(skb, llc->dev->dev_addr, llc->daddr.mac);
rc = 0;
llc_conn_send_pdu(sk, skb);
}
return rc;
}
int llc_conn_ac_send_sabme_cmd_p_set_x(struct sock *sk,
struct llc_conn_state_ev *ev)
{
int rc = 1;
struct sk_buff *skb = llc_alloc_frame();
struct llc_opt *llc = llc_sk(sk);
u8 p_bit = 1;
if (skb) {
struct llc_sap *sap = llc->sap;
u8 *dmac = llc->daddr.mac;
if (llc->dev->flags & IFF_LOOPBACK)
dmac = llc->dev->dev_addr;
skb->dev = llc->dev;
llc_pdu_header_init(skb, LLC_PDU_TYPE_U, sap->laddr.lsap,
llc->daddr.lsap, LLC_PDU_CMD);
llc_pdu_init_as_sabme_cmd(skb, p_bit);
lan_hdrs_init(skb, llc->dev->dev_addr, dmac);
rc = 0;
llc_conn_send_pdu(sk, skb);
}
llc->p_flag = p_bit;
return rc;
}
int llc_conn_ac_send_ua_rsp_f_set_f_flag(struct sock *sk,
struct llc_conn_state_ev *ev)
{
int rc = 1;
struct sk_buff *skb = llc_alloc_frame();
if (skb) {
struct llc_opt *llc = llc_sk(sk);
struct llc_sap *sap = llc->sap;
u8 f_bit = llc->f_flag;
skb->dev = llc->dev;
llc_pdu_header_init(skb, LLC_PDU_TYPE_U, sap->laddr.lsap,
llc->daddr.lsap, LLC_PDU_RSP);
llc_pdu_init_as_ua_rsp(skb, f_bit);
lan_hdrs_init(skb, llc->dev->dev_addr, llc->daddr.mac);
rc = 0;
llc_conn_send_pdu(sk, skb);
}
return rc;
}
int llc_conn_ac_send_ua_rsp_f_set_p(struct sock *sk,
struct llc_conn_state_ev *ev)
{
u8 f_bit;
int rc = 1;
struct sk_buff *rx_skb = ev->data.pdu.skb;
struct sk_buff *skb;
llc_pdu_decode_pf_bit(rx_skb, &f_bit);
skb = llc_alloc_frame();
if (skb) {
struct llc_opt *llc = llc_sk(sk);
struct llc_sap *sap = llc->sap;
skb->dev = llc->dev;
llc_pdu_header_init(skb, LLC_PDU_TYPE_U, sap->laddr.lsap,
llc->daddr.lsap, LLC_PDU_RSP);
llc_pdu_init_as_ua_rsp(skb, f_bit);
lan_hdrs_init(skb, llc->dev->dev_addr, llc->daddr.mac);
rc = 0;
llc_conn_send_pdu(sk, skb);
}
return rc;
}
int llc_conn_ac_set_s_flag_0(struct sock *sk, struct llc_conn_state_ev *ev)
{
llc_sk(sk)->s_flag = 0;
return 0;
}
int llc_conn_ac_set_s_flag_1(struct sock *sk, struct llc_conn_state_ev *ev)
{
llc_sk(sk)->s_flag = 1;
return 0;
}
int llc_conn_ac_start_p_timer(struct sock *sk, struct llc_conn_state_ev *ev)
{
struct llc_opt *llc = llc_sk(sk);
llc->p_flag = 1;
del_timer(&llc->pf_cycle_timer.timer);
llc->pf_cycle_timer.timer.expires = jiffies +
llc->pf_cycle_timer.expire * HZ;
llc->pf_cycle_timer.timer.data = (unsigned long)sk;
llc->pf_cycle_timer.timer.function = llc_conn_pf_cycle_tmr_cb;
add_timer(&llc->pf_cycle_timer.timer);
llc->pf_cycle_timer.running = 1;
return 0;
}
/**
* llc_conn_ac_send_ack_if_needed - check if ack is needed
* @sk: current connection structure
* @ev: current event
*
* Checks number of received PDUs which have not been acknowledged, yet,
* If number of them reaches to "npta"(Number of PDUs To Acknowledge) then
* sends an RR response as acknowledgement for them. Returns 0 for
* success, 1 otherwise.
*/
int llc_conn_ac_send_ack_if_needed(struct sock *sk,
struct llc_conn_state_ev *ev)
{
u8 pf_bit;
struct sk_buff *skb = ev->data.pdu.skb;
struct llc_opt *llc = llc_sk(sk);
llc_pdu_decode_pf_bit(skb, &pf_bit);
llc->ack_pf |= pf_bit & 1;
if (!llc->ack_must_be_send) {
llc->first_pdu_Ns = llc->vR;
llc->ack_must_be_send = 1;
llc->ack_pf = pf_bit & 1;
}
if (((llc->vR - llc->first_pdu_Ns + 129) % 128) >= llc->npta) {
llc_conn_ac_send_rr_rsp_f_set_ackpf(sk, ev);
llc->ack_must_be_send = 0;
llc->ack_pf = 0;
llc_conn_ac_inc_npta_value(sk, ev);
}
return 0;
}
/**
* llc_conn_ac_rst_sendack_flag - resets ack_must_be_send flag
* @sk: current connection structure
* @ev: current event
*
* This action resets ack_must_be_send flag of given connection, this flag
* indicates if there is any PDU which has not been acknowledged yet.
* Returns 0 for success, 1 otherwise.
*/
int llc_conn_ac_rst_sendack_flag(struct sock *sk,
struct llc_conn_state_ev *ev)
{
llc_sk(sk)->ack_must_be_send = llc_sk(sk)->ack_pf = 0;
return 0;
}
/**
* llc_conn_ac_send_i_rsp_f_set_ackpf - acknowledge received PDUs
* @sk: current connection structure
* @ev: current event
*
* Sends an I response PDU with f-bit set to ack_pf flag as acknowledge to
* all received PDUs which have not been acknowledged, yet. ack_pf flag is
* set to one if one PDU with p-bit set to one is received. Returns 0 for
* success, 1 otherwise.
*/
int llc_conn_ac_send_i_rsp_f_set_ackpf(struct sock *sk,
struct llc_conn_state_ev *ev)
{
struct sk_buff *skb = ev->data.prim.data->data->data.skb;
struct llc_opt *llc = llc_sk(sk);
u8 p_bit = llc->ack_pf;
struct llc_sap *sap = llc->sap;
llc_pdu_header_init(skb, LLC_PDU_TYPE_I, sap->laddr.lsap,
llc->daddr.lsap, LLC_PDU_RSP);
llc_pdu_init_as_i_cmd(skb, p_bit, llc->vS, llc->vR);
lan_hdrs_init(skb, llc->dev->dev_addr, llc->daddr.mac);
llc_conn_send_pdu(sk, skb);
llc_conn_ac_inc_vs_by_1(sk, ev);
return 0;
}
/**
* llc_conn_ac_send_i_as_ack - sends an I-format PDU to acknowledge rx PDUs
* @sk: current connection structure.
* @ev: current event.
*
* This action sends an I-format PDU as acknowledge to received PDUs which
* have not been acknowledged, yet, if there is any. By using of this
* action number of acknowledgements decreases, this technic is called
* piggy backing. Returns 0 for success, 1 otherwise.
*/
int llc_conn_ac_send_i_as_ack(struct sock *sk, struct llc_conn_state_ev *ev)
{
struct llc_opt *llc = llc_sk(sk);
if (llc->ack_must_be_send) {
llc_conn_ac_send_i_rsp_f_set_ackpf(sk, ev);
llc->ack_must_be_send = 0 ;
llc->ack_pf = 0;
} else
llc_conn_ac_send_i_cmd_p_set_0(sk, ev);
return 0;
}
/**
* llc_conn_ac_send_rr_rsp_f_set_ackpf - ack all rx PDUs not yet acked
* @sk: current connection structure.
* @ev: current event.
*
* This action sends an RR response with f-bit set to ack_pf flag as
* acknowledge to all received PDUs which have not been acknowledged, yet,
* if there is any. ack_pf flag indicates if a PDU has been received with
* p-bit set to one. Returns 0 for success, 1 otherwise.
*/
int llc_conn_ac_send_rr_rsp_f_set_ackpf(struct sock *sk,
struct llc_conn_state_ev *ev)
{
int rc = 1;
struct sk_buff *skb = llc_alloc_frame();
if (skb) {
struct llc_opt *llc = llc_sk(sk);
struct llc_sap *sap = llc->sap;
u8 f_bit = llc->ack_pf;
skb->dev = llc->dev;
llc_pdu_header_init(skb, LLC_PDU_TYPE_S, sap->laddr.lsap,
llc->daddr.lsap, LLC_PDU_RSP);
llc_pdu_init_as_rr_rsp(skb, f_bit, llc->vR);
lan_hdrs_init(skb, llc->dev->dev_addr, llc->daddr.mac);
rc = 0;
llc_conn_send_pdu(sk, skb);
}
return rc;
}
/**
* llc_conn_ac_inc_npta_value - tries to make value of npta greater
* @sk: current connection structure.
* @ev: current event.
*
* After "inc_cntr" times calling of this action, "npta" increase by one.
* this action tries to make vale of "npta" greater as possible; number of
* acknowledgements decreases by increasing of "npta". Returns 0 for
* success, 1 otherwise.
*/
int llc_conn_ac_inc_npta_value(struct sock *sk, struct llc_conn_state_ev *ev)
{
struct llc_opt *llc = llc_sk(sk);
if (!llc->inc_cntr) {
llc->dec_step = 0;
llc->dec_cntr = llc->inc_cntr = 2;
++llc->npta;
if (llc->npta > 127)
llc->npta = 127 ;
} else
--llc->inc_cntr;
return 0;
}
/**
* llc_conn_ac_adjust_npta_by_rr - decreases "npta" by one
* @sk: current connection structure.
* @ev: current event.
*
* After receiving "dec_cntr" times RR command, this action decreases
* "npta" by one. Returns 0 for success, 1 otherwise.
*/
int llc_conn_ac_adjust_npta_by_rr(struct sock *sk, struct llc_conn_state_ev *ev)
{
struct llc_opt *llc = llc_sk(sk);
if (!llc->connect_step && !llc->remote_busy_flag) {
if (!llc->dec_step) {
if (!llc->dec_cntr) {
llc->inc_cntr = llc->dec_cntr = 2;
if (llc->npta > 0)
llc->npta = llc->npta - 1;
} else
llc->dec_cntr -=1;
}
} else
llc->connect_step = 0 ;
return 0;
}
/**
* llc_conn_ac_adjust_npta_by_rnr - decreases "npta" by one
* @sk: current connection structure.
* @ev: current event.
*
* After receiving "dec_cntr" times RNR command, this action decreases
* "npta" by one. Returns 0 for success, 1 otherwise.
*/
int llc_conn_ac_adjust_npta_by_rnr(struct sock *sk,
struct llc_conn_state_ev *ev)
{
struct llc_opt *llc = llc_sk(sk);
if (llc->remote_busy_flag)
if (!llc->dec_step) {
if (!llc->dec_cntr) {
llc->inc_cntr = llc->dec_cntr = 2;
if (llc->npta > 0)
--llc->npta;
} else
--llc->dec_cntr;
}
return 0;
}
/**
* llc_conn_ac_dec_tx_win_size - decreases tx window size
* @sk: current connection structure.
* @ev: current event.
*
* After receiving of a REJ command or response, transmit window size is
* decreased by number of PDUs which are outstanding yet. Returns 0 for
* success, 1 otherwise.
*/
int llc_conn_ac_dec_tx_win_size(struct sock *sk, struct llc_conn_state_ev *ev)
{
struct llc_opt *llc = llc_sk(sk);
u8 unacked_pdu = skb_queue_len(&llc->pdu_unack_q);
llc->k -= unacked_pdu;
if (llc->k < 2)
llc->k = 2;
return 0;
}
/**
* llc_conn_ac_inc_tx_win_size - tx window size is inc by 1
* @sk: current connection structure.
* @ev: current event.
*
* After receiving an RR response with f-bit set to one, transmit window
* size is increased by one. Returns 0 for success, 1 otherwise.
*/
int llc_conn_ac_inc_tx_win_size(struct sock *sk, struct llc_conn_state_ev *ev)
{
struct llc_opt *llc = llc_sk(sk);
llc->k += 1;
if (llc->k > 128)
llc->k = 128 ;
return 0;
}
int llc_conn_ac_stop_all_timers(struct sock *sk, struct llc_conn_state_ev *ev)
{
struct llc_opt *llc = llc_sk(sk);
del_timer(&llc->pf_cycle_timer.timer);
llc->pf_cycle_timer.running = 0;
del_timer(&llc->ack_timer.timer);
llc->ack_timer.running = 0;
del_timer(&llc->rej_sent_timer.timer);
llc->rej_sent_timer.running = 0;
del_timer(&llc->busy_state_timer.timer);
llc->busy_state_timer.running = 0;
llc->ack_must_be_send = 0;
llc->ack_pf = 0;
return 0;
}
int llc_conn_ac_stop_other_timers(struct sock *sk, struct llc_conn_state_ev *ev)
{
struct llc_opt *llc = llc_sk(sk);
del_timer(&llc->rej_sent_timer.timer);
llc->rej_sent_timer.running = 0;
del_timer(&llc->pf_cycle_timer.timer);
llc->pf_cycle_timer.running = 0;
del_timer(&llc->busy_state_timer.timer);
llc->busy_state_timer.running = 0;
llc->ack_must_be_send = 0;
llc->ack_pf = 0;
return 0;
}
int llc_conn_ac_start_ack_timer(struct sock *sk, struct llc_conn_state_ev *ev)
{
struct llc_opt *llc = llc_sk(sk);
del_timer(&llc->ack_timer.timer);
llc->ack_timer.timer.expires = jiffies + llc->ack_timer.expire * HZ;
llc->ack_timer.timer.data = (unsigned long)sk;
llc->ack_timer.timer.function = llc_conn_ack_tmr_cb;
add_timer(&llc->ack_timer.timer);
llc->ack_timer.running = 1;
return 0;
}
int llc_conn_ac_start_rej_timer(struct sock *sk, struct llc_conn_state_ev *ev)
{
struct llc_opt *llc = llc_sk(sk);
del_timer(&llc->rej_sent_timer.timer);
llc->rej_sent_timer.timer.expires = jiffies +
llc->rej_sent_timer.expire * HZ;
llc->rej_sent_timer.timer.data = (unsigned long)sk;
llc->rej_sent_timer.timer.function = llc_conn_rej_tmr_cb;
add_timer(&llc->rej_sent_timer.timer);
llc->rej_sent_timer.running = 1;
return 0;
}
int llc_conn_ac_start_ack_tmr_if_not_running(struct sock *sk,
struct llc_conn_state_ev *ev)
{
struct llc_opt *llc = llc_sk(sk);
if (!llc->ack_timer.running) {
llc->ack_timer.timer.expires = jiffies +
llc->ack_timer.expire * HZ;
llc->ack_timer.timer.data = (unsigned long)sk;
llc->ack_timer.timer.function = llc_conn_ack_tmr_cb;
add_timer(&llc->ack_timer.timer);
llc->ack_timer.running = 1;
}
return 0;
}
int llc_conn_ac_stop_ack_timer(struct sock *sk, struct llc_conn_state_ev *ev)
{
del_timer(&llc_sk(sk)->ack_timer.timer);
llc_sk(sk)->ack_timer.running = 0;
return 0;
}
int llc_conn_ac_stop_p_timer(struct sock *sk, struct llc_conn_state_ev *ev)
{
struct llc_opt *llc = llc_sk(sk);
del_timer(&llc->pf_cycle_timer.timer);
llc->pf_cycle_timer.running = 0;
llc->p_flag = 0;
return 0;
}
int llc_conn_ac_stop_rej_timer(struct sock *sk, struct llc_conn_state_ev *ev)
{
del_timer(&llc_sk(sk)->rej_sent_timer.timer);
llc_sk(sk)->rej_sent_timer.running = 0;
return 0;
}
int llc_conn_ac_upd_nr_received(struct sock *sk, struct llc_conn_state_ev *ev)
{
int acked;
u16 unacked = 0;
u8 fbit;
struct sk_buff *skb = ev->data.pdu.skb;
llc_pdu_sn_t *rx_pdu = (llc_pdu_sn_t *)skb->nh.raw;
struct llc_opt *llc = llc_sk(sk);
llc->last_nr = PDU_SUPV_GET_Nr(rx_pdu);
acked = llc_conn_remove_acked_pdus(sk, llc->last_nr, &unacked);
/* On loopback we don't queue I frames in unack_pdu_q queue. */
if (acked > 0 || (llc->dev->flags & IFF_LOOPBACK)) {
llc->retry_count = 0;
del_timer(&llc->ack_timer.timer);
llc->ack_timer.running = 0;
if (llc->failed_data_req) {
/* already, we did not accept data from upper layer
* (tx_window full or unacceptable state). Now, we
* can send data and must inform to upper layer.
*/
llc->failed_data_req = 0;
llc_conn_ac_data_confirm(sk, ev);
}
if (unacked) {
llc->ack_timer.timer.expires = jiffies +
llc->ack_timer.expire * HZ;
llc->ack_timer.timer.data = (unsigned long)sk;
llc->ack_timer.timer.function = llc_conn_ack_tmr_cb;
add_timer(&llc->ack_timer.timer);
llc->ack_timer.running = 1;
}
} else if (llc->failed_data_req) {
llc_pdu_decode_pf_bit(skb, &fbit);
if (fbit == 1) {
llc->failed_data_req = 0;
llc_conn_ac_data_confirm(sk, ev);
}
}
return 0;
}
int llc_conn_ac_upd_p_flag(struct sock *sk, struct llc_conn_state_ev *ev)
{
struct sk_buff *skb = ev->data.pdu.skb;
llc_pdu_sn_t *rx_pdu = (llc_pdu_sn_t *)skb->nh.raw;
u8 f_bit;
if (!LLC_PDU_IS_RSP(rx_pdu) &&
!llc_pdu_decode_pf_bit(skb, &f_bit) && f_bit) {
llc_sk(sk)->p_flag = 0;
llc_conn_ac_stop_p_timer(sk, ev);
}
return 0;
}
int llc_conn_ac_set_data_flag_2(struct sock *sk, struct llc_conn_state_ev *ev)
{
llc_sk(sk)->data_flag = 2;
return 0;
}
int llc_conn_ac_set_data_flag_0(struct sock *sk, struct llc_conn_state_ev *ev)
{
llc_sk(sk)->data_flag = 0;
return 0;
}
int llc_conn_ac_set_data_flag_1(struct sock *sk, struct llc_conn_state_ev *ev)
{
llc_sk(sk)->data_flag = 1;
return 0;
}
int llc_conn_ac_set_data_flag_1_if_data_flag_eq_0(struct sock *sk,
struct llc_conn_state_ev *ev)
{
if (!llc_sk(sk)->data_flag)
llc_sk(sk)->data_flag = 1;
return 0;
}
int llc_conn_ac_set_p_flag_0(struct sock *sk, struct llc_conn_state_ev *ev)
{
llc_sk(sk)->p_flag = 0;
return 0;
}
int llc_conn_ac_set_p_flag_1(struct sock *sk, struct llc_conn_state_ev *ev)
{
llc_sk(sk)->p_flag = 1;
return 0;
}
int llc_conn_ac_set_remote_busy_0(struct sock *sk, struct llc_conn_state_ev *ev)
{
llc_sk(sk)->remote_busy_flag = 0;
return 0;
}
int llc_conn_ac_set_cause_flag_0(struct sock *sk, struct llc_conn_state_ev *ev)
{
llc_sk(sk)->cause_flag = 0;
return 0;
}
int llc_conn_ac_set_cause_flag_1(struct sock *sk, struct llc_conn_state_ev *ev)
{
llc_sk(sk)->cause_flag = 1;
return 0;
}
int llc_conn_ac_set_retry_cnt_0(struct sock *sk, struct llc_conn_state_ev *ev)
{
llc_sk(sk)->retry_count = 0;
return 0;
}
int llc_conn_ac_inc_retry_cnt_by_1(struct sock *sk,
struct llc_conn_state_ev *ev)
{
llc_sk(sk)->retry_count++;
return 0;
}
int llc_conn_ac_set_vr_0(struct sock *sk, struct llc_conn_state_ev *ev)
{
llc_sk(sk)->vR = 0;
return 0;
}
int llc_conn_ac_inc_vr_by_1(struct sock *sk, struct llc_conn_state_ev *ev)
{
llc_sk(sk)->vR = PDU_GET_NEXT_Vr(llc_sk(sk)->vR);
return 0;
}
int llc_conn_ac_set_vs_0(struct sock *sk, struct llc_conn_state_ev *ev)
{
llc_sk(sk)->vS = 0;
return 0;
}
int llc_conn_ac_set_vs_nr(struct sock *sk, struct llc_conn_state_ev *ev)
{
llc_sk(sk)->vS = llc_sk(sk)->last_nr;
return 0;
}
int llc_conn_ac_inc_vs_by_1(struct sock *sk, struct llc_conn_state_ev *ev)
{
llc_sk(sk)->vS = (llc_sk(sk)->vS + 1) % 128;
return 0;
}
int llc_conn_ac_set_f_flag_p(struct sock *sk, struct llc_conn_state_ev *ev)
{
llc_pdu_decode_pf_bit(ev->data.pdu.skb, &llc_sk(sk)->f_flag);
return 0;
}
void llc_conn_pf_cycle_tmr_cb(unsigned long timeout_data)
{
struct sock *sk = (struct sock *)timeout_data;
struct llc_conn_state_ev *ev;
llc_sk(sk)->pf_cycle_timer.running = 0;
ev = llc_conn_alloc_ev(sk);
if (ev) {
ev->type = LLC_CONN_EV_TYPE_P_TMR;
ev->data.tmr.timer_specific = NULL;
llc_process_tmr_ev(sk, ev);
}
}
static void llc_conn_busy_tmr_cb(unsigned long timeout_data)
{
struct sock *sk = (struct sock *)timeout_data;
struct llc_conn_state_ev *ev;
llc_sk(sk)->busy_state_timer.running = 0;
ev = llc_conn_alloc_ev(sk);
if (ev) {
ev->type = LLC_CONN_EV_TYPE_BUSY_TMR;
ev->data.tmr.timer_specific = NULL;
llc_process_tmr_ev(sk, ev);
}
}
void llc_conn_ack_tmr_cb(unsigned long timeout_data)
{
struct sock* sk = (struct sock *)timeout_data;
struct llc_conn_state_ev *ev;
llc_sk(sk)->ack_timer.running = 0;
ev = llc_conn_alloc_ev(sk);
if (ev) {
ev->type = LLC_CONN_EV_TYPE_ACK_TMR;
ev->data.tmr.timer_specific = NULL;
llc_process_tmr_ev(sk, ev);
}
}
static void llc_conn_rej_tmr_cb(unsigned long timeout_data)
{
struct sock *sk = (struct sock *)timeout_data;
struct llc_conn_state_ev *ev;
llc_sk(sk)->rej_sent_timer.running = 0;
ev = llc_conn_alloc_ev(sk);
if (ev) {
ev->type = LLC_CONN_EV_TYPE_REJ_TMR;
ev->data.tmr.timer_specific = NULL;
llc_process_tmr_ev(sk, ev);
}
}
int llc_conn_ac_rst_vs(struct sock *sk, struct llc_conn_state_ev *ev)
{
llc_sk(sk)->X = llc_sk(sk)->vS;
llc_conn_ac_set_vs_nr(sk, ev);
return 0;
}
int llc_conn_ac_upd_vs(struct sock *sk, struct llc_conn_state_ev *ev)
{
llc_pdu_sn_t *rx_pdu = (llc_pdu_sn_t *)ev->data.pdu.skb->nh.raw;
u8 nr = PDU_SUPV_GET_Nr(rx_pdu);
if (llc_circular_between(llc_sk(sk)->vS, nr, llc_sk(sk)->X))
llc_conn_ac_set_vs_nr(sk, ev);
return 0;
}
/*
* Non-standard actions; these not contained in IEEE specification; for
* our own usage
*/
/**
* llc_conn_disc - removes connection from SAP list and frees it
* @sk: closed connection
* @ev: occurred event
*
* Returns 2, to indicate the state machine that the connection was freed.
*/
int llc_conn_disc(struct sock *sk, struct llc_conn_state_ev *ev)
{
llc_sap_unassign_sock(llc_sk(sk)->sap, sk);
llc_sock_free(sk);
return 2;
}
/**
* llc_conn_reset - resets connection
* @sk : reseting connection.
* @ev: occurred event.
*
* Stop all timers, empty all queues and reset all flags.
*/
int llc_conn_reset(struct sock *sk, struct llc_conn_state_ev *ev)
{
llc_sock_reset(sk);
return 0;
}
/**
* llc_circular_between - designates that b is between a and c or not
* @a: lower bound
* @b: element to see if is between a and b
* @c: upper bound
*
* This function designates that b is between a and c or not (for example,
* 0 is between 127 and 1). Returns 1 if b is between a and c, 0
* otherwise.
*/
u8 llc_circular_between(u8 a, u8 b, u8 c)
{
b = b - a;
c = c - a;
return b <= c;
}
/**
* llc_process_tmr_ev - timer backend
* @sk: active connection
* @ev: occurred event
*
* This function is called from timer callback functions. When connection
* is busy (during sending a data frame) timer expiration event must be
* queued. Otherwise this event can be sent to connection state machine.
* Queued events will process by process_rxframes_events function after
* sending data frame. Returns 0 for success, 1 otherwise.
*/
static void llc_process_tmr_ev(struct sock *sk, struct llc_conn_state_ev *ev)
{
bh_lock_sock(sk);
if (llc_sk(sk)->state == LLC_CONN_OUT_OF_SVC) {
printk(KERN_WARNING "timer called on closed connection\n");
llc_conn_free_ev(ev);
goto out;
}
if (!sk->lock.users)
llc_conn_send_ev(sk, ev);
else {
struct sk_buff *skb = alloc_skb(1, GFP_ATOMIC);
if (skb) {
skb->cb[0] = LLC_EVENT;
skb->data = (void *)ev;
sk_add_backlog(sk, skb);
} else
llc_conn_free_ev(ev);
}
out:
bh_unlock_sock(sk);
}
/*
* llc_c_ev.c - Connection component state transition event qualifiers
*
* A 'state' consists of a number of possible event matching functions,
* the actions associated with each being executed when that event is
* matched; a 'state machine' accepts events in a serial fashion from an
* event queue. Each event is passed to each successive event matching
* function until a match is made (the event matching function returns
* success, or '0') or the list of event matching functions is exhausted.
* If a match is made, the actions associated with the event are executed
* and the state is changed to that event's transition state. Before some
* events are recognized, even after a match has been made, a certain
* number of 'event qualifier' functions must also be executed. If these
* all execute successfully, then the event is finally executed.
*
* These event functions must return 0 for success, to show a matched
* event, of 1 if the event does not match. Event qualifier functions
* must return a 0 for success or a non-zero for failure. Each function
* is simply responsible for verifying one single thing and returning
* either a success or failure.
*
* All of followed event functions are described in 802.2 LLC Protocol
* standard document except two functions that we added that will explain
* in their comments, at below.
*
* Copyright (c) 1997 by Procom Technology, Inc.
* 2001 by Arnaldo Carvalho de Melo <acme@conectiva.com.br>
*
* This program can be redistributed or modified under the terms of the
* GNU General Public License as published by the Free Software Foundation.
* This program is distributed without any warranty or implied warranty
* of merchantability or fitness for a particular purpose.
*
* See the GNU General Public License for more details.
*/
#include <linux/netdevice.h>
#include <net/llc_conn.h>
#include <net/llc_sap.h>
#include <net/sock.h>
#include <net/llc_c_ev.h>
#include <net/llc_pdu.h>
#if 0
#define dprintk(args...) printk(KERN_DEBUG args)
#else
#define dprintk(args...)
#endif
extern u16 llc_circular_between(u8 a, u8 b, u8 c);
/**
* llc_util_ns_inside_rx_window - check if sequence number is in rx window
* @ns: sequence number of received pdu.
* @vr: sequence number which receiver expects to receive.
* @rw: receive window size of receiver.
*
* Checks if sequence number of received PDU is in range of receive
* window. Returns 0 for success, 1 otherwise
*/
static u16 llc_util_ns_inside_rx_window(u8 ns, u8 vr, u8 rw)
{
return !llc_circular_between(vr, ns,
(vr + rw - 1) % LLC_2_SEQ_NBR_MODULO);
}
/**
* llc_util_nr_inside_tx_window - check if sequence number is in tx window
* @sk: current connection.
* @nr: N(R) of received PDU.
*
* This routine checks if N(R) of received PDU is in range of transmit
* window; on the other hand checks if received PDU acknowledges some
* outstanding PDUs that are in transmit window. Returns 0 for success, 1
* otherwise.
*/
static u16 llc_util_nr_inside_tx_window(struct sock *sk, u8 nr)
{
u8 nr1, nr2;
struct sk_buff *skb;
llc_pdu_sn_t *pdu;
struct llc_opt *llc = llc_sk(sk);
int rc = 0;
if (llc->dev->flags & IFF_LOOPBACK)
goto out;
rc = 1;
if (!skb_queue_len(&llc->pdu_unack_q))
goto out;
skb = skb_peek(&llc->pdu_unack_q);
pdu = (llc_pdu_sn_t *)skb->nh.raw;
nr1 = LLC_I_GET_NS(pdu);
skb = skb_peek_tail(&llc->pdu_unack_q);
pdu = (llc_pdu_sn_t *)skb->nh.raw;
nr2 = LLC_I_GET_NS(pdu);
rc = !llc_circular_between(nr1, nr, (nr2 + 1) % LLC_2_SEQ_NBR_MODULO);
out:
return rc;
}
int llc_conn_ev_conn_req(struct sock *sk, struct llc_conn_state_ev *ev)
{
return ev->data.prim.prim == LLC_CONN_PRIM &&
ev->data.prim.type == LLC_PRIM_TYPE_REQ ? 0 : 1;
}
int llc_conn_ev_conn_resp(struct sock *sk, struct llc_conn_state_ev *ev)
{
return ev->data.prim.prim == LLC_CONN_PRIM &&
ev->data.prim.type == LLC_PRIM_TYPE_RESP ? 0 : 1;
}
int llc_conn_ev_data_req(struct sock *sk, struct llc_conn_state_ev *ev)
{
return ev->data.prim.prim == LLC_DATA_PRIM &&
ev->data.prim.type == LLC_PRIM_TYPE_REQ ? 0 : 1;
}
int llc_conn_ev_disc_req(struct sock *sk, struct llc_conn_state_ev *ev)
{
return ev->data.prim.prim == LLC_DISC_PRIM &&
ev->data.prim.type == LLC_PRIM_TYPE_REQ ? 0 : 1;
}
int llc_conn_ev_rst_req(struct sock *sk, struct llc_conn_state_ev *ev)
{
return ev->data.prim.prim == LLC_RESET_PRIM &&
ev->data.prim.type == LLC_PRIM_TYPE_REQ ? 0 : 1;
}
int llc_conn_ev_rst_resp(struct sock *sk, struct llc_conn_state_ev *ev)
{
return ev->data.prim.prim == LLC_RESET_PRIM &&
ev->data.prim.type == LLC_PRIM_TYPE_RESP ? 0 : 1;
}
int llc_conn_ev_local_busy_detected(struct sock *sk,
struct llc_conn_state_ev *ev)
{
return ev->type == LLC_CONN_EV_TYPE_SIMPLE &&
ev->data.a.ev == LLC_CONN_EV_LOCAL_BUSY_DETECTED ? 0 : 1;
}
int llc_conn_ev_local_busy_cleared(struct sock *sk,
struct llc_conn_state_ev *ev)
{
return ev->type == LLC_CONN_EV_TYPE_SIMPLE &&
ev->data.a.ev == LLC_CONN_EV_LOCAL_BUSY_CLEARED ? 0 : 1;
}
int llc_conn_ev_rx_bad_pdu(struct sock *sk, struct llc_conn_state_ev *ev)
{
return 1;
}
int llc_conn_ev_rx_disc_cmd_pbit_set_x(struct sock *sk,
struct llc_conn_state_ev *ev)
{
llc_pdu_un_t *pdu = (llc_pdu_un_t *)ev->data.pdu.skb->nh.raw;
return !LLC_PDU_IS_CMD(pdu) && !LLC_PDU_TYPE_IS_U(pdu) &&
LLC_U_PDU_CMD(pdu) == LLC_2_PDU_CMD_DISC ? 0 : 1;
}
int llc_conn_ev_rx_dm_rsp_fbit_set_x(struct sock *sk,
struct llc_conn_state_ev *ev)
{
llc_pdu_un_t *pdu = (llc_pdu_un_t *)ev->data.pdu.skb->nh.raw;
return !LLC_PDU_IS_RSP(pdu) && !LLC_PDU_TYPE_IS_U(pdu) &&
LLC_U_PDU_RSP(pdu) == LLC_2_PDU_RSP_DM ? 0 : 1;
}
int llc_conn_ev_rx_frmr_rsp_fbit_set_x(struct sock *sk,
struct llc_conn_state_ev *ev)
{
llc_pdu_un_t *pdu = (llc_pdu_un_t *)ev->data.pdu.skb->nh.raw;
return !LLC_PDU_IS_RSP(pdu) && !LLC_PDU_TYPE_IS_U(pdu) &&
LLC_U_PDU_RSP(pdu) == LLC_2_PDU_RSP_FRMR ? 0 : 1;
}
int llc_conn_ev_rx_i_cmd_pbit_set_0(struct sock *sk,
struct llc_conn_state_ev *ev)
{
llc_pdu_sn_t *pdu = (llc_pdu_sn_t *)ev->data.pdu.skb->nh.raw;
return !LLC_PDU_IS_CMD(pdu) && !LLC_PDU_TYPE_IS_I(pdu) &&
!LLC_I_PF_IS_0(pdu) &&
LLC_I_GET_NS(pdu) == llc_sk(sk)->vR ? 0 : 1;
}
int llc_conn_ev_rx_i_cmd_pbit_set_1(struct sock *sk,
struct llc_conn_state_ev *ev)
{
llc_pdu_sn_t *pdu = (llc_pdu_sn_t *)ev->data.pdu.skb->nh.raw;
return !LLC_PDU_IS_CMD(pdu) && !LLC_PDU_TYPE_IS_I(pdu) &&
!LLC_I_PF_IS_1(pdu) &&
LLC_I_GET_NS(pdu) == llc_sk(sk)->vR ? 0 : 1;
}
int llc_conn_ev_rx_i_cmd_pbit_set_0_unexpd_ns(struct sock *sk,
struct llc_conn_state_ev *ev)
{
llc_pdu_sn_t *pdu = (llc_pdu_sn_t *)ev->data.pdu.skb->nh.raw;
u8 vr = llc_sk(sk)->vR;
u8 ns = LLC_I_GET_NS(pdu);
return !LLC_PDU_IS_CMD(pdu) && !LLC_PDU_TYPE_IS_I(pdu) &&
!LLC_I_PF_IS_0(pdu) && ns != vr &&
!llc_util_ns_inside_rx_window(ns, vr, llc_sk(sk)->rw) ? 0 : 1;
}
int llc_conn_ev_rx_i_cmd_pbit_set_1_unexpd_ns(struct sock *sk,
struct llc_conn_state_ev *ev)
{
llc_pdu_sn_t *pdu = (llc_pdu_sn_t *)ev->data.pdu.skb->nh.raw;
u8 vr = llc_sk(sk)->vR;
u8 ns = LLC_I_GET_NS(pdu);
return !LLC_PDU_IS_CMD(pdu) && !LLC_PDU_TYPE_IS_I(pdu) &&
!LLC_I_PF_IS_1(pdu) && ns != vr &&
!llc_util_ns_inside_rx_window(ns, vr, llc_sk(sk)->rw) ? 0 : 1;
}
int llc_conn_ev_rx_i_cmd_pbit_set_x_inval_ns(struct sock *sk,
struct llc_conn_state_ev *ev)
{
llc_pdu_sn_t * pdu = (llc_pdu_sn_t *)ev->data.pdu.skb->nh.raw;
u8 vr = llc_sk(sk)->vR;
u8 ns = LLC_I_GET_NS(pdu);
u16 rc = !LLC_PDU_IS_CMD(pdu) && !LLC_PDU_TYPE_IS_I(pdu) && ns != vr &&
llc_util_ns_inside_rx_window(ns, vr, llc_sk(sk)->rw) ? 0 : 1;
if (!rc)
dprintk(KERN_WARNING "rx_i_cmd_p_bit_set_x_inval_ns matched,"
"state = %d, ns = %d, vr = %d\n",
llc_sk(sk)->state, ns, vr);
return rc;
}
int llc_conn_ev_rx_i_rsp_fbit_set_0(struct sock *sk,
struct llc_conn_state_ev *ev)
{
llc_pdu_sn_t *pdu = (llc_pdu_sn_t *)ev->data.pdu.skb->nh.raw;
return !LLC_PDU_IS_RSP(pdu) && !LLC_PDU_TYPE_IS_I(pdu) &&
!LLC_I_PF_IS_0(pdu) &&
LLC_I_GET_NS(pdu) == llc_sk(sk)->vR ? 0 : 1;
}
int llc_conn_ev_rx_i_rsp_fbit_set_1(struct sock *sk,
struct llc_conn_state_ev *ev)
{
llc_pdu_sn_t *pdu = (llc_pdu_sn_t *)ev->data.pdu.skb->nh.raw;
return !LLC_PDU_IS_RSP(pdu) && !LLC_PDU_TYPE_IS_I(pdu) &&
!LLC_I_PF_IS_1(pdu) &&
LLC_I_GET_NS(pdu) == llc_sk(sk)->vR ? 0 : 1;
}
int llc_conn_ev_rx_i_rsp_fbit_set_x(struct sock *sk,
struct llc_conn_state_ev *ev)
{
llc_pdu_sn_t *pdu = (llc_pdu_sn_t *)ev->data.pdu.skb->nh.raw;
return !LLC_PDU_IS_RSP(pdu) && !LLC_PDU_TYPE_IS_I(pdu) &&
LLC_I_GET_NS(pdu) == llc_sk(sk)->vR ? 0 : 1;
}
int llc_conn_ev_rx_i_rsp_fbit_set_0_unexpd_ns(struct sock *sk,
struct llc_conn_state_ev *ev)
{
llc_pdu_sn_t *pdu = (llc_pdu_sn_t *)ev->data.pdu.skb->nh.raw;
u8 vr = llc_sk(sk)->vR;
u8 ns = LLC_I_GET_NS(pdu);
return !LLC_PDU_IS_RSP(pdu) && !LLC_PDU_TYPE_IS_I(pdu) &&
!LLC_I_PF_IS_0(pdu) && ns != vr &&
!llc_util_ns_inside_rx_window(ns, vr, llc_sk(sk)->rw) ? 0 : 1;
}
int llc_conn_ev_rx_i_rsp_fbit_set_1_unexpd_ns(struct sock *sk,
struct llc_conn_state_ev *ev)
{
llc_pdu_sn_t *pdu = (llc_pdu_sn_t *)ev->data.pdu.skb->nh.raw;
u8 vr = llc_sk(sk)->vR;
u8 ns = LLC_I_GET_NS(pdu);
return !LLC_PDU_IS_RSP(pdu) && !LLC_PDU_TYPE_IS_I(pdu) &&
!LLC_I_PF_IS_1(pdu) && ns != vr &&
!llc_util_ns_inside_rx_window(ns, vr, llc_sk(sk)->rw) ? 0 : 1;
}
int llc_conn_ev_rx_i_rsp_fbit_set_x_unexpd_ns(struct sock *sk,
struct llc_conn_state_ev *ev)
{
llc_pdu_sn_t *pdu = (llc_pdu_sn_t *)ev->data.pdu.skb->nh.raw;
u8 vr = llc_sk(sk)->vR;
u8 ns = LLC_I_GET_NS(pdu);
return !LLC_PDU_IS_RSP(pdu) && !LLC_PDU_TYPE_IS_I(pdu) && ns != vr &&
!llc_util_ns_inside_rx_window(ns, vr, llc_sk(sk)->rw) ? 0 : 1;
}
int llc_conn_ev_rx_i_rsp_fbit_set_x_inval_ns(struct sock *sk,
struct llc_conn_state_ev *ev)
{
llc_pdu_sn_t *pdu = (llc_pdu_sn_t *)ev->data.pdu.skb->nh.raw;
u8 vr = llc_sk(sk)->vR;
u8 ns = LLC_I_GET_NS(pdu);
u16 rc = !LLC_PDU_IS_RSP(pdu) && !LLC_PDU_TYPE_IS_I(pdu) && ns != vr &&
llc_util_ns_inside_rx_window(ns, vr, llc_sk(sk)->rw) ? 0 : 1;
if (!rc)
dprintk(KERN_WARNING "conn_ev_rx_i_rsp_fbit_set_x_inval_ns "
"matched : state = %d, ns = %d, vr = %d\n",
llc_sk(sk)->state, ns, vr);
return rc;
}
int llc_conn_ev_rx_rej_cmd_pbit_set_0(struct sock *sk,
struct llc_conn_state_ev *ev)
{
llc_pdu_sn_t *pdu = (llc_pdu_sn_t *)ev->data.pdu.skb->nh.raw;
return !LLC_PDU_IS_CMD(pdu) && !LLC_PDU_TYPE_IS_S(pdu) &&
!LLC_S_PF_IS_0(pdu) &&
LLC_S_PDU_CMD(pdu) == LLC_2_PDU_CMD_REJ ? 0 : 1;
}
int llc_conn_ev_rx_rej_cmd_pbit_set_1(struct sock *sk,
struct llc_conn_state_ev *ev)
{
llc_pdu_sn_t *pdu = (llc_pdu_sn_t *)ev->data.pdu.skb->nh.raw;
return !LLC_PDU_IS_CMD(pdu) && !LLC_PDU_TYPE_IS_S(pdu) &&
!LLC_S_PF_IS_1(pdu) &&
LLC_S_PDU_CMD(pdu) == LLC_2_PDU_CMD_REJ ? 0 : 1;
}
int llc_conn_ev_rx_rej_rsp_fbit_set_0(struct sock *sk,
struct llc_conn_state_ev *ev)
{
llc_pdu_sn_t *pdu = (llc_pdu_sn_t *)ev->data.pdu.skb->nh.raw;
return !LLC_PDU_IS_RSP(pdu) && !LLC_PDU_TYPE_IS_S(pdu) &&
!LLC_S_PF_IS_0(pdu) &&
LLC_S_PDU_RSP(pdu) == LLC_2_PDU_RSP_REJ ? 0 : 1;
}
int llc_conn_ev_rx_rej_rsp_fbit_set_1(struct sock *sk,
struct llc_conn_state_ev *ev)
{
llc_pdu_sn_t *pdu = (llc_pdu_sn_t *)ev->data.pdu.skb->nh.raw;
return !LLC_PDU_IS_RSP(pdu) && !LLC_PDU_TYPE_IS_S(pdu) &&
!LLC_S_PF_IS_1(pdu) &&
LLC_S_PDU_RSP(pdu) == LLC_2_PDU_RSP_REJ ? 0 : 1;
}
int llc_conn_ev_rx_rej_rsp_fbit_set_x(struct sock *sk,
struct llc_conn_state_ev *ev)
{
llc_pdu_un_t *pdu = (llc_pdu_un_t *)ev->data.pdu.skb->nh.raw;
return !LLC_PDU_IS_RSP(pdu) && !LLC_PDU_TYPE_IS_S(pdu) &&
LLC_S_PDU_RSP(pdu) == LLC_2_PDU_RSP_REJ ? 0 : 1;
}
int llc_conn_ev_rx_rnr_cmd_pbit_set_0(struct sock *sk,
struct llc_conn_state_ev *ev)
{
llc_pdu_sn_t *pdu = (llc_pdu_sn_t *)ev->data.pdu.skb->nh.raw;
return !LLC_PDU_IS_CMD(pdu) && !LLC_PDU_TYPE_IS_S(pdu) &&
!LLC_S_PF_IS_0(pdu) &&
LLC_S_PDU_CMD(pdu) == LLC_2_PDU_CMD_RNR ? 0 : 1;
}
int llc_conn_ev_rx_rnr_cmd_pbit_set_1(struct sock *sk,
struct llc_conn_state_ev *ev)
{
llc_pdu_sn_t *pdu = (llc_pdu_sn_t *)ev->data.pdu.skb->nh.raw;
return !LLC_PDU_IS_CMD(pdu) && !LLC_PDU_TYPE_IS_S(pdu) &&
!LLC_S_PF_IS_1(pdu) &&
LLC_S_PDU_CMD(pdu) == LLC_2_PDU_CMD_RNR ? 0 : 1;
}
int llc_conn_ev_rx_rnr_rsp_fbit_set_0(struct sock *sk,
struct llc_conn_state_ev *ev)
{
llc_pdu_sn_t *pdu = (llc_pdu_sn_t *)ev->data.pdu.skb->nh.raw;
return !LLC_PDU_IS_RSP(pdu) && !LLC_PDU_TYPE_IS_S(pdu) &&
!LLC_S_PF_IS_0(pdu) &&
LLC_S_PDU_RSP(pdu) == LLC_2_PDU_RSP_RNR ? 0 : 1;
}
int llc_conn_ev_rx_rnr_rsp_fbit_set_1(struct sock *sk,
struct llc_conn_state_ev *ev)
{
llc_pdu_sn_t *pdu = (llc_pdu_sn_t *)ev->data.pdu.skb->nh.raw;
return !LLC_PDU_IS_RSP(pdu) && !LLC_PDU_TYPE_IS_S(pdu) &&
!LLC_S_PF_IS_1(pdu) &&
LLC_S_PDU_RSP(pdu) == LLC_2_PDU_RSP_RNR ? 0 : 1;
}
int llc_conn_ev_rx_rr_cmd_pbit_set_0(struct sock *sk,
struct llc_conn_state_ev *ev)
{
llc_pdu_sn_t *pdu = (llc_pdu_sn_t *)ev->data.pdu.skb->nh.raw;
return !LLC_PDU_IS_CMD(pdu) && !LLC_PDU_TYPE_IS_S(pdu) &&
!LLC_S_PF_IS_0(pdu) &&
LLC_S_PDU_CMD(pdu) == LLC_2_PDU_CMD_RR ? 0 : 1;
}
int llc_conn_ev_rx_rr_cmd_pbit_set_1(struct sock *sk,
struct llc_conn_state_ev *ev)
{
llc_pdu_sn_t *pdu = (llc_pdu_sn_t *)ev->data.pdu.skb->nh.raw;
return !LLC_PDU_IS_CMD(pdu) && !LLC_PDU_TYPE_IS_S(pdu) &&
!LLC_S_PF_IS_1(pdu) &&
LLC_S_PDU_CMD(pdu) == LLC_2_PDU_CMD_RR ? 0 : 1;
}
int llc_conn_ev_rx_rr_rsp_fbit_set_0(struct sock *sk,
struct llc_conn_state_ev *ev)
{
llc_pdu_sn_t *pdu = (llc_pdu_sn_t *)ev->data.pdu.skb->nh.raw;
return !LLC_PDU_IS_RSP(pdu) && !LLC_PDU_TYPE_IS_S(pdu) &&
!LLC_S_PF_IS_0(pdu) &&
LLC_S_PDU_RSP(pdu) == LLC_2_PDU_RSP_RR ? 0 : 1;
}
int llc_conn_ev_rx_rr_rsp_fbit_set_1(struct sock *sk,
struct llc_conn_state_ev *ev)
{
llc_pdu_sn_t *pdu = (llc_pdu_sn_t *)ev->data.pdu.skb->nh.raw;
return !LLC_PDU_IS_RSP(pdu) && !LLC_PDU_TYPE_IS_S(pdu) &&
!LLC_S_PF_IS_1(pdu) &&
LLC_S_PDU_RSP(pdu) == LLC_2_PDU_RSP_RR ? 0 : 1;
}
int llc_conn_ev_rx_sabme_cmd_pbit_set_x(struct sock *sk,
struct llc_conn_state_ev *ev)
{
llc_pdu_un_t *pdu = (llc_pdu_un_t *)ev->data.pdu.skb->nh.raw;
return !LLC_PDU_IS_CMD(pdu) && !LLC_PDU_TYPE_IS_U(pdu) &&
LLC_U_PDU_CMD(pdu) == LLC_2_PDU_CMD_SABME ? 0 : 1;
}
int llc_conn_ev_rx_ua_rsp_fbit_set_x(struct sock *sk,
struct llc_conn_state_ev *ev)
{
llc_pdu_un_t *pdu = (llc_pdu_un_t *)ev->data.pdu.skb->nh.raw;
return !LLC_PDU_IS_RSP(pdu) && !LLC_PDU_TYPE_IS_U(pdu) &&
LLC_U_PDU_RSP(pdu) == LLC_2_PDU_RSP_UA ? 0 : 1;
}
int llc_conn_ev_rx_xxx_cmd_pbit_set_1(struct sock *sk,
struct llc_conn_state_ev *ev)
{
u16 rc = 1;
llc_pdu_sn_t *pdu = (llc_pdu_sn_t *)ev->data.pdu.skb->nh.raw;
if (!LLC_PDU_IS_CMD(pdu)) {
if (!LLC_PDU_TYPE_IS_I(pdu) || !LLC_PDU_TYPE_IS_S(pdu)) {
if (!LLC_I_PF_IS_1(pdu))
rc = 0;
} else if (!LLC_PDU_TYPE_IS_U(pdu) && !LLC_U_PF_IS_1(pdu))
rc = 0;
}
return rc;
}
int llc_conn_ev_rx_xxx_cmd_pbit_set_0(struct sock *sk,
struct llc_conn_state_ev *ev)
{
u16 rc = 1;
llc_pdu_sn_t *pdu = (llc_pdu_sn_t *)ev->data.pdu.skb->nh.raw;
if (!LLC_PDU_IS_CMD(pdu)) {
if (!LLC_PDU_TYPE_IS_I(pdu) || !LLC_PDU_TYPE_IS_S(pdu)) {
if (!LLC_I_PF_IS_0(pdu))
rc = 0;
} else if (!LLC_PDU_TYPE_IS_U(pdu))
switch (LLC_U_PDU_CMD(pdu)) {
case LLC_2_PDU_CMD_SABME:
case LLC_2_PDU_CMD_DISC:
if (!LLC_U_PF_IS_0(pdu))
rc = 0;
break;
}
}
return rc;
}
int llc_conn_ev_rx_xxx_cmd_pbit_set_x(struct sock *sk,
struct llc_conn_state_ev *ev)
{
u16 rc = 1;
llc_pdu_un_t *pdu = (llc_pdu_un_t *)ev->data.pdu.skb->nh.raw;
if (!LLC_PDU_IS_CMD(pdu)) {
if (!LLC_PDU_TYPE_IS_I(pdu) || !LLC_PDU_TYPE_IS_S(pdu))
rc = 0;
else if (!LLC_PDU_TYPE_IS_U(pdu))
switch (LLC_U_PDU_CMD(pdu)) {
case LLC_2_PDU_CMD_SABME:
case LLC_2_PDU_CMD_DISC:
rc = 0;
break;
}
}
return rc;
}
int llc_conn_ev_rx_xxx_rsp_fbit_set_1(struct sock *sk,
struct llc_conn_state_ev *ev)
{
u16 rc = 1;
llc_pdu_sn_t *pdu = (llc_pdu_sn_t *)ev->data.pdu.skb->nh.raw;
if (!LLC_PDU_IS_RSP(pdu)) {
if (!LLC_PDU_TYPE_IS_I(pdu) || !LLC_PDU_TYPE_IS_S(pdu)) {
if (!LLC_I_PF_IS_1(pdu))
rc = 0;
} else if (!LLC_PDU_TYPE_IS_U(pdu))
switch (LLC_U_PDU_RSP(pdu)) {
case LLC_2_PDU_RSP_UA:
case LLC_2_PDU_RSP_DM:
case LLC_2_PDU_RSP_FRMR:
if (!LLC_U_PF_IS_1(pdu))
rc = 0;
break;
}
}
return rc;
}
int llc_conn_ev_rx_xxx_rsp_fbit_set_x(struct sock *sk,
struct llc_conn_state_ev *ev)
{
u16 rc = 1;
llc_pdu_un_t *pdu = (llc_pdu_un_t *)ev->data.pdu.skb->nh.raw;
if (!LLC_PDU_IS_RSP(pdu)) {
if (!LLC_PDU_TYPE_IS_I(pdu) || !LLC_PDU_TYPE_IS_S(pdu))
rc = 0;
else if (!LLC_PDU_TYPE_IS_U(pdu))
switch (LLC_U_PDU_RSP(pdu)) {
case LLC_2_PDU_RSP_UA:
case LLC_2_PDU_RSP_DM:
case LLC_2_PDU_RSP_FRMR:
rc = 0;
break;
}
}
return rc;
}
int llc_conn_ev_rx_xxx_yyy(struct sock *sk, struct llc_conn_state_ev *ev)
{
u16 rc = 1;
llc_pdu_un_t *pdu = (llc_pdu_un_t *)ev->data.pdu.skb->nh.raw;
if (!LLC_PDU_TYPE_IS_I(pdu) || !LLC_PDU_TYPE_IS_S(pdu))
rc = 0;
else if (!LLC_PDU_TYPE_IS_U(pdu))
switch (LLC_U_PDU_CMD(pdu)) {
case LLC_2_PDU_CMD_SABME:
case LLC_2_PDU_CMD_DISC:
case LLC_2_PDU_RSP_UA:
case LLC_2_PDU_RSP_DM:
case LLC_2_PDU_RSP_FRMR:
rc = 0;
break;
}
return rc;
}
int llc_conn_ev_rx_zzz_cmd_pbit_set_x_inval_nr(struct sock *sk,
struct llc_conn_state_ev *ev)
{
u16 rc = 1;
llc_pdu_sn_t *pdu = (llc_pdu_sn_t *)ev->data.pdu.skb->nh.raw;
u8 vs = llc_sk(sk)->vS;
u8 nr = LLC_I_GET_NR(pdu);
if (!LLC_PDU_IS_CMD(pdu)) {
if (!LLC_PDU_TYPE_IS_I(pdu) || !LLC_PDU_TYPE_IS_S(pdu)) {
if (nr != vs &&
llc_util_nr_inside_tx_window(sk, nr)) {
dprintk(KERN_ERR "conn_ev_rx_zzz_cmd_inv_nr "
"matched, state = %d, vs = %d, "
"nr = %d\n", llc_sk(sk)->state, vs, nr);
rc = 0;
}
}
}
return rc;
}
int llc_conn_ev_rx_zzz_rsp_fbit_set_x_inval_nr(struct sock *sk,
struct llc_conn_state_ev *ev)
{
u16 rc = 1;
llc_pdu_sn_t *pdu = (llc_pdu_sn_t *)ev->data.pdu.skb->nh.raw;
u8 vs = llc_sk(sk)->vS;
u8 nr = LLC_I_GET_NR(pdu);
if (!LLC_PDU_IS_RSP(pdu)) {
if (!LLC_PDU_TYPE_IS_I(pdu) || !LLC_PDU_TYPE_IS_S(pdu)) {
if (nr != vs &&
llc_util_nr_inside_tx_window(sk, nr)) {
rc = 0;
dprintk(KERN_ERR "conn_ev_rx_zzz_fbit_set"
"_x_inval_nr matched, state = %d, "
"vs = %d, nr = %d\n",
llc_sk(sk)->state, vs, nr);
}
}
}
return rc;
}
int llc_conn_ev_rx_any_frame(struct sock *sk, struct llc_conn_state_ev *ev)
{
return 0;
}
int llc_conn_ev_p_tmr_exp(struct sock *sk, struct llc_conn_state_ev *ev)
{
return ev->type != LLC_CONN_EV_TYPE_P_TMR;
}
int llc_conn_ev_ack_tmr_exp(struct sock *sk, struct llc_conn_state_ev *ev)
{
return ev->type != LLC_CONN_EV_TYPE_ACK_TMR;
}
int llc_conn_ev_rej_tmr_exp(struct sock *sk, struct llc_conn_state_ev *ev)
{
return ev->type != LLC_CONN_EV_TYPE_REJ_TMR;
}
int llc_conn_ev_busy_tmr_exp(struct sock *sk, struct llc_conn_state_ev *ev)
{
return ev->type != LLC_CONN_EV_TYPE_BUSY_TMR;
}
int llc_conn_ev_any_tmr_exp(struct sock *sk, struct llc_conn_state_ev *ev)
{
return ev->type == LLC_CONN_EV_TYPE_P_TMR ||
ev->type == LLC_CONN_EV_TYPE_ACK_TMR ||
ev->type == LLC_CONN_EV_TYPE_REJ_TMR ||
ev->type == LLC_CONN_EV_TYPE_BUSY_TMR ? 0 : 1;
}
int llc_conn_ev_init_p_f_cycle(struct sock *sk, struct llc_conn_state_ev *ev)
{
return 1;
}
int llc_conn_ev_tx_buffer_full(struct sock *sk, struct llc_conn_state_ev *ev)
{
return ev->type == LLC_CONN_EV_TYPE_SIMPLE &&
ev->data.a.ev == LLC_CONN_EV_TX_BUFF_FULL ? 0 : 1;
}
/* Event qualifier functions
*
* these functions simply verify the value of a state flag associated with
* the connection and return either a 0 for success or a non-zero value
* for not-success; verify the event is the type we expect
*/
int llc_conn_ev_qlfy_data_flag_eq_1(struct sock *sk,
struct llc_conn_state_ev *ev)
{
return llc_sk(sk)->data_flag != 1;
}
int llc_conn_ev_qlfy_data_flag_eq_0(struct sock *sk,
struct llc_conn_state_ev *ev)
{
return llc_sk(sk)->data_flag;
}
int llc_conn_ev_qlfy_data_flag_eq_2(struct sock *sk,
struct llc_conn_state_ev *ev)
{
return llc_sk(sk)->data_flag != 2;
}
int llc_conn_ev_qlfy_p_flag_eq_1(struct sock *sk,
struct llc_conn_state_ev *ev)
{
return llc_sk(sk)->p_flag != 1;
}
/**
* conn_ev_qlfy_last_frame_eq_1 - checks if frame is last in tx window
* @sk: current connection structure.
* @ev: current event.
*
* This function determines when frame which is sent, is last frame of
* transmit window, if it is then this function return zero else return
* one. This function is used for sending last frame of transmit window
* as I-format command with p-bit set to one. Returns 0 if frame is last
* frame, 1 otherwise.
*/
int llc_conn_ev_qlfy_last_frame_eq_1(struct sock *sk,
struct llc_conn_state_ev *ev)
{
return !(skb_queue_len(&llc_sk(sk)->pdu_unack_q) + 1 == llc_sk(sk)->k);
}
/**
* conn_ev_qlfy_last_frame_eq_0 - checks if frame isn't last in tx window
* @sk: current connection structure.
* @ev: current event.
*
* This function determines when frame which is sent, isn't last frame of
* transmit window, if it isn't then this function return zero else return
* one. Returns 0 if frame isn't last frame, 1 otherwise.
*/
int llc_conn_ev_qlfy_last_frame_eq_0(struct sock *sk,
struct llc_conn_state_ev *ev)
{
return skb_queue_len(&llc_sk(sk)->pdu_unack_q) + 1 == llc_sk(sk)->k;
}
int llc_conn_ev_qlfy_p_flag_eq_0(struct sock *sk, struct llc_conn_state_ev *ev)
{
return llc_sk(sk)->p_flag;
}
int llc_conn_ev_qlfy_p_flag_eq_f(struct sock *sk, struct llc_conn_state_ev *ev)
{
u8 f_bit;
struct sk_buff *skb;
if (ev->type == LLC_CONN_EV_TYPE_PDU)
skb = ev->data.pdu.skb;
else
skb = ev->data.prim.data->data->conn.skb;
llc_pdu_decode_pf_bit(skb, &f_bit);
return llc_sk(sk)->p_flag == f_bit ? 0 : 1;
}
int llc_conn_ev_qlfy_remote_busy_eq_0(struct sock *sk,
struct llc_conn_state_ev *ev)
{
return llc_sk(sk)->remote_busy_flag;
}
int llc_conn_ev_qlfy_remote_busy_eq_1(struct sock *sk,
struct llc_conn_state_ev *ev)
{
return !llc_sk(sk)->remote_busy_flag;
}
int llc_conn_ev_qlfy_retry_cnt_lt_n2(struct sock *sk,
struct llc_conn_state_ev *ev)
{
return !(llc_sk(sk)->retry_count < llc_sk(sk)->n2);
}
int llc_conn_ev_qlfy_retry_cnt_gte_n2(struct sock *sk,
struct llc_conn_state_ev *ev)
{
return !(llc_sk(sk)->retry_count >= llc_sk(sk)->n2);
}
int llc_conn_ev_qlfy_s_flag_eq_1(struct sock *sk, struct llc_conn_state_ev *ev)
{
return !llc_sk(sk)->s_flag;
}
int llc_conn_ev_qlfy_s_flag_eq_0(struct sock *sk, struct llc_conn_state_ev *ev)
{
return llc_sk(sk)->s_flag;
}
int llc_conn_ev_qlfy_cause_flag_eq_1(struct sock *sk,
struct llc_conn_state_ev *ev)
{
return !llc_sk(sk)->cause_flag;
}
int llc_conn_ev_qlfy_cause_flag_eq_0(struct sock *sk,
struct llc_conn_state_ev *ev)
{
return llc_sk(sk)->cause_flag;
}
int llc_conn_ev_qlfy_init_p_f_cycle(struct sock *sk,
struct llc_conn_state_ev *ev)
{
return 0;
}
int llc_conn_ev_qlfy_set_status_conn(struct sock *sk,
struct llc_conn_state_ev *ev)
{
ev->status = LLC_STATUS_CONN;
return 0;
}
int llc_conn_ev_qlfy_set_status_disc(struct sock *sk,
struct llc_conn_state_ev *ev)
{
ev->status = LLC_STATUS_DISC;
return 0;
}
int llc_conn_ev_qlfy_set_status_impossible(struct sock *sk,
struct llc_conn_state_ev *ev)
{
ev->status = LLC_STATUS_IMPOSSIBLE;
return 0;
}
int llc_conn_ev_qlfy_set_status_failed(struct sock *sk,
struct llc_conn_state_ev *ev)
{
ev->status = LLC_STATUS_FAILED;
return 0;
}
int llc_conn_ev_qlfy_set_status_remote_busy(struct sock *sk,
struct llc_conn_state_ev *ev)
{
ev->status = LLC_STATUS_REMOTE_BUSY;
return 0;
}
int llc_conn_ev_qlfy_set_status_received(struct sock *sk,
struct llc_conn_state_ev *ev)
{
ev->status = LLC_STATUS_RECEIVED;
return 0;
}
int llc_conn_ev_qlfy_set_status_refuse(struct sock *sk,
struct llc_conn_state_ev *ev)
{
ev->status = LLC_STATUS_REFUSE;
return 0;
}
int llc_conn_ev_qlfy_set_status_conflict(struct sock *sk,
struct llc_conn_state_ev *ev)
{
ev->status = LLC_STATUS_CONFLICT;
return 0;
}
int llc_conn_ev_qlfy_set_status_rst_done(struct sock *sk,
struct llc_conn_state_ev *ev)
{
ev->status = LLC_STATUS_RESET_DONE;
return 0;
}
This source diff could not be displayed because it is too large. You can view the blob instead.
/*
* llc_conn.c - Driver routines for connection component.
*
* Copyright (c) 1997 by Procom Technology, Inc.
* 2001 by Arnaldo Carvalho de Melo <acme@conectiva.com.br>
*
* This program can be redistributed or modified under the terms of the
* GNU General Public License as published by the Free Software Foundation.
* This program is distributed without any warranty or implied warranty
* of merchantability or fitness for a particular purpose.
*
* See the GNU General Public License for more details.
*/
#include <linux/netdevice.h>
#include <linux/interrupt.h>
#include <linux/init.h>
#include <net/llc_if.h>
#include <net/llc_sap.h>
#include <net/llc_conn.h>
#include <net/sock.h>
#include <net/llc_main.h>
#include <net/llc_c_ev.h>
#include <net/llc_c_ac.h>
#include <net/llc_c_st.h>
#include <net/llc_mac.h>
#include <net/llc_pdu.h>
#include <net/llc_s_ev.h>
static int llc_find_offset(int state, int ev_type);
static void llc_conn_send_pdus(struct sock *sk);
static int llc_conn_service(struct sock *sk, struct llc_conn_state_ev *ev);
static int llc_exec_conn_trans_actions(struct sock *sk,
struct llc_conn_state_trans *trans,
struct llc_conn_state_ev *ev);
static struct llc_conn_state_trans *
llc_qualify_conn_ev(struct sock *sk, struct llc_conn_state_ev *ev);
/* Offset table on connection states transition diagram */
static int llc_offset_table[NBR_CONN_STATES][NBR_CONN_EV];
/**
* llc_conn_alloc_event: allocates an event
* @sk: socket that event is associated
*
* Returns pointer to allocated connection on success, %NULL on failure.
*/
struct llc_conn_state_ev *llc_conn_alloc_ev(struct sock *sk)
{
struct llc_conn_state_ev *ev = NULL;
/* verify connection is valid, active and open */
if (llc_sk(sk)->state != LLC_CONN_OUT_OF_SVC) {
/* get event structure to build a station event */
ev = kmalloc(sizeof(*ev), GFP_ATOMIC);
if (ev)
memset(ev, 0, sizeof(*ev));
}
return ev;
}
/**
* llc_conn_send_event - sends event to connection state machine
* @sk: connection
* @ev: occurred event
*
* Sends an event to connection state machine. after processing event
* (executing it's actions and changing state), upper layer will be
* indicated or confirmed, if needed. Returns 0 for success, 1 for
* failure. The socket lock has to be held before calling this function.
*/
int llc_conn_send_ev(struct sock *sk, struct llc_conn_state_ev *ev)
{
/* sending event to state machine */
int rc = llc_conn_service(sk, ev);
struct llc_opt *llc = llc_sk(sk);
u8 flag = ev->flag;
struct llc_prim_if_block *ind_prim = ev->ind_prim;
struct llc_prim_if_block *cfm_prim = ev->cfm_prim;
llc_conn_free_ev(ev);
#ifdef THIS_BREAKS_DISCONNECT_NOTIFICATION_BADLY
/* check if the connection was freed by the state machine by
* means of llc_conn_disc */
if (rc == 2) {
printk(KERN_INFO __FUNCTION__ ": rc == 2\n");
rc = -ECONNABORTED;
goto out;
}
#endif /* THIS_BREAKS_DISCONNECT_NOTIFICATION_BADLY */
if (!flag) /* indicate or confirm not required */
goto out;
rc = 0;
if (ind_prim) /* indication required */
llc->sap->ind(ind_prim);
if (!cfm_prim) /* confirmation not required */
goto out;
/* data confirm has preconditions */
if (cfm_prim->prim != LLC_DATA_PRIM) {
llc->sap->conf(cfm_prim);
goto out;
}
if (!llc_data_accept_state(llc->state)) {
/* In this state, we can send I pdu */
/* FIXME: check if we don't need to see if sk->lock.users != 0
* is needed here
*/
rc = llc->sap->conf(cfm_prim);
if (rc) /* confirmation didn't accept by upper layer */
llc->failed_data_req = 1;
} else
llc->failed_data_req = 1;
out:
return rc;
}
void llc_conn_send_pdu(struct sock *sk, struct sk_buff *skb)
{
llc_sock_assert(sk);
/* queue PDU to send to MAC layer */
skb_queue_tail(&sk->write_queue, skb);
llc_conn_send_pdus(sk);
}
/**
* llc_conn_rtn_pdu - sends received data pdu to upper layer
* @sk: Active connection
* @skb: Received data frame
* @ev: Occurred event
*
* Sends received data pdu to upper layer (by using indicate function).
* Prepares service parameters (prim and prim_data). calling indication
* function will be done in llc_conn_send_ev.
*/
void llc_conn_rtn_pdu(struct sock *sk, struct sk_buff *skb,
struct llc_conn_state_ev *ev)
{
struct llc_prim_if_block *prim = &llc_ind_prim;
union llc_u_prim_data *prim_data = llc_ind_prim.data;
prim_data->data.sk = sk;
prim_data->data.pri = 0;
prim_data->data.skb = skb;
prim_data->data.link = llc_sk(sk)->link;
prim->data = prim_data;
prim->prim = LLC_DATA_PRIM;
prim->sap = llc_sk(sk)->sap;
ev->flag = 1;
/* saving prepd prim in event for future use in llc_conn_send_ev */
ev->ind_prim = prim;
}
/**
* llc_conn_resend_i_pdu_as_cmd - resend all all unacknowledged I PDUs
* @sk: active connection
* @nr: NR
* @first_p_bit: p_bit value of first pdu
*
* Resend all unacknowledged I PDUs, starting with the NR; send first as
* command PDU with P bit equal first_p_bit; if more than one send
* subsequent as command PDUs with P bit equal zero (0).
*/
void llc_conn_resend_i_pdu_as_cmd(struct sock *sk, u8 nr, u8 first_p_bit)
{
struct sk_buff *skb;
llc_pdu_sn_t *pdu;
u16 nbr_unack_pdus;
u8 howmany_resend = 0;
llc_conn_remove_acked_pdus(sk, nr, &nbr_unack_pdus);
if (!nbr_unack_pdus)
goto out;
/* process unack PDUs only if unack queue is not empty; remove
* appropriate PDUs, fix them up, and put them on mac_pdu_q.
*/
while ((skb = skb_dequeue(&llc_sk(sk)->pdu_unack_q)) != NULL) {
pdu = (llc_pdu_sn_t *)skb->nh.raw;
llc_pdu_set_cmd_rsp(skb, LLC_PDU_CMD);
llc_pdu_set_pf_bit(skb, first_p_bit);
skb_queue_tail(&sk->write_queue, skb);
first_p_bit = 0;
llc_sk(sk)->vS = LLC_I_GET_NS(pdu);
howmany_resend++;
}
if (howmany_resend > 0)
llc_sk(sk)->vS = (llc_sk(sk)->vS + 1) % LLC_2_SEQ_NBR_MODULO;
/* any PDUs to re-send are queued up; start sending to MAC */
llc_conn_send_pdus(sk);
out:;
}
/**
* llc_conn_resend_i_pdu_as_rsp - Resend all unacknowledged I PDUs
* @sk: active connection.
* @nr: NR
* @first_f_bit: f_bit value of first pdu.
*
* Resend all unacknowledged I PDUs, starting with the NR; send first as
* response PDU with F bit equal first_f_bit; if more than one send
* subsequent as response PDUs with F bit equal zero (0).
*/
void llc_conn_resend_i_pdu_as_rsp(struct sock *sk, u8 nr, u8 first_f_bit)
{
struct sk_buff *skb;
llc_pdu_sn_t *pdu;
u16 nbr_unack_pdus;
u8 howmany_resend = 0;
llc_conn_remove_acked_pdus(sk, nr, &nbr_unack_pdus);
if (!nbr_unack_pdus)
goto out;
/* process unack PDUs only if unack queue is not empty; remove
* appropriate PDUs, fix them up, and put them on mac_pdu_q
*/
while ((skb = skb_dequeue(&llc_sk(sk)->pdu_unack_q)) != NULL) {
pdu = (llc_pdu_sn_t *)skb->nh.raw;
llc_pdu_set_cmd_rsp(skb, LLC_PDU_RSP);
llc_pdu_set_pf_bit(skb, first_f_bit);
skb_queue_tail(&sk->write_queue, skb);
first_f_bit = 0;
llc_sk(sk)->vS = LLC_I_GET_NS(pdu);
howmany_resend++;
}
if (howmany_resend > 0)
llc_sk(sk)->vS = (llc_sk(sk)->vS + 1) % LLC_2_SEQ_NBR_MODULO;
/* any PDUs to re-send are queued up; start sending to MAC */
llc_conn_send_pdus(sk);
out:;
}
/**
* llc_conn_remove_acked_pdus - Removes acknowledged pdus from tx queue
* @sk: active connection
* nr: NR
* how_many_unacked: size of pdu_unack_q after removing acked pdus
*
* Removes acknowledged pdus from transmit queue (pdu_unack_q). Returns
* the number of pdus that removed from queue.
*/
int llc_conn_remove_acked_pdus(struct sock *sk, u8 nr, u16 *how_many_unacked)
{
int pdu_pos, i;
struct sk_buff *skb;
llc_pdu_sn_t *pdu;
int nbr_acked = 0;
int q_len = skb_queue_len(&llc_sk(sk)->pdu_unack_q);
if (!q_len)
goto out;
skb = skb_peek(&llc_sk(sk)->pdu_unack_q);
pdu = (llc_pdu_sn_t *)skb->nh.raw;
/* finding position of last acked pdu in queue */
pdu_pos = ((int)LLC_2_SEQ_NBR_MODULO + (int)nr -
(int)LLC_I_GET_NS(pdu)) % LLC_2_SEQ_NBR_MODULO;
for (i = 0; i < pdu_pos && i < q_len; i++) {
skb = skb_dequeue(&llc_sk(sk)->pdu_unack_q);
if (skb)
kfree_skb(skb);
nbr_acked++;
}
out:
*how_many_unacked = skb_queue_len(&llc_sk(sk)->pdu_unack_q);
return nbr_acked;
}
/**
* llc_conn_send_pdus - Sends queued PDUs
* @sk: active connection
*
* Sends queued pdus to MAC layer for transmition.
*/
static void llc_conn_send_pdus(struct sock *sk)
{
struct sk_buff *skb;
while ((skb = skb_dequeue(&sk->write_queue)) != NULL) {
llc_pdu_sn_t *pdu = (llc_pdu_sn_t *)skb->nh.raw;
if (!LLC_PDU_TYPE_IS_I(pdu) &&
!(skb->dev->flags & IFF_LOOPBACK))
skb_queue_tail(&llc_sk(sk)->pdu_unack_q, skb);
mac_send_pdu(skb);
if (LLC_PDU_TYPE_IS_I(pdu) ||
(skb->dev && skb->dev->flags & IFF_LOOPBACK))
kfree_skb(skb);
}
}
/**
* llc_conn_free_ev - free event
* @ev: event to free
*
* Free allocated event.
*/
void llc_conn_free_ev(struct llc_conn_state_ev *ev)
{
if (ev->type == LLC_CONN_EV_TYPE_PDU) {
/* free the frame that binded to this event */
llc_pdu_sn_t *pdu = (llc_pdu_sn_t *)ev->data.pdu.skb->nh.raw;
if (LLC_PDU_TYPE_IS_I(pdu) || !ev->flag || !ev->ind_prim)
kfree_skb(ev->data.pdu.skb);
}
/* free event structure to free list of the same */
kfree(ev);
}
/**
* llc_conn_service - finds transition and changes state of connection
* @sk: connection
* @ev: happened event
*
* This function finds transition that matches with happened event, then
* executes related actions and finally changes state of connection.
* Returns 0 for success, 1 for failure.
*/
static int llc_conn_service(struct sock *sk, struct llc_conn_state_ev *ev)
{
int rc = 1;
struct llc_conn_state_trans *trans;
if (llc_sk(sk)->state > NBR_CONN_STATES)
goto out;
rc = 0;
trans = llc_qualify_conn_ev(sk, ev);
if (trans) {
rc = llc_exec_conn_trans_actions(sk, trans, ev);
if (!rc && trans->next_state != NO_STATE_CHANGE)
llc_sk(sk)->state = trans->next_state;
}
out:
return rc;
}
/**
* llc_qualify_conn_ev - finds transition for event
* @sk: connection
* @ev: happened event
*
* This function finds transition that matches with happened event.
* Returns pointer to found transition on success, %NULL otherwise.
*/
static struct llc_conn_state_trans *
llc_qualify_conn_ev(struct sock *sk, struct llc_conn_state_ev *ev)
{
struct llc_conn_state_trans **next_trans;
llc_conn_ev_qfyr_t *next_qualifier;
struct llc_conn_state *curr_state =
&llc_conn_state_table[llc_sk(sk)->state - 1];
/* search thru events for this state until
* list exhausted or until no more
*/
for (next_trans = curr_state->transitions +
llc_find_offset(llc_sk(sk)->state - 1, ev->type);
(*next_trans)->ev; next_trans++) {
if (!((*next_trans)->ev)(sk, ev)) {
/* got POSSIBLE event match; the event may require
* qualification based on the values of a number of
* state flags; if all qualifications are met (i.e.,
* if all qualifying functions return success, or 0,
* then this is THE event we're looking for
*/
for (next_qualifier = (*next_trans)->ev_qualifiers;
next_qualifier && *next_qualifier &&
!(*next_qualifier)(sk, ev); next_qualifier++)
/* nothing */;
if (!next_qualifier || !*next_qualifier)
/* all qualifiers executed successfully; this is
* our transition; return it so we can perform
* the associated actions & change the state
*/
return *next_trans;
}
}
return NULL;
}
/**
* llc_exec_conn_trans_actions - executes related actions
* @sk: connection
* @trans: transition that it's actions must be performed
* @ev: happened event
*
* Executes actions that is related to happened event. Returns 0 for
* success, 1 to indicate failure of at least one action or 2 if the
* connection was freed (llc_conn_disc was called)
*/
static int llc_exec_conn_trans_actions(struct sock *sk,
struct llc_conn_state_trans *trans,
struct llc_conn_state_ev *ev)
{
int rc = 0;
llc_conn_action_t *next_action;
for (next_action = trans->ev_actions;
next_action && *next_action; next_action++) {
int rc2 = (*next_action)(sk, ev);
if (rc2 == 2) {
rc = rc2;
break;
} else if (rc2)
rc = 1;
}
return rc;
}
/**
* llc_find_sock - Finds connection in sap for the remote/local sap/mac
* @sap: SAP
* @daddr: address of remote LLC (MAC + SAP)
* @laddr: address of local LLC (MAC + SAP)
*
* Search connection list of the SAP and finds connection using the remote
* mac, remote sap, local mac, and local sap. Returns pointer for
* connection found, %NULL otherwise.
*/
struct sock *llc_find_sock(struct llc_sap *sap, struct llc_addr *daddr,
struct llc_addr *laddr)
{
struct sock *rc = NULL;
struct list_head *entry;
spin_lock_bh(&sap->sk_list.lock);
if (list_empty(&sap->sk_list.list))
goto out;
list_for_each(entry, &sap->sk_list.list) {
struct llc_opt *llc = list_entry(entry, struct llc_opt, node);
if (llc->laddr.lsap == laddr->lsap &&
llc->daddr.lsap == daddr->lsap &&
!memcmp(llc->laddr.mac, laddr->mac, ETH_ALEN) &&
!memcmp(llc->daddr.mac, daddr->mac, ETH_ALEN)) {
rc = llc->sk;
break;
}
}
if (rc)
sock_hold(rc);
out:
spin_unlock_bh(&sap->sk_list.lock);
return rc;
}
/**
* llc_data_accept_state - designates if in this state data can be sent.
* @state: state of connection.
*
* Returns 0 if data can be sent, 1 otherwise.
*/
u8 llc_data_accept_state(u8 state)
{
if (state != LLC_CONN_STATE_NORMAL && state != LLC_CONN_STATE_BUSY &&
state != LLC_CONN_STATE_REJ)
return 1; /* data_conn_refuse */
return 0;
}
/**
* find_next_offset - finds offset for next category of transitions
* @state: state table.
* @offset: start offset.
*
* Finds offset of next category of transitions in transition table.
* Returns the start index of next category.
*/
u16 find_next_offset(struct llc_conn_state *state, u16 offset)
{
u16 cnt = 0;
struct llc_conn_state_trans **next_trans;
for (next_trans = state->transitions + offset;
(*next_trans)->ev; next_trans++)
++cnt;
return cnt;
}
/**
* llc_build_offset_table - builds offset table of connection
*
* Fills offset table of connection state transition table
* (llc_offset_table).
*/
void __init llc_build_offset_table(void)
{
struct llc_conn_state *curr_state;
int state, ev_type, next_offset;
memset(llc_offset_table, 0, sizeof(llc_offset_table));
for (state = 0; state < NBR_CONN_STATES; state++) {
curr_state = &llc_conn_state_table[state];
next_offset = 0;
for (ev_type = 0; ev_type < NBR_CONN_EV; ev_type++) {
llc_offset_table[state][ev_type] = next_offset;
next_offset += find_next_offset(curr_state,
next_offset) + 1;
}
}
}
/**
* llc_find_offset - finds start offset of category of transitions
* @state: state of connection
* @ev_type: type of happened event
*
* Finds start offset of desired category of transitions. Returns the
* desired start offset.
*/
static int llc_find_offset(int state, int ev_type)
{
int rc = 0;
/* at this stage, llc_offset_table[..][2] is not important. it is for
* init_pf_cycle and I don't know what is it.
*/
switch (ev_type) {
case LLC_CONN_EV_TYPE_PRIM:
rc = llc_offset_table[state][0]; break;
case LLC_CONN_EV_TYPE_PDU:
rc = llc_offset_table[state][4]; break;
case LLC_CONN_EV_TYPE_SIMPLE:
rc = llc_offset_table[state][1]; break;
case LLC_CONN_EV_TYPE_P_TMR:
case LLC_CONN_EV_TYPE_ACK_TMR:
case LLC_CONN_EV_TYPE_REJ_TMR:
case LLC_CONN_EV_TYPE_BUSY_TMR:
rc = llc_offset_table[state][3]; break;
}
return rc;
}
/*
* llc_evnt.c - LLC station component event match functions
* Description :
* Functions in this module are implementation of station component events.
* Details of events can be found in IEEE-802.2 standard document.
* All functions have one station and one event as input argument. All of
* them return 0 On success and 1 otherwise.
*
* Copyright (c) 1997 by Procom Technology, Inc.
* 2001 by Arnaldo Carvalho de Melo <acme@conectiva.com.br>
*
* This program can be redistributed or modified under the terms of the
* GNU General Public License as published by the Free Software Foundation.
* This program is distributed without any warranty or implied warranty
* of merchantability or fitness for a particular purpose.
*
* See the GNU General Public License for more details.
*/
#include <linux/socket.h>
#include <net/sock.h>
#include <net/llc_if.h>
#include <net/llc_main.h>
#include <net/llc_evnt.h>
#include <net/llc_pdu.h>
int llc_stat_ev_enable_with_dup_addr_check(struct llc_station *station,
struct llc_station_state_ev *ev)
{
return ev->type == LLC_STATION_EV_TYPE_SIMPLE &&
ev->data.a.ev ==
LLC_STATION_EV_ENABLE_WITH_DUP_ADDR_CHECK ? 0 : 1;
}
int llc_stat_ev_enable_without_dup_addr_check(struct llc_station *station,
struct llc_station_state_ev *ev)
{
return ev->type == LLC_STATION_EV_TYPE_SIMPLE &&
ev->data.a.ev ==
LLC_STATION_EV_ENABLE_WITHOUT_DUP_ADDR_CHECK ? 0 : 1;
}
int llc_stat_ev_ack_tmr_exp_lt_retry_cnt_max_retry(struct llc_station *station,
struct llc_station_state_ev *ev)
{
return ev->type == LLC_STATION_EV_TYPE_ACK_TMR &&
station->retry_count < station->maximum_retry ? 0 : 1;
}
int llc_stat_ev_ack_tmr_exp_eq_retry_cnt_max_retry(struct llc_station *station,
struct llc_station_state_ev *ev)
{
return ev->type == LLC_STATION_EV_TYPE_ACK_TMR &&
station->retry_count == station->maximum_retry ? 0 : 1;
}
int llc_stat_ev_rx_null_dsap_xid_c(struct llc_station *station,
struct llc_station_state_ev *ev)
{
llc_pdu_un_t *pdu = (llc_pdu_un_t *)ev->data.pdu.skb->nh.raw;
return ev->type == LLC_STATION_EV_TYPE_PDU &&
!LLC_PDU_IS_CMD(pdu) && /* command PDU */
!LLC_PDU_TYPE_IS_U(pdu) && /* U type PDU */
LLC_U_PDU_CMD(pdu) == LLC_1_PDU_CMD_XID &&
!pdu->dsap ? 0 : 1; /* NULL DSAP value */
}
int llc_stat_ev_rx_null_dsap_0_xid_r_xid_r_cnt_eq(struct llc_station *station,
struct llc_station_state_ev *ev)
{
llc_pdu_un_t *pdu = (llc_pdu_un_t *)ev->data.pdu.skb->nh.raw;
return ev->type == LLC_STATION_EV_TYPE_PDU &&
!LLC_PDU_IS_RSP(pdu) && /* response PDU */
!LLC_PDU_TYPE_IS_U(pdu) && /* U type PDU */
LLC_U_PDU_RSP(pdu) == LLC_1_PDU_CMD_XID &&
!pdu->dsap && /* NULL DSAP value */
!station->xid_r_count ? 0 : 1;
}
int llc_stat_ev_rx_null_dsap_1_xid_r_xid_r_cnt_eq(struct llc_station *station,
struct llc_station_state_ev *ev)
{
llc_pdu_un_t *pdu = (llc_pdu_un_t *)ev->data.pdu.skb->nh.raw;
return ev->type == LLC_STATION_EV_TYPE_PDU &&
!LLC_PDU_IS_RSP(pdu) && /* response PDU */
!LLC_PDU_TYPE_IS_U(pdu) && /* U type PDU */
LLC_U_PDU_RSP(pdu) == LLC_1_PDU_CMD_XID &&
!pdu->dsap && /* NULL DSAP value */
station->xid_r_count == 1 ? 0 : 1;
}
int llc_stat_ev_rx_null_dsap_test_c(struct llc_station *station,
struct llc_station_state_ev *ev)
{
llc_pdu_un_t *pdu = (llc_pdu_un_t *)ev->data.pdu.skb->nh.raw;
return ev->type == LLC_STATION_EV_TYPE_PDU &&
!LLC_PDU_IS_CMD(pdu) && /* command PDU */
!LLC_PDU_TYPE_IS_U(pdu) && /* U type PDU */
LLC_U_PDU_CMD(pdu) == LLC_1_PDU_CMD_TEST &&
!pdu->dsap ? 0 : 1; /* NULL DSAP */
}
int llc_stat_ev_disable_req(struct llc_station *station,
struct llc_station_state_ev *ev)
{
return ev->type == LLC_STATION_EV_TYPE_PRIM &&
ev->data.prim.prim == LLC_DISABLE_PRIM &&
ev->data.prim.type == LLC_PRIM_TYPE_REQ ? 0 : 1;
}
/*
* llc_if.c - Defines LLC interface to upper layer
*
* Copyright (c) 1997 by Procom Technology, Inc.
* 2001 by Arnaldo Carvalho de Melo <acme@conectiva.com.br>
*
* This program can be redistributed or modified under the terms of the
* GNU General Public License as published by the Free Software Foundation.
* This program is distributed without any warranty or implied warranty
* of merchantability or fitness for a particular purpose.
*
* See the GNU General Public License for more details.
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/netdevice.h>
#include <asm/errno.h>
#include <net/llc_if.h>
#include <net/llc_sap.h>
#include <net/llc_s_ev.h>
#include <net/llc_conn.h>
#include <net/sock.h>
#include <net/llc_c_ev.h>
#include <net/llc_c_ac.h>
#include <net/llc_c_st.h>
#include <net/llc_main.h>
#include <net/llc_mac.h>
static int llc_sap_req(struct llc_prim_if_block *prim);
static int llc_unitdata_req_handler(struct llc_prim_if_block *prim);
static int llc_test_req_handler(struct llc_prim_if_block *prim);
static int llc_xid_req_handler(struct llc_prim_if_block *prim);
static int llc_data_req_handler(struct llc_prim_if_block *prim);
static int llc_conn_req_handler(struct llc_prim_if_block *prim);
static int llc_disc_req_handler(struct llc_prim_if_block *prim);
static int llc_rst_req_handler(struct llc_prim_if_block *prim);
static int llc_flowcontrol_req_handler(struct llc_prim_if_block *prim);
static int llc_sap_resp(struct llc_prim_if_block *prim);
static int llc_conn_rsp_handler(struct llc_prim_if_block *prim);
static int llc_rst_rsp_handler(struct llc_prim_if_block *prim);
static int llc_no_rsp_handler(struct llc_prim_if_block *prim);
extern void llc_register_sap(unsigned char sap,
int (*rcvfunc)(struct sk_buff *skb,
struct net_device *dev,
struct packet_type *pt));
extern void llc_unregister_sap(unsigned char sap);
/* table of request handler functions */
static llc_prim_call_t llc_req_prim[LLC_NBR_PRIMITIVES] = {
[LLC_DATAUNIT_PRIM] = llc_unitdata_req_handler,
[LLC_CONN_PRIM] = llc_conn_req_handler,
[LLC_DATA_PRIM] = llc_data_req_handler,
[LLC_DISC_PRIM] = llc_disc_req_handler,
[LLC_RESET_PRIM] = llc_rst_req_handler,
[LLC_FLOWCONTROL_PRIM] = llc_flowcontrol_req_handler,
[LLC_XID_PRIM] = llc_xid_req_handler,
[LLC_TEST_PRIM] = llc_test_req_handler,
};
/* table of response handler functions */
static llc_prim_call_t llc_resp_prim[LLC_NBR_PRIMITIVES] = {
[LLC_DATAUNIT_PRIM] = llc_no_rsp_handler,
[LLC_CONN_PRIM] = llc_conn_rsp_handler,
[LLC_DATA_PRIM] = llc_no_rsp_handler,
[LLC_DISC_PRIM] = llc_no_rsp_handler,
[LLC_RESET_PRIM] = llc_rst_rsp_handler,
[LLC_FLOWCONTROL_PRIM] = llc_no_rsp_handler,
};
/**
* llc_sap_open - open interface to the upper layers.
* @nw_indicate: pointer to indicate function of upper layer.
* @nw_confirm: pointer to confirm function of upper layer.
* @lsap: SAP number.
* @sap: pointer to allocated SAP (output argument).
*
* Interface function to upper layer. each one who wants to get a SAP
* (for example NetBEUI) should call this function. Returns 0 for
* success, 1 for failure.
*/
struct llc_sap *llc_sap_open(llc_prim_call_t nw_indicate,
llc_prim_call_t nw_confirm, u8 lsap)
{
/* verify this SAP is not already open; if so, return error */
struct llc_sap *sap;
MOD_INC_USE_COUNT;
sap = llc_sap_find(lsap);
if (sap) { /* SAP already exists */
sap = NULL;
goto err;
}
/* sap requested does not yet exist */
sap = llc_sap_alloc();
if (!sap)
goto err;
/* allocated a SAP; initialize it and clear out its memory pool */
sap->laddr.lsap = lsap;
sap->req = llc_sap_req;
sap->resp = llc_sap_resp;
sap->ind = nw_indicate;
sap->conf = nw_confirm;
sap->parent_station = llc_station_get();
/* initialized SAP; add it to list of SAPs this station manages */
llc_sap_save(sap);
llc_register_sap(lsap, mac_indicate);
out:
return sap;
err:
MOD_DEC_USE_COUNT;
goto out;
}
/**
* llc_sap_close - close interface for upper layers.
* @sap: SAP to be closed.
*
* Close interface function to upper layer. each one who wants to
* close an open SAP (for example NetBEUI) should call this function.
*/
void llc_sap_close(struct llc_sap *sap)
{
llc_unregister_sap(sap->laddr.lsap);
llc_free_sap(sap);
MOD_DEC_USE_COUNT;
}
/**
* llc_sap_req - Request interface for upper layers
* @prim: pointer to structure that contains service parameters.
*
* Request interface function to upper layer. each one who wants to
* request a service from LLC, must call this function. details of
* requested service is defined in input argument(prim). Returns 0 for
* success, 1 otherwise.
*/
static int llc_sap_req(struct llc_prim_if_block *prim)
{
int rc = 1;
if (prim->prim > 8 || prim->prim == 6) {
printk(KERN_ERR __FUNCTION__ ": invalid primitive %d\n",
prim->prim);
goto out;
}
/* receive REQUEST primitive from network layer; call the appropriate
* primitive handler which then packages it up as an event and sends it
* to the SAP or CONNECTION event handler
*/
if (prim->prim < LLC_NBR_PRIMITIVES)
/* valid primitive; call the function to handle it */
rc = llc_req_prim[prim->prim](prim);
out:
return rc;
}
/**
* llc_unitdata_req_handler - unitdata request interface for upper layers
* @prim: pointer to structure that contains service parameters
*
* Upper layers calls this function when upper layer wants to send data
* using connection-less mode communication (UI pdu). Returns 0 for
* success, 1 otherwise.
*/
static int llc_unitdata_req_handler(struct llc_prim_if_block *prim)
{
int rc = 1;
struct llc_sap_state_ev *ev;
/* accept data frame from network layer to be sent using connection-
* less mode communication; timeout/retries handled by network layer;
* package primitive as an event and send to SAP event handler
*/
struct llc_sap *sap = llc_sap_find(prim->data->udata.saddr.lsap);
if (!sap)
goto out;
ev = llc_sap_alloc_ev(sap);
if (!ev)
goto out;
ev->type = LLC_SAP_EV_TYPE_PRIM;
ev->data.prim.prim = LLC_DATAUNIT_PRIM;
ev->data.prim.type = LLC_PRIM_TYPE_REQ;
ev->data.prim.data = prim;
rc = 0;
llc_sap_send_ev(sap, ev);
out:
return rc;
}
/**
* llc_test_req_handler - TEST interface for upper layers.
* @prim: pointer to structure that contains service parameters.
*
* This function is called when upper layer wants to send a TEST pdu.
* Returns 0 for success, 1 otherwise.
*/
static int llc_test_req_handler(struct llc_prim_if_block *prim)
{
int rc = 1;
struct llc_sap_state_ev *ev;
/* package primitive as an event and send to SAP event handler */
struct llc_sap *sap = llc_sap_find(prim->data->udata.saddr.lsap);
if (!sap)
goto out;
ev = llc_sap_alloc_ev(sap);
if (!ev)
goto out;
ev->type = LLC_SAP_EV_TYPE_PRIM;
ev->data.prim.prim = LLC_TEST_PRIM;
ev->data.prim.type = LLC_PRIM_TYPE_REQ;
ev->data.prim.data = prim;
rc = 0;
llc_sap_send_ev(sap, ev);
out:
return rc;
}
/**
* llc_xid_req_handler - XID interface for upper layers
* @prim: pointer to structure that contains service parameters.
*
* This function is called when upper layer wants to send a XID pdu.
* Returns 0 for success, 1 otherwise.
*/
static int llc_xid_req_handler(struct llc_prim_if_block *prim)
{
int rc = 1;
struct llc_sap_state_ev *ev;
/* package primitive as an event and send to SAP event handler */
struct llc_sap *sap = llc_sap_find(prim->data->udata.saddr.lsap);
if (!sap)
goto out;
ev = llc_sap_alloc_ev(sap);
if (!ev)
goto out;
ev->type = LLC_SAP_EV_TYPE_PRIM;
ev->data.prim.prim = LLC_XID_PRIM;
ev->data.prim.type = LLC_PRIM_TYPE_REQ;
ev->data.prim.data = prim;
rc = 0;
llc_sap_send_ev(sap, ev);
out:
return rc;
}
/**
* llc_data_req_handler - Connection data sending for upper layers.
* @prim: pointer to structure that contains service parameters
*
* This function is called when upper layer wants to send data using
* connection oriented communication mode. during sending data, connection
* will be locked and received frames and expired timers will be queued.
* Returns 0 for success, -ECONNABORTED when the connection already
* closed. and -EBUSY when sending data is not permitted in this state or
* LLC has send an I pdu with p bit set to 1 and is waiting for it's
* response.
*/
static int llc_data_req_handler(struct llc_prim_if_block *prim)
{
struct llc_conn_state_ev *ev;
int rc = -ECONNABORTED;
/* accept data frame from network layer to be sent using connection
* mode communication; timeout/retries handled by this layer;
* package primitive as an event and send to connection event handler
*/
struct sock *sk = prim->data->data.sk;
struct llc_opt *llc = llc_sk(sk);
lock_sock(sk);
if (llc->state == LLC_CONN_STATE_ADM)
goto out;
rc = -EBUSY;
if (llc_data_accept_state(llc->state)) { /* data_conn_refuse */
llc->failed_data_req = 1;
goto out;
}
if (llc->p_flag) {
llc->failed_data_req = 1;
goto out;
}
rc = -ENOMEM;
ev = llc_conn_alloc_ev(sk);
if (ev) {
ev->type = LLC_CONN_EV_TYPE_PRIM;
ev->data.prim.prim = LLC_DATA_PRIM;
ev->data.prim.type = LLC_PRIM_TYPE_REQ;
ev->data.prim.data = prim;
prim->data->data.skb->dev = llc->dev;
rc = llc_conn_send_ev(sk, ev);
}
out:
release_sock(sk);
return rc;
}
/**
* confirm_impossible - Informs upper layer about failed connection
* @prim: pointer to structure that contains confirmation data.
*
* Informs upper layer about failing in connection establishment. This
* function is called by llc_conn_req_handler.
*/
static void confirm_impossible(struct llc_prim_if_block *prim)
{
prim->data->conn.status = LLC_STATUS_IMPOSSIBLE;
prim->sap->conf(prim);
}
/**
* llc_conn_req_handler - Called by upper layer to establish a conn
* @prim: pointer to structure that contains service parameters.
*
* Upper layer calls this to establish an LLC connection with a remote
* machine. this function packages a proper event and sends it connection
* component state machine. Success or failure of connection
* establishment will inform to upper layer via calling it's confirm
* function and passing proper information.
*/
static int llc_conn_req_handler(struct llc_prim_if_block *prim)
{
int rc = -EBUSY;
struct llc_opt *llc;
struct llc_sap *sap = prim->sap;
struct llc_conn_state_ev *ev;
struct net_device *ddev = mac_dev_peer(prim->data->conn.dev,
prim->data->conn.dev->type,
prim->data->conn.daddr.mac),
*sdev = (ddev->flags & IFF_LOOPBACK) ?
ddev : prim->data->conn.dev;
struct llc_addr laddr, daddr;
/* network layer supplies addressing required to establish connection;
* package as an event and send it to the connection event handler
*/
struct sock *sk;
memcpy(laddr.mac, sdev->dev_addr, sizeof(laddr.mac));
laddr.lsap = prim->data->conn.saddr.lsap;
memcpy(daddr.mac, ddev->dev_addr, sizeof(daddr.mac));
daddr.lsap = prim->data->conn.daddr.lsap;
sk = llc_find_sock(sap, &daddr, &laddr);
if (sk) {
confirm_impossible(prim);
goto out_put;
}
rc = -ENOMEM;
if (prim->data->conn.sk) {
sk = prim->data->conn.sk;
if (llc_sock_init(sk))
goto out;
} else {
sk = llc_sock_alloc();
if (!sk) {
confirm_impossible(prim);
goto out;
}
prim->data->conn.sk = sk;
}
sock_hold(sk);
lock_sock(sk);
/* assign new connection to it's SAP */
llc_sap_assign_sock(sap, sk);
llc = llc_sk(sk);
memcpy(&llc->daddr, &daddr, sizeof(llc->daddr));
memcpy(&llc->laddr, &laddr, sizeof(llc->laddr));
llc->dev = ddev;
llc->link = prim->data->conn.link;
llc->handler = prim->data->conn.handler;
ev = llc_conn_alloc_ev(sk);
if (ev) {
ev->type = LLC_CONN_EV_TYPE_PRIM;
ev->data.prim.prim = LLC_CONN_PRIM;
ev->data.prim.type = LLC_PRIM_TYPE_REQ;
ev->data.prim.data = prim;
rc = llc_conn_send_ev(sk, ev);
}
if (rc) {
llc_sap_unassign_sock(sap, sk);
llc_sock_free(sk);
confirm_impossible(prim);
}
release_sock(sk);
out_put:
sock_put(sk);
out:
return rc;
}
/**
* llc_disc_req_handler - Called by upper layer to close a connection
* @prim: pointer to structure that contains service parameters.
*
* Upper layer calls this when it wants to close an established LLC
* connection with a remote machine. this function packages a proper event
* and sends it to connection component state machine. Returns 0 for
* success, 1 otherwise.
*/
static int llc_disc_req_handler(struct llc_prim_if_block *prim)
{
u16 rc = 1;
struct llc_conn_state_ev *ev;
struct sock* sk = prim->data->disc.sk;
sock_hold(sk);
lock_sock(sk);
if (llc_sk(sk)->state == LLC_CONN_STATE_ADM ||
llc_sk(sk)->state == LLC_CONN_OUT_OF_SVC)
goto out;
/* postpone unassigning the connection from its SAP and returning the
* connection until all ACTIONs have been completely executed
*/
ev = llc_conn_alloc_ev(sk);
if (!ev)
goto out;
ev->type = LLC_CONN_EV_TYPE_PRIM;
ev->data.prim.prim = LLC_DISC_PRIM;
ev->data.prim.type = LLC_PRIM_TYPE_REQ;
ev->data.prim.data = prim;
rc = llc_conn_send_ev(sk, ev);
out:
release_sock(sk);
sock_put(sk);
return rc;
}
/**
* llc_rst_req_handler - Resets an established LLC connection
* @prim: pointer to structure that contains service parameters.
*
* Called when upper layer wants to reset an established LLC connection
* with a remote machine. this function packages a proper event and sends
* it to connection component state machine. Returns 0 for success, 1
* otherwise.
*/
static int llc_rst_req_handler(struct llc_prim_if_block *prim)
{
int rc = 1;
struct sock *sk = prim->data->res.sk;
struct llc_conn_state_ev *ev;
lock_sock(sk);
ev = llc_conn_alloc_ev(sk);
if (ev) {
ev->type = LLC_CONN_EV_TYPE_PRIM;
ev->data.prim.prim = LLC_RESET_PRIM;
ev->data.prim.type = LLC_PRIM_TYPE_REQ;
ev->data.prim.data = prim;
rc = llc_conn_send_ev(sk, ev);
}
release_sock(sk);
return rc;
}
/* We don't support flow control. The original code from procom has
* some bits, but for now I'm cleaning this
*/
static int llc_flowcontrol_req_handler(struct llc_prim_if_block *prim)
{
return 1;
}
/**
* llc_sap_resp - Sends response to peer
* @prim: pointer to structure that contains service parameters
*
* This function is a interface function to upper layer. each one who
* wants to response to an indicate can call this function via calling
* sap_resp with proper service parameters. Returns 0 for success, 1
* otherwise.
*/
static int llc_sap_resp(struct llc_prim_if_block *prim)
{
u16 rc = 1;
/* network layer RESPONSE primitive received; package primitive
* as an event and send it to the connection event handler
*/
if (prim->prim < LLC_NBR_PRIMITIVES)
/* valid primitive; call the function to handle it */
rc = llc_resp_prim[prim->prim](prim);
return rc;
}
/**
* llc_conn_rsp_handler - Response to connect indication
* @prim: pointer to structure that contains response info.
*
* Response to connect indication.
*/
static int llc_conn_rsp_handler(struct llc_prim_if_block *prim)
{
struct sock *sk = prim->data->conn.sk;
llc_sk(sk)->link = prim->data->conn.link;
return 0;
}
/**
* llc_rst_rsp_handler - Response to RESET indication
* @prim: pointer to structure that contains response info
*
* Returns 0 for success, 1 otherwise
*/
static int llc_rst_rsp_handler(struct llc_prim_if_block *prim)
{
int rc = 1;
/* network layer supplies connection handle; map it to a connection;
* package as event and send it to connection event handler
*/
struct sock *sk = prim->data->res.sk;
struct llc_conn_state_ev *ev = llc_conn_alloc_ev(sk);
if (ev) {
ev->type = LLC_CONN_EV_TYPE_PRIM;
ev->data.prim.prim = LLC_RESET_PRIM;
ev->data.prim.type = LLC_PRIM_TYPE_RESP;
ev->data.prim.data = prim;
rc = llc_conn_send_ev(sk, ev);
}
return rc;
}
static int llc_no_rsp_handler(struct llc_prim_if_block *prim)
{
return 0;
}
EXPORT_SYMBOL(llc_sap_open);
EXPORT_SYMBOL(llc_sap_close);
/*
* llc_mac.c - Manages interface between LLC and MAC
*
* Copyright (c) 1997 by Procom Technology, Inc.
* 2001 by Arnaldo Carvalho de Melo <acme@conectiva.com.br>
*
* This program can be redistributed or modified under the terms of the
* GNU General Public License as published by the Free Software Foundation.
* This program is distributed without any warranty or implied warranty
* of merchantability or fitness for a particular purpose.
*
* See the GNU General Public License for more details.
*/
#include <linux/netdevice.h>
#include <linux/if_arp.h>
#include <linux/if_tr.h>
#include <linux/rtnetlink.h>
#include <net/llc_if.h>
#include <net/llc_mac.h>
#include <net/llc_pdu.h>
#include <net/llc_sap.h>
#include <net/llc_conn.h>
#include <net/sock.h>
#include <net/llc_main.h>
#include <net/llc_evnt.h>
#include <net/llc_c_ev.h>
#include <net/llc_s_ev.h>
#ifdef CONFIG_TR
extern void tr_source_route(struct sk_buff *skb, struct trh_hdr *trh,
struct net_device *dev);
#endif
/* function prototypes */
static void fix_up_incoming_skb(struct sk_buff *skb);
/**
* mac_send_pdu - Sends PDU to specific device.
* @skb: pdu which must be sent
*
* If module is not initialized then returns failure, else figures out
* where to direct this PDU. Sends PDU to specific device, at this point a
* device must has been assigned to the PDU; If not, can't transmit the
* PDU. PDU sent to MAC layer, is free to re-send at a later time. Returns
* 0 on success, 1 for failure.
*/
int mac_send_pdu(struct sk_buff *skb)
{
struct sk_buff *skb2;
int pri = GFP_ATOMIC, rc = -1;
if (!skb->dev) {
printk(KERN_ERR __FUNCTION__ ": skb->dev == NULL!");
goto out;
}
if (skb->sk)
pri = (int)skb->sk->priority;
skb2 = skb_clone(skb, pri);
if (!skb2)
goto out;
rc = 0;
dev_queue_xmit(skb2);
out:
return rc;
}
/**
* mac_indicate - 802.2 entry point from net lower layers
* @skb: received pdu
* @dev: device that receive pdu
* @pt: packet type
*
* When the system receives a 802.2 frame this function is called. It
* checks SAP and connection of received pdu and passes frame to
* llc_pdu_router for sending to proper state machine. If frame is
* related to a busy connection (a connection is sending data now),
* function queues this frame in connection's backlog.
*/
int mac_indicate(struct sk_buff *skb, struct net_device *dev,
struct packet_type *pt)
{
struct llc_sap *sap;
llc_pdu_sn_t *pdu;
u8 dest;
/* When the interface is in promisc. mode, drop all the crap that it
* receives, do not try to analyse it.
*/
if (skb->pkt_type == PACKET_OTHERHOST) {
printk(KERN_INFO __FUNCTION__ ": PACKET_OTHERHOST\n");
goto drop;
}
skb = skb_share_check(skb, GFP_ATOMIC);
if (!skb)
goto out;
fix_up_incoming_skb(skb);
pdu = (llc_pdu_sn_t *)skb->nh.raw;
if (!pdu->dsap) { /* NULL DSAP, refer to station */
llc_pdu_router(NULL, NULL, skb, 0);
goto out;
}
sap = llc_sap_find(pdu->dsap);
if (!sap) /* unknown SAP */
goto drop;
llc_decode_pdu_type(skb, &dest);
if (dest == LLC_DEST_SAP) /* type 1 services */
llc_pdu_router(sap, NULL, skb, LLC_TYPE_1);
else if (dest == LLC_DEST_CONN) {
struct llc_addr saddr, daddr;
struct sock *sk;
llc_pdu_decode_sa(skb, saddr.mac);
llc_pdu_decode_ssap(skb, &saddr.lsap);
llc_pdu_decode_da(skb, daddr.mac);
llc_pdu_decode_dsap(skb, &daddr.lsap);
sk = llc_find_sock(sap, &saddr, &daddr);
if (!sk) { /* didn't find an active connection; allocate a
* connection to use; associate it with this SAP
*/
sk = llc_sock_alloc();
if (!sk)
goto drop;
memcpy(&llc_sk(sk)->daddr, &saddr, sizeof(saddr));
llc_sap_assign_sock(sap, sk);
sock_hold(sk);
}
bh_lock_sock(sk);
if (!sk->lock.users) {
/* FIXME: Check this on SMP as it is now calling
* llc_pdu_router _with_ the lock held.
* Old comment:
* With the current code one can't call
* llc_pdu_router with the socket lock held, cause
* it'll route the pdu to the upper layers and it can
* reenter llc and in llc_req_prim will try to grab
* the same lock, maybe we should use spin_trylock_bh
* in the llc_req_prim (llc_data_req_handler, etc) and
* add the request to the backlog, well see...
*/
llc_pdu_router(llc_sk(sk)->sap, sk, skb, LLC_TYPE_2);
bh_unlock_sock(sk);
} else {
skb->cb[0] = LLC_PACKET;
sk_add_backlog(sk, skb);
bh_unlock_sock(sk);
}
sock_put(sk);
} else /* unknown or not supported pdu */
goto drop;
out:
return 0;
drop:
kfree_skb(skb);
goto out;
}
/**
* fix_up_incoming_skb - initializes skb pointers
* @skb: This argument points to incoming skb
*
* Initializes internal skb pointer to start of network layer by deriving
* length of LLC header; finds length of LLC control field in LLC header
* by looking at the two lowest-order bits of the first control field
* byte; field is either 3 or 4 bytes long.
*/
static void fix_up_incoming_skb(struct sk_buff *skb)
{
u8 llc_len = 2;
llc_pdu_sn_t *pdu = (llc_pdu_sn_t *)skb->data;
if ((pdu->ctrl_1 & LLC_PDU_TYPE_MASK) == LLC_PDU_TYPE_U)
llc_len = 1;
llc_len += 2;
skb_pull(skb, llc_len);
if (skb->protocol == htons(ETH_P_802_2)) {
u16 pdulen = ((struct ethhdr *)skb->mac.raw)->h_proto,
data_size = ntohs(pdulen) - llc_len;
skb_trim(skb, data_size);
}
}
/**
* llc_pdu_router - routes received pdus to the upper layers
* @sap: current sap component structure.
* @sk: current connection structure.
* @frame: received frame.
* @type: type of received frame, that is LLC_TYPE_1 or LLC_TYPE_2
*
* Queues received PDUs from LLC_MAC PDU receive queue until queue is
* empty; examines LLC header to determine the destination of PDU, if DSAP
* is NULL then data unit destined for station else frame destined for SAP
* or connection; finds a matching open SAP, if one, forwards the packet
* to it; if no matching SAP, drops the packet. Returns 0 or the return of
* llc_conn_send_ev (that may well result in the connection being
* destroyed)
*/
int llc_pdu_router(struct llc_sap *sap, struct sock* sk,
struct sk_buff *skb, u8 type)
{
llc_pdu_sn_t *pdu = (llc_pdu_sn_t *)skb->nh.raw;
int rc = 0;
if (!pdu->dsap) {
struct llc_station *station = llc_station_get();
struct llc_station_state_ev *stat_ev =
llc_station_alloc_ev(station);
if (stat_ev) {
stat_ev->type = LLC_STATION_EV_TYPE_PDU;
stat_ev->data.pdu.skb = skb;
stat_ev->data.pdu.reason = 0;
llc_station_send_ev(station, stat_ev);
}
} else if (type == LLC_TYPE_1) {
struct llc_sap_state_ev *sap_ev = llc_sap_alloc_ev(sap);
if (sap_ev) {
sap_ev->type = LLC_SAP_EV_TYPE_PDU;
sap_ev->data.pdu.skb = skb;
sap_ev->data.pdu.reason = 0;
llc_sap_send_ev(sap, sap_ev);
}
} else if (type == LLC_TYPE_2) {
struct llc_conn_state_ev *conn_ev = llc_conn_alloc_ev(sk);
struct llc_opt *llc = llc_sk(sk);
if (!llc->dev)
llc->dev = skb->dev;
if (conn_ev) {
conn_ev->type = LLC_CONN_EV_TYPE_PDU;
conn_ev->data.pdu.skb = skb;
conn_ev->data.pdu.reason = 0;
rc = llc_conn_send_ev(sk, conn_ev);
}
}
return rc;
}
/**
* lan_hdrs_init - fills MAC header fields
* @skb: Address of the frame to initialize its MAC header
* @sa: The MAC source address
* @da: The MAC destination address
*
* Fills MAC header fields, depending on MAC type. Returns 0, If MAC type
* is a valid type and initialization completes correctly 1, otherwise.
*/
u16 lan_hdrs_init(struct sk_buff *skb, u8 *sa, u8 *da)
{
u8 *saddr;
u8 *daddr;
u16 rc = 0;
switch (skb->dev->type) {
#ifdef CONFIG_TR
case ARPHRD_IEEE802_TR: {
struct trh_hdr *trh = (struct trh_hdr *)
skb_push(skb, sizeof(*trh));
struct net_device *dev = skb->dev;
trh->ac = AC;
trh->fc = LLC_FRAME;
if (sa)
memcpy(trh->saddr, sa, dev->addr_len);
else
memset(trh->saddr, 0, dev->addr_len);
if (da) {
memcpy(trh->daddr, da, dev->addr_len);
tr_source_route(skb, trh, dev);
}
skb->mac.raw = skb->data;
break;
}
#endif
case ARPHRD_ETHER:
case ARPHRD_LOOPBACK: {
unsigned short len = skb->len;
skb->mac.raw = skb_push(skb, sizeof(struct ethhdr));
memset(skb->mac.raw, 0, sizeof(struct ethhdr));
((struct ethhdr *)skb->mac.raw)->h_proto = htons(len);
daddr = ((struct ethhdr *)skb->mac.raw)->h_dest;
saddr = ((struct ethhdr *)skb->mac.raw)->h_source;
memcpy(daddr, da, ETH_ALEN);
memcpy(saddr, sa, ETH_ALEN);
break;
}
default:
printk(KERN_WARNING "Unknown DEVICE type : %d\n",
skb->dev->type);
rc = 1;
}
return rc;
}
/**
* mac_dev_peer - search the appropriate dev to send packets to peer
* @current_dev - Current device suggested by upper layer
* @type - hardware type
* @mac - mac address
*
* Check if the we should use loopback to send packets, i.e., if the
* dmac belongs to one of the local interfaces, returning the pointer
* to the loopback &net_device struct or the current_dev if it is not
* local.
*/
struct net_device *mac_dev_peer(struct net_device *current_dev, int type,
u8 *mac)
{
struct net_device *dev;
rtnl_lock();
dev = dev_getbyhwaddr(type, mac);
if (dev)
dev = __dev_get_by_name("lo");
rtnl_unlock();
return dev ? : current_dev;
}
/*
* llc_main.c - This module contains main functions to manage station, saps
* and connections of the LLC.
*
* Copyright (c) 1997 by Procom Technology, Inc.
* 2001 by Arnaldo Carvalho de Melo <acme@conectiva.com.br>
*
* This program can be redistributed or modified under the terms of the
* GNU General Public License as published by the Free Software Foundation.
* This program is distributed without any warranty or implied warranty
* of merchantability or fitness for a particular purpose.
*
* See the GNU General Public License for more details.
*/
#include <linux/kernel.h>
#include <linux/netdevice.h>
#include <net/sock.h>
#include <linux/sched.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/proc_fs.h>
#include <net/llc_if.h>
#include <net/llc_sap.h>
#include <net/llc_conn.h>
#include <net/llc_main.h>
#include <net/llc_evnt.h>
#include <net/llc_actn.h>
#include <net/llc_stat.h>
#include <net/llc_c_ac.h>
#include <net/llc_s_ac.h>
#include <net/llc_c_ev.h>
#include <net/llc_c_st.h>
#include <net/llc_s_ev.h>
#include <net/llc_s_st.h>
#include <net/llc_mac.h>
#include <linux/llc.h>
/* static function prototypes */
static void llc_station_service_events(struct llc_station *station);
static void llc_station_free_ev(struct llc_station *station,
struct llc_station_state_ev *ev);
static void llc_station_send_pdus(struct llc_station *station);
static u16 llc_station_next_state(struct llc_station *station,
struct llc_station_state_ev *ev);
static u16 llc_exec_station_trans_actions(struct llc_station *station,
struct llc_station_state_trans *trans,
struct llc_station_state_ev *ev);
static struct llc_station_state_trans *
llc_find_station_trans(struct llc_station *station,
struct llc_station_state_ev *ev);
static int llc_rtn_all_conns(struct llc_sap *sap);
extern void llc_register_sap(unsigned char sap,
int (*rcvfunc)(struct sk_buff *skb,
struct net_device *dev,
struct packet_type *pt));
extern void llc_unregister_sap(unsigned char sap);
static struct llc_station llc_main_station; /* only one of its kind */
struct llc_prim_if_block llc_ind_prim, llc_cfm_prim;
static union llc_u_prim_data llc_ind_data_prim, llc_cfm_data_prim;
/**
* llc_sap_alloc - allocates and initializes sap.
*
* Allocates and initializes sap.
*/
struct llc_sap *llc_sap_alloc(void)
{
struct llc_sap *sap = kmalloc(sizeof(*sap), GFP_ATOMIC);
if (sap) {
memset(sap, 0, sizeof(*sap));
sap->state = LLC_SAP_STATE_ACTIVE;
memcpy(sap->laddr.mac, llc_main_station.mac_sa, ETH_ALEN);
spin_lock_init(&sap->sk_list.lock);
INIT_LIST_HEAD(&sap->sk_list.list);
skb_queue_head_init(&sap->mac_pdu_q);
}
return sap;
}
/**
* llc_free_sap - frees a sap
* @sap: Address of the sap
*
* Frees all associated connections (if any), removes this sap from
* the list of saps in te station and them frees the memory for this sap.
*/
void llc_free_sap(struct llc_sap *sap)
{
struct llc_station *station = sap->parent_station;
llc_rtn_all_conns(sap);
spin_lock_bh(&station->sap_list.lock);
list_del(&sap->node);
spin_unlock_bh(&station->sap_list.lock);
kfree(sap);
}
/**
* llc_sap_save - add sap to station list
* @sap: Address of the sap
*
* Adds a sap to the LLC's station sap list.
*/
void llc_sap_save(struct llc_sap *sap)
{
spin_lock_bh(&llc_main_station.sap_list.lock);
list_add_tail(&sap->node, &llc_main_station.sap_list.list);
spin_unlock_bh(&llc_main_station.sap_list.lock);
}
/**
* llc_sap_find - searchs a SAP in station
* @sap_value: sap to be found
*
* Searchs for a sap in the sap list of the LLC's station upon the sap ID.
* Returns the sap or %NULL if not found.
*/
struct llc_sap *llc_sap_find(u8 sap_value)
{
struct llc_sap* sap = NULL;
struct list_head *entry;
spin_lock_bh(&llc_main_station.sap_list.lock);
list_for_each(entry, &llc_main_station.sap_list.list) {
sap = list_entry(entry, struct llc_sap, node);
if (sap->laddr.lsap == sap_value)
break;
}
if (entry == &llc_main_station.sap_list.list) /* not found */
sap = NULL;
spin_unlock_bh(&llc_main_station.sap_list.lock);
return sap;
}
/**
* llc_backlog_rcv - Processes rx frames and expired timers.
* @sk: LLC sock (p8022 connection)
* @skb: queued rx frame or event
*
* This function processes frames that has received and timers that has
* expired during sending an I pdu (refer to data_req_handler). frames
* queue by mac_indicate function (llc_mac.c) and timers queue by timer
* callback functions(llc_c_ac.c).
*/
static int llc_backlog_rcv(struct sock *sk, struct sk_buff *skb)
{
int rc = 0;
struct llc_opt *llc = llc_sk(sk);
if (skb->cb[0] == LLC_PACKET) {
if (llc->state > 1) /* not closed */
rc = llc_pdu_router(llc->sap, sk, skb, LLC_TYPE_2);
else
kfree_skb(skb);
} else if (skb->cb[0] == LLC_EVENT) {
struct llc_conn_state_ev *ev =
(struct llc_conn_state_ev *)skb->data;
/* timer expiration event */
if (llc->state > 1) /* not closed */
rc = llc_conn_send_ev(sk, ev);
else
llc_conn_free_ev(ev);
kfree_skb(skb);
}
return rc;
}
/**
* llc_sock_init - Initialize a socket with default llc values.
* @sk: socket to intiailize.
*/
int llc_sock_init(struct sock* sk)
{
struct llc_opt *llc = kmalloc(sizeof(*llc), GFP_ATOMIC);
int rc = -ENOMEM;
if (!llc)
goto out;
memset(llc, 0, sizeof(*llc));
rc = 0;
llc->sk = sk;
llc->state = LLC_CONN_STATE_ADM;
llc->inc_cntr = llc->dec_cntr = 2;
llc->dec_step = llc->connect_step = 1;
llc->ack_timer.expire = LLC_ACK_TIME;
llc->pf_cycle_timer.expire = LLC_P_TIME;
llc->rej_sent_timer.expire = LLC_REJ_TIME;
llc->busy_state_timer.expire = LLC_BUSY_TIME;
llc->n2 = 2; /* max retransmit */
llc->k = 2; /* tx win size, will adjust dynam */
llc->rw = 128; /* rx win size (opt and equal to
* tx_win of remote LLC)
*/
skb_queue_head_init(&llc->pdu_unack_q);
sk->backlog_rcv = llc_backlog_rcv;
llc_sk(sk) = llc;
out:
return rc;
}
/**
* __llc_sock_alloc - Allocates LLC sock
*
* Allocates a LLC sock and initializes it. Returns the new LLC sock
* or %NULL if there's no memory available for one
*/
struct sock *__llc_sock_alloc(void)
{
struct sock *sk = sk_alloc(PF_LLC, GFP_ATOMIC, 1, NULL);
if (!sk)
goto out;
if (llc_sock_init(sk))
goto outsk;
sock_init_data(NULL, sk);
out:
return sk;
outsk:
sk_free(sk);
sk = NULL;
goto out;
}
/**
* __llc_sock_free - Frees a LLC socket
* @sk - socket to free
*
* Frees a LLC socket
*/
void __llc_sock_free(struct sock *sk, u8 free)
{
struct llc_opt *llc = llc_sk(sk);
llc->state = LLC_CONN_OUT_OF_SVC;
/* stop all (possibly) running timers */
llc_conn_ac_stop_all_timers(sk, NULL);
/* handle return of frames on lists */
printk(KERN_INFO __FUNCTION__ ": unackq=%d, txq=%d\n",
skb_queue_len(&llc->pdu_unack_q),
skb_queue_len(&sk->write_queue));
skb_queue_purge(&sk->write_queue);
skb_queue_purge(&llc->pdu_unack_q);
if (free)
sock_put(sk);
}
/**
* llc_sock_reset - resets a connection
* @sk: LLC socket to reset
*
* Resets a connection to the out of service state. Stops its timers
* and frees any frames in the queues of the connection.
*/
void llc_sock_reset(struct sock *sk)
{
struct llc_opt *llc = llc_sk(sk);
llc_conn_ac_stop_all_timers(sk, NULL);
skb_queue_purge(&sk->write_queue);
skb_queue_purge(&llc->pdu_unack_q);
llc->remote_busy_flag = 0;
llc->cause_flag = 0;
llc->retry_count = 0;
llc->p_flag = 0;
llc->f_flag = 0;
llc->s_flag = 0;
llc->ack_pf = 0;
llc->first_pdu_Ns = 0;
llc->ack_must_be_send = 0;
llc->dec_step = 1;
llc->inc_cntr = 2;
llc->dec_cntr = 2;
llc->X = 0;
llc->failed_data_req = 0 ;
llc->last_nr = 0;
}
/**
* llc_rtn_all_conns - Closes all connections of a sap
* @sap: sap to close its connections
*
* Closes all connections of a sap. Returns 0 if all actions complete
* successfully, nonzero otherwise
*/
static int llc_rtn_all_conns(struct llc_sap *sap)
{
int rc = 0;
union llc_u_prim_data prim_data;
struct llc_prim_if_block prim;
struct list_head *entry, *tmp;
spin_lock_bh(&sap->sk_list.lock);
if (list_empty(&sap->sk_list.list))
goto out;
list_for_each_safe(entry, tmp, &sap->sk_list.list) {
struct llc_opt *llc = list_entry(entry, struct llc_opt, node);
prim.sap = sap;
prim_data.disc.sk = llc->sk;
prim.prim = LLC_DISC_PRIM;
prim.data = &prim_data;
llc->state = LLC_CONN_STATE_TEMP;
if (sap->req(&prim))
rc = 1;
}
out:
spin_unlock_bh(&sap->sk_list.lock);
return rc;
}
/**
* llc_station_get - get addr of global station.
*
* Returns address of a place to copy the global station to it.
*/
struct llc_station *llc_station_get(void)
{
return &llc_main_station;
}
/**
* llc_station_alloc_ev - allocates an event
* @station: Address of the station
*
* Allocates an event in this station. Returns the allocated event on
* success, %NULL otherwise.
*/
struct llc_station_state_ev *llc_station_alloc_ev(struct llc_station *station)
{
struct llc_station_state_ev *ev = kmalloc(sizeof(*ev), GFP_ATOMIC);
if (ev)
memset(ev, 0, sizeof(*ev));
return ev;
}
/**
* llc_station_send_ev: queue event and try to process queue.
* @station: Address of the station
* @ev: Address of the event
*
* Queues an event (on the station event queue) for handling by the
* station state machine and attempts to process any queued-up events.
*/
void llc_station_send_ev(struct llc_station *station,
struct llc_station_state_ev *ev)
{
spin_lock_bh(&station->ev_q.lock);
list_add_tail(&ev->node, &station->ev_q.list);
llc_station_service_events(station);
spin_unlock_bh(&station->ev_q.lock);
}
/**
* llc_station_send_pdu - queues PDU to send
* @station: Address of the station
* @skb: Address of the PDU
*
* Queues a PDU to send to the MAC layer.
*/
void llc_station_send_pdu(struct llc_station *station, struct sk_buff *skb)
{
skb_queue_tail(&station->mac_pdu_q, skb);
llc_station_send_pdus(station);
}
/**
* llc_station_send_pdus - tries to send queued PDUs
* @station: Address of the station
*
* Tries to send any PDUs queued in the station mac_pdu_q to the MAC
* layer.
*/
static void llc_station_send_pdus(struct llc_station *station)
{
struct sk_buff *skb;
while ((skb = skb_dequeue(&station->mac_pdu_q)) != NULL) {
int rc = mac_send_pdu(skb);
kfree_skb(skb);
if (rc)
break;
}
}
/**
* llc_station_free_ev - frees an event
* @station: Address of the station
* @event: Address of the event
*
* Frees an event.
*/
static void llc_station_free_ev(struct llc_station *station,
struct llc_station_state_ev *ev)
{
struct sk_buff *skb = ev->data.pdu.skb;
if (ev->type == LLC_STATION_EV_TYPE_PDU)
kfree_skb(skb);
kfree(ev);
}
/**
* llc_station_service_events - service events in the queue
* @station: Address of the station
*
* Get an event from the station event queue (if any); attempt to service
* the event; if event serviced, get the next event (if any) on the event
* queue; if event not service, re-queue the event on the event queue and
* attempt to service the next event; when serviced all events in queue,
* finished; if don't transition to different state, just service all
* events once; if transition to new state, service all events again.
* Caller must hold station->ev_q.lock.
*/
static void llc_station_service_events(struct llc_station *station)
{
struct llc_station_state_ev *ev;
struct list_head *entry, *tmp;
list_for_each_safe(entry, tmp, &station->ev_q.list) {
ev = list_entry(entry, struct llc_station_state_ev, node);
list_del(&ev->node);
llc_station_next_state(station, ev);
}
}
/**
* llc_station_next_state - processes event and goes to the next state
* @station: Address of the station
* @ev: Address of the event
*
* Processes an event, executes any transitions related to that event and
* updates the state of the station.
*/
static u16 llc_station_next_state(struct llc_station *station,
struct llc_station_state_ev *ev)
{
u16 rc = 1;
struct llc_station_state_trans *trans;
if (station->state > LLC_NBR_STATION_STATES)
goto out;
trans = llc_find_station_trans(station, ev);
if (trans) {
/* got the state to which we next transition; perform the
* actions associated with this transition before actually
* transitioning to the next state
*/
rc = llc_exec_station_trans_actions(station, trans, ev);
if (!rc)
/* transition station to next state if all actions
* execute successfully; done; wait for next event
*/
station->state = trans->next_state;
} else
/* event not recognized in current state; re-queue it for
* processing again at a later time; return failure
*/
rc = 0;
out:
llc_station_free_ev(station, ev);
return rc;
}
/**
* llc_find_station_trans - finds transition for this event
* @station: Address of the station
* @ev: Address of the event
*
* Search thru events of the current state of the station until list
* exhausted or it's obvious that the event is not valid for the current
* state. Returns the address of the transition if cound, %NULL otherwise.
*/
static struct llc_station_state_trans *
llc_find_station_trans(struct llc_station *station,
struct llc_station_state_ev *ev)
{
int i = 0;
struct llc_station_state_trans *rc = NULL;
struct llc_station_state_trans **next_trans;
struct llc_station_state *curr_state =
&llc_station_state_table[station->state - 1];
for (next_trans = curr_state->transitions; next_trans[i]->ev; i++)
if (!next_trans[i]->ev(station, ev)) {
rc = next_trans[i];
break;
}
return rc;
}
/**
* llc_exec_station_trans_actions - executes actions for transition
* @station: Address of the station
* @trans: Address of the transition
* @ev: Address of the event that caused the transition
*
* Executes actions of a transition of the station state machine. Returns
* 0 if all actions complete successfully, nonzero otherwise.
*/
static u16 llc_exec_station_trans_actions(struct llc_station *station,
struct llc_station_state_trans *trans,
struct llc_station_state_ev *ev)
{
u16 rc = 0;
llc_station_action_t *next_action;
for (next_action = trans->ev_actions;
next_action && *next_action; next_action++)
if ((*next_action)(station, ev))
rc = 1;
return rc;
}
/**
* llc_alloc_frame - allocates sk_buff for frame
*
* Allocates an sk_buff for frame and initializes sk_buff fields.
* Returns allocated skb or %NULL when out of memory.
*/
struct sk_buff *llc_alloc_frame(void)
{
struct sk_buff *skb = alloc_skb(128, GFP_ATOMIC);
if (skb) {
skb_reserve(skb, 50);
skb->nh.raw = skb->h.raw = skb->data;
skb->protocol = htons(ETH_P_802_2);
skb->dev = dev_base->next;
skb->mac.raw = skb->head;
}
return skb;
}
static int llc_proc_get_info(char *bf, char **start, off_t offset, int length)
{
struct llc_opt *llc;
struct list_head *sap_entry, *llc_entry;
off_t begin = 0, pos = 0;
int len = 0;
spin_lock_bh(&llc_main_station.sap_list.lock);
list_for_each(sap_entry, &llc_main_station.sap_list.list) {
struct llc_sap *sap = list_entry(sap_entry, struct llc_sap,
node);
len += sprintf(bf + len, "lsap=%d\n", sap->laddr.lsap);
spin_lock_bh(&sap->sk_list.lock);
if (list_empty(&sap->sk_list.list)) {
len += sprintf(bf + len, "no connections\n");
goto unlock;
}
len += sprintf(bf + len,
"connection list:\nstate retr txwin rxwin\n");
list_for_each(llc_entry, &sap->sk_list.list) {
llc = list_entry(llc_entry, struct llc_opt, node);
len += sprintf(bf + len, " %-5d%-5d%-6d%-5d\n",
llc->state, llc->retry_count, llc->k,
llc->rw);
}
unlock:
spin_unlock_bh(&sap->sk_list.lock);
pos = begin + len;
if (pos < offset) {
len = 0; /* Keep dumping into the buffer start */
begin = pos;
}
if (pos > offset + length) /* We have dumped enough */
break;
}
spin_unlock_bh(&llc_main_station.sap_list.lock);
/* The data in question runs from begin to begin + len */
*start = bf + (offset - begin); /* Start of wanted data */
len -= (offset - begin); /* Remove unwanted header data from length */
return len;
}
static char llc_banner[] __initdata =
KERN_INFO "LLC 2.0 by Procom, 1997, Arnaldo C. Melo, 2001\n"
KERN_INFO "NET4.0 IEEE 802.2 extended support\n";
static char llc_error_msg[] __initdata =
KERN_ERR "LLC install NOT successful.\n";
static int __init llc_init(void)
{
u16 rc = 0;
struct llc_station_state_ev *ev;
printk(llc_banner);
INIT_LIST_HEAD(&llc_main_station.ev_q.list);
spin_lock_init(&llc_main_station.ev_q.lock);
INIT_LIST_HEAD(&llc_main_station.sap_list.list);
spin_lock_init(&llc_main_station.sap_list.lock);
skb_queue_head_init(&llc_main_station.mac_pdu_q);
ev = kmalloc(sizeof(*ev), GFP_ATOMIC);
if (!ev)
goto err;
memset(ev, 0, sizeof(*ev));
if(dev_base->next)
memcpy(llc_main_station.mac_sa, dev_base->next->dev_addr, ETH_ALEN);
else
memset(llc_main_station.mac_sa, 0, ETH_ALEN);
llc_main_station.ack_timer.expires = jiffies + 3 * HZ;
/* initialize the station component */
llc_register_sap(0, mac_indicate);
llc_main_station.maximum_retry = 1;
llc_main_station.state = LLC_STATION_STATE_DOWN;
ev->type = LLC_STATION_EV_TYPE_SIMPLE;
ev->data.a.ev = LLC_STATION_EV_ENABLE_WITHOUT_DUP_ADDR_CHECK;
rc = llc_station_next_state(&llc_main_station, ev);
llc_build_offset_table();
llc_ind_prim.data = &llc_ind_data_prim;
llc_cfm_prim.data = &llc_cfm_data_prim;
proc_net_create("802.2", 0, llc_proc_get_info);
llc_ui_init();
out:
return rc;
err:
printk(llc_error_msg);
rc = 1;
goto out;
}
static void __exit llc_exit(void)
{
llc_ui_exit();
llc_unregister_sap(0);
proc_net_remove("802.2");
}
module_init(llc_init);
module_exit(llc_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Procom, 1997, Arnaldo C. Melo, Jay Schullist, 2001");
MODULE_DESCRIPTION("LLC 2.0, NET4.0 IEEE 802.2 extended support");
/*
* llc_pdu.c - access to PDU internals
*
* Copyright (c) 1997 by Procom Technology, Inc.
* 2001 by Arnaldo Carvalho de Melo <acme@conectiva.com.br>
*
* This program can be redistributed or modified under the terms of the
* GNU General Public License as published by the Free Software Foundation.
* This program is distributed without any warranty or implied warranty
* of merchantability or fitness for a particular purpose.
*
* See the GNU General Public License for more details.
*/
#include <linux/netdevice.h>
#include <linux/if_tr.h>
#include <net/llc_pdu.h>
#include <net/llc_if.h>
#include <net/llc_mac.h>
#include <net/llc_main.h>
static int llc_pdu_decode_pdu_type(struct sk_buff *skb, u8 *type);
static int llc_get_llc_hdr_length(u8 pdu_type);
static u8 llc_pdu_get_pf_bit(llc_pdu_sn_t *pdu);
/**
* llc_pdu_header_init - initializes pdu header
* @skb: input skb that header must be set into it.
* @pdu_type: type of PDU (U, I or S).
* @ssap: source sap.
* @dsap: destination sap.
* @cr: command/response bit (0 or 1).
*
* This function sets DSAP, SSAP and command/Response bit in LLC header.
*/
void llc_pdu_header_init(struct sk_buff *skb, u8 pdu_type, u8 ssap,
u8 dsap, u8 cr)
{
llc_pdu_un_t *p;
skb->nh.raw = skb_push(skb, llc_get_llc_hdr_length(pdu_type));
p = (llc_pdu_un_t *)skb->nh.raw;
p->dsap = dsap;
p->ssap = ssap;
p->ssap |= cr;
}
void llc_pdu_set_cmd_rsp(struct sk_buff *skb, u8 pdu_type)
{
((llc_pdu_un_t *)skb->nh.raw)->ssap |= pdu_type;
}
/**
* pdu_set_pf_bit - sets poll/final bit in LLC header
* @pdu_frame: input frame that p/f bit must be set into it.
* @bit_value: poll/final bit (0 or 1).
*
* This function sets poll/final bit in LLC header (based on type of PDU).
* in I or S pdus, p/f bit is right bit of fourth byte in header. in U
* pdus p/f bit is fifth bit of third byte.
*/
void llc_pdu_set_pf_bit(struct sk_buff *skb, u8 bit_value)
{
u8 pdu_type;
if (llc_pdu_decode_pdu_type(skb, &pdu_type))
goto out;
switch (pdu_type) {
case LLC_PDU_TYPE_I:
case LLC_PDU_TYPE_S:
((llc_pdu_sn_t *)skb->nh.raw)->ctrl_2 =
(((llc_pdu_sn_t *)skb->nh.raw)->ctrl_2 & 0xFE) |
bit_value;
break;
case LLC_PDU_TYPE_U:
((llc_pdu_un_t *)skb->nh.raw)->ctrl_1 |=
(((llc_pdu_un_t *)skb->nh.raw)->ctrl_1 & 0xEF) |
(bit_value << 4);
break;
}
out:;
}
/**
* llc_pdu_decode_pf_bit - extracs poll/final bit from LLC header
* @skb: input skb that p/f bit must be extracted from it
* @pf_bit: poll/final bit (0 or 1)
*
* This function extracts poll/final bit from LLC header (based on type of
* PDU). In I or S pdus, p/f bit is right bit of fourth byte in header. In
* U pdus p/f bit is fifth bit of third byte.
*/
int llc_pdu_decode_pf_bit(struct sk_buff *skb, u8 *pf_bit)
{
u8 pdu_type;
int rc = llc_pdu_decode_pdu_type(skb, &pdu_type);
if (rc)
goto out;
switch (pdu_type) {
case LLC_PDU_TYPE_I:
case LLC_PDU_TYPE_S:
*pf_bit = ((llc_pdu_sn_t *)skb->nh.raw)->ctrl_2 &
LLC_S_PF_BIT_MASK;
break;
case LLC_PDU_TYPE_U:
*pf_bit = (((llc_pdu_un_t *)skb->nh.raw)->ctrl_1 &
LLC_U_PF_BIT_MASK) >> 4;
break;
}
out:
return 0;
}
/**
* llc_pdu_decode_cr_bit - extracs command response bit from LLC header
* @skb: input skb that c/r bit must be extracted from it.
* @cr_bit: command/response bit (0 or 1).
*
* This function extracts command/response bit from LLC header. this bit
* is right bit of source SAP.
*/
int llc_pdu_decode_cr_bit(struct sk_buff *skb, u8 *cr_bit)
{
*cr_bit = ((llc_pdu_un_t *)skb->nh.raw)->ssap & LLC_PDU_CMD_RSP_MASK;
return 0;
}
/**
* llc_pdu_decode_sa - extracs source address (MAC) of input frame
* @skb: input skb that source address must be extracted from it.
* @sa: pointer to source address (6 byte array).
*
* This function extracts source address(MAC) of input frame.
*/
int llc_pdu_decode_sa(struct sk_buff *skb, u8 *sa)
{
if (skb->protocol == ntohs(ETH_P_802_2))
memcpy(sa, ((struct ethhdr *)skb->mac.raw)->h_source, ETH_ALEN);
else if (skb->protocol == ntohs(ETH_P_TR_802_2))
memcpy(sa, ((struct trh_hdr *)skb->mac.raw)->saddr, ETH_ALEN);
return 0;
}
/**
* llc_pdu_decode_da - extracts dest address of input frame
* @skb: input skb that destination address must be extracted from it
* @sa: pointer to destination address (6 byte array).
*
* This function extracts destination address(MAC) of input frame.
*/
int llc_pdu_decode_da(struct sk_buff *skb, u8 *da)
{
if (skb->protocol == ntohs(ETH_P_802_2))
memcpy(da, ((struct ethhdr *)skb->mac.raw)->h_dest, ETH_ALEN);
else if (skb->protocol == ntohs(ETH_P_TR_802_2))
memcpy(da, ((struct trh_hdr *)skb->mac.raw)->daddr, ETH_ALEN);
return 0;
}
/**
* llc_pdu_decode_dsap - extracts dest SAP of input frame
* @skb: input skb that destination SAP must be extracted from it.
* @dsap: destination SAP (output argument).
*
* This function extracts destination SAP of input frame. right bit of
* DSAP designates individual/group SAP.
*/
int llc_pdu_decode_dsap(struct sk_buff *skb, u8 *dsap)
{
*dsap = ((llc_pdu_un_t *)skb->nh.raw)->dsap & 0xFE;
return 0;
}
/**
* llc_pdu_decode_ssap - extracts source SAP of input frame
* @skb: input skb that source SAP must be extracted from it.
* @ssap: source SAP (output argument).
*
* This function extracts source SAP of input frame. right bit of SSAP is
* command/response bit.
*/
int llc_pdu_decode_ssap(struct sk_buff *skb, u8 *ssap)
{
*ssap = ((llc_pdu_un_t *)skb->nh.raw)->ssap & 0xFE;
return 0;
}
/**
* llc_pdu_init_as_ui_cmd - sets LLC header as UI PDU
* @skb: input skb that header must be set into it.
*
* This function sets third byte of LLC header as a UI PDU.
*/
int llc_pdu_init_as_ui_cmd(struct sk_buff *skb)
{
llc_pdu_un_t *pdu = (llc_pdu_un_t *)skb->nh.raw;
pdu->ctrl_1 = LLC_PDU_TYPE_U;
pdu->ctrl_1 |= LLC_1_PDU_CMD_UI;
return 0;
}
/**
* llc_pdu_init_as_xid_cmd - sets bytes 3, 4 & 5 of LLC header as XID
* @skb: input skb that header must be set into it.
*
* This function sets third,fourth,fifth and sixth bytes of LLC header as
* a XID PDU.
*/
int llc_pdu_init_as_xid_cmd(struct sk_buff *skb, u8 svcs_supported,
u8 rx_window)
{
llc_xid_info_t *xid_info;
llc_pdu_un_t *pdu = (llc_pdu_un_t *)skb->nh.raw;
pdu->ctrl_1 = LLC_PDU_TYPE_U;
pdu->ctrl_1 |= LLC_1_PDU_CMD_XID;
pdu->ctrl_1 |= LLC_U_PF_BIT_MASK;
xid_info = (llc_xid_info_t *)(((u8 *)&pdu->ctrl_1) + 1);
xid_info->fmt_id = LLC_XID_FMT_ID; /* 0x81 */
xid_info->type = svcs_supported;
xid_info->rw = rx_window << 1; /* size of recieve window */
skb_put(skb, 3);
return 0;
}
/**
* llc_pdu_init_as_test_cmd - sets PDU as TEST
* @skb - Address of the skb to build
*
* Sets a PDU as TEST
*/
int llc_pdu_init_as_test_cmd(struct sk_buff *skb)
{
llc_pdu_un_t *pdu = (llc_pdu_un_t *)skb->nh.raw;
pdu->ctrl_1 = LLC_PDU_TYPE_U;
pdu->ctrl_1 |= LLC_1_PDU_CMD_TEST;
pdu->ctrl_1 |= LLC_U_PF_BIT_MASK;
return 0;
}
/**
* llc_pdu_init_as_disc_cmd - Builds DISC PDU
* @skb: Address of the skb to build
* @p_bit: The P bit to set in the PDU
*
* Builds a pdu frame as a DISC command.
*/
int llc_pdu_init_as_disc_cmd(struct sk_buff *skb, u8 p_bit)
{
llc_pdu_un_t *pdu = (llc_pdu_un_t *)skb->nh.raw;
pdu->ctrl_1 = LLC_PDU_TYPE_U;
pdu->ctrl_1 |= LLC_2_PDU_CMD_DISC;
pdu->ctrl_1 |= ((p_bit & 1) << 4) & LLC_U_PF_BIT_MASK;
return 0;
}
/**
* pdu_init_as_i_cmd - builds I pdu
* @skb: Address of the skb to build
* @p_bit: The P bit to set in the PDU
* @ns: The sequence number of the data PDU
* @nr: The seq. number of the expected I PDU from the remote
*
* Builds a pdu frame as an I command.
*/
int llc_pdu_init_as_i_cmd(struct sk_buff *skb, u8 p_bit, u8 ns, u8 nr)
{
llc_pdu_sn_t *pdu = (llc_pdu_sn_t *)skb->nh.raw;
pdu->ctrl_1 = LLC_PDU_TYPE_I;
pdu->ctrl_2 = 0;
pdu->ctrl_2 |= (p_bit & LLC_I_PF_BIT_MASK); /* p/f bit */
pdu->ctrl_1 |= (ns << 1) & 0xFE; /* set N(S) in bits 2..8 */
pdu->ctrl_2 |= (nr << 1) & 0xFE; /* set N(R) in bits 10..16 */
return 0;
}
/**
* pdu_init_as_rej_cmd - builds REJ PDU
* @skb: Address of the skb to build
* @p_bit: The P bit to set in the PDU
* @nr: The seq. number of the expected I PDU from the remote
*
* Builds a pdu frame as a REJ command.
*/
int llc_pdu_init_as_rej_cmd(struct sk_buff *skb, u8 p_bit, u8 nr)
{
llc_pdu_sn_t *pdu = (llc_pdu_sn_t *)skb->nh.raw;
pdu->ctrl_1 = LLC_PDU_TYPE_S;
pdu->ctrl_1 |= LLC_2_PDU_CMD_REJ;
pdu->ctrl_2 = 0;
pdu->ctrl_2 |= p_bit & LLC_S_PF_BIT_MASK;
pdu->ctrl_1 &= 0x0F; /* setting bits 5..8 to zero(reserved) */
pdu->ctrl_2 |= (nr << 1) & 0xFE; /* set N(R) in bits 10..16 */
return 0;
}
/**
* pdu_init_as_rnr_cmd - builds RNR pdu
* @skb: Address of the skb to build
* @p_bit: The P bit to set in the PDU
* @nr: The seq. number of the expected I PDU from the remote
*
* Builds a pdu frame as an RNR command.
*/
int llc_pdu_init_as_rnr_cmd(struct sk_buff *skb, u8 p_bit, u8 nr)
{
llc_pdu_sn_t *pdu = (llc_pdu_sn_t *)skb->nh.raw;
pdu->ctrl_1 = LLC_PDU_TYPE_S;
pdu->ctrl_1 |= LLC_2_PDU_CMD_RNR;
pdu->ctrl_2 = 0;
pdu->ctrl_2 |= p_bit & LLC_S_PF_BIT_MASK;
pdu->ctrl_1 &= 0x0F; /* setting bits 5..8 to zero(reserved) */
pdu->ctrl_2 |= (nr << 1) & 0xFE; /* set N(R) in bits 10..16 */
return 0;
}
/**
* pdu_init_as_rr_cmd - Builds RR pdu
* @skb: Address of the skb to build
* @p_bit: The P bit to set in the PDU
* @nr: The seq. number of the expected I PDU from the remote
*
* Builds a pdu frame as an RR command.
*/
int llc_pdu_init_as_rr_cmd(struct sk_buff *skb, u8 p_bit, u8 nr)
{
llc_pdu_sn_t *pdu = (llc_pdu_sn_t *)skb->nh.raw;
pdu->ctrl_1 = LLC_PDU_TYPE_S;
pdu->ctrl_1 |= LLC_2_PDU_CMD_RR;
pdu->ctrl_2 = p_bit & LLC_S_PF_BIT_MASK;
pdu->ctrl_1 &= 0x0F; /* setting bits 5..8 to zero(reserved) */
pdu->ctrl_2 |= (nr << 1) & 0xFE; /* set N(R) in bits 10..16 */
return 0;
}
/**
* pdu_init_as_sabme_cmd - builds SABME pdu
* @skb: Address of the skb to build
* @p_bit: The P bit to set in the PDU
*
* Builds a pdu frame as an SABME command.
*/
int llc_pdu_init_as_sabme_cmd(struct sk_buff *skb, u8 p_bit)
{
llc_pdu_un_t *pdu = (llc_pdu_un_t *)skb->nh.raw;
pdu->ctrl_1 = LLC_PDU_TYPE_U;
pdu->ctrl_1 |= LLC_2_PDU_CMD_SABME;
pdu->ctrl_1 |= ((p_bit & 1) << 4) & LLC_U_PF_BIT_MASK;
return 0;
}
/**
* pdu_init_as_dm_rsp - builds DM response pdu
* @skb: Address of the skb to build
* @f_bit: The F bit to set in the PDU
*
* Builds a pdu frame as a DM response.
*/
int llc_pdu_init_as_dm_rsp(struct sk_buff *skb, u8 f_bit)
{
llc_pdu_un_t *pdu = (llc_pdu_un_t *)skb->nh.raw;
pdu->ctrl_1 = LLC_PDU_TYPE_U;
pdu->ctrl_1 |= LLC_2_PDU_RSP_DM;
pdu->ctrl_1 |= ((f_bit & 1) << 4) & LLC_U_PF_BIT_MASK;
return 0;
}
/**
* pdu_init_as_xid_rsp - builds XID response PDU
* @skb: Address of the skb to build
* @svcs_supported: The class of the LLC (I or II)
* @rx_window: The size of the receive window of the LLC
*
* Builds a pdu frame as an XID response.
*/
int llc_pdu_init_as_xid_rsp(struct sk_buff *skb, u8 svcs_supported,
u8 rx_window)
{
llc_xid_info_t *xid_info;
llc_pdu_un_t *pdu = (llc_pdu_un_t *)skb->nh.raw;
pdu->ctrl_1 = LLC_PDU_TYPE_U;
pdu->ctrl_1 |= LLC_1_PDU_CMD_XID;
pdu->ctrl_1 |= LLC_U_PF_BIT_MASK;
xid_info = (llc_xid_info_t *)(((u8 *)&pdu->ctrl_1) + 1);
xid_info->fmt_id = LLC_XID_FMT_ID;
xid_info->type = svcs_supported;
xid_info->rw = rx_window << 1;
skb_put(skb, 3);
return 0;
}
/**
* pdu_init_as_test_rsp - build TEST response PDU
* @skb: Address of the skb to build
* @ev_skb: The received TEST command PDU frame
*
* Builds a pdu frame as a TEST response.
*/
int llc_pdu_init_as_test_rsp(struct sk_buff *skb, struct sk_buff *ev_skb)
{
int dsize;
llc_pdu_un_t *pdu = (llc_pdu_un_t *)skb->nh.raw;
pdu->ctrl_1 = LLC_PDU_TYPE_U;
pdu->ctrl_1 |= LLC_1_PDU_CMD_TEST;
pdu->ctrl_1 |= LLC_U_PF_BIT_MASK;
if (ev_skb->protocol == ntohs(ETH_P_802_2)) {
dsize = ntohs(((struct ethhdr *)ev_skb->mac.raw)->h_proto) - 3;
memcpy(((u8 *)skb->nh.raw) + 3,
((u8 *)ev_skb->nh.raw) + 3, dsize);
skb_put(skb, dsize);
}
return 0;
}
/**
* pdu_init_as_frmr_rsp - builds FRMR response PDU
* @pdu_frame: Address of the frame to build
* @prev_pdu: The rejected PDU frame
* @f_bit: The F bit to set in the PDU
* @vs: tx state vari value for the data link conn at the rejecting LLC
* @vr: rx state var value for the data link conn at the rejecting LLC
* @vzyxw: completely described in the IEEE Std 802.2 document (Pg 55)
*
* Builds a pdu frame as a FRMR response.
*/
int llc_pdu_init_as_frmr_rsp(struct sk_buff *skb, llc_pdu_sn_t *prev_pdu,
u8 f_bit, u8 vs, u8 vr, u8 vzyxw)
{
llc_frmr_info_t *frmr_info;
u8 prev_pf = 0;
u8 *ctrl;
llc_pdu_sn_t *pdu = (llc_pdu_sn_t *)skb->nh.raw;
pdu->ctrl_1 = LLC_PDU_TYPE_U;
pdu->ctrl_1 |= LLC_2_PDU_RSP_FRMR;
pdu->ctrl_1 |= ((f_bit & 1) << 4) & LLC_U_PF_BIT_MASK;
frmr_info = (llc_frmr_info_t *)&pdu->ctrl_2;
ctrl = (u8 *)&prev_pdu->ctrl_1;
FRMR_INFO_SET_REJ_CNTRL(frmr_info,ctrl);
FRMR_INFO_SET_Vs(frmr_info, vs);
FRMR_INFO_SET_Vr(frmr_info, vr);
prev_pf = llc_pdu_get_pf_bit(prev_pdu);
FRMR_INFO_SET_C_R_BIT(frmr_info, prev_pf);
FRMR_INFO_SET_INVALID_PDU_CTRL_IND(frmr_info, vzyxw);
FRMR_INFO_SET_INVALID_PDU_INFO_IND(frmr_info, vzyxw);
FRMR_INFO_SET_PDU_INFO_2LONG_IND(frmr_info, vzyxw);
FRMR_INFO_SET_PDU_INVALID_Nr_IND(frmr_info, vzyxw);
FRMR_INFO_SET_PDU_INVALID_Ns_IND(frmr_info, vzyxw);
skb_put(skb, 5);
return 0;
}
/**
* pdu_init_as_rr_rsp - builds RR response pdu
* @skb: Address of the skb to build
* @f_bit: The F bit to set in the PDU
* @nr: The seq. number of the expected data PDU from the remote
*
* Builds a pdu frame as an RR response.
*/
int llc_pdu_init_as_rr_rsp(struct sk_buff *skb, u8 f_bit, u8 nr)
{
llc_pdu_sn_t *pdu = (llc_pdu_sn_t *)skb->nh.raw;
pdu->ctrl_1 = LLC_PDU_TYPE_S;
pdu->ctrl_1 |= LLC_2_PDU_RSP_RR;
pdu->ctrl_2 = 0;
pdu->ctrl_2 |= f_bit & LLC_S_PF_BIT_MASK;
pdu->ctrl_1 &= 0x0F; /* setting bits 5..8 to zero(reserved) */
pdu->ctrl_2 |= (nr << 1) & 0xFE; /* set N(R) in bits 10..16 */
return 0;
}
/**
* pdu_init_as_rej_rsp - builds REJ response pdu
* @skb: Address of the skb to build
* @f_bit: The F bit to set in the PDU
* @nr: The seq. number of the expected data PDU from the remote
*
* Builds a pdu frame as a REJ response.
*/
int llc_pdu_init_as_rej_rsp(struct sk_buff *skb, u8 f_bit, u8 nr)
{
llc_pdu_sn_t *pdu = (llc_pdu_sn_t *)skb->nh.raw;
pdu->ctrl_1 = LLC_PDU_TYPE_S;
pdu->ctrl_1 |= LLC_2_PDU_RSP_REJ;
pdu->ctrl_2 = 0;
pdu->ctrl_2 |= f_bit & LLC_S_PF_BIT_MASK;
pdu->ctrl_1 &= 0x0F; /* setting bits 5..8 to zero(reserved) */
pdu->ctrl_2 |= (nr << 1) & 0xFE; /* set N(R) in bits 10..16 */
return 0;
}
/**
* pdu_init_as_rnr_rsp - builds RNR response pdu
* @pdu_frame: Address of the frame to build
* @f_bit: The F bit to set in the PDU
* @nr: The seq. number of the expected data PDU from the remote
*
* Builds a pdu frame as an RNR response.
*/
int llc_pdu_init_as_rnr_rsp(struct sk_buff *skb, u8 f_bit, u8 nr)
{
llc_pdu_sn_t *pdu = (llc_pdu_sn_t *)skb->nh.raw;
pdu->ctrl_1 = LLC_PDU_TYPE_S;
pdu->ctrl_1 |= LLC_2_PDU_RSP_RNR;
pdu->ctrl_2 = 0;
pdu->ctrl_2 |= f_bit & LLC_S_PF_BIT_MASK;
pdu->ctrl_1 &= 0x0F; /* setting bits 5..8 to zero(reserved) */
pdu->ctrl_2 |= (nr << 1) & 0xFE; /* set N(R) in bits 10..16 */
return 0;
}
/**
* pdu_init_as_ua_rsp - builds UA response pdu
* @skb: Address of the frame to build
* @f_bit: The F bit to set in the PDU
*
* Builds a pdu frame as a UA response.
*/
int llc_pdu_init_as_ua_rsp(struct sk_buff *skb, u8 f_bit)
{
llc_pdu_un_t *pdu = (llc_pdu_un_t *)skb->nh.raw;
pdu->ctrl_1 = LLC_PDU_TYPE_U;
pdu->ctrl_1 |= LLC_2_PDU_RSP_UA;
pdu->ctrl_1 |= ((f_bit & 1) << 4) & LLC_U_PF_BIT_MASK;
return 0;
}
/**
* llc_pdu_decode_pdu_type - designates PDU type
* @skb: input skb that type of it must be designated.
* @type: type of PDU (output argument).
*
* This function designates type of PDU (I,S or U).
*/
static int llc_pdu_decode_pdu_type(struct sk_buff *skb, u8 *type)
{
llc_pdu_un_t *pdu = (llc_pdu_un_t *)skb->nh.raw;
if (pdu->ctrl_1 & 1) {
if ((pdu->ctrl_1 & LLC_PDU_TYPE_U) == LLC_PDU_TYPE_U)
*type = LLC_PDU_TYPE_U;
else
*type = LLC_PDU_TYPE_S;
} else
*type = LLC_PDU_TYPE_I;
return 0;
}
/**
* llc_decode_pdu_type - designates component LLC must handle for PDU
* @skb: input skb
* @dest: destination component
*
* This function designates which component of LLC must handle this PDU.
*/
int llc_decode_pdu_type(struct sk_buff *skb, u8 *dest)
{
u8 type = LLC_DEST_CONN; /* I-PDU or S-PDU type */
llc_pdu_sn_t *pdu = (llc_pdu_sn_t *)skb->nh.raw;
if ((pdu->ctrl_1 & LLC_PDU_TYPE_MASK) != LLC_PDU_TYPE_U)
goto out;
switch (LLC_U_PDU_CMD(pdu)) {
case LLC_1_PDU_CMD_XID:
case LLC_1_PDU_CMD_UI:
case LLC_1_PDU_CMD_TEST:
type = LLC_DEST_SAP;
break;
case LLC_2_PDU_CMD_SABME:
case LLC_2_PDU_CMD_DISC:
case LLC_2_PDU_RSP_UA:
case LLC_2_PDU_RSP_DM:
case LLC_2_PDU_RSP_FRMR:
break;
default:
type = LLC_DEST_INVALID;
break;
}
out:
*dest = type;
return 0;
}
/**
* get_llc_hdr_len - designates LLC header length
* @pdu_type: type of PDU.
*
* This function designates LLC header length of PDU. header length for I
* and S PDU is 4 and for U is 3 bytes. Returns the length of header.
*/
static int llc_get_llc_hdr_length(u8 pdu_type)
{
int rtn_val = 0;
switch (pdu_type) {
case LLC_PDU_TYPE_I:
case LLC_PDU_TYPE_S:
rtn_val = 4;
break;
case LLC_PDU_TYPE_U:
rtn_val = 3;
break;
}
return rtn_val;
}
/**
* llc_pdu_get_pf_bit - extracts p/f bit of input PDU
* @pdu: pointer to LLC header.
*
* This function extracts p/f bit of input PDU. at first examines type of
* PDU and then extracts p/f bit. Returns the p/f bit.
*/
static u8 llc_pdu_get_pf_bit(llc_pdu_sn_t *pdu)
{
u8 pdu_type;
u8 pf_bit = 0;
if (pdu->ctrl_1 & 1) {
if ((pdu->ctrl_1 & LLC_PDU_TYPE_U) == LLC_PDU_TYPE_U)
pdu_type = LLC_PDU_TYPE_U;
else
pdu_type = LLC_PDU_TYPE_S;
} else
pdu_type = LLC_PDU_TYPE_I;
switch (pdu_type) {
case LLC_PDU_TYPE_I:
case LLC_PDU_TYPE_S:
pf_bit = pdu->ctrl_2 & LLC_S_PF_BIT_MASK;
break;
case LLC_PDU_TYPE_U:
pf_bit = (pdu->ctrl_1 & LLC_U_PF_BIT_MASK) >> 4;
break;
}
return pf_bit;
}
/*
* llc_s_ac.c - actions performed during sap state transition.
*
* Description :
* Functions in this module are implementation of sap component actions.
* Details of actions can be found in IEEE-802.2 standard document.
* All functions have one sap and one event as input argument. All of
* them return 0 On success and 1 otherwise.
*
* Copyright (c) 1997 by Procom Technology, Inc.
* 2001 by Arnaldo Carvalho de Melo <acme@conectiva.com.br>
*
* This program can be redistributed or modified under the terms of the
* GNU General Public License as published by the Free Software Foundation.
* This program is distributed without any warranty or implied warranty
* of merchantability or fitness for a particular purpose.
*
* See the GNU General Public License for more details.
*/
#include <linux/netdevice.h>
#include <net/llc_if.h>
#include <net/llc_sap.h>
#include <net/llc_main.h>
#include <net/llc_s_ev.h>
#include <net/llc_pdu.h>
#include <net/llc_mac.h>
/**
* llc_sap_action_unit_data_ind - forward UI PDU to network layer
* @sap: SAP
* @ev: the event to forward
*
* Received a UI PDU from MAC layer; forward to network layer as a
* UNITDATA INDICATION; verify our event is the kind we expect
*/
int llc_sap_action_unitdata_ind(struct llc_sap *sap,
struct llc_sap_state_ev *ev)
{
llc_sap_rtn_pdu(sap, ev->data.pdu.skb, ev);
return 0;
}
/**
* llc_sap_action_send_ui - sends UI PDU resp to UNITDATA REQ to MAC layer
* @sap: SAP
* @ev: the event to send
*
* Sends a UI PDU to the MAC layer in response to a UNITDATA REQUEST
* primitive from the network layer. Verifies event is a primitive type of
* event. Verify the primitive is a UNITDATA REQUEST.
*/
int llc_sap_action_send_ui(struct llc_sap *sap, struct llc_sap_state_ev *ev)
{
struct llc_prim_if_block *prim = ev->data.prim.data;
struct llc_prim_unit_data *prim_data = &prim->data->udata;
struct sk_buff *skb = prim->data->udata.skb;
int rc;
llc_pdu_header_init(skb, LLC_PDU_TYPE_U, prim_data->saddr.lsap,
prim_data->daddr.lsap, LLC_PDU_CMD);
rc = llc_pdu_init_as_ui_cmd(skb);
if (rc)
goto out;
rc = lan_hdrs_init(skb, prim_data->saddr.mac, prim_data->daddr.mac);
if (!rc)
llc_sap_send_pdu(sap, skb);
out:
return rc;
}
/**
* llc_sap_action_send_xid_c - send XID PDU as response to XID REQ
* @sap: SAP
* @ev: the event to send
*
* Send a XID command PDU to MAC layer in response to a XID REQUEST
* primitive from the network layer. Verify event is a primitive type
* event. Verify the primitive is a XID REQUEST.
*/
int llc_sap_action_send_xid_c(struct llc_sap *sap, struct llc_sap_state_ev *ev)
{
struct llc_prim_if_block *prim = ev->data.prim.data;
struct llc_prim_xid *prim_data = &prim->data->xid;
struct sk_buff *skb = prim_data->skb;
int rc;
llc_pdu_header_init(skb, LLC_PDU_TYPE_U, prim_data->saddr.lsap,
prim_data->daddr.lsap, LLC_PDU_CMD);
rc = llc_pdu_init_as_xid_cmd(skb, LLC_XID_NULL_CLASS_2, 0);
if (rc)
goto out;
rc = lan_hdrs_init(skb, prim_data->saddr.mac, prim_data->daddr.mac);
if (!rc)
llc_sap_send_pdu(sap, skb);
out:
return rc;
}
/**
* llc_sap_action_send_xid_r - send XID PDU resp to MAC for received XID
* @sap: SAP
* @ev: the event to send
*
* Send XID response PDU to MAC in response to an earlier received XID
* command PDU. Verify event is a PDU type event
*/
int llc_sap_action_send_xid_r(struct llc_sap *sap, struct llc_sap_state_ev *ev)
{
u8 mac_da[ETH_ALEN], mac_sa[ETH_ALEN], dsap;
int rc = 1;
struct sk_buff *ev_skb = ev->data.pdu.skb;
struct sk_buff *skb;
llc_pdu_decode_sa(ev_skb, mac_da);
llc_pdu_decode_da(ev_skb, mac_sa);
llc_pdu_decode_ssap(ev_skb, &dsap);
skb = llc_alloc_frame();
if (!skb)
goto out;
skb->dev = ev_skb->dev;
llc_pdu_header_init(skb, LLC_PDU_TYPE_U, sap->laddr.lsap, dsap,
LLC_PDU_RSP);
rc = llc_pdu_init_as_xid_rsp(skb, LLC_XID_NULL_CLASS_2, 0);
if (rc)
goto out;
rc = lan_hdrs_init(skb, mac_sa, mac_da);
if (!rc)
llc_sap_send_pdu(sap, skb);
out:
return rc;
}
/**
* llc_sap_action_send_test_c - send TEST PDU to MAC in resp to TEST REQ
* @sap: SAP
* @ev: the event to send
*
* Send a TEST command PDU to the MAC layer in response to a TEST REQUEST
* primitive from the network layer. Verify event is a primitive type
* event; verify the primitive is a TEST REQUEST.
*/
int llc_sap_action_send_test_c(struct llc_sap *sap, struct llc_sap_state_ev *ev)
{
struct llc_prim_if_block *prim = ev->data.prim.data;
struct llc_prim_test *prim_data = &prim->data->test;
struct sk_buff *skb = prim_data->skb;
int rc;
llc_pdu_header_init(skb, LLC_PDU_TYPE_U, prim_data->saddr.lsap,
prim_data->daddr.lsap, LLC_PDU_CMD);
rc = llc_pdu_init_as_test_cmd(skb);
if (rc)
goto out;
rc = lan_hdrs_init(skb, prim_data->saddr.mac, prim_data->daddr.mac);
if (!rc)
llc_sap_send_pdu(sap, skb);
out:
return rc;
}
int llc_sap_action_send_test_r(struct llc_sap *sap, struct llc_sap_state_ev *ev)
{
u8 mac_da[ETH_ALEN], mac_sa[ETH_ALEN], dsap;
int rc = 1;
struct sk_buff *ev_skb = ev->data.pdu.skb;
struct sk_buff *skb;
llc_pdu_decode_sa(ev_skb, mac_da);
llc_pdu_decode_da(ev_skb, mac_sa);
llc_pdu_decode_ssap(ev_skb, &dsap);
skb = llc_alloc_frame();
if (!skb)
goto out;
skb->dev = ev_skb->dev;
llc_pdu_header_init(skb, LLC_PDU_TYPE_U, sap->laddr.lsap, dsap,
LLC_PDU_RSP);
rc = llc_pdu_init_as_test_rsp(skb, ev_skb);
if (rc)
goto out;
rc = lan_hdrs_init(skb, mac_sa, mac_da);
if (!rc)
llc_sap_send_pdu(sap, skb);
out:
return rc;
}
/**
* llc_sap_action_report_status - report data link status to layer mgmt
* @sap: SAP
* @ev: the event to send
*
* Report data link status to layer management. Verify our event is the
* kind we expect.
*/
int llc_sap_action_report_status(struct llc_sap *sap,
struct llc_sap_state_ev *ev)
{
return 0;
}
/**
* llc_sap_action_xid_ind - send XID PDU resp to net layer via XID IND
* @sap: SAP
* @ev: the event to send
*
* Send a XID response PDU to the network layer via a XID INDICATION
* primitive.
*/
int llc_sap_action_xid_ind(struct llc_sap *sap, struct llc_sap_state_ev *ev)
{
llc_sap_rtn_pdu(sap, ev->data.pdu.skb, ev);
return 0;
}
/**
* llc_sap_action_test_ind - send TEST PDU to net layer via TEST IND
* @sap: SAP
* @ev: the event to send
*
* Send a TEST response PDU to the network layer via a TEST INDICATION
* primitive. Verify our event is a PDU type event.
*/
int llc_sap_action_test_ind(struct llc_sap *sap, struct llc_sap_state_ev *ev)
{
llc_sap_rtn_pdu(sap, ev->data.pdu.skb, ev);
return 0;
}
/*
* llc_s_ev.c - Defines SAP component events
*
* The followed event functions are SAP component events which are described
* in 802.2 LLC protocol standard document.
*
* Copyright (c) 1997 by Procom Technology, Inc.
* 2001 by Arnaldo Carvalho de Melo <acme@conectiva.com.br>
*
* This program can be redistributed or modified under the terms of the
* GNU General Public License as published by the Free Software Foundation.
* This program is distributed without any warranty or implied warranty
* of merchantability or fitness for a particular purpose.
*
* See the GNU General Public License for more details.
*/
#include <linux/socket.h>
#include <net/sock.h>
#include <net/llc_if.h>
#include <net/llc_s_ev.h>
#include <net/llc_pdu.h>
int llc_sap_ev_activation_req(struct llc_sap *sap, struct llc_sap_state_ev *ev)
{
return ev->type == LLC_SAP_EV_TYPE_SIMPLE &&
ev->data.a.ev == LLC_SAP_EV_ACTIVATION_REQ ? 0 : 1;
}
int llc_sap_ev_rx_ui(struct llc_sap *sap, struct llc_sap_state_ev *ev)
{
llc_pdu_un_t *pdu = (llc_pdu_un_t *)ev->data.pdu.skb->nh.raw;
return ev->type == LLC_SAP_EV_TYPE_PDU && !LLC_PDU_IS_CMD(pdu) &&
!LLC_PDU_TYPE_IS_U(pdu) &&
LLC_U_PDU_CMD(pdu) == LLC_1_PDU_CMD_UI ? 0 : 1;
}
int llc_sap_ev_unitdata_req(struct llc_sap *sap, struct llc_sap_state_ev *ev)
{
return ev->type == LLC_SAP_EV_TYPE_PRIM &&
ev->data.prim.prim == LLC_DATAUNIT_PRIM &&
ev->data.prim.type == LLC_PRIM_TYPE_REQ ? 0 : 1;
}
int llc_sap_ev_xid_req(struct llc_sap *sap, struct llc_sap_state_ev *ev)
{
return ev->type == LLC_SAP_EV_TYPE_PRIM &&
ev->data.prim.prim == LLC_XID_PRIM &&
ev->data.prim.type == LLC_PRIM_TYPE_REQ ? 0 : 1;
}
int llc_sap_ev_rx_xid_c(struct llc_sap *sap, struct llc_sap_state_ev *ev)
{
llc_pdu_un_t *pdu = (llc_pdu_un_t *)ev->data.pdu.skb->nh.raw;
return ev->type == LLC_SAP_EV_TYPE_PDU && !LLC_PDU_IS_CMD(pdu) &&
!LLC_PDU_TYPE_IS_U(pdu) &&
LLC_U_PDU_CMD(pdu) == LLC_1_PDU_CMD_XID ? 0 : 1;
}
int llc_sap_ev_rx_xid_r(struct llc_sap *sap, struct llc_sap_state_ev *ev)
{
llc_pdu_un_t *pdu = (llc_pdu_un_t *)ev->data.pdu.skb->nh.raw;
return ev->type == LLC_SAP_EV_TYPE_PDU && !LLC_PDU_IS_RSP(pdu) &&
!LLC_PDU_TYPE_IS_U(pdu) &&
LLC_U_PDU_RSP(pdu) == LLC_1_PDU_CMD_XID ? 0 : 1;
}
int llc_sap_ev_test_req(struct llc_sap *sap, struct llc_sap_state_ev *ev)
{
return ev->type == LLC_SAP_EV_TYPE_PRIM &&
ev->data.prim.prim == LLC_TEST_PRIM &&
ev->data.prim.type == LLC_PRIM_TYPE_REQ ? 0 : 1;
}
int llc_sap_ev_rx_test_c(struct llc_sap *sap, struct llc_sap_state_ev *ev)
{
llc_pdu_un_t *pdu = (llc_pdu_un_t *)ev->data.pdu.skb->nh.raw;
return ev->type == LLC_SAP_EV_TYPE_PDU && !LLC_PDU_IS_CMD(pdu) &&
!LLC_PDU_TYPE_IS_U(pdu) &&
LLC_U_PDU_CMD(pdu) == LLC_1_PDU_CMD_TEST ? 0 : 1;
}
int llc_sap_ev_rx_test_r(struct llc_sap *sap, struct llc_sap_state_ev *ev)
{
llc_pdu_un_t *pdu = (llc_pdu_un_t *)ev->data.pdu.skb->nh.raw;
return ev->type == LLC_SAP_EV_TYPE_PDU && !LLC_PDU_IS_RSP(pdu) &&
!LLC_PDU_TYPE_IS_U(pdu) &&
LLC_U_PDU_RSP(pdu) == LLC_1_PDU_CMD_TEST ? 0 : 1;
}
int llc_sap_ev_deactivation_req(struct llc_sap *sap,
struct llc_sap_state_ev *ev)
{
return ev->type == LLC_SAP_EV_TYPE_SIMPLE &&
ev->data.a.ev == LLC_SAP_EV_DEACTIVATION_REQ ? 0 : 1;
}
/*
* llc_s_st.c - Defines SAP component state machine transitions.
*
* The followed transitions are SAP component state machine transitions
* which are described in 802.2 LLC protocol standard document.
*
* Copyright (c) 1997 by Procom Technology, Inc.
* 2001 by Arnaldo Carvalho de Melo <acme@conectiva.com.br>
*
* This program can be redistributed or modified under the terms of the
* GNU General Public License as published by the Free Software Foundation.
* This program is distributed without any warranty or implied warranty
* of merchantability or fitness for a particular purpose.
*
* See the GNU General Public License for more details.
*/
#include <linux/types.h>
#include <net/llc_if.h>
#include <net/llc_s_ev.h>
#include <net/llc_s_ac.h>
#include <net/llc_s_st.h>
/* dummy last-transition indicator; common to all state transition groups
* last entry for this state
* all members are zeros, .bss zeroes it
*/
static struct llc_sap_state_trans llc_sap_state_trans_n;
/* state LLC_SAP_STATE_INACTIVE transition for
* LLC_SAP_EV_ACTIVATION_REQ event
*/
static llc_sap_action_t llc_sap_inactive_state_actions_1[] = {
[0] = llc_sap_action_report_status,
[1] = NULL,
};
static struct llc_sap_state_trans llc_sap_inactive_state_trans_1 = {
.ev = llc_sap_ev_activation_req,
.next_state = LLC_SAP_STATE_ACTIVE,
.ev_actions = llc_sap_inactive_state_actions_1,
};
/* array of pointers; one to each transition */
static struct llc_sap_state_trans *llc_sap_inactive_state_transitions[] = {
[0] = &llc_sap_inactive_state_trans_1,
[1] = &llc_sap_state_trans_n,
};
/* state LLC_SAP_STATE_ACTIVE transition for LLC_SAP_EV_RX_UI event */
static llc_sap_action_t llc_sap_active_state_actions_1[] = {
[0] = llc_sap_action_unitdata_ind,
[1] = NULL,
};
static struct llc_sap_state_trans llc_sap_active_state_trans_1 = {
.ev = llc_sap_ev_rx_ui,
.next_state = LLC_SAP_STATE_ACTIVE,
.ev_actions = llc_sap_active_state_actions_1,
};
/* state LLC_SAP_STATE_ACTIVE transition for LLC_SAP_EV_UNITDATA_REQ event */
static llc_sap_action_t llc_sap_active_state_actions_2[] = {
[0] = llc_sap_action_send_ui,
[1] = NULL,
};
static struct llc_sap_state_trans llc_sap_active_state_trans_2 = {
.ev = llc_sap_ev_unitdata_req,
.next_state = LLC_SAP_STATE_ACTIVE,
.ev_actions = llc_sap_active_state_actions_2,
};
/* state LLC_SAP_STATE_ACTIVE transition for LLC_SAP_EV_XID_REQ event */
static llc_sap_action_t llc_sap_active_state_actions_3[] = {
[0] = llc_sap_action_send_xid_c,
[1] = NULL,
};
static struct llc_sap_state_trans llc_sap_active_state_trans_3 = {
.ev = llc_sap_ev_xid_req,
.next_state = LLC_SAP_STATE_ACTIVE,
.ev_actions = llc_sap_active_state_actions_3,
};
/* state LLC_SAP_STATE_ACTIVE transition for LLC_SAP_EV_RX_XID_C event */
static llc_sap_action_t llc_sap_active_state_actions_4[] = {
[0] = llc_sap_action_send_xid_r,
[1] = NULL,
};
static struct llc_sap_state_trans llc_sap_active_state_trans_4 = {
.ev = llc_sap_ev_rx_xid_c,
.next_state = LLC_SAP_STATE_ACTIVE,
.ev_actions = llc_sap_active_state_actions_4,
};
/* state LLC_SAP_STATE_ACTIVE transition for LLC_SAP_EV_RX_XID_R event */
static llc_sap_action_t llc_sap_active_state_actions_5[] = {
[0] = llc_sap_action_xid_ind,
[1] = NULL,
};
static struct llc_sap_state_trans llc_sap_active_state_trans_5 = {
.ev = llc_sap_ev_rx_xid_r,
.next_state = LLC_SAP_STATE_ACTIVE,
.ev_actions = llc_sap_active_state_actions_5,
};
/* state LLC_SAP_STATE_ACTIVE transition for LLC_SAP_EV_TEST_REQ event */
static llc_sap_action_t llc_sap_active_state_actions_6[] = {
[0] = llc_sap_action_send_test_c,
[1] = NULL,
};
static struct llc_sap_state_trans llc_sap_active_state_trans_6 = {
.ev = llc_sap_ev_test_req,
.next_state = LLC_SAP_STATE_ACTIVE,
.ev_actions = llc_sap_active_state_actions_6,
};
/* state LLC_SAP_STATE_ACTIVE transition for LLC_SAP_EV_RX_TEST_C event */
static llc_sap_action_t llc_sap_active_state_actions_7[] = {
[0] = llc_sap_action_send_test_r,
[1] = NULL,
};
static struct llc_sap_state_trans llc_sap_active_state_trans_7 = {
.ev = llc_sap_ev_rx_test_c,
.next_state = LLC_SAP_STATE_ACTIVE,
.ev_actions = llc_sap_active_state_actions_7
};
/* state LLC_SAP_STATE_ACTIVE transition for LLC_SAP_EV_RX_TEST_R event */
static llc_sap_action_t llc_sap_active_state_actions_8[] = {
[0] = llc_sap_action_test_ind,
[1] = NULL,
};
static struct llc_sap_state_trans llc_sap_active_state_trans_8 = {
.ev = llc_sap_ev_rx_test_r,
.next_state = LLC_SAP_STATE_ACTIVE,
.ev_actions = llc_sap_active_state_actions_8,
};
/* state LLC_SAP_STATE_ACTIVE transition for
* LLC_SAP_EV_DEACTIVATION_REQ event
*/
static llc_sap_action_t llc_sap_active_state_actions_9[] = {
[0] = llc_sap_action_report_status,
[1] = NULL,
};
static struct llc_sap_state_trans llc_sap_active_state_trans_9 = {
.ev = llc_sap_ev_deactivation_req,
.next_state = LLC_SAP_STATE_INACTIVE,
.ev_actions = llc_sap_active_state_actions_9
};
/* array of pointers; one to each transition */
static struct llc_sap_state_trans *llc_sap_active_state_transitions[] = {
[0] = &llc_sap_active_state_trans_2,
[1] = &llc_sap_active_state_trans_1,
[2] = &llc_sap_active_state_trans_3,
[3] = &llc_sap_active_state_trans_4,
[4] = &llc_sap_active_state_trans_5,
[5] = &llc_sap_active_state_trans_6,
[6] = &llc_sap_active_state_trans_7,
[7] = &llc_sap_active_state_trans_8,
[8] = &llc_sap_active_state_trans_9,
[9] = &llc_sap_state_trans_n,
};
/* SAP state transition table */
struct llc_sap_state llc_sap_state_table[] = {
{
curr_state: LLC_SAP_STATE_INACTIVE,
transitions: llc_sap_inactive_state_transitions,
},
{
curr_state: LLC_SAP_STATE_ACTIVE,
transitions: llc_sap_active_state_transitions,
},
};
/*
* llc_sap.c - driver routines for SAP component.
*
* Copyright (c) 1997 by Procom Technology, Inc.
* 2001 by Arnaldo Carvalho de Melo <acme@conectiva.com.br>
*
* This program can be redistributed or modified under the terms of the
* GNU General Public License as published by the Free Software Foundation.
* This program is distributed without any warranty or implied warranty
* of merchantability or fitness for a particular purpose.
*
* See the GNU General Public License for more details.
*/
#include <linux/skbuff.h>
#include <net/llc_conn.h>
#include <net/llc_sap.h>
#include <net/llc_s_ev.h>
#include <net/llc_s_ac.h>
#include <net/llc_s_st.h>
#include <net/sock.h>
#include <net/llc_main.h>
#include <net/llc_mac.h>
#include <net/llc_pdu.h>
#include <linux/if_tr.h>
static void llc_sap_free_ev(struct llc_sap *sap, struct llc_sap_state_ev *ev);
static int llc_sap_next_state(struct llc_sap *sap, struct llc_sap_state_ev *ev);
static int llc_exec_sap_trans_actions(struct llc_sap *sap,
struct llc_sap_state_trans *trans,
struct llc_sap_state_ev *ev);
static struct llc_sap_state_trans *llc_find_sap_trans(struct llc_sap *sap,
struct llc_sap_state_ev *ev);
/**
* llc_sap_assign_sock - adds a connection to a SAP
* @sap: pointer to SAP.
* @conn: pointer to connection.
*
* This function adds a connection to connection_list of a SAP.
*/
void llc_sap_assign_sock(struct llc_sap *sap, struct sock *sk)
{
spin_lock_bh(&sap->sk_list.lock);
llc_sk(sk)->sap = sap;
list_add_tail(&llc_sk(sk)->node, &sap->sk_list.list);
sock_hold(sk);
spin_unlock_bh(&sap->sk_list.lock);
}
/**
* llc_sap_unassign_sock - removes a connection from SAP
* @sap: SAP
* @sk: pointer to connection
*
* This function removes a connection from connection_list of a SAP.
* List locking is performed by caller (rtn_all_conns).
*/
void llc_sap_unassign_sock(struct llc_sap *sap, struct sock *sk)
{
spin_lock_bh(&sap->sk_list.lock);
list_del(&llc_sk(sk)->node);
sock_put(sk);
spin_unlock_bh(&sap->sk_list.lock);
}
/**
* llc_sap_alloc_ev - allocates sap event
* @sap: pointer to SAP
* @ev: allocated event (output argument)
*
* Returns the allocated sap event or %NULL when out of memory.
*/
struct llc_sap_state_ev *llc_sap_alloc_ev(struct llc_sap *sap)
{
struct llc_sap_state_ev *ev = kmalloc(sizeof(*ev), GFP_ATOMIC);
if (ev)
memset(ev, 0, sizeof(*ev));
return ev;
}
/**
* llc_sap_send_ev - sends event to SAP state machine
* @sap: pointer to SAP
* @ev: pointer to occurred event
*
* After executing actions of the event, upper layer will be indicated
* if needed(on receiving an UI frame).
*/
void llc_sap_send_ev(struct llc_sap *sap, struct llc_sap_state_ev *ev)
{
struct llc_prim_if_block *prim;
u8 flag;
llc_sap_next_state(sap, ev);
flag = ev->ind_cfm_flag;
prim = ev->prim;
if (flag == LLC_IND) {
skb_get(ev->data.pdu.skb);
sap->ind(prim);
}
llc_sap_free_ev(sap, ev);
}
/**
* llc_sap_rtn_pdu - Informs upper layer on rx of an UI, XID or TEST pdu.
* @sap: pointer to SAP
* @skb: received pdu
* @ev: pointer to occurred event
*/
void llc_sap_rtn_pdu(struct llc_sap *sap, struct sk_buff *skb,
struct llc_sap_state_ev *ev)
{
llc_pdu_un_t *pdu;
struct llc_prim_if_block *prim = &llc_ind_prim;
union llc_u_prim_data *prim_data = llc_ind_prim.data;
u8 lfb;
llc_pdu_decode_sa(skb, prim_data->udata.saddr.mac);
llc_pdu_decode_da(skb, prim_data->udata.daddr.mac);
llc_pdu_decode_dsap(skb, &prim_data->udata.daddr.lsap);
llc_pdu_decode_ssap(skb, &prim_data->udata.saddr.lsap);
prim_data->udata.pri = 0;
prim_data->udata.skb = skb;
pdu = (llc_pdu_un_t *)skb->nh.raw;
switch (LLC_U_PDU_RSP(pdu)) {
case LLC_1_PDU_CMD_TEST:
prim->prim = LLC_TEST_PRIM;
break;
case LLC_1_PDU_CMD_XID:
prim->prim = LLC_XID_PRIM;
break;
case LLC_1_PDU_CMD_UI:
if (skb->protocol == ntohs(ETH_P_TR_802_2)) {
if (((struct trh_hdr *)skb->mac.raw)->rcf) {
lfb = ntohs(((struct trh_hdr *)
skb->mac.raw)->rcf) &
0x0070;
prim_data->udata.lfb = lfb >> 4;
} else {
lfb = 0xFF;
prim_data->udata.lfb = 0xFF;
}
}
prim->prim = LLC_DATAUNIT_PRIM;
break;
}
prim->data = prim_data;
prim->sap = sap;
ev->ind_cfm_flag = LLC_IND;
ev->prim = prim;
}
/**
* llc_sap_send_pdu - Sends a frame to MAC layer for transmition
* @sap: pointer to SAP
* @skb: pdu that must be sent
*/
void llc_sap_send_pdu(struct llc_sap *sap, struct sk_buff *skb)
{
mac_send_pdu(skb);
kfree_skb(skb);
}
/**
* llc_sap_free_ev - frees an sap event
* @sap: pointer to SAP
* @ev: released event
*/
static void llc_sap_free_ev(struct llc_sap *sap, struct llc_sap_state_ev *ev)
{
if (ev->type == LLC_SAP_EV_TYPE_PDU) {
llc_pdu_un_t *pdu = (llc_pdu_un_t *)ev->data.pdu.skb->nh.raw;
if (LLC_U_PDU_CMD(pdu) != LLC_1_PDU_CMD_UI)
kfree_skb(ev->data.pdu.skb);
}
kfree(ev);
}
/**
* llc_sap_next_state - finds transition, execs actions & change SAP state
* @sap: pointer to SAP
* @ev: happened event
*
* This function finds transition that matches with happened event, then
* executes related actions and finally changes state of SAP. It returns
* 0 on success and 1 for failure.
*/
static int llc_sap_next_state(struct llc_sap *sap, struct llc_sap_state_ev *ev)
{
int rc = 1;
struct llc_sap_state_trans *trans;
if (sap->state <= LLC_NBR_SAP_STATES) {
trans = llc_find_sap_trans(sap, ev);
if (trans) {
/* got the state to which we next transition; perform
* the actions associated with this transition before
* actually transitioning to the next state
*/
rc = llc_exec_sap_trans_actions(sap, trans, ev);
if (!rc)
/* transition SAP to next state if all actions
* execute successfully
*/
sap->state = trans->next_state;
}
}
return rc;
}
/**
* llc_find_sap_trans - finds transition for event
* @sap: pointer to SAP
* @ev: happened event
*
* This function finds transition that matches with happened event.
* Returns the pointer to found transition on success or %NULL for
* failure.
*/
static struct llc_sap_state_trans *llc_find_sap_trans(struct llc_sap *sap,
struct llc_sap_state_ev* ev)
{
int i = 0;
struct llc_sap_state_trans *rc = NULL;
struct llc_sap_state_trans **next_trans;
struct llc_sap_state *curr_state = &llc_sap_state_table[sap->state - 1];
/* search thru events for this state until list exhausted or until
* its obvious the event is not valid for the current state
*/
for (next_trans = curr_state->transitions; next_trans [i]->ev; i++)
if (!next_trans[i]->ev(sap, ev)) {
/* got event match; return it */
rc = next_trans[i];
break;
}
return rc;
}
/**
* llc_exec_sap_trans_actions - execute actions related to event
* @sap: pointer to SAP
* @trans: pointer to transition that it's actions must be performed
* @ev: happened event.
*
* This function executes actions that is related to happened event.
* Returns 0 for success and 1 for failure of at least one action.
*/
static int llc_exec_sap_trans_actions(struct llc_sap *sap,
struct llc_sap_state_trans *trans,
struct llc_sap_state_ev *ev)
{
int rc = 0;
llc_sap_action_t *next_action;
for (next_action = trans->ev_actions;
next_action && *next_action; next_action++)
if ((*next_action)(sap, ev))
rc = 1;
return rc;
}
/*
* llc_sock.c - LLC User Interface SAPs
* Description:
* Functions in this module are implementation of socket based llc
* communications for the Linux operating system. Support of llc class
* one and class two is provided via SOCK_DGRAM and SOCK_STREAM
* respectively.
*
* An llc2 connection is (mac + sap), only one llc2 sap connection
* is allowed per mac. Though one sap may have multiple mac + sap
* connections.
*
* Copyright (c) 2001 by Jay Schulist <jschlst@samba.org>
*
* This program can be redistributed or modified under the terms of the
* GNU General Public License as published by the Free Software Foundation.
* This program is distributed without any warranty or implied warranty
* of merchantability or fitness for a particular purpose.
*
* See the GNU General Public License for more details.
*/
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/socket.h>
#include <linux/sockios.h>
#include <asm/uaccess.h>
#include <asm/ioctls.h>
#include <linux/proc_fs.h>
#include <linux/in.h>
#include <linux/tcp.h>
#include <linux/netdevice.h>
#include <linux/inetdevice.h>
#include <linux/errno.h>
#include <net/sock.h>
#include <net/llc_if.h>
#include <net/llc_sap.h>
#include <net/llc_pdu.h>
#include <net/llc_conn.h>
#include <linux/llc.h>
#include <linux/if_arp.h>
#include <linux/rtnetlink.h>
#include <linux/init.h>
#define dprintk(format, a...) printk(KERN_INFO __FUNCTION__ ": " format, ##a)
/* remember: uninitialized global data is zeroed because its in .bss */
static u16 llc_ui_sap_last_autoport = LLC_SAP_DYN_START;
static u16 llc_ui_sap_link_no_max[256];
static u8 llc_ui_addrany[IFHWADDRLEN];
static struct sockaddr_llc llc_ui_addrnull;
static struct proto_ops llc_ui_ops;
static struct sock *llc_ui_sockets;
static rwlock_t llc_ui_sockets_lock = RW_LOCK_UNLOCKED;
static int llc_ui_indicate(struct llc_prim_if_block *prim);
static int llc_ui_confirm(struct llc_prim_if_block *prim);
static int llc_ui_wait_for_conn(struct sock *sk, int seconds);
static int llc_ui_wait_for_disc(struct sock *sk, int seconds);
/**
* llc_ui_next_link_no - return the next unused link number for a sap
* @sap: Address of sap to get link number from.
*
* Return the next unused link number for a given sap.
*/
static inline u16 llc_ui_next_link_no(int sap)
{
return llc_ui_sap_link_no_max[sap]++;
}
/**
* llc_ui_mac_match - determines if two mac addresses are the same
* @mac1: First mac address to compare.
* @mac2: Second mac address to compare.
*
* Determines if two given mac address are the same. Returns 0 if there
* is not a complete match up to len, 1 if a complete match up to len is
* found.
*/
static inline u8 llc_ui_mac_match(u8 *mac1, u8 *mac2)
{
return !memcmp(mac1, mac2, IFHWADDRLEN);
}
/**
* llc_ui_mac_null - determines if a address is a null mac address
* @mac: Mac address to test if null.
*
* Determines if a given address is a null mac address. Returns 0 if the
* address is not a null mac, 1 if the address is a null mac.
*/
static inline u8 llc_ui_mac_null(u8 *mac)
{
return !memcmp(mac, llc_ui_addrany, IFHWADDRLEN);
}
/**
* llc_ui_addr_null - determines if a address structure is null
* @addr: Address to test if null.
*/
static inline u8 llc_ui_addr_null(struct sockaddr_llc *addr)
{
return !memcmp(addr, &llc_ui_addrnull, sizeof(*addr));
}
/**
* llc_ui_protocol_type - return eth protocol for ARP header type
* @arphrd: ARP header type.
*
* Given an ARP header type return the corresponding ethernet protocol.
* Returns 0 if ARP header type not supported or the corresponding
* ethernet protocol type.
*/
static inline u16 llc_ui_protocol_type(u16 arphrd)
{
u16 rc = htons(ETH_P_802_2);
if (arphrd == ARPHRD_IEEE802_TR)
rc = htons(ETH_P_TR_802_2);
return rc;
}
/**
* llc_ui_header_len - return length of llc header based on operation
* @sk: Socket which contains a valid llc socket type.
* @addr: Complete sockaddr_llc structure received from the user.
*
* Provide the length of the llc header depending on what kind of
* operation the user would like to perform and the type of socket.
* Returns the correct llc header length.
*/
static inline u8 llc_ui_header_len(struct sock *sk, struct sockaddr_llc *addr)
{
u8 rc = LLC_PDU_LEN_U;
if (addr->sllc_test || addr->sllc_xid)
rc = LLC_PDU_LEN_U;
else if (sk->type == SOCK_STREAM)
rc = LLC_PDU_LEN_I;
return rc;
}
/**
* llc_ui_send_conn - send connect command for new llc2 connection
* @sap : Sap the socket is bound to.
* @addr: Source and destination fields provided by the user.
* @dev : Device which this connection should use.
* @link: Link number to assign to this connection.
*
* Send a connect command to the llc layer for a new llc2 connection.
* Returns 0 upon success, non-zero if action didn't succeed.
*/
static int llc_ui_send_conn(struct sock *sk, struct llc_sap *sap,
struct sockaddr_llc *addr,
struct net_device *dev, int link)
{
struct llc_ui_opt *llc_ui = llc_ui_sk(sk);
union llc_u_prim_data prim_data;
struct llc_prim_if_block prim;
prim.data = &prim_data;
prim.sap = sap;
prim.prim = LLC_CONN_PRIM;
prim_data.conn.dev = dev;
prim_data.conn.link = link;
prim_data.conn.sk = NULL;
prim_data.conn.handler = sk;
prim_data.conn.pri = 0;
prim_data.conn.saddr.lsap = llc_ui->addr.sllc_ssap;
prim_data.conn.daddr.lsap = addr->sllc_dsap;
memcpy(prim_data.conn.saddr.mac, dev->dev_addr, IFHWADDRLEN);
memcpy(prim_data.conn.daddr.mac, addr->sllc_dmac, IFHWADDRLEN);
return sap->req(&prim);
}
/**
* llc_ui_send_disc - send disc command to llc layer
* @sk: Socket with valid llc information.
*
* Send a disconnect command to the llc layer for an established
* llc2 connection.
* Returns 0 upon success, non-zero if action did not succeed.
*/
static int llc_ui_send_disc(struct sock *sk)
{
struct llc_ui_opt *llc_ui = llc_ui_sk(sk);
union llc_u_prim_data prim_data;
struct llc_prim_if_block prim;
int rc = 0;
if (sk->type != SOCK_STREAM || sk->state != TCP_ESTABLISHED)
goto out;
sk->state = TCP_CLOSING;
prim.data = &prim_data;
prim.sap = llc_ui->sap;
prim.prim = LLC_DISC_PRIM;
prim_data.disc.sk = llc_ui->core_sk;
prim_data.disc.link = llc_ui->link;
rc = llc_ui->sap->req(&prim);
out:
return rc;
}
/**
* llc_ui_send_data - send data via reliable llc2 connection
* @sap: Sap the socket is bound to.
* @sk: Connection the socket is using.
* @skb: Data the user wishes to send.
* @addr: Source and destination fields provided by the user.
*
* Send data via reliable llc2 connection.
* Returns 0 upon success, non-zero if action did not succeed.
*/
static int llc_ui_send_data(struct llc_sap *sap, struct sock* sk,
struct sk_buff *skb, struct sockaddr_llc *addr)
{
union llc_u_prim_data prim_data;
struct llc_prim_if_block prim;
struct llc_ui_opt* llc_ui = llc_ui_sk(sk);
struct llc_opt* llc_core = llc_sk(llc_ui->core_sk);
int rc;
prim.data = &prim_data;
prim.sap = sap;
prim.prim = LLC_DATA_PRIM;
prim_data.data.skb = skb;
prim_data.data.pri = 0;
prim_data.data.sk = llc_ui->core_sk;
skb->protocol = llc_ui_protocol_type(addr->sllc_arphrd);
sock_hold(sk);
try:
rc = sap->req(&prim);
if (rc != -EBUSY)
goto out;
rc = wait_event_interruptible(sk->socket->wait, !llc_ui->core_sk ||
!llc_core->failed_data_req);
if (!rc)
goto try;
if (!llc_ui->core_sk)
rc = -ENOTCONN;
out:
sock_put(sk);
return rc;
}
/**
* llc_ui_send_llc1 - send llc1 prim data block to llc layer.
* @sap : Sap the socket is bound to.
* @skb : Data the user wishes to send.
* @addr : Source and destination fields provided by the user.
* @primitive: Action the llc layer should perform.
*
* Send an llc1 primitive data block to the llc layer for processing.
* This function is used for test, xid and unit_data messages.
* Returns 0 upon success, non-zero if action did not succeed.
*/
static int llc_ui_send_llc1(struct llc_sap *sap, struct sk_buff *skb,
struct sockaddr_llc *addr, int primitive)
{
union llc_u_prim_data prim_data;
struct llc_prim_if_block prim;
prim.data = &prim_data;
prim.sap = sap;
prim.prim = primitive;
prim_data.test.skb = skb;
prim_data.test.saddr.lsap = sap->laddr.lsap;
prim_data.test.daddr.lsap = addr->sllc_dsap;
skb->protocol = llc_ui_protocol_type(addr->sllc_arphrd);
memcpy(prim_data.test.saddr.mac, skb->dev->dev_addr, IFHWADDRLEN);
memcpy(prim_data.test.daddr.mac, addr->sllc_dmac, IFHWADDRLEN);
return sap->req(&prim);
}
/**
* llc_ui_find_sap - returns sap struct that matches sap number specified
* @sap: Sap number to search for.
*
* Search the local socket list and return the first instance of the sap
* structure which matches the sap number the user specified.
* Returns llc_sap upon match, %NULL otherwise.
*/
static inline struct llc_sap *llc_ui_find_sap(u8 sap)
{
struct sock *sk;
struct llc_sap *s = NULL;
read_lock_bh(&llc_ui_sockets_lock);
for (sk = llc_ui_sockets; sk; sk = sk->next) {
struct llc_ui_opt *llc_ui = llc_ui_sk(sk);
if (!llc_ui->sap)
continue;
if (llc_ui->sap->laddr.lsap == sap) {
s = llc_ui->sap;
break;
}
}
read_unlock_bh(&llc_ui_sockets_lock);
return s;
}
static struct sock *__llc_ui_find_sk_by_exact(struct llc_addr *laddr,
struct llc_addr *daddr)
{
struct sock *sk;
for (sk = llc_ui_sockets; sk; sk = sk->next) {
struct llc_ui_opt *llc_ui = llc_ui_sk(sk);
if (llc_ui->addr.sllc_ssap == laddr->lsap &&
llc_ui->addr.sllc_dsap == daddr->lsap &&
llc_ui_mac_null(llc_ui->addr.sllc_mmac) &&
llc_ui_mac_match(llc_ui->addr.sllc_smac, laddr->mac) &&
llc_ui_mac_match(llc_ui->addr.sllc_dmac, daddr->mac))
break;
}
return sk;
}
/**
* __llc_ui_find_sk_by_addr - return socket matching local mac + sap.
* @addr: Local address to match.
*
* Search the local socket list and return the socket which has a matching
* local (mac + sap) address (allows null mac). This search will work on
* unconnected and connected sockets, though find_by_link_no is recommend
* for connected sockets.
* Returns sock upon match, %NULL otherwise.
*/
static struct sock *__llc_ui_find_sk_by_addr(struct llc_addr *laddr,
struct llc_addr *daddr,
struct net_device *dev)
{
struct sock *sk, *tmp_sk;
for (sk = llc_ui_sockets; sk; sk = sk->next) {
struct llc_ui_opt *llc_ui = llc_ui_sk(sk);
if (llc_ui->addr.sllc_ssap != laddr->lsap)
continue;
if (llc_ui_mac_null(llc_ui->addr.sllc_smac)) {
if (!llc_ui_mac_null(llc_ui->addr.sllc_mmac) &&
!llc_ui_mac_match(llc_ui->addr.sllc_mmac,
laddr->mac))
continue;
break;
}
if (dev && !llc_ui_mac_null(llc_ui->addr.sllc_mmac) &&
llc_ui_mac_match(llc_ui->addr.sllc_mmac, laddr->mac) &&
llc_ui_mac_match(llc_ui->addr.sllc_smac, dev->dev_addr))
break;
if (dev->flags & IFF_LOOPBACK)
break;
if (!llc_ui_mac_match(llc_ui->addr.sllc_smac, laddr->mac))
continue;
tmp_sk = __llc_ui_find_sk_by_exact(laddr, daddr);
if (tmp_sk) {
sk = tmp_sk;
break;
}
if (llc_ui_mac_null(llc_ui->addr.sllc_dmac))
break;
}
return sk;
}
static struct sock *llc_ui_find_sk_by_addr(struct llc_addr *addr,
struct llc_addr *daddr,
struct net_device *dev)
{
struct sock *sk;
read_lock(&llc_ui_sockets_lock);
sk = __llc_ui_find_sk_by_addr(addr, daddr, dev);
if (sk)
sock_hold(sk);
read_unlock(&llc_ui_sockets_lock);
return sk;
}
static struct sock *llc_ui_bh_find_sk_by_addr(struct llc_addr *addr,
struct llc_addr *daddr,
struct net_device *dev)
{
struct sock *sk;
read_lock_bh(&llc_ui_sockets_lock);
sk = __llc_ui_find_sk_by_addr(addr, daddr, dev);
if (sk)
sock_hold(sk);
read_unlock_bh(&llc_ui_sockets_lock);
return sk;
}
/**
* llc_ui_insert_socket - insert socket into list
* @sk: Socket to insert.
*
* Insert a socket into the local llc socket list.
*/
static inline void llc_ui_insert_socket(struct sock *sk)
{
write_lock_bh(&llc_ui_sockets_lock);
sk->next = llc_ui_sockets;
if (sk->next)
llc_ui_sockets->pprev = &sk->next;
llc_ui_sockets = sk;
sk->pprev = &llc_ui_sockets;
sock_hold(sk);
write_unlock_bh(&llc_ui_sockets_lock);
}
/**
* llc_ui_remove_socket - remove socket from list
* @sk: Socket to remove.
*
* Remove a socket from the local llc socket list.
*/
static inline void llc_ui_remove_socket(struct sock *sk)
{
write_lock_bh(&llc_ui_sockets_lock);
if (sk->pprev) {
if (sk->next)
sk->next->pprev = sk->pprev;
*sk->pprev = sk->next;
sk->pprev = NULL;
/* this only makes sense if the socket was inserted on the
* list, if sk->pprev is NULL it wasn't
*/
sock_put(sk);
}
write_unlock_bh(&llc_ui_sockets_lock);
}
/**
* llc_ui_destroy_sk - destroy socket
* @data: Socket which is to be destroyed.
*
* Really destroy the socket.
*/
static void llc_ui_destroy_sk(struct sock *sk)
{
skb_queue_purge(&sk->receive_queue);
skb_queue_purge(&sk->write_queue);
sock_put(sk);
MOD_DEC_USE_COUNT;
}
/**
* llc_ui_destroy_timer - try to destroy socket again
* @data: Socket which is to be destroyed.
*
* Attempt to destroy a socket which was previously destroyed but
* was still in use at the time.
*/
static void llc_ui_destroy_timer(unsigned long data)
{
struct sock *sk = (struct sock *)data;
if (!atomic_read(&sk->wmem_alloc) &&
!atomic_read(&sk->rmem_alloc) && sk->dead)
llc_ui_destroy_sk(sk);
else {
sk->timer.expires = jiffies + SOCK_DESTROY_TIME;
add_timer(&sk->timer);
}
}
/**
* llc_ui_create - alloc and init a new llc_ui socket
* @sock: Socket to initialize and attach allocated sk to.
* @protocol: Unused.
*
* Allocate and initialize a new llc_ui socket, validate the user wants a
* socket type we have available.
* Returns 0 upon success, negative upon failure.
*/
static int llc_ui_create(struct socket *sock, int protocol)
{
struct sock *sk;
struct llc_ui_opt *llc_ui;
int rc = -ESOCKTNOSUPPORT;
MOD_INC_USE_COUNT;
if (sock->type != SOCK_DGRAM && sock->type != SOCK_STREAM)
goto decmod;
rc = -ENOMEM;
sk = sk_alloc(PF_LLC, GFP_KERNEL, 1, NULL);
if (!sk)
goto decmod;
llc_ui = kmalloc(sizeof(*llc_ui), GFP_KERNEL);
if (!llc_ui)
goto outsk;
memset(llc_ui, 0, sizeof(*llc_ui));
rc = 0;
sock_init_data(sock, sk);
llc_ui_sk(sk) = llc_ui;
sock->ops = &llc_ui_ops;
out:
return rc;
outsk:
sk_free(sk);
decmod:
MOD_DEC_USE_COUNT;
goto out;
}
/**
* llc_ui_release - shutdown socket
* @sock: Socket to release.
*
* Shutdown and deallocate an existing socket.
*/
static int llc_ui_release(struct socket *sock)
{
struct sock *sk = sock->sk;
struct llc_ui_opt *llc_ui;
if (!sk)
goto out;
llc_ui = llc_ui_sk(sk);
if (llc_ui->core_sk && !llc_ui_send_disc(sk))
llc_ui_wait_for_disc(sk, 255);
llc_ui_remove_socket(sk);
if (llc_ui->sap && !llc_ui_find_sap(llc_ui->sap->laddr.lsap))
llc_sap_close(llc_ui->sap);
dprintk("rxq=%d, txq=%d\n", skb_queue_len(&sk->receive_queue),
skb_queue_len(&sk->write_queue));
sock_orphan(sk);
sock->sk = NULL;
if (!atomic_read(&sk->wmem_alloc) &&
!atomic_read(&sk->rmem_alloc) && sk->dead)
llc_ui_destroy_sk(sk);
else {
init_timer(&sk->timer);
sk->timer.expires = jiffies + SOCK_DESTROY_TIME;
sk->timer.function = llc_ui_destroy_timer;
sk->timer.data = (unsigned long)sk;
add_timer(&sk->timer);
}
out:
return 0;
}
/**
* llc_ui_autoport - provide dynamicly allocate SAP number
*
* Provide the caller with a dynamicly allocated SAP number according
* to the rules that are set in this function. Returns: 0, upon failure,
* SAP number otherwise.
*/
static int llc_ui_autoport(void)
{
struct llc_sap *sap;
int i, tries = 0;
while (tries < LLC_SAP_DYN_TRIES) {
for (i = llc_ui_sap_last_autoport;
i < LLC_SAP_DYN_STOP; i += 2) {
sap = llc_ui_find_sap(i);
if (!sap) {
llc_ui_sap_last_autoport = i + 2;
goto out;
}
}
llc_ui_sap_last_autoport = LLC_SAP_DYN_START;
tries++;
}
i = 0;
out:
return i;
}
/**
* llc_ui_autobind - Bind a socket to a specific address.
* @sk: Socket to bind an address to.
* @addr: Address the user wants the socket bound to.
*
* Bind a socket to a specific address. For llc a user is able to bind to
* a specific sap only or mac + sap. If the user only specifies a sap and
* a null dmac (all zeros) the user is attempting to bind to an entire
* sap. This will stop anyone else on the local system from using that
* sap. If someone else has a mac + sap open the bind to null + sap will
* fail.
* If the user desires to bind to a specific mac + sap, it is possible to
* have multiple sap connections via multiple macs.
* Bind and autobind for that matter must enforce the correct sap usage
* otherwise all hell will break loose.
* Returns: 0 upon success, negative otherwise.
*/
static int llc_ui_autobind(struct socket *sock, struct sockaddr_llc *addr)
{
struct sock *sk = sock->sk;
struct llc_ui_opt *llc_ui = llc_ui_sk(sk);
struct llc_sap *sap;
struct net_device *dev = NULL;
int rc = -EINVAL;
if (!sk->zapped)
goto out;
/* bind to a specific mac, optional. */
if (!llc_ui_mac_null(addr->sllc_smac)) {
rtnl_lock();
dev = dev_getbyhwaddr(addr->sllc_arphrd, addr->sllc_smac);
rtnl_unlock();
rc = -ENETUNREACH;
if (!dev)
goto out;
llc_ui->dev = dev;
}
/* bind to a specific sap, optional. */
if (!addr->sllc_ssap) {
rc = -EUSERS;
addr->sllc_ssap = llc_ui_autoport();
if (!addr->sllc_ssap)
goto out;
}
sap = llc_ui_find_sap(addr->sllc_ssap);
if (!sap) {
sap = llc_sap_open(llc_ui_indicate, llc_ui_confirm,
addr->sllc_ssap);
rc = -EBUSY; /* some other network layer is using the sap */
if (!sap)
goto out;
} else {
struct llc_addr laddr, daddr;
struct sock *ask;
rc = -EUSERS; /* can't get exclusive use of sap */
if (!dev && llc_ui_mac_null(addr->sllc_mmac))
goto out;
memset(&laddr, 0, sizeof(laddr));
memset(&daddr, 0, sizeof(daddr));
if (!llc_ui_mac_null(addr->sllc_mmac)) {
if (sk->type != SOCK_DGRAM) {
rc = -EOPNOTSUPP;
goto out;
}
memcpy(laddr.mac, addr->sllc_mmac, IFHWADDRLEN);
} else
memcpy(laddr.mac, addr->sllc_smac, IFHWADDRLEN);
laddr.lsap = addr->sllc_ssap;
rc = -EADDRINUSE; /* mac + sap clash. */
ask = llc_ui_bh_find_sk_by_addr(&laddr, &daddr, dev);
if (ask) {
sock_put(ask);
goto out;
}
}
memcpy(&llc_ui->addr, addr, sizeof(*addr));
llc_ui->sap = sap;
rc = sk->zapped = 0;
llc_ui_insert_socket(sk);
out:
return rc;
}
/**
* llc_ui_bind - bind a socket to a specific address.
* @sock: Socket to bind an address to.
* @uaddr: Address the user wants the socket bound to.
* @addrlen: Length of the uaddr structure.
*
* Bind a socket to a specific address. For llc a user is able to bind to
* a specific sap only or mac + sap. If the user only specifies a sap and
* a null dmac (all zeros) the user is attempting to bind to an entire
* sap. This will stop anyone else on the local system from using that
* sap. If someone else has a mac + sap open the bind to null + sap will
* fail.
* If the user desires to bind to a specific mac + sap, it is possible to
* have multiple sap connections via multiple macs.
* Bind and autobind for that matter must enforce the correct sap usage
* otherwise all hell will break loose.
* Returns: 0 upon success, negative otherwise.
*/
static int llc_ui_bind(struct socket *sock, struct sockaddr *uaddr, int addrlen)
{
struct sockaddr_llc *addr = (struct sockaddr_llc *)uaddr;
struct sock *sk = sock->sk;
int rc = -EINVAL;
if (!sk->zapped || addrlen != sizeof(*addr))
goto out;
rc = -EAFNOSUPPORT;
if (addr->sllc_family != AF_LLC)
goto out;
/* use autobind, to avoid code replication. */
rc = llc_ui_autobind(sock, addr);
out:
return rc;
}
/**
* llc_ui_shutdown - shutdown a connect llc2 socket.
* @sock: Socket to shutdown.
* @how: What part of the socket to shutdown.
*
* Shutdown a connected llc2 socket. Currently this function only supports
* shutting down both sends and receives (2), we could probably make this
* function such that a user can shutdown only half the connection but not
* right now.
* Returns: 0 upon success, negative otherwise.
*/
static int llc_ui_shutdown(struct socket *sock, int how)
{
struct sock *sk = sock->sk;
int rc = -ENOTCONN;
lock_sock(sk);
if (sk->state != TCP_ESTABLISHED)
goto out;
rc = -EINVAL;
if (how != 2)
goto out;
rc = llc_ui_send_disc(sk);
if (!rc)
llc_ui_wait_for_disc(sk, 255);
/* Wake up anyone sleeping in poll */
sk->state_change(sk);
out:
release_sock(sk);
return rc;
}
/**
* llc_ui_connect - Connect to a remote llc2 mac + sap.
* @sock: Socket which will be connected to the remote destination.
* @uaddr: Remote and possibly the local address of the new connection.
* @addrlen: Size of uaddr structure.
* @flags: Operational flags specified by the user.
*
* Connect to a remote llc2 mac + sap. The caller must specify the
* destination mac and address to connect to. If the user previously
* called bind(2) with a smac the user does not need to specify the source
* address and mac.
* This function will autobind if user did not previously call bind.
* Returns: 0 upon success, negative otherwise.
*/
static int llc_ui_connect(struct socket *sock, struct sockaddr *uaddr,
int addrlen, int flags)
{
struct sock *sk = sock->sk;
struct llc_ui_opt *llc_ui = llc_ui_sk(sk);
struct sockaddr_llc *addr = (struct sockaddr_llc *)uaddr;
struct net_device *dev;
int rc = -EINVAL;
lock_sock(sk);
if (addrlen != sizeof(*addr))
goto out;
rc = -EAFNOSUPPORT;
if (addr->sllc_family != AF_LLC)
goto out;
/* bind connection to sap if user hasn't done it. */
if (sk->zapped) {
/* bind to sap with null dev, exclusive */
rc = llc_ui_autobind(sock, addr);
if (rc)
goto out;
}
if (!llc_ui->dev) {
rtnl_lock();
dev = dev_getbyhwaddr(addr->sllc_arphrd, addr->sllc_smac);
rtnl_unlock();
if (!dev)
goto out;
} else
dev = llc_ui->dev;
if (sk->type != SOCK_STREAM)
goto out;
rc = -EALREADY;
if (sock->state == SS_CONNECTING)
goto out;
sock->state = SS_CONNECTING;
sk->state = TCP_SYN_SENT;
llc_ui->link = llc_ui_next_link_no(llc_ui->sap->laddr.lsap);
rc = llc_ui_send_conn(sk, llc_ui->sap, addr, dev, llc_ui->link);
if (rc) {
sock->state = SS_UNCONNECTED;
sk->state = TCP_CLOSE;
goto out;
}
rc = llc_ui_wait_for_conn(sk, 255);
out:
release_sock(sk);
return rc;
}
/**
* llc_ui_listen - allow a normal socket to accept incoming connections
* @sock: Socket to allow incoming connections on.
* @backlog: Number of connections to queue.
*
* Allow a normal socket to accept incoming connections.
* Returns 0 upon success, negative otherwise.
*/
static int llc_ui_listen(struct socket *sock, int backlog)
{
struct sock *sk = sock->sk;
int rc = -EINVAL;
lock_sock(sk);
if (sock->state != SS_UNCONNECTED)
goto out;
rc = -EOPNOTSUPP;
if (sk->type != SOCK_STREAM && sk->type != SOCK_SEQPACKET)
goto out;
rc = -EAGAIN;
if (sk->zapped)
goto out;
rc = 0;
if (!(unsigned)backlog) /* BSDism */
backlog = 1;
if ((unsigned)backlog > SOMAXCONN)
backlog = SOMAXCONN;
sk->max_ack_backlog = backlog;
if (sk->state != TCP_LISTEN) {
sk->ack_backlog = 0;
sk->state = TCP_LISTEN;
}
sk->socket->flags |= __SO_ACCEPTCON;
out:
release_sock(sk);
return rc;
}
static int llc_ui_wait_for_disc(struct sock *sk, int seconds)
{
DECLARE_WAITQUEUE(wait, current);
int rc, timeout = seconds * HZ;
add_wait_queue_exclusive(sk->sleep, &wait);
for (;;) {
__set_current_state(TASK_INTERRUPTIBLE);
rc = 0;
if (sk->state != TCP_CLOSE)
timeout = schedule_timeout(timeout);
else
break;
rc = -ERESTARTSYS;
if (signal_pending(current))
break;
rc = -EAGAIN;
if (!timeout)
break;
}
__set_current_state(TASK_RUNNING);
remove_wait_queue(sk->sleep, &wait);
return rc;
}
static int llc_ui_wait_for_conn(struct sock *sk, int seconds)
{
struct llc_ui_opt *llc_ui = llc_ui_sk(sk);
DECLARE_WAITQUEUE(wait, current);
int rc, timeout = seconds * HZ;
add_wait_queue_exclusive(sk->sleep, &wait);
for (;;) {
__set_current_state(TASK_INTERRUPTIBLE);
rc = 0;
if (sk->state != TCP_ESTABLISHED)
timeout = schedule_timeout(timeout);
if (sk->state == TCP_ESTABLISHED) {
if (!llc_ui->core_sk)
rc = -EAGAIN;
break;
}
rc = -EAGAIN;
if (sk->state == TCP_CLOSE)
break;
rc = -ERESTARTSYS;
if (signal_pending(current))
break;
rc = -EAGAIN;
if (!timeout)
break;
}
__set_current_state(TASK_RUNNING);
remove_wait_queue(sk->sleep, &wait);
return rc;
}
/**
* llc_ui_accept - accept a new incoming connection.
* @sock: Socket which connections arrive on.
* @newsock: Socket to move incoming connection to.
* @flags: User specified operational flags.
*
* Accept a new incoming connection.
* Returns 0 upon success, negative otherwise.
*/
static int llc_ui_accept(struct socket *sock, struct socket *newsock, int flags)
{
struct sock *sk = sock->sk, *newsk;
struct llc_ui_opt *llc_ui, *newllc_ui;
struct llc_opt *newllc_core;
struct sk_buff *skb;
int rc = -EOPNOTSUPP;
lock_sock(sk);
if (sk->type != SOCK_SEQPACKET && sk->type != SOCK_STREAM)
goto out;
rc = -EINVAL;
if (sock->state != SS_UNCONNECTED || sk->state != TCP_LISTEN)
goto out;
/* wait for a connection to arrive. */
do {
skb = skb_dequeue(&sk->receive_queue);
if (!skb) {
rc = -EWOULDBLOCK;
if (flags & O_NONBLOCK)
goto out;
interruptible_sleep_on(sk->sleep);
rc = -ERESTARTSYS;
if (signal_pending(current))
goto out;
}
} while (!skb);
rc = -EINVAL;
if(!skb->sk)
goto frees;
/* attach connection to a new socket. */
rc = llc_ui_create(newsock, sk->protocol);
if (rc)
goto frees;
rc = 0;
newsk = newsock->sk;
newsk->pair = NULL;
newsk->socket = newsock;
newsk->sleep = &newsock->wait;
newsk->zapped = 0;
newsk->state = TCP_ESTABLISHED;
newsock->state = SS_CONNECTED;
llc_ui = llc_ui_sk(sk);
newllc_ui = llc_ui_sk(newsk);
newllc_ui->sap = llc_ui->sap;
newllc_ui->dev = llc_ui->dev;
newllc_ui->core_sk = skb->sk;
newllc_core = llc_sk(newllc_ui->core_sk);
newllc_ui->link = newllc_core->link;
newllc_core->handler = newsk;
memcpy(&newllc_ui->addr, &llc_ui->addr, sizeof(newllc_ui->addr));
memcpy(newllc_ui->addr.sllc_dmac, newllc_core->daddr.mac, IFHWADDRLEN);
newllc_ui->addr.sllc_dsap = newllc_core->daddr.lsap;
/* put original socket back into a clean listen state. */
sk->state = TCP_LISTEN;
sk->ack_backlog--;
llc_ui_insert_socket(newsk);
skb->sk = NULL;
frees:
kfree_skb(skb);
out:
release_sock(sk);
return rc;
}
/**
* llc_ui_recvmsg - copy received data to the socket user.
* @sock: Socket to copy data from.
* @msg: Various user space related information.
* @size: Size of user buffer.
* @flags: User specified flags.
* @scm: Unknown.
*
* Copy received data to the socket user.
* Returns non-negative upon success, negative otherwise.
*/
static int llc_ui_recvmsg(struct socket *sock, struct msghdr *msg, int size,
int flags, struct scm_cookie *scm)
{
struct sock *sk = sock->sk;
struct sockaddr_llc *uaddr = (struct sockaddr_llc *)msg->msg_name;
struct sk_buff *skb;
int rc = -ENOMEM, copied = 0;
int noblock = flags & MSG_DONTWAIT;
lock_sock(sk);
skb = skb_recv_datagram(sk, flags, noblock, &rc);
if (!skb)
goto out;
copied = skb->len;
if (copied > size) {
copied = size;
msg->msg_flags |= MSG_TRUNC;
}
rc = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
if (rc)
goto dgram_free;
if (uaddr)
memcpy(uaddr, llc_ui_skb_cb(skb), sizeof(*uaddr));
msg->msg_namelen = sizeof(*uaddr);
dgram_free:
skb_free_datagram(sk, skb); /* Free the datagram. */
out:
release_sock(sk);
return rc ? : copied;
}
/**
* llc_ui_sendmsg - Transmit data provided by the socket user.
* @sock: Socket to transmit data from.
* @msg: Various user related information.
* @len: Length of data to transmit.
* @scm: Unknown.
*
* Transmit data provided by the socket user.
* Returns non-negative upon success, negative otherwise.
*/
static int llc_ui_sendmsg(struct socket *sock, struct msghdr *msg, int len,
struct scm_cookie *scm)
{
struct sock *sk = sock->sk;
struct llc_ui_opt *llc_ui = llc_ui_sk(sk);
struct sockaddr_llc *addr = (struct sockaddr_llc *)msg->msg_name;
int flags = msg->msg_flags;
struct net_device *dev;
struct sk_buff *skb;
int rc = -EOPNOTSUPP, size = 0;
lock_sock(sk);
if (flags & ~MSG_DONTWAIT)
goto release;
rc = -EINVAL;
if (addr) {
if (msg->msg_namelen < sizeof(*addr))
goto release;
} else {
if (llc_ui_addr_null(&llc_ui->addr))
goto release;
addr = &llc_ui->addr;
}
/* must bind connection to sap if user hasn't done it. */
if (sk->zapped) {
/* bind to sap with null dev, exclusive. */
rc = llc_ui_autobind(sock, addr);
if (rc)
goto release;
}
if (!llc_ui->dev) {
rtnl_lock();
dev = dev_getbyhwaddr(addr->sllc_arphrd, addr->sllc_smac);
rtnl_unlock();
rc = -ENETUNREACH;
if (!dev)
goto release;
} else
dev = llc_ui->dev;
size = dev->hard_header_len + len + llc_ui_header_len(sk, addr);
rc = -EMSGSIZE;
if (size > dev->mtu)
goto release;
skb = sock_alloc_send_skb(sk, size, flags & MSG_DONTWAIT, &rc);
if (!skb)
goto release;
skb->sk = sk;
skb->dev = dev;
skb_reserve(skb, dev->hard_header_len + llc_ui_header_len(sk, addr));
rc = memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len);
if (rc)
goto release;
if (addr->sllc_test) {
rc = llc_ui_send_llc1(llc_ui->sap, skb, addr, LLC_TEST_PRIM);
goto out;
}
if (addr->sllc_xid) {
rc = llc_ui_send_llc1(llc_ui->sap, skb, addr, LLC_XID_PRIM);
goto out;
}
if (sk->type == SOCK_DGRAM || addr->sllc_ua) {
rc = llc_ui_send_llc1(llc_ui->sap, skb, addr, LLC_DATAUNIT_PRIM);
goto out;
}
rc = -ENOPROTOOPT;
if (!(sk->type == SOCK_STREAM && !addr->sllc_ua))
goto out;
rc = -ENOTCONN;
if (!llc_ui->core_sk)
goto out;
rc = llc_ui_send_data(llc_ui->sap, sk, skb, addr);
out:
if (rc)
skb_free_datagram(sk, skb);
release:
release_sock(sk);
return rc ? : len;
}
/**
* llc_ui_getname - return the address info of a socket
* @sock: Socket to get address of.
* @uaddr: Address structure to return information.
* @uaddrlen: Length of address structure.
* @peer: Does user want local or remote address information.
*
* Return the address information of a socket.
*/
static int llc_ui_getname(struct socket *sock, struct sockaddr *uaddr,
int *uaddrlen, int peer)
{
struct sockaddr_llc sllc;
struct sock *sk = sock->sk;
struct llc_ui_opt *llc_ui = llc_ui_sk(sk);
int rc = 0;
lock_sock(sk);
if (sk->zapped)
goto out;
*uaddrlen = sizeof(sllc);
memset(uaddr, 0, *uaddrlen);
if (peer) {
rc = -ENOTCONN;
if (sk->state != TCP_ESTABLISHED)
goto out;
if(llc_ui->dev)
sllc.sllc_arphrd = llc_ui->dev->type;
sllc.sllc_dsap = llc_sk(llc_ui->core_sk)->daddr.lsap;
memcpy(&sllc.sllc_dmac, &llc_sk(llc_ui->core_sk)->daddr.mac,
IFHWADDRLEN);
} else {
rc = -EINVAL;
if (!llc_ui->sap)
goto out;
sllc.sllc_ssap = llc_ui->sap->laddr.lsap;
if (llc_ui->dev) {
sllc.sllc_arphrd = llc_ui->dev->type;
memcpy(&sllc.sllc_smac, &llc_ui->dev->dev_addr,
IFHWADDRLEN);
}
}
rc = 0;
sllc.sllc_family = AF_LLC;
memcpy(uaddr, &sllc, sizeof(sllc));
out:
release_sock(sk);
return rc;
}
/**
* llc_ui_ioctl - io controls for PF_LLC
* @sock: Socket to get/set info
* @cmd: command
* @arg: optional argument for cmd
*
* get/set info on llc sockets
*/
static int llc_ui_ioctl(struct socket *sock, unsigned int cmd,
unsigned long arg)
{
return dev_ioctl(cmd, (void *)arg);
}
/**
* llc_ui_setsockopt - set various connection specific parameters.
* @sock: Socket to set options on.
* @level: Socket level user is requesting operations on.
* @optname: Operation name.
* @optval User provided operation data.
* @optlen: Length of optval.
*
* Set various connection specific parameters.
*/
static int llc_ui_setsockopt(struct socket *sock, int level, int optname,
char *optval, int optlen)
{
struct sock *sk = sock->sk;
struct llc_ui_opt *llc_ui = llc_ui_sk(sk);
struct llc_opt *llc_core;
int rc = -EINVAL, opt;
lock_sock(sk);
if (level != SOL_LLC || optlen != sizeof(int))
goto out;
rc = -ENOTCONN;
if (!llc_ui->core_sk)
goto out;
rc = get_user(opt, (int *)optval);
if (rc)
goto out;
rc = -EINVAL;
llc_core = llc_sk(llc_ui->core_sk);
switch (optname) {
case LLC_OPT_RETRY:
if (opt > LLC_OPT_MAX_RETRY)
goto out;
llc_core->n2 = opt;
break;
case LLC_OPT_SIZE:
if (opt > LLC_OPT_MAX_SIZE)
goto out;
llc_core->n1 = opt;
break;
case LLC_OPT_ACK_TMR_EXP:
if (opt > LLC_OPT_MAX_ACK_TMR_EXP)
goto out;
llc_core->ack_timer.expire = opt;
break;
case LLC_OPT_P_TMR_EXP:
if (opt > LLC_OPT_MAX_P_TMR_EXP)
goto out;
llc_core->pf_cycle_timer.expire = opt;
break;
case LLC_OPT_REJ_TMR_EXP:
if (opt > LLC_OPT_MAX_REJ_TMR_EXP)
goto out;
llc_core->rej_sent_timer.expire = opt;
break;
case LLC_OPT_BUSY_TMR_EXP:
if (opt > LLC_OPT_MAX_BUSY_TMR_EXP)
goto out;
llc_core->busy_state_timer.expire = opt;
break;
case LLC_OPT_TX_WIN:
if (opt > LLC_OPT_MAX_WIN)
goto out;
llc_core->k = opt;
break;
case LLC_OPT_RX_WIN:
if (opt > LLC_OPT_MAX_WIN)
goto out;
llc_core->rw = opt;
break;
default:
rc = -ENOPROTOOPT;
goto out;
}
rc = 0;
out:
release_sock(sk);
return rc;
}
/**
* llc_ui_getsockopt - get connection specific socket info
* @sock: Socket to get information from.
* @level: Socket level user is requesting operations on.
* @optname: Operation name.
* @optval: Variable to return operation data in.
* @optlen: Length of optval.
*
* Get connection specific socket information.
*/
static int llc_ui_getsockopt(struct socket *sock, int level, int optname,
char *optval, int *optlen)
{
struct sock *sk = sock->sk;
struct llc_ui_opt *llc_ui = llc_ui_sk(sk);
struct llc_opt *llc_core;
int val = 0, len = 0, rc = -EINVAL;
lock_sock(sk);
if (level != SOL_LLC)
goto out;
rc = -ENOTCONN;
if (!llc_ui->core_sk)
goto out;
rc = get_user(len, optlen);
if (rc)
goto out;
rc = -EINVAL;
if (len != sizeof(int))
goto out;
llc_core = llc_sk(llc_ui->core_sk);
switch (optname) {
case LLC_OPT_RETRY:
val = llc_core->n2; break;
case LLC_OPT_SIZE:
val = llc_core->n1; break;
case LLC_OPT_ACK_TMR_EXP:
val = llc_core->ack_timer.expire; break;
case LLC_OPT_P_TMR_EXP:
val = llc_core->pf_cycle_timer.expire; break;
case LLC_OPT_REJ_TMR_EXP:
val = llc_core->rej_sent_timer.expire; break;
case LLC_OPT_BUSY_TMR_EXP:
val = llc_core->busy_state_timer.expire; break;
case LLC_OPT_TX_WIN:
val = llc_core->k; break;
case LLC_OPT_RX_WIN:
val = llc_core->rw; break;
default:
rc = -ENOPROTOOPT;
goto out;
}
rc = 0;
if (put_user(len, optlen) || copy_to_user(optval, &val, len))
rc = -EFAULT;
out:
release_sock(sk);
return rc;
}
/**
* llc_ui_ind_test - handle TEST indication
* @prim: Primitive block provided by the llc layer.
*
* handle TEST indication.
*/
static void llc_ui_ind_test(struct llc_prim_if_block *prim)
{
struct llc_prim_test *prim_data = &prim->data->test;
struct sk_buff *skb = prim_data->skb;
struct sockaddr_llc *llc_ui = llc_ui_skb_cb(skb);
struct sock *sk = llc_ui_find_sk_by_addr(&prim_data->daddr,
&prim_data->saddr, skb->dev);
if (!sk)
goto out;
if (sk->state == TCP_LISTEN)
goto out_put;
/* save primitive for use by the user. */
llc_ui->sllc_family = AF_LLC;
llc_ui->sllc_arphrd = skb->dev->type;
llc_ui->sllc_test = 1;
llc_ui->sllc_xid = 0;
llc_ui->sllc_ua = 0;
llc_ui->sllc_dsap = prim_data->daddr.lsap;
memcpy(llc_ui->sllc_dmac, prim_data->daddr.mac, IFHWADDRLEN);
llc_ui->sllc_ssap = prim_data->saddr.lsap;
memcpy(llc_ui->sllc_smac, prim_data->saddr.mac, IFHWADDRLEN);
/* queue skb to the user. */
if (sock_queue_rcv_skb(sk, skb))
kfree_skb(skb);
out_put:
sock_put(sk);
out:;
}
/**
* llc_ui_ind_xid - handle XID indication
* @prim: Primitive block provided by the llc layer.
*
* handle XID indication.
*/
static void llc_ui_ind_xid(struct llc_prim_if_block *prim)
{
struct llc_prim_xid *prim_data = &prim->data->xid;
struct sk_buff *skb = prim_data->skb;
struct sockaddr_llc *llc_ui = llc_ui_skb_cb(skb);
struct sock *sk = llc_ui_find_sk_by_addr(&prim_data->daddr,
&prim_data->saddr, skb->dev);
if (!sk)
goto out;
if (sk->state == TCP_LISTEN)
goto out_put;
/* save primitive for use by the user. */
llc_ui->sllc_family = AF_LLC;
llc_ui->sllc_arphrd = 0;
llc_ui->sllc_test = 0;
llc_ui->sllc_xid = 1;
llc_ui->sllc_ua = 0;
llc_ui->sllc_dsap = prim_data->daddr.lsap;
memcpy(llc_ui->sllc_dmac, prim_data->daddr.mac, IFHWADDRLEN);
llc_ui->sllc_ssap = prim_data->saddr.lsap;
memcpy(llc_ui->sllc_smac, prim_data->saddr.mac, IFHWADDRLEN);
/* queue skb to the user. */
if (sock_queue_rcv_skb(sk, skb))
kfree_skb(skb);
out_put:
sock_put(sk);
out:;
}
/**
* llc_ui_ind_dataunit - handle DATAUNIT indication
* @prim: Primitive block provided by the llc layer.
*
* handle DATAUNIT indication.
*/
static void llc_ui_ind_dataunit(struct llc_prim_if_block *prim)
{
struct llc_prim_unit_data *prim_data = &prim->data->udata;
struct sk_buff *skb = prim_data->skb;
struct sockaddr_llc *llc_ui = llc_ui_skb_cb(skb);
struct sock *sk = llc_ui_find_sk_by_addr(&prim_data->daddr,
&prim_data->saddr, skb->dev);
if (!sk)
goto out;
if (sk->state == TCP_LISTEN)
goto out_put;
/* save primitive for use by the user. */
llc_ui->sllc_family = AF_LLC;
llc_ui->sllc_arphrd = skb->dev->type;
llc_ui->sllc_test = 0;
llc_ui->sllc_xid = 0;
llc_ui->sllc_ua = 1;
llc_ui->sllc_dsap = prim_data->daddr.lsap;
memcpy(llc_ui->sllc_dmac, prim_data->daddr.mac, IFHWADDRLEN);
llc_ui->sllc_ssap = prim_data->saddr.lsap;
memcpy(llc_ui->sllc_smac, prim_data->saddr.mac, IFHWADDRLEN);
/* queue skb to the user. */
if (sock_queue_rcv_skb(sk, skb))
kfree_skb(skb);
out_put:
sock_put(sk);
out:;
}
/**
* llc_ui_ind_conn - handle CONNECT indication
* @prim: Primitive block provided by the llc layer.
*
* handle CONNECT indication.
*/
static void llc_ui_ind_conn(struct llc_prim_if_block *prim)
{
struct llc_prim_conn *prim_data = &prim->data->conn;
struct sock* sk;
struct sk_buff *skb2;
llc_sk(prim_data->sk)->laddr.lsap = prim->sap->laddr.lsap;
sk = llc_ui_find_sk_by_addr(&llc_sk(prim_data->sk)->laddr,
&prim_data->saddr, prim_data->dev);
if (!sk) {
dprintk("llc_ui_find_sk_by_addr failed\n");
goto out;
}
if (sk->type != SOCK_STREAM || sk->state != TCP_LISTEN)
goto out_put;
if (prim->data->conn.status)
goto out_put; /* bad status. */
/* give this connection a link number. */
llc_sk(prim_data->sk)->link =
llc_ui_next_link_no(llc_sk(prim_data->sk)->laddr.lsap);
skb2 = alloc_skb(0, GFP_ATOMIC);
if (!skb2)
goto out_put;
skb2->sk = prim_data->sk;
skb_queue_tail(&sk->receive_queue, skb2);
sk->state_change(sk);
out_put:
sock_put(sk);
out:;
}
/**
* llc_ui_ind_data - handle DATA indication
* @prim: Primitive block provided by the llc layer.
*
* handle CONNECT indication.
*/
static void llc_ui_ind_data(struct llc_prim_if_block *prim)
{
struct llc_prim_data *prim_data = &prim->data->data;
struct sk_buff *skb = prim_data->skb;
struct sockaddr_llc *llc_ui = llc_ui_skb_cb(skb);
struct sock* sk = llc_sk(prim_data->sk)->handler;
if (!sk)
goto out;
sock_hold(sk);
if (sk->type != SOCK_STREAM || sk->state != TCP_ESTABLISHED)
goto out_put;
/* save primitive for use by the user. */
llc_ui->sllc_family = AF_LLC;
llc_ui->sllc_arphrd = skb->dev->type;
llc_ui->sllc_test = 0;
llc_ui->sllc_xid = 0;
llc_ui->sllc_ua = 0;
llc_ui->sllc_dsap = llc_ui_sk(sk)->sap->laddr.lsap;
memcpy(llc_ui->sllc_dmac, llc_sk(prim_data->sk)->laddr.mac,
IFHWADDRLEN);
llc_ui->sllc_ssap = llc_sk(prim_data->sk)->daddr.lsap;
memcpy(llc_ui->sllc_smac, llc_sk(prim_data->sk)->daddr.mac,
IFHWADDRLEN);
/* queue skb to the user. */
if (sock_queue_rcv_skb(sk, skb)) {
dprintk("sock_queue_rcv_skb failed!\n");
kfree_skb(skb);
}
out_put:
sock_put(sk);
out:;
}
/**
* llc_ui_ind_disc - handle DISC indication
* @prim: Primitive block provided by the llc layer.
*
* handle DISC indication.
*/
static void llc_ui_ind_disc(struct llc_prim_if_block *prim)
{
struct llc_prim_disc *prim_data = &prim->data->disc;
struct sock* sk = llc_sk(prim_data->sk)->handler;
if (!sk)
goto out;
sock_hold(sk);
if (sk->type != SOCK_STREAM || sk->state != TCP_ESTABLISHED)
goto out_put;
llc_ui_sk(sk)->core_sk = NULL;
sk->shutdown = SHUTDOWN_MASK;
sk->socket->state = SS_UNCONNECTED;
sk->state = TCP_CLOSE;
if (!sk->dead) {
sk->state_change(sk);
sk->dead = 1;
}
out_put:
sock_put(sk);
out:;
}
/**
* llc_ui_indicate - LLC user interface hook into the LLC layer.
* @prim: Primitive block provided by the llc layer.
*
* LLC user interface hook into the LLC layer, every llc_ui sap references
* this function as its indicate handler.
* Always returns 0 to indicate reception of primitive.
*/
static int llc_ui_indicate(struct llc_prim_if_block *prim)
{
switch (prim->prim) {
case LLC_TEST_PRIM:
llc_ui_ind_test(prim); break;
case LLC_XID_PRIM:
llc_ui_ind_xid(prim); break;
case LLC_DATAUNIT_PRIM:
llc_ui_ind_dataunit(prim); break;
case LLC_CONN_PRIM:
llc_ui_ind_conn(prim); break;
case LLC_DATA_PRIM:
llc_ui_ind_data(prim); break;
case LLC_DISC_PRIM:
llc_ui_ind_disc(prim); break;
case LLC_RESET_PRIM:
case LLC_FLOWCONTROL_PRIM:
default: break;
}
return 0;
}
/**
* llc_ui_conf_conn - handle CONN confirm.
* @prim: Primitive block provided by the llc layer.
*
* handle CONN confirm.
*/
static void llc_ui_conf_conn(struct llc_prim_if_block *prim)
{
struct llc_prim_conn *prim_data = &prim->data->conn;
struct llc_opt *llc_core = llc_sk(prim_data->sk);
struct llc_ui_opt *llc_ui = llc_ui_sk(prim_data->sk);
struct sock* sk = llc_core->handler;
if (!sk) {
dprintk("llc_core->handler == NULL!\n");
goto out;
}
sock_hold(sk);
if (sk->type != SOCK_STREAM || sk->state != TCP_SYN_SENT)
goto out_put;
if (!prim->data->conn.status) {
sk->socket->state = SS_CONNECTED;
sk->state = TCP_ESTABLISHED;
llc_ui->core_sk = prim_data->sk;
} else {
dprintk("prim->data->conn.status = %d\n",
prim->data->conn.status);
sk->socket->state = SS_UNCONNECTED;
sk->state = TCP_CLOSE;
llc_ui->core_sk = NULL;
}
sk->state_change(sk);
out_put:
sock_put(sk);
out:;
}
/**
* llc_ui_conf_data - handle DATA confirm.
* @prim: Primitive block provided by the llc layer.
*
* handle DATA confirm.
*/
static void llc_ui_conf_data(struct llc_prim_if_block *prim)
{
struct llc_prim_data *prim_data = &prim->data->data;
struct sock* sk = llc_sk(prim_data->sk)->handler;
if (sk)
wake_up(sk->sleep);
}
/**
* llc_ui_conf_disc - handle DISC confirm.
* @prim: Primitive block provided by the llc layer.
*
* handle DISC confirm.
*/
static void llc_ui_conf_disc(struct llc_prim_if_block *prim)
{
struct llc_prim_disc *prim_data = &prim->data->disc;
struct sock* sk = llc_sk(prim_data->sk)->handler;
if (!sk)
goto out;
sock_hold(sk);
if (sk->type != SOCK_STREAM || sk->state != TCP_CLOSING)
goto out_put;
llc_ui_sk(sk)->core_sk = NULL;
sk->socket->state = SS_UNCONNECTED;
sk->state = TCP_CLOSE;
sk->state_change(sk);
out_put:
sock_put(sk);
out:;
}
/**
* llc_ui_confirm - LLC user interface hook into the LLC layer
* @prim: Primitive block provided by the llc layer.
*
* LLC user interface hook into the LLC layer, every llc_ui sap references
* this function as its confirm handler.
* Always returns 0 to indicate reception of primitive.
*/
static int llc_ui_confirm(struct llc_prim_if_block *prim)
{
switch (prim->prim) {
case LLC_CONN_PRIM:
llc_ui_conf_conn(prim); break;
case LLC_DATA_PRIM:
llc_ui_conf_data(prim); break;
case LLC_DISC_PRIM:
llc_ui_conf_disc(prim); break;
case LLC_RESET_PRIM: break;
default:
printk(KERN_ERR __FUNCTION__ ": unknown prim %d\n",
prim->prim);
break;
}
return 0;
}
#ifdef CONFIG_PROC_FS
/**
* llc_ui_get_info - return info to procfs
* @buffer: where to put the formatted output
* @start: starting from
* @offset: offset into buffer.
* @length: size of the buffer
*
* Get the output of the local llc ui socket list to the caller.
* Returns the length of data wrote to buffer.
*/
static int llc_ui_get_info(char *buffer, char **start, off_t offset, int length)
{
off_t pos = 0;
off_t begin = 0;
struct sock *s;
int len = sprintf(buffer, "SocketID SKt Mc local_mac_sap\t "
"remote_mac_sap\t tx_queue rx_queue st uid "
"link_no\n");
/* Output the LLC socket data for the /proc filesystem */
read_lock_bh(&llc_ui_sockets_lock);
for (s = llc_ui_sockets; s; s = s->next) {
struct llc_ui_opt *llc_ui = llc_ui_sk(s);
len += sprintf(buffer + len, "%p %02X %02X ", s, s->type,
!llc_ui_mac_null(llc_ui->addr.sllc_mmac));
if (llc_ui->sap) {
if (llc_ui->dev &&
llc_ui_mac_null(llc_ui->addr.sllc_mmac))
len += sprintf(buffer + len,
"%02X:%02X:%02X:%02X:%02X:%02X",
llc_ui->dev->dev_addr[0],
llc_ui->dev->dev_addr[1],
llc_ui->dev->dev_addr[2],
llc_ui->dev->dev_addr[3],
llc_ui->dev->dev_addr[4],
llc_ui->dev->dev_addr[5]);
else {
if (!llc_ui_mac_null(llc_ui->addr.sllc_mmac))
len += sprintf(buffer + len,
"%02X:%02X:%02X:%02X:%02X:%02X",
llc_ui->addr.sllc_mmac[0],
llc_ui->addr.sllc_mmac[1],
llc_ui->addr.sllc_mmac[2],
llc_ui->addr.sllc_mmac[3],
llc_ui->addr.sllc_mmac[4],
llc_ui->addr.sllc_mmac[5]);
else
len += sprintf(buffer + len,
"00:00:00:00:00:00");
}
len += sprintf(buffer + len, "@%02X ",
llc_ui->sap->laddr.lsap);
} else
len += sprintf(buffer + len, "00:00:00:00:00:00@00 ");
len += sprintf(buffer + len,
"%02X:%02X:%02X:%02X:%02X:%02X@%02X "
"%08X:%08X %02X %-3d ",
llc_ui->addr.sllc_dmac[0],
llc_ui->addr.sllc_dmac[1],
llc_ui->addr.sllc_dmac[2],
llc_ui->addr.sllc_dmac[3],
llc_ui->addr.sllc_dmac[4],
llc_ui->addr.sllc_dmac[5],
llc_ui->addr.sllc_dsap,
atomic_read(&s->wmem_alloc),
atomic_read(&s->rmem_alloc), s->state,
SOCK_INODE(s->socket)->i_uid);
if (llc_ui->core_sk)
len += sprintf(buffer + len, "%-7d\n",
llc_sk(llc_ui->core_sk)->link);
else
len += sprintf(buffer + len, "no_link\n");
/* Are we still dumping unwanted data then discard the record */
pos = begin + len;
if (pos < offset) {
len = 0; /* Keep dumping into the buffer start */
begin = pos;
}
if (pos > offset + length) /* We have dumped enough */
break;
}
read_unlock_bh(&llc_ui_sockets_lock);
/* The data in question runs from begin to begin + len */
*start = buffer + offset - begin; /* Start of wanted data */
len -= offset - begin; /* Remove unwanted header data from length */
if (len > length)
len = length; /* Remove unwanted tail data from length */
return len;
}
#endif /* CONFIG_PROC_FS */
static struct net_proto_family llc_ui_family_ops = {
.family = PF_LLC,
.create = llc_ui_create,
};
static struct proto_ops SOCKOPS_WRAPPED(llc_ui_ops) = {
.family = PF_LLC,
.release = llc_ui_release,
.bind = llc_ui_bind,
.connect = llc_ui_connect,
.socketpair = sock_no_socketpair,
.accept = llc_ui_accept,
.getname = llc_ui_getname,
.poll = datagram_poll,
.ioctl = llc_ui_ioctl,
.listen = llc_ui_listen,
.shutdown = llc_ui_shutdown,
.setsockopt = llc_ui_setsockopt,
.getsockopt = llc_ui_getsockopt,
.sendmsg = llc_ui_sendmsg,
.recvmsg = llc_ui_recvmsg,
.mmap = sock_no_mmap,
.sendpage = sock_no_sendpage,
};
#include <linux/smp_lock.h>
SOCKOPS_WRAP(llc_ui, PF_LLC);
static char llc_ui_banner[] __initdata =
KERN_INFO "NET4.0 IEEE 802.2 User Interface SAPs, Jay Schulist, 2001\n";
int __init llc_ui_init(void)
{
llc_ui_sap_last_autoport = LLC_SAP_DYN_START;
sock_register(&llc_ui_family_ops);
proc_net_create("llc", 0, llc_ui_get_info);
printk(llc_ui_banner);
return 0;
}
void __exit llc_ui_exit(void)
{
proc_net_remove("llc");
sock_unregister(PF_LLC);
}
/*
* llc_stat.c - Implementation of LLC station component state machine
* transitions
* Copyright (c) 1997 by Procom Technology, Inc.
* 2001 by Arnaldo Carvalho de Melo <acme@conectiva.com.br>
*
* This program can be redistributed or modified under the terms of the
* GNU General Public License as published by the Free Software Foundation.
* This program is distributed without any warranty or implied warranty
* of merchantability or fitness for a particular purpose.
*
* See the GNU General Public License for more details.
*/
#include <linux/types.h>
#include <net/llc_if.h>
#include <net/llc_sap.h>
#include <net/llc_evnt.h>
#include <net/llc_actn.h>
#include <net/llc_stat.h>
/* COMMON STATION STATE transitions */
/* dummy last-transition indicator; common to all state transition groups
* last entry for this state
* all members are zeros, .bss zeroes it
*/
static struct llc_station_state_trans llc_stat_state_trans_n;
/* DOWN STATE transitions */
/* state transition for LLC_STATION_EV_ENABLE_WITH_DUP_ADDR_CHECK event */
static llc_station_action_t llc_stat_down_state_actions_1[] = {
[0] = llc_station_ac_start_ack_timer,
[1] = llc_station_ac_set_retry_cnt_0,
[2] = llc_station_ac_set_xid_r_cnt_0,
[3] = llc_station_ac_send_null_dsap_xid_c,
[4] = NULL,
};
static struct llc_station_state_trans llc_stat_down_state_trans_1 = {
.ev = llc_stat_ev_enable_with_dup_addr_check,
.next_state = LLC_STATION_STATE_DUP_ADDR_CHK,
.ev_actions = llc_stat_down_state_actions_1,
};
/* state transition for LLC_STATION_EV_ENABLE_WITHOUT_DUP_ADDR_CHECK event */
static llc_station_action_t llc_stat_down_state_actions_2[] = {
[0] = llc_station_ac_report_status, /* STATION UP */
[1] = NULL,
};
static struct llc_station_state_trans llc_stat_down_state_trans_2 = {
.ev = llc_stat_ev_enable_without_dup_addr_check,
.next_state = LLC_STATION_STATE_UP,
.ev_actions = llc_stat_down_state_actions_2,
};
/* array of pointers; one to each transition */
static struct llc_station_state_trans *llc_stat_dwn_state_trans[] = {
[0] = &llc_stat_down_state_trans_1,
[1] = &llc_stat_down_state_trans_2,
[2] = &llc_stat_state_trans_n,
};
/* UP STATE transitions */
/* state transition for LLC_STATION_EV_DISABLE_REQ event */
static llc_station_action_t llc_stat_up_state_actions_1[] = {
[0] = llc_station_ac_report_status, /* STATION DOWN */
[1] = NULL,
};
static struct llc_station_state_trans llc_stat_up_state_trans_1 = {
.ev = llc_stat_ev_disable_req,
.next_state = LLC_STATION_STATE_DOWN,
.ev_actions = llc_stat_up_state_actions_1,
};
/* state transition for LLC_STATION_EV_RX_NULL_DSAP_XID_C event */
static llc_station_action_t llc_stat_up_state_actions_2[] = {
[0] = llc_station_ac_send_xid_r,
[1] = NULL,
};
static struct llc_station_state_trans llc_stat_up_state_trans_2 = {
.ev = llc_stat_ev_rx_null_dsap_xid_c,
.next_state = LLC_STATION_STATE_UP,
.ev_actions = llc_stat_up_state_actions_2,
};
/* state transition for LLC_STATION_EV_RX_NULL_DSAP_TEST_C event */
static llc_station_action_t llc_stat_up_state_actions_3[] = {
[0] = llc_station_ac_send_test_r,
[1] = NULL,
};
static struct llc_station_state_trans llc_stat_up_state_trans_3 = {
.ev = llc_stat_ev_rx_null_dsap_test_c,
.next_state = LLC_STATION_STATE_UP,
.ev_actions = llc_stat_up_state_actions_3,
};
/* array of pointers; one to each transition */
static struct llc_station_state_trans *llc_stat_up_state_trans [] = {
[0] = &llc_stat_up_state_trans_1,
[1] = &llc_stat_up_state_trans_2,
[2] = &llc_stat_up_state_trans_3,
[3] = &llc_stat_state_trans_n,
};
/* DUP ADDR CHK STATE transitions */
/* state transition for LLC_STATION_EV_RX_NULL_DSAP_0_XID_R_XID_R_CNT_EQ
* event
*/
static llc_station_action_t llc_stat_dupaddr_state_actions_1[] = {
[0] = llc_station_ac_inc_xid_r_cnt_by_1,
[1] = NULL,
};
static struct llc_station_state_trans llc_stat_dupaddr_state_trans_1 = {
.ev = llc_stat_ev_rx_null_dsap_0_xid_r_xid_r_cnt_eq,
.next_state = LLC_STATION_STATE_DUP_ADDR_CHK,
.ev_actions = llc_stat_dupaddr_state_actions_1,
};
/* state transition for LLC_STATION_EV_RX_NULL_DSAP_1_XID_R_XID_R_CNT_EQ
* event
*/
static llc_station_action_t llc_stat_dupaddr_state_actions_2[] = {
[0] = llc_station_ac_report_status, /* DUPLICATE ADDRESS FOUND */
[1] = NULL,
};
static struct llc_station_state_trans llc_stat_dupaddr_state_trans_2 = {
.ev = llc_stat_ev_rx_null_dsap_1_xid_r_xid_r_cnt_eq,
.next_state = LLC_STATION_STATE_DOWN,
.ev_actions = llc_stat_dupaddr_state_actions_2,
};
/* state transition for LLC_STATION_EV_RX_NULL_DSAP_XID_C event */
static llc_station_action_t llc_stat_dupaddr_state_actions_3[] = {
[0] = llc_station_ac_send_xid_r,
[1] = NULL,
};
static struct llc_station_state_trans llc_stat_dupaddr_state_trans_3 = {
.ev = llc_stat_ev_rx_null_dsap_xid_c,
.next_state = LLC_STATION_STATE_DUP_ADDR_CHK,
.ev_actions = llc_stat_dupaddr_state_actions_3,
};
/* state transition for LLC_STATION_EV_ACK_TMR_EXP_LT_RETRY_CNT_MAX_RETRY
* event
*/
static llc_station_action_t llc_stat_dupaddr_state_actions_4[] = {
[0] = llc_station_ac_start_ack_timer,
[1] = llc_station_ac_inc_retry_cnt_by_1,
[2] = llc_station_ac_set_xid_r_cnt_0,
[3] = llc_station_ac_send_null_dsap_xid_c,
[4] = NULL,
};
static struct llc_station_state_trans llc_stat_dupaddr_state_trans_4 = {
.ev = llc_stat_ev_ack_tmr_exp_lt_retry_cnt_max_retry,
.next_state = LLC_STATION_STATE_DUP_ADDR_CHK,
.ev_actions = llc_stat_dupaddr_state_actions_4
};
/* state transition for LLC_STATION_EV_ACK_TMR_EXP_EQ_RETRY_CNT_MAX_RETRY
* event
*/
static llc_station_action_t llc_stat_dupaddr_state_actions_5[] = {
[0] = llc_station_ac_report_status, /* STATION UP */
[1] = NULL,
};
static struct llc_station_state_trans llc_stat_dupaddr_state_trans_5 = {
.ev = llc_stat_ev_ack_tmr_exp_eq_retry_cnt_max_retry,
.next_state = LLC_STATION_STATE_UP,
.ev_actions = llc_stat_dupaddr_state_actions_5,
};
/* state transition for LLC_STATION_EV_DISABLE_REQ event */
static llc_station_action_t llc_stat_dupaddr_state_actions_6[] = {
[0] = llc_station_ac_report_status, /* STATION DOWN */
[1] = NULL,
};
static struct llc_station_state_trans llc_stat_dupaddr_state_trans_6 = {
.ev = llc_stat_ev_disable_req,
.next_state = LLC_STATION_STATE_DOWN,
.ev_actions = llc_stat_dupaddr_state_actions_6,
};
/* array of pointers; one to each transition */
static struct llc_station_state_trans *llc_stat_dupaddr_state_trans[] = {
[0] = &llc_stat_dupaddr_state_trans_6, /* Request */
[1] = &llc_stat_dupaddr_state_trans_4, /* Timer */
[2] = &llc_stat_dupaddr_state_trans_5,
[3] = &llc_stat_dupaddr_state_trans_1, /* Receive frame */
[4] = &llc_stat_dupaddr_state_trans_2,
[5] = &llc_stat_dupaddr_state_trans_3,
[6] = &llc_stat_state_trans_n
};
struct llc_station_state llc_station_state_table[LLC_NBR_STATION_STATES] = {
{
.curr_state = LLC_STATION_STATE_DOWN,
.transitions = llc_stat_dwn_state_trans,
},
{
.curr_state = LLC_STATION_STATE_DUP_ADDR_CHK,
.transitions = llc_stat_dupaddr_state_trans,
},
{
.curr_state = LLC_STATION_STATE_UP,
.transitions = llc_stat_up_state_trans,
}
};
...@@ -444,6 +444,7 @@ EXPORT_SYMBOL(arp_find); ...@@ -444,6 +444,7 @@ EXPORT_SYMBOL(arp_find);
#endif /* CONFIG_INET */ #endif /* CONFIG_INET */
#ifdef CONFIG_TR #ifdef CONFIG_TR
EXPORT_SYMBOL(tr_source_route);
EXPORT_SYMBOL(tr_type_trans); EXPORT_SYMBOL(tr_type_trans);
#endif #endif
...@@ -462,6 +463,7 @@ EXPORT_SYMBOL(dev_get_by_index); ...@@ -462,6 +463,7 @@ EXPORT_SYMBOL(dev_get_by_index);
EXPORT_SYMBOL(__dev_get_by_index); EXPORT_SYMBOL(__dev_get_by_index);
EXPORT_SYMBOL(dev_get_by_name); EXPORT_SYMBOL(dev_get_by_name);
EXPORT_SYMBOL(__dev_get_by_name); EXPORT_SYMBOL(__dev_get_by_name);
EXPORT_SYMBOL(dev_getbyhwaddr);
EXPORT_SYMBOL(netdev_finish_unregister); EXPORT_SYMBOL(netdev_finish_unregister);
EXPORT_SYMBOL(netdev_set_master); EXPORT_SYMBOL(netdev_set_master);
EXPORT_SYMBOL(eth_type_trans); EXPORT_SYMBOL(eth_type_trans);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment