4 * Copyright (C) ST-Ericsson AB 2010
5 * Author: Sjur Brendeland/sjur.brandeland@stericsson.com
6 * License terms: GNU General Public License (GPL) version 2
9 #define pr_fmt(fmt) KBUILD_MODNAME ":%s(): " fmt, __func__
11 #include <linux/stddef.h>
12 #include <linux/spinlock.h>
13 #include <linux/slab.h>
14 #include <linux/crc-ccitt.h>
15 #include <linux/netdevice.h>
16 #include <net/caif/caif_layer.h>
17 #include <net/caif/cfpkt.h>
18 #include <net/caif/cffrml.h>
20 #define container_obj(layr) container_of(layr, struct cffrml, layer)
24 bool dofcs
; /* !< FCS active */
25 int __percpu
*pcpu_refcnt
;
28 static int cffrml_receive(struct cflayer
*layr
, struct cfpkt
*pkt
);
29 static int cffrml_transmit(struct cflayer
*layr
, struct cfpkt
*pkt
);
30 static void cffrml_ctrlcmd(struct cflayer
*layr
, enum caif_ctrlcmd ctrl
,
33 static u32 cffrml_rcv_error
;
34 static u32 cffrml_rcv_checsum_error
;
35 struct cflayer
*cffrml_create(u16 phyid
, bool use_fcs
)
37 struct cffrml
*this = kmalloc(sizeof(struct cffrml
), GFP_ATOMIC
);
39 pr_warn("Out of memory\n");
42 this->pcpu_refcnt
= alloc_percpu(int);
43 if (this->pcpu_refcnt
== NULL
) {
48 caif_assert(offsetof(struct cffrml
, layer
) == 0);
50 memset(this, 0, sizeof(struct cflayer
));
51 this->layer
.receive
= cffrml_receive
;
52 this->layer
.transmit
= cffrml_transmit
;
53 this->layer
.ctrlcmd
= cffrml_ctrlcmd
;
54 snprintf(this->layer
.name
, CAIF_LAYER_NAME_SZ
, "frm%d", phyid
);
55 this->dofcs
= use_fcs
;
56 this->layer
.id
= phyid
;
57 return (struct cflayer
*) this;
60 void cffrml_free(struct cflayer
*layer
)
62 struct cffrml
*this = container_obj(layer
);
63 free_percpu(this->pcpu_refcnt
);
67 void cffrml_set_uplayer(struct cflayer
*this, struct cflayer
*up
)
72 void cffrml_set_dnlayer(struct cflayer
*this, struct cflayer
*dn
)
77 static u16
cffrml_checksum(u16 chks
, void *buf
, u16 len
)
79 /* FIXME: FCS should be moved to glue in order to use OS-Specific
82 return crc_ccitt(chks
, buf
, len
);
85 static int cffrml_receive(struct cflayer
*layr
, struct cfpkt
*pkt
)
92 this = container_obj(layr
);
94 cfpkt_extr_head(pkt
, &tmp
, 2);
95 len
= le16_to_cpu(tmp
);
97 /* Subtract for FCS on length if FCS is not used. */
101 if (cfpkt_setlen(pkt
, len
) < 0) {
103 pr_err("Framing length error (%d)\n", len
);
108 * Don't do extract if FCS is false, rather do setlen - then we don't
112 cfpkt_extr_trail(pkt
, &tmp
, 2);
113 hdrchks
= le16_to_cpu(tmp
);
114 pktchks
= cfpkt_iterate(pkt
, cffrml_checksum
, 0xffff);
115 if (pktchks
!= hdrchks
) {
116 cfpkt_add_trail(pkt
, &tmp
, 2);
118 ++cffrml_rcv_checsum_error
;
119 pr_info("Frame checksum error (0x%x != 0x%x)\n",
124 if (cfpkt_erroneous(pkt
)) {
126 pr_err("Packet is erroneous!\n");
131 if (layr
->up
== NULL
) {
132 pr_err("Layr up is missing!\n");
137 return layr
->up
->receive(layr
->up
, pkt
);
140 static int cffrml_transmit(struct cflayer
*layr
, struct cfpkt
*pkt
)
145 struct cffrml
*this = container_obj(layr
);
147 chks
= cfpkt_iterate(pkt
, cffrml_checksum
, 0xffff);
148 tmp
= cpu_to_le16(chks
);
149 cfpkt_add_trail(pkt
, &tmp
, 2);
151 cfpkt_pad_trail(pkt
, 2);
153 len
= cfpkt_getlen(pkt
);
154 tmp
= cpu_to_le16(len
);
155 cfpkt_add_head(pkt
, &tmp
, 2);
156 cfpkt_info(pkt
)->hdr_len
+= 2;
157 if (cfpkt_erroneous(pkt
)) {
158 pr_err("Packet is erroneous!\n");
163 if (layr
->dn
== NULL
) {
168 return layr
->dn
->transmit(layr
->dn
, pkt
);
171 static void cffrml_ctrlcmd(struct cflayer
*layr
, enum caif_ctrlcmd ctrl
,
174 if (layr
->up
&& layr
->up
->ctrlcmd
)
175 layr
->up
->ctrlcmd(layr
->up
, ctrl
, layr
->id
);
178 void cffrml_put(struct cflayer
*layr
)
180 struct cffrml
*this = container_obj(layr
);
181 if (layr
!= NULL
&& this->pcpu_refcnt
!= NULL
)
182 irqsafe_cpu_dec(*this->pcpu_refcnt
);
185 void cffrml_hold(struct cflayer
*layr
)
187 struct cffrml
*this = container_obj(layr
);
188 if (layr
!= NULL
&& this->pcpu_refcnt
!= NULL
)
189 irqsafe_cpu_inc(*this->pcpu_refcnt
);
192 int cffrml_refcnt_read(struct cflayer
*layr
)
195 struct cffrml
*this = container_obj(layr
);
196 for_each_possible_cpu(i
)
197 refcnt
+= *per_cpu_ptr(this->pcpu_refcnt
, i
);