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 #include <linux/stddef.h>
8 #include <linux/spinlock.h>
9 #include <linux/slab.h>
10 #include <net/caif/caif_layer.h>
11 #include <net/caif/cfpkt.h>
12 #include <net/caif/cfctrl.h>
14 #define container_obj(layr) container_of(layr, struct cfctrl, serv.layer)
15 #define UTILITY_NAME_LENGTH 16
16 #define CFPKT_CTRL_PKT_LEN 20
20 static int handle_loop(struct cfctrl
*ctrl
,
21 int cmd
, struct cfpkt
*pkt
){
25 static int handle_loop(struct cfctrl
*ctrl
,
26 int cmd
, struct cfpkt
*pkt
);
28 static int cfctrl_recv(struct cflayer
*layr
, struct cfpkt
*pkt
);
29 static void cfctrl_ctrlcmd(struct cflayer
*layr
, enum caif_ctrlcmd ctrl
,
33 struct cflayer
*cfctrl_create(void)
35 struct dev_info dev_info
;
37 kmalloc(sizeof(struct cfctrl
), GFP_ATOMIC
);
39 pr_warning("CAIF: %s(): Out of memory\n", __func__
);
42 caif_assert(offsetof(struct cfctrl
, serv
.layer
) == 0);
43 memset(&dev_info
, 0, sizeof(dev_info
));
45 memset(this, 0, sizeof(*this));
46 cfsrvl_init(&this->serv
, 0, &dev_info
, false);
47 atomic_set(&this->req_seq_no
, 1);
48 atomic_set(&this->rsp_seq_no
, 1);
49 this->serv
.layer
.receive
= cfctrl_recv
;
50 sprintf(this->serv
.layer
.name
, "ctrl");
51 this->serv
.layer
.ctrlcmd
= cfctrl_ctrlcmd
;
52 spin_lock_init(&this->loop_linkid_lock
);
53 spin_lock_init(&this->info_list_lock
);
54 INIT_LIST_HEAD(&this->list
);
55 this->loop_linkid
= 1;
56 return &this->serv
.layer
;
59 static bool param_eq(struct cfctrl_link_param
*p1
, struct cfctrl_link_param
*p2
)
62 p1
->linktype
== p2
->linktype
&&
63 p1
->priority
== p2
->priority
&&
64 p1
->phyid
== p2
->phyid
&&
65 p1
->endpoint
== p2
->endpoint
&& p1
->chtype
== p2
->chtype
;
70 switch (p1
->linktype
) {
73 case CFCTRL_SRV_DATAGRAM
:
74 return p1
->u
.datagram
.connid
== p2
->u
.datagram
.connid
;
77 p1
->u
.rfm
.connid
== p2
->u
.rfm
.connid
&&
78 strcmp(p1
->u
.rfm
.volume
, p2
->u
.rfm
.volume
) == 0;
81 p1
->u
.utility
.fifosize_kb
== p2
->u
.utility
.fifosize_kb
82 && p1
->u
.utility
.fifosize_bufs
==
83 p2
->u
.utility
.fifosize_bufs
84 && strcmp(p1
->u
.utility
.name
, p2
->u
.utility
.name
) == 0
85 && p1
->u
.utility
.paramlen
== p2
->u
.utility
.paramlen
86 && memcmp(p1
->u
.utility
.params
, p2
->u
.utility
.params
,
87 p1
->u
.utility
.paramlen
) == 0;
89 case CFCTRL_SRV_VIDEO
:
90 return p1
->u
.video
.connid
== p2
->u
.video
.connid
;
101 bool cfctrl_req_eq(struct cfctrl_request_info
*r1
,
102 struct cfctrl_request_info
*r2
)
104 if (r1
->cmd
!= r2
->cmd
)
106 if (r1
->cmd
== CFCTRL_CMD_LINK_SETUP
)
107 return param_eq(&r1
->param
, &r2
->param
);
109 return r1
->channel_id
== r2
->channel_id
;
112 /* Insert request at the end */
113 void cfctrl_insert_req(struct cfctrl
*ctrl
,
114 struct cfctrl_request_info
*req
)
116 spin_lock(&ctrl
->info_list_lock
);
117 atomic_inc(&ctrl
->req_seq_no
);
118 req
->sequence_no
= atomic_read(&ctrl
->req_seq_no
);
119 list_add_tail(&req
->list
, &ctrl
->list
);
120 spin_unlock(&ctrl
->info_list_lock
);
123 /* Compare and remove request */
124 struct cfctrl_request_info
*cfctrl_remove_req(struct cfctrl
*ctrl
,
125 struct cfctrl_request_info
*req
)
127 struct cfctrl_request_info
*p
, *tmp
, *first
;
129 spin_lock(&ctrl
->info_list_lock
);
130 first
= list_first_entry(&ctrl
->list
, struct cfctrl_request_info
, list
);
132 list_for_each_entry_safe(p
, tmp
, &ctrl
->list
, list
) {
133 if (cfctrl_req_eq(req
, p
)) {
135 pr_warning("CAIF: %s(): Requests are not "
136 "received in order\n",
139 atomic_set(&ctrl
->rsp_seq_no
,
147 spin_unlock(&ctrl
->info_list_lock
);
151 struct cfctrl_rsp
*cfctrl_get_respfuncs(struct cflayer
*layer
)
153 struct cfctrl
*this = container_obj(layer
);
157 void cfctrl_set_dnlayer(struct cflayer
*this, struct cflayer
*dn
)
162 void cfctrl_set_uplayer(struct cflayer
*this, struct cflayer
*up
)
167 static void init_info(struct caif_payload_info
*info
, struct cfctrl
*cfctrl
)
170 info
->channel_id
= cfctrl
->serv
.layer
.id
;
171 info
->dev_info
= &cfctrl
->serv
.dev_info
;
174 void cfctrl_enum_req(struct cflayer
*layer
, u8 physlinkid
)
176 struct cfctrl
*cfctrl
= container_obj(layer
);
178 struct cfpkt
*pkt
= cfpkt_create(CFPKT_CTRL_PKT_LEN
);
180 pr_warning("CAIF: %s(): Out of memory\n", __func__
);
183 caif_assert(offsetof(struct cfctrl
, serv
.layer
) == 0);
184 init_info(cfpkt_info(pkt
), cfctrl
);
185 cfpkt_info(pkt
)->dev_info
->id
= physlinkid
;
186 cfctrl
->serv
.dev_info
.id
= physlinkid
;
187 cfpkt_addbdy(pkt
, CFCTRL_CMD_ENUM
);
188 cfpkt_addbdy(pkt
, physlinkid
);
190 cfctrl
->serv
.layer
.dn
->transmit(cfctrl
->serv
.layer
.dn
, pkt
);
192 pr_err("CAIF: %s(): Could not transmit enum message\n",
198 int cfctrl_linkup_request(struct cflayer
*layer
,
199 struct cfctrl_link_param
*param
,
200 struct cflayer
*user_layer
)
202 struct cfctrl
*cfctrl
= container_obj(layer
);
206 struct cfctrl_request_info
*req
;
208 char utility_name
[16];
209 struct cfpkt
*pkt
= cfpkt_create(CFPKT_CTRL_PKT_LEN
);
211 pr_warning("CAIF: %s(): Out of memory\n", __func__
);
214 cfpkt_addbdy(pkt
, CFCTRL_CMD_LINK_SETUP
);
215 cfpkt_addbdy(pkt
, (param
->chtype
<< 4) + param
->linktype
);
216 cfpkt_addbdy(pkt
, (param
->priority
<< 3) + param
->phyid
);
217 cfpkt_addbdy(pkt
, param
->endpoint
& 0x03);
219 switch (param
->linktype
) {
222 case CFCTRL_SRV_VIDEO
:
223 cfpkt_addbdy(pkt
, (u8
) param
->u
.video
.connid
);
227 case CFCTRL_SRV_DATAGRAM
:
228 tmp32
= cpu_to_le32(param
->u
.datagram
.connid
);
229 cfpkt_add_body(pkt
, &tmp32
, 4);
232 /* Construct a frame, convert DatagramConnectionID to network
233 * format long and copy it out...
235 tmp32
= cpu_to_le32(param
->u
.rfm
.connid
);
236 cfpkt_add_body(pkt
, &tmp32
, 4);
237 /* Add volume name, including zero termination... */
238 cfpkt_add_body(pkt
, param
->u
.rfm
.volume
,
239 strlen(param
->u
.rfm
.volume
) + 1);
241 case CFCTRL_SRV_UTIL
:
242 tmp16
= cpu_to_le16(param
->u
.utility
.fifosize_kb
);
243 cfpkt_add_body(pkt
, &tmp16
, 2);
244 tmp16
= cpu_to_le16(param
->u
.utility
.fifosize_bufs
);
245 cfpkt_add_body(pkt
, &tmp16
, 2);
246 memset(utility_name
, 0, sizeof(utility_name
));
247 strncpy(utility_name
, param
->u
.utility
.name
,
248 UTILITY_NAME_LENGTH
- 1);
249 cfpkt_add_body(pkt
, utility_name
, UTILITY_NAME_LENGTH
);
250 tmp8
= param
->u
.utility
.paramlen
;
251 cfpkt_add_body(pkt
, &tmp8
, 1);
252 cfpkt_add_body(pkt
, param
->u
.utility
.params
,
253 param
->u
.utility
.paramlen
);
256 pr_warning("CAIF: %s():Request setup of bad link type = %d\n",
257 __func__
, param
->linktype
);
260 req
= kzalloc(sizeof(*req
), GFP_KERNEL
);
262 pr_warning("CAIF: %s(): Out of memory\n", __func__
);
265 req
->client_layer
= user_layer
;
266 req
->cmd
= CFCTRL_CMD_LINK_SETUP
;
268 cfctrl_insert_req(cfctrl
, req
);
269 init_info(cfpkt_info(pkt
), cfctrl
);
271 * NOTE:Always send linkup and linkdown request on the same
272 * device as the payload. Otherwise old queued up payload
273 * might arrive with the newly allocated channel ID.
275 cfpkt_info(pkt
)->dev_info
->id
= param
->phyid
;
277 cfctrl
->serv
.layer
.dn
->transmit(cfctrl
->serv
.layer
.dn
, pkt
);
279 pr_err("CAIF: %s(): Could not transmit linksetup request\n",
287 int cfctrl_linkdown_req(struct cflayer
*layer
, u8 channelid
,
288 struct cflayer
*client
)
291 struct cfctrl
*cfctrl
= container_obj(layer
);
292 struct cfpkt
*pkt
= cfpkt_create(CFPKT_CTRL_PKT_LEN
);
294 pr_warning("CAIF: %s(): Out of memory\n", __func__
);
297 cfpkt_addbdy(pkt
, CFCTRL_CMD_LINK_DESTROY
);
298 cfpkt_addbdy(pkt
, channelid
);
299 init_info(cfpkt_info(pkt
), cfctrl
);
301 cfctrl
->serv
.layer
.dn
->transmit(cfctrl
->serv
.layer
.dn
, pkt
);
303 pr_err("CAIF: %s(): Could not transmit link-down request\n",
310 void cfctrl_sleep_req(struct cflayer
*layer
)
313 struct cfctrl
*cfctrl
= container_obj(layer
);
314 struct cfpkt
*pkt
= cfpkt_create(CFPKT_CTRL_PKT_LEN
);
316 pr_warning("CAIF: %s(): Out of memory\n", __func__
);
319 cfpkt_addbdy(pkt
, CFCTRL_CMD_SLEEP
);
320 init_info(cfpkt_info(pkt
), cfctrl
);
322 cfctrl
->serv
.layer
.dn
->transmit(cfctrl
->serv
.layer
.dn
, pkt
);
327 void cfctrl_wake_req(struct cflayer
*layer
)
330 struct cfctrl
*cfctrl
= container_obj(layer
);
331 struct cfpkt
*pkt
= cfpkt_create(CFPKT_CTRL_PKT_LEN
);
333 pr_warning("CAIF: %s(): Out of memory\n", __func__
);
336 cfpkt_addbdy(pkt
, CFCTRL_CMD_WAKE
);
337 init_info(cfpkt_info(pkt
), cfctrl
);
339 cfctrl
->serv
.layer
.dn
->transmit(cfctrl
->serv
.layer
.dn
, pkt
);
344 void cfctrl_getstartreason_req(struct cflayer
*layer
)
347 struct cfctrl
*cfctrl
= container_obj(layer
);
348 struct cfpkt
*pkt
= cfpkt_create(CFPKT_CTRL_PKT_LEN
);
350 pr_warning("CAIF: %s(): Out of memory\n", __func__
);
353 cfpkt_addbdy(pkt
, CFCTRL_CMD_START_REASON
);
354 init_info(cfpkt_info(pkt
), cfctrl
);
356 cfctrl
->serv
.layer
.dn
->transmit(cfctrl
->serv
.layer
.dn
, pkt
);
362 void cfctrl_cancel_req(struct cflayer
*layr
, struct cflayer
*adap_layer
)
364 struct cfctrl_request_info
*p
, *tmp
;
365 struct cfctrl
*ctrl
= container_obj(layr
);
366 spin_lock(&ctrl
->info_list_lock
);
367 pr_warning("CAIF: %s(): enter\n", __func__
);
369 list_for_each_entry_safe(p
, tmp
, &ctrl
->list
, list
) {
370 if (p
->client_layer
== adap_layer
) {
371 pr_warning("CAIF: %s(): cancel req :%d\n", __func__
,
378 spin_unlock(&ctrl
->info_list_lock
);
381 static int cfctrl_recv(struct cflayer
*layer
, struct cfpkt
*pkt
)
390 struct cfctrl
*cfctrl
= container_obj(layer
);
391 struct cfctrl_request_info rsp
, *req
;
394 cfpkt_extr_head(pkt
, &cmdrsp
, 1);
395 cmd
= cmdrsp
& CFCTRL_CMD_MASK
;
396 if (cmd
!= CFCTRL_CMD_LINK_ERR
397 && CFCTRL_RSP_BIT
!= (CFCTRL_RSP_BIT
& cmdrsp
)) {
398 if (handle_loop(cfctrl
, cmd
, pkt
) != 0)
399 cmdrsp
|= CFCTRL_ERR_BIT
;
403 case CFCTRL_CMD_LINK_SETUP
:
405 enum cfctrl_srv serv
;
406 enum cfctrl_srv servtype
;
414 struct cfctrl_link_param linkparam
;
415 memset(&linkparam
, 0, sizeof(linkparam
));
417 cfpkt_extr_head(pkt
, &tmp
, 1);
419 serv
= tmp
& CFCTRL_SRV_MASK
;
420 linkparam
.linktype
= serv
;
423 linkparam
.chtype
= servtype
;
425 cfpkt_extr_head(pkt
, &tmp
, 1);
426 physlinkid
= tmp
& 0x07;
429 linkparam
.priority
= prio
;
430 linkparam
.phyid
= physlinkid
;
431 cfpkt_extr_head(pkt
, &endpoint
, 1);
432 linkparam
.endpoint
= endpoint
& 0x03;
437 if (CFCTRL_ERR_BIT
& cmdrsp
)
440 cfpkt_extr_head(pkt
, &linkid
, 1);
442 case CFCTRL_SRV_VIDEO
:
443 cfpkt_extr_head(pkt
, &tmp
, 1);
444 linkparam
.u
.video
.connid
= tmp
;
445 if (CFCTRL_ERR_BIT
& cmdrsp
)
448 cfpkt_extr_head(pkt
, &linkid
, 1);
451 case CFCTRL_SRV_DATAGRAM
:
452 cfpkt_extr_head(pkt
, &tmp32
, 4);
453 linkparam
.u
.datagram
.connid
=
455 if (CFCTRL_ERR_BIT
& cmdrsp
)
458 cfpkt_extr_head(pkt
, &linkid
, 1);
461 /* Construct a frame, convert
462 * DatagramConnectionID
463 * to network format long and copy it out...
465 cfpkt_extr_head(pkt
, &tmp32
, 4);
466 linkparam
.u
.rfm
.connid
=
468 cp
= (u8
*) linkparam
.u
.rfm
.volume
;
469 for (cfpkt_extr_head(pkt
, &tmp
, 1);
470 cfpkt_more(pkt
) && tmp
!= '\0';
471 cfpkt_extr_head(pkt
, &tmp
, 1))
475 if (CFCTRL_ERR_BIT
& cmdrsp
)
478 cfpkt_extr_head(pkt
, &linkid
, 1);
481 case CFCTRL_SRV_UTIL
:
482 /* Construct a frame, convert
483 * DatagramConnectionID
484 * to network format long and copy it out...
487 cfpkt_extr_head(pkt
, &tmp16
, 2);
488 linkparam
.u
.utility
.fifosize_kb
=
491 cfpkt_extr_head(pkt
, &tmp16
, 2);
492 linkparam
.u
.utility
.fifosize_bufs
=
495 cp
= (u8
*) linkparam
.u
.utility
.name
;
496 caif_assert(sizeof(linkparam
.u
.utility
.name
)
497 >= UTILITY_NAME_LENGTH
);
499 i
< UTILITY_NAME_LENGTH
500 && cfpkt_more(pkt
); i
++) {
501 cfpkt_extr_head(pkt
, &tmp
, 1);
505 cfpkt_extr_head(pkt
, &len
, 1);
506 linkparam
.u
.utility
.paramlen
= len
;
508 cp
= linkparam
.u
.utility
.params
;
509 while (cfpkt_more(pkt
) && len
--) {
510 cfpkt_extr_head(pkt
, &tmp
, 1);
513 if (CFCTRL_ERR_BIT
& cmdrsp
)
516 cfpkt_extr_head(pkt
, &linkid
, 1);
518 cfpkt_extr_head(pkt
, &len
, 1);
520 cfpkt_extr_head(pkt
, ¶m
, len
);
523 pr_warning("CAIF: %s(): Request setup "
524 "- invalid link type (%d)",
530 rsp
.param
= linkparam
;
531 req
= cfctrl_remove_req(cfctrl
, &rsp
);
533 if (CFCTRL_ERR_BIT
== (CFCTRL_ERR_BIT
& cmdrsp
) ||
534 cfpkt_erroneous(pkt
)) {
535 pr_err("CAIF: %s(): Invalid O/E bit or parse "
536 "error on CAIF control channel",
538 cfctrl
->res
.reject_rsp(cfctrl
->serv
.layer
.up
,
540 req
? req
->client_layer
543 cfctrl
->res
.linksetup_rsp(cfctrl
->serv
.
547 client_layer
: NULL
);
554 case CFCTRL_CMD_LINK_DESTROY
:
555 cfpkt_extr_head(pkt
, &linkid
, 1);
556 cfctrl
->res
.linkdestroy_rsp(cfctrl
->serv
.layer
.up
, linkid
);
558 case CFCTRL_CMD_LINK_ERR
:
559 pr_err("CAIF: %s(): Frame Error Indication received\n",
561 cfctrl
->res
.linkerror_ind();
563 case CFCTRL_CMD_ENUM
:
564 cfctrl
->res
.enum_rsp();
566 case CFCTRL_CMD_SLEEP
:
567 cfctrl
->res
.sleep_rsp();
569 case CFCTRL_CMD_WAKE
:
570 cfctrl
->res
.wake_rsp();
572 case CFCTRL_CMD_LINK_RECONF
:
573 cfctrl
->res
.restart_rsp();
575 case CFCTRL_CMD_RADIO_SET
:
576 cfctrl
->res
.radioset_rsp();
579 pr_err("CAIF: %s(): Unrecognized Control Frame\n", __func__
);
589 static void cfctrl_ctrlcmd(struct cflayer
*layr
, enum caif_ctrlcmd ctrl
,
592 struct cfctrl
*this = container_obj(layr
);
594 case _CAIF_CTRLCMD_PHYIF_FLOW_OFF_IND
:
595 case CAIF_CTRLCMD_FLOW_OFF_IND
:
596 spin_lock(&this->info_list_lock
);
597 if (!list_empty(&this->list
)) {
598 pr_debug("CAIF: %s(): Received flow off in "
599 "control layer", __func__
);
601 spin_unlock(&this->info_list_lock
);
609 static int handle_loop(struct cfctrl
*ctrl
, int cmd
, struct cfpkt
*pkt
)
611 static int last_linkid
;
612 u8 linkid
, linktype
, tmp
;
614 case CFCTRL_CMD_LINK_SETUP
:
615 spin_lock(&ctrl
->loop_linkid_lock
);
616 for (linkid
= last_linkid
+ 1; linkid
< 255; linkid
++)
617 if (!ctrl
->loop_linkused
[linkid
])
619 for (linkid
= last_linkid
- 1; linkid
> 0; linkid
--)
620 if (!ctrl
->loop_linkused
[linkid
])
622 spin_unlock(&ctrl
->loop_linkid_lock
);
623 pr_err("CAIF: %s(): Out of link-ids\n", __func__
);
626 if (!ctrl
->loop_linkused
[linkid
])
627 ctrl
->loop_linkused
[linkid
] = 1;
629 last_linkid
= linkid
;
631 cfpkt_add_trail(pkt
, &linkid
, 1);
632 spin_unlock(&ctrl
->loop_linkid_lock
);
633 cfpkt_peek_head(pkt
, &linktype
, 1);
634 if (linktype
== CFCTRL_SRV_UTIL
) {
636 cfpkt_add_trail(pkt
, &tmp
, 1);
637 cfpkt_add_trail(pkt
, &tmp
, 1);
641 case CFCTRL_CMD_LINK_DESTROY
:
642 spin_lock(&ctrl
->loop_linkid_lock
);
643 cfpkt_peek_head(pkt
, &linkid
, 1);
644 ctrl
->loop_linkused
[linkid
] = 0;
645 spin_unlock(&ctrl
->loop_linkid_lock
);