rtl8187: Fix for kernel oops when unloading with LEDs enabled
[linux-2.6.git] / drivers / net / wireless / rtl818x / rtl8187_leds.c
1 /*
2  * Linux LED driver for RTL8187
3  *
4  * Copyright 2009 Larry Finger <Larry.Finger@lwfinger.net>
5  *
6  * Based on the LED handling in the r8187 driver, which is:
7  * Copyright (c) Realtek Semiconductor Corp. All rights reserved.
8  *
9  * Thanks to Realtek for their support!
10  *
11  * This program is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License version 2 as
13  * published by the Free Software Foundation.
14  */
15
16 #ifdef CONFIG_RTL8187_LEDS
17
18 #include <net/mac80211.h>
19 #include <linux/usb.h>
20 #include <linux/eeprom_93cx6.h>
21
22 #include "rtl8187.h"
23 #include "rtl8187_leds.h"
24
25 static void led_turn_on(struct work_struct *work)
26 {
27         /* As this routine does read/write operations on the hardware, it must
28          * be run from a work queue.
29          */
30         u8 reg;
31         struct rtl8187_priv *priv = container_of(work, struct rtl8187_priv,
32                                     led_on.work);
33         struct rtl8187_led *led = &priv->led_tx;
34
35         /* Don't change the LED, when the device is down. */
36         if (priv->mode == NL80211_IFTYPE_UNSPECIFIED)
37                 return ;
38
39         /* Skip if the LED is not registered. */
40         if (!led->dev)
41                 return;
42         mutex_lock(&priv->conf_mutex);
43         switch (led->ledpin) {
44         case LED_PIN_GPIO0:
45                 rtl818x_iowrite8(priv, &priv->map->GPIO, 0x01);
46                 rtl818x_iowrite8(priv, &priv->map->GP_ENABLE, 0x00);
47                 break;
48         case LED_PIN_LED0:
49                 reg = rtl818x_ioread8(priv, &priv->map->PGSELECT) & ~(1 << 4);
50                 rtl818x_iowrite8(priv, &priv->map->PGSELECT, reg);
51                 break;
52         case LED_PIN_LED1:
53                 reg = rtl818x_ioread8(priv, &priv->map->PGSELECT) & ~(1 << 5);
54                 rtl818x_iowrite8(priv, &priv->map->PGSELECT, reg);
55                 break;
56         case LED_PIN_HW:
57         default:
58                 break;
59         }
60         mutex_unlock(&priv->conf_mutex);
61 }
62
63 static void led_turn_off(struct work_struct *work)
64 {
65         /* As this routine does read/write operations on the hardware, it must
66          * be run from a work queue.
67          */
68         u8 reg;
69         struct rtl8187_priv *priv = container_of(work, struct rtl8187_priv,
70                                     led_off.work);
71         struct rtl8187_led *led = &priv->led_tx;
72
73         /* Don't change the LED, when the device is down. */
74         if (priv->mode == NL80211_IFTYPE_UNSPECIFIED)
75                 return ;
76
77         /* Skip if the LED is not registered. */
78         if (!led->dev)
79                 return;
80         mutex_lock(&priv->conf_mutex);
81         switch (led->ledpin) {
82         case LED_PIN_GPIO0:
83                 rtl818x_iowrite8(priv, &priv->map->GPIO, 0x01);
84                 rtl818x_iowrite8(priv, &priv->map->GP_ENABLE, 0x01);
85                 break;
86         case LED_PIN_LED0:
87                 reg = rtl818x_ioread8(priv, &priv->map->PGSELECT) | (1 << 4);
88                 rtl818x_iowrite8(priv, &priv->map->PGSELECT, reg);
89                 break;
90         case LED_PIN_LED1:
91                 reg = rtl818x_ioread8(priv, &priv->map->PGSELECT) | (1 << 5);
92                 rtl818x_iowrite8(priv, &priv->map->PGSELECT, reg);
93                 break;
94         case LED_PIN_HW:
95         default:
96                 break;
97         }
98         mutex_unlock(&priv->conf_mutex);
99 }
100
101 /* Callback from the LED subsystem. */
102 static void rtl8187_led_brightness_set(struct led_classdev *led_dev,
103                                    enum led_brightness brightness)
104 {
105         struct rtl8187_led *led = container_of(led_dev, struct rtl8187_led,
106                                                led_dev);
107         struct ieee80211_hw *hw = led->dev;
108         struct rtl8187_priv *priv = hw->priv;
109
110         if (brightness == LED_OFF) {
111                 queue_delayed_work(hw->workqueue, &priv->led_off, 0);
112                 /* The LED is off for 1/20 sec so that it just blinks. */
113                 queue_delayed_work(hw->workqueue, &priv->led_on, HZ / 20);
114         } else
115                 queue_delayed_work(hw->workqueue, &priv->led_on, 0);
116 }
117
118 static int rtl8187_register_led(struct ieee80211_hw *dev,
119                                 struct rtl8187_led *led, const char *name,
120                                 const char *default_trigger, u8 ledpin)
121 {
122         int err;
123         struct rtl8187_priv *priv = dev->priv;
124
125         if (led->dev)
126                 return -EEXIST;
127         if (!default_trigger)
128                 return -EINVAL;
129         led->dev = dev;
130         led->ledpin = ledpin;
131         strncpy(led->name, name, sizeof(led->name));
132
133         led->led_dev.name = led->name;
134         led->led_dev.default_trigger = default_trigger;
135         led->led_dev.brightness_set = rtl8187_led_brightness_set;
136
137         err = led_classdev_register(&priv->udev->dev, &led->led_dev);
138         if (err) {
139                 printk(KERN_INFO "LEDs: Failed to register %s\n", name);
140                 led->dev = NULL;
141                 return err;
142         }
143         return 0;
144 }
145
146 static void rtl8187_unregister_led(struct rtl8187_led *led)
147 {
148         led_classdev_unregister(&led->led_dev);
149         led->dev = NULL;
150 }
151
152 void rtl8187_leds_init(struct ieee80211_hw *dev, u16 custid)
153 {
154         struct rtl8187_priv *priv = dev->priv;
155         char name[RTL8187_LED_MAX_NAME_LEN + 1];
156         u8 ledpin;
157         int err;
158
159         /* According to the vendor driver, the LED operation depends on the
160          * customer ID encoded in the EEPROM
161          */
162         printk(KERN_INFO "rtl8187: Customer ID is 0x%02X\n", custid);
163         switch (custid) {
164         case EEPROM_CID_RSVD0:
165         case EEPROM_CID_RSVD1:
166         case EEPROM_CID_SERCOMM_PS:
167         case EEPROM_CID_QMI:
168         case EEPROM_CID_DELL:
169         case EEPROM_CID_TOSHIBA:
170                 ledpin = LED_PIN_GPIO0;
171                 break;
172         case EEPROM_CID_ALPHA0:
173                 ledpin = LED_PIN_LED0;
174                 break;
175         case EEPROM_CID_HW:
176                 ledpin = LED_PIN_HW;
177                 break;
178         default:
179                 ledpin = LED_PIN_GPIO0;
180         }
181
182         INIT_DELAYED_WORK(&priv->led_on, led_turn_on);
183         INIT_DELAYED_WORK(&priv->led_off, led_turn_off);
184
185         snprintf(name, sizeof(name),
186                  "rtl8187-%s::tx", wiphy_name(dev->wiphy));
187         err = rtl8187_register_led(dev, &priv->led_tx, name,
188                          ieee80211_get_tx_led_name(dev), ledpin);
189         if (err)
190                 goto error;
191         snprintf(name, sizeof(name),
192                  "rtl8187-%s::rx", wiphy_name(dev->wiphy));
193         err = rtl8187_register_led(dev, &priv->led_rx, name,
194                          ieee80211_get_rx_led_name(dev), ledpin);
195         if (!err) {
196                 queue_delayed_work(dev->workqueue, &priv->led_on, 0);
197                 return;
198         }
199         /* registration of RX LED failed - unregister TX */
200         rtl8187_unregister_led(&priv->led_tx);
201 error:
202         /* If registration of either failed, cancel delayed work */
203         cancel_delayed_work_sync(&priv->led_off);
204         cancel_delayed_work_sync(&priv->led_on);
205 }
206
207 void rtl8187_leds_exit(struct ieee80211_hw *dev)
208 {
209         struct rtl8187_priv *priv = dev->priv;
210
211         /* turn the LED off before exiting */
212         queue_delayed_work(dev->workqueue, &priv->led_off, 0);
213         cancel_delayed_work_sync(&priv->led_off);
214         cancel_delayed_work_sync(&priv->led_on);
215         rtl8187_unregister_led(&priv->led_rx);
216         rtl8187_unregister_led(&priv->led_tx);
217 }
218 #endif /* def CONFIG_RTL8187_LED */
219