diff -Naurp wireless-dev-old/include/net/d80211_regdomains.h wireless-dev/include/net/d80211_regdomains.h --- wireless-dev-old/include/net/d80211_regdomains.h 1969-12-31 19:00:00.000000000 -0500 +++ wireless-dev/include/net/d80211_regdomains.h 2006-10-23 16:55:19.000000000 -0400 @@ -0,0 +1,210 @@ +#ifndef _IEEE80211_REGDOMAIN_H +#define _IEEE80211_REGDOMAIN_H +/* + * Copyright (C) 2006 Luis R. Rodriguez + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include + +#define IEEE80211_REGDOMAINS_VERSION "0.9" + +/* Max default power for Equivalent Isotropically Radiated Power (EIRP) + * EIRP = IR - loss in transmission line + passive gain of the antenna */ +#define MAX_DF_80211_EIRP_POWER 20 /* 20dBm (100mW) */ +/* Regulatory domain name size */ +#define REGNAMSIZ 22 + +#define CHANNEL_24MHZ_FREQ(chan) (chan == 14) ? 2484 : ((2407 + (5 * (chan)))) +#define CHANNEL_5MHZ_FREQ(chan) \ + (chan >= 240 && chan <= 252) ? 5000 - (5 * (chan)) : 5000 + (5 * (chan)) + +/* Most 5GHz band 802.11a channels restrictions skip channels 65-148 */ +#define number_80211a_channels_defaults(amin, amax) \ + (((amax - 149 + 4) + (64 - amin + 4)) / 4) + +/* Stack regulatory domain values. Atheros seems to have started this + * convention and most hardware vendors followed their example. + * Although these are *very* common we allow for a hw-specific + * map between stack and device regdomain values. */ +enum stack_regdomain { + REGDOMAIN_WORLD = 0x00, /* World compliance, minimum set */ + REGDOMAIN_FCC = 0x10, /* USA */ + REGDOMAIN_IC = 0x20, /* Canada */ + REGDOMAIN_ETSI = 0x30, /* Europe */ + REGDOMAIN_SPAIN = 0x31, /* Spain */ + REGDOMAIN_FRANCE = 0x32, /* France */ + REGDOMAIN_MKK = 0x40, /* Japan */ +}; + +struct ieee80211_regdomain { + u32 regdomain_id; /* stack-aware value */ + char regdomain_name[REGNAMSIZ]; /* stack-aware value */ + struct list_head list; /* node, part of ieee80211_regdomain_list */ + /* List of supported bands (struct ieee80211_band_restrictions) */ + struct list_head band_restrictions_list; +}; + +enum ieee80211_band { + IEEE80211_BAND_24GHZ = 0, + IEEE80211_BAND_5GHZ, + IEEE80211_NUM_SUPPORTED_BANDS, /* stack supported bands */ +}; + +struct ieee80211_band_restrictions { + enum ieee80211_band band; /* IEEE80211_BAND_24GHZ, IEEE80211_BAND_5GHZ */ + /* node, part of band_restrictions_list */ + struct list_head list; + /* list of subband restrictions for PtMP (Point to MultiPoint) + * (struct ieee80211_subband_restrictions) */ + struct list_head subband_restrictions_list; + /* list of subband restrictions for PtP (Point to Point) + * (struct ieee80211_subband_restrictions) */ + struct list_head subband_restrictions_list_p2p; +}; + +enum subband_type { + IEEE80211_SUBBAND_RANGE = 0, /* for sequential channels */ + IEEE80211_SUBBAND_SET, /* non sequential set of channels */ +}; + +#define IEEE80211_SUBBAND_INDOOR_CAP (1 << 1) +#define IEEE80211_SUBBAND_OUTDOOR_CAP (1 << 2) + +/* A subband is a set of channels with a shared set of common values. + * Channels who have the same: + * - type + * - mode + * - environment cap + * belong to the same subband */ +struct ieee80211_subband_restrictions { + enum subband_type type; + /* XXX: patch d80211 to make mode enum available, use here then */ + u8 mode; /* MODE_IEEE80211A, MODE_IEEE80211B, MODE_IEEE80211G, etc */ + u8 environment_cap; /* indoor and outdoor capabilities */ + /* For power we use Equivalent Isotropically Radiated Power (EIRP) + * EIRP is the total radiated power. Each device driver should specify their + * antenna gain. The IR can then be computed approx as: + * IR = regdomain EIRP - passive gain of antenna + */ + u8 max_eirp_indoor; /* maximum allowed power in dBm */ + u8 max_eirp_outdoor; /* maximum allowed power in dBm */ + u8 num_channels; /* avoid 5GHz channel hop arithmetic mess */ + u8 min_chan; /* what channel regdomain starts in */ + u8 max_chan; /* what channel regdomain end in */ + /* node, part of subband_restrictions_list */ + struct list_head list; + /* list of channels (struct ieee80211_regdomain_channel) */ + struct list_head channel_list; +}; + +/* Wrapper to make ieee80211_channel a list node, part of a channel_list */ +/* XXX: consider putting a struct list_head on ieee80211_channel to + * remove this struct */ +struct ieee80211_regdomain_channel { + struct list_head list; + struct ieee80211_channel channel; +}; + + +/* Now we define regulatory domain maps for the Stack --> + * I. iso3166-1 alpha3 + * II. Device hw regdomains + */ + +/* I. Generic stack isocountry --> regulatory domain map */ +struct ieee80211_iso3166_reg_map { + u32 regdomain_id; /* stack-aware value */ + /* Used as 802.11d uses 3 octects for Country Information Element, + * this should be the ISO-3166-1 alpha 3 codes */ + char alpha3[ISOCOUNTRYSIZ3]; + struct list_head list; /* node, part of iso3166_reg_map_list */ +}; + +/* II. Device specific - stack regdomain --> hw regdomain map */ +struct ieee80211_regulatory_map { + char hwreg_reg_mapname[ISOCOUNTRYSIZ]; + struct list_head hwreg_map_list; /* struct ieee80211_hwreg_map */ + struct list_head list; /* node, part of ieee80211_regulatory_maps */ +}; +struct ieee80211_hwreg_map { + u32 regdomain_id; /* stack-aware value */ + u32 regdomain_id_hw; /* hw regdomain equivalent */ + struct list_head list; /* node, part of hwreg_reg_maps_list */ +}; + +static inline int number_80211a_channels(int amin, int amax){ + if(amin > 64 || amax < 149) + return -1; + else + return number_80211a_channels_defaults(amin, amax); +} + +static inline int check_reg_addition(int, char *); +int regdomain_name_valid(char *); +int regdomain_exists(int, char *); +static inline void setup_regdomain(struct ieee80211_regdomain *, u8 ,char *); +static inline void setup_band_restrictions( + struct ieee80211_band_restrictions *b, + enum ieee80211_band band); +static inline void setup_subband_restrictions( + struct ieee80211_subband_restrictions *, + enum subband_type, u8, u8, u8, u8, u8, u8, u8); +static void free_regdomains(void); +static int add_regdomain_bga(u8 reg_id, char *reg_name, + u8 channel_min_g, u8 channel_max_g, + u8 channel_min_a, u8 channel_max_a); +void print_regdomain(struct ieee80211_regdomain *); +void print_regdomains(void); +void print_iso3166_reg_map(void); +void print_hwreg_reg_map(void); +int get_ieee80211_regname(u32, char *); +static int load_user_regdomains(void); +static int load_regdomain_defaults(void); +static int update_ieee80211_regdomains(void); + + +int alpha3_valid(char *); +int add_iso3166_reg_map(char *, u32); +inline int iso3166_to_reg_exists(char *); +int iso3166_to_reg(char *, u32); + +/* TODO: Complete 802.11d support */ +#ifdef CONFIG_IEEE80211D +/* You are either: + * IEEE80211D_NON_CONFORMING not conforming to 802.11d or any regdomain + * IEEE80211D_MINIMUM conforming to world regulatory domain + * IEEE80211D_REGULATORY conforming to a regulatory domain on list + * IEEE80211D_BSS_REQUEST your BSS has requested to conform to 802.11d + * IEEE80211D_COMFORMING joined BSS which offers 802.11d conformance + * */ +enum ieee80211d_status { + IEEE80211D_NON_CONFORMING = 0, + IEEE80211D_MINIMUM, + IEEE80211D_REGULATORY, + IEEE80211D_BSS_REQUEST, + IEEE80211D_COMFORMING, +}; + +/* 802.11d conformance */ +struct ieee80211d_conformance { + char alpha3[ISOCOUNTRYSIZ3]; /* captured from BSS */ + enum ieee80211d_status status; /* describes 802.11d/regulatory status */ +}; +#endif /* CONFIG_IEEE80211D */ + +#endif diff -Naurp wireless-dev-old/net/d80211/ieee80211_regdomains.c wireless-dev/net/d80211/ieee80211_regdomains.c --- wireless-dev-old/net/d80211/ieee80211_regdomains.c 1969-12-31 19:00:00.000000000 -0500 +++ wireless-dev/net/d80211/ieee80211_regdomains.c 2006-10-23 17:03:53.000000000 -0400 @@ -0,0 +1,1325 @@ +/* + * Copyright (C) 2006 Luis R. Rodriguez + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#include +#include +#include +#include + +#define DRV_NAME "ieee80211_regdomains" +#define DRV_DESCRIPTION "IEEE-802.11 Regulatory Domain wireless stack driver" +#define DRV_VERSION IEEE80211_REGDOMAINS_VERSION + +MODULE_AUTHOR("Luis R. Rodriguez "); +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_DESCRIPTION(DRV_DESCRIPTION); +MODULE_VERSION(DRV_VERSION); + +/* Default supported list of regulatory domains */ +LIST_HEAD(ieee80211_regdomains_list); +/* Map of iso3166-alpha3 country codes to specific stack regdomain */ +LIST_HEAD(iso3166_reg_map_list); +/* List of available hwreg->reg maps which devices adhere to */ +LIST_HEAD(ieee80211_reg_map_list); + +rwlock_t regdomain_rwlock = RW_LOCK_UNLOCKED; +rwlock_t iso3166_reg_rwlock = RW_LOCK_UNLOCKED; +rwlock_t reg_map_rwlock = RW_LOCK_UNLOCKED; + +static inline void setup_regdomain(struct ieee80211_regdomain *r, + u8 regdomain_id, char *regdomain_name) +{ + strcpy(r->regdomain_name, regdomain_name); + r->regdomain_id = regdomain_id; +} + +static inline void setup_band_restrictions( + struct ieee80211_band_restrictions *b, + enum ieee80211_band band) +{ + b->band = band; +} + +static inline void setup_subband_restrictions( + struct ieee80211_subband_restrictions *sb, + enum subband_type type, u8 mode, + u8 min_chan, u8 max_chan, u8 num_channels, + u8 max_eirp_indoor, u8 max_eirp_outdoor, + u8 environment_cap) +{ + sb->type = type; + sb->mode = mode; + sb->environment_cap = environment_cap; + sb->max_eirp_indoor = max_eirp_indoor; + sb->max_eirp_outdoor = max_eirp_outdoor; + sb->min_chan = min_chan; + sb->max_chan = max_chan; + sb->num_channels= num_channels; +} + +static inline void setup_reg_channel( + struct ieee80211_regdomain_channel *reg_chan, + enum ieee80211_band band, + short channel) +{ + struct ieee80211_channel *chan = ®_chan->channel; + chan->chan = channel; + chan->val = channel; + chan->flag = IEEE80211_CHAN_W_SCAN | + IEEE80211_CHAN_W_ACTIVE_SCAN | + IEEE80211_CHAN_W_IBSS; + switch(band){ + case IEEE80211_BAND_5GHZ: + chan->freq = CHANNEL_5MHZ_FREQ(channel); + break; + case IEEE80211_BAND_24GHZ: + default: + chan->freq = CHANNEL_24MHZ_FREQ(channel); + break; + } +} + +int regdomain_name_valid(char *regdomain_name) { + if(strlen(regdomain_name) >= REGNAMSIZ) + return 0; + return 1; +} + +/* Expects you to hold lock */ +int regdomain_exists(int regdomain_id, char *regdomain_name) +{ + struct ieee80211_regdomain *reg; + int r = 0; + if(!regdomain_name_valid(regdomain_name)) { + r = 4; + return r; + } + if (unlikely(list_empty(&ieee80211_regdomains_list))) + return r; + list_for_each_entry(reg, &ieee80211_regdomains_list, list) { + if(reg->regdomain_id == regdomain_id) + r++; + if(strcmp(reg->regdomain_name, regdomain_name)==0) + r+=2; + if(r>0) + break; + } + return r; +} + +static int alloc_reg_bands_subbands(struct ieee80211_regdomain **reg, + struct ieee80211_band_restrictions *bands[], int num_bands, + struct ieee80211_subband_restrictions *subbands[], int num_subs) +{ + int b_num, s_num, i; + struct ieee80211_band_restrictions *band; + struct ieee80211_subband_restrictions *subband; + + /* Alloc regdomain */ + *reg = kmalloc(sizeof(struct ieee80211_regdomain), GFP_KERNEL); + if (*reg == NULL) + goto exit; + memset(*reg, 0, sizeof(struct ieee80211_regdomain)); + + /* Alloc band restrictions */ + for(b_num = 0; b_num < num_bands; b_num++) { + band = kmalloc(sizeof(struct ieee80211_band_restrictions), + GFP_KERNEL); + if (band == NULL) + goto free_bands; + memset(band, 0, sizeof(struct ieee80211_band_restrictions)); + bands[b_num] = band; + } + + /* Alloc subband restrictions */ + for(s_num = 0; s_num < num_subs; s_num++) { + subband=kmalloc(sizeof(struct ieee80211_subband_restrictions), + GFP_KERNEL); + if (subband == NULL) + goto free_subbands; + memset(subband, 0, + sizeof(struct ieee80211_subband_restrictions)); + subbands[s_num] = subband; + } + + return 0; + +free_subbands: + for(i=0; i < s_num; i++) + kfree(subbands[i]); +free_bands: + for(i=0; i < b_num; i++) + kfree(bands[i]); + kfree(*reg); +exit: + return -ENOMEM; +} + +static void free_subband_channel_list(struct list_head *channel_list) +{ + struct ieee80211_regdomain_channel *reg_chan, *reg_chan_tmp; + list_for_each_entry_safe(reg_chan, reg_chan_tmp, channel_list, list) { + list_del(®_chan->list); + kfree(reg_chan); + } +} + +static void free_regdomains(void) +{ + struct ieee80211_regdomain *reg, *reg_tmp; + struct ieee80211_band_restrictions *b, *b_tmp; + struct ieee80211_subband_restrictions *sb, *sb_tmp; + list_for_each_entry_safe(reg,reg_tmp,&ieee80211_regdomains_list,list) { + list_del(®->list); + list_for_each_entry_safe(b,b_tmp,®->band_restrictions_list,list) { + list_del(&b->list); + list_for_each_entry_safe(sb,sb_tmp,&b->subband_restrictions_list,list){ + list_del(&sb->list); + free_subband_channel_list(&sb->channel_list); + kfree(sb); + } + kfree(b); + } + kfree(reg); + } +} + +static void free_iso3166_reg_map_list(void) +{ + struct ieee80211_iso3166_reg_map *map, *tmp; + list_for_each_entry_safe(map, tmp, &iso3166_reg_map_list, list) { + list_del(&map->list); + kfree(map); + } +} + + +static void free_hwreg_reg_maps(void) +{ + struct ieee80211_regulatory_map *map, *tmp; + struct ieee80211_hwreg_map *hwreg_map, *hw_tmp; + + list_for_each_entry_safe(map, tmp, &ieee80211_reg_map_list, list) { + list_for_each_entry_safe(hwreg_map, hw_tmp, + &map->hwreg_map_list, list) { + list_del(&hwreg_map->list); + kfree(hwreg_map); + } + list_del(&map->list); + kfree(map); + } +} + +static int add_regdomain_world(void) +{ + int r = -ENOMEM, i, num_sbs = 4; + short chan; + struct ieee80211_regdomain *reg = NULL; + u32 reg_id = REGDOMAIN_WORLD; + char *reg_name = "World compliance"; + + struct ieee80211_band_restrictions *bands[2]; + struct ieee80211_subband_restrictions *subbands[num_sbs]; + + struct ieee80211_band_restrictions *b24r; + struct ieee80211_band_restrictions *b5r; + struct ieee80211_subband_restrictions *ism24; + struct ieee80211_subband_restrictions *unii_1; + struct ieee80211_subband_restrictions *unii_2; + struct ieee80211_subband_restrictions *unii_3; + + read_lock(®domain_rwlock); + r = regdomain_exists(reg_id, reg_name); + if(r) { + read_unlock(®domain_rwlock); + goto exit; + } + read_unlock(®domain_rwlock); + + r = alloc_reg_bands_subbands(®, bands, 2, subbands, num_sbs); + if (r) + goto exit; + + /* "Who's your daddy and what does he do?" */ + b24r = bands[0]; + b5r = bands[1]; + ism24 = subbands[0]; + unii_1 = subbands[1]; + unii_2 = subbands[2]; + unii_3 = subbands[3]; + + /* Setup regdomain, bands and subbands */ + setup_regdomain(reg, REGDOMAIN_WORLD, reg_name); + setup_band_restrictions(b24r, IEEE80211_BAND_24GHZ); + setup_band_restrictions(b5r, IEEE80211_BAND_5GHZ); + + /* As per FCC 15.247 */ + + /* ISM 2.4 GHz Band */ + setup_subband_restrictions(ism24, + IEEE80211_SUBBAND_RANGE, MODE_IEEE80211G, + /* min_chan */ 1, + /* max_chan */ 11, + /* num,_channels */ 11, + /* max_eirp_indoor */ MAX_DF_80211_EIRP_POWER, + /* max_eirp_outdoor */ 0, + IEEE80211_SUBBAND_INDOOR_CAP); + + /* UNII-1 Band (5150 - 5250 MHz) */ + setup_subband_restrictions(unii_1, + IEEE80211_SUBBAND_RANGE, MODE_IEEE80211A, + /* min_chan */ 36, + /* max_chan */ 48, + /* num,_channels */ 4, + /* max_eirp_indoor */ MAX_DF_80211_EIRP_POWER, + /* max_eirp_outdoor */ 0, + IEEE80211_SUBBAND_INDOOR_CAP); + + /* UNII-2 Band (5250 - 5350 MHz) */ + setup_subband_restrictions(unii_2, + IEEE80211_SUBBAND_RANGE, MODE_IEEE80211A, + /* min_chan */ 52, + /* max_chan */ 64, + /* num,_channels */ 4, + /* max_eirp_indoor */ MAX_DF_80211_EIRP_POWER, + /* max_eirp_outdoor */ 0, + IEEE80211_SUBBAND_INDOOR_CAP); + + /* UNII-3 Band (5725 - 5825 MHz) */ + setup_subband_restrictions(unii_3, + IEEE80211_SUBBAND_RANGE, MODE_IEEE80211A, + /* min_chan */ 149, + /* max_chan */ 161, + /* num,_channels */ 4, + /* max_eirp_indoor */ MAX_DF_80211_EIRP_POWER, + /* max_eirp_outdoor */ 0, + IEEE80211_SUBBAND_INDOOR_CAP); + + /* Initialize all lists */ + INIT_LIST_HEAD(®->band_restrictions_list); + INIT_LIST_HEAD(&b24r->subband_restrictions_list); + INIT_LIST_HEAD(&b5r->subband_restrictions_list); + INIT_LIST_HEAD(&ism24->channel_list); + INIT_LIST_HEAD(&unii_1->channel_list); + INIT_LIST_HEAD(&unii_2->channel_list); + INIT_LIST_HEAD(&unii_3->channel_list); + + /* Alloc/setup/add g channels */ + for(chan = ism24->min_chan; chan <= ism24->max_chan; chan++) { + struct ieee80211_regdomain_channel *reg_chan; + reg_chan = kmalloc(sizeof(struct ieee80211_regdomain_channel), + GFP_KERNEL); + if(reg_chan == NULL) { + r = -ENOMEM; + goto free_g_channels; + } + memset(reg_chan, 0, + sizeof(struct ieee80211_regdomain_channel)); + setup_reg_channel(reg_chan, + IEEE80211_BAND_24GHZ, chan); + list_add_tail(®_chan->list, &ism24->channel_list); + } + + /* Alloc/setup/add a channels */ + for(i=1; i < num_sbs; i++) { + struct ieee80211_subband_restrictions *a_band; + a_band = subbands[i]; + for(chan = a_band->min_chan; chan <= a_band->max_chan; + chan = (chan == 64) ? 149 : chan+4) { + struct ieee80211_regdomain_channel *reg_chan; + reg_chan = kmalloc(sizeof(struct + ieee80211_regdomain_channel), + GFP_KERNEL); + if(reg_chan == NULL) { + r = -ENOMEM; + goto free_a_channels; + } + memset(reg_chan, 0, sizeof(struct + ieee80211_regdomain_channel)); + setup_reg_channel(reg_chan, + IEEE80211_BAND_5GHZ, chan); + list_add_tail(®_chan->list, &a_band->channel_list); + } + } + + /* Hook regdomain, bands, subbands all together */ + list_add_tail(&b24r->list, ®->band_restrictions_list); + list_add_tail(&b5r->list, ®->band_restrictions_list); + list_add_tail(&ism24->list, &b24r->subband_restrictions_list); + list_add_tail(&unii_1->list, &b5r->subband_restrictions_list); + list_add_tail(&unii_2->list, &b5r->subband_restrictions_list); + list_add_tail(&unii_3->list, &b5r->subband_restrictions_list); + + /* Finally, add this reg to list of regdomains */ + write_lock(®domain_rwlock); + list_add_tail(®->list, &ieee80211_regdomains_list); + write_unlock(®domain_rwlock); + + check_reg_addition(r, reg_name); + return r; + +free_a_channels: + for(i=1; i < num_sbs; i++) { + free_subband_channel_list(&subbands[i]->channel_list); + } +free_g_channels: + free_subband_channel_list(&ism24->channel_list); + + list_del(&ism24->channel_list); + list_del(&unii_1->channel_list); + list_del(&unii_2->channel_list); + list_del(&unii_3->channel_list); + list_del(&b24r->subband_restrictions_list); + list_del(&b5r->subband_restrictions_list); + list_del(®->band_restrictions_list); +exit: + check_reg_addition(r, reg_name); + return r; +} + +#ifdef D80211_REGDOMAIN_FCC +static int add_regdomain_fcc(void) +{ + int r = -ENOMEM, i, num_sbs = 4; + short chan; + struct ieee80211_regdomain *reg = NULL; + u32 reg_id = REGDOMAIN_FCC; + char *reg_name = "FCC"; + + struct ieee80211_band_restrictions *bands[2]; + struct ieee80211_subband_restrictions *subbands[num_sbs]; + + struct ieee80211_band_restrictions *b24r; + struct ieee80211_band_restrictions *b5r; + struct ieee80211_subband_restrictions *ism24; + struct ieee80211_subband_restrictions *unii_1; + struct ieee80211_subband_restrictions *unii_2; + struct ieee80211_subband_restrictions *unii_3; + + read_lock(®domain_rwlock); + r = regdomain_exists(reg_id, reg_name); + if(r) { + read_unlock(®domain_rwlock); + goto exit; + } + read_unlock(®domain_rwlock); + + r = alloc_reg_bands_subbands(®, bands, 2, subbands, num_sbs); + if (r) + goto exit; + + b24r = bands[0]; + b5r = bands[1]; + ism24 = subbands[0]; + unii_1 = subbands[1]; + unii_2 = subbands[2]; + unii_3 = subbands[3]; + + /* Setup regdomain, bands and subbands */ + setup_regdomain(reg, REGDOMAIN_FCC, reg_name); + setup_band_restrictions(b24r, IEEE80211_BAND_24GHZ); + setup_band_restrictions(b5r, IEEE80211_BAND_5GHZ); + + /* As per FCC 15.247 */ + + /* ISM 2.4 GHz Band */ + setup_subband_restrictions(ism24, + IEEE80211_SUBBAND_RANGE, MODE_IEEE80211G, + /* min_chan */ 1, + /* max_chan */ 11, + /* num,_channels */ 11, + /* max_eirp_indoor */ 30, + /* max_eirp_outdoor */ 30, + IEEE80211_SUBBAND_INDOOR_CAP | + IEEE80211_SUBBAND_OUTDOOR_CAP); + + /* UNII-1 Band (5150 - 5250 MHz) */ + setup_subband_restrictions(unii_1, + IEEE80211_SUBBAND_RANGE, MODE_IEEE80211A, + /* min_chan */ 36, + /* max_chan */ 48, + /* num,_channels */ 4, + /* max_eirp_indoor */ 30, + /* max_eirp_outdoor */ 0, + IEEE80211_SUBBAND_INDOOR_CAP); + + /* UNII-2 Band (5250 - 5350 MHz) */ + setup_subband_restrictions(unii_2, + IEEE80211_SUBBAND_RANGE, MODE_IEEE80211A, + /* min_chan */ 52, + /* max_chan */ 64, + /* num,_channels */ 4, + /* max_eirp_indoor */ 30, + /* max_eirp_outdoor */ 30, + IEEE80211_SUBBAND_INDOOR_CAP | + IEEE80211_SUBBAND_OUTDOOR_CAP); + + /* UNII-3 Band (5725 - 5825 MHz) */ + setup_subband_restrictions(unii_3, + IEEE80211_SUBBAND_RANGE, MODE_IEEE80211A, + /* min_chan */ 149, + /* max_chan */ 161, + /* num,_channels */ 4, + /* max_eirp_indoor */ 36, + /* max_eirp_outdoor */ 36, + IEEE80211_SUBBAND_INDOOR_CAP | + IEEE80211_SUBBAND_OUTDOOR_CAP); + + /* Initialize all lists */ + INIT_LIST_HEAD(®->band_restrictions_list); + INIT_LIST_HEAD(&b24r->subband_restrictions_list); + INIT_LIST_HEAD(&b5r->subband_restrictions_list); + INIT_LIST_HEAD(&ism24->channel_list); + INIT_LIST_HEAD(&unii_1->channel_list); + INIT_LIST_HEAD(&unii_2->channel_list); + INIT_LIST_HEAD(&unii_3->channel_list); + + /* Alloc/setup/add g channels */ + for(chan = ism24->min_chan; chan <= ism24->max_chan; chan++) { + struct ieee80211_regdomain_channel *reg_chan; + reg_chan = kmalloc(sizeof(struct ieee80211_regdomain_channel), + GFP_KERNEL); + if(reg_chan == NULL) { + r = -ENOMEM; + goto free_g_channels; + } + memset(reg_chan, 0, + sizeof(struct ieee80211_regdomain_channel)); + setup_reg_channel(reg_chan, + IEEE80211_BAND_24GHZ, chan); + list_add_tail(®_chan->list, &ism24->channel_list); + } + + /* Alloc/setup/add a channels */ + for(i=1; i < num_sbs; i++) { + struct ieee80211_subband_restrictions *a_band; + a_band = subbands[i]; + for(chan = a_band->min_chan; chan <= a_band->max_chan; + chan = (chan == 64) ? 149 : chan+4) { + struct ieee80211_regdomain_channel *reg_chan; + reg_chan = kmalloc(sizeof(struct + ieee80211_regdomain_channel), + GFP_KERNEL); + if(reg_chan == NULL) { + r = -ENOMEM; + goto free_a_channels; + } + memset(reg_chan, 0, sizeof(struct + ieee80211_regdomain_channel)); + setup_reg_channel(reg_chan, + IEEE80211_BAND_5GHZ, chan); + list_add_tail(®_chan->list, &a_band->channel_list); + } + } + + /* Hook regdomain, bands, subbands all together */ + list_add_tail(&b24r->list, ®->band_restrictions_list); + list_add_tail(&b5r->list, ®->band_restrictions_list); + list_add_tail(&ism24->list, &b24r->subband_restrictions_list); + list_add_tail(&unii_1->list, &b5r->subband_restrictions_list); + list_add_tail(&unii_2->list, &b5r->subband_restrictions_list); + list_add_tail(&unii_3->list, &b5r->subband_restrictions_list); + + /* Finally, add this reg to list of regdomains */ + write_lock(®domain_rwlock); + list_add_tail(®->list, &ieee80211_regdomains_list); + write_unlock(®domain_rwlock); + + check_reg_addition(r, reg_name); + return r; + +free_a_channels: + for(i=1; i < num_sbs; i++) { + free_subband_channel_list(&subbands[i]->channel_list); + } +free_g_channels: + free_subband_channel_list(&ism24->channel_list); + + list_del(&ism24->channel_list); + list_del(&unii_1->channel_list); + list_del(&unii_2->channel_list); + list_del(&unii_3->channel_list); + list_del(&b24r->subband_restrictions_list); + list_del(&b5r->subband_restrictions_list); + list_del(®->band_restrictions_list); +exit: + check_reg_addition(r, reg_name); + return r; +} +#else +static int add_regdomain_fcc(void) +{ + return; +} +#endif + +#ifdef D80211_REGDOMAIN_MKK +static int add_regdomain_mkk(void) +{ + int r = -ENOMEM, i, num_sbs = 3; + short chan; + struct ieee80211_regdomain *reg = NULL; + u32 reg_id = REGDOMAIN_MKK; + char *reg_name = "MKK"; + + struct ieee80211_band_restrictions *bands[2]; + struct ieee80211_subband_restrictions *subbands[num_sbs]; + + struct ieee80211_band_restrictions *b24r; + struct ieee80211_band_restrictions *b5r; + struct ieee80211_subband_restrictions *ism24; + struct ieee80211_subband_restrictions *unii_1; + struct ieee80211_subband_restrictions *unii_2; + + read_lock(®domain_rwlock); + r = regdomain_exists(reg_id, reg_name); + if(r) { + read_unlock(®domain_rwlock); + goto exit; + } + read_unlock(®domain_rwlock); + + r = alloc_reg_bands_subbands(®, bands, 2, subbands, num_sbs); + if (r) + goto exit; + + b24r = bands[0]; + b5r = bands[1]; + ism24 = subbands[0]; + unii_1 = subbands[1]; + unii_2 = subbands[2]; + + /* Setup regdomain, bands and subbands */ + setup_regdomain(reg, REGDOMAIN_FCC, reg_name); + setup_band_restrictions(b24r, IEEE80211_BAND_24GHZ); + setup_band_restrictions(b5r, IEEE80211_BAND_5GHZ); + + /* As per FCC 15.247 */ + + /* ISM 2.4 GHz Band */ + setup_subband_restrictions(ism24, + IEEE80211_SUBBAND_RANGE, MODE_IEEE80211G, + /* min_chan */ 1, + /* max_chan */ 13, + /* num,_channels */ 13, + /* max_eirp_indoor */ 30, /* needs verification */ + /* max_eirp_outdoor */ 30, /* needs verification */ + IEEE80211_SUBBAND_INDOOR_CAP | + IEEE80211_SUBBAND_OUTDOOR_CAP); + + + /* UNII-1 Band (5150 - 5250 MHz) */ + /* Note: There are 4 MKK channels: 34, 38, 42, 46 */ + setup_subband_restrictions(unii_1, + IEEE80211_SUBBAND_RANGE, MODE_IEEE80211A, + /* min_chan */ 34, + /* max_chan */ 46, + /* num,_channels */ 4, + /* max_eirp_indoor */ 30, /* needs verification */ + /* max_eirp_outdoor */ 0, /* needs verification */ + IEEE80211_SUBBAND_INDOOR_CAP); + + /* UNII-2 Band (5250 - 5350 MHz) */ + setup_subband_restrictions(unii_2, + IEEE80211_SUBBAND_RANGE, MODE_IEEE80211A, + /* min_chan */ 52, + /* max_chan */ 64, + /* num,_channels */ 4, + /* max_eirp_indoor */ 30, + /* max_eirp_outdoor */ 30, + IEEE80211_SUBBAND_INDOOR_CAP | + IEEE80211_SUBBAND_OUTDOOR_CAP); + + /* Initialize all lists */ + INIT_LIST_HEAD(®->band_restrictions_list); + INIT_LIST_HEAD(&b24r->subband_restrictions_list); + INIT_LIST_HEAD(&b5r->subband_restrictions_list); + INIT_LIST_HEAD(&ism24->channel_list); + INIT_LIST_HEAD(&unii_1->channel_list); + INIT_LIST_HEAD(&unii_2->channel_list); + + /* Alloc/setup/add g channels */ + for(chan = ism24->min_chan; chan <= ism24->max_chan; chan++) { + struct ieee80211_regdomain_channel *reg_chan; + reg_chan = kmalloc(sizeof(struct ieee80211_regdomain_channel), + GFP_KERNEL); + if(reg_chan == NULL) { + r = -ENOMEM; + goto free_g_channels; + } + memset(reg_chan, 0, + sizeof(struct ieee80211_regdomain_channel)); + setup_reg_channel(reg_chan, + IEEE80211_BAND_24GHZ, chan); + list_add_tail(®_chan->list, &ism24->channel_list); + } + + /* Alloc/setup/add a channels */ + for(i=1; i < num_sbs; i++) { + struct ieee80211_subband_restrictions *a_band; + a_band = subbands[i]; + for(chan = a_band->min_chan; chan <= a_band->max_chan; + chan = (chan == 64) ? 149 : chan+4) { + struct ieee80211_regdomain_channel *reg_chan; + reg_chan = kmalloc(sizeof(struct + ieee80211_regdomain_channel), + GFP_KERNEL); + if(reg_chan == NULL) { + r = -ENOMEM; + goto free_a_channels; + } + memset(reg_chan, 0, sizeof(struct + ieee80211_regdomain_channel)); + setup_reg_channel(reg_chan, + IEEE80211_BAND_5GHZ, chan); + list_add_tail(®_chan->list, &a_band->channel_list); + } + } + + /* Hook regdomain, bands, subbands all together */ + list_add_tail(&b24r->list, ®->band_restrictions_list); + list_add_tail(&b5r->list, ®->band_restrictions_list); + list_add_tail(&ism24->list, &b24r->subband_restrictions_list); + list_add_tail(&unii_1->list, &b5r->subband_restrictions_list); + list_add_tail(&unii_2->list, &b5r->subband_restrictions_list); + + /* Finally, add this reg to list of regdomains */ + write_lock(®domain_rwlock); + list_add_tail(®->list, &ieee80211_regdomains_list); + write_unlock(®domain_rwlock); + + check_reg_addition(r, reg_name); + return r; + +free_a_channels: + for(i=1; i < num_sbs; i++) { + free_subband_channel_list(&subbands[i]->channel_list); + } +free_g_channels: + free_subband_channel_list(&ism24->channel_list); + + list_del(&ism24->channel_list); + list_del(&unii_1->channel_list); + list_del(&unii_2->channel_list); + list_del(&b24r->subband_restrictions_list); + list_del(&b5r->subband_restrictions_list); + list_del(®->band_restrictions_list); +exit: + check_reg_addition(r, reg_name); + return r; +} +#else +static int add_regdomain_mkk(void) +{ + return; +} +#endif + +/* Adds a regulatory domain which has restrictions on + * 802.11bg and 802.11a modes with one subband on each + * Note MODE_IEEE80211G's restrictions will also define + * 802.11b restrictions as 802.11g is backward + * compatible with 802.11b */ +static int add_regdomain_bga(u8 reg_id, char *reg_name, + u8 channel_min_g, u8 channel_max_g, + u8 channel_min_a, u8 channel_max_a) +{ + int r = -ENOMEM; + short chan; + struct ieee80211_regdomain *reg = NULL; + struct ieee80211_band_restrictions *b24r = NULL; + struct ieee80211_band_restrictions *b5r = NULL; + struct ieee80211_subband_restrictions *sbg = NULL; + struct ieee80211_subband_restrictions *sba = NULL; + struct ieee80211_band_restrictions *bands[2]; + struct ieee80211_subband_restrictions *subbands[2]; + int num_g_channels = channel_max_g - channel_min_g + 1; + int num_a_channels = number_80211a_channels(channel_min_a, channel_max_a); + + read_lock(®domain_rwlock); + r = regdomain_exists(reg_id, reg_name); + read_unlock(®domain_rwlock); + if(r) + goto exit; + + r = alloc_reg_bands_subbands(®, bands, 2, subbands, 2); + if (r) + goto exit; + + b24r = bands[0]; + b5r = bands[1]; + sbg = subbands[0]; + sba = subbands[1]; + + /* Setup regdomain, bands, subbands */ + setup_regdomain(reg, reg_id, reg_name); + setup_band_restrictions(b24r, IEEE80211_BAND_24GHZ); + setup_band_restrictions(b5r, IEEE80211_BAND_24GHZ); + + setup_subband_restrictions(sbg, + IEEE80211_SUBBAND_RANGE, MODE_IEEE80211G, + channel_min_g, channel_max_g, num_g_channels, + MAX_DF_80211_EIRP_POWER, 0, + IEEE80211_SUBBAND_INDOOR_CAP); + setup_subband_restrictions(sba, + IEEE80211_SUBBAND_RANGE, MODE_IEEE80211A, + channel_min_a, channel_max_a, num_a_channels, + MAX_DF_80211_EIRP_POWER, 0, + IEEE80211_SUBBAND_INDOOR_CAP); + + /* Initialize all lists */ + INIT_LIST_HEAD(®->band_restrictions_list); + INIT_LIST_HEAD(&b24r->subband_restrictions_list); + INIT_LIST_HEAD(&b5r->subband_restrictions_list); + INIT_LIST_HEAD(&sbg->channel_list); + INIT_LIST_HEAD(&sba->channel_list); + + /* Alloc/setup/add g channels */ + for(chan = channel_min_g; chan <= channel_max_g; chan++) { + struct ieee80211_regdomain_channel *reg_chan; + reg_chan = kmalloc(sizeof(struct ieee80211_regdomain_channel), + GFP_KERNEL); + if(reg_chan == NULL) { + r = -ENOMEM; + goto free_g_channels; + } + memset(reg_chan, 0, + sizeof(struct ieee80211_regdomain_channel)); + setup_reg_channel(reg_chan, + IEEE80211_BAND_24GHZ, chan); + list_add_tail(®_chan->list, &sbg->channel_list); + } + + /* Alloc/setup/add a channels */ + for(chan = channel_min_a; chan <= channel_max_a; + chan = (chan == 64) ? 149 : chan+4) { + struct ieee80211_regdomain_channel *reg_chan; + reg_chan = kmalloc(sizeof(struct ieee80211_regdomain_channel), + GFP_KERNEL); + if(reg_chan == NULL) { + r = -ENOMEM; + goto free_a_channels; + } + memset(reg_chan, 0, + sizeof(struct ieee80211_regdomain_channel)); + setup_reg_channel(reg_chan, + IEEE80211_BAND_5GHZ, chan); + list_add_tail(®_chan->list, &sba->channel_list); + } + + /* Hook regdomain, bands, subbands all together */ + list_add_tail(&b24r->list, ®->band_restrictions_list); + list_add_tail(&b5r->list, ®->band_restrictions_list); + list_add_tail(&sbg->list, &b24r->subband_restrictions_list); + list_add_tail(&sba->list, &b5r->subband_restrictions_list); + + /* Finally, add this reg to list of regdomains */ + write_lock(®domain_rwlock); + list_add_tail(®->list, &ieee80211_regdomains_list); + write_unlock(®domain_rwlock); + check_reg_addition(r, reg_name); + return r; + +free_a_channels: + free_subband_channel_list(&sba->channel_list); +free_g_channels: + free_subband_channel_list(&sbg->channel_list); + + list_del(&sbg->channel_list); + list_del(&sba->channel_list); + list_del(&b24r->subband_restrictions_list); + list_del(&b5r->subband_restrictions_list); + list_del(®->band_restrictions_list); +exit: + check_reg_addition(r, reg_name); + return r; +} + +/* old: add_regdomain_defaults */ +static inline int check_reg_addition(int r, char *reg_name) +{ + printk("%s: regulatory domain %18s - ", DRV_NAME, reg_name); + switch(r) { + case -ENOMEM: + printk("Unable to allocate memory\n"); + break; + case -EINVAL: + printk("reg_name is invalid\n"); + break; + case 1: + printk("reg_id already in use\n"); + r = -EINVAL; + break; + case 2: + printk("reg_name already in use\n"); + r = -EINVAL; + break; + case 3: + printk("reg_id and reg_name already in use\n"); + r = -EINVAL; + break; + case 0: + printk("registered\n"); + break; + default: + printk("Unexpected error detected\n"); + r = -EINVAL; + break; + } + return r; +} + +void print_regdomain(struct ieee80211_regdomain *reg) +{ + struct ieee80211_band_restrictions *b; + struct ieee80211_subband_restrictions *sb; + struct ieee80211_regdomain_channel *reg_chan; + + if (reg == NULL) { + printk("Invalid regulatory domain encountered\n"); + return; + } + + printk("Regulatory Domain:\t%s\tRegulatory Domain ID:\t0x%02x\n", + reg->regdomain_name, + reg->regdomain_id); + + /* TODO: ieee80211_ioctl.c's ieee80211_ioctl_giwrange() has similar + * switch, consolidate */ + list_for_each_entry(b, ®->band_restrictions_list, list) { + list_for_each_entry(sb, &b->subband_restrictions_list, list){ + switch(sb->mode) { + case MODE_IEEE80211G: + if (sb->num_channels <= 0) { + printk("\t802.11bg not supported\n"); + continue; + } + printk("\t802.11bg"); + break; + case MODE_IEEE80211A: + if (sb->num_channels <= 0) { + printk("\t802.11a not supported\n"); + continue; + } + printk("\t802.11a"); + break; + default: + printk("\t802.11 mode (%d) not supported\n", + sb->mode); + continue; + } + printk(" (num_channels:%d)(min_chan:%d)(max_chan:%d)" + "(max_eirp_indoor:%d)" + "(max_eirp_outdoor:%d)\n", + sb->num_channels, sb->min_chan, + sb->max_chan, sb->max_eirp_indoor, + sb->max_eirp_outdoor); + printk("\t\tChannel\tFreq\t\n"); + + list_for_each_entry(reg_chan, &sb->channel_list, list){ + struct ieee80211_channel *chan = ®_chan->channel; + printk("\t\t%d\t%d\t\n", chan->chan, chan->freq); + } + } + } + printk("\n"); +} + +#ifdef CONFIG_D80211_DEBUG +void print_regdomains(void) +{ + struct ieee80211_regdomain *reg; + read_lock(®domain_rwlock); + list_for_each_entry(reg, &ieee80211_regdomains_list, list) { + print_regdomain(reg); + } + read_unlock(®domain_rwlock); +} + +void print_iso3166_reg_map(void) +{ + struct ieee80211_iso3166_reg_map *map; + char regdomain_name[REGNAMSIZ]; + printk("ISO3166 <--> regulatory domain map:\n"); + printk("\tCTRY\t-->\tRegdomain\n"); + read_lock(&iso3166_reg_rwlock); + list_for_each_entry(map, &iso3166_reg_map_list, list) { + if(get_ieee80211_regname(map->regdomain_id, regdomain_name)) + printk("\t%s\t-->\t0x%02x (reg_id not registered in " + "regulatory db)\n", + map->alpha3, map->regdomain_id); + else + printk("\t%s\t-->\t%s\n", map->alpha3, regdomain_name); + } + read_unlock(&iso3166_reg_rwlock); +} +void print_hwreg_reg_map(void) +{ + struct ieee80211_regulatory_map *regmap; + struct ieee80211_hwreg_map *hwreg_map; + printk("Hardware regulatory domain map\n"); + read_lock(®_map_rwlock); + list_for_each_entry(regmap, &ieee80211_reg_map_list, list) { + printk("Regulatory map: %s\n", regmap->hwreg_reg_mapname); + printk("\t\tDevice\t-->\tStack\n"); + list_for_each_entry(hwreg_map, ®map->hwreg_map_list, list) { + printk("\t\t%04x\t-->\t%04x\n", + hwreg_map->regdomain_id, + hwreg_map->regdomain_id_hw); + } + } + read_unlock(®_map_rwlock); +} +#else +void print_regdomains(void) +{ + return; +} +void print_iso3166_reg_map(void) +{ + return; +} +void print_hwreg_reg_map(void) +{ + return; +} +#endif + +/* XXX: add this functionality using nl80211 */ +#ifdef CONFIG_D80211_REGULATORY_USER +static int load_user_regdomains(void) +{ + return -ENOTSUPP; +} +#else +static int load_user_regdomains(void) +{ + return 0; +} +#endif + +int get_ieee80211_regname(u32 regdomain_id, char *regdomain_name) +{ + struct ieee80211_regdomain *reg; + read_lock(®domain_rwlock); + list_for_each_entry(reg, &ieee80211_regdomains_list, list) { + if(regdomain_id == reg->regdomain_id) { + strcpy(regdomain_name, reg->regdomain_name); + read_unlock(®domain_rwlock); + return 0; + } + } + read_unlock(®domain_rwlock); + return -ENOTSUPP; +} + +static int load_regdomain_defaults(void) +{ + int r = 0; + /* "World" complies with most regulatory domains */ + r = add_regdomain_world(); + r |= add_regdomain_fcc(); + r |= add_regdomain_mkk(); + /* Revise these and move them to their own routine, right now we are + * adding them with only one subband, assuming default power + * restrictions */ +#ifdef D80211_REGDOMAIN_ETSI + r |= add_regdomain_bga(REGDOMAIN_ETSI, "ETSI", 1, 13, 36, 165); +#endif + //r |= add_regdomain_bga(REGDOMAIN_FRANCE, "France", 11, 13, 36, 165); + r |= add_regdomain_bga(REGDOMAIN_IC, "IC", 3, 11, 36, 165); + //r |= add_regdomain_bga(REGDOMAIN_SPAIN, "Spain", 10, 11, 36, 165); + + return r; +} + +static int update_ieee80211_regdomains(void) +{ + int r; + INIT_LIST_HEAD(&ieee80211_regdomains_list); + r = load_regdomain_defaults(); + if(r) { + printk("error while trying to register " + "built-in regdomains\n"); + return r; + } + r = load_user_regdomains(); + return r; +} + +static inline void setup_iso3166_reg_map(struct ieee80211_iso3166_reg_map *map, + char *alpha3, u32 regdomain_id) +{ + strcpy(map->alpha3, alpha3); + map->regdomain_id = regdomain_id; +} + +inline int iso3166_to_reg_exists(char *alpha3) +{ + struct ieee80211_iso3166_reg_map *map; + read_lock(&iso3166_reg_rwlock); + list_for_each_entry(map, &iso3166_reg_map_list, list) + if(strcmp(map->alpha3, alpha3)==0) { + read_unlock(&iso3166_reg_rwlock); + return 1; + } + read_unlock(&iso3166_reg_rwlock); + return 0; +} + +int iso3166_to_reg(char *alpha3, u32 regdomain_id) +{ + struct ieee80211_iso3166_reg_map *map; + read_lock(&iso3166_reg_rwlock); + list_for_each_entry(map, &iso3166_reg_map_list, list) + if(strcmp(map->alpha3, alpha3)==0) { + regdomain_id = map->regdomain_id; + read_unlock(&iso3166_reg_rwlock); + return 1; + } + read_unlock(&iso3166_reg_rwlock); + return 0; +} + +int alpha3_valid(char *alpha3) { + if(strlen(alpha3) == ISOCOUNTRYSIZ3) + return 1; + return 0; +} + +int add_iso3166_reg_map(char *alpha3, u32 regdomain_id) +{ + int r = 0; + struct ieee80211_iso3166_reg_map *map; + write_lock(&iso3166_reg_rwlock); + /* If not valid or, iso map does not exist or if already already present */ + if(!alpha3_valid(alpha3) || !iso3166_1_exists(alpha3) || + iso3166_to_reg_exists(alpha3)){ + r = -EPERM; + goto unlock_and_exit; + } + map = kmalloc(sizeof(struct ieee80211_iso3166_reg_map), GFP_KERNEL); + if(map == NULL) { + r = -ENOMEM; + goto unlock_and_exit; + } + setup_iso3166_reg_map(map, alpha3, regdomain_id); + list_add_tail(&map->list, &iso3166_reg_map_list); +unlock_and_exit: + write_unlock(&iso3166_reg_rwlock); + return r; +} + +static inline int load_iso3166_regmap_fcc(void) +{ + int r = 0; + r |= add_iso3166_reg_map("USA", REGDOMAIN_FCC); + r |= add_iso3166_reg_map("COL", REGDOMAIN_FCC); + r |= add_iso3166_reg_map("DOM", REGDOMAIN_FCC); + r |= add_iso3166_reg_map("GTM", REGDOMAIN_FCC); + r |= add_iso3166_reg_map("MEX", REGDOMAIN_FCC); + r |= add_iso3166_reg_map("PAN", REGDOMAIN_FCC); + r |= add_iso3166_reg_map("PRI", REGDOMAIN_FCC); + return r; +} + +static inline int load_iso3166_regmap_etsi(void) +{ + int r = 0; + r |= add_iso3166_reg_map("DNK", REGDOMAIN_ETSI); + r |= add_iso3166_reg_map("EST", REGDOMAIN_ETSI); + r |= add_iso3166_reg_map("FIN", REGDOMAIN_ETSI); + r |= add_iso3166_reg_map("DEU", REGDOMAIN_ETSI); + r |= add_iso3166_reg_map("ISL", REGDOMAIN_ETSI); + r |= add_iso3166_reg_map("IRL", REGDOMAIN_ETSI); + r |= add_iso3166_reg_map("ITA", REGDOMAIN_ETSI); + r |= add_iso3166_reg_map("LTU", REGDOMAIN_ETSI); + r |= add_iso3166_reg_map("LUX", REGDOMAIN_ETSI); + r |= add_iso3166_reg_map("NLD", REGDOMAIN_ETSI); + r |= add_iso3166_reg_map("NOR", REGDOMAIN_ETSI); + r |= add_iso3166_reg_map("POL", REGDOMAIN_ETSI); + r |= add_iso3166_reg_map("PRT", REGDOMAIN_ETSI); + r |= add_iso3166_reg_map("SVN", REGDOMAIN_ETSI); + r |= add_iso3166_reg_map("ZAF", REGDOMAIN_ETSI); + r |= add_iso3166_reg_map("SWE", REGDOMAIN_ETSI); + r |= add_iso3166_reg_map("GBR", REGDOMAIN_ETSI); + r |= add_iso3166_reg_map("FRA", REGDOMAIN_ETSI); /* France goes here? */ + r |= add_iso3166_reg_map("ESP", REGDOMAIN_ETSI); /* Spain goes here? */ + return r; +} + +static inline int load_iso3166_regmap_misc(void) +{ + int r = 0; + r |= add_iso3166_reg_map("CAN", REGDOMAIN_IC); + r |= add_iso3166_reg_map("JPN", REGDOMAIN_MKK); + //r |= add_iso3166_reg_map("ESP", REGDOMAIN_SPAIN); /* or here? */ + return r; +} + +static int load_iso3166_reg_defaults(void) +{ + int r = 0; + r |= load_iso3166_regmap_fcc(); + r |= load_iso3166_regmap_etsi(); + r |= load_iso3166_regmap_misc(); + return r; +} + +static int update_iso3166_reg_map_list(void) +{ + int r; + INIT_LIST_HEAD(&iso3166_reg_map_list); + r = load_iso3166_reg_defaults(); + if(r) + printk("error while trying to load iso3166-1 alpha3 country " + "code -> stack regdomain map\n"); + return r; +} + +static inline void setup_hwreg_map(struct ieee80211_hwreg_map *hwreg_map, + u32 regdomain_id, u32 regdomain_id_hw) +{ + hwreg_map->regdomain_id = regdomain_id; + hwreg_map->regdomain_id_hw = regdomain_id_hw; +} + +static int add_hwreg_reg_map(struct ieee80211_regulatory_map *reg_map, + u32 regdomain_id, u32 regdomain_id_hw) +{ + struct ieee80211_hwreg_map *hwreg_map; + hwreg_map = kmalloc(sizeof(struct ieee80211_hwreg_map), GFP_KERNEL); + if (hwreg_map == NULL) + return -ENOMEM; + setup_hwreg_map(hwreg_map, regdomain_id, regdomain_id_hw); + list_add_tail(&hwreg_map->list, ®_map->hwreg_map_list); + return 0; +} + +static inline int load_atheros_regulatory_map(void) +{ + int r = 0; + struct ieee80211_regulatory_map *reg_map; + reg_map = kmalloc(sizeof(struct ieee80211_regulatory_map), GFP_KERNEL); + if (reg_map == NULL) + return -ENOMEM; + + strcpy(reg_map->hwreg_reg_mapname, "Atheros"); + INIT_LIST_HEAD(®_map->hwreg_map_list); + r |= add_hwreg_reg_map(reg_map, REGDOMAIN_WORLD, REGDOMAIN_WORLD); + r |= add_hwreg_reg_map(reg_map, REGDOMAIN_FCC, REGDOMAIN_FCC); + r |= add_hwreg_reg_map(reg_map, REGDOMAIN_IC, REGDOMAIN_IC); + r |= add_hwreg_reg_map(reg_map, REGDOMAIN_ETSI, REGDOMAIN_ETSI); + // r |= add_hwreg_reg_map(reg_map, REGDOMAIN_SPAIN, REGDOMAIN_SPAIN); + // r |= add_hwreg_reg_map(reg_map, REGDOMAIN_FRANCE, REGDOMAIN_FRANCE); + r |= add_hwreg_reg_map(reg_map, REGDOMAIN_MKK, REGDOMAIN_MKK); + + write_lock(®_map_rwlock); + list_add_tail(®_map->list, &ieee80211_reg_map_list); + write_unlock(®_map_rwlock); + return r; +} + +static int load_hwreg_reg_maps_defaults(void) { + int r = 0; + r |= load_atheros_regulatory_map(); + return r; +} + +static int update_hwreg_reg_maps_list(void) +{ + int r; + INIT_LIST_HEAD(&ieee80211_reg_map_list); + r = load_hwreg_reg_maps_defaults(); + if(r) + printk("error while trying to load hardware regulatory -> " + "stack regulatory domain map\n"); + return r; +} + +static int regdomains_init(void) +{ + int r; + printk("%s: %s v%s loaded\n", DRV_NAME, DRV_DESCRIPTION, DRV_VERSION); + + /* Update regdomain list */ + r = update_ieee80211_regdomains(); + if(r) + goto free_regs; + print_regdomains(); + + /* Load iso->reg map */ + r = update_iso3166_reg_map_list(); + if(r) + goto free_iso_reg_map_list; + print_iso3166_reg_map(); + + /* Load hwreg->reg maps */ + r = update_hwreg_reg_maps_list(); + if(r) + goto free_hwreg_reg_maps_list; + print_hwreg_reg_map(); + + return r; +free_hwreg_reg_maps_list: + free_hwreg_reg_maps(); /* TODO */ +free_iso_reg_map_list: + free_iso3166_reg_map_list(); +free_regs: + free_regdomains(); + return r; +} + +static void regdomains_exit(void) +{ + /* Regdomains */ + write_lock(®domain_rwlock); + free_regdomains(); + list_del(&ieee80211_regdomains_list); + write_unlock(®domain_rwlock); + + /* Iso->reg map */ + write_lock(&iso3166_reg_rwlock); + free_iso3166_reg_map_list(); + list_del(&iso3166_reg_map_list); + write_unlock(&iso3166_reg_rwlock); + printk("%s: unloaded\n", DRV_NAME); +} + +/* TODO: add get_reg_channels(regdomain_id, mode) returns array of channels */ +module_init(regdomains_init); +module_exit(regdomains_exit); +EXPORT_SYMBOL(print_regdomain); +#ifdef CONFIG_D80211_DEBUG +EXPORT_SYMBOL(print_regdomains); +EXPORT_SYMBOL(print_iso3166_reg_map); +EXPORT_SYMBOL(print_hwreg_reg_map); +#endif +EXPORT_SYMBOL(get_ieee80211_regname); + +EXPORT_SYMBOL(ieee80211_regdomains_list); +EXPORT_SYMBOL(iso3166_reg_map_list); +EXPORT_SYMBOL(ieee80211_reg_map_list); + +EXPORT_SYMBOL(regdomain_rwlock); +EXPORT_SYMBOL(iso3166_reg_rwlock); +EXPORT_SYMBOL(reg_map_rwlock); + +EXPORT_SYMBOL(alpha3_valid); /* Move to iso module */ +EXPORT_SYMBOL(iso3166_to_reg_exists); +EXPORT_SYMBOL(iso3166_to_reg); +EXPORT_SYMBOL(add_iso3166_reg_map);