1f444de05SJohannes Berg /* 2f444de05SJohannes Berg * mac80211 - channel management 3f444de05SJohannes Berg */ 4f444de05SJohannes Berg 50aaffa9bSJohannes Berg #include <linux/nl80211.h> 63117bbdbSPaul Stewart #include <net/cfg80211.h> 7f444de05SJohannes Berg #include "ieee80211_i.h" 835f2fce9SMichal Kazior #include "driver-ops.h" 9f444de05SJohannes Berg 1023a85b45SMichal Kazior static bool 1123a85b45SMichal Kazior ieee80211_channel_types_are_compatible(enum nl80211_channel_type chantype1, 1223a85b45SMichal Kazior enum nl80211_channel_type chantype2, 1323a85b45SMichal Kazior enum nl80211_channel_type *compat) 1423a85b45SMichal Kazior { 1523a85b45SMichal Kazior /* 1623a85b45SMichal Kazior * start out with chantype1 being the result, 1723a85b45SMichal Kazior * overwriting later if needed 1823a85b45SMichal Kazior */ 1923a85b45SMichal Kazior if (compat) 2023a85b45SMichal Kazior *compat = chantype1; 2123a85b45SMichal Kazior 2223a85b45SMichal Kazior switch (chantype1) { 230aaffa9bSJohannes Berg case NL80211_CHAN_NO_HT: 2423a85b45SMichal Kazior if (compat) 2523a85b45SMichal Kazior *compat = chantype2; 2623a85b45SMichal Kazior break; 270aaffa9bSJohannes Berg case NL80211_CHAN_HT20: 280aaffa9bSJohannes Berg /* 290aaffa9bSJohannes Berg * allow any change that doesn't go to no-HT 300aaffa9bSJohannes Berg * (if it already is no-HT no change is needed) 310aaffa9bSJohannes Berg */ 3223a85b45SMichal Kazior if (chantype2 == NL80211_CHAN_NO_HT) 330aaffa9bSJohannes Berg break; 3423a85b45SMichal Kazior if (compat) 3523a85b45SMichal Kazior *compat = chantype2; 360aaffa9bSJohannes Berg break; 370aaffa9bSJohannes Berg case NL80211_CHAN_HT40PLUS: 380aaffa9bSJohannes Berg case NL80211_CHAN_HT40MINUS: 390aaffa9bSJohannes Berg /* allow smaller bandwidth and same */ 4023a85b45SMichal Kazior if (chantype2 == NL80211_CHAN_NO_HT) 410aaffa9bSJohannes Berg break; 4223a85b45SMichal Kazior if (chantype2 == NL80211_CHAN_HT20) 430aaffa9bSJohannes Berg break; 4423a85b45SMichal Kazior if (chantype2 == chantype1) 450aaffa9bSJohannes Berg break; 4623a85b45SMichal Kazior return false; 470aaffa9bSJohannes Berg } 480aaffa9bSJohannes Berg 4923a85b45SMichal Kazior return true; 5023a85b45SMichal Kazior } 5123a85b45SMichal Kazior 52e89a96f5SMichal Kazior static void ieee80211_change_chantype(struct ieee80211_local *local, 53e89a96f5SMichal Kazior struct ieee80211_chanctx *ctx, 54e89a96f5SMichal Kazior enum nl80211_channel_type chantype) 55e89a96f5SMichal Kazior { 56e89a96f5SMichal Kazior if (chantype == ctx->conf.channel_type) 57e89a96f5SMichal Kazior return; 58e89a96f5SMichal Kazior 59e89a96f5SMichal Kazior ctx->conf.channel_type = chantype; 60e89a96f5SMichal Kazior drv_change_chanctx(local, ctx, IEEE80211_CHANCTX_CHANGE_CHANNEL_TYPE); 6155de908aSJohannes Berg 6255de908aSJohannes Berg if (!local->use_chanctx) { 6355de908aSJohannes Berg local->_oper_channel_type = chantype; 6455de908aSJohannes Berg ieee80211_hw_config(local, 0); 6555de908aSJohannes Berg } 660aaffa9bSJohannes Berg } 67d01a1e65SMichal Kazior 68d01a1e65SMichal Kazior static struct ieee80211_chanctx * 69d01a1e65SMichal Kazior ieee80211_find_chanctx(struct ieee80211_local *local, 70d01a1e65SMichal Kazior struct ieee80211_channel *channel, 71d01a1e65SMichal Kazior enum nl80211_channel_type channel_type, 72d01a1e65SMichal Kazior enum ieee80211_chanctx_mode mode) 73d01a1e65SMichal Kazior { 74d01a1e65SMichal Kazior struct ieee80211_chanctx *ctx; 75e89a96f5SMichal Kazior enum nl80211_channel_type compat_type; 76d01a1e65SMichal Kazior 77d01a1e65SMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 78d01a1e65SMichal Kazior 79d01a1e65SMichal Kazior if (mode == IEEE80211_CHANCTX_EXCLUSIVE) 80d01a1e65SMichal Kazior return NULL; 81d01a1e65SMichal Kazior if (WARN_ON(!channel)) 82d01a1e65SMichal Kazior return NULL; 83d01a1e65SMichal Kazior 84d01a1e65SMichal Kazior list_for_each_entry(ctx, &local->chanctx_list, list) { 85e89a96f5SMichal Kazior compat_type = ctx->conf.channel_type; 86e89a96f5SMichal Kazior 87d01a1e65SMichal Kazior if (ctx->mode == IEEE80211_CHANCTX_EXCLUSIVE) 88d01a1e65SMichal Kazior continue; 89d01a1e65SMichal Kazior if (ctx->conf.channel != channel) 90d01a1e65SMichal Kazior continue; 91e89a96f5SMichal Kazior if (!ieee80211_channel_types_are_compatible(ctx->conf.channel_type, 92e89a96f5SMichal Kazior channel_type, 93e89a96f5SMichal Kazior &compat_type)) 94d01a1e65SMichal Kazior continue; 95d01a1e65SMichal Kazior 96e89a96f5SMichal Kazior ieee80211_change_chantype(local, ctx, compat_type); 97e89a96f5SMichal Kazior 98d01a1e65SMichal Kazior return ctx; 99d01a1e65SMichal Kazior } 100d01a1e65SMichal Kazior 101d01a1e65SMichal Kazior return NULL; 102d01a1e65SMichal Kazior } 103d01a1e65SMichal Kazior 104d01a1e65SMichal Kazior static struct ieee80211_chanctx * 105d01a1e65SMichal Kazior ieee80211_new_chanctx(struct ieee80211_local *local, 106d01a1e65SMichal Kazior struct ieee80211_channel *channel, 107d01a1e65SMichal Kazior enum nl80211_channel_type channel_type, 108d01a1e65SMichal Kazior enum ieee80211_chanctx_mode mode) 109d01a1e65SMichal Kazior { 110d01a1e65SMichal Kazior struct ieee80211_chanctx *ctx; 11135f2fce9SMichal Kazior int err; 112d01a1e65SMichal Kazior 113d01a1e65SMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 114d01a1e65SMichal Kazior 115d01a1e65SMichal Kazior ctx = kzalloc(sizeof(*ctx) + local->hw.chanctx_data_size, GFP_KERNEL); 116d01a1e65SMichal Kazior if (!ctx) 117d01a1e65SMichal Kazior return ERR_PTR(-ENOMEM); 118d01a1e65SMichal Kazior 119d01a1e65SMichal Kazior ctx->conf.channel = channel; 120d01a1e65SMichal Kazior ctx->conf.channel_type = channel_type; 12104ecd257SJohannes Berg ctx->conf.rx_chains_static = 1; 12204ecd257SJohannes Berg ctx->conf.rx_chains_dynamic = 1; 123d01a1e65SMichal Kazior ctx->mode = mode; 124d01a1e65SMichal Kazior 12555de908aSJohannes Berg if (!local->use_chanctx) { 12655de908aSJohannes Berg local->_oper_channel_type = channel_type; 12755de908aSJohannes Berg local->_oper_channel = channel; 12855de908aSJohannes Berg ieee80211_hw_config(local, 0); 12955de908aSJohannes Berg } else { 13035f2fce9SMichal Kazior err = drv_add_chanctx(local, ctx); 13135f2fce9SMichal Kazior if (err) { 13235f2fce9SMichal Kazior kfree(ctx); 13335f2fce9SMichal Kazior return ERR_PTR(err); 13435f2fce9SMichal Kazior } 13555de908aSJohannes Berg } 13635f2fce9SMichal Kazior 137d01a1e65SMichal Kazior list_add(&ctx->list, &local->chanctx_list); 138d01a1e65SMichal Kazior 139d01a1e65SMichal Kazior return ctx; 140d01a1e65SMichal Kazior } 141d01a1e65SMichal Kazior 142d01a1e65SMichal Kazior static void ieee80211_free_chanctx(struct ieee80211_local *local, 143d01a1e65SMichal Kazior struct ieee80211_chanctx *ctx) 144d01a1e65SMichal Kazior { 145d01a1e65SMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 146d01a1e65SMichal Kazior 147d01a1e65SMichal Kazior WARN_ON_ONCE(ctx->refcount != 0); 148d01a1e65SMichal Kazior 14955de908aSJohannes Berg if (!local->use_chanctx) { 15055de908aSJohannes Berg local->_oper_channel_type = NL80211_CHAN_NO_HT; 15155de908aSJohannes Berg ieee80211_hw_config(local, 0); 15255de908aSJohannes Berg } else { 15335f2fce9SMichal Kazior drv_remove_chanctx(local, ctx); 15455de908aSJohannes Berg } 15535f2fce9SMichal Kazior 156d01a1e65SMichal Kazior list_del(&ctx->list); 157d01a1e65SMichal Kazior kfree_rcu(ctx, rcu_head); 158d01a1e65SMichal Kazior } 159d01a1e65SMichal Kazior 160d01a1e65SMichal Kazior static int ieee80211_assign_vif_chanctx(struct ieee80211_sub_if_data *sdata, 161d01a1e65SMichal Kazior struct ieee80211_chanctx *ctx) 162d01a1e65SMichal Kazior { 16335f2fce9SMichal Kazior struct ieee80211_local *local = sdata->local; 16435f2fce9SMichal Kazior int ret; 165d01a1e65SMichal Kazior 166d01a1e65SMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 167d01a1e65SMichal Kazior 16835f2fce9SMichal Kazior ret = drv_assign_vif_chanctx(local, sdata, ctx); 16935f2fce9SMichal Kazior if (ret) 17035f2fce9SMichal Kazior return ret; 17135f2fce9SMichal Kazior 172d01a1e65SMichal Kazior rcu_assign_pointer(sdata->vif.chanctx_conf, &ctx->conf); 173d01a1e65SMichal Kazior ctx->refcount++; 174d01a1e65SMichal Kazior 175d01a1e65SMichal Kazior return 0; 176d01a1e65SMichal Kazior } 177d01a1e65SMichal Kazior 178e89a96f5SMichal Kazior static enum nl80211_channel_type 179e89a96f5SMichal Kazior ieee80211_calc_chantype(struct ieee80211_local *local, 180e89a96f5SMichal Kazior struct ieee80211_chanctx *ctx) 181e89a96f5SMichal Kazior { 182e89a96f5SMichal Kazior struct ieee80211_chanctx_conf *conf = &ctx->conf; 183e89a96f5SMichal Kazior struct ieee80211_sub_if_data *sdata; 184e89a96f5SMichal Kazior enum nl80211_channel_type result = NL80211_CHAN_NO_HT; 185e89a96f5SMichal Kazior 186e89a96f5SMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 187e89a96f5SMichal Kazior 188e89a96f5SMichal Kazior rcu_read_lock(); 189e89a96f5SMichal Kazior list_for_each_entry_rcu(sdata, &local->interfaces, list) { 190e89a96f5SMichal Kazior if (!ieee80211_sdata_running(sdata)) 191e89a96f5SMichal Kazior continue; 192e89a96f5SMichal Kazior if (rcu_access_pointer(sdata->vif.chanctx_conf) != conf) 193e89a96f5SMichal Kazior continue; 194e89a96f5SMichal Kazior 195e89a96f5SMichal Kazior WARN_ON_ONCE(!ieee80211_channel_types_are_compatible( 196e89a96f5SMichal Kazior sdata->vif.bss_conf.channel_type, 197e89a96f5SMichal Kazior result, &result)); 198e89a96f5SMichal Kazior } 199e89a96f5SMichal Kazior rcu_read_unlock(); 200e89a96f5SMichal Kazior 201e89a96f5SMichal Kazior return result; 202e89a96f5SMichal Kazior } 203e89a96f5SMichal Kazior 204e89a96f5SMichal Kazior static void ieee80211_recalc_chanctx_chantype(struct ieee80211_local *local, 205e89a96f5SMichal Kazior struct ieee80211_chanctx *ctx) 206e89a96f5SMichal Kazior { 207e89a96f5SMichal Kazior enum nl80211_channel_type chantype; 208e89a96f5SMichal Kazior 209e89a96f5SMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 210e89a96f5SMichal Kazior 211e89a96f5SMichal Kazior chantype = ieee80211_calc_chantype(local, ctx); 212e89a96f5SMichal Kazior ieee80211_change_chantype(local, ctx, chantype); 213e89a96f5SMichal Kazior } 214e89a96f5SMichal Kazior 215d01a1e65SMichal Kazior static void ieee80211_unassign_vif_chanctx(struct ieee80211_sub_if_data *sdata, 216d01a1e65SMichal Kazior struct ieee80211_chanctx *ctx) 217d01a1e65SMichal Kazior { 21835f2fce9SMichal Kazior struct ieee80211_local *local = sdata->local; 219d01a1e65SMichal Kazior 220d01a1e65SMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 221d01a1e65SMichal Kazior 222d01a1e65SMichal Kazior ctx->refcount--; 223d01a1e65SMichal Kazior rcu_assign_pointer(sdata->vif.chanctx_conf, NULL); 22435f2fce9SMichal Kazior 22535f2fce9SMichal Kazior drv_unassign_vif_chanctx(local, sdata, ctx); 226e89a96f5SMichal Kazior 22704ecd257SJohannes Berg if (ctx->refcount > 0) { 228e89a96f5SMichal Kazior ieee80211_recalc_chanctx_chantype(sdata->local, ctx); 22904ecd257SJohannes Berg ieee80211_recalc_smps_chanctx(local, ctx); 23004ecd257SJohannes Berg } 231d01a1e65SMichal Kazior } 232d01a1e65SMichal Kazior 233d01a1e65SMichal Kazior static void __ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata) 234d01a1e65SMichal Kazior { 235d01a1e65SMichal Kazior struct ieee80211_local *local = sdata->local; 236d01a1e65SMichal Kazior struct ieee80211_chanctx_conf *conf; 237d01a1e65SMichal Kazior struct ieee80211_chanctx *ctx; 238d01a1e65SMichal Kazior 239d01a1e65SMichal Kazior lockdep_assert_held(&local->chanctx_mtx); 240d01a1e65SMichal Kazior 241d01a1e65SMichal Kazior conf = rcu_dereference_protected(sdata->vif.chanctx_conf, 242d01a1e65SMichal Kazior lockdep_is_held(&local->chanctx_mtx)); 243d01a1e65SMichal Kazior if (!conf) 244d01a1e65SMichal Kazior return; 245d01a1e65SMichal Kazior 246d01a1e65SMichal Kazior ctx = container_of(conf, struct ieee80211_chanctx, conf); 247d01a1e65SMichal Kazior 248d01a1e65SMichal Kazior ieee80211_unassign_vif_chanctx(sdata, ctx); 249d01a1e65SMichal Kazior if (ctx->refcount == 0) 250d01a1e65SMichal Kazior ieee80211_free_chanctx(local, ctx); 251d01a1e65SMichal Kazior } 252d01a1e65SMichal Kazior 25304ecd257SJohannes Berg void ieee80211_recalc_smps_chanctx(struct ieee80211_local *local, 25404ecd257SJohannes Berg struct ieee80211_chanctx *chanctx) 25504ecd257SJohannes Berg { 25604ecd257SJohannes Berg struct ieee80211_sub_if_data *sdata; 25704ecd257SJohannes Berg u8 rx_chains_static, rx_chains_dynamic; 25804ecd257SJohannes Berg 25904ecd257SJohannes Berg lockdep_assert_held(&local->chanctx_mtx); 26004ecd257SJohannes Berg 26104ecd257SJohannes Berg rx_chains_static = 1; 26204ecd257SJohannes Berg rx_chains_dynamic = 1; 26304ecd257SJohannes Berg 26404ecd257SJohannes Berg rcu_read_lock(); 26504ecd257SJohannes Berg list_for_each_entry_rcu(sdata, &local->interfaces, list) { 26604ecd257SJohannes Berg u8 needed_static, needed_dynamic; 26704ecd257SJohannes Berg 26804ecd257SJohannes Berg if (!ieee80211_sdata_running(sdata)) 26904ecd257SJohannes Berg continue; 27004ecd257SJohannes Berg 27104ecd257SJohannes Berg if (rcu_access_pointer(sdata->vif.chanctx_conf) != 27204ecd257SJohannes Berg &chanctx->conf) 27304ecd257SJohannes Berg continue; 27404ecd257SJohannes Berg 27504ecd257SJohannes Berg switch (sdata->vif.type) { 27604ecd257SJohannes Berg case NL80211_IFTYPE_P2P_DEVICE: 27704ecd257SJohannes Berg continue; 27804ecd257SJohannes Berg case NL80211_IFTYPE_STATION: 27904ecd257SJohannes Berg if (!sdata->u.mgd.associated) 28004ecd257SJohannes Berg continue; 28104ecd257SJohannes Berg break; 28204ecd257SJohannes Berg case NL80211_IFTYPE_AP_VLAN: 28304ecd257SJohannes Berg continue; 28404ecd257SJohannes Berg case NL80211_IFTYPE_AP: 28504ecd257SJohannes Berg case NL80211_IFTYPE_ADHOC: 28604ecd257SJohannes Berg case NL80211_IFTYPE_WDS: 28704ecd257SJohannes Berg case NL80211_IFTYPE_MESH_POINT: 28804ecd257SJohannes Berg break; 28904ecd257SJohannes Berg default: 29004ecd257SJohannes Berg WARN_ON_ONCE(1); 29104ecd257SJohannes Berg } 29204ecd257SJohannes Berg 29304ecd257SJohannes Berg switch (sdata->smps_mode) { 29404ecd257SJohannes Berg default: 29504ecd257SJohannes Berg WARN_ONCE(1, "Invalid SMPS mode %d\n", 29604ecd257SJohannes Berg sdata->smps_mode); 29704ecd257SJohannes Berg /* fall through */ 29804ecd257SJohannes Berg case IEEE80211_SMPS_OFF: 29904ecd257SJohannes Berg needed_static = sdata->needed_rx_chains; 30004ecd257SJohannes Berg needed_dynamic = sdata->needed_rx_chains; 30104ecd257SJohannes Berg break; 30204ecd257SJohannes Berg case IEEE80211_SMPS_DYNAMIC: 30304ecd257SJohannes Berg needed_static = 1; 30404ecd257SJohannes Berg needed_dynamic = sdata->needed_rx_chains; 30504ecd257SJohannes Berg break; 30604ecd257SJohannes Berg case IEEE80211_SMPS_STATIC: 30704ecd257SJohannes Berg needed_static = 1; 30804ecd257SJohannes Berg needed_dynamic = 1; 30904ecd257SJohannes Berg break; 31004ecd257SJohannes Berg } 31104ecd257SJohannes Berg 31204ecd257SJohannes Berg rx_chains_static = max(rx_chains_static, needed_static); 31304ecd257SJohannes Berg rx_chains_dynamic = max(rx_chains_dynamic, needed_dynamic); 31404ecd257SJohannes Berg } 31504ecd257SJohannes Berg rcu_read_unlock(); 31604ecd257SJohannes Berg 31704ecd257SJohannes Berg if (!local->use_chanctx) { 31804ecd257SJohannes Berg if (rx_chains_static > 1) 31904ecd257SJohannes Berg local->smps_mode = IEEE80211_SMPS_OFF; 32004ecd257SJohannes Berg else if (rx_chains_dynamic > 1) 32104ecd257SJohannes Berg local->smps_mode = IEEE80211_SMPS_DYNAMIC; 32204ecd257SJohannes Berg else 32304ecd257SJohannes Berg local->smps_mode = IEEE80211_SMPS_STATIC; 32404ecd257SJohannes Berg ieee80211_hw_config(local, 0); 32504ecd257SJohannes Berg } 32604ecd257SJohannes Berg 32704ecd257SJohannes Berg if (rx_chains_static == chanctx->conf.rx_chains_static && 32804ecd257SJohannes Berg rx_chains_dynamic == chanctx->conf.rx_chains_dynamic) 32904ecd257SJohannes Berg return; 33004ecd257SJohannes Berg 33104ecd257SJohannes Berg chanctx->conf.rx_chains_static = rx_chains_static; 33204ecd257SJohannes Berg chanctx->conf.rx_chains_dynamic = rx_chains_dynamic; 33304ecd257SJohannes Berg drv_change_chanctx(local, chanctx, IEEE80211_CHANCTX_CHANGE_RX_CHAINS); 33404ecd257SJohannes Berg } 33504ecd257SJohannes Berg 336d01a1e65SMichal Kazior int ieee80211_vif_use_channel(struct ieee80211_sub_if_data *sdata, 337d01a1e65SMichal Kazior struct ieee80211_channel *channel, 338d01a1e65SMichal Kazior enum nl80211_channel_type channel_type, 339d01a1e65SMichal Kazior enum ieee80211_chanctx_mode mode) 340d01a1e65SMichal Kazior { 341d01a1e65SMichal Kazior struct ieee80211_local *local = sdata->local; 342d01a1e65SMichal Kazior struct ieee80211_chanctx *ctx; 343d01a1e65SMichal Kazior int ret; 344d01a1e65SMichal Kazior 34555de908aSJohannes Berg WARN_ON(sdata->dev && netif_carrier_ok(sdata->dev)); 34655de908aSJohannes Berg 347d01a1e65SMichal Kazior mutex_lock(&local->chanctx_mtx); 348d01a1e65SMichal Kazior __ieee80211_vif_release_channel(sdata); 349d01a1e65SMichal Kazior 350d01a1e65SMichal Kazior ctx = ieee80211_find_chanctx(local, channel, channel_type, mode); 351d01a1e65SMichal Kazior if (!ctx) 352d01a1e65SMichal Kazior ctx = ieee80211_new_chanctx(local, channel, channel_type, mode); 353d01a1e65SMichal Kazior if (IS_ERR(ctx)) { 354d01a1e65SMichal Kazior ret = PTR_ERR(ctx); 355d01a1e65SMichal Kazior goto out; 356d01a1e65SMichal Kazior } 357d01a1e65SMichal Kazior 35855de908aSJohannes Berg sdata->vif.bss_conf.channel_type = channel_type; 35955de908aSJohannes Berg 360d01a1e65SMichal Kazior ret = ieee80211_assign_vif_chanctx(sdata, ctx); 361d01a1e65SMichal Kazior if (ret) { 362d01a1e65SMichal Kazior /* if assign fails refcount stays the same */ 363d01a1e65SMichal Kazior if (ctx->refcount == 0) 364d01a1e65SMichal Kazior ieee80211_free_chanctx(local, ctx); 365d01a1e65SMichal Kazior goto out; 366d01a1e65SMichal Kazior } 367d01a1e65SMichal Kazior 36804ecd257SJohannes Berg ieee80211_recalc_smps_chanctx(local, ctx); 369d01a1e65SMichal Kazior out: 370d01a1e65SMichal Kazior mutex_unlock(&local->chanctx_mtx); 371d01a1e65SMichal Kazior return ret; 372d01a1e65SMichal Kazior } 373d01a1e65SMichal Kazior 374d01a1e65SMichal Kazior void ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata) 375d01a1e65SMichal Kazior { 37655de908aSJohannes Berg WARN_ON(sdata->dev && netif_carrier_ok(sdata->dev)); 37755de908aSJohannes Berg 378d01a1e65SMichal Kazior mutex_lock(&sdata->local->chanctx_mtx); 379d01a1e65SMichal Kazior __ieee80211_vif_release_channel(sdata); 380d01a1e65SMichal Kazior mutex_unlock(&sdata->local->chanctx_mtx); 381d01a1e65SMichal Kazior } 382