]> git.karo-electronics.de Git - karo-tx-linux.git/blob - net/caif/cfctrl.c
Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/davem/net-3.6
[karo-tx-linux.git] / net / caif / cfctrl.c
1 /*
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
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/cfctrl.h>
15
16 #define container_obj(layr) container_of(layr, struct cfctrl, serv.layer)
17 #define UTILITY_NAME_LENGTH 16
18 #define CFPKT_CTRL_PKT_LEN 20
19
20
21 #ifdef CAIF_NO_LOOP
22 static int handle_loop(struct cfctrl *ctrl,
23                               int cmd, struct cfpkt *pkt){
24         return -1;
25 }
26 #else
27 static int handle_loop(struct cfctrl *ctrl,
28                 int cmd, struct cfpkt *pkt);
29 #endif
30 static int cfctrl_recv(struct cflayer *layr, struct cfpkt *pkt);
31 static void cfctrl_ctrlcmd(struct cflayer *layr, enum caif_ctrlcmd ctrl,
32                            int phyid);
33
34
35 struct cflayer *cfctrl_create(void)
36 {
37         struct dev_info dev_info;
38         struct cfctrl *this =
39                 kmalloc(sizeof(struct cfctrl), GFP_ATOMIC);
40         if (!this) {
41                 pr_warn("Out of memory\n");
42                 return NULL;
43         }
44         caif_assert(offsetof(struct cfctrl, serv.layer) == 0);
45         memset(&dev_info, 0, sizeof(dev_info));
46         dev_info.id = 0xff;
47         memset(this, 0, sizeof(*this));
48         cfsrvl_init(&this->serv, 0, &dev_info, false);
49         atomic_set(&this->req_seq_no, 1);
50         atomic_set(&this->rsp_seq_no, 1);
51         this->serv.layer.receive = cfctrl_recv;
52         sprintf(this->serv.layer.name, "ctrl");
53         this->serv.layer.ctrlcmd = cfctrl_ctrlcmd;
54         spin_lock_init(&this->loop_linkid_lock);
55         spin_lock_init(&this->info_list_lock);
56         INIT_LIST_HEAD(&this->list);
57         this->loop_linkid = 1;
58         return &this->serv.layer;
59 }
60
61 static bool param_eq(const struct cfctrl_link_param *p1,
62                         const struct cfctrl_link_param *p2)
63 {
64         bool eq =
65             p1->linktype == p2->linktype &&
66             p1->priority == p2->priority &&
67             p1->phyid == p2->phyid &&
68             p1->endpoint == p2->endpoint && p1->chtype == p2->chtype;
69
70         if (!eq)
71                 return false;
72
73         switch (p1->linktype) {
74         case CFCTRL_SRV_VEI:
75                 return true;
76         case CFCTRL_SRV_DATAGRAM:
77                 return p1->u.datagram.connid == p2->u.datagram.connid;
78         case CFCTRL_SRV_RFM:
79                 return
80                     p1->u.rfm.connid == p2->u.rfm.connid &&
81                     strcmp(p1->u.rfm.volume, p2->u.rfm.volume) == 0;
82         case CFCTRL_SRV_UTIL:
83                 return
84                     p1->u.utility.fifosize_kb == p2->u.utility.fifosize_kb
85                     && p1->u.utility.fifosize_bufs ==
86                     p2->u.utility.fifosize_bufs
87                     && strcmp(p1->u.utility.name, p2->u.utility.name) == 0
88                     && p1->u.utility.paramlen == p2->u.utility.paramlen
89                     && memcmp(p1->u.utility.params, p2->u.utility.params,
90                               p1->u.utility.paramlen) == 0;
91
92         case CFCTRL_SRV_VIDEO:
93                 return p1->u.video.connid == p2->u.video.connid;
94         case CFCTRL_SRV_DBG:
95                 return true;
96         case CFCTRL_SRV_DECM:
97                 return false;
98         default:
99                 return false;
100         }
101         return false;
102 }
103
104 static bool cfctrl_req_eq(const struct cfctrl_request_info *r1,
105                           const struct cfctrl_request_info *r2)
106 {
107         if (r1->cmd != r2->cmd)
108                 return false;
109         if (r1->cmd == CFCTRL_CMD_LINK_SETUP)
110                 return param_eq(&r1->param, &r2->param);
111         else
112                 return r1->channel_id == r2->channel_id;
113 }
114
115 /* Insert request at the end */
116 static void cfctrl_insert_req(struct cfctrl *ctrl,
117                               struct cfctrl_request_info *req)
118 {
119         spin_lock(&ctrl->info_list_lock);
120         atomic_inc(&ctrl->req_seq_no);
121         req->sequence_no = atomic_read(&ctrl->req_seq_no);
122         list_add_tail(&req->list, &ctrl->list);
123         spin_unlock(&ctrl->info_list_lock);
124 }
125
126 /* Compare and remove request */
127 static struct cfctrl_request_info *cfctrl_remove_req(struct cfctrl *ctrl,
128                                                 struct cfctrl_request_info *req)
129 {
130         struct cfctrl_request_info *p, *tmp, *first;
131
132         spin_lock(&ctrl->info_list_lock);
133         first = list_first_entry(&ctrl->list, struct cfctrl_request_info, list);
134
135         list_for_each_entry_safe(p, tmp, &ctrl->list, list) {
136                 if (cfctrl_req_eq(req, p)) {
137                         if (p != first)
138                                 pr_warn("Requests are not received in order\n");
139
140                         atomic_set(&ctrl->rsp_seq_no,
141                                          p->sequence_no);
142                         list_del(&p->list);
143                         goto out;
144                 }
145         }
146         p = NULL;
147 out:
148         spin_unlock(&ctrl->info_list_lock);
149         return p;
150 }
151
152 struct cfctrl_rsp *cfctrl_get_respfuncs(struct cflayer *layer)
153 {
154         struct cfctrl *this = container_obj(layer);
155         return &this->res;
156 }
157
158 static void init_info(struct caif_payload_info *info, struct cfctrl *cfctrl)
159 {
160         info->hdr_len = 0;
161         info->channel_id = cfctrl->serv.layer.id;
162         info->dev_info = &cfctrl->serv.dev_info;
163 }
164
165 void cfctrl_enum_req(struct cflayer *layer, u8 physlinkid)
166 {
167         struct cfctrl *cfctrl = container_obj(layer);
168         int ret;
169         struct cfpkt *pkt = cfpkt_create(CFPKT_CTRL_PKT_LEN);
170         if (!pkt) {
171                 pr_warn("Out of memory\n");
172                 return;
173         }
174         caif_assert(offsetof(struct cfctrl, serv.layer) == 0);
175         init_info(cfpkt_info(pkt), cfctrl);
176         cfpkt_info(pkt)->dev_info->id = physlinkid;
177         cfctrl->serv.dev_info.id = physlinkid;
178         cfpkt_addbdy(pkt, CFCTRL_CMD_ENUM);
179         cfpkt_addbdy(pkt, physlinkid);
180         ret =
181             cfctrl->serv.layer.dn->transmit(cfctrl->serv.layer.dn, pkt);
182         if (ret < 0) {
183                 pr_err("Could not transmit enum message\n");
184                 cfpkt_destroy(pkt);
185         }
186 }
187
188 int cfctrl_linkup_request(struct cflayer *layer,
189                            struct cfctrl_link_param *param,
190                            struct cflayer *user_layer)
191 {
192         struct cfctrl *cfctrl = container_obj(layer);
193         u32 tmp32;
194         u16 tmp16;
195         u8 tmp8;
196         struct cfctrl_request_info *req;
197         int ret;
198         char utility_name[16];
199         struct cfpkt *pkt = cfpkt_create(CFPKT_CTRL_PKT_LEN);
200         if (!pkt) {
201                 pr_warn("Out of memory\n");
202                 return -ENOMEM;
203         }
204         cfpkt_addbdy(pkt, CFCTRL_CMD_LINK_SETUP);
205         cfpkt_addbdy(pkt, (param->chtype << 4) + param->linktype);
206         cfpkt_addbdy(pkt, (param->priority << 3) + param->phyid);
207         cfpkt_addbdy(pkt, param->endpoint & 0x03);
208
209         switch (param->linktype) {
210         case CFCTRL_SRV_VEI:
211                 break;
212         case CFCTRL_SRV_VIDEO:
213                 cfpkt_addbdy(pkt, (u8) param->u.video.connid);
214                 break;
215         case CFCTRL_SRV_DBG:
216                 break;
217         case CFCTRL_SRV_DATAGRAM:
218                 tmp32 = cpu_to_le32(param->u.datagram.connid);
219                 cfpkt_add_body(pkt, &tmp32, 4);
220                 break;
221         case CFCTRL_SRV_RFM:
222                 /* Construct a frame, convert DatagramConnectionID to network
223                  * format long and copy it out...
224                  */
225                 tmp32 = cpu_to_le32(param->u.rfm.connid);
226                 cfpkt_add_body(pkt, &tmp32, 4);
227                 /* Add volume name, including zero termination... */
228                 cfpkt_add_body(pkt, param->u.rfm.volume,
229                                strlen(param->u.rfm.volume) + 1);
230                 break;
231         case CFCTRL_SRV_UTIL:
232                 tmp16 = cpu_to_le16(param->u.utility.fifosize_kb);
233                 cfpkt_add_body(pkt, &tmp16, 2);
234                 tmp16 = cpu_to_le16(param->u.utility.fifosize_bufs);
235                 cfpkt_add_body(pkt, &tmp16, 2);
236                 memset(utility_name, 0, sizeof(utility_name));
237                 strncpy(utility_name, param->u.utility.name,
238                         UTILITY_NAME_LENGTH - 1);
239                 cfpkt_add_body(pkt, utility_name, UTILITY_NAME_LENGTH);
240                 tmp8 = param->u.utility.paramlen;
241                 cfpkt_add_body(pkt, &tmp8, 1);
242                 cfpkt_add_body(pkt, param->u.utility.params,
243                                param->u.utility.paramlen);
244                 break;
245         default:
246                 pr_warn("Request setup of bad link type = %d\n",
247                         param->linktype);
248                 return -EINVAL;
249         }
250         req = kzalloc(sizeof(*req), GFP_KERNEL);
251         if (!req) {
252                 pr_warn("Out of memory\n");
253                 return -ENOMEM;
254         }
255         req->client_layer = user_layer;
256         req->cmd = CFCTRL_CMD_LINK_SETUP;
257         req->param = *param;
258         cfctrl_insert_req(cfctrl, req);
259         init_info(cfpkt_info(pkt), cfctrl);
260         /*
261          * NOTE:Always send linkup and linkdown request on the same
262          *      device as the payload. Otherwise old queued up payload
263          *      might arrive with the newly allocated channel ID.
264          */
265         cfpkt_info(pkt)->dev_info->id = param->phyid;
266         ret =
267             cfctrl->serv.layer.dn->transmit(cfctrl->serv.layer.dn, pkt);
268         if (ret < 0) {
269                 pr_err("Could not transmit linksetup request\n");
270                 cfpkt_destroy(pkt);
271                 return -ENODEV;
272         }
273         return 0;
274 }
275
276 int cfctrl_linkdown_req(struct cflayer *layer, u8 channelid,
277                                 struct cflayer *client)
278 {
279         int ret;
280         struct cfctrl *cfctrl = container_obj(layer);
281         struct cfpkt *pkt = cfpkt_create(CFPKT_CTRL_PKT_LEN);
282         if (!pkt) {
283                 pr_warn("Out of memory\n");
284                 return -ENOMEM;
285         }
286         cfpkt_addbdy(pkt, CFCTRL_CMD_LINK_DESTROY);
287         cfpkt_addbdy(pkt, channelid);
288         init_info(cfpkt_info(pkt), cfctrl);
289         ret =
290             cfctrl->serv.layer.dn->transmit(cfctrl->serv.layer.dn, pkt);
291         if (ret < 0) {
292                 pr_err("Could not transmit link-down request\n");
293                 cfpkt_destroy(pkt);
294         }
295         return ret;
296 }
297
298 void cfctrl_cancel_req(struct cflayer *layr, struct cflayer *adap_layer)
299 {
300         struct cfctrl_request_info *p, *tmp;
301         struct cfctrl *ctrl = container_obj(layr);
302         spin_lock(&ctrl->info_list_lock);
303
304         list_for_each_entry_safe(p, tmp, &ctrl->list, list) {
305                 if (p->client_layer == adap_layer) {
306                         pr_debug("cancel req :%d\n", p->sequence_no);
307                         list_del(&p->list);
308                         kfree(p);
309                 }
310         }
311
312         spin_unlock(&ctrl->info_list_lock);
313 }
314
315 static int cfctrl_recv(struct cflayer *layer, struct cfpkt *pkt)
316 {
317         u8 cmdrsp;
318         u8 cmd;
319         int ret = -1;
320         u16 tmp16;
321         u8 len;
322         u8 param[255];
323         u8 linkid;
324         struct cfctrl *cfctrl = container_obj(layer);
325         struct cfctrl_request_info rsp, *req;
326
327
328         cfpkt_extr_head(pkt, &cmdrsp, 1);
329         cmd = cmdrsp & CFCTRL_CMD_MASK;
330         if (cmd != CFCTRL_CMD_LINK_ERR
331             && CFCTRL_RSP_BIT != (CFCTRL_RSP_BIT & cmdrsp)) {
332                 if (handle_loop(cfctrl, cmd, pkt) != 0)
333                         cmdrsp |= CFCTRL_ERR_BIT;
334         }
335
336         switch (cmd) {
337         case CFCTRL_CMD_LINK_SETUP:
338                 {
339                         enum cfctrl_srv serv;
340                         enum cfctrl_srv servtype;
341                         u8 endpoint;
342                         u8 physlinkid;
343                         u8 prio;
344                         u8 tmp;
345                         u32 tmp32;
346                         u8 *cp;
347                         int i;
348                         struct cfctrl_link_param linkparam;
349                         memset(&linkparam, 0, sizeof(linkparam));
350
351                         cfpkt_extr_head(pkt, &tmp, 1);
352
353                         serv = tmp & CFCTRL_SRV_MASK;
354                         linkparam.linktype = serv;
355
356                         servtype = tmp >> 4;
357                         linkparam.chtype = servtype;
358
359                         cfpkt_extr_head(pkt, &tmp, 1);
360                         physlinkid = tmp & 0x07;
361                         prio = tmp >> 3;
362
363                         linkparam.priority = prio;
364                         linkparam.phyid = physlinkid;
365                         cfpkt_extr_head(pkt, &endpoint, 1);
366                         linkparam.endpoint = endpoint & 0x03;
367
368                         switch (serv) {
369                         case CFCTRL_SRV_VEI:
370                         case CFCTRL_SRV_DBG:
371                                 if (CFCTRL_ERR_BIT & cmdrsp)
372                                         break;
373                                 /* Link ID */
374                                 cfpkt_extr_head(pkt, &linkid, 1);
375                                 break;
376                         case CFCTRL_SRV_VIDEO:
377                                 cfpkt_extr_head(pkt, &tmp, 1);
378                                 linkparam.u.video.connid = tmp;
379                                 if (CFCTRL_ERR_BIT & cmdrsp)
380                                         break;
381                                 /* Link ID */
382                                 cfpkt_extr_head(pkt, &linkid, 1);
383                                 break;
384
385                         case CFCTRL_SRV_DATAGRAM:
386                                 cfpkt_extr_head(pkt, &tmp32, 4);
387                                 linkparam.u.datagram.connid =
388                                     le32_to_cpu(tmp32);
389                                 if (CFCTRL_ERR_BIT & cmdrsp)
390                                         break;
391                                 /* Link ID */
392                                 cfpkt_extr_head(pkt, &linkid, 1);
393                                 break;
394                         case CFCTRL_SRV_RFM:
395                                 /* Construct a frame, convert
396                                  * DatagramConnectionID
397                                  * to network format long and copy it out...
398                                  */
399                                 cfpkt_extr_head(pkt, &tmp32, 4);
400                                 linkparam.u.rfm.connid =
401                                   le32_to_cpu(tmp32);
402                                 cp = (u8 *) linkparam.u.rfm.volume;
403                                 for (cfpkt_extr_head(pkt, &tmp, 1);
404                                      cfpkt_more(pkt) && tmp != '\0';
405                                      cfpkt_extr_head(pkt, &tmp, 1))
406                                         *cp++ = tmp;
407                                 *cp = '\0';
408
409                                 if (CFCTRL_ERR_BIT & cmdrsp)
410                                         break;
411                                 /* Link ID */
412                                 cfpkt_extr_head(pkt, &linkid, 1);
413
414                                 break;
415                         case CFCTRL_SRV_UTIL:
416                                 /* Construct a frame, convert
417                                  * DatagramConnectionID
418                                  * to network format long and copy it out...
419                                  */
420                                 /* Fifosize KB */
421                                 cfpkt_extr_head(pkt, &tmp16, 2);
422                                 linkparam.u.utility.fifosize_kb =
423                                     le16_to_cpu(tmp16);
424                                 /* Fifosize bufs */
425                                 cfpkt_extr_head(pkt, &tmp16, 2);
426                                 linkparam.u.utility.fifosize_bufs =
427                                     le16_to_cpu(tmp16);
428                                 /* name */
429                                 cp = (u8 *) linkparam.u.utility.name;
430                                 caif_assert(sizeof(linkparam.u.utility.name)
431                                              >= UTILITY_NAME_LENGTH);
432                                 for (i = 0;
433                                      i < UTILITY_NAME_LENGTH
434                                      && cfpkt_more(pkt); i++) {
435                                         cfpkt_extr_head(pkt, &tmp, 1);
436                                         *cp++ = tmp;
437                                 }
438                                 /* Length */
439                                 cfpkt_extr_head(pkt, &len, 1);
440                                 linkparam.u.utility.paramlen = len;
441                                 /* Param Data */
442                                 cp = linkparam.u.utility.params;
443                                 while (cfpkt_more(pkt) && len--) {
444                                         cfpkt_extr_head(pkt, &tmp, 1);
445                                         *cp++ = tmp;
446                                 }
447                                 if (CFCTRL_ERR_BIT & cmdrsp)
448                                         break;
449                                 /* Link ID */
450                                 cfpkt_extr_head(pkt, &linkid, 1);
451                                 /* Length */
452                                 cfpkt_extr_head(pkt, &len, 1);
453                                 /* Param Data */
454                                 cfpkt_extr_head(pkt, &param, len);
455                                 break;
456                         default:
457                                 pr_warn("Request setup - invalid link type (%d)\n",
458                                         serv);
459                                 goto error;
460                         }
461
462                         rsp.cmd = cmd;
463                         rsp.param = linkparam;
464                         req = cfctrl_remove_req(cfctrl, &rsp);
465
466                         if (CFCTRL_ERR_BIT == (CFCTRL_ERR_BIT & cmdrsp) ||
467                                 cfpkt_erroneous(pkt)) {
468                                 pr_err("Invalid O/E bit or parse error on CAIF control channel\n");
469                                 cfctrl->res.reject_rsp(cfctrl->serv.layer.up,
470                                                        0,
471                                                        req ? req->client_layer
472                                                        : NULL);
473                         } else {
474                                 cfctrl->res.linksetup_rsp(cfctrl->serv.
475                                                           layer.up, linkid,
476                                                           serv, physlinkid,
477                                                           req ? req->
478                                                           client_layer : NULL);
479                         }
480
481                         if (req != NULL)
482                                 kfree(req);
483                 }
484                 break;
485         case CFCTRL_CMD_LINK_DESTROY:
486                 cfpkt_extr_head(pkt, &linkid, 1);
487                 cfctrl->res.linkdestroy_rsp(cfctrl->serv.layer.up, linkid);
488                 break;
489         case CFCTRL_CMD_LINK_ERR:
490                 pr_err("Frame Error Indication received\n");
491                 cfctrl->res.linkerror_ind();
492                 break;
493         case CFCTRL_CMD_ENUM:
494                 cfctrl->res.enum_rsp();
495                 break;
496         case CFCTRL_CMD_SLEEP:
497                 cfctrl->res.sleep_rsp();
498                 break;
499         case CFCTRL_CMD_WAKE:
500                 cfctrl->res.wake_rsp();
501                 break;
502         case CFCTRL_CMD_LINK_RECONF:
503                 cfctrl->res.restart_rsp();
504                 break;
505         case CFCTRL_CMD_RADIO_SET:
506                 cfctrl->res.radioset_rsp();
507                 break;
508         default:
509                 pr_err("Unrecognized Control Frame\n");
510                 goto error;
511                 break;
512         }
513         ret = 0;
514 error:
515         cfpkt_destroy(pkt);
516         return ret;
517 }
518
519 static void cfctrl_ctrlcmd(struct cflayer *layr, enum caif_ctrlcmd ctrl,
520                         int phyid)
521 {
522         struct cfctrl *this = container_obj(layr);
523         switch (ctrl) {
524         case _CAIF_CTRLCMD_PHYIF_FLOW_OFF_IND:
525         case CAIF_CTRLCMD_FLOW_OFF_IND:
526                 spin_lock(&this->info_list_lock);
527                 if (!list_empty(&this->list)) {
528                         pr_debug("Received flow off in control layer\n");
529                 }
530                 spin_unlock(&this->info_list_lock);
531                 break;
532         default:
533                 break;
534         }
535 }
536
537 #ifndef CAIF_NO_LOOP
538 static int handle_loop(struct cfctrl *ctrl, int cmd, struct cfpkt *pkt)
539 {
540         static int last_linkid;
541         u8 linkid, linktype, tmp;
542         switch (cmd) {
543         case CFCTRL_CMD_LINK_SETUP:
544                 spin_lock(&ctrl->loop_linkid_lock);
545                 for (linkid = last_linkid + 1; linkid < 255; linkid++)
546                         if (!ctrl->loop_linkused[linkid])
547                                 goto found;
548                 for (linkid = last_linkid - 1; linkid > 0; linkid--)
549                         if (!ctrl->loop_linkused[linkid])
550                                 goto found;
551                 spin_unlock(&ctrl->loop_linkid_lock);
552                 pr_err("Out of link-ids\n");
553                 return -EINVAL;
554 found:
555                 if (!ctrl->loop_linkused[linkid])
556                         ctrl->loop_linkused[linkid] = 1;
557
558                 last_linkid = linkid;
559
560                 cfpkt_add_trail(pkt, &linkid, 1);
561                 spin_unlock(&ctrl->loop_linkid_lock);
562                 cfpkt_peek_head(pkt, &linktype, 1);
563                 if (linktype ==  CFCTRL_SRV_UTIL) {
564                         tmp = 0x01;
565                         cfpkt_add_trail(pkt, &tmp, 1);
566                         cfpkt_add_trail(pkt, &tmp, 1);
567                 }
568                 break;
569
570         case CFCTRL_CMD_LINK_DESTROY:
571                 spin_lock(&ctrl->loop_linkid_lock);
572                 cfpkt_peek_head(pkt, &linkid, 1);
573                 ctrl->loop_linkused[linkid] = 0;
574                 spin_unlock(&ctrl->loop_linkid_lock);
575                 break;
576         default:
577                 break;
578         }
579         return 0;
580 }
581 #endif