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_i2c_master.c b/arch/arm/src/samdl/sam_i2c_master.c index 3e5a232f27d..b377f1580a3 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,19 @@ 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; + int errcode = errno; + i2cinfo("timedwait error %d\n", errcode); + return -errcode; } - 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 +643,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 +663,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 +677,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 +732,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 +783,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 +819,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 +832,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 +872,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 +900,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 +911,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 +951,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 +1048,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 +1059,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 +1072,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 +1120,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 +1128,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 +1142,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 +1158,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 +1181,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 +1222,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 +1255,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 +1269,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 +1283,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 +1297,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 +1311,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 +1329,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 +1352,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 +1377,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; diff --git a/arch/arm/src/samdl/sam_usb.c b/arch/arm/src/samdl/sam_usb.c new file mode 100644 index 00000000000..175e486a38a --- /dev/null +++ b/arch/arm/src/samdl/sam_usb.c @@ -0,0 +1,4056 @@ +/***************************************************************************** + * 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, + .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_END +}; +#endif + +/**************************************************************************** + * 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)); + + if (dirin) + { + /* Disable bank1 (IN) */ + + intflags = USBDEV_EPINT_TRCPT1 | USBDEV_EPINT_STALL1; + } + else + { + /* Disable bank0 (OUT) */ + + intflags = USBDEV_EPINT_TRCPT0 | USBDEV_EPINT_STALL0; + } + + /* write back disabled config */ + + sam_putreg8(0x7e, SAM_USBDEV_EPINTENCLR(epno)); + sam_putreg8(0x7e, SAM_USBDEV_EPINTFLAG(epno)); + + /* Re-configure and enable the endpoint */ + + 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_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)); + } + 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)); + } + } + + /* Transmit FAIL! */ + + if ((flags & USBDEV_EPINT_TRFAIL0) != 0) + { + 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) + { + 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; + + /* 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); + 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 */ + /* 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); + 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. + * + * 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. + */ + } + + /* SOF interrupt*/ + + else if ((pending & USBDEV_INT_SOF) != 0) + { + /* Clear the pending SOF interrupt */ + + 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; + } + +#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 +} + +void up_usbuninitialize(void) +{ + uinfo("up_usbuninitialize()\n"); +} + +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; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * 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 diff --git a/include/nuttx/net/net.h b/include/nuttx/net/net.h index e12fb99d1ed..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,11 +99,44 @@ 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. + */ + +struct socket; /* Forward reference */ + +struct sock_intf_s +{ + 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 * descriptor. */ -struct devif_callback_s; /* Forward reference */ +struct devif_callback_s; /* Forward reference */ struct socket { @@ -120,6 +158,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/libc/stdlib/lib_mkstemp.c b/libc/stdlib/lib_mkstemp.c index fdf93cdfaf4..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. * ****************************************************************************/ @@ -284,6 +283,8 @@ int mkstemp(FAR char *path_template) return fd; } + + retries--; } /* We could not find an unique filename */ 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..983e17950ab 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 */ @@ -302,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 @@ -343,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 @@ -421,7 +433,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 @@ -448,9 +460,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_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_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 new file mode 100644 index 00000000000..56c4b513a96 --- /dev/null +++ b/net/local/local_sockif.c @@ -0,0 +1,574 @@ +/**************************************************************************** + * 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 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 + ****************************************************************************/ + +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 */ + local_accept, /* si_accept */ + local_send, /* si_send */ + local_sendto, /* si_sendto */ + local_recvfrom, /* si_recvfrom */ + local_close /* si_close */ +}; + +/**************************************************************************** + * 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_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 + * + * 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 + * + * 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, a negated + * errno value is returned (see send() for the list of appropriate error + * 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, a negated + * errno value is returned (see send_to() for the list of appropriate error + * 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; +} + +/**************************************************************************** + * 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 + ****************************************************************************/ + +/**************************************************************************** + * Name: + * + * Description: + * + * Parameters: + * + * Returned Value: + * + * Assumptions: + * + ****************************************************************************/ + +#endif /* CONFIG_NET_LOCAL */ diff --git a/net/pkt/Make.defs b/net/pkt/Make.defs index ced242a430c..12583853345 100644 --- a/net/pkt/Make.defs +++ b/net/pkt/Make.defs @@ -39,7 +39,9 @@ ifeq ($(CONFIG_NET_PKT),y) # Socket layer +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 b7b6e0b0a92..61dee0929a8 100644 --- a/net/pkt/pkt.h +++ b/net/pkt/pkt.h @@ -91,12 +91,17 @@ extern "C" # define EXTERN extern #endif +/* The packet socket interface */ + +EXTERN const struct sock_intf_s g_pkt_sockif; + /**************************************************************************** * Public Function Prototypes ****************************************************************************/ struct net_driver_s; /* Forward reference */ struct eth_hdr_s; /* Forward reference */ +struct socket; /* Forward reference */ /**************************************************************************** * Name: pkt_initialize() @@ -197,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..e7075fccaa5 --- /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 new file mode 100644 index 00000000000..ae2542d619f --- /dev/null +++ b/net/pkt/pkt_sockif.c @@ -0,0 +1,551 @@ +/**************************************************************************** + * 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 + +#include "pkt/pkt.h" + +#ifdef CONFIG_NET_PKT + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +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 + ****************************************************************************/ + +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 */ + pkt_accept, /* si_accept */ + pkt_send, /* si_send */ + pkt_sendto, /* si_sendto */ + pkt_recvfrom, /* si_recvfrom */ + pkt_close /* si_close */ +}; + +/**************************************************************************** + * 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_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 + * + * 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_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 + * + * 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_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 + * + * 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, a negated + * errno value is returned (see send() for the list of appropriate error + * 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, a negated + * errno value is returned (see send_to() for the list of appropriate error + * 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; +} + +/**************************************************************************** + * 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 + ****************************************************************************/ + +/**************************************************************************** + * Name: + * + * Description: + * + * Parameters: + * + * Returned Value: + * + * Assumptions: + * + ****************************************************************************/ + +#endif /* CONFIG_NET_PKT */ diff --git a/net/socket/Make.defs b/net/socket/Make.defs index 6f433cd97a8..1b9c99d5d1e 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 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 inet_close.c +endif + # TCP/IP support ifeq ($(CONFIG_NET_TCP),y) diff --git a/net/socket/accept.c b/net/socket/accept.c index a93e490b9e2..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 ****************************************************************************/ @@ -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 @@ -130,29 +129,20 @@ 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); + 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,127 +153,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; - - /* 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. */ @@ -293,11 +172,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(); @@ -447,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/bind.c b/net/socket/bind.c index a6671ec40d2..8d7270aee1b 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 ****************************************************************************/ @@ -157,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; @@ -169,165 +106,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_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_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 new file mode 100644 index 00000000000..d908e12cd40 --- /dev/null +++ b/net/socket/inet_recvfrom.c @@ -0,0 +1,1674 @@ +/**************************************************************************** + * 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 */ + +#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); + ret = -ENOSYS; + } + break; + } + + return ret; +} + +#endif /* CONFIG_NET */ diff --git a/net/socket/inet_sockif.c b/net/socket/inet_sockif.c new file mode 100644 index 00000000000..6ef3f8b824c --- /dev/null +++ b/net/socket/inet_sockif.c @@ -0,0 +1,1022 @@ +/**************************************************************************** + * 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 "usrsock/usrsock.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 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 + ****************************************************************************/ + +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 */ + inet_accept, /* si_accept */ + inet_send, /* si_send */ + inet_sendto, /* si_sendto */ + inet_recvfrom, /* si_recvfrom */ + inet_close /* si_close */ +}; + +/**************************************************************************** + * 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: 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 + * + * 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) +{ +#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. + * + * 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 + warning("WARNING: SOCK_STREAM disabled\n"); + 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 + warning("WARNING: SOCK_DGRAM disabled\n"); + return -ENETDOWN; +#endif +#endif /* CONFIG_NET_UDP */ + + default: + nerr("ERROR: Unsupported type: %d\n", psock->s_type); + return -EPROTONOSUPPORT; + } +} + +/**************************************************************************** + * 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 + * + * 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_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 + * + * 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, a negated + * errno value is returned (see send() for the list of appropriate error + * 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 */ + + /* 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 + * 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, a negated + * errno value is returned (see send_to() for the list of appropriate error + * 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; + +#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; +#endif + +#ifdef CONFIG_NET_IPv6 + case AF_INET6: + minlen = sizeof(struct sockaddr_in6); + 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 + nwarn("WARNING: UDP not available in this configuiration\n") + nsent = -ENOSYS; +#endif /* CONFIG_NET_6LOWPAN */ +#else + nwarn("WARNING: UDP not enabled in this configuiration\n") + 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/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; diff --git a/net/socket/net_clone.c b/net/socket/net_clone.c index d720eb8731b..7ab3852574d 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 */ @@ -94,65 +95,19 @@ 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; } #endif /* CONFIG_NET */ - - 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/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/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"); } diff --git a/net/socket/recvfrom.c b/net/socket/recvfrom.c index ab16ee9c661..1144bf6d6fe 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,15 @@ #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 +128,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 +136,36 @@ 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; } -#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; - } -#endif - - /* 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 - -#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 */ + /* Let logic specific to this address family handle the recvfrom() + * operation. + */ - switch (psock->s_type) - { -#ifdef CONFIG_NET_PKT - case SOCK_RAW: - { - ret = pkt_recvfrom(psock, buf, len, from, fromlen); - } - break; -#endif /* CONFIG_NET_PKT */ + DEBUGASSERT(psock->s_sockif != NULL && + psock->s_sockif->si_recvfrom != NULL); -#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 - { - 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; - } + ret = psock->s_sockif->si_recvfrom(psock, buf, len, flags, from, fromlen); /* 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/send.c b/net/socket/send.c index c47158aca25..9af7e88cdb7 100644 --- a/net/socket/send.c +++ b/net/socket/send.c @@ -42,6 +42,8 @@ #include #include #include +#include +#include #include @@ -51,7 +53,6 @@ #include "sixlowpan/sixlowpan.h" #include "local/local.h" #include "socket/socket.h" -#include "usrsock/usrsock.h" /**************************************************************************** * Public Functions @@ -126,132 +127,21 @@ 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 */ + /* Let the address family's send() method handle the operation */ - 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 */ - -#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 - * 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..6ddf2a59589 100644 --- a/net/socket/sendto.c +++ b/net/socket/sendto.c @@ -42,6 +42,7 @@ #include #include #include +#include #include #include @@ -52,7 +53,6 @@ #include "sixlowpan/sixlowpan.h" #include "local/local.h" #include "socket/socket.h" -#include "usrsock/usrsock.h" /**************************************************************************** * Public Functions @@ -127,18 +127,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) @@ -150,57 +148,6 @@ ssize_t psock_sendto(FAR struct socket *psock, FAR const void *buf, #endif } -#ifdef CONFIG_NET_USRSOCK - if (psock->s_type == SOCK_USRSOCK_TYPE) - { - /* 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) @@ -210,68 +157,21 @@ ssize_t psock_sendto(FAR struct socket *psock, FAR const void *buf, goto errout; } - /* If this is a connected socket, then return EISCONN */ + /* Let the address family's send() method handle the operation */ - 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() */ - - 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 */ - } -#endif /* CONFIG_NET_UDP */ + 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 */ 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..7265e5cce9c 100644 --- a/net/socket/socket.c +++ b/net/socket/socket.c @@ -38,239 +38,15 @@ ****************************************************************************/ #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" -/**************************************************************************** - * 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 - * - * 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 */ +#ifdef CONFIG_NET /**************************************************************************** * Public Functions @@ -316,148 +92,11 @@ 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; - -#ifdef CONFIG_NET_USRSOCK - ret = usrsock_socket_setup(domain, type, protocol, psock); - if (ret < 0) - { - 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 */ + int ret; + + /* Initialize the socket structure */ psock->s_domain = domain; psock->s_type = type; @@ -466,115 +105,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 +185,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 +204,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..3465f74b964 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 * @@ -410,13 +431,99 @@ 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_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 + * + * 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); + +/**************************************************************************** + * 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) } 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);