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/// Internal socket implementation
153#[derive(Debug)]
154#[cfg_attr(feature = "defmt", derive(defmt::Format))]
155pub struct Socket {
156    /// The file descriptor given by the modem lib
157    fd: i32,
158    /// The socket family (required to know when we need to decipher an incoming IP address)
159    family: SocketFamily,
160    /// The link this socket holds to keep the LTE alive.
161    /// This is an option because when deactivating we need to take ownership of it.
162    link: Option<LteLink>,
163    /// Gets set to true when the socket has been split. This is relevant for the drop functions
164    split: bool,
165}
166
167impl Socket {
168    /// Create a new socket with the given parameters
169    pub async fn create(
170        family: SocketFamily,
171        s_type: SocketType,
172        protocol: SocketProtocol,
173    ) -> Result<Self, Error> {
174        #[cfg(feature = "defmt")]
175        defmt::debug!(
176            "Creating socket with family: {}, type: {}, protocol: {}",
177            family as u32 as i32,
178            s_type as u32 as i32,
179            protocol as u32 as i32
180        );
181
182        if unsafe { !nrfxlib_sys::nrf_modem_is_initialized() } {
183            return Err(Error::ModemNotInitialized);
184        }
185
186        // Let's activate the modem
187        let link = LteLink::new().await?;
188
189        // Create the socket in the nrf-modem lib
190        let fd = unsafe {
191            nrfxlib_sys::nrf_socket(
192                family as u32 as i32,
193                s_type as u32 as i32,
194                protocol as u32 as i32,
195            )
196        };
197
198        // If the fd is -1, then there is an error in `errno`
199        if fd == -1 {
200            return Err(Error::NrfError(get_last_error()));
201        }
202
203        // Set the socket to non-blocking
204        unsafe {
205            let result = nrfxlib_sys::nrf_fcntl(
206                fd,
207                nrfxlib_sys::NRF_F_SETFL as _,
208                nrfxlib_sys::NRF_O_NONBLOCK as _,
209            );
210
211            if result == -1 {
212                return Err(Error::NrfError(get_last_error()));
213            }
214        }
215
216        // Register the callback of the socket. This will be used to wake up the socket waker
217        let poll_callback = nrfxlib_sys::nrf_modem_pollcb {
218            callback: Some(socket_poll_callback),
219            events: (nrfxlib_sys::NRF_POLLIN | nrfxlib_sys::NRF_POLLOUT) as _, // All events
220            oneshot: false,
221        };
222
223        unsafe {
224            let result = nrfxlib_sys::nrf_setsockopt(
225                fd,
226                nrfxlib_sys::NRF_SOL_SOCKET as _,
227                nrfxlib_sys::NRF_SO_POLLCB as _,
228                (&poll_callback as *const nrfxlib_sys::nrf_modem_pollcb).cast(),
229                core::mem::size_of::<nrfxlib_sys::nrf_modem_pollcb>() as u32,
230            );
231
232            if result == -1 {
233                return Err(Error::NrfError(get_last_error()));
234            }
235        }
236
237        Ok(Socket {
238            fd,
239            family,
240            link: Some(link),
241            split: false,
242        })
243    }
244
245    /// Get the nrf-modem file descriptor so the user can opt out of using this high level wrapper for things
246    pub fn as_raw_fd(&self) -> i32 {
247        self.fd
248    }
249
250    pub async fn split(mut self) -> Result<(SplitSocketHandle, SplitSocketHandle), Error> {
251        let index = SplitSocketHandle::get_new_spot();
252        self.split = true;
253
254        Ok((
255            SplitSocketHandle {
256                inner: Some(Socket {
257                    fd: self.fd,
258                    family: self.family,
259                    link: Some(LteLink::new().await?),
260                    split: true,
261                }),
262                index,
263            },
264            SplitSocketHandle {
265                inner: Some(self),
266                index,
267            },
268        ))
269    }
270
271    /// Connect to the given socket address.
272    ///
273    /// This calls the `nrf_connect` function and can be used for tcp streams, udp connections and dtls connections.
274    ///
275    /// ## Safety
276    ///
277    /// If the connect is cancelled, the socket may be in a weird state and should be dropped.
278    pub async unsafe fn connect(
279        &self,
280        address: SocketAddr,
281        token: &CancellationToken,
282    ) -> Result<(), Error> {
283        #[cfg(feature = "defmt")]
284        defmt::debug!(
285            "Connecting socket {} to {:?}",
286            self.fd,
287            defmt::Debug2Format(&address)
288        );
289
290        token.bind_to_current_task().await;
291
292        // Before we can connect, we need to make sure we have a working link.
293        // It is possible this will never resolve, but it is up to the user to manage the timeouts.
294        self.link
295            .as_ref()
296            .unwrap()
297            .wait_for_link_with_cancellation(token)
298            .await?;
299
300        core::future::poll_fn(|cx| {
301            #[cfg(feature = "defmt")]
302            defmt::trace!("Connecting socket {}", self.fd);
303
304            if token.is_cancelled() {
305                return Poll::Ready(Err(Error::OperationCancelled));
306            }
307
308            // Cast the address to something the nrf-modem understands
309            let address = NrfSockAddr::from(address);
310
311            register_socket_waker(cx.waker().clone(), self.fd, SocketDirection::Either);
312
313            // Do the connect call, this is non-blocking due to the socket setup
314            let mut connect_result = unsafe {
315                nrfxlib_sys::nrf_connect(self.fd, address.as_ptr(), address.size() as u32)
316            } as isize;
317
318            const NRF_EINPROGRESS: isize = nrfxlib_sys::NRF_EINPROGRESS as isize;
319            const NRF_EALREADY: isize = nrfxlib_sys::NRF_EALREADY as isize;
320            const NRF_EISCONN: isize = nrfxlib_sys::NRF_EISCONN as isize;
321
322            if connect_result == -1 {
323                connect_result = get_last_error();
324            }
325
326            #[cfg(feature = "defmt")]
327            defmt::trace!("Connect result {}", connect_result);
328
329            match connect_result {
330                // 0 when we have succesfully connected
331                0 => Poll::Ready(Ok(())),
332                // The socket was already connected
333                NRF_EISCONN => Poll::Ready(Ok(())),
334                // The socket is not yet connected
335                NRF_EINPROGRESS | NRF_EALREADY => Poll::Pending,
336                // Something else, this is likely an error
337                error => Poll::Ready(Err(Error::NrfError(error))),
338            }
339        })
340        .await?;
341
342        Ok(())
343    }
344
345    /// Bind the socket to a given address.
346    ///
347    /// This calls the `nrf_bind` function and can be used for udp sockets
348    ///
349    /// ## Safety
350    ///
351    /// If the bind is cancelled, the socket may be in a weird state and should be dropped.
352    pub async unsafe fn bind(
353        &self,
354        address: SocketAddr,
355        token: &CancellationToken,
356    ) -> Result<(), Error> {
357        #[cfg(feature = "defmt")]
358        defmt::debug!(
359            "Binding socket {} to {:?}",
360            self.fd,
361            defmt::Debug2Format(&address)
362        );
363
364        token.bind_to_current_task().await;
365
366        // Before we can connect, we need to make sure we have a working link.
367        // It is possible this will never resolve, but it is up to the user to manage the timeouts.
368        self.link
369            .as_ref()
370            .unwrap()
371            .wait_for_link_with_cancellation(token)
372            .await?;
373
374        core::future::poll_fn(|cx| {
375            #[cfg(feature = "defmt")]
376            defmt::trace!("Binding socket {}", self.fd);
377
378            if token.is_cancelled() {
379                return Poll::Ready(Err(Error::OperationCancelled));
380            }
381
382            // Cast the address to something the nrf-modem understands
383            let address = NrfSockAddr::from(address);
384
385            register_socket_waker(cx.waker().clone(), self.fd, SocketDirection::Either);
386
387            // Do the bind call, this is non-blocking due to the socket setup
388            let mut bind_result =
389                unsafe { nrfxlib_sys::nrf_bind(self.fd, address.as_ptr(), address.size() as u32) }
390                    as isize;
391
392            const NRF_EINPROGRESS: isize = nrfxlib_sys::NRF_EINPROGRESS as isize;
393            const NRF_EALREADY: isize = nrfxlib_sys::NRF_EALREADY as isize;
394            const NRF_EISCONN: isize = nrfxlib_sys::NRF_EISCONN as isize;
395
396            if bind_result == -1 {
397                bind_result = get_last_error();
398            }
399
400            #[cfg(feature = "defmt")]
401            defmt::trace!("Bind result {}", bind_result);
402
403            match bind_result {
404                // 0 when we have succesfully connected
405                0 => Poll::Ready(Ok(())),
406                // The socket was already connected
407                NRF_EISCONN => Poll::Ready(Ok(())),
408                // The socket is not yet connected
409                NRF_EINPROGRESS | NRF_EALREADY => Poll::Pending,
410                // Something else, this is likely an error
411                error => Poll::Ready(Err(Error::NrfError(error))),
412            }
413        })
414        .await?;
415
416        Ok(())
417    }
418
419    /// Call the [nrfxlib_sys::nrf_send] in an async fashion
420    pub async fn write(&self, buffer: &[u8], token: &CancellationToken) -> Result<usize, Error> {
421        token.bind_to_current_task().await;
422
423        core::future::poll_fn(|cx| {
424            #[cfg(feature = "defmt")]
425            defmt::trace!("Sending with socket {}", self.fd);
426
427            if token.is_cancelled() {
428                return Poll::Ready(Err(Error::OperationCancelled));
429            }
430
431            register_socket_waker(cx.waker().clone(), self.fd, SocketDirection::Out);
432
433            let mut send_result = unsafe {
434                nrfxlib_sys::nrf_send(self.fd, buffer.as_ptr() as *const _, buffer.len(), 0)
435            };
436
437            if send_result == -1 {
438                send_result = get_last_error().abs().neg();
439            }
440
441            #[cfg(feature = "defmt")]
442            defmt::trace!("Send result {}", send_result);
443
444            const NRF_EWOULDBLOCK: isize = -(nrfxlib_sys::NRF_EWOULDBLOCK as isize);
445            const NRF_ENOTCONN: isize = -(nrfxlib_sys::NRF_ENOTCONN as isize);
446
447            match send_result {
448                0 if !buffer.is_empty() => Poll::Ready(Err(Error::Disconnected)),
449                NRF_ENOTCONN => Poll::Ready(Err(Error::Disconnected)),
450                bytes_sent @ 0.. => Poll::Ready(Ok(bytes_sent as usize)),
451                NRF_EWOULDBLOCK => Poll::Pending,
452                error => Poll::Ready(Err(Error::NrfError(error))),
453            }
454        })
455        .await
456    }
457
458    /// Call the [nrfxlib_sys::nrf_recv] in an async fashion
459    pub async fn receive(
460        &self,
461        buffer: &mut [u8],
462        token: &CancellationToken,
463    ) -> Result<usize, Error> {
464        token.bind_to_current_task().await;
465
466        core::future::poll_fn(|cx| {
467            #[cfg(feature = "defmt")]
468            defmt::trace!("Receiving with socket {}", self.fd);
469
470            if token.is_cancelled() {
471                return Poll::Ready(Err(Error::OperationCancelled));
472            }
473
474            register_socket_waker(cx.waker().clone(), self.fd, SocketDirection::In);
475
476            let mut receive_result = unsafe {
477                nrfxlib_sys::nrf_recv(self.fd, buffer.as_mut_ptr() as *mut _, buffer.len(), 0)
478            };
479
480            if receive_result == -1 {
481                receive_result = get_last_error().abs().neg();
482            }
483
484            #[cfg(feature = "defmt")]
485            defmt::trace!("Receive result {}", receive_result);
486
487            const NRF_EWOULDBLOCK: isize = -(nrfxlib_sys::NRF_EWOULDBLOCK as isize);
488            const NRF_ENOTCONN: isize = -(nrfxlib_sys::NRF_ENOTCONN as isize);
489            const NRF_EMSGSIZE: isize = -(nrfxlib_sys::NRF_EMSGSIZE as isize);
490
491            match receive_result {
492                0 if !buffer.is_empty() => Poll::Ready(Err(Error::Disconnected)),
493                NRF_ENOTCONN => Poll::Ready(Err(Error::Disconnected)),
494                NRF_EMSGSIZE => Poll::Ready(Err(Error::TlsPacketTooBig)),
495                bytes_received @ 0.. => Poll::Ready(Ok(bytes_received as usize)),
496                NRF_EWOULDBLOCK => Poll::Pending,
497                error => Poll::Ready(Err(Error::NrfError(error))),
498            }
499        })
500        .await
501    }
502
503    /// Call the [nrfxlib_sys::nrf_recvfrom] in an async fashion
504    pub async fn receive_from(
505        &self,
506        buffer: &mut [u8],
507        token: &CancellationToken,
508    ) -> Result<(usize, SocketAddr), Error> {
509        token.bind_to_current_task().await;
510
511        core::future::poll_fn(|cx| {
512            #[cfg(feature = "defmt")]
513            defmt::trace!("Receiving with socket {}", self.fd);
514
515            if token.is_cancelled() {
516                return Poll::Ready(Err(Error::OperationCancelled));
517            }
518
519            // Big enough to store both ipv4 and ipv6
520            let mut socket_addr_store =
521                [0u8; core::mem::size_of::<nrfxlib_sys::nrf_sockaddr_in6>()];
522            let socket_addr_ptr = socket_addr_store.as_mut_ptr() as *mut nrfxlib_sys::nrf_sockaddr;
523            let mut socket_addr_len = socket_addr_store.len() as u32;
524
525            register_socket_waker(cx.waker().clone(), self.fd, SocketDirection::In);
526
527            let mut receive_result = unsafe {
528                nrfxlib_sys::nrf_recvfrom(
529                    self.fd,
530                    buffer.as_mut_ptr() as *mut _,
531                    buffer.len(),
532                    0,
533                    socket_addr_ptr,
534                    &mut socket_addr_len as *mut u32,
535                )
536            };
537
538            if receive_result == -1 {
539                receive_result = get_last_error().abs().neg();
540            }
541
542            #[cfg(feature = "defmt")]
543            defmt::trace!("Receive result {}", receive_result);
544
545            const NRF_EWOULDBLOCK: isize = -(nrfxlib_sys::NRF_EWOULDBLOCK as isize);
546            const NRF_ENOTCONN: isize = -(nrfxlib_sys::NRF_ENOTCONN 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                bytes_received @ 0.. => Poll::Ready(Ok((bytes_received as usize, {
552                    unsafe { (*socket_addr_ptr).sa_family = self.family as u16 }
553                    NrfSockAddr::from(socket_addr_ptr as *const _).into()
554                }))),
555                NRF_EWOULDBLOCK => Poll::Pending,
556                error => Poll::Ready(Err(Error::NrfError(error))),
557            }
558        })
559        .await
560    }
561
562    /// Call the [nrfxlib_sys::nrf_sendto] in an async fashion
563    pub async fn send_to(
564        &self,
565        buffer: &[u8],
566        address: SocketAddr,
567        token: &CancellationToken,
568    ) -> Result<usize, Error> {
569        token.bind_to_current_task().await;
570
571        core::future::poll_fn(|cx| {
572            #[cfg(feature = "defmt")]
573            defmt::trace!("Sending with socket {}", self.fd);
574
575            if token.is_cancelled() {
576                return Poll::Ready(Err(Error::OperationCancelled));
577            }
578
579            let addr = NrfSockAddr::from(address);
580
581            register_socket_waker(cx.waker().clone(), self.fd, SocketDirection::Out);
582
583            let mut send_result = unsafe {
584                nrfxlib_sys::nrf_sendto(
585                    self.fd,
586                    buffer.as_ptr() as *mut _,
587                    buffer.len(),
588                    0,
589                    addr.as_ptr(),
590                    addr.size() as u32,
591                )
592            };
593
594            if send_result == -1 {
595                send_result = get_last_error().abs().neg();
596            }
597
598            #[cfg(feature = "defmt")]
599            defmt::trace!("Sending result {}", send_result);
600
601            const NRF_EWOULDBLOCK: isize = -(nrfxlib_sys::NRF_EWOULDBLOCK as isize);
602            const NRF_ENOTCONN: isize = -(nrfxlib_sys::NRF_ENOTCONN as isize);
603
604            match send_result {
605                0 if !buffer.is_empty() => Poll::Ready(Err(Error::Disconnected)),
606                NRF_ENOTCONN => Poll::Ready(Err(Error::Disconnected)),
607                bytes_received @ 0.. => Poll::Ready(Ok(bytes_received as usize)),
608                NRF_EWOULDBLOCK => Poll::Pending,
609                error => Poll::Ready(Err(Error::NrfError(error))),
610            }
611        })
612        .await
613    }
614
615    pub fn set_option<'a>(&'a self, option: SocketOption<'a>) -> Result<(), SocketOptionError> {
616        let length = option.get_length();
617
618        let result = unsafe {
619            nrfxlib_sys::nrf_setsockopt(
620                self.fd,
621                nrfxlib_sys::NRF_SOL_SECURE.try_into().unwrap(),
622                option.get_name(),
623                option.get_value(),
624                length,
625            )
626        };
627
628        if result < 0 {
629            Err(result.into())
630        } else {
631            Ok(())
632        }
633    }
634
635    /// Deactivates the socket and the LTE link.
636    /// A normal drop will do the same thing, but blocking.
637    pub async fn deactivate(mut self) -> Result<(), Error> {
638        self.link.take().unwrap().deactivate().await?;
639        Ok(())
640    }
641}
642
643impl Drop for Socket {
644    fn drop(&mut self) {
645        if !self.split {
646            let e = unsafe { nrfxlib_sys::nrf_close(self.fd) };
647
648            if e == -1 {
649                panic!("{:?}", Error::NrfError(get_last_error()));
650            }
651        }
652    }
653}
654
655impl PartialEq for Socket {
656    fn eq(&self, other: &Self) -> bool {
657        self.fd == other.fd
658    }
659}
660impl Eq for Socket {}
661
662#[repr(u32)]
663#[derive(Debug, Clone, Copy, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
664#[cfg_attr(feature = "defmt", derive(defmt::Format))]
665pub enum SocketFamily {
666    Unspecified = nrfxlib_sys::NRF_AF_UNSPEC,
667    Ipv4 = nrfxlib_sys::NRF_AF_INET,
668    Ipv6 = nrfxlib_sys::NRF_AF_INET6,
669    Raw = nrfxlib_sys::NRF_AF_PACKET,
670}
671
672#[repr(u32)]
673#[derive(Debug, Clone, Copy, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
674#[cfg_attr(feature = "defmt", derive(defmt::Format))]
675pub enum SocketType {
676    Stream = nrfxlib_sys::NRF_SOCK_STREAM,
677    Datagram = nrfxlib_sys::NRF_SOCK_DGRAM,
678    Raw = nrfxlib_sys::NRF_SOCK_RAW,
679}
680
681#[repr(u32)]
682#[derive(Debug, Clone, Copy, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
683#[cfg_attr(feature = "defmt", derive(defmt::Format))]
684pub enum SocketProtocol {
685    IP = nrfxlib_sys::NRF_IPPROTO_IP,
686    Tcp = nrfxlib_sys::NRF_IPPROTO_TCP,
687    Udp = nrfxlib_sys::NRF_IPPROTO_UDP,
688    Ipv6 = nrfxlib_sys::NRF_IPPROTO_IPV6,
689    Raw = nrfxlib_sys::NRF_IPPROTO_RAW,
690    All = nrfxlib_sys::NRF_IPPROTO_ALL,
691    Tls1v2 = nrfxlib_sys::NRF_SPROTO_TLS1v2,
692    DTls1v2 = nrfxlib_sys::NRF_SPROTO_DTLS1v2,
693}
694
695#[allow(clippy::enum_variant_names)]
696#[derive(Debug)]
697pub enum SocketOption<'a> {
698    TlsHostName(&'a str),
699    TlsPeerVerify(i32),
700    TlsSessionCache(i32),
701    TlsTagList(&'a [nrfxlib_sys::nrf_sec_tag_t]),
702    TlsCipherSuiteList(&'a [i32]),
703}
704impl SocketOption<'_> {
705    pub(crate) fn get_name(&self) -> i32 {
706        match self {
707            SocketOption::TlsHostName(_) => nrfxlib_sys::NRF_SO_SEC_HOSTNAME as i32,
708            SocketOption::TlsPeerVerify(_) => nrfxlib_sys::NRF_SO_SEC_PEER_VERIFY as i32,
709            SocketOption::TlsSessionCache(_) => nrfxlib_sys::NRF_SO_SEC_SESSION_CACHE as i32,
710            SocketOption::TlsTagList(_) => nrfxlib_sys::NRF_SO_SEC_TAG_LIST as i32,
711            SocketOption::TlsCipherSuiteList(_) => nrfxlib_sys::NRF_SO_SEC_CIPHERSUITE_LIST as i32,
712        }
713    }
714
715    pub(crate) fn get_value(&self) -> *const core::ffi::c_void {
716        match self {
717            SocketOption::TlsHostName(s) => s.as_ptr() as *const core::ffi::c_void,
718            SocketOption::TlsPeerVerify(x) => x as *const _ as *const core::ffi::c_void,
719            SocketOption::TlsSessionCache(x) => x as *const _ as *const core::ffi::c_void,
720            SocketOption::TlsTagList(x) => x.as_ptr() as *const core::ffi::c_void,
721            SocketOption::TlsCipherSuiteList(x) => x.as_ptr() as *const core::ffi::c_void,
722        }
723    }
724
725    pub(crate) fn get_length(&self) -> u32 {
726        match self {
727            SocketOption::TlsHostName(s) => s.len() as u32,
728            SocketOption::TlsPeerVerify(x) => core::mem::size_of_val(x) as u32,
729            SocketOption::TlsSessionCache(x) => core::mem::size_of_val(x) as u32,
730            SocketOption::TlsTagList(x) => core::mem::size_of_val(*x) as u32,
731            SocketOption::TlsCipherSuiteList(x) => core::mem::size_of_val(*x) as u32,
732        }
733    }
734}
735
736#[derive(Debug, Copy, Clone)]
737pub enum PeerVerification {
738    Enabled,
739    Optional,
740    Disabled,
741}
742
743impl PeerVerification {
744    pub fn as_integer(self) -> i32 {
745        match self {
746            PeerVerification::Enabled => 2,
747            PeerVerification::Optional => 1,
748            PeerVerification::Disabled => 0,
749        }
750    }
751}
752
753/// These are the allowed cipher suites for the nrf9160 modem as per <https://docs.nordicsemi.com/bundle/nrfxlib-apis-latest/page/group_nrf_socket_tls_cipher_suites.html>
754#[repr(i32)]
755#[allow(non_camel_case_types)]
756#[derive(Debug, Copy, Clone)]
757pub enum CipherSuite {
758    TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 =
759        nrfxlib_sys::NRF_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 as i32,
760    TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA =
761        nrfxlib_sys::NRF_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA as i32,
762    TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 =
763        nrfxlib_sys::NRF_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 as i32,
764    TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA =
765        nrfxlib_sys::NRF_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA as i32,
766    TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA = nrfxlib_sys::NRF_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA as i32,
767    TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 =
768        nrfxlib_sys::NRF_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 as i32,
769    TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA = nrfxlib_sys::NRF_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA as i32,
770    TLS_PSK_WITH_AES_256_CBC_SHA = nrfxlib_sys::NRF_TLS_PSK_WITH_AES_256_CBC_SHA as i32,
771    TLS_PSK_WITH_AES_128_CBC_SHA256 = nrfxlib_sys::NRF_TLS_PSK_WITH_AES_128_CBC_SHA256 as i32,
772    TLS_PSK_WITH_AES_128_CBC_SHA = nrfxlib_sys::NRF_TLS_PSK_WITH_AES_128_CBC_SHA as i32,
773    TLS_PSK_WITH_AES_128_CCM_8 = nrfxlib_sys::NRF_TLS_PSK_WITH_AES_128_CCM_8 as i32,
774    TLS_EMPTY_RENEGOTIATIONINFO_SCSV = nrfxlib_sys::NRF_TLS_EMPTY_RENEGOTIATIONINFO_SCSV as i32,
775    TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 =
776        nrfxlib_sys::NRF_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 as i32,
777    TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 =
778        nrfxlib_sys::NRF_TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 as i32,
779    TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 =
780        nrfxlib_sys::NRF_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 as i32,
781}
782
783#[derive(Debug, Clone)]
784#[cfg_attr(feature = "defmt", derive(defmt::Format))]
785pub enum SocketOptionError {
786    // The socket argument is not a valid file descriptor.
787    InvalidFileDescriptor,
788    // The send and receive timeout values are too big to fit into the timeout fields in the socket structure.
789    TimeoutTooBig,
790    // The specified option is invalid at the specified socket level or the socket has been shut down.
791    InvalidOption,
792    // The socket is already connected, and a specified option cannot be set while the socket is connected.
793    AlreadyConnected,
794    // The option is not supported by the protocol.
795    UnsupportedOption,
796    // The socket argument does not refer to a socket.
797    NotASocket,
798    // There was insufficient memory available for the operation to complete.
799    OutOfMemory,
800    // Insufficient resources are available in the system to complete the call.
801    OutOfResources,
802}
803
804impl From<i32> for SocketOptionError {
805    fn from(errno: i32) -> Self {
806        match errno.unsigned_abs() {
807            nrfxlib_sys::NRF_EBADF => SocketOptionError::InvalidFileDescriptor,
808            nrfxlib_sys::NRF_EINVAL => SocketOptionError::InvalidOption,
809            nrfxlib_sys::NRF_EISCONN => SocketOptionError::AlreadyConnected,
810            nrfxlib_sys::NRF_ENOPROTOOPT => SocketOptionError::UnsupportedOption,
811            nrfxlib_sys::NRF_ENOTSOCK => SocketOptionError::NotASocket,
812            nrfxlib_sys::NRF_ENOMEM => SocketOptionError::OutOfMemory,
813            nrfxlib_sys::NRF_ENOBUFS => SocketOptionError::OutOfResources,
814            _ => panic!("Unknown error code: {}", errno),
815        }
816    }
817}
818
819#[allow(clippy::declare_interior_mutable_const)]
820const ATOMIC_U8_INIT: AtomicU8 = AtomicU8::new(0);
821static ACTIVE_SPLIT_SOCKETS: [AtomicU8; nrfxlib_sys::NRF_MODEM_MAX_SOCKET_COUNT as usize] =
822    [ATOMIC_U8_INIT; nrfxlib_sys::NRF_MODEM_MAX_SOCKET_COUNT as usize];
823
824pub struct SplitSocketHandle {
825    inner: Option<Socket>,
826    index: usize,
827}
828
829impl SplitSocketHandle {
830    pub async fn deactivate(mut self) -> Result<(), Error> {
831        let mut inner = self.inner.take().unwrap();
832
833        if ACTIVE_SPLIT_SOCKETS[self.index].fetch_sub(1, Ordering::SeqCst) == 1 {
834            // We were the last handle to drop so the inner socket isn't split anymore
835            inner.split = false;
836        }
837
838        inner.deactivate().await?;
839
840        Ok(())
841    }
842
843    fn get_new_spot() -> usize {
844        for (index, count) in ACTIVE_SPLIT_SOCKETS.iter().enumerate() {
845            if count
846                .compare_exchange(0, 2, Ordering::SeqCst, Ordering::SeqCst)
847                .is_ok()
848            {
849                return index;
850            }
851        }
852
853        unreachable!("It should not be possible to have more splits than the maximum socket count");
854    }
855}
856
857impl Deref for SplitSocketHandle {
858    type Target = Socket;
859
860    fn deref(&self) -> &Self::Target {
861        self.inner.as_ref().unwrap()
862    }
863}
864
865impl Drop for SplitSocketHandle {
866    fn drop(&mut self) {
867        if let Some(inner) = self.inner.as_mut() {
868            if ACTIVE_SPLIT_SOCKETS[self.index].fetch_sub(1, Ordering::SeqCst) == 1 {
869                // We were the last handle to drop so the inner socket isn't split anymore
870                inner.split = false;
871            }
872        }
873    }
874}