static int ns_ioctl()

in nicstar.c [2480:2690]


static int ns_ioctl(struct atm_dev *dev, unsigned int cmd, void __user * arg)
{
	ns_dev *card;
	pool_levels pl;
	long btype;
	unsigned long flags;

	card = dev->dev_data;
	switch (cmd) {
	case NS_GETPSTAT:
		if (get_user
		    (pl.buftype, &((pool_levels __user *) arg)->buftype))
			return -EFAULT;
		switch (pl.buftype) {
		case NS_BUFTYPE_SMALL:
			pl.count =
			    ns_stat_sfbqc_get(readl(card->membase + STAT));
			pl.level.min = card->sbnr.min;
			pl.level.init = card->sbnr.init;
			pl.level.max = card->sbnr.max;
			break;

		case NS_BUFTYPE_LARGE:
			pl.count =
			    ns_stat_lfbqc_get(readl(card->membase + STAT));
			pl.level.min = card->lbnr.min;
			pl.level.init = card->lbnr.init;
			pl.level.max = card->lbnr.max;
			break;

		case NS_BUFTYPE_HUGE:
			pl.count = card->hbpool.count;
			pl.level.min = card->hbnr.min;
			pl.level.init = card->hbnr.init;
			pl.level.max = card->hbnr.max;
			break;

		case NS_BUFTYPE_IOVEC:
			pl.count = card->iovpool.count;
			pl.level.min = card->iovnr.min;
			pl.level.init = card->iovnr.init;
			pl.level.max = card->iovnr.max;
			break;

		default:
			return -ENOIOCTLCMD;

		}
		if (!copy_to_user((pool_levels __user *) arg, &pl, sizeof(pl)))
			return (sizeof(pl));
		else
			return -EFAULT;

	case NS_SETBUFLEV:
		if (!capable(CAP_NET_ADMIN))
			return -EPERM;
		if (copy_from_user(&pl, (pool_levels __user *) arg, sizeof(pl)))
			return -EFAULT;
		if (pl.level.min >= pl.level.init
		    || pl.level.init >= pl.level.max)
			return -EINVAL;
		if (pl.level.min == 0)
			return -EINVAL;
		switch (pl.buftype) {
		case NS_BUFTYPE_SMALL:
			if (pl.level.max > TOP_SB)
				return -EINVAL;
			card->sbnr.min = pl.level.min;
			card->sbnr.init = pl.level.init;
			card->sbnr.max = pl.level.max;
			break;

		case NS_BUFTYPE_LARGE:
			if (pl.level.max > TOP_LB)
				return -EINVAL;
			card->lbnr.min = pl.level.min;
			card->lbnr.init = pl.level.init;
			card->lbnr.max = pl.level.max;
			break;

		case NS_BUFTYPE_HUGE:
			if (pl.level.max > TOP_HB)
				return -EINVAL;
			card->hbnr.min = pl.level.min;
			card->hbnr.init = pl.level.init;
			card->hbnr.max = pl.level.max;
			break;

		case NS_BUFTYPE_IOVEC:
			if (pl.level.max > TOP_IOVB)
				return -EINVAL;
			card->iovnr.min = pl.level.min;
			card->iovnr.init = pl.level.init;
			card->iovnr.max = pl.level.max;
			break;

		default:
			return -EINVAL;

		}
		return 0;

	case NS_ADJBUFLEV:
		if (!capable(CAP_NET_ADMIN))
			return -EPERM;
		btype = (long)arg;	/* a long is the same size as a pointer or bigger */
		switch (btype) {
		case NS_BUFTYPE_SMALL:
			while (card->sbfqc < card->sbnr.init) {
				struct sk_buff *sb;

				sb = __dev_alloc_skb(NS_SMSKBSIZE, GFP_KERNEL);
				if (sb == NULL)
					return -ENOMEM;
				NS_PRV_BUFTYPE(sb) = BUF_SM;
				skb_queue_tail(&card->sbpool.queue, sb);
				skb_reserve(sb, NS_AAL0_HEADER);
				push_rxbufs(card, sb);
			}
			break;

		case NS_BUFTYPE_LARGE:
			while (card->lbfqc < card->lbnr.init) {
				struct sk_buff *lb;

				lb = __dev_alloc_skb(NS_LGSKBSIZE, GFP_KERNEL);
				if (lb == NULL)
					return -ENOMEM;
				NS_PRV_BUFTYPE(lb) = BUF_LG;
				skb_queue_tail(&card->lbpool.queue, lb);
				skb_reserve(lb, NS_SMBUFSIZE);
				push_rxbufs(card, lb);
			}
			break;

		case NS_BUFTYPE_HUGE:
			while (card->hbpool.count > card->hbnr.init) {
				struct sk_buff *hb;

				spin_lock_irqsave(&card->int_lock, flags);
				hb = skb_dequeue(&card->hbpool.queue);
				card->hbpool.count--;
				spin_unlock_irqrestore(&card->int_lock, flags);
				if (hb == NULL)
					printk
					    ("nicstar%d: huge buffer count inconsistent.\n",
					     card->index);
				else
					dev_kfree_skb_any(hb);

			}
			while (card->hbpool.count < card->hbnr.init) {
				struct sk_buff *hb;

				hb = __dev_alloc_skb(NS_HBUFSIZE, GFP_KERNEL);
				if (hb == NULL)
					return -ENOMEM;
				NS_PRV_BUFTYPE(hb) = BUF_NONE;
				spin_lock_irqsave(&card->int_lock, flags);
				skb_queue_tail(&card->hbpool.queue, hb);
				card->hbpool.count++;
				spin_unlock_irqrestore(&card->int_lock, flags);
			}
			break;

		case NS_BUFTYPE_IOVEC:
			while (card->iovpool.count > card->iovnr.init) {
				struct sk_buff *iovb;

				spin_lock_irqsave(&card->int_lock, flags);
				iovb = skb_dequeue(&card->iovpool.queue);
				card->iovpool.count--;
				spin_unlock_irqrestore(&card->int_lock, flags);
				if (iovb == NULL)
					printk
					    ("nicstar%d: iovec buffer count inconsistent.\n",
					     card->index);
				else
					dev_kfree_skb_any(iovb);

			}
			while (card->iovpool.count < card->iovnr.init) {
				struct sk_buff *iovb;

				iovb = alloc_skb(NS_IOVBUFSIZE, GFP_KERNEL);
				if (iovb == NULL)
					return -ENOMEM;
				NS_PRV_BUFTYPE(iovb) = BUF_NONE;
				spin_lock_irqsave(&card->int_lock, flags);
				skb_queue_tail(&card->iovpool.queue, iovb);
				card->iovpool.count++;
				spin_unlock_irqrestore(&card->int_lock, flags);
			}
			break;

		default:
			return -EINVAL;

		}
		return 0;

	default:
		if (dev->phy && dev->phy->ioctl) {
			return dev->phy->ioctl(dev, cmd, arg);
		} else {
			printk("nicstar%d: %s == NULL \n", card->index,
			       dev->phy ? "dev->phy->ioctl" : "dev->phy");
			return -ENOIOCTLCMD;
		}
	}
}