From 1e0560b22ffb1a99fb098ffdcbed4f169921332c Mon Sep 17 00:00:00 2001 From: Matt Thompson Date: Tue, 11 Jul 2017 12:48:38 -0600 Subject: [PATCH 01/18] SAMD21: Fix some SPI-related issues. --- arch/arm/src/samdl/sam_serial.c | 2 +- arch/arm/src/samdl/sam_spi.c | 5 +++++ arch/arm/src/samdl/samd_clockconfig.c | 4 ++-- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/arch/arm/src/samdl/sam_serial.c b/arch/arm/src/samdl/sam_serial.c index 8c88850acc6..aab2d81d23d 100644 --- a/arch/arm/src/samdl/sam_serial.c +++ b/arch/arm/src/samdl/sam_serial.c @@ -101,7 +101,7 @@ # define USART4_ASSIGNED 1 #elif defined(CONFIG_USART5_SERIAL_CONSOLE) # define CONSOLE_DEV g_usart5port /* USART5 is console */ -# define TTYS5_DEV g_usart5port /* USART5 is ttyS0 */ +# define TTYS0_DEV g_usart5port /* USART5 is ttyS0 */ #else # undef CONSOLE_DEV /* No console */ # if defined(SAMDL_HAVE_USART0) diff --git a/arch/arm/src/samdl/sam_spi.c b/arch/arm/src/samdl/sam_spi.c index cc17758d86a..7c031edbb5f 100644 --- a/arch/arm/src/samdl/sam_spi.c +++ b/arch/arm/src/samdl/sam_spi.c @@ -1261,7 +1261,12 @@ static void spi_recvblock(struct spi_dev_s *dev, void *buffer, size_t nwords) static void spi_wait_synchronization(struct sam_spidev_s *priv) { + +#if defined(CONFIG_ARCH_FAMILY_SAMD20) while ((spi_getreg16(priv, SAM_SPI_STATUS_OFFSET) & SPI_STATUS_SYNCBUSY) != 0); +#elif defined(CONFIG_ARCH_FAMILY_SAMD21) || defined(CONFIG_ARCH_FAMILY_SAML21) + while ((spi_getreg16(priv, SAM_SPI_SYNCBUSY_OFFSET) & SPI_SYNCBUSY_ALL) != 0); +#endif } /**************************************************************************** diff --git a/arch/arm/src/samdl/samd_clockconfig.c b/arch/arm/src/samdl/samd_clockconfig.c index 6dc0f4c76fc..a17c30235c8 100644 --- a/arch/arm/src/samdl/samd_clockconfig.c +++ b/arch/arm/src/samdl/samd_clockconfig.c @@ -301,7 +301,7 @@ static inline void sam_xosc_config(void) /* Configure the XOSC clock */ - regval = BOARD_XOSC_STARTUPTIME + regval = BOARD_XOSC_STARTUPTIME; #ifdef BOARD_XOSC_ISCRYSTAL /* XOSC is a crystal */ @@ -383,7 +383,7 @@ static inline void sam_xosc32k_config(void) /* Configure XOSC32K */ - regval = BOARD_XOSC32K_STARTUPTIME + regval = BOARD_XOSC32K_STARTUPTIME; #ifdef BOARD_XOSC32K_ISCRYSTAL regval |= SYSCTRL_XOSC32K_XTALEN; From e10545493766527e4ca4a504ec8742a15775980a Mon Sep 17 00:00:00 2001 From: Gregory Nutt Date: Tue, 11 Jul 2017 14:59:42 -0600 Subject: [PATCH 02/18] ICMPv6 w/Autoconfiguration: Fix a compile issue introduced with recent change for 6LoWPAN support. --- net/icmpv6/icmpv6_input.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/net/icmpv6/icmpv6_input.c b/net/icmpv6/icmpv6_input.c index 14a87891373..bcc03429124 100644 --- a/net/icmpv6/icmpv6_input.c +++ b/net/icmpv6/icmpv6_input.c @@ -224,6 +224,7 @@ void icmpv6_input(FAR struct net_driver_s *dev) case ICMPV6_ROUTER_ADVERTISE: { FAR struct icmpv6_router_advertise_s *adv; + FAR uint8_t uint8_t *options; uint16_t pktlen; uint16_t optlen; int ndx; @@ -241,14 +242,17 @@ void icmpv6_input(FAR struct net_driver_s *dev) optlen = ICMPv6_RADV_OPTLEN(pktlen); /* We need to have a valid router advertisement with a Prefix and - * with the "A" bit set in the flags. + * with the "A" bit set in the flags. Options immediately follow + * the ICMPv6 router advertisement. */ - adv = ICMPv6RADVERTISE; + adv = ICMPv6RADVERTISE; + options = (FAR uint8_t *)adv + sizeof(struct icmpv6_router_advertise_s); + for (ndx = 0; ndx + sizeof(struct icmpv6_prefixinfo_s) <= optlen; ) { FAR struct icmpv6_prefixinfo_s *opt = - (FAR struct icmpv6_prefixinfo_s *)&adv->options[ndx]; + (FAR struct icmpv6_prefixinfo_s *)&options[ndx]; /* Is this the sought for prefix? Is it the correct size? Is * the "A" flag set? From c9666247198b9f24131dd8782f563579df86d12d Mon Sep 17 00:00:00 2001 From: Gregory Nutt Date: Tue, 11 Jul 2017 16:25:04 -0600 Subject: [PATCH 03/18] sockets: Coding fix fix + minor structuring for forthcoming socket interface logic. --- net/socket/socket.c | 112 ++++++++++++++++++++++++++++++-------------- 1 file changed, 76 insertions(+), 36 deletions(-) diff --git a/net/socket/socket.c b/net/socket/socket.c index 38407462fc5..a0db45768a8 100644 --- a/net/socket/socket.c +++ b/net/socket/socket.c @@ -200,6 +200,78 @@ static int psock_local_alloc(FAR struct socket *psock) } #endif /* CONFIG_NET_LOCAL */ +/**************************************************************************** + * Name: usrsock_socket_setup + * + * Description: + * Special socket setup may be required by user sockets. + * + * Parameters: + * domain (see sys/socket.h) + * type (see sys/socket.h) + * protocol (see sys/socket.h) + * psock A pointer to a user allocated socket structure to be initialized. + * + * Returned Value: + * 0 on success; -1 on error with errno set appropriately + * + ****************************************************************************/ + +#ifdef CONFIG_NET_USRSOCK +static int usrsock_socket_setup(int domain, int type, int protocol, + FAR struct socket *psock) +{ + int errcode; + int ret; + + switch (domain) + { + default: + break; + + case PF_INET: + case PF_INET6: + { +#ifndef CONFIG_NET_USRSOCK_UDP + if (type == SOCK_DGRAM) + { + break; + } +#endif +#ifndef CONFIG_NET_USRSOCK_TCP + if (type == SOCK_STREAM) + { + break; + } +#endif + psock->s_type = 0; + psock->s_conn = NULL; + + ret = usrsock_socket(domain, type, protocol, psock); + if (ret >= 0) + { + /* Successfully handled and opened by usrsock daemon. */ + + return OK; + } + else if (ret == -ENETDOWN) + { + /* Net down means that usrsock daemon is not running. + * Attempt to open socket with kernel networking stack. + */ + } + else + { + return ret; + } + } + } + + return OK; +} +#else +#endif /* CONFIG_NET_USRSOCK */ + /**************************************************************************** * Public Functions ****************************************************************************/ @@ -252,43 +324,11 @@ int psock_socket(int domain, int type, int protocol, FAR struct socket *psock) int errcode; #ifdef CONFIG_NET_USRSOCK - switch (domain) + ret = usrsock_socket_setup(domain, type, protocol, psock); + if (ret < 0) { - default: - break; - - case PF_INET: - case PF_INET6: - { -#ifndef CONFIG_NET_USRSOCK_UDP - if (type == SOCK_DGRAM) - break; -#endif -#ifndef CONFIG_NET_USRSOCK_TCP - if (type == SOCK_STREAM) - break; -#endif - psock->s_type = 0; - psock->s_conn = NULL; - - ret = usrsock_socket(domain, type, protocol, psock); - if (ret >= 0) - { - /* Successfully handled and opened by usrsock daemon. */ - - return OK; - } - else if (ret == -ENETDOWN) - { - /* Net down means that usrsock daemon is not running. - * Attempt to open socket with kernel networking stack. */ - } - else - { - errcode = -ret; - goto errout; - } - } + errcode = -ret; + goto errout; } #endif /* CONFIG_NET_USRSOCK */ From cfe0d253e94e75e404eaed42fd4af26f6a532d88 Mon Sep 17 00:00:00 2001 From: Gregory Nutt Date: Tue, 11 Jul 2017 23:59:08 +0000 Subject: [PATCH 04/18] icmpv6_input.c edited online with Bitbucket --- net/icmpv6/icmpv6_input.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/icmpv6/icmpv6_input.c b/net/icmpv6/icmpv6_input.c index bcc03429124..6d20160c64d 100644 --- a/net/icmpv6/icmpv6_input.c +++ b/net/icmpv6/icmpv6_input.c @@ -224,7 +224,7 @@ void icmpv6_input(FAR struct net_driver_s *dev) case ICMPV6_ROUTER_ADVERTISE: { FAR struct icmpv6_router_advertise_s *adv; - FAR uint8_t uint8_t *options; + FAR uint8_t *options; uint16_t pktlen; uint16_t optlen; int ndx; From 5448c99ff2b7fdfad20778df7251e502c867f7f4 Mon Sep 17 00:00:00 2001 From: Matt Thompson Date: Wed, 12 Jul 2017 06:44:53 -0600 Subject: [PATCH 05/18] I was having issues with the bus freezing up .. slaves holding SDL low.. so I rewrote a good portion of the interrupt logic based on the application notes from Atmel. One major improvement is using the RXNACK flag in the STATUS register, which indicates that no device responded to an address packet. Assuming that the chip will always give an interrupt status, I believe it's possible to eliminate the timer as well. --- arch/arm/src/samdl/sam_i2c_master.c | 1132 ++++++++------------------- 1 file changed, 331 insertions(+), 801 deletions(-) diff --git a/arch/arm/src/samdl/sam_i2c_master.c b/arch/arm/src/samdl/sam_i2c_master.c index 3e5a232f27d..38fc1a26b4b 100644 --- a/arch/arm/src/samdl/sam_i2c_master.c +++ b/arch/arm/src/samdl/sam_i2c_master.c @@ -3,6 +3,7 @@ * * Copyright (C) 2013-2014 Gregory Nutt. All rights reserved. * Copyright (C) 2015 Filament - www.filament.com + * Author: Matt Thompson * Author: Alan Carvalho de Assis * Author: Gregory Nutt * @@ -60,7 +61,7 @@ #include #include #include -#include +#include #include @@ -81,6 +82,7 @@ /******************************************************************************* * Pre-processor Definitions *******************************************************************************/ + /* Configuration ***************************************************************/ #ifndef CONFIG_SAM_I2C0_FREQUENCY @@ -88,26 +90,27 @@ #endif #ifndef CONFIG_SAM_I2C1_FREQUENCY - #define CONFIG_SAM_I2C1_FREQUENCY 100000 +# define CONFIG_SAM_I2C1_FREQUENCY 100000 #endif #ifndef CONFIG_SAM_I2C2_FREQUENCY - #define CONFIG_SAM_I2C2_FREQUENCY 100000 +# define CONFIG_SAM_I2C2_FREQUENCY 100000 #endif #ifndef CONFIG_SAM_I2C3_FREQUENCY - #define CONFIG_SAM_I2C3_FREQUENCY 100000 +# define CONFIG_SAM_I2C3_FREQUENCY 100000 #endif #ifndef CONFIG_SAM_I2C4_FREQUENCY - #define CONFIG_SAM_I2C4_FREQUENCY 100000 +# define CONFIG_SAM_I2C4_FREQUENCY 100000 #endif #ifndef CONFIG_SAM_I2C5_FREQUENCY - #define CONFIG_SAM_I2C5_FREQUENCY 100000 +# define CONFIG_SAM_I2C5_FREQUENCY 100000 #endif /* Driver internal definitions *************************************************/ + /* If verbose I2C debug output is enable, then allow more time before we declare * a timeout. The debug output from i2c_interrupt will really slow things down! * @@ -116,69 +119,67 @@ */ #ifdef CONFIG_DEBUG_I2C_INFO -# define I2C_TIMEOUT_MSPB (65000) /* 50 msec/byte */ +# define I2C_TIMEOUT_MSPB (65000) /* 50 msec/byte */ #else -# define I2C_TIMEOUT_MSPB (5000) /* 5 msec/byte */ +# define I2C_TIMEOUT_MSPB (5000) /* 5 msec/byte */ #endif /* Clocking to the I2C module(s) is provided by the main clock, divided down * as necessary. */ -#define I2C_MAX_FREQUENCY 66000000 /* Maximum I2C frequency */ +#define I2C_MAX_FREQUENCY 66000000 /* Maximum I2C frequency */ /******************************************************************************* * Private Types *******************************************************************************/ + /* Invariant attributes of a I2C bus */ struct i2c_attr_s { - uint8_t i2c; /* I2C device number (for debug output) */ - uint8_t sercom; /* Identifies the SERCOM peripheral */ - uint8_t irq; /* SERCOM IRQ number */ - uint8_t gclkgen; /* Source GCLK generator */ - uint8_t slowgen; /* Slow GCLK generator */ - port_pinset_t pad0; /* Pin configuration for PAD0 */ - port_pinset_t pad1; /* Pin configuration for PAD1 */ - uint32_t muxconfig; /* Pad multiplexing configuration */ - uint32_t srcfreq; /* Source clock frequency */ - uintptr_t base; /* Base address of I2C registers */ - bool runinstdby; /* Run in Stand-by ? */ - uint32_t sdaholdtime; /* Hold time after start bit */ - uint32_t speed; /* I2C Speed: Standard; Fast; High */ - bool scllowtout; /* SCL low timeout */ - uint32_t inactout; /* Inactive Bus Timeout */ - bool sclstretch; /* SCL stretch only after ACK */ - bool sclslvextout; /* SCL Slave extend timeout */ - bool sclmstextout; /* SCL Master extend timeout */ + uint8_t i2c; /* I2C device number (for debug output) */ + uint8_t sercom; /* Identifies the SERCOM peripheral */ + uint8_t irq; /* SERCOM IRQ number */ + uint8_t gclkgen; /* Source GCLK generator */ + uint8_t slowgen; /* Slow GCLK generator */ + port_pinset_t pad0; /* Pin configuration for PAD0 */ + port_pinset_t pad1; /* Pin configuration for PAD1 */ + uint32_t muxconfig; /* Pad multiplexing configuration */ + uint32_t srcfreq; /* Source clock frequency */ + uintptr_t base; /* Base address of I2C registers */ + bool runinstdby; /* Run in Stand-by ? */ + uint32_t sdaholdtime; /* Hold time after start bit */ + uint32_t speed; /* I2C Speed: Standard; Fast; High */ + bool scllowtout; /* SCL low timeout */ + uint32_t inactout; /* Inactive Bus Timeout */ + bool sclstretch; /* SCL stretch only after ACK */ + bool sclslvextout; /* SCL Slave extend timeout */ + bool sclmstextout; /* SCL Master extend timeout */ }; /* State of a I2C bus */ struct sam_i2c_dev_s { - struct i2c_dev_s dev; /* Generic I2C device */ + struct i2c_master_s dev; /* I2C master device */ const struct i2c_attr_s *attr; /* Invariant attributes of I2C device */ - struct i2c_msg_s *msg; /* Message list */ - uint32_t frequency; /* I2C transfer clock frequency */ - uint16_t address; /* Slave address */ - uint16_t flags; /* Transfer flags */ - uint8_t msgc; /* Number of message in the message list */ + struct i2c_msg_s *msg; /* Current message being processed */ + uint32_t frequency; /* I2C transfer clock frequency */ + uint16_t flags; /* Transfer flags */ - sem_t exclsem; /* Only one thread can access at a time */ - sem_t waitsem; /* Wait for I2C transfer completion */ - WDOG_ID timeout; /* Watchdog to recover from bus hangs */ - volatile int result; /* The result of the transfer */ - volatile int xfrd; /* Number of bytes transfers */ + sem_t exclsem; /* Only one thread can access at a time */ + sem_t waitsem; /* Wait for I2C transfer completion */ + volatile int result; /* The result of the transfer */ + volatile int xfrd; /* Number of bytes transfers */ /* Debug stuff */ #ifdef CONFIG_SAM_I2C_REGDEBUG - bool wrlast; /* Last was a write */ - uint32_t addrlast; /* Last address */ - uint32_t vallast; /* Last value */ - int ntimes; /* Number of times */ + bool wrlast; /* Last was a write */ + uint32_t addrlast; /* Last address */ + uint32_t vallast; /* Last value */ + int ntimes; /* Number of times */ #endif }; @@ -188,78 +189,54 @@ struct sam_i2c_dev_s /* Low-level helper functions */ -static uint8_t i2c_getreg8(struct sam_i2c_dev_s *priv, - unsigned int offset); +static uint8_t i2c_getreg8(struct sam_i2c_dev_s *priv, unsigned int offset); static void i2c_putreg8(struct sam_i2c_dev_s *priv, uint8_t regval, - unsigned int offset); -static uint16_t i2c_getreg16(struct sam_i2c_dev_s *priv, - unsigned int offset); + unsigned int offset); +static uint16_t i2c_getreg16(struct sam_i2c_dev_s *priv, unsigned int offset); static void i2c_putreg16(struct sam_i2c_dev_s *priv, uint16_t regval, - unsigned int offset); -static uint32_t i2c_getreg32(struct sam_i2c_dev_s *priv, - unsigned int offset); + unsigned int offset); +static uint32_t i2c_getreg32(struct sam_i2c_dev_s *priv, unsigned int offset); static void i2c_putreg32(struct sam_i2c_dev_s *priv, uint32_t regval, - unsigned int offset); + unsigned int offset); -static void i2c_takesem(sem_t *sem); -#define i2c_givesem(sem) (sem_post(sem)) +static void i2c_takesem(sem_t * sem); +#define i2c_givesem(sem) (sem_post(sem)) #ifdef CONFIG_SAM_I2C_REGDEBUG static bool i2c_checkreg(struct sam_i2c_dev_s *priv, bool wr, - uint32_t value, uintptr_t address); + uint32_t value, uintptr_t address); static uint32_t i2c_getabs(struct sam_i2c_dev_s *priv, uintptr_t address); static void i2c_putabs(struct sam_i2c_dev_s *priv, uintptr_t address, - uint32_t value); + uint32_t value); #else -# define i2c_checkreg(priv,wr,value,address) (false) -# define i2c_putabs(p,a,v) putreg32(v,a) -# define i2c_getabs(p,a) getreg32(a) +# define i2c_checkreg(priv,wr,value,address) (false) +# define i2c_putabs(p,a,v) putreg32(v,a) +# define i2c_getabs(p,a) getreg32(a) #endif static inline uint32_t i2c_getrel(struct sam_i2c_dev_s *priv, - unsigned int offset); + unsigned int offset); static inline void i2c_putrel(struct sam_i2c_dev_s *priv, unsigned int offset, - uint32_t value); + uint32_t value); /* I2C transfer helper functions */ static int i2c_wait_for_bus(struct sam_i2c_dev_s *priv, unsigned int size); static void i2c_wakeup(struct sam_i2c_dev_s *priv, int result); -static int i2c_interrupt(int irq, FAR void *context); -static void i2c_timeout(int argc, uint32_t arg, ...); +static int i2c_interrupt(int irq, FAR void *context, void *arg); static void i2c_startread(struct sam_i2c_dev_s *priv, struct i2c_msg_s *msg); static void i2c_startwrite(struct sam_i2c_dev_s *priv, struct i2c_msg_s *msg); static void i2c_startmessage(struct sam_i2c_dev_s *priv, struct i2c_msg_s *msg); -static int i2c_addr_response(struct sam_i2c_dev_s *priv); -/* I2C device operations */ - -static uint32_t i2c_setfrequency(FAR struct i2c_dev_s *dev, - uint32_t frequency); -static int i2c_setaddress(FAR struct i2c_dev_s *dev, int addr, int nbits); -static int i2c_write(FAR struct i2c_dev_s *dev, const uint8_t *buffer, - int buflen); -static int i2c_read(FAR struct i2c_dev_s *dev, uint8_t *buffer, int buflen); -#ifdef CONFIG_I2C_WRITEREAD -static int i2c_writeread(FAR struct i2c_dev_s *inst, const uint8_t *wbuffer, - int wbuflen, uint8_t *rbuffer, int rbuflen); -#endif -#ifdef CONFIG_I2C_TRANSFER -static int i2c_transfer(FAR struct i2c_dev_s *dev, - FAR struct i2c_msg_s *msgs, int count); -#endif -#ifdef CONFIG_I2C_SLAVE -static int i2c_setownaddress(FAR struct i2c_dev_s *dev, int addr, int nbits); -static int i2c_registercallback(FAR struct i2c_dev_s *dev, - int (*callback)(FAR void *arg), FAR void *arg); -#endif +static int sam_i2c_transfer(FAR struct i2c_master_s *dev, + FAR struct i2c_msg_s *msgs, int count); /* Initialization */ -static uint32_t i2c_hw_setfrequency(struct sam_i2c_dev_s *priv, - uint32_t frequency); +static uint32_t sam_i2c_setfrequency(struct sam_i2c_dev_s *priv, + uint32_t frequency); static void i2c_hw_initialize(struct sam_i2c_dev_s *priv, uint32_t frequency); static void i2c_wait_synchronization(struct sam_i2c_dev_s *priv); static void i2c_pad_configure(struct sam_i2c_dev_s *priv); @@ -271,24 +248,16 @@ static void i2c_pad_configure(struct sam_i2c_dev_s *priv); #ifdef SAMDL_HAVE_I2C0 static const struct i2c_attr_s g_i2c0attr = { - .i2c = 0, - .sercom = 0, - .irq = SAM_IRQ_SERCOM0, - .gclkgen = BOARD_SERCOM0_GCLKGEN, - .slowgen = BOARD_SERCOM0_SLOW_GCLKGEN, - .pad0 = BOARD_SERCOM0_PINMAP_PAD0, - .pad1 = BOARD_SERCOM0_PINMAP_PAD1, - .muxconfig = BOARD_SERCOM0_MUXCONFIG, - .srcfreq = BOARD_SERCOM0_FREQUENCY, - .base = SAM_SERCOM0_BASE, - .runinstdby = BOARD_SERCOM0_I2C_RUNINSTDBY, - .sdaholdtime = BOARD_SERCOM0_I2C_START_HOLD_TIME, - .speed = BOARD_SERCOM0_I2C_SPEED, - .scllowtout = BOARD_SERCOM0_I2C_SCL_LOW_TIMEOUT, - .inactout = BOARD_SERCOM0_I2C_INACTIVE_TIMEOUT, - .sclstretch = BOARD_SERCOM0_I2C_SCL_STRETCH_ACK, - .sclslvextout = BOARD_SERCOM0_I2C_SCL_SLAVE_EXT_TIMEOUT, - .sclmstextout = BOARD_SERCOM0_I2C_SCL_MASTER_EXT_TIMEOUT, + .i2c = 0, + .sercom = 0, + .irq = SAM_IRQ_SERCOM0, + .gclkgen = BOARD_SERCOM0_GCLKGEN, + .slowgen = BOARD_SERCOM0_SLOW_GCLKGEN, + .pad0 = BOARD_SERCOM0_PINMAP_PAD0, + .pad1 = BOARD_SERCOM0_PINMAP_PAD1, + .muxconfig = BOARD_SERCOM0_MUXCONFIG, + .srcfreq = BOARD_SERCOM0_FREQUENCY, + .base = SAM_SERCOM0_BASE, }; static struct sam_i2c_dev_s g_i2c0; @@ -296,24 +265,16 @@ static struct sam_i2c_dev_s g_i2c0; #ifdef SAMDL_HAVE_I2C1 static const struct i2c_attr_s g_i2c1attr = { - .i2c = 1, - .sercom = 1, - .irq = SAM_IRQ_SERCOM1, - .gclkgen = BOARD_SERCOM1_GCLKGEN, - .slowgen = BOARD_SERCOM1_SLOW_GCLKGEN, - .pad0 = BOARD_SERCOM1_PINMAP_PAD0, - .pad1 = BOARD_SERCOM1_PINMAP_PAD1, - .muxconfig = BOARD_SERCOM1_MUXCONFIG, - .srcfreq = BOARD_SERCOM1_FREQUENCY, - .base = SAM_SERCOM1_BASE, - .runinstdby = BOARD_SERCOM1_I2C_RUNINSTDBY, - .sdaholdtime = BOARD_SERCOM1_I2C_START_HOLD_TIME, - .speed = BOARD_SERCOM1_I2C_SPEED, - .scllowtout = BOARD_SERCOM1_I2C_SCL_LOW_TIMEOUT, - .inactout = BOARD_SERCOM1_I2C_INACTIVE_TIMEOUT, - .sclstretch = BOARD_SERCOM1_I2C_SCL_STRETCH_ACK, - .sclslvextout = BOARD_SERCOM1_I2C_SCL_SLAVE_EXT_TIMEOUT, - .sclmstextout = BOARD_SERCOM1_I2C_SCL_MASTER_EXT_TIMEOUT, + .i2c = 1, + .sercom = 1, + .irq = SAM_IRQ_SERCOM1, + .gclkgen = BOARD_SERCOM1_GCLKGEN, + .slowgen = BOARD_SERCOM1_SLOW_GCLKGEN, + .pad0 = BOARD_SERCOM1_PINMAP_PAD0, + .pad1 = BOARD_SERCOM1_PINMAP_PAD1, + .muxconfig = BOARD_SERCOM1_MUXCONFIG, + .srcfreq = BOARD_SERCOM1_FREQUENCY, + .base = SAM_SERCOM1_BASE, }; static struct sam_i2c_dev_s g_i2c1; @@ -322,24 +283,16 @@ static struct sam_i2c_dev_s g_i2c1; #ifdef SAMDL_HAVE_I2C2 static const struct i2c_attr_s g_i2c2attr = { - .i2c = 2, - .sercom = 2, - .irq = SAM_IRQ_SERCOM2, - .gclkgen = BOARD_SERCOM2_GCLKGEN, - .slowgen = BOARD_SERCOM2_SLOW_GCLKGEN, - .pad0 = BOARD_SERCOM2_PINMAP_PAD0, - .pad1 = BOARD_SERCOM2_PINMAP_PAD1, - .muxconfig = BOARD_SERCOM2_MUXCONFIG, - .srcfreq = BOARD_SERCOM2_FREQUENCY, - .base = SAM_SERCOM2_BASE, - .runinstdby = BOARD_SERCOM2_I2C_RUNINSTDBY, - .sdaholdtime = BOARD_SERCOM2_I2C_START_HOLD_TIME, - .speed = BOARD_SERCOM2_I2C_SPEED, - .scllowtout = BOARD_SERCOM2_I2C_SCL_LOW_TIMEOUT, - .inactout = BOARD_SERCOM2_I2C_INACTIVE_TIMEOUT, - .sclstretch = BOARD_SERCOM2_I2C_SCL_STRETCH_ACK, - .sclslvextout = BOARD_SERCOM2_I2C_SCL_SLAVE_EXT_TIMEOUT, - .sclmstextout = BOARD_SERCOM2_I2C_SCL_MASTER_EXT_TIMEOUT, + .i2c = 2, + .sercom = 2, + .irq = SAM_IRQ_SERCOM2, + .gclkgen = BOARD_SERCOM2_GCLKGEN, + .slowgen = BOARD_SERCOM2_SLOW_GCLKGEN, + .pad0 = BOARD_SERCOM2_PINMAP_PAD0, + .pad1 = BOARD_SERCOM2_PINMAP_PAD1, + .muxconfig = BOARD_SERCOM2_MUXCONFIG, + .srcfreq = BOARD_SERCOM2_FREQUENCY, + .base = SAM_SERCOM2_BASE, }; static struct sam_i2c_dev_s g_i2c2; @@ -348,24 +301,16 @@ static struct sam_i2c_dev_s g_i2c2; #ifdef SAMDL_HAVE_I2C3 static const struct i2c_attr_s g_i2c3attr = { - .i2c = 3, - .sercom = 3, - .irq = SAM_IRQ_SERCOM3, - .gclkgen = BOARD_SERCOM3_GCLKGEN, - .slowgen = BOARD_SERCOM3_SLOW_GCLKGEN, - .pad0 = BOARD_SERCOM3_PINMAP_PAD0, - .pad1 = BOARD_SERCOM3_PINMAP_PAD1, - .muxconfig = BOARD_SERCOM3_MUXCONFIG, - .srcfreq = BOARD_SERCOM3_FREQUENCY, - .base = SAM_SERCOM3_BASE, - .runinstdby = BOARD_SERCOM3_I2C_RUNINSTDBY, - .sdaholdtime = BOARD_SERCOM3_I2C_START_HOLD_TIME, - .speed = BOARD_SERCOM3_I2C_SPEED, - .scllowtout = BOARD_SERCOM3_I2C_SCL_LOW_TIMEOUT, - .inactout = BOARD_SERCOM3_I2C_INACTIVE_TIMEOUT, - .sclstretch = BOARD_SERCOM3_I2C_SCL_STRETCH_ACK, - .sclslvextout = BOARD_SERCOM3_I2C_SCL_SLAVE_EXT_TIMEOUT, - .sclmstextout = BOARD_SERCOM3_I2C_SCL_MASTER_EXT_TIMEOUT, + .i2c = 3, + .sercom = 3, + .irq = SAM_IRQ_SERCOM3, + .gclkgen = BOARD_SERCOM3_GCLKGEN, + .slowgen = BOARD_SERCOM3_SLOW_GCLKGEN, + .pad0 = BOARD_SERCOM3_PINMAP_PAD0, + .pad1 = BOARD_SERCOM3_PINMAP_PAD1, + .muxconfig = BOARD_SERCOM3_MUXCONFIG, + .srcfreq = BOARD_SERCOM3_FREQUENCY, + .base = SAM_SERCOM3_BASE, }; static struct sam_i2c_dev_s g_i2c3; @@ -374,24 +319,16 @@ static struct sam_i2c_dev_s g_i2c3; #ifdef SAMDL_HAVE_I2C4 static const struct i2c_attr_s g_i2c4attr = { - .i2c = 4, - .sercom = 4, - .irq = SAM_IRQ_SERCOM4, - .gclkgen = BOARD_SERCOM4_GCLKGEN, - .slowgen = BOARD_SERCOM4_SLOW_GCLKGEN, - .pad0 = BOARD_SERCOM4_PINMAP_PAD0, - .pad1 = BOARD_SERCOM4_PINMAP_PAD1, - .muxconfig = BOARD_SERCOM4_MUXCONFIG, - .srcfreq = BOARD_SERCOM4_FREQUENCY, - .base = SAM_SERCOM4_BASE, - .runinstdby = BOARD_SERCOM4_I2C_RUNINSTDBY, - .sdaholdtime = BOARD_SERCOM4_I2C_START_HOLD_TIME, - .speed = BOARD_SERCOM4_I2C_SPEED, - .scllowtout = BOARD_SERCOM4_I2C_SCL_LOW_TIMEOUT, - .inactout = BOARD_SERCOM4_I2C_INACTIVE_TIMEOUT, - .sclstretch = BOARD_SERCOM4_I2C_SCL_STRETCH_ACK, - .sclslvextout = BOARD_SERCOM4_I2C_SCL_SLAVE_EXT_TIMEOUT, - .sclmstextout = BOARD_SERCOM4_I2C_SCL_MASTER_EXT_TIMEOUT, + .i2c = 4, + .sercom = 4, + .irq = SAM_IRQ_SERCOM4, + .gclkgen = BOARD_SERCOM4_GCLKGEN, + .slowgen = BOARD_SERCOM4_SLOW_GCLKGEN, + .pad0 = BOARD_SERCOM4_PINMAP_PAD0, + .pad1 = BOARD_SERCOM4_PINMAP_PAD1, + .muxconfig = BOARD_SERCOM4_MUXCONFIG, + .srcfreq = BOARD_SERCOM4_FREQUENCY, + .base = SAM_SERCOM4_BASE, }; static struct sam_i2c_dev_s g_i2c4; @@ -400,24 +337,16 @@ static struct sam_i2c_dev_s g_i2c4; #ifdef SAMDL_HAVE_I2C5 static const struct i2c_attr_s g_i2c5attr = { - .i2c = 5, - .sercom = 5, - .irq = SAM_IRQ_SERCOM5, - .gclkgen = BOARD_SERCOM5_GCLKGEN, - .slowgen = BOARD_SERCOM5_SLOW_GCLKGEN, - .pad0 = BOARD_SERCOM5_PINMAP_PAD0, - .pad1 = BOARD_SERCOM5_PINMAP_PAD1, - .muxconfig = BOARD_SERCOM5_MUXCONFIG, - .srcfreq = BOARD_SERCOM5_FREQUENCY, - .base = SAM_SERCOM5_BASE, - .runinstdby = BOARD_SERCOM5_I2C_RUNINSTDBY, - .sdaholdtime = BOARD_SERCOM5_I2C_START_HOLD_TIME, - .speed = BOARD_SERCOM5_I2C_SPEED, - .scllowtout = BOARD_SERCOM5_I2C_SCL_LOW_TIMEOUT, - .inactout = BOARD_SERCOM5_I2C_INACTIVE_TIMEOUT, - .sclstretch = BOARD_SERCOM5_I2C_SCL_STRETCH_ACK, - .sclslvextout = BOARD_SERCOM5_I2C_SCL_SLAVE_EXT_TIMEOUT, - .sclmstextout = BOARD_SERCOM5_I2C_SCL_MASTER_EXT_TIMEOUT, + .i2c = 5, + .sercom = 5, + .irq = SAM_IRQ_SERCOM5, + .gclkgen = BOARD_SERCOM5_GCLKGEN, + .slowgen = BOARD_SERCOM5_SLOW_GCLKGEN, + .pad0 = BOARD_SERCOM5_PINMAP_PAD0, + .pad1 = BOARD_SERCOM5_PINMAP_PAD1, + .muxconfig = BOARD_SERCOM5_MUXCONFIG, + .srcfreq = BOARD_SERCOM5_FREQUENCY, + .base = SAM_SERCOM5_BASE, }; static struct sam_i2c_dev_s g_i2c5; @@ -425,46 +354,36 @@ static struct sam_i2c_dev_s g_i2c5; struct i2c_ops_s g_i2cops = { - .setfrequency = i2c_setfrequency, - .setaddress = i2c_setaddress, - .write = i2c_write, - .read = i2c_read, -#ifdef CONFIG_I2C_WRITEREAD - .writeread = i2c_writeread, -#endif -#ifdef CONFIG_I2C_TRANSFER - .transfer = i2c_transfer -#endif -#ifdef CONFIG_I2C_SLAVE - .setownaddress = i2c_setownaddress - .registercallback = i2c_registercallback + .transfer = sam_i2c_transfer, +#ifdef CONFIG_I2C_RESET + .reset = sam_i2c_reset, #endif }; -/**************************************************************************** +/******************************************************************************* * Low-level Helpers - ****************************************************************************/ + *******************************************************************************/ -/************************************************************************************ +/******************************************************************************* * Name: i2c_getreg8 * * Description: * Get a 8-bit register value by offset * - ************************************************************************************/ + *******************************************************************************/ static uint8_t i2c_getreg8(struct sam_i2c_dev_s *priv, unsigned int offset) { return getreg8(priv->attr->base + offset); } -/************************************************************************************ +/******************************************************************************* * Name: i2c_putreg8 * * Description: * Put a 8-bit register value by offset * - ************************************************************************************/ + *******************************************************************************/ static void i2c_putreg8(struct sam_i2c_dev_s *priv, uint8_t regval, unsigned int offset) @@ -472,52 +391,53 @@ static void i2c_putreg8(struct sam_i2c_dev_s *priv, uint8_t regval, putreg8(regval, priv->attr->base + offset); } -/************************************************************************************ +/******************************************************************************* * Name: i2c_getreg16 * * Description: * Get a 16-bit register value by offset * - ************************************************************************************/ -static uint16_t i2c_getreg16(struct sam_i2c_dev_s *priv, - unsigned int offset) + *******************************************************************************/ + +static uint16_t i2c_getreg16(struct sam_i2c_dev_s *priv, unsigned int offset) { return getreg16(priv->attr->base + offset); } -/************************************************************************************ +/******************************************************************************* * Name: i2c_putreg16 * * Description: * Put a 16-bit register value by offset * - ************************************************************************************/ -static void i2c_putreg16(struct sam_i2c_dev_s *priv, uint16_t regval, - unsigned int offset) + *******************************************************************************/ + +static void i2c_putreg16(struct sam_i2c_dev_s *priv, uint16_t regval, + unsigned int offset) { putreg16(regval, priv->attr->base + offset); } -/************************************************************************************ +/******************************************************************************* * Name: i2c_getreg32 * * Description: * Get a 32-bit register value by offset * - ************************************************************************************/ -static uint32_t i2c_getreg32(struct sam_i2c_dev_s *priv, - unsigned int offset) + *******************************************************************************/ + +static uint32_t i2c_getreg32(struct sam_i2c_dev_s *priv, unsigned int offset) { return getreg32(priv->attr->base + offset); } -/************************************************************************************ +/******************************************************************************* * Name: i2c_putreg32 * * Description: * Put a 32-bit register value by offset * - ************************************************************************************/ + *******************************************************************************/ static void i2c_putreg32(struct sam_i2c_dev_s *priv, uint32_t regval, unsigned int offset) @@ -525,7 +445,7 @@ static void i2c_putreg32(struct sam_i2c_dev_s *priv, uint32_t regval, putreg32(regval, priv->attr->base + offset); } -/**************************************************************************** +/******************************************************************************* * Name: i2c_takesem * * Description: @@ -538,9 +458,9 @@ static void i2c_putreg32(struct sam_i2c_dev_s *priv, uint32_t regval, * Returned Value: * None * - ****************************************************************************/ + *******************************************************************************/ -static void i2c_takesem(sem_t *sem) +static void i2c_takesem(sem_t * sem) { /* Take the semaphore (perhaps waiting) */ @@ -554,7 +474,7 @@ static void i2c_takesem(sem_t *sem) } } -/**************************************************************************** +/******************************************************************************* * Name: i2c_checkreg * * Description: @@ -568,15 +488,15 @@ static void i2c_takesem(sem_t *sem) * true: This is the first register access of this type. * false: This is the same as the preceding register access. * - ****************************************************************************/ + *******************************************************************************/ #ifdef CONFIG_SAM_I2C_REGDEBUG static bool i2c_checkreg(struct sam_i2c_dev_s *priv, bool wr, uint32_t value, uint32_t address) { - if (wr == priv->wrlast && /* Same kind of access? */ - value == priv->vallast && /* Same value? */ - address == priv->addrlast) /* Same address? */ + if (wr == priv->wrlast && /* Same kind of access? */ + value == priv->vallast && /* Same value? */ + address == priv->addrlast) /* Same address? */ { /* Yes, then just keep a count of the number of times we did this. */ @@ -608,13 +528,13 @@ static bool i2c_checkreg(struct sam_i2c_dev_s *priv, bool wr, uint32_t value, } #endif -/**************************************************************************** +/******************************************************************************* * Name: i2c_getabs * * Description: * Read any 32-bit register using an absolute * - ****************************************************************************/ + *******************************************************************************/ #ifdef CONFIG_SAM_I2C_REGDEBUG static uint32_t i2c_getabs(struct sam_i2c_dev_s *priv, uintptr_t address) @@ -630,13 +550,13 @@ static uint32_t i2c_getabs(struct sam_i2c_dev_s *priv, uintptr_t address) } #endif -/**************************************************************************** +/******************************************************************************* * Name: i2c_putabs * * Description: * Write to any 32-bit register using an absolute address * - ****************************************************************************/ + *******************************************************************************/ #ifdef CONFIG_SAM_I2C_REGDEBUG static void i2c_putabs(struct sam_i2c_dev_s *priv, uintptr_t address, @@ -651,27 +571,28 @@ static void i2c_putabs(struct sam_i2c_dev_s *priv, uintptr_t address, } #endif -/**************************************************************************** +/******************************************************************************* * Name: i2c_getrel * * Description: * Read a I2C register using an offset relative to the I2C base address * - ****************************************************************************/ + *******************************************************************************/ -static inline uint32_t i2c_getrel(struct sam_i2c_dev_s *priv, unsigned int offset) +static inline uint32_t i2c_getrel(struct sam_i2c_dev_s *priv, + unsigned int offset) { return i2c_getabs(priv, priv->attr->base + offset); } -/**************************************************************************** +/******************************************************************************* * Name: i2c_putrel * * Description: * Write a value to a I2C register using an offset relative to the I2C base * address. * - ****************************************************************************/ + *******************************************************************************/ static inline void i2c_putrel(struct sam_i2c_dev_s *priv, unsigned int offset, uint32_t value) @@ -679,15 +600,16 @@ static inline void i2c_putrel(struct sam_i2c_dev_s *priv, unsigned int offset, i2c_putabs(priv, priv->attr->base + offset, value); } -/**************************************************************************** +/******************************************************************************* * I2C transfer helper functions - ****************************************************************************/ + *******************************************************************************/ /******************************************************************************* * Name: i2c_wait_for_bus * * Description: - * Perform a I2C transfer start + * Wait for the ISR to post the semaphore, indicating the transaction is + * complete * * Assumptions: * Interrupts are disabled @@ -696,23 +618,18 @@ static inline void i2c_putrel(struct sam_i2c_dev_s *priv, unsigned int offset, static int i2c_wait_for_bus(struct sam_i2c_dev_s *priv, unsigned int size) { - int32_t timeout; - uint32_t regval; + struct timespec ts; - timeout = MSEC2TICK(I2C_TIMEOUT_MSPB); - if (timeout < 1) + clock_gettime(CLOCK_REALTIME, &ts); + ts.tv_nsec += 200e3; + + if (sem_timedwait(&priv->waitsem, (const struct timespec *)&ts) != OK) { - timeout = 1; + i2cinfo("timedwait error %d\n", errno); + return -errno; } - while (!((regval = i2c_getreg8(priv, SAM_I2C_INTFLAG_OFFSET)) & I2C_INT_MB) && - !((regval = i2c_getreg8(priv, SAM_I2C_INTFLAG_OFFSET)) & I2C_INT_SB)) - { - if (--timeout == 0) - return -ETIMEDOUT; - } - - return timeout; + return priv->result; } /******************************************************************************* @@ -725,17 +642,14 @@ static int i2c_wait_for_bus(struct sam_i2c_dev_s *priv, unsigned int size) static void i2c_wakeup(struct sam_i2c_dev_s *priv, int result) { - /* Cancel any pending timeout */ - - wd_cancel(priv->timeout); - /* Disable any further I2C interrupts */ + i2c_putreg8(priv, I2C_INT_MB | I2C_INT_SB, SAM_I2C_INTENCLR_OFFSET); /* Wake up the waiting thread with the result of the transfer */ priv->result = result; - i2c_givesem(&priv->waitsem); + sem_post(&priv->waitsem); } /******************************************************************************* @@ -748,11 +662,8 @@ static void i2c_wakeup(struct sam_i2c_dev_s *priv, int result) static int i2c_interrupt(int irq, FAR void *context, FAR void *arg) { - struct sam_i2c_dev_s *priv = ()arg; + struct sam_i2c_dev_s *priv = (struct sam_i2c_dev_s *)arg; struct i2c_msg_s *msg; - uint32_t sr; - uint32_t imr; - uint32_t pending; uint32_t regval; msg = priv->msg; @@ -765,44 +676,53 @@ static int i2c_interrupt(int irq, FAR void *context, FAR void *arg) /* Clear error INTFLAG */ - i2c_putreg16(priv, I2C_INT_ERR, SAM_I2C_INTFLAG_OFFSET); + i2c_putreg16(priv, I2C_INT_ERROR, SAM_I2C_INTFLAG_OFFSET); /* Cancel timeout */ - i2c_wakeup(priv, OK); + i2c_wakeup(priv, -EBUSY); return -EBUSY; } - /* It is a read or write operation? */ - - if (msg->flags & I2C_M_READ) + if ((i2c_getreg8(priv, SAM_I2C_INTFLAG_OFFSET) & I2C_INT_SB) == I2C_INT_SB) { - /* Clear read interrupt flag */ - //i2c_putreg8(priv, I2C_INT_SB, SAM_I2C_INTFLAG_OFFSET); - - /* Send STOP after transmit last byte */ + /* Send NACK and STOP after transmission of last byte */ if (priv->xfrd == (msg->length - 1)) { - /* Wait for sync */ + /* NACK */ + regval = i2c_getreg32(priv, SAM_I2C_CTRLB_OFFSET); + regval |= I2C_CTRLB_ACKACT; + i2c_putreg32(priv, regval, SAM_I2C_CTRLB_OFFSET); i2c_wait_synchronization(priv); - /* Send STOP condition */ + /* STOP */ regval = i2c_getreg32(priv, SAM_I2C_CTRLB_OFFSET); regval |= I2C_CTRLB_CMD_ACKSTOP; i2c_putreg32(priv, regval, SAM_I2C_CTRLB_OFFSET); + i2c_wait_synchronization(priv); + + msg->buffer[priv->xfrd++] = i2c_getreg8(priv, SAM_I2C_DATA_OFFSET); + i2c_wait_synchronization(priv); } + else + { + regval = i2c_getreg32(priv, SAM_I2C_CTRLB_OFFSET); + regval &= ~I2C_CTRLB_ACKACT; + i2c_putreg32(priv, regval, SAM_I2C_CTRLB_OFFSET); + i2c_wait_synchronization(priv); - /* Wait for Sync */ + msg->buffer[priv->xfrd++] = i2c_getreg8(priv, SAM_I2C_DATA_OFFSET); + i2c_wait_synchronization(priv); - i2c_wait_synchronization(priv); - - /* Read byte from bus */ - - msg->buffer[priv->xfrd++] = i2c_getreg8(priv, SAM_I2C_DATA_OFFSET); + regval = i2c_getreg32(priv, SAM_I2C_CTRLB_OFFSET); + regval |= I2C_CTRLB_CMD_ACKREAD; + i2c_putreg32(priv, regval, SAM_I2C_CTRLB_OFFSET); + i2c_wait_synchronization(priv); + } /* Disable Interrupt after last byte */ @@ -811,109 +731,47 @@ static int i2c_interrupt(int irq, FAR void *context, FAR void *arg) /* Cancel timeout */ i2c_wakeup(priv, OK); - i2cinfo("Got data = 0x%02X\n", msg->buffer[0]); + // i2cinfo("Got data = 0x%02X\n", msg->buffer[0]); } + + i2c_putreg8(priv, I2C_INT_SB, SAM_I2C_INTFLAG_OFFSET); } - else + + if ((i2c_getreg8(priv, SAM_I2C_INTFLAG_OFFSET) & I2C_INT_MB) == I2C_INT_MB) { - /* Wait for sync */ + /* If no device responded to the address packet, STATUS.RXNACK will be + * set + */ - i2c_wait_synchronization(priv); - - /* Write byte to bus */ - - i2c_putreg8(priv, msg->buffer[priv->xfrd], SAM_I2C_DATA_OFFSET); - priv->xfrd++; - - /* Clear write interrupt flag */ - - i2c_putreg8(priv, I2C_INT_MB, SAM_I2C_INTFLAG_OFFSET); + if ((i2c_getreg16(priv, SAM_I2C_STATUS_OFFSET) & I2C_STATUS_RXNACK) == + I2C_STATUS_RXNACK) + { + i2c_wakeup(priv, -ENODEV); + return OK; + } if (priv->xfrd == msg->length) { - /* Wait for sync */ - - i2c_wait_synchronization(priv); - /* Send STOP condition */ regval = i2c_getreg32(priv, SAM_I2C_CTRLB_OFFSET); regval |= I2C_CTRLB_CMD_ACKSTOP; i2c_putreg32(priv, regval, SAM_I2C_CTRLB_OFFSET); + i2c_wait_synchronization(priv); - /* Disable Interrupts */ - - regval = I2C_INT_MB | I2C_INT_SB; - i2c_putreg8(priv, regval, SAM_I2C_INTENCLR_OFFSET); + i2c_wakeup(priv, OK); } + else + { + i2c_putreg8(priv, msg->buffer[priv->xfrd++], SAM_I2C_DATA_OFFSET); + } + + i2c_putreg8(priv, I2C_INT_MB, SAM_I2C_INTFLAG_OFFSET); } return OK; } -/******************************************************************************* - * Name: i2c_timeout - * - * Description: - * Watchdog timer for timeout of I2C operation - * - * Assumptions: - * Called from the timer interrupt handler with interrupts disabled. - * - *******************************************************************************/ - -static void i2c_timeout(int argc, uint32_t arg, ...) -{ - struct sam_i2c_dev_s *priv = (struct sam_i2c_dev_s *)arg; - - i2cerr("ERROR: I2C%d Timeout!\n", priv->attr->i2c); - i2c_wakeup(priv, -ETIMEDOUT); -} - -/******************************************************************************* - * Name: i2c_addr_response - * - * Description: - * Detect and clear I2C address bus response or timeout - * - *******************************************************************************/ - -static int i2c_addr_response(struct sam_i2c_dev_s *priv) -{ - uint32_t regval; - - /* Verify if there are errors */ - - regval = i2c_getreg8(priv, SAM_I2C_INTFLAG_OFFSET); - if (regval & I2C_INT_SB) - { - /* Clear the interrupt flag */ - - i2c_putreg8(priv, I2C_INT_SB, SAM_I2C_INTFLAG_OFFSET); - - /* Check arbitration */ - - regval = i2c_getreg16(priv, SAM_I2C_STATUS_OFFSET); - if (regval & I2C_STATUS_ARBLOST) - { - i2cerr("ERROR: Transfer I2C Bus Collision!\n"); - return -EAGAIN; - } - } - - /* Verify if slave device reply with ACK */ - - else if ((regval = i2c_getreg16(priv, SAM_I2C_STATUS_OFFSET)) & I2C_STATUS_RXNACK) - { - /* Slave is busy, issue an ACK and STOP */ - - i2c_putreg32(priv, I2C_CTRLB_CMD_ACKSTOP, SAM_I2C_CTRLB_OFFSET); - return -EBUSY; - } - - return 0; -} - /******************************************************************************* * Name: i2c_startread * @@ -924,13 +782,12 @@ static int i2c_addr_response(struct sam_i2c_dev_s *priv) static void i2c_startread(struct sam_i2c_dev_s *priv, struct i2c_msg_s *msg) { - int ret; uint32_t regval; /* Setup for the transfer */ priv->result = -EBUSY; - priv->xfrd = 0; + priv->xfrd = 0; /* Set action to ACK */ @@ -961,6 +818,7 @@ static void i2c_startread(struct sam_i2c_dev_s *priv, struct i2c_msg_s *msg) /* Set the ADDR register */ i2c_putreg32(priv, regval, SAM_I2C_ADDR_OFFSET); + i2c_wait_synchronization(priv); } /******************************************************************************* @@ -973,13 +831,12 @@ static void i2c_startread(struct sam_i2c_dev_s *priv, struct i2c_msg_s *msg) static void i2c_startwrite(struct sam_i2c_dev_s *priv, struct i2c_msg_s *msg) { - int ret; uint32_t regval; /* Setup for the transfer */ priv->result = -EBUSY; - priv->xfrd = 0; + priv->xfrd = 0; /* Wait bus sync */ @@ -1014,6 +871,7 @@ static void i2c_startwrite(struct sam_i2c_dev_s *priv, struct i2c_msg_s *msg) /* Set the ADDR register */ i2c_putreg32(priv, regval, SAM_I2C_ADDR_OFFSET); + i2c_wait_synchronization(priv); } /******************************************************************************* @@ -1041,287 +899,7 @@ static void i2c_startmessage(struct sam_i2c_dev_s *priv, struct i2c_msg_s *msg) *******************************************************************************/ /******************************************************************************* - * Name: i2c_setfrequency - * - * Description: - * Set the frequency for the next transfer - * - *******************************************************************************/ - -static uint32_t i2c_setfrequency(FAR struct i2c_dev_s *dev, uint32_t frequency) -{ - struct sam_i2c_dev_s *priv = (struct sam_i2c_dev_s *)dev; - uint32_t actual; - - DEBUGASSERT(dev); - - i2cinfo("sercom=%d frequency=%d\n", priv->attr->sercom, frequency); - - /* Get exclusive access to the device */ - - i2c_takesem(&priv->exclsem); - - /* And setup the clock frequency */ - - actual = i2c_hw_setfrequency(priv, frequency); - i2c_givesem(&priv->exclsem); - return actual; -} - -/******************************************************************************* - * Name: i2c_setaddress - * - * Description: - * Set the I2C slave address for a subsequent read/write - * - *******************************************************************************/ - -static int i2c_setaddress(FAR struct i2c_dev_s *dev, int addr, int nbits) -{ - struct sam_i2c_dev_s *priv = (struct sam_i2c_dev_s *) dev; - - i2cinfo("I2C%d address: %02x nbits: %d\n", priv->attr->i2c, addr, nbits); - DEBUGASSERT(dev != NULL && nbits == 7); - - /* Get exclusive access to the device */ - - i2c_takesem(&priv->exclsem); - - /* Remember 7- or 10-bit address */ - - priv->address = addr; - priv->flags = (nbits == 10) ? I2C_M_TEN : 0; - - i2c_givesem(&priv->exclsem); - return OK; -} - -/******************************************************************************* - * Name: i2c_write - * - * Description: - * Send a block of data on I2C using the previously selected I2C - * frequency and slave address. - * - *******************************************************************************/ - -static int i2c_write(FAR struct i2c_dev_s *dev, const uint8_t *wbuffer, int wbuflen) -{ - struct sam_i2c_dev_s *priv = (struct sam_i2c_dev_s *) dev; - irqstate_t flags; - int ret; - - struct i2c_msg_s msg = - { - .addr = priv->address, - .flags = priv->flags, - .buffer = (uint8_t *)wbuffer, /* Override const */ - .length = wbuflen - }; - - i2cinfo("I2C%d buflen: %d\n", priv->attr->i2c, wbuflen); - DEBUGASSERT(dev != NULL); - - /* Get exclusive access to the device */ - - i2c_takesem(&priv->exclsem); - - /* Initiate the write */ - - priv->msg = &msg; - priv->msgc = 1; - - /* Initiate the write operation. The rest will be handled from interrupt - * logic. Interrupts must be disabled to prevent re-entrance from the - * interrupt level. - */ - - flags = enter_critical_section(); - i2c_startwrite(priv, &msg); - - /* And wait for the write to complete. Interrupts will be re-enabled while - * we are waiting. - */ - - ret = i2c_wait_for_bus(priv, wbuflen); - if (ret < 0) - { - i2cerr("ERROR: Transfer failed: %d\n", ret); - } - - leave_critical_section(flags); - i2c_givesem(&priv->exclsem); - return ret; -} - -/******************************************************************************* - * Name: i2c_read - * - * Description: - * Receive a block of data on I2C using the previously selected I2C - * frequency and slave address. - * - *******************************************************************************/ - -static int i2c_read(FAR struct i2c_dev_s *dev, uint8_t *rbuffer, int rbuflen) -{ - struct sam_i2c_dev_s *priv = (struct sam_i2c_dev_s *)dev; - irqstate_t flags; - int ret; - - struct i2c_msg_s msg = - { - .addr = priv->address, - .flags = priv->flags | I2C_M_READ, - .buffer = rbuffer, - .length = rbuflen - }; - - DEBUGASSERT(dev != NULL); - i2cinfo("I2C%d rbuflen: %d\n", priv->attr->i2c, rbuflen); - - /* Get exclusive access to the device */ - - i2c_takesem(&priv->exclsem); - - /* Initiate the read */ - - priv->msg = &msg; - priv->msgc = 1; - - /* Initiate the read operation. The rest will be handled from interrupt - * logic. Interrupts must be disabled to prevent re-entrance from the - * interrupt level. - */ - - flags = enter_critical_section(); - i2c_startread(priv, &msg); - - /* And wait for the read to complete. Interrupts will be re-enabled while - * we are waiting. - */ - - ret = i2c_wait_for_bus(priv, rbuflen); - if (ret < 0) - { - i2cerr("ERROR: Transfer failed: %d\n", ret); - } - - leave_critical_section(flags); - i2c_givesem(&priv->exclsem); - return ret; -} - -/******************************************************************************* - * Name: i2c_writeread - * - * Description: - * - *******************************************************************************/ - -#ifdef CONFIG_I2C_WRITEREAD -static int i2c_writeread(FAR struct i2c_dev_s *dev, const uint8_t *wbuffer, - int wbuflen, uint8_t *rbuffer, int rbuflen) -{ - struct sam_i2c_dev_s *priv = (struct sam_i2c_dev_s *)dev; - struct i2c_msg_s msgv[2]; - irqstate_t flags; - int ret; - - DEBUGASSERT(dev != NULL); - i2cinfo("I2C%d wbuflen: %d rbuflen: %d\n", priv->attr->i2c, wbuflen, rbuflen); - - /* Format two messages: The first is a write */ - - msgv[0].addr = priv->address; - msgv[0].flags = priv->flags; - msgv[0].buffer = (uint8_t *)wbuffer; /* Override const */ - msgv[0].length = wbuflen; - - /* The second is either a read (rbuflen > 0) or a write (rbuflen < 0) with - * no restart. - */ - - if (rbuflen > 0) - { - msgv[1].flags = (priv->flags | I2C_M_READ); - } - else - { - msgv[1].flags = (priv->flags | I2C_M_NORESTART), - rbuflen = -rbuflen; - } - - msgv[1].addr = priv->address; - msgv[1].buffer = rbuffer; - msgv[1].length = rbuflen; - - /* Get exclusive access to the device */ - - i2c_takesem(&priv->exclsem); - - /* Initiate the read */ - - priv->msg = msgv; - priv->msgc = 2; - - /* Initiate the write operation. The rest will be handled from interrupt - * logic. Interrupts must be disabled to prevent re-entrance from the - * interrupt level. - */ - - flags = enter_critical_section(); - i2c_startwrite(priv, msgv); - - /* And wait for the write/read to complete. Interrupts will be re-enabled - * while we are waiting. - */ - - ret = i2c_wait_for_bus(priv, wbuflen + rbuflen); - if (ret < 0) - { - i2cerr("ERROR: Transfer failed: %d\n", ret); - } - - leave_critical_section(flags); - i2c_givesem(&priv->exclsem); - return ret; -} -#endif - -/******************************************************************************* - * Name: i2c_setownaddress - * - * Description: - * - *******************************************************************************/ - -#ifdef CONFIG_I2C_SLAVE -static int i2c_setownaddress(FAR struct i2c_dev_s *dev, int addr, int nbits) -{ -#error Not implemented - return -ENOSYS; -} -#endif - -/******************************************************************************* - * Name: i2c_registercallback - * - * Description: - * - *******************************************************************************/ - -#ifdef CONFIG_I2C_SLAVE -static int i2c_registercallback((FAR struct i2c_dev_s *dev, - int (*callback)(FAR void *arg), FAR void *arg) -{ -#error Not implemented - return -ENOSYS; -} -#endif - -/******************************************************************************* - * Name: i2c_transfer + * Name: sam_i2c_transfer * * Description: * Receive a block of data on I2C using the previously selected I2C @@ -1332,18 +910,27 @@ static int i2c_registercallback((FAR struct i2c_dev_s *dev, * *******************************************************************************/ -#ifdef CONFIG_I2C_TRANSFER -static int i2c_transfer(FAR struct i2c_dev_s *dev, - FAR struct i2c_msg_s *msgs, int count) +static int sam_i2c_transfer(FAR struct i2c_master_s *dev, + FAR struct i2c_msg_s *msgs, int count) { struct sam_i2c_dev_s *priv = (struct sam_i2c_dev_s *)dev; irqstate_t flags; unsigned int size; int i; - int ret; + int ret = -EBUSY; DEBUGASSERT(dev != NULL && msgs != NULL && count > 0); - i2cinfo("I2C%d count: %d\n", priv->attr->i2c, count); + + /* Set the frequency from the first message in the msgs vector */ + + if (count) + { + if (priv->frequency != msgs->frequency) + { + sam_i2c_setfrequency(priv, msgs->frequency); + priv->frequency = msgs->frequency; + } + } /* Calculate the total transfer size so that we can calculate a reasonable * timeout value. @@ -1363,53 +950,68 @@ static int i2c_transfer(FAR struct i2c_dev_s *dev, /* Initiate the message transfer */ - priv->msg = msgs; - priv->msgc = count; - - /* Initiate the transfer. The rest will be handled from interrupt - * logic. Interrupts must be disabled to prevent re-entrance from the - * interrupt level. + /* Initiate the transfer. The rest will be handled from interrupt logic. + * Interrupts must be disabled to prevent re-entrance from the interrupt + * level. */ - flags = enter_critical_section(); - i2c_startmessage(priv, msgs); - - /* And wait for the transfers to complete. Interrupts will be re-enabled - * while we are waiting. - */ - - ret = i2c_wait_for_bus(priv, size); - if (ret < 0) + while (count--) { - i2cerr("ERROR: Transfer failed: %d\n", ret); + priv->msg = msgs; + flags = enter_critical_section(); + i2c_startmessage(priv, msgs); + + /* And wait for the transfers to complete. Interrupts will be re-enabled + * while we are waiting. + */ + + ret = i2c_wait_for_bus(priv, msgs->length); + if (ret < 0) + { +#if 0 + i2cerr("ERROR: Transfer failed: %d\n", ret); + i2cinfo("STATUS: 0x%08x\n", + i2c_getreg16(priv, SAM_I2C_STATUS_OFFSET)); + i2cinfo("INTFLAG: 0x%02x\n", + i2c_getreg8(priv, SAM_I2C_INTFLAG_OFFSET)); +#endif + leave_critical_section(flags); + i2c_givesem(&priv->exclsem); + return ret; + } + + leave_critical_section(flags); + + /* Move to the next message */ + + msgs++; } - leave_critical_section(flags); i2c_givesem(&priv->exclsem); return ret; } -#endif /******************************************************************************* * Initialization *******************************************************************************/ /******************************************************************************* - * Name: i2c_hw_setfrequency + * Name: sam_i2c_setfrequency * * Description: * Set the frequency for the next transfer * *******************************************************************************/ -static uint32_t i2c_hw_setfrequency(struct sam_i2c_dev_s *priv, uint32_t frequency) +static uint32_t sam_i2c_setfrequency(struct sam_i2c_dev_s *priv, + uint32_t frequency) { uint32_t maxfreq; uint32_t baud = 0; uint32_t baud_hs = 0; uint32_t ctrla; - i2cinfo("sercom=%d frequency=%d\n", priv->attr->sercom, frequency); + // i2cinfo("sercom=%d frequency=%d\n", priv->attr->sercom, frequency); /* Check if the configured BAUD is within the valid range */ @@ -1445,6 +1047,7 @@ static uint32_t i2c_hw_setfrequency(struct sam_i2c_dev_s *priv, uint32_t frequen else { /* Find baudrate for high speed */ + baud_hs = ((priv->attr->srcfreq * 10) / (478 * frequency)) - 1; if (baud_hs > 255) @@ -1455,7 +1058,7 @@ static uint32_t i2c_hw_setfrequency(struct sam_i2c_dev_s *priv, uint32_t frequen } /* Momentarily disable I2C while we apply the new BAUD setting (if it was - * previously enabled) + * previously enabled). */ ctrla = i2c_getreg32(priv, SAM_I2C_CTRLA_OFFSET); @@ -1468,18 +1071,23 @@ static uint32_t i2c_hw_setfrequency(struct sam_i2c_dev_s *priv, uint32_t frequen /* Set the new BAUD value */ - i2c_putreg32(priv, (uint32_t) ((baud_hs << 16) | baud), SAM_I2C_BAUD_OFFSET); + i2c_putreg32(priv, (uint32_t) ((baud_hs << 16) | baud), + SAM_I2C_BAUD_OFFSET); /* Re-enable I2C.. waiting for synchronization */ i2c_putreg32(priv, ctrla, SAM_I2C_CTRLA_OFFSET); i2c_wait_synchronization(priv); + + i2c_putreg16(priv, I2C_STATUS_BUSSTATE_IDLE, SAM_I2C_STATUS_OFFSET); + i2c_wait_synchronization(priv); } else { /* Set the new BAUD when the I2C is already disabled */ - i2c_putreg32(priv, (uint32_t) ((baud_hs << 16) | baud), SAM_I2C_BAUD_OFFSET); + i2c_putreg32(priv, (uint32_t) ((baud_hs << 16) | baud), + SAM_I2C_BAUD_OFFSET); } priv->frequency = frequency; @@ -1511,8 +1119,7 @@ static void i2c_hw_initialize(struct sam_i2c_dev_s *priv, uint32_t frequency) /* Configure the GCLKs for the SERCOM module */ - //sercom_coreclk_configure(priv->attr->sercom, priv->attr->gclkgen, false); - sam_gclk_chan_enable(priv->attr->sercom + GCLK_CHAN_SERCOM0_CORE, priv->attr->gclkgen); + sercom_coreclk_configure(priv->attr->sercom, priv->attr->gclkgen, false); sercom_slowclk_configure(priv->attr->sercom, priv->attr->slowgen); /* Check if module is enabled */ @@ -1520,9 +1127,10 @@ static void i2c_hw_initialize(struct sam_i2c_dev_s *priv, uint32_t frequency) regval = i2c_getreg32(priv, SAM_I2C_CTRLA_OFFSET); if (regval & I2C_CTRLA_ENABLE) { - i2cerr("ERROR: Cannot initialize I2C because it is already initialized!\n"); + i2cerr + ("ERROR: Cannot initialize I2C because it is already initialized!\n"); return; - } + } /* Check if reset is in progress */ @@ -1533,67 +1141,15 @@ static void i2c_hw_initialize(struct sam_i2c_dev_s *priv, uint32_t frequency) return; } - /* Set the SERCOM in I2C master mode */ - - regval = i2c_getreg32(priv, SAM_I2C_CTRLA_OFFSET); - regval &= ~I2C_CTRLA_MODE_MASK; - regval |= (I2C_CTRLA_MODE_MASTER); - i2c_putreg32(priv, regval, SAM_I2C_CTRLA_OFFSET); - /* Configure pads */ i2c_pad_configure(priv); - /* Should it run in stand-by mode ?*/ - - if (priv->attr->runinstdby) - { - ctrla = I2C_CTRLA_RUNSTDBY; - } - - /* Setup start data hold timeout */ - - ctrla |= priv->attr->sdaholdtime; - - /* Setup transfer speed */ - - ctrla |= priv->attr->speed; - - /* Setup Inactive Bus Timeout */ - - ctrla |= priv->attr->inactout; - - /* Setup SCL low timeout */ - - if (priv->attr->scllowtout) - { - ctrla |= I2C_CTRLA_LOWTOUT; - } - - /* Setup SCL clock stretch mode */ - - if (priv->attr->sclstretch) - { - ctrla |= I2C_CTRLA_SCLAM; - } - - /* Setup slave SCL low extend timeout */ - - if (priv->attr->sclslvextout) - { - ctrla |= I2C_CTRLA_SEXTTOEN; - } - - /* Setup master SCL low extend timeout */ - - if (priv->attr->sclmstextout) - { - ctrla |= I2C_CTRLA_MEXTTOEN; - } - - regval = i2c_getreg32(priv, SAM_I2C_CTRLA_OFFSET); - regval |= ctrla; - i2c_putreg32(priv, regval, SAM_I2C_CTRLA_OFFSET); + ctrla = + I2C_CTRLA_MODE_MASTER | I2C_CTRLA_RUNSTDBY | I2C_CTRLA_SPEED_FAST | + I2C_CTRLA_SDAHOLD_450NS | priv->attr->muxconfig; + i2c_putreg32(priv, ctrla, SAM_I2C_CTRLA_OFFSET); + i2c_wait_synchronization(priv); /* Enable Smart Mode */ @@ -1601,23 +1157,22 @@ static void i2c_hw_initialize(struct sam_i2c_dev_s *priv, uint32_t frequency) /* Set an initial baud value. */ - (void)i2c_hw_setfrequency(priv, 100000); + sam_i2c_setfrequency(priv, 100000); /* Enable I2C */ - regval = i2c_getreg32(priv, SAM_I2C_CTRLA_OFFSET); + regval = i2c_getreg32(priv, SAM_I2C_CTRLA_OFFSET); regval |= I2C_CTRLA_ENABLE; i2c_putreg32(priv, regval, SAM_I2C_CTRLA_OFFSET); - i2c_wait_synchronization(priv); /* Force IDLE bus state */ - regval = i2c_getreg16(priv, SAM_I2C_STATUS_OFFSET); - if (!(regval & I2C_STATUS_BUSSTATE_IDLE)) - { - i2c_putreg16(priv, I2C_STATUS_BUSSTATE_IDLE, SAM_I2C_STATUS_OFFSET); - } + i2c_putreg16(priv, I2C_STATUS_BUSSTATE_IDLE, SAM_I2C_STATUS_OFFSET); + i2c_wait_synchronization(priv); + + i2c_putreg8(priv, I2C_INT_ALL, SAM_I2C_INTENCLR_OFFSET); + i2c_putreg8(priv, I2C_INT_ALL, SAM_I2C_INTFLAG_OFFSET); /* Enable SERCOM interrupts at the NVIC */ @@ -1625,26 +1180,26 @@ static void i2c_hw_initialize(struct sam_i2c_dev_s *priv, uint32_t frequency) leave_critical_section(flags); } -/**************************************************************************** +/******************************************************************************* * Name: i2c_wait_synchronization * * Description: * Wait until the SERCOM I2C reports that it is synchronized. * - ****************************************************************************/ + *******************************************************************************/ static void i2c_wait_synchronization(struct sam_i2c_dev_s *priv) { while ((i2c_getreg16(priv, SAM_I2C_SYNCBUSY_OFFSET) & 0x7) != 0); } -/**************************************************************************** +/******************************************************************************* * Name: i2c_pad_configure * * Description: * Configure the SERCOM I2C pads. * - ****************************************************************************/ + *******************************************************************************/ static void i2c_pad_configure(struct sam_i2c_dev_s *priv) { @@ -1666,28 +1221,26 @@ static void i2c_pad_configure(struct sam_i2c_dev_s *priv) *******************************************************************************/ /******************************************************************************* - * Name: up_i2cinitialize + * Name: sam_i2c_master_initialize * * Description: * Initialize a I2C device for I2C operation * *******************************************************************************/ -struct i2c_dev_s *up_i2cinitialize(int bus) +struct i2c_master_s *sam_i2c_master_initialize(int bus) { struct sam_i2c_dev_s *priv; uint32_t frequency; irqstate_t flags; int ret = 0; - i2cinfo("Initializing I2C%d\n", bus); - #ifdef SAMDL_HAVE_I2C0 if (bus == 0) { /* Select up I2C0 and setup invariant attributes */ - priv = &g_i2c0; + priv = &g_i2c0; priv->attr = &g_i2c0attr; /* Select the (initial) I2C frequency */ @@ -1701,7 +1254,7 @@ struct i2c_dev_s *up_i2cinitialize(int bus) { /* Select up I2C1 and setup invariant attributes */ - priv = &g_i2c1; + priv = &g_i2c1; priv->attr = &g_i2c1attr; /* Select the (initial) I2C frequency */ @@ -1715,7 +1268,7 @@ struct i2c_dev_s *up_i2cinitialize(int bus) { /* Select up I2C2 and setup invariant attributes */ - priv = &g_i2c2; + priv = &g_i2c2; priv->attr = &g_i2c2attr; /* Select the (initial) I2C frequency */ @@ -1729,7 +1282,7 @@ struct i2c_dev_s *up_i2cinitialize(int bus) { /* Select up I2C3 and setup invariant attributes */ - priv = &g_i2c3; + priv = &g_i2c3; priv->attr = &g_i2c3attr; /* Select the (initial) I2C frequency */ @@ -1743,7 +1296,7 @@ struct i2c_dev_s *up_i2cinitialize(int bus) { /* Select up I2C4 and setup invariant attributes */ - priv = &g_i2c4; + priv = &g_i2c4; priv->attr = &g_i2c4attr; /* Select the (initial) I2C frequency */ @@ -1757,7 +1310,7 @@ struct i2c_dev_s *up_i2cinitialize(int bus) { /* Select up I2C5 and setup invariant attributes */ - priv = &g_i2c5; + priv = &g_i2c5; priv->attr = &g_i2c5attr; /* Select the (initial) I2C frequency */ @@ -1775,29 +1328,20 @@ struct i2c_dev_s *up_i2cinitialize(int bus) flags = enter_critical_section(); - /* Allocate a watchdog timer */ - - priv->timeout = wd_create(); - if (priv->timeout == NULL) - { - idbg("ERROR: Failed to allocate a timer\n"); - goto errout_with_irq; - } - /* Attach Interrupt Handler */ ret = irq_attach(priv->attr->irq, i2c_interrupt, priv); if (ret < 0) { - idbg("ERROR: Failed to attach irq %d\n", priv->attr->irq); - goto errout_with_wdog; + i2cerr("ERROR: Failed to attach irq %d\n", priv->attr->irq); + leave_critical_section(flags); + return NULL; } /* Initialize the I2C driver structure */ priv->dev.ops = &g_i2cops; - priv->address = 0; - priv->flags = 0; + priv->flags = 0; (void)sem_init(&priv->exclsem, 0, 1); (void)sem_init(&priv->waitsem, 0, 0); @@ -1807,28 +1351,19 @@ struct i2c_dev_s *up_i2cinitialize(int bus) i2c_hw_initialize(priv, frequency); leave_critical_section(flags); return &priv->dev; - -errout_with_wdog: - wd_delete(priv->timeout); - priv->timeout = NULL; - -errout_with_irq: - - leave_critical_section(flags); - return NULL; } /******************************************************************************* - * Name: up_i2cuninitalize + * Name: sam_i2c_uninitalize * * Description: * Uninitialize an I2C device * *******************************************************************************/ -int up_i2cuninitialize(FAR struct i2c_dev_s *dev) +int sam_i2c_uninitialize(FAR struct i2c_master_s *dev) { - struct sam_i2c_dev_s *priv = (struct sam_i2c_dev_s *) dev; + struct sam_i2c_dev_s *priv = (struct sam_i2c_dev_s *)dev; i2cinfo("I2C%d Un-initializing\n", priv->attr->i2c); @@ -1841,27 +1376,22 @@ int up_i2cuninitialize(FAR struct i2c_dev_s *dev) sem_destroy(&priv->exclsem); sem_destroy(&priv->waitsem); - /* Free the watchdog timer */ - - wd_delete(priv->timeout); - priv->timeout = NULL; - /* Detach Interrupt Handler */ (void)irq_detach(priv->attr->irq); return OK; } -/************************************************************************************ - * Name: up_i2creset +/******************************************************************************* + * Name: sam_i2c_reset * * Description: * Reset an I2C bus * - ************************************************************************************/ + *******************************************************************************/ #ifdef CONFIG_I2C_RESET -int up_i2creset(FAR struct i2c_dev_s *dev) +int sam_i2c_reset(FAR struct i2c_master_s *dev) { struct sam_i2c_dev_s *priv = (struct sam_i2c_dev_s *)dev; int ret; From 76ea6f09ec14cb186450bd85718d2c6431ca920f Mon Sep 17 00:00:00 2001 From: Janne Rosberg Date: Wed, 12 Jul 2017 07:46:46 -0600 Subject: [PATCH 06/18] SAMD/L21: Add a USB driver. Developed for Filament Inc. by Offcode, LTD. --- arch/arm/src/samdl/Kconfig | 26 + arch/arm/src/samdl/Make.defs | 4 + arch/arm/src/samdl/chip/saml_usb.h | 24 + arch/arm/src/samdl/sam_usb.c | 4134 +++++++++++++++++++++++ arch/arm/src/samdl/sam_usb.h | 99 + configs/samd21-xplained/include/board.h | 13 + configs/saml21-xplained/include/board.h | 13 + 7 files changed, 4313 insertions(+) create mode 100644 arch/arm/src/samdl/sam_usb.c create mode 100644 arch/arm/src/samdl/sam_usb.h diff --git a/arch/arm/src/samdl/Kconfig b/arch/arm/src/samdl/Kconfig index 59e444d1c64..ec8507a048b 100644 --- a/arch/arm/src/samdl/Kconfig +++ b/arch/arm/src/samdl/Kconfig @@ -466,6 +466,10 @@ config SAMDL_HAVE_TC7 bool default n +config SAMDL_HAVE_USB + bool + default n + config SAMDL_AC bool "Analog Comparator" default n @@ -737,3 +741,25 @@ config SAMDL_I2C_REGDEBUG Enable very low-level register access debug. Depends on DEBUG_I2C. endif # SAMDL_HAVE_I2C + +if SAMDL_HAVE_USB + +config SAMDL_USB_ENABLE_PPEP + bool "Enable Ping-Pong Endpoints" + default n + ---help--- + To maximize throughput, an endpoint can be configured for ping-pong + operation. When this is done the input and output endpoint with the same + address are used in the same direction. The CPU or DMA Controller can + then read/write one data buffer while the USB module writes/reads from + the other buffer. This gives double buffered communication. + +config SAMDL_USB_REGDEBUG + bool "USB register-Level Debug" + default n + depends on DEBUG_USB_INFO + ---help--- + Enable very low-level register access debug. Depends on + CONFIG_DEBUG_USB_INFO. + +endif # SAMDL_HAVE_USB diff --git a/arch/arm/src/samdl/Make.defs b/arch/arm/src/samdl/Make.defs index 188ee7d40dd..71b156c6c77 100644 --- a/arch/arm/src/samdl/Make.defs +++ b/arch/arm/src/samdl/Make.defs @@ -98,3 +98,7 @@ endif ifeq ($(CONFIG_SAMDL_HAVE_I2C),y) CHIP_CSRCS += sam_i2c_master.c endif + +ifeq ($(CONFIG_SAMDL_USB),y) +CHIP_CSRCS += sam_usb.c +endif diff --git a/arch/arm/src/samdl/chip/saml_usb.h b/arch/arm/src/samdl/chip/saml_usb.h index 190df37d9f8..29ffadde9ae 100644 --- a/arch/arm/src/samdl/chip/saml_usb.h +++ b/arch/arm/src/samdl/chip/saml_usb.h @@ -37,6 +37,7 @@ * ********************************************************************************************/ + #ifndef __ARCH_ARM_SRC_SAMDL_CHIP_SAML_USB_H #define __ARCH_ARM_SRC_SAMDL_CHIP_SAML_USB_H @@ -53,6 +54,29 @@ /******************************************************************************************** * Pre-processor Definitions ********************************************************************************************/ + +/* Capabilities and characteristics of endpoints ********************************************/ + +/* EP EP BANKS EP SIZE EP TYPE + * --- --------- --------- --------- + * 0 2 64/1023 Control/Bulk/Iso/Interrupt + * 1 2 64/1023 Control/Bulk/Iso/Interrupt + * 2 2 64/1023 Control/Bulk/Iso/Interrupt + * 3 2 64/1023 Control/Bulk/Iso/Interrupt + * 4 2 64/1023 Control/Bulk/Iso/Interrupt + * 5 2 64/1023 Control/Bulk/Iso/Interrupt + * 6 2 64/1023 Control/Bulk/Iso/Interrupt + * 7 2 64/1023 Control/Bulk/Iso/Interrupt + */ + +#define SAM_USB_NENDPOINTS (8) +#define SAM_USB_MAXPACKETSIZE(ep) (64) +#define SAM_USB_NBANKS(ep) (2) +#define SAM_USB_CONTROL(ep) (true) +#define SAM_USB_BULK(ep) (true) +#define SAM_USB_ISOCHRONOUS(ep) (true) +#define SAM_USB_INTERRUPT(ep) (true) + /* USB register offsets ********************************************************************/ /* Common USB Device/Host Register Offsets */ diff --git a/arch/arm/src/samdl/sam_usb.c b/arch/arm/src/samdl/sam_usb.c new file mode 100644 index 00000000000..f878b322d06 --- /dev/null +++ b/arch/arm/src/samdl/sam_usb.c @@ -0,0 +1,4134 @@ +/***************************************************************************** + * arch/arm/src/samdl/sam_usb.h + * + * Copyright (C) 2015 Filament - www.filament.com + * Copyright (C) 2015 Offcode Ltd. All rights reserved. + * Author: Janne Rosberg + * + * This driver is derived from the SAM34 UDP driver: + * Copyright (C) 2014 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * USB core features: + * + * - Compatible with the USB 2.1 specification + * - USB Embedded Host and Device mode + * - Supports full (12Mbit/s) and low (1.5Mbit/s) speed communication + * - Supports Link Power Management (LPM-L1) protocol + * - On-chip transceivers with built-in pull-ups and pull-downs + * - On-Chip USB serial resistors + * - 1kHz SOF clock available on external pin + * + * Device mode + * - Supports 8 IN endpoints and 8 OUT endpoints + * – No endpoint size limitations + * – Built-in DMA with multi-packet and dual bank for all endpoints + * – Supports feedback endpoint + * – Supports crystal less clock + * + * Host mode + * - Supports 8 physical pipes + * – No pipe size limitations + * – Supports multiplexed virtual pipe on one physical pipe to allow an + * unlimited USB tree + * – Built-in DMA with multi-packet support and dual bank for all pipes + * – Supports feedback endpoint + * – Supports the USB 2.0 Phase-locked SOFs feature + * + ****************************************************************************/ + + +/**************************************************************************** + * WIP NOTES: + * + * DS 38.1 (859) + * To maximize throughput, an endpoint can be configured for ping-pong + * operation. + * When this is done the input and output endpoint with the same address are + * used in the same direction. The CPU or DMA Controller can then read/write + * one data buffer while the USB module writes/reads from the other buffer. + * This gives double buffered communication. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include "up_arch.h" +#include "up_internal.h" +#include "saml_periphclks.h" +#include "sam_gclk.h" +#include "chip.h" +#include "sam_port.h" +#include "sam_pinmap.h" +#include "sam_usb.h" + +#if defined(CONFIG_USBHOST) && defined(CONFIG_SAMDL_USB) +# error USBHOST mode not yet implemented! +#endif + +#if defined(CONFIG_USBDEV) && defined(CONFIG_SAMDL_USB) + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Configuration ************************************************************/ + +#ifndef CONFIG_USBDEV_EP0_MAXSIZE +# define CONFIG_USBDEV_EP0_MAXSIZE 64 +#endif + +/* Extremely detailed register debug that you would normally never want + * enabled. + */ + +#ifndef CONFIG_DEBUG +# undef CONFIG_SAMDL_USB_REGDEBUG +#endif + +/* Driver Definitions *******************************************************/ + +#define EP0 (0) +#define SAM_EPSET_ALL (0xff) /* All endpoints */ +#define SAM_EPSET_NOTEP0 (0xfe) /* All endpoints except EP0 */ +#define SAM_EP_BIT(ep) (1 << (ep)) +#define SAM_EP0_MAXPACKET (CONFIG_USBDEV_EP0_MAXSIZE) /* EP0 Max. packet size */ +#define SAM_MAX_MULTIPACKET_SIZE (0x3fff) + +/* Request queue operations *************************************************/ + +#define sam_rqempty(q) ((q)->head == NULL) +#define sam_rqpeek(q) ((q)->head) + +/* USB trace ****************************************************************/ +/* Trace error codes */ + +#define SAM_TRACEERR_ALLOCFAIL 0x0001 +#define SAM_TRACEERR_BADCLEARFEATURE 0x0002 +#define SAM_TRACEERR_BADDEVGETSTATUS 0x0003 +#define SAM_TRACEERR_BADEPGETSTATUS 0x0004 +#define SAM_TRACEERR_BADEOBSTATE 0x0005 +#define SAM_TRACEERR_BADEPNO 0x0006 +#define SAM_TRACEERR_BADEPTYPE 0x0007 +#define SAM_TRACEERR_BADGETCONFIG 0x0008 +#define SAM_TRACEERR_BADGETSETDESC 0x0009 +#define SAM_TRACEERR_BADGETSTATUS 0x000a +#define SAM_TRACEERR_BADSETADDRESS 0x000b +#define SAM_TRACEERR_BADSETCONFIG 0x000c +#define SAM_TRACEERR_BADSETFEATURE 0x000d +#define SAM_TRACEERR_BINDFAILED 0x000e +#define SAM_TRACEERR_DISPATCHSTALL 0x000f +#define SAM_TRACEERR_DRIVER 0x0010 +#define SAM_TRACEERR_DRIVERREGISTERED 0x0011 +#define SAM_TRACEERR_EP0SETUPOUTSIZE 0x0012 +#define SAM_TRACEERR_EP0SETUPSTALLED 0x0013 +#define SAM_TRACEERR_EPOUTNULLPACKET 0x0014 +#define SAM_TRACEERR_EPRESERVE 0x0015 +#define SAM_TRACEERR_INVALIDCTRLREQ 0x0016 +#define SAM_TRACEERR_INVALIDPARMS 0x0017 +#define SAM_TRACEERR_IRQREGISTRATION 0x0018 +#define SAM_TRACEERR_NOTCONFIGURED 0x0019 +#define SAM_TRACEERR_REQABORTED 0x001a +#define SAM_TRACEERR_RXDATABKERR 0x001b +#define SAM_TRACEERR_TXCOMPERR 0x001c +#define SAM_TRACEERR_UNSUPPEPTYPE 0x001d + +/* Trace interrupt codes */ + +#define SAM_TRACEINTID_INTERRUPT 0x0001 +#define SAM_TRACEINTID_PENDING 0x0002 +#define SAM_TRACEINTID_PENDING_EP 0x0003 +#define SAM_TRACEINTID_SUSPEND 0x0004 +#define SAM_TRACEINTID_SOF 0x0005 +#define SAM_TRACEINTID_EORST 0x0006 +#define SAM_TRACEINTID_WAKEUP 0x0007 +#define SAM_TRACEINTID_EORSM 0x0008 +#define SAM_TRACEINTID_UPRSM 0x0009 +#define SAM_TRACEINTID_RAMACER 0x000a +#define SAM_TRACEINTID_LPMNYET 0x000b +#define SAM_TRACEINTID_LPMSUSP 0x000c +#define SAM_TRACEINTID_EPNO 0x000d +#define SAM_TRACEINTID_EPINTFLAGS 0x000e +#define SAM_TRACEINTID_EPTRCPT0 0x000f +#define SAM_TRACEINTID_EPTRCPT1 0x0010 +#define SAM_TRACEINTID_EPTRFAIL0 0x0011 +#define SAM_TRACEINTID_EPTRFAIL1 0x0012 +#define SAM_TRACEINTID_EPRXSTP 0x0013 +#define SAM_TRACEINTID_EPSTALL0 0x0014 +#define SAM_TRACEINTID_EPSTALL1 0x0015 +#define SAM_TRACEINTID_EPINQEMPTY 0x0016 +#define SAM_TRACEINTID_EPOUTQEMPTY 0x0017 +#define SAM_TRACEINTID_EP0SETUPOUT 0x0018 +#define SAM_TRACEINTID_EP0SETUPIN 0x0019 +#define SAM_TRACEINTID_EP0SETUPSETADDRESS 0x001a +#define SAM_TRACEINTID_NOSTDREQ 0x001b +#define SAM_TRACEINTID_GETSTATUS 0x001c +#define SAM_TRACEINTID_DEVGETSTATUS 0x001d +#define SAM_TRACEINTID_IFGETSTATUS 0x001e +#define SAM_TRACEINTID_CLEARFEATURE 0x001f +#define SAM_TRACEINTID_SETFEATURE 0x0020 +#define SAM_TRACEINTID_GETSETDESC 0x0021 +#define SAM_TRACEINTID_GETCONFIG 0x0022 +#define SAM_TRACEINTID_SETCONFIG 0x0023 +#define SAM_TRACEINTID_GETSETIF 0x0024 +#define SAM_TRACEINTID_SYNCHFRAME 0x0025 +#define SAM_TRACEINTID_DISPATCH 0x0026 +#define SAM_TRACEINTID_ADDRESSED 0x0027 +#define SAM_TRACEINTID_EPCONF 0x0028 +#define SAM_TRACEINTID_EPINTEN 0x0029 +#define SAM_TRACEINTID_EP0WRSTATUS 0x002a +#define SAM_TRACEINTID_EPTRCPT0_LEN 0x002b + +/* Ever-present MIN and MAX macros */ + +#ifndef MIN +# define MIN(a,b) (a < b ? a : b) +#endif + +#ifndef MAX +# define MAX(a,b) (a > b ? a : b) +#endif + +/* Byte ordering in host-based values */ + +#ifdef CONFIG_ENDIAN_BIG +# define LSB 1 +# define MSB 0 +#else +# define LSB 0 +# define MSB 1 +#endif + +/**************************************************************************** + * Private Type Definitions + ****************************************************************************/ + +/* State of an endpoint */ + +enum sam_epstate_e +{ + /* --- All Endpoints --- */ + USB_EPSTATE_DISABLED = 0, /* Endpoint is disabled */ + USB_EPSTATE_STALLED, /* Endpoint is stalled */ + USB_EPSTATE_IDLE, /* Endpoint is idle (i.e. ready for transmission) */ + USB_EPSTATE_SENDING, /* Endpoint is sending data */ + USB_EPSTATE_RXSTOPPED, /* OUT endpoint is stopped waiting for a read request */ + /* --- Endpoint 0 Only --- */ + USB_EPSTATE_EP0DATAOUT, /* Endpoint 0 is receiving SETUP OUT data */ + USB_EPSTATE_EP0STATUSIN, /* Endpoint 0 is sending SETUP status */ + USB_EPSTATE_EP0ADDRESS /* Address change is pending completion of status */ +}; + +/* The overall state of the device */ + +enum sam_devstate_e +{ + USB_DEVSTATE_SUSPENDED = 0, /* The device is currently suspended */ + USB_DEVSTATE_POWERED, /* Host is providing +5V through the USB cable */ + USB_DEVSTATE_DEFAULT, /* Device has been reset */ + USB_DEVSTATE_ADDRESSED, /* The device has been given an address on the bus */ + USB_DEVSTATE_CONFIGURED /* A valid configuration has been selected. */ +}; + +/* The result of EP0 SETUP processing */ + +enum sam_ep0setup_e +{ + USB_EP0SETUP_SUCCESS = 0, /* The SETUP was handle without incident */ + USB_EP0SETUP_DISPATCHED, /* The SETUP was forwarded to the class driver */ + USB_EP0SETUP_ADDRESS, /* A new device address is pending */ + USB_EP0SETUP_STALL /* An error occurred */ +}; + +union wb_u +{ + uint16_t w; + uint8_t b[2]; +}; + +/* A container for a request so that the request may be retained in a list */ + +struct sam_req_s +{ + struct usbdev_req_s req; /* Standard USB request */ + struct sam_req_s *flink; /* Supports a singly linked list */ + uint16_t inflight; /* Number of TX bytes tansmitting or + * number of RX bytes we are waiting */ +}; + +/* The head of a queue of requests */ + +struct sam_rqhead_s +{ + struct sam_req_s *head; /* Requests are added to the head of the list */ + struct sam_req_s *tail; /* Requests are removed from the tail of the list */ +}; + + +/* This is the internal representation of an endpoint */ + +struct sam_ep_s +{ + /* Common endpoint fields. This must be the first thing defined in the + * structure so that it is possible to simply cast from struct usbdev_ep_s + * to struct sam_ep_s. + */ + + struct usbdev_ep_s ep; /* Standard endpoint structure */ + + /* SAMDL-specific fields */ + + struct sam_usbdev_s *dev; /* Reference to private driver data */ + struct sam_rqhead_s reqq; /* Read/write request queue */ + struct sam_rqhead_s pendq; /* Write requests pending stall sent */ + struct usbdev_epdesc_s *descb[2]; /* Pointers to this endpoints descriptors */ + volatile uint8_t epstate; /* State of the endpoint (see enum sam_epstate_e) */ + uint8_t stalled:1; /* true: Endpoint is stalled */ + uint8_t pending:1; /* true: IN Endpoint stall is pending */ + uint8_t halted:1; /* true: Endpoint feature halted */ + uint8_t zlpsent:1; /* Zero length packet has been sent */ + uint8_t txbusy:1; /* Write request queue is busy (recursion avoidance kludge) */ + uint8_t rxactive:1; /* read request is active (for top of queue) */ +}; + +struct sam_usbdev_s +{ + /* Common device fields. This must be the first thing defined in the + * structure so that it is possible to simply cast from struct usbdev_s + * to struct sam_usbdev_s. + */ + + struct usbdev_s usbdev; + + /* The bound device class driver */ + + struct usbdevclass_driver_s *driver; + + /* USB-specific fields */ + + struct usb_ctrlreq_s ctrl; /* Last EP0 request */ + uint8_t devstate; /* State of the device (see enum sam_devstate_e) */ + uint8_t prevstate; /* Previous state of the device before SUSPEND */ + uint8_t devaddr; /* Assigned device address */ + uint8_t selfpowered:1; /* 1: Device is self powered */ + uint16_t epavail; /* Bitset of available endpoints */ + + /* The endpoint list */ + + struct sam_ep_s eplist[SAM_USB_NENDPOINTS]; + + /* Endpoint descriptors 2 banks for each endpoint */ + + struct usbdev_epdesc_s ep_descriptors[SAM_USB_NENDPOINTS * 2]; + + /* EP0 data buffer. For data that is included in an EP0 SETUP OUT + * transaction. In this case, no request is in place from the class + * driver and the incoming data is caught in this buffer. The size + * of valid data in the buffer is given by ctrlreg.len[]. For the + * case of EP0 SETUP IN transaction, the normal request mechanism is + * used and the class driver provides the buffering. + */ + + uint8_t ep0out[SAM_EP0_MAXPACKET]; +}; + + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/* Register operations ******************************************************/ + +#ifdef CONFIG_SAMDL_USB_REGDEBUG +static void sam_printreg(uintptr_t regaddr, uint32_t regval, bool iswrite); +static void sam_checkreg(uintptr_t regaddr, uint32_t regval, bool iswrite); +static uint32_t sam_getreg32(uintptr_t regaddr); +static void sam_putreg32(uint32_t regval, uintptr_t regaddr); +static uint32_t sam_getreg16(uintptr_t regaddr); +static void sam_putreg16(uint16_t regval, uintptr_t regaddr); +static uint32_t sam_getreg8(uintptr_t regaddr); +static void sam_putreg8(uint8_t regval, uintptr_t regaddr); +static void sam_dumpep(struct sam_usbdev_s *priv, uint8_t epno); +#else +static inline uint32_t sam_getreg32(uintptr_t regaddr); +static inline void sam_putreg32(uint32_t regval, uintptr_t regaddr); +static inline uint32_t sam_getreg16(uintptr_t regaddr); +static inline void sam_putreg16(uint16_t regval, uintptr_t regaddr); +static inline uint32_t sam_getreg8(uintptr_t regaddr); +static inline void sam_putreg8(uint8_t regval, uintptr_t regaddr); +# define sam_dumpep(priv,epno) +#endif + +/* Suspend/Resume Helpers ***************************************************/ + +static void sam_suspend(struct sam_usbdev_s *priv); +static void sam_resume(struct sam_usbdev_s *priv); + +/* Request Helpers **********************************************************/ + +static struct sam_req_s * + sam_req_dequeue(struct sam_rqhead_s *queue); +static void sam_req_enqueue(struct sam_rqhead_s *queue, + struct sam_req_s *req); +static void sam_req_complete(struct sam_ep_s *privep, int16_t result); +static void sam_req_wrsetup(struct sam_usbdev_s *priv, + struct sam_ep_s *privep, struct sam_req_s *privreq); +static int sam_req_write(struct sam_usbdev_s *priv, + struct sam_ep_s *privep); +static int sam_req_read(struct sam_usbdev_s *priv, + struct sam_ep_s *privep, uint16_t recvsize); +static void sam_req_cancel(struct sam_ep_s *privep, int16_t status); + + +/* Interrupt level processing ***********************************************/ + +static void sam_ep0_ctrlread(struct sam_usbdev_s *priv); +static void sam_ep0_wrstatus(struct sam_usbdev_s *priv, + const uint8_t *buffer, size_t buflen); +static void sam_ep0_dispatch(struct sam_usbdev_s *priv); +static void sam_setdevaddr(struct sam_usbdev_s *priv, uint8_t value); +static void sam_ep0_setup(struct sam_usbdev_s *priv); +static void sam_ep_interrupt(struct sam_usbdev_s *priv, int epno); +static int sam_usb_interrupt(int irq, void *context, void *arg); + +/* Endpoint helpers *********************************************************/ + +static void sam_ep_reset(struct sam_usbdev_s *priv, uint8_t epno); +static void sam_epset_reset(struct sam_usbdev_s *priv, uint16_t epset); +static int sam_ep_stall(struct sam_ep_s *privep); +static int sam_ep_resume(struct sam_ep_s *privep); +static inline struct sam_ep_s * + sam_ep_reserve(struct sam_usbdev_s *priv, uint8_t epset); +static inline void + sam_ep_unreserve(struct sam_usbdev_s *priv, + struct sam_ep_s *privep); +static inline bool + sam_ep_reserved(struct sam_usbdev_s *priv, int epno); +static int sam_ep_configure_internal(struct sam_ep_s *privep, + const struct usb_epdesc_s *desc); + +/* Endpoint operations ******************************************************/ + +static int sam_ep_configure(struct usbdev_ep_s *ep, + const struct usb_epdesc_s *desc, bool last); +static int sam_ep_disable(struct usbdev_ep_s *ep); +static struct usbdev_req_s * + sam_ep_allocreq(struct usbdev_ep_s *ep); +static void sam_ep_freereq(struct usbdev_ep_s *ep, + struct usbdev_req_s *); +static int sam_ep_submit(struct usbdev_ep_s *ep, + struct usbdev_req_s *req); +static int sam_ep_cancel(struct usbdev_ep_s *ep, + struct usbdev_req_s *req); +static int sam_ep_stallresume(struct usbdev_ep_s *ep, bool resume); + +/* USB device controller operations *****************************************/ + +static struct usbdev_ep_s * + sam_allocep(struct usbdev_s *dev, uint8_t epno, bool in, + uint8_t eptype); +static void sam_freeep(struct usbdev_s *dev, struct usbdev_ep_s *ep); +static int sam_getframe(struct usbdev_s *dev); +static int sam_wakeup(struct usbdev_s *dev); +static int sam_selfpowered(struct usbdev_s *dev, bool selfpowered); +static int sam_pullup(struct usbdev_s *dev, bool enable); + +/* Initialization/Reset *****************************************************/ + +static void sam_reset(struct sam_usbdev_s *priv); +static void sam_enableclks(void); +static void sam_disableclks(void); +static void sam_hw_setup(struct sam_usbdev_s *priv); +static void sam_sw_setup(struct sam_usbdev_s *priv); +static void sam_hw_shutdown(struct sam_usbdev_s *priv); +static void sam_sw_shutdown(struct sam_usbdev_s *priv); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* Since there is only a single USB interface, all status information can be + * be simply retained in a single global instance. + */ + +static struct sam_usbdev_s g_usbd; + +static const struct usbdev_epops_s g_epops = +{ + .configure = sam_ep_configure, + .disable = sam_ep_disable, + .allocreq = sam_ep_allocreq, + .freereq = sam_ep_freereq, +#ifdef CONFIG_USBDEV_DMA + .allocbuffer = sam_ep_allocbuffer, + .freebuffer = sam_ep_freebuffer, +#endif + .submit = sam_ep_submit, + .cancel = sam_ep_cancel, + .stall = sam_ep_stallresume, +}; + +static const struct usbdev_ops_s g_devops = +{ + .allocep = sam_allocep, + .freeep = sam_freeep, + .getframe = sam_getframe, + .wakeup = sam_wakeup, + .selfpowered = sam_selfpowered, + .pullup = sam_pullup, +}; + +/* This describes endpoint 0 */ + +static const struct usb_epdesc_s g_ep0desc = +{ + .len = USB_SIZEOF_EPDESC, + .type = USB_DESC_TYPE_ENDPOINT, + .addr = EP0, + .attr = USB_EP_ATTR_XFER_CONTROL, + // REVISIT: + .mxpacketsize = {64, 0}, + .interval = 0 +}; + +/* Device error strings that may be enabled for more desciptive USB trace + * output. + */ + +#ifdef CONFIG_USBDEV_TRACE_STRINGS +const struct trace_msg_t g_usb_trace_strings_deverror[] = +{ + TRACE_STR(SAM_TRACEERR_ALLOCFAIL), + TRACE_STR(SAM_TRACEERR_BADCLEARFEATURE), + TRACE_STR(SAM_TRACEERR_BADDEVGETSTATUS), + TRACE_STR(SAM_TRACEERR_BADEPGETSTATUS), + TRACE_STR(SAM_TRACEERR_BADEOBSTATE), + TRACE_STR(SAM_TRACEERR_BADEPNO), + TRACE_STR(SAM_TRACEERR_BADEPTYPE), + TRACE_STR(SAM_TRACEERR_BADGETCONFIG), + TRACE_STR(SAM_TRACEERR_BADGETSETDESC), + TRACE_STR(SAM_TRACEERR_BADGETSTATUS), + TRACE_STR(SAM_TRACEERR_BADSETADDRESS), + TRACE_STR(SAM_TRACEERR_BADSETCONFIG), + TRACE_STR(SAM_TRACEERR_BADSETFEATURE), + TRACE_STR(SAM_TRACEERR_BINDFAILED), + TRACE_STR(SAM_TRACEERR_DISPATCHSTALL), + TRACE_STR(SAM_TRACEERR_DRIVER), + TRACE_STR(SAM_TRACEERR_DRIVERREGISTERED), + TRACE_STR(SAM_TRACEERR_EP0SETUPOUTSIZE), + TRACE_STR(SAM_TRACEERR_EP0SETUPSTALLED), + TRACE_STR(SAM_TRACEERR_EPOUTNULLPACKET), + TRACE_STR(SAM_TRACEERR_EPRESERVE), + TRACE_STR(SAM_TRACEERR_INVALIDCTRLREQ), + TRACE_STR(SAM_TRACEERR_INVALIDPARMS), + TRACE_STR(SAM_TRACEERR_IRQREGISTRATION), + TRACE_STR(SAM_TRACEERR_NOTCONFIGURED), + TRACE_STR(SAM_TRACEERR_REQABORTED), + TRACE_STR(SAM_TRACEERR_RXDATABKERR), + TRACE_STR(SAM_TRACEERR_TXCOMPERR), + TRACE_STR(SAM_TRACEERR_UNSUPPEPTYPE), + TRACE_STR_END +}; +#endif + + +/* Interrupt event strings that may be enabled for more desciptive USB trace + * output. + */ + +#ifdef CONFIG_USBDEV_TRACE_STRINGS +const struct trace_msg_t g_usb_trace_strings_intdecode[] = +{ + TRACE_STR(SAM_TRACEINTID_INTERRUPT), + TRACE_STR(SAM_TRACEINTID_PENDING), + TRACE_STR(SAM_TRACEINTID_PENDING_EP), + TRACE_STR(SAM_TRACEINTID_SUSPEND), + TRACE_STR(SAM_TRACEINTID_SOF), + TRACE_STR(SAM_TRACEINTID_EORST), + TRACE_STR(SAM_TRACEINTID_WAKEUP), + TRACE_STR(SAM_TRACEINTID_EORSM), + TRACE_STR(SAM_TRACEINTID_UPRSM), + TRACE_STR(SAM_TRACEINTID_RAMACER), + TRACE_STR(SAM_TRACEINTID_LPMNYET), + TRACE_STR(SAM_TRACEINTID_LPMSUSP), + + TRACE_STR(SAM_TRACEINTID_EPNO), + TRACE_STR(SAM_TRACEINTID_EPINTFLAGS), + TRACE_STR(SAM_TRACEINTID_EPTRCPT0), + TRACE_STR(SAM_TRACEINTID_EPTRCPT1), + TRACE_STR(SAM_TRACEINTID_EPTRFAIL0), + TRACE_STR(SAM_TRACEINTID_EPTRFAIL1), + TRACE_STR(SAM_TRACEINTID_EPRXSTP), + TRACE_STR(SAM_TRACEINTID_EPSTALL0), + TRACE_STR(SAM_TRACEINTID_EPSTALL1), + + TRACE_STR(SAM_TRACEINTID_EPINQEMPTY), + TRACE_STR(SAM_TRACEINTID_EPOUTQEMPTY), + TRACE_STR(SAM_TRACEINTID_EP0SETUPOUT), + TRACE_STR(SAM_TRACEINTID_EP0SETUPIN), + TRACE_STR(SAM_TRACEINTID_EP0SETUPSETADDRESS), + TRACE_STR(SAM_TRACEINTID_NOSTDREQ), + TRACE_STR(SAM_TRACEINTID_GETSTATUS), + TRACE_STR(SAM_TRACEINTID_DEVGETSTATUS), + TRACE_STR(SAM_TRACEINTID_IFGETSTATUS), + TRACE_STR(SAM_TRACEINTID_CLEARFEATURE), + TRACE_STR(SAM_TRACEINTID_SETFEATURE), + TRACE_STR(SAM_TRACEINTID_GETSETDESC), + TRACE_STR(SAM_TRACEINTID_GETCONFIG), + TRACE_STR(SAM_TRACEINTID_SETCONFIG), + TRACE_STR(SAM_TRACEINTID_GETSETIF), + TRACE_STR(SAM_TRACEINTID_SYNCHFRAME), + + TRACE_STR(SAM_TRACEINTID_DISPATCH), + TRACE_STR(SAM_TRACEINTID_ADDRESSED), + TRACE_STR(SAM_TRACEINTID_EPCONF), + TRACE_STR(SAM_TRACEINTID_EPINTEN), + TRACE_STR(SAM_TRACEINTID_EP0WRSTATUS), + TRACE_STR(SAM_TRACEINTID_EPTRCPT0_LEN), + + /* + TRACE_STR(SAM_TRACEINTID_ADDRESSED), + TRACE_STR(SAM_TRACEINTID_CLEARFEATURE), + TRACE_STR(SAM_TRACEINTID_RXSUSP), + TRACE_STR(SAM_TRACEINTID_DEVGETSTATUS), + TRACE_STR(SAM_TRACEINTID_DISPATCH), + TRACE_STR(SAM_TRACEINTID_ENDBUSRES), + TRACE_STR(SAM_TRACEINTID_EP), + TRACE_STR(SAM_TRACEINTID_EP0SETUPIN), + TRACE_STR(SAM_TRACEINTID_EP0SETUPOUT), + TRACE_STR(SAM_TRACEINTID_EP0SETUPSETADDRESS), + TRACE_STR(SAM_TRACEINTID_EPGETSTATUS), + TRACE_STR(SAM_TRACEINTID_EPINQEMPTY), + TRACE_STR(SAM_TRACEINTID_EPOUTQEMPTY), + TRACE_STR(SAM_TRACEINTID_GETCONFIG), + TRACE_STR(SAM_TRACEINTID_GETSETDESC), + TRACE_STR(SAM_TRACEINTID_GETSETIF), + TRACE_STR(SAM_TRACEINTID_GETSTATUS), + TRACE_STR(SAM_TRACEINTID_IFGETSTATUS), + TRACE_STR(SAM_TRACEINTID_SOF), + TRACE_STR(SAM_TRACEINTID_NOSTDREQ), + TRACE_STR(SAM_TRACEINTID_RXDATABK0), + TRACE_STR(SAM_TRACEINTID_RXDATABK1), + TRACE_STR(SAM_TRACEINTID_RXSETUP), + TRACE_STR(SAM_TRACEINTID_SETCONFIG), + TRACE_STR(SAM_TRACEINTID_SETFEATURE), + TRACE_STR(SAM_TRACEINTID_STALLSNT), + TRACE_STR(SAM_TRACEINTID_SYNCHFRAME), + TRACE_STR(SAM_TRACEINTID_TXCOMP), + TRACE_STR(SAM_TRACEINTID_UPSTRRES), + TRACE_STR(SAM_TRACEINTID_WAKEUP), + */ + TRACE_STR_END +}; +#endif + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/**************************************************************************** + * Private Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Register Operations + ****************************************************************************/ + +/******************************************************************************* + * Name: sam_printreg + * + * Description: + * Print the SAMDL USB register access + * + *******************************************************************************/ + +#ifdef CONFIG_SAMDL_USB_REGDEBUG +static void sam_printreg(uintptr_t regaddr, uint32_t regval, bool iswrite) +{ + uinfo("%p%s%08x\n", regaddr, iswrite ? "<-" : "->", regval); +} +#endif + +/******************************************************************************* + * Name: sam_checkreg + * + * Description: + * Check if it is time to output debug information for accesses to a SAMDL + * USB registers + * + *******************************************************************************/ + +#ifdef CONFIG_SAMDL_USB_REGDEBUG +static void sam_checkreg(uintptr_t regaddr, uint32_t regval, bool iswrite) +{ + static uintptr_t prevaddr = 0; + static uint32_t preval = 0; + static uint32_t count = 0; + static bool prevwrite = false; + + /* Is this the same value that we read from/wrote to the same register last time? + * Are we polling the register? If so, suppress the output. + */ + + if (regaddr == prevaddr && regval == preval && prevwrite == iswrite) + { + /* Yes.. Just increment the count */ + + count++; + } + else + { + /* No this is a new address or value or operation. Were there any + * duplicate accesses before this one? + */ + + if (count > 0) + { + /* Yes.. Just one? */ + + if (count == 1) + { + /* Yes.. Just one */ + + sam_printreg(prevaddr, preval, prevwrite); + } + else + { + /* No.. More than one. */ + + uinfo("[repeats %d more times]\n", count); + } + } + + /* Save the new address, value, count, and operation for next time */ + + prevaddr = regaddr; + preval = regval; + count = 0; + prevwrite = iswrite; + + /* Show the new register access */ + + sam_printreg(regaddr, regval, iswrite); + } +} +#endif + +/**************************************************************************** + * Name: sam_getreg32 + * + * Description: + * Get the contents of an 32-bit SAMDL USB register + * + ****************************************************************************/ + +#ifdef CONFIG_SAMDL_USB_REGDEBUG +static uint32_t sam_getreg32(uintptr_t regaddr) +{ + /* Read the value from the register */ + + uint32_t regval = getreg32(regaddr); + + /* Check if we need to print this value */ + + sam_checkreg(regaddr, regval, false); + return regval; +} +#else +static inline uint32_t sam_getreg32(uintptr_t regaddr) +{ + return getreg32(regaddr); +} +#endif + +/**************************************************************************** + * Name: sam_putreg32 + * + * Description: + * Set the contents of an 32-bit SAMDL USB register to a value + * + ****************************************************************************/ + +#ifdef CONFIG_SAMDL_USB_REGDEBUG +static void sam_putreg32(uint32_t regval, uintptr_t regaddr) +{ + /* Check if we need to print this value */ + + sam_checkreg(regaddr, regval, true); + + /* Write the value */ + + putreg32(regval, regaddr); +} +#else +static inline void sam_putreg32(uint32_t regval, uint32_t regaddr) +{ + putreg32(regval, regaddr); +} +#endif + +/**************************************************************************** + * Name: sam_getreg16 + * + * Description: + * Get the contents of an 16-bit SAMDL USB register + * + ****************************************************************************/ + +#ifdef CONFIG_SAMDL_USB_REGDEBUG +static uint32_t sam_getreg16(uintptr_t regaddr) +{ + /* Read the value from the register */ + + uint32_t regval = getreg16(regaddr); + + /* Check if we need to print this value */ + + sam_checkreg(regaddr, regval, false); + return regval; +} +#else +static inline uint32_t sam_getreg16(uintptr_t regaddr) +{ + return getreg16(regaddr); +} +#endif + +/**************************************************************************** + * Name: sam_putreg16 + * + * Description: + * Set the contents of an 16-bit SAMDL USB register to a value + * + ****************************************************************************/ + +#ifdef CONFIG_SAMDL_USB_REGDEBUG +static void sam_putreg16(uint16_t regval, uintptr_t regaddr) +{ + /* Check if we need to print this value */ + + sam_checkreg(regaddr, regval, true); + + /* Write the value */ + + putreg16(regval, regaddr); +} +#else +static inline void sam_putreg16(uint16_t regval, uint32_t regaddr) +{ + putreg16(regval, regaddr); +} +#endif + +/**************************************************************************** + * Name: sam_getreg8 + * + * Description: + * Get the contents of an 8-bit SAMDL USB register + * + ****************************************************************************/ + +#ifdef CONFIG_SAMDL_USB_REGDEBUG +static uint32_t sam_getreg8(uintptr_t regaddr) +{ + /* Read the value from the register */ + + uint32_t regval = getreg8(regaddr); + + /* Check if we need to print this value */ + + sam_checkreg(regaddr, regval, false); + return regval; +} +#else +static inline uint32_t sam_getreg8(uintptr_t regaddr) +{ + return getreg8(regaddr); +} +#endif + +/**************************************************************************** + * Name: sam_putreg8 + * + * Description: + * Set the contents of an 8-bit SAMDL USB register to a value + * + ****************************************************************************/ + +#ifdef CONFIG_SAMDL_USB_REGDEBUG +static void sam_putreg8(uint8_t regval, uintptr_t regaddr) +{ + /* Check if we need to print this value */ + + sam_checkreg(regaddr, regval, true); + + /* Write the value */ + + putreg8(regval, regaddr); +} +#else +static inline void sam_putreg8(uint8_t regval, uint32_t regaddr) +{ + putreg8(regval, regaddr); +} +#endif + + +/**************************************************************************** + * Name: sam_dumpep + ****************************************************************************/ + +#if defined(CONFIG_SAMDL_USB_REGDEBUG) && defined(CONFIG_DEBUG) +static void sam_dumpep(struct sam_usbdev_s *priv, uint8_t epno) +{ + /* Global Registers */ + + uinfo("Global Registers:\n"); + uinfo(" CTRLB: %04x\n", sam_getreg16(SAM_USBDEV_CTRLB)); + uinfo(" FNUM: %04x\n", sam_getreg16(SAM_USBDEV_FNUM)); + uinfo(" DADD: %02x\n", sam_getreg8(SAM_USBDEV_DADD)); + uinfo(" INTEN: %04x\n", sam_getreg16(SAM_USBDEV_INTENSET)); + uinfo(" STATUS: %02x\n", sam_getreg8(SAM_USBDEV_STATUS)); + uinfo(" INTFLAG: %04x\n", sam_getreg16(SAM_USBDEV_INTFLAG)); + uinfo(" EPCFG[%d]: %02x\n", epno, sam_getreg8(SAM_USBDEV_EPCFG(epno))); + uinfo("EPSTATUS[%d]: %02x\n", epno, sam_getreg8(SAM_USBDEV_EPSTATUS(epno))); +} +#endif + +/**************************************************************************** + * Request Helpers + ****************************************************************************/ +/**************************************************************************** + * Name: sam_req_dequeue + ****************************************************************************/ + +static struct sam_req_s *sam_req_dequeue(struct sam_rqhead_s *queue) +{ + struct sam_req_s *ret = queue->head; + + if (ret) + { + queue->head = ret->flink; + if (!queue->head) + { + queue->tail = NULL; + } + + ret->flink = NULL; + } + + return ret; +} + +/**************************************************************************** + * Name: sam_req_enqueue + ****************************************************************************/ + +static void sam_req_enqueue(struct sam_rqhead_s *queue, struct sam_req_s *req) +{ + req->flink = NULL; + if (!queue->head) + { + queue->head = req; + queue->tail = req; + } + else + { + queue->tail->flink = req; + queue->tail = req; + } +} + +/**************************************************************************** + * Name: sam_req_complete + ****************************************************************************/ + +static void sam_req_complete(struct sam_ep_s *privep, int16_t result) +{ + struct sam_req_s *privreq; + irqstate_t flags; + + /* Remove the completed request at the head of the endpoint request list */ + + flags = enter_critical_section(); + privreq = sam_req_dequeue(&privep->reqq); + leave_critical_section(flags); + + if (privreq) + { + /* Save the result in the request structure */ + + privreq->req.result = result; + + /* Callback to the request completion handler */ + + privreq->flink = NULL; + privreq->req.callback(&privep->ep, &privreq->req); + + /* mark endpoint ready to next transmission */ + + privep->epstate = USB_EPSTATE_IDLE; + privep->zlpsent = false; + } +} + +/**************************************************************************** + * Name: sam_req_wrsetup + * + * Description: + * Process the next queued write request. + * + ****************************************************************************/ + +static void sam_req_wrsetup(struct sam_usbdev_s *priv, + struct sam_ep_s *privep, + struct sam_req_s *privreq) +{ + const uint8_t *buf; + uint8_t epno; + int nbytes; + uint32_t packetsize; + + /* Get the unadorned endpoint number */ + + epno = USB_EPNO(privep->ep.eplog); + + /* Get the number of bytes remaining to be sent. */ + + DEBUGASSERT(privreq->req.xfrd < privreq->req.len); + nbytes = privreq->req.len - privreq->req.xfrd; + + /* Either send the maxpacketsize(multi) or all of the remaining data in + * the request. + */ + + if (nbytes >= SAM_MAX_MULTIPACKET_SIZE) + { + nbytes = SAM_MAX_MULTIPACKET_SIZE; + } + + /* This is the new number of bytes "in-flight" */ + + privreq->inflight = nbytes; + usbtrace(TRACE_WRITE(USB_EPNO(privep->ep.eplog)), nbytes); + + /* The new buffer pointer is the start of the buffer plus the number of + * bytes successfully transferred plus the number of bytes previously + * "in-flight". + */ + + buf = privreq->req.buf + privreq->req.xfrd; + + /* setup TX transfer using ep configured maxpacket size */ + + priv->eplist[epno].descb[1]->addr = (uint32_t) buf; + packetsize = priv->eplist[epno].descb[1]->pktsize; + packetsize &= ~USBDEV_PKTSIZE_BCNT_MASK; + packetsize &= ~USBDEV_PKTSIZE_MPKTSIZE_MASK; + packetsize |= USBDEV_PKTSIZE_BCNT(nbytes); + + /* also set automatic ZLP sending if requested on req */ + + if (privreq->req.flags & USBDEV_REQFLAGS_NULLPKT) + { + packetsize |= USBDEV_PKTSIZE_AUTOZLP; + } + + priv->eplist[epno].descb[1]->pktsize = packetsize; + + /* Indicate that we are in the sending state + * This indication will be need in interrupt processing (TRCPT1) + * in order to properly terminate the request. + */ + + privep->epstate = USB_EPSTATE_SENDING; + + /* Set BK1RDY to notify the USB hardware that TX data is ready on + * descriptor bank1. We will be notified that the descriptor has been + * transmitted by the USB device when TRCPT1 in the endpoint's EPINTFLAG + * register has been set. + */ + + sam_putreg8(USBDEV_EPSTATUS_BK1RDY, SAM_USBDEV_EPSTATUSSET(epno)); +} + +/**************************************************************************** + * Name: sam_req_write + * + * Description: + * Process the next queued write request. This function is called in one + * of three contexts: + * (1) When the endpoint is IDLE and a new write request is submitted + * (with interrupts disabled), + * (2) from TRCPT1 interrupt handling when the current Tx transfer + * completes + * (3) when resuming a stalled IN or control endpoint. + * + * Calling rules: + * + * The transfer state must IDLE + * + * When a request is queued, the request 'len' is the number of bytes + * to transfer and 'xfrd' and 'inflight' must be zero. + * + * When this function starts a transfer it will update the request + * 'inflight' field to indicate the size of the transfer. + * + * When the transfer completes, the 'inflight' field must hold the + * number of bytes that have completed the transfer. This function will + * update 'xfrd' with the new size of the transfer. + * + ****************************************************************************/ + +static int sam_req_write(struct sam_usbdev_s *priv, struct sam_ep_s *privep) +{ + struct sam_req_s *privreq; + uint8_t epno; + int bytesleft; + + /* Get the unadorned endpoint number */ + + epno = USB_EPNO(privep->ep.eplog); + + /* We get here when an IN endpoint interrupt occurs. So now we know that + * there is no TX transfer in progress (epstate should be IDLE). + */ + + DEBUGASSERT(privep->epstate == USB_EPSTATE_IDLE); + while (privep->epstate == USB_EPSTATE_IDLE) + { + /* Check the request from the head of the endpoint request queue */ + + privreq = sam_rqpeek(&privep->reqq); + if (!privreq) + { + /* There is no TX transfer in progress and no new pending TX + * requests to send. + */ + + usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_EPINQEMPTY), epno); + + /* Was there a pending endpoint stall? */ + + if (privep->pending) + { + /* Yes... stall the endpoint now */ + + (void)sam_ep_stall(privep); + } + + return -ENOENT; + } + + uinfo("epno=%d req=%p: len=%d xfrd=%d inflight=%d\n", + epno, privreq, privreq->req.len, privreq->req.xfrd, + privreq->inflight); + + /* Handle any bytes in flight. */ + + privreq->req.xfrd += privreq->inflight; + privreq->inflight = 0; + + /* Get the number of bytes left to be sent in the packet */ + + bytesleft = privreq->req.len - privreq->req.xfrd; + if (bytesleft > 0) + { + /* Perform the write operation. epstate will become SENDING. */ + + sam_req_wrsetup(priv, privep, privreq); + } + + /* No data to send... + * This can happen on one of two ways: + * (1) The last packet sent was the final packet of a transfer. + * Or + * (2) called with a request packet that has len == 0 + * + * len == 0 means that it is requested to send a zero length packet + * required by protocoll + */ + + else if ((privreq->req.len == 0) && !privep->zlpsent) + { + /* If we get here, we requested to send the zero length packet now. + */ + + privep->epstate = USB_EPSTATE_SENDING; + privep->zlpsent = true; + privreq->inflight = 0; + + usbtrace(TRACE_WRITE(epno), 0); + + /* setup 0 length TX transfer */ + + priv->eplist[0].descb[1]->addr = (uint32_t) &priv->ep0out[0]; + priv->eplist[0].descb[1]->pktsize &= ~USBDEV_PKTSIZE_BCNT_MASK; + priv->eplist[0].descb[1]->pktsize &= ~USBDEV_PKTSIZE_MPKTSIZE_MASK; + priv->eplist[0].descb[1]->pktsize |= USBDEV_PKTSIZE_BCNT(0); + sam_putreg8(USBDEV_EPSTATUS_BK1RDY, SAM_USBDEV_EPSTATUSSET(epno)); + } + + /* If all of the bytes were sent (including any final zero length + * packet) then we are finished with the request buffer and we can + * return the request buffer to the class driver. The state will + * remain IDLE only if nothing else was put in flight. + * + * Note that we will then loop to check to check the next queued + * write request. + */ + + if (privep->epstate == USB_EPSTATE_IDLE) + { + /* Return the write request to the class driver. Set the txbusy + * bit to prevent being called recursively from any new submission + * generated by returning the write request. + */ + + usbtrace(TRACE_COMPLETE(epno), privreq->req.xfrd); + DEBUGASSERT(privreq->req.len == privreq->req.xfrd); + + privep->txbusy = true; + sam_req_complete(privep, OK); + privep->txbusy = false; + } + } + + return OK; +} + +/**************************************************************************** + * Name: sam_req_read + * + * Description: + * Complete the last read request. full or partial. + * The USB core has transferred the data to user request buffer. + * return the completed read request to the class + * implementation, and try to start the next queued read request. + * + * REVISIT: + * This function is called in one of two contexts: + * The normal case is + * (1) When the endpoint is IDLE and a new read request is submitted + * (with interrupts disabled), + * (2) from interrupt handling when the current RX transfer completes. + * But there is also a special case + * (3) when the OUT endpoint is stopped because there are no + * available read requests. + * + * Calling rules: + * + * The transfer state must IDLE + * + * When a request is queued, the request 'len' is size of the request + * buffer. Any OUT request can be received that will fit in this + * buffer. 'xfrd' and 'inflight' in the request must be zero + * If sam_req_read() is called to start a new transfer, the recvsize + * parameter must be zero. + * + * When the transfer completes, the 'recvsize' is the number of bytes + * ready on req->buffer. + * + ****************************************************************************/ + +static int sam_req_read(struct sam_usbdev_s *priv, struct sam_ep_s *privep, + uint16_t recvsize) +{ + struct sam_req_s *privreq; + uint32_t packetsize; + int epno; + + DEBUGASSERT(priv && privep && privep->epstate == USB_EPSTATE_IDLE); + + /* Check the request from the head of the endpoint request queue */ + + epno = USB_EPNO(privep->ep.eplog); + do + { + /* Peek at the next read request in the request queue */ + + privreq = sam_rqpeek(&privep->reqq); + if (!privreq) + { + /* No read request to receive data */ + + usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_EPOUTQEMPTY), epno); + + /* When no read requests are pending no EP descriptors are set to + * ready. HW sends NAK to host if it tries to send something. + */ + + privep->epstate = USB_EPSTATE_RXSTOPPED; + return -ENOENT; + } + + uinfo("EP%d: req.len=%d xfrd=%d recvsize=%d\n", + epno, privreq->req.len, privreq->req.xfrd, recvsize); + + /* Ignore any attempt to receive a zero length packet */ + + if (privreq->req.len == 0) + { + usbtrace(TRACE_DEVERROR(SAM_TRACEERR_EPOUTNULLPACKET), 0); + sam_req_complete(privep, OK); + privreq = NULL; + } + + /* complete read request with available data */ + + if ((privreq->inflight) && (recvsize != 0)) + { + usbtrace(TRACE_READ(USB_EPNO(privep->ep.eplog)), recvsize); + + /* Update the total number of bytes transferred */ + + privreq->req.xfrd += recvsize; + privreq->inflight = 0; + usbtrace(TRACE_COMPLETE(epno), privreq->req.xfrd); + sam_req_complete(privep, OK); + + /* need to set recvsize to zero. When calling sam_req_complete() + * class driver could call submit() again and we have new request + * ready on next while() loop. + */ + + privep->rxactive = false; + recvsize = 0; + privreq = NULL; + } + } + while (privreq == NULL); + + DEBUGASSERT(recvsize == 0); + + /* activate new read request from queue */ + + privep->rxactive = true; + privreq->req.xfrd = 0; + privreq->inflight = privreq->req.len; + priv->eplist[epno].descb[0]->addr = (uint32_t) privreq->req.buf; + packetsize = priv->eplist[epno].descb[0]->pktsize; + packetsize &= ~USBDEV_PKTSIZE_BCNT_MASK; + packetsize &= ~USBDEV_PKTSIZE_MPKTSIZE_MASK; + packetsize |= USBDEV_PKTSIZE_MPKTSIZE(privreq->inflight); + sam_putreg8(USBDEV_EPSTATUS_BK0RDY, SAM_USBDEV_EPSTATUSCLR(epno)); + + return OK; +} + +/**************************************************************************** + * Name: sam_req_cancel + ****************************************************************************/ + +static void sam_req_cancel(struct sam_ep_s *privep, int16_t result) +{ + /* Complete every queued request with the specified status */ + + while (!sam_rqempty(&privep->reqq)) + { + usbtrace(TRACE_COMPLETE(USB_EPNO(privep->ep.eplog)), + (sam_rqpeek(&privep->reqq))->req.xfrd); + sam_req_complete(privep, result); + } +} + +/**************************************************************************** + * Name: sam_ep_configure_internal + * + * Description: + * This is the internal implementation of the endpoint configuration logic + * and implements the endpoint configuration method of the usbdev_ep_s + * interface. As an internal interface, it will be used to configure + * endpoint 0 which is not available to the class implementation. + * + ****************************************************************************/ + +static int sam_ep_configure_internal(struct sam_ep_s *privep, + const struct usb_epdesc_s *desc) +{ + uint8_t epconf; + uint16_t maxpacket; + uint8_t epno; + uint8_t eptype; + uint8_t intflags; + uint32_t ephwsize; + bool dirin; + + DEBUGASSERT(privep && privep->dev && desc); + + uinfo("len: %02x type: %02x addr: %02x attr: %02x " + "maxpacketsize: %02x %02x interval: %02x\n", + desc->len, desc->type, desc->addr, desc->attr, + desc->mxpacketsize[0], desc->mxpacketsize[1], + desc->interval); + + /* Decode the endpoint descriptor */ + + epno = USB_EPNO(desc->addr); + dirin = (desc->addr & USB_DIR_MASK) == USB_REQ_DIR_IN; + eptype = (desc->attr & USB_EP_ATTR_XFERTYPE_MASK); + maxpacket = GETUINT16(desc->mxpacketsize); + + if (maxpacket <= 8) + { + ephwsize = USBDEV_PKTSIZE_SIZE_8B; + } + else if (maxpacket <= 16) + { + ephwsize = USBDEV_PKTSIZE_SIZE_16B; + } + else if (maxpacket <= 32) + { + ephwsize = USBDEV_PKTSIZE_SIZE_32B; + } + else if (maxpacket <= 64) + { + ephwsize = USBDEV_PKTSIZE_SIZE_64B; + } + else if ((maxpacket <= 128) && (eptype == USB_EP_ATTR_XFER_ISOC)) + { + ephwsize = USBDEV_PKTSIZE_SIZE_128B; + } + else if ((maxpacket <= 256) && (eptype == USB_EP_ATTR_XFER_ISOC)) + { + ephwsize = USBDEV_PKTSIZE_SIZE_256B; + } + else if ((maxpacket <= 512) && (eptype == USB_EP_ATTR_XFER_ISOC)) + { + ephwsize = USBDEV_PKTSIZE_SIZE_512B; + } + else if ((maxpacket <= 1023) && (eptype == USB_EP_ATTR_XFER_ISOC)) + { + ephwsize = USBDEV_PKTSIZE_SIZE_1023B; + } + else + { + return -EINVAL; + } + + /* update endpoint descriptors to correct size */ + + privep->descb[0]->pktsize = ephwsize; + privep->descb[1]->pktsize = ephwsize; + + /* Initialize the endpoint structure */ + + privep->ep.eplog = desc->addr; /* Includes direction */ + privep->ep.maxpacket = maxpacket; + privep->epstate = USB_EPSTATE_IDLE; + + /* get current config IN and OUT */ + + epconf = 0x00; + sam_putreg8(0x00, SAM_USBDEV_EPCFG(epno)); + //epconf = sam_getreg8(SAM_USBDEV_EPCFG(epno)); + //uinfo("BEFORE: epconf=0x%X\n", epconf); + + if (dirin) + { + /* disable bank1 (IN) */ + + //epconf &= ~USBDEV_EPCFG_EPTYPE1_MASK; + intflags = USBDEV_EPINT_TRCPT1 | /*USBDEV_EPINT_TRFAIL1 |*/ USBDEV_EPINT_STALL1; + } + else + { + /* disable bank0 (OUT) */ + //epconf &= ~USBDEV_EPCFG_EPTYPE0_MASK; + intflags = USBDEV_EPINT_TRCPT0 | /*USBDEV_EPINT_TRFAIL0 |*/ USBDEV_EPINT_STALL0; + } + + /* write back disabled config */ + +//sam_putreg8(epconf, SAM_USBDEV_EPCFG(epno)); + sam_putreg8(0x7e, SAM_USBDEV_EPINTENCLR(epno)); + sam_putreg8(0x7e, SAM_USBDEV_EPINTFLAG(epno)); +//sam_putreg8(intflags, SAM_USBDEV_EPINTENCLR(epno)); +//sam_putreg8(intflags, SAM_USBDEV_EPINTFLAG(epno)); + + /* re-configure and enable the endpoint */ + + //epconf = SAM_USBDEV_EPCFG(epno); + switch (eptype) + { + case USB_EP_ATTR_XFER_CONTROL: + { + epconf = USBDEV_EPCCFG_EPTYPE0_CTRLOUT | USBDEV_EPCCFG_EPTYPE1_CTRLIN; + + /* Also enable IN interrupts */ + + intflags = USBDEV_EPINT_TRCPT0 | /* USBDEV_EPINT_TRFAIL1 |*/ + USBDEV_EPINT_STALL0; + intflags |= USBDEV_EPINT_TRCPT1 | /* USBDEV_EPINT_TRFAIL0 |*/ + USBDEV_EPINT_STALL1; + intflags |= USBDEV_EPINT_RXSTP; + sam_putreg8(USBDEV_EPSTATUS_BK0RDY, SAM_USBDEV_EPSTATUSSET(0)); + sam_putreg8(USBDEV_EPSTATUS_BK1RDY, SAM_USBDEV_EPSTATUSCLR(0)); + } + break; + +#ifdef CONFIG_USBDEV_ISOCHRONOUS + case USB_EP_ATTR_XFER_ISOC: + if (dirin) + { + epconf |= USBDEV_EPCCFG_EPTYPE1_ISOCIN; + } + else + { + epconf |= USBDEV_EPCCFG_EPTYPE0_ISOCOUT; + } + break; +#endif + + case USB_EP_ATTR_XFER_BULK: + if (dirin) + { + epconf |= USBDEV_EPCCFG_EPTYPE1_BULKIN; + } + else + { + epconf |= USBDEV_EPCCFG_EPTYPE0_BULKOUT; + } + break; + + case USB_EP_ATTR_XFER_INT: + if (dirin) + { + epconf |= USBDEV_EPCCFG_EPTYPE1_INTIN; + } + else + { + epconf |= USBDEV_EPCCFG_EPTYPE0_INTOUT; + } + break; + + default: + usbtrace(TRACE_DEVERROR(SAM_TRACEERR_BADEPTYPE), + eptype >> USB_EP_ATTR_XFERTYPE_SHIFT); + return -EINVAL; + } + + + sam_putreg8(epconf, SAM_USBDEV_EPCFG(epno)); + + /* Enable endpoint interrupts */ + + sam_putreg8(intflags, SAM_USBDEV_EPINTENSET(epno)); + usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_EPCONF), epno << 8 | epconf); + usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_EPINTEN), epno << 8 | intflags); + + sam_dumpep(privep->dev, epno); + return OK; +} + +/**************************************************************************** + * Name: sam_ep_reserve + * + * Description: + * Find and un-reserved endpoint number and reserve it for the caller. + * + ****************************************************************************/ + +static inline struct sam_ep_s * +sam_ep_reserve(struct sam_usbdev_s *priv, uint8_t epset) +{ + struct sam_ep_s *privep = NULL; + irqstate_t flags; + int epndx = 0; + + flags = enter_critical_section(); + epset &= priv->epavail; + if (epset) + { + /* Select the lowest bit in the set of matching, available endpoints + * (skipping EP0) + */ + + for (epndx = 1; epndx < SAM_USB_NENDPOINTS; epndx++) + { + uint8_t bit = SAM_EP_BIT(epndx); + if ((epset & bit) != 0) + { + /* Mark the endpoint no longer available */ + + priv->epavail &= ~bit; + + /* And return the pointer to the standard endpoint structure */ + + privep = &priv->eplist[epndx]; + break; + } + } + } + + leave_critical_section(flags); + return privep; +} + +/**************************************************************************** + * Name: sam_ep_unreserve + * + * Description: + * The endpoint is no long in-used. It will be unreserved and can be + * re-used if needed. + * + ****************************************************************************/ + +static inline void +sam_ep_unreserve(struct sam_usbdev_s *priv, struct sam_ep_s *privep) +{ + irqstate_t flags = enter_critical_section(); + priv->epavail |= SAM_EP_BIT(USB_EPNO(privep->ep.eplog)); + leave_critical_section(flags); +} + +/**************************************************************************** + * Name: sam_ep_reserved + * + * Description: + * Check if the endpoint has already been allocated. + * + ****************************************************************************/ + +static inline bool +sam_ep_reserved(struct sam_usbdev_s *priv, int epno) +{ + return ((priv->epavail & SAM_EP_BIT(epno)) == 0); +} + +/**************************************************************************** + * Endpoint operations + ****************************************************************************/ +/**************************************************************************** + * Name: sam_ep_configure + * + * Description: + * This is the endpoint configuration method of the usbdev_ep_s interface. + * + ****************************************************************************/ + +static int sam_ep_configure(struct usbdev_ep_s *ep, + const struct usb_epdesc_s *desc, + bool last) +{ + struct sam_ep_s *privep = (struct sam_ep_s *)ep; + int ret; + + /* Verify parameters. Endpoint 0 is not available at this interface */ + +#if defined(CONFIG_DEBUG) || defined(CONFIG_USBDEV_TRACE) + uint8_t epno = USB_EPNO(desc->addr); + usbtrace(TRACE_EPCONFIGURE, (uint16_t)epno); + + DEBUGASSERT(ep && desc && epno > 0 && epno < SAM_USB_NENDPOINTS); + DEBUGASSERT(epno == USB_EPNO(ep->eplog)); +#endif + + /* This logic is implemented in sam_ep_configure_internal */ + + ret = sam_ep_configure_internal(privep, desc); + + /* If this was the last endpoint, then the class driver is fully + * configured. + */ + + if (ret == OK && last) + { + struct sam_usbdev_s *priv = privep->dev; + + /* Go to the configured state (we should have been in the addressed + * state) + */ + + DEBUGASSERT(priv && priv->devstate == USB_DEVSTATE_ADDRESSED); + priv->devstate = USB_DEVSTATE_CONFIGURED; + } + + return ret; +} + +/**************************************************************************** + * Name: sam_ep_disable + * + * Description: + * This is the disable() method of the USB device endpoint structure. + * + ****************************************************************************/ + +static int sam_ep_disable(struct usbdev_ep_s *ep) +{ + struct sam_ep_s *privep = (struct sam_ep_s *)ep; + struct sam_usbdev_s *priv; + irqstate_t flags; + uint8_t epno; + +#ifdef CONFIG_DEBUG + if (!ep) + { + usbtrace(TRACE_DEVERROR(SAM_TRACEERR_INVALIDPARMS), 0); + uerr("ERROR: ep=%p\n", ep); + return -EINVAL; + } +#endif + + epno = USB_EPNO(ep->eplog); + usbtrace(TRACE_EPDISABLE, epno); + + /* Reset the endpoint and cancel any ongoing activity */ + + flags = enter_critical_section(); + priv = privep->dev; + sam_ep_reset(priv, epno); + + /* Revert to the addressed-but-not-configured state */ + + sam_setdevaddr(priv, priv->devaddr); + leave_critical_section(flags); + return OK; +} + +/**************************************************************************** + * Name: sam_ep_allocreq + * + * Description: + * This is the allocreq() method of the USB device endpoint structure. + * + ****************************************************************************/ + +static struct usbdev_req_s *sam_ep_allocreq(struct usbdev_ep_s *ep) +{ + struct sam_req_s *privreq; + +#ifdef CONFIG_DEBUG + if (!ep) + { + usbtrace(TRACE_DEVERROR(SAM_TRACEERR_INVALIDPARMS), 0); + return NULL; + } +#endif + + usbtrace(TRACE_EPALLOCREQ, USB_EPNO(ep->eplog)); + + privreq = (struct sam_req_s *)kmm_malloc(sizeof(struct sam_req_s)); + if (!privreq) + { + usbtrace(TRACE_DEVERROR(SAM_TRACEERR_ALLOCFAIL), 0); + return NULL; + } + + memset(privreq, 0, sizeof(struct sam_req_s)); + return &privreq->req; +} + +/**************************************************************************** + * Name: sam_ep_freereq + * + * Description: + * This is the freereq() method of the USB device endpoint structure. + * + ****************************************************************************/ + +static void sam_ep_freereq(struct usbdev_ep_s *ep, struct usbdev_req_s *req) +{ + struct sam_req_s *privreq = (struct sam_req_s*)req; + +#ifdef CONFIG_DEBUG + if (!ep || !req) + { + usbtrace(TRACE_DEVERROR(SAM_TRACEERR_INVALIDPARMS), 0); + return; + } +#endif + usbtrace(TRACE_EPFREEREQ, USB_EPNO(ep->eplog)); + + kmm_free(privreq); +} + +#if 0 // FIXME, not required, check... + +/**************************************************************************** + * Name: sam_ep_allocbuffer + * + * Description: + * This is the allocbuffer() method of the USB device endpoint structure. + * + ****************************************************************************/ + +#ifdef CONFIG_USBDEV_DMA +static void *sam_ep_allocbuffer(struct usbdev_ep_s *ep, uint16_t nbytes) +{ + /* There is not special buffer allocation requirement */ + + return kumm_malloc(nbytes); +} +#endif + +/**************************************************************************** + * Name: sam_ep_freebuffer + * + * Description: + * This is the freebuffer() method of the USB device endpoint structure. + * + ****************************************************************************/ + +#ifdef CONFIG_USBDEV_DMA +static void sam_ep_freebuffer(struct usbdev_ep_s *ep, void *buf) +{ + /* There is not special buffer allocation requirement */ + + kumm_free(buf); +} +#endif + +#endif + +/**************************************************************************** + * Name: sam_ep_submit + * + * Description: + * This is the submit() method of the USB device endpoint structure. + * + ****************************************************************************/ + +static int sam_ep_submit(struct usbdev_ep_s *ep, struct usbdev_req_s *req) +{ + struct sam_req_s *privreq = (struct sam_req_s *)req; + struct sam_ep_s *privep = (struct sam_ep_s *)ep; + struct sam_usbdev_s *priv; + irqstate_t flags; + uint8_t epno; + int ret = OK; + +#ifdef CONFIG_DEBUG + if (!req || !req->callback || !req->buf || !ep) + { + usbtrace(TRACE_DEVERROR(SAM_TRACEERR_INVALIDPARMS), 0); + uerr("ERROR: req=%p callback=%p buf=%p ep=%p\n", req, req->callback, + req->buf, ep); + return -EINVAL; + } +#endif + + usbtrace(TRACE_EPSUBMIT, USB_EPNO(ep->eplog)); + priv = privep->dev; + +#ifdef CONFIG_DEBUG + if (!priv->driver) + { + usbtrace(TRACE_DEVERROR(SAM_TRACEERR_NOTCONFIGURED), priv->usbdev.speed); + uerr("ERROR: driver=%p\n", priv->driver); + return -ESHUTDOWN; + } +#endif + + /* Handle the request from the class driver */ + + epno = USB_EPNO(ep->eplog); + req->result = -EINPROGRESS; + req->xfrd = 0; + privreq->inflight = 0; + flags = enter_critical_section(); + + /* Handle IN (device-to-host) requests. NOTE: If the class device is + * using the bi-directional EP0, then we assume that they intend the EP0 + * IN functionality (EP0 SETUP OUT data receipt does not use requests). + */ + + if (USB_ISEPIN(ep->eplog) || epno == EP0) + { + /* Check if the endpoint is stalled (or there is a stall pending) */ + + if (privep->stalled || privep->pending) + { + /* Yes.. in this case, save the new they will get in a special + * "pending" they will get queue until the stall is cleared. + */ + + uinfo("Pending stall clear\n"); + sam_req_enqueue(&privep->pendq, privreq); + usbtrace(TRACE_INREQQUEUED(epno), req->len); + ret = OK; + } + + else + { + /* Add the new request to the request queue for the IN endpoint */ + + sam_req_enqueue(&privep->reqq, privreq); + usbtrace(TRACE_INREQQUEUED(epno), req->len); + + /* If the IN endpoint is IDLE and there is not write queue + * processing in progress, then transfer the data now. + */ + + if (privep->epstate == USB_EPSTATE_IDLE && !privep->txbusy) + { + ret = sam_req_write(priv, privep); + } + } + } + + /* Handle OUT (host-to-device) requests */ + + else + { + /* Add the new request to the request queue for the OUT endpoint */ + + sam_req_enqueue(&privep->reqq, privreq); + usbtrace(TRACE_OUTREQQUEUED(epno), req->len); + + /* Check if we have stopped RX receipt due to lack of read + * requests. In that case we are not receiving anything from host. + * and HW sends NAK to host. see sam_req_read() + * so this "state" is actually not required (at least yet) + */ + + if (privep->epstate == USB_EPSTATE_RXSTOPPED) + { + privep->epstate = USB_EPSTATE_IDLE; + } + + /* start new read if no active yet */ + + if (!privep->rxactive) + { + ret = sam_req_read(priv, privep, 0); + } + + } + + leave_critical_section(flags); + return ret; +} + +/**************************************************************************** + * Name: sam_ep_cancel + ****************************************************************************/ + +static int sam_ep_cancel(struct usbdev_ep_s *ep, struct usbdev_req_s *req) +{ + struct sam_ep_s *privep = (struct sam_ep_s *)ep; + irqstate_t flags; + +#ifdef CONFIG_DEBUG + if (!ep || !req) + { + usbtrace(TRACE_DEVERROR(SAM_TRACEERR_INVALIDPARMS), 0); + return -EINVAL; + } +#endif + usbtrace(TRACE_EPCANCEL, USB_EPNO(ep->eplog)); + + flags = enter_critical_section(); + sam_req_cancel(privep, -EAGAIN); + leave_critical_section(flags); + return OK; +} + +/**************************************************************************** + * Name: sam_ep_stallresume + ****************************************************************************/ + +static int sam_ep_stallresume(struct usbdev_ep_s *ep, bool resume) +{ + struct sam_ep_s *privep; + uint8_t epno; + irqstate_t flags; + int ret; + +#ifdef CONFIG_DEBUG + if (!ep) + { + usbtrace(TRACE_DEVERROR(SAM_TRACEERR_INVALIDPARMS), 0); + return -EINVAL; + } +#endif + + /* Handle the resume condition */ + + privep = (struct sam_ep_s *)ep; + if (resume) + { + ret = sam_ep_resume(privep); + } + + /* Handle the stall condition */ + + else + { + /* If this is an IN endpoint (and not EP0) and if there are queued + * write requests, then we cannot stall now. Perhaps this is a + * protocol stall. In that case, we will need to drain the write + * requests before sending the stall. + */ + + flags = enter_critical_section(); + epno = USB_EPNO(ep->eplog); + if (epno != 0 && USB_ISEPIN(ep->eplog)) + { + /* Are there any unfinished write requests in the request queue? */ + + if (!sam_rqempty(&privep->reqq)) + { + /* Just set a flag to indicate that the endpoint must be + * stalled on the next TRCPTx interrupt when the request + * queue becomes empty. + */ + + privep->pending = true; + leave_critical_section(flags); + return OK; + } + } + + /* Not an IN endpoint, endpoint 0, or no pending write requests. + * Stall the endpoint now. + */ + + ret = sam_ep_stall(privep); + leave_critical_section(flags); + } + + return ret; +} + +/**************************************************************************** + * Device Controller Operations + ****************************************************************************/ +/**************************************************************************** + * Name: sam_allocep + * + * Description: + * This is the allocep() method of the USB device driver interface + * + ****************************************************************************/ + +static struct usbdev_ep_s *sam_allocep(struct usbdev_s *dev, uint8_t epno, + bool in, uint8_t eptype) +{ + struct sam_usbdev_s *priv = (struct sam_usbdev_s *)dev; + struct sam_ep_s *privep = NULL; + uint16_t epset = SAM_EPSET_NOTEP0; + + usbtrace(TRACE_DEVALLOCEP, (uint16_t)epno); +#ifdef CONFIG_DEBUG + if (!dev) + { + usbtrace(TRACE_DEVERROR(SAM_TRACEERR_INVALIDPARMS), 0); + return NULL; + } +#endif + + /* Ignore any direction bits in the logical address */ + + epno = USB_EPNO(epno); + + /* A logical address of 0 means that any endpoint will do */ + + if (epno > 0) + { + /* Otherwise, we will return the endpoint structure only for the requested + * 'logical' endpoint. All of the other checks will still be performed. + * + * First, verify that the logical endpoint is in the range supported by + * by the hardware. + */ + + if (epno >= SAM_USB_NENDPOINTS) + { + usbtrace(TRACE_DEVERROR(SAM_TRACEERR_BADEPNO), (uint16_t)epno); + return NULL; + } + + /* Convert the logical address to a physical OUT endpoint address and + * remove all of the candidate endpoints from the bitset except for the + * the IN/OUT pair for this logical address. + */ + + epset = SAM_EP_BIT(epno); + } + + /* Check if the selected endpoint number is available */ + + privep = sam_ep_reserve(priv, epset); + if (!privep) + { + usbtrace(TRACE_DEVERROR(SAM_TRACEERR_EPRESERVE), (uint16_t)epset); + return NULL; + } + + return &privep->ep; +} + +/**************************************************************************** + * Name: sam_freeep + * + * Description: + * This is the freeep() method of the USB device driver interface + * + ****************************************************************************/ + +static void sam_freeep(struct usbdev_s *dev, struct usbdev_ep_s *ep) +{ + struct sam_usbdev_s *priv; + struct sam_ep_s *privep; + +#ifdef CONFIG_DEBUG + if (!dev || !ep) + { + usbtrace(TRACE_DEVERROR(SAM_TRACEERR_INVALIDPARMS), 0); + return; + } +#endif + priv = (struct sam_usbdev_s *)dev; + privep = (struct sam_ep_s *)ep; + usbtrace(TRACE_DEVFREEEP, (uint16_t)USB_EPNO(ep->eplog)); + + if (priv && privep) + { + /* Mark the endpoint as available */ + + sam_ep_unreserve(priv, privep); + } +} + +/**************************************************************************** + * Name: sam_getframe + * + * Description: + * This is the getframe() method of the USB device driver interface + * + ****************************************************************************/ + +static int sam_getframe(struct usbdev_s *dev) +{ + uint32_t regval; + uint16_t frameno; + +#ifdef CONFIG_DEBUG + if (!dev) + { + usbtrace(TRACE_DEVERROR(SAM_TRACEERR_INVALIDPARMS), 0); + return -EINVAL; + } +#endif + + /* Return the last frame number detected by the hardware */ + + regval = sam_getreg16(SAM_USBDEV_FNUM); + frameno = (regval & USBDEV_FNUM_MASK) >> USBDEV_FNUM_SHIFT; + + usbtrace(TRACE_DEVGETFRAME, frameno); + return frameno; +} + +/**************************************************************************** + * Name: sam_wakeup + * + * Description: + * This is the wakeup() method of the USB device driver interface + * + ****************************************************************************/ + +static int sam_wakeup(struct usbdev_s *dev) +{ + struct sam_usbdev_s *priv = (struct sam_usbdev_s *)dev; + irqstate_t flags; + uint16_t regval; + + usbtrace(TRACE_DEVWAKEUP, 0); +#ifdef CONFIG_DEBUG + if (!dev) + { + usbtrace(TRACE_DEVERROR(SAM_TRACEERR_INVALIDPARMS), 0); + return -EINVAL; + } +#endif + + /* Resume normal operation */ + + flags = enter_critical_section(); + sam_resume(priv); + + /* Activate a remote wakeup. (aka upstream resume) + * Setting the Remote Wakeup bit in CTRLB.UPRSM starts the + * Remote Wake Up procedure. + * + * This will automatically be done by the controller after 5 ms of + * inactivity on the USB bus. + * + * When the controller sends the Upstream Resume INTFLAG.WAKEUP is set + * and INTFLAG.SUSPEND is cleared. + * The CTRLB.UPRSM is cleared at the end of the transmitting Upstream Resume. + */ + + regval = sam_getreg16(SAM_USBDEV_CTRLB); + regval |= USBDEV_CTRLB_UPRSM; + sam_putreg16(regval, SAM_USBDEV_CTRLB); + + leave_critical_section(flags); + return OK; +} + +/**************************************************************************** + * Name: sam_selfpowered + * + * Description: + * This is the selfpowered() method of the USB device driver interface + * + ****************************************************************************/ + +static int sam_selfpowered(struct usbdev_s *dev, bool selfpowered) +{ + struct sam_usbdev_s *priv = (struct sam_usbdev_s *)dev; + + usbtrace(TRACE_DEVSELFPOWERED, (uint16_t)selfpowered); + +#ifdef CONFIG_DEBUG + if (!dev) + { + usbtrace(TRACE_DEVERROR(SAM_TRACEERR_INVALIDPARMS), 0); + return -ENODEV; + } +#endif + + priv->selfpowered = selfpowered; + return OK; +} + + +/**************************************************************************** + * Suspend/Resume Helpers + ****************************************************************************/ +/**************************************************************************** + * Name: sam_suspend + ****************************************************************************/ + +static void sam_suspend(struct sam_usbdev_s *priv) +{ + /* Don't do anything if the device is already suspended */ + + if (priv->devstate != USB_DEVSTATE_SUSPENDED) + { + /* Notify the class driver of the suspend event */ + + if (priv->driver) + { + CLASS_SUSPEND(priv->driver, &priv->usbdev); + } + + /* Switch to the Suspended state */ + + priv->prevstate = priv->devstate; + priv->devstate = USB_DEVSTATE_SUSPENDED; + + /* Disable clocking to the USB peripheral */ + + sam_disableclks(); + + /* Let the board-specific logic know that we have entered the + * suspend state. This may trigger additional reduced power + * consumption measures. + */ + + sam_usb_suspend((struct usbdev_s *)priv, false); + } +} + +/**************************************************************************** + * Name: sam_resume + ****************************************************************************/ + +static void sam_resume(struct sam_usbdev_s *priv) +{ + /* This function is called when either (1) a WAKEUP interrupt is received + * from the host PC, or (2) the class device implementation calls the + * wakeup() method. + */ + + /* Don't do anything if the device was not suspended */ + + if (priv->devstate == USB_DEVSTATE_SUSPENDED) + { + /* Revert to the previous state */ + + priv->devstate = priv->prevstate; + + /* Restore clocking to the USB peripheral */ + + sam_enableclks(); + + /* Restore full power -- whatever that means for this particular board */ + + sam_usb_suspend((struct usbdev_s *)priv, true); + + /* Notify the class driver of the resume event */ + + if (priv->driver) + { + CLASS_RESUME(priv->driver, &priv->usbdev); + } + } +} + +/**************************************************************************** + * Initialization/Reset + ****************************************************************************/ + +/**************************************************************************** + * Name: sam_reset + ****************************************************************************/ + +static void sam_reset(struct sam_usbdev_s *priv) +{ + uint16_t regval; + uint8_t epno; + + /* Make sure that clocking is enabled to the USB peripheral. */ + + sam_enableclks(); + + /* Tell the class driver that we are disconnected. The class driver + * should then accept any new configurations. + */ + + CLASS_DISCONNECT(priv->driver, &priv->usbdev); + + /* The device enters the Default state (un-addressed and un-configured) */ + + priv->devaddr = 0; + sam_setdevaddr(priv, 0); + + priv->devstate = USB_DEVSTATE_DEFAULT; + + /* Reset and disable all endpoints. Then re-configure EP0 */ + + sam_epset_reset(priv, SAM_EPSET_ALL); + sam_ep_configure_internal(&priv->eplist[EP0], &g_ep0desc); + + /* set EP0 waiting for SETUP */ + + sam_ep0_ctrlread(priv); + + /* Reset endpoint data structures */ + + for (epno = 0; epno < SAM_USB_NENDPOINTS; epno++) + { + struct sam_ep_s *privep = &priv->eplist[epno]; + + /* Cancel any queued requests. Since they are cancelled + * with status -ESHUTDOWN, then will not be requeued + * until the configuration is reset. NOTE: This should + * not be necessary... the CLASS_DISCONNECT above should + * result in the class implementation calling sam_ep_disable + * for each of its configured endpoints. + */ + + sam_req_cancel(privep, -ESHUTDOWN); + + /* Reset endpoint status */ + + privep->stalled = false; + privep->pending = false; + privep->halted = false; + privep->zlpsent = false; + privep->txbusy = false; + privep->rxactive = false; + } + + /* Re-configure the USB controller in its initial, unconnected state */ + + priv->usbdev.speed = USB_SPEED_FULL; + + /* Clear all pending interrupt status */ + + regval = USBDEV_INT_SUSPEND | USBDEV_INT_SOF | USBDEV_INT_EORST | + USBDEV_INT_WAKEUP | USBDEV_INT_EORSM | USBDEV_INT_UPRSM | + USBDEV_INT_RAMACER | USBDEV_INT_LPMNYET | USBDEV_INT_LPMSUSP; + + sam_putreg16(regval, SAM_USBDEV_INTFLAG); + + /* Enable normal operational interrupts + * endpoint 0 is enabled on sam_ep_configure_internal() + */ + + regval = USBDEV_INT_SOF | USBDEV_INT_WAKEUP | USBDEV_INT_SUSPEND; + sam_putreg16(regval, SAM_USBDEV_INTENSET); + + sam_dumpep(priv, EP0); +} + +/**************************************************************************** + * Interrupt Level Processing + ****************************************************************************/ + +/**************************************************************************** + * Name: sam_ep0_wrstatus + * + * Description: + * write ep0 status reply back to host + * + ****************************************************************************/ + +static void sam_ep0_wrstatus(struct sam_usbdev_s *priv, + const uint8_t *buffer, size_t buflen) +{ + uint32_t packetsize; + + /* we need to make copy of data as source is in stack + * reusing the static ep0 setup buffer + */ + + DEBUGASSERT(buflen < SAM_EP0_MAXPACKET); + memcpy(&priv->ep0out[0], buffer, buflen); + + /* set read for next setup OUT */ + + sam_ep0_ctrlread(priv); + + /* setup TX transfer */ + + priv->eplist[0].descb[1]->addr = (uint32_t) &priv->ep0out[0]; + packetsize = priv->eplist[0].descb[1]->pktsize; + packetsize &= ~USBDEV_PKTSIZE_BCNT_MASK; + packetsize &= ~USBDEV_PKTSIZE_MPKTSIZE_MASK; + packetsize |= USBDEV_PKTSIZE_BCNT(buflen); + priv->eplist[0].descb[1]->pktsize = packetsize; + + /* Set BK1RDY to notify the USB hardware that TX data is ready on + * descriptor bank1. We will be notified that the descriptor has been + * transmitted by the USB device when TRCPT1 in the endpoint's EPINTFLAG + * register has been set. + */ + + sam_putreg8(USBDEV_EPSTATUS_BK1RDY, SAM_USBDEV_EPSTATUSSET(0)); + + usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_EP0WRSTATUS), buflen); +} + +/**************************************************************************** + * Name: sam_ep0_dispatch + ****************************************************************************/ + +static void sam_ep0_dispatch(struct sam_usbdev_s *priv) +{ + uint8_t *dataout; + size_t outlen; + int ret; + + usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_DISPATCH), 0); + if (priv && priv->driver) + { + /* Assume IN SETUP (or OUT SETUP with no data) */ + + dataout = NULL; + outlen = 0; + + /* Was this an OUT SETUP command? */ + + if (USB_REQ_ISOUT(priv->ctrl.type)) + { + uint16_t tmplen = GETUINT16(priv->ctrl.len); + if (tmplen > 0) + { + dataout = priv->ep0out; + outlen = tmplen; + } + } + + /* Forward to the control request to the class driver implementation */ + + ret = CLASS_SETUP(priv->driver, &priv->usbdev, &priv->ctrl, + dataout, outlen); + if (ret < 0) + { + /* Stall on failure */ + + usbtrace(TRACE_DEVERROR(SAM_TRACEERR_DISPATCHSTALL), 0); + (void)sam_ep_stall(&priv->eplist[EP0]); + } + } +} + +/**************************************************************************** + * Name: sam_setdevaddr + * + * Description: + * This function is called after the completion of the STATUS phase to + * instantiate the device address that was received during the SETUP + * phase. This enters the ADDRESSED state from either the DEFAULT or the + * CONFIGURED states. + * + * If called with address == 0, then function will revert to the DEFAULT, + * un-configured and un-addressed state. + * + ****************************************************************************/ + +static void sam_setdevaddr(struct sam_usbdev_s *priv, uint8_t address) +{ + uint32_t regval; + + DEBUGASSERT(address <= 0x7f); + if (address) + { + /* Enable the address */ + + address |= USBDEV_DADD_ADDEN; + sam_putreg8(address, SAM_USBDEV_DADD); + + /* Go to the addressed but not configured state */ + + priv->devstate = USB_DEVSTATE_ADDRESSED; + } + else + { + /* Set address to zero. clear ADDEN bit */ + + sam_putreg8(0x00, SAM_USBDEV_DADD); + + /* Revert to the un-addressed, default state */ + + priv->devstate = USB_DEVSTATE_DEFAULT; + } +} + +/**************************************************************************** + * Name: sam_ep0_setup + * + * Description: + * This function is called after the receiving of the SETUP packet + * data is ready on usb_ctrlreq_s struct + * + ****************************************************************************/ + +static void sam_ep0_setup(struct sam_usbdev_s *priv) +{ + struct sam_ep_s *ep0 = &priv->eplist[EP0]; + struct sam_ep_s *privep; + union wb_u value; + union wb_u index; + union wb_u len; + union wb_u response; + enum sam_ep0setup_e ep0result; + uint8_t epno; + int nbytes = 0; /* Assume zero-length packet */ + int ret; + + /* Terminate any pending requests */ + + sam_req_cancel(ep0, -EPROTO); + + /* Assume NOT stalled; no TX in progress */ + + ep0->stalled = false; + ep0->pending = false; + ep0->epstate = USB_EPSTATE_IDLE; + + /* And extract the little-endian 16-bit values to host order */ + + value.w = GETUINT16(priv->ctrl.value); + index.w = GETUINT16(priv->ctrl.index); + len.w = GETUINT16(priv->ctrl.len); + + uinfo("SETUP: type=%02x req=%02x value=%04x index=%04x len=%04x\n", + priv->ctrl.type, priv->ctrl.req, value.w, index.w, len.w); + + /* Dispatch any non-standard requests */ + + if ((priv->ctrl.type & USB_REQ_TYPE_MASK) != USB_REQ_TYPE_STANDARD) + { + usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_NOSTDREQ), priv->ctrl.type); + + /* Let the class implementation handle all non-standard requests */ + + sam_ep0_dispatch(priv); + return; + } + + /* Handle standard request. Pick off the things of interest to the + * USB device controller driver; pass what is left to the class driver + */ + + ep0result = USB_EP0SETUP_SUCCESS; + switch (priv->ctrl.req) + { + case USB_REQ_GETSTATUS: + { + /* type: device-to-host; recipient = device, interface, endpoint + * value: 0 + * index: zero interface endpoint + * len: 2; data = status + */ + + usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_GETSTATUS), priv->ctrl.type); + if (len.w != 2 || (priv->ctrl.type & USB_REQ_DIR_IN) == 0 || + index.b[MSB] != 0 || value.w != 0) + { + usbtrace(TRACE_DEVERROR(SAM_TRACEERR_BADEPGETSTATUS), 0); + ep0result = USB_EP0SETUP_STALL; + } + else + { + switch (priv->ctrl.type & USB_REQ_RECIPIENT_MASK) + { + case USB_REQ_RECIPIENT_ENDPOINT: + { + epno = USB_EPNO(index.b[LSB]); + usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_GETSTATUS), epno); + if (epno >= SAM_USB_NENDPOINTS) + { + usbtrace(TRACE_DEVERROR(SAM_TRACEERR_BADEPGETSTATUS), epno); + ep0result = USB_EP0SETUP_STALL; + } + else + { + privep = &priv->eplist[epno]; + response.w = 0; /* Not stalled */ + nbytes = 2; /* Response size: 2 bytes */ + + if (privep->stalled) + { + /* Endpoint stalled */ + + response.b[LSB] = 1; /* Stalled */ + } + } + } + break; + + case USB_REQ_RECIPIENT_DEVICE: + { + if (index.w == 0) + { + usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_DEVGETSTATUS), 0); + + /* Features: Remote Wakeup=YES; selfpowered=? */ + + response.w = 0; + response.b[LSB] = (priv->selfpowered << USB_FEATURE_SELFPOWERED) | + (1 << USB_FEATURE_REMOTEWAKEUP); + nbytes = 2; /* Response size: 2 bytes */ + } + else + { + usbtrace(TRACE_DEVERROR(SAM_TRACEERR_BADDEVGETSTATUS), 0); + ep0result = USB_EP0SETUP_STALL; + } + } + break; + + case USB_REQ_RECIPIENT_INTERFACE: + { + usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_IFGETSTATUS), 0); + response.w = 0; + nbytes = 2; /* Response size: 2 bytes */ + } + break; + + default: + { + usbtrace(TRACE_DEVERROR(SAM_TRACEERR_BADGETSTATUS), 0); + ep0result = USB_EP0SETUP_STALL; + } + break; + } + } + } + break; + + case USB_REQ_CLEARFEATURE: + { + /* type: host-to-device; recipient = device, interface or endpoint + * value: feature selector + * index: zero interface endpoint; + * len: zero, data = none + */ + + usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_CLEARFEATURE), priv->ctrl.type); + if ((priv->ctrl.type & USB_REQ_RECIPIENT_MASK) != USB_REQ_RECIPIENT_ENDPOINT) + { + /* Let the class implementation handle all recipients (except for the + * endpoint recipient) + */ + + sam_ep0_dispatch(priv); + ep0result = USB_EP0SETUP_DISPATCHED; + } + else + { + /* Endpoint recipient */ + + epno = USB_EPNO(index.b[LSB]); + if (epno < SAM_USB_NENDPOINTS && index.b[MSB] == 0 && + value.w == USB_FEATURE_ENDPOINTHALT && len.w == 0) + { + privep = &priv->eplist[epno]; + privep->halted = false; + + ret = sam_ep_resume(privep); + if (ret < 0) + { + ep0result = USB_EP0SETUP_STALL; + } + } + else + { + usbtrace(TRACE_DEVERROR(SAM_TRACEERR_BADCLEARFEATURE), 0); + ep0result = USB_EP0SETUP_STALL; + } + } + } + break; + + case USB_REQ_SETFEATURE: + { + /* type: host-to-device; recipient = device, interface, endpoint + * value: feature selector + * index: zero interface endpoint; + * len: 0; data = none + */ + + usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_SETFEATURE), priv->ctrl.type); + if (((priv->ctrl.type & USB_REQ_RECIPIENT_MASK) == USB_REQ_RECIPIENT_DEVICE) && + value.w == USB_FEATURE_TESTMODE) + { + /* Special case recipient=device test mode */ + + uinfo("test mode: %d\n", index.w); + } + else if ((priv->ctrl.type & USB_REQ_RECIPIENT_MASK) != USB_REQ_RECIPIENT_ENDPOINT) + { + /* The class driver handles all recipients except recipient=endpoint */ + + sam_ep0_dispatch(priv); + ep0result = USB_EP0SETUP_DISPATCHED; + } + else + { + /* Handler recipient=endpoint */ + + epno = USB_EPNO(index.b[LSB]); + if (epno < SAM_USB_NENDPOINTS && index.b[MSB] == 0 && + value.w == USB_FEATURE_ENDPOINTHALT && len.w == 0) + { + privep = &priv->eplist[epno]; + privep->halted = true; + + ret = sam_ep_stall(privep); + if (ret < 0) + { + ep0result = USB_EP0SETUP_STALL; + } + } + else + { + usbtrace(TRACE_DEVERROR(SAM_TRACEERR_BADSETFEATURE), 0); + ep0result = USB_EP0SETUP_STALL; + } + } + } + break; + + case USB_REQ_SETADDRESS: + { + /* type: host-to-device; recipient = device + * value: device address + * index: 0 + * len: 0; data = none + */ + + if ((priv->ctrl.type & USB_REQ_RECIPIENT_MASK) != USB_REQ_RECIPIENT_DEVICE || + index.w != 0 || len.w != 0 || value.w > 127) + { + usbtrace(TRACE_DEVERROR(SAM_TRACEERR_BADSETADDRESS), 0); + ep0result = USB_EP0SETUP_STALL; + } + else + { + /* Note that setting of the device address will be deferred. + * A zero-length packet will be sent and the device address will + * be set when the zero-length packet transfer completes. + */ + + usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_EP0SETUPSETADDRESS), value.w); + priv->devaddr = value.w; + ep0result = USB_EP0SETUP_ADDRESS; + } + } + break; + + case USB_REQ_GETDESCRIPTOR: + /* type: device-to-host; recipient = device + * value: descriptor type and index + * index: 0 or language ID; + * len: descriptor len; data = descriptor + */ + case USB_REQ_SETDESCRIPTOR: + /* type: host-to-device; recipient = device + * value: descriptor type and index + * index: 0 or language ID; + * len: descriptor len; data = descriptor + */ + + { + usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_GETSETDESC), priv->ctrl.type); + if ((priv->ctrl.type & USB_REQ_RECIPIENT_MASK) == USB_REQ_RECIPIENT_DEVICE) + { + /* The request seems valid... let the class implementation handle it */ + + sam_ep0_dispatch(priv); + ep0result = USB_EP0SETUP_DISPATCHED; + } + else + { + usbtrace(TRACE_DEVERROR(SAM_TRACEERR_BADGETSETDESC), 0); + ep0result = USB_EP0SETUP_STALL; + } + } + break; + + case USB_REQ_GETCONFIGURATION: + /* type: device-to-host; recipient = device + * value: 0; + * index: 0; + * len: 1; data = configuration value + */ + + { + usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_GETCONFIG), priv->ctrl.type); + if ((priv->ctrl.type & USB_REQ_RECIPIENT_MASK) == USB_REQ_RECIPIENT_DEVICE && + value.w == 0 && index.w == 0 && len.w == 1) + { + /* The request seems valid... let the class implementation handle it */ + + sam_ep0_dispatch(priv); + ep0result = USB_EP0SETUP_DISPATCHED; + } + else + { + usbtrace(TRACE_DEVERROR(SAM_TRACEERR_BADGETCONFIG), 0); + ep0result = USB_EP0SETUP_STALL; + } + } + break; + + case USB_REQ_SETCONFIGURATION: + /* type: host-to-device; recipient = device + * value: configuration value + * index: 0; + * len: 0; data = none + */ + + { + usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_SETCONFIG), priv->ctrl.type); + if ((priv->ctrl.type & USB_REQ_RECIPIENT_MASK) == USB_REQ_RECIPIENT_DEVICE && + index.w == 0 && len.w == 0) + { + /* The request seems valid... let the class implementation handle it. + * If the class implementation accepts it new configuration, it will + * call sam_ep_configure() to configure the endpoints. + */ + + sam_ep0_dispatch(priv); + ep0result = USB_EP0SETUP_DISPATCHED; + } + else + { + usbtrace(TRACE_DEVERROR(SAM_TRACEERR_BADSETCONFIG), 0); + ep0result = USB_EP0SETUP_STALL; + } + } + break; + + case USB_REQ_GETINTERFACE: + /* type: device-to-host; recipient = interface + * value: 0 + * index: interface; + * len: 1; data = alt interface + */ + case USB_REQ_SETINTERFACE: + /* type: host-to-device; recipient = interface + * value: alternate setting + * index: interface; + * len: 0; data = none + */ + + { + /* Let the class implementation handle the request */ + + usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_GETSETIF), priv->ctrl.type); + sam_ep0_dispatch(priv); + ep0result = USB_EP0SETUP_DISPATCHED; + } + break; + + case USB_REQ_SYNCHFRAME: + /* type: device-to-host; recipient = endpoint + * value: 0 + * index: endpoint; + * len: 2; data = frame number + */ + + { + usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_SYNCHFRAME), 0); + } + break; + + default: + { + usbtrace(TRACE_DEVERROR(SAM_TRACEERR_INVALIDCTRLREQ), priv->ctrl.req); + ep0result = USB_EP0SETUP_STALL; + } + break; + } + + /* Restrict the data length to the length requested in the setup packet */ + + if (nbytes > len.w) + { + nbytes = len.w; + } + + /* At this point, the request has been handled and there are three + * (or four) possible outcomes: + * + * 1a. ep0result == USB_EP0SETUP_SUCCESS + * + * The setup request was successfully handled above and a response + * packet must be sent (may be a zero length packet). + * + * 1b. ep0result == USB_EP0SETUP_ADDRESS + * + * A special case is the case where epstate=USB_EPSTATE_EP0ADDRESS. + * This means that the above processing generated an additional state + * where we need to wait until we complete the status phase before + * applying the new device address. + * + * 2. ep0result == USB_EP0SETUP_DISPATCHED; + * + * The request was forwarded to the class implementation. In case, + * EP0 IN data may have already been sent and the EP0 IN response + * has already been queued? Or perhaps the endpoint has already + * been stalled? This is all under the control of the class driver. + * + * NOTE that for the case of non-standard SETUP requested, those + * requests were forwarded to the class driver and we don't even get + * to this logic. + * + * 3. ep0result == USB_EP0SETUP_STALL; + * + * An error was detected in either the above logic or by the class + * implementation logic. + */ + + switch (ep0result) + { + case USB_EP0SETUP_SUCCESS: + { + /* Send the response (might be a zero-length packet) */ + + ep0->epstate = USB_EPSTATE_EP0STATUSIN; + sam_ep0_wrstatus(priv, response.b, nbytes); + } + break; + + case USB_EP0SETUP_ADDRESS: + { + /* Send the response (might be a zero-length packet) */ + + ep0->epstate = USB_EPSTATE_EP0ADDRESS; + sam_ep0_wrstatus(priv, response.b, nbytes); + } + break; + + case USB_EP0SETUP_STALL: + { + /* Stall EP0 */ + + usbtrace(TRACE_DEVERROR(SAM_TRACEERR_EP0SETUPSTALLED), + priv->ctrl.req); + + (void)sam_ep_stall(&priv->eplist[EP0]); + } + break; + + case USB_EP0SETUP_DISPATCHED: + default: + break; + } +} + + + + +/**************************************************************************** + * Name: sam_ctrla_write + * + * Description: + * writes value to CTRLA register some bits needs write-synchronisation + * + ****************************************************************************/ + +static void sam_ctrla_write(uint8_t value) +{ + sam_putreg8(value, SAM_USB_CTRLA); + + if (value & USB_CTRLA_SWRST) + { + + /* Due to synchronization there is a delay from writing CTRLA.SWRST + * until the reset is complete. CTRLA.SWRST and SYNCBUSY.SWRST will + * both be cleared when the reset is complete. + */ + + while ((sam_getreg8(SAM_USB_CTRLA) & USB_CTRLA_SWRST) && + (sam_getreg8(SAM_USB_SYNCBUSY) & USB_SYNCBUSY_SWRST)) + ; + + return; + } + + if (value & USB_CTRLA_ENABLE) + { + + /* Due to synchronization there is delay from writing CTRLA.ENABLE + * until the peripheral is enabled/disabled. + * SYNCBUSY.ENABLE will be cleared when the operation is complete. + */ + + while ((sam_getreg8(SAM_USB_SYNCBUSY) & USB_SYNCBUSY_ENABLE)) + ; + } +} + +/**************************************************************************** + * Name: sam_ep_trcpt_interrupt + * + * Description: + * Transmit completed on Bank 0/1 + * Normal: + * OUT data transmit has been completed bank=0 + * Ping-Pong: + * TODO: + * + ****************************************************************************/ +static void sam_ep_trcpt_interrupt(struct sam_usbdev_s *priv, + struct sam_ep_s *privep, + uint32_t flags, int bank) +{ + uint32_t eptype; + uint16_t pktsize; + uint8_t epno; + + /* Get the endpoint type */ + + epno = USB_EPNO(privep->ep.eplog); + eptype = sam_getreg8(SAM_USBDEV_EPCFG(epno)) & USBDEV_EPCFG_EPTYPE0_MASK; + + /* Are we receiving data for a read request? EP0 does not receive data + * using read requests. + */ + + /* Get the size of the packet that we just received */ + + pktsize = privep->descb[bank]->pktsize & USBDEV_PKTSIZE_BCNT_MASK; + usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_EPTRCPT0_LEN), (uint16_t)pktsize); + + if (privep->epstate == USB_EPSTATE_IDLE && epno != 0) + { + /* continue processing the read request. */ + + (void)sam_req_read(priv, privep, pktsize); + } + + /* Did we just receive the data associated with an OUT SETUP command? */ + + else if (privep->epstate == USB_EPSTATE_EP0DATAOUT) + { + uint16_t len; + + DEBUGASSERT(epno == EP0 && bank == 0); + + /* Yes.. back to the IDLE state */ + + privep->epstate = USB_EPSTATE_IDLE; + + /* Get the size that we expected to receive */ + + len = GETUINT16(priv->ctrl.len); + if (len == pktsize) + { + /* And handle the EP0 SETUP now. */ + + sam_ep0_setup(priv); + } + else + { + /* Then stall. */ + + usbtrace(TRACE_DEVERROR(SAM_TRACEERR_EP0SETUPOUTSIZE), pktsize); + + (void)sam_ep_stall(privep); + } + } + + /* Check for a EP0 STATUS packet returned by the host at the end of a + * SETUP status phase + */ + + else if ((eptype == USBDEV_EPCCFG_EPTYPE0_CTRLOUT) && pktsize == 0) + { + DEBUGASSERT(epno == EP0 && bank == 0); + } + + /* Otherwise there is a problem. Complain an clear the interrupt */ + + else + { + usbtrace(TRACE_DEVERROR(SAM_TRACEERR_RXDATABKERR), privep->epstate); + } +} + +/**************************************************************************** + * Name: sam_ep0_ctrlread + * + * Description: + * setup 8-byte read req for ep0-ctrl setup phase. + * data is received on priv->ep0out buffer. This is notified by + * endpoint TRCPT0 interrupt + * + ****************************************************************************/ + +static void sam_ep0_ctrlread(struct sam_usbdev_s *priv) +{ + priv->eplist[0].descb[0]->addr = (uint32_t) &priv->ep0out[0]; + priv->eplist[0].descb[0]->pktsize = USBDEV_PKTSIZE_MPKTSIZE(8) | USBDEV_PKTSIZE_SIZE_64B; + sam_putreg8(USBDEV_EPSTATUS_BK0RDY, SAM_USBDEV_EPSTATUSCLR(0)); +} + +/**************************************************************************** + * Name: sam_ep_interrupt + * + * Description: + * Handle the USB endpoint interrupt + * + ****************************************************************************/ + +static void sam_ep_interrupt(struct sam_usbdev_s *priv, int epno) +{ + struct sam_ep_s *privep; + uint16_t flags; + + DEBUGASSERT((unsigned)epno < SAM_USB_NENDPOINTS); + + /* Get the endpoint structure */ + + privep = &priv->eplist[epno]; + + /* Get the endpoint irq */ + + flags = sam_getreg8(SAM_USBDEV_EPINTFLAG(epno)); + usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_EPINTFLAGS), flags); + + /* TRCPT1: IN packet sent and acknowledged by the host */ + + if ((flags & USBDEV_EPINT_TRCPT1) != 0) + { + usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_EPTRCPT1), flags); + + /* Clear the TRCPT1 interrupt */ + + sam_putreg8(USBDEV_EPINT_TRCPT1, SAM_USBDEV_EPINTFLAG(epno)); + + /* Sending state. This is the completion of a "normal" write request + * transfer. In this case, we need to resume request processing in + * order to send the next outgoing packet. + */ + + if (privep->epstate == USB_EPSTATE_SENDING || + privep->epstate == USB_EPSTATE_EP0STATUSIN) + { + /* Continue/resume processing the write requests */ + + privep->epstate = USB_EPSTATE_IDLE; + (void)sam_req_write(priv, privep); + } + + /* Setting of the device address is a special case. The address was + * obtained when a preceding SETADDRESS SETUP command was processed. + * But the address is not set until the final SETUP status phase + * completes. This interrupt indicates the completion of that status + * phase and now we set the address. + */ + + else if (privep->epstate == USB_EPSTATE_EP0ADDRESS) + { + usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_ADDRESSED), priv->devaddr); + DEBUGASSERT(epno == EP0); + + /* Set the device address */ + + privep->epstate = USB_EPSTATE_IDLE; + sam_setdevaddr(priv, priv->devaddr); + } + else + { + /* Unexpected TRCPT1 interrupt */ + + usbtrace(TRACE_DEVERROR(SAM_TRACEERR_TXCOMPERR), privep->epstate); + } + } + + /* OUT packet received */ + + if ((flags & USBDEV_EPINT_TRCPT0) != 0) + { + usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_EPTRCPT0), flags); + + /* Clear the TRCPT0 interrupt. */ + + sam_putreg8(USBDEV_EPINT_TRCPT0, SAM_USBDEV_EPINTFLAG(epno)); + + sam_ep_trcpt_interrupt(priv, privep, flags, 0); + } + + /* Endpoint stall */ + + if ((flags & USBDEV_EPINT_STALL0) != 0) + { + sam_putreg8(USBDEV_EPINT_STALL0, SAM_USBDEV_EPINTFLAG(epno)); + usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_EPSTALL0), flags); + + /* If EP is not halted, clear STALL */ + + if (privep->epstate != USB_EPSTATE_STALLED) + { + sam_putreg8(USBDEV_EPSTATUS_STALLRQ0, SAM_USBDEV_EPSTATUSCLR(epno)); + } + } + + if ((flags & USBDEV_EPINT_STALL1) != 0) + { + sam_putreg8(USBDEV_EPINT_STALL1, SAM_USBDEV_EPINTFLAG(epno)); + usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_EPSTALL1), flags); + + /* If EP is not halted, clear STALL */ + + if (privep->epstate != USB_EPSTATE_STALLED) + { + sam_putreg8(USBDEV_EPSTATUS_STALLRQ1, SAM_USBDEV_EPSTATUSCLR(epno)); + } + } + + // FIXME: Transmit FAIL! + if ((flags & USBDEV_EPINT_TRFAIL0) != 0) + { + //uerr("ERROR: OUT TRFAIL0!\n"); + sam_putreg8(USBDEV_EPINT_TRFAIL0, SAM_USBDEV_EPINTFLAG(epno)); + privep->descb[0]->stausbk &= ~USBDEV_STATUSBK_ERRORFLOW; + usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_EPTRFAIL0), flags); + } + if ((flags & USBDEV_EPINT_TRFAIL1) != 0) + { + //uerr("ERROR: IN TRFAIL1!\n"); + sam_putreg8(USBDEV_EPINT_TRFAIL1, SAM_USBDEV_EPINTFLAG(epno)); + privep->descb[1]->stausbk &= ~USBDEV_STATUSBK_ERRORFLOW; + usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_EPTRFAIL1), flags); + } + + /* SETUP packet received */ + + if ((flags & USBDEV_EPINT_RXSTP) != 0) + { + uint16_t len; + + usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_EPRXSTP), flags); + + /* If a write request transfer was pending, complete it. */ + + if (privep->epstate == USB_EPSTATE_SENDING) + { + sam_req_complete(privep, -EPROTO); + } + + /* SETUP data is ready in the ep0out buffer. */ + + memcpy((uint8_t *)&priv->ctrl, (uint8_t *)&priv->ep0out[0], + USB_SIZEOF_CTRLREQ); + + /* Check for a SETUP IN transaction with data. */ + + len = GETUINT16(priv->ctrl.len); + if (USB_REQ_ISOUT(priv->ctrl.type) && len > 0) + { + /* Yes.. then we have to wait for the OUT data phase to complete + * before processing the SETUP command. + */ + + usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_EP0SETUPOUT), priv->ctrl.req); + privep->epstate = USB_EPSTATE_EP0DATAOUT; + + //sam_ep0_ctrlread(priv); + + /* Clear the RXSTP indication. */ + + sam_putreg8(USBDEV_EPINT_RXSTP, SAM_USBDEV_EPINTFLAG(epno)); + } + else + { + /* This is an SETUP IN command (or a SETUP IN with no data). */ + + usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_EP0SETUPIN), len); + privep->epstate = USB_EPSTATE_IDLE; + + /* Clear the RXSTP indication. */ + + sam_putreg8(USBDEV_EPINT_RXSTP, SAM_USBDEV_EPINTFLAG(epno)); + + /* Handle the SETUP OUT command now */ + + sam_ep0_setup(priv); + } + + /* ready for next setup data */ + + sam_ep0_ctrlread(priv); + + } +} + +/**************************************************************************** + * Name: sam_usb_interrupt + * + * Description: + * Handle the USB interrupt. + * Device Mode only TODO: Host + * + ****************************************************************************/ + +static int sam_usb_interrupt(int irq, void *context, void *arg) +{ + struct sam_usbdev_s *priv = (struct sam_usbdev_s *)arg; + uint16_t isr; + uint16_t pending; + uint16_t regval; + uint16_t pendingep; + int i; + + /* Get the set of pending device interrupts */ + + isr = sam_getreg16(SAM_USBDEV_INTFLAG); + + //usbtrace(TRACE_INTENTRY(SAM_TRACEINTID_INTERRUPT), irq); + + regval = sam_getreg16(SAM_USBDEV_INTENSET); + pending = isr & regval; + + /* Get the set of pending enpoint interrupts */ + + pendingep = sam_getreg16(SAM_USBDEV_EPINTSMRY); + + /* Handle all pending USB interrupts (and new interrupts that become + * pending) + */ + + //while (pending || pendingep) + { + //usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_PENDING), pending); + + /* serve Endpoint Interrupts first */ + + if (pendingep) + { + usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_PENDING_EP), pendingep); + + for (i = 0; i < SAM_USB_NENDPOINTS; i++) + { + if ((pendingep & USBDEV_EPINTSMRY_EPINT(i))) + { + usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_EPNO), (uint16_t)i); + sam_ep_interrupt(priv, i); + } + } + } + + /* Suspend, treated last */ + + if (pending == USBDEV_INT_SUSPEND) + { + usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_SUSPEND), pending); + + /* Enable wakeup interrupts */ + + sam_putreg16(USBDEV_INT_SUSPEND, SAM_USBDEV_INTENCLR); + // REVISIT: Check USBDEV_INT_UPRSM + sam_putreg16(USBDEV_INT_UPRSM | USBDEV_INT_WAKEUP | USBDEV_INT_EORSM, + SAM_USBDEV_INTENSET); + + /* Clear the pending suspend (and any wakeup) interrupts */ + + sam_putreg16(USBDEV_INT_SUSPEND | USBDEV_INT_WAKEUP, + SAM_USBDEV_INTFLAG); + + /* Perform board-specific suspend operations. + * REVISIT: is the following true? + * The USB device peripheral clocks can be switched off. + * Resume event is asynchronously detected. MCK and USBCK can be + * switched off in the Power Management controller and + * Other board-specific operations could also be performed. + */ + + // FIXME: fix suspend... + //sam_suspend(priv); + } + + /* SOF interrupt*/ + + else if ((pending & USBDEV_INT_SOF) != 0) + { + /* Clear the pending SOF interrupt */ + + //usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_SOF), pending); + sam_putreg16(SAM_TRACEINTID_SOF, SAM_USBDEV_INTFLAG); + + /* TODO: do we need check frame errors FNUM.FNCERR */ + } + + /* Resume or wakeup. REVISIT: Treat the same? */ + + else if ((pending & (USBDEV_INT_WAKEUP | USBDEV_INT_EORSM)) != 0) + { + usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_WAKEUP), (uint16_t)pending); + sam_resume(priv); + + /* Clear the pending wakeup, resume, (and any suspend) interrupts */ + + sam_putreg16(USBDEV_INT_WAKEUP | USBDEV_INT_EORSM | + USBDEV_INT_SUSPEND, SAM_USBDEV_INTFLAG); + + /* Disable wakup and endofresume Enable suspend interrupt */ + + sam_putreg16(USBDEV_INT_WAKEUP | USBDEV_INT_EORSM, SAM_USBDEV_INTENCLR); + sam_putreg16(USBDEV_INT_SUSPEND, SAM_USBDEV_INTENSET); + } + + /* End of Reset. Set by hardware when an End Of Reset has been + * detected by the USB controller. + */ + + if ((pending & USBDEV_INT_EORST) != 0) + { + usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_EORST), pending); + + /* Clear the end-of-reset interrupt */ + + sam_putreg16(USBDEV_INT_EORST, SAM_USBDEV_INTFLAG); + + /* Handle the reset */ + + sam_reset(priv); + + /* REVISIT: Set the device speed Why here ?? */ + + priv->usbdev.speed = USB_SPEED_FULL; + } + + /* Re-sample the set of pending interrupts */ + + isr = sam_getreg16(SAM_USBDEV_INTFLAG); + regval = sam_getreg16(SAM_USBDEV_INTENSET); + pending = isr & regval; + pendingep = sam_getreg16(SAM_USBDEV_EPINTSMRY); + + } /* while (pending) */ + +#if 0 + /* for DEBUG help: check for pending unhandled irq's */ + + isr = sam_getreg16(SAM_USBDEV_INTFLAG); + if (isr) + { + uwarn("WARNING: Unhandled:0x%X\n", isr); + } + + pendingep = sam_getreg16(SAM_USBDEV_EPINTSMRY); + if (pendingep) + { + uwarn("WARNING: Unhandled_EP:0x%X\n", pendingep); + } +#endif + + //usbtrace(TRACE_INTEXIT(SAM_TRACEINTID_INTERRUPT), irq); +} + +void up_usbuninitialize(void) +{ + uinfo("up_usbuninitialize()\n"); + // FIXME: +} + +void up_usbinitialize(void) +{ + /* For now there is only one USB controller, but we will always refer to + * it using a pointer to make any future ports to multiple USB controllers + * easier. + */ + + struct sam_usbdev_s *priv = &g_usbd; + + usbtrace(TRACE_DEVINIT, 0); + + /* Software initialization */ + + sam_sw_setup(priv); + + /* Power up and initialize USB controller. Interrupt from the USB + * controller is initialized here, but will not be enabled at the AIC + * until the class driver is installed. + */ + + sam_hw_setup(priv); + + /* Attach USB controller interrupt handlers. The hardware will not be + * initialized and interrupts will not be enabled until the class device + * driver is bound. Getting the IRQs here only makes sure that we have + * them when we need them later. + */ + + if (irq_attach(SAM_IRQ_USB, sam_usb_interrupt, priv) != 0) + { + usbtrace(TRACE_DEVERROR(SAM_TRACEERR_IRQREGISTRATION), + (uint16_t)SAM_IRQ_USB); + goto errout; + } + + uinfo("OK\n"); + return; + +errout: + up_usbuninitialize(); +} + +/**************************************************************************** + * Endpoint Helpers + ****************************************************************************/ + +/**************************************************************************** + * Name: sam_ep_reset + * + * Description + * Reset and disable one endpoints. + * + ****************************************************************************/ + +static void sam_ep_reset(struct sam_usbdev_s *priv, uint8_t epno) +{ + struct sam_ep_s *privep = &priv->eplist[epno]; + + /* Disable endpoint interrupts */ + + sam_putreg8(0x7e, SAM_USBDEV_EPINTENCLR(epno)); + sam_putreg8(0x7e, SAM_USBDEV_EPINTFLAG(epno)); + + /* Cancel any queued requests. Since they are cancelled with status + * -ESHUTDOWN, then will not be requeued until the configuration is reset. + * NOTE: This should not be necessary... the CLASS_DISCONNECT above + * should result in the class implementation calling sam_ep_disable + * for each of its configured endpoints. + */ + + sam_req_cancel(privep, -ESHUTDOWN); + + /* Reset endpoint status */ + + privep->epstate = USB_EPSTATE_DISABLED; + privep->stalled = false; + privep->pending = false; + privep->halted = false; + privep->zlpsent = false; + privep->txbusy = false; + privep->rxactive = false; +} + +/**************************************************************************** + * Name: sam_epset_reset + * + * Description + * Reset and disable a set of endpoints. + * + ****************************************************************************/ + +static void sam_epset_reset(struct sam_usbdev_s *priv, uint16_t epset) +{ + uint32_t bit; + int epno; + + /* Reset each endpoint in the set */ + + for (epno = 0, bit = 1, epset &= SAM_EPSET_ALL; + epno < SAM_USB_NENDPOINTS && epset != 0; + epno++, bit <<= 1) + { + /* Is this endpoint in the set? */ + + if ((epset & bit) != 0) + { + /* Yes.. reset and disable it */ + + sam_ep_reset(priv, epno); + epset &= ~bit; + } + } +} + +/**************************************************************************** + * Name: sam_ep_stall + ****************************************************************************/ + +static int sam_ep_stall(struct sam_ep_s *privep) +{ + irqstate_t flags; + uint8_t epno; + + /* Check that endpoint is in Idle state */ + + DEBUGASSERT(/* privep->epstate == UDP_EPSTATE_IDLE && */ privep->dev); + + /* Check that endpoint is enabled and not already in Halt state */ + + flags = enter_critical_section(); + if ((privep->epstate != USB_EPSTATE_DISABLED) && + (privep->epstate != USB_EPSTATE_STALLED)) + { + epno = USB_EPNO(privep->ep.eplog); + usbtrace(TRACE_EPSTALL, epno); + + /* If this is an IN endpoint (or endpoint 0), then cancel any + * write requests in progress. + */ + + if (epno == 0 || USB_ISEPIN(privep->ep.eplog)) + { + sam_req_cancel(privep, -EPERM); + } + + /* Put endpoint into stalled state */ + + privep->epstate = USB_EPSTATE_STALLED; + privep->stalled = true; + privep->pending = false; + + sam_putreg8(USBDEV_EPSTATUS_STALLRQ0 | USBDEV_EPSTATUS_STALLRQ1, + SAM_USBDEV_EPSTATUSSET(epno)); + } + + leave_critical_section(flags); + return OK; +} + +/**************************************************************************** + * Name: sam_ep_resume + ****************************************************************************/ + +static int sam_ep_resume(struct sam_ep_s *privep) +{ + struct sam_usbdev_s *priv; + struct sam_req_s *req; + irqstate_t flags; + uint8_t epno; + + /* Check that endpoint is in Idle state */ + + DEBUGASSERT(/* privep->epstate == UDP_EPSTATE_IDLE && */ privep->dev); + + flags = enter_critical_section(); + + /* Check if the endpoint is stalled */ + + if (privep->epstate == USB_EPSTATE_STALLED) + { + epno = USB_EPNO(privep->ep.eplog); + usbtrace(TRACE_EPRESUME, epno); + + priv = (struct sam_usbdev_s *)privep->dev; + + /* Return endpoint to Idle state */ + + privep->stalled = false; + privep->pending = false; + privep->epstate = USB_EPSTATE_IDLE; + + /* Clear STALLRQx request and reset data toggle */ + + if (USB_ISEPIN(privep->ep.eplog)) + { + sam_putreg8(USBDEV_EPSTATUS_STALLRQ1, SAM_USBDEV_EPSTATUSCLR(epno)); + sam_putreg8(USBDEV_EPSTATUS_DTGLIN, SAM_USBDEV_EPSTATUSCLR(epno)); + sam_putreg8(USBDEV_EPINT_STALL1, SAM_USBDEV_EPINTFLAG(epno)); + } + else + { + sam_putreg8(USBDEV_EPSTATUS_STALLRQ0, SAM_USBDEV_EPSTATUSCLR(epno)); + sam_putreg8(USBDEV_EPSTATUS_DTGLOUT, SAM_USBDEV_EPSTATUSCLR(epno)); + sam_putreg8(USBDEV_EPINT_STALL0, SAM_USBDEV_EPINTFLAG(epno)); + } + + /* Copy any requests in the pending request queue to the working + * request queue. + */ + + while ((req = sam_req_dequeue(&privep->pendq)) != NULL) + { + sam_req_enqueue(&privep->reqq, req); + } + + /* Resuming any blocked data transfers on the endpoint */ + + if (epno == 0 || USB_ISEPIN(privep->ep.eplog)) + { + /* IN endpoint (or EP0). Restart any queued write requests */ + + (void)sam_req_write(priv, privep); + } + } + + leave_critical_section(flags); + return OK; +} + +/**************************************************************************** + * Name: sam_pullup + * + * Description: + * This is the pullup() method of the USB device driver interface + * + ****************************************************************************/ + +static int sam_pullup(struct usbdev_s *dev, bool enable) +{ + struct sam_usbdev_s *priv = (struct sam_usbdev_s *)dev; + uint32_t regval; + + usbtrace(TRACE_DEVPULLUP, (uint16_t)enable); + + /* Enable/disable the USB pull-up resistor */ + + regval = sam_getreg8(SAM_USBDEV_CTRLB); + + if (enable) + { + /* Connect the 1.5 KOhm integrated pull-up on USB */ + + regval &= ~USBDEV_CTRLB_DETACH; + } + else + { + /* Disconnect the 1.5 KOhm integrated pull-up on USB */ + + regval |= USBDEV_CTRLB_DETACH; + + /* Device returns to the Powered state */ + + if (priv->devstate > USB_DEVSTATE_POWERED) + { + priv->devstate = USB_DEVSTATE_POWERED; + } + } + + sam_putreg8((uint8_t)regval, SAM_USBDEV_CTRLB); + + return OK; +} + +/**************************************************************************** + * Initialization/Reset + ****************************************************************************/ + +/**************************************************************************** + * Name: sam_enableclks + ****************************************************************************/ + +static void sam_enableclks(void) +{ + sam_usb_enableperiph(); + sam_gclk_chan_enable(GCLK_CHAN_USB, BOARD_USB_GCLKGEN); +} + +/**************************************************************************** + * Name: sam_disableclks + ****************************************************************************/ + +static void sam_disableclks(void) +{ + sam_gclk_chan_disable(GCLK_CHAN_USB); + sam_usb_disableperiph(); + +} + +/**************************************************************************** + * Name: sam_hw_setup + ****************************************************************************/ + +static void sam_hw_setup(struct sam_usbdev_s *priv) +{ + int i; + uint16_t regval; + uint32_t padcalib; + + /* To use the USB, the programmer must first configure the USB clock + * input, + */ + + sam_enableclks(); + + /* full reset USB */ + + sam_ctrla_write(USB_CTRLA_SWRST); + + /* TODO: load PAD calibration from NVM or ... + * now using default values + */ + + padcalib = USB_PADCAL_TRANSP(29) | USB_PADCAL_TRANSN(5) | USB_PADCAL_TRIM(3); + sam_putreg32(padcalib, SAM_USB_PADCAL); + + /* set config + * NREPLY = Any transaction to endpoint 0 will be ignored except SETUP + * cleared by hardware when receiving a SETUP packet. + * DETACH = The internal device pull-ups are disabled + */ + + regval = USBDEV_CTRLB_NREPLY | USBDEV_CTRLB_DETACH; + + /* do we need config to set LOW_SPEED mode? */ + + regval |= USBDEV_CTRLB_SPDCONF_FULL; + + sam_putreg16(regval, SAM_USBDEV_CTRLB); + + /* Enable USB core */ + +#ifdef CONFIG_USBDEV + sam_ctrla_write(USB_CTRLA_ENABLE | USB_CTRLA_MODE_DEVICE); +#endif +#ifdef CONFIG_USBHOST + sam_ctrla_write(USB_CTRLA_ENABLE | USB_CTRLA_MODE_HOST); +#endif + + /* Set up the USB DP/DM pins */ + + sam_configport(PORT_USB_DP); + sam_configport(PORT_USB_DM); + + /* Reset and disable endpoints */ + + sam_epset_reset(priv, SAM_EPSET_ALL); + + /* Initialize Endpoints */ + + for (i = 0; i < SAM_USB_NENDPOINTS; i++) + { + /* Reset endpoint configuration */ + + sam_putreg8(0, SAM_USBDEV_EPCFG(i)); + } + + /* Init descriptor base address */ + + sam_putreg32((uint32_t)&priv->ep_descriptors, SAM_USB_DESCADD); + + /* clear all previous descriptor data so no accidental + * DMA transfers could happen */ + + memset((uint8_t *)(&priv->ep_descriptors[0]), 0, + sizeof(priv->ep_descriptors)); + + /* Disable all interrupts */ + + sam_putreg16(USBDEV_INT_SUSPEND | USBDEV_INT_SOF | USBDEV_INT_EORST | + USBDEV_INT_WAKEUP | USBDEV_INT_EORSM | USBDEV_INT_UPRSM | + USBDEV_INT_RAMACER | USBDEV_INT_LPMNYET | USBDEV_INT_LPMSUSP, + SAM_USBDEV_INTENCLR); + + sam_pullup(&priv->usbdev, false); +} + +/**************************************************************************** + * Name: sam_hw_shutdown + ****************************************************************************/ + +static void sam_hw_shutdown(struct sam_usbdev_s *priv) +{ + uint16_t regval; + + priv->usbdev.speed = USB_SPEED_UNKNOWN; + + /* Disable all interrupts */ + + sam_putreg16(USBDEV_INT_SUSPEND | USBDEV_INT_SOF | USBDEV_INT_EORST | + USBDEV_INT_WAKEUP | USBDEV_INT_EORSM | USBDEV_INT_UPRSM | + USBDEV_INT_RAMACER | USBDEV_INT_LPMNYET | USBDEV_INT_LPMSUSP, + SAM_USBDEV_INTENCLR); + + /* Clear all pending interrupt status */ + + sam_putreg16(USBDEV_INT_SUSPEND | USBDEV_INT_SOF | USBDEV_INT_EORST | + USBDEV_INT_WAKEUP | USBDEV_INT_EORSM | USBDEV_INT_UPRSM | + USBDEV_INT_RAMACER | USBDEV_INT_LPMNYET | USBDEV_INT_LPMSUSP, + SAM_USBDEV_INTFLAG); + + /* Disconnect the device / disable the pull-up */ + + sam_pullup(&priv->usbdev, false); + + /* Disable clocking to the UDP peripheral */ + + sam_disableclks(); +} + +/**************************************************************************** + * Name: sam_sw_setup + ****************************************************************************/ + +static void sam_sw_setup(struct sam_usbdev_s *priv) +{ + int epno; + + /* Initialize the device state structure. NOTE: many fields + * have the initial value of zero and, hence, are not explicitly + * initialized here. + */ + + memset(priv, 0, sizeof(struct sam_usbdev_s)); + priv->usbdev.ops = &g_devops; + priv->usbdev.ep0 = &priv->eplist[EP0].ep; + priv->epavail = SAM_EPSET_ALL & ~SAM_EP_BIT(EP0); + priv->devstate = USB_DEVSTATE_SUSPENDED; + priv->prevstate = USB_DEVSTATE_POWERED; + + /* Initialize the endpoint list */ + + for (epno = 0; epno < SAM_USB_NENDPOINTS; epno++) + { + /* Set endpoint operations, reference to driver structure (not + * really necessary because there is only one controller), and + * the (physical) endpoint number which is just the index to the + * endpoint. + */ + + priv->eplist[epno].ep.ops = &g_epops; + priv->eplist[epno].dev = priv; + priv->eplist[epno].ep.eplog = epno; + + /* We will use a maxpacket size for supported for each endpoint */ + + priv->eplist[epno].ep.maxpacket = SAM_USB_MAXPACKETSIZE(epno); + + /* set descriptor addresses */ + + priv->eplist[epno].descb[0] = &priv->ep_descriptors[(epno << 1)]; + priv->eplist[epno].descb[1] = &priv->ep_descriptors[(epno << 1) + 1]; + } + + /* Select a smaller endpoint size for EP0 */ + +#if SAM_EP0_MAXPACKET < SAM_USB_MAXPACKETSIZE(0) + priv->eplist[EP0].ep.maxpacket = SAM_EP0_MAXPACKET; +#endif +} + +/**************************************************************************** + * Name: sam_sw_shutdown + ****************************************************************************/ + +static void sam_sw_shutdown(struct sam_usbdev_s *priv) +{ + (void)priv; +} + +/**************************************************************************** + * Name: usbdev_register + * + * Description: + * Register a USB device class driver. The class driver's bind() method + * will be called to bind it to a USB device driver. + * + ****************************************************************************/ + +int usbdev_register(struct usbdevclass_driver_s *driver) +{ + /* For now there is only one USB controller, but we will always refer to + * it using a pointer to make any future ports to multiple USB controllers + * easier. + */ + + struct sam_usbdev_s *priv = &g_usbd; + int ret; + + usbtrace(TRACE_DEVREGISTER, 0); + +#ifdef CONFIG_DEBUG + if (!driver || !driver->ops->bind || !driver->ops->unbind || + !driver->ops->disconnect || !driver->ops->setup) + { + usbtrace(TRACE_DEVERROR(SAM_TRACEERR_INVALIDPARMS), 0); + return -EINVAL; + } + + if (priv->driver) + { + usbtrace(TRACE_DEVERROR(SAM_TRACEERR_DRIVER), 0); + return -EBUSY; + } +#endif + + /* First hook up the driver */ + + priv->driver = driver; + + /* Then bind the class driver */ + + ret = CLASS_BIND(driver, &priv->usbdev); + if (ret) + { + usbtrace(TRACE_DEVERROR(SAM_TRACEERR_BINDFAILED), (uint16_t)-ret); + priv->driver = NULL; + } + else + { + /* Enable USB controller interrupts at the NVIC. */ + + sam_hw_setup(priv); + up_enable_irq(SAM_IRQ_USB); + + /* Enable EORST irq */ + + sam_putreg16(USBDEV_INT_EORST, SAM_USBDEV_INTENSET); + + /* Enable pull-up to connect the device. The host should enumerate us + * some time after this. The next thing we expect is the EORST + * interrupt. + */ + + sam_pullup(&priv->usbdev, true); + priv->usbdev.speed = USB_SPEED_FULL; + } + + return ret; +} + +/**************************************************************************** + * Name: usbdev_unregister + * + * Description: + * Un-register usbdev class driver. If the USB device is connected to a + * USB host, it will first disconnect(). The driver is also requested to + * unbind() and clean up any device state, before this procedure finally + * returns. + * + ****************************************************************************/ + +int usbdev_unregister(struct usbdevclass_driver_s *driver) +{ + /* For now there is only one USB controller, but we will always refer to + * it using a pointer to make any future ports to multiple USB controllers + * easier. + */ + + struct sam_usbdev_s *priv = &g_usbd; + irqstate_t flags; + + usbtrace(TRACE_DEVUNREGISTER, 0); + +#ifdef CONFIG_DEBUG + if (driver != priv->driver) + { + usbtrace(TRACE_DEVERROR(SAM_TRACEERR_INVALIDPARMS), 0); + return -EINVAL; + } +#endif + + /* Reset the hardware and cancel all requests. All requests must be + * canceled while the class driver is still bound. + */ + + flags = enter_critical_section(); + + /* Unbind the class driver */ + + CLASS_UNBIND(driver, &priv->usbdev); + + /* Disable USB controller interrupts (but keep them attached) */ + + up_disable_irq(SAM_IRQ_USB); + + /* Put the hardware in an inactive state. Then bring the hardware back up + * in the initial state. This is essentially the same state as we were + * in when up_usbinitialize() was first called. + */ + + sam_hw_shutdown(priv); + sam_sw_shutdown(priv); + + sam_sw_setup(priv); + sam_hw_setup(priv); + + /* Unhook the driver */ + + priv->driver = NULL; + leave_critical_section(flags); + return OK; +} + +#endif /* CONFIG_USBDEV && CONFIG_SAMDL_USB */ diff --git a/arch/arm/src/samdl/sam_usb.h b/arch/arm/src/samdl/sam_usb.h new file mode 100644 index 00000000000..278f8e88ebf --- /dev/null +++ b/arch/arm/src/samdl/sam_usb.h @@ -0,0 +1,99 @@ +/****************************************************************************** + * arch/arm/src/samdl/sam_usb.h + * + * Copyright (C) 2015 Filament - www.filament.com + * Copyright (C) 2015 Offcode Ltd. All rights reserved. + * Author: Janne Rosberg + * + * This driver is derived from the SAM34 UDP driver: + * Copyright (C) 2014 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + *****************************************************************************/ + +#ifndef __ARCH_ARM_SRC_SAMDL_SAM_USB_H +#define __ARCH_ARM_SRC_SAMDL_SAM_USB_H + +/***************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include + +#include "chip.h" +#include "chip/saml_usb.h" + +/***************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +#ifndef __ASSEMBLY__ + +#undef EXTERN +#if defined(__cplusplus) +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +/***************************************************************************** + * Name: sam_usb_suspend + * + * Description: + * Board logic must provide the sam_usb_suspend logic if the USB driver is + * used. This function is called whenever the USB enters or leaves + * suspend mode. + * + * When 'resume' is false, this function call provides an opportunity to + * perform board-specific power-saving actions so that less power is consumed + * while the USB is suspended. + * + * XXX: + * Certain power-saving operations are performed by the UDP driver when it + * enters suspend mode: The USB device peripheral clocks are be switched off. + * MCK and UDPCK are switched off and the USB transceiver is disabled. + * + * When 'resume' is true, normal clocking and operations must all be restored. + * + *****************************************************************************/ + +void sam_udp_suspend(FAR struct usbdev_s *dev, bool resume); + +#undef EXTERN +#if defined(__cplusplus) +} +#endif + +#endif /* __ASSEMBLY__ */ +#endif /* __ARCH_ARM_SRC_SAMDL_SAM_USB_H */ diff --git a/configs/samd21-xplained/include/board.h b/configs/samd21-xplained/include/board.h index 40b19e775fc..e51baf7a90f 100644 --- a/configs/samd21-xplained/include/board.h +++ b/configs/samd21-xplained/include/board.h @@ -480,6 +480,19 @@ #define BOARD_SERCOM5_FREQUENCY BOARD_GCLK0_FREQUENCY +/* USB definitions ******************************************************************/ +/* This is the source clock generator for the GCLK_USB clock + */ + +#define BOARD_USB_GCLKGEN 0 +#define BOARD_USB_FREQUENCY BOARD_GCLK0_FREQUENCY + +/* default USB Pad calibration (not used yet by USB driver) */ + +#define BOARD_USB_PADCAL_P 29 +#define BOARD_USB_PADCAL_N 5 +#define BOARD_USB_PADCAL_TRIM 3 + /* LED definitions ******************************************************************/ /* There are three LEDs on board the SAMD21 Xplained Pro board: The EDBG * controls two of the LEDs, a power LED and a status LED. There is only diff --git a/configs/saml21-xplained/include/board.h b/configs/saml21-xplained/include/board.h index ec93296ec80..5cc260d2741 100644 --- a/configs/saml21-xplained/include/board.h +++ b/configs/saml21-xplained/include/board.h @@ -604,6 +604,19 @@ #define BOARD_SERCOM5_FREQUENCY BOARD_GCLK0_FREQUENCY +/* USB definitions ******************************************************************/ +/* This is the source clock generator for the GCLK_USB clock + */ + +#define BOARD_USB_GCLKGEN 0 +#define BOARD_USB_FREQUENCY BOARD_GCLK0_FREQUENCY + +/* default USB Pad calibration (not used yet by USB driver) */ + +#define BOARD_USB_PADCAL_P 29 +#define BOARD_USB_PADCAL_N 5 +#define BOARD_USB_PADCAL_TRIM 3 + /* LED definitions ******************************************************************/ /* There are three LEDs on board the SAML21 Xplained Pro board: The EDBG * controls two of the LEDs, a power LED and a status LED. There is only From 10fbb2b089825c27a74bc48549b5d9527237302a Mon Sep 17 00:00:00 2001 From: Gregory Nutt Date: Wed, 12 Jul 2017 08:10:21 -0600 Subject: [PATCH 07/18] SAMD/L21 USB: Remove all commented out logic. --- arch/arm/src/samdl/sam_usb.c | 260 ++++++++++++----------------------- 1 file changed, 91 insertions(+), 169 deletions(-) diff --git a/arch/arm/src/samdl/sam_usb.c b/arch/arm/src/samdl/sam_usb.c index f878b322d06..175e486a38a 100644 --- a/arch/arm/src/samdl/sam_usb.c +++ b/arch/arm/src/samdl/sam_usb.c @@ -537,7 +537,6 @@ static const struct usb_epdesc_s g_ep0desc = .type = USB_DESC_TYPE_ENDPOINT, .addr = EP0, .attr = USB_EP_ATTR_XFER_CONTROL, - // REVISIT: .mxpacketsize = {64, 0}, .interval = 0 }; @@ -582,7 +581,6 @@ const struct trace_msg_t g_usb_trace_strings_deverror[] = }; #endif - /* Interrupt event strings that may be enabled for more desciptive USB trace * output. */ @@ -637,46 +635,10 @@ const struct trace_msg_t g_usb_trace_strings_intdecode[] = TRACE_STR(SAM_TRACEINTID_EP0WRSTATUS), TRACE_STR(SAM_TRACEINTID_EPTRCPT0_LEN), - /* - TRACE_STR(SAM_TRACEINTID_ADDRESSED), - TRACE_STR(SAM_TRACEINTID_CLEARFEATURE), - TRACE_STR(SAM_TRACEINTID_RXSUSP), - TRACE_STR(SAM_TRACEINTID_DEVGETSTATUS), - TRACE_STR(SAM_TRACEINTID_DISPATCH), - TRACE_STR(SAM_TRACEINTID_ENDBUSRES), - TRACE_STR(SAM_TRACEINTID_EP), - TRACE_STR(SAM_TRACEINTID_EP0SETUPIN), - TRACE_STR(SAM_TRACEINTID_EP0SETUPOUT), - TRACE_STR(SAM_TRACEINTID_EP0SETUPSETADDRESS), - TRACE_STR(SAM_TRACEINTID_EPGETSTATUS), - TRACE_STR(SAM_TRACEINTID_EPINQEMPTY), - TRACE_STR(SAM_TRACEINTID_EPOUTQEMPTY), - TRACE_STR(SAM_TRACEINTID_GETCONFIG), - TRACE_STR(SAM_TRACEINTID_GETSETDESC), - TRACE_STR(SAM_TRACEINTID_GETSETIF), - TRACE_STR(SAM_TRACEINTID_GETSTATUS), - TRACE_STR(SAM_TRACEINTID_IFGETSTATUS), - TRACE_STR(SAM_TRACEINTID_SOF), - TRACE_STR(SAM_TRACEINTID_NOSTDREQ), - TRACE_STR(SAM_TRACEINTID_RXDATABK0), - TRACE_STR(SAM_TRACEINTID_RXDATABK1), - TRACE_STR(SAM_TRACEINTID_RXSETUP), - TRACE_STR(SAM_TRACEINTID_SETCONFIG), - TRACE_STR(SAM_TRACEINTID_SETFEATURE), - TRACE_STR(SAM_TRACEINTID_STALLSNT), - TRACE_STR(SAM_TRACEINTID_SYNCHFRAME), - TRACE_STR(SAM_TRACEINTID_TXCOMP), - TRACE_STR(SAM_TRACEINTID_UPSTRRES), - TRACE_STR(SAM_TRACEINTID_WAKEUP), - */ TRACE_STR_END }; #endif -/**************************************************************************** - * Public Data - ****************************************************************************/ - /**************************************************************************** * Private Private Functions ****************************************************************************/ @@ -1213,7 +1175,7 @@ static int sam_req_write(struct sam_usbdev_s *priv, struct sam_ep_s *privep) /* setup 0 length TX transfer */ - priv->eplist[0].descb[1]->addr = (uint32_t) &priv->ep0out[0]; + priv->eplist[0].descb[1]->addr = (uint32_t) &priv->ep0out[0]; priv->eplist[0].descb[1]->pktsize &= ~USBDEV_PKTSIZE_BCNT_MASK; priv->eplist[0].descb[1]->pktsize &= ~USBDEV_PKTSIZE_MPKTSIZE_MASK; priv->eplist[0].descb[1]->pktsize |= USBDEV_PKTSIZE_BCNT(0); @@ -1354,14 +1316,14 @@ static int sam_req_read(struct sam_usbdev_s *priv, struct sam_ep_s *privep, /* activate new read request from queue */ - privep->rxactive = true; + privep->rxactive = true; privreq->req.xfrd = 0; privreq->inflight = privreq->req.len; priv->eplist[epno].descb[0]->addr = (uint32_t) privreq->req.buf; - packetsize = priv->eplist[epno].descb[0]->pktsize; - packetsize &= ~USBDEV_PKTSIZE_BCNT_MASK; - packetsize &= ~USBDEV_PKTSIZE_MPKTSIZE_MASK; - packetsize |= USBDEV_PKTSIZE_MPKTSIZE(privreq->inflight); + packetsize = priv->eplist[epno].descb[0]->pktsize; + packetsize &= ~USBDEV_PKTSIZE_BCNT_MASK; + packetsize &= ~USBDEV_PKTSIZE_MPKTSIZE_MASK; + packetsize |= USBDEV_PKTSIZE_MPKTSIZE(privreq->inflight); sam_putreg8(USBDEV_EPSTATUS_BK0RDY, SAM_USBDEV_EPSTATUSCLR(epno)); return OK; @@ -1464,7 +1426,7 @@ static int sam_ep_configure_internal(struct sam_ep_s *privep, /* Initialize the endpoint structure */ - privep->ep.eplog = desc->addr; /* Includes direction */ + privep->ep.eplog = desc->addr; /* Includes direction */ privep->ep.maxpacket = maxpacket; privep->epstate = USB_EPSTATE_IDLE; @@ -1472,34 +1434,27 @@ static int sam_ep_configure_internal(struct sam_ep_s *privep, epconf = 0x00; sam_putreg8(0x00, SAM_USBDEV_EPCFG(epno)); - //epconf = sam_getreg8(SAM_USBDEV_EPCFG(epno)); - //uinfo("BEFORE: epconf=0x%X\n", epconf); if (dirin) { - /* disable bank1 (IN) */ + /* Disable bank1 (IN) */ - //epconf &= ~USBDEV_EPCFG_EPTYPE1_MASK; - intflags = USBDEV_EPINT_TRCPT1 | /*USBDEV_EPINT_TRFAIL1 |*/ USBDEV_EPINT_STALL1; + intflags = USBDEV_EPINT_TRCPT1 | USBDEV_EPINT_STALL1; } else { - /* disable bank0 (OUT) */ - //epconf &= ~USBDEV_EPCFG_EPTYPE0_MASK; - intflags = USBDEV_EPINT_TRCPT0 | /*USBDEV_EPINT_TRFAIL0 |*/ USBDEV_EPINT_STALL0; + /* Disable bank0 (OUT) */ + + intflags = USBDEV_EPINT_TRCPT0 | USBDEV_EPINT_STALL0; } /* write back disabled config */ -//sam_putreg8(epconf, SAM_USBDEV_EPCFG(epno)); sam_putreg8(0x7e, SAM_USBDEV_EPINTENCLR(epno)); sam_putreg8(0x7e, SAM_USBDEV_EPINTFLAG(epno)); -//sam_putreg8(intflags, SAM_USBDEV_EPINTENCLR(epno)); -//sam_putreg8(intflags, SAM_USBDEV_EPINTFLAG(epno)); - /* re-configure and enable the endpoint */ + /* Re-configure and enable the endpoint */ - //epconf = SAM_USBDEV_EPCFG(epno); switch (eptype) { case USB_EP_ATTR_XFER_CONTROL: @@ -1508,10 +1463,8 @@ static int sam_ep_configure_internal(struct sam_ep_s *privep, /* Also enable IN interrupts */ - intflags = USBDEV_EPINT_TRCPT0 | /* USBDEV_EPINT_TRFAIL1 |*/ - USBDEV_EPINT_STALL0; - intflags |= USBDEV_EPINT_TRCPT1 | /* USBDEV_EPINT_TRFAIL0 |*/ - USBDEV_EPINT_STALL1; + intflags = USBDEV_EPINT_TRCPT0 | USBDEV_EPINT_STALL0; + intflags |= USBDEV_EPINT_TRCPT1 | USBDEV_EPINT_STALL1; intflags |= USBDEV_EPINT_RXSTP; sam_putreg8(USBDEV_EPSTATUS_BK0RDY, SAM_USBDEV_EPSTATUSSET(0)); sam_putreg8(USBDEV_EPSTATUS_BK1RDY, SAM_USBDEV_EPSTATUSCLR(0)); @@ -1795,7 +1748,7 @@ static void sam_ep_freereq(struct usbdev_ep_s *ep, struct usbdev_req_s *req) kmm_free(privreq); } -#if 0 // FIXME, not required, check... +#if 0 /* FIXME, not required, check... */ /**************************************************************************** * Name: sam_ep_allocbuffer @@ -2247,10 +2200,10 @@ static int sam_selfpowered(struct usbdev_s *dev, bool selfpowered) return OK; } - /**************************************************************************** * Suspend/Resume Helpers ****************************************************************************/ + /**************************************************************************** * Name: sam_suspend ****************************************************************************/ @@ -3009,9 +2962,6 @@ static void sam_ep0_setup(struct sam_usbdev_s *priv) } } - - - /**************************************************************************** * Name: sam_ctrla_write * @@ -3026,7 +2976,6 @@ static void sam_ctrla_write(uint8_t value) if (value & USB_CTRLA_SWRST) { - /* Due to synchronization there is a delay from writing CTRLA.SWRST * until the reset is complete. CTRLA.SWRST and SYNCBUSY.SWRST will * both be cleared when the reset is complete. @@ -3041,7 +2990,6 @@ static void sam_ctrla_write(uint8_t value) if (value & USB_CTRLA_ENABLE) { - /* Due to synchronization there is delay from writing CTRLA.ENABLE * until the peripheral is enabled/disabled. * SYNCBUSY.ENABLE will be cleared when the operation is complete. @@ -3271,17 +3219,16 @@ static void sam_ep_interrupt(struct sam_usbdev_s *priv, int epno) } } - // FIXME: Transmit FAIL! + /* Transmit FAIL! */ + if ((flags & USBDEV_EPINT_TRFAIL0) != 0) { - //uerr("ERROR: OUT TRFAIL0!\n"); sam_putreg8(USBDEV_EPINT_TRFAIL0, SAM_USBDEV_EPINTFLAG(epno)); privep->descb[0]->stausbk &= ~USBDEV_STATUSBK_ERRORFLOW; usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_EPTRFAIL0), flags); } if ((flags & USBDEV_EPINT_TRFAIL1) != 0) { - //uerr("ERROR: IN TRFAIL1!\n"); sam_putreg8(USBDEV_EPINT_TRFAIL1, SAM_USBDEV_EPINTFLAG(epno)); privep->descb[1]->stausbk &= ~USBDEV_STATUSBK_ERRORFLOW; usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_EPTRFAIL1), flags); @@ -3319,8 +3266,6 @@ static void sam_ep_interrupt(struct sam_usbdev_s *priv, int epno) usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_EP0SETUPOUT), priv->ctrl.req); privep->epstate = USB_EPSTATE_EP0DATAOUT; - //sam_ep0_ctrlread(priv); - /* Clear the RXSTP indication. */ sam_putreg8(USBDEV_EPINT_RXSTP, SAM_USBDEV_EPINTFLAG(epno)); @@ -3368,10 +3313,7 @@ static int sam_usb_interrupt(int irq, void *context, void *arg) /* Get the set of pending device interrupts */ - isr = sam_getreg16(SAM_USBDEV_INTFLAG); - - //usbtrace(TRACE_INTENTRY(SAM_TRACEINTID_INTERRUPT), irq); - + isr = sam_getreg16(SAM_USBDEV_INTFLAG); regval = sam_getreg16(SAM_USBDEV_INTENSET); pending = isr & regval; @@ -3379,119 +3321,98 @@ static int sam_usb_interrupt(int irq, void *context, void *arg) pendingep = sam_getreg16(SAM_USBDEV_EPINTSMRY); - /* Handle all pending USB interrupts (and new interrupts that become - * pending) - */ + /* Handle all pending USB interrupts */ + /* Serve Endpoint Interrupts first */ - //while (pending || pendingep) + if (pendingep) { - //usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_PENDING), pending); + usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_PENDING_EP), pendingep); - /* serve Endpoint Interrupts first */ - - if (pendingep) + for (i = 0; i < SAM_USB_NENDPOINTS; i++) { - usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_PENDING_EP), pendingep); - - for (i = 0; i < SAM_USB_NENDPOINTS; i++) + if ((pendingep & USBDEV_EPINTSMRY_EPINT(i))) { - if ((pendingep & USBDEV_EPINTSMRY_EPINT(i))) - { - usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_EPNO), (uint16_t)i); - sam_ep_interrupt(priv, i); - } + usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_EPNO), (uint16_t)i); + sam_ep_interrupt(priv, i); } } + } - /* Suspend, treated last */ + /* Suspend, treated last */ - if (pending == USBDEV_INT_SUSPEND) - { - usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_SUSPEND), pending); + if (pending == USBDEV_INT_SUSPEND) + { + usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_SUSPEND), pending); - /* Enable wakeup interrupts */ + /* Enable wakeup interrupts */ - sam_putreg16(USBDEV_INT_SUSPEND, SAM_USBDEV_INTENCLR); - // REVISIT: Check USBDEV_INT_UPRSM - sam_putreg16(USBDEV_INT_UPRSM | USBDEV_INT_WAKEUP | USBDEV_INT_EORSM, - SAM_USBDEV_INTENSET); + sam_putreg16(USBDEV_INT_SUSPEND, SAM_USBDEV_INTENCLR); + sam_putreg16(USBDEV_INT_UPRSM | USBDEV_INT_WAKEUP | USBDEV_INT_EORSM, + SAM_USBDEV_INTENSET); - /* Clear the pending suspend (and any wakeup) interrupts */ + /* Clear the pending suspend (and any wakeup) interrupts */ - sam_putreg16(USBDEV_INT_SUSPEND | USBDEV_INT_WAKEUP, - SAM_USBDEV_INTFLAG); + sam_putreg16(USBDEV_INT_SUSPEND | USBDEV_INT_WAKEUP, + SAM_USBDEV_INTFLAG); - /* Perform board-specific suspend operations. - * REVISIT: is the following true? - * The USB device peripheral clocks can be switched off. - * Resume event is asynchronously detected. MCK and USBCK can be - * switched off in the Power Management controller and - * Other board-specific operations could also be performed. - */ - - // FIXME: fix suspend... - //sam_suspend(priv); - } - - /* SOF interrupt*/ - - else if ((pending & USBDEV_INT_SOF) != 0) - { - /* Clear the pending SOF interrupt */ - - //usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_SOF), pending); - sam_putreg16(SAM_TRACEINTID_SOF, SAM_USBDEV_INTFLAG); - - /* TODO: do we need check frame errors FNUM.FNCERR */ - } - - /* Resume or wakeup. REVISIT: Treat the same? */ - - else if ((pending & (USBDEV_INT_WAKEUP | USBDEV_INT_EORSM)) != 0) - { - usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_WAKEUP), (uint16_t)pending); - sam_resume(priv); - - /* Clear the pending wakeup, resume, (and any suspend) interrupts */ - - sam_putreg16(USBDEV_INT_WAKEUP | USBDEV_INT_EORSM | - USBDEV_INT_SUSPEND, SAM_USBDEV_INTFLAG); - - /* Disable wakup and endofresume Enable suspend interrupt */ - - sam_putreg16(USBDEV_INT_WAKEUP | USBDEV_INT_EORSM, SAM_USBDEV_INTENCLR); - sam_putreg16(USBDEV_INT_SUSPEND, SAM_USBDEV_INTENSET); - } - - /* End of Reset. Set by hardware when an End Of Reset has been - * detected by the USB controller. + /* Perform board-specific suspend operations. + * + * The USB device peripheral clocks can be switched off. + * Resume event is asynchronously detected. MCK and USBCK can be + * switched off in the Power Management controller and + * Other board-specific operations could also be performed. */ + } - if ((pending & USBDEV_INT_EORST) != 0) - { - usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_EORST), pending); + /* SOF interrupt*/ - /* Clear the end-of-reset interrupt */ + else if ((pending & USBDEV_INT_SOF) != 0) + { + /* Clear the pending SOF interrupt */ - sam_putreg16(USBDEV_INT_EORST, SAM_USBDEV_INTFLAG); + sam_putreg16(SAM_TRACEINTID_SOF, SAM_USBDEV_INTFLAG); - /* Handle the reset */ + /* TODO: do we need check frame errors FNUM.FNCERR */ + } - sam_reset(priv); + /* Resume or wakeup. REVISIT: Treat the same? */ - /* REVISIT: Set the device speed Why here ?? */ + else if ((pending & (USBDEV_INT_WAKEUP | USBDEV_INT_EORSM)) != 0) + { + usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_WAKEUP), (uint16_t)pending); + sam_resume(priv); - priv->usbdev.speed = USB_SPEED_FULL; - } + /* Clear the pending wakeup, resume, (and any suspend) interrupts */ - /* Re-sample the set of pending interrupts */ + sam_putreg16(USBDEV_INT_WAKEUP | USBDEV_INT_EORSM | + USBDEV_INT_SUSPEND, SAM_USBDEV_INTFLAG); - isr = sam_getreg16(SAM_USBDEV_INTFLAG); - regval = sam_getreg16(SAM_USBDEV_INTENSET); - pending = isr & regval; - pendingep = sam_getreg16(SAM_USBDEV_EPINTSMRY); + /* Disable wakup and endofresume Enable suspend interrupt */ - } /* while (pending) */ + sam_putreg16(USBDEV_INT_WAKEUP | USBDEV_INT_EORSM, SAM_USBDEV_INTENCLR); + sam_putreg16(USBDEV_INT_SUSPEND, SAM_USBDEV_INTENSET); + } + + /* End of Reset. Set by hardware when an End Of Reset has been + * detected by the USB controller. + */ + + if ((pending & USBDEV_INT_EORST) != 0) + { + usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_EORST), pending); + + /* Clear the end-of-reset interrupt */ + + sam_putreg16(USBDEV_INT_EORST, SAM_USBDEV_INTFLAG); + + /* Handle the reset */ + + sam_reset(priv); + + /* REVISIT: Set the device speed Why here ?? */ + + priv->usbdev.speed = USB_SPEED_FULL; + } #if 0 /* for DEBUG help: check for pending unhandled irq's */ @@ -3508,14 +3429,11 @@ static int sam_usb_interrupt(int irq, void *context, void *arg) uwarn("WARNING: Unhandled_EP:0x%X\n", pendingep); } #endif - - //usbtrace(TRACE_INTEXIT(SAM_TRACEINTID_INTERRUPT), irq); } void up_usbuninitialize(void) { uinfo("up_usbuninitialize()\n"); - // FIXME: } void up_usbinitialize(void) @@ -3997,6 +3915,10 @@ static void sam_sw_shutdown(struct sam_usbdev_s *priv) (void)priv; } +/**************************************************************************** + * Public Functions + ****************************************************************************/ + /**************************************************************************** * Name: usbdev_register * From 1b9cb7082816002b5a94d33b91ea0ae8c8b37fb5 Mon Sep 17 00:00:00 2001 From: Gregory Nutt Date: Wed, 12 Jul 2017 15:07:32 -0600 Subject: [PATCH 08/18] Sockets: Initial steps to adde a socket interface to the networking. Each address family will have an interface that describes how to perform socket operations on that address family. Currently only a couple of methods are defined in the table as a proof of concept. More to come. Currently there are only tables for the INET/INET6 family, the Unix LOCAL family, and the raw PACKET family. Hopefully there will be AF_IEEE802154 and AF_BLUETOOTH comming down the pike. --- include/nuttx/net/net.h | 22 +- net/local/Make.defs | 1 + net/local/local.h | 7 + net/local/local_sockif.c | 305 +++++++++++++++++++++++++++ net/pkt/Make.defs | 1 + net/pkt/pkt.h | 4 + net/pkt/pkt_sockif.c | 242 ++++++++++++++++++++++ net/socket/Make.defs | 12 +- net/socket/accept.c | 1 + net/socket/inet_sockif.c | 435 +++++++++++++++++++++++++++++++++++++++ net/socket/net_clone.c | 1 + net/socket/net_sockif.c | 111 ++++++++++ net/socket/send.c | 128 ++---------- net/socket/sendto.c | 118 ++--------- net/socket/socket.c | 420 +++---------------------------------- net/socket/socket.h | 21 ++ 16 files changed, 1222 insertions(+), 607 deletions(-) create mode 100644 net/local/local_sockif.c create mode 100644 net/pkt/pkt_sockif.c create mode 100644 net/socket/inet_sockif.c create mode 100644 net/socket/net_sockif.c diff --git a/include/nuttx/net/net.h b/include/nuttx/net/net.h index e12fb99d1ed..92d09a2623b 100644 --- a/include/nuttx/net/net.h +++ b/include/nuttx/net/net.h @@ -94,11 +94,27 @@ typedef uint16_t sockopt_t; typedef uint16_t socktimeo_t; +/* This callbacks are socket operations that may be performed on a socket of + * a given address family. + */ + +struct socket; /* Forward reference */ + +struct sock_intf_s +{ + CODE int (*si_setup)(FAR struct socket *psock, int protocol); + CODE ssize_t (*si_send)(FAR struct socket *psock, FAR const void *buf, + size_t len, int flags); + CODE ssize_t (*si_sendto)(FAR struct socket *psock, FAR const void *buf, + size_t len, int flags, FAR const struct sockaddr *to, + socklen_t tolen); +}; + /* This is the internal representation of a socket reference by a file * descriptor. */ -struct devif_callback_s; /* Forward reference */ +struct devif_callback_s; /* Forward reference */ struct socket { @@ -120,6 +136,10 @@ struct socket FAR void *s_conn; /* Connection: struct tcp_conn_s or udp_conn_s */ + /* Socket interface */ + + FAR const struct sock_intf_s *s_sockif; + #ifdef CONFIG_NET_TCP_WRITE_BUFFERS /* Callback instance for TCP send */ diff --git a/net/local/Make.defs b/net/local/Make.defs index 1d6f0cbd974..46b26daac6b 100644 --- a/net/local/Make.defs +++ b/net/local/Make.defs @@ -39,6 +39,7 @@ ifeq ($(CONFIG_NET_LOCAL),y) NET_CSRCS += local_conn.c local_release.c local_bind.c local_fifo.c NET_CSRCS += local_recvfrom.c local_sendpacket.c local_recvutils.c +NET_CSRCS += local_sockif.c ifeq ($(CONFIG_NET_LOCAL_STREAM),y) NET_CSRCS += local_connect.c local_listen.c local_accept.c local_send.c diff --git a/net/local/local.h b/net/local/local.h index db4fffc0faf..222d846cc0f 100644 --- a/net/local/local.h +++ b/net/local/local.h @@ -51,6 +51,8 @@ #include #include +#include + #ifdef CONFIG_NET_LOCAL /**************************************************************************** @@ -77,6 +79,7 @@ /**************************************************************************** * Public Type Definitions ****************************************************************************/ + /* Local, Unix domain socket types */ enum local_type_e @@ -204,6 +207,10 @@ extern "C" # define EXTERN extern #endif +/* The local socket interface */ + +EXTERN const struct sock_intf_s g_local_sockif; + #ifdef CONFIG_NET_LOCAL_STREAM /* A list of all SOCK_STREAM listener connections */ diff --git a/net/local/local_sockif.c b/net/local/local_sockif.c new file mode 100644 index 00000000000..c7e9c1ba54c --- /dev/null +++ b/net/local/local_sockif.c @@ -0,0 +1,305 @@ +/**************************************************************************** + * net/local/local_sockif.c + * + * Copyright (C) 2017 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include + +#include + +#include + +#include "local/local.h" + +#ifdef CONFIG_NET_LOCAL + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static ssize_t local_setup(FAR struct socket *psock, int protocol); +static ssize_t local_send(FAR struct socket *psock, FAR const void *buf, + size_t len, int flags); +static ssize_t local_sendto(FAR struct socket *psock, FAR const void *buf, + size_t len, int flags, FAR const struct sockaddr *to, + socklen_t tolen); + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +const struct sock_intf_s g_local_sockif = +{ + local_setup, /* si_setup */ + local_send, /* si_send */ + local_sendto, /* si_sendto */ +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: local_sockif_alloc + * + * Description: + * Allocate and attach a local, Unix domain connection structure. + * + ****************************************************************************/ + +static int local_sockif_alloc(FAR struct socket *psock) +{ + /* Allocate the local connection structure */ + + FAR struct local_conn_s *conn = local_alloc(); + if (conn == NULL) + { + /* Failed to reserve a connection structure */ + + return -ENOMEM; + } + + /* Set the reference count on the connection structure. This reference + * count will be incremented only if the socket is dup'ed + */ + + DEBUGASSERT(conn->lc_crefs == 0); + conn->lc_crefs = 1; + + /* Save the pre-allocated connection in the socket structure */ + + psock->s_conn = conn; + return OK; +} + +/**************************************************************************** + * Name: local_setup + * + * Description: + * Called for socket() to verify that the provided socket type and + * protocol are usable by this address family. Perform any family- + * specific socket fields. + * + * Parameters: + * psock A pointer to a user allocated socket structure to be initialized. + * protocol (see sys/socket.h) + * + * Returned Value: + * Zero (OK) is returned on success. Otherwise, a negater errno value is + * returned. + * + ****************************************************************************/ + +static int local_setup(FAR struct socket *psock, int protocol) +{ + /* Allocate the appropriate connection structure. This reserves the + * the connection structure is is unallocated at this point. It will + * not actually be initialized until the socket is connected. + * + * Only SOCK_STREAM and SOCK_DGRAM and possible SOCK_RAW are supported. + */ + + switch (psock->s_type) + { +#ifdef CONFIG_NET_TCP + case SOCK_STREAM: + if (protocol != 0 && protocol != IPPROTO_TCP) + { + return -EPROTONOSUPPORT; + } + + /* Allocate and attach the local connection structure */ + + return local_sockif_alloc(psock); +#endif /* CONFIG_NET_TCP */ + +#ifdef CONFIG_NET_UDP + case SOCK_DGRAM: + if (protocol != 0 && protocol != IPPROTO_UDP) + { + return -EPROTONOSUPPORT; + } + + /* Allocate and attach the local connection structure */ + + return local_sockif_alloc(psock); +#endif /* CONFIG_NET_UDP */ + + default: + return -EPROTONOSUPPORT; + } +} + +/**************************************************************************** + * Name: local_send + * + * Description: + * Implements the send() operation for the case of the local, Unix socket. + * + * Parameters: + * psock An instance of the internal socket structure. + * buf Data to send + * len Length of data to send + * flags Send flags + * + * Returned Value: + * On success, returns the number of characters sent. On error, -1 is + * returned, and errno is set appropriately (see send() for the list of + * appropriate errors values. + * + ****************************************************************************/ + +static ssize_t local_send(FAR struct socket *psock, FAR const void *buf, + size_t len, int flags) +{ + ssize_t ret; + + switch (psock->s_type) + { +#ifdef CONFIG_NET_TCP + case SOCK_STREAM: + { + /* Local TCP packet send */ + + ret = psock_local_send(psock, buf, len, flags); + } + break; +#endif /* CONFIG_NET_TCP */ + +#ifdef CONFIG_NET_UDP + case SOCK_DGRAM: + { + /* Local UDP packet send */ +#warning Missing logic + ret = -ENOSYS; + } + break; +#endif /* CONFIG_NET_UDP */ + + default: + { + /* EDESTADDRREQ. Signifies that the socket is not connection-mode + * and no peer address is set. + */ + + ret = -EDESTADDRREQ; + } + break; + } + + return ret; +} + +/**************************************************************************** + * Name: local_sendto + * + * Description: + * Implements the sendto() operation for the case of the local, Unix socket. + * + * Parameters: + * psock A pointer to a NuttX-specific, internal socket structure + * buf Data to send + * len Length of data to send + * flags Send flags + * to Address of recipient + * tolen The length of the address structure + * + * Returned Value: + * On success, returns the number of characters sent. On error, -1 is + * returned, and errno is set appropriately (see send_to() for the list of + * appropriate errors values. + * + ****************************************************************************/ + +ssize_t local_sendto(FAR struct socket *psock, FAR const void *buf, + size_t len, int flags, FAR const struct sockaddr *to, + socklen_t tolen) +{ + ssize_t nsent; + + /* Verify that a valid address has been provided */ + + if (to->sa_family != AF_LOCAL || tolen < sizeof(sa_family_t)) + { + nerr("ERROR: Unrecognized address family: %d\n", + to->sa_family); + return -EAFNOSUPPORT; + } + +#ifdef CONFIG_NET_UDP + /* If this is a connected socket, then return EISCONN */ + + if (psock->s_type != SOCK_DGRAM) + { + nerr("ERROR: Connected socket\n"); + return -EISCONN; + } + + /* Now handle the local UDP sendto() operation */ + + nsent = psock_local_sendto(psock, buf, len, flags, to, tolen); +#else + nsent = -EISCONN; +#endif /* CONFIG_NET_LOCAL_DGRAM */ + + return nsent; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: + * + * Description: + * + * Parameters: + * + * Returned Value: + * + * Assumptions: + * + ****************************************************************************/ + +#endif /* CONFIG_NET_LOCAL */ diff --git a/net/pkt/Make.defs b/net/pkt/Make.defs index ced242a430c..f3e97740ba9 100644 --- a/net/pkt/Make.defs +++ b/net/pkt/Make.defs @@ -39,6 +39,7 @@ ifeq ($(CONFIG_NET_PKT),y) # Socket layer +SOCK_CSRCS += pkt_sockif.c SOCK_CSRCS += pkt_send.c # Transport layer diff --git a/net/pkt/pkt.h b/net/pkt/pkt.h index b7b6e0b0a92..041246b2fc1 100644 --- a/net/pkt/pkt.h +++ b/net/pkt/pkt.h @@ -91,6 +91,10 @@ extern "C" # define EXTERN extern #endif +/* The packet socket interface */ + +EXTERN const struct sock_intf_s g_pkt_sockif; + /**************************************************************************** * Public Function Prototypes ****************************************************************************/ diff --git a/net/pkt/pkt_sockif.c b/net/pkt/pkt_sockif.c new file mode 100644 index 00000000000..2f698de2b1a --- /dev/null +++ b/net/pkt/pkt_sockif.c @@ -0,0 +1,242 @@ +/**************************************************************************** + * net/socket/pkt_sockif.c + * + * Copyright (C) 2017 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include + +#include + +#include "pkt/pkt.h" + +#ifdef CONFIG_NET_PKT + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static ssize_t pkt_setup(FAR struct socket *psock, int protocol); +static ssize_t pkt_send(FAR struct socket *psock, FAR const void *buf, + size_t len, int flags); +static ssize_t pkt_sendto(FAR struct socket *psock, FAR const void *buf, + size_t len, int flags, FAR const struct sockaddr *to, + socklen_t tolen); + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +const struct sock_intf_s g_pkt_sockif = +{ + pkt_setup, /* si_setup */ + pkt_send, /* si_send */ + pkt_sendto, /* si_sendto */ +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: pkt_sockif_alloc + * + * Description: + * Allocate and attach a raw packet connection structure. + * + ****************************************************************************/ + +static int pkt_sockif_alloc(FAR struct socket *psock) +{ + /* Allocate the packet socket connection structure and save in the new + * socket instance. + */ + + FAR struct pkt_conn_s *conn = pkt_alloc(); + if (conn == NULL) + { + /* Failed to reserve a connection structure */ + + return -ENOMEM; + } + + /* Set the reference count on the connection structure. This reference + * count will be incremented only if the socket is dup'ed + */ + + DEBUGASSERT(conn->crefs == 0); + conn->crefs = 1; + + /* Save the pre-allocated connection in the socket structure */ + + psock->s_conn = conn; + return OK; +} + +/**************************************************************************** + * Name: pkt_setup + * + * Description: + * Called for socket() to verify that the provided socket type and + * protocol are usable by this address family. Perform any family- + * specific socket fields. + * + * Parameters: + * psock A pointer to a user allocated socket structure to be + * initialized. + * protocol (see sys/socket.h) + * + * Returned Value: + * Zero (OK) is returned on success. Otherwise, a negater errno value is + * returned. + * + ****************************************************************************/ + +static int pkt_setup(FAR struct socket *psock, int protocol) +{ + /* Allocate the appropriate connection structure. This reserves the + * the connection structure is is unallocated at this point. It will + * not actually be initialized until the socket is connected. + * + * Only SOCK_RAW is supported. + */ + + if (psock->s_type == SOCK_RAW) + { + return pkt_sockif_alloc(psock); + } + else + { + return -EPROTONOSUPPORT; + } +} + +/**************************************************************************** + * Name: pkt_send + * + * Description: + * Socket send() method for the raw packet socket. + * + * Parameters: + * psock An instance of the internal socket structure. + * buf Data to send + * len Length of data to send + * flags Send flags + * + * Returned Value: + * On success, returns the number of characters sent. On error, -1 is + * returned, and errno is set appropriately (see send() for the list of + * appropriate errors values. + * + ****************************************************************************/ + +static ssize_t pkt_send(FAR struct socket *psock, FAR const void *buf, + size_t len, int flags) +{ + ssize_t ret; + + /* Only SOCK_RAW is supported */ + + if (psock->s_type == SOCK_RAW) + { + /* Raw packet send */ + + ret = psock_pkt_send(psock, buf, len); + } + else + { + /* EDESTADDRREQ. Signifies that the socket is not connection-mode and no peer + * address is set. + */ + + ret = -EDESTADDRREQ; + } + + return ret; +} + +/**************************************************************************** + * Name: pkt_sendto + * + * Description: + * Implements the sendto() operation for the case of the raw packet socket. + * + * Parameters: + * psock A pointer to a NuttX-specific, internal socket structure + * buf Data to send + * len Length of data to send + * flags Send flags + * to Address of recipient + * tolen The length of the address structure + * + * Returned Value: + * On success, returns the number of characters sent. On error, -1 is + * returned, and errno is set appropriately (see send_to() for the list of + * appropriate errors values. + * + ****************************************************************************/ + +static ssize_t pkt_sendto(FAR struct socket *psock, FAR const void *buf, + size_t len, int flags, FAR const struct sockaddr *to, + socklen_t tolen) +{ + nerr("ERROR: sendto() not supported for raw packet sockets\n"); + return -EAFNOSUPPORT; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: + * + * Description: + * + * Parameters: + * + * Returned Value: + * + * Assumptions: + * + ****************************************************************************/ + +#endif /* CONFIG_NET_PKT */ diff --git a/net/socket/Make.defs b/net/socket/Make.defs index 6f433cd97a8..93a3d1b8c54 100644 --- a/net/socket/Make.defs +++ b/net/socket/Make.defs @@ -1,7 +1,7 @@ ############################################################################ # net/socket/Make.defs # -# Copyright (C) 2014-2015 Gregory Nutt. All rights reserved. +# Copyright (C) 2014-2015, 2017 Gregory Nutt. All rights reserved. # Author: Gregory Nutt # # Redistribution and use in source and binary forms, with or without @@ -39,6 +39,16 @@ SOCK_CSRCS += bind.c connect.c getsockname.c recv.c recvfrom.c send.c SOCK_CSRCS += sendto.c socket.c net_sockets.c net_close.c net_dupsd.c SOCK_CSRCS += net_dupsd2.c net_clone.c net_poll.c net_vfcntl.c +# Socket address families + +SOCK_CSRCS += net_sockif.c + +ifeq ($(CONFIG_NET_IPv4),y) +SOCK_CSRCS += inet_sockif.c +else ifeq ($(CONFIG_NET_IPv6),y) +SOCK_CSRCS += inet_sockif.c +endif + # TCP/IP support ifeq ($(CONFIG_NET_TCP),y) diff --git a/net/socket/accept.c b/net/socket/accept.c index a93e490b9e2..b295542c28f 100644 --- a/net/socket/accept.c +++ b/net/socket/accept.c @@ -224,6 +224,7 @@ int psock_accept(FAR struct socket *psock, FAR struct sockaddr *addr, newsock->s_domain = psock->s_domain; newsock->s_type = SOCK_STREAM; + newsock->s_sockif = psock->s_sockif; /* Perform the correct accept operation for this address domain */ diff --git a/net/socket/inet_sockif.c b/net/socket/inet_sockif.c new file mode 100644 index 00000000000..62f8de0eb56 --- /dev/null +++ b/net/socket/inet_sockif.c @@ -0,0 +1,435 @@ +/**************************************************************************** + * net/socket/inet_sockif.c + * + * Copyright (C) 2017 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include + +#include + +#include "tcp/tcp.h" +#include "udp/udp.h" +#include "socket/socket.h" + +#if defined(CONFIG_NET_IPv4) || defined(CONFIG_NET_IPv6) + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static int inet_setup(FAR struct socket *psock, int protocol); +static ssize_t inet_send(FAR struct socket *psock, FAR const void *buf, + size_t len, int flags); +static ssize_t inet_sendto(FAR struct socket *psock, FAR const void *buf, + size_t len, int flags, FAR const struct sockaddr *to, + socklen_t tolen); + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +const struct sock_intf_s g_inet_sockif = +{ + inet_setup, /* si_setup */ + inet_send, /* si_send */ + inet_sendto, /* si_sendto */ +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: inet_tcp_alloc + * + * Description: + * Allocate and attach a TCP connection structure. + * + ****************************************************************************/ + +#ifdef NET_TCP_HAVE_STACK +static int inet_tcp_alloc(FAR struct socket *psock) +{ + /* Allocate the TCP connection structure */ + + FAR struct tcp_conn_s *conn = tcp_alloc(psock->s_domain); + if (conn == NULL) + { + /* Failed to reserve a connection structure */ + + nerr("ERROR: Failed to reserve TCP connection structure\n"); + return -ENOMEM; + } + + /* Set the reference count on the connection structure. This reference + * count will be incremented only if the socket is dup'ed + */ + + DEBUGASSERT(conn->crefs == 0); + conn->crefs = 1; + + /* Save the pre-allocated connection in the socket structure */ + + psock->s_conn = conn; + return OK; +} +#endif /* NET_TCP_HAVE_STACK */ + +/**************************************************************************** + * Name: inet_udp_alloc + * + * Description: + * Allocate and attach a UDP connection structure. + * + ****************************************************************************/ + +#ifdef NET_UDP_HAVE_STACK +static int inet_udp_alloc(FAR struct socket *psock) +{ + /* Allocate the UDP connection structure */ + + FAR struct udp_conn_s *conn = udp_alloc(psock->s_domain); + if (conn == NULL) + { + /* Failed to reserve a connection structure */ + + nerr("ERROR: Failed to reserve UDP connection structure\n"); + return -ENOMEM; + } + + /* Set the reference count on the connection structure. This reference + * count will be incremented only if the socket is dup'ed + */ + + DEBUGASSERT(conn->crefs == 0); + conn->crefs = 1; + + /* Save the pre-allocated connection in the socket structure */ + + psock->s_conn = conn; + return OK; +} +#endif /* NET_UDP_HAVE_STACK */ + +/**************************************************************************** + * Name: inet_setup + * + * Description: + * Called for socket() to verify that the provided socket type and + * protocol are usable by this address family. Perform any family- + * specific socket fields. + * + * NOTE: This is common logic for both the AF_INET and AF_INET6 address + * families. + * + * Parameters: + * psock A pointer to a user allocated socket structure to be initialized. + * protocol (see sys/socket.h) + * + * Returned Value: + * Zero (OK) is returned on success. Otherwise, a negater errno value is + * returned. + * + ****************************************************************************/ + +static int inet_setup(FAR struct socket *psock, int protocol) +{ + /* Allocate the appropriate connection structure. This reserves the + * the connection structure is is unallocated at this point. It will + * not actually be initialized until the socket is connected. + * + * Only SOCK_STREAM and SOCK_DGRAM and possible SOCK_RAW are supported. + */ + + switch (psock->s_type) + { +#ifdef CONFIG_NET_TCP + case SOCK_STREAM: + if (protocol != 0 && protocol != IPPROTO_TCP) + { + nerr("ERROR: Unsupported stream protocol: %d\n", protocol); + return -EPROTONOSUPPORT; + } + +#ifdef NET_TCP_HAVE_STACK + /* Allocate and attach the TCP connection structure */ + + return inet_tcp_alloc(psock); +#else + return = -ENETDOWN; +#endif +#endif /* CONFIG_NET_TCP */ + +#ifdef CONFIG_NET_UDP + case SOCK_DGRAM: + if (protocol != 0 && protocol != IPPROTO_UDP) + { + nerr("ERROR: Unsupported datagram protocol: %d\n", protocol); + return -EPROTONOSUPPORT; + } + +#ifdef NET_UDP_HAVE_STACK + /* Allocate and attach the UDP connection structure */ + + return inet_udp_alloc(psock); +#else + return -ENETDOWN; +#endif +#endif /* CONFIG_NET_UDP */ + + default: + nerr("ERROR: Unsupported type: %d\n", psock->s_type); + return -EPROTONOSUPPORT; + } +} + +/**************************************************************************** + * Name: inet_send + * + * Description: + * The inet_send() call may be used only when the socket is in a connected + * state (so that the intended recipient is known). + * + * Parameters: + * psock An instance of the internal socket structure. + * buf Data to send + * len Length of data to send + * flags Send flags + * + * Returned Value: + * On success, returns the number of characters sent. On error, -1 is + * returned, and errno is set appropriately (see send() for the list of + * appropriate errors values. + * + ****************************************************************************/ + +static ssize_t inet_send(FAR struct socket *psock, FAR const void *buf, + size_t len, int flags) +{ + ssize_t ret; + + switch (psock->s_type) + { +#ifdef CONFIG_NET_TCP + case SOCK_STREAM: + { +#ifdef CONFIG_NET_6LOWPAN + /* Try 6LoWPAN TCP packet send */ + + ret = psock_6lowpan_tcp_send(psock, buf, len); + +#if defined(CONFIG_NETDEV_MULTINIC) && defined(NET_TCP_HAVE_STACK) + if (ret < 0) + { + /* TCP/IP packet send */ + + ret = psock_tcp_send(psock, buf, len); + } +#endif /* CONFIG_NETDEV_MULTINIC && NET_TCP_HAVE_STACK */ +#elif defined(NET_TCP_HAVE_STACK) + ret = psock_tcp_send(psock, buf, len); +#else + ret = -ENOSYS; +#endif /* CONFIG_NET_6LOWPAN */ + } + break; +#endif /* CONFIG_NET_TCP */ + +#ifdef CONFIG_NET_UDP + case SOCK_DGRAM: + { +#if defined(CONFIG_NET_6LOWPAN) + /* Try 6LoWPAN UDP packet send */ + + ret = psock_6lowpan_udp_send(psock, buf, len); + +#if defined(CONFIG_NETDEV_MULTINIC) && defined(NET_UDP_HAVE_STACK) + if (ret < 0) + { + /* UDP/IP packet send */ + + ret = psock_udp_send(psock, buf, len); + } +#endif /* CONFIG_NETDEV_MULTINIC && NET_UDP_HAVE_STACK */ +#elif defined(NET_UDP_HAVE_STACK) + /* Only UDP/IP packet send */ + + ret = psock_udp_send(psock, buf, len); +#else + ret = -ENOSYS; +#endif /* CONFIG_NET_6LOWPAN */ + } + break; +#endif /* CONFIG_NET_UDP */ + + default: + { + /* EDESTADDRREQ. Signifies that the socket is not connection-mode + * and no peer address is set. + */ + + nerr("ERROR: Bad socket type: %d\n", psock->s_type); + ret = -EDESTADDRREQ; + } + break; + } + + return ret; +} + +/**************************************************************************** + * Name: inet_sendto + * + * Description: + * Implements the sendto() operation for the case of the AF_INET and + * AF_INET6 sockets. + * + * Parameters: + * psock A pointer to a NuttX-specific, internal socket structure + * buf Data to send + * len Length of data to send + * flags Send flags + * to Address of recipient + * tolen The length of the address structure + * + * Returned Value: + * On success, returns the number of characters sent. On error, -1 is + * returned, and errno is set appropriately (see send_to() for the list of + * appropriate errors values. + * + ****************************************************************************/ + +static ssize_t inet_sendto(FAR struct socket *psock, FAR const void *buf, + size_t len, int flags, FAR const struct sockaddr *to, + socklen_t tolen) +{ + socklen_t minlen; + ssize_t nsent; + + /* Verify that a valid address has been provided */ + + switch (to->sa_family) + { +#ifdef CONFIG_NET_IPv4 + case AF_INET: + minlen = sizeof(struct sockaddr_in); + break; +#endif + +#ifdef CONFIG_NET_IPv6 + case AF_INET6: + minlen = sizeof(struct sockaddr_in6); + break; +#endif + +#ifdef CONFIG_NET_LOCAL_DGRAM + case AF_LOCAL: + minlen = sizeof(sa_family_t); + break; +#endif + + default: + nerr("ERROR: Unrecognized address family: %d\n", to->sa_family); + return -EAFNOSUPPORT; + } + + if (tolen < minlen) + { + nerr("ERROR: Invalid address length: %d < %d\n", tolen, minlen); + return -EBADF; + } + +#ifdef CONFIG_NET_UDP + /* If this is a connected socket, then return EISCONN */ + + if (psock->s_type != SOCK_DGRAM) + { + nerr("ERROR: Connected socket\n"); + return -EBADF; + } + + /* Now handle the INET sendto() operation */ + +#if defined(CONFIG_NET_6LOWPAN) + /* Try 6LoWPAN UDP packet sendto() */ + + nsent = psock_6lowpan_udp_sendto(psock, buf, len, flags, to, tolen); + +#if defined(CONFIG_NETDEV_MULTINIC) && defined(NET_UDP_HAVE_STACK) + if (nsent < 0) + { + /* UDP/IP packet sendto */ + + nsent = psock_udp_sendto(psock, buf, len, flags, to, tolen); + } +#endif /* CONFIG_NETDEV_MULTINIC && NET_UDP_HAVE_STACK */ +#elif defined(NET_UDP_HAVE_STACK) + nsent = psock_udp_sendto(psock, buf, len, flags, to, tolen); +#else + nsent = -ENOSYS; +#endif /* CONFIG_NET_6LOWPAN */ +#else + nsent = -EISCONN; +#endif /* CONFIG_NET_UDP */ + + return nsent; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: + * + * Description: + * + * Parameters: + * + * Returned Value: + * + * Assumptions: + * + ****************************************************************************/ + +#endif /* CONFIG_NET_IPv4 || CONFIG_NET_IPv6 */ diff --git a/net/socket/net_clone.c b/net/socket/net_clone.c index d720eb8731b..c134116d7f4 100644 --- a/net/socket/net_clone.c +++ b/net/socket/net_clone.c @@ -79,6 +79,7 @@ int net_clone(FAR struct socket *psock1, FAR struct socket *psock2) psock2->s_domain = psock1->s_domain; /* IP domain: PF_INET, PF_INET6, or PF_PACKET */ psock2->s_type = psock1->s_type; /* Protocol type: Only SOCK_STREAM or SOCK_DGRAM */ + psock2->s_sockif = psock1->s_sockif; /* Socket interface */ psock2->s_flags = psock1->s_flags; /* See _SF_* definitions */ #ifdef CONFIG_NET_SOCKOPTS psock2->s_options = psock1->s_options; /* Selected socket options */ diff --git a/net/socket/net_sockif.c b/net/socket/net_sockif.c new file mode 100644 index 00000000000..76e273ff97d --- /dev/null +++ b/net/socket/net_sockif.c @@ -0,0 +1,111 @@ +/**************************************************************************** + * net/socket/net_sockif.c + * + * Copyright (C) 2017 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include + +#include + +#include "local/local.h" +#include "pkt/pkt.h" +#include "socket/socket.h" + +#ifdef CONFIG_NET + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: net_sockif + * + * Description: + * Return the socket interface associated with this address family. + * + * Parameters: + * family - Address family + * + * Returned Value: + * On success, a non-NULL instance of struct sock_intf_s is returned. NULL + * is returned only if the address family is not supported. + * + ****************************************************************************/ + +FAR const struct sock_intf_s *net_sockif(sa_family_t family) +{ + FAR const struct sock_intf_s *sockif = NULL; + + /* Get the socket interface */ + + switch (family) + { +#if defined(CONFIG_NET_IPv4) || defined(CONFIG_NET_IPv6) +#ifdef CONFIG_NET_IPv4 + case PF_INET: +#endif +#ifdef CONFIG_NET_IPv6 + case PF_INET6: +#endif + sockif = &g_inet_sockif; + break; +#endif + +#ifdef CONFIG_NET_LOCAL + case PF_LOCAL: + sockif = &g_local_sockif; + break; +#endif + +#ifdef CONFIG_NET_PKT + case PF_PACKET: + sockif = &g_pkt_sockif; + break; +#endif + + default: + nerr("ERROR: Address family unsupported: %d\n", family); + } + + return sockif; +} + +#endif /* CONFIG_NET */ diff --git a/net/socket/send.c b/net/socket/send.c index c47158aca25..1b8a39d47c3 100644 --- a/net/socket/send.c +++ b/net/socket/send.c @@ -42,6 +42,8 @@ #include #include #include +#include +#include #include @@ -126,132 +128,32 @@ ssize_t psock_send(FAR struct socket *psock, FAR const void *buf, size_t len, { ssize_t ret; + DEBUGASSERT(psock != NULL && buf != NULL); + /* Treat as a cancellation point */ (void)enter_cancellation_point(); - switch (psock->s_type) - { -#if defined(CONFIG_NET_PKT) - case SOCK_RAW: - { - /* Raw packet send */ - - ret = psock_pkt_send(psock, buf, len); - } - break; -#endif - -#if defined(CONFIG_NET_TCP) || defined(CONFIG_NET_LOCAL_STREAM) - case SOCK_STREAM: - { -#ifdef CONFIG_NET_LOCAL_STREAM -#ifdef CONFIG_NET_TCP - if (psock->s_domain == PF_LOCAL) -#endif - { - /* Local TCP packet send */ - - ret = psock_local_send(psock, buf, len, flags); - } -#endif /* CONFIG_NET_LOCAL_STREAM */ - -#ifdef CONFIG_NET_TCP -#ifdef CONFIG_NET_LOCAL_STREAM - else -#endif - { -#ifdef CONFIG_NET_6LOWPAN - /* Try 6LoWPAN TCP packet send */ - - ret = psock_6lowpan_tcp_send(psock, buf, len); - -#if defined(CONFIG_NETDEV_MULTINIC) && defined(NET_TCP_HAVE_STACK) - if (ret < 0) - { - /* TCP/IP packet send */ - - ret = psock_tcp_send(psock, buf, len); - } -#endif /* CONFIG_NETDEV_MULTINIC && NET_TCP_HAVE_STACK */ -#elif defined(NET_TCP_HAVE_STACK) - ret = psock_tcp_send(psock, buf, len); -#else - ret = -ENOSYS; -#endif /* CONFIG_NET_6LOWPAN */ - } -#endif /* CONFIG_NET_TCP */ - } - break; -#endif /* CONFIG_NET_TCP || CONFIG_NET_LOCAL_STREAM */ - -#ifdef CONFIG_NET_UDP - case SOCK_DGRAM: - { -#ifdef CONFIG_NET_LOCAL_DGRAM -#ifdef CONFIG_NET_UDP - if (psock->s_domain == PF_LOCAL) -#endif - { - /* Local UDP packet send */ -#warning Missing logic - ret = -ENOSYS; - } -#endif /* CONFIG_NET_LOCAL_DGRAM */ - -#ifdef CONFIG_NET_UDP -#ifdef CONFIG_NET_LOCAL_DGRAM - else -#endif - { -#if defined(CONFIG_NET_6LOWPAN) - /* Try 6LoWPAN UDP packet send */ - - ret = psock_6lowpan_udp_send(psock, buf, len); - -#if defined(CONFIG_NETDEV_MULTINIC) && defined(NET_UDP_HAVE_STACK) - if (ret < 0) - { - /* UDP/IP packet send */ - - ret = psock_udp_send(psock, buf, len); - } -#endif /* CONFIG_NETDEV_MULTINIC && NET_UDP_HAVE_STACK */ -#elif defined(NET_UDP_HAVE_STACK) - /* Only UDP/IP packet send */ - - ret = psock_udp_send(psock, buf, len); -#else - ret = -ENOSYS; -#endif /* CONFIG_NET_6LOWPAN */ - } -#endif /* CONFIG_NET_UDP */ - } - break; -#endif /* CONFIG_NET_UDP */ + /* Special case user sockets */ #ifdef CONFIG_NET_USRSOCK - case SOCK_USRSOCK_TYPE: - { - ret = usrsock_sendto(psock, buf, len, NULL, 0); - } - break; + if (psock->s_type SOCK_USRSOCK_TYPE) + { + ret = usrsock_sendto(psock, buf, len, NULL, 0); + } + else #endif /*CONFIG_NET_USRSOCK*/ + { + /* Let the address family's send() method handle the operation */ - default: - { - /* EDESTADDRREQ. Signifies that the socket is not connection-mode - * and no peer address is set. - */ - - ret = -EDESTADDRREQ; - } - break; + DEBUGASSERT(psock->s_sockif != NULL && psock->s_sockif->si_send != NULL); + ret = psock->s_sockif->si_send(psock, buf, len, flags); } leave_cancellation_point(); if (ret < 0) { + nerr("ERROR: socket si_send() (or usrsock_sendto()) failed: %d\n", ret); set_errno(-ret); ret = ERROR; } diff --git a/net/socket/sendto.c b/net/socket/sendto.c index 7b7c15f6c0e..6a7dd9a51aa 100644 --- a/net/socket/sendto.c +++ b/net/socket/sendto.c @@ -42,6 +42,7 @@ #include #include #include +#include #include #include @@ -127,18 +128,16 @@ ssize_t psock_sendto(FAR struct socket *psock, FAR const void *buf, size_t len, int flags, FAR const struct sockaddr *to, socklen_t tolen) { - socklen_t minlen; -#if defined(CONFIG_NET_UDP) || defined(CONFIG_NET_LOCAL_DGRAM) || \ - defined(CONFIG_NET_USRSOCK) ssize_t nsent; -#endif int errcode; + DEBUGASSERT(psock != NULL && buf != NULL); + /* If to is NULL or tolen is zero, then this function is same as send (for * connected socket types) */ - if (!to || !tolen) + if (to == NULL || tolen <= 0) { #if defined(CONFIG_NET_TCP) || defined(CONFIG_NET_LOCAL_STREAM) || \ defined(CONFIG_NET_USRSOCK) @@ -156,122 +155,35 @@ ssize_t psock_sendto(FAR struct socket *psock, FAR const void *buf, /* Perform the usrsock sendto operation */ nsent = usrsock_sendto(psock, buf, len, to, tolen); - if (nsent < 0) - { - errcode = -nsent; - goto errout; - } - - return nsent; } -#endif - - /* Verify that a valid address has been provided */ - - switch (to->sa_family) - { -#ifdef CONFIG_NET_IPv4 - case AF_INET: - minlen = sizeof(struct sockaddr_in); - break; -#endif - -#ifdef CONFIG_NET_IPv6 - case AF_INET6: - minlen = sizeof(struct sockaddr_in6); - break; -#endif - -#ifdef CONFIG_NET_LOCAL_DGRAM - case AF_LOCAL: - minlen = sizeof(sa_family_t); - break; -#endif - - default: - nerr("ERROR: Unrecognized address family: %d\n", to->sa_family); - errcode = EAFNOSUPPORT; - goto errout; - } - - if (tolen < minlen) - { - nerr("ERROR: Invalid address length: %d < %d\n", tolen, minlen); - errcode = EBADF; - goto errout; - } - - /* Verify that the psock corresponds to valid, allocated socket */ - - if (!psock || psock->s_crefs <= 0) - { - nerr("ERROR: Invalid socket\n"); - errcode = EBADF; - goto errout; - } - - /* If this is a connected socket, then return EISCONN */ - - if (psock->s_type != SOCK_DGRAM) - { - nerr("ERROR: Connected socket\n"); - errcode = EISCONN; - goto errout; - } - -#if defined(CONFIG_NET_UDP) || defined(CONFIG_NET_LOCAL_DGRAM) - /* Now handle the sendto() operation according to the socket domain, - * currently either IP or Unix domains. - */ - -#ifdef CONFIG_NET_LOCAL_DGRAM -#ifdef CONFIG_NET_UDP - if (psock->s_domain == PF_LOCAL) -#endif - { - nsent = psock_local_sendto(psock, buf, len, flags, to, tolen); - } -#endif /* CONFIG_NET_LOCAL_DGRAM */ - -#ifdef CONFIG_NET_UDP -#ifdef CONFIG_NET_LOCAL_DGRAM else #endif { -#if defined(CONFIG_NET_6LOWPAN) - /* Try 6LoWPAN UDP packet sendto() */ + /* Verify that the psock corresponds to valid, allocated socket */ - nsent = psock_6lowpan_udp_sendto(psock, buf, len, flags, to, tolen); - -#if defined(CONFIG_NETDEV_MULTINIC) && defined(NET_UDP_HAVE_STACK) - if (nsent < 0) + if (!psock || psock->s_crefs <= 0) { - /* UDP/IP packet sendto */ - - nsent = psock_udp_sendto(psock, buf, len, flags, to, tolen); + nerr("ERROR: Invalid socket\n"); + errcode = EBADF; + goto errout; } -#endif /* CONFIG_NETDEV_MULTINIC && NET_UDP_HAVE_STACK */ -#elif defined(NET_UDP_HAVE_STACK) - nsent = psock_udp_sendto(psock, buf, len, flags, to, tolen); -#else - nsent = -ENOSYS; -#endif /* CONFIG_NET_6LOWPAN */ + + /* Let the address family's send() method handle the operation */ + + DEBUGASSERT(psock->s_sockif != NULL && psock->s_sockif->si_send != NULL); + nsent = psock->s_sockif->si_send(psock, buf, len, flags); } -#endif /* CONFIG_NET_UDP */ /* Check if the domain-specific sendto() logic failed */ if (nsent < 0) { - nerr("ERROR: UDP or Unix domain sendto() failed: %ld\n", (long)nsent); + nerr("ERROR: Family-specific send failed: %ld\n", (long)nsent); errcode = -nsent; goto errout; } return nsent; -#else - errcode = ENOSYS; -#endif /* CONFIG_NET_UDP || CONFIG_NET_LOCAL_DGRAM */ errout: set_errno(errcode); diff --git a/net/socket/socket.c b/net/socket/socket.c index a0db45768a8..8c63c8095ff 100644 --- a/net/socket/socket.c +++ b/net/socket/socket.c @@ -38,168 +38,21 @@ ****************************************************************************/ #include -#ifdef CONFIG_NET #include #include #include #include -#include - -#include "socket/socket.h" -#include "tcp/tcp.h" -#include "udp/udp.h" -#include "pkt/pkt.h" -#include "local/local.h" #include "usrsock/usrsock.h" +#include "socket/socket.h" + +#ifdef CONFIG_NET /**************************************************************************** * Private Functions ****************************************************************************/ -/**************************************************************************** - * Name: psock_tcp_alloc - * - * Description: - * Allocate and attach a TCP connection structure. - * - ****************************************************************************/ - -#ifdef NET_TCP_HAVE_STACK -static int psock_tcp_alloc(FAR struct socket *psock) -{ - /* Allocate the TCP connection structure */ - - FAR struct tcp_conn_s *conn = tcp_alloc(psock->s_domain); - if (!conn) - { - /* Failed to reserve a connection structure */ - - return -ENOMEM; - } - - /* Set the reference count on the connection structure. This reference - * count will be incremented only if the socket is dup'ed - */ - - DEBUGASSERT(conn->crefs == 0); - conn->crefs = 1; - - /* Save the pre-allocated connection in the socket structure */ - - psock->s_conn = conn; - return OK; -} -#endif /* NET_TCP_HAVE_STACK */ - -/**************************************************************************** - * Name: psock_udp_alloc - * - * Description: - * Allocate and attach a UDP connection structure. - * - ****************************************************************************/ - -#ifdef NET_UDP_HAVE_STACK -static int psock_udp_alloc(FAR struct socket *psock) -{ - /* Allocate the UDP connection structure */ - - FAR struct udp_conn_s *conn = udp_alloc(psock->s_domain); - if (!conn) - { - /* Failed to reserve a connection structure */ - - return -ENOMEM; - } - - /* Set the reference count on the connection structure. This reference - * count will be incremented only if the socket is dup'ed - */ - - DEBUGASSERT(conn->crefs == 0); - conn->crefs = 1; - - /* Save the pre-allocated connection in the socket structure */ - - psock->s_conn = conn; - return OK; -} -#endif /* NET_UDP_HAVE_STACK */ - -/**************************************************************************** - * Name: psock_pkt_alloc - * - * Description: - * Allocate and attach a raw packet connection structure. - * - ****************************************************************************/ - -#ifdef CONFIG_NET_PKT -static int psock_pkt_alloc(FAR struct socket *psock) -{ - /* Allocate the packet socket connection structure and save in the new - * socket instance. - */ - - FAR struct pkt_conn_s *conn = pkt_alloc(); - if (!conn) - { - /* Failed to reserve a connection structure */ - - return -ENOMEM; - } - - /* Set the reference count on the connection structure. This reference - * count will be incremented only if the socket is dup'ed - */ - - DEBUGASSERT(conn->crefs == 0); - conn->crefs = 1; - - /* Save the pre-allocated connection in the socket structure */ - - psock->s_conn = conn; - return OK; -} -#endif /* CONFIG_NET_PKT */ - -/**************************************************************************** - * Name: psock_local_alloc - * - * Description: - * Allocate and attach a local, Unix domain connection structure. - * - ****************************************************************************/ - -#ifdef CONFIG_NET_LOCAL -static int psock_local_alloc(FAR struct socket *psock) -{ - /* Allocate the local connection structure */ - - FAR struct local_conn_s *conn = local_alloc(); - if (!conn) - { - /* Failed to reserve a connection structure */ - - return -ENOMEM; - } - - /* Set the reference count on the connection structure. This reference - * count will be incremented only if the socket is dup'ed - */ - - DEBUGASSERT(conn->lc_crefs == 0); - conn->lc_crefs = 1; - - /* Save the pre-allocated connection in the socket structure */ - - psock->s_conn = conn; - return OK; -} -#endif /* CONFIG_NET_LOCAL */ - /**************************************************************************** * Name: usrsock_socket_setup * @@ -316,148 +169,21 @@ static int usrsock_socket_setup(int domain, int type, int protocol, int psock_socket(int domain, int type, int protocol, FAR struct socket *psock) { -#ifdef CONFIG_NET_LOCAL - bool ipdomain = false; -#endif - bool dgramok = false; - int ret; + FAR const struct sock_intf_s *sockif = NULL; int errcode; - + int ret; + #ifdef CONFIG_NET_USRSOCK ret = usrsock_socket_setup(domain, type, protocol, psock); if (ret < 0) { + nerr("ERROR: usrsock_socket_setup() failed: %d\n", ret); errcode = -ret; goto errout; } #endif /* CONFIG_NET_USRSOCK */ - /* Only PF_INET, PF_INET6 or PF_PACKET domains supported */ - - switch (domain) - { -#ifdef CONFIG_NET_IPv4 - case PF_INET: -#ifdef CONFIG_NET_LOCAL - ipdomain = true; -#endif - dgramok = true; - break; -#endif - -#ifdef CONFIG_NET_IPv6 - case PF_INET6: -#ifdef CONFIG_NET_LOCAL - ipdomain = true; -#endif - dgramok = true; - break; -#endif - -#ifdef CONFIG_NET_LOCAL - case PF_LOCAL: - dgramok = true; - break; -#endif - -#ifdef CONFIG_NET_PKT - case PF_PACKET: - break; -#endif - - default: - errcode = EAFNOSUPPORT; - goto errout; - } - -#if defined(CONFIG_NET_LOCAL) && !defined(CONFIG_NET_LOCAL_STREAM) && !defined(CONFIG_NET_LOCAL_DGRAM) - UNUSED(ipdomain); -#endif - - /* Only SOCK_STREAM, SOCK_DGRAM and possible SOCK_RAW are supported */ - - switch (type) - { -#if defined(CONFIG_NET_TCP) || defined(CONFIG_NET_LOCAL_STREAM) - case SOCK_STREAM: -#ifdef CONFIG_NET_TCP -#ifdef CONFIG_NET_LOCAL_STREAM - if (ipdomain) -#endif - { - if ((protocol != 0 && protocol != IPPROTO_TCP) || !dgramok) - { - errcode = EPROTONOSUPPORT; - goto errout; - } - } -#endif /* CONFIG_NET_TCP */ - -#ifdef CONFIG_NET_LOCAL_STREAM -#ifdef CONFIG_NET_TCP - else -#endif - { - if (protocol != 0 || !dgramok) - { - errcode = EPROTONOSUPPORT; - goto errout; - } - } -#endif /* CONFIG_NET_LOCAL_STREAM */ - - break; -#endif /* CONFIG_NET_TCP || CONFIG_NET_LOCAL_STREAM */ - -#if defined(CONFIG_NET_UDP) || defined(CONFIG_NET_LOCAL_DGRAM) - case SOCK_DGRAM: -#ifdef CONFIG_NET_UDP -#ifdef CONFIG_NET_LOCAL_DGRAM - if (ipdomain) -#endif - { - if ((protocol != 0 && protocol != IPPROTO_UDP) || !dgramok) - { - errcode = EPROTONOSUPPORT; - goto errout; - } - } -#endif /* CONFIG_NET_UDP */ - -#ifdef CONFIG_NET_LOCAL_DGRAM -#ifdef CONFIG_NET_UDP - else -#endif - { - if (protocol != 0 || !dgramok) - { - errcode = EPROTONOSUPPORT; - goto errout; - } - } -#endif /* CONFIG_NET_LOCAL_DGRAM */ - - break; -#endif /* CONFIG_NET_UDP || CONFIG_NET_LOCAL_DGRAM */ - -#ifdef CONFIG_NET_PKT - case SOCK_RAW: - if (dgramok) - { - errcode = EPROTONOSUPPORT; - goto errout; - } - - break; -#endif - - default: - errcode = EPROTONOSUPPORT; - goto errout; - } - - /* Everything looks good. Initialize the socket structure */ - /* Save the protocol type */ + /* Initialize the socket structure */ psock->s_domain = domain; psock->s_type = type; @@ -466,115 +192,29 @@ int psock_socket(int domain, int type, int protocol, FAR struct socket *psock) psock->s_sndcb = NULL; #endif - /* Allocate the appropriate connection structure. This reserves the - * the connection structure is is unallocated at this point. It will - * not actually be initialized until the socket is connected. + /* Get the socket interface */ + + sockif = net_sockif(domain); + if (sockif == NULL) + { + nerr("ERROR: socket address family unsupported: %d\n", domain); + errcode = EAFNOSUPPORT; + goto errout; + } + + /* The remaining of the socket initialization depends on the address + * family. */ - errcode = ENOMEM; /* Assume failure to allocate connection instance */ - switch (type) + DEBUGASSERT(sockif->si_setup != NULL); + psock->s_sockif = sockif; + + ret = sockif->si_setup(psock, protocol); + if (ret < 0) { -#if defined(CONFIG_NET_TCP) || defined(CONFIG_NET_LOCAL_STREAM) - case SOCK_STREAM: - { -#ifdef CONFIG_NET_TCP -#ifdef CONFIG_NET_LOCAL_STREAM - if (ipdomain) -#endif - { -#ifdef NET_TCP_HAVE_STACK - /* Allocate and attach the TCP connection structure */ - - ret = psock_tcp_alloc(psock); -#else - ret = -ENETDOWN; -#endif - } -#endif /* CONFIG_NET_TCP */ - -#ifdef CONFIG_NET_LOCAL_STREAM -#ifdef CONFIG_NET_TCP - else -#endif - { - /* Allocate and attach the local connection structure */ - - ret = psock_local_alloc(psock); - } -#endif /* CONFIG_NET_LOCAL_STREAM */ - - /* Check for failures to allocate the connection structure. */ - - if (ret < 0) - { - /* Failed to reserve a connection structure */ - - errcode = -ret; - goto errout; - } - } - break; -#endif - -#if defined(CONFIG_NET_UDP) || defined(CONFIG_NET_LOCAL_DGRAM) - case SOCK_DGRAM: - { -#ifdef CONFIG_NET_UDP -#ifdef CONFIG_NET_LOCAL_DGRAM - if (ipdomain) -#endif - { -#ifdef NET_UDP_HAVE_STACK - /* Allocate and attach the UDP connection structure */ - - ret = psock_udp_alloc(psock); -#else - ret = -ENETDOWN; -#endif - } -#endif /* CONFIG_NET_UDP */ - -#ifdef CONFIG_NET_LOCAL_DGRAM -#ifdef CONFIG_NET_UDP - else -#endif - { - /* Allocate and attach the local connection structure */ - - ret = psock_local_alloc(psock); - } -#endif /* CONFIG_NET_LOCAL_DGRAM */ - - /* Check for failures to allocate the connection structure. */ - - if (ret < 0) - { - /* Failed to reserve a connection structure */ - - errcode = -ret; - goto errout; - } - } - break; -#endif - -#ifdef CONFIG_NET_PKT - case SOCK_RAW: - { - ret = psock_pkt_alloc(psock); - if (ret < 0) - { - /* Failed to reserve a connection structure */ - - errcode = -ret; - goto errout; - } - } - break; -#endif - - default: - break; + nerr("ERROR: socket si_setup() failed: %d\n", ret); + errcode = -ret; + goto errout; } return OK; @@ -632,6 +272,7 @@ int socket(int domain, int type, int protocol) sockfd = sockfd_allocate(0); if (sockfd < 0) { + nerr("ERROR: Failed to allodate a socket descriptor\n"); set_errno(ENFILE); return ERROR; } @@ -650,8 +291,9 @@ int socket(int domain, int type, int protocol) ret = psock_socket(domain, type, protocol, psock); if (ret < 0) { - /* Error already set by psock_socket() */ + /* errno already set by psock_socket() */ + nerr("ERROR: psock_socket() failed: %d\n", ret); goto errout; } diff --git a/net/socket/socket.h b/net/socket/socket.h index d4b05258090..36788d465ab 100644 --- a/net/socket/socket.h +++ b/net/socket/socket.h @@ -149,6 +149,10 @@ extern "C" #define EXTERN extern #endif +#if defined(CONFIG_NET_IPv4) || defined(CONFIG_NET_IPv6) +EXTERN const struct sock_intf_s g_inet_sockif; +#endif + /**************************************************************************** * Public Function Prototypes ****************************************************************************/ @@ -223,6 +227,23 @@ void sockfd_release(int sockfd); FAR struct socket *sockfd_socket(int sockfd); +/**************************************************************************** + * Name: net_sockif + * + * Description: + * Return the socket interface associated with this address family. + * + * Parameters: + * family - Address family + * + * Returned Value: + * On success, a non-NULL instance of struct sock_intf_s is returned. NULL + * is returned only if the address family is not supported. + * + ****************************************************************************/ + +FAR const struct sock_intf_s *net_sockif(sa_family_t family); + /**************************************************************************** * Name: net_startmonitor * From 0d08ce500e335896ee78e1476affa0f8b7e31ac4 Mon Sep 17 00:00:00 2001 From: Alan Carvalho de Assis Date: Wed, 12 Jul 2017 15:25:55 -0600 Subject: [PATCH 09/18] C library: Fix an error in mkstemp() the could result in an infinite loop. --- libc/stdlib/lib_mkstemp.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libc/stdlib/lib_mkstemp.c b/libc/stdlib/lib_mkstemp.c index fdf93cdfaf4..498d424e373 100644 --- a/libc/stdlib/lib_mkstemp.c +++ b/libc/stdlib/lib_mkstemp.c @@ -284,6 +284,8 @@ int mkstemp(FAR char *path_template) return fd; } + + retries--; } /* We could not find an unique filename */ From 447785b264a964da217a5688e66f740a55d1336f Mon Sep 17 00:00:00 2001 From: Gregory Nutt Date: Wed, 12 Jul 2017 16:22:23 -0600 Subject: [PATCH 10/18] SAMD/L21: Need to preserve errno value across syslog() call. --- arch/arm/src/samdl/sam_i2c_master.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/arch/arm/src/samdl/sam_i2c_master.c b/arch/arm/src/samdl/sam_i2c_master.c index 38fc1a26b4b..b377f1580a3 100644 --- a/arch/arm/src/samdl/sam_i2c_master.c +++ b/arch/arm/src/samdl/sam_i2c_master.c @@ -625,8 +625,9 @@ static int i2c_wait_for_bus(struct sam_i2c_dev_s *priv, unsigned int size) if (sem_timedwait(&priv->waitsem, (const struct timespec *)&ts) != OK) { - i2cinfo("timedwait error %d\n", errno); - return -errno; + int errcode = errno; + i2cinfo("timedwait error %d\n", errcode); + return -errcode; } return priv->result; From a71c504142b9e9ce2c2073719a95d0b7931a6472 Mon Sep 17 00:00:00 2001 From: Gregory Nutt Date: Wed, 12 Jul 2017 17:36:05 -0600 Subject: [PATCH 11/18] Socket I/F: Add recvfrom() method to interface. --- include/nuttx/net/net.h | 11 +- net/local/local.h | 8 +- net/local/local_recvfrom.c | 17 +- net/local/local_sockif.c | 19 +- net/pkt/Make.defs | 1 + net/pkt/pkt.h | 36 + net/pkt/pkt_recvfrom.c | 485 +++++++++ net/pkt/pkt_sockif.c | 20 +- net/socket/Make.defs | 4 +- net/socket/inet_recvfrom.c | 1664 +++++++++++++++++++++++++++++++ net/socket/inet_sockif.c | 19 +- net/socket/recvfrom.c | 1906 +----------------------------------- net/socket/socket.h | 37 +- 13 files changed, 2296 insertions(+), 1931 deletions(-) create mode 100644 net/pkt/pkt_recvfrom.c create mode 100644 net/socket/inet_recvfrom.c diff --git a/include/nuttx/net/net.h b/include/nuttx/net/net.h index 92d09a2623b..6d343cfd5f9 100644 --- a/include/nuttx/net/net.h +++ b/include/nuttx/net/net.h @@ -102,12 +102,15 @@ struct socket; /* Forward reference */ struct sock_intf_s { - CODE int (*si_setup)(FAR struct socket *psock, int protocol); + CODE int (*si_setup)(FAR struct socket *psock, int protocol); CODE ssize_t (*si_send)(FAR struct socket *psock, FAR const void *buf, - size_t len, int flags); + size_t len, int flags); CODE ssize_t (*si_sendto)(FAR struct socket *psock, FAR const void *buf, - size_t len, int flags, FAR const struct sockaddr *to, - socklen_t tolen); + size_t len, int flags, FAR const struct sockaddr *to, + socklen_t tolen); + CODE ssize_t (*si_recvfrom)(FAR struct socket *psock, FAR void *buf, + size_t len, int flags, FAR struct sockaddr *from, + FAR socklen_t *fromlen); }; /* This is the internal representation of a socket reference by a file diff --git a/net/local/local.h b/net/local/local.h index 222d846cc0f..2233b2c8df1 100644 --- a/net/local/local.h +++ b/net/local/local.h @@ -428,7 +428,7 @@ ssize_t psock_local_sendto(FAR struct socket *psock, FAR const void *buf, int local_send_packet(int fd, FAR const uint8_t *buf, size_t len); /**************************************************************************** - * Name: psock_recvfrom + * Name: local_recvfrom * * Description: * recvfrom() receives messages from a local socket, and may be used to @@ -455,9 +455,9 @@ int local_send_packet(int fd, FAR const uint8_t *buf, size_t len); * ****************************************************************************/ -ssize_t psock_local_recvfrom(FAR struct socket *psock, FAR void *buf, - size_t len, int flags, FAR struct sockaddr *from, - FAR socklen_t *fromlen); +ssize_t local_recvfrom(FAR struct socket *psock, FAR void *buf, + size_t len, int flags, FAR struct sockaddr *from, + FAR socklen_t *fromlen); /**************************************************************************** * Name: local_fifo_read diff --git a/net/local/local_recvfrom.c b/net/local/local_recvfrom.c index a19d5323b39..f9de54d6d10 100644 --- a/net/local/local_recvfrom.c +++ b/net/local/local_recvfrom.c @@ -387,11 +387,11 @@ errout_with_halfduplex: ****************************************************************************/ /**************************************************************************** - * Name: psock_local_recvfrom + * Name: local_recvfrom * * Description: - * psock_local_recvfrom() receives messages from a local socket and may be - * used to receive data on a socket whether or not it is connection-oriented. + * local_recvfrom() receives messages from a local socket and may be used + * to receive data on a socket whether or not it is connection-oriented. * * If from is not NULL, and the underlying protocol provides the source * address, this source address is filled in. The argument fromlen @@ -409,14 +409,15 @@ errout_with_halfduplex: * Returned Value: * On success, returns the number of characters received. If no data is * available to be received and the peer has performed an orderly shutdown, - * recv() will return 0. Otherwise, on errors, -1 is returned, and errno - * is set appropriately (see receive from for the complete list). + * recv() will return 0. Otherwise, on errors, a negated errno value is + * returned (see recv_from() for the complete list of appropriate error + * values). * ****************************************************************************/ -ssize_t psock_local_recvfrom(FAR struct socket *psock, FAR void *buf, - size_t len, int flags, FAR struct sockaddr *from, - FAR socklen_t *fromlen) +ssize_t local_recvfrom(FAR struct socket *psock, FAR void *buf, + size_t len, int flags, FAR struct sockaddr *from, + FAR socklen_t *fromlen) { DEBUGASSERT(psock && psock->s_conn && buf); diff --git a/net/local/local_sockif.c b/net/local/local_sockif.c index c7e9c1ba54c..0baeab57e29 100644 --- a/net/local/local_sockif.c +++ b/net/local/local_sockif.c @@ -70,9 +70,10 @@ static ssize_t local_sendto(FAR struct socket *psock, FAR const void *buf, const struct sock_intf_s g_local_sockif = { - local_setup, /* si_setup */ - local_send, /* si_send */ - local_sendto, /* si_sendto */ + local_setup, /* si_setup */ + local_send, /* si_send */ + local_sendto, /* si_sendto */ + local_recvfrom /* si_recvfrom */ }; /**************************************************************************** @@ -183,9 +184,9 @@ static int local_setup(FAR struct socket *psock, int protocol) * flags Send flags * * Returned Value: - * On success, returns the number of characters sent. On error, -1 is - * returned, and errno is set appropriately (see send() for the list of - * appropriate errors values. + * On success, returns the number of characters sent. On error, a negated + * errno value is returned (see send() for the list of appropriate error + * values. * ****************************************************************************/ @@ -245,9 +246,9 @@ static ssize_t local_send(FAR struct socket *psock, FAR const void *buf, * tolen The length of the address structure * * Returned Value: - * On success, returns the number of characters sent. On error, -1 is - * returned, and errno is set appropriately (see send_to() for the list of - * appropriate errors values. + * On success, returns the number of characters sent. On error, a negated + * errno value is returned (see send_to() for the list of appropriate error + * values. * ****************************************************************************/ diff --git a/net/pkt/Make.defs b/net/pkt/Make.defs index f3e97740ba9..12583853345 100644 --- a/net/pkt/Make.defs +++ b/net/pkt/Make.defs @@ -41,6 +41,7 @@ ifeq ($(CONFIG_NET_PKT),y) SOCK_CSRCS += pkt_sockif.c SOCK_CSRCS += pkt_send.c +SOCK_CSRCS += pkt_recvfrom.c # Transport layer diff --git a/net/pkt/pkt.h b/net/pkt/pkt.h index 041246b2fc1..61dee0929a8 100644 --- a/net/pkt/pkt.h +++ b/net/pkt/pkt.h @@ -101,6 +101,7 @@ EXTERN const struct sock_intf_s g_pkt_sockif; struct net_driver_s; /* Forward reference */ struct eth_hdr_s; /* Forward reference */ +struct socket; /* Forward reference */ /**************************************************************************** * Name: pkt_initialize() @@ -201,6 +202,41 @@ uint16_t pkt_callback(FAR struct net_driver_s *dev, /* pkt_input() is prototyped in include/nuttx/net/pkt.h */ +/**************************************************************************** + * Name: pkt_recvfrom + * + * Description: + * Implements the socket recvfrom interface for the case of the AF_INET + * and AF_INET6 address families. pkt_recvfrom() receives messages from + * a socket, and may be used to receive data on a socket whether or not it + * is connection-oriented. + * + * If 'from' is not NULL, and the underlying protocol provides the source + * address, this source address is filled in. The argument 'fromlen' is + * initialized to the size of the buffer associated with from, and + * modified on return to indicate the actual size of the address stored + * there. + * + * Parameters: + * psock A pointer to a NuttX-specific, internal socket structure + * buf Buffer to receive data + * len Length of buffer + * flags Receive flags + * from Address of source (may be NULL) + * fromlen The length of the address structure + * + * Returned Value: + * On success, returns the number of characters received. If no data is + * available to be received and the peer has performed an orderly shutdown, + * recv() will return 0. Otherwise, on errors, a negated errno value is + * returned (see recvfrom() for the list of appropriate error values). + * + ****************************************************************************/ + +ssize_t pkt_recvfrom(FAR struct socket *psock, FAR void *buf, size_t len, + int flags, FAR struct sockaddr *from, + FAR socklen_t *fromlen); + /**************************************************************************** * Name: pkt_find_device * diff --git a/net/pkt/pkt_recvfrom.c b/net/pkt/pkt_recvfrom.c new file mode 100644 index 00000000000..d4348f31b43 --- /dev/null +++ b/net/pkt/pkt_recvfrom.c @@ -0,0 +1,485 @@ +/**************************************************************************** + * net/socket/pkt_recvfrom.c + * + * Copyright (C) 2007-2009, 2011-2017 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#ifdef CONFIG_NET + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include "netdev/netdev.h" +#include "devif/devif.h" +#include "pkt/pkt.h" +#include "socket/socket.h" +#include + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct pkt_recvfrom_s +{ + FAR struct devif_callback_s *pr_cb; /* Reference to callback instance */ + sem_t pr_sem; /* Semaphore signals recv completion */ + size_t pr_buflen; /* Length of receive buffer */ + uint8_t *pr_buffer; /* Pointer to receive buffer */ + ssize_t pr_recvlen; /* The received length */ + int pr_result; /* Success:OK, failure:negated errno */ +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: pkt_add_recvlen + * + * Description: + * Update information about space available for new data and update size + * of data in buffer, This logic accounts for the case where + * recvfrom_udpreadahead() sets state.pr_recvlen == -1 . + * + * Parameters: + * pstate recvfrom state structure + * recvlen size of new data appended to buffer + * + * Returned Value: + * None + * + ****************************************************************************/ + +static inline void pkt_add_recvlen(FAR struct pkt_recvfrom_s *pstate, + size_t recvlen) +{ + if (pstate->pr_recvlen < 0) + { + pstate->pr_recvlen = 0; + } + + pstate->pr_recvlen += recvlen; + pstate->pr_buffer += recvlen; + pstate->pr_buflen -= recvlen; +} + +/**************************************************************************** + * Name: pkt_recvfrom_newdata + * + * Description: + * Copy the read data from the packet + * + * Parameters: + * dev The structure of the network driver that caused the interrupt + * pstate recvfrom state structure + * + * Returned Value: + * None. + * + * Assumptions: + * The network is locked. + * + ****************************************************************************/ + +static void pkt_recvfrom_newdata(FAR struct net_driver_s *dev, + FAR struct pkt_recvfrom_s *pstate) +{ + size_t recvlen; + + if (dev->d_len > pstate->pr_buflen) + { + recvlen = pstate->pr_buflen; + } + else + { + recvlen = dev->d_len; + } + + /* Copy the new packet data into the user buffer */ + + memcpy(pstate->pr_buffer, dev->d_buf, recvlen); + ninfo("Received %d bytes (of %d)\n", (int)recvlen, (int)dev->d_len); + + /* Update the accumulated size of the data read */ + + pkt_add_recvlen(pstate, recvlen); +} + +/**************************************************************************** + * Name: pkt_recvfrom_sender + * + * Description: + * + * Parameters: + * + * Returned Values: + * + * Assumptions: + * + ****************************************************************************/ + +static inline void pkt_recvfrom_sender(FAR struct net_driver_s *dev, + FAR struct pkt_recvfrom_s *pstate) +{ +} + +/**************************************************************************** + * Name: pkt_recvfrom_interrupt + * + * Description: + * + * Parameters: + * + * Returned Values: + * + * Assumptions: + * + ****************************************************************************/ + +static uint16_t pkt_recvfrom_interrupt(FAR struct net_driver_s *dev, + FAR void *pvconn, FAR void *pvpriv, + uint16_t flags) +{ + struct pkt_recvfrom_s *pstate = (struct pkt_recvfrom_s *)pvpriv; + + ninfo("flags: %04x\n", flags); + + /* 'priv' might be null in some race conditions (?) */ + + if (pstate) + { + /* If a new packet is available, then complete the read action. */ + + if ((flags & PKT_NEWDATA) != 0) + { + /* Copy the packet */ + + pkt_recvfrom_newdata(dev, pstate); + + /* We are finished. */ + + ninfo("PKT done\n"); + + /* Don't allow any further call backs. */ + + pstate->pr_cb->flags = 0; + pstate->pr_cb->priv = NULL; + pstate->pr_cb->event = NULL; +#if 0 + /* Save the sender's address in the caller's 'from' location */ + + pkt_recvfrom_sender(dev, pstate); +#endif + /* indicate that the data has been consumed */ + + flags &= ~PKT_NEWDATA; + + /* Wake up the waiting thread, returning the number of bytes + * actually read. + */ + + sem_post(&pstate->pr_sem); + } + } + + return flags; +} + +/**************************************************************************** + * Name: pkt_recvfrom_initialize + * + * Description: + * Initialize the state structure + * + * Parameters: + * psock Pointer to the socket structure for the socket + * buf Buffer to receive data + * len Length of buffer + * pstate A pointer to the state structure to be initialized + * + * Returned Value: + * None + * + * Assumptions: + * + ****************************************************************************/ + +static void pkt_recvfrom_initialize(FAR struct socket *psock, FAR void *buf, + size_t len, FAR struct sockaddr *infrom, + FAR socklen_t *fromlen, + FAR struct pkt_recvfrom_s *pstate) +{ + /* Initialize the state structure. */ + + memset(pstate, 0, sizeof(struct pkt_recvfrom_s)); + + /* This semaphore is used for signaling and, hence, should not have + * priority inheritance enabled. + */ + + (void)sem_init(&pstate->pr_sem, 0, 0); /* Doesn't really fail */ + (void)sem_setprotocol(&pstate->pr_sem, SEM_PRIO_NONE); + + pstate->pr_buflen = len; + pstate->pr_buffer = buf; +} + +/* The only un-initialization that has to be performed is destroying the + * semaphore. + */ + +#define pkt_recvfrom_uninitialize(s) sem_destroy(&(s)->pr_sem) + +/**************************************************************************** + * Name: pkt_recvfrom_result + * + * Description: + * Evaluate the result of the recv operations + * + * Parameters: + * result The result of the net_lockedwait operation (may indicate EINTR) + * pstate A pointer to the state structure to be initialized + * + * Returned Value: + * The result of the recv operation with errno set appropriately + * + * Assumptions: + * + ****************************************************************************/ + +static ssize_t pkt_recvfrom_result(int result, struct pkt_recvfrom_s *pstate) +{ + int save_errno = get_errno(); /* In case something we do changes it */ + + /* Check for a error/timeout detected by the interrupt handler. Errors are + * signaled by negative errno values for the rcv length + */ + + if (pstate->pr_result < 0) + { + /* This might return EAGAIN on a timeout or ENOTCONN on loss of + * connection (TCP only) + */ + + return pstate->pr_result; + } + + /* If net_lockedwait failed, then we were probably reawakened by a signal. In + * this case, net_lockedwait will have set errno appropriately. + */ + + if (result < 0) + { + return -save_errno; + } + + return pstate->pr_recvlen; +} + +/**************************************************************************** + * Name: pkt_recvfrom_rxnotify + * + * Description: + * Notify the appropriate device driver that we are ready to receive a + * packet (PKT) + * + * Parameters: + * conn - The PKT connection structure + * + * Returned Value: + * None + * + ****************************************************************************/ + +#if 0 /* Not implemented */ +static void pkt_recvfrom_rxnotify(FAR struct pkt_conn_s *conn) +{ +# warning Missing logic +} +#endif + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: pkt_recvfrom + * + * Description: + * Implements the socket recvfrom interface for the case of the AF_INET + * and AF_INET6 address families. pkt_recvfrom() receives messages from + * a socket, and may be used to receive data on a socket whether or not it + * is connection-oriented. + * + * If 'from' is not NULL, and the underlying protocol provides the source + * address, this source address is filled in. The argument 'fromlen' is + * initialized to the size of the buffer associated with from, and + * modified on return to indicate the actual size of the address stored + * there. + * + * Parameters: + * psock A pointer to a NuttX-specific, internal socket structure + * buf Buffer to receive data + * len Length of buffer + * flags Receive flags + * from Address of source (may be NULL) + * fromlen The length of the address structure + * + * Returned Value: + * On success, returns the number of characters received. If no data is + * available to be received and the peer has performed an orderly shutdown, + * recv() will return 0. Otherwise, on errors, a negated errno value is + * returned (see recvfrom() for the list of appropriate error values). + * + ****************************************************************************/ + +ssize_t pkt_recvfrom(FAR struct socket *psock, FAR void *buf, size_t len, + int flags, FAR struct sockaddr *from, + FAR socklen_t *fromlen) +{ + FAR struct pkt_conn_s *conn = (FAR struct pkt_conn_s *)psock->s_conn; + FAR struct net_driver_s *dev; + struct pkt_recvfrom_s state; + ssize_t ret; + + /* If a 'from' address has been provided, verify that it is large + * enough to hold this address family. + */ + + if (from != NULL && *fromlen < sizeof(sa_family_t)) + { + return -EINVAL; + } + + if (psock->s_type != -SOCK_RAW) + { + nerr("ERROR: Unsupported socket type: %d\n", psock->s_type); + ret = -ENOSYS; + } + + /* Perform the packet recvfrom() operation */ + + /* Initialize the state structure. This is done with interrupts + * disabled because we don't want anything to happen until we + * are ready. + */ + + net_lock(); + pkt_recvfrom_initialize(psock, buf, len, from, fromlen, &state); + + /* Get the device driver that will service this transfer */ + + dev = pkt_find_device(conn); + if (dev == NULL) + { + ret = -ENODEV; + goto errout_with_state; + } + + /* TODO pkt_recvfrom_initialize() expects from to be of type sockaddr_in, but + * in our case is sockaddr_ll + */ + +#if 0 + ret = pkt_connect(conn, NULL); + if (ret < 0) + { + goto errout_with_state; + } +#endif + + /* Set the socket state to receiving */ + + psock->s_flags = _SS_SETSTATE(psock->s_flags, _SF_RECV); + + /* Set up the callback in the connection */ + + state.pr_cb = pkt_callback_alloc(dev, conn); + if (state.pr_cb) + { + state.pr_cb->flags = (PKT_NEWDATA | PKT_POLL); + state.pr_cb->priv = (FAR void *)&state; + state.pr_cb->event = pkt_recvfrom_interrupt; + + /* Notify the device driver of the receive call */ + +#if 0 /* Not implemented */ + pkt_recvfrom_rxnotify(conn); +#endif + + /* Wait for either the receive to complete or for an error/timeout to occur. + * NOTES: (1) net_lockedwait will also terminate if a signal is received, (2) + * interrupts are disabled! They will be re-enabled while the task sleeps + * and automatically re-enabled when the task restarts. + */ + + ret = net_lockedwait(&state.pr_sem); + + /* Make sure that no further interrupts are processed */ + + pkt_callback_free(dev, conn, state.pr_cb); + ret = pkt_recvfrom_result(ret, &state); + } + else + { + ret = -EBUSY; + } + + /* Set the socket state to idle */ + + psock->s_flags = _SS_SETSTATE(psock->s_flags, _SF_IDLE); + +errout_with_state: + net_unlock(); + pkt_recvfrom_uninitialize(&state); + return ret; +} + +#endif /* CONFIG_NET */ diff --git a/net/pkt/pkt_sockif.c b/net/pkt/pkt_sockif.c index 2f698de2b1a..9e3f0f6758b 100644 --- a/net/pkt/pkt_sockif.c +++ b/net/pkt/pkt_sockif.c @@ -43,6 +43,7 @@ #include #include #include +#include #include @@ -67,9 +68,10 @@ static ssize_t pkt_sendto(FAR struct socket *psock, FAR const void *buf, const struct sock_intf_s g_pkt_sockif = { - pkt_setup, /* si_setup */ - pkt_send, /* si_send */ - pkt_sendto, /* si_sendto */ + pkt_setup, /* si_setup */ + pkt_send, /* si_send */ + pkt_sendto, /* si_sendto */ + pkt_recvfrom /* si_recvfrom */ }; /**************************************************************************** @@ -162,9 +164,9 @@ static int pkt_setup(FAR struct socket *psock, int protocol) * flags Send flags * * Returned Value: - * On success, returns the number of characters sent. On error, -1 is - * returned, and errno is set appropriately (see send() for the list of - * appropriate errors values. + * On success, returns the number of characters sent. On error, a negated + * errno value is returned (see send() for the list of appropriate error + * values. * ****************************************************************************/ @@ -208,9 +210,9 @@ static ssize_t pkt_send(FAR struct socket *psock, FAR const void *buf, * tolen The length of the address structure * * Returned Value: - * On success, returns the number of characters sent. On error, -1 is - * returned, and errno is set appropriately (see send_to() for the list of - * appropriate errors values. + * On success, returns the number of characters sent. On error, a negated + * errno value is returned (see send_to() for the list of appropriate error + * values. * ****************************************************************************/ diff --git a/net/socket/Make.defs b/net/socket/Make.defs index 93a3d1b8c54..105f70a6361 100644 --- a/net/socket/Make.defs +++ b/net/socket/Make.defs @@ -44,9 +44,9 @@ SOCK_CSRCS += net_dupsd2.c net_clone.c net_poll.c net_vfcntl.c SOCK_CSRCS += net_sockif.c ifeq ($(CONFIG_NET_IPv4),y) -SOCK_CSRCS += inet_sockif.c +SOCK_CSRCS += inet_sockif.c inet_recvfrom.c else ifeq ($(CONFIG_NET_IPv6),y) -SOCK_CSRCS += inet_sockif.c +SOCK_CSRCS += inet_sockif.c inet_recvfrom.c endif # TCP/IP support diff --git a/net/socket/inet_recvfrom.c b/net/socket/inet_recvfrom.c new file mode 100644 index 00000000000..b38c15e82d2 --- /dev/null +++ b/net/socket/inet_recvfrom.c @@ -0,0 +1,1664 @@ +/**************************************************************************** + * net/socket/inet_recvfrom.c + * + * Copyright (C) 2007-2009, 2011-2017 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#ifdef CONFIG_NET + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "netdev/netdev.h" +#include "devif/devif.h" +#include "tcp/tcp.h" +#include "udp/udp.h" +#include "pkt/pkt.h" +#include "local/local.h" +#include "socket/socket.h" +#include "usrsock/usrsock.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define IPv4BUF ((struct ipv4_hdr_s *)&dev->d_buf[NET_LL_HDRLEN(dev)]) +#define IPv6BUF ((struct ipv6_hdr_s *)&dev->d_buf[NET_LL_HDRLEN(dev)]) + +#define UDPIPv4BUF ((struct udp_hdr_s *)&dev->d_buf[NET_LL_HDRLEN(dev) + IPv4_HDRLEN]) +#define UDPIPv6BUF ((struct udp_hdr_s *)&dev->d_buf[NET_LL_HDRLEN(dev) + IPv6_HDRLEN]) + +#define TCPIPv4BUF ((struct tcp_hdr_s *)&dev->d_buf[NET_LL_HDRLEN(dev) + IPv4_HDRLEN]) +#define TCPIPv6BUF ((struct tcp_hdr_s *)&dev->d_buf[NET_LL_HDRLEN(dev) + IPv6_HDRLEN]) + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +#if defined(NET_UDP_HAVE_STACK) || defined(NET_TCP_HAVE_STACK) +struct inet_recvfrom_s +{ + FAR struct socket *ir_sock; /* The parent socket structure */ +#ifdef CONFIG_NET_SOCKOPTS + systime_t ir_starttime; /* rcv start time for determining timeout */ +#endif + FAR struct devif_callback_s *ir_cb; /* Reference to callback instance */ + sem_t ir_sem; /* Semaphore signals recv completion */ + size_t ir_buflen; /* Length of receive buffer */ + uint8_t *ir_buffer; /* Pointer to receive buffer */ + FAR struct sockaddr *ir_from; /* Address of sender */ + FAR socklen_t *ir_fromlen; /* Number of bytes allocated for address of sender */ + ssize_t ir_recvlen; /* The received length */ + int ir_result; /* Success:OK, failure:negated errno */ +}; +#endif /* NET_UDP_HAVE_STACK || NET_TCP_HAVE_STACK */ + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: inet_update_recvlen + * + * Description: + * Update information about space available for new data and update size + * of data in buffer, This logic accounts for the case where + * inet_udp_readahead() sets state.ir_recvlen == -1 . + * + * Parameters: + * pstate recvfrom state structure + * recvlen size of new data appended to buffer + * + * Returned Value: + * None + * + ****************************************************************************/ + +#if defined(NET_UDP_HAVE_STACK) || defined(NET_TCP_HAVE_STACK) + +static inline void inet_update_recvlen(FAR struct inet_recvfrom_s *pstate, + size_t recvlen) +{ + if (pstate->ir_recvlen < 0) + { + pstate->ir_recvlen = 0; + } + + pstate->ir_recvlen += recvlen; + pstate->ir_buffer += recvlen; + pstate->ir_buflen -= recvlen; +} +#endif /* NET_UDP_HAVE_STACK || NET_TCP_HAVE_STACK */ + +/**************************************************************************** + * Name: inet_recvfrom_newdata + * + * Description: + * Copy the read data from the packet + * + * Parameters: + * dev The structure of the network driver that caused the interrupt + * pstate recvfrom state structure + * + * Returned Value: + * The number of bytes taken from the packet. + * + * Assumptions: + * The network is locked. + * + ****************************************************************************/ + +#if defined(NET_UDP_HAVE_STACK) || defined(NET_TCP_HAVE_STACK) +static size_t inet_recvfrom_newdata(FAR struct net_driver_s *dev, + FAR struct inet_recvfrom_s *pstate) +{ + size_t recvlen; + + /* Get the length of the data to return */ + + if (dev->d_len > pstate->ir_buflen) + { + recvlen = pstate->ir_buflen; + } + else + { + recvlen = dev->d_len; + } + + /* Copy the new appdata into the user buffer */ + + memcpy(pstate->ir_buffer, dev->d_appdata, recvlen); + ninfo("Received %d bytes (of %d)\n", (int)recvlen, (int)dev->d_len); + + /* Update the accumulated size of the data read */ + + inet_update_recvlen(pstate, recvlen); + + return recvlen; +} +#endif /* NET_UDP_HAVE_STACK || NET_TCP_HAVE_STACK */ + +/**************************************************************************** + * Name: inet_tcp_newdata + * + * Description: + * Copy the read data from the packet + * + * Parameters: + * dev The structure of the network driver that caused the interrupt + * pstate recvfrom state structure + * + * Returned Value: + * None. + * + * Assumptions: + * The network is locked. + * + ****************************************************************************/ + +#ifdef NET_TCP_HAVE_STACK +static inline void inet_tcp_newdata(FAR struct net_driver_s *dev, + FAR struct inet_recvfrom_s *pstate) +{ + /* Take as much data from the packet as we can */ + + size_t recvlen = inet_recvfrom_newdata(dev, pstate); + + /* If there is more data left in the packet that we could not buffer, then + * add it to the read-ahead buffers. + */ + + if (recvlen < dev->d_len) + { +#ifdef CONFIG_NET_TCP_READAHEAD + FAR struct tcp_conn_s *conn = (FAR struct tcp_conn_s *)pstate->ir_sock->s_conn; + FAR uint8_t *buffer = (FAR uint8_t *)dev->d_appdata + recvlen; + uint16_t buflen = dev->d_len - recvlen; +#ifdef CONFIG_DEBUG_NET + uint16_t nsaved; + + nsaved = tcp_datahandler(conn, buffer, buflen); +#else + (void)tcp_datahandler(conn, buffer, buflen); +#endif + + /* There are complicated buffering issues that are not addressed fully + * here. For example, what if up_datahandler() cannot buffer the + * remainder of the packet? In that case, the data will be dropped but + * still ACKed. Therefore it would not be resent. + * + * This is probably not an issue here because we only get here if the + * read-ahead buffers are empty and there would have to be something + * serioulsy wrong with the configuration not to be able to buffer a + * partial packet in this context. + */ + +#ifdef CONFIG_DEBUG_NET + if (nsaved < buflen) + { + nerr("ERROR: packet data not saved (%d bytes)\n", buflen - nsaved); + } +#endif +#else + nerr("ERROR: packet data lost (%d bytes)\n", dev->d_len - recvlen); +#endif + } + + /* Indicate no data in the buffer */ + + dev->d_len = 0; +} +#endif /* NET_TCP_HAVE_STACK */ + +/**************************************************************************** + * Name: inet_udp_newdata + * + * Description: + * Copy the read data from the packet + * + * Parameters: + * dev The sructure of the network driver that caused the interrupt + * pstate recvfrom state structure + * + * Returned Value: + * None. + * + * Assumptions: + * The network is locked. + * + ****************************************************************************/ + +#ifdef NET_UDP_HAVE_STACK +static inline void inet_udp_newdata(FAR struct net_driver_s *dev, + FAR struct inet_recvfrom_s *pstate) +{ + /* Take as much data from the packet as we can */ + + (void)inet_recvfrom_newdata(dev, pstate); + + /* Indicate no data in the buffer */ + + dev->d_len = 0; +} +#endif /* NET_UDP_HAVE_STACK */ + +/**************************************************************************** + * Name: inet_tcp_readahead + * + * Description: + * Copy the read data from the packet + * + * Parameters: + * dev The structure of the network driver that caused the interrupt + * pstate recvfrom state structure + * + * Returned Value: + * None + * + * Assumptions: + * The network is locked. + * + ****************************************************************************/ + +#if defined(NET_TCP_HAVE_STACK) && defined(CONFIG_NET_TCP_READAHEAD) +static inline void inet_tcp_readahead(struct inet_recvfrom_s *pstate) +{ + FAR struct tcp_conn_s *conn = (FAR struct tcp_conn_s *)pstate->ir_sock->s_conn; + FAR struct iob_s *iob; + int recvlen; + + /* Check there is any TCP data already buffered in a read-ahead + * buffer. + */ + + while ((iob = iob_peek_queue(&conn->readahead)) != NULL && + pstate->ir_buflen > 0) + { + DEBUGASSERT(iob->io_pktlen > 0); + + /* Transfer that buffered data from the I/O buffer chain into + * the user buffer. + */ + + recvlen = iob_copyout(pstate->ir_buffer, iob, pstate->ir_buflen, 0); + ninfo("Received %d bytes (of %d)\n", recvlen, iob->io_pktlen); + + /* Update the accumulated size of the data read */ + + inet_update_recvlen(pstate, recvlen); + + /* If we took all of the ata from the I/O buffer chain is empty, then + * release it. If there is still data available in the I/O buffer + * chain, then just trim the data that we have taken from the + * beginning of the I/O buffer chain. + */ + + if (recvlen >= iob->io_pktlen) + { + FAR struct iob_s *tmp; + + /* Remove the I/O buffer chain from the head of the read-ahead + * buffer queue. + */ + + tmp = iob_remove_queue(&conn->readahead); + DEBUGASSERT(tmp == iob); + UNUSED(tmp); + + /* And free the I/O buffer chain */ + + (void)iob_free_chain(iob); + } + else + { + /* The bytes that we have received from the head of the I/O + * buffer chain (probably changing the head of the I/O + * buffer queue). + */ + + (void)iob_trimhead_queue(&conn->readahead, recvlen); + } + } +} +#endif /* NET_TCP_HAVE_STACK && CONFIG_NET_TCP_READAHEAD */ + +#if defined(NET_UDP_HAVE_STACK) && defined(CONFIG_NET_UDP_READAHEAD) + +static inline void inet_udp_readahead(struct inet_recvfrom_s *pstate) +{ + FAR struct udp_conn_s *conn = (FAR struct udp_conn_s *)pstate->ir_sock->s_conn; + FAR struct iob_s *iob; + int recvlen; + + /* Check there is any UDP datagram already buffered in a read-ahead + * buffer. + */ + + pstate->ir_recvlen = -1; + + if ((iob = iob_peek_queue(&conn->readahead)) != NULL) + { + FAR struct iob_s *tmp; + uint8_t src_addr_size; + + DEBUGASSERT(iob->io_pktlen > 0); + + /* Transfer that buffered data from the I/O buffer chain into + * the user buffer. + */ + + recvlen = iob_copyout(&src_addr_size, iob, sizeof(uint8_t), 0); + if (recvlen != sizeof(uint8_t)) + { + goto out; + } + + if (0 +#ifdef CONFIG_NET_IPv6 + || src_addr_size == sizeof(struct sockaddr_in6) +#endif +#ifdef CONFIG_NET_IPv4 + || src_addr_size == sizeof(struct sockaddr_in) +#endif + ) + { + if (pstate->ir_from) + { + socklen_t len = *pstate->ir_fromlen; + len = (socklen_t)src_addr_size > len ? len : (socklen_t)src_addr_size; + + recvlen = iob_copyout((FAR uint8_t *)pstate->ir_from, iob, + len, sizeof(uint8_t)); + if (recvlen != len) + { + goto out; + } + } + } + + if (pstate->ir_buflen > 0) + { + recvlen = iob_copyout(pstate->ir_buffer, iob, pstate->ir_buflen, + src_addr_size + sizeof(uint8_t)); + + ninfo("Received %d bytes (of %d)\n", recvlen, iob->io_pktlen); + + /* Update the accumulated size of the data read */ + + pstate->ir_recvlen = recvlen; + pstate->ir_buffer += recvlen; + pstate->ir_buflen -= recvlen; + } + else + { + pstate->ir_recvlen = 0; + } + +out: + /* Remove the I/O buffer chain from the head of the read-ahead + * buffer queue. + */ + + tmp = iob_remove_queue(&conn->readahead); + DEBUGASSERT(tmp == iob); + UNUSED(tmp); + + /* And free the I/O buffer chain */ + + (void)iob_free_chain(iob); + } +} +#endif + +/**************************************************************************** + * Name: inet_recvfrom_timeout + * + * Description: + * Check for recvfrom timeout. + * + * Parameters: + * pstate recvfrom state structure + * + * Returned Value: + * TRUE:timeout FALSE:no timeout + * + * Assumptions: + * The network is locked. + * + ****************************************************************************/ + +#if defined(NET_UDP_HAVE_STACK) || defined(NET_TCP_HAVE_STACK) +#ifdef CONFIG_NET_SOCKOPTS +static int inet_recvfrom_timeout(struct inet_recvfrom_s *pstate) +{ + FAR struct socket *psock = 0; + socktimeo_t timeo = 0; + + /* Check for a timeout configured via setsockopts(SO_RCVTIMEO). If none... + * we well let the read hang forever (except for the special case below). + */ + + /* Get the socket reference from the private data */ + + psock = pstate->ir_sock; + if (psock) + { + /* Recover the timeout value (zero if no timeout) */ + + timeo = psock->s_rcvtimeo; + } + + /* Use a fixed, configurable delay under the following circumstances: + * + * 1) This delay function has been enabled with CONFIG_NET_TCP_RECVDELAY > 0 + * 2) Some data has already been received from the socket. Since this can + * only be true for a TCP/IP socket, this logic applies only to TCP/IP + * sockets. And either + * 3) There is no configured receive timeout, or + * 4) The configured receive timeout is greater than than the delay + */ + +#if CONFIG_NET_TCP_RECVDELAY > 0 + if ((timeo == 0 || timeo > CONFIG_NET_TCP_RECVDELAY) && + pstate->ir_recvlen > 0) + { + /* Use the configured timeout */ + + timeo = CONFIG_NET_TCP_RECVDELAY; + } +#endif + + /* Is there an effective timeout? */ + + if (timeo) + { + /* Yes.. Check if the timeout has elapsed */ + + return net_timeo(pstate->ir_starttime, timeo); + } + + /* No timeout -- hang forever waiting for data. */ + + return FALSE; +} +#endif /* CONFIG_NET_SOCKOPTS */ +#endif /* NET_UDP_HAVE_STACK || NET_TCP_HAVE_STACK */ + +/**************************************************************************** + * Name: inet_tcp_sender + * + * Description: + * Getting the sender's address from the UDP packet + * + * Parameters: + * dev - The device driver data structure + * pstate - the recvfrom state structure + * + * Returned Value: + * None + * + * Assumptions: + * Running at the interrupt level + * + ****************************************************************************/ + +#ifdef NET_TCP_HAVE_STACK +static inline void inet_tcp_sender(FAR struct net_driver_s *dev, + FAR struct inet_recvfrom_s *pstate) +{ + /* Get the family from the packet type, IP address from the IP header, and + * the port number from the TCP header. + */ + +#ifdef CONFIG_NET_IPv6 +#ifdef CONFIG_NET_IPv4 + if (IFF_IS_IPv6(dev->d_flags)) +#endif + { + FAR struct sockaddr_in6 *infrom = + (FAR struct sockaddr_in6 *)pstate->ir_from; + + if (infrom) + { + FAR struct tcp_hdr_s *tcp = TCPIPv6BUF; + FAR struct ipv6_hdr_s *ipv6 = IPv6BUF; + + infrom->sin6_family = AF_INET6; + infrom->sin6_port = tcp->srcport; + + net_ipv6addr_copy(infrom->sin6_addr.s6_addr, ipv6->srcipaddr); + } + } +#endif /* CONFIG_NET_IPv6 */ + +#ifdef CONFIG_NET_IPv4 +#ifdef CONFIG_NET_IPv6 + else +#endif + { + FAR struct sockaddr_in *infrom = + (FAR struct sockaddr_in *)pstate->ir_from; + + if (infrom) + { + FAR struct tcp_hdr_s *tcp = TCPIPv4BUF; + FAR struct ipv4_hdr_s *ipv4 = IPv4BUF; + + infrom->sin_family = AF_INET; + infrom->sin_port = tcp->srcport; + + net_ipv4addr_copy(infrom->sin_addr.s_addr, + net_ip4addr_conv32(ipv4->srcipaddr)); + } + } +#endif /* CONFIG_NET_IPv4 */ +} +#endif /* NET_TCP_HAVE_STACK */ + +/**************************************************************************** + * Name: inet_tcp_interrupt + * + * Description: + * This function is called from the interrupt level to perform the actual + * TCP receive operation via by the lower, device interfacing layer. + * + * Parameters: + * dev The structure of the network driver that caused the interrupt + * pvconn The connection structure associated with the socket + * flags Set of events describing why the callback was invoked + * + * Returned Value: + * None + * + * Assumptions: + * The network is locked. + * + ****************************************************************************/ + +#ifdef NET_TCP_HAVE_STACK +static uint16_t inet_tcp_interrupt(FAR struct net_driver_s *dev, + FAR void *pvconn, FAR void *pvpriv, + uint16_t flags) +{ + FAR struct inet_recvfrom_s *pstate = (struct inet_recvfrom_s *)pvpriv; + +#if 0 /* REVISIT: The assertion fires. Why? */ +#ifdef CONFIG_NETDEV_MULTINIC + FAR struct tcp_conn_s *conn = (FAR struct tcp_conn_s *)pvconn; + + /* The TCP socket is connected and, hence, should be bound to a device. + * Make sure that the polling device is the own that we are bound to. + */ + + DEBUGASSERT(conn->dev == NULL || conn->dev == dev); + if (conn->dev != NULL && conn->dev != dev) + { + return flags; + } +#endif +#endif + + ninfo("flags: %04x\n", flags); + + /* 'priv' might be null in some race conditions (?) */ + + if (pstate) + { + /* If new data is available, then complete the read action. */ + + if ((flags & TCP_NEWDATA) != 0) + { + /* Copy the data from the packet (saving any unused bytes from the + * packet in the read-ahead buffer). + */ + + inet_tcp_newdata(dev, pstate); + + /* Save the sender's address in the caller's 'from' location */ + + inet_tcp_sender(dev, pstate); + + /* Indicate that the data has been consumed and that an ACK + * should be sent. + */ + + flags = (flags & ~TCP_NEWDATA) | TCP_SNDACK; + + /* Check for transfer complete. We will consider the transfer + * complete in own of two different ways, depending on the setting + * of CONFIG_NET_TCP_RECVDELAY. + * + * 1) If CONFIG_NET_TCP_RECVDELAY == 0 then we will consider the + * TCP/IP transfer complete as soon as any data has been received. + * This is safe because if any additional data is received, it + * will be retained in the TCP/IP read-ahead buffer until the + * next receive is performed. + * 2) CONFIG_NET_TCP_RECVDELAY > 0 may be set to wait a little + * bit to determine if more data will be received. You might + * do this if read-ahead buffering is disabled and we want to + * minimize the loss of back-to-back packets. In this case, + * the transfer is complete when either a) the entire user buffer + * is full or 2) when the receive timeout occurs (below). + */ + +#if CONFIG_NET_TCP_RECVDELAY > 0 + if (pstate->ir_buflen == 0) +#else + if (pstate->ir_recvlen > 0) +#endif + { + ninfo("TCP resume\n"); + + /* The TCP receive buffer is non-empty. Return now and don't + * allow any further TCP call backs. + */ + + pstate->ir_cb->flags = 0; + pstate->ir_cb->priv = NULL; + pstate->ir_cb->event = NULL; + + /* Wake up the waiting thread, returning the number of bytes + * actually read. + */ + + sem_post(&pstate->ir_sem); + } + +#ifdef CONFIG_NET_SOCKOPTS + /* Reset the timeout. We will want a short timeout to terminate + * the TCP receive. + */ + + pstate->ir_starttime = clock_systimer(); +#endif + } + + /* Check for a loss of connection. + * + * TCP_DISCONN_EVENTS: + * TCP_CLOSE: The remote host has closed the connection + * TCP_ABORT: The remote host has aborted the connection + * TCP_TIMEDOUT: Connection aborted due to too many retransmissions. + * NETDEV_DOWN: The network device went down + */ + + else if ((flags & TCP_DISCONN_EVENTS) != 0) + { + ninfo("Lost connection\n"); + + /* Stop further callbacks */ + + pstate->ir_cb->flags = 0; + pstate->ir_cb->priv = NULL; + pstate->ir_cb->event = NULL; + + /* Handle loss-of-connection event */ + + net_lostconnection(pstate->ir_sock, flags); + + /* Check if the peer gracefully closed the connection. */ + + if ((flags & TCP_CLOSE) != 0) + { + /* This case should always return success (zero)! The value of + * ir_recvlen, if zero, will indicate that the connection was + * gracefully closed. + */ + + pstate->ir_result = 0; + } + else + { + /* If no data has been received, then return ENOTCONN. + * Otherwise, let this return success. The failure will + * be reported the next time that recv[from]() is called. + */ + +#if CONFIG_NET_TCP_RECVDELAY > 0 + if (pstate->ir_recvlen > 0) + { + pstate->ir_result = 0; + } + else + { + pstate->ir_result = -ENOTCONN; + } +#else + pstate->ir_result = -ENOTCONN; +#endif + } + + /* Wake up the waiting thread */ + + sem_post(&pstate->ir_sem); + } + +#ifdef CONFIG_NET_SOCKOPTS + /* No data has been received -- this is some other event... probably a + * poll -- check for a timeout. + */ + + else if (inet_recvfrom_timeout(pstate)) + { + /* Yes.. the timeout has elapsed... do not allow any further + * callbacks + */ + + ninfo("TCP timeout\n"); + + pstate->ir_cb->flags = 0; + pstate->ir_cb->priv = NULL; + pstate->ir_cb->event = NULL; + + /* Report an error only if no data has been received. (If + * CONFIG_NET_TCP_RECVDELAY then ir_recvlen should always be + * less than or equal to zero). + */ + +#if CONFIG_NET_TCP_RECVDELAY > 0 + if (pstate->ir_recvlen <= 0) +#endif + { + /* Report the timeout error */ + + pstate->ir_result = -EAGAIN; + } + + /* Wake up the waiting thread, returning either the error -EAGAIN + * that signals the timeout event or the data received up to + * the point that the timeout occurred (no error). + */ + + sem_post(&pstate->ir_sem); + } +#endif /* CONFIG_NET_SOCKOPTS */ + } + + return flags; +} +#endif /* NET_TCP_HAVE_STACK */ + +/**************************************************************************** + * Name: inet_udp_sender + * + * Description: + * Getting the sender's address from the UDP packet + * + * Parameters: + * dev - The device driver data structure + * pstate - the recvfrom state structure + * + * Returned Value: + * None + * + * Assumptions: + * The network is locked. + * + ****************************************************************************/ + +#ifdef NET_UDP_HAVE_STACK +static inline void inet_udp_sender(struct net_driver_s *dev, struct inet_recvfrom_s *pstate) +{ + /* Get the family from the packet type, IP address from the IP header, and + * the port number from the UDP header. + */ + +#ifdef CONFIG_NET_IPv6 +#ifdef CONFIG_NET_IPv4 + if (IFF_IS_IPv6(dev->d_flags)) +#endif + { + FAR struct sockaddr_in6 *infrom = + (FAR struct sockaddr_in6 *)pstate->ir_from; + FAR socklen_t *fromlen = pstate->ir_fromlen; + + if (infrom) + { + FAR struct udp_hdr_s *udp = UDPIPv6BUF; + FAR struct ipv6_hdr_s *ipv6 = IPv6BUF; + + infrom->sin6_family = AF_INET6; + infrom->sin6_port = udp->srcport; + *fromlen = sizeof(struct sockaddr_in6); + + net_ipv6addr_copy(infrom->sin6_addr.s6_addr, ipv6->srcipaddr); + } + } +#endif /* CONFIG_NET_IPv6 */ + +#ifdef CONFIG_NET_IPv4 +#ifdef CONFIG_NET_IPv6 + else +#endif + { + FAR struct sockaddr_in *infrom = + (FAR struct sockaddr_in *)pstate->ir_from; + + if (infrom) + { +#ifdef CONFIG_NET_IPv6 + FAR struct udp_conn_s *conn = + (FAR struct udp_conn_s *)pstate->ir_sock->s_conn; + + /* Hybrid dual-stack IPv6/IPv4 implementations recognize a special + * class of addresses, the IPv4-mapped IPv6 addresses. + */ + + if (conn->domain == PF_INET6) + { + FAR struct sockaddr_in6 *infrom6 = (FAR struct sockaddr_in6 *)infrom; + FAR socklen_t *fromlen = pstate->ir_fromlen; + FAR struct udp_hdr_s *udp = UDPIPv6BUF; + FAR struct ipv6_hdr_s *ipv6 = IPv6BUF; + in_addr_t ipv4addr; + + /* Encode the IPv4 address as an IPv4-mapped IPv6 address */ + + infrom6->sin6_family = AF_INET6; + infrom6->sin6_port = udp->srcport; + *fromlen = sizeof(struct sockaddr_in6); + + ipv4addr = net_ip4addr_conv32(ipv6->srcipaddr); + ip6_map_ipv4addr(ipv4addr, infrom6->sin6_addr.s6_addr16); + } + else +#endif + { + FAR struct udp_hdr_s *udp = UDPIPv4BUF; + FAR struct ipv4_hdr_s *ipv4 = IPv4BUF; + + infrom->sin_family = AF_INET; + infrom->sin_port = udp->srcport; + + net_ipv4addr_copy(infrom->sin_addr.s_addr, + net_ip4addr_conv32(ipv4->srcipaddr)); + } + } + } +#endif /* CONFIG_NET_IPv4 */ +} +#endif /* NET_UDP_HAVE_STACK */ + +/**************************************************************************** + * Name: inet_udp_terminate + * + * Description: + * Terminate the UDP transfer. + * + * Parameters: + * pstate - The recvfrom state structure + * result - The result of the operation + * + * Returned Value: + * None + * + ****************************************************************************/ + +#ifdef NET_UDP_HAVE_STACK +static void inet_udp_terminate(FAR struct inet_recvfrom_s *pstate, int result) +{ + /* Don't allow any further UDP call backs. */ + + pstate->ir_cb->flags = 0; + pstate->ir_cb->priv = NULL; + pstate->ir_cb->event = NULL; + + /* Save the result of the transfer */ + + pstate->ir_result = result; + + /* Wake up the waiting thread, returning the number of bytes + * actually read. + */ + + sem_post(&pstate->ir_sem); +} +#endif /* NET_UDP_HAVE_STACK */ + +/**************************************************************************** + * Name: inet_udp_interrupt + * + * Description: + * This function is called from the interrupt level to perform the actual + * UDP receive operation via by the lower, device interfacing layer. + * + * Parameters: + * dev The structure of the network driver that caused the interrupt + * pvconn The connection structure associated with the socket + * flags Set of events describing why the callback was invoked + * + * Returned Value: + * None + * + * Assumptions: + * The network is locked. + * + ****************************************************************************/ + +#ifdef NET_UDP_HAVE_STACK +static uint16_t inet_udp_interrupt(FAR struct net_driver_s *dev, + FAR void *pvconn, FAR void *pvpriv, + uint16_t flags) +{ + FAR struct inet_recvfrom_s *pstate = (FAR struct inet_recvfrom_s *)pvpriv; + + ninfo("flags: %04x\n", flags); + + /* 'priv' might be null in some race conditions (?) */ + + if (pstate) + { + /* If the network device has gone down, then we will have terminate + * the wait now with an error. + */ + + if ((flags & NETDEV_DOWN) != 0) + { + /* Terminate the transfer with an error. */ + + nerr("ERROR: Network is down\n"); + inet_udp_terminate(pstate, -ENETUNREACH); + } + + /* If new data is available, then complete the read action. */ + + else if ((flags & UDP_NEWDATA) != 0) + { + /* Copy the data from the packet */ + + inet_udp_newdata(dev, pstate); + + /* We are finished. */ + + ninfo("UDP done\n"); + + /* Save the sender's address in the caller's 'from' location */ + + inet_udp_sender(dev, pstate); + + /* Don't allow any further UDP call backs. */ + + inet_udp_terminate(pstate, OK); + + /* Indicate that the data has been consumed */ + + flags &= ~UDP_NEWDATA; + } + +#ifdef CONFIG_NET_SOCKOPTS + /* No data has been received -- this is some other event... probably a + * poll -- check for a timeout. + */ + + else if (inet_recvfrom_timeout(pstate)) + { + /* Yes.. the timeout has elapsed... do not allow any further + * callbacks + */ + + nerr("ERROR: UDP timeout\n"); + + /* Terminate the transfer with an -EAGAIN error */ + + inet_udp_terminate(pstate, -EAGAIN); + } +#endif /* CONFIG_NET_SOCKOPTS */ + } + + return flags; +} +#endif /* NET_UDP_HAVE_STACK */ + +/**************************************************************************** + * Name: inet_recvfrom_initialize + * + * Description: + * Initialize the state structure + * + * Parameters: + * psock Pointer to the socket structure for the socket + * buf Buffer to receive data + * len Length of buffer + * pstate A pointer to the state structure to be initialized + * + * Returned Value: + * None + * + * Assumptions: + * + ****************************************************************************/ + +#if defined(NET_UDP_HAVE_STACK) || defined(NET_TCP_HAVE_STACK) +static void inet_recvfrom_initialize(FAR struct socket *psock, FAR void *buf, + size_t len, FAR struct sockaddr *infrom, + FAR socklen_t *fromlen, + FAR struct inet_recvfrom_s *pstate) +{ + /* Initialize the state structure. */ + + memset(pstate, 0, sizeof(struct inet_recvfrom_s)); + + /* This semaphore is used for signaling and, hence, should not have + * priority inheritance enabled. + */ + + (void)sem_init(&pstate->ir_sem, 0, 0); /* Doesn't really fail */ + (void)sem_setprotocol(&pstate->ir_sem, SEM_PRIO_NONE); + + pstate->ir_buflen = len; + pstate->ir_buffer = buf; + pstate->ir_from = infrom; + pstate->ir_fromlen = fromlen; + + /* Set up the start time for the timeout */ + + pstate->ir_sock = psock; +#ifdef CONFIG_NET_SOCKOPTS + pstate->ir_starttime = clock_systimer(); +#endif +} + +/* The only un-initialization that has to be performed is destroying the + * semaphore. + */ + +#define inet_recvfrom_uninitialize(s) sem_destroy(&(s)->ir_sem) + +#endif /* NET_UDP_HAVE_STACK || NET_TCP_HAVE_STACK */ + +/**************************************************************************** + * Name: inet_recvfrom_result + * + * Description: + * Evaluate the result of the recv operations + * + * Parameters: + * result The result of the net_lockedwait operation (may indicate EINTR) + * pstate A pointer to the state structure to be initialized + * + * Returned Value: + * The result of the recv operation with errno set appropriately + * + * Assumptions: + * + ****************************************************************************/ + +#if defined(NET_UDP_HAVE_STACK) || defined(NET_TCP_HAVE_STACK) +static ssize_t inet_recvfrom_result(int result, struct inet_recvfrom_s *pstate) +{ + int save_errno = get_errno(); /* In case something we do changes it */ + + /* Check for a error/timeout detected by the interrupt handler. Errors are + * signaled by negative errno values for the rcv length + */ + + if (pstate->ir_result < 0) + { + /* This might return EAGAIN on a timeout or ENOTCONN on loss of + * connection (TCP only) + */ + + return pstate->ir_result; + } + + /* If net_lockedwait failed, then we were probably reawakened by a signal. In + * this case, net_lockedwait will have set errno appropriately. + */ + + if (result < 0) + { + return -save_errno; + } + + return pstate->ir_recvlen; +} +#endif /* NET_UDP_HAVE_STACK || NET_TCP_HAVE_STACK */ + +/**************************************************************************** + * Name: inet_udp_rxnotify + * + * Description: + * Notify the appropriate device driver that we are ready to receive a + * packet (UDP) + * + * Parameters: + * psock - Socket state structure + * conn - The UDP connection structure + * + * Returned Value: + * None + * + ****************************************************************************/ + +#ifdef NET_UDP_HAVE_STACK +static inline void inet_udp_rxnotify(FAR struct socket *psock, + FAR struct udp_conn_s *conn) +{ +#ifdef CONFIG_NET_IPv4 +#ifdef CONFIG_NET_IPv6 + /* If both IPv4 and IPv6 support are enabled, then we will need to select + * the device driver using the appropriate IP domain. + */ + + if (psock->s_domain == PF_INET) +#endif + { + /* Notify the device driver of the receive ready */ + +#ifdef CONFIG_NETDEV_MULTINIC + netdev_ipv4_rxnotify(conn->u.ipv4.laddr, conn->u.ipv4.raddr); +#else + netdev_ipv4_rxnotify(conn->u.ipv4.raddr); +#endif + } +#endif /* CONFIG_NET_IPv4 */ + +#ifdef CONFIG_NET_IPv6 +#ifdef CONFIG_NET_IPv4 + else /* if (psock->s_domain == PF_INET6) */ +#endif /* CONFIG_NET_IPv4 */ + { + /* Notify the device driver of the receive ready */ + + DEBUGASSERT(psock->s_domain == PF_INET6); +#ifdef CONFIG_NETDEV_MULTINIC + netdev_ipv6_rxnotify(conn->u.ipv6.laddr, conn->u.ipv6.raddr); +#else + netdev_ipv6_rxnotify(conn->u.ipv6.raddr); +#endif + } +#endif /* CONFIG_NET_IPv6 */ +} +#endif /* NET_UDP_HAVE_STACK */ + +/**************************************************************************** + * Name: inet_udp_recvfrom + * + * Description: + * Perform the recvfrom operation for a UDP SOCK_DGRAM + * + * Parameters: + * psock Pointer to the socket structure for the SOCK_DRAM socket + * buf Buffer to receive data + * len Length of buffer + * from INET address of source (may be NULL) + * + * Returned Value: + * On success, returns the number of characters received. On error, + * -errno is returned (see recvfrom for list of errnos). + * + * Assumptions: + * + ****************************************************************************/ + +#ifdef NET_UDP_HAVE_STACK +static ssize_t inet_udp_recvfrom(FAR struct socket *psock, FAR void *buf, size_t len, + FAR struct sockaddr *from, FAR socklen_t *fromlen) +{ + FAR struct udp_conn_s *conn = (FAR struct udp_conn_s *)psock->s_conn; + FAR struct net_driver_s *dev; + struct inet_recvfrom_s state; + int ret; + + /* Perform the UDP recvfrom() operation */ + + /* Initialize the state structure. This is done with interrupts + * disabled because we don't want anything to happen until we + * are ready. + */ + + net_lock(); + inet_recvfrom_initialize(psock, buf, len, from, fromlen, &state); + + /* Setup the UDP remote connection */ + + ret = udp_connect(conn, NULL); + if (ret < 0) + { + goto errout_with_state; + } + +#ifdef CONFIG_NET_UDP_READAHEAD + inet_udp_readahead(&state); + + /* The default return value is the number of bytes that we just copied + * into the user buffer. We will return this if the socket has become + * disconnected or if the user request was completely satisfied with + * data from the readahead buffers. + */ + + ret = state.ir_recvlen; + +#else + /* Otherwise, the default return value of zero is used (only for the case + * where len == state.ir_buflen is zero). + */ + + ret = 0; +#endif + +#ifdef CONFIG_NET_UDP_READAHEAD + if (_SS_ISNONBLOCK(psock->s_flags)) + { + /* Return the number of bytes read from the read-ahead buffer if + * something was received (already in 'ret'); EAGAIN if not. + */ + + if (ret < 0) + { + /* Nothing was received */ + + ret = -EAGAIN; + } + } + + /* It is okay to block if we need to. If there is space to receive anything + * more, then we will wait to receive the data. Otherwise return the number + * of bytes read from the read-ahead buffer (already in 'ret'). + * + * NOTE: that inet_udp_readahead() may set state.ir_recvlen == -1. + */ + + else if (state.ir_recvlen <= 0) +#endif + { + /* Get the device that will handle the packet transfers. This may be + * NULL if the UDP socket is bound to INADDR_ANY. In that case, no + * NETDEV_DOWN notifications will be received. + */ + + dev = udp_find_laddr_device(conn); + + /* Set up the callback in the connection */ + + state.ir_cb = udp_callback_alloc(dev, conn); + if (state.ir_cb) + { + /* Set up the callback in the connection */ + + state.ir_cb->flags = (UDP_NEWDATA | UDP_POLL | NETDEV_DOWN); + state.ir_cb->priv = (FAR void *)&state; + state.ir_cb->event = inet_udp_interrupt; + + /* Notify the device driver of the receive call */ + + inet_udp_rxnotify(psock, conn); + + /* Wait for either the receive to complete or for an error/timeout + * to occur. NOTES: (1) net_lockedwait will also terminate if a + * signal is received, (2) interrupts are disabled! They will be + * re-enabled while the task sleeps and automatically re-enabled + * when the task restarts. + */ + + ret = net_lockedwait(&state. ir_sem); + + /* Make sure that no further interrupts are processed */ + + udp_callback_free(dev, conn, state.ir_cb); + ret = inet_recvfrom_result(ret, &state); + } + else + { + ret = -EBUSY; + } + } + +errout_with_state: + net_unlock(); + inet_recvfrom_uninitialize(&state); + return ret; +} +#endif /* NET_UDP_HAVE_STACK */ + +/**************************************************************************** + * Name: inet_tcp_recvfrom + * + * Description: + * Perform the recvfrom operation for a TCP/IP SOCK_STREAM + * + * Parameters: + * psock Pointer to the socket structure for the SOCK_DRAM socket + * buf Buffer to receive data + * len Length of buffer + * from INET address of source (may be NULL) + * + * Returned Value: + * On success, returns the number of characters received. On error, + * -errno is returned (see recvfrom for list of errnos). + * + * Assumptions: + * + ****************************************************************************/ + +#ifdef NET_TCP_HAVE_STACK +static ssize_t inet_tcp_recvfrom(FAR struct socket *psock, FAR void *buf, size_t len, + FAR struct sockaddr *from, FAR socklen_t *fromlen) +{ + struct inet_recvfrom_s state; + int ret; + + /* Initialize the state structure. This is done with interrupts + * disabled because we don't want anything to happen until we + * are ready. + */ + + net_lock(); + inet_recvfrom_initialize(psock, buf, len, from, fromlen, &state); + + /* Handle any any TCP data already buffered in a read-ahead buffer. NOTE + * that there may be read-ahead data to be retrieved even after the + * socket has been disconnected. + */ + +#ifdef CONFIG_NET_TCP_READAHEAD + inet_tcp_readahead(&state); + + /* The default return value is the number of bytes that we just copied + * into the user buffer. We will return this if the socket has become + * disconnected or if the user request was completely satisfied with + * data from the readahead buffers. + */ + + ret = state.ir_recvlen; + +#else + /* Otherwise, the default return value of zero is used (only for the case + * where len == state.ir_buflen is zero). + */ + + ret = 0; +#endif + + /* Verify that the SOCK_STREAM has been and still is connected */ + + if (!_SS_ISCONNECTED(psock->s_flags)) + { + /* Was any data transferred from the readahead buffer after we were + * disconnected? If so, then return the number of bytes received. We + * will wait to return end disconnection indications the next time that + * recvfrom() is called. + * + * If no data was received (i.e., ret == 0 -- it will not be negative) + * and the connection was gracefully closed by the remote peer, then return + * success. If ir_recvlen is zero, the caller of recvfrom() will get an + * end-of-file indication. + */ + +#ifdef CONFIG_NET_TCP_READAHEAD + if (ret <= 0 && !_SS_ISCLOSED(psock->s_flags)) +#else + if (!_SS_ISCLOSED(psock->s_flags)) +#endif + { + /* Nothing was previously received from the readahead buffers. + * The SOCK_STREAM must be (re-)connected in order to receive any + * additional data. + */ + + ret = -ENOTCONN; + } + } + + /* In general, this implementation will not support non-blocking socket + * operations... except in a few cases: Here for TCP receive with read-ahead + * enabled. If this socket is configured as non-blocking then return EAGAIN + * if no data was obtained from the read-ahead buffers. + */ + + else +#ifdef CONFIG_NET_TCP_READAHEAD + if (_SS_ISNONBLOCK(psock->s_flags)) + { + /* Return the number of bytes read from the read-ahead buffer if + * something was received (already in 'ret'); EAGAIN if not. + */ + + if (ret <= 0) + { + /* Nothing was received */ + + ret = -EAGAIN; + } + } + + /* It is okay to block if we need to. If there is space to receive anything + * more, then we will wait to receive the data. Otherwise return the number + * of bytes read from the read-ahead buffer (already in 'ret'). + */ + + else +#endif + + /* We get here when we we decide that we need to setup the wait for incoming + * TCP/IP data. Just a few more conditions to check: + * + * 1) Make sure thet there is buffer space to receive additional data + * (state.ir_buflen > 0). This could be zero, for example, if read-ahead + * buffering was enabled and we filled the user buffer with data from + * the read-ahead buffers. And + * 2) if read-ahead buffering is enabled (CONFIG_NET_TCP_READAHEAD) + * and delay logic is disabled (CONFIG_NET_TCP_RECVDELAY == 0), then we + * not want to wait if we already obtained some data from the read-ahead + * buffer. In that case, return now with what we have (don't want for more + * because there may be no timeout). + */ + +#if CONFIG_NET_TCP_RECVDELAY == 0 && defined(CONFIG_NET_TCP_READAHEAD) + if (state.ir_recvlen == 0 && state.ir_buflen > 0) +#else + if (state.ir_buflen > 0) +#endif + { + FAR struct tcp_conn_s *conn = (FAR struct tcp_conn_s *)psock->s_conn; + + /* Set up the callback in the connection */ + + state.ir_cb = tcp_callback_alloc(conn); + if (state.ir_cb) + { + state.ir_cb->flags = (TCP_NEWDATA | TCP_POLL | TCP_DISCONN_EVENTS); + state.ir_cb->priv = (FAR void *)&state; + state.ir_cb->event = inet_tcp_interrupt; + + /* Wait for either the receive to complete or for an error/timeout + * to occur. + * + * NOTES: (1) net_lockedwait will also terminate if a signal is + * received, (2) interrupts may be disabled! They will be re- + * enabled while the task sleeps and automatically re-enabled when + * the task restarts. + */ + + ret = net_lockedwait(&state.ir_sem); + + /* Make sure that no further interrupts are processed */ + + tcp_callback_free(conn, state.ir_cb); + ret = inet_recvfrom_result(ret, &state); + } + else + { + ret = -EBUSY; + } + } + + net_unlock(); + inet_recvfrom_uninitialize(&state); + return (ssize_t)ret; +} +#endif /* NET_TCP_HAVE_STACK */ + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: inet_recvfrom + * + * Description: + * Implements the socket recvfrom interface for the case of the AF_INET + * and AF_INET6 address families. inet_recvfrom() receives messages from + * a socket, and may be used to receive data on a socket whether or not it + * is connection-oriented. + * + * If 'from' is not NULL, and the underlying protocol provides the source + * address, this source address is filled in. The argument 'fromlen' is + * initialized to the size of the buffer associated with from, and + * modified on return to indicate the actual size of the address stored + * there. + * + * Parameters: + * psock A pointer to a NuttX-specific, internal socket structure + * buf Buffer to receive data + * len Length of buffer + * flags Receive flags + * from Address of source (may be NULL) + * fromlen The length of the address structure + * + * Returned Value: + * On success, returns the number of characters received. If no data is + * available to be received and the peer has performed an orderly shutdown, + * recv() will return 0. Otherwise, on errors, a negated errno value is + * returned (see recvfrom() for the list of appropriate error values). + * + ****************************************************************************/ + +ssize_t inet_recvfrom(FAR struct socket *psock, FAR void *buf, size_t len, + int flags, FAR struct sockaddr *from, + FAR socklen_t *fromlen) +{ + ssize_t ret; + + /* If a 'from' address has been provided, verify that it is large + * enough to hold this address family. + */ + + if (from) + { + socklen_t minlen; + + /* Get the minimum socket length */ + + switch (psock->s_domain) + { +#ifdef CONFIG_NET_IPv4 + case PF_INET: + { + minlen = sizeof(struct sockaddr_in); + } + break; +#endif + +#ifdef CONFIG_NET_IPv6 + case PF_INET6: + { + minlen = sizeof(struct sockaddr_in6); + } + break; +#endif + + default: + DEBUGPANIC(); + return -EINVAL; + } + + if (*fromlen < minlen) + { + return -EINVAL; + } + } + + /* Read from the network interface driver buffer */ + /* Or perform the TCP/IP or UDP recv() operation */ + + switch (psock->s_type) + { +#ifdef CONFIG_NET_TCP + case SOCK_STREAM: + { +#ifdef NET_TCP_HAVE_STACK + ret = inet_tcp_recvfrom(psock, buf, len, from, fromlen); +#else + ret = -ENOSYS; +#endif + } + break; +#endif /* CONFIG_NET_TCP */ + +#ifdef CONFIG_NET_UDP + case SOCK_DGRAM: + { +#ifdef NET_UDP_HAVE_STACK + ret = inet_udp_recvfrom(psock, buf, len, from, fromlen); +#else + ret = -ENOSYS; +#endif + } + break; +#endif /* CONFIG_NET_UDP || CONFIG_NET_LOCAL_DGRAM */ + + default: + { + nerr("ERROR: Unsupported socket type: %d\n", psock->s_type); + ret = -ENOSYS; + } + break; + } + + return ret; +} + +#endif /* CONFIG_NET */ diff --git a/net/socket/inet_sockif.c b/net/socket/inet_sockif.c index 62f8de0eb56..4866afd6959 100644 --- a/net/socket/inet_sockif.c +++ b/net/socket/inet_sockif.c @@ -70,9 +70,10 @@ static ssize_t inet_sendto(FAR struct socket *psock, FAR const void *buf, const struct sock_intf_s g_inet_sockif = { - inet_setup, /* si_setup */ - inet_send, /* si_send */ - inet_sendto, /* si_sendto */ + inet_setup, /* si_setup */ + inet_send, /* si_send */ + inet_sendto, /* si_sendto */ + inet_recvfrom /* si_recvfrom */ }; /**************************************************************************** @@ -237,9 +238,9 @@ static int inet_setup(FAR struct socket *psock, int protocol) * flags Send flags * * Returned Value: - * On success, returns the number of characters sent. On error, -1 is - * returned, and errno is set appropriately (see send() for the list of - * appropriate errors values. + * On success, returns the number of characters sent. On error, a negated + * errno value is returned (see send() for the list of appropriate error + * values. * ****************************************************************************/ @@ -333,9 +334,9 @@ static ssize_t inet_send(FAR struct socket *psock, FAR const void *buf, * tolen The length of the address structure * * Returned Value: - * On success, returns the number of characters sent. On error, -1 is - * returned, and errno is set appropriately (see send_to() for the list of - * appropriate errors values. + * On success, returns the number of characters sent. On error, a negated + * errno value is returned (see send_to() for the list of appropriate error + * values. * ****************************************************************************/ diff --git a/net/socket/recvfrom.c b/net/socket/recvfrom.c index ab16ee9c661..9b576cbcb85 100644 --- a/net/socket/recvfrom.c +++ b/net/socket/recvfrom.c @@ -1,7 +1,7 @@ /**************************************************************************** * net/socket/recvfrom.c * - * Copyright (C) 2007-2009, 2011-2016 Gregory Nutt. All rights reserved. + * Copyright (C) 2007-2009, 2011-2017 Gregory Nutt. All rights reserved. * Author: Gregory Nutt * * Redistribution and use in source and binary forms, with or without @@ -39,1758 +39,16 @@ #include -#ifdef CONFIG_NET - -#include -#include -#include -#include -#include -#include #include +#include -#ifdef CONFIG_NET_PKT -# include -#endif - -#include - -#include -#include #include #include -#include -#include -#include -#include -#include -#include "netdev/netdev.h" -#include "devif/devif.h" -#include "tcp/tcp.h" -#include "udp/udp.h" -#include "pkt/pkt.h" -#include "local/local.h" #include "socket/socket.h" #include "usrsock/usrsock.h" -/**************************************************************************** - * Pre-processor Definitions - ****************************************************************************/ - -#define IPv4BUF ((struct ipv4_hdr_s *)&dev->d_buf[NET_LL_HDRLEN(dev)]) -#define IPv6BUF ((struct ipv6_hdr_s *)&dev->d_buf[NET_LL_HDRLEN(dev)]) - -#define UDPIPv4BUF ((struct udp_hdr_s *)&dev->d_buf[NET_LL_HDRLEN(dev) + IPv4_HDRLEN]) -#define UDPIPv6BUF ((struct udp_hdr_s *)&dev->d_buf[NET_LL_HDRLEN(dev) + IPv6_HDRLEN]) - -#define TCPIPv4BUF ((struct tcp_hdr_s *)&dev->d_buf[NET_LL_HDRLEN(dev) + IPv4_HDRLEN]) -#define TCPIPv6BUF ((struct tcp_hdr_s *)&dev->d_buf[NET_LL_HDRLEN(dev) + IPv6_HDRLEN]) - -/**************************************************************************** - * Private Types - ****************************************************************************/ - -#if defined(NET_UDP_HAVE_STACK) || defined(NET_TCP_HAVE_STACK) \ - || defined(CONFIG_NET_PKT) -struct recvfrom_s -{ - FAR struct socket *rf_sock; /* The parent socket structure */ -#ifdef CONFIG_NET_SOCKOPTS - systime_t rf_starttime; /* rcv start time for determining timeout */ -#endif - FAR struct devif_callback_s *rf_cb; /* Reference to callback instance */ - sem_t rf_sem; /* Semaphore signals recv completion */ - size_t rf_buflen; /* Length of receive buffer */ - uint8_t *rf_buffer; /* Pointer to receive buffer */ - FAR struct sockaddr *rf_from; /* Address of sender */ - FAR socklen_t *rf_fromlen; /* Number of bytes allocated for address of sender */ - ssize_t rf_recvlen; /* The received length */ - int rf_result; /* Success:OK, failure:negated errno */ -}; -#endif /* NET_UDP_HAVE_STACK || NET_TCP_HAVE_STACK */ - -/**************************************************************************** - * Private Functions - ****************************************************************************/ - -/**************************************************************************** - * Name: recvfrom_add_recvlen - * - * Description: - * Update information about space available for new data and update size - * of data in buffer, This logic accounts for the case where - * recvfrom_udpreadahead() sets state.rf_recvlen == -1 . - * - * Parameters: - * pstate recvfrom state structure - * recvlen size of new data appended to buffer - * - * Returned Value: - * None - * - ****************************************************************************/ - -#if defined(NET_UDP_HAVE_STACK) || defined(NET_TCP_HAVE_STACK) \ - || defined(CONFIG_NET_PKT) - -static inline void recvfrom_add_recvlen(FAR struct recvfrom_s *pstate, - size_t recvlen) -{ - if (pstate->rf_recvlen < 0) - { - pstate->rf_recvlen = 0; - } - - pstate->rf_recvlen += recvlen; - pstate->rf_buffer += recvlen; - pstate->rf_buflen -= recvlen; -} -#endif /* NET_UDP_HAVE_STACK || NET_TCP_HAVE_STACK || CONFIG_NET_PKT */ - -/**************************************************************************** - * Name: recvfrom_newdata - * - * Description: - * Copy the read data from the packet - * - * Parameters: - * dev The structure of the network driver that caused the interrupt - * pstate recvfrom state structure - * - * Returned Value: - * The number of bytes taken from the packet. - * - * Assumptions: - * The network is locked. - * - ****************************************************************************/ - -#if defined(NET_UDP_HAVE_STACK) || defined(NET_TCP_HAVE_STACK) -static size_t recvfrom_newdata(FAR struct net_driver_s *dev, - FAR struct recvfrom_s *pstate) -{ - size_t recvlen; - - /* Get the length of the data to return */ - - if (dev->d_len > pstate->rf_buflen) - { - recvlen = pstate->rf_buflen; - } - else - { - recvlen = dev->d_len; - } - - /* Copy the new appdata into the user buffer */ - - memcpy(pstate->rf_buffer, dev->d_appdata, recvlen); - ninfo("Received %d bytes (of %d)\n", (int)recvlen, (int)dev->d_len); - - /* Update the accumulated size of the data read */ - - recvfrom_add_recvlen(pstate, recvlen); - - return recvlen; -} -#endif /* NET_UDP_HAVE_STACK || NET_TCP_HAVE_STACK */ - -/**************************************************************************** - * Name: recvfrom_newpktdata - * - * Description: - * Copy the read data from the packet - * - * Parameters: - * dev The structure of the network driver that caused the interrupt - * pstate recvfrom state structure - * - * Returned Value: - * None. - * - * Assumptions: - * The network is locked. - * - ****************************************************************************/ - -#ifdef CONFIG_NET_PKT -static void recvfrom_newpktdata(FAR struct net_driver_s *dev, - FAR struct recvfrom_s *pstate) -{ - size_t recvlen; - - if (dev->d_len > pstate->rf_buflen) - { - recvlen = pstate->rf_buflen; - } - else - { - recvlen = dev->d_len; - } - - /* Copy the new packet data into the user buffer */ - - memcpy(pstate->rf_buffer, dev->d_buf, recvlen); - ninfo("Received %d bytes (of %d)\n", (int)recvlen, (int)dev->d_len); - - /* Update the accumulated size of the data read */ - - recvfrom_add_recvlen(pstate, recvlen); -} -#endif /* CONFIG_NET_PKT */ - -/**************************************************************************** - * Name: recvfrom_newtcpdata - * - * Description: - * Copy the read data from the packet - * - * Parameters: - * dev The structure of the network driver that caused the interrupt - * pstate recvfrom state structure - * - * Returned Value: - * None. - * - * Assumptions: - * The network is locked. - * - ****************************************************************************/ - -#ifdef NET_TCP_HAVE_STACK -static inline void recvfrom_newtcpdata(FAR struct net_driver_s *dev, - FAR struct recvfrom_s *pstate) -{ - /* Take as much data from the packet as we can */ - - size_t recvlen = recvfrom_newdata(dev, pstate); - - /* If there is more data left in the packet that we could not buffer, then - * add it to the read-ahead buffers. - */ - - if (recvlen < dev->d_len) - { -#ifdef CONFIG_NET_TCP_READAHEAD - FAR struct tcp_conn_s *conn = (FAR struct tcp_conn_s *)pstate->rf_sock->s_conn; - FAR uint8_t *buffer = (FAR uint8_t *)dev->d_appdata + recvlen; - uint16_t buflen = dev->d_len - recvlen; -#ifdef CONFIG_DEBUG_NET - uint16_t nsaved; - - nsaved = tcp_datahandler(conn, buffer, buflen); -#else - (void)tcp_datahandler(conn, buffer, buflen); -#endif - - /* There are complicated buffering issues that are not addressed fully - * here. For example, what if up_datahandler() cannot buffer the - * remainder of the packet? In that case, the data will be dropped but - * still ACKed. Therefore it would not be resent. - * - * This is probably not an issue here because we only get here if the - * read-ahead buffers are empty and there would have to be something - * serioulsy wrong with the configuration not to be able to buffer a - * partial packet in this context. - */ - -#ifdef CONFIG_DEBUG_NET - if (nsaved < buflen) - { - nerr("ERROR: packet data not saved (%d bytes)\n", buflen - nsaved); - } -#endif -#else - nerr("ERROR: packet data lost (%d bytes)\n", dev->d_len - recvlen); -#endif - } - - /* Indicate no data in the buffer */ - - dev->d_len = 0; -} -#endif /* NET_TCP_HAVE_STACK */ - -/**************************************************************************** - * Name: recvfrom_newudpdata - * - * Description: - * Copy the read data from the packet - * - * Parameters: - * dev The sructure of the network driver that caused the interrupt - * pstate recvfrom state structure - * - * Returned Value: - * None. - * - * Assumptions: - * The network is locked. - * - ****************************************************************************/ - -#ifdef NET_UDP_HAVE_STACK -static inline void recvfrom_newudpdata(FAR struct net_driver_s *dev, - FAR struct recvfrom_s *pstate) -{ - /* Take as much data from the packet as we can */ - - (void)recvfrom_newdata(dev, pstate); - - /* Indicate no data in the buffer */ - - dev->d_len = 0; -} -#endif /* NET_UDP_HAVE_STACK */ - -/**************************************************************************** - * Name: recvfrom_tcpreadahead - * - * Description: - * Copy the read data from the packet - * - * Parameters: - * dev The structure of the network driver that caused the interrupt - * pstate recvfrom state structure - * - * Returned Value: - * None - * - * Assumptions: - * The network is locked. - * - ****************************************************************************/ - -#if defined(NET_TCP_HAVE_STACK) && defined(CONFIG_NET_TCP_READAHEAD) -static inline void recvfrom_tcpreadahead(struct recvfrom_s *pstate) -{ - FAR struct tcp_conn_s *conn = (FAR struct tcp_conn_s *)pstate->rf_sock->s_conn; - FAR struct iob_s *iob; - int recvlen; - - /* Check there is any TCP data already buffered in a read-ahead - * buffer. - */ - - while ((iob = iob_peek_queue(&conn->readahead)) != NULL && - pstate->rf_buflen > 0) - { - DEBUGASSERT(iob->io_pktlen > 0); - - /* Transfer that buffered data from the I/O buffer chain into - * the user buffer. - */ - - recvlen = iob_copyout(pstate->rf_buffer, iob, pstate->rf_buflen, 0); - ninfo("Received %d bytes (of %d)\n", recvlen, iob->io_pktlen); - - /* Update the accumulated size of the data read */ - - recvfrom_add_recvlen(pstate, recvlen); - - /* If we took all of the ata from the I/O buffer chain is empty, then - * release it. If there is still data available in the I/O buffer - * chain, then just trim the data that we have taken from the - * beginning of the I/O buffer chain. - */ - - if (recvlen >= iob->io_pktlen) - { - FAR struct iob_s *tmp; - - /* Remove the I/O buffer chain from the head of the read-ahead - * buffer queue. - */ - - tmp = iob_remove_queue(&conn->readahead); - DEBUGASSERT(tmp == iob); - UNUSED(tmp); - - /* And free the I/O buffer chain */ - - (void)iob_free_chain(iob); - } - else - { - /* The bytes that we have received from the head of the I/O - * buffer chain (probably changing the head of the I/O - * buffer queue). - */ - - (void)iob_trimhead_queue(&conn->readahead, recvlen); - } - } -} -#endif /* NET_TCP_HAVE_STACK && CONFIG_NET_TCP_READAHEAD */ - -#if defined(NET_UDP_HAVE_STACK) && defined(CONFIG_NET_UDP_READAHEAD) - -static inline void recvfrom_udpreadahead(struct recvfrom_s *pstate) -{ - FAR struct udp_conn_s *conn = (FAR struct udp_conn_s *)pstate->rf_sock->s_conn; - FAR struct iob_s *iob; - int recvlen; - - /* Check there is any UDP datagram already buffered in a read-ahead - * buffer. - */ - - pstate->rf_recvlen = -1; - - if ((iob = iob_peek_queue(&conn->readahead)) != NULL) - { - FAR struct iob_s *tmp; - uint8_t src_addr_size; - - DEBUGASSERT(iob->io_pktlen > 0); - - /* Transfer that buffered data from the I/O buffer chain into - * the user buffer. - */ - - recvlen = iob_copyout(&src_addr_size, iob, sizeof(uint8_t), 0); - if (recvlen != sizeof(uint8_t)) - { - goto out; - } - - if (0 -#ifdef CONFIG_NET_IPv6 - || src_addr_size == sizeof(struct sockaddr_in6) -#endif -#ifdef CONFIG_NET_IPv4 - || src_addr_size == sizeof(struct sockaddr_in) -#endif - ) - { - if (pstate->rf_from) - { - socklen_t len = *pstate->rf_fromlen; - len = (socklen_t)src_addr_size > len ? len : (socklen_t)src_addr_size; - - recvlen = iob_copyout((FAR uint8_t *)pstate->rf_from, iob, - len, sizeof(uint8_t)); - if (recvlen != len) - { - goto out; - } - } - } - - if (pstate->rf_buflen > 0) - { - recvlen = iob_copyout(pstate->rf_buffer, iob, pstate->rf_buflen, - src_addr_size + sizeof(uint8_t)); - - ninfo("Received %d bytes (of %d)\n", recvlen, iob->io_pktlen); - - /* Update the accumulated size of the data read */ - - pstate->rf_recvlen = recvlen; - pstate->rf_buffer += recvlen; - pstate->rf_buflen -= recvlen; - } - else - { - pstate->rf_recvlen = 0; - } - -out: - /* Remove the I/O buffer chain from the head of the read-ahead - * buffer queue. - */ - - tmp = iob_remove_queue(&conn->readahead); - DEBUGASSERT(tmp == iob); - UNUSED(tmp); - - /* And free the I/O buffer chain */ - - (void)iob_free_chain(iob); - } -} -#endif - -/**************************************************************************** - * Name: recvfrom_timeout - * - * Description: - * Check for recvfrom timeout. - * - * Parameters: - * pstate recvfrom state structure - * - * Returned Value: - * TRUE:timeout FALSE:no timeout - * - * Assumptions: - * The network is locked. - * - ****************************************************************************/ - -#if defined(NET_UDP_HAVE_STACK) || defined(NET_TCP_HAVE_STACK) -#ifdef CONFIG_NET_SOCKOPTS -static int recvfrom_timeout(struct recvfrom_s *pstate) -{ - FAR struct socket *psock = 0; - socktimeo_t timeo = 0; - - /* Check for a timeout configured via setsockopts(SO_RCVTIMEO). If none... - * we well let the read hang forever (except for the special case below). - */ - - /* Get the socket reference from the private data */ - - psock = pstate->rf_sock; - if (psock) - { - /* Recover the timeout value (zero if no timeout) */ - - timeo = psock->s_rcvtimeo; - } - - /* Use a fixed, configurable delay under the following circumstances: - * - * 1) This delay function has been enabled with CONFIG_NET_TCP_RECVDELAY > 0 - * 2) Some data has already been received from the socket. Since this can - * only be true for a TCP/IP socket, this logic applies only to TCP/IP - * sockets. And either - * 3) There is no configured receive timeout, or - * 4) The configured receive timeout is greater than than the delay - */ - -#if CONFIG_NET_TCP_RECVDELAY > 0 - if ((timeo == 0 || timeo > CONFIG_NET_TCP_RECVDELAY) && - pstate->rf_recvlen > 0) - { - /* Use the configured timeout */ - - timeo = CONFIG_NET_TCP_RECVDELAY; - } -#endif - - /* Is there an effective timeout? */ - - if (timeo) - { - /* Yes.. Check if the timeout has elapsed */ - - return net_timeo(pstate->rf_starttime, timeo); - } - - /* No timeout -- hang forever waiting for data. */ - - return FALSE; -} -#endif /* CONFIG_NET_SOCKOPTS */ -#endif /* NET_UDP_HAVE_STACK || NET_TCP_HAVE_STACK */ - -/**************************************************************************** - * Name: recvfrom_pktsender - * - * Description: - * - * Parameters: - * - * Returned Values: - * - * Assumptions: - * - ****************************************************************************/ - -#ifdef CONFIG_NET_PKT -static inline void recvfrom_pktsender(FAR struct net_driver_s *dev, - FAR struct recvfrom_s *pstate) -{ -} -#endif /* CONFIG_NET_PKT */ - -/**************************************************************************** - * Name: recvfrom_pktinterrupt - * - * Description: - * - * Parameters: - * - * Returned Values: - * - * Assumptions: - * - ****************************************************************************/ - -#ifdef CONFIG_NET_PKT -static uint16_t recvfrom_pktinterrupt(FAR struct net_driver_s *dev, - FAR void *pvconn, FAR void *pvpriv, - uint16_t flags) -{ - struct recvfrom_s *pstate = (struct recvfrom_s *)pvpriv; - - ninfo("flags: %04x\n", flags); - - /* 'priv' might be null in some race conditions (?) */ - - if (pstate) - { - /* If a new packet is available, then complete the read action. */ - - if ((flags & PKT_NEWDATA) != 0) - { - /* Copy the packet */ - recvfrom_newpktdata(dev, pstate); - - /* We are finished. */ - - ninfo("PKT done\n"); - - /* Don't allow any further call backs. */ - - pstate->rf_cb->flags = 0; - pstate->rf_cb->priv = NULL; - pstate->rf_cb->event = NULL; -#if 0 - /* Save the sender's address in the caller's 'from' location */ - - recvfrom_pktsender(dev, pstate); -#endif - /* indicate that the data has been consumed */ - - flags &= ~PKT_NEWDATA; - - /* Wake up the waiting thread, returning the number of bytes - * actually read. - */ - - sem_post(&pstate->rf_sem); - } - } - - return flags; -} -#endif /* CONFIG_NET_PKT */ - -/**************************************************************************** - * Name: recvfrom_tcpsender - * - * Description: - * Getting the sender's address from the UDP packet - * - * Parameters: - * dev - The device driver data structure - * pstate - the recvfrom state structure - * - * Returned Value: - * None - * - * Assumptions: - * Running at the interrupt level - * - ****************************************************************************/ - -#ifdef NET_TCP_HAVE_STACK -static inline void recvfrom_tcpsender(FAR struct net_driver_s *dev, - FAR struct recvfrom_s *pstate) -{ - /* Get the family from the packet type, IP address from the IP header, and - * the port number from the TCP header. - */ - -#ifdef CONFIG_NET_IPv6 -#ifdef CONFIG_NET_IPv4 - if (IFF_IS_IPv6(dev->d_flags)) -#endif - { - FAR struct sockaddr_in6 *infrom = - (FAR struct sockaddr_in6 *)pstate->rf_from; - - if (infrom) - { - FAR struct tcp_hdr_s *tcp = TCPIPv6BUF; - FAR struct ipv6_hdr_s *ipv6 = IPv6BUF; - - infrom->sin6_family = AF_INET6; - infrom->sin6_port = tcp->srcport; - - net_ipv6addr_copy(infrom->sin6_addr.s6_addr, ipv6->srcipaddr); - } - } -#endif /* CONFIG_NET_IPv6 */ - -#ifdef CONFIG_NET_IPv4 -#ifdef CONFIG_NET_IPv6 - else -#endif - { - FAR struct sockaddr_in *infrom = - (FAR struct sockaddr_in *)pstate->rf_from; - - if (infrom) - { - FAR struct tcp_hdr_s *tcp = TCPIPv4BUF; - FAR struct ipv4_hdr_s *ipv4 = IPv4BUF; - - infrom->sin_family = AF_INET; - infrom->sin_port = tcp->srcport; - - net_ipv4addr_copy(infrom->sin_addr.s_addr, - net_ip4addr_conv32(ipv4->srcipaddr)); - } - } -#endif /* CONFIG_NET_IPv4 */ -} -#endif /* NET_TCP_HAVE_STACK */ - -/**************************************************************************** - * Name: recvfrom_tcpinterrupt - * - * Description: - * This function is called from the interrupt level to perform the actual - * TCP receive operation via by the lower, device interfacing layer. - * - * Parameters: - * dev The structure of the network driver that caused the interrupt - * pvconn The connection structure associated with the socket - * flags Set of events describing why the callback was invoked - * - * Returned Value: - * None - * - * Assumptions: - * The network is locked. - * - ****************************************************************************/ - -#ifdef NET_TCP_HAVE_STACK -static uint16_t recvfrom_tcpinterrupt(FAR struct net_driver_s *dev, - FAR void *pvconn, FAR void *pvpriv, - uint16_t flags) -{ - FAR struct recvfrom_s *pstate = (struct recvfrom_s *)pvpriv; - -#if 0 /* REVISIT: The assertion fires. Why? */ -#ifdef CONFIG_NETDEV_MULTINIC - FAR struct tcp_conn_s *conn = (FAR struct tcp_conn_s *)pvconn; - - /* The TCP socket is connected and, hence, should be bound to a device. - * Make sure that the polling device is the own that we are bound to. - */ - - DEBUGASSERT(conn->dev == NULL || conn->dev == dev); - if (conn->dev != NULL && conn->dev != dev) - { - return flags; - } -#endif -#endif - - ninfo("flags: %04x\n", flags); - - /* 'priv' might be null in some race conditions (?) */ - - if (pstate) - { - /* If new data is available, then complete the read action. */ - - if ((flags & TCP_NEWDATA) != 0) - { - /* Copy the data from the packet (saving any unused bytes from the - * packet in the read-ahead buffer). - */ - - recvfrom_newtcpdata(dev, pstate); - - /* Save the sender's address in the caller's 'from' location */ - - recvfrom_tcpsender(dev, pstate); - - /* Indicate that the data has been consumed and that an ACK - * should be sent. - */ - - flags = (flags & ~TCP_NEWDATA) | TCP_SNDACK; - - /* Check for transfer complete. We will consider the transfer - * complete in own of two different ways, depending on the setting - * of CONFIG_NET_TCP_RECVDELAY. - * - * 1) If CONFIG_NET_TCP_RECVDELAY == 0 then we will consider the - * TCP/IP transfer complete as soon as any data has been received. - * This is safe because if any additional data is received, it - * will be retained in the TCP/IP read-ahead buffer until the - * next receive is performed. - * 2) CONFIG_NET_TCP_RECVDELAY > 0 may be set to wait a little - * bit to determine if more data will be received. You might - * do this if read-ahead buffering is disabled and we want to - * minimize the loss of back-to-back packets. In this case, - * the transfer is complete when either a) the entire user buffer - * is full or 2) when the receive timeout occurs (below). - */ - -#if CONFIG_NET_TCP_RECVDELAY > 0 - if (pstate->rf_buflen == 0) -#else - if (pstate->rf_recvlen > 0) -#endif - { - ninfo("TCP resume\n"); - - /* The TCP receive buffer is non-empty. Return now and don't - * allow any further TCP call backs. - */ - - pstate->rf_cb->flags = 0; - pstate->rf_cb->priv = NULL; - pstate->rf_cb->event = NULL; - - /* Wake up the waiting thread, returning the number of bytes - * actually read. - */ - - sem_post(&pstate->rf_sem); - } - -#ifdef CONFIG_NET_SOCKOPTS - /* Reset the timeout. We will want a short timeout to terminate - * the TCP receive. - */ - - pstate->rf_starttime = clock_systimer(); -#endif - } - - /* Check for a loss of connection. - * - * TCP_DISCONN_EVENTS: - * TCP_CLOSE: The remote host has closed the connection - * TCP_ABORT: The remote host has aborted the connection - * TCP_TIMEDOUT: Connection aborted due to too many retransmissions. - * NETDEV_DOWN: The network device went down - */ - - else if ((flags & TCP_DISCONN_EVENTS) != 0) - { - ninfo("Lost connection\n"); - - /* Stop further callbacks */ - - pstate->rf_cb->flags = 0; - pstate->rf_cb->priv = NULL; - pstate->rf_cb->event = NULL; - - /* Handle loss-of-connection event */ - - net_lostconnection(pstate->rf_sock, flags); - - /* Check if the peer gracefully closed the connection. */ - - if ((flags & TCP_CLOSE) != 0) - { - /* This case should always return success (zero)! The value of - * rf_recvlen, if zero, will indicate that the connection was - * gracefully closed. - */ - - pstate->rf_result = 0; - } - else - { - /* If no data has been received, then return ENOTCONN. - * Otherwise, let this return success. The failure will - * be reported the next time that recv[from]() is called. - */ - -#if CONFIG_NET_TCP_RECVDELAY > 0 - if (pstate->rf_recvlen > 0) - { - pstate->rf_result = 0; - } - else - { - pstate->rf_result = -ENOTCONN; - } -#else - pstate->rf_result = -ENOTCONN; -#endif - } - - /* Wake up the waiting thread */ - - sem_post(&pstate->rf_sem); - } - -#ifdef CONFIG_NET_SOCKOPTS - /* No data has been received -- this is some other event... probably a - * poll -- check for a timeout. - */ - - else if (recvfrom_timeout(pstate)) - { - /* Yes.. the timeout has elapsed... do not allow any further - * callbacks - */ - - ninfo("TCP timeout\n"); - - pstate->rf_cb->flags = 0; - pstate->rf_cb->priv = NULL; - pstate->rf_cb->event = NULL; - - /* Report an error only if no data has been received. (If - * CONFIG_NET_TCP_RECVDELAY then rf_recvlen should always be - * less than or equal to zero). - */ - -#if CONFIG_NET_TCP_RECVDELAY > 0 - if (pstate->rf_recvlen <= 0) -#endif - { - /* Report the timeout error */ - - pstate->rf_result = -EAGAIN; - } - - /* Wake up the waiting thread, returning either the error -EAGAIN - * that signals the timeout event or the data received up to - * the point that the timeout occurred (no error). - */ - - sem_post(&pstate->rf_sem); - } -#endif /* CONFIG_NET_SOCKOPTS */ - } - - return flags; -} -#endif /* NET_TCP_HAVE_STACK */ - -/**************************************************************************** - * Name: recvfrom_udpsender - * - * Description: - * Getting the sender's address from the UDP packet - * - * Parameters: - * dev - The device driver data structure - * pstate - the recvfrom state structure - * - * Returned Value: - * None - * - * Assumptions: - * The network is locked. - * - ****************************************************************************/ - -#ifdef NET_UDP_HAVE_STACK -static inline void recvfrom_udpsender(struct net_driver_s *dev, struct recvfrom_s *pstate) -{ - /* Get the family from the packet type, IP address from the IP header, and - * the port number from the UDP header. - */ - -#ifdef CONFIG_NET_IPv6 -#ifdef CONFIG_NET_IPv4 - if (IFF_IS_IPv6(dev->d_flags)) -#endif - { - FAR struct sockaddr_in6 *infrom = - (FAR struct sockaddr_in6 *)pstate->rf_from; - FAR socklen_t *fromlen = pstate->rf_fromlen; - - if (infrom) - { - FAR struct udp_hdr_s *udp = UDPIPv6BUF; - FAR struct ipv6_hdr_s *ipv6 = IPv6BUF; - - infrom->sin6_family = AF_INET6; - infrom->sin6_port = udp->srcport; - *fromlen = sizeof(struct sockaddr_in6); - - net_ipv6addr_copy(infrom->sin6_addr.s6_addr, ipv6->srcipaddr); - } - } -#endif /* CONFIG_NET_IPv6 */ - -#ifdef CONFIG_NET_IPv4 -#ifdef CONFIG_NET_IPv6 - else -#endif - { - FAR struct sockaddr_in *infrom = - (FAR struct sockaddr_in *)pstate->rf_from; - - if (infrom) - { -#ifdef CONFIG_NET_IPv6 - FAR struct udp_conn_s *conn = - (FAR struct udp_conn_s *)pstate->rf_sock->s_conn; - - /* Hybrid dual-stack IPv6/IPv4 implementations recognize a special - * class of addresses, the IPv4-mapped IPv6 addresses. - */ - - if (conn->domain == PF_INET6) - { - FAR struct sockaddr_in6 *infrom6 = (FAR struct sockaddr_in6 *)infrom; - FAR socklen_t *fromlen = pstate->rf_fromlen; - FAR struct udp_hdr_s *udp = UDPIPv6BUF; - FAR struct ipv6_hdr_s *ipv6 = IPv6BUF; - in_addr_t ipv4addr; - - /* Encode the IPv4 address as an IPv4-mapped IPv6 address */ - - infrom6->sin6_family = AF_INET6; - infrom6->sin6_port = udp->srcport; - *fromlen = sizeof(struct sockaddr_in6); - - ipv4addr = net_ip4addr_conv32(ipv6->srcipaddr); - ip6_map_ipv4addr(ipv4addr, infrom6->sin6_addr.s6_addr16); - } - else -#endif - { - FAR struct udp_hdr_s *udp = UDPIPv4BUF; - FAR struct ipv4_hdr_s *ipv4 = IPv4BUF; - - infrom->sin_family = AF_INET; - infrom->sin_port = udp->srcport; - - net_ipv4addr_copy(infrom->sin_addr.s_addr, - net_ip4addr_conv32(ipv4->srcipaddr)); - } - } - } -#endif /* CONFIG_NET_IPv4 */ -} -#endif /* NET_UDP_HAVE_STACK */ - -/**************************************************************************** - * Name: recvfrom_udp_terminate - * - * Description: - * Terminate the UDP transfer. - * - * Parameters: - * pstate - The recvfrom state structure - * result - The result of the operation - * - * Returned Value: - * None - * - ****************************************************************************/ - -#ifdef NET_UDP_HAVE_STACK -static void recvfrom_udp_terminate(FAR struct recvfrom_s *pstate, int result) -{ - /* Don't allow any further UDP call backs. */ - - pstate->rf_cb->flags = 0; - pstate->rf_cb->priv = NULL; - pstate->rf_cb->event = NULL; - - /* Save the result of the transfer */ - - pstate->rf_result = result; - - /* Wake up the waiting thread, returning the number of bytes - * actually read. - */ - - sem_post(&pstate->rf_sem); -} -#endif /* NET_UDP_HAVE_STACK */ - -/**************************************************************************** - * Name: recvfrom_udp_interrupt - * - * Description: - * This function is called from the interrupt level to perform the actual - * UDP receive operation via by the lower, device interfacing layer. - * - * Parameters: - * dev The structure of the network driver that caused the interrupt - * pvconn The connection structure associated with the socket - * flags Set of events describing why the callback was invoked - * - * Returned Value: - * None - * - * Assumptions: - * The network is locked. - * - ****************************************************************************/ - -#ifdef NET_UDP_HAVE_STACK -static uint16_t recvfrom_udp_interrupt(FAR struct net_driver_s *dev, - FAR void *pvconn, FAR void *pvpriv, - uint16_t flags) -{ - FAR struct recvfrom_s *pstate = (FAR struct recvfrom_s *)pvpriv; - - ninfo("flags: %04x\n", flags); - - /* 'priv' might be null in some race conditions (?) */ - - if (pstate) - { - /* If the network device has gone down, then we will have terminate - * the wait now with an error. - */ - - if ((flags & NETDEV_DOWN) != 0) - { - /* Terminate the transfer with an error. */ - - nerr("ERROR: Network is down\n"); - recvfrom_udp_terminate(pstate, -ENETUNREACH); - } - - /* If new data is available, then complete the read action. */ - - else if ((flags & UDP_NEWDATA) != 0) - { - /* Copy the data from the packet */ - - recvfrom_newudpdata(dev, pstate); - - /* We are finished. */ - - ninfo("UDP done\n"); - - /* Save the sender's address in the caller's 'from' location */ - - recvfrom_udpsender(dev, pstate); - - /* Don't allow any further UDP call backs. */ - - recvfrom_udp_terminate(pstate, OK); - - /* Indicate that the data has been consumed */ - - flags &= ~UDP_NEWDATA; - } - -#ifdef CONFIG_NET_SOCKOPTS - /* No data has been received -- this is some other event... probably a - * poll -- check for a timeout. - */ - - else if (recvfrom_timeout(pstate)) - { - /* Yes.. the timeout has elapsed... do not allow any further - * callbacks - */ - - nerr("ERROR: UDP timeout\n"); - - /* Terminate the transfer with an -EAGAIN error */ - - recvfrom_udp_terminate(pstate, -EAGAIN); - } -#endif /* CONFIG_NET_SOCKOPTS */ - } - - return flags; -} -#endif /* NET_UDP_HAVE_STACK */ - -/**************************************************************************** - * Name: recvfrom_init - * - * Description: - * Initialize the state structure - * - * Parameters: - * psock Pointer to the socket structure for the socket - * buf Buffer to receive data - * len Length of buffer - * pstate A pointer to the state structure to be initialized - * - * Returned Value: - * None - * - * Assumptions: - * - ****************************************************************************/ - -#if defined(NET_UDP_HAVE_STACK) || defined(NET_TCP_HAVE_STACK) \ - || defined(CONFIG_NET_PKT) -static void recvfrom_init(FAR struct socket *psock, FAR void *buf, - size_t len, FAR struct sockaddr *infrom, - FAR socklen_t *fromlen, - FAR struct recvfrom_s *pstate) -{ - /* Initialize the state structure. */ - - memset(pstate, 0, sizeof(struct recvfrom_s)); - - /* This semaphore is used for signaling and, hence, should not have - * priority inheritance enabled. - */ - - (void)sem_init(&pstate->rf_sem, 0, 0); /* Doesn't really fail */ - (void)sem_setprotocol(&pstate->rf_sem, SEM_PRIO_NONE); - - pstate->rf_buflen = len; - pstate->rf_buffer = buf; - pstate->rf_from = infrom; - pstate->rf_fromlen = fromlen; - - /* Set up the start time for the timeout */ - - pstate->rf_sock = psock; -#ifdef CONFIG_NET_SOCKOPTS - pstate->rf_starttime = clock_systimer(); -#endif -} - -/* The only un-initialization that has to be performed is destroying the - * semaphore. - */ - -#define recvfrom_uninit(s) sem_destroy(&(s)->rf_sem) - -#endif /* NET_UDP_HAVE_STACK || NET_TCP_HAVE_STACK */ - -/**************************************************************************** - * Name: recvfrom_result - * - * Description: - * Evaluate the result of the recv operations - * - * Parameters: - * result The result of the net_lockedwait operation (may indicate EINTR) - * pstate A pointer to the state structure to be initialized - * - * Returned Value: - * The result of the recv operation with errno set appropriately - * - * Assumptions: - * - ****************************************************************************/ - -#if defined(NET_UDP_HAVE_STACK) || defined(NET_TCP_HAVE_STACK) \ - || defined(CONFIG_NET_PKT) -static ssize_t recvfrom_result(int result, struct recvfrom_s *pstate) -{ - int save_errno = get_errno(); /* In case something we do changes it */ - - /* Check for a error/timeout detected by the interrupt handler. Errors are - * signaled by negative errno values for the rcv length - */ - - if (pstate->rf_result < 0) - { - /* This might return EAGAIN on a timeout or ENOTCONN on loss of - * connection (TCP only) - */ - - return pstate->rf_result; - } - - /* If net_lockedwait failed, then we were probably reawakened by a signal. In - * this case, net_lockedwait will have set errno appropriately. - */ - - if (result < 0) - { - return -save_errno; - } - - return pstate->rf_recvlen; -} -#endif /* NET_UDP_HAVE_STACK || NET_TCP_HAVE_STACK */ - -/**************************************************************************** - * Name: recvfromo_pkt_rxnotify - * - * Description: - * Notify the appropriate device driver that we are ready to receive a - * packet (PKT) - * - * Parameters: - * conn - The PKT connection structure - * - * Returned Value: - * None - * - ****************************************************************************/ - -#if 0 /* Not implemented */ -static void recvfromo_pkt_rxnotify(FAR struct pkt_conn_s *conn) -{ -# warning Missing logic -} -#endif - -/**************************************************************************** - * Name: recvfrom_udp_rxnotify - * - * Description: - * Notify the appropriate device driver that we are ready to receive a - * packet (UDP) - * - * Parameters: - * psock - Socket state structure - * conn - The UDP connection structure - * - * Returned Value: - * None - * - ****************************************************************************/ - -#ifdef NET_UDP_HAVE_STACK -static inline void recvfrom_udp_rxnotify(FAR struct socket *psock, - FAR struct udp_conn_s *conn) -{ -#ifdef CONFIG_NET_IPv4 -#ifdef CONFIG_NET_IPv6 - /* If both IPv4 and IPv6 support are enabled, then we will need to select - * the device driver using the appropriate IP domain. - */ - - if (psock->s_domain == PF_INET) -#endif - { - /* Notify the device driver of the receive ready */ - -#ifdef CONFIG_NETDEV_MULTINIC - netdev_ipv4_rxnotify(conn->u.ipv4.laddr, conn->u.ipv4.raddr); -#else - netdev_ipv4_rxnotify(conn->u.ipv4.raddr); -#endif - } -#endif /* CONFIG_NET_IPv4 */ - -#ifdef CONFIG_NET_IPv6 -#ifdef CONFIG_NET_IPv4 - else /* if (psock->s_domain == PF_INET6) */ -#endif /* CONFIG_NET_IPv4 */ - { - /* Notify the device driver of the receive ready */ - - DEBUGASSERT(psock->s_domain == PF_INET6); -#ifdef CONFIG_NETDEV_MULTINIC - netdev_ipv6_rxnotify(conn->u.ipv6.laddr, conn->u.ipv6.raddr); -#else - netdev_ipv6_rxnotify(conn->u.ipv6.raddr); -#endif - } -#endif /* CONFIG_NET_IPv6 */ -} -#endif /* NET_UDP_HAVE_STACK */ - -/**************************************************************************** - * Name: pkt_recvfrom - * - * Description: - * Perform the recvfrom operation for packet socket - * - * Parameters: - * - * Returned Value: - * - * Assumptions: - * - ****************************************************************************/ - -#ifdef CONFIG_NET_PKT -static ssize_t pkt_recvfrom(FAR struct socket *psock, FAR void *buf, size_t len, - FAR struct sockaddr *from, FAR socklen_t *fromlen) -{ - FAR struct pkt_conn_s *conn = (FAR struct pkt_conn_s *)psock->s_conn; - FAR struct net_driver_s *dev; - struct recvfrom_s state; - int ret; - - /* Perform the packet recvfrom() operation */ - - /* Initialize the state structure. This is done with interrupts - * disabled because we don't want anything to happen until we - * are ready. - */ - - net_lock(); - recvfrom_init(psock, buf, len, from, fromlen, &state); - - /* Get the device driver that will service this transfer */ - - dev = pkt_find_device(conn); - if (dev == NULL) - { - ret = -ENODEV; - goto errout_with_state; - } - - /* TODO recvfrom_init() expects from to be of type sockaddr_in, but - * in our case is sockaddr_ll - */ - -#if 0 - ret = pkt_connect(conn, NULL); - if (ret < 0) - { - goto errout_with_state; - } -#endif - - /* Set up the callback in the connection */ - - state.rf_cb = pkt_callback_alloc(dev, conn); - if (state.rf_cb) - { - state.rf_cb->flags = (PKT_NEWDATA | PKT_POLL); - state.rf_cb->priv = (FAR void *)&state; - state.rf_cb->event = recvfrom_pktinterrupt; - - /* Notify the device driver of the receive call */ - -#if 0 /* Not implemented */ - recvfromo_pkt_rxnotify(conn); -#endif - - /* Wait for either the receive to complete or for an error/timeout to occur. - * NOTES: (1) net_lockedwait will also terminate if a signal is received, (2) - * interrupts are disabled! They will be re-enabled while the task sleeps - * and automatically re-enabled when the task restarts. - */ - - ret = net_lockedwait(&state.rf_sem); - - /* Make sure that no further interrupts are processed */ - - pkt_callback_free(dev, conn, state.rf_cb); - ret = recvfrom_result(ret, &state); - } - else - { - ret = -EBUSY; - } - -errout_with_state: - net_unlock(); - recvfrom_uninit(&state); - return ret; -} -#endif /* CONFIG_NET_PKT */ - -/**************************************************************************** - * Name: udp_recvfrom - * - * Description: - * Perform the recvfrom operation for a UDP SOCK_DGRAM - * - * Parameters: - * psock Pointer to the socket structure for the SOCK_DRAM socket - * buf Buffer to receive data - * len Length of buffer - * from INET address of source (may be NULL) - * - * Returned Value: - * On success, returns the number of characters received. On error, - * -errno is returned (see recvfrom for list of errnos). - * - * Assumptions: - * - ****************************************************************************/ - -#ifdef NET_UDP_HAVE_STACK -static ssize_t udp_recvfrom(FAR struct socket *psock, FAR void *buf, size_t len, - FAR struct sockaddr *from, FAR socklen_t *fromlen) -{ - FAR struct udp_conn_s *conn = (FAR struct udp_conn_s *)psock->s_conn; - FAR struct net_driver_s *dev; - struct recvfrom_s state; - int ret; - - /* Perform the UDP recvfrom() operation */ - - /* Initialize the state structure. This is done with interrupts - * disabled because we don't want anything to happen until we - * are ready. - */ - - net_lock(); - recvfrom_init(psock, buf, len, from, fromlen, &state); - - /* Setup the UDP remote connection */ - - ret = udp_connect(conn, NULL); - if (ret < 0) - { - goto errout_with_state; - } - -#ifdef CONFIG_NET_UDP_READAHEAD - recvfrom_udpreadahead(&state); - - /* The default return value is the number of bytes that we just copied - * into the user buffer. We will return this if the socket has become - * disconnected or if the user request was completely satisfied with - * data from the readahead buffers. - */ - - ret = state.rf_recvlen; - -#else - /* Otherwise, the default return value of zero is used (only for the case - * where len == state.rf_buflen is zero). - */ - - ret = 0; -#endif - -#ifdef CONFIG_NET_UDP_READAHEAD - if (_SS_ISNONBLOCK(psock->s_flags)) - { - /* Return the number of bytes read from the read-ahead buffer if - * something was received (already in 'ret'); EAGAIN if not. - */ - - if (ret < 0) - { - /* Nothing was received */ - - ret = -EAGAIN; - } - } - - /* It is okay to block if we need to. If there is space to receive anything - * more, then we will wait to receive the data. Otherwise return the number - * of bytes read from the read-ahead buffer (already in 'ret'). - * - * NOTE: that recvfrom_udpreadahead() may set state.rf_recvlen == -1. - */ - - else if (state.rf_recvlen <= 0) -#endif - { - /* Get the device that will handle the packet transfers. This may be - * NULL if the UDP socket is bound to INADDR_ANY. In that case, no - * NETDEV_DOWN notifications will be received. - */ - - dev = udp_find_laddr_device(conn); - - /* Set up the callback in the connection */ - - state.rf_cb = udp_callback_alloc(dev, conn); - if (state.rf_cb) - { - /* Set up the callback in the connection */ - - state.rf_cb->flags = (UDP_NEWDATA | UDP_POLL | NETDEV_DOWN); - state.rf_cb->priv = (FAR void *)&state; - state.rf_cb->event = recvfrom_udp_interrupt; - - /* Notify the device driver of the receive call */ - - recvfrom_udp_rxnotify(psock, conn); - - /* Wait for either the receive to complete or for an error/timeout - * to occur. NOTES: (1) net_lockedwait will also terminate if a - * signal is received, (2) interrupts are disabled! They will be - * re-enabled while the task sleeps and automatically re-enabled - * when the task restarts. - */ - - ret = net_lockedwait(&state. rf_sem); - - /* Make sure that no further interrupts are processed */ - - udp_callback_free(dev, conn, state.rf_cb); - ret = recvfrom_result(ret, &state); - } - else - { - ret = -EBUSY; - } - } - -errout_with_state: - net_unlock(); - recvfrom_uninit(&state); - return ret; -} -#endif /* NET_UDP_HAVE_STACK */ - -/**************************************************************************** - * Name: tcp_recvfrom - * - * Description: - * Perform the recvfrom operation for a TCP/IP SOCK_STREAM - * - * Parameters: - * psock Pointer to the socket structure for the SOCK_DRAM socket - * buf Buffer to receive data - * len Length of buffer - * from INET address of source (may be NULL) - * - * Returned Value: - * On success, returns the number of characters received. On error, - * -errno is returned (see recvfrom for list of errnos). - * - * Assumptions: - * - ****************************************************************************/ - -#ifdef NET_TCP_HAVE_STACK -static ssize_t tcp_recvfrom(FAR struct socket *psock, FAR void *buf, size_t len, - FAR struct sockaddr *from, FAR socklen_t *fromlen) -{ - struct recvfrom_s state; - int ret; - - /* Initialize the state structure. This is done with interrupts - * disabled because we don't want anything to happen until we - * are ready. - */ - - net_lock(); - recvfrom_init(psock, buf, len, from, fromlen, &state); - - /* Handle any any TCP data already buffered in a read-ahead buffer. NOTE - * that there may be read-ahead data to be retrieved even after the - * socket has been disconnected. - */ - -#ifdef CONFIG_NET_TCP_READAHEAD - recvfrom_tcpreadahead(&state); - - /* The default return value is the number of bytes that we just copied - * into the user buffer. We will return this if the socket has become - * disconnected or if the user request was completely satisfied with - * data from the readahead buffers. - */ - - ret = state.rf_recvlen; - -#else - /* Otherwise, the default return value of zero is used (only for the case - * where len == state.rf_buflen is zero). - */ - - ret = 0; -#endif - - /* Verify that the SOCK_STREAM has been and still is connected */ - - if (!_SS_ISCONNECTED(psock->s_flags)) - { - /* Was any data transferred from the readahead buffer after we were - * disconnected? If so, then return the number of bytes received. We - * will wait to return end disconnection indications the next time that - * recvfrom() is called. - * - * If no data was received (i.e., ret == 0 -- it will not be negative) - * and the connection was gracefully closed by the remote peer, then return - * success. If rf_recvlen is zero, the caller of recvfrom() will get an - * end-of-file indication. - */ - -#ifdef CONFIG_NET_TCP_READAHEAD - if (ret <= 0 && !_SS_ISCLOSED(psock->s_flags)) -#else - if (!_SS_ISCLOSED(psock->s_flags)) -#endif - { - /* Nothing was previously received from the readahead buffers. - * The SOCK_STREAM must be (re-)connected in order to receive any - * additional data. - */ - - ret = -ENOTCONN; - } - } - - /* In general, this implementation will not support non-blocking socket - * operations... except in a few cases: Here for TCP receive with read-ahead - * enabled. If this socket is configured as non-blocking then return EAGAIN - * if no data was obtained from the read-ahead buffers. - */ - - else -#ifdef CONFIG_NET_TCP_READAHEAD - if (_SS_ISNONBLOCK(psock->s_flags)) - { - /* Return the number of bytes read from the read-ahead buffer if - * something was received (already in 'ret'); EAGAIN if not. - */ - - if (ret <= 0) - { - /* Nothing was received */ - - ret = -EAGAIN; - } - } - - /* It is okay to block if we need to. If there is space to receive anything - * more, then we will wait to receive the data. Otherwise return the number - * of bytes read from the read-ahead buffer (already in 'ret'). - */ - - else -#endif - - /* We get here when we we decide that we need to setup the wait for incoming - * TCP/IP data. Just a few more conditions to check: - * - * 1) Make sure thet there is buffer space to receive additional data - * (state.rf_buflen > 0). This could be zero, for example, if read-ahead - * buffering was enabled and we filled the user buffer with data from - * the read-ahead buffers. And - * 2) if read-ahead buffering is enabled (CONFIG_NET_TCP_READAHEAD) - * and delay logic is disabled (CONFIG_NET_TCP_RECVDELAY == 0), then we - * not want to wait if we already obtained some data from the read-ahead - * buffer. In that case, return now with what we have (don't want for more - * because there may be no timeout). - */ - -#if CONFIG_NET_TCP_RECVDELAY == 0 && defined(CONFIG_NET_TCP_READAHEAD) - if (state.rf_recvlen == 0 && state.rf_buflen > 0) -#else - if (state.rf_buflen > 0) -#endif - { - FAR struct tcp_conn_s *conn = (FAR struct tcp_conn_s *)psock->s_conn; - - /* Set up the callback in the connection */ - - state.rf_cb = tcp_callback_alloc(conn); - if (state.rf_cb) - { - state.rf_cb->flags = (TCP_NEWDATA | TCP_POLL | TCP_DISCONN_EVENTS); - state.rf_cb->priv = (FAR void *)&state; - state.rf_cb->event = recvfrom_tcpinterrupt; - - /* Wait for either the receive to complete or for an error/timeout - * to occur. - * - * NOTES: (1) net_lockedwait will also terminate if a signal is - * received, (2) interrupts may be disabled! They will be re- - * enabled while the task sleeps and automatically re-enabled when - * the task restarts. - */ - - ret = net_lockedwait(&state.rf_sem); - - /* Make sure that no further interrupts are processed */ - - tcp_callback_free(conn, state.rf_cb); - ret = recvfrom_result(ret, &state); - } - else - { - ret = -EBUSY; - } - } - - net_unlock(); - recvfrom_uninit(&state); - return (ssize_t)ret; -} -#endif /* NET_TCP_HAVE_STACK */ +#ifdef CONFIG_NET /**************************************************************************** * Public Functions @@ -1871,7 +129,7 @@ ssize_t psock_recvfrom(FAR struct socket *psock, FAR void *buf, size_t len, } #endif - if (from && !fromlen) + if (from != NULL && fromlen <= 0) { errcode = EINVAL; goto errout; @@ -1879,175 +137,55 @@ ssize_t psock_recvfrom(FAR struct socket *psock, FAR void *buf, size_t len, /* Verify that the sockfd corresponds to valid, allocated socket */ - if (!psock || psock->s_crefs <= 0) + if (psock == NULL || psock->s_crefs <= 0) { errcode = EBADF; goto errout; } + /* Set the socket state to receiving */ + + psock->s_flags = _SS_SETSTATE(psock->s_flags, _SF_RECV); + #ifdef CONFIG_NET_USRSOCK if (psock->s_type == SOCK_USRSOCK_TYPE) { /* Perform the usrsock recvfrom operation */ ret = usrsock_recvfrom(psock, buf, len, from, fromlen); - if (ret < 0) - { - errcode = -ret; - goto errout; - } - - return ret; } + else #endif + { + /* Let logic specific to this address family handle the recvfrom() + * operation. + */ - /* If a 'from' address has been provided, verify that it is large - * enough to hold this address family. - */ + DEBUGASSERT(psock->s_sockif != NULL && + psock->s_sockif->si_recvfrom != NULL); - if (from) - { - socklen_t minlen; - - /* Get the minimum socket length */ - - switch (psock->s_domain) - { -#ifdef CONFIG_NET_IPv4 - case PF_INET: - { - minlen = sizeof(struct sockaddr_in); - } - break; -#endif - -#ifdef CONFIG_NET_IPv6 - case PF_INET6: - { - minlen = sizeof(struct sockaddr_in6); - } - break; -#endif - -#ifdef CONFIG_NET_LOCAL - case PF_LOCAL: - { - minlen = sizeof(sa_family_t); - } - break; -#endif - - default: - DEBUGPANIC(); - errcode = EINVAL; - goto errout; - } - - if (*fromlen < minlen) - { - errcode = EINVAL; - goto errout; - } - } - - /* Set the socket state to receiving */ - - psock->s_flags = _SS_SETSTATE(psock->s_flags, _SF_RECV); - - /* Read from the network interface driver buffer */ - /* Or perform the TCP/IP or UDP recv() operation */ - - switch (psock->s_type) - { -#ifdef CONFIG_NET_PKT - case SOCK_RAW: + if (psock->s_sockif->si_recvfrom != NULL) { - ret = pkt_recvfrom(psock, buf, len, from, fromlen); + ret = psock->s_sockif->si_recvfrom(psock, buf, len, flags, + from, fromlen); } - break; -#endif /* CONFIG_NET_PKT */ - -#if defined(CONFIG_NET_TCP) || defined(CONFIG_NET_LOCAL_STREAM) - case SOCK_STREAM: + else { -#ifdef CONFIG_NET_LOCAL_STREAM -#ifdef CONFIG_NET_TCP - if (psock->s_domain == PF_LOCAL) -#endif - { - ret = psock_local_recvfrom(psock, buf, len, flags, - from, fromlen); - } -#endif /* CONFIG_NET_LOCAL_STREAM */ - -#ifdef CONFIG_NET_TCP -#ifdef CONFIG_NET_LOCAL_STREAM - else -#endif - { -#ifdef NET_TCP_HAVE_STACK - ret = tcp_recvfrom(psock, buf, len, from, fromlen); -#else - ret = -ENOSYS; -#endif - } -#endif /* CONFIG_NET_TCP */ - } - break; -#endif /* CONFIG_NET_TCP || CONFIG_NET_LOCAL_STREAM */ - -#if defined(CONFIG_NET_UDP) || defined(CONFIG_NET_LOCAL_DGRAM) - case SOCK_DGRAM: - { -#ifdef CONFIG_NET_LOCAL_DGRAM -#ifdef CONFIG_NET_UDP - if (psock->s_domain == PF_LOCAL) -#endif - { - ret = psock_local_recvfrom(psock, buf, len, flags, - from, fromlen); - } -#endif /* CONFIG_NET_LOCAL_DGRAM */ - -#ifdef CONFIG_NET_UDP -#ifdef CONFIG_NET_LOCAL_DGRAM - else -#endif - { -#ifdef NET_UDP_HAVE_STACK - ret = udp_recvfrom(psock, buf, len, from, fromlen); -#else - ret = -ENOSYS; -#endif - } -#endif /* CONFIG_NET_UDP */ - } - break; -#endif /* CONFIG_NET_UDP || CONFIG_NET_LOCAL_DGRAM */ - - default: - { - nerr("ERROR: Unsupported socket type: %d\n", psock->s_type); ret = -ENOSYS; } - break; - } + } /* Set the socket state to idle */ psock->s_flags = _SS_SETSTATE(psock->s_flags, _SF_IDLE); - /* Handle returned errors */ - + leave_cancellation_point(); if (ret < 0) { errcode = -ret; goto errout; } - /* Success return */ - - leave_cancellation_point(); return ret; errout: diff --git a/net/socket/socket.h b/net/socket/socket.h index 36788d465ab..a7f43ce10e8 100644 --- a/net/socket/socket.h +++ b/net/socket/socket.h @@ -431,13 +431,46 @@ int net_timeo(systime_t start_time, socktimeo_t timeo); * In this case the process will also receive a SIGPIPE unless * MSG_NOSIGNAL is set. * - * Assumptions: - * ****************************************************************************/ ssize_t psock_send(FAR struct socket *psock, FAR const void *buf, size_t len, int flags); +/**************************************************************************** + * Name: inet_recvfrom + * + * Description: + * Implements the socket recvfrom interface for the case of the AF_INET + * and AF_INET6 address families. inet_recvfrom() receives messages from + * a socket, and may be used to receive data on a socket whether or not it + * is connection-oriented. + * + * If 'from' is not NULL, and the underlying protocol provides the source + * address, this source address is filled in. The argument 'fromlen' is + * initialized to the size of the buffer associated with from, and + * modified on return to indicate the actual size of the address stored + * there. + * + * Parameters: + * psock A pointer to a NuttX-specific, internal socket structure + * buf Buffer to receive data + * len Length of buffer + * flags Receive flags + * from Address of source (may be NULL) + * fromlen The length of the address structure + * + * Returned Value: + * On success, returns the number of characters received. If no data is + * available to be received and the peer has performed an orderly shutdown, + * recv() will return 0. Otherwise, on errors, a negated errno value is + * returned (see recvfrom() for the list of appropriate error values). + * + ****************************************************************************/ + +ssize_t inet_recvfrom(FAR struct socket *psock, FAR void *buf, size_t len, + int flags, FAR struct sockaddr *from, + FAR socklen_t *fromlen); + #undef EXTERN #if defined(__cplusplus) } From 1e781ec4a7e395c5be95e33972d5e9da146f03f1 Mon Sep 17 00:00:00 2001 From: Gregory Nutt Date: Thu, 13 Jul 2017 00:48:11 +0000 Subject: [PATCH 12/18] configure.c edited online with Bitbucket --- tools/configure.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/configure.c b/tools/configure.c index dc1f639112c..3fcfbedf4bf 100644 --- a/tools/configure.c +++ b/tools/configure.c @@ -874,7 +874,7 @@ static void enable_feature(const char *destconfig, const char *varname) int ret; snprintf(g_buffer, BUFFER_SIZE, - "kconfig-tweak --file %s --disable %s", + "kconfig-tweak --file %s --enable %s", destconfig, varname); ret = system(g_buffer); From 3d4b4c208ed066e03bfd9afeb63cb5f5240665f0 Mon Sep 17 00:00:00 2001 From: Gregory Nutt Date: Thu, 13 Jul 2017 02:06:08 +0000 Subject: [PATCH 13/18] pkt_recvfrom.c edited online with Bitbucket --- net/pkt/pkt_recvfrom.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/pkt/pkt_recvfrom.c b/net/pkt/pkt_recvfrom.c index d4348f31b43..1f0c25c2331 100644 --- a/net/pkt/pkt_recvfrom.c +++ b/net/pkt/pkt_recvfrom.c @@ -398,7 +398,7 @@ ssize_t pkt_recvfrom(FAR struct socket *psock, FAR void *buf, size_t len, return -EINVAL; } - if (psock->s_type != -SOCK_RAW) + if (psock->s_type != SOCK_RAW) { nerr("ERROR: Unsupported socket type: %d\n", psock->s_type); ret = -ENOSYS; @@ -426,7 +426,7 @@ ssize_t pkt_recvfrom(FAR struct socket *psock, FAR void *buf, size_t len, /* TODO pkt_recvfrom_initialize() expects from to be of type sockaddr_in, but * in our case is sockaddr_ll */ - +) #if 0 ret = pkt_connect(conn, NULL); if (ret < 0) From 44ce0cce2f231f7c01342de29ad038b3b417a66f Mon Sep 17 00:00:00 2001 From: Gregory Nutt Date: Thu, 13 Jul 2017 02:08:40 +0000 Subject: [PATCH 14/18] pkt_recvfrom.c edited online with Bitbucket --- net/pkt/pkt_recvfrom.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/pkt/pkt_recvfrom.c b/net/pkt/pkt_recvfrom.c index 1f0c25c2331..e7075fccaa5 100644 --- a/net/pkt/pkt_recvfrom.c +++ b/net/pkt/pkt_recvfrom.c @@ -426,7 +426,7 @@ ssize_t pkt_recvfrom(FAR struct socket *psock, FAR void *buf, size_t len, /* TODO pkt_recvfrom_initialize() expects from to be of type sockaddr_in, but * in our case is sockaddr_ll */ -) + #if 0 ret = pkt_connect(conn, NULL); if (ret < 0) From 85b1ae4cf00355ec05e85fab51f124221b8d2194 Mon Sep 17 00:00:00 2001 From: Gregory Nutt Date: Thu, 13 Jul 2017 09:27:56 -0600 Subject: [PATCH 15/18] Socket interface: Added bind() and connect() interfaces. --- include/nuttx/net/net.h | 4 + net/local/local_sockif.c | 156 +++++++++- net/pkt/pkt_sockif.c | 122 +++++++- net/socket/Make.defs | 4 +- net/socket/bind.c | 233 +-------------- net/socket/connect.c | 561 +----------------------------------- net/socket/inet_connect.c | 567 +++++++++++++++++++++++++++++++++++++ net/socket/inet_recvfrom.c | 10 + net/socket/inet_sockif.c | 320 ++++++++++++++++++--- net/socket/net_clone.c | 2 - net/socket/recvfrom.c | 32 +-- net/socket/send.c | 18 +- net/socket/sendto.c | 30 +- net/socket/socket.c | 87 ------ net/socket/socket.h | 35 +++ 15 files changed, 1203 insertions(+), 978 deletions(-) create mode 100644 net/socket/inet_connect.c diff --git a/include/nuttx/net/net.h b/include/nuttx/net/net.h index 6d343cfd5f9..06ab7710806 100644 --- a/include/nuttx/net/net.h +++ b/include/nuttx/net/net.h @@ -103,6 +103,10 @@ struct socket; /* Forward reference */ struct sock_intf_s { CODE int (*si_setup)(FAR struct socket *psock, int protocol); + CODE int (*si_bind)(FAR struct socket *psock, + FAR const struct sockaddr *addr, socklen_t addrlen); + CODE int (*si_connect)(FAR struct socket *psock, + FAR const struct sockaddr *addr, socklen_t addrlen); CODE ssize_t (*si_send)(FAR struct socket *psock, FAR const void *buf, size_t len, int flags); CODE ssize_t (*si_sendto)(FAR struct socket *psock, FAR const void *buf, diff --git a/net/local/local_sockif.c b/net/local/local_sockif.c index 0baeab57e29..73113cccc2d 100644 --- a/net/local/local_sockif.c +++ b/net/local/local_sockif.c @@ -57,7 +57,11 @@ * Private Function Prototypes ****************************************************************************/ -static ssize_t local_setup(FAR struct socket *psock, int protocol); +static int local_setup(FAR struct socket *psock, int protocol); +static int local_bind(FAR struct socket *psock, + FAR const struct sockaddr *addr, socklen_t addrlen); +static int local_connect(FAR struct socket *psock, + FAR const struct sockaddr *addr, socklen_t addrlen); static ssize_t local_send(FAR struct socket *psock, FAR const void *buf, size_t len, int flags); static ssize_t local_sendto(FAR struct socket *psock, FAR const void *buf, @@ -71,6 +75,8 @@ static ssize_t local_sendto(FAR struct socket *psock, FAR const void *buf, const struct sock_intf_s g_local_sockif = { local_setup, /* si_setup */ + local_bind, /* si_bind */ + local_connect, /* si_connect */ local_send, /* si_send */ local_sendto, /* si_sendto */ local_recvfrom /* si_recvfrom */ @@ -171,6 +177,154 @@ static int local_setup(FAR struct socket *psock, int protocol) } } +/**************************************************************************** + * Name: local_bind + * + * Description: + * local_bind() gives the socket 'psock' the local address 'addr'. 'addr' + * is 'addrlen' bytes long. Traditionally, this is called "assigning a + * name to a socket." When a socket is created with socket(), it exists + * in a name space (address family) but has no name assigned. + * + * Parameters: + * psock Socket structure of the socket to bind + * addr Socket local address + * addrlen Length of 'addr' + * + * Returned Value: + * 0 on success; A negated errno value is returned on failure. See + * bind() for a list a appropriate error values. + * + ****************************************************************************/ + +static int local_bind(FAR struct socket *psock, + FAR const struct sockaddr *addr, socklen_t addrlen) +{ + int ret; + + /* Verify that a valid address has been provided */ + + if (addr->sa_family != AF_LOCAL || addrlen < sizeof(sa_family_t)) + { + nerr("ERROR: Invalid address length: %d < %d\n", + addrlen, sizeof(sa_family_t)); + return -EBADF; + } + + /* Perform the binding depending on the protocol type */ + + switch (psock->s_type) + { + /* Bind a local TCP/IP stream or datagram socket */ + +#if defined(ONFIG_NET_TCP) || defined(CONFIG_NET_UDP) +#ifdef CONFIG_NET_TCP + case SOCK_STREAM: +#endif +#ifdef CONFIG_NET_UDP + case SOCK_DGRAM: +#endif + { + /* Bind the Unix domain connection structure */ + + ret psock_local_bind(psock, addr, addrlen); + + /* Mark the socket bound */ + + if (ret >= 0) + { + psock->s_flags |= _SF_BOUND; + } + } + break; +#endif /* CONFIG_NET_TCP || CONFIG_NET_UDP*/ + + default: + ret = -EBADF; + break; + } + + return ret; +} + +/**************************************************************************** + * Name: local_connect + * + * Description: + * local_connect() connects the local socket referred to by the structure + * 'psock' to the address specified by 'addr'. The addrlen argument + * specifies the size of 'addr'. The format of the address in 'addr' is + * determined by the address space of the socket 'psock'. + * + * If the socket 'psock' is of type SOCK_DGRAM then 'addr' is the address + * to which datagrams are sent by default, and the only address from which + * datagrams are received. If the socket is of type SOCK_STREAM or + * SOCK_SEQPACKET, this call attempts to make a connection to the socket + * that is bound to the address specified by 'addr'. + * + * Generally, connection-based protocol sockets may successfully + * local_connect() only once; connectionless protocol sockets may use + * local_connect() multiple times to change their association. + * Connectionless sockets may dissolve the association by connecting to + * an address with the sa_family member of sockaddr set to AF_UNSPEC. + * + * Parameters: + * psock Pointer to a socket structure initialized by psock_socket() + * addr Server address (form depends on type of socket) + * addrlen Length of actual 'addr' + * + * Returned Value: + * 0 on success; a negated errno value on failue. See connect() for the + * list of appropriate errno values to be returned. + * + ****************************************************************************/ + +static int local_connect(FAR struct socket *psock, + FAR const struct sockaddr *addr, socklen_t addrlen) +{ + /* Verify that a valid address has been provided */ + + if (addr->sa_family != AF_LOCAL || addrlen < sizeof(sa_family_t)) + { + return -EBADF; + } + + /* Perform the connection depending on the protocol type */ + + switch (psock->s_type) + { +#ifdef CONFIG_NET_TCP + case SOCK_STREAM: + { + /* Verify that the socket is not already connected */ + + if (_SS_ISCONNECTED(psock->s_flags)) + { + return -EISCONN; + } + + /* It's not... Connect to the local Unix domain server */ + + return psock_local_connect(psock, addr); + } + break; +#endif /* CONFIG_NET_TCP */ + +#ifdef CONFIG_NET_UDP + case SOCK_DGRAM: + { + /* Perform the datagram connection logic */ + + return psock_local_connect(psock, addr); + } + break; +#endif /* CONFIG_NET_UDP */ + + default: + return -EBADF; + } +} + /**************************************************************************** * Name: local_send * diff --git a/net/pkt/pkt_sockif.c b/net/pkt/pkt_sockif.c index 9e3f0f6758b..72e7751f8bc 100644 --- a/net/pkt/pkt_sockif.c +++ b/net/pkt/pkt_sockif.c @@ -55,7 +55,11 @@ * Private Function Prototypes ****************************************************************************/ -static ssize_t pkt_setup(FAR struct socket *psock, int protocol); +static int pkt_setup(FAR struct socket *psock, int protocol); +static int pkt_bind(FAR struct socket *psock, + FAR const struct sockaddr *addr, socklen_t addrlen); +static int pkt_connect(FAR struct socket *psock, + FAR const struct sockaddr *addr, socklen_t addrlen); static ssize_t pkt_send(FAR struct socket *psock, FAR const void *buf, size_t len, int flags); static ssize_t pkt_sendto(FAR struct socket *psock, FAR const void *buf, @@ -69,6 +73,8 @@ static ssize_t pkt_sendto(FAR struct socket *psock, FAR const void *buf, const struct sock_intf_s g_pkt_sockif = { pkt_setup, /* si_setup */ + pkt_bind, /* si_bind */ + pkt_connect, /* si_connect */ pkt_send, /* si_send */ pkt_sendto, /* si_sendto */ pkt_recvfrom /* si_recvfrom */ @@ -151,6 +157,120 @@ static int pkt_setup(FAR struct socket *psock, int protocol) } } +/**************************************************************************** + * Name: pkt_connect + * + * Description: + * pkt_connect() connects the local socket referred to by the structure + * 'psock' to the address specified by 'addr'. The addrlen argument + * specifies the size of 'addr'. The format of the address in 'addr' is + * determined by the address space of the socket 'psock'. + * + * If the socket 'psock' is of type SOCK_DGRAM then 'addr' is the address + * to which datagrams are sent by default, and the only address from which + * datagrams are received. If the socket is of type SOCK_STREAM or + * SOCK_SEQPACKET, this call attempts to make a connection to the socket + * that is bound to the address specified by 'addr'. + * + * Generally, connection-based protocol sockets may successfully + * pkt_connect() only once; connectionless protocol sockets may use + * pkt_connect() multiple times to change their association. + * Connectionless sockets may dissolve the association by connecting to + * an address with the sa_family member of sockaddr set to AF_UNSPEC. + * + * Parameters: + * psock Pointer to a socket structure initialized by psock_socket() + * addr Server address (form depends on type of socket) + * addrlen Length of actual 'addr' + * + * Returned Value: + * 0 on success; a negated errno value on failue. See connect() for the + * list of appropriate errno values to be returned. + * + ****************************************************************************/ + +static int pkt_connect(FAR struct socket *psock, + FAR const struct sockaddr *addr, socklen_t addrlen) +{ + return -EAFNOSUPPORT; +} + +/**************************************************************************** + * Name: pkt_bind + * + * Description: + * pkt_bind() gives the socket 'psock' the local address 'addr'. 'addr' + * is 'addrlen' bytes long. Traditionally, this is called "assigning a + * name to a socket." When a socket is created with socket(), it exists + * in a name space (address family) but has no name assigned. + * + * Parameters: + * psock Socket structure of the socket to bind + * addr Socket local address + * addrlen Length of 'addr' + * + * Returned Value: + * 0 on success; A negated errno value is returned on failure. See + * bind() for a list a appropriate error values. + * + ****************************************************************************/ + +static int pkt_bind(FAR struct socket *psock, FAR const struct sockaddr *addr, + socklen_t addrlen) +{ +#if 0 + char hwaddr[6] = /* our MAC for debugging */ + { + 0x00, 0xa1, 0xb1, 0xc1, 0xd1, 0xe1 + }; +#endif + char hwaddr[6] = /* MAC from ifconfig */ + { + 0x00, 0xe0, 0xde, 0xad, 0xbe, 0xef + }; + int ifindex; + + /* Verify that a valid address has been provided */ + + if (addr->sa_family != AF_PACKET || addrlen < sizeof(struct sockaddr_ll) + { + nerr("ERROR: Invalid address length: %d < %d\n", + addrlen, sizeof(struct sockaddr_ll); + return -EBADF; + } + + /* Bind a raw socket to an network device. */ + + if (psock->s_type == SOCK_RAW) + { + FAR struct pkt_conn_s *conn = (FAR struct pkt_conn_s *)psock->s_conn; + + /* Look at the addr and identify network interface */ + + ifindex = addr->sll_ifindex; + +#if 0 + /* Get the MAC address of that interface */ + + memcpy(hwaddr, g_netdevices->d_mac.ether, 6); +#endif + + /* Put ifindex and mac address into connection */ + + conn->ifindex = ifindex; + memcpy(conn->lmac, hwaddr, 6); + + /* Mark the socket bound */ + + psock->s_flags |= _SF_BOUND; + return OK; + } + else + { + return -EBADF; + } +} + /**************************************************************************** * Name: pkt_send * diff --git a/net/socket/Make.defs b/net/socket/Make.defs index 105f70a6361..34deabf99eb 100644 --- a/net/socket/Make.defs +++ b/net/socket/Make.defs @@ -44,9 +44,9 @@ SOCK_CSRCS += net_dupsd2.c net_clone.c net_poll.c net_vfcntl.c SOCK_CSRCS += net_sockif.c ifeq ($(CONFIG_NET_IPv4),y) -SOCK_CSRCS += inet_sockif.c inet_recvfrom.c +SOCK_CSRCS += inet_sockif.c inet_recvfrom.c inet_connect.c else ifeq ($(CONFIG_NET_IPv6),y) -SOCK_CSRCS += inet_sockif.c inet_recvfrom.c +SOCK_CSRCS += inet_sockif.c inet_recvfrom.c inet_connect.c endif # TCP/IP support diff --git a/net/socket/bind.c b/net/socket/bind.c index a6671ec40d2..cf4c025f464 100644 --- a/net/socket/bind.c +++ b/net/socket/bind.c @@ -1,7 +1,7 @@ /**************************************************************************** * net/socket/bind.c * - * Copyright (C) 2007-2009, 2012, 2014-2016 Gregory Nutt. All rights reserved. + * Copyright (C) 2007-2009, 2012, 2014-2017 Gregory Nutt. All rights reserved. * Author: Gregory Nutt * * Redistribution and use in source and binary forms, with or without @@ -38,85 +38,23 @@ ****************************************************************************/ #include -#ifdef CONFIG_NET #include #include -#include -#include -#include -#include #include - -#ifdef CONFIG_NET_PKT -# include -#endif +#include +#include #include -#include #include "socket/socket.h" -#include "netdev/netdev.h" -#include "tcp/tcp.h" -#include "udp/udp.h" -#include "pkt/pkt.h" -#include "local/local.h" -#include "usrsock/usrsock.h" + +#ifdef CONFIG_NET /**************************************************************************** * Private Functions ****************************************************************************/ -/**************************************************************************** - * Name: pkt_bind - * - * Description: - * Bind a raw socket to an network device. - * - * Parameters: - * conn AF_PACKET connection structure - * addr Peer address information - * - * Returned Value: - * 0 on success; -1 on error with errno set appropriately - * - ****************************************************************************/ - -#ifdef CONFIG_NET_PKT -static int pkt_bind(FAR struct pkt_conn_s *conn, - FAR const struct sockaddr_ll *addr) -{ - int ifindex; -#if 0 - char hwaddr[6] = /* our MAC for debugging */ - { - 0x00, 0xa1, 0xb1, 0xc1, 0xd1, 0xe1 - }; -#endif - char hwaddr[6] = /* MAC from ifconfig */ - { - 0x00, 0xe0, 0xde, 0xad, 0xbe, 0xef - }; - - /* Look at the addr and identify network interface */ - - ifindex = addr->sll_ifindex; - -#if 0 - /* Get the MAC address of that interface */ - - memcpy(hwaddr, g_netdevices->d_mac.ether, 6); -#endif - - /* Put ifindex and mac address into connection */ - - conn->ifindex = ifindex; - memcpy(conn->lmac, hwaddr, 6); - - return OK; -} -#endif /* CONFIG_NET_PKT */ - /**************************************************************************** * Public Functions ****************************************************************************/ @@ -169,165 +107,10 @@ int psock_bind(FAR struct socket *psock, const struct sockaddr *addr, goto errout; } - /* Verify that a valid address has been provided */ + /* Let the address family's connect() method handle the operation */ - switch (addr->sa_family) - { -#ifdef CONFIG_NET_IPv4 - case AF_INET: - minlen = sizeof(struct sockaddr_in); - break; -#endif - -#ifdef CONFIG_NET_IPv6 - case AF_INET6: - minlen = sizeof(struct sockaddr_in6); - break; -#endif - -#ifdef CONFIG_NET_LOCAL - case AF_LOCAL: - minlen = sizeof(sa_family_t); - break; -#endif - -#ifdef CONFIG_NET_PKT - case AF_PACKET: - minlen = sizeof(struct sockaddr_ll); - break; -#endif - - default: - nerr("ERROR: Unrecognized address family: %d\n", addr->sa_family); - errcode = EAFNOSUPPORT; - goto errout; - } - - if (addrlen < minlen) - { - nerr("ERROR: Invalid address length: %d < %d\n", addrlen, minlen); - errcode = EBADF; - goto errout; - } - - /* Perform the binding depending on the protocol type */ - - switch (psock->s_type) - { -#ifdef CONFIG_NET_USRSOCK - case SOCK_USRSOCK_TYPE: - { - FAR struct usrsock_conn_s *conn = psock->s_conn; - - DEBUGASSERT(conn); - - /* Perform the usrsock bind operation */ - - ret = usrsock_bind(conn, addr, addrlen); - } - break; -#endif - -#ifdef CONFIG_NET_PKT - case SOCK_RAW: - ret = pkt_bind(psock->s_conn, lladdr); - break; -#endif - - /* Bind a stream socket which may either be TCP/IP or a local, Unix - * domain socket. - */ - -#if defined(CONFIG_NET_TCP) || defined(CONFIG_NET_LOCAL_STREAM) - case SOCK_STREAM: - { -#ifdef CONFIG_NET_LOCAL_STREAM -#ifdef CONFIG_NET_TCP - /* Is this a Unix domain socket? */ - - if (psock->s_domain == PF_LOCAL) -#endif - { - /* Bind the Unix domain connection structure */ - - ret = psock_local_bind(psock, addr, addrlen); - } -#endif /* CONFIG_NET_LOCAL_STREAM */ - -#ifdef CONFIG_NET_TCP -#ifdef CONFIG_NET_LOCAL_STREAM - else -#endif - { -#ifdef NET_TCP_HAVE_STACK - /* Bind the TCP/IP connection structure */ - - ret = tcp_bind(psock->s_conn, addr); -#else - ret = -ENOSYS; -#endif - } -#endif /* CONFIG_NET_TCP */ - - /* Mark the socket bound */ - - if (ret >= 0) - { - psock->s_flags |= _SF_BOUND; - } - } - break; -#endif /* CONFIG_NET_TCP || CONFIG_NET_LOCAL_STREAM */ - - /* Bind a datagram socket which may either be TCP/IP or a local, Unix - * domain socket. - */ - -#if defined(CONFIG_NET_UDP) || defined(CONFIG_NET_LOCAL_DGRAM) - case SOCK_DGRAM: - { -#ifdef CONFIG_NET_LOCAL_DGRAM -#ifdef CONFIG_NET_UDP - /* Is this a Unix domain socket? */ - - if (psock->s_domain == PF_LOCAL) -#endif - { - /* Bind the Unix domain connection structure */ - - ret = psock_local_bind(psock, addr, addrlen); - } -#endif /* CONFIG_NET_LOCAL_DGRAM */ - -#ifdef CONFIG_NET_UDP -#ifdef CONFIG_NET_LOCAL_DGRAM - else -#endif - { -#ifdef NET_UDP_HAVE_STACK - /* Bind the UDPP/IP connection structure */ - - ret = udp_bind(psock->s_conn, addr); -#else - ret = -ENOSYS; -#endif - } -#endif /* CONFIG_NET_UDP */ - - /* Mark the socket bound */ - - if (ret >= 0) - { - psock->s_flags |= _SF_BOUND; - } - } - break; -#endif /* CONFIG_NET_UDP || CONFIG_NET_LOCAL_DGRAM */ - - default: - errcode = EBADF; - goto errout; - } + DEBUGASSERT(psock->s_sockif != NULL && psock->s_sockif->si_bind != NULL); + ret = psock->s_sockif->si_bind(psock, addr, addrlen); /* Was the bind successful */ diff --git a/net/socket/connect.c b/net/socket/connect.c index dd3ee97e096..4a8fbe52e24 100644 --- a/net/socket/connect.c +++ b/net/socket/connect.c @@ -38,7 +38,6 @@ ****************************************************************************/ #include -#ifdef CONFIG_NET #include #include @@ -48,398 +47,17 @@ #include #include -#include - -#include #include #include -#include -#include -#include -#include "devif/devif.h" -#include "tcp/tcp.h" -#include "udp/udp.h" -#include "local/local.h" #include "socket/socket.h" -#include "usrsock/usrsock.h" -/**************************************************************************** - * Private Types - ****************************************************************************/ - -#ifdef NET_TCP_HAVE_STACK -struct tcp_connect_s -{ - FAR struct tcp_conn_s *tc_conn; /* Reference to TCP connection structure */ - FAR struct devif_callback_s *tc_cb; /* Reference to callback instance */ - FAR struct socket *tc_psock; /* The socket being connected */ - sem_t tc_sem; /* Semaphore signals recv completion */ - int tc_result; /* OK on success, otherwise a negated errno. */ -}; -#endif - -/**************************************************************************** - * Private Function Prototypes - ****************************************************************************/ - -#ifdef NET_TCP_HAVE_STACK -static inline int psock_setup_callbacks(FAR struct socket *psock, - FAR struct tcp_connect_s *pstate); -static void psock_teardown_callbacks(FAR struct tcp_connect_s *pstate, - int status); -static uint16_t psock_connect_interrupt(FAR struct net_driver_s *dev, - FAR void *pvconn, FAR void *pvpriv, - uint16_t flags); -static inline int psock_tcp_connect(FAR struct socket *psock, - FAR const struct sockaddr *addr); -#endif /* NET_TCP_HAVE_STACK */ - -/**************************************************************************** - * Private Functions - ****************************************************************************/ -/**************************************************************************** - * Name: psock_setup_callbacks - ****************************************************************************/ - -#ifdef NET_TCP_HAVE_STACK -static inline int psock_setup_callbacks(FAR struct socket *psock, - FAR struct tcp_connect_s *pstate) -{ - FAR struct tcp_conn_s *conn = psock->s_conn; - int ret = -EBUSY; - - /* Initialize the TCP state structure */ - - /* This semaphore is used for signaling and, hence, should not have - * priority inheritance enabled. - */ - - (void)sem_init(&pstate->tc_sem, 0, 0); /* Doesn't really fail */ - (void)sem_setprotocol(&pstate->tc_sem, SEM_PRIO_NONE); - - pstate->tc_conn = conn; - pstate->tc_psock = psock; - pstate->tc_result = -EAGAIN; - - /* Set up the callbacks in the connection */ - - pstate->tc_cb = tcp_callback_alloc(conn); - if (pstate->tc_cb) - { - /* Set up the connection "interrupt" handler */ - - pstate->tc_cb->flags = (TCP_NEWDATA | TCP_CLOSE | TCP_ABORT | - TCP_TIMEDOUT | TCP_CONNECTED | NETDEV_DOWN); - pstate->tc_cb->priv = (FAR void *)pstate; - pstate->tc_cb->event = psock_connect_interrupt; - ret = OK; - } - - return ret; -} -#endif /* NET_TCP_HAVE_STACK */ - -/**************************************************************************** - * Name: psock_teardown_callbacks - ****************************************************************************/ - -#ifdef NET_TCP_HAVE_STACK -static void psock_teardown_callbacks(FAR struct tcp_connect_s *pstate, - int status) -{ - FAR struct tcp_conn_s *conn = pstate->tc_conn; - - /* Make sure that no further interrupts are processed */ - - tcp_callback_free(conn, pstate->tc_cb); - pstate->tc_cb = NULL; - - /* If we successfully connected, we will continue to monitor the connection - * state via callbacks. - */ - - if (status < 0) - { - /* Failed to connect. Stop the connection event monitor */ - - net_stopmonitor(conn); - } -} -#endif /* NET_TCP_HAVE_STACK */ - -/**************************************************************************** - * Name: psock_connect_interrupt - * - * Description: - * This function is called from the interrupt level to perform the actual - * connection operation via by the lower, device interfacing layer. - * - * Parameters: - * dev The structure of the network driver that caused the interrupt - * pvconn The connection structure associated with the socket - * flags Set of events describing why the callback was invoked - * - * Returned Value: - * The new flags setting - * - * Assumptions: - * Running at the interrupt level - * - ****************************************************************************/ - -#ifdef NET_TCP_HAVE_STACK -static uint16_t psock_connect_interrupt(FAR struct net_driver_s *dev, - FAR void *pvconn, FAR void *pvpriv, - uint16_t flags) -{ - struct tcp_connect_s *pstate = (struct tcp_connect_s *)pvpriv; - - ninfo("flags: %04x\n", flags); - - /* 'priv' might be null in some race conditions (?) */ - - if (pstate) - { - /* The following errors should be detected here (someday) - * - * ECONNREFUSED - * No one listening on the remote address. - * ENETUNREACH - * Network is unreachable. - * ETIMEDOUT - * Timeout while attempting connection. The server may be too busy - * to accept new connections. - */ - - /* TCP_CLOSE: The remote host has closed the connection - * TCP_ABORT: The remote host has aborted the connection - */ - - if ((flags & (TCP_CLOSE | TCP_ABORT)) != 0) - { - /* Indicate that remote host refused the connection */ - - pstate->tc_result = -ECONNREFUSED; - } - - /* TCP_TIMEDOUT: Connection aborted due to too many retransmissions. */ - - else if ((flags & TCP_TIMEDOUT) != 0) - { - /* Indicate that the connection timedout?) */ - - pstate->tc_result = -ETIMEDOUT; - } - - else if ((flags & NETDEV_DOWN) != 0) - { - /* The network device went down. Indicate that the remote host - * is unreachable. - */ - - pstate->tc_result = -ENETUNREACH; - } - - /* TCP_CONNECTED: The socket is successfully connected */ - - else if ((flags & TCP_CONNECTED) != 0) - { - FAR struct socket *psock = pstate->tc_psock; - DEBUGASSERT(psock); - - /* Mark the connection bound and connected. NOTE this is - * is done here (vs. later) in order to avoid any race condition - * in the socket state. It is known to connected here and now, - * but not necessarily at any time later. - */ - - psock->s_flags |= (_SF_BOUND | _SF_CONNECTED); - - /* Indicate that the socket is no longer connected */ - - pstate->tc_result = OK; - } - - /* Otherwise, it is not an event of importance to us at the moment */ - - else - { - /* Drop data received in this state */ - - dev->d_len = 0; - return flags & ~TCP_NEWDATA; - } - - ninfo("Resuming: %d\n", pstate->tc_result); - - /* Stop further callbacks */ - - psock_teardown_callbacks(pstate, pstate->tc_result); - -#ifdef CONFIG_NET_MULTILINK - /* When we set up the connection structure, we did not know the size - * of the initial MSS. Now that the connection is associated with a - * network device, we now know the size of link layer header and can - * determine the correct initial MSS. - */ - - DEBUGASSERT(pstate->tc_conn); - -#ifdef CONFIG_NET_IPv4 -#ifdef CONFIG_NET_IPv6 - if (pstate->tc_conn->domain == PF_INET) -#endif - { - pstate->tc_conn->mss = TCP_IPv4_INITIAL_MSS(dev); - } -#endif /* CONFIG_NET_IPv4 */ - -#ifdef CONFIG_NET_IPv6 -#ifdef CONFIG_NET_IPv4 - else -#endif - { - pstate->tc_conn->mss = TCP_IPv6_INITIAL_MSS(dev); - } -#endif /* CONFIG_NET_IPv6 */ - -#ifdef CONFIG_NETDEV_MULTINIC - /* We now have to filter all outgoing transfers so that they use only - * the MSS of this device. - */ - - DEBUGASSERT(pstate->tc_conn->dev == NULL || - pstate->tc_conn->dev == dev); - pstate->tc_conn->dev = dev; - -#endif /* CONFIG_NETDEV_MULTINIC */ -#endif /* CONFIG_NET_MULTILINK */ - - /* Wake up the waiting thread */ - - sem_post(&pstate->tc_sem); - } - - return flags; -} -#endif /* NET_TCP_HAVE_STACK */ - -/**************************************************************************** - * Name: psock_tcp_connect - * - * Description: - * Perform a TCP connection - * - * Parameters: - * psock - A reference to the socket structure of the socket to be connected - * addr - The address of the remote server to connect to - * - * Returned Value: - * None - * - * Assumptions: - * Running at the interrupt level - * - ****************************************************************************/ - -#ifdef NET_TCP_HAVE_STACK -static inline int psock_tcp_connect(FAR struct socket *psock, - FAR const struct sockaddr *addr) -{ - struct tcp_connect_s state; - int ret = OK; - - /* Interrupts must be disabled through all of the following because - * we cannot allow the network callback to occur until we are completely - * setup. - */ - - net_lock(); - - /* Get the connection reference from the socket */ - - if (!psock->s_conn) /* Should always be non-NULL */ - { - ret = -EINVAL; - } - else - { - /* Perform the TCP connection operation */ - - ret = tcp_connect(psock->s_conn, addr); - } - - if (ret >= 0) - { - /* Set up the callbacks in the connection */ - - ret = psock_setup_callbacks(psock, &state); - if (ret >= 0) - { - /* Wait for either the connect to complete or for an error/timeout - * to occur. NOTES: (1) net_lockedwait will also terminate if a signal - * is received, (2) interrupts may be disabled! They will be re- - * enabled while the task sleeps and automatically re-disabled - * when the task restarts. - */ - - ret = net_lockedwait(&state.tc_sem); - - /* Uninitialize the state structure */ - - (void)sem_destroy(&state.tc_sem); - - /* If net_lockedwait failed, recover the negated error (probably -EINTR) */ - - if (ret < 0) - { - ret = -get_errno(); - } - else - { - /* If the wait succeeded, then get the new error value from - * the state structure - */ - - ret = state.tc_result; - } - - /* Make sure that no further interrupts are processed */ - - psock_teardown_callbacks(&state, ret); - } - - /* Check if the socket was successfully connected. */ - - if (ret >= 0) - { - /* Yes... Now that we are connected, we need to set up to monitor - * the state of the connection up the connection event monitor. - */ - - ret = net_startmonitor(psock); - if (ret < 0) - { - /* net_startmonitor() can only fail on certain race - * conditions where the connection was lost just before - * this function was called. That is not expected to - * happen in this context, but just in case... - */ - - net_lostconnection(psock, TCP_ABORT); - } - } - } - - net_unlock(); - return ret; -} -#endif /* NET_TCP_HAVE_STACK */ +#ifdef CONFIG_NET /**************************************************************************** * Public Functions ****************************************************************************/ + /**************************************************************************** * Name: psock_connect * @@ -512,12 +130,8 @@ static inline int psock_tcp_connect(FAR struct socket *psock, int psock_connect(FAR struct socket *psock, FAR const struct sockaddr *addr, socklen_t addrlen) { - FAR const struct sockaddr_in *inaddr = (FAR const struct sockaddr_in *)addr; -#if defined(CONFIG_NET_TCP) || defined(CONFIG_NET_UDP) || \ - defined(CONFIG_NET_LOCAL) || defined(CONFIG_NET_USRSOCK) - int ret; -#endif int errcode; + int ret; /* Treat as a cancellation point */ @@ -525,179 +139,22 @@ int psock_connect(FAR struct socket *psock, FAR const struct sockaddr *addr, /* Verify that the psock corresponds to valid, allocated socket */ - if (!psock || psock->s_crefs <= 0) + if (psock == NULL || psock->s_crefs <= 0) { errcode = EBADF; goto errout; } - /* Verify that a valid address has been provided */ + /* Let the address family's connect() method handle the operation */ - switch (inaddr->sin_family) + DEBUGASSERT(psock->s_sockif != NULL && psock->s_sockif->si_connect != NULL); + ret = psock->s_sockif->si_connect(psock, addr, addrlen); + if (ret < 0) { -#ifdef CONFIG_NET_IPv4 - case AF_INET: - { - if (addrlen < sizeof(struct sockaddr_in)) - { - errcode = EBADF; - goto errout; - } - } - break; -#endif - -#ifdef CONFIG_NET_IPv6 - case AF_INET6: - { - if (addrlen < sizeof(struct sockaddr_in6)) - { - errcode = EBADF; - goto errout; - } - } - break; -#endif - -#ifdef CONFIG_NET_LOCAL - case AF_LOCAL: - { - if (addrlen < sizeof(sa_family_t)) - { - errcode = EBADF; - goto errout; - } - } - break; -#endif - - default: -#ifdef CONFIG_NET_USRSOCK - if (psock->s_type == SOCK_USRSOCK_TYPE) - { - break; - } -#endif - - DEBUGPANIC(); - errcode = EAFNOSUPPORT; + errcode = -ret; goto errout; } - /* Perform the connection depending on the protocol type */ - - switch (psock->s_type) - { -#if defined(CONFIG_NET_TCP) || defined(CONFIG_NET_LOCAL_STREAM) - case SOCK_STREAM: - { - /* Verify that the socket is not already connected */ - - if (_SS_ISCONNECTED(psock->s_flags)) - { - errcode = EISCONN; - goto errout; - } - - /* It's not ... connect it */ - -#ifdef CONFIG_NET_LOCAL_STREAM -#ifdef CONFIG_NET_TCP - if (psock->s_domain == PF_LOCAL) -#endif - { - /* Connect to the local Unix domain server */ - - ret = psock_local_connect(psock, addr); - } -#endif /* CONFIG_NET_LOCAL_STREAM */ - -#ifdef CONFIG_NET_TCP -#ifdef CONFIG_NET_LOCAL_STREAM - else -#endif - { -#ifdef NET_TCP_HAVE_STACK - /* Connect the TCP/IP socket */ - - ret = psock_tcp_connect(psock, addr); -#else - ret = -ENOSYS; -#endif - } -#endif /* CONFIG_NET_TCP */ - - if (ret < 0) - { - errcode = -ret; - goto errout; - } - } - break; -#endif /* CONFIG_NET_TCP || CONFIG_NET_LOCAL_STREAM */ - -#if defined(CONFIG_NET_UDP) || defined(CONFIG_NET_LOCAL_DGRAM) - case SOCK_DGRAM: - { -#ifdef CONFIG_NET_LOCAL_DGRAM -#ifdef CONFIG_NET_UDP - if (psock->s_domain == PF_LOCAL) -#endif - { - /* Perform the datagram connection logic */ - - ret = psock_local_connect(psock, addr); - } -#endif /* CONFIG_NET_LOCAL_DGRAM */ - -#ifdef CONFIG_NET_UDP -#ifdef CONFIG_NET_LOCAL_DGRAM - else -#endif - { -#ifdef NET_UDP_HAVE_STACK - ret = udp_connect(psock->s_conn, addr); - if (ret < 0 || addr == NULL) - { - psock->s_flags &= ~_SF_CONNECTED; - } - else - { - psock->s_flags |= _SF_CONNECTED; - } -#else - ret = -ENOSYS; -#endif - } -#endif /* CONFIG_NET_UDP */ - - if (ret < 0) - { - errcode = -ret; - goto errout; - } - } - break; -#endif /* CONFIG_NET_UDP || CONFIG_NET_LOCAL_DGRAM */ - -#ifdef CONFIG_NET_USRSOCK - case SOCK_USRSOCK_TYPE: - { - ret = usrsock_connect(psock, addr, addrlen); - if (ret < 0) - { - errcode = -ret; - goto errout; - } - } - break; -#endif /* CONFIG_NET_USRSOCK */ - - default: - errcode = EBADF; - goto errout; - } - leave_cancellation_point(); return OK; diff --git a/net/socket/inet_connect.c b/net/socket/inet_connect.c new file mode 100644 index 00000000000..49aa17cd75b --- /dev/null +++ b/net/socket/inet_connect.c @@ -0,0 +1,567 @@ +/**************************************************************************** + * net/socket/inet_connect.c + * + * Copyright (C) 2007-2012, 2015-2017 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include "devif/devif.h" +#include "tcp/tcp.h" +#include "udp/udp.h" +#include "socket/socket.h" +#include "usrsock/usrsock.h" + +#ifdef CONFIG_NET + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +#ifdef NET_TCP_HAVE_STACK +struct tcp_connect_s +{ + FAR struct tcp_conn_s *tc_conn; /* Reference to TCP connection structure */ + FAR struct devif_callback_s *tc_cb; /* Reference to callback instance */ + FAR struct socket *tc_psock; /* The socket being connected */ + sem_t tc_sem; /* Semaphore signals recv completion */ + int tc_result; /* OK on success, otherwise a negated errno. */ +}; +#endif + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +#ifdef NET_TCP_HAVE_STACK +static inline int psock_setup_callbacks(FAR struct socket *psock, + FAR struct tcp_connect_s *pstate); +static void psock_teardown_callbacks(FAR struct tcp_connect_s *pstate, + int status); +static uint16_t psock_connect_interrupt(FAR struct net_driver_s *dev, + FAR void *pvconn, FAR void *pvpriv, + uint16_t flags); +static inline int psock_tcp_connect(FAR struct socket *psock, + FAR const struct sockaddr *addr); +#endif /* NET_TCP_HAVE_STACK */ + +/**************************************************************************** + * Private Functions + ****************************************************************************/ +/**************************************************************************** + * Name: psock_setup_callbacks + ****************************************************************************/ + +#ifdef NET_TCP_HAVE_STACK +static inline int psock_setup_callbacks(FAR struct socket *psock, + FAR struct tcp_connect_s *pstate) +{ + FAR struct tcp_conn_s *conn = psock->s_conn; + int ret = -EBUSY; + + /* Initialize the TCP state structure */ + + /* This semaphore is used for signaling and, hence, should not have + * priority inheritance enabled. + */ + + (void)sem_init(&pstate->tc_sem, 0, 0); /* Doesn't really fail */ + (void)sem_setprotocol(&pstate->tc_sem, SEM_PRIO_NONE); + + pstate->tc_conn = conn; + pstate->tc_psock = psock; + pstate->tc_result = -EAGAIN; + + /* Set up the callbacks in the connection */ + + pstate->tc_cb = tcp_callback_alloc(conn); + if (pstate->tc_cb) + { + /* Set up the connection "interrupt" handler */ + + pstate->tc_cb->flags = (TCP_NEWDATA | TCP_CLOSE | TCP_ABORT | + TCP_TIMEDOUT | TCP_CONNECTED | NETDEV_DOWN); + pstate->tc_cb->priv = (FAR void *)pstate; + pstate->tc_cb->event = psock_connect_interrupt; + ret = OK; + } + + return ret; +} +#endif /* NET_TCP_HAVE_STACK */ + +/**************************************************************************** + * Name: psock_teardown_callbacks + ****************************************************************************/ + +#ifdef NET_TCP_HAVE_STACK +static void psock_teardown_callbacks(FAR struct tcp_connect_s *pstate, + int status) +{ + FAR struct tcp_conn_s *conn = pstate->tc_conn; + + /* Make sure that no further interrupts are processed */ + + tcp_callback_free(conn, pstate->tc_cb); + pstate->tc_cb = NULL; + + /* If we successfully connected, we will continue to monitor the connection + * state via callbacks. + */ + + if (status < 0) + { + /* Failed to connect. Stop the connection event monitor */ + + net_stopmonitor(conn); + } +} +#endif /* NET_TCP_HAVE_STACK */ + +/**************************************************************************** + * Name: psock_connect_interrupt + * + * Description: + * This function is called from the interrupt level to perform the actual + * connection operation via by the lower, device interfacing layer. + * + * Parameters: + * dev The structure of the network driver that caused the interrupt + * pvconn The connection structure associated with the socket + * flags Set of events describing why the callback was invoked + * + * Returned Value: + * The new flags setting + * + * Assumptions: + * Running at the interrupt level + * + ****************************************************************************/ + +#ifdef NET_TCP_HAVE_STACK +static uint16_t psock_connect_interrupt(FAR struct net_driver_s *dev, + FAR void *pvconn, FAR void *pvpriv, + uint16_t flags) +{ + struct tcp_connect_s *pstate = (struct tcp_connect_s *)pvpriv; + + ninfo("flags: %04x\n", flags); + + /* 'priv' might be null in some race conditions (?) */ + + if (pstate) + { + /* The following errors should be detected here (someday) + * + * ECONNREFUSED + * No one listening on the remote address. + * ENETUNREACH + * Network is unreachable. + * ETIMEDOUT + * Timeout while attempting connection. The server may be too busy + * to accept new connections. + */ + + /* TCP_CLOSE: The remote host has closed the connection + * TCP_ABORT: The remote host has aborted the connection + */ + + if ((flags & (TCP_CLOSE | TCP_ABORT)) != 0) + { + /* Indicate that remote host refused the connection */ + + pstate->tc_result = -ECONNREFUSED; + } + + /* TCP_TIMEDOUT: Connection aborted due to too many retransmissions. */ + + else if ((flags & TCP_TIMEDOUT) != 0) + { + /* Indicate that the connection timedout?) */ + + pstate->tc_result = -ETIMEDOUT; + } + + else if ((flags & NETDEV_DOWN) != 0) + { + /* The network device went down. Indicate that the remote host + * is unreachable. + */ + + pstate->tc_result = -ENETUNREACH; + } + + /* TCP_CONNECTED: The socket is successfully connected */ + + else if ((flags & TCP_CONNECTED) != 0) + { + FAR struct socket *psock = pstate->tc_psock; + DEBUGASSERT(psock); + + /* Mark the connection bound and connected. NOTE this is + * is done here (vs. later) in order to avoid any race condition + * in the socket state. It is known to connected here and now, + * but not necessarily at any time later. + */ + + psock->s_flags |= (_SF_BOUND | _SF_CONNECTED); + + /* Indicate that the socket is no longer connected */ + + pstate->tc_result = OK; + } + + /* Otherwise, it is not an event of importance to us at the moment */ + + else + { + /* Drop data received in this state */ + + dev->d_len = 0; + return flags & ~TCP_NEWDATA; + } + + ninfo("Resuming: %d\n", pstate->tc_result); + + /* Stop further callbacks */ + + psock_teardown_callbacks(pstate, pstate->tc_result); + +#ifdef CONFIG_NET_MULTILINK + /* When we set up the connection structure, we did not know the size + * of the initial MSS. Now that the connection is associated with a + * network device, we now know the size of link layer header and can + * determine the correct initial MSS. + */ + + DEBUGASSERT(pstate->tc_conn); + +#ifdef CONFIG_NET_IPv4 +#ifdef CONFIG_NET_IPv6 + if (pstate->tc_conn->domain == PF_INET) +#endif + { + pstate->tc_conn->mss = TCP_IPv4_INITIAL_MSS(dev); + } +#endif /* CONFIG_NET_IPv4 */ + +#ifdef CONFIG_NET_IPv6 +#ifdef CONFIG_NET_IPv4 + else +#endif + { + pstate->tc_conn->mss = TCP_IPv6_INITIAL_MSS(dev); + } +#endif /* CONFIG_NET_IPv6 */ + +#ifdef CONFIG_NETDEV_MULTINIC + /* We now have to filter all outgoing transfers so that they use only + * the MSS of this device. + */ + + DEBUGASSERT(pstate->tc_conn->dev == NULL || + pstate->tc_conn->dev == dev); + pstate->tc_conn->dev = dev; + +#endif /* CONFIG_NETDEV_MULTINIC */ +#endif /* CONFIG_NET_MULTILINK */ + + /* Wake up the waiting thread */ + + sem_post(&pstate->tc_sem); + } + + return flags; +} +#endif /* NET_TCP_HAVE_STACK */ + +/**************************************************************************** + * Name: psock_tcp_connect + * + * Description: + * Perform a TCP connection + * + * Parameters: + * psock - A reference to the socket structure of the socket to be connected + * addr - The address of the remote server to connect to + * + * Returned Value: + * None + * + * Assumptions: + * Running at the interrupt level + * + ****************************************************************************/ + +#ifdef NET_TCP_HAVE_STACK +static inline int psock_tcp_connect(FAR struct socket *psock, + FAR const struct sockaddr *addr) +{ + struct tcp_connect_s state; + int ret = OK; + + /* Interrupts must be disabled through all of the following because + * we cannot allow the network callback to occur until we are completely + * setup. + */ + + net_lock(); + + /* Get the connection reference from the socket */ + + if (!psock->s_conn) /* Should always be non-NULL */ + { + ret = -EINVAL; + } + else + { + /* Perform the TCP connection operation */ + + ret = tcp_connect(psock->s_conn, addr); + } + + if (ret >= 0) + { + /* Set up the callbacks in the connection */ + + ret = psock_setup_callbacks(psock, &state); + if (ret >= 0) + { + /* Wait for either the connect to complete or for an error/timeout + * to occur. NOTES: (1) net_lockedwait will also terminate if a signal + * is received, (2) interrupts may be disabled! They will be re- + * enabled while the task sleeps and automatically re-disabled + * when the task restarts. + */ + + ret = net_lockedwait(&state.tc_sem); + + /* Uninitialize the state structure */ + + (void)sem_destroy(&state.tc_sem); + + /* If net_lockedwait failed, recover the negated error (probably -EINTR) */ + + if (ret < 0) + { + ret = -get_errno(); + } + else + { + /* If the wait succeeded, then get the new error value from + * the state structure + */ + + ret = state.tc_result; + } + + /* Make sure that no further interrupts are processed */ + + psock_teardown_callbacks(&state, ret); + } + + /* Check if the socket was successfully connected. */ + + if (ret >= 0) + { + /* Yes... Now that we are connected, we need to set up to monitor + * the state of the connection up the connection event monitor. + */ + + ret = net_startmonitor(psock); + if (ret < 0) + { + /* net_startmonitor() can only fail on certain race + * conditions where the connection was lost just before + * this function was called. That is not expected to + * happen in this context, but just in case... + */ + + net_lostconnection(psock, TCP_ABORT); + } + } + } + + net_unlock(); + return ret; +} +#endif /* NET_TCP_HAVE_STACK */ + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: inet_connect + * + * Description: + * inet_connect() connects the local socket referred to by the structure + * 'psock' to the address specified by 'addr'. The addrlen argument + * specifies the size of 'addr'. The format of the address in 'addr' is + * determined by the address space of the socket 'psock'. + * + * If the socket 'psock' is of type SOCK_DGRAM then 'addr' is the address + * to which datagrams are sent by default, and the only address from which + * datagrams are received. If the socket is of type SOCK_STREAM or + * SOCK_SEQPACKET, this call attempts to make a connection to the socket + * that is bound to the address specified by 'addr'. + * + * Generally, connection-based protocol sockets may successfully + * inet_connect() only once; connectionless protocol sockets may use + * inet_connect() multiple times to change their association. + * Connectionless sockets may dissolve the association by connecting to + * an address with the sa_family member of sockaddr set to AF_UNSPEC. + * + * Parameters: + * psock Pointer to a socket structure initialized by psock_socket() + * addr Server address (form depends on type of socket) + * addrlen Length of actual 'addr' + * + * Returned Value: + * 0 on success; a negated errno value on failue. See connect() for the + * list of appropriate errno values to be returned. + * + ****************************************************************************/ + +int inet_connect(FAR struct socket *psock, FAR const struct sockaddr *addr, + socklen_t addrlen) +{ + FAR const struct sockaddr_in *inaddr = (FAR const struct sockaddr_in *)addr; + + /* Verify that a valid address has been provided */ + + switch (inaddr->sin_family) + { +#ifdef CONFIG_NET_IPv4 + case AF_INET: + { + if (addrlen < sizeof(struct sockaddr_in)) + { + return -EBADF; + } + } + break; +#endif + +#ifdef CONFIG_NET_IPv6 + case AF_INET6: + { + if (addrlen < sizeof(struct sockaddr_in6)) + { + return -EBADF; + } + } + break; +#endif + + default: +#ifdef CONFIG_NET_USRSOCK + if (psock->s_type == SOCK_USRSOCK_TYPE) + { + break; + } +#endif + + DEBUGPANIC(); + return -EAFNOSUPPORT; + } + + /* Perform the connection depending on the protocol type */ + + switch (psock->s_type) + { +#ifdef CONFIG_NET_TCP + case SOCK_STREAM: + { + /* Verify that the socket is not already connected */ + + if (_SS_ISCONNECTED(psock->s_flags)) + { + return -EISCONN; + } + + /* It's not ... Connect the TCP/IP socket */ + + return psock_tcp_connect(psock, addr); + } +#endif /* CONFIG_NET_TCP */ + +#ifdef CONFIG_NET_UDP + case SOCK_DGRAM: + { + int ret = udp_connect(psock->s_conn, addr); + if (ret < 0 || addr == NULL) + { + psock->s_flags &= ~_SF_CONNECTED; + } + else + { + psock->s_flags |= _SF_CONNECTED; + } + + return ret; + } +#endif /* CONFIG_NET_UDP */ + +#ifdef CONFIG_NET_USRSOCK + case SOCK_USRSOCK_TYPE: + { + return usrsock_connect(psock, addr, addrlen); + } +#endif /* CONFIG_NET_USRSOCK */ + + default: + return -EBADF; + } +} + +#endif /* CONFIG_NET */ diff --git a/net/socket/inet_recvfrom.c b/net/socket/inet_recvfrom.c index b38c15e82d2..3e29b93e4ba 100644 --- a/net/socket/inet_recvfrom.c +++ b/net/socket/inet_recvfrom.c @@ -1650,6 +1650,16 @@ ssize_t inet_recvfrom(FAR struct socket *psock, FAR void *buf, size_t len, break; #endif /* CONFIG_NET_UDP || CONFIG_NET_LOCAL_DGRAM */ +#ifdef CONFIG_NET_USRSOCK + case SOCK_USRSOCK_TYPE: + { + /* Perform the usrsock recvfrom operation */ + + ret = usrsock_recvfrom(psock, buf, len, from, fromlen); + } + break; +#endif + default: { nerr("ERROR: Unsupported socket type: %d\n", psock->s_type); diff --git a/net/socket/inet_sockif.c b/net/socket/inet_sockif.c index 4866afd6959..4574f2a1e40 100644 --- a/net/socket/inet_sockif.c +++ b/net/socket/inet_sockif.c @@ -49,6 +49,7 @@ #include "tcp/tcp.h" #include "udp/udp.h" +#include "usrsock/usrsock.h" #include "socket/socket.h" #if defined(CONFIG_NET_IPv4) || defined(CONFIG_NET_IPv6) @@ -57,12 +58,14 @@ * Private Function Prototypes ****************************************************************************/ -static int inet_setup(FAR struct socket *psock, int protocol); +static int inet_setup(FAR struct socket *psock, int protocol); +static int inet_bind(FAR struct socket *psock, + FAR const struct sockaddr *addr, socklen_t addrlen); static ssize_t inet_send(FAR struct socket *psock, FAR const void *buf, - size_t len, int flags); + size_t len, int flags); static ssize_t inet_sendto(FAR struct socket *psock, FAR const void *buf, - size_t len, int flags, FAR const struct sockaddr *to, - socklen_t tolen); + size_t len, int flags, FAR const struct sockaddr *to, + socklen_t tolen); /**************************************************************************** * Public Data @@ -71,6 +74,8 @@ static ssize_t inet_sendto(FAR struct socket *psock, FAR const void *buf, const struct sock_intf_s g_inet_sockif = { inet_setup, /* si_setup */ + inet_bind, /* si_bind */ + inet_connect, /* si_connect */ inet_send, /* si_send */ inet_sendto, /* si_sendto */ inet_recvfrom /* si_recvfrom */ @@ -152,6 +157,65 @@ static int inet_udp_alloc(FAR struct socket *psock) } #endif /* NET_UDP_HAVE_STACK */ +/**************************************************************************** + * Name: usrsock_socket_setup + * + * Description: + * Special socket setup may be required by user sockets. + * + * Parameters: + * domain (see sys/socket.h) + * type (see sys/socket.h) + * protocol (see sys/socket.h) + * psock A pointer to a user allocated socket structure to be initialized. + * + * Returned Value: + * 0 on success; a negated errno value is returned on failure. + * + ****************************************************************************/ + +#ifdef CONFIG_NET_USRSOCK +static int usrsock_socket_setup(int domain, int type, int protocol, + FAR struct socket *psock) +{ + switch (domain) + { + default: + return OK; + + case PF_INET: + case PF_INET6: + { +#ifndef CONFIG_NET_USRSOCK_UDP + if (type == SOCK_DGRAM) + { + return OK; + } +#endif +#ifndef CONFIG_NET_USRSOCK_TCP + if (type == SOCK_STREAM) + { + return OK; + } +#endif + psock->s_type = PF_UNSPEC; + psock->s_conn = NULL; + + /* Let the user socket logic handle the setup... + * + * A return value of zero means that the operation was + * successfully handled by usrsock. A negative value means that + * an error occurred. The special error value -ENETDOWN means + * that usrsock daemon is not running. The caller should attempt + * to open socket with kernel networking stack in this case. + */ + + return usrsock_socket(domain, type, protocol, psock); + } + } +} +#endif /* CONFIG_NET_USRSOCK */ + /**************************************************************************** * Name: inet_setup * @@ -175,6 +239,27 @@ static int inet_udp_alloc(FAR struct socket *psock) static int inet_setup(FAR struct socket *psock, int protocol) { +#ifdef CONFIG_NET_USRSOCK + /* Handle speical setup for user INET sockets */ + + ret = usrsock_socket_setup(domain, type, protocol, psock); + if (ret < 0) + { + if (ret = -ENETDOWN) + { + /* -ENETDOWN means that usrsock daemon is not running. Attempt to + * open socket with kernel networking stack. + */ + + warn("WARNING: usrsock daemon is not running\n"); + } + else + { + return ret; + } + } +#endif /* CONFIG_NET_USRSOCK */ + /* Allocate the appropriate connection structure. This reserves the * the connection structure is is unallocated at this point. It will * not actually be initialized until the socket is connected. @@ -197,6 +282,7 @@ static int inet_setup(FAR struct socket *psock, int protocol) return inet_tcp_alloc(psock); #else + warning("WARNING: SOCK_STREAM disabled\n"); return = -ENETDOWN; #endif #endif /* CONFIG_NET_TCP */ @@ -214,6 +300,7 @@ static int inet_setup(FAR struct socket *psock, int protocol) return inet_udp_alloc(psock); #else + warning("WARNING: SOCK_DGRAM disabled\n"); return -ENETDOWN; #endif #endif /* CONFIG_NET_UDP */ @@ -224,6 +311,130 @@ static int inet_setup(FAR struct socket *psock, int protocol) } } +/**************************************************************************** + * Name: inet_bind + * + * Description: + * inet_bind() gives the socket 'psock' the local address 'addr'. 'addr' + * is 'addrlen' bytes long. Traditionally, this is called "assigning a + * name to a socket." When a socket is created with socket(), it exists + * in a name space (address family) but has no name assigned. + * + * Parameters: + * psock Socket structure of the socket to bind + * addr Socket local address + * addrlen Length of 'addr' + * + * Returned Value: + * 0 on success; A negated errno value is returned on failure. See + * bind() for a list a appropriate error values. + * + ****************************************************************************/ + +static int inet_bind(FAR struct socket *psock, + FAR const struct sockaddr *addr, socklen_t addrlen) +{ + int minlen; + int ret; + + /* Verify that a valid address has been provided */ + + switch (addr->sa_family) + { +#ifdef CONFIG_NET_IPv4 + case AF_INET: + minlen = sizeof(struct sockaddr_in); + break; +#endif + +#ifdef CONFIG_NET_IPv6 + case AF_INET6: + minlen = sizeof(struct sockaddr_in6); + break; +#endif + + default: + nerr("ERROR: Unrecognized address family: %d\n", addr->sa_family); + return -EAFNOSUPPORT; + } + + if (addrlen < minlen) + { + nerr("ERROR: Invalid address length: %d < %d\n", addrlen, minlen); + return -EBADF; + } + + /* Perform the binding depending on the protocol type */ + + switch (psock->s_type) + { +#ifdef CONFIG_NET_USRSOCK + case SOCK_USRSOCK_TYPE: + { + FAR struct usrsock_conn_s *conn = psock->s_conn; + + DEBUGASSERT(conn != NULL); + + /* Perform the usrsock bind operation */ + + ret = usrsock_bind(conn, addr, addrlen); + } + break; +#endif + +#ifdef CONFIG_NET_TCP + case SOCK_STREAM: + { +#ifdef NET_TCP_HAVE_STACK + /* Bind a TCP/IP stream socket. */ + + ret = tcp_bind(psock->s_conn, addr); + + /* Mark the socket bound */ + + if (ret >= 0) + { + psock->s_flags |= _SF_BOUND; + } +#else + nwarn("WARNING: TCP/IP stack is not available in this configuration\n"); + return -ENOSYS; +#endif + } + break; +#endif /* CONFIG_NET_TCP */ + +#ifdef CONFIG_NET_UDP + case SOCK_DGRAM: + { +#ifdef NET_UDP_HAVE_STACK + /* Bind a UDP/IP datagram socket */ + + ret = udp_bind(psock->s_conn, addr); + + /* Mark the socket bound */ + + if (ret >= 0) + { + psock->s_flags |= _SF_BOUND; + } +#else + nwarn("WARNING: UDP stack is not available in this configuration\n"); + ret = -ENOSYS; +#endif + } + break; +#endif /* CONFIG_NET_UDP */ + + default: + nerr("ERROR: Unsupported socket type: %d\n", psock->s_type); + ret = -EBADF; + break; + } + + return ret; +} + /**************************************************************************** * Name: inet_send * @@ -303,6 +514,16 @@ static ssize_t inet_send(FAR struct socket *psock, FAR const void *buf, break; #endif /* CONFIG_NET_UDP */ + /* Special case user sockets */ + +#ifdef CONFIG_NET_USRSOCK + case SOCK_USRSOCK_TYPE: + { + ret = usrsock_sendto(psock, buf, len, NULL, 0); + } + break; +#endif /*CONFIG_NET_USRSOCK*/ + default: { /* EDESTADDRREQ. Signifies that the socket is not connection-mode @@ -347,71 +568,78 @@ static ssize_t inet_sendto(FAR struct socket *psock, FAR const void *buf, socklen_t minlen; ssize_t nsent; - /* Verify that a valid address has been provided */ - - switch (to->sa_family) +#ifdef CONFIG_NET_USRSOCK + if (psock->s_type == SOCK_USRSOCK_TYPE) { + /* Perform the usrsock sendto operation */ + + nsent = usrsock_sendto(psock, buf, len, to, tolen); + } + else +#endif + { + /* Verify that a valid address has been provided */ + + switch (to->sa_family) + { #ifdef CONFIG_NET_IPv4 - case AF_INET: - minlen = sizeof(struct sockaddr_in); - break; + case AF_INET: + minlen = sizeof(struct sockaddr_in); + break; #endif #ifdef CONFIG_NET_IPv6 - case AF_INET6: - minlen = sizeof(struct sockaddr_in6); - break; + case AF_INET6: + minlen = sizeof(struct sockaddr_in6); + break; #endif -#ifdef CONFIG_NET_LOCAL_DGRAM - case AF_LOCAL: - minlen = sizeof(sa_family_t); - break; -#endif + default: + nerr("ERROR: Unrecognized address family: %d\n", to->sa_family); + return -EAFNOSUPPORT; + } - default: - nerr("ERROR: Unrecognized address family: %d\n", to->sa_family); - return -EAFNOSUPPORT; - } - - if (tolen < minlen) - { - nerr("ERROR: Invalid address length: %d < %d\n", tolen, minlen); - return -EBADF; - } + if (tolen < minlen) + { + nerr("ERROR: Invalid address length: %d < %d\n", tolen, minlen); + return -EBADF; + } #ifdef CONFIG_NET_UDP - /* If this is a connected socket, then return EISCONN */ + /* If this is a connected socket, then return EISCONN */ - if (psock->s_type != SOCK_DGRAM) - { - nerr("ERROR: Connected socket\n"); - return -EBADF; - } + if (psock->s_type != SOCK_DGRAM) + { + nerr("ERROR: Connected socket\n"); + return -EBADF; + } - /* Now handle the INET sendto() operation */ + /* Now handle the INET sendto() operation */ #if defined(CONFIG_NET_6LOWPAN) - /* Try 6LoWPAN UDP packet sendto() */ + /* Try 6LoWPAN UDP packet sendto() */ - nsent = psock_6lowpan_udp_sendto(psock, buf, len, flags, to, tolen); + nsent = psock_6lowpan_udp_sendto(psock, buf, len, flags, to, tolen); #if defined(CONFIG_NETDEV_MULTINIC) && defined(NET_UDP_HAVE_STACK) - if (nsent < 0) - { - /* UDP/IP packet sendto */ + if (nsent < 0) + { + /* UDP/IP packet sendto */ - nsent = psock_udp_sendto(psock, buf, len, flags, to, tolen); - } + nsent = psock_udp_sendto(psock, buf, len, flags, to, tolen); + } #endif /* CONFIG_NETDEV_MULTINIC && NET_UDP_HAVE_STACK */ #elif defined(NET_UDP_HAVE_STACK) - nsent = psock_udp_sendto(psock, buf, len, flags, to, tolen); + nsent = psock_udp_sendto(psock, buf, len, flags, to, tolen); #else - nsent = -ENOSYS; + nwarn("WARNING: UDP not available in this configuiration\n") + nsent = -ENOSYS; #endif /* CONFIG_NET_6LOWPAN */ #else - nsent = -EISCONN; + nwarn("WARNING: UDP not enabled in this configuiration\n") + nsent = -EISCONN; #endif /* CONFIG_NET_UDP */ + } return nsent; } @@ -421,7 +649,7 @@ static ssize_t inet_sendto(FAR struct socket *psock, FAR const void *buf, ****************************************************************************/ /**************************************************************************** - * Name: + * Name: * * Description: * diff --git a/net/socket/net_clone.c b/net/socket/net_clone.c index c134116d7f4..5c417027747 100644 --- a/net/socket/net_clone.c +++ b/net/socket/net_clone.c @@ -155,5 +155,3 @@ int net_clone(FAR struct socket *psock1, FAR struct socket *psock2) } #endif /* CONFIG_NET */ - - diff --git a/net/socket/recvfrom.c b/net/socket/recvfrom.c index 9b576cbcb85..1144bf6d6fe 100644 --- a/net/socket/recvfrom.c +++ b/net/socket/recvfrom.c @@ -46,7 +46,6 @@ #include #include "socket/socket.h" -#include "usrsock/usrsock.h" #ifdef CONFIG_NET @@ -147,33 +146,14 @@ ssize_t psock_recvfrom(FAR struct socket *psock, FAR void *buf, size_t len, psock->s_flags = _SS_SETSTATE(psock->s_flags, _SF_RECV); -#ifdef CONFIG_NET_USRSOCK - if (psock->s_type == SOCK_USRSOCK_TYPE) - { - /* Perform the usrsock recvfrom operation */ + /* Let logic specific to this address family handle the recvfrom() + * operation. + */ - ret = usrsock_recvfrom(psock, buf, len, from, fromlen); - } - else -#endif - { - /* Let logic specific to this address family handle the recvfrom() - * operation. - */ + DEBUGASSERT(psock->s_sockif != NULL && + psock->s_sockif->si_recvfrom != NULL); - DEBUGASSERT(psock->s_sockif != NULL && - psock->s_sockif->si_recvfrom != NULL); - - if (psock->s_sockif->si_recvfrom != NULL) - { - ret = psock->s_sockif->si_recvfrom(psock, buf, len, flags, - from, fromlen); - } - else - { - ret = -ENOSYS; - } - } + ret = psock->s_sockif->si_recvfrom(psock, buf, len, flags, from, fromlen); /* Set the socket state to idle */ diff --git a/net/socket/send.c b/net/socket/send.c index 1b8a39d47c3..9af7e88cdb7 100644 --- a/net/socket/send.c +++ b/net/socket/send.c @@ -53,7 +53,6 @@ #include "sixlowpan/sixlowpan.h" #include "local/local.h" #include "socket/socket.h" -#include "usrsock/usrsock.h" /**************************************************************************** * Public Functions @@ -134,21 +133,10 @@ ssize_t psock_send(FAR struct socket *psock, FAR const void *buf, size_t len, (void)enter_cancellation_point(); - /* Special case user sockets */ + /* Let the address family's send() method handle the operation */ -#ifdef CONFIG_NET_USRSOCK - if (psock->s_type SOCK_USRSOCK_TYPE) - { - ret = usrsock_sendto(psock, buf, len, NULL, 0); - } - else -#endif /*CONFIG_NET_USRSOCK*/ - { - /* Let the address family's send() method handle the operation */ - - DEBUGASSERT(psock->s_sockif != NULL && psock->s_sockif->si_send != NULL); - ret = psock->s_sockif->si_send(psock, buf, len, flags); - } + DEBUGASSERT(psock->s_sockif != NULL && psock->s_sockif->si_send != NULL); + ret = psock->s_sockif->si_send(psock, buf, len, flags); leave_cancellation_point(); if (ret < 0) diff --git a/net/socket/sendto.c b/net/socket/sendto.c index 6a7dd9a51aa..6ddf2a59589 100644 --- a/net/socket/sendto.c +++ b/net/socket/sendto.c @@ -53,7 +53,6 @@ #include "sixlowpan/sixlowpan.h" #include "local/local.h" #include "socket/socket.h" -#include "usrsock/usrsock.h" /**************************************************************************** * Public Functions @@ -149,30 +148,19 @@ ssize_t psock_sendto(FAR struct socket *psock, FAR const void *buf, #endif } -#ifdef CONFIG_NET_USRSOCK - if (psock->s_type == SOCK_USRSOCK_TYPE) + /* Verify that the psock corresponds to valid, allocated socket */ + + if (!psock || psock->s_crefs <= 0) { - /* Perform the usrsock sendto operation */ - - nsent = usrsock_sendto(psock, buf, len, to, tolen); + nerr("ERROR: Invalid socket\n"); + errcode = EBADF; + goto errout; } - else -#endif - { - /* Verify that the psock corresponds to valid, allocated socket */ - if (!psock || psock->s_crefs <= 0) - { - nerr("ERROR: Invalid socket\n"); - errcode = EBADF; - goto errout; - } + /* Let the address family's send() method handle the operation */ - /* Let the address family's send() method handle the operation */ - - DEBUGASSERT(psock->s_sockif != NULL && psock->s_sockif->si_send != NULL); - nsent = psock->s_sockif->si_send(psock, buf, len, flags); - } + DEBUGASSERT(psock->s_sockif != NULL && psock->s_sockif->si_send != NULL); + nsent = psock->s_sockif->si_send(psock, buf, len, flags); /* Check if the domain-specific sendto() logic failed */ diff --git a/net/socket/socket.c b/net/socket/socket.c index 8c63c8095ff..7265e5cce9c 100644 --- a/net/socket/socket.c +++ b/net/socket/socket.c @@ -44,87 +44,10 @@ #include #include -#include "usrsock/usrsock.h" #include "socket/socket.h" #ifdef CONFIG_NET -/**************************************************************************** - * Private Functions - ****************************************************************************/ - -/**************************************************************************** - * Name: usrsock_socket_setup - * - * Description: - * Special socket setup may be required by user sockets. - * - * Parameters: - * domain (see sys/socket.h) - * type (see sys/socket.h) - * protocol (see sys/socket.h) - * psock A pointer to a user allocated socket structure to be initialized. - * - * Returned Value: - * 0 on success; -1 on error with errno set appropriately - * - ****************************************************************************/ - -#ifdef CONFIG_NET_USRSOCK -static int usrsock_socket_setup(int domain, int type, int protocol, - FAR struct socket *psock) -{ - int errcode; - int ret; - - switch (domain) - { - default: - break; - - case PF_INET: - case PF_INET6: - { -#ifndef CONFIG_NET_USRSOCK_UDP - if (type == SOCK_DGRAM) - { - break; - } -#endif -#ifndef CONFIG_NET_USRSOCK_TCP - if (type == SOCK_STREAM) - { - break; - } -#endif - psock->s_type = 0; - psock->s_conn = NULL; - - ret = usrsock_socket(domain, type, protocol, psock); - if (ret >= 0) - { - /* Successfully handled and opened by usrsock daemon. */ - - return OK; - } - else if (ret == -ENETDOWN) - { - /* Net down means that usrsock daemon is not running. - * Attempt to open socket with kernel networking stack. - */ - } - else - { - return ret; - } - } - } - - return OK; -} -#else -#endif /* CONFIG_NET_USRSOCK */ - /**************************************************************************** * Public Functions ****************************************************************************/ @@ -173,16 +96,6 @@ int psock_socket(int domain, int type, int protocol, FAR struct socket *psock) int errcode; int ret; -#ifdef CONFIG_NET_USRSOCK - ret = usrsock_socket_setup(domain, type, protocol, psock); - if (ret < 0) - { - nerr("ERROR: usrsock_socket_setup() failed: %d\n", ret); - errcode = -ret; - goto errout; - } -#endif /* CONFIG_NET_USRSOCK */ - /* Initialize the socket structure */ psock->s_domain = domain; diff --git a/net/socket/socket.h b/net/socket/socket.h index a7f43ce10e8..fec553de298 100644 --- a/net/socket/socket.h +++ b/net/socket/socket.h @@ -436,6 +436,41 @@ int net_timeo(systime_t start_time, socktimeo_t timeo); ssize_t psock_send(FAR struct socket *psock, FAR const void *buf, size_t len, int flags); +/**************************************************************************** + * Name: inet_connect + * + * Description: + * inet_connect() connects the local socket referred to by the structure + * 'psock' to the address specified by 'addr'. The addrlen argument + * specifies the size of 'addr'. The format of the address in 'addr' is + * determined by the address space of the socket 'psock'. + * + * If the socket 'psock' is of type SOCK_DGRAM then 'addr' is the address + * to which datagrams are sent by default, and the only address from which + * datagrams are received. If the socket is of type SOCK_STREAM or + * SOCK_SEQPACKET, this call attempts to make a connection to the socket + * that is bound to the address specified by 'addr'. + * + * Generally, connection-based protocol sockets may successfully + * inet_connect() only once; connectionless protocol sockets may use + * inet_connect() multiple times to change their association. + * Connectionless sockets may dissolve the association by connecting to + * an address with the sa_family member of sockaddr set to AF_UNSPEC. + * + * Parameters: + * psock Pointer to a socket structure initialized by psock_socket() + * addr Server address (form depends on type of socket) + * addrlen Length of actual 'addr' + * + * Returned Value: + * 0 on success; a negated errno value on failue. See connect() for the + * list of appropriate errno values to be returned. + * + ****************************************************************************/ + +int inet_connect(FAR struct socket *psock, FAR const struct sockaddr *addr, + socklen_t addrlen); + /**************************************************************************** * Name: inet_recvfrom * From 0c840ed22f55f69ff98af272a388d097f2970699 Mon Sep 17 00:00:00 2001 From: Gregory Nutt Date: Thu, 13 Jul 2017 11:15:00 -0600 Subject: [PATCH 16/18] Socket interface: Added listen() and accept() interfaces. --- include/nuttx/net/net.h | 3 + net/local/local.h | 33 ++--- net/local/local_accept.c | 20 ++- net/local/local_listen.c | 43 +++++-- net/local/local_sockif.c | 10 +- net/pkt/pkt_sockif.c | 83 ++++++++++++- net/socket/accept.c | 149 ++--------------------- net/socket/bind.c | 1 - net/socket/inet_sockif.c | 254 +++++++++++++++++++++++++++++++++++++++ net/socket/listen.c | 86 +++---------- 10 files changed, 438 insertions(+), 244 deletions(-) diff --git a/include/nuttx/net/net.h b/include/nuttx/net/net.h index 06ab7710806..8553e1e92bf 100644 --- a/include/nuttx/net/net.h +++ b/include/nuttx/net/net.h @@ -105,8 +105,11 @@ struct sock_intf_s CODE int (*si_setup)(FAR struct socket *psock, int protocol); CODE int (*si_bind)(FAR struct socket *psock, FAR const struct sockaddr *addr, socklen_t addrlen); + CODE int (*si_listen)(FAR struct socket *psock, int backlog); CODE int (*si_connect)(FAR struct socket *psock, FAR const struct sockaddr *addr, socklen_t addrlen); + CODE int (*si_accept)(FAR struct socket *psock, FAR struct sockaddr *addr, + FAR socklen_t *addrlen, FAR struct socket *newsock); CODE ssize_t (*si_send)(FAR struct socket *psock, FAR const void *buf, size_t len, int flags); CODE ssize_t (*si_sendto)(FAR struct socket *psock, FAR const void *buf, diff --git a/net/local/local.h b/net/local/local.h index 2233b2c8df1..983e17950ab 100644 --- a/net/local/local.h +++ b/net/local/local.h @@ -309,26 +309,31 @@ int local_release(FAR struct local_conn_s *conn); * Name: local_listen * * Description: - * Listen for a new connection of a SOCK_STREAM Unix domain socket. + * To accept connections, a socket is first created with psock_socket(), a + * willingness to accept incoming connections and a queue limit for + * incoming connections are specified with psock_listen(), and then the + * connections are accepted with psock_accept(). For the case of local + * Unix sockets, psock_listen() calls this function. The psock_listen() + * call applies only to sockets of type SOCK_STREAM or SOCK_SEQPACKET. * - * This function is called as part of the implementation of listen(); - * - * Input Parameters: - * server - A reference to the server-side local connection structure - * backlog - Maximum number of pending connections. + * Parameters: + * psock Reference to an internal, boound socket structure. + * backlog The maximum length the queue of pending connections may grow. + * If a connection request arrives with the queue full, the client + * may receive an error with an indication of ECONNREFUSED or, + * if the underlying protocol supports retransmission, the request + * may be ignored so that retries succeed. * * Returned Value: - * Zero (OK) on success; a negated errno value on failure. - * - * Assumptions: - * The network is NOT locked + * On success, zero is returned. On error, a negated errno value is + * returned. See list() for the set of appropriate error values. * ****************************************************************************/ -int local_listen(FAR struct local_conn_s *server, int backlog); +int local_listen(FAR struct socket *psock, int backlog); /**************************************************************************** - * Name: psock_local_accept + * Name: local_accept * * Description: * This function implements accept() for Unix domain sockets. See the @@ -350,8 +355,8 @@ int local_listen(FAR struct local_conn_s *server, int backlog); * ****************************************************************************/ -int psock_local_accept(FAR struct socket *psock, FAR struct sockaddr *addr, - FAR socklen_t *addrlen, FAR void **newconn); +int local_accept(FAR struct socket *psock, FAR struct sockaddr *addr, + FAR socklen_t *addrlen, FAR void **newconn); /**************************************************************************** * Name: psock_local_send diff --git a/net/local/local_accept.c b/net/local/local_accept.c index 2ead57d1039..e1aa8132b46 100644 --- a/net/local/local_accept.c +++ b/net/local/local_accept.c @@ -88,7 +88,7 @@ static int local_waitlisten(FAR struct local_conn_s *server) ****************************************************************************/ /**************************************************************************** - * Name: psock_local_accept + * Name: local_accept * * Description: * This function implements accept() for Unix domain sockets. See the @@ -110,8 +110,8 @@ static int local_waitlisten(FAR struct local_conn_s *server) * ****************************************************************************/ -int psock_local_accept(FAR struct socket *psock, FAR struct sockaddr *addr, - FAR socklen_t *addrlen, FAR void **newconn) +int local_accept(FAR struct socket *psock, FAR struct sockaddr *addr, + FAR socklen_t *addrlen, FAR void **newconn) { FAR struct local_conn_s *server; @@ -122,6 +122,18 @@ int psock_local_accept(FAR struct socket *psock, FAR struct sockaddr *addr, /* Some sanity checks */ DEBUGASSERT(psock && psock->s_conn); + + /* Is the socket a stream? */ + + if (psock->s_domain != PF_LOCAL || psock->s_type != SOCK_STREAM) + { + return -EOPNOTSUPP; + } + + /* Verify that a valid memory block has been provided to receive the + * address + */ + server = (FAR struct local_conn_s *)psock->s_conn; if (server->lc_proto != SOCK_STREAM || @@ -213,7 +225,7 @@ int psock_local_accept(FAR struct socket *psock, FAR struct sockaddr *addr, /* Return the address family */ - if (addr) + if (addr != NULL) { ret = local_getaddr(client, addr, addrlen); } diff --git a/net/local/local_listen.c b/net/local/local_listen.c index a1020570e77..996bb049fef 100644 --- a/net/local/local_listen.c +++ b/net/local/local_listen.c @@ -66,29 +66,46 @@ dq_queue_t g_local_listeners; * Name: local_listen * * Description: - * Listen for a new connection of a SOCK_STREAM Unix domain socket. + * To accept connections, a socket is first created with psock_socket(), a + * willingness to accept incoming connections and a queue limit for + * incoming connections are specified with psock_listen(), and then the + * connections are accepted with psock_accept(). For the case of local + * Unix sockets, psock_listen() calls this function. The psock_listen() + * call applies only to sockets of type SOCK_STREAM or SOCK_SEQPACKET. * - * This function is called as part of the implementation of listen(); - * - * Input Parameters: - * server - A reference to the server-side local connection structure - * backlog - Maximum number of pending connections. + * Parameters: + * psock Reference to an internal, boound socket structure. + * backlog The maximum length the queue of pending connections may grow. + * If a connection request arrives with the queue full, the client + * may receive an error with an indication of ECONNREFUSED or, + * if the underlying protocol supports retransmission, the request + * may be ignored so that retries succeed. * * Returned Value: - * Zero (OK) on success; a negated errno value on failure. - * - * Assumptions: - * The network is NOT locked + * On success, zero is returned. On error, a negated errno value is + * returned. See list() for the set of appropriate error values. * ****************************************************************************/ -int local_listen(FAR struct local_conn_s *server, int backlog) +int local_listen(FAR struct socket *psock, int backlog) { + FAR struct local_conn_s *server; + + /* Verify that the sockfd corresponds to a connected SOCK_STREAM in this + * address family. + */ + + if (psock->s_domain != PF_LOCAL || psock->s_type != SOCK_STREAM) + { + nerr("ERROR: Unsupported socket family=%d or socket type=%d\n", + psocl->s_domain, psock->s_type); + return -EOPNOTSUPP; + } + + server = (FAR struct local_conn_s *)psock->s_conn; /* Some sanity checks */ - DEBUGASSERT(server); - if (server->lc_proto != SOCK_STREAM || server->lc_state == LOCAL_STATE_UNBOUND || server->lc_type != LOCAL_TYPE_PATHNAME) diff --git a/net/local/local_sockif.c b/net/local/local_sockif.c index 73113cccc2d..68f93e7df09 100644 --- a/net/local/local_sockif.c +++ b/net/local/local_sockif.c @@ -59,9 +59,11 @@ static int local_setup(FAR struct socket *psock, int protocol); static int local_bind(FAR struct socket *psock, - FAR const struct sockaddr *addr, socklen_t addrlen); + FAR const struct sockaddr *addr, socklen_t addrlen); static int local_connect(FAR struct socket *psock, - FAR const struct sockaddr *addr, socklen_t addrlen); + FAR const struct sockaddr *addr, socklen_t addrlen); +static int local_accept(FAR struct socket *psock, FAR struct sockaddr *addr, + FAR socklen_t *addrlen, FAR struct socket *newsock); static ssize_t local_send(FAR struct socket *psock, FAR const void *buf, size_t len, int flags); static ssize_t local_sendto(FAR struct socket *psock, FAR const void *buf, @@ -76,7 +78,9 @@ const struct sock_intf_s g_local_sockif = { local_setup, /* si_setup */ local_bind, /* si_bind */ + local_listen, /* si_listen */ local_connect, /* si_connect */ + local_accept, /* si_accept */ local_send, /* si_send */ local_sendto, /* si_sendto */ local_recvfrom /* si_recvfrom */ @@ -445,7 +449,7 @@ ssize_t local_sendto(FAR struct socket *psock, FAR const void *buf, ****************************************************************************/ /**************************************************************************** - * Name: + * Name: * * Description: * diff --git a/net/pkt/pkt_sockif.c b/net/pkt/pkt_sockif.c index 72e7751f8bc..c110b578f64 100644 --- a/net/pkt/pkt_sockif.c +++ b/net/pkt/pkt_sockif.c @@ -58,8 +58,11 @@ static int pkt_setup(FAR struct socket *psock, int protocol); static int pkt_bind(FAR struct socket *psock, FAR const struct sockaddr *addr, socklen_t addrlen); +static int pkt_listen(FAR struct socket *psock, int backlog); static int pkt_connect(FAR struct socket *psock, FAR const struct sockaddr *addr, socklen_t addrlen); +static int pkt_accept(FAR struct socket *psock, FAR struct sockaddr *addr, + FAR socklen_t *addrlen, FAR struct socket *newsock); static ssize_t pkt_send(FAR struct socket *psock, FAR const void *buf, size_t len, int flags); static ssize_t pkt_sendto(FAR struct socket *psock, FAR const void *buf, @@ -74,7 +77,9 @@ const struct sock_intf_s g_pkt_sockif = { pkt_setup, /* si_setup */ pkt_bind, /* si_bind */ + pkt_listen, /* si_listen */ pkt_connect, /* si_connect */ + pkt_accept, /* si_accept */ pkt_send, /* si_send */ pkt_sendto, /* si_sendto */ pkt_recvfrom /* si_recvfrom */ @@ -195,6 +200,52 @@ static int pkt_connect(FAR struct socket *psock, return -EAFNOSUPPORT; } +/**************************************************************************** + * Name: pkt_accept + * + * Description: + * The pkt_accept function is used with connection-based socket types + * (SOCK_STREAM, SOCK_SEQPACKET and SOCK_RDM). It extracts the first + * connection request on the queue of pending connections, creates a new + * connected socket with mostly the same properties as 'sockfd', and + * allocates a new socket descriptor for the socket, which is returned. The + * newly created socket is no longer in the listening state. The original + * socket 'sockfd' is unaffected by this call. Per file descriptor flags + * are not inherited across an pkt_accept. + * + * The 'sockfd' argument is a socket descriptor that has been created with + * socket(), bound to a local address with bind(), and is listening for + * connections after a call to listen(). + * + * On return, the 'addr' structure is filled in with the address of the + * connecting entity. The 'addrlen' argument initially contains the size + * of the structure pointed to by 'addr'; on return it will contain the + * actual length of the address returned. + * + * If no pending connections are present on the queue, and the socket is + * not marked as non-blocking, pkt_accept blocks the caller until a + * connection is present. If the socket is marked non-blocking and no + * pending connections are present on the queue, pkt_accept returns + * EAGAIN. + * + * Parameters: + * psock Reference to the listening socket structure + * addr Receives the address of the connecting client + * addrlen Input: allocated size of 'addr', Return: returned size of 'addr' + * newsock Location to return the accepted socket information. + * + * Returned Value: + * Returns 0 (OK) on success. On failure, it returns a negated errno + * value. See accept() for a desrciption of the approriate error value. + * + ****************************************************************************/ + +static int pkt_accept(FAR struct socket *psock, FAR struct sockaddr *addr, + FAR socklen_t *addrlen, FAR struct socket *newsock) +{ + return -EAFNOSUPPORT; +} + /**************************************************************************** * Name: pkt_bind * @@ -271,6 +322,36 @@ static int pkt_bind(FAR struct socket *psock, FAR const struct sockaddr *addr, } } +/**************************************************************************** + * Name: pkt_listen + * + * Description: + * To accept connections, a socket is first created with psock_socket(), a + * willingness to accept incoming connections and a queue limit for + * incoming connections are specified with psock_listen(), and then the + * connections are accepted with psock_accept(). For the case of raw + * packet sockets, psock_listen() calls this function. The psock_listen() + * call applies only to sockets of type SOCK_STREAM or SOCK_SEQPACKET. + * + * Parameters: + * psock Reference to an internal, boound socket structure. + * backlog The maximum length the queue of pending connections may grow. + * If a connection request arrives with the queue full, the client + * may receive an error with an indication of ECONNREFUSED or, + * if the underlying protocol supports retransmission, the request + * may be ignored so that retries succeed. + * + * Returned Value: + * On success, zero is returned. On error, a negated errno value is + * returned. See list() for the set of appropriate error values. + * + ****************************************************************************/ + +int pkt_listen(FAR struct socket *psock, int backlog) +{ + return -EOPNOTSUPP; +} + /**************************************************************************** * Name: pkt_send * @@ -349,7 +430,7 @@ static ssize_t pkt_sendto(FAR struct socket *psock, FAR const void *buf, ****************************************************************************/ /**************************************************************************** - * Name: + * Name: * * Description: * diff --git a/net/socket/accept.c b/net/socket/accept.c index b295542c28f..093b16e438d 100644 --- a/net/socket/accept.c +++ b/net/socket/accept.c @@ -115,8 +115,7 @@ * ENFILE * The system maximum for file descriptors has been reached. * EFAULT - * The addr parameter is not in a writable part of the user address - * space. + * The addr parameter is not in a writable part of the user address space. * ENOBUFS or ENOMEM * Not enough free memory. * EPROTO @@ -134,25 +133,18 @@ int psock_accept(FAR struct socket *psock, FAR struct sockaddr *addr, int ret; #endif - DEBUGASSERT(psock != NULL); + DEBUGASSERT(psock != NULL && psock->s_conn != NULL && newsock != NULL); /* Treat as a cancellation point */ (void)enter_cancellation_point(); - /* Is the socket a stream? */ + /* May sure that the socket has been opened with socket() */ - if (psock->s_type != SOCK_STREAM) + if (psock == NULL || psock->s_conn == NULL) { -#ifdef CONFIG_NET_USRSOCK - if (psock->s_type == SOCK_USRSOCK_TYPE) - { -#warning "Missing logic" - } -#endif - - errcode = EOPNOTSUPP; - goto errout; + nerr("ERROR: Socket invalid or not opened\n"); + return -EINVAL; } /* Is the socket listening for a connection? */ @@ -163,128 +155,16 @@ int psock_accept(FAR struct socket *psock, FAR struct sockaddr *addr, goto errout; } - /* Verify that a valid memory block has been provided to receive the - * address - */ + /* Let the address family's accept() method handle the operation */ - if (addr) + DEBUGASSERT(psock->s_sockif != NULL && psock->s_sockif->si_accept != NULL); + ret = psock->s_sockif->si_accept(psock, addr, addrlen, newsock); + if (ret < 0) { - /* If an address is provided, then the length must also be provided. */ - - DEBUGASSERT(addrlen); - - /* A valid length depends on the address domain */ - - switch (psock->s_domain) - { -#ifdef CONFIG_NET_IPv4 - case PF_INET: - { - if (*addrlen < sizeof(struct sockaddr_in)) - { - errcode = EBADF; - goto errout; - } - } - break; -#endif /* CONFIG_NET_IPv4 */ - -#ifdef CONFIG_NET_IPv6 - case PF_INET6: - { - if (*addrlen < sizeof(struct sockaddr_in6)) - { - errcode = EBADF; - goto errout; - } - } - break; -#endif /* CONFIG_NET_IPv6 */ - -#ifdef CONFIG_NET_LOCAL_STREAM - case PF_LOCAL: - { - if (*addrlen < sizeof(sa_family_t)) - { - errcode = EBADF; - goto errout; - } - } - break; -#endif /* CONFIG_NET_IPv6 */ - - default: - DEBUGPANIC(); - errcode = EINVAL; - goto errout; - } - } - - /* Initialize the socket structure. */ - - newsock->s_domain = psock->s_domain; - newsock->s_type = SOCK_STREAM; - newsock->s_sockif = psock->s_sockif; - - /* Perform the correct accept operation for this address domain */ - -#ifdef CONFIG_NET_LOCAL_STREAM -#ifdef CONFIG_NET_TCP - if (psock->s_domain == PF_LOCAL) -#endif - { - /* Perform the local accept operation (with the network unlocked) */ - - ret = psock_local_accept(psock, addr, addrlen, &newsock->s_conn); - if (ret < 0) - { - errcode = -ret; - goto errout; - } - } -#endif /* CONFIG_NET_LOCAL_STREAM */ - -#ifdef CONFIG_NET_TCP -#ifdef CONFIG_NET_LOCAL_STREAM - else -#endif - { -#ifdef NET_TCP_HAVE_STACK - /* Perform the local accept operation (with the network locked) */ - - net_lock(); - ret = psock_tcp_accept(psock, addr, addrlen, &newsock->s_conn); - if (ret < 0) - { - net_unlock(); - errcode = -ret; - goto errout; - } - - /* Begin monitoring for TCP connection events on the newly connected - * socket - */ - - ret = net_startmonitor(newsock); - if (ret < 0) - { - /* net_startmonitor() can only fail on certain race conditions - * where the connection was lost just before this function was - * called. Undo everything we have done and return a failure. - */ - - net_unlock(); - errcode = -ret; - goto errout_after_accept; - } - - net_unlock(); -#else - errcode = EOPNOTSUPP; + nerr("ERROR: si_accept failed: %d\n", ret); + errcode = -ret; goto errout; -#endif /* NET_TCP_HAVE_STACK */ } -#endif /* CONFIG_NET_TCP */ /* Mark the new socket as connected. */ @@ -294,11 +174,6 @@ int psock_accept(FAR struct socket *psock, FAR struct sockaddr *addr, leave_cancellation_point(); return OK; -#ifdef NET_TCP_HAVE_STACK -errout_after_accept: - psock_close(newsock); -#endif - errout: set_errno(errcode); leave_cancellation_point(); diff --git a/net/socket/bind.c b/net/socket/bind.c index cf4c025f464..8d7270aee1b 100644 --- a/net/socket/bind.c +++ b/net/socket/bind.c @@ -95,7 +95,6 @@ int psock_bind(FAR struct socket *psock, const struct sockaddr *addr, #ifdef CONFIG_NET_PKT FAR const struct sockaddr_ll *lladdr = (const struct sockaddr_ll *)addr; #endif - socklen_t minlen; int errcode; int ret = OK; diff --git a/net/socket/inet_sockif.c b/net/socket/inet_sockif.c index 4574f2a1e40..470315c2295 100644 --- a/net/socket/inet_sockif.c +++ b/net/socket/inet_sockif.c @@ -61,6 +61,9 @@ static int inet_setup(FAR struct socket *psock, int protocol); static int inet_bind(FAR struct socket *psock, FAR const struct sockaddr *addr, socklen_t addrlen); +static int inet_listen(FAR struct socket *psock, int backlog); +static int inet_accept(FAR struct socket *psock, FAR struct sockaddr *addr, + FAR socklen_t *addrlen, FAR struct socket *newsock); static ssize_t inet_send(FAR struct socket *psock, FAR const void *buf, size_t len, int flags); static ssize_t inet_sendto(FAR struct socket *psock, FAR const void *buf, @@ -75,7 +78,9 @@ const struct sock_intf_s g_inet_sockif = { inet_setup, /* si_setup */ inet_bind, /* si_bind */ + inet_listen, /* si_listen */ inet_connect, /* si_connect */ + inet_accept, /* si_accept */ inet_send, /* si_send */ inet_sendto, /* si_sendto */ inet_recvfrom /* si_recvfrom */ @@ -435,6 +440,255 @@ static int inet_bind(FAR struct socket *psock, return ret; } +/**************************************************************************** + * Name: inet_listen + * + * Description: + * To accept connections, a socket is first created with psock_socket(), a + * willingness to accept incoming connections and a queue limit for + * incoming connections are specified with psock_listen(), and then the + * connections are accepted with psock_accept(). For the case of AFINET + * and AFINET6 sockets, psock_listen() calls this function. The + * psock_listen() call applies only to sockets of type SOCK_STREAM or + * SOCK_SEQPACKET. + * + * Parameters: + * psock Reference to an internal, boound socket structure. + * backlog The maximum length the queue of pending connections may grow. + * If a connection request arrives with the queue full, the client + * may receive an error with an indication of ECONNREFUSED or, + * if the underlying protocol supports retransmission, the request + * may be ignored so that retries succeed. + * + * Returned Value: + * On success, zero is returned. On error, a negated errno value is + * returned. See list() for the set of appropriate error values. + * + ****************************************************************************/ + +int inet_listen(FAR struct socket *psock, int backlog) +{ +#if defined(CONFIG_NET_TCP) && defined(NET_TCP_HAVE_STACK) + FAR struct tcp_conn_s *conn; + int ret; +#endif + + /* Verify that the sockfd corresponds to a connected SOCK_STREAM */ + + if (psock->s_type != SOCK_STREAM) + { +#ifdef CONFIG_NET_USRSOCK + if (psock->s_type == SOCK_USRSOCK_TYPE) + { +#warning "Missing logic" + } +#endif + + nerr("ERROR: Unsupported socket type: %d\n", + psock->s_type); + return -EOPNOTSUPP; + } + +#ifdef CONFIG_NET_TCP +#ifdef NET_TCP_HAVE_STACK + conn = (FAR struct tcp_conn_s *)psock->s_conn; + + if (conn->lport <= 0) + { + return -EOPNOTSUPP; + } + +#ifdef CONFIG_NET_TCPBACKLOG + /* Set up the backlog for this connection */ + + ret = tcp_backlogcreate(conn, backlog); + if (ret < 0) + { + nerr("ERROR: tcp_backlogcreate failed: %d\n", ret); + return ret; + } +#endif + + /* Start listening to the bound port. This enables callbacks when + * accept() is called and enables poll()/select() logic. + */ + + ret = tcp_listen(conn); + if (ret < 0) + { + nerr("ERROR: tcp_listen failed: %d\n", ret); + } + + return ret; +#else + nwarn("WARNING: Stream socket support not available\n"); + return -EOPNOTSUPP; +#endif /* NET_TCP_HAVE_STACK */ +#else + nwarn("WARNING: Stream socket support not enabled\n"); + return -EOPNOTSUPP; +#endif /* CONFIG_NET_TCP */ +} + +/**************************************************************************** + * Name: inet_accept + * + * Description: + * The inet_accept function is used with connection-based socket types + * (SOCK_STREAM, SOCK_SEQPACKET and SOCK_RDM). It extracts the first + * connection request on the queue of pending connections, creates a new + * connected socket with mostly the same properties as 'sockfd', and + * allocates a new socket descriptor for the socket, which is returned. The + * newly created socket is no longer in the listening state. The original + * socket 'sockfd' is unaffected by this call. Per file descriptor flags + * are not inherited across an inet_accept. + * + * The 'sockfd' argument is a socket descriptor that has been created with + * socket(), bound to a local address with bind(), and is listening for + * connections after a call to listen(). + * + * On return, the 'addr' structure is filled in with the address of the + * connecting entity. The 'addrlen' argument initially contains the size + * of the structure pointed to by 'addr'; on return it will contain the + * actual length of the address returned. + * + * If no pending connections are present on the queue, and the socket is + * not marked as non-blocking, inet_accept blocks the caller until a + * connection is present. If the socket is marked non-blocking and no + * pending connections are present on the queue, inet_accept returns + * EAGAIN. + * + * Parameters: + * psock Reference to the listening socket structure + * addr Receives the address of the connecting client + * addrlen Input: allocated size of 'addr', Return: returned size of 'addr' + * newsock Location to return the accepted socket information. + * + * Returned Value: + * Returns 0 (OK) on success. On failure, it returns a negated errno + * value. See accept() for a desrciption of the approriate error value. + * + ****************************************************************************/ + +static int inet_accept(FAR struct socket *psock, FAR struct sockaddr *addr, + FAR socklen_t *addrlen, FAR struct socket *newsock) +{ +#if defined(CONFIG_NET_TCP) && defined(NET_TCP_HAVE_STACK) + int ret; +#endif + + /* Is the socket a stream? */ + + if (psock->s_type != SOCK_STREAM) + { +#ifdef CONFIG_NET_USRSOCK + if (psock->s_type == SOCK_USRSOCK_TYPE) + { +#warning "Missing logic" + } +#endif + + nerr("ERROR: Inappropreat socket type: %d\n", psock->s_type); + return -EOPNOTSUPP; + } + + /* Verify that a valid memory block has been provided to receive the + * address + */ + + if (addr != NULL) + { + /* If an address is provided, then the length must also be provided. */ + + DEBUGASSERT(addrlen > 0); + + /* A valid length depends on the address domain */ + + switch (psock->s_domain) + { +#ifdef CONFIG_NET_IPv4 + case PF_INET: + { + if (*addrlen < sizeof(struct sockaddr_in)) + { + return -EBADF; + } + } + break; +#endif /* CONFIG_NET_IPv4 */ + +#ifdef CONFIG_NET_IPv6 + case PF_INET6: + { + if (*addrlen < sizeof(struct sockaddr_in6)) + { + return -EBADF; + } + } + break; +#endif /* CONFIG_NET_IPv6 */ + + default: + DEBUGPANIC(); + return -EINVAL; + } + } + + /* Initialize the socket structure. */ + + newsock->s_domain = psock->s_domain; + newsock->s_type = SOCK_STREAM; + newsock->s_sockif = psock->s_sockif; + + /* Perform the correct accept operation for this address domain */ + +#ifdef CONFIG_NET_TCP +#ifdef NET_TCP_HAVE_STACK + /* Perform the local accept operation (with the network locked) */ + + net_lock(); + ret = psock_tcp_accept(psock, addr, addrlen, &newsock->s_conn); + if (ret < 0) + { + nerr("ERROR: psock_tcp_accept failed: %d\n", ret); + goto errout_with_lock; + } + + /* Begin monitoring for TCP connection events on the newly connected + * socket + */ + + ret = net_startmonitor(newsock); + if (ret < 0) + { + /* net_startmonitor() can only fail on certain race conditions where + * the connection was lost just before this function was called. Undo + * everything we have done and return a failure. + */ + + goto errout_after_accept; + } + + net_unlock(); + return OK; + +errout_after_accept: + psock_close(newsock); + +errout_with_lock: + net_unlock(); + return ret; + +#else + nwarn("WARNING: SOCK_STREAM not supported in this configuration\n"); + return -EOPNOTSUPP; +#endif /* NET_TCP_HAVE_STACK */ +#else + nwarn("WARNING: TCP/IP not supported in this configuration\n"); + return -EOPNOTSUPP; +#endif /* CONFIG_NET_TCP */ +} + /**************************************************************************** * Name: inet_send * diff --git a/net/socket/listen.c b/net/socket/listen.c index 26a9501807c..fd4a410c94e 100644 --- a/net/socket/listen.c +++ b/net/socket/listen.c @@ -1,7 +1,7 @@ /**************************************************************************** * net/socket/listen.c * - * Copyright (C) 2007-2009, 201-2016 Gregory Nutt. All rights reserved. + * Copyright (C) 2007-2009, 201-2017 Gregory Nutt. All rights reserved. * Author: Gregory Nutt * * Redistribution and use in source and binary forms, with or without @@ -38,17 +38,15 @@ ****************************************************************************/ #include -#if defined(CONFIG_NET) && CONFIG_NSOCKET_DESCRIPTORS > 0 #include -#include #include +#include #include -#include "tcp/tcp.h" -#include "local/local.h" #include "socket/socket.h" -#include "usrsock/usrsock.h" + +#if defined(CONFIG_NET) && CONFIG_NSOCKET_DESCRIPTORS > 0 /**************************************************************************** * Public Functions @@ -86,83 +84,29 @@ int psock_listen(FAR struct socket *psock, int backlog) { int errcode; + int ret; DEBUGASSERT(psock != NULL); /* Verify that the sockfd corresponds to a connected SOCK_STREAM */ - if (psock->s_type != SOCK_STREAM || !psock->s_conn) + if (psock == NULL || psock->s_conn == NULL) { -#ifdef CONFIG_NET_USRSOCK - if (psock->s_type == SOCK_USRSOCK_TYPE) - { -#warning "Missing logic" - } -#endif - - errcode = EOPNOTSUPP; + nerr("ERROR: Invalid or unconnected socket\n"); + errcode = EINVAL; goto errout; } -#ifdef CONFIG_NET_LOCAL -#ifdef CONFIG_NET_TCP - if (psock->s_domain == PF_LOCAL) -#endif + /* Let the address family's listen() method handle the operation */ + + DEBUGASSERT(psock->s_sockif != NULL && psock->s_sockif->si_listen != NULL); + ret = psock->s_sockif->si_listen(psock, backlog); + if (ret < 0) { - FAR struct local_conn_s *conn = - (FAR struct local_conn_s *)psock->s_conn; - - errcode = local_listen(conn, backlog); - if (errcode < 0) - { - errcode = -errcode; - goto errout; - } - } -#endif /* CONFIG_NET_LOCAL */ - -#ifdef CONFIG_NET_TCP -#ifdef CONFIG_NET_LOCAL - else -#endif - { -#ifdef NET_TCP_HAVE_STACK - FAR struct tcp_conn_s *conn = - (FAR struct tcp_conn_s *)psock->s_conn; - - if (conn->lport <= 0) - { - errcode = EOPNOTSUPP; - goto errout; - } - - /* Set up the backlog for this connection */ - -#ifdef CONFIG_NET_TCPBACKLOG - errcode = tcp_backlogcreate(conn, backlog); - if (errcode < 0) - { - errcode = -errcode; - goto errout; - } -#endif - - /* Start listening to the bound port. This enables callbacks when - * accept() is called and enables poll()/select() logic. - */ - - errcode = tcp_listen(conn); - if (errcode < 0) - { - errcode = -errcode; - goto errout; - } -#else - errcode = EOPNOTSUPP; + nerr("ERROR: si_listen failed: %d\n", ret); + errcode = -ret; goto errout; -#endif /* NET_TCP_HAVE_STACK */ } -#endif /* CONFIG_NET_TCP */ psock->s_flags |= _SF_LISTENING; return OK; From 833ee2275d6fd114c8a722631168fac693d2c8ec Mon Sep 17 00:00:00 2001 From: Gregory Nutt Date: Thu, 13 Jul 2017 12:15:15 -0600 Subject: [PATCH 17/18] Socket interface: Added close() interface. --- include/nuttx/net/net.h | 1 + net/local/local_sockif.c | 61 +++- net/pkt/pkt_sockif.c | 57 +++- net/socket/Make.defs | 4 +- net/socket/inet_close.c | 610 +++++++++++++++++++++++++++++++++++++ net/socket/inet_sockif.c | 3 +- net/socket/net_close.c | 634 +-------------------------------------- net/socket/socket.h | 18 ++ 8 files changed, 760 insertions(+), 628 deletions(-) create mode 100644 net/socket/inet_close.c diff --git a/include/nuttx/net/net.h b/include/nuttx/net/net.h index 8553e1e92bf..93a6deadc88 100644 --- a/include/nuttx/net/net.h +++ b/include/nuttx/net/net.h @@ -118,6 +118,7 @@ struct sock_intf_s CODE ssize_t (*si_recvfrom)(FAR struct socket *psock, FAR void *buf, size_t len, int flags, FAR struct sockaddr *from, FAR socklen_t *fromlen); + CODE int (*si_close)(FAR struct socket *psock); }; /* This is the internal representation of a socket reference by a file diff --git a/net/local/local_sockif.c b/net/local/local_sockif.c index 68f93e7df09..1b8d42b6408 100644 --- a/net/local/local_sockif.c +++ b/net/local/local_sockif.c @@ -69,6 +69,7 @@ static ssize_t local_send(FAR struct socket *psock, FAR const void *buf, static ssize_t local_sendto(FAR struct socket *psock, FAR const void *buf, size_t len, int flags, FAR const struct sockaddr *to, socklen_t tolen); +static int local_close(FAR struct socket *psock); /**************************************************************************** * Public Data @@ -83,7 +84,8 @@ const struct sock_intf_s g_local_sockif = local_accept, /* si_accept */ local_send, /* si_send */ local_sendto, /* si_sendto */ - local_recvfrom /* si_recvfrom */ + local_recvfrom, /* si_recvfrom */ + local_close /* si_close */ }; /**************************************************************************** @@ -444,6 +446,63 @@ ssize_t local_sendto(FAR struct socket *psock, FAR const void *buf, return nsent; } +/**************************************************************************** + * Name: local_close + * + * Description: + * Performs the close operation on a local, Unix socket instance + * + * Parameters: + * psock Socket instance + * + * Returned Value: + * 0 on success; a negated errno value is returned on any failure. + * + * Assumptions: + * + ****************************************************************************/ + +static int local_close(FAR struct socket *psock) +{ + /* Perform some pre-close operations for the local address type */ + + switch (psock->s_type) + { +#if defined(CONFIG_NET_TCP) || defined(CONFIG_NET_UDP) +#ifdef CONFIG_NET_TCP + case SOCK_STREAM: +#endif +#ifdef CONFIG_NET_UDP + case SOCK_DGRAM: +#endif + { + FAR struct local_conn_s *conn = psock->s_conn; + + /* Is this the last reference to the connection structure (there + * could be more if the socket was dup'ed). + */ + + if (conn->lc_crefs <= 1) + { + conn->lc_crefs = 0; + local_release(conn); + } + else + { + /* No.. Just decrement the reference count */ + + conn->lc_crefs--; + } + + return OK; + } +#endif /* CONFIG_NET_TCP || CONFIG_NET_UDP*/ + + default: + return -EBADF; + } +} + /**************************************************************************** * Public Functions ****************************************************************************/ diff --git a/net/pkt/pkt_sockif.c b/net/pkt/pkt_sockif.c index c110b578f64..ded05d6ea89 100644 --- a/net/pkt/pkt_sockif.c +++ b/net/pkt/pkt_sockif.c @@ -68,6 +68,7 @@ static ssize_t pkt_send(FAR struct socket *psock, FAR const void *buf, static ssize_t pkt_sendto(FAR struct socket *psock, FAR const void *buf, size_t len, int flags, FAR const struct sockaddr *to, socklen_t tolen); +static int pkt_close(FAR struct socket *psock); /**************************************************************************** * Public Data @@ -82,7 +83,8 @@ const struct sock_intf_s g_pkt_sockif = pkt_accept, /* si_accept */ pkt_send, /* si_send */ pkt_sendto, /* si_sendto */ - pkt_recvfrom /* si_recvfrom */ + pkt_recvfrom, /* si_recvfrom */ + pkt_close /* si_close */ }; /**************************************************************************** @@ -425,6 +427,59 @@ static ssize_t pkt_sendto(FAR struct socket *psock, FAR const void *buf, return -EAFNOSUPPORT; } +/**************************************************************************** + * Name: pkt_close + * + * Description: + * Performs the close operation on a raw packet socket instance + * + * Parameters: + * psock Socket instance + * + * Returned Value: + * 0 on success; a negated errno value is returned on any failure. + * + * Assumptions: + * + ****************************************************************************/ + +static int pkt_close(FAR struct socket *psock) +{ + /* Perform some pre-close operations for the raw packet address type */ + + switch (psock->s_type) + { + case SOCK_RAW: + { + FAR struct pkt_conn_s *conn = psock->s_conn; + + /* Is this the last reference to the connection structure (there + * could be more if the socket was dup'ed). + */ + + if (conn->crefs <= 1) + { + /* Yes... free the connection structure */ + + conn->crefs = 0; /* No more references on the connection */ + pkt_free(psock->s_conn); /* Free network resources */ + } + else + { + /* No.. Just decrement the reference count */ + + conn->crefs--; + } + + return OK; + } +#endif + + default: + return -EBADF; + } +} + /**************************************************************************** * Public Functions ****************************************************************************/ diff --git a/net/socket/Make.defs b/net/socket/Make.defs index 34deabf99eb..1b9c99d5d1e 100644 --- a/net/socket/Make.defs +++ b/net/socket/Make.defs @@ -44,9 +44,9 @@ SOCK_CSRCS += net_dupsd2.c net_clone.c net_poll.c net_vfcntl.c SOCK_CSRCS += net_sockif.c ifeq ($(CONFIG_NET_IPv4),y) -SOCK_CSRCS += inet_sockif.c inet_recvfrom.c inet_connect.c +SOCK_CSRCS += inet_sockif.c inet_recvfrom.c inet_connect.c inet_close.c else ifeq ($(CONFIG_NET_IPv6),y) -SOCK_CSRCS += inet_sockif.c inet_recvfrom.c inet_connect.c +SOCK_CSRCS += inet_sockif.c inet_recvfrom.c inet_connect.c inet_close.c endif # TCP/IP support diff --git a/net/socket/inet_close.c b/net/socket/inet_close.c new file mode 100644 index 00000000000..0461a60d10e --- /dev/null +++ b/net/socket/inet_close.c @@ -0,0 +1,610 @@ +/**************************************************************************** + * net/socket/inet_close.c + * + * Copyright (C) 2007-2017 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#ifdef CONFIG_NET + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +#ifdef CONFIG_NET_SOLINGER +# include +#endif + +#include "netdev/netdev.h" +#include "devif/devif.h" +#include "tcp/tcp.h" +#include "udp/udp.h" +#include "pkt/pkt.h" +#include "local/local.h" +#include "socket/socket.h" +#include "usrsock/usrsock.h" + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +#ifdef NET_TCP_HAVE_STACK +struct tcp_close_s +{ + FAR struct devif_callback_s *cl_cb; /* Reference to TCP callback instance */ +#ifdef CONFIG_NET_SOLINGER + FAR struct socket *cl_psock; /* Reference to the TCP socket */ + sem_t cl_sem; /* Signals disconnect completion */ + int cl_result; /* The result of the close */ + systime_t cl_start; /* Time close started (in ticks) */ +#endif +}; +#endif + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: tcp_close_timeout + * + * Description: + * Check for a timeout on a lingering close. + * + * Parameters: + * pstate - close state structure + * + * Returned Value: + * TRUE:timeout FALSE:no timeout + * + * Assumptions: + * Running at the interrupt level + * + ****************************************************************************/ + +#if defined(NET_TCP_HAVE_STACK) && defined(CONFIG_NET_SOLINGER) +static inline int tcp_close_timeout(FAR struct tcp_close_s *pstate) +{ + FAR struct socket *psock = 0; + + /* Make sure that we are performing a lingering close */ + + if (pstate != NULL) + { + /* Yes Check for a timeout configured via setsockopts(SO_LINGER). + * If none... we well let the send wait forever. + */ + + psock = pstate->cl_psock; + if (psock && psock->s_linger != 0) + { + /* Check if the configured timeout has elapsed */ + + return net_timeo(pstate->cl_start, psock->s_linger); + } + } + + /* No timeout */ + + return FALSE; +} +#endif /* NET_TCP_HAVE_STACK && CONFIG_NET_SOLINGER */ + +/**************************************************************************** + * Name: tcp_close_interrupt + * + * Description: + * Handle network callback events. + * + * Parameters: + * conn - TCP connection structure + * + * Returned Value: + * None + * + * Assumptions: + * Called from normal user-level logic + * + ****************************************************************************/ + +#ifdef NET_TCP_HAVE_STACK +static uint16_t tcp_close_interrupt(FAR struct net_driver_s *dev, + FAR void *pvconn, FAR void *pvpriv, + uint16_t flags) +{ +#ifdef CONFIG_NET_SOLINGER + FAR struct tcp_close_s *pstate = (FAR struct tcp_close_s *)pvpriv; +#endif + FAR struct tcp_conn_s *conn = (FAR struct tcp_conn_s *)pvconn; + + DEBUGASSERT(conn != NULL); + + ninfo("conn: %p flags: %04x\n", conn, flags); + + /* TCP_DISCONN_EVENTS: + * TCP_CLOSE: The remote host has closed the connection + * TCP_ABORT: The remote host has aborted the connection + * TCP_TIMEDOUT: The remote did not respond, the connection timed out + * NETDEV_DOWN: The network device went down + */ + + if ((flags & TCP_DISCONN_EVENTS) != 0) + { + /* The disconnection is complete */ + +#ifdef CONFIG_NET_SOLINGER + /* pstate non-NULL means that we are performing a LINGERing close. */ + + if (pstate != NULL) + { + /* Wake up the waiting thread with a successful result */ + + pstate->cl_result = OK; + goto end_wait; + } + + /* Otherwise, nothing is waiting on the close event and we can perform + * the completion actions here. + */ + + else +#endif + { + /* Free connection resources */ + + tcp_free(conn); + + /* Stop further callbacks */ + + flags = 0; + } + } + +#ifdef CONFIG_NET_SOLINGER + /* Check for a timeout. */ + + else if (pstate && tcp_close_timeout(pstate)) + { + /* Yes.. Wake up the waiting thread and report the timeout */ + + nerr("ERROR: CLOSE timeout\n"); + pstate->cl_result = -ETIMEDOUT; + goto end_wait; + } + +#endif /* CONFIG_NET_SOLINGER */ + +#ifdef CONFIG_NET_TCP_WRITE_BUFFERS + /* Check if all outstanding bytes have been ACKed */ + + else if (conn->unacked != 0 || !sq_empty(&conn->write_q)) + { + /* No... we are still waiting for ACKs. Drop any received data, but + * do not yet report TCP_CLOSE in the response. + */ + + dev->d_len = 0; + flags = (flags & ~TCP_NEWDATA); + } + +#endif /* CONFIG_NET_TCP_WRITE_BUFFERS */ + + else + { + /* Drop data received in this state and make sure that TCP_CLOSE + * is set in the response + */ + + dev->d_len = 0; + flags = (flags & ~TCP_NEWDATA) | TCP_CLOSE; + } + + return flags; + +#ifdef CONFIG_NET_SOLINGER +end_wait: + pstate->cl_cb->flags = 0; + pstate->cl_cb->priv = NULL; + pstate->cl_cb->event = NULL; + sem_post(&pstate->cl_sem); + + ninfo("Resuming\n"); + return 0; +#endif +} +#endif /* NET_TCP_HAVE_STACK */ + +/**************************************************************************** + * Name: tcp_close_txnotify + * + * Description: + * Notify the appropriate device driver that we are have data ready to + * be send (TCP) + * + * Parameters: + * psock - Socket state structure + * conn - The TCP connection structure + * + * Returned Value: + * None + * + ****************************************************************************/ + +#ifdef NET_TCP_HAVE_STACK +static inline void tcp_close_txnotify(FAR struct socket *psock, + FAR struct tcp_conn_s *conn) +{ +#ifdef CONFIG_NET_IPv4 +#ifdef CONFIG_NET_IPv6 + /* If both IPv4 and IPv6 support are enabled, then we will need to select + * the device driver using the appropriate IP domain. + */ + + if (psock->s_domain == PF_INET) +#endif + { + /* Notify the device driver that send data is available */ + +#ifdef CONFIG_NETDEV_MULTINIC + netdev_ipv4_txnotify(conn->u.ipv4.laddr, conn->u.ipv4.raddr); +#else + netdev_ipv4_txnotify(conn->u.ipv4.raddr); +#endif + } +#endif /* CONFIG_NET_IPv4 */ + +#ifdef CONFIG_NET_IPv6 +#ifdef CONFIG_NET_IPv4 + else /* if (psock->s_domain == PF_INET6) */ +#endif /* CONFIG_NET_IPv4 */ + { + /* Notify the device driver that send data is available */ + + DEBUGASSERT(psock->s_domain == PF_INET6); +#ifdef CONFIG_NETDEV_MULTINIC + netdev_ipv6_txnotify(conn->u.ipv6.laddr, conn->u.ipv6.raddr); +#else + netdev_ipv6_txnotify(conn->u.ipv6.raddr); +#endif + } +#endif /* CONFIG_NET_IPv6 */ +} +#endif /* NET_TCP_HAVE_STACK */ + +/**************************************************************************** + * Name: tcp_close_disconnect + * + * Description: + * Break any current TCP connection + * + * Parameters: + * conn - TCP connection structure + * + * Returned Value: + * None + * + * Assumptions: + * Called from normal user-level logic + * + ****************************************************************************/ + +#ifdef NET_TCP_HAVE_STACK +static inline int tcp_close_disconnect(FAR struct socket *psock) +{ + struct tcp_close_s state; + FAR struct tcp_conn_s *conn; +#ifdef CONFIG_NET_SOLINGER + bool linger; +#endif + int ret = OK; + + /* Interrupts are disabled here to avoid race conditions */ + + net_lock(); + conn = (FAR struct tcp_conn_s *)psock->s_conn; + + /* If we have a semi-permanent write buffer callback in place, then + * release it now. + */ + +#ifdef CONFIG_NET_TCP_WRITE_BUFFERS + if (psock->s_sndcb) + { + psock->s_sndcb = NULL; + } +#endif + + DEBUGASSERT(conn != NULL); + + /* Check for the case where the host beat us and disconnected first */ + + if (conn->tcpstateflags == TCP_ESTABLISHED && + (state.cl_cb = tcp_callback_alloc(conn)) != NULL) + { + /* Set up to receive TCP data event callbacks */ + + state.cl_cb->flags = (TCP_NEWDATA | TCP_POLL | TCP_DISCONN_EVENTS); + state.cl_cb->event = tcp_close_interrupt; + +#ifdef CONFIG_NET_SOLINGER + /* Check for a lingering close */ + + linger = _SO_GETOPT(psock->s_options, SO_LINGER); + + /* Has a lingering close been requested */ + + if (linger) + { + /* A non-NULL value of the priv field means that lingering is + * enabled. + */ + + state.cl_cb->priv = (FAR void *)&state; + + /* Set up for the lingering wait */ + + state.cl_psock = psock; + state.cl_result = -EBUSY; + + /* This semaphore is used for signaling and, hence, should not have + * priority inheritance enabled. + */ + + sem_init(&state.cl_sem, 0, 0); + sem_setprotocol(&state.cl_sem, SEM_PRIO_NONE); + + /* Record the time that we started the wait (in ticks) */ + + state.cl_start = clock_systimer(); + } + else +#endif /* CONFIG_NET_SOLINGER */ + + { + /* We will close immediately. The NULL priv field signals this */ + + state.cl_cb->priv = NULL; + + /* No further references on the connection */ + + conn->crefs = 0; + } + + /* Notify the device driver of the availability of TX data */ + + tcp_close_txnotify(psock, conn); + +#ifdef CONFIG_NET_SOLINGER + /* Wait only if we are lingering */ + + if (linger) + { + /* Wait for the disconnect event */ + + (void)net_lockedwait(&state.cl_sem); + + /* We are now disconnected */ + + sem_destroy(&state.cl_sem); + tcp_callback_free(conn, state.cl_cb); + + /* Free the connection */ + + conn->crefs = 0; /* No more references on the connection */ + tcp_free(conn); /* Free network resources */ + + /* Get the result of the close */ + + ret = state.cl_result; + } +#endif /* CONFIG_NET_SOLINGER */ + } + else + { + tcp_free(conn); + } + + net_unlock(); + return ret; +} +#endif /* NET_TCP_HAVE_STACK */ + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: inet_close + * + * Description: + * Performs the close operation on an AF_INET or AF_INET6 socket instance + * + * Parameters: + * psock Socket instance + * + * Returned Value: + * 0 on success; -1 on error with errno set appropriately. + * + * Assumptions: + * + ****************************************************************************/ + +int inet_close(FAR struct socket *psock) +{ + /* Perform some pre-close operations for the AF_INET/AF_INET6 address + * types. + */ + + switch (psock->s_type) + { +#ifdef CONFIG_NET_TCP + case SOCK_STREAM: + { +#ifdef NET_TCP_HAVE_STACK + FAR struct tcp_conn_s *conn = psock->s_conn; + int ret; + + /* Is this the last reference to the connection structure (there + * could be more if the socket was dup'ed). + */ + + if (conn->crefs <= 1) + { + /* Yes... then perform the disconnection now */ + + tcp_unlisten(conn); /* No longer accepting connections */ + conn->crefs = 0; /* Discard our reference to the connection */ + + /* Break any current connections */ + + ret = tcp_close_disconnect(psock); + if (ret < 0) + { + /* This would normally occur only if there is a timeout + * from a lingering close. + */ + + nerr("ERROR: tcp_close_disconnect failed: %d\n", ret); + return ret; + } + + /* Stop the network monitor */ + + net_stopmonitor(conn); + } + else + { + /* No.. Just decrement the reference count */ + + conn->crefs--; + } +#else + nwarn("WARNING: SOCK_STREAM support is not available in this configuration\n"); + return -EAFNOSUPPORT; +#endif /* NET_TCP_HAVE_STACK */ + } + break; +#endif /* CONFIG_NET_TCP */ + +#ifdef CONFIG_NET_UDP + case SOCK_DGRAM: + { +#ifdef NET_UDP_HAVE_STACK + FAR struct udp_conn_s *conn = psock->s_conn; + + /* Is this the last reference to the connection structure (there + * could be more if the socket was dup'ed). + */ + + if (conn->crefs <= 1) + { + /* Yes... free the connection structure */ + + conn->crefs = 0; + udp_free(psock->s_conn); + } + else + { + /* No.. Just decrement the reference count */ + + conn->crefs--; + } +#else + nwarn("WARNING: SOCK_DGRAM support is not available in this configuration\n"); + return -EAFNOSUPPORT; +#endif /* NET_UDP_HAVE_STACK */ + } + break; +#endif /* CONFIG_NET_UDP */ + +#ifdef CONFIG_NET_USRSOCK + case SOCK_USRSOCK_TYPE: + { + FAR struct usrsock_conn_s *conn = psock->s_conn; + int ret; + + /* Is this the last reference to the connection structure (there + * could be more if the socket was dup'ed). + */ + + if (conn->crefs <= 1) + { + /* Yes... inform user-space daemon of socket close. */ + + ret = usrsock_close(conn); + + /* Free the connection structure */ + + conn->crefs = 0; + usrsock_free(psock->s_conn); + + if (ret < 0) + { + /* Return with error code, but free resources. */ + + nerr("ERROR: usrsock_close failed: %d\n", ret); + return ret; + } + } + else + { + /* No.. Just decrement the reference count */ + + conn->crefs--; + } + } + break; +#endif + + default: + return -EBADF; + } + + return OK; +} +#endif /* CONFIG_NET */ diff --git a/net/socket/inet_sockif.c b/net/socket/inet_sockif.c index 470315c2295..f1c1f9ae636 100644 --- a/net/socket/inet_sockif.c +++ b/net/socket/inet_sockif.c @@ -83,7 +83,8 @@ const struct sock_intf_s g_inet_sockif = inet_accept, /* si_accept */ inet_send, /* si_send */ inet_sendto, /* si_sendto */ - inet_recvfrom /* si_recvfrom */ + inet_recvfrom, /* si_recvfrom */ + inet_close /* si_close */ }; /**************************************************************************** diff --git a/net/socket/net_close.c b/net/socket/net_close.c index adb1812ad26..63cfb131b7d 100644 --- a/net/socket/net_close.c +++ b/net/socket/net_close.c @@ -38,7 +38,6 @@ ****************************************************************************/ #include -#ifdef CONFIG_NET #include #include @@ -48,450 +47,11 @@ #include #include -#include - -#include #include -#include -#include -#include -#ifdef CONFIG_NET_SOLINGER -# include -#endif - -#include "netdev/netdev.h" -#include "devif/devif.h" -#include "tcp/tcp.h" -#include "udp/udp.h" -#include "pkt/pkt.h" -#include "local/local.h" #include "socket/socket.h" -#include "usrsock/usrsock.h" -/**************************************************************************** - * Private Types - ****************************************************************************/ - -#ifdef NET_TCP_HAVE_STACK -struct tcp_close_s -{ - FAR struct devif_callback_s *cl_cb; /* Reference to TCP callback instance */ -#ifdef CONFIG_NET_SOLINGER - FAR struct socket *cl_psock; /* Reference to the TCP socket */ - sem_t cl_sem; /* Signals disconnect completion */ - int cl_result; /* The result of the close */ - systime_t cl_start; /* Time close started (in ticks) */ -#endif -}; -#endif - -/**************************************************************************** - * Private Functions - ****************************************************************************/ - -/**************************************************************************** - * Name: close_timeout - * - * Description: - * Check for a timeout on a lingering close. - * - * Parameters: - * pstate - close state structure - * - * Returned Value: - * TRUE:timeout FALSE:no timeout - * - * Assumptions: - * Running at the interrupt level - * - ****************************************************************************/ - -#if defined(NET_TCP_HAVE_STACK) && defined(CONFIG_NET_SOLINGER) -static inline int close_timeout(FAR struct tcp_close_s *pstate) -{ - FAR struct socket *psock = 0; - - /* Make sure that we are performing a lingering close */ - - if (pstate) - { - /* Yes Check for a timeout configured via setsockopts(SO_LINGER). - * If none... we well let the send wait forever. - */ - - psock = pstate->cl_psock; - if (psock && psock->s_linger != 0) - { - /* Check if the configured timeout has elapsed */ - - return net_timeo(pstate->cl_start, psock->s_linger); - } - } - - /* No timeout */ - - return FALSE; -} -#endif /* NET_TCP_HAVE_STACK && CONFIG_NET_SOLINGER */ - -/**************************************************************************** - * Name: netclose_interrupt - * - * Description: - * Handle network callback events. - * - * Parameters: - * conn - TCP connection structure - * - * Returned Value: - * None - * - * Assumptions: - * Called from normal user-level logic - * - ****************************************************************************/ - -#ifdef NET_TCP_HAVE_STACK -static uint16_t netclose_interrupt(FAR struct net_driver_s *dev, - FAR void *pvconn, FAR void *pvpriv, - uint16_t flags) -{ -#ifdef CONFIG_NET_SOLINGER - FAR struct tcp_close_s *pstate = (FAR struct tcp_close_s *)pvpriv; -#endif - FAR struct tcp_conn_s *conn = (FAR struct tcp_conn_s *)pvconn; - - DEBUGASSERT(conn != NULL); - - ninfo("conn: %p flags: %04x\n", conn, flags); - - /* TCP_DISCONN_EVENTS: - * TCP_CLOSE: The remote host has closed the connection - * TCP_ABORT: The remote host has aborted the connection - * TCP_TIMEDOUT: The remote did not respond, the connection timed out - * NETDEV_DOWN: The network device went down - */ - - if ((flags & TCP_DISCONN_EVENTS) != 0) - { - /* The disconnection is complete */ - -#ifdef CONFIG_NET_SOLINGER - /* pstate non-NULL means that we are performing a LINGERing close. */ - - if (pstate) - { - /* Wake up the waiting thread with a successful result */ - - pstate->cl_result = OK; - goto end_wait; - } - - /* Otherwise, nothing is waiting on the close event and we can perform - * the completion actions here. - */ - - else -#endif - { - /* Free connection resources */ - - tcp_free(conn); - - /* Stop further callbacks */ - - flags = 0; - } - } - -#ifdef CONFIG_NET_SOLINGER - /* Check for a timeout. */ - - else if (pstate && close_timeout(pstate)) - { - /* Yes.. Wake up the waiting thread and report the timeout */ - - nerr("ERROR: CLOSE timeout\n"); - pstate->cl_result = -ETIMEDOUT; - goto end_wait; - } - -#endif /* CONFIG_NET_SOLINGER */ - -#ifdef CONFIG_NET_TCP_WRITE_BUFFERS - /* Check if all outstanding bytes have been ACKed */ - - else if (conn->unacked != 0 || !sq_empty(&conn->write_q)) - { - /* No... we are still waiting for ACKs. Drop any received data, but - * do not yet report TCP_CLOSE in the response. - */ - - dev->d_len = 0; - flags = (flags & ~TCP_NEWDATA); - } - -#endif /* CONFIG_NET_TCP_WRITE_BUFFERS */ - - else - { - /* Drop data received in this state and make sure that TCP_CLOSE - * is set in the response - */ - - dev->d_len = 0; - flags = (flags & ~TCP_NEWDATA) | TCP_CLOSE; - } - - return flags; - -#ifdef CONFIG_NET_SOLINGER -end_wait: - pstate->cl_cb->flags = 0; - pstate->cl_cb->priv = NULL; - pstate->cl_cb->event = NULL; - sem_post(&pstate->cl_sem); - - ninfo("Resuming\n"); - return 0; -#endif -} -#endif /* NET_TCP_HAVE_STACK */ - -/**************************************************************************** - * Name: netclose_txnotify - * - * Description: - * Notify the appropriate device driver that we are have data ready to - * be send (TCP) - * - * Parameters: - * psock - Socket state structure - * conn - The TCP connection structure - * - * Returned Value: - * None - * - ****************************************************************************/ - -#ifdef NET_TCP_HAVE_STACK -static inline void netclose_txnotify(FAR struct socket *psock, - FAR struct tcp_conn_s *conn) -{ -#ifdef CONFIG_NET_IPv4 -#ifdef CONFIG_NET_IPv6 - /* If both IPv4 and IPv6 support are enabled, then we will need to select - * the device driver using the appropriate IP domain. - */ - - if (psock->s_domain == PF_INET) -#endif - { - /* Notify the device driver that send data is available */ - -#ifdef CONFIG_NETDEV_MULTINIC - netdev_ipv4_txnotify(conn->u.ipv4.laddr, conn->u.ipv4.raddr); -#else - netdev_ipv4_txnotify(conn->u.ipv4.raddr); -#endif - } -#endif /* CONFIG_NET_IPv4 */ - -#ifdef CONFIG_NET_IPv6 -#ifdef CONFIG_NET_IPv4 - else /* if (psock->s_domain == PF_INET6) */ -#endif /* CONFIG_NET_IPv4 */ - { - /* Notify the device driver that send data is available */ - - DEBUGASSERT(psock->s_domain == PF_INET6); -#ifdef CONFIG_NETDEV_MULTINIC - netdev_ipv6_txnotify(conn->u.ipv6.laddr, conn->u.ipv6.raddr); -#else - netdev_ipv6_txnotify(conn->u.ipv6.raddr); -#endif - } -#endif /* CONFIG_NET_IPv6 */ -} -#endif /* NET_TCP_HAVE_STACK */ - -/**************************************************************************** - * Name: netclose_disconnect - * - * Description: - * Break any current TCP connection - * - * Parameters: - * conn - TCP connection structure - * - * Returned Value: - * None - * - * Assumptions: - * Called from normal user-level logic - * - ****************************************************************************/ - -#ifdef NET_TCP_HAVE_STACK -static inline int netclose_disconnect(FAR struct socket *psock) -{ - struct tcp_close_s state; - FAR struct tcp_conn_s *conn; -#ifdef CONFIG_NET_SOLINGER - bool linger; -#endif - int ret = OK; - - /* Interrupts are disabled here to avoid race conditions */ - - net_lock(); - conn = (FAR struct tcp_conn_s *)psock->s_conn; - - /* If we have a semi-permanent write buffer callback in place, then - * release it now. - */ - -#ifdef CONFIG_NET_TCP_WRITE_BUFFERS - if (psock->s_sndcb) - { - psock->s_sndcb = NULL; - } -#endif - - DEBUGASSERT(conn != NULL); - - /* Check for the case where the host beat us and disconnected first */ - - if (conn->tcpstateflags == TCP_ESTABLISHED && - (state.cl_cb = tcp_callback_alloc(conn)) != NULL) - { - /* Set up to receive TCP data event callbacks */ - - state.cl_cb->flags = (TCP_NEWDATA | TCP_POLL | TCP_DISCONN_EVENTS); - state.cl_cb->event = netclose_interrupt; - -#ifdef CONFIG_NET_SOLINGER - /* Check for a lingering close */ - - linger = _SO_GETOPT(psock->s_options, SO_LINGER); - - /* Has a lingering close been requested */ - - if (linger) - { - /* A non-NULL value of the priv field means that lingering is - * enabled. - */ - - state.cl_cb->priv = (FAR void *)&state; - - /* Set up for the lingering wait */ - - state.cl_psock = psock; - state.cl_result = -EBUSY; - - /* This semaphore is used for signaling and, hence, should not have - * priority inheritance enabled. - */ - - sem_init(&state.cl_sem, 0, 0); - sem_setprotocol(&state.cl_sem, SEM_PRIO_NONE); - - /* Record the time that we started the wait (in ticks) */ - - state.cl_start = clock_systimer(); - } - else -#endif /* CONFIG_NET_SOLINGER */ - - { - /* We will close immediately. The NULL priv field signals this */ - - state.cl_cb->priv = NULL; - - /* No further references on the connection */ - - conn->crefs = 0; - } - - /* Notify the device driver of the availability of TX data */ - - netclose_txnotify(psock, conn); - -#ifdef CONFIG_NET_SOLINGER - /* Wait only if we are lingering */ - - if (linger) - { - /* Wait for the disconnect event */ - - (void)net_lockedwait(&state.cl_sem); - - /* We are now disconnected */ - - sem_destroy(&state.cl_sem); - tcp_callback_free(conn, state.cl_cb); - - /* Free the connection */ - - conn->crefs = 0; /* No more references on the connection */ - tcp_free(conn); /* Free network resources */ - - /* Get the result of the close */ - - ret = state.cl_result; - } -#endif /* CONFIG_NET_SOLINGER */ - } - else - { - tcp_free(conn); - } - - net_unlock(); - return ret; -} -#endif /* NET_TCP_HAVE_STACK */ - -/**************************************************************************** - * Name: local_close - * - * Description: - * Performs the close operation on a local socket instance - * - * Parameters: - * psock Socket instance - * - * Returned Value: - * 0 on success; -1 on error with errno set appropriately. - * - * Assumptions: - * - ****************************************************************************/ - -#ifdef CONFIG_NET_LOCAL -static void local_close(FAR struct socket *psock) -{ - FAR struct local_conn_s *conn = psock->s_conn; - - /* Is this the last reference to the connection structure (there could - * be more if the socket was dup'ed). - */ - - if (conn->lc_crefs <= 1) - { - conn->lc_crefs = 0; - local_release(conn); - } - else - { - /* No.. Just decrement the reference count */ - - conn->lc_crefs--; - } -} -#endif /* CONFIG_NET_LOCAL */ +#ifdef CONFIG_NET /**************************************************************************** * Public Functions @@ -516,6 +76,7 @@ static void local_close(FAR struct socket *psock) int psock_close(FAR struct socket *psock) { int errcode; + int ret; /* Verify that the sockfd corresponds to valid, allocated socket */ @@ -535,185 +96,17 @@ int psock_close(FAR struct socket *psock) if (psock->s_crefs <= 1 && psock->s_conn != NULL) { - /* Perform local side of the close depending on the protocol type */ + /* Let the address family's close() method handle the operation */ - switch (psock->s_type) + DEBUGASSERT(psock->s_sockif != NULL && psock->s_sockif->si_close != NULL); + ret = psock->s_sockif->si_close(psock); + + /* Was the close successful */ + + if (ret < 0) { -#if defined(CONFIG_NET_TCP) || defined(CONFIG_NET_LOCAL_STREAM) - case SOCK_STREAM: - { -#ifdef CONFIG_NET_LOCAL_STREAM -#ifdef CONFIG_NET_TCP - if (psock->s_domain == PF_LOCAL) -#endif - { - /* Release our reference to the local connection structure */ - - local_close(psock); - } -#endif /* CONFIG_NET_LOCAL_STREAM */ - -#ifdef CONFIG_NET_TCP -#ifdef CONFIG_NET_LOCAL_STREAM - else -#endif - { -#ifdef NET_TCP_HAVE_STACK - FAR struct tcp_conn_s *conn = psock->s_conn; - - /* Is this the last reference to the connection structure - * (there could be more if the socket was dup'ed). - */ - - if (conn->crefs <= 1) - { - /* Yes... then perform the disconnection now */ - - tcp_unlisten(conn); /* No longer accepting connections */ - conn->crefs = 0; /* Discard our reference to the connection */ - - /* Break any current connections */ - - errcode = netclose_disconnect(psock); - if (errcode < 0) - { - /* This would normally occur only if there is a - * timeout from a lingering close. - */ - - goto errout_with_psock; - } - - /* Stop the network monitor */ - - net_stopmonitor(conn); - } - else - { - /* No.. Just decrement the reference count */ - - conn->crefs--; - } -#endif /* NET_TCP_HAVE_STACK */ - } -#endif /* CONFIG_NET_TCP || CONFIG_NET_LOCAL_STREAM */ - } - break; -#endif - -#if defined(CONFIG_NET_UDP) || defined(CONFIG_NET_LOCAL_DGRAM) - case SOCK_DGRAM: - { -#ifdef CONFIG_NET_LOCAL_DGRAM -#ifdef CONFIG_NET_UDP - if (psock->s_domain == PF_LOCAL) -#endif - { - /* Release our reference to the local connection structure */ - - local_close(psock); - } -#endif /* CONFIG_NET_LOCAL_DGRAM */ - -#ifdef CONFIG_NET_UDP -#ifdef CONFIG_NET_LOCAL_DGRAM - else -#endif - { -#ifdef NET_UDP_HAVE_STACK - FAR struct udp_conn_s *conn = psock->s_conn; - - /* Is this the last reference to the connection structure - * (there could be more if the socket was dup'ed). - */ - - if (conn->crefs <= 1) - { - /* Yes... free the connection structure */ - - conn->crefs = 0; - udp_free(psock->s_conn); - } - else - { - /* No.. Just decrement the reference count */ - - conn->crefs--; - } -#endif /* NET_UDP_HAVE_STACK */ - } -#endif /* CONFIG_NET_UDP || CONFIG_NET_LOCAL_DGRAM */ - } - break; -#endif - -#ifdef CONFIG_NET_PKT - case SOCK_RAW: - { - FAR struct pkt_conn_s *conn = psock->s_conn; - - /* Is this the last reference to the connection structure (there - * could be more if the socket was dup'ed). - */ - - if (conn->crefs <= 1) - { - /* Yes... free the connection structure */ - - conn->crefs = 0; /* No more references on the connection */ - pkt_free(psock->s_conn); /* Free network resources */ - } - else - { - /* No.. Just decrement the reference count */ - - conn->crefs--; - } - } - break; -#endif - -#ifdef CONFIG_NET_USRSOCK - case SOCK_USRSOCK_TYPE: - { - FAR struct usrsock_conn_s *conn = psock->s_conn; - - /* Is this the last reference to the connection structure (there - * could be more if the socket was dup'ed). - */ - - if (conn->crefs <= 1) - { - /* Yes... inform user-space daemon of socket close. */ - - errcode = usrsock_close(conn); - - /* Free the connection structure */ - - conn->crefs = 0; - usrsock_free(psock->s_conn); - - if (errcode < 0) - { - /* Return with error code, but free resources. */ - - errcode = -errcode; - goto errout_with_psock; - } - } - else - { - /* No.. Just decrement the reference count */ - - conn->crefs--; - } - } - break; -#endif - - default: - errcode = EBADF; - goto errout; + errcode = -ret; + goto errout; } } @@ -722,11 +115,6 @@ int psock_close(FAR struct socket *psock) sock_release(psock); return OK; -#if defined(NET_TCP_HAVE_STACK) || defined(CONFIG_NET_USRSOCK) -errout_with_psock: - sock_release(psock); -#endif - errout: set_errno(errcode); return ERROR; diff --git a/net/socket/socket.h b/net/socket/socket.h index fec553de298..3465f74b964 100644 --- a/net/socket/socket.h +++ b/net/socket/socket.h @@ -506,6 +506,24 @@ ssize_t inet_recvfrom(FAR struct socket *psock, FAR void *buf, size_t len, int flags, FAR struct sockaddr *from, FAR socklen_t *fromlen); +/**************************************************************************** + * Name: inet_close + * + * Description: + * Performs the close operation on an AF_INET or AF_INET6 socket instance + * + * Parameters: + * psock Socket instance + * + * Returned Value: + * 0 on success; -1 on error with errno set appropriately. + * + * Assumptions: + * + ****************************************************************************/ + +int inet_close(FAR struct socket *psock); + #undef EXTERN #if defined(__cplusplus) } From 5033a6def79ffeb027a71ba16684d40c9c817f16 Mon Sep 17 00:00:00 2001 From: Gregory Nutt Date: Thu, 13 Jul 2017 13:36:18 -0600 Subject: [PATCH 18/18] Socket interface: Added addref() and sockcaps() interfaces. --- include/nuttx/net/net.h | 45 ++++++++----- libc/stdlib/lib_mkstemp.c | 5 +- net/local/local_sockif.c | 77 +++++++++++++++++++---- net/pkt/pkt_sockif.c | 79 ++++++++++++++++++----- net/socket/accept.c | 8 +-- net/socket/inet_recvfrom.c | 2 +- net/socket/inet_sockif.c | 125 +++++++++++++++++++++++++++++++++---- net/socket/net_clone.c | 58 +++-------------- net/socket/net_vfcntl.c | 71 +++++---------------- 9 files changed, 299 insertions(+), 171 deletions(-) diff --git a/include/nuttx/net/net.h b/include/nuttx/net/net.h index 93a6deadc88..7dee03d5ae4 100644 --- a/include/nuttx/net/net.h +++ b/include/nuttx/net/net.h @@ -64,6 +64,11 @@ # define __SOCKFD_OFFSET 0 #endif +/* Capabilities of a socket */ + +#define SOCKCAP_NONBLOCKING (1 << 0) /* Bit 0: Socket supports non-blocking + * operation. */ + /**************************************************************************** * Public Types ****************************************************************************/ @@ -94,6 +99,10 @@ typedef uint16_t sockopt_t; typedef uint16_t socktimeo_t; +/* This type defines the type of the socket capabilities set */ + +typedef uint8_t sockcaps_t; + /* This callbacks are socket operations that may be performed on a socket of * a given address family. */ @@ -102,23 +111,25 @@ struct socket; /* Forward reference */ struct sock_intf_s { - CODE int (*si_setup)(FAR struct socket *psock, int protocol); - CODE int (*si_bind)(FAR struct socket *psock, - FAR const struct sockaddr *addr, socklen_t addrlen); - CODE int (*si_listen)(FAR struct socket *psock, int backlog); - CODE int (*si_connect)(FAR struct socket *psock, - FAR const struct sockaddr *addr, socklen_t addrlen); - CODE int (*si_accept)(FAR struct socket *psock, FAR struct sockaddr *addr, - FAR socklen_t *addrlen, FAR struct socket *newsock); - CODE ssize_t (*si_send)(FAR struct socket *psock, FAR const void *buf, - size_t len, int flags); - CODE ssize_t (*si_sendto)(FAR struct socket *psock, FAR const void *buf, - size_t len, int flags, FAR const struct sockaddr *to, - socklen_t tolen); - CODE ssize_t (*si_recvfrom)(FAR struct socket *psock, FAR void *buf, - size_t len, int flags, FAR struct sockaddr *from, - FAR socklen_t *fromlen); - CODE int (*si_close)(FAR struct socket *psock); + CODE int (*si_setup)(FAR struct socket *psock, int protocol); + CODE sockcaps_t (*si_sockcaps)(FAR struct socket *psock); + CODE void (*si_addref)(FAR struct socket *psock); + CODE int (*si_bind)(FAR struct socket *psock, + FAR const struct sockaddr *addr, socklen_t addrlen); + CODE int (*si_listen)(FAR struct socket *psock, int backlog); + CODE int (*si_connect)(FAR struct socket *psock, + FAR const struct sockaddr *addr, socklen_t addrlen); + CODE int (*si_accept)(FAR struct socket *psock, FAR struct sockaddr *addr, + FAR socklen_t *addrlen, FAR struct socket *newsock); + CODE ssize_t (*si_send)(FAR struct socket *psock, FAR const void *buf, + size_t len, int flags); + CODE ssize_t (*si_sendto)(FAR struct socket *psock, FAR const void *buf, + size_t len, int flags, FAR const struct sockaddr *to, + socklen_t tolen); + CODE ssize_t (*si_recvfrom)(FAR struct socket *psock, FAR void *buf, + size_t len, int flags, FAR struct sockaddr *from, + FAR socklen_t *fromlen); + CODE int (*si_close)(FAR struct socket *psock); }; /* This is the internal representation of a socket reference by a file diff --git a/libc/stdlib/lib_mkstemp.c b/libc/stdlib/lib_mkstemp.c index 498d424e373..1e85f2b2d84 100644 --- a/libc/stdlib/lib_mkstemp.c +++ b/libc/stdlib/lib_mkstemp.c @@ -209,9 +209,8 @@ static void copy_base62(FAR char *dest, int len) * that full path. * * Returned Value: - * - * Upon successful completion, mkstemp() returns an open file descriptor. - * Otherwise, -1 is returned if no suitable file could be created. + * Upon successful completion, mkstemp() returns an open file descriptor. + * Otherwise, -1 is returned if no suitable file could be created. * ****************************************************************************/ diff --git a/net/local/local_sockif.c b/net/local/local_sockif.c index 1b8d42b6408..56c4b513a96 100644 --- a/net/local/local_sockif.c +++ b/net/local/local_sockif.c @@ -57,19 +57,21 @@ * Private Function Prototypes ****************************************************************************/ -static int local_setup(FAR struct socket *psock, int protocol); -static int local_bind(FAR struct socket *psock, - FAR const struct sockaddr *addr, socklen_t addrlen); -static int local_connect(FAR struct socket *psock, - FAR const struct sockaddr *addr, socklen_t addrlen); -static int local_accept(FAR struct socket *psock, FAR struct sockaddr *addr, - FAR socklen_t *addrlen, FAR struct socket *newsock); -static ssize_t local_send(FAR struct socket *psock, FAR const void *buf, - size_t len, int flags); -static ssize_t local_sendto(FAR struct socket *psock, FAR const void *buf, - size_t len, int flags, FAR const struct sockaddr *to, - socklen_t tolen); -static int local_close(FAR struct socket *psock); +static int local_setup(FAR struct socket *psock, int protocol); +static sockcaps_t local_sockcaps(FAR struct socket *psock); +static void local_addref(FAR struct socket *psock); +static int local_bind(FAR struct socket *psock, + FAR const struct sockaddr *addr, socklen_t addrlen); +static int local_connect(FAR struct socket *psock, + FAR const struct sockaddr *addr, socklen_t addrlen); +static int local_accept(FAR struct socket *psock, FAR struct sockaddr *addr, + FAR socklen_t *addrlen, FAR struct socket *newsock); +static ssize_t local_send(FAR struct socket *psock, FAR const void *buf, + size_t len, int flags); +static ssize_t local_sendto(FAR struct socket *psock, FAR const void *buf, + size_t len, int flags, FAR const struct sockaddr *to, + socklen_t tolen); +static int local_close(FAR struct socket *psock); /**************************************************************************** * Public Data @@ -78,6 +80,8 @@ static int local_close(FAR struct socket *psock); const struct sock_intf_s g_local_sockif = { local_setup, /* si_setup */ + local_sockcaps, /* si_sockcaps */ + local_addref, /* si_addref */ local_bind, /* si_bind */ local_listen, /* si_listen */ local_connect, /* si_connect */ @@ -183,6 +187,53 @@ static int local_setup(FAR struct socket *psock, int protocol) } } +/**************************************************************************** + * Name: local_sockcaps + * + * Description: + * Return the bit encoded capabilities of this socket. + * + * Parameters: + * psock - Socket structure of the socket whose capabilities are being + * queried. + * + * Returned Value: + * The set of socket cababilities is returned. + * + ****************************************************************************/ + +static sockcaps_t local_sockcaps(FAR struct socket *psock) +{ + return SOCKCAP_NONBLOCKING; +} + +/**************************************************************************** + * Name: local_addref + * + * Description: + * Increment the refernce count on the underlying connection structure. + * + * Parameters: + * psock - Socket structure of the socket whose reference count will be + * incremented. + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void local_addref(FAR struct socket *psock) +{ + FAR struct local_conn_s *conn; + + DEBUGASSERT(psock != NULL && psock->s_conn != NULL && + psock->s_domain == PF_LOCAL); + + conn = psock->s_conn; + DEBUGASSERT(conn->lc_crefs > 0 && conn->lc_crefs < 255); + conn->lc_crefs++; +} + /**************************************************************************** * Name: local_bind * diff --git a/net/pkt/pkt_sockif.c b/net/pkt/pkt_sockif.c index ded05d6ea89..ae2542d619f 100644 --- a/net/pkt/pkt_sockif.c +++ b/net/pkt/pkt_sockif.c @@ -55,20 +55,22 @@ * Private Function Prototypes ****************************************************************************/ -static int pkt_setup(FAR struct socket *psock, int protocol); -static int pkt_bind(FAR struct socket *psock, - FAR const struct sockaddr *addr, socklen_t addrlen); -static int pkt_listen(FAR struct socket *psock, int backlog); -static int pkt_connect(FAR struct socket *psock, - FAR const struct sockaddr *addr, socklen_t addrlen); -static int pkt_accept(FAR struct socket *psock, FAR struct sockaddr *addr, - FAR socklen_t *addrlen, FAR struct socket *newsock); -static ssize_t pkt_send(FAR struct socket *psock, FAR const void *buf, - size_t len, int flags); -static ssize_t pkt_sendto(FAR struct socket *psock, FAR const void *buf, - size_t len, int flags, FAR const struct sockaddr *to, - socklen_t tolen); -static int pkt_close(FAR struct socket *psock); +static int pkt_setup(FAR struct socket *psock, int protocol); +static sockcaps_t pkt_sockcaps(FAR struct socket *psock); +static void pkt_addref(FAR struct socket *psock); +static int pkt_bind(FAR struct socket *psock, + FAR const struct sockaddr *addr, socklen_t addrlen); +static int pkt_listen(FAR struct socket *psock, int backlog); +static int pkt_connect(FAR struct socket *psock, + FAR const struct sockaddr *addr, socklen_t addrlen); +static int pkt_accept(FAR struct socket *psock, FAR struct sockaddr *addr, + FAR socklen_t *addrlen, FAR struct socket *newsock); +static ssize_t pkt_send(FAR struct socket *psock, FAR const void *buf, + size_t len, int flags); +static ssize_t pkt_sendto(FAR struct socket *psock, FAR const void *buf, + size_t len, int flags, FAR const struct sockaddr *to, + socklen_t tolen); +static int pkt_close(FAR struct socket *psock); /**************************************************************************** * Public Data @@ -77,6 +79,8 @@ static int pkt_close(FAR struct socket *psock); const struct sock_intf_s g_pkt_sockif = { pkt_setup, /* si_setup */ + pkt_sockcaps, /* si_sockcaps */ + pkt_addref, /* si_addref */ pkt_bind, /* si_bind */ pkt_listen, /* si_listen */ pkt_connect, /* si_connect */ @@ -164,6 +168,53 @@ static int pkt_setup(FAR struct socket *psock, int protocol) } } +/**************************************************************************** + * Name: pkt_sockcaps + * + * Description: + * Return the bit encoded capabilities of this socket. + * + * Parameters: + * psock - Socket structure of the socket whose capabilities are being + * queried. + * + * Returned Value: + * The set of socket cababilities is returned. + * + ****************************************************************************/ + +static sockcaps_t pkt_sockcaps(FAR struct socket *psock) +{ + return 0; +} + +/**************************************************************************** + * Name: pkt_addref + * + * Description: + * Increment the refernce count on the underlying connection structure. + * + * Parameters: + * psock - Socket structure of the socket whose reference count will be + * incremented. + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void pkt_addref(FAR struct socket *psock) +{ + FAR struct pkt_conn_s *conn; + + DEBUGASSERT(psock != NULL && psock->s_conn != NULL && + psock->s_type == SOCK_RAW); + + conn = psock->s_conn; + DEBUGASSERT(conn->crefs > 0 && conn->crefs < 255); + conn->crefs++; +} + /**************************************************************************** * Name: pkt_connect * diff --git a/net/socket/accept.c b/net/socket/accept.c index 093b16e438d..48b76d15e04 100644 --- a/net/socket/accept.c +++ b/net/socket/accept.c @@ -38,8 +38,6 @@ ****************************************************************************/ #include -#if defined(CONFIG_NET) && CONFIG_NSOCKET_DESCRIPTORS > 0 && \ - (defined(CONFIG_NET_TCP) || defined(CONFIG_NET_LOCAL_STREAM)) #include #include @@ -56,6 +54,8 @@ #include "socket/socket.h" #include "usrsock/usrsock.h" +#if defined(CONFIG_NET) && CONFIG_NSOCKET_DESCRIPTORS > 0 + /**************************************************************************** * Public Functions ****************************************************************************/ @@ -129,9 +129,7 @@ int psock_accept(FAR struct socket *psock, FAR struct sockaddr *addr, FAR socklen_t *addrlen, FAR struct socket *newsock) { int errcode; -#if defined(NET_TCP_HAVE_STACK) || defined(CONFIG_NET_LOCAL_STREAM) int ret; -#endif DEBUGASSERT(psock != NULL && psock->s_conn != NULL && newsock != NULL); @@ -323,4 +321,4 @@ errout: return ERROR; } -#endif /* CONFIG_NET && CONFIG_NSOCKET_DESCRIPTORS && (CONFIG_NET_TCP || CONFIG_NET_LOCAL_STREAM) */ +#endif /* CONFIG_NET && CONFIG_NSOCKET_DESCRIPTORS */ diff --git a/net/socket/inet_recvfrom.c b/net/socket/inet_recvfrom.c index 3e29b93e4ba..d908e12cd40 100644 --- a/net/socket/inet_recvfrom.c +++ b/net/socket/inet_recvfrom.c @@ -1648,7 +1648,7 @@ ssize_t inet_recvfrom(FAR struct socket *psock, FAR void *buf, size_t len, #endif } break; -#endif /* CONFIG_NET_UDP || CONFIG_NET_LOCAL_DGRAM */ +#endif /* CONFIG_NET_UDP */ #ifdef CONFIG_NET_USRSOCK case SOCK_USRSOCK_TYPE: diff --git a/net/socket/inet_sockif.c b/net/socket/inet_sockif.c index f1c1f9ae636..6ef3f8b824c 100644 --- a/net/socket/inet_sockif.c +++ b/net/socket/inet_sockif.c @@ -58,17 +58,20 @@ * Private Function Prototypes ****************************************************************************/ -static int inet_setup(FAR struct socket *psock, int protocol); -static int inet_bind(FAR struct socket *psock, - FAR const struct sockaddr *addr, socklen_t addrlen); -static int inet_listen(FAR struct socket *psock, int backlog); -static int inet_accept(FAR struct socket *psock, FAR struct sockaddr *addr, - FAR socklen_t *addrlen, FAR struct socket *newsock); -static ssize_t inet_send(FAR struct socket *psock, FAR const void *buf, - size_t len, int flags); -static ssize_t inet_sendto(FAR struct socket *psock, FAR const void *buf, - size_t len, int flags, FAR const struct sockaddr *to, - socklen_t tolen); +static int inet_setup(FAR struct socket *psock, int protocol); +static sockcaps_t inet_sockcaps(FAR struct socket *psock); +static void inet_addref(FAR struct socket *psock); +static int inet_bind(FAR struct socket *psock, + FAR const struct sockaddr *addr, socklen_t addrlen); +static int inet_listen(FAR struct socket *psock, int backlog); +static int inet_accept(FAR struct socket *psock, + FAR struct sockaddr *addr, FAR socklen_t *addrlen, + FAR struct socket *newsock); +static ssize_t inet_send(FAR struct socket *psock, FAR const void *buf, + size_t len, int flags); +static ssize_t inet_sendto(FAR struct socket *psock, FAR const void *buf, + size_t len, int flags, FAR const struct sockaddr *to, + socklen_t tolen); /**************************************************************************** * Public Data @@ -77,6 +80,8 @@ static ssize_t inet_sendto(FAR struct socket *psock, FAR const void *buf, const struct sock_intf_s g_inet_sockif = { inet_setup, /* si_setup */ + inet_sockcaps, /* si_sockcaps */ + inet_addref, /* si_addref */ inet_bind, /* si_bind */ inet_listen, /* si_listen */ inet_connect, /* si_connect */ @@ -317,6 +322,104 @@ static int inet_setup(FAR struct socket *psock, int protocol) } } +/**************************************************************************** + * Name: inet_sockcaps + * + * Description: + * Return the bit encoded capabilities of this socket. + * + * Parameters: + * psock - Socket structure of the socket whose capabilities are being + * queried. + * + * Returned Value: + * The non-negative set of socket cababilities is returned. + * + ****************************************************************************/ + +static sockcaps_t inet_sockcaps(FAR struct socket *psock) +{ + switch (psock->s_type) + { +#ifdef NET_TCP_HAVE_STACK + case SOCK_STREAM: +#ifdef CONFIG_NET_TCP_READAHEAD + return SOCKCAP_NONBLOCKING; +#else + return 0; +#endif +#endif + +#ifdef NET_UDP_HAVE_STACK + case SOCK_DGRAM: +#ifdef CONFIG_NET_UDP_READAHEAD + return SOCKCAP_NONBLOCKING; +#else + return 0; +#endif +#endif + +#ifdef CONFIG_NET_USRSOCK + case SOCK_USRSOCK_TYPE: + return SOCKCAP_NONBLOCKING; +#endif + + default: + return 0; + } +} + +/**************************************************************************** + * Name: inet_addref + * + * Description: + * Increment the refernce count on the underlying connection structure. + * + * Parameters: + * psock - Socket structure of the socket whose reference count will be + * incremented. + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void inet_addref(FAR struct socket *psock) +{ + DEBUGASSERT(psock != NULL && psock->s_conn != NULL); + +#ifdef NET_TCP_HAVE_STACK + if (psock->s_type == SOCK_STREAM) + { + FAR struct tcp_conn_s *conn = psock->s_conn; + DEBUGASSERT(conn->crefs > 0 && conn->crefs < 255); + conn->crefs++; + } + else +#endif +#ifdef NET_UDP_HAVE_STACK + if (psock->s_type == SOCK_DGRAM) + { + FAR struct udp_conn_s *conn = psock->s_conn; + DEBUGASSERT(conn->crefs > 0 && conn->crefs < 255); + conn->crefs++; + } + else +#endif +#ifdef CONFIG_NET_USRSOCK + if (psock->s_type == SOCK_USRSOCK_TYPE) + { + FAR struct usrsock_conn_s *conn = psock->s_conn; + DEBUGASSERT(conn->crefs > 0 && conn->crefs < 255); + conn->crefs++; + } + else +#endif + { + nerr("ERROR: Unsupported type: %d\n", psock->s_type); + } +} + /**************************************************************************** * Name: inet_bind * diff --git a/net/socket/net_clone.c b/net/socket/net_clone.c index 5c417027747..7ab3852574d 100644 --- a/net/socket/net_clone.c +++ b/net/socket/net_clone.c @@ -95,60 +95,16 @@ int net_clone(FAR struct socket *psock1, FAR struct socket *psock2) * instance for TCP send */ #endif - /* Increment the reference count on the connection */ + /* Increment the reference count on the socket */ - DEBUGASSERT(psock2->s_conn); psock2->s_crefs = 1; /* One reference on the new socket itself */ -#ifdef CONFIG_NET_LOCAL - if (psock2->s_domain == PF_LOCAL) - { - FAR struct local_conn_s *conn = psock2->s_conn; - DEBUGASSERT(conn->lc_crefs > 0 && conn->lc_crefs < 255); - conn->lc_crefs++; - } - else -#endif -#ifdef CONFIG_NET_PKT - if (psock2->s_type == SOCK_RAW) - { - FAR struct pkt_conn_s *conn = psock2->s_conn; - DEBUGASSERT(conn->crefs > 0 && conn->crefs < 255); - conn->crefs++; - } - else -#endif -#ifdef NET_TCP_HAVE_STACK - if (psock2->s_type == SOCK_STREAM) - { - FAR struct tcp_conn_s *conn = psock2->s_conn; - DEBUGASSERT(conn->crefs > 0 && conn->crefs < 255); - conn->crefs++; - } - else -#endif -#ifdef NET_UDP_HAVE_STACK - if (psock2->s_type == SOCK_DGRAM) - { - FAR struct udp_conn_s *conn = psock2->s_conn; - DEBUGASSERT(conn->crefs > 0 && conn->crefs < 255); - conn->crefs++; - } - else -#endif -#ifdef CONFIG_NET_USRSOCK - if (psock2->s_type == SOCK_USRSOCK_TYPE) - { - FAR struct usrsock_conn_s *conn = psock2->s_conn; - DEBUGASSERT(conn->crefs > 0 && conn->crefs < 255); - conn->crefs++; - } - else -#endif - { - nerr("ERROR: Unsupported type: %d\n", psock2->s_type); - ret = -EBADF; - } + /* Increment the reference count on the underlying connection structure + * for this address family type. + */ + + DEBUGASSERT(psock2->s_sockif != NULL && psock2->s_sockif->si_addref != NULL); + psock2->s_sockif->si_addref(psock2); net_unlock(); return ret; diff --git a/net/socket/net_vfcntl.c b/net/socket/net_vfcntl.c index 79df78379bc..d96988515f0 100644 --- a/net/socket/net_vfcntl.c +++ b/net/socket/net_vfcntl.c @@ -84,7 +84,7 @@ int net_vfcntl(int sockfd, int cmd, va_list ap) /* Verify that the sockfd corresponds to valid, allocated socket */ - if (!psock || psock->s_crefs <= 0) + if (psock == NULL || psock->s_crefs <= 0) { errcode = EBADF; goto errout; @@ -140,38 +140,25 @@ int net_vfcntl(int sockfd, int cmd, va_list ap) */ { + sockcaps_t sockcaps; + /* This summarizes the behavior of all NuttX sockets */ ret = O_RDWR | O_SYNC | O_RSYNC; -#if defined(CONFIG_NET_LOCAL) || defined(CONFIG_NET_TCP_READAHEAD) || \ - defined(CONFIG_NET_UDP_READAHEAD) /* Unix domain sockets may be non-blocking. TCP/IP and UDP/IP * sockets may also be non-blocking if read-ahead is enabled */ - if ((0 -#ifdef CONFIG_NET_LOCAL - || psock->s_domain == PF_LOCAL /* Unix domain stream or datagram */ -#endif -#ifdef CONFIG_NET_TCP_READAHEAD - || psock->s_type == SOCK_STREAM /* IP or Unix domain stream */ -#endif -#ifdef CONFIG_NET_UDP_READAHEAD - || psock->s_type == SOCK_DGRAM /* IP or Unix domain datagram */ -#endif - ) && _SS_ISNONBLOCK(psock->s_flags)) - { - ret |= O_NONBLOCK; - } -#endif /* CONFIG_NET_LOCAL || CONFIG_NET_TCP_READAHEAD || CONFIG_NET_UDP_READAHEAD */ + DEBUGASSERT(psock->s_sockif != NULL && + psock->s_sockif->si_sockcaps != NULL); + sockcaps = psock->s_sockif->si_sockcaps(psock); -#ifdef CONFIG_NET_USRSOCK - if (psock->s_type == SOCK_USRSOCK_TYPE && _SS_ISNONBLOCK(psock->s_flags)) + if ((sockcaps & SOCKCAP_NONBLOCKING) != 0 && + _SS_ISNONBLOCK(psock->s_flags)) { ret |= O_NONBLOCK; } -#endif } break; @@ -185,16 +172,19 @@ int net_vfcntl(int sockfd, int cmd, va_list ap) */ { -#if defined(CONFIG_NET_LOCAL) || defined(CONFIG_NET_TCP_READAHEAD) || \ - defined(CONFIG_NET_UDP_READAHEAD) || defined(CONFIG_NET_USRSOCK) /* Non-blocking is the only configurable option. And it applies * only Unix domain sockets and to read operations on TCP/IP * and UDP/IP sockets when read-ahead is enabled. */ int mode = va_arg(ap, int); -#if defined(CONFIG_NET_LOCAL_STREAM) || defined(CONFIG_NET_TCP_READAHEAD) - if (psock->s_type == SOCK_STREAM) /* IP or Unix domain stream */ + sockcaps_t sockcaps; + + DEBUGASSERT(psock->s_sockif != NULL && + psock->s_sockif->si_sockcaps != NULL); + sockcaps = psock->s_sockif->si_sockcaps(psock); + + if ((sockcaps & SOCKCAP_NONBLOCKING) != 0) { if ((mode & O_NONBLOCK) != 0) { @@ -206,37 +196,6 @@ int net_vfcntl(int sockfd, int cmd, va_list ap) } } else -#endif -#if defined(CONFIG_NET_LOCAL_DGRAM) || defined(CONFIG_NET_UDP_READAHEAD) - if (psock->s_type == SOCK_DGRAM) /* IP or Unix domain datagram */ - { - if ((mode & O_NONBLOCK) != 0) - { - psock->s_flags |= _SF_NONBLOCK; - } - else - { - psock->s_flags &= ~_SF_NONBLOCK; - } - } - else -#endif -#if defined(CONFIG_NET_USRSOCK) - if (psock->s_type == SOCK_USRSOCK_TYPE) /* usrsock socket */ - { - if ((mode & O_NONBLOCK) != 0) - { - psock->s_flags |= _SF_NONBLOCK; - } - else - { - psock->s_flags &= ~_SF_NONBLOCK; - } - } - else -#endif -#endif /* CONFIG_NET_LOCAL || CONFIG_NET_TCP_READAHEAD || - CONFIG_NET_UDP_READAHEAD || CONFIG_NET_USRSOCK */ { nerr("ERROR: Non-blocking not supported for this socket\n"); }