📄 horizon.c
字号:
dev->tx_last = tx_channel; } PRINTD (DBG_TX, "using channel %u", tx_channel); YELLOW_LED_OFF(dev); // TX start transfer { unsigned int tx_len = skb->len; unsigned int tx_iovcnt = ATM_SKB(skb)->iovcnt; // remember this so we can free it later dev->tx_skb = skb; if (tx_iovcnt) { // scatter gather transfer dev->tx_regions = tx_iovcnt; dev->tx_iovec = (struct iovec *) skb->data; dev->tx_bytes = 0; PRINTD (DBG_TX|DBG_BUS, "TX start scatter-gather transfer (iovec %p, len %d)", skb->data, tx_len); } else { // simple transfer dev->tx_regions = 0; dev->tx_iovec = 0; dev->tx_bytes = tx_len; dev->tx_addr = skb->data; PRINTD (DBG_TX|DBG_BUS, "TX start simple transfer (addr %p, len %d)", skb->data, tx_len); } // and do the business tx_schedule (dev, 0); } return 0;}/********** reset a card **********/static void __init hrz_reset (const hrz_dev * dev) { u32 control_0_reg = rd_regl (dev, CONTROL_0_REG); // why not set RESET_HORIZON to one and wait for the card to // reassert that bit as zero? Like so: control_0_reg = control_0_reg & RESET_HORIZON; wr_regl (dev, CONTROL_0_REG, control_0_reg); while (control_0_reg & RESET_HORIZON) control_0_reg = rd_regl (dev, CONTROL_0_REG); // old reset code retained: wr_regl (dev, CONTROL_0_REG, control_0_reg | RESET_ATM | RESET_RX | RESET_TX | RESET_HOST); // just guessing here udelay (1000); wr_regl (dev, CONTROL_0_REG, control_0_reg);}/********** read the burnt in address **********/static u16 __init read_bia (const hrz_dev * dev, u16 addr) { u32 ctrl = rd_regl (dev, CONTROL_0_REG); void WRITE_IT_WAIT (void) { wr_regl (dev, CONTROL_0_REG, ctrl); udelay (5); } void CLOCK_IT (void) { // DI must be valid around rising SK edge ctrl &= ~SEEPROM_SK; WRITE_IT_WAIT(); ctrl |= SEEPROM_SK; WRITE_IT_WAIT(); } const unsigned int addr_bits = 6; const unsigned int data_bits = 16; unsigned int i; u16 res; ctrl &= ~(SEEPROM_CS | SEEPROM_SK | SEEPROM_DI); WRITE_IT_WAIT(); // wake Serial EEPROM and send 110 (READ) command ctrl |= (SEEPROM_CS | SEEPROM_DI); CLOCK_IT(); ctrl |= SEEPROM_DI; CLOCK_IT(); ctrl &= ~SEEPROM_DI; CLOCK_IT(); for (i=0; i<addr_bits; i++) { if (addr & (1 << (addr_bits-1))) ctrl |= SEEPROM_DI; else ctrl &= ~SEEPROM_DI; CLOCK_IT(); addr = addr << 1; } // we could check that we have DO = 0 here ctrl &= ~SEEPROM_DI; res = 0; for (i=0;i<data_bits;i++) { res = res >> 1; CLOCK_IT(); if (rd_regl (dev, CONTROL_0_REG) & SEEPROM_DO) res |= (1 << (data_bits-1)); } ctrl &= ~(SEEPROM_SK | SEEPROM_CS); WRITE_IT_WAIT(); return res;}/********** initialise a card **********/static int __init hrz_init (hrz_dev * dev) { int onefivefive; u16 chan; int buff_count; HDW * mem; cell_buf * tx_desc; cell_buf * rx_desc; u32 ctrl; ctrl = rd_regl (dev, CONTROL_0_REG); PRINTD (DBG_INFO, "ctrl0reg is %#x", ctrl); onefivefive = ctrl & ATM_LAYER_STATUS; if (onefivefive) printk (DEV_LABEL ": Horizon Ultra (at 155.52 MBps)"); else printk (DEV_LABEL ": Horizon (at 25 MBps)"); printk (":"); // Reset the card to get everything in a known state printk (" reset"); hrz_reset (dev); // Clear all the buffer memory printk (" clearing memory"); for (mem = (HDW *) memmap; mem < (HDW *) (memmap + 1); ++mem) wr_mem (dev, mem, 0); printk (" tx channels"); // All transmit eight channels are set up as AAL5 ABR channels with // a 16us cell spacing. Why? // Channel 0 gets the free buffer at 100h, channel 1 gets the free // buffer at 110h etc. for (chan = 0; chan < TX_CHANS; ++chan) { tx_ch_desc * tx_desc = &memmap->tx_descs[chan]; cell_buf * buf = &memmap->inittxbufs[chan]; // initialise the read and write buffer pointers wr_mem (dev, &tx_desc->rd_buf_type, BUF_PTR(buf)); wr_mem (dev, &tx_desc->wr_buf_type, BUF_PTR(buf)); // set the status of the initial buffers to empty wr_mem (dev, &buf->next, BUFF_STATUS_EMPTY); } // Use space bufn3 at the moment for tx buffers printk (" tx buffers"); tx_desc = memmap->bufn3; wr_mem (dev, &memmap->txfreebufstart.next, BUF_PTR(tx_desc) | BUFF_STATUS_EMPTY); for (buff_count = 0; buff_count < BUFN3_SIZE-1; buff_count++) { wr_mem (dev, &tx_desc->next, BUF_PTR(tx_desc+1) | BUFF_STATUS_EMPTY); tx_desc++; } wr_mem (dev, &tx_desc->next, BUF_PTR(&memmap->txfreebufend) | BUFF_STATUS_EMPTY); // Initialise the transmit free buffer count wr_regw (dev, TX_FREE_BUFFER_COUNT_OFF, BUFN3_SIZE); printk (" rx channels"); // Initialise all of the receive channels to be AAL5 disabled with // an interrupt threshold of 0 for (chan = 0; chan < RX_CHANS; ++chan) { rx_ch_desc * rx_desc = &memmap->rx_descs[chan]; wr_mem (dev, &rx_desc->wr_buf_type, CHANNEL_TYPE_AAL5 | RX_CHANNEL_DISABLED); } printk (" rx buffers"); // Use space bufn4 at the moment for rx buffers rx_desc = memmap->bufn4; wr_mem (dev, &memmap->rxfreebufstart.next, BUF_PTR(rx_desc) | BUFF_STATUS_EMPTY); for (buff_count = 0; buff_count < BUFN4_SIZE-1; buff_count++) { wr_mem (dev, &rx_desc->next, BUF_PTR(rx_desc+1) | BUFF_STATUS_EMPTY); rx_desc++; } wr_mem (dev, &rx_desc->next, BUF_PTR(&memmap->rxfreebufend) | BUFF_STATUS_EMPTY); // Initialise the receive free buffer count wr_regw (dev, RX_FREE_BUFFER_COUNT_OFF, BUFN4_SIZE); // Initialize Horizons registers // TX config wr_regw (dev, TX_CONFIG_OFF, ABR_ROUND_ROBIN | TX_NORMAL_OPERATION | DRVR_DRVRBAR_ENABLE); // RX config. Use 10-x VC bits, x VP bits, non user cells in channel 0. wr_regw (dev, RX_CONFIG_OFF, DISCARD_UNUSED_VPI_VCI_BITS_SET | NON_USER_CELLS_IN_ONE_CHANNEL | vpi_bits); // RX line config wr_regw (dev, RX_LINE_CONFIG_OFF, LOCK_DETECT_ENABLE | FREQUENCY_DETECT_ENABLE | GXTALOUT_SELECT_DIV4); // Set the max AAL5 cell count to be just enough to contain the // largest AAL5 frame that the user wants to receive wr_regw (dev, MAX_AAL5_CELL_COUNT_OFF, (max_rx_size + ATM_AAL5_TRAILER + ATM_CELL_PAYLOAD - 1) / ATM_CELL_PAYLOAD); // Enable receive wr_regw (dev, RX_CONFIG_OFF, rd_regw (dev, RX_CONFIG_OFF) | RX_ENABLE); printk (" control"); // Drive the OE of the LEDs then turn the green LED on ctrl |= GREEN_LED_OE | YELLOW_LED_OE | GREEN_LED | YELLOW_LED; wr_regl (dev, CONTROL_0_REG, ctrl); // Test for a 155-capable card if (onefivefive) { // Select 155 mode... make this a choice (or: how do we detect // external line speed and switch?) ctrl |= ATM_LAYER_SELECT; wr_regl (dev, CONTROL_0_REG, ctrl); // test SUNI-lite vs SAMBA // Register 0x00 in the SUNI will have some of bits 3-7 set, and // they will always be zero for the SAMBA. Ha! Bloody hardware // engineers. It'll never work. if (rd_framer (dev, 0) & 0x00f0) { // SUNI printk (" SUNI"); // Reset, just in case wr_framer (dev, 0x00, 0x0080); wr_framer (dev, 0x00, 0x0000); // Configure transmit FIFO wr_framer (dev, 0x63, rd_framer (dev, 0x63) | 0x0002); // Set line timed mode wr_framer (dev, 0x05, rd_framer (dev, 0x05) | 0x0001); } else { // SAMBA printk (" SAMBA"); // Reset, just in case wr_framer (dev, 0, rd_framer (dev, 0) | 0x0001); wr_framer (dev, 0, rd_framer (dev, 0) &~ 0x0001); // Turn off diagnostic loopback and enable line-timed mode wr_framer (dev, 0, 0x0002); // Turn on transmit outputs wr_framer (dev, 2, 0x0B80); } } else { // Select 25 mode ctrl &= ~ATM_LAYER_SELECT; // Madge B154 setup // none required? } printk (" LEDs"); GREEN_LED_ON(dev); YELLOW_LED_ON(dev); printk (" ESI="); { u16 b = 0; int i; u8 * esi = dev->atm_dev->esi; // in the card I have, EEPROM // addresses 0, 1, 2 contain 0 // addresess 5, 6 etc. contain ffff // NB: Madge prefix is 00 00 f6 (which is 00 00 6f in Ethernet bit order) // the read_bia routine gets the BIA in Ethernet bit order for (i=0; i < ESI_LEN; ++i) { if (i % 2 == 0) b = read_bia (dev, i/2 + 2); else b = b >> 8; esi[i] = b & 0xFF; printk ("%02x", esi[i]); } } // Enable RX_Q and ?X_COMPLETE interrupts only wr_regl (dev, INT_ENABLE_REG_OFF, INTERESTING_INTERRUPTS); printk (" IRQ on"); printk (".\n"); return onefivefive;}/********** check max_sdu **********/static int check_max_sdu (hrz_aal aal, struct atm_trafprm * tp, unsigned int max_frame_size) { PRINTD (DBG_FLOW|DBG_QOS, "check_max_sdu"); switch (aal) { case aal0: if (!(tp->max_sdu)) { PRINTD (DBG_QOS, "defaulting max_sdu"); tp->max_sdu = ATM_AAL0_SDU; } else if (tp->max_sdu != ATM_AAL0_SDU) { PRINTD (DBG_QOS|DBG_ERR, "rejecting max_sdu"); return -EINVAL; } break; case aal34: if (tp->max_sdu == 0 || tp->max_sdu > ATM_MAX_AAL34_PDU) { PRINTD (DBG_QOS, "%sing max_sdu", tp->max_sdu ? "capp" : "default"); tp->max_sdu = ATM_MAX_AAL34_PDU; } break; case aal5: if (tp->max_sdu == 0 || tp->max_sdu > max_frame_size) { PRINTD (DBG_QOS, "%sing max_sdu", tp->max_sdu ? "capp" : "default"); tp->max_sdu = max_frame_size; } break; } return 0;}/********** check pcr **********/// something like this should be part of ATM Linuxstatic int atm_pcr_check (struct atm_trafprm * tp, unsigned int pcr) { // we are assuming non-UBR, and non-special values of pcr if (tp->min_pcr == ATM_MAX_PCR) PRINTD (DBG_QOS, "luser gave min_pcr = ATM_MAX_PCR"); else if (tp->min_pcr < 0) PRINTD (DBG_QOS, "luser gave negative min_pcr"); else if (tp->min_pcr && tp->min_pcr > pcr) PRINTD (DBG_QOS, "pcr less than min_pcr"); else // !! max_pcr = UNSPEC (0) is equivalent to max_pcr = MAX (-1) // easier to #define ATM_MAX_PCR 0 and have all rates unsigned? // [this would get rid of next two conditionals] if ((0) && tp->max_pcr == ATM_MAX_PCR) PRINTD (DBG_QOS, "luser gave max_pcr = ATM_MAX_PCR"); else if ((tp->max_pcr != ATM_MAX_PCR) && tp->max_pcr < 0) PRINTD (DBG_QOS, "luser gave negative max_pcr"); else if (tp->max_pcr && tp->max_pcr != ATM_MAX_PCR && tp->max_pcr < pcr) PRINTD (DBG_QOS, "pcr greater than max_pcr"); else { // each limit unspecified or not violated PRINTD (DBG_QOS, "xBR(pcr) OK"); return 0; } PRINTD (DBG_QOS, "pcr=%u, tp: min_pcr=%d, pcr=%d, max_pcr=%d", pcr, tp->min_pcr, tp->pcr, tp->max_pcr); return -EINVAL;}/********** open VC **********/static int hrz_open (struct atm_vcc * atm_vcc, short vpi, int vci) { int error; u16 channel; struct atm_qos * qos; struct atm_trafprm * txtp; struct atm_trafprm * rxtp; hrz_dev * dev = HRZ_DEV(atm_vcc->dev); hrz_vcc vcc; hrz_vcc * vccp; // allocated late PRINTD (DBG_FLOW|DBG_VCC, "hrz_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 // deal with possibly wildcarded VCs error = atm_find_ci (atm_vcc
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -