net-caif: add CAIF core protocol stack
[pandora-kernel.git] / net / caif / cfmuxl.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 #include <linux/stddef.h>
7 #include <linux/spinlock.h>
8 #include <linux/slab.h>
9 #include <net/caif/cfpkt.h>
10 #include <net/caif/cfmuxl.h>
11 #include <net/caif/cfsrvl.h>
12 #include <net/caif/cffrml.h>
13
14 #define container_obj(layr) container_of(layr, struct cfmuxl, layer)
15
16 #define CAIF_CTRL_CHANNEL 0
17 #define UP_CACHE_SIZE 8
18 #define DN_CACHE_SIZE 8
19
20 struct cfmuxl {
21         struct cflayer layer;
22         struct list_head srvl_list;
23         struct list_head frml_list;
24         struct cflayer *up_cache[UP_CACHE_SIZE];
25         struct cflayer *dn_cache[DN_CACHE_SIZE];
26         /*
27          * Set when inserting or removing downwards layers.
28          */
29         spinlock_t transmit_lock;
30
31         /*
32          * Set when inserting or removing upwards layers.
33          */
34         spinlock_t receive_lock;
35
36 };
37
38 static int cfmuxl_receive(struct cflayer *layr, struct cfpkt *pkt);
39 static int cfmuxl_transmit(struct cflayer *layr, struct cfpkt *pkt);
40 static void cfmuxl_ctrlcmd(struct cflayer *layr, enum caif_ctrlcmd ctrl,
41                                 int phyid);
42 static struct cflayer *get_up(struct cfmuxl *muxl, u16 id);
43
44 struct cflayer *cfmuxl_create(void)
45 {
46         struct cfmuxl *this = kmalloc(sizeof(struct cfmuxl), GFP_ATOMIC);
47         if (!this)
48                 return NULL;
49         memset(this, 0, sizeof(*this));
50         this->layer.receive = cfmuxl_receive;
51         this->layer.transmit = cfmuxl_transmit;
52         this->layer.ctrlcmd = cfmuxl_ctrlcmd;
53         INIT_LIST_HEAD(&this->srvl_list);
54         INIT_LIST_HEAD(&this->frml_list);
55         spin_lock_init(&this->transmit_lock);
56         spin_lock_init(&this->receive_lock);
57         snprintf(this->layer.name, CAIF_LAYER_NAME_SZ, "mux");
58         return &this->layer;
59 }
60
61 int cfmuxl_set_uplayer(struct cflayer *layr, struct cflayer *up, u8 linkid)
62 {
63         struct cfmuxl *muxl = container_obj(layr);
64         spin_lock(&muxl->receive_lock);
65         list_add(&up->node, &muxl->srvl_list);
66         spin_unlock(&muxl->receive_lock);
67         return 0;
68 }
69
70 bool cfmuxl_is_phy_inuse(struct cflayer *layr, u8 phyid)
71 {
72         struct list_head *node;
73         struct cflayer *layer;
74         struct cfmuxl *muxl = container_obj(layr);
75         bool match = false;
76         spin_lock(&muxl->receive_lock);
77
78         list_for_each(node, &muxl->srvl_list) {
79                 layer = list_entry(node, struct cflayer, node);
80                 if (cfsrvl_phyid_match(layer, phyid)) {
81                         match = true;
82                         break;
83                 }
84
85         }
86         spin_unlock(&muxl->receive_lock);
87         return match;
88 }
89
90 u8 cfmuxl_get_phyid(struct cflayer *layr, u8 channel_id)
91 {
92         struct cflayer *up;
93         int phyid;
94         struct cfmuxl *muxl = container_obj(layr);
95         spin_lock(&muxl->receive_lock);
96         up = get_up(muxl, channel_id);
97         if (up != NULL)
98                 phyid = cfsrvl_getphyid(up);
99         else
100                 phyid = 0;
101         spin_unlock(&muxl->receive_lock);
102         return phyid;
103 }
104
105 int cfmuxl_set_dnlayer(struct cflayer *layr, struct cflayer *dn, u8 phyid)
106 {
107         struct cfmuxl *muxl = (struct cfmuxl *) layr;
108         spin_lock(&muxl->transmit_lock);
109         list_add(&dn->node, &muxl->frml_list);
110         spin_unlock(&muxl->transmit_lock);
111         return 0;
112 }
113
114 static struct cflayer *get_from_id(struct list_head *list, u16 id)
115 {
116         struct list_head *node;
117         struct cflayer *layer;
118         list_for_each(node, list) {
119                 layer = list_entry(node, struct cflayer, node);
120                 if (layer->id == id)
121                         return layer;
122         }
123         return NULL;
124 }
125
126 struct cflayer *cfmuxl_remove_dnlayer(struct cflayer *layr, u8 phyid)
127 {
128         struct cfmuxl *muxl = container_obj(layr);
129         struct cflayer *dn;
130         spin_lock(&muxl->transmit_lock);
131         memset(muxl->dn_cache, 0, sizeof(muxl->dn_cache));
132         dn = get_from_id(&muxl->frml_list, phyid);
133         if (dn == NULL) {
134                 spin_unlock(&muxl->transmit_lock);
135                 return NULL;
136         }
137         list_del(&dn->node);
138         caif_assert(dn != NULL);
139         spin_unlock(&muxl->transmit_lock);
140         return dn;
141 }
142
143 /* Invariant: lock is taken */
144 static struct cflayer *get_up(struct cfmuxl *muxl, u16 id)
145 {
146         struct cflayer *up;
147         int idx = id % UP_CACHE_SIZE;
148         up = muxl->up_cache[idx];
149         if (up == NULL || up->id != id) {
150                 up = get_from_id(&muxl->srvl_list, id);
151                 muxl->up_cache[idx] = up;
152         }
153         return up;
154 }
155
156 /* Invariant: lock is taken */
157 static struct cflayer *get_dn(struct cfmuxl *muxl, struct dev_info *dev_info)
158 {
159         struct cflayer *dn;
160         int idx = dev_info->id % DN_CACHE_SIZE;
161         dn = muxl->dn_cache[idx];
162         if (dn == NULL || dn->id != dev_info->id) {
163                 dn = get_from_id(&muxl->frml_list, dev_info->id);
164                 muxl->dn_cache[idx] = dn;
165         }
166         return dn;
167 }
168
169 struct cflayer *cfmuxl_remove_uplayer(struct cflayer *layr, u8 id)
170 {
171         struct cflayer *up;
172         struct cfmuxl *muxl = container_obj(layr);
173         spin_lock(&muxl->receive_lock);
174         up = get_up(muxl, id);
175         memset(muxl->up_cache, 0, sizeof(muxl->up_cache));
176         list_del(&up->node);
177         spin_unlock(&muxl->receive_lock);
178         return up;
179 }
180
181 static int cfmuxl_receive(struct cflayer *layr, struct cfpkt *pkt)
182 {
183         int ret;
184         struct cfmuxl *muxl = container_obj(layr);
185         u8 id;
186         struct cflayer *up;
187         if (cfpkt_extr_head(pkt, &id, 1) < 0) {
188                 pr_err("CAIF: %s(): erroneous Caif Packet\n", __func__);
189                 cfpkt_destroy(pkt);
190                 return -EPROTO;
191         }
192
193         spin_lock(&muxl->receive_lock);
194         up = get_up(muxl, id);
195         spin_unlock(&muxl->receive_lock);
196         if (up == NULL) {
197                 pr_info("CAIF: %s():Received data on unknown link ID = %d "
198                         "(0x%x)  up == NULL", __func__, id, id);
199                 cfpkt_destroy(pkt);
200                 /*
201                  * Don't return ERROR, since modem misbehaves and sends out
202                  * flow on before linksetup response.
203                  */
204                 return /* CFGLU_EPROT; */ 0;
205         }
206
207         ret = up->receive(up, pkt);
208         return ret;
209 }
210
211 static int cfmuxl_transmit(struct cflayer *layr, struct cfpkt *pkt)
212 {
213         int ret;
214         struct cfmuxl *muxl = container_obj(layr);
215         u8 linkid;
216         struct cflayer *dn;
217         struct caif_payload_info *info = cfpkt_info(pkt);
218         dn = get_dn(muxl, cfpkt_info(pkt)->dev_info);
219         if (dn == NULL) {
220                 pr_warning("CAIF: %s(): Send data on unknown phy "
221                            "ID = %d (0x%x)\n",
222                            __func__, info->dev_info->id, info->dev_info->id);
223                 return -ENOTCONN;
224         }
225         info->hdr_len += 1;
226         linkid = info->channel_id;
227         cfpkt_add_head(pkt, &linkid, 1);
228         ret = dn->transmit(dn, pkt);
229         /* Remove MUX protocol header upon error. */
230         if (ret < 0)
231                 cfpkt_extr_head(pkt, &linkid, 1);
232         return ret;
233 }
234
235 static void cfmuxl_ctrlcmd(struct cflayer *layr, enum caif_ctrlcmd ctrl,
236                                 int phyid)
237 {
238         struct cfmuxl *muxl = container_obj(layr);
239         struct list_head *node;
240         struct cflayer *layer;
241         list_for_each(node, &muxl->srvl_list) {
242                 layer = list_entry(node, struct cflayer, node);
243                 if (cfsrvl_phyid_match(layer, phyid))
244                         layer->ctrlcmd(layer, ctrl, phyid);
245         }
246 }