patch-2.4.10 linux/drivers/usb/CDCEther.c
Next file: linux/drivers/usb/CDCEther.h
Previous file: linux/drivers/telephony/phonedev.c
Back to the patch index
Back to the overall index
- Lines: 156
- Date:
Sun Sep 23 09:49:00 2001
- Orig file:
v2.4.9/linux/drivers/usb/CDCEther.c
- Orig date:
Mon Aug 27 12:41:45 2001
diff -u --recursive --new-file v2.4.9/linux/drivers/usb/CDCEther.c linux/drivers/usb/CDCEther.c
@@ -29,16 +29,27 @@
#include <linux/module.h>
#include "CDCEther.h"
-static const char *version = __FILE__ ": v0.98.4 4 July 2001 Brad Hards and another";
+static const char *version = __FILE__ ": v0.98.5 22 Sep 2001 Brad Hards and another";
-// We will attempt to probe anything that is in the
-// communication device class...
-// We will sort through them later.
+/* We need to be selective about what we try to match on, to avoiding loading for a CDC
+ * ACM (ISDN or PSTN) modem */
static struct usb_device_id CDCEther_ids[] = {
- { USB_DEVICE_INFO(2, 0, 0) },
- { }
+ { match_flags: (USB_DEVICE_ID_MATCH_INT_CLASS | USB_DEVICE_ID_MATCH_INT_SUBCLASS),
+ bInterfaceClass: USB_CLASS_COMM, bInterfaceSubClass: 6},
+ { } /* Terminating null entry */
};
+/*
+ * module parameter that provides an alternate upper limit on the
+ * number of multicast filters we use, with a default to use all
+ * the filters available to us. Note that the actual number used
+ * is the lesser of this parameter and the number returned in the
+ * descriptor for the particular device. See Table 41 of the CDC
+ * spec for more info on the descriptor limit.
+ */
+static int multicast_filter_limit = 32767;
+
+
//////////////////////////////////////////////////////////////////////////////
// Callback routines from USB device /////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
@@ -409,32 +420,83 @@
}
}
+static void CDC_SetEthernetPacketFilter (ether_dev_t *ether_dev)
+{
+ usb_control_msg(ether_dev->usb,
+ usb_sndctrlpipe(ether_dev->usb, 0),
+ SET_ETHERNET_PACKET_FILTER, /* request */
+ USB_TYPE_CLASS | USB_DIR_OUT | USB_RECIP_INTERFACE, /* request type */
+ cpu_to_le16(ether_dev->mode_flags), /* value */
+ cpu_to_le16((u16)ether_dev->comm_interface), /* index */
+ NULL,
+ 0, /* size */
+ HZ); /* timeout */
+}
+
+
static void CDCEther_set_multicast( struct net_device *net )
{
ether_dev_t *ether_dev = net->priv;
+ int i;
+ __u8 *buff;
+
// Tell the kernel to stop sending us frames while we get this
// all set up.
netif_stop_queue(net);
- // Do what we are told.
- if (net->flags & IFF_PROMISC) {
- // TODO - Turn on promiscuous mode
- info( "%s: Promiscuous mode enabled", net->name);
- } else if (net->flags & IFF_ALLMULTI){
- // TODO - Here we need to tell the device to block ALL multicast traffic.
- info("%s: set allmulti", net->name);
- } else if (net->mc_count > ether_dev->wNumberMCFilters) {
- // TODO - Here we need to set multicast filters, but
- // There are more than our limit... Hmm...
- info("%s: set too many MC filters", net->name);
- } else {
- // TODO - Here we are supposed to set SOME of the multicast filters.
- // I must learn how to do this...
- //info("%s: set Rx mode", net->name);
+ /* Note: do not reorder, GCC is clever about common statements. */
+ if (net->flags & IFF_PROMISC) {
+ /* Unconditionally log net taps. */
+ info( "%s: Promiscuous mode enabled", net->name);
+ ether_dev->mode_flags = MODE_FLAG_PROMISCUOUS |
+ MODE_FLAG_ALL_MULTICAST |
+ MODE_FLAG_DIRECTED |
+ MODE_FLAG_BROADCAST |
+ MODE_FLAG_MULTICAST;
+ } else if (net->mc_count > ether_dev->wNumberMCFilters) {
+ /* Too many to filter perfectly -- accept all multicasts. */
+ info("%s: set too many MC filters, using allmulti", net->name);
+ ether_dev->mode_flags = MODE_FLAG_ALL_MULTICAST |
+ MODE_FLAG_DIRECTED |
+ MODE_FLAG_BROADCAST |
+ MODE_FLAG_MULTICAST;
+ } else if (net->flags & IFF_ALLMULTI) {
+ /* Filter in software */
+ info("%s: using allmulti", net->name);
+ ether_dev->mode_flags = MODE_FLAG_ALL_MULTICAST |
+ MODE_FLAG_DIRECTED |
+ MODE_FLAG_BROADCAST |
+ MODE_FLAG_MULTICAST;
+ } else {
+ /* do multicast filtering in hardware */
+ struct dev_mc_list *mclist;
+ info("%s: set multicast filters", net->name);
+ ether_dev->mode_flags = MODE_FLAG_ALL_MULTICAST |
+ MODE_FLAG_DIRECTED |
+ MODE_FLAG_BROADCAST |
+ MODE_FLAG_MULTICAST;
+ buff = kmalloc(6 * net->mc_count, in_interrupt() ? GFP_ATOMIC : GFP_KERNEL);
+ for (i = 0, mclist = net->mc_list;
+ mclist && i < net->mc_count;
+ i++, mclist = mclist->next) {
+ memcpy(&mclist->dmi_addr, &buff[i * 6], 6);
+ }
+ usb_control_msg(ether_dev->usb,
+ usb_sndctrlpipe(ether_dev->usb, 0),
+ SET_ETHERNET_MULTICAST_FILTER, /* request */
+ USB_TYPE_CLASS | USB_DIR_OUT | USB_RECIP_INTERFACE, /* request type */
+ cpu_to_le16(net->mc_count), /* value */
+ cpu_to_le16((u16)ether_dev->comm_interface), /* index */
+ buff,
+ (6* net->mc_count), /* size */
+ HZ); /* timeout */
+ kfree(buff);
}
-
- // Tell the kernel to start giving frames to us again.
+
+ CDC_SetEthernetPacketFilter(ether_dev);
+
+ // Tell the kernel to start giving frames to us again.
netif_wake_queue(net);
}
@@ -545,6 +607,9 @@
ether_dev->bmEthernetStatistics = data[1] + (data[2] << 8) + (data[3] << 16) + (data[4] << 24);
ether_dev->wMaxSegmentSize = data[5] + (data[6] << 8);
ether_dev->wNumberMCFilters = (data[7] + (data[8] << 8)) & 0x00007FFF;
+ if (ether_dev->wNumberMCFilters > multicast_filter_limit) {
+ ether_dev->wNumberMCFilters = multicast_filter_limit;
+ }
ether_dev->bNumberPowerFilters = data[9];
// We've seen one of these now.
@@ -1271,6 +1336,10 @@
MODULE_AUTHOR("Brad Hards and another");
MODULE_DESCRIPTION("USB CDC Ethernet driver");
+MODULE_LICENSE("GPL");
+
+MODULE_PARM (multicast_filter_limit, "i");
+MODULE_PARM_DESC (multicast_filter_limit, "CDCEther maximum number of filtered multicast addresses");
MODULE_DEVICE_TABLE (usb, CDCEther_ids);
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)