📄 wireless.c
字号:
/* * This file implement the Wireless Extensions APIs. * * Authors : Jean Tourrilhes - HPL - <jt@hpl.hp.com> * Copyright (c) 1997-2003 Jean Tourrilhes, All Rights Reserved. * * (As all part of the Linux kernel, this file is GPL) *//************************** DOCUMENTATION **************************//* * API definition : * -------------- * See <linux/wireless.h> for details of the APIs and the rest. * * History : * ------- * * v1 - 5.12.01 - Jean II * o Created this file. * * v2 - 13.12.01 - Jean II * o Move /proc/net/wireless stuff from net/core/dev.c to here * o Make Wireless Extension IOCTLs go through here * o Added iw_handler handling ;-) * o Added standard ioctl description * o Initial dumb commit strategy based on orinoco.c * * v3 - 19.12.01 - Jean II * o Make sure we don't go out of standard_ioctl[] in ioctl_standard_call * o Add event dispatcher function * o Add event description * o Propagate events as rtnetlink IFLA_WIRELESS option * o Generate event on selected SET requests * * v4 - 18.04.02 - Jean II * o Fix stupid off by one in iw_ioctl_description : IW_ESSID_MAX_SIZE + 1 * * v5 - 21.06.02 - Jean II * o Add IW_PRIV_TYPE_ADDR in priv_type_size (+cleanup) * o Reshuffle IW_HEADER_TYPE_XXX to map IW_PRIV_TYPE_XXX changes * o Add IWEVCUSTOM for driver specific event/scanning token * o Turn on WE_STRICT_WRITE by default + kernel warning * o Fix WE_STRICT_WRITE in ioctl_export_private() (32 => iw_num) * o Fix off-by-one in test (extra_size <= IFNAMSIZ) * * v6 - 9.01.03 - Jean II * o Add common spy support : iw_handler_set_spy(), wireless_spy_update() * o Add enhanced spy support : iw_handler_set_thrspy() and event. * o Add WIRELESS_EXT version display in /proc/net/wireless *//***************************** INCLUDES *****************************/#include <linux/config.h> /* Not needed ??? */#include <linux/module.h>#include <linux/types.h> /* off_t */#include <linux/netdevice.h> /* struct ifreq, dev_get_by_name() */#include <linux/proc_fs.h>#include <linux/rtnetlink.h> /* rtnetlink stuff */#include <linux/seq_file.h>#include <linux/init.h> /* for __init */#include <linux/if_arp.h> /* ARPHRD_ETHER */#include <linux/wireless.h> /* Pretty obvious */#include <net/iw_handler.h> /* New driver API */#include <asm/uaccess.h> /* copy_to_user() *//**************************** CONSTANTS ****************************//* Enough lenience, let's make sure things are proper... */#define WE_STRICT_WRITE /* Check write buffer size *//* I'll probably drop both the define and kernel message in the next version *//* Debugging stuff */#undef WE_IOCTL_DEBUG /* Debug IOCTL API */#undef WE_EVENT_DEBUG /* Debug Event dispatcher */#undef WE_SPY_DEBUG /* Debug enhanced spy support *//* Options */#define WE_EVENT_NETLINK /* Propagate events using rtnetlink */#define WE_SET_EVENT /* Generate an event on some set commands *//************************* GLOBAL VARIABLES *************************//* * You should not use global variables, because of re-entrancy. * On our case, it's only const, so it's OK... *//* * Meta-data about all the standard Wireless Extension request we * know about. */static const struct iw_ioctl_description standard_ioctl[] = { [SIOCSIWCOMMIT - SIOCIWFIRST] = { .header_type = IW_HEADER_TYPE_NULL, }, [SIOCGIWNAME - SIOCIWFIRST] = { .header_type = IW_HEADER_TYPE_CHAR, .flags = IW_DESCR_FLAG_DUMP, }, [SIOCSIWNWID - SIOCIWFIRST] = { .header_type = IW_HEADER_TYPE_PARAM, .flags = IW_DESCR_FLAG_EVENT, }, [SIOCGIWNWID - SIOCIWFIRST] = { .header_type = IW_HEADER_TYPE_PARAM, .flags = IW_DESCR_FLAG_DUMP, }, [SIOCSIWFREQ - SIOCIWFIRST] = { .header_type = IW_HEADER_TYPE_FREQ, .flags = IW_DESCR_FLAG_EVENT, }, [SIOCGIWFREQ - SIOCIWFIRST] = { .header_type = IW_HEADER_TYPE_FREQ, .flags = IW_DESCR_FLAG_DUMP, }, [SIOCSIWMODE - SIOCIWFIRST] = { .header_type = IW_HEADER_TYPE_UINT, .flags = IW_DESCR_FLAG_EVENT, }, [SIOCGIWMODE - SIOCIWFIRST] = { .header_type = IW_HEADER_TYPE_UINT, .flags = IW_DESCR_FLAG_DUMP, }, [SIOCSIWSENS - SIOCIWFIRST] = { .header_type = IW_HEADER_TYPE_PARAM, }, [SIOCGIWSENS - SIOCIWFIRST] = { .header_type = IW_HEADER_TYPE_PARAM, }, [SIOCSIWRANGE - SIOCIWFIRST] = { .header_type = IW_HEADER_TYPE_NULL, }, [SIOCGIWRANGE - SIOCIWFIRST] = { .header_type = IW_HEADER_TYPE_POINT, .token_size = 1, .max_tokens = sizeof(struct iw_range), .flags = IW_DESCR_FLAG_DUMP, }, [SIOCSIWPRIV - SIOCIWFIRST] = { .header_type = IW_HEADER_TYPE_NULL, }, [SIOCGIWPRIV - SIOCIWFIRST] = { /* (handled directly by us) */ .header_type = IW_HEADER_TYPE_NULL, }, [SIOCSIWSTATS - SIOCIWFIRST] = { .header_type = IW_HEADER_TYPE_NULL, }, [SIOCGIWSTATS - SIOCIWFIRST] = { /* (handled directly by us) */ .header_type = IW_HEADER_TYPE_NULL, .flags = IW_DESCR_FLAG_DUMP, }, [SIOCSIWSPY - SIOCIWFIRST] = { .header_type = IW_HEADER_TYPE_POINT, .token_size = sizeof(struct sockaddr), .max_tokens = IW_MAX_SPY, }, [SIOCGIWSPY - SIOCIWFIRST] = { .header_type = IW_HEADER_TYPE_POINT, .token_size = sizeof(struct sockaddr) + sizeof(struct iw_quality), .max_tokens = IW_MAX_SPY, }, [SIOCSIWTHRSPY - SIOCIWFIRST] = { .header_type = IW_HEADER_TYPE_POINT, .token_size = sizeof(struct iw_thrspy), .min_tokens = 1, .max_tokens = 1, }, [SIOCGIWTHRSPY - SIOCIWFIRST] = { .header_type = IW_HEADER_TYPE_POINT, .token_size = sizeof(struct iw_thrspy), .min_tokens = 1, .max_tokens = 1, }, [SIOCSIWAP - SIOCIWFIRST] = { .header_type = IW_HEADER_TYPE_ADDR, }, [SIOCGIWAP - SIOCIWFIRST] = { .header_type = IW_HEADER_TYPE_ADDR, .flags = IW_DESCR_FLAG_DUMP, }, [SIOCGIWAPLIST - SIOCIWFIRST] = { .header_type = IW_HEADER_TYPE_POINT, .token_size = sizeof(struct sockaddr) + sizeof(struct iw_quality), .max_tokens = IW_MAX_AP, }, [SIOCSIWSCAN - SIOCIWFIRST] = { .header_type = IW_HEADER_TYPE_PARAM, }, [SIOCGIWSCAN - SIOCIWFIRST] = { .header_type = IW_HEADER_TYPE_POINT, .token_size = 1, .max_tokens = IW_SCAN_MAX_DATA, }, [SIOCSIWESSID - SIOCIWFIRST] = { .header_type = IW_HEADER_TYPE_POINT, .token_size = 1, .max_tokens = IW_ESSID_MAX_SIZE + 1, .flags = IW_DESCR_FLAG_EVENT, }, [SIOCGIWESSID - SIOCIWFIRST] = { .header_type = IW_HEADER_TYPE_POINT, .token_size = 1, .max_tokens = IW_ESSID_MAX_SIZE + 1, .flags = IW_DESCR_FLAG_DUMP, }, [SIOCSIWNICKN - SIOCIWFIRST] = { .header_type = IW_HEADER_TYPE_POINT, .token_size = 1, .max_tokens = IW_ESSID_MAX_SIZE + 1, }, [SIOCGIWNICKN - SIOCIWFIRST] = { .header_type = IW_HEADER_TYPE_POINT, .token_size = 1, .max_tokens = IW_ESSID_MAX_SIZE + 1, }, [SIOCSIWRATE - SIOCIWFIRST] = { .header_type = IW_HEADER_TYPE_PARAM, }, [SIOCGIWRATE - SIOCIWFIRST] = { .header_type = IW_HEADER_TYPE_PARAM, }, [SIOCSIWRTS - SIOCIWFIRST] = { .header_type = IW_HEADER_TYPE_PARAM, }, [SIOCGIWRTS - SIOCIWFIRST] = { .header_type = IW_HEADER_TYPE_PARAM, }, [SIOCSIWFRAG - SIOCIWFIRST] = { .header_type = IW_HEADER_TYPE_PARAM, }, [SIOCGIWFRAG - SIOCIWFIRST] = { .header_type = IW_HEADER_TYPE_PARAM, }, [SIOCSIWTXPOW - SIOCIWFIRST] = { .header_type = IW_HEADER_TYPE_PARAM, }, [SIOCGIWTXPOW - SIOCIWFIRST] = { .header_type = IW_HEADER_TYPE_PARAM, }, [SIOCSIWRETRY - SIOCIWFIRST] = { .header_type = IW_HEADER_TYPE_PARAM, }, [SIOCGIWRETRY - SIOCIWFIRST] = { .header_type = IW_HEADER_TYPE_PARAM, }, [SIOCSIWENCODE - SIOCIWFIRST] = { .header_type = IW_HEADER_TYPE_POINT, .token_size = 1, .max_tokens = IW_ENCODING_TOKEN_MAX, .flags = IW_DESCR_FLAG_EVENT | IW_DESCR_FLAG_RESTRICT, }, [SIOCGIWENCODE - SIOCIWFIRST] = { .header_type = IW_HEADER_TYPE_POINT, .token_size = 1, .max_tokens = IW_ENCODING_TOKEN_MAX, .flags = IW_DESCR_FLAG_DUMP | IW_DESCR_FLAG_RESTRICT, }, [SIOCSIWPOWER - SIOCIWFIRST] = { .header_type = IW_HEADER_TYPE_PARAM, }, [SIOCGIWPOWER - SIOCIWFIRST] = { .header_type = IW_HEADER_TYPE_PARAM, },};static const int standard_ioctl_num = (sizeof(standard_ioctl) / sizeof(struct iw_ioctl_description));/* * Meta-data about all the additional standard Wireless Extension events * we know about. */static const struct iw_ioctl_description standard_event[] = { [IWEVTXDROP - IWEVFIRST] = { .header_type = IW_HEADER_TYPE_ADDR, }, [IWEVQUAL - IWEVFIRST] = { .header_type = IW_HEADER_TYPE_QUAL, }, [IWEVCUSTOM - IWEVFIRST] = { .header_type = IW_HEADER_TYPE_POINT, .token_size = 1, .max_tokens = IW_CUSTOM_MAX, }, [IWEVREGISTERED - IWEVFIRST] = { .header_type = IW_HEADER_TYPE_ADDR, }, [IWEVEXPIRED - IWEVFIRST] = { .header_type = IW_HEADER_TYPE_ADDR, },};static const int standard_event_num = (sizeof(standard_event) / sizeof(struct iw_ioctl_description));/* Size (in bytes) of the various private data types */static const char priv_type_size[] = { 0, /* IW_PRIV_TYPE_NONE */ 1, /* IW_PRIV_TYPE_BYTE */ 1, /* IW_PRIV_TYPE_CHAR */ 0, /* Not defined */ sizeof(__u32), /* IW_PRIV_TYPE_INT */ sizeof(struct iw_freq), /* IW_PRIV_TYPE_FLOAT */ sizeof(struct sockaddr), /* IW_PRIV_TYPE_ADDR */ 0, /* Not defined */};/* Size (in bytes) of various events */static const int event_type_size[] = { IW_EV_LCP_LEN, /* IW_HEADER_TYPE_NULL */ 0, IW_EV_CHAR_LEN, /* IW_HEADER_TYPE_CHAR */ 0, IW_EV_UINT_LEN, /* IW_HEADER_TYPE_UINT */ IW_EV_FREQ_LEN, /* IW_HEADER_TYPE_FREQ */ IW_EV_ADDR_LEN, /* IW_HEADER_TYPE_ADDR */ 0, IW_EV_POINT_LEN, /* Without variable payload */ IW_EV_PARAM_LEN, /* IW_HEADER_TYPE_PARAM */ IW_EV_QUAL_LEN, /* IW_HEADER_TYPE_QUAL */};/************************ COMMON SUBROUTINES ************************//* * Stuff that may be used in various place or doesn't fit in one * of the section below. *//* ---------------------------------------------------------------- *//* * Return the driver handler associated with a specific Wireless Extension. * Called from various place, so make sure it remains efficient. */static inline iw_handler get_handler(struct net_device *dev, unsigned int cmd){ /* Don't "optimise" the following variable, it will crash */ unsigned int index; /* *MUST* be unsigned */ /* Check if we have some wireless handlers defined */ if(dev->wireless_handlers == NULL) return NULL; /* Try as a standard command */ index = cmd - SIOCIWFIRST; if(index < dev->wireless_handlers->num_standard) return dev->wireless_handlers->standard[index]; /* Try as a private command */ index = cmd - SIOCIWFIRSTPRIV; if(index < dev->wireless_handlers->num_private) return dev->wireless_handlers->private[index]; /* Not found */ return NULL;}/* ---------------------------------------------------------------- *//* * Get statistics out of the driver */static inline struct iw_statistics *get_wireless_stats(struct net_device *dev){ return (dev->get_wireless_stats ? dev->get_wireless_stats(dev) : (struct iw_statistics *) NULL); /* In the future, get_wireless_stats may move from 'struct net_device' * to 'struct iw_handler_def', to de-bloat struct net_device. * Definitely worse a thought... */}/* ---------------------------------------------------------------- *//* * Call the commit handler in the driver * (if exist and if conditions are right) * * Note : our current commit strategy is currently pretty dumb, * but we will be able to improve on that... * The goal is to try to agreagate as many changes as possible * before doing the commit. Drivers that will define a commit handler * are usually those that need a reset after changing parameters, so * we want to minimise the number of reset. * A cool idea is to use a timer : at each "set" command, we re-set the * timer, when the timer eventually fires, we call the driver. * Hopefully, more on that later. * * Also, I'm waiting to see how many people will complain about the * netif_running(dev) test. I'm open on that one... * Hopefully, the driver will remember to do a commit in "open()" ;-) */static inline int call_commit_handler(struct net_device * dev){ if((netif_running(dev)) && (dev->wireless_handlers->standard[0] != NULL)) { /* Call the commit handler on the driver */ return dev->wireless_handlers->standard[0](dev, NULL, NULL, NULL); } else return 0; /* Command completed successfully */}/* ---------------------------------------------------------------- *//* * Number of private arguments */static inline int get_priv_size(__u16 args){ int num = args & IW_PRIV_SIZE_MASK; int type = (args & IW_PRIV_TYPE_MASK) >> 12; return num * priv_type_size[type];}/******************** /proc/net/wireless SUPPORT ********************//* * The /proc/net/wireless file is a human readable user-space interface * exporting various wireless specific statistics from the wireless devices. * This is the most popular part of the Wireless Extensions ;-) * * This interface is a pure clone of /proc/net/dev (in net/core/dev.c). * The content of the file is basically the content of "struct iw_statistics". */#ifdef CONFIG_PROC_FS/* ---------------------------------------------------------------- *//* * Print one entry (line) of /proc/net/wireless */static __inline__ void wireless_seq_printf_stats(struct seq_file *seq, struct net_device *dev){ /* Get stats from the driver */ struct iw_statistics *stats = get_wireless_stats(dev); if (stats) { seq_printf(seq, "%6s: %04x %3d%c %3d%c %3d%c %6d %6d %6d " "%6d %6d %6d\n", dev->name, stats->status, stats->qual.qual, stats->qual.updated & 1 ? '.' : ' ', ((__u8) stats->qual.level), stats->qual.updated & 2 ? '.' : ' ', ((__u8) stats->qual.noise), stats->qual.updated & 4 ? '.' : ' ', stats->discard.nwid, stats->discard.code, stats->discard.fragment, stats->discard.retries, stats->discard.misc, stats->miss.beacon); stats->qual.updated = 0; }}/* ---------------------------------------------------------------- *//* * Print info for /proc/net/wireless (print all entries) */static int wireless_seq_show(struct seq_file *seq, void *v)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -