2 * Copyright (C) ST-Ericsson AB 2010
3 * Author: Sjur Brendeland/sjur.brandeland@stericsson.com
4 * License terms: GNU General Public License (GPL) version 2
7 #define pr_fmt(fmt) KBUILD_MODNAME ":%s(): " fmt, __func__
9 #include <linux/stddef.h>
10 #include <linux/spinlock.h>
11 #include <linux/slab.h>
12 #include <net/caif/cfpkt.h>
13 #include <net/caif/cfmuxl.h>
14 #include <net/caif/cfsrvl.h>
15 #include <net/caif/cffrml.h>
17 #define container_obj(layr) container_of(layr, struct cfmuxl, layer)
19 #define CAIF_CTRL_CHANNEL 0
20 #define UP_CACHE_SIZE 8
21 #define DN_CACHE_SIZE 8
25 struct list_head srvl_list
;
26 struct list_head frml_list
;
27 struct cflayer
*up_cache
[UP_CACHE_SIZE
];
28 struct cflayer
*dn_cache
[DN_CACHE_SIZE
];
30 * Set when inserting or removing downwards layers.
32 spinlock_t transmit_lock
;
35 * Set when inserting or removing upwards layers.
37 spinlock_t receive_lock
;
41 static int cfmuxl_receive(struct cflayer
*layr
, struct cfpkt
*pkt
);
42 static int cfmuxl_transmit(struct cflayer
*layr
, struct cfpkt
*pkt
);
43 static void cfmuxl_ctrlcmd(struct cflayer
*layr
, enum caif_ctrlcmd ctrl
,
45 static struct cflayer
*get_up(struct cfmuxl
*muxl
, u16 id
);
47 struct cflayer
*cfmuxl_create(void)
49 struct cfmuxl
*this = kmalloc(sizeof(struct cfmuxl
), GFP_ATOMIC
);
52 memset(this, 0, sizeof(*this));
53 this->layer
.receive
= cfmuxl_receive
;
54 this->layer
.transmit
= cfmuxl_transmit
;
55 this->layer
.ctrlcmd
= cfmuxl_ctrlcmd
;
56 INIT_LIST_HEAD(&this->srvl_list
);
57 INIT_LIST_HEAD(&this->frml_list
);
58 spin_lock_init(&this->transmit_lock
);
59 spin_lock_init(&this->receive_lock
);
60 snprintf(this->layer
.name
, CAIF_LAYER_NAME_SZ
, "mux");
64 int cfmuxl_set_uplayer(struct cflayer
*layr
, struct cflayer
*up
, u8 linkid
)
66 struct cfmuxl
*muxl
= container_obj(layr
);
67 spin_lock(&muxl
->receive_lock
);
69 list_add(&up
->node
, &muxl
->srvl_list
);
70 spin_unlock(&muxl
->receive_lock
);
74 bool cfmuxl_is_phy_inuse(struct cflayer
*layr
, u8 phyid
)
76 struct list_head
*node
;
77 struct cflayer
*layer
;
78 struct cfmuxl
*muxl
= container_obj(layr
);
80 spin_lock(&muxl
->receive_lock
);
82 list_for_each(node
, &muxl
->srvl_list
) {
83 layer
= list_entry(node
, struct cflayer
, node
);
84 if (cfsrvl_phyid_match(layer
, phyid
)) {
90 spin_unlock(&muxl
->receive_lock
);
94 u8
cfmuxl_get_phyid(struct cflayer
*layr
, u8 channel_id
)
98 struct cfmuxl
*muxl
= container_obj(layr
);
99 spin_lock(&muxl
->receive_lock
);
100 up
= get_up(muxl
, channel_id
);
102 phyid
= cfsrvl_getphyid(up
);
105 spin_unlock(&muxl
->receive_lock
);
109 int cfmuxl_set_dnlayer(struct cflayer
*layr
, struct cflayer
*dn
, u8 phyid
)
111 struct cfmuxl
*muxl
= (struct cfmuxl
*) layr
;
112 spin_lock(&muxl
->transmit_lock
);
113 list_add(&dn
->node
, &muxl
->frml_list
);
114 spin_unlock(&muxl
->transmit_lock
);
118 static struct cflayer
*get_from_id(struct list_head
*list
, u16 id
)
120 struct list_head
*node
;
121 struct cflayer
*layer
;
122 list_for_each(node
, list
) {
123 layer
= list_entry(node
, struct cflayer
, node
);
130 struct cflayer
*cfmuxl_remove_dnlayer(struct cflayer
*layr
, u8 phyid
)
132 struct cfmuxl
*muxl
= container_obj(layr
);
134 spin_lock(&muxl
->transmit_lock
);
135 memset(muxl
->dn_cache
, 0, sizeof(muxl
->dn_cache
));
136 dn
= get_from_id(&muxl
->frml_list
, phyid
);
138 spin_unlock(&muxl
->transmit_lock
);
142 caif_assert(dn
!= NULL
);
143 spin_unlock(&muxl
->transmit_lock
);
147 /* Invariant: lock is taken */
148 static struct cflayer
*get_up(struct cfmuxl
*muxl
, u16 id
)
151 int idx
= id
% UP_CACHE_SIZE
;
152 up
= muxl
->up_cache
[idx
];
153 if (up
== NULL
|| up
->id
!= id
) {
154 up
= get_from_id(&muxl
->srvl_list
, id
);
155 muxl
->up_cache
[idx
] = up
;
160 /* Invariant: lock is taken */
161 static struct cflayer
*get_dn(struct cfmuxl
*muxl
, struct dev_info
*dev_info
)
164 int idx
= dev_info
->id
% DN_CACHE_SIZE
;
165 dn
= muxl
->dn_cache
[idx
];
166 if (dn
== NULL
|| dn
->id
!= dev_info
->id
) {
167 dn
= get_from_id(&muxl
->frml_list
, dev_info
->id
);
168 muxl
->dn_cache
[idx
] = dn
;
173 struct cflayer
*cfmuxl_remove_uplayer(struct cflayer
*layr
, u8 id
)
176 struct cfmuxl
*muxl
= container_obj(layr
);
177 spin_lock(&muxl
->receive_lock
);
178 up
= get_up(muxl
, id
);
181 memset(muxl
->up_cache
, 0, sizeof(muxl
->up_cache
));
185 spin_unlock(&muxl
->receive_lock
);
189 static int cfmuxl_receive(struct cflayer
*layr
, struct cfpkt
*pkt
)
192 struct cfmuxl
*muxl
= container_obj(layr
);
195 if (cfpkt_extr_head(pkt
, &id
, 1) < 0) {
196 pr_err("erroneous Caif Packet\n");
201 spin_lock(&muxl
->receive_lock
);
202 up
= get_up(muxl
, id
);
203 spin_unlock(&muxl
->receive_lock
);
205 pr_info("Received data on unknown link ID = %d (0x%x) up == NULL",
209 * Don't return ERROR, since modem misbehaves and sends out
210 * flow on before linksetup response.
212 return /* CFGLU_EPROT; */ 0;
215 ret
= up
->receive(up
, pkt
);
220 static int cfmuxl_transmit(struct cflayer
*layr
, struct cfpkt
*pkt
)
223 struct cfmuxl
*muxl
= container_obj(layr
);
226 struct caif_payload_info
*info
= cfpkt_info(pkt
);
227 dn
= get_dn(muxl
, cfpkt_info(pkt
)->dev_info
);
229 pr_warn("Send data on unknown phy ID = %d (0x%x)\n",
230 info
->dev_info
->id
, info
->dev_info
->id
);
234 linkid
= info
->channel_id
;
235 cfpkt_add_head(pkt
, &linkid
, 1);
236 ret
= dn
->transmit(dn
, pkt
);
237 /* Remove MUX protocol header upon error. */
239 cfpkt_extr_head(pkt
, &linkid
, 1);
243 static void cfmuxl_ctrlcmd(struct cflayer
*layr
, enum caif_ctrlcmd ctrl
,
246 struct cfmuxl
*muxl
= container_obj(layr
);
247 struct list_head
*node
;
248 struct cflayer
*layer
;
249 list_for_each(node
, &muxl
->srvl_list
) {
250 layer
= list_entry(node
, struct cflayer
, node
);
251 if (cfsrvl_phyid_match(layer
, phyid
))
252 layer
->ctrlcmd(layer
, ctrl
, phyid
);