2010-03-30 17:56:24 +04:00
/*
* Copyright ( C ) ST - Ericsson AB 2010
* Author : Sjur Brendeland / sjur . brandeland @ stericsson . com
* License terms : GNU General Public License ( GPL ) version 2
*/
2010-09-06 01:31:11 +04:00
# define pr_fmt(fmt) KBUILD_MODNAME ":%s(): " fmt, __func__
2010-03-30 17:56:24 +04:00
# include <linux/kernel.h>
# include <linux/stddef.h>
2010-04-01 11:28:49 +04:00
# include <linux/slab.h>
2010-06-17 10:55:40 +04:00
# include <linux/netdevice.h>
2011-05-13 06:44:01 +04:00
# include <linux/module.h>
2010-03-30 17:56:24 +04:00
# include <net/caif/caif_layer.h>
# include <net/caif/cfpkt.h>
# include <net/caif/cfcnfg.h>
# include <net/caif/cfctrl.h>
# include <net/caif/cfmuxl.h>
# include <net/caif/cffrml.h>
# include <net/caif/cfserl.h>
# include <net/caif/cfsrvl.h>
2011-05-13 06:44:01 +04:00
# include <net/caif/caif_dev.h>
2010-03-30 17:56:24 +04:00
# define container_obj(layr) container_of(layr, struct cfcnfg, layer)
/* Information about CAIF physical interfaces held by Config Module in order
* to manage physical interfaces
*/
struct cfcnfg_phyinfo {
2011-05-13 06:44:01 +04:00
struct list_head node ;
bool up ;
2010-03-30 17:56:24 +04:00
/* Pointer to the layer below the MUX (framing layer) */
struct cflayer * frm_layer ;
/* Pointer to the lowest actual physical layer */
struct cflayer * phy_layer ;
/* Unique identifier of the physical interface */
unsigned int id ;
/* Preference of the physical in interface */
enum cfcnfg_phy_preference pref ;
/* Information about the physical device */
struct dev_info dev_info ;
2010-06-17 10:55:40 +04:00
/* Interface index */
int ifindex ;
/* Use Start of frame extension */
bool use_stx ;
/* Use Start of frame checksum */
bool use_fcs ;
2010-03-30 17:56:24 +04:00
} ;
struct cfcnfg {
struct cflayer layer ;
struct cflayer * ctrl ;
struct cflayer * mux ;
2011-05-13 06:44:01 +04:00
struct list_head phys ;
struct mutex lock ;
2010-03-30 17:56:24 +04:00
} ;
2010-04-28 12:54:35 +04:00
static void cfcnfg_linkup_rsp ( struct cflayer * layer , u8 channel_id ,
2010-03-30 17:56:24 +04:00
enum cfctrl_srv serv , u8 phyid ,
struct cflayer * adapt_layer ) ;
2010-04-28 12:54:37 +04:00
static void cfcnfg_linkdestroy_rsp ( struct cflayer * layer , u8 channel_id ) ;
2010-04-28 12:54:35 +04:00
static void cfcnfg_reject_rsp ( struct cflayer * layer , u8 channel_id ,
2010-03-30 17:56:24 +04:00
struct cflayer * adapt_layer ) ;
static void cfctrl_resp_func ( void ) ;
static void cfctrl_enum_resp ( void ) ;
struct cfcnfg * cfcnfg_create ( void )
{
struct cfcnfg * this ;
struct cfctrl_rsp * resp ;
2011-05-13 06:44:01 +04:00
might_sleep ( ) ;
2010-03-30 17:56:24 +04:00
/* Initiate this layer */
2010-05-13 14:03:32 +04:00
this = kzalloc ( sizeof ( struct cfcnfg ) , GFP_ATOMIC ) ;
2011-08-25 17:22:24 +04:00
if ( ! this )
2010-03-30 17:56:24 +04:00
return NULL ;
this - > mux = cfmuxl_create ( ) ;
if ( ! this - > mux )
goto out_of_mem ;
this - > ctrl = cfctrl_create ( ) ;
if ( ! this - > ctrl )
goto out_of_mem ;
/* Initiate response functions */
resp = cfctrl_get_respfuncs ( this - > ctrl ) ;
resp - > enum_rsp = cfctrl_enum_resp ;
resp - > linkerror_ind = cfctrl_resp_func ;
2010-04-28 12:54:35 +04:00
resp - > linkdestroy_rsp = cfcnfg_linkdestroy_rsp ;
2010-03-30 17:56:24 +04:00
resp - > sleep_rsp = cfctrl_resp_func ;
resp - > wake_rsp = cfctrl_resp_func ;
resp - > restart_rsp = cfctrl_resp_func ;
resp - > radioset_rsp = cfctrl_resp_func ;
2010-04-28 12:54:35 +04:00
resp - > linksetup_rsp = cfcnfg_linkup_rsp ;
resp - > reject_rsp = cfcnfg_reject_rsp ;
2011-05-13 06:44:01 +04:00
INIT_LIST_HEAD ( & this - > phys ) ;
2010-03-30 17:56:24 +04:00
cfmuxl_set_uplayer ( this - > mux , this - > ctrl , 0 ) ;
layer_set_dn ( this - > ctrl , this - > mux ) ;
layer_set_up ( this - > ctrl , this ) ;
2011-05-13 06:44:01 +04:00
mutex_init ( & this - > lock ) ;
2010-03-30 17:56:24 +04:00
return this ;
out_of_mem :
2011-05-13 06:44:01 +04:00
synchronize_rcu ( ) ;
2010-03-30 17:56:24 +04:00
kfree ( this - > mux ) ;
kfree ( this - > ctrl ) ;
kfree ( this ) ;
return NULL ;
}
void cfcnfg_remove ( struct cfcnfg * cfg )
{
2011-05-13 06:44:01 +04:00
might_sleep ( ) ;
2010-03-30 17:56:24 +04:00
if ( cfg ) {
2011-05-13 06:44:01 +04:00
synchronize_rcu ( ) ;
2010-03-30 17:56:24 +04:00
kfree ( cfg - > mux ) ;
2011-05-13 06:44:06 +04:00
cfctrl_remove ( cfg - > ctrl ) ;
2010-03-30 17:56:24 +04:00
kfree ( cfg ) ;
}
}
static void cfctrl_resp_func ( void )
{
}
2011-05-13 06:44:01 +04:00
static struct cfcnfg_phyinfo * cfcnfg_get_phyinfo_rcu ( struct cfcnfg * cnfg ,
u8 phyid )
{
struct cfcnfg_phyinfo * phy ;
list_for_each_entry_rcu ( phy , & cnfg - > phys , node )
if ( phy - > id = = phyid )
return phy ;
return NULL ;
}
2010-03-30 17:56:24 +04:00
static void cfctrl_enum_resp ( void )
{
}
2011-05-13 06:44:05 +04:00
static struct dev_info * cfcnfg_get_phyid ( struct cfcnfg * cnfg ,
2010-03-30 17:56:24 +04:00
enum cfcnfg_phy_preference phy_pref )
{
/* Try to match with specified preference */
2011-05-13 06:44:01 +04:00
struct cfcnfg_phyinfo * phy ;
list_for_each_entry_rcu ( phy , & cnfg - > phys , node ) {
if ( phy - > up & & phy - > pref = = phy_pref & &
phy - > frm_layer ! = NULL )
return & phy - > dev_info ;
2010-03-30 17:56:24 +04:00
}
2011-05-13 06:44:01 +04:00
/* Otherwise just return something */
list_for_each_entry_rcu ( phy , & cnfg - > phys , node )
if ( phy - > up )
return & phy - > dev_info ;
2010-03-30 17:56:24 +04:00
return NULL ;
}
2011-05-13 06:44:05 +04:00
static int cfcnfg_get_id_from_ifi ( struct cfcnfg * cnfg , int ifi )
2010-03-30 17:56:24 +04:00
{
2011-05-13 06:44:01 +04:00
struct cfcnfg_phyinfo * phy ;
list_for_each_entry_rcu ( phy , & cnfg - > phys , node )
if ( phy - > ifindex = = ifi & & phy - > up )
return phy - > id ;
2010-11-01 14:52:47 +03:00
return - ENODEV ;
2010-03-30 17:56:24 +04:00
}
2011-05-13 06:44:05 +04:00
int caif_disconnect_client ( struct net * net , struct cflayer * adap_layer )
2010-03-30 17:56:24 +04:00
{
2011-05-22 15:18:51 +04:00
u8 channel_id ;
2011-05-13 06:44:05 +04:00
struct cfcnfg * cfg = get_cfcnfg ( net ) ;
2011-01-15 06:06:39 +03:00
2010-03-30 17:56:24 +04:00
caif_assert ( adap_layer ! = NULL ) ;
2011-05-13 06:44:01 +04:00
cfctrl_cancel_req ( cfg - > ctrl , adap_layer ) ;
2011-05-22 15:18:51 +04:00
channel_id = adap_layer - > id ;
if ( channel_id ! = 0 ) {
struct cflayer * servl ;
servl = cfmuxl_remove_uplayer ( cfg - > mux , channel_id ) ;
if ( servl ! = NULL )
layer_set_up ( servl , NULL ) ;
} else
pr_debug ( " nothing to disconnect \n " ) ;
cfctrl_linkdown_req ( cfg - > ctrl , channel_id , adap_layer ) ;
2011-05-13 06:44:01 +04:00
/* Do RCU sync before initiating cleanup */
synchronize_rcu ( ) ;
2010-04-28 12:54:37 +04:00
if ( adap_layer - > ctrlcmd ! = NULL )
adap_layer - > ctrlcmd ( adap_layer , CAIF_CTRLCMD_DEINIT_RSP , 0 ) ;
2011-05-22 15:18:51 +04:00
return 0 ;
2010-03-30 17:56:24 +04:00
}
2011-05-13 06:44:05 +04:00
EXPORT_SYMBOL ( caif_disconnect_client ) ;
2010-04-28 12:54:36 +04:00
2010-04-28 12:54:37 +04:00
static void cfcnfg_linkdestroy_rsp ( struct cflayer * layer , u8 channel_id )
2010-03-30 17:56:24 +04:00
{
}
2011-04-11 14:43:50 +04:00
static const int protohead [ CFCTRL_SRV_MASK ] = {
2010-06-17 10:55:40 +04:00
[ CFCTRL_SRV_VEI ] = 4 ,
[ CFCTRL_SRV_DATAGRAM ] = 7 ,
[ CFCTRL_SRV_UTIL ] = 4 ,
[ CFCTRL_SRV_RFM ] = 3 ,
[ CFCTRL_SRV_DBG ] = 3 ,
} ;
2011-05-13 06:44:05 +04:00
static int caif_connect_req_to_link_param ( struct cfcnfg * cnfg ,
struct caif_connect_request * s ,
struct cfctrl_link_param * l )
{
struct dev_info * dev_info ;
enum cfcnfg_phy_preference pref ;
int res ;
memset ( l , 0 , sizeof ( * l ) ) ;
/* In caif protocol low value is high priority */
l - > priority = CAIF_PRIO_MAX - s - > priority + 1 ;
if ( s - > ifindex ! = 0 ) {
res = cfcnfg_get_id_from_ifi ( cnfg , s - > ifindex ) ;
if ( res < 0 )
return res ;
l - > phyid = res ;
} else {
switch ( s - > link_selector ) {
case CAIF_LINK_HIGH_BANDW :
pref = CFPHYPREF_HIGH_BW ;
break ;
case CAIF_LINK_LOW_LATENCY :
pref = CFPHYPREF_LOW_LAT ;
break ;
default :
return - EINVAL ;
}
dev_info = cfcnfg_get_phyid ( cnfg , pref ) ;
if ( dev_info = = NULL )
return - ENODEV ;
l - > phyid = dev_info - > id ;
}
switch ( s - > protocol ) {
case CAIFPROTO_AT :
l - > linktype = CFCTRL_SRV_VEI ;
l - > endpoint = ( s - > sockaddr . u . at . type > > 2 ) & 0x3 ;
l - > chtype = s - > sockaddr . u . at . type & 0x3 ;
break ;
case CAIFPROTO_DATAGRAM :
l - > linktype = CFCTRL_SRV_DATAGRAM ;
l - > chtype = 0x00 ;
l - > u . datagram . connid = s - > sockaddr . u . dgm . connection_id ;
break ;
case CAIFPROTO_DATAGRAM_LOOP :
l - > linktype = CFCTRL_SRV_DATAGRAM ;
l - > chtype = 0x03 ;
l - > endpoint = 0x00 ;
l - > u . datagram . connid = s - > sockaddr . u . dgm . connection_id ;
break ;
case CAIFPROTO_RFM :
l - > linktype = CFCTRL_SRV_RFM ;
l - > u . datagram . connid = s - > sockaddr . u . rfm . connection_id ;
strncpy ( l - > u . rfm . volume , s - > sockaddr . u . rfm . volume ,
sizeof ( l - > u . rfm . volume ) - 1 ) ;
l - > u . rfm . volume [ sizeof ( l - > u . rfm . volume ) - 1 ] = 0 ;
break ;
case CAIFPROTO_UTIL :
l - > linktype = CFCTRL_SRV_UTIL ;
l - > endpoint = 0x00 ;
l - > chtype = 0x00 ;
strncpy ( l - > u . utility . name , s - > sockaddr . u . util . service ,
sizeof ( l - > u . utility . name ) - 1 ) ;
l - > u . utility . name [ sizeof ( l - > u . utility . name ) - 1 ] = 0 ;
caif_assert ( sizeof ( l - > u . utility . name ) > 10 ) ;
l - > u . utility . paramlen = s - > param . size ;
if ( l - > u . utility . paramlen > sizeof ( l - > u . utility . params ) )
l - > u . utility . paramlen = sizeof ( l - > u . utility . params ) ;
memcpy ( l - > u . utility . params , s - > param . data ,
l - > u . utility . paramlen ) ;
break ;
case CAIFPROTO_DEBUG :
l - > linktype = CFCTRL_SRV_DBG ;
l - > endpoint = s - > sockaddr . u . dbg . service ;
l - > chtype = s - > sockaddr . u . dbg . type ;
break ;
default :
return - EINVAL ;
}
return 0 ;
}
int caif_connect_client ( struct net * net , struct caif_connect_request * conn_req ,
struct cflayer * adap_layer , int * ifindex ,
2010-06-17 10:55:40 +04:00
int * proto_head ,
int * proto_tail )
2010-03-30 17:56:24 +04:00
{
struct cflayer * frml ;
2011-05-13 06:44:01 +04:00
struct cfcnfg_phyinfo * phy ;
int err ;
2011-05-13 06:44:05 +04:00
struct cfctrl_link_param param ;
struct cfcnfg * cfg = get_cfcnfg ( net ) ;
caif_assert ( cfg ! = NULL ) ;
2011-05-13 06:44:01 +04:00
rcu_read_lock ( ) ;
2011-05-13 06:44:05 +04:00
err = caif_connect_req_to_link_param ( cfg , conn_req , & param ) ;
if ( err )
goto unlock ;
phy = cfcnfg_get_phyinfo_rcu ( cfg , param . phyid ) ;
2011-05-13 06:44:01 +04:00
if ( ! phy ) {
err = - ENODEV ;
goto unlock ;
}
err = - EINVAL ;
2010-03-30 17:56:24 +04:00
if ( adap_layer = = NULL ) {
2010-09-06 01:31:11 +04:00
pr_err ( " adap_layer is zero \n " ) ;
2011-05-13 06:44:01 +04:00
goto unlock ;
2010-03-30 17:56:24 +04:00
}
if ( adap_layer - > receive = = NULL ) {
2010-09-06 01:31:11 +04:00
pr_err ( " adap_layer->receive is NULL \n " ) ;
2011-05-13 06:44:01 +04:00
goto unlock ;
2010-03-30 17:56:24 +04:00
}
if ( adap_layer - > ctrlcmd = = NULL ) {
2010-09-06 01:31:11 +04:00
pr_err ( " adap_layer->ctrlcmd == NULL \n " ) ;
2011-05-13 06:44:01 +04:00
goto unlock ;
2010-03-30 17:56:24 +04:00
}
2011-05-13 06:44:01 +04:00
err = - ENODEV ;
frml = phy - > frm_layer ;
2010-03-30 17:56:24 +04:00
if ( frml = = NULL ) {
2010-09-06 01:31:11 +04:00
pr_err ( " Specified PHY type does not exist! \n " ) ;
2011-05-13 06:44:01 +04:00
goto unlock ;
2010-03-30 17:56:24 +04:00
}
2011-05-13 06:44:05 +04:00
caif_assert ( param . phyid = = phy - > id ) ;
2011-05-13 06:44:01 +04:00
caif_assert ( phy - > frm_layer - > id = =
2011-05-13 06:44:05 +04:00
param . phyid ) ;
2011-05-13 06:44:01 +04:00
caif_assert ( phy - > phy_layer - > id = =
2011-05-13 06:44:05 +04:00
param . phyid ) ;
2010-06-17 10:55:40 +04:00
2011-05-13 06:44:01 +04:00
* ifindex = phy - > ifindex ;
* proto_tail = 2 ;
2010-06-17 10:55:40 +04:00
* proto_head =
2011-05-13 06:44:05 +04:00
protohead [ param . linktype ] + ( phy - > use_stx ? 1 : 0 ) ;
2010-06-17 10:55:40 +04:00
2011-05-13 06:44:01 +04:00
rcu_read_unlock ( ) ;
2010-06-17 10:55:40 +04:00
2010-03-30 17:56:24 +04:00
/* FIXME: ENUMERATE INITIALLY WHEN ACTIVATING PHYSICAL INTERFACE */
2011-05-13 06:44:05 +04:00
cfctrl_enum_req ( cfg - > ctrl , param . phyid ) ;
return cfctrl_linkup_request ( cfg - > ctrl , & param , adap_layer ) ;
2011-05-13 06:44:01 +04:00
unlock :
rcu_read_unlock ( ) ;
return err ;
2010-03-30 17:56:24 +04:00
}
2011-05-13 06:44:05 +04:00
EXPORT_SYMBOL ( caif_connect_client ) ;
2010-03-30 17:56:24 +04:00
2010-04-28 12:54:35 +04:00
static void cfcnfg_reject_rsp ( struct cflayer * layer , u8 channel_id ,
2010-03-30 17:56:24 +04:00
struct cflayer * adapt_layer )
{
if ( adapt_layer ! = NULL & & adapt_layer - > ctrlcmd ! = NULL )
adapt_layer - > ctrlcmd ( adapt_layer ,
CAIF_CTRLCMD_INIT_FAIL_RSP , 0 ) ;
}
static void
2010-04-28 12:54:35 +04:00
cfcnfg_linkup_rsp ( struct cflayer * layer , u8 channel_id , enum cfctrl_srv serv ,
2011-05-13 06:44:01 +04:00
u8 phyid , struct cflayer * adapt_layer )
2010-03-30 17:56:24 +04:00
{
struct cfcnfg * cnfg = container_obj ( layer ) ;
struct cflayer * servicel = NULL ;
struct cfcnfg_phyinfo * phyinfo ;
2010-06-17 10:55:40 +04:00
struct net_device * netdev ;
2011-05-22 15:18:51 +04:00
if ( channel_id = = 0 ) {
pr_warn ( " received channel_id zero \n " ) ;
if ( adapt_layer ! = NULL & & adapt_layer - > ctrlcmd ! = NULL )
adapt_layer - > ctrlcmd ( adapt_layer ,
CAIF_CTRLCMD_INIT_FAIL_RSP , 0 ) ;
return ;
}
2011-05-13 06:44:01 +04:00
rcu_read_lock ( ) ;
2010-03-30 17:56:24 +04:00
if ( adapt_layer = = NULL ) {
2011-05-13 06:44:01 +04:00
pr_debug ( " link setup response but no client exist, "
" send linkdown back \n " ) ;
2010-04-28 12:54:37 +04:00
cfctrl_linkdown_req ( cnfg - > ctrl , channel_id , NULL ) ;
2011-05-13 06:44:01 +04:00
goto unlock ;
2010-03-30 17:56:24 +04:00
}
caif_assert ( cnfg ! = NULL ) ;
caif_assert ( phyid ! = 0 ) ;
2011-05-13 06:44:01 +04:00
phyinfo = cfcnfg_get_phyinfo_rcu ( cnfg , phyid ) ;
if ( phyinfo = = NULL ) {
pr_err ( " ERROR: Link Layer Device dissapeared "
" while connecting \n " ) ;
goto unlock ;
}
caif_assert ( phyinfo ! = NULL ) ;
2010-03-30 17:56:24 +04:00
caif_assert ( phyinfo - > id = = phyid ) ;
caif_assert ( phyinfo - > phy_layer ! = NULL ) ;
caif_assert ( phyinfo - > phy_layer - > id = = phyid ) ;
2010-04-28 12:54:35 +04:00
adapt_layer - > id = channel_id ;
2010-03-30 17:56:24 +04:00
switch ( serv ) {
case CFCTRL_SRV_VEI :
2010-04-28 12:54:35 +04:00
servicel = cfvei_create ( channel_id , & phyinfo - > dev_info ) ;
2010-03-30 17:56:24 +04:00
break ;
case CFCTRL_SRV_DATAGRAM :
2011-05-13 06:44:01 +04:00
servicel = cfdgml_create ( channel_id ,
& phyinfo - > dev_info ) ;
2010-03-30 17:56:24 +04:00
break ;
case CFCTRL_SRV_RFM :
2010-06-17 10:55:40 +04:00
netdev = phyinfo - > dev_info . dev ;
2010-06-17 10:55:39 +04:00
servicel = cfrfml_create ( channel_id , & phyinfo - > dev_info ,
2010-06-17 10:55:40 +04:00
netdev - > mtu ) ;
2010-03-30 17:56:24 +04:00
break ;
case CFCTRL_SRV_UTIL :
2010-04-28 12:54:35 +04:00
servicel = cfutill_create ( channel_id , & phyinfo - > dev_info ) ;
2010-03-30 17:56:24 +04:00
break ;
case CFCTRL_SRV_VIDEO :
2010-04-28 12:54:35 +04:00
servicel = cfvidl_create ( channel_id , & phyinfo - > dev_info ) ;
2010-03-30 17:56:24 +04:00
break ;
case CFCTRL_SRV_DBG :
2010-04-28 12:54:35 +04:00
servicel = cfdbgl_create ( channel_id , & phyinfo - > dev_info ) ;
2010-03-30 17:56:24 +04:00
break ;
default :
2011-05-13 06:44:01 +04:00
pr_err ( " Protocol error. Link setup response "
" - unknown channel type \n " ) ;
goto unlock ;
2010-03-30 17:56:24 +04:00
}
2011-08-25 17:22:24 +04:00
if ( ! servicel )
2011-05-13 06:44:01 +04:00
goto unlock ;
2010-03-30 17:56:24 +04:00
layer_set_dn ( servicel , cnfg - > mux ) ;
2010-04-28 12:54:35 +04:00
cfmuxl_set_uplayer ( cnfg - > mux , servicel , channel_id ) ;
2010-03-30 17:56:24 +04:00
layer_set_up ( servicel , adapt_layer ) ;
layer_set_dn ( adapt_layer , servicel ) ;
2011-05-13 06:44:01 +04:00
rcu_read_unlock ( ) ;
2010-03-30 17:56:24 +04:00
servicel - > ctrlcmd ( servicel , CAIF_CTRLCMD_INIT_RSP , 0 ) ;
2011-05-13 06:44:01 +04:00
return ;
unlock :
rcu_read_unlock ( ) ;
2010-03-30 17:56:24 +04:00
}
void
cfcnfg_add_phy_layer ( struct cfcnfg * cnfg , enum cfcnfg_phy_type phy_type ,
2010-06-17 10:55:40 +04:00
struct net_device * dev , struct cflayer * phy_layer ,
2011-05-13 06:44:05 +04:00
enum cfcnfg_phy_preference pref ,
2010-03-30 17:56:24 +04:00
bool fcs , bool stx )
{
struct cflayer * frml ;
struct cflayer * phy_driver = NULL ;
2011-05-13 06:44:01 +04:00
struct cfcnfg_phyinfo * phyinfo ;
2010-03-30 17:56:24 +04:00
int i ;
2011-05-13 06:44:01 +04:00
u8 phyid ;
2010-03-30 17:56:24 +04:00
2011-05-13 06:44:01 +04:00
mutex_lock ( & cnfg - > lock ) ;
2010-03-30 17:56:24 +04:00
2011-05-13 06:44:01 +04:00
/* CAIF protocol allow maximum 6 link-layers */
for ( i = 0 ; i < 7 ; i + + ) {
phyid = ( dev - > ifindex + i ) & 0x7 ;
if ( phyid = = 0 )
continue ;
if ( cfcnfg_get_phyinfo_rcu ( cnfg , phyid ) = = NULL )
goto got_phyid ;
2010-03-30 17:56:24 +04:00
}
2011-05-13 06:44:01 +04:00
pr_warn ( " Too many CAIF Link Layers (max 6) \n " ) ;
goto out ;
got_phyid :
phyinfo = kzalloc ( sizeof ( struct cfcnfg_phyinfo ) , GFP_ATOMIC ) ;
2010-03-30 17:56:24 +04:00
switch ( phy_type ) {
case CFPHYTYPE_FRAG :
phy_driver =
2011-05-13 06:44:01 +04:00
cfserl_create ( CFPHYTYPE_FRAG , phyid , stx ) ;
2011-08-25 17:22:24 +04:00
if ( ! phy_driver )
2011-05-13 06:44:01 +04:00
goto out ;
2010-03-30 17:56:24 +04:00
break ;
case CFPHYTYPE_CAIF :
phy_driver = NULL ;
break ;
default :
2011-05-13 06:44:01 +04:00
goto out ;
2010-03-30 17:56:24 +04:00
}
2011-05-13 06:44:01 +04:00
phy_layer - > id = phyid ;
phyinfo - > pref = pref ;
phyinfo - > id = phyid ;
phyinfo - > dev_info . id = phyid ;
phyinfo - > dev_info . dev = dev ;
phyinfo - > phy_layer = phy_layer ;
phyinfo - > ifindex = dev - > ifindex ;
phyinfo - > use_stx = stx ;
phyinfo - > use_fcs = fcs ;
2010-06-17 10:55:40 +04:00
2011-05-13 06:44:01 +04:00
frml = cffrml_create ( phyid , fcs ) ;
2010-03-30 17:56:24 +04:00
if ( ! frml ) {
2011-05-13 06:44:01 +04:00
kfree ( phyinfo ) ;
goto out ;
2010-03-30 17:56:24 +04:00
}
2011-05-13 06:44:01 +04:00
phyinfo - > frm_layer = frml ;
2010-03-30 17:56:24 +04:00
layer_set_up ( frml , cnfg - > mux ) ;
if ( phy_driver ! = NULL ) {
2011-05-13 06:44:01 +04:00
phy_driver - > id = phyid ;
2010-03-30 17:56:24 +04:00
layer_set_dn ( frml , phy_driver ) ;
layer_set_up ( phy_driver , frml ) ;
layer_set_dn ( phy_driver , phy_layer ) ;
layer_set_up ( phy_layer , phy_driver ) ;
} else {
layer_set_dn ( frml , phy_layer ) ;
layer_set_up ( phy_layer , frml ) ;
}
2011-05-13 06:44:01 +04:00
list_add_rcu ( & phyinfo - > node , & cnfg - > phys ) ;
out :
mutex_unlock ( & cnfg - > lock ) ;
2010-03-30 17:56:24 +04:00
}
EXPORT_SYMBOL ( cfcnfg_add_phy_layer ) ;
2011-05-13 06:44:01 +04:00
int cfcnfg_set_phy_state ( struct cfcnfg * cnfg , struct cflayer * phy_layer ,
bool up )
{
struct cfcnfg_phyinfo * phyinfo ;
rcu_read_lock ( ) ;
phyinfo = cfcnfg_get_phyinfo_rcu ( cnfg , phy_layer - > id ) ;
if ( phyinfo = = NULL ) {
rcu_read_unlock ( ) ;
return - ENODEV ;
}
if ( phyinfo - > up = = up ) {
rcu_read_unlock ( ) ;
return 0 ;
}
phyinfo - > up = up ;
if ( up ) {
cffrml_hold ( phyinfo - > frm_layer ) ;
cfmuxl_set_dnlayer ( cnfg - > mux , phyinfo - > frm_layer ,
phy_layer - > id ) ;
} else {
cfmuxl_remove_dnlayer ( cnfg - > mux , phy_layer - > id ) ;
cffrml_put ( phyinfo - > frm_layer ) ;
}
rcu_read_unlock ( ) ;
return 0 ;
}
EXPORT_SYMBOL ( cfcnfg_set_phy_state ) ;
2010-03-30 17:56:24 +04:00
int cfcnfg_del_phy_layer ( struct cfcnfg * cnfg , struct cflayer * phy_layer )
{
struct cflayer * frml , * frml_dn ;
u16 phyid ;
2011-05-13 06:44:01 +04:00
struct cfcnfg_phyinfo * phyinfo ;
might_sleep ( ) ;
mutex_lock ( & cnfg - > lock ) ;
2010-03-30 17:56:24 +04:00
phyid = phy_layer - > id ;
2011-05-13 06:44:01 +04:00
phyinfo = cfcnfg_get_phyinfo_rcu ( cnfg , phyid ) ;
2011-05-13 06:44:05 +04:00
if ( phyinfo = = NULL ) {
mutex_unlock ( & cnfg - > lock ) ;
2011-05-13 06:44:01 +04:00
return 0 ;
2011-05-13 06:44:05 +04:00
}
2011-05-13 06:44:01 +04:00
caif_assert ( phyid = = phyinfo - > id ) ;
caif_assert ( phy_layer = = phyinfo - > phy_layer ) ;
2010-03-30 17:56:24 +04:00
caif_assert ( phy_layer - > id = = phyid ) ;
2011-05-13 06:44:01 +04:00
caif_assert ( phyinfo - > frm_layer - > id = = phyid ) ;
2011-05-13 06:44:05 +04:00
list_del_rcu ( & phyinfo - > node ) ;
synchronize_rcu ( ) ;
2011-05-13 06:44:02 +04:00
/* Fail if reference count is not zero */
if ( cffrml_refcnt_read ( phyinfo - > frm_layer ) ! = 0 ) {
pr_info ( " Wait for device inuse \n " ) ;
2011-05-13 06:44:05 +04:00
list_add_rcu ( & phyinfo - > node , & cnfg - > phys ) ;
2011-05-13 06:44:02 +04:00
mutex_unlock ( & cnfg - > lock ) ;
return - EAGAIN ;
}
2011-05-13 06:44:01 +04:00
frml = phyinfo - > frm_layer ;
2010-03-30 17:56:24 +04:00
frml_dn = frml - > dn ;
cffrml_set_uplayer ( frml , NULL ) ;
cffrml_set_dnlayer ( frml , NULL ) ;
if ( phy_layer ! = frml_dn ) {
layer_set_up ( frml_dn , NULL ) ;
layer_set_dn ( frml_dn , NULL ) ;
}
layer_set_up ( phy_layer , NULL ) ;
2011-05-13 06:44:01 +04:00
if ( phyinfo - > phy_layer ! = frml_dn )
kfree ( frml_dn ) ;
2011-05-13 06:44:02 +04:00
cffrml_free ( frml ) ;
2011-05-13 06:44:01 +04:00
kfree ( phyinfo ) ;
mutex_unlock ( & cnfg - > lock ) ;
2010-03-30 17:56:24 +04:00
return 0 ;
}
EXPORT_SYMBOL ( cfcnfg_del_phy_layer ) ;