diff options
Diffstat (limited to 'net/caif/cfcnfg.c')
-rw-r--r-- | net/caif/cfcnfg.c | 507 |
1 files changed, 326 insertions, 181 deletions
diff --git a/net/caif/cfcnfg.c b/net/caif/cfcnfg.c index f1f98d967d8a..351c2ca7e7b9 100644 --- a/net/caif/cfcnfg.c +++ b/net/caif/cfcnfg.c | |||
@@ -10,6 +10,7 @@ | |||
10 | #include <linux/stddef.h> | 10 | #include <linux/stddef.h> |
11 | #include <linux/slab.h> | 11 | #include <linux/slab.h> |
12 | #include <linux/netdevice.h> | 12 | #include <linux/netdevice.h> |
13 | #include <linux/module.h> | ||
13 | #include <net/caif/caif_layer.h> | 14 | #include <net/caif/caif_layer.h> |
14 | #include <net/caif/cfpkt.h> | 15 | #include <net/caif/cfpkt.h> |
15 | #include <net/caif/cfcnfg.h> | 16 | #include <net/caif/cfcnfg.h> |
@@ -18,11 +19,7 @@ | |||
18 | #include <net/caif/cffrml.h> | 19 | #include <net/caif/cffrml.h> |
19 | #include <net/caif/cfserl.h> | 20 | #include <net/caif/cfserl.h> |
20 | #include <net/caif/cfsrvl.h> | 21 | #include <net/caif/cfsrvl.h> |
21 | 22 | #include <net/caif/caif_dev.h> | |
22 | #include <linux/module.h> | ||
23 | #include <asm/atomic.h> | ||
24 | |||
25 | #define MAX_PHY_LAYERS 7 | ||
26 | 23 | ||
27 | #define container_obj(layr) container_of(layr, struct cfcnfg, layer) | 24 | #define container_obj(layr) container_of(layr, struct cfcnfg, layer) |
28 | 25 | ||
@@ -30,6 +27,9 @@ | |||
30 | * to manage physical interfaces | 27 | * to manage physical interfaces |
31 | */ | 28 | */ |
32 | struct cfcnfg_phyinfo { | 29 | struct cfcnfg_phyinfo { |
30 | struct list_head node; | ||
31 | bool up; | ||
32 | |||
33 | /* Pointer to the layer below the MUX (framing layer) */ | 33 | /* Pointer to the layer below the MUX (framing layer) */ |
34 | struct cflayer *frm_layer; | 34 | struct cflayer *frm_layer; |
35 | /* Pointer to the lowest actual physical layer */ | 35 | /* Pointer to the lowest actual physical layer */ |
@@ -39,9 +39,6 @@ struct cfcnfg_phyinfo { | |||
39 | /* Preference of the physical in interface */ | 39 | /* Preference of the physical in interface */ |
40 | enum cfcnfg_phy_preference pref; | 40 | enum cfcnfg_phy_preference pref; |
41 | 41 | ||
42 | /* Reference count, number of channels using the device */ | ||
43 | int phy_ref_count; | ||
44 | |||
45 | /* Information about the physical device */ | 42 | /* Information about the physical device */ |
46 | struct dev_info dev_info; | 43 | struct dev_info dev_info; |
47 | 44 | ||
@@ -59,8 +56,8 @@ struct cfcnfg { | |||
59 | struct cflayer layer; | 56 | struct cflayer layer; |
60 | struct cflayer *ctrl; | 57 | struct cflayer *ctrl; |
61 | struct cflayer *mux; | 58 | struct cflayer *mux; |
62 | u8 last_phyid; | 59 | struct list_head phys; |
63 | struct cfcnfg_phyinfo phy_layers[MAX_PHY_LAYERS]; | 60 | struct mutex lock; |
64 | }; | 61 | }; |
65 | 62 | ||
66 | static void cfcnfg_linkup_rsp(struct cflayer *layer, u8 channel_id, | 63 | static void cfcnfg_linkup_rsp(struct cflayer *layer, u8 channel_id, |
@@ -76,6 +73,9 @@ struct cfcnfg *cfcnfg_create(void) | |||
76 | { | 73 | { |
77 | struct cfcnfg *this; | 74 | struct cfcnfg *this; |
78 | struct cfctrl_rsp *resp; | 75 | struct cfctrl_rsp *resp; |
76 | |||
77 | might_sleep(); | ||
78 | |||
79 | /* Initiate this layer */ | 79 | /* Initiate this layer */ |
80 | this = kzalloc(sizeof(struct cfcnfg), GFP_ATOMIC); | 80 | this = kzalloc(sizeof(struct cfcnfg), GFP_ATOMIC); |
81 | if (!this) { | 81 | if (!this) { |
@@ -99,27 +99,33 @@ struct cfcnfg *cfcnfg_create(void) | |||
99 | resp->radioset_rsp = cfctrl_resp_func; | 99 | resp->radioset_rsp = cfctrl_resp_func; |
100 | resp->linksetup_rsp = cfcnfg_linkup_rsp; | 100 | resp->linksetup_rsp = cfcnfg_linkup_rsp; |
101 | resp->reject_rsp = cfcnfg_reject_rsp; | 101 | resp->reject_rsp = cfcnfg_reject_rsp; |
102 | 102 | INIT_LIST_HEAD(&this->phys); | |
103 | this->last_phyid = 1; | ||
104 | 103 | ||
105 | cfmuxl_set_uplayer(this->mux, this->ctrl, 0); | 104 | cfmuxl_set_uplayer(this->mux, this->ctrl, 0); |
106 | layer_set_dn(this->ctrl, this->mux); | 105 | layer_set_dn(this->ctrl, this->mux); |
107 | layer_set_up(this->ctrl, this); | 106 | layer_set_up(this->ctrl, this); |
107 | mutex_init(&this->lock); | ||
108 | |||
108 | return this; | 109 | return this; |
109 | out_of_mem: | 110 | out_of_mem: |
110 | pr_warn("Out of memory\n"); | 111 | pr_warn("Out of memory\n"); |
112 | |||
113 | synchronize_rcu(); | ||
114 | |||
111 | kfree(this->mux); | 115 | kfree(this->mux); |
112 | kfree(this->ctrl); | 116 | kfree(this->ctrl); |
113 | kfree(this); | 117 | kfree(this); |
114 | return NULL; | 118 | return NULL; |
115 | } | 119 | } |
116 | EXPORT_SYMBOL(cfcnfg_create); | ||
117 | 120 | ||
118 | void cfcnfg_remove(struct cfcnfg *cfg) | 121 | void cfcnfg_remove(struct cfcnfg *cfg) |
119 | { | 122 | { |
123 | might_sleep(); | ||
120 | if (cfg) { | 124 | if (cfg) { |
125 | synchronize_rcu(); | ||
126 | |||
121 | kfree(cfg->mux); | 127 | kfree(cfg->mux); |
122 | kfree(cfg->ctrl); | 128 | cfctrl_remove(cfg->ctrl); |
123 | kfree(cfg); | 129 | kfree(cfg); |
124 | } | 130 | } |
125 | } | 131 | } |
@@ -128,132 +134,96 @@ static void cfctrl_resp_func(void) | |||
128 | { | 134 | { |
129 | } | 135 | } |
130 | 136 | ||
137 | static struct cfcnfg_phyinfo *cfcnfg_get_phyinfo_rcu(struct cfcnfg *cnfg, | ||
138 | u8 phyid) | ||
139 | { | ||
140 | struct cfcnfg_phyinfo *phy; | ||
141 | |||
142 | list_for_each_entry_rcu(phy, &cnfg->phys, node) | ||
143 | if (phy->id == phyid) | ||
144 | return phy; | ||
145 | return NULL; | ||
146 | } | ||
147 | |||
131 | static void cfctrl_enum_resp(void) | 148 | static void cfctrl_enum_resp(void) |
132 | { | 149 | { |
133 | } | 150 | } |
134 | 151 | ||
135 | struct dev_info *cfcnfg_get_phyid(struct cfcnfg *cnfg, | 152 | static struct dev_info *cfcnfg_get_phyid(struct cfcnfg *cnfg, |
136 | enum cfcnfg_phy_preference phy_pref) | 153 | enum cfcnfg_phy_preference phy_pref) |
137 | { | 154 | { |
138 | u16 i; | ||
139 | |||
140 | /* Try to match with specified preference */ | 155 | /* Try to match with specified preference */ |
141 | for (i = 1; i < MAX_PHY_LAYERS; i++) { | 156 | struct cfcnfg_phyinfo *phy; |
142 | if (cnfg->phy_layers[i].id == i && | 157 | |
143 | cnfg->phy_layers[i].pref == phy_pref && | 158 | list_for_each_entry_rcu(phy, &cnfg->phys, node) { |
144 | cnfg->phy_layers[i].frm_layer != NULL) { | 159 | if (phy->up && phy->pref == phy_pref && |
145 | caif_assert(cnfg->phy_layers != NULL); | 160 | phy->frm_layer != NULL) |
146 | caif_assert(cnfg->phy_layers[i].id == i); | 161 | |
147 | return &cnfg->phy_layers[i].dev_info; | 162 | return &phy->dev_info; |
148 | } | ||
149 | } | 163 | } |
164 | |||
150 | /* Otherwise just return something */ | 165 | /* Otherwise just return something */ |
151 | for (i = 1; i < MAX_PHY_LAYERS; i++) { | 166 | list_for_each_entry_rcu(phy, &cnfg->phys, node) |
152 | if (cnfg->phy_layers[i].id == i) { | 167 | if (phy->up) |
153 | caif_assert(cnfg->phy_layers != NULL); | 168 | return &phy->dev_info; |
154 | caif_assert(cnfg->phy_layers[i].id == i); | ||
155 | return &cnfg->phy_layers[i].dev_info; | ||
156 | } | ||
157 | } | ||
158 | 169 | ||
159 | return NULL; | 170 | return NULL; |
160 | } | 171 | } |
161 | 172 | ||
162 | static struct cfcnfg_phyinfo *cfcnfg_get_phyinfo(struct cfcnfg *cnfg, | 173 | static int cfcnfg_get_id_from_ifi(struct cfcnfg *cnfg, int ifi) |
163 | u8 phyid) | ||
164 | { | 174 | { |
165 | int i; | 175 | struct cfcnfg_phyinfo *phy; |
166 | /* Try to match with specified preference */ | ||
167 | for (i = 0; i < MAX_PHY_LAYERS; i++) | ||
168 | if (cnfg->phy_layers[i].frm_layer != NULL && | ||
169 | cnfg->phy_layers[i].id == phyid) | ||
170 | return &cnfg->phy_layers[i]; | ||
171 | return NULL; | ||
172 | } | ||
173 | |||
174 | 176 | ||
175 | int cfcnfg_get_id_from_ifi(struct cfcnfg *cnfg, int ifi) | 177 | list_for_each_entry_rcu(phy, &cnfg->phys, node) |
176 | { | 178 | if (phy->ifindex == ifi && phy->up) |
177 | int i; | 179 | return phy->id; |
178 | for (i = 0; i < MAX_PHY_LAYERS; i++) | ||
179 | if (cnfg->phy_layers[i].frm_layer != NULL && | ||
180 | cnfg->phy_layers[i].ifindex == ifi) | ||
181 | return i; | ||
182 | return -ENODEV; | 180 | return -ENODEV; |
183 | } | 181 | } |
184 | 182 | ||
185 | int cfcnfg_disconn_adapt_layer(struct cfcnfg *cnfg, struct cflayer *adap_layer) | 183 | int caif_disconnect_client(struct net *net, struct cflayer *adap_layer) |
186 | { | 184 | { |
187 | u8 channel_id = 0; | 185 | u8 channel_id = 0; |
188 | int ret = 0; | 186 | int ret = 0; |
189 | struct cflayer *servl = NULL; | 187 | struct cflayer *servl = NULL; |
190 | struct cfcnfg_phyinfo *phyinfo = NULL; | 188 | struct cfcnfg *cfg = get_cfcnfg(net); |
191 | u8 phyid = 0; | ||
192 | 189 | ||
193 | caif_assert(adap_layer != NULL); | 190 | caif_assert(adap_layer != NULL); |
191 | |||
194 | channel_id = adap_layer->id; | 192 | channel_id = adap_layer->id; |
195 | if (adap_layer->dn == NULL || channel_id == 0) { | 193 | if (adap_layer->dn == NULL || channel_id == 0) { |
196 | pr_err("adap_layer->dn == NULL or adap_layer->id is 0\n"); | 194 | pr_err("adap_layer->dn == NULL or adap_layer->id is 0\n"); |
197 | ret = -ENOTCONN; | 195 | ret = -ENOTCONN; |
198 | goto end; | 196 | goto end; |
199 | } | 197 | } |
200 | servl = cfmuxl_remove_uplayer(cnfg->mux, channel_id); | 198 | |
199 | servl = cfmuxl_remove_uplayer(cfg->mux, channel_id); | ||
201 | if (servl == NULL) { | 200 | if (servl == NULL) { |
202 | pr_err("PROTOCOL ERROR - Error removing service_layer Channel_Id(%d)", | 201 | pr_err("PROTOCOL ERROR - " |
203 | channel_id); | 202 | "Error removing service_layer Channel_Id(%d)", |
203 | channel_id); | ||
204 | ret = -EINVAL; | 204 | ret = -EINVAL; |
205 | goto end; | 205 | goto end; |
206 | } | 206 | } |
207 | layer_set_up(servl, NULL); | 207 | |
208 | ret = cfctrl_linkdown_req(cnfg->ctrl, channel_id, adap_layer); | 208 | ret = cfctrl_linkdown_req(cfg->ctrl, channel_id, adap_layer); |
209 | if (ret) | 209 | |
210 | goto end; | ||
211 | caif_assert(channel_id == servl->id); | ||
212 | if (adap_layer->dn != NULL) { | ||
213 | phyid = cfsrvl_getphyid(adap_layer->dn); | ||
214 | |||
215 | phyinfo = cfcnfg_get_phyinfo(cnfg, phyid); | ||
216 | if (phyinfo == NULL) { | ||
217 | pr_warn("No interface to send disconnect to\n"); | ||
218 | ret = -ENODEV; | ||
219 | goto end; | ||
220 | } | ||
221 | if (phyinfo->id != phyid || | ||
222 | phyinfo->phy_layer->id != phyid || | ||
223 | phyinfo->frm_layer->id != phyid) { | ||
224 | pr_err("Inconsistency in phy registration\n"); | ||
225 | ret = -EINVAL; | ||
226 | goto end; | ||
227 | } | ||
228 | } | ||
229 | if (phyinfo != NULL && --phyinfo->phy_ref_count == 0 && | ||
230 | phyinfo->phy_layer != NULL && | ||
231 | phyinfo->phy_layer->modemcmd != NULL) { | ||
232 | phyinfo->phy_layer->modemcmd(phyinfo->phy_layer, | ||
233 | _CAIF_MODEMCMD_PHYIF_USELESS); | ||
234 | } | ||
235 | end: | 210 | end: |
236 | cfsrvl_put(servl); | 211 | cfctrl_cancel_req(cfg->ctrl, adap_layer); |
237 | cfctrl_cancel_req(cnfg->ctrl, adap_layer); | 212 | |
213 | /* Do RCU sync before initiating cleanup */ | ||
214 | synchronize_rcu(); | ||
238 | if (adap_layer->ctrlcmd != NULL) | 215 | if (adap_layer->ctrlcmd != NULL) |
239 | adap_layer->ctrlcmd(adap_layer, CAIF_CTRLCMD_DEINIT_RSP, 0); | 216 | adap_layer->ctrlcmd(adap_layer, CAIF_CTRLCMD_DEINIT_RSP, 0); |
240 | return ret; | 217 | return ret; |
241 | 218 | ||
242 | } | 219 | } |
243 | EXPORT_SYMBOL(cfcnfg_disconn_adapt_layer); | 220 | EXPORT_SYMBOL(caif_disconnect_client); |
244 | |||
245 | void cfcnfg_release_adap_layer(struct cflayer *adap_layer) | ||
246 | { | ||
247 | if (adap_layer->dn) | ||
248 | cfsrvl_put(adap_layer->dn); | ||
249 | } | ||
250 | EXPORT_SYMBOL(cfcnfg_release_adap_layer); | ||
251 | 221 | ||
252 | static void cfcnfg_linkdestroy_rsp(struct cflayer *layer, u8 channel_id) | 222 | static void cfcnfg_linkdestroy_rsp(struct cflayer *layer, u8 channel_id) |
253 | { | 223 | { |
254 | } | 224 | } |
255 | 225 | ||
256 | int protohead[CFCTRL_SRV_MASK] = { | 226 | static const int protohead[CFCTRL_SRV_MASK] = { |
257 | [CFCTRL_SRV_VEI] = 4, | 227 | [CFCTRL_SRV_VEI] = 4, |
258 | [CFCTRL_SRV_DATAGRAM] = 7, | 228 | [CFCTRL_SRV_DATAGRAM] = 7, |
259 | [CFCTRL_SRV_UTIL] = 4, | 229 | [CFCTRL_SRV_UTIL] = 4, |
@@ -261,49 +231,157 @@ int protohead[CFCTRL_SRV_MASK] = { | |||
261 | [CFCTRL_SRV_DBG] = 3, | 231 | [CFCTRL_SRV_DBG] = 3, |
262 | }; | 232 | }; |
263 | 233 | ||
264 | int cfcnfg_add_adaptation_layer(struct cfcnfg *cnfg, | 234 | |
265 | struct cfctrl_link_param *param, | 235 | static int caif_connect_req_to_link_param(struct cfcnfg *cnfg, |
266 | struct cflayer *adap_layer, | 236 | struct caif_connect_request *s, |
267 | int *ifindex, | 237 | struct cfctrl_link_param *l) |
238 | { | ||
239 | struct dev_info *dev_info; | ||
240 | enum cfcnfg_phy_preference pref; | ||
241 | int res; | ||
242 | |||
243 | memset(l, 0, sizeof(*l)); | ||
244 | /* In caif protocol low value is high priority */ | ||
245 | l->priority = CAIF_PRIO_MAX - s->priority + 1; | ||
246 | |||
247 | if (s->ifindex != 0) { | ||
248 | res = cfcnfg_get_id_from_ifi(cnfg, s->ifindex); | ||
249 | if (res < 0) | ||
250 | return res; | ||
251 | l->phyid = res; | ||
252 | } else { | ||
253 | switch (s->link_selector) { | ||
254 | case CAIF_LINK_HIGH_BANDW: | ||
255 | pref = CFPHYPREF_HIGH_BW; | ||
256 | break; | ||
257 | case CAIF_LINK_LOW_LATENCY: | ||
258 | pref = CFPHYPREF_LOW_LAT; | ||
259 | break; | ||
260 | default: | ||
261 | return -EINVAL; | ||
262 | } | ||
263 | dev_info = cfcnfg_get_phyid(cnfg, pref); | ||
264 | if (dev_info == NULL) | ||
265 | return -ENODEV; | ||
266 | l->phyid = dev_info->id; | ||
267 | } | ||
268 | switch (s->protocol) { | ||
269 | case CAIFPROTO_AT: | ||
270 | l->linktype = CFCTRL_SRV_VEI; | ||
271 | l->endpoint = (s->sockaddr.u.at.type >> 2) & 0x3; | ||
272 | l->chtype = s->sockaddr.u.at.type & 0x3; | ||
273 | break; | ||
274 | case CAIFPROTO_DATAGRAM: | ||
275 | l->linktype = CFCTRL_SRV_DATAGRAM; | ||
276 | l->chtype = 0x00; | ||
277 | l->u.datagram.connid = s->sockaddr.u.dgm.connection_id; | ||
278 | break; | ||
279 | case CAIFPROTO_DATAGRAM_LOOP: | ||
280 | l->linktype = CFCTRL_SRV_DATAGRAM; | ||
281 | l->chtype = 0x03; | ||
282 | l->endpoint = 0x00; | ||
283 | l->u.datagram.connid = s->sockaddr.u.dgm.connection_id; | ||
284 | break; | ||
285 | case CAIFPROTO_RFM: | ||
286 | l->linktype = CFCTRL_SRV_RFM; | ||
287 | l->u.datagram.connid = s->sockaddr.u.rfm.connection_id; | ||
288 | strncpy(l->u.rfm.volume, s->sockaddr.u.rfm.volume, | ||
289 | sizeof(l->u.rfm.volume)-1); | ||
290 | l->u.rfm.volume[sizeof(l->u.rfm.volume)-1] = 0; | ||
291 | break; | ||
292 | case CAIFPROTO_UTIL: | ||
293 | l->linktype = CFCTRL_SRV_UTIL; | ||
294 | l->endpoint = 0x00; | ||
295 | l->chtype = 0x00; | ||
296 | strncpy(l->u.utility.name, s->sockaddr.u.util.service, | ||
297 | sizeof(l->u.utility.name)-1); | ||
298 | l->u.utility.name[sizeof(l->u.utility.name)-1] = 0; | ||
299 | caif_assert(sizeof(l->u.utility.name) > 10); | ||
300 | l->u.utility.paramlen = s->param.size; | ||
301 | if (l->u.utility.paramlen > sizeof(l->u.utility.params)) | ||
302 | l->u.utility.paramlen = sizeof(l->u.utility.params); | ||
303 | |||
304 | memcpy(l->u.utility.params, s->param.data, | ||
305 | l->u.utility.paramlen); | ||
306 | |||
307 | break; | ||
308 | case CAIFPROTO_DEBUG: | ||
309 | l->linktype = CFCTRL_SRV_DBG; | ||
310 | l->endpoint = s->sockaddr.u.dbg.service; | ||
311 | l->chtype = s->sockaddr.u.dbg.type; | ||
312 | break; | ||
313 | default: | ||
314 | return -EINVAL; | ||
315 | } | ||
316 | return 0; | ||
317 | } | ||
318 | |||
319 | int caif_connect_client(struct net *net, struct caif_connect_request *conn_req, | ||
320 | struct cflayer *adap_layer, int *ifindex, | ||
268 | int *proto_head, | 321 | int *proto_head, |
269 | int *proto_tail) | 322 | int *proto_tail) |
270 | { | 323 | { |
271 | struct cflayer *frml; | 324 | struct cflayer *frml; |
325 | struct cfcnfg_phyinfo *phy; | ||
326 | int err; | ||
327 | struct cfctrl_link_param param; | ||
328 | struct cfcnfg *cfg = get_cfcnfg(net); | ||
329 | caif_assert(cfg != NULL); | ||
330 | |||
331 | rcu_read_lock(); | ||
332 | err = caif_connect_req_to_link_param(cfg, conn_req, ¶m); | ||
333 | if (err) | ||
334 | goto unlock; | ||
335 | |||
336 | phy = cfcnfg_get_phyinfo_rcu(cfg, param.phyid); | ||
337 | if (!phy) { | ||
338 | err = -ENODEV; | ||
339 | goto unlock; | ||
340 | } | ||
341 | err = -EINVAL; | ||
342 | |||
272 | if (adap_layer == NULL) { | 343 | if (adap_layer == NULL) { |
273 | pr_err("adap_layer is zero\n"); | 344 | pr_err("adap_layer is zero\n"); |
274 | return -EINVAL; | 345 | goto unlock; |
275 | } | 346 | } |
276 | if (adap_layer->receive == NULL) { | 347 | if (adap_layer->receive == NULL) { |
277 | pr_err("adap_layer->receive is NULL\n"); | 348 | pr_err("adap_layer->receive is NULL\n"); |
278 | return -EINVAL; | 349 | goto unlock; |
279 | } | 350 | } |
280 | if (adap_layer->ctrlcmd == NULL) { | 351 | if (adap_layer->ctrlcmd == NULL) { |
281 | pr_err("adap_layer->ctrlcmd == NULL\n"); | 352 | pr_err("adap_layer->ctrlcmd == NULL\n"); |
282 | return -EINVAL; | 353 | goto unlock; |
283 | } | 354 | } |
284 | frml = cnfg->phy_layers[param->phyid].frm_layer; | 355 | |
356 | err = -ENODEV; | ||
357 | frml = phy->frm_layer; | ||
285 | if (frml == NULL) { | 358 | if (frml == NULL) { |
286 | pr_err("Specified PHY type does not exist!\n"); | 359 | pr_err("Specified PHY type does not exist!\n"); |
287 | return -ENODEV; | 360 | goto unlock; |
288 | } | 361 | } |
289 | caif_assert(param->phyid == cnfg->phy_layers[param->phyid].id); | 362 | caif_assert(param.phyid == phy->id); |
290 | caif_assert(cnfg->phy_layers[param->phyid].frm_layer->id == | 363 | caif_assert(phy->frm_layer->id == |
291 | param->phyid); | 364 | param.phyid); |
292 | caif_assert(cnfg->phy_layers[param->phyid].phy_layer->id == | 365 | caif_assert(phy->phy_layer->id == |
293 | param->phyid); | 366 | param.phyid); |
294 | 367 | ||
295 | *ifindex = cnfg->phy_layers[param->phyid].ifindex; | 368 | *ifindex = phy->ifindex; |
369 | *proto_tail = 2; | ||
296 | *proto_head = | 370 | *proto_head = |
297 | protohead[param->linktype]+ | ||
298 | (cnfg->phy_layers[param->phyid].use_stx ? 1 : 0); | ||
299 | 371 | ||
300 | *proto_tail = 2; | 372 | protohead[param.linktype] + (phy->use_stx ? 1 : 0); |
373 | |||
374 | rcu_read_unlock(); | ||
301 | 375 | ||
302 | /* FIXME: ENUMERATE INITIALLY WHEN ACTIVATING PHYSICAL INTERFACE */ | 376 | /* FIXME: ENUMERATE INITIALLY WHEN ACTIVATING PHYSICAL INTERFACE */ |
303 | cfctrl_enum_req(cnfg->ctrl, param->phyid); | 377 | cfctrl_enum_req(cfg->ctrl, param.phyid); |
304 | return cfctrl_linkup_request(cnfg->ctrl, param, adap_layer); | 378 | return cfctrl_linkup_request(cfg->ctrl, ¶m, adap_layer); |
379 | |||
380 | unlock: | ||
381 | rcu_read_unlock(); | ||
382 | return err; | ||
305 | } | 383 | } |
306 | EXPORT_SYMBOL(cfcnfg_add_adaptation_layer); | 384 | EXPORT_SYMBOL(caif_connect_client); |
307 | 385 | ||
308 | static void cfcnfg_reject_rsp(struct cflayer *layer, u8 channel_id, | 386 | static void cfcnfg_reject_rsp(struct cflayer *layer, u8 channel_id, |
309 | struct cflayer *adapt_layer) | 387 | struct cflayer *adapt_layer) |
@@ -315,32 +393,37 @@ static void cfcnfg_reject_rsp(struct cflayer *layer, u8 channel_id, | |||
315 | 393 | ||
316 | static void | 394 | static void |
317 | cfcnfg_linkup_rsp(struct cflayer *layer, u8 channel_id, enum cfctrl_srv serv, | 395 | cfcnfg_linkup_rsp(struct cflayer *layer, u8 channel_id, enum cfctrl_srv serv, |
318 | u8 phyid, struct cflayer *adapt_layer) | 396 | u8 phyid, struct cflayer *adapt_layer) |
319 | { | 397 | { |
320 | struct cfcnfg *cnfg = container_obj(layer); | 398 | struct cfcnfg *cnfg = container_obj(layer); |
321 | struct cflayer *servicel = NULL; | 399 | struct cflayer *servicel = NULL; |
322 | struct cfcnfg_phyinfo *phyinfo; | 400 | struct cfcnfg_phyinfo *phyinfo; |
323 | struct net_device *netdev; | 401 | struct net_device *netdev; |
324 | 402 | ||
403 | rcu_read_lock(); | ||
404 | |||
325 | if (adapt_layer == NULL) { | 405 | if (adapt_layer == NULL) { |
326 | pr_debug("link setup response but no client exist, send linkdown back\n"); | 406 | pr_debug("link setup response but no client exist," |
407 | "send linkdown back\n"); | ||
327 | cfctrl_linkdown_req(cnfg->ctrl, channel_id, NULL); | 408 | cfctrl_linkdown_req(cnfg->ctrl, channel_id, NULL); |
328 | return; | 409 | goto unlock; |
329 | } | 410 | } |
330 | 411 | ||
331 | caif_assert(cnfg != NULL); | 412 | caif_assert(cnfg != NULL); |
332 | caif_assert(phyid != 0); | 413 | caif_assert(phyid != 0); |
333 | phyinfo = &cnfg->phy_layers[phyid]; | 414 | |
415 | phyinfo = cfcnfg_get_phyinfo_rcu(cnfg, phyid); | ||
416 | if (phyinfo == NULL) { | ||
417 | pr_err("ERROR: Link Layer Device dissapeared" | ||
418 | "while connecting\n"); | ||
419 | goto unlock; | ||
420 | } | ||
421 | |||
422 | caif_assert(phyinfo != NULL); | ||
334 | caif_assert(phyinfo->id == phyid); | 423 | caif_assert(phyinfo->id == phyid); |
335 | caif_assert(phyinfo->phy_layer != NULL); | 424 | caif_assert(phyinfo->phy_layer != NULL); |
336 | caif_assert(phyinfo->phy_layer->id == phyid); | 425 | caif_assert(phyinfo->phy_layer->id == phyid); |
337 | 426 | ||
338 | phyinfo->phy_ref_count++; | ||
339 | if (phyinfo->phy_ref_count == 1 && | ||
340 | phyinfo->phy_layer->modemcmd != NULL) { | ||
341 | phyinfo->phy_layer->modemcmd(phyinfo->phy_layer, | ||
342 | _CAIF_MODEMCMD_PHYIF_USEFULL); | ||
343 | } | ||
344 | adapt_layer->id = channel_id; | 427 | adapt_layer->id = channel_id; |
345 | 428 | ||
346 | switch (serv) { | 429 | switch (serv) { |
@@ -348,7 +431,8 @@ cfcnfg_linkup_rsp(struct cflayer *layer, u8 channel_id, enum cfctrl_srv serv, | |||
348 | servicel = cfvei_create(channel_id, &phyinfo->dev_info); | 431 | servicel = cfvei_create(channel_id, &phyinfo->dev_info); |
349 | break; | 432 | break; |
350 | case CFCTRL_SRV_DATAGRAM: | 433 | case CFCTRL_SRV_DATAGRAM: |
351 | servicel = cfdgml_create(channel_id, &phyinfo->dev_info); | 434 | servicel = cfdgml_create(channel_id, |
435 | &phyinfo->dev_info); | ||
352 | break; | 436 | break; |
353 | case CFCTRL_SRV_RFM: | 437 | case CFCTRL_SRV_RFM: |
354 | netdev = phyinfo->dev_info.dev; | 438 | netdev = phyinfo->dev_info.dev; |
@@ -365,94 +449,93 @@ cfcnfg_linkup_rsp(struct cflayer *layer, u8 channel_id, enum cfctrl_srv serv, | |||
365 | servicel = cfdbgl_create(channel_id, &phyinfo->dev_info); | 449 | servicel = cfdbgl_create(channel_id, &phyinfo->dev_info); |
366 | break; | 450 | break; |
367 | default: | 451 | default: |
368 | pr_err("Protocol error. Link setup response - unknown channel type\n"); | 452 | pr_err("Protocol error. Link setup response " |
369 | return; | 453 | "- unknown channel type\n"); |
454 | goto unlock; | ||
370 | } | 455 | } |
371 | if (!servicel) { | 456 | if (!servicel) { |
372 | pr_warn("Out of memory\n"); | 457 | pr_warn("Out of memory\n"); |
373 | return; | 458 | goto unlock; |
374 | } | 459 | } |
375 | layer_set_dn(servicel, cnfg->mux); | 460 | layer_set_dn(servicel, cnfg->mux); |
376 | cfmuxl_set_uplayer(cnfg->mux, servicel, channel_id); | 461 | cfmuxl_set_uplayer(cnfg->mux, servicel, channel_id); |
377 | layer_set_up(servicel, adapt_layer); | 462 | layer_set_up(servicel, adapt_layer); |
378 | layer_set_dn(adapt_layer, servicel); | 463 | layer_set_dn(adapt_layer, servicel); |
379 | cfsrvl_get(servicel); | 464 | |
465 | rcu_read_unlock(); | ||
466 | |||
380 | servicel->ctrlcmd(servicel, CAIF_CTRLCMD_INIT_RSP, 0); | 467 | servicel->ctrlcmd(servicel, CAIF_CTRLCMD_INIT_RSP, 0); |
468 | return; | ||
469 | unlock: | ||
470 | rcu_read_unlock(); | ||
381 | } | 471 | } |
382 | 472 | ||
383 | void | 473 | void |
384 | cfcnfg_add_phy_layer(struct cfcnfg *cnfg, enum cfcnfg_phy_type phy_type, | 474 | cfcnfg_add_phy_layer(struct cfcnfg *cnfg, enum cfcnfg_phy_type phy_type, |
385 | struct net_device *dev, struct cflayer *phy_layer, | 475 | struct net_device *dev, struct cflayer *phy_layer, |
386 | u16 *phyid, enum cfcnfg_phy_preference pref, | 476 | enum cfcnfg_phy_preference pref, |
387 | bool fcs, bool stx) | 477 | bool fcs, bool stx) |
388 | { | 478 | { |
389 | struct cflayer *frml; | 479 | struct cflayer *frml; |
390 | struct cflayer *phy_driver = NULL; | 480 | struct cflayer *phy_driver = NULL; |
481 | struct cfcnfg_phyinfo *phyinfo; | ||
391 | int i; | 482 | int i; |
483 | u8 phyid; | ||
392 | 484 | ||
485 | mutex_lock(&cnfg->lock); | ||
393 | 486 | ||
394 | if (cnfg->phy_layers[cnfg->last_phyid].frm_layer == NULL) { | 487 | /* CAIF protocol allow maximum 6 link-layers */ |
395 | *phyid = cnfg->last_phyid; | 488 | for (i = 0; i < 7; i++) { |
396 | 489 | phyid = (dev->ifindex + i) & 0x7; | |
397 | /* range: * 1..(MAX_PHY_LAYERS-1) */ | 490 | if (phyid == 0) |
398 | cnfg->last_phyid = | 491 | continue; |
399 | (cnfg->last_phyid % (MAX_PHY_LAYERS - 1)) + 1; | 492 | if (cfcnfg_get_phyinfo_rcu(cnfg, phyid) == NULL) |
400 | } else { | 493 | goto got_phyid; |
401 | *phyid = 0; | ||
402 | for (i = 1; i < MAX_PHY_LAYERS; i++) { | ||
403 | if (cnfg->phy_layers[i].frm_layer == NULL) { | ||
404 | *phyid = i; | ||
405 | break; | ||
406 | } | ||
407 | } | ||
408 | } | ||
409 | if (*phyid == 0) { | ||
410 | pr_err("No Available PHY ID\n"); | ||
411 | return; | ||
412 | } | 494 | } |
495 | pr_warn("Too many CAIF Link Layers (max 6)\n"); | ||
496 | goto out; | ||
497 | |||
498 | got_phyid: | ||
499 | phyinfo = kzalloc(sizeof(struct cfcnfg_phyinfo), GFP_ATOMIC); | ||
413 | 500 | ||
414 | switch (phy_type) { | 501 | switch (phy_type) { |
415 | case CFPHYTYPE_FRAG: | 502 | case CFPHYTYPE_FRAG: |
416 | phy_driver = | 503 | phy_driver = |
417 | cfserl_create(CFPHYTYPE_FRAG, *phyid, stx); | 504 | cfserl_create(CFPHYTYPE_FRAG, phyid, stx); |
418 | if (!phy_driver) { | 505 | if (!phy_driver) { |
419 | pr_warn("Out of memory\n"); | 506 | pr_warn("Out of memory\n"); |
420 | return; | 507 | goto out; |
421 | } | 508 | } |
422 | |||
423 | break; | 509 | break; |
424 | case CFPHYTYPE_CAIF: | 510 | case CFPHYTYPE_CAIF: |
425 | phy_driver = NULL; | 511 | phy_driver = NULL; |
426 | break; | 512 | break; |
427 | default: | 513 | default: |
428 | pr_err("%d\n", phy_type); | 514 | goto out; |
429 | return; | ||
430 | break; | ||
431 | } | 515 | } |
432 | 516 | phy_layer->id = phyid; | |
433 | phy_layer->id = *phyid; | 517 | phyinfo->pref = pref; |
434 | cnfg->phy_layers[*phyid].pref = pref; | 518 | phyinfo->id = phyid; |
435 | cnfg->phy_layers[*phyid].id = *phyid; | 519 | phyinfo->dev_info.id = phyid; |
436 | cnfg->phy_layers[*phyid].dev_info.id = *phyid; | 520 | phyinfo->dev_info.dev = dev; |
437 | cnfg->phy_layers[*phyid].dev_info.dev = dev; | 521 | phyinfo->phy_layer = phy_layer; |
438 | cnfg->phy_layers[*phyid].phy_layer = phy_layer; | 522 | phyinfo->ifindex = dev->ifindex; |
439 | cnfg->phy_layers[*phyid].phy_ref_count = 0; | 523 | phyinfo->use_stx = stx; |
440 | cnfg->phy_layers[*phyid].ifindex = dev->ifindex; | 524 | phyinfo->use_fcs = fcs; |
441 | cnfg->phy_layers[*phyid].use_stx = stx; | ||
442 | cnfg->phy_layers[*phyid].use_fcs = fcs; | ||
443 | 525 | ||
444 | phy_layer->type = phy_type; | 526 | phy_layer->type = phy_type; |
445 | frml = cffrml_create(*phyid, fcs); | 527 | frml = cffrml_create(phyid, fcs); |
528 | |||
446 | if (!frml) { | 529 | if (!frml) { |
447 | pr_warn("Out of memory\n"); | 530 | pr_warn("Out of memory\n"); |
448 | return; | 531 | kfree(phyinfo); |
532 | goto out; | ||
449 | } | 533 | } |
450 | cnfg->phy_layers[*phyid].frm_layer = frml; | 534 | phyinfo->frm_layer = frml; |
451 | cfmuxl_set_dnlayer(cnfg->mux, frml, *phyid); | ||
452 | layer_set_up(frml, cnfg->mux); | 535 | layer_set_up(frml, cnfg->mux); |
453 | 536 | ||
454 | if (phy_driver != NULL) { | 537 | if (phy_driver != NULL) { |
455 | phy_driver->id = *phyid; | 538 | phy_driver->id = phyid; |
456 | layer_set_dn(frml, phy_driver); | 539 | layer_set_dn(frml, phy_driver); |
457 | layer_set_up(phy_driver, frml); | 540 | layer_set_up(phy_driver, frml); |
458 | layer_set_dn(phy_driver, phy_layer); | 541 | layer_set_dn(phy_driver, phy_layer); |
@@ -461,33 +544,95 @@ cfcnfg_add_phy_layer(struct cfcnfg *cnfg, enum cfcnfg_phy_type phy_type, | |||
461 | layer_set_dn(frml, phy_layer); | 544 | layer_set_dn(frml, phy_layer); |
462 | layer_set_up(phy_layer, frml); | 545 | layer_set_up(phy_layer, frml); |
463 | } | 546 | } |
547 | |||
548 | list_add_rcu(&phyinfo->node, &cnfg->phys); | ||
549 | out: | ||
550 | mutex_unlock(&cnfg->lock); | ||
464 | } | 551 | } |
465 | EXPORT_SYMBOL(cfcnfg_add_phy_layer); | 552 | EXPORT_SYMBOL(cfcnfg_add_phy_layer); |
466 | 553 | ||
554 | int cfcnfg_set_phy_state(struct cfcnfg *cnfg, struct cflayer *phy_layer, | ||
555 | bool up) | ||
556 | { | ||
557 | struct cfcnfg_phyinfo *phyinfo; | ||
558 | |||
559 | rcu_read_lock(); | ||
560 | phyinfo = cfcnfg_get_phyinfo_rcu(cnfg, phy_layer->id); | ||
561 | if (phyinfo == NULL) { | ||
562 | rcu_read_unlock(); | ||
563 | return -ENODEV; | ||
564 | } | ||
565 | |||
566 | if (phyinfo->up == up) { | ||
567 | rcu_read_unlock(); | ||
568 | return 0; | ||
569 | } | ||
570 | phyinfo->up = up; | ||
571 | |||
572 | if (up) { | ||
573 | cffrml_hold(phyinfo->frm_layer); | ||
574 | cfmuxl_set_dnlayer(cnfg->mux, phyinfo->frm_layer, | ||
575 | phy_layer->id); | ||
576 | } else { | ||
577 | cfmuxl_remove_dnlayer(cnfg->mux, phy_layer->id); | ||
578 | cffrml_put(phyinfo->frm_layer); | ||
579 | } | ||
580 | |||
581 | rcu_read_unlock(); | ||
582 | return 0; | ||
583 | } | ||
584 | EXPORT_SYMBOL(cfcnfg_set_phy_state); | ||
585 | |||
467 | int cfcnfg_del_phy_layer(struct cfcnfg *cnfg, struct cflayer *phy_layer) | 586 | int cfcnfg_del_phy_layer(struct cfcnfg *cnfg, struct cflayer *phy_layer) |
468 | { | 587 | { |
469 | struct cflayer *frml, *frml_dn; | 588 | struct cflayer *frml, *frml_dn; |
470 | u16 phyid; | 589 | u16 phyid; |
590 | struct cfcnfg_phyinfo *phyinfo; | ||
591 | |||
592 | might_sleep(); | ||
593 | |||
594 | mutex_lock(&cnfg->lock); | ||
595 | |||
471 | phyid = phy_layer->id; | 596 | phyid = phy_layer->id; |
472 | caif_assert(phyid == cnfg->phy_layers[phyid].id); | 597 | phyinfo = cfcnfg_get_phyinfo_rcu(cnfg, phyid); |
473 | caif_assert(phy_layer == cnfg->phy_layers[phyid].phy_layer); | 598 | |
599 | if (phyinfo == NULL) { | ||
600 | mutex_unlock(&cnfg->lock); | ||
601 | return 0; | ||
602 | } | ||
603 | caif_assert(phyid == phyinfo->id); | ||
604 | caif_assert(phy_layer == phyinfo->phy_layer); | ||
474 | caif_assert(phy_layer->id == phyid); | 605 | caif_assert(phy_layer->id == phyid); |
475 | caif_assert(cnfg->phy_layers[phyid].frm_layer->id == phyid); | 606 | caif_assert(phyinfo->frm_layer->id == phyid); |
476 | 607 | ||
477 | memset(&cnfg->phy_layers[phy_layer->id], 0, | 608 | list_del_rcu(&phyinfo->node); |
478 | sizeof(struct cfcnfg_phyinfo)); | 609 | synchronize_rcu(); |
479 | frml = cfmuxl_remove_dnlayer(cnfg->mux, phy_layer->id); | 610 | |
611 | /* Fail if reference count is not zero */ | ||
612 | if (cffrml_refcnt_read(phyinfo->frm_layer) != 0) { | ||
613 | pr_info("Wait for device inuse\n"); | ||
614 | list_add_rcu(&phyinfo->node, &cnfg->phys); | ||
615 | mutex_unlock(&cnfg->lock); | ||
616 | return -EAGAIN; | ||
617 | } | ||
618 | |||
619 | frml = phyinfo->frm_layer; | ||
480 | frml_dn = frml->dn; | 620 | frml_dn = frml->dn; |
481 | cffrml_set_uplayer(frml, NULL); | 621 | cffrml_set_uplayer(frml, NULL); |
482 | cffrml_set_dnlayer(frml, NULL); | 622 | cffrml_set_dnlayer(frml, NULL); |
483 | kfree(frml); | ||
484 | |||
485 | if (phy_layer != frml_dn) { | 623 | if (phy_layer != frml_dn) { |
486 | layer_set_up(frml_dn, NULL); | 624 | layer_set_up(frml_dn, NULL); |
487 | layer_set_dn(frml_dn, NULL); | 625 | layer_set_dn(frml_dn, NULL); |
488 | kfree(frml_dn); | ||
489 | } | 626 | } |
490 | layer_set_up(phy_layer, NULL); | 627 | layer_set_up(phy_layer, NULL); |
628 | |||
629 | if (phyinfo->phy_layer != frml_dn) | ||
630 | kfree(frml_dn); | ||
631 | |||
632 | cffrml_free(frml); | ||
633 | kfree(phyinfo); | ||
634 | mutex_unlock(&cnfg->lock); | ||
635 | |||
491 | return 0; | 636 | return 0; |
492 | } | 637 | } |
493 | EXPORT_SYMBOL(cfcnfg_del_phy_layer); | 638 | EXPORT_SYMBOL(cfcnfg_del_phy_layer); |