Skip to content
Snippets Groups Projects
rndis_wlan.c 92.2 KiB
Newer Older
	if (modparam_workaround_interval < 0)
		priv->param_workaround_interval = 500;
	else
		priv->param_workaround_interval = modparam_workaround_interval;
}

static int bcm4320a_early_init(struct usbnet *usbdev)
{
	/* copy module parameters for bcm4320a so that iwconfig reports txpower
	 * and workaround parameter is copied to private structure correctly.
	 */
	rndis_copy_module_params(usbdev);

	/* bcm4320a doesn't handle configuration parameters well. Try
	 * set any and you get partially zeroed mac and broken device.
	 */

	return 0;
}

static int bcm4320b_early_init(struct usbnet *usbdev)
{
	struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev);
	char buf[8];

	rndis_copy_module_params(usbdev);

	/* Early initialization settings, setting these won't have effect
	 * if called after generic_rndis_bind().
	 */
	rndis_set_config_parameter_str(usbdev, "Country", priv->param_country);
	rndis_set_config_parameter_str(usbdev, "FrameBursting",
					priv->param_frameburst ? "1" : "0");
	rndis_set_config_parameter_str(usbdev, "Afterburner",
					priv->param_afterburner ? "1" : "0");
	sprintf(buf, "%d", priv->param_power_save);
	rndis_set_config_parameter_str(usbdev, "PowerSaveMode", buf);
	sprintf(buf, "%d", priv->param_power_output);
	rndis_set_config_parameter_str(usbdev, "PwrOut", buf);
	sprintf(buf, "%d", priv->param_roamtrigger);
	rndis_set_config_parameter_str(usbdev, "RoamTrigger", buf);
	sprintf(buf, "%d", priv->param_roamdelta);
	rndis_set_config_parameter_str(usbdev, "RoamDelta", buf);
/* same as rndis_netdev_ops but with local multicast handler */
static const struct net_device_ops rndis_wlan_netdev_ops = {
	.ndo_open		= usbnet_open,
	.ndo_stop		= usbnet_stop,
	.ndo_start_xmit		= usbnet_start_xmit,
	.ndo_tx_timeout		= usbnet_tx_timeout,
	.ndo_set_mac_address 	= eth_mac_addr,
	.ndo_validate_addr	= eth_validate_addr,
	.ndo_set_multicast_list	= rndis_wlan_set_multicast_list,
static int rndis_wlan_bind(struct usbnet *usbdev, struct usb_interface *intf)
	struct wiphy *wiphy;
	/* allocate wiphy and rndis private data
	 * NOTE: We only support a single virtual interface, so wiphy
	 * and wireless_dev are somewhat synonymous for this device.
	 */
	wiphy = wiphy_new(&rndis_config_ops, sizeof(struct rndis_wlan_private));
	priv = wiphy_priv(wiphy);
	usbdev->net->ieee80211_ptr = &priv->wdev;
	priv->wdev.wiphy = wiphy;
	priv->wdev.iftype = NL80211_IFTYPE_STATION;

	/* These have to be initialized before calling generic_rndis_bind().
	 * Otherwise we'll be in big trouble in rndis_wlan_early_init().
	usbdev->driver_priv = priv;
	priv->usbdev = usbdev;
	/* because rndis_command() sleeps we need to use workqueue */
	priv->workqueue = create_singlethread_workqueue("rndis_wlan");
	INIT_WORK(&priv->work, rndis_wlan_worker);
	INIT_DELAYED_WORK(&priv->dev_poller_work, rndis_device_poller);
	INIT_DELAYED_WORK(&priv->scan_work, rndis_get_scan_results);

	/* try bind rndis_host */
	retval = generic_rndis_bind(usbdev, intf, FLAG_RNDIS_PHYM_WIRELESS);
	if (retval < 0)
		goto fail;

	/* generic_rndis_bind set packet filter to multicast_all+
	 * promisc mode which doesn't work well for our devices (device
	 * picks up rssi to closest station instead of to access point).
	 *
	 * rndis_host wants to avoid all OID as much as possible
	 * so do promisc/multicast handling in rndis_wlan.
	usbdev->net->netdev_ops = &rndis_wlan_netdev_ops;
	tmp = RNDIS_PACKET_TYPE_DIRECTED | RNDIS_PACKET_TYPE_BROADCAST;
	retval = rndis_set_oid(usbdev, OID_GEN_CURRENT_PACKET_FILTER, &tmp,
	retval = rndis_query_oid(usbdev, OID_802_3_MAXIMUM_LIST_SIZE, &tmp,
								&len);
	priv->multicast_size = le32_to_cpu(tmp);
	if (retval < 0 || priv->multicast_size < 0)
		priv->multicast_size = 0;
	if (priv->multicast_size > 0)
		usbdev->net->flags |= IFF_MULTICAST;
		usbdev->net->flags &= ~IFF_MULTICAST;
	/* fill-out wiphy structure and register w/ cfg80211 */
	memcpy(wiphy->perm_addr, usbdev->net->dev_addr, ETH_ALEN);
	wiphy->privid = rndis_wiphy_privid;
	wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION)
					| BIT(NL80211_IFTYPE_ADHOC);
	wiphy->max_scan_ssids = 1;

	/* TODO: fill-out band/encr information based on priv->caps */
	rndis_wlan_get_caps(usbdev, wiphy);

	memcpy(priv->channels, rndis_channels, sizeof(rndis_channels));
	memcpy(priv->rates, rndis_rates, sizeof(rndis_rates));
	priv->band.channels = priv->channels;
	priv->band.n_channels = ARRAY_SIZE(rndis_channels);
	priv->band.bitrates = priv->rates;
	priv->band.n_bitrates = ARRAY_SIZE(rndis_rates);
	wiphy->bands[IEEE80211_BAND_2GHZ] = &priv->band;
	wiphy->signal_type = CFG80211_SIGNAL_TYPE_UNSPEC;
	memcpy(priv->cipher_suites, rndis_cipher_suites,
						sizeof(rndis_cipher_suites));
	wiphy->cipher_suites = priv->cipher_suites;
	wiphy->n_cipher_suites = ARRAY_SIZE(rndis_cipher_suites);

	set_wiphy_dev(wiphy, &usbdev->udev->dev);

	if (wiphy_register(wiphy)) {
	set_default_iw_params(usbdev);
	/* set default rts/frag */
	rndis_set_wiphy_params(wiphy,
			WIPHY_PARAM_FRAG_THRESHOLD | WIPHY_PARAM_RTS_THRESHOLD);

	priv->radio_on = true;
	disassociate(usbdev, true);
	netif_carrier_off(usbdev->net);
	cancel_delayed_work_sync(&priv->dev_poller_work);
	cancel_delayed_work_sync(&priv->scan_work);
	cancel_work_sync(&priv->work);
	flush_workqueue(priv->workqueue);
	destroy_workqueue(priv->workqueue);

static void rndis_wlan_unbind(struct usbnet *usbdev, struct usb_interface *intf)
	struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev);
	disassociate(usbdev, false);
	cancel_delayed_work_sync(&priv->dev_poller_work);
	cancel_delayed_work_sync(&priv->scan_work);
	cancel_work_sync(&priv->work);
	flush_workqueue(priv->workqueue);
	destroy_workqueue(priv->workqueue);

	rndis_unbind(usbdev, intf);

	wiphy_unregister(priv->wdev.wiphy);
	wiphy_free(priv->wdev.wiphy);
static int rndis_wlan_reset(struct usbnet *usbdev)
	struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev);
	netdev_dbg(usbdev->net, "%s()\n", __func__);
		netdev_warn(usbdev->net, "rndis_reset failed: %d\n", retval);
	/* rndis_reset cleared multicast list, so restore here.
	   (set_multicast_list() also turns on current packet filter) */
	queue_delayed_work(priv->workqueue, &priv->dev_poller_work,
		round_jiffies_relative(DEVICE_POLLER_JIFFIES));
	return deauthenticate(usbdev);
static int rndis_wlan_stop(struct usbnet *usbdev)
{
	struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev);
	int retval;
	netdev_dbg(usbdev->net, "%s()\n", __func__);
	retval = disassociate(usbdev, false);
	cancel_delayed_work_sync(&priv->dev_poller_work);
	cancel_delayed_work_sync(&priv->scan_work);
	cancel_work_sync(&priv->work);
	flush_workqueue(priv->workqueue);

	if (priv->scan_request) {
		cfg80211_scan_done(priv->scan_request, true);
		priv->scan_request = NULL;
	}

	/* Set current packet filter zero to block receiving data packets from
	   device. */
	filter = 0;
	rndis_set_oid(usbdev, OID_GEN_CURRENT_PACKET_FILTER, &filter,
								sizeof(filter));

static const struct driver_info	bcm4320b_info = {
	.description =	"Wireless RNDIS device, BCM4320b based",
	.flags =	FLAG_WLAN | FLAG_FRAMING_RN | FLAG_NO_SETINT |
				FLAG_AVOID_UNLINK_URBS,
	.bind =		rndis_wlan_bind,
	.unbind =	rndis_wlan_unbind,
	.status =	rndis_status,
	.rx_fixup =	rndis_rx_fixup,
	.tx_fixup =	rndis_tx_fixup,
	.stop =		rndis_wlan_stop,
	.early_init =	bcm4320b_early_init,
	.indication =	rndis_wlan_indication,
};

static const struct driver_info	bcm4320a_info = {
	.description =	"Wireless RNDIS device, BCM4320a based",
	.flags =	FLAG_WLAN | FLAG_FRAMING_RN | FLAG_NO_SETINT |
				FLAG_AVOID_UNLINK_URBS,
	.bind =		rndis_wlan_bind,
	.unbind =	rndis_wlan_unbind,
	.status =	rndis_status,
	.rx_fixup =	rndis_rx_fixup,
	.tx_fixup =	rndis_tx_fixup,
	.stop =		rndis_wlan_stop,
	.early_init =	bcm4320a_early_init,
	.indication =	rndis_wlan_indication,
static const struct driver_info rndis_wlan_info = {
	.description =	"Wireless RNDIS device",
	.flags =	FLAG_WLAN | FLAG_FRAMING_RN | FLAG_NO_SETINT |
				FLAG_AVOID_UNLINK_URBS,
	.bind =		rndis_wlan_bind,
	.unbind =	rndis_wlan_unbind,
	.status =	rndis_status,
	.rx_fixup =	rndis_rx_fixup,
	.tx_fixup =	rndis_tx_fixup,
	.stop =		rndis_wlan_stop,
	.early_init =	bcm4320a_early_init,
	.indication =	rndis_wlan_indication,
};

/*-------------------------------------------------------------------------*/

static const struct usb_device_id products [] = {
#define	RNDIS_MASTER_INTERFACE \
	.bInterfaceClass	= USB_CLASS_COMM, \
	.bInterfaceSubClass	= 2 /* ACM */, \
	.bInterfaceProtocol	= 0x0ff

/* INF driver for these devices have DriverVer >= 4.xx.xx.xx and many custom
 * parameters available. Chipset marked as 'BCM4320SKFBG' in NDISwrapper-wiki.
 */
{
	.match_flags	=   USB_DEVICE_ID_MATCH_INT_INFO
			  | USB_DEVICE_ID_MATCH_DEVICE,
	.idVendor		= 0x0411,
	.idProduct		= 0x00bc,	/* Buffalo WLI-U2-KG125S */
	RNDIS_MASTER_INTERFACE,
	.driver_info		= (unsigned long) &bcm4320b_info,
}, {
	.match_flags	=   USB_DEVICE_ID_MATCH_INT_INFO
			  | USB_DEVICE_ID_MATCH_DEVICE,
	.idVendor		= 0x0baf,
	.idProduct		= 0x011b,	/* U.S. Robotics USR5421 */
	RNDIS_MASTER_INTERFACE,
	.driver_info		= (unsigned long) &bcm4320b_info,
}, {
	.match_flags	=   USB_DEVICE_ID_MATCH_INT_INFO
			  | USB_DEVICE_ID_MATCH_DEVICE,
	.idVendor		= 0x050d,
	.idProduct		= 0x011b,	/* Belkin F5D7051 */
	RNDIS_MASTER_INTERFACE,
	.driver_info		= (unsigned long) &bcm4320b_info,
}, {
	.match_flags	=   USB_DEVICE_ID_MATCH_INT_INFO
			  | USB_DEVICE_ID_MATCH_DEVICE,
	.idVendor		= 0x1799,	/* Belkin has two vendor ids */
	.idProduct		= 0x011b,	/* Belkin F5D7051 */
	RNDIS_MASTER_INTERFACE,
	.driver_info		= (unsigned long) &bcm4320b_info,
}, {
	.match_flags	=   USB_DEVICE_ID_MATCH_INT_INFO
			  | USB_DEVICE_ID_MATCH_DEVICE,
	.idVendor		= 0x13b1,
	.idProduct		= 0x0014,	/* Linksys WUSB54GSv2 */
	RNDIS_MASTER_INTERFACE,
	.driver_info		= (unsigned long) &bcm4320b_info,
}, {
	.match_flags	=   USB_DEVICE_ID_MATCH_INT_INFO
			  | USB_DEVICE_ID_MATCH_DEVICE,
	.idVendor		= 0x13b1,
	.idProduct		= 0x0026,	/* Linksys WUSB54GSC */
	RNDIS_MASTER_INTERFACE,
	.driver_info		= (unsigned long) &bcm4320b_info,
}, {
	.match_flags	=   USB_DEVICE_ID_MATCH_INT_INFO
			  | USB_DEVICE_ID_MATCH_DEVICE,
	.idVendor		= 0x0b05,
	.idProduct		= 0x1717,	/* Asus WL169gE */
	RNDIS_MASTER_INTERFACE,
	.driver_info		= (unsigned long) &bcm4320b_info,
}, {
	.match_flags	=   USB_DEVICE_ID_MATCH_INT_INFO
			  | USB_DEVICE_ID_MATCH_DEVICE,
	.idVendor		= 0x0a5c,
	.idProduct		= 0xd11b,	/* Eminent EM4045 */
	RNDIS_MASTER_INTERFACE,
	.driver_info		= (unsigned long) &bcm4320b_info,
}, {
	.match_flags	=   USB_DEVICE_ID_MATCH_INT_INFO
			  | USB_DEVICE_ID_MATCH_DEVICE,
	.idVendor		= 0x1690,
	.idProduct		= 0x0715,	/* BT Voyager 1055 */
	RNDIS_MASTER_INTERFACE,
	.driver_info		= (unsigned long) &bcm4320b_info,
},
/* These devices have DriverVer < 4.xx.xx.xx and do not have any custom
 * parameters available, hardware probably contain older firmware version with
 * no way of updating. Chipset marked as 'BCM4320????' in NDISwrapper-wiki.
 */
{
	.match_flags	=   USB_DEVICE_ID_MATCH_INT_INFO
			  | USB_DEVICE_ID_MATCH_DEVICE,
	.idVendor		= 0x13b1,
	.idProduct		= 0x000e,	/* Linksys WUSB54GSv1 */
	RNDIS_MASTER_INTERFACE,
	.driver_info		= (unsigned long) &bcm4320a_info,
}, {
	.match_flags	=   USB_DEVICE_ID_MATCH_INT_INFO
			  | USB_DEVICE_ID_MATCH_DEVICE,
	.idVendor		= 0x0baf,
	.idProduct		= 0x0111,	/* U.S. Robotics USR5420 */
	RNDIS_MASTER_INTERFACE,
	.driver_info		= (unsigned long) &bcm4320a_info,
}, {
	.match_flags	=   USB_DEVICE_ID_MATCH_INT_INFO
			  | USB_DEVICE_ID_MATCH_DEVICE,
	.idVendor		= 0x0411,
	.idProduct		= 0x004b,	/* BUFFALO WLI-USB-G54 */
	RNDIS_MASTER_INTERFACE,
	.driver_info		= (unsigned long) &bcm4320a_info,
},
/* Generic Wireless RNDIS devices that we don't have exact
 * idVendor/idProduct/chip yet.
 */
{
	/* RNDIS is MSFT's un-official variant of CDC ACM */
	USB_INTERFACE_INFO(USB_CLASS_COMM, 2 /* ACM */, 0x0ff),
	.driver_info = (unsigned long) &rndis_wlan_info,
}, {
	/* "ActiveSync" is an undocumented variant of RNDIS, used in WM5 */
	USB_INTERFACE_INFO(USB_CLASS_MISC, 1, 1),
	.driver_info = (unsigned long) &rndis_wlan_info,
},
	{ },		// END
};
MODULE_DEVICE_TABLE(usb, products);

static struct usb_driver rndis_wlan_driver = {
	.name =		"rndis_wlan",
	.id_table =	products,
	.probe =	usbnet_probe,
	.disconnect =	usbnet_disconnect,
	.suspend =	usbnet_suspend,
	.resume =	usbnet_resume,
};

static int __init rndis_wlan_init(void)
{
	return usb_register(&rndis_wlan_driver);
}
module_init(rndis_wlan_init);

static void __exit rndis_wlan_exit(void)
{
	usb_deregister(&rndis_wlan_driver);
}
module_exit(rndis_wlan_exit);

MODULE_AUTHOR("Bjorge Dijkstra");
MODULE_AUTHOR("Jussi Kivilinna");
MODULE_DESCRIPTION("Driver for RNDIS based USB Wireless adapters");
MODULE_LICENSE("GPL");