diff --git a/drivers/net/tun.c b/drivers/net/tun.c index 44f67bee01..53869a59e7 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -134,12 +134,13 @@ struct tun_device_s { bool bifup; /* true:ifup false:ifdown */ bool read_wait; + bool write_wait; WDOG_ID txpoll; /* TX poll timer */ struct work_s work; /* For deferring poll work to the work queue */ - FAR struct file *filep; FAR struct pollfd *poll_fds; sem_t waitsem; sem_t read_wait_sem; + sem_t write_wait_sem; size_t read_d_len; size_t write_d_len; @@ -170,7 +171,7 @@ static void tun_unlock(FAR struct tun_device_s *priv); /* Common TX logic */ -static int tun_fd_transmit(FAR struct tun_device_s *priv); +static void tun_fd_transmit(FAR struct tun_device_s *priv); static int tun_txpoll(FAR struct net_driver_s *dev); #ifdef CONFIG_NET_ETHERNET static int tun_txpoll_tap(FAR struct net_driver_s *dev); @@ -201,13 +202,11 @@ static int tun_txavail(FAR struct net_driver_s *dev); static int tun_addmac(FAR struct net_driver_s *dev, FAR const uint8_t *mac); static int tun_rmmac(FAR struct net_driver_s *dev, FAR const uint8_t *mac); #endif -#ifdef CONFIG_NET_ICMPv6 -static void tun_ipv6multicast(FAR struct tun_device_s *priv); -#endif -static int tun_dev_init(FAR struct tun_device_s *priv, FAR struct file *filep, +static int tun_dev_init(FAR struct tun_device_s *priv, + FAR struct file *filep, FAR const char *devfmt, bool tun); -static int tun_dev_uninit(FAR struct tun_device_s *priv); +static void tun_dev_uninit(FAR struct tun_device_s *priv); /* File interface */ @@ -291,6 +290,18 @@ static void tun_pollnotify(FAR struct tun_device_s *priv, { FAR struct pollfd *fds = priv->poll_fds; + if (priv->read_wait && (eventset & POLLIN)) + { + priv->read_wait = false; + nxsem_post(&priv->read_wait_sem); + } + + if (priv->write_wait && (eventset & POLLOUT)) + { + priv->write_wait = false; + nxsem_post(&priv->write_wait_sem); + } + if (fds == NULL) { return; @@ -316,7 +327,7 @@ static void tun_pollnotify(FAR struct tun_device_s *priv, * priv - Reference to the driver state structure * * Returned Value: - * OK on success; a negated errno on failure + * None * * Assumptions: * May or may not be called from an interrupt handler. In either case, @@ -325,23 +336,10 @@ static void tun_pollnotify(FAR struct tun_device_s *priv, * ****************************************************************************/ -static int tun_fd_transmit(FAR struct tun_device_s *priv) +static void tun_fd_transmit(FAR struct tun_device_s *priv) { NETDEV_TXPACKETS(&priv->dev); - - /* 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 (priv->read_wait) - { - priv->read_wait = false; - nxsem_post(&priv->read_wait_sem); - } - tun_pollnotify(priv, POLLIN); - return OK; } /**************************************************************************** @@ -452,8 +450,8 @@ static int tun_txpoll_tap(FAR struct net_driver_s *dev) } } - /* If zero is returned, the polling will continue until all connections have - * been examined. + /* If zero is returned, the polling will continue until all connections + * have been examined. */ return 0; @@ -589,33 +587,6 @@ static void tun_net_receive_tap(FAR struct tun_device_s *priv) arp_ipin(&priv->dev); ipv4_input(&priv->dev); - - /* If the above function invocation resulted in data that should be - * sent out on the network, the field d_len will set to a value > 0. - */ - - if (priv->dev.d_len > 0) - { - /* Update the Ethernet header with the correct MAC address */ - -#ifdef CONFIG_NET_IPv6 - if (IFF_IS_IPv4(priv->dev.d_flags)) -#endif - { - arp_out(&priv->dev); - } -#ifdef CONFIG_NET_IPv6 - else - { - neighbor_out(&priv->dev); - } -#endif - - /* And send the packet */ - - priv->write_d_len = priv->dev.d_len; - tun_fd_transmit(priv); - } } else #endif @@ -628,31 +599,6 @@ static void tun_net_receive_tap(FAR struct tun_device_s *priv) /* Give the IPv6 packet to the network layer. */ ipv6_input(&priv->dev); - - /* If the above function invocation resulted in data that should be - * sent out on the network, the field d_len will set to a value > 0. - */ - - if (priv->dev.d_len > 0) - { - /* Update the Ethernet header with the correct MAC address */ - -#ifdef CONFIG_NET_IPv4 - if (IFF_IS_IPv4(priv->dev.d_flags)) - { - arp_out(&priv->dev); - } - else -#endif -#ifdef CONFIG_NET_IPv6 - { - neighbor_out(&priv->dev); - } -#endif - - priv->write_d_len = priv->dev.d_len; - tun_fd_transmit(priv); - } } else #endif @@ -670,12 +616,41 @@ static void tun_net_receive_tap(FAR struct tun_device_s *priv) { priv->write_d_len = priv->dev.d_len; tun_fd_transmit(priv); + priv->dev.d_len = 0; } } else #endif { NETDEV_RXDROPPED(&priv->dev); + priv->dev.d_len = 0; + } + + /* If the above function invocation resulted in data that should be + * sent out on the network, the field d_len will set to a value > 0. + */ + + if (priv->dev.d_len > 0) + { + /* Update the Ethernet header with the correct MAC address */ + +#ifdef CONFIG_NET_IPv6 + if (IFF_IS_IPv4(priv->dev.d_flags)) +#endif + { + arp_out(&priv->dev); + } +#ifdef CONFIG_NET_IPv6 + else + { + neighbor_out(&priv->dev); + } +#endif + + /* And send the packet */ + + priv->write_d_len = priv->dev.d_len; + tun_fd_transmit(priv); } } #endif @@ -889,16 +864,6 @@ static int tun_ifup(FAR struct net_driver_s *dev) dev->d_ipv6addr[6], dev->d_ipv6addr[7]); #endif - /* Initialize PHYs, the Ethernet interface, and setup up Ethernet interrupts */ - - /* Instantiate the MAC address from priv->dev.d_mac.ether.ether_addr_octet */ - -#ifdef CONFIG_NET_ICMPv6 - /* Set up IPv6 multicast address filtering */ - - tun_ipv6multicast(priv); -#endif - /* Set and activate a timer process */ wd_start(priv->txpoll, TUN_WDDELAY, tun_poll_expiry, @@ -1113,7 +1078,8 @@ static void tun_ipv6multicast(FAR struct tun_device_s *priv) * ****************************************************************************/ -static int tun_dev_init(FAR struct tun_device_s *priv, FAR struct file *filep, +static int tun_dev_init(FAR struct tun_device_s *priv, + FAR struct file *filep, FAR const char *devfmt, bool tun) { int ret; @@ -1134,16 +1100,18 @@ static int tun_dev_init(FAR struct tun_device_s *priv, FAR struct file *filep, nxsem_init(&priv->waitsem, 0, 1); nxsem_init(&priv->read_wait_sem, 0, 0); + nxsem_init(&priv->write_wait_sem, 0, 0); /* The wait semaphore is used for signaling and, hence, should not have * priority inheritance enabled. */ nxsem_setprotocol(&priv->read_wait_sem, SEM_PRIO_NONE); + nxsem_setprotocol(&priv->write_wait_sem, SEM_PRIO_NONE); /* Create a watchdog for timing polling for and timing of transmissions */ - priv->txpoll = wd_create(); /* Create periodic poll timer */ + priv->txpoll = wd_create(); /* Create periodic poll timer */ /* Assign d_ifname if specified. */ @@ -1159,12 +1127,11 @@ static int tun_dev_init(FAR struct tun_device_s *priv, FAR struct file *filep, { nxsem_destroy(&priv->waitsem); nxsem_destroy(&priv->read_wait_sem); + nxsem_destroy(&priv->write_wait_sem); return ret; } - priv->filep = filep; /* Set link to file */ - filep->f_priv = priv; /* Set link to TUN device */ - + filep->f_priv = priv; /* Set link to TUN device */ return ret; } @@ -1172,7 +1139,7 @@ static int tun_dev_init(FAR struct tun_device_s *priv, FAR struct file *filep, * Name: tun_dev_uninit ****************************************************************************/ -static int tun_dev_uninit(FAR struct tun_device_s *priv) +static void tun_dev_uninit(FAR struct tun_device_s *priv) { /* Put the interface in the down state */ @@ -1184,8 +1151,7 @@ static int tun_dev_uninit(FAR struct tun_device_s *priv) nxsem_destroy(&priv->waitsem); nxsem_destroy(&priv->read_wait_sem); - - return OK; + nxsem_destroy(&priv->write_wait_sem); } /**************************************************************************** @@ -1195,7 +1161,6 @@ static int tun_dev_uninit(FAR struct tun_device_s *priv) static int tun_open(FAR struct file *filep) { filep->f_priv = 0; - return OK; } @@ -1222,7 +1187,6 @@ static int tun_close(FAR struct file *filep) tun_dev_uninit(priv); tundev_unlock(tun); - return OK; } @@ -1236,40 +1200,47 @@ static ssize_t tun_write(FAR struct file *filep, FAR const char *buffer, FAR struct tun_device_s *priv = filep->f_priv; ssize_t ret; - if (priv == NULL) + if (priv == NULL || buflen > CONFIG_NET_TUN_PKTSIZE) { return -EINVAL; } tun_lock(priv); - if (priv->write_d_len > 0) + for (; ; ) { + /* Check if there are free space to write */ + + if (priv->write_d_len == 0) + { + memcpy(priv->write_buf, buffer, buflen); + + net_lock(); + priv->dev.d_buf = priv->write_buf; + priv->dev.d_len = buflen; + + tun_net_receive(priv); + net_unlock(); + + ret = buflen; + break; + } + + /* Wait if there are no free space to write */ + + if ((filep->f_oflags & O_NONBLOCK) != 0) + { + ret = -EAGAIN; + break; + } + + priv->write_wait = true; tun_unlock(priv); - return -EBUSY; + nxsem_wait(&priv->write_wait_sem); + tun_lock(priv); } - net_lock(); - - if (buflen > CONFIG_NET_TUN_PKTSIZE) - { - ret = -EINVAL; - } - else - { - memcpy(priv->write_buf, buffer, buflen); - - priv->dev.d_buf = priv->write_buf; - priv->dev.d_len = buflen; - - tun_net_receive(priv); - - ret = (ssize_t)buflen; - } - - net_unlock(); tun_unlock(priv); - return ret; } @@ -1282,8 +1253,6 @@ static ssize_t tun_read(FAR struct file *filep, FAR char *buffer, { FAR struct tun_device_s *priv = filep->f_priv; ssize_t ret; - size_t write_d_len; - size_t read_d_len; if (priv == NULL) { @@ -1292,33 +1261,53 @@ static ssize_t tun_read(FAR struct file *filep, FAR char *buffer, tun_lock(priv); - /* Check if there are data to read in write buffer */ - - write_d_len = priv->write_d_len; - if (write_d_len > 0) + for (; ; ) { - if (buflen < write_d_len) + /* Check if there are data to read in write buffer */ + + if (priv->write_d_len > 0) { - ret = -EINVAL; - goto out; + if (buflen < priv->write_d_len) + { + ret = -EINVAL; + break; + } + + memcpy(buffer, priv->write_buf, priv->write_d_len); + ret = priv->write_d_len; + priv->write_d_len = 0; + + NETDEV_TXDONE(&priv->dev); + tun_pollnotify(priv, POLLOUT); + break; } - memcpy(buffer, priv->write_buf, write_d_len); - ret = (ssize_t)write_d_len; + /* Check if there are data to read in read buffer */ - priv->write_d_len = 0; - NETDEV_TXDONE(&priv->dev); - tun_pollnotify(priv, POLLOUT); + if (priv->read_d_len > 0) + { + if (buflen < priv->read_d_len) + { + ret = -EINVAL; + break; + } - goto out; - } + memcpy(buffer, priv->read_buf, priv->read_d_len); + ret = priv->read_d_len; + priv->read_d_len = 0; + + net_lock(); + tun_txdone(priv); + net_unlock(); + break; + } + + /* Wait if there are no data to read */ - if (priv->read_d_len == 0) - { if ((filep->f_oflags & O_NONBLOCK) != 0) { ret = -EAGAIN; - goto out; + break; } priv->read_wait = true; @@ -1327,27 +1316,7 @@ static ssize_t tun_read(FAR struct file *filep, FAR char *buffer, tun_lock(priv); } - net_lock(); - - read_d_len = priv->read_d_len; - if (buflen < read_d_len) - { - ret = -EINVAL; - } - else - { - memcpy(buffer, priv->read_buf, read_d_len); - ret = (ssize_t)read_d_len; - } - - priv->read_d_len = 0; - tun_txdone(priv); - - net_unlock(); - -out: tun_unlock(priv); - return ret; } @@ -1410,7 +1379,6 @@ int tun_poll(FAR struct file *filep, FAR struct pollfd *fds, bool setup) errout: tun_unlock(priv); - return ret; }