ambassador.c
来自「linux 内核源代码」· C语言 代码 · 共 2,334 行 · 第 1/5 页
C
2,334 行
if (r == round_up) { return -EINVAL; } else { exp = 31; man = 511; } } else if (rate) { // representable rate exp = 31; man = rate; // invariant: rate = man*2^(exp-31) while (!(man & (1<<31))) { exp = exp - 1; man = man<<1; } // man has top bit set // rate = (2^31+(man-2^31))*2^(exp-31) // rate = (1+(man-2^31)/2^31)*2^exp man = man<<1; man &= 0xffffffffU; // a nop on 32-bit systems // rate = (1+man/2^32)*2^exp // exp is in the range 0 to 31, man is in the range 0 to 2^32-1 // time to lose significance... we want m in the range 0 to 2^9-1 // rounding presents a minor problem... we first decide which way // we are rounding (based on given rounding direction and possibly // the bits of the mantissa that are to be discarded). switch (r) { case round_down: { // just truncate man = man>>(32-9); break; } case round_up: { // check all bits that we are discarding if (man & (~0U>>9)) { man = (man>>(32-9)) + 1; if (man == (1<<9)) { // no need to check for round up outside of range man = 0; exp += 1; } } else { man = (man>>(32-9)); } break; } case round_nearest: { // check msb that we are discarding if (man & (1<<(32-9-1))) { man = (man>>(32-9)) + 1; if (man == (1<<9)) { // no need to check for round up outside of range man = 0; exp += 1; } } else { man = (man>>(32-9)); } break; } } } else { // zero rate - not representable if (r == round_down) { return -EINVAL; } else { exp = 0; man = 0; } } PRINTD (DBG_QOS, "rate: man=%u, exp=%hu", man, exp); if (bits) *bits = /* (1<<14) | */ (exp<<9) | man; if (actual) *actual = (exp >= 9) ? (1 << exp) + (man << (exp-9)) : (1 << exp) + ((man + (1<<(9-exp-1))) >> (9-exp)); return 0;}/********** Linux ATM Operations **********/// some are not yet implemented while others do not make sense for// this device/********** Open a VC **********/static int amb_open (struct atm_vcc * atm_vcc){ int error; struct atm_qos * qos; struct atm_trafprm * txtp; struct atm_trafprm * rxtp; u16 tx_rate_bits = -1; // hush gcc u16 tx_vc_bits = -1; // hush gcc u16 tx_frame_bits = -1; // hush gcc amb_dev * dev = AMB_DEV(atm_vcc->dev); amb_vcc * vcc; unsigned char pool = -1; // hush gcc short vpi = atm_vcc->vpi; int vci = atm_vcc->vci; PRINTD (DBG_FLOW|DBG_VCC, "amb_open %x %x", vpi, vci); #ifdef ATM_VPI_UNSPEC // UNSPEC is deprecated, remove this code eventually if (vpi == ATM_VPI_UNSPEC || vci == ATM_VCI_UNSPEC) { PRINTK (KERN_WARNING, "rejecting open with unspecified VPI/VCI (deprecated)"); return -EINVAL; }#endif if (!(0 <= vpi && vpi < (1<<NUM_VPI_BITS) && 0 <= vci && vci < (1<<NUM_VCI_BITS))) { PRINTD (DBG_WARN|DBG_VCC, "VPI/VCI out of range: %hd/%d", vpi, vci); return -EINVAL; } qos = &atm_vcc->qos; if (qos->aal != ATM_AAL5) { PRINTD (DBG_QOS, "AAL not supported"); return -EINVAL; } // traffic parameters PRINTD (DBG_QOS, "TX:"); txtp = &qos->txtp; if (txtp->traffic_class != ATM_NONE) { switch (txtp->traffic_class) { case ATM_UBR: { // we take "the PCR" as a rate-cap int pcr = atm_pcr_goal (txtp); if (!pcr) { // no rate cap tx_rate_bits = 0; tx_vc_bits = TX_UBR; tx_frame_bits = TX_FRAME_NOTCAP; } else { rounding r; if (pcr < 0) { r = round_down; pcr = -pcr; } else { r = round_up; } error = make_rate (pcr, r, &tx_rate_bits, NULL); if (error) return error; tx_vc_bits = TX_UBR_CAPPED; tx_frame_bits = TX_FRAME_CAPPED; } break; }#if 0 case ATM_ABR: { pcr = atm_pcr_goal (txtp); PRINTD (DBG_QOS, "pcr goal = %d", pcr); break; }#endif default: { // PRINTD (DBG_QOS, "request for non-UBR/ABR denied"); PRINTD (DBG_QOS, "request for non-UBR denied"); return -EINVAL; } } PRINTD (DBG_QOS, "tx_rate_bits=%hx, tx_vc_bits=%hx", tx_rate_bits, tx_vc_bits); } PRINTD (DBG_QOS, "RX:"); rxtp = &qos->rxtp; if (rxtp->traffic_class == ATM_NONE) { // do nothing } else { // choose an RX pool (arranged in increasing size) for (pool = 0; pool < NUM_RX_POOLS; ++pool) if ((unsigned int) rxtp->max_sdu <= dev->rxq[pool].buffer_size) { PRINTD (DBG_VCC|DBG_QOS|DBG_POOL, "chose pool %hu (max_sdu %u <= %u)", pool, rxtp->max_sdu, dev->rxq[pool].buffer_size); break; } if (pool == NUM_RX_POOLS) { PRINTD (DBG_WARN|DBG_VCC|DBG_QOS|DBG_POOL, "no pool suitable for VC (RX max_sdu %d is too large)", rxtp->max_sdu); return -EINVAL; } switch (rxtp->traffic_class) { case ATM_UBR: { break; }#if 0 case ATM_ABR: { pcr = atm_pcr_goal (rxtp); PRINTD (DBG_QOS, "pcr goal = %d", pcr); break; }#endif default: { // PRINTD (DBG_QOS, "request for non-UBR/ABR denied"); PRINTD (DBG_QOS, "request for non-UBR denied"); return -EINVAL; } } } // get space for our vcc stuff vcc = kmalloc (sizeof(amb_vcc), GFP_KERNEL); if (!vcc) { PRINTK (KERN_ERR, "out of memory!"); return -ENOMEM; } atm_vcc->dev_data = (void *) vcc; // no failures beyond this point // we are not really "immediately before allocating the connection // identifier in hardware", but it will just have to do! set_bit(ATM_VF_ADDR,&atm_vcc->flags); if (txtp->traffic_class != ATM_NONE) { command cmd; vcc->tx_frame_bits = tx_frame_bits; down (&dev->vcc_sf); if (dev->rxer[vci]) { // RXer on the channel already, just modify rate... cmd.request = cpu_to_be32 (SRB_MODIFY_VC_RATE); cmd.args.modify_rate.vc = cpu_to_be32 (vci); // vpi 0 cmd.args.modify_rate.rate = cpu_to_be32 (tx_rate_bits << SRB_RATE_SHIFT); while (command_do (dev, &cmd)) schedule(); // ... and TX flags, preserving the RX pool cmd.request = cpu_to_be32 (SRB_MODIFY_VC_FLAGS); cmd.args.modify_flags.vc = cpu_to_be32 (vci); // vpi 0 cmd.args.modify_flags.flags = cpu_to_be32 ( (AMB_VCC(dev->rxer[vci])->rx_info.pool << SRB_POOL_SHIFT) | (tx_vc_bits << SRB_FLAGS_SHIFT) ); while (command_do (dev, &cmd)) schedule(); } else { // no RXer on the channel, just open (with pool zero) cmd.request = cpu_to_be32 (SRB_OPEN_VC); cmd.args.open.vc = cpu_to_be32 (vci); // vpi 0 cmd.args.open.flags = cpu_to_be32 (tx_vc_bits << SRB_FLAGS_SHIFT); cmd.args.open.rate = cpu_to_be32 (tx_rate_bits << SRB_RATE_SHIFT); while (command_do (dev, &cmd)) schedule(); } dev->txer[vci].tx_present = 1; up (&dev->vcc_sf); } if (rxtp->traffic_class != ATM_NONE) { command cmd; vcc->rx_info.pool = pool; down (&dev->vcc_sf); /* grow RX buffer pool */ if (!dev->rxq[pool].buffers_wanted) dev->rxq[pool].buffers_wanted = rx_lats; dev->rxq[pool].buffers_wanted += 1; fill_rx_pool (dev, pool, GFP_KERNEL); if (dev->txer[vci].tx_present) { // TXer on the channel already // switch (from pool zero) to this pool, preserving the TX bits cmd.request = cpu_to_be32 (SRB_MODIFY_VC_FLAGS); cmd.args.modify_flags.vc = cpu_to_be32 (vci); // vpi 0 cmd.args.modify_flags.flags = cpu_to_be32 ( (pool << SRB_POOL_SHIFT) | (dev->txer[vci].tx_vc_bits << SRB_FLAGS_SHIFT) ); } else { // no TXer on the channel, open the VC (with no rate info) cmd.request = cpu_to_be32 (SRB_OPEN_VC); cmd.args.open.vc = cpu_to_be32 (vci); // vpi 0 cmd.args.open.flags = cpu_to_be32 (pool << SRB_POOL_SHIFT); cmd.args.open.rate = cpu_to_be32 (0); } while (command_do (dev, &cmd)) schedule(); // this link allows RX frames through dev->rxer[vci] = atm_vcc; up (&dev->vcc_sf); } // indicate readiness set_bit(ATM_VF_READY,&atm_vcc->flags); return 0;}/********** Close a VC **********/static void amb_close (struct atm_vcc * atm_vcc) { amb_dev * dev = AMB_DEV (atm_vcc->dev); amb_vcc * vcc = AMB_VCC (atm_vcc); u16 vci = atm_vcc->vci; PRINTD (DBG_VCC|DBG_FLOW, "amb_close"); // indicate unreadiness clear_bit(ATM_VF_READY,&atm_vcc->flags); // disable TXing if (atm_vcc->qos.txtp.traffic_class != ATM_NONE) { command cmd; down (&dev->vcc_sf); if (dev->rxer[vci]) { // RXer still on the channel, just modify rate... XXX not really needed cmd.request = cpu_to_be32 (SRB_MODIFY_VC_RATE); cmd.args.modify_rate.vc = cpu_to_be32 (vci); // vpi 0 cmd.args.modify_rate.rate = cpu_to_be32 (0); // ... and clear TX rate flags (XXX to stop RM cell output?), preserving RX pool } else { // no RXer on the channel, close channel cmd.request = cpu_to_be32 (SRB_CLOSE_VC); cmd.args.close.vc = cpu_to_be32 (vci); // vpi 0 } dev->txer[vci].tx_present = 0; while (command_do (dev, &cmd)) schedule(); up (&dev->vcc_sf); } // disable RXing if (atm_vcc->qos.rxtp.traffic_class != ATM_NONE) { command cmd; // this is (the?) one reason why we need the amb_vcc struct unsigned char pool = vcc->rx_info.pool; down (&dev->vcc_sf); if (dev->txer[vci].tx_present) { // TXer still on the channel, just go to pool zero XXX not really needed cmd.request = cpu_to_be32 (SRB_MODIFY_VC_FLAGS); cmd.args.modify_flags.vc = cpu_to_be32 (vci); // vpi 0 cmd.args.modify_flags.flags = cpu_to_be32 (dev->txer[vci].tx_vc_bits << SRB_FLAGS_SHIFT); } else { // no TXer on the channel, close the VC cmd.request = cpu_to_be32 (SRB_CLOSE_VC); cmd.args.close.vc = cpu_to_be32 (vci); // vpi 0 } // forget the rxer - no more skbs will be pushed if (atm_vcc != dev->rxer[vci]) PRINTK (KERN_ERR, "%s vcc=%p rxer[vci]=%p", "arghhh! we're going to die!", vcc, dev->rxer[vci]); dev->rxer[vci] = NULL; while (command_do (dev, &cmd)) schedule(); /* shrink RX buffer pool */ dev->rxq[pool].buffers_wanted -= 1; if (dev->rxq[pool].buffers_wanted == rx_lats) { dev->rxq[pool].buffers_wanted = 0; drain_rx_pool (dev, pool); } up (&dev->vcc_sf); } // free our structure kfree (vcc); // say the VPI/VCI is free again clear_bit(ATM_VF_ADDR,&atm_vcc->flags); return;}/********** Set socket options for a VC **********/// int amb_getsockopt (struct atm_vcc * atm_vcc, int level, int optname, void * optval, int optlen);/********** Set socket options for a VC **********/// int amb_setsockopt (struct atm_vcc * atm_vcc, int level, int optname, void * optval, int optlen);/********** Send **********/static int amb_send (struct atm_vcc * atm_vcc, struct sk_buff * skb) { amb_dev * dev = AMB_DEV(atm_vcc->dev); amb_vcc * vcc = AMB_VCC(atm_vcc); u16 vc = atm_vcc->vci; unsigned int tx_len = skb->len; unsigned char * tx_data = skb->data; tx_simple * tx_descr; tx_in tx; if (test_bit (dead, &dev->flags)) return -EIO; PRINTD (DBG_FLOW|DBG_TX, "amb_send vc %x data %p len %u", vc, tx_data, tx_len); dump_skb (">>>", vc, skb); if (!dev->txer[vc].tx_present) { PRINTK (KERN_ERR, "attempt to send on RX-only VC %x", vc); return -EBADFD; } // this is a driver private field so we have to set it ourselves, // despite the fact that we are _required_ to use it to check for a // pop function ATM_SKB(skb)->vcc = atm_vcc; if (skb->len > (size_t) atm_vcc->qos.txtp.max_sdu) { PRINTK (KERN_ERR, "sk_buff length greater than agreed max_sdu, dropping..."); return -EIO; } if (check_area (skb->data, skb->len)) { atomic_inc(&atm_vcc->stats->tx_err); return -ENOMEM; // ? } // allocate memory for fragments tx_descr = kmalloc (sizeof(tx_simple), GFP_KERNEL); if (!tx_descr) { PRINTK (KERN_ERR, "could not allocate TX descriptor"); return -ENOMEM; } if (check_area (tx_descr, sizeof(tx_simple))) { kfree (tx_descr); return -ENOMEM; } PRINTD (DBG_TX, "fragment list allocated at %p", tx_descr); tx_descr->skb = skb; tx_descr->tx_frag.bytes = cpu_to_be32 (tx_len); tx_descr->tx_frag.address = cpu_to_be32 (virt_to_bus (tx_data)); tx_descr->tx_frag_end.handle = virt_to_bus (tx_descr); tx_descr->tx_frag_end.vc = 0; tx_descr->tx_frag_end.next_descriptor_length = 0; tx_descr->tx_frag_end.next_descriptor = 0;#ifdef AMB_NEW_MICROCODE tx_descr->tx_frag_end.cpcs_uu = 0; tx_descr->tx_frag_end.cpi = 0; tx_descr->tx_frag_end.pad = 0;#endif
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?