📄 sbni.c
字号:
if( nl->state & FL_SLAVE ) spin_unlock( &((struct net_local *) nl->master->priv)->lock );#endif}/* * Routine returns 1 if it need to acknoweledge received frame. * Empty frame received without errors won't be acknoweledged. */static intrecv_frame( struct net_device *dev ){ struct net_local *nl = (struct net_local *) dev->priv; unsigned long ioaddr = dev->base_addr; u32 crc = CRC32_INITIAL; unsigned framelen, frameno, ack; unsigned is_first, frame_ok; if( check_fhdr( ioaddr, &framelen, &frameno, &ack, &is_first, &crc ) ) { frame_ok = framelen > 4 ? upload_data( dev, framelen, frameno, is_first, crc ) : skip_tail( ioaddr, framelen, crc ); if( frame_ok ) interpret_ack( dev, ack ); } else frame_ok = 0; outb( inb( ioaddr + CSR0 ) ^ CT_ZER, ioaddr + CSR0 ); if( frame_ok ) { nl->state |= FL_PREV_OK; if( framelen > 4 ) nl->in_stats.all_rx_number++; } else nl->state &= ~FL_PREV_OK, change_level( dev ), nl->in_stats.all_rx_number++, nl->in_stats.bad_rx_number++; return !frame_ok || framelen > 4;}static voidsend_frame( struct net_device *dev ){ struct net_local *nl = (struct net_local *) dev->priv; u32 crc = CRC32_INITIAL; if( nl->state & FL_NEED_RESEND ) { /* if frame was sended but not ACK'ed - resend it */ if( nl->trans_errors ) { --nl->trans_errors; if( nl->framelen != 0 ) nl->in_stats.resend_tx_number++; } else { /* cannot xmit with many attempts */#ifdef CONFIG_SBNI_MULTILINE if( (nl->state & FL_SLAVE) || nl->link )#endif nl->state |= FL_LINE_DOWN; drop_xmit_queue( dev ); goto do_send; } } else nl->trans_errors = TR_ERROR_COUNT; send_frame_header( dev, &crc ); nl->state |= FL_NEED_RESEND; /* * FL_NEED_RESEND will be cleared after ACK, but if empty * frame sended then in prepare_to_send next frame */ if( nl->framelen ) { download_data( dev, &crc ); nl->in_stats.all_tx_number++; nl->state |= FL_WAIT_ACK; } outsb( dev->base_addr + DAT, (u8 *)&crc, sizeof crc );do_send: outb( inb( dev->base_addr + CSR0 ) & ~TR_REQ, dev->base_addr + CSR0 ); if( nl->tx_frameno ) /* next frame exists - we request card to send it */ outb( inb( dev->base_addr + CSR0 ) | TR_REQ, dev->base_addr + CSR0 );}/* * Write the frame data into adapter's buffer memory, and calculate CRC. * Do padding if necessary. */static voiddownload_data( struct net_device *dev, u32 *crc_p ){ struct net_local *nl = (struct net_local *) dev->priv; struct sk_buff *skb = nl->tx_buf_p; unsigned len = min_t(unsigned int, skb->len - nl->outpos, nl->framelen); outsb( dev->base_addr + DAT, skb->data + nl->outpos, len ); *crc_p = calc_crc32( *crc_p, skb->data + nl->outpos, len ); /* if packet too short we should write some more bytes to pad */ for( len = nl->framelen - len; len--; ) outb( 0, dev->base_addr + DAT ), *crc_p = CRC32( 0, *crc_p );}static intupload_data( struct net_device *dev, unsigned framelen, unsigned frameno, unsigned is_first, u32 crc ){ struct net_local *nl = (struct net_local *) dev->priv; int frame_ok; if( is_first ) nl->wait_frameno = frameno, nl->inppos = 0; if( nl->wait_frameno == frameno ) { if( nl->inppos + framelen <= ETHER_MAX_LEN ) frame_ok = append_frame_to_pkt( dev, framelen, crc ); /* * if CRC is right but framelen incorrect then transmitter * error was occurred... drop entire packet */ else if( (frame_ok = skip_tail( dev->base_addr, framelen, crc )) != 0 ) nl->wait_frameno = 0, nl->inppos = 0,#ifdef CONFIG_SBNI_MULTILINE ((struct net_local *) nl->master->priv) ->stats.rx_errors++, ((struct net_local *) nl->master->priv) ->stats.rx_missed_errors++;#else nl->stats.rx_errors++, nl->stats.rx_missed_errors++;#endif /* now skip all frames until is_first != 0 */ } else frame_ok = skip_tail( dev->base_addr, framelen, crc ); if( is_first && !frame_ok ) /* * Frame has been broken, but we had already stored * is_first... Drop entire packet. */ nl->wait_frameno = 0,#ifdef CONFIG_SBNI_MULTILINE ((struct net_local *) nl->master->priv)->stats.rx_errors++, ((struct net_local *) nl->master->priv)->stats.rx_crc_errors++;#else nl->stats.rx_errors++, nl->stats.rx_crc_errors++;#endif return frame_ok;}static __inline voidsend_complete( struct net_local *nl ){#ifdef CONFIG_SBNI_MULTILINE ((struct net_local *) nl->master->priv)->stats.tx_packets++; ((struct net_local *) nl->master->priv)->stats.tx_bytes += nl->tx_buf_p->len;#else nl->stats.tx_packets++; nl->stats.tx_bytes += nl->tx_buf_p->len;#endif dev_kfree_skb_irq( nl->tx_buf_p ); nl->tx_buf_p = NULL; nl->outpos = 0; nl->state &= ~(FL_WAIT_ACK | FL_NEED_RESEND); nl->framelen = 0;}static voidinterpret_ack( struct net_device *dev, unsigned ack ){ struct net_local *nl = (struct net_local *) dev->priv; if( ack == FRAME_SENT_OK ) { nl->state &= ~FL_NEED_RESEND; if( nl->state & FL_WAIT_ACK ) { nl->outpos += nl->framelen; if( --nl->tx_frameno ) nl->framelen = min_t(unsigned int, nl->maxframe, nl->tx_buf_p->len - nl->outpos); else send_complete( nl ),#ifdef CONFIG_SBNI_MULTILINE netif_wake_queue( nl->master );#else netif_wake_queue( dev );#endif } } nl->state &= ~FL_WAIT_ACK;}/* * Glue received frame with previous fragments of packet. * Indicate packet when last frame would be accepted. */static intappend_frame_to_pkt( struct net_device *dev, unsigned framelen, u32 crc ){ struct net_local *nl = (struct net_local *) dev->priv; u8 *p; if( nl->inppos + framelen > ETHER_MAX_LEN ) return 0; if( !nl->rx_buf_p && !(nl->rx_buf_p = get_rx_buf( dev )) ) return 0; p = nl->rx_buf_p->data + nl->inppos; insb( dev->base_addr + DAT, p, framelen ); if( calc_crc32( crc, p, framelen ) != CRC32_REMAINDER ) return 0; nl->inppos += framelen - 4; if( --nl->wait_frameno == 0 ) /* last frame received */ indicate_pkt( dev ); return 1;}/* * Prepare to start output on adapter. * Transmitter will be actually activated when marker is accepted. */static voidprepare_to_send( struct sk_buff *skb, struct net_device *dev ){ struct net_local *nl = (struct net_local *) dev->priv; unsigned int len; /* nl->tx_buf_p == NULL here! */ if( nl->tx_buf_p ) printk( KERN_ERR "%s: memory leak!\n", dev->name ); nl->outpos = 0; nl->state &= ~(FL_WAIT_ACK | FL_NEED_RESEND); len = skb->len; if( len < SBNI_MIN_LEN ) len = SBNI_MIN_LEN; nl->tx_buf_p = skb; nl->tx_frameno = (len + nl->maxframe - 1) / nl->maxframe; nl->framelen = len < nl->maxframe ? len : nl->maxframe; outb( inb( dev->base_addr + CSR0 ) | TR_REQ, dev->base_addr + CSR0 );#ifdef CONFIG_SBNI_MULTILINE nl->master->trans_start = jiffies;#else dev->trans_start = jiffies;#endif}static voiddrop_xmit_queue( struct net_device *dev ){ struct net_local *nl = (struct net_local *) dev->priv; if( nl->tx_buf_p ) dev_kfree_skb_any( nl->tx_buf_p ), nl->tx_buf_p = NULL,#ifdef CONFIG_SBNI_MULTILINE ((struct net_local *) nl->master->priv) ->stats.tx_errors++, ((struct net_local *) nl->master->priv) ->stats.tx_carrier_errors++;#else nl->stats.tx_errors++, nl->stats.tx_carrier_errors++;#endif nl->tx_frameno = 0; nl->framelen = 0; nl->outpos = 0; nl->state &= ~(FL_WAIT_ACK | FL_NEED_RESEND);#ifdef CONFIG_SBNI_MULTILINE netif_start_queue( nl->master ); nl->master->trans_start = jiffies;#else netif_start_queue( dev ); dev->trans_start = jiffies;#endif}static voidsend_frame_header( struct net_device *dev, u32 *crc_p ){ struct net_local *nl = (struct net_local *) dev->priv; u32 crc = *crc_p; u32 len_field = nl->framelen + 6; /* CRC + frameno + reserved */ u8 value; if( nl->state & FL_NEED_RESEND ) len_field |= FRAME_RETRY; /* non-first attempt... */ if( nl->outpos == 0 ) len_field |= FRAME_FIRST; len_field |= (nl->state & FL_PREV_OK) ? FRAME_SENT_OK : FRAME_SENT_BAD; outb( SBNI_SIG, dev->base_addr + DAT ); value = (u8) len_field; outb( value, dev->base_addr + DAT ); crc = CRC32( value, crc ); value = (u8) (len_field >> 8); outb( value, dev->base_addr + DAT ); crc = CRC32( value, crc ); outb( nl->tx_frameno, dev->base_addr + DAT ); crc = CRC32( nl->tx_frameno, crc ); outb( 0, dev->base_addr + DAT ); crc = CRC32( 0, crc ); *crc_p = crc;}/* * if frame tail not needed (incorrect number or received twice), * it won't store, but CRC will be calculated */static intskip_tail( unsigned int ioaddr, unsigned int tail_len, u32 crc ){ while( tail_len-- ) crc = CRC32( inb( ioaddr + DAT ), crc ); return crc == CRC32_REMAINDER;}/* * Preliminary checks if frame header is correct, calculates its CRC * and split it to simple fields */static intcheck_fhdr( u32 ioaddr, u32 *framelen, u32 *frameno, u32 *ack, u32 *is_first, u32 *crc_p ){ u32 crc = *crc_p; u8 value; if( inb( ioaddr + DAT ) != SBNI_SIG ) return 0; value = inb( ioaddr + DAT ); *framelen = (u32)value; crc = CRC32( value, crc ); value = inb( ioaddr + DAT ); *framelen |= ((u32)value) << 8; crc = CRC32( value, crc ); *ack = *framelen & FRAME_ACK_MASK; *is_first = (*framelen & FRAME_FIRST) != 0; if( (*framelen &= FRAME_LEN_MASK) < 6 || *framelen > SBNI_MAX_FRAME - 3 ) return 0; value = inb( ioaddr + DAT ); *frameno = (u32)value; crc = CRC32( value, crc ); crc = CRC32( inb( ioaddr + DAT ), crc ); /* reserved byte */ *framelen -= 2; *crc_p = crc; return 1;}static struct sk_buff *get_rx_buf( struct net_device *dev ){ /* +2 is to compensate for the alignment fixup below */ struct sk_buff *skb = dev_alloc_skb( ETHER_MAX_LEN + 2 ); if( !skb ) return NULL;#ifdef CONFIG_SBNI_MULTILINE skb->dev = ((struct net_local *) dev->priv)->master;#else skb->dev = dev;#endif skb_reserve( skb, 2 ); /* Align IP on longword boundaries */ return skb;}static voidindicate_pkt( struct net_device *dev ){ struct net_local *nl = (struct net_local *) dev->priv; struct sk_buff *skb = nl->rx_buf_p; skb_put( skb, nl->inppos );#ifdef CONFIG_SBNI_MULTILINE skb->protocol = eth_type_trans( skb, nl->master ); netif_rx( skb ); dev->last_rx = jiffies; ++((struct net_local *) nl->master->priv)->stats.rx_packets; ((struct net_local *) nl->master->priv)->stats.rx_bytes += nl->inppos;#else skb->protocol = eth_type_trans( skb, dev ); netif_rx( skb ); dev->last_rx = jiffies; ++nl->stats.rx_packets; nl->stats.rx_bytes += nl->inppos;#endif nl->rx_buf_p = NULL; /* protocol driver will clear this sk_buff */}/* -------------------------------------------------------------------------- *//* * Routine checks periodically wire activity and regenerates marker if * connect was inactive for a long time. */static voidsbni_watchdog( unsigned long arg ){ struct net_device *dev = (struct net_device *) arg; struct net_local *nl = (struct net_local *) dev->priv; struct timer_list *w = &nl->watchdog; unsigned long flags; unsigned char csr0; spin_lock_irqsave( &nl->lock, flags ); csr0 = inb( dev->base_addr + CSR0 ); if( csr0 & RC_CHK ) { if( nl->timer_ticks ) { if( csr0 & (RC_RDY | BU_EMP) ) /* receiving not active */ nl->timer_ticks--; } else { nl->in_stats.timeout_number++; if( nl->delta_rxl ) timeout_change_level( dev ); outb( *(u_char *)&nl->csr1 | PR_RES, dev->base_addr + CSR1 ); csr0 = inb( dev->base_addr + CSR0 ); } } else nl->state &= ~FL_LINE_DOWN; outb( csr0 | RC_CHK, dev->base_addr + CSR0 ); init_timer( w ); w->expires = jiffies + SBNI_TIMEOUT; w->data = arg; w->function = sbni_watchdog; add_timer( w ); spin_unlock_irqrestore( &nl->lock, flags );}static unsigned char rxl_tab[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x08, 0x0a, 0x0c, 0x0f, 0x16, 0x18, 0x1a, 0x1c, 0x1f};#define SIZE_OF_TIMEOUT_RXL_TAB 4static unsigned char timeout_rxl_tab[] = { 0x03, 0x05, 0x08, 0x0b};/* -------------------------------------------------------------------------- */static voidcard_start( struct net_device *dev ){ struct net_local *nl = (struct net_local *) dev->priv; nl->timer_ticks = CHANGE_LEVEL_START_TICKS; nl->state &= ~(FL_WAIT_ACK | FL_NEED_RESEND); nl->state |= FL_PREV_OK; nl->inppos = nl->outpos = 0; nl->wait_frameno = 0; nl->tx_frameno = 0; nl->framelen = 0; outb( *(u_char *)&nl->csr1 | PR_RES, dev->base_addr + CSR1 ); outb( EN_INT, dev->base_addr + CSR0 );}/* -------------------------------------------------------------------------- *//* Receive level auto-selection */static voidchange_level( struct net_device *dev ){ struct net_local *nl = (struct net_local *) dev->priv; if( nl->delta_rxl == 0 ) /* do not auto-negotiate RxL */ return; if( nl->cur_rxl_index == 0 ) nl->delta_rxl = 1; else if( nl->cur_rxl_index == 15 ) nl->delta_rxl = -1; else if( nl->cur_rxl_rcvd < nl->prev_rxl_rcvd ) nl->delta_rxl = -nl->delta_rxl; nl->csr1.rxl = rxl_tab[ nl->cur_rxl_index += nl->delta_rxl ]; inb( dev->base_addr + CSR0 ); /* needs for PCI cards */ outb( *(u8 *)&nl->csr1, dev->base_addr + CSR1 ); nl->prev_rxl_rcvd = nl->cur_rxl_rcvd; nl->cur_rxl_rcvd = 0;}static voidtimeout_change_level( struct net_device *dev ){ struct net_local *nl = (struct net_local *) dev->priv; nl->cur_rxl_index = timeout_rxl_tab[ nl->timeout_rxl ]; if( ++nl->timeout_rxl >= 4 ) nl->timeout_rxl = 0; nl->csr1.rxl = rxl_tab[ nl->cur_rxl_index ]; inb( dev->base_addr + CSR0 ); outb( *(unsigned char *)&nl->csr1, dev->base_addr + CSR1 ); nl->prev_rxl_rcvd = nl->cur_rxl_rcvd;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -