📄 wrapndis.c
字号:
skb->len, PCI_DMA_TODEVICE); oob_data->ndis_sg_element.length = skb->len; oob_data->ndis_sg_list.nent = 1; oob_data->ndis_sg_list.elements = &oob_data->ndis_sg_element; oob_data->extension.info[ScatterGatherListPacketInfo] = &oob_data->ndis_sg_list; }#if 0 if (wnd->tx_csum_info.value) oob_data->extension.info[TcpIpChecksumPacketInfo] = &wnd->tx_csum_info;#endif DBG_BLOCK(4) { dump_bytes(__FUNCTION__, skb->data, skb->len); } DBGTRACE3("packet: %p, buffer: %p, skb: %p", packet, buffer, skb); return packet;}void free_tx_packet(struct wrap_ndis_device *wnd, struct ndis_packet *packet, NDIS_STATUS status){ ndis_buffer *buffer; struct ndis_packet_oob_data *oob_data; TRACEENTER3("%p, %08X", packet, status); if (status == NDIS_STATUS_SUCCESS) { pre_atomic_add(wnd->stats.tx_bytes, packet->private.len); atomic_inc_var(wnd->stats.tx_packets); } else { DBGTRACE1("packet dropped: %08X", status); atomic_inc_var(wnd->stats.tx_dropped); } oob_data = NDIS_PACKET_OOB_DATA(packet); if (wnd->use_sg_dma) PCI_DMA_UNMAP_SINGLE(wnd->wd->pci.pdev, oob_data->ndis_sg_element.address, oob_data->ndis_sg_element.length, PCI_DMA_TODEVICE); buffer = packet->private.buffer_head; DBGTRACE3("freeing buffer %p", buffer); NdisFreeBuffer(buffer); dev_kfree_skb_any(oob_data->skb); DBGTRACE3("freeing packet %p", packet); NdisFreePacket(packet); TRACEEXIT3(return);}/* MiniportSend and MiniportSendPackets *//* this function is called holding tx_ring_mutex. start and n are such * that start + n < TX_RING_SIZE; i.e., packets don't wrap around * ring */static int miniport_tx_packets(struct wrap_ndis_device *wnd, int start, int n){ NDIS_STATUS res; struct miniport_char *miniport; struct ndis_packet *packet; int sent; KIRQL irql; DBGTRACE3("%d, %d", start, n); miniport = &wnd->wd->driver->ndis_driver->miniport; if (miniport->send_packets) { if (deserialized_driver(wnd)) { LIN2WIN3(miniport->send_packets, wnd->nmb->adapter_ctx, &wnd->tx_ring[start], n); sent = n; } else { struct ndis_packet_oob_data *oob_data; irql = raise_irql(DISPATCH_LEVEL); serialize_lock(wnd); LIN2WIN3(miniport->send_packets, wnd->nmb->adapter_ctx, &wnd->tx_ring[start], n); serialize_unlock(wnd); lower_irql(irql); for (sent = 0; sent < n && wnd->tx_ok; sent++) { NDIS_STATUS pkt_status; packet = wnd->tx_ring[start + sent]; oob_data = NDIS_PACKET_OOB_DATA(packet); switch ((pkt_status = xchg(&oob_data->status, NDIS_STATUS_NOT_RECOGNIZED))) { case NDIS_STATUS_SUCCESS: free_tx_packet(wnd, packet, NDIS_STATUS_SUCCESS); break; case NDIS_STATUS_PENDING: break; case NDIS_STATUS_RESOURCES: atomic_dec_var(wnd->tx_ok); /* resubmit this packet and * the rest when resources * become available */ sent--; break; case NDIS_STATUS_FAILURE: free_tx_packet(wnd, packet, NDIS_STATUS_FAILURE); break; default: ERROR("%p: invalid status: %08X", packet, pkt_status); free_tx_packet(wnd, packet, oob_data->status); break; } DBGTRACE3("%p, %d", packet, pkt_status); } } DBGTRACE3("sent: %d(%d)", sent, n); } else { irql = PASSIVE_LEVEL; for (sent = 0; sent < n && wnd->tx_ok; sent++) { struct ndis_packet_oob_data *oob_data; packet = wnd->tx_ring[start + sent]; oob_data = NDIS_PACKET_OOB_DATA(packet); oob_data->status = NDIS_STATUS_NOT_RECOGNIZED; irql = serialize_lock_irql(wnd); res = LIN2WIN3(miniport->send, wnd->nmb->adapter_ctx, packet, packet->private.flags); serialize_unlock_irql(wnd, irql); switch (res) { case NDIS_STATUS_SUCCESS: free_tx_packet(wnd, packet, res); break; case NDIS_STATUS_PENDING: break; case NDIS_STATUS_RESOURCES: atomic_dec_var(wnd->tx_ok); /* resend this packet when resources * become available */ sent--; break; case NDIS_STATUS_FAILURE: free_tx_packet(wnd, packet, res); break; default: ERROR("packet %p: invalid status: %08X", packet, res); break; } } } TRACEEXIT3(return sent);}static void tx_worker(void *param){ struct wrap_ndis_device *wnd = param; int n; TRACEENTER3("tx_ok %d", wnd->tx_ok); while (wnd->tx_ok) { if (down_interruptible(&wnd->tx_ring_mutex)) break; /* end == start if either ring is empty or full; in * the latter case is_tx_ring_full is set */ n = wnd->tx_ring_end - wnd->tx_ring_start; DBGTRACE3("%d, %d, %d", wnd->tx_ring_start, wnd->tx_ring_end, n); if (n == 0) { if (wnd->is_tx_ring_full) n = TX_RING_SIZE - wnd->tx_ring_start; else { up(&wnd->tx_ring_mutex); break; } } else if (n < 0) n = TX_RING_SIZE - wnd->tx_ring_start; if (unlikely(n > wnd->max_tx_packets)) n = wnd->max_tx_packets; n = miniport_tx_packets(wnd, wnd->tx_ring_start, n); if (n > 0) { wnd->net_dev->trans_start = jiffies; wnd->tx_ring_start = (wnd->tx_ring_start + n) % TX_RING_SIZE; wnd->is_tx_ring_full = 0; if (netif_queue_stopped(wnd->net_dev)) netif_wake_queue(wnd->net_dev); } up(&wnd->tx_ring_mutex); DBGTRACE3("%d, %d, %d", wnd->tx_ring_start, wnd->tx_ring_end, n); } TRACEEXIT3(return);}static int tx_skbuff(struct sk_buff *skb, struct net_device *dev){ struct wrap_ndis_device *wnd = netdev_priv(dev); struct ndis_packet *packet; packet = alloc_tx_packet(wnd, skb); if (!packet) { WARNING("couldn't allocate packet"); return NETDEV_TX_BUSY; } /* no need for lock here - already called holding * net_dev->xmit_lock and tx_ring_end is not updated * elsewhere */ wnd->tx_ring[wnd->tx_ring_end++] = packet; if (wnd->tx_ring_end == TX_RING_SIZE) wnd->tx_ring_end = 0; if (wnd->tx_ring_end == wnd->tx_ring_start) { wnd->is_tx_ring_full = 1; netif_stop_queue(wnd->net_dev); } DBGTRACE3("ring: %d, %d", wnd->tx_ring_start, wnd->tx_ring_end); schedule_wrap_work(&wnd->tx_work); return NETDEV_TX_OK;}static int set_packet_filter(struct wrap_ndis_device *wnd, ULONG packet_filter){ NDIS_STATUS res; while (1) { res = miniport_set_int(wnd, OID_GEN_CURRENT_PACKET_FILTER, packet_filter); if (res == NDIS_STATUS_SUCCESS) break; DBGTRACE2("couldn't set filter 0x%08x", packet_filter); /* NDIS_PACKET_TYPE_PROMISCUOUS may not work with 802.11 */ if (packet_filter & NDIS_PACKET_TYPE_PROMISCUOUS) { packet_filter &= ~NDIS_PACKET_TYPE_PROMISCUOUS; continue; } if (packet_filter & NDIS_PACKET_TYPE_ALL_LOCAL) { packet_filter &= ~NDIS_PACKET_TYPE_ALL_LOCAL; continue; } if (packet_filter & NDIS_PACKET_TYPE_ALL_FUNCTIONAL) { packet_filter &= ~NDIS_PACKET_TYPE_ALL_FUNCTIONAL; continue; } if (packet_filter & NDIS_PACKET_TYPE_MULTICAST) { packet_filter &= ~NDIS_PACKET_TYPE_MULTICAST; packet_filter |= NDIS_PACKET_TYPE_ALL_MULTICAST; continue; } if (packet_filter & NDIS_PACKET_TYPE_ALL_MULTICAST) { packet_filter &= ~NDIS_PACKET_TYPE_ALL_MULTICAST; continue; } break; } wnd->packet_filter = packet_filter; res = miniport_query_int(wnd, OID_GEN_CURRENT_PACKET_FILTER, &packet_filter); if (packet_filter != wnd->packet_filter) { WARNING("filter not set: 0x%08x, 0x%08x", packet_filter, wnd->packet_filter); wnd->packet_filter = packet_filter; } if (wnd->packet_filter) TRACEEXIT3(return 0); else TRACEEXIT3(return -1);}static int ndis_net_dev_open(struct net_device *net_dev){ struct wrap_ndis_device *wnd = netdev_priv(net_dev); TRACEENTER1("%p", wnd); if (set_packet_filter(wnd, wnd->packet_filter)) { WARNING("couldn't set packet filter"); return -ENODEV; } netif_wake_queue(net_dev); netif_poll_enable(net_dev); return 0;}static int ndis_net_dev_close(struct net_device *net_dev){ netif_poll_disable(net_dev); netif_tx_disable(net_dev); return 0;}#ifdef CONFIG_NET_POLL_CONTROLLERstatic void ndis_poll_controller(struct net_device *dev){ struct wrap_ndis_device *wnd = netdev_priv(dev); disable_irq(dev->irq); ndis_isr(dev->irq, wnd, NULL); enable_irq(dev->irq);}#endif/* called from BH context */static struct net_device_stats *ndis_get_stats(struct net_device *dev){ struct wrap_ndis_device *wnd = netdev_priv(dev); return &wnd->stats;}/* called from BH context */static void ndis_set_multicast_list(struct net_device *dev){ struct wrap_ndis_device *wnd = netdev_priv(dev); set_bit(SET_MULTICAST_LIST, &wnd->wrap_ndis_pending_work); schedule_wrap_work(&wnd->wrap_ndis_work);}/* called from BH context */struct iw_statistics *get_wireless_stats(struct net_device *dev){ struct wrap_ndis_device *wnd = netdev_priv(dev); return &wnd->wireless_stats;}#if defined(HAVE_ETHTOOL)static void ndis_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info){ struct wrap_ndis_device *wnd = netdev_priv(dev); strncpy(info->driver, DRIVER_NAME, sizeof(info->driver) - 1); strncpy(info->version, DRIVER_VERSION, sizeof(info->version) - 1); strncpy(info->fw_version, wnd->wd->driver->version, sizeof(info->fw_version) - 1); if (wrap_is_pci_bus(wnd->wd->dev_bus)) strncpy(info->bus_info, pci_name(wnd->wd->pci.pdev), sizeof(info->bus_info) - 1);#ifdef CONFIG_USB else usb_make_path(wnd->wd->usb.udev, info->bus_info, sizeof(info->bus_info) - 1);#endif return;}static u32 ndis_get_link(struct net_device *dev){ struct wrap_ndis_device *wnd = netdev_priv(dev); return netif_carrier_ok(wnd->net_dev);}static void ndis_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol){ struct wrap_ndis_device *wnd = netdev_priv(dev); if (wnd->ndis_wolopts & NDIS_PNP_WAKE_UP_MAGIC_PACKET) wol->wolopts |= WAKE_MAGIC; /* no other options supported */ return;}static int ndis_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol){ struct wrap_ndis_device *wnd = netdev_priv(dev); struct ndis_pnp_capabilities pnp_capa; NDIS_STATUS status; if (!(wol->wolopts & WAKE_MAGIC)) return -EINVAL; if (!wnd->pm_capa) return -EOPNOTSUPP; status = miniport_query_info(wnd, OID_PNP_CAPABILITIES, &pnp_capa, sizeof(pnp_capa)); if (status != NDIS_STATUS_SUCCESS) return -EOPNOTSUPP; /* we always suspend to D3 */ DBGTRACE1("%d, %d", pnp_capa.wakeup_capa.min_magic_packet_wakeup, pnp_capa.wakeup_capa.min_pattern_wakeup); if (pnp_capa.wakeup_capa.min_magic_packet_wakeup != NdisDeviceStateD3) return -EOPNOTSUPP; /* no other options supported */ wnd->ndis_wolopts = NDIS_PNP_WAKE_UP_MAGIC_PACKET; return 0;}static struct ethtool_ops ndis_ethtool_ops = { .get_drvinfo = ndis_get_drvinfo, .get_link = ndis_get_link, .get_wol = ndis_get_wol, .set_wol = ndis_set_wol,};#endifstatic int notifier_event(struct notifier_block *notifier, unsigned long event, void *ptr){ struct net_device *net_dev = (struct net_device *)ptr; struct wrap_ndis_device *wnd = netdev_priv(net_dev); /* called with rtnl lock held, so no need to lock */ switch (event) { case NETDEV_CHANGENAME: wrap_procfs_remove_ndis_device(wnd); printk(KERN_INFO "%s: changing interface name from '%s' to " "'%s'\n", DRIVER_NAME, wnd->netdev_name, net_dev->name); memcpy(wnd->netdev_name, net_dev->name, sizeof(wnd->netdev_name)); wrap_procfs_add_ndis_device(wnd); break; } return NOTIFY_DONE;}static struct notifier_block netdev_notifier = { .notifier_call = notifier_event,};static void update_wireless_stats(struct wrap_ndis_device *wnd){ struct iw_statistics *iw_stats = &wnd->wireless_stats; struct ndis_wireless_stats ndis_stats; NDIS_STATUS res; ndis_rssi rssi; unsigned long frag; TRACEENTER2("%p", wnd); if (wnd->stats_enabled == FALSE || !netif_carrier_ok(wnd->net_dev)) { memset(iw_stats, 0, sizeof(*iw_stats)); TRACEEXIT2(return); } res = miniport_query_info(wnd, OID_802_11_RSSI, &rssi, sizeof(rssi)); if (res == NDIS_STATUS_SUCCESS) iw_stats->qual.level = rssi; memset(&ndis_stats, 0, sizeof(ndis_stats)); res = miniport_query_info(wnd, OID_802_11_STATISTICS, &ndis_stats, sizeof(ndis_stats)); if (res != NDIS_STATUS_SUCCESS) TRACEEXIT2(return); iw_stats->discard.retries = (unsigned long)ndis_stats.retry + (unsigned long)ndis_stats.multi_retry; iw_stats->discard.misc = (unsigned long)ndis_stats.fcs_err + (unsigned long)ndis_stats.rtss_fail + (unsigned long)ndis_stats.ack_fail + (unsigned long)ndis_stats.frame_dup; frag = 6 * (unsigned long)ndis_stats.tx_frag; if (frag) iw_stats->qual.qual = 100 - 100 * (((unsigned long)ndis_stats.retry + 2*(unsigned long)ndis_stats.multi_retry + 3*(unsigned long)ndis_stats.failed) / frag); else iw_stats->qual.qual = 100; TRACEEXIT2(return);}static void set_multicast_list(struct wrap_ndis_device *wnd){ struct net_device *net_dev; ULONG packet_filter; NDIS_STATUS res; net_dev = wnd->net_dev; packet_filter = wnd->packet_filter; DBGTRACE2("0x%08x", packet_filter); if (net_dev->flags & IFF_PROMISC) { packet_filter |= NDIS_PACKET_TYPE_PROMISCUOUS | NDIS_PACKET_TYPE_ALL_LOCAL; } else if (net_dev->flags & IFF_ALLMULTI || net_dev->mc_count > wnd->multicast_size) { packet_filter |= NDIS_PACKET_TYPE_ALL_MULTICAST; DBGTRACE2("0x%08x", packet_filter); } else if (net_dev->mc_count > 0) { int i, size; char *buf; struct dev_mc_list *mclist; size = min(wnd->multicast_size, net_dev->mc_count); DBGTRACE2("%d, %d", wnd->multicast_size, net_dev->mc_count); buf = kmalloc(size * ETH_ALEN, GFP_KERNEL); if (!buf) { WARNING("couldn't allocate memory"); TRACEEXIT2(return); } mclist = net_dev->mc_list; for (i = 0; i < size && mclist; mclist = mclist->next) { if (mclist->dmi_addrlen != ETH_ALEN) continue; memcpy(buf + i * ETH_ALEN, mclist->dmi_addr, ETH_ALEN); DBGTRACE2(MACSTRSEP, MAC2STR(mclist->dmi_addr));
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -