int netdriver_ifup(struct net_driver_s *dev) { netdev_ifup(dev->d_ipaddr); return OK; }
static int netdev_ifrioctl(FAR struct socket *psock, int cmd, FAR struct ifreq *req) { FAR struct net_driver_s *dev; int ret = -EINVAL; nvdbg("cmd: %d\n", cmd); /* Execute the command */ switch (cmd) { #ifdef CONFIG_NET_IPv4 case SIOCGIFADDR: /* Get IP address */ { dev = netdev_ifrdev(req); if (dev) { ioctl_getipv4addr(&req->ifr_addr, dev->d_ipaddr); ret = OK; } } break; #endif #ifdef CONFIG_NET_IPv4 case SIOCSIFADDR: /* Set IP address */ { dev = netdev_ifrdev(req); if (dev) { netdev_ifdown(dev); ioctl_setipv4addr(&dev->d_ipaddr, &req->ifr_addr); netdev_ifup(dev); ret = OK; } } break; #endif #ifdef CONFIG_NET_IPv4 case SIOCGIFDSTADDR: /* Get P-to-P address */ { dev = netdev_ifrdev(req); if (dev) { ioctl_getipv4addr(&req->ifr_dstaddr, dev->d_draddr); ret = OK; } } break; #endif #ifdef CONFIG_NET_IPv4 case SIOCSIFDSTADDR: /* Set P-to-P address */ { dev = netdev_ifrdev(req); if (dev) { ioctl_setipv4addr(&dev->d_draddr, &req->ifr_dstaddr); ret = OK; } } break; #endif #ifdef CONFIG_NET_IPv4 case SIOCGIFBRDADDR: /* Get broadcast IP address */ case SIOCSIFBRDADDR: /* Set broadcast IP address */ { ret = -ENOSYS; } break; #endif #ifdef CONFIG_NET_IPv4 case SIOCGIFNETMASK: /* Get network mask */ { dev = netdev_ifrdev(req); if (dev) { ioctl_getipv4addr(&req->ifr_addr, dev->d_netmask); ret = OK; } } break; #endif #ifdef CONFIG_NET_IPv4 case SIOCSIFNETMASK: /* Set network mask */ { dev = netdev_ifrdev(req); if (dev) { ioctl_setipv4addr(&dev->d_netmask, &req->ifr_addr); ret = OK; } } break; #endif #ifdef CONFIG_NET_IPv6 case SIOCGLIFADDR: /* Get IP address */ { dev = netdev_ifrdev(req); if (dev) { FAR struct lifreq *lreq = (FAR struct lifreq *)req; ioctl_getipv6addr(&lreq->lifr_addr, dev->d_ipv6addr); ret = OK; } } break; #endif #ifdef CONFIG_NET_IPv6 case SIOCSLIFADDR: /* Set IP address */ { dev = netdev_ifrdev(req); if (dev) { FAR struct lifreq *lreq = (FAR struct lifreq *)req; netdev_ifdown(dev); ioctl_setipv6addr(dev->d_ipv6addr, &lreq->lifr_addr); netdev_ifup(dev); ret = OK; } } break; #endif #ifdef CONFIG_NET_IPv6 case SIOCGLIFDSTADDR: /* Get P-to-P address */ { dev = netdev_ifrdev(req); if (dev) { FAR struct lifreq *lreq = (FAR struct lifreq *)req; ioctl_getipv6addr(&lreq->lifr_dstaddr, dev->d_ipv6draddr); ret = OK; } } break; #endif #ifdef CONFIG_NET_IPv6 case SIOCSLIFDSTADDR: /* Set P-to-P address */ { dev = netdev_ifrdev(req); if (dev) { FAR struct lifreq *lreq = (FAR struct lifreq *)req; ioctl_setipv6addr(dev->d_ipv6draddr, &lreq->lifr_dstaddr); ret = OK; } } break; #endif #ifdef CONFIG_NET_IPv6 case SIOCGLIFBRDADDR: /* Get broadcast IP address */ case SIOCSLIFBRDADDR: /* Set broadcast IP address */ { ret = -ENOSYS; } break; #endif #ifdef CONFIG_NET_IPv6 case SIOCGLIFNETMASK: /* Get network mask */ { dev = netdev_ifrdev(req); if (dev) { FAR struct lifreq *lreq = (FAR struct lifreq *)req; ioctl_getipv6addr(&lreq->lifr_addr, dev->d_ipv6netmask); ret = OK; } } break; #endif #ifdef CONFIG_NET_IPv6 case SIOCSLIFNETMASK: /* Set network mask */ { dev = netdev_ifrdev(req); if (dev) { FAR struct lifreq *lreq = (FAR struct lifreq *)req; ioctl_setipv6addr(dev->d_ipv6netmask, &lreq->lifr_addr); ret = OK; } } break; #endif case SIOCGLIFMTU: /* Get MTU size */ case SIOCGIFMTU: /* Get MTU size */ { dev = netdev_ifrdev(req); if (dev) { req->ifr_mtu = NET_DEV_MTU(dev); ret = OK; } } break; #ifdef CONFIG_NET_ICMPv6_AUTOCONF case SIOCIFAUTOCONF: /* Perform ICMPv6 auto-configuration */ { dev = netdev_ifrdev(req); if (dev) { ret = icmpv6_autoconfig(dev); } } break; #endif case SIOCSIFFLAGS: /* Sets the interface flags */ { /* Is this a request to bring the interface up? */ dev = netdev_ifrdev(req); if (dev) { if (req->ifr_flags & IFF_UP) { /* Yes.. bring the interface up */ netdev_ifup(dev); } /* Is this a request to take the interface down? */ else if (req->ifr_flags & IFF_DOWN) { /* Yes.. take the interface down */ netdev_ifdown(dev); } } ret = OK; } break; case SIOCGIFFLAGS: /* Gets the interface flags */ { dev = netdev_ifrdev(req); if (dev) { req->ifr_flags = dev->d_flags; } ret = OK; } break; /* MAC address operations only make sense if Ethernet is supported */ #ifdef CONFIG_NET_ETHERNET case SIOCGIFHWADDR: /* Get hardware address */ { dev = netdev_ifrdev(req); if (dev) { req->ifr_hwaddr.sa_family = AF_INETX; memcpy(req->ifr_hwaddr.sa_data, dev->d_mac.ether_addr_octet, IFHWADDRLEN); ret = OK; } } break; case SIOCSIFHWADDR: /* Set hardware address -- will not take effect until ifup */ { dev = netdev_ifrdev(req); if (dev) { memcpy(dev->d_mac.ether_addr_octet, req->ifr_hwaddr.sa_data, IFHWADDRLEN); ret = OK; } } break; #endif case SIOCDIFADDR: /* Delete IP address */ { dev = netdev_ifrdev(req); if (dev) { netdev_ifdown(dev); #ifdef CONFIG_NET_IPv4 dev->d_ipaddr = 0; #endif #ifdef CONFIG_NET_IPv6 memset(&dev->d_ipv6addr, 0, sizeof(net_ipv6addr_t)); #endif ret = OK; } } break; case SIOCGIFCOUNT: /* Get number of devices */ { req->ifr_count = netdev_count(); ret = -ENOSYS; } break; #ifdef CONFIG_NET_ARPIOCTLS case SIOCSARP: /* Set a ARP mapping */ case SIOCDARP: /* Delete an ARP mapping */ case SIOCGARP: /* Get an ARP mapping */ # error "IOCTL Commands not implemented" #endif #ifdef CONFIG_NETDEV_PHY_IOCTL #ifdef CONFIG_ARCH_PHY_INTERRUPT case SIOCMIINOTIFY: /* Set up for PHY event notifications */ { dev = netdev_ifrdev(req); if (dev && dev->d_ioctl) { struct mii_iotcl_notify_s *notify = &req->ifr_ifru.ifru_mii_notify; ret = dev->d_ioctl(dev, cmd, ((long)(uintptr_t)notify)); } } break; #endif case SIOCGMIIPHY: /* Get address of MII PHY in use */ case SIOCGMIIREG: /* Get MII register via MDIO */ case SIOCSMIIREG: /* Set MII register via MDIO */ { dev = netdev_ifrdev(req); if (dev && dev->d_ioctl) { struct mii_ioctl_data_s *mii_data = &req->ifr_ifru.ifru_mii_data; ret = dev->d_ioctl(dev, cmd, ((long)(uintptr_t)mii_data)); } } break; #endif default: { ret = -ENOTTY; } break;; } return ret; }
int icmpv6_autoconfig(FAR struct net_driver_s *dev) { #ifndef CONFIG_NET_ETHERNET /* Only Ethernet supported for now */ nerr("ERROR: Only Ethernet is supported\n"); return -ENOSYS; #else /* CONFIG_NET_ETHERNET */ struct icmpv6_rnotify_s notify; net_ipv6addr_t lladdr; int retries; int ret; /* Sanity checks */ DEBUGASSERT(dev); ninfo("Auto-configuring %s\n", dev->d_ifname); #ifdef CONFIG_NET_MULTILINK /* Only Ethernet devices are supported for now */ if (dev->d_lltype != NET_LL_ETHERNET) { nerr("ERROR: Only Ethernet is supported\n"); return -ENOSYS; } #endif /* The interface should be in the down state */ net_lock(); netdev_ifdown(dev); net_unlock(); /* IPv6 Stateless Autoconfiguration * Reference: http://www.tcpipguide.com/free/t_IPv6AutoconfigurationandRenumbering.htm * * The following is a summary of the steps a device takes when using * stateless auto-configuration: * * 1. Link-Local Address Generation: The device generates a link-local * address. Recall that this is one of the two types of local-use IPv6 * addresses. Link-local addresses have "1111 1110 10" for the first * ten bits. The generated address uses those ten bits followed by 54 * zeroes and then the 64 bit interface identifier. Typically this * will be derived from the data link layer (MAC) address. * * IEEE 802 MAC addresses, used by Ethernet and other IEEE 802 Project * networking technologies, have 48 bits. The IEEE has also defined a * format called the 64-bit extended unique identifier, abbreviated * EUI-64. To get the modified EUI-64 interface ID for a device, you * simply take the EUI-64 address and change the 7th bit from the left * (the"universal/local" or "U/L" bit) from a zero to a one. * * 128 112 96 80 64 48 32 16 * ---- ---- ---- ---- ---- ---- ---- ---- * fe80 0000 0000 0000 0000 xxxx xxxx xxxx */ lladdr[0] = HTONS(0xfe80); /* 10-bit address + 6 zeroes */ memset(&lladdr[1], 0, 4 * sizeof(uint16_t)); /* 64 more zeroes */ memcpy(&lladdr[5], dev->d_mac.ether_addr_octet, sizeof(struct ether_addr)); /* 48-bit Ethernet address */ ninfo("lladdr=%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n", lladdr[0], lladdr[1], lladdr[2], lladdr[3], lladdr[4], lladdr[6], lladdr[6], lladdr[7]); #ifdef CONFIG_NET_ICMPv6_NEIGHBOR /* Bring the interface up with no IP address */ net_lock(); netdev_ifup(dev); net_unlock(); /* 2. Link-Local Address Uniqueness Test: The node tests to ensure that * the address it generated isn't for some reason already in use on the * local network. (This is very unlikely to be an issue if the link-local * address came from a MAC address but more likely if it was based on a * generated token.) It sends a Neighbor Solicitation message using the * Neighbor Discovery (ND) protocol. It then listens for a Neighbor * Advertisement in response that indicates that another device is * already using its link-local address; if so, either a new address * must be generated, or auto-configuration fails and another method * must be employed. */ ret = icmpv6_neighbor(lladdr); /* Take the interface back down */ net_lock(); netdev_ifdown(dev); net_unlock(); if (ret == OK) { /* Hmmm... someone else responded to our Neighbor Solicitation. We * have not back-up plan in place. Just bail. */ nerr("ERROR: IP conflict\n"); return -EEXIST; } #endif /* 3. Link-Local Address Assignment: Assuming the uniqueness test passes, * the device assigns the link-local address to its IP interface. This * address can be used for communication on the local network, but not * on the wider Internet (since link-local addresses are not routed). */ net_lock(); net_ipv6addr_copy(dev->d_ipv6addr, lladdr); /* Bring the interface up with the new, temporary IP address */ netdev_ifup(dev); /* 4. Router Contact: The node next attempts to contact a local router for * more information on continuing the configuration. This is done either * by listening for Router Advertisement messages sent periodically by * routers, or by sending a specific Router Solicitation to ask a router * for information on what to do next. */ for (retries = 0; retries < CONFIG_ICMPv6_AUTOCONF_MAXTRIES; retries++) { /* Set up the Router Advertisement BEFORE we send the Router * Solicitation. */ icmpv6_rwait_setup(dev, ¬ify); /* Send the ICMPv6 Router solicitation message */ ret = icmpv6_send_message(dev, false); if (ret < 0) { nerr("ERROR: Failed send router solicitation: %d\n", ret); break; } /* Wait to receive the Router Advertisement message */ ret = icmpv6_wait_radvertise(dev, ¬ify); if (ret != -ETIMEDOUT) { /* ETIMEDOUT is the only expected failure. We will retry on that * case only. */ break; } ninfo("Timed out... retrying %d\n", retries + 1); } /* Check for failures. Note: On successful return, the network will be * in the down state, but not in the event of failures. */ if (ret < 0) { nerr("ERROR: Failed to get the router advertisement: %d (retries=%d)\n", ret, retries); /* Claim the link local address as ours by sending the ICMPv6 Neighbor * Advertisement message. */ ret = icmpv6_send_message(dev, true); if (ret < 0) { nerr("ERROR: Failed send neighbor advertisement: %d\n", ret); netdev_ifdown(dev); } /* No off-link communications; No router address. */ net_ipv6addr_copy(dev->d_ipv6draddr, g_ipv6_allzeroaddr); /* Set a netmask for the local link address */ net_ipv6addr_copy(dev->d_ipv6netmask, g_ipv6_llnetmask); /* Leave the network up and return success (even though things did not * work out quite the way we wanted). */ net_unlock(); return ret; } /* 5. Router Direction: The router provides direction to the node on how to * proceed with the auto-configuration. It may tell the node that on this * network "stateful" auto-configuration is in use, and tell it the * address of a DHCP server to use. Alternately, it will tell the host * how to determine its global Internet address. * * 6. Global Address Configuration: Assuming that stateless auto- * configuration is in use on the network, the host will configure * itself with its globally-unique Internet address. This address is * generally formed from a network prefix provided to the host by the * router, combined with the device's identifier as generated in the * first step. */ /* On success, the new address was already set (in icmpv_rnotify()). We * need only to bring the network back to the up state and return success. */ netdev_ifup(dev); net_unlock(); return OK; #endif /* CONFIG_NET_ETHERNET */ }
static int netdev_ifr_ioctl(FAR struct socket *psock, int cmd, FAR struct ifreq *req) { FAR struct net_driver_s *dev; int ret = -EINVAL; ninfo("cmd: %d\n", cmd); /* Execute the command */ switch (cmd) { #ifdef CONFIG_NET_IPv4 case SIOCGIFADDR: /* Get IP address */ { dev = netdev_ifr_dev(req); if (dev) { ioctl_get_ipv4addr(&req->ifr_addr, dev->d_ipaddr); ret = OK; } } break; #endif #ifdef CONFIG_NET_IPv4 case SIOCSIFADDR: /* Set IP address */ { dev = netdev_ifr_dev(req); if (dev) { ioctl_set_ipv4addr(&dev->d_ipaddr, &req->ifr_addr); ret = OK; } } break; #endif #ifdef CONFIG_NET_IPv4 case SIOCGIFDSTADDR: /* Get P-to-P address */ { dev = netdev_ifr_dev(req); if (dev) { ioctl_get_ipv4addr(&req->ifr_dstaddr, dev->d_draddr); ret = OK; } } break; #endif #ifdef CONFIG_NET_IPv4 case SIOCSIFDSTADDR: /* Set P-to-P address */ { dev = netdev_ifr_dev(req); if (dev) { ioctl_set_ipv4addr(&dev->d_draddr, &req->ifr_dstaddr); ret = OK; } } break; #endif #ifdef CONFIG_NET_IPv4 case SIOCGIFBRDADDR: /* Get broadcast IP address */ { dev = netdev_ifr_dev(req); if (dev) { ioctl_get_ipv4broadcast(&req->ifr_broadaddr, dev->d_ipaddr, dev->d_netmask); ret = OK; } } break; #endif #ifdef CONFIG_NET_IPv4 case SIOCSIFBRDADDR: /* Set broadcast IP address */ { ret = -ENOSYS; } break; #endif #ifdef CONFIG_NET_IPv4 case SIOCGIFNETMASK: /* Get network mask */ { dev = netdev_ifr_dev(req); if (dev) { ioctl_get_ipv4addr(&req->ifr_addr, dev->d_netmask); ret = OK; } } break; #endif #ifdef CONFIG_NET_IPv4 case SIOCSIFNETMASK: /* Set network mask */ { dev = netdev_ifr_dev(req); if (dev) { ioctl_set_ipv4addr(&dev->d_netmask, &req->ifr_addr); ret = OK; } } break; #endif #ifdef CONFIG_NET_IPv6 case SIOCGLIFADDR: /* Get IP address */ { dev = netdev_ifr_dev(req); if (dev) { FAR struct lifreq *lreq = (FAR struct lifreq *)req; ioctl_get_ipv6addr(&lreq->lifr_addr, dev->d_ipv6addr); ret = OK; } } break; #endif #ifdef CONFIG_NET_IPv6 case SIOCSLIFADDR: /* Set IP address */ { dev = netdev_ifr_dev(req); if (dev) { FAR struct lifreq *lreq = (FAR struct lifreq *)req; ioctl_set_ipv6addr(dev->d_ipv6addr, &lreq->lifr_addr); ret = OK; } } break; #endif #ifdef CONFIG_NET_IPv6 case SIOCGLIFDSTADDR: /* Get P-to-P address */ { dev = netdev_ifr_dev(req); if (dev) { FAR struct lifreq *lreq = (FAR struct lifreq *)req; ioctl_get_ipv6addr(&lreq->lifr_dstaddr, dev->d_ipv6draddr); ret = OK; } } break; #endif #ifdef CONFIG_NET_IPv6 case SIOCSLIFDSTADDR: /* Set P-to-P address */ { dev = netdev_ifr_dev(req); if (dev) { FAR struct lifreq *lreq = (FAR struct lifreq *)req; ioctl_set_ipv6addr(dev->d_ipv6draddr, &lreq->lifr_dstaddr); ret = OK; } } break; #endif #ifdef CONFIG_NET_IPv6 case SIOCGLIFBRDADDR: /* Get broadcast IP address */ case SIOCSLIFBRDADDR: /* Set broadcast IP address */ { ret = -ENOSYS; } break; #endif #ifdef CONFIG_NET_IPv6 case SIOCGLIFNETMASK: /* Get network mask */ { dev = netdev_ifr_dev(req); if (dev) { FAR struct lifreq *lreq = (FAR struct lifreq *)req; ioctl_get_ipv6addr(&lreq->lifr_addr, dev->d_ipv6netmask); ret = OK; } } break; #endif #ifdef CONFIG_NET_IPv6 case SIOCSLIFNETMASK: /* Set network mask */ { dev = netdev_ifr_dev(req); if (dev) { FAR struct lifreq *lreq = (FAR struct lifreq *)req; ioctl_set_ipv6addr(dev->d_ipv6netmask, &lreq->lifr_addr); ret = OK; } } break; #endif case SIOCGLIFMTU: /* Get MTU size */ case SIOCGIFMTU: /* Get MTU size */ { dev = netdev_ifr_dev(req); if (dev) { req->ifr_mtu = NETDEV_PKTSIZE(dev); ret = OK; } } break; #ifdef CONFIG_NET_ICMPv6_AUTOCONF case SIOCIFAUTOCONF: /* Perform ICMPv6 auto-configuration */ { dev = netdev_ifr_dev(req); if (dev) { ret = icmpv6_autoconfig(dev); } } break; #endif case SIOCSIFFLAGS: /* Sets the interface flags */ { /* Is this a request to bring the interface up? */ dev = netdev_ifr_dev(req); if (dev) { if ((req->ifr_flags & IFF_UP) != 0) { /* Yes.. bring the interface up */ netdev_ifup(dev); } /* Is this a request to take the interface down? */ else if ((req->ifr_flags & IFF_DOWN) != 0) { /* Yes.. take the interface down */ netdev_ifdown(dev); } } ret = OK; } break; case SIOCGIFFLAGS: /* Gets the interface flags */ { dev = netdev_ifr_dev(req); if (dev) { req->ifr_flags = dev->d_flags; } ret = OK; } break; /* MAC address operations only make sense if Ethernet or 6LoWPAN are * supported. */ #if defined(CONFIG_NET_ETHERNET) || defined(CONFIG_NET_6LOWPAN) case SIOCGIFHWADDR: /* Get hardware address */ { dev = netdev_ifr_dev(req); if (dev) { #ifdef CONFIG_NET_ETHERNET if (dev->d_lltype == NET_LL_ETHERNET || dev->d_lltype == NET_LL_IEEE80211) { req->ifr_hwaddr.sa_family = AF_INETX; memcpy(req->ifr_hwaddr.sa_data, dev->d_mac.ether.ether_addr_octet, IFHWADDRLEN); ret = OK; } else #endif #ifdef CONFIG_NET_6LOWPAN if (dev->d_lltype == NET_LL_IEEE802154 || dev->d_lltype == NET_LL_PKTRADIO) { req->ifr_hwaddr.sa_family = AF_INETX; memcpy(req->ifr_hwaddr.sa_data, dev->d_mac.radio.nv_addr, dev->d_mac.radio.nv_addrlen); ret = OK; } else #endif { nerr("Unsupported link layer\n"); } } } break; case SIOCSIFHWADDR: /* Set hardware address -- will not take effect until ifup */ { dev = netdev_ifr_dev(req); if (dev) { #ifdef CONFIG_NET_ETHERNET if (dev->d_lltype == NET_LL_ETHERNET) { memcpy(dev->d_mac.ether.ether_addr_octet, req->ifr_hwaddr.sa_data, IFHWADDRLEN); ret = OK; } else #endif #ifdef CONFIG_NET_6LOWPAN if (dev->d_lltype == NET_LL_IEEE802154 || dev->d_lltype == NET_LL_PKTRADIO) { FAR struct radio_driver_s *radio; struct radiodev_properties_s properties; /* Get the radio properties */ radio = (FAR struct radio_driver_s *)dev; DEBUGASSERT(radio->r_properties != NULL); ret = radio->r_properties(radio, &properties); if (ret >= 0) { dev->d_mac.radio.nv_addrlen = properties.sp_addrlen; memcpy(dev->d_mac.radio.nv_addr, req->ifr_hwaddr.sa_data, NET_6LOWPAN_ADDRSIZE); } } else #endif { nerr("Unsupported link layer\n"); } } } break; #endif case SIOCDIFADDR: /* Delete IP address */ { dev = netdev_ifr_dev(req); if (dev) { #ifdef CONFIG_NET_IPv4 dev->d_ipaddr = 0; #endif #ifdef CONFIG_NET_IPv6 memset(&dev->d_ipv6addr, 0, sizeof(net_ipv6addr_t)); #endif ret = OK; } } break; case SIOCGIFCOUNT: /* Get number of devices */ { req->ifr_count = netdev_count(); ret = -ENOSYS; } break; #ifdef CONFIG_NET_IPv4 case SIOCGIFCONF: /* Return an interface list (IPv4) */ { ret = netdev_ipv4_ifconf((FAR struct ifconf *)req); } break; #endif #ifdef CONFIG_NET_IPv6 case SIOCGLIFCONF: /* Return an interface list (IPv6) */ { ret = netdev_ipv6_ifconf((FAR struct lifconf *)req); } break; #endif #if defined(CONFIG_NETDEV_IOCTL) && defined(CONFIG_NETDEV_PHY_IOCTL) #ifdef CONFIG_ARCH_PHY_INTERRUPT case SIOCMIINOTIFY: /* Set up for PHY event notifications */ { dev = netdev_ifr_dev(req); if (dev && dev->d_ioctl) { struct mii_iotcl_notify_s *notify = &req->ifr_ifru.ifru_mii_notify; ret = dev->d_ioctl(dev, cmd, ((unsigned long)(uintptr_t)notify)); } } break; #endif case SIOCGMIIPHY: /* Get address of MII PHY in use */ case SIOCGMIIREG: /* Get MII register via MDIO */ case SIOCSMIIREG: /* Set MII register via MDIO */ { dev = netdev_ifr_dev(req); if (dev && dev->d_ioctl) { struct mii_ioctl_data_s *mii_data = &req->ifr_ifru.ifru_mii_data; ret = dev->d_ioctl(dev, cmd, ((unsigned long)(uintptr_t)mii_data)); } } break; #endif default: { ret = -ENOTTY; } break; } return ret; }