nrf_modem/
socket.rs

1use crate::{
2    error::Error, ffi::get_last_error, ip::NrfSockAddr, lte_link::LteLink, CancellationToken,
3};
4use core::net::SocketAddr;
5use core::{
6    cell::RefCell,
7    ops::{BitOr, BitOrAssign, Deref, Neg},
8    sync::atomic::{AtomicU8, Ordering},
9    task::{Poll, Waker},
10};
11use critical_section::Mutex;
12use num_enum::{IntoPrimitive, TryFromPrimitive};
13
14// use 16 slots for wakers instead of 8, which is the max number of sockets allowed, so they
15// are not overwritten when split into their rx/tx counterparts and run in separate tasks.
16const WAKER_SLOTS: usize = (nrfxlib_sys::NRF_MODEM_MAX_SOCKET_COUNT * 2) as usize;
17const WAKER_INIT: Option<(Waker, i32, SocketDirection)> = None;
18#[allow(clippy::type_complexity)]
19static SOCKET_WAKERS: Mutex<RefCell<[Option<(Waker, i32, SocketDirection)>; WAKER_SLOTS]>> =
20    Mutex::new(RefCell::new([WAKER_INIT; WAKER_SLOTS]));
21
22fn wake_sockets(socket_fd: i32, socket_dir: SocketDirection) {
23    critical_section::with(|cs| {
24        SOCKET_WAKERS
25            .borrow_ref_mut(cs)
26            .iter_mut()
27            .filter(|slot| {
28                if let Some((_, fd, dir)) = slot {
29                    *fd == socket_fd && dir.same_direction(socket_dir)
30                } else {
31                    false
32                }
33            })
34            .for_each(|slot| {
35                let (waker, _, _) = slot.take().unwrap();
36                waker.wake();
37            });
38    });
39}
40
41fn register_socket_waker(waker: Waker, socket_fd: i32, socket_dir: SocketDirection) {
42    critical_section::with(|cs| {
43        // Get the wakers
44        let mut wakers = SOCKET_WAKERS.borrow_ref_mut(cs);
45
46        // Search for an empty spot or a spot that already stores the waker for the socket
47        let empty_waker = wakers.iter_mut().find(|waker| {
48            waker.is_none()
49                || waker.as_ref().map(|(_, fd, dir)| (*fd, *dir)) == Some((socket_fd, socket_dir))
50        });
51
52        if let Some(empty_waker) = empty_waker {
53            // In principle we should always have an empty spot and run this code
54            *empty_waker = Some((waker, socket_fd, socket_dir));
55        } else {
56            // It shouldn't ever happen, but if there's no empty spot, we just evict the first socket
57            // That socket will just reregister itself
58            wakers
59                .first_mut()
60                .unwrap()
61                .replace((waker, socket_fd, socket_dir))
62                .unwrap()
63                .0
64                .wake();
65        }
66    });
67}
68
69unsafe extern "C" fn socket_poll_callback(pollfd: *mut nrfxlib_sys::nrf_pollfd) {
70    let pollfd = *pollfd;
71
72    let mut direction = SocketDirection::Neither;
73
74    if pollfd.revents as u32 & nrfxlib_sys::NRF_POLLIN != 0 {
75        direction |= SocketDirection::In;
76    }
77
78    if pollfd.revents as u32 & nrfxlib_sys::NRF_POLLOUT != 0 {
79        direction |= SocketDirection::Out;
80    }
81
82    if pollfd.revents as u32
83        & (nrfxlib_sys::NRF_POLLERR | nrfxlib_sys::NRF_POLLHUP | nrfxlib_sys::NRF_POLLNVAL)
84        != 0
85    {
86        direction |= SocketDirection::Either;
87    }
88
89    #[cfg(feature = "defmt")]
90    defmt::trace!(
91        "Socket poll callback. fd: {}, revents: {:X}, direction: {}",
92        pollfd.fd,
93        pollfd.revents,
94        direction
95    );
96
97    wake_sockets(pollfd.fd, direction);
98}
99
100/// Used as a identifier for wakers when a socket is split into RX/TX halves
101#[derive(Clone, Copy, Debug, PartialEq, Eq)]
102#[cfg_attr(feature = "defmt", derive(defmt::Format))]
103enum SocketDirection {
104    /// Neither option
105    Neither,
106    /// RX
107    In,
108    /// TX
109    Out,
110    /// RX and/or TX
111    Either,
112}
113
114impl BitOrAssign for SocketDirection {
115    fn bitor_assign(&mut self, rhs: Self) {
116        *self = *self | rhs;
117    }
118}
119
120impl BitOr for SocketDirection {
121    type Output = SocketDirection;
122
123    fn bitor(self, rhs: Self) -> Self::Output {
124        match (self, rhs) {
125            (SocketDirection::Neither, rhs) => rhs,
126            (lhs, SocketDirection::Neither) => lhs,
127            (SocketDirection::In, SocketDirection::In) => SocketDirection::In,
128            (SocketDirection::Out, SocketDirection::Out) => SocketDirection::Out,
129            (SocketDirection::In, SocketDirection::Out) => SocketDirection::Either,
130            (SocketDirection::Out, SocketDirection::In) => SocketDirection::Either,
131            (SocketDirection::Either, _) => SocketDirection::Either,
132            (_, SocketDirection::Either) => SocketDirection::Either,
133        }
134    }
135}
136
137impl SocketDirection {
138    fn same_direction(&self, other: Self) -> bool {
139        match (self, other) {
140            (SocketDirection::Neither, _) => false,
141            (_, SocketDirection::Neither) => false,
142            (SocketDirection::In, SocketDirection::In) => true,
143            (SocketDirection::Out, SocketDirection::Out) => true,
144            (SocketDirection::In, SocketDirection::Out) => false,
145            (SocketDirection::Out, SocketDirection::In) => false,
146            (_, SocketDirection::Either) => true,
147            (SocketDirection::Either, _) => true,
148        }
149    }
150}
151
152/// A socket for network communication through the nRF modem.
153///
154/// This struct provides an async interface to the nRF modem's socket functionality,
155/// supporting TCP, UDP, TLS, and DTLS protocols. The socket automatically manages
156/// the LTE link lifetime and provides non-blocking async operations.
157#[derive(Debug)]
158#[cfg_attr(feature = "defmt", derive(defmt::Format))]
159pub struct Socket {
160    /// The file descriptor given by the modem lib
161    fd: i32,
162    /// The socket family (required to know when we need to decipher an incoming IP address)
163    family: SocketFamily,
164    /// The link this socket holds to keep the LTE alive.
165    /// This is an option because when deactivating we need to take ownership of it.
166    link: Option<LteLink>,
167    /// Gets set to true when the socket has been split. This is relevant for the drop functions
168    split: bool,
169}
170
171impl Socket {
172    /// Create a new socket with the given parameters
173    pub async fn create(
174        family: SocketFamily,
175        s_type: SocketType,
176        protocol: SocketProtocol,
177    ) -> Result<Self, Error> {
178        #[cfg(feature = "defmt")]
179        defmt::debug!(
180            "Creating socket with family: {}, type: {}, protocol: {}",
181            family as u32 as i32,
182            s_type as u32 as i32,
183            protocol as u32 as i32
184        );
185
186        if unsafe { !nrfxlib_sys::nrf_modem_is_initialized() } {
187            return Err(Error::ModemNotInitialized);
188        }
189
190        // Let's activate the modem
191        let link = LteLink::new().await?;
192
193        // Create the socket in the nrf-modem lib
194        let fd = unsafe {
195            nrfxlib_sys::nrf_socket(
196                family as u32 as i32,
197                s_type as u32 as i32,
198                protocol as u32 as i32,
199            )
200        };
201
202        // If the fd is -1, then there is an error in `errno`
203        if fd == -1 {
204            return Err(Error::NrfError(get_last_error()));
205        }
206
207        // Set the socket to non-blocking
208        unsafe {
209            let result = nrfxlib_sys::nrf_fcntl(
210                fd,
211                nrfxlib_sys::NRF_F_SETFL as _,
212                nrfxlib_sys::NRF_O_NONBLOCK as _,
213            );
214
215            if result == -1 {
216                return Err(Error::NrfError(get_last_error()));
217            }
218        }
219
220        // Register the callback of the socket. This will be used to wake up the socket waker
221        let poll_callback = nrfxlib_sys::nrf_modem_pollcb {
222            callback: Some(socket_poll_callback),
223            events: (nrfxlib_sys::NRF_POLLIN | nrfxlib_sys::NRF_POLLOUT) as _, // All events
224            oneshot: false,
225        };
226
227        unsafe {
228            let result = nrfxlib_sys::nrf_setsockopt(
229                fd,
230                nrfxlib_sys::NRF_SOL_SOCKET as _,
231                nrfxlib_sys::NRF_SO_POLLCB as _,
232                (&poll_callback as *const nrfxlib_sys::nrf_modem_pollcb).cast(),
233                core::mem::size_of::<nrfxlib_sys::nrf_modem_pollcb>() as u32,
234            );
235
236            if result == -1 {
237                return Err(Error::NrfError(get_last_error()));
238            }
239        }
240
241        Ok(Socket {
242            fd,
243            family,
244            link: Some(link),
245            split: false,
246        })
247    }
248
249    /// Get the nrf-modem file descriptor so the user can opt out of using this high level wrapper for things
250    pub fn as_raw_fd(&self) -> i32 {
251        self.fd
252    }
253
254    /// Split the socket into two handles that can be used independently.
255    ///
256    /// This is useful for splitting a socket into separate read and write handles
257    /// that can be used in different async tasks. Each handle maintains its own
258    /// LTE link to keep the connection alive.
259    pub async fn split(mut self) -> Result<(SplitSocketHandle, SplitSocketHandle), Error> {
260        let index = SplitSocketHandle::get_new_spot();
261        self.split = true;
262
263        Ok((
264            SplitSocketHandle {
265                inner: Some(Socket {
266                    fd: self.fd,
267                    family: self.family,
268                    link: Some(LteLink::new().await?),
269                    split: true,
270                }),
271                index,
272            },
273            SplitSocketHandle {
274                inner: Some(self),
275                index,
276            },
277        ))
278    }
279
280    /// Connect to the given socket address.
281    ///
282    /// This calls the [nrfxlib_sys::nrf_connect] function and can be used for tcp streams, udp connections and dtls connections.
283    pub async fn connect(&self, address: SocketAddr) -> Result<(), Error> {
284        unsafe {
285            self.connect_with_cancellation(address, &Default::default())
286                .await
287        }
288    }
289
290    /// Connect to the given socket address.
291    ///
292    /// This calls the [nrfxlib_sys::nrf_connect] function and can be used for tcp streams, udp connections and dtls connections.
293    ///
294    /// ## Safety
295    ///
296    /// If the connect is cancelled, the socket may be in a weird state and should be dropped.
297    pub async unsafe fn connect_with_cancellation(
298        &self,
299        address: SocketAddr,
300        token: &CancellationToken,
301    ) -> Result<(), Error> {
302        #[cfg(feature = "defmt")]
303        defmt::debug!(
304            "Connecting socket {} to {:?}",
305            self.fd,
306            defmt::Debug2Format(&address)
307        );
308
309        token.bind_to_current_task().await;
310
311        // Before we can connect, we need to make sure we have a working link.
312        // It is possible this will never resolve, but it is up to the user to manage the timeouts.
313        self.link
314            .as_ref()
315            .unwrap()
316            .wait_for_link_with_cancellation(token)
317            .await?;
318
319        core::future::poll_fn(|cx| {
320            #[cfg(feature = "defmt")]
321            defmt::trace!("Connecting socket {}", self.fd);
322
323            if token.is_cancelled() {
324                return Poll::Ready(Err(Error::OperationCancelled));
325            }
326
327            // Cast the address to something the nrf-modem understands
328            let address = NrfSockAddr::from(address);
329
330            register_socket_waker(cx.waker().clone(), self.fd, SocketDirection::Either);
331
332            // Do the connect call, this is non-blocking due to the socket setup
333            let mut connect_result = unsafe {
334                nrfxlib_sys::nrf_connect(self.fd, address.as_ptr(), address.size() as u32)
335            } as isize;
336
337            const NRF_EINPROGRESS: isize = nrfxlib_sys::NRF_EINPROGRESS as isize;
338            const NRF_EALREADY: isize = nrfxlib_sys::NRF_EALREADY as isize;
339            const NRF_EISCONN: isize = nrfxlib_sys::NRF_EISCONN as isize;
340
341            if connect_result == -1 {
342                connect_result = get_last_error();
343            }
344
345            #[cfg(feature = "defmt")]
346            defmt::trace!("Connect result {}", connect_result);
347
348            match connect_result {
349                // 0 when we have succesfully connected
350                0 => Poll::Ready(Ok(())),
351                // The socket was already connected
352                NRF_EISCONN => Poll::Ready(Ok(())),
353                // The socket is not yet connected
354                NRF_EINPROGRESS | NRF_EALREADY => Poll::Pending,
355                // Something else, this is likely an error
356                error => Poll::Ready(Err(Error::NrfError(error))),
357            }
358        })
359        .await?;
360
361        Ok(())
362    }
363
364    /// Bind the socket to a given address.
365    ///
366    /// This calls the [nrfxlib_sys::nrf_bind] function and can be used for UDP sockets.
367    pub async fn bind(&self, address: SocketAddr) -> Result<(), Error> {
368        unsafe {
369            self.bind_with_cancellation(address, &Default::default())
370                .await
371        }
372    }
373
374    /// Bind the socket to a given address.
375    ///
376    /// This calls the [nrfxlib_sys::nrf_bind] function and can be used for UDP sockets.
377    ///
378    /// ## Safety
379    ///
380    /// If the bind is cancelled, the socket may be in a weird state and should be dropped.
381    pub async unsafe fn bind_with_cancellation(
382        &self,
383        address: SocketAddr,
384        token: &CancellationToken,
385    ) -> Result<(), Error> {
386        #[cfg(feature = "defmt")]
387        defmt::debug!(
388            "Binding socket {} to {:?}",
389            self.fd,
390            defmt::Debug2Format(&address)
391        );
392
393        token.bind_to_current_task().await;
394
395        // Before we can connect, we need to make sure we have a working link.
396        // It is possible this will never resolve, but it is up to the user to manage the timeouts.
397        self.link
398            .as_ref()
399            .unwrap()
400            .wait_for_link_with_cancellation(token)
401            .await?;
402
403        core::future::poll_fn(|cx| {
404            #[cfg(feature = "defmt")]
405            defmt::trace!("Binding socket {}", self.fd);
406
407            if token.is_cancelled() {
408                return Poll::Ready(Err(Error::OperationCancelled));
409            }
410
411            // Cast the address to something the nrf-modem understands
412            let address = NrfSockAddr::from(address);
413
414            register_socket_waker(cx.waker().clone(), self.fd, SocketDirection::Either);
415
416            // Do the bind call, this is non-blocking due to the socket setup
417            let mut bind_result =
418                unsafe { nrfxlib_sys::nrf_bind(self.fd, address.as_ptr(), address.size() as u32) }
419                    as isize;
420
421            const NRF_EINPROGRESS: isize = nrfxlib_sys::NRF_EINPROGRESS as isize;
422            const NRF_EALREADY: isize = nrfxlib_sys::NRF_EALREADY as isize;
423            const NRF_EISCONN: isize = nrfxlib_sys::NRF_EISCONN as isize;
424
425            if bind_result == -1 {
426                bind_result = get_last_error();
427            }
428
429            #[cfg(feature = "defmt")]
430            defmt::trace!("Bind result {}", bind_result);
431
432            match bind_result {
433                // 0 when we have succesfully connected
434                0 => Poll::Ready(Ok(())),
435                // The socket was already connected
436                NRF_EISCONN => Poll::Ready(Ok(())),
437                // The socket is not yet connected
438                NRF_EINPROGRESS | NRF_EALREADY => Poll::Pending,
439                // Something else, this is likely an error
440                error => Poll::Ready(Err(Error::NrfError(error))),
441            }
442        })
443        .await?;
444
445        Ok(())
446    }
447
448    /// Write data to the socket.
449    ///
450    /// This calls the [nrfxlib_sys::nrf_send] function and can be used for TCP streams and dTLS connections.
451    pub async fn write(&self, buffer: &[u8]) -> Result<usize, Error> {
452        self.write_with_cancellation(buffer, &Default::default())
453            .await
454    }
455
456    /// Write data to the socket with cancellation support.
457    ///
458    /// This calls the [nrfxlib_sys::nrf_send] function and can be used for TCP streams and dTLS connections.
459    ///
460    /// This operation can be cancelled using the provided [`CancellationToken`].
461    pub async fn write_with_cancellation(
462        &self,
463        buffer: &[u8],
464        token: &CancellationToken,
465    ) -> Result<usize, Error> {
466        token.bind_to_current_task().await;
467
468        core::future::poll_fn(|cx| {
469            #[cfg(feature = "defmt")]
470            defmt::trace!("Sending with socket {}", self.fd);
471
472            if token.is_cancelled() {
473                return Poll::Ready(Err(Error::OperationCancelled));
474            }
475
476            register_socket_waker(cx.waker().clone(), self.fd, SocketDirection::Out);
477
478            let mut send_result = unsafe {
479                nrfxlib_sys::nrf_send(self.fd, buffer.as_ptr() as *const _, buffer.len(), 0)
480            };
481
482            if send_result == -1 {
483                send_result = get_last_error().abs().neg();
484            }
485
486            #[cfg(feature = "defmt")]
487            defmt::trace!("Send result {}", send_result);
488
489            const NRF_EWOULDBLOCK: isize = -(nrfxlib_sys::NRF_EWOULDBLOCK as isize);
490            const NRF_ENOTCONN: isize = -(nrfxlib_sys::NRF_ENOTCONN as isize);
491
492            match send_result {
493                0 if !buffer.is_empty() => Poll::Ready(Err(Error::Disconnected)),
494                NRF_ENOTCONN => Poll::Ready(Err(Error::Disconnected)),
495                bytes_sent @ 0.. => Poll::Ready(Ok(bytes_sent as usize)),
496                NRF_EWOULDBLOCK => Poll::Pending,
497                error => Poll::Ready(Err(Error::NrfError(error))),
498            }
499        })
500        .await
501    }
502
503    /// Receive data from the socket.
504    ///
505    /// This calls the [nrfxlib_sys::nrf_recv] function and can be used for TCP streams and dTLS connections.
506    pub async fn receive(&self, buffer: &mut [u8]) -> Result<usize, Error> {
507        self.receive_with_cancellation(buffer, &Default::default())
508            .await
509    }
510
511    /// Receive data from the socket with cancellation support.
512    ///
513    /// This calls the [nrfxlib_sys::nrf_recv] function and can be used for TCP streams and dTLS connections.
514    ///
515    /// This operation can be cancelled using the provided [`CancellationToken`].
516    pub async fn receive_with_cancellation(
517        &self,
518        buffer: &mut [u8],
519        token: &CancellationToken,
520    ) -> Result<usize, Error> {
521        token.bind_to_current_task().await;
522
523        core::future::poll_fn(|cx| {
524            #[cfg(feature = "defmt")]
525            defmt::trace!("Receiving with socket {}", self.fd);
526
527            if token.is_cancelled() {
528                return Poll::Ready(Err(Error::OperationCancelled));
529            }
530
531            register_socket_waker(cx.waker().clone(), self.fd, SocketDirection::In);
532
533            let mut receive_result = unsafe {
534                nrfxlib_sys::nrf_recv(self.fd, buffer.as_mut_ptr() as *mut _, buffer.len(), 0)
535            };
536
537            if receive_result == -1 {
538                receive_result = get_last_error().abs().neg();
539            }
540
541            #[cfg(feature = "defmt")]
542            defmt::trace!("Receive result {}", receive_result);
543
544            const NRF_EWOULDBLOCK: isize = -(nrfxlib_sys::NRF_EWOULDBLOCK as isize);
545            const NRF_ENOTCONN: isize = -(nrfxlib_sys::NRF_ENOTCONN as isize);
546            const NRF_EMSGSIZE: isize = -(nrfxlib_sys::NRF_EMSGSIZE as isize);
547
548            match receive_result {
549                0 if !buffer.is_empty() => Poll::Ready(Err(Error::Disconnected)),
550                NRF_ENOTCONN => Poll::Ready(Err(Error::Disconnected)),
551                NRF_EMSGSIZE => Poll::Ready(Err(Error::TlsPacketTooBig)),
552                bytes_received @ 0.. => Poll::Ready(Ok(bytes_received as usize)),
553                NRF_EWOULDBLOCK => Poll::Pending,
554                error => Poll::Ready(Err(Error::NrfError(error))),
555            }
556        })
557        .await
558    }
559
560    /// Receive data from the socket along with the sender's address.
561    ///
562    /// This calls the [nrfxlib_sys::nrf_recvfrom] function and can be used for UDP sockets.
563    pub async fn receive_from(&self, buffer: &mut [u8]) -> Result<(usize, SocketAddr), Error> {
564        self.receive_from_with_cancellation(buffer, &Default::default())
565            .await
566    }
567
568    /// Receive data from the socket along with the sender's address, with cancellation support.
569    ///
570    /// This calls the [nrfxlib_sys::nrf_recvfrom] function and can be used for UDP sockets.
571    ///
572    /// This operation can be cancelled using the provided [`CancellationToken`].
573    pub async fn receive_from_with_cancellation(
574        &self,
575        buffer: &mut [u8],
576        token: &CancellationToken,
577    ) -> Result<(usize, SocketAddr), Error> {
578        token.bind_to_current_task().await;
579
580        core::future::poll_fn(|cx| {
581            #[cfg(feature = "defmt")]
582            defmt::trace!("Receiving with socket {}", self.fd);
583
584            if token.is_cancelled() {
585                return Poll::Ready(Err(Error::OperationCancelled));
586            }
587
588            // Big enough to store both ipv4 and ipv6
589            let mut socket_addr_store =
590                [0u8; core::mem::size_of::<nrfxlib_sys::nrf_sockaddr_in6>()];
591            let socket_addr_ptr = socket_addr_store.as_mut_ptr() as *mut nrfxlib_sys::nrf_sockaddr;
592            let mut socket_addr_len = socket_addr_store.len() as u32;
593
594            register_socket_waker(cx.waker().clone(), self.fd, SocketDirection::In);
595
596            let mut receive_result = unsafe {
597                nrfxlib_sys::nrf_recvfrom(
598                    self.fd,
599                    buffer.as_mut_ptr() as *mut _,
600                    buffer.len(),
601                    0,
602                    socket_addr_ptr,
603                    &mut socket_addr_len as *mut u32,
604                )
605            };
606
607            if receive_result == -1 {
608                receive_result = get_last_error().abs().neg();
609            }
610
611            #[cfg(feature = "defmt")]
612            defmt::trace!("Receive result {}", receive_result);
613
614            const NRF_EWOULDBLOCK: isize = -(nrfxlib_sys::NRF_EWOULDBLOCK as isize);
615            const NRF_ENOTCONN: isize = -(nrfxlib_sys::NRF_ENOTCONN as isize);
616
617            match receive_result {
618                0 if !buffer.is_empty() => Poll::Ready(Err(Error::Disconnected)),
619                NRF_ENOTCONN => Poll::Ready(Err(Error::Disconnected)),
620                bytes_received @ 0.. => Poll::Ready(Ok((bytes_received as usize, {
621                    unsafe { (*socket_addr_ptr).sa_family = self.family as u16 }
622                    NrfSockAddr::from(socket_addr_ptr as *const _).into()
623                }))),
624                NRF_EWOULDBLOCK => Poll::Pending,
625                error => Poll::Ready(Err(Error::NrfError(error))),
626            }
627        })
628        .await
629    }
630
631    /// Send data to a specific address through the socket.
632    ///
633    /// This calls the [nrfxlib_sys::nrf_sendto] function and can be used for UDP sockets.
634    pub async fn send_to(&self, buffer: &[u8], address: SocketAddr) -> Result<usize, Error> {
635        self.send_to_with_cancellation(buffer, address, &Default::default())
636            .await
637    }
638
639    /// Send data to a specific address through the socket with cancellation support.
640    ///
641    /// This calls the [nrfxlib_sys::nrf_sendto] function and can be used for UDP sockets.
642    ///
643    /// This operation can be cancelled using the provided [`CancellationToken`].
644    pub async fn send_to_with_cancellation(
645        &self,
646        buffer: &[u8],
647        address: SocketAddr,
648        token: &CancellationToken,
649    ) -> Result<usize, Error> {
650        token.bind_to_current_task().await;
651
652        core::future::poll_fn(|cx| {
653            #[cfg(feature = "defmt")]
654            defmt::trace!("Sending with socket {}", self.fd);
655
656            if token.is_cancelled() {
657                return Poll::Ready(Err(Error::OperationCancelled));
658            }
659
660            let addr = NrfSockAddr::from(address);
661
662            register_socket_waker(cx.waker().clone(), self.fd, SocketDirection::Out);
663
664            let mut send_result = unsafe {
665                nrfxlib_sys::nrf_sendto(
666                    self.fd,
667                    buffer.as_ptr() as *mut _,
668                    buffer.len(),
669                    0,
670                    addr.as_ptr(),
671                    addr.size() as u32,
672                )
673            };
674
675            if send_result == -1 {
676                send_result = get_last_error().abs().neg();
677            }
678
679            #[cfg(feature = "defmt")]
680            defmt::trace!("Sending result {}", send_result);
681
682            const NRF_EWOULDBLOCK: isize = -(nrfxlib_sys::NRF_EWOULDBLOCK as isize);
683            const NRF_ENOTCONN: isize = -(nrfxlib_sys::NRF_ENOTCONN as isize);
684
685            match send_result {
686                0 if !buffer.is_empty() => Poll::Ready(Err(Error::Disconnected)),
687                NRF_ENOTCONN => Poll::Ready(Err(Error::Disconnected)),
688                bytes_received @ 0.. => Poll::Ready(Ok(bytes_received as usize)),
689                NRF_EWOULDBLOCK => Poll::Pending,
690                error => Poll::Ready(Err(Error::NrfError(error))),
691            }
692        })
693        .await
694    }
695
696    /// Set a socket option.
697    ///
698    /// This calls the [nrfxlib_sys::nrf_setsockopt] function and provides access to various socket
699    /// configuration options including timeouts, TLS settings, PDN binding, and protocol-specific
700    /// options.
701    ///
702    /// See [`SocketOption`] for available options.
703    pub fn set_option<'a>(&'a self, option: SocketOption<'a>) -> Result<(), SocketOptionError> {
704        let length = option.get_length();
705
706        let result = unsafe {
707            nrfxlib_sys::nrf_setsockopt(
708                self.fd,
709                option.get_level(),
710                option.get_name(),
711                option.get_value(),
712                length,
713            )
714        };
715
716        if result == -1 {
717            Err((get_last_error() as i32).into())
718        } else {
719            Ok(())
720        }
721    }
722
723    /// Deactivates the socket and the LTE link.
724    /// A normal drop will do the same thing, but blocking.
725    pub async fn deactivate(mut self) -> Result<(), Error> {
726        self.link.take().unwrap().deactivate().await?;
727        Ok(())
728    }
729}
730
731impl Drop for Socket {
732    fn drop(&mut self) {
733        if !self.split {
734            let e = unsafe { nrfxlib_sys::nrf_close(self.fd) };
735
736            if e == -1 {
737                panic!("{:?}", Error::NrfError(get_last_error()));
738            }
739        }
740    }
741}
742
743impl PartialEq for Socket {
744    fn eq(&self, other: &Self) -> bool {
745        self.fd == other.fd
746    }
747}
748impl Eq for Socket {}
749
750/// Socket address family.
751///
752/// Specifies the address family to use for the socket.
753#[repr(u32)]
754#[derive(Debug, Clone, Copy, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
755#[cfg_attr(feature = "defmt", derive(defmt::Format))]
756pub enum SocketFamily {
757    /// Unspecified address family.
758    Unspecified = nrfxlib_sys::NRF_AF_UNSPEC,
759    /// IPv4 address family.
760    Ipv4 = nrfxlib_sys::NRF_AF_INET,
761    /// IPv6 address family.
762    Ipv6 = nrfxlib_sys::NRF_AF_INET6,
763    /// Raw packet interface.
764    Raw = nrfxlib_sys::NRF_AF_PACKET,
765}
766
767/// Socket type.
768///
769/// Specifies the communication semantics for the socket.
770#[repr(u32)]
771#[derive(Debug, Clone, Copy, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
772#[cfg_attr(feature = "defmt", derive(defmt::Format))]
773pub enum SocketType {
774    /// Stream socket (TCP).
775    ///
776    /// Provides sequenced, reliable, two-way, connection-based byte streams.
777    Stream = nrfxlib_sys::NRF_SOCK_STREAM,
778    /// Datagram socket (UDP).
779    ///
780    /// Provides connectionless, unreliable messages of a fixed maximum length.
781    Datagram = nrfxlib_sys::NRF_SOCK_DGRAM,
782    /// Raw socket.
783    ///
784    /// Provides raw network protocol access.
785    Raw = nrfxlib_sys::NRF_SOCK_RAW,
786}
787
788/// Socket protocol.
789///
790/// Specifies the protocol to use with the socket.
791#[repr(u32)]
792#[derive(Debug, Clone, Copy, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
793#[cfg_attr(feature = "defmt", derive(defmt::Format))]
794pub enum SocketProtocol {
795    /// Internet Protocol.
796    IP = nrfxlib_sys::NRF_IPPROTO_IP,
797    /// Transmission Control Protocol.
798    Tcp = nrfxlib_sys::NRF_IPPROTO_TCP,
799    /// User Datagram Protocol.
800    Udp = nrfxlib_sys::NRF_IPPROTO_UDP,
801    /// Internet Protocol Version 6.
802    Ipv6 = nrfxlib_sys::NRF_IPPROTO_IPV6,
803    /// Raw IP packets.
804    Raw = nrfxlib_sys::NRF_IPPROTO_RAW,
805    /// All protocols.
806    All = nrfxlib_sys::NRF_IPPROTO_ALL,
807    /// Transport Layer Security 1.2.
808    Tls1v2 = nrfxlib_sys::NRF_SPROTO_TLS1v2,
809    /// Datagram Transport Layer Security 1.2.
810    DTls1v2 = nrfxlib_sys::NRF_SPROTO_DTLS1v2,
811}
812
813/// Socket configuration options.
814///
815/// These options can be set using [`Socket::set_option`] to configure various
816/// aspects of socket behavior including timeouts, TLS settings, PDN binding,
817/// and protocol-specific options.
818#[allow(clippy::enum_variant_names)]
819#[derive(Debug)]
820pub enum SocketOption<'a> {
821    // NRF_SOL_SOCKET level options
822    /// Non-zero requests reuse of local addresses in bind (protocol-specific).
823    ReuseAddr(i32),
824    /// Timeout value for socket receive and accept operations.
825    ///
826    /// Minimum supported resolution is 1 millisecond.
827    ReceiveTimeout(nrfxlib_sys::nrf_timeval),
828    /// Timeout value for socket send operation.
829    ///
830    /// Minimum supported resolution is 1 millisecond.
831    SendTimeout(nrfxlib_sys::nrf_timeval),
832    /// Bind this socket to a specific PDN ID.
833    BindToPdn(i32),
834    /// Send data on socket as part of exceptional event.
835    ///
836    /// Requires network support and PDN configuration with AT%EXCEPTIONALDATA.
837    ExceptionalData(i32),
838    /// Keep the socket open when its PDN connection is lost, or the device is set to flight mode.
839    KeepOpen(i32),
840    /// Release Assistance Indication (RAI).
841    ///
842    /// Values: NRF_RAI_NO_DATA, NRF_RAI_LAST, NRF_RAI_ONE_RESP, NRF_RAI_ONGOING, NRF_RAI_WAIT_MORE
843    Rai(i32),
844
845    // Protocol-level options
846    /// Non-zero disables ICMP echo replies on both IPv4 and IPv6.
847    SilenceAll(i32),
848    /// Non-zero enables ICMP echo replies on IPv4.
849    IpEchoReply(i32),
850    /// Non-zero enables ICMP echo replies on IPv6.
851    Ipv6EchoReply(i32),
852    /// Non-zero delays IPv6 address refresh during power saving mode.
853    Ipv6DelayedAddrRefresh(i32),
854    /// Configure TCP server session inactivity timeout (0-135 seconds).
855    TcpServerSessionTimeout(i32),
856
857    // NRF_SOL_SECURE level options
858    /// Set the hostname used for peer verification.
859    TlsHostName(&'a str),
860    /// Set the peer verification level.
861    ///
862    /// Values: 0 (disabled), 1 (optional), 2 (required)
863    TlsPeerVerify(i32),
864    /// Non-zero enables TLS session caching.
865    TlsSessionCache(i32),
866    /// Set/get the security tag associated with a socket.
867    TlsTagList(&'a [nrfxlib_sys::nrf_sec_tag_t]),
868    /// Set/get allowed cipher suite list.
869    TlsCipherSuiteList(&'a [i32]),
870    /// Set the role for the connection (client or server).
871    ///
872    /// Values: 0 (client), 1 (server)
873    TlsRole(i32),
874    /// Delete TLS session cache (write-only).
875    TlsSessionCachePurge(i32),
876    /// Set the DTLS handshake timeout.
877    ///
878    /// Values: 0 (no timeout), or specific timeout values
879    DtlsHandshakeTimeout(i32),
880    /// Set DTLS Connection ID setting.
881    ///
882    /// Values: 0 (disabled), 1 (supported), 2 (enabled)
883    DtlsCid(i32),
884    /// Save DTLS connection (write-only).
885    DtlsConnSave(i32),
886    /// Load DTLS connection (write-only).
887    DtlsConnLoad(i32),
888}
889impl SocketOption<'_> {
890    pub(crate) fn get_level(&self) -> i32 {
891        match self {
892            // NRF_SOL_SOCKET level
893            SocketOption::ReuseAddr(_)
894            | SocketOption::ReceiveTimeout(_)
895            | SocketOption::SendTimeout(_)
896            | SocketOption::BindToPdn(_)
897            | SocketOption::ExceptionalData(_)
898            | SocketOption::KeepOpen(_)
899            | SocketOption::Rai(_) => nrfxlib_sys::NRF_SOL_SOCKET as i32,
900
901            // Protocol levels
902            SocketOption::SilenceAll(_) => nrfxlib_sys::NRF_IPPROTO_ALL as i32,
903            SocketOption::IpEchoReply(_) => nrfxlib_sys::NRF_IPPROTO_IP as i32,
904            SocketOption::Ipv6EchoReply(_) | SocketOption::Ipv6DelayedAddrRefresh(_) => {
905                nrfxlib_sys::NRF_IPPROTO_IPV6 as i32
906            }
907            SocketOption::TcpServerSessionTimeout(_) => nrfxlib_sys::NRF_IPPROTO_TCP as i32,
908
909            // NRF_SOL_SECURE level
910            SocketOption::TlsHostName(_)
911            | SocketOption::TlsPeerVerify(_)
912            | SocketOption::TlsSessionCache(_)
913            | SocketOption::TlsTagList(_)
914            | SocketOption::TlsCipherSuiteList(_)
915            | SocketOption::TlsRole(_)
916            | SocketOption::TlsSessionCachePurge(_)
917            | SocketOption::DtlsHandshakeTimeout(_)
918            | SocketOption::DtlsCid(_)
919            | SocketOption::DtlsConnSave(_)
920            | SocketOption::DtlsConnLoad(_) => nrfxlib_sys::NRF_SOL_SECURE as i32,
921        }
922    }
923
924    pub(crate) fn get_name(&self) -> i32 {
925        match self {
926            // NRF_SOL_SOCKET level
927            SocketOption::ReuseAddr(_) => nrfxlib_sys::NRF_SO_REUSEADDR as i32,
928            SocketOption::ReceiveTimeout(_) => nrfxlib_sys::NRF_SO_RCVTIMEO as i32,
929            SocketOption::SendTimeout(_) => nrfxlib_sys::NRF_SO_SNDTIMEO as i32,
930            SocketOption::BindToPdn(_) => nrfxlib_sys::NRF_SO_BINDTOPDN as i32,
931            SocketOption::ExceptionalData(_) => nrfxlib_sys::NRF_SO_EXCEPTIONAL_DATA as i32,
932            SocketOption::KeepOpen(_) => nrfxlib_sys::NRF_SO_KEEPOPEN as i32,
933            SocketOption::Rai(_) => nrfxlib_sys::NRF_SO_RAI as i32,
934
935            // Protocol-level options
936            SocketOption::SilenceAll(_) => nrfxlib_sys::NRF_SO_SILENCE_ALL as i32,
937            SocketOption::IpEchoReply(_) => nrfxlib_sys::NRF_SO_IP_ECHO_REPLY as i32,
938            SocketOption::Ipv6EchoReply(_) => nrfxlib_sys::NRF_SO_IPV6_ECHO_REPLY as i32,
939            SocketOption::Ipv6DelayedAddrRefresh(_) => {
940                nrfxlib_sys::NRF_SO_IPV6_DELAYED_ADDR_REFRESH as i32
941            }
942            SocketOption::TcpServerSessionTimeout(_) => {
943                nrfxlib_sys::NRF_SO_TCP_SRV_SESSTIMEO as i32
944            }
945
946            // NRF_SOL_SECURE level
947            SocketOption::TlsHostName(_) => nrfxlib_sys::NRF_SO_SEC_HOSTNAME as i32,
948            SocketOption::TlsPeerVerify(_) => nrfxlib_sys::NRF_SO_SEC_PEER_VERIFY as i32,
949            SocketOption::TlsSessionCache(_) => nrfxlib_sys::NRF_SO_SEC_SESSION_CACHE as i32,
950            SocketOption::TlsTagList(_) => nrfxlib_sys::NRF_SO_SEC_TAG_LIST as i32,
951            SocketOption::TlsCipherSuiteList(_) => nrfxlib_sys::NRF_SO_SEC_CIPHERSUITE_LIST as i32,
952            SocketOption::TlsRole(_) => nrfxlib_sys::NRF_SO_SEC_ROLE as i32,
953            SocketOption::TlsSessionCachePurge(_) => {
954                nrfxlib_sys::NRF_SO_SEC_SESSION_CACHE_PURGE as i32
955            }
956            SocketOption::DtlsHandshakeTimeout(_) => {
957                nrfxlib_sys::NRF_SO_SEC_DTLS_HANDSHAKE_TIMEO as i32
958            }
959            SocketOption::DtlsCid(_) => nrfxlib_sys::NRF_SO_SEC_DTLS_CID as i32,
960            SocketOption::DtlsConnSave(_) => nrfxlib_sys::NRF_SO_SEC_DTLS_CONN_SAVE as i32,
961            SocketOption::DtlsConnLoad(_) => nrfxlib_sys::NRF_SO_SEC_DTLS_CONN_LOAD as i32,
962        }
963    }
964
965    pub(crate) fn get_value(&self) -> *const core::ffi::c_void {
966        match self {
967            // NRF_SOL_SOCKET level
968            SocketOption::ReuseAddr(x)
969            | SocketOption::BindToPdn(x)
970            | SocketOption::ExceptionalData(x)
971            | SocketOption::KeepOpen(x)
972            | SocketOption::Rai(x) => x as *const _ as *const core::ffi::c_void,
973            SocketOption::ReceiveTimeout(x) | SocketOption::SendTimeout(x) => {
974                x as *const _ as *const core::ffi::c_void
975            }
976
977            // Protocol-level options
978            SocketOption::SilenceAll(x)
979            | SocketOption::IpEchoReply(x)
980            | SocketOption::Ipv6EchoReply(x)
981            | SocketOption::Ipv6DelayedAddrRefresh(x)
982            | SocketOption::TcpServerSessionTimeout(x) => x as *const _ as *const core::ffi::c_void,
983
984            // NRF_SOL_SECURE level
985            SocketOption::TlsHostName(s) => s.as_ptr() as *const core::ffi::c_void,
986            SocketOption::TlsPeerVerify(x)
987            | SocketOption::TlsSessionCache(x)
988            | SocketOption::TlsRole(x)
989            | SocketOption::TlsSessionCachePurge(x)
990            | SocketOption::DtlsHandshakeTimeout(x)
991            | SocketOption::DtlsCid(x)
992            | SocketOption::DtlsConnSave(x)
993            | SocketOption::DtlsConnLoad(x) => x as *const _ as *const core::ffi::c_void,
994            SocketOption::TlsTagList(x) => x.as_ptr() as *const core::ffi::c_void,
995            SocketOption::TlsCipherSuiteList(x) => x.as_ptr() as *const core::ffi::c_void,
996        }
997    }
998
999    pub(crate) fn get_length(&self) -> u32 {
1000        match self {
1001            // NRF_SOL_SOCKET level
1002            SocketOption::ReuseAddr(x)
1003            | SocketOption::BindToPdn(x)
1004            | SocketOption::ExceptionalData(x)
1005            | SocketOption::KeepOpen(x)
1006            | SocketOption::Rai(x) => core::mem::size_of_val(x) as u32,
1007            SocketOption::ReceiveTimeout(x) | SocketOption::SendTimeout(x) => {
1008                core::mem::size_of_val(x) as u32
1009            }
1010
1011            // Protocol-level options
1012            SocketOption::SilenceAll(x)
1013            | SocketOption::IpEchoReply(x)
1014            | SocketOption::Ipv6EchoReply(x)
1015            | SocketOption::Ipv6DelayedAddrRefresh(x)
1016            | SocketOption::TcpServerSessionTimeout(x) => core::mem::size_of_val(x) as u32,
1017
1018            // NRF_SOL_SECURE level
1019            SocketOption::TlsHostName(s) => s.len() as u32,
1020            SocketOption::TlsPeerVerify(x)
1021            | SocketOption::TlsSessionCache(x)
1022            | SocketOption::TlsRole(x)
1023            | SocketOption::TlsSessionCachePurge(x)
1024            | SocketOption::DtlsHandshakeTimeout(x)
1025            | SocketOption::DtlsCid(x)
1026            | SocketOption::DtlsConnSave(x)
1027            | SocketOption::DtlsConnLoad(x) => core::mem::size_of_val(x) as u32,
1028            SocketOption::TlsTagList(x) => core::mem::size_of_val(*x) as u32,
1029            SocketOption::TlsCipherSuiteList(x) => core::mem::size_of_val(*x) as u32,
1030        }
1031    }
1032}
1033
1034/// TLS peer verification level.
1035///
1036/// Controls whether and how the peer's TLS certificate is verified.
1037#[derive(Debug, Copy, Clone)]
1038pub enum PeerVerification {
1039    /// Peer verification is required. The connection will fail if verification fails.
1040    Enabled,
1041    /// Peer verification is optional. The connection proceeds even if verification fails.
1042    Optional,
1043    /// Peer verification is disabled. No verification is performed.
1044    Disabled,
1045}
1046
1047impl PeerVerification {
1048    /// Convert the peer verification level to an integer value for use with socket options.
1049    pub fn as_integer(self) -> i32 {
1050        match self {
1051            PeerVerification::Enabled => 2,
1052            PeerVerification::Optional => 1,
1053            PeerVerification::Disabled => 0,
1054        }
1055    }
1056}
1057
1058/// TLS cipher suites supported by the nRF9160 modem.
1059///
1060/// These are the allowed cipher suites for the nRF9160 modem.
1061/// For more information, see the [Nordic documentation](https://docs.nordicsemi.com/bundle/nrfxlib-apis-latest/page/group_nrf_socket_tls_cipher_suites.html).
1062#[repr(i32)]
1063#[allow(non_camel_case_types)]
1064#[derive(Debug, Copy, Clone)]
1065pub enum CipherSuite {
1066    TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 =
1067        nrfxlib_sys::NRF_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 as i32,
1068    TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA =
1069        nrfxlib_sys::NRF_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA as i32,
1070    TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 =
1071        nrfxlib_sys::NRF_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 as i32,
1072    TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA =
1073        nrfxlib_sys::NRF_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA as i32,
1074    TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA = nrfxlib_sys::NRF_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA as i32,
1075    TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 =
1076        nrfxlib_sys::NRF_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 as i32,
1077    TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA = nrfxlib_sys::NRF_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA as i32,
1078    TLS_PSK_WITH_AES_256_CBC_SHA = nrfxlib_sys::NRF_TLS_PSK_WITH_AES_256_CBC_SHA as i32,
1079    TLS_PSK_WITH_AES_128_CBC_SHA256 = nrfxlib_sys::NRF_TLS_PSK_WITH_AES_128_CBC_SHA256 as i32,
1080    TLS_PSK_WITH_AES_128_CBC_SHA = nrfxlib_sys::NRF_TLS_PSK_WITH_AES_128_CBC_SHA as i32,
1081    TLS_PSK_WITH_AES_128_CCM_8 = nrfxlib_sys::NRF_TLS_PSK_WITH_AES_128_CCM_8 as i32,
1082    TLS_EMPTY_RENEGOTIATIONINFO_SCSV = nrfxlib_sys::NRF_TLS_EMPTY_RENEGOTIATIONINFO_SCSV as i32,
1083    TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 =
1084        nrfxlib_sys::NRF_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 as i32,
1085    TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 =
1086        nrfxlib_sys::NRF_TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 as i32,
1087    TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 =
1088        nrfxlib_sys::NRF_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 as i32,
1089}
1090
1091/// Errors that can occur when setting socket options.
1092#[derive(Debug, Clone)]
1093#[cfg_attr(feature = "defmt", derive(defmt::Format))]
1094pub enum SocketOptionError {
1095    /// The option could not be set when requested, try again.
1096    TryAgain,
1097    /// The socket argument is not a valid file descriptor.
1098    InvalidFileDescriptor,
1099    /// The socket option NRF_SO_RAI with value NRF_RAI_NO_DATA cannot be set on a socket that is not connected.
1100    DestinationAddressRequired,
1101    /// The send and receive timeout values are too big to fit into the timeout fields in the socket structure.
1102    TimeoutTooBig,
1103    /// The specified option is invalid at the specified socket level or the socket has been shut down.
1104    InvalidOption,
1105    /// The socket is already connected, and a specified option cannot be set while the socket is connected.
1106    AlreadyConnected,
1107    /// The option is not supported by the protocol.
1108    UnsupportedOption,
1109    /// The socket argument does not refer to a socket.
1110    NotASocket,
1111    /// There was insufficient memory available for the operation to complete.
1112    OutOfMemory,
1113    /// Insufficient resources are available in the system to complete the call.
1114    OutOfResources,
1115    /// The option is not supported with the current socket configuration.
1116    OperationNotSupported,
1117    /// Modem was shut down.
1118    ModemShutdown,
1119}
1120
1121impl From<i32> for SocketOptionError {
1122    fn from(errno: i32) -> Self {
1123        match errno.unsigned_abs() {
1124            nrfxlib_sys::NRF_EAGAIN => SocketOptionError::TryAgain,
1125            nrfxlib_sys::NRF_EBADF => SocketOptionError::InvalidFileDescriptor,
1126            nrfxlib_sys::NRF_EDESTADDRREQ => SocketOptionError::DestinationAddressRequired,
1127            nrfxlib_sys::NRF_EINVAL => SocketOptionError::InvalidOption,
1128            nrfxlib_sys::NRF_EISCONN => SocketOptionError::AlreadyConnected,
1129            nrfxlib_sys::NRF_ENOPROTOOPT => SocketOptionError::UnsupportedOption,
1130            nrfxlib_sys::NRF_ENOTSOCK => SocketOptionError::NotASocket,
1131            nrfxlib_sys::NRF_ENOMEM => SocketOptionError::OutOfMemory,
1132            nrfxlib_sys::NRF_ENOBUFS => SocketOptionError::OutOfResources,
1133            nrfxlib_sys::NRF_EOPNOTSUPP => SocketOptionError::OperationNotSupported,
1134            nrfxlib_sys::NRF_ESHUTDOWN => SocketOptionError::ModemShutdown,
1135            _ => panic!("Unknown error code: {}", errno),
1136        }
1137    }
1138}
1139
1140#[allow(clippy::declare_interior_mutable_const)]
1141const ATOMIC_U8_INIT: AtomicU8 = AtomicU8::new(0);
1142static ACTIVE_SPLIT_SOCKETS: [AtomicU8; nrfxlib_sys::NRF_MODEM_MAX_SOCKET_COUNT as usize] =
1143    [ATOMIC_U8_INIT; nrfxlib_sys::NRF_MODEM_MAX_SOCKET_COUNT as usize];
1144
1145/// A handle to a split socket.
1146///
1147/// Created by calling [`Socket::split`]. This allows a socket to be split into
1148/// two handles that can be used independently in different async tasks (e.g., one
1149/// for reading and one for writing).
1150///
1151/// Each handle maintains its own LTE link and can be deactivated independently,
1152/// though the underlying socket is only closed when the last handle is dropped.
1153pub struct SplitSocketHandle {
1154    inner: Option<Socket>,
1155    index: usize,
1156}
1157
1158impl SplitSocketHandle {
1159    /// Deactivates this socket handle and its LTE link.
1160    ///
1161    /// This will deactivate the LTE link associated with this handle. If this is the
1162    /// last remaining handle to the split socket, the underlying socket will also be closed.
1163    ///
1164    /// A normal drop will do the same thing, but blocking.
1165    pub async fn deactivate(mut self) -> Result<(), Error> {
1166        let mut inner = self.inner.take().unwrap();
1167
1168        if ACTIVE_SPLIT_SOCKETS[self.index].fetch_sub(1, Ordering::SeqCst) == 1 {
1169            // We were the last handle to drop so the inner socket isn't split anymore
1170            inner.split = false;
1171        }
1172
1173        inner.deactivate().await?;
1174
1175        Ok(())
1176    }
1177
1178    fn get_new_spot() -> usize {
1179        for (index, count) in ACTIVE_SPLIT_SOCKETS.iter().enumerate() {
1180            if count
1181                .compare_exchange(0, 2, Ordering::SeqCst, Ordering::SeqCst)
1182                .is_ok()
1183            {
1184                return index;
1185            }
1186        }
1187
1188        unreachable!("It should not be possible to have more splits than the maximum socket count");
1189    }
1190}
1191
1192impl Deref for SplitSocketHandle {
1193    type Target = Socket;
1194
1195    fn deref(&self) -> &Self::Target {
1196        self.inner.as_ref().unwrap()
1197    }
1198}
1199
1200impl Drop for SplitSocketHandle {
1201    fn drop(&mut self) {
1202        if let Some(inner) = self.inner.as_mut() {
1203            if ACTIVE_SPLIT_SOCKETS[self.index].fetch_sub(1, Ordering::SeqCst) == 1 {
1204                // We were the last handle to drop so the inner socket isn't split anymore
1205                inner.split = false;
1206            }
1207        }
1208    }
1209}