📄 pegasus.c
字号:
ret = set_register(pegasus, Gpio1, 0x26); ret = set_register(pegasus, Gpio0, pegasus->features); ret = set_register(pegasus, Gpio0, DEFAULT_GPIO_SET); break; } } if (i == REG_TIMEOUT) return 1; if (usb_dev_id[pegasus->dev_index].vendor == VENDOR_LINKSYS || usb_dev_id[pegasus->dev_index].vendor == VENDOR_DLINK) { ret = set_register(pegasus, Gpio0, 0x24); ret = set_register(pegasus, Gpio0, 0x26); } if (usb_dev_id[pegasus->dev_index].vendor == VENDOR_ELCON) { __u16 auxmode; read_mii_word(pegasus, 3, 0x1b, &auxmode); write_mii_word(pegasus, 3, 0x1b, auxmode | 4); } return 0;}static int enable_net_traffic(struct net_device *dev, struct usb_device *usb){ __u16 linkpart; __u8 data[4]; pegasus_t *pegasus = netdev_priv(dev); int ret; read_mii_word(pegasus, pegasus->phy, MII_LPA, &linkpart); data[0] = 0xc9; data[1] = 0; if (linkpart & (ADVERTISE_100FULL | ADVERTISE_10FULL)) data[1] |= 0x20; /* set full duplex */ if (linkpart & (ADVERTISE_100FULL | ADVERTISE_100HALF)) data[1] |= 0x10; /* set 100 Mbps */ if (mii_mode) data[1] = 0; data[2] = (loopback & 1) ? 0x09 : 0x01; memcpy(pegasus->eth_regs, data, sizeof (data)); ret = set_registers(pegasus, EthCtrl0, 3, data); if (usb_dev_id[pegasus->dev_index].vendor == VENDOR_LINKSYS || usb_dev_id[pegasus->dev_index].vendor == VENDOR_DLINK) { u16 auxmode; read_mii_word(pegasus, 0, 0x1b, &auxmode); write_mii_word(pegasus, 0, 0x1b, auxmode | 4); } return 0;}static void fill_skb_pool(pegasus_t * pegasus){ int i; for (i = 0; i < RX_SKBS; i++) { if (pegasus->rx_pool[i]) continue; pegasus->rx_pool[i] = dev_alloc_skb(PEGASUS_MTU + 2); /* ** we give up if the allocation fail. the tasklet will be ** rescheduled again anyway... */ if (pegasus->rx_pool[i] == NULL) return; pegasus->rx_pool[i]->dev = pegasus->net; skb_reserve(pegasus->rx_pool[i], 2); }}static void free_skb_pool(pegasus_t * pegasus){ int i; for (i = 0; i < RX_SKBS; i++) { if (pegasus->rx_pool[i]) { dev_kfree_skb(pegasus->rx_pool[i]); pegasus->rx_pool[i] = NULL; } }}static inline struct sk_buff *pull_skb(pegasus_t * pegasus){ int i; struct sk_buff *skb; for (i = 0; i < RX_SKBS; i++) { if (likely(pegasus->rx_pool[i] != NULL)) { skb = pegasus->rx_pool[i]; pegasus->rx_pool[i] = NULL; return skb; } } return NULL;}static void read_bulk_callback(struct urb *urb, struct pt_regs *regs){ pegasus_t *pegasus = urb->context; struct net_device *net; int rx_status, count = urb->actual_length; u8 *buf = urb->transfer_buffer; __u16 pkt_len; if (!pegasus) return; net = pegasus->net; if (!netif_device_present(net) || !netif_running(net)) return; switch (urb->status) { case 0: break; case -ETIMEDOUT: if (netif_msg_rx_err(pegasus)) pr_debug("%s: reset MAC\n", net->name); pegasus->flags &= ~PEGASUS_RX_BUSY; break; case -EPIPE: /* stall, or disconnect from TT */ /* FIXME schedule work to clear the halt */ if (netif_msg_rx_err(pegasus)) printk(KERN_WARNING "%s: no rx stall recovery\n", net->name); return; case -ENOENT: case -ECONNRESET: case -ESHUTDOWN: if (netif_msg_ifdown(pegasus)) pr_debug("%s: rx unlink, %d\n", net->name, urb->status); return; default: if (netif_msg_rx_err(pegasus)) pr_debug("%s: RX status %d\n", net->name, urb->status); goto goon; } if (!count || count < 4) goto goon; rx_status = buf[count - 2]; if (rx_status & 0x1e) { if (netif_msg_rx_err(pegasus)) pr_debug("%s: RX packet error %x\n", net->name, rx_status); pegasus->stats.rx_errors++; if (rx_status & 0x06) // long or runt pegasus->stats.rx_length_errors++; if (rx_status & 0x08) pegasus->stats.rx_crc_errors++; if (rx_status & 0x10) // extra bits pegasus->stats.rx_frame_errors++; goto goon; } if (pegasus->chip == 0x8513) { pkt_len = le32_to_cpu(*(__le32 *)urb->transfer_buffer); pkt_len &= 0x0fff; pegasus->rx_skb->data += 2; } else { pkt_len = buf[count - 3] << 8; pkt_len += buf[count - 4]; pkt_len &= 0xfff; pkt_len -= 8; } /* * If the packet is unreasonably long, quietly drop it rather than * kernel panicing by calling skb_put. */ if (pkt_len > PEGASUS_MTU) goto goon; /* * at this point we are sure pegasus->rx_skb != NULL * so we go ahead and pass up the packet. */ skb_put(pegasus->rx_skb, pkt_len); pegasus->rx_skb->protocol = eth_type_trans(pegasus->rx_skb, net); netif_rx(pegasus->rx_skb); pegasus->stats.rx_packets++; pegasus->stats.rx_bytes += pkt_len; if (pegasus->flags & PEGASUS_UNPLUG) return; spin_lock(&pegasus->rx_pool_lock); pegasus->rx_skb = pull_skb(pegasus); spin_unlock(&pegasus->rx_pool_lock); if (pegasus->rx_skb == NULL) goto tl_sched;goon: usb_fill_bulk_urb(pegasus->rx_urb, pegasus->usb, usb_rcvbulkpipe(pegasus->usb, 1), pegasus->rx_skb->data, PEGASUS_MTU + 8, read_bulk_callback, pegasus); if (usb_submit_urb(pegasus->rx_urb, GFP_ATOMIC)) { pegasus->flags |= PEGASUS_RX_URB_FAIL; goto tl_sched; } else { pegasus->flags &= ~PEGASUS_RX_URB_FAIL; } return;tl_sched: tasklet_schedule(&pegasus->rx_tl);}static void rx_fixup(unsigned long data){ pegasus_t *pegasus; unsigned long flags; pegasus = (pegasus_t *) data; if (pegasus->flags & PEGASUS_UNPLUG) return; spin_lock_irqsave(&pegasus->rx_pool_lock, flags); fill_skb_pool(pegasus); if (pegasus->flags & PEGASUS_RX_URB_FAIL) if (pegasus->rx_skb) goto try_again; if (pegasus->rx_skb == NULL) { pegasus->rx_skb = pull_skb(pegasus); } if (pegasus->rx_skb == NULL) { if (netif_msg_rx_err(pegasus)) printk(KERN_WARNING "%s: low on memory\n", pegasus->net->name); tasklet_schedule(&pegasus->rx_tl); goto done; } usb_fill_bulk_urb(pegasus->rx_urb, pegasus->usb, usb_rcvbulkpipe(pegasus->usb, 1), pegasus->rx_skb->data, PEGASUS_MTU + 8, read_bulk_callback, pegasus);try_again: if (usb_submit_urb(pegasus->rx_urb, GFP_ATOMIC)) { pegasus->flags |= PEGASUS_RX_URB_FAIL; tasklet_schedule(&pegasus->rx_tl); } else { pegasus->flags &= ~PEGASUS_RX_URB_FAIL; }done: spin_unlock_irqrestore(&pegasus->rx_pool_lock, flags);}static void write_bulk_callback(struct urb *urb, struct pt_regs *regs){ pegasus_t *pegasus = urb->context; struct net_device *net = pegasus->net; if (!pegasus) return; if (!netif_device_present(net) || !netif_running(net)) return; switch (urb->status) { case -EPIPE: /* FIXME schedule_work() to clear the tx halt */ netif_stop_queue(net); if (netif_msg_tx_err(pegasus)) printk(KERN_WARNING "%s: no tx stall recovery\n", net->name); return; case -ENOENT: case -ECONNRESET: case -ESHUTDOWN: if (netif_msg_ifdown(pegasus)) pr_debug("%s: tx unlink, %d\n", net->name, urb->status); return; default: if (netif_msg_tx_err(pegasus)) pr_info("%s: TX status %d\n", net->name, urb->status); /* FALL THROUGH */ case 0: break; } net->trans_start = jiffies; netif_wake_queue(net);}static void intr_callback(struct urb *urb, struct pt_regs *regs){ pegasus_t *pegasus = urb->context; struct net_device *net; int status; if (!pegasus) return; net = pegasus->net; switch (urb->status) { case 0: break; case -ECONNRESET: /* unlink */ case -ENOENT: case -ESHUTDOWN: return; default: /* some Pegasus-I products report LOTS of data * toggle errors... avoid log spamming */ if (netif_msg_timer(pegasus)) pr_debug("%s: intr status %d\n", net->name, urb->status); } if (urb->actual_length >= 6) { u8 * d = urb->transfer_buffer; /* byte 0 == tx_status1, reg 2B */ if (d[0] & (TX_UNDERRUN|EXCESSIVE_COL |LATE_COL|JABBER_TIMEOUT)) { pegasus->stats.tx_errors++; if (d[0] & TX_UNDERRUN) pegasus->stats.tx_fifo_errors++; if (d[0] & (EXCESSIVE_COL | JABBER_TIMEOUT)) pegasus->stats.tx_aborted_errors++; if (d[0] & LATE_COL) pegasus->stats.tx_window_errors++; } /* d[5].LINK_STATUS lies on some adapters. * d[0].NO_CARRIER kicks in only with failed TX. * ... so monitoring with MII may be safest. */ if (d[0] & NO_CARRIER) netif_carrier_off(net); else netif_carrier_on(net); /* bytes 3-4 == rx_lostpkt, reg 2E/2F */ pegasus->stats.rx_missed_errors += ((d[3] & 0x7f) << 8) | d[4]; } status = usb_submit_urb(urb, SLAB_ATOMIC); if (status && netif_msg_timer(pegasus)) printk(KERN_ERR "%s: can't resubmit interrupt urb, %d\n", net->name, status);}static void pegasus_tx_timeout(struct net_device *net){ pegasus_t *pegasus = netdev_priv(net); if (netif_msg_timer(pegasus)) printk(KERN_WARNING "%s: tx timeout\n", net->name); usb_unlink_urb(pegasus->tx_urb); pegasus->stats.tx_errors++;}static int pegasus_start_xmit(struct sk_buff *skb, struct net_device *net){ pegasus_t *pegasus = netdev_priv(net); int count = ((skb->len + 2) & 0x3f) ? skb->len + 2 : skb->len + 3; int res; __u16 l16 = skb->len; netif_stop_queue(net); ((__le16 *) pegasus->tx_buff)[0] = cpu_to_le16(l16); memcpy(pegasus->tx_buff + 2, skb->data, skb->len); usb_fill_bulk_urb(pegasus->tx_urb, pegasus->usb, usb_sndbulkpipe(pegasus->usb, 2), pegasus->tx_buff, count, write_bulk_callback, pegasus); if ((res = usb_submit_urb(pegasus->tx_urb, GFP_ATOMIC))) { if (netif_msg_tx_err(pegasus)) printk(KERN_WARNING "%s: fail tx, %d\n", net->name, res); switch (res) { case -EPIPE: /* stall, or disconnect from TT */ /* cleanup should already have been scheduled */ break; case -ENODEV: /* disconnect() upcoming */ break; default: pegasus->stats.tx_errors++; netif_start_queue(net); } } else { pegasus->stats.tx_packets++; pegasus->stats.tx_bytes += skb->len; net->trans_start = jiffies; } dev_kfree_skb(skb); return 0;}static struct net_device_stats *pegasus_netdev_stats(struct net_device *dev){ return &((pegasus_t *) netdev_priv(dev))->stats;}static inline void disable_net_traffic(pegasus_t * pegasus){ int tmp = 0; int ret; ret = set_registers(pegasus, EthCtrl0, 2, &tmp);}static inline void get_interrupt_interval(pegasus_t * pegasus){ __u8 data[2]; read_eprom_word(pegasus, 4, (__u16 *) data); if (pegasus->usb->speed != USB_SPEED_HIGH) { if (data[1] < 0x80) { if (netif_msg_timer(pegasus)) dev_info(&pegasus->intf->dev, "intr interval " "changed from %ums to %ums\n", data[1], 0x80); data[1] = 0x80;#ifdef PEGASUS_WRITE_EEPROM write_eprom_word(pegasus, 4, *(__u16 *) data);#endif } } pegasus->intr_interval = data[1];}static void set_carrier(struct net_device *net){ pegasus_t *pegasus = netdev_priv(net); u16 tmp; if (!read_mii_word(pegasus, pegasus->phy, MII_BMSR, &tmp)) return; if (tmp & BMSR_LSTATUS) netif_carrier_on(net); else netif_carrier_off(net);}static void free_all_urbs(pegasus_t * pegasus){ usb_free_urb(pegasus->intr_urb); usb_free_urb(pegasus->tx_urb); usb_free_urb(pegasus->rx_urb); usb_free_urb(pegasus->ctrl_urb);}static void unlink_all_urbs(pegasus_t * pegasus){ usb_kill_urb(pegasus->intr_urb); usb_kill_urb(pegasus->tx_urb); usb_kill_urb(pegasus->rx_urb); usb_kill_urb(pegasus->ctrl_urb);}static int alloc_urbs(pegasus_t * pegasus){ pegasus->ctrl_urb = usb_alloc_urb(0, GFP_KERNEL); if (!pegasus->ctrl_urb) { return 0; } pegasus->rx_urb = usb_alloc_urb(0, GFP_KERNEL); if (!pegasus->rx_urb) { usb_free_urb(pegasus->ctrl_urb); return 0; } pegasus->tx_urb = usb_alloc_urb(0, GFP_KERNEL); if (!pegasus->tx_urb) { usb_free_urb(pegasus->rx_urb); usb_free_urb(pegasus->ctrl_urb); return 0; } pegasus->intr_urb = usb_alloc_urb(0, GFP_KERNEL);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -