2010-03-30 17:56:23 +04:00
/*
* CAIF Framing Layer .
*
* Copyright ( C ) ST - Ericsson AB 2010
2013-04-23 03:57:01 +04:00
* Author : Sjur Brendeland
2010-03-30 17:56:23 +04:00
* 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/stddef.h>
# include <linux/spinlock.h>
# include <linux/slab.h>
# include <linux/crc-ccitt.h>
2011-05-13 06:44:02 +04:00
# include <linux/netdevice.h>
2010-03-30 17:56:23 +04:00
# include <net/caif/caif_layer.h>
# include <net/caif/cfpkt.h>
# include <net/caif/cffrml.h>
# define container_obj(layr) container_of(layr, struct cffrml, layer)
struct cffrml {
struct cflayer layer ;
bool dofcs ; /* !< FCS active */
2011-05-13 06:44:02 +04:00
int __percpu * pcpu_refcnt ;
2010-03-30 17:56:23 +04:00
} ;
static int cffrml_receive ( struct cflayer * layr , struct cfpkt * pkt ) ;
static int cffrml_transmit ( struct cflayer * layr , struct cfpkt * pkt ) ;
static void cffrml_ctrlcmd ( struct cflayer * layr , enum caif_ctrlcmd ctrl ,
2013-03-06 23:39:57 +04:00
int phyid ) ;
2010-03-30 17:56:23 +04:00
static u32 cffrml_rcv_error ;
static u32 cffrml_rcv_checsum_error ;
struct cflayer * cffrml_create ( u16 phyid , bool use_fcs )
{
2011-08-25 17:22:24 +04:00
struct cffrml * this = kzalloc ( sizeof ( struct cffrml ) , GFP_ATOMIC ) ;
if ( ! this )
2010-03-30 17:56:23 +04:00
return NULL ;
2011-05-13 06:44:02 +04:00
this - > pcpu_refcnt = alloc_percpu ( int ) ;
if ( this - > pcpu_refcnt = = NULL ) {
kfree ( this ) ;
return NULL ;
}
2010-03-30 17:56:23 +04:00
caif_assert ( offsetof ( struct cffrml , layer ) = = 0 ) ;
this - > layer . receive = cffrml_receive ;
this - > layer . transmit = cffrml_transmit ;
this - > layer . ctrlcmd = cffrml_ctrlcmd ;
snprintf ( this - > layer . name , CAIF_LAYER_NAME_SZ , " frm%d " , phyid ) ;
this - > dofcs = use_fcs ;
this - > layer . id = phyid ;
return ( struct cflayer * ) this ;
}
2011-05-13 06:44:02 +04:00
void cffrml_free ( struct cflayer * layer )
{
struct cffrml * this = container_obj ( layer ) ;
free_percpu ( this - > pcpu_refcnt ) ;
kfree ( layer ) ;
}
2010-03-30 17:56:23 +04:00
void cffrml_set_uplayer ( struct cflayer * this , struct cflayer * up )
{
this - > up = up ;
}
void cffrml_set_dnlayer ( struct cflayer * this , struct cflayer * dn )
{
this - > dn = dn ;
}
static u16 cffrml_checksum ( u16 chks , void * buf , u16 len )
{
/* FIXME: FCS should be moved to glue in order to use OS-Specific
* solutions
*/
return crc_ccitt ( chks , buf , len ) ;
}
static int cffrml_receive ( struct cflayer * layr , struct cfpkt * pkt )
{
u16 tmp ;
u16 len ;
u16 hdrchks ;
2015-02-19 12:13:13 +03:00
int pktchks ;
2010-03-30 17:56:23 +04:00
struct cffrml * this ;
this = container_obj ( layr ) ;
cfpkt_extr_head ( pkt , & tmp , 2 ) ;
len = le16_to_cpu ( tmp ) ;
/* Subtract for FCS on length if FCS is not used. */
if ( ! this - > dofcs )
len - = 2 ;
if ( cfpkt_setlen ( pkt , len ) < 0 ) {
+ + cffrml_rcv_error ;
2010-09-06 01:31:11 +04:00
pr_err ( " Framing length error (%d) \n " , len ) ;
2010-03-30 17:56:23 +04:00
cfpkt_destroy ( pkt ) ;
return - EPROTO ;
}
/*
* Don ' t do extract if FCS is false , rather do setlen - then we don ' t
* get a cache - miss .
*/
if ( this - > dofcs ) {
cfpkt_extr_trail ( pkt , & tmp , 2 ) ;
hdrchks = le16_to_cpu ( tmp ) ;
pktchks = cfpkt_iterate ( pkt , cffrml_checksum , 0xffff ) ;
if ( pktchks ! = hdrchks ) {
cfpkt_add_trail ( pkt , & tmp , 2 ) ;
+ + cffrml_rcv_error ;
+ + cffrml_rcv_checsum_error ;
2010-09-06 01:31:11 +04:00
pr_info ( " Frame checksum error (0x%x != 0x%x) \n " ,
hdrchks , pktchks ) ;
2010-03-30 17:56:23 +04:00
return - EILSEQ ;
}
}
if ( cfpkt_erroneous ( pkt ) ) {
+ + cffrml_rcv_error ;
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 ;
}
2011-05-13 06:44:06 +04:00
if ( layr - > up = = NULL ) {
pr_err ( " Layr up is missing! \n " ) ;
cfpkt_destroy ( pkt ) ;
return - EINVAL ;
}
2010-03-30 17:56:23 +04:00
return layr - > up - > receive ( layr - > up , pkt ) ;
}
static int cffrml_transmit ( struct cflayer * layr , struct cfpkt * pkt )
{
u16 chks ;
u16 len ;
2011-11-22 01:46:24 +04:00
__le16 data ;
2010-03-30 17:56:23 +04:00
struct cffrml * this = container_obj ( layr ) ;
if ( this - > dofcs ) {
chks = cfpkt_iterate ( pkt , cffrml_checksum , 0xffff ) ;
2011-11-22 01:46:24 +04:00
data = cpu_to_le16 ( chks ) ;
cfpkt_add_trail ( pkt , & data , 2 ) ;
2010-03-30 17:56:23 +04:00
} else {
cfpkt_pad_trail ( pkt , 2 ) ;
}
len = cfpkt_getlen ( pkt ) ;
2011-11-22 01:46:24 +04:00
data = cpu_to_le16 ( len ) ;
cfpkt_add_head ( pkt , & data , 2 ) ;
2010-03-30 17:56:23 +04:00
cfpkt_info ( pkt ) - > hdr_len + = 2 ;
if ( cfpkt_erroneous ( pkt ) ) {
2010-09-06 01:31:11 +04:00
pr_err ( " Packet is erroneous! \n " ) ;
2011-05-13 06:44:06 +04:00
cfpkt_destroy ( pkt ) ;
2010-03-30 17:56:23 +04:00
return - EPROTO ;
}
2011-05-13 06:44:06 +04:00
if ( layr - > dn = = NULL ) {
cfpkt_destroy ( pkt ) ;
return - ENODEV ;
}
2011-04-11 14:43:51 +04:00
return layr - > dn - > transmit ( layr - > dn , pkt ) ;
2010-03-30 17:56:23 +04:00
}
static void cffrml_ctrlcmd ( struct cflayer * layr , enum caif_ctrlcmd ctrl ,
2013-03-06 23:39:57 +04:00
int phyid )
2010-03-30 17:56:23 +04:00
{
2011-05-13 06:44:06 +04:00
if ( layr - > up & & layr - > up - > ctrlcmd )
2010-03-30 17:56:23 +04:00
layr - > up - > ctrlcmd ( layr - > up , ctrl , layr - > id ) ;
}
2011-05-13 06:43:59 +04:00
void cffrml_put ( struct cflayer * layr )
{
2011-05-13 06:44:02 +04:00
struct cffrml * this = container_obj ( layr ) ;
if ( layr ! = NULL & & this - > pcpu_refcnt ! = NULL )
2011-12-22 21:58:51 +04:00
this_cpu_dec ( * this - > pcpu_refcnt ) ;
2011-05-13 06:43:59 +04:00
}
void cffrml_hold ( struct cflayer * layr )
{
2011-05-13 06:44:02 +04:00
struct cffrml * this = container_obj ( layr ) ;
if ( layr ! = NULL & & this - > pcpu_refcnt ! = NULL )
2011-12-22 21:58:51 +04:00
this_cpu_inc ( * this - > pcpu_refcnt ) ;
2011-05-13 06:44:02 +04:00
}
int cffrml_refcnt_read ( struct cflayer * layr )
{
int i , refcnt = 0 ;
struct cffrml * this = container_obj ( layr ) ;
for_each_possible_cpu ( i )
refcnt + = * per_cpu_ptr ( this - > pcpu_refcnt , i ) ;
return refcnt ;
2011-05-13 06:43:59 +04:00
}