diff options
-rw-r--r-- | Documentation/networking/phonet.txt | 53 | ||||
-rw-r--r-- | net/phonet/pep.c | 172 | ||||
-rw-r--r-- | net/phonet/socket.c | 102 |
3 files changed, 165 insertions, 162 deletions
diff --git a/Documentation/networking/phonet.txt b/Documentation/networking/phonet.txt index cacac968c1c3..3d127791cb06 100644 --- a/Documentation/networking/phonet.txt +++ b/Documentation/networking/phonet.txt | |||
@@ -154,9 +154,28 @@ connections, one per accept()'d socket. | |||
154 | write(cfd, msg, msglen); | 154 | write(cfd, msg, msglen); |
155 | } | 155 | } |
156 | 156 | ||
157 | Connections are established between two endpoints by a "third party" | 157 | Connections are traditionally established between two endpoints by a |
158 | application. This means that both endpoints are passive; so connect() | 158 | "third party" application. This means that both endpoints are passive. |
159 | is not possible. | 159 | |
160 | |||
161 | As of Linux kernel version 2.6.39, it is also possible to connect | ||
162 | two endpoints directly, using connect() on the active side. This is | ||
163 | intended to support the newer Nokia Wireless Modem API, as found in | ||
164 | e.g. the Nokia Slim Modem in the ST-Ericsson U8500 platform: | ||
165 | |||
166 | struct sockaddr_spn spn; | ||
167 | int fd; | ||
168 | |||
169 | fd = socket(PF_PHONET, SOCK_SEQPACKET, PN_PROTO_PIPE); | ||
170 | memset(&spn, 0, sizeof(spn)); | ||
171 | spn.spn_family = AF_PHONET; | ||
172 | spn.spn_obj = ...; | ||
173 | spn.spn_dev = ...; | ||
174 | spn.spn_resource = 0xD9; | ||
175 | connect(fd, (struct sockaddr *)&spn, sizeof(spn)); | ||
176 | /* normal I/O here ... */ | ||
177 | close(fd); | ||
178 | |||
160 | 179 | ||
161 | WARNING: | 180 | WARNING: |
162 | When polling a connected pipe socket for writability, there is an | 181 | When polling a connected pipe socket for writability, there is an |
@@ -189,17 +208,8 @@ The pipe protocol provides two socket options at the SOL_PNPIPE level: | |||
189 | Phonet Pipe-controller Implementation | 208 | Phonet Pipe-controller Implementation |
190 | ------------------------------------- | 209 | ------------------------------------- |
191 | 210 | ||
192 | Phonet Pipe-controller is enabled by selecting the CONFIG_PHONET_PIPECTRLR Kconfig | 211 | Phonet Pipe-controller is enabled by selecting the CONFIG_PHONET_PIPECTRLR |
193 | option. It is useful when communicating with those Nokia Modems which do not | 212 | Kconfig option. |
194 | implement Pipe controller in them e.g. Nokia Slim Modem used in ST-Ericsson | ||
195 | U8500 platform. | ||
196 | |||
197 | The implementation is based on the Data Connection Establishment Sequence | ||
198 | depicted in 'Nokia Wireless Modem API - Wireless_modem_user_guide.pdf' | ||
199 | document. | ||
200 | |||
201 | It allows a phonet sequenced socket (host-pep) to initiate a Pipe connection | ||
202 | between itself and a remote pipe-end point (e.g. modem). | ||
203 | 213 | ||
204 | The implementation adds socket options at SOL_PNPIPE level: | 214 | The implementation adds socket options at SOL_PNPIPE level: |
205 | 215 | ||
@@ -207,21 +217,6 @@ The implementation adds socket options at SOL_PNPIPE level: | |||
207 | is disabled. If the value is non-zero, the pipe is enabled. If the pipe | 217 | is disabled. If the value is non-zero, the pipe is enabled. If the pipe |
208 | is not (yet) connected, ENOTCONN is error is returned. | 218 | is not (yet) connected, ENOTCONN is error is returned. |
209 | 219 | ||
210 | The implementation also adds socket 'connect'. On calling the 'connect', pipe | ||
211 | will be created between the source socket and the destination, and the pipe | ||
212 | state will be set to PIPE_DISABLED. | ||
213 | |||
214 | After a pipe has been created and enabled successfully, the Pipe data can be | ||
215 | exchanged between the host-pep and remote-pep (modem). | ||
216 | |||
217 | User-space would typically follow below sequence with Pipe controller:- | ||
218 | -socket | ||
219 | -bind | ||
220 | -setsockopt for PNPIPE_PIPE_HANDLE | ||
221 | -connect | ||
222 | -setsockopt for PNPIPE_ENCAP_IP | ||
223 | -setsockopt for PNPIPE_ENABLE | ||
224 | |||
225 | 220 | ||
226 | Authors | 221 | Authors |
227 | ------- | 222 | ------- |
diff --git a/net/phonet/pep.c b/net/phonet/pep.c index abfb795af142..671effb4ea15 100644 --- a/net/phonet/pep.c +++ b/net/phonet/pep.c | |||
@@ -136,7 +136,6 @@ static int pep_indicate(struct sock *sk, u8 id, u8 code, | |||
136 | 136 | ||
137 | #define PAD 0x00 | 137 | #define PAD 0x00 |
138 | 138 | ||
139 | #ifdef CONFIG_PHONET_PIPECTRLR | ||
140 | static int pipe_handler_request(struct sock *sk, u8 id, u8 code, | 139 | static int pipe_handler_request(struct sock *sk, u8 id, u8 code, |
141 | const void *data, int len) | 140 | const void *data, int len) |
142 | { | 141 | { |
@@ -168,11 +167,7 @@ static int pipe_handler_send_created_ind(struct sock *sk) | |||
168 | data, 4, GFP_ATOMIC); | 167 | data, 4, GFP_ATOMIC); |
169 | } | 168 | } |
170 | 169 | ||
171 | static int pipe_handler_send_ind(struct sock *sk, u8 id) | 170 | #ifdef CONFIG_PHONET_PIPECTRLR |
172 | { | ||
173 | return pep_indicate(sk, id, PAD, NULL, 0, GFP_ATOMIC); | ||
174 | } | ||
175 | |||
176 | static int pipe_handler_enable_pipe(struct sock *sk, int enable) | 171 | static int pipe_handler_enable_pipe(struct sock *sk, int enable) |
177 | { | 172 | { |
178 | u8 id = enable ? PNS_PEP_ENABLE_REQ : PNS_PEP_DISABLE_REQ; | 173 | u8 id = enable ? PNS_PEP_ENABLE_REQ : PNS_PEP_DISABLE_REQ; |
@@ -376,32 +371,11 @@ static int pipe_do_rcv(struct sock *sk, struct sk_buff *skb) | |||
376 | sk->sk_state_change(sk); | 371 | sk->sk_state_change(sk); |
377 | break; | 372 | break; |
378 | 373 | ||
379 | #ifdef CONFIG_PHONET_PIPECTRLR | ||
380 | case PNS_PEP_DISCONNECT_RESP: | ||
381 | sk->sk_state = TCP_CLOSE; | ||
382 | break; | ||
383 | #endif | ||
384 | |||
385 | case PNS_PEP_ENABLE_REQ: | 374 | case PNS_PEP_ENABLE_REQ: |
386 | /* Wait for PNS_PIPE_(ENABLED|REDIRECTED)_IND */ | 375 | /* Wait for PNS_PIPE_(ENABLED|REDIRECTED)_IND */ |
387 | pep_reply(sk, skb, PN_PIPE_NO_ERROR, NULL, 0, GFP_ATOMIC); | 376 | pep_reply(sk, skb, PN_PIPE_NO_ERROR, NULL, 0, GFP_ATOMIC); |
388 | break; | 377 | break; |
389 | 378 | ||
390 | #ifdef CONFIG_PHONET_PIPECTRLR | ||
391 | case PNS_PEP_ENABLE_RESP: | ||
392 | pipe_handler_send_ind(sk, PNS_PIPE_ENABLED_IND); | ||
393 | |||
394 | if (!pn_flow_safe(pn->tx_fc)) { | ||
395 | atomic_set(&pn->tx_credits, 1); | ||
396 | sk->sk_write_space(sk); | ||
397 | } | ||
398 | if (sk->sk_state == TCP_ESTABLISHED) | ||
399 | break; /* Nothing to do */ | ||
400 | sk->sk_state = TCP_ESTABLISHED; | ||
401 | pipe_grant_credits(sk, GFP_ATOMIC); | ||
402 | break; | ||
403 | #endif | ||
404 | |||
405 | case PNS_PEP_RESET_REQ: | 379 | case PNS_PEP_RESET_REQ: |
406 | switch (hdr->state_after_reset) { | 380 | switch (hdr->state_after_reset) { |
407 | case PN_PIPE_DISABLE: | 381 | case PN_PIPE_DISABLE: |
@@ -420,15 +394,6 @@ static int pipe_do_rcv(struct sock *sk, struct sk_buff *skb) | |||
420 | pep_reply(sk, skb, PN_PIPE_NO_ERROR, NULL, 0, GFP_ATOMIC); | 394 | pep_reply(sk, skb, PN_PIPE_NO_ERROR, NULL, 0, GFP_ATOMIC); |
421 | break; | 395 | break; |
422 | 396 | ||
423 | #ifdef CONFIG_PHONET_PIPECTRLR | ||
424 | case PNS_PEP_DISABLE_RESP: | ||
425 | atomic_set(&pn->tx_credits, 0); | ||
426 | pipe_handler_send_ind(sk, PNS_PIPE_DISABLED_IND); | ||
427 | sk->sk_state = TCP_SYN_RECV; | ||
428 | pn->rx_credits = 0; | ||
429 | break; | ||
430 | #endif | ||
431 | |||
432 | case PNS_PEP_CTRL_REQ: | 397 | case PNS_PEP_CTRL_REQ: |
433 | if (skb_queue_len(&pn->ctrlreq_queue) >= PNPIPE_CTRLREQ_MAX) { | 398 | if (skb_queue_len(&pn->ctrlreq_queue) >= PNPIPE_CTRLREQ_MAX) { |
434 | atomic_inc(&sk->sk_drops); | 399 | atomic_inc(&sk->sk_drops); |
@@ -521,7 +486,6 @@ static void pipe_destruct(struct sock *sk) | |||
521 | skb_queue_purge(&pn->ctrlreq_queue); | 486 | skb_queue_purge(&pn->ctrlreq_queue); |
522 | } | 487 | } |
523 | 488 | ||
524 | #ifdef CONFIG_PHONET_PIPECTRLR | ||
525 | static u8 pipe_negotiate_fc(const u8 *fcs, unsigned n) | 489 | static u8 pipe_negotiate_fc(const u8 *fcs, unsigned n) |
526 | { | 490 | { |
527 | unsigned i; | 491 | unsigned i; |
@@ -546,6 +510,8 @@ static int pep_connresp_rcv(struct sock *sk, struct sk_buff *skb) | |||
546 | return -EINVAL; | 510 | return -EINVAL; |
547 | 511 | ||
548 | hdr = pnp_hdr(skb); | 512 | hdr = pnp_hdr(skb); |
513 | if (hdr->error_code != PN_PIPE_NO_ERROR) | ||
514 | return -ECONNREFUSED; | ||
549 | 515 | ||
550 | /* Parse sub-blocks */ | 516 | /* Parse sub-blocks */ |
551 | n_sb = hdr->data[4]; | 517 | n_sb = hdr->data[4]; |
@@ -573,14 +539,74 @@ static int pep_connresp_rcv(struct sock *sk, struct sk_buff *skb) | |||
573 | n_sb--; | 539 | n_sb--; |
574 | } | 540 | } |
575 | 541 | ||
576 | sk->sk_state = TCP_SYN_RECV; | ||
577 | sk->sk_backlog_rcv = pipe_do_rcv; | ||
578 | pn->rx_credits = 0; | ||
579 | sk->sk_state_change(sk); | ||
580 | |||
581 | return pipe_handler_send_created_ind(sk); | 542 | return pipe_handler_send_created_ind(sk); |
582 | } | 543 | } |
583 | #endif | 544 | |
545 | /* Queue an skb to an actively connected sock. | ||
546 | * Socket lock must be held. */ | ||
547 | static int pipe_handler_do_rcv(struct sock *sk, struct sk_buff *skb) | ||
548 | { | ||
549 | struct pep_sock *pn = pep_sk(sk); | ||
550 | struct pnpipehdr *hdr = pnp_hdr(skb); | ||
551 | int err = NET_RX_SUCCESS; | ||
552 | |||
553 | switch (hdr->message_id) { | ||
554 | case PNS_PIPE_ALIGNED_DATA: | ||
555 | __skb_pull(skb, 1); | ||
556 | /* fall through */ | ||
557 | case PNS_PIPE_DATA: | ||
558 | __skb_pull(skb, 3); /* Pipe data header */ | ||
559 | if (!pn_flow_safe(pn->rx_fc)) { | ||
560 | err = sock_queue_rcv_skb(sk, skb); | ||
561 | if (!err) | ||
562 | return NET_RX_SUCCESS; | ||
563 | err = NET_RX_DROP; | ||
564 | break; | ||
565 | } | ||
566 | |||
567 | if (pn->rx_credits == 0) { | ||
568 | atomic_inc(&sk->sk_drops); | ||
569 | err = NET_RX_DROP; | ||
570 | break; | ||
571 | } | ||
572 | pn->rx_credits--; | ||
573 | skb->dev = NULL; | ||
574 | skb_set_owner_r(skb, sk); | ||
575 | err = skb->len; | ||
576 | skb_queue_tail(&sk->sk_receive_queue, skb); | ||
577 | if (!sock_flag(sk, SOCK_DEAD)) | ||
578 | sk->sk_data_ready(sk, err); | ||
579 | return NET_RX_SUCCESS; | ||
580 | |||
581 | case PNS_PEP_CONNECT_RESP: | ||
582 | if (sk->sk_state != TCP_SYN_SENT) | ||
583 | break; | ||
584 | if (!sock_flag(sk, SOCK_DEAD)) | ||
585 | sk->sk_state_change(sk); | ||
586 | if (pep_connresp_rcv(sk, skb)) { | ||
587 | sk->sk_state = TCP_CLOSE_WAIT; | ||
588 | break; | ||
589 | } | ||
590 | |||
591 | sk->sk_state = TCP_ESTABLISHED; | ||
592 | if (!pn_flow_safe(pn->tx_fc)) { | ||
593 | atomic_set(&pn->tx_credits, 1); | ||
594 | sk->sk_write_space(sk); | ||
595 | } | ||
596 | pipe_grant_credits(sk, GFP_ATOMIC); | ||
597 | break; | ||
598 | |||
599 | case PNS_PEP_DISCONNECT_RESP: | ||
600 | /* sock should already be dead, nothing to do */ | ||
601 | break; | ||
602 | |||
603 | case PNS_PEP_STATUS_IND: | ||
604 | pipe_rcv_status(sk, skb); | ||
605 | break; | ||
606 | } | ||
607 | kfree_skb(skb); | ||
608 | return err; | ||
609 | } | ||
584 | 610 | ||
585 | /* Listening sock must be locked */ | 611 | /* Listening sock must be locked */ |
586 | static struct sock *pep_find_pipe(const struct hlist_head *hlist, | 612 | static struct sock *pep_find_pipe(const struct hlist_head *hlist, |
@@ -649,12 +675,6 @@ static int pep_do_rcv(struct sock *sk, struct sk_buff *skb) | |||
649 | sk->sk_data_ready(sk, 0); | 675 | sk->sk_data_ready(sk, 0); |
650 | return NET_RX_SUCCESS; | 676 | return NET_RX_SUCCESS; |
651 | 677 | ||
652 | #ifdef CONFIG_PHONET_PIPECTRLR | ||
653 | case PNS_PEP_CONNECT_RESP: | ||
654 | pep_connresp_rcv(sk, skb); | ||
655 | break; | ||
656 | #endif | ||
657 | |||
658 | case PNS_PEP_DISCONNECT_REQ: | 678 | case PNS_PEP_DISCONNECT_REQ: |
659 | pep_reply(sk, skb, PN_PIPE_NO_ERROR, NULL, 0, GFP_ATOMIC); | 679 | pep_reply(sk, skb, PN_PIPE_NO_ERROR, NULL, 0, GFP_ATOMIC); |
660 | break; | 680 | break; |
@@ -667,15 +687,19 @@ static int pep_do_rcv(struct sock *sk, struct sk_buff *skb) | |||
667 | case PNS_PEP_ENABLE_REQ: | 687 | case PNS_PEP_ENABLE_REQ: |
668 | case PNS_PEP_DISABLE_REQ: | 688 | case PNS_PEP_DISABLE_REQ: |
669 | /* invalid handle is not even allowed here! */ | 689 | /* invalid handle is not even allowed here! */ |
670 | default: | ||
671 | break; | 690 | break; |
691 | |||
692 | default: | ||
693 | if ((1 << sk->sk_state) | ||
694 | & ~(TCPF_CLOSE|TCPF_LISTEN|TCPF_CLOSE_WAIT)) | ||
695 | /* actively connected socket */ | ||
696 | return pipe_handler_do_rcv(sk, skb); | ||
672 | } | 697 | } |
673 | drop: | 698 | drop: |
674 | kfree_skb(skb); | 699 | kfree_skb(skb); |
675 | return NET_RX_SUCCESS; | 700 | return NET_RX_SUCCESS; |
676 | } | 701 | } |
677 | 702 | ||
678 | #ifndef CONFIG_PHONET_PIPECTRLR | ||
679 | static int pipe_do_remove(struct sock *sk) | 703 | static int pipe_do_remove(struct sock *sk) |
680 | { | 704 | { |
681 | struct pep_sock *pn = pep_sk(sk); | 705 | struct pep_sock *pn = pep_sk(sk); |
@@ -693,7 +717,6 @@ static int pipe_do_remove(struct sock *sk) | |||
693 | ph->data[0] = PAD; | 717 | ph->data[0] = PAD; |
694 | return pn_skb_send(sk, skb, NULL); | 718 | return pn_skb_send(sk, skb, NULL); |
695 | } | 719 | } |
696 | #endif | ||
697 | 720 | ||
698 | /* associated socket ceases to exist */ | 721 | /* associated socket ceases to exist */ |
699 | static void pep_sock_close(struct sock *sk, long timeout) | 722 | static void pep_sock_close(struct sock *sk, long timeout) |
@@ -706,13 +729,12 @@ static void pep_sock_close(struct sock *sk, long timeout) | |||
706 | 729 | ||
707 | lock_sock(sk); | 730 | lock_sock(sk); |
708 | if ((1 << sk->sk_state) & (TCPF_SYN_RECV|TCPF_ESTABLISHED)) { | 731 | if ((1 << sk->sk_state) & (TCPF_SYN_RECV|TCPF_ESTABLISHED)) { |
709 | #ifndef CONFIG_PHONET_PIPECTRLR | 732 | if (sk->sk_backlog_rcv == pipe_do_rcv) |
710 | /* Forcefully remove dangling Phonet pipe */ | 733 | /* Forcefully remove dangling Phonet pipe */ |
711 | pipe_do_remove(sk); | 734 | pipe_do_remove(sk); |
712 | #else | 735 | else |
713 | /* send pep disconnect request */ | 736 | pipe_handler_request(sk, PNS_PEP_DISCONNECT_REQ, PAD, |
714 | pipe_handler_request(sk, PNS_PEP_DISCONNECT_REQ, PAD, NULL, 0); | 737 | NULL, 0); |
715 | #endif | ||
716 | } | 738 | } |
717 | sk->sk_state = TCP_CLOSE; | 739 | sk->sk_state = TCP_CLOSE; |
718 | 740 | ||
@@ -844,20 +866,22 @@ drop: | |||
844 | return newsk; | 866 | return newsk; |
845 | } | 867 | } |
846 | 868 | ||
847 | #ifdef CONFIG_PHONET_PIPECTRLR | ||
848 | static int pep_sock_connect(struct sock *sk, struct sockaddr *addr, int len) | 869 | static int pep_sock_connect(struct sock *sk, struct sockaddr *addr, int len) |
849 | { | 870 | { |
850 | struct pep_sock *pn = pep_sk(sk); | 871 | struct pep_sock *pn = pep_sk(sk); |
851 | const struct sockaddr_pn *spn = (struct sockaddr_pn *)addr; | 872 | int err; |
852 | u8 data[4] = { 0 /* sub-blocks */, PAD, PAD, PAD }; | 873 | u8 data[4] = { 0 /* sub-blocks */, PAD, PAD, PAD }; |
853 | 874 | ||
854 | pn->pn_sk.dobject = pn_sockaddr_get_object(spn); | ||
855 | pn->pn_sk.resource = pn_sockaddr_get_resource(spn); | ||
856 | pn->pipe_handle = 1; /* anything but INVALID_HANDLE */ | 875 | pn->pipe_handle = 1; /* anything but INVALID_HANDLE */ |
857 | return pipe_handler_request(sk, PNS_PEP_CONNECT_REQ, | 876 | err = pipe_handler_request(sk, PNS_PEP_CONNECT_REQ, |
858 | PN_PIPE_DISABLE, data, 4); | 877 | PN_PIPE_ENABLE, data, 4); |
878 | if (err) { | ||
879 | pn->pipe_handle = PN_PIPE_INVALID_HANDLE; | ||
880 | return err; | ||
881 | } | ||
882 | sk->sk_state = TCP_SYN_SENT; | ||
883 | return 0; | ||
859 | } | 884 | } |
860 | #endif | ||
861 | 885 | ||
862 | static int pep_ioctl(struct sock *sk, int cmd, unsigned long arg) | 886 | static int pep_ioctl(struct sock *sk, int cmd, unsigned long arg) |
863 | { | 887 | { |
@@ -890,8 +914,16 @@ static int pep_init(struct sock *sk) | |||
890 | 914 | ||
891 | sk->sk_destruct = pipe_destruct; | 915 | sk->sk_destruct = pipe_destruct; |
892 | INIT_HLIST_HEAD(&pn->hlist); | 916 | INIT_HLIST_HEAD(&pn->hlist); |
917 | pn->listener = NULL; | ||
893 | skb_queue_head_init(&pn->ctrlreq_queue); | 918 | skb_queue_head_init(&pn->ctrlreq_queue); |
919 | atomic_set(&pn->tx_credits, 0); | ||
920 | pn->ifindex = 0; | ||
921 | pn->peer_type = 0; | ||
894 | pn->pipe_handle = PN_PIPE_INVALID_HANDLE; | 922 | pn->pipe_handle = PN_PIPE_INVALID_HANDLE; |
923 | pn->rx_credits = 0; | ||
924 | pn->rx_fc = pn->tx_fc = PN_LEGACY_FLOW_CONTROL; | ||
925 | pn->init_enable = 1; | ||
926 | pn->aligned = 0; | ||
895 | return 0; | 927 | return 0; |
896 | } | 928 | } |
897 | 929 | ||
@@ -1219,9 +1251,9 @@ static void pep_sock_unhash(struct sock *sk) | |||
1219 | 1251 | ||
1220 | lock_sock(sk); | 1252 | lock_sock(sk); |
1221 | 1253 | ||
1222 | #ifndef CONFIG_PHONET_PIPECTRLR | 1254 | if (pn->listener != NULL) { |
1223 | if ((1 << sk->sk_state) & ~(TCPF_CLOSE|TCPF_LISTEN)) { | ||
1224 | skparent = pn->listener; | 1255 | skparent = pn->listener; |
1256 | pn->listener = NULL; | ||
1225 | release_sock(sk); | 1257 | release_sock(sk); |
1226 | 1258 | ||
1227 | pn = pep_sk(skparent); | 1259 | pn = pep_sk(skparent); |
@@ -1229,7 +1261,7 @@ static void pep_sock_unhash(struct sock *sk) | |||
1229 | sk_del_node_init(sk); | 1261 | sk_del_node_init(sk); |
1230 | sk = skparent; | 1262 | sk = skparent; |
1231 | } | 1263 | } |
1232 | #endif | 1264 | |
1233 | /* Unhash a listening sock only when it is closed | 1265 | /* Unhash a listening sock only when it is closed |
1234 | * and all of its active connected pipes are closed. */ | 1266 | * and all of its active connected pipes are closed. */ |
1235 | if (hlist_empty(&pn->hlist)) | 1267 | if (hlist_empty(&pn->hlist)) |
@@ -1243,9 +1275,7 @@ static void pep_sock_unhash(struct sock *sk) | |||
1243 | static struct proto pep_proto = { | 1275 | static struct proto pep_proto = { |
1244 | .close = pep_sock_close, | 1276 | .close = pep_sock_close, |
1245 | .accept = pep_sock_accept, | 1277 | .accept = pep_sock_accept, |
1246 | #ifdef CONFIG_PHONET_PIPECTRLR | ||
1247 | .connect = pep_sock_connect, | 1278 | .connect = pep_sock_connect, |
1248 | #endif | ||
1249 | .ioctl = pep_ioctl, | 1279 | .ioctl = pep_ioctl, |
1250 | .init = pep_init, | 1280 | .init = pep_init, |
1251 | .setsockopt = pep_setsockopt, | 1281 | .setsockopt = pep_setsockopt, |
diff --git a/net/phonet/socket.c b/net/phonet/socket.c index 1eccfc35bcc0..b1adafab377c 100644 --- a/net/phonet/socket.c +++ b/net/phonet/socket.c | |||
@@ -225,15 +225,18 @@ static int pn_socket_autobind(struct socket *sock) | |||
225 | return 0; /* socket was already bound */ | 225 | return 0; /* socket was already bound */ |
226 | } | 226 | } |
227 | 227 | ||
228 | #ifdef CONFIG_PHONET_PIPECTRLR | ||
229 | static int pn_socket_connect(struct socket *sock, struct sockaddr *addr, | 228 | static int pn_socket_connect(struct socket *sock, struct sockaddr *addr, |
230 | int len, int flags) | 229 | int len, int flags) |
231 | { | 230 | { |
232 | struct sock *sk = sock->sk; | 231 | struct sock *sk = sock->sk; |
232 | struct pn_sock *pn = pn_sk(sk); | ||
233 | struct sockaddr_pn *spn = (struct sockaddr_pn *)addr; | 233 | struct sockaddr_pn *spn = (struct sockaddr_pn *)addr; |
234 | long timeo; | 234 | struct task_struct *tsk = current; |
235 | long timeo = sock_rcvtimeo(sk, flags & O_NONBLOCK); | ||
235 | int err; | 236 | int err; |
236 | 237 | ||
238 | if (pn_socket_autobind(sock)) | ||
239 | return -ENOBUFS; | ||
237 | if (len < sizeof(struct sockaddr_pn)) | 240 | if (len < sizeof(struct sockaddr_pn)) |
238 | return -EINVAL; | 241 | return -EINVAL; |
239 | if (spn->spn_family != AF_PHONET) | 242 | if (spn->spn_family != AF_PHONET) |
@@ -243,82 +246,61 @@ static int pn_socket_connect(struct socket *sock, struct sockaddr *addr, | |||
243 | 246 | ||
244 | switch (sock->state) { | 247 | switch (sock->state) { |
245 | case SS_UNCONNECTED: | 248 | case SS_UNCONNECTED: |
246 | sk->sk_state = TCP_CLOSE; | 249 | if (sk->sk_state != TCP_CLOSE) { |
247 | break; | ||
248 | case SS_CONNECTING: | ||
249 | switch (sk->sk_state) { | ||
250 | case TCP_SYN_RECV: | ||
251 | sock->state = SS_CONNECTED; | ||
252 | err = -EISCONN; | ||
253 | goto out; | ||
254 | case TCP_CLOSE: | ||
255 | err = -EALREADY; | ||
256 | if (flags & O_NONBLOCK) | ||
257 | goto out; | ||
258 | goto wait_connect; | ||
259 | } | ||
260 | break; | ||
261 | case SS_CONNECTED: | ||
262 | switch (sk->sk_state) { | ||
263 | case TCP_SYN_RECV: | ||
264 | err = -EISCONN; | 250 | err = -EISCONN; |
265 | goto out; | 251 | goto out; |
266 | case TCP_CLOSE: | ||
267 | sock->state = SS_UNCONNECTED; | ||
268 | break; | ||
269 | } | 252 | } |
270 | break; | 253 | break; |
271 | case SS_DISCONNECTING: | 254 | case SS_CONNECTING: |
272 | case SS_FREE: | 255 | err = -EALREADY; |
273 | break; | 256 | goto out; |
257 | default: | ||
258 | err = -EISCONN; | ||
259 | goto out; | ||
274 | } | 260 | } |
275 | sk->sk_state = TCP_CLOSE; | ||
276 | sk_stream_kill_queues(sk); | ||
277 | 261 | ||
262 | pn->dobject = pn_sockaddr_get_object(spn); | ||
263 | pn->resource = pn_sockaddr_get_resource(spn); | ||
278 | sock->state = SS_CONNECTING; | 264 | sock->state = SS_CONNECTING; |
265 | |||
279 | err = sk->sk_prot->connect(sk, addr, len); | 266 | err = sk->sk_prot->connect(sk, addr, len); |
280 | if (err < 0) { | 267 | if (err) { |
281 | sock->state = SS_UNCONNECTED; | 268 | sock->state = SS_UNCONNECTED; |
282 | sk->sk_state = TCP_CLOSE; | 269 | pn->dobject = 0; |
283 | goto out; | 270 | goto out; |
284 | } | 271 | } |
285 | 272 | ||
286 | err = -EINPROGRESS; | 273 | while (sk->sk_state == TCP_SYN_SENT) { |
287 | wait_connect: | 274 | DEFINE_WAIT(wait); |
288 | if (sk->sk_state != TCP_SYN_RECV && (flags & O_NONBLOCK)) | ||
289 | goto out; | ||
290 | |||
291 | timeo = sock_sndtimeo(sk, flags & O_NONBLOCK); | ||
292 | release_sock(sk); | ||
293 | 275 | ||
294 | err = -ERESTARTSYS; | 276 | if (!timeo) { |
295 | timeo = wait_event_interruptible_timeout(*sk_sleep(sk), | 277 | err = -EINPROGRESS; |
296 | sk->sk_state != TCP_CLOSE, | 278 | goto out; |
297 | timeo); | 279 | } |
298 | 280 | if (signal_pending(tsk)) { | |
299 | lock_sock(sk); | 281 | err = sock_intr_errno(timeo); |
300 | if (timeo < 0) | 282 | goto out; |
301 | goto out; /* -ERESTARTSYS */ | 283 | } |
302 | |||
303 | err = -ETIMEDOUT; | ||
304 | if (timeo == 0 && sk->sk_state != TCP_SYN_RECV) | ||
305 | goto out; | ||
306 | 284 | ||
307 | if (sk->sk_state != TCP_SYN_RECV) { | 285 | prepare_to_wait_exclusive(sk_sleep(sk), &wait, |
308 | sock->state = SS_UNCONNECTED; | 286 | TASK_INTERRUPTIBLE); |
309 | err = sock_error(sk); | 287 | release_sock(sk); |
310 | if (!err) | 288 | timeo = schedule_timeout(timeo); |
311 | err = -ECONNREFUSED; | 289 | lock_sock(sk); |
312 | goto out; | 290 | finish_wait(sk_sleep(sk), &wait); |
313 | } | 291 | } |
314 | sock->state = SS_CONNECTED; | ||
315 | err = 0; | ||
316 | 292 | ||
293 | if ((1 << sk->sk_state) & (TCPF_SYN_RECV|TCPF_ESTABLISHED)) | ||
294 | err = 0; | ||
295 | else if (sk->sk_state == TCP_CLOSE_WAIT) | ||
296 | err = -ECONNRESET; | ||
297 | else | ||
298 | err = -ECONNREFUSED; | ||
299 | sock->state = err ? SS_UNCONNECTED : SS_CONNECTED; | ||
317 | out: | 300 | out: |
318 | release_sock(sk); | 301 | release_sock(sk); |
319 | return err; | 302 | return err; |
320 | } | 303 | } |
321 | #endif | ||
322 | 304 | ||
323 | static int pn_socket_accept(struct socket *sock, struct socket *newsock, | 305 | static int pn_socket_accept(struct socket *sock, struct socket *newsock, |
324 | int flags) | 306 | int flags) |
@@ -486,11 +468,7 @@ const struct proto_ops phonet_stream_ops = { | |||
486 | .owner = THIS_MODULE, | 468 | .owner = THIS_MODULE, |
487 | .release = pn_socket_release, | 469 | .release = pn_socket_release, |
488 | .bind = pn_socket_bind, | 470 | .bind = pn_socket_bind, |
489 | #ifdef CONFIG_PHONET_PIPECTRLR | ||
490 | .connect = pn_socket_connect, | 471 | .connect = pn_socket_connect, |
491 | #else | ||
492 | .connect = sock_no_connect, | ||
493 | #endif | ||
494 | .socketpair = sock_no_socketpair, | 472 | .socketpair = sock_no_socketpair, |
495 | .accept = pn_socket_accept, | 473 | .accept = pn_socket_accept, |
496 | .getname = pn_socket_getname, | 474 | .getname = pn_socket_getname, |