Index: kernel-acme/Documentation/Configure.help diff -u kernel-acme/Documentation/Configure.help:1.1.1.9 kernel-acme/Documentation/Configure.help:1.1.1.9.2.1 --- kernel-acme/Documentation/Configure.help:1.1.1.9 Tue Nov 6 18:34:52 2001 +++ kernel-acme/Documentation/Configure.help Tue Nov 6 20:13:46 2001 @@ -5082,6 +5082,12 @@ Ethernet, using ordinary Ethernet cards. +ANSI/IEEE 802.2 Data link layer User Interface SAPs (EXPERIMENTAL) +CONFIG_LLC_UI + LLC User Interface SAPs is a Linux socket interface into the + LLC datalink layer. This allows a user to create entire user + space network layers tied to a real SAP. + Frame Diverter (EXPERIMENTAL) CONFIG_NET_DIVERT The Frame Diverter allows you to divert packets from the Index: kernel-acme/include/linux/af_netb.h diff -u /dev/null kernel-acme/include/linux/af_netb.h:1.1.8.1 --- /dev/null Thu Nov 22 23:54:49 2001 +++ kernel-acme/include/linux/af_netb.h Tue Nov 6 20:13:47 2001 @@ -0,0 +1,192 @@ +#ifndef _LINUX_AF_NETB_H +#define _LINUX_AF_NETB_H +/* + * Copyright (c) 1997 by Procom Technology,Inc. + * 2001 by Arnaldo Carvalho de Melo + * + * 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. + */ +/* General definitions */ +#define NETBEUI_MAX_DATALEN (64 * 1024) /* 64K-bytes max data buffer len */ +#define NETBEUI_MAX_NAMES 255 /* Maximum number of local names */ +#define NETBEUI_MAX_ADAPTERS 16 /* Maximum number of local adapters */ +#define NETBEUI_MAX_LINKS 256 /* Maximum number of links to others */ +#define NETBEUI_MAX_SESSIONS 255 /* Maximum number of sessions on a link */ + +#define SOCK_NAME SOCK_RAW /* Socket type for Name registration */ +#define NETBEUI_NAME_LEN 16 + +/* NetBEUI address type definitions */ +typedef enum { + NETBEUI_NAME_UNIQUE = 1, + NETBEUI_NAME_GROUP +} name_type_t; + +struct netbeui_addr { + char name[NETBEUI_NAME_LEN]; + unsigned char reserved; /* Safety region */ + name_type_t name_type; +}; + +/* Maximum permitted length of this structure is MAX_SOCK_ADDR defined in + * 'net/socket.c' */ +struct sockaddr_netbeui { + unsigned short snb_family; /* s.b. AF_NETBEUI */ + struct netbeui_addr snb_addr; +}; + +/* NetBEUI Socket Layer Default Values */ +#define NBSO_DFLT_STO_SEC 0 /* Seconds of 'Send Time Out' value */ +#define NBSO_DFLT_STO_MICSEC 0 /* Micro-Seconds of 'Send Time Out' value */ + +#define NBSO_DFLT_RTO_SEC 0 /* Seconds of 'Receive Time Out' value */ +#define NBSO_DFLT_RTO_MICSEC 0 /* Micro-Seconds of 'Receive Time Out' value */ + +/* NetBEUI ioctls */ +/* From 0x89E0 to 0x89EF are protocol private 'ioctl numbers' *\ + * For more information see SIOCDEVPRIVATE and SIOCPROTOPRIVATE definitions * +\* in 'include/linux/sockios.h' file */ + +/* NetBEUI specific I/O ConTroL calls (ioctl) */ +#define SIOCGSTATE (SIOCPROTOPRIVATE) /* Gets NetBEUI socket state */ +#define SIOCTRIMDATA (SIOCPROTOPRIVATE + 1) /* Trims last bytes of current + message */ +#define SIOCSENDZERO (SIOCPROTOPRIVATE + 2) /* Send a dummy session message with + zero size */ +#define SIOCRUWDGF (SIOCPROTOPRIVATE + 3) /* Removes an UnWanted DataGram + Frames */ +#define NBIOCGSTATUS (SIOCPROTOPRIVATE + 4) /* Gets NetBIOS STATUS of local or + remote node */ +#define NBIOCCONFIG (SIOCPROTOPRIVATE + 5) /* Configures NetBEUI during + running */ +/* NetBEUI 'SIOCGSTATE' ioctl() Constants */ +typedef enum { + NBSO_INIT = 0, + NBSO_LISTENING, + NBSO_RUNNING +} nbso_state_t; + +/* NetBEUI Configuration (via NBIOCCONFIG ioctl) */ +/* NetBEUI Configuration request constants */ +#define NETBEUI_UNBIND_FLAG_SAFE 0 /* Unbinds device only if no connection + exist on it */ +#define NETBEUI_UNBIND_FLAG_DROP 1 /* Drops connections that exist on device + and then unbinds it */ +#define NETBEUI_CFGCMD_NIF_UNBIND 0 +#define NETBEUI_CFGCMD_NIF_BIND 1 +#define NETBEUI_CFGCMD_DROP_SESS 2 +#define NETBEUI_CFGCMD_DROP_LINK 3 + +/* NetBEUI Configuration request data structure */ +struct netbeui_cfg { + unsigned short command; /* NETBEUI_CFGCMD_... */ + char nif_name[IFNAMSIZ]; + unsigned char reserved; /* Safety region, always must be zero */ + unsigned char flag; /* NETBEUI_UNBIND_FLAG_... */ + int ln_num; /* 0 <= ln_num < NETBEUI_MAX_LINKS */ + int sn_num; /* 0 < sn_num < NETBEUI_MAX_SESSIONS */ +}; + +/* NetBEUI setsockopt / getsockopt */ +/* + * NetBEUI specific option names for setsockopt() & getsockopt() + * + * Note: Remainder of option names defined in 'asm/socket.h' from 1 to 15 + */ +#define SO_URGENTACK 106 +#define SO_NBPARAM 107 + +/* + * NetBEUI configurable parameters use by SO_NBPARAM + * + * Note: Setting value of a parameter to zero means no change to the + * current value. + */ +struct netbeui_config { + __u8 inactivity_timeout; + __u8 transmit_timeout; + __u8 transmit_count; + __u8 resource_timeout; + __u8 data_ack_timeout; +}; + +/* Definition of default and maximum value of NetBIOS configurable parameters */ +#define NETBEUI_DFLT_LINK_INACT_TMOUT 30 /* Unit is second */ +#define NETBEUI_MAX_LINK_INACT_TMOUT 255 /* Unit is second */ +#define NETBEUI_DFLT_TX_TMOUT 1 /* Unit is 1/2 second */ +#define NETBEUI_MAX_TX_TMOUT 10 /* Unit is 1/2 second */ +#define NETBEUI_DFLT_TX_COUNT 6 +#define NETBEUI_MAX_TX_COUNT 10 +#define NETBEUI_DFLT_RSRC_TMOUT 1 /* Unit is 1/10 second */ +#define NETBEUI_MAX_RSRC_TMOUT 10 /* Unit is 1/10 second */ +#define NETBEUI_DFLT_DATA_ACK_TMOUT 1 /* Unit is 1/10 second */ +#define NETBEUI_MAX_DATA_ACK_TMOUT 10 /* Unit is 1/10 second */ + +/* NetBEUI STATUS Service */ +/* NetBEUI status service constants */ +#define NETBEUI_MIN_STATUS_BUFF_LEN 60 +#define NETBEUI_MAX_STATUS_BUFF_LEN (NETBEUI_MIN_STATUS_BUFF_LEN + \ + 18 * NETBEUI_MAX_NAMES) +/* NetBEUI STATUS request data structure */ +struct netbeui_status { + char called_name[NETBEUI_NAME_LEN]; + char reserved; /* Safety region, always must be zero */ + int buff_len; + char status_buff[0]; /* A dynamic length array for status information */ +}; + +/* Adapter types in status information */ +enum { + NETBEUI_3174_PEER = 0xFB, + NETBEUI_IBM_FDDI, + NETBEUI_ETHERNET, + NETBEUI_PC_NETWORK, + NETBEUI_TOKEN_RING +}; + +/* Name structure in status information */ +struct nb_status_names { + char name[NETBEUI_NAME_LEN]; + __u8 name_number; + __u8 name_status; +}; + +/* STATUS information structure */ +typedef struct { + __u8 adptr_addr[6]; + __u8 sftwr_release_no; + __u8 zero; + struct { + __u8 adptr_type; + __u8 sftwr_level; + } adptr_type_AND_sftwr_level; + __u16 duration; + __u16 no_rx_FRMR; /* NOT SUPPORTED YET */ + __u16 no_tx_FRMR; /* NOT SUPPORTED YET */ + __u16 no_rx_Iformat_LPDUs; /* NOT SUPPORTED YET */ + __u16 no_abrtd_transmissions; /* NOT SUPPORTED YET */ + __u32 no_succ_tx_packets; /* NOT SUPPORTED YET */ + __u32 no_succ_rx_packets; /* NOT SUPPORTED YET */ + __u16 no_tx_Iformat_LPDUs; /* NOT SUPPORTED YET */ + __u16 lost_data_OR_buff_fails; /* NOT SUPPORTED YET */ + __u16 no_DLC_T1_expired; /* NOT SUPPORTED YET */ + __u16 no_DLC_Ti_expired; /* NOT SUPPORTED YET */ + __u32 ext_status_inf_addr; /* NOT SUPPORTED YET */ + __u16 no_free_NCBs; /* NOT SUPPORTED YET */ + __u16 config_max_NCBs; /* NOT SUPPORTED YET */ + __u16 max_no_NCBs; /* NOT SUPPORTED YET */ + __u16 local_busy_OR_buff_fails; /* NOT SUPPORTED YET */ + __u16 max_dgram_packet_size; + __u16 no_pend_sess; /* NOT SUPPORTED YET */ + __u16 config_max_pend_sess; /* NOT SUPPORTED YET */ + __u16 max_no_pend_sess; + __u16 max_size_sess_data_packet; + __u16 no_names_in_local_name_tbl; + struct nb_status_names local_names[NETBEUI_MAX_NAMES]; +} nb_status_buffer_t; +#endif /* _LINUX_AF_NETB_H */ Index: kernel-acme/include/linux/brlock.h diff -u kernel-acme/include/linux/brlock.h:1.1.1.2 kernel-acme/include/linux/brlock.h:1.1.1.2.4.1 --- kernel-acme/include/linux/brlock.h:1.1.1.2 Wed Sep 26 19:05:08 2001 +++ kernel-acme/include/linux/brlock.h Tue Nov 6 20:13:47 2001 @@ -28,13 +28,15 @@ * load-locked/store-conditional cpus (ALPHA/MIPS/PPC) and * compare-and-swap cpus (Sparc64). So we control which * 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. */ enum brlock_indices { BR_GLOBALIRQ_LOCK, BR_NETPROTO_LOCK, - + BR_LLC_LOCK, __BR_END }; Index: kernel-acme/include/linux/dextab.h diff -u /dev/null kernel-acme/include/linux/dextab.h:1.1.8.1 --- /dev/null Thu Nov 22 23:54:49 2001 +++ kernel-acme/include/linux/dextab.h Tue Nov 6 20:13:47 2001 @@ -0,0 +1,58 @@ +#ifndef __LINUX_DEXTAB_H +#define __LINUX_DEXTAB_H +/* + * dextab.h - Contains definition of dynamically expandable tables + * + * Copyright (c) 1997 by Procom Technology,Inc. + * 2001 by Arnaldo Carvalho de Melo + * + * 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 + +/* Dynamically expandable table structure */ +struct dex_table { + void ** addr; + __u16 size; + __u16 reserved; + __u16 max_size; + __u16 count; + spinlock_t lock; +}; +typedef struct dex_table dextab_t; + +#define DEXTAB_NOFREE_ENTRY 0 +#define DEXTAB_FREE_ENTRY 1 + +#define dextab_add_entry(tbl, entry_size) \ + dextab_add_insert_entry(tbl, entry_size, NULL) +#define dextab_insert_entry(tbl, entry) dextab_add_insert_entry(tbl, 0, entry) +#define dextab_remove_index(tbl, entry_index) \ + dextab_remove_delete_index(tbl, entry_index, DEXTAB_FREE_ENTRY) +#define dextab_delete_index(tbl, entry_index) \ + dextab_remove_delete_index(tbl, entry_index, DEXTAB_NOFREE_ENTRY) +#define __dextab_delete_index(tbl, entry_index) \ + __dextab_remove_delete_index(tbl, entry_index, DEXTAB_NOFREE_ENTRY) +#define dextab_remove_entry(tbl, entry) \ + dextab_remove_delete_index(tbl, dextab_entry_index(tbl, entry), \ + DEXTAB_FREE_ENTRY) +#define dextab_delete_entry(tbl, entry) \ + dextab_remove_delete_index(tbl, dextab_entry_index(tbl, entry), \ + DEXTAB_NOFREE_ENTRY) +/* Dynamically expandable table interface */ +extern void dextab_init(dextab_t *tbl, int reserved, int max_size); +extern int dextab_add_insert_entry(dextab_t *tbl, int entry_size, void *entry); +extern void __dextab_remove_delete_index(dextab_t *tbl, int entry_index, + int flag); +extern void dextab_remove_delete_index(dextab_t *tbl, int entry_index, + int flag); +extern int dextab_entry_index(dextab_t *tbl, void *entry); +extern inline int __dextab_count_entries(dextab_t *tbl); +extern inline int dextab_count_entries(dextab_t *tbl); +extern void dextab_destruct(dextab_t *tbl); +#endif __LINUX_DEXTAB_H Index: kernel-acme/include/linux/llc.h diff -u /dev/null kernel-acme/include/linux/llc.h:1.1.4.6 --- /dev/null Thu Nov 22 23:54:49 2001 +++ kernel-acme/include/linux/llc.h Wed Nov 21 00:19:20 2001 @@ -0,0 +1,96 @@ +#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 + * + * 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) (&sk->protinfo.af_llc) + +extern int llc_ui_init(void); +extern void llc_ui_exit(void); +#endif /* __KERNEL__ */ +#endif /* __LINUX_LLC_H */ Index: kernel-acme/include/linux/netbeui.h diff -u kernel-acme/include/linux/netbeui.h:1.1.1.1 kernel-acme/include/linux/netbeui.h:1.1.1.1.8.1 --- kernel-acme/include/linux/netbeui.h:1.1.1.1 Tue Jun 26 14:34:01 2001 +++ kernel-acme/include/linux/netbeui.h Tue Nov 6 20:13:47 2001 @@ -1,16 +1,587 @@ #ifndef _LINUX_NETBEUI_H #define _LINUX_NETBEUI_H +/* + * Copyright (c) 1997 by Procom Technology,Inc. + * 2001 by Arnaldo Carvalho de Melo + * + * 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 +#include +#include +#include +#include +/* NetBIOS frame commands - listed by functionality */ +#define NETBEUI_ADD_GROUP_NAME_QUERY 0x00 +#define NETBEUI_ADD_NAME_QUERY 0x01 +#define NETBEUI_ADD_NAME_RESPONSE 0x0D +#define NETBEUI_NAME_IN_CONFLICT 0x02 -#include +#define NETBEUI_NAME_QUERY 0x0A +#define NETBEUI_NAME_RECOGNIZED 0x0E +#define NETBEUI_SESSION_ALIVE 0x1F +#define NETBEUI_SESSION_CONFIRM 0x17 +#define NETBEUI_SESSION_END 0x18 +#define NETBEUI_SESSION_INITIALIZE 0x19 -#define NB_NAME_LEN 20 /* Set this properly from the full docs when - I get them */ - -struct sockaddr_netbeui -{ - sa_family snb_family; - char snb_name[NB_NAME_LEN]; - char snb_devhint[IFNAMSIZ]; +#define NETBEUI_DATA_ACK 0x14 +#define NETBEUI_DATA_FIRST_MIDDLE 0x15 +#define NETBEUI_DATAGRAM 0x08 +#define NETBEUI_DATAGRAM_BROADCAST 0x09 +#define NETBEUI_DATA_ONLY_LAST 0x16 +#define NETBEUI_NO_RECEIVE 0x1A +#define NETBEUI_RECEIVE_CONTINUE 0x1C +#define NETBEUI_RECEIVE_OUTSTANDING 0x1B + +#define NETBEUI_STATUS_QUERY 0x03 +#define NETBEUI_STATUS_RESPONSE 0x0F +#define NETBEUI_TERMINATE_TRACE 0x07 +#define NETBEUI_TERMINATE_TRACE2 0x13 + +#define NETBEUI_MAX_COMMAND_CODE 0x1F +#define NETBEUI_MIN_COMMAND_LEN 0x0B + +extern u8 nb_cmd_hdr_len[]; + +/* NetBIOS name service constants */ +typedef enum { + NETBEUI_NAME_INITIAL = 0, + NETBEUI_NAME_ADDWAIT, + NETBEUI_NAME_COLLIDED, + NETBEUI_NAME_ACQUIRED +} name_state_t; + +typedef enum { + NETBEUI_NAME_ADD_NAME = 0, + NETBEUI_NAME_RETRY_TIMEOUT, + NETBEUI_NAME_RESPONSE_TIMEOUT, + NETBEUI_NAME_ADD_NAME_RESPONSE1, + NETBEUI_NAME_ADD_NAME_RESPONSE2, + NETBEUI_NAME_ADD_NAME_QUERY, + NETBEUI_NAME_NAME_CONFLICT, + NETBEUI_NAME_REMOVE_NAME +} name_event_t; + +/* NetBIOS query service constants */ +#define NETBEUI_QUERY_MAX_CACHE_ENTRIES 50 +#define NETBEUI_QUERY_CACHE_LIVING_TIME (5 * 60 * HZ) + +typedef enum { + NETBEUI_QUERY_INITIAL = 0, + NETBEUI_QUERY_QRYWAIT, + NETBEUI_QUERY_FINDWAIT, + NETBEUI_QUERY_RECOGNIZED +} query_state_t; + +typedef enum { + NETBEUI_QUERY_NAME_QUERY = 0, + NETBEUI_QUERY_NAME_FIND, + NETBEUI_QUERY_RETRY_TIMEOUT, + NETBEUI_QUERY_RESPONSE_TIMEOUT, + NETBEUI_QUERY_NAME_RECOGNIZED, + NETBEUI_QUERY_END_QUERY +} query_event_t; + +#define NETBEUI_CALL_TT(data2) ((data2 & 0xFF00) >> 8) +#define NETBEUI_CALL_SS(data2) (data2 & 0x00FF) +#define NETBEUI_CALL_DATA2(tt,ss) (((tt << 8) & 0xFF00) | (ss & 0x00FF)) + +/* NetBIOS session service constants */ +typedef enum { + NETBEUI_SESS_INITIAL = 0, + NETBEUI_SESS_CALLWAIT, + NETBEUI_SESS_CONFWAIT, + NETBEUI_SESS_LISTENWAIT, + NETBEUI_SESS_INITWAIT, + NETBEUI_SESS_CONNECTED, + NETBEUI_SESS_DISCWAIT, + NETBEUI_SESS_CONTWAIT, + NETBEUI_SESS_STANDWAIT, + NETBEUI_SESS_ACKWAIT, + NETBEUI_SESS_RSRCWAIT, + NETBEUI_SESS_NORMAL +} session_state_t; + +typedef enum { + NETBEUI_SESS_CALL = 0, + NETBEUI_SESS_LISTEN, + NETBEUI_SESS_CONFIRM, + NETBEUI_SESS_REJECT, + NETBEUI_SESS_CONNECT, + NETBEUI_SESS_TIMEOUT, + NETBEUI_SESS_ABORT, + NETBEUI_SESS_HANGUP, + NETBEUI_SESS_END, + NETBEUI_SESS_FIRST_MIDDLE_CONT, + NETBEUI_SESS_FIRST_MIDDLE, + NETBEUI_SESS_CONTINUE, + NETBEUI_SESS_NONBLOCK, + NETBEUI_SESS_PAUSE, + NETBEUI_SESS_PAUSE2, + NETBEUI_SESS_RESTART, + NETBEUI_SESS_ONLY_LAST_ACK, + NETBEUI_SESS_ONLY_LAST, + NETBEUI_SESS_DATA_ACKED, + NETBEUI_SESS_RESOURCE, + NETBEUI_SESS_CONN_RETRY, + NETBEUI_SESS_NORM_RETRY, + NETBEUI_SESS_ABORT_SEND, +} session_event_t; + +typedef enum { + NETBEUI_RECV_NORMAL= 0, + NETBEUI_RECV_NO_RECEIVE, + NETBEUI_RECV_RECEIVE_OUTSTANDING +} input_state_t; + +#define NETBEUI_VERSION_1xx 0 +#define NETBEUI_VERSION_2xx 1 + +#define NETBEUI_NACK_NONE 0 +#define NETBEUI_NACK_ABLE 1 + +#define NETBEUI_ORIGIN_CONNECTED 1 +#define NETBEUI_ORIGIN_NORMAL 2 + +#define NETBEUI_IS_ABLE_TO_HANDLE_NACK(hdr) (hdr->data1 & 0x80) +#define NETBIOS_VERSION(hdr) (hdr->data1 & 0x01) +#define NETBEUI_TR_FRAME_LF(hdr) (((hdr->data1) & 0x0E) >> 1) + +#define NETBEUI_RECEIVE_CONTINUE_REQUESTED(hdr) (hdr->data1 & 0x01) +#define NETBEUI_NACK_INDICATOR(hdr) (hdr->data1 & 0x02) +#define NETBEUI_ACK_WITH_DATA_ALLOWED(hdr) (hdr->data1 & 0x04) +#define NETBEUI_ACK_WITH_DATA_INCLUDED(hdr) (hdr->data1 & 0x08) + +#define NETBEUI_REQUEST_RECEIVE_CONTINUE(hdr) (hdr->data1 |= 0x01) +#define NETBEUI_INDICATE_NACK(hdr) (hdr->data1 |= 0x02) +#define NETBEUI_ALLOW_ACK_WITH_DATA(hdr) (hdr->data1 |= 0x04) +#define NETBEUI_INCLUDE_ACK_WITH_DATA(hdr) (hdr->data1 |= 0x08) + +#define NETBEUI_RESYNCH_INDICATOR(hdr) (hdr->data2) + +#define NETBEUI_ACK_FLAG 0x80000000 + +/* NetBIOS link manager constants */ +typedef enum { + NETBEUI_LINK_INITIAL = 0, + NETBEUI_LINK_CONNWAIT, + NETBEUI_LINK_UP +} link_state_t; + +typedef enum { + NETBEUI_LINK_CONN_INDICATE = 0, + NETBEUI_LINK_CONN_REQUEST, + NETBEUI_LINK_DUMMY_CONN, + NETBEUI_LINK_CONN_CONFIRM, + NETBEUI_LINK_CONN_REJECT, + NETBEUI_LINK_RESET_INDICATE, + NETBEUI_LINK_SESSION_ALIVE, + NETBEUI_LINK_DISC_REQUEST, + NETBEUI_LINK_DISC_INDICATE +} link_event_t; + +/* NetBIOS constants */ +#define MIN(a,b) (((a) < (b)) ? (a) : (b)) +#define MAX(a,b) (((a) > (b)) ? (a) : (b)) + +#define NB_SK(sk) (&sk->tp_pinfo.af_netbeui) + +#define MAC_ADDR_LEN 6 + +#define NETBIOS_FUNC_ADDR_4ETH "\x03\x00\x00\x00\x00\x01" +#define NETBIOS_FUNC_ADDR_4TR "\xC0\x00\x00\x00\x00\x80" + +#define NETBEUI_DELIMITER 0xEFFF + +#define NETBEUI_LLC_I_HEADLEN 4 /* 4 bytes for LLC header of I_frames */ +#define NETBEUI_ILEN 0x0E /* NetBEUI I_frame header length */ + +#define NETBEUI_LLC_UI_HEADLEN 3 /* 3 bytes for LLC header of UI_frames */ +#define NETBEUI_UILEN 0x2C /* NetBEUI UI_frame header length */ + +#define NETBEUI_MAC_B_HEADLEN MAX(sizeof(struct ethhdr), sizeof(struct trh_hdr)) + +/* NetBIOS status service constants */ +typedef enum { + NBS_STAT_INITIAL = 0, + NBS_STAT_RESPWAIT +} status_state_t; + +typedef enum { + NBE_STAT_STATUS_QUERY = 0, + NBE_STAT_RETRY_TIMEOUT, + NBE_STAT_RESPONSE_TIMEOUT, + NBE_STAT_STATUS_RESPONSE, + NBE_STAT_INCOMP_RESPONSE +} status_event_t; + +typedef enum { + NO_RESPONSE = 0, + INCOMPLETE_RESPONSE, + USER_BUFFER_OVERFLOW, + COMPLETED_RESPONSE +} status_rc_t; + +struct nb_adapters { + struct net_device *dev[NETBEUI_MAX_ADAPTERS]; + int count; + int autobind; + rwlock_t lock; +}; + +/* NetBIOS name data structure */ +struct nb_name { + struct nb_name * next; + atomic_t refcnt; + name_state_t volatile state; + s32 volatile status; + __u8 name[NETBEUI_NAME_LEN]; + __u8 reserved; + __u8 conflicted; + name_type_t type; + __u8 name_number; + __u32 identifier; + wait_queue_head_t waitq; + struct timer_list timer; + struct sk_buff * skb; + __u16 resp_correlator; + __u16 xmit_correlator; + __u8 retries; + __u8 responses; + __u8 remote_mac[6]; + struct net_device *dev; +}; +typedef struct nb_name name_t; + +/* NetBIOS Query Data Structure */ +struct nb_query { + struct nb_query *next; + query_state_t volatile state; + s32 volatile status; + name_t * calling_name; + __u8 called_name[NETBEUI_NAME_LEN]; + __u8 reserved; + __u8 lsn; + __u16 resp_correlator; + char *mac_buff; + struct net_device **dev_buff; + __u8 buff_len; + __u8 buff_ofs; + __u8 rsn; + __u8 remote_mac[6]; + struct net_device *dev; + __u16 xmit_correlator; + __u8 tr_lfb; /* for Token-Ring support */ + struct timer_list timer; + struct sk_buff *skb; + __u8 retries; + __u8 responses; + wait_queue_head_t waitq; +}; +typedef struct nb_query query_t; + +struct nb_session; +typedef struct nb_session session_t; + +typedef void (*abort_owner_cbt)(struct sock *sk, session_t *session); +typedef void (*session_ready_cbt)(struct sock *sk, session_t *session); + +/* NetBIOS Session Data Structure */ +struct nb_session { + struct nb_session * next; + struct nb_session * prev; + session_state_t volatile state; + s32 volatile status; + atomic_t refcnt; + int link; + struct sock * owner; + name_t * local_name; + name_type_t remote_name_type; + __u8 remote_name[NETBEUI_NAME_LEN]; + __u8 reserved; + __u8 lsn; + __u8 rsn; + __u8 version; + __u8 nack_indicator; + __u8 tr_frame_lf; + __u16 mtu; + __u16 llcmac_ihl; + __u8 urgent_ack; + __u8 users; + __u8 zapped; + struct sk_buff_head back_log; + abort_owner_cbt abort_owner_callback; + session_ready_cbt session_ready_callback; + wait_queue_head_t waitq; + struct timer_list timer; + struct sk_buff * skb; + struct net_device * dev; + __u8 remote_mac[6]; + __u16 xmit_correlator; + __u16 resp_correlator; + __u8 o_nonblock; + __u8 o_noack; + __u8 o_aborted; + __u8 o_no_receive; + __u8 o_receive_continue; + __u8 o_receive_outstanding; + __u8 o_rsrc_origin; + __u8 * o_buff; + __u16 o_buffsize; + __u16 o_size; + __u16 o_txed; + __u16 o_acked; + __u16 r_acked; + __u32 o_total; + __u32 o_ack_correlator; + __u16 i_rcvbuf; + __u16 i_aborted; + input_state_t i_state; + struct sk_buff_head i_skbq; + __u16 i_notacked; + __u16 i_size; + __u32 i_total; +}; + +/* NetBIOS-LLC link data structure */ +struct nb_link { + link_state_t volatile state; + s32 volatile status; + void *llc_handle; + __u32 link; + atomic_t refcnt; + __u8 remote_mac[6]; + struct net_device *dev; + struct sk_buff_head skbq; + __u8 llc_busy; + __u32 iactivity; + __u8 keep_alive; + struct timer_list timer; + dextab_t session_table; + wait_queue_head_t waitq; +}; +typedef struct nb_link link_t; + +/* NetBIOS dataGram data structure */ +struct name_dgrms { + char name[NETBEUI_NAME_LEN]; + struct sk_buff_head frameq; + struct sk_buff *curr_skb; + volatile unsigned char connected; + char *conn_name; + wait_queue_head_t *waitq; + struct name_dgrms **list; + struct name_dgrms *next; + struct name_dgrms *prev; +}; +typedef struct name_dgrms name_dgrms_t; + +/* NetBIOS socket supplement structures, present in struct sock's tp_pinfo */ +struct netbeui_opt { + name_t *name; + union { + struct { + session_t *session; + session_t *backlog; + volatile struct timeval sto; + volatile struct timeval rto; + } st; + struct { + struct sockaddr_netbeui conn_name; + name_dgrms_t *namep; + } dg; + } u; +}; + +/* NetBIOS status data structure */ +struct nb_status { + struct nb_status *next; + __u8 *called_name; + char *user_sbuff; + int sbuff_len; + status_rc_t resp_status; + volatile status_state_t state; + __u8 retries; + int len_rx_info; + __u8 no_rx_names; + __u8 overflowed; + __u16 resp_correlator; + __u8 unicast; + __u8 remote_mac[MAC_ADDR_LEN]; + struct sk_buff *tx_skb; + struct sk_buff *rx_skb; + volatile unsigned char locked; + struct timer_list timer; + wait_queue_head_t waitq; +}; +typedef struct nb_status status_t; + +/* NetBIOS datagram packet header */ +struct nb_dgram { + __u16 length; + __u16 delimiter; + __u8 command; + __u8 data1; + __u16 data2; + __u16 xmit_correlator; + __u16 resp_correlator; + __u8 dest_name[NETBEUI_NAME_LEN]; + __u8 source_name[NETBEUI_NAME_LEN]; +}; +typedef struct nb_dgram dgram_t; + +/* NetBIOS session packet header */ +struct nb_packet { + __u16 length; + __u16 delimiter; + __u8 command; + __u8 data1; + __u16 data2; + __u16 xmit_correlator; + __u16 resp_correlator; + __u8 dest_num; + __u8 source_num; }; +typedef struct nb_packet packet_t; + +/* NetBIOS Configuration Definitions */ +typedef struct netbeui_config config_t; + +extern config_t netbios_config; +extern struct nb_adapters netbeui_adapters; + +#define NETBEUI_INACTIVITY_TIMEOUT (netbios_config.inactivity_timeout * HZ) +#define NETBEUI_TRANSMIT_TIMEOUT (netbios_config.transmit_timeout * (HZ / 2)) +#define NETBEUI_TRANSMIT_COUNT (netbios_config.transmit_count) +#define NETBEUI_RESOURCE_TIMEOUT (netbios_config.resource_timeout * (HZ / 10)) +#define NETBEUI_DATA_ACK_TIMEOUT (netbios_config.data_ack_timeout * (HZ / 10)) + +/* Common functions interface */ +extern inline char *netbeui_funcaddr(struct net_device *dev); +extern unsigned char nbcm_apt_dev(struct net_device *dev); +extern inline int MAC_HEADLEN(struct net_device *dev); +extern inline int LLCMAC_I_HEADLEN(struct net_device *dev); +extern inline int LLCMAC_UI_HEADLEN(int mac_hlen); +extern inline int LLCMAC_UIB_HEADLEN(void); +extern inline int CALC_DG_SKBLEN(int mac_hlen, int user_datalen); + +/* LLC supplement interface */ +extern int nbll_attach_session(session_t *session, struct net_device *dev, + unsigned char *remote_mac); +extern int nbll_link_session(int link); +extern int nbll_isend(int link, struct sk_buff *skb); +extern int nbll_uisend(unsigned char *remote_maccaddr, struct sk_buff *skb); +extern void nbll_detach_session(int link, unsigned char session_no); +extern int nbll_drop_link(int link); +extern dextab_t *nbll_get_link_table(void); +extern link_t *nbll_get_link(int link); +extern inline void nbll_link_put(link_t *nb_link); +extern void nbll_test(void); + +/* Name service interface */ +extern unsigned char *netbeui_dev_name_number_1(struct net_device *dev); +extern int nbns_validate_name(char *name); +extern void nbns_init_name_number_1(struct net_device *adapters[]); +extern name_t *nbns_name_number_1(void); +extern int nbns_add_name(char *name, name_type_t type, name_t **out_name); +extern name_t *nbns_find_name(char *name); +extern inline void nbns_name_hold(name_t *nb_name); +extern inline void nbns_name_put(name_t *nb_name); +extern void nbns_del_name(name_t *name); +extern void nbns_del_identifier(unsigned long id); +extern void nbns_get_add_name_query(struct sk_buff *skb, + unsigned char *remote_mac, int type); +extern void nbns_get_add_name_response(struct sk_buff *skb, + unsigned char *remote_mac); +extern void nbns_get_name_conflict(struct sk_buff *skb); +extern dextab_t *nbns_get_name_table(void); +extern name_t *nbns_get_name_list(void); +extern int nbns_count_names(void); +extern void nbns_test(void); + +/* Query service interface */ +extern void nbqs_get_name_recognized(struct sk_buff *skb, + unsigned char *remote_mac); +extern int nbqs_query_name(char *called_name, name_t *calling_name, + unsigned char lsn, unsigned char *rsn, + unsigned char *lfb, unsigned short *xmit_correlator); +extern int nbqs_find_name(char *called_name, char *mac_buff, + struct net_device **dev_buff, int buff_len); +extern void nbqs_add_rnc(char *name, struct net_device *dev, + unsigned char *mac); +extern void nbqs_delete_rnc(char *name); +extern void nbqs_test(void); + +/* Session service interface */ +extern void nbss_get_name_query(struct sk_buff *skb, unsigned char *remote_mac); +extern void nbss_deliver_frame(session_t *session, struct sk_buff *skb); +extern int nbss_call(name_t *calling_name, char *called_name, + struct sock *owner, abort_owner_cbt itf_abort_owner, + session_t **session_ptr); +extern int nbss_listen(name_t *name, int backlog, struct sock *owner, + abort_owner_cbt itf_abort_owner, + session_ready_cbt itf_session_ready); +extern int nbss_listen_bh(name_t *nb_name, int backlog, struct sock *owner, + abort_owner_cbt itf_abort_owner, + session_ready_cbt itf_session_ready); +extern void __nbss_end_listen(name_t *name); +extern int nbss_send(session_t *session, unsigned char *buf, + unsigned short size, unsigned char nonblock, + unsigned char noack); +extern int nbss_send_zero(session_t *session, char *buff); +extern void nbss_abort_send(session_t *session); +extern int nbss_send_ready(session_t *session); +extern int nbss_receive(session_t *session, unsigned char *buf, + unsigned short size, unsigned char nonblock); +extern void nbss_abort_receive(session_t *session); +extern int nbss_receive_ready(session_t *session); +extern int nbss_trim_data(session_t *session); +extern void nbss_hangup(session_t *session); +extern void nbss_abort_session(session_t *session); +extern int nbss_drop_session(int link, int session_no); +extern dextab_t *nbss_get_session_table(int link); +extern void nbss_test(char *service_name); + +/* DataGram service interface */ +extern void nbdg_set_dgbc_mtu(void); +extern int nbdg_remove_unwanted_dgf(name_dgrms_t *namep, int len); +extern void nbdg_register_peername(name_dgrms_t *namep, char *remote_name); +extern void nbdg_deregister_peername(name_dgrms_t *namep); +extern int nbdg_add_name(char *local_name, wait_queue_head_t *wq, + name_dgrms_t **namep); +extern void nbdg_del_name(name_dgrms_t *namep); +extern int nbdg_receive_ready(name_dgrms_t *namep); +extern int nbdg_send(struct sock *sk, char *local_name, char *dest_name, + name_type_t dest_type, struct iovec *iov, int len, + int noblock); +extern int nbdg_receive(name_dgrms_t *namep, char *source_name, char *dest_name, + char *buff, int bufflen, int nonblock); +extern void nbdg_get_datagram(struct sk_buff *skb); +extern void nbdg_get_datagram_broadcast(struct sk_buff *skb); + +/* Socket Supplement Interface */ +extern int nbso_init(void); +extern int nbso_exit(void); + +/* Status service interface */ +void nbst_init_status(void); +int nbst_obtain_status(char *called_name, char *status_buff, int *buff_len); +void nbst_get_status_query(struct sk_buff *skb, unsigned char *remote_mac); +void nbst_get_status_response(struct sk_buff *skb, unsigned char *remote_mac); + +/* PROC entry interface */ +extern int netbeui_proc_init(void); +extern void netbeui_proc_clean(void); -#endif +/* Configuration system interface */ +extern int nbcs_setsockopt(struct socket *sock, int optname, void *optval, + int optlen); +extern int nbcs_getsockopt(struct socket *sock, int optname, void *optval, + int *optlen); +extern int nbcs_ioctl(unsigned int cmd, void *arg); +#endif /* _LINUX_NETBEUI_H */ Index: kernel-acme/include/linux/socket.h diff -u kernel-acme/include/linux/socket.h:1.1.1.2 kernel-acme/include/linux/socket.h:1.1.1.2.6.2 --- kernel-acme/include/linux/socket.h:1.1.1.2 Thu Aug 16 15:49:55 2001 +++ kernel-acme/include/linux/socket.h Thu Nov 22 19:51:49 2001 @@ -156,6 +156,7 @@ #define AF_IRDA 23 /* IRDA sockets */ #define AF_PPPOX 24 /* PPPoX sockets */ #define AF_WANPIPE 25 /* Wanpipe API Sockets */ +#define AF_LLC 26 /* Linux LLC */ #define AF_BLUETOOTH 31 /* Bluetooth sockets */ #define AF_MAX 32 /* For now.. */ @@ -187,6 +188,7 @@ #define PF_IRDA AF_IRDA #define PF_PPPOX AF_PPPOX #define PF_WANPIPE AF_WANPIPE +#define PF_LLC AF_LLC #define PF_BLUETOOTH AF_BLUETOOTH #define PF_MAX AF_MAX @@ -237,6 +239,8 @@ #define SOL_ATM 264 /* ATM layer (cell level) */ #define SOL_AAL 265 /* ATM Adaption Layer (packet level) */ #define SOL_IRDA 266 +#define SOL_NETBEUI 267 +#define SOL_LLC 268 /* IPX options */ #define IPX_TYPE 1 Index: kernel-acme/include/linux/trdevice.h diff -u kernel-acme/include/linux/trdevice.h:1.1.1.1 kernel-acme/include/linux/trdevice.h:1.1.1.1.8.1 --- kernel-acme/include/linux/trdevice.h:1.1.1.1 Tue Jun 26 14:34:03 2001 +++ kernel-acme/include/linux/trdevice.h Tue Nov 6 20:13:47 2001 @@ -31,6 +31,9 @@ extern int tr_header(struct sk_buff *skb, struct net_device *dev, unsigned short type, void *daddr, void *saddr, unsigned len); +extern void tr_source_route(struct sk_buff *skb, + struct trh_hdr *trh, + struct net_device *dev); extern int tr_rebuild_header(struct sk_buff *skb); extern unsigned short tr_type_trans(struct sk_buff *skb, struct net_device *dev); extern struct net_device *init_trdev(struct net_device *dev, int sizeof_priv); Index: kernel-acme/include/net/datalink.h diff -u kernel-acme/include/net/datalink.h:1.1.1.1 kernel-acme/include/net/datalink.h:1.1.1.1.8.1 --- kernel-acme/include/net/datalink.h:1.1.1.1 Tue Jun 26 14:33:58 2001 +++ kernel-acme/include/net/datalink.h Tue Nov 6 20:13:47 2001 @@ -2,15 +2,24 @@ #define _NET_INET_DATALINK_H_ struct datalink_proto { - unsigned short type_len; - unsigned char type[8]; - const char *string_name; - unsigned short header_length; - int (*rcvfunc)(struct sk_buff *, struct net_device *, - struct packet_type *); - void (*datalink_header)(struct datalink_proto *, struct sk_buff *, - unsigned char *); - struct datalink_proto *next; + unsigned short type_len; + unsigned char type[8]; + const char *string_name; + + union { + struct llc_pinfo *llc; + } ll_pinfo; + + struct llc_sc_info *llc_sc; + struct sock *sock; + + unsigned short header_length; + + int (*rcvfunc)(struct sk_buff *, struct net_device *, + struct packet_type *); + void (*datalink_header)(struct datalink_proto *, struct sk_buff *, + unsigned char *); + struct datalink_proto *next; }; #endif Index: kernel-acme/include/net/llc_actn.h diff -u /dev/null kernel-acme/include/net/llc_actn.h:1.1.8.4 --- /dev/null Thu Nov 22 23:54:51 2001 +++ kernel-acme/include/net/llc_actn.h Sun Nov 11 15:29:45 2001 @@ -0,0 +1,48 @@ +#ifndef LLC_ACTN_H +#define LLC_ACTN_H +/* + * Copyright (c) 1997 by Procom Technology,Inc. + * 2001 by Arnaldo Carvalho de Melo + * + * 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_INCREMENT_RETRY_CNT_BY_1 3 +#define LLC_STATION_AC_SET_XID_R_CNT_0 4 +#define LLC_STATION_AC_INCREMENT_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 */ Index: kernel-acme/include/net/llc_c_ac.h diff -u /dev/null kernel-acme/include/net/llc_c_ac.h:1.1.8.5 --- /dev/null Thu Nov 22 23:54:51 2001 +++ kernel-acme/include/net/llc_c_ac.h Thu Nov 15 00:26:02 2001 @@ -0,0 +1,254 @@ +#ifndef LLC_C_AC_H +#define LLC_C_AC_H +/* + * Copyright (c) 1997 by Procom Technology,Inc. + * 2001 by Arnaldo Carvalho de Melo + * + * 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_CLEAR_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_CLEAR_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_INCREMENT_BY_1 63 +#define LLC_CONN_AC_Vr_SET_0 64 +#define LLC_CONN_AC_Vr_INCREMENT_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 */ Index: kernel-acme/include/net/llc_c_ev.h diff -u /dev/null kernel-acme/include/net/llc_c_ev.h:1.1.8.4 --- /dev/null Thu Nov 22 23:54:51 2001 +++ kernel-acme/include/net/llc_c_ev.h Thu Nov 15 00:26:02 2001 @@ -0,0 +1,323 @@ +#ifndef LLC_C_EV_H +#define LLC_C_EV_H +/* + * Copyright (c) 1997 by Procom Technology,Inc. + * 2001 by Arnaldo Carvalho de Melo + * + * 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 */ Index: kernel-acme/include/net/llc_c_st.h diff -u /dev/null kernel-acme/include/net/llc_c_st.h:1.1.8.2 --- /dev/null Thu Nov 22 23:54:51 2001 +++ kernel-acme/include/net/llc_c_st.h Wed Nov 7 14:38:54 2001 @@ -0,0 +1,48 @@ +#ifndef LLC_C_ST_H +#define LLC_C_ST_H +/* + * Copyright (c) 1997 by Procom Technology,Inc. + * 2001 by Arnaldo Carvalho de Melo + * + * 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 /* disconnected, 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_REJECT 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_REJECT 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 */ Index: kernel-acme/include/net/llc_conn.h diff -u /dev/null kernel-acme/include/net/llc_conn.h:1.1.8.6 --- /dev/null Thu Nov 22 23:54:51 2001 +++ kernel-acme/include/net/llc_conn.h Wed Nov 21 19:42:23 2001 @@ -0,0 +1,157 @@ +#ifndef LLC_CONN_H +#define LLC_CONN_H +/* + * Copyright (c) 1997 by Procom Technology,Inc. + * + * 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 +#include + +#define 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 */ + 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 data_conn_req + (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) (&sk->protinfo.core_llc) + +#define sock_list_entry(ptr,type,entry) ((struct sock *)(((char *) \ + list_entry(ptr, type, entry)) - \ + offsetof(struct sock, 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 void 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_conn(struct llc_sap *sap, + struct llc_addr *remote_addr, + struct llc_addr *local_addr); +extern u8 llc_data_accept_state(u8 state); +extern void llc_build_offset_table(void); +#endif /* LLC_CONN_H */ Index: kernel-acme/include/net/llc_evnt.h diff -u /dev/null kernel-acme/include/net/llc_evnt.h:1.1.8.4 --- /dev/null Thu Nov 22 23:54:51 2001 +++ kernel-acme/include/net/llc_evnt.h Sun Nov 11 15:29:45 2001 @@ -0,0 +1,93 @@ +#ifndef LLC_EVNT_H +#define LLC_EVNT_H +/* + * Copyright (c) 1997 by Procom Technology,Inc. + * 2001 by Arnaldo Carvalho de Melo + * + * 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 */ Index: kernel-acme/include/net/llc_if.h diff -u /dev/null kernel-acme/include/net/llc_if.h:1.1.8.4 --- /dev/null Thu Nov 22 23:54:52 2001 +++ kernel-acme/include/net/llc_if.h Wed Nov 21 00:19:21 2001 @@ -0,0 +1,152 @@ +#ifndef LLC_IF_H +#define LLC_IF_H +/* + * Copyright (c) 1997 by Procom Technology,Inc. + * 2001 by Arnaldo Carvalho de Melo + * + * 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 */ +#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[6]; +}; + +/* 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 */ + 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 local_sap); +extern void llc_sap_close(struct llc_sap *sap); +#endif /* LLC_IF_H */ Index: kernel-acme/include/net/llc_mac.h diff -u /dev/null kernel-acme/include/net/llc_mac.h:1.1.8.1 --- /dev/null Thu Nov 22 23:54:52 2001 +++ kernel-acme/include/net/llc_mac.h Tue Nov 6 20:13:48 2001 @@ -0,0 +1,40 @@ +#ifndef LLC_MAC_H +#define LLC_MAC_H +/* + * Copyright (c) 1997 by Procom Technology,Inc. + * 2001 by Arnaldo Carvalho de Melo + * + * 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 MAC_ADDR_LEN 6 + +/* 802.3/Ethernet MAC structure */ +typedef struct { + u8 da[MAC_ADDR_LEN]; /* destination address */ + u8 sa[MAC_ADDR_LEN]; /* source address */ + u16 lpdu_len; /* length of "information" */ +} ieee_802_3_mac_hdr_t; + +/* Token-ring MAC structure */ +typedef struct { + u8 ac; /* access control */ + u8 fc; /* frame control */ + u8 da[MAC_ADDR_LEN]; /* destination address */ + u8 sa[MAC_ADDR_LEN]; /* source address */ + u16 rcf; /* route control field */ + u16 rseg[8]; /* routing registers */ +} token_ring_mac_hdr_t; + +/* Defines MAC-layer interface to LLC layer */ +extern u16 mac_send_pdu(struct sk_buff *skb); +extern int mac_indicate(struct sk_buff *skb, struct net_device *dev, + struct packet_type *pt); +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 */ Index: kernel-acme/include/net/llc_main.h diff -u /dev/null kernel-acme/include/net/llc_main.h:1.1.8.3 --- /dev/null Thu Nov 22 23:54:52 2001 +++ kernel-acme/include/net/llc_main.h Wed Nov 7 14:38:54 2001 @@ -0,0 +1,69 @@ +#ifndef LLC_MAIN_H +#define LLC_MAIN_H +/* + * Copyright (c) 1997 by Procom Technology, Inc. + * 2001 by Arnaldo Carvalho de Melo + * + * 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 u16 llc_check_init(void); +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 */ Index: kernel-acme/include/net/llc_pdu.h diff -u /dev/null kernel-acme/include/net/llc_pdu.h:1.1.8.4 --- /dev/null Thu Nov 22 23:54:52 2001 +++ kernel-acme/include/net/llc_pdu.h Thu Nov 22 20:58:31 2001 @@ -0,0 +1,257 @@ +#ifndef LLC_PDU_H +#define LLC_PDU_H +/* + * Copyright (c) 1997 by Procom Technology,Inc. + * 2001 by Arnaldo Carvalho de Melo + * + * 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 GLOBAL_SAP 0xFF +#define 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 RDE_SAP 0xA6 /* route ... */ + +/* SAP field bit masks */ +#define ISO_RESERVED_SAP 0x02 +#define SAP_GROUP_DSAP 0x01 +#define 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 XID_FMT_ID 0x81 /* first byte must be this */ + +/* LLC types/classes identifier (byte 2) */ +#define XID_CLASS_ZEROS_MASK 0xE0 /* these must be zeros */ +#define XID_CLASS_MASK 0x1F /* AND with byte to get below */ + +#define XID_NULL_CLASS_1 0x01 /* if NULL LSAP...use these */ +#define XID_NULL_CLASS_2 0x03 +#define XID_NULL_CLASS_3 0x05 +#define XID_NULL_CLASS_4 0x07 + +#define XID_NNULL_TYPE_1 0x01 /* if non-NULL LSAP...use these */ +#define XID_NNULL_TYPE_2 0x02 +#define XID_NNULL_TYPE_3 0x04 +#define XID_NNULL_TYPE_1_2 0x03 +#define XID_NNULL_TYPE_1_3 0x05 +#define XID_NNULL_TYPE_2_3 0x06 +#define XID_NNULL_ALL 0x07 + +/* Sender Receive Window (byte 3) */ +#define XID_RW_MASK 0xFE /* AND with value to get below */ + +#define 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 { + u8 dsap; + u8 ssap; + u8 ctrl_1; + u8 ctrl_2; +} pdu_sn_t; + +/* Un-numbered PDU format (3 bytes in length) */ +typedef struct { + u8 dsap; + u8 ssap; + u8 ctrl_1; +} pdu_un_t; + +/* LLC Type 1 XID command/response information fields format */ +typedef struct { + u8 fmt_id; /* always 0x18 for LLC */ + u8 type; /* different if NULL/non-NULL LSAP */ + u8 rw; /* sender receive window */ +} xid_info_t; + +/* LLC Type 2 FRMR response information field format */ +typedef struct { + 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 */ +} 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, 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 */ Index: kernel-acme/include/net/llc_s_ac.h diff -u /dev/null kernel-acme/include/net/llc_s_ac.h:1.1.8.3 --- /dev/null Thu Nov 22 23:54:52 2001 +++ kernel-acme/include/net/llc_s_ac.h Sun Nov 11 15:29:45 2001 @@ -0,0 +1,47 @@ +#ifndef LLC_S_AC_H +#define LLC_S_AC_H +/* + * Copyright (c) 1997 by Procom Technology,Inc. + * 2001 by Arnaldo Carvalho de Melo + * + * 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 */ Index: kernel-acme/include/net/llc_s_ev.h diff -u /dev/null kernel-acme/include/net/llc_s_ev.h:1.1.8.3 --- /dev/null Thu Nov 22 23:54:52 2001 +++ kernel-acme/include/net/llc_s_ev.h Sun Nov 11 15:29:45 2001 @@ -0,0 +1,101 @@ +#ifndef LLC_S_EV_H +#define LLC_S_EV_H +/* + * Copyright (c) 1997 by Procom Technology,Inc. + * 2001 by Arnaldo Carvalho de Melo + * + * 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 */ Index: kernel-acme/include/net/llc_s_st.h diff -u /dev/null kernel-acme/include/net/llc_s_st.h:1.1.8.2 --- /dev/null Thu Nov 22 23:54:52 2001 +++ kernel-acme/include/net/llc_s_st.h Wed Nov 7 14:38:54 2001 @@ -0,0 +1,34 @@ +#ifndef LLC_S_ST_H +#define LLC_S_ST_H +/* + * Copyright (c) 1997 by Procom Technology,Inc. + * 2001 by Arnaldo Carvalho de Melo + * + * 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 */ Index: kernel-acme/include/net/llc_sap.h diff -u /dev/null kernel-acme/include/net/llc_sap.h:1.1.8.3 --- /dev/null Thu Nov 22 23:54:52 2001 +++ kernel-acme/include/net/llc_sap.h Sat Nov 17 16:19:36 2001 @@ -0,0 +1,42 @@ +#ifndef LLC_SAP_H +#define LLC_SAP_H +/* + * Copyright (c) 1997 by Procom Technology,Inc. + * 2001 by Arnaldo Carvalho de Melo + * + * 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 +/* 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 struct llc_sap_state_ev *llc_sap_alloc_ev(struct llc_sap *sap); +extern int 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); +#endif /* LLC_SAP_H */ Index: kernel-acme/include/net/llc_stat.h diff -u /dev/null kernel-acme/include/net/llc_stat.h:1.1.8.3 --- /dev/null Thu Nov 22 23:54:52 2001 +++ kernel-acme/include/net/llc_stat.h Wed Nov 7 14:38:54 2001 @@ -0,0 +1,35 @@ +#ifndef LLC_STAT_H +#define LLC_STAT_H +/* + * Copyright (c) 1997 by Procom Technology,Inc. + * 2001 by Arnaldo Carvalho de Melo + * + * 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 */ Index: kernel-acme/include/net/p8022.h diff -u kernel-acme/include/net/p8022.h:1.1.1.1 kernel-acme/include/net/p8022.h:1.1.1.1.8.1 --- kernel-acme/include/net/p8022.h:1.1.1.1 Tue Jun 26 14:33:57 2001 +++ kernel-acme/include/net/p8022.h Tue Nov 6 20:13:48 2001 @@ -1,7 +1,9 @@ #ifndef _NET_P8022_H #define _NET_P8022_H - -extern struct datalink_proto *register_8022_client(unsigned char type, int (*rcvfunc)(struct sk_buff *, struct net_device *, struct packet_type *)); +extern struct datalink_proto *register_8022_client(unsigned char type, + int (*rcvfunc) + (struct sk_buff *, + struct net_device *, + struct packet_type *)); extern void unregister_8022_client(unsigned char type); - #endif Index: kernel-acme/include/net/sock.h diff -u kernel-acme/include/net/sock.h:1.1.1.4 kernel-acme/include/net/sock.h:1.1.1.4.2.3 --- kernel-acme/include/net/sock.h:1.1.1.4 Tue Nov 6 18:35:05 2001 +++ kernel-acme/include/net/sock.h Sat Nov 17 16:19:36 2001 @@ -24,6 +24,8 @@ * Alan Cox : Eliminate low level recv/recvfrom * David S. Miller : New socket lookup architecture. * Steve Whitehouse: Default routines for sock_ops + * Arnaldo C. Melo : include netbeui_opt in sock->tp_pinfo, + * llc_opt and llc_ui_opt in sock->protinfo * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -94,7 +96,15 @@ #if defined(CONFIG_IRDA) || defined(CONFIG_IRDA_MODULE) #include #endif - +#if defined(CONFIG_NETBEUI) || defined(CONFIG_NETBEUI_MODULE) +#include /* struct netbeui_opt */ +#endif +#if defined(CONFIG_LLC) || defined(CONFIG_LLC_MODULE) +#include /* struct llc_opt */ +#if defined(CONFIG_LLC_UI) +#include /* struct llc_ui_opt */ +#endif +#endif #if defined(CONFIG_ATM) || defined(CONFIG_ATM_MODULE) struct atm_vcc; #endif @@ -584,7 +594,9 @@ #if defined(CONFIG_SPX) || defined (CONFIG_SPX_MODULE) struct spx_opt af_spx; #endif /* CONFIG_SPX */ - +#if defined(CONFIG_NETBEUI) || defined(CONFIG_NETBEUI_MODULE) + struct netbeui_opt af_netbeui; +#endif } tp_pinfo; int err, err_soft; /* Soft holds errors that don't @@ -654,6 +666,12 @@ #endif #if defined(CONFIG_IRDA) || defined(CONFIG_IRDA_MODULE) struct irda_sock *irda; +#endif +#if defined(CONFIG_LLC) || defined(CONFIG_LLC_MODULE) + struct llc_opt core_llc; +#endif +#if defined(CONFIG_LLC_UI) + struct llc_ui_opt af_llc; #endif #if defined(CONFIG_WAN_ROUTER) || defined(CONFIG_WAN_ROUTER_MODULE) struct wanpipe_opt *af_wanpipe; Index: kernel-acme/net/Config.in diff -u kernel-acme/net/Config.in:1.1.1.2 kernel-acme/net/Config.in:1.1.1.2.2.1 --- kernel-acme/net/Config.in:1.1.1.2 Tue Nov 6 18:30:30 2001 +++ kernel-acme/net/Config.in Tue Nov 6 20:13:48 2001 @@ -65,11 +65,12 @@ if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then tristate 'CCITT X.25 Packet Layer (EXPERIMENTAL)' CONFIG_X25 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 + bool ' ANSI/IEEE 802.2 Data link layer User Interface SAPs' CONFIG_LLC_UI + tristate 'The NETBEUI protocol' CONFIG_NETBEUI + fi bool 'Frame Diverter (EXPERIMENTAL)' CONFIG_NET_DIVERT -# if [ "$CONFIG_LLC" = "y" ]; then -# bool ' Netbeui (EXPERIMENTAL)' CONFIG_NETBEUI -# fi if [ "$CONFIG_INET" = "y" ]; then tristate 'Acorn Econet/AUN protocols (EXPERIMENTAL)' CONFIG_ECONET fi Index: kernel-acme/net/Makefile diff -u kernel-acme/net/Makefile:1.1.1.3 kernel-acme/net/Makefile:1.1.1.3.2.1 --- kernel-acme/net/Makefile:1.1.1.3 Tue Nov 6 18:30:30 2001 +++ kernel-acme/net/Makefile Tue Nov 6 20:13:48 2001 @@ -7,7 +7,7 @@ O_TARGET := network.o -mod-subdirs := ipv4/netfilter ipv6/netfilter ipx irda bluetooth atm netlink sched +mod-subdirs := ipv4/netfilter ipv6/netfilter ipx irda bluetooth atm netlink sched llc netbeui export-objs := netsyms.o subdir-y := core ethernet @@ -46,7 +46,8 @@ subdir-$(CONFIG_DECNET) += decnet subdir-$(CONFIG_ECONET) += econet subdir-$(CONFIG_VLAN_8021Q) += 8021q - +subdir-$(CONFIG_LLC) += llc +subdir-$(CONFIG_NETBEUI) += netbeui obj-y := socket.o $(join $(subdir-y), $(patsubst %,/%.o,$(notdir $(subdir-y)))) ifeq ($(CONFIG_NET),y) Index: kernel-acme/net/netsyms.c diff -u kernel-acme/net/netsyms.c:1.1.1.6 kernel-acme/net/netsyms.c:1.1.1.6.2.2 --- kernel-acme/net/netsyms.c:1.1.1.6 Tue Nov 6 18:30:30 2001 +++ kernel-acme/net/netsyms.c Fri Nov 16 13:11:50 2001 @@ -459,6 +459,7 @@ #endif /* CONFIG_INET */ #ifdef CONFIG_TR +EXPORT_SYMBOL(tr_source_route); EXPORT_SYMBOL(tr_type_trans); #endif @@ -477,6 +478,7 @@ EXPORT_SYMBOL(__dev_get_by_index); EXPORT_SYMBOL(dev_get_by_name); EXPORT_SYMBOL(__dev_get_by_name); +EXPORT_SYMBOL(dev_getbyhwaddr); EXPORT_SYMBOL(netdev_finish_unregister); EXPORT_SYMBOL(netdev_set_master); EXPORT_SYMBOL(eth_type_trans); Index: kernel-acme/net/802/p8022.c diff -u kernel-acme/net/802/p8022.c:1.1.1.1 kernel-acme/net/802/p8022.c:1.1.1.1.8.2 --- kernel-acme/net/802/p8022.c:1.1.1.1 Tue Jun 26 14:28:54 2001 +++ kernel-acme/net/802/p8022.c Fri Nov 9 23:42:33 2001 @@ -11,11 +11,10 @@ * matches. The control byte is ignored and handling of such items * is up to the routine passed the frame. * - * Unlike the 802.3 datalink we have a list of 802.2 entries as there - * are multiple protocols to demux. The list is currently short (3 or - * 4 entries at most). The current demux assumes this. + * Unlike the 802.3 datalink we have a list of 802.2 entries as + * there are multiple protocols to demux. The list is currently + * short (3 or 4 entries at most). The current demux assumes this. */ - #include #include #include @@ -25,8 +24,13 @@ #include #include -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 * 802.2 command set, multicast SAP identifiers and non UI @@ -34,91 +38,68 @@ * IP and Appletalk phase 2. See the llc_* routines for * support libraries if your protocol needs these. */ - static struct datalink_proto *find_8022_client(unsigned char type) { - struct datalink_proto *proto; - - for (proto = p8022_list; - ((proto != NULL) && (*(proto->type) != type)); - proto = proto->next) - ; + struct datalink_proto *proto = p8022_list; + while (proto && *(proto->type) != type) + proto = proto->next; 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)); - if (proto != NULL) - { - skb->h.raw += 3; - skb->nh.raw += 3; - skb_pull(skb,3); - return proto->rcvfunc(skb, dev, pt); + if (!proto) { + skb->sk = NULL; + kfree_skb(skb); + goto out; } - - skb->sk = NULL; - kfree_skb(skb); - return 0; + 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, - struct sk_buff *skb, unsigned char *dest_node) + struct sk_buff *skb, unsigned char *dest_node) { - struct net_device *dev = skb->dev; - unsigned char *rawp; + struct net_device *dev = skb->dev; + unsigned char *rawp = skb_push(skb, 3); - rawp = skb_push(skb,3); *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); } - -static struct packet_type p8022_packet_type = -{ - 0, /* MUTTER ntohs(ETH_P_8022),*/ - NULL, /* All devices */ - p8022_rcv, - NULL, - NULL, -}; - -EXPORT_SYMBOL(register_8022_client); -EXPORT_SYMBOL(unregister_8022_client); - -static int __init p8022_init(void) -{ - p8022_packet_type.type=htons(ETH_P_802_2); - dev_add_pack(&p8022_packet_type); - 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_len = 1; - proto->rcvfunc = rcvfunc; - proto->header_length = 3; - proto->datalink_header = p8022_datalink_header; - proto->string_name = "802.2"; - proto->next = p8022_list; - p8022_list = proto; +struct datalink_proto *register_8022_client(unsigned char type, + int (*rcvfunc)(struct sk_buff *, + struct net_device *, + struct packet_type *)) +{ + struct datalink_proto *proto = NULL; + + if (find_8022_client(type)) + goto out; + proto = kmalloc(sizeof(*proto), GFP_ATOMIC); + if (proto) { + proto->type[0] = type; + proto->type_len = 1; + proto->rcvfunc = rcvfunc; + proto->header_length = 3; + proto->datalink_header = p8022_datalink_header; + proto->string_name = "802.2"; + proto->next = p8022_list; + p8022_list = proto; + llc_register_sap(type, p8022_rcv); } - - return proto; +out: return proto; } void unregister_8022_client(unsigned char type) @@ -128,17 +109,18 @@ save_flags(flags); cli(); - - while ((tmp = *clients) != NULL) - { + while (*clients) { + tmp = *clients; if (tmp->type[0] == type) { *clients = tmp->next; kfree(tmp); + llc_unregister_sap(type); break; - } else { - clients = &tmp->next; } + clients = &tmp->next; } - restore_flags(flags); } + +EXPORT_SYMBOL(register_8022_client); +EXPORT_SYMBOL(unregister_8022_client); Index: kernel-acme/net/802/tr.c diff -u kernel-acme/net/802/tr.c:1.1.1.1 kernel-acme/net/802/tr.c:1.1.1.1.8.1 --- kernel-acme/net/802/tr.c:1.1.1.1 Tue Jun 26 14:28:54 2001 +++ kernel-acme/net/802/tr.c Tue Nov 6 20:13:48 2001 @@ -36,7 +36,8 @@ #include #include -static void tr_source_route(struct sk_buff *skb, struct trh_hdr *trh, struct net_device *dev); +void tr_source_route(struct sk_buff *skb, struct trh_hdr *trh, + struct net_device *dev); static void tr_add_rif_info(struct trh_hdr *trh, struct net_device *dev); static void rif_check_expire(unsigned long dummy); @@ -65,7 +66,7 @@ * up a lot. */ -rif_cache rif_table[RIF_TABLE_SIZE]={ NULL, }; +rif_cache rif_table[RIF_TABLE_SIZE]; static spinlock_t rif_lock = SPIN_LOCK_UNLOCKED; @@ -230,7 +231,8 @@ * We try to do source routing... */ -static void tr_source_route(struct sk_buff *skb,struct trh_hdr *trh,struct net_device *dev) +void tr_source_route(struct sk_buff *skb, struct trh_hdr *trh, + struct net_device *dev) { int i, slack; unsigned int hash; Index: kernel-acme/net/core/Makefile diff -u kernel-acme/net/core/Makefile:1.1.1.2 kernel-acme/net/core/Makefile:1.1.1.2.2.1 --- kernel-acme/net/core/Makefile:1.1.1.2 Tue Nov 6 18:30:33 2001 +++ kernel-acme/net/core/Makefile Tue Nov 6 20:13:48 2001 @@ -9,7 +9,7 @@ O_TARGET := core.o -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 @@ -22,6 +22,10 @@ obj-$(CONFIG_FILTER) += filter.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_NET_DIVERT) += dv.o Index: kernel-acme/net/core/ext8022.c diff -u /dev/null kernel-acme/net/core/ext8022.c:1.1.8.1 --- /dev/null Thu Nov 22 23:54:54 2001 +++ kernel-acme/net/core/ext8022.c Tue Nov 6 20:13:48 2001 @@ -0,0 +1,76 @@ +/* + * (ext8022.c) + * + * Copyright (c) 1997 by Procom Technology, Inc. + * 2001 by Arnaldo Carvalho de Melo + * + * 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 +#include +#include +#include + +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); Index: kernel-acme/net/llc/Makefile diff -u /dev/null kernel-acme/net/llc/Makefile:1.1.2.1 --- /dev/null Thu Nov 22 23:54:56 2001 +++ kernel-acme/net/llc/Makefile Tue Nov 6 20:13:48 2001 @@ -0,0 +1,38 @@ +########################################################################### +# 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 +# +# 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 Index: kernel-acme/net/llc/llc_actn.c diff -u /dev/null kernel-acme/net/llc/llc_actn.c:1.1.2.4 --- /dev/null Thu Nov 22 23:54:56 2001 +++ kernel-acme/net/llc/llc_actn.c Sun Nov 11 15:29:45 2001 @@ -0,0 +1,147 @@ +/* + * 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 + * + * 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 +#include +#include +#include +#include +#include + +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, 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[MAC_ADDR_LEN], 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, 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[MAC_ADDR_LEN], 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); + } +} Index: kernel-acme/net/llc/llc_c_ac.c diff -u /dev/null kernel-acme/net/llc/llc_c_ac.c:1.1.2.8 --- /dev/null Thu Nov 22 23:54:56 2001 +++ kernel-acme/net/llc/llc_c_ac.c Thu Nov 22 23:49:28 2001 @@ -0,0 +1,1639 @@ +/* + * 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 + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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; + pdu_sn_t *rx_pdu = (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; + 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_data->conn.dev = skb->dev; + 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_sap *sap = LLC_SK(sk)->sap; + + prim_data->conn.sk = sk; + prim_data->conn.pri = 0; + prim_data->conn.status = ev->status; + prim_data->conn.link = LLC_SK(sk)->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) { + pdu_un_t *rx_pdu = (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; + pdu_un_t *rx_pdu = (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) +{ + pdu_sn_t *rx_pdu = (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; + pdu_sn_t *rx_pdu = (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) { + struct llc_opt *llc = LLC_SK(sk); + struct llc_sap *sap = llc->sap; + pdu_sn_t *rx_pdu = (pdu_sn_t *)&llc->rx_pdu_hdr; + u8 f_bit = 0; + + 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) { + pdu_sn_t *rx_pdu = (pdu_sn_t *)ev->data.pdu.skb->nh.raw; + 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_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) +{ + pdu_sn_t *rx_pdu = (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) +{ + pdu_sn_t *rx_pdu = (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) +{ + pdu_sn_t *rx_pdu = (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; + pdu_sn_t *rx_pdu = (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) +{ + pdu_sn_t *rx_pdu = (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; + + 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, llc->daddr.mac); + 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; + pdu_sn_t *rx_pdu = (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; + pdu_sn_t *rx_pdu = (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) +{ + pdu_sn_t *rx_pdu = (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 + * @conn: 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 + * @conn: 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); +} Index: kernel-acme/net/llc/llc_c_ev.c diff -u /dev/null kernel-acme/net/llc/llc_c_ev.c:1.1.2.6 --- /dev/null Thu Nov 22 23:54:56 2001 +++ kernel-acme/net/llc/llc_c_ev.c Wed Nov 21 19:42:23 2001 @@ -0,0 +1,868 @@ +/* + * 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 + * + * 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 +#include +#include +#include +#include +#include + +#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; + pdu_sn_t *pdu; + + if (LLC_SK(sk)->dev->flags & IFF_LOOPBACK) + return 0; + if (!skb_queue_len(&LLC_SK(sk)->pdu_unack_q)) + return 1; + skb = skb_peek(&LLC_SK(sk)->pdu_unack_q); + pdu = (pdu_sn_t *)skb->nh.raw; + nr1 = LLC_I_GET_NS(pdu); + skb = skb_peek_tail(&LLC_SK(sk)->pdu_unack_q); + pdu = (pdu_sn_t *)skb->nh.raw; + nr2 = LLC_I_GET_NS(pdu); + return !llc_circular_between(nr1, nr, (nr2 + 1) % LLC_2_SEQ_NBR_MODULO); +} + +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) +{ + pdu_un_t *pdu = (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) +{ + pdu_un_t *pdu = (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) +{ + pdu_un_t *pdu = (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) +{ + pdu_sn_t *pdu = (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) +{ + pdu_sn_t *pdu = (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) +{ + pdu_sn_t *pdu = (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) +{ + pdu_sn_t *pdu = (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) +{ + pdu_sn_t * pdu = (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) +{ + pdu_sn_t *pdu = (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) +{ + pdu_sn_t *pdu = (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) +{ + pdu_sn_t *pdu = (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) +{ + pdu_sn_t *pdu = (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) +{ + pdu_sn_t *pdu = (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) +{ + pdu_sn_t *pdu = (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) +{ + pdu_sn_t *pdu = (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) +{ + pdu_sn_t *pdu = (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) +{ + pdu_sn_t *pdu = (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) +{ + pdu_sn_t *pdu = (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) +{ + pdu_sn_t *pdu = (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) +{ + pdu_un_t *pdu = (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) +{ + pdu_sn_t *pdu = (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) +{ + pdu_sn_t *pdu = (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) +{ + pdu_sn_t *pdu = (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) +{ + pdu_sn_t *pdu = (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) +{ + pdu_sn_t *pdu = (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) +{ + pdu_sn_t *pdu = (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) +{ + pdu_sn_t *pdu = (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) +{ + pdu_sn_t *pdu = (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) +{ + pdu_un_t *pdu = (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) +{ + pdu_un_t *pdu = (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; + pdu_sn_t *pdu = (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; + pdu_sn_t *pdu = (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; + pdu_un_t *pdu = (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; + pdu_sn_t *pdu = (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; + pdu_un_t *pdu = (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; + pdu_un_t *pdu = (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; + pdu_sn_t *pdu = (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; + pdu_sn_t *pdu = (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; +} Index: kernel-acme/net/llc/llc_c_st.c diff -u /dev/null kernel-acme/net/llc/llc_c_st.c:1.1.2.4 --- /dev/null Thu Nov 22 23:54:56 2001 +++ kernel-acme/net/llc/llc_c_st.c Thu Nov 15 00:26:02 2001 @@ -0,0 +1,4911 @@ +/* + * llc_c_st.c - This module contains state transition of connection component. + * + * Description of event functions and actions there is in 802.2 LLC standard, + * or in "llc_c_ac.c" and "llc_c_ev.c" modules. + * + * Copyright (c) 1997 by Procom Technology, Inc. + * 2001 by Arnaldo Carvalho de Melo + * + * 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 +#include +#include +#include +#include +#include + +#define LLC_NO_EVENT_QUALIFIERS NULL +#define LLC_NO_TRANSITION_ACTIONS NULL + +/* ----------------- COMMON CONNECTION STATE transitions ----------------- * + * Common transitions for + * LLC_CONN_STATE_NORMAL, + * LLC_CONN_STATE_BUSY, + * LLC_CONN_STATE_REJECT, + * LLC_CONN_STATE_AWAIT, + * LLC_CONN_STATE_AWAIT_BUSY and + * LLC_CONN_STATE_AWAIT_REJECT states + */ +/* State transitions for LLC_CONN_EV_DISC_REQ event */ +static llc_conn_action_t llc_common_actions_1[] = { + llc_conn_ac_send_disc_cmd_p_set_x, + llc_conn_ac_start_ack_timer, + llc_conn_ac_stop_other_timers, + llc_conn_ac_set_retry_cnt_0, + llc_conn_ac_set_cause_flag_1, + NULL +}; + +static struct llc_conn_state_trans llc_common_state_trans_1 = { + llc_conn_ev_disc_req, + LLC_CONN_STATE_D_CONN, + LLC_NO_EVENT_QUALIFIERS, + llc_common_actions_1 +}; + +/* State transitions for LLC_CONN_EV_RESET_REQ event */ +static llc_conn_action_t llc_common_actions_2[] = { + llc_conn_ac_send_sabme_cmd_p_set_x, + llc_conn_ac_start_ack_timer, + llc_conn_ac_stop_other_timers, + llc_conn_ac_set_retry_cnt_0, + llc_conn_ac_set_cause_flag_1, + NULL +}; + +static struct llc_conn_state_trans llc_common_state_trans_2 = { + llc_conn_ev_rst_req, + LLC_CONN_STATE_RESET, + LLC_NO_EVENT_QUALIFIERS, + llc_common_actions_2 +}; + +/* State transitions for LLC_CONN_EV_RX_SABME_CMD_Pbit_SET_X event */ +static llc_conn_action_t llc_common_actions_3[] = { + llc_conn_ac_stop_all_timers, + llc_conn_ac_set_vs_0, + llc_conn_ac_set_vr_0, + llc_conn_ac_send_ua_rsp_f_set_p, + llc_conn_ac_rst_ind, + llc_conn_ac_set_p_flag_0, + llc_conn_ac_set_remote_busy_0, + llc_conn_reset, + NULL +}; + +static struct llc_conn_state_trans llc_common_state_trans_3 = { + llc_conn_ev_rx_sabme_cmd_pbit_set_x, + LLC_CONN_STATE_NORMAL, + LLC_NO_EVENT_QUALIFIERS, + llc_common_actions_3 +}; + +/* State transitions for LLC_CONN_EV_RX_DISC_CMD_Pbit_SET_X event */ +static llc_conn_action_t llc_common_actions_4[] = { + llc_conn_ac_stop_all_timers, + llc_conn_ac_send_ua_rsp_f_set_p, + llc_conn_ac_disc_ind, + llc_conn_disc, + NULL +}; + +static struct llc_conn_state_trans llc_common_state_trans_4 = { + llc_conn_ev_rx_disc_cmd_pbit_set_x, + LLC_CONN_STATE_ADM, + LLC_NO_EVENT_QUALIFIERS, + llc_common_actions_4 +}; + +/* State transitions for LLC_CONN_EV_RX_FRMR_RSP_Fbit_SET_X event */ +static llc_conn_action_t llc_common_actions_5[] = { + llc_conn_ac_send_sabme_cmd_p_set_x, + llc_conn_ac_start_ack_timer, + llc_conn_ac_stop_other_timers, + llc_conn_ac_set_retry_cnt_0, + llc_conn_ac_rst_ind, + llc_conn_ac_set_cause_flag_0, + llc_conn_reset, + NULL +}; + +static struct llc_conn_state_trans llc_common_state_trans_5 = { + llc_conn_ev_rx_frmr_rsp_fbit_set_x, + LLC_CONN_STATE_RESET, + LLC_NO_EVENT_QUALIFIERS, + llc_common_actions_5 +}; + +/* State transitions for LLC_CONN_EV_RX_DM_RSP_Fbit_SET_X event */ +static llc_conn_action_t llc_common_actions_6[] = { + llc_conn_ac_disc_ind, + llc_conn_ac_stop_all_timers, + llc_conn_disc, + NULL +}; + +static struct llc_conn_state_trans llc_common_state_trans_6 = { + llc_conn_ev_rx_dm_rsp_fbit_set_x, + LLC_CONN_STATE_ADM, + LLC_NO_EVENT_QUALIFIERS, + llc_common_actions_6 +}; + +/* State transitions for LLC_CONN_EV_RX_ZZZ_CMD_Pbit_SET_X_INVAL_Nr event */ +static llc_conn_action_t llc_common_actions_7a[] = { + llc_conn_ac_send_frmr_rsp_f_set_x, + llc_conn_ac_start_ack_timer, + llc_conn_ac_stop_other_timers, + llc_conn_ac_set_retry_cnt_0, + NULL +}; + +static struct llc_conn_state_trans llc_common_state_trans_7a = { + llc_conn_ev_rx_zzz_cmd_pbit_set_x_inval_nr, + LLC_CONN_STATE_ERROR, + LLC_NO_EVENT_QUALIFIERS, + llc_common_actions_7a +}; + +/* State transitions for LLC_CONN_EV_RX_I_CMD_Pbit_SET_X_INVAL_Ns event */ +static llc_conn_action_t llc_common_actions_7b[] = { + llc_conn_ac_send_frmr_rsp_f_set_x, + llc_conn_ac_start_ack_timer, + llc_conn_ac_stop_other_timers, + llc_conn_ac_set_retry_cnt_0, + NULL +}; + +static struct llc_conn_state_trans llc_common_state_trans_7b = { + llc_conn_ev_rx_i_cmd_pbit_set_x_inval_ns, + LLC_CONN_STATE_ERROR, + LLC_NO_EVENT_QUALIFIERS, + llc_common_actions_7b +}; + +/* State transitions for LLC_CONN_EV_RX_ZZZ_RSP_Fbit_SET_X_INVAL_Nr event */ +static llc_conn_action_t llc_common_actions_8a[] = { + llc_conn_ac_send_frmr_rsp_f_set_x, + llc_conn_ac_start_ack_timer, + llc_conn_ac_stop_other_timers, + llc_conn_ac_set_retry_cnt_0, + NULL +}; + +static struct llc_conn_state_trans llc_common_state_trans_8a = { + llc_conn_ev_rx_zzz_rsp_fbit_set_x_inval_nr, + LLC_CONN_STATE_ERROR, + LLC_NO_EVENT_QUALIFIERS, + llc_common_actions_8a +}; + +/* State transitions for LLC_CONN_EV_RX_I_RSP_Fbit_SET_X_INVAL_Ns event */ +static llc_conn_action_t llc_common_actions_8b[] = { + llc_conn_ac_send_frmr_rsp_f_set_x, + llc_conn_ac_start_ack_timer, + llc_conn_ac_stop_other_timers, + llc_conn_ac_set_retry_cnt_0, + NULL +}; + +static struct llc_conn_state_trans llc_common_state_trans_8b = { + llc_conn_ev_rx_i_rsp_fbit_set_x_inval_ns, + LLC_CONN_STATE_ERROR, + LLC_NO_EVENT_QUALIFIERS, + llc_common_actions_8b +}; + +/* State transitions for LLC_CONN_EV_RX_BAD_PDU event */ +static llc_conn_action_t llc_common_actions_8c[] = { + llc_conn_ac_send_frmr_rsp_f_set_x, + llc_conn_ac_start_ack_timer, + llc_conn_ac_stop_other_timers, + llc_conn_ac_set_retry_cnt_0, + NULL +}; + +static struct llc_conn_state_trans llc_common_state_trans_8c = { + llc_conn_ev_rx_bad_pdu, + LLC_CONN_STATE_ERROR, + LLC_NO_EVENT_QUALIFIERS, + llc_common_actions_8c +}; + +/* State transitions for LLC_CONN_EV_RX_UA_RSP_Fbit_SET_X event */ +static llc_conn_action_t llc_common_actions_9[] = { + llc_conn_ac_send_frmr_rsp_f_set_x, + llc_conn_ac_start_ack_timer, + llc_conn_ac_stop_other_timers, + llc_conn_ac_set_retry_cnt_0, + NULL +}; + +static struct llc_conn_state_trans llc_common_state_trans_9 = { + llc_conn_ev_rx_ua_rsp_fbit_set_x, + LLC_CONN_STATE_ERROR, + LLC_NO_EVENT_QUALIFIERS, + llc_common_actions_9 +}; + +/* State transitions for LLC_CONN_EV_RX_XXX_RSP_Fbit_SET_1 event */ +#if 0 +static llc_conn_ev_qfyr_t llc_common_ev_qfyrs_10[] = { + llc_conn_ev_qlfy_p_flag_eq_0, + NULL +}; + +static llc_conn_action_t llc_common_actions_10[] = { + llc_conn_ac_send_frmr_rsp_f_set_x, + llc_conn_ac_start_ack_timer, + llc_conn_ac_stop_other_timers, + llc_conn_ac_set_retry_cnt_0, + NULL +}; + +static struct llc_conn_state_trans llc_common_state_trans_10 = { + llc_conn_ev_rx_xxx_rsp_fbit_set_1, + LLC_CONN_STATE_ERROR, + llc_common_ev_qfyrs_10, + llc_common_actions_10 +}; +#endif + +/* State transitions for LLC_CONN_EV_P_TMR_EXP event */ +static llc_conn_ev_qfyr_t llc_common_ev_qfyrs_11a[] = { + llc_conn_ev_qlfy_retry_cnt_gte_n2, + NULL +}; + +static llc_conn_action_t llc_common_actions_11a[] = { + llc_conn_ac_send_sabme_cmd_p_set_x, + llc_conn_ac_start_ack_timer, + llc_conn_ac_stop_other_timers, + llc_conn_ac_set_retry_cnt_0, + llc_conn_ac_set_cause_flag_0, + NULL +}; + +static struct llc_conn_state_trans llc_common_state_trans_11a = { + llc_conn_ev_p_tmr_exp, + LLC_CONN_STATE_RESET, + llc_common_ev_qfyrs_11a, + llc_common_actions_11a +}; + +/* State transitions for LLC_CONN_EV_ACK_TMR_EXP event */ +static llc_conn_ev_qfyr_t llc_common_ev_qfyrs_11b[] = { + llc_conn_ev_qlfy_retry_cnt_gte_n2, + NULL +}; + +static llc_conn_action_t llc_common_actions_11b[] = { + llc_conn_ac_send_sabme_cmd_p_set_x, + llc_conn_ac_start_ack_timer, + llc_conn_ac_stop_other_timers, + llc_conn_ac_set_retry_cnt_0, + llc_conn_ac_set_cause_flag_0, + NULL +}; + +static struct llc_conn_state_trans llc_common_state_trans_11b = { + llc_conn_ev_ack_tmr_exp, + LLC_CONN_STATE_RESET, + llc_common_ev_qfyrs_11b, + llc_common_actions_11b +}; + +/* State transitions for LLC_CONN_EV_REJ_TMR_EXP event */ +static llc_conn_ev_qfyr_t llc_common_ev_qfyrs_11c[] = { + llc_conn_ev_qlfy_retry_cnt_gte_n2, + NULL +}; + +static llc_conn_action_t llc_common_actions_11c[] = { + llc_conn_ac_send_sabme_cmd_p_set_x, + llc_conn_ac_start_ack_timer, + llc_conn_ac_stop_other_timers, + llc_conn_ac_set_retry_cnt_0, + llc_conn_ac_set_cause_flag_0, + NULL +}; + +static struct llc_conn_state_trans llc_common_state_trans_11c = { + llc_conn_ev_rej_tmr_exp, + LLC_CONN_STATE_RESET, + llc_common_ev_qfyrs_11c, + llc_common_actions_11c +}; + +/* State transitions for LLC_CONN_EV_BUSY_TMR_EXP event */ +static llc_conn_ev_qfyr_t llc_common_ev_qfyrs_11d[] = { + llc_conn_ev_qlfy_retry_cnt_gte_n2, + NULL +}; + +static llc_conn_action_t llc_common_actions_11d[] = { + llc_conn_ac_send_sabme_cmd_p_set_x, + llc_conn_ac_start_ack_timer, + llc_conn_ac_stop_other_timers, + llc_conn_ac_set_retry_cnt_0, + llc_conn_ac_set_cause_flag_0, + NULL +}; + +static struct llc_conn_state_trans llc_common_state_trans_11d = { + llc_conn_ev_busy_tmr_exp, + LLC_CONN_STATE_RESET, + llc_common_ev_qfyrs_11d, + llc_common_actions_11d +}; + +/* + * Common dummy state transition; must be last entry for all state + * transition groups + */ +static struct llc_conn_state_trans llc_common_state_trans_n = { + NULL, + 0, + NULL, + NULL +}; + +/* --------------------- LLC_CONN_STATE_ADM transitions -------------------- */ +/* State transitions for LLC_CONN_EV_CONN_REQ event */ +static llc_conn_action_t llc_adm_actions_1[] = { + llc_conn_ac_send_sabme_cmd_p_set_x, + llc_conn_ac_start_ack_timer, + llc_conn_ac_set_retry_cnt_0, + llc_conn_ac_set_s_flag_0, + NULL +}; + +static struct llc_conn_state_trans llc_adm_state_trans_1 = { + llc_conn_ev_conn_req, + LLC_CONN_STATE_SETUP, + LLC_NO_EVENT_QUALIFIERS, + llc_adm_actions_1 +}; + +/* State transitions for LLC_CONN_EV_RX_SABME_CMD_Pbit_SET_X event */ +static llc_conn_action_t llc_adm_actions_2[] = { + llc_conn_ac_send_ua_rsp_f_set_p, + llc_conn_ac_set_vs_0, + llc_conn_ac_set_vr_0, + llc_conn_ac_set_retry_cnt_0, + llc_conn_ac_set_p_flag_0, + llc_conn_ac_set_remote_busy_0, + llc_conn_ac_conn_ind, + NULL +}; + +static struct llc_conn_state_trans llc_adm_state_trans_2 = { + llc_conn_ev_rx_sabme_cmd_pbit_set_x, + LLC_CONN_STATE_NORMAL, + LLC_NO_EVENT_QUALIFIERS, + llc_adm_actions_2 +}; + +/* State transitions for LLC_CONN_EV_RX_DISC_CMD_Pbit_SET_X event */ +static llc_conn_action_t llc_adm_actions_3[] = { + llc_conn_ac_send_dm_rsp_f_set_p, + llc_conn_disc, + NULL +}; + +static struct llc_conn_state_trans llc_adm_state_trans_3 = { + llc_conn_ev_rx_disc_cmd_pbit_set_x, + LLC_CONN_STATE_ADM, + LLC_NO_EVENT_QUALIFIERS, + llc_adm_actions_3 +}; + +/* State transitions for LLC_CONN_EV_RX_XXX_CMD_Pbit_SET_1 event */ +static llc_conn_action_t llc_adm_actions_4[] = { + llc_conn_ac_send_dm_rsp_f_set_1, + llc_conn_disc, + NULL +}; + +static struct llc_conn_state_trans llc_adm_state_trans_4 = { + llc_conn_ev_rx_xxx_cmd_pbit_set_1, + LLC_CONN_STATE_ADM, + LLC_NO_EVENT_QUALIFIERS, + llc_adm_actions_4 +}; + +/* State transitions for LLC_CONN_EV_RX_XXX_YYY event */ +static llc_conn_action_t llc_adm_actions_5[] = { + llc_conn_disc, + NULL +}; + +static struct llc_conn_state_trans llc_adm_state_trans_5 = { + llc_conn_ev_rx_any_frame, + LLC_CONN_OUT_OF_SVC, + LLC_NO_EVENT_QUALIFIERS, + llc_adm_actions_5 +}; + +/* + * Array of pointers; + * one to each transition + */ +static struct llc_conn_state_trans *llc_adm_state_transitions[] = { + &llc_adm_state_trans_1, // Request + &llc_common_state_trans_n, + &llc_common_state_trans_n, // local_busy + &llc_common_state_trans_n, // init_pf_cycle + &llc_common_state_trans_n, // timer + &llc_adm_state_trans_2, // Receive frame + &llc_adm_state_trans_3, + &llc_adm_state_trans_4, + &llc_adm_state_trans_5, + &llc_common_state_trans_n +}; + +/* --------------------- LLC_CONN_STATE_SETUP transitions ----------------- */ +/* State transitions for LLC_CONN_EV_RX_SABME_CMD_Pbit_SET_X event */ +static llc_conn_action_t llc_setup_actions_1[] = { + llc_conn_ac_send_ua_rsp_f_set_p, + llc_conn_ac_set_vs_0, + llc_conn_ac_set_vr_0, + llc_conn_ac_set_s_flag_1, + NULL +}; + +static struct llc_conn_state_trans llc_setup_state_trans_1 = { + llc_conn_ev_rx_sabme_cmd_pbit_set_x, + LLC_CONN_STATE_SETUP, + LLC_NO_EVENT_QUALIFIERS, + llc_setup_actions_1 +}; + +/* State transitions for LLC_CONN_EV_RX_UA_RSP_Fbit_SET_X event */ +static llc_conn_ev_qfyr_t llc_setup_ev_qfyrs_2[] = { + llc_conn_ev_qlfy_p_flag_eq_f, + llc_conn_ev_qlfy_set_status_conn, + NULL +}; + +static llc_conn_action_t llc_setup_actions_2[] = { + llc_conn_ac_stop_ack_timer, + llc_conn_ac_set_vs_0, + llc_conn_ac_set_vr_0, + llc_conn_ac_upd_p_flag, + llc_conn_ac_set_remote_busy_0, + llc_conn_ac_conn_confirm, + NULL +}; + +static struct llc_conn_state_trans llc_setup_state_trans_2 = { + llc_conn_ev_rx_ua_rsp_fbit_set_x, + LLC_CONN_STATE_NORMAL, + llc_setup_ev_qfyrs_2, + llc_setup_actions_2 +}; + +/* State transitions for LLC_CONN_EV_ACK_TMR_EXP event */ +static llc_conn_ev_qfyr_t llc_setup_ev_qfyrs_3[] = { + llc_conn_ev_qlfy_s_flag_eq_1, + llc_conn_ev_qlfy_set_status_conn, + NULL +}; + +static llc_conn_action_t llc_setup_actions_3[] = { + llc_conn_ac_set_p_flag_0, + llc_conn_ac_set_remote_busy_0, + llc_conn_ac_conn_confirm, + NULL +}; + +static struct llc_conn_state_trans llc_setup_state_trans_3 = { + llc_conn_ev_ack_tmr_exp, + LLC_CONN_STATE_NORMAL, + llc_setup_ev_qfyrs_3, + llc_setup_actions_3 +}; + +/* State transitions for LLC_CONN_EV_RX_DISC_CMD_Pbit_SET_X event */ +static llc_conn_ev_qfyr_t llc_setup_ev_qfyrs_4[] = { + llc_conn_ev_qlfy_set_status_disc, + NULL +}; + +static llc_conn_action_t llc_setup_actions_4[] = { + llc_conn_ac_send_dm_rsp_f_set_p, + llc_conn_ac_stop_ack_timer, + llc_conn_ac_conn_confirm, + llc_conn_disc, + NULL +}; + +static struct llc_conn_state_trans llc_setup_state_trans_4 = { + llc_conn_ev_rx_disc_cmd_pbit_set_x, + LLC_CONN_STATE_ADM, + llc_setup_ev_qfyrs_4, + llc_setup_actions_4 +}; + +/* State transitions for LLC_CONN_EV_RX_DM_RSP_Fbit_SET_X event */ +static llc_conn_ev_qfyr_t llc_setup_ev_qfyrs_5[] = { + llc_conn_ev_qlfy_set_status_disc, + NULL +}; + +static llc_conn_action_t llc_setup_actions_5[] = { + llc_conn_ac_stop_ack_timer, + llc_conn_ac_conn_confirm, + llc_conn_disc, + NULL +}; + +static struct llc_conn_state_trans llc_setup_state_trans_5 = { + llc_conn_ev_rx_dm_rsp_fbit_set_x, + LLC_CONN_STATE_ADM, + llc_setup_ev_qfyrs_5, + llc_setup_actions_5 +}; + +/* State transitions for LLC_CONN_EV_ACK_TMR_EXP event */ +static llc_conn_ev_qfyr_t llc_setup_ev_qfyrs_7[] = { + llc_conn_ev_qlfy_retry_cnt_lt_n2, + llc_conn_ev_qlfy_s_flag_eq_0, + NULL +}; + +static llc_conn_action_t llc_setup_actions_7[] = { + llc_conn_ac_send_sabme_cmd_p_set_x, + llc_conn_ac_start_ack_timer, + llc_conn_ac_inc_retry_cnt_by_1, + NULL +}; + +static struct llc_conn_state_trans llc_setup_state_trans_7 = { + llc_conn_ev_ack_tmr_exp, + LLC_CONN_STATE_SETUP, + llc_setup_ev_qfyrs_7, + llc_setup_actions_7 +}; + +/* State transitions for LLC_CONN_EV_ACK_TMR_EXP event */ +static llc_conn_ev_qfyr_t llc_setup_ev_qfyrs_8[] = { + llc_conn_ev_qlfy_retry_cnt_gte_n2, + llc_conn_ev_qlfy_s_flag_eq_0, + llc_conn_ev_qlfy_set_status_failed, + NULL +}; + +static llc_conn_action_t llc_setup_actions_8[] = { + llc_conn_ac_conn_confirm, + llc_conn_disc, + NULL +}; + +static struct llc_conn_state_trans llc_setup_state_trans_8 = { + llc_conn_ev_ack_tmr_exp, + LLC_CONN_STATE_ADM, + llc_setup_ev_qfyrs_8, + llc_setup_actions_8 +}; + +/* + * Array of pointers; + * one to each transition + */ +static struct llc_conn_state_trans *llc_setup_state_transitions[] = { + &llc_common_state_trans_n, // Request + &llc_common_state_trans_n, // local busy + &llc_common_state_trans_n, // init_pf_cycle + &llc_setup_state_trans_3, // Timer + &llc_setup_state_trans_7, + &llc_setup_state_trans_8, + &llc_common_state_trans_n, + &llc_setup_state_trans_1, // Receive frame + &llc_setup_state_trans_2, + &llc_setup_state_trans_4, + &llc_setup_state_trans_5, + &llc_common_state_trans_n +}; + +/* -------------------- LLC_CONN_STATE_NORMAL transitions ------------------ */ +/* State transitions for LLC_CONN_EV_DATA_REQ event */ +static llc_conn_ev_qfyr_t llc_normal_ev_qfyrs_1[] = { + llc_conn_ev_qlfy_remote_busy_eq_0, + llc_conn_ev_qlfy_p_flag_eq_0, + llc_conn_ev_qlfy_last_frame_eq_0, + NULL +}; + +static llc_conn_action_t llc_normal_actions_1[] = { + llc_conn_ac_send_i_as_ack, + llc_conn_ac_start_ack_tmr_if_not_running, + NULL +}; + +static struct llc_conn_state_trans llc_normal_state_trans_1 = { + llc_conn_ev_data_req, + LLC_CONN_STATE_NORMAL, + llc_normal_ev_qfyrs_1, + llc_normal_actions_1 +}; + +/* State transitions for LLC_CONN_EV_DATA_REQ event */ +static llc_conn_ev_qfyr_t llc_normal_ev_qfyrs_2[] = { + llc_conn_ev_qlfy_remote_busy_eq_0, + llc_conn_ev_qlfy_p_flag_eq_0, + llc_conn_ev_qlfy_last_frame_eq_1, + NULL +}; + +static llc_conn_action_t llc_normal_actions_2[] = { + llc_conn_ac_send_i_cmd_p_set_1, + llc_conn_ac_start_p_timer, + NULL +}; + +static struct llc_conn_state_trans llc_normal_state_trans_2 = { + llc_conn_ev_data_req, + LLC_CONN_STATE_NORMAL, + llc_normal_ev_qfyrs_2, + llc_normal_actions_2 +}; + +/* State transitions for LLC_CONN_EV_DATA_REQ event */ +static llc_conn_ev_qfyr_t llc_normal_ev_qfyrs_2_1[] = { + llc_conn_ev_qlfy_remote_busy_eq_1, + llc_conn_ev_qlfy_set_status_remote_busy, + NULL +}; + +static llc_conn_action_t llc_normal_actions_2_1[] = { + NULL +}; + +static struct llc_conn_state_trans llc_normal_state_trans_2_1 = { + llc_conn_ev_data_req, + LLC_CONN_STATE_NORMAL, + llc_normal_ev_qfyrs_2_1, + llc_normal_actions_2_1 +}; + +/* State transitions for LLC_CONN_EV_LOCAL_BUSY_DETECTED event */ +static llc_conn_ev_qfyr_t llc_normal_ev_qfyrs_3[] = { + llc_conn_ev_qlfy_p_flag_eq_0, + NULL +}; + +static llc_conn_action_t llc_normal_actions_3[] = { + llc_conn_ac_rst_sendack_flag, + llc_conn_ac_send_rnr_xxx_x_set_0, + llc_conn_ac_set_data_flag_0, + NULL +}; + +static struct llc_conn_state_trans llc_normal_state_trans_3 = { + llc_conn_ev_local_busy_detected, + LLC_CONN_STATE_BUSY, + llc_normal_ev_qfyrs_3, + llc_normal_actions_3 +}; + +/* State transitions for LLC_CONN_EV_LOCAL_BUSY_DETECTED event */ +static llc_conn_ev_qfyr_t llc_normal_ev_qfyrs_4[] = { + llc_conn_ev_qlfy_p_flag_eq_1, + NULL +}; + +static llc_conn_action_t llc_normal_actions_4[] = { + llc_conn_ac_rst_sendack_flag, + llc_conn_ac_send_rnr_xxx_x_set_0, + llc_conn_ac_set_data_flag_0, + NULL +}; + +static struct llc_conn_state_trans llc_normal_state_trans_4 = { + llc_conn_ev_local_busy_detected, + LLC_CONN_STATE_BUSY, + llc_normal_ev_qfyrs_4, + llc_normal_actions_4 +}; + +/* State transitions for LLC_CONN_EV_RX_I_CMD_Pbit_SET_0_UNEXPD_Ns event */ +static llc_conn_ev_qfyr_t llc_normal_ev_qfyrs_5a[] = { + llc_conn_ev_qlfy_p_flag_eq_0, + NULL +}; + +static llc_conn_action_t llc_normal_actions_5a[] = { + llc_conn_ac_rst_sendack_flag, + llc_conn_ac_send_rej_xxx_x_set_0, + llc_conn_ac_upd_nr_received, + llc_conn_ac_upd_p_flag, + llc_conn_ac_start_rej_timer, + llc_conn_ac_clear_remote_busy_if_f_eq_1, + NULL +}; + +static struct llc_conn_state_trans llc_normal_state_trans_5a = { + llc_conn_ev_rx_i_cmd_pbit_set_0_unexpd_ns, + LLC_CONN_STATE_REJECT, + llc_normal_ev_qfyrs_5a, + llc_normal_actions_5a +}; + +/* State transitions for LLC_CONN_EV_RX_I_RSP_Fbit_SET_0_UNEXPD_Ns event */ +static llc_conn_ev_qfyr_t llc_normal_ev_qfyrs_5b[] = { + llc_conn_ev_qlfy_p_flag_eq_0, + NULL +}; + +static llc_conn_action_t llc_normal_actions_5b[] = { + llc_conn_ac_rst_sendack_flag, + llc_conn_ac_send_rej_xxx_x_set_0, + llc_conn_ac_upd_nr_received, + llc_conn_ac_upd_p_flag, + llc_conn_ac_start_rej_timer, + llc_conn_ac_clear_remote_busy_if_f_eq_1, + NULL +}; + +static struct llc_conn_state_trans llc_normal_state_trans_5b = { + llc_conn_ev_rx_i_rsp_fbit_set_0_unexpd_ns, + LLC_CONN_STATE_REJECT, + llc_normal_ev_qfyrs_5b, + llc_normal_actions_5b +}; + +/* State transitions for LLC_CONN_EV_RX_I_RSP_Fbit_SET_1_UNEXPD_Ns event */ +static llc_conn_ev_qfyr_t llc_normal_ev_qfyrs_5c[] = { + llc_conn_ev_qlfy_p_flag_eq_1, + NULL +}; + +static llc_conn_action_t llc_normal_actions_5c[] = { + llc_conn_ac_rst_sendack_flag, + llc_conn_ac_send_rej_xxx_x_set_0, + llc_conn_ac_upd_nr_received, + llc_conn_ac_upd_p_flag, + llc_conn_ac_start_rej_timer, + llc_conn_ac_clear_remote_busy_if_f_eq_1, + NULL +}; + +static struct llc_conn_state_trans llc_normal_state_trans_5c = { + llc_conn_ev_rx_i_rsp_fbit_set_1_unexpd_ns, + LLC_CONN_STATE_REJECT, + llc_normal_ev_qfyrs_5c, + llc_normal_actions_5c +}; + +/* State transitions for LLC_CONN_EV_RX_I_CMD_Pbit_SET_0_UNEXPD_Ns event */ +static llc_conn_ev_qfyr_t llc_normal_ev_qfyrs_6a[] = { + llc_conn_ev_qlfy_p_flag_eq_1, + NULL +}; + +static llc_conn_action_t llc_normal_actions_6a[] = { + llc_conn_ac_rst_sendack_flag, + llc_conn_ac_send_rej_xxx_x_set_0, + llc_conn_ac_upd_nr_received, + llc_conn_ac_start_rej_timer, + NULL +}; + +static struct llc_conn_state_trans llc_normal_state_trans_6a = { + llc_conn_ev_rx_i_cmd_pbit_set_0_unexpd_ns, + LLC_CONN_STATE_REJECT, + llc_normal_ev_qfyrs_6a, + llc_normal_actions_6a +}; + +/* State transitions for LLC_CONN_EV_RX_I_RSP_Fbit_SET_0_UNEXPD_Ns event */ +static llc_conn_ev_qfyr_t llc_normal_ev_qfyrs_6b[] = { + llc_conn_ev_qlfy_p_flag_eq_1, + NULL +}; + +static llc_conn_action_t llc_normal_actions_6b[] = { + llc_conn_ac_rst_sendack_flag, + llc_conn_ac_send_rej_xxx_x_set_0, + llc_conn_ac_upd_nr_received, + llc_conn_ac_start_rej_timer, + NULL +}; + +static struct llc_conn_state_trans llc_normal_state_trans_6b = { + llc_conn_ev_rx_i_rsp_fbit_set_0_unexpd_ns, + LLC_CONN_STATE_REJECT, + llc_normal_ev_qfyrs_6b, + llc_normal_actions_6b +}; + +/* State transitions for LLC_CONN_EV_RX_I_CMD_Pbit_SET_1_UNEXPD_Ns event */ +static llc_conn_action_t llc_normal_actions_7[] = { + llc_conn_ac_rst_sendack_flag, + llc_conn_ac_send_rej_rsp_f_set_1, + llc_conn_ac_upd_nr_received, + llc_conn_ac_start_rej_timer, + NULL +}; + +static struct llc_conn_state_trans llc_normal_state_trans_7 = { + llc_conn_ev_rx_i_cmd_pbit_set_1_unexpd_ns, + LLC_CONN_STATE_REJECT, + LLC_NO_EVENT_QUALIFIERS, + llc_normal_actions_7 +}; + +/* State transitions for LLC_CONN_EV_RX_I_RSP_Fbit_SET_X event */ +static llc_conn_ev_qfyr_t llc_normal_ev_qfyrs_8a[] = { + llc_conn_ev_qlfy_p_flag_eq_f, + NULL +}; + +static llc_conn_action_t llc_normal_actions_8[] = { + llc_conn_ac_inc_vr_by_1, + llc_conn_ac_data_ind, + llc_conn_ac_upd_p_flag, + llc_conn_ac_upd_nr_received, + llc_conn_ac_clear_remote_busy_if_f_eq_1, + llc_conn_ac_send_ack_if_needed, + NULL +}; + +static struct llc_conn_state_trans llc_normal_state_trans_8a = { + llc_conn_ev_rx_i_rsp_fbit_set_x, + LLC_CONN_STATE_NORMAL, + llc_normal_ev_qfyrs_8a, + llc_normal_actions_8 +}; + +/* State transitions for LLC_CONN_EV_RX_I_CMD_Pbit_SET_0 event */ +static llc_conn_ev_qfyr_t llc_normal_ev_qfyrs_8b[] = { + llc_conn_ev_qlfy_p_flag_eq_0, + NULL +}; + +static struct llc_conn_state_trans llc_normal_state_trans_8b = { + llc_conn_ev_rx_i_cmd_pbit_set_0, + LLC_CONN_STATE_NORMAL, + llc_normal_ev_qfyrs_8b, + llc_normal_actions_8 +}; + +/* State transitions for LLC_CONN_EV_RX_I_RSP_Fbit_SET_0 event */ +static llc_conn_ev_qfyr_t llc_normal_ev_qfyrs_9a[] = { + llc_conn_ev_qlfy_p_flag_eq_1, + NULL +}; + +static llc_conn_action_t llc_normal_actions_9a[] = { + llc_conn_ac_inc_vr_by_1, + llc_conn_ac_upd_nr_received, + llc_conn_ac_data_ind, + llc_conn_ac_send_ack_if_needed, + NULL +}; + +static struct llc_conn_state_trans llc_normal_state_trans_9a = { + llc_conn_ev_rx_i_rsp_fbit_set_0, + LLC_CONN_STATE_NORMAL, + llc_normal_ev_qfyrs_9a, + llc_normal_actions_9a +}; + +/* State transitions for LLC_CONN_EV_RX_I_CMD_Pbit_SET_0 event */ +static llc_conn_ev_qfyr_t llc_normal_ev_qfyrs_9b[] = { + llc_conn_ev_qlfy_p_flag_eq_1, + NULL +}; + +static llc_conn_action_t llc_normal_actions_9b[] = { + llc_conn_ac_inc_vr_by_1, + llc_conn_ac_upd_nr_received, + llc_conn_ac_data_ind, + llc_conn_ac_send_ack_if_needed, + NULL +}; + +static struct llc_conn_state_trans llc_normal_state_trans_9b = { + llc_conn_ev_rx_i_cmd_pbit_set_0, + LLC_CONN_STATE_NORMAL, + llc_normal_ev_qfyrs_9b, + llc_normal_actions_9b +}; + +/* State transitions for LLC_CONN_EV_RX_I_CMD_Pbit_SET_1 event */ +static llc_conn_action_t llc_normal_actions_10[] = { + llc_conn_ac_inc_vr_by_1, + llc_conn_ac_send_ack_rsp_f_set_1, + llc_conn_ac_rst_sendack_flag, + llc_conn_ac_upd_nr_received, + llc_conn_ac_data_ind, + NULL +}; + +static struct llc_conn_state_trans llc_normal_state_trans_10 = { + llc_conn_ev_rx_i_cmd_pbit_set_1, + LLC_CONN_STATE_NORMAL, + LLC_NO_EVENT_QUALIFIERS, + llc_normal_actions_10 +}; + +/* State transitions for * LLC_CONN_EV_RX_RR_CMD_Pbit_SET_0 event */ +static llc_conn_action_t llc_normal_actions_11a[] = { + llc_conn_ac_upd_p_flag, + llc_conn_ac_upd_nr_received, + llc_conn_ac_clear_remote_busy, + NULL +}; + +static struct llc_conn_state_trans llc_normal_state_trans_11a = { + llc_conn_ev_rx_rr_cmd_pbit_set_0, + LLC_CONN_STATE_NORMAL, + LLC_NO_EVENT_QUALIFIERS, + llc_normal_actions_11a +}; + +/* State transitions for LLC_CONN_EV_RX_RR_RSP_Fbit_SET_0 event */ +static llc_conn_action_t llc_normal_actions_11b[] = { + llc_conn_ac_upd_p_flag, + llc_conn_ac_upd_nr_received, + llc_conn_ac_clear_remote_busy, + NULL +}; + +static struct llc_conn_state_trans llc_normal_state_trans_11b = { + llc_conn_ev_rx_rr_rsp_fbit_set_0, + LLC_CONN_STATE_NORMAL, + LLC_NO_EVENT_QUALIFIERS, + llc_normal_actions_11b +}; + +/* State transitions for LLC_CONN_EV_RX_RR_RSP_Fbit_SET_1 event */ +static llc_conn_ev_qfyr_t llc_normal_ev_qfyrs_11c[] = { + llc_conn_ev_qlfy_p_flag_eq_1, + NULL +}; + +static llc_conn_action_t llc_normal_actions_11c[] = { + llc_conn_ac_upd_p_flag, + llc_conn_ac_upd_nr_received, + llc_conn_ac_inc_tx_win_size, + llc_conn_ac_clear_remote_busy, + NULL +}; + +static struct llc_conn_state_trans llc_normal_state_trans_11c = { + llc_conn_ev_rx_rr_rsp_fbit_set_1, + LLC_CONN_STATE_NORMAL, + llc_normal_ev_qfyrs_11c, + llc_normal_actions_11c +}; + +/* State transitions for LLC_CONN_EV_RX_RR_CMD_Pbit_SET_1 event */ +static llc_conn_action_t llc_normal_actions_12[] = { + llc_conn_ac_send_ack_rsp_f_set_1, + llc_conn_ac_upd_nr_received, + llc_conn_ac_adjust_npta_by_rr, + llc_conn_ac_rst_sendack_flag, + llc_conn_ac_clear_remote_busy, + NULL +}; + +static struct llc_conn_state_trans llc_normal_state_trans_12 = { + llc_conn_ev_rx_rr_cmd_pbit_set_1, + LLC_CONN_STATE_NORMAL, + LLC_NO_EVENT_QUALIFIERS, + llc_normal_actions_12 +}; + +/* State transitions for LLC_CONN_EV_RX_RNR_CMD_Pbit_SET_0 event */ +static llc_conn_action_t llc_normal_actions_13a[] = { + llc_conn_ac_upd_p_flag, + llc_conn_ac_upd_nr_received, + llc_conn_ac_set_remote_busy, + NULL +}; + +static struct llc_conn_state_trans llc_normal_state_trans_13a = { + llc_conn_ev_rx_rnr_cmd_pbit_set_0, + LLC_CONN_STATE_NORMAL, + LLC_NO_EVENT_QUALIFIERS, + llc_normal_actions_13a +}; + +/* State transitions for LLC_CONN_EV_RX_RNR_RSP_Fbit_SET_0 event */ +static llc_conn_action_t llc_normal_actions_13b[] = { + llc_conn_ac_upd_p_flag, + llc_conn_ac_upd_nr_received, + llc_conn_ac_set_remote_busy, + NULL +}; + +static struct llc_conn_state_trans llc_normal_state_trans_13b = { + llc_conn_ev_rx_rnr_rsp_fbit_set_0, + LLC_CONN_STATE_NORMAL, + LLC_NO_EVENT_QUALIFIERS, + llc_normal_actions_13b +}; + +/* State transitions for LLC_CONN_EV_RX_RNR_RSP_Fbit_SET_1 event */ +static llc_conn_ev_qfyr_t llc_normal_ev_qfyrs_13c[] = { + llc_conn_ev_qlfy_p_flag_eq_1, + NULL +}; + +static llc_conn_action_t llc_normal_actions_13c[] = { + llc_conn_ac_upd_p_flag, + llc_conn_ac_upd_nr_received, + llc_conn_ac_set_remote_busy, + NULL +}; + +static struct llc_conn_state_trans llc_normal_state_trans_13c = { + llc_conn_ev_rx_rnr_rsp_fbit_set_1, + LLC_CONN_STATE_NORMAL, + llc_normal_ev_qfyrs_13c, + llc_normal_actions_13c +}; + +/* State transitions for LLC_CONN_EV_RX_RNR_CMD_Pbit_SET_1 event */ +static llc_conn_action_t llc_normal_actions_14[] = { + llc_conn_ac_send_rr_rsp_f_set_1, + llc_conn_ac_upd_nr_received, + llc_conn_ac_adjust_npta_by_rnr, + llc_conn_ac_rst_sendack_flag, + llc_conn_ac_set_remote_busy, + NULL +}; + +static struct llc_conn_state_trans llc_normal_state_trans_14 = { + llc_conn_ev_rx_rnr_cmd_pbit_set_1, + LLC_CONN_STATE_NORMAL, + LLC_NO_EVENT_QUALIFIERS, + llc_normal_actions_14 +}; + +/* State transitions for LLC_CONN_EV_RX_REJ_CMD_Pbit_SET_0 event */ +static llc_conn_ev_qfyr_t llc_normal_ev_qfyrs_15a[] = { + llc_conn_ev_qlfy_p_flag_eq_0, + NULL +}; + +static llc_conn_action_t llc_normal_actions_15a[] = { + llc_conn_ac_set_vs_nr, + llc_conn_ac_upd_nr_received, + llc_conn_ac_upd_p_flag, + llc_conn_ac_dec_tx_win_size, + llc_conn_ac_resend_i_xxx_x_set_0, + llc_conn_ac_clear_remote_busy, + NULL +}; + +static struct llc_conn_state_trans llc_normal_state_trans_15a = { + llc_conn_ev_rx_rej_cmd_pbit_set_0, + LLC_CONN_STATE_NORMAL, + llc_normal_ev_qfyrs_15a, + llc_normal_actions_15a +}; + +/* State transitions for LLC_CONN_EV_RX_REJ_RSP_Fbit_SET_X event */ +static llc_conn_ev_qfyr_t llc_normal_ev_qfyrs_15b[] = { + llc_conn_ev_qlfy_p_flag_eq_f, + NULL +}; + +static llc_conn_action_t llc_normal_actions_15b[] = { + llc_conn_ac_set_vs_nr, + llc_conn_ac_upd_nr_received, + llc_conn_ac_upd_p_flag, + llc_conn_ac_dec_tx_win_size, + llc_conn_ac_resend_i_xxx_x_set_0, + llc_conn_ac_clear_remote_busy, + NULL +}; + +static struct llc_conn_state_trans llc_normal_state_trans_15b = { + llc_conn_ev_rx_rej_rsp_fbit_set_x, + LLC_CONN_STATE_NORMAL, + llc_normal_ev_qfyrs_15b, + llc_normal_actions_15b +}; + +/* State transitions for LLC_CONN_EV_RX_REJ_CMD_Pbit_SET_0 event */ +static llc_conn_ev_qfyr_t llc_normal_ev_qfyrs_16a[] = { + llc_conn_ev_qlfy_p_flag_eq_1, + NULL +}; + +static llc_conn_action_t llc_normal_actions_16a[] = { + llc_conn_ac_set_vs_nr, + llc_conn_ac_upd_nr_received, + llc_conn_ac_dec_tx_win_size, + llc_conn_ac_resend_i_xxx_x_set_0, + llc_conn_ac_clear_remote_busy, + NULL +}; + +static struct llc_conn_state_trans llc_normal_state_trans_16a = { + llc_conn_ev_rx_rej_cmd_pbit_set_0, + LLC_CONN_STATE_NORMAL, + llc_normal_ev_qfyrs_16a, + llc_normal_actions_16a +}; + +/* State transitions for LLC_CONN_EV_RX_REJ_RSP_Fbit_SET_0 event */ +static llc_conn_ev_qfyr_t llc_normal_ev_qfyrs_16b[] = { + llc_conn_ev_qlfy_p_flag_eq_1, + NULL +}; + +static llc_conn_action_t llc_normal_actions_16b[] = { + llc_conn_ac_set_vs_nr, + llc_conn_ac_upd_nr_received, + llc_conn_ac_dec_tx_win_size, + llc_conn_ac_resend_i_xxx_x_set_0, + llc_conn_ac_clear_remote_busy, + NULL +}; + +static struct llc_conn_state_trans llc_normal_state_trans_16b = { + llc_conn_ev_rx_rej_rsp_fbit_set_0, + LLC_CONN_STATE_NORMAL, + llc_normal_ev_qfyrs_16b, + llc_normal_actions_16b +}; + +/* State transitions for LLC_CONN_EV_RX_REJ_CMD_Pbit_SET_1 event */ +static llc_conn_action_t llc_normal_actions_17[] = { + llc_conn_ac_set_vs_nr, + llc_conn_ac_upd_nr_received, + llc_conn_ac_dec_tx_win_size, + llc_conn_ac_resend_i_rsp_f_set_1, + llc_conn_ac_clear_remote_busy, + NULL +}; + +static struct llc_conn_state_trans llc_normal_state_trans_17 = { + llc_conn_ev_rx_rej_cmd_pbit_set_1, + LLC_CONN_STATE_NORMAL, + LLC_NO_EVENT_QUALIFIERS, + llc_normal_actions_17 +}; + +/* State transitions for LLC_CONN_EV_INIT_P_F_CYCLE event */ +static llc_conn_ev_qfyr_t llc_normal_ev_qfyrs_18[] = { + llc_conn_ev_qlfy_p_flag_eq_0, + NULL +}; + +static llc_conn_action_t llc_normal_actions_18[] = { + llc_conn_ac_send_rr_cmd_p_set_1, + llc_conn_ac_start_p_timer, + NULL +}; + +static struct llc_conn_state_trans llc_normal_state_trans_18 = { + llc_conn_ev_init_p_f_cycle, + LLC_CONN_STATE_NORMAL, + llc_normal_ev_qfyrs_18, + llc_normal_actions_18 +}; + +/* State transitions for LLC_CONN_EV_P_TMR_EXP event */ +static llc_conn_ev_qfyr_t llc_normal_ev_qfyrs_19[] = { + llc_conn_ev_qlfy_retry_cnt_lt_n2, + NULL +}; + +static llc_conn_action_t llc_normal_actions_19[] = { + llc_conn_ac_rst_sendack_flag, + llc_conn_ac_send_rr_cmd_p_set_1, + llc_conn_ac_rst_vs, + llc_conn_ac_start_p_timer, + llc_conn_ac_inc_retry_cnt_by_1, + NULL +}; + +static struct llc_conn_state_trans llc_normal_state_trans_19 = { + llc_conn_ev_p_tmr_exp, + LLC_CONN_STATE_AWAIT, + llc_normal_ev_qfyrs_19, + llc_normal_actions_19 +}; + +/* State transitions for LLC_CONN_EV_ACK_TMR_EXP event */ +static llc_conn_ev_qfyr_t llc_normal_ev_qfyrs_20a[] = { + llc_conn_ev_qlfy_p_flag_eq_0, + llc_conn_ev_qlfy_retry_cnt_lt_n2, + NULL +}; + +static llc_conn_action_t llc_normal_actions_20a[] = { + llc_conn_ac_rst_sendack_flag, + llc_conn_ac_send_rr_cmd_p_set_1, + llc_conn_ac_rst_vs, + llc_conn_ac_start_p_timer, + llc_conn_ac_inc_retry_cnt_by_1, + NULL +}; + +static struct llc_conn_state_trans llc_normal_state_trans_20a = { + llc_conn_ev_ack_tmr_exp, + LLC_CONN_STATE_AWAIT, + llc_normal_ev_qfyrs_20a, + llc_normal_actions_20a +}; + +/* State transitions for LLC_CONN_EV_BUSY_TMR_EXP event */ +static llc_conn_ev_qfyr_t llc_normal_ev_qfyrs_20b[] = { + llc_conn_ev_qlfy_p_flag_eq_0, + llc_conn_ev_qlfy_retry_cnt_lt_n2, + NULL +}; + +static llc_conn_action_t llc_normal_actions_20b[] = { + llc_conn_ac_rst_sendack_flag, + llc_conn_ac_send_rr_cmd_p_set_1, + llc_conn_ac_rst_vs, + llc_conn_ac_start_p_timer, + llc_conn_ac_inc_retry_cnt_by_1, + NULL +}; + +static struct llc_conn_state_trans llc_normal_state_trans_20b = { + llc_conn_ev_busy_tmr_exp, + LLC_CONN_STATE_AWAIT, + llc_normal_ev_qfyrs_20b, + llc_normal_actions_20b +}; + +/* State transitions for LLC_CONN_EV_TX_BUFF_FULL event */ +static llc_conn_ev_qfyr_t llc_normal_ev_qfyrs_21[] = { + llc_conn_ev_qlfy_p_flag_eq_0, + NULL +}; + +static llc_conn_action_t llc_normal_actions_21[] = { + llc_conn_ac_send_rr_cmd_p_set_1, + llc_conn_ac_start_p_timer, + NULL +}; + +static struct llc_conn_state_trans llc_normal_state_trans_21 = { + llc_conn_ev_tx_buffer_full, + LLC_CONN_STATE_NORMAL, + llc_normal_ev_qfyrs_21, + llc_normal_actions_21 +}; + +/* + * Array of pointers; + * one to each transition + */ +static struct llc_conn_state_trans *llc_normal_state_transitions[] = { + &llc_normal_state_trans_1, // Requests + &llc_normal_state_trans_2, + &llc_normal_state_trans_2_1, + &llc_common_state_trans_1, + &llc_common_state_trans_2, + &llc_common_state_trans_n, + &llc_normal_state_trans_21, + &llc_normal_state_trans_3, // Local busy + &llc_normal_state_trans_4, + &llc_common_state_trans_n, + &llc_normal_state_trans_18, // Init pf cycle + &llc_common_state_trans_n, + &llc_common_state_trans_11a, // Timers + &llc_common_state_trans_11b, + &llc_common_state_trans_11c, + &llc_common_state_trans_11d, + &llc_normal_state_trans_19, + &llc_normal_state_trans_20a, + &llc_normal_state_trans_20b, + &llc_common_state_trans_n, + &llc_normal_state_trans_8b, // Receive frames + &llc_normal_state_trans_9b, + &llc_normal_state_trans_10, + &llc_normal_state_trans_11b, + &llc_normal_state_trans_11c, + &llc_normal_state_trans_5a, + &llc_normal_state_trans_5b, + &llc_normal_state_trans_5c, + &llc_normal_state_trans_6a, + &llc_normal_state_trans_6b, + &llc_normal_state_trans_7, + &llc_normal_state_trans_8a, + &llc_normal_state_trans_9a, + &llc_normal_state_trans_11a, + &llc_normal_state_trans_12, + &llc_normal_state_trans_13a, + &llc_normal_state_trans_13b, + &llc_normal_state_trans_13c, + &llc_normal_state_trans_14, + &llc_normal_state_trans_15a, + &llc_normal_state_trans_15b, + &llc_normal_state_trans_16a, + &llc_normal_state_trans_16b, + &llc_normal_state_trans_17, + &llc_common_state_trans_3, + &llc_common_state_trans_4, + &llc_common_state_trans_5, + &llc_common_state_trans_6, + &llc_common_state_trans_7a, + &llc_common_state_trans_7b, + &llc_common_state_trans_8a, + &llc_common_state_trans_8b, + &llc_common_state_trans_8c, + &llc_common_state_trans_9, + //&llc_common_state_trans_10, + &llc_common_state_trans_n +}; + +/* --------------------- LLC_CONN_STATE_BUSY transitions ------------------- */ +/* State transitions for LLC_CONN_EV_DATA_REQ event */ +static llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_1[] = { + llc_conn_ev_qlfy_remote_busy_eq_0, + llc_conn_ev_qlfy_p_flag_eq_0, + NULL +}; + +static llc_conn_action_t llc_busy_actions_1[] = { + llc_conn_ac_send_i_xxx_x_set_0, + llc_conn_ac_start_ack_tmr_if_not_running, + NULL +}; + +static struct llc_conn_state_trans llc_busy_state_trans_1 = { + llc_conn_ev_data_req, + LLC_CONN_STATE_BUSY, + llc_busy_ev_qfyrs_1, + llc_busy_actions_1 +}; + +/* State transitions for LLC_CONN_EV_DATA_REQ event */ +static llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_2[] = { + llc_conn_ev_qlfy_remote_busy_eq_0, + llc_conn_ev_qlfy_p_flag_eq_1, + NULL +}; + +static llc_conn_action_t llc_busy_actions_2[] = { + llc_conn_ac_send_i_xxx_x_set_0, + llc_conn_ac_start_ack_tmr_if_not_running, + NULL +}; + +static struct llc_conn_state_trans llc_busy_state_trans_2 = { + llc_conn_ev_data_req, + LLC_CONN_STATE_BUSY, + llc_busy_ev_qfyrs_2, + llc_busy_actions_2 +}; + +/* State transitions for LLC_CONN_EV_DATA_REQ event */ +static llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_2_1[] = { + llc_conn_ev_qlfy_remote_busy_eq_1, + llc_conn_ev_qlfy_set_status_remote_busy, + NULL +}; + +static llc_conn_action_t llc_busy_actions_2_1[] = { + NULL +}; + +static struct llc_conn_state_trans llc_busy_state_trans_2_1 = { + llc_conn_ev_data_req, + LLC_CONN_STATE_BUSY, + llc_busy_ev_qfyrs_2_1, + llc_busy_actions_2_1 +}; + +/* State transitions for LLC_CONN_EV_LOCAL_BUSY_CLEARED event */ +static llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_3[] = { + llc_conn_ev_qlfy_data_flag_eq_1, + llc_conn_ev_qlfy_p_flag_eq_0, + NULL +}; + +static llc_conn_action_t llc_busy_actions_3[] = { + llc_conn_ac_send_rej_xxx_x_set_0, + llc_conn_ac_start_rej_timer, + NULL +}; + +static struct llc_conn_state_trans llc_busy_state_trans_3 = { + llc_conn_ev_local_busy_cleared, + LLC_CONN_STATE_REJECT, + llc_busy_ev_qfyrs_3, + llc_busy_actions_3 +}; + +/* State transitions for LLC_CONN_EV_LOCAL_BUSY_CLEARED event */ +static llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_4[] = { + llc_conn_ev_qlfy_data_flag_eq_1, + llc_conn_ev_qlfy_p_flag_eq_1, + NULL +}; + +static llc_conn_action_t llc_busy_actions_4[] = { + llc_conn_ac_send_rej_xxx_x_set_0, + llc_conn_ac_start_rej_timer, + NULL +}; + +static struct llc_conn_state_trans llc_busy_state_trans_4 = { + llc_conn_ev_local_busy_cleared, + LLC_CONN_STATE_REJECT, + llc_busy_ev_qfyrs_4, + llc_busy_actions_4 +}; + +/* State transitions for LLC_CONN_EV_LOCAL_BUSY_CLEARED event */ +static llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_5[] = { + llc_conn_ev_qlfy_data_flag_eq_0, + llc_conn_ev_qlfy_p_flag_eq_0, + NULL +}; + +static llc_conn_action_t llc_busy_actions_5[] = { + llc_conn_ac_send_rr_xxx_x_set_0, + NULL +}; + +static struct llc_conn_state_trans llc_busy_state_trans_5 = { + llc_conn_ev_local_busy_cleared, + LLC_CONN_STATE_NORMAL, + llc_busy_ev_qfyrs_5, + llc_busy_actions_5 +}; + +/* State transitions for LLC_CONN_EV_LOCAL_BUSY_CLEARED event */ +static llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_6[] = { + llc_conn_ev_qlfy_data_flag_eq_0, + llc_conn_ev_qlfy_p_flag_eq_1, + NULL +}; + +static llc_conn_action_t llc_busy_actions_6[] = { + llc_conn_ac_send_rr_xxx_x_set_0, + NULL +}; + +static struct llc_conn_state_trans llc_busy_state_trans_6 = { + llc_conn_ev_local_busy_cleared, + LLC_CONN_STATE_NORMAL, + llc_busy_ev_qfyrs_6, + llc_busy_actions_6 +}; + +/* State transitions for LLC_CONN_EV_LOCAL_BUSY_CLEARED event */ +static llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_7[] = { + llc_conn_ev_qlfy_data_flag_eq_2, + llc_conn_ev_qlfy_p_flag_eq_0, + NULL +}; + +static llc_conn_action_t llc_busy_actions_7[] = { + llc_conn_ac_send_rr_xxx_x_set_0, + NULL +}; + +static struct llc_conn_state_trans llc_busy_state_trans_7 = { + llc_conn_ev_local_busy_cleared, + LLC_CONN_STATE_REJECT, + llc_busy_ev_qfyrs_7, + llc_busy_actions_7 +}; + +/* State transitions for LLC_CONN_EV_LOCAL_BUSY_CLEARED event */ +static llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_8[] = { + llc_conn_ev_qlfy_data_flag_eq_2, + llc_conn_ev_qlfy_p_flag_eq_1, + NULL +}; + +static llc_conn_action_t llc_busy_actions_8[] = { + llc_conn_ac_send_rr_xxx_x_set_0, + NULL +}; + +static struct llc_conn_state_trans llc_busy_state_trans_8 = { + llc_conn_ev_local_busy_cleared, + LLC_CONN_STATE_REJECT, + llc_busy_ev_qfyrs_8, + llc_busy_actions_8 +}; + +/* State transitions for LLC_CONN_EV_RX_I_RSP_Fbit_SET_X_UNEXPD_Ns event */ +static llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_9a[] = { + llc_conn_ev_qlfy_p_flag_eq_f, + NULL +}; + +static llc_conn_action_t llc_busy_actions_9a[] = { + llc_conn_ac_opt_send_rnr_xxx_x_set_0, + llc_conn_ac_upd_p_flag, + llc_conn_ac_upd_nr_received, + llc_conn_ac_set_data_flag_1_if_data_flag_eq_0, + llc_conn_ac_clear_remote_busy_if_f_eq_1, + NULL +}; + +static struct llc_conn_state_trans llc_busy_state_trans_9a = { + llc_conn_ev_rx_i_rsp_fbit_set_x_unexpd_ns, + LLC_CONN_STATE_BUSY, + llc_busy_ev_qfyrs_9a, + llc_busy_actions_9a +}; + +/* State transitions for LLC_CONN_EV_RX_I_CMD_Pbit_SET_0_UNEXPD_Ns event */ +static llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_9b[] = { + llc_conn_ev_qlfy_p_flag_eq_0, + NULL +}; + +static llc_conn_action_t llc_busy_actions_9b[] = { + llc_conn_ac_opt_send_rnr_xxx_x_set_0, + llc_conn_ac_upd_p_flag, + llc_conn_ac_upd_nr_received, + llc_conn_ac_set_data_flag_1_if_data_flag_eq_0, + llc_conn_ac_clear_remote_busy_if_f_eq_1, + NULL +}; + +static struct llc_conn_state_trans llc_busy_state_trans_9b = { + llc_conn_ev_rx_i_cmd_pbit_set_0_unexpd_ns, + LLC_CONN_STATE_BUSY, + llc_busy_ev_qfyrs_9b, + llc_busy_actions_9b +}; + +/* State transitions for LLC_CONN_EV_RX_I_RSP_Fbit_SET_0_UNEXPD_Ns event */ +static llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_10a[] = { + llc_conn_ev_qlfy_p_flag_eq_1, + NULL +}; + +static llc_conn_action_t llc_busy_actions_10a[] = { + llc_conn_ac_opt_send_rnr_xxx_x_set_0, + llc_conn_ac_upd_nr_received, + llc_conn_ac_set_data_flag_1_if_data_flag_eq_0, + NULL +}; + +static struct llc_conn_state_trans llc_busy_state_trans_10a = { + llc_conn_ev_rx_i_rsp_fbit_set_0_unexpd_ns, + LLC_CONN_STATE_BUSY, + llc_busy_ev_qfyrs_10a, + llc_busy_actions_10a +}; + +/* State transitions for LLC_CONN_EV_RX_I_CMD_Pbit_SET_0_UNEXPD_Ns event */ +static llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_10b[] = { + llc_conn_ev_qlfy_p_flag_eq_1, + NULL +}; + +static llc_conn_action_t llc_busy_actions_10b[] = { + llc_conn_ac_opt_send_rnr_xxx_x_set_0, + llc_conn_ac_upd_nr_received, + llc_conn_ac_set_data_flag_1_if_data_flag_eq_0, + NULL +}; + +static struct llc_conn_state_trans llc_busy_state_trans_10b = { + llc_conn_ev_rx_i_cmd_pbit_set_0_unexpd_ns, + LLC_CONN_STATE_BUSY, + llc_busy_ev_qfyrs_10b, + llc_busy_actions_10b +}; + +/* State transitions for LLC_CONN_EV_RX_I_CMD_Pbit_SET_1_UNEXPD_Ns event */ +static llc_conn_action_t llc_busy_actions_11[] = { + llc_conn_ac_send_rnr_rsp_f_set_1, + llc_conn_ac_upd_nr_received, + llc_conn_ac_set_data_flag_1_if_data_flag_eq_0, + NULL +}; + +static struct llc_conn_state_trans llc_busy_state_trans_11 = { + llc_conn_ev_rx_i_cmd_pbit_set_1_unexpd_ns, + LLC_CONN_STATE_BUSY, + LLC_NO_EVENT_QUALIFIERS, + llc_busy_actions_11 +}; + +/* State transitions for LLC_CONN_EV_RX_I_CMD_Pbit_SET_1 event */ +static llc_conn_action_t llc_busy_actions_12[] = { + llc_conn_ac_inc_vr_by_1, + llc_conn_ac_data_ind, + llc_conn_ac_send_rnr_rsp_f_set_1, + llc_conn_ac_upd_nr_received, + llc_conn_ac_stop_rej_tmr_if_data_flag_eq_2, + llc_conn_ac_set_data_flag_0, + NULL +}; + +static struct llc_conn_state_trans llc_busy_state_trans_12 = { + llc_conn_ev_rx_i_cmd_pbit_set_1, + LLC_CONN_STATE_BUSY, + LLC_NO_EVENT_QUALIFIERS, + llc_busy_actions_12 +}; + +/* State transitions for LLC_CONN_EV_RX_I_RSP_Fbit_SET_X event */ +static llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_13a[] = { + llc_conn_ev_qlfy_p_flag_eq_f, + NULL +}; + +static llc_conn_action_t llc_busy_actions_13a[] = { + llc_conn_ac_inc_vr_by_1, + llc_conn_ac_data_ind, + llc_conn_ac_upd_p_flag, + llc_conn_ac_opt_send_rnr_xxx_x_set_0, + llc_conn_ac_upd_nr_received, + llc_conn_ac_stop_rej_tmr_if_data_flag_eq_2, + llc_conn_ac_set_data_flag_0, + llc_conn_ac_clear_remote_busy_if_f_eq_1, + NULL +}; + +static struct llc_conn_state_trans llc_busy_state_trans_13a = { + llc_conn_ev_rx_i_rsp_fbit_set_x, + LLC_CONN_STATE_BUSY, + llc_busy_ev_qfyrs_13a, + llc_busy_actions_13a +}; + +/* State transitions for LLC_CONN_EV_RX_I_CMD_Pbit_SET_0 event */ +static llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_13b[] = { + llc_conn_ev_qlfy_p_flag_eq_0, + NULL +}; + +static llc_conn_action_t llc_busy_actions_13b[] = { + llc_conn_ac_inc_vr_by_1, + llc_conn_ac_data_ind, + llc_conn_ac_upd_p_flag, + llc_conn_ac_opt_send_rnr_xxx_x_set_0, + llc_conn_ac_upd_nr_received, + llc_conn_ac_stop_rej_tmr_if_data_flag_eq_2, + llc_conn_ac_set_data_flag_0, + llc_conn_ac_clear_remote_busy_if_f_eq_1, + NULL +}; + +static struct llc_conn_state_trans llc_busy_state_trans_13b = { + llc_conn_ev_rx_i_cmd_pbit_set_0, + LLC_CONN_STATE_BUSY, + llc_busy_ev_qfyrs_13b, + llc_busy_actions_13b +}; + +/* State transitions for LLC_CONN_EV_RX_I_RSP_Fbit_SET_0 event */ +static llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_14a[] = { + llc_conn_ev_qlfy_p_flag_eq_1, + NULL +}; + +static llc_conn_action_t llc_busy_actions_14a[] = { + llc_conn_ac_inc_vr_by_1, + llc_conn_ac_data_ind, + llc_conn_ac_opt_send_rnr_xxx_x_set_0, + llc_conn_ac_upd_nr_received, + llc_conn_ac_stop_rej_tmr_if_data_flag_eq_2, + llc_conn_ac_set_data_flag_0, + NULL +}; + +static struct llc_conn_state_trans llc_busy_state_trans_14a = { + llc_conn_ev_rx_i_rsp_fbit_set_0, + LLC_CONN_STATE_BUSY, + llc_busy_ev_qfyrs_14a, + llc_busy_actions_14a +}; + +/* State transitions for LLC_CONN_EV_RX_I_CMD_Pbit_SET_0 event */ +static llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_14b[] = { + llc_conn_ev_qlfy_p_flag_eq_1, + NULL +}; + +static llc_conn_action_t llc_busy_actions_14b[] = { + llc_conn_ac_inc_vr_by_1, + llc_conn_ac_data_ind, + llc_conn_ac_opt_send_rnr_xxx_x_set_0, + llc_conn_ac_upd_nr_received, + llc_conn_ac_stop_rej_tmr_if_data_flag_eq_2, + llc_conn_ac_set_data_flag_0, + NULL +}; + +static struct llc_conn_state_trans llc_busy_state_trans_14b = { + llc_conn_ev_rx_i_cmd_pbit_set_0, + LLC_CONN_STATE_BUSY, + llc_busy_ev_qfyrs_14b, + llc_busy_actions_14b +}; + +/* State transitions for LLC_CONN_EV_RX_RR_CMD_Pbit_SET_0 event */ +static llc_conn_action_t llc_busy_actions_15a[] = { + llc_conn_ac_upd_p_flag, + llc_conn_ac_upd_nr_received, + llc_conn_ac_clear_remote_busy, + NULL +}; + +static struct llc_conn_state_trans llc_busy_state_trans_15a = { + llc_conn_ev_rx_rr_cmd_pbit_set_0, + LLC_CONN_STATE_BUSY, + LLC_NO_EVENT_QUALIFIERS, + llc_busy_actions_15a +}; + +/* State transitions for LLC_CONN_EV_RX_RR_RSP_Fbit_SET_0 event */ +static llc_conn_action_t llc_busy_actions_15b[] = { + llc_conn_ac_upd_p_flag, + llc_conn_ac_upd_nr_received, + llc_conn_ac_clear_remote_busy, + NULL +}; + +static struct llc_conn_state_trans llc_busy_state_trans_15b = { + llc_conn_ev_rx_rr_rsp_fbit_set_0, + LLC_CONN_STATE_BUSY, + LLC_NO_EVENT_QUALIFIERS, + llc_busy_actions_15b +}; + +/* State transitions for LLC_CONN_EV_RX_RR_RSP_Fbit_SET_1 event */ +static llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_15c[] = { + llc_conn_ev_qlfy_p_flag_eq_1, + NULL +}; + +static llc_conn_action_t llc_busy_actions_15c[] = { + llc_conn_ac_upd_p_flag, + llc_conn_ac_upd_nr_received, + llc_conn_ac_clear_remote_busy, + NULL +}; + +static struct llc_conn_state_trans llc_busy_state_trans_15c = { + llc_conn_ev_rx_rr_rsp_fbit_set_1, + LLC_CONN_STATE_BUSY, + llc_busy_ev_qfyrs_15c, + llc_busy_actions_15c +}; + +/* State transitions for LLC_CONN_EV_RX_RR_CMD_Pbit_SET_1 event */ +static llc_conn_action_t llc_busy_actions_16[] = { + llc_conn_ac_send_rnr_rsp_f_set_1, + llc_conn_ac_upd_nr_received, + llc_conn_ac_clear_remote_busy, + NULL +}; + +static struct llc_conn_state_trans llc_busy_state_trans_16 = { + llc_conn_ev_rx_rr_cmd_pbit_set_1, + LLC_CONN_STATE_BUSY, + LLC_NO_EVENT_QUALIFIERS, + llc_busy_actions_16 +}; + +/* State transitions for LLC_CONN_EV_RX_RNR_CMD_Pbit_SET_0 event */ +static llc_conn_action_t llc_busy_actions_17a[] = { + llc_conn_ac_upd_p_flag, + llc_conn_ac_upd_nr_received, + llc_conn_ac_set_remote_busy, + NULL +}; + +static struct llc_conn_state_trans llc_busy_state_trans_17a = { + llc_conn_ev_rx_rnr_cmd_pbit_set_0, + LLC_CONN_STATE_BUSY, + LLC_NO_EVENT_QUALIFIERS, + llc_busy_actions_17a +}; + +/* State transitions for LLC_CONN_EV_RX_RNR_RSP_Fbit_SET_0 event */ +static llc_conn_action_t llc_busy_actions_17b[] = { + llc_conn_ac_upd_p_flag, + llc_conn_ac_upd_nr_received, + llc_conn_ac_set_remote_busy, + NULL +}; + +static struct llc_conn_state_trans llc_busy_state_trans_17b = { + llc_conn_ev_rx_rnr_rsp_fbit_set_0, + LLC_CONN_STATE_BUSY, + LLC_NO_EVENT_QUALIFIERS, + llc_busy_actions_17b +}; + +/* State transitions for LLC_CONN_EV_RX_RNR_RSP_Fbit_SET_1 event */ +static llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_17c[] = { + llc_conn_ev_qlfy_p_flag_eq_1, + NULL +}; + +static llc_conn_action_t llc_busy_actions_17c[] = { + llc_conn_ac_upd_p_flag, + llc_conn_ac_upd_nr_received, + llc_conn_ac_set_remote_busy, + NULL +}; + +static struct llc_conn_state_trans llc_busy_state_trans_17c = { + llc_conn_ev_rx_rnr_rsp_fbit_set_1, + LLC_CONN_STATE_BUSY, + llc_busy_ev_qfyrs_17c, + llc_busy_actions_17c +}; + +/* State transitions for LLC_CONN_EV_RX_RNR_CMD_Pbit_SET_1 event */ +static llc_conn_action_t llc_busy_actions_18[] = { + llc_conn_ac_send_rnr_rsp_f_set_1, + llc_conn_ac_upd_nr_received, + llc_conn_ac_set_remote_busy, + NULL +}; + +static struct llc_conn_state_trans llc_busy_state_trans_18 = { + llc_conn_ev_rx_rnr_cmd_pbit_set_1, + LLC_CONN_STATE_BUSY, + LLC_NO_EVENT_QUALIFIERS, + llc_busy_actions_18 +}; + +/* State transitions for LLC_CONN_EV_RX_REJ_CMD_Pbit_SET_0 event */ +static llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_19a[] = { + llc_conn_ev_qlfy_p_flag_eq_0, + NULL +}; + +static llc_conn_action_t llc_busy_actions_19a[] = { + llc_conn_ac_set_vs_nr, + llc_conn_ac_upd_nr_received, + llc_conn_ac_upd_p_flag, + llc_conn_ac_resend_i_xxx_x_set_0, + llc_conn_ac_clear_remote_busy, + NULL +}; + +static struct llc_conn_state_trans llc_busy_state_trans_19a = { + llc_conn_ev_rx_rej_cmd_pbit_set_0, + LLC_CONN_STATE_BUSY, + llc_busy_ev_qfyrs_19a, + llc_busy_actions_19a +}; + +/* State transitions for LLC_CONN_EV_RX_REJ_RSP_Fbit_SET_X event */ +static llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_19b[] = { + llc_conn_ev_qlfy_p_flag_eq_f, + NULL +}; + +static llc_conn_action_t llc_busy_actions_19b[] = { + llc_conn_ac_set_vs_nr, + llc_conn_ac_upd_nr_received, + llc_conn_ac_upd_p_flag, + llc_conn_ac_resend_i_xxx_x_set_0, + llc_conn_ac_clear_remote_busy, + NULL +}; + +static struct llc_conn_state_trans llc_busy_state_trans_19b = { + llc_conn_ev_rx_rej_rsp_fbit_set_x, + LLC_CONN_STATE_BUSY, + llc_busy_ev_qfyrs_19b, + llc_busy_actions_19b +}; + +/* State transitions for LLC_CONN_EV_RX_REJ_CMD_Pbit_SET_0 event */ +static llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_20a[] = { + llc_conn_ev_qlfy_p_flag_eq_1, + NULL +}; + +static llc_conn_action_t llc_busy_actions_20a[] = { + llc_conn_ac_set_vs_nr, + llc_conn_ac_upd_nr_received, + llc_conn_ac_resend_i_xxx_x_set_0, + llc_conn_ac_clear_remote_busy, + NULL +}; + +static struct llc_conn_state_trans llc_busy_state_trans_20a = { + llc_conn_ev_rx_rej_cmd_pbit_set_0, + LLC_CONN_STATE_BUSY, + llc_busy_ev_qfyrs_20a, + llc_busy_actions_20a +}; + +/* State transitions for LLC_CONN_EV_RX_REJ_RSP_Fbit_SET_0 event */ +static llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_20b[] = { + llc_conn_ev_qlfy_p_flag_eq_1, + NULL +}; + +static llc_conn_action_t llc_busy_actions_20b[] = { + llc_conn_ac_set_vs_nr, + llc_conn_ac_upd_nr_received, + llc_conn_ac_resend_i_xxx_x_set_0, + llc_conn_ac_clear_remote_busy, + NULL +}; + +static struct llc_conn_state_trans llc_busy_state_trans_20b = { + llc_conn_ev_rx_rej_rsp_fbit_set_0, + LLC_CONN_STATE_BUSY, + llc_busy_ev_qfyrs_20b, + llc_busy_actions_20b +}; + +/* State transitions for LLC_CONN_EV_RX_REJ_CMD_Pbit_SET_1 event */ +static llc_conn_action_t llc_busy_actions_21[] = { + llc_conn_ac_set_vs_nr, + llc_conn_ac_upd_nr_received, + llc_conn_ac_send_rnr_rsp_f_set_1, + llc_conn_ac_resend_i_xxx_x_set_0, + llc_conn_ac_clear_remote_busy, + NULL +}; + +static struct llc_conn_state_trans llc_busy_state_trans_21 = { + llc_conn_ev_rx_rej_cmd_pbit_set_1, + LLC_CONN_STATE_BUSY, + LLC_NO_EVENT_QUALIFIERS, + llc_busy_actions_21 +}; + +/* State transitions for LLC_CONN_EV_INIT_P_F_CYCLE event */ +static llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_22[] = { + llc_conn_ev_qlfy_p_flag_eq_0, + NULL +}; + +static llc_conn_action_t llc_busy_actions_22[] = { + llc_conn_ac_send_rnr_cmd_p_set_1, + llc_conn_ac_start_p_timer, + NULL +}; + +static struct llc_conn_state_trans llc_busy_state_trans_22 = { + llc_conn_ev_init_p_f_cycle, + LLC_CONN_STATE_BUSY, + llc_busy_ev_qfyrs_22, + llc_busy_actions_22 +}; + +/* State transitions for LLC_CONN_EV_P_TMR_EXP event */ +static llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_23[] = { + llc_conn_ev_qlfy_retry_cnt_lt_n2, + NULL +}; + +static llc_conn_action_t llc_busy_actions_23[] = { + llc_conn_ac_send_rnr_cmd_p_set_1, + llc_conn_ac_rst_vs, + llc_conn_ac_start_p_timer, + llc_conn_ac_inc_retry_cnt_by_1, + NULL +}; + +static struct llc_conn_state_trans llc_busy_state_trans_23 = { + llc_conn_ev_p_tmr_exp, + LLC_CONN_STATE_AWAIT_BUSY, + llc_busy_ev_qfyrs_23, + llc_busy_actions_23 +}; + +/* State transitions for LLC_CONN_EV_ACK_TMR_EXP event */ +static llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_24a[] = { + llc_conn_ev_qlfy_p_flag_eq_0, + llc_conn_ev_qlfy_retry_cnt_lt_n2, + NULL +}; + +static llc_conn_action_t llc_busy_actions_24a[] = { + llc_conn_ac_send_rnr_cmd_p_set_1, + llc_conn_ac_start_p_timer, + llc_conn_ac_inc_retry_cnt_by_1, + llc_conn_ac_rst_vs, + NULL +}; + +static struct llc_conn_state_trans llc_busy_state_trans_24a = { + llc_conn_ev_ack_tmr_exp, + LLC_CONN_STATE_AWAIT_BUSY, + llc_busy_ev_qfyrs_24a, + llc_busy_actions_24a +}; + +/* State transitions for LLC_CONN_EV_BUSY_TMR_EXP event */ +static llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_24b[] = { + llc_conn_ev_qlfy_p_flag_eq_0, + llc_conn_ev_qlfy_retry_cnt_lt_n2, + NULL +}; + +static llc_conn_action_t llc_busy_actions_24b[] = { + llc_conn_ac_send_rnr_cmd_p_set_1, + llc_conn_ac_start_p_timer, + llc_conn_ac_inc_retry_cnt_by_1, + llc_conn_ac_rst_vs, + NULL +}; + +static struct llc_conn_state_trans llc_busy_state_trans_24b = { + llc_conn_ev_busy_tmr_exp, + LLC_CONN_STATE_AWAIT_BUSY, + llc_busy_ev_qfyrs_24b, + llc_busy_actions_24b +}; + +/* State transitions for LLC_CONN_EV_REJ_TMR_EXP event */ +static llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_25[] = { + llc_conn_ev_qlfy_p_flag_eq_0, + llc_conn_ev_qlfy_retry_cnt_lt_n2, + NULL +}; + +static llc_conn_action_t llc_busy_actions_25[] = { + llc_conn_ac_send_rnr_cmd_p_set_1, + llc_conn_ac_start_p_timer, + llc_conn_ac_inc_retry_cnt_by_1, + llc_conn_ac_rst_vs, + llc_conn_ac_set_data_flag_1, + NULL +}; + +static struct llc_conn_state_trans llc_busy_state_trans_25 = { + llc_conn_ev_rej_tmr_exp, + LLC_CONN_STATE_AWAIT_BUSY, + llc_busy_ev_qfyrs_25, + llc_busy_actions_25 +}; + +/* State transitions for LLC_CONN_EV_REJ_TMR_EXP event */ +static llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_26[] = { + llc_conn_ev_qlfy_p_flag_eq_1, + llc_conn_ev_qlfy_retry_cnt_lt_n2, + NULL +}; + +static llc_conn_action_t llc_busy_actions_26[] = { + llc_conn_ac_set_data_flag_1, + NULL +}; + +static struct llc_conn_state_trans llc_busy_state_trans_26 = { + llc_conn_ev_rej_tmr_exp, + LLC_CONN_STATE_BUSY, + llc_busy_ev_qfyrs_26, + llc_busy_actions_26 +}; + +/* + * Array of pointers; + * one to each transition + */ +static struct llc_conn_state_trans *llc_busy_state_transitions[] = { + &llc_common_state_trans_1, // Request + &llc_common_state_trans_2, + &llc_busy_state_trans_1, + &llc_busy_state_trans_2, + &llc_busy_state_trans_2_1, + &llc_common_state_trans_n, + &llc_busy_state_trans_3, // Local busy + &llc_busy_state_trans_4, + &llc_busy_state_trans_5, + &llc_busy_state_trans_6, + &llc_busy_state_trans_7, + &llc_busy_state_trans_8, + &llc_common_state_trans_n, + &llc_busy_state_trans_22, // Initiate PF cycle + &llc_common_state_trans_n, + &llc_common_state_trans_11a, // Timer + &llc_common_state_trans_11b, + &llc_common_state_trans_11c, + &llc_common_state_trans_11d, + &llc_busy_state_trans_23, + &llc_busy_state_trans_24a, + &llc_busy_state_trans_24b, + &llc_busy_state_trans_25, + &llc_busy_state_trans_26, + &llc_common_state_trans_n, + &llc_busy_state_trans_9a, // Receive frame + &llc_busy_state_trans_9b, + &llc_busy_state_trans_10a, + &llc_busy_state_trans_10b, + &llc_busy_state_trans_11, + &llc_busy_state_trans_12, + &llc_busy_state_trans_13a, + &llc_busy_state_trans_13b, + &llc_busy_state_trans_14a, + &llc_busy_state_trans_14b, + &llc_busy_state_trans_15a, + &llc_busy_state_trans_15b, + &llc_busy_state_trans_15c, + &llc_busy_state_trans_16, + &llc_busy_state_trans_17a, + &llc_busy_state_trans_17b, + &llc_busy_state_trans_17c, + &llc_busy_state_trans_18, + &llc_busy_state_trans_19a, + &llc_busy_state_trans_19b, + &llc_busy_state_trans_20a, + &llc_busy_state_trans_20b, + &llc_busy_state_trans_21, + &llc_common_state_trans_3, + &llc_common_state_trans_4, + &llc_common_state_trans_5, + &llc_common_state_trans_6, + &llc_common_state_trans_7a, + &llc_common_state_trans_7b, + &llc_common_state_trans_8a, + &llc_common_state_trans_8b, + &llc_common_state_trans_8c, + &llc_common_state_trans_9, + // &llc_common_state_trans_10, + &llc_common_state_trans_n +}; + +/* -------------------- LLC_CONN_STATE_REJECT transitions ------------------ */ +/* State transitions for LLC_CONN_EV_DATA_REQ event */ +static llc_conn_ev_qfyr_t llc_reject_ev_qfyrs_1[] = { + llc_conn_ev_qlfy_remote_busy_eq_0, + llc_conn_ev_qlfy_p_flag_eq_0, + NULL +}; + +static llc_conn_action_t llc_reject_actions_1[] = { + llc_conn_ac_send_i_xxx_x_set_0, + NULL +}; + +static struct llc_conn_state_trans llc_reject_state_trans_1 = { + llc_conn_ev_data_req, + LLC_CONN_STATE_REJECT, + llc_reject_ev_qfyrs_1, + llc_reject_actions_1 +}; + +/* State transitions for LLC_CONN_EV_DATA_REQ event */ +static llc_conn_ev_qfyr_t llc_reject_ev_qfyrs_2[] = { + llc_conn_ev_qlfy_remote_busy_eq_0, + llc_conn_ev_qlfy_p_flag_eq_1, + NULL +}; + +static llc_conn_action_t llc_reject_actions_2[] = { + llc_conn_ac_send_i_xxx_x_set_0, + NULL +}; + +static struct llc_conn_state_trans llc_reject_state_trans_2 = { + llc_conn_ev_data_req, + LLC_CONN_STATE_REJECT, + llc_reject_ev_qfyrs_2, + llc_reject_actions_2 +}; + +/* State transitions for LLC_CONN_EV_DATA_REQ event */ +static llc_conn_ev_qfyr_t llc_reject_ev_qfyrs_2_1[] = { + llc_conn_ev_qlfy_remote_busy_eq_1, + llc_conn_ev_qlfy_set_status_remote_busy, + NULL +}; + +static llc_conn_action_t llc_reject_actions_2_1[] = { + NULL +}; + +static struct llc_conn_state_trans llc_reject_state_trans_2_1 = { + llc_conn_ev_data_req, + LLC_CONN_STATE_REJECT, + llc_reject_ev_qfyrs_2_1, + llc_reject_actions_2_1 +}; + + +/* State transitions for LLC_CONN_EV_LOCAL_BUSY_DETECTED event */ +static llc_conn_ev_qfyr_t llc_reject_ev_qfyrs_3[] = { + llc_conn_ev_qlfy_p_flag_eq_0, + NULL +}; + +static llc_conn_action_t llc_reject_actions_3[] = { + llc_conn_ac_send_rnr_xxx_x_set_0, + llc_conn_ac_set_data_flag_2, + NULL +}; + +static struct llc_conn_state_trans llc_reject_state_trans_3 = { + llc_conn_ev_local_busy_detected, + LLC_CONN_STATE_BUSY, + llc_reject_ev_qfyrs_3, + llc_reject_actions_3 +}; + +/* State transitions for LLC_CONN_EV_LOCAL_BUSY_DETECTED event */ +static llc_conn_ev_qfyr_t llc_reject_ev_qfyrs_4[] = { + llc_conn_ev_qlfy_p_flag_eq_1, + NULL +}; + +static llc_conn_action_t llc_reject_actions_4[] = { + llc_conn_ac_send_rnr_xxx_x_set_0, + llc_conn_ac_set_data_flag_2, + NULL +}; + +static struct llc_conn_state_trans llc_reject_state_trans_4 = { + llc_conn_ev_local_busy_detected, + LLC_CONN_STATE_BUSY, + llc_reject_ev_qfyrs_4, + llc_reject_actions_4 +}; + +/* State transitions for LLC_CONN_EV_RX_I_CMD_Pbit_SET_0_UNEXPD_Ns event */ +static llc_conn_action_t llc_reject_actions_5a[] = { + llc_conn_ac_upd_nr_received, + llc_conn_ac_upd_p_flag, + llc_conn_ac_clear_remote_busy_if_f_eq_1, + NULL +}; + +static struct llc_conn_state_trans llc_reject_state_trans_5a = { + llc_conn_ev_rx_i_cmd_pbit_set_0_unexpd_ns, + LLC_CONN_STATE_REJECT, + LLC_NO_EVENT_QUALIFIERS, + llc_reject_actions_5a +}; + +/* State transitions for LLC_CONN_EV_RX_I_RSP_Fbit_SET_0_UNEXPD_Ns event */ +static llc_conn_action_t llc_reject_actions_5b[] = { + llc_conn_ac_upd_nr_received, + llc_conn_ac_upd_p_flag, + llc_conn_ac_clear_remote_busy_if_f_eq_1, + NULL +}; + +static struct llc_conn_state_trans llc_reject_state_trans_5b = { + llc_conn_ev_rx_i_rsp_fbit_set_0_unexpd_ns, + LLC_CONN_STATE_REJECT, + LLC_NO_EVENT_QUALIFIERS, + llc_reject_actions_5b +}; + +/* State transitions for LLC_CONN_EV_RX_I_RSP_Fbit_SET_1_UNEXPD_Ns event */ +static llc_conn_ev_qfyr_t llc_reject_ev_qfyrs_5c[] = { + llc_conn_ev_qlfy_p_flag_eq_1, + NULL +}; + +static llc_conn_action_t llc_reject_actions_5c[] = { + llc_conn_ac_upd_nr_received, + llc_conn_ac_upd_p_flag, + llc_conn_ac_clear_remote_busy_if_f_eq_1, + NULL +}; + +static struct llc_conn_state_trans llc_reject_state_trans_5c = { + llc_conn_ev_rx_i_rsp_fbit_set_1_unexpd_ns, + LLC_CONN_STATE_REJECT, + llc_reject_ev_qfyrs_5c, + llc_reject_actions_5c +}; + +/* State transitions for LLC_CONN_EV_RX_I_CMD_Pbit_SET_1_UNEXPD_Ns event */ +static llc_conn_action_t llc_reject_actions_6[] = { + llc_conn_ac_send_rr_rsp_f_set_1, + llc_conn_ac_upd_nr_received, + NULL +}; + +static struct llc_conn_state_trans llc_reject_state_trans_6 = { + llc_conn_ev_rx_i_cmd_pbit_set_1_unexpd_ns, + LLC_CONN_STATE_REJECT, + LLC_NO_EVENT_QUALIFIERS, + llc_reject_actions_6 +}; + +/* State transitions for LLC_CONN_EV_RX_I_RSP_Fbit_SET_X event */ +static llc_conn_ev_qfyr_t llc_reject_ev_qfyrs_7a[] = { + llc_conn_ev_qlfy_p_flag_eq_f, + NULL +}; + +static llc_conn_action_t llc_reject_actions_7a[] = { + llc_conn_ac_inc_vr_by_1, + llc_conn_ac_data_ind, + llc_conn_ac_upd_p_flag, + llc_conn_ac_send_ack_xxx_x_set_0, + llc_conn_ac_upd_nr_received, + llc_conn_ac_clear_remote_busy_if_f_eq_1, + llc_conn_ac_stop_rej_timer, + NULL + +}; + +static struct llc_conn_state_trans llc_reject_state_trans_7a = { + llc_conn_ev_rx_i_rsp_fbit_set_x, + LLC_CONN_STATE_NORMAL, + llc_reject_ev_qfyrs_7a, + llc_reject_actions_7a +}; + +/* State transitions for LLC_CONN_EV_RX_I_CMD_Pbit_SET_0 event */ +static llc_conn_ev_qfyr_t llc_reject_ev_qfyrs_7b[] = { + llc_conn_ev_qlfy_p_flag_eq_0, + NULL +}; + +static llc_conn_action_t llc_reject_actions_7b[] = { + llc_conn_ac_inc_vr_by_1, + llc_conn_ac_data_ind, + llc_conn_ac_upd_p_flag, + llc_conn_ac_send_ack_xxx_x_set_0, + llc_conn_ac_upd_nr_received, + llc_conn_ac_clear_remote_busy_if_f_eq_1, + llc_conn_ac_stop_rej_timer, + NULL +}; + +static struct llc_conn_state_trans llc_reject_state_trans_7b = { + llc_conn_ev_rx_i_cmd_pbit_set_0, + LLC_CONN_STATE_NORMAL, + llc_reject_ev_qfyrs_7b, + llc_reject_actions_7b +}; + +/* State transitions for LLC_CONN_EV_RX_I_RSP_Fbit_SET_0 event */ +static llc_conn_ev_qfyr_t llc_reject_ev_qfyrs_8a[] = { + llc_conn_ev_qlfy_p_flag_eq_1, + NULL +}; + +static llc_conn_action_t llc_reject_actions_8a[] = { + llc_conn_ac_inc_vr_by_1, + llc_conn_ac_data_ind, + llc_conn_ac_send_ack_xxx_x_set_0, + llc_conn_ac_upd_nr_received, + llc_conn_ac_stop_rej_timer, + NULL +}; + +static struct llc_conn_state_trans llc_reject_state_trans_8a = { + llc_conn_ev_rx_i_rsp_fbit_set_0, + LLC_CONN_STATE_NORMAL, + llc_reject_ev_qfyrs_8a, + llc_reject_actions_8a +}; + +/* State transitions for LLC_CONN_EV_RX_I_CMD_Pbit_SET_0 event */ +static llc_conn_ev_qfyr_t llc_reject_ev_qfyrs_8b[] = { + llc_conn_ev_qlfy_p_flag_eq_1, + NULL +}; + +static llc_conn_action_t llc_reject_actions_8b[] = { + llc_conn_ac_inc_vr_by_1, + llc_conn_ac_data_ind, + llc_conn_ac_send_ack_xxx_x_set_0, + llc_conn_ac_upd_nr_received, + llc_conn_ac_stop_rej_timer, + NULL +}; + +static struct llc_conn_state_trans llc_reject_state_trans_8b = { + llc_conn_ev_rx_i_cmd_pbit_set_0, + LLC_CONN_STATE_NORMAL, + llc_reject_ev_qfyrs_8b, + llc_reject_actions_8b +}; + +/* State transitions for LLC_CONN_EV_RX_I_CMD_Pbit_SET_1 event */ +static llc_conn_action_t llc_reject_actions_9[] = { + llc_conn_ac_inc_vr_by_1, + llc_conn_ac_data_ind, + llc_conn_ac_send_ack_rsp_f_set_1, + llc_conn_ac_upd_nr_received, + llc_conn_ac_stop_rej_timer, + NULL +}; + +static struct llc_conn_state_trans llc_reject_state_trans_9 = { + llc_conn_ev_rx_i_cmd_pbit_set_1, + LLC_CONN_STATE_NORMAL, + LLC_NO_EVENT_QUALIFIERS, + llc_reject_actions_9 +}; + +/* State transitions for LLC_CONN_EV_RX_RR_CMD_Pbit_SET_0 event */ +static llc_conn_action_t llc_reject_actions_10a[] = { + llc_conn_ac_upd_p_flag, + llc_conn_ac_upd_nr_received, + llc_conn_ac_clear_remote_busy, + NULL +}; + +static struct llc_conn_state_trans llc_reject_state_trans_10a = { + llc_conn_ev_rx_rr_cmd_pbit_set_0, + LLC_CONN_STATE_REJECT, + LLC_NO_EVENT_QUALIFIERS, + llc_reject_actions_10a +}; + +/* State transitions for LLC_CONN_EV_RX_RR_RSP_Fbit_SET_0 event */ +static llc_conn_action_t llc_reject_actions_10b[] = { + llc_conn_ac_upd_p_flag, + llc_conn_ac_upd_nr_received, + llc_conn_ac_clear_remote_busy, + NULL +}; + +static struct llc_conn_state_trans llc_reject_state_trans_10b = { + llc_conn_ev_rx_rr_rsp_fbit_set_0, + LLC_CONN_STATE_REJECT, + LLC_NO_EVENT_QUALIFIERS, + llc_reject_actions_10b +}; + +/* State transitions for LLC_CONN_EV_RX_RR_RSP_Fbit_SET_1 event */ +static llc_conn_ev_qfyr_t llc_reject_ev_qfyrs_10c[] = { + llc_conn_ev_qlfy_p_flag_eq_1, + NULL +}; + +static llc_conn_action_t llc_reject_actions_10c[] = { + llc_conn_ac_upd_p_flag, + llc_conn_ac_upd_nr_received, + llc_conn_ac_clear_remote_busy, + NULL +}; + +static struct llc_conn_state_trans llc_reject_state_trans_10c = { + llc_conn_ev_rx_rr_rsp_fbit_set_1, + LLC_CONN_STATE_REJECT, + llc_reject_ev_qfyrs_10c, + llc_reject_actions_10c +}; + +/* State transitions for LLC_CONN_EV_RX_RR_CMD_Pbit_SET_1 event */ +static llc_conn_action_t llc_reject_actions_11[] = { + llc_conn_ac_send_ack_rsp_f_set_1, + llc_conn_ac_upd_nr_received, + llc_conn_ac_clear_remote_busy, + NULL +}; + +static struct llc_conn_state_trans llc_reject_state_trans_11 = { + llc_conn_ev_rx_rr_cmd_pbit_set_1, + LLC_CONN_STATE_REJECT, + LLC_NO_EVENT_QUALIFIERS, + llc_reject_actions_11 +}; + +/* State transitions for LLC_CONN_EV_RX_RNR_CMD_Pbit_SET_0 event */ +static llc_conn_action_t llc_reject_actions_12a[] = { + llc_conn_ac_upd_p_flag, + llc_conn_ac_upd_nr_received, + llc_conn_ac_set_remote_busy, + NULL +}; + +static struct llc_conn_state_trans llc_reject_state_trans_12a = { + llc_conn_ev_rx_rnr_cmd_pbit_set_0, + LLC_CONN_STATE_REJECT, + LLC_NO_EVENT_QUALIFIERS, + llc_reject_actions_12a +}; + +/* State transitions for LLC_CONN_EV_RX_RNR_RSP_Fbit_SET_0 event */ +static llc_conn_action_t llc_reject_actions_12b[] = { + llc_conn_ac_upd_p_flag, + llc_conn_ac_upd_nr_received, + llc_conn_ac_set_remote_busy, + NULL +}; + +static struct llc_conn_state_trans llc_reject_state_trans_12b = { + llc_conn_ev_rx_rnr_rsp_fbit_set_0, + LLC_CONN_STATE_REJECT, + LLC_NO_EVENT_QUALIFIERS, + llc_reject_actions_12b +}; + +/* State transitions for LLC_CONN_EV_RX_RNR_RSP_Fbit_SET_1 event */ +static llc_conn_ev_qfyr_t llc_reject_ev_qfyrs_12c[] = { + llc_conn_ev_qlfy_p_flag_eq_1, + NULL +}; + +static llc_conn_action_t llc_reject_actions_12c[] = { + llc_conn_ac_upd_p_flag, + llc_conn_ac_upd_nr_received, + llc_conn_ac_set_remote_busy, + NULL +}; + +static struct llc_conn_state_trans llc_reject_state_trans_12c = { + llc_conn_ev_rx_rnr_rsp_fbit_set_1, + LLC_CONN_STATE_REJECT, + llc_reject_ev_qfyrs_12c, + llc_reject_actions_12c +}; + +/* State transitions for LLC_CONN_EV_RX_RNR_CMD_Pbit_SET_1 event */ +static llc_conn_action_t llc_reject_actions_13[] = { + llc_conn_ac_send_rr_rsp_f_set_1, + llc_conn_ac_upd_nr_received, + llc_conn_ac_set_remote_busy, + NULL +}; + +static struct llc_conn_state_trans llc_reject_state_trans_13 = { + llc_conn_ev_rx_rnr_cmd_pbit_set_1, + LLC_CONN_STATE_REJECT, + LLC_NO_EVENT_QUALIFIERS, + llc_reject_actions_13 +}; + +/* State transitions for LLC_CONN_EV_RX_REJ_CMD_Pbit_SET_0 event */ +static llc_conn_ev_qfyr_t llc_reject_ev_qfyrs_14a[] = { + llc_conn_ev_qlfy_p_flag_eq_0, + NULL +}; + +static llc_conn_action_t llc_reject_actions_14a[] = { + llc_conn_ac_set_vs_nr, + llc_conn_ac_upd_nr_received, + llc_conn_ac_upd_p_flag, + llc_conn_ac_resend_i_xxx_x_set_0, + llc_conn_ac_clear_remote_busy, + NULL +}; + +static struct llc_conn_state_trans llc_reject_state_trans_14a = { + llc_conn_ev_rx_rej_cmd_pbit_set_0, + LLC_CONN_STATE_REJECT, + llc_reject_ev_qfyrs_14a, + llc_reject_actions_14a +}; + +/* State transitions for LLC_CONN_EV_RX_REJ_RSP_Fbit_SET_X event */ +static llc_conn_ev_qfyr_t llc_reject_ev_qfyrs_14b[] = { + llc_conn_ev_qlfy_p_flag_eq_f, + NULL +}; + +static llc_conn_action_t llc_reject_actions_14b[] = { + llc_conn_ac_set_vs_nr, + llc_conn_ac_upd_nr_received, + llc_conn_ac_upd_p_flag, + llc_conn_ac_resend_i_xxx_x_set_0, + llc_conn_ac_clear_remote_busy, + NULL +}; + +static struct llc_conn_state_trans llc_reject_state_trans_14b = { + llc_conn_ev_rx_rej_rsp_fbit_set_x, + LLC_CONN_STATE_REJECT, + llc_reject_ev_qfyrs_14b, + llc_reject_actions_14b +}; + +/* State transitions for LLC_CONN_EV_RX_REJ_CMD_Pbit_SET_0 event */ +static llc_conn_ev_qfyr_t llc_reject_ev_qfyrs_15a[] = { + llc_conn_ev_qlfy_p_flag_eq_1, + NULL +}; + +static llc_conn_action_t llc_reject_actions_15a[] = { + llc_conn_ac_set_vs_nr, + llc_conn_ac_upd_nr_received, + llc_conn_ac_resend_i_xxx_x_set_0, + llc_conn_ac_clear_remote_busy, + NULL +}; + +static struct llc_conn_state_trans llc_reject_state_trans_15a = { + llc_conn_ev_rx_rej_cmd_pbit_set_0, + LLC_CONN_STATE_REJECT, + llc_reject_ev_qfyrs_15a, + llc_reject_actions_15a +}; + +/* State transitions for LLC_CONN_EV_RX_REJ_RSP_Fbit_SET_0 event */ +static llc_conn_ev_qfyr_t llc_reject_ev_qfyrs_15b[] = { + llc_conn_ev_qlfy_p_flag_eq_1, + NULL +}; + +static llc_conn_action_t llc_reject_actions_15b[] = { + llc_conn_ac_set_vs_nr, + llc_conn_ac_upd_nr_received, + llc_conn_ac_resend_i_xxx_x_set_0, + llc_conn_ac_clear_remote_busy, + NULL +}; + +static struct llc_conn_state_trans llc_reject_state_trans_15b = { + llc_conn_ev_rx_rej_rsp_fbit_set_0, + LLC_CONN_STATE_REJECT, + llc_reject_ev_qfyrs_15b, + llc_reject_actions_15b +}; + +/* State transitions for LLC_CONN_EV_RX_REJ_CMD_Pbit_SET_1 event */ +static llc_conn_action_t llc_reject_actions_16[] = { + llc_conn_ac_set_vs_nr, + llc_conn_ac_upd_nr_received, + llc_conn_ac_resend_i_rsp_f_set_1, + llc_conn_ac_clear_remote_busy, + NULL +}; + +static struct llc_conn_state_trans llc_reject_state_trans_16 = { + llc_conn_ev_rx_rej_cmd_pbit_set_1, + LLC_CONN_STATE_REJECT, + LLC_NO_EVENT_QUALIFIERS, + llc_reject_actions_16 +}; + +/* State transitions for LLC_CONN_EV_INIT_P_F_CYCLE event */ +static llc_conn_ev_qfyr_t llc_reject_ev_qfyrs_17[] = { + llc_conn_ev_qlfy_p_flag_eq_0, + NULL +}; + +static llc_conn_action_t llc_reject_actions_17[] = { + llc_conn_ac_send_rr_cmd_p_set_1, + llc_conn_ac_start_p_timer, + NULL +}; + +static struct llc_conn_state_trans llc_reject_state_trans_17 = { + llc_conn_ev_init_p_f_cycle, + LLC_CONN_STATE_REJECT, + llc_reject_ev_qfyrs_17, + llc_reject_actions_17 +}; + +/* State transitions for LLC_CONN_EV_REJ_TMR_EXP event */ +static llc_conn_ev_qfyr_t llc_reject_ev_qfyrs_18[] = { + llc_conn_ev_qlfy_p_flag_eq_0, + llc_conn_ev_qlfy_retry_cnt_lt_n2, + NULL +}; + +static llc_conn_action_t llc_reject_actions_18[] = { + llc_conn_ac_send_rej_cmd_p_set_1, + llc_conn_ac_start_p_timer, + llc_conn_ac_start_rej_timer, + llc_conn_ac_inc_retry_cnt_by_1, + NULL +}; + +static struct llc_conn_state_trans llc_reject_state_trans_18 = { + llc_conn_ev_rej_tmr_exp, + LLC_CONN_STATE_REJECT, + llc_reject_ev_qfyrs_18, + llc_reject_actions_18 +}; + +/* State transitions for LLC_CONN_EV_P_TMR_EXP event */ +static llc_conn_ev_qfyr_t llc_reject_ev_qfyrs_19[] = { + llc_conn_ev_qlfy_retry_cnt_lt_n2, + NULL +}; + +static llc_conn_action_t llc_reject_actions_19[] = { + llc_conn_ac_send_rr_cmd_p_set_1, + llc_conn_ac_start_p_timer, + llc_conn_ac_start_rej_timer, + llc_conn_ac_inc_retry_cnt_by_1, + llc_conn_ac_rst_vs, + NULL +}; + +static struct llc_conn_state_trans llc_reject_state_trans_19 = { + llc_conn_ev_p_tmr_exp, + LLC_CONN_STATE_AWAIT_REJECT, + llc_reject_ev_qfyrs_19, + llc_reject_actions_19 +}; + +/* State transitions for LLC_CONN_EV_ACK_TMR_EXP event */ +static llc_conn_ev_qfyr_t llc_reject_ev_qfyrs_20a[] = { + llc_conn_ev_qlfy_p_flag_eq_0, + llc_conn_ev_qlfy_retry_cnt_lt_n2, + NULL +}; + +static llc_conn_action_t llc_reject_actions_20a[] = { + llc_conn_ac_send_rr_cmd_p_set_1, + llc_conn_ac_start_p_timer, + llc_conn_ac_start_rej_timer, + llc_conn_ac_inc_retry_cnt_by_1, + llc_conn_ac_rst_vs, + NULL +}; + +static struct llc_conn_state_trans llc_reject_state_trans_20a = { + llc_conn_ev_ack_tmr_exp, + LLC_CONN_STATE_AWAIT_REJECT, + llc_reject_ev_qfyrs_20a, + llc_reject_actions_20a +}; + +/* State transitions for LLC_CONN_EV_BUSY_TMR_EXP event */ +static llc_conn_ev_qfyr_t llc_reject_ev_qfyrs_20b[] = { + llc_conn_ev_qlfy_p_flag_eq_0, + llc_conn_ev_qlfy_retry_cnt_lt_n2, + NULL +}; + +static llc_conn_action_t llc_reject_actions_20b[] = { + llc_conn_ac_send_rr_cmd_p_set_1, + llc_conn_ac_start_p_timer, + llc_conn_ac_start_rej_timer, + llc_conn_ac_inc_retry_cnt_by_1, + llc_conn_ac_rst_vs, + NULL +}; + +static struct llc_conn_state_trans llc_reject_state_trans_20b = { + llc_conn_ev_busy_tmr_exp, + LLC_CONN_STATE_AWAIT_REJECT, + llc_reject_ev_qfyrs_20b, + llc_reject_actions_20b +}; + +/* + * Array of pointers; + * one to each transition + */ +static struct llc_conn_state_trans *llc_reject_state_transitions[] = { + &llc_common_state_trans_1, // Request + &llc_common_state_trans_2, + &llc_common_state_trans_n, + &llc_reject_state_trans_1, + &llc_reject_state_trans_2, + &llc_reject_state_trans_2_1, + &llc_reject_state_trans_3, // Local busy + &llc_reject_state_trans_4, + &llc_common_state_trans_n, + &llc_reject_state_trans_17, // Initiate PF cycle + &llc_common_state_trans_n, + &llc_common_state_trans_11a, // Timer + &llc_common_state_trans_11b, + &llc_common_state_trans_11c, + &llc_common_state_trans_11d, + &llc_reject_state_trans_18, + &llc_reject_state_trans_19, + &llc_reject_state_trans_20a, + &llc_reject_state_trans_20b, + &llc_common_state_trans_n, + &llc_common_state_trans_3, // Receive frame + &llc_common_state_trans_4, + &llc_common_state_trans_5, + &llc_common_state_trans_6, + &llc_common_state_trans_7a, + &llc_common_state_trans_7b, + &llc_common_state_trans_8a, + &llc_common_state_trans_8b, + &llc_common_state_trans_8c, + &llc_common_state_trans_9, + // &llc_common_state_trans_10, + &llc_reject_state_trans_5a, + &llc_reject_state_trans_5b, + &llc_reject_state_trans_5c, + &llc_reject_state_trans_6, + &llc_reject_state_trans_7a, + &llc_reject_state_trans_7b, + &llc_reject_state_trans_8a, + &llc_reject_state_trans_8b, + &llc_reject_state_trans_9, + &llc_reject_state_trans_10a, + &llc_reject_state_trans_10b, + &llc_reject_state_trans_10c, + &llc_reject_state_trans_11, + &llc_reject_state_trans_12a, + &llc_reject_state_trans_12b, + &llc_reject_state_trans_12c, + &llc_reject_state_trans_13, + &llc_reject_state_trans_14a, + &llc_reject_state_trans_14b, + &llc_reject_state_trans_15a, + &llc_reject_state_trans_15b, + &llc_reject_state_trans_16, + &llc_common_state_trans_n +}; + +/* -------------------- LLC_CONN_STATE_AWAIT transitions ------------------- */ +/* State transitions for LLC_CONN_EV_DATA_REQ event */ +static llc_conn_ev_qfyr_t llc_await_ev_qfyrs_1_0[] = { + llc_conn_ev_qlfy_set_status_refuse, + NULL +}; + +static llc_conn_action_t llc_await_actions_1_0[] = { + NULL +}; + +static struct llc_conn_state_trans llc_await_state_trans_1_0 = { + llc_conn_ev_data_req, + LLC_CONN_STATE_AWAIT, + llc_await_ev_qfyrs_1_0, + llc_await_actions_1_0 +}; + +/* State transitions for LLC_CONN_EV_LOCAL_BUSY_DETECTED event */ +static llc_conn_action_t llc_await_actions_1[] = { + llc_conn_ac_send_rnr_xxx_x_set_0, + llc_conn_ac_set_data_flag_0, + NULL +}; + +static struct llc_conn_state_trans llc_await_state_trans_1 = { + llc_conn_ev_local_busy_detected, + LLC_CONN_STATE_AWAIT_BUSY, + LLC_NO_EVENT_QUALIFIERS, + llc_await_actions_1 +}; + +/* State transitions for LLC_CONN_EV_RX_I_RSP_Fbit_SET_1_UNEXPD_Ns event */ +static llc_conn_action_t llc_await_actions_2[] = { + llc_conn_ac_send_rej_xxx_x_set_0, + llc_conn_ac_upd_nr_received, + llc_conn_ac_upd_vs, + llc_conn_ac_stop_p_timer, + llc_conn_ac_resend_i_xxx_x_set_0, + llc_conn_ac_start_rej_timer, + llc_conn_ac_clear_remote_busy, + NULL +}; + +static struct llc_conn_state_trans llc_await_state_trans_2 = { + llc_conn_ev_rx_i_rsp_fbit_set_1_unexpd_ns, + LLC_CONN_STATE_REJECT, + LLC_NO_EVENT_QUALIFIERS, + llc_await_actions_2 +}; + +/* State transitions for LLC_CONN_EV_RX_I_CMD_Pbit_SET_0_UNEXPD_Ns event */ +static llc_conn_action_t llc_await_actions_3a[] = { + llc_conn_ac_send_rej_xxx_x_set_0, + llc_conn_ac_upd_nr_received, + llc_conn_ac_upd_vs, + llc_conn_ac_start_rej_timer, + NULL +}; + +static struct llc_conn_state_trans llc_await_state_trans_3a = { + llc_conn_ev_rx_i_cmd_pbit_set_0_unexpd_ns, + LLC_CONN_STATE_AWAIT_REJECT, + LLC_NO_EVENT_QUALIFIERS, + llc_await_actions_3a +}; + +/* State transitions for LLC_CONN_EV_RX_I_RSP_Fbit_SET_0_UNEXPD_Ns event */ +static llc_conn_action_t llc_await_actions_3b[] = { + llc_conn_ac_send_rej_xxx_x_set_0, + llc_conn_ac_upd_nr_received, + llc_conn_ac_upd_vs, + llc_conn_ac_start_rej_timer, + NULL +}; + +static struct llc_conn_state_trans llc_await_state_trans_3b = { + llc_conn_ev_rx_i_rsp_fbit_set_0_unexpd_ns, + LLC_CONN_STATE_AWAIT_REJECT, + LLC_NO_EVENT_QUALIFIERS, + llc_await_actions_3b +}; + +/* State transitions for LLC_CONN_EV_RX_I_CMD_Pbit_SET_1_UNEXPD_Ns event */ +static llc_conn_action_t llc_await_actions_4[] = { + llc_conn_ac_send_rej_rsp_f_set_1, + llc_conn_ac_upd_nr_received, + llc_conn_ac_upd_vs, + llc_conn_ac_start_rej_timer, + llc_conn_ac_start_p_timer, + NULL +}; + +static struct llc_conn_state_trans llc_await_state_trans_4 = { + llc_conn_ev_rx_i_cmd_pbit_set_1_unexpd_ns, + LLC_CONN_STATE_AWAIT_REJECT, + LLC_NO_EVENT_QUALIFIERS, + llc_await_actions_4 +}; + +/* State transitions for LLC_CONN_EV_RX_I_RSP_Fbit_SET_1 event */ +static llc_conn_action_t llc_await_actions_5[] = { + llc_conn_ac_inc_vr_by_1, + llc_conn_ac_data_ind, + llc_conn_ac_stop_p_timer, + llc_conn_ac_upd_nr_received, + llc_conn_ac_upd_vs, + llc_conn_ac_resend_i_xxx_x_set_0_or_send_rr, + llc_conn_ac_clear_remote_busy, + NULL +}; + +static struct llc_conn_state_trans llc_await_state_trans_5 = { + llc_conn_ev_rx_i_rsp_fbit_set_1, + LLC_CONN_STATE_NORMAL, + LLC_NO_EVENT_QUALIFIERS, + llc_await_actions_5 +}; + +/* State transitions for LLC_CONN_EV_RX_I_RSP_Fbit_SET_0 event */ +static llc_conn_action_t llc_await_actions_6a[] = { + llc_conn_ac_inc_vr_by_1, + llc_conn_ac_data_ind, + llc_conn_ac_send_rr_xxx_x_set_0, + llc_conn_ac_upd_nr_received, + llc_conn_ac_upd_vs, + NULL +}; + +static struct llc_conn_state_trans llc_await_state_trans_6a = { + llc_conn_ev_rx_i_rsp_fbit_set_0, + LLC_CONN_STATE_AWAIT, + LLC_NO_EVENT_QUALIFIERS, + llc_await_actions_6a +}; + +/* State transitions for LLC_CONN_EV_RX_I_CMD_Pbit_SET_0 event */ +static llc_conn_action_t llc_await_actions_6b[] = { + llc_conn_ac_inc_vr_by_1, + llc_conn_ac_data_ind, + llc_conn_ac_send_rr_xxx_x_set_0, + llc_conn_ac_upd_nr_received, + llc_conn_ac_upd_vs, + NULL +}; + +static struct llc_conn_state_trans llc_await_state_trans_6b = { + llc_conn_ev_rx_i_cmd_pbit_set_0, + LLC_CONN_STATE_AWAIT, + LLC_NO_EVENT_QUALIFIERS, + llc_await_actions_6b +}; + +/* State transitions for LLC_CONN_EV_RX_I_CMD_Pbit_SET_1 event */ +static llc_conn_action_t llc_await_actions_7[] = { + llc_conn_ac_inc_vr_by_1, + llc_conn_ac_data_ind, + llc_conn_ac_send_rr_rsp_f_set_1, + llc_conn_ac_upd_nr_received, + llc_conn_ac_upd_vs, + NULL +}; + +static struct llc_conn_state_trans llc_await_state_trans_7 = { + llc_conn_ev_rx_i_cmd_pbit_set_1, + LLC_CONN_STATE_AWAIT, + LLC_NO_EVENT_QUALIFIERS, + llc_await_actions_7 +}; + +/* State transitions for LLC_CONN_EV_RX_RR_RSP_Fbit_SET_1 event */ +static llc_conn_action_t llc_await_actions_8a[] = { + llc_conn_ac_upd_nr_received, + llc_conn_ac_upd_vs, + llc_conn_ac_stop_p_timer, + llc_conn_ac_resend_i_xxx_x_set_0, + llc_conn_ac_clear_remote_busy, + NULL +}; + +static struct llc_conn_state_trans llc_await_state_trans_8a = { + llc_conn_ev_rx_rr_rsp_fbit_set_1, + LLC_CONN_STATE_NORMAL, + LLC_NO_EVENT_QUALIFIERS, + llc_await_actions_8a +}; + +/* State transitions for LLC_CONN_EV_RX_REJ_RSP_Fbit_SET_1 event */ +static llc_conn_action_t llc_await_actions_8b[] = { + llc_conn_ac_upd_nr_received, + llc_conn_ac_upd_vs, + llc_conn_ac_stop_p_timer, + llc_conn_ac_resend_i_xxx_x_set_0, + llc_conn_ac_clear_remote_busy, + NULL +}; + +static struct llc_conn_state_trans llc_await_state_trans_8b = { + llc_conn_ev_rx_rej_rsp_fbit_set_1, + LLC_CONN_STATE_NORMAL, + LLC_NO_EVENT_QUALIFIERS, + llc_await_actions_8b +}; + +/* State transitions for LLC_CONN_EV_RX_RR_CMD_Pbit_SET_0 event */ +static llc_conn_action_t llc_await_actions_9a[] = { + llc_conn_ac_upd_nr_received, + llc_conn_ac_upd_vs, + llc_conn_ac_clear_remote_busy, + NULL +}; + +static struct llc_conn_state_trans llc_await_state_trans_9a = { + llc_conn_ev_rx_rr_cmd_pbit_set_0, + LLC_CONN_STATE_AWAIT, + LLC_NO_EVENT_QUALIFIERS, + llc_await_actions_9a +}; + +/* State transitions for LLC_CONN_EV_RX_RR_RSP_Fbit_SET_0 event */ +static llc_conn_action_t llc_await_actions_9b[] = { + llc_conn_ac_upd_nr_received, + llc_conn_ac_upd_vs, + llc_conn_ac_clear_remote_busy, + NULL +}; + +static struct llc_conn_state_trans llc_await_state_trans_9b = { + llc_conn_ev_rx_rr_rsp_fbit_set_0, + LLC_CONN_STATE_AWAIT, + LLC_NO_EVENT_QUALIFIERS, + llc_await_actions_9b +}; + +/* State transitions for LLC_CONN_EV_RX_REJ_CMD_Pbit_SET_0 event */ +static llc_conn_action_t llc_await_actions_9c[] = { + llc_conn_ac_upd_nr_received, + llc_conn_ac_upd_vs, + llc_conn_ac_clear_remote_busy, + NULL +}; + +static struct llc_conn_state_trans llc_await_state_trans_9c = { + llc_conn_ev_rx_rej_cmd_pbit_set_0, + LLC_CONN_STATE_AWAIT, + LLC_NO_EVENT_QUALIFIERS, + llc_await_actions_9c +}; + +/* State transitions for LLC_CONN_EV_RX_REJ_RSP_Fbit_SET_0 event */ +static llc_conn_action_t llc_await_actions_9d[] = { + llc_conn_ac_upd_nr_received, + llc_conn_ac_upd_vs, + llc_conn_ac_clear_remote_busy, + NULL +}; + +static struct llc_conn_state_trans llc_await_state_trans_9d = { + llc_conn_ev_rx_rej_rsp_fbit_set_0, + LLC_CONN_STATE_AWAIT, + LLC_NO_EVENT_QUALIFIERS, + llc_await_actions_9d +}; + +/* State transitions for LLC_CONN_EV_RX_RR_CMD_Pbit_SET_1 event */ +static llc_conn_action_t llc_await_actions_10a[] = { + llc_conn_ac_send_rr_rsp_f_set_1, + llc_conn_ac_upd_nr_received, + llc_conn_ac_upd_vs, + llc_conn_ac_clear_remote_busy, + NULL +}; + +static struct llc_conn_state_trans llc_await_state_trans_10a = { + llc_conn_ev_rx_rr_cmd_pbit_set_1, + LLC_CONN_STATE_AWAIT, + LLC_NO_EVENT_QUALIFIERS, + llc_await_actions_10a +}; + +/* State transitions for LLC_CONN_EV_RX_REJ_CMD_Pbit_SET_1 event */ +static llc_conn_action_t llc_await_actions_10b[] = { + llc_conn_ac_send_rr_rsp_f_set_1, + llc_conn_ac_upd_nr_received, + llc_conn_ac_upd_vs, + llc_conn_ac_clear_remote_busy, + NULL +}; + +static struct llc_conn_state_trans llc_await_state_trans_10b = { + llc_conn_ev_rx_rej_cmd_pbit_set_1, + LLC_CONN_STATE_AWAIT, + LLC_NO_EVENT_QUALIFIERS, + llc_await_actions_10b +}; + +/* State transitions for LLC_CONN_EV_RX_RNR_RSP_Fbit_SET_1 event */ +static llc_conn_action_t llc_await_actions_11[] = { + llc_conn_ac_upd_nr_received, + llc_conn_ac_upd_vs, + llc_conn_ac_stop_p_timer, + llc_conn_ac_set_remote_busy, + NULL +}; + +static struct llc_conn_state_trans llc_await_state_trans_11 = { + llc_conn_ev_rx_rnr_rsp_fbit_set_1, + LLC_CONN_STATE_NORMAL, + LLC_NO_EVENT_QUALIFIERS, + llc_await_actions_11 +}; + +/* State transitions for LLC_CONN_EV_RX_RNR_CMD_Pbit_SET_0 event */ +static llc_conn_action_t llc_await_actions_12a[] = { + llc_conn_ac_upd_nr_received, + llc_conn_ac_upd_vs, + llc_conn_ac_set_remote_busy, + NULL +}; + +static struct llc_conn_state_trans llc_await_state_trans_12a = { + llc_conn_ev_rx_rnr_cmd_pbit_set_0, + LLC_CONN_STATE_AWAIT, + LLC_NO_EVENT_QUALIFIERS, + llc_await_actions_12a +}; + +/* State transitions for LLC_CONN_EV_RX_RNR_RSP_Fbit_SET_0 event */ +static llc_conn_action_t llc_await_actions_12b[] = { + llc_conn_ac_upd_nr_received, + llc_conn_ac_upd_vs, + llc_conn_ac_set_remote_busy, + NULL +}; + +static struct llc_conn_state_trans llc_await_state_trans_12b = { + llc_conn_ev_rx_rnr_rsp_fbit_set_0, + LLC_CONN_STATE_AWAIT, + LLC_NO_EVENT_QUALIFIERS, + llc_await_actions_12b +}; + +/* State transitions for LLC_CONN_EV_RX_RNR_CMD_Pbit_SET_1 event */ +static llc_conn_action_t llc_await_actions_13[] = { + llc_conn_ac_send_rr_rsp_f_set_1, + llc_conn_ac_upd_nr_received, + llc_conn_ac_upd_vs, + llc_conn_ac_set_remote_busy, + NULL +}; + +static struct llc_conn_state_trans llc_await_state_trans_13 = { + llc_conn_ev_rx_rnr_cmd_pbit_set_1, + LLC_CONN_STATE_AWAIT, + LLC_NO_EVENT_QUALIFIERS, + llc_await_actions_13 +}; + +/* State transitions for LLC_CONN_EV_P_TMR_EXP event */ +static llc_conn_ev_qfyr_t llc_await_ev_qfyrs_14[] = { + llc_conn_ev_qlfy_retry_cnt_lt_n2, + NULL +}; + +static llc_conn_action_t llc_await_actions_14[] = { + llc_conn_ac_send_rr_cmd_p_set_1, + llc_conn_ac_start_p_timer, + llc_conn_ac_inc_retry_cnt_by_1, + NULL +}; + +static struct llc_conn_state_trans llc_await_state_trans_14 = { + llc_conn_ev_p_tmr_exp, + LLC_CONN_STATE_AWAIT, + llc_await_ev_qfyrs_14, + llc_await_actions_14 +}; + +/* + * Array of pointers; + * one to each transition + */ +static struct llc_conn_state_trans *llc_await_state_transitions[] = { + &llc_common_state_trans_1, // Request + &llc_common_state_trans_2, + &llc_await_state_trans_1_0, + &llc_common_state_trans_n, + &llc_await_state_trans_1, // Local busy + &llc_common_state_trans_n, + &llc_common_state_trans_n, // Initiate PF Cycle + &llc_common_state_trans_11a, // Timer + &llc_common_state_trans_11b, + &llc_common_state_trans_11c, + &llc_common_state_trans_11d, + &llc_await_state_trans_14, + &llc_common_state_trans_n, + &llc_common_state_trans_3, // Receive frame + &llc_common_state_trans_4, + &llc_common_state_trans_5, + &llc_common_state_trans_6, + &llc_common_state_trans_7a, + &llc_common_state_trans_7b, + &llc_common_state_trans_8a, + &llc_common_state_trans_8b, + &llc_common_state_trans_8c, + &llc_common_state_trans_9, + // &llc_common_state_trans_10, + &llc_await_state_trans_2, + &llc_await_state_trans_3a, + &llc_await_state_trans_3b, + &llc_await_state_trans_4, + &llc_await_state_trans_5, + &llc_await_state_trans_6a, + &llc_await_state_trans_6b, + &llc_await_state_trans_7, + &llc_await_state_trans_8a, + &llc_await_state_trans_8b, + &llc_await_state_trans_9a, + &llc_await_state_trans_9b, + &llc_await_state_trans_9c, + &llc_await_state_trans_9d, + &llc_await_state_trans_10a, + &llc_await_state_trans_10b, + &llc_await_state_trans_11, + &llc_await_state_trans_12a, + &llc_await_state_trans_12b, + &llc_await_state_trans_13, + &llc_common_state_trans_n +}; + +/* ------------------ LLC_CONN_STATE_AWAIT_BUSY transitions ---------------- */ +/* State transitions for LLC_CONN_EV_DATA_CONN_REQ event */ +static llc_conn_ev_qfyr_t llc_await_busy_ev_qfyrs_1_0[] = { + llc_conn_ev_qlfy_set_status_refuse, + NULL +}; + +static llc_conn_action_t llc_await_busy_actions_1_0[] = { + NULL +}; + +static struct llc_conn_state_trans llc_await_busy_state_trans_1_0 = { + llc_conn_ev_data_req, + LLC_CONN_STATE_AWAIT_BUSY, + llc_await_busy_ev_qfyrs_1_0, + llc_await_busy_actions_1_0 +}; + +/* State transitions for LLC_CONN_EV_LOCAL_BUSY_CLEARED event */ +static llc_conn_ev_qfyr_t llc_await_busy_ev_qfyrs_1[] = { + llc_conn_ev_qlfy_data_flag_eq_1, + NULL +}; + +static llc_conn_action_t llc_await_busy_actions_1[] = { + llc_conn_ac_send_rej_xxx_x_set_0, + llc_conn_ac_start_rej_timer, + NULL +}; + +static struct llc_conn_state_trans llc_await_busy_state_trans_1 = { + llc_conn_ev_local_busy_cleared, + LLC_CONN_STATE_AWAIT_REJECT, + llc_await_busy_ev_qfyrs_1, + llc_await_busy_actions_1 +}; + +/* State transitions for LLC_CONN_EV_LOCAL_BUSY_CLEARED event */ +static llc_conn_ev_qfyr_t llc_await_busy_ev_qfyrs_2[] = { + llc_conn_ev_qlfy_data_flag_eq_0, + NULL +}; + +static llc_conn_action_t llc_await_busy_actions_2[] = { + llc_conn_ac_send_rr_xxx_x_set_0, + NULL +}; + +static struct llc_conn_state_trans llc_await_busy_state_trans_2 = { + llc_conn_ev_local_busy_cleared, + LLC_CONN_STATE_AWAIT, + llc_await_busy_ev_qfyrs_2, + llc_await_busy_actions_2 +}; + +/* State transitions for LLC_CONN_EV_LOCAL_BUSY_CLEARED event */ +static llc_conn_ev_qfyr_t llc_await_busy_ev_qfyrs_3[] = { + llc_conn_ev_qlfy_data_flag_eq_2, + NULL +}; + +static llc_conn_action_t llc_await_busy_actions_3[] = { + llc_conn_ac_send_rr_xxx_x_set_0, + NULL +}; + +static struct llc_conn_state_trans llc_await_busy_state_trans_3 = { + llc_conn_ev_local_busy_cleared, + LLC_CONN_STATE_AWAIT_REJECT, + llc_await_busy_ev_qfyrs_3, + llc_await_busy_actions_3 +}; + +/* State transitions for LLC_CONN_EV_RX_I_RSP_Fbit_SET_1_UNEXPD_Ns event */ +static llc_conn_action_t llc_await_busy_actions_4[] = { + llc_conn_ac_opt_send_rnr_xxx_x_set_0, + llc_conn_ac_upd_nr_received, + llc_conn_ac_upd_vs, + llc_conn_ac_stop_p_timer, + llc_conn_ac_set_data_flag_1, + llc_conn_ac_clear_remote_busy, + llc_conn_ac_resend_i_xxx_x_set_0, + NULL +}; + +static struct llc_conn_state_trans llc_await_busy_state_trans_4 = { + llc_conn_ev_rx_i_rsp_fbit_set_1_unexpd_ns, + LLC_CONN_STATE_BUSY, + LLC_NO_EVENT_QUALIFIERS, + llc_await_busy_actions_4 +}; + +/* State transitions for LLC_CONN_EV_RX_I_CMD_Pbit_SET_0_UNEXPD_Ns event */ +static llc_conn_action_t llc_await_busy_actions_5a[] = { + llc_conn_ac_opt_send_rnr_xxx_x_set_0, + llc_conn_ac_upd_nr_received, + llc_conn_ac_upd_vs, + llc_conn_ac_set_data_flag_1, + NULL +}; + +static struct llc_conn_state_trans llc_await_busy_state_trans_5a = { + llc_conn_ev_rx_i_cmd_pbit_set_0_unexpd_ns, + LLC_CONN_STATE_AWAIT_BUSY, + LLC_NO_EVENT_QUALIFIERS, + llc_await_busy_actions_5a +}; + +/* State transitions for LLC_CONN_EV_RX_I_RSP_Fbit_SET_0_UNEXPD_Ns event */ +static llc_conn_action_t llc_await_busy_actions_5b[] = { + llc_conn_ac_opt_send_rnr_xxx_x_set_0, + llc_conn_ac_upd_nr_received, + llc_conn_ac_upd_vs, + llc_conn_ac_set_data_flag_1, + NULL +}; + +static struct llc_conn_state_trans llc_await_busy_state_trans_5b = { + llc_conn_ev_rx_i_rsp_fbit_set_0_unexpd_ns, + LLC_CONN_STATE_AWAIT_BUSY, + LLC_NO_EVENT_QUALIFIERS, + llc_await_busy_actions_5b +}; + +/* State transitions for LLC_CONN_EV_RX_I_CMD_Pbit_SET_1_UNEXPD_Ns event */ +static llc_conn_action_t llc_await_busy_actions_6[] = { + llc_conn_ac_send_rnr_rsp_f_set_1, + llc_conn_ac_upd_nr_received, + llc_conn_ac_upd_vs, + llc_conn_ac_set_data_flag_1, + NULL +}; + +static struct llc_conn_state_trans llc_await_busy_state_trans_6 = { + llc_conn_ev_rx_i_cmd_pbit_set_1_unexpd_ns, + LLC_CONN_STATE_AWAIT_BUSY, + LLC_NO_EVENT_QUALIFIERS, + llc_await_busy_actions_6 +}; + +/* State transitions for LLC_CONN_EV_RX_I_RSP_Fbit_SET_1 event */ +static llc_conn_action_t llc_await_busy_actions_7[] = { + llc_conn_ac_opt_send_rnr_xxx_x_set_0, + llc_conn_ac_inc_vr_by_1, + llc_conn_ac_data_ind, + llc_conn_ac_stop_p_timer, + llc_conn_ac_upd_nr_received, + llc_conn_ac_upd_vs, + llc_conn_ac_set_data_flag_0, + llc_conn_ac_clear_remote_busy, + llc_conn_ac_resend_i_xxx_x_set_0, + NULL +}; + +static struct llc_conn_state_trans llc_await_busy_state_trans_7 = { + llc_conn_ev_rx_i_rsp_fbit_set_1, + LLC_CONN_STATE_BUSY, + LLC_NO_EVENT_QUALIFIERS, + llc_await_busy_actions_7 +}; + +/* State transitions for LLC_CONN_EV_RX_I_RSP_Fbit_SET_0 event */ +static llc_conn_action_t llc_await_busy_actions_8a[] = { + llc_conn_ac_opt_send_rnr_xxx_x_set_0, + llc_conn_ac_inc_vr_by_1, + llc_conn_ac_data_ind, + llc_conn_ac_upd_nr_received, + llc_conn_ac_upd_vs, + llc_conn_ac_set_data_flag_0, + NULL +}; + +static struct llc_conn_state_trans llc_await_busy_state_trans_8a = { + llc_conn_ev_rx_i_rsp_fbit_set_0, + LLC_CONN_STATE_AWAIT_BUSY, + LLC_NO_EVENT_QUALIFIERS, + llc_await_busy_actions_8a +}; + +/* State transitions for LLC_CONN_EV_RX_I_CMD_Pbit_SET_0 event */ +static llc_conn_action_t llc_await_busy_actions_8b[] = { + llc_conn_ac_opt_send_rnr_xxx_x_set_0, + llc_conn_ac_inc_vr_by_1, + llc_conn_ac_data_ind, + llc_conn_ac_upd_nr_received, + llc_conn_ac_upd_vs, + llc_conn_ac_set_data_flag_0, + NULL +}; + +static struct llc_conn_state_trans llc_await_busy_state_trans_8b = { + llc_conn_ev_rx_i_cmd_pbit_set_0, + LLC_CONN_STATE_AWAIT_BUSY, + LLC_NO_EVENT_QUALIFIERS, + llc_await_busy_actions_8b +}; + +/* State transitions for LLC_CONN_EV_RX_I_CMD_Pbit_SET_1 event */ +static llc_conn_action_t llc_await_busy_actions_9[] = { + llc_conn_ac_send_rnr_rsp_f_set_1, + llc_conn_ac_inc_vr_by_1, + llc_conn_ac_data_ind, + llc_conn_ac_upd_nr_received, + llc_conn_ac_upd_vs, + llc_conn_ac_set_data_flag_0, + NULL +}; + +static struct llc_conn_state_trans llc_await_busy_state_trans_9 = { + llc_conn_ev_rx_i_cmd_pbit_set_1, + LLC_CONN_STATE_AWAIT_BUSY, + LLC_NO_EVENT_QUALIFIERS, + llc_await_busy_actions_9 +}; + +/* State transitions for LLC_CONN_EV_RX_RR_RSP_Fbit_SET_1 event */ +static llc_conn_action_t llc_await_busy_actions_10a[] = { + llc_conn_ac_upd_nr_received, + llc_conn_ac_upd_vs, + llc_conn_ac_stop_p_timer, + llc_conn_ac_resend_i_xxx_x_set_0, + llc_conn_ac_clear_remote_busy, + NULL +}; + +static struct llc_conn_state_trans llc_await_busy_state_trans_10a = { + llc_conn_ev_rx_rr_rsp_fbit_set_1, + LLC_CONN_STATE_BUSY, + LLC_NO_EVENT_QUALIFIERS, + llc_await_busy_actions_10a +}; + +/* State transitions for LLC_CONN_EV_RX_REJ_RSP_Fbit_SET_1 event */ +static llc_conn_action_t llc_await_busy_actions_10b[] = { + llc_conn_ac_upd_nr_received, + llc_conn_ac_upd_vs, + llc_conn_ac_stop_p_timer, + llc_conn_ac_resend_i_xxx_x_set_0, + llc_conn_ac_clear_remote_busy, + NULL +}; + +static struct llc_conn_state_trans llc_await_busy_state_trans_10b = { + llc_conn_ev_rx_rej_rsp_fbit_set_1, + LLC_CONN_STATE_BUSY, + LLC_NO_EVENT_QUALIFIERS, + llc_await_busy_actions_10b +}; + +/* State transitions for LLC_CONN_EV_RX_RR_CMD_Pbit_SET_0 event */ +static llc_conn_action_t llc_await_busy_actions_11a[] = { + llc_conn_ac_upd_nr_received, + llc_conn_ac_upd_vs, + llc_conn_ac_clear_remote_busy, + NULL +}; + +static struct llc_conn_state_trans llc_await_busy_state_trans_11a = { + llc_conn_ev_rx_rr_cmd_pbit_set_0, + LLC_CONN_STATE_AWAIT_BUSY, + LLC_NO_EVENT_QUALIFIERS, + llc_await_busy_actions_11a +}; + +/* State transitions for LLC_CONN_EV_RX_RR_RSP_Fbit_SET_0 event */ +static llc_conn_action_t llc_await_busy_actions_11b[] = { + llc_conn_ac_upd_nr_received, + llc_conn_ac_upd_vs, + llc_conn_ac_clear_remote_busy, + NULL +}; + +static struct llc_conn_state_trans llc_await_busy_state_trans_11b = { + llc_conn_ev_rx_rr_rsp_fbit_set_0, + LLC_CONN_STATE_AWAIT_BUSY, + LLC_NO_EVENT_QUALIFIERS, + llc_await_busy_actions_11b +}; + +/* State transitions for LLC_CONN_EV_RX_REJ_CMD_Pbit_SET_0 event */ +static llc_conn_action_t llc_await_busy_actions_11c[] = { + llc_conn_ac_upd_nr_received, + llc_conn_ac_upd_vs, + llc_conn_ac_clear_remote_busy, + NULL +}; + +static struct llc_conn_state_trans llc_await_busy_state_trans_11c = { + llc_conn_ev_rx_rej_cmd_pbit_set_0, + LLC_CONN_STATE_AWAIT_BUSY, + LLC_NO_EVENT_QUALIFIERS, + llc_await_busy_actions_11c +}; + +/* State transitions for LLC_CONN_EV_RX_REJ_RSP_Fbit_SET_0 event */ +static llc_conn_action_t llc_await_busy_actions_11d[] = { + llc_conn_ac_upd_nr_received, + llc_conn_ac_upd_vs, + llc_conn_ac_clear_remote_busy, + NULL +}; + +static struct llc_conn_state_trans llc_await_busy_state_trans_11d = { + llc_conn_ev_rx_rej_rsp_fbit_set_0, + LLC_CONN_STATE_AWAIT_BUSY, + LLC_NO_EVENT_QUALIFIERS, + llc_await_busy_actions_11d +}; + +/* State transitions for LLC_CONN_EV_RX_RR_CMD_Pbit_SET_1 event */ +static llc_conn_action_t llc_await_busy_actions_12a[] = { + llc_conn_ac_send_rnr_rsp_f_set_1, + llc_conn_ac_upd_nr_received, + llc_conn_ac_upd_vs, + llc_conn_ac_clear_remote_busy, + NULL +}; + +static struct llc_conn_state_trans llc_await_busy_state_trans_12a = { + llc_conn_ev_rx_rr_cmd_pbit_set_1, + LLC_CONN_STATE_AWAIT_BUSY, + LLC_NO_EVENT_QUALIFIERS, + llc_await_busy_actions_12a +}; + +/* State transitions for LLC_CONN_EV_RX_REJ_CMD_Pbit_SET_1 event */ +static llc_conn_action_t llc_await_busy_actions_12b[] = { + llc_conn_ac_send_rnr_rsp_f_set_1, + llc_conn_ac_upd_nr_received, + llc_conn_ac_upd_vs, + llc_conn_ac_clear_remote_busy, + NULL +}; + +static struct llc_conn_state_trans llc_await_busy_state_trans_12b = { + llc_conn_ev_rx_rej_cmd_pbit_set_1, + LLC_CONN_STATE_AWAIT_BUSY, + LLC_NO_EVENT_QUALIFIERS, + llc_await_busy_actions_12b +}; + +/* State transitions for LLC_CONN_EV_RX_RNR_RSP_Fbit_SET_1 event */ +static llc_conn_action_t llc_await_busy_actions_13[] = { + llc_conn_ac_upd_nr_received, + llc_conn_ac_upd_vs, + llc_conn_ac_stop_p_timer, + llc_conn_ac_set_remote_busy, + NULL +}; + +static struct llc_conn_state_trans llc_await_busy_state_trans_13 = { + llc_conn_ev_rx_rnr_rsp_fbit_set_1, + LLC_CONN_STATE_BUSY, + LLC_NO_EVENT_QUALIFIERS, + llc_await_busy_actions_13 +}; + +/* State transitions for LLC_CONN_EV_RX_RNR_CMD_Pbit_SET_0 event */ +static llc_conn_action_t llc_await_busy_actions_14a[] = { + llc_conn_ac_upd_nr_received, + llc_conn_ac_upd_vs, + llc_conn_ac_set_remote_busy, + NULL +}; + +static struct llc_conn_state_trans llc_await_busy_state_trans_14a = { + llc_conn_ev_rx_rnr_cmd_pbit_set_0, + LLC_CONN_STATE_AWAIT_BUSY, + LLC_NO_EVENT_QUALIFIERS, + llc_await_busy_actions_14a +}; + +/* State transitions for LLC_CONN_EV_RX_RNR_RSP_Fbit_SET_0 event */ +static llc_conn_action_t llc_await_busy_actions_14b[] = { + llc_conn_ac_upd_nr_received, + llc_conn_ac_upd_vs, + llc_conn_ac_set_remote_busy, + NULL +}; + +static struct llc_conn_state_trans llc_await_busy_state_trans_14b = { + llc_conn_ev_rx_rnr_rsp_fbit_set_0, + LLC_CONN_STATE_AWAIT_BUSY, + LLC_NO_EVENT_QUALIFIERS, + llc_await_busy_actions_14b +}; + +/* State transitions for LLC_CONN_EV_RX_RNR_CMD_Pbit_SET_1 event */ +static llc_conn_action_t llc_await_busy_actions_15[] = { + llc_conn_ac_send_rnr_rsp_f_set_1, + llc_conn_ac_upd_nr_received, + llc_conn_ac_upd_vs, + llc_conn_ac_set_remote_busy, + NULL +}; + +static struct llc_conn_state_trans llc_await_busy_state_trans_15 = { + llc_conn_ev_rx_rnr_cmd_pbit_set_1, + LLC_CONN_STATE_AWAIT_BUSY, + LLC_NO_EVENT_QUALIFIERS, + llc_await_busy_actions_15 +}; + +/* State transitions for LLC_CONN_EV_P_TMR_EXP event */ +static llc_conn_ev_qfyr_t llc_await_busy_ev_qfyrs_16[] = { + llc_conn_ev_qlfy_retry_cnt_lt_n2, + NULL +}; + +static llc_conn_action_t llc_await_busy_actions_16[] = { + llc_conn_ac_send_rnr_cmd_p_set_1, + llc_conn_ac_start_p_timer, + llc_conn_ac_inc_retry_cnt_by_1, + NULL +}; + +static struct llc_conn_state_trans llc_await_busy_state_trans_16 = { + llc_conn_ev_p_tmr_exp, + LLC_CONN_STATE_AWAIT_BUSY, + llc_await_busy_ev_qfyrs_16, + llc_await_busy_actions_16 +}; + +/* + * Array of pointers; + * one to each transition + */ +static struct llc_conn_state_trans *llc_await_busy_state_transitions[] = { + &llc_common_state_trans_1, // Request + &llc_common_state_trans_2, + &llc_await_busy_state_trans_1_0, + &llc_common_state_trans_n, + &llc_await_busy_state_trans_1, // Local busy + &llc_await_busy_state_trans_2, + &llc_await_busy_state_trans_3, + &llc_common_state_trans_n, + &llc_common_state_trans_n, // Initiate PF cycle + &llc_common_state_trans_11a, // Timer + &llc_common_state_trans_11b, + &llc_common_state_trans_11c, + &llc_common_state_trans_11d, + &llc_await_busy_state_trans_16, + &llc_common_state_trans_n, + &llc_await_busy_state_trans_4, // Receive frame + &llc_await_busy_state_trans_5a, + &llc_await_busy_state_trans_5b, + &llc_await_busy_state_trans_6, + &llc_await_busy_state_trans_7, + &llc_await_busy_state_trans_8a, + &llc_await_busy_state_trans_8b, + &llc_await_busy_state_trans_9, + &llc_await_busy_state_trans_10a, + &llc_await_busy_state_trans_10b, + &llc_await_busy_state_trans_11a, + &llc_await_busy_state_trans_11b, + &llc_await_busy_state_trans_11c, + &llc_await_busy_state_trans_11d, + &llc_await_busy_state_trans_12a, + &llc_await_busy_state_trans_12b, + &llc_await_busy_state_trans_13, + &llc_await_busy_state_trans_14a, + &llc_await_busy_state_trans_14b, + &llc_await_busy_state_trans_15, + &llc_common_state_trans_3, + &llc_common_state_trans_4, + &llc_common_state_trans_5, + &llc_common_state_trans_6, + &llc_common_state_trans_7a, + &llc_common_state_trans_7b, + &llc_common_state_trans_8a, + &llc_common_state_trans_8b, + &llc_common_state_trans_8c, + &llc_common_state_trans_9, + // &llc_common_state_trans_10, + &llc_common_state_trans_n +}; + +/* ----------------- LLC_CONN_STATE_AWAIT_REJECT transitions --------------- */ +/* State transitions for LLC_CONN_EV_DATA_CONN_REQ event */ +static llc_conn_ev_qfyr_t llc_await_reject_ev_qfyrs_1_0[] = { + llc_conn_ev_qlfy_set_status_refuse, + NULL +}; + +static llc_conn_action_t llc_await_reject_actions_1_0[] = { + NULL +}; + +static struct llc_conn_state_trans llc_await_reject_state_trans_1_0 = { + llc_conn_ev_data_req, + LLC_CONN_STATE_AWAIT_REJECT, + llc_await_reject_ev_qfyrs_1_0, + llc_await_reject_actions_1_0 +}; + +/* State transitions for LLC_CONN_EV_LOCAL_BUSY_DETECTED event */ +static llc_conn_action_t llc_await_rejct_actions_1[] = { + llc_conn_ac_send_rnr_xxx_x_set_0, + llc_conn_ac_set_data_flag_2, + NULL +}; + +static struct llc_conn_state_trans llc_await_rejct_state_trans_1 = { + llc_conn_ev_local_busy_detected, + LLC_CONN_STATE_AWAIT_BUSY, + LLC_NO_EVENT_QUALIFIERS, + llc_await_rejct_actions_1 +}; + +/* State transitions for LLC_CONN_EV_RX_I_CMD_Pbit_SET_0_UNEXPD_Ns event */ +static llc_conn_action_t llc_await_rejct_actions_2a[] = { + llc_conn_ac_upd_nr_received, + llc_conn_ac_upd_vs, + NULL +}; + +static struct llc_conn_state_trans llc_await_rejct_state_trans_2a = { + llc_conn_ev_rx_i_cmd_pbit_set_0_unexpd_ns, + LLC_CONN_STATE_AWAIT_REJECT, + LLC_NO_EVENT_QUALIFIERS, + llc_await_rejct_actions_2a +}; + +/* State transitions for LLC_CONN_EV_RX_I_RSP_Fbit_SET_0_UNEXPD_Ns event */ +static llc_conn_action_t llc_await_rejct_actions_2b[] = { + llc_conn_ac_upd_nr_received, + llc_conn_ac_upd_vs, + NULL +}; + +static struct llc_conn_state_trans llc_await_rejct_state_trans_2b = { + llc_conn_ev_rx_i_rsp_fbit_set_0_unexpd_ns, + LLC_CONN_STATE_AWAIT_REJECT, + LLC_NO_EVENT_QUALIFIERS, + llc_await_rejct_actions_2b +}; + +/* State transitions for LLC_CONN_EV_RX_I_CMD_Pbit_SET_1_UNEXPD_Ns event */ +static llc_conn_action_t llc_await_rejct_actions_3[] = { + llc_conn_ac_send_rr_rsp_f_set_1, + llc_conn_ac_upd_nr_received, + llc_conn_ac_upd_vs, + NULL +}; + +static struct llc_conn_state_trans llc_await_rejct_state_trans_3 = { + llc_conn_ev_rx_i_cmd_pbit_set_1_unexpd_ns, + LLC_CONN_STATE_AWAIT_REJECT, + LLC_NO_EVENT_QUALIFIERS, + llc_await_rejct_actions_3 +}; + +/* State transitions for LLC_CONN_EV_RX_I_RSP_Fbit_SET_1 event */ +static llc_conn_action_t llc_await_rejct_actions_4[] = { + llc_conn_ac_inc_vr_by_1, + llc_conn_ac_data_ind, + llc_conn_ac_stop_p_timer, + llc_conn_ac_stop_rej_timer, + llc_conn_ac_upd_nr_received, + llc_conn_ac_upd_vs, + llc_conn_ac_resend_i_xxx_x_set_0_or_send_rr, + llc_conn_ac_clear_remote_busy, + NULL +}; + +static struct llc_conn_state_trans llc_await_rejct_state_trans_4 = { + llc_conn_ev_rx_i_rsp_fbit_set_1, + LLC_CONN_STATE_NORMAL, + LLC_NO_EVENT_QUALIFIERS, + llc_await_rejct_actions_4 +}; + +/* State transitions for LLC_CONN_EV_RX_I_RSP_Fbit_SET_0 event */ +static llc_conn_action_t llc_await_rejct_actions_5a[] = { + llc_conn_ac_inc_vr_by_1, + llc_conn_ac_data_ind, + llc_conn_ac_send_rr_xxx_x_set_0, + llc_conn_ac_stop_rej_timer, + llc_conn_ac_upd_nr_received, + llc_conn_ac_upd_vs, + NULL +}; + +static struct llc_conn_state_trans llc_await_rejct_state_trans_5a = { + llc_conn_ev_rx_i_rsp_fbit_set_0, + LLC_CONN_STATE_AWAIT, + LLC_NO_EVENT_QUALIFIERS, + llc_await_rejct_actions_5a +}; + +/* State transitions for LLC_CONN_EV_RX_I_CMD_Pbit_SET_0 event */ +static llc_conn_action_t llc_await_rejct_actions_5b[] = { + llc_conn_ac_inc_vr_by_1, + llc_conn_ac_data_ind, + llc_conn_ac_send_rr_xxx_x_set_0, + llc_conn_ac_stop_rej_timer, + llc_conn_ac_upd_nr_received, + llc_conn_ac_upd_vs, + NULL +}; + +static struct llc_conn_state_trans llc_await_rejct_state_trans_5b = { + llc_conn_ev_rx_i_cmd_pbit_set_0, LLC_CONN_STATE_AWAIT, + LLC_NO_EVENT_QUALIFIERS, llc_await_rejct_actions_5b +}; + +/* State transitions for LLC_CONN_EV_RX_I_CMD_Pbit_SET_1 event */ +static llc_conn_action_t llc_await_rejct_actions_6[] = { + llc_conn_ac_inc_vr_by_1, + llc_conn_ac_data_ind, + llc_conn_ac_send_rr_rsp_f_set_1, + llc_conn_ac_stop_rej_timer, + llc_conn_ac_upd_nr_received, + llc_conn_ac_upd_vs, + NULL +}; + +static struct llc_conn_state_trans llc_await_rejct_state_trans_6 = { + llc_conn_ev_rx_i_cmd_pbit_set_1, + LLC_CONN_STATE_AWAIT, + LLC_NO_EVENT_QUALIFIERS, + llc_await_rejct_actions_6 +}; + +/* State transitions for LLC_CONN_EV_RX_RR_RSP_Fbit_SET_1 event */ +static llc_conn_action_t llc_await_rejct_actions_7a[] = { + llc_conn_ac_upd_nr_received, + llc_conn_ac_upd_vs, + llc_conn_ac_stop_p_timer, + llc_conn_ac_resend_i_xxx_x_set_0, + llc_conn_ac_clear_remote_busy, + NULL +}; + +static struct llc_conn_state_trans llc_await_rejct_state_trans_7a = { + llc_conn_ev_rx_rr_rsp_fbit_set_1, + LLC_CONN_STATE_REJECT, + LLC_NO_EVENT_QUALIFIERS, + llc_await_rejct_actions_7a +}; + +/* State transitions for LLC_CONN_EV_RX_REJ_RSP_Fbit_SET_1 event */ +static llc_conn_action_t llc_await_rejct_actions_7b[] = { + llc_conn_ac_upd_nr_received, + llc_conn_ac_upd_vs, + llc_conn_ac_stop_p_timer, + llc_conn_ac_resend_i_xxx_x_set_0, + llc_conn_ac_clear_remote_busy, + NULL +}; + +static struct llc_conn_state_trans llc_await_rejct_state_trans_7b = { + llc_conn_ev_rx_rej_rsp_fbit_set_1, + LLC_CONN_STATE_REJECT, + LLC_NO_EVENT_QUALIFIERS, + llc_await_rejct_actions_7b +}; + +/* State transitions for LLC_CONN_EV_RX_I_RSP_Fbit_SET_1_UNEXPD_Ns event */ +static llc_conn_action_t llc_await_rejct_actions_7c[] = { + llc_conn_ac_upd_nr_received, + llc_conn_ac_upd_vs, + llc_conn_ac_stop_p_timer, + llc_conn_ac_resend_i_xxx_x_set_0, + llc_conn_ac_clear_remote_busy, + NULL +}; + +static struct llc_conn_state_trans llc_await_rejct_state_trans_7c = { + llc_conn_ev_rx_i_rsp_fbit_set_1_unexpd_ns, + LLC_CONN_STATE_REJECT, + LLC_NO_EVENT_QUALIFIERS, + llc_await_rejct_actions_7c +}; + +/* State transitions for LLC_CONN_EV_RX_RR_CMD_Pbit_SET_0 event */ +static llc_conn_action_t llc_await_rejct_actions_8a[] = { + llc_conn_ac_upd_nr_received, + llc_conn_ac_upd_vs, + llc_conn_ac_clear_remote_busy, + NULL +}; + +static struct llc_conn_state_trans llc_await_rejct_state_trans_8a = { + llc_conn_ev_rx_rr_cmd_pbit_set_0, + LLC_CONN_STATE_AWAIT_REJECT, + LLC_NO_EVENT_QUALIFIERS, + llc_await_rejct_actions_8a +}; + +/* State transitions for LLC_CONN_EV_RX_RR_RSP_Fbit_SET_0 event */ +static llc_conn_action_t llc_await_rejct_actions_8b[] = { + llc_conn_ac_upd_nr_received, + llc_conn_ac_upd_vs, + llc_conn_ac_clear_remote_busy, + NULL +}; + +static struct llc_conn_state_trans llc_await_rejct_state_trans_8b = { + llc_conn_ev_rx_rr_rsp_fbit_set_0, + LLC_CONN_STATE_AWAIT_REJECT, + LLC_NO_EVENT_QUALIFIERS, + llc_await_rejct_actions_8b +}; + +/* State transitions for LLC_CONN_EV_RX_REJ_CMD_Pbit_SET_0 event */ +static llc_conn_action_t llc_await_rejct_actions_8c[] = { + llc_conn_ac_upd_nr_received, + llc_conn_ac_upd_vs, + llc_conn_ac_clear_remote_busy, + NULL +}; + +static struct llc_conn_state_trans llc_await_rejct_state_trans_8c = { + llc_conn_ev_rx_rej_cmd_pbit_set_0, + LLC_CONN_STATE_AWAIT_REJECT, + LLC_NO_EVENT_QUALIFIERS, + llc_await_rejct_actions_8c +}; + +/* State transitions for LLC_CONN_EV_RX_REJ_RSP_Fbit_SET_0 event */ +static llc_conn_action_t llc_await_rejct_actions_8d[] = { + llc_conn_ac_upd_nr_received, + llc_conn_ac_upd_vs, + llc_conn_ac_clear_remote_busy, + NULL +}; + +static struct llc_conn_state_trans llc_await_rejct_state_trans_8d = { + llc_conn_ev_rx_rej_rsp_fbit_set_0, + LLC_CONN_STATE_AWAIT_REJECT, + LLC_NO_EVENT_QUALIFIERS, + llc_await_rejct_actions_8d +}; + +/* State transitions for LLC_CONN_EV_RX_RR_CMD_Pbit_SET_1 event */ +static llc_conn_action_t llc_await_rejct_actions_9a[] = { + llc_conn_ac_send_rr_rsp_f_set_1, + llc_conn_ac_upd_nr_received, + llc_conn_ac_upd_vs, + llc_conn_ac_clear_remote_busy, + NULL +}; + +static struct llc_conn_state_trans llc_await_rejct_state_trans_9a = { + llc_conn_ev_rx_rr_cmd_pbit_set_1, + LLC_CONN_STATE_AWAIT_REJECT, + LLC_NO_EVENT_QUALIFIERS, + llc_await_rejct_actions_9a +}; + +/* State transitions for LLC_CONN_EV_RX_REJ_CMD_Pbit_SET_1 event */ +static llc_conn_action_t llc_await_rejct_actions_9b[] = { + llc_conn_ac_send_rr_rsp_f_set_1, + llc_conn_ac_upd_nr_received, + llc_conn_ac_upd_vs, + llc_conn_ac_clear_remote_busy, + NULL +}; + +static struct llc_conn_state_trans llc_await_rejct_state_trans_9b = { + llc_conn_ev_rx_rej_cmd_pbit_set_1, + LLC_CONN_STATE_AWAIT_REJECT, + LLC_NO_EVENT_QUALIFIERS, + llc_await_rejct_actions_9b +}; + +/* State transitions for LLC_CONN_EV_RX_RNR_RSP_Fbit_SET_1 event */ +static llc_conn_action_t llc_await_rejct_actions_10[] = { + llc_conn_ac_upd_nr_received, + llc_conn_ac_upd_vs, + llc_conn_ac_stop_p_timer, + llc_conn_ac_set_remote_busy, + NULL +}; + +static struct llc_conn_state_trans llc_await_rejct_state_trans_10 = { + llc_conn_ev_rx_rnr_rsp_fbit_set_1, + LLC_CONN_STATE_REJECT, + LLC_NO_EVENT_QUALIFIERS, + llc_await_rejct_actions_10 +}; + +/* State transitions for LLC_CONN_EV_RX_RNR_CMD_Pbit_SET_0 event */ +static llc_conn_action_t llc_await_rejct_actions_11a[] = { + llc_conn_ac_upd_nr_received, + llc_conn_ac_upd_vs, + llc_conn_ac_set_remote_busy, + NULL +}; + +static struct llc_conn_state_trans llc_await_rejct_state_trans_11a = { + llc_conn_ev_rx_rnr_cmd_pbit_set_0, + LLC_CONN_STATE_AWAIT_REJECT, + LLC_NO_EVENT_QUALIFIERS, + llc_await_rejct_actions_11a +}; + +/* State transitions for LLC_CONN_EV_RX_RNR_RSP_Fbit_SET_0 event */ +static llc_conn_action_t llc_await_rejct_actions_11b[] = { + llc_conn_ac_upd_nr_received, + llc_conn_ac_upd_vs, + llc_conn_ac_set_remote_busy, + NULL +}; + +static struct llc_conn_state_trans llc_await_rejct_state_trans_11b = { + llc_conn_ev_rx_rnr_rsp_fbit_set_0, + LLC_CONN_STATE_AWAIT_REJECT, + LLC_NO_EVENT_QUALIFIERS, + llc_await_rejct_actions_11b +}; + +/* State transitions for LLC_CONN_EV_RX_RNR_CMD_Pbit_SET_1 event */ +static llc_conn_action_t llc_await_rejct_actions_12[] = { + llc_conn_ac_send_rr_rsp_f_set_1, + llc_conn_ac_upd_nr_received, + llc_conn_ac_upd_vs, + llc_conn_ac_set_remote_busy, + NULL +}; + +static struct llc_conn_state_trans llc_await_rejct_state_trans_12 = { + llc_conn_ev_rx_rnr_cmd_pbit_set_1, + LLC_CONN_STATE_AWAIT_REJECT, + LLC_NO_EVENT_QUALIFIERS, + llc_await_rejct_actions_12 +}; + +/* State transitions for LLC_CONN_EV_P_TMR_EXP event */ +static llc_conn_ev_qfyr_t llc_await_rejct_ev_qfyrs_13[] = { + llc_conn_ev_qlfy_retry_cnt_lt_n2, + NULL +}; + +static llc_conn_action_t llc_await_rejct_actions_13[] = { + llc_conn_ac_send_rej_cmd_p_set_1, + llc_conn_ac_stop_p_timer, + llc_conn_ac_inc_retry_cnt_by_1, + NULL +}; + +static struct llc_conn_state_trans llc_await_rejct_state_trans_13 = { + llc_conn_ev_p_tmr_exp, + LLC_CONN_STATE_AWAIT_REJECT, + llc_await_rejct_ev_qfyrs_13, + llc_await_rejct_actions_13 +}; + +/* + * Array of pointers; + * one to each transition + */ +static struct llc_conn_state_trans *llc_await_rejct_state_transitions[] = { + &llc_await_reject_state_trans_1_0, + &llc_common_state_trans_1, // requests + &llc_common_state_trans_2, + &llc_common_state_trans_n, + &llc_await_rejct_state_trans_1, // local busy + &llc_common_state_trans_n, + &llc_common_state_trans_n, // Initiate PF cycle + &llc_await_rejct_state_trans_13, // timers + &llc_common_state_trans_11a, + &llc_common_state_trans_11b, + &llc_common_state_trans_11c, + &llc_common_state_trans_11d, + &llc_common_state_trans_n, + &llc_await_rejct_state_trans_2a, // receive frames + &llc_await_rejct_state_trans_2b, + &llc_await_rejct_state_trans_3, + &llc_await_rejct_state_trans_4, + &llc_await_rejct_state_trans_5a, + &llc_await_rejct_state_trans_5b, + &llc_await_rejct_state_trans_6, + &llc_await_rejct_state_trans_7a, + &llc_await_rejct_state_trans_7b, + &llc_await_rejct_state_trans_7c, + &llc_await_rejct_state_trans_8a, + &llc_await_rejct_state_trans_8b, + &llc_await_rejct_state_trans_8c, + &llc_await_rejct_state_trans_8d, + &llc_await_rejct_state_trans_9a, + &llc_await_rejct_state_trans_9b, + &llc_await_rejct_state_trans_10, + &llc_await_rejct_state_trans_11a, + &llc_await_rejct_state_trans_11b, + &llc_await_rejct_state_trans_12, + &llc_common_state_trans_3, + &llc_common_state_trans_4, + &llc_common_state_trans_5, + &llc_common_state_trans_6, + &llc_common_state_trans_7a, + &llc_common_state_trans_7b, + &llc_common_state_trans_8a, + &llc_common_state_trans_8b, + &llc_common_state_trans_8c, + &llc_common_state_trans_9, + // &llc_common_state_trans_10, + &llc_common_state_trans_n +}; + +/* -------------------- LLC_CONN_STATE_D_CONN transitions ------------------ */ +/* State transitions for LLC_CONN_EV_RX_SABME_CMD_Pbit_SET_X event, + * cause_flag = 1 */ +static llc_conn_ev_qfyr_t llc_d_conn_ev_qfyrs_1[] = { + llc_conn_ev_qlfy_cause_flag_eq_1, + llc_conn_ev_qlfy_set_status_conflict, + NULL +}; + +static llc_conn_action_t llc_d_conn_actions_1[] = { + llc_conn_ac_send_dm_rsp_f_set_p, + llc_conn_ac_stop_ack_timer, + llc_conn_ac_disc_confirm, + llc_conn_disc, + NULL +}; + +static struct llc_conn_state_trans llc_d_conn_state_trans_1 = { + llc_conn_ev_rx_sabme_cmd_pbit_set_x, + LLC_CONN_STATE_ADM, + llc_d_conn_ev_qfyrs_1, + llc_d_conn_actions_1 +}; + +/* State transitions for LLC_CONN_EV_RX_SABME_CMD_Pbit_SET_X event, + * cause_flag = 0 + */ +static llc_conn_ev_qfyr_t llc_d_conn_ev_qfyrs_1_1[] = { + llc_conn_ev_qlfy_cause_flag_eq_0, + llc_conn_ev_qlfy_set_status_conflict, + NULL +}; + +static llc_conn_action_t llc_d_conn_actions_1_1[] = { + llc_conn_ac_send_dm_rsp_f_set_p, + llc_conn_ac_stop_ack_timer, + llc_conn_disc, + NULL +}; + +static struct llc_conn_state_trans llc_d_conn_state_trans_1_1 = { + llc_conn_ev_rx_sabme_cmd_pbit_set_x, + LLC_CONN_STATE_ADM, + llc_d_conn_ev_qfyrs_1_1, + llc_d_conn_actions_1_1 +}; + +/* State transitions for LLC_CONN_EV_RX_UA_RSP_Fbit_SET_X event, + * cause_flag = 1 */ +static llc_conn_ev_qfyr_t llc_d_conn_ev_qfyrs_2[] = { + llc_conn_ev_qlfy_p_flag_eq_f, + llc_conn_ev_qlfy_cause_flag_eq_1, + llc_conn_ev_qlfy_set_status_disc, + NULL +}; + +static llc_conn_action_t llc_d_conn_actions_2[] = { + llc_conn_ac_stop_ack_timer, + llc_conn_ac_disc_confirm, + llc_conn_disc, + NULL +}; + +static struct llc_conn_state_trans llc_d_conn_state_trans_2 = { + llc_conn_ev_rx_ua_rsp_fbit_set_x, + LLC_CONN_STATE_ADM, + llc_d_conn_ev_qfyrs_2, + llc_d_conn_actions_2 +}; + +/* State transitions for LLC_CONN_EV_RX_UA_RSP_Fbit_SET_X event, + * cause_flag = 0 */ +static llc_conn_ev_qfyr_t llc_d_conn_ev_qfyrs_2_1[] = { + llc_conn_ev_qlfy_p_flag_eq_f, + llc_conn_ev_qlfy_cause_flag_eq_0, + llc_conn_ev_qlfy_set_status_disc, + NULL +}; + +static llc_conn_action_t llc_d_conn_actions_2_1[] = { + llc_conn_ac_stop_ack_timer, + llc_conn_disc, + NULL +}; + +static struct llc_conn_state_trans llc_d_conn_state_trans_2_1 = { + llc_conn_ev_rx_ua_rsp_fbit_set_x, + LLC_CONN_STATE_ADM, + llc_d_conn_ev_qfyrs_2_1, + llc_d_conn_actions_2_1 +}; + +/* State transitions for LLC_CONN_EV_RX_DISC_CMD_Pbit_SET_X event */ +static llc_conn_action_t llc_d_conn_actions_3[] = { + llc_conn_ac_send_ua_rsp_f_set_p, + NULL +}; + +static struct llc_conn_state_trans llc_d_conn_state_trans_3 = { + llc_conn_ev_rx_disc_cmd_pbit_set_x, + LLC_CONN_STATE_D_CONN, + LLC_NO_EVENT_QUALIFIERS, + llc_d_conn_actions_3 +}; + +/* State transitions for LLC_CONN_EV_RX_DM_RSP_Fbit_SET_X event, + * cause_flag = 1 */ +static llc_conn_ev_qfyr_t llc_d_conn_ev_qfyrs_4[] = { + llc_conn_ev_qlfy_cause_flag_eq_1, + llc_conn_ev_qlfy_set_status_disc, + NULL +}; + +static llc_conn_action_t llc_d_conn_actions_4[] = { + llc_conn_ac_stop_ack_timer, + llc_conn_ac_disc_confirm, + llc_conn_disc, + NULL +}; + +static struct llc_conn_state_trans llc_d_conn_state_trans_4 = { + llc_conn_ev_rx_dm_rsp_fbit_set_x, + LLC_CONN_STATE_ADM, + llc_d_conn_ev_qfyrs_4, + llc_d_conn_actions_4 +}; + +/* State transitions for LLC_CONN_EV_RX_DM_RSP_Fbit_SET_X event, + * cause_flag = 0 */ +static llc_conn_ev_qfyr_t llc_d_conn_ev_qfyrs_4_1[] = { + llc_conn_ev_qlfy_cause_flag_eq_0, + llc_conn_ev_qlfy_set_status_disc, + NULL +}; + +static llc_conn_action_t llc_d_conn_actions_4_1[] = { + llc_conn_ac_stop_ack_timer, + llc_conn_disc, + NULL +}; + +static struct llc_conn_state_trans llc_d_conn_state_trans_4_1 = { + llc_conn_ev_rx_dm_rsp_fbit_set_x, + LLC_CONN_STATE_ADM, + llc_d_conn_ev_qfyrs_4_1, + llc_d_conn_actions_4_1 +}; + +/* + * State transition for + * LLC_CONN_EV_DATA_CONN_REQ event + */ +static llc_conn_ev_qfyr_t llc_d_conn_ev_qfyrs_5[] = { + llc_conn_ev_qlfy_set_status_refuse, + NULL +}; + +static llc_conn_action_t llc_d_conn_actions_5[] = { + NULL +}; + +static struct llc_conn_state_trans llc_d_conn_state_trans_5 = { + llc_conn_ev_data_req, LLC_CONN_STATE_D_CONN, + llc_d_conn_ev_qfyrs_5, llc_d_conn_actions_5 +}; + +/* State transitions for LLC_CONN_EV_ACK_TMR_EXP event */ +static llc_conn_ev_qfyr_t llc_d_conn_ev_qfyrs_6[] = { + llc_conn_ev_qlfy_retry_cnt_lt_n2, + NULL +}; + +static llc_conn_action_t llc_d_conn_actions_6[] = { + llc_conn_ac_send_disc_cmd_p_set_x, + llc_conn_ac_start_ack_timer, + llc_conn_ac_inc_retry_cnt_by_1, + NULL +}; + +static struct llc_conn_state_trans llc_d_conn_state_trans_6 = { + llc_conn_ev_ack_tmr_exp, + LLC_CONN_STATE_D_CONN, + llc_d_conn_ev_qfyrs_6, + llc_d_conn_actions_6 +}; + +/* State transitions for LLC_CONN_EV_ACK_TMR_EXP event, cause_flag = 1 */ +static llc_conn_ev_qfyr_t llc_d_conn_ev_qfyrs_7[] = { + llc_conn_ev_qlfy_retry_cnt_gte_n2, + llc_conn_ev_qlfy_cause_flag_eq_1, + llc_conn_ev_qlfy_set_status_failed, + NULL +}; + +static llc_conn_action_t llc_d_conn_actions_7[] = { + llc_conn_ac_disc_confirm, + llc_conn_disc, + NULL +}; + +static struct llc_conn_state_trans llc_d_conn_state_trans_7 = { + llc_conn_ev_ack_tmr_exp, + LLC_CONN_STATE_ADM, + llc_d_conn_ev_qfyrs_7, + llc_d_conn_actions_7 +}; + +/* State transitions for LLC_CONN_EV_ACK_TMR_EXP event, cause_flag = 0 */ +static llc_conn_ev_qfyr_t llc_d_conn_ev_qfyrs_8[] = { + llc_conn_ev_qlfy_retry_cnt_gte_n2, + llc_conn_ev_qlfy_cause_flag_eq_0, + llc_conn_ev_qlfy_set_status_failed, + NULL +}; + +static llc_conn_action_t llc_d_conn_actions_8[] = { + llc_conn_disc, + NULL +}; + +static struct llc_conn_state_trans llc_d_conn_state_trans_8 = { + llc_conn_ev_ack_tmr_exp, + LLC_CONN_STATE_ADM, + llc_d_conn_ev_qfyrs_8, + llc_d_conn_actions_8 +}; + +/* + * Array of pointers; + * one to each transition + */ +static struct llc_conn_state_trans *llc_d_conn_state_transitions[] = { + &llc_d_conn_state_trans_5, // Request + &llc_common_state_trans_n, + &llc_common_state_trans_n, // Local busy + &llc_common_state_trans_n, // Initiate PF cycle + &llc_d_conn_state_trans_6, // Timer + &llc_d_conn_state_trans_7, + &llc_d_conn_state_trans_8, + &llc_common_state_trans_n, + &llc_d_conn_state_trans_1, // Receive frame + &llc_d_conn_state_trans_1_1, + &llc_d_conn_state_trans_2, + &llc_d_conn_state_trans_2_1, + &llc_d_conn_state_trans_3, + &llc_d_conn_state_trans_4, + &llc_d_conn_state_trans_4_1, + &llc_common_state_trans_n +}; + +/* -------------------- LLC_CONN_STATE_RESET transitions ------------------- */ +/* State transitions for LLC_CONN_EV_RX_SABME_CMD_Pbit_SET_X event */ +static llc_conn_action_t llc_rst_actions_1[] = { + llc_conn_ac_set_vs_0, + llc_conn_ac_set_vr_0, + llc_conn_ac_set_s_flag_1, + llc_conn_ac_send_ua_rsp_f_set_p, + NULL +}; + +static struct llc_conn_state_trans llc_rst_state_trans_1 = { + llc_conn_ev_rx_sabme_cmd_pbit_set_x, + LLC_CONN_STATE_RESET, + LLC_NO_EVENT_QUALIFIERS, + llc_rst_actions_1 +}; + +/* State transitions for LLC_CONN_EV_RX_UA_RSP_Fbit_SET_X event, + * cause_flag = 1 */ +static llc_conn_ev_qfyr_t llc_rst_ev_qfyrs_2[] = { + llc_conn_ev_qlfy_p_flag_eq_f, + llc_conn_ev_qlfy_cause_flag_eq_1, + llc_conn_ev_qlfy_set_status_conn, + NULL +}; + +static llc_conn_action_t llc_rst_actions_2[] = { + llc_conn_ac_stop_ack_timer, + llc_conn_ac_set_vs_0, + llc_conn_ac_set_vr_0, + llc_conn_ac_upd_p_flag, + llc_conn_ac_rst_confirm, + llc_conn_ac_set_remote_busy_0, + llc_conn_reset, + NULL +}; + +static struct llc_conn_state_trans llc_rst_state_trans_2 = { + llc_conn_ev_rx_ua_rsp_fbit_set_x, + LLC_CONN_STATE_NORMAL, + llc_rst_ev_qfyrs_2, + llc_rst_actions_2 +}; + +/* State transitions for LLC_CONN_EV_RX_UA_RSP_Fbit_SET_X event, + * cause_flag = 0 */ +static llc_conn_ev_qfyr_t llc_rst_ev_qfyrs_2_1[] = { + llc_conn_ev_qlfy_p_flag_eq_f, + llc_conn_ev_qlfy_cause_flag_eq_0, + llc_conn_ev_qlfy_set_status_rst_done, + NULL +}; + +static llc_conn_action_t llc_rst_actions_2_1[] = { + llc_conn_ac_stop_ack_timer, + llc_conn_ac_set_vs_0, + llc_conn_ac_set_vr_0, + llc_conn_ac_upd_p_flag, + llc_conn_ac_rst_confirm, + llc_conn_ac_set_remote_busy_0, + llc_conn_reset, + NULL +}; + +static struct llc_conn_state_trans llc_rst_state_trans_2_1 = { + llc_conn_ev_rx_ua_rsp_fbit_set_x, + LLC_CONN_STATE_NORMAL, + llc_rst_ev_qfyrs_2_1, + llc_rst_actions_2_1 +}; + +/* State transitions for LLC_CONN_EV_ACK_TMR_EXP event */ +static llc_conn_ev_qfyr_t llc_rst_ev_qfyrs_3[] = { + llc_conn_ev_qlfy_s_flag_eq_1, + llc_conn_ev_qlfy_set_status_rst_done, + NULL +}; + +static llc_conn_action_t llc_rst_actions_3[] = { + llc_conn_ac_set_p_flag_0, + llc_conn_ac_set_remote_busy_0, + NULL +}; + +static struct llc_conn_state_trans llc_rst_state_trans_3 = { + llc_conn_ev_ack_tmr_exp, + LLC_CONN_STATE_NORMAL, + llc_rst_ev_qfyrs_3, + llc_rst_actions_3 +}; + +/* State transitions for LLC_CONN_EV_RX_DISC_CMD_Pbit_SET_X event, + * cause_flag = 1 */ +static llc_conn_ev_qfyr_t llc_rst_ev_qfyrs_4[] = { + llc_conn_ev_qlfy_cause_flag_eq_1, + llc_conn_ev_qlfy_set_status_disc, + NULL +}; +static llc_conn_action_t llc_rst_actions_4[] = { + llc_conn_ac_send_dm_rsp_f_set_p, + llc_conn_ac_disc_ind, + llc_conn_ac_stop_ack_timer, + llc_conn_disc, + NULL +}; + +static struct llc_conn_state_trans llc_rst_state_trans_4 = { + llc_conn_ev_rx_disc_cmd_pbit_set_x, + LLC_CONN_STATE_ADM, + llc_rst_ev_qfyrs_4, + llc_rst_actions_4 +}; + +/* State transitions for LLC_CONN_EV_RX_DISC_CMD_Pbit_SET_X event, + * cause_flag = 0 */ +static llc_conn_ev_qfyr_t llc_rst_ev_qfyrs_4_1[] = { + llc_conn_ev_qlfy_cause_flag_eq_0, + llc_conn_ev_qlfy_set_status_refuse, + NULL +}; + +static llc_conn_action_t llc_rst_actions_4_1[] = { + llc_conn_ac_send_dm_rsp_f_set_p, + llc_conn_ac_stop_ack_timer, + llc_conn_disc, + NULL +}; + +static struct llc_conn_state_trans llc_rst_state_trans_4_1 = { + llc_conn_ev_rx_disc_cmd_pbit_set_x, + LLC_CONN_STATE_ADM, + llc_rst_ev_qfyrs_4_1, + llc_rst_actions_4_1 +}; + +/* State transitions for LLC_CONN_EV_RX_DM_RSP_Fbit_SET_X event, + * cause_flag = 1 */ +static llc_conn_ev_qfyr_t llc_rst_ev_qfyrs_5[] = { + llc_conn_ev_qlfy_cause_flag_eq_1, + llc_conn_ev_qlfy_set_status_disc, + NULL +}; + +static llc_conn_action_t llc_rst_actions_5[] = { + llc_conn_ac_disc_ind, + llc_conn_ac_stop_ack_timer, + llc_conn_disc, + NULL +}; + +static struct llc_conn_state_trans llc_rst_state_trans_5 = { + llc_conn_ev_rx_dm_rsp_fbit_set_x, + LLC_CONN_STATE_ADM, + llc_rst_ev_qfyrs_5, + llc_rst_actions_5 +}; + +/* State transitions for LLC_CONN_EV_RX_DM_RSP_Fbit_SET_X event, + * cause_flag = 0 */ +static llc_conn_ev_qfyr_t llc_rst_ev_qfyrs_5_1[] = { + llc_conn_ev_qlfy_cause_flag_eq_0, + llc_conn_ev_qlfy_set_status_refuse, + NULL +}; + +static llc_conn_action_t llc_rst_actions_5_1[] = { + llc_conn_ac_stop_ack_timer, + llc_conn_disc, + NULL +}; + +static struct llc_conn_state_trans llc_rst_state_trans_5_1 = { + llc_conn_ev_rx_dm_rsp_fbit_set_x, + LLC_CONN_STATE_ADM, + llc_rst_ev_qfyrs_5_1, + llc_rst_actions_5_1 +}; + +/* State transitions for DATA_CONN_REQ event */ +static llc_conn_ev_qfyr_t llc_rst_ev_qfyrs_6[] = { + llc_conn_ev_qlfy_set_status_refuse, + NULL +}; + +static llc_conn_action_t llc_rst_actions_6[] = { + NULL +}; + +static struct llc_conn_state_trans llc_rst_state_trans_6 = { + llc_conn_ev_data_req, + LLC_CONN_STATE_RESET, + llc_rst_ev_qfyrs_6, + llc_rst_actions_6 +}; + +/* State transitions for LLC_CONN_EV_ACK_TMR_EXP event */ +static llc_conn_ev_qfyr_t llc_rst_ev_qfyrs_7[] = { + llc_conn_ev_qlfy_retry_cnt_lt_n2, + llc_conn_ev_qlfy_s_flag_eq_0, + NULL +}; + +static llc_conn_action_t llc_rst_actions_7[] = { + llc_conn_ac_send_sabme_cmd_p_set_x, + llc_conn_ac_start_ack_timer, + llc_conn_ac_inc_retry_cnt_by_1, + NULL +}; + +static struct llc_conn_state_trans llc_rst_state_trans_7 = { + llc_conn_ev_ack_tmr_exp, + LLC_CONN_STATE_RESET, + llc_rst_ev_qfyrs_7, + llc_rst_actions_7 +}; + +/* State transitions for LLC_CONN_EV_ACK_TMR_EXP event */ +static llc_conn_ev_qfyr_t llc_rst_ev_qfyrs_8[] = { + llc_conn_ev_qlfy_retry_cnt_gte_n2, + llc_conn_ev_qlfy_s_flag_eq_0, + llc_conn_ev_qlfy_cause_flag_eq_1, + llc_conn_ev_qlfy_set_status_failed, + NULL +}; +static llc_conn_action_t llc_rst_actions_8[] = { + llc_conn_ac_disc_ind, + llc_conn_disc, + NULL +}; + +static struct llc_conn_state_trans llc_rst_state_trans_8 = { + llc_conn_ev_ack_tmr_exp, + LLC_CONN_STATE_ADM, + llc_rst_ev_qfyrs_8, + llc_rst_actions_8 +}; + +/* State transitions for LLC_CONN_EV_ACK_TMR_EXP event */ +static llc_conn_ev_qfyr_t llc_rst_ev_qfyrs_8_1[] = { + llc_conn_ev_qlfy_retry_cnt_gte_n2, + llc_conn_ev_qlfy_s_flag_eq_0, + llc_conn_ev_qlfy_cause_flag_eq_0, + llc_conn_ev_qlfy_set_status_failed, + NULL +}; +static llc_conn_action_t llc_rst_actions_8_1[] = { + llc_conn_ac_disc_ind, + llc_conn_disc, + NULL +}; + +static struct llc_conn_state_trans llc_rst_state_trans_8_1 = { + llc_conn_ev_ack_tmr_exp, + LLC_CONN_STATE_ADM, + llc_rst_ev_qfyrs_8_1, + llc_rst_actions_8_1 +}; + +/* + * Array of pointers; + * one to each transition + */ +static struct llc_conn_state_trans *llc_rst_state_transitions[] = { + &llc_rst_state_trans_6, // Request + &llc_common_state_trans_n, + &llc_common_state_trans_n, // Local busy + &llc_common_state_trans_n, // Initiate PF cycle + &llc_rst_state_trans_3, // Timer + &llc_rst_state_trans_7, + &llc_rst_state_trans_8, + &llc_rst_state_trans_8_1, + &llc_common_state_trans_n, + &llc_rst_state_trans_1, // Receive frame + &llc_rst_state_trans_2, + &llc_rst_state_trans_2_1, + &llc_rst_state_trans_4, + &llc_rst_state_trans_4_1, + &llc_rst_state_trans_5, + &llc_rst_state_trans_5_1, + &llc_common_state_trans_n +}; + +/* -------------------- LLC_CONN_STATE_ERROR transitions ------------------- */ +/* State transitions for LLC_CONN_EV_RX_SABME_CMD_Pbit_SET_X event */ +static llc_conn_action_t llc_error_actions_1[] = { + llc_conn_ac_set_vs_0, + llc_conn_ac_set_vr_0, + llc_conn_ac_send_ua_rsp_f_set_p, + llc_conn_ac_rst_ind, + llc_conn_ac_set_p_flag_0, + llc_conn_ac_set_remote_busy_0, + llc_conn_ac_stop_ack_timer, + llc_conn_reset, + NULL +}; + +static struct llc_conn_state_trans llc_error_state_trans_1 = { + llc_conn_ev_rx_sabme_cmd_pbit_set_x, + LLC_CONN_STATE_NORMAL, + LLC_NO_EVENT_QUALIFIERS, + llc_error_actions_1 +}; + +/* State transitions for LLC_CONN_EV_RX_DISC_CMD_Pbit_SET_X event */ +static llc_conn_action_t llc_error_actions_2[] = { + llc_conn_ac_send_ua_rsp_f_set_p, + llc_conn_ac_disc_ind, + llc_conn_ac_stop_ack_timer, + llc_conn_disc, + NULL +}; + +static struct llc_conn_state_trans llc_error_state_trans_2 = { + llc_conn_ev_rx_disc_cmd_pbit_set_x, + LLC_CONN_STATE_ADM, + LLC_NO_EVENT_QUALIFIERS, + llc_error_actions_2 +}; + +/* State transitions for LLC_CONN_EV_RX_DM_RSP_Fbit_SET_X event */ +static llc_conn_action_t llc_error_actions_3[] = { + llc_conn_ac_disc_ind, + llc_conn_ac_stop_ack_timer, + llc_conn_disc, + NULL +}; + +static struct llc_conn_state_trans llc_error_state_trans_3 = { + llc_conn_ev_rx_dm_rsp_fbit_set_x, + LLC_CONN_STATE_ADM, + LLC_NO_EVENT_QUALIFIERS, + llc_error_actions_3 +}; + +/* State transitions for LLC_CONN_EV_RX_FRMR_RSP_Fbit_SET_X event */ +static llc_conn_action_t llc_error_actions_4[] = { + llc_conn_ac_send_sabme_cmd_p_set_x, + llc_conn_ac_start_ack_timer, + llc_conn_ac_set_retry_cnt_0, + llc_conn_ac_set_cause_flag_0, + NULL +}; + +static struct llc_conn_state_trans llc_error_state_trans_4 = { + llc_conn_ev_rx_frmr_rsp_fbit_set_x, + LLC_CONN_STATE_RESET, + LLC_NO_EVENT_QUALIFIERS, + llc_error_actions_4 +}; + +/* State transitions for LLC_CONN_EV_RX_XXX_CMD_Pbit_SET_X event */ +static llc_conn_action_t llc_error_actions_5[] = { + llc_conn_ac_resend_frmr_rsp_f_set_p, + NULL +}; + +static struct llc_conn_state_trans llc_error_state_trans_5 = { + llc_conn_ev_rx_xxx_cmd_pbit_set_x, + LLC_CONN_STATE_ERROR, + LLC_NO_EVENT_QUALIFIERS, + llc_error_actions_5 +}; + +/* State transitions for LLC_CONN_EV_RX_XXX_RSP_Fbit_SET_X event */ +static struct llc_conn_state_trans llc_error_state_trans_6 = { + llc_conn_ev_rx_xxx_rsp_fbit_set_x, + LLC_CONN_STATE_ERROR, + LLC_NO_EVENT_QUALIFIERS, + LLC_NO_TRANSITION_ACTIONS +}; + +/* State transitions for LLC_CONN_EV_ACK_TMR_EXP event */ +static llc_conn_ev_qfyr_t llc_error_ev_qfyrs_7[] = { + llc_conn_ev_qlfy_retry_cnt_lt_n2, + NULL +}; + +static llc_conn_action_t llc_error_actions_7[] = { + llc_conn_ac_resend_frmr_rsp_f_set_0, + llc_conn_ac_start_ack_timer, + llc_conn_ac_inc_retry_cnt_by_1, + NULL +}; + +static struct llc_conn_state_trans llc_error_state_trans_7 = { + llc_conn_ev_ack_tmr_exp, + LLC_CONN_STATE_ERROR, + llc_error_ev_qfyrs_7, + llc_error_actions_7 +}; + +/* State transitions for LLC_CONN_EV_ACK_TMR_EXP event */ +static llc_conn_ev_qfyr_t llc_error_ev_qfyrs_8[] = { + llc_conn_ev_qlfy_retry_cnt_gte_n2, + NULL +}; + +static llc_conn_action_t llc_error_actions_8[] = { + llc_conn_ac_send_sabme_cmd_p_set_x, + llc_conn_ac_set_s_flag_0, + llc_conn_ac_start_ack_timer, + llc_conn_ac_set_retry_cnt_0, + llc_conn_ac_set_cause_flag_0, + NULL +}; + +static struct llc_conn_state_trans llc_error_state_trans_8 = { + llc_conn_ev_ack_tmr_exp, + LLC_CONN_STATE_RESET, + llc_error_ev_qfyrs_8, + llc_error_actions_8 +}; + +/* State transitions for LLC_CONN_EV_DATA_CONN_REQ event */ +static llc_conn_ev_qfyr_t llc_error_ev_qfyrs_9[] = { + llc_conn_ev_qlfy_set_status_refuse, + NULL +}; + +static llc_conn_action_t llc_error_actions_9[] = { + NULL +}; + +static struct llc_conn_state_trans llc_error_state_trans_9 = { + llc_conn_ev_data_req, + LLC_CONN_STATE_ERROR, + llc_error_ev_qfyrs_9, + llc_error_actions_9 +}; + +/* + * Array of pointers; + * one to each transition + */ +static struct llc_conn_state_trans *llc_error_state_transitions[] = { + &llc_error_state_trans_9, // Request + &llc_common_state_trans_n, + &llc_common_state_trans_n, // Local busy + &llc_common_state_trans_n, // Initiate PF cycle + &llc_error_state_trans_7, // Timer + &llc_error_state_trans_8, + &llc_common_state_trans_n, + &llc_error_state_trans_1, // Receive frame + &llc_error_state_trans_2, + &llc_error_state_trans_3, + &llc_error_state_trans_4, + &llc_error_state_trans_5, + &llc_error_state_trans_6, + &llc_common_state_trans_n +}; + +/* ------------------- LLC_CONN_STATE_TEMP transitions ----------------- */ +/* State transitions for LLC_CONN_EV_DISC_REQ event */ +static llc_conn_action_t llc_temp_actions_1[] = { + llc_conn_ac_stop_all_timers, + llc_conn_ac_send_disc_cmd_p_set_x, + llc_conn_disc, + NULL +}; + +static struct llc_conn_state_trans llc_temp_state_trans_1 = { + llc_conn_ev_disc_req, + LLC_CONN_STATE_ADM, + LLC_NO_EVENT_QUALIFIERS, + llc_temp_actions_1 +}; + +/* + * Array of pointers; + * one to each transition + */ +static struct llc_conn_state_trans *llc_temp_state_transitions[] = { + &llc_temp_state_trans_1, /* requests */ + &llc_common_state_trans_n, + &llc_common_state_trans_n, /* local busy */ + &llc_common_state_trans_n, /* init_pf_cycle */ + &llc_common_state_trans_n, /* timer */ + &llc_common_state_trans_n /* recieve */ +}; + +/* Connection State Transition Table */ +struct llc_conn_state llc_conn_state_table[] = { + { LLC_CONN_STATE_ADM, llc_adm_state_transitions }, + { LLC_CONN_STATE_SETUP, llc_setup_state_transitions }, + { LLC_CONN_STATE_NORMAL, llc_normal_state_transitions }, + { LLC_CONN_STATE_BUSY, llc_busy_state_transitions }, + { LLC_CONN_STATE_REJECT, llc_reject_state_transitions }, + { LLC_CONN_STATE_AWAIT, llc_await_state_transitions }, + { LLC_CONN_STATE_AWAIT_BUSY, llc_await_busy_state_transitions }, + { LLC_CONN_STATE_AWAIT_REJECT, llc_await_rejct_state_transitions }, + { LLC_CONN_STATE_D_CONN, llc_d_conn_state_transitions }, + { LLC_CONN_STATE_RESET, llc_rst_state_transitions }, + { LLC_CONN_STATE_ERROR, llc_error_state_transitions }, + { LLC_CONN_STATE_TEMP, llc_temp_state_transitions } +}; Index: kernel-acme/net/llc/llc_conn.c diff -u /dev/null kernel-acme/net/llc/llc_conn.c:1.1.2.7 --- /dev/null Thu Nov 22 23:54:58 2001 +++ kernel-acme/net/llc/llc_conn.c Wed Nov 21 19:42:23 2001 @@ -0,0 +1,519 @@ +/* + * llc_conn.c - Driver routines for connection component. + * + * Copyright (c) 1997 by Procom Technology, Inc. + * 2001 by Arnaldo Carvalho de Melo + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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; + 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 = (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; + 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 = (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; + 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 = (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) { + pdu_sn_t *pdu = (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 */ + pdu_sn_t *pdu = (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++); + 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_conn - Finds connection in sap for the remote/local sap/mac + * @sap: SAP + * @remote_addr: address of remote LLC (MAC + SAP) + * @local_addr: 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_conn(struct llc_sap *sap, struct llc_addr *remote_addr, + struct llc_addr *local_addr) +{ + 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 (!memcmp(llc->laddr.mac, local_addr->mac, MAC_ADDR_LEN) && + !memcmp(llc->daddr.mac, remote_addr->mac, MAC_ADDR_LEN)) { + rc = sock_list_entry(entry, struct llc_opt, node); + 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_REJECT) + 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; +} Index: kernel-acme/net/llc/llc_evnt.c diff -u /dev/null kernel-acme/net/llc/llc_evnt.c:1.1.2.4 --- /dev/null Thu Nov 22 23:54:58 2001 +++ kernel-acme/net/llc/llc_evnt.c Sun Nov 11 15:29:45 2001 @@ -0,0 +1,111 @@ +/* + * 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 + * + * 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 +#include +#include +#include +#include + +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) +{ + pdu_un_t *pdu = (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) +{ + pdu_un_t *pdu = (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) +{ + pdu_un_t *pdu = (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) +{ + pdu_un_t *pdu = (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; +} Index: kernel-acme/net/llc/llc_if.c diff -u /dev/null kernel-acme/net/llc/llc_if.c:1.1.2.8 --- /dev/null Thu Nov 22 23:54:58 2001 +++ kernel-acme/net/llc/llc_if.c Thu Nov 22 23:49:28 2001 @@ -0,0 +1,487 @@ +/* + * llc_if.c - Defines LLC interface to upper layer + * + * Copyright (c) 1997 by Procom Technology, Inc. + * 2001 by Arnaldo Carvalho de Melo + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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_unitdata_req_handler, /* order of functions must not change */ + llc_conn_req_handler, + llc_data_req_handler, + llc_disc_req_handler, + llc_rst_req_handler, + llc_flowcontrol_req_handler, + NULL, + llc_xid_req_handler, + llc_test_req_handler, +}; + +/* table of response handler functions */ +static llc_prim_call_t llc_resp_prim[LLC_NBR_PRIMITIVES] = { + llc_no_rsp_handler, /* order of functions must not change */ + llc_conn_rsp_handler, + llc_no_rsp_handler, + llc_no_rsp_handler, + llc_rst_rsp_handler, + 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. + * @local_sap: 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 local_sap) +{ + /* verify this SAP is not already open; if so, return error */ + struct llc_sap *sap; + + MOD_INC_USE_COUNT; + sap = llc_sap_find(local_sap); + 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 = local_sap; + 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(local_sap, 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 = 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 = 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 = 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); + + 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; + rc = llc_conn_send_ev(sk, ev); + } +out: 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; + /* network layer supplies addressing required to establish connection; + * package as an event and send it to the connection event handler */ + struct sock *sk = llc_find_conn(sap, &prim->data->conn.daddr, + &prim->data->conn.saddr); + if (sk) { + confirm_impossible(prim); + goto out; + } + if (prim->data->conn.sk) { + sk = prim->data->conn.sk; + llc_sock_init(sk); + } else { + sk = llc_sock_alloc(); + rc = -ENOMEM; + if (!sk) { + confirm_impossible(prim); + goto out; + } + prim->data->conn.sk = sk; + } + sock_hold(sk); + /* assign new connection to it's SAP */ + llc_sap_assign_sock(sap, sk); + llc = LLC_SK(sk); + memcpy(&llc->daddr, &prim->data->conn.daddr, sizeof(llc->daddr)); + memcpy(&llc->laddr, &prim->data->conn.saddr, sizeof(llc->laddr)); + llc->dev = prim->data->conn.dev; + llc->link = prim->data->conn.link; + ev = llc_conn_alloc_ev(sk); + rc = -ENOMEM; + 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); + } +out: sock_put(sk); + 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; + + 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: 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 = 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); + } + 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); Index: kernel-acme/net/llc/llc_mac.c diff -u /dev/null kernel-acme/net/llc/llc_mac.c:1.1.2.4 --- /dev/null Thu Nov 22 23:54:59 2001 +++ kernel-acme/net/llc/llc_mac.c Sat Nov 17 16:19:36 2001 @@ -0,0 +1,277 @@ +/* + * llc_mac.c - Manages interface between LLC and MAC + * + * Copyright (c) 1997 by Procom Technology, Inc. + * 2001 by Arnaldo Carvalho de Melo + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* 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. + */ +u16 mac_send_pdu(struct sk_buff *skb) +{ + struct sk_buff *skb2; + int pri = GFP_ATOMIC; + + if (!skb->dev) { + printk(KERN_ERR __FUNCTION__ ": skb->dev == NULL!"); + return -1; + } + if (skb->sk) + pri = (int)skb->sk->priority; + skb2 = skb_clone(skb, pri); + if (!skb2) + return -1; + dev_queue_xmit(skb2); + return 0; +} + +/** + * 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; + 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) + goto drop; + skb = skb_share_check(skb, GFP_ATOMIC); + if (!skb) + goto out; + fix_up_incoming_skb(skb); + pdu = (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_conn(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: with the current code one cannot call + * llc_pdu_router with the socket lock held, cause + * it'll route the pdu to the upper layers and it can + * reenter 802.2 and in Request_primitives will try to + * grab the same lock, maybe we should use + * spin_trylock_bh in the Request_primitives + * (data_req_handler, etc) and add the request to the + * backlog, well see... */ + bh_unlock_sock(sk); + llc_pdu_router(LLC_SK(sk)->sap, sk, skb, LLC_TYPE_2); + } 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; + pdu_sn_t *pdu = (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 = ((ieee_802_3_mac_hdr_t *)skb->mac.raw)->lpdu_len, + 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) +{ + pdu_sn_t *pdu = (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); + + if (!LLC_SK(sk)->dev) + LLC_SK(sk)->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; + u16 lpdu_len; + + switch (skb->dev->type) { +#ifdef CONFIG_TR + case ARPHRD_IEEE802: { + token_ring_mac_hdr_t *trh = (token_ring_mac_hdr_t *) + skb_push(skb, sizeof(*trh)); + struct net_device *dev = skb->dev; + + trh->ac = AC; + trh->fc = LLC_FRAME; + if (sa) + memcpy(trh->sa, sa, dev->addr_len); + else + memset(trh->sa, 0, dev->addr_len); + if (da) { + memcpy(trh->da, da, dev->addr_len); + tr_source_route((struct trh_hdr *)trh, dev); + } + skb->mac.raw = skb->data; + break; + } +#endif + case ARPHRD_ETHER : + case ARPHRD_LOOPBACK : + lpdu_len = skb->len; + skb->mac.raw = skb_push(skb, + sizeof(ieee_802_3_mac_hdr_t)); + memset(skb->mac.raw, 0, sizeof(ieee_802_3_mac_hdr_t)); + ((ieee_802_3_mac_hdr_t *)skb->mac.raw)->lpdu_len = + htons(lpdu_len); + daddr = ((ieee_802_3_mac_hdr_t *)skb->mac.raw)->da; + saddr = ((ieee_802_3_mac_hdr_t *)skb->mac.raw)->sa; + memcpy(daddr, da, MAC_ADDR_LEN); + memcpy(saddr, sa, MAC_ADDR_LEN); + break; + default: + printk(KERN_WARNING "Unknown DEVICE type : %d\n", + skb->dev->type); + rc = 1; + } + return rc; +} Index: kernel-acme/net/llc/llc_main.c diff -u /dev/null kernel-acme/net/llc/llc_main.c:1.1.2.5 --- /dev/null Thu Nov 22 23:54:59 2001 +++ kernel-acme/net/llc/llc_main.c Sat Nov 17 16:19:36 2001 @@ -0,0 +1,607 @@ +/* + * 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 + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* 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 *, + struct net_device *, + struct packet_type *)); +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, MAC_ADDR_LEN); + 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. + */ + +void llc_sock_init(struct sock* sk) +{ + struct llc_opt *llc = 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 window size, will adjust dynam */ + llc->rw = 128; /* rx window size (optional and equal to tx_window + of remote LLC) */ + skb_queue_head_init(&llc->pdu_unack_q); + sk->backlog_rcv = llc_backlog_rcv; +} + +/** + * __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); + + if (!sk) + goto out; + sock_init_data(NULL, sk); + llc_sock_init(sk); +out: return sk; +} + +/** + * __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 */ + 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 sock *sk = sock_list_entry(entry, struct llc_opt, node); + + prim.sap = sap; + prim_data.disc.sk = sk; + prim.prim = LLC_DISC_PRIM; + prim.data = &prim_data; + LLC_SK(sk)->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) +{ + u16 i; + struct llc_station_state_trans **next_trans; + struct llc_station_state *curr_state = + &llc_station_state_table[station->state - 1]; + + for (i = 0, next_trans = curr_state->transitions; + next_trans[i]->ev; i++) + if (!next_trans[i]->ev(station, ev)) + return next_trans[i]; + return NULL; +} + +/** + * 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 retries\n"); + list_for_each(llc_entry, &sap->sk_list.list) { + llc = list_entry(llc_entry, struct llc_opt, node); + len += sprintf(bf + len, " %-5d%-8d\n", + llc->state, llc->retry_count); + } +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; +} + +const char 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"; +const char err_llc[] __initdata = KERN_ERR "LLC installation NOT successful.\n"; + +static int __init llc_init(void) +{ + u16 rc = 0; + struct llc_station_state_ev *ev; + + printk(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)); + memcpy(llc_main_station.mac_sa, dev_base->next->dev_addr, MAC_ADDR_LEN); + 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); +#ifdef CONFIG_LLC_UI + llc_ui_init(); +#endif +out: return rc; +err: printk(err_llc); + rc = 1; + goto out; +} + +static void __exit llc_exit(void) +{ +#ifdef CONFIG_LLC_UI + llc_ui_exit(); +#endif + llc_unregister_sap(0); + proc_net_remove("802.2"); +} + +module_init(llc_init); +module_exit(llc_exit); Index: kernel-acme/net/llc/llc_pdu.c diff -u /dev/null kernel-acme/net/llc/llc_pdu.c:1.1.2.5 --- /dev/null Thu Nov 22 23:54:59 2001 +++ kernel-acme/net/llc/llc_pdu.c Thu Nov 22 22:16:04 2001 @@ -0,0 +1,655 @@ +/* + * llc_pdu.c - access to PDU internals + * + * Copyright (c) 1997 by Procom Technology, Inc. + * 2001 by Arnaldo Carvalho de Melo + * + * 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 +#include +#include +#include +#include + +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(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) +{ + pdu_un_t *p; + + skb->nh.raw = skb_push(skb, llc_get_llc_hdr_length(pdu_type)); + p = (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) +{ + ((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: + ((pdu_sn_t *)skb->nh.raw)->ctrl_2 = + (((pdu_sn_t *)skb->nh.raw)->ctrl_2 & 0xFE) | + bit_value; + break; + case LLC_PDU_TYPE_U: + ((pdu_un_t *)skb->nh.raw)->ctrl_1 |= + (((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 = ((pdu_sn_t *)skb->nh.raw)->ctrl_2 & + LLC_S_PF_BIT_MASK; + break; + case LLC_PDU_TYPE_U: + *pf_bit = (((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 = ((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, ((ieee_802_3_mac_hdr_t *)skb->mac.raw)->sa, + MAC_ADDR_LEN); + else if (skb->protocol == ntohs(ETH_P_TR_802_2)) + memcpy(sa, ((token_ring_mac_hdr_t *)skb->mac.raw)->sa, + MAC_ADDR_LEN); + 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, ((ieee_802_3_mac_hdr_t *)skb->mac.raw)->da, + MAC_ADDR_LEN); + else if (skb->protocol == ntohs(ETH_P_TR_802_2)) + memcpy(da, ((token_ring_mac_hdr_t *)skb->mac.raw)->da, + MAC_ADDR_LEN); + 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 = ((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 = ((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) +{ + pdu_un_t *pdu = (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) +{ + xid_info_t *xid_info; + pdu_un_t *pdu = (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 = (xid_info_t *)(((u8 *)&pdu->ctrl_1) + 1); + xid_info->fmt_id = 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) +{ + pdu_un_t *pdu = (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) +{ + pdu_un_t *pdu = (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) +{ + pdu_sn_t *pdu = (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) +{ + pdu_sn_t *pdu = (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) +{ + pdu_sn_t *pdu = (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) +{ + pdu_sn_t *pdu = (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) +{ + pdu_un_t *pdu = (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) +{ + pdu_un_t *pdu = (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) +{ + xid_info_t *xid_info; + pdu_un_t *pdu = (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 = (xid_info_t *)(((u8 *)&pdu->ctrl_1) + 1); + xid_info->fmt_id = 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 data_size = 0; + pdu_un_t *pdu = (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)) { + data_size = ntohs(((ieee_802_3_mac_hdr_t *) + ev_skb->mac.raw)->lpdu_len) - 3; + memcpy(((u8 *)skb->nh.raw) + 3, + ((u8 *)ev_skb->nh.raw) + 3, data_size); + skb_put(skb, data_size); + } + 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, pdu_sn_t *prev_pdu, u8 f_bit, + u8 vs, u8 vr, u8 vzyxw) +{ + frmr_info_t *frmr_info; + u8 prev_pf = 0; + u8 *ctrl; + pdu_sn_t *pdu = (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 = (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) +{ + pdu_sn_t *pdu = (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) +{ + pdu_sn_t *pdu = (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) +{ + pdu_sn_t *pdu = (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) +{ + pdu_un_t *pdu = (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) +{ + pdu_un_t *pdu = (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 */ + pdu_sn_t *pdu = (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(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; +} Index: kernel-acme/net/llc/llc_s_ac.c diff -u /dev/null kernel-acme/net/llc/llc_s_ac.c:1.1.2.4 --- /dev/null Thu Nov 22 23:54:59 2001 +++ kernel-acme/net/llc/llc_s_ac.c Wed Nov 21 00:19:21 2001 @@ -0,0 +1,222 @@ +/* + * 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 + * + * 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 +#include +#include +#include +#include +#include +#include + +/** + * 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, 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[MAC_ADDR_LEN], mac_sa[MAC_ADDR_LEN], 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, 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[MAC_ADDR_LEN], mac_sa[MAC_ADDR_LEN], 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; +} Index: kernel-acme/net/llc/llc_s_ev.c diff -u /dev/null kernel-acme/net/llc/llc_s_ev.c:1.1.2.3 --- /dev/null Thu Nov 22 23:54:59 2001 +++ kernel-acme/net/llc/llc_s_ev.c Sun Nov 11 15:29:45 2001 @@ -0,0 +1,100 @@ +/* + * 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 + * + * 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 +#include +#include +#include + +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) +{ + pdu_un_t *pdu = (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) +{ + pdu_un_t *pdu = (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) +{ + pdu_un_t *pdu = (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) +{ + pdu_un_t *pdu = (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) +{ + pdu_un_t *pdu = (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; +} Index: kernel-acme/net/llc/llc_s_st.c diff -u /dev/null kernel-acme/net/llc/llc_s_st.c:1.1.2.5 --- /dev/null Thu Nov 22 23:54:59 2001 +++ kernel-acme/net/llc/llc_s_st.c Tue Nov 13 23:28:00 2001 @@ -0,0 +1,163 @@ +/* + * 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 + * + * 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 +#include +#include +#include +#include + +/* dummy last-transition indicator; common to all state transition groups */ +static struct llc_sap_state_trans llc_sap_state_trans_n = { + NULL, 0, NULL /* last entry for this state */ +}; + +/* state LLC_SAP_STATE_INACTIVE transition for LLC_SAP_EV_ACTIVATION_REQ event */ +static llc_sap_action_t llc_sap_inactive_state_actions_1[] = { + llc_sap_action_report_status, + NULL +}; + +static struct llc_sap_state_trans llc_sap_inactive_state_trans_1 = { + llc_sap_ev_activation_req, LLC_SAP_STATE_ACTIVE, + llc_sap_inactive_state_actions_1 +}; + +/* array of pointers; one to each transition */ +static struct llc_sap_state_trans *llc_sap_inactive_state_transitions[] = { + &llc_sap_inactive_state_trans_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[] = { + llc_sap_action_unitdata_ind, + NULL +}; + +static struct llc_sap_state_trans llc_sap_active_state_trans_1 = { + llc_sap_ev_rx_ui, LLC_SAP_STATE_ACTIVE, + 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[] = { + llc_sap_action_send_ui, + NULL +}; + +static struct llc_sap_state_trans llc_sap_active_state_trans_2 = { + llc_sap_ev_unitdata_req, LLC_SAP_STATE_ACTIVE, + 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[] = { + llc_sap_action_send_xid_c, + NULL +}; + +static struct llc_sap_state_trans llc_sap_active_state_trans_3 = { + llc_sap_ev_xid_req, LLC_SAP_STATE_ACTIVE, + 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[] = { + llc_sap_action_send_xid_r, + NULL +}; + +static struct llc_sap_state_trans llc_sap_active_state_trans_4 = { + llc_sap_ev_rx_xid_c, LLC_SAP_STATE_ACTIVE, + 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[] = { + llc_sap_action_xid_ind, + NULL +}; + +static struct llc_sap_state_trans llc_sap_active_state_trans_5 = { + llc_sap_ev_rx_xid_r, LLC_SAP_STATE_ACTIVE, + 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[] = { + llc_sap_action_send_test_c, + NULL +}; + +static struct llc_sap_state_trans llc_sap_active_state_trans_6 = { + llc_sap_ev_test_req, LLC_SAP_STATE_ACTIVE, + 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[] = { + llc_sap_action_send_test_r, + NULL +}; + +static struct llc_sap_state_trans llc_sap_active_state_trans_7 = { + llc_sap_ev_rx_test_c, LLC_SAP_STATE_ACTIVE, + 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[] = { + llc_sap_action_test_ind, + NULL +}; + +static struct llc_sap_state_trans llc_sap_active_state_trans_8 = { + llc_sap_ev_rx_test_r, LLC_SAP_STATE_ACTIVE, + 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[] = { + llc_sap_action_report_status, + NULL +}; + +static struct llc_sap_state_trans llc_sap_active_state_trans_9 = { + llc_sap_ev_deactivation_req, LLC_SAP_STATE_INACTIVE, + llc_sap_active_state_actions_9 +}; + +/* array of pointers; one to each transition */ +static struct llc_sap_state_trans *llc_sap_active_state_transitions[] = { + &llc_sap_active_state_trans_2, + &llc_sap_active_state_trans_1, + &llc_sap_active_state_trans_3, + &llc_sap_active_state_trans_4, + &llc_sap_active_state_trans_5, + &llc_sap_active_state_trans_6, + &llc_sap_active_state_trans_7, + &llc_sap_active_state_trans_8, + &llc_sap_active_state_trans_9, + &llc_sap_state_trans_n +}; + +/* SAP state transition table */ +struct llc_sap_state llc_sap_state_table[] = { + { LLC_SAP_STATE_INACTIVE, llc_sap_inactive_state_transitions }, + { LLC_SAP_STATE_ACTIVE, llc_sap_active_state_transitions } +}; Index: kernel-acme/net/llc/llc_sap.c diff -u /dev/null kernel-acme/net/llc/llc_sap.c:1.1.2.3 --- /dev/null Thu Nov 22 23:54:59 2001 +++ kernel-acme/net/llc/llc_sap.c Sun Nov 11 15:29:45 2001 @@ -0,0 +1,254 @@ +/* + * llc_sap.c - driver routines for SAP component. + * + * Copyright (c) 1997 by Procom Technology, Inc. + * 2001 by Arnaldo Carvalho de Melo + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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); + 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) +{ + list_del(&LLC_SK(sk)->node); +} + +/** + * 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). + */ +int 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); + return 0; +} + +/** + * 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) +{ + 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 = (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 (((token_ring_mac_hdr_t *) + skb->mac.raw)->rcf) { + lfb = ntohs(((token_ring_mac_hdr_t *) + 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) { + pdu_un_t *pdu = (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; + 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 (i = 0, next_trans = curr_state->transitions; + next_trans [i]->ev; i++) + if (!next_trans[i]->ev(sap, ev)) + /* got event match; return it */ + return next_trans[i]; + return NULL; +} + +/** + * 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; +} Index: kernel-acme/net/llc/llc_sock.c diff -u /dev/null kernel-acme/net/llc/llc_sock.c:1.1.2.10 --- /dev/null Thu Nov 22 23:54:59 2001 +++ kernel-acme/net/llc/llc_sock.c Thu Nov 22 23:49:28 2001 @@ -0,0 +1,1743 @@ +/* + * 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 + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static u16 llc_ui_sap_last_autoport; +static u16 llc_ui_sap_link_no_max[256]; +static u8 llc_ui_addrany[IFHWADDRLEN]; /* zeroed because its in .bss */ +static struct sock *llc_ui_sockets; +static struct proto_ops llc_ui_ops; +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 upto len, 1 if a complete match upto 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_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) + rc = htons(ETH_P_TR_802_2); + return rc; +} + +/** + * llc_ui_header_len - return lenght 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) +{ + 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.pri = 0; + prim_data.conn.saddr.lsap = LLC_UI_SK(sk)->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 = 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->sap; + prim.prim = LLC_DISC_PRIM; + prim_data.disc.sk = llc->core_sk; + prim_data.disc.link = llc->link; + rc = llc->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 sock* core_sk = LLC_UI_SK(sk)->core_sk; + + 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 = core_sk; + skb->protocol = llc_ui_protocol_type(addr->sllc_arphrd); + wait_event(sk->socket->wait, !LLC_SK(core_sk)->failed_data_req); + return sap->req(&prim); +} + +/** + * 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 = LLC_UI_SK(sk); + + if (!llc->sap) + continue; + if (llc->sap->laddr.lsap == sap) { + s = llc->sap; + break; + } + } + read_unlock_bh(&llc_ui_sockets_lock); + return s; +} + +/** + * llc_ui_find_sk_by_link_no - return socket matching sap and link_no + * @sap: Sap to search for. + * @link: link number of connection to match. + * + * Search the local socket list and return the socket which has a matching + * sap and link_no. This search only works on connected sockets. + * Returns sock upon match, %NULL otherwise. + */ +static struct sock *llc_ui_find_sk_by_link_no(struct llc_sap *sap, u16 link) +{ + struct sock *sk; + + read_lock(&llc_ui_sockets_lock); + for (sk = llc_ui_sockets; sk; sk = sk->next) { + struct llc_ui_opt *llc = LLC_UI_SK(sk); + + if (llc->sap && llc->sap->laddr.lsap == sap->laddr.lsap && + llc->core_sk && /* if no core_sk, link_no is invalid */ + llc->link == link) + break; /* exact mac + sap match. */ + } + if (sk) + sock_hold(sk); + read_unlock(&llc_ui_sockets_lock); + 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 *addr, + struct net_device *dev) +{ + struct sock *sk; + + for (sk = llc_ui_sockets; sk; sk = sk->next) { + struct llc_ui_opt *llc = LLC_UI_SK(sk); + + if (!llc->sap || llc->sap->laddr.lsap != addr->lsap) + continue; + if (!llc->dev) { + if (!llc_ui_mac_null(llc->addr.sllc_mmac) && + !llc_ui_mac_match(addr->mac, llc->addr.sllc_mmac)) + continue; + break; + } + if (llc_ui_mac_match(addr->mac, llc->addr.sllc_smac) && + llc_ui_mac_null(llc->addr.sllc_mmac)) + break; + if (!llc_ui_mac_null(llc->addr.sllc_mmac) && + llc_ui_mac_match(addr->mac, llc->addr.sllc_mmac) && + llc_ui_mac_match(llc->addr.sllc_smac, dev->dev_addr)) + break; + } + return sk; +} + +static struct sock *llc_ui_find_sk_by_addr(struct llc_addr *addr, + struct net_device *dev) +{ + struct sock *sk; + read_lock(&llc_ui_sockets_lock); + sk = __llc_ui_find_sk_by_addr(addr, 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 net_device *dev) +{ + struct sock *sk; + read_lock_bh(&llc_ui_sockets_lock); + sk = __llc_ui_find_sk_by_addr(addr, 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_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; + + skb_queue_purge(&sk->receive_queue); + skb_queue_purge(&sk->write_queue); + if (!atomic_read(&sk->wmem_alloc) && + !atomic_read(&sk->rmem_alloc) && sk->dead) + MOD_DEC_USE_COUNT; + 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 proto_ops *prot; + struct sock *sk; + int rc = -ESOCKTNOSUPPORT; + + MOD_INC_USE_COUNT; + switch (sock->type) { + case SOCK_DGRAM: /* llc1 */ + case SOCK_STREAM: /* llc2 */ + prot = &llc_ui_ops; + break; + case SOCK_SEQPACKET: /* llc3, not supported */ + case SOCK_RAW: /* not supported */ + default: + goto decmod; + } + rc = -ENOMEM; + sk = sk_alloc(PF_LLC, GFP_KERNEL, 1); + if (!sk) + goto decmod; + rc = 0; + sock_init_data(sock, sk); + sock->ops = prot; +out: return rc; +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; + + if (!sk) + goto out; + llc_ui_remove_socket(sk); + llc = LLC_UI_SK(sk); + if (llc->core_sk) + llc_ui_send_disc(sk); + if (llc->sap && !llc_ui_find_sap(llc->sap->laddr.lsap)) + llc_sap_close(llc->sap); + skb_queue_purge(&sk->receive_queue); + skb_queue_purge(&sk->write_queue); + sock_orphan(sk); + sock->sk = NULL; + if (!atomic_read(&sk->wmem_alloc) && + !atomic_read(&sk->rmem_alloc) && sk->dead) + MOD_DEC_USE_COUNT; + 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_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_SK(sk)->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 local_addr; + struct sock *ask; + + rc = -EUSERS; /* can't get exclusive use of sap */ + if (!dev && llc_ui_mac_null(addr->sllc_mmac)) + goto out; + memset(&local_addr, 0, sizeof(local_addr)); + if (!llc_ui_mac_null(addr->sllc_mmac)) + memcpy(local_addr.mac, addr->sllc_mmac, IFHWADDRLEN); + else + memcpy(local_addr.mac, addr->sllc_smac, IFHWADDRLEN); + local_addr.lsap = addr->sllc_ssap; + rc = -EADDRINUSE; /* mac + sap clash. */ + ask = llc_ui_bh_find_sk_by_addr(&local_addr, dev); + if (ask) { + sock_put(ask); + goto out; + } + } + memcpy(&LLC_UI_SK(sk)->addr, addr, sizeof(*addr)); + LLC_UI_SK(sk)->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. + * @addr_len: 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 addr_len) +{ + struct sockaddr_llc *addr = (struct sockaddr_llc *)uaddr; + struct sock *sk = sock->sk; + int rc = -EINVAL; + + if (!sk->zapped || addr_len != 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. + * @addr_len: 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 addr_len, int flags) +{ + struct sock *sk = sock->sk; + struct llc_ui_opt *llc = LLC_UI_SK(sk); + struct sockaddr_llc *addr = (struct sockaddr_llc *)uaddr; + struct net_device *dev; + int rc = -EINVAL; + + lock_sock(sk); + if (addr_len != sizeof(*addr)) + goto out; + rc = -EAFNOSUPPORT; + if (addr->sllc_family != AF_LLC) + goto out; + rc = -EINVAL; + 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; + /* 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) { + sock->state = SS_UNCONNECTED; + sk->state = TCP_CLOSE; + goto out; + } + } + if (!llc->dev) { + rtnl_lock(); + dev = dev_getbyhwaddr(addr->sllc_arphrd, addr->sllc_smac); + rtnl_unlock(); + if (!dev) { + sock->state = SS_UNCONNECTED; + sk->state = TCP_CLOSE; + goto out; + } + } else + dev = llc->dev; + llc->link = llc_ui_next_link_no(llc->sap->laddr.lsap); + rc = llc_ui_send_conn(sk, llc->sap, addr, dev, llc->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 incomming 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) +{ + 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_SK(sk)->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 incomming 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 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_SK(newsk)->sap = LLC_UI_SK(sk)->sap; + LLC_UI_SK(newsk)->dev = LLC_UI_SK(sk)->dev; + LLC_UI_SK(newsk)->link = LLC_SK(skb->sk)->link; + LLC_UI_SK(newsk)->core_sk = skb->sk; + memcpy(&LLC_UI_SK(newsk)->addr, &LLC_UI_SK(sk)->addr, + sizeof(LLC_UI_SK(sk)->addr)); + /* put orignal 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 sockaddr_llc saddr; + 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; + rc = -EPROTO; + if (skb->len < sizeof(saddr)) + goto dgram_free; + /* remove saved sllc header. */ + memcpy(&saddr, skb->head, sizeof(saddr)); + skb_pull(skb, sizeof(saddr)); + 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, &saddr, 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 = 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 (sk->type == SOCK_DGRAM && msg->msg_namelen < sizeof(*addr)) + goto release; + if (sk->type == SOCK_STREAM && !addr) + addr = &llc->addr; + if (!addr) + goto release; + /* 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) { + sock->state = SS_UNCONNECTED; + sk->state = TCP_CLOSE; + goto release; + } + } + if (!llc->dev) { + rtnl_lock(); + dev = dev_getbyhwaddr(addr->sllc_arphrd, addr->sllc_smac); + rtnl_unlock(); + rc = -ENETUNREACH; + if (!dev) + goto release; + } else + dev = llc->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->sap, skb, addr, LLC_TEST_PRIM); + goto out; + } + if (addr->sllc_xid) { + rc = llc_ui_send_llc1(llc->sap, skb, addr, LLC_XID_PRIM); + goto out; + } + if (sk->type == SOCK_DGRAM || addr->sllc_ua) { + rc = llc_ui_send_llc1(llc->sap, skb, addr, LLC_DATAUNIT_PRIM); + goto out; + } + rc = -ENOPROTOOPT; + if (!(sk->type == SOCK_STREAM && !addr->sllc_ua)) + goto out; + rc = -ENOTCONN; + if (!llc->core_sk) + goto out; + rc = llc_ui_send_data(llc->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. + * @uaddr_len: 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 *uaddr_len, int peer) +{ + struct sockaddr_llc sllc; + struct sock *sk = sock->sk; + struct llc_ui_opt *llc = LLC_UI_SK(sk); + int rc = 0; + + lock_sock(sk); + if (sk->zapped) + goto out; + *uaddr_len = sizeof(sllc); + memset(uaddr, 0, *uaddr_len); + if (peer) { + rc = -ENOTCONN; + if (sk->state != TCP_ESTABLISHED) + goto out; + if(llc->dev) + sllc.sllc_arphrd = llc->dev->type; + sllc.sllc_dsap = LLC_SK(llc->core_sk)->daddr.lsap; + memcpy(&sllc.sllc_dmac, &LLC_SK(llc->core_sk)->daddr.mac, + IFHWADDRLEN); + } else { + rc = -EINVAL; + if (!llc->sap) + goto out; + sllc.sllc_ssap = llc->sap->laddr.lsap; + + if (llc->dev) { + sllc.sllc_arphrd = llc->dev->type; + memcpy(&sllc.sllc_smac, &llc->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 = LLC_UI_SK(sk); + int rc = -EINVAL, opt; + + lock_sock(sk); + if (level != SOL_LLC || optlen != sizeof(int)) + goto out; + rc = -ENOTCONN; + if (!llc->core_sk) + goto out; + rc = get_user(opt, (int *)optval); + if (rc) + goto out; + rc = -EINVAL; + switch (optname) { + case LLC_OPT_RETRY: + if (opt > LLC_OPT_MAX_RETRY) + goto out; + LLC_SK(llc->core_sk)->n2 = opt; + break; + case LLC_OPT_SIZE: + if (opt > LLC_OPT_MAX_SIZE) + goto out; + LLC_SK(llc->core_sk)->n1 = opt; + break; + case LLC_OPT_ACK_TMR_EXP: + if (opt > LLC_OPT_MAX_ACK_TMR_EXP) + goto out; + LLC_SK(llc->core_sk)->ack_timer.expire = opt; + break; + case LLC_OPT_P_TMR_EXP: + if (opt > LLC_OPT_MAX_P_TMR_EXP) + goto out; + LLC_SK(llc->core_sk)->pf_cycle_timer.expire = opt; + break; + case LLC_OPT_REJ_TMR_EXP: + if (opt > LLC_OPT_MAX_REJ_TMR_EXP) + goto out; + LLC_SK(llc->core_sk)->rej_sent_timer.expire = opt; + break; + case LLC_OPT_BUSY_TMR_EXP: + if (opt > LLC_OPT_MAX_BUSY_TMR_EXP) + goto out; + LLC_SK(llc->core_sk)->busy_state_timer.expire = opt; + break; + case LLC_OPT_TX_WIN: + if (opt > LLC_OPT_MAX_WIN) + goto out; + LLC_SK(llc->core_sk)->k = opt; + break; + case LLC_OPT_RX_WIN: + if (opt > LLC_OPT_MAX_WIN) + goto out; + LLC_SK(llc->core_sk)->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 = LLC_UI_SK(sk); + int val = 0, len = 0, rc = -EINVAL; + + lock_sock(sk); + if (level != SOL_LLC) + goto out; + rc = -ENOTCONN; + if (!llc->core_sk) + goto out; + rc = get_user(len, optlen); + if (rc) + goto out; + rc = -EINVAL; + if (len != sizeof(int)) + goto out; + switch (optname) { + case LLC_OPT_RETRY: + val = LLC_SK(llc->core_sk)->n2; + break; + case LLC_OPT_SIZE: + val = LLC_SK(llc->core_sk)->n1; + break; + case LLC_OPT_ACK_TMR_EXP: + val = LLC_SK(llc->core_sk)->ack_timer.expire; + break; + case LLC_OPT_P_TMR_EXP: + val = LLC_SK(llc->core_sk)->pf_cycle_timer.expire; + break; + case LLC_OPT_REJ_TMR_EXP: + val = LLC_SK(llc->core_sk)->rej_sent_timer.expire; + break; + case LLC_OPT_BUSY_TMR_EXP: + val = LLC_SK(llc->core_sk)->busy_state_timer.expire; + break; + case LLC_OPT_TX_WIN: + val = LLC_SK(llc->core_sk)->k; + break; + case LLC_OPT_RX_WIN: + val = LLC_SK(llc->core_sk)->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 sockaddr_llc *llc; + struct sk_buff *skb2, *skb = prim_data->skb; + struct sock *sk = llc_ui_find_sk_by_addr(&prim_data->daddr, skb->dev); + + if (!sk) + goto out; + if (sk->state == TCP_LISTEN) + goto out_put; + skb2 = skb_copy_expand(skb, sizeof(*llc), 0, GFP_ATOMIC); + if (!skb2) + goto out_put; + /* save primitive for use by the user. (pulled in recvmsg) */ + llc = (struct sockaddr_llc *)skb_push(skb2, sizeof(*llc)); + llc->sllc_family = AF_LLC; + llc->sllc_arphrd = skb->dev->type; + llc->sllc_test = 1; + llc->sllc_xid = 0; + llc->sllc_ua = 0; + llc->sllc_dsap = prim_data->daddr.lsap; + memcpy(llc->sllc_dmac, prim_data->daddr.mac, IFHWADDRLEN); + llc->sllc_ssap = prim_data->saddr.lsap; + memcpy(llc->sllc_smac, prim_data->saddr.mac, IFHWADDRLEN); + /* queue skb to the user. */ + if (sock_queue_rcv_skb(sk, skb2) < 0) + kfree_skb(skb2); +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 *skb2, *skb = prim_data->skb; + struct sockaddr_llc *llc; + struct sock *sk = llc_ui_find_sk_by_addr(&prim_data->daddr, skb->dev); + + if (!sk) + goto out; + if (sk->state == TCP_LISTEN) + goto out_put; + skb2 = skb_copy_expand(skb, sizeof(*llc), 0, GFP_ATOMIC); + if (!skb2) + goto out_put; + /* save primitive for use by the user. (pulled in + * recvmsg) */ + llc = (struct sockaddr_llc *)skb_push(skb2, sizeof(*llc)); + llc->sllc_family = AF_LLC; + llc->sllc_arphrd = 0; + llc->sllc_test = 0; + llc->sllc_xid = 1; + llc->sllc_ua = 0; + llc->sllc_dsap = prim_data->daddr.lsap; + memcpy(llc->sllc_dmac, prim_data->daddr.mac, IFHWADDRLEN); + llc->sllc_ssap = prim_data->saddr.lsap; + memcpy(llc->sllc_smac, prim_data->saddr.mac, IFHWADDRLEN); + /* queue skb to the user. */ + if (sock_queue_rcv_skb(sk, skb2) < 0) + kfree_skb(skb2); +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 sockaddr_llc *llc; + struct sk_buff *skb2, *skb = prim_data->skb; + struct sock *sk = llc_ui_find_sk_by_addr(&prim_data->daddr, skb->dev); + + if (!sk) + goto out; + if (sk->state == TCP_LISTEN) + goto out_put; + skb2 = skb_copy_expand(skb, sizeof(*llc), 0, GFP_ATOMIC); + if (!skb2) + goto out_put; + /* save primitive for use by the user. (pulled in + * recvmsg) */ + llc = (struct sockaddr_llc *)skb_push(skb2, sizeof(*llc)); + llc->sllc_family = AF_LLC; + llc->sllc_arphrd = skb->dev->type; + llc->sllc_test = 0; + llc->sllc_xid = 0; + llc->sllc_ua = 1; + llc->sllc_dsap = prim_data->daddr.lsap; + memcpy(llc->sllc_dmac, prim_data->daddr.mac, IFHWADDRLEN); + llc->sllc_ssap = prim_data->saddr.lsap; + memcpy(llc->sllc_smac, prim_data->saddr.mac, IFHWADDRLEN); + /* queue skb to the user. */ + if (sock_queue_rcv_skb(sk, skb2) < 0) + kfree_skb(skb2); +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->dev); + if (!sk) + 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 sockaddr_llc *llc; + struct sk_buff *skb2, *skb = prim_data->skb; + struct sock *sk = llc_ui_find_sk_by_link_no(prim->sap, prim_data->link); + + if (!sk) + goto out; + if (sk->type != SOCK_STREAM || sk->state != TCP_ESTABLISHED) + goto out_put; + skb2 = skb_copy_expand(skb, sizeof(*llc), 0, GFP_ATOMIC); + if (!skb2) + goto out_put; + /* save primitive for use by the user. (pulled in recvmsg) */ + llc = (struct sockaddr_llc *)skb_push(skb2, sizeof(*llc)); + llc->sllc_family = AF_LLC; + llc->sllc_arphrd = skb->dev->type; + llc->sllc_test = 0; + llc->sllc_xid = 0; + llc->sllc_ua = 0; + llc->sllc_dsap = LLC_UI_SK(sk)->sap->laddr.lsap; + memcpy(llc->sllc_dmac, LLC_SK(prim_data->sk)->laddr.mac, IFHWADDRLEN); + llc->sllc_ssap = LLC_SK(prim_data->sk)->daddr.lsap; + memcpy(llc->sllc_smac, LLC_SK(prim_data->sk)->daddr.mac, IFHWADDRLEN); + /* queue skb to the user. */ + if (sock_queue_rcv_skb(sk, skb2)) + kfree_skb(skb2); +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_ui_find_sk_by_link_no(prim->sap, prim_data->link); + + if (!sk) + goto out; + if (sk->type != SOCK_STREAM || sk->state != TCP_ESTABLISHED) + goto out_put; + LLC_UI_SK(sk)->core_sk = NULL; + 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 sock *sk = llc_ui_find_sk_by_addr(&LLC_SK(prim_data->sk)->laddr, + prim_data->dev); + + if (!sk) + goto out; + 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_SK(sk)->core_sk = prim_data->sk; + } else { + sk->socket->state = SS_UNCONNECTED; + sk->state = TCP_CLOSE; + LLC_UI_SK(sk)->core_sk = NULL; + } + sk->state_change(sk); + wake_up_interruptible(sk->sleep); +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_ui_find_sk_by_link_no(prim->sap, prim_data->link); + + if (sk) { + wake_up(sk->sleep); + sock_put(sk); + } +} + +/** + * 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_ui_find_sk_by_link_no(prim->sap, prim_data->link); + + if (!sk) + goto out; + 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); + wake_up_interruptible(sk->sleep); +out_put: + sock_put(sk); +out:; +} + +/** + * llc_ui_confirm - LLC user interface hook into the LLC layer + * @prim: Primative 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 " + "remote_mac_sap 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 = LLC_UI_SK(s); + len += sprintf(buffer + len, "%p ", s); + len += sprintf(buffer + len, "%02X ", s->type); + len += sprintf(buffer + len, "%02X ", + !llc_ui_mac_null(llc->addr.sllc_mmac)); + if (llc->sap) { + if (llc->dev && llc_ui_mac_null(llc->addr.sllc_mmac)) + len += sprintf(buffer + len, + "%02X:%02X:%02X:%02X:%02X:%02X", + llc->dev->dev_addr[0], + llc->dev->dev_addr[1], + llc->dev->dev_addr[2], + llc->dev->dev_addr[3], + llc->dev->dev_addr[4], + llc->dev->dev_addr[5]); + else { + if (!llc_ui_mac_null(llc->addr.sllc_mmac)) + len += sprintf(buffer + len, + "%02X:%02X:%02X:%02X:%02X:%02X", + llc->addr.sllc_mmac[0], + llc->addr.sllc_mmac[1], + llc->addr.sllc_mmac[2], + llc->addr.sllc_mmac[3], + llc->addr.sllc_mmac[4], + llc->addr.sllc_mmac[5]); + else + len += sprintf(buffer + len, + "%02X:%02X:%02X:%02X:%02X:%02X", + 0, 0, 0, 0, 0, 0); + } + len += sprintf(buffer + len, "@0x%02X ", + llc->sap->laddr.lsap); + } else + len += sprintf(buffer + len, + "%02X:%02X:%02X:%02X:%02X:%02X@0x%02X ", + 0, 0, 0, 0, 0, 0, 0); + if (llc->core_sk) { + len += sprintf(buffer + len, + "%02X:%02X:%02X:%02X:%02X:%02X@0x%02X ", + LLC_SK(llc->core_sk)->daddr.mac[0], + LLC_SK(llc->core_sk)->daddr.mac[1], + LLC_SK(llc->core_sk)->daddr.mac[2], + LLC_SK(llc->core_sk)->daddr.mac[3], + LLC_SK(llc->core_sk)->daddr.mac[4], + LLC_SK(llc->core_sk)->daddr.mac[5], + LLC_SK(llc->core_sk)->daddr.lsap); + } else + len += sprintf(buffer + len, + "%02X:%02X:%02X:%02X:%02X:%02X@0x%02X ", + 0, 0, 0, 0, 0, 0, 0); + len += sprintf(buffer + len, "%08X:%08X ", + atomic_read(&s->wmem_alloc), + atomic_read(&s->rmem_alloc)); + len += sprintf(buffer + len,"%02X %-3d ", s->state, + SOCK_INODE(s->socket)->i_uid); + if (llc->core_sk) + len += sprintf(buffer + len, "%-7d\n", + LLC_SK(llc->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 +SOCKOPS_WRAP(llc_ui, PF_LLC); + +int llc_ui_init(void) +{ + int i; + + for (i = 0; i < 256; i++) + llc_ui_sap_link_no_max[i] = 0; + llc_ui_sap_last_autoport = LLC_SAP_DYN_START; + (void)sock_register(&llc_ui_family_ops); + proc_net_create("llc", 0, llc_ui_get_info); + printk(KERN_INFO "NET4.0 IEEE 802.2 User Interface SAPs, " + "Jay Schulist, 2001\n"); + return 0; +} + +void llc_ui_exit(void) +{ + proc_net_remove("llc"); + sock_unregister(PF_LLC); + return; +} Index: kernel-acme/net/llc/llc_stat.c diff -u /dev/null kernel-acme/net/llc/llc_stat.c:1.1.2.4 --- /dev/null Thu Nov 22 23:55:00 2001 +++ kernel-acme/net/llc/llc_stat.c Sun Nov 11 15:29:45 2001 @@ -0,0 +1,207 @@ +/* + * llc_stat.c - Implementation of LLC station component state machine + * transitions + * Copyright (c) 1997 by Procom Technology, Inc. + * 2001 by Arnaldo Carvalho de Melo + * + * 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 +#include +#include +#include +#include +#include + +/* ------------------- COMMON STATION STATE transitions ------------------ */ + +/* dummy last-transition indicator; common to all state transition groups */ +static struct llc_station_state_trans llc_stat_state_trans_n = { + NULL, 0, NULL /* last entry for this state */ +}; + +/* ------------------------ 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[] = { + llc_station_ac_start_ack_timer, + llc_station_ac_set_retry_cnt_0, + llc_station_ac_set_xid_r_cnt_0, + llc_station_ac_send_null_dsap_xid_c, + NULL +}; + +static struct llc_station_state_trans llc_stat_down_state_trans_1 = { + llc_stat_ev_enable_with_dup_addr_check, + LLC_STATION_STATE_DUP_ADDR_CHK, + 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[] = { + llc_station_ac_report_status, /* STATION UP */ + NULL +}; + +static struct llc_station_state_trans llc_stat_down_state_trans_2 = { + llc_stat_ev_enable_without_dup_addr_check, + LLC_STATION_STATE_UP, + llc_stat_down_state_actions_2 +}; + +/* array of pointers; one to each transition */ +static struct llc_station_state_trans *llc_stat_dwn_state_trans[] = { + &llc_stat_down_state_trans_1, + &llc_stat_down_state_trans_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[] = { + llc_station_ac_report_status, /* STATION DOWN */ + NULL +}; + +static struct llc_station_state_trans llc_stat_up_state_trans_1 = { + llc_stat_ev_disable_req, LLC_STATION_STATE_DOWN, + 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[] = { + llc_station_ac_send_xid_r, + NULL +}; + +static struct llc_station_state_trans llc_stat_up_state_trans_2 = { + llc_stat_ev_rx_null_dsap_xid_c, LLC_STATION_STATE_UP, + 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[] = { + llc_station_ac_send_test_r, + NULL +}; + +static struct llc_station_state_trans llc_stat_up_state_trans_3 = { + llc_stat_ev_rx_null_dsap_test_c, LLC_STATION_STATE_UP, + llc_stat_up_state_actions_3 +}; + +/* array of pointers; one to each transition */ +static struct llc_station_state_trans *llc_stat_up_state_trans [] = { + &llc_stat_up_state_trans_1, + &llc_stat_up_state_trans_2, + &llc_stat_up_state_trans_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[] = { + llc_station_ac_inc_xid_r_cnt_by_1, + NULL +}; + +static struct llc_station_state_trans llc_stat_dupaddr_state_trans_1 = { + llc_stat_ev_rx_null_dsap_0_xid_r_xid_r_cnt_eq, + LLC_STATION_STATE_DUP_ADDR_CHK, + 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[] = { + llc_station_ac_report_status, /* DUPLICATE ADDRESS FOUND */ + NULL +}; + +static struct llc_station_state_trans llc_stat_dupaddr_state_trans_2 = { + llc_stat_ev_rx_null_dsap_1_xid_r_xid_r_cnt_eq, + LLC_STATION_STATE_DOWN, + 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[] = { + llc_station_ac_send_xid_r, + NULL +}; + +static struct llc_station_state_trans llc_stat_dupaddr_state_trans_3 = { + llc_stat_ev_rx_null_dsap_xid_c, LLC_STATION_STATE_DUP_ADDR_CHK, + 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[] = { + llc_station_ac_start_ack_timer, + llc_station_ac_inc_retry_cnt_by_1, + llc_station_ac_set_xid_r_cnt_0, + llc_station_ac_send_null_dsap_xid_c, + NULL +}; + +static struct llc_station_state_trans llc_stat_dupaddr_state_trans_4 = { + llc_stat_ev_ack_tmr_exp_lt_retry_cnt_max_retry, + LLC_STATION_STATE_DUP_ADDR_CHK, + 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[] = { + llc_station_ac_report_status, /* STATION UP */ + NULL +}; + +static struct llc_station_state_trans llc_stat_dupaddr_state_trans_5 = { + llc_stat_ev_ack_tmr_exp_eq_retry_cnt_max_retry, + LLC_STATION_STATE_UP, + 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[] = { + llc_station_ac_report_status, /* STATION DOWN */ + NULL +}; + +static struct llc_station_state_trans llc_stat_dupaddr_state_trans_6 = { + llc_stat_ev_disable_req, LLC_STATION_STATE_DOWN, + llc_stat_dupaddr_state_actions_6 +}; + +/* array of pointers; one to each transition */ +static struct llc_station_state_trans *llc_stat_dupaddr_state_trans[] = { + &llc_stat_dupaddr_state_trans_6, // Request + &llc_stat_dupaddr_state_trans_4, // Timer + &llc_stat_dupaddr_state_trans_5, + &llc_stat_dupaddr_state_trans_1, // Receive frame + &llc_stat_dupaddr_state_trans_2, + &llc_stat_dupaddr_state_trans_3, + &llc_stat_state_trans_n +}; + +struct llc_station_state llc_station_state_table[LLC_NBR_STATION_STATES] = { + { LLC_STATION_STATE_DOWN, llc_stat_dwn_state_trans }, + { LLC_STATION_STATE_DUP_ADDR_CHK, llc_stat_dupaddr_state_trans }, + { LLC_STATION_STATE_UP, llc_stat_up_state_trans } +}; Index: kernel-acme/net/netbeui/Makefile diff -u /dev/null kernel-acme/net/netbeui/Makefile:1.1.8.1 --- /dev/null Thu Nov 22 23:55:00 2001 +++ kernel-acme/net/netbeui/Makefile Tue Nov 6 20:13:49 2001 @@ -0,0 +1,22 @@ +# +# Makefile for the Linux NETBEUI layer. +# +# Note! 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... + +# We only get in/to here if CONFIG_NETBEUI = 'y' or 'm' + +O_TARGET := netbeui.o + +obj-y := llc_supp.o af_netb.o config.o dextab.o dgram_serve.o \ + name_serve.o nb_common.o proc.o query_serve.o session_serve.o \ + sock_dgram.o sock_name.o sock_session.o status_serve.o + +ifeq ($(CONFIG_NETBEUI),m) + obj-m += $(O_TARGET) +endif + +include $(TOPDIR)/Rules.make Index: kernel-acme/net/netbeui/af_netb.c diff -u /dev/null kernel-acme/net/netbeui/af_netb.c:1.1.8.1 --- /dev/null Thu Nov 22 23:55:00 2001 +++ kernel-acme/net/netbeui/af_netb.c Tue Nov 6 20:13:49 2001 @@ -0,0 +1,119 @@ +/* + * af_netb.c - Contains functions that supply socket system calls for NetBEUI + * protocol stack which their names has a 'netbeui_' prefix, and + * also some utility functions that their names has a 'nbso_' + * prefix. + * Notes: + * - VRP in comments is the acronym of "Value Result Parameter" + * + * Copyright (c) 1997 by Procom Technology, Inc. + * 2001 by Arnaldo Carvalho de Melo + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +extern struct proto_ops nbso_name_proto_ops; +extern struct proto_ops nbso_dgram_proto_ops; +extern struct proto_ops nbso_session_proto_ops; + +/* Socket System Calls */ +/* + * Function: netbeui_create + * Creates a NetBEUI socket. + * Parameters: + * sock : pointer to 'struct socket' that created by system before call + * of this function. + * protocol: an integer that have protocol specific meaning and we do not + * use it. + * Returns: int + * 0 : if socket has been created successfully. + * negative: if a fault occurs. + * -ESOCKTNOSUPPORT: Specified socket type NOT supported. + * -ENOMEM : Out of memory condition. + */ +static int netbeui_create(struct socket *sock, int protocol) +{ + struct proto_ops *prot; + struct sock *sk; + int rc = 0; + + MOD_INC_USE_COUNT; + switch (sock->type) { + case SOCK_STREAM: + prot = &nbso_session_proto_ops; + break; + case SOCK_DGRAM: + prot = &nbso_dgram_proto_ops; + break; + case SOCK_RAW: + prot = &nbso_name_proto_ops; + break; + case SOCK_SEQPACKET: + default: + rc = -ESOCKTNOSUPPORT; + goto err_notsupport; + } + sk = sk_alloc(PF_NETBEUI, GFP_KERNEL, 1); + if (!sk) + goto err_sk; + memset(NB_SK(sk), 0, sizeof(*NB_SK(sk))); + sock_init_data(sock, sk); + sock->ops = prot; + sk->state = NBSO_INIT; +out: return rc; +err_sk: rc = -ENOMEM; +err_notsupport: + MOD_DEC_USE_COUNT; + goto out; +} + +/* Socket family declarations */ +static struct net_proto_family netbeui_family_ops = { + family: PF_NETBEUI, + create: netbeui_create, +}; + +/* + * Function: nbso_init + * Initializes socket interface for servicing. This function is called + * in start of NetBEUI module installation in kernel memory. + * Parameters: None + * Returns: int + * 0 : if operation is performed successfully. + * negative: if a fault occurs. + */ +int nbso_init(void) +{ + return sock_register(&netbeui_family_ops); +} + +/* + * Function: nbso_exit + * Prepares socket interface for termination of NetBEUI module operation. + * Parameters: none + * Returns: int + * 0 : if operation is performed successfully. + * negative: if a fault occurs. + */ +int nbso_exit(void) +{ + return sock_unregister(netbeui_family_ops.family); +} Index: kernel-acme/net/netbeui/config.c diff -u /dev/null kernel-acme/net/netbeui/config.c:1.1.8.1 --- /dev/null Thu Nov 22 23:55:00 2001 +++ kernel-acme/net/netbeui/config.c Tue Nov 6 20:13:49 2001 @@ -0,0 +1,408 @@ +/* + * config.c - Contains functions that configure or query NetBEUI parameters + * from API tools such as ioctl/setsockopt/getsockopt. + * Notes: + * - VRP in comments is the acronym of "Value Result Parameter" + * + * Copyright (c) 1997 by Procom Technology, Inc. + * 2001 by Arnaldo Carvalho de Melo + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +/* Holds NetBIOS configurable parameters */ +config_t netbios_config = { + NETBEUI_DFLT_LINK_INACT_TMOUT, + NETBEUI_DFLT_TX_TMOUT, + NETBEUI_DFLT_TX_COUNT, + NETBEUI_DFLT_RSRC_TMOUT, + NETBEUI_DFLT_DATA_ACK_TMOUT +}; + +/* + * Function: nbcs_update_netbios_config + * Updates NetBIOS parameters with new values while considers special + * cases. + * Parameters: + * newcfg: pointer to config_t which contains new values for NetBIOS + * parameters. + * Returns: none + * Notes: + * - Since most NetBIOS parameters are tuned automatically both in LLC + * and NetBEUI, this implementation contains only a subset. + * - Setting value of parameter to zero means using NetBIOS defaults + * - Two parameters "resource_timeout" and "data_ack_timeout" are added + * by this implementation. + */ +static void nbcs_update_netbios_config(config_t *newcfg) +{ + /* Setting INACT TMOUT */ + if (!newcfg->inactivity_timeout) + netbios_config.inactivity_timeout = + NETBEUI_DFLT_LINK_INACT_TMOUT; + else if (newcfg->inactivity_timeout > NETBEUI_MAX_LINK_INACT_TMOUT) + netbios_config.inactivity_timeout = + NETBEUI_MAX_LINK_INACT_TMOUT; + else + netbios_config.inactivity_timeout = newcfg->inactivity_timeout; + + /* Setting TX TMOUT */ + if (!newcfg->transmit_timeout) + netbios_config.transmit_timeout = NETBEUI_DFLT_TX_TMOUT; + else if (newcfg->transmit_timeout > NETBEUI_MAX_TX_TMOUT) + netbios_config.transmit_timeout = NETBEUI_MAX_TX_TMOUT; + else + netbios_config.transmit_timeout = newcfg->transmit_timeout; + + /* Setting TX COUNT */ + if (!newcfg->transmit_count) + netbios_config.transmit_count = NETBEUI_DFLT_TX_COUNT; + else if (newcfg->transmit_count > NETBEUI_MAX_TX_COUNT) + netbios_config.transmit_count = NETBEUI_MAX_TX_COUNT; + else + netbios_config.transmit_count = newcfg->transmit_count; + + /* Setting RSRC TMOUT */ + if (!newcfg->resource_timeout) + netbios_config.resource_timeout = NETBEUI_DFLT_RSRC_TMOUT; + else if (newcfg->resource_timeout > NETBEUI_MAX_RSRC_TMOUT) + netbios_config.resource_timeout = NETBEUI_MAX_RSRC_TMOUT; + else + netbios_config.resource_timeout = newcfg->resource_timeout; + + /* Setting DATA ACK TMOUT */ + if (!newcfg->data_ack_timeout) + netbios_config.data_ack_timeout = NETBEUI_DFLT_DATA_ACK_TMOUT; + else if (newcfg->data_ack_timeout > NETBEUI_MAX_DATA_ACK_TMOUT) + netbios_config.data_ack_timeout = NETBEUI_MAX_DATA_ACK_TMOUT; + else + netbios_config.data_ack_timeout = newcfg->data_ack_timeout; +} + +/* + * Function: nbcs_setsockopt + * Sets SOL_NETBEUI layer options provided by setsockopt system call. + * Parameters: + * sock : pointer to socket structure + * optname: option code + * optval : pointer to new value of parameter (option) + * optlen : length of option value in bytes. + * Returns: + * 0 : if new value for option set successfully + * -EOPNOTSUPP : if option is not supported on socket type + * -ENOTPROTOOPT: if NetBEUI layer does not support the option. + * Notes: + * - Refer to Implementation documents for a complete description of + * NetBEUI layer options accessible from setsockopt. + * - user buffer is checked in upper layer. + */ +int nbcs_setsockopt(struct socket *sock, int optname, void *optval, int optlen) +{ + struct sock *sk = sock->sk; + int rc = -ENOPROTOOPT; + + if (optname == SO_URGENTACK) { + rc = -ENOTCONN; + if (NB_SK(sk)->u.st.session) { + __u8 urg_ack; + + rc = -EFAULT; + if (get_user(urg_ack, (__u8 *)optval)) + goto out; + rc = 0; + NB_SK(sk)->u.st.session->urgent_ack = urg_ack; + } + } else if (optname == SO_NBPARAM) { + config_t tmp_config; + + rc = -EINVAL; + if (optlen != sizeof(netbios_config)) + goto out; + rc = -EFAULT; + if (copy_from_user((void *)&tmp_config, optval, optlen)) + goto out; + rc = 0; + nbcs_update_netbios_config(&tmp_config); + } +out: return rc; +} + +/* + * Function: nbcs_getsockopt + * Gets SOL_NETBEUI layer options requested by getsockopt system call. + * Parameters: + * sock : pointer to socket structure + * optname: option code + * optval : (VRP) pointer to buffer to save value of parameter (option) + * optlen : (VRP) pointer length of option value in bytes. + * Returns: + * 0 : if value of option is moved to optval successfully + * -EOPNOTSUPP : if option is not supported on socket type + * -ENOTPROTOOPT: if NetBEUI layer does not support the option. + * Notes: + * - Refer to Implementation documents for a complete description of + * NetBEUI layer options accessible from setsockopt. + * - user buffer is checked in upper layer. + */ +int nbcs_getsockopt(struct socket *sock, int optname, void *optval, int *optlen) +{ + struct sock *sk = sock->sk; + int len, rc = -EFAULT; + + if (get_user(len, (int *)optlen)) + goto out; + if (optname == SO_URGENTACK) { + rc = -EOPNOTSUPP; + if (!NB_SK(sk)->u.st.session) + goto out; + len = MIN(len, sizeof(NB_SK(sk)->u.st.session->urgent_ack)); + rc = -EFAULT; + if (copy_to_user(optval, + (void *)&NB_SK(sk)->u.st.session->urgent_ack, + len) || put_user(len, (int *)optlen)) + goto out; + rc = 0; + } else if (optname == SO_NBPARAM) { + len = MIN(len, sizeof(netbios_config)); + rc = -EFAULT; + if (copy_to_user(optval, (void *)&netbios_config, len) || + put_user(len, (int *)optlen)) + goto out; + rc = 0; + } else + rc = -ENOPROTOOPT; +out: return rc; +} + +/* + * Function: nbcs_purge_links + * Disconnects links on a specific network device (if exist). + * Parameters: + * dev : pointer to device that links on it must be disconnect. + * flag: type of unbinding that specified by user. The + * NETBEUI_UNBIND_FLAG_SAFE flag means that protocol stack unbinds + * from the device only no links exist on it. + * Returns: int + * zero : if command be performed successfully. + * negative: if command fails. error codes that bubble to user are + * dependent to cmd. + * -EISCONN: the device has some links as yet, but the + * NETBEUI_UNBIND_FLAG_SAFE flag is specified. + */ +static int nbcs_purge_links(struct net_device *dev, unsigned char flag) +{ + int i, count, rc = 0; + dextab_t *ltbl = nbll_get_link_table(); + + spin_lock_bh(<bl->lock); + count = ltbl->count; + for (i = ltbl->reserved; count; i++, count--) { + link_t *nb_link = ltbl->addr[i]; + + if (nb_link->dev == dev) { + if (flag == NETBEUI_UNBIND_FLAG_DROP) + nbll_drop_link(nb_link->link); + else { /* NETBEUI_UNBIND_FLAG_SAFE */ + rc = -EISCONN; + goto out; + } + } + } +out: spin_unlock_bh(<bl->lock); + return rc; +} + +static int nbcs_config_bind(struct netbeui_cfg *usp) +{ + char name[IFNAMSIZ]; + struct net_device *dev; + unsigned char reserved; + int i, rc = -EFAULT; + + if (get_user(reserved, &usp->reserved)) + goto out; + rc = -EINVAL; + if (reserved) + goto out; + rc = -EFAULT; + if (copy_from_user(name, usp->nif_name, IFNAMSIZ)) + goto out; + rc = -ENOSPC; + write_lock_bh(&netbeui_adapters.lock); + if (netbeui_adapters.count == NETBEUI_MAX_ADAPTERS) + goto unlock_adapters; + rc = 0; + for (i = 0; i < netbeui_adapters.count; i++) + if (!strcmp(netbeui_adapters.dev[i]->name, name)) + goto unlock_adapters; /* It binds already */ + read_lock_bh(&dev_base_lock); + for (dev = dev_base; dev; dev = dev->next) { + if (strcmp(dev->name, name)) + continue; + if (nbcm_apt_dev(dev)) { + dev_hold(dev); + netbeui_adapters.dev[netbeui_adapters.count] = dev; + netbeui_adapters.count++; + dev_mc_add(dev, netbeui_funcaddr(dev), + dev->addr_len, 0); + goto unlock_dev_base; + } else { + rc = -EOPNOTSUPP; + goto unlock_dev_base; + } + } + rc = -ENODEV; +unlock_dev_base: + read_unlock_bh(&dev_base_lock); +unlock_adapters: + write_unlock_bh(&netbeui_adapters.lock); +out: return rc; +} + +static int nbcs_config_unbind(struct netbeui_cfg *usp) +{ + char name[IFNAMSIZ]; + unsigned char reserved, flag; + int i, rc = -EFAULT; + + if (get_user(reserved, &usp->reserved)) + goto out; + rc = -EINVAL; + if (reserved) + goto out; + rc = -EFAULT; + if (copy_from_user(name, usp->nif_name, IFNAMSIZ) || + get_user(flag, &usp->flag)) + goto out; + write_lock_bh(&netbeui_adapters.lock); + for (i = 0; i < netbeui_adapters.count; i++) { + if (strcmp(netbeui_adapters.dev[i]->name, name)) + continue; + rc = nbcs_purge_links(netbeui_adapters.dev[i], flag); + if (rc) + goto unlock; + dev_mc_delete(netbeui_adapters.dev[i], + netbeui_funcaddr(netbeui_adapters.dev[i]), + netbeui_adapters.dev[i]->addr_len, 0); + dev_put(netbeui_adapters.dev[i]); + netbeui_adapters.count--; + if (i < netbeui_adapters.count) + netbeui_adapters.dev[i] = + netbeui_adapters.dev[netbeui_adapters.count]; + netbeui_adapters.dev[netbeui_adapters.count] = NULL; + rc = 0; + goto unlock; + } + rc = -ENODEV; +unlock: write_unlock_bh(&netbeui_adapters.lock); +out: return rc; +} + +/* + * Function: nbcs_config + * Performs configuration actions on the NetBEUI via NBIOCCONFIG ioctl + * command. + * Parameters: + * usp: pointer to 'struct netbeui_cfg' that contains command and its + * arguments. + * Returns: int + * zero : if command is performed successfully. + * negative: if command fails. error codes that bubble to user are + * dependent to usp->command. + * -EOPNOTSUPP: the command is not valid. + */ +static int nbcs_config(struct netbeui_cfg *usp) +{ + unsigned short command; + int lnn, snn, rc = -EFAULT; + + if (get_user(command, &usp->command)) + goto out; + switch (command) { + case NETBEUI_CFGCMD_NIF_UNBIND: + rc = nbcs_config_unbind(usp); + break; + case NETBEUI_CFGCMD_NIF_BIND: + rc = nbcs_config_bind(usp); + break; + case NETBEUI_CFGCMD_DROP_SESS: + if (get_user(lnn, &usp->ln_num) || + get_user(snn, &usp->sn_num)) + break; + rc = -EINVAL; + if (lnn < 0 || lnn >= NETBEUI_MAX_LINKS || + snn < 1 || snn >= NETBEUI_MAX_SESSIONS) + break; + rc = nbss_drop_session(lnn, snn); + break; + case NETBEUI_CFGCMD_DROP_LINK: + if (get_user(lnn, &usp->ln_num)) + break; + rc = -EINVAL; + if (lnn < 0 || lnn >= NETBEUI_MAX_LINKS) + break; + rc = nbll_drop_link(lnn); + break; + default: + rc = -EOPNOTSUPP; + break; + } +out: return rc; +} + +/* + * Function: nbcs_ioctl + * Provides NetBEUI level requests of ioctl() system call. + * Parameters: + * cmd: code of command that must perform. + * arg: pointer to appropriate data structure, related to command. + * Returns: int + * zero : if command be performed successfully. + * negative: if command fails. error codes that bubble to user are + * dependent to cmd. + * -EOPNOTSUPP: the command is not valid. + */ +int nbcs_ioctl(unsigned int cmd, void *arg) +{ + int len, rc = -EOPNOTSUPP; + + switch (cmd) { + case NBIOCGSTATUS: { + struct netbeui_status *usp = arg; + + rc = -EFAULT; + if (get_user(len, &usp->buff_len)) + break; + rc = -EINVAL; + if (len < NETBEUI_MIN_STATUS_BUFF_LEN) + break; + rc = nbst_obtain_status(usp->called_name, + usp->status_buff, + &usp->buff_len); + break; + } + case NBIOCCONFIG: { + struct netbeui_cfg *usp = arg; + + rc = -EACCES; + if (!capable(CAP_NET_ADMIN)) + break; + rc = nbcs_config(usp); + break; + } + } + return rc; +} Index: kernel-acme/net/netbeui/dextab.c diff -u /dev/null kernel-acme/net/netbeui/dextab.c:1.1.8.1 --- /dev/null Thu Nov 22 23:55:00 2001 +++ kernel-acme/net/netbeui/dextab.c Tue Nov 6 20:13:49 2001 @@ -0,0 +1,244 @@ +/* + * dextab.c - Contains functions that implement a dynamically expandable table. + * The abstraction includes a data structure with a table of + * addresses which expands in order of two. The table never shrinks. + * + * Copyright (c) 1997 by Procom Technology, Inc. + * 2001 by Arnaldo Carvalho de Melo + * + * 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 +#include +#include +#include +#include + +/* + * Function: dextab_init + * Initializes dextab_t structure + * Parameters: + * tbl : pointer to dextab_t to be initialized + * reserved: number of entries to reserve from head of dextab. This value + * instructs other functions not to use those entries. + * max_size: maximum number of entries the can hold. + * Returns: none + * Note: + * - There are two ways to initialize a dextab: + * 1- Staticly initializating when defining dextab_t + * 2- calling dextab_init + */ +void dextab_init(dextab_t *tbl, int reserved, int max_size) +{ + tbl->addr = NULL; + tbl->size = 0; + tbl->reserved = reserved; + tbl->max_size = max_size; + tbl->count = 0; + spin_lock_init(&tbl->lock); +} + +/* + * Function: dextab_expand + * Expands table array of pointers in order of two. The old entries + * contents are safely kept in their place. + * Parameters: + * tbl : pointer to dextab_t to be expanded + * Returns: + * 0 : If table expanded successfully. + * non-zero: if table entry count has reached maximum size determined + * in initialization steps and if memory allocation failed. + * Don't grab tbl->lock, dextab_add_insert_entry already does this + */ +static int dextab_expand(dextab_t *tbl) +{ + int new_size; + void **new_addr; + int rc = -1; + + new_size = !tbl->size ? 16 : (tbl->size * 2); + /* Table size should not exceed maximum table size */ + if (new_size > tbl->max_size) { + if (tbl->size < tbl->max_size) + new_size = tbl->max_size; + else + goto out; + } + new_addr = kmalloc(new_size * sizeof(void *), GFP_ATOMIC); + if (!new_addr) + goto out; + /* Copy previous data from old area to new one */ + memset(new_addr, 0, new_size * sizeof(void *)); + if (tbl->addr) { + memcpy(new_addr, tbl->addr, tbl->size * sizeof(void *)); + kfree(tbl->addr); + } + tbl->addr = new_addr; + tbl->size = new_size; + rc = 0; +out: return rc; +} + +/* + * Function: dextab_add_insert_entry + * This is a back-end function which depending on its arguments creates + * an entry or puts a pre-allocated entry into dextab. The front ends + * are macros defined in header files. + * Parameters: + * tbl : pointer to dextab_t to insert element into + * entry_size: size of entry to add. if non-zero value passed, the memory + * is allocated with entry_size bytes. + * entry : address of entry to insert into table + * Returns: + * >=0 : uopn successfull insertion into table, this is the index + * of entry in table. + * <0 : unsuccessful insertion + * Note: + * - The entry_size and entry are exclusive arguments and entry_size + * precedes entry in decisions. + * - Add means allocate and insert entry, which is usually indicated by + * setting entry_size to non-zero value. + * - Insert means insert a pre-allocated entry, which is usually indicated + * by setting entry_size to zero and passing an address in entry. + */ +int __dextab_add_insert_entry(dextab_t *tbl, int entry_size, void *entry) +{ + int index, rc = -1; + + /* If you can find a free slot in table add entry there */ + for (index = tbl->reserved; index < tbl->size; index++) { + if (tbl->addr[index]) + continue; + tbl->addr[index] = entry_size > 0 ? + kmalloc(entry_size, GFP_ATOMIC) : entry; + if (tbl->addr[index]) { + tbl->count++; + rc = index; + } + goto out; + } + /* Try to expand table then retry again */ + if (!dextab_expand(tbl)) { + rc = __dextab_add_insert_entry(tbl, entry_size, entry); + goto out; + } + /* Could not expand table return error */ + rc = -1; +out: return rc; +} + +int dextab_add_insert_entry(dextab_t *tbl, int entry_size, void *entry) +{ + int rc = -1; + + spin_lock_bh(&tbl->lock); + rc = __dextab_add_insert_entry(tbl, entry_size, entry); + spin_unlock_bh(&tbl->lock); + return rc; +} + +/* + * Function: dextab_remove_delete_index + * This is a back-end function which depending on its arguments removes + * destructs an entry from dextab. The front ends are macros defined in + * header files. + * Parameters: + * tbl : pointer to dextab_t to remove element from + * entry_index: index of entry in table + * flag : removal or deletion flag. Values are: + * - DEXTAB_FREE_ENTRY + * - DEXTAB_NOFREE_ENTRY (otherwise) + * Returns: none + * Note: + * - The flag value is checked against DEXTAB_FREE_ENTRY + * - Remove means delete entry from table and free its memory, This is + * indicated by setting qflag DEXTAB_FREE_ENTRY + * - Delete means delete entry from memory, this is indicated by setting + * qflag to everything other than DEXTAB_FREE_ENTRY usually + * DEXTAB_NOFREE_ENTRY + */ +void __dextab_remove_delete_index(dextab_t *tbl, int entry_index, int flag) +{ + if (entry_index < tbl->size && entry_index >= tbl->reserved) + if (tbl->addr[entry_index]) { + if (flag == DEXTAB_FREE_ENTRY) + kfree(tbl->addr[entry_index]); + tbl->addr[entry_index] = NULL; + tbl->count--; + } +} + +void dextab_remove_delete_index(dextab_t *tbl, int entry_index, int flag) +{ + spin_lock_bh(&tbl->lock); + __dextab_remove_delete_index(tbl, entry_index, flag); + spin_unlock_bh(&tbl->lock); +} + +/* + * Function: dextab_entry_index + * Returns index of an entry in table using its address. + * Parameters: + * tbl : pointer to dextab_t to find entry in + * entry: address of entry to find in table + * Returns: + * >= 0: index of entry found in table + * < 0 : if entry not found in table + */ +int dextab_entry_index(dextab_t *tbl, void *entry) +{ + int index; + + spin_lock_bh(&tbl->lock); + for (index = tbl->reserved; index < tbl->size; index++) + if (tbl->addr[index] == entry) + break; + if (index >= tbl->size) + index = -1; + spin_unlock_bh(&tbl->lock); + return index; +} + +/* + * Function: dextab_count_entries + * returns count of entries in dextab + * Parameters: + * tbl : pointer to dextab_t to count its entries + * Returns: + * >0 : count of entries in table + */ +inline int __dextab_count_entries(dextab_t *tbl) +{ + return tbl->count; +} + +inline int dextab_count_entries(dextab_t *tbl) +{ + int rc; + + spin_lock_bh(&tbl->lock); + rc = __dextab_count_entries(tbl); + spin_unlock_bh(&tbl->lock); + return rc; +} + +/* + * Function: dextab_destruct + * housekeep a dextab, deallocates memories, .... + * Parameters: + * tbl: pointer to dextab_t to destruct + * Returns: none + */ +void dextab_destruct(dextab_t *tbl) +{ + spin_lock_bh(&tbl->lock); + if (tbl->addr) + kfree(tbl->addr); + spin_unlock_bh(&tbl->lock); + dextab_init(tbl, tbl->reserved, tbl->max_size); +} Index: kernel-acme/net/netbeui/dgram_serve.c diff -u /dev/null kernel-acme/net/netbeui/dgram_serve.c:1.1.8.1 --- /dev/null Thu Nov 22 23:55:00 2001 +++ kernel-acme/net/netbeui/dgram_serve.c Tue Nov 6 20:13:49 2001 @@ -0,0 +1,601 @@ +/* + * dgram_serve.c - Contains functions that supply DATAGRAM service for NetBEUI + * protocol stack, and also some utility functions. + * + * Notes: + * - VRP in comments is the acronym of "Value Result Parameter" + * + * Copyright (c) 1997 by Procom Technology, Inc. + * 2001 by Arnaldo Carvalho de Melo + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +#ifndef spin_trylock_bh +#define spin_trylock_bh(lock) ({ int __r; local_bh_disable(); \ + __r = spin_trylock(lock); \ + if (!__r) local_bh_enable(); \ + __r; }) +#endif + +unsigned int dgbc_mtu; /* DataGram BroadCast Maximum Transfer Unit */ + +static struct { + spinlock_t lock; + name_dgrms_t *obc; /* One Behind Cache */ + name_dgrms_t *normal_list; + name_dgrms_t *star_list; +} nbdg_names = { + lock: SPIN_LOCK_UNLOCKED, +}; + +/* Internal NBDG functions */ +/* + * Function: nbdg_find_name + * Finds sightly name_dgrms structure address depend on its name in the + * normal_list. + * + * Parameters: + * name : pointer to name that must find its structure address. + * + * Returns: name_dgrms_t * + * non NULL : address of related name_dgrms structure. + * NULL : means requested name not exist in normal_list. + */ +static inline name_dgrms_t *nbdg_find_name (char *name) +{ + name_dgrms_t *curr; + + for (curr = nbdg_names.normal_list; curr; curr = curr->next) + if (!memcmp(curr->name, name, NETBEUI_NAME_LEN)) + break; + return curr; +} + +/* + * Function: nbdg_find_such_frame + * Finds another frame in 'frameq' that has same "frame type" and + * "source name" with 'skb'. + * + * Parameters: + * skb : pointer to sk_buff that contains sightly frame. + * + * Returns: struct sk_buff * + * non NULL : address of appropriate sk_buff. + * NULL : if no such frame exist in frameq. + */ +static struct sk_buff *nbdg_find_such_frame(struct sk_buff *skb) +{ + char source_name[NETBEUI_NAME_LEN]; + dgram_t *hdrp = (dgram_t *)skb->data; + int frame_type = hdrp->command; + struct sk_buff_head *frameq = skb->list; + + memcpy(source_name, hdrp->source_name, NETBEUI_NAME_LEN); + skb = skb->next; + while (skb != (struct sk_buff *)frameq) { + hdrp = (dgram_t *)skb->data; + + if (hdrp->command == frame_type) + if (!memcmp(hdrp->source_name, source_name, + NETBEUI_NAME_LEN)) + return skb; + skb = skb->next; + } + return NULL; +} + +/* + * Function: nbdg_enqueue_name + * Enqueues a name_dgrms structure at begin of 'list' queue. it guards + * code from race conditions by Lock/UnLock 'nbdg_names' structure. + * + * Parameters: + * namep : pointer to name_dgrms structure that must be inserted into + * the list. + * list : pointer to a pointer that points to first member of list. + * + * Returns: none + */ +static void nbdg_enqueue_name(name_dgrms_t *namep, name_dgrms_t **list) +{ + spin_lock_bh(&nbdg_names.lock); + namep->prev = NULL; + namep->next = *list; + *list = namep; + if (namep->next) + namep->next->prev = namep; + namep->list = list; + spin_unlock_bh(&nbdg_names.lock); +} + +/* + * Function: nbdg_dequeue_name + * Dequeues a name_dgrms structure from 'list' queue. it guards code + * from race conditions by Lock/UnLock 'nbdg_names' structure. + * + * Parameters: + * namep : pointer to name_dgrms structure that must be removed from + * its list. + * + * Returns: none + */ +static void nbdg_dequeue_name(name_dgrms_t *namep) +{ + spin_lock_bh(&nbdg_names.lock); + if (namep->next) + namep->next->prev = namep->prev; + if (namep->prev) + namep->prev->next = namep->next; + else + *namep->list = namep->next; + spin_unlock_bh(&nbdg_names.lock); +} + +/* + * Exported NBDG functions + */ +/* + * Function: nbdg_set_dgbc_mtu + * Tunes the 'dgbc_mtu' global variable up to correct value. this function + * is called at begin of protocol stack installation in kernel space, and + * also must be called each time 'netbeui_adapters.dev[]' array altered. + * + * Parameters: none + * + * Returns: none + */ +void nbdg_set_dgbc_mtu(void) +{ + int i, tmp; + + read_lock(&netbeui_adapters.lock); + if (!netbeui_adapters.dev[0]) + goto out; + tmp = netbeui_adapters.dev[0]->mtu; + for (i = 1; i < NETBEUI_MAX_ADAPTERS && netbeui_adapters.dev[i]; i++) + tmp = MIN(tmp, netbeui_adapters.dev[i]->mtu); + dgbc_mtu = tmp - LLCMAC_UIB_HEADLEN() - NETBEUI_UILEN; +out: read_unlock(&netbeui_adapters.lock); +} + +/* + * Function: nbdg_remove_unwanted_dgf + * Removes frames in frame queue that contain up to 'len' bytes of data. + * user program can uses this function by call ioctl() with SIOCRUWDGF + * command. because we have not any mechanism in NetBEUI to recognize + * message boundaries, this is user (program) that recognizes them and + * announce us that Maximum 'len' bytes of remainder data is not + * beneficial and can be removed. 'len' == 0 means that we must remove all + * of such frames from queue. + * + * Parameters: + * namep : pointer to name_dgrms structure that operation perform on its + * frameq. + * len : maximum length which upper layer knows that is not usable. + * + * Returns: int + * 0 : this function always succeed. + */ +int nbdg_remove_unwanted_dgf(name_dgrms_t *namep, int len) +{ + struct sk_buff *skb = namep->curr_skb, + *such_skb; + if (!skb) + return 0; /* We are at begin of a message boundary now */ + namep->curr_skb = NULL; + if (len) { + len -= skb->len; + while (len > 0) { + such_skb = nbdg_find_such_frame(skb); + skb_unlink(skb); + kfree_skb(skb); + if (!such_skb) + return 0; + skb = such_skb; + len -= skb->len; + } + } else /* Remove all of such frames */ + while ((such_skb = nbdg_find_such_frame(skb))) { + skb_unlink(skb); + kfree_skb(skb); + skb = such_skb; + } + skb_unlink(skb); + kfree_skb(skb); + return 0; +} + +/* + * Function: nbdg_register_peername + * Registers a remote name for a local name. it causes that received + * frames only from specified remote name be acceptable. broadcasted frames + * will be accepted only if 'remote_name[0]' is equal to '*'. + * + * Parameters: + * namep : pointer to name_dgrms structure that remote name registers + * for it. + * remote_name : pointer to remote name that must be registered. + * + * Returns: none + */ +void nbdg_register_peername(name_dgrms_t *namep, char *remote_name) +{ + if (remote_name[0] == '*') { + if (namep->list != &nbdg_names.star_list) { + nbdg_dequeue_name(namep); + nbdg_enqueue_name(namep, &nbdg_names.star_list); + } + } else + if (namep->list != &nbdg_names.normal_list) { + nbdg_dequeue_name(namep); + nbdg_enqueue_name(namep, &nbdg_names.normal_list); + } + namep->conn_name = remote_name; + namep->connected = 1; +} + +/* + * Function: nbdg_deregister_peername + * Deregisters a registered name for a local name. it causes that received + * frames which are broadcasted or have our name in dest_name field of + * their NetBIOS header, be acceptable. + * + * Parameters: + * namep : pointer to name_dgrms structure that must deregister its + * attached name. + * + * Returns: none + */ +void nbdg_deregister_peername(name_dgrms_t *namep) +{ + if (namep->conn_name[0] == '*') { + nbdg_dequeue_name(namep); + nbdg_enqueue_name(namep, &nbdg_names.normal_list); + } + namep->connected = 0; +} + +/* + * Function: nbdg_add_name + * Creates a name_dgrms structure and adds it to normal_list. + * + * Parameters: + * local_name : pointer to name that must add its structure to normal_list. + * wq : pointer to a pointer that points to dependent wait_queue. + * namep : (VRP) pointer to a pointer buffer that in successful return + * contains address of related name_dgrms structure in list. + * + * Returns: int + * 0 : if operation is performed successfully. + * negative : if a fault occurs. + * -ENOMEM : Out of memory condition. + */ +int nbdg_add_name(char *local_name, wait_queue_head_t *wq, name_dgrms_t **namep) +{ + name_dgrms_t *new = kmalloc(sizeof(name_dgrms_t), GFP_KERNEL); + + if (!new) + return -ENOMEM; + memset(new, 0, sizeof(*new)); + skb_queue_head_init(&new->frameq); + memcpy(new->name, local_name, NETBEUI_NAME_LEN); + new->waitq = wq; + init_waitqueue_head(new->waitq); + nbdg_enqueue_name(new, &nbdg_names.normal_list); + *namep = new; + return 0; +} + +/* + * Function: nbdg_del_name + * Removes a name_dgrms structure from its list. + * + * Parameters: + * namep : pointer to name_dgrms structure that must deleted. + * + * Returns: none + */ +void nbdg_del_name(name_dgrms_t *namep) +{ + spin_lock_bh(&nbdg_names.lock); + if (nbdg_names.obc == namep) + nbdg_names.obc = NULL; + spin_unlock_bh(&nbdg_names.lock); + nbdg_dequeue_name(namep); + skb_queue_purge(&namep->frameq); + kfree(namep); +} + +/* + * Function: nbdg_receive_ready + * Answers to the question "Do in transport layer exist any queued + * received data for this name ?" + * + * Parameters: + * namep : pointer to name_dgrms structure that question is about it. + * + * Returns: int + * 0 : means "OK, exist some data". + * -1 : means "NO, not exist any queued received data as yet". + */ +int nbdg_receive_ready(name_dgrms_t *namep) +{ + return skb_queue_empty(&namep->frameq) ? -1 : 0 /* OK! */; +} + +/* + * Function: nbdg_send + * Sends datagrams to the sightly destination(s). + * + * Parameters: + * local_name : pointer to name that must copied to source_name field of + * NetBIOS header of all sent frames. + * dest_name : pointer to name that must copied to dest_name field of + * NetBIOS header of all sent frames (target name). + * dest_type : type of destination of frames. + * buff : pointer to buffer that contains data that must be sent. + * bufflen : length of data buffer. + * + * Returns: int + * positive : number of bytes that was sent. + * negative : if a fault occurs. + * -ENOMEM : Out of memory condition. + */ +int nbdg_send(struct sock *sk, char *local_name, char *dest_name, + name_type_t dest_type, struct iovec *iov, int len, int noblock) +{ + int rc, + fskbl, + offset = 0, + bytes_put = 0; + dgram_t hdr; + struct sk_buff *skb; + unsigned char *datap; + + if (dest_name[0] == '*') + hdr.command = NETBEUI_DATAGRAM_BROADCAST; + else { + hdr.command = NETBEUI_DATAGRAM; + memcpy(hdr.dest_name, dest_name, NETBEUI_NAME_LEN); + } + hdr.length = NETBEUI_UILEN; + hdr.delimiter = NETBEUI_DELIMITER; + memcpy(hdr.source_name, local_name, NETBEUI_NAME_LEN); + fskbl = CALC_DG_SKBLEN(NETBEUI_MAC_B_HEADLEN, dgbc_mtu + NETBEUI_UILEN); + skb = sock_alloc_send_skb(sk, fskbl, noblock, &rc); + if (!skb) + return -ENOMEM; + skb_reserve(skb, LLCMAC_UIB_HEADLEN()); + memcpy(skb_put(skb, sizeof(hdr)), &hdr, sizeof(hdr)); + + if (len > dgbc_mtu) { + datap = skb_put(skb, dgbc_mtu); + do { + rc = memcpy_fromiovecend(datap, iov, offset, dgbc_mtu); + if (rc) + goto out; + offset += dgbc_mtu; + bytes_put += dgbc_mtu; + len -= dgbc_mtu; + rc = nbll_uisend(NULL, skb); + if (rc) + goto out; + } while (len > dgbc_mtu); /* FIXME: > or >= ? */ + } + if (len) { + skb_trim(skb, sizeof(hdr)); + rc = memcpy_fromiovecend(skb_put(skb, len), iov, offset, len); + if (rc) + goto out; + bytes_put += len; + + rc = nbll_uisend(NULL, skb); + if (rc) + goto out; + } + rc = bytes_put; +out: kfree_skb(skb); + return rc; +} + +/* + * Function: nbdg_receive + * Copies data that received and queued into user data buffer. + * + * Parameters: + * namep : pointer to name_dgrms that we want receive data that + * queued for it. + * source_name : (VRP) pointer to a name buffer. if this parameter is not + * NULL at return the name buffer will be filled with message + * sender name. + * dest_name : (VRP) pointer to a name buffer. if this parameter is not + * NULL at return the name buffer will be filled with message + * destination name. + * buff : (VRP) pointer to data buffer. at return this buffer + * contains data that received. + * bufflen : indicates Maximum no of bytes that user wants to receive. + * nonblock : an integer that if be set to non-zero value means that + * no waiting (sleeping, blocking & ...) acceptable during + * operation. + * + * Returns: int + * positive : no of bytes that was received. + * negative : if a fault occurs. + * -EWOULDBLOCK : user requests non-blocking operation, but + * operation would block. + * -ERESTARTSYS : interrupted system call. + */ +int nbdg_receive(name_dgrms_t *namep, char *source_name, char *dest_name, + char *buff, int bufflen, int nonblock) +{ + int bytes_gotten = 0; + char *datap; + dgram_t *hdrp; + struct sk_buff *skb; + + while (skb_queue_empty(&namep->frameq)) + if (nonblock) + return -EWOULDBLOCK; + else { + interruptible_sleep_on(namep->waitq); + if (signal_pending(current)) + return -ERESTARTSYS; + } + skb = namep->curr_skb; + if (!skb) { + skb = namep->curr_skb = skb_peek(&namep->frameq); + skb->len -= sizeof(dgram_t); + } + hdrp = (dgram_t *)skb->data; + datap = (char *)(skb->tail - skb->len); + if (source_name) + memcpy(source_name, hdrp->source_name, NETBEUI_NAME_LEN); + if (dest_name) { + if (hdrp->command == NETBEUI_DATAGRAM) { + if (copy_to_user(dest_name, hdrp->dest_name, + NETBEUI_NAME_LEN)) + return -EFAULT; + } else + if (put_user('*', dest_name)) + return -EFAULT; + } + while (bufflen) { + int len = MIN(bufflen, skb->len); + + if (copy_to_user(buff, datap, len)) + return -EFAULT; + buff += len; + bufflen -= len; + datap += len; + skb->len -= len; + bytes_gotten += len; + + if (!skb->len) { /* sk_buff copied completely */ + struct sk_buff *such_skb = nbdg_find_such_frame(skb); + + skb_unlink(skb); + kfree_skb(skb); + namep->curr_skb = such_skb; + if (!such_skb) /* No any such frame */ + return bytes_gotten; + skb = such_skb; + hdrp = (dgram_t *)skb->data; + skb->len -= sizeof(dgram_t); + datap = (char *)(hdrp + sizeof(dgram_t)); + } + } + return bytes_gotten; +} + +/* + * Function: nbdg_get_datagram + * Takes a NETBEUI_DATAGRAM frame from 'llc supplementary' and place it on + * appropriate frame queues. + * + * Parameters: + * skb : pointer to sk_buff that contains NETBEUI_DATAGRAM frame. + * + * Returns: none + */ +void nbdg_get_datagram(struct sk_buff *skb) +{ + dgram_t *hdrp; + name_dgrms_t *namep; + + if (!spin_trylock_bh(&nbdg_names.lock)) + goto drop; + /* Place a copy of the skb in frame queue of all star_list's members */ + for (namep = nbdg_names.star_list; namep; namep = namep->next) { + struct sk_buff *new_skb = skb_clone(skb, GFP_ATOMIC); + + if (!new_skb) + continue; + skb_queue_tail(&namep->frameq, new_skb); + wake_up_interruptible(namep->waitq); + } + hdrp = (dgram_t *)skb->data; + + if (nbdg_names.obc && /* If One Behind Cache is not empty and ... */ + !memcmp(nbdg_names.obc->name, hdrp->dest_name, NETBEUI_NAME_LEN)) + namep = nbdg_names.obc; + else { + namep = nbdg_find_name(hdrp->dest_name); + if (!namep) /* No registered name for this sk_buff */ + goto unlock_drop; + nbdg_names.obc = namep; + } + if (namep->connected) + /* To force short circuiting by compiler */ + if (!memcmp(namep->conn_name, hdrp->source_name, + NETBEUI_NAME_LEN)) + /* Not connected to this source_name */ + goto unlock_drop; + /* Now we find the apposite name */ + skb_queue_tail(&namep->frameq, skb); + spin_unlock_bh(&nbdg_names.lock); + wake_up_interruptible(namep->waitq); +out: return; +unlock_drop: + spin_unlock_bh(&nbdg_names.lock); +drop: kfree_skb(skb); + goto out; +} + +/* + * Function: nbdg_get_datagram_broadcast + * Takes a NETBEUI_DATAGRAM_BROADCAST frame from 'llc supplementary' and + * place it on appropriate frame queues. + * + * Parameters: + * skb : pointer to sk_buff that contains + * NETBEUI_DATAGRAM_BROADCAST frame. + * + * Returns: none + */ +void nbdg_get_datagram_broadcast(struct sk_buff *skb) +{ + name_dgrms_t *namep; + struct sk_buff *new_skb; + + barrier(); + if (!spin_trylock_bh(&nbdg_names.lock)) + goto drop; + /* Place a copy of skb in frame queue of all star_list's members */ + for (namep = nbdg_names.star_list; namep; namep = namep->next) { + new_skb = skb_clone(skb, GFP_ATOMIC); + if (!new_skb) + continue; + skb_queue_tail(&namep->frameq, new_skb); + wake_up_interruptible(namep->waitq); + } + for (namep = nbdg_names.normal_list; namep; namep = namep->next) { + if (namep->connected) + /* To force short circuiting by compiler */ + if (memcmp(namep->conn_name, + ((dgram_t *)skb->data)->source_name, + NETBEUI_NAME_LEN)) + /* Not connected to this source_name */ + continue; + new_skb = skb_clone(skb, GFP_ATOMIC); + if (!new_skb) + continue; + skb_queue_tail(&namep->frameq, new_skb); + wake_up_interruptible(namep->waitq); + } + spin_unlock_bh(&nbdg_names.lock); +drop: kfree_skb(skb); +} Index: kernel-acme/net/netbeui/llc_supp.c diff -u /dev/null kernel-acme/net/netbeui/llc_supp.c:1.1.8.5 --- /dev/null Thu Nov 22 23:55:00 2001 +++ kernel-acme/net/netbeui/llc_supp.c Wed Nov 21 00:19:21 2001 @@ -0,0 +1,1461 @@ +/* + * llc_supp.c - Contains three category of functions + * 1- functions that implement Link Service State Machine + * 2- functions that simplify LLC interface layer services + * 3- dispatching functions + * + * Copyright (c) 1997 by Procom Technology, Inc. + * 2001 by Arnaldo Carvalho de Melo + * + * 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 +#include +#include +#include +#include +#define __KERNEL_SYSCALLS__ +#include +#include +#include +#include +#include +#include + +static void nbll_timer_function(unsigned long input); +/* These functions are Link State Transition handlers */ +static int nbll_conn_indicate_in_initial(link_t *nb_link); +static int nbll_conn_request_in_connwait(link_t *nb_link); +static int nbll_dummy_conn_in_initial(link_t *nb_link); +static int nbll_conn_confirm_in_connwait(link_t *nb_link); +static int nbll_conn_indicate_in_connwait(link_t *nb_link); +static int nbll_conn_reject_in_connwait(link_t *nb_link); +static int nbll_disc_request_in_connwait(link_t *nb_link); +static int nbll_reset_indicate_in_up(link_t *nb_link); +static int nbll_session_alive_in_up(link_t *nb_link); +static int nbll_disc_request_in_up(link_t *nb_link); +static int nbll_disc_indicate_in_up(link_t *nb_link); + +static int autobind; +MODULE_PARM(autobind, "i"); +MODULE_PARM_DESC(autobind, "Autobinds all multicast interfaces"); + +/* Contains hardcoded NetBIOS Frame Header length */ +u8 nb_cmd_hdr_len[] = { + 0x2C, 0x2C, 0x2C, 0x2C, 0x00, 0x00, 0x00, 0x2C, + 0x2C, 0x2C, 0x2C, 0x00, 0x00, 0x2C, 0x2C, 0x2C, + 0x00, 0x00, 0x00, 0x2C, 0x0E, 0x0E, 0x0E, 0x0E, + 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x00, 0x00, 0x0E +}; + +/* + * LLC is not re-entrant service to upper layer (NetBEUI) + * This flag, set upon entering LLC code, prevents NetBEUI to re-enter the + * code when its timers fire. + */ +volatile int llc_in_progress = 0; + +/* This is the communication medium between LLC and NetBEUI */ +static struct llc_sap *netbeui_sap; + +/* This is a list of adapters NetBEUI is bound to. */ +struct nb_adapters netbeui_adapters = { + lock: RW_LOCK_UNLOCKED, +}; + +/* LLC links to other network nodes */ +static dextab_t link_table = { + max_size: NETBEUI_MAX_LINKS, + lock: SPIN_LOCK_UNLOCKED +}; + +#define link_table_entry(i) ((link_t *)link_table.addr[i]) + +/* Link service state machine definition */ +typedef int (*link_event_handler_t)(link_t *); + +struct event_struct { + link_state_t next_state; + link_event_handler_t event_handler; +}; + +static struct event_struct link_state_table[3][9] = { + /* NETBEUI_LINK_INITIAL */ +{ +{ NETBEUI_LINK_UP, + nbll_conn_indicate_in_initial }, /* NETBEUI_LINK_CONN_INDICATE */ +{ -1, NULL }, /* NETBEUI_LINK_CONN_REQUEST */ +{ NETBEUI_LINK_CONNWAIT, + nbll_dummy_conn_in_initial }, /* NETBEUI_LINK_DUMMY_CONN */ +{ -1, NULL }, /* NETBEUI_LINK_CONN_CONFIRM */ +{ -1, NULL }, /* NETBEUI_LINK_CONN_REJECT */ +{ -1, NULL }, /* NETBEUI_LINK_RESET_INDICATE */ +{ -1, NULL }, /* NETBEUI_LINK_SESSION_ALIVE */ +{ -1, NULL }, /* NETBEUI_LINK_DISC_REQUEST */ +{ -1, NULL } /* NETBEUI_LINK_DISC_INDICATE */ +}, + /* NETBEUI_LINK_CONNWAIT */ +{ +{ NETBEUI_LINK_UP, + nbll_conn_indicate_in_connwait }, /* NETBEUI_LINK_CONN_INDICATE */ +{ NETBEUI_LINK_CONNWAIT, + nbll_conn_request_in_connwait }, /* NETBEUI_LINK_CONN_REQUEST */ +{ -1, NULL }, /* NETBEUI_LINK_DUMMY_CONN */ +{ NETBEUI_LINK_UP, + nbll_conn_confirm_in_connwait }, /* NETBEUI_LINK_CONN_CONFIRM */ +{ NETBEUI_LINK_INITIAL, + nbll_conn_reject_in_connwait }, /* NETBEUI_LINK_CONN_REJECT */ +{ -1, NULL }, /* NETBEUI_LINK_RESET_INDICATE */ +{ -1, NULL }, /* NETBEUI_LINK_SESSION_ALIVE */ +{ NETBEUI_LINK_INITIAL, + nbll_disc_request_in_connwait }, /* NETBEUI_LINK_DISC_REQUEST */ +{ -1, NULL } /* NETBEUI_LINK_DISC_INDICATE */ +}, + /* NETBEUI_LINK_UP */ +{ +{ -1, NULL }, /* NETBEUI_LINK_CONN_INDICATE */ +{ -1, NULL }, /* NETBEUI_LINK_CONN_REQUEST */ +{ -1, NULL }, /* NETBEUI_LINK_DUMMY_CONN */ +{ -1, NULL }, /* NETBEUI_LINK_CONN_CONFIRM */ +{ -1, NULL }, /* NETBEUI_LINK_CONN_REJECT */ +{ NETBEUI_LINK_UP, + nbll_reset_indicate_in_up }, /* NETBEUI_LINK_RESET_INDICATE */ +{ NETBEUI_LINK_UP, + nbll_session_alive_in_up }, /* NETBEUI_LINK_SESSION_ALIVE */ +{ NETBEUI_LINK_INITIAL, + nbll_disc_request_in_up }, /* NETBEUI_LINK_DISC_REQUEST */ +{ NETBEUI_LINK_INITIAL, + nbll_disc_indicate_in_up } /* NETBEUI_LINK_DISC_INDICATE */ +} +}; +/* + * LLC interface functions + * Data-out functions + */ +/* + * Function: nbll_isend_link + * This is the lowest level LLC interface function in sending and I-Frame + * It prepares a LLC frame structure, considers LLC re-entrancy problems + * and decides on LLC return value to handle many special cases. + * Parameters: + * rdev : pointer to struct net_device or adapter the frame is sent to + * This parameter is designed to minimize critical regions + * llc_handle: LLC handle of remote machine (is a pointer to LLC data) + * skb : the pointer to sk_buff containing data to send + * qflag : queuing flag. NetBEUI links control flow of data to LLC + * layer using a skb queue. Depending on the situation we + * are in, an unsuccessful try in delivering frame to LLC + * should queue the frame for later trials. This flag controls + * how this function should queue the skb. + * 0 : queue unsuccessful skb in tail and return 0 + * other: queue unsuccessful skb in head and return + * non-zero + * Returns: + * 0 : if LLC accepts the frame or frame is queue in Link for + * later trial + * qflag<> 0 : if queues frame for later trial + * non-zero : any other error value returned by LLC + * Notes: + * - Any modification to this function highly affects the whole system + * + * - A special race condition is an interrupt occurred when LLC returns + * from sap_request to NetBEUI. To cover it a critical region is created + * before LLC return statement in LLC code and is destroyed after + * sap_request statement in NetBEUI code. This is why you may see + * wonderful save and restore flag calls in the code. + * + * acme: with all the locking changes I think that this race is gone + * so, for now I'll get rid of all this 'wonderful' save and restore + */ +static int nbll_isend_link(struct net_device *rdev, struct sock *llc_handle, + struct sk_buff *skb, unsigned char link, + int qflag) +{ + union llc_u_prim_data prim_data; + struct llc_prim_if_block prim; + int rc; + + prim.data = &prim_data; + skb->protocol = htons(ETH_P_802_2); + if (!memcmp(rdev->name, "tr", 2)) + skb->protocol = htons(ETH_P_TR_802_2); + skb->dev = rdev; + skb->mac.raw = skb->head; + prim_data.data.pri = 0; + prim_data.data.sk = llc_handle; + prim_data.data.skb = skb; + prim.sap = netbeui_sap; + prim.prim = LLC_DATA_PRIM; +restart: + llc_in_progress = 1; + barrier(); + rc = netbeui_sap->req(&prim); + llc_in_progress = 0; + barrier(); + switch (rc) { + case 0: + goto out; + case -ERESTART: + goto restart; + case -EBUSY: { + link_t *nb_link = link_table_entry(link); + + if (!nb_link) { + kfree_skb(skb); + rc = -ECONNABORTED; + goto out; + } + nb_link->llc_busy = 1; + if (!qflag) + __skb_queue_tail(&nb_link->skbq, skb); + else + __skb_queue_head(&nb_link->skbq, skb); + rc = qflag; + goto out; + } + } + kfree_skb(skb); +out: return rc; +} + +/* + * Function: nbll_free_link + * Does a complete housekeeping for a link including queue/table/... + * Parameters: + * nb_link : pointer to link_t to destruct + * Returns: none + */ +static inline void nbll_free_link(link_t *nb_link) +{ + skb_queue_purge(&nb_link->skbq); + dextab_destruct(&nb_link->session_table); + kfree(nb_link); +} + +/* used by session_serve.c as well, so cannot be static */ +inline void nbll_link_put(link_t *nb_link) +{ + if (atomic_dec_and_test(&nb_link->refcnt)) + nbll_free_link(nb_link); +} + +static inline void nbll_link_hold(link_t *nb_link) +{ + atomic_inc(&nb_link->refcnt); +} + +/** + * nbll_isend - sends I-Frames on a LLC connection + * @link: An integer containing link number in link table + * @skb: pointer to sk_buff containing data to send + * + * This is the interface routine that actually tries to send I-Frames + * on an LLC connection. + * Returns: + * 0: if LLC accepts frame or it is queued for later trial + * -ECONNABORTED: If connection is aborted by remote peer + * -ENOMEM : If cannot allocate a skb + * non-zero: any other error value returned by LLC + * Notes: + * - Since I intended to call this function freely in upper layer codes, + * it contains a critical region checking LINK consistency + */ +int nbll_isend(int link, struct sk_buff *skb) +{ + link_t *nb_link = NULL; + struct net_device *link_dev; + struct sock *link_llc_handle; + int rc = -EINVAL; + + spin_lock_bh(&link_table.lock); + if (link > link_table.count) { + spin_unlock_bh(&link_table.lock); + goto freeskb; + } + nb_link = link_table_entry(link); + rc = -ECONNABORTED; + if (!nb_link) { + spin_unlock(&link_table.lock); + goto freeskb; + } + nbll_link_hold(nb_link); + spin_unlock_bh(&link_table.lock); + rc = 0; + if (nb_link->llc_busy == 1) { + __skb_queue_tail(&nb_link->skbq, skb); + goto out; + } + nb_link->iactivity++; + link_dev = nb_link->dev; + link_llc_handle = nb_link->llc_handle; + rc = nbll_isend_link(link_dev, link_llc_handle, skb, link, 0); +out: nbll_link_put(nb_link); + return rc; +freeskb: + kfree_skb(skb); + goto out; +} + +/* + * Function: nbll_uisend_mac + * This is the lowest level LLC interface function in sending and UI-Frame + * Parameters: + * remote_mac: pointer to MAC address of destination node or NetBIOS + * functional address of device the frame is sent to. + * skb : pointer to sk_buff containing data to send + * Returns: + * 0 : if LLC accepts frame + * -ENOMEM : If cannot allocate a skb + * non-zero: any other error value returned by LLC + */ +static int nbll_uisend_mac(unsigned char *remote_mac, struct sk_buff *skb) +{ + union llc_u_prim_data prim_data; + struct llc_prim_if_block prim; + + prim.data = &prim_data; + skb->protocol = htons(ETH_P_802_2); + if (!memcmp(skb->dev->name, "tr", 2)) + skb->protocol = htons(ETH_P_TR_802_2); + + prim_data.udata.saddr.lsap = LLC_SAP_NETBEUI; + memcpy(prim_data.udata.saddr.mac, skb->dev->dev_addr, + skb->dev->addr_len); + prim_data.udata.daddr.lsap = LLC_SAP_NETBEUI; + memcpy(prim_data.udata.daddr.mac, remote_mac, skb->dev->addr_len); + prim_data.udata.skb = skb; + prim.sap = netbeui_sap; + prim.prim = LLC_DATAUNIT_PRIM; + return netbeui_sap->req(&prim); +} + +/* + * Function: nbll_uisend + * This is the interface routine that actually tries to send UI-Frames + * on an LLC connection. + * Parameters: + * remote_mac: pointer to MAC address of destination system or NULL + * to indicate broadcast to all NICs NetBEUI is bound to + * skb : pointer to sk_buff containing data to send + * Returns: + * 0 : if LLC accepts frame + * -ENOMEM : If cannot allocate a skb + * or cannot create a copy from skb. + * non-zero: any other error value returned by LLC + */ +int nbll_uisend(unsigned char *remote_mac, struct sk_buff *skb) +{ + int i, rc = -ENOMEM; + + /* If datagram has a destination */ + if (remote_mac) { + rc = nbll_uisend_mac(remote_mac, skb); + goto out; + } + /* Datagram is to be broadcasted to all interfaces */ + read_lock(&netbeui_adapters.lock); + for (i = 0; i < NETBEUI_MAX_ADAPTERS && netbeui_adapters.dev[i]; i++) { + struct sk_buff *skb2 = skb_copy(skb, GFP_ATOMIC); + + if (!skb2) + goto out; + skb2->dev = netbeui_adapters.dev[i]; + nbll_uisend_mac(netbeui_funcaddr(skb2->dev), skb2); + } + rc = 0; +out: read_unlock(&netbeui_adapters.lock); + return rc; +} + +/* + * Link service state machine functions + * Implementing general functions + */ +/* + * Function: nbll_alloc_link + * Allocates and Initializes a link_t structure + * Parameters: none + * Returns: + * NULL : if can not allocate link_t structure + * non-NULL: if link_t is allocated and initialized + * Note: + * - Link timer is initialized but not started + * - The call to memset does implicitly initialize all fields. Those + * fields that need explicit non-zero initialization are manipulated + * afterwards. + */ +static link_t *nbll_alloc_link(void) +{ + link_t *nb_link = kmalloc(sizeof(*nb_link), GFP_ATOMIC); + + if (!nb_link) + goto out; + /* Implicitly initialize all fields */ + memset(nb_link, 0, sizeof(*nb_link)); + nb_link->state = NETBEUI_LINK_INITIAL; + nb_link->link = -1; + dextab_init(&nb_link->session_table, 1, NETBEUI_MAX_SESSIONS); + skb_queue_head_init(&nb_link->skbq); + init_waitqueue_head(&nb_link->waitq); + init_timer(&nb_link->timer); + nb_link->timer.data = (unsigned long)nb_link; + nb_link->timer.function = nbll_timer_function; + atomic_set(&nb_link->refcnt, 1); +out: return nb_link; +} + +/* + * Function: nbll_insert_link_into_table + * Inserts a previously allocated/initialized link into system link table + * Parameters: + * nb_link : pointer to link_t to insert + * Returns: none + * 0 : if nb_link is inserted into system link_table + * -ENOSPC : if link_table is full + */ +static int nbll_insert_link_into_table(link_t *nb_link) +{ + int link; + int rc = 0; + + nbll_link_hold(nb_link); + link = dextab_insert_entry(&link_table, nb_link); + if (link < 0) + rc = -ENOSPC; + else + nb_link->link = link; + nbll_link_put(nb_link); + return rc; +} + +/* + * Function: nbll_delete_link_from_table + * Deletes a link from system link table + * Parameters: + * nb_link : pointer to link_t to delete + * Returns: none + */ +static void nbll_delete_link_from_table(link_t *nb_link) +{ + nbll_link_hold(nb_link); + dextab_delete_index(&link_table, nb_link->link); + nb_link->link = -1; + nbll_link_put(nb_link); +} + +/* + * Function: nbll_find_link + * Finds a link in system link table to a remote node from its mac and + * the device we are connected via + * Parameters: + * dev : pointer to struct net_device we are connected to remote + * node via + * remote_mac: pointer to MAC address of remote node we are connected to + * Returns: + * NULL : if a corresponding link is not found + * non-NULL : the link_t address of corresponding link + */ +static link_t *nbll_find_link(struct net_device *dev, unsigned char *remote_mac) +{ + int index; + link_t *nb_link = NULL; + + spin_lock(&link_table.lock); + for (index = link_table.reserved; index < link_table.size; index++) { + nb_link = link_table_entry(index); + if (nb_link && nb_link->dev == dev && + !memcmp(nb_link->remote_mac, remote_mac, dev->addr_len)) + break; + } + if (nb_link) + nbll_link_hold(nb_link); + spin_unlock(&link_table.lock); + return nb_link; +} + +/* + * Function: nbll_request_llc_connect + * Prepares and sends a connection request to LLC layer. + * Parameters: + * nb_link : pointer to link_t structure prepared for this new connection + * A NetBEUI link_t is the counterpart of LLC connection struct. + * Returns: + * 0 : LLC accepted connection request + * non-zero: LLC rejected connection request + */ +static int nbll_request_llc_connect(link_t *nb_link) +{ + struct net_device *dev; + struct llc_prim_if_block prim; + union llc_u_prim_data prim_data; + int rc; + + nbll_link_hold(nb_link); + dev = nb_link->dev; + prim.data = &prim_data; + prim_data.conn.saddr.lsap = LLC_SAP_NETBEUI; + memcpy(prim_data.conn.saddr.mac, dev->dev_addr, dev->addr_len); + prim_data.conn.daddr.lsap = LLC_SAP_NETBEUI; + memcpy(prim_data.conn.daddr.mac, nb_link->remote_mac, + dev->addr_len); + prim_data.conn.dev = dev; + prim_data.conn.link = nb_link->link; + prim_data.conn.sk = NULL; + prim_data.conn.pri = 0; + prim.prim = LLC_CONN_PRIM; + prim.sap = netbeui_sap; + rc = netbeui_sap->req(&prim); + nbll_link_put(nb_link); + return rc; +} + +/* + * Function: nbll_request_llc_disconnect + * Prepares and sends a disconnection request to LLC layer. + * Parameters: + * nb_link : pointer to link_t structure responsible for this connection + * A NetBEUI link_t is the counterpart of LLC connection struct. + * Returns: + * 0 : LLC accepted disconnection request + * non-zero: LLC rejected disconnection request + * Note: + * - Currently LLC always accepts disconnection request. + */ +static int nbll_request_llc_disconnect(link_t *nb_link) +{ + struct llc_prim_if_block prim; + union llc_u_prim_data prim_data; + int rc; + + nbll_link_hold(nb_link); + prim.data = &prim_data; + prim_data.disc.sk = nb_link->llc_handle; + prim.prim = LLC_DISC_PRIM; + prim.sap = netbeui_sap; + rc = netbeui_sap->req(&prim); + nbll_link_put(nb_link); + return rc; +} + +/* + * Function: nbll_isend_session_alive + * Prepares a NetBIOS SESSION ALIVE frame and nbll_isends it to link + * Parameters: + * nb_link: pointer to link_t structure to send SESSION ALIVE to + * Returns: None + */ +static void nbll_isend_session_alive(link_t *nb_link) +{ + packet_t *hdr; + int llcmac_ihl, + session_packet_len = nb_cmd_hdr_len[NETBEUI_SESSION_ALIVE]; + struct sk_buff *skb; + + nbll_link_hold(nb_link); + llcmac_ihl = LLCMAC_I_HEADLEN(nb_link->dev); + skb = alloc_skb((session_packet_len + llcmac_ihl), GFP_ATOMIC); + if (!skb) + goto out; + skb_reserve(skb, llcmac_ihl); + skb->nh.raw = skb->h.raw = skb->data; + hdr = (packet_t *)skb_put(skb, session_packet_len); + hdr->length = session_packet_len; + hdr->delimiter = NETBEUI_DELIMITER; + hdr->command = NETBEUI_SESSION_ALIVE; + hdr->data1 = 0; + hdr->data2 = 0; + hdr->xmit_correlator = 0; + hdr->resp_correlator = 0; + hdr->dest_num = 0; + hdr->source_num = 0; + nbll_isend(nb_link->link, skb); +out: nbll_link_put(nb_link); +} + +/* + * Function: nbll_handle_event + * This is the heart of Link Service State Machine, which performs a + * transition from current state of link element to new state based + * on event occurred and link state table contents. + * Parameters: + * event : An integer of NETBEUI_LINK_* family that implies type of event + * nb_link : pointer to link_t structure which the event occurred on + * Returns: none + * Notes: + * - The state changes before actions be executed. This is due to + * non deterministic behavior of actions which may sleep the current + * process, thus stopping the function in the mid-way. + */ +static void nbll_handle_event(link_event_t event, link_t *nb_link) +{ + struct event_struct *ev; + + nbll_link_hold(nb_link); + ev = &link_state_table[nb_link->state][event]; + if (ev && ev->event_handler) { + link_state_t old_state = nb_link->state; + + nb_link->state = ev->next_state; + if (ev->event_handler(nb_link)) + nb_link->state = old_state; + } + nbll_link_put(nb_link); +} + +/* + * Function: nbll_timer_function + * This is the callback function triggered upon expiration of link + * inactivity timer. It just injects an event into state machine for + * its link. + * Parameters: + * input : pointer to link_t structure whose timer is expired. + * Returns: none + */ +static void nbll_timer_function(unsigned long input) +{ + link_t *nb_link = (link_t *)input; + + nbll_link_hold(nb_link); + nbll_handle_event(NETBEUI_LINK_SESSION_ALIVE, nb_link); + nbll_link_put(nb_link); +} + +/* + * Link service state machine functions + * Implementing transition functions + */ +/* + * Function: nbll_xxxx_in_ssss + * The section below contains functions that implement actions needed + * to legally transit from one state to another. + * Parameters: + * nb_link : pointer to link_t structure which the actions are to be + * applied to + * Returns: + * 0 : if all actions are done successfully + * non-zero: if one of actions failed + * Note: + * - For the sake of simplicity, the actions are automatically rollbacked + * in each function, if an action in transition fails. The design + * documents do not cover these parts of code. + */ +static int nbll_conn_indicate_in_initial(link_t *nb_link) +{ + int rc; + + nbll_link_hold(nb_link); + nb_link->status = nbll_insert_link_into_table(nb_link); + if (!nb_link->status) { + nb_link->timer.expires = jiffies + NETBEUI_INACTIVITY_TIMEOUT; + add_timer(&nb_link->timer); + } + rc = nb_link->status; + nbll_link_put(nb_link); + return rc; +} + +static int nbll_conn_request_in_connwait(link_t *nb_link) +{ + int rc = -1; + + nbll_link_hold(nb_link); + if (nbll_request_llc_connect(nb_link)) + goto out; + wait_event(nb_link->waitq, nb_link->state != NETBEUI_LINK_CONNWAIT); + rc = 0; +out: nbll_link_put(nb_link); + return rc; +} + +static int nbll_dummy_conn_in_initial(link_t *nb_link) +{ + int rc; + + nbll_link_hold(nb_link); + rc = nbll_insert_link_into_table(nb_link); + nbll_link_put(nb_link); + return rc; +} + +static int nbll_conn_confirm_in_connwait(link_t *nb_link) +{ + nbll_link_hold(nb_link); + nb_link->timer.expires = jiffies + NETBEUI_INACTIVITY_TIMEOUT; + add_timer(&nb_link->timer); + nb_link->status = 0; + wake_up(&nb_link->waitq); + nbll_link_put(nb_link); + return 0; +} + +static int nbll_conn_indicate_in_connwait(link_t *nb_link) +{ + nbll_link_hold(nb_link); + nb_link->timer.expires = jiffies + NETBEUI_INACTIVITY_TIMEOUT; + add_timer(&nb_link->timer); + nbll_link_put(nb_link); + return 0; +} + +static int nbll_conn_reject_in_connwait(link_t *nb_link) +{ + nbll_link_hold(nb_link); + nbll_delete_link_from_table(nb_link); + nb_link->status = -ECONNREFUSED; + wake_up(&nb_link->waitq); + nbll_link_put(nb_link); + return 0; +} + +static int nbll_disc_request_in_connwait(link_t *nb_link) +{ + nbll_link_hold(nb_link); + nbll_delete_link_from_table(nb_link); + nbll_link_put(nb_link); + return 0; +} + +static int nbll_reset_indicate_in_up(link_t *nb_link) +{ + int index; + + nbll_link_hold(nb_link); + for (index = nb_link->session_table.reserved; + index < nb_link->session_table.size; index++) { + session_t *session = (session_t *) + nb_link->session_table.addr[index]; + if (session) { + nbss_abort_session(session); + dextab_delete_index(&nb_link->session_table, + session->lsn); + } + } + del_timer(&nb_link->timer); + nb_link->iactivity = 0; + nb_link->timer.expires = jiffies + NETBEUI_INACTIVITY_TIMEOUT; + add_timer(&nb_link->timer); + nbll_link_put(nb_link); + return 0; +} + +static int nbll_session_alive_in_up(link_t *nb_link) +{ + nbll_link_hold(nb_link); + if (!nb_link->iactivity) + nbll_isend_session_alive(nb_link); + nb_link->iactivity = 0; + nb_link->timer.expires = jiffies + NETBEUI_INACTIVITY_TIMEOUT; + add_timer(&nb_link->timer); + nbll_link_put(nb_link); + return 0; +} + +static int nbll_disc_request_in_up(link_t *nb_link) +{ + nbll_link_hold(nb_link); + del_timer(&nb_link->timer); + nbll_request_llc_disconnect(nb_link); + nbll_delete_link_from_table(nb_link); + nbll_link_put(nb_link); + return 0; +} + +static int nbll_disc_indicate_in_up(link_t *nb_link) +{ + int index; + + nbll_link_hold(nb_link); + del_timer(&nb_link->timer); + + for (index = nb_link->session_table.reserved; + index < nb_link->session_table.size; index++) { + session_t *session = (session_t *) + nb_link->session_table.addr[index]; + if (session) { + nbss_abort_session(session); + dextab_delete_index(&nb_link->session_table, + session->lsn); + } + } + dextab_destruct(&nb_link->session_table); + nbll_delete_link_from_table(nb_link); + nbll_link_put(nb_link); + return 0; +} + +/* + * Link service state machine functions + * Implementing interface functions + */ +/* + * Function: get_disconnect_indicate + * Accepts a connection indication from LLC layer. If it can establish + * a link anyway, generates an event on link element, otherwise requests + * LLC to disconnect (! this is LLC rule) + * Parameters: + * prim: pointer to primary interface block type, the LLC communication + * data structure. This is a union with different fields with + * different meaning for different purposes. + * Returns: none + */ +static void nbll_get_connect_indicate(struct llc_prim_if_block *prim) +{ + struct llc_prim_conn *prim_data = &prim->data->conn; + link_t *nb_link = nbll_find_link(prim_data->dev, prim_data->saddr.mac); + + if (!nb_link) { + nb_link = nbll_alloc_link(); + if (nb_link) + nbll_link_hold(nb_link); + } + if (nb_link) { + nb_link->llc_handle = prim_data->sk; + nb_link->dev = prim_data->dev; + memcpy(nb_link->remote_mac, prim_data->saddr.mac, + nb_link->dev->addr_len); + nbll_handle_event(NETBEUI_LINK_CONN_INDICATE, nb_link); + } + if (!nb_link || nb_link->state == NETBEUI_LINK_INITIAL) { + /* Request link disconnection */ + prim->data->disc.sk = prim_data->sk; + prim->prim = LLC_DISC_PRIM; + prim->sap = netbeui_sap; + netbeui_sap->req(prim); + if (nb_link) + nbll_link_put(nb_link); + } else { + prim->prim = LLC_CONN_PRIM; + prim->sap = netbeui_sap; + prim_data->link = nb_link->link; + netbeui_sap->resp(prim); + } + if (nb_link) + nbll_link_put(nb_link); +} + +/* + * Function: nbll_get_connect_confirm + * Accepts a connection confirm for a previously requested connection from + * LLC layer. The LLC may confirm a connection request positively or + * negatively. + * Parameters: + * prim : pointer to primary interface block type, the LLC communication + * data structure. This is a union with different fields with + * different meaning for different purposes. + * Returns: none + */ +static void nbll_get_connect_confirm(struct llc_prim_if_block *prim) +{ + struct llc_prim_conn *prim_data = &prim->data->conn; + link_t *nb_link = link_table_entry(prim_data->link); + + nbll_link_hold(nb_link); + if (prim_data->status == LLC_STATUS_CONN) { + nb_link->llc_handle = prim_data->sk; + nbll_handle_event(NETBEUI_LINK_CONN_CONFIRM, nb_link); + } else + nbll_handle_event(NETBEUI_LINK_CONN_REJECT, nb_link); + nbll_link_put(nb_link); +} + +/* + * Function: nbll_get_data_confirm + * Accepts a data confirm from LLC layer, according to a -EBUSY return + * value on latest try to send I-Frame to a LLC connection. This primitive + * informs NetBEUI to flush its link queue to LLC connection. + * Parameters: + * prim: pointer to primary interface block type, the LLC communication + * data structure. This is a union with different fields with + * different meaning for different purposes. + * Returns: + * 0 : if all queue contents are flushed successfully. + * non-Zero: if LLC does not accept all entries and queue has more entries + * to flush. + * Note: + * - Since this function is raised via LLC, the return value has special + * meaning to both NetBEUI and LLC to sync their flags. The llc_busy + * flag in NetBEUI link demonstrates existence of skbs in link queue + * ready to send to LLC layer. + */ +static int nbll_get_data_confirm(struct llc_prim_if_block *prim) +{ + struct llc_prim_data *prim_data = &prim->data->data; + link_t *nb_link = link_table_entry(prim_data->link); + struct sk_buff *skb; + int rc = -EBUSY; + + printk(KERN_INFO __FUNCTION__ ": enter\n"); + nbll_link_hold(nb_link); + while ((skb = __skb_dequeue(&nb_link->skbq)) != NULL) + if (nbll_isend_link(nb_link->dev, nb_link->llc_handle, + skb, prim_data->link, 1) == 1) + goto out; + rc = nb_link->llc_busy = 0; +out: nbll_link_put(nb_link); + return rc; +} + +/* + * Function: nbll_get_disconnect_indicate + * Accepts a disconnection indication for a connection from LLC layer. + * Parameters: + * prim: pointer to primary interface block type, the LLC communication + * data structure. This is a union with different fields with + * different meaning for different purposes. + * Returns: none + */ +static void nbll_get_disconnect_indicate(struct llc_prim_if_block *prim) +{ + link_t *nb_link = link_table_entry(prim->data->disc.link); + nbll_link_hold(nb_link); + nbll_handle_event(NETBEUI_LINK_DISC_INDICATE, nb_link); + nbll_link_put(nb_link); + nbll_link_put(nb_link); +} + +/* + * Function: nbll_get_disconnect_confirm + * Accepts a disconnection confirm for a previous disconnect request, from + * LLC layer. + * Parameters: + * prim: pointer to primary interface block type, the LLC communication + * data structure. This is a union with different fields with + * different meaning for different purposes. + * Returns: none + * Note: + * - Currently LLC does not generate a disconnection confirm. Both NetBEUI + * and LLC assume that a disconnection request is always satisfied. + */ +static void nbll_get_disconnect_confirm(struct llc_prim_if_block *prim) +{ + /* Nothing to do, we do not wait for disconnection confirmation */ +} + +/* + * Function: nbll_get_reset_indicate + * Accepts a reset indication for a connection from LLC layer. + * Parameters: + * prim: pointer to primary interface block type, the LLC communication + * data structure. This is a union with different fields with + * different meaning for different purposes. + * Returns: none + * Note: + * - LLC reset indication means close all sessions on link and reset + * link parameters. Just the link itself remains UP and RUNNING. + */ +static void nbll_get_reset_indicate(struct llc_prim_if_block *prim) +{ + link_t *nb_link = link_table_entry(prim->data->res.link); + + nbll_link_hold(nb_link); + nbll_handle_event(NETBEUI_LINK_RESET_INDICATE, nb_link); + nb_link->llc_busy = 0; + nb_link->iactivity = 0; + skb_queue_purge(&nb_link->skbq); + nbll_link_put(nb_link); +} + +/* + * Function: get_link_table + * returns a pointer to NetBEUI link table. The proc support code uses + * the link table to map its contents to /proc/sys/netbeui entry. + * Parameters: none + * Returns: + * non-NULL: pointer to NetBEUI link table + */ +dextab_t *nbll_get_link_table(void) +{ + return &link_table; +} + +link_t *nbll_get_link(int link) +{ + link_t *nb_link = NULL; + + spin_lock(&link_table.lock); + if (link <= link_table.size) + nb_link = link_table_entry(link); + if (nb_link) + nbll_link_hold(nb_link); + spin_unlock(&link_table.lock); + return nb_link; +} + +/* + * Function: nbll_attach_session + * This is a sophisticated interface to session service module, which + * attaches (links) a session to a link. Depending on the existence and + * state of the link, it interacts with LLC and manipulates link session + * table to content the request. + * Parameters: + * session : pointer to session_t structure to add to session table of + * link + * dev : pointer to device structure the link to remote node is on + * remote_mac: pointer to MAC address of remote node the link is to + * Returns: + * 0 : if session is successfully added to link session table + * -ENOSPC : if link session table has no space to add session + * -ENOMEM : if can not allocate memory for creating new link + * -EHOSTUNREACH: if LLC connection request for a new link failed. + */ +int nbll_attach_session(session_t *session, struct net_device *dev, + unsigned char *remote_mac) +{ + link_t *nb_link = nbll_find_link(dev, remote_mac); + int session_no; + int rc; + + /* If a link exists previously and is up or requested within an + interrupt */ + if (nb_link) { + session_no = dextab_insert_entry(&nb_link->session_table, + session); + rc = -ENOSPC; + if (session_no < 0) + goto plink; + session = (session_t *)nb_link->session_table.addr[session_no]; + session->link = nb_link->link; + session->lsn = session_no; + rc = 0; + goto plink; + } + /* Request for link (dummy) */ + nb_link = nbll_alloc_link(); + rc = -ENOMEM; + if (!nb_link) + goto out; + nbll_link_hold(nb_link); + nb_link->llc_handle = 0; + nb_link->dev = dev; + memcpy(nb_link->remote_mac, remote_mac, dev->addr_len); + nbll_handle_event(NETBEUI_LINK_DUMMY_CONN, nb_link); + rc = -EHOSTUNREACH; + if (nb_link->state == NETBEUI_LINK_INITIAL) { + nbll_link_put(nb_link); + goto plink; + } + rc = nbll_attach_session(session, dev, remote_mac); +plink: nbll_link_put(nb_link); +out: return rc; +} + +/* + * Function: nbll_link_session + * This is a joke !!!!! + * NetBEUI connection request has two steps NAME QUERY and LLC CONNECTION + * establishment. To overcome some traditional limits in total session + * count new implementations add a NAME FIND step before the other two + * steps, thus letting the other two be used interchangeably. + * Unfortunately Microsoft implementations force using NAME QUERY before + * LLC CONNECTION. To overcome the problem we first put a link into + * CONNWAIT, attach a session to it and first issue NAME QUERY then + * request LLC CONNECTION. This function does the actual LLC connection + * request on the dummy link. + * Parameters: + * link : an integer representing link number in link table + * Returns: + * 0 : if dummy link is now established successfully. + * -ECONNRESET : if dummy link is reset + * -EHOSTUNREACH: if LLC connection establishment failed. + */ +int nbll_link_session(int link) +{ + link_t *nb_link; + int rc = -ECONNRESET; + + spin_lock(&link_table.lock); + nb_link = link_table_entry(link); + if (!nb_link) { + spin_unlock(&link_table.lock); + goto out; + } + nbll_link_hold(nb_link); + spin_unlock(&link_table.lock); + nbll_handle_event(NETBEUI_LINK_CONN_REQUEST, nb_link); + rc = -EHOSTUNREACH; + if (nb_link->state == NETBEUI_LINK_INITIAL) { + nbll_link_put(nb_link); + goto plink; + } + rc = 0; +plink: nbll_link_put(nb_link); +out: return rc; +} + +/* + * Function: nbll_detach_session + * Detaches a session from its link session table. If the session was the + * last session link, it tries to drop the link to free resources. + * Parameters: + * link : an integer representing link number in link table + * session_no: an integer representing session number in link session table + * Returns: none + */ +void nbll_detach_session(int link, unsigned char session_no) +{ + session_t *session; + link_t *nb_link; + + spin_lock(&link_table.lock); + nb_link = link_table_entry(link); + if (!nb_link) { + spin_unlock(&link_table.lock); + goto out; + } + nbll_link_hold(nb_link); + spin_unlock(&link_table.lock); + session = (session_t *)nb_link->session_table.addr[session_no]; + if (session) + session->lsn = 0; + __dextab_delete_index(&nb_link->session_table, session_no); + if (dextab_count_entries(&nb_link->session_table) > 0) + goto plink; + nbll_handle_event(NETBEUI_LINK_DISC_REQUEST, nb_link); + nbll_link_put(nb_link); +plink: nbll_link_put(nb_link); +out:; +} + +/* + * Function: nbll_drop_link + * Drops a specified link with its sessions. + * Parameters: + * link: an integer representing link number in link table. + * Returns: int + * zero : if the link dropped successfully. + * negative: if operation fails. + * -EINVAL: the link number is invalid. + * Notes: + * - Only called from user context, in config.c, ioctl path + */ +int nbll_drop_link(int link) +{ + link_t *nb_link = nbll_get_link(link); + dextab_t *sn_tbl; + int i, sn_cnt, rc = -EINVAL; + + if (!nb_link) /* Invalid link number */ + goto out; + sn_tbl = &nb_link->session_table; + spin_lock_bh(&sn_tbl->lock); + rc = 0; + sn_cnt = sn_tbl->count; + for (i = sn_tbl->reserved; sn_cnt; i++) { + session_t *sn = sn_tbl->addr[i]; + + if (sn) { + nbll_detach_session(link, i); + nbss_abort_session(sn); + sn_cnt--; + } + } + spin_unlock_bh(&sn_tbl->lock); + nbll_link_put(nb_link); +out: return rc; +} + +/* + * LLC interface functions + * Data-in functions + */ +/* + * Function: nbll_disconnect_all_links + * Generates disconnection event on all NetBEUI links. + * Parameters: none + * Returns: none + */ +static void nbll_disconnect_all_links(void) +{ + int index; + link_t *nb_link; + + spin_lock(&link_table.lock); + for (index = link_table.reserved; index < link_table.size; index++) { + nb_link = link_table_entry(index); + spin_unlock(&link_table.lock); + if (nb_link) { + nbll_handle_event(NETBEUI_LINK_DISC_REQUEST, nb_link); + nbll_link_put(nb_link); + } + spin_lock(&link_table.lock); + } + spin_unlock(&link_table.lock); +} + +/* + * Function: nbll_deliver_packet + * While checking input I-Frame consistency, routes frame to session + * service interface. Since manipulating I-Frames is always the + * responsibility of session service, the dispatching mechanism is + * implemented there. + * Parameters: + * skb : sk_buff which holds actual data + * prim_data: pointer to LLC primitive data description. + * Returns: none + * Notes: + * - Imagine struct llc_prim_if_block is the function that LLC calls in + * NetBEUI code and union llc_u_prim_data as argument to this function. + * - This function is a barrier for ill frames with anomalous content. + */ +static void nbll_deliver_packet(struct sk_buff *skb, + struct llc_prim_data *prim_data) +{ + int command = ((packet_t *)(skb->data))->command; + link_t *nb_link = link_table_entry(prim_data->link); + session_t *session; + + nbll_link_hold(nb_link); + /* Sanity check if command length and code is valid, all checks should + * be done! */ + /* It checks ill netbios headers, and prevents memory faults */ + if (skb->len < NETBEUI_MIN_COMMAND_LEN || + command > NETBEUI_MAX_COMMAND_CODE || + skb->len < nb_cmd_hdr_len[command] || + ((packet_t *)skb->data)->delimiter != NETBEUI_DELIMITER || + ((packet_t *)skb->data)->dest_num >= nb_link->session_table.size) + goto err; + nb_link->iactivity++; + session = (session_t *) + nb_link->session_table.addr[((packet_t *)skb->data)->dest_num]; + if (!session || command == NETBEUI_SESSION_ALIVE) + goto err; + nbss_deliver_frame(session, skb); +out: nbll_link_put(nb_link); + return; +err: kfree_skb(skb); + goto out; +} + +/* + * Function: nbll_deliver_datagram + * The UI-Frame dispatcher + * While checking input UI-Frame consistency, routes frame to final + * destination in name service, session service and datagram distribution. + * Due to different destinations the dispatching mechanism for UI-Frames is + * implemented here in this function. + * Parameters: + * skb : sk_buff which holds actual data + * prim_data: pointer to LLC primitive data description. + * Returns: none + */ +static void nbll_deliver_datagram(struct sk_buff *skb, + struct llc_prim_unit_data *prim_data) +{ + int command = ((dgram_t *)(skb->data))->command; + + /* Sanity check if command length and code is valid, all checks + * should be done !!! */ + /* It checks ill netbios headers, and prevents memory faults */ + if (skb->len < NETBEUI_MIN_COMMAND_LEN || + command > NETBEUI_MAX_COMMAND_CODE || + skb->len < nb_cmd_hdr_len[command] || + ((dgram_t *)skb->data)->delimiter != NETBEUI_DELIMITER) + goto fskb; + /* Token Ring support */ + skb->cb[0] = prim_data->lfb; + + switch (command) { + case NETBEUI_ADD_GROUP_NAME_QUERY: + nbns_get_add_name_query(skb, prim_data->saddr.mac, + NETBEUI_NAME_GROUP); + break; + case NETBEUI_ADD_NAME_QUERY: + nbns_get_add_name_query(skb, prim_data->saddr.mac, + NETBEUI_NAME_UNIQUE); + break; + case NETBEUI_ADD_NAME_RESPONSE: + nbns_get_add_name_response(skb, prim_data->saddr.mac); + break; + case NETBEUI_NAME_IN_CONFLICT: + nbns_get_name_conflict(skb); + break; + case NETBEUI_NAME_QUERY: + nbss_get_name_query(skb, prim_data->saddr.mac); + break; + case NETBEUI_NAME_RECOGNIZED: + nbqs_get_name_recognized(skb, prim_data->saddr.mac); + break; + case NETBEUI_DATAGRAM: + nbdg_get_datagram(skb); + break; + case NETBEUI_DATAGRAM_BROADCAST: + nbdg_get_datagram_broadcast(skb); + break; + case NETBEUI_STATUS_QUERY: + nbst_get_status_query(skb, prim_data->saddr.mac); + break; + case NETBEUI_STATUS_RESPONSE: + nbst_get_status_response(skb, prim_data->saddr.mac); + break; + case NETBEUI_TERMINATE_TRACE: + case NETBEUI_TERMINATE_TRACE2: + goto fskb; + } +out: return; +fskb: kfree_skb(skb); + goto out; +} + +/** + * netbeui_indicate - The callback for LLC indicate primitive dispatching + * @prim: primary interface block type, the LLC comm data structure. + * + * It dispatches LLC primitive to Link Service State Machine interfaces. + * Note: LLC flow control indication is not generated. We have used data + * request/confirm to control flow. Think if it is a misconception or is + * accepted anyway. + */ +static int netbeui_indicate(struct llc_prim_if_block *prim) +{ + struct sk_buff *skb = NULL; + + switch (prim->prim) { + case LLC_DATAUNIT_PRIM: + skb = prim->data->udata.skb; + nbll_deliver_datagram(skb, &prim->data->udata); + break; + case LLC_CONN_PRIM: + nbll_get_connect_indicate(prim); + break; + case LLC_DATA_PRIM: + skb = prim->data->data.skb; + nbll_deliver_packet(skb, &prim->data->data); + break; + case LLC_DISC_PRIM: + nbll_get_disconnect_indicate(prim); + break; + case LLC_RESET_PRIM: + nbll_get_reset_indicate(prim); + break; + case LLC_FLOWCONTROL_PRIM: + break; + } + return 0; +} + +/** + * netbeui_confirm - callback for LLC confirm primitive. + * @prim: primary interface block type, the LLC comm data structure. + * + * The callback for LLC confirm primitive dispatching. It dispatches LLC + * primitive to Link Service State Machine interfaces. + * Note: LLC reset confirm is not generated, since we never generate + * reset request + */ +static int netbeui_confirm(struct llc_prim_if_block *prim) +{ + int status = 0; + + switch (prim->prim) { + case LLC_CONN_PRIM: + nbll_get_connect_confirm(prim); break; + case LLC_DATA_PRIM: + status = nbll_get_data_confirm(prim); break; + case LLC_DISC_PRIM: + nbll_get_disconnect_confirm(prim); break; + case LLC_RESET_PRIM: break; + } + return status; +} + +static char banner[] __initdata = + KERN_INFO "NetBEUI 2.0 by Procom, 1997, Arnaldo C. Melo, 2001\n"; +static char err_sap[] __initdata = + KERN_ERR "Error opening NetBIOS SAP 0x%X on LLC.\n"; +static char err_socket[] __initdata = + KERN_ERR "Error registering NetBEUI socket.\n"; +static char err_proc[] __initdata = + KERN_INFO "Error registering NetBEUI proc entries.\n"; + +/** + * netbeui_init - The starting point of NetBEUI module. + * + * Since NetBEUI is distributed as a module, netbeui_init does all initial + * steps itself. Returns 0 if module initialized successfully, non-zero + * if a step in initialization failed (mostly opening LLC sap) + */ +static int __init netbeui_init(void) +{ + int rc = 0; + + printk(banner); + netbeui_sap = llc_sap_open(netbeui_indicate, netbeui_confirm, + LLC_SAP_NETBEUI); + if (!netbeui_sap) { + printk(err_sap); + goto err; + } + write_lock(&netbeui_adapters.lock); + netbeui_adapters.autobind = autobind; + if (autobind) { + int i = 0; + struct net_device *dev; + + read_lock(&dev_base_lock); + for (dev = dev_base; dev && i < NETBEUI_MAX_ADAPTERS; + dev = dev->next) + if (nbcm_apt_dev(dev)) { + dev_hold(dev); + netbeui_adapters.dev[i++] = dev; + dev_mc_add(dev, netbeui_funcaddr(dev), + dev->addr_len, 0); + } + read_unlock(&dev_base_lock); + netbeui_adapters.count = i; + } + nbns_init_name_number_1(netbeui_adapters.dev); + write_unlock(&netbeui_adapters.lock); + nbdg_set_dgbc_mtu(); + + if (nbso_init()) { + printk(err_socket); + goto err_sap; + } + if (netbeui_proc_init()) { + printk(err_proc); + goto err_nbso; + } + nbst_init_status(); +out: return rc; +err_nbso: + nbso_exit(); +err_sap: + llc_sap_close(netbeui_sap); +err: rc = -1; + goto out; +} + +static char err_exit[] __exitdata = + KERN_ERR "Error unregistering NetBEUI socket.\n"; + +/** + * netbeui_exit - The ending point of NetBEUI module. + * + * Since NetBEUI is distributed as a module, netbeui_exit does all + * housekeeping steps itself + */ +static void __exit netbeui_exit(void) +{ + int i; + + nbll_disconnect_all_links(); + read_lock(&netbeui_adapters.lock); + for (i = 0; i < netbeui_adapters.count; i++) { + dev_mc_delete(netbeui_adapters.dev[i], + netbeui_funcaddr(netbeui_adapters.dev[i]), + netbeui_adapters.dev[i]->addr_len, 0); + dev_put(netbeui_adapters.dev[i]); + } + read_unlock(&netbeui_adapters.lock); + if (nbso_exit()) + printk(err_exit); + netbeui_proc_clean(); + llc_sap_close(netbeui_sap); +} +module_init(netbeui_init); +module_exit(netbeui_exit); Index: kernel-acme/net/netbeui/name_serve.c diff -u /dev/null kernel-acme/net/netbeui/name_serve.c:1.1.8.1 --- /dev/null Thu Nov 22 23:55:00 2001 +++ kernel-acme/net/netbeui/name_serve.c Tue Nov 6 20:13:49 2001 @@ -0,0 +1,898 @@ +/* + * name_serve.c - Contains functions that implement NetBIOS Name Service + * + * Notes: + * - Two intermixed structures hold local name table a dextab_t that is + * used for assigning numbers to names and implicit name rule checking + * and a unidirectional linked list which is used for name search. every + * name entry may be found in both data structures. + * + * Copyright (c) 1997 by Procom Technology, Inc. + * 2001 by Arnaldo Carvalho de Melo + * + * 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 +#include +#include +#include +#include +#include +#include + +static void nbns_timer_function(unsigned long input); + +/* These functions are Name State Transition handlers */ +static int nbns_add_name_in_initial(name_t *nb_name); +static int nbns_retry_timeout_in_all(name_t *nb_name); +static int nbns_response_timeout_in_addwait(name_t *nb_name); +static int nbns_add_name_response1_in_addwait(name_t *nb_name); +static int nbns_add_name_query_in_addwait(name_t *nb_name); +static int nbns_response_timeout_in_collided(name_t *nb_name); +static int nbns_add_name_response2_in_collided(name_t *nb_name); +static int nbns_add_name_query_in_acquired(name_t *nb_name); +static int nbns_name_conflict_in_acquired(name_t *nb_name); +static int nbns_remove_name_in_acquired(name_t *nb_name); + +static void nbns_handle_event(name_event_t event, name_t *nb_name); +static unsigned short int nbns_correlator; +#define nbns_next_correlator() (++nbns_correlator) + +/* We need to have two paths to access names from dextab and linked-list */ +static name_t *name_list; +static rwlock_t name_list_lock = RW_LOCK_UNLOCKED; +dextab_t name_table = { + reserved: 2, + max_size: NETBEUI_MAX_NAMES, + lock: SPIN_LOCK_UNLOCKED +}; +static name_t name_number_1; + +#define name_table_entry(i) ((name_t *)name_table.addr[i]) + +/* Name Service State Machine definition */ +typedef int (* name_event_handler_t)(name_t *); + +struct event_struct { + name_state_t next_state; + name_event_handler_t event_handler; +}; +static struct event_struct name_state_table[4][8] = { + /* NETBEUI_NAME_INITIAL */ +{ +{ NETBEUI_NAME_ADDWAIT, + nbns_add_name_in_initial }, /* NETBEUI_NAME_ADD_NAME */ +{ -1, NULL }, /* NETBEUI_NAME_RETRY_TIMEOUT */ +{ -1, NULL }, /* NETBEUI_NAME_RESPONSE_TIMEOUT */ +{ -1, NULL }, /* NETBEUI_NAME_ADD_NAME_RESPONSE1 */ +{ -1, NULL }, /* NETBEUI_NAME_ADD_NAME_RESPONSE2 */ +{ -1, NULL }, /* NETBEUI_NAME_ADD_NAME_QUERY */ +{ -1, NULL }, /* NETBEUI_NAME_NAME_CONFLICT */ +{ -1, NULL } /* NETBEUI_NAME_REMOVE_NAME */ +}, + /* NETBEUI_NAME_ADDWAIT */ +{ +{ -1, NULL }, /* NETBEUI_NAME_ADD_NAME */ +{ NETBEUI_NAME_ADDWAIT, + nbns_retry_timeout_in_all }, /* NETBEUI_NAME_RETRY_TIMEOUT */ +{ NETBEUI_NAME_ACQUIRED, + nbns_response_timeout_in_addwait }, /* NETBEUI_NAME_RESPONSE_TIMEOUT */ +{ NETBEUI_NAME_COLLIDED, + nbns_add_name_response1_in_addwait }, /* NETBEUI_NAME_ADD_NAME_RESPONSE1 */ +{ -1, NULL }, /* NETBEUI_NAME_ADD_NAME_RESPONSE2 */ +{NETBEUI_NAME_ADDWAIT, + nbns_add_name_query_in_addwait }, /* NETBEUI_NAME_ADD_NAME_QUERY */ +{ -1, NULL }, /* NETBEUI_NAME_NAME_CONFLICT */ +{ -1, NULL } /* NETBEUI_NAME_REMOVE_NAME */ +}, + /* NETBEUI_NAME_COLLIDED */ +{ +{ -1, NULL }, /* NETBEUI_NAME_ADD_NAME */ +{ NETBEUI_NAME_COLLIDED, + nbns_retry_timeout_in_all }, /* NETBEUI_NAME_RETRY_TIMEOUT */ +{ NETBEUI_NAME_INITIAL, + nbns_response_timeout_in_collided }, /* NETBEUI_NAME_RESPONSE_TIMEOUT */ +{ -1, NULL }, /* NETBEUI_NAME_ADD_NAME_RESPONSE1 */ +{ NETBEUI_NAME_INITIAL, + nbns_add_name_response2_in_collided }, /* NETBEUI_NAME_ADD_NAME_RESPONSE2 */ +{ -1, NULL }, /* NETBEUI_NAME_ADD_NAME_QUERY */ +{ -1, NULL }, /* NETBEUI_NAME_NAME_CONFLICT */ +{ -1, NULL } /* NETBEUI_NAME_REMOVE_NAME */ +}, + /* NETBEUI_NAME_ACQUIRED */ +{ +{ -1, NULL }, /* NETBEUI_NAME_ADD_NAME */ +{ -1, NULL }, /* NETBEUI_NAME_RETRY_TIMEOUT */ +{ -1, NULL }, /* NETBEUI_NAME_RESPONSE_TIMEOUT */ +{ -1, NULL }, /* NETBEUI_NAME_ADD_NAME_RESPONSE1 */ +{ -1, NULL }, /* NETBEUI_NAME_ADD_NAME_RESPONSE2 */ +{ NETBEUI_NAME_ACQUIRED, + nbns_add_name_query_in_acquired }, /* NETBEUI_NAME_ADD_NAME_QUERY */ +{ NETBEUI_NAME_ACQUIRED, + nbns_name_conflict_in_acquired }, /* NETBEUI_NAME_NAME_CONFLICT */ +{ NETBEUI_NAME_INITIAL, + nbns_remove_name_in_acquired }, /* NETBEUI_NAME_REMOVE_NAME */ +} +}; + +/* NAME_NUMBER_1 assignment and interface functions */ +/* + * Function: nbns_dev_name_number_1 + * Returns the name number of built by device MAC address + * Parameters: + * dev: pointer to device structure to build its NAME_NUMBER_1 + * Returns: + * non-NULL: pointer to NAME_NUMBER_1 built by device MAC address + * Note: + * NAME_NUMBER_1 is defined as (16-n) bytes zero concatenated by (n) + * bytes of MAC address, where n is size of MAC address in bytes. + */ +unsigned char *nbns_dev_name_number_1(struct net_device *dev) +{ + static char dev_name_number_1[NETBEUI_NAME_LEN]; + int zero_count = NETBEUI_NAME_LEN - dev->addr_len; + + memset(dev_name_number_1, 0, zero_count); + memcpy(dev_name_number_1 + zero_count, dev->dev_addr, dev->addr_len); + return dev_name_number_1; +} + +/* + * Function: nbns_validate_name + * Checks a NetBIOS name validity according to NetBIOS name rules. + * Parameters: + * name: pointer to NetBIOS name + * Returns: + * 0 : if name is a valid NetBIOS name + * non-zero: if name is not a valid NetBIOS name + */ +int nbns_validate_name(char *name) +{ + return (!memcmp(name, "IBM", 3) || + memchr(name, '\0', NETBEUI_NAME_LEN - 1)) ? -1 : 0; +} + +/* + * Function: nbns_init_name_number_1 + * Finds a correct value for NAME_NUMBER_1 + * Parameters: + * adapters: list of adapters the NetBEUI is bound to + * Returns: None + * Notes: + * - A NetBEUI implementation should provide a unique NAME_NUMBER_1. + * Since our implementations supports both multiple interface and + * loopback device, we need to decide on the device that is used + * for generating NAME_NUMBER_1. + */ +void nbns_init_name_number_1(struct net_device *adapters[]) +{ + name_number_1.type = NETBEUI_NAME_UNIQUE; + name_number_1.state = NETBEUI_NAME_ACQUIRED; + name_number_1.name_number = 1; + + if (adapters[0] && !(adapters[0]->flags & IFF_LOOPBACK)) { + memcpy(name_number_1.name, nbns_dev_name_number_1(adapters[0]), + NETBEUI_NAME_LEN); + goto out; + } + if (adapters[1]) { + memcpy(name_number_1.name, nbns_dev_name_number_1(adapters[1]), + NETBEUI_NAME_LEN); + goto out; + } + if (adapters[0]) { + memcpy(name_number_1.name, "LOOPBACK DEVICE ", + NETBEUI_NAME_LEN); + goto out; + } + name_number_1.name_number = -1; +out:; +} + +/* + * Function: nbns_name_number_1 + * The Name Service interface access routine for NAME_NUMBER_1 + * Parameters: None + * Returns: + * non-NULL: pointer to name_t structure that holds NAME_NUMBER_1 + */ +name_t *nbns_name_number_1(void) +{ + return name_number_1.name_number == 1 ? &name_number_1 : NULL; +} + +/* + * Name service state machine functions + * Implementing general functions + */ +/* + * Function: nbns_alloc_skb + * allocates a sk_buff that suits Name Service framing needs + * Parameters: + * data_len: number of data bytes that the calling routine intends + * to put in skb + * Returns: + * NULL : if can not allocate memory for skb + * non-NULL: pointer to skb allocated with data_len bytes space for data + */ +static struct sk_buff *nbns_alloc_skb(int mac_hlen, int data_len) +{ + struct sk_buff *skb = alloc_skb(CALC_DG_SKBLEN(mac_hlen, data_len), + GFP_ATOMIC); + if (!skb) + goto out; + skb_reserve(skb, LLCMAC_UI_HEADLEN(mac_hlen)); + skb->nh.raw = skb->h.raw = skb->data; + skb_put(skb, data_len); + skb->dev = NULL; +out: return skb; +} + +/* + * Function: nbns_alloc_name + * Allocates a name_t structure and does completely initialize all fields + * Parameters: None + * Returns: + * NULL : if can not allocate memory for name_t or its sk_buff + * non-NULL: pointer to name_t + * Notes: + * - An skb is attached to each new name for name registration purposes + * - Name timer is initialized but not started. + * - The call to memset does implicitly initialize all fields. Those fields + * that need explicit non-zero initialization are manipulated afterwards. + */ +static name_t *nbns_alloc_name(void) +{ + name_t *nb_name = kmalloc(sizeof(*nb_name), GFP_ATOMIC); + + if (!nb_name) + goto out; + /* Implicitly initialize all fields */ + memset(nb_name, 0, sizeof(*nb_name)); + init_timer(&nb_name->timer); + nb_name->timer.data = (unsigned long)nb_name; + nb_name->timer.function = nbns_timer_function; + init_waitqueue_head(&nb_name->waitq); + atomic_set(&nb_name->refcnt, 1); + /* Allocate name skb */ + nb_name->skb = nbns_alloc_skb(NETBEUI_MAC_B_HEADLEN, + nb_cmd_hdr_len[NETBEUI_ADD_NAME_QUERY]); + if (!nb_name->skb) { + kfree(nb_name); + nb_name = NULL; + } +out: return nb_name; +} + +/* + * Function: nbns_free_name + * Deallocates memory used for name_t and its sk_buff + * Parameters: + * nb_name: pointer to name_t memory to be freed + * Returns: None + */ +static void __nbns_free_name(name_t *nb_name) +{ + kfree_skb(nb_name->skb); + kfree(nb_name); +} + +static void nbns_free_name(name_t *nb_name) +{ + nbns_handle_event(NETBEUI_NAME_REMOVE_NAME, nb_name); + __nbns_free_name(nb_name); +} + +inline void nbns_name_hold(name_t *nb_name) +{ + atomic_inc(&nb_name->refcnt); +} + +inline void nbns_name_put(name_t *nb_name) +{ + if (atomic_dec_and_test(&nb_name->refcnt)) + nbns_free_name(nb_name); +} + +/* + * Function: nbns_add_name_to_table + * Inserts a previously allocated/initialized name_t into system local name + * table and local name list. + * Parameters: + * nb_name: pointer to name_t to insert + * Returns: + * 0 : if name is successfully inserted into name table + * -ENOSPC: if local name table is full + */ +static int nbns_add_name_to_table(name_t *nb_name) +{ + /* Allocate a name table entry */ + int name_number = dextab_insert_entry(&name_table, nb_name); + + if (name_number <= 1) + return -ENOSPC; + nb_name->name_number = name_number; + /* Add name to name list */ + write_lock_bh(&name_list_lock); + nb_name->next = name_list; + name_list = nb_name; + write_unlock_bh(&name_list_lock); + return 0; +} + +/* + * Function: nbns_remove_name_from_table + * Removes a name from system local name table. + * Parameters: + * nb_name: pointer to name_t to remove + * Returns: None + */ +static void nbns_remove_name_from_table(name_t *nb_name) +{ + name_t *entry; + name_t *prev_entry = NULL; + + dextab_delete_entry(&name_table, nb_name); + write_lock_bh(&name_list_lock); + entry = name_list; + while (entry) { + if (entry == nb_name) { + if (prev_entry) + prev_entry->next = entry->next; + else + name_list = entry->next; + break; + } + prev_entry = entry; + entry = entry->next; + } + write_unlock_bh(&name_list_lock); +} + +/* + * Function: nbns_find_correlator + * Finds a name in local name list, which has a specific NetBIOS name + * and has transmitted frames with a specific correlator + * Parameters: + * correlator: a sixteen bit integer which contains the response + * correlator of input frame. It should be matched against + * xmit correlator of frames sent by the element. + * name : pointer to NetBIOS name that the requested element should + * have + * Returns: + * NULL : if no name_t element found with the requested characteristics + * non-NULL: pointer to matching name_t with requested characteristics + * Notes: + * - This routine is useful for relating incoming response frames with + * name elements that have transmitted requests to remote nodes. + */ +static name_t *nbns_find_correlator(unsigned short correlator, char *name) +{ + name_t *nb_name; + + read_lock_bh(&name_list_lock); + nb_name = name_list; + while (nb_name) + if (nb_name->resp_correlator == correlator && + nb_name->state != NETBEUI_NAME_ACQUIRED && + !memcmp(nb_name->name, name, NETBEUI_NAME_LEN)) + break; + else + nb_name = nb_name->next; + if (nb_name) + nbns_name_hold(nb_name); + read_unlock_bh(&name_list_lock); + return nb_name; +} + +/* + * Function: nbns_boradcast_add_name_query + * Prepares a NetBIOS ADD NAME QUERY frame and nbll_uisends it to network + * Parameters: + * nb_name: pointer to name_t structure which the frame should be built for + * Returns: + * 0 : if frame is successfully broadcasted to network + * non-zero: if frame transmission encountered an error (usually at NDI + * layer) + * Notes: + * - Since ADD NAME QUERY frames are retransmitted in timed intervals, it + * is considered to build frame once, but transmit it multiple times. + * having built frames in each retransmission does generate multiple + * correlators and does frustrate processing responses. + */ +static int nbns_broadcast_add_name_query(name_t *nb_name) +{ + if (!nb_name->retries) { + dgram_t *hdr = (dgram_t *)nb_name->skb->data; + + hdr->length = nb_cmd_hdr_len[NETBEUI_ADD_NAME_QUERY]; + hdr->delimiter = NETBEUI_DELIMITER; + hdr->command = nb_name->type == NETBEUI_NAME_UNIQUE ? + NETBEUI_ADD_NAME_QUERY : + NETBEUI_ADD_GROUP_NAME_QUERY; + hdr->data1 = 0; + hdr->data2 = 0; + hdr->xmit_correlator = 0; + hdr->resp_correlator = nb_name->resp_correlator = + nbns_next_correlator(); + memset(hdr->dest_name, 0, NETBEUI_NAME_LEN); + memcpy(hdr->source_name, nb_name->name, NETBEUI_NAME_LEN); + } + return nbll_uisend(NULL, nb_name->skb); +} + +/* + * Function: nbns_broadcast_name_in_conflict + * Prepares a NetBIOS NAME IN CONFLICT frame and nbll_uisends it to network + * Parameters: + * nb_name: pointer to name_t structure which the frame should be built for + * Returns: + * 0 : if frame is successfully broadcasted to network + * non-zero: if frame transmission encountered an error (usually at NDI + * layer) + */ +static int nbns_broadcast_name_in_conflict(name_t *nb_name) +{ + dgram_t *hdr = (dgram_t *)nb_name->skb->data; + + hdr->length = nb_cmd_hdr_len[NETBEUI_NAME_IN_CONFLICT]; + hdr->delimiter = NETBEUI_DELIMITER; + hdr->command = NETBEUI_NAME_IN_CONFLICT; + hdr->data1 = hdr->data2 = 0; + hdr->xmit_correlator = hdr->resp_correlator = 0; + memcpy(hdr->dest_name, nb_name->name, NETBEUI_NAME_LEN); + memcpy(hdr->source_name, nbns_name_number_1()->name, NETBEUI_NAME_LEN); + return nbll_uisend(NULL, nb_name->skb); +} + +/* + * Function: nbns_unicast_add_name_response + * Prepares a NetBIOS ADD NAME RESPONSE frame and nbll_uisends it to + * network + * Parameters: + * nb_name: pointer to name_t structure which the frame should be built for + * Returns: + * 0 : if frame is successfully broadcasted to network + * non-zero: if frame transmission encountered an error (usually at NDI + * layer) or it can not allocate memory for frame. + * Note: + * - The lack of memory and inability to send response frame may cause a + * name to conflict on network. This is simply the case on a heavily + * loaded server, however since the query is retransmitted multiple times + * we hope the server would be able to defend its name at least once. In + * addition if an administrator finds the case he can increase either + * retransmissions or timeouts for all nodes on network. + */ +static int nbns_unicast_add_name_response(name_t *nb_name) +{ + dgram_t *hdr; + struct sk_buff *skb = nbns_alloc_skb(MAC_HEADLEN(nb_name->dev), + nb_cmd_hdr_len[NETBEUI_ADD_NAME_RESPONSE]); + if (!skb) + return -ENOMEM; + hdr = (dgram_t *)skb->data; + hdr->length = nb_cmd_hdr_len[NETBEUI_ADD_NAME_RESPONSE]; + hdr->delimiter = NETBEUI_DELIMITER; + hdr->command = NETBEUI_ADD_NAME_RESPONSE; + hdr->data1 = nb_name->state == NETBEUI_NAME_ACQUIRED ? 0 : 1; + hdr->data2 = nb_name->type; + hdr->xmit_correlator = nb_name->xmit_correlator; + hdr->resp_correlator = 0; + memcpy(hdr->source_name, nb_name->name, NETBEUI_NAME_LEN); + memcpy(hdr->dest_name, nb_name->name, NETBEUI_NAME_LEN); + skb->dev = nb_name->dev; + return nbll_uisend(nb_name->remote_mac, skb); +} + +/* + * Function: nbns_handle_event + * This is the heart of Name Service State Machine, which performs a + * transition from current state of name element to new state based + * on event occurred and name state table contents. + * Parameters: + * event : An integer of NETBEUI_NAME_* family that implies type of event + * nb_name: pointer to name_t structure which the event occurred on + * Returns: None + * Notes: + * - The state changes before actions be executed. This is due to + * non deterministic behavior of actions which may sleep the current + * process, thus stopping the function in the mid-way. + */ +static void nbns_handle_event(name_event_t event, name_t *nb_name) +{ + struct event_struct *ev = &name_state_table[nb_name->state][event]; + + if (ev && ev->event_handler) { + unsigned char old_state = nb_name->state; + + nb_name->state = ev->next_state; + if (ev->event_handler(nb_name)) + nb_name->state = old_state; + } +} + +/* + * Function: nbns_timer_function + * This is the callback function triggered upon expiration of name + * retransmittion timer. It just injects an event into state machine for + * its link. + * Parameters: + * input: pointer to name_t structure whose timer is expired. + * Returns: None + */ +static void nbns_timer_function(unsigned long input) +{ + name_t *nb_name = (name_t *)input; + + if (nb_name->retries < NETBEUI_TRANSMIT_COUNT) + nbns_handle_event(NETBEUI_NAME_RETRY_TIMEOUT, nb_name); + else + nbns_handle_event(NETBEUI_NAME_RESPONSE_TIMEOUT, nb_name); +} + +/* + * Name service state machine functions + * Implementing transition functions + */ +/* + * Function: nbns_xxxx_in_ssss + * The section below contains functions that implement actions needed + * to legally transit from one state to another. + * Parameters: + * nb_name: pointer to name_t structure which the actions are to be + * applied to + * Returns: + * 0 : if all actions are done successfully + * non-zero: if one of actions failed + * Note: + * - For the sake of simplicity, the actions are automatically rollbacked + * in each function, if an action in transition fails. The design + * documents do not cover these parts of code. + */ +static int nbns_add_name_in_initial(name_t *nb_name) +{ + int rc = -1; + + nb_name->retries = 0; + nb_name->status = nbns_broadcast_add_name_query(nb_name); + if (nb_name->status) + goto out; + nb_name->status = nbns_add_name_to_table(nb_name); + if (nb_name->status) + goto out; + rc = 0; + nb_name->retries++; + nb_name->timer.expires = jiffies + NETBEUI_TRANSMIT_TIMEOUT; + add_timer(&nb_name->timer); + sleep_on(&nb_name->waitq); +out: return rc; +} + +static int nbns_retry_timeout_in_all(name_t *nb_name) +{ + nb_name->timer.expires = jiffies + NETBEUI_TRANSMIT_TIMEOUT; + add_timer(&nb_name->timer); + + if (nbns_broadcast_add_name_query(nb_name)) + return -1; + nb_name->retries++; + return 0; +} + +static int nbns_response_timeout_in_addwait(name_t *nb_name) +{ + wake_up(&nb_name->waitq); + return 0; +} + +static int nbns_add_name_response1_in_addwait(name_t *nb_name) +{ + nb_name->status = -EADDRINUSE; + return 0; +} + +static int nbns_add_name_query_in_addwait(name_t *nb_name) +{ + return nbns_unicast_add_name_response(nb_name) ? -1 : 0; +} + +static int nbns_response_timeout_in_collided(name_t *nb_name) +{ + wake_up(&nb_name->waitq); + nbns_remove_name_from_table(nb_name); + return 0; +} + +static int nbns_add_name_response2_in_collided(name_t *nb_name) +{ + nbns_broadcast_name_in_conflict(nb_name); + del_timer(&nb_name->timer); + wake_up(&nb_name->waitq); + nbns_remove_name_from_table(nb_name); + return 0; +} + +static int nbns_add_name_query_in_acquired(name_t *nb_name) +{ + return nbns_unicast_add_name_response(nb_name) ? -1 : 0; +} + +static int nbns_name_conflict_in_acquired(name_t *nb_name) +{ + nb_name->conflicted = 1; + return 0; +} + +static int nbns_remove_name_in_acquired(name_t *nb_name) +{ + nbns_remove_name_from_table(nb_name); + return 0; +} + +/* + * Name service state machine functions + * Implementing interface functions + */ +/* + * Function: nbns_add_name + * Adds a name to local name table after checking network (being + * permitted). + * Parameters: + * name : pointer to 16 byte NetBIOS name + * type : type of NetBIOS name that is NETBEUI_NAME_GROUP, + * NETBEUI_NAME_UNIQUE + * out_name: pointer to name_t structure built for that name. Since this + * a result argument, its value depends on function return value + * if return value specifies successful operation then this + * argument contains a valid pointer. + * Returns: + * 0 : if name successfully registered into local name table + * -EINVAL : if name is not a valid NetBIOS name + * -EADDRINUSE: if name is registered either in local name table or + * on another machine in network. + * -ENOMEM : if memory allocation for name element name_t fails + * others : any other error value reported by LLC or system. + */ +int nbns_add_name(char *name, name_type_t type, name_t **out_name) +{ + name_t *nb_name; + int rc = -EINVAL; + + if (nbns_validate_name(name)) + goto out; + rc = -EADDRINUSE; + nb_name = nbns_find_name(name); + if (nb_name) + goto out_put; + nb_name = nbns_alloc_name(); + rc = -ENOMEM; + if (!nb_name) + goto out; + *out_name = NULL; + nb_name->state = NETBEUI_NAME_INITIAL; + nb_name->type = type; + memcpy(nb_name->name, name, NETBEUI_NAME_LEN); + nbns_handle_event(NETBEUI_NAME_ADD_NAME, nb_name); + if (nb_name->state != NETBEUI_NAME_ACQUIRED) { + rc = nb_name->status ? : -1; + __nbns_free_name(nb_name); + goto out; + } + rc = 0; + *out_name = nb_name; + nbns_name_hold(nb_name); +out: return rc; +out_put: + nbns_name_put(nb_name); + goto out; +} + +/* + * Function: nbns_find_name + * Finds a name with a specific 16 bytes NetBIOS name in local name table. + * Parameters: + * name: pointer to 16 bytes NetBIOS name to be located in local name table + * Returns: + * NULL : if NetBIOS name not found in local name table + * non-NULL: pointer to name_t element found in local name table. + */ +name_t *nbns_find_name(char *name) +{ + name_t *nb_name; + + read_lock_bh(&name_list_lock); + nb_name = name_list; + while (nb_name) { + if (!memcmp(nb_name->name, name, NETBEUI_NAME_LEN)) + break; + nb_name = nb_name->next; + } + if (nb_name) + nbns_name_hold(nb_name); + read_unlock_bh(&name_list_lock); + return nb_name; +} + +/* + * Function: nbns_del_name + * Removes a name_t element from local name table and local name list, + * using its NetBIOS name. + * Parameters: + * nb_name: pointer to name_t element to remove from local name table + * Returns: None + * Notes: + * - Removing the name_t is a multi-pass mechanism depending on the value + * of name use count. A name is actually removed from local name table + * when its use count reaches zero. + */ +void nbns_del_name(name_t *nb_name) +{ + if (nb_name->name_number != 1) + nbns_name_put(nb_name); +} + +/* + * Function: nbns_del_identifier + * Removes a name_t element from local name table and local name list, + * using its identification number. + * Parameters: + * id: id or number of the name to remove from local name table + * Returns: None + * Notes: + * - Refer to notes section of nbns_del_name comment + * - The identification number is a notion used in sock_name. Every + * SOCK_NAME type socket assigns a unique identifier to all the name + * it registers, which lets not to keep another table of registered + * names in socket data structures. The value is simple memory address + * of socket and is assigned directly from SOCK_NAME codes. + * - Multiple names may have similar identifier. + */ +void nbns_del_identifier(unsigned long id) +{ + name_t *nb_name; + int index; + + for (index = name_table.reserved; index < name_table.size; index++) { + nb_name = name_table_entry(index); + if (nb_name && nb_name->identifier == id) + nbns_del_name(nb_name); + } +} + +/* + * Function: nbns_get_add_name_query + * Accepts an ADD NAME QUERY frame and generates an event for Name + * Service State Machine. + * Parameters: + * skb : pointer to sk_buff that holds the frame + * remote_mac: pointer to MAC address of remote node, whom sent the frame + * type : the flag indicates the type of query: + * NETBEUI_NAME_GROUP indicates ADD GROUP NAME QUERY + * NETBEUI_NAME_UNIQUE indicates ADD NAME QUERY + * Returns: none + */ +void nbns_get_add_name_query(struct sk_buff *skb, unsigned char *remote_mac, + int type) +{ + dgram_t *hdr = (dgram_t *)skb->data; + name_t *nb_name = nbns_find_name(hdr->source_name); + + /* If name not found in name table or both are group names */ + if (!nb_name) + goto out; + if ((skb->dev->flags & IFF_LOOPBACK) || + (nb_name->type == NETBEUI_NAME_GROUP && type == NETBEUI_NAME_GROUP)) + goto out_put; + memcpy(nb_name->remote_mac, remote_mac, skb->dev->addr_len); + nb_name->dev = skb->dev; + nb_name->xmit_correlator = hdr->resp_correlator; + nbns_handle_event(NETBEUI_NAME_ADD_NAME_QUERY, nb_name); +out_put: + nbns_name_put(nb_name); +out: kfree_skb(skb); +} + +/* + * Function: nbns_get_add_name_response + * Accepts an ADD NAME RESPONSE frame and generates an event for Name + * Service State Machine. + * Parameters: + * skb : pointer to sk_buff that holds the frame + * remote_mac: pointer to MAC address of remote node, whom sent the frame + * Returns: none + */ +void nbns_get_add_name_response(struct sk_buff *skb, unsigned char *remote_mac) +{ + dgram_t *hdr = (dgram_t *)skb->data; + name_t *nb_name = nbns_find_correlator(hdr->xmit_correlator, + hdr->dest_name); + /* If it does not match a query */ + if (!nb_name) + goto out; + /* The name registration query got a negative response */ + /* Test if it is a duplicate and already received */ + + if (nb_name->dev != skb->dev || + memcmp(nb_name->remote_mac, remote_mac, skb->dev->addr_len)) { + nb_name->dev = skb->dev; + memcpy(nb_name->remote_mac, remote_mac, skb->dev->addr_len); + nb_name->responses++; + if (nb_name->responses == 1) + nbns_handle_event(NETBEUI_NAME_ADD_NAME_RESPONSE1, + nb_name); + else + nbns_handle_event(NETBEUI_NAME_ADD_NAME_RESPONSE2, + nb_name); + } + nbns_name_put(nb_name); +out: kfree_skb(skb); +} + +/* + * Function: nbns_get_name_conflict + * Accepts a NAME CONFLICT frame and generates an event for Name + * Service State Machine. + * Parameters: + * skb: pointer to sk_buff that holds the frame + * Returns: None + * Notes: + * - The strategy chosen for manipulating NAME CONFLICT frames is to + * set a flag in name_t structure, which is available via /proc entry + * interface. + */ +void nbns_get_name_conflict(struct sk_buff *skb) +{ + name_t *nb_name = nbns_find_name(((dgram_t *)skb->data)->source_name); + + if (nb_name) { + nbns_handle_event(NETBEUI_NAME_NAME_CONFLICT, nb_name); + nbns_name_put(nb_name); + } + kfree_skb(skb); +} + +/* + * Function: nbns_get_link_table + * returns a pointer to NetBEUI name table. The proc support code uses + * the name table to map its contents to /proc/sys/netbeui entry. + * Parameters: None + * Returns: + * non-NULL: pointer to NetBEUI name table + */ +dextab_t *nbns_get_name_table(void) +{ + return &name_table; +} + +/* + * Function: nbns_get_name_list + * returns a pointer to NetBEUI name list. The status module uses + * the name list to prepare its status response. + * Parameters: None + * Returns: + * NULL : name list has no entry + * non-NULL: pointer to NetBEUI name list + */ +name_t *nbns_get_name_list(void) +{ + return name_list; +} + +/* + * Function: nbns_count_names + * returns number of entries in name table. The status module uses + * the this number to prepare its status response. + * Parameters: None + * Returns: + * zero : name table has no entry + * positive: number of entries in name table + */ +int nbns_count_names(void) +{ + return name_table.count; +} Index: kernel-acme/net/netbeui/nb_common.c diff -u /dev/null kernel-acme/net/netbeui/nb_common.c:1.1.8.1 --- /dev/null Thu Nov 22 23:55:00 2001 +++ kernel-acme/net/netbeui/nb_common.c Tue Nov 6 20:13:49 2001 @@ -0,0 +1,134 @@ +/* + * nb_common.c - Contains common in use functions for NetBEUI services. + * + * Copyright (c) 1997 by Procom Technology, Inc. + * 2001 by Arnaldo Carvalho de Melo + * + * 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 +#include + +/* + * Function: netbeui_funcaddr + * Returns functional address of a network device. + * Parameters: + * dev : pointer to device that need to its functional address. + * Returns: char * + */ +char *netbeui_funcaddr(struct net_device *dev) +{ + char* rc = NETBIOS_FUNC_ADDR_4ETH; + + if (dev->type != ARPHRD_ETHER && dev->type != ARPHRD_EETHER && + dev->type != ARPHRD_LOOPBACK) { + if (dev->type == ARPHRD_IEEE802) + rc = NETBIOS_FUNC_ADDR_4TR; + else + rc = "\x00\x00\x00\x00\x00\x00"; + } + return rc; +} + +/* + * Function: nbcm_dev_supported + * Indicates that a network device is supported by us or not. + * Parameters: + * dev_type : device type that must check it. + * Returns: unsigned char + * 0 : device not supported by us. + * 1 : device supported by us. + */ +static unsigned char nbcm_dev_supported(unsigned int dev_type) +{ + return dev_type == ARPHRD_ETHER || dev_type == ARPHRD_EETHER || + dev_type == ARPHRD_IEEE802 || dev_type == ARPHRD_LOOPBACK; +} + +/* + * Function: nbcm_apt_dev + * Parameters: None + * Returns: unsigned char + */ +unsigned char nbcm_apt_dev(struct net_device *dev) +{ + return (dev->flags & IFF_LOOPBACK) || + ((dev->flags & IFF_MULTICAST) && nbcm_dev_supported(dev->type)); +} + +/* + * Function: MAC_HEADLEN + * Calculates the MAC header length of a device. + * Parameters: + * dev: pointer to device that we need to its MAC header length. + * Returns: int + * Always positive: MAC header length of the device. + * Note: + * if we do not recognize device type, the function returns ethernet + * MAC header length. + */ +int MAC_HEADLEN(struct net_device *dev) +{ + return dev->type == ARPHRD_IEEE802 ? sizeof(struct trh_hdr) : + sizeof(struct ethhdr); +} + +/* + * Function: LLCMAC_I_HEADLEN + * Calculates lengths of LLC header and MAC header together for 'I frames' + * on a specific device. + * Parameters: + * dev: pointer to device that calculation is performed for it. + * Returns: int + * Always positive : I frame's MAC + LLC header length of the device. + */ +inline int LLCMAC_I_HEADLEN(struct net_device *dev) +{ + return MAC_HEADLEN(dev) + NETBEUI_LLC_I_HEADLEN; +} + +/* + * Function: LLCMAC_UI_HEADLEN + * Calculates lengths of LLC header and MAC header together for UI frames. + * Parameters: + * mac_hlen : MAC header length for UI frames. + * Returns: int + * Always positive: UI frame's MAC + LLC header length of the device. + */ +inline int LLCMAC_UI_HEADLEN(int mac_hlen) +{ + return mac_hlen + NETBEUI_LLC_UI_HEADLEN; +} + +/* + * Function: LLCMAC_UIB_HEADLEN + * Calculates lengths of LLC header and MAC header together for + * broadcasted UI frames. + * Parameters: none + * Returns: int + * Always positive: broadcasted UI frame's MAC + LLC header length. + */ +inline int LLCMAC_UIB_HEADLEN(void) +{ + return NETBEUI_MAC_B_HEADLEN + NETBEUI_LLC_UI_HEADLEN; +} + +/* + * Function: CALC_DG_SKBLEN + * Calculates appropriate sk_buff length for sending UI frames. + * Parameters: + * mac_hlen : MAC header length. + * user_datalen: length of data that must put into sk_buff after + * MAC + LLC headers (including NetBIOS header). + * Returns: int + * Always positive: appropriate sk_buff length for sending UI frames. + */ +inline int CALC_DG_SKBLEN(int mac_hlen, int user_datalen) +{ + return LLCMAC_UI_HEADLEN(mac_hlen) + user_datalen; +} Index: kernel-acme/net/netbeui/proc.c diff -u /dev/null kernel-acme/net/netbeui/proc.c:1.1.8.1 --- /dev/null Thu Nov 22 23:55:00 2001 +++ kernel-acme/net/netbeui/proc.c Tue Nov 6 20:13:49 2001 @@ -0,0 +1,281 @@ +/* + * proc.c - Implements directory entries for NETBEUI names , links and + * sessions below proc filesystem in memory. + * Notes: + * - Proc file system for NETBEUI is created under /proc/net/netbeui. + * - There are two types of directory entries below netbeui under proc : + * Static directory entries which are created at module initialization + * into memory. Dynamic entries which are created at runtime like session + * entries per link. + * + * Copyright (c) 1997 by Procom Technology, Inc. + * 2001 by Arnaldo Carvalho de Melo + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +#define NETBEUI_PROJECT "Procom, 1997, Arnaldo C. Melo, 2001" +#define NETBEUI_RELEASE "2.0" + +static struct proc_dir_entry *proc_netbeui; + +static const char *netbeui_version = "NetBEUI version " NETBEUI_RELEASE " " + NETBEUI_PROJECT "\n"; + +static int netbeui_version_proc_get_info(char *buffer, char **start, + off_t offset, int count, int* eof, + void* data) +{ + return sprintf(buffer, netbeui_version); +} + +static int netbeui_adapters_proc_get_info(char *buffer, char **start, + off_t offset, int count, int *eof, + void* data) +{ + int i, l = 0; + int j = 0; + char *b; + + read_lock(&netbeui_adapters.lock); + for (i = 0, b = buffer; netbeui_adapters.dev[i]; i++, j += l, b += l) + l = sprintf(b, "%-*.*s %5u %02X:%02X:%02X:%02X:%02X:%02X\n", + IFNAMSIZ, IFNAMSIZ, netbeui_adapters.dev[i]->name, + netbeui_adapters.dev[i]->mtu, + netbeui_adapters.dev[i]->dev_addr[0], + netbeui_adapters.dev[i]->dev_addr[1], + netbeui_adapters.dev[i]->dev_addr[2], + netbeui_adapters.dev[i]->dev_addr[3], + netbeui_adapters.dev[i]->dev_addr[4], + netbeui_adapters.dev[i]->dev_addr[5]); + read_unlock(&netbeui_adapters.lock); + return j; +} + +static int netbeui_names_proc_get_info(char *buffer, char **start, off_t offset, + int count, int* eof, void* data) +{ + int i, len = 0; + dextab_t *name_table = nbns_get_name_table(); + + spin_lock_bh(&name_table->lock); + if (!name_table->count) + goto out; + len += sprintf(buffer, + "%-*.*s stt stu conflict nameNr users\n", + NETBEUI_NAME_LEN + 5, NETBEUI_NAME_LEN + 5, "name"); + for (i = name_table->reserved; i < name_table->size; ++i) { + unsigned char printable_name[NETBEUI_NAME_LEN + 10]; + unsigned char name_port; + name_t *name = name_table->addr[i]; + + if (!name) + continue; + memcpy(printable_name, name->name, NETBEUI_NAME_LEN + 1); + name_port = name->name[NETBEUI_NAME_LEN - 1]; + if (name_port < 0x20 || name_port >= 0x80) + sprintf(printable_name + NETBEUI_NAME_LEN - 1, + "<%2x>", name_port); + len += sprintf(buffer + len, + "%-*.*s %3d %3d %8u %6u %5u\n", + NETBEUI_NAME_LEN + 5, NETBEUI_NAME_LEN + 5, + printable_name, name->state, name->status, + name->conflicted, name->name_number, + atomic_read(&name->refcnt)); + } +out: spin_unlock_bh(&name_table->lock); + if (offset >= len) { + *start = buffer; + *eof = 1; + len = 0; + } else { + *start = buffer + offset; + if ((len -= offset) > count) + len = count; + else + *eof = 1; + } + return len; +} + +static int netbeui_sessions_proc_get_info(char *buffer, char **start, + off_t offset, int count, int* eof, + void* data) +{ + int i, len = 0; + dextab_t *link_table = nbll_get_link_table(); + + spin_lock_bh(&link_table->lock); + if (!link_table->count) + goto out; + len += sprintf(buffer, + "lk st su %-*.*s%-*.*slsn rsn ver nI " + "tr mtu oTotal iTotal\n", + NETBEUI_NAME_LEN - 1, NETBEUI_NAME_LEN - 1, + "localName", + NETBEUI_NAME_LEN - 1, NETBEUI_NAME_LEN - 1, + "remoteName"); + + for (i = link_table->reserved; i < link_table->size; ++i) { + int j; + link_t *link = link_table->addr[i]; + dextab_t *session_table; + + if (!link) + continue; + session_table = &link->session_table; + spin_lock_bh(&session_table->lock); + if (!session_table->count) + goto unlock_session_table; + for (j = session_table->reserved; + j < session_table->size; ++j) { + struct nb_session *session = session_table->addr[j]; + char* name = NULL; + + if (!session) + continue; + if (session->local_name) + name = (char *)&session->local_name->name; + len += sprintf(buffer + len, + "%2u %2d %2d %-*.*s%-*.*s%3u " + "%3u %3u %2u %2u %4u %8u %8u\n", + session->link, + session->state, session->status, + NETBEUI_NAME_LEN - 1, + NETBEUI_NAME_LEN - 1, + name, + NETBEUI_NAME_LEN - 1, + NETBEUI_NAME_LEN - 1, + session->remote_name, + session->lsn, session->rsn, + session->version, + session->nack_indicator, + session->tr_frame_lf, session->mtu, + session->o_total, session->i_total); + } +unlock_session_table: + spin_unlock_bh(&session_table->lock); + } +out: spin_unlock_bh(&link_table->lock); + if (offset >= len) { + *start = buffer; + *eof = 1; + len = 0; + } else { + *start = buffer + offset; + if ((len -= offset) > count) + len = count; + else + *eof = 1; + } + return len; +} + +static int netbeui_links_proc_get_info(char *buffer, char **start, off_t offset, + int count, int *eof, void *data) +{ + int i, len = 0; + dextab_t *table = nbll_get_link_table(); + + spin_lock_bh(&table->lock); + if (table->count) + len = sprintf(buffer, + "lk st su %-17.17s %-*.*s llcBusy frames\n", + "remoteMac", IFNAMSIZ, IFNAMSIZ, "remoteDevice"); + else + goto out; + for (i = table->reserved; i < table->size; ++i) { + link_t *link = table->addr[i]; + char *name = NULL; + + if (!link) + continue; + if (link->dev) + name = link->dev->name; + len += sprintf(buffer + len, + "%2u %2d %2d %02X:%02X:%02X:%02X:%02X:%02X " + "%-*.*s %7u %6u\n", + link->link, link->state, link->status, + link->remote_mac[0], link->remote_mac[1], + link->remote_mac[2], link->remote_mac[3], + link->remote_mac[4], link->remote_mac[5], + IFNAMSIZ, IFNAMSIZ, name, link->llc_busy, + skb_queue_len(&link->skbq)); + } +out: spin_unlock_bh(&table->lock); + if (offset >= len) { + *start = buffer; + *eof = 1; + len = 0; + } else { + *start = buffer + offset; + if ((len -= offset) > count) + len = count; + else + *eof = 1; + } + return len; +} + +int netbeui_proc_init(void) +{ + int rc = 0; + + proc_netbeui = proc_mkdir("netbeui", proc_net); + if (!proc_netbeui) + goto out_err; + if (!create_proc_read_entry("links", 0, proc_netbeui, + netbeui_links_proc_get_info, NULL)) + goto out_netbeui; + if (!create_proc_read_entry("sessions", 0, proc_netbeui, + netbeui_sessions_proc_get_info, NULL)) + goto out_links; + if (!create_proc_read_entry("names", 0, proc_netbeui, + netbeui_names_proc_get_info, NULL)) + goto out_sessions; + if (!create_proc_read_entry("version", 0, proc_netbeui, + netbeui_version_proc_get_info, NULL)) + goto out_names; + if (!create_proc_read_entry("adapters", 0, proc_netbeui, + netbeui_adapters_proc_get_info, NULL)) + goto out_version; +out: return rc; +out_version: + remove_proc_entry("version", proc_netbeui); +out_names: + remove_proc_entry("names", proc_netbeui); +out_sessions: + remove_proc_entry("sessions", proc_netbeui); +out_links: + remove_proc_entry("links", proc_netbeui); +out_netbeui: + remove_proc_entry("netbeui", proc_net); +out_err: + rc = -ENOMEM; + goto out; +} + +void netbeui_proc_clean(void) +{ + if (proc_netbeui) { + remove_proc_entry("links", proc_netbeui); + remove_proc_entry("sessions", proc_netbeui); + remove_proc_entry("names", proc_netbeui); + remove_proc_entry("version", proc_netbeui); + remove_proc_entry("adapters", proc_netbeui); + remove_proc_entry("netbeui", proc_net); + } +} Index: kernel-acme/net/netbeui/query_serve.c diff -u /dev/null kernel-acme/net/netbeui/query_serve.c:1.1.8.1 --- /dev/null Thu Nov 22 23:55:00 2001 +++ kernel-acme/net/netbeui/query_serve.c Tue Nov 6 20:13:49 2001 @@ -0,0 +1,778 @@ +/* + * query_serve.c - Contains functions that implement NetBIOS Query Service. + * It also implements a remote name cache which is intended + * to cache a mapping between NetBIOS name and MAC address. + * Notes: + * - VRP in comments is the acronym of "Value Result Parameter" + * - Remote name cache needs upper layer considerations which makes its + * impossible or hardly possible in other modules. Up to now only Session + * service uses this mechanism to speed-up connection establishment. + * + * Copyright (c) 1997 by Procom Technology, Inc. + * 2001 by Arnaldo Carvalho de Melo + * + * 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 +#include +#include +#include +#include +#include +#include + +static void nbqs_timer_function(unsigned long); + +/* These functions are Query State Transition handlers */ +static int nbqs_name_query_in_initial(query_t *nb_query); +static int nbqs_name_find_in_initial(query_t *nb_query); +static int nbqs_retry_timeout_in_all(query_t *nb_query); +static int nbqs_response_timeout_in_all(query_t *nb_query); +static int nbqs_name_recognized_in_qrywait(query_t *nb_query); +static int nbqs_name_recognized_in_findwait(query_t *nb_query); +static int nbqs_end_query_in_name_recognized(query_t *nb_query); + +static unsigned short int nbqs_correlator; +#define nbqs_next_correlator() (++nbqs_correlator) + +static query_t *query_list; +rwlock_t query_list_lock = RW_LOCK_UNLOCKED; + +/* Remote name cache definition */ +struct rnc_struct { + __u8 name[NETBEUI_NAME_LEN]; + __u8 mac[6]; + struct net_device *dev; + unsigned long int time_stamp; + struct rnc_struct *prev; + struct rnc_struct *next; +}; +typedef struct rnc_struct rnc_t; + +static rnc_t *rnc_list; +static int rnc_count; +static spinlock_t rnc_list_lock = SPIN_LOCK_UNLOCKED; + +/* Query Service State Machine definition */ +typedef int (* query_event_handler_t)(query_t *); + +struct event_struct { + query_state_t next_state; + query_event_handler_t event_handler; +}; + +static struct event_struct query_state_table[4][6] = { + /* NETBEUI_QUERY_INITIAL */ +{ +{ NETBEUI_QUERY_QRYWAIT, + nbqs_name_query_in_initial }, /* NETBEUI_QUERY_NAME_QUERY */ +{ NETBEUI_QUERY_FINDWAIT, + nbqs_name_find_in_initial }, /* NETBEUI_QUERY_NAME_FIND */ +{ -1, NULL }, /* NETBEUI_QUERY_RETRY_TIMEOUT */ +{ -1, NULL }, /* NETBEUI_QUERY_RESPONSE_TIMEOUT */ +{ -1, NULL }, /* NETBEUI_QUERY_NAME_RECOGNIZED */ +{ -1, NULL }, /* NETBEUI_QUERY_END_QURY */ +}, + /* NETBEUI_QUERY_QRYWAIT */ +{ +{ -1, NULL }, /* NETBEUI_QUERY_NAME_QUERY */ +{ -1, NULL }, /* NETBEUI_QUERY_NAME_FIND */ +{ NETBEUI_QUERY_QRYWAIT, + nbqs_retry_timeout_in_all }, /* NETBEUI_QUERY_RETRY_TIMEOUT */ +{ NETBEUI_QUERY_INITIAL, + nbqs_response_timeout_in_all }, /* NETBEUI_QUERY_RESPONSE_TIMEOUT */ +{ NETBEUI_QUERY_RECOGNIZED, + nbqs_name_recognized_in_qrywait }, /* NETBEUI_QUERY_NAME_RECOGNIZED */ +{ -1, NULL }, /* NETBEUI_QUERY_END_QURY */ +}, + /* NETBEUI_QUERY_FINDWAIT */ +{ +{ -1, NULL }, /* NETBEUI_QUERY_NAME_QUERY */ +{ -1, NULL }, /* NETBEUI_QUERY_NAME_FIND */ +{ NETBEUI_QUERY_FINDWAIT, + nbqs_retry_timeout_in_all }, /* NETBEUI_QUERY_RETRY_TIMEOUT */ +{ NETBEUI_QUERY_INITIAL, + nbqs_response_timeout_in_all }, /* NETBEUI_QUERY_RESPONSE_TIMEOUT */ +{ NETBEUI_QUERY_FINDWAIT, + nbqs_name_recognized_in_findwait }, /* NETBEUI_QUERY_NAME_RECOGNIZED */ +{ -1, NULL }, /* NETBEUI_QUERY_END_QURY */ +}, + /* NETBEUI_QUERY_RECOGNIZED */ +{ +{ -1, NULL }, /* NETBEUI_QUERY_NAME_QUERY */ +{ -1, NULL }, /* NETBEUI_QUERY_NAME_FIND */ +{ -1, NULL }, /* NETBEUI_QUERY_RETRY_TIMEOUT */ +{ -1, NULL }, /* NETBEUI_QUERY_RESPONSE_TIMEOUT */ +{ -1, NULL }, /* NETBEUI_QUERY_NAME_RECOGNIZED */ +{ NETBEUI_QUERY_INITIAL, + nbqs_end_query_in_name_recognized },/* NETBEUI_QUERY_END_QURY */ +} +}; +/* + * Query service state machine functions + * Implementing remote name cache + */ +/* + * Function: nbqs_remove_rnc + * Removes a rnc_t entry from remote name cache list rnc_list + * + * Parameters: + * nb_rnc : pointer to rnc_t entry to remove + * + * Returns: none + */ +static void __nbqs_remove_rnc(rnc_t *nb_rnc) +{ + if (nb_rnc->next) + nb_rnc->next->prev = nb_rnc->prev; + if (nb_rnc->prev) + nb_rnc->prev->next = nb_rnc->next; + else + rnc_list = nb_rnc->next; +} + +static inline void nbqs_remove_rnc(rnc_t *nb_rnc) +{ + spin_lock(&rnc_list_lock); + __nbqs_remove_rnc(nb_rnc); + spin_unlock(&rnc_list_lock); +} + +/* + * Function: nbqs_find_rnc + * Finds a rnc_t entry in remote name cache list for a specific NetBIOS + * 16 byte name. + * + * Parameters: + * name : NetBIOS name of remote node to find its rnc_t + * + * Returns: + * NULL : if no matching entry found in remote cache name list + * non-NULL: pointer to rnc_t in remote_cache name list + */ +static inline rnc_t *__nbqs_find_rnc(char *name) +{ + rnc_t *nb_rnc = rnc_list; + + while (nb_rnc) { + if (!memcmp(nb_rnc->name, name, NETBEUI_NAME_LEN)) { + nb_rnc->time_stamp = jiffies; + break; + } + nb_rnc = nb_rnc->next; + } + return nb_rnc; +} + +static inline rnc_t *nbqs_find_rnc(char *name) +{ + rnc_t *nb_rnc; + + spin_lock(&rnc_list_lock); + nb_rnc = __nbqs_find_rnc(name); + spin_unlock(&rnc_list_lock); + return nb_rnc; +} + +/* + * Function: nbqs_cleanup_rnc + * This routine will throw those cache entries who live in cache more + * than a specific time (measured in jiffies) . + * + * Parameters: none + * + * Returns: none + * + * Notes: + * - For the sake of efficiency, remote name cache list wont grow more + * than a specific size . Note that this is + * just a high water mark. Refer to nbqs_add_rnc + */ +static void __nbqs_cleanup_rnc(void) +{ + rnc_t *nb_rnc = rnc_list; + + while (nb_rnc) { + if (jiffies - nb_rnc->time_stamp > NETBEUI_QUERY_CACHE_LIVING_TIME) { + rnc_t *tmp_rnc = nb_rnc; + + nb_rnc = nb_rnc->next; + nbqs_remove_rnc(tmp_rnc); + kfree(tmp_rnc); + } else { + rnc_count++; + nb_rnc = nb_rnc->next; + } + } +} + +static inline void nbqs_cleanup_rnc(void) +{ + spin_lock(&rnc_list_lock); + __nbqs_cleanup_rnc(); + spin_unlock(&rnc_list_lock); +} + +/* + * Function: nbqs_add_rnc + * Adds/Updates an entry to remote name cache list + * + * Parameters: + * name : pointer to NetBIOS name of remote node + * dev : pointer to device structure node is connected via + * mac : pointer to MAC address of remote node + * + * Returns: none + * + * Notes: + * - If an rnc_t corresponding to NetBIOS name exists in cache it is + * updated else a new entry is created. + * - This routine handles cache grow beyond NETBEUI_QUERY_MAX_CACHE_ENTRIES + * but does not guarantee to preserve the entry count below the value + * - This routine does not guarantee adding memory since it may fail + * to allocate memory. This does not affect the system consistency. + */ +void nbqs_add_rnc(char *name, struct net_device *dev, unsigned char *mac) +{ + rnc_t *nb_rnc; + + spin_lock(&rnc_list_lock); + nb_rnc = __nbqs_find_rnc(name); + if (nb_rnc) { + memcpy(nb_rnc->mac, mac, 6); + nb_rnc->dev = dev; + nb_rnc->time_stamp = jiffies; + } + if (rnc_count >= NETBEUI_QUERY_MAX_CACHE_ENTRIES) + __nbqs_cleanup_rnc(); + nb_rnc = kmalloc(sizeof(rnc_t), GFP_KERNEL); + if (!nb_rnc) + goto out; + rnc_count++; + memcpy(nb_rnc->name, name, NETBEUI_NAME_LEN); + memcpy(nb_rnc->mac, mac, 6); + nb_rnc->dev = dev; + nb_rnc->time_stamp = jiffies; + if (rnc_list) { + rnc_list->prev = nb_rnc; + nb_rnc->next = rnc_list; + nb_rnc->prev = NULL; + rnc_list = nb_rnc; + } else { + rnc_list = nb_rnc; + nb_rnc->next = nb_rnc->prev = NULL; + } +out: spin_unlock(&rnc_list_lock); +} + +/* + * Function: nbqs_delete_rnc + * This is a wrapper to nbqs_remove_rnc which both remove entry from + * remote name cache and deallocates memory. + * + * Parameters: + * name : NetBIOS name of remote node to remove its rnc_t + * + * Returns: none + */ +void nbqs_delete_rnc(char *name) +{ + rnc_t *nb_rnc; + + spin_lock(&rnc_list_lock); + nb_rnc = __nbqs_find_rnc(name); + if (nb_rnc) { + __nbqs_remove_rnc(nb_rnc); + kfree(nb_rnc); + rnc_count--; + } + spin_unlock(&rnc_list_lock); +} + +/* + * Query service state machine functions + * Implementing general functions + */ +/* + * Function: nbqs_alloc_query + * Allocates a query_t structure and does completely initialize all fields + * + * Parameters: none + * + * Returns: + * NULL : if can not allocate memory for query_t or its sk_buff + * non-NULL: pointer to query_t + * + * Notes: + * - An skb is attached to each new query for query processing purposes + * - query timer is initialized but not started. + * - The call to memset does implicitly initialize all fields. Those + * fields that need explicit non-zero initialization are manipulated + * afterwards. + */ +static query_t *nbqs_alloc_query(void) +{ + int name_dgram_len; + query_t *nb_query = kmalloc(sizeof(*nb_query), GFP_KERNEL); + + if (!nb_query) + goto out; + /* Implicitly initialize all fields */ + memset(nb_query, 0, sizeof(*nb_query)); + init_timer(&nb_query->timer); + nb_query->timer.data = (unsigned long)nb_query; + nb_query->timer.function = nbqs_timer_function; + init_waitqueue_head(&nb_query->waitq); + + /* Allocate query skb */ + /* All commands use same length */ + name_dgram_len = nb_cmd_hdr_len[NETBEUI_NAME_QUERY]; + nb_query->skb = alloc_skb(CALC_DG_SKBLEN(NETBEUI_MAC_B_HEADLEN, + name_dgram_len), GFP_KERNEL); + if (!nb_query->skb) + goto err; + skb_reserve(nb_query->skb, LLCMAC_UIB_HEADLEN()); + nb_query->skb->nh.raw = nb_query->skb->h.raw = nb_query->skb->data; + skb_put(nb_query->skb, name_dgram_len); + nb_query->skb->dev = NULL; +out: return nb_query; +err: kfree(nb_query); + nb_query = NULL; + goto out; +} + +/* + * Function: nbqs_free_query + * Deallocates memory used for a query_t and its sk_buff + * + * Parameters: + * nb_query : pointer to query_t memory to be freed + * + * Returns: none + */ +static inline void nbqs_free_query(query_t *nb_query) +{ + kfree_skb(nb_query->skb); + kfree(nb_query); +} + +/* + * Function: nbqs_add_query_to_list + * Inserts a previously allocated/initialized query_t into query list + * + * Parameters: + * nb_query : pointer to query_t to insert + * + * Returns: + * 0 : always returns zero + */ +static inline int nbqs_add_query_to_list(query_t *nb_query) +{ + write_lock(&query_list_lock); + nb_query->next = query_list; + query_list = nb_query; + write_unlock(&query_list_lock); + return 0; +} + +/* + * Function: nbqs_remove_query_from_list + * Removes a query_t from query list + * + * Parameters: + * nb_query : pointer to query_t to remove + * + * Returns: none + */ +static void nbqs_remove_query_from_list(query_t *nb_query) +{ + query_t *entry, *prev_entry = NULL; + + write_lock(&query_list_lock); + entry = query_list; + while (entry) { + if (entry == nb_query) { + if (prev_entry) + prev_entry->next = entry->next; + else + query_list = entry->next; + break; + } + prev_entry = entry; + entry = entry->next; + } + write_unlock(&query_list_lock); +} + +/* + * Function: nbqs_find_correlator + * finds a query_t in query_list, which has transmitted frames with + * specific correlator. + * + * Parameters: + * correlator : a sixteen bit integer which contains the response + * correlator of input frame. It should be matched against + * xmit correlator of frames sent by the element. + * + * Returns: + * NULL : if no query_t element found with the requested + * characteristic. + * non-NULL : pointer to matching query_t with the requested + * characteristic. + */ +static query_t *nbqs_find_correlator(unsigned short correlator) +{ + query_t *nb_query; + + read_lock(&query_list_lock); + nb_query = query_list; + while (nb_query) { + if (nb_query->resp_correlator == correlator) + break; + nb_query = nb_query->next; + } + read_unlock(&query_list_lock); + return nb_query; +} + +/* + * Function: nbqs_boradcast_name_query + * Prepares a NetBIOS NAME QUERY frames and nbll_uisends it to network. + * + * Parameters: + * nb_query : pointer to query_t element which the frame should be built + * from + * + * Returns: + * 0 : if frame is successfully broadcasted to network + * non-zero : if frame transmission encountered an error (usually at NDI + * layer) + * + * Notes: + * - Since ADD NAME QUERY frames are retransmitted in timed intervals, it + * is considered to build frame once, but transmit it multiple times. + * having built frames in each retransmission does generate multiple + * correlators and does frustrate processing responses. + */ +static int nbqs_broadcast_name_query(query_t *nb_query) +{ + if (!nb_query->retries) { + dgram_t *hdr = (dgram_t *)nb_query->skb->data; + + hdr->length = nb_cmd_hdr_len[NETBEUI_NAME_QUERY]; + hdr->delimiter = NETBEUI_DELIMITER; + hdr->command = NETBEUI_NAME_QUERY; + hdr->data1 = 0; + if (nb_query->lsn > 0) + hdr->data2 = + NETBEUI_CALL_DATA2(nb_query->calling_name->type, + nb_query->lsn); + else + hdr->data2 = 0; + hdr->xmit_correlator = 0; + hdr->resp_correlator = + nb_query->resp_correlator = nbqs_next_correlator(); + memcpy(hdr->dest_name, nb_query->called_name, NETBEUI_NAME_LEN); + if (nb_query->lsn > 0) + memcpy(hdr->source_name, nb_query->calling_name->name, + NETBEUI_NAME_LEN); + else + memset(hdr->source_name, 0, NETBEUI_NAME_LEN); + } + return nbll_uisend(NULL, nb_query->skb); +} + +/* + * Function: nbqs_handle_event + * This is the heart of Query Service State Machine, which performs a + * transition from current state of query element to new state based + * on event occurred and query state table contents. + * + * Parameters: + * event : An integer of NETBEUI_QUERY_* family that implies type of + * event + * nb_name : pointer to query_t structure which the event occurred on + * + * Returns: none + * + * Notes: + * - The state changes before actions be executed. This is due to + * non deterministic behavior of actions which may sleep the current + * process, thus stopping the function in the mid-way. + */ +static void nbqs_handle_event(query_event_t event, query_t *nb_query) +{ + struct event_struct *ev = &query_state_table[nb_query->state][event]; + + if (ev && ev->event_handler) { + unsigned char old_state = nb_query->state; + + nb_query->state = ev->next_state; + if (ev->event_handler(nb_query)) + nb_query->state = old_state; + } +} + +/* + * Function: nbqs_timer_function + * This is the callback function triggered upon expiration of name + * retransmittion timer. It just injects an event into state machine for + * its link. + * + * Parameters: + * input : pointer to query_t structure whose timer is expired. + * + * Returns: none + */ +static void nbqs_timer_function(unsigned long input) +{ + query_t *nb_query = (query_t *)input; + + if (nb_query->retries < NETBEUI_TRANSMIT_COUNT) + nbqs_handle_event(NETBEUI_QUERY_RETRY_TIMEOUT, nb_query); + else + nbqs_handle_event(NETBEUI_QUERY_RESPONSE_TIMEOUT, nb_query); +} + +/* + * Query service state machine functions + * Implementing transition actions + */ +/* + * Function: nbqs_xxxx_in_ssss + * The section below contains functions that implement actions needed + * to legally transit from one state to another. + * + * Parameters: + * nb_query: pointer to query_t structure which the actions are to be + * applied to + * + * Returns: + * 0 : if all actions are done successfully + * non-zero: if one of actions failed + * + * Note: + * - For the sake of simplicity, the actions are automatically rollbacked + * in each function, if an action in transition fails. The design + * documents do not cover these parts of code. + */ +static int nbqs_name_query_in_initial(query_t *nb_query) +{ + nb_query->retries = nb_query->responses = 0; + + if (nbqs_broadcast_name_query(nb_query)) + return -ENOMEM; + nb_query->retries++; + nb_query->timer.expires = jiffies + NETBEUI_TRANSMIT_TIMEOUT; + add_timer(&nb_query->timer); + nbqs_add_query_to_list(nb_query); + sleep_on(&nb_query->waitq); + return 0; +} + +static int nbqs_name_find_in_initial(query_t *nb_query) +{ + nb_query->retries = nb_query->responses = nb_query->buff_ofs = 0; + + if (nbqs_broadcast_name_query(nb_query)) + return -ENOMEM; + nb_query->retries++; + nb_query->timer.expires = jiffies + NETBEUI_TRANSMIT_TIMEOUT; + add_timer(&nb_query->timer); + nbqs_add_query_to_list(nb_query); + sleep_on(&nb_query->waitq); + return 0; +} + +static int nbqs_retry_timeout_in_all(query_t *nb_query) +{ + nb_query->timer.expires = jiffies + NETBEUI_TRANSMIT_TIMEOUT; + add_timer(&nb_query->timer); + + if (nbqs_broadcast_name_query(nb_query)) + return -ENOMEM; + nb_query->retries++; + return 0; +} + +static int nbqs_response_timeout_in_all(query_t *nb_query) +{ + wake_up(&nb_query->waitq); + nbqs_remove_query_from_list(nb_query); + return 0; +} + +static int nbqs_name_recognized_in_qrywait(query_t *nb_query) +{ + del_timer(&nb_query->timer); + wake_up(&nb_query->waitq); + nbqs_remove_query_from_list(nb_query); + return 0; +} + +static int nbqs_name_recognized_in_findwait(query_t *nb_query) +{ + /* Here we should add the MAC header to buffer, considering + * unicity of resp */ + int i; + struct net_device *dev; + char *mac_buff = nb_query->mac_buff; + + for (i = 0; i < nb_query->buff_ofs; i++) { + dev = nb_query->dev_buff[i]; + + if (nb_query->dev == dev && + !memcmp(nb_query->remote_mac, mac_buff, dev->addr_len)) + return 0; + mac_buff += dev->addr_len; + } + if (nb_query->buff_ofs < nb_query->buff_len) { + dev = nb_query->dev; + nb_query->dev_buff[nb_query->buff_ofs] = dev; + memcpy(mac_buff, nb_query->remote_mac, dev->addr_len); + nb_query->buff_ofs++; + } + return 0; +} + +static int nbqs_end_query_in_name_recognized(query_t *nb_query) +{ + return 0; +} + +/* + * Query service state machine functions + * Implementing interface functions + */ +/* + * Function: nbqs_query_name + * Does the first step of session establishment process and determines + * remote session number, Largest Frame Bits and response correlator for + * further negotiations. + * + * Parameters: + * called_name : pointer to NetBIOS name of remote node + * calling_name : pointer to name_t element registered locally + * lsn : local session number + * rsn : (VRP) pointer to remote session number + * valid if return value is zero + * lfb : (VRP) pointer to Largest Frame Bits (for support of + * Token-Ring) + * xmit_correlator: (VRP) pointer to xmit correlator found in NAME RESPONSE + * valid if return value is zero + * + * Returns: + * 0 : if query was successful + * -EINVAL : if called_name is not a valid NetBIOS name + * -ENOMEM : if memory allocation for query_t element failed + * -ETIMEDOUT : if remote node did not respond + */ +int nbqs_query_name(char *called_name, name_t *calling_name, unsigned char lsn, + unsigned char *rsn, unsigned char *lfb, + unsigned short *xmit_correlator) +{ + query_t *nb_query; + + if (nbns_validate_name(called_name)) + return -EINVAL; + nb_query = nbqs_alloc_query(); + if (!nb_query) + return -ENOMEM; + nb_query->state = NETBEUI_QUERY_INITIAL; + nb_query->calling_name = calling_name; + memcpy(nb_query->called_name, called_name, NETBEUI_NAME_LEN); + nb_query->lsn = lsn; + nbqs_handle_event(NETBEUI_QUERY_NAME_QUERY, nb_query); + + if (nb_query->state != NETBEUI_QUERY_RECOGNIZED) { + nbqs_free_query(nb_query); + return -ETIMEDOUT; + } + *rsn = nb_query->rsn; + *lfb = nb_query->tr_lfb; + *xmit_correlator = nb_query->xmit_correlator; + nbqs_handle_event(NETBEUI_QUERY_END_QUERY, nb_query); + nbqs_free_query(nb_query); + return 0; +} + +/* + * Function: nbqs_find_name + * This is NAME FIND interface which determines MAC address of a remote + * node with a specific name. It also finds the device which connects + * the host with remote node. + * + * Parameters: + * called_name : pointer to NetBIOS name of remote node + * mac_buff : pointer to array of MAC buffers + * dev_buff : pointer to array of device pointers + * buff_len : maximum number of entries in mac_buff and dev_buff arrays + * + * Returns: + * >= 0 : count of dev_buff and mac_buff entries corresponding to + * to nodes which own called_name + * -EINVAL : if called_name is not a valid NetBIOS name + * -ENOMEM : if memory allocation for query_t element failed + * + * Notes: + * - This routines is the point in which RNC cache is used for NetBIOS + * name resolution. The consistency of cache contents depends on + * upper layers which insert/delete cache entries. + */ +int nbqs_find_name(char *called_name, char *mac_buff, + struct net_device **dev_buff, int buff_len) +{ + int names_found; + query_t *nb_query; + rnc_t *nb_rnc; + + if (nbns_validate_name(called_name)) + return -EINVAL; + nb_rnc = nbqs_find_rnc(called_name); + if (nb_rnc) { + *dev_buff = nb_rnc->dev; + memcpy(mac_buff, nb_rnc->mac, 6); + return 1; + } + nb_query = nbqs_alloc_query(); + if (!nb_query) + return -ENOMEM; + nb_query->state = NETBEUI_QUERY_INITIAL; + nb_query->calling_name = NULL; + memcpy(nb_query->called_name, called_name, NETBEUI_NAME_LEN); + nb_query->lsn = 0; + nb_query->mac_buff = mac_buff; + nb_query->dev_buff = dev_buff; + nb_query->buff_len = buff_len; + nbqs_handle_event(NETBEUI_QUERY_NAME_FIND, nb_query); + names_found = nb_query->buff_ofs; + nbqs_free_query(nb_query); + if (names_found > 0) + nbqs_add_rnc(called_name, *dev_buff, mac_buff); + return names_found; +} + +void nbqs_get_name_recognized(struct sk_buff *skb, unsigned char *remote_mac) +{ + dgram_t *dgram = (dgram_t *)skb->data; + unsigned short data2 = dgram->data2; + query_t *nb_query = nbqs_find_correlator(dgram->xmit_correlator); + + /* If it does not match a query */ + if (!nb_query) + goto out; + nb_query->rsn = NETBEUI_CALL_SS(data2); + nb_query->dev = skb->dev; + memcpy(nb_query->remote_mac, remote_mac, 6); + nb_query->xmit_correlator = dgram->resp_correlator; + nb_query->tr_lfb = skb->cb[0]; /* Token Ring support */ + nb_query->responses++; + /* We should also set mac_header */ + nbqs_handle_event(NETBEUI_QUERY_NAME_RECOGNIZED, nb_query); +out: kfree_skb(skb); +} Index: kernel-acme/net/netbeui/session_serve.c diff -u /dev/null kernel-acme/net/netbeui/session_serve.c:1.1.8.1 --- /dev/null Thu Nov 22 23:55:01 2001 +++ kernel-acme/net/netbeui/session_serve.c Tue Nov 6 20:13:49 2001 @@ -0,0 +1,2821 @@ +/* + * session_serve.c - Contains functions that implement NetBIOS Session Service + * + * Important Note: + * - Consider the following design issues when reading session service + * sources + * 1- Session Establishment, Session Termination and Session Data + * Output Stream are joined to from the Session States + * 2- Session Data Input Stream does is not implemented in State + * Machine model, it is simply a buffering and acknowledgement + * mechanism. + * Notes: + * - VRP in comments is the acronym of "Value Result Parameter" + * - Session Service is the most complicated service in NetBIOS. Numerous + * states and events used for session establishment, session data + * transfer and session termination need to much effort to overcome. + * Please read documentations before changing even a bit (!) in source + * codes. + * + * Copyright (c) 1997 by Procom Technology, Inc. + * 2001 by Arnaldo Carvalho de Melo + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define dprintk(format, a...) \ + printk(KERN_INFO __FUNCTION__ ": " format, ##a) + +extern int llc_in_progress; + +/* These functions are used for locking sessions to avoid race conditions */ +static void nbss_lock_session(session_t *session); +static int nbss_release_session(session_t *session); + +static void nbss_wait_timer_function(unsigned long input); +static void nbss_resource_timer_function(unsigned long input); + +/* These functions are Session Service State Transition handlers */ +static int nbss_event_abort_send_in_all(session_t *session); +static int nbss_event_call_in_initial(session_t *session); +static int nbss_event_listen_in_initial(session_t *session); + +static int nbss_event_confirm_in_callwait(session_t *session); +static int nbss_event_reject_in_callwait(session_t *session); +static int nbss_event_abort_in_callwait(session_t *session); + +static int nbss_event_connect_in_confwait(session_t *session); +static int nbss_event_timeout_in_confwait(session_t *session); +static int nbss_event_abort_in_confwait(session_t *session); + +static int nbss_event_confirm_in_listenwait(session_t *session); +static int nbss_event_reject_in_listenwait(session_t *session); + +static int nbss_event_connect_in_initwait(session_t *session); +static int nbss_event_timeout_in_initwait(session_t *session); +static int nbss_event_reject_in_initwait(session_t *session); +static int nbss_event_abort_in_initwait(session_t *session); + +static int nbss_event_hangup_in_connected(session_t *session); +static int nbss_event_first_middle_cont_in_connected(session_t *session); +static int nbss_event_first_middle_in_connected(session_t *session); +static int nbss_event_only_last_ack_in_connected(session_t *session); +static int nbss_event_only_last_in_connected(session_t *session); +static int nbss_event_resource_in_connected(session_t *session); +static int nbss_event_end_in_connected(session_t *session); +static int nbss_event_abort_in_connected(session_t *session); + +static int nbss_event_hangup_in_discwait(session_t *session); + +static int nbss_event_continue_in_contwait(session_t *session); +static int nbss_event_restart_in_contwait(session_t *session); +static int nbss_event_pause_in_contwait(session_t *session); +static int nbss_event_nonblock_in_contwait(session_t *session); + +static int nbss_event_restart_in_standwait(session_t *session); +static int nbss_event_pause2_in_standwait(session_t *session); + +static int nbss_event_data_acked_in_ackwait(session_t *session); +static int nbss_event_restart_in_ackwait(session_t *session); +static int nbss_event_pause_in_ackwait(session_t *session); +static int nbss_event_nonblock_in_ackwait(session_t *session); + +static int nbss_event_norm_retry_in_rsrcwait(session_t *session); +static int nbss_event_conn_retry_in_rsrcwait(session_t *session); + +static int nbss_event_first_middle_in_normal(session_t *session); +static int nbss_event_only_last_ack_in_normal(session_t *session); +static int nbss_event_only_last_in_normal(session_t *session); +static int nbss_event_pause_in_normal(session_t *session); +static int nbss_event_restart_in_normal(session_t *session); +static int nbss_event_resource_in_normal(session_t *session); +static int nbss_event_nonblock_in_normal(session_t *session); + +static unsigned short int nbss_correlator; +#define nbss_next_correlator() (++nbss_correlator) + +/* This is a list for pending session listened for incoming session + * establishment requests */ +static session_t *session_list; +static spinlock_t session_list_lock = SPIN_LOCK_UNLOCKED; + +/* Session service state machine definition */ +typedef int (*session_event_handler_t)(session_t *session); + +struct event_struct { + session_state_t next_state; + session_event_handler_t event_handler; +}; + +static struct event_struct session_state_table[12][23] = { + /* NETBEUI_SESS_INITIAL */ +{ +{ NETBEUI_SESS_CALLWAIT, + nbss_event_call_in_initial }, /* NETBEUI_SESS_CALL */ +{ NETBEUI_SESS_LISTENWAIT, + nbss_event_listen_in_initial }, /* NETBEUI_SESS_LISTEN */ +{ -1, NULL }, /* NETBEUI_SESS_CONFIRM */ +{ -1, NULL }, /* NETBEUI_SESS_REJECT */ +{ -1, NULL }, /* NETBEUI_SESS_CONNECT */ +{ -1, NULL }, /* NETBEUI_SESS_TIMEOUT */ +{ -1, NULL }, /* NETBEUI_SESS_ABORT */ +{ -1, NULL }, /* NETBEUI_SESS_HANGUP */ +{ -1, NULL }, /* NETBEUI_SESS_END */ +{ -1, NULL }, /* NETBEUI_SESS_FIRST_MIDDLE_CONT */ +{ -1, NULL }, /* NETBEUI_SESS_FIRST_MIDDLE */ +{ -1, NULL }, /* NETBEUI_SESS_CONTINUE */ +{ -1, NULL }, /* NETBEUI_SESS_NONBLOCK */ +{ -1, NULL }, /* NETBEUI_SESS_PAUSE */ +{ -1, NULL }, /* NETBEUI_SESS_PAUSE2 */ +{ -1, NULL }, /* NETBEUI_SESS_RESTART */ +{ -1, NULL }, /* NETBEUI_SESS_ONLY_LAST_ACK */ +{ -1, NULL }, /* NETBEUI_SESS_ONLY_LAST */ +{ -1, NULL }, /* NETBEUI_SESS_DATA_ACKED */ +{ -1, NULL }, /* NETBEUI_SESS_RESOURCE */ +{ -1, NULL }, /* NETBEUI_SESS_CONN_RETRY */ +{ -1, NULL }, /* NETBEUI_SESS_NORM_RETRY */ +{ -1, NULL } /* NETBEUI_SESS_ABORT_SEND */ +}, + /* NETBEUI_SESS_CALLWAIT */ +{ +{ -1, NULL }, /* NETBEUI_SESS_CALL */ +{ -1, NULL }, /* NETBEUI_SESS_LISTEN */ +{ NETBEUI_SESS_CONFWAIT, + nbss_event_confirm_in_callwait },/* NETBEUI_SESS_CONFIRM */ +{ NETBEUI_SESS_INITIAL, + nbss_event_reject_in_callwait },/* NETBEUI_SESS_REJECT */ +{ -1, NULL }, /* NETBEUI_SESS_CONNECT */ +{ -1, NULL }, /* NETBEUI_SESS_TIMEOUT */ +{ NETBEUI_SESS_INITIAL, + nbss_event_abort_in_callwait }, /* NETBEUI_SESS_ABORT */ +{ -1, NULL }, /* NETBEUI_SESS_HANGUP */ +{ -1, NULL }, /* NETBEUI_SESS_END */ +{ -1, NULL }, /* NETBEUI_SESS_FIRST_MIDDLE_CONT */ +{ -1, NULL }, /* NETBEUI_SESS_FIRST_MIDDLE */ +{ -1, NULL }, /* NETBEUI_SESS_CONTINUE */ +{ -1, NULL }, /* NETBEUI_SESS_NONBLOCK */ +{ -1, NULL }, /* NETBEUI_SESS_PAUSE */ +{ -1, NULL }, /* NETBEUI_SESS_PAUSE2 */ +{ -1, NULL }, /* NETBEUI_SESS_RESTART */ +{ -1, NULL }, /* NETBEUI_SESS_ONLY_LAST_ACK */ +{ -1, NULL }, /* NETBEUI_SESS_ONLY_LAST */ +{ -1, NULL }, /* NETBEUI_SESS_DATA_ACKED */ +{ -1, NULL }, /* NETBEUI_SESS_RESOURCE */ +{ -1, NULL }, /* NETBEUI_SESS_CONN_RETRY */ +{ -1, NULL }, /* NETBEUI_SESS_NORM_RETRY */ +{ -1, NULL } /* NETBEUI_SESS_ABORT_SEND */ +}, + /* NETBEUI_SESS_CONFWAIT */ +{ +{ -1, NULL }, /* NETBEUI_SESS_CALL */ +{ -1, NULL }, /* NETBEUI_SESS_LISTEN */ +{ -1, NULL }, /* NETBEUI_SESS_CONFIRM */ +{ -1, NULL }, /* NETBEUI_SESS_REJECT */ +{ NETBEUI_SESS_CONNECTED, + nbss_event_connect_in_confwait },/* NETBEUI_SESS_CONNECT */ +{ NETBEUI_SESS_INITIAL, + nbss_event_timeout_in_confwait },/* NETBEUI_SESS_TIMEOUT */ +{ NETBEUI_SESS_INITIAL, + nbss_event_abort_in_confwait }, /* NETBEUI_SESS_ABORT */ +{ -1, NULL }, /* NETBEUI_SESS_HANGUP */ +{ -1, NULL }, /* NETBEUI_SESS_END */ +{ -1, NULL }, /* NETBEUI_SESS_FIRST_MIDDLE_CONT */ +{ -1, NULL }, /* NETBEUI_SESS_FIRST_MIDDLE */ +{ -1, NULL }, /* NETBEUI_SESS_CONTINUE */ +{ -1, NULL }, /* NETBEUI_SESS_NONBLOCK */ +{ -1, NULL }, /* NETBEUI_SESS_PAUSE */ +{ -1, NULL }, /* NETBEUI_SESS_PAUSE2 */ +{ -1, NULL }, /* NETBEUI_SESS_RESTART */ +{ -1, NULL }, /* NETBEUI_SESS_ONLY_LAST_ACK */ +{ -1, NULL }, /* NETBEUI_SESS_ONLY_LAST */ +{ -1, NULL }, /* NETBEUI_SESS_DATA_ACKED */ +{ -1, NULL }, /* NETBEUI_SESS_RESOURCE */ +{ -1, NULL }, /* NETBEUI_SESS_CONN_RETRY */ +{ -1, NULL }, /* NETBEUI_SESS_NORM_RETRY */ +{ -1, NULL } /* NETBEUI_SESS_ABORT_SEND */ +}, + /* NETBEUI_SESS_LISTENWAIT */ +{ +{ -1, NULL }, /* NETBEUI_SESS_CALL */ +{ -1, NULL }, /* NETBEUI_SESS_LISTEN */ +{ NETBEUI_SESS_INITWAIT, + nbss_event_confirm_in_listenwait },/* NETBEUI_SESS_CONFIRM */ +{ NETBEUI_SESS_INITIAL, + nbss_event_reject_in_listenwait },/* NETBEUI_SESS_REJECT */ +{ -1, NULL }, /* NETBEUI_SESS_CONNECT */ +{ -1, NULL }, /* NETBEUI_SESS_TIMEOUT */ +{ -1, NULL }, /* NETBEUI_SESS_ABORT */ +{ -1, NULL }, /* NETBEUI_SESS_HANGUP */ +{ -1, NULL }, /* NETBEUI_SESS_END */ +{ -1, NULL }, /* NETBEUI_SESS_FIRST_MIDDLE_CONT */ +{ -1, NULL }, /* NETBEUI_SESS_FIRST_MIDDLE */ +{ -1, NULL }, /* NETBEUI_SESS_CONTINUE */ +{ -1, NULL }, /* NETBEUI_SESS_NONBLOCK */ +{ -1, NULL }, /* NETBEUI_SESS_PAUSE */ +{ -1, NULL }, /* NETBEUI_SESS_PAUSE2 */ +{ -1, NULL }, /* NETBEUI_SESS_RESTART */ +{ -1, NULL }, /* NETBEUI_SESS_ONLY_LAST_ACK */ +{ -1, NULL }, /* NETBEUI_SESS_ONLY_LAST */ +{ -1, NULL }, /* NETBEUI_SESS_DATA_ACKED */ +{ -1, NULL }, /* NETBEUI_SESS_RESOURCE */ +{ -1, NULL }, /* NETBEUI_SESS_CONN_RETRY */ +{ -1, NULL }, /* NETBEUI_SESS_NORM_RETRY */ +{ -1, NULL } /* NETBEUI_SESS_ABORT_SEND */ +}, + /* NETBEUI_SESS_INITWAIT */ +{ +{ -1, NULL }, /* NETBEUI_SESS_CALL */ +{ -1, NULL }, /* NETBEUI_SESS_LISTEN */ +{ -1, NULL }, /* NETBEUI_SESS_CONFIRM */ +{ NETBEUI_SESS_INITIAL, + nbss_event_reject_in_initwait },/* NETBEUI_SESS_REJECT */ +{ NETBEUI_SESS_CONNECTED, + nbss_event_connect_in_initwait },/* NETBEUI_SESS_CONNECT */ +{ NETBEUI_SESS_LISTENWAIT, + nbss_event_timeout_in_initwait },/* NETBEUI_SESS_TIMEOUT */ +{ NETBEUI_SESS_LISTENWAIT, + nbss_event_abort_in_initwait }, /* NETBEUI_SESS_ABORT */ +{ -1, NULL }, /* NETBEUI_SESS_HANGUP */ +{ -1, NULL }, /* NETBEUI_SESS_END */ +{ -1, NULL }, /* NETBEUI_SESS_FIRST_MIDDLE_CONT */ +{ -1, NULL }, /* NETBEUI_SESS_FIRST_MIDDLE */ +{ -1, NULL }, /* NETBEUI_SESS_CONTINUE */ +{ -1, NULL }, /* NETBEUI_SESS_NONBLOCK */ +{ -1, NULL }, /* NETBEUI_SESS_PAUSE */ +{ -1, NULL }, /* NETBEUI_SESS_PAUSE2 */ +{ -1, NULL }, /* NETBEUI_SESS_RESTART */ +{ -1, NULL }, /* NETBEUI_SESS_ONLY_LAST_ACK */ +{ -1, NULL }, /* NETBEUI_SESS_ONLY_LAST */ +{ -1, NULL }, /* NETBEUI_SESS_DATA_ACKED */ +{ -1, NULL }, /* NETBEUI_SESS_RESOURCE */ +{ -1, NULL }, /* NETBEUI_SESS_CONN_RETRY */ +{ -1, NULL }, /* NETBEUI_SESS_NORM_RETRY */ +{ -1, NULL } /* NETBEUI_SESS_ABORT_SEND */ +}, + /* NETBEUI_SESS_CONNECTED */ +{ +{ -1, NULL }, /* NETBEUI_SESS_CALL */ +{ -1, NULL }, /* NETBEUI_SESS_LISTEN */ +{ -1, NULL }, /* NETBEUI_SESS_CONFIRM */ +{ -1, NULL }, /* NETBEUI_SESS_REJECT */ +{ -1, NULL }, /* NETBEUI_SESS_CONNECT */ +{ -1, NULL }, /* NETBEUI_SESS_TIMEOUT */ +{ NETBEUI_SESS_DISCWAIT, + nbss_event_abort_in_connected },/* NETBEUI_SESS_ABORT */ +{ NETBEUI_SESS_INITIAL, + nbss_event_hangup_in_connected },/* NETBEUI_SESS_HANGUP */ +{ NETBEUI_SESS_DISCWAIT, + nbss_event_end_in_connected }, /* NETBEUI_SESS_END */ +{ NETBEUI_SESS_CONTWAIT, + nbss_event_first_middle_cont_in_connected }, /* NETBEUI_SESS_FIRST_MIDDLE_CONT */ +{ NETBEUI_SESS_NORMAL, + nbss_event_first_middle_in_connected }, /* NETBEUI_SESS_FIRST_MIDDLE */ +{ -1, NULL }, /* NETBEUI_SESS_CONTINUE */ +{ -1, NULL }, /* NETBEUI_SESS_NONBLOCK */ +{ -1, NULL }, /* NETBEUI_SESS_PAUSE */ +{ -1, NULL }, /* NETBEUI_SESS_PAUSE2 */ +{ -1, NULL }, /* NETBEUI_SESS_RESTART */ +{ NETBEUI_SESS_ACKWAIT, + nbss_event_only_last_ack_in_connected }, /* NETBEUI_SESS_ONLY_LAST_ACK */ +{ NETBEUI_SESS_CONNECTED, + nbss_event_only_last_in_connected },/* NETBEUI_SESS_ONLY_LAST */ +{ -1, NULL }, /* NETBEUI_SESS_DATA_ACKED */ +{ NETBEUI_SESS_RSRCWAIT, + nbss_event_resource_in_connected },/* NETBEUI_SESS_RESOURCE */ +{ -1, NULL }, /* NETBEUI_SESS_CONN_RETRY */ +{ -1, NULL }, /* NETBEUI_SESS_NORM_RETRY */ +{ -1, NULL } /* NETBEUI_SESS_ABORT_SEND */ +}, + /* NETBEUI_SESS_DISCWAIT */ +{ +{ -1, NULL }, /* NETBEUI_SESS_CALL */ +{ -1, NULL }, /* NETBEUI_SESS_LISTEN */ +{ -1, NULL }, /* NETBEUI_SESS_CONFIRM */ +{ -1, NULL }, /* NETBEUI_SESS_REJECT */ +{ -1, NULL }, /* NETBEUI_SESS_CONNECT */ +{ -1, NULL }, /* NETBEUI_SESS_TIMEOUT */ +{ -1, NULL }, /* NETBEUI_SESS_ABORT */ +{ NETBEUI_SESS_INITIAL, + nbss_event_hangup_in_discwait }, /* NETBEUI_SESS_HANGUP */ +{ -1, NULL }, /* NETBEUI_SESS_END */ +{ -1, NULL }, /* NETBEUI_SESS_FIRST_MIDDLE_CONT */ +{ -1, NULL }, /* NETBEUI_SESS_FIRST_MIDDLE */ +{ -1, NULL }, /* NETBEUI_SESS_CONTINUE */ +{ -1, NULL }, /* NETBEUI_SESS_NONBLOCK */ +{ -1, NULL }, /* NETBEUI_SESS_PAUSE */ +{ -1, NULL }, /* NETBEUI_SESS_PAUSE2 */ +{ -1, NULL }, /* NETBEUI_SESS_RESTART */ +{ -1, NULL }, /* NETBEUI_SESS_ONLY_LAST_ACK */ +{ -1, NULL }, /* NETBEUI_SESS_ONLY_LAST */ +{ -1, NULL }, /* NETBEUI_SESS_DATA_ACKED */ +{ -1, NULL }, /* NETBEUI_SESS_RESOURCE */ +{ -1, NULL }, /* NETBEUI_SESS_CONN_RETRY */ +{ -1, NULL }, /* NETBEUI_SESS_NORM_RETRY */ +{ -1, NULL } /* NETBEUI_SESS_ABORT_SEND */ +}, + /* NETBEUI_SESS_CONTWAIT */ +{ +{ -1, NULL }, /* NETBEUI_SESS_CALL */ +{ -1, NULL }, /* NETBEUI_SESS_LISTEN */ +{ -1, NULL }, /* NETBEUI_SESS_CONFIRM */ +{ -1, NULL }, /* NETBEUI_SESS_REJECT */ +{ -1, NULL }, /* NETBEUI_SESS_CONNECT */ +{ -1, NULL }, /* NETBEUI_SESS_TIMEOUT */ +{ -1, NULL }, /* NETBEUI_SESS_ABORT */ +{ -1, NULL }, /* NETBEUI_SESS_HANGUP */ +{ -1, NULL }, /* NETBEUI_SESS_END */ +{ -1, NULL }, /* NETBEUI_SESS_FIRST_MIDDLE_CONT */ +{ -1, NULL }, /* NETBEUI_SESS_FIRST_MIDDLE */ +{ NETBEUI_SESS_NORMAL, + nbss_event_continue_in_contwait },/* NETBEUI_SESS_CONTINUE */ +{ NETBEUI_SESS_CONNECTED, + nbss_event_nonblock_in_contwait },/* NETBEUI_SESS_NONBLOCK */ +{ NETBEUI_SESS_STANDWAIT, + nbss_event_pause_in_contwait }, /* NETBEUI_SESS_PAUSE */ +{ -1, NULL }, /* NETBEUI_SESS_PAUSE2 */ +{ NETBEUI_SESS_NORMAL, + nbss_event_restart_in_contwait },/* NETBEUI_SESS_RESTART */ +{ -1, NULL }, /* NETBEUI_SESS_ONLY_LAST_ACK */ +{ -1, NULL }, /* NETBEUI_SESS_ONLY_LAST */ +{ -1, NULL }, /* NETBEUI_SESS_DATA_ACKED */ +{ -1, NULL }, /* NETBEUI_SESS_RESOURCE */ +{ -1, NULL }, /* NETBEUI_SESS_CONN_RETRY */ +{ -1, NULL }, /* NETBEUI_SESS_NORM_RETRY */ +{ NETBEUI_SESS_CONNECTED, + nbss_event_abort_send_in_all } /* NETBEUI_SESS_ABORT_SEND */ +}, + /* NETBEUI_SESS_STANDWAIT */ +{ +{ -1, NULL }, /* NETBEUI_SESS_CALL */ +{ -1, NULL }, /* NETBEUI_SESS_LISTEN */ +{ -1, NULL }, /* NETBEUI_SESS_CONFIRM */ +{ -1, NULL }, /* NETBEUI_SESS_REJECT */ +{ -1, NULL }, /* NETBEUI_SESS_CONNECT */ +{ -1, NULL }, /* NETBEUI_SESS_TIMEOUT */ +{ -1, NULL }, /* NETBEUI_SESS_ABORT */ +{ -1, NULL }, /* NETBEUI_SESS_HANGUP */ +{ -1, NULL }, /* NETBEUI_SESS_END */ +{ -1, NULL }, /* NETBEUI_SESS_FIRST_MIDDLE_CONT */ +{ -1, NULL }, /* NETBEUI_SESS_FIRST_MIDDLE */ +{ -1, NULL }, /* NETBEUI_SESS_CONTINUE */ +{ -1, NULL }, /* NETBEUI_SESS_NONBLOCK */ +{ -1, NULL }, /* NETBEUI_SESS_PAUSE */ +{ NETBEUI_SESS_STANDWAIT, + nbss_event_pause2_in_standwait },/* NETBEUI_SESS_PAUSE2 */ +{ NETBEUI_SESS_NORMAL, + nbss_event_restart_in_standwait },/* NETBEUI_SESS_RESTART */ +{ -1, NULL }, /* NETBEUI_SESS_ONLY_LAST_ACK */ +{ -1, NULL }, /* NETBEUI_SESS_ONLY_LAST */ +{ -1, NULL }, /* NETBEUI_SESS_DATA_ACKED */ +{ -1, NULL }, /* NETBEUI_SESS_RESOURCE */ +{ -1, NULL }, /* NETBEUI_SESS_CONN_RETRY */ +{ -1, NULL }, /* NETBEUI_SESS_NORM_RETRY */ +{ NETBEUI_SESS_CONNECTED, + nbss_event_abort_send_in_all } /* NETBEUI_SESS_ABORT_SEND */ +}, + /* NETBEUI_SESS_ACKWAIT */ +{ +{ -1, NULL }, /* NETBEUI_SESS_CALL */ +{ -1, NULL }, /* NETBEUI_SESS_LISTEN */ +{ -1, NULL }, /* NETBEUI_SESS_CONFIRM */ +{ -1, NULL }, /* NETBEUI_SESS_REJECT */ +{ -1, NULL }, /* NETBEUI_SESS_CONNECT */ +{ -1, NULL }, /* NETBEUI_SESS_TIMEOUT */ +{ -1, NULL }, /* NETBEUI_SESS_ABORT */ +{ -1, NULL }, /* NETBEUI_SESS_HANGUP */ +{ -1, NULL }, /* NETBEUI_SESS_END */ +{ -1, NULL }, /* NETBEUI_SESS_FIRST_MIDDLE_CONT */ +{ -1, NULL }, /* NETBEUI_SESS_FIRST_MIDDLE */ +{ -1, NULL }, /* NETBEUI_SESS_CONTINUE */ +{ NETBEUI_SESS_CONNECTED, + nbss_event_nonblock_in_ackwait },/* NETBEUI_SESS_NONBLOCK */ +{ NETBEUI_SESS_STANDWAIT, + nbss_event_pause_in_ackwait }, /* NETBEUI_SESS_PAUSE */ +{ -1, NULL }, /* NETBEUI_SESS_PAUSE2 */ +{ NETBEUI_SESS_NORMAL, + nbss_event_restart_in_ackwait },/* NETBEUI_SESS_RESTART */ +{ -1, NULL }, /* NETBEUI_SESS_ONLY_LAST_ACK */ +{ -1, NULL }, /* NETBEUI_SESS_ONLY_LAST */ +{ NETBEUI_SESS_CONNECTED, + nbss_event_data_acked_in_ackwait },/* NETBEUI_SESS_DATA_ACKED */ +{ -1, NULL }, /* NETBEUI_SESS_RESOURCE */ +{ -1, NULL }, /* NETBEUI_SESS_CONN_RETRY */ +{ -1, NULL }, /* NETBEUI_SESS_NORM_RETRY */ +{ NETBEUI_SESS_CONNECTED, + nbss_event_abort_send_in_all } /* NETBEUI_SESS_ABORT_SEND */ +}, + /* NETBEUI_SESS_RSRCAIT */ +{ +{ -1, NULL }, /* NETBEUI_SESS_CALL */ +{ -1, NULL }, /* NETBEUI_SESS_LISTEN */ +{ -1, NULL }, /* NETBEUI_SESS_CONFIRM */ +{ -1, NULL }, /* NETBEUI_SESS_REJECT */ +{ -1, NULL }, /* NETBEUI_SESS_CONNECT */ +{ -1, NULL }, /* NETBEUI_SESS_TIMEOUT */ +{ -1, NULL }, /* NETBEUI_SESS_ABORT */ +{ -1, NULL }, /* NETBEUI_SESS_HANGUP */ +{ -1, NULL }, /* NETBEUI_SESS_END */ +{ -1, NULL }, /* NETBEUI_SESS_FIRST_MIDDLE_CONT */ +{ -1, NULL }, /* NETBEUI_SESS_FIRST_MIDDLE */ +{ -1, NULL }, /* NETBEUI_SESS_CONTINUE */ +{ -1, NULL }, /* NETBEUI_SESS_NONBLOCK */ +{ -1, NULL }, /* NETBEUI_SESS_PAUSE */ +{ -1, NULL }, /* NETBEUI_SESS_PAUSE2 */ +{ -1, NULL }, /* NETBEUI_SESS_RESTART */ +{ -1, NULL }, /* NETBEUI_SESS_ONLY_LAST_ACK */ +{ -1, NULL }, /* NETBEUI_SESS_ONLY_LAST */ +{ -1, NULL }, /* NETBEUI_SESS_DATA_ACKED */ +{ -1, NULL }, /* NETBEUI_SESS_RESOURCE */ +{ NETBEUI_SESS_CONNECTED, + nbss_event_conn_retry_in_rsrcwait },/* NETBEUI_SESS_CONN_RETRY */ +{ NETBEUI_SESS_NORMAL, + nbss_event_norm_retry_in_rsrcwait },/* NETBEUI_SESS_NORM_RETRY */ +{ NETBEUI_SESS_CONNECTED, + nbss_event_abort_send_in_all } /* NETBEUI_SESS_ABORT_SEND */ +}, + /* NETBEUI_SESS_NORMAL */ +{ +{ -1, NULL }, /* NETBEUI_SESS_CALL */ +{ -1, NULL }, /* NETBEUI_SESS_LISTEN */ +{ -1, NULL }, /* NETBEUI_SESS_CONFIRM */ +{ -1, NULL }, /* NETBEUI_SESS_REJECT */ +{ -1, NULL }, /* NETBEUI_SESS_CONNECT */ +{ -1, NULL }, /* NETBEUI_SESS_TIMEOUT */ +{ -1, NULL }, /* NETBEUI_SESS_ABORT */ +{ -1, NULL }, /* NETBEUI_SESS_HANGUP */ +{ -1, NULL }, /* NETBEUI_SESS_END */ +{ -1, NULL }, /* NETBEUI_SESS_FIRST_MIDDLE_CONT */ +{ NETBEUI_SESS_NORMAL, + nbss_event_first_middle_in_normal },/* NETBEUI_SESS_FIRST_MIDDLE */ +{ -1, NULL }, /* NETBEUI_SESS_CONTINUE */ +{ NETBEUI_SESS_CONNECTED, + nbss_event_nonblock_in_normal },/* NETBEUI_SESS_NONBLOCK */ +{ NETBEUI_SESS_STANDWAIT, + nbss_event_pause_in_normal }, /* NETBEUI_SESS_PAUSE */ +{ -1, NULL }, /* NETBEUI_SESS_PAUSE2 */ +{ NETBEUI_SESS_NORMAL, + nbss_event_restart_in_normal }, /* NETBEUI_SESS_RESTART */ +{ NETBEUI_SESS_ACKWAIT, + nbss_event_only_last_ack_in_normal },/* NETBEUI_SESS_ONLY_LAST_ACK */ +{ NETBEUI_SESS_CONNECTED, + nbss_event_only_last_in_normal },/* NETBEUI_SESS_ONLY_LAST */ +{ -1, NULL }, /* NETBEUI_SESS_DATA_ACKED */ +{ NETBEUI_SESS_RSRCWAIT, + nbss_event_resource_in_normal },/* NETBEUI_SESS_RESOURCE */ +{ -1, NULL }, /* NETBEUI_SESS_CONN_RETRY */ +{ -1, NULL }, /* NETBEUI_SESS_NORM_RETRY */ +{ NETBEUI_SESS_CONNECTED, + nbss_event_abort_send_in_all} /* NETBEUI_SESS_ABORT_SEND */ +} +}; + +/* Session service state machine functions */ +/* + * Function: nbss_free_session + * Deallocates memory used for session_t and does a complete housekeeping + * Parameters: + * session: pointer to session_t memory to be freed + * Returns: None + */ +static void nbss_free_session(session_t *session) +{ + skb_queue_purge(&session->back_log); + skb_queue_purge(&session->i_skbq); + kfree(session); +} + +static inline void session_put(session_t *session) +{ + if (atomic_dec_and_test(&session->refcnt)) + nbss_free_session(session); +} + +static inline void session_hold(session_t *session) +{ + atomic_inc(&session->refcnt); +} + +static inline void nbss_wait_event(session_t *session) +{ + set_current_state(TASK_UNINTERRUPTIBLE); + if (!nbss_release_session(session)) + wait_event(session->waitq, (!session->o_txed || + session->o_aborted)); + else + set_current_state(TASK_RUNNING); +} + +/* + * Function: NETBEUI_CALC_SESS_MTU + * Calculates MAXimum length of user data that can send through a + * session, and sets the 'mtu' field of it. + * Parameters: + * sn: pointer to session that calculation is for it. + * Returns: None + */ +static void NETBEUI_CALC_SESS_MTU(session_t *sn) +{ + session_hold(sn); + sn->mtu = sn->dev->type == ARPHRD_LOOPBACK ? 1500 : sn->dev->mtu; + if (sn->dev->type == ARPHRD_IEEE802) { /* Token Ring */ + __u16 dolfb; + + switch (sn->tr_frame_lf) { + case 0x00: dolfb = 516; break; + case 0x01: dolfb = 1470; break; + case 0x02: dolfb = 2052; break; + case 0x03: dolfb = 4399; break; + case 0x04: dolfb = 8130; break; + case 0x05: dolfb = 11407; break; + case 0x06: dolfb = 17749; break; + case 0x07: + default: dolfb = 0xFFFF; break; + } + if (sn->mtu > dolfb) + sn->mtu = dolfb; + } + sn->mtu = sn->mtu - sn->llcmac_ihl - NETBEUI_ILEN; + session_put(sn); +} + +/* + * Function: nbss_alloc_session + * Allocates a session_t structure and does completely initialize all + * fields + * Parameters: None + * Returns: + * NULL : if can not allocate memory for session_t + * non-NULL: pointer to session_t + * Notes: + * - Session timer is initialized but not started. + * - The call to memset does implicitly initialize all fields. Those + * fields that need explicit non-zero initialization are manipulated + * afterwards. + */ +static session_t *nbss_alloc_session(void) +{ + session_t *session = kmalloc(sizeof(*session), GFP_KERNEL); + + if (!session) + goto out; + /* Implicitly initialize all fields */ + memset(session, 0, sizeof(*session)); + init_timer(&session->timer); + session->timer.data = (unsigned long)session; + session->timer.function = nbss_wait_timer_function; + init_waitqueue_head(&session->waitq); + session->version = NETBEUI_VERSION_2xx; + session->nack_indicator = NETBEUI_NACK_ABLE; + skb_queue_head_init(&session->back_log); + skb_queue_head_init(&session->i_skbq); + session->o_receive_continue = 1; + session->i_rcvbuf = 0xFFFF; + atomic_set(&session->refcnt, 1); +out: return session; +} + +/* + * Function: nbss_ack_bytes + * Acknowledges reception of some bytes from input stream, by correctly + * setting control variables of session. + * Parameters: + * session: pointer to session_t whose bytes are to be acknowledged. + * Returns: None + * Notes: + * - This function is used in transition handlers. + * - It does not actually acknowledge byte, it simply justifies + * acknowledgement control variables. + */ +static void nbss_ack_bytes(session_t *session) +{ + unsigned short r_nacked; + + session_hold(session); + r_nacked = session->o_txed - session->r_acked; + session->o_size += r_nacked; + session->o_buff -= r_nacked; + session->o_acked += session->r_acked; + session->o_txed = session->r_acked = 0; + session_put(session); +} + +/* + * Function: nbss_sleep_on + * This is a process control routine and customized version of Linux + * kernel sleep_on for session operations. It works in conjunction + * with session locking facilities to provide a secure mechanism for + * process control. + * Parameters: + * session: pointer to session_t whose process is to be controlled + * state : Linux state parameter that describes how to sleep. Usually + * TASK_INTERRUPTIBLE or TASK_UNINTERRUPTIBLE are values passed + * for this argument. + * Returns: None + * Notes: + * - The only difference between nbss_sleep_on and sleep_on is in the + * decision made before schedule() which in nbss_sleep_on is determined + * by nbss_release_session(). The reason is in abrupt termination of a + * session while the sleeping procedure runs. This routines integrates + * conditions so that race conditions are avoided. + */ +static inline void nbss_sleep_on(session_t *session, int state) +{ + wait_queue_t wait; + + init_waitqueue_entry(&wait, current); + set_current_state(state); + add_wait_queue(&session->waitq, &wait); + + if (!nbss_release_session(session)) { + dprintk("about to call schedule\n"); + schedule(); +#if WAITQUEUE_DEBUG + dprintk("sleeper=%p, waker=%lx\n", + current_text_addr(), wait.__waker); +#endif + } else + set_current_state(TASK_RUNNING); + remove_wait_queue(&session->waitq, &wait); +} + +/* + * Function: nbss_add_session_to_list + * Inserts a previously allocated/initialized session_t into session + * pending list. + * Parameters: + * session: pointer to session_t to add to pending session list + * Returns: None + */ +static void __nbss_add_session_to_list(session_t *session) +{ + session->next = session_list; + session_list = session; +} + +static void nbss_add_session_to_list(session_t *session) +{ + spin_lock_bh(&session_list_lock); + __nbss_add_session_to_list(session); + spin_unlock_bh(&session_list_lock); +} + +/* + * Function: nbss_remove_session_from_list + * Removes a session from pending session list + * Parameters: + * session: Pointer to session_t to remove from pending session list + * Returns: None + */ +static void nbss_remove_session_from_list(session_t *session) +{ + session_t *entry, *prev_entry = NULL; + + spin_lock_bh(&session_list_lock); + entry = session_list; + while (entry) { + if (entry == session) { + if (prev_entry) + prev_entry->next = entry->next; + else + session_list = entry->next; + } + prev_entry = entry; + entry = entry->next; + } + spin_unlock_bh(&session_list_lock); +} + +/* + * Function: nbss_find_listen + * Finds a session_t in session pending list who listens to a specific + * name. + * Parameters: + * name: pointer to NetBIOS name the session listens to + * Returns: + * NULL : if no pending session found listening to name + * non-NULL: pointer to a pending session in list who listens to name + */ +static session_t *nbss_find_listen(unsigned char *name) +{ + session_t *session; + + spin_lock_bh(&session_list_lock); + session = session_list; + while (session) { + if (session->state == NETBEUI_SESS_LISTENWAIT && + !memcmp(session->local_name->name, name, NETBEUI_NAME_LEN)) + break; + session = session->next; + } + if (session) + session_hold(session); + spin_unlock_bh(&session_list_lock); + return session; +} + +/* + * Function: nbss_unicast_name_recognized + * Prepares a NetBIOS NAME RECOGNIZED frame and nbll_uisends it to network + * Parameters: + * session: pointer to session_t structure the frame should be built from + * this session usually is selected from pending session list. + * Returns: + * 0 : if frame is successfully transmitted to network. + * non-zero: if frame transmission encountered an error (usually at NDI + * layer) + * Notes: + * - NetBIOS NAME RECOGNIZED frame is the response to a NetBIOS NAME QUERY + * frame received from network. The interface which accepts NAME QUERY + * frame passes the sk_buff via session->skb to reuse it for NAME + * RECOGNIZED frame, thus reducing memory consumption. + */ +static int nbss_unicast_name_recognized(session_t *session) +{ + /* It is supposed that name query skb is put in session->skb f/ reuse */ + dgram_t *hdr; + int rc; + + session_hold(session); + hdr = (dgram_t *)session->skb->data; + hdr->length = nb_cmd_hdr_len[NETBEUI_NAME_RECOGNIZED]; + hdr->command = NETBEUI_NAME_RECOGNIZED; + hdr->data1 = 0; + hdr->data2 = NETBEUI_CALL_DATA2(session->local_name->type, + session->lsn); + hdr->xmit_correlator = hdr->resp_correlator; + hdr->resp_correlator = + session->resp_correlator = nbss_next_correlator(); + memcpy(hdr->dest_name, hdr->source_name, NETBEUI_NAME_LEN); + memcpy(hdr->source_name, session->local_name->name, NETBEUI_NAME_LEN); + rc = nbll_uisend(session->remote_mac, session->skb); + session_put(session); + return rc; +} + +/* + * Function: nbss_alloc_session_skb + * Allocates and prepares a skb for session service purposes + * Parameters: + * sn : pointer to session that sk_buff is allocated for it. + * len : length of data in skb + * priority: a Linux kernel style memory allocation policy flag from + * GFP_* (GFP_KERNEL, GF_ATOMIC, GFP_USER, ...) family. + * Returns: + * NULL : if can not allocate memory for sk_buff + * non-NULL: pointer to sk_buff + */ +static struct sk_buff *nbss_alloc_session_skb(session_t *sn, int len, + int priority) +{ + struct sk_buff *skb; + + session_hold(sn); + skb = alloc_skb(len + sn->llcmac_ihl, priority); + if (skb) { + skb_reserve(skb, sn->llcmac_ihl); + skb->nh.raw = skb->h.raw = skb->data; + } + session_put(sn); + return skb; +} + +/* + * Function: nbss_ack_with_data + * Acknowledges incoming bytes with an outgoing frame by justifying data + * control bytes. + * Parameters: + * session: pointer to session_t, the acknowledgement is generated for + * hdr : pointer to packet_t header of outgoing frame. + * Returns: None + */ +static void nbss_ack_with_data(session_t *session, packet_t *hdr) +{ + session_hold(session); + barrier(); + if (session->o_ack_correlator) { + del_timer(&session->timer); + NETBEUI_INCLUDE_ACK_WITH_DATA(hdr); + hdr->xmit_correlator = (unsigned short) + session->o_ack_correlator; + session->o_ack_correlator = 0; + } + session_put(session); +} + +/* + * Function: nbss_isend_session_initialize + * Prepares a NetBIOS SESSION INITIALIZE frame and nbll_isends it toward + * remote session. + * Parameters: + * session: pointer to session_t structure the frame should be built for + * Returns: + * 0 : if frame is successfully transmitted to network. + * -ENOMEM: if memory allocation for sk_buff failed. + * other : if frame transmission encountered an error + * Notes: + * - NetBIOS NAME RECOGNIZED frame is the response to a NetBIOS NAME QUERY + * frame received from network. The interface which accepts NAME QUERY + * frame passes the sk_buff via session->skb to reuse it for NAME + * RECOGNIZED frame, thus reducing memory consumption. + */ +static int nbss_isend_session_initialize(session_t *session) +{ + int session_packet_len = nb_cmd_hdr_len[NETBEUI_SESSION_INITIALIZE]; + struct sk_buff *skb; + packet_t *hdr; + int rc = -ENOMEM; + + session_hold(session); + skb = nbss_alloc_session_skb(session, session_packet_len, GFP_ATOMIC); + if (!skb) + goto out; + hdr = (packet_t *)skb_put(skb, session_packet_len); + hdr->length = session_packet_len; + hdr->delimiter = NETBEUI_DELIMITER; + hdr->command = NETBEUI_SESSION_INITIALIZE; + hdr->data1 = 0x81; /* NetBIOS 2.2 with ability to + handle nack */ + /* Token Ring support */ + NETBEUI_CALC_SESS_MTU(session); + hdr->data1 |= (session->tr_frame_lf << 1) & 0x0E; + hdr->data2 = session->mtu; + hdr->xmit_correlator = session->xmit_correlator; + hdr->resp_correlator = + session->resp_correlator = nbss_next_correlator(); + hdr->dest_num = session->rsn; + hdr->source_num = session->lsn; + rc = nbll_isend(session->link, skb); +out: session_put(session); + return rc; +} + +/* + * Function: nbss_isend_session_confirm + * Prepares a NetBIOS SESSION CONFIRM frame and nbll_isends it toward + * remote session. + * Parameters: + * session : pointer to session_t structure the frame should be built for + * Returns: + * 0 : if frame is successfully transmitted to remote session. + * non-zero: if frame transmission encountered an error + * Notes: + * - NetBIOS SESSION INITIALIZE frame is the response to a NetBIOS SESSION + * CONFIRM frame received from remote session. The interface which + * accepts SESSION INITIALIZE frame passes the sk_buff via session->skb + * to reuse it for SESSION CONFIRM frame, thus reducing memory + * consumption. + */ +static int nbss_isend_session_confirm(session_t *session) +{ + /* I suppose get_session_initialize has set session->skb for reuse */ + packet_t *hdr; + int rc; + + session_hold(session); + hdr = (packet_t *)session->skb->data; + hdr->length = nb_cmd_hdr_len[NETBEUI_SESSION_CONFIRM]; + hdr->command = NETBEUI_SESSION_CONFIRM; + hdr->data1 = 0x81; /* NetBIOS 2.2 with ability to + handle nack */ + hdr->data2 = session->mtu; + session->resp_correlator = hdr->resp_correlator; + hdr->resp_correlator = session->xmit_correlator; + hdr->xmit_correlator = session->resp_correlator; + hdr->dest_num = session->rsn; + hdr->source_num = session->lsn; + rc = nbll_isend(session->link, session->skb); + session_put(session); + return rc; +} + +/* + * Function: nbss_isend_data_first_middle + * Prepares a NetBIOS DATA FIRST MIDDLE frame and nbll_isends it toward + * remote session. + * Parameters: + * session: pointer to session_t structure the frame should be built for + * cflow : flag that indicates requesting RECEIVE CONTINUE from remote + * session: + * zero means do not request RECEIVE CONTINUE + * non-zero means request RECEIVE CONTINUE + * Returns: + * 0 : if frame is successfully transmitted to remote session. + * -ENOMEM: if memory allocation for sk_buff failed. + * other : if frame transmission encountered an error + */ +static int nbss_isend_data_first_middle(session_t *session, int cflow) +{ + int session_packet_len = nb_cmd_hdr_len[NETBEUI_DATA_FIRST_MIDDLE]; + struct sk_buff *skb; + packet_t *hdr; + int rc = -ENOMEM; + + session_hold(session); + skb = nbss_alloc_session_skb(session, session_packet_len + session->mtu, + GFP_ATOMIC); + if (!skb) + goto out; + hdr = (packet_t *)skb_put(skb, session_packet_len); + hdr->length = session_packet_len; + hdr->delimiter = NETBEUI_DELIMITER; + hdr->command = NETBEUI_DATA_FIRST_MIDDLE; + hdr->data1 = 0; + hdr->data2 = session->o_receive_outstanding; + session->o_receive_outstanding = 0; + hdr->xmit_correlator = 0; + hdr->resp_correlator = 0; + hdr->dest_num = session->rsn; + hdr->source_num = session->lsn; + + if (cflow) { + NETBEUI_REQUEST_RECEIVE_CONTINUE(hdr); + session->o_receive_continue = 0; + hdr->resp_correlator = + session->resp_correlator = nbss_next_correlator(); + } + if (session->o_noack) + NETBEUI_INDICATE_NACK(hdr); + rc = -EFAULT; + if (copy_from_user(skb_put(skb, session->mtu), session->o_buff, + session->mtu)) + goto err; + nbss_ack_with_data(session, hdr); + rc = nbll_isend(session->link, skb); +out: session_put(session); + return rc; +err: kfree_skb(skb); + goto out; +} + +/* + * Function: nbss_isend_data_only_last + * Prepares a NetBIOS DATA ONLY LAST frame and nbll_isends it toward remote + * session. + * Parameters: + * session: pointer to session_t structure the frame should be built for + * Returns: + * 0 : if frame is successfully transmitted to remote session. + * -ENOMEM: if memory allocation for sk_buff failed. + * other : if frame transmission encountered an error + */ +static int nbss_isend_data_only_last(session_t *session) +{ + int session_packet_len = nb_cmd_hdr_len[NETBEUI_DATA_ONLY_LAST]; + struct sk_buff *skb; + packet_t *hdr; + int rc = -ENOMEM; + + session_hold(session); + skb = nbss_alloc_session_skb(session, session_packet_len + + session->o_size, GFP_ATOMIC); + if (!skb) + goto out; + hdr = (packet_t *)skb_put(skb, session_packet_len); + hdr->length = session_packet_len; + hdr->delimiter = NETBEUI_DELIMITER; + hdr->command = NETBEUI_DATA_ONLY_LAST; + hdr->data1 = 0; + hdr->data2 = session->o_receive_outstanding; + session->o_receive_outstanding = 0; + hdr->xmit_correlator = 0; + hdr->resp_correlator = 0; + hdr->dest_num = session->rsn; + hdr->source_num = session->lsn; + + if (session->o_noack) + NETBEUI_INDICATE_NACK(hdr); + else { + hdr->resp_correlator = session->resp_correlator = + nbss_next_correlator(); + NETBEUI_ALLOW_ACK_WITH_DATA(hdr); + } + rc = -EFAULT; + if (copy_from_user(skb_put(skb, session->o_size), session->o_buff, + session->o_size)) + goto err; + nbss_ack_with_data(session, hdr); + rc = nbll_isend(session->link, skb); +out: session_put(session); + return rc; +err: kfree_skb(skb); + goto out; +} + +/* + * Function: nbss_isend_receive_continue + * Prepares a NetBIOS RECEIVE CONTINUE frame and nbll_isends it toward + * remote session. + * Parameters: + * session: pointer to session_t structure the frame should be built for + * hdr : pointer to packet_t header of the incoming frame which the + * RECEIVE CONTINUE frame is built for. + * Returns: + * 0 : if frame is successfully transmitted to remote session. + * -ENOMEM: if memory allocation for sk_buff failed. + * other : if frame transmission encountered an error + */ +static int nbss_isend_receive_continue(packet_t *source_hdr, session_t *session) +{ + packet_t *hdr; + int session_packet_len = nb_cmd_hdr_len[NETBEUI_RECEIVE_CONTINUE]; + struct sk_buff *skb; + int rc = -ENOMEM; + + session_hold(session); + skb = nbss_alloc_session_skb(session, session_packet_len, GFP_ATOMIC); + if (!skb) + goto out; + hdr = (packet_t *)skb_put(skb, session_packet_len); + hdr->length = session_packet_len; + hdr->delimiter = NETBEUI_DELIMITER; + hdr->command = NETBEUI_RECEIVE_CONTINUE; + hdr->data1 = 0; + hdr->data2 = 0; + hdr->xmit_correlator = source_hdr->resp_correlator; + hdr->resp_correlator = 0; + hdr->dest_num = session->rsn; + hdr->source_num = session->lsn; + rc = nbll_isend(session->link, skb); +out: session_put(session); + return rc; +} + +/* + * Function: nbss_isend_no_receive + * Prepares a NetBIOS RECEIVE CONTINUE frame and nbll_isends it toward + * remote session. + * Parameters: + * session: pointer to session_t structure the frame should be built for + * skb : pointer to sk_buff to use for building frame. This sk_buff is + * the one that contained incoming frame and since its data is not + * accepted from input stream, it is passed for reuse. + * Returns: + * 0 : if frame is successfully transmitted to remote session. + * -ENOMEM: if memory allocation for sk_buff failed. + * other : if frame transmission encountered an error + */ +static int nbss_isend_no_receive(struct sk_buff *skb, session_t *session) +{ + int session_packet_len = nb_cmd_hdr_len[NETBEUI_NO_RECEIVE]; + packet_t *hdr; + unsigned char data1; + int rc; + + session_hold(session); + data1 = NETBEUI_NACK_INDICATOR(((packet_t *)skb->data)); + skb_trim(skb, 0); + hdr = (packet_t *)skb_put(skb, session_packet_len); + hdr->length = session_packet_len; + hdr->delimiter = NETBEUI_DELIMITER; + hdr->command = NETBEUI_NO_RECEIVE; + hdr->data1 = data1; + hdr->data2 = session->i_notacked; + hdr->xmit_correlator = 0; + hdr->resp_correlator = 0; + hdr->dest_num = session->rsn; + hdr->source_num = session->lsn; + + rc = nbll_isend(session->link, skb); + if (!rc) { + session->i_state = NETBEUI_RECV_NO_RECEIVE; + session->i_notacked = 0; + } + session_put(session); + return rc; +} + +/* + * Function: nbss_isend_receive_outstanding + * Prepares a NetBIOS RECEIVE OUTSTANDING frame and nbll_isends it toward + * remote session. + * Parameters: + * session: pointer to session_t structure the frame should be built for + * Returns: + * 0 : if frame is successfully transmitted to remote session. + * -ENOMEM: if memory allocation for sk_buff failed. + * other : if frame transmission encountered an error + * Notes: + * - Since the frame is transmitted within a user process context, + * and and works with input state variable, it locks a session during + * transmission period. + * - no need to hold refcnt, only called from nbss_receive that already + * does that + */ +static int nbss_isend_receive_outstanding(session_t *session) +{ + int session_packet_len = nb_cmd_hdr_len[NETBEUI_RECEIVE_OUTSTANDING]; + packet_t *hdr; + int rc = -ENOMEM; + struct sk_buff *skb = nbss_alloc_session_skb(session, + session_packet_len, + GFP_KERNEL); + if (!skb) + goto out; + hdr = (packet_t *)skb_put(skb, session_packet_len); + hdr->length = session_packet_len; + hdr->delimiter = NETBEUI_DELIMITER; + hdr->command = NETBEUI_RECEIVE_OUTSTANDING; + hdr->data1 = 0; + hdr->data2 = 0; + hdr->xmit_correlator = 0; + hdr->resp_correlator = 0; + hdr->dest_num = session->rsn; + hdr->source_num = session->lsn; + nbss_lock_session(session); + + rc = nbll_isend(session->link, skb); + if (!rc) + session->i_state = NETBEUI_RECV_RECEIVE_OUTSTANDING; + nbss_release_session(session); +out: return rc; +} + +/* + * Function: nbss_isend_data_ack + * Prepares a NetBIOS DATA ACK frame and nbll_isends it toward remote + * session. + * Parameters: + * session : pointer to session_t structure the frame should be + * built for + * ack_correlator: this is the xmit correlator found in the incoming frame + * passed to be put in response correlator of the DATA ACK + * Returns: + * 0 : if frame is successfully transmitted to remote session. + * -ENOMEM: if memory allocation for sk_buff failed. + * other : if frame transmission encountered an error + */ +static int nbss_isend_data_ack(session_t *session, + unsigned short ack_correlator) +{ + int session_packet_len = nb_cmd_hdr_len[NETBEUI_DATA_ACK]; + struct sk_buff *skb; + packet_t *hdr; + int rc = -ENOMEM; + + session_hold(session); + skb = nbss_alloc_session_skb(session, session_packet_len, GFP_ATOMIC); + if (!skb) + goto out; + hdr = (packet_t *)skb_put(skb, session_packet_len); + hdr->length = session_packet_len; + hdr->delimiter = NETBEUI_DELIMITER; + hdr->command = NETBEUI_DATA_ACK; + hdr->data1 = 0; + hdr->data2 = 0; + hdr->xmit_correlator = ack_correlator; + hdr->resp_correlator = 0; + hdr->dest_num = session->rsn; + hdr->source_num = session->lsn; + rc = nbll_isend(session->link, skb); +out: session_put(session); + return rc; +} + +/* + * Function: nbss_ack_session_data + * Actually acknowledges incoming session data depending on current + * condition either by sending DATA ACK frame or by postponing and sending + * ACK WITH DATA. + * Parameters: + * session: pointer to session_t structure the frame should be built for + * hdr : pointer to packet_t header of the incoming frame which the + * acknowledgement is built for. + * Returns: + * 0 : if frame is successfully transmitted to remote session. + * -ENOMEM: if memory allocation for sk_buff failed. + * other : if frame transmission encountered an error + */ +static int nbss_ack_session_data(packet_t *hdr, session_t *session) +{ + int rc = 0; + + session_hold(session); + if (NETBEUI_ACK_WITH_DATA_ALLOWED(hdr) && + !session->o_ack_correlator && !session->urgent_ack) { + session->o_ack_correlator = hdr->resp_correlator | + NETBEUI_ACK_FLAG; + session->timer.expires = jiffies + NETBEUI_DATA_ACK_TIMEOUT; + add_timer(&session->timer); + goto out; + } + rc = nbss_isend_data_ack(session, hdr->resp_correlator); +out: session_put(session); + return rc; +} + +/* + * Function: nbss_ack_timer_function + * Handles delayed acknowledgement for a session when its timer expires + * by sending a DATA ACK frame. + * Parameters: + * input: pointer to session_t structure whose timer is expired. + * Returns: None + * Notes: + * - We never re-enter LLC from its upper layer interface since it is + * not re-entrant. However ack timers expire and thus cause NetBEUI + * to send DATA ACK which re-enters LLC. + */ +static void nbss_ack_timer_function(unsigned long input) +{ + session_t *session = (session_t *)input; + + session_hold(session); + if (llc_in_progress || + nbss_isend_data_ack(session, session->o_ack_correlator)) { + session->timer.expires = jiffies + NETBEUI_DATA_ACK_TIMEOUT; + add_timer(&session->timer); + } else + session->o_ack_correlator = 0; + session_put(session); +} + +/* + * Function: nbss_isend_session_end + * Prepares a NetBIOS SESSION END frame and nbll_isends it toward remote + * session. + * Parameters: + * session : pointer to session_t structure the frame should be built for + * Returns: + * 0 : if frame is successfully transmitted to remote session. + * -ENOMEM: if memory allocation for sk_buff failed. + * other : if frame transmission encountered an error + */ +static int nbss_isend_session_end(session_t *session) +{ + int session_packet_len = nb_cmd_hdr_len[NETBEUI_SESSION_END]; + struct sk_buff *skb; + packet_t *hdr; + int rc = -ENOMEM; + + session_hold(session); + skb = nbss_alloc_session_skb(session, session_packet_len, GFP_ATOMIC); + if (!skb) + goto out; + hdr = (packet_t *)skb_put(skb, session_packet_len); + hdr->length = session_packet_len; + hdr->delimiter = NETBEUI_DELIMITER; + hdr->command = NETBEUI_SESSION_END; + hdr->data1 = 0; + hdr->data2 = 0; + hdr->xmit_correlator = 0; + hdr->resp_correlator = 0; + hdr->dest_num = session->rsn; + hdr->source_num = session->lsn; + rc = nbll_isend(session->link, skb); +out: session_put(session); + return rc; +} + +/* + * Function: nbns_handle_event + * This is the heart of Session Service State Machine, which performs a + * transition from current state of session element to new state based + * on event occurred and session state table contents. + * Parameters: + * event : An integer of NETBEUI_SESS_* family that implies type of event + * nb_name: pointer to name_t structure which the event occurred on + * Returns: None + * Notes: + * - The state changes before actions be executed. This is due to + * non deterministic behavior of actions which may sleep the current + * process, thus stopping the function in the mid-way. + */ +static void nbss_handle_event(session_event_t event, session_t *session) +{ + struct event_struct *ev; + + session_hold(session); + ev = &session_state_table[session->state][event]; + if (ev && ev->event_handler) { + unsigned char old_state = session->state; + + session->state = ev->next_state; + if (ev->event_handler(session)) + session->state = old_state; + } + session_put(session); +} + +/* + * Function: nbss_wait_timer_function + * This is the callback function triggered upon expiration of session + * wait timer used first during session establishment and then for + * delaying acknowledgements. + * Parameters: + * input: pointer to session_t structure whose timer is expired. + * Returns: None + * Notes: + * - This timer function, set in session allocation time is used during + * session establishment. + */ +static void nbss_wait_timer_function(unsigned long input) +{ + session_t *session = (session_t *)input; + + session_hold(session); + session->status = -ETIMEDOUT; + nbss_handle_event(NETBEUI_SESS_TIMEOUT, session); + session_put(session); +} + +/* + * Function: + * This is the callback function trigerred upon expiration of session + * resource timer used when resources (usually memory) are ended up. + * Parameters: + * input: pointer to session_t structure whose timer is expired. + * Returns: None + */ +static void nbss_resource_timer_function(unsigned long input) +{ + session_t *session = (session_t *)input; + + session_hold(session); + switch (session->o_rsrc_origin) { + case NETBEUI_ORIGIN_CONNECTED: + nbss_handle_event(NETBEUI_SESS_CONN_RETRY, session); + break; + case NETBEUI_ORIGIN_NORMAL: + nbss_handle_event(NETBEUI_SESS_NORM_RETRY, session); + break; + default: + printk(KERN_ERR "NetBEUI serious error: Resource " + "timer expired from %u\n", + session->o_rsrc_origin); + break; + } + session_put(session); +} + +/* + * Session service state machine functions + * Implementing transition actions + */ +static int nbss_event_abort_send_in_all(session_t *session) +{ + session->o_aborted = 1; + wake_up(&session->waitq); + return 0; +} + +static int nbss_event_call_in_initial(session_t *session) +{ + int rc = -1; + + session->status = nbll_attach_session(session, session->dev, + session->remote_mac); + if (session->status) + goto out; + rc = 0; + nbss_add_session_to_list(session); +out: return rc; +} + +static int nbss_event_listen_in_initial(session_t *session) +{ + __nbss_add_session_to_list(session); + return 0; +} + +static int nbss_event_confirm_in_callwait(session_t *session) +{ + int rc; + + session->status = nbll_link_session(session->link); + if (session->status) { + rc = session->status; + goto out; + } + session->status = nbss_isend_session_initialize(session); + rc = -ENOMEM; + if (session->status) + goto out; + rc = 0; + nbss_remove_session_from_list(session); + session->timer.expires = jiffies + NETBEUI_TRANSMIT_TIMEOUT * + NETBEUI_TRANSMIT_COUNT; + add_timer(&session->timer); + sleep_on(&session->waitq); +out: return rc; +} + +static int nbss_event_reject_in_callwait(session_t *session) +{ + nbss_remove_session_from_list(session); + nbll_detach_session(session->link, session->lsn); + session->status = -ECONNABORTED; + return 0; +} + +static int nbss_event_abort_in_callwait(session_t *session) +{ + nbss_remove_session_from_list(session); + session->status = -ECONNRESET; + return 0; +} + +static int nbss_event_connect_in_confwait(session_t *session) +{ + del_timer(&session->timer); + wake_up(&session->waitq); + session->status = 0; + return 0; +} + +static int nbss_event_timeout_in_confwait(session_t *session) +{ + nbll_detach_session(session->link, session->lsn); + wake_up(&session->waitq); + session->status = -ECONNREFUSED; + return 0; +} + +static int nbss_event_abort_in_confwait(session_t *session) +{ + del_timer(&session->timer); + wake_up(&session->waitq); + session->status = -ECONNRESET; + return 0; +} + +static int nbss_event_confirm_in_listenwait(session_t *session) +{ + int rc = -1; + + session->status = nbll_attach_session(session, session->dev, + session->remote_mac); + if (session->status) + goto out; + session->status = nbss_unicast_name_recognized(session); + if (session->status) { + nbll_detach_session(session->link, session->lsn); + goto out; + } + rc = 0; + session->timer.expires = jiffies + NETBEUI_TRANSMIT_TIMEOUT * + NETBEUI_TRANSMIT_COUNT; + add_timer(&session->timer); +out: return rc; +} + +static int nbss_event_reject_in_listenwait(session_t *session) +{ + nbss_remove_session_from_list(session); + session->status = -ECONNABORTED; + return 0; +} + +static int nbss_event_connect_in_initwait(session_t *session) +{ + int rc = -1; + + session->status = nbss_isend_session_confirm(session); + if (session->status) + goto out; + rc = 0; + del_timer(&session->timer); + nbss_remove_session_from_list(session); + session->session_ready_callback(session->owner, session); + session->status = 0; +out: return rc; +} + +static int nbss_event_timeout_in_initwait(session_t *session) +{ + nbll_detach_session(session->link, session->lsn); + session->status = -ECONNREFUSED; + return 0; +} + +static int nbss_event_abort_in_initwait(session_t *session) +{ + del_timer(&session->timer); + session->status = -ECONNRESET; + return 0; +} + +static int nbss_event_reject_in_initwait(session_t *session) +{ + del_timer(&session->timer); + nbll_detach_session(session->link, session->lsn); + nbss_remove_session_from_list(session); + session->status = -ECONNABORTED; + return 0; +} + +static int nbss_event_abort_in_connected(session_t *session) +{ + if (session->owner) + session->abort_owner_callback(session->owner, session); + session->status = -ECONNRESET; + return 0; +} + +static int nbss_event_hangup_in_connected(session_t *session) +{ + nbss_isend_session_end(session); + nbll_detach_session(session->link, session->lsn); + session->status = -ECONNABORTED; + return 0; +} + +static int nbss_event_end_in_connected(session_t *session) +{ + if (session->owner) + session->abort_owner_callback(session->owner, session); + nbll_detach_session(session->link, session->lsn); + session->status = -ECONNRESET; + return 0; +} + +static int nbss_event_hangup_in_discwait(session_t *session) +{ + return 0; +} + +static int nbss_event_first_middle_cont_in_connected(session_t *session) +{ + int rc = -1; + + session->status = nbss_isend_data_first_middle(session, 1); + if (session->status) + goto out; + rc = 0; + session->o_buff += session->mtu; + session->o_size -= session->mtu; + session->o_txed += session->mtu; + nbss_wait_event(session); +out: return rc; +} + +static int nbss_event_first_middle_in_connected(session_t *session) +{ + int rc = -1; + + session->status = nbss_isend_data_first_middle(session, 0); + if (session->status) + goto out; + rc = 0; + session->o_buff += session->mtu; + session->o_size -= session->mtu; + session->o_txed += session->mtu; +out: return rc; +} + +static int nbss_event_only_last_ack_in_connected(session_t *session) +{ + int rc = -1; + + session->status = nbss_isend_data_only_last(session); + if (session->status) + goto out; + rc = 0; + session->o_buff += session->o_size; + session->o_txed += session->o_size; + session->o_size -= session->o_size; + nbss_wait_event(session); +out: return rc; +} + +static int nbss_event_only_last_in_connected(session_t *session) +{ + int rc = -1; + + session->status = nbss_isend_data_only_last(session); + if (session->status) + goto out; + rc = 0; + session->o_buff += session->o_size; + session->o_txed += session->o_size; + session->o_size -= session->o_size; +out: return rc; +} + +static int nbss_event_resource_in_connected(session_t *session) +{ + session->o_rsrc_origin = NETBEUI_ORIGIN_CONNECTED; + session->timer.expires = jiffies + NETBEUI_RESOURCE_TIMEOUT; + session->timer.function = nbss_resource_timer_function; + add_timer(&session->timer); + sleep_on(&session->waitq); + return 0; +} + +static int nbss_event_continue_in_contwait(session_t *session) +{ + session->o_acked += session->o_txed; + session->o_txed = 0; + wake_up(&session->waitq); + return 0; +} + +static int nbss_event_restart_in_contwait(session_t *session) +{ + nbss_ack_bytes(session); + wake_up(&session->waitq); + return 0; +} + +static int nbss_event_pause_in_contwait(session_t *session) +{ + nbss_ack_bytes(session); + session->o_no_receive = 1; + wake_up(&session->waitq); + return 0; +} + +static int nbss_event_nonblock_in_contwait(session_t *session) +{ + nbss_ack_bytes(session); + session->o_no_receive = 1; + wake_up(&session->waitq); + return 0; +} + +static int nbss_event_restart_in_standwait(session_t *session) +{ + session->o_no_receive = 0; + wake_up(&session->waitq); + return 0; +} + +static int nbss_event_pause2_in_standwait(session_t *session) +{ + nbss_sleep_on(session, TASK_INTERRUPTIBLE); + return 0; +} + +static int nbss_event_data_acked_in_ackwait(session_t *session) +{ + session->o_acked += session->o_txed; + session->o_txed = 0; + wake_up(&session->waitq); + return 0; +} + +static int nbss_event_restart_in_ackwait(session_t *session) +{ + nbss_ack_bytes(session); + wake_up(&session->waitq); + return 0; +} + +static int nbss_event_pause_in_ackwait(session_t *session) +{ + nbss_ack_bytes(session); + session->o_no_receive = 1; + wake_up(&session->waitq); + return 0; +} + +static int nbss_event_nonblock_in_ackwait(session_t *session) +{ + nbss_ack_bytes(session); + session->o_no_receive = 1; + wake_up(&session->waitq); + return 0; +} + +static int nbss_event_norm_retry_in_rsrcwait(session_t *session) +{ + wake_up(&session->waitq); + return 0; +} + +static int nbss_event_conn_retry_in_rsrcwait(session_t *session) +{ + wake_up(&session->waitq); + return 0; +} + +static int nbss_event_first_middle_in_normal(session_t *session) +{ + int rc = -1; + + session->status = nbss_isend_data_first_middle(session, 0); + if (session->status) + goto out; + rc = 0; + session->o_buff += session->mtu; + session->o_size -= session->mtu; + session->o_txed += session->mtu; +out: return rc; +} + +static int nbss_event_restart_in_normal(session_t *session) +{ + nbss_ack_bytes(session); + return 0; +} + +static int nbss_event_only_last_ack_in_normal(session_t *session) +{ + int rc = -1; + + session->status = nbss_isend_data_only_last(session); + if (session->status) + goto out; + rc = 0; + session->o_buff += session->o_size; + session->o_txed += session->o_size; + session->o_size -= session->o_size; + nbss_wait_event(session); +out: return rc; +} + +static int nbss_event_only_last_in_normal(session_t *session) +{ + int rc = -1; + + session->status = nbss_isend_data_only_last(session); + if (session->status) + goto out; + rc = 0; + session->o_buff += session->o_size; + session->o_txed += session->o_size; + session->o_size -= session->o_size; +out: return rc; +} + +static int nbss_event_pause_in_normal(session_t *session) +{ + nbss_ack_bytes(session); + session->o_no_receive = 1; + return 0; +} + +static int nbss_event_nonblock_in_normal(session_t *session) +{ + nbss_ack_bytes(session); + session->o_no_receive = 1; + return 0; +} + +static int nbss_event_resource_in_normal(session_t *session) +{ + session->o_rsrc_origin = NETBEUI_ORIGIN_NORMAL; + session->timer.expires = jiffies + NETBEUI_RESOURCE_TIMEOUT; + session->timer.function = nbss_resource_timer_function; + add_timer(&session->timer); + sleep_on(&session->waitq); + return 0; +} + +/* + * Session service state machine functions + * Implementing interface functions + */ +/* + * Function: nbss_call + * This is a sophisticated interface routine for establishing a session + * with a remote hosts. Finding host that owns name, establishing session + * building LLC connection and configuring session are the phases a session + * element passes through them to prepare session for transferring data. + * Parameters: + * calling_name : pointer to name_t structure that the session is built + * either registered by client or NAME_NUMBER_1 auto bound + * called_name : pointer to NetBIOS name of remote node + * owner : pointer to a data structure of the owner of session + * used for calling its call backs + * itf_abort_owner: pointer to a callback routine for alerting owner, of + * session abortion + * session_ptr : (VRP) pointer to session_structure if return value + * indicates a valid value which is a pointer to session_t + * The owner later uses this value for getting services + * from Session Service module + * Returns: + * 0 : if a session successfully established to remote node + * -ENOMEM : if failed to allocate memory for session_t structure. + * -EHOSTUNREACH: if can not find a node on network who owns the name. + * -ETOONAMYREFS: if more than one node responded to session establishment + * request. This means that it can not establish session + * with group names. + * -ECONNREFUSED: if node responded to request but refused to establish a + * session anyway. + * -ECONNRESET : if connection reset by LLC before session establishment + * ends + * other : any other error reported by LLC. + */ +int nbss_call(name_t *calling_name, char *called_name, struct sock *owner, + abort_owner_cbt itf_abort_owner, session_t **session_ptr) +{ + int status; +#define MAX_FIND 3 + char macs[MAX_FIND][6]; + struct net_device *devs[MAX_FIND]; + session_t *session = nbss_alloc_session(); + int rc = -ENOMEM; + + if (!session) + goto out; + *session_ptr = NULL; + session->state = NETBEUI_SESS_INITIAL; + session->owner = owner; + session->local_name = calling_name; + memcpy(session->remote_name, called_name, NETBEUI_NAME_LEN); + session->abort_owner_callback = itf_abort_owner; + + rc = status = nbqs_find_name(called_name, (char *)macs, devs, MAX_FIND); + if (status < 0) + goto sput; + else if (!status) { + rc = -EHOSTUNREACH; + goto sput; + } else if (status != 1) { + rc = -ETOOMANYREFS; + goto sput; + } + session_hold(session); + session->dev = devs[0]; + memcpy(session->remote_mac, macs, session->dev->addr_len); + + for (;;) { + nbss_handle_event(NETBEUI_SESS_CALL, session); + if (session->state == NETBEUI_SESS_INITIAL) { + rc = status = session->status; + goto dsput; + } + session->status = nbqs_query_name(called_name, calling_name, + session->lsn, &session->rsn, + &session->tr_frame_lf, + &session->xmit_correlator); + /* We may have got a session abort here */ + if (session->state == NETBEUI_SESS_INITIAL) + continue; + else + break; + } + /* We may also could not query server */ + rc = -ECONNREFUSED; + if (session->status || !session->rsn || session->rsn == 0xFF) { + nbss_handle_event(NETBEUI_SESS_REJECT, session); + goto dsput; + } + /* Token Ring support */ + session->llcmac_ihl = LLCMAC_I_HEADLEN(session->dev); + nbss_handle_event(NETBEUI_SESS_CONFIRM, session); + if (session->state != NETBEUI_SESS_CONNECTED || session->status) { + status = session->status; + nbqs_delete_rnc(called_name); + rc = status ? : -ECONNRESET; + goto dsput; + } + rc = 0; + *session_ptr = session; +sput: session_put(session); +out: return rc; +dsput: session_put(session); + goto sput; +} + +/* + * Function: __nbss_listen + * Prepares backlog session_ts for a server who is going to listen on + * a name, kept in pending session list. Has to be called with + * session_list_lock held, with spin_lock_bh or spin_lock, depending + * on the context (process or BH). + * Parameters: + * nb_name : pointer to name_t the sessions listen to connection + * requests on it + * backlog : count of concurrent session establishments the + * session service should process at once. + * owner : pointer to a data structure of the owner of session + * used for calling its call backs + * itf_abort_owner : pointer to a callback routine for alerting owner, of + * session abortion + * itf_session_ready: pointer to callback routine alerting owner, of + * existence of an established session. + * Returns: + * >= 0: count of backlog pending session in list who listen to name for + * connection establishment requests. + * Notes: + * - The session does not guarantee to have as many pending sessions + * as requested by backlog parameter due to memory allocation fails. + * This should be considered by upper layer routines. + */ +int __nbss_listen(name_t *nb_name, int backlog, struct sock *owner, + abort_owner_cbt itf_abort_owner, + session_ready_cbt itf_session_ready) +{ + int count, exist = 0; + session_t *session = session_list; + + while (session) { + if (session->local_name == nb_name) + exist++; + session = session->next; + } + for (count = exist; count < backlog; count++) { + session = nbss_alloc_session(); + if (session) { + session->state = NETBEUI_SESS_INITIAL; + session->owner = owner; + session->local_name = nb_name; + session->abort_owner_callback = itf_abort_owner; + session->session_ready_callback = itf_session_ready; + nbss_handle_event(NETBEUI_SESS_LISTEN, session); + exist++; + } else + break; + } + return exist; +} + +int nbss_listen(name_t *nb_name, int backlog, struct sock *owner, + abort_owner_cbt itf_abort_owner, + session_ready_cbt itf_session_ready) +{ + int rc; + spin_lock(&session_list_lock); + rc = __nbss_listen(nb_name, backlog, owner, itf_abort_owner, + itf_session_ready); + spin_unlock(&session_list_lock); + return rc; +} + +int nbss_listen_bh(name_t *nb_name, int backlog, struct sock *owner, + abort_owner_cbt itf_abort_owner, + session_ready_cbt itf_session_ready) +{ + int rc; + spin_lock_bh(&session_list_lock); + rc = __nbss_listen(nb_name, backlog, owner, itf_abort_owner, + itf_session_ready); + spin_unlock_bh(&session_list_lock); + return rc; +} + +/* + * Function: nbss_end_listen + * Removes session_ts on pending session list who listen to session + * establishment requests on a specific name, called only from + * nbso_session_release, in process context, so no need to spin_lock_bh, + * plain spin_lock is enough. + * Parameters: + * nb_name: pointer to name which sessions listening to it should be + * removed from pending session list. + * Returns: None + */ +void __nbss_end_listen(name_t *nb_name) +{ + session_t *session = session_list; + + while (session) { + session_t *next_session = session->next; + + if (session->local_name == nb_name) { + nbss_handle_event(NETBEUI_SESS_REJECT, session); + session_put(session); + } + session = next_session; + } +} + +/* + * Function: nbss_send + * This is the heart of output data stream. + * Sends a block of user data to remote session by fragmenting and + * controlling flow of data. In addition to simplicity it operates + * a considerable part of Session Service State Machine by generating + * different events carefully + * Parameters: + * session : pointer to session_t to send data on + * buf : pointer to user buffer that contains data + * size : size of data in user buffer + * nonblock: flag that indicates to block user process or not block + * it (0/1) + * noack : flag that indicates to receive data receive acknowledgement + * or not (0/1) + * Returns: + * >= 0 : count of data bytes successfully sent to remote session + * -ECONNABORTED: if session aborted. + * -EINPROGRESS : if another process currently sends data in session this + * may happen if two process share a socket. + * -ETIME : if send timeout. this happens if interface layer calls + * nbss_send_abort when its timers expire. + */ +int nbss_send(session_t *session, unsigned char *buf, unsigned short size, + unsigned char nonblock, unsigned char noack) +{ + int rc = -ECONNABORTED; + + session_hold(session); + if (session->zapped) + goto out; + rc = -EINPROGRESS; + if (session->state != NETBEUI_SESS_CONNECTED) + goto out; + session->o_nonblock = nonblock; + session->o_noack = session->version == NETBEUI_VERSION_2xx ? + noack : 0; + session->o_no_receive = 0; + session->o_rsrc_origin = 0; + session->o_buff = buf; + session->o_buffsize = size; + session->o_size = size; + session->o_txed = 0; + session->o_acked = 0; + session->r_acked = 0; + session->o_aborted = 0; + while (session->o_acked < session->o_buffsize) { + nbss_lock_session(session); + session->status = 0; + + if (session->o_size > session->mtu && + session->o_size == session->o_buffsize && + session->o_receive_continue) + nbss_handle_event(NETBEUI_SESS_FIRST_MIDDLE_CONT, + session); + else if (session->o_size > session->mtu) + nbss_handle_event(NETBEUI_SESS_FIRST_MIDDLE, session); + else if (!noack && session->o_size > 0) + nbss_handle_event(NETBEUI_SESS_ONLY_LAST_ACK, session); + else if (session->o_size > 0) + nbss_handle_event(NETBEUI_SESS_ONLY_LAST, session); + rc = session->status; + if (nbss_release_session(session)) + goto out; + rc = -ETIME; + if (session->o_aborted) + goto out; + if (session->o_no_receive && session->o_nonblock) { + rc = session->o_acked; + break; + } + if (session->o_no_receive) { + nbss_lock_session(session); + nbss_handle_event(NETBEUI_SESS_PAUSE2, session); + nbss_release_session(session); + if (signal_pending(current)) { + rc = session->o_acked > 0 ? session->o_acked : + -ERESTART; + break; + } + } + if (session->status == -ENOMEM) { + nbss_lock_session(session); + nbss_handle_event(NETBEUI_SESS_RESOURCE, session); + nbss_release_session(session); + } + rc = session->o_acked; + } + session->state = NETBEUI_SESS_CONNECTED; + if (rc > size) + rc = size; + if (rc > 0) + session->o_total += rc; +out: session_put(session); + return rc; +} + +/* + * Function: nbss_send_zero + * Sends a zero byte DATA ONLY LAST frame to remote node. + * Parameters: + * session: pointer to session_t to send frame on + * buf : pointer to a user buffer that is referenced in transition + * handlers of state machine + * Returns: + * >= 0 : if size of data bytes successfully sent to remote session + * -ECONNABORTED: if session aborted. + * -EINPROGRESS : if another process currently sends data in session this + * may happen if two process share a socket. + * Notes: + * - This feature is activated via ioctl interface and has special + * meaning to SAMBA that operates over NetBEUI + */ +int nbss_send_zero(session_t *session, char *buf) +{ + int rc = -ECONNABORTED; + + session_hold(session); + if (session->zapped) + goto out; + rc = -EINPROGRESS; + if (session->state != NETBEUI_SESS_CONNECTED) + goto out; + session->o_nonblock = 1; + session->o_noack = 1; + session->o_no_receive = 0; + session->o_rsrc_origin = 0; + session->o_buff = buf; + session->o_buffsize = 0; + session->o_size = 0; + session->o_txed = 0; + session->o_acked = 0; + session->r_acked = 0; + session->o_aborted = 0; + session->status = 0; + nbss_handle_event(NETBEUI_SESS_ONLY_LAST, session); + rc = session->status; +out: session_put(session); + return rc; +} + +/* + * Function: nbss_abort_send + * The interface which aborts a send operation, usually called from + * upper interface when send timers expire. + * Parameters: + * session: pointer to session_t whose send operation is to be aborted. + * Returns: None + */ +void nbss_abort_send(session_t *session) +{ + session_hold(session); + session->o_aborted = 1; + nbss_handle_event(NETBEUI_SESS_ABORT_SEND, session); + session_put(session); +} + +/* + * Function: nbss_send_ready + * Determines if session is ready to send data or not + * Parameters: + * session: pointer to session_t. + * Returns: + * 1: if session is ready to send data + * 0: if session is not ready to send data + */ +int nbss_send_ready(session_t *session) +{ + int rc; + + session_hold(session); + rc = !session->zapped && + session->state == NETBEUI_SESS_CONNECTED ? 1 : 0; + session_put(session); + return rc; +} + +/* + * Function: nbss_receive + * Receives data from input stream buffer and copies it into user buffer. + * Parameters: + * session : pointer to session_t to receive data from + * buf : pointer to user space buffer to put data into + * size : maximum size of data to put in buf + * nonblock: flag that indicates whether to block user process if data was + * not available + * Returns: + * >= 0 : count of data bytes fetched from input stream queue + * -ECONNABORTED: if session aborted. + * -ETIME : if receive timeout. this happens if interface layer calls + * nbss_receive_abort when its timers expire. + * -ERESTART : if process caught a signal + * -EAGAIN : if zero data bytes received. + */ +int nbss_receive(session_t *session, unsigned char *buf, unsigned short size, + unsigned char nonblock) +{ + unsigned short i_size = size; + struct sk_buff *skb; + int rc = -ECONNABORTED; + int passed_2nd = 0; + + session_hold(session); + if (session->zapped && !skb_queue_len(&session->i_skbq)) + goto out; + session->i_aborted = 0; +try_read: + nbss_lock_session(session); + while (i_size > 0 && (skb = skb_peek(&session->i_skbq)) != NULL) { + if (passed_2nd) + dprintk("entering 1st loop after the 2nd...\n"); + passed_2nd = 0; + nbss_release_session(session); + if (i_size >= skb->len) { + rc = -EFAULT; + if (copy_to_user(buf, skb->data, skb->len)) + goto out; + i_size -= skb->len; + buf += skb->len; + nbss_lock_session(session); + __skb_unlink(skb, &session->i_skbq); + session->i_size -= skb->len; + kfree_skb(skb); + } else { + rc = -EFAULT; + if (copy_to_user(buf, skb->data, i_size)) + goto out; + skb_pull(skb, i_size); + buf += i_size; + nbss_lock_session(session); + session->i_size -= i_size; + i_size = 0; + } + } + while (i_size == size && !nonblock) { + dprintk("entering second loop...\n"); + passed_2nd = 1; + rc = 0; + if (session->i_state == NETBEUI_RECV_NO_RECEIVE && + nbss_isend_receive_outstanding(session)) { + dprintk("session->i_state == " + "NETBEUI_RECV_NO_RECEIVE && " + "nbss_isend_receive_outstanding(session)\n"); + goto out; + } + /* nbss_sleep_on calls nbss_release_session */ + nbss_sleep_on(session, TASK_INTERRUPTIBLE); + rc = -ERESTART; + if (signal_pending(current)) { + dprintk("signal_pending(current)\n"); + goto out; + } + if (session->i_size) { + dprintk("session->i_size != 0, goto try_read\n"); + goto try_read; + } + rc = -ECONNABORTED; + if (session->zapped) { + dprintk("session->zapped != 0\n"); + goto out; + } + rc = -ETIME; + if (session->i_aborted) { + dprintk("session->i_aborted != 0\n"); + goto out; + } + } + nbss_release_session(session); + rc = i_size == size ? -EAGAIN : (size - i_size); +out: session_put(session); + return rc; +} + +/* + * Function: nbss_abort_receive + * The interface which aborts a receive operation, usually called from + * upper interface when receive timers expire. + * Parameters: + * session: pointer to session_t whose send operation is to be aborted. + * Returns: None + */ +void nbss_abort_receive(session_t *session) +{ + session_hold(session); + session->i_aborted = 1; + session->status = -ETIME; + wake_up_interruptible(&session->waitq); + session_put(session); +} + +/* + * Function: nbss_receive_ready + * Determines if session has data in input stream queue or not + * Parameters: + * session: pointer to session_t. + * Returns: + * 1: if session is ready to send data + * 0: if session is not ready to send data + */ +int nbss_receive_ready(session_t *session) +{ + int rc; + session_hold(session); + rc = session->i_size; + session_put(session); + return rc; +} + +/* + * Function: nbss_trim_data + * Removes some bytes from a DATA ONLY LAST frame which is fetched + * before. + * Parameters: + * session: pointer to session_t. + * Returns: + * 0: always returns zero + * Notes: + * - This feature is activated via ioctl interface and has special + * meaning to SAMBA that operates over NetBEUI + * - skb->acked flags the frame as a DATA ONLY LAST frame + * - skb->used indicates that some of frame data is fetched + * Important Note: + * - Windows API implements NetBEUI socket interface of type SOCK_SEQPACKET + * and sends garbage at end of SMB messages. We have implemented + * SOCK_STREAM which accepts those garbages. The users process + * activates an IOCTL to remove unused data bytes from the stream of + * bytes. + * - Another solution is modifying socket read mechanism so that a process + * can read the whole message. This may significantly improve SAMBA reads + * but reduces NetBEUI extensibility. + */ +int nbss_trim_data(session_t *session) +{ + struct sk_buff *skb; + + session_hold(session); + skb = skb_peek(&session->i_skbq); + if (skb) { + nbss_lock_session(session); + __skb_unlink(skb, &session->i_skbq); + session->i_size -= skb->len; + nbss_release_session(session); + kfree_skb(skb); + } + session_put(session); + return 0; +} + +/* + * Function: nbss_hangup + * The upper layer interface which terminates a session by generating a + * proper event. + * Parameters: + * session: pointer to session_t which is to be terminated + * Returns: None + * Notes: + * - During session termination there may be some unacknowledged data in + * input stream queue whose acknowledgement is not a an action of session + * transition handlers, since session data input stream is not part of + * Session State Machine. + */ +void nbss_hangup(session_t *session) +{ + session_hold(session); + del_timer(&session->timer); + if (session->o_ack_correlator) + nbss_isend_data_ack(session, + (unsigned short)session->o_ack_correlator); + nbss_handle_event(NETBEUI_SESS_HANGUP, session); + session_put(session); + session_put(session); +} + +/* + * Function: nbss_abort_session + * This upper layer interface abnormally terminates a session. + * Parameters: + * session: pointer to session_t which is to be aborted + * Returns: None + */ +void nbss_abort_session(session_t *session) +{ + session_hold(session); + session->zapped = 1; + del_timer(&session->timer); + nbss_abort_receive(session); + nbss_handle_event(NETBEUI_SESS_ABORT_SEND, session); + nbss_handle_event(NETBEUI_SESS_ABORT, session); + session_put(session); +} + +/* + * Function: nbss_get_name_query + * Accepts a NAME QUERY frame and depending on FIND/QUERY characteristic + * of it either responds NAME FIND or generates an event to start + * session establishment. + * Parameters: + * skb : pointer to sk_buff that holds the frame + * remote_mac: pointer to MAC address of remote node who sent the frame + * Returns: None + * Notes: + * - NAME FIND and NAME QUERY use the same frame format with different + * flags and field values. + * - NAME FIND processing is simply responding with a NAME RECOGNIZED + * - NAME QUERY processing is doing the first step of session establishment + * - Since the skb is reused for generating response frame it is passed to + * element in session->skb + */ +void nbss_get_name_query(struct sk_buff *skb, unsigned char *remote_mac) +{ + dgram_t *hdr = (dgram_t *)skb->data; + name_t *nb_name = nbns_find_name(hdr->dest_name); + session_t *session = nbss_find_listen(hdr->dest_name); + session_t tmp_session; + + if (!session) { + session = &tmp_session; + session->local_name = nb_name; + session->rsn = 0; + atomic_set(&session->refcnt, 1); + session_hold(session); + } else + session->rsn = NETBEUI_CALL_SS(hdr->data2); + if (!nb_name) { + kfree_skb(skb); + goto out; + } + session->skb = skb; + session->dev = skb->dev; + memcpy(session->remote_mac, remote_mac, skb->dev->addr_len); + memcpy(session->remote_name, hdr->source_name, NETBEUI_NAME_LEN); + session->lsn = 0; + if (!session->rsn || session->rsn == 0xFF) + nbss_unicast_name_recognized(session); + else { + /* Token Ring support */ + session->llcmac_ihl = LLCMAC_I_HEADLEN(session->dev); + nbss_handle_event(NETBEUI_SESS_CONFIRM, session); + } + nbns_name_put(nb_name); +out: session_put(session); +} + +/* + * Function: nbss_switch_get_session_initialize + * Accepts a SESSION INITIALIZE frame and generates proper event. + * Parameters: + * skb : pointer to sk_buff that holds the frame + * session: pointer to session_t structure the frame is received from + * Returns: None + * Notes: + * - Due to implementation of events in NetBEUI whose data is put element + * temporary variable and just event identifier is passed to state + * machine, the routine first checks if the session is in a proper state + * to accept event or not. + * - Since the skb is reused for generating response frame it is passed to + * element in session->skb + * - At this point session_t element timer callback function changes to the + * callback that processes delayed acknowledgements of input data + */ +static void nbss_switch_get_session_initialize(struct sk_buff *skb, + session_t *session) +{ + packet_t *hdr; + unsigned short data2; + + hdr = (packet_t *)skb->data; + data2 = hdr->data2; + if (session->state != NETBEUI_SESS_INITWAIT || + session->resp_correlator != hdr->xmit_correlator) { + kfree_skb(skb); + goto out; + } + if (!NETBEUI_IS_ABLE_TO_HANDLE_NACK(hdr)) + session->nack_indicator = NETBEUI_NACK_NONE; + if (!NETBIOS_VERSION(hdr)) + session->version = NETBEUI_VERSION_1xx; + session->tr_frame_lf = NETBEUI_TR_FRAME_LF(hdr); + /* Token Ring support */ + NETBEUI_CALC_SESS_MTU(session); + if (session->mtu > data2) + session->mtu = data2; + session->skb = skb; + nbss_handle_event(NETBEUI_SESS_CONNECT, session); + /* From now on, the session timer is used for ACK piggy-backing */ + session->timer.function = nbss_ack_timer_function; + /* skb is reused, thus we don't need to free it */ +out:; +} + +/* + * Function: nbss_switch_get_session_confirm + * Accepts a SESSION confirm frame and generates proper event. + * Parameters: + * hdr : pointer to packet_t which is frame header in sk_buff + * session: pointer to session_t structure the frame is received from + * Returns: None + * Notes: + * - Due to implementation of events in NetBEUI whose data is put element + * temporary variable and just event identifier is passed to state + * machine, the routine first checks if the session is in a proper state + * to accept event or not. + * - At this point session_t element timer callback function changes to the + * callback that processes delayed acknowledgements of input data + */ +static void nbss_switch_get_session_confirm(packet_t *hdr, session_t *session) +{ + unsigned short data2; + + data2 = hdr->data2; + if (session->state != NETBEUI_SESS_CONFWAIT || + session->resp_correlator != hdr->xmit_correlator) + goto out; + if (!NETBEUI_IS_ABLE_TO_HANDLE_NACK(hdr)) + session->nack_indicator = NETBEUI_NACK_NONE; + if (!NETBIOS_VERSION(hdr)) + session->version = NETBEUI_VERSION_1xx; + if (session->mtu > data2) + session->mtu = data2; + nbss_handle_event(NETBEUI_SESS_CONNECT, session); + /* From now on, the session timer is used for ACK piggy-backing */ + session->timer.function = nbss_ack_timer_function; +out:; +} + +/* + * Function: nbss_switch_get_data_ack + * Accepts a DATA ACK frame and generates proper event. + * Parameters: + * hdr : pointer to packet_t which is frame header in sk_buff + * session: pointer to session_t structure the frame is received from + * Returns: None + */ +static void nbss_switch_get_data_ack(packet_t *hdr, session_t *session) +{ + if (session->resp_correlator == hdr->xmit_correlator) + nbss_handle_event(NETBEUI_SESS_DATA_ACKED, session); +} + +/* + * Function: nbss_switch_get_session_data + * Accepts DATA FIRST MIDDLE and DATA ONLY LAST frames and tries to process + * frame by checking input state, data acknowledgement, controlling flow of + * data and queueing frame in input stream queue. (It does more than you + * may think at first glance) + * Parameters: + * skb : pointer to sk_buff which holds the frame + * session: pointer to session_t structure the frame is received from + * Returns: None + * Notes: + * - The frame pointers are adjusted to user data when frame is queued. + * - skb->acked is set for DATA ONLY LAST frames and is used in trimming + * user data in nbss_trim_data(). It is since we adjust pointers to user + * data not NetBIOS header of frame. + */ +static void nbss_switch_get_session_data(struct sk_buff *skb, + session_t *session) +{ + packet_t *hdr; + + hdr = (packet_t *)skb->data; + if (NETBEUI_ACK_WITH_DATA_INCLUDED(hdr)) + nbss_switch_get_data_ack(hdr, session); + if (session->i_state == NETBEUI_RECV_NO_RECEIVE || + (session->i_state == NETBEUI_RECV_RECEIVE_OUTSTANDING && + NETBEUI_RESYNCH_INDICATOR(hdr) != 0x0001) || + (session->i_state == NETBEUI_RECV_NORMAL && + NETBEUI_RESYNCH_INDICATOR(hdr) != 0x0000)) { + kfree_skb(skb); + goto out; + } + session->i_state = NETBEUI_RECV_NORMAL; + + if (session->i_size > session->i_rcvbuf || + (hdr->command == NETBEUI_DATA_FIRST_MIDDLE && + NETBEUI_RECEIVE_CONTINUE_REQUESTED(hdr) && + nbss_isend_receive_continue(hdr, session)) || + (hdr->command == NETBEUI_DATA_ONLY_LAST && + !NETBEUI_NACK_INDICATOR(hdr) && + nbss_ack_session_data(hdr, session))) { + nbss_isend_no_receive(skb, session); + goto out; + } + skb_pull(skb, hdr->length); + __skb_queue_tail(&session->i_skbq, skb); + session->i_size += skb->len; + if (hdr->command == NETBEUI_DATA_FIRST_MIDDLE) { + if (!NETBEUI_RECEIVE_CONTINUE_REQUESTED(hdr)) + session->i_notacked += skb->len; + else + session->i_notacked = 0; + } else /* DATA ONLY LAST */ + session->i_notacked = 0; + + wake_up_interruptible(&session->waitq); + session->i_total += skb->len; +out:; +} + +/* + * Function: nbss_switch_get_receive_continue + * Accepts a RECEIVE CONTINUE frame and generates a proper event. + * Parameters: + * hdr : pointer to packet_t which is frame header in sk_buff + * session: pointer to session_t structure the frame is received from + * Returns: None + */ +static void nbss_switch_get_receive_continue(packet_t *hdr, session_t *session) +{ + if (session->resp_correlator == hdr->xmit_correlator) + nbss_handle_event(NETBEUI_SESS_CONTINUE, session); +} + +/* + * Function: nbss_switch_get_no_receive + * Accepts a NO RECEIVE frame and generates proper events. The session + * state diagram purpose different events for blocking and non-blocking + * sends + * Parameters: + * hdr : pointer to packet_t which is frame header in sk_buff + * session: pointer to session_t structure the frame is received from + * Returns: None + */ +static void nbss_switch_get_no_receive(packet_t *hdr, session_t *session) +{ + session->o_receive_continue = 1; + if (!session->o_nonblock) + nbss_handle_event(NETBEUI_SESS_PAUSE, session); + else + nbss_handle_event(NETBEUI_SESS_NONBLOCK, session); +} + +/* + * Function: nbss_switch_get_receive_outstanding + * Accepts a RECEIVE OUTSTANDING frame and generates a proper event. + * Parameters: + * hdr : pointer to packet_t which is frame header in sk_buff + * session: pointer to session_t structure the frame is received from + * Returns: None + */ +static void nbss_switch_get_receive_outstanding(packet_t *hdr, + session_t *session) +{ + if (session->version == NETBEUI_VERSION_1xx) + hdr->data2 = 0; + session->o_receive_outstanding = 1; + session->r_acked = hdr->data2; + nbss_handle_event(NETBEUI_SESS_RESTART, session); +} + +/* + * Function: nbss_switch_get_session_end + * Accepts a SESSION END frame and generates a proper event. + * Parameters: + * session: pointer to session_t structure the frame is received from + * Returns: None + */ +static void nbss_switch_get_session_end(session_t *session) +{ + session->zapped = 1; + del_timer(&session->timer); + nbss_abort_receive(session); + nbss_handle_event(NETBEUI_SESS_ABORT_SEND, session); + nbss_handle_event(NETBEUI_SESS_END, session); +} + +/* + * Function: nbss_switch_frame + * This is the I-frame dispatcher. + * Parameters: + * skb : pointer to sk_buff which holds the frame + * session: pointer to session_t structure the frame is received from + * Returns: None + * Notes: + * - This function has to be called with a reference held + */ +static void nbss_switch_frame(session_t *session, struct sk_buff *skb) +{ + packet_t *hdr = (packet_t *)skb->data; + + switch (hdr->command) { + case NETBEUI_DATA_FIRST_MIDDLE: + case NETBEUI_DATA_ONLY_LAST: + nbss_switch_get_session_data(skb, session); + goto out; /* SKB is reused */ + case NETBEUI_DATA_ACK: + nbss_switch_get_data_ack(hdr, session); + break; + case NETBEUI_RECEIVE_CONTINUE: + nbss_switch_get_receive_continue(hdr, session); + break; + case NETBEUI_NO_RECEIVE: + nbss_switch_get_no_receive(hdr, session); + break; + case NETBEUI_RECEIVE_OUTSTANDING: + nbss_switch_get_receive_outstanding(hdr, session); + break; + case NETBEUI_SESSION_INITIALIZE: + nbss_switch_get_session_initialize(skb, session); + goto out; /* SKB is reused */ + case NETBEUI_SESSION_CONFIRM: + nbss_switch_get_session_confirm(hdr, session); + break; + case NETBEUI_SESSION_END: + nbss_switch_get_session_end(session); + break; + } + kfree_skb(skb); +out:; +} + +/* + * Function: nbss_deliver_frame + * Accepts an I-Frame frame and depending on session locking state either + * queues the frame for later processing (when session unlocked) or calls + * frame dispatcher for direct processing. + * Parameters: + * session: pointer to session_t structure the frame is received from + * skb : pointer to sk_buff which holds the frame + * Returns: None + */ +void nbss_deliver_frame(session_t *session, struct sk_buff *skb) +{ + session_hold(session); + if (session->users) + __skb_queue_tail(&session->back_log, skb); + else + nbss_switch_frame(session, skb); + session_put(session); +} + +/* + * Function: nbss_drop_session + * Drops a specified session on a specified link. + * Parameters: + * link : an integer that indicates the link number. + * session_no : an integer that indicates the session number. + * Returns: int + * zero : if the session dropped successfully. + * negative: if operation fails. + * -EINVAL: at least one of the arguments is invalid. + */ +int nbss_drop_session(int link, int session_no) +{ + session_t *sn; + link_t *nb_link = nbll_get_link(link); + dextab_t *sn_tbl; + int rc = -EINVAL; + + if (!nb_link) /* Invalid link number */ + goto out; + sn_tbl = &nb_link->session_table; + spin_lock_bh(&sn_tbl->lock); + rc = -EINVAL; + if (session_no > sn_tbl->size) { /* Invalid session number */ + spin_unlock_bh(&sn_tbl->lock); + goto out; + } + sn = sn_tbl->addr[session_no]; + spin_unlock_bh(&sn_tbl->lock); + if (!sn) /* no such session */ + goto out; + session_hold(sn); + /* Announce the link manager */ + nbll_detach_session(link, session_no); + /* Announce the session state machine */ + nbss_abort_session(sn); + session_put(sn); +out: nbll_link_put(nb_link); + return rc; +} + +/* Session service state machine functions */ +/* + * Function: nbss_lock_session + * Locks a session while processing a critical region. + * Parameters: + * session: pointer to session_t structure which is to be locked + * Returns: None + * Notes: + * - Locking is a low cost mechanism for avoiding races when a critical + * region appears in code. + */ +static void nbss_lock_session(session_t *session) +{ + session->users++; + barrier(); +} + +/* + * Function: nbss_process_backlog + * Processes frames entered on a session while the session was locked it + * simply calls dispatcher for every queued frame. + * Parameters: + * session: pointer to session_t structure who may have some frames in its + * backlog. + * Returns: None + */ +static void nbss_process_backlog(session_t *session) +{ + struct sk_buff *skb; + + session_hold(session); + while ((skb = __skb_dequeue(&session->back_log)) != NULL) + nbss_switch_frame(session, skb); + session_put(session); +} + +/* + * Function: nbss_release_session + * Unlocks a previously locked session. + * Parameters: + * session : pointer to session_t structure which is to be unlocked + * Returns: + * 0 : if session is successfully unlocked. + * -ECONNABORTED: if session is aborted + * Notes: + * - The return value is important to nbss_sleep_on() who decides whether + * to re-schedule system or continue execution of current process. + */ +static int nbss_release_session(session_t *session) +{ + int rc = 0; + + barrier(); + if (!session->users) + goto out; + if (!--session->users) + nbss_process_backlog(session); + if (session->zapped) + rc = session->status = -ECONNABORTED; +out: return rc; +} Index: kernel-acme/net/netbeui/sock_dgram.c diff -u /dev/null kernel-acme/net/netbeui/sock_dgram.c:1.1.8.1 --- /dev/null Thu Nov 22 23:55:01 2001 +++ kernel-acme/net/netbeui/sock_dgram.c Tue Nov 6 20:13:49 2001 @@ -0,0 +1,458 @@ +/* + * sock_dgram.c - Contains functions that supply SOCK_DGRAM type sockets for + * NetBEUI protocol stack which their names has a 'nbso_dgram_' + * prefix. + * Notes: + * - VRP in comments is the acronym of "Value Result Parameter" + * + * Copyright (c) 1997 by Procom Technology, Inc. + * 2001 by Arnaldo Carvalho de Melo + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* SOCK_DGRAM Calls */ +/* + * Function: nbso_dgram_release + * Performs additional actions at release of SOCK_DGRAM sockets. + * + * Parameters: + * sock : pointer to socket that must be released. + * + * Returns: int + * 0 : in all cases. (this function always succeed) + */ +static int nbso_dgram_release(struct socket *sock) +{ + struct sock *sk = sock->sk; + + if (sk) { + sock_orphan(sk); + sock_hold(sk); + lock_sock(sk); + if (NB_SK(sk)->u.dg.namep) { + nbdg_del_name(NB_SK(sk)->u.dg.namep); + if (NB_SK(sk)->name) + nbns_del_name(NB_SK(sk)->name); + } + release_sock(sk); + sock_put(sk); + } + MOD_DEC_USE_COUNT; + return 0; +} + +/* + * Function: nbso_dgram_bind + * Performs additional actions at bind of SOCK_DGRAM sockets to names. + * + * Parameters: + * sock : pointer to socket that must bind a name to it. + * uaddr : pointer to 'struct sockaddr_netbeui' that contains + * information about sightly name. + * addr_len : length of 'struct sockaddr_netbeui'. + * + * Returns: int + * 0 : if name is binded to socket successfully. + * negative : if a fault occurs. + * -EINVAL : if socket binds already, or given name is not + * valid. + */ +static int nbso_dgram_bind(struct socket *sock, struct sockaddr *uaddr, + int addr_len) +{ + name_t *nb_name; + struct sock *sk = sock->sk; + struct sockaddr_netbeui *addr = (struct sockaddr_netbeui *)uaddr; + int rc = -EPERM; + + if (!capable(CAP_NET_BIND_SERVICE)) + goto out; + rc = -EINVAL; + if (NB_SK(sk)->u.dg.namep || addr->snb_addr.name[0] == '*') + goto out; + rc = nbns_add_name(addr->snb_addr.name, addr->snb_addr.name_type, + &nb_name); + if (rc) + goto out; + rc = nbdg_add_name(addr->snb_addr.name, sk->sleep, + &NB_SK(sk)->u.dg.namep); + if (rc) + nbns_del_name(nb_name); + else + NB_SK(sk)->name = nb_name; + nbns_name_put(nb_name); +out: return rc; +} + +/* + * Function: nbso_dgram_connect + * Performs additional actions at attach of SOCK_DGRAM sockets to a + * specified peer. + * + * Parameters: + * sock : pointer to socket that must attach to peer. + * uaddr : pointer to 'struct sockaddr_netbeui' that contains + * information about peer. + * addr_len : length of 'struct sockaddr_netbeui'. + * sflags : bitwise integer that contains socket flags. + * + * Returns: int + * 0 : if socket attaches to the specified peer successfully. + * negative : if a fault occurs. + * -EINVAL : means socket not bounded normally. + * -EACCES : permission denied for broadcasting. user must + * sets the SO_BROADCAST socket option to active, + * before try to connect to all ('*'). + */ +static int nbso_dgram_connect(struct socket *sock, struct sockaddr *uaddr, + int addr_len, int sflags) +{ + struct sock *sk = sock->sk; + struct sockaddr_netbeui *addr = (struct sockaddr_netbeui *)uaddr; + int rc = -EINVAL; + + lock_sock(sk); + if (!NB_SK(sk)->name) + goto out; + + if (uaddr && addr_len == sizeof(*addr)) { + rc = -EACCES; + if (addr->snb_addr.name[0] == '*' && !sk->broadcast) + goto out; + sock->state = SS_CONNECTING; + memcpy(&NB_SK(sk)->u.dg.conn_name, addr, sizeof(*addr)); + nbdg_register_peername(NB_SK(sk)->u.dg.namep, + NB_SK(sk)->u.dg.conn_name.snb_addr.name); + sock->state = SS_CONNECTED; + } else { /* Invalid address means detach from previous address */ + if (sock->state == SS_UNCONNECTED) + goto out_ok; + sock->state = SS_DISCONNECTING; + nbdg_deregister_peername(NB_SK(sk)->u.dg.namep); + sock->state = SS_UNCONNECTED; + } +out_ok: rc = 0; +out: release_sock(sk); + return rc; +} + +/* + * Function: nbso_dgram_getname + * Gets SOCK_DGRAM socket name or peer name that attached to it. + * + * Parameters: + * sock : pointer to socket that we need to name of it or its peer. + * uaddr : (VRP) pointer to 'struct sockaddr_netbeui' that be filled + * with requested information. + * uaddr_len : (VRP) pointer to an integer that returns length of + * 'struct sockaddr_netbeui'. + * peer : an integer that indicates type of request. + * + * Returns: int + * 0 : if requested name is retrieved successfully. + * negative : if a fault occurs. + * -ENOTCONN : name of peer was requested but socket has not + * any attachment. + * -EBADF : socket not bounded to a name but name of it + * was requested. + */ +static int nbso_dgram_getname(struct socket *sock, struct sockaddr *uaddr, + int *uaddr_len, int peer) +{ + struct sock *sk = sock->sk; + struct sockaddr_netbeui *addr = (struct sockaddr_netbeui *)uaddr; + int rc; + + *uaddr_len = sizeof(*addr); + lock_sock(sk); + if (peer) { + rc = -ENOTCONN; + if (sock->state != SS_CONNECTED) + goto out; + memcpy(addr, &NB_SK(sk)->u.dg.conn_name, sizeof(*addr)); + } else { + rc = -EBADF; + if (!NB_SK(sk)->u.dg.namep) + goto out; + memcpy(addr->snb_addr.name, NB_SK(sk)->u.dg.namep->name, + NETBEUI_NAME_LEN); + addr->snb_addr.name_type = NB_SK(sk)->name->type; + } + rc = 0; +out: release_sock(sk); + return rc; +} + +/* + * Function: nbso_dgram_poll + * Determines operational (particularly I/O) condition of SOCK_STREAM + * socket. + * + * Parameters: + * sock : pointer to socket that check it. + * sel_type : an integer that determines type of checking. + * wait : pointer to a particular structure that contains some + * wait queues. The system itself checks members of these + * wait queues for their time outs. we only sleep on this + * structure if there is not exist a categoric answer, so far. + * + * Returns: int + * 0 : means that however must wait. + * 1 : means that answer is positive or an error occurred. + */ +static unsigned int nbso_dgram_poll(struct file *file, struct socket *sock, + poll_table *wait) +{ + struct sock *sk = sock->sk; + unsigned int mask = POLLWRNORM; + + poll_wait(file, sk->sleep, wait); + + if (sk->err) + mask |= POLLERR; + if (!NB_SK(sk)->u.dg.namep) + mask |= POLLERR; + else + if (!nbdg_receive_ready(NB_SK(sk)->u.dg.namep)) + mask |= POLLIN | POLLRDNORM; + return mask; +} + +/* + * Function: nbso_dgram_ioctl + * Performs some particular operations on SOCK_DGRAM socket, that can not + * do with regular system calls. + * + * Parameters: + * sock : pointer to socket that action must perform on it. + * cmd : an integer that indicates type of operation. + * arg : this parameter often is a pointer to 'cmd' relative data + * structure that be used by it as an argument. + * + * Returns: int + * 0 : if cmd is performed successfully. + * negative : if a fault occurs. error codes that bubble to user are + * dependent to cmd. + */ +static int nbso_dgram_ioctl(struct socket *sock, unsigned int cmd, + unsigned long arg) +{ + struct sock *sk = sock->sk; + int rc = -EINVAL; + + lock_sock(sk); + if (cmd == SIOCRUWDGF) { + int len; + + if (!NB_SK(sk)->u.dg.namep) + goto out; + rc = -EFAULT; + if (get_user(len, (int *)arg)) + goto out; + rc = -EINVAL; + if (len < 0) + goto out; + rc = nbdg_remove_unwanted_dgf(NB_SK(sk)->u.dg.namep, len); + } +out: release_sock(sk); + return rc; +} + +/* + * Function: nbso_dgram_sendmsg + * Sends a DATAGRAM message through a SOCK_DGRAM socket to desired + * target(s). + * + * Parameters: + * sock : a pointer to socket that data sends through it. + * msg : a pointer to 'struct msghdr' that contains message body, + * target name and etc. + * Note: msg->msg_control AND msg->msg_controllen are per + * protocol magic fields, and in our sendmsg() they + * indicate alias name to use for message sender name + * instead of the name that socket binds to it. + * len : length of message all around. + * nonblock : an integer that if be set to non-zero value means that + * no waiting (sleeping, blocking & ...) acceptable during + * operation. + * sflags : bitwise integer that contains socket flags. + * + * Returns: int + * positive : indicates how many bytes of data was sent. + * negative : if a fault occurs. + * -EINVAL : if a flag specified (we do not support any + * flags), or socket not bounded normally, or + * given target name is not valid. + * -EACCES : permission denied for broadcasting. user must + * sets the SO_BROADCAST socket option to active, + * before try to broadcasts a message. + * -ENOTCONN : target name not given and socket is not + * attached to any peer. + */ +static int nbso_dgram_sendmsg(struct socket *sock, struct msghdr *msg, int len, + struct scm_cookie *scm) +{ + char *local_name, name_buff[NETBEUI_NAME_LEN]; + struct sock *sk = sock->sk; + int rc, noblock = msg->msg_flags & MSG_DONTWAIT; + struct sockaddr_netbeui *remote_addr; + + lock_sock(sk); + if (msg->msg_name) { + struct sockaddr_netbeui *addr = (struct sockaddr_netbeui *) + msg->msg_name; + rc = -EINVAL; + if (!NB_SK(sk)->name || msg->msg_namelen < sizeof(*addr) || + (addr->snb_family && addr->snb_family != AF_NETBEUI)) + goto out; + rc = -EACCES; + if (addr->snb_addr.name[0] == '*' && !sk->broadcast) + goto out; + remote_addr = addr; + } else { + rc = -ENOTCONN; + if (sock->state != SS_CONNECTED) + goto out; + remote_addr = &NB_SK(sk)->u.dg.conn_name; + } + if (msg->msg_control && msg->msg_controllen == NETBEUI_NAME_LEN) { + rc = -EFAULT; + if (copy_from_user(name_buff, msg->msg_control, + NETBEUI_NAME_LEN)) + goto out; + local_name = name_buff; + } else + local_name = NB_SK(sk)->name->name; + /* All things are good, so start to send data ... */ + rc = nbdg_send(sk, local_name, remote_addr->snb_addr.name, + remote_addr->snb_addr.name_type, msg->msg_iov, + len, noblock); +out: release_sock(sk); + return rc; +} + +/* + * Function: nbso_dgram_recvmsg + * Receives a DATAGRAM message through a SOCK_DGRAM socket. + * + * Parameters: + * sock : a pointer to socket that data receives through it. + * msg : (VRP) a pointer to 'struct msghdr' that at return contains + * message body, source name and etc. + * Note: msg->msg_control AND msg->msg_controllen are per + * protocol magic fields, and in our recvmsg() at return + * they indicate target name of received message, that + * for sockets which connect to all ('*') may be different + * from the socket name. + * size : MAXimum length of message all around. + * nonblock : an integer that if be set to non-zero value means that + * no waiting (sleeping, blocking & ...) acceptable during + * operation. + * sflags : bitwise integer that contains socket flags. + * addr_len : (VRP) a pointer to an integer that if it is not NULL, at + * return will be filled with length of 'struct + * sockaddr_netbeui'. + * + * Returns: int + * positive : indicates how many bytes of data was received. + * negative : if a fault occurs. + * -EINVAL : if a flag specified (we do not support any + * flags), or socket not bounded to a name + * normally. + */ +static int nbso_dgram_recvmsg(struct socket *sock, struct msghdr *msg, + int size, int flags, struct scm_cookie *scm) +{ + int iov_no, bytes_received; + struct iovec *iov; + int nonblock = msg->msg_flags & MSG_DONTWAIT; + char *dest_name = NULL, + *source_name = NULL; + struct sock *sk = sock->sk; + int rc = -EINVAL; + + lock_sock(sk); + if (!NB_SK(sk)->u.dg.namep) + goto out; + if (msg->msg_name) { + struct sockaddr_netbeui *addr = msg->msg_name; + + addr->snb_family = AF_NETBEUI; + addr->snb_addr.reserved = 0; + source_name = addr->snb_addr.name; + } + if (msg->msg_control && msg->msg_controllen == NETBEUI_NAME_LEN) { + rc = verify_area(VERIFY_WRITE, msg->msg_control, + NETBEUI_NAME_LEN); + if (rc) + goto out; + dest_name = msg->msg_control; + } + bytes_received = 0; + iov = msg->msg_iov; + iov_no = msg->msg_iovlen; + + /* All things are good, so start to receive data ... */ + while (iov_no--) { + rc = verify_area(VERIFY_WRITE, iov->iov_base, iov->iov_len); + if (rc) { + rc = bytes_received ? : rc; + break; + } + rc = nbdg_receive(NB_SK(sk)->u.dg.namep, source_name, dest_name, + iov->iov_base, iov->iov_len, nonblock); + if (rc < 0) { + rc = bytes_received ? : rc; + break; + } + bytes_received += rc; + ++iov; + rc = bytes_received; + } +out: release_sock(sk); + return rc; +} + +/* Dispatcher struct for SOCK_DGRAM calls */ +struct proto_ops SOCKOPS_WRAPPED(nbso_dgram_proto_ops) = { + family: PF_NETBEUI, + release: nbso_dgram_release, + bind: nbso_dgram_bind, + connect: nbso_dgram_connect, + socketpair: sock_no_socketpair, + accept: sock_no_accept, + getname: nbso_dgram_getname, + poll: nbso_dgram_poll, + ioctl: nbso_dgram_ioctl, + listen: sock_no_listen, + shutdown: sock_no_shutdown, + setsockopt: sock_no_setsockopt, + getsockopt: sock_no_getsockopt, + sendmsg: nbso_dgram_sendmsg, + recvmsg: nbso_dgram_recvmsg, +}; +#include +SOCKOPS_WRAP(nbso_dgram_proto, PF_NETBEUI); Index: kernel-acme/net/netbeui/sock_name.c diff -u /dev/null kernel-acme/net/netbeui/sock_name.c:1.1.8.1 --- /dev/null Thu Nov 22 23:55:01 2001 +++ kernel-acme/net/netbeui/sock_name.c Tue Nov 6 20:13:49 2001 @@ -0,0 +1,156 @@ +/* + * sock_name.c - Contains functions that implement first layer of socket + * interface for socket type SOCK_NAME. SOCK_NAME is a new + * socket type introduced in NetBEUI used as a interface for + * manipulating NetBIOS names under kernel control. + * + * Copyright (c) 1997 by Procom Technology, Inc. + * 2001 by Arnaldo Carvalho de Melo + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Socket interface layer + * SOCK_NAME interface routines + */ +/* + * Function: nbso_name_release + * Releases a SOCK_NAME by requesting Name Service to remove all names + * the socket owns. + * + * Parameters: + * sock : pointer to socket that must be released. + * + * Returns: + * 0 : always returns zero + * + * Notes: + * - The names a SOCK_NAME type socket owns are detected by their + * identifier which is socket memory address. + */ +static int nbso_name_release(struct socket *sock) +{ + struct sock *sk = sock->sk; + unsigned long id = (unsigned long)NB_SK(sk); + + nbns_del_identifier(id); + MOD_DEC_USE_COUNT; + return 0; +} + +/* + * Function: nbso_name_bind + * Manipulates (binds/unbinds) names to SOCK_NAME type socket. + * + * Parameters: + * sock : pointer to socket structure + * uaddr : pointer to 'struct sockaddr_netbeui' that contains + * information about sightly name. + * addr_len : length of 'struct sockaddr_netbeui'. + * + * Returns: + * 0 : if name is bound to socket successfully. + * other : errors reported by name service module. + * + * Notes: + * - NetBEUI extends SOCK_NAME bind system call by defining + * bind+ for registering a name from socket + * bind- for deregistering or releasing a name from socket + * * bind- is recognized by setting name_type in sockaddr_netbeui to 255 + * * bind+ is recognized by setting name_type in sockaddr_netbeui to + * other values + * + * - The names a SOCK_NAME type socket owns are detected by their + * identifier which is socket memory address. + */ +static int nbso_name_bind(struct socket *sock, struct sockaddr *uaddr, + int addr_len) +{ + struct sock *sk = sock->sk; + unsigned long id = (unsigned long)NB_SK(sk); + struct sockaddr_netbeui *addr = (struct sockaddr_netbeui *)uaddr; + name_t *nb_name; + int rc = -EPERM; + + if (!capable(CAP_NET_BIND_SERVICE)) + goto out; + nb_name = nbns_find_name(addr->snb_addr.name); + rc = 0; + if (addr->snb_addr.name_type == 0xFF) { /* Remove name from socket */ + if (nb_name) { + if (nb_name->identifier == id) + nbns_del_name(nb_name); + nbns_name_put(nb_name); + } + goto out; + } + /* Add name to socket */ + if (nb_name) { + nbns_name_put(nb_name); + goto out; + } + rc = nbns_add_name(addr->snb_addr.name, addr->snb_addr.name_type, + &nb_name); + if (!rc) { + nb_name->identifier = id; + nbns_name_put(nb_name); + } +out: return rc; +} + +/* + * Function: nbso_name_ioctl + * + * Parameters: + * + * Returns: + * + * Notes: + * - is not supported in SOCK_NAME, pass it to the config functions + */ +static int nbso_name_ioctl(struct socket *sock, unsigned int cmd, + unsigned long arg) +{ + return nbcs_ioctl(cmd, (void *)arg); +} + +/* proto_ops definition for SOCK_NAME socket type */ +struct proto_ops SOCKOPS_WRAPPED(nbso_name_proto_ops) = { + family: PF_NETBEUI, + release: nbso_name_release, + bind: nbso_name_bind, + connect: sock_no_connect, + socketpair: sock_no_socketpair, + accept: sock_no_accept, + getname: sock_no_getname, + ioctl: nbso_name_ioctl, + listen: sock_no_listen, + shutdown: sock_no_shutdown, + setsockopt: sock_no_setsockopt, + getsockopt: sock_no_getsockopt, + sendmsg: sock_no_sendmsg, + recvmsg: sock_no_recvmsg, +}; +#include +SOCKOPS_WRAP(nbso_name_proto, PF_NETBEUI); Index: kernel-acme/net/netbeui/sock_session.c diff -u /dev/null kernel-acme/net/netbeui/sock_session.c:1.1.8.1 --- /dev/null Thu Nov 22 23:55:01 2001 +++ kernel-acme/net/netbeui/sock_session.c Tue Nov 6 20:13:49 2001 @@ -0,0 +1,864 @@ +/* + * sock_session.c - Contains functions that supply SOCK_STREAM type sockets for + * NetBEUI protocol stack which their names has a + * 'nbso_session_' prefix, and also some utility functions that + * their names only has a 'nbso_' prefix. + * Notes: + * - VRP in comments is the acronym of "Value Result Parameter" + * + * Copyright (c) 1997 by Procom Technology, Inc. + * 2001 by Arnaldo Carvalho de Melo + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* SOCK_STREAM internal functions */ +/* + * Function: nbso_end_backlog + * Closes sessions that their connection was established but not + * accepted yet. + * Parameters: + * sk: pointer to NetBEUI socket that must close its waited connections. + * Returns: none + */ +static void nbso_end_backlog(struct sock *sk) +{ + while (NB_SK(sk)->u.st.backlog) { + session_t *sn = NB_SK(sk)->u.st.backlog; + + NB_SK(sk)->u.st.backlog = NB_SK(sk)->u.st.backlog->next; + nbss_hangup(sn); + } +} + +/* + * Function: nbso_autobind + * Automatically binds a NetBEUI socket to NAME_NUMBER_1 . + * Parameters: + * sk: pointer to NetBEUI socket that must bind it to NAME_NUMBER_1 . + * Returns: int + * 0 : if NetBEUI socket binds to NAME_NUMBER_1 successfully. + * -1: if can not bind NetBEUI socket to NAME_NUMBER_1 . + */ +static int nbso_autobind(struct sock *sk) +{ + NB_SK(sk)->name = nbns_name_number_1(); + return NB_SK(sk)->name ? 0 : -1; +} + +/* + * Function: nbso_session_ready (Call back function) + * Gets an established session from transport layer, and puts it + * in its owner's queue for sessions which are waited for accept. + * Parameters: + * sk : a pointer to NetBEUI socket that is owner of established + * session. + * session: a pointer to established session. + * Returns: none + */ +static void nbso_session_ready(struct sock *sk, session_t *sn) +{ + sock_hold(sk); + bh_lock_sock(sk); + if (NB_SK(sk)->u.st.backlog) + NB_SK(sk)->u.st.backlog->prev = sn; + sn->next = NB_SK(sk)->u.st.backlog; + sn->prev = NULL; + NB_SK(sk)->u.st.backlog = sn; + wake_up_interruptible(sk->sleep); + bh_unlock_sock(sk); + sock_put(sk); +} +/* + * Function: nbso_abort_interface_session (Call back function) + * Transport layer announces that a session is not valid from now. + * we must perform different actions depend on NetBEUI socket state: + * i) if socket state is NBSO_INIT, we are in a special case that causes + * when a 'nbss_call()' was interrupted. we must only announce + * process which wants to establish connection that its request was + * aborted. + * ii) if socket state is NBSO_RUNNING, we must announce all processes + * that wait for something on the socket. + * iii) if socket state is NBSO_LISTENNING, we only must remove the + * session from backlog list. + * Parameters: + * owner : a pointer to NetBEUI socket that is owner of aborted session. + * session: a pointer to aborted session. + * Returns: none + */ +static void nbso_abort_interface_session(struct sock *sk, session_t *sn) +{ + sock_hold(sk); + bh_lock_sock(sk); + if (sk->state == NBSO_INIT) { + sk->socket->state = SS_UNCONNECTED; + goto out; + } + if (sk->state == NBSO_RUNNING) { + sk->state = NBSO_INIT; + sk->socket->state = SS_UNCONNECTED; + wake_up_interruptible(sk->sleep); + goto out; + } + /* NOW, Certainly state is NBSO_LISTENING */ + if (sn->next) + sn->next->prev = sn->prev; + if (sn->prev) + sn->prev->next = sn->next; + else + NB_SK(sk)->u.st.backlog = sn->next; + /* To countervail deletion of this session from backlog list */ + nbss_listen_bh(NB_SK(sk)->name, sk->max_ack_backlog, sk, + nbso_abort_interface_session, nbso_session_ready); +out: bh_unlock_sock(sk); + sock_put(sk); +} + +/* SOCK_STREAM Calls */ +/* + * Function: nbso_session_release + * Performs additional actions at release of SOCK_STREAM sockets. + * Parameters: + * sock : pointer to socket that must be released. + * Returns: int + * 0 : in all cases. (this function always succeed) + */ +static int nbso_session_release(struct socket *sock) +{ + struct sock *sk = sock->sk; + + if (!sk) + goto out; + sock_orphan(sk); + sock_hold(sk); + lock_sock(sk); + sk->shutdown = SHUTDOWN_MASK; + if (sk->state == NBSO_LISTENING) { + __nbss_end_listen(NB_SK(sk)->name); + nbso_end_backlog(sk); + } + if (NB_SK(sk)->u.st.session) + nbss_hangup(NB_SK(sk)->u.st.session); + if (NB_SK(sk)->name) + nbns_del_name(NB_SK(sk)->name); + release_sock(sk); + sock_put(sk); + MOD_DEC_USE_COUNT; +out: return 0; +} + +/* + * Function: nbso_session_bind + * Performs additional actions at bind of SOCK_STREAM sockets to names. + * Parameters: + * sock : pointer to socket that must bind a name to it. + * uaddr : pointer to 'struct sockaddr_netbeui' that contains + * information about sightly name. + * addr_len: length of 'struct sockaddr_netbeui'. + * Returns: int + * 0 : if name is binded to socket successfully. + * negative: if a fault occurs. + * -EINVAL: if socket is bound already. + */ +static int nbso_session_bind(struct socket *sock, struct sockaddr *uaddr, + int addr_len) +{ + name_t *nb_name; + struct sock *sk = sock->sk; + struct sockaddr_netbeui *addr = (struct sockaddr_netbeui *)uaddr; + int rc = -EINVAL; + + if (!NB_SK(sk)->name) { + rc = nbns_add_name(addr->snb_addr.name, + addr->snb_addr.name_type, &nb_name); + if (!rc) { + NB_SK(sk)->name = nb_name; + nbns_name_put(nb_name); + } + } + return rc; +} + +/* + * Function: nbso_session_connect + * Performs additional actions at connect of SOCK_STREAM sockets to a + * specified peer. + * Parameters: + * sock : pointer to socket that must connect to peer. + * uaddr : pointer to 'struct sockaddr_netbeui' that contains + * information about peer. + * addr_len: length of 'struct sockaddr_netbeui'. + * flags : bitwise integer that contains socket flags. + * Returns: int + * 0 : if socket connects to the specified peer successfully. + * negative: if a fault occurs. + * -EISCONN : socket connected already or listens for + * incoming connection requests. + * -EAGAIN : no name available for the socket now, try + * again. + * -ECONNRESET: connection reset by peer. + */ +static int nbso_session_connect(struct socket *sock, struct sockaddr *uaddr, + int addr_len, int flags) +{ + int rc; + session_t *sn; + struct sock *sk = sock->sk; + struct sockaddr_netbeui *addr = (struct sockaddr_netbeui *)uaddr; + + lock_sock(sk); + rc = -EISCONN; + if (sk->state != NBSO_INIT) + goto out; + if (!NB_SK(sk)->name) { + rc = -EAGAIN; /* return -EADDRNOTAVAIL; ?! */ + if (nbso_autobind(sk)) + goto out; + } + sock->state = SS_CONNECTING; + rc = nbss_call(NB_SK(sk)->name, addr->snb_addr.name, sk, + nbso_abort_interface_session, &sn); + if (rc) + goto out; + rc = -ECONNRESET; + if (sock->state != SS_CONNECTING) + goto out; + sock->state = SS_CONNECTED; + sk->state = NBSO_RUNNING; + sk->sleep = &sn->waitq; + NB_SK(sk)->u.st.session = sn; + rc = 0; +out: release_sock(sk); + return rc; +} + +static struct sock *nbso_session_accept_newsk(struct sock *sk, + struct socket *newsock) +{ + struct sock *nsk = sk_alloc(PF_NETBEUI, sk->allocation, 1); + + if (!nsk) + goto out; + memset(NB_SK(nsk), 0, sizeof(*NB_SK(nsk))); + sock_init_data(newsock, nsk); + NB_SK(nsk)->u.st.session = NB_SK(sk)->u.st.backlog; + NB_SK(nsk)->u.st.session->owner = nsk; + newsock->state = SS_CONNECTED; + NB_SK(nsk)->name = NB_SK(sk)->name; + nbns_name_hold(NB_SK(sk)->name); + nsk->state = NBSO_RUNNING; + nsk->sleep = &NB_SK(nsk)->u.st.session->waitq; + NB_SK(sk)->u.st.backlog = NB_SK(sk)->u.st.backlog->next; + + if (NB_SK(sk)->u.st.backlog) + NB_SK(sk)->u.st.backlog->prev = NULL; + nbss_listen(NB_SK(sk)->name, sk->max_ack_backlog, sk, + nbso_abort_interface_session, nbso_session_ready); +out: return nsk; +} + +/* + * Wait for an incoming connection, avoid race conditions. This must be called + * with the socket locked. + */ +static int nbso_session_wait_for_connect(struct sock *sk) +{ + DECLARE_WAITQUEUE(wait, current); + int rc, timeout = netbios_config.inactivity_timeout * HZ; + + add_wait_queue_exclusive(sk->sleep, &wait); + for (;;) { + __set_current_state(TASK_INTERRUPTIBLE); + if (!NB_SK(sk)->u.st.backlog) { + release_sock(sk); + timeout = schedule_timeout(timeout); + lock_sock(sk); + } + rc = 0; + if (NB_SK(sk)->u.st.backlog) + break; + rc = -EINVAL; + if (sk->state != NBSO_LISTENING) + 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; +} + +/* + * Function: nbso_session_accept + * Performs additional actions at accept of incoming connection requests + * for a SOCK_STREAM socket. + * Parameters: + * sock : pointer to socket that wants to accept the incoming + * connection requests. + * newsock: (Semi VRP!) pointer to a new socket with attributes like + * original that connection between it & peer will be + * established. This 'struct socket' created by system before + * call us, and we only must complete its fields. + * flags : bitwise integer that contains socket flags. + * + * Returns: int + * 0 : if connection is successfully established between newsock + * and peer. + * negative: if a fault occurs. + * -EINVAL : this operation permitted only after a + * successful call of listen(). + * -EAGAIN : user requests non-blocking operation, but + * operation would block. + * -ERESTARTSYS: interrupted system call. + */ +static int nbso_session_accept(struct socket *sock, struct socket *newsock, + int flags) +{ + struct sock *sk = sock->sk, *nsk; + int rc = -EINVAL; + + lock_sock(sk); + if (sk->state != NBSO_LISTENING) + goto out; + newsock->state = SS_CONNECTING; + + if (!NB_SK(sk)->u.st.backlog) { + rc = -EAGAIN; + if (flags & O_NONBLOCK) + goto err; + rc = nbso_session_wait_for_connect(sk); + if (rc) + goto err; + } + nsk = nbso_session_accept_newsk(sk, newsock); + rc = -ENOBUFS; + if (!nsk) + goto out; + rc = nbss_listen(NB_SK(sk)->name, sk->max_ack_backlog, sk, + nbso_abort_interface_session, nbso_session_ready); + if (rc < 0) + goto err; + MOD_INC_USE_COUNT; +out: release_sock(sk); + return rc; +err: newsock->state = SS_UNCONNECTED; + goto out; +} + +/* + * Function: nbso_session_getname + * Gets SOCK_STREAM socket name or peer name that connected to it. + * Parameters: + * sock : pointer to socket that we need to name of it or its peer. + * uaddr : (VRP) pointer to 'struct sockaddr_netbeui' that be filled + * with requested information. + * uaddr_len : (VRP) pointer to an integer that returns length of + * 'struct sockaddr_netbeui'. + * peer : an integer that indicates type of request. + * Returns: int + * 0 : if requested name is retrieved successfully. + * negative : if a fault occurs. + * -ENOTCONN: name of peer was requested but socket has not + * any connection. + * -EBADF : socket not bounded to a name but name of it + * was requested. + */ +static int nbso_session_getname(struct socket *sock, struct sockaddr *uaddr, + int *uaddr_len, int peer) +{ + int rc; + struct sock *sk = sock->sk; + struct sockaddr_netbeui *addr = (struct sockaddr_netbeui *)uaddr; + + lock_sock(sk); + if (peer) { + rc = -ENOTCONN; + if (sk->state != NBSO_RUNNING) + goto out; + memcpy(addr->snb_addr.name, + NB_SK(sk)->u.st.session->remote_name, NETBEUI_NAME_LEN); + addr->snb_addr.name_type = + NB_SK(sk)->u.st.session->remote_name_type; + } else { + rc = -EBADF; + if (!NB_SK(sk)->name) + goto out; + memcpy(addr->snb_addr.name, NB_SK(sk)->name->name, + NETBEUI_NAME_LEN); + addr->snb_addr.name_type = NB_SK(sk)->name->type; + } + rc = 0; + *uaddr_len = sizeof(*addr); +out: release_sock(sk); + return rc; +} + +/* + * Function: nbso_session_poll + * Determines operational (particularly I/O) condition of SOCK_STREAM + * socket. + * Parameters: + * sock : pointer to socket that check it. + * sel_type: an integer that determines type of checking. + * wait : pointer to a particular structure that contains some wait + * queues. The system itself checks members of these wait queues + * for their time outs. we only sleep on this structure if there + * is not exist a categoric answer, so far. + * Returns: int + * 0: means that however must wait. + * 1: means that answer is positive or an error occurred. + */ +static unsigned int nbso_session_poll(struct file *file, struct socket *sock, + poll_table *wait) +{ + struct sock *sk = sock->sk; + unsigned int mask = 0; + + poll_wait(file, sk->sleep, wait); + if (sk->err) + mask |= POLLERR; + if (sk->shutdown == SHUTDOWN_MASK || sk->state != NBSO_RUNNING) + mask |= POLLHUP; + if (sk->shutdown & RCV_SHUTDOWN) + mask |= POLLIN | POLLRDNORM; + /* Connected? */ + if (sk->state == NBSO_RUNNING) { + if (nbss_receive_ready(NB_SK(sk)->u.st.session)) + mask |= POLLIN | POLLRDNORM; + if (!(sk->shutdown & SEND_SHUTDOWN) && + nbss_send_ready(NB_SK(sk)->u.st.session)) + mask |= POLLOUT | POLLWRNORM; + } + return mask; +} + +/* + * Function: nbso_session_ioctl + * Performs some particular operations on SOCK_STREAM socket, that can not + * do with regular system calls. + * Parameters: + * sock: pointer to socket that action must perform on it. + * cmd : an integer that indicates type of operation. + * arg : this parameter often is a pointer to 'cmd' relative data + * structure that be used by it as an argument. + * Returns: int + * 0 : if cmd is performed successfully. + * negative: if a fault occurs. error codes that bubble to user are + * dependent to cmd. + */ +static int nbso_session_ioctl(struct socket *sock, unsigned int cmd, + unsigned long arg) +{ + int rc = -EOPNOTSUPP; + struct sock *sk = sock->sk; + session_t *session; + + lock_sock(sk); + session = NB_SK(sk)->u.st.session; + if (cmd == SIOCTRIMDATA) { + if (sk->state == NBSO_RUNNING) + rc = nbss_trim_data(session); + } else if (cmd == SIOCSENDZERO) { + if (sk->state == NBSO_RUNNING) + rc = nbss_send_zero(session, (char *)arg); + } else + rc = -EINVAL; + release_sock(sk); + return rc; +} + +/* + * Function: nbso_session_listen + * Listens for incoming connection requests, and places them in a queue + * that MAXimum of its length is 'backlog'. this operation defined only + * for SOCK_STREAM functions. + * Parameters: + * sock : pointer to socket that must listens for incoming requests. + * backlog: an integer that indicates length of queue which holds + * incoming requests that not accepted yet. + * Returns: int + * 0 : if operation is performed successfully. + * negative: if a fault occurs. + * -EPERM : this operation not permitted on a connected socket. + */ +static int nbso_session_listen(struct socket *sock, int backlog) +{ + struct sock *sk = sock->sk; + int rc = -EPERM; + + lock_sock(sk); + if (sk->state == NBSO_RUNNING) + goto out; + if (!(unsigned)backlog) /* BSDism */ + backlog = 1; + if ((unsigned)backlog > SOMAXCONN) + backlog = SOMAXCONN; + if (backlog > sk->max_ack_backlog) + nbss_listen(NB_SK(sk)->name, backlog, sk, + nbso_abort_interface_session, nbso_session_ready); + sk->max_ack_backlog = backlog; + sk->state = NBSO_LISTENING; + rc = 0; +out: release_sock(sk); + return rc; +} + +/* + * Function: nbso_session_shutdown + * Shuts down part of a full-duplex connection. + * Parameters: + * sock: pointer to socket that part of its connection must be closed. + * how : an integer that indicates part of full_duplex connection that + * must be closed. + * Returns: int + * 0 : if operation is performed successfully. + * negative: if a fault occurs. + * -ENOTCONN: the specified socket is not connected. + * -EINVAL : the 'how' parameter has not a valid value. + */ +static int nbso_session_shutdown(struct socket *sock, int how) +{ + struct sock *sk = sock->sk; + int rc = -ENOTCONN; + + lock_sock(sk); + if (!sk) + goto out; + /* To avoid short circuiting by compiler */ + rc = -ENOTCONN; + if (sk->state != NBSO_RUNNING) + goto out; + how++; /* + * maps 0->1 has the advantage of making bit 1 rcvs and + * 1->2 bit 2 snds. + * 2->3 + */ + rc = -EINVAL; + if ((how & ~SHUTDOWN_MASK) || !how) /* Is "1 <= how <= 3" ?! */ + goto out; + sk->shutdown |= how; + rc = 0; +out: release_sock(sk); + return rc; +} + +/* + * Function: nbso_session_setsockopt + * Sets some operational options of SOCK_STREAM sockets. + * Parameters: + * sock : a pointer to socket that must tune its options. + * level : this parameter is not used in this function and always is + * zero. + * optname: an integer that indicates option that must tune. + * optval : a pointer to related data structure which used for assign + * value(s) to option. + * optlen : length of data structure that 'optval' points to it. + * + * Returns: int + * 0 : if tuning is performed successfully. + * negative: if a fault occurs. + * -EOPNOTSUPP : Operation not supported by us. + * -ENOPROTOOPT: Option name is not defined for us. + */ +static int nbso_session_setsockopt(struct socket *sock, int level, int optname, + char *optval, int optlen) +{ + int rc = 0; + struct sock *sk = sock->sk; + + lock_sock(sk); + switch (optname) { + case SO_SNDBUF: + case SO_RCVBUF: + case SO_SNDLOWAT: + case SO_RCVLOWAT: + rc = -EOPNOTSUPP; + break; + case SO_SNDTIMEO: + if (copy_from_user((void *)&NB_SK(sk)->u.st.sto, optval, + MIN(sizeof(NB_SK(sk)->u.st.sto), + optlen))) + rc = -EFAULT; + break; + case SO_RCVTIMEO: + if (copy_from_user((void *)&NB_SK(sk)->u.st.rto, optval, + MIN(sizeof(NB_SK(sk)->u.st.rto), + optlen))) + rc = -EFAULT; + break; + default: + rc = -ENOPROTOOPT; + break; + } + release_sock(sk); + return rc; +} + +/* + * Function: nbso_session_getsockopt + * Gets some operational options of SOCK_STREAM sockets. + * Parameters: + * sock : a pointer to socket that action performs on it. + * level : this parameter is not used in this function and always is + * zero. + * optname: an integer that indicates option that must be gotten. + * optval : (VRP) a pointer to related data structure which used for + * getting value(s) of option. + * optlen : (VRP) length of data structure that 'optval' points to it. + * + * Returns: int + * 0 : if operation is performed successfully. + * negative: if a fault occurs. + * -EOPNOTSUPP : Operation not supported by us. + * -ENOPROTOOPT: Option name is not defined for us. + */ +static int nbso_session_getsockopt(struct socket *sock, int level, int optname, + char *optval, int *optlen) +{ + int len, rc = 0; + struct sock *sk = sock->sk; + + lock_sock(sk); + switch (optname) { + case SO_SNDBUF: + case SO_RCVBUF: + case SO_SNDLOWAT: + case SO_RCVLOWAT: + rc = -EOPNOTSUPP; + break; + case SO_SNDTIMEO: + if (get_user(len, (int *)optlen)) { + rc = -EFAULT; + break; + } + len = MIN(len, sizeof(NB_SK(sk)->u.st.sto)); + if (copy_to_user(optval, + (void *)&NB_SK(sk)->u.st.sto, len) || + put_user(len, (int *)optlen)) + rc = -EFAULT; + break; + case SO_RCVTIMEO: + if (get_user(len, (int *)optlen)) { + rc = -EFAULT; + break; + } + len = MIN(len, sizeof(NB_SK(sk)->u.st.rto)); + if (copy_to_user(optval, + (void *)&NB_SK(sk)->u.st.rto, len) || + put_user(len, (int *)optlen)) + rc = -EFAULT; + break; + default: + rc = -ENOPROTOOPT; + break; + } + release_sock(sk); + return rc; +} + +/* + * Function: nbso_session_sendmsg + * Sends a message through a SOCK_STREAM socket to desired target. + * Parameters: + * sock : a pointer to socket that data sends through it. + * msg : a pointer to 'struct msghdr' that contains message body, + * target name and etc. + * len : length of message all around. + * nonblock: an integer that if be set to non-zero value means that + * no waiting (sleeping, blocking & ...) acceptable during + * operation. + * flags : bitwise integer that contains socket flags. + * Returns: int + * positive: indicates how many bytes of data was sent. + * negative: if a fault occurs. + * -EINVAL : we do not support any flags. + * -EPIPE : 'send part' of full-duplex connection was + * closed already. + * -ENOTCONN : socket is not connected yet. + * -EMSGSIZE : length of one of iovec buffers is greater + * than NETBEUI_MAX_DATALEN. + * -EWOULDBLOCK: user requests non-blocking operation, but + * operation would block. + */ +static int nbso_session_sendmsg(struct socket *sock, struct msghdr *msg, + int len, struct scm_cookie *scm) +{ + int rc, iov_no, bytes_sent; + int nonblock = msg->msg_flags & MSG_DONTWAIT; + struct iovec *iov; + struct timer_list snd_tmr; + struct sock *sk = sock->sk; + + lock_sock(sk); + rc = -EPIPE; + if (sk->shutdown & SEND_SHUTDOWN) { + send_sig(SIGPIPE, current, 1); + goto out; + } + rc = -ENOTCONN; + if (sock->state != SS_CONNECTED || sk->state != NBSO_RUNNING) + goto out; + init_timer(&snd_tmr); + if (NB_SK(sk)->u.st.sto.tv_sec || NB_SK(sk)->u.st.sto.tv_usec) { + snd_tmr.expires = jiffies + NB_SK(sk)->u.st.sto.tv_sec * HZ + + NB_SK(sk)->u.st.sto.tv_usec / (1000000 / HZ); + snd_tmr.data = (unsigned long)NB_SK(sk)->u.st.session; + snd_tmr.function = (void *)nbss_abort_send; + add_timer(&snd_tmr); + } + rc = bytes_sent = 0; + iov = msg->msg_iov; + iov_no = msg->msg_iovlen; + /* All things are good, so start to send data ... */ + while (iov_no--) { + /* Currently, limit on size of data + which can be sent is 64K-bytes */ + if (iov->iov_len > NETBEUI_MAX_DATALEN) { + rc = bytes_sent ? : -EMSGSIZE; + break; + } + rc = nbss_send(NB_SK(sk)->u.st.session, iov->iov_base, + iov->iov_len, nonblock, 0); + if (rc < 0) { + rc = bytes_sent ? : rc; + break; + } + bytes_sent += rc; + + if (rc < iov->iov_len) { + rc = bytes_sent ? : -EWOULDBLOCK; + break; + } + ++iov; + rc = bytes_sent; + } + del_timer(&snd_tmr); +out: release_sock(sk); + return rc; +} + +/* + * Function: nbso_session_recvmsg + * Receives a message through a SOCK_STREAM socket from desired source. + * Parameters: + * sock : a pointer to socket that data receives through it. + * msg : (VRP) a pointer to 'struct msghdr' that at return contains + * message body, source name and etc. + * size : MAXimum length of message all around. + * nonblock: an integer that if be set to non-zero value means that no + * waiting (sleeping, blocking & ...) acceptable during + * operation. + * flags : bitwise integer that contains socket flags. + * addr_len : (VRP) a pointer to an integer that if it is not NULL, at + * return will be filled with length of struct sockaddr_netbeui. + * + * Returns: int + * positive: indicates how many bytes of data was received. + * negative: if a fault occurs. + * -EINVAL : we do not support any flags. + * -EPIPE : 'receive part' of full-duplex connection was + * closed already. + * -ENOTCONN : socket is not connected yet. + * -EWOULDBLOCK: user requests non-blocking operation, but + * operation would block. + */ +static int nbso_session_recvmsg(struct socket *sock, struct msghdr *msg, + int size, int flags, struct scm_cookie *scm) +{ + int rc, iov_no, bytes_received; + int nonblock = msg->msg_flags & MSG_DONTWAIT; + struct iovec *iov; + struct timer_list rcv_tmr; + struct sock *sk = sock->sk; + + lock_sock(sk); + rc = -EPIPE; + if (sk->shutdown & RCV_SHUTDOWN) { + send_sig(SIGPIPE, current, 1); + goto out; + } + if (sock->state != SS_CONNECTED || sk->state != NBSO_RUNNING) { + rc = -ENOTCONN; + if (!(NB_SK(sk)->u.st.session && + nbss_receive_ready(NB_SK(sk)->u.st.session))) + goto out; + } + init_timer(&rcv_tmr); + if (NB_SK(sk)->u.st.rto.tv_sec || NB_SK(sk)->u.st.rto.tv_usec) { + rcv_tmr.expires = jiffies + NB_SK(sk)->u.st.rto.tv_sec * HZ + + NB_SK(sk)->u.st.rto.tv_usec / (1000000 / HZ); + rcv_tmr.data = (unsigned long)NB_SK(sk)->u.st.session; + rcv_tmr.function = (void *)nbss_abort_receive; + add_timer(&rcv_tmr); + } + rc = bytes_received = 0; + iov = msg->msg_iov; + iov_no = msg->msg_iovlen; + + while (iov_no--) { + rc = nbss_receive(NB_SK(sk)->u.st.session, iov->iov_base, + iov->iov_len, nonblock); + if (rc < 0) { + rc = bytes_received ? : rc; + break; + } + bytes_received += rc; + + if (rc < iov->iov_len) { + rc = bytes_received ? : -EWOULDBLOCK; + break; + } + ++iov; + rc = bytes_received; + } + del_timer(&rcv_tmr); +out: release_sock(sk); + return rc; +} + +/* Dispatcher struct for SOCK_STREAM calls */ +struct proto_ops SOCKOPS_WRAPPED(nbso_session_proto_ops) = { + family: PF_NETBEUI, + release: nbso_session_release, + bind: nbso_session_bind, + connect: nbso_session_connect, + socketpair: sock_no_socketpair, + accept: nbso_session_accept, + getname: nbso_session_getname, + poll: nbso_session_poll, + ioctl: nbso_session_ioctl, + listen: nbso_session_listen, + shutdown: nbso_session_shutdown, + setsockopt: nbso_session_setsockopt, + getsockopt: nbso_session_getsockopt, + sendmsg: nbso_session_sendmsg, + recvmsg: nbso_session_recvmsg, +}; +#include +SOCKOPS_WRAP(nbso_session_proto, PF_NETBEUI); Index: kernel-acme/net/netbeui/status_serve.c diff -u /dev/null kernel-acme/net/netbeui/status_serve.c:1.1.8.1 --- /dev/null Thu Nov 22 23:55:02 2001 +++ kernel-acme/net/netbeui/status_serve.c Tue Nov 6 20:13:49 2001 @@ -0,0 +1,767 @@ +/* + * status_serve.c - Contains functions that supply STATUS service for NetBEUI + * protocol stack, and also some utility functions. + * Notes: + * - VRP in comments is the acronym of "Value Result Parameter" + * - EHF in comments is the acronym of "Event Handling Function". + * + * Copyright (c) 1997 by Procom Technology, Inc. + * 2001 by Arnaldo Carvalho de Melo + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static void nbst_timer_function(unsigned long input); + +/* These functions are STATUS State Transition handlers */ +static int nbst_status_query_in_initial(status_t *nb_status); +static int nbst_retry_timeout_in_all(status_t *nb_status); +static int nbst_response_timeout_in_all(status_t *nb_status); +static int nbst_status_response_in_respwait(status_t *nb_status); +static int nbst_incomp_response_in_respwait(status_t *nb_status); + +#define NBST_LENGTH_MASK 0x3FFF +#define NBST_OVERFLOW_MASK 0x4000 +#define NBST_FRAGMENT_MASK 0x8000 + +/* DataGram BroadCast Maximum Transfer Unit */ +extern unsigned int dgbc_mtu; + +static unsigned long jiffies_at_reset; + +static unsigned short int nbst_correlator; +#define nbst_next_correlator() (++nbst_correlator) + +static status_t *status_request_list; +static rwlock_t status_request_list_lock = RW_LOCK_UNLOCKED; + +typedef int (* status_event_handler_t)(status_t *); + +struct event_struct { + status_state_t next_state; + status_event_handler_t event_handler; +}; + +static struct event_struct status_state_table[2][5] = { + /* NBS_STAT_INITIAL */ +{ +{ NBS_STAT_RESPWAIT, + nbst_status_query_in_initial }, /* NBE_STAT_STATUS_QUERY */ +{ -1, NULL }, /* NBE_STAT_RETRY_TIMEOUT */ +{ -1, NULL }, /* NBE_STAT_RESPONSE_TIMEOUT */ +{ -1, NULL }, /* NBE_STAT_STATUS_RESPONSE */ +{ -1, NULL } /* NBE_STAT_INCOMP_RESPONSE */ +}, + /* NBS_STAT_RESPWAIT */ +{ +{ -1, NULL }, /* NBE_STAT_STATUS_QUERY */ +{ NBS_STAT_RESPWAIT, + nbst_retry_timeout_in_all }, /* NBE_STAT_RETRY_TIMEOUT */ +{ NBS_STAT_INITIAL, + nbst_response_timeout_in_all }, /* NBE_STAT_RESPONSE_TIMEOUT */ +{ NBS_STAT_INITIAL, + nbst_status_response_in_respwait }, /* NBE_STAT_STATUS_RESPONSE */ +{ NBS_STAT_RESPWAIT, + nbst_incomp_response_in_respwait } /* NBE_STAT_INCOMP_RESPONSE */ +} +}; + +static nb_status_buffer_t nb_status_record; +/* Internal NBST functions */ +/* + * Function: nbst_alloc_status + * Allocates a 'nb_status' structure and initializes its fields. + * Parameters: none + * Returns: status_t * + * non NULL : address of allocated nb_status. + * NULL : if can not allocate nb_status, or initialize its fields + * properly. + */ +static status_t *nbst_alloc_status(void) +{ + int status_dgram_len; + status_t *nb_status = kmalloc(sizeof(*nb_status), GFP_KERNEL); + + if (!nb_status) + goto out; + /* Implicitly initialize all fields */ + memset(nb_status, 0, sizeof(*nb_status)); + /* Allocate status skb */ + status_dgram_len = nb_cmd_hdr_len[NETBEUI_STATUS_QUERY]; + nb_status->tx_skb = alloc_skb(CALC_DG_SKBLEN(NETBEUI_MAC_B_HEADLEN, + status_dgram_len), GFP_KERNEL); + if (!nb_status->tx_skb) + goto err; + skb_reserve(nb_status->tx_skb, LLCMAC_UIB_HEADLEN()); + nb_status->tx_skb->nh.raw = nb_status->tx_skb->h.raw = + nb_status->tx_skb->data; + skb_put(nb_status->tx_skb, status_dgram_len); + nb_status->tx_skb->dev = NULL; + init_timer(&nb_status->timer); + nb_status->timer.data = (unsigned long)nb_status; + nb_status->timer.function = nbst_timer_function; + init_waitqueue_head(&nb_status->waitq); +out: return nb_status; +err: kfree(nb_status); + nb_status = NULL; + goto out; +} + +/* + * Function: nbst_free_status + * Frees a nb_status structure and its sk_buff. + * Parameters: + * nb_status : pointer to nb_status structure that must be freed. + * Returns: none + */ +static void nbst_free_status(status_t *nb_status) +{ + kfree_skb(nb_status->tx_skb); + kfree(nb_status); +} + +/* + * Function: nbst_add_status_to_list + * Adds a nb_status structure to the 'status_request_list'. + * Parameters: + * nb_status : pointer to nb_status structure that must be added to the + * list. + * Returns: none + */ +static inline void nbst_add_status_to_list(status_t *nb_status) +{ + write_lock(&status_request_list_lock); + nb_status->next = status_request_list; + status_request_list = nb_status; + write_unlock(&status_request_list_lock); +} + +/* + * Function: nbst_remove_status_from_list + * Removes a nb_status structure from the 'status_request_list'. + * Parameters: + * nb_status : pointer to nb_status structure that must be removed from + * the list. + * Returns: none + */ +static void nbst_remove_status_from_list(status_t *nb_status) +{ + status_t *entry, *prev_entry = NULL; + + write_lock(&status_request_list_lock); + for (entry = status_request_list; entry; entry = entry->next) { + if (entry == nb_status) { + if (prev_entry) + prev_entry->next = entry->next; + else + status_request_list = entry->next; + break; + } + prev_entry = entry; + } + write_unlock(&status_request_list_lock); +} + +/* + * Function: nbst_find_correlator + * Finds a nb_status in the 'status_request_list' that have a specific + * response correlator. + * Parameters: + * correlator : the response correlator that we search for it. + * Returns: status_t * + * non NULL : address of sightly nb_status in the list. + * NULL : if no match be found. + */ +static status_t *nbst_find_correlator(__u16 correlator) +{ + status_t *nb_status; + + read_lock(&status_request_list_lock); + nb_status = status_request_list; + for (; nb_status; nb_status = nb_status->next) + if (nb_status->resp_correlator == correlator) + break; + read_unlock(&status_request_list_lock); + return nb_status; +} + +/* + * Function: nbst_handle_event + * Handles an entrant event to STATUS state machine. + * Parameters: + * event : the entrant event. + * nb_status : (Semi VRP!) pointer to nb_status structure (entity) that + * event is for it. + * Returns: none + */ +static void nbst_handle_event(status_event_t event, status_t *nb_status) +{ + struct event_struct *ev = &status_state_table[nb_status->state][event]; + + if (ev && ev->event_handler) + if (!ev->event_handler(nb_status)) + nb_status->state = ev->next_state; +} + +/* + * Function: nbst_timer_function (Call back function) + * Produces an appropriate event related to number of retries + * so far, due to fire of a timer. + * Parameters: + * input : pointer to nb_status structure that timer of it has been fired. + * Returns: none + */ +static void nbst_timer_function(unsigned long input) +{ + status_t *nb_status = (status_t *)input; + + if (nb_status->retries < NETBEUI_TRANSMIT_COUNT) + nbst_handle_event(NBE_STAT_RETRY_TIMEOUT, nb_status); + else + nbst_handle_event(NBE_STAT_RESPONSE_TIMEOUT, nb_status); +} + +/* + * Function: nbst_status_query_in_initial + * (EHF) Handles NBE_STAT_STATUS_QUERY event in NBS_STAT_INITIAL state. + * Parameters: + * nb_status : pointer to nb_status structure that event happens on it. + * Returns: int + * zero : if event be handled successfully. + * non zero : if an error occurred during event handling. + */ +static int nbst_status_query_in_initial(status_t *nb_status) +{ + int rc; + dgram_t *hdrp; + + /* all fields were set to zero in nbst_alloc_status() */ + nb_status->resp_status = NO_RESPONSE; + hdrp = (dgram_t *)nb_status->tx_skb->data; + hdrp->length = nb_cmd_hdr_len[NETBEUI_STATUS_QUERY]; + hdrp->delimiter = NETBEUI_DELIMITER; + hdrp->command = NETBEUI_STATUS_QUERY; + /* We implement NetBIOS 3.0 */ + hdrp->data1 = 1; + hdrp->data2 = nb_status->sbuff_len; + hdrp->resp_correlator = nb_status->resp_correlator = + nbst_next_correlator(); + rc = -EFAULT; + if (copy_from_user(hdrp->dest_name, nb_status->called_name, + NETBEUI_NAME_LEN)) + goto out; + memcpy(hdrp->source_name, nbns_name_number_1()->name, NETBEUI_NAME_LEN); + + /* BroadCast NETBEUI_STATUS_QUERY */ + rc = nbll_uisend(NULL, nb_status->tx_skb); + if (!rc) { + nb_status->retries++; + nb_status->timer.expires = jiffies + NETBEUI_TRANSMIT_TIMEOUT; + add_timer(&nb_status->timer); + nbst_add_status_to_list(nb_status); + } +out: return rc; +} + +/* + * Function: nbst_retry_timeout_in_all + * (EHF) Handles NBE_STAT_RETRY_TIMEOUT event in all of states. + * Parameters: + * nb_status : pointer to nb_status structure that event happens on it. + * Returns: int + * zero : if event be handled successfully. + * non zero : if an error occurred during event handling. + */ +static int nbst_retry_timeout_in_all(status_t *nb_status) +{ + int rc = 0; + + nb_status->timer.expires = jiffies+ NETBEUI_TRANSMIT_TIMEOUT; + add_timer(&nb_status->timer); + if (nb_status->unicast) { + /* UniCast NETBEUI_STATUS_QUERY */ + rc = nbll_uisend(nb_status->remote_mac, nb_status->tx_skb); + if (rc) + goto out; + skb_pull(nb_status->tx_skb, + LLCMAC_UI_HEADLEN(MAC_HEADLEN(nb_status->tx_skb->dev))); + } else { + /* BroadCast NETBEUI_STATUS_QUERY */ + rc = nbll_uisend(NULL, nb_status->tx_skb); + if (rc) + goto out; + } + nb_status->retries++; +out: return rc; +} + +/* + * Function: nbst_response_timeout_in_all + * (EHF) Handles NBE_STAT_RESPONSE_TIMEOUT event in all of states. + * Parameters: + * nb_status : pointer to nb_status structure that event happens on it. + * Returns: int + * zero : this function always succeed. + */ +static int nbst_response_timeout_in_all(status_t *nb_status) +{ + nbst_remove_status_from_list(nb_status); + wake_up(&nb_status->waitq); + return 0; +} + +/* + * Function: nbst_status_response_in_respwait + * (EHF) Handles NBE_STAT_STATUS_RESPONSE event in NBS_STAT_RESPWAIT state. + * Parameters: + * nb_status : pointer to nb_status structure that event happens on it. + * Returns: int + * zero : this function always succeed. + */ +static int nbst_status_response_in_respwait(status_t *nb_status) +{ + del_timer(&nb_status->timer); + nbst_remove_status_from_list(nb_status); + if (nb_status->overflowed) + nb_status->resp_status = USER_BUFFER_OVERFLOW; + else + nb_status->resp_status = COMPLETED_RESPONSE; + return 0; +} + +/* + * Function: nbst_incomp_response_in_respwait + * (EHF) Handles NBE_STAT_INCOMP_RESPONSE event in NBS_STAT_RESPWAIT state. + * Parameters: + * nb_status : pointer to nb_status structure that event happens on it. + * Returns: int + * zero : if event be handled successfully. + * non zero : if an error occurred during event handling. + */ +static int nbst_incomp_response_in_respwait(status_t *nb_status) +{ + int rc; + + del_timer(&nb_status->timer); + nb_status->unicast = 1; + nb_status->retries = 0; + nb_status->resp_status = INCOMPLETE_RESPONSE; + ((dgram_t *)(nb_status->tx_skb->data))->data1 = nb_status->no_rx_names; + ((dgram_t *)(nb_status->tx_skb->data))->data2 = nb_status->sbuff_len - + nb_status->len_rx_info; + nb_status->timer.expires = jiffies + NETBEUI_TRANSMIT_TIMEOUT; + add_timer(&nb_status->timer); + rc = nbll_uisend(nb_status->remote_mac, nb_status->tx_skb); + if (!rc) { + skb_pull(nb_status->tx_skb, + LLCMAC_UI_HEADLEN(MAC_HEADLEN(nb_status->tx_skb->dev))); + nb_status->retries++; + } + return rc; +} + +/* + * Function: nbst_gather_status_info + * Gathers NetBIOS status information in local machine. + * Parameters: + * dev : pointer to struct net_device that its related status information + * has been requested. + * Returns: int + * positive : length of gathered status information. this function always + * succeed. + */ +static int nbst_gather_status_info(struct net_device *dev) +{ + int i; + __u8 tmp8; + __u16 non; + name_t *ntp; + unsigned long tmpl; + + if (!dev) + dev = netbeui_adapters.dev[0]; + if (dev) { + dev_hold(dev); + memcpy(nb_status_record.adptr_addr, dev->dev_addr, 6); + if (dev->type == ARPHRD_IEEE802) + nb_status_record.adptr_type_AND_sftwr_level.adptr_type = + NETBEUI_TOKEN_RING; + else if (dev->type == ARPHRD_ETHER || + dev->type == ARPHRD_EETHER) + nb_status_record.adptr_type_AND_sftwr_level.adptr_type = + NETBEUI_ETHERNET; + } else { + memset(nb_status_record.adptr_addr, 0, 6); + nb_status_record.adptr_type_AND_sftwr_level.adptr_type = 0; + } + /* We support new parameters , NetBIOS 3.0 */ + nb_status_record.adptr_type_AND_sftwr_level.sftwr_level = 0x20; + nb_status_record.sftwr_release_no = 3; /* We implement NetBIOS 3.0 */ + tmpl = (jiffies - jiffies_at_reset) / (HZ * 60); + nb_status_record.duration = tmpl > 0xFFFF ? 0xFFFF : tmpl; + nb_status_record.max_dgram_packet_size = dgbc_mtu; + nb_status_record.max_no_pend_sess = 0xFFFF; /* No limit! */ + nb_status_record.max_size_sess_data_packet = dev ? + (dev->mtu - LLCMAC_I_HEADLEN(dev) - NETBEUI_ILEN) : 0; + if (dev) + dev_put(dev); + non = nbns_count_names(); + nb_status_record.no_names_in_local_name_tbl = non; + ntp = nbns_get_name_list(); + for (i = 0; i < non; i++, ntp = ntp->next) { + memcpy(nb_status_record.local_names[i].name, ntp->name, + NETBEUI_NAME_LEN); + nb_status_record.local_names[i].name_number = ntp->name_number; + + tmp8 = ntp->type == NETBEUI_NAME_GROUP ? 0x80 : 0x00; + switch (ntp->state) { + case NETBEUI_NAME_ACQUIRED: /* A registered name */ + tmp8 |= 0x04; + break; + case NETBEUI_NAME_INITIAL: /* A deregistered name */ + tmp8 |= 0x05; + default: ; /* Only for silence of the gcc! */ + } + if (ntp->conflicted) /* A detected duplicate name */ + tmp8 |= 0x06; + nb_status_record.local_names[i].name_status = tmp8; + } + return NETBEUI_MIN_STATUS_BUFF_LEN + (NETBEUI_NAME_LEN + 2) * non; +} + +/* + * Function: nbst_local_status + * Retrieves local NetBIOS status information. + * + * Parameters: + * status_buff : pointer to user buffer that must be filled with local + * status information. + * buff_len : (VRP) pointer to an integer that indicates length of + * user buffer at start and length of retrieved information + * at end. + * + * Returns: int + * zero : if information retrieved successfully. + * negative : if something is bad. + * -EOVERFLOW : user buffer length is not large enough to + * keep all of information, so some of it was + * copied to user buffer. + */ +static int nbst_local_status(char *status_buff, int *buff_len) +{ + int len, info_len; + + if (get_user(len, buff_len)) + return -EFAULT; + info_len = nbst_gather_status_info(NULL); + len = MIN(len, info_len); + if (copy_to_user(status_buff, &nb_status_record, len) || + put_user(len, buff_len)) + return -EFAULT; + return len < info_len ? -EOVERFLOW : 0; +} + +/* + * Function: nbst_wait_for_resp + * Waits for end of STATUS state machine operation, and supervise + * received status response frames. + * + * Parameters: + * nb_status : pointer to nb_status structure that operations perform + * on it. + * + * Returns: none + */ +static void nbst_wait_for_resp(status_t *nb_status) +{ + sleep_on(&nb_status->waitq); + nb_status->locked = 1; + + while (nb_status->rx_skb) { + int sdl, tmp; + char *sdp; + dgram_t *hdrp = (dgram_t *)nb_status->rx_skb->data; + + nb_status->overflowed = hdrp->data2 & NBST_OVERFLOW_MASK; + /* Pointer to the status data in the received frame */ + sdp = skb_pull(nb_status->rx_skb, NETBEUI_UILEN); + sdl = hdrp->data2 & NBST_LENGTH_MASK; + tmp = nb_status->len_rx_info ? + sdl : (sdl - NETBEUI_MIN_STATUS_BUFF_LEN); + nb_status->no_rx_names += tmp / (NETBEUI_NAME_LEN + 2); + nb_status->len_rx_info += sdl; + + if (nb_status->len_rx_info < nb_status->sbuff_len) { + /* FIXME(acme): check copy_to_user result! */ + copy_to_user(nb_status->user_sbuff, (char *)sdp, sdl); + + if ((hdrp->data2 & NBST_FRAGMENT_MASK) && hdrp->data1) + nbst_handle_event(NBE_STAT_INCOMP_RESPONSE, + nb_status); + else + nbst_handle_event(NBE_STAT_STATUS_RESPONSE, + nb_status); + } + else { + sdl -= nb_status->len_rx_info - nb_status->sbuff_len; + copy_to_user(nb_status->user_sbuff, sdp, sdl); + + nbst_handle_event(NBE_STAT_STATUS_RESPONSE, nb_status); + } + nb_status->user_sbuff += sdl; + kfree_skb(nb_status->rx_skb); + nb_status->rx_skb = NULL; + + if (nb_status->state == NBS_STAT_RESPWAIT) { + barrier(); + nb_status->locked = 0; + sleep_on(&nb_status->waitq); + } + } +} + +/* + * Function: nbst_remote_status + * Retrieves NetBIOS status information of a remote host. + * + * Parameters: + * remote_name : pointer to a NetBIOS name that must retrieve status + * information about it. + * status_buff : (VRP) pointer to user buffer that must be filled with + * status information of remote host. + * buff_len : (VRP) pointer to an integer that indicates length of + * user buffer at start and length of retrieved information + * at end. + * + * Returns: int + * zero : if information retrieved successfully. + * negative : if something is bad. + * -ENONET : no network device found. + * -ENOMEM : out of memory condition. + * -EHOSTUNREACH : no response from remote host. + * -ETIMEDOUT : system timed out before retrieving all of + * information, so some of it was copied to + * user buffer. + * -EOVERFLOW : user buffer length is not large enough to + * keep all of information, so some of it was + * copied to user buffer. + */ +static int nbst_remote_status(char *remote_name, char *status_buff, + int *buff_len) +{ + status_t *nb_status; + int rc = -ENONET; + + if (!netbeui_adapters.dev[0]) + goto out; + nb_status = nbst_alloc_status(); + rc = -ENOMEM; + if (!nb_status) + goto out; + nb_status->called_name = remote_name; + nb_status->user_sbuff = status_buff; + rc = -EFAULT; + if (get_user(nb_status->sbuff_len, buff_len)) + goto out_status; + nb_status->state = NBS_STAT_INITIAL; + nbst_handle_event(NBE_STAT_STATUS_QUERY, nb_status); + if (nb_status->state != NBS_STAT_INITIAL) + nbst_wait_for_resp(nb_status); + rc = -EFAULT; + if (put_user(MIN(nb_status->sbuff_len, nb_status->len_rx_info), + buff_len)) + goto out_status; + switch (nb_status->resp_status) { + case NO_RESPONSE: + rc = -EHOSTUNREACH; + break; + case INCOMPLETE_RESPONSE: + rc = -ETIMEDOUT; + break; + case USER_BUFFER_OVERFLOW: + rc = -EOVERFLOW; + default: /* COMPLETED_RESPONSE */ + rc = 0; + } +out_status: + nbst_free_status(nb_status); +out: return rc; +} + +/* Exported N B S T Functions */ +/* + * Function: nbst_init_status + * Performs some initializes for STATUS service functions during + * installation of NetBEUI in memory. + * + * Parameters: none + * + * Returns: none + */ +void nbst_init_status(void) +{ + memset(&nb_status_record, 0, sizeof(nb_status_record)); + jiffies_at_reset = jiffies; +} + +/* + * Function: nbst_obtain_status + * Interface function for NetBEUI STATUS service. + * + * Parameters: + * remote_name : pointer to a NetBIOS name that must retrieve status + * information about it. + * status_buff : (VRP) pointer to user buffer that must be filled with + * status information. + * buff_len : (VRP) pointer to an integer that indicates length of + * user buffer at start and length of retrieved information + * at end. + * + * Returns: int + * zero : if information retrieved successfully. + * negative : if something is bad. + */ +int nbst_obtain_status(char *called_name, char *status_buff, int *buff_len) +{ + int name; + + if (get_user(name, called_name)) + return -EFAULT; + return name == '*' ? nbst_local_status(status_buff, buff_len) : + nbst_remote_status(called_name, status_buff, + buff_len); +} + +/* + * Function: nbst_get_status_query + * Takes a NETBEUI_STATUS_QUERY frame from 'llc supplementary' and response + * to it. + * + * Parameters: + * skb : pointer to sk_buff that contains received frame. + * remote_mac : pointer to buffer that contains MAC address of sender + * of the frame. + * + * Returns: none + */ +void nbst_get_status_query(struct sk_buff *skb, unsigned char *remote_mac) +{ + int tmp, losd, nonts, info_len; + __u16 loits; + struct sk_buff *resp_skb; + dgram_t *resp_hdrp, *hdrp = (dgram_t *)skb->data; + /* Test for illegal requests */ + name_t *nb_name = nbns_find_name(hdrp->dest_name); + + if (!nb_name) + goto free_skb; + /* Status query for a group name has no meaning */ + if (nb_name->type == NETBEUI_NAME_GROUP) + goto out_put; + info_len = nbst_gather_status_info(skb->dev); + if (hdrp->data1 > 1 && + hdrp->data1 >= nb_status_record.no_names_in_local_name_tbl) + goto out_put; + /* Make a sk_buff for response */ + resp_skb = alloc_skb(skb->dev->mtu, GFP_ATOMIC); + if (!resp_skb) + goto out_put; + tmp = LLCMAC_UI_HEADLEN(MAC_HEADLEN(skb->dev)); + skb_reserve(resp_skb, tmp); + resp_skb->nh.raw = resp_skb->h.raw = resp_skb->data; + resp_hdrp = (dgram_t *)skb_put(resp_skb, + nb_cmd_hdr_len[NETBEUI_STATUS_RESPONSE]); + resp_hdrp->length = nb_cmd_hdr_len[NETBEUI_STATUS_RESPONSE]; + resp_hdrp->delimiter = NETBEUI_DELIMITER; + resp_hdrp->command = NETBEUI_STATUS_RESPONSE; + resp_hdrp->xmit_correlator = hdrp->resp_correlator; + /* Find length of data that can be sent */ + tmp = skb->dev->mtu - tmp; + losd = tmp - nb_cmd_hdr_len[NETBEUI_STATUS_RESPONSE]; + losd = MIN(losd, hdrp->data2); + + if (hdrp->data1 <= 1) { /* An initial request */ + nonts = (losd - NETBEUI_MIN_STATUS_BUFF_LEN) / + (NETBEUI_NAME_LEN + 2); + nonts = MIN(nonts, nb_status_record.no_names_in_local_name_tbl); + loits = NETBEUI_MIN_STATUS_BUFF_LEN + nonts * + (NETBEUI_NAME_LEN + 2); + memcpy(skb_put(resp_skb, loits), &nb_status_record, loits); + resp_hdrp->data1 = nonts ? nonts : 1; + } else { + nonts = losd / (NETBEUI_NAME_LEN + 2); + tmp = nb_status_record.no_names_in_local_name_tbl - hdrp->data1; + nonts = MIN(nonts, tmp); + info_len = tmp * (NETBEUI_NAME_LEN + 2); + loits = nonts * (NETBEUI_NAME_LEN + 2); + memcpy(skb_put(resp_skb, loits), + &nb_status_record.local_names[hdrp->data1], loits); + resp_hdrp->data1 = nonts + hdrp->data1; + } + loits &= NBST_LENGTH_MASK; + if (info_len > losd) + loits |= NBST_FRAGMENT_MASK; + if (info_len > hdrp->data2) + loits |= NBST_OVERFLOW_MASK; + resp_hdrp->data2 = loits; + memcpy(resp_hdrp->dest_name, hdrp->source_name, NETBEUI_NAME_LEN); + memcpy(resp_hdrp->source_name, hdrp->dest_name, NETBEUI_NAME_LEN); + resp_skb->dev = skb->dev; + kfree_skb(skb); + nbll_uisend(remote_mac, resp_skb); + nbns_name_put(nb_name); +out: return; +out_put: + nbns_name_put(nb_name); +free_skb: + kfree_skb(skb); + goto out; +} + +/* + * Function: nbst_get_status_response + * Takes a NETBEUI_STATUS_RESPONSE frame from 'llc supplementary' and + * place it on appropriate nb_status structure. + * + * Parameters: + * skb : pointer to sk_buff that contains received frame. + * remote_mac : pointer to buffer that contains MAC address of sender + * of the frame. + * + * Returns: none + */ +void nbst_get_status_response(struct sk_buff *skb, unsigned char *remote_mac) +{ + dgram_t *dgram = (dgram_t *)skb->data; + status_t *nb_status = nbst_find_correlator(dgram->xmit_correlator); + + /* If it does not match a waited status query */ + if (!nb_status) + goto free_skb; + barrier(); + if (nb_status->locked) + goto free_skb; + nb_status->rx_skb = skb; + nb_status->tx_skb->dev = skb->dev; + memcpy(nb_status->remote_mac, remote_mac, MAC_ADDR_LEN); + wake_up(&nb_status->waitq); +out: return; +free_skb: + kfree_skb(skb); + goto out; +}