Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3b640f21 authored by Ivo van Doorn's avatar Ivo van Doorn Committed by John W. Linville
Browse files

rt2x00: Enable LED class support for rt2500usb/rt73usb



Add kerneldoc for vendor request functions in rt2x00usb.
Add asynchroneous vendor request function in rt2x00usb.

With the availability of the asynchroneuous vendor request
we can now enable LED class support for rt2500usb and rt73usb.
Since LED handling is not important, it doesn't really matter
if a register call fails (This solution is better then no
LED class support at all).

Signed-off-by: default avatarIvo van Doorn <IvDoorn@gmail.com>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent a9450b70
Loading
Loading
Loading
Loading
+14 −0
Original line number Diff line number Diff line
@@ -133,6 +133,13 @@ config RT2500USB

	  When compiled as a module, this driver will be called "rt2500usb.ko".

config RT2500USB_LEDS
	bool "RT2500 leds support"
	depends on RT2500USB
	select RT2X00_LIB_LEDS
	---help---
	  This adds support for led triggers provided my mac80211.

config RT73USB
	tristate "Ralink rt73 usb support"
	depends on RT2X00 && USB
@@ -143,6 +150,13 @@ config RT73USB

	  When compiled as a module, this driver will be called "rt73usb.ko".

config RT73USB_LEDS
	bool "RT73 leds support"
	depends on RT73USB
	select RT2X00_LIB_LEDS
	---help---
	  This adds support for led triggers provided my mac80211.

config RT2X00_LIB_DEBUGFS
	bool "Ralink debugfs support"
	depends on RT2X00_LIB && MAC80211_DEBUGFS
+13 −7
Original line number Diff line number Diff line
@@ -291,17 +291,16 @@ static void rt2500usb_led_brightness(struct led_classdev *led_cdev,
	unsigned int enabled = brightness != LED_OFF;
	unsigned int activity =
	    led->rt2x00dev->led_flags & LED_SUPPORT_ACTIVITY;
	u16 reg;

	rt2500usb_register_read(led->rt2x00dev, MAC_CSR20, &reg);

	if (led->type == LED_TYPE_RADIO || led->type == LED_TYPE_ASSOC) {
		rt2x00_set_field16(&reg, MAC_CSR20_LINK, enabled);
		rt2x00_set_field16(&reg, MAC_CSR20_ACTIVITY,
				   enabled && activity);
		rt2x00_set_field16(&led->rt2x00dev->led_mcu_reg,
				   MAC_CSR20_LINK, enabled);
		rt2x00_set_field16(&led->rt2x00dev->led_mcu_reg,
				   MAC_CSR20_ACTIVITY, enabled && activity);
	}

	rt2500usb_register_write(led->rt2x00dev, MAC_CSR20, reg);
	rt2x00usb_vendor_request_async(led->rt2x00dev, USB_SINGLE_WRITE,
				       MAC_CSR20, led->rt2x00dev->led_mcu_reg);
}
#else
#define rt2500usb_led_brightness	NULL
@@ -1377,6 +1376,13 @@ static int rt2500usb_init_eeprom(struct rt2x00_dev *rt2x00dev)
		rt2x00dev->led_flags = LED_SUPPORT_RADIO;
		break;
	}

	/*
	 * Store the current led register value, we need it later
	 * in set_brightness but that is called in irq context which
	 * means we can't use rt2500usb_register_read() at that time.
	 */
	rt2500usb_register_read(rt2x00dev, MAC_CSR20, &rt2x00dev->led_mcu_reg);
#endif /* CONFIG_RT2500USB_LEDS */

	/*
+217 −0
Original line number Diff line number Diff line
/*
	Copyright (C) 2004 - 2008 rt2x00 SourceForge Project
	<http://rt2x00.serialmonkey.com>

	This program is free software; you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation; either version 2 of the License, or
	(at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with this program; if not, write to the
	Free Software Foundation, Inc.,
	59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

/*
	Module: rt2x00lib
	Abstract: rt2x00 led specific routines.
 */

#include <linux/kernel.h>
#include <linux/module.h>

#include "rt2x00.h"
#include "rt2x00lib.h"

void rt2x00leds_led_quality(struct rt2x00_dev *rt2x00dev, int rssi)
{
	if (!rt2x00dev->trigger_qual.registered)
		return;

	/*
	 * Led handling requires a positive value for the rssi,
	 * to do that correctly we need to add the correction.
	 */
	rssi += rt2x00dev->rssi_offset;

	/*
	 * Get the rssi level, this is used to convert the rssi
	 * to a LED value inside the range LED_OFF - LED_FULL.
	 */
	if (rssi <= 30)
		rssi = 0;
	else if (rssi <= 39)
		rssi = 1;
	else if (rssi <= 49)
		rssi = 2;
	else if (rssi <= 53)
		rssi = 3;
	else if (rssi <= 63)
		rssi = 4;
	else
		rssi = 5;

	/*
	 * Note that we must _not_ send LED_OFF since the driver
	 * is going to calculate the value and might use it in a
	 * division.
	 */
	led_trigger_event(&rt2x00dev->trigger_qual.trigger,
			  ((LED_FULL / 6) * rssi) + 1);
}

static int rt2x00leds_register_trigger(struct rt2x00_dev *rt2x00dev,
				       struct rt2x00_trigger *trigger,
				       const char *name)
{
	int retval;

	trigger->trigger.name = name;
	retval = led_trigger_register(&trigger->trigger);
	if (retval) {
		ERROR(rt2x00dev, "Failed to register led trigger.\n");
		return retval;
	}

	trigger->registered = 1;

	return 0;
}

static int rt2x00leds_register_led(struct rt2x00_dev *rt2x00dev,
				   struct rt2x00_led *led,
				   enum led_type type,
				   const char *name, char *trigger)
{
	struct device *device = wiphy_dev(rt2x00dev->hw->wiphy);
	int retval;

	led->led_dev.name = name;
	led->led_dev.brightness_set = rt2x00dev->ops->lib->led_brightness;
	led->led_dev.default_trigger = trigger;

	retval = led_classdev_register(device, &led->led_dev);
	if (retval) {
		ERROR(rt2x00dev, "Failed to register led handler.\n");
		return retval;
	}

	led->rt2x00dev = rt2x00dev;
	led->type = type;
	led->registered = 1;

	return 0;
}

int rt2x00leds_register(struct rt2x00_dev *rt2x00dev)
{
	char *trigger;
	char dev_name[16];
	char name[32];
	int retval;

	if (!rt2x00dev->ops->lib->led_brightness)
		return 0;

	snprintf(dev_name, sizeof(dev_name), "%s-%s",
		 rt2x00dev->ops->name, wiphy_name(rt2x00dev->hw->wiphy));

	if (rt2x00dev->led_flags & LED_SUPPORT_RADIO) {
		trigger = ieee80211_get_radio_led_name(rt2x00dev->hw);
		snprintf(name, sizeof(name), "%s:radio", dev_name);

		retval = rt2x00leds_register_led(rt2x00dev,
						 &rt2x00dev->led_radio,
						 LED_TYPE_RADIO,
						 name, trigger);
		if (retval)
			goto exit_fail;
	}

	if (rt2x00dev->led_flags & LED_SUPPORT_ASSOC) {
		trigger = ieee80211_get_assoc_led_name(rt2x00dev->hw);
		snprintf(name, sizeof(name), "%s:assoc", dev_name);

		retval = rt2x00leds_register_led(rt2x00dev,
						 &rt2x00dev->led_assoc,
						 LED_TYPE_ASSOC,
						 name, trigger);
		if (retval)
			goto exit_fail;
	}

	if (rt2x00dev->led_flags & LED_SUPPORT_QUALITY) {
		snprintf(name, sizeof(name), "%s:quality", dev_name);

		retval = rt2x00leds_register_trigger(rt2x00dev,
						     &rt2x00dev->trigger_qual,
						     name);

		retval = rt2x00leds_register_led(rt2x00dev,
						 &rt2x00dev->led_qual,
						 LED_TYPE_QUALITY,
						 name, name);
		if (retval)
			goto exit_fail;
	}

	return 0;

exit_fail:
	rt2x00leds_unregister(rt2x00dev);
	return retval;
}

static void rt2x00leds_unregister_trigger(struct rt2x00_trigger *trigger)
{
	if (!trigger->registered)
		return;

	led_trigger_unregister(&trigger->trigger);
	trigger->registered = 0;
}

static void rt2x00leds_unregister_led(struct rt2x00_led *led)
{
	if (!led->registered)
		return;

	led_classdev_unregister(&led->led_dev);

	led->led_dev.brightness_set(&led->led_dev, LED_OFF);
	led->registered = 0;
}

void rt2x00leds_unregister(struct rt2x00_dev *rt2x00dev)
{
	rt2x00leds_unregister_trigger(&rt2x00dev->trigger_qual);
	rt2x00leds_unregister_led(&rt2x00dev->led_qual);
	rt2x00leds_unregister_led(&rt2x00dev->led_assoc);
	rt2x00leds_unregister_led(&rt2x00dev->led_radio);
}

void rt2x00leds_suspend(struct rt2x00_dev *rt2x00dev)
{
	if (rt2x00dev->led_qual.registered)
		led_classdev_suspend(&rt2x00dev->led_qual.led_dev);
	if (rt2x00dev->led_assoc.registered)
		led_classdev_suspend(&rt2x00dev->led_assoc.led_dev);
	if (rt2x00dev->led_radio.registered)
		led_classdev_suspend(&rt2x00dev->led_radio.led_dev);
}

void rt2x00leds_resume(struct rt2x00_dev *rt2x00dev)
{
	if (rt2x00dev->led_radio.registered)
		led_classdev_resume(&rt2x00dev->led_radio.led_dev);
	if (rt2x00dev->led_assoc.registered)
		led_classdev_resume(&rt2x00dev->led_assoc.led_dev);
	if (rt2x00dev->led_qual.registered)
		led_classdev_resume(&rt2x00dev->led_qual.led_dev);
}
+63 −0
Original line number Diff line number Diff line
/*
	Copyright (C) 2004 - 2008 rt2x00 SourceForge Project
	<http://rt2x00.serialmonkey.com>

	This program is free software; you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation; either version 2 of the License, or
	(at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with this program; if not, write to the
	Free Software Foundation, Inc.,
	59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

/*
	Module: rt2x00lib
	Abstract: rt2x00 led datastructures and routines
 */

#ifndef RT2X00LEDS_H
#define RT2X00LEDS_H

/*
* Flags used by driver to indicate which
 * which led types are supported.
 */
#define LED_SUPPORT_RADIO	0x000001
#define LED_SUPPORT_ASSOC	0x000002
#define LED_SUPPORT_ACTIVITY	0x000004
#define LED_SUPPORT_QUALITY	0x000008

enum led_type {
	LED_TYPE_RADIO,
	LED_TYPE_ASSOC,
	LED_TYPE_QUALITY,
};

#ifdef CONFIG_RT2X00_LIB_LEDS

struct rt2x00_led {
	struct rt2x00_dev *rt2x00dev;
	struct led_classdev led_dev;

	enum led_type type;
	unsigned int registered;
};

struct rt2x00_trigger {
	struct led_trigger trigger;

	enum led_type type;
	unsigned int registered;
};

#endif /* CONFIG_RT2X00_LIB_LEDS */

#endif /* RT2X00LEDS_H */
+52 −0
Original line number Diff line number Diff line
@@ -122,6 +122,58 @@ int rt2x00usb_vendor_request_buff(struct rt2x00_dev *rt2x00dev,
}
EXPORT_SYMBOL_GPL(rt2x00usb_vendor_request_buff);

static void rt2x00usb_vendor_request_async_complete(struct urb *urb)
{
	/*
	 * We're done with it, descrease usage count and let the
	 * usb layer delete it as soon as it is done with it.
	 */
	usb_put_urb(urb);
}

int rt2x00usb_vendor_request_async(struct rt2x00_dev *rt2x00dev,
				   const u8 request, const u16 offset,
				   const u16 value)
{
	struct usb_device *usb_dev = rt2x00dev_usb_dev(rt2x00dev);
	struct usb_ctrlrequest *ctrl;
	struct urb *urb;
	int status;

	urb = usb_alloc_urb(0, GFP_NOIO);
	if (!urb)
		return -ENOMEM;

	ctrl = kmalloc(sizeof(struct usb_ctrlrequest), GFP_NOIO);
	if (!ctrl) {
		status = -ENOMEM;
		goto exit;
	}

	ctrl->bRequestType= USB_VENDOR_REQUEST_OUT;
	ctrl->bRequest = request;
	ctrl->wValue = cpu_to_le16p(&value);
	ctrl->wIndex = cpu_to_le16p(&offset);
	ctrl->wLength = 0;

	usb_fill_control_urb(urb, usb_dev, usb_sndctrlpipe(usb_dev, 0),
			     (unsigned char *)ctrl, NULL, 0,
			     rt2x00usb_vendor_request_async_complete, NULL);

	status = usb_submit_urb(urb, GFP_ATOMIC);
	if (!status)
		goto exit;

	return 0;

exit:
	usb_put_urb(urb);
	kfree(ctrl);

	return status;
}
EXPORT_SYMBOL_GPL(rt2x00usb_vendor_request_async);

/*
 * TX data handlers.
 */
Loading