diff --git a/ChangeLog b/ChangeLog index 01cb1230ae..a0ce6054be 100755 --- a/ChangeLog +++ b/ChangeLog @@ -11216,4 +11216,7 @@ * arch/arm/src/samv7: Port the WDT driver from the SAMA5D3/4 to the SAMV7 (s015-12-06). * arch/arm/src/samv7: Add an RSWDT driver (2015-12-06). + * drivers/net/telnet.c: Move the Telnet driver from apps/netutils/telnetd + to drivers/net. It is a driver a belongs in the OS. There are still + some interface related issues, however (2015-12-07). diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index c1461e9153..49ad8d710f 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -16,6 +16,33 @@ config NETDEV_LOOPBACK networking devices that are enabled must be compatible with CONFIG_NET_NOINTS. +config NETDEV_TELNET + bool "Telnet driver" + default n + depends on NET && NET_TCP + ---help--- + The Telnet driver generates a character driver instance to support a + Telnet session. This driver is used by the Telnet daemon. The + Telnet daeman will instantiate a new Telnet driver to support + standard I/O on the new Telnet session. + +if NETDEV_TELNET + +config TELNET_RXBUFFER_SIZE + int "Telnet RX buffer size" + default 256 + +config TELNET_TXBUFFER_SIZE + int "Telnet TX buffer size" + default 256 + +config TELNET_DUMPBUFFER + bool "Dump Telnet buffers" + default n + depends on DEBUG_NET + +endif # NETDEV_TELNET + config NETDEV_MULTINIC bool "Multiple network interface support" default n if !NETDEV_LOOPBACK diff --git a/drivers/net/Make.defs b/drivers/net/Make.defs index e09162c00c..fe68efb267 100644 --- a/drivers/net/Make.defs +++ b/drivers/net/Make.defs @@ -43,6 +43,10 @@ ifeq ($(CONFIG_NETDEV_LOOPBACK),y) CSRCS += loopback.c endif +ifeq ($(CONFIG_NETDEV_TELNET),y) + CSRCS += telnet.c +endif + ifeq ($(CONFIG_NET_DM90x0),y) CSRCS += dm90x0.c endif diff --git a/drivers/net/telnet.c b/drivers/net/telnet.c new file mode 100644 index 0000000000..b935a5b457 --- /dev/null +++ b/drivers/net/telnet.c @@ -0,0 +1,865 @@ +/**************************************************************************** + * apps/netutils/telnet_driver.c + * + * Copyright (C) 2007, 2009, 2011-2013 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * This is a leverage of similar logic from uIP which has a compatible BSD + * license: + * + * Author: Adam Dunkels + * Copyright (c) 2003, Adam Dunkels. + * All rights reserved. + * + * 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 of the Institute, 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 INSTITUTE 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 INSTITUTE 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 + +#include +#include +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ +/* Configuration ************************************************************/ + +#ifndef CONFIG_TELNET_RXBUFFER_SIZE +# define CONFIG_TELNET_RXBUFFER_SIZE 256 +#endif + +#ifndef CONFIG_TELNET_TXBUFFER_SIZE +# define CONFIG_TELNET_TXBUFFER_SIZE 256 +#endif + +/* Telnet protocol stuff ****************************************************/ + +#define ISO_nl 0x0a +#define ISO_cr 0x0d + +#define TELNET_IAC 255 +#define TELNET_WILL 251 +#define TELNET_WONT 252 +#define TELNET_DO 253 +#define TELNET_DONT 254 + +/* Device stuff *************************************************************/ + +#define TELNETD_DEVFMT "/dev/telnet%d" + +/**************************************************************************** + * Private Types + ****************************************************************************/ +/* The state of the telnet parser */ + +enum telnet_state_e +{ + STATE_NORMAL = 0, + STATE_IAC, + STATE_WILL, + STATE_WONT, + STATE_DO, + STATE_DONT +}; + +/* This structure describes the internal state of the driver */ + +struct telnet_dev_s +{ + sem_t td_exclsem; /* Enforces mutually exclusive access */ + uint8_t td_state; /* (See telnet_state_e) */ + uint8_t td_pending; /* Number of valid, pending bytes in the rxbuffer */ + uint8_t td_offset; /* Offset to the valid, pending bytes in the rxbuffer */ + uint8_t td_crefs; /* The number of open references to the session */ + int td_minor; /* Minor device number */ + FAR struct socket td_psock; /* A clone of the internal socket structure */ + char td_rxbuffer[CONFIG_TELNET_RXBUFFER_SIZE]; + char td_txbuffer[CONFIG_TELNET_TXBUFFER_SIZE]; +}; + +/* This structure contains global information visable to all telnet driver + * instances. + */ + +struct telnet_common_s +{ + sem_t tc_exclsem; /* Enforces exclusive access to 'minor' */ + int tc_minor; /* The next minor number to use */ +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ +/* Support functions */ + +#ifdef CONFIG_TELNET_DUMPBUFFER +static inline void telnet_dumpbuffer(FAR const char *msg, + FAR const char *buffer, unsigned int nbytes); +#else +# define telnet_dumpbuffer(msg,buffer,nbytes) +#endif +static void telnet_getchar(FAR struct telnet_dev_s *priv, uint8_t ch, + FAR char *dest, int *nread); +static ssize_t telnet_receive(FAR struct telnet_dev_s *priv, + FAR const char *src, size_t srclen, FAR char *dest, + size_t destlen); +static bool telnet_putchar(FAR struct telnet_dev_s *priv, uint8_t ch, + int *nwritten); +static void telnet_sendopt(FAR struct telnet_dev_s *priv, uint8_t option, + uint8_t value); + +/* Character driver methods */ + +static int telnet_open(FAR struct file *filep); +static int telnet_close(FAR struct file *filep); +static ssize_t telnet_read(FAR struct file *, FAR char *, size_t); +static ssize_t telnet_write(FAR struct file *, FAR const char *, size_t); +static int telnet_ioctl(FAR struct file *filep, int cmd, + unsigned long arg); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static const struct file_operations g_telnet_fops = +{ + telnet_open, /* open */ + telnet_close, /* close */ + telnet_read, /* read */ + telnet_write, /* write */ + 0, /* seek */ + telnet_ioctl /* ioctl */ +#ifndef CONFIG_DISABLE_POLL + , 0 /* poll */ +#endif +}; + +/* Global information shared amongst telnet driver instanaces. */ + +static struct telnet_common_s g_telnet_common = +{ + SEM_INITIALIZER(1), + 0 +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: telnet_dumpbuffer + * + * Description: + * Dump a buffer of data (debug only) + * + ****************************************************************************/ + +#ifdef CONFIG_TELNET_DUMPBUFFER +static inline void telnet_dumpbuffer(FAR const char *msg, + FAR const char *buffer, + unsigned int nbytes) +{ + /* CONFIG_DEBUG, CONFIG_DEBUG_VERBOSE, and CONFIG_DEBUG_NET have to be + * defined or the following does nothing. + */ + + nvdbgdumpbuffer(msg, (FAR const uint8_t*)buffer, nbytes); +} +#endif + +/**************************************************************************** + * Name: telnet_getchar + * + * Description: + * Get another character for the user received buffer from the RX buffer + * + ****************************************************************************/ + +static void telnet_getchar(FAR struct telnet_dev_s *priv, uint8_t ch, + FAR char *dest, int *nread) +{ + register int index; + + /* Ignore carriage returns */ + + if (ch != ISO_cr) + { + /* Add all other characters to the destination buffer */ + + index = *nread; + dest[index++] = ch; + *nread = index; + } +} + +/**************************************************************************** + * Name: telnet_receive + * + * Description: + * Process a received Telnet buffer + * + ****************************************************************************/ + +static ssize_t telnet_receive(FAR struct telnet_dev_s *priv, FAR const char *src, + size_t srclen, FAR char *dest, size_t destlen) +{ + int nread; + uint8_t ch; + + nllvdbg("srclen: %d destlen: %d\n", srclen, destlen); + + for (nread = 0; srclen > 0 && nread < destlen; srclen--) + { + ch = *src++; + nllvdbg("ch=%02x state=%d\n", ch, priv->td_state); + + switch (priv->td_state) + { + case STATE_IAC: + if (ch == TELNET_IAC) + { + telnet_getchar(priv, ch, dest, &nread); + priv->td_state = STATE_NORMAL; + } + else + { + switch (ch) + { + case TELNET_WILL: + priv->td_state = STATE_WILL; + break; + + case TELNET_WONT: + priv->td_state = STATE_WONT; + break; + + case TELNET_DO: + priv->td_state = STATE_DO; + break; + + case TELNET_DONT: + priv->td_state = STATE_DONT; + break; + + default: + priv->td_state = STATE_NORMAL; + break; + } + } + break; + + case STATE_WILL: + /* Reply with a DONT */ + + telnet_sendopt(priv, TELNET_DONT, ch); + priv->td_state = STATE_NORMAL; + break; + + case STATE_WONT: + /* Reply with a DONT */ + + telnet_sendopt(priv, TELNET_DONT, ch); + priv->td_state = STATE_NORMAL; + break; + + case STATE_DO: + /* Reply with a WONT */ + + telnet_sendopt(priv, TELNET_WONT, ch); + priv->td_state = STATE_NORMAL; + break; + + case STATE_DONT: + /* Reply with a WONT */ + + telnet_sendopt(priv, TELNET_WONT, ch); + priv->td_state = STATE_NORMAL; + break; + + case STATE_NORMAL: + if (ch == TELNET_IAC) + { + priv->td_state = STATE_IAC; + } + else + { + telnet_getchar(priv, ch, dest, &nread); + } + break; + } + } + + /* We get here if (1) all of the received bytes have been processed, or + * (2) if the user's buffer has become full. + */ + + if (srclen > 0) + { + /* Remember where we left off. These bytes will be returned the next + * time that telnet_read() is called. + */ + + priv->td_pending = srclen; + priv->td_offset = (src - priv->td_rxbuffer); + } + else + { + /* All of the received bytes were consumed */ + + priv->td_pending = 0; + priv->td_offset = 0; + } + + return nread; +} + +/**************************************************************************** + * Name: telnet_putchar + * + * Description: + * Put another character from the user buffer to the TX buffer. + * + ****************************************************************************/ + +static bool telnet_putchar(FAR struct telnet_dev_s *priv, uint8_t ch, + int *nread) +{ + register int index; + bool ret = false; + + /* Ignore carriage returns (we will put these in automatically as necesary) */ + + if (ch != ISO_cr) + { + /* Add all other characters to the destination buffer */ + + index = *nread; + priv->td_txbuffer[index++] = ch; + + /* Check for line feeds */ + + if (ch == ISO_nl) + { + /* Now add the carriage return */ + + priv->td_txbuffer[index++] = ISO_cr; + priv->td_txbuffer[index++] = '\0'; + + /* End of line */ + + ret = true; + } + + *nread = index; + } + + return ret; +} + +/**************************************************************************** + * Name: telnet_sendopt + * + * Description: + * Send the telnet option bytes + * + ****************************************************************************/ + +static void telnet_sendopt(FAR struct telnet_dev_s *priv, uint8_t option, + uint8_t value) +{ + uint8_t optbuf[4]; + optbuf[0] = TELNET_IAC; + optbuf[1] = option; + optbuf[2] = value; + optbuf[3] = 0; + + telnet_dumpbuffer("Send optbuf", optbuf, 4); + if (psock_send(&priv->td_psock, optbuf, 4, 0) < 0) + { + nlldbg("Failed to send TELNET_IAC\n"); + } +} + +/**************************************************************************** + * Name: telnet_open + ****************************************************************************/ + +static int telnet_open(FAR struct file *filep) +{ + FAR struct inode *inode = filep->f_inode; + FAR struct telnet_dev_s *priv = inode->i_private; + int tmp; + int ret; + + nllvdbg("td_crefs: %d\n", priv->td_crefs); + + /* O_NONBLOCK is not supported */ + + if (filep->f_oflags & O_NONBLOCK) + { + ret = -ENOSYS; + goto errout; + } + + /* Get exclusive access to the device structures */ + + ret = sem_wait(&priv->td_exclsem); + if (ret < 0) + { + ret = -errno; + goto errout; + } + + /* Increment the count of references to the device. If this the first + * time that the driver has been opened for this device, then initialize + * the device. + */ + + tmp = priv->td_crefs + 1; + if (tmp > 255) + { + /* More than 255 opens; uint8_t would overflow to zero */ + + ret = -EMFILE; + goto errout_with_sem; + } + + /* Save the new open count on success */ + + priv->td_crefs = tmp; + ret = OK; + +errout_with_sem: + sem_post(&priv->td_exclsem); + +errout: + return ret; +} + +/**************************************************************************** + * Name: telnet_close + ****************************************************************************/ + +static int telnet_close(FAR struct file *filep) +{ + FAR struct inode *inode = filep->f_inode; + FAR struct telnet_dev_s *priv = inode->i_private; + FAR char *devpath; + int ret; + + nllvdbg("td_crefs: %d\n", priv->td_crefs); + + /* Get exclusive access to the device structures */ + + ret = sem_wait(&priv->td_exclsem); + if (ret < 0) + { + ret = -errno; + goto errout; + } + + /* Decrement the references to the driver. If the reference count will + * decrement to 0, then uninitialize the driver. + */ + + if (priv->td_crefs > 1) + { + /* Just decrement the reference count and release the semaphore */ + + priv->td_crefs--; + sem_post(&priv->td_exclsem); + } + else + { + /* Re-create the path to the driver. */ + + sched_lock(); + ret = asprintf(&devpath, TELNETD_DEVFMT, priv->td_minor); + if (ret < 0) + { + nlldbg("ERROR: Failed to allocate the driver path\n"); + } + else + { + /* Un-register the character driver */ + + ret = unregister_driver(devpath); + if (ret < 0) + { + /* NOTE: a return value of -EBUSY is not an error, it simply + * means that the Telnet driver is busy now and cannot be + * registered now because there are other sessions using the + * connection. The driver will be properly unregistered when + * the final session terminates. + */ + + if (ret != -EBUSY) + { + nlldbg("Failed to unregister the driver %s: %d\n", + devpath, ret); + } + } + + free(devpath); + } + + /* Close the socket */ + + psock_close(&priv->td_psock); + + /* Release the driver memory. What if there are threads waiting on + * td_exclsem? They will never be awakened! How could this happen? + * crefs == 1 so there are no other open references to the driver. + * But this could have if someone were trying to re-open the driver + * after every other thread has closed it. That really should not + * happen in the intended usage model. + */ + + DEBUGASSERT(priv->td_exclsem.semcount == 0); + sem_destroy(&priv->td_exclsem); + free(priv); + sched_unlock(); + } + + ret = OK; + +errout: + return ret; +} + +/**************************************************************************** + * Name: telnet_read + ****************************************************************************/ + +static ssize_t telnet_read(FAR struct file *filep, FAR char *buffer, size_t len) +{ + FAR struct inode *inode = filep->f_inode; + FAR struct telnet_dev_s *priv = inode->i_private; + ssize_t ret; + + nllvdbg("len: %d\n", len); + + /* First, handle the case where there are still valid bytes left in the + * I/O buffer from the last time that read was called. NOTE: Much of + * what we read may be protocol stuff and may not correspond to user + * data. Hence we need the loop and we need may need to call psock_recv() + * multiple times in order to get data that the client is interested in. + */ + + do + { + if (priv->td_pending > 0) + { + /* Process the buffered telnet data */ + + FAR const char *src = &priv->td_rxbuffer[priv->td_offset]; + ret = telnet_receive(priv, src, priv->td_pending, buffer, len); + } + + /* Read a buffer of data from the telnet client */ + + else + { + ret = psock_recv(&priv->td_psock, priv->td_rxbuffer, + CONFIG_TELNET_RXBUFFER_SIZE, 0); + + /* Did we receive anything? */ + + if (ret > 0) + { + /* Yes.. Process the newly received telnet data */ + + telnet_dumpbuffer("Received buffer", priv->td_rxbuffer, ret); + ret = telnet_receive(priv, priv->td_rxbuffer, ret, buffer, len); + } + + /* Otherwise the peer closed the connection (ret == 0) or an error + * occurred (ret < 0). + */ + + else + { + break; + } + } + } + while (ret == 0); + + /* Return: + * + * ret > 0: The number of characters copied into the user buffer by + * telnet_receive(). + * ret <= 0: Loss of connection or error events reported by recv(). + */ + + return ret; +} + +/**************************************************************************** + * Name: telnet_write + ****************************************************************************/ + +static ssize_t telnet_write(FAR struct file *filep, FAR const char *buffer, size_t len) +{ + FAR struct inode *inode = filep->f_inode; + FAR struct telnet_dev_s *priv = inode->i_private; + FAR const char *src = buffer; + ssize_t nsent; + ssize_t ret; + int ncopied; + char ch; + bool eol; + + nllvdbg("len: %d\n", len); + + /* Process each character from the user buffer */ + + for (nsent = 0, ncopied = 0; nsent < len; nsent++) + { + /* Get the next character from the user buffer */ + + ch = *src++; + + /* Add the character to the TX buffer */ + + eol = telnet_putchar(priv, ch, &ncopied); + + /* Was that the end of a line? Or is the buffer too full to hold the + * next largest character sequence ("\r\n\0")? + */ + + if (eol || ncopied > CONFIG_TELNET_TXBUFFER_SIZE-3) + { + /* Yes... send the data now */ + + ret = psock_send(&priv->td_psock, priv->td_txbuffer, ncopied, 0); + if (ret < 0) + { + nlldbg("psock_send failed '%s': %d\n", priv->td_txbuffer, ret); + return ret; + } + + /* Reset the index to the beginning of the TX buffer. */ + + ncopied = 0; + } + } + + /* Send anything remaining in the TX buffer */ + + if (ncopied > 0) + { + ret = psock_send(&priv->td_psock, priv->td_txbuffer, ncopied, 0); + if (ret < 0) + { + nlldbg("psock_send failed '%s': %d\n", priv->td_txbuffer, ret); + return ret; + } + } + + /* Notice that we don't actually return the number of bytes sent, but + * rather, the number of bytes that the caller asked us to send. We may + * have sent more bytes (because of CR-LF expansion and because of NULL + * termination). But it confuses some logic if you report that you sent + * more than you were requested to. + */ + + return len; +} + +/**************************************************************************** + * Name: telnet_poll + ****************************************************************************/ + +static int telnet_ioctl(FAR struct file *filep, int cmd, unsigned long arg) +{ +#if 0 /* No ioctl commands are yet supported */ + struct inode *inode = filep->f_inode; + struct cdcacm_dev_s *priv = inode->i_private; + int ret = OK; + + switch (cmd) + { + /* Add ioctl commands here */ + + default: + ret = -ENOTTY; + break; + } + + return ret; +#else + return -ENOTTY; +#endif +} + +/**************************************************************************** + * Name: telnet_poll + ****************************************************************************/ + +#if 0 /* Not used by this driver */ +static int telnet_poll(FAR struct file *filep, FAR struct pollfd *fds, + bool setup) +{ + FAR struct inode *inode = filep->f_inode; + FAR struct telnet_dev_s *priv = inode->i_private; +} +#endif + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: telnet_driver + * + * Description: + * Create a character driver to "wrap" the telnet session. This function + * will select and return a unique path for the new telnet device. + * + * Parameters: + * sd - The socket descriptor that represents the new telnet connection. + * + * Return: + * An allocated string represent the full path to the created driver. The + * receiver of the string must de-allocate this memory when it is no longer + * needed. NULL is returned on a failure. + * + ****************************************************************************/ + +FAR char *telnet_driver(int sd) +{ + FAR struct telnet_dev_s *priv; + FAR struct socket *psock; + FAR char *devpath = NULL; + int ret; + + /* Allocate instance data for this driver */ + + priv = (FAR struct telnet_dev_s*)malloc(sizeof(struct telnet_dev_s)); + if (!priv) + { + nlldbg("Failed to allocate the driver data structure\n"); + return NULL; + } + + /* Initialize the allocated driver instance */ + + sem_init(&priv->td_exclsem, 0, 1); + + priv->td_state = STATE_NORMAL; + priv->td_crefs = 0; + priv->td_pending = 0; + priv->td_offset = 0; + + /* Clone the internal socket structure. We do this so that it will be + * independent of threads and of socket descriptors (the original socket + * instance resided in the daemon's task group`). + */ + + psock = sockfd_socket(sd); + if (!psock) + { + nlldbg("Failed to convert sd=%d to a socket structure\n", sd); + goto errout_with_dev; + } + + ret = net_clone(psock, &priv->td_psock); + if (ret < 0) + { + nlldbg("net_clone failed: %d\n", ret); + goto errout_with_dev; + } + + /* And close the original */ + + psock_close(psock); + + /* Allocate a unique minor device number of the telnet drvier */ + + do + { + ret = sem_wait(&g_telnet_common.tc_exclsem); + if (ret < 0 && errno != -EINTR) + { + goto errout_with_dev; + } + } + while (ret < 0); + + priv->td_minor = g_telnet_common.tc_minor; + g_telnet_common.tc_minor++; + sem_post(&g_telnet_common.tc_exclsem); + + /* Create a path and name for the driver. */ + + ret = asprintf(&devpath, TELNETD_DEVFMT, priv->td_minor); + if (ret < 0) + { + nlldbg("Failed to allocate the driver path\n"); + goto errout_with_dev; + } + + /* Register the driver */ + + ret = register_driver(devpath, &g_telnet_fops, 0666, priv); + if (ret < 0) + { + nlldbg("Failed to register the driver %s: %d\n", devpath, ret); + goto errout_with_devpath; + } + + /* Return the path to the new telnet driver */ + + return devpath; + +errout_with_devpath: + free(devpath); +errout_with_dev: + free(priv); + return NULL; +} diff --git a/include/nuttx/net/telnet.h b/include/nuttx/net/telnet.h new file mode 100644 index 0000000000..6820260fad --- /dev/null +++ b/include/nuttx/net/telnet.h @@ -0,0 +1,69 @@ +/**************************************************************************** + * include/nuttx/net/telnet.h + * + * Copyright (C) 2012, 2015 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 Gregory Nutt 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 __INCLUDE_NUTTX_NET_TELNET_H +#define __INCLUDE_NUTTX_NET_TELNET_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: telnet_driver + * + * Description: + * Create a character driver to "wrap" the telnet session. This function + * will select and return a unique path for the new telnet device. + * + * Parameters: + * sd - The socket descriptor that represents the new telnet connection. + * + * Return: + * An allocated string represent the full path to the created driver. The + * receiver of the string must de-allocate this memory when it is no longer + * needed. NULL is returned on a failure. + * + ****************************************************************************/ + +FAR char *telnet_driver(int sd); + +#endif /* __INCLUDE_NUTTX_NET_TELNET_H */ +