diff options
-rw-r--r-- | drivers/net/wireless/orinoco/Makefile | 2 | ||||
-rw-r--r-- | drivers/net/wireless/orinoco/main.c | 2355 | ||||
-rw-r--r-- | drivers/net/wireless/orinoco/main.h | 63 | ||||
-rw-r--r-- | drivers/net/wireless/orinoco/wext.c | 2325 | ||||
-rw-r--r-- | drivers/net/wireless/orinoco/wext.h | 13 |
5 files changed, 2408 insertions, 2350 deletions
diff --git a/drivers/net/wireless/orinoco/Makefile b/drivers/net/wireless/orinoco/Makefile index f40f54d31e59..431ba399a9f2 100644 --- a/drivers/net/wireless/orinoco/Makefile +++ b/drivers/net/wireless/orinoco/Makefile | |||
@@ -1,7 +1,7 @@ | |||
1 | # | 1 | # |
2 | # Makefile for the orinoco wireless device drivers. | 2 | # Makefile for the orinoco wireless device drivers. |
3 | # | 3 | # |
4 | orinoco-objs := main.o fw.o hw.o mic.o scan.o | 4 | orinoco-objs := main.o fw.o hw.o mic.o scan.o wext.o |
5 | 5 | ||
6 | obj-$(CONFIG_HERMES) += orinoco.o hermes.o hermes_dld.o | 6 | obj-$(CONFIG_HERMES) += orinoco.o hermes.o hermes_dld.o |
7 | obj-$(CONFIG_PCMCIA_HERMES) += orinoco_cs.o | 7 | obj-$(CONFIG_PCMCIA_HERMES) += orinoco_cs.o |
diff --git a/drivers/net/wireless/orinoco/main.c b/drivers/net/wireless/orinoco/main.c index a340c7d75dd1..54dfc4540b82 100644 --- a/drivers/net/wireless/orinoco/main.c +++ b/drivers/net/wireless/orinoco/main.c | |||
@@ -95,6 +95,8 @@ | |||
95 | #include "scan.h" | 95 | #include "scan.h" |
96 | #include "mic.h" | 96 | #include "mic.h" |
97 | #include "fw.h" | 97 | #include "fw.h" |
98 | #include "wext.h" | ||
99 | #include "main.h" | ||
98 | 100 | ||
99 | #include "orinoco.h" | 101 | #include "orinoco.h" |
100 | 102 | ||
@@ -125,22 +127,11 @@ module_param(ignore_disconnect, int, 0644); | |||
125 | MODULE_PARM_DESC(ignore_disconnect, | 127 | MODULE_PARM_DESC(ignore_disconnect, |
126 | "Don't report lost link to the network layer"); | 128 | "Don't report lost link to the network layer"); |
127 | 129 | ||
128 | static int force_monitor; /* = 0 */ | 130 | int force_monitor; /* = 0 */ |
129 | module_param(force_monitor, int, 0644); | 131 | module_param(force_monitor, int, 0644); |
130 | MODULE_PARM_DESC(force_monitor, "Allow monitor mode for all firmware versions"); | 132 | MODULE_PARM_DESC(force_monitor, "Allow monitor mode for all firmware versions"); |
131 | 133 | ||
132 | /********************************************************************/ | 134 | /********************************************************************/ |
133 | /* Compile time configuration and compatibility stuff */ | ||
134 | /********************************************************************/ | ||
135 | |||
136 | /* We do this this way to avoid ifdefs in the actual code */ | ||
137 | #ifdef WIRELESS_SPY | ||
138 | #define SPY_NUMBER(priv) (priv->spy_data.spy_number) | ||
139 | #else | ||
140 | #define SPY_NUMBER(priv) 0 | ||
141 | #endif /* WIRELESS_SPY */ | ||
142 | |||
143 | /********************************************************************/ | ||
144 | /* Internal constants */ | 135 | /* Internal constants */ |
145 | /********************************************************************/ | 136 | /********************************************************************/ |
146 | 137 | ||
@@ -170,9 +161,6 @@ static const u8 encaps_hdr[] = {0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00}; | |||
170 | | HERMES_EV_WTERR | HERMES_EV_INFO \ | 161 | | HERMES_EV_WTERR | HERMES_EV_INFO \ |
171 | | HERMES_EV_INFDROP) | 162 | | HERMES_EV_INFDROP) |
172 | 163 | ||
173 | #define MAX_RID_LEN 1024 | ||
174 | |||
175 | static const struct iw_handler_def orinoco_handler_def; | ||
176 | static const struct ethtool_ops orinoco_ethtool_ops; | 164 | static const struct ethtool_ops orinoco_ethtool_ops; |
177 | 165 | ||
178 | /********************************************************************/ | 166 | /********************************************************************/ |
@@ -221,14 +209,13 @@ struct orinoco_rx_data { | |||
221 | /* Function prototypes */ | 209 | /* Function prototypes */ |
222 | /********************************************************************/ | 210 | /********************************************************************/ |
223 | 211 | ||
224 | static int __orinoco_program_rids(struct net_device *dev); | ||
225 | static void __orinoco_set_multicast_list(struct net_device *dev); | 212 | static void __orinoco_set_multicast_list(struct net_device *dev); |
226 | 213 | ||
227 | /********************************************************************/ | 214 | /********************************************************************/ |
228 | /* Internal helper functions */ | 215 | /* Internal helper functions */ |
229 | /********************************************************************/ | 216 | /********************************************************************/ |
230 | 217 | ||
231 | static inline void set_port_type(struct orinoco_private *priv) | 218 | void set_port_type(struct orinoco_private *priv) |
232 | { | 219 | { |
233 | switch (priv->iw_mode) { | 220 | switch (priv->iw_mode) { |
234 | case IW_MODE_INFRA: | 221 | case IW_MODE_INFRA: |
@@ -254,33 +241,6 @@ static inline void set_port_type(struct orinoco_private *priv) | |||
254 | } | 241 | } |
255 | } | 242 | } |
256 | 243 | ||
257 | static inline u8 *orinoco_get_ie(u8 *data, size_t len, | ||
258 | enum ieee80211_eid eid) | ||
259 | { | ||
260 | u8 *p = data; | ||
261 | while ((p + 2) < (data + len)) { | ||
262 | if (p[0] == eid) | ||
263 | return p; | ||
264 | p += p[1] + 2; | ||
265 | } | ||
266 | return NULL; | ||
267 | } | ||
268 | |||
269 | #define WPA_OUI_TYPE "\x00\x50\xF2\x01" | ||
270 | #define WPA_SELECTOR_LEN 4 | ||
271 | static inline u8 *orinoco_get_wpa_ie(u8 *data, size_t len) | ||
272 | { | ||
273 | u8 *p = data; | ||
274 | while ((p + 2 + WPA_SELECTOR_LEN) < (data + len)) { | ||
275 | if ((p[0] == WLAN_EID_GENERIC) && | ||
276 | (memcmp(&p[2], WPA_OUI_TYPE, WPA_SELECTOR_LEN) == 0)) | ||
277 | return p; | ||
278 | p += p[1] + 2; | ||
279 | } | ||
280 | return NULL; | ||
281 | } | ||
282 | |||
283 | |||
284 | /********************************************************************/ | 244 | /********************************************************************/ |
285 | /* Device methods */ | 245 | /* Device methods */ |
286 | /********************************************************************/ | 246 | /********************************************************************/ |
@@ -330,68 +290,6 @@ static struct net_device_stats *orinoco_get_stats(struct net_device *dev) | |||
330 | return &priv->stats; | 290 | return &priv->stats; |
331 | } | 291 | } |
332 | 292 | ||
333 | static struct iw_statistics *orinoco_get_wireless_stats(struct net_device *dev) | ||
334 | { | ||
335 | struct orinoco_private *priv = netdev_priv(dev); | ||
336 | hermes_t *hw = &priv->hw; | ||
337 | struct iw_statistics *wstats = &priv->wstats; | ||
338 | int err; | ||
339 | unsigned long flags; | ||
340 | |||
341 | if (!netif_device_present(dev)) { | ||
342 | printk(KERN_WARNING "%s: get_wireless_stats() called while device not present\n", | ||
343 | dev->name); | ||
344 | return NULL; /* FIXME: Can we do better than this? */ | ||
345 | } | ||
346 | |||
347 | /* If busy, return the old stats. Returning NULL may cause | ||
348 | * the interface to disappear from /proc/net/wireless */ | ||
349 | if (orinoco_lock(priv, &flags) != 0) | ||
350 | return wstats; | ||
351 | |||
352 | /* We can't really wait for the tallies inquiry command to | ||
353 | * complete, so we just use the previous results and trigger | ||
354 | * a new tallies inquiry command for next time - Jean II */ | ||
355 | /* FIXME: Really we should wait for the inquiry to come back - | ||
356 | * as it is the stats we give don't make a whole lot of sense. | ||
357 | * Unfortunately, it's not clear how to do that within the | ||
358 | * wireless extensions framework: I think we're in user | ||
359 | * context, but a lock seems to be held by the time we get in | ||
360 | * here so we're not safe to sleep here. */ | ||
361 | hermes_inquire(hw, HERMES_INQ_TALLIES); | ||
362 | |||
363 | if (priv->iw_mode == IW_MODE_ADHOC) { | ||
364 | memset(&wstats->qual, 0, sizeof(wstats->qual)); | ||
365 | /* If a spy address is defined, we report stats of the | ||
366 | * first spy address - Jean II */ | ||
367 | if (SPY_NUMBER(priv)) { | ||
368 | wstats->qual.qual = priv->spy_data.spy_stat[0].qual; | ||
369 | wstats->qual.level = priv->spy_data.spy_stat[0].level; | ||
370 | wstats->qual.noise = priv->spy_data.spy_stat[0].noise; | ||
371 | wstats->qual.updated = | ||
372 | priv->spy_data.spy_stat[0].updated; | ||
373 | } | ||
374 | } else { | ||
375 | struct { | ||
376 | __le16 qual, signal, noise, unused; | ||
377 | } __attribute__ ((packed)) cq; | ||
378 | |||
379 | err = HERMES_READ_RECORD(hw, USER_BAP, | ||
380 | HERMES_RID_COMMSQUALITY, &cq); | ||
381 | |||
382 | if (!err) { | ||
383 | wstats->qual.qual = (int)le16_to_cpu(cq.qual); | ||
384 | wstats->qual.level = (int)le16_to_cpu(cq.signal) - 0x95; | ||
385 | wstats->qual.noise = (int)le16_to_cpu(cq.noise) - 0x95; | ||
386 | wstats->qual.updated = | ||
387 | IW_QUAL_ALL_UPDATED | IW_QUAL_DBM; | ||
388 | } | ||
389 | } | ||
390 | |||
391 | orinoco_unlock(priv, &flags); | ||
392 | return wstats; | ||
393 | } | ||
394 | |||
395 | static void orinoco_set_multicast_list(struct net_device *dev) | 293 | static void orinoco_set_multicast_list(struct net_device *dev) |
396 | { | 294 | { |
397 | struct orinoco_private *priv = netdev_priv(dev); | 295 | struct orinoco_private *priv = netdev_priv(dev); |
@@ -1716,7 +1614,7 @@ int orinoco_reinit_firmware(struct net_device *dev) | |||
1716 | } | 1614 | } |
1717 | EXPORT_SYMBOL(orinoco_reinit_firmware); | 1615 | EXPORT_SYMBOL(orinoco_reinit_firmware); |
1718 | 1616 | ||
1719 | static int __orinoco_program_rids(struct net_device *dev) | 1617 | int __orinoco_program_rids(struct net_device *dev) |
1720 | { | 1618 | { |
1721 | struct orinoco_private *priv = netdev_priv(dev); | 1619 | struct orinoco_private *priv = netdev_priv(dev); |
1722 | hermes_t *hw = &priv->hw; | 1620 | hermes_t *hw = &priv->hw; |
@@ -1970,7 +1868,7 @@ __orinoco_set_multicast_list(struct net_device *dev) | |||
1970 | 1868 | ||
1971 | /* This must be called from user context, without locks held - use | 1869 | /* This must be called from user context, without locks held - use |
1972 | * schedule_work() */ | 1870 | * schedule_work() */ |
1973 | static void orinoco_reset(struct work_struct *work) | 1871 | void orinoco_reset(struct work_struct *work) |
1974 | { | 1872 | { |
1975 | struct orinoco_private *priv = | 1873 | struct orinoco_private *priv = |
1976 | container_of(work, struct orinoco_private, reset_work); | 1874 | container_of(work, struct orinoco_private, reset_work); |
@@ -2711,2247 +2609,6 @@ void free_orinocodev(struct net_device *dev) | |||
2711 | } | 2609 | } |
2712 | EXPORT_SYMBOL(free_orinocodev); | 2610 | EXPORT_SYMBOL(free_orinocodev); |
2713 | 2611 | ||
2714 | /********************************************************************/ | ||
2715 | /* Wireless extensions */ | ||
2716 | /********************************************************************/ | ||
2717 | |||
2718 | static int orinoco_ioctl_getname(struct net_device *dev, | ||
2719 | struct iw_request_info *info, | ||
2720 | char *name, | ||
2721 | char *extra) | ||
2722 | { | ||
2723 | struct orinoco_private *priv = netdev_priv(dev); | ||
2724 | int numrates; | ||
2725 | int err; | ||
2726 | |||
2727 | err = orinoco_hw_get_bitratelist(priv, &numrates, NULL, 0); | ||
2728 | |||
2729 | if (!err && (numrates > 2)) | ||
2730 | strcpy(name, "IEEE 802.11b"); | ||
2731 | else | ||
2732 | strcpy(name, "IEEE 802.11-DS"); | ||
2733 | |||
2734 | return 0; | ||
2735 | } | ||
2736 | |||
2737 | static int orinoco_ioctl_setwap(struct net_device *dev, | ||
2738 | struct iw_request_info *info, | ||
2739 | struct sockaddr *ap_addr, | ||
2740 | char *extra) | ||
2741 | { | ||
2742 | struct orinoco_private *priv = netdev_priv(dev); | ||
2743 | int err = -EINPROGRESS; /* Call commit handler */ | ||
2744 | unsigned long flags; | ||
2745 | static const u8 off_addr[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; | ||
2746 | static const u8 any_addr[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; | ||
2747 | |||
2748 | if (orinoco_lock(priv, &flags) != 0) | ||
2749 | return -EBUSY; | ||
2750 | |||
2751 | /* Enable automatic roaming - no sanity checks are needed */ | ||
2752 | if (memcmp(&ap_addr->sa_data, off_addr, ETH_ALEN) == 0 || | ||
2753 | memcmp(&ap_addr->sa_data, any_addr, ETH_ALEN) == 0) { | ||
2754 | priv->bssid_fixed = 0; | ||
2755 | memset(priv->desired_bssid, 0, ETH_ALEN); | ||
2756 | |||
2757 | /* "off" means keep existing connection */ | ||
2758 | if (ap_addr->sa_data[0] == 0) { | ||
2759 | __orinoco_hw_set_wap(priv); | ||
2760 | err = 0; | ||
2761 | } | ||
2762 | goto out; | ||
2763 | } | ||
2764 | |||
2765 | if (priv->firmware_type == FIRMWARE_TYPE_AGERE) { | ||
2766 | printk(KERN_WARNING "%s: Lucent/Agere firmware doesn't " | ||
2767 | "support manual roaming\n", | ||
2768 | dev->name); | ||
2769 | err = -EOPNOTSUPP; | ||
2770 | goto out; | ||
2771 | } | ||
2772 | |||
2773 | if (priv->iw_mode != IW_MODE_INFRA) { | ||
2774 | printk(KERN_WARNING "%s: Manual roaming supported only in " | ||
2775 | "managed mode\n", dev->name); | ||
2776 | err = -EOPNOTSUPP; | ||
2777 | goto out; | ||
2778 | } | ||
2779 | |||
2780 | /* Intersil firmware hangs without Desired ESSID */ | ||
2781 | if (priv->firmware_type == FIRMWARE_TYPE_INTERSIL && | ||
2782 | strlen(priv->desired_essid) == 0) { | ||
2783 | printk(KERN_WARNING "%s: Desired ESSID must be set for " | ||
2784 | "manual roaming\n", dev->name); | ||
2785 | err = -EOPNOTSUPP; | ||
2786 | goto out; | ||
2787 | } | ||
2788 | |||
2789 | /* Finally, enable manual roaming */ | ||
2790 | priv->bssid_fixed = 1; | ||
2791 | memcpy(priv->desired_bssid, &ap_addr->sa_data, ETH_ALEN); | ||
2792 | |||
2793 | out: | ||
2794 | orinoco_unlock(priv, &flags); | ||
2795 | return err; | ||
2796 | } | ||
2797 | |||
2798 | static int orinoco_ioctl_getwap(struct net_device *dev, | ||
2799 | struct iw_request_info *info, | ||
2800 | struct sockaddr *ap_addr, | ||
2801 | char *extra) | ||
2802 | { | ||
2803 | struct orinoco_private *priv = netdev_priv(dev); | ||
2804 | |||
2805 | hermes_t *hw = &priv->hw; | ||
2806 | int err = 0; | ||
2807 | unsigned long flags; | ||
2808 | |||
2809 | if (orinoco_lock(priv, &flags) != 0) | ||
2810 | return -EBUSY; | ||
2811 | |||
2812 | ap_addr->sa_family = ARPHRD_ETHER; | ||
2813 | err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_CURRENTBSSID, | ||
2814 | ETH_ALEN, NULL, ap_addr->sa_data); | ||
2815 | |||
2816 | orinoco_unlock(priv, &flags); | ||
2817 | |||
2818 | return err; | ||
2819 | } | ||
2820 | |||
2821 | static int orinoco_ioctl_setmode(struct net_device *dev, | ||
2822 | struct iw_request_info *info, | ||
2823 | u32 *mode, | ||
2824 | char *extra) | ||
2825 | { | ||
2826 | struct orinoco_private *priv = netdev_priv(dev); | ||
2827 | int err = -EINPROGRESS; /* Call commit handler */ | ||
2828 | unsigned long flags; | ||
2829 | |||
2830 | if (priv->iw_mode == *mode) | ||
2831 | return 0; | ||
2832 | |||
2833 | if (orinoco_lock(priv, &flags) != 0) | ||
2834 | return -EBUSY; | ||
2835 | |||
2836 | switch (*mode) { | ||
2837 | case IW_MODE_ADHOC: | ||
2838 | if (!priv->has_ibss && !priv->has_port3) | ||
2839 | err = -EOPNOTSUPP; | ||
2840 | break; | ||
2841 | |||
2842 | case IW_MODE_INFRA: | ||
2843 | break; | ||
2844 | |||
2845 | case IW_MODE_MONITOR: | ||
2846 | if (priv->broken_monitor && !force_monitor) { | ||
2847 | printk(KERN_WARNING "%s: Monitor mode support is " | ||
2848 | "buggy in this firmware, not enabling\n", | ||
2849 | dev->name); | ||
2850 | err = -EOPNOTSUPP; | ||
2851 | } | ||
2852 | break; | ||
2853 | |||
2854 | default: | ||
2855 | err = -EOPNOTSUPP; | ||
2856 | break; | ||
2857 | } | ||
2858 | |||
2859 | if (err == -EINPROGRESS) { | ||
2860 | priv->iw_mode = *mode; | ||
2861 | set_port_type(priv); | ||
2862 | } | ||
2863 | |||
2864 | orinoco_unlock(priv, &flags); | ||
2865 | |||
2866 | return err; | ||
2867 | } | ||
2868 | |||
2869 | static int orinoco_ioctl_getmode(struct net_device *dev, | ||
2870 | struct iw_request_info *info, | ||
2871 | u32 *mode, | ||
2872 | char *extra) | ||
2873 | { | ||
2874 | struct orinoco_private *priv = netdev_priv(dev); | ||
2875 | |||
2876 | *mode = priv->iw_mode; | ||
2877 | return 0; | ||
2878 | } | ||
2879 | |||
2880 | static int orinoco_ioctl_getiwrange(struct net_device *dev, | ||
2881 | struct iw_request_info *info, | ||
2882 | struct iw_point *rrq, | ||
2883 | char *extra) | ||
2884 | { | ||
2885 | struct orinoco_private *priv = netdev_priv(dev); | ||
2886 | int err = 0; | ||
2887 | struct iw_range *range = (struct iw_range *) extra; | ||
2888 | int numrates; | ||
2889 | int i, k; | ||
2890 | |||
2891 | rrq->length = sizeof(struct iw_range); | ||
2892 | memset(range, 0, sizeof(struct iw_range)); | ||
2893 | |||
2894 | range->we_version_compiled = WIRELESS_EXT; | ||
2895 | range->we_version_source = 22; | ||
2896 | |||
2897 | /* Set available channels/frequencies */ | ||
2898 | range->num_channels = NUM_CHANNELS; | ||
2899 | k = 0; | ||
2900 | for (i = 0; i < NUM_CHANNELS; i++) { | ||
2901 | if (priv->channel_mask & (1 << i)) { | ||
2902 | range->freq[k].i = i + 1; | ||
2903 | range->freq[k].m = (ieee80211_dsss_chan_to_freq(i + 1) * | ||
2904 | 100000); | ||
2905 | range->freq[k].e = 1; | ||
2906 | k++; | ||
2907 | } | ||
2908 | |||
2909 | if (k >= IW_MAX_FREQUENCIES) | ||
2910 | break; | ||
2911 | } | ||
2912 | range->num_frequency = k; | ||
2913 | range->sensitivity = 3; | ||
2914 | |||
2915 | if (priv->has_wep) { | ||
2916 | range->max_encoding_tokens = ORINOCO_MAX_KEYS; | ||
2917 | range->encoding_size[0] = SMALL_KEY_SIZE; | ||
2918 | range->num_encoding_sizes = 1; | ||
2919 | |||
2920 | if (priv->has_big_wep) { | ||
2921 | range->encoding_size[1] = LARGE_KEY_SIZE; | ||
2922 | range->num_encoding_sizes = 2; | ||
2923 | } | ||
2924 | } | ||
2925 | |||
2926 | if (priv->has_wpa) | ||
2927 | range->enc_capa = IW_ENC_CAPA_WPA | IW_ENC_CAPA_CIPHER_TKIP; | ||
2928 | |||
2929 | if ((priv->iw_mode == IW_MODE_ADHOC) && (!SPY_NUMBER(priv))) { | ||
2930 | /* Quality stats meaningless in ad-hoc mode */ | ||
2931 | } else { | ||
2932 | range->max_qual.qual = 0x8b - 0x2f; | ||
2933 | range->max_qual.level = 0x2f - 0x95 - 1; | ||
2934 | range->max_qual.noise = 0x2f - 0x95 - 1; | ||
2935 | /* Need to get better values */ | ||
2936 | range->avg_qual.qual = 0x24; | ||
2937 | range->avg_qual.level = 0xC2; | ||
2938 | range->avg_qual.noise = 0x9E; | ||
2939 | } | ||
2940 | |||
2941 | err = orinoco_hw_get_bitratelist(priv, &numrates, | ||
2942 | range->bitrate, IW_MAX_BITRATES); | ||
2943 | if (err) | ||
2944 | return err; | ||
2945 | range->num_bitrates = numrates; | ||
2946 | |||
2947 | /* Set an indication of the max TCP throughput in bit/s that we can | ||
2948 | * expect using this interface. May be use for QoS stuff... | ||
2949 | * Jean II */ | ||
2950 | if (numrates > 2) | ||
2951 | range->throughput = 5 * 1000 * 1000; /* ~5 Mb/s */ | ||
2952 | else | ||
2953 | range->throughput = 1.5 * 1000 * 1000; /* ~1.5 Mb/s */ | ||
2954 | |||
2955 | range->min_rts = 0; | ||
2956 | range->max_rts = 2347; | ||
2957 | range->min_frag = 256; | ||
2958 | range->max_frag = 2346; | ||
2959 | |||
2960 | range->min_pmp = 0; | ||
2961 | range->max_pmp = 65535000; | ||
2962 | range->min_pmt = 0; | ||
2963 | range->max_pmt = 65535 * 1000; /* ??? */ | ||
2964 | range->pmp_flags = IW_POWER_PERIOD; | ||
2965 | range->pmt_flags = IW_POWER_TIMEOUT; | ||
2966 | range->pm_capa = (IW_POWER_PERIOD | IW_POWER_TIMEOUT | | ||
2967 | IW_POWER_UNICAST_R); | ||
2968 | |||
2969 | range->retry_capa = IW_RETRY_LIMIT | IW_RETRY_LIFETIME; | ||
2970 | range->retry_flags = IW_RETRY_LIMIT; | ||
2971 | range->r_time_flags = IW_RETRY_LIFETIME; | ||
2972 | range->min_retry = 0; | ||
2973 | range->max_retry = 65535; /* ??? */ | ||
2974 | range->min_r_time = 0; | ||
2975 | range->max_r_time = 65535 * 1000; /* ??? */ | ||
2976 | |||
2977 | if (priv->firmware_type == FIRMWARE_TYPE_AGERE) | ||
2978 | range->scan_capa = IW_SCAN_CAPA_ESSID; | ||
2979 | else | ||
2980 | range->scan_capa = IW_SCAN_CAPA_NONE; | ||
2981 | |||
2982 | /* Event capability (kernel) */ | ||
2983 | IW_EVENT_CAPA_SET_KERNEL(range->event_capa); | ||
2984 | /* Event capability (driver) */ | ||
2985 | IW_EVENT_CAPA_SET(range->event_capa, SIOCGIWTHRSPY); | ||
2986 | IW_EVENT_CAPA_SET(range->event_capa, SIOCGIWAP); | ||
2987 | IW_EVENT_CAPA_SET(range->event_capa, SIOCGIWSCAN); | ||
2988 | IW_EVENT_CAPA_SET(range->event_capa, IWEVTXDROP); | ||
2989 | |||
2990 | return 0; | ||
2991 | } | ||
2992 | |||
2993 | static int orinoco_ioctl_setiwencode(struct net_device *dev, | ||
2994 | struct iw_request_info *info, | ||
2995 | struct iw_point *erq, | ||
2996 | char *keybuf) | ||
2997 | { | ||
2998 | struct orinoco_private *priv = netdev_priv(dev); | ||
2999 | int index = (erq->flags & IW_ENCODE_INDEX) - 1; | ||
3000 | int setindex = priv->tx_key; | ||
3001 | int encode_alg = priv->encode_alg; | ||
3002 | int restricted = priv->wep_restrict; | ||
3003 | u16 xlen = 0; | ||
3004 | int err = -EINPROGRESS; /* Call commit handler */ | ||
3005 | unsigned long flags; | ||
3006 | |||
3007 | if (!priv->has_wep) | ||
3008 | return -EOPNOTSUPP; | ||
3009 | |||
3010 | if (erq->pointer) { | ||
3011 | /* We actually have a key to set - check its length */ | ||
3012 | if (erq->length > LARGE_KEY_SIZE) | ||
3013 | return -E2BIG; | ||
3014 | |||
3015 | if ((erq->length > SMALL_KEY_SIZE) && !priv->has_big_wep) | ||
3016 | return -E2BIG; | ||
3017 | } | ||
3018 | |||
3019 | if (orinoco_lock(priv, &flags) != 0) | ||
3020 | return -EBUSY; | ||
3021 | |||
3022 | /* Clear any TKIP key we have */ | ||
3023 | if ((priv->has_wpa) && (priv->encode_alg == IW_ENCODE_ALG_TKIP)) | ||
3024 | (void) orinoco_clear_tkip_key(priv, setindex); | ||
3025 | |||
3026 | if (erq->length > 0) { | ||
3027 | if ((index < 0) || (index >= ORINOCO_MAX_KEYS)) | ||
3028 | index = priv->tx_key; | ||
3029 | |||
3030 | /* Adjust key length to a supported value */ | ||
3031 | if (erq->length > SMALL_KEY_SIZE) | ||
3032 | xlen = LARGE_KEY_SIZE; | ||
3033 | else if (erq->length > 0) | ||
3034 | xlen = SMALL_KEY_SIZE; | ||
3035 | else | ||
3036 | xlen = 0; | ||
3037 | |||
3038 | /* Switch on WEP if off */ | ||
3039 | if ((encode_alg != IW_ENCODE_ALG_WEP) && (xlen > 0)) { | ||
3040 | setindex = index; | ||
3041 | encode_alg = IW_ENCODE_ALG_WEP; | ||
3042 | } | ||
3043 | } else { | ||
3044 | /* Important note : if the user do "iwconfig eth0 enc off", | ||
3045 | * we will arrive there with an index of -1. This is valid | ||
3046 | * but need to be taken care off... Jean II */ | ||
3047 | if ((index < 0) || (index >= ORINOCO_MAX_KEYS)) { | ||
3048 | if ((index != -1) || (erq->flags == 0)) { | ||
3049 | err = -EINVAL; | ||
3050 | goto out; | ||
3051 | } | ||
3052 | } else { | ||
3053 | /* Set the index : Check that the key is valid */ | ||
3054 | if (priv->keys[index].len == 0) { | ||
3055 | err = -EINVAL; | ||
3056 | goto out; | ||
3057 | } | ||
3058 | setindex = index; | ||
3059 | } | ||
3060 | } | ||
3061 | |||
3062 | if (erq->flags & IW_ENCODE_DISABLED) | ||
3063 | encode_alg = IW_ENCODE_ALG_NONE; | ||
3064 | if (erq->flags & IW_ENCODE_OPEN) | ||
3065 | restricted = 0; | ||
3066 | if (erq->flags & IW_ENCODE_RESTRICTED) | ||
3067 | restricted = 1; | ||
3068 | |||
3069 | if (erq->pointer && erq->length > 0) { | ||
3070 | priv->keys[index].len = cpu_to_le16(xlen); | ||
3071 | memset(priv->keys[index].data, 0, | ||
3072 | sizeof(priv->keys[index].data)); | ||
3073 | memcpy(priv->keys[index].data, keybuf, erq->length); | ||
3074 | } | ||
3075 | priv->tx_key = setindex; | ||
3076 | |||
3077 | /* Try fast key change if connected and only keys are changed */ | ||
3078 | if ((priv->encode_alg == encode_alg) && | ||
3079 | (priv->wep_restrict == restricted) && | ||
3080 | netif_carrier_ok(dev)) { | ||
3081 | err = __orinoco_hw_setup_wepkeys(priv); | ||
3082 | /* No need to commit if successful */ | ||
3083 | goto out; | ||
3084 | } | ||
3085 | |||
3086 | priv->encode_alg = encode_alg; | ||
3087 | priv->wep_restrict = restricted; | ||
3088 | |||
3089 | out: | ||
3090 | orinoco_unlock(priv, &flags); | ||
3091 | |||
3092 | return err; | ||
3093 | } | ||
3094 | |||
3095 | static int orinoco_ioctl_getiwencode(struct net_device *dev, | ||
3096 | struct iw_request_info *info, | ||
3097 | struct iw_point *erq, | ||
3098 | char *keybuf) | ||
3099 | { | ||
3100 | struct orinoco_private *priv = netdev_priv(dev); | ||
3101 | int index = (erq->flags & IW_ENCODE_INDEX) - 1; | ||
3102 | u16 xlen = 0; | ||
3103 | unsigned long flags; | ||
3104 | |||
3105 | if (!priv->has_wep) | ||
3106 | return -EOPNOTSUPP; | ||
3107 | |||
3108 | if (orinoco_lock(priv, &flags) != 0) | ||
3109 | return -EBUSY; | ||
3110 | |||
3111 | if ((index < 0) || (index >= ORINOCO_MAX_KEYS)) | ||
3112 | index = priv->tx_key; | ||
3113 | |||
3114 | erq->flags = 0; | ||
3115 | if (!priv->encode_alg) | ||
3116 | erq->flags |= IW_ENCODE_DISABLED; | ||
3117 | erq->flags |= index + 1; | ||
3118 | |||
3119 | if (priv->wep_restrict) | ||
3120 | erq->flags |= IW_ENCODE_RESTRICTED; | ||
3121 | else | ||
3122 | erq->flags |= IW_ENCODE_OPEN; | ||
3123 | |||
3124 | xlen = le16_to_cpu(priv->keys[index].len); | ||
3125 | |||
3126 | erq->length = xlen; | ||
3127 | |||
3128 | memcpy(keybuf, priv->keys[index].data, ORINOCO_MAX_KEY_SIZE); | ||
3129 | |||
3130 | orinoco_unlock(priv, &flags); | ||
3131 | return 0; | ||
3132 | } | ||
3133 | |||
3134 | static int orinoco_ioctl_setessid(struct net_device *dev, | ||
3135 | struct iw_request_info *info, | ||
3136 | struct iw_point *erq, | ||
3137 | char *essidbuf) | ||
3138 | { | ||
3139 | struct orinoco_private *priv = netdev_priv(dev); | ||
3140 | unsigned long flags; | ||
3141 | |||
3142 | /* Note : ESSID is ignored in Ad-Hoc demo mode, but we can set it | ||
3143 | * anyway... - Jean II */ | ||
3144 | |||
3145 | /* Hum... Should not use Wireless Extension constant (may change), | ||
3146 | * should use our own... - Jean II */ | ||
3147 | if (erq->length > IW_ESSID_MAX_SIZE) | ||
3148 | return -E2BIG; | ||
3149 | |||
3150 | if (orinoco_lock(priv, &flags) != 0) | ||
3151 | return -EBUSY; | ||
3152 | |||
3153 | /* NULL the string (for NULL termination & ESSID = ANY) - Jean II */ | ||
3154 | memset(priv->desired_essid, 0, sizeof(priv->desired_essid)); | ||
3155 | |||
3156 | /* If not ANY, get the new ESSID */ | ||
3157 | if (erq->flags) | ||
3158 | memcpy(priv->desired_essid, essidbuf, erq->length); | ||
3159 | |||
3160 | orinoco_unlock(priv, &flags); | ||
3161 | |||
3162 | return -EINPROGRESS; /* Call commit handler */ | ||
3163 | } | ||
3164 | |||
3165 | static int orinoco_ioctl_getessid(struct net_device *dev, | ||
3166 | struct iw_request_info *info, | ||
3167 | struct iw_point *erq, | ||
3168 | char *essidbuf) | ||
3169 | { | ||
3170 | struct orinoco_private *priv = netdev_priv(dev); | ||
3171 | int active; | ||
3172 | int err = 0; | ||
3173 | unsigned long flags; | ||
3174 | |||
3175 | if (netif_running(dev)) { | ||
3176 | err = orinoco_hw_get_essid(priv, &active, essidbuf); | ||
3177 | if (err < 0) | ||
3178 | return err; | ||
3179 | erq->length = err; | ||
3180 | } else { | ||
3181 | if (orinoco_lock(priv, &flags) != 0) | ||
3182 | return -EBUSY; | ||
3183 | memcpy(essidbuf, priv->desired_essid, IW_ESSID_MAX_SIZE); | ||
3184 | erq->length = strlen(priv->desired_essid); | ||
3185 | orinoco_unlock(priv, &flags); | ||
3186 | } | ||
3187 | |||
3188 | erq->flags = 1; | ||
3189 | |||
3190 | return 0; | ||
3191 | } | ||
3192 | |||
3193 | static int orinoco_ioctl_setnick(struct net_device *dev, | ||
3194 | struct iw_request_info *info, | ||
3195 | struct iw_point *nrq, | ||
3196 | char *nickbuf) | ||
3197 | { | ||
3198 | struct orinoco_private *priv = netdev_priv(dev); | ||
3199 | unsigned long flags; | ||
3200 | |||
3201 | if (nrq->length > IW_ESSID_MAX_SIZE) | ||
3202 | return -E2BIG; | ||
3203 | |||
3204 | if (orinoco_lock(priv, &flags) != 0) | ||
3205 | return -EBUSY; | ||
3206 | |||
3207 | memset(priv->nick, 0, sizeof(priv->nick)); | ||
3208 | memcpy(priv->nick, nickbuf, nrq->length); | ||
3209 | |||
3210 | orinoco_unlock(priv, &flags); | ||
3211 | |||
3212 | return -EINPROGRESS; /* Call commit handler */ | ||
3213 | } | ||
3214 | |||
3215 | static int orinoco_ioctl_getnick(struct net_device *dev, | ||
3216 | struct iw_request_info *info, | ||
3217 | struct iw_point *nrq, | ||
3218 | char *nickbuf) | ||
3219 | { | ||
3220 | struct orinoco_private *priv = netdev_priv(dev); | ||
3221 | unsigned long flags; | ||
3222 | |||
3223 | if (orinoco_lock(priv, &flags) != 0) | ||
3224 | return -EBUSY; | ||
3225 | |||
3226 | memcpy(nickbuf, priv->nick, IW_ESSID_MAX_SIZE); | ||
3227 | orinoco_unlock(priv, &flags); | ||
3228 | |||
3229 | nrq->length = strlen(priv->nick); | ||
3230 | |||
3231 | return 0; | ||
3232 | } | ||
3233 | |||
3234 | static int orinoco_ioctl_setfreq(struct net_device *dev, | ||
3235 | struct iw_request_info *info, | ||
3236 | struct iw_freq *frq, | ||
3237 | char *extra) | ||
3238 | { | ||
3239 | struct orinoco_private *priv = netdev_priv(dev); | ||
3240 | int chan = -1; | ||
3241 | unsigned long flags; | ||
3242 | int err = -EINPROGRESS; /* Call commit handler */ | ||
3243 | |||
3244 | /* In infrastructure mode the AP sets the channel */ | ||
3245 | if (priv->iw_mode == IW_MODE_INFRA) | ||
3246 | return -EBUSY; | ||
3247 | |||
3248 | if ((frq->e == 0) && (frq->m <= 1000)) { | ||
3249 | /* Setting by channel number */ | ||
3250 | chan = frq->m; | ||
3251 | } else { | ||
3252 | /* Setting by frequency */ | ||
3253 | int denom = 1; | ||
3254 | int i; | ||
3255 | |||
3256 | /* Calculate denominator to rescale to MHz */ | ||
3257 | for (i = 0; i < (6 - frq->e); i++) | ||
3258 | denom *= 10; | ||
3259 | |||
3260 | chan = ieee80211_freq_to_dsss_chan(frq->m / denom); | ||
3261 | } | ||
3262 | |||
3263 | if ((chan < 1) || (chan > NUM_CHANNELS) || | ||
3264 | !(priv->channel_mask & (1 << (chan-1)))) | ||
3265 | return -EINVAL; | ||
3266 | |||
3267 | if (orinoco_lock(priv, &flags) != 0) | ||
3268 | return -EBUSY; | ||
3269 | |||
3270 | priv->channel = chan; | ||
3271 | if (priv->iw_mode == IW_MODE_MONITOR) { | ||
3272 | /* Fast channel change - no commit if successful */ | ||
3273 | hermes_t *hw = &priv->hw; | ||
3274 | err = hermes_docmd_wait(hw, HERMES_CMD_TEST | | ||
3275 | HERMES_TEST_SET_CHANNEL, | ||
3276 | chan, NULL); | ||
3277 | } | ||
3278 | orinoco_unlock(priv, &flags); | ||
3279 | |||
3280 | return err; | ||
3281 | } | ||
3282 | |||
3283 | static int orinoco_ioctl_getfreq(struct net_device *dev, | ||
3284 | struct iw_request_info *info, | ||
3285 | struct iw_freq *frq, | ||
3286 | char *extra) | ||
3287 | { | ||
3288 | struct orinoco_private *priv = netdev_priv(dev); | ||
3289 | int tmp; | ||
3290 | |||
3291 | /* Locking done in there */ | ||
3292 | tmp = orinoco_hw_get_freq(priv); | ||
3293 | if (tmp < 0) | ||
3294 | return tmp; | ||
3295 | |||
3296 | frq->m = tmp * 100000; | ||
3297 | frq->e = 1; | ||
3298 | |||
3299 | return 0; | ||
3300 | } | ||
3301 | |||
3302 | static int orinoco_ioctl_getsens(struct net_device *dev, | ||
3303 | struct iw_request_info *info, | ||
3304 | struct iw_param *srq, | ||
3305 | char *extra) | ||
3306 | { | ||
3307 | struct orinoco_private *priv = netdev_priv(dev); | ||
3308 | hermes_t *hw = &priv->hw; | ||
3309 | u16 val; | ||
3310 | int err; | ||
3311 | unsigned long flags; | ||
3312 | |||
3313 | if (!priv->has_sensitivity) | ||
3314 | return -EOPNOTSUPP; | ||
3315 | |||
3316 | if (orinoco_lock(priv, &flags) != 0) | ||
3317 | return -EBUSY; | ||
3318 | err = hermes_read_wordrec(hw, USER_BAP, | ||
3319 | HERMES_RID_CNFSYSTEMSCALE, &val); | ||
3320 | orinoco_unlock(priv, &flags); | ||
3321 | |||
3322 | if (err) | ||
3323 | return err; | ||
3324 | |||
3325 | srq->value = val; | ||
3326 | srq->fixed = 0; /* auto */ | ||
3327 | |||
3328 | return 0; | ||
3329 | } | ||
3330 | |||
3331 | static int orinoco_ioctl_setsens(struct net_device *dev, | ||
3332 | struct iw_request_info *info, | ||
3333 | struct iw_param *srq, | ||
3334 | char *extra) | ||
3335 | { | ||
3336 | struct orinoco_private *priv = netdev_priv(dev); | ||
3337 | int val = srq->value; | ||
3338 | unsigned long flags; | ||
3339 | |||
3340 | if (!priv->has_sensitivity) | ||
3341 | return -EOPNOTSUPP; | ||
3342 | |||
3343 | if ((val < 1) || (val > 3)) | ||
3344 | return -EINVAL; | ||
3345 | |||
3346 | if (orinoco_lock(priv, &flags) != 0) | ||
3347 | return -EBUSY; | ||
3348 | priv->ap_density = val; | ||
3349 | orinoco_unlock(priv, &flags); | ||
3350 | |||
3351 | return -EINPROGRESS; /* Call commit handler */ | ||
3352 | } | ||
3353 | |||
3354 | static int orinoco_ioctl_setrts(struct net_device *dev, | ||
3355 | struct iw_request_info *info, | ||
3356 | struct iw_param *rrq, | ||
3357 | char *extra) | ||
3358 | { | ||
3359 | struct orinoco_private *priv = netdev_priv(dev); | ||
3360 | int val = rrq->value; | ||
3361 | unsigned long flags; | ||
3362 | |||
3363 | if (rrq->disabled) | ||
3364 | val = 2347; | ||
3365 | |||
3366 | if ((val < 0) || (val > 2347)) | ||
3367 | return -EINVAL; | ||
3368 | |||
3369 | if (orinoco_lock(priv, &flags) != 0) | ||
3370 | return -EBUSY; | ||
3371 | |||
3372 | priv->rts_thresh = val; | ||
3373 | orinoco_unlock(priv, &flags); | ||
3374 | |||
3375 | return -EINPROGRESS; /* Call commit handler */ | ||
3376 | } | ||
3377 | |||
3378 | static int orinoco_ioctl_getrts(struct net_device *dev, | ||
3379 | struct iw_request_info *info, | ||
3380 | struct iw_param *rrq, | ||
3381 | char *extra) | ||
3382 | { | ||
3383 | struct orinoco_private *priv = netdev_priv(dev); | ||
3384 | |||
3385 | rrq->value = priv->rts_thresh; | ||
3386 | rrq->disabled = (rrq->value == 2347); | ||
3387 | rrq->fixed = 1; | ||
3388 | |||
3389 | return 0; | ||
3390 | } | ||
3391 | |||
3392 | static int orinoco_ioctl_setfrag(struct net_device *dev, | ||
3393 | struct iw_request_info *info, | ||
3394 | struct iw_param *frq, | ||
3395 | char *extra) | ||
3396 | { | ||
3397 | struct orinoco_private *priv = netdev_priv(dev); | ||
3398 | int err = -EINPROGRESS; /* Call commit handler */ | ||
3399 | unsigned long flags; | ||
3400 | |||
3401 | if (orinoco_lock(priv, &flags) != 0) | ||
3402 | return -EBUSY; | ||
3403 | |||
3404 | if (priv->has_mwo) { | ||
3405 | if (frq->disabled) | ||
3406 | priv->mwo_robust = 0; | ||
3407 | else { | ||
3408 | if (frq->fixed) | ||
3409 | printk(KERN_WARNING "%s: Fixed fragmentation " | ||
3410 | "is not supported on this firmware. " | ||
3411 | "Using MWO robust instead.\n", | ||
3412 | dev->name); | ||
3413 | priv->mwo_robust = 1; | ||
3414 | } | ||
3415 | } else { | ||
3416 | if (frq->disabled) | ||
3417 | priv->frag_thresh = 2346; | ||
3418 | else { | ||
3419 | if ((frq->value < 256) || (frq->value > 2346)) | ||
3420 | err = -EINVAL; | ||
3421 | else | ||
3422 | /* must be even */ | ||
3423 | priv->frag_thresh = frq->value & ~0x1; | ||
3424 | } | ||
3425 | } | ||
3426 | |||
3427 | orinoco_unlock(priv, &flags); | ||
3428 | |||
3429 | return err; | ||
3430 | } | ||
3431 | |||
3432 | static int orinoco_ioctl_getfrag(struct net_device *dev, | ||
3433 | struct iw_request_info *info, | ||
3434 | struct iw_param *frq, | ||
3435 | char *extra) | ||
3436 | { | ||
3437 | struct orinoco_private *priv = netdev_priv(dev); | ||
3438 | hermes_t *hw = &priv->hw; | ||
3439 | int err; | ||
3440 | u16 val; | ||
3441 | unsigned long flags; | ||
3442 | |||
3443 | if (orinoco_lock(priv, &flags) != 0) | ||
3444 | return -EBUSY; | ||
3445 | |||
3446 | if (priv->has_mwo) { | ||
3447 | err = hermes_read_wordrec(hw, USER_BAP, | ||
3448 | HERMES_RID_CNFMWOROBUST_AGERE, | ||
3449 | &val); | ||
3450 | if (err) | ||
3451 | val = 0; | ||
3452 | |||
3453 | frq->value = val ? 2347 : 0; | ||
3454 | frq->disabled = !val; | ||
3455 | frq->fixed = 0; | ||
3456 | } else { | ||
3457 | err = hermes_read_wordrec(hw, USER_BAP, | ||
3458 | HERMES_RID_CNFFRAGMENTATIONTHRESHOLD, | ||
3459 | &val); | ||
3460 | if (err) | ||
3461 | val = 0; | ||
3462 | |||
3463 | frq->value = val; | ||
3464 | frq->disabled = (val >= 2346); | ||
3465 | frq->fixed = 1; | ||
3466 | } | ||
3467 | |||
3468 | orinoco_unlock(priv, &flags); | ||
3469 | |||
3470 | return err; | ||
3471 | } | ||
3472 | |||
3473 | static int orinoco_ioctl_setrate(struct net_device *dev, | ||
3474 | struct iw_request_info *info, | ||
3475 | struct iw_param *rrq, | ||
3476 | char *extra) | ||
3477 | { | ||
3478 | struct orinoco_private *priv = netdev_priv(dev); | ||
3479 | int ratemode; | ||
3480 | int bitrate; /* 100s of kilobits */ | ||
3481 | unsigned long flags; | ||
3482 | |||
3483 | /* As the user space doesn't know our highest rate, it uses -1 | ||
3484 | * to ask us to set the highest rate. Test it using "iwconfig | ||
3485 | * ethX rate auto" - Jean II */ | ||
3486 | if (rrq->value == -1) | ||
3487 | bitrate = 110; | ||
3488 | else { | ||
3489 | if (rrq->value % 100000) | ||
3490 | return -EINVAL; | ||
3491 | bitrate = rrq->value / 100000; | ||
3492 | } | ||
3493 | |||
3494 | ratemode = orinoco_get_bitratemode(bitrate, !rrq->fixed); | ||
3495 | |||
3496 | if (ratemode == -1) | ||
3497 | return -EINVAL; | ||
3498 | |||
3499 | if (orinoco_lock(priv, &flags) != 0) | ||
3500 | return -EBUSY; | ||
3501 | priv->bitratemode = ratemode; | ||
3502 | orinoco_unlock(priv, &flags); | ||
3503 | |||
3504 | return -EINPROGRESS; | ||
3505 | } | ||
3506 | |||
3507 | static int orinoco_ioctl_getrate(struct net_device *dev, | ||
3508 | struct iw_request_info *info, | ||
3509 | struct iw_param *rrq, | ||
3510 | char *extra) | ||
3511 | { | ||
3512 | struct orinoco_private *priv = netdev_priv(dev); | ||
3513 | int err = 0; | ||
3514 | int bitrate, automatic; | ||
3515 | unsigned long flags; | ||
3516 | |||
3517 | if (orinoco_lock(priv, &flags) != 0) | ||
3518 | return -EBUSY; | ||
3519 | |||
3520 | orinoco_get_ratemode_cfg(priv->bitratemode, &bitrate, &automatic); | ||
3521 | |||
3522 | /* If the interface is running we try to find more about the | ||
3523 | current mode */ | ||
3524 | if (netif_running(dev)) | ||
3525 | err = orinoco_hw_get_act_bitrate(priv, &bitrate); | ||
3526 | |||
3527 | orinoco_unlock(priv, &flags); | ||
3528 | |||
3529 | rrq->value = bitrate; | ||
3530 | rrq->fixed = !automatic; | ||
3531 | rrq->disabled = 0; | ||
3532 | |||
3533 | return err; | ||
3534 | } | ||
3535 | |||
3536 | static int orinoco_ioctl_setpower(struct net_device *dev, | ||
3537 | struct iw_request_info *info, | ||
3538 | struct iw_param *prq, | ||
3539 | char *extra) | ||
3540 | { | ||
3541 | struct orinoco_private *priv = netdev_priv(dev); | ||
3542 | int err = -EINPROGRESS; /* Call commit handler */ | ||
3543 | unsigned long flags; | ||
3544 | |||
3545 | if (orinoco_lock(priv, &flags) != 0) | ||
3546 | return -EBUSY; | ||
3547 | |||
3548 | if (prq->disabled) { | ||
3549 | priv->pm_on = 0; | ||
3550 | } else { | ||
3551 | switch (prq->flags & IW_POWER_MODE) { | ||
3552 | case IW_POWER_UNICAST_R: | ||
3553 | priv->pm_mcast = 0; | ||
3554 | priv->pm_on = 1; | ||
3555 | break; | ||
3556 | case IW_POWER_ALL_R: | ||
3557 | priv->pm_mcast = 1; | ||
3558 | priv->pm_on = 1; | ||
3559 | break; | ||
3560 | case IW_POWER_ON: | ||
3561 | /* No flags : but we may have a value - Jean II */ | ||
3562 | break; | ||
3563 | default: | ||
3564 | err = -EINVAL; | ||
3565 | goto out; | ||
3566 | } | ||
3567 | |||
3568 | if (prq->flags & IW_POWER_TIMEOUT) { | ||
3569 | priv->pm_on = 1; | ||
3570 | priv->pm_timeout = prq->value / 1000; | ||
3571 | } | ||
3572 | if (prq->flags & IW_POWER_PERIOD) { | ||
3573 | priv->pm_on = 1; | ||
3574 | priv->pm_period = prq->value / 1000; | ||
3575 | } | ||
3576 | /* It's valid to not have a value if we are just toggling | ||
3577 | * the flags... Jean II */ | ||
3578 | if (!priv->pm_on) { | ||
3579 | err = -EINVAL; | ||
3580 | goto out; | ||
3581 | } | ||
3582 | } | ||
3583 | |||
3584 | out: | ||
3585 | orinoco_unlock(priv, &flags); | ||
3586 | |||
3587 | return err; | ||
3588 | } | ||
3589 | |||
3590 | static int orinoco_ioctl_getpower(struct net_device *dev, | ||
3591 | struct iw_request_info *info, | ||
3592 | struct iw_param *prq, | ||
3593 | char *extra) | ||
3594 | { | ||
3595 | struct orinoco_private *priv = netdev_priv(dev); | ||
3596 | hermes_t *hw = &priv->hw; | ||
3597 | int err = 0; | ||
3598 | u16 enable, period, timeout, mcast; | ||
3599 | unsigned long flags; | ||
3600 | |||
3601 | if (orinoco_lock(priv, &flags) != 0) | ||
3602 | return -EBUSY; | ||
3603 | |||
3604 | err = hermes_read_wordrec(hw, USER_BAP, | ||
3605 | HERMES_RID_CNFPMENABLED, &enable); | ||
3606 | if (err) | ||
3607 | goto out; | ||
3608 | |||
3609 | err = hermes_read_wordrec(hw, USER_BAP, | ||
3610 | HERMES_RID_CNFMAXSLEEPDURATION, &period); | ||
3611 | if (err) | ||
3612 | goto out; | ||
3613 | |||
3614 | err = hermes_read_wordrec(hw, USER_BAP, | ||
3615 | HERMES_RID_CNFPMHOLDOVERDURATION, &timeout); | ||
3616 | if (err) | ||
3617 | goto out; | ||
3618 | |||
3619 | err = hermes_read_wordrec(hw, USER_BAP, | ||
3620 | HERMES_RID_CNFMULTICASTRECEIVE, &mcast); | ||
3621 | if (err) | ||
3622 | goto out; | ||
3623 | |||
3624 | prq->disabled = !enable; | ||
3625 | /* Note : by default, display the period */ | ||
3626 | if ((prq->flags & IW_POWER_TYPE) == IW_POWER_TIMEOUT) { | ||
3627 | prq->flags = IW_POWER_TIMEOUT; | ||
3628 | prq->value = timeout * 1000; | ||
3629 | } else { | ||
3630 | prq->flags = IW_POWER_PERIOD; | ||
3631 | prq->value = period * 1000; | ||
3632 | } | ||
3633 | if (mcast) | ||
3634 | prq->flags |= IW_POWER_ALL_R; | ||
3635 | else | ||
3636 | prq->flags |= IW_POWER_UNICAST_R; | ||
3637 | |||
3638 | out: | ||
3639 | orinoco_unlock(priv, &flags); | ||
3640 | |||
3641 | return err; | ||
3642 | } | ||
3643 | |||
3644 | static int orinoco_ioctl_set_encodeext(struct net_device *dev, | ||
3645 | struct iw_request_info *info, | ||
3646 | union iwreq_data *wrqu, | ||
3647 | char *extra) | ||
3648 | { | ||
3649 | struct orinoco_private *priv = netdev_priv(dev); | ||
3650 | struct iw_point *encoding = &wrqu->encoding; | ||
3651 | struct iw_encode_ext *ext = (struct iw_encode_ext *)extra; | ||
3652 | int idx, alg = ext->alg, set_key = 1; | ||
3653 | unsigned long flags; | ||
3654 | int err = -EINVAL; | ||
3655 | u16 key_len; | ||
3656 | |||
3657 | if (orinoco_lock(priv, &flags) != 0) | ||
3658 | return -EBUSY; | ||
3659 | |||
3660 | /* Determine and validate the key index */ | ||
3661 | idx = encoding->flags & IW_ENCODE_INDEX; | ||
3662 | if (idx) { | ||
3663 | if ((idx < 1) || (idx > 4)) | ||
3664 | goto out; | ||
3665 | idx--; | ||
3666 | } else | ||
3667 | idx = priv->tx_key; | ||
3668 | |||
3669 | if (encoding->flags & IW_ENCODE_DISABLED) | ||
3670 | alg = IW_ENCODE_ALG_NONE; | ||
3671 | |||
3672 | if (priv->has_wpa && (alg != IW_ENCODE_ALG_TKIP)) { | ||
3673 | /* Clear any TKIP TX key we had */ | ||
3674 | (void) orinoco_clear_tkip_key(priv, priv->tx_key); | ||
3675 | } | ||
3676 | |||
3677 | if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) { | ||
3678 | priv->tx_key = idx; | ||
3679 | set_key = ((alg == IW_ENCODE_ALG_TKIP) || | ||
3680 | (ext->key_len > 0)) ? 1 : 0; | ||
3681 | } | ||
3682 | |||
3683 | if (set_key) { | ||
3684 | /* Set the requested key first */ | ||
3685 | switch (alg) { | ||
3686 | case IW_ENCODE_ALG_NONE: | ||
3687 | priv->encode_alg = alg; | ||
3688 | priv->keys[idx].len = 0; | ||
3689 | break; | ||
3690 | |||
3691 | case IW_ENCODE_ALG_WEP: | ||
3692 | if (ext->key_len > SMALL_KEY_SIZE) | ||
3693 | key_len = LARGE_KEY_SIZE; | ||
3694 | else if (ext->key_len > 0) | ||
3695 | key_len = SMALL_KEY_SIZE; | ||
3696 | else | ||
3697 | goto out; | ||
3698 | |||
3699 | priv->encode_alg = alg; | ||
3700 | priv->keys[idx].len = cpu_to_le16(key_len); | ||
3701 | |||
3702 | key_len = min(ext->key_len, key_len); | ||
3703 | |||
3704 | memset(priv->keys[idx].data, 0, ORINOCO_MAX_KEY_SIZE); | ||
3705 | memcpy(priv->keys[idx].data, ext->key, key_len); | ||
3706 | break; | ||
3707 | |||
3708 | case IW_ENCODE_ALG_TKIP: | ||
3709 | { | ||
3710 | hermes_t *hw = &priv->hw; | ||
3711 | u8 *tkip_iv = NULL; | ||
3712 | |||
3713 | if (!priv->has_wpa || | ||
3714 | (ext->key_len > sizeof(priv->tkip_key[0]))) | ||
3715 | goto out; | ||
3716 | |||
3717 | priv->encode_alg = alg; | ||
3718 | memset(&priv->tkip_key[idx], 0, | ||
3719 | sizeof(priv->tkip_key[idx])); | ||
3720 | memcpy(&priv->tkip_key[idx], ext->key, ext->key_len); | ||
3721 | |||
3722 | if (ext->ext_flags & IW_ENCODE_EXT_RX_SEQ_VALID) | ||
3723 | tkip_iv = &ext->rx_seq[0]; | ||
3724 | |||
3725 | err = __orinoco_hw_set_tkip_key(hw, idx, | ||
3726 | ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY, | ||
3727 | (u8 *) &priv->tkip_key[idx], | ||
3728 | tkip_iv, NULL); | ||
3729 | if (err) | ||
3730 | printk(KERN_ERR "%s: Error %d setting TKIP key" | ||
3731 | "\n", dev->name, err); | ||
3732 | |||
3733 | goto out; | ||
3734 | } | ||
3735 | default: | ||
3736 | goto out; | ||
3737 | } | ||
3738 | } | ||
3739 | err = -EINPROGRESS; | ||
3740 | out: | ||
3741 | orinoco_unlock(priv, &flags); | ||
3742 | |||
3743 | return err; | ||
3744 | } | ||
3745 | |||
3746 | static int orinoco_ioctl_get_encodeext(struct net_device *dev, | ||
3747 | struct iw_request_info *info, | ||
3748 | union iwreq_data *wrqu, | ||
3749 | char *extra) | ||
3750 | { | ||
3751 | struct orinoco_private *priv = netdev_priv(dev); | ||
3752 | struct iw_point *encoding = &wrqu->encoding; | ||
3753 | struct iw_encode_ext *ext = (struct iw_encode_ext *)extra; | ||
3754 | int idx, max_key_len; | ||
3755 | unsigned long flags; | ||
3756 | int err; | ||
3757 | |||
3758 | if (orinoco_lock(priv, &flags) != 0) | ||
3759 | return -EBUSY; | ||
3760 | |||
3761 | err = -EINVAL; | ||
3762 | max_key_len = encoding->length - sizeof(*ext); | ||
3763 | if (max_key_len < 0) | ||
3764 | goto out; | ||
3765 | |||
3766 | idx = encoding->flags & IW_ENCODE_INDEX; | ||
3767 | if (idx) { | ||
3768 | if ((idx < 1) || (idx > 4)) | ||
3769 | goto out; | ||
3770 | idx--; | ||
3771 | } else | ||
3772 | idx = priv->tx_key; | ||
3773 | |||
3774 | encoding->flags = idx + 1; | ||
3775 | memset(ext, 0, sizeof(*ext)); | ||
3776 | |||
3777 | ext->alg = priv->encode_alg; | ||
3778 | switch (priv->encode_alg) { | ||
3779 | case IW_ENCODE_ALG_NONE: | ||
3780 | ext->key_len = 0; | ||
3781 | encoding->flags |= IW_ENCODE_DISABLED; | ||
3782 | break; | ||
3783 | case IW_ENCODE_ALG_WEP: | ||
3784 | ext->key_len = min_t(u16, le16_to_cpu(priv->keys[idx].len), | ||
3785 | max_key_len); | ||
3786 | memcpy(ext->key, priv->keys[idx].data, ext->key_len); | ||
3787 | encoding->flags |= IW_ENCODE_ENABLED; | ||
3788 | break; | ||
3789 | case IW_ENCODE_ALG_TKIP: | ||
3790 | ext->key_len = min_t(u16, sizeof(struct orinoco_tkip_key), | ||
3791 | max_key_len); | ||
3792 | memcpy(ext->key, &priv->tkip_key[idx], ext->key_len); | ||
3793 | encoding->flags |= IW_ENCODE_ENABLED; | ||
3794 | break; | ||
3795 | } | ||
3796 | |||
3797 | err = 0; | ||
3798 | out: | ||
3799 | orinoco_unlock(priv, &flags); | ||
3800 | |||
3801 | return err; | ||
3802 | } | ||
3803 | |||
3804 | static int orinoco_ioctl_set_auth(struct net_device *dev, | ||
3805 | struct iw_request_info *info, | ||
3806 | union iwreq_data *wrqu, char *extra) | ||
3807 | { | ||
3808 | struct orinoco_private *priv = netdev_priv(dev); | ||
3809 | hermes_t *hw = &priv->hw; | ||
3810 | struct iw_param *param = &wrqu->param; | ||
3811 | unsigned long flags; | ||
3812 | int ret = -EINPROGRESS; | ||
3813 | |||
3814 | if (orinoco_lock(priv, &flags) != 0) | ||
3815 | return -EBUSY; | ||
3816 | |||
3817 | switch (param->flags & IW_AUTH_INDEX) { | ||
3818 | case IW_AUTH_WPA_VERSION: | ||
3819 | case IW_AUTH_CIPHER_PAIRWISE: | ||
3820 | case IW_AUTH_CIPHER_GROUP: | ||
3821 | case IW_AUTH_RX_UNENCRYPTED_EAPOL: | ||
3822 | case IW_AUTH_PRIVACY_INVOKED: | ||
3823 | case IW_AUTH_DROP_UNENCRYPTED: | ||
3824 | /* | ||
3825 | * orinoco does not use these parameters | ||
3826 | */ | ||
3827 | break; | ||
3828 | |||
3829 | case IW_AUTH_KEY_MGMT: | ||
3830 | /* wl_lkm implies value 2 == PSK for Hermes I | ||
3831 | * which ties in with WEXT | ||
3832 | * no other hints tho :( | ||
3833 | */ | ||
3834 | priv->key_mgmt = param->value; | ||
3835 | break; | ||
3836 | |||
3837 | case IW_AUTH_TKIP_COUNTERMEASURES: | ||
3838 | /* When countermeasures are enabled, shut down the | ||
3839 | * card; when disabled, re-enable the card. This must | ||
3840 | * take effect immediately. | ||
3841 | * | ||
3842 | * TODO: Make sure that the EAPOL message is getting | ||
3843 | * out before card disabled | ||
3844 | */ | ||
3845 | if (param->value) { | ||
3846 | priv->tkip_cm_active = 1; | ||
3847 | ret = hermes_enable_port(hw, 0); | ||
3848 | } else { | ||
3849 | priv->tkip_cm_active = 0; | ||
3850 | ret = hermes_disable_port(hw, 0); | ||
3851 | } | ||
3852 | break; | ||
3853 | |||
3854 | case IW_AUTH_80211_AUTH_ALG: | ||
3855 | if (param->value & IW_AUTH_ALG_SHARED_KEY) | ||
3856 | priv->wep_restrict = 1; | ||
3857 | else if (param->value & IW_AUTH_ALG_OPEN_SYSTEM) | ||
3858 | priv->wep_restrict = 0; | ||
3859 | else | ||
3860 | ret = -EINVAL; | ||
3861 | break; | ||
3862 | |||
3863 | case IW_AUTH_WPA_ENABLED: | ||
3864 | if (priv->has_wpa) { | ||
3865 | priv->wpa_enabled = param->value ? 1 : 0; | ||
3866 | } else { | ||
3867 | if (param->value) | ||
3868 | ret = -EOPNOTSUPP; | ||
3869 | /* else silently accept disable of WPA */ | ||
3870 | priv->wpa_enabled = 0; | ||
3871 | } | ||
3872 | break; | ||
3873 | |||
3874 | default: | ||
3875 | ret = -EOPNOTSUPP; | ||
3876 | } | ||
3877 | |||
3878 | orinoco_unlock(priv, &flags); | ||
3879 | return ret; | ||
3880 | } | ||
3881 | |||
3882 | static int orinoco_ioctl_get_auth(struct net_device *dev, | ||
3883 | struct iw_request_info *info, | ||
3884 | union iwreq_data *wrqu, char *extra) | ||
3885 | { | ||
3886 | struct orinoco_private *priv = netdev_priv(dev); | ||
3887 | struct iw_param *param = &wrqu->param; | ||
3888 | unsigned long flags; | ||
3889 | int ret = 0; | ||
3890 | |||
3891 | if (orinoco_lock(priv, &flags) != 0) | ||
3892 | return -EBUSY; | ||
3893 | |||
3894 | switch (param->flags & IW_AUTH_INDEX) { | ||
3895 | case IW_AUTH_KEY_MGMT: | ||
3896 | param->value = priv->key_mgmt; | ||
3897 | break; | ||
3898 | |||
3899 | case IW_AUTH_TKIP_COUNTERMEASURES: | ||
3900 | param->value = priv->tkip_cm_active; | ||
3901 | break; | ||
3902 | |||
3903 | case IW_AUTH_80211_AUTH_ALG: | ||
3904 | if (priv->wep_restrict) | ||
3905 | param->value = IW_AUTH_ALG_SHARED_KEY; | ||
3906 | else | ||
3907 | param->value = IW_AUTH_ALG_OPEN_SYSTEM; | ||
3908 | break; | ||
3909 | |||
3910 | case IW_AUTH_WPA_ENABLED: | ||
3911 | param->value = priv->wpa_enabled; | ||
3912 | break; | ||
3913 | |||
3914 | default: | ||
3915 | ret = -EOPNOTSUPP; | ||
3916 | } | ||
3917 | |||
3918 | orinoco_unlock(priv, &flags); | ||
3919 | return ret; | ||
3920 | } | ||
3921 | |||
3922 | static int orinoco_ioctl_set_genie(struct net_device *dev, | ||
3923 | struct iw_request_info *info, | ||
3924 | union iwreq_data *wrqu, char *extra) | ||
3925 | { | ||
3926 | struct orinoco_private *priv = netdev_priv(dev); | ||
3927 | u8 *buf; | ||
3928 | unsigned long flags; | ||
3929 | |||
3930 | /* cut off at IEEE80211_MAX_DATA_LEN */ | ||
3931 | if ((wrqu->data.length > IEEE80211_MAX_DATA_LEN) || | ||
3932 | (wrqu->data.length && (extra == NULL))) | ||
3933 | return -EINVAL; | ||
3934 | |||
3935 | if (wrqu->data.length) { | ||
3936 | buf = kmalloc(wrqu->data.length, GFP_KERNEL); | ||
3937 | if (buf == NULL) | ||
3938 | return -ENOMEM; | ||
3939 | |||
3940 | memcpy(buf, extra, wrqu->data.length); | ||
3941 | } else | ||
3942 | buf = NULL; | ||
3943 | |||
3944 | if (orinoco_lock(priv, &flags) != 0) { | ||
3945 | kfree(buf); | ||
3946 | return -EBUSY; | ||
3947 | } | ||
3948 | |||
3949 | kfree(priv->wpa_ie); | ||
3950 | priv->wpa_ie = buf; | ||
3951 | priv->wpa_ie_len = wrqu->data.length; | ||
3952 | |||
3953 | if (priv->wpa_ie) { | ||
3954 | /* Looks like wl_lkm wants to check the auth alg, and | ||
3955 | * somehow pass it to the firmware. | ||
3956 | * Instead it just calls the key mgmt rid | ||
3957 | * - we do this in set auth. | ||
3958 | */ | ||
3959 | } | ||
3960 | |||
3961 | orinoco_unlock(priv, &flags); | ||
3962 | return 0; | ||
3963 | } | ||
3964 | |||
3965 | static int orinoco_ioctl_get_genie(struct net_device *dev, | ||
3966 | struct iw_request_info *info, | ||
3967 | union iwreq_data *wrqu, char *extra) | ||
3968 | { | ||
3969 | struct orinoco_private *priv = netdev_priv(dev); | ||
3970 | unsigned long flags; | ||
3971 | int err = 0; | ||
3972 | |||
3973 | if (orinoco_lock(priv, &flags) != 0) | ||
3974 | return -EBUSY; | ||
3975 | |||
3976 | if ((priv->wpa_ie_len == 0) || (priv->wpa_ie == NULL)) { | ||
3977 | wrqu->data.length = 0; | ||
3978 | goto out; | ||
3979 | } | ||
3980 | |||
3981 | if (wrqu->data.length < priv->wpa_ie_len) { | ||
3982 | err = -E2BIG; | ||
3983 | goto out; | ||
3984 | } | ||
3985 | |||
3986 | wrqu->data.length = priv->wpa_ie_len; | ||
3987 | memcpy(extra, priv->wpa_ie, priv->wpa_ie_len); | ||
3988 | |||
3989 | out: | ||
3990 | orinoco_unlock(priv, &flags); | ||
3991 | return err; | ||
3992 | } | ||
3993 | |||
3994 | static int orinoco_ioctl_set_mlme(struct net_device *dev, | ||
3995 | struct iw_request_info *info, | ||
3996 | union iwreq_data *wrqu, char *extra) | ||
3997 | { | ||
3998 | struct orinoco_private *priv = netdev_priv(dev); | ||
3999 | hermes_t *hw = &priv->hw; | ||
4000 | struct iw_mlme *mlme = (struct iw_mlme *)extra; | ||
4001 | unsigned long flags; | ||
4002 | int ret = 0; | ||
4003 | |||
4004 | if (orinoco_lock(priv, &flags) != 0) | ||
4005 | return -EBUSY; | ||
4006 | |||
4007 | switch (mlme->cmd) { | ||
4008 | case IW_MLME_DEAUTH: | ||
4009 | /* silently ignore */ | ||
4010 | break; | ||
4011 | |||
4012 | case IW_MLME_DISASSOC: | ||
4013 | { | ||
4014 | struct { | ||
4015 | u8 addr[ETH_ALEN]; | ||
4016 | __le16 reason_code; | ||
4017 | } __attribute__ ((packed)) buf; | ||
4018 | |||
4019 | memcpy(buf.addr, mlme->addr.sa_data, ETH_ALEN); | ||
4020 | buf.reason_code = cpu_to_le16(mlme->reason_code); | ||
4021 | ret = HERMES_WRITE_RECORD(hw, USER_BAP, | ||
4022 | HERMES_RID_CNFDISASSOCIATE, | ||
4023 | &buf); | ||
4024 | break; | ||
4025 | } | ||
4026 | default: | ||
4027 | ret = -EOPNOTSUPP; | ||
4028 | } | ||
4029 | |||
4030 | orinoco_unlock(priv, &flags); | ||
4031 | return ret; | ||
4032 | } | ||
4033 | |||
4034 | static int orinoco_ioctl_getretry(struct net_device *dev, | ||
4035 | struct iw_request_info *info, | ||
4036 | struct iw_param *rrq, | ||
4037 | char *extra) | ||
4038 | { | ||
4039 | struct orinoco_private *priv = netdev_priv(dev); | ||
4040 | hermes_t *hw = &priv->hw; | ||
4041 | int err = 0; | ||
4042 | u16 short_limit, long_limit, lifetime; | ||
4043 | unsigned long flags; | ||
4044 | |||
4045 | if (orinoco_lock(priv, &flags) != 0) | ||
4046 | return -EBUSY; | ||
4047 | |||
4048 | err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_SHORTRETRYLIMIT, | ||
4049 | &short_limit); | ||
4050 | if (err) | ||
4051 | goto out; | ||
4052 | |||
4053 | err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_LONGRETRYLIMIT, | ||
4054 | &long_limit); | ||
4055 | if (err) | ||
4056 | goto out; | ||
4057 | |||
4058 | err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_MAXTRANSMITLIFETIME, | ||
4059 | &lifetime); | ||
4060 | if (err) | ||
4061 | goto out; | ||
4062 | |||
4063 | rrq->disabled = 0; /* Can't be disabled */ | ||
4064 | |||
4065 | /* Note : by default, display the retry number */ | ||
4066 | if ((rrq->flags & IW_RETRY_TYPE) == IW_RETRY_LIFETIME) { | ||
4067 | rrq->flags = IW_RETRY_LIFETIME; | ||
4068 | rrq->value = lifetime * 1000; /* ??? */ | ||
4069 | } else { | ||
4070 | /* By default, display the min number */ | ||
4071 | if ((rrq->flags & IW_RETRY_LONG)) { | ||
4072 | rrq->flags = IW_RETRY_LIMIT | IW_RETRY_LONG; | ||
4073 | rrq->value = long_limit; | ||
4074 | } else { | ||
4075 | rrq->flags = IW_RETRY_LIMIT; | ||
4076 | rrq->value = short_limit; | ||
4077 | if (short_limit != long_limit) | ||
4078 | rrq->flags |= IW_RETRY_SHORT; | ||
4079 | } | ||
4080 | } | ||
4081 | |||
4082 | out: | ||
4083 | orinoco_unlock(priv, &flags); | ||
4084 | |||
4085 | return err; | ||
4086 | } | ||
4087 | |||
4088 | static int orinoco_ioctl_reset(struct net_device *dev, | ||
4089 | struct iw_request_info *info, | ||
4090 | void *wrqu, | ||
4091 | char *extra) | ||
4092 | { | ||
4093 | struct orinoco_private *priv = netdev_priv(dev); | ||
4094 | |||
4095 | if (!capable(CAP_NET_ADMIN)) | ||
4096 | return -EPERM; | ||
4097 | |||
4098 | if (info->cmd == (SIOCIWFIRSTPRIV + 0x1)) { | ||
4099 | printk(KERN_DEBUG "%s: Forcing reset!\n", dev->name); | ||
4100 | |||
4101 | /* Firmware reset */ | ||
4102 | orinoco_reset(&priv->reset_work); | ||
4103 | } else { | ||
4104 | printk(KERN_DEBUG "%s: Force scheduling reset!\n", dev->name); | ||
4105 | |||
4106 | schedule_work(&priv->reset_work); | ||
4107 | } | ||
4108 | |||
4109 | return 0; | ||
4110 | } | ||
4111 | |||
4112 | static int orinoco_ioctl_setibssport(struct net_device *dev, | ||
4113 | struct iw_request_info *info, | ||
4114 | void *wrqu, | ||
4115 | char *extra) | ||
4116 | |||
4117 | { | ||
4118 | struct orinoco_private *priv = netdev_priv(dev); | ||
4119 | int val = *((int *) extra); | ||
4120 | unsigned long flags; | ||
4121 | |||
4122 | if (orinoco_lock(priv, &flags) != 0) | ||
4123 | return -EBUSY; | ||
4124 | |||
4125 | priv->ibss_port = val ; | ||
4126 | |||
4127 | /* Actually update the mode we are using */ | ||
4128 | set_port_type(priv); | ||
4129 | |||
4130 | orinoco_unlock(priv, &flags); | ||
4131 | return -EINPROGRESS; /* Call commit handler */ | ||
4132 | } | ||
4133 | |||
4134 | static int orinoco_ioctl_getibssport(struct net_device *dev, | ||
4135 | struct iw_request_info *info, | ||
4136 | void *wrqu, | ||
4137 | char *extra) | ||
4138 | { | ||
4139 | struct orinoco_private *priv = netdev_priv(dev); | ||
4140 | int *val = (int *) extra; | ||
4141 | |||
4142 | *val = priv->ibss_port; | ||
4143 | return 0; | ||
4144 | } | ||
4145 | |||
4146 | static int orinoco_ioctl_setport3(struct net_device *dev, | ||
4147 | struct iw_request_info *info, | ||
4148 | void *wrqu, | ||
4149 | char *extra) | ||
4150 | { | ||
4151 | struct orinoco_private *priv = netdev_priv(dev); | ||
4152 | int val = *((int *) extra); | ||
4153 | int err = 0; | ||
4154 | unsigned long flags; | ||
4155 | |||
4156 | if (orinoco_lock(priv, &flags) != 0) | ||
4157 | return -EBUSY; | ||
4158 | |||
4159 | switch (val) { | ||
4160 | case 0: /* Try to do IEEE ad-hoc mode */ | ||
4161 | if (!priv->has_ibss) { | ||
4162 | err = -EINVAL; | ||
4163 | break; | ||
4164 | } | ||
4165 | priv->prefer_port3 = 0; | ||
4166 | |||
4167 | break; | ||
4168 | |||
4169 | case 1: /* Try to do Lucent proprietary ad-hoc mode */ | ||
4170 | if (!priv->has_port3) { | ||
4171 | err = -EINVAL; | ||
4172 | break; | ||
4173 | } | ||
4174 | priv->prefer_port3 = 1; | ||
4175 | break; | ||
4176 | |||
4177 | default: | ||
4178 | err = -EINVAL; | ||
4179 | } | ||
4180 | |||
4181 | if (!err) { | ||
4182 | /* Actually update the mode we are using */ | ||
4183 | set_port_type(priv); | ||
4184 | err = -EINPROGRESS; | ||
4185 | } | ||
4186 | |||
4187 | orinoco_unlock(priv, &flags); | ||
4188 | |||
4189 | return err; | ||
4190 | } | ||
4191 | |||
4192 | static int orinoco_ioctl_getport3(struct net_device *dev, | ||
4193 | struct iw_request_info *info, | ||
4194 | void *wrqu, | ||
4195 | char *extra) | ||
4196 | { | ||
4197 | struct orinoco_private *priv = netdev_priv(dev); | ||
4198 | int *val = (int *) extra; | ||
4199 | |||
4200 | *val = priv->prefer_port3; | ||
4201 | return 0; | ||
4202 | } | ||
4203 | |||
4204 | static int orinoco_ioctl_setpreamble(struct net_device *dev, | ||
4205 | struct iw_request_info *info, | ||
4206 | void *wrqu, | ||
4207 | char *extra) | ||
4208 | { | ||
4209 | struct orinoco_private *priv = netdev_priv(dev); | ||
4210 | unsigned long flags; | ||
4211 | int val; | ||
4212 | |||
4213 | if (!priv->has_preamble) | ||
4214 | return -EOPNOTSUPP; | ||
4215 | |||
4216 | /* 802.11b has recently defined some short preamble. | ||
4217 | * Basically, the Phy header has been reduced in size. | ||
4218 | * This increase performance, especially at high rates | ||
4219 | * (the preamble is transmitted at 1Mb/s), unfortunately | ||
4220 | * this give compatibility troubles... - Jean II */ | ||
4221 | val = *((int *) extra); | ||
4222 | |||
4223 | if (orinoco_lock(priv, &flags) != 0) | ||
4224 | return -EBUSY; | ||
4225 | |||
4226 | if (val) | ||
4227 | priv->preamble = 1; | ||
4228 | else | ||
4229 | priv->preamble = 0; | ||
4230 | |||
4231 | orinoco_unlock(priv, &flags); | ||
4232 | |||
4233 | return -EINPROGRESS; /* Call commit handler */ | ||
4234 | } | ||
4235 | |||
4236 | static int orinoco_ioctl_getpreamble(struct net_device *dev, | ||
4237 | struct iw_request_info *info, | ||
4238 | void *wrqu, | ||
4239 | char *extra) | ||
4240 | { | ||
4241 | struct orinoco_private *priv = netdev_priv(dev); | ||
4242 | int *val = (int *) extra; | ||
4243 | |||
4244 | if (!priv->has_preamble) | ||
4245 | return -EOPNOTSUPP; | ||
4246 | |||
4247 | *val = priv->preamble; | ||
4248 | return 0; | ||
4249 | } | ||
4250 | |||
4251 | /* ioctl interface to hermes_read_ltv() | ||
4252 | * To use with iwpriv, pass the RID as the token argument, e.g. | ||
4253 | * iwpriv get_rid [0xfc00] | ||
4254 | * At least Wireless Tools 25 is required to use iwpriv. | ||
4255 | * For Wireless Tools 25 and 26 append "dummy" are the end. */ | ||
4256 | static int orinoco_ioctl_getrid(struct net_device *dev, | ||
4257 | struct iw_request_info *info, | ||
4258 | struct iw_point *data, | ||
4259 | char *extra) | ||
4260 | { | ||
4261 | struct orinoco_private *priv = netdev_priv(dev); | ||
4262 | hermes_t *hw = &priv->hw; | ||
4263 | int rid = data->flags; | ||
4264 | u16 length; | ||
4265 | int err; | ||
4266 | unsigned long flags; | ||
4267 | |||
4268 | /* It's a "get" function, but we don't want users to access the | ||
4269 | * WEP key and other raw firmware data */ | ||
4270 | if (!capable(CAP_NET_ADMIN)) | ||
4271 | return -EPERM; | ||
4272 | |||
4273 | if (rid < 0xfc00 || rid > 0xffff) | ||
4274 | return -EINVAL; | ||
4275 | |||
4276 | if (orinoco_lock(priv, &flags) != 0) | ||
4277 | return -EBUSY; | ||
4278 | |||
4279 | err = hermes_read_ltv(hw, USER_BAP, rid, MAX_RID_LEN, &length, | ||
4280 | extra); | ||
4281 | if (err) | ||
4282 | goto out; | ||
4283 | |||
4284 | data->length = min_t(u16, HERMES_RECLEN_TO_BYTES(length), | ||
4285 | MAX_RID_LEN); | ||
4286 | |||
4287 | out: | ||
4288 | orinoco_unlock(priv, &flags); | ||
4289 | return err; | ||
4290 | } | ||
4291 | |||
4292 | /* Trigger a scan (look for other cells in the vicinity) */ | ||
4293 | static int orinoco_ioctl_setscan(struct net_device *dev, | ||
4294 | struct iw_request_info *info, | ||
4295 | struct iw_point *srq, | ||
4296 | char *extra) | ||
4297 | { | ||
4298 | struct orinoco_private *priv = netdev_priv(dev); | ||
4299 | hermes_t *hw = &priv->hw; | ||
4300 | struct iw_scan_req *si = (struct iw_scan_req *) extra; | ||
4301 | int err = 0; | ||
4302 | unsigned long flags; | ||
4303 | |||
4304 | /* Note : you may have realised that, as this is a SET operation, | ||
4305 | * this is privileged and therefore a normal user can't | ||
4306 | * perform scanning. | ||
4307 | * This is not an error, while the device perform scanning, | ||
4308 | * traffic doesn't flow, so it's a perfect DoS... | ||
4309 | * Jean II */ | ||
4310 | |||
4311 | if (orinoco_lock(priv, &flags) != 0) | ||
4312 | return -EBUSY; | ||
4313 | |||
4314 | /* Scanning with port 0 disabled would fail */ | ||
4315 | if (!netif_running(dev)) { | ||
4316 | err = -ENETDOWN; | ||
4317 | goto out; | ||
4318 | } | ||
4319 | |||
4320 | /* In monitor mode, the scan results are always empty. | ||
4321 | * Probe responses are passed to the driver as received | ||
4322 | * frames and could be processed in software. */ | ||
4323 | if (priv->iw_mode == IW_MODE_MONITOR) { | ||
4324 | err = -EOPNOTSUPP; | ||
4325 | goto out; | ||
4326 | } | ||
4327 | |||
4328 | /* Note : because we don't lock out the irq handler, the way | ||
4329 | * we access scan variables in priv is critical. | ||
4330 | * o scan_inprogress : not touched by irq handler | ||
4331 | * o scan_mode : not touched by irq handler | ||
4332 | * Before modifying anything on those variables, please think hard ! | ||
4333 | * Jean II */ | ||
4334 | |||
4335 | /* Save flags */ | ||
4336 | priv->scan_mode = srq->flags; | ||
4337 | |||
4338 | /* Always trigger scanning, even if it's in progress. | ||
4339 | * This way, if the info frame get lost, we will recover somewhat | ||
4340 | * gracefully - Jean II */ | ||
4341 | |||
4342 | if (priv->has_hostscan) { | ||
4343 | switch (priv->firmware_type) { | ||
4344 | case FIRMWARE_TYPE_SYMBOL: | ||
4345 | err = hermes_write_wordrec(hw, USER_BAP, | ||
4346 | HERMES_RID_CNFHOSTSCAN_SYMBOL, | ||
4347 | HERMES_HOSTSCAN_SYMBOL_ONCE | | ||
4348 | HERMES_HOSTSCAN_SYMBOL_BCAST); | ||
4349 | break; | ||
4350 | case FIRMWARE_TYPE_INTERSIL: { | ||
4351 | __le16 req[3]; | ||
4352 | |||
4353 | req[0] = cpu_to_le16(0x3fff); /* All channels */ | ||
4354 | req[1] = cpu_to_le16(0x0001); /* rate 1 Mbps */ | ||
4355 | req[2] = 0; /* Any ESSID */ | ||
4356 | err = HERMES_WRITE_RECORD(hw, USER_BAP, | ||
4357 | HERMES_RID_CNFHOSTSCAN, &req); | ||
4358 | } | ||
4359 | break; | ||
4360 | case FIRMWARE_TYPE_AGERE: | ||
4361 | if (priv->scan_mode & IW_SCAN_THIS_ESSID) { | ||
4362 | struct hermes_idstring idbuf; | ||
4363 | size_t len = min(sizeof(idbuf.val), | ||
4364 | (size_t) si->essid_len); | ||
4365 | idbuf.len = cpu_to_le16(len); | ||
4366 | memcpy(idbuf.val, si->essid, len); | ||
4367 | |||
4368 | err = hermes_write_ltv(hw, USER_BAP, | ||
4369 | HERMES_RID_CNFSCANSSID_AGERE, | ||
4370 | HERMES_BYTES_TO_RECLEN(len + 2), | ||
4371 | &idbuf); | ||
4372 | } else | ||
4373 | err = hermes_write_wordrec(hw, USER_BAP, | ||
4374 | HERMES_RID_CNFSCANSSID_AGERE, | ||
4375 | 0); /* Any ESSID */ | ||
4376 | if (err) | ||
4377 | break; | ||
4378 | |||
4379 | if (priv->has_ext_scan) { | ||
4380 | /* Clear scan results at the start of | ||
4381 | * an extended scan */ | ||
4382 | orinoco_clear_scan_results(priv, | ||
4383 | msecs_to_jiffies(15000)); | ||
4384 | |||
4385 | /* TODO: Is this available on older firmware? | ||
4386 | * Can we use it to scan specific channels | ||
4387 | * for IW_SCAN_THIS_FREQ? */ | ||
4388 | err = hermes_write_wordrec(hw, USER_BAP, | ||
4389 | HERMES_RID_CNFSCANCHANNELS2GHZ, | ||
4390 | 0x7FFF); | ||
4391 | if (err) | ||
4392 | goto out; | ||
4393 | |||
4394 | err = hermes_inquire(hw, | ||
4395 | HERMES_INQ_CHANNELINFO); | ||
4396 | } else | ||
4397 | err = hermes_inquire(hw, HERMES_INQ_SCAN); | ||
4398 | break; | ||
4399 | } | ||
4400 | } else | ||
4401 | err = hermes_inquire(hw, HERMES_INQ_SCAN); | ||
4402 | |||
4403 | /* One more client */ | ||
4404 | if (!err) | ||
4405 | priv->scan_inprogress = 1; | ||
4406 | |||
4407 | out: | ||
4408 | orinoco_unlock(priv, &flags); | ||
4409 | return err; | ||
4410 | } | ||
4411 | |||
4412 | #define MAX_CUSTOM_LEN 64 | ||
4413 | |||
4414 | /* Translate scan data returned from the card to a card independant | ||
4415 | * format that the Wireless Tools will understand - Jean II */ | ||
4416 | static inline char *orinoco_translate_scan(struct net_device *dev, | ||
4417 | struct iw_request_info *info, | ||
4418 | char *current_ev, | ||
4419 | char *end_buf, | ||
4420 | union hermes_scan_info *bss, | ||
4421 | unsigned long last_scanned) | ||
4422 | { | ||
4423 | struct orinoco_private *priv = netdev_priv(dev); | ||
4424 | u16 capabilities; | ||
4425 | u16 channel; | ||
4426 | struct iw_event iwe; /* Temporary buffer */ | ||
4427 | char custom[MAX_CUSTOM_LEN]; | ||
4428 | |||
4429 | memset(&iwe, 0, sizeof(iwe)); | ||
4430 | |||
4431 | /* First entry *MUST* be the AP MAC address */ | ||
4432 | iwe.cmd = SIOCGIWAP; | ||
4433 | iwe.u.ap_addr.sa_family = ARPHRD_ETHER; | ||
4434 | memcpy(iwe.u.ap_addr.sa_data, bss->a.bssid, ETH_ALEN); | ||
4435 | current_ev = iwe_stream_add_event(info, current_ev, end_buf, | ||
4436 | &iwe, IW_EV_ADDR_LEN); | ||
4437 | |||
4438 | /* Other entries will be displayed in the order we give them */ | ||
4439 | |||
4440 | /* Add the ESSID */ | ||
4441 | iwe.u.data.length = le16_to_cpu(bss->a.essid_len); | ||
4442 | if (iwe.u.data.length > 32) | ||
4443 | iwe.u.data.length = 32; | ||
4444 | iwe.cmd = SIOCGIWESSID; | ||
4445 | iwe.u.data.flags = 1; | ||
4446 | current_ev = iwe_stream_add_point(info, current_ev, end_buf, | ||
4447 | &iwe, bss->a.essid); | ||
4448 | |||
4449 | /* Add mode */ | ||
4450 | iwe.cmd = SIOCGIWMODE; | ||
4451 | capabilities = le16_to_cpu(bss->a.capabilities); | ||
4452 | if (capabilities & (WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_IBSS)) { | ||
4453 | if (capabilities & WLAN_CAPABILITY_ESS) | ||
4454 | iwe.u.mode = IW_MODE_MASTER; | ||
4455 | else | ||
4456 | iwe.u.mode = IW_MODE_ADHOC; | ||
4457 | current_ev = iwe_stream_add_event(info, current_ev, end_buf, | ||
4458 | &iwe, IW_EV_UINT_LEN); | ||
4459 | } | ||
4460 | |||
4461 | channel = bss->s.channel; | ||
4462 | if ((channel >= 1) && (channel <= NUM_CHANNELS)) { | ||
4463 | /* Add channel and frequency */ | ||
4464 | iwe.cmd = SIOCGIWFREQ; | ||
4465 | iwe.u.freq.m = channel; | ||
4466 | iwe.u.freq.e = 0; | ||
4467 | current_ev = iwe_stream_add_event(info, current_ev, end_buf, | ||
4468 | &iwe, IW_EV_FREQ_LEN); | ||
4469 | |||
4470 | iwe.u.freq.m = ieee80211_dsss_chan_to_freq(channel) * 100000; | ||
4471 | iwe.u.freq.e = 1; | ||
4472 | current_ev = iwe_stream_add_event(info, current_ev, end_buf, | ||
4473 | &iwe, IW_EV_FREQ_LEN); | ||
4474 | } | ||
4475 | |||
4476 | /* Add quality statistics. level and noise in dB. No link quality */ | ||
4477 | iwe.cmd = IWEVQUAL; | ||
4478 | iwe.u.qual.updated = IW_QUAL_DBM | IW_QUAL_QUAL_INVALID; | ||
4479 | iwe.u.qual.level = (__u8) le16_to_cpu(bss->a.level) - 0x95; | ||
4480 | iwe.u.qual.noise = (__u8) le16_to_cpu(bss->a.noise) - 0x95; | ||
4481 | /* Wireless tools prior to 27.pre22 will show link quality | ||
4482 | * anyway, so we provide a reasonable value. */ | ||
4483 | if (iwe.u.qual.level > iwe.u.qual.noise) | ||
4484 | iwe.u.qual.qual = iwe.u.qual.level - iwe.u.qual.noise; | ||
4485 | else | ||
4486 | iwe.u.qual.qual = 0; | ||
4487 | current_ev = iwe_stream_add_event(info, current_ev, end_buf, | ||
4488 | &iwe, IW_EV_QUAL_LEN); | ||
4489 | |||
4490 | /* Add encryption capability */ | ||
4491 | iwe.cmd = SIOCGIWENCODE; | ||
4492 | if (capabilities & WLAN_CAPABILITY_PRIVACY) | ||
4493 | iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY; | ||
4494 | else | ||
4495 | iwe.u.data.flags = IW_ENCODE_DISABLED; | ||
4496 | iwe.u.data.length = 0; | ||
4497 | current_ev = iwe_stream_add_point(info, current_ev, end_buf, | ||
4498 | &iwe, NULL); | ||
4499 | |||
4500 | /* Bit rate is not available in Lucent/Agere firmwares */ | ||
4501 | if (priv->firmware_type != FIRMWARE_TYPE_AGERE) { | ||
4502 | char *current_val = current_ev + iwe_stream_lcp_len(info); | ||
4503 | int i; | ||
4504 | int step; | ||
4505 | |||
4506 | if (priv->firmware_type == FIRMWARE_TYPE_SYMBOL) | ||
4507 | step = 2; | ||
4508 | else | ||
4509 | step = 1; | ||
4510 | |||
4511 | iwe.cmd = SIOCGIWRATE; | ||
4512 | /* Those two flags are ignored... */ | ||
4513 | iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0; | ||
4514 | /* Max 10 values */ | ||
4515 | for (i = 0; i < 10; i += step) { | ||
4516 | /* NULL terminated */ | ||
4517 | if (bss->p.rates[i] == 0x0) | ||
4518 | break; | ||
4519 | /* Bit rate given in 500 kb/s units (+ 0x80) */ | ||
4520 | iwe.u.bitrate.value = | ||
4521 | ((bss->p.rates[i] & 0x7f) * 500000); | ||
4522 | current_val = iwe_stream_add_value(info, current_ev, | ||
4523 | current_val, | ||
4524 | end_buf, &iwe, | ||
4525 | IW_EV_PARAM_LEN); | ||
4526 | } | ||
4527 | /* Check if we added any event */ | ||
4528 | if ((current_val - current_ev) > iwe_stream_lcp_len(info)) | ||
4529 | current_ev = current_val; | ||
4530 | } | ||
4531 | |||
4532 | /* Beacon interval */ | ||
4533 | iwe.cmd = IWEVCUSTOM; | ||
4534 | iwe.u.data.length = snprintf(custom, MAX_CUSTOM_LEN, | ||
4535 | "bcn_int=%d", | ||
4536 | le16_to_cpu(bss->a.beacon_interv)); | ||
4537 | if (iwe.u.data.length) | ||
4538 | current_ev = iwe_stream_add_point(info, current_ev, end_buf, | ||
4539 | &iwe, custom); | ||
4540 | |||
4541 | /* Capabilites */ | ||
4542 | iwe.cmd = IWEVCUSTOM; | ||
4543 | iwe.u.data.length = snprintf(custom, MAX_CUSTOM_LEN, | ||
4544 | "capab=0x%04x", | ||
4545 | capabilities); | ||
4546 | if (iwe.u.data.length) | ||
4547 | current_ev = iwe_stream_add_point(info, current_ev, end_buf, | ||
4548 | &iwe, custom); | ||
4549 | |||
4550 | /* Add EXTRA: Age to display seconds since last beacon/probe response | ||
4551 | * for given network. */ | ||
4552 | iwe.cmd = IWEVCUSTOM; | ||
4553 | iwe.u.data.length = snprintf(custom, MAX_CUSTOM_LEN, | ||
4554 | " Last beacon: %dms ago", | ||
4555 | jiffies_to_msecs(jiffies - last_scanned)); | ||
4556 | if (iwe.u.data.length) | ||
4557 | current_ev = iwe_stream_add_point(info, current_ev, end_buf, | ||
4558 | &iwe, custom); | ||
4559 | |||
4560 | return current_ev; | ||
4561 | } | ||
4562 | |||
4563 | static inline char *orinoco_translate_ext_scan(struct net_device *dev, | ||
4564 | struct iw_request_info *info, | ||
4565 | char *current_ev, | ||
4566 | char *end_buf, | ||
4567 | struct agere_ext_scan_info *bss, | ||
4568 | unsigned long last_scanned) | ||
4569 | { | ||
4570 | u16 capabilities; | ||
4571 | u16 channel; | ||
4572 | struct iw_event iwe; /* Temporary buffer */ | ||
4573 | char custom[MAX_CUSTOM_LEN]; | ||
4574 | u8 *ie; | ||
4575 | |||
4576 | memset(&iwe, 0, sizeof(iwe)); | ||
4577 | |||
4578 | /* First entry *MUST* be the AP MAC address */ | ||
4579 | iwe.cmd = SIOCGIWAP; | ||
4580 | iwe.u.ap_addr.sa_family = ARPHRD_ETHER; | ||
4581 | memcpy(iwe.u.ap_addr.sa_data, bss->bssid, ETH_ALEN); | ||
4582 | current_ev = iwe_stream_add_event(info, current_ev, end_buf, | ||
4583 | &iwe, IW_EV_ADDR_LEN); | ||
4584 | |||
4585 | /* Other entries will be displayed in the order we give them */ | ||
4586 | |||
4587 | /* Add the ESSID */ | ||
4588 | ie = bss->data; | ||
4589 | iwe.u.data.length = ie[1]; | ||
4590 | if (iwe.u.data.length) { | ||
4591 | if (iwe.u.data.length > 32) | ||
4592 | iwe.u.data.length = 32; | ||
4593 | iwe.cmd = SIOCGIWESSID; | ||
4594 | iwe.u.data.flags = 1; | ||
4595 | current_ev = iwe_stream_add_point(info, current_ev, end_buf, | ||
4596 | &iwe, &ie[2]); | ||
4597 | } | ||
4598 | |||
4599 | /* Add mode */ | ||
4600 | capabilities = le16_to_cpu(bss->capabilities); | ||
4601 | if (capabilities & (WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_IBSS)) { | ||
4602 | iwe.cmd = SIOCGIWMODE; | ||
4603 | if (capabilities & WLAN_CAPABILITY_ESS) | ||
4604 | iwe.u.mode = IW_MODE_MASTER; | ||
4605 | else | ||
4606 | iwe.u.mode = IW_MODE_ADHOC; | ||
4607 | current_ev = iwe_stream_add_event(info, current_ev, end_buf, | ||
4608 | &iwe, IW_EV_UINT_LEN); | ||
4609 | } | ||
4610 | |||
4611 | ie = orinoco_get_ie(bss->data, sizeof(bss->data), WLAN_EID_DS_PARAMS); | ||
4612 | channel = ie ? ie[2] : 0; | ||
4613 | if ((channel >= 1) && (channel <= NUM_CHANNELS)) { | ||
4614 | /* Add channel and frequency */ | ||
4615 | iwe.cmd = SIOCGIWFREQ; | ||
4616 | iwe.u.freq.m = channel; | ||
4617 | iwe.u.freq.e = 0; | ||
4618 | current_ev = iwe_stream_add_event(info, current_ev, end_buf, | ||
4619 | &iwe, IW_EV_FREQ_LEN); | ||
4620 | |||
4621 | iwe.u.freq.m = ieee80211_dsss_chan_to_freq(channel) * 100000; | ||
4622 | iwe.u.freq.e = 1; | ||
4623 | current_ev = iwe_stream_add_event(info, current_ev, end_buf, | ||
4624 | &iwe, IW_EV_FREQ_LEN); | ||
4625 | } | ||
4626 | |||
4627 | /* Add quality statistics. level and noise in dB. No link quality */ | ||
4628 | iwe.cmd = IWEVQUAL; | ||
4629 | iwe.u.qual.updated = IW_QUAL_DBM | IW_QUAL_QUAL_INVALID; | ||
4630 | iwe.u.qual.level = bss->level - 0x95; | ||
4631 | iwe.u.qual.noise = bss->noise - 0x95; | ||
4632 | /* Wireless tools prior to 27.pre22 will show link quality | ||
4633 | * anyway, so we provide a reasonable value. */ | ||
4634 | if (iwe.u.qual.level > iwe.u.qual.noise) | ||
4635 | iwe.u.qual.qual = iwe.u.qual.level - iwe.u.qual.noise; | ||
4636 | else | ||
4637 | iwe.u.qual.qual = 0; | ||
4638 | current_ev = iwe_stream_add_event(info, current_ev, end_buf, | ||
4639 | &iwe, IW_EV_QUAL_LEN); | ||
4640 | |||
4641 | /* Add encryption capability */ | ||
4642 | iwe.cmd = SIOCGIWENCODE; | ||
4643 | if (capabilities & WLAN_CAPABILITY_PRIVACY) | ||
4644 | iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY; | ||
4645 | else | ||
4646 | iwe.u.data.flags = IW_ENCODE_DISABLED; | ||
4647 | iwe.u.data.length = 0; | ||
4648 | current_ev = iwe_stream_add_point(info, current_ev, end_buf, | ||
4649 | &iwe, NULL); | ||
4650 | |||
4651 | /* WPA IE */ | ||
4652 | ie = orinoco_get_wpa_ie(bss->data, sizeof(bss->data)); | ||
4653 | if (ie) { | ||
4654 | iwe.cmd = IWEVGENIE; | ||
4655 | iwe.u.data.length = ie[1] + 2; | ||
4656 | current_ev = iwe_stream_add_point(info, current_ev, end_buf, | ||
4657 | &iwe, ie); | ||
4658 | } | ||
4659 | |||
4660 | /* RSN IE */ | ||
4661 | ie = orinoco_get_ie(bss->data, sizeof(bss->data), WLAN_EID_RSN); | ||
4662 | if (ie) { | ||
4663 | iwe.cmd = IWEVGENIE; | ||
4664 | iwe.u.data.length = ie[1] + 2; | ||
4665 | current_ev = iwe_stream_add_point(info, current_ev, end_buf, | ||
4666 | &iwe, ie); | ||
4667 | } | ||
4668 | |||
4669 | ie = orinoco_get_ie(bss->data, sizeof(bss->data), WLAN_EID_SUPP_RATES); | ||
4670 | if (ie) { | ||
4671 | char *p = current_ev + iwe_stream_lcp_len(info); | ||
4672 | int i; | ||
4673 | |||
4674 | iwe.cmd = SIOCGIWRATE; | ||
4675 | /* Those two flags are ignored... */ | ||
4676 | iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0; | ||
4677 | |||
4678 | for (i = 2; i < (ie[1] + 2); i++) { | ||
4679 | iwe.u.bitrate.value = ((ie[i] & 0x7F) * 500000); | ||
4680 | p = iwe_stream_add_value(info, current_ev, p, end_buf, | ||
4681 | &iwe, IW_EV_PARAM_LEN); | ||
4682 | } | ||
4683 | /* Check if we added any event */ | ||
4684 | if (p > (current_ev + iwe_stream_lcp_len(info))) | ||
4685 | current_ev = p; | ||
4686 | } | ||
4687 | |||
4688 | /* Timestamp */ | ||
4689 | iwe.cmd = IWEVCUSTOM; | ||
4690 | iwe.u.data.length = | ||
4691 | snprintf(custom, MAX_CUSTOM_LEN, "tsf=%016llx", | ||
4692 | (unsigned long long) le64_to_cpu(bss->timestamp)); | ||
4693 | if (iwe.u.data.length) | ||
4694 | current_ev = iwe_stream_add_point(info, current_ev, end_buf, | ||
4695 | &iwe, custom); | ||
4696 | |||
4697 | /* Beacon interval */ | ||
4698 | iwe.cmd = IWEVCUSTOM; | ||
4699 | iwe.u.data.length = snprintf(custom, MAX_CUSTOM_LEN, | ||
4700 | "bcn_int=%d", | ||
4701 | le16_to_cpu(bss->beacon_interval)); | ||
4702 | if (iwe.u.data.length) | ||
4703 | current_ev = iwe_stream_add_point(info, current_ev, end_buf, | ||
4704 | &iwe, custom); | ||
4705 | |||
4706 | /* Capabilites */ | ||
4707 | iwe.cmd = IWEVCUSTOM; | ||
4708 | iwe.u.data.length = snprintf(custom, MAX_CUSTOM_LEN, | ||
4709 | "capab=0x%04x", | ||
4710 | capabilities); | ||
4711 | if (iwe.u.data.length) | ||
4712 | current_ev = iwe_stream_add_point(info, current_ev, end_buf, | ||
4713 | &iwe, custom); | ||
4714 | |||
4715 | /* Add EXTRA: Age to display seconds since last beacon/probe response | ||
4716 | * for given network. */ | ||
4717 | iwe.cmd = IWEVCUSTOM; | ||
4718 | iwe.u.data.length = snprintf(custom, MAX_CUSTOM_LEN, | ||
4719 | " Last beacon: %dms ago", | ||
4720 | jiffies_to_msecs(jiffies - last_scanned)); | ||
4721 | if (iwe.u.data.length) | ||
4722 | current_ev = iwe_stream_add_point(info, current_ev, end_buf, | ||
4723 | &iwe, custom); | ||
4724 | |||
4725 | return current_ev; | ||
4726 | } | ||
4727 | |||
4728 | /* Return results of a scan */ | ||
4729 | static int orinoco_ioctl_getscan(struct net_device *dev, | ||
4730 | struct iw_request_info *info, | ||
4731 | struct iw_point *srq, | ||
4732 | char *extra) | ||
4733 | { | ||
4734 | struct orinoco_private *priv = netdev_priv(dev); | ||
4735 | int err = 0; | ||
4736 | unsigned long flags; | ||
4737 | char *current_ev = extra; | ||
4738 | |||
4739 | if (orinoco_lock(priv, &flags) != 0) | ||
4740 | return -EBUSY; | ||
4741 | |||
4742 | if (priv->scan_inprogress) { | ||
4743 | /* Important note : we don't want to block the caller | ||
4744 | * until results are ready for various reasons. | ||
4745 | * First, managing wait queues is complex and racy. | ||
4746 | * Second, we grab some rtnetlink lock before comming | ||
4747 | * here (in dev_ioctl()). | ||
4748 | * Third, we generate an Wireless Event, so the | ||
4749 | * caller can wait itself on that - Jean II */ | ||
4750 | err = -EAGAIN; | ||
4751 | goto out; | ||
4752 | } | ||
4753 | |||
4754 | if (priv->has_ext_scan) { | ||
4755 | struct xbss_element *bss; | ||
4756 | |||
4757 | list_for_each_entry(bss, &priv->bss_list, list) { | ||
4758 | /* Translate this entry to WE format */ | ||
4759 | current_ev = | ||
4760 | orinoco_translate_ext_scan(dev, info, | ||
4761 | current_ev, | ||
4762 | extra + srq->length, | ||
4763 | &bss->bss, | ||
4764 | bss->last_scanned); | ||
4765 | |||
4766 | /* Check if there is space for one more entry */ | ||
4767 | if ((extra + srq->length - current_ev) | ||
4768 | <= IW_EV_ADDR_LEN) { | ||
4769 | /* Ask user space to try again with a | ||
4770 | * bigger buffer */ | ||
4771 | err = -E2BIG; | ||
4772 | goto out; | ||
4773 | } | ||
4774 | } | ||
4775 | |||
4776 | } else { | ||
4777 | struct bss_element *bss; | ||
4778 | |||
4779 | list_for_each_entry(bss, &priv->bss_list, list) { | ||
4780 | /* Translate this entry to WE format */ | ||
4781 | current_ev = orinoco_translate_scan(dev, info, | ||
4782 | current_ev, | ||
4783 | extra + srq->length, | ||
4784 | &bss->bss, | ||
4785 | bss->last_scanned); | ||
4786 | |||
4787 | /* Check if there is space for one more entry */ | ||
4788 | if ((extra + srq->length - current_ev) | ||
4789 | <= IW_EV_ADDR_LEN) { | ||
4790 | /* Ask user space to try again with a | ||
4791 | * bigger buffer */ | ||
4792 | err = -E2BIG; | ||
4793 | goto out; | ||
4794 | } | ||
4795 | } | ||
4796 | } | ||
4797 | |||
4798 | srq->length = (current_ev - extra); | ||
4799 | srq->flags = (__u16) priv->scan_mode; | ||
4800 | |||
4801 | out: | ||
4802 | orinoco_unlock(priv, &flags); | ||
4803 | return err; | ||
4804 | } | ||
4805 | |||
4806 | /* Commit handler, called after set operations */ | ||
4807 | static int orinoco_ioctl_commit(struct net_device *dev, | ||
4808 | struct iw_request_info *info, | ||
4809 | void *wrqu, | ||
4810 | char *extra) | ||
4811 | { | ||
4812 | struct orinoco_private *priv = netdev_priv(dev); | ||
4813 | struct hermes *hw = &priv->hw; | ||
4814 | unsigned long flags; | ||
4815 | int err = 0; | ||
4816 | |||
4817 | if (!priv->open) | ||
4818 | return 0; | ||
4819 | |||
4820 | if (priv->broken_disableport) { | ||
4821 | orinoco_reset(&priv->reset_work); | ||
4822 | return 0; | ||
4823 | } | ||
4824 | |||
4825 | if (orinoco_lock(priv, &flags) != 0) | ||
4826 | return err; | ||
4827 | |||
4828 | err = hermes_disable_port(hw, 0); | ||
4829 | if (err) { | ||
4830 | printk(KERN_WARNING "%s: Unable to disable port " | ||
4831 | "while reconfiguring card\n", dev->name); | ||
4832 | priv->broken_disableport = 1; | ||
4833 | goto out; | ||
4834 | } | ||
4835 | |||
4836 | err = __orinoco_program_rids(dev); | ||
4837 | if (err) { | ||
4838 | printk(KERN_WARNING "%s: Unable to reconfigure card\n", | ||
4839 | dev->name); | ||
4840 | goto out; | ||
4841 | } | ||
4842 | |||
4843 | err = hermes_enable_port(hw, 0); | ||
4844 | if (err) { | ||
4845 | printk(KERN_WARNING "%s: Unable to enable port while reconfiguring card\n", | ||
4846 | dev->name); | ||
4847 | goto out; | ||
4848 | } | ||
4849 | |||
4850 | out: | ||
4851 | if (err) { | ||
4852 | printk(KERN_WARNING "%s: Resetting instead...\n", dev->name); | ||
4853 | schedule_work(&priv->reset_work); | ||
4854 | err = 0; | ||
4855 | } | ||
4856 | |||
4857 | orinoco_unlock(priv, &flags); | ||
4858 | return err; | ||
4859 | } | ||
4860 | |||
4861 | static const struct iw_priv_args orinoco_privtab[] = { | ||
4862 | { SIOCIWFIRSTPRIV + 0x0, 0, 0, "force_reset" }, | ||
4863 | { SIOCIWFIRSTPRIV + 0x1, 0, 0, "card_reset" }, | ||
4864 | { SIOCIWFIRSTPRIV + 0x2, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, | ||
4865 | 0, "set_port3" }, | ||
4866 | { SIOCIWFIRSTPRIV + 0x3, 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, | ||
4867 | "get_port3" }, | ||
4868 | { SIOCIWFIRSTPRIV + 0x4, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, | ||
4869 | 0, "set_preamble" }, | ||
4870 | { SIOCIWFIRSTPRIV + 0x5, 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, | ||
4871 | "get_preamble" }, | ||
4872 | { SIOCIWFIRSTPRIV + 0x6, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, | ||
4873 | 0, "set_ibssport" }, | ||
4874 | { SIOCIWFIRSTPRIV + 0x7, 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, | ||
4875 | "get_ibssport" }, | ||
4876 | { SIOCIWFIRSTPRIV + 0x9, 0, IW_PRIV_TYPE_BYTE | MAX_RID_LEN, | ||
4877 | "get_rid" }, | ||
4878 | }; | ||
4879 | |||
4880 | |||
4881 | /* | ||
4882 | * Structures to export the Wireless Handlers | ||
4883 | */ | ||
4884 | |||
4885 | #define STD_IW_HANDLER(id, func) \ | ||
4886 | [IW_IOCTL_IDX(id)] = (iw_handler) func | ||
4887 | static const iw_handler orinoco_handler[] = { | ||
4888 | STD_IW_HANDLER(SIOCSIWCOMMIT, orinoco_ioctl_commit), | ||
4889 | STD_IW_HANDLER(SIOCGIWNAME, orinoco_ioctl_getname), | ||
4890 | STD_IW_HANDLER(SIOCSIWFREQ, orinoco_ioctl_setfreq), | ||
4891 | STD_IW_HANDLER(SIOCGIWFREQ, orinoco_ioctl_getfreq), | ||
4892 | STD_IW_HANDLER(SIOCSIWMODE, orinoco_ioctl_setmode), | ||
4893 | STD_IW_HANDLER(SIOCGIWMODE, orinoco_ioctl_getmode), | ||
4894 | STD_IW_HANDLER(SIOCSIWSENS, orinoco_ioctl_setsens), | ||
4895 | STD_IW_HANDLER(SIOCGIWSENS, orinoco_ioctl_getsens), | ||
4896 | STD_IW_HANDLER(SIOCGIWRANGE, orinoco_ioctl_getiwrange), | ||
4897 | STD_IW_HANDLER(SIOCSIWSPY, iw_handler_set_spy), | ||
4898 | STD_IW_HANDLER(SIOCGIWSPY, iw_handler_get_spy), | ||
4899 | STD_IW_HANDLER(SIOCSIWTHRSPY, iw_handler_set_thrspy), | ||
4900 | STD_IW_HANDLER(SIOCGIWTHRSPY, iw_handler_get_thrspy), | ||
4901 | STD_IW_HANDLER(SIOCSIWAP, orinoco_ioctl_setwap), | ||
4902 | STD_IW_HANDLER(SIOCGIWAP, orinoco_ioctl_getwap), | ||
4903 | STD_IW_HANDLER(SIOCSIWSCAN, orinoco_ioctl_setscan), | ||
4904 | STD_IW_HANDLER(SIOCGIWSCAN, orinoco_ioctl_getscan), | ||
4905 | STD_IW_HANDLER(SIOCSIWESSID, orinoco_ioctl_setessid), | ||
4906 | STD_IW_HANDLER(SIOCGIWESSID, orinoco_ioctl_getessid), | ||
4907 | STD_IW_HANDLER(SIOCSIWNICKN, orinoco_ioctl_setnick), | ||
4908 | STD_IW_HANDLER(SIOCGIWNICKN, orinoco_ioctl_getnick), | ||
4909 | STD_IW_HANDLER(SIOCSIWRATE, orinoco_ioctl_setrate), | ||
4910 | STD_IW_HANDLER(SIOCGIWRATE, orinoco_ioctl_getrate), | ||
4911 | STD_IW_HANDLER(SIOCSIWRTS, orinoco_ioctl_setrts), | ||
4912 | STD_IW_HANDLER(SIOCGIWRTS, orinoco_ioctl_getrts), | ||
4913 | STD_IW_HANDLER(SIOCSIWFRAG, orinoco_ioctl_setfrag), | ||
4914 | STD_IW_HANDLER(SIOCGIWFRAG, orinoco_ioctl_getfrag), | ||
4915 | STD_IW_HANDLER(SIOCGIWRETRY, orinoco_ioctl_getretry), | ||
4916 | STD_IW_HANDLER(SIOCSIWENCODE, orinoco_ioctl_setiwencode), | ||
4917 | STD_IW_HANDLER(SIOCGIWENCODE, orinoco_ioctl_getiwencode), | ||
4918 | STD_IW_HANDLER(SIOCSIWPOWER, orinoco_ioctl_setpower), | ||
4919 | STD_IW_HANDLER(SIOCGIWPOWER, orinoco_ioctl_getpower), | ||
4920 | STD_IW_HANDLER(SIOCSIWGENIE, orinoco_ioctl_set_genie), | ||
4921 | STD_IW_HANDLER(SIOCGIWGENIE, orinoco_ioctl_get_genie), | ||
4922 | STD_IW_HANDLER(SIOCSIWMLME, orinoco_ioctl_set_mlme), | ||
4923 | STD_IW_HANDLER(SIOCSIWAUTH, orinoco_ioctl_set_auth), | ||
4924 | STD_IW_HANDLER(SIOCGIWAUTH, orinoco_ioctl_get_auth), | ||
4925 | STD_IW_HANDLER(SIOCSIWENCODEEXT, orinoco_ioctl_set_encodeext), | ||
4926 | STD_IW_HANDLER(SIOCGIWENCODEEXT, orinoco_ioctl_get_encodeext), | ||
4927 | }; | ||
4928 | |||
4929 | |||
4930 | /* | ||
4931 | Added typecasting since we no longer use iwreq_data -- Moustafa | ||
4932 | */ | ||
4933 | static const iw_handler orinoco_private_handler[] = { | ||
4934 | [0] = (iw_handler) orinoco_ioctl_reset, | ||
4935 | [1] = (iw_handler) orinoco_ioctl_reset, | ||
4936 | [2] = (iw_handler) orinoco_ioctl_setport3, | ||
4937 | [3] = (iw_handler) orinoco_ioctl_getport3, | ||
4938 | [4] = (iw_handler) orinoco_ioctl_setpreamble, | ||
4939 | [5] = (iw_handler) orinoco_ioctl_getpreamble, | ||
4940 | [6] = (iw_handler) orinoco_ioctl_setibssport, | ||
4941 | [7] = (iw_handler) orinoco_ioctl_getibssport, | ||
4942 | [9] = (iw_handler) orinoco_ioctl_getrid, | ||
4943 | }; | ||
4944 | |||
4945 | static const struct iw_handler_def orinoco_handler_def = { | ||
4946 | .num_standard = ARRAY_SIZE(orinoco_handler), | ||
4947 | .num_private = ARRAY_SIZE(orinoco_private_handler), | ||
4948 | .num_private_args = ARRAY_SIZE(orinoco_privtab), | ||
4949 | .standard = orinoco_handler, | ||
4950 | .private = orinoco_private_handler, | ||
4951 | .private_args = orinoco_privtab, | ||
4952 | .get_wireless_stats = orinoco_get_wireless_stats, | ||
4953 | }; | ||
4954 | |||
4955 | static void orinoco_get_drvinfo(struct net_device *dev, | 2612 | static void orinoco_get_drvinfo(struct net_device *dev, |
4956 | struct ethtool_drvinfo *info) | 2613 | struct ethtool_drvinfo *info) |
4957 | { | 2614 | { |
diff --git a/drivers/net/wireless/orinoco/main.h b/drivers/net/wireless/orinoco/main.h new file mode 100644 index 000000000000..af2bae4fe395 --- /dev/null +++ b/drivers/net/wireless/orinoco/main.h | |||
@@ -0,0 +1,63 @@ | |||
1 | /* Exports from main to helper modules | ||
2 | * | ||
3 | * See copyright notice in main.c | ||
4 | */ | ||
5 | #ifndef _ORINOCO_MAIN_H_ | ||
6 | #define _ORINOCO_MAIN_H_ | ||
7 | |||
8 | #include <linux/ieee80211.h> | ||
9 | #include "orinoco.h" | ||
10 | |||
11 | /********************************************************************/ | ||
12 | /* Compile time configuration and compatibility stuff */ | ||
13 | /********************************************************************/ | ||
14 | |||
15 | /* We do this this way to avoid ifdefs in the actual code */ | ||
16 | #ifdef WIRELESS_SPY | ||
17 | #define SPY_NUMBER(priv) (priv->spy_data.spy_number) | ||
18 | #else | ||
19 | #define SPY_NUMBER(priv) 0 | ||
20 | #endif /* WIRELESS_SPY */ | ||
21 | |||
22 | /********************************************************************/ | ||
23 | |||
24 | /* Export module parameter */ | ||
25 | extern int force_monitor; | ||
26 | |||
27 | /* Forward declarations */ | ||
28 | struct net_device; | ||
29 | struct work_struct; | ||
30 | |||
31 | void set_port_type(struct orinoco_private *priv); | ||
32 | int __orinoco_program_rids(struct net_device *dev); | ||
33 | void orinoco_reset(struct work_struct *work); | ||
34 | |||
35 | |||
36 | /* Information element helpers - find a home for these... */ | ||
37 | static inline u8 *orinoco_get_ie(u8 *data, size_t len, | ||
38 | enum ieee80211_eid eid) | ||
39 | { | ||
40 | u8 *p = data; | ||
41 | while ((p + 2) < (data + len)) { | ||
42 | if (p[0] == eid) | ||
43 | return p; | ||
44 | p += p[1] + 2; | ||
45 | } | ||
46 | return NULL; | ||
47 | } | ||
48 | |||
49 | #define WPA_OUI_TYPE "\x00\x50\xF2\x01" | ||
50 | #define WPA_SELECTOR_LEN 4 | ||
51 | static inline u8 *orinoco_get_wpa_ie(u8 *data, size_t len) | ||
52 | { | ||
53 | u8 *p = data; | ||
54 | while ((p + 2 + WPA_SELECTOR_LEN) < (data + len)) { | ||
55 | if ((p[0] == WLAN_EID_GENERIC) && | ||
56 | (memcmp(&p[2], WPA_OUI_TYPE, WPA_SELECTOR_LEN) == 0)) | ||
57 | return p; | ||
58 | p += p[1] + 2; | ||
59 | } | ||
60 | return NULL; | ||
61 | } | ||
62 | |||
63 | #endif /* _ORINOCO_MAIN_H_ */ | ||
diff --git a/drivers/net/wireless/orinoco/wext.c b/drivers/net/wireless/orinoco/wext.c new file mode 100644 index 000000000000..3f0814234392 --- /dev/null +++ b/drivers/net/wireless/orinoco/wext.c | |||
@@ -0,0 +1,2325 @@ | |||
1 | /* Wireless extensions support. | ||
2 | * | ||
3 | * See copyright notice in main.c | ||
4 | */ | ||
5 | #include <linux/kernel.h> | ||
6 | #include <linux/if_arp.h> | ||
7 | #include <linux/wireless.h> | ||
8 | #include <linux/ieee80211.h> | ||
9 | #include <net/iw_handler.h> | ||
10 | |||
11 | #include "hermes.h" | ||
12 | #include "hermes_rid.h" | ||
13 | #include "orinoco.h" | ||
14 | |||
15 | #include "hw.h" | ||
16 | #include "mic.h" | ||
17 | #include "scan.h" | ||
18 | #include "main.h" | ||
19 | |||
20 | #include "wext.h" | ||
21 | |||
22 | #define MAX_RID_LEN 1024 | ||
23 | |||
24 | static struct iw_statistics *orinoco_get_wireless_stats(struct net_device *dev) | ||
25 | { | ||
26 | struct orinoco_private *priv = netdev_priv(dev); | ||
27 | hermes_t *hw = &priv->hw; | ||
28 | struct iw_statistics *wstats = &priv->wstats; | ||
29 | int err; | ||
30 | unsigned long flags; | ||
31 | |||
32 | if (!netif_device_present(dev)) { | ||
33 | printk(KERN_WARNING "%s: get_wireless_stats() called while device not present\n", | ||
34 | dev->name); | ||
35 | return NULL; /* FIXME: Can we do better than this? */ | ||
36 | } | ||
37 | |||
38 | /* If busy, return the old stats. Returning NULL may cause | ||
39 | * the interface to disappear from /proc/net/wireless */ | ||
40 | if (orinoco_lock(priv, &flags) != 0) | ||
41 | return wstats; | ||
42 | |||
43 | /* We can't really wait for the tallies inquiry command to | ||
44 | * complete, so we just use the previous results and trigger | ||
45 | * a new tallies inquiry command for next time - Jean II */ | ||
46 | /* FIXME: Really we should wait for the inquiry to come back - | ||
47 | * as it is the stats we give don't make a whole lot of sense. | ||
48 | * Unfortunately, it's not clear how to do that within the | ||
49 | * wireless extensions framework: I think we're in user | ||
50 | * context, but a lock seems to be held by the time we get in | ||
51 | * here so we're not safe to sleep here. */ | ||
52 | hermes_inquire(hw, HERMES_INQ_TALLIES); | ||
53 | |||
54 | if (priv->iw_mode == IW_MODE_ADHOC) { | ||
55 | memset(&wstats->qual, 0, sizeof(wstats->qual)); | ||
56 | /* If a spy address is defined, we report stats of the | ||
57 | * first spy address - Jean II */ | ||
58 | if (SPY_NUMBER(priv)) { | ||
59 | wstats->qual.qual = priv->spy_data.spy_stat[0].qual; | ||
60 | wstats->qual.level = priv->spy_data.spy_stat[0].level; | ||
61 | wstats->qual.noise = priv->spy_data.spy_stat[0].noise; | ||
62 | wstats->qual.updated = | ||
63 | priv->spy_data.spy_stat[0].updated; | ||
64 | } | ||
65 | } else { | ||
66 | struct { | ||
67 | __le16 qual, signal, noise, unused; | ||
68 | } __attribute__ ((packed)) cq; | ||
69 | |||
70 | err = HERMES_READ_RECORD(hw, USER_BAP, | ||
71 | HERMES_RID_COMMSQUALITY, &cq); | ||
72 | |||
73 | if (!err) { | ||
74 | wstats->qual.qual = (int)le16_to_cpu(cq.qual); | ||
75 | wstats->qual.level = (int)le16_to_cpu(cq.signal) - 0x95; | ||
76 | wstats->qual.noise = (int)le16_to_cpu(cq.noise) - 0x95; | ||
77 | wstats->qual.updated = | ||
78 | IW_QUAL_ALL_UPDATED | IW_QUAL_DBM; | ||
79 | } | ||
80 | } | ||
81 | |||
82 | orinoco_unlock(priv, &flags); | ||
83 | return wstats; | ||
84 | } | ||
85 | |||
86 | /********************************************************************/ | ||
87 | /* Wireless extensions */ | ||
88 | /********************************************************************/ | ||
89 | |||
90 | static int orinoco_ioctl_getname(struct net_device *dev, | ||
91 | struct iw_request_info *info, | ||
92 | char *name, | ||
93 | char *extra) | ||
94 | { | ||
95 | struct orinoco_private *priv = netdev_priv(dev); | ||
96 | int numrates; | ||
97 | int err; | ||
98 | |||
99 | err = orinoco_hw_get_bitratelist(priv, &numrates, NULL, 0); | ||
100 | |||
101 | if (!err && (numrates > 2)) | ||
102 | strcpy(name, "IEEE 802.11b"); | ||
103 | else | ||
104 | strcpy(name, "IEEE 802.11-DS"); | ||
105 | |||
106 | return 0; | ||
107 | } | ||
108 | |||
109 | static int orinoco_ioctl_setwap(struct net_device *dev, | ||
110 | struct iw_request_info *info, | ||
111 | struct sockaddr *ap_addr, | ||
112 | char *extra) | ||
113 | { | ||
114 | struct orinoco_private *priv = netdev_priv(dev); | ||
115 | int err = -EINPROGRESS; /* Call commit handler */ | ||
116 | unsigned long flags; | ||
117 | static const u8 off_addr[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; | ||
118 | static const u8 any_addr[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; | ||
119 | |||
120 | if (orinoco_lock(priv, &flags) != 0) | ||
121 | return -EBUSY; | ||
122 | |||
123 | /* Enable automatic roaming - no sanity checks are needed */ | ||
124 | if (memcmp(&ap_addr->sa_data, off_addr, ETH_ALEN) == 0 || | ||
125 | memcmp(&ap_addr->sa_data, any_addr, ETH_ALEN) == 0) { | ||
126 | priv->bssid_fixed = 0; | ||
127 | memset(priv->desired_bssid, 0, ETH_ALEN); | ||
128 | |||
129 | /* "off" means keep existing connection */ | ||
130 | if (ap_addr->sa_data[0] == 0) { | ||
131 | __orinoco_hw_set_wap(priv); | ||
132 | err = 0; | ||
133 | } | ||
134 | goto out; | ||
135 | } | ||
136 | |||
137 | if (priv->firmware_type == FIRMWARE_TYPE_AGERE) { | ||
138 | printk(KERN_WARNING "%s: Lucent/Agere firmware doesn't " | ||
139 | "support manual roaming\n", | ||
140 | dev->name); | ||
141 | err = -EOPNOTSUPP; | ||
142 | goto out; | ||
143 | } | ||
144 | |||
145 | if (priv->iw_mode != IW_MODE_INFRA) { | ||
146 | printk(KERN_WARNING "%s: Manual roaming supported only in " | ||
147 | "managed mode\n", dev->name); | ||
148 | err = -EOPNOTSUPP; | ||
149 | goto out; | ||
150 | } | ||
151 | |||
152 | /* Intersil firmware hangs without Desired ESSID */ | ||
153 | if (priv->firmware_type == FIRMWARE_TYPE_INTERSIL && | ||
154 | strlen(priv->desired_essid) == 0) { | ||
155 | printk(KERN_WARNING "%s: Desired ESSID must be set for " | ||
156 | "manual roaming\n", dev->name); | ||
157 | err = -EOPNOTSUPP; | ||
158 | goto out; | ||
159 | } | ||
160 | |||
161 | /* Finally, enable manual roaming */ | ||
162 | priv->bssid_fixed = 1; | ||
163 | memcpy(priv->desired_bssid, &ap_addr->sa_data, ETH_ALEN); | ||
164 | |||
165 | out: | ||
166 | orinoco_unlock(priv, &flags); | ||
167 | return err; | ||
168 | } | ||
169 | |||
170 | static int orinoco_ioctl_getwap(struct net_device *dev, | ||
171 | struct iw_request_info *info, | ||
172 | struct sockaddr *ap_addr, | ||
173 | char *extra) | ||
174 | { | ||
175 | struct orinoco_private *priv = netdev_priv(dev); | ||
176 | |||
177 | hermes_t *hw = &priv->hw; | ||
178 | int err = 0; | ||
179 | unsigned long flags; | ||
180 | |||
181 | if (orinoco_lock(priv, &flags) != 0) | ||
182 | return -EBUSY; | ||
183 | |||
184 | ap_addr->sa_family = ARPHRD_ETHER; | ||
185 | err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_CURRENTBSSID, | ||
186 | ETH_ALEN, NULL, ap_addr->sa_data); | ||
187 | |||
188 | orinoco_unlock(priv, &flags); | ||
189 | |||
190 | return err; | ||
191 | } | ||
192 | |||
193 | static int orinoco_ioctl_setmode(struct net_device *dev, | ||
194 | struct iw_request_info *info, | ||
195 | u32 *mode, | ||
196 | char *extra) | ||
197 | { | ||
198 | struct orinoco_private *priv = netdev_priv(dev); | ||
199 | int err = -EINPROGRESS; /* Call commit handler */ | ||
200 | unsigned long flags; | ||
201 | |||
202 | if (priv->iw_mode == *mode) | ||
203 | return 0; | ||
204 | |||
205 | if (orinoco_lock(priv, &flags) != 0) | ||
206 | return -EBUSY; | ||
207 | |||
208 | switch (*mode) { | ||
209 | case IW_MODE_ADHOC: | ||
210 | if (!priv->has_ibss && !priv->has_port3) | ||
211 | err = -EOPNOTSUPP; | ||
212 | break; | ||
213 | |||
214 | case IW_MODE_INFRA: | ||
215 | break; | ||
216 | |||
217 | case IW_MODE_MONITOR: | ||
218 | if (priv->broken_monitor && !force_monitor) { | ||
219 | printk(KERN_WARNING "%s: Monitor mode support is " | ||
220 | "buggy in this firmware, not enabling\n", | ||
221 | dev->name); | ||
222 | err = -EOPNOTSUPP; | ||
223 | } | ||
224 | break; | ||
225 | |||
226 | default: | ||
227 | err = -EOPNOTSUPP; | ||
228 | break; | ||
229 | } | ||
230 | |||
231 | if (err == -EINPROGRESS) { | ||
232 | priv->iw_mode = *mode; | ||
233 | set_port_type(priv); | ||
234 | } | ||
235 | |||
236 | orinoco_unlock(priv, &flags); | ||
237 | |||
238 | return err; | ||
239 | } | ||
240 | |||
241 | static int orinoco_ioctl_getmode(struct net_device *dev, | ||
242 | struct iw_request_info *info, | ||
243 | u32 *mode, | ||
244 | char *extra) | ||
245 | { | ||
246 | struct orinoco_private *priv = netdev_priv(dev); | ||
247 | |||
248 | *mode = priv->iw_mode; | ||
249 | return 0; | ||
250 | } | ||
251 | |||
252 | static int orinoco_ioctl_getiwrange(struct net_device *dev, | ||
253 | struct iw_request_info *info, | ||
254 | struct iw_point *rrq, | ||
255 | char *extra) | ||
256 | { | ||
257 | struct orinoco_private *priv = netdev_priv(dev); | ||
258 | int err = 0; | ||
259 | struct iw_range *range = (struct iw_range *) extra; | ||
260 | int numrates; | ||
261 | int i, k; | ||
262 | |||
263 | rrq->length = sizeof(struct iw_range); | ||
264 | memset(range, 0, sizeof(struct iw_range)); | ||
265 | |||
266 | range->we_version_compiled = WIRELESS_EXT; | ||
267 | range->we_version_source = 22; | ||
268 | |||
269 | /* Set available channels/frequencies */ | ||
270 | range->num_channels = NUM_CHANNELS; | ||
271 | k = 0; | ||
272 | for (i = 0; i < NUM_CHANNELS; i++) { | ||
273 | if (priv->channel_mask & (1 << i)) { | ||
274 | range->freq[k].i = i + 1; | ||
275 | range->freq[k].m = (ieee80211_dsss_chan_to_freq(i + 1) * | ||
276 | 100000); | ||
277 | range->freq[k].e = 1; | ||
278 | k++; | ||
279 | } | ||
280 | |||
281 | if (k >= IW_MAX_FREQUENCIES) | ||
282 | break; | ||
283 | } | ||
284 | range->num_frequency = k; | ||
285 | range->sensitivity = 3; | ||
286 | |||
287 | if (priv->has_wep) { | ||
288 | range->max_encoding_tokens = ORINOCO_MAX_KEYS; | ||
289 | range->encoding_size[0] = SMALL_KEY_SIZE; | ||
290 | range->num_encoding_sizes = 1; | ||
291 | |||
292 | if (priv->has_big_wep) { | ||
293 | range->encoding_size[1] = LARGE_KEY_SIZE; | ||
294 | range->num_encoding_sizes = 2; | ||
295 | } | ||
296 | } | ||
297 | |||
298 | if (priv->has_wpa) | ||
299 | range->enc_capa = IW_ENC_CAPA_WPA | IW_ENC_CAPA_CIPHER_TKIP; | ||
300 | |||
301 | if ((priv->iw_mode == IW_MODE_ADHOC) && (!SPY_NUMBER(priv))) { | ||
302 | /* Quality stats meaningless in ad-hoc mode */ | ||
303 | } else { | ||
304 | range->max_qual.qual = 0x8b - 0x2f; | ||
305 | range->max_qual.level = 0x2f - 0x95 - 1; | ||
306 | range->max_qual.noise = 0x2f - 0x95 - 1; | ||
307 | /* Need to get better values */ | ||
308 | range->avg_qual.qual = 0x24; | ||
309 | range->avg_qual.level = 0xC2; | ||
310 | range->avg_qual.noise = 0x9E; | ||
311 | } | ||
312 | |||
313 | err = orinoco_hw_get_bitratelist(priv, &numrates, | ||
314 | range->bitrate, IW_MAX_BITRATES); | ||
315 | if (err) | ||
316 | return err; | ||
317 | range->num_bitrates = numrates; | ||
318 | |||
319 | /* Set an indication of the max TCP throughput in bit/s that we can | ||
320 | * expect using this interface. May be use for QoS stuff... | ||
321 | * Jean II */ | ||
322 | if (numrates > 2) | ||
323 | range->throughput = 5 * 1000 * 1000; /* ~5 Mb/s */ | ||
324 | else | ||
325 | range->throughput = 1.5 * 1000 * 1000; /* ~1.5 Mb/s */ | ||
326 | |||
327 | range->min_rts = 0; | ||
328 | range->max_rts = 2347; | ||
329 | range->min_frag = 256; | ||
330 | range->max_frag = 2346; | ||
331 | |||
332 | range->min_pmp = 0; | ||
333 | range->max_pmp = 65535000; | ||
334 | range->min_pmt = 0; | ||
335 | range->max_pmt = 65535 * 1000; /* ??? */ | ||
336 | range->pmp_flags = IW_POWER_PERIOD; | ||
337 | range->pmt_flags = IW_POWER_TIMEOUT; | ||
338 | range->pm_capa = (IW_POWER_PERIOD | IW_POWER_TIMEOUT | | ||
339 | IW_POWER_UNICAST_R); | ||
340 | |||
341 | range->retry_capa = IW_RETRY_LIMIT | IW_RETRY_LIFETIME; | ||
342 | range->retry_flags = IW_RETRY_LIMIT; | ||
343 | range->r_time_flags = IW_RETRY_LIFETIME; | ||
344 | range->min_retry = 0; | ||
345 | range->max_retry = 65535; /* ??? */ | ||
346 | range->min_r_time = 0; | ||
347 | range->max_r_time = 65535 * 1000; /* ??? */ | ||
348 | |||
349 | if (priv->firmware_type == FIRMWARE_TYPE_AGERE) | ||
350 | range->scan_capa = IW_SCAN_CAPA_ESSID; | ||
351 | else | ||
352 | range->scan_capa = IW_SCAN_CAPA_NONE; | ||
353 | |||
354 | /* Event capability (kernel) */ | ||
355 | IW_EVENT_CAPA_SET_KERNEL(range->event_capa); | ||
356 | /* Event capability (driver) */ | ||
357 | IW_EVENT_CAPA_SET(range->event_capa, SIOCGIWTHRSPY); | ||
358 | IW_EVENT_CAPA_SET(range->event_capa, SIOCGIWAP); | ||
359 | IW_EVENT_CAPA_SET(range->event_capa, SIOCGIWSCAN); | ||
360 | IW_EVENT_CAPA_SET(range->event_capa, IWEVTXDROP); | ||
361 | |||
362 | return 0; | ||
363 | } | ||
364 | |||
365 | static int orinoco_ioctl_setiwencode(struct net_device *dev, | ||
366 | struct iw_request_info *info, | ||
367 | struct iw_point *erq, | ||
368 | char *keybuf) | ||
369 | { | ||
370 | struct orinoco_private *priv = netdev_priv(dev); | ||
371 | int index = (erq->flags & IW_ENCODE_INDEX) - 1; | ||
372 | int setindex = priv->tx_key; | ||
373 | int encode_alg = priv->encode_alg; | ||
374 | int restricted = priv->wep_restrict; | ||
375 | u16 xlen = 0; | ||
376 | int err = -EINPROGRESS; /* Call commit handler */ | ||
377 | unsigned long flags; | ||
378 | |||
379 | if (!priv->has_wep) | ||
380 | return -EOPNOTSUPP; | ||
381 | |||
382 | if (erq->pointer) { | ||
383 | /* We actually have a key to set - check its length */ | ||
384 | if (erq->length > LARGE_KEY_SIZE) | ||
385 | return -E2BIG; | ||
386 | |||
387 | if ((erq->length > SMALL_KEY_SIZE) && !priv->has_big_wep) | ||
388 | return -E2BIG; | ||
389 | } | ||
390 | |||
391 | if (orinoco_lock(priv, &flags) != 0) | ||
392 | return -EBUSY; | ||
393 | |||
394 | /* Clear any TKIP key we have */ | ||
395 | if ((priv->has_wpa) && (priv->encode_alg == IW_ENCODE_ALG_TKIP)) | ||
396 | (void) orinoco_clear_tkip_key(priv, setindex); | ||
397 | |||
398 | if (erq->length > 0) { | ||
399 | if ((index < 0) || (index >= ORINOCO_MAX_KEYS)) | ||
400 | index = priv->tx_key; | ||
401 | |||
402 | /* Adjust key length to a supported value */ | ||
403 | if (erq->length > SMALL_KEY_SIZE) | ||
404 | xlen = LARGE_KEY_SIZE; | ||
405 | else if (erq->length > 0) | ||
406 | xlen = SMALL_KEY_SIZE; | ||
407 | else | ||
408 | xlen = 0; | ||
409 | |||
410 | /* Switch on WEP if off */ | ||
411 | if ((encode_alg != IW_ENCODE_ALG_WEP) && (xlen > 0)) { | ||
412 | setindex = index; | ||
413 | encode_alg = IW_ENCODE_ALG_WEP; | ||
414 | } | ||
415 | } else { | ||
416 | /* Important note : if the user do "iwconfig eth0 enc off", | ||
417 | * we will arrive there with an index of -1. This is valid | ||
418 | * but need to be taken care off... Jean II */ | ||
419 | if ((index < 0) || (index >= ORINOCO_MAX_KEYS)) { | ||
420 | if ((index != -1) || (erq->flags == 0)) { | ||
421 | err = -EINVAL; | ||
422 | goto out; | ||
423 | } | ||
424 | } else { | ||
425 | /* Set the index : Check that the key is valid */ | ||
426 | if (priv->keys[index].len == 0) { | ||
427 | err = -EINVAL; | ||
428 | goto out; | ||
429 | } | ||
430 | setindex = index; | ||
431 | } | ||
432 | } | ||
433 | |||
434 | if (erq->flags & IW_ENCODE_DISABLED) | ||
435 | encode_alg = IW_ENCODE_ALG_NONE; | ||
436 | if (erq->flags & IW_ENCODE_OPEN) | ||
437 | restricted = 0; | ||
438 | if (erq->flags & IW_ENCODE_RESTRICTED) | ||
439 | restricted = 1; | ||
440 | |||
441 | if (erq->pointer && erq->length > 0) { | ||
442 | priv->keys[index].len = cpu_to_le16(xlen); | ||
443 | memset(priv->keys[index].data, 0, | ||
444 | sizeof(priv->keys[index].data)); | ||
445 | memcpy(priv->keys[index].data, keybuf, erq->length); | ||
446 | } | ||
447 | priv->tx_key = setindex; | ||
448 | |||
449 | /* Try fast key change if connected and only keys are changed */ | ||
450 | if ((priv->encode_alg == encode_alg) && | ||
451 | (priv->wep_restrict == restricted) && | ||
452 | netif_carrier_ok(dev)) { | ||
453 | err = __orinoco_hw_setup_wepkeys(priv); | ||
454 | /* No need to commit if successful */ | ||
455 | goto out; | ||
456 | } | ||
457 | |||
458 | priv->encode_alg = encode_alg; | ||
459 | priv->wep_restrict = restricted; | ||
460 | |||
461 | out: | ||
462 | orinoco_unlock(priv, &flags); | ||
463 | |||
464 | return err; | ||
465 | } | ||
466 | |||
467 | static int orinoco_ioctl_getiwencode(struct net_device *dev, | ||
468 | struct iw_request_info *info, | ||
469 | struct iw_point *erq, | ||
470 | char *keybuf) | ||
471 | { | ||
472 | struct orinoco_private *priv = netdev_priv(dev); | ||
473 | int index = (erq->flags & IW_ENCODE_INDEX) - 1; | ||
474 | u16 xlen = 0; | ||
475 | unsigned long flags; | ||
476 | |||
477 | if (!priv->has_wep) | ||
478 | return -EOPNOTSUPP; | ||
479 | |||
480 | if (orinoco_lock(priv, &flags) != 0) | ||
481 | return -EBUSY; | ||
482 | |||
483 | if ((index < 0) || (index >= ORINOCO_MAX_KEYS)) | ||
484 | index = priv->tx_key; | ||
485 | |||
486 | erq->flags = 0; | ||
487 | if (!priv->encode_alg) | ||
488 | erq->flags |= IW_ENCODE_DISABLED; | ||
489 | erq->flags |= index + 1; | ||
490 | |||
491 | if (priv->wep_restrict) | ||
492 | erq->flags |= IW_ENCODE_RESTRICTED; | ||
493 | else | ||
494 | erq->flags |= IW_ENCODE_OPEN; | ||
495 | |||
496 | xlen = le16_to_cpu(priv->keys[index].len); | ||
497 | |||
498 | erq->length = xlen; | ||
499 | |||
500 | memcpy(keybuf, priv->keys[index].data, ORINOCO_MAX_KEY_SIZE); | ||
501 | |||
502 | orinoco_unlock(priv, &flags); | ||
503 | return 0; | ||
504 | } | ||
505 | |||
506 | static int orinoco_ioctl_setessid(struct net_device *dev, | ||
507 | struct iw_request_info *info, | ||
508 | struct iw_point *erq, | ||
509 | char *essidbuf) | ||
510 | { | ||
511 | struct orinoco_private *priv = netdev_priv(dev); | ||
512 | unsigned long flags; | ||
513 | |||
514 | /* Note : ESSID is ignored in Ad-Hoc demo mode, but we can set it | ||
515 | * anyway... - Jean II */ | ||
516 | |||
517 | /* Hum... Should not use Wireless Extension constant (may change), | ||
518 | * should use our own... - Jean II */ | ||
519 | if (erq->length > IW_ESSID_MAX_SIZE) | ||
520 | return -E2BIG; | ||
521 | |||
522 | if (orinoco_lock(priv, &flags) != 0) | ||
523 | return -EBUSY; | ||
524 | |||
525 | /* NULL the string (for NULL termination & ESSID = ANY) - Jean II */ | ||
526 | memset(priv->desired_essid, 0, sizeof(priv->desired_essid)); | ||
527 | |||
528 | /* If not ANY, get the new ESSID */ | ||
529 | if (erq->flags) | ||
530 | memcpy(priv->desired_essid, essidbuf, erq->length); | ||
531 | |||
532 | orinoco_unlock(priv, &flags); | ||
533 | |||
534 | return -EINPROGRESS; /* Call commit handler */ | ||
535 | } | ||
536 | |||
537 | static int orinoco_ioctl_getessid(struct net_device *dev, | ||
538 | struct iw_request_info *info, | ||
539 | struct iw_point *erq, | ||
540 | char *essidbuf) | ||
541 | { | ||
542 | struct orinoco_private *priv = netdev_priv(dev); | ||
543 | int active; | ||
544 | int err = 0; | ||
545 | unsigned long flags; | ||
546 | |||
547 | if (netif_running(dev)) { | ||
548 | err = orinoco_hw_get_essid(priv, &active, essidbuf); | ||
549 | if (err < 0) | ||
550 | return err; | ||
551 | erq->length = err; | ||
552 | } else { | ||
553 | if (orinoco_lock(priv, &flags) != 0) | ||
554 | return -EBUSY; | ||
555 | memcpy(essidbuf, priv->desired_essid, IW_ESSID_MAX_SIZE); | ||
556 | erq->length = strlen(priv->desired_essid); | ||
557 | orinoco_unlock(priv, &flags); | ||
558 | } | ||
559 | |||
560 | erq->flags = 1; | ||
561 | |||
562 | return 0; | ||
563 | } | ||
564 | |||
565 | static int orinoco_ioctl_setnick(struct net_device *dev, | ||
566 | struct iw_request_info *info, | ||
567 | struct iw_point *nrq, | ||
568 | char *nickbuf) | ||
569 | { | ||
570 | struct orinoco_private *priv = netdev_priv(dev); | ||
571 | unsigned long flags; | ||
572 | |||
573 | if (nrq->length > IW_ESSID_MAX_SIZE) | ||
574 | return -E2BIG; | ||
575 | |||
576 | if (orinoco_lock(priv, &flags) != 0) | ||
577 | return -EBUSY; | ||
578 | |||
579 | memset(priv->nick, 0, sizeof(priv->nick)); | ||
580 | memcpy(priv->nick, nickbuf, nrq->length); | ||
581 | |||
582 | orinoco_unlock(priv, &flags); | ||
583 | |||
584 | return -EINPROGRESS; /* Call commit handler */ | ||
585 | } | ||
586 | |||
587 | static int orinoco_ioctl_getnick(struct net_device *dev, | ||
588 | struct iw_request_info *info, | ||
589 | struct iw_point *nrq, | ||
590 | char *nickbuf) | ||
591 | { | ||
592 | struct orinoco_private *priv = netdev_priv(dev); | ||
593 | unsigned long flags; | ||
594 | |||
595 | if (orinoco_lock(priv, &flags) != 0) | ||
596 | return -EBUSY; | ||
597 | |||
598 | memcpy(nickbuf, priv->nick, IW_ESSID_MAX_SIZE); | ||
599 | orinoco_unlock(priv, &flags); | ||
600 | |||
601 | nrq->length = strlen(priv->nick); | ||
602 | |||
603 | return 0; | ||
604 | } | ||
605 | |||
606 | static int orinoco_ioctl_setfreq(struct net_device *dev, | ||
607 | struct iw_request_info *info, | ||
608 | struct iw_freq *frq, | ||
609 | char *extra) | ||
610 | { | ||
611 | struct orinoco_private *priv = netdev_priv(dev); | ||
612 | int chan = -1; | ||
613 | unsigned long flags; | ||
614 | int err = -EINPROGRESS; /* Call commit handler */ | ||
615 | |||
616 | /* In infrastructure mode the AP sets the channel */ | ||
617 | if (priv->iw_mode == IW_MODE_INFRA) | ||
618 | return -EBUSY; | ||
619 | |||
620 | if ((frq->e == 0) && (frq->m <= 1000)) { | ||
621 | /* Setting by channel number */ | ||
622 | chan = frq->m; | ||
623 | } else { | ||
624 | /* Setting by frequency */ | ||
625 | int denom = 1; | ||
626 | int i; | ||
627 | |||
628 | /* Calculate denominator to rescale to MHz */ | ||
629 | for (i = 0; i < (6 - frq->e); i++) | ||
630 | denom *= 10; | ||
631 | |||
632 | chan = ieee80211_freq_to_dsss_chan(frq->m / denom); | ||
633 | } | ||
634 | |||
635 | if ((chan < 1) || (chan > NUM_CHANNELS) || | ||
636 | !(priv->channel_mask & (1 << (chan-1)))) | ||
637 | return -EINVAL; | ||
638 | |||
639 | if (orinoco_lock(priv, &flags) != 0) | ||
640 | return -EBUSY; | ||
641 | |||
642 | priv->channel = chan; | ||
643 | if (priv->iw_mode == IW_MODE_MONITOR) { | ||
644 | /* Fast channel change - no commit if successful */ | ||
645 | hermes_t *hw = &priv->hw; | ||
646 | err = hermes_docmd_wait(hw, HERMES_CMD_TEST | | ||
647 | HERMES_TEST_SET_CHANNEL, | ||
648 | chan, NULL); | ||
649 | } | ||
650 | orinoco_unlock(priv, &flags); | ||
651 | |||
652 | return err; | ||
653 | } | ||
654 | |||
655 | static int orinoco_ioctl_getfreq(struct net_device *dev, | ||
656 | struct iw_request_info *info, | ||
657 | struct iw_freq *frq, | ||
658 | char *extra) | ||
659 | { | ||
660 | struct orinoco_private *priv = netdev_priv(dev); | ||
661 | int tmp; | ||
662 | |||
663 | /* Locking done in there */ | ||
664 | tmp = orinoco_hw_get_freq(priv); | ||
665 | if (tmp < 0) | ||
666 | return tmp; | ||
667 | |||
668 | frq->m = tmp * 100000; | ||
669 | frq->e = 1; | ||
670 | |||
671 | return 0; | ||
672 | } | ||
673 | |||
674 | static int orinoco_ioctl_getsens(struct net_device *dev, | ||
675 | struct iw_request_info *info, | ||
676 | struct iw_param *srq, | ||
677 | char *extra) | ||
678 | { | ||
679 | struct orinoco_private *priv = netdev_priv(dev); | ||
680 | hermes_t *hw = &priv->hw; | ||
681 | u16 val; | ||
682 | int err; | ||
683 | unsigned long flags; | ||
684 | |||
685 | if (!priv->has_sensitivity) | ||
686 | return -EOPNOTSUPP; | ||
687 | |||
688 | if (orinoco_lock(priv, &flags) != 0) | ||
689 | return -EBUSY; | ||
690 | err = hermes_read_wordrec(hw, USER_BAP, | ||
691 | HERMES_RID_CNFSYSTEMSCALE, &val); | ||
692 | orinoco_unlock(priv, &flags); | ||
693 | |||
694 | if (err) | ||
695 | return err; | ||
696 | |||
697 | srq->value = val; | ||
698 | srq->fixed = 0; /* auto */ | ||
699 | |||
700 | return 0; | ||
701 | } | ||
702 | |||
703 | static int orinoco_ioctl_setsens(struct net_device *dev, | ||
704 | struct iw_request_info *info, | ||
705 | struct iw_param *srq, | ||
706 | char *extra) | ||
707 | { | ||
708 | struct orinoco_private *priv = netdev_priv(dev); | ||
709 | int val = srq->value; | ||
710 | unsigned long flags; | ||
711 | |||
712 | if (!priv->has_sensitivity) | ||
713 | return -EOPNOTSUPP; | ||
714 | |||
715 | if ((val < 1) || (val > 3)) | ||
716 | return -EINVAL; | ||
717 | |||
718 | if (orinoco_lock(priv, &flags) != 0) | ||
719 | return -EBUSY; | ||
720 | priv->ap_density = val; | ||
721 | orinoco_unlock(priv, &flags); | ||
722 | |||
723 | return -EINPROGRESS; /* Call commit handler */ | ||
724 | } | ||
725 | |||
726 | static int orinoco_ioctl_setrts(struct net_device *dev, | ||
727 | struct iw_request_info *info, | ||
728 | struct iw_param *rrq, | ||
729 | char *extra) | ||
730 | { | ||
731 | struct orinoco_private *priv = netdev_priv(dev); | ||
732 | int val = rrq->value; | ||
733 | unsigned long flags; | ||
734 | |||
735 | if (rrq->disabled) | ||
736 | val = 2347; | ||
737 | |||
738 | if ((val < 0) || (val > 2347)) | ||
739 | return -EINVAL; | ||
740 | |||
741 | if (orinoco_lock(priv, &flags) != 0) | ||
742 | return -EBUSY; | ||
743 | |||
744 | priv->rts_thresh = val; | ||
745 | orinoco_unlock(priv, &flags); | ||
746 | |||
747 | return -EINPROGRESS; /* Call commit handler */ | ||
748 | } | ||
749 | |||
750 | static int orinoco_ioctl_getrts(struct net_device *dev, | ||
751 | struct iw_request_info *info, | ||
752 | struct iw_param *rrq, | ||
753 | char *extra) | ||
754 | { | ||
755 | struct orinoco_private *priv = netdev_priv(dev); | ||
756 | |||
757 | rrq->value = priv->rts_thresh; | ||
758 | rrq->disabled = (rrq->value == 2347); | ||
759 | rrq->fixed = 1; | ||
760 | |||
761 | return 0; | ||
762 | } | ||
763 | |||
764 | static int orinoco_ioctl_setfrag(struct net_device *dev, | ||
765 | struct iw_request_info *info, | ||
766 | struct iw_param *frq, | ||
767 | char *extra) | ||
768 | { | ||
769 | struct orinoco_private *priv = netdev_priv(dev); | ||
770 | int err = -EINPROGRESS; /* Call commit handler */ | ||
771 | unsigned long flags; | ||
772 | |||
773 | if (orinoco_lock(priv, &flags) != 0) | ||
774 | return -EBUSY; | ||
775 | |||
776 | if (priv->has_mwo) { | ||
777 | if (frq->disabled) | ||
778 | priv->mwo_robust = 0; | ||
779 | else { | ||
780 | if (frq->fixed) | ||
781 | printk(KERN_WARNING "%s: Fixed fragmentation " | ||
782 | "is not supported on this firmware. " | ||
783 | "Using MWO robust instead.\n", | ||
784 | dev->name); | ||
785 | priv->mwo_robust = 1; | ||
786 | } | ||
787 | } else { | ||
788 | if (frq->disabled) | ||
789 | priv->frag_thresh = 2346; | ||
790 | else { | ||
791 | if ((frq->value < 256) || (frq->value > 2346)) | ||
792 | err = -EINVAL; | ||
793 | else | ||
794 | /* must be even */ | ||
795 | priv->frag_thresh = frq->value & ~0x1; | ||
796 | } | ||
797 | } | ||
798 | |||
799 | orinoco_unlock(priv, &flags); | ||
800 | |||
801 | return err; | ||
802 | } | ||
803 | |||
804 | static int orinoco_ioctl_getfrag(struct net_device *dev, | ||
805 | struct iw_request_info *info, | ||
806 | struct iw_param *frq, | ||
807 | char *extra) | ||
808 | { | ||
809 | struct orinoco_private *priv = netdev_priv(dev); | ||
810 | hermes_t *hw = &priv->hw; | ||
811 | int err; | ||
812 | u16 val; | ||
813 | unsigned long flags; | ||
814 | |||
815 | if (orinoco_lock(priv, &flags) != 0) | ||
816 | return -EBUSY; | ||
817 | |||
818 | if (priv->has_mwo) { | ||
819 | err = hermes_read_wordrec(hw, USER_BAP, | ||
820 | HERMES_RID_CNFMWOROBUST_AGERE, | ||
821 | &val); | ||
822 | if (err) | ||
823 | val = 0; | ||
824 | |||
825 | frq->value = val ? 2347 : 0; | ||
826 | frq->disabled = !val; | ||
827 | frq->fixed = 0; | ||
828 | } else { | ||
829 | err = hermes_read_wordrec(hw, USER_BAP, | ||
830 | HERMES_RID_CNFFRAGMENTATIONTHRESHOLD, | ||
831 | &val); | ||
832 | if (err) | ||
833 | val = 0; | ||
834 | |||
835 | frq->value = val; | ||
836 | frq->disabled = (val >= 2346); | ||
837 | frq->fixed = 1; | ||
838 | } | ||
839 | |||
840 | orinoco_unlock(priv, &flags); | ||
841 | |||
842 | return err; | ||
843 | } | ||
844 | |||
845 | static int orinoco_ioctl_setrate(struct net_device *dev, | ||
846 | struct iw_request_info *info, | ||
847 | struct iw_param *rrq, | ||
848 | char *extra) | ||
849 | { | ||
850 | struct orinoco_private *priv = netdev_priv(dev); | ||
851 | int ratemode; | ||
852 | int bitrate; /* 100s of kilobits */ | ||
853 | unsigned long flags; | ||
854 | |||
855 | /* As the user space doesn't know our highest rate, it uses -1 | ||
856 | * to ask us to set the highest rate. Test it using "iwconfig | ||
857 | * ethX rate auto" - Jean II */ | ||
858 | if (rrq->value == -1) | ||
859 | bitrate = 110; | ||
860 | else { | ||
861 | if (rrq->value % 100000) | ||
862 | return -EINVAL; | ||
863 | bitrate = rrq->value / 100000; | ||
864 | } | ||
865 | |||
866 | ratemode = orinoco_get_bitratemode(bitrate, !rrq->fixed); | ||
867 | |||
868 | if (ratemode == -1) | ||
869 | return -EINVAL; | ||
870 | |||
871 | if (orinoco_lock(priv, &flags) != 0) | ||
872 | return -EBUSY; | ||
873 | priv->bitratemode = ratemode; | ||
874 | orinoco_unlock(priv, &flags); | ||
875 | |||
876 | return -EINPROGRESS; | ||
877 | } | ||
878 | |||
879 | static int orinoco_ioctl_getrate(struct net_device *dev, | ||
880 | struct iw_request_info *info, | ||
881 | struct iw_param *rrq, | ||
882 | char *extra) | ||
883 | { | ||
884 | struct orinoco_private *priv = netdev_priv(dev); | ||
885 | int err = 0; | ||
886 | int bitrate, automatic; | ||
887 | unsigned long flags; | ||
888 | |||
889 | if (orinoco_lock(priv, &flags) != 0) | ||
890 | return -EBUSY; | ||
891 | |||
892 | orinoco_get_ratemode_cfg(priv->bitratemode, &bitrate, &automatic); | ||
893 | |||
894 | /* If the interface is running we try to find more about the | ||
895 | current mode */ | ||
896 | if (netif_running(dev)) | ||
897 | err = orinoco_hw_get_act_bitrate(priv, &bitrate); | ||
898 | |||
899 | orinoco_unlock(priv, &flags); | ||
900 | |||
901 | rrq->value = bitrate; | ||
902 | rrq->fixed = !automatic; | ||
903 | rrq->disabled = 0; | ||
904 | |||
905 | return err; | ||
906 | } | ||
907 | |||
908 | static int orinoco_ioctl_setpower(struct net_device *dev, | ||
909 | struct iw_request_info *info, | ||
910 | struct iw_param *prq, | ||
911 | char *extra) | ||
912 | { | ||
913 | struct orinoco_private *priv = netdev_priv(dev); | ||
914 | int err = -EINPROGRESS; /* Call commit handler */ | ||
915 | unsigned long flags; | ||
916 | |||
917 | if (orinoco_lock(priv, &flags) != 0) | ||
918 | return -EBUSY; | ||
919 | |||
920 | if (prq->disabled) { | ||
921 | priv->pm_on = 0; | ||
922 | } else { | ||
923 | switch (prq->flags & IW_POWER_MODE) { | ||
924 | case IW_POWER_UNICAST_R: | ||
925 | priv->pm_mcast = 0; | ||
926 | priv->pm_on = 1; | ||
927 | break; | ||
928 | case IW_POWER_ALL_R: | ||
929 | priv->pm_mcast = 1; | ||
930 | priv->pm_on = 1; | ||
931 | break; | ||
932 | case IW_POWER_ON: | ||
933 | /* No flags : but we may have a value - Jean II */ | ||
934 | break; | ||
935 | default: | ||
936 | err = -EINVAL; | ||
937 | goto out; | ||
938 | } | ||
939 | |||
940 | if (prq->flags & IW_POWER_TIMEOUT) { | ||
941 | priv->pm_on = 1; | ||
942 | priv->pm_timeout = prq->value / 1000; | ||
943 | } | ||
944 | if (prq->flags & IW_POWER_PERIOD) { | ||
945 | priv->pm_on = 1; | ||
946 | priv->pm_period = prq->value / 1000; | ||
947 | } | ||
948 | /* It's valid to not have a value if we are just toggling | ||
949 | * the flags... Jean II */ | ||
950 | if (!priv->pm_on) { | ||
951 | err = -EINVAL; | ||
952 | goto out; | ||
953 | } | ||
954 | } | ||
955 | |||
956 | out: | ||
957 | orinoco_unlock(priv, &flags); | ||
958 | |||
959 | return err; | ||
960 | } | ||
961 | |||
962 | static int orinoco_ioctl_getpower(struct net_device *dev, | ||
963 | struct iw_request_info *info, | ||
964 | struct iw_param *prq, | ||
965 | char *extra) | ||
966 | { | ||
967 | struct orinoco_private *priv = netdev_priv(dev); | ||
968 | hermes_t *hw = &priv->hw; | ||
969 | int err = 0; | ||
970 | u16 enable, period, timeout, mcast; | ||
971 | unsigned long flags; | ||
972 | |||
973 | if (orinoco_lock(priv, &flags) != 0) | ||
974 | return -EBUSY; | ||
975 | |||
976 | err = hermes_read_wordrec(hw, USER_BAP, | ||
977 | HERMES_RID_CNFPMENABLED, &enable); | ||
978 | if (err) | ||
979 | goto out; | ||
980 | |||
981 | err = hermes_read_wordrec(hw, USER_BAP, | ||
982 | HERMES_RID_CNFMAXSLEEPDURATION, &period); | ||
983 | if (err) | ||
984 | goto out; | ||
985 | |||
986 | err = hermes_read_wordrec(hw, USER_BAP, | ||
987 | HERMES_RID_CNFPMHOLDOVERDURATION, &timeout); | ||
988 | if (err) | ||
989 | goto out; | ||
990 | |||
991 | err = hermes_read_wordrec(hw, USER_BAP, | ||
992 | HERMES_RID_CNFMULTICASTRECEIVE, &mcast); | ||
993 | if (err) | ||
994 | goto out; | ||
995 | |||
996 | prq->disabled = !enable; | ||
997 | /* Note : by default, display the period */ | ||
998 | if ((prq->flags & IW_POWER_TYPE) == IW_POWER_TIMEOUT) { | ||
999 | prq->flags = IW_POWER_TIMEOUT; | ||
1000 | prq->value = timeout * 1000; | ||
1001 | } else { | ||
1002 | prq->flags = IW_POWER_PERIOD; | ||
1003 | prq->value = period * 1000; | ||
1004 | } | ||
1005 | if (mcast) | ||
1006 | prq->flags |= IW_POWER_ALL_R; | ||
1007 | else | ||
1008 | prq->flags |= IW_POWER_UNICAST_R; | ||
1009 | |||
1010 | out: | ||
1011 | orinoco_unlock(priv, &flags); | ||
1012 | |||
1013 | return err; | ||
1014 | } | ||
1015 | |||
1016 | static int orinoco_ioctl_set_encodeext(struct net_device *dev, | ||
1017 | struct iw_request_info *info, | ||
1018 | union iwreq_data *wrqu, | ||
1019 | char *extra) | ||
1020 | { | ||
1021 | struct orinoco_private *priv = netdev_priv(dev); | ||
1022 | struct iw_point *encoding = &wrqu->encoding; | ||
1023 | struct iw_encode_ext *ext = (struct iw_encode_ext *)extra; | ||
1024 | int idx, alg = ext->alg, set_key = 1; | ||
1025 | unsigned long flags; | ||
1026 | int err = -EINVAL; | ||
1027 | u16 key_len; | ||
1028 | |||
1029 | if (orinoco_lock(priv, &flags) != 0) | ||
1030 | return -EBUSY; | ||
1031 | |||
1032 | /* Determine and validate the key index */ | ||
1033 | idx = encoding->flags & IW_ENCODE_INDEX; | ||
1034 | if (idx) { | ||
1035 | if ((idx < 1) || (idx > 4)) | ||
1036 | goto out; | ||
1037 | idx--; | ||
1038 | } else | ||
1039 | idx = priv->tx_key; | ||
1040 | |||
1041 | if (encoding->flags & IW_ENCODE_DISABLED) | ||
1042 | alg = IW_ENCODE_ALG_NONE; | ||
1043 | |||
1044 | if (priv->has_wpa && (alg != IW_ENCODE_ALG_TKIP)) { | ||
1045 | /* Clear any TKIP TX key we had */ | ||
1046 | (void) orinoco_clear_tkip_key(priv, priv->tx_key); | ||
1047 | } | ||
1048 | |||
1049 | if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) { | ||
1050 | priv->tx_key = idx; | ||
1051 | set_key = ((alg == IW_ENCODE_ALG_TKIP) || | ||
1052 | (ext->key_len > 0)) ? 1 : 0; | ||
1053 | } | ||
1054 | |||
1055 | if (set_key) { | ||
1056 | /* Set the requested key first */ | ||
1057 | switch (alg) { | ||
1058 | case IW_ENCODE_ALG_NONE: | ||
1059 | priv->encode_alg = alg; | ||
1060 | priv->keys[idx].len = 0; | ||
1061 | break; | ||
1062 | |||
1063 | case IW_ENCODE_ALG_WEP: | ||
1064 | if (ext->key_len > SMALL_KEY_SIZE) | ||
1065 | key_len = LARGE_KEY_SIZE; | ||
1066 | else if (ext->key_len > 0) | ||
1067 | key_len = SMALL_KEY_SIZE; | ||
1068 | else | ||
1069 | goto out; | ||
1070 | |||
1071 | priv->encode_alg = alg; | ||
1072 | priv->keys[idx].len = cpu_to_le16(key_len); | ||
1073 | |||
1074 | key_len = min(ext->key_len, key_len); | ||
1075 | |||
1076 | memset(priv->keys[idx].data, 0, ORINOCO_MAX_KEY_SIZE); | ||
1077 | memcpy(priv->keys[idx].data, ext->key, key_len); | ||
1078 | break; | ||
1079 | |||
1080 | case IW_ENCODE_ALG_TKIP: | ||
1081 | { | ||
1082 | hermes_t *hw = &priv->hw; | ||
1083 | u8 *tkip_iv = NULL; | ||
1084 | |||
1085 | if (!priv->has_wpa || | ||
1086 | (ext->key_len > sizeof(priv->tkip_key[0]))) | ||
1087 | goto out; | ||
1088 | |||
1089 | priv->encode_alg = alg; | ||
1090 | memset(&priv->tkip_key[idx], 0, | ||
1091 | sizeof(priv->tkip_key[idx])); | ||
1092 | memcpy(&priv->tkip_key[idx], ext->key, ext->key_len); | ||
1093 | |||
1094 | if (ext->ext_flags & IW_ENCODE_EXT_RX_SEQ_VALID) | ||
1095 | tkip_iv = &ext->rx_seq[0]; | ||
1096 | |||
1097 | err = __orinoco_hw_set_tkip_key(hw, idx, | ||
1098 | ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY, | ||
1099 | (u8 *) &priv->tkip_key[idx], | ||
1100 | tkip_iv, NULL); | ||
1101 | if (err) | ||
1102 | printk(KERN_ERR "%s: Error %d setting TKIP key" | ||
1103 | "\n", dev->name, err); | ||
1104 | |||
1105 | goto out; | ||
1106 | } | ||
1107 | default: | ||
1108 | goto out; | ||
1109 | } | ||
1110 | } | ||
1111 | err = -EINPROGRESS; | ||
1112 | out: | ||
1113 | orinoco_unlock(priv, &flags); | ||
1114 | |||
1115 | return err; | ||
1116 | } | ||
1117 | |||
1118 | static int orinoco_ioctl_get_encodeext(struct net_device *dev, | ||
1119 | struct iw_request_info *info, | ||
1120 | union iwreq_data *wrqu, | ||
1121 | char *extra) | ||
1122 | { | ||
1123 | struct orinoco_private *priv = netdev_priv(dev); | ||
1124 | struct iw_point *encoding = &wrqu->encoding; | ||
1125 | struct iw_encode_ext *ext = (struct iw_encode_ext *)extra; | ||
1126 | int idx, max_key_len; | ||
1127 | unsigned long flags; | ||
1128 | int err; | ||
1129 | |||
1130 | if (orinoco_lock(priv, &flags) != 0) | ||
1131 | return -EBUSY; | ||
1132 | |||
1133 | err = -EINVAL; | ||
1134 | max_key_len = encoding->length - sizeof(*ext); | ||
1135 | if (max_key_len < 0) | ||
1136 | goto out; | ||
1137 | |||
1138 | idx = encoding->flags & IW_ENCODE_INDEX; | ||
1139 | if (idx) { | ||
1140 | if ((idx < 1) || (idx > 4)) | ||
1141 | goto out; | ||
1142 | idx--; | ||
1143 | } else | ||
1144 | idx = priv->tx_key; | ||
1145 | |||
1146 | encoding->flags = idx + 1; | ||
1147 | memset(ext, 0, sizeof(*ext)); | ||
1148 | |||
1149 | ext->alg = priv->encode_alg; | ||
1150 | switch (priv->encode_alg) { | ||
1151 | case IW_ENCODE_ALG_NONE: | ||
1152 | ext->key_len = 0; | ||
1153 | encoding->flags |= IW_ENCODE_DISABLED; | ||
1154 | break; | ||
1155 | case IW_ENCODE_ALG_WEP: | ||
1156 | ext->key_len = min_t(u16, le16_to_cpu(priv->keys[idx].len), | ||
1157 | max_key_len); | ||
1158 | memcpy(ext->key, priv->keys[idx].data, ext->key_len); | ||
1159 | encoding->flags |= IW_ENCODE_ENABLED; | ||
1160 | break; | ||
1161 | case IW_ENCODE_ALG_TKIP: | ||
1162 | ext->key_len = min_t(u16, sizeof(struct orinoco_tkip_key), | ||
1163 | max_key_len); | ||
1164 | memcpy(ext->key, &priv->tkip_key[idx], ext->key_len); | ||
1165 | encoding->flags |= IW_ENCODE_ENABLED; | ||
1166 | break; | ||
1167 | } | ||
1168 | |||
1169 | err = 0; | ||
1170 | out: | ||
1171 | orinoco_unlock(priv, &flags); | ||
1172 | |||
1173 | return err; | ||
1174 | } | ||
1175 | |||
1176 | static int orinoco_ioctl_set_auth(struct net_device *dev, | ||
1177 | struct iw_request_info *info, | ||
1178 | union iwreq_data *wrqu, char *extra) | ||
1179 | { | ||
1180 | struct orinoco_private *priv = netdev_priv(dev); | ||
1181 | hermes_t *hw = &priv->hw; | ||
1182 | struct iw_param *param = &wrqu->param; | ||
1183 | unsigned long flags; | ||
1184 | int ret = -EINPROGRESS; | ||
1185 | |||
1186 | if (orinoco_lock(priv, &flags) != 0) | ||
1187 | return -EBUSY; | ||
1188 | |||
1189 | switch (param->flags & IW_AUTH_INDEX) { | ||
1190 | case IW_AUTH_WPA_VERSION: | ||
1191 | case IW_AUTH_CIPHER_PAIRWISE: | ||
1192 | case IW_AUTH_CIPHER_GROUP: | ||
1193 | case IW_AUTH_RX_UNENCRYPTED_EAPOL: | ||
1194 | case IW_AUTH_PRIVACY_INVOKED: | ||
1195 | case IW_AUTH_DROP_UNENCRYPTED: | ||
1196 | /* | ||
1197 | * orinoco does not use these parameters | ||
1198 | */ | ||
1199 | break; | ||
1200 | |||
1201 | case IW_AUTH_KEY_MGMT: | ||
1202 | /* wl_lkm implies value 2 == PSK for Hermes I | ||
1203 | * which ties in with WEXT | ||
1204 | * no other hints tho :( | ||
1205 | */ | ||
1206 | priv->key_mgmt = param->value; | ||
1207 | break; | ||
1208 | |||
1209 | case IW_AUTH_TKIP_COUNTERMEASURES: | ||
1210 | /* When countermeasures are enabled, shut down the | ||
1211 | * card; when disabled, re-enable the card. This must | ||
1212 | * take effect immediately. | ||
1213 | * | ||
1214 | * TODO: Make sure that the EAPOL message is getting | ||
1215 | * out before card disabled | ||
1216 | */ | ||
1217 | if (param->value) { | ||
1218 | priv->tkip_cm_active = 1; | ||
1219 | ret = hermes_enable_port(hw, 0); | ||
1220 | } else { | ||
1221 | priv->tkip_cm_active = 0; | ||
1222 | ret = hermes_disable_port(hw, 0); | ||
1223 | } | ||
1224 | break; | ||
1225 | |||
1226 | case IW_AUTH_80211_AUTH_ALG: | ||
1227 | if (param->value & IW_AUTH_ALG_SHARED_KEY) | ||
1228 | priv->wep_restrict = 1; | ||
1229 | else if (param->value & IW_AUTH_ALG_OPEN_SYSTEM) | ||
1230 | priv->wep_restrict = 0; | ||
1231 | else | ||
1232 | ret = -EINVAL; | ||
1233 | break; | ||
1234 | |||
1235 | case IW_AUTH_WPA_ENABLED: | ||
1236 | if (priv->has_wpa) { | ||
1237 | priv->wpa_enabled = param->value ? 1 : 0; | ||
1238 | } else { | ||
1239 | if (param->value) | ||
1240 | ret = -EOPNOTSUPP; | ||
1241 | /* else silently accept disable of WPA */ | ||
1242 | priv->wpa_enabled = 0; | ||
1243 | } | ||
1244 | break; | ||
1245 | |||
1246 | default: | ||
1247 | ret = -EOPNOTSUPP; | ||
1248 | } | ||
1249 | |||
1250 | orinoco_unlock(priv, &flags); | ||
1251 | return ret; | ||
1252 | } | ||
1253 | |||
1254 | static int orinoco_ioctl_get_auth(struct net_device *dev, | ||
1255 | struct iw_request_info *info, | ||
1256 | union iwreq_data *wrqu, char *extra) | ||
1257 | { | ||
1258 | struct orinoco_private *priv = netdev_priv(dev); | ||
1259 | struct iw_param *param = &wrqu->param; | ||
1260 | unsigned long flags; | ||
1261 | int ret = 0; | ||
1262 | |||
1263 | if (orinoco_lock(priv, &flags) != 0) | ||
1264 | return -EBUSY; | ||
1265 | |||
1266 | switch (param->flags & IW_AUTH_INDEX) { | ||
1267 | case IW_AUTH_KEY_MGMT: | ||
1268 | param->value = priv->key_mgmt; | ||
1269 | break; | ||
1270 | |||
1271 | case IW_AUTH_TKIP_COUNTERMEASURES: | ||
1272 | param->value = priv->tkip_cm_active; | ||
1273 | break; | ||
1274 | |||
1275 | case IW_AUTH_80211_AUTH_ALG: | ||
1276 | if (priv->wep_restrict) | ||
1277 | param->value = IW_AUTH_ALG_SHARED_KEY; | ||
1278 | else | ||
1279 | param->value = IW_AUTH_ALG_OPEN_SYSTEM; | ||
1280 | break; | ||
1281 | |||
1282 | case IW_AUTH_WPA_ENABLED: | ||
1283 | param->value = priv->wpa_enabled; | ||
1284 | break; | ||
1285 | |||
1286 | default: | ||
1287 | ret = -EOPNOTSUPP; | ||
1288 | } | ||
1289 | |||
1290 | orinoco_unlock(priv, &flags); | ||
1291 | return ret; | ||
1292 | } | ||
1293 | |||
1294 | static int orinoco_ioctl_set_genie(struct net_device *dev, | ||
1295 | struct iw_request_info *info, | ||
1296 | union iwreq_data *wrqu, char *extra) | ||
1297 | { | ||
1298 | struct orinoco_private *priv = netdev_priv(dev); | ||
1299 | u8 *buf; | ||
1300 | unsigned long flags; | ||
1301 | |||
1302 | /* cut off at IEEE80211_MAX_DATA_LEN */ | ||
1303 | if ((wrqu->data.length > IEEE80211_MAX_DATA_LEN) || | ||
1304 | (wrqu->data.length && (extra == NULL))) | ||
1305 | return -EINVAL; | ||
1306 | |||
1307 | if (wrqu->data.length) { | ||
1308 | buf = kmalloc(wrqu->data.length, GFP_KERNEL); | ||
1309 | if (buf == NULL) | ||
1310 | return -ENOMEM; | ||
1311 | |||
1312 | memcpy(buf, extra, wrqu->data.length); | ||
1313 | } else | ||
1314 | buf = NULL; | ||
1315 | |||
1316 | if (orinoco_lock(priv, &flags) != 0) { | ||
1317 | kfree(buf); | ||
1318 | return -EBUSY; | ||
1319 | } | ||
1320 | |||
1321 | kfree(priv->wpa_ie); | ||
1322 | priv->wpa_ie = buf; | ||
1323 | priv->wpa_ie_len = wrqu->data.length; | ||
1324 | |||
1325 | if (priv->wpa_ie) { | ||
1326 | /* Looks like wl_lkm wants to check the auth alg, and | ||
1327 | * somehow pass it to the firmware. | ||
1328 | * Instead it just calls the key mgmt rid | ||
1329 | * - we do this in set auth. | ||
1330 | */ | ||
1331 | } | ||
1332 | |||
1333 | orinoco_unlock(priv, &flags); | ||
1334 | return 0; | ||
1335 | } | ||
1336 | |||
1337 | static int orinoco_ioctl_get_genie(struct net_device *dev, | ||
1338 | struct iw_request_info *info, | ||
1339 | union iwreq_data *wrqu, char *extra) | ||
1340 | { | ||
1341 | struct orinoco_private *priv = netdev_priv(dev); | ||
1342 | unsigned long flags; | ||
1343 | int err = 0; | ||
1344 | |||
1345 | if (orinoco_lock(priv, &flags) != 0) | ||
1346 | return -EBUSY; | ||
1347 | |||
1348 | if ((priv->wpa_ie_len == 0) || (priv->wpa_ie == NULL)) { | ||
1349 | wrqu->data.length = 0; | ||
1350 | goto out; | ||
1351 | } | ||
1352 | |||
1353 | if (wrqu->data.length < priv->wpa_ie_len) { | ||
1354 | err = -E2BIG; | ||
1355 | goto out; | ||
1356 | } | ||
1357 | |||
1358 | wrqu->data.length = priv->wpa_ie_len; | ||
1359 | memcpy(extra, priv->wpa_ie, priv->wpa_ie_len); | ||
1360 | |||
1361 | out: | ||
1362 | orinoco_unlock(priv, &flags); | ||
1363 | return err; | ||
1364 | } | ||
1365 | |||
1366 | static int orinoco_ioctl_set_mlme(struct net_device *dev, | ||
1367 | struct iw_request_info *info, | ||
1368 | union iwreq_data *wrqu, char *extra) | ||
1369 | { | ||
1370 | struct orinoco_private *priv = netdev_priv(dev); | ||
1371 | hermes_t *hw = &priv->hw; | ||
1372 | struct iw_mlme *mlme = (struct iw_mlme *)extra; | ||
1373 | unsigned long flags; | ||
1374 | int ret = 0; | ||
1375 | |||
1376 | if (orinoco_lock(priv, &flags) != 0) | ||
1377 | return -EBUSY; | ||
1378 | |||
1379 | switch (mlme->cmd) { | ||
1380 | case IW_MLME_DEAUTH: | ||
1381 | /* silently ignore */ | ||
1382 | break; | ||
1383 | |||
1384 | case IW_MLME_DISASSOC: | ||
1385 | { | ||
1386 | struct { | ||
1387 | u8 addr[ETH_ALEN]; | ||
1388 | __le16 reason_code; | ||
1389 | } __attribute__ ((packed)) buf; | ||
1390 | |||
1391 | memcpy(buf.addr, mlme->addr.sa_data, ETH_ALEN); | ||
1392 | buf.reason_code = cpu_to_le16(mlme->reason_code); | ||
1393 | ret = HERMES_WRITE_RECORD(hw, USER_BAP, | ||
1394 | HERMES_RID_CNFDISASSOCIATE, | ||
1395 | &buf); | ||
1396 | break; | ||
1397 | } | ||
1398 | default: | ||
1399 | ret = -EOPNOTSUPP; | ||
1400 | } | ||
1401 | |||
1402 | orinoco_unlock(priv, &flags); | ||
1403 | return ret; | ||
1404 | } | ||
1405 | |||
1406 | static int orinoco_ioctl_getretry(struct net_device *dev, | ||
1407 | struct iw_request_info *info, | ||
1408 | struct iw_param *rrq, | ||
1409 | char *extra) | ||
1410 | { | ||
1411 | struct orinoco_private *priv = netdev_priv(dev); | ||
1412 | hermes_t *hw = &priv->hw; | ||
1413 | int err = 0; | ||
1414 | u16 short_limit, long_limit, lifetime; | ||
1415 | unsigned long flags; | ||
1416 | |||
1417 | if (orinoco_lock(priv, &flags) != 0) | ||
1418 | return -EBUSY; | ||
1419 | |||
1420 | err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_SHORTRETRYLIMIT, | ||
1421 | &short_limit); | ||
1422 | if (err) | ||
1423 | goto out; | ||
1424 | |||
1425 | err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_LONGRETRYLIMIT, | ||
1426 | &long_limit); | ||
1427 | if (err) | ||
1428 | goto out; | ||
1429 | |||
1430 | err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_MAXTRANSMITLIFETIME, | ||
1431 | &lifetime); | ||
1432 | if (err) | ||
1433 | goto out; | ||
1434 | |||
1435 | rrq->disabled = 0; /* Can't be disabled */ | ||
1436 | |||
1437 | /* Note : by default, display the retry number */ | ||
1438 | if ((rrq->flags & IW_RETRY_TYPE) == IW_RETRY_LIFETIME) { | ||
1439 | rrq->flags = IW_RETRY_LIFETIME; | ||
1440 | rrq->value = lifetime * 1000; /* ??? */ | ||
1441 | } else { | ||
1442 | /* By default, display the min number */ | ||
1443 | if ((rrq->flags & IW_RETRY_LONG)) { | ||
1444 | rrq->flags = IW_RETRY_LIMIT | IW_RETRY_LONG; | ||
1445 | rrq->value = long_limit; | ||
1446 | } else { | ||
1447 | rrq->flags = IW_RETRY_LIMIT; | ||
1448 | rrq->value = short_limit; | ||
1449 | if (short_limit != long_limit) | ||
1450 | rrq->flags |= IW_RETRY_SHORT; | ||
1451 | } | ||
1452 | } | ||
1453 | |||
1454 | out: | ||
1455 | orinoco_unlock(priv, &flags); | ||
1456 | |||
1457 | return err; | ||
1458 | } | ||
1459 | |||
1460 | static int orinoco_ioctl_reset(struct net_device *dev, | ||
1461 | struct iw_request_info *info, | ||
1462 | void *wrqu, | ||
1463 | char *extra) | ||
1464 | { | ||
1465 | struct orinoco_private *priv = netdev_priv(dev); | ||
1466 | |||
1467 | if (!capable(CAP_NET_ADMIN)) | ||
1468 | return -EPERM; | ||
1469 | |||
1470 | if (info->cmd == (SIOCIWFIRSTPRIV + 0x1)) { | ||
1471 | printk(KERN_DEBUG "%s: Forcing reset!\n", dev->name); | ||
1472 | |||
1473 | /* Firmware reset */ | ||
1474 | orinoco_reset(&priv->reset_work); | ||
1475 | } else { | ||
1476 | printk(KERN_DEBUG "%s: Force scheduling reset!\n", dev->name); | ||
1477 | |||
1478 | schedule_work(&priv->reset_work); | ||
1479 | } | ||
1480 | |||
1481 | return 0; | ||
1482 | } | ||
1483 | |||
1484 | static int orinoco_ioctl_setibssport(struct net_device *dev, | ||
1485 | struct iw_request_info *info, | ||
1486 | void *wrqu, | ||
1487 | char *extra) | ||
1488 | |||
1489 | { | ||
1490 | struct orinoco_private *priv = netdev_priv(dev); | ||
1491 | int val = *((int *) extra); | ||
1492 | unsigned long flags; | ||
1493 | |||
1494 | if (orinoco_lock(priv, &flags) != 0) | ||
1495 | return -EBUSY; | ||
1496 | |||
1497 | priv->ibss_port = val ; | ||
1498 | |||
1499 | /* Actually update the mode we are using */ | ||
1500 | set_port_type(priv); | ||
1501 | |||
1502 | orinoco_unlock(priv, &flags); | ||
1503 | return -EINPROGRESS; /* Call commit handler */ | ||
1504 | } | ||
1505 | |||
1506 | static int orinoco_ioctl_getibssport(struct net_device *dev, | ||
1507 | struct iw_request_info *info, | ||
1508 | void *wrqu, | ||
1509 | char *extra) | ||
1510 | { | ||
1511 | struct orinoco_private *priv = netdev_priv(dev); | ||
1512 | int *val = (int *) extra; | ||
1513 | |||
1514 | *val = priv->ibss_port; | ||
1515 | return 0; | ||
1516 | } | ||
1517 | |||
1518 | static int orinoco_ioctl_setport3(struct net_device *dev, | ||
1519 | struct iw_request_info *info, | ||
1520 | void *wrqu, | ||
1521 | char *extra) | ||
1522 | { | ||
1523 | struct orinoco_private *priv = netdev_priv(dev); | ||
1524 | int val = *((int *) extra); | ||
1525 | int err = 0; | ||
1526 | unsigned long flags; | ||
1527 | |||
1528 | if (orinoco_lock(priv, &flags) != 0) | ||
1529 | return -EBUSY; | ||
1530 | |||
1531 | switch (val) { | ||
1532 | case 0: /* Try to do IEEE ad-hoc mode */ | ||
1533 | if (!priv->has_ibss) { | ||
1534 | err = -EINVAL; | ||
1535 | break; | ||
1536 | } | ||
1537 | priv->prefer_port3 = 0; | ||
1538 | |||
1539 | break; | ||
1540 | |||
1541 | case 1: /* Try to do Lucent proprietary ad-hoc mode */ | ||
1542 | if (!priv->has_port3) { | ||
1543 | err = -EINVAL; | ||
1544 | break; | ||
1545 | } | ||
1546 | priv->prefer_port3 = 1; | ||
1547 | break; | ||
1548 | |||
1549 | default: | ||
1550 | err = -EINVAL; | ||
1551 | } | ||
1552 | |||
1553 | if (!err) { | ||
1554 | /* Actually update the mode we are using */ | ||
1555 | set_port_type(priv); | ||
1556 | err = -EINPROGRESS; | ||
1557 | } | ||
1558 | |||
1559 | orinoco_unlock(priv, &flags); | ||
1560 | |||
1561 | return err; | ||
1562 | } | ||
1563 | |||
1564 | static int orinoco_ioctl_getport3(struct net_device *dev, | ||
1565 | struct iw_request_info *info, | ||
1566 | void *wrqu, | ||
1567 | char *extra) | ||
1568 | { | ||
1569 | struct orinoco_private *priv = netdev_priv(dev); | ||
1570 | int *val = (int *) extra; | ||
1571 | |||
1572 | *val = priv->prefer_port3; | ||
1573 | return 0; | ||
1574 | } | ||
1575 | |||
1576 | static int orinoco_ioctl_setpreamble(struct net_device *dev, | ||
1577 | struct iw_request_info *info, | ||
1578 | void *wrqu, | ||
1579 | char *extra) | ||
1580 | { | ||
1581 | struct orinoco_private *priv = netdev_priv(dev); | ||
1582 | unsigned long flags; | ||
1583 | int val; | ||
1584 | |||
1585 | if (!priv->has_preamble) | ||
1586 | return -EOPNOTSUPP; | ||
1587 | |||
1588 | /* 802.11b has recently defined some short preamble. | ||
1589 | * Basically, the Phy header has been reduced in size. | ||
1590 | * This increase performance, especially at high rates | ||
1591 | * (the preamble is transmitted at 1Mb/s), unfortunately | ||
1592 | * this give compatibility troubles... - Jean II */ | ||
1593 | val = *((int *) extra); | ||
1594 | |||
1595 | if (orinoco_lock(priv, &flags) != 0) | ||
1596 | return -EBUSY; | ||
1597 | |||
1598 | if (val) | ||
1599 | priv->preamble = 1; | ||
1600 | else | ||
1601 | priv->preamble = 0; | ||
1602 | |||
1603 | orinoco_unlock(priv, &flags); | ||
1604 | |||
1605 | return -EINPROGRESS; /* Call commit handler */ | ||
1606 | } | ||
1607 | |||
1608 | static int orinoco_ioctl_getpreamble(struct net_device *dev, | ||
1609 | struct iw_request_info *info, | ||
1610 | void *wrqu, | ||
1611 | char *extra) | ||
1612 | { | ||
1613 | struct orinoco_private *priv = netdev_priv(dev); | ||
1614 | int *val = (int *) extra; | ||
1615 | |||
1616 | if (!priv->has_preamble) | ||
1617 | return -EOPNOTSUPP; | ||
1618 | |||
1619 | *val = priv->preamble; | ||
1620 | return 0; | ||
1621 | } | ||
1622 | |||
1623 | /* ioctl interface to hermes_read_ltv() | ||
1624 | * To use with iwpriv, pass the RID as the token argument, e.g. | ||
1625 | * iwpriv get_rid [0xfc00] | ||
1626 | * At least Wireless Tools 25 is required to use iwpriv. | ||
1627 | * For Wireless Tools 25 and 26 append "dummy" are the end. */ | ||
1628 | static int orinoco_ioctl_getrid(struct net_device *dev, | ||
1629 | struct iw_request_info *info, | ||
1630 | struct iw_point *data, | ||
1631 | char *extra) | ||
1632 | { | ||
1633 | struct orinoco_private *priv = netdev_priv(dev); | ||
1634 | hermes_t *hw = &priv->hw; | ||
1635 | int rid = data->flags; | ||
1636 | u16 length; | ||
1637 | int err; | ||
1638 | unsigned long flags; | ||
1639 | |||
1640 | /* It's a "get" function, but we don't want users to access the | ||
1641 | * WEP key and other raw firmware data */ | ||
1642 | if (!capable(CAP_NET_ADMIN)) | ||
1643 | return -EPERM; | ||
1644 | |||
1645 | if (rid < 0xfc00 || rid > 0xffff) | ||
1646 | return -EINVAL; | ||
1647 | |||
1648 | if (orinoco_lock(priv, &flags) != 0) | ||
1649 | return -EBUSY; | ||
1650 | |||
1651 | err = hermes_read_ltv(hw, USER_BAP, rid, MAX_RID_LEN, &length, | ||
1652 | extra); | ||
1653 | if (err) | ||
1654 | goto out; | ||
1655 | |||
1656 | data->length = min_t(u16, HERMES_RECLEN_TO_BYTES(length), | ||
1657 | MAX_RID_LEN); | ||
1658 | |||
1659 | out: | ||
1660 | orinoco_unlock(priv, &flags); | ||
1661 | return err; | ||
1662 | } | ||
1663 | |||
1664 | /* Trigger a scan (look for other cells in the vicinity) */ | ||
1665 | static int orinoco_ioctl_setscan(struct net_device *dev, | ||
1666 | struct iw_request_info *info, | ||
1667 | struct iw_point *srq, | ||
1668 | char *extra) | ||
1669 | { | ||
1670 | struct orinoco_private *priv = netdev_priv(dev); | ||
1671 | hermes_t *hw = &priv->hw; | ||
1672 | struct iw_scan_req *si = (struct iw_scan_req *) extra; | ||
1673 | int err = 0; | ||
1674 | unsigned long flags; | ||
1675 | |||
1676 | /* Note : you may have realised that, as this is a SET operation, | ||
1677 | * this is privileged and therefore a normal user can't | ||
1678 | * perform scanning. | ||
1679 | * This is not an error, while the device perform scanning, | ||
1680 | * traffic doesn't flow, so it's a perfect DoS... | ||
1681 | * Jean II */ | ||
1682 | |||
1683 | if (orinoco_lock(priv, &flags) != 0) | ||
1684 | return -EBUSY; | ||
1685 | |||
1686 | /* Scanning with port 0 disabled would fail */ | ||
1687 | if (!netif_running(dev)) { | ||
1688 | err = -ENETDOWN; | ||
1689 | goto out; | ||
1690 | } | ||
1691 | |||
1692 | /* In monitor mode, the scan results are always empty. | ||
1693 | * Probe responses are passed to the driver as received | ||
1694 | * frames and could be processed in software. */ | ||
1695 | if (priv->iw_mode == IW_MODE_MONITOR) { | ||
1696 | err = -EOPNOTSUPP; | ||
1697 | goto out; | ||
1698 | } | ||
1699 | |||
1700 | /* Note : because we don't lock out the irq handler, the way | ||
1701 | * we access scan variables in priv is critical. | ||
1702 | * o scan_inprogress : not touched by irq handler | ||
1703 | * o scan_mode : not touched by irq handler | ||
1704 | * Before modifying anything on those variables, please think hard ! | ||
1705 | * Jean II */ | ||
1706 | |||
1707 | /* Save flags */ | ||
1708 | priv->scan_mode = srq->flags; | ||
1709 | |||
1710 | /* Always trigger scanning, even if it's in progress. | ||
1711 | * This way, if the info frame get lost, we will recover somewhat | ||
1712 | * gracefully - Jean II */ | ||
1713 | |||
1714 | if (priv->has_hostscan) { | ||
1715 | switch (priv->firmware_type) { | ||
1716 | case FIRMWARE_TYPE_SYMBOL: | ||
1717 | err = hermes_write_wordrec(hw, USER_BAP, | ||
1718 | HERMES_RID_CNFHOSTSCAN_SYMBOL, | ||
1719 | HERMES_HOSTSCAN_SYMBOL_ONCE | | ||
1720 | HERMES_HOSTSCAN_SYMBOL_BCAST); | ||
1721 | break; | ||
1722 | case FIRMWARE_TYPE_INTERSIL: { | ||
1723 | __le16 req[3]; | ||
1724 | |||
1725 | req[0] = cpu_to_le16(0x3fff); /* All channels */ | ||
1726 | req[1] = cpu_to_le16(0x0001); /* rate 1 Mbps */ | ||
1727 | req[2] = 0; /* Any ESSID */ | ||
1728 | err = HERMES_WRITE_RECORD(hw, USER_BAP, | ||
1729 | HERMES_RID_CNFHOSTSCAN, &req); | ||
1730 | } | ||
1731 | break; | ||
1732 | case FIRMWARE_TYPE_AGERE: | ||
1733 | if (priv->scan_mode & IW_SCAN_THIS_ESSID) { | ||
1734 | struct hermes_idstring idbuf; | ||
1735 | size_t len = min(sizeof(idbuf.val), | ||
1736 | (size_t) si->essid_len); | ||
1737 | idbuf.len = cpu_to_le16(len); | ||
1738 | memcpy(idbuf.val, si->essid, len); | ||
1739 | |||
1740 | err = hermes_write_ltv(hw, USER_BAP, | ||
1741 | HERMES_RID_CNFSCANSSID_AGERE, | ||
1742 | HERMES_BYTES_TO_RECLEN(len + 2), | ||
1743 | &idbuf); | ||
1744 | } else | ||
1745 | err = hermes_write_wordrec(hw, USER_BAP, | ||
1746 | HERMES_RID_CNFSCANSSID_AGERE, | ||
1747 | 0); /* Any ESSID */ | ||
1748 | if (err) | ||
1749 | break; | ||
1750 | |||
1751 | if (priv->has_ext_scan) { | ||
1752 | /* Clear scan results at the start of | ||
1753 | * an extended scan */ | ||
1754 | orinoco_clear_scan_results(priv, | ||
1755 | msecs_to_jiffies(15000)); | ||
1756 | |||
1757 | /* TODO: Is this available on older firmware? | ||
1758 | * Can we use it to scan specific channels | ||
1759 | * for IW_SCAN_THIS_FREQ? */ | ||
1760 | err = hermes_write_wordrec(hw, USER_BAP, | ||
1761 | HERMES_RID_CNFSCANCHANNELS2GHZ, | ||
1762 | 0x7FFF); | ||
1763 | if (err) | ||
1764 | goto out; | ||
1765 | |||
1766 | err = hermes_inquire(hw, | ||
1767 | HERMES_INQ_CHANNELINFO); | ||
1768 | } else | ||
1769 | err = hermes_inquire(hw, HERMES_INQ_SCAN); | ||
1770 | break; | ||
1771 | } | ||
1772 | } else | ||
1773 | err = hermes_inquire(hw, HERMES_INQ_SCAN); | ||
1774 | |||
1775 | /* One more client */ | ||
1776 | if (!err) | ||
1777 | priv->scan_inprogress = 1; | ||
1778 | |||
1779 | out: | ||
1780 | orinoco_unlock(priv, &flags); | ||
1781 | return err; | ||
1782 | } | ||
1783 | |||
1784 | #define MAX_CUSTOM_LEN 64 | ||
1785 | |||
1786 | /* Translate scan data returned from the card to a card independant | ||
1787 | * format that the Wireless Tools will understand - Jean II */ | ||
1788 | static inline char *orinoco_translate_scan(struct net_device *dev, | ||
1789 | struct iw_request_info *info, | ||
1790 | char *current_ev, | ||
1791 | char *end_buf, | ||
1792 | union hermes_scan_info *bss, | ||
1793 | unsigned long last_scanned) | ||
1794 | { | ||
1795 | struct orinoco_private *priv = netdev_priv(dev); | ||
1796 | u16 capabilities; | ||
1797 | u16 channel; | ||
1798 | struct iw_event iwe; /* Temporary buffer */ | ||
1799 | char custom[MAX_CUSTOM_LEN]; | ||
1800 | |||
1801 | memset(&iwe, 0, sizeof(iwe)); | ||
1802 | |||
1803 | /* First entry *MUST* be the AP MAC address */ | ||
1804 | iwe.cmd = SIOCGIWAP; | ||
1805 | iwe.u.ap_addr.sa_family = ARPHRD_ETHER; | ||
1806 | memcpy(iwe.u.ap_addr.sa_data, bss->a.bssid, ETH_ALEN); | ||
1807 | current_ev = iwe_stream_add_event(info, current_ev, end_buf, | ||
1808 | &iwe, IW_EV_ADDR_LEN); | ||
1809 | |||
1810 | /* Other entries will be displayed in the order we give them */ | ||
1811 | |||
1812 | /* Add the ESSID */ | ||
1813 | iwe.u.data.length = le16_to_cpu(bss->a.essid_len); | ||
1814 | if (iwe.u.data.length > 32) | ||
1815 | iwe.u.data.length = 32; | ||
1816 | iwe.cmd = SIOCGIWESSID; | ||
1817 | iwe.u.data.flags = 1; | ||
1818 | current_ev = iwe_stream_add_point(info, current_ev, end_buf, | ||
1819 | &iwe, bss->a.essid); | ||
1820 | |||
1821 | /* Add mode */ | ||
1822 | iwe.cmd = SIOCGIWMODE; | ||
1823 | capabilities = le16_to_cpu(bss->a.capabilities); | ||
1824 | if (capabilities & (WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_IBSS)) { | ||
1825 | if (capabilities & WLAN_CAPABILITY_ESS) | ||
1826 | iwe.u.mode = IW_MODE_MASTER; | ||
1827 | else | ||
1828 | iwe.u.mode = IW_MODE_ADHOC; | ||
1829 | current_ev = iwe_stream_add_event(info, current_ev, end_buf, | ||
1830 | &iwe, IW_EV_UINT_LEN); | ||
1831 | } | ||
1832 | |||
1833 | channel = bss->s.channel; | ||
1834 | if ((channel >= 1) && (channel <= NUM_CHANNELS)) { | ||
1835 | /* Add channel and frequency */ | ||
1836 | iwe.cmd = SIOCGIWFREQ; | ||
1837 | iwe.u.freq.m = channel; | ||
1838 | iwe.u.freq.e = 0; | ||
1839 | current_ev = iwe_stream_add_event(info, current_ev, end_buf, | ||
1840 | &iwe, IW_EV_FREQ_LEN); | ||
1841 | |||
1842 | iwe.u.freq.m = ieee80211_dsss_chan_to_freq(channel) * 100000; | ||
1843 | iwe.u.freq.e = 1; | ||
1844 | current_ev = iwe_stream_add_event(info, current_ev, end_buf, | ||
1845 | &iwe, IW_EV_FREQ_LEN); | ||
1846 | } | ||
1847 | |||
1848 | /* Add quality statistics. level and noise in dB. No link quality */ | ||
1849 | iwe.cmd = IWEVQUAL; | ||
1850 | iwe.u.qual.updated = IW_QUAL_DBM | IW_QUAL_QUAL_INVALID; | ||
1851 | iwe.u.qual.level = (__u8) le16_to_cpu(bss->a.level) - 0x95; | ||
1852 | iwe.u.qual.noise = (__u8) le16_to_cpu(bss->a.noise) - 0x95; | ||
1853 | /* Wireless tools prior to 27.pre22 will show link quality | ||
1854 | * anyway, so we provide a reasonable value. */ | ||
1855 | if (iwe.u.qual.level > iwe.u.qual.noise) | ||
1856 | iwe.u.qual.qual = iwe.u.qual.level - iwe.u.qual.noise; | ||
1857 | else | ||
1858 | iwe.u.qual.qual = 0; | ||
1859 | current_ev = iwe_stream_add_event(info, current_ev, end_buf, | ||
1860 | &iwe, IW_EV_QUAL_LEN); | ||
1861 | |||
1862 | /* Add encryption capability */ | ||
1863 | iwe.cmd = SIOCGIWENCODE; | ||
1864 | if (capabilities & WLAN_CAPABILITY_PRIVACY) | ||
1865 | iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY; | ||
1866 | else | ||
1867 | iwe.u.data.flags = IW_ENCODE_DISABLED; | ||
1868 | iwe.u.data.length = 0; | ||
1869 | current_ev = iwe_stream_add_point(info, current_ev, end_buf, | ||
1870 | &iwe, NULL); | ||
1871 | |||
1872 | /* Bit rate is not available in Lucent/Agere firmwares */ | ||
1873 | if (priv->firmware_type != FIRMWARE_TYPE_AGERE) { | ||
1874 | char *current_val = current_ev + iwe_stream_lcp_len(info); | ||
1875 | int i; | ||
1876 | int step; | ||
1877 | |||
1878 | if (priv->firmware_type == FIRMWARE_TYPE_SYMBOL) | ||
1879 | step = 2; | ||
1880 | else | ||
1881 | step = 1; | ||
1882 | |||
1883 | iwe.cmd = SIOCGIWRATE; | ||
1884 | /* Those two flags are ignored... */ | ||
1885 | iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0; | ||
1886 | /* Max 10 values */ | ||
1887 | for (i = 0; i < 10; i += step) { | ||
1888 | /* NULL terminated */ | ||
1889 | if (bss->p.rates[i] == 0x0) | ||
1890 | break; | ||
1891 | /* Bit rate given in 500 kb/s units (+ 0x80) */ | ||
1892 | iwe.u.bitrate.value = | ||
1893 | ((bss->p.rates[i] & 0x7f) * 500000); | ||
1894 | current_val = iwe_stream_add_value(info, current_ev, | ||
1895 | current_val, | ||
1896 | end_buf, &iwe, | ||
1897 | IW_EV_PARAM_LEN); | ||
1898 | } | ||
1899 | /* Check if we added any event */ | ||
1900 | if ((current_val - current_ev) > iwe_stream_lcp_len(info)) | ||
1901 | current_ev = current_val; | ||
1902 | } | ||
1903 | |||
1904 | /* Beacon interval */ | ||
1905 | iwe.cmd = IWEVCUSTOM; | ||
1906 | iwe.u.data.length = snprintf(custom, MAX_CUSTOM_LEN, | ||
1907 | "bcn_int=%d", | ||
1908 | le16_to_cpu(bss->a.beacon_interv)); | ||
1909 | if (iwe.u.data.length) | ||
1910 | current_ev = iwe_stream_add_point(info, current_ev, end_buf, | ||
1911 | &iwe, custom); | ||
1912 | |||
1913 | /* Capabilites */ | ||
1914 | iwe.cmd = IWEVCUSTOM; | ||
1915 | iwe.u.data.length = snprintf(custom, MAX_CUSTOM_LEN, | ||
1916 | "capab=0x%04x", | ||
1917 | capabilities); | ||
1918 | if (iwe.u.data.length) | ||
1919 | current_ev = iwe_stream_add_point(info, current_ev, end_buf, | ||
1920 | &iwe, custom); | ||
1921 | |||
1922 | /* Add EXTRA: Age to display seconds since last beacon/probe response | ||
1923 | * for given network. */ | ||
1924 | iwe.cmd = IWEVCUSTOM; | ||
1925 | iwe.u.data.length = snprintf(custom, MAX_CUSTOM_LEN, | ||
1926 | " Last beacon: %dms ago", | ||
1927 | jiffies_to_msecs(jiffies - last_scanned)); | ||
1928 | if (iwe.u.data.length) | ||
1929 | current_ev = iwe_stream_add_point(info, current_ev, end_buf, | ||
1930 | &iwe, custom); | ||
1931 | |||
1932 | return current_ev; | ||
1933 | } | ||
1934 | |||
1935 | static inline char *orinoco_translate_ext_scan(struct net_device *dev, | ||
1936 | struct iw_request_info *info, | ||
1937 | char *current_ev, | ||
1938 | char *end_buf, | ||
1939 | struct agere_ext_scan_info *bss, | ||
1940 | unsigned long last_scanned) | ||
1941 | { | ||
1942 | u16 capabilities; | ||
1943 | u16 channel; | ||
1944 | struct iw_event iwe; /* Temporary buffer */ | ||
1945 | char custom[MAX_CUSTOM_LEN]; | ||
1946 | u8 *ie; | ||
1947 | |||
1948 | memset(&iwe, 0, sizeof(iwe)); | ||
1949 | |||
1950 | /* First entry *MUST* be the AP MAC address */ | ||
1951 | iwe.cmd = SIOCGIWAP; | ||
1952 | iwe.u.ap_addr.sa_family = ARPHRD_ETHER; | ||
1953 | memcpy(iwe.u.ap_addr.sa_data, bss->bssid, ETH_ALEN); | ||
1954 | current_ev = iwe_stream_add_event(info, current_ev, end_buf, | ||
1955 | &iwe, IW_EV_ADDR_LEN); | ||
1956 | |||
1957 | /* Other entries will be displayed in the order we give them */ | ||
1958 | |||
1959 | /* Add the ESSID */ | ||
1960 | ie = bss->data; | ||
1961 | iwe.u.data.length = ie[1]; | ||
1962 | if (iwe.u.data.length) { | ||
1963 | if (iwe.u.data.length > 32) | ||
1964 | iwe.u.data.length = 32; | ||
1965 | iwe.cmd = SIOCGIWESSID; | ||
1966 | iwe.u.data.flags = 1; | ||
1967 | current_ev = iwe_stream_add_point(info, current_ev, end_buf, | ||
1968 | &iwe, &ie[2]); | ||
1969 | } | ||
1970 | |||
1971 | /* Add mode */ | ||
1972 | capabilities = le16_to_cpu(bss->capabilities); | ||
1973 | if (capabilities & (WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_IBSS)) { | ||
1974 | iwe.cmd = SIOCGIWMODE; | ||
1975 | if (capabilities & WLAN_CAPABILITY_ESS) | ||
1976 | iwe.u.mode = IW_MODE_MASTER; | ||
1977 | else | ||
1978 | iwe.u.mode = IW_MODE_ADHOC; | ||
1979 | current_ev = iwe_stream_add_event(info, current_ev, end_buf, | ||
1980 | &iwe, IW_EV_UINT_LEN); | ||
1981 | } | ||
1982 | |||
1983 | ie = orinoco_get_ie(bss->data, sizeof(bss->data), WLAN_EID_DS_PARAMS); | ||
1984 | channel = ie ? ie[2] : 0; | ||
1985 | if ((channel >= 1) && (channel <= NUM_CHANNELS)) { | ||
1986 | /* Add channel and frequency */ | ||
1987 | iwe.cmd = SIOCGIWFREQ; | ||
1988 | iwe.u.freq.m = channel; | ||
1989 | iwe.u.freq.e = 0; | ||
1990 | current_ev = iwe_stream_add_event(info, current_ev, end_buf, | ||
1991 | &iwe, IW_EV_FREQ_LEN); | ||
1992 | |||
1993 | iwe.u.freq.m = ieee80211_dsss_chan_to_freq(channel) * 100000; | ||
1994 | iwe.u.freq.e = 1; | ||
1995 | current_ev = iwe_stream_add_event(info, current_ev, end_buf, | ||
1996 | &iwe, IW_EV_FREQ_LEN); | ||
1997 | } | ||
1998 | |||
1999 | /* Add quality statistics. level and noise in dB. No link quality */ | ||
2000 | iwe.cmd = IWEVQUAL; | ||
2001 | iwe.u.qual.updated = IW_QUAL_DBM | IW_QUAL_QUAL_INVALID; | ||
2002 | iwe.u.qual.level = bss->level - 0x95; | ||
2003 | iwe.u.qual.noise = bss->noise - 0x95; | ||
2004 | /* Wireless tools prior to 27.pre22 will show link quality | ||
2005 | * anyway, so we provide a reasonable value. */ | ||
2006 | if (iwe.u.qual.level > iwe.u.qual.noise) | ||
2007 | iwe.u.qual.qual = iwe.u.qual.level - iwe.u.qual.noise; | ||
2008 | else | ||
2009 | iwe.u.qual.qual = 0; | ||
2010 | current_ev = iwe_stream_add_event(info, current_ev, end_buf, | ||
2011 | &iwe, IW_EV_QUAL_LEN); | ||
2012 | |||
2013 | /* Add encryption capability */ | ||
2014 | iwe.cmd = SIOCGIWENCODE; | ||
2015 | if (capabilities & WLAN_CAPABILITY_PRIVACY) | ||
2016 | iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY; | ||
2017 | else | ||
2018 | iwe.u.data.flags = IW_ENCODE_DISABLED; | ||
2019 | iwe.u.data.length = 0; | ||
2020 | current_ev = iwe_stream_add_point(info, current_ev, end_buf, | ||
2021 | &iwe, NULL); | ||
2022 | |||
2023 | /* WPA IE */ | ||
2024 | ie = orinoco_get_wpa_ie(bss->data, sizeof(bss->data)); | ||
2025 | if (ie) { | ||
2026 | iwe.cmd = IWEVGENIE; | ||
2027 | iwe.u.data.length = ie[1] + 2; | ||
2028 | current_ev = iwe_stream_add_point(info, current_ev, end_buf, | ||
2029 | &iwe, ie); | ||
2030 | } | ||
2031 | |||
2032 | /* RSN IE */ | ||
2033 | ie = orinoco_get_ie(bss->data, sizeof(bss->data), WLAN_EID_RSN); | ||
2034 | if (ie) { | ||
2035 | iwe.cmd = IWEVGENIE; | ||
2036 | iwe.u.data.length = ie[1] + 2; | ||
2037 | current_ev = iwe_stream_add_point(info, current_ev, end_buf, | ||
2038 | &iwe, ie); | ||
2039 | } | ||
2040 | |||
2041 | ie = orinoco_get_ie(bss->data, sizeof(bss->data), WLAN_EID_SUPP_RATES); | ||
2042 | if (ie) { | ||
2043 | char *p = current_ev + iwe_stream_lcp_len(info); | ||
2044 | int i; | ||
2045 | |||
2046 | iwe.cmd = SIOCGIWRATE; | ||
2047 | /* Those two flags are ignored... */ | ||
2048 | iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0; | ||
2049 | |||
2050 | for (i = 2; i < (ie[1] + 2); i++) { | ||
2051 | iwe.u.bitrate.value = ((ie[i] & 0x7F) * 500000); | ||
2052 | p = iwe_stream_add_value(info, current_ev, p, end_buf, | ||
2053 | &iwe, IW_EV_PARAM_LEN); | ||
2054 | } | ||
2055 | /* Check if we added any event */ | ||
2056 | if (p > (current_ev + iwe_stream_lcp_len(info))) | ||
2057 | current_ev = p; | ||
2058 | } | ||
2059 | |||
2060 | /* Timestamp */ | ||
2061 | iwe.cmd = IWEVCUSTOM; | ||
2062 | iwe.u.data.length = | ||
2063 | snprintf(custom, MAX_CUSTOM_LEN, "tsf=%016llx", | ||
2064 | (unsigned long long) le64_to_cpu(bss->timestamp)); | ||
2065 | if (iwe.u.data.length) | ||
2066 | current_ev = iwe_stream_add_point(info, current_ev, end_buf, | ||
2067 | &iwe, custom); | ||
2068 | |||
2069 | /* Beacon interval */ | ||
2070 | iwe.cmd = IWEVCUSTOM; | ||
2071 | iwe.u.data.length = snprintf(custom, MAX_CUSTOM_LEN, | ||
2072 | "bcn_int=%d", | ||
2073 | le16_to_cpu(bss->beacon_interval)); | ||
2074 | if (iwe.u.data.length) | ||
2075 | current_ev = iwe_stream_add_point(info, current_ev, end_buf, | ||
2076 | &iwe, custom); | ||
2077 | |||
2078 | /* Capabilites */ | ||
2079 | iwe.cmd = IWEVCUSTOM; | ||
2080 | iwe.u.data.length = snprintf(custom, MAX_CUSTOM_LEN, | ||
2081 | "capab=0x%04x", | ||
2082 | capabilities); | ||
2083 | if (iwe.u.data.length) | ||
2084 | current_ev = iwe_stream_add_point(info, current_ev, end_buf, | ||
2085 | &iwe, custom); | ||
2086 | |||
2087 | /* Add EXTRA: Age to display seconds since last beacon/probe response | ||
2088 | * for given network. */ | ||
2089 | iwe.cmd = IWEVCUSTOM; | ||
2090 | iwe.u.data.length = snprintf(custom, MAX_CUSTOM_LEN, | ||
2091 | " Last beacon: %dms ago", | ||
2092 | jiffies_to_msecs(jiffies - last_scanned)); | ||
2093 | if (iwe.u.data.length) | ||
2094 | current_ev = iwe_stream_add_point(info, current_ev, end_buf, | ||
2095 | &iwe, custom); | ||
2096 | |||
2097 | return current_ev; | ||
2098 | } | ||
2099 | |||
2100 | /* Return results of a scan */ | ||
2101 | static int orinoco_ioctl_getscan(struct net_device *dev, | ||
2102 | struct iw_request_info *info, | ||
2103 | struct iw_point *srq, | ||
2104 | char *extra) | ||
2105 | { | ||
2106 | struct orinoco_private *priv = netdev_priv(dev); | ||
2107 | int err = 0; | ||
2108 | unsigned long flags; | ||
2109 | char *current_ev = extra; | ||
2110 | |||
2111 | if (orinoco_lock(priv, &flags) != 0) | ||
2112 | return -EBUSY; | ||
2113 | |||
2114 | if (priv->scan_inprogress) { | ||
2115 | /* Important note : we don't want to block the caller | ||
2116 | * until results are ready for various reasons. | ||
2117 | * First, managing wait queues is complex and racy. | ||
2118 | * Second, we grab some rtnetlink lock before comming | ||
2119 | * here (in dev_ioctl()). | ||
2120 | * Third, we generate an Wireless Event, so the | ||
2121 | * caller can wait itself on that - Jean II */ | ||
2122 | err = -EAGAIN; | ||
2123 | goto out; | ||
2124 | } | ||
2125 | |||
2126 | if (priv->has_ext_scan) { | ||
2127 | struct xbss_element *bss; | ||
2128 | |||
2129 | list_for_each_entry(bss, &priv->bss_list, list) { | ||
2130 | /* Translate this entry to WE format */ | ||
2131 | current_ev = | ||
2132 | orinoco_translate_ext_scan(dev, info, | ||
2133 | current_ev, | ||
2134 | extra + srq->length, | ||
2135 | &bss->bss, | ||
2136 | bss->last_scanned); | ||
2137 | |||
2138 | /* Check if there is space for one more entry */ | ||
2139 | if ((extra + srq->length - current_ev) | ||
2140 | <= IW_EV_ADDR_LEN) { | ||
2141 | /* Ask user space to try again with a | ||
2142 | * bigger buffer */ | ||
2143 | err = -E2BIG; | ||
2144 | goto out; | ||
2145 | } | ||
2146 | } | ||
2147 | |||
2148 | } else { | ||
2149 | struct bss_element *bss; | ||
2150 | |||
2151 | list_for_each_entry(bss, &priv->bss_list, list) { | ||
2152 | /* Translate this entry to WE format */ | ||
2153 | current_ev = orinoco_translate_scan(dev, info, | ||
2154 | current_ev, | ||
2155 | extra + srq->length, | ||
2156 | &bss->bss, | ||
2157 | bss->last_scanned); | ||
2158 | |||
2159 | /* Check if there is space for one more entry */ | ||
2160 | if ((extra + srq->length - current_ev) | ||
2161 | <= IW_EV_ADDR_LEN) { | ||
2162 | /* Ask user space to try again with a | ||
2163 | * bigger buffer */ | ||
2164 | err = -E2BIG; | ||
2165 | goto out; | ||
2166 | } | ||
2167 | } | ||
2168 | } | ||
2169 | |||
2170 | srq->length = (current_ev - extra); | ||
2171 | srq->flags = (__u16) priv->scan_mode; | ||
2172 | |||
2173 | out: | ||
2174 | orinoco_unlock(priv, &flags); | ||
2175 | return err; | ||
2176 | } | ||
2177 | |||
2178 | /* Commit handler, called after set operations */ | ||
2179 | static int orinoco_ioctl_commit(struct net_device *dev, | ||
2180 | struct iw_request_info *info, | ||
2181 | void *wrqu, | ||
2182 | char *extra) | ||
2183 | { | ||
2184 | struct orinoco_private *priv = netdev_priv(dev); | ||
2185 | struct hermes *hw = &priv->hw; | ||
2186 | unsigned long flags; | ||
2187 | int err = 0; | ||
2188 | |||
2189 | if (!priv->open) | ||
2190 | return 0; | ||
2191 | |||
2192 | if (priv->broken_disableport) { | ||
2193 | orinoco_reset(&priv->reset_work); | ||
2194 | return 0; | ||
2195 | } | ||
2196 | |||
2197 | if (orinoco_lock(priv, &flags) != 0) | ||
2198 | return err; | ||
2199 | |||
2200 | err = hermes_disable_port(hw, 0); | ||
2201 | if (err) { | ||
2202 | printk(KERN_WARNING "%s: Unable to disable port " | ||
2203 | "while reconfiguring card\n", dev->name); | ||
2204 | priv->broken_disableport = 1; | ||
2205 | goto out; | ||
2206 | } | ||
2207 | |||
2208 | err = __orinoco_program_rids(dev); | ||
2209 | if (err) { | ||
2210 | printk(KERN_WARNING "%s: Unable to reconfigure card\n", | ||
2211 | dev->name); | ||
2212 | goto out; | ||
2213 | } | ||
2214 | |||
2215 | err = hermes_enable_port(hw, 0); | ||
2216 | if (err) { | ||
2217 | printk(KERN_WARNING "%s: Unable to enable port while reconfiguring card\n", | ||
2218 | dev->name); | ||
2219 | goto out; | ||
2220 | } | ||
2221 | |||
2222 | out: | ||
2223 | if (err) { | ||
2224 | printk(KERN_WARNING "%s: Resetting instead...\n", dev->name); | ||
2225 | schedule_work(&priv->reset_work); | ||
2226 | err = 0; | ||
2227 | } | ||
2228 | |||
2229 | orinoco_unlock(priv, &flags); | ||
2230 | return err; | ||
2231 | } | ||
2232 | |||
2233 | static const struct iw_priv_args orinoco_privtab[] = { | ||
2234 | { SIOCIWFIRSTPRIV + 0x0, 0, 0, "force_reset" }, | ||
2235 | { SIOCIWFIRSTPRIV + 0x1, 0, 0, "card_reset" }, | ||
2236 | { SIOCIWFIRSTPRIV + 0x2, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, | ||
2237 | 0, "set_port3" }, | ||
2238 | { SIOCIWFIRSTPRIV + 0x3, 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, | ||
2239 | "get_port3" }, | ||
2240 | { SIOCIWFIRSTPRIV + 0x4, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, | ||
2241 | 0, "set_preamble" }, | ||
2242 | { SIOCIWFIRSTPRIV + 0x5, 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, | ||
2243 | "get_preamble" }, | ||
2244 | { SIOCIWFIRSTPRIV + 0x6, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, | ||
2245 | 0, "set_ibssport" }, | ||
2246 | { SIOCIWFIRSTPRIV + 0x7, 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, | ||
2247 | "get_ibssport" }, | ||
2248 | { SIOCIWFIRSTPRIV + 0x9, 0, IW_PRIV_TYPE_BYTE | MAX_RID_LEN, | ||
2249 | "get_rid" }, | ||
2250 | }; | ||
2251 | |||
2252 | |||
2253 | /* | ||
2254 | * Structures to export the Wireless Handlers | ||
2255 | */ | ||
2256 | |||
2257 | #define STD_IW_HANDLER(id, func) \ | ||
2258 | [IW_IOCTL_IDX(id)] = (iw_handler) func | ||
2259 | static const iw_handler orinoco_handler[] = { | ||
2260 | STD_IW_HANDLER(SIOCSIWCOMMIT, orinoco_ioctl_commit), | ||
2261 | STD_IW_HANDLER(SIOCGIWNAME, orinoco_ioctl_getname), | ||
2262 | STD_IW_HANDLER(SIOCSIWFREQ, orinoco_ioctl_setfreq), | ||
2263 | STD_IW_HANDLER(SIOCGIWFREQ, orinoco_ioctl_getfreq), | ||
2264 | STD_IW_HANDLER(SIOCSIWMODE, orinoco_ioctl_setmode), | ||
2265 | STD_IW_HANDLER(SIOCGIWMODE, orinoco_ioctl_getmode), | ||
2266 | STD_IW_HANDLER(SIOCSIWSENS, orinoco_ioctl_setsens), | ||
2267 | STD_IW_HANDLER(SIOCGIWSENS, orinoco_ioctl_getsens), | ||
2268 | STD_IW_HANDLER(SIOCGIWRANGE, orinoco_ioctl_getiwrange), | ||
2269 | STD_IW_HANDLER(SIOCSIWSPY, iw_handler_set_spy), | ||
2270 | STD_IW_HANDLER(SIOCGIWSPY, iw_handler_get_spy), | ||
2271 | STD_IW_HANDLER(SIOCSIWTHRSPY, iw_handler_set_thrspy), | ||
2272 | STD_IW_HANDLER(SIOCGIWTHRSPY, iw_handler_get_thrspy), | ||
2273 | STD_IW_HANDLER(SIOCSIWAP, orinoco_ioctl_setwap), | ||
2274 | STD_IW_HANDLER(SIOCGIWAP, orinoco_ioctl_getwap), | ||
2275 | STD_IW_HANDLER(SIOCSIWSCAN, orinoco_ioctl_setscan), | ||
2276 | STD_IW_HANDLER(SIOCGIWSCAN, orinoco_ioctl_getscan), | ||
2277 | STD_IW_HANDLER(SIOCSIWESSID, orinoco_ioctl_setessid), | ||
2278 | STD_IW_HANDLER(SIOCGIWESSID, orinoco_ioctl_getessid), | ||
2279 | STD_IW_HANDLER(SIOCSIWNICKN, orinoco_ioctl_setnick), | ||
2280 | STD_IW_HANDLER(SIOCGIWNICKN, orinoco_ioctl_getnick), | ||
2281 | STD_IW_HANDLER(SIOCSIWRATE, orinoco_ioctl_setrate), | ||
2282 | STD_IW_HANDLER(SIOCGIWRATE, orinoco_ioctl_getrate), | ||
2283 | STD_IW_HANDLER(SIOCSIWRTS, orinoco_ioctl_setrts), | ||
2284 | STD_IW_HANDLER(SIOCGIWRTS, orinoco_ioctl_getrts), | ||
2285 | STD_IW_HANDLER(SIOCSIWFRAG, orinoco_ioctl_setfrag), | ||
2286 | STD_IW_HANDLER(SIOCGIWFRAG, orinoco_ioctl_getfrag), | ||
2287 | STD_IW_HANDLER(SIOCGIWRETRY, orinoco_ioctl_getretry), | ||
2288 | STD_IW_HANDLER(SIOCSIWENCODE, orinoco_ioctl_setiwencode), | ||
2289 | STD_IW_HANDLER(SIOCGIWENCODE, orinoco_ioctl_getiwencode), | ||
2290 | STD_IW_HANDLER(SIOCSIWPOWER, orinoco_ioctl_setpower), | ||
2291 | STD_IW_HANDLER(SIOCGIWPOWER, orinoco_ioctl_getpower), | ||
2292 | STD_IW_HANDLER(SIOCSIWGENIE, orinoco_ioctl_set_genie), | ||
2293 | STD_IW_HANDLER(SIOCGIWGENIE, orinoco_ioctl_get_genie), | ||
2294 | STD_IW_HANDLER(SIOCSIWMLME, orinoco_ioctl_set_mlme), | ||
2295 | STD_IW_HANDLER(SIOCSIWAUTH, orinoco_ioctl_set_auth), | ||
2296 | STD_IW_HANDLER(SIOCGIWAUTH, orinoco_ioctl_get_auth), | ||
2297 | STD_IW_HANDLER(SIOCSIWENCODEEXT, orinoco_ioctl_set_encodeext), | ||
2298 | STD_IW_HANDLER(SIOCGIWENCODEEXT, orinoco_ioctl_get_encodeext), | ||
2299 | }; | ||
2300 | |||
2301 | |||
2302 | /* | ||
2303 | Added typecasting since we no longer use iwreq_data -- Moustafa | ||
2304 | */ | ||
2305 | static const iw_handler orinoco_private_handler[] = { | ||
2306 | [0] = (iw_handler) orinoco_ioctl_reset, | ||
2307 | [1] = (iw_handler) orinoco_ioctl_reset, | ||
2308 | [2] = (iw_handler) orinoco_ioctl_setport3, | ||
2309 | [3] = (iw_handler) orinoco_ioctl_getport3, | ||
2310 | [4] = (iw_handler) orinoco_ioctl_setpreamble, | ||
2311 | [5] = (iw_handler) orinoco_ioctl_getpreamble, | ||
2312 | [6] = (iw_handler) orinoco_ioctl_setibssport, | ||
2313 | [7] = (iw_handler) orinoco_ioctl_getibssport, | ||
2314 | [9] = (iw_handler) orinoco_ioctl_getrid, | ||
2315 | }; | ||
2316 | |||
2317 | const struct iw_handler_def orinoco_handler_def = { | ||
2318 | .num_standard = ARRAY_SIZE(orinoco_handler), | ||
2319 | .num_private = ARRAY_SIZE(orinoco_private_handler), | ||
2320 | .num_private_args = ARRAY_SIZE(orinoco_privtab), | ||
2321 | .standard = orinoco_handler, | ||
2322 | .private = orinoco_private_handler, | ||
2323 | .private_args = orinoco_privtab, | ||
2324 | .get_wireless_stats = orinoco_get_wireless_stats, | ||
2325 | }; | ||
diff --git a/drivers/net/wireless/orinoco/wext.h b/drivers/net/wireless/orinoco/wext.h new file mode 100644 index 000000000000..1479f4e26dde --- /dev/null +++ b/drivers/net/wireless/orinoco/wext.h | |||
@@ -0,0 +1,13 @@ | |||
1 | /* Wireless extensions support. | ||
2 | * | ||
3 | * See copyright notice in main.c | ||
4 | */ | ||
5 | #ifndef _ORINOCO_WEXT_H_ | ||
6 | #define _ORINOCO_WEXT_H_ | ||
7 | |||
8 | #include <net/iw_handler.h> | ||
9 | |||
10 | /* Structure defining all our WEXT handlers */ | ||
11 | extern const struct iw_handler_def orinoco_handler_def; | ||
12 | |||
13 | #endif /* _ORINOCO_WEXT_H_ */ | ||