]>
Commit | Line | Data |
---|---|---|
53618cc1 PS |
1 | /* |
2 | * Shared Transport Line discipline driver Core | |
3 | * This hooks up ST KIM driver and ST LL driver | |
a0cc2f3b PS |
4 | * Copyright (C) 2009-2010 Texas Instruments |
5 | * Author: Pavan Savoy <pavan_savoy@ti.com> | |
53618cc1 PS |
6 | * |
7 | * This program is free software; you can redistribute it and/or modify | |
8 | * it under the terms of the GNU General Public License version 2 as | |
9 | * published by the Free Software Foundation. | |
10 | * | |
11 | * This program is distributed in the hope that it will be useful, | |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | * GNU General Public License for more details. | |
15 | * | |
16 | * You should have received a copy of the GNU General Public License | |
17 | * along with this program; if not, write to the Free Software | |
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
19 | * | |
20 | */ | |
21 | ||
22 | #define pr_fmt(fmt) "(stc): " fmt | |
23 | #include <linux/module.h> | |
24 | #include <linux/kernel.h> | |
25 | #include <linux/init.h> | |
26 | #include <linux/tty.h> | |
27 | ||
5c88b021 PS |
28 | #include <linux/seq_file.h> |
29 | #include <linux/skbuff.h> | |
30 | ||
e5558679 | 31 | #include <linux/ti_wilink_st.h> |
53618cc1 | 32 | |
53618cc1 PS |
33 | /* function pointer pointing to either, |
34 | * st_kim_recv during registration to receive fw download responses | |
35 | * st_int_recv after registration to receive proto stack responses | |
36 | */ | |
37 | void (*st_recv) (void*, const unsigned char*, long); | |
38 | ||
39 | /********************************************************************/ | |
5c88b021 PS |
40 | static void add_channel_to_table(struct st_data_s *st_gdata, |
41 | struct st_proto_s *new_proto) | |
53618cc1 | 42 | { |
5c88b021 PS |
43 | pr_info("%s: id %d\n", __func__, new_proto->chnl_id); |
44 | /* list now has the channel id as index itself */ | |
45 | st_gdata->list[new_proto->chnl_id] = new_proto; | |
764b0c4b | 46 | st_gdata->is_registered[new_proto->chnl_id] = true; |
5c88b021 PS |
47 | } |
48 | ||
49 | static void remove_channel_from_table(struct st_data_s *st_gdata, | |
50 | struct st_proto_s *proto) | |
51 | { | |
52 | pr_info("%s: id %d\n", __func__, proto->chnl_id); | |
764b0c4b PS |
53 | /* st_gdata->list[proto->chnl_id] = NULL; */ |
54 | st_gdata->is_registered[proto->chnl_id] = false; | |
53618cc1 | 55 | } |
36b5aee4 | 56 | |
ef04d121 PS |
57 | /* |
58 | * called from KIM during firmware download. | |
59 | * | |
60 | * This is a wrapper function to tty->ops->write_room. | |
61 | * It returns number of free space available in | |
62 | * uart tx buffer. | |
63 | */ | |
64 | int st_get_uart_wr_room(struct st_data_s *st_gdata) | |
65 | { | |
66 | struct tty_struct *tty; | |
67 | if (unlikely(st_gdata == NULL || st_gdata->tty == NULL)) { | |
68 | pr_err("tty unavailable to perform write"); | |
69 | return -1; | |
70 | } | |
71 | tty = st_gdata->tty; | |
72 | return tty->ops->write_room(tty); | |
73 | } | |
74 | ||
53618cc1 PS |
75 | /* can be called in from |
76 | * -- KIM (during fw download) | |
77 | * -- ST Core (during st_write) | |
78 | * | |
79 | * This is the internal write function - a wrapper | |
80 | * to tty->ops->write | |
81 | */ | |
82 | int st_int_write(struct st_data_s *st_gdata, | |
83 | const unsigned char *data, int count) | |
84 | { | |
53618cc1 PS |
85 | struct tty_struct *tty; |
86 | if (unlikely(st_gdata == NULL || st_gdata->tty == NULL)) { | |
87 | pr_err("tty unavailable to perform write"); | |
70442664 | 88 | return -EINVAL; |
53618cc1 PS |
89 | } |
90 | tty = st_gdata->tty; | |
91 | #ifdef VERBOSE | |
e6d9e64e PS |
92 | print_hex_dump(KERN_DEBUG, "<out<", DUMP_PREFIX_NONE, |
93 | 16, 1, data, count, 0); | |
53618cc1 PS |
94 | #endif |
95 | return tty->ops->write(tty, data, count); | |
96 | ||
97 | } | |
98 | ||
99 | /* | |
100 | * push the skb received to relevant | |
101 | * protocol stacks | |
102 | */ | |
5c88b021 | 103 | void st_send_frame(unsigned char chnl_id, struct st_data_s *st_gdata) |
53618cc1 | 104 | { |
6710fcff | 105 | pr_debug(" %s(prot:%d) ", __func__, chnl_id); |
53618cc1 PS |
106 | |
107 | if (unlikely | |
108 | (st_gdata == NULL || st_gdata->rx_skb == NULL | |
764b0c4b | 109 | || st_gdata->is_registered[chnl_id] == false)) { |
5c88b021 PS |
110 | pr_err("chnl_id %d not registered, no data to send?", |
111 | chnl_id); | |
53618cc1 PS |
112 | kfree_skb(st_gdata->rx_skb); |
113 | return; | |
114 | } | |
115 | /* this cannot fail | |
116 | * this shouldn't take long | |
117 | * - should be just skb_queue_tail for the | |
118 | * protocol stack driver | |
119 | */ | |
5c88b021 | 120 | if (likely(st_gdata->list[chnl_id]->recv != NULL)) { |
bb8f3c06 | 121 | if (unlikely |
5c88b021 PS |
122 | (st_gdata->list[chnl_id]->recv |
123 | (st_gdata->list[chnl_id]->priv_data, st_gdata->rx_skb) | |
320920cb | 124 | != 0)) { |
5c88b021 | 125 | pr_err(" proto stack %d's ->recv failed", chnl_id); |
53618cc1 PS |
126 | kfree_skb(st_gdata->rx_skb); |
127 | return; | |
128 | } | |
129 | } else { | |
5c88b021 | 130 | pr_err(" proto stack %d's ->recv null", chnl_id); |
53618cc1 PS |
131 | kfree_skb(st_gdata->rx_skb); |
132 | } | |
53618cc1 PS |
133 | return; |
134 | } | |
135 | ||
36b5aee4 PS |
136 | /** |
137 | * st_reg_complete - | |
53618cc1 PS |
138 | * to call registration complete callbacks |
139 | * of all protocol stack drivers | |
140 | */ | |
141 | void st_reg_complete(struct st_data_s *st_gdata, char err) | |
142 | { | |
143 | unsigned char i = 0; | |
144 | pr_info(" %s ", __func__); | |
5c88b021 | 145 | for (i = 0; i < ST_MAX_CHANNELS; i++) { |
764b0c4b PS |
146 | if (likely(st_gdata != NULL && |
147 | st_gdata->is_registered[i] == true && | |
148 | st_gdata->list[i]->reg_complete_cb != NULL)) { | |
bb8f3c06 PS |
149 | st_gdata->list[i]->reg_complete_cb |
150 | (st_gdata->list[i]->priv_data, err); | |
70442664 PS |
151 | pr_info("protocol %d's cb sent %d\n", i, err); |
152 | if (err) { /* cleanup registered protocol */ | |
153 | st_gdata->protos_registered--; | |
764b0c4b | 154 | st_gdata->is_registered[i] = false; |
70442664 PS |
155 | } |
156 | } | |
53618cc1 PS |
157 | } |
158 | } | |
159 | ||
160 | static inline int st_check_data_len(struct st_data_s *st_gdata, | |
5c88b021 | 161 | unsigned char chnl_id, int len) |
53618cc1 | 162 | { |
73f12e8d | 163 | int room = skb_tailroom(st_gdata->rx_skb); |
53618cc1 | 164 | |
e6d9e64e | 165 | pr_debug("len %d room %d", len, room); |
53618cc1 PS |
166 | |
167 | if (!len) { | |
168 | /* Received packet has only packet header and | |
169 | * has zero length payload. So, ask ST CORE to | |
170 | * forward the packet to protocol driver (BT/FM/GPS) | |
171 | */ | |
5c88b021 | 172 | st_send_frame(chnl_id, st_gdata); |
53618cc1 PS |
173 | |
174 | } else if (len > room) { | |
175 | /* Received packet's payload length is larger. | |
176 | * We can't accommodate it in created skb. | |
177 | */ | |
178 | pr_err("Data length is too large len %d room %d", len, | |
179 | room); | |
180 | kfree_skb(st_gdata->rx_skb); | |
181 | } else { | |
182 | /* Packet header has non-zero payload length and | |
183 | * we have enough space in created skb. Lets read | |
184 | * payload data */ | |
5c88b021 | 185 | st_gdata->rx_state = ST_W4_DATA; |
53618cc1 PS |
186 | st_gdata->rx_count = len; |
187 | return len; | |
188 | } | |
189 | ||
190 | /* Change ST state to continue to process next | |
191 | * packet */ | |
192 | st_gdata->rx_state = ST_W4_PACKET_TYPE; | |
193 | st_gdata->rx_skb = NULL; | |
194 | st_gdata->rx_count = 0; | |
5c88b021 | 195 | st_gdata->rx_chnl = 0; |
53618cc1 PS |
196 | |
197 | return 0; | |
198 | } | |
199 | ||
36b5aee4 PS |
200 | /** |
201 | * st_wakeup_ack - internal function for action when wake-up ack | |
202 | * received | |
53618cc1 PS |
203 | */ |
204 | static inline void st_wakeup_ack(struct st_data_s *st_gdata, | |
205 | unsigned char cmd) | |
206 | { | |
73f12e8d | 207 | struct sk_buff *waiting_skb; |
53618cc1 PS |
208 | unsigned long flags = 0; |
209 | ||
210 | spin_lock_irqsave(&st_gdata->lock, flags); | |
211 | /* de-Q from waitQ and Q in txQ now that the | |
212 | * chip is awake | |
213 | */ | |
214 | while ((waiting_skb = skb_dequeue(&st_gdata->tx_waitq))) | |
215 | skb_queue_tail(&st_gdata->txq, waiting_skb); | |
216 | ||
217 | /* state forwarded to ST LL */ | |
218 | st_ll_sleep_state(st_gdata, (unsigned long)cmd); | |
219 | spin_unlock_irqrestore(&st_gdata->lock, flags); | |
220 | ||
221 | /* wake up to send the recently copied skbs from waitQ */ | |
222 | st_tx_wakeup(st_gdata); | |
223 | } | |
224 | ||
36b5aee4 PS |
225 | /** |
226 | * st_int_recv - ST's internal receive function. | |
227 | * Decodes received RAW data and forwards to corresponding | |
228 | * client drivers (Bluetooth,FM,GPS..etc). | |
229 | * This can receive various types of packets, | |
230 | * HCI-Events, ACL, SCO, 4 types of HCI-LL PM packets | |
231 | * CH-8 packets from FM, CH-9 packets from GPS cores. | |
53618cc1 PS |
232 | */ |
233 | void st_int_recv(void *disc_data, | |
234 | const unsigned char *data, long count) | |
235 | { | |
73f12e8d | 236 | char *ptr; |
5c88b021 PS |
237 | struct st_proto_s *proto; |
238 | unsigned short payload_len = 0; | |
239 | int len = 0, type = 0; | |
240 | unsigned char *plen; | |
53618cc1 | 241 | struct st_data_s *st_gdata = (struct st_data_s *)disc_data; |
6d71ba21 | 242 | unsigned long flags; |
53618cc1 PS |
243 | |
244 | ptr = (char *)data; | |
245 | /* tty_receive sent null ? */ | |
246 | if (unlikely(ptr == NULL) || (st_gdata == NULL)) { | |
247 | pr_err(" received null from TTY "); | |
248 | return; | |
249 | } | |
250 | ||
6710fcff | 251 | pr_debug("count %ld rx_state %ld" |
53618cc1 PS |
252 | "rx_count %ld", count, st_gdata->rx_state, |
253 | st_gdata->rx_count); | |
254 | ||
6d71ba21 | 255 | spin_lock_irqsave(&st_gdata->lock, flags); |
53618cc1 PS |
256 | /* Decode received bytes here */ |
257 | while (count) { | |
258 | if (st_gdata->rx_count) { | |
259 | len = min_t(unsigned int, st_gdata->rx_count, count); | |
260 | memcpy(skb_put(st_gdata->rx_skb, len), ptr, len); | |
261 | st_gdata->rx_count -= len; | |
262 | count -= len; | |
263 | ptr += len; | |
264 | ||
265 | if (st_gdata->rx_count) | |
266 | continue; | |
267 | ||
268 | /* Check ST RX state machine , where are we? */ | |
269 | switch (st_gdata->rx_state) { | |
5c88b021 PS |
270 | /* Waiting for complete packet ? */ |
271 | case ST_W4_DATA: | |
e6d9e64e | 272 | pr_debug("Complete pkt received"); |
53618cc1 PS |
273 | /* Ask ST CORE to forward |
274 | * the packet to protocol driver */ | |
5c88b021 | 275 | st_send_frame(st_gdata->rx_chnl, st_gdata); |
53618cc1 PS |
276 | |
277 | st_gdata->rx_state = ST_W4_PACKET_TYPE; | |
278 | st_gdata->rx_skb = NULL; | |
53618cc1 | 279 | continue; |
5c88b021 PS |
280 | /* parse the header to know details */ |
281 | case ST_W4_HEADER: | |
282 | proto = st_gdata->list[st_gdata->rx_chnl]; | |
283 | plen = | |
284 | &st_gdata->rx_skb->data | |
285 | [proto->offset_len_in_hdr]; | |
6710fcff | 286 | pr_debug("plen pointing to %x\n", *plen); |
5c88b021 PS |
287 | if (proto->len_size == 1)/* 1 byte len field */ |
288 | payload_len = *(unsigned char *)plen; | |
289 | else if (proto->len_size == 2) | |
290 | payload_len = | |
291 | __le16_to_cpu(*(unsigned short *)plen); | |
292 | else | |
293 | pr_info("%s: invalid length " | |
294 | "for id %d\n", | |
295 | __func__, proto->chnl_id); | |
296 | st_check_data_len(st_gdata, proto->chnl_id, | |
297 | payload_len); | |
6710fcff | 298 | pr_debug("off %d, pay len %d\n", |
5c88b021 | 299 | proto->offset_len_in_hdr, payload_len); |
53618cc1 PS |
300 | continue; |
301 | } /* end of switch rx_state */ | |
302 | } | |
303 | ||
304 | /* end of if rx_count */ | |
305 | /* Check first byte of packet and identify module | |
306 | * owner (BT/FM/GPS) */ | |
307 | switch (*ptr) { | |
53618cc1 PS |
308 | case LL_SLEEP_IND: |
309 | case LL_SLEEP_ACK: | |
310 | case LL_WAKE_UP_IND: | |
6710fcff | 311 | pr_debug("PM packet"); |
53618cc1 PS |
312 | /* this takes appropriate action based on |
313 | * sleep state received -- | |
314 | */ | |
315 | st_ll_sleep_state(st_gdata, *ptr); | |
6d71ba21 PS |
316 | /* if WAKEUP_IND collides copy from waitq to txq |
317 | * and assume chip awake | |
318 | */ | |
319 | spin_unlock_irqrestore(&st_gdata->lock, flags); | |
320 | if (st_ll_getstate(st_gdata) == ST_LL_AWAKE) | |
321 | st_wakeup_ack(st_gdata, LL_WAKE_UP_ACK); | |
322 | spin_lock_irqsave(&st_gdata->lock, flags); | |
323 | ||
53618cc1 PS |
324 | ptr++; |
325 | count--; | |
326 | continue; | |
327 | case LL_WAKE_UP_ACK: | |
6710fcff | 328 | pr_debug("PM packet"); |
6d71ba21 PS |
329 | |
330 | spin_unlock_irqrestore(&st_gdata->lock, flags); | |
53618cc1 PS |
331 | /* wake up ack received */ |
332 | st_wakeup_ack(st_gdata, *ptr); | |
6d71ba21 PS |
333 | spin_lock_irqsave(&st_gdata->lock, flags); |
334 | ||
53618cc1 PS |
335 | ptr++; |
336 | count--; | |
337 | continue; | |
338 | /* Unknow packet? */ | |
339 | default: | |
5c88b021 PS |
340 | type = *ptr; |
341 | st_gdata->rx_skb = alloc_skb( | |
342 | st_gdata->list[type]->max_frame_size, | |
343 | GFP_ATOMIC); | |
344 | skb_reserve(st_gdata->rx_skb, | |
345 | st_gdata->list[type]->reserve); | |
346 | /* next 2 required for BT only */ | |
347 | st_gdata->rx_skb->cb[0] = type; /*pkt_type*/ | |
348 | st_gdata->rx_skb->cb[1] = 0; /*incoming*/ | |
349 | st_gdata->rx_chnl = *ptr; | |
350 | st_gdata->rx_state = ST_W4_HEADER; | |
351 | st_gdata->rx_count = st_gdata->list[type]->hdr_len; | |
6710fcff | 352 | pr_debug("rx_count %ld\n", st_gdata->rx_count); |
53618cc1 PS |
353 | }; |
354 | ptr++; | |
355 | count--; | |
53618cc1 | 356 | } |
6d71ba21 | 357 | spin_unlock_irqrestore(&st_gdata->lock, flags); |
e6d9e64e | 358 | pr_debug("done %s", __func__); |
53618cc1 PS |
359 | return; |
360 | } | |
361 | ||
36b5aee4 PS |
362 | /** |
363 | * st_int_dequeue - internal de-Q function. | |
364 | * If the previous data set was not written | |
365 | * completely, return that skb which has the pending data. | |
366 | * In normal cases, return top of txq. | |
53618cc1 PS |
367 | */ |
368 | struct sk_buff *st_int_dequeue(struct st_data_s *st_gdata) | |
369 | { | |
370 | struct sk_buff *returning_skb; | |
371 | ||
e6d9e64e | 372 | pr_debug("%s", __func__); |
53618cc1 PS |
373 | if (st_gdata->tx_skb != NULL) { |
374 | returning_skb = st_gdata->tx_skb; | |
375 | st_gdata->tx_skb = NULL; | |
376 | return returning_skb; | |
377 | } | |
53618cc1 PS |
378 | return skb_dequeue(&st_gdata->txq); |
379 | } | |
380 | ||
36b5aee4 PS |
381 | /** |
382 | * st_int_enqueue - internal Q-ing function. | |
383 | * Will either Q the skb to txq or the tx_waitq | |
384 | * depending on the ST LL state. | |
385 | * If the chip is asleep, then Q it onto waitq and | |
386 | * wakeup the chip. | |
387 | * txq and waitq needs protection since the other contexts | |
388 | * may be sending data, waking up chip. | |
53618cc1 PS |
389 | */ |
390 | void st_int_enqueue(struct st_data_s *st_gdata, struct sk_buff *skb) | |
391 | { | |
392 | unsigned long flags = 0; | |
393 | ||
e6d9e64e | 394 | pr_debug("%s", __func__); |
53618cc1 PS |
395 | spin_lock_irqsave(&st_gdata->lock, flags); |
396 | ||
397 | switch (st_ll_getstate(st_gdata)) { | |
398 | case ST_LL_AWAKE: | |
6710fcff | 399 | pr_debug("ST LL is AWAKE, sending normally"); |
53618cc1 PS |
400 | skb_queue_tail(&st_gdata->txq, skb); |
401 | break; | |
402 | case ST_LL_ASLEEP_TO_AWAKE: | |
403 | skb_queue_tail(&st_gdata->tx_waitq, skb); | |
404 | break; | |
36b5aee4 | 405 | case ST_LL_AWAKE_TO_ASLEEP: |
53618cc1 PS |
406 | pr_err("ST LL is illegal state(%ld)," |
407 | "purging received skb.", st_ll_getstate(st_gdata)); | |
408 | kfree_skb(skb); | |
409 | break; | |
53618cc1 | 410 | case ST_LL_ASLEEP: |
53618cc1 PS |
411 | skb_queue_tail(&st_gdata->tx_waitq, skb); |
412 | st_ll_wakeup(st_gdata); | |
413 | break; | |
414 | default: | |
415 | pr_err("ST LL is illegal state(%ld)," | |
416 | "purging received skb.", st_ll_getstate(st_gdata)); | |
417 | kfree_skb(skb); | |
418 | break; | |
419 | } | |
36b5aee4 | 420 | |
53618cc1 | 421 | spin_unlock_irqrestore(&st_gdata->lock, flags); |
e6d9e64e | 422 | pr_debug("done %s", __func__); |
53618cc1 PS |
423 | return; |
424 | } | |
425 | ||
426 | /* | |
427 | * internal wakeup function | |
428 | * called from either | |
429 | * - TTY layer when write's finished | |
430 | * - st_write (in context of the protocol stack) | |
431 | */ | |
432 | void st_tx_wakeup(struct st_data_s *st_data) | |
433 | { | |
434 | struct sk_buff *skb; | |
435 | unsigned long flags; /* for irq save flags */ | |
e6d9e64e | 436 | pr_debug("%s", __func__); |
53618cc1 PS |
437 | /* check for sending & set flag sending here */ |
438 | if (test_and_set_bit(ST_TX_SENDING, &st_data->tx_state)) { | |
6710fcff | 439 | pr_debug("ST already sending"); |
53618cc1 PS |
440 | /* keep sending */ |
441 | set_bit(ST_TX_WAKEUP, &st_data->tx_state); | |
442 | return; | |
443 | /* TX_WAKEUP will be checked in another | |
444 | * context | |
445 | */ | |
446 | } | |
447 | do { /* come back if st_tx_wakeup is set */ | |
448 | /* woke-up to write */ | |
449 | clear_bit(ST_TX_WAKEUP, &st_data->tx_state); | |
450 | while ((skb = st_int_dequeue(st_data))) { | |
451 | int len; | |
452 | spin_lock_irqsave(&st_data->lock, flags); | |
453 | /* enable wake-up from TTY */ | |
454 | set_bit(TTY_DO_WRITE_WAKEUP, &st_data->tty->flags); | |
455 | len = st_int_write(st_data, skb->data, skb->len); | |
456 | skb_pull(skb, len); | |
457 | /* if skb->len = len as expected, skb->len=0 */ | |
458 | if (skb->len) { | |
459 | /* would be the next skb to be sent */ | |
460 | st_data->tx_skb = skb; | |
461 | spin_unlock_irqrestore(&st_data->lock, flags); | |
462 | break; | |
463 | } | |
464 | kfree_skb(skb); | |
465 | spin_unlock_irqrestore(&st_data->lock, flags); | |
466 | } | |
467 | /* if wake-up is set in another context- restart sending */ | |
468 | } while (test_bit(ST_TX_WAKEUP, &st_data->tx_state)); | |
469 | ||
470 | /* clear flag sending */ | |
471 | clear_bit(ST_TX_SENDING, &st_data->tx_state); | |
472 | } | |
473 | ||
474 | /********************************************************************/ | |
475 | /* functions called from ST KIM | |
476 | */ | |
c1afac15 | 477 | void kim_st_list_protocols(struct st_data_s *st_gdata, void *buf) |
53618cc1 | 478 | { |
c1afac15 | 479 | seq_printf(buf, "[%d]\nBT=%c\nFM=%c\nGPS=%c\n", |
36e574fe | 480 | st_gdata->protos_registered, |
764b0c4b PS |
481 | st_gdata->is_registered[0x04] == true ? 'R' : 'U', |
482 | st_gdata->is_registered[0x08] == true ? 'R' : 'U', | |
483 | st_gdata->is_registered[0x09] == true ? 'R' : 'U'); | |
53618cc1 PS |
484 | } |
485 | ||
486 | /********************************************************************/ | |
487 | /* | |
488 | * functions called from protocol stack drivers | |
489 | * to be EXPORT-ed | |
490 | */ | |
491 | long st_register(struct st_proto_s *new_proto) | |
492 | { | |
493 | struct st_data_s *st_gdata; | |
320920cb | 494 | long err = 0; |
53618cc1 PS |
495 | unsigned long flags = 0; |
496 | ||
dbd3a870 | 497 | st_kim_ref(&st_gdata, 0); |
5c88b021 | 498 | pr_info("%s(%d) ", __func__, new_proto->chnl_id); |
53618cc1 PS |
499 | if (st_gdata == NULL || new_proto == NULL || new_proto->recv == NULL |
500 | || new_proto->reg_complete_cb == NULL) { | |
501 | pr_err("gdata/new_proto/recv or reg_complete_cb not ready"); | |
70442664 | 502 | return -EINVAL; |
53618cc1 PS |
503 | } |
504 | ||
5c88b021 PS |
505 | if (new_proto->chnl_id >= ST_MAX_CHANNELS) { |
506 | pr_err("chnl_id %d not supported", new_proto->chnl_id); | |
320920cb | 507 | return -EPROTONOSUPPORT; |
53618cc1 PS |
508 | } |
509 | ||
764b0c4b | 510 | if (st_gdata->is_registered[new_proto->chnl_id] == true) { |
5c88b021 | 511 | pr_err("chnl_id %d already registered", new_proto->chnl_id); |
320920cb | 512 | return -EALREADY; |
53618cc1 PS |
513 | } |
514 | ||
515 | /* can be from process context only */ | |
516 | spin_lock_irqsave(&st_gdata->lock, flags); | |
517 | ||
518 | if (test_bit(ST_REG_IN_PROGRESS, &st_gdata->st_state)) { | |
5c88b021 | 519 | pr_info(" ST_REG_IN_PROGRESS:%d ", new_proto->chnl_id); |
53618cc1 | 520 | /* fw download in progress */ |
53618cc1 | 521 | |
5c88b021 | 522 | add_channel_to_table(st_gdata, new_proto); |
36e574fe | 523 | st_gdata->protos_registered++; |
53618cc1 PS |
524 | new_proto->write = st_write; |
525 | ||
526 | set_bit(ST_REG_PENDING, &st_gdata->st_state); | |
527 | spin_unlock_irqrestore(&st_gdata->lock, flags); | |
320920cb | 528 | return -EINPROGRESS; |
53618cc1 | 529 | } else if (st_gdata->protos_registered == ST_EMPTY) { |
5c88b021 | 530 | pr_info(" chnl_id list empty :%d ", new_proto->chnl_id); |
53618cc1 PS |
531 | set_bit(ST_REG_IN_PROGRESS, &st_gdata->st_state); |
532 | st_recv = st_kim_recv; | |
533 | ||
534 | /* release lock previously held - re-locked below */ | |
535 | spin_unlock_irqrestore(&st_gdata->lock, flags); | |
536 | ||
537 | /* enable the ST LL - to set default chip state */ | |
538 | st_ll_enable(st_gdata); | |
539 | /* this may take a while to complete | |
540 | * since it involves BT fw download | |
541 | */ | |
38d9df49 | 542 | err = st_kim_start(st_gdata->kim_data); |
320920cb | 543 | if (err != 0) { |
53618cc1 PS |
544 | clear_bit(ST_REG_IN_PROGRESS, &st_gdata->st_state); |
545 | if ((st_gdata->protos_registered != ST_EMPTY) && | |
546 | (test_bit(ST_REG_PENDING, &st_gdata->st_state))) { | |
547 | pr_err(" KIM failure complete callback "); | |
70442664 | 548 | st_reg_complete(st_gdata, err); |
53618cc1 | 549 | } |
70442664 | 550 | return -EINVAL; |
53618cc1 PS |
551 | } |
552 | ||
53618cc1 PS |
553 | clear_bit(ST_REG_IN_PROGRESS, &st_gdata->st_state); |
554 | st_recv = st_int_recv; | |
555 | ||
556 | /* this is where all pending registration | |
557 | * are signalled to be complete by calling callback functions | |
558 | */ | |
559 | if ((st_gdata->protos_registered != ST_EMPTY) && | |
560 | (test_bit(ST_REG_PENDING, &st_gdata->st_state))) { | |
e6d9e64e | 561 | pr_debug(" call reg complete callback "); |
320920cb | 562 | st_reg_complete(st_gdata, 0); |
53618cc1 PS |
563 | } |
564 | clear_bit(ST_REG_PENDING, &st_gdata->st_state); | |
565 | ||
566 | /* check for already registered once more, | |
567 | * since the above check is old | |
568 | */ | |
764b0c4b | 569 | if (st_gdata->is_registered[new_proto->chnl_id] == true) { |
53618cc1 | 570 | pr_err(" proto %d already registered ", |
5c88b021 | 571 | new_proto->chnl_id); |
320920cb | 572 | return -EALREADY; |
53618cc1 PS |
573 | } |
574 | ||
575 | spin_lock_irqsave(&st_gdata->lock, flags); | |
5c88b021 | 576 | add_channel_to_table(st_gdata, new_proto); |
36e574fe | 577 | st_gdata->protos_registered++; |
53618cc1 PS |
578 | new_proto->write = st_write; |
579 | spin_unlock_irqrestore(&st_gdata->lock, flags); | |
580 | return err; | |
581 | } | |
582 | /* if fw is already downloaded & new stack registers protocol */ | |
583 | else { | |
5c88b021 | 584 | add_channel_to_table(st_gdata, new_proto); |
36e574fe | 585 | st_gdata->protos_registered++; |
53618cc1 PS |
586 | new_proto->write = st_write; |
587 | ||
588 | /* lock already held before entering else */ | |
589 | spin_unlock_irqrestore(&st_gdata->lock, flags); | |
590 | return err; | |
591 | } | |
5c88b021 | 592 | pr_debug("done %s(%d) ", __func__, new_proto->chnl_id); |
53618cc1 PS |
593 | } |
594 | EXPORT_SYMBOL_GPL(st_register); | |
595 | ||
596 | /* to unregister a protocol - | |
597 | * to be called from protocol stack driver | |
598 | */ | |
5c88b021 | 599 | long st_unregister(struct st_proto_s *proto) |
53618cc1 | 600 | { |
320920cb | 601 | long err = 0; |
53618cc1 PS |
602 | unsigned long flags = 0; |
603 | struct st_data_s *st_gdata; | |
604 | ||
5c88b021 | 605 | pr_debug("%s: %d ", __func__, proto->chnl_id); |
53618cc1 | 606 | |
dbd3a870 | 607 | st_kim_ref(&st_gdata, 0); |
5c88b021 PS |
608 | if (proto->chnl_id >= ST_MAX_CHANNELS) { |
609 | pr_err(" chnl_id %d not supported", proto->chnl_id); | |
320920cb | 610 | return -EPROTONOSUPPORT; |
53618cc1 PS |
611 | } |
612 | ||
613 | spin_lock_irqsave(&st_gdata->lock, flags); | |
614 | ||
5c88b021 PS |
615 | if (st_gdata->list[proto->chnl_id] == NULL) { |
616 | pr_err(" chnl_id %d not registered", proto->chnl_id); | |
53618cc1 | 617 | spin_unlock_irqrestore(&st_gdata->lock, flags); |
320920cb | 618 | return -EPROTONOSUPPORT; |
53618cc1 PS |
619 | } |
620 | ||
621 | st_gdata->protos_registered--; | |
5c88b021 | 622 | remove_channel_from_table(st_gdata, proto); |
53618cc1 PS |
623 | spin_unlock_irqrestore(&st_gdata->lock, flags); |
624 | ||
625 | if ((st_gdata->protos_registered == ST_EMPTY) && | |
626 | (!test_bit(ST_REG_PENDING, &st_gdata->st_state))) { | |
5c88b021 | 627 | pr_info(" all chnl_ids unregistered "); |
53618cc1 PS |
628 | |
629 | /* stop traffic on tty */ | |
630 | if (st_gdata->tty) { | |
631 | tty_ldisc_flush(st_gdata->tty); | |
632 | stop_tty(st_gdata->tty); | |
633 | } | |
634 | ||
5c88b021 | 635 | /* all chnl_ids now unregistered */ |
38d9df49 | 636 | st_kim_stop(st_gdata->kim_data); |
53618cc1 PS |
637 | /* disable ST LL */ |
638 | st_ll_disable(st_gdata); | |
639 | } | |
640 | return err; | |
641 | } | |
642 | ||
643 | /* | |
644 | * called in protocol stack drivers | |
645 | * via the write function pointer | |
646 | */ | |
647 | long st_write(struct sk_buff *skb) | |
648 | { | |
649 | struct st_data_s *st_gdata; | |
53618cc1 PS |
650 | long len; |
651 | ||
dbd3a870 | 652 | st_kim_ref(&st_gdata, 0); |
53618cc1 PS |
653 | if (unlikely(skb == NULL || st_gdata == NULL |
654 | || st_gdata->tty == NULL)) { | |
655 | pr_err("data/tty unavailable to perform write"); | |
70442664 | 656 | return -EINVAL; |
53618cc1 | 657 | } |
c1605f2e | 658 | |
e6d9e64e | 659 | pr_debug("%d to be written", skb->len); |
53618cc1 PS |
660 | len = skb->len; |
661 | ||
662 | /* st_ll to decide where to enqueue the skb */ | |
663 | st_int_enqueue(st_gdata, skb); | |
664 | /* wake up */ | |
665 | st_tx_wakeup(st_gdata); | |
666 | ||
667 | /* return number of bytes written */ | |
668 | return len; | |
669 | } | |
670 | ||
671 | /* for protocols making use of shared transport */ | |
672 | EXPORT_SYMBOL_GPL(st_unregister); | |
673 | ||
674 | /********************************************************************/ | |
675 | /* | |
676 | * functions called from TTY layer | |
677 | */ | |
678 | static int st_tty_open(struct tty_struct *tty) | |
679 | { | |
320920cb | 680 | int err = 0; |
53618cc1 PS |
681 | struct st_data_s *st_gdata; |
682 | pr_info("%s ", __func__); | |
683 | ||
dbd3a870 | 684 | st_kim_ref(&st_gdata, 0); |
53618cc1 PS |
685 | st_gdata->tty = tty; |
686 | tty->disc_data = st_gdata; | |
687 | ||
688 | /* don't do an wakeup for now */ | |
689 | clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags); | |
690 | ||
691 | /* mem already allocated | |
692 | */ | |
693 | tty->receive_room = 65536; | |
694 | /* Flush any pending characters in the driver and discipline. */ | |
695 | tty_ldisc_flush(tty); | |
696 | tty_driver_flush_buffer(tty); | |
697 | /* | |
698 | * signal to UIM via KIM that - | |
699 | * installation of N_TI_WL ldisc is complete | |
700 | */ | |
38d9df49 | 701 | st_kim_complete(st_gdata->kim_data); |
e6d9e64e | 702 | pr_debug("done %s", __func__); |
53618cc1 PS |
703 | return err; |
704 | } | |
705 | ||
706 | static void st_tty_close(struct tty_struct *tty) | |
707 | { | |
5c88b021 | 708 | unsigned char i = ST_MAX_CHANNELS; |
53618cc1 PS |
709 | unsigned long flags = 0; |
710 | struct st_data_s *st_gdata = tty->disc_data; | |
711 | ||
712 | pr_info("%s ", __func__); | |
713 | ||
714 | /* TODO: | |
715 | * if a protocol has been registered & line discipline | |
716 | * un-installed for some reason - what should be done ? | |
717 | */ | |
718 | spin_lock_irqsave(&st_gdata->lock, flags); | |
5c88b021 | 719 | for (i = ST_BT; i < ST_MAX_CHANNELS; i++) { |
53618cc1 PS |
720 | if (st_gdata->list[i] != NULL) |
721 | pr_err("%d not un-registered", i); | |
722 | st_gdata->list[i] = NULL; | |
723 | } | |
bb8f3c06 | 724 | st_gdata->protos_registered = 0; |
53618cc1 PS |
725 | spin_unlock_irqrestore(&st_gdata->lock, flags); |
726 | /* | |
727 | * signal to UIM via KIM that - | |
728 | * N_TI_WL ldisc is un-installed | |
729 | */ | |
38d9df49 | 730 | st_kim_complete(st_gdata->kim_data); |
53618cc1 PS |
731 | st_gdata->tty = NULL; |
732 | /* Flush any pending characters in the driver and discipline. */ | |
733 | tty_ldisc_flush(tty); | |
734 | tty_driver_flush_buffer(tty); | |
735 | ||
736 | spin_lock_irqsave(&st_gdata->lock, flags); | |
737 | /* empty out txq and tx_waitq */ | |
738 | skb_queue_purge(&st_gdata->txq); | |
739 | skb_queue_purge(&st_gdata->tx_waitq); | |
740 | /* reset the TTY Rx states of ST */ | |
741 | st_gdata->rx_count = 0; | |
742 | st_gdata->rx_state = ST_W4_PACKET_TYPE; | |
743 | kfree_skb(st_gdata->rx_skb); | |
744 | st_gdata->rx_skb = NULL; | |
745 | spin_unlock_irqrestore(&st_gdata->lock, flags); | |
746 | ||
e6d9e64e | 747 | pr_debug("%s: done ", __func__); |
53618cc1 PS |
748 | } |
749 | ||
55db4c64 LT |
750 | static void st_tty_receive(struct tty_struct *tty, const unsigned char *data, |
751 | char *tty_flags, int count) | |
53618cc1 | 752 | { |
53618cc1 | 753 | #ifdef VERBOSE |
e6d9e64e PS |
754 | print_hex_dump(KERN_DEBUG, ">in>", DUMP_PREFIX_NONE, |
755 | 16, 1, data, count, 0); | |
53618cc1 PS |
756 | #endif |
757 | ||
758 | /* | |
759 | * if fw download is in progress then route incoming data | |
760 | * to KIM for validation | |
761 | */ | |
762 | st_recv(tty->disc_data, data, count); | |
e6d9e64e | 763 | pr_debug("done %s", __func__); |
53618cc1 PS |
764 | } |
765 | ||
766 | /* wake-up function called in from the TTY layer | |
767 | * inside the internal wakeup function will be called | |
768 | */ | |
769 | static void st_tty_wakeup(struct tty_struct *tty) | |
770 | { | |
771 | struct st_data_s *st_gdata = tty->disc_data; | |
e6d9e64e | 772 | pr_debug("%s ", __func__); |
53618cc1 PS |
773 | /* don't do an wakeup for now */ |
774 | clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags); | |
775 | ||
776 | /* call our internal wakeup */ | |
777 | st_tx_wakeup((void *)st_gdata); | |
778 | } | |
779 | ||
780 | static void st_tty_flush_buffer(struct tty_struct *tty) | |
781 | { | |
782 | struct st_data_s *st_gdata = tty->disc_data; | |
e6d9e64e | 783 | pr_debug("%s ", __func__); |
53618cc1 PS |
784 | |
785 | kfree_skb(st_gdata->tx_skb); | |
786 | st_gdata->tx_skb = NULL; | |
787 | ||
788 | tty->ops->flush_buffer(tty); | |
789 | return; | |
790 | } | |
791 | ||
73f12e8d PS |
792 | static struct tty_ldisc_ops st_ldisc_ops = { |
793 | .magic = TTY_LDISC_MAGIC, | |
794 | .name = "n_st", | |
795 | .open = st_tty_open, | |
796 | .close = st_tty_close, | |
797 | .receive_buf = st_tty_receive, | |
798 | .write_wakeup = st_tty_wakeup, | |
799 | .flush_buffer = st_tty_flush_buffer, | |
800 | .owner = THIS_MODULE | |
801 | }; | |
802 | ||
53618cc1 PS |
803 | /********************************************************************/ |
804 | int st_core_init(struct st_data_s **core_data) | |
805 | { | |
806 | struct st_data_s *st_gdata; | |
807 | long err; | |
53618cc1 | 808 | |
73f12e8d | 809 | err = tty_register_ldisc(N_TI_WL, &st_ldisc_ops); |
53618cc1 PS |
810 | if (err) { |
811 | pr_err("error registering %d line discipline %ld", | |
812 | N_TI_WL, err); | |
53618cc1 PS |
813 | return err; |
814 | } | |
e6d9e64e | 815 | pr_debug("registered n_shared line discipline"); |
53618cc1 PS |
816 | |
817 | st_gdata = kzalloc(sizeof(struct st_data_s), GFP_KERNEL); | |
818 | if (!st_gdata) { | |
819 | pr_err("memory allocation failed"); | |
820 | err = tty_unregister_ldisc(N_TI_WL); | |
821 | if (err) | |
822 | pr_err("unable to un-register ldisc %ld", err); | |
53618cc1 PS |
823 | err = -ENOMEM; |
824 | return err; | |
825 | } | |
826 | ||
827 | /* Initialize ST TxQ and Tx waitQ queue head. All BT/FM/GPS module skb's | |
828 | * will be pushed in this queue for actual transmission. | |
829 | */ | |
830 | skb_queue_head_init(&st_gdata->txq); | |
831 | skb_queue_head_init(&st_gdata->tx_waitq); | |
832 | ||
833 | /* Locking used in st_int_enqueue() to avoid multiple execution */ | |
834 | spin_lock_init(&st_gdata->lock); | |
835 | ||
53618cc1 PS |
836 | err = st_ll_init(st_gdata); |
837 | if (err) { | |
838 | pr_err("error during st_ll initialization(%ld)", err); | |
839 | kfree(st_gdata); | |
840 | err = tty_unregister_ldisc(N_TI_WL); | |
841 | if (err) | |
842 | pr_err("unable to un-register ldisc"); | |
70442664 | 843 | return err; |
53618cc1 PS |
844 | } |
845 | *core_data = st_gdata; | |
846 | return 0; | |
847 | } | |
848 | ||
849 | void st_core_exit(struct st_data_s *st_gdata) | |
850 | { | |
851 | long err; | |
852 | /* internal module cleanup */ | |
853 | err = st_ll_deinit(st_gdata); | |
854 | if (err) | |
855 | pr_err("error during deinit of ST LL %ld", err); | |
73f12e8d | 856 | |
53618cc1 PS |
857 | if (st_gdata != NULL) { |
858 | /* Free ST Tx Qs and skbs */ | |
859 | skb_queue_purge(&st_gdata->txq); | |
860 | skb_queue_purge(&st_gdata->tx_waitq); | |
861 | kfree_skb(st_gdata->rx_skb); | |
862 | kfree_skb(st_gdata->tx_skb); | |
863 | /* TTY ldisc cleanup */ | |
864 | err = tty_unregister_ldisc(N_TI_WL); | |
865 | if (err) | |
866 | pr_err("unable to un-register ldisc %ld", err); | |
53618cc1 PS |
867 | /* free the global data pointer */ |
868 | kfree(st_gdata); | |
869 | } | |
870 | } | |
871 | ||
872 |