2005-04-17 02:20:36 +04:00
/*
* llc_if . c - Defines LLC interface to upper layer
*
* Copyright ( c ) 1997 by Procom Technology , Inc .
* 2001 - 2003 by Arnaldo Carvalho de Melo < acme @ conectiva . com . br >
*
* This program can be redistributed or modified under the terms of the
* GNU General Public License as published by the Free Software Foundation .
* This program is distributed without any warranty or implied warranty
* of merchantability or fitness for a particular purpose .
*
* See the GNU General Public License for more details .
*/
# include <linux/module.h>
# include <linux/kernel.h>
# include <linux/netdevice.h>
# include <asm/errno.h>
# include <net/llc_if.h>
# include <net/llc_sap.h>
# include <net/llc_s_ev.h>
# include <net/llc_conn.h>
# include <net/sock.h>
# include <net/llc_c_ev.h>
# include <net/llc_c_ac.h>
# include <net/llc_c_st.h>
2005-08-10 07:08:28 +04:00
# include <net/tcp_states.h>
2005-04-17 02:20:36 +04:00
/**
* llc_build_and_send_pkt - Connection data sending for upper layers .
* @ sk : connection
* @ skb : packet to send
*
* 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 .
*/
int llc_build_and_send_pkt ( struct sock * sk , struct sk_buff * skb )
{
struct llc_conn_state_ev * ev ;
int rc = - ECONNABORTED ;
struct llc_sock * llc = llc_sk ( sk ) ;
2005-09-22 11:32:10 +04:00
if ( unlikely ( llc - > state = = LLC_CONN_STATE_ADM ) )
2005-04-17 02:20:36 +04:00
goto out ;
rc = - EBUSY ;
2005-09-22 11:32:10 +04:00
if ( unlikely ( llc_data_accept_state ( llc - > state ) | | /* data_conn_refuse */
llc - > p_flag ) ) {
2005-04-17 02:20:36 +04:00
llc - > failed_data_req = 1 ;
goto out ;
}
ev = llc_conn_ev ( skb ) ;
ev - > type = LLC_CONN_EV_TYPE_PRIM ;
ev - > prim = LLC_DATA_PRIM ;
ev - > prim_type = LLC_PRIM_TYPE_REQ ;
skb - > dev = llc - > dev ;
rc = llc_conn_state_process ( sk , skb ) ;
out :
return rc ;
}
/**
* llc_establish_connection - Called by upper layer to establish a conn
* @ sk : connection
* @ lmac : local mac address
* @ dmac : destination mac address
* @ dsap : destination sap
*
* 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 .
*/
int llc_establish_connection ( struct sock * sk , u8 * lmac , u8 * dmac , u8 dsap )
{
int rc = - EISCONN ;
struct llc_addr laddr , daddr ;
struct sk_buff * skb ;
struct llc_sock * llc = llc_sk ( sk ) ;
struct sock * existing ;
laddr . lsap = llc - > sap - > laddr . lsap ;
daddr . lsap = dsap ;
memcpy ( daddr . mac , dmac , sizeof ( daddr . mac ) ) ;
memcpy ( laddr . mac , lmac , sizeof ( laddr . mac ) ) ;
existing = llc_lookup_established ( llc - > sap , & daddr , & laddr ) ;
if ( existing ) {
if ( existing - > sk_state = = TCP_ESTABLISHED ) {
sk = existing ;
goto out_put ;
} else
sock_put ( existing ) ;
}
sock_hold ( sk ) ;
rc = - ENOMEM ;
skb = alloc_skb ( 0 , GFP_ATOMIC ) ;
if ( skb ) {
struct llc_conn_state_ev * ev = llc_conn_ev ( skb ) ;
ev - > type = LLC_CONN_EV_TYPE_PRIM ;
ev - > prim = LLC_CONN_PRIM ;
ev - > prim_type = LLC_PRIM_TYPE_REQ ;
2005-09-22 14:57:21 +04:00
skb_set_owner_w ( skb , sk ) ;
2005-04-17 02:20:36 +04:00
rc = llc_conn_state_process ( sk , skb ) ;
}
out_put :
sock_put ( sk ) ;
return rc ;
}
/**
* llc_send_disc - Called by upper layer to close a connection
* @ sk : connection to be closed
*
* 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 .
*/
int llc_send_disc ( struct sock * sk )
{
u16 rc = 1 ;
struct llc_conn_state_ev * ev ;
struct sk_buff * skb ;
sock_hold ( sk ) ;
if ( sk - > sk_type ! = SOCK_STREAM | | sk - > sk_state ! = TCP_ESTABLISHED | |
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
*/
skb = alloc_skb ( 0 , GFP_ATOMIC ) ;
if ( ! skb )
goto out ;
2005-09-22 14:57:21 +04:00
skb_set_owner_w ( skb , sk ) ;
2005-04-17 02:20:36 +04:00
sk - > sk_state = TCP_CLOSING ;
ev = llc_conn_ev ( skb ) ;
ev - > type = LLC_CONN_EV_TYPE_PRIM ;
ev - > prim = LLC_DISC_PRIM ;
ev - > prim_type = LLC_PRIM_TYPE_REQ ;
rc = llc_conn_state_process ( sk , skb ) ;
out :
sock_put ( sk ) ;
return rc ;
}