nuttx/drivers/net/slip.c

1025 lines
26 KiB
C
Raw Normal View History

/****************************************************************************
* drivers/net/slip.c
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
/* Reference: RFC 1055 */
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <fcntl.h>
#include <poll.h>
#include <stdint.h>
#include <stdbool.h>
#include <time.h>
#include <string.h>
#include <errno.h>
#include <assert.h>
#include <debug.h>
#include <arpa/inet.h>
#include <nuttx/arch.h>
#include <nuttx/irq.h>
#include <nuttx/signal.h>
#include <nuttx/wdog.h>
#include <nuttx/wqueue.h>
#include <nuttx/net/ip.h>
#include <nuttx/net/netdev.h>
#ifdef CONFIG_NET_SLIP
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/* NOTE: Slip requires UART hardware handshake. If hardware handshake is
* not available with your UART, then you might try the 'slattach' option
* -L which enable "3-wire operation." That allows operation without the
* hardware handshake (but with the possibility of data overrun).
*/
/* Configuration ************************************************************/
/* The Linux slip module hard-codes its MTU size to 296 (40 bytes for the
* IP+TCP headers plus 256 bytes of data). So you might as well set
This commit attempts remove some long standard confusion in naming and some actual problems that result from the naming confusion. The basic problem is the standard MTU does not include the size of the Ethernet header. For clarity, I changed the naming of most things called MTU to PKTSIZE. For example, CONFIG_NET_ETH_MTU is now CONFIG_NET_ETH_PKTSIZE. This makes the user interface a little hostile. People thing of an MTU of 1500 bytes, but the corresponding packet is really 1514 bytes (including the 14 byte Ethernet header). A more friendly solution would configure the MTU (as before), but then derive the packet buffer size by adding the MAC header length. Instead, we define the packet buffer size then derive the MTU. The MTU is not common currency in networking. On the wire, the only real issue is the MSS which is derived from MTU by subtracting the IP header and TCP header sizes (for the case of TCP). Now it is derived for the PKTSIZE by subtracting the IP header, the TCP header, and the MAC header sizes. So we should be all good and without the recurring 14 byte error in MTU's and MSS's. Squashed commit of the following: Trivial update to fix some spacing issues. net/: Rename several macros containing _MTU to _PKTSIZE. net/: Rename CONFIG_NET_SLIP_MTU to CONFIG_NET_SLIP_PKTSIZE and similarly for CONFIG_NET_TUN_MTU. These are not the MTU which does not include the size of the link layer header. These are the full size of the packet buffer memory (minus any GUARD bytes). net/: Rename CONFIG_NET_6LOWPAN_MTU to CONFIG_NET_6LOWPAN_PKTSIZE and similarly for CONFIG_NET_TUN_MTU. These are not the MTU which does not include the size of the link layer header. These are the full size of the packet buffer memory (minus any GUARD bytes). net/: Rename CONFIG_NET_ETH_MTU to CONFIG_NET_ETH_PKTSIZE. This is not the MTU which does not include the size of the link layer header. This is the full size of the packet buffer memory (minus any GUARD bytes). net/: Rename the file d_mtu in the network driver structure to d_pktsize. That value saved there is not the MTU. The packetsize is the memory large enough to hold the maximum packet PLUS the size of the link layer header. The MTU does not include the link layer header.
2018-07-04 22:10:40 +02:00
* CONFIG_NET_SLIP_PKTSIZE to 296 as well.
*
* There may be an issue with this setting, however. I see that Linux uses
* a MTU of 296 and window of 256, but actually only sends 168 bytes of data:
* 40 + 128. I believe that is to allow for the 2x worst cast packet
* expansion. Ideally we would like to advertise the 256 MSS, but restrict
* transfers to 128 bytes (possibly by modifying the tcp_mss() macro).
*/
This commit attempts remove some long standard confusion in naming and some actual problems that result from the naming confusion. The basic problem is the standard MTU does not include the size of the Ethernet header. For clarity, I changed the naming of most things called MTU to PKTSIZE. For example, CONFIG_NET_ETH_MTU is now CONFIG_NET_ETH_PKTSIZE. This makes the user interface a little hostile. People thing of an MTU of 1500 bytes, but the corresponding packet is really 1514 bytes (including the 14 byte Ethernet header). A more friendly solution would configure the MTU (as before), but then derive the packet buffer size by adding the MAC header length. Instead, we define the packet buffer size then derive the MTU. The MTU is not common currency in networking. On the wire, the only real issue is the MSS which is derived from MTU by subtracting the IP header and TCP header sizes (for the case of TCP). Now it is derived for the PKTSIZE by subtracting the IP header, the TCP header, and the MAC header sizes. So we should be all good and without the recurring 14 byte error in MTU's and MSS's. Squashed commit of the following: Trivial update to fix some spacing issues. net/: Rename several macros containing _MTU to _PKTSIZE. net/: Rename CONFIG_NET_SLIP_MTU to CONFIG_NET_SLIP_PKTSIZE and similarly for CONFIG_NET_TUN_MTU. These are not the MTU which does not include the size of the link layer header. These are the full size of the packet buffer memory (minus any GUARD bytes). net/: Rename CONFIG_NET_6LOWPAN_MTU to CONFIG_NET_6LOWPAN_PKTSIZE and similarly for CONFIG_NET_TUN_MTU. These are not the MTU which does not include the size of the link layer header. These are the full size of the packet buffer memory (minus any GUARD bytes). net/: Rename CONFIG_NET_ETH_MTU to CONFIG_NET_ETH_PKTSIZE. This is not the MTU which does not include the size of the link layer header. This is the full size of the packet buffer memory (minus any GUARD bytes). net/: Rename the file d_mtu in the network driver structure to d_pktsize. That value saved there is not the MTU. The packetsize is the memory large enough to hold the maximum packet PLUS the size of the link layer header. The MTU does not include the link layer header.
2018-07-04 22:10:40 +02:00
#if CONFIG_NET_SLIP_PKTSIZE < 296
# error "CONFIG_NET_SLIP_PKTSIZE >= 296 is required"
#endif
/* Work queue support is required. */
#if !defined(CONFIG_SCHED_WORKQUEUE)
# error Work queue support is required in this configuration (CONFIG_SCHED_WORKQUEUE)
#else
/* The low priority work queue is preferred. If it is not enabled, LPWORK
* will be the same as HPWORK.
*
* NOTE: However, the network should NEVER run on the high priority work
* queue! That queue is intended only to service short back end interrupt
* processing that never suspends. Suspending the high priority work queue
* may bring the system to its knees!
*/
#define SLIPWORK LPWORK
/* CONFIG_NET_SLIP_NINTERFACES determines the number of
* physical interfaces that will be supported.
*/
#ifndef CONFIG_NET_SLIP_NINTERFACES
# define CONFIG_NET_SLIP_NINTERFACES 1
#endif
/* SLIP special character codes ********************************************/
#define SLIP_END 0300 /* Indicates end of packet */
#define SLIP_ESC 0333 /* Indicates byte stuffing */
#define SLIP_ESC_END 0334 /* ESC ESC_END means SLIP_END data byte */
#define SLIP_ESC_ESC 0335 /* ESC ESC_ESC means ESC data byte */
/****************************************************************************
* Private Types
****************************************************************************/
/* The slip_driver_s encapsulates all state information for a single hardware
* interface
*/
struct slip_driver_s
{
bool bifup; /* true:ifup false:ifdown */
struct work_s irqwork; /* For deferring interrupt work */
struct work_s pollwork; /* For deferring poll work to the work queue */
struct file tty; /* TTY file */
struct pollfd pollfd; /* Polling TTY for read- or writeable */
uint8_t rxbuf[2 * CONFIG_NET_SLIP_PKTSIZE + 2];
size_t rxlen;
uint8_t txbuf[2 * CONFIG_NET_SLIP_PKTSIZE + 2];
size_t txlen;
size_t txsent;
/* This holds the information visible to the NuttX network */
struct net_driver_s dev; /* Interface understood by the network */
};
/****************************************************************************
* Private Data
****************************************************************************/
/* Driver state structure */
static struct slip_driver_s g_slip[CONFIG_NET_SLIP_NINTERFACES];
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
/* Common TX logic */
static void slip_transmit(FAR struct slip_driver_s *self);
static int slip_txpoll(FAR struct net_driver_s *dev);
/* Interrupt handling */
static void slip_reply(FAR struct slip_driver_s *self);
static void slip_receive(FAR struct slip_driver_s *self);
static void slip_txdone(FAR struct slip_driver_s *self);
static void slip_interrupt_work(FAR void *arg);
static void slip_pollfd_cb(FAR struct pollfd *pollfd);
static void slip_set_pollfd_events(FAR struct slip_driver_s *self,
short events);
/* NuttX callback functions */
static int slip_ifup(FAR struct net_driver_s *dev);
static int slip_ifdown(FAR struct net_driver_s *dev);
static void slip_txavail_work(FAR void *arg);
static int slip_txavail(FAR struct net_driver_s *dev);
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: slip_pollfd_cb
*
* Description:
* TTY reports to be read- or writable
*
* Input Parameters:
* pollfd - Information about the event that happened.
*
* Returned Value:
* OK on success
*
****************************************************************************/
static void slip_pollfd_cb(FAR struct pollfd *pollfd)
{
FAR struct slip_driver_s *self = (FAR struct slip_driver_s *)pollfd->arg;
DEBUGASSERT(self != NULL);
/* Schedule to perform the processing on the worker thread. */
if (work_available(&self->irqwork))
{
work_queue(SLIPWORK, &self->irqwork, slip_interrupt_work, self, 0);
}
}
/****************************************************************************
* Name: slip_set_pollfd_events
*
* Description:
* Setup TTY to report poll events (such as POLLIN and POLLOUT)
*
* Input Parameters:
* self - The SLIP interface to register for poll events
* events - The poll events to request reporting for
*
****************************************************************************/
static void slip_set_pollfd_events(FAR struct slip_driver_s *self,
short events)
{
int ret;
/* Teardown any potentially pending poll, if applicable */
if (self->pollfd.events != 0)
{
ret = file_poll(&self->tty, &self->pollfd, false);
if (ret != OK)
{
nerr("file_poll(false) failed: %d\n", ret);
}
}
memset(&self->pollfd, 0, sizeof(self->pollfd));
/* Setup requested poll, if applicable */
if (events != 0)
{
self->pollfd.arg = self;
self->pollfd.cb = slip_pollfd_cb;
self->pollfd.events = events;
self->pollfd.revents = 0;
self->pollfd.priv = NULL;
ret = file_poll(&self->tty, &self->pollfd, true);
if (ret != OK)
{
nerr("file_poll(true) failed: %d\n", ret);
}
}
}
/****************************************************************************
* Name: slip_transmit
*
* Description:
* Start hardware transmission. Called either from the txdone interrupt
* handling or from watchdog based polling.
*
* Input Parameters:
* self - Reference to the driver state structure
*
* Assumptions:
* The network is locked.
*
****************************************************************************/
static void slip_transmit(FAR struct slip_driver_s *self)
{
ssize_t ssz;
uint8_t *p;
DEBUGASSERT(self->dev.d_len > 0);
/* Verify that the hardware is ready to send another packet. If we get
* here, then we are committed to sending a packet; Higher level logic
* must have assured that there is no transmission in progress.
*/
if (self->txlen > 0)
{
int i;
/* Transmission of previous packet is still pending. This might happen
* in the 'slip_receive' -> 'slip_reply' -> 'slip_transmit' case. Try
* to forward pending packet into UART's transmit buffer. Timeout on
* packet if not forwarded within a second.
*/
for (i = 0; (i < 10) && (self->txsent != self->txlen); )
{
ssz = file_write(&self->tty,
&self->txbuf[self->txsent],
self->txlen - self->txsent);
if (ssz <= 0)
{
nxsig_usleep(10000);
i++;
continue;
}
self->txsent += ssz;
}
if (self->txsent == self->txlen)
{
slip_txdone(self);
}
else
{
NETDEV_TXTIMEOUTS(&self->dev);
}
}
self->txlen = 0;
self->txsent = 0;
/* Send an initial END character to flush out any data that may have
* accumulated in the receiver due to line noise
*/
self->txbuf[self->txlen++] = SLIP_END;
/* Now copy the I/O buffer into self->txbuf */
for (unsigned int bytesread = 0; bytesread < self->dev.d_len; )
{
unsigned int chunk_sz = sizeof(self->txbuf) - self->txlen;
int copied;
if (self->dev.d_len - bytesread < chunk_sz)
{
chunk_sz = self->dev.d_len - bytesread;
}
copied = iob_copyout(&self->txbuf[self->txlen],
self->dev.d_iob,
chunk_sz,
bytesread);
if (copied <= 0)
{
goto error;
}
bytesread += (unsigned int)copied;
self->txlen += (size_t)copied;
}
/* SLIP encode self->txbuf. First escape the ESC bytes. */
for (p = memchr(&self->txbuf[1], SLIP_ESC, self->txlen - 1);
p != NULL;
p = memchr(p, SLIP_ESC, self->txlen - 1))
{
ssize_t postfix_len = self->txlen - (p - self->txbuf) - 1;
if (self->txlen >= sizeof(self->txbuf))
{
goto error;
}
if (postfix_len > 0)
{
memmove(p + 2, p + 1, postfix_len);
}
p++;
*p = SLIP_ESC_ESC;
self->txlen++;
}
/* SLIP encode self->txbuf. Then escape the END bytes. */
for (p = memchr(&self->txbuf[1], SLIP_END, self->txlen - 1);
p != NULL;
p = memchr(p, SLIP_END, self->txlen - 1))
{
ssize_t postfix_len = self->txlen - (p - self->txbuf) - 1;
if (self->txlen >= sizeof(self->txbuf))
{
goto error;
}
if (postfix_len > 0)
{
memmove(p + 2, p + 1, postfix_len);
}
*p = SLIP_ESC;
p++;
*p = SLIP_ESC_END;
self->txlen++;
}
/* Append the END token */
if (self->txlen >= sizeof(self->txbuf))
{
goto error;
}
self->txbuf[self->txlen++] = SLIP_END;
/* Increment statistics */
NETDEV_TXPACKETS(&self->dev);
/* Try to send packet */
ssz = file_write(&self->tty, self->txbuf, self->txlen);
if (ssz > 0)
{
self->txsent = (size_t)ssz;
}
else
{
self->txsent = 0;
}
if (self->txsent == self->txlen)
{
/* Complete packet went out at first try. */
slip_txdone(self);
}
return;
error:
/* Drop the packet and reset the receiver logic. */
self->txlen = 0;
self->txsent = 0;
NETDEV_TXERRORS(&self->dev);
}
/****************************************************************************
* Name: slip_txpoll
*
* Description:
* The transmitter is available, check if the network has any outgoing
* packets ready to send. This is a callback from devif_poll().
* devif_poll() may be called:
*
* 1. When the preceding TX packet send is complete,
* 2. When the preceding TX packet send timesout and the interface is reset
* 3. During normal TX polling
*
* Input Parameters:
* dev - Reference to the NuttX driver state structure
*
* Returned Value:
* OK on success; a negated errno on failure
*
* Assumptions:
* The network is locked.
*
****************************************************************************/
static int slip_txpoll(FAR struct net_driver_s *dev)
{
FAR struct slip_driver_s *self =
(FAR struct slip_driver_s *)dev->d_private;
/* Send the packet */
slip_transmit(self);
/* If zero is returned, the polling will continue until all connections
* have been examined. We return -EBUSY if there is still transmission
* data pending in the TTY's buffer.
*/
return (self->txlen > 0) ? -EBUSY : OK;
}
/****************************************************************************
* Name: slip_reply
*
* Description:
* After a packet has been received and dispatched to the network, it
* may return return with an outgoing packet. This function checks for
* that case and performs the transmission if necessary.
*
* Input Parameters:
* self - Reference to the driver state structure
*
* Returned Value:
* None
*
* Assumptions:
* The network is locked.
*
****************************************************************************/
static void slip_reply(struct slip_driver_s *self)
{
/* If the packet dispatch resulted in data that should be sent out on the
* network, the field d_len will set to a value > 0.
*/
if (self->dev.d_len > 0)
{
/* And send the packet */
slip_transmit(self);
}
}
/****************************************************************************
* Name: slip_receive
*
* Description:
* An interrupt was received indicating the availability of a new RX packet
*
* Input Parameters:
* self - Reference to the driver state structure
*
* Returned Value:
* None
*
* Assumptions:
* The network is locked.
*
****************************************************************************/
static void slip_receive(FAR struct slip_driver_s *self)
{
FAR struct net_driver_s *dev = &self->dev;
FAR struct iob_s *iob;
FAR uint8_t *p;
FAR uint8_t *pend;
size_t remaining;
size_t copied;
int ret;
/* Drop potential prefix SLIP_ENDs */
while ((self->rxbuf[0] == SLIP_END) && (self->rxlen > 0))
{
self->rxlen--;
memmove(&self->rxbuf[0], &self->rxbuf[1], self->rxlen);
}
/* Find end of packet */
pend = memchr(self->rxbuf, SLIP_END, self->rxlen);
if (pend == NULL)
{
/* No complete packet present. Let's wait for more bytes to arrive. */
if (self->rxlen == sizeof(self->rxbuf))
{
/* Purge receive buffer overflow due to overflow. */
NETDEV_RXERRORS(&self->dev);
self->rxlen = 0;
}
return;
}
p = self->rxbuf;
remaining = pend - p;
copied = 0;
iob = iob_alloc(false);
iob_reserve(iob, CONFIG_NET_LL_GUARDSIZE);
while (remaining)
{
uint8_t *pesc = memchr(p, SLIP_ESC, remaining);
if (pesc != NULL)
{
unsigned int prefix_len = (unsigned int)(pesc - p);
if (prefix_len > 0)
{
ret = iob_copyin(iob, p, prefix_len, copied, false);
DEBUGASSERT(ret >= 0);
DEBUGASSERT((unsigned int)ret == prefix_len);
copied += prefix_len;
remaining -= prefix_len;
}
p = pesc + 1;
remaining--;
switch (*p)
{
case SLIP_ESC_END:
*p = SLIP_END;
break;
case SLIP_ESC_ESC:
*p = SLIP_ESC;
break;
default:
/* SLIP protocol error */
goto error;
}
ret = iob_copyin(iob, p, 1, copied, false);
DEBUGASSERT(ret == 1);
p++;
copied++;
remaining--;
}
else
{
ret = iob_copyin(iob, p, remaining, copied, false);
DEBUGASSERT(ret >= 0);
DEBUGASSERT((unsigned int)ret == remaining);
p += remaining;
copied += remaining;
remaining = 0;
}
}
/* Move remaining bytes in rxbuf to the front */
DEBUGASSERT((pend - self->rxbuf) <= self->rxlen);
self->rxlen -= (pend - self->rxbuf);
memmove(self->rxbuf, pend, self->rxlen);
/* Handle the IP input. */
netdev_iob_replace(&self->dev, iob);
iob = NULL;
NETDEV_RXPACKETS(&self->dev);
/* All packets are assumed to be IP packets (we don't have a choice..
* there is no Ethernet header containing the EtherType). So pass the
* received packet on for IP processing -- but only if it is big
* enough to hold an IP header.
*/
if ((IPv4BUF->vhl & IP_VERSION_MASK) == IPv4_VERSION)
{
NETDEV_RXIPV4(&self->dev);
ipv4_input(&self->dev);
slip_reply(self);
}
else
{
NETDEV_RXDROPPED(&self->dev);
}
return;
error:
NETDEV_RXERRORS(&self->dev);
if (iob)
{
iob_free_chain(iob);
iob = NULL;
}
/* Move remaining bytes in rxbuf to the front */
DEBUGASSERT((pend - self->rxbuf) <= self->rxlen);
self->rxlen -= (pend - self->rxbuf);
memmove(self->rxbuf, pend, self->rxlen);
}
/****************************************************************************
* Name: slip_txdone
*
* Description:
* An interrupt was received indicating that the last TX packet(s) is done
*
* Input Parameters:
* self - Reference to the driver state structure
*
* Returned Value:
* None
*
* Assumptions:
* The network is locked.
*
****************************************************************************/
static void slip_txdone(FAR struct slip_driver_s *self)
{
/* Update statistics */
self->txlen = 0;
self->txsent = 0;
2014-04-13 22:32:20 +02:00
NETDEV_TXDONE(&self->dev);
/* Poll the network for new TX data */
if (work_available(&self->pollwork))
{
work_queue(SLIPWORK, &self->pollwork, slip_txavail_work, self, 0);
}
}
/****************************************************************************
* Name: slip_interrupt_work
*
* Description:
* Perform interrupt related work from the worker thread
*
* Input Parameters:
* arg - The argument passed when work_queue() was called.
*
* Returned Value:
* OK on success
*
* Assumptions:
* Runs on a worker thread.
*
****************************************************************************/
static void slip_interrupt_work(FAR void *arg)
{
FAR struct slip_driver_s *self = (FAR struct slip_driver_s *)arg;
ssize_t ssz;
if (!self->bifup)
{
return;
}
/* Lock the network and serialize driver operations if necessary.
* NOTE: Serialization is only required in the case where the driver work
* is performed on an LP worker thread and where more than one LP worker
* thread has been configured.
*/
net_lock();
/* Process pending Ethernet interrupts */
/* Get and clear interrupt status bits */
/* Handle interrupts according to status bit settings */
if (self->rxlen < sizeof(self->rxbuf))
{
ssz = file_read(&self->tty,
&self->rxbuf[self->rxlen],
sizeof(self->rxbuf) - self->rxlen);
if (ssz > 0)
{
self->rxlen += (size_t)ssz;
}
}
if (self->txsent < self->txlen)
{
ssz = file_write(&self->tty,
&self->txbuf[self->txsent],
self->txlen - self->txsent);
if (ssz > 0)
{
self->txsent += (size_t)ssz;
}
}
/* Check if we received an incoming packet, if so, call slip_receive() */
if (self->rxlen == sizeof(self->rxbuf) ||
memchr(self->rxbuf, SLIP_END, self->rxlen))
{
slip_receive(self);
}
/* Check if a packet transmission just completed. If so, call skel_txdone.
* This may disable further Tx interrupts if there are no pending
* transmissions.
*/
if (self->txlen > 0 && self->txsent == self->txlen)
{
slip_txdone(self);
}
net_unlock();
}
/****************************************************************************
* Name: slip_ifup
*
* Description:
* NuttX Callback: Bring up the Ethernet interface when an IP address is
2014-04-13 22:32:20 +02:00
* provided
*
* Input Parameters:
* dev - Reference to the NuttX driver state structure
*
* Returned Value:
* None
*
* Assumptions:
* The network is locked.
*
****************************************************************************/
static int slip_ifup(FAR struct net_driver_s *dev)
{
FAR struct slip_driver_s *self =
(FAR struct slip_driver_s *)dev->d_private;
ninfo("Bringing up: %u.%u.%u.%u\n",
ip4_addr1(dev->d_ipaddr), ip4_addr2(dev->d_ipaddr),
ip4_addr3(dev->d_ipaddr), ip4_addr4(dev->d_ipaddr));
/* Enable POLLIN and POLLOUT events on the TTY */
slip_set_pollfd_events(self, POLLIN | POLLOUT);
/* Mark the device "up" */
self->bifup = true;
return OK;
}
/****************************************************************************
* Name: slip_ifdown
*
* Description:
* NuttX Callback: Stop the interface.
*
* Input Parameters:
* dev - Reference to the NuttX driver state structure
*
* Returned Value:
* None
*
* Assumptions:
* The network is locked.
*
****************************************************************************/
static int slip_ifdown(FAR struct net_driver_s *dev)
{
FAR struct slip_driver_s *self =
(FAR struct slip_driver_s *)dev->d_private;
/* Disable the Ethernet interrupt */
slip_set_pollfd_events(self, 0);
/* Mark the device "down" */
self->bifup = false;
return OK;
}
/****************************************************************************
* Name: slip_txavail_work
*
* Description:
* Perform an out-of-cycle poll on the worker thread.
*
* Input Parameters:
* arg - Reference to the NuttX driver state structure (cast to void*)
*
* Returned Value:
* None
*
* Assumptions:
* Runs on a work queue thread.
*
****************************************************************************/
static void slip_txavail_work(FAR void *arg)
{
FAR struct slip_driver_s *self = (FAR struct slip_driver_s *)arg;
/* Lock the network and serialize driver operations if necessary.
* NOTE: Serialization is only required in the case where the driver work
* is performed on an LP worker thread and where more than one LP worker
* thread has been configured.
*/
net_lock();
/* Ignore the notification if the interface is not yet up */
if (self->bifup)
{
/* Check if there is room in the hardware to hold another packet. */
if (self->txlen == 0)
{
/* If so, then poll the network for new XMIT data */
self->dev.d_buf = NULL;
devif_poll(&self->dev, slip_txpoll);
}
}
net_unlock();
}
/****************************************************************************
* Name: slip_txavail
*
* Description:
* Driver callback invoked when new TX data is available. This is a
* stimulus perform an out-of-cycle poll and, thereby, reduce the TX
* latency.
*
* Input Parameters:
* dev - Reference to the NuttX driver state structure
*
* Returned Value:
* None
*
* Assumptions:
* The network is locked.
*
****************************************************************************/
static int slip_txavail(FAR struct net_driver_s *dev)
{
FAR struct slip_driver_s *self =
(FAR struct slip_driver_s *)dev->d_private;
/* Is our single work structure available? It may not be if there are
* pending interrupt actions and we will have to ignore the Tx
* availability action.
*/
if (work_available(&self->pollwork))
{
/* Schedule to serialize the poll on the worker thread. */
work_queue(SLIPWORK, &self->pollwork, slip_txavail_work, self, 0);
}
return OK;
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: slip_initialize
*
* Description:
* Instantiate a SLIP network interface.
*
* Input Parameters:
* intf - In the case where there are multiple SLIP interfaces, this
* value identifies which is to be initialized. The number of
* possible SLIP interfaces is determined by
* devname - This is the path to the serial device that will support SLIP.
* For example, this might be "/dev/ttyS1"
*
* Returned Value:
* OK on success; Negated errno on failure.
*
****************************************************************************/
int slip_initialize(int intf, FAR const char *devname)
{
FAR struct slip_driver_s *self;
int ret;
/* Get the interface structure associated with this interface number. */
DEBUGASSERT(intf < CONFIG_NET_SLIP_NINTERFACES);
self = &g_slip[intf];
/* Initialize the driver structure */
memset(self, 0, sizeof(struct slip_driver_s));
self->dev.d_ifup = slip_ifup; /* I/F up (new IP address) callback */
self->dev.d_ifdown = slip_ifdown; /* I/F down callback */
self->dev.d_txavail = slip_txavail; /* New TX data callback */
self->dev.d_private = self; /* Used to recover SLIP I/F instance */
ret = file_open(&self->tty, devname, O_RDWR | O_NONBLOCK);
if (ret < 0)
{
nerr("ERROR: Failed to open %s: %d\n", devname, ret);
return ret;
}
/* Put the interface in the down state. This usually amounts to resetting
* the device and/or calling slip_ifdown().
*/
slip_set_pollfd_events(self, 0);
self->bifup = false;
/* Register the device with the OS so that socket IOCTLs can be performed */
netdev_register(&self->dev, NET_LL_SLIP);
return OK;
}
#endif /* !defined(CONFIG_SCHED_WORKQUEUE) */
#endif /* CONFIG_NET_SLIP */