static int ns_open()

in nicstar.c [1232:1412]


static int ns_open(struct atm_vcc *vcc)
{
	ns_dev *card;
	vc_map *vc;
	unsigned long tmpl, modl;
	int tcr, tcra;		/* target cell rate, and absolute value */
	int n = 0;		/* Number of entries in the TST. Initialized to remove
				   the compiler warning. */
	u32 u32d[4];
	int frscdi = 0;		/* Index of the SCD. Initialized to remove the compiler
				   warning. How I wish compilers were clever enough to
				   tell which variables can truly be used
				   uninitialized... */
	int inuse;		/* tx or rx vc already in use by another vcc */
	short vpi = vcc->vpi;
	int vci = vcc->vci;

	card = (ns_dev *) vcc->dev->dev_data;
	PRINTK("nicstar%d: opening vpi.vci %d.%d \n", card->index, (int)vpi,
	       vci);
	if (vcc->qos.aal != ATM_AAL5 && vcc->qos.aal != ATM_AAL0) {
		PRINTK("nicstar%d: unsupported AAL.\n", card->index);
		return -EINVAL;
	}

	vc = &(card->vcmap[vpi << card->vcibits | vci]);
	vcc->dev_data = vc;

	inuse = 0;
	if (vcc->qos.txtp.traffic_class != ATM_NONE && vc->tx)
		inuse = 1;
	if (vcc->qos.rxtp.traffic_class != ATM_NONE && vc->rx)
		inuse += 2;
	if (inuse) {
		printk("nicstar%d: %s vci already in use.\n", card->index,
		       inuse == 1 ? "tx" : inuse == 2 ? "rx" : "tx and rx");
		return -EINVAL;
	}

	set_bit(ATM_VF_ADDR, &vcc->flags);

	/* NOTE: You are not allowed to modify an open connection's QOS. To change
	   that, remove the ATM_VF_PARTIAL flag checking. There may be other changes
	   needed to do that. */
	if (!test_bit(ATM_VF_PARTIAL, &vcc->flags)) {
		scq_info *scq;

		set_bit(ATM_VF_PARTIAL, &vcc->flags);
		if (vcc->qos.txtp.traffic_class == ATM_CBR) {
			/* Check requested cell rate and availability of SCD */
			if (vcc->qos.txtp.max_pcr == 0 && vcc->qos.txtp.pcr == 0
			    && vcc->qos.txtp.min_pcr == 0) {
				PRINTK
				    ("nicstar%d: trying to open a CBR vc with cell rate = 0 \n",
				     card->index);
				clear_bit(ATM_VF_PARTIAL, &vcc->flags);
				clear_bit(ATM_VF_ADDR, &vcc->flags);
				return -EINVAL;
			}

			tcr = atm_pcr_goal(&(vcc->qos.txtp));
			tcra = tcr >= 0 ? tcr : -tcr;

			PRINTK("nicstar%d: target cell rate = %d.\n",
			       card->index, vcc->qos.txtp.max_pcr);

			tmpl =
			    (unsigned long)tcra *(unsigned long)
			    NS_TST_NUM_ENTRIES;
			modl = tmpl % card->max_pcr;

			n = (int)(tmpl / card->max_pcr);
			if (tcr > 0) {
				if (modl > 0)
					n++;
			} else if (tcr == 0) {
				if ((n =
				     (card->tst_free_entries -
				      NS_TST_RESERVED)) <= 0) {
					PRINTK
					    ("nicstar%d: no CBR bandwidth free.\n",
					     card->index);
					clear_bit(ATM_VF_PARTIAL, &vcc->flags);
					clear_bit(ATM_VF_ADDR, &vcc->flags);
					return -EINVAL;
				}
			}

			if (n == 0) {
				printk
				    ("nicstar%d: selected bandwidth < granularity.\n",
				     card->index);
				clear_bit(ATM_VF_PARTIAL, &vcc->flags);
				clear_bit(ATM_VF_ADDR, &vcc->flags);
				return -EINVAL;
			}

			if (n > (card->tst_free_entries - NS_TST_RESERVED)) {
				PRINTK
				    ("nicstar%d: not enough free CBR bandwidth.\n",
				     card->index);
				clear_bit(ATM_VF_PARTIAL, &vcc->flags);
				clear_bit(ATM_VF_ADDR, &vcc->flags);
				return -EINVAL;
			} else
				card->tst_free_entries -= n;

			XPRINTK("nicstar%d: writing %d tst entries.\n",
				card->index, n);
			for (frscdi = 0; frscdi < NS_FRSCD_NUM; frscdi++) {
				if (card->scd2vc[frscdi] == NULL) {
					card->scd2vc[frscdi] = vc;
					break;
				}
			}
			if (frscdi == NS_FRSCD_NUM) {
				PRINTK
				    ("nicstar%d: no SCD available for CBR channel.\n",
				     card->index);
				card->tst_free_entries += n;
				clear_bit(ATM_VF_PARTIAL, &vcc->flags);
				clear_bit(ATM_VF_ADDR, &vcc->flags);
				return -EBUSY;
			}

			vc->cbr_scd = NS_FRSCD + frscdi * NS_FRSCD_SIZE;

			scq = get_scq(card, CBR_SCQSIZE, vc->cbr_scd);
			if (scq == NULL) {
				PRINTK("nicstar%d: can't get fixed rate SCQ.\n",
				       card->index);
				card->scd2vc[frscdi] = NULL;
				card->tst_free_entries += n;
				clear_bit(ATM_VF_PARTIAL, &vcc->flags);
				clear_bit(ATM_VF_ADDR, &vcc->flags);
				return -ENOMEM;
			}
			vc->scq = scq;
			u32d[0] = scq_virt_to_bus(scq, scq->base);
			u32d[1] = (u32) 0x00000000;
			u32d[2] = (u32) 0xffffffff;
			u32d[3] = (u32) 0x00000000;
			ns_write_sram(card, vc->cbr_scd, u32d, 4);

			fill_tst(card, n, vc);
		} else if (vcc->qos.txtp.traffic_class == ATM_UBR) {
			vc->cbr_scd = 0x00000000;
			vc->scq = card->scq0;
		}

		if (vcc->qos.txtp.traffic_class != ATM_NONE) {
			vc->tx = 1;
			vc->tx_vcc = vcc;
			vc->tbd_count = 0;
		}
		if (vcc->qos.rxtp.traffic_class != ATM_NONE) {
			u32 status;

			vc->rx = 1;
			vc->rx_vcc = vcc;
			vc->rx_iov = NULL;

			/* Open the connection in hardware */
			if (vcc->qos.aal == ATM_AAL5)
				status = NS_RCTE_AAL5 | NS_RCTE_CONNECTOPEN;
			else	/* vcc->qos.aal == ATM_AAL0 */
				status = NS_RCTE_AAL0 | NS_RCTE_CONNECTOPEN;
#ifdef RCQ_SUPPORT
			status |= NS_RCTE_RAWCELLINTEN;
#endif /* RCQ_SUPPORT */
			ns_write_sram(card,
				      NS_RCT +
				      (vpi << card->vcibits | vci) *
				      NS_RCT_ENTRY_SIZE, &status, 1);
		}

	}

	set_bit(ATM_VF_READY, &vcc->flags);
	return 0;
}