aboutsummaryrefslogtreecommitdiffstats
path: root/net/nfc/llcp/llcp.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/nfc/llcp/llcp.c')
-rw-r--r--net/nfc/llcp/llcp.c241
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
134static 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
167static void nfc_llcp_sock_put(struct nfc_llcp_sock *sock)
168{
169 sock_put(&sock->sk);
170}
171
134static void nfc_llcp_timeout_work(struct work_struct *work) 172static 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
232static
233struct 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
194u8 nfc_llcp_get_sdp_ssap(struct nfc_llcp_local *local, 277u8 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
401static 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
295static int nfc_llcp_build_gb(struct nfc_llcp_local *local) 423static 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
496static 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
530static struct nfc_llcp_sock *nfc_llcp_sock_get_sn(struct nfc_llcp_local *local, 624static 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
573static void nfc_llcp_sock_put(struct nfc_llcp_sock *sock)
574{
575 sock_put(&sock->sk);
576}
577
578static u8 *nfc_llcp_connect_sn(struct sk_buff *skb, size_t *sn_len) 639static 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);