| 
									
										
										
										
											2009-04-16 19:56:38 -05:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * Linux LED driver for RTL8187 | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Copyright 2009 Larry Finger <Larry.Finger@lwfinger.net> | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Based on the LED handling in the r8187 driver, which is: | 
					
						
							|  |  |  |  * Copyright (c) Realtek Semiconductor Corp. All rights reserved. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Thanks to Realtek for their support! | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This program is free software; you can redistribute it and/or modify | 
					
						
							|  |  |  |  * it under the terms of the GNU General Public License version 2 as | 
					
						
							|  |  |  |  * published by the Free Software Foundation. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #ifdef CONFIG_RTL8187_LEDS
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <net/mac80211.h>
 | 
					
						
							|  |  |  | #include <linux/usb.h>
 | 
					
						
							|  |  |  | #include <linux/eeprom_93cx6.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "rtl8187.h"
 | 
					
						
							| 
									
										
										
										
											2010-12-20 15:16:53 -05:00
										 |  |  | #include "leds.h"
 | 
					
						
							| 
									
										
										
										
											2009-04-16 19:56:38 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | static void led_turn_on(struct work_struct *work) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	/* As this routine does read/write operations on the hardware, it must
 | 
					
						
							|  |  |  | 	 * be run from a work queue. | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	u8 reg; | 
					
						
							|  |  |  | 	struct rtl8187_priv *priv = container_of(work, struct rtl8187_priv, | 
					
						
							|  |  |  | 				    led_on.work); | 
					
						
							|  |  |  | 	struct rtl8187_led *led = &priv->led_tx; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Don't change the LED, when the device is down. */ | 
					
						
							| 
									
										
										
										
											2009-12-22 18:13:05 -05:00
										 |  |  | 	if (!priv->vif || priv->vif->type == NL80211_IFTYPE_UNSPECIFIED) | 
					
						
							| 
									
										
										
										
											2009-04-16 19:56:38 -05:00
										 |  |  | 		return ; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Skip if the LED is not registered. */ | 
					
						
							|  |  |  | 	if (!led->dev) | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 	mutex_lock(&priv->conf_mutex); | 
					
						
							|  |  |  | 	switch (led->ledpin) { | 
					
						
							|  |  |  | 	case LED_PIN_GPIO0: | 
					
						
							| 
									
										
										
										
											2009-08-26 13:54:09 -03:00
										 |  |  | 		rtl818x_iowrite8(priv, &priv->map->GPIO0, 0x01); | 
					
						
							| 
									
										
										
										
											2009-04-16 19:56:38 -05:00
										 |  |  | 		rtl818x_iowrite8(priv, &priv->map->GP_ENABLE, 0x00); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	case LED_PIN_LED0: | 
					
						
							|  |  |  | 		reg = rtl818x_ioread8(priv, &priv->map->PGSELECT) & ~(1 << 4); | 
					
						
							|  |  |  | 		rtl818x_iowrite8(priv, &priv->map->PGSELECT, reg); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	case LED_PIN_LED1: | 
					
						
							|  |  |  | 		reg = rtl818x_ioread8(priv, &priv->map->PGSELECT) & ~(1 << 5); | 
					
						
							|  |  |  | 		rtl818x_iowrite8(priv, &priv->map->PGSELECT, reg); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	case LED_PIN_HW: | 
					
						
							|  |  |  | 	default: | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	mutex_unlock(&priv->conf_mutex); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void led_turn_off(struct work_struct *work) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	/* As this routine does read/write operations on the hardware, it must
 | 
					
						
							|  |  |  | 	 * be run from a work queue. | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	u8 reg; | 
					
						
							|  |  |  | 	struct rtl8187_priv *priv = container_of(work, struct rtl8187_priv, | 
					
						
							|  |  |  | 				    led_off.work); | 
					
						
							|  |  |  | 	struct rtl8187_led *led = &priv->led_tx; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Don't change the LED, when the device is down. */ | 
					
						
							| 
									
										
										
										
											2009-12-22 18:13:05 -05:00
										 |  |  | 	if (!priv->vif || priv->vif->type == NL80211_IFTYPE_UNSPECIFIED) | 
					
						
							| 
									
										
										
										
											2009-04-16 19:56:38 -05:00
										 |  |  | 		return ; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Skip if the LED is not registered. */ | 
					
						
							|  |  |  | 	if (!led->dev) | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 	mutex_lock(&priv->conf_mutex); | 
					
						
							|  |  |  | 	switch (led->ledpin) { | 
					
						
							|  |  |  | 	case LED_PIN_GPIO0: | 
					
						
							| 
									
										
										
										
											2009-08-26 13:54:09 -03:00
										 |  |  | 		rtl818x_iowrite8(priv, &priv->map->GPIO0, 0x01); | 
					
						
							| 
									
										
										
										
											2009-04-16 19:56:38 -05:00
										 |  |  | 		rtl818x_iowrite8(priv, &priv->map->GP_ENABLE, 0x01); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	case LED_PIN_LED0: | 
					
						
							|  |  |  | 		reg = rtl818x_ioread8(priv, &priv->map->PGSELECT) | (1 << 4); | 
					
						
							|  |  |  | 		rtl818x_iowrite8(priv, &priv->map->PGSELECT, reg); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	case LED_PIN_LED1: | 
					
						
							|  |  |  | 		reg = rtl818x_ioread8(priv, &priv->map->PGSELECT) | (1 << 5); | 
					
						
							|  |  |  | 		rtl818x_iowrite8(priv, &priv->map->PGSELECT, reg); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	case LED_PIN_HW: | 
					
						
							|  |  |  | 	default: | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	mutex_unlock(&priv->conf_mutex); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* Callback from the LED subsystem. */ | 
					
						
							|  |  |  | static void rtl8187_led_brightness_set(struct led_classdev *led_dev, | 
					
						
							|  |  |  | 				   enum led_brightness brightness) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct rtl8187_led *led = container_of(led_dev, struct rtl8187_led, | 
					
						
							|  |  |  | 					       led_dev); | 
					
						
							|  |  |  | 	struct ieee80211_hw *hw = led->dev; | 
					
						
							| 
									
										
										
										
											2009-12-09 14:56:13 -02:00
										 |  |  | 	struct rtl8187_priv *priv; | 
					
						
							|  |  |  | 	static bool radio_on; | 
					
						
							| 
									
										
										
										
											2009-04-16 19:56:38 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-12-09 14:56:13 -02:00
										 |  |  | 	if (!hw) | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 	priv = hw->priv; | 
					
						
							|  |  |  | 	if (led->is_radio) { | 
					
						
							|  |  |  | 		if (brightness == LED_FULL) { | 
					
						
							|  |  |  | 			ieee80211_queue_delayed_work(hw, &priv->led_on, 0); | 
					
						
							|  |  |  | 			radio_on = true; | 
					
						
							|  |  |  | 		} else if (radio_on) { | 
					
						
							|  |  |  | 			radio_on = false; | 
					
						
							| 
									
										
										
										
											2012-05-16 11:06:21 +02:00
										 |  |  | 			cancel_delayed_work(&priv->led_on); | 
					
						
							| 
									
										
										
										
											2009-12-09 14:56:13 -02:00
										 |  |  | 			ieee80211_queue_delayed_work(hw, &priv->led_off, 0); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} else if (radio_on) { | 
					
						
							|  |  |  | 		if (brightness == LED_OFF) { | 
					
						
							|  |  |  | 			ieee80211_queue_delayed_work(hw, &priv->led_off, 0); | 
					
						
							|  |  |  | 			/* The LED is off for 1/20 sec - it just blinks. */ | 
					
						
							|  |  |  | 			ieee80211_queue_delayed_work(hw, &priv->led_on, | 
					
						
							|  |  |  | 						     HZ / 20); | 
					
						
							|  |  |  | 		} else | 
					
						
							|  |  |  | 			ieee80211_queue_delayed_work(hw, &priv->led_on, 0); | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2009-04-16 19:56:38 -05:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int rtl8187_register_led(struct ieee80211_hw *dev, | 
					
						
							|  |  |  | 				struct rtl8187_led *led, const char *name, | 
					
						
							| 
									
										
										
										
											2009-12-09 14:56:13 -02:00
										 |  |  | 				const char *default_trigger, u8 ledpin, | 
					
						
							|  |  |  | 				bool is_radio) | 
					
						
							| 
									
										
										
										
											2009-04-16 19:56:38 -05:00
										 |  |  | { | 
					
						
							|  |  |  | 	int err; | 
					
						
							|  |  |  | 	struct rtl8187_priv *priv = dev->priv; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (led->dev) | 
					
						
							|  |  |  | 		return -EEXIST; | 
					
						
							|  |  |  | 	if (!default_trigger) | 
					
						
							|  |  |  | 		return -EINVAL; | 
					
						
							|  |  |  | 	led->dev = dev; | 
					
						
							|  |  |  | 	led->ledpin = ledpin; | 
					
						
							| 
									
										
										
										
											2009-12-09 14:56:13 -02:00
										 |  |  | 	led->is_radio = is_radio; | 
					
						
							| 
									
										
										
										
											2009-04-16 19:56:38 -05:00
										 |  |  | 	strncpy(led->name, name, sizeof(led->name)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	led->led_dev.name = led->name; | 
					
						
							|  |  |  | 	led->led_dev.default_trigger = default_trigger; | 
					
						
							|  |  |  | 	led->led_dev.brightness_set = rtl8187_led_brightness_set; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	err = led_classdev_register(&priv->udev->dev, &led->led_dev); | 
					
						
							|  |  |  | 	if (err) { | 
					
						
							|  |  |  | 		printk(KERN_INFO "LEDs: Failed to register %s\n", name); | 
					
						
							|  |  |  | 		led->dev = NULL; | 
					
						
							|  |  |  | 		return err; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void rtl8187_unregister_led(struct rtl8187_led *led) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2009-12-09 14:56:13 -02:00
										 |  |  | 	struct ieee80211_hw *hw = led->dev; | 
					
						
							|  |  |  | 	struct rtl8187_priv *priv = hw->priv; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-16 19:56:38 -05:00
										 |  |  | 	led_classdev_unregister(&led->led_dev); | 
					
						
							| 
									
										
										
										
											2009-12-09 14:56:13 -02:00
										 |  |  | 	flush_delayed_work(&priv->led_off); | 
					
						
							| 
									
										
										
										
											2009-04-16 19:56:38 -05:00
										 |  |  | 	led->dev = NULL; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void rtl8187_leds_init(struct ieee80211_hw *dev, u16 custid) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct rtl8187_priv *priv = dev->priv; | 
					
						
							|  |  |  | 	char name[RTL8187_LED_MAX_NAME_LEN + 1]; | 
					
						
							|  |  |  | 	u8 ledpin; | 
					
						
							|  |  |  | 	int err; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* According to the vendor driver, the LED operation depends on the
 | 
					
						
							|  |  |  | 	 * customer ID encoded in the EEPROM | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	printk(KERN_INFO "rtl8187: Customer ID is 0x%02X\n", custid); | 
					
						
							|  |  |  | 	switch (custid) { | 
					
						
							|  |  |  | 	case EEPROM_CID_RSVD0: | 
					
						
							|  |  |  | 	case EEPROM_CID_RSVD1: | 
					
						
							|  |  |  | 	case EEPROM_CID_SERCOMM_PS: | 
					
						
							|  |  |  | 	case EEPROM_CID_QMI: | 
					
						
							|  |  |  | 	case EEPROM_CID_DELL: | 
					
						
							|  |  |  | 	case EEPROM_CID_TOSHIBA: | 
					
						
							|  |  |  | 		ledpin = LED_PIN_GPIO0; | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	case EEPROM_CID_ALPHA0: | 
					
						
							|  |  |  | 		ledpin = LED_PIN_LED0; | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	case EEPROM_CID_HW: | 
					
						
							|  |  |  | 		ledpin = LED_PIN_HW; | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	default: | 
					
						
							|  |  |  | 		ledpin = LED_PIN_GPIO0; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	INIT_DELAYED_WORK(&priv->led_on, led_turn_on); | 
					
						
							|  |  |  | 	INIT_DELAYED_WORK(&priv->led_off, led_turn_off); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-12-09 14:56:13 -02:00
										 |  |  | 	snprintf(name, sizeof(name), | 
					
						
							|  |  |  | 		 "rtl8187-%s::radio", wiphy_name(dev->wiphy)); | 
					
						
							|  |  |  | 	err = rtl8187_register_led(dev, &priv->led_radio, name, | 
					
						
							|  |  |  | 			 ieee80211_get_radio_led_name(dev), ledpin, true); | 
					
						
							|  |  |  | 	if (err) | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-16 19:56:38 -05:00
										 |  |  | 	snprintf(name, sizeof(name), | 
					
						
							|  |  |  | 		 "rtl8187-%s::tx", wiphy_name(dev->wiphy)); | 
					
						
							|  |  |  | 	err = rtl8187_register_led(dev, &priv->led_tx, name, | 
					
						
							| 
									
										
										
										
											2009-12-09 14:56:13 -02:00
										 |  |  | 			 ieee80211_get_tx_led_name(dev), ledpin, false); | 
					
						
							| 
									
										
										
										
											2009-04-16 19:56:38 -05:00
										 |  |  | 	if (err) | 
					
						
							| 
									
										
										
										
											2009-12-09 14:56:13 -02:00
										 |  |  | 		goto err_tx; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-16 19:56:38 -05:00
										 |  |  | 	snprintf(name, sizeof(name), | 
					
						
							|  |  |  | 		 "rtl8187-%s::rx", wiphy_name(dev->wiphy)); | 
					
						
							|  |  |  | 	err = rtl8187_register_led(dev, &priv->led_rx, name, | 
					
						
							| 
									
										
										
										
											2009-12-09 14:56:13 -02:00
										 |  |  | 			 ieee80211_get_rx_led_name(dev), ledpin, false); | 
					
						
							|  |  |  | 	if (!err) | 
					
						
							| 
									
										
										
										
											2009-04-16 19:56:38 -05:00
										 |  |  | 		return; | 
					
						
							| 
									
										
										
										
											2009-12-09 14:56:13 -02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/* registration of RX LED failed - unregister */ | 
					
						
							| 
									
										
										
										
											2009-04-16 19:56:38 -05:00
										 |  |  | 	rtl8187_unregister_led(&priv->led_tx); | 
					
						
							| 
									
										
										
										
											2009-12-09 14:56:13 -02:00
										 |  |  | err_tx: | 
					
						
							|  |  |  | 	rtl8187_unregister_led(&priv->led_radio); | 
					
						
							| 
									
										
										
										
											2009-04-16 19:56:38 -05:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void rtl8187_leds_exit(struct ieee80211_hw *dev) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct rtl8187_priv *priv = dev->priv; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-12-09 14:56:13 -02:00
										 |  |  | 	rtl8187_unregister_led(&priv->led_radio); | 
					
						
							| 
									
										
										
										
											2009-04-16 19:56:38 -05:00
										 |  |  | 	rtl8187_unregister_led(&priv->led_rx); | 
					
						
							| 
									
										
										
										
											2009-07-14 15:55:16 -05:00
										 |  |  | 	rtl8187_unregister_led(&priv->led_tx); | 
					
						
							| 
									
										
										
										
											2009-11-04 00:00:25 -06:00
										 |  |  | 	cancel_delayed_work_sync(&priv->led_off); | 
					
						
							|  |  |  | 	cancel_delayed_work_sync(&priv->led_on); | 
					
						
							| 
									
										
										
										
											2009-04-16 19:56:38 -05:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2010-02-13 18:10:53 +01:00
										 |  |  | #endif /* def CONFIG_RTL8187_LEDS */
 | 
					
						
							| 
									
										
										
										
											2009-04-16 19:56:38 -05:00
										 |  |  | 
 |