diff options
author | Liu Hong <hong.liu@intel.com> | 2005-08-19 10:33:10 -0400 |
---|---|---|
committer | James Ketrenos <jketreno@linux.intel.com> | 2005-11-07 18:51:03 -0500 |
commit | 1fe0adb4314009362d28205bbf09f6d758e82002 (patch) | |
tree | 63809744601a0f97ed8499c72377ab49d78a78ab /drivers | |
parent | f6c5cb7c6f8a85045996bfc3442af963d6578d60 (diff) |
Migrated some of the channel verification code back into the driver to
keep regulatory consistency in one location.
Signed-off-by: James Ketrenos
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/net/wireless/ipw2200.c | 173 |
1 files changed, 152 insertions, 21 deletions
diff --git a/drivers/net/wireless/ipw2200.c b/drivers/net/wireless/ipw2200.c index bcb5993b68bd..40759e5f3cc5 100644 --- a/drivers/net/wireless/ipw2200.c +++ b/drivers/net/wireless/ipw2200.c | |||
@@ -149,6 +149,12 @@ static int init_supported_rates(struct ipw_priv *priv, | |||
149 | static void ipw_set_hwcrypto_keys(struct ipw_priv *); | 149 | static void ipw_set_hwcrypto_keys(struct ipw_priv *); |
150 | static void ipw_send_wep_keys(struct ipw_priv *, int); | 150 | static void ipw_send_wep_keys(struct ipw_priv *, int); |
151 | 151 | ||
152 | static int ipw_is_valid_channel(struct ieee80211_device *, u8); | ||
153 | static int ipw_channel_to_index(struct ieee80211_device *, u8); | ||
154 | static u8 ipw_freq_to_channel(struct ieee80211_device *, u32); | ||
155 | static int ipw_set_geo(struct ieee80211_device *, const struct ieee80211_geo *); | ||
156 | static const struct ieee80211_geo *ipw_get_geo(struct ieee80211_device *); | ||
157 | |||
152 | static int snprint_line(char *buf, size_t count, | 158 | static int snprint_line(char *buf, size_t count, |
153 | const u8 * data, u32 len, u32 ofs) | 159 | const u8 * data, u32 len, u32 ofs) |
154 | { | 160 | { |
@@ -1596,7 +1602,7 @@ static ssize_t store_speed_scan(struct device *d, struct device_attribute *attr, | |||
1596 | break; | 1602 | break; |
1597 | } | 1603 | } |
1598 | 1604 | ||
1599 | if (ieee80211_is_valid_channel(priv->ieee, channel)) | 1605 | if (ipw_is_valid_channel(priv->ieee, channel)) |
1600 | priv->speed_scan[pos++] = channel; | 1606 | priv->speed_scan[pos++] = channel; |
1601 | else | 1607 | else |
1602 | IPW_WARNING("Skipping invalid channel request: %d\n", | 1608 | IPW_WARNING("Skipping invalid channel request: %d\n", |
@@ -2194,7 +2200,7 @@ static int ipw_send_tx_power(struct ipw_priv *priv, struct ipw_tx_power *power) | |||
2194 | 2200 | ||
2195 | static int ipw_set_tx_power(struct ipw_priv *priv) | 2201 | static int ipw_set_tx_power(struct ipw_priv *priv) |
2196 | { | 2202 | { |
2197 | const struct ieee80211_geo *geo = ieee80211_get_geo(priv->ieee); | 2203 | const struct ieee80211_geo *geo = ipw_get_geo(priv->ieee); |
2198 | struct ipw_tx_power tx_power; | 2204 | struct ipw_tx_power tx_power; |
2199 | s8 max_power; | 2205 | s8 max_power; |
2200 | int i; | 2206 | int i; |
@@ -5503,6 +5509,15 @@ static int ipw_best_network(struct ipw_priv *priv, | |||
5503 | return 0; | 5509 | return 0; |
5504 | } | 5510 | } |
5505 | 5511 | ||
5512 | /* Filter out invalid channel in current GEO */ | ||
5513 | if (!ipw_is_valid_channel(priv->ieee, network->channel)) { | ||
5514 | IPW_DEBUG_ASSOC("Network '%s (" MAC_FMT ")' excluded " | ||
5515 | "because of invalid channel in current GEO\n", | ||
5516 | escape_essid(network->ssid, network->ssid_len), | ||
5517 | MAC_ARG(network->bssid)); | ||
5518 | return 0; | ||
5519 | } | ||
5520 | |||
5506 | /* Ensure that the rates supported by the driver are compatible with | 5521 | /* Ensure that the rates supported by the driver are compatible with |
5507 | * this AP, including verification of basic rates (mandatory) */ | 5522 | * this AP, including verification of basic rates (mandatory) */ |
5508 | if (!ipw_compatible_rates(priv, network, &rates)) { | 5523 | if (!ipw_compatible_rates(priv, network, &rates)) { |
@@ -5540,7 +5555,7 @@ static int ipw_best_network(struct ipw_priv *priv, | |||
5540 | static void ipw_adhoc_create(struct ipw_priv *priv, | 5555 | static void ipw_adhoc_create(struct ipw_priv *priv, |
5541 | struct ieee80211_network *network) | 5556 | struct ieee80211_network *network) |
5542 | { | 5557 | { |
5543 | const struct ieee80211_geo *geo = ieee80211_get_geo(priv->ieee); | 5558 | const struct ieee80211_geo *geo = ipw_get_geo(priv->ieee); |
5544 | int i; | 5559 | int i; |
5545 | 5560 | ||
5546 | /* | 5561 | /* |
@@ -5555,10 +5570,10 @@ static void ipw_adhoc_create(struct ipw_priv *priv, | |||
5555 | * FW fatal error. | 5570 | * FW fatal error. |
5556 | * | 5571 | * |
5557 | */ | 5572 | */ |
5558 | switch (ieee80211_is_valid_channel(priv->ieee, priv->channel)) { | 5573 | switch (ipw_is_valid_channel(priv->ieee, priv->channel)) { |
5559 | case IEEE80211_52GHZ_BAND: | 5574 | case IEEE80211_52GHZ_BAND: |
5560 | network->mode = IEEE_A; | 5575 | network->mode = IEEE_A; |
5561 | i = ieee80211_channel_to_index(priv->ieee, priv->channel); | 5576 | i = ipw_channel_to_index(priv->ieee, priv->channel); |
5562 | if (i == -1) | 5577 | if (i == -1) |
5563 | BUG(); | 5578 | BUG(); |
5564 | if (geo->a[i].flags & IEEE80211_CH_PASSIVE_ONLY) { | 5579 | if (geo->a[i].flags & IEEE80211_CH_PASSIVE_ONLY) { |
@@ -5572,6 +5587,13 @@ static void ipw_adhoc_create(struct ipw_priv *priv, | |||
5572 | network->mode = IEEE_G; | 5587 | network->mode = IEEE_G; |
5573 | else | 5588 | else |
5574 | network->mode = IEEE_B; | 5589 | network->mode = IEEE_B; |
5590 | i = ipw_channel_to_index(priv->ieee, priv->channel); | ||
5591 | if (i == -1) | ||
5592 | BUG(); | ||
5593 | if (geo->bg[i].flags & IEEE80211_CH_PASSIVE_ONLY) { | ||
5594 | IPW_WARNING("Overriding invalid channel\n"); | ||
5595 | priv->channel = geo->bg[0].channel; | ||
5596 | } | ||
5575 | break; | 5597 | break; |
5576 | 5598 | ||
5577 | default: | 5599 | default: |
@@ -5899,7 +5921,7 @@ static void ipw_add_scan_channels(struct ipw_priv *priv, | |||
5899 | const struct ieee80211_geo *geo; | 5921 | const struct ieee80211_geo *geo; |
5900 | int i; | 5922 | int i; |
5901 | 5923 | ||
5902 | geo = ieee80211_get_geo(priv->ieee); | 5924 | geo = ipw_get_geo(priv->ieee); |
5903 | 5925 | ||
5904 | if (priv->ieee->freq_band & IEEE80211_52GHZ_BAND) { | 5926 | if (priv->ieee->freq_band & IEEE80211_52GHZ_BAND) { |
5905 | int start = channel_index; | 5927 | int start = channel_index; |
@@ -5909,7 +5931,11 @@ static void ipw_add_scan_channels(struct ipw_priv *priv, | |||
5909 | continue; | 5931 | continue; |
5910 | channel_index++; | 5932 | channel_index++; |
5911 | scan->channels_list[channel_index] = geo->a[i].channel; | 5933 | scan->channels_list[channel_index] = geo->a[i].channel; |
5912 | ipw_set_scan_type(scan, channel_index, scan_type); | 5934 | ipw_set_scan_type(scan, channel_index, |
5935 | geo->a[i]. | ||
5936 | flags & IEEE80211_CH_PASSIVE_ONLY ? | ||
5937 | IPW_SCAN_PASSIVE_FULL_DWELL_SCAN : | ||
5938 | scan_type); | ||
5913 | } | 5939 | } |
5914 | 5940 | ||
5915 | if (start != channel_index) { | 5941 | if (start != channel_index) { |
@@ -5922,6 +5948,7 @@ static void ipw_add_scan_channels(struct ipw_priv *priv, | |||
5922 | if (priv->ieee->freq_band & IEEE80211_24GHZ_BAND) { | 5948 | if (priv->ieee->freq_band & IEEE80211_24GHZ_BAND) { |
5923 | int start = channel_index; | 5949 | int start = channel_index; |
5924 | if (priv->config & CFG_SPEED_SCAN) { | 5950 | if (priv->config & CFG_SPEED_SCAN) { |
5951 | int index; | ||
5925 | u8 channels[IEEE80211_24GHZ_CHANNELS] = { | 5952 | u8 channels[IEEE80211_24GHZ_CHANNELS] = { |
5926 | /* nop out the list */ | 5953 | /* nop out the list */ |
5927 | [0] = 0 | 5954 | [0] = 0 |
@@ -5953,8 +5980,14 @@ static void ipw_add_scan_channels(struct ipw_priv *priv, | |||
5953 | priv->speed_scan_pos++; | 5980 | priv->speed_scan_pos++; |
5954 | channel_index++; | 5981 | channel_index++; |
5955 | scan->channels_list[channel_index] = channel; | 5982 | scan->channels_list[channel_index] = channel; |
5983 | index = | ||
5984 | ipw_channel_to_index(priv->ieee, channel); | ||
5956 | ipw_set_scan_type(scan, channel_index, | 5985 | ipw_set_scan_type(scan, channel_index, |
5957 | scan_type); | 5986 | geo->bg[index]. |
5987 | flags & | ||
5988 | IEEE80211_CH_PASSIVE_ONLY ? | ||
5989 | IPW_SCAN_PASSIVE_FULL_DWELL_SCAN | ||
5990 | : scan_type); | ||
5958 | } | 5991 | } |
5959 | } else { | 5992 | } else { |
5960 | for (i = 0; i < geo->bg_channels; i++) { | 5993 | for (i = 0; i < geo->bg_channels; i++) { |
@@ -5965,7 +5998,11 @@ static void ipw_add_scan_channels(struct ipw_priv *priv, | |||
5965 | scan->channels_list[channel_index] = | 5998 | scan->channels_list[channel_index] = |
5966 | geo->bg[i].channel; | 5999 | geo->bg[i].channel; |
5967 | ipw_set_scan_type(scan, channel_index, | 6000 | ipw_set_scan_type(scan, channel_index, |
5968 | scan_type); | 6001 | geo->bg[i]. |
6002 | flags & | ||
6003 | IEEE80211_CH_PASSIVE_ONLY ? | ||
6004 | IPW_SCAN_PASSIVE_FULL_DWELL_SCAN | ||
6005 | : scan_type); | ||
5969 | } | 6006 | } |
5970 | } | 6007 | } |
5971 | 6008 | ||
@@ -6017,7 +6054,7 @@ static int ipw_request_scan(struct ipw_priv *priv) | |||
6017 | 6054 | ||
6018 | scan.dwell_time[IPW_SCAN_ACTIVE_BROADCAST_AND_DIRECT_SCAN] = | 6055 | scan.dwell_time[IPW_SCAN_ACTIVE_BROADCAST_AND_DIRECT_SCAN] = |
6019 | cpu_to_le16(20); | 6056 | cpu_to_le16(20); |
6020 | scan.dwell_time[IPW_SCAN_PASSIVE_FULL_DWELL_SCAN] = cpu_to_le16(20); | 6057 | scan.dwell_time[IPW_SCAN_PASSIVE_FULL_DWELL_SCAN] = cpu_to_le16(120); |
6021 | 6058 | ||
6022 | scan.full_scan_index = cpu_to_le32(ieee80211_get_scans(priv->ieee)); | 6059 | scan.full_scan_index = cpu_to_le32(ieee80211_get_scans(priv->ieee)); |
6023 | 6060 | ||
@@ -6026,7 +6063,7 @@ static int ipw_request_scan(struct ipw_priv *priv) | |||
6026 | u8 channel; | 6063 | u8 channel; |
6027 | u8 band = 0; | 6064 | u8 band = 0; |
6028 | 6065 | ||
6029 | switch (ieee80211_is_valid_channel(priv->ieee, priv->channel)) { | 6066 | switch (ipw_is_valid_channel(priv->ieee, priv->channel)) { |
6030 | case IEEE80211_52GHZ_BAND: | 6067 | case IEEE80211_52GHZ_BAND: |
6031 | band = (u8) (IPW_A_MODE << 6) | 1; | 6068 | band = (u8) (IPW_A_MODE << 6) | 1; |
6032 | channel = priv->channel; | 6069 | channel = priv->channel; |
@@ -8401,10 +8438,11 @@ static int ipw_wx_set_freq(struct net_device *dev, | |||
8401 | union iwreq_data *wrqu, char *extra) | 8438 | union iwreq_data *wrqu, char *extra) |
8402 | { | 8439 | { |
8403 | struct ipw_priv *priv = ieee80211_priv(dev); | 8440 | struct ipw_priv *priv = ieee80211_priv(dev); |
8404 | const struct ieee80211_geo *geo = ieee80211_get_geo(priv->ieee); | 8441 | const struct ieee80211_geo *geo = ipw_get_geo(priv->ieee); |
8405 | struct iw_freq *fwrq = &wrqu->freq; | 8442 | struct iw_freq *fwrq = &wrqu->freq; |
8406 | int ret = 0, i; | 8443 | int ret = 0, i; |
8407 | u8 channel; | 8444 | u8 channel, flags; |
8445 | int band; | ||
8408 | 8446 | ||
8409 | if (fwrq->m == 0) { | 8447 | if (fwrq->m == 0) { |
8410 | IPW_DEBUG_WX("SET Freq/Channel -> any\n"); | 8448 | IPW_DEBUG_WX("SET Freq/Channel -> any\n"); |
@@ -8415,20 +8453,23 @@ static int ipw_wx_set_freq(struct net_device *dev, | |||
8415 | } | 8453 | } |
8416 | /* if setting by freq convert to channel */ | 8454 | /* if setting by freq convert to channel */ |
8417 | if (fwrq->e == 1) { | 8455 | if (fwrq->e == 1) { |
8418 | channel = ieee80211_freq_to_channel(priv->ieee, fwrq->m); | 8456 | channel = ipw_freq_to_channel(priv->ieee, fwrq->m); |
8419 | if (channel == 0) | 8457 | if (channel == 0) |
8420 | return -EINVAL; | 8458 | return -EINVAL; |
8421 | } else | 8459 | } else |
8422 | channel = fwrq->m; | 8460 | channel = fwrq->m; |
8423 | 8461 | ||
8424 | if (!ieee80211_is_valid_channel(priv->ieee, channel)) | 8462 | if (!(band = ipw_is_valid_channel(priv->ieee, channel))) |
8425 | return -EINVAL; | 8463 | return -EINVAL; |
8426 | 8464 | ||
8427 | if (priv->ieee->iw_mode == IW_MODE_ADHOC && priv->ieee->mode & IEEE_A) { | 8465 | if (priv->ieee->iw_mode == IW_MODE_ADHOC) { |
8428 | i = ieee80211_channel_to_index(priv->ieee, channel); | 8466 | i = ipw_channel_to_index(priv->ieee, channel); |
8429 | if (i == -1) | 8467 | if (i == -1) |
8430 | return -EINVAL; | 8468 | return -EINVAL; |
8431 | if (geo->a[i].flags & IEEE80211_CH_PASSIVE_ONLY) { | 8469 | |
8470 | flags = (band == IEEE80211_24GHZ_BAND) ? | ||
8471 | geo->bg[i].flags : geo->a[i].flags; | ||
8472 | if (flags & IEEE80211_CH_PASSIVE_ONLY) { | ||
8432 | IPW_DEBUG_WX("Invalid Ad-Hoc channel for 802.11a\n"); | 8473 | IPW_DEBUG_WX("Invalid Ad-Hoc channel for 802.11a\n"); |
8433 | return -EINVAL; | 8474 | return -EINVAL; |
8434 | } | 8475 | } |
@@ -8546,7 +8587,7 @@ static int ipw_wx_get_range(struct net_device *dev, | |||
8546 | { | 8587 | { |
8547 | struct ipw_priv *priv = ieee80211_priv(dev); | 8588 | struct ipw_priv *priv = ieee80211_priv(dev); |
8548 | struct iw_range *range = (struct iw_range *)extra; | 8589 | struct iw_range *range = (struct iw_range *)extra; |
8549 | const struct ieee80211_geo *geo = ieee80211_get_geo(priv->ieee); | 8590 | const struct ieee80211_geo *geo = ipw_get_geo(priv->ieee); |
8550 | int i = 0, j; | 8591 | int i = 0, j; |
8551 | 8592 | ||
8552 | wrqu->data.length = sizeof(*range); | 8593 | wrqu->data.length = sizeof(*range); |
@@ -9147,7 +9188,7 @@ static int ipw_request_direct_scan(struct ipw_priv *priv, char *essid, | |||
9147 | 9188 | ||
9148 | scan.dwell_time[IPW_SCAN_ACTIVE_BROADCAST_AND_DIRECT_SCAN] = | 9189 | scan.dwell_time[IPW_SCAN_ACTIVE_BROADCAST_AND_DIRECT_SCAN] = |
9149 | cpu_to_le16(20); | 9190 | cpu_to_le16(20); |
9150 | scan.dwell_time[IPW_SCAN_PASSIVE_FULL_DWELL_SCAN] = cpu_to_le16(20); | 9191 | scan.dwell_time[IPW_SCAN_PASSIVE_FULL_DWELL_SCAN] = cpu_to_le16(120); |
9151 | scan.dwell_time[IPW_SCAN_ACTIVE_DIRECT_SCAN] = cpu_to_le16(20); | 9192 | scan.dwell_time[IPW_SCAN_ACTIVE_DIRECT_SCAN] = cpu_to_le16(20); |
9152 | 9193 | ||
9153 | scan.full_scan_index = cpu_to_le32(ieee80211_get_scans(priv->ieee)); | 9194 | scan.full_scan_index = cpu_to_le32(ieee80211_get_scans(priv->ieee)); |
@@ -10775,6 +10816,96 @@ static const struct ieee80211_geo ipw_geos[] = { | |||
10775 | } | 10816 | } |
10776 | }; | 10817 | }; |
10777 | 10818 | ||
10819 | /* GEO code borrowed from ieee80211_geo.c */ | ||
10820 | static int ipw_is_valid_channel(struct ieee80211_device *ieee, u8 channel) | ||
10821 | { | ||
10822 | int i; | ||
10823 | |||
10824 | /* Driver needs to initialize the geography map before using | ||
10825 | * these helper functions */ | ||
10826 | BUG_ON(ieee->geo.bg_channels == 0 && ieee->geo.a_channels == 0); | ||
10827 | |||
10828 | if (ieee->freq_band & IEEE80211_24GHZ_BAND) | ||
10829 | for (i = 0; i < ieee->geo.bg_channels; i++) | ||
10830 | /* NOTE: If G mode is currently supported but | ||
10831 | * this is a B only channel, we don't see it | ||
10832 | * as valid. */ | ||
10833 | if ((ieee->geo.bg[i].channel == channel) && | ||
10834 | (!(ieee->mode & IEEE_G) || | ||
10835 | !(ieee->geo.bg[i].flags & IEEE80211_CH_B_ONLY))) | ||
10836 | return IEEE80211_24GHZ_BAND; | ||
10837 | |||
10838 | if (ieee->freq_band & IEEE80211_52GHZ_BAND) | ||
10839 | for (i = 0; i < ieee->geo.a_channels; i++) | ||
10840 | if (ieee->geo.a[i].channel == channel) | ||
10841 | return IEEE80211_52GHZ_BAND; | ||
10842 | |||
10843 | return 0; | ||
10844 | } | ||
10845 | |||
10846 | static int ipw_channel_to_index(struct ieee80211_device *ieee, u8 channel) | ||
10847 | { | ||
10848 | int i; | ||
10849 | |||
10850 | /* Driver needs to initialize the geography map before using | ||
10851 | * these helper functions */ | ||
10852 | BUG_ON(ieee->geo.bg_channels == 0 && ieee->geo.a_channels == 0); | ||
10853 | |||
10854 | if (ieee->freq_band & IEEE80211_24GHZ_BAND) | ||
10855 | for (i = 0; i < ieee->geo.bg_channels; i++) | ||
10856 | if (ieee->geo.bg[i].channel == channel) | ||
10857 | return i; | ||
10858 | |||
10859 | if (ieee->freq_band & IEEE80211_52GHZ_BAND) | ||
10860 | for (i = 0; i < ieee->geo.a_channels; i++) | ||
10861 | if (ieee->geo.a[i].channel == channel) | ||
10862 | return i; | ||
10863 | |||
10864 | return -1; | ||
10865 | } | ||
10866 | |||
10867 | static u8 ipw_freq_to_channel(struct ieee80211_device *ieee, u32 freq) | ||
10868 | { | ||
10869 | int i; | ||
10870 | |||
10871 | /* Driver needs to initialize the geography map before using | ||
10872 | * these helper functions */ | ||
10873 | BUG_ON(ieee->geo.bg_channels == 0 && ieee->geo.a_channels == 0); | ||
10874 | |||
10875 | freq /= 100000; | ||
10876 | |||
10877 | if (ieee->freq_band & IEEE80211_24GHZ_BAND) | ||
10878 | for (i = 0; i < ieee->geo.bg_channels; i++) | ||
10879 | if (ieee->geo.bg[i].freq == freq) | ||
10880 | return ieee->geo.bg[i].channel; | ||
10881 | |||
10882 | if (ieee->freq_band & IEEE80211_52GHZ_BAND) | ||
10883 | for (i = 0; i < ieee->geo.a_channels; i++) | ||
10884 | if (ieee->geo.a[i].freq == freq) | ||
10885 | return ieee->geo.a[i].channel; | ||
10886 | |||
10887 | return 0; | ||
10888 | } | ||
10889 | |||
10890 | static int ipw_set_geo(struct ieee80211_device *ieee, | ||
10891 | const struct ieee80211_geo *geo) | ||
10892 | { | ||
10893 | memcpy(ieee->geo.name, geo->name, 3); | ||
10894 | ieee->geo.name[3] = '\0'; | ||
10895 | ieee->geo.bg_channels = geo->bg_channels; | ||
10896 | ieee->geo.a_channels = geo->a_channels; | ||
10897 | memcpy(ieee->geo.bg, geo->bg, geo->bg_channels * | ||
10898 | sizeof(struct ieee80211_channel)); | ||
10899 | memcpy(ieee->geo.a, geo->a, ieee->geo.a_channels * | ||
10900 | sizeof(struct ieee80211_channel)); | ||
10901 | return 0; | ||
10902 | } | ||
10903 | |||
10904 | static const struct ieee80211_geo *ipw_get_geo(struct ieee80211_device *ieee) | ||
10905 | { | ||
10906 | return &ieee->geo; | ||
10907 | } | ||
10908 | |||
10778 | #define MAX_HW_RESTARTS 5 | 10909 | #define MAX_HW_RESTARTS 5 |
10779 | static int ipw_up(struct ipw_priv *priv) | 10910 | static int ipw_up(struct ipw_priv *priv) |
10780 | { | 10911 | { |
@@ -10816,7 +10947,7 @@ static int ipw_up(struct ipw_priv *priv) | |||
10816 | } | 10947 | } |
10817 | if (j == ARRAY_SIZE(ipw_geos)) | 10948 | if (j == ARRAY_SIZE(ipw_geos)) |
10818 | j = 0; | 10949 | j = 0; |
10819 | if (ieee80211_set_geo(priv->ieee, &ipw_geos[j])) { | 10950 | if (ipw_set_geo(priv->ieee, &ipw_geos[j])) { |
10820 | IPW_WARNING("Could not set geography."); | 10951 | IPW_WARNING("Could not set geography."); |
10821 | return 0; | 10952 | return 0; |
10822 | } | 10953 | } |