⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 horizon.c

📁 讲述linux的初始化过程
💻 C
📖 第 1 页 / 共 5 页
字号:
    ;  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 + -