2010-03-30 17:56:23 +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:23 +04:00
# include <linux/kernel.h>
# include <linux/types.h>
# include <linux/errno.h>
# include <linux/slab.h>
2011-05-13 06:44:03 +04:00
# include <linux/module.h>
2012-04-12 12:27:24 +04:00
# include <linux/pkt_sched.h>
2010-03-30 17:56:23 +04:00
# include <net/caif/caif_layer.h>
# include <net/caif/cfsrvl.h>
# include <net/caif/cfpkt.h>
# define SRVL_CTRL_PKT_SIZE 1
# define SRVL_FLOW_OFF 0x81
# define SRVL_FLOW_ON 0x80
# define SRVL_SET_PIN 0x82
# define SRVL_CTRL_PKT_SIZE 1
# define container_obj(layr) container_of(layr, struct cfsrvl, layer)
static void cfservl_ctrlcmd ( struct cflayer * layr , enum caif_ctrlcmd ctrl ,
int phyid )
{
struct cfsrvl * service = container_obj ( layr ) ;
2010-06-17 10:55:38 +04:00
2011-05-13 06:44:03 +04:00
if ( layr - > up = = NULL | | layr - > up - > ctrlcmd = = NULL )
return ;
2010-06-17 10:55:38 +04:00
2010-03-30 17:56:23 +04:00
switch ( ctrl ) {
case CAIF_CTRLCMD_INIT_RSP :
service - > open = true ;
layr - > up - > ctrlcmd ( layr - > up , ctrl , phyid ) ;
break ;
case CAIF_CTRLCMD_DEINIT_RSP :
case CAIF_CTRLCMD_INIT_FAIL_RSP :
service - > open = false ;
layr - > up - > ctrlcmd ( layr - > up , ctrl , phyid ) ;
break ;
case _CAIF_CTRLCMD_PHYIF_FLOW_OFF_IND :
if ( phyid ! = service - > dev_info . id )
break ;
if ( service - > modem_flow_on )
layr - > up - > ctrlcmd ( layr - > up ,
CAIF_CTRLCMD_FLOW_OFF_IND , phyid ) ;
service - > phy_flow_on = false ;
break ;
case _CAIF_CTRLCMD_PHYIF_FLOW_ON_IND :
if ( phyid ! = service - > dev_info . id )
return ;
if ( service - > modem_flow_on ) {
layr - > up - > ctrlcmd ( layr - > up ,
CAIF_CTRLCMD_FLOW_ON_IND ,
phyid ) ;
}
service - > phy_flow_on = true ;
break ;
case CAIF_CTRLCMD_FLOW_OFF_IND :
if ( service - > phy_flow_on ) {
layr - > up - > ctrlcmd ( layr - > up ,
CAIF_CTRLCMD_FLOW_OFF_IND , phyid ) ;
}
service - > modem_flow_on = false ;
break ;
case CAIF_CTRLCMD_FLOW_ON_IND :
if ( service - > phy_flow_on ) {
layr - > up - > ctrlcmd ( layr - > up ,
CAIF_CTRLCMD_FLOW_ON_IND , phyid ) ;
}
service - > modem_flow_on = true ;
break ;
case _CAIF_CTRLCMD_PHYIF_DOWN_IND :
/* In case interface is down, let's fake a remove shutdown */
layr - > up - > ctrlcmd ( layr - > up ,
CAIF_CTRLCMD_REMOTE_SHUTDOWN_IND , phyid ) ;
break ;
case CAIF_CTRLCMD_REMOTE_SHUTDOWN_IND :
layr - > up - > ctrlcmd ( layr - > up , ctrl , phyid ) ;
break ;
default :
2010-09-06 01:31:11 +04:00
pr_warn ( " Unexpected ctrl in cfsrvl (%d) \n " , ctrl ) ;
2010-03-30 17:56:23 +04:00
/* We have both modem and phy flow on, send flow on */
layr - > up - > ctrlcmd ( layr - > up , ctrl , phyid ) ;
service - > phy_flow_on = true ;
break ;
}
}
static int cfservl_modemcmd ( struct cflayer * layr , enum caif_modemcmd ctrl )
{
struct cfsrvl * service = container_obj ( layr ) ;
2010-06-17 10:55:38 +04:00
2010-03-30 17:56:23 +04:00
caif_assert ( layr ! = NULL ) ;
caif_assert ( layr - > dn ! = NULL ) ;
caif_assert ( layr - > dn - > transmit ! = NULL ) ;
2010-06-17 10:55:38 +04:00
if ( ! service - > supports_flowctrl )
return 0 ;
2010-03-30 17:56:23 +04:00
switch ( ctrl ) {
case CAIF_MODEMCMD_FLOW_ON_REQ :
{
struct cfpkt * pkt ;
struct caif_payload_info * info ;
u8 flow_on = SRVL_FLOW_ON ;
pkt = cfpkt_create ( SRVL_CTRL_PKT_SIZE ) ;
2011-08-25 17:22:24 +04:00
if ( ! pkt )
2010-03-30 17:56:23 +04:00
return - ENOMEM ;
if ( cfpkt_add_head ( pkt , & flow_on , 1 ) < 0 ) {
2010-09-06 01:31:11 +04:00
pr_err ( " Packet is erroneous! \n " ) ;
2010-03-30 17:56:23 +04:00
cfpkt_destroy ( pkt ) ;
return - EPROTO ;
}
info = cfpkt_info ( pkt ) ;
info - > channel_id = service - > layer . id ;
info - > hdr_len = 1 ;
info - > dev_info = & service - > dev_info ;
2012-04-12 12:27:24 +04:00
cfpkt_set_prio ( pkt , TC_PRIO_CONTROL ) ;
2010-03-30 17:56:23 +04:00
return layr - > dn - > transmit ( layr - > dn , pkt ) ;
}
case CAIF_MODEMCMD_FLOW_OFF_REQ :
{
struct cfpkt * pkt ;
struct caif_payload_info * info ;
u8 flow_off = SRVL_FLOW_OFF ;
pkt = cfpkt_create ( SRVL_CTRL_PKT_SIZE ) ;
2011-08-25 17:22:24 +04:00
if ( ! pkt )
2010-05-21 06:16:09 +04:00
return - ENOMEM ;
2010-03-30 17:56:23 +04:00
if ( cfpkt_add_head ( pkt , & flow_off , 1 ) < 0 ) {
2010-09-06 01:31:11 +04:00
pr_err ( " Packet is erroneous! \n " ) ;
2010-03-30 17:56:23 +04:00
cfpkt_destroy ( pkt ) ;
return - EPROTO ;
}
info = cfpkt_info ( pkt ) ;
info - > channel_id = service - > layer . id ;
info - > hdr_len = 1 ;
info - > dev_info = & service - > dev_info ;
2012-04-12 12:27:24 +04:00
cfpkt_set_prio ( pkt , TC_PRIO_CONTROL ) ;
2010-03-30 17:56:23 +04:00
return layr - > dn - > transmit ( layr - > dn , pkt ) ;
}
default :
break ;
}
return - EINVAL ;
}
2011-05-13 06:44:03 +04:00
static void cfsrvl_release ( struct cflayer * layer )
2010-06-17 10:55:39 +04:00
{
2011-05-13 06:44:03 +04:00
struct cfsrvl * service = container_of ( layer , struct cfsrvl , layer ) ;
2010-06-17 10:55:39 +04:00
kfree ( service ) ;
}
2010-03-30 17:56:23 +04:00
void cfsrvl_init ( struct cfsrvl * service ,
2010-06-17 10:55:38 +04:00
u8 channel_id ,
struct dev_info * dev_info ,
bool supports_flowctrl
)
2010-03-30 17:56:23 +04:00
{
caif_assert ( offsetof ( struct cfsrvl , layer ) = = 0 ) ;
service - > open = false ;
service - > modem_flow_on = true ;
service - > phy_flow_on = true ;
service - > layer . id = channel_id ;
service - > layer . ctrlcmd = cfservl_ctrlcmd ;
service - > layer . modemcmd = cfservl_modemcmd ;
service - > dev_info = * dev_info ;
2010-06-17 10:55:38 +04:00
service - > supports_flowctrl = supports_flowctrl ;
2010-06-17 10:55:39 +04:00
service - > release = cfsrvl_release ;
2010-04-28 12:54:36 +04:00
}
2010-03-30 17:56:23 +04:00
bool cfsrvl_ready ( struct cfsrvl * service , int * err )
{
if ( ! service - > open ) {
* err = - ENOTCONN ;
return false ;
}
2012-03-11 14:28:31 +04:00
return true ;
2010-03-30 17:56:23 +04:00
}
2011-05-13 06:44:03 +04:00
2010-03-30 17:56:23 +04:00
u8 cfsrvl_getphyid ( struct cflayer * layer )
{
struct cfsrvl * servl = container_obj ( layer ) ;
return servl - > dev_info . id ;
}
bool cfsrvl_phyid_match ( struct cflayer * layer , int phyid )
{
struct cfsrvl * servl = container_obj ( layer ) ;
return servl - > dev_info . id = = phyid ;
}
2011-05-13 06:44:03 +04:00
void caif_free_client ( struct cflayer * adap_layer )
{
struct cfsrvl * servl ;
if ( adap_layer = = NULL | | adap_layer - > dn = = NULL )
return ;
servl = container_obj ( adap_layer - > dn ) ;
servl - > release ( & servl - > layer ) ;
}
EXPORT_SYMBOL ( caif_free_client ) ;
void caif_client_register_refcnt ( struct cflayer * adapt_layer ,
void ( * hold ) ( struct cflayer * lyr ) ,
void ( * put ) ( struct cflayer * lyr ) )
{
struct cfsrvl * service ;
service = container_of ( adapt_layer - > dn , struct cfsrvl , layer ) ;
WARN_ON ( adapt_layer = = NULL | | adapt_layer - > dn = = NULL ) ;
service - > hold = hold ;
service - > put = put ;
}
EXPORT_SYMBOL ( caif_client_register_refcnt ) ;