static int netdev_ifr_ioctl()

in net/netdev/netdev_ioctl.c [776:1255]


static int netdev_ifr_ioctl(FAR struct socket *psock, int cmd,
                            FAR struct ifreq *req)
{
  FAR struct net_driver_s *dev = NULL;
  unsigned int idx = 0;
  int ret = OK;

  ninfo("cmd: %d\n", cmd);

  net_lock();

  /* Execute commands that do not need ifr_name or lifr_name */

  switch (cmd)
    {
      case SIOCGIFCOUNT:  /* Get number of devices */
        req->ifr_count = netdev_count();
        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

#ifdef CONFIG_NETDEV_IFINDEX
      case SIOCSIFNAME:   /* Set interface name */
        {
          FAR struct net_driver_s *tmpdev;
          tmpdev = netdev_findbyindex(req->ifr_ifindex);
          if (tmpdev != NULL)
            {
              strlcpy(tmpdev->d_ifname, req->ifr_name, IFNAMSIZ);
            }
          else
            {
              ret = -ENODEV;
            }
        }
        break;

      case SIOCGIFNAME:  /* Get interface name */
        {
          FAR struct net_driver_s *tmpdev;
          tmpdev = netdev_findbyindex(req->ifr_ifindex);
          if (tmpdev != NULL)
            {
              strlcpy(req->ifr_name, tmpdev->d_ifname, IFNAMSIZ);
            }
          else
            {
              ret = -ENODEV;
            }
        }
        break;
#endif
      default:
        if (req == NULL)
          {
            net_unlock();
            return -ENOTTY;
          }

        if (net_ioctl_ifreq_arglen(psock->s_domain, cmd)
            >= (ssize_t)sizeof(struct ifreq))
          {
            idx = netdev_ifr_split_idx(req);
            UNUSED(idx);
            dev = netdev_findbyname(req->ifr_name);
          }
        else if (net_ioctl_ifreq_arglen(psock->s_domain, cmd)
                 == (ssize_t)sizeof(struct in6_ifreq))
          {
            FAR struct in6_ifreq *ifr6 = (FAR struct in6_ifreq *)req;
            dev = netdev_findbyindex(ifr6->ifr6_ifindex);
          }

        if (dev == NULL)
          {
            ret = -ENOTTY;
          }
        break;
    }

  if (dev == NULL)
    {
      net_unlock();
      return ret;
    }

  /* Execute commands that need ifr_name or lifr_name */

  switch (cmd)
    {
#ifdef CONFIG_NET_IPv4
      case SIOCGIFADDR:  /* Get IP address */
        ioctl_get_ipv4addr(&req->ifr_addr, dev->d_ipaddr);
        break;

      case SIOCGIFDSTADDR:  /* Get P-to-P address */
        ioctl_get_ipv4addr(&req->ifr_dstaddr, dev->d_draddr);
        break;

      case SIOCSIFDSTADDR:  /* Set P-to-P address */
        ioctl_set_ipv4addr(&dev->d_draddr, &req->ifr_dstaddr);
        break;

      case SIOCGIFBRDADDR:  /* Get broadcast IP address */
        ioctl_get_ipv4broadcast(&req->ifr_broadaddr, dev->d_ipaddr,
                                dev->d_netmask);
        break;

      case SIOCSIFBRDADDR:  /* Set broadcast IP address */
        ret = -ENOSYS;
        break;

      case SIOCGIFNETMASK:  /* Get network mask */
        ioctl_get_ipv4addr(&req->ifr_addr, dev->d_netmask);
        break;

      case SIOCSIFNETMASK:  /* Set network mask */
        ioctl_set_ipv4addr(&dev->d_netmask, &req->ifr_addr);
        break;
#endif

#ifdef CONFIG_NET_IPv6
      case SIOCGLIFADDR:  /* Get IP address */
        {
          FAR struct lifreq *lreq = (FAR struct lifreq *)req;
          idx = MIN(idx, CONFIG_NETDEV_MAX_IPv6_ADDR - 1);
          ioctl_get_ipv6addr(&lreq->lifr_addr, dev->d_ipv6[idx].addr);
        }
        break;

      case SIOCSLIFADDR:  /* Set IP address */
        {
          FAR struct lifreq *lreq = (FAR struct lifreq *)req;
          idx = MIN(idx, CONFIG_NETDEV_MAX_IPv6_ADDR - 1);

          netdev_ipv6_removemcastmac(dev, dev->d_ipv6[idx].addr);
          ioctl_set_ipv6addr(dev->d_ipv6[idx].addr, &lreq->lifr_addr);
          netdev_ipv6_addmcastmac(dev, dev->d_ipv6[idx].addr);

          netlink_device_notify_ipaddr(dev, RTM_NEWADDR, AF_INET6,
           dev->d_ipv6[idx].addr, net_ipv6_mask2pref(dev->d_ipv6[idx].mask));
        }
        break;

      case SIOCGLIFDSTADDR:  /* Get P-to-P address */
        {
          FAR struct lifreq *lreq = (FAR struct lifreq *)req;
          ioctl_get_ipv6addr(&lreq->lifr_dstaddr, dev->d_ipv6draddr);
        }
        break;

      case SIOCSLIFDSTADDR:  /* Set P-to-P address */
        {
          FAR struct lifreq *lreq = (FAR struct lifreq *)req;
          ioctl_set_ipv6addr(dev->d_ipv6draddr, &lreq->lifr_dstaddr);
        }
        break;

      case SIOCGLIFBRDADDR:  /* Get broadcast IP address */
      case SIOCSLIFBRDADDR:  /* Set broadcast IP address */
        ret = -ENOSYS;
        break;

      case SIOCGLIFNETMASK:  /* Get network mask */
        {
          FAR struct lifreq *lreq = (FAR struct lifreq *)req;
          idx = MIN(idx, CONFIG_NETDEV_MAX_IPv6_ADDR - 1);
          ioctl_get_ipv6addr(&lreq->lifr_addr, dev->d_ipv6[idx].mask);
        }
        break;

      case SIOCSLIFNETMASK:  /* Set network mask */
        {
          FAR struct lifreq *lreq = (FAR struct lifreq *)req;
          idx = MIN(idx, CONFIG_NETDEV_MAX_IPv6_ADDR - 1);
          ioctl_set_ipv6addr(dev->d_ipv6[idx].mask, &lreq->lifr_addr);
        }
        break;
#endif

      case SIOCGLIFMTU:  /* Get MTU size */
      case SIOCGIFMTU:   /* Get MTU size */
        req->ifr_mtu = NETDEV_PKTSIZE(dev) - dev->d_llhdrlen;
        break;
      case SIOCSIFMTU:   /* Set MTU size */
        NETDEV_PKTSIZE(dev) = req->ifr_mtu + dev->d_llhdrlen;
        break;

#ifdef CONFIG_NET_ICMPv6_AUTOCONF
      case SIOCIFAUTOCONF:  /* Perform ICMPv6 auto-configuration */
        ret = icmpv6_autoconfig(dev);
        break;
#endif

      case SIOCSIFFLAGS:  /* Sets the interface flags */

        /* Is this a request to bring the interface up? */

        if ((req->ifr_flags & IFF_UP) != 0)
          {
            /* Yes.. bring the interface up */

            ret = netdev_ifup(dev);
#ifdef CONFIG_NET_ARP_ACD
            /* having address then start acd */

            arp_acd_setup(dev);
#endif /* CONFIG_NET_ARP_ACD */
          }
        else
          {
            /* Yes.. take the interface down */

            ret = netdev_ifdown(dev);
          }
        break;

      case SIOCGIFFLAGS:  /* Gets the interface flags */
        req->ifr_flags = dev->d_flags;
        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 */
#ifdef CONFIG_NET_ETHERNET
        if (dev->d_lltype == NET_LL_ETHERNET ||
            dev->d_lltype == NET_LL_IEEE80211)
          {
            req->ifr_hwaddr.sa_family = ARPHRD_ETHER;
            memcpy(req->ifr_hwaddr.sa_data,
                   dev->d_mac.ether.ether_addr_octet, IFHWADDRLEN);
          }
        else
#endif
#ifdef CONFIG_NET_6LOWPAN
        if (dev->d_lltype == NET_LL_IEEE802154 ||
            dev->d_lltype == NET_LL_PKTRADIO)
          {
            req->ifr_hwaddr.sa_family = ARPHRD_IEEE802154;
            memcpy(req->ifr_hwaddr.sa_data,
                   dev->d_mac.radio.nv_addr,
                   dev->d_mac.radio.nv_addrlen);
          }
        else
#endif
          {
            nwarn("WARNING: Unsupported link layer\n");
            ret = -EAFNOSUPPORT;
          }
        break;

      case SIOCSIFHWADDR:  /* Set hardware address -- will not take effect until ifup */
#ifdef CONFIG_NET_ETHERNET
        if (dev->d_lltype == NET_LL_ETHERNET ||
            dev->d_lltype == NET_LL_IEEE80211)
          {
            memcpy(dev->d_mac.ether.ether_addr_octet,
                   req->ifr_hwaddr.sa_data, IFHWADDRLEN);
          }
        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;

                DEBUGASSERT(dev->d_mac.radio.nv_addrlen <=
                            sizeof(dev->d_mac.radio.nv_addr));
                DEBUGASSERT(dev->d_mac.radio.nv_addrlen <=
                            sizeof(req->ifr_hwaddr.sa_data));

                memcpy(dev->d_mac.radio.nv_addr,
                       req->ifr_hwaddr.sa_data, dev->d_mac.radio.nv_addrlen);
              }
          }
        else
#endif
          {
            nerr("Unsupported link layer\n");
            ret = -EAFNOSUPPORT;
          }
        break;
#endif

      case SIOCSIFADDR:  /* Set IP address */
#ifdef CONFIG_NET_IPv4
        if (psock->s_domain != PF_INET6)
          {
            if (net_ipv4addr_cmp(dev->d_ipaddr,
                ((FAR struct sockaddr_in *)&req->ifr_addr)->sin_addr.s_addr))
              {
                break;
              }

            ioctl_set_ipv4addr(&dev->d_ipaddr, &req->ifr_addr);
            netlink_device_notify_ipaddr(dev, RTM_NEWADDR, AF_INET,
                         &dev->d_ipaddr, net_ipv4_mask2pref(dev->d_netmask));

#ifdef CONFIG_NET_ARP_ACD
            arp_acd_set_addr(dev);
#endif /* CONFIG_NET_ARP_ACD */
          }
#endif

#ifdef CONFIG_NET_IPv6
        if (psock->s_domain == PF_INET6)
          {
            FAR struct in6_ifreq *ifr6 = (FAR struct in6_ifreq *)req;
            ret = netdev_ipv6_add(dev, ifr6->ifr6_addr.in6_u.u6_addr16,
                                  ifr6->ifr6_prefixlen);
            if (ret == OK)
              {
                netlink_device_notify_ipaddr(dev, RTM_NEWADDR, AF_INET6,
                      ifr6->ifr6_addr.in6_u.u6_addr16, ifr6->ifr6_prefixlen);
              }
          }
#endif
        break;

      case SIOCDIFADDR:  /* Delete IP address */
#ifdef CONFIG_NET_IPv4
        if (psock->s_domain != PF_INET6)
          {
            netlink_device_notify_ipaddr(dev, RTM_DELADDR, AF_INET,
                         &dev->d_ipaddr, net_ipv4_mask2pref(dev->d_netmask));
            dev->d_ipaddr = 0;
          }
#endif

#ifdef CONFIG_NET_IPv6
        if (psock->s_domain == PF_INET6)
          {
            FAR struct in6_ifreq *ifr6 = (FAR struct in6_ifreq *)req;
            ret = netdev_ipv6_del(dev, ifr6->ifr6_addr.in6_u.u6_addr16,
                                  ifr6->ifr6_prefixlen);
            if (ret == OK)
              {
                netlink_device_notify_ipaddr(dev, RTM_DELADDR, AF_INET6,
                      ifr6->ifr6_addr.in6_u.u6_addr16, ifr6->ifr6_prefixlen);
              }
          }
#endif
        break;

#if defined(CONFIG_NETDEV_IOCTL) && defined(CONFIG_NETDEV_PHY_IOCTL)
#ifdef CONFIG_ARCH_PHY_INTERRUPT
      case SIOCMIINOTIFY: /* Set up for PHY event notifications */
        if (dev->d_ioctl)
          {
            FAR struct mii_ioctl_notify_s *notify =
              &req->ifr_ifru.ifru_mii_notify;
            ret = dev->d_ioctl(dev, cmd, (unsigned long)(uintptr_t)notify);
          }
        else
          {
            ret = -ENOSYS;
          }
        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 */
        if (dev->d_ioctl)
          {
            FAR 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);
          }
        else
          {
            ret = -ENOSYS;
          }
        break;
#endif

#if defined(CONFIG_NETDEV_IOCTL) && defined(CONFIG_NETDEV_CAN_BITRATE_IOCTL)
      case SIOCSCANBITRATE:  /* Set bitrate of a CAN controller */
        if (dev->d_flags & IFF_UP)
          {
            /* Cannot set bitrate if the interface is up. */

            ret = -EBUSY;
            break;
          }

        /* If down, fall-through to common code in SIOCGCANBITRATE. */

      case SIOCGCANBITRATE:  /* Get bitrate from a CAN controller */
        if (dev->d_ioctl)
          {
            FAR struct can_ioctl_data_s *can_bitrate_data =
              &req->ifr_ifru.ifru_can_data;
            ret = dev->d_ioctl(dev, cmd,
                          (unsigned long)(uintptr_t)can_bitrate_data);
          }
        else
          {
            ret = -ENOSYS;
          }
        break;
#endif

#if defined(CONFIG_NETDEV_IOCTL) && defined(CONFIG_NETDEV_CAN_FILTER_IOCTL)
      case SIOCACANEXTFILTER:  /* Add an extended-ID filter */
      case SIOCDCANEXTFILTER:  /* Delete an extended-ID filter */
      case SIOCACANSTDFILTER:  /* Add a standard-ID filter */
      case SIOCDCANSTDFILTER:  /* Delete a standard-ID filter */
      case SIOCCANRECOVERY:    /* Recovery can controller when bus-off */
        if (dev->d_ioctl)
          {
            FAR struct can_ioctl_filter_s *can_filter =
              &req->ifr_ifru.ifru_can_filter;
            ret = dev->d_ioctl(dev, cmd,
                          (unsigned long)(uintptr_t)can_filter);
          }
        else
          {
            ret = -ENOSYS;
          }
        break;
#endif

#if defined(CONFIG_NETDEV_IOCTL) && defined(CONFIG_NETDEV_CAN_STATE_IOCTL)
      case SIOCGCANSTATE:  /* Get state from a CAN/LIN controller */
      case SIOCSCANSTATE:  /* Set the LIN/CAN controller state */
        if (dev->d_ioctl)
          {
            FAR struct can_ioctl_state_s *can_state =
              &req->ifr_ifru.ifru_can_state;
            ret = dev->d_ioctl(dev, cmd,
                          (unsigned long)(uintptr_t)can_state);
          }
        else
          {
            ret = -ENOSYS;
          }
        break;
#endif

#ifdef CONFIG_NETDEV_IFINDEX
      case SIOCGIFINDEX:  /* Index to name mapping */
        req->ifr_ifindex = dev->d_ifindex;
        break;
#endif

      default:
        ret = -ENOTTY;
        break;
    }

  net_unlock();
  return ret;
}