diff options
Diffstat (limited to 'net/nfc/llcp/llcp.c')
-rw-r--r-- | net/nfc/llcp/llcp.c | 241 |
1 files changed, 164 insertions, 77 deletions
diff --git a/net/nfc/llcp/llcp.c b/net/nfc/llcp/llcp.c index 9ab17ec50ce7..6094a2099bd6 100644 --- a/net/nfc/llcp/llcp.c +++ b/net/nfc/llcp/llcp.c | |||
@@ -131,6 +131,44 @@ int nfc_llcp_local_put(struct nfc_llcp_local *local) | |||
131 | return kref_put(&local->ref, local_release); | 131 | return kref_put(&local->ref, local_release); |
132 | } | 132 | } |
133 | 133 | ||
134 | static struct nfc_llcp_sock *nfc_llcp_sock_get(struct nfc_llcp_local *local, | ||
135 | u8 ssap, u8 dsap) | ||
136 | { | ||
137 | struct sock *sk; | ||
138 | struct hlist_node *node; | ||
139 | struct nfc_llcp_sock *llcp_sock; | ||
140 | |||
141 | pr_debug("ssap dsap %d %d\n", ssap, dsap); | ||
142 | |||
143 | if (ssap == 0 && dsap == 0) | ||
144 | return NULL; | ||
145 | |||
146 | read_lock(&local->sockets.lock); | ||
147 | |||
148 | llcp_sock = NULL; | ||
149 | |||
150 | sk_for_each(sk, node, &local->sockets.head) { | ||
151 | llcp_sock = nfc_llcp_sock(sk); | ||
152 | |||
153 | if (llcp_sock->ssap == ssap && llcp_sock->dsap == dsap) | ||
154 | break; | ||
155 | } | ||
156 | |||
157 | read_unlock(&local->sockets.lock); | ||
158 | |||
159 | if (llcp_sock == NULL) | ||
160 | return NULL; | ||
161 | |||
162 | sock_hold(&llcp_sock->sk); | ||
163 | |||
164 | return llcp_sock; | ||
165 | } | ||
166 | |||
167 | static void nfc_llcp_sock_put(struct nfc_llcp_sock *sock) | ||
168 | { | ||
169 | sock_put(&sock->sk); | ||
170 | } | ||
171 | |||
134 | static void nfc_llcp_timeout_work(struct work_struct *work) | 172 | static void nfc_llcp_timeout_work(struct work_struct *work) |
135 | { | 173 | { |
136 | struct nfc_llcp_local *local = container_of(work, struct nfc_llcp_local, | 174 | struct nfc_llcp_local *local = container_of(work, struct nfc_llcp_local, |
@@ -191,6 +229,51 @@ static int nfc_llcp_wks_sap(char *service_name, size_t service_name_len) | |||
191 | return -EINVAL; | 229 | return -EINVAL; |
192 | } | 230 | } |
193 | 231 | ||
232 | static | ||
233 | struct nfc_llcp_sock *nfc_llcp_sock_from_sn(struct nfc_llcp_local *local, | ||
234 | u8 *sn, size_t sn_len) | ||
235 | { | ||
236 | struct sock *sk; | ||
237 | struct hlist_node *node; | ||
238 | struct nfc_llcp_sock *llcp_sock, *tmp_sock; | ||
239 | |||
240 | pr_debug("sn %zd %p\n", sn_len, sn); | ||
241 | |||
242 | if (sn == NULL || sn_len == 0) | ||
243 | return NULL; | ||
244 | |||
245 | read_lock(&local->sockets.lock); | ||
246 | |||
247 | llcp_sock = NULL; | ||
248 | |||
249 | sk_for_each(sk, node, &local->sockets.head) { | ||
250 | tmp_sock = nfc_llcp_sock(sk); | ||
251 | |||
252 | pr_debug("llcp sock %p\n", tmp_sock); | ||
253 | |||
254 | if (tmp_sock->sk.sk_state != LLCP_LISTEN) | ||
255 | continue; | ||
256 | |||
257 | if (tmp_sock->service_name == NULL || | ||
258 | tmp_sock->service_name_len == 0) | ||
259 | continue; | ||
260 | |||
261 | if (tmp_sock->service_name_len != sn_len) | ||
262 | continue; | ||
263 | |||
264 | if (memcmp(sn, tmp_sock->service_name, sn_len) == 0) { | ||
265 | llcp_sock = tmp_sock; | ||
266 | break; | ||
267 | } | ||
268 | } | ||
269 | |||
270 | read_unlock(&local->sockets.lock); | ||
271 | |||
272 | pr_debug("Found llcp sock %p\n", llcp_sock); | ||
273 | |||
274 | return llcp_sock; | ||
275 | } | ||
276 | |||
194 | u8 nfc_llcp_get_sdp_ssap(struct nfc_llcp_local *local, | 277 | u8 nfc_llcp_get_sdp_ssap(struct nfc_llcp_local *local, |
195 | struct nfc_llcp_sock *sock) | 278 | struct nfc_llcp_sock *sock) |
196 | { | 279 | { |
@@ -217,22 +300,19 @@ u8 nfc_llcp_get_sdp_ssap(struct nfc_llcp_local *local, | |||
217 | } | 300 | } |
218 | 301 | ||
219 | /* | 302 | /* |
220 | * This is not a well known service, | 303 | * Check if there already is a non WKS socket bound |
221 | * we should try to find a local SDP free spot | 304 | * to this service name. |
222 | */ | 305 | */ |
223 | ssap = find_first_zero_bit(&local->local_sdp, LLCP_SDP_NUM_SAP); | 306 | if (nfc_llcp_sock_from_sn(local, sock->service_name, |
224 | if (ssap == LLCP_SDP_NUM_SAP) { | 307 | sock->service_name_len) != NULL) { |
225 | mutex_unlock(&local->sdp_lock); | 308 | mutex_unlock(&local->sdp_lock); |
226 | 309 | ||
227 | return LLCP_SAP_MAX; | 310 | return LLCP_SAP_MAX; |
228 | } | 311 | } |
229 | 312 | ||
230 | pr_debug("SDP ssap %d\n", LLCP_WKS_NUM_SAP + ssap); | ||
231 | |||
232 | set_bit(ssap, &local->local_sdp); | ||
233 | mutex_unlock(&local->sdp_lock); | 313 | mutex_unlock(&local->sdp_lock); |
234 | 314 | ||
235 | return LLCP_WKS_NUM_SAP + ssap; | 315 | return LLCP_SDP_UNBOUND; |
236 | 316 | ||
237 | } else if (sock->ssap != 0 && sock->ssap < LLCP_WKS_NUM_SAP) { | 317 | } else if (sock->ssap != 0 && sock->ssap < LLCP_WKS_NUM_SAP) { |
238 | if (!test_bit(sock->ssap, &local->local_wks)) { | 318 | if (!test_bit(sock->ssap, &local->local_wks)) { |
@@ -276,8 +356,34 @@ void nfc_llcp_put_ssap(struct nfc_llcp_local *local, u8 ssap) | |||
276 | local_ssap = ssap; | 356 | local_ssap = ssap; |
277 | sdp = &local->local_wks; | 357 | sdp = &local->local_wks; |
278 | } else if (ssap < LLCP_LOCAL_NUM_SAP) { | 358 | } else if (ssap < LLCP_LOCAL_NUM_SAP) { |
359 | atomic_t *client_cnt; | ||
360 | |||
279 | local_ssap = ssap - LLCP_WKS_NUM_SAP; | 361 | local_ssap = ssap - LLCP_WKS_NUM_SAP; |
280 | sdp = &local->local_sdp; | 362 | sdp = &local->local_sdp; |
363 | client_cnt = &local->local_sdp_cnt[local_ssap]; | ||
364 | |||
365 | pr_debug("%d clients\n", atomic_read(client_cnt)); | ||
366 | |||
367 | mutex_lock(&local->sdp_lock); | ||
368 | |||
369 | if (atomic_dec_and_test(client_cnt)) { | ||
370 | struct nfc_llcp_sock *l_sock; | ||
371 | |||
372 | pr_debug("No more clients for SAP %d\n", ssap); | ||
373 | |||
374 | clear_bit(local_ssap, sdp); | ||
375 | |||
376 | /* Find the listening sock and set it back to UNBOUND */ | ||
377 | l_sock = nfc_llcp_sock_get(local, ssap, LLCP_SAP_SDP); | ||
378 | if (l_sock) { | ||
379 | l_sock->ssap = LLCP_SDP_UNBOUND; | ||
380 | nfc_llcp_sock_put(l_sock); | ||
381 | } | ||
382 | } | ||
383 | |||
384 | mutex_unlock(&local->sdp_lock); | ||
385 | |||
386 | return; | ||
281 | } else if (ssap < LLCP_MAX_SAP) { | 387 | } else if (ssap < LLCP_MAX_SAP) { |
282 | local_ssap = ssap - LLCP_LOCAL_NUM_SAP; | 388 | local_ssap = ssap - LLCP_LOCAL_NUM_SAP; |
283 | sdp = &local->local_sap; | 389 | sdp = &local->local_sap; |
@@ -292,6 +398,28 @@ void nfc_llcp_put_ssap(struct nfc_llcp_local *local, u8 ssap) | |||
292 | mutex_unlock(&local->sdp_lock); | 398 | mutex_unlock(&local->sdp_lock); |
293 | } | 399 | } |
294 | 400 | ||
401 | static u8 nfc_llcp_reserve_sdp_ssap(struct nfc_llcp_local *local) | ||
402 | { | ||
403 | u8 ssap; | ||
404 | |||
405 | mutex_lock(&local->sdp_lock); | ||
406 | |||
407 | ssap = find_first_zero_bit(&local->local_sdp, LLCP_SDP_NUM_SAP); | ||
408 | if (ssap == LLCP_SDP_NUM_SAP) { | ||
409 | mutex_unlock(&local->sdp_lock); | ||
410 | |||
411 | return LLCP_SAP_MAX; | ||
412 | } | ||
413 | |||
414 | pr_debug("SDP ssap %d\n", LLCP_WKS_NUM_SAP + ssap); | ||
415 | |||
416 | set_bit(ssap, &local->local_sdp); | ||
417 | |||
418 | mutex_unlock(&local->sdp_lock); | ||
419 | |||
420 | return LLCP_WKS_NUM_SAP + ssap; | ||
421 | } | ||
422 | |||
295 | static int nfc_llcp_build_gb(struct nfc_llcp_local *local) | 423 | static int nfc_llcp_build_gb(struct nfc_llcp_local *local) |
296 | { | 424 | { |
297 | u8 *gb_cur, *version_tlv, version, version_length; | 425 | u8 *gb_cur, *version_tlv, version, version_length; |
@@ -493,74 +621,12 @@ out: | |||
493 | return llcp_sock; | 621 | return llcp_sock; |
494 | } | 622 | } |
495 | 623 | ||
496 | static struct nfc_llcp_sock *nfc_llcp_sock_get(struct nfc_llcp_local *local, | ||
497 | u8 ssap, u8 dsap) | ||
498 | { | ||
499 | struct sock *sk; | ||
500 | struct hlist_node *node; | ||
501 | struct nfc_llcp_sock *llcp_sock; | ||
502 | |||
503 | pr_debug("ssap dsap %d %d\n", ssap, dsap); | ||
504 | |||
505 | if (ssap == 0 && dsap == 0) | ||
506 | return NULL; | ||
507 | |||
508 | read_lock(&local->sockets.lock); | ||
509 | |||
510 | llcp_sock = NULL; | ||
511 | |||
512 | sk_for_each(sk, node, &local->sockets.head) { | ||
513 | llcp_sock = nfc_llcp_sock(sk); | ||
514 | |||
515 | if (llcp_sock->ssap == ssap && | ||
516 | llcp_sock->dsap == dsap) | ||
517 | break; | ||
518 | } | ||
519 | |||
520 | read_unlock(&local->sockets.lock); | ||
521 | |||
522 | if (llcp_sock == NULL) | ||
523 | return NULL; | ||
524 | |||
525 | sock_hold(&llcp_sock->sk); | ||
526 | |||
527 | return llcp_sock; | ||
528 | } | ||
529 | |||
530 | static struct nfc_llcp_sock *nfc_llcp_sock_get_sn(struct nfc_llcp_local *local, | 624 | static struct nfc_llcp_sock *nfc_llcp_sock_get_sn(struct nfc_llcp_local *local, |
531 | u8 *sn, size_t sn_len) | 625 | u8 *sn, size_t sn_len) |
532 | { | 626 | { |
533 | struct sock *sk; | ||
534 | struct hlist_node *node; | ||
535 | struct nfc_llcp_sock *llcp_sock; | 627 | struct nfc_llcp_sock *llcp_sock; |
536 | 628 | ||
537 | pr_debug("sn %zd\n", sn_len); | 629 | llcp_sock = nfc_llcp_sock_from_sn(local, sn, sn_len); |
538 | |||
539 | if (sn == NULL || sn_len == 0) | ||
540 | return NULL; | ||
541 | |||
542 | read_lock(&local->sockets.lock); | ||
543 | |||
544 | llcp_sock = NULL; | ||
545 | |||
546 | sk_for_each(sk, node, &local->sockets.head) { | ||
547 | llcp_sock = nfc_llcp_sock(sk); | ||
548 | |||
549 | if (llcp_sock->sk.sk_state != LLCP_LISTEN) | ||
550 | continue; | ||
551 | |||
552 | if (llcp_sock->service_name == NULL || | ||
553 | llcp_sock->service_name_len == 0) | ||
554 | continue; | ||
555 | |||
556 | if (llcp_sock->service_name_len != sn_len) | ||
557 | continue; | ||
558 | |||
559 | if (memcmp(sn, llcp_sock->service_name, sn_len) == 0) | ||
560 | break; | ||
561 | } | ||
562 | |||
563 | read_unlock(&local->sockets.lock); | ||
564 | 630 | ||
565 | if (llcp_sock == NULL) | 631 | if (llcp_sock == NULL) |
566 | return NULL; | 632 | return NULL; |
@@ -570,11 +636,6 @@ static struct nfc_llcp_sock *nfc_llcp_sock_get_sn(struct nfc_llcp_local *local, | |||
570 | return llcp_sock; | 636 | return llcp_sock; |
571 | } | 637 | } |
572 | 638 | ||
573 | static void nfc_llcp_sock_put(struct nfc_llcp_sock *sock) | ||
574 | { | ||
575 | sock_put(&sock->sk); | ||
576 | } | ||
577 | |||
578 | static u8 *nfc_llcp_connect_sn(struct sk_buff *skb, size_t *sn_len) | 639 | static u8 *nfc_llcp_connect_sn(struct sk_buff *skb, size_t *sn_len) |
579 | { | 640 | { |
580 | u8 *tlv = &skb->data[2], type, length; | 641 | u8 *tlv = &skb->data[2], type, length; |
@@ -646,6 +707,21 @@ static void nfc_llcp_recv_connect(struct nfc_llcp_local *local, | |||
646 | goto fail; | 707 | goto fail; |
647 | } | 708 | } |
648 | 709 | ||
710 | if (sock->ssap == LLCP_SDP_UNBOUND) { | ||
711 | u8 ssap = nfc_llcp_reserve_sdp_ssap(local); | ||
712 | |||
713 | pr_debug("First client, reserving %d\n", ssap); | ||
714 | |||
715 | if (ssap == LLCP_SAP_MAX) { | ||
716 | reason = LLCP_DM_REJ; | ||
717 | release_sock(&sock->sk); | ||
718 | sock_put(&sock->sk); | ||
719 | goto fail; | ||
720 | } | ||
721 | |||
722 | sock->ssap = ssap; | ||
723 | } | ||
724 | |||
649 | new_sk = nfc_llcp_sock_alloc(NULL, parent->sk_type, GFP_ATOMIC); | 725 | new_sk = nfc_llcp_sock_alloc(NULL, parent->sk_type, GFP_ATOMIC); |
650 | if (new_sk == NULL) { | 726 | if (new_sk == NULL) { |
651 | reason = LLCP_DM_REJ; | 727 | reason = LLCP_DM_REJ; |
@@ -659,10 +735,21 @@ static void nfc_llcp_recv_connect(struct nfc_llcp_local *local, | |||
659 | new_sock->local = nfc_llcp_local_get(local); | 735 | new_sock->local = nfc_llcp_local_get(local); |
660 | new_sock->miu = local->remote_miu; | 736 | new_sock->miu = local->remote_miu; |
661 | new_sock->nfc_protocol = sock->nfc_protocol; | 737 | new_sock->nfc_protocol = sock->nfc_protocol; |
662 | new_sock->ssap = sock->ssap; | ||
663 | new_sock->dsap = ssap; | 738 | new_sock->dsap = ssap; |
664 | new_sock->target_idx = local->target_idx; | 739 | new_sock->target_idx = local->target_idx; |
665 | new_sock->parent = parent; | 740 | new_sock->parent = parent; |
741 | new_sock->ssap = sock->ssap; | ||
742 | if (sock->ssap < LLCP_LOCAL_NUM_SAP && sock->ssap >= LLCP_WKS_NUM_SAP) { | ||
743 | atomic_t *client_count; | ||
744 | |||
745 | pr_debug("reserved_ssap %d for %p\n", sock->ssap, new_sock); | ||
746 | |||
747 | client_count = | ||
748 | &local->local_sdp_cnt[sock->ssap - LLCP_WKS_NUM_SAP]; | ||
749 | |||
750 | atomic_inc(client_count); | ||
751 | new_sock->reserved_ssap = sock->ssap; | ||
752 | } | ||
666 | 753 | ||
667 | nfc_llcp_parse_connection_tlv(new_sock, &skb->data[LLCP_HEADER_SIZE], | 754 | nfc_llcp_parse_connection_tlv(new_sock, &skb->data[LLCP_HEADER_SIZE], |
668 | skb->len - LLCP_HEADER_SIZE); | 755 | skb->len - LLCP_HEADER_SIZE); |