📄 horizon.c
字号:
; return;}/* TX */static inline void SELECT_TX_CHANNEL (hrz_dev * dev, u16 tx_channel) { wr_regl (dev, TX_CHANNEL_PORT_OFF, tx_channel); return;}/* Update or query one configuration parameter of a particular channel. */static inline void update_tx_channel_config (hrz_dev * dev, short chan, u8 mode, u16 value) { wr_regw (dev, TX_CHANNEL_CONFIG_COMMAND_OFF, chan * TX_CHANNEL_CONFIG_MULT | mode); wr_regw (dev, TX_CHANNEL_CONFIG_DATA_OFF, value); return;}static inline u16 query_tx_channel_config (hrz_dev * dev, short chan, u8 mode) { wr_regw (dev, TX_CHANNEL_CONFIG_COMMAND_OFF, chan * TX_CHANNEL_CONFIG_MULT | mode); return rd_regw (dev, TX_CHANNEL_CONFIG_DATA_OFF);}/********** dump functions **********/static inline void dump_skb (char * prefix, unsigned int vc, struct sk_buff * skb) {#ifdef DEBUG_HORIZON unsigned int i; unsigned char * data = skb->data; PRINTDB (DBG_DATA, "%s(%u) ", prefix, vc); for (i=0; i<skb->len && i < 256;i++) PRINTDM (DBG_DATA, "%02x ", data[i]); PRINTDE (DBG_DATA,"");#else (void) prefix; (void) vc; (void) skb;#endif return;}static inline void dump_regs (hrz_dev * dev) {#ifdef DEBUG_HORIZON PRINTD (DBG_REGS, "CONTROL 0: %#x", rd_regl (dev, CONTROL_0_REG)); PRINTD (DBG_REGS, "RX CONFIG: %#x", rd_regw (dev, RX_CONFIG_OFF)); PRINTD (DBG_REGS, "TX CONFIG: %#x", rd_regw (dev, TX_CONFIG_OFF)); PRINTD (DBG_REGS, "TX STATUS: %#x", rd_regw (dev, TX_STATUS_OFF)); PRINTD (DBG_REGS, "IRQ ENBLE: %#x", rd_regl (dev, INT_ENABLE_REG_OFF)); PRINTD (DBG_REGS, "IRQ SORCE: %#x", rd_regl (dev, INT_SOURCE_REG_OFF));#else (void) dev;#endif return;}static inline void dump_framer (hrz_dev * dev) {#ifdef DEBUG_HORIZON unsigned int i; PRINTDB (DBG_REGS, "framer registers:"); for (i = 0; i < 0x10; ++i) PRINTDM (DBG_REGS, " %02x", rd_framer (dev, i)); PRINTDE (DBG_REGS,"");#else (void) dev;#endif return;}/********** VPI/VCI <-> (RX) channel conversions **********//* RX channels are 10 bit integers, these fns are quite paranoid */static inline int channel_to_vpivci (const u16 channel, short * vpi, int * vci) { unsigned short vci_bits = 10 - vpi_bits; if ((channel & RX_CHANNEL_MASK) == channel) { *vci = channel & ((~0)<<vci_bits); *vpi = channel >> vci_bits; return channel ? 0 : -EINVAL; } return -EINVAL;}static inline int vpivci_to_channel (u16 * channel, const short vpi, const int vci) { unsigned short vci_bits = 10 - vpi_bits; if (0 <= vpi && vpi < 1<<vpi_bits && 0 <= vci && vci < 1<<vci_bits) { *channel = vpi<<vci_bits | vci; return *channel ? 0 : -EINVAL; } return -EINVAL;}/********** decode RX queue entries **********/static inline u16 rx_q_entry_to_length (u32 x) { return x & RX_Q_ENTRY_LENGTH_MASK;}static inline u16 rx_q_entry_to_rx_channel (u32 x) { return (x>>RX_Q_ENTRY_CHANNEL_SHIFT) & RX_CHANNEL_MASK;}/* Cell Transmit Rate Values * * the cell transmit rate (cells per sec) can be set to a variety of * different values by specifying two parameters: a timer preload from * 1 to 16 (stored as 0 to 15) and a clock divider (2 to the power of * an exponent from 0 to 14; the special value 15 disables the timer). * * cellrate = baserate / (preload * 2^divider) * * The maximum cell rate that can be specified is therefore just the * base rate. Halving the preload is equivalent to adding 1 to the * divider and so values 1 to 8 of the preload are redundant except * in the case of a maximal divider (14). * * Given a desired cell rate, an algorithm to determine the preload * and divider is: * * a) x = baserate / cellrate, want p * 2^d = x (as far as possible) * b) if x > 16 * 2^14 then set p = 16, d = 14 (min rate), done * if x <= 16 then set p = x, d = 0 (high rates), done * c) now have 16 < x <= 2^18, or 1 < x/16 <= 2^14 and we want to * know n such that 2^(n-1) < x/16 <= 2^n, so slide a bit until * we find the range (n will be between 1 and 14), set d = n * d) Also have 8 < x/2^n <= 16, so set p nearest x/2^n * * The algorithm used below is a minor variant of the above. * * The base rate is derived from the oscillator frequency (Hz) using a * fixed divider: * * baserate = freq / 32 in the case of some Unknown Card * baserate = freq / 8 in the case of the Horizon 25 * baserate = freq / 8 in the case of the Horizon Ultra 155 * * The Horizon cards have oscillators and base rates as follows: * * Card Oscillator Base Rate * Unknown Card 33 MHz 1.03125 MHz (33 MHz = PCI freq) * Horizon 25 32 MHz 4 MHz * Horizon Ultra 155 40 MHz 5 MHz * * The following defines give the base rates in Hz. These were * previously a factor of 100 larger, no doubt someone was using * cps*100. */#define BR_UKN 1031250l#define BR_HRZ 4000000l#define BR_ULT 5000000l// d is an exponent#define CR_MIND 0#define CR_MAXD 14// p ranges from 1 to a power of 2#define CR_MAXPEXP 4static int make_rate (const hrz_dev * dev, u32 c, rounding r, u16 * bits, unsigned int * actual) { // note: rounding the rate down means rounding 'p' up const unsigned long br = test_bit (ultra, (hrz_flags *) &dev->flags) ? BR_ULT : BR_HRZ; u32 div = CR_MIND; u32 pre; // local fn to build the timer bits int set_cr (void) { // paranoia if (div > CR_MAXD || (!pre) || pre > 1<<CR_MAXPEXP) { PRINTD (DBG_QOS, "set_cr internal failure: d=%u p=%u", div, pre); return -EINVAL; } else { if (bits) *bits = (div<<CLOCK_SELECT_SHIFT) | (pre-1); if (actual) { *actual = (br + (pre<<div) - 1) / (pre<<div); PRINTD (DBG_QOS, "actual rate: %u", *actual); } return 0; } } // br_exp and br_man are used to avoid overflowing (c*maxp*2^d) in // the tests below. We could think harder about exact possibilities // of failure... unsigned long br_man = br; unsigned int br_exp = 0; PRINTD (DBG_QOS|DBG_FLOW, "make_rate b=%lu, c=%u, %s", br, c, (r == round_up) ? "up" : (r == round_down) ? "down" : "nearest"); // avoid div by zero if (!c) { PRINTD (DBG_QOS|DBG_ERR, "zero rate is not allowed!"); return -EINVAL; } while (br_exp < CR_MAXPEXP + CR_MIND && (br_man % 2 == 0)) { br_man = br_man >> 1; ++br_exp; } // (br >>br_exp) <<br_exp == br and // br_exp <= CR_MAXPEXP+CR_MIND if (br_man <= (c << (CR_MAXPEXP+CR_MIND-br_exp))) { // Equivalent to: B <= (c << (MAXPEXP+MIND)) // take care of rounding switch (r) { case round_down: pre = (br+(c<<div)-1)/(c<<div); // but p must be non-zero if (!pre) pre = 1; break; case round_nearest: pre = (br+(c<<div)/2)/(c<<div); // but p must be non-zero if (!pre) pre = 1; break; case round_up: pre = br/(c<<div); // but p must be non-zero if (!pre) return -EINVAL; break; } PRINTD (DBG_QOS, "A: p=%u, d=%u", pre, div); return set_cr (); } // at this point we have // d == MIND and (c << (MAXPEXP+MIND)) < B while (div < CR_MAXD) { div++; if (br_man <= (c << (CR_MAXPEXP+div-br_exp))) { // Equivalent to: B <= (c << (MAXPEXP+d)) // c << (MAXPEXP+d-1) < B <= c << (MAXPEXP+d) // 1 << (MAXPEXP-1) < B/2^d/c <= 1 << MAXPEXP // MAXP/2 < B/c2^d <= MAXP // take care of rounding switch (r) { case round_down: pre = (br+(c<<div)-1)/(c<<div); break; case round_nearest: pre = (br+(c<<div)/2)/(c<<div); break; case round_up: pre = br/(c<<div); break; } PRINTD (DBG_QOS, "B: p=%u, d=%u", pre, div); return set_cr (); } } // at this point we have // d == MAXD and (c << (MAXPEXP+MAXD)) < B // but we cannot go any higher // take care of rounding switch (r) { case round_down: return -EINVAL; break; case round_nearest: break; case round_up: break; } pre = 1 << CR_MAXPEXP; PRINTD (DBG_QOS, "C: p=%u, d=%u", pre, div); return set_cr ();}static int make_rate_with_tolerance (const hrz_dev * dev, u32 c, rounding r, unsigned int tol, u16 * bit_pattern, unsigned int * actual) { unsigned int my_actual; PRINTD (DBG_QOS|DBG_FLOW, "make_rate_with_tolerance c=%u, %s, tol=%u", c, (r == round_up) ? "up" : (r == round_down) ? "down" : "nearest", tol); if (!actual) // actual rate is not returned actual = &my_actual; if (make_rate (dev, c, round_nearest, bit_pattern, actual)) // should never happen as round_nearest always succeeds return -1; if (c - tol <= *actual && *actual <= c + tol) // within tolerance return 0; else // intolerant, try rounding instead return make_rate (dev, c, r, bit_pattern, actual);}/********** Listen on a VC **********/static int hrz_open_rx (hrz_dev * dev, u16 channel) { // is there any guarantee that we don't get two simulataneous // identical calls of this function from different processes? yes // rate_lock unsigned long flags; u32 channel_type; // u16? u16 buf_ptr = RX_CHANNEL_IDLE; rx_ch_desc * rx_desc = &memmap->rx_descs[channel]; PRINTD (DBG_FLOW, "hrz_open_rx %x", channel); spin_lock_irqsave (&dev->mem_lock, flags); channel_type = rd_mem (dev, &rx_desc->wr_buf_type) & BUFFER_PTR_MASK; spin_unlock_irqrestore (&dev->mem_lock, flags); // very serious error, should never occur if (channel_type != RX_CHANNEL_DISABLED) { PRINTD (DBG_ERR|DBG_VCC, "RX channel for VC already open"); return -EBUSY; // clean up? } // Give back spare buffer if (dev->noof_spare_buffers) { buf_ptr = dev->spare_buffers[--dev->noof_spare_buffers]; PRINTD (DBG_VCC, "using a spare buffer: %u", buf_ptr); // should never occur if (buf_ptr == RX_CHANNEL_DISABLED || buf_ptr == RX_CHANNEL_IDLE) { // but easy to recover from PRINTD (DBG_ERR|DBG_VCC, "bad spare buffer pointer, using IDLE"); buf_ptr = RX_CHANNEL_IDLE; } } else { PRINTD (DBG_VCC, "using IDLE buffer pointer"); } // Channel is currently disabled so change its status to idle // do we really need to save the flags again? spin_lock_irqsave (&dev->mem_lock, flags); wr_mem (dev, &rx_desc->wr_buf_type, buf_ptr | CHANNEL_TYPE_AAL5 | FIRST_CELL_OF_AAL5_FRAME); if (buf_ptr != RX_CHANNEL_IDLE) wr_mem (dev, &rx_desc->rd_buf_type, buf_ptr); spin_unlock_irqrestore (&dev->mem_lock, flags); // rxer->rate = make_rate (qos->peak_cells); PRINTD (DBG_FLOW, "hrz_open_rx ok"); return 0;}#if 0/********** change vc rate for a given vc **********/static void hrz_change_vc_qos (ATM_RXER * rxer, MAAL_QOS * qos) { rxer->rate = make_rate (qos->peak_cells);}#endif/********** free an skb (as per ATM device driver documentation) **********/static inline void hrz_kfree_skb (struct sk_buff * skb) { if (ATM_SKB(skb)->vcc->pop) { ATM_SKB(skb)->vcc->pop (ATM_SKB(skb)->vcc, skb); } else { dev_kfree_skb_any (skb); }}/********** cancel listen on a VC **********/static void hrz_close_rx (hrz_dev * dev, u16 vc) { unsigned long flags; u32 value; u32 r1, r2; rx_ch_desc * rx_desc = &memmap->rx_descs[vc]; int was_idle = 0; spin_lock_irqsave (&dev->mem_lock, flags); value = rd_mem (dev, &rx_desc->wr_buf_type) & BUFFER_PTR_MASK; spin_unlock_irqrestore (&dev->mem_lock, flags); if (value == RX_CHANNEL_DISABLED) { // I suppose this could happen once we deal with _NONE traffic properly PRINTD (DBG_VCC, "closing VC: RX channel %u already disabled", vc); return; } if (value == RX_CHANNEL_IDLE) was_idle = 1; spin_lock_irqsave (&dev->mem_lock, flags); for (;;) { wr_mem (dev, &rx_desc->wr_buf_type, RX_CHANNEL_DISABLED); if ((rd_mem (dev, &rx_desc->wr_buf_type) & BUFFER_PTR_MASK) == RX_CHANNEL_DISABLED) break; was_idle = 0; } if (was_idle) { spin_unlock_irqrestore (&dev->mem_lock, flags); return; } WAIT_FLUSH_RX_COMPLETE(dev); // XXX Is this all really necessary? We can rely on the rx_data_av // handler to discard frames that remain queued for delivery. If the // worry is that immediately reopening the channel (perhaps by a // different process) may cause some data to be mis-delivered then // there may still be a simpler solution (such as busy-waiting on // rx_busy once the channel is disabled or before a new one is // opened - does this leave any holes?). Arguably setting up and // tearing down the TX and RX halves of each virtual circuit could // most safely be done within ?x_busy protected regions. // OK, current changes are that Simon's marker is disabled and we DO // look for NULL rxer elsewhere. The code here seems flush frames // and then remember the last dead cell belonging to the channel // just disabled - the cell gets relinked at the next vc_open.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -