aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/net/wireless/wl12xx/wl1271.h3
-rw-r--r--drivers/net/wireless/wl12xx/wl1271_acx.c71
-rw-r--r--drivers/net/wireless/wl12xx/wl1271_acx.h56
-rw-r--r--drivers/net/wireless/wl12xx/wl1271_boot.c3
-rw-r--r--drivers/net/wireless/wl12xx/wl1271_conf.h103
-rw-r--r--drivers/net/wireless/wl12xx/wl1271_event.c24
-rw-r--r--drivers/net/wireless/wl12xx/wl1271_event.h8
-rw-r--r--drivers/net/wireless/wl12xx/wl1271_init.c5
-rw-r--r--drivers/net/wireless/wl12xx/wl1271_main.c52
9 files changed, 228 insertions, 97 deletions
diff --git a/drivers/net/wireless/wl12xx/wl1271.h b/drivers/net/wireless/wl12xx/wl1271.h
index 789460074670..75887e74205b 100644
--- a/drivers/net/wireless/wl12xx/wl1271.h
+++ b/drivers/net/wireless/wl12xx/wl1271.h
@@ -473,6 +473,9 @@ struct wl1271 {
473 /* in dBm */ 473 /* in dBm */
474 int power_level; 474 int power_level;
475 475
476 int rssi_thold;
477 int last_rssi_event;
478
476 struct wl1271_stats stats; 479 struct wl1271_stats stats;
477 struct wl1271_debugfs debugfs; 480 struct wl1271_debugfs debugfs;
478 481
diff --git a/drivers/net/wireless/wl12xx/wl1271_acx.c b/drivers/net/wireless/wl12xx/wl1271_acx.c
index 621c94691e7e..1a6b2ec1db58 100644
--- a/drivers/net/wireless/wl12xx/wl1271_acx.c
+++ b/drivers/net/wireless/wl12xx/wl1271_acx.c
@@ -1165,6 +1165,7 @@ out:
1165 kfree(acx); 1165 kfree(acx);
1166 return ret; 1166 return ret;
1167} 1167}
1168
1168int wl1271_acx_keep_alive_config(struct wl1271 *wl, u8 index, u8 tpl_valid) 1169int wl1271_acx_keep_alive_config(struct wl1271 *wl, u8 index, u8 tpl_valid)
1169{ 1170{
1170 struct wl1271_acx_keep_alive_config *acx = NULL; 1171 struct wl1271_acx_keep_alive_config *acx = NULL;
@@ -1194,3 +1195,73 @@ out:
1194 kfree(acx); 1195 kfree(acx);
1195 return ret; 1196 return ret;
1196} 1197}
1198
1199int wl1271_acx_rssi_snr_trigger(struct wl1271 *wl, bool enable,
1200 s16 thold, u8 hyst)
1201{
1202 struct wl1271_acx_rssi_snr_trigger *acx = NULL;
1203 int ret = 0;
1204
1205 wl1271_debug(DEBUG_ACX, "acx rssi snr trigger");
1206
1207 acx = kzalloc(sizeof(*acx), GFP_KERNEL);
1208 if (!acx) {
1209 ret = -ENOMEM;
1210 goto out;
1211 }
1212
1213 wl->last_rssi_event = -1;
1214
1215 acx->pacing = cpu_to_le16(wl->conf.roam_trigger.trigger_pacing);
1216 acx->metric = WL1271_ACX_TRIG_METRIC_RSSI_BEACON;
1217 acx->type = WL1271_ACX_TRIG_TYPE_EDGE;
1218 if (enable)
1219 acx->enable = WL1271_ACX_TRIG_ENABLE;
1220 else
1221 acx->enable = WL1271_ACX_TRIG_DISABLE;
1222
1223 acx->index = WL1271_ACX_TRIG_IDX_RSSI;
1224 acx->dir = WL1271_ACX_TRIG_DIR_BIDIR;
1225 acx->threshold = cpu_to_le16(thold);
1226 acx->hysteresis = hyst;
1227
1228 ret = wl1271_cmd_configure(wl, ACX_RSSI_SNR_TRIGGER, acx, sizeof(*acx));
1229 if (ret < 0) {
1230 wl1271_warning("acx rssi snr trigger setting failed: %d", ret);
1231 goto out;
1232 }
1233
1234out:
1235 kfree(acx);
1236 return ret;
1237}
1238
1239int wl1271_acx_rssi_snr_avg_weights(struct wl1271 *wl)
1240{
1241 struct wl1271_acx_rssi_snr_avg_weights *acx = NULL;
1242 struct conf_roam_trigger_settings *c = &wl->conf.roam_trigger;
1243 int ret = 0;
1244
1245 wl1271_debug(DEBUG_ACX, "acx rssi snr avg weights");
1246
1247 acx = kzalloc(sizeof(*acx), GFP_KERNEL);
1248 if (!acx) {
1249 ret = -ENOMEM;
1250 goto out;
1251 }
1252
1253 acx->rssi_beacon = c->avg_weight_rssi_beacon;
1254 acx->rssi_data = c->avg_weight_rssi_data;
1255 acx->snr_beacon = c->avg_weight_snr_beacon;
1256 acx->snr_data = c->avg_weight_snr_data;
1257
1258 ret = wl1271_cmd_configure(wl, ACX_RSSI_SNR_WEIGHTS, acx, sizeof(*acx));
1259 if (ret < 0) {
1260 wl1271_warning("acx rssi snr trigger weights failed: %d", ret);
1261 goto out;
1262 }
1263
1264out:
1265 kfree(acx);
1266 return ret;
1267}
diff --git a/drivers/net/wireless/wl12xx/wl1271_acx.h b/drivers/net/wireless/wl12xx/wl1271_acx.h
index 15cc56192de9..420e7e2fc021 100644
--- a/drivers/net/wireless/wl12xx/wl1271_acx.h
+++ b/drivers/net/wireless/wl12xx/wl1271_acx.h
@@ -943,6 +943,57 @@ struct wl1271_acx_keep_alive_config {
943} __attribute__ ((packed)); 943} __attribute__ ((packed));
944 944
945enum { 945enum {
946 WL1271_ACX_TRIG_TYPE_LEVEL = 0,
947 WL1271_ACX_TRIG_TYPE_EDGE,
948};
949
950enum {
951 WL1271_ACX_TRIG_DIR_LOW = 0,
952 WL1271_ACX_TRIG_DIR_HIGH,
953 WL1271_ACX_TRIG_DIR_BIDIR,
954};
955
956enum {
957 WL1271_ACX_TRIG_ENABLE = 1,
958 WL1271_ACX_TRIG_DISABLE,
959};
960
961enum {
962 WL1271_ACX_TRIG_METRIC_RSSI_BEACON = 0,
963 WL1271_ACX_TRIG_METRIC_RSSI_DATA,
964 WL1271_ACX_TRIG_METRIC_SNR_BEACON,
965 WL1271_ACX_TRIG_METRIC_SNR_DATA,
966};
967
968enum {
969 WL1271_ACX_TRIG_IDX_RSSI = 0,
970 WL1271_ACX_TRIG_COUNT = 8,
971};
972
973struct wl1271_acx_rssi_snr_trigger {
974 struct acx_header header;
975
976 __le16 threshold;
977 __le16 pacing; /* 0 - 60000 ms */
978 u8 metric;
979 u8 type;
980 u8 dir;
981 u8 hysteresis;
982 u8 index;
983 u8 enable;
984 u8 padding[2];
985};
986
987struct wl1271_acx_rssi_snr_avg_weights {
988 struct acx_header header;
989
990 u8 rssi_beacon;
991 u8 rssi_data;
992 u8 snr_beacon;
993 u8 snr_data;
994};
995
996enum {
946 ACX_WAKE_UP_CONDITIONS = 0x0002, 997 ACX_WAKE_UP_CONDITIONS = 0x0002,
947 ACX_MEM_CFG = 0x0003, 998 ACX_MEM_CFG = 0x0003,
948 ACX_SLOT = 0x0004, 999 ACX_SLOT = 0x0004,
@@ -990,7 +1041,7 @@ enum {
990 ACX_FRAG_CFG = 0x004F, 1041 ACX_FRAG_CFG = 0x004F,
991 ACX_BET_ENABLE = 0x0050, 1042 ACX_BET_ENABLE = 0x0050,
992 ACX_RSSI_SNR_TRIGGER = 0x0051, 1043 ACX_RSSI_SNR_TRIGGER = 0x0051,
993 ACX_RSSI_SNR_WEIGHTS = 0x0051, 1044 ACX_RSSI_SNR_WEIGHTS = 0x0052,
994 ACX_KEEP_ALIVE_MODE = 0x0053, 1045 ACX_KEEP_ALIVE_MODE = 0x0053,
995 ACX_SET_KEEP_ALIVE_CONFIG = 0x0054, 1046 ACX_SET_KEEP_ALIVE_CONFIG = 0x0054,
996 ACX_BA_SESSION_RESPONDER_POLICY = 0x0055, 1047 ACX_BA_SESSION_RESPONDER_POLICY = 0x0055,
@@ -1060,5 +1111,8 @@ int wl1271_acx_arp_ip_filter(struct wl1271 *wl, bool enable, u8 *address,
1060int wl1271_acx_pm_config(struct wl1271 *wl); 1111int wl1271_acx_pm_config(struct wl1271 *wl);
1061int wl1271_acx_keep_alive_mode(struct wl1271 *wl, bool enable); 1112int wl1271_acx_keep_alive_mode(struct wl1271 *wl, bool enable);
1062int wl1271_acx_keep_alive_config(struct wl1271 *wl, u8 index, u8 tpl_valid); 1113int wl1271_acx_keep_alive_config(struct wl1271 *wl, u8 index, u8 tpl_valid);
1114int wl1271_acx_rssi_snr_trigger(struct wl1271 *wl, bool enable,
1115 s16 thold, u8 hyst);
1116int wl1271_acx_rssi_snr_avg_weights(struct wl1271 *wl);
1063 1117
1064#endif /* __WL1271_ACX_H__ */ 1118#endif /* __WL1271_ACX_H__ */
diff --git a/drivers/net/wireless/wl12xx/wl1271_boot.c b/drivers/net/wireless/wl12xx/wl1271_boot.c
index 7acef88df1fe..f16d15bd5643 100644
--- a/drivers/net/wireless/wl12xx/wl1271_boot.c
+++ b/drivers/net/wireless/wl12xx/wl1271_boot.c
@@ -412,7 +412,8 @@ static int wl1271_boot_run_firmware(struct wl1271 *wl)
412 SCAN_COMPLETE_EVENT_ID | 412 SCAN_COMPLETE_EVENT_ID |
413 PS_REPORT_EVENT_ID | 413 PS_REPORT_EVENT_ID |
414 JOIN_EVENT_COMPLETE_ID | 414 JOIN_EVENT_COMPLETE_ID |
415 DISCONNECT_EVENT_COMPLETE_ID; 415 DISCONNECT_EVENT_COMPLETE_ID |
416 RSSI_SNR_TRIGGER_0_EVENT_ID;
416 417
417 ret = wl1271_event_unmask(wl); 418 ret = wl1271_event_unmask(wl);
418 if (ret < 0) { 419 if (ret < 0) {
diff --git a/drivers/net/wireless/wl12xx/wl1271_conf.h b/drivers/net/wireless/wl12xx/wl1271_conf.h
index d76ae03762a3..c44307c4bcf8 100644
--- a/drivers/net/wireless/wl12xx/wl1271_conf.h
+++ b/drivers/net/wireless/wl12xx/wl1271_conf.h
@@ -756,65 +756,6 @@ enum {
756 CONF_TRIG_EVENT_DIR_BIDIR 756 CONF_TRIG_EVENT_DIR_BIDIR
757}; 757};
758 758
759
760struct conf_sig_trigger {
761 /*
762 * The RSSI / SNR threshold value.
763 *
764 * FIXME: what is the range?
765 */
766 s16 threshold;
767
768 /*
769 * Minimum delay between two trigger events for this trigger in ms.
770 *
771 * Range: 0 - 60000
772 */
773 u16 pacing;
774
775 /*
776 * The measurement data source for this trigger.
777 *
778 * Range: CONF_TRIG_METRIC_*
779 */
780 u8 metric;
781
782 /*
783 * The trigger type of this trigger.
784 *
785 * Range: CONF_TRIG_EVENT_TYPE_*
786 */
787 u8 type;
788
789 /*
790 * The direction of the trigger.
791 *
792 * Range: CONF_TRIG_EVENT_DIR_*
793 */
794 u8 direction;
795
796 /*
797 * Hysteresis range of the trigger around the threshold (in dB)
798 *
799 * Range: u8
800 */
801 u8 hysteresis;
802
803 /*
804 * Index of the trigger rule.
805 *
806 * Range: 0 - CONF_MAX_RSSI_SNR_TRIGGERS-1
807 */
808 u8 index;
809
810 /*
811 * Enable / disable this rule (to use for clearing rules.)
812 *
813 * Range: 1 - Enabled, 2 - Not enabled
814 */
815 u8 enable;
816};
817
818struct conf_sig_weights { 759struct conf_sig_weights {
819 760
820 /* 761 /*
@@ -933,12 +874,6 @@ struct conf_conn_settings {
933 u8 ps_poll_threshold; 874 u8 ps_poll_threshold;
934 875
935 /* 876 /*
936 * Configuration of signal (rssi/snr) triggers.
937 */
938 u8 sig_trigger_count;
939 struct conf_sig_trigger sig_trigger[CONF_MAX_RSSI_SNR_TRIGGERS];
940
941 /*
942 * Configuration of signal average weights. 877 * Configuration of signal average weights.
943 */ 878 */
944 struct conf_sig_weights sig_weights; 879 struct conf_sig_weights sig_weights;
@@ -1045,6 +980,43 @@ struct conf_pm_config_settings {
1045 bool host_fast_wakeup_support; 980 bool host_fast_wakeup_support;
1046}; 981};
1047 982
983struct conf_roam_trigger_settings {
984 /*
985 * The minimum interval between two trigger events.
986 *
987 * Range: 0 - 60000 ms
988 */
989 u16 trigger_pacing;
990
991 /*
992 * The weight for rssi/beacon average calculation
993 *
994 * Range: 0 - 255
995 */
996 u8 avg_weight_rssi_beacon;
997
998 /*
999 * The weight for rssi/data frame average calculation
1000 *
1001 * Range: 0 - 255
1002 */
1003 u8 avg_weight_rssi_data;
1004
1005 /*
1006 * The weight for snr/beacon average calculation
1007 *
1008 * Range: 0 - 255
1009 */
1010 u8 avg_weight_snr_beacon;
1011
1012 /*
1013 * The weight for snr/data frame average calculation
1014 *
1015 * Range: 0 - 255
1016 */
1017 u8 avg_weight_snr_data;
1018};
1019
1048struct conf_drv_settings { 1020struct conf_drv_settings {
1049 struct conf_sg_settings sg; 1021 struct conf_sg_settings sg;
1050 struct conf_rx_settings rx; 1022 struct conf_rx_settings rx;
@@ -1053,6 +1025,7 @@ struct conf_drv_settings {
1053 struct conf_init_settings init; 1025 struct conf_init_settings init;
1054 struct conf_itrim_settings itrim; 1026 struct conf_itrim_settings itrim;
1055 struct conf_pm_config_settings pm_config; 1027 struct conf_pm_config_settings pm_config;
1028 struct conf_roam_trigger_settings roam_trigger;
1056}; 1029};
1057 1030
1058#endif 1031#endif
diff --git a/drivers/net/wireless/wl12xx/wl1271_event.c b/drivers/net/wireless/wl12xx/wl1271_event.c
index daacf176cf09..cf37aa6eb137 100644
--- a/drivers/net/wireless/wl12xx/wl1271_event.c
+++ b/drivers/net/wireless/wl12xx/wl1271_event.c
@@ -125,6 +125,24 @@ static int wl1271_event_ps_report(struct wl1271 *wl,
125 return ret; 125 return ret;
126} 126}
127 127
128static void wl1271_event_rssi_trigger(struct wl1271 *wl,
129 struct event_mailbox *mbox)
130{
131 enum nl80211_cqm_rssi_threshold_event event;
132 s8 metric = mbox->rssi_snr_trigger_metric[0];
133
134 wl1271_debug(DEBUG_EVENT, "RSSI trigger metric: %d", metric);
135
136 if (metric <= wl->rssi_thold)
137 event = NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW;
138 else
139 event = NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH;
140
141 if (event != wl->last_rssi_event)
142 ieee80211_cqm_rssi_notify(wl->vif, event, GFP_KERNEL);
143 wl->last_rssi_event = event;
144}
145
128static void wl1271_event_mbox_dump(struct event_mailbox *mbox) 146static void wl1271_event_mbox_dump(struct event_mailbox *mbox)
129{ 147{
130 wl1271_debug(DEBUG_EVENT, "MBOX DUMP:"); 148 wl1271_debug(DEBUG_EVENT, "MBOX DUMP:");
@@ -173,6 +191,12 @@ static int wl1271_event_process(struct wl1271 *wl, struct event_mailbox *mbox)
173 return ret; 191 return ret;
174 } 192 }
175 193
194 if (vector & RSSI_SNR_TRIGGER_0_EVENT_ID) {
195 wl1271_debug(DEBUG_EVENT, "RSSI_SNR_TRIGGER_0_EVENT");
196 if (wl->vif)
197 wl1271_event_rssi_trigger(wl, mbox);
198 }
199
176 if (wl->vif && beacon_loss) 200 if (wl->vif && beacon_loss)
177 ieee80211_connection_loss(wl->vif); 201 ieee80211_connection_loss(wl->vif);
178 202
diff --git a/drivers/net/wireless/wl12xx/wl1271_event.h b/drivers/net/wireless/wl12xx/wl1271_event.h
index 278f9206aa56..58371008f270 100644
--- a/drivers/net/wireless/wl12xx/wl1271_event.h
+++ b/drivers/net/wireless/wl12xx/wl1271_event.h
@@ -38,6 +38,14 @@
38 */ 38 */
39 39
40enum { 40enum {
41 RSSI_SNR_TRIGGER_0_EVENT_ID = BIT(0),
42 RSSI_SNR_TRIGGER_1_EVENT_ID = BIT(1),
43 RSSI_SNR_TRIGGER_2_EVENT_ID = BIT(2),
44 RSSI_SNR_TRIGGER_3_EVENT_ID = BIT(3),
45 RSSI_SNR_TRIGGER_4_EVENT_ID = BIT(4),
46 RSSI_SNR_TRIGGER_5_EVENT_ID = BIT(5),
47 RSSI_SNR_TRIGGER_6_EVENT_ID = BIT(6),
48 RSSI_SNR_TRIGGER_7_EVENT_ID = BIT(7),
41 MEASUREMENT_START_EVENT_ID = BIT(8), 49 MEASUREMENT_START_EVENT_ID = BIT(8),
42 MEASUREMENT_COMPLETE_EVENT_ID = BIT(9), 50 MEASUREMENT_COMPLETE_EVENT_ID = BIT(9),
43 SCAN_COMPLETE_EVENT_ID = BIT(10), 51 SCAN_COMPLETE_EVENT_ID = BIT(10),
diff --git a/drivers/net/wireless/wl12xx/wl1271_init.c b/drivers/net/wireless/wl12xx/wl1271_init.c
index 9ab336829044..b880382cf15d 100644
--- a/drivers/net/wireless/wl12xx/wl1271_init.c
+++ b/drivers/net/wireless/wl12xx/wl1271_init.c
@@ -352,6 +352,11 @@ int wl1271_hw_init(struct wl1271 *wl)
352 if (ret < 0) 352 if (ret < 0)
353 goto out_free_memmap; 353 goto out_free_memmap;
354 354
355 /* Configure rssi/snr averaging weights */
356 ret = wl1271_acx_rssi_snr_avg_weights(wl);
357 if (ret < 0)
358 goto out_free_memmap;
359
355 return 0; 360 return 0;
356 361
357 out_free_memmap: 362 out_free_memmap:
diff --git a/drivers/net/wireless/wl12xx/wl1271_main.c b/drivers/net/wireless/wl12xx/wl1271_main.c
index 551714164ff5..283d5dade1ae 100644
--- a/drivers/net/wireless/wl12xx/wl1271_main.c
+++ b/drivers/net/wireless/wl12xx/wl1271_main.c
@@ -234,35 +234,6 @@ static struct conf_drv_settings default_conf = {
234 .broadcast_timeout = 20000, 234 .broadcast_timeout = 20000,
235 .rx_broadcast_in_ps = 1, 235 .rx_broadcast_in_ps = 1,
236 .ps_poll_threshold = 20, 236 .ps_poll_threshold = 20,
237 .sig_trigger_count = 2,
238 .sig_trigger = {
239 [0] = {
240 .threshold = -75,
241 .pacing = 500,
242 .metric = CONF_TRIG_METRIC_RSSI_BEACON,
243 .type = CONF_TRIG_EVENT_TYPE_EDGE,
244 .direction = CONF_TRIG_EVENT_DIR_LOW,
245 .hysteresis = 2,
246 .index = 0,
247 .enable = 1
248 },
249 [1] = {
250 .threshold = -75,
251 .pacing = 500,
252 .metric = CONF_TRIG_METRIC_RSSI_BEACON,
253 .type = CONF_TRIG_EVENT_TYPE_EDGE,
254 .direction = CONF_TRIG_EVENT_DIR_HIGH,
255 .hysteresis = 2,
256 .index = 1,
257 .enable = 1
258 }
259 },
260 .sig_weights = {
261 .rssi_bcn_avg_weight = 10,
262 .rssi_pkt_avg_weight = 10,
263 .snr_bcn_avg_weight = 10,
264 .snr_pkt_avg_weight = 10
265 },
266 .bet_enable = CONF_BET_MODE_ENABLE, 237 .bet_enable = CONF_BET_MODE_ENABLE,
267 .bet_max_consecutive = 10, 238 .bet_max_consecutive = 10,
268 .psm_entry_retries = 3, 239 .psm_entry_retries = 3,
@@ -281,6 +252,14 @@ static struct conf_drv_settings default_conf = {
281 .pm_config = { 252 .pm_config = {
282 .host_clk_settling_time = 5000, 253 .host_clk_settling_time = 5000,
283 .host_fast_wakeup_support = false 254 .host_fast_wakeup_support = false
255 },
256 .roam_trigger = {
257 /* FIXME: due to firmware bug, must use value 1 for now */
258 .trigger_pacing = 1,
259 .avg_weight_rssi_beacon = 20,
260 .avg_weight_rssi_data = 10,
261 .avg_weight_snr_beacon = 20,
262 .avg_weight_snr_data = 10
284 } 263 }
285}; 264};
286 265
@@ -1703,6 +1682,18 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw,
1703 do_join = true; 1682 do_join = true;
1704 } 1683 }
1705 1684
1685 if (changed & BSS_CHANGED_CQM) {
1686 bool enable = false;
1687 if (bss_conf->cqm_rssi_thold)
1688 enable = true;
1689 ret = wl1271_acx_rssi_snr_trigger(wl, enable,
1690 bss_conf->cqm_rssi_thold,
1691 bss_conf->cqm_rssi_hyst);
1692 if (ret < 0)
1693 goto out;
1694 wl->rssi_thold = bss_conf->cqm_rssi_thold;
1695 }
1696
1706 if ((changed & BSS_CHANGED_BSSID) && 1697 if ((changed & BSS_CHANGED_BSSID) &&
1707 /* 1698 /*
1708 * Now we know the correct bssid, so we send a new join command 1699 * Now we know the correct bssid, so we send a new join command
@@ -2283,7 +2274,8 @@ int wl1271_init_ieee80211(struct wl1271 *wl)
2283 IEEE80211_HW_SUPPORTS_PS | 2274 IEEE80211_HW_SUPPORTS_PS |
2284 IEEE80211_HW_SUPPORTS_UAPSD | 2275 IEEE80211_HW_SUPPORTS_UAPSD |
2285 IEEE80211_HW_HAS_RATE_CONTROL | 2276 IEEE80211_HW_HAS_RATE_CONTROL |
2286 IEEE80211_HW_CONNECTION_MONITOR; 2277 IEEE80211_HW_CONNECTION_MONITOR |
2278 IEEE80211_HW_SUPPORTS_CQM_RSSI;
2287 2279
2288 wl->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | 2280 wl->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
2289 BIT(NL80211_IFTYPE_ADHOC); 2281 BIT(NL80211_IFTYPE_ADHOC);