Esempio n. 1
0
/*
  Process the upper socket ioctl command
*/
static int dmfe_do_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
{
	board_info_t *db = (board_info_t *)dev->priv;
	#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,7) /* for kernel 2.6.7 */
    struct mii_ioctl_data *data=(struct mii_ioctl_data *)&ifr->ifr_data; 
	#endif
  int rc=0;
    	
	DMFE_DBUG(0, "dmfe_do_ioctl()", 0);
	
    	if (!netif_running(dev))
    		return -EINVAL;

    	if (cmd == SIOCETHTOOL)
        rc = dmfe_ethtool_ioctl(dev, (void *) ifr->ifr_data);
	else {
		spin_lock_irq(&db->lock);
		#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,7) /* for kernel 2.6.7 */
			rc = generic_mii_ioctl(&db->mii, data, cmd, NULL);
		#else
			rc = generic_mii_ioctl(&db->mii, if_mii(ifr), cmd, NULL);
		#endif
		spin_unlock_irq(&db->lock);
	}

	return rc;
}
Esempio n. 2
0
/* optional */
static int ftmac100_do_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd)
{
	struct ftmac100 *priv = netdev_priv(netdev);
	struct mii_ioctl_data *data = if_mii(ifr);

	return generic_mii_ioctl(&priv->mii, data, cmd, NULL);
}
/**
 * et131x_ioctl_mii - The function which handles MII IOCTLs
 * @netdev: device on which the query is being made
 * @reqbuf: the request-specific data buffer
 * @cmd: the command request code
 *
 * Returns 0 on success, errno on failure (as defined in errno.h)
 */
int et131x_ioctl_mii(struct net_device *netdev, struct ifreq *reqbuf, int cmd)
{
	int status = 0;
	struct et131x_adapter *etdev = netdev_priv(netdev);
	struct mii_ioctl_data *data = if_mii(reqbuf);

	switch (cmd) {
	case SIOCGMIIPHY:
		data->phy_id = etdev->Stats.xcvr_addr;
		break;

	case SIOCGMIIREG:
		if (!capable(CAP_NET_ADMIN))
			status = -EPERM;
		else
			status = MiRead(etdev,
					data->reg_num, &data->val_out);
		break;

	case SIOCSMIIREG:
		if (!capable(CAP_NET_ADMIN))
			status = -EPERM;
		else
			status = MiWrite(etdev, data->reg_num,
					 data->val_in);
		break;

	default:
		status = -EOPNOTSUPP;
	}
	return status;
}
Esempio n. 4
0
static int eth_ioctl(struct net_device *dev, struct ifreq *req, int cmd)
{
	struct port *port = netdev_priv(dev);

	if (!netif_running(dev))
		return -EINVAL;
	return phy_mii_ioctl(port->phydev, if_mii(req), cmd);
}
Esempio n. 5
0
/**
 * phylink_mii_ioctl() - generic mii ioctl interface
 * @pl: a pointer to a &struct phylink returned from phylink_create()
 * @ifr: a pointer to a &struct ifreq for socket ioctls
 * @cmd: ioctl cmd to execute
 *
 * Perform the specified MII ioctl on the PHY attached to the phylink instance
 * specified by @pl. If no PHY is attached, emulate the presence of the PHY.
 *
 * Returns: zero on success or negative error code.
 *
 * %SIOCGMIIPHY:
 *  read register from the current PHY.
 * %SIOCGMIIREG:
 *  read register from the specified PHY.
 * %SIOCSMIIREG:
 *  set a register on the specified PHY.
 */
int phylink_mii_ioctl(struct phylink *pl, struct ifreq *ifr, int cmd)
{
	struct mii_ioctl_data *mii = if_mii(ifr);
	int  ret;

	ASSERT_RTNL();

	if (pl->phydev) {
		/* PHYs only exist for MLO_AN_PHY and SGMII */
		switch (cmd) {
		case SIOCGMIIPHY:
			mii->phy_id = pl->phydev->mdio.addr;
			/* fall through */

		case SIOCGMIIREG:
			ret = phylink_phy_read(pl, mii->phy_id, mii->reg_num);
			if (ret >= 0) {
				mii->val_out = ret;
				ret = 0;
			}
			break;

		case SIOCSMIIREG:
			ret = phylink_phy_write(pl, mii->phy_id, mii->reg_num,
						mii->val_in);
			break;

		default:
			ret = phy_mii_ioctl(pl->phydev, ifr, cmd);
			break;
		}
	} else {
		switch (cmd) {
		case SIOCGMIIPHY:
			mii->phy_id = 0;
			/* fall through */

		case SIOCGMIIREG:
			ret = phylink_mii_read(pl, mii->phy_id, mii->reg_num);
			if (ret >= 0) {
				mii->val_out = ret;
				ret = 0;
			}
			break;

		case SIOCSMIIREG:
			ret = phylink_mii_write(pl, mii->phy_id, mii->reg_num,
						mii->val_in);
			break;

		default:
			ret = -EOPNOTSUPP;
			break;
		}
	}

	return ret;
}
Esempio n. 6
0
static int au1000_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
{
	struct au1000_private *aup = netdev_priv(dev);

	if (!netif_running(dev)) return -EINVAL;

	if (!aup->phy_dev) return -EINVAL; // PHY not controllable

	return phy_mii_ioctl(aup->phy_dev, if_mii(rq), cmd);
}
Esempio n. 7
0
static int dsa_slave_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
{
	struct dsa_slave_priv *p = netdev_priv(dev);
	struct mii_ioctl_data *mii_data = if_mii(ifr);

	if (p->phy != NULL)
		return phy_mii_ioctl(p->phy, mii_data, cmd);

	return -EOPNOTSUPP;
}
/**
 * IOCTL support for PHY control
 *
 * @dev:    Device to change
 * @rq:     the request
 * @cmd:    the command
 * Returns Zero on success
 */
int cvm_oct_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
{
	struct octeon_ethernet *priv = netdev_priv(dev);

	if (!netif_running(dev))
		return -EINVAL;

	if (!priv->phydev)
		return -EINVAL;

	return phy_mii_ioctl(priv->phydev, if_mii(rq), cmd);
}
Esempio n. 9
0
static int b44_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
{
	struct mii_ioctl_data *data = if_mii(ifr);
	struct b44 *bp = netdev_priv(dev);
	int err;

	spin_lock_irq(&bp->lock);
	err = generic_mii_ioctl(&bp->mii_if, data, cmd, NULL);
	spin_unlock_irq(&bp->lock);

	return err;
}
Esempio n. 10
0
static int bcm_enet_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
{
	struct bcm_enet_priv *priv;

	priv = netdev_priv(dev);
	if (priv->has_phy) {
		if (!priv->phydev)
			return -ENODEV;
		return phy_mii_ioctl(priv->phydev, if_mii(rq), cmd);
	} else {
		struct mii_if_info mii;

		mii.dev = dev;
		mii.mdio_read = bcm_enet_mdio_read_mii;
		mii.mdio_write = bcm_enet_mdio_write_mii;
		mii.phy_id = 0;
		mii.phy_id_mask = 0x3f;
		mii.reg_num_mask = 0x1f;
		return generic_mii_ioctl(&mii, if_mii(rq), cmd, NULL);
	}
}
Esempio n. 11
0
int cvm_oct_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
{
	struct octeon_ethernet *priv = netdev_priv(dev);
	struct mii_ioctl_data *data = if_mii(rq);
	unsigned int duplex_chg;
	int ret;

	down(&mdio_sem);
	ret = generic_mii_ioctl(&priv->mii_info, data, cmd, &duplex_chg);
	up(&mdio_sem);

	return ret;
}
Esempio n. 12
0
static void wpa_driver_roboswitch_mdio_write(
	struct wpa_driver_roboswitch_data *drv, u8 reg, u16 val)
{
	struct mii_ioctl_data *mii = if_mii(&drv->ifr);

	mii->phy_id = ROBO_PHY_ADDR;
	mii->reg_num = reg;
	mii->val_in = val;

	if (ioctl(drv->fd, SIOCSMIIREG, &drv->ifr) < 0) {
		perror("ioctl[SIOCSMIIREG");
	}
}
Esempio n. 13
0
static int enet_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
{
	struct tangox_enet_priv *priv;
	int ret;

	priv = netdev_priv(dev);

	spin_lock_bh(&priv->mii);
	ret = generic_mii_ioctl(&priv->mii, if_mii(rq), cmd, NULL);
	spin_unlock_bh(&priv->mii);

	return ret;
}
Esempio n. 14
0
static int cp_ioctl (struct net_device *dev, struct ifreq *rq, int cmd)
{
	struct cp_private *cp = netdev_priv(dev);
	int rc;
	unsigned long flags;

	if (!netif_running(dev))
		return -EINVAL;

	spin_lock_irqsave(&cp->lock, flags);
	rc = generic_mii_ioctl(&cp->mii_if, if_mii(rq), cmd, NULL);
	spin_unlock_irqrestore(&cp->lock, flags);
	return rc;
}
Esempio n. 15
0
static u16 wpa_driver_roboswitch_mdio_read(
	struct wpa_driver_roboswitch_data *drv, u8 reg)
{
	struct mii_ioctl_data *mii = if_mii(&drv->ifr);

	mii->phy_id = ROBO_PHY_ADDR;
	mii->reg_num = reg;

	if (ioctl(drv->fd, SIOCGMIIREG, &drv->ifr) < 0) {
		perror("ioctl[SIOCGMIIREG]");
		return 0x00;
	}
	return mii->val_out;
}
Esempio n. 16
0
/* ioctl to device funciotn*/
static int sh_eth_do_ioctl(struct net_device *ndev, struct ifreq *rq,
                           int cmd)
{
    struct sh_eth_private *mdp = netdev_priv(ndev);
    struct phy_device *phydev = mdp->phydev;

    if (!netif_running(ndev))
        return -EINVAL;

    if (!phydev)
        return -ENODEV;

    return phy_mii_ioctl(phydev, if_mii(rq), cmd);
}
Esempio n. 17
0
static int netdev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
{
	struct mii_ioctl_data *data = if_mii(rq);
	struct am_net_private *np = netdev_priv(dev);
	char addr[MAX_ADDR_LEN];
	if (debug > 0)
		printk("Ethernet Driver  ioctl (%x) \n", cmd);
	switch (cmd) {
	case SIOCGMIIPHY:	/* Get address of MII PHY in use. */
		data->phy_id =
		    ((struct am_net_private *)netdev_priv(dev))->phys[0] & 0x1f;
		/* Fall Through */

	case SIOCGMIIREG:	/* Read MII PHY register. */
		spin_lock_irq(&np->lock);
		data->val_out =
		    mdio_read(dev, data->phy_id & 0x1f, data->reg_num & 0x1f);
		spin_unlock_irq(&np->lock);
		return 0;

	case SIOCSMIIREG:	/* Write MII PHY register. */
		if (!capable(CAP_NET_ADMIN))
			return -EPERM;
		spin_lock_irq(&np->lock);
		mdio_write(dev, data->phy_id & 0x1f, data->reg_num & 0x1f,data->val_in);
		spin_unlock_irq(&np->lock);
		return 0;
	case SIOCSIFHWADDR:

		if (copy_from_user(&addr,
				   (void __user *)rq->ifr_hwaddr.sa_data,
				   MAX_ADDR_LEN)) {
			return -EFAULT;
		}
		if (debug > 0)
			printk("set mac addr to %02x:%02x:%02x:%02x:%02x:%02x\n",
			     addr[0], addr[1], addr[2], addr[3], addr[4],
			     addr[5]);
		spin_lock_irq(&np->lock);
		memcpy(dev->dev_addr, &addr, MAX_ADDR_LEN);
		write_mac_addr(dev, addr);
		spin_unlock_irq(&np->lock);
	default:
		if (debug > 0)
			printk("Ethernet Driver unknow ioctl (%x) \n", cmd);
		return -EOPNOTSUPP;
	}
	return 0;
}
Esempio n. 18
0
static int ubi32_eth_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
{
	struct ubi32_eth_private *priv = netdev_priv(dev);
	struct mii_ioctl_data *data = if_mii(rq);

	printk(KERN_INFO "ioctl %s, %d\n", dev->name, cmd);
	switch (cmd) {
	case SIOCGMIIPHY:
		data->phy_id = 0;
		break;

	case SIOCGMIIREG:
		if ((data->reg_num & 0x1F) == MII_BMCR) {
			/* Make up MII control register value from what we know */
			data->val_out = 0x0000
			| ((priv->regs->status & UBI32_ETH_VP_STATUS_DUPLEX)
					? BMCR_FULLDPLX : 0)
			| ((priv->regs->status & UBI32_ETH_VP_STATUS_SPEED100)
					? BMCR_SPEED100 : 0)
			| ((priv->regs->status & UBI32_ETH_VP_STATUS_SPEED1000)
					? BMCR_SPEED1000 : 0);
		} else if ((data->reg_num & 0x1F) == MII_BMSR) {
			/* Make up MII status register value from what we know */
			data->val_out =
			(BMSR_100FULL|BMSR_100HALF|BMSR_10FULL|BMSR_10HALF)
			| ((priv->regs->status & UBI32_ETH_VP_STATUS_LINK)
					? BMSR_LSTATUS : 0);
		} else {
			return -EIO;
		}
		break;

	case SIOCSMIIREG:
		return -EOPNOTSUPP;
		break;

	default:
		return -EOPNOTSUPP;
	}

	return 0;
}
Esempio n. 19
0
static int wrn_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
{
	struct wrn_ep *ep = netdev_priv(dev);
	int res;
	u32 reg;

	switch (cmd) {
	case SIOCSHWTSTAMP:
		return wrn_tstamp_ioctl(dev, rq, cmd);
	case PRIV_IOCGCALIBRATE:
		return wrn_calib_ioctl(dev, rq, cmd);
	case PRIV_IOCGGETPHASE:
		return wrn_phase_ioctl(dev, rq, cmd);
	case PRIV_IOCREADREG:
		if (get_user(reg, (u32 *)rq->ifr_data) < 0)
			return -EFAULT;
		if (reg > sizeof(struct EP_WB) || reg & 3)
			return -EINVAL;
		reg = readl((void *)ep->ep_regs + reg);
		if (put_user(reg, (u32 *)rq->ifr_data) < 0)
			return -EFAULT;
		return 0;
	case PRIV_IOCPHYREG:
		/* this command allows to read and write a phy register */
		if (get_user(reg, (u32 *)rq->ifr_data) < 0)
			return -EFAULT;
		if (reg & (1<<31)) {
			wrn_phy_write(dev, 0, (reg >> 16) & 0xff,
				      reg & 0xffff);
			return 0;
		}
		reg = wrn_phy_read(dev, 0, (reg >> 16) & 0xff);
		if (put_user(reg, (u32 *)rq->ifr_data) < 0)
			return -EFAULT;
		return 0;

	default:
		spin_lock_irq(&ep->lock);
		res = generic_mii_ioctl(&ep->mii, if_mii(rq), cmd, NULL);
		spin_unlock_irq(&ep->lock);
		return res;
	}
Esempio n. 20
0
/**
 * et131x_ioctl_mii - The function which handles MII IOCTLs
 * @netdev: device on which the query is being made
 * @reqbuf: the request-specific data buffer
 * @cmd: the command request code
 *
 * Returns 0 on success, errno on failure (as defined in errno.h)
 */
int et131x_ioctl_mii(struct net_device *netdev, struct ifreq *reqbuf, int cmd)
{
	int status = 0;
	struct et131x_adapter *pAdapter = netdev_priv(netdev);
	struct mii_ioctl_data *data = if_mii(reqbuf);

	DBG_ENTER(et131x_dbginfo);

	switch (cmd) {
	case SIOCGMIIPHY:
		DBG_VERBOSE(et131x_dbginfo, "SIOCGMIIPHY\n");
		data->phy_id = pAdapter->Stats.xcvr_addr;
		break;

	case SIOCGMIIREG:
		DBG_VERBOSE(et131x_dbginfo, "SIOCGMIIREG\n");
		if (!capable(CAP_NET_ADMIN)) {
			status = -EPERM;
		} else {
			status = MiRead(pAdapter,
					data->reg_num, &data->val_out);
		}
		break;

	case SIOCSMIIREG:
		DBG_VERBOSE(et131x_dbginfo, "SIOCSMIIREG\n");
		if (!capable(CAP_NET_ADMIN)) {
			status = -EPERM;
		} else {
			status = MiWrite(pAdapter, data->reg_num,
					 data->val_in);
		}
		break;

	default:
		status = -EOPNOTSUPP;
	}

	DBG_LEAVE(et131x_dbginfo);
	return status;
}
Esempio n. 21
0
static int ei_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
{
    pcnet_dev_t *info = PRIV(dev);
    struct mii_ioctl_data *data = if_mii(rq);
    unsigned int mii_addr = dev->base_addr + DLINK_GPIO;

    if (!(info->flags & (IS_DL10019|IS_DL10022)))
	return -EINVAL;

    switch (cmd) {
    case SIOCGMIIPHY:
	data->phy_id = info->phy_id;
    case SIOCGMIIREG:		/* Read MII PHY register. */
	data->val_out = mdio_read(mii_addr, data->phy_id, data->reg_num & 0x1f);
	return 0;
    case SIOCSMIIREG:		/* Write MII PHY register. */
	mdio_write(mii_addr, data->phy_id, data->reg_num & 0x1f, data->val_in);
	return 0;
    }
    return -EOPNOTSUPP;
}
Esempio n. 22
0
static int t1_ioctl(struct net_device *dev, struct ifreq *req, int cmd)
{
        struct adapter *adapter = dev->priv;
        struct mii_ioctl_data *data = if_mii(req);

	switch (cmd) {
        case SIOCGMIIPHY:
                data->phy_id = adapter->port[dev->if_port].phy->addr;
                /* FALLTHRU */
        case SIOCGMIIREG: {
		struct cphy *phy = adapter->port[dev->if_port].phy;
		u32 val;

		if (!phy->mdio_read)
            return -EOPNOTSUPP;
		phy->mdio_read(adapter, data->phy_id, 0, data->reg_num & 0x1f,
			       &val);
                data->val_out = val;
                break;
	}
        case SIOCSMIIREG: {
		struct cphy *phy = adapter->port[dev->if_port].phy;

                if (!capable(CAP_NET_ADMIN))
                    return -EPERM;
		if (!phy->mdio_write)
            return -EOPNOTSUPP;
		phy->mdio_write(adapter, data->phy_id, 0, data->reg_num & 0x1f,
			        data->val_in);
                break;
	}

	default:
		return -EOPNOTSUPP;
	}
	return 0;
}
Esempio n. 23
0
static int qf9700_ioctl(struct net_device *net, struct ifreq *rq, int cmd)
{
	struct usbnet *dev = netdev_priv(net);

	return generic_mii_ioctl(&dev->mii, if_mii(rq), cmd, NULL);
}
Esempio n. 24
0
/**
 * cvm_oct_ioctl - IOCTL support for PHY control
 * @dev:    Device to change
 * @rq:     the request
 * @cmd:    the command
 *
 * Returns Zero on success
 */
int cvm_oct_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
{
	struct octeon_ethernet *priv = netdev_priv(dev);
	cvmx_srio_tx_message_header_t tx_header;
	int ivalue;

	switch (cmd) {
	case CAVIUM_NET_IOCTL_SETPRIO:
		ivalue = rq->ifr_ifru.ifru_ivalue;
		if ((ivalue >= 0) && (ivalue < 4)) {
			tx_header.u64 = priv->srio_tx_header;
			tx_header.s.prio = ivalue;
			priv->srio_tx_header = tx_header.u64;
			return 0;
		}
		return -EINVAL;

	case CAVIUM_NET_IOCTL_GETPRIO:
		tx_header.u64 = priv->srio_tx_header;
		rq->ifr_ifru.ifru_ivalue = tx_header.s.prio;
		return 0;

	case CAVIUM_NET_IOCTL_SETIDSIZE:
		ivalue = rq->ifr_ifru.ifru_ivalue;
		if ((ivalue >= 0) && (ivalue < 2)) {
			tx_header.u64 = priv->srio_tx_header;
			tx_header.s.tt = ivalue;
			priv->srio_tx_header = tx_header.u64;
			return 0;
		}
		return -EINVAL;

	case CAVIUM_NET_IOCTL_GETIDSIZE:
		tx_header.u64 = priv->srio_tx_header;
		rq->ifr_ifru.ifru_ivalue = tx_header.s.tt;
		return 0;

	case CAVIUM_NET_IOCTL_SETSRCID:
		ivalue = rq->ifr_ifru.ifru_ivalue;
		if ((ivalue >= 0) && (ivalue < 2)) {
			tx_header.u64 = priv->srio_tx_header;
			tx_header.s.sis = ivalue;
			priv->srio_tx_header = tx_header.u64;
			return 0;
		}
		return -EINVAL;

	case CAVIUM_NET_IOCTL_GETSRCID:
		tx_header.u64 = priv->srio_tx_header;
		rq->ifr_ifru.ifru_ivalue = tx_header.s.sis;
		return 0;

	case CAVIUM_NET_IOCTL_SETLETTER:
		ivalue = rq->ifr_ifru.ifru_ivalue;
		if ((ivalue >= -1) && (ivalue < 4)) {
			tx_header.u64 = priv->srio_tx_header;
			tx_header.s.lns = (ivalue == -1);
			if (tx_header.s.lns)
				tx_header.s.letter = 0;
			else
				tx_header.s.letter = ivalue;
			priv->srio_tx_header = tx_header.u64;
			return 0;
		}
		return -EINVAL;

	case CAVIUM_NET_IOCTL_GETLETTER:
		tx_header.u64 = priv->srio_tx_header;
		if (tx_header.s.lns)
			rq->ifr_ifru.ifru_ivalue = -1;
		else
			rq->ifr_ifru.ifru_ivalue = tx_header.s.letter;
		return 0;

	case SIOCSHWTSTAMP:
		return cvm_oct_ioctl_hwtstamp(dev, rq, cmd);

	default:
		if (priv->phydev)
			return phy_mii_ioctl(priv->phydev, if_mii(rq), cmd);
		return -EINVAL;
	}
}
static void * wpa_driver_roboswitch_init(void *ctx, const char *ifname)
{
	struct wpa_driver_roboswitch_data *drv;
	char *sep;
	u16 vlan = 0, _read[2];

	drv = os_zalloc(sizeof(*drv));
	if (drv == NULL) return NULL;
	drv->ctx = ctx;
	drv->own_addr[0] = '\0';

	/* copy ifname and take a pointer to the second to last character */
	sep = drv->ifname +
	      os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname)) - 2;
	/* find the '.' separating <interface> and <vlan> */
	while (sep > drv->ifname && *sep != '.') sep--;
	if (sep <= drv->ifname) {
		wpa_printf(MSG_INFO, "%s: No <interface>.<vlan> pair in "
			   "interface name %s", __func__, drv->ifname);
		os_free(drv);
		return NULL;
	}
	*sep = '\0';
	while (*++sep) {
		if (*sep < '0' || *sep > '9') {
			wpa_printf(MSG_INFO, "%s: Invalid vlan specification "
				   "in interface name %s", __func__, ifname);
			os_free(drv);
			return NULL;
		}
		vlan *= 10;
		vlan += *sep - '0';
		if (vlan > ROBO_VLAN_MAX) {
			wpa_printf(MSG_INFO, "%s: VLAN out of range in "
				   "interface name %s", __func__, ifname);
			os_free(drv);
			return NULL;
		}
	}

	drv->fd = socket(PF_INET, SOCK_DGRAM, 0);
	if (drv->fd < 0) {
		wpa_printf(MSG_INFO, "%s: Unable to create socket", __func__);
		os_free(drv);
		return NULL;
	}

	os_memset(&drv->ifr, 0, sizeof(drv->ifr));
	os_strlcpy(drv->ifr.ifr_name, drv->ifname, IFNAMSIZ);
	if (ioctl(drv->fd, SIOCGMIIPHY, &drv->ifr) < 0) {
		wpa_printf(MSG_ERROR, "ioctl[SIOCGMIIPHY]: %s",
			   strerror(errno));
		os_free(drv);
		return NULL;
	}
	/* BCM63xx devices provide 0 here */
	if (if_mii(&drv->ifr)->phy_id != ROBO_PHY_ADDR &&
	    if_mii(&drv->ifr)->phy_id != 0) {
		wpa_printf(MSG_INFO, "%s: Invalid phy address (not a "
			   "RoboSwitch?)", __func__);
		os_free(drv);
		return NULL;
	}

	/* set and read back to see if the register can be used */
	_read[0] = ROBO_VLAN_MAX;
	wpa_driver_roboswitch_write(drv, ROBO_VLAN_PAGE, ROBO_VLAN_ACCESS_5350,
				    _read, 1);
	wpa_driver_roboswitch_read(drv, ROBO_VLAN_PAGE, ROBO_VLAN_ACCESS_5350,
				   _read + 1, 1);
	drv->is_5350 = _read[0] == _read[1];

	/* set the read bit */
	vlan |= 1 << 13;
	wpa_driver_roboswitch_write(drv, ROBO_VLAN_PAGE,
				    drv->is_5350 ? ROBO_VLAN_ACCESS_5350
						 : ROBO_VLAN_ACCESS,
				    &vlan, 1);
	wpa_driver_roboswitch_read(drv, ROBO_VLAN_PAGE, ROBO_VLAN_READ, _read,
				   drv->is_5350 ? 2 : 1);
	if (!(drv->is_5350 ? _read[1] & (1 << 4) : _read[0] & (1 << 14))) {
		wpa_printf(MSG_INFO, "%s: Could not get port information for "
				     "VLAN %d", __func__, vlan & ~(1 << 13));
		os_free(drv);
		return NULL;
	}
	drv->ports = _read[0] & 0x001F;
	/* add the MII port */
	drv->ports |= 1 << 8;
	if (wpa_driver_roboswitch_join(drv, drv->ports, pae_group_addr) < 0) {
		wpa_printf(MSG_INFO, "%s: Unable to join PAE group", __func__);
		os_free(drv);
		return NULL;
	} else {
		wpa_printf(MSG_DEBUG, "%s: Added PAE group address to "
			   "RoboSwitch ARL", __func__);
	}

	return drv;
}