1/*
2 * CAIF Framing Layer.
3 *
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
7 */
8
9#include <linux/stddef.h>
10#include <linux/spinlock.h>
11#include <linux/slab.h>
12#include <linux/crc-ccitt.h>
13#include <net/caif/caif_layer.h>
14#include <net/caif/cfpkt.h>
15#include <net/caif/cffrml.h>
16
17#define container_obj(layr) container_of(layr, struct cffrml, layer)
18
19struct cffrml {
20	struct cflayer layer;
21	bool dofcs;		/* !< FCS active */
22};
23
24static int cffrml_receive(struct cflayer *layr, struct cfpkt *pkt);
25static int cffrml_transmit(struct cflayer *layr, struct cfpkt *pkt);
26static void cffrml_ctrlcmd(struct cflayer *layr, enum caif_ctrlcmd ctrl,
27				int phyid);
28
29static u32 cffrml_rcv_error;
30static u32 cffrml_rcv_checsum_error;
31struct cflayer *cffrml_create(u16 phyid, bool use_fcs)
32{
33	struct cffrml *this = kmalloc(sizeof(struct cffrml), GFP_ATOMIC);
34	if (!this) {
35		pr_warning("CAIF: %s(): Out of memory\n", __func__);
36		return NULL;
37	}
38	caif_assert(offsetof(struct cffrml, layer) == 0);
39
40	memset(this, 0, sizeof(struct cflayer));
41	this->layer.receive = cffrml_receive;
42	this->layer.transmit = cffrml_transmit;
43	this->layer.ctrlcmd = cffrml_ctrlcmd;
44	snprintf(this->layer.name, CAIF_LAYER_NAME_SZ, "frm%d", phyid);
45	this->dofcs = use_fcs;
46	this->layer.id = phyid;
47	return (struct cflayer *) this;
48}
49
50void cffrml_set_uplayer(struct cflayer *this, struct cflayer *up)
51{
52	this->up = up;
53}
54
55void cffrml_set_dnlayer(struct cflayer *this, struct cflayer *dn)
56{
57	this->dn = dn;
58}
59
60static u16 cffrml_checksum(u16 chks, void *buf, u16 len)
61{
62	return crc_ccitt(chks, buf, len);
63}
64
65static int cffrml_receive(struct cflayer *layr, struct cfpkt *pkt)
66{
67	u16 tmp;
68	u16 len;
69	u16 hdrchks;
70	u16 pktchks;
71	struct cffrml *this;
72	this = container_obj(layr);
73
74	cfpkt_extr_head(pkt, &tmp, 2);
75	len = le16_to_cpu(tmp);
76
77	/* Subtract for FCS on length if FCS is not used. */
78	if (!this->dofcs)
79		len -= 2;
80
81	if (cfpkt_setlen(pkt, len) < 0) {
82		++cffrml_rcv_error;
83		pr_err("CAIF: %s():Framing length error (%d)\n", __func__, len);
84		cfpkt_destroy(pkt);
85		return -EPROTO;
86	}
87	/*
88	 * Don't do extract if FCS is false, rather do setlen - then we don't
89	 * get a cache-miss.
90	 */
91	if (this->dofcs) {
92		cfpkt_extr_trail(pkt, &tmp, 2);
93		hdrchks = le16_to_cpu(tmp);
94		pktchks = cfpkt_iterate(pkt, cffrml_checksum, 0xffff);
95		if (pktchks != hdrchks) {
96			cfpkt_add_trail(pkt, &tmp, 2);
97			++cffrml_rcv_error;
98			++cffrml_rcv_checsum_error;
99			pr_info("CAIF: %s(): Frame checksum error "
100				"(0x%x != 0x%x)\n", __func__, hdrchks, pktchks);
101			return -EILSEQ;
102		}
103	}
104	if (cfpkt_erroneous(pkt)) {
105		++cffrml_rcv_error;
106		pr_err("CAIF: %s(): Packet is erroneous!\n", __func__);
107		cfpkt_destroy(pkt);
108		return -EPROTO;
109	}
110	return layr->up->receive(layr->up, pkt);
111}
112
113static int cffrml_transmit(struct cflayer *layr, struct cfpkt *pkt)
114{
115	int tmp;
116	u16 chks;
117	u16 len;
118	int ret;
119	struct cffrml *this = container_obj(layr);
120	if (this->dofcs) {
121		chks = cfpkt_iterate(pkt, cffrml_checksum, 0xffff);
122		tmp = cpu_to_le16(chks);
123		cfpkt_add_trail(pkt, &tmp, 2);
124	} else {
125		cfpkt_pad_trail(pkt, 2);
126	}
127	len = cfpkt_getlen(pkt);
128	tmp = cpu_to_le16(len);
129	cfpkt_add_head(pkt, &tmp, 2);
130	cfpkt_info(pkt)->hdr_len += 2;
131	if (cfpkt_erroneous(pkt)) {
132		pr_err("CAIF: %s(): Packet is erroneous!\n", __func__);
133		return -EPROTO;
134	}
135	ret = layr->dn->transmit(layr->dn, pkt);
136	if (ret < 0) {
137		/* Remove header on faulty packet. */
138		cfpkt_extr_head(pkt, &tmp, 2);
139	}
140	return ret;
141}
142
143static void cffrml_ctrlcmd(struct cflayer *layr, enum caif_ctrlcmd ctrl,
144					int phyid)
145{
146	if (layr->up->ctrlcmd)
147		layr->up->ctrlcmd(layr->up, ctrl, layr->id);
148}
149