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