in firestream.c [849:1095]
static int fs_open(struct atm_vcc *atm_vcc)
{
struct fs_dev *dev;
struct fs_vcc *vcc;
struct fs_transmit_config *tc;
struct atm_trafprm * txtp;
struct atm_trafprm * rxtp;
/* struct fs_receive_config *rc;*/
/* struct FS_QENTRY *qe; */
int error;
int bfp;
int to;
unsigned short tmc0;
short vpi = atm_vcc->vpi;
int vci = atm_vcc->vci;
func_enter ();
dev = FS_DEV(atm_vcc->dev);
fs_dprintk (FS_DEBUG_OPEN, "fs: open on dev: %p, vcc at %p\n",
dev, atm_vcc);
if (vci != ATM_VPI_UNSPEC && vpi != ATM_VCI_UNSPEC)
set_bit(ATM_VF_ADDR, &atm_vcc->flags);
if ((atm_vcc->qos.aal != ATM_AAL5) &&
(atm_vcc->qos.aal != ATM_AAL2))
return -EINVAL; /* XXX AAL0 */
fs_dprintk (FS_DEBUG_OPEN, "fs: (itf %d): open %d.%d\n",
atm_vcc->dev->number, atm_vcc->vpi, atm_vcc->vci);
/* XXX handle qos parameters (rate limiting) ? */
vcc = kmalloc(sizeof(struct fs_vcc), GFP_KERNEL);
fs_dprintk (FS_DEBUG_ALLOC, "Alloc VCC: %p(%zd)\n", vcc, sizeof(struct fs_vcc));
if (!vcc) {
clear_bit(ATM_VF_ADDR, &atm_vcc->flags);
return -ENOMEM;
}
atm_vcc->dev_data = vcc;
vcc->last_skb = NULL;
init_waitqueue_head (&vcc->close_wait);
txtp = &atm_vcc->qos.txtp;
rxtp = &atm_vcc->qos.rxtp;
if (!test_bit(ATM_VF_PARTIAL, &atm_vcc->flags)) {
if (IS_FS50(dev)) {
/* Increment the channel numer: take a free one next time. */
for (to=33;to;to--, dev->channo++) {
/* We only have 32 channels */
if (dev->channo >= 32)
dev->channo = 0;
/* If we need to do RX, AND the RX is inuse, try the next */
if (DO_DIRECTION(rxtp) && dev->atm_vccs[dev->channo])
continue;
/* If we need to do TX, AND the TX is inuse, try the next */
if (DO_DIRECTION(txtp) && test_bit (dev->channo, dev->tx_inuse))
continue;
/* Ok, both are free! (or not needed) */
break;
}
if (!to) {
printk ("No more free channels for FS50..\n");
kfree(vcc);
return -EBUSY;
}
vcc->channo = dev->channo;
dev->channo &= dev->channel_mask;
} else {
vcc->channo = (vpi << FS155_VCI_BITS) | (vci);
if (((DO_DIRECTION(rxtp) && dev->atm_vccs[vcc->channo])) ||
( DO_DIRECTION(txtp) && test_bit (vcc->channo, dev->tx_inuse))) {
printk ("Channel is in use for FS155.\n");
kfree(vcc);
return -EBUSY;
}
}
fs_dprintk (FS_DEBUG_OPEN, "OK. Allocated channel %x(%d).\n",
vcc->channo, vcc->channo);
}
if (DO_DIRECTION (txtp)) {
tc = kmalloc (sizeof (struct fs_transmit_config), GFP_KERNEL);
fs_dprintk (FS_DEBUG_ALLOC, "Alloc tc: %p(%zd)\n",
tc, sizeof (struct fs_transmit_config));
if (!tc) {
fs_dprintk (FS_DEBUG_OPEN, "fs: can't alloc transmit_config.\n");
kfree(vcc);
return -ENOMEM;
}
/* Allocate the "open" entry from the high priority txq. This makes
it most likely that the chip will notice it. It also prevents us
from having to wait for completion. On the other hand, we may
need to wait for completion anyway, to see if it completed
successfully. */
switch (atm_vcc->qos.aal) {
case ATM_AAL2:
case ATM_AAL0:
tc->flags = 0
| TC_FLAGS_TRANSPARENT_PAYLOAD
| TC_FLAGS_PACKET
| (1 << 28)
| TC_FLAGS_TYPE_UBR /* XXX Change to VBR -- PVDL */
| TC_FLAGS_CAL0;
break;
case ATM_AAL5:
tc->flags = 0
| TC_FLAGS_AAL5
| TC_FLAGS_PACKET /* ??? */
| TC_FLAGS_TYPE_CBR
| TC_FLAGS_CAL0;
break;
default:
printk ("Unknown aal: %d\n", atm_vcc->qos.aal);
tc->flags = 0;
}
/* Docs are vague about this atm_hdr field. By the way, the FS
* chip makes odd errors if lower bits are set.... -- REW */
tc->atm_hdr = (vpi << 20) | (vci << 4);
tmc0 = 0;
{
int pcr = atm_pcr_goal (txtp);
fs_dprintk (FS_DEBUG_OPEN, "pcr = %d.\n", pcr);
/* XXX Hmm. officially we're only allowed to do this if rounding
is round_down -- REW */
if (IS_FS50(dev)) {
if (pcr > 51840000/53/8) pcr = 51840000/53/8;
} else {
if (pcr > 155520000/53/8) pcr = 155520000/53/8;
}
if (!pcr) {
/* no rate cap */
tmc0 = IS_FS50(dev)?0x61BE:0x64c9; /* Just copied over the bits from Fujitsu -- REW */
} else {
int r;
if (pcr < 0) {
r = ROUND_DOWN;
pcr = -pcr;
} else {
r = ROUND_UP;
}
error = make_rate (pcr, r, &tmc0, NULL);
if (error) {
kfree(tc);
kfree(vcc);
return error;
}
}
fs_dprintk (FS_DEBUG_OPEN, "pcr = %d.\n", pcr);
}
tc->TMC[0] = tmc0 | 0x4000;
tc->TMC[1] = 0; /* Unused */
tc->TMC[2] = 0; /* Unused */
tc->TMC[3] = 0; /* Unused */
tc->spec = 0; /* UTOPIA address, UDF, HEC: Unused -> 0 */
tc->rtag[0] = 0; /* What should I do with routing tags???
-- Not used -- AS -- Thanks -- REW*/
tc->rtag[1] = 0;
tc->rtag[2] = 0;
if (fs_debug & FS_DEBUG_OPEN) {
fs_dprintk (FS_DEBUG_OPEN, "TX config record:\n");
my_hd (tc, sizeof (*tc));
}
/* We now use the "submit_command" function to submit commands to
the firestream. There is a define up near the definition of
that routine that switches this routine between immediate write
to the immediate command registers and queuing the commands in
the HPTXQ for execution. This last technique might be more
efficient if we know we're going to submit a whole lot of
commands in one go, but this driver is not setup to be able to
use such a construct. So it probably doen't matter much right
now. -- REW */
/* The command is IMMediate and INQueue. The parameters are out-of-line.. */
submit_command (dev, &dev->hp_txq,
QE_CMD_CONFIG_TX | QE_CMD_IMM_INQ | vcc->channo,
virt_to_bus (tc), 0, 0);
submit_command (dev, &dev->hp_txq,
QE_CMD_TX_EN | QE_CMD_IMM_INQ | vcc->channo,
0, 0, 0);
set_bit (vcc->channo, dev->tx_inuse);
}
if (DO_DIRECTION (rxtp)) {
dev->atm_vccs[vcc->channo] = atm_vcc;
for (bfp = 0;bfp < FS_NR_FREE_POOLS; bfp++)
if (atm_vcc->qos.rxtp.max_sdu <= dev->rx_fp[bfp].bufsize) break;
if (bfp >= FS_NR_FREE_POOLS) {
fs_dprintk (FS_DEBUG_OPEN, "No free pool fits sdu: %d.\n",
atm_vcc->qos.rxtp.max_sdu);
/* XXX Cleanup? -- Would just calling fs_close work??? -- REW */
/* XXX clear tx inuse. Close TX part? */
dev->atm_vccs[vcc->channo] = NULL;
kfree (vcc);
return -EINVAL;
}
switch (atm_vcc->qos.aal) {
case ATM_AAL0:
case ATM_AAL2:
submit_command (dev, &dev->hp_txq,
QE_CMD_CONFIG_RX | QE_CMD_IMM_INQ | vcc->channo,
RC_FLAGS_TRANSP |
RC_FLAGS_BFPS_BFP * bfp |
RC_FLAGS_RXBM_PSB, 0, 0);
break;
case ATM_AAL5:
submit_command (dev, &dev->hp_txq,
QE_CMD_CONFIG_RX | QE_CMD_IMM_INQ | vcc->channo,
RC_FLAGS_AAL5 |
RC_FLAGS_BFPS_BFP * bfp |
RC_FLAGS_RXBM_PSB, 0, 0);
break;
}
if (IS_FS50 (dev)) {
submit_command (dev, &dev->hp_txq,
QE_CMD_REG_WR | QE_CMD_IMM_INQ,
0x80 + vcc->channo,
(vpi << 16) | vci, 0 ); /* XXX -- Use defines. */
}
submit_command (dev, &dev->hp_txq,
QE_CMD_RX_EN | QE_CMD_IMM_INQ | vcc->channo,
0, 0, 0);
}
/* Indicate we're done! */
set_bit(ATM_VF_READY, &atm_vcc->flags);
func_exit ();
return 0;
}