Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/net/caif/cfserl.c
26278 views
1
// SPDX-License-Identifier: GPL-2.0-only
2
/*
3
* Copyright (C) ST-Ericsson AB 2010
4
* Author: Sjur Brendeland
5
*/
6
7
#define pr_fmt(fmt) KBUILD_MODNAME ":%s(): " fmt, __func__
8
9
#include <linux/stddef.h>
10
#include <linux/spinlock.h>
11
#include <linux/slab.h>
12
#include <net/caif/caif_layer.h>
13
#include <net/caif/cfpkt.h>
14
#include <net/caif/cfserl.h>
15
16
#define container_obj(layr) ((struct cfserl *) layr)
17
18
#define CFSERL_STX 0x02
19
#define SERIAL_MINIUM_PACKET_SIZE 4
20
#define SERIAL_MAX_FRAMESIZE 4096
21
struct cfserl {
22
struct cflayer layer;
23
struct cfpkt *incomplete_frm;
24
/* Protects parallel processing of incoming packets */
25
spinlock_t sync;
26
bool usestx;
27
};
28
29
static int cfserl_receive(struct cflayer *layr, struct cfpkt *pkt);
30
static int cfserl_transmit(struct cflayer *layr, struct cfpkt *pkt);
31
static void cfserl_ctrlcmd(struct cflayer *layr, enum caif_ctrlcmd ctrl,
32
int phyid);
33
34
void cfserl_release(struct cflayer *layer)
35
{
36
kfree(layer);
37
}
38
39
struct cflayer *cfserl_create(int instance, bool use_stx)
40
{
41
struct cfserl *this = kzalloc(sizeof(struct cfserl), GFP_ATOMIC);
42
if (!this)
43
return NULL;
44
caif_assert(offsetof(struct cfserl, layer) == 0);
45
this->layer.receive = cfserl_receive;
46
this->layer.transmit = cfserl_transmit;
47
this->layer.ctrlcmd = cfserl_ctrlcmd;
48
this->usestx = use_stx;
49
spin_lock_init(&this->sync);
50
snprintf(this->layer.name, CAIF_LAYER_NAME_SZ, "ser1");
51
return &this->layer;
52
}
53
54
static int cfserl_receive(struct cflayer *l, struct cfpkt *newpkt)
55
{
56
struct cfserl *layr = container_obj(l);
57
u16 pkt_len;
58
struct cfpkt *pkt = NULL;
59
struct cfpkt *tail_pkt = NULL;
60
u8 tmp8;
61
u16 tmp;
62
u8 stx = CFSERL_STX;
63
int ret;
64
u16 expectlen = 0;
65
66
caif_assert(newpkt != NULL);
67
spin_lock(&layr->sync);
68
69
if (layr->incomplete_frm != NULL) {
70
layr->incomplete_frm =
71
cfpkt_append(layr->incomplete_frm, newpkt, expectlen);
72
pkt = layr->incomplete_frm;
73
if (pkt == NULL) {
74
spin_unlock(&layr->sync);
75
return -ENOMEM;
76
}
77
} else {
78
pkt = newpkt;
79
}
80
layr->incomplete_frm = NULL;
81
82
do {
83
/* Search for STX at start of pkt if STX is used */
84
if (layr->usestx) {
85
cfpkt_extr_head(pkt, &tmp8, 1);
86
if (tmp8 != CFSERL_STX) {
87
while (cfpkt_more(pkt)
88
&& tmp8 != CFSERL_STX) {
89
cfpkt_extr_head(pkt, &tmp8, 1);
90
}
91
if (!cfpkt_more(pkt)) {
92
cfpkt_destroy(pkt);
93
layr->incomplete_frm = NULL;
94
spin_unlock(&layr->sync);
95
return -EPROTO;
96
}
97
}
98
}
99
100
pkt_len = cfpkt_getlen(pkt);
101
102
/*
103
* pkt_len is the accumulated length of the packet data
104
* we have received so far.
105
* Exit if frame doesn't hold length.
106
*/
107
108
if (pkt_len < 2) {
109
if (layr->usestx)
110
cfpkt_add_head(pkt, &stx, 1);
111
layr->incomplete_frm = pkt;
112
spin_unlock(&layr->sync);
113
return 0;
114
}
115
116
/*
117
* Find length of frame.
118
* expectlen is the length we need for a full frame.
119
*/
120
cfpkt_peek_head(pkt, &tmp, 2);
121
expectlen = le16_to_cpu(tmp) + 2;
122
/*
123
* Frame error handling
124
*/
125
if (expectlen < SERIAL_MINIUM_PACKET_SIZE
126
|| expectlen > SERIAL_MAX_FRAMESIZE) {
127
if (!layr->usestx) {
128
if (pkt != NULL)
129
cfpkt_destroy(pkt);
130
layr->incomplete_frm = NULL;
131
spin_unlock(&layr->sync);
132
return -EPROTO;
133
}
134
continue;
135
}
136
137
if (pkt_len < expectlen) {
138
/* Too little received data */
139
if (layr->usestx)
140
cfpkt_add_head(pkt, &stx, 1);
141
layr->incomplete_frm = pkt;
142
spin_unlock(&layr->sync);
143
return 0;
144
}
145
146
/*
147
* Enough data for at least one frame.
148
* Split the frame, if too long
149
*/
150
if (pkt_len > expectlen)
151
tail_pkt = cfpkt_split(pkt, expectlen);
152
else
153
tail_pkt = NULL;
154
155
/* Send the first part of packet upwards.*/
156
spin_unlock(&layr->sync);
157
ret = layr->layer.up->receive(layr->layer.up, pkt);
158
spin_lock(&layr->sync);
159
if (ret == -EILSEQ) {
160
if (layr->usestx) {
161
if (tail_pkt != NULL)
162
pkt = cfpkt_append(pkt, tail_pkt, 0);
163
/* Start search for next STX if frame failed */
164
continue;
165
} else {
166
cfpkt_destroy(pkt);
167
pkt = NULL;
168
}
169
}
170
171
pkt = tail_pkt;
172
173
} while (pkt != NULL);
174
175
spin_unlock(&layr->sync);
176
return 0;
177
}
178
179
static int cfserl_transmit(struct cflayer *layer, struct cfpkt *newpkt)
180
{
181
struct cfserl *layr = container_obj(layer);
182
u8 tmp8 = CFSERL_STX;
183
if (layr->usestx)
184
cfpkt_add_head(newpkt, &tmp8, 1);
185
return layer->dn->transmit(layer->dn, newpkt);
186
}
187
188
static void cfserl_ctrlcmd(struct cflayer *layr, enum caif_ctrlcmd ctrl,
189
int phyid)
190
{
191
layr->up->ctrlcmd(layr->up, ctrl, phyid);
192
}
193
194