linux/drivers/net/wireless/ath/dfs_pri_detector.c
<<
>>
Prefs
   1/*
   2 * Copyright (c) 2012 Neratec Solutions AG
   3 *
   4 * Permission to use, copy, modify, and/or distribute this software for any
   5 * purpose with or without fee is hereby granted, provided that the above
   6 * copyright notice and this permission notice appear in all copies.
   7 *
   8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
   9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  15 */
  16
  17#include <linux/slab.h>
  18#include <linux/spinlock.h>
  19
  20#include "ath.h"
  21#include "dfs_pattern_detector.h"
  22#include "dfs_pri_detector.h"
  23
  24struct ath_dfs_pool_stats global_dfs_pool_stats = {};
  25
  26#define DFS_POOL_STAT_INC(c) (global_dfs_pool_stats.c++)
  27#define DFS_POOL_STAT_DEC(c) (global_dfs_pool_stats.c--)
  28#define GET_PRI_TO_USE(MIN, MAX, RUNTIME) \
  29        (MIN + PRI_TOLERANCE == MAX - PRI_TOLERANCE ? \
  30        MIN + PRI_TOLERANCE : RUNTIME)
  31
  32/*
  33 * struct pulse_elem - elements in pulse queue
  34 */
  35struct pulse_elem {
  36        struct list_head head;
  37        u64 ts;
  38};
  39
  40/*
  41 * pde_get_multiple() - get number of multiples considering a given tolerance
  42 * Return value: factor if abs(val - factor*fraction) <= tolerance, 0 otherwise
  43 */
  44static u32 pde_get_multiple(u32 val, u32 fraction, u32 tolerance)
  45{
  46        u32 remainder;
  47        u32 factor;
  48        u32 delta;
  49
  50        if (fraction == 0)
  51                return 0;
  52
  53        delta = (val < fraction) ? (fraction - val) : (val - fraction);
  54
  55        if (delta <= tolerance)
  56                /* val and fraction are within tolerance */
  57                return 1;
  58
  59        factor = val / fraction;
  60        remainder = val % fraction;
  61        if (remainder > tolerance) {
  62                /* no exact match */
  63                if ((fraction - remainder) <= tolerance)
  64                        /* remainder is within tolerance */
  65                        factor++;
  66                else
  67                        factor = 0;
  68        }
  69        return factor;
  70}
  71
  72/*
  73 * DOC: Singleton Pulse and Sequence Pools
  74 *
  75 * Instances of pri_sequence and pulse_elem are kept in singleton pools to
  76 * reduce the number of dynamic allocations. They are shared between all
  77 * instances and grow up to the peak number of simultaneously used objects.
  78 *
  79 * Memory is freed after all references to the pools are released.
  80 */
  81static u32 singleton_pool_references;
  82static LIST_HEAD(pulse_pool);
  83static LIST_HEAD(pseq_pool);
  84static DEFINE_SPINLOCK(pool_lock);
  85
  86static void pool_register_ref(void)
  87{
  88        spin_lock_bh(&pool_lock);
  89        singleton_pool_references++;
  90        DFS_POOL_STAT_INC(pool_reference);
  91        spin_unlock_bh(&pool_lock);
  92}
  93
  94static void pool_deregister_ref(void)
  95{
  96        spin_lock_bh(&pool_lock);
  97        singleton_pool_references--;
  98        DFS_POOL_STAT_DEC(pool_reference);
  99        if (singleton_pool_references == 0) {
 100                /* free singleton pools with no references left */
 101                struct pri_sequence *ps, *ps0;
 102                struct pulse_elem *p, *p0;
 103
 104                list_for_each_entry_safe(p, p0, &pulse_pool, head) {
 105                        list_del(&p->head);
 106                        DFS_POOL_STAT_DEC(pulse_allocated);
 107                        kfree(p);
 108                }
 109                list_for_each_entry_safe(ps, ps0, &pseq_pool, head) {
 110                        list_del(&ps->head);
 111                        DFS_POOL_STAT_DEC(pseq_allocated);
 112                        kfree(ps);
 113                }
 114        }
 115        spin_unlock_bh(&pool_lock);
 116}
 117
 118static void pool_put_pulse_elem(struct pulse_elem *pe)
 119{
 120        spin_lock_bh(&pool_lock);
 121        list_add(&pe->head, &pulse_pool);
 122        DFS_POOL_STAT_DEC(pulse_used);
 123        spin_unlock_bh(&pool_lock);
 124}
 125
 126static void pool_put_pseq_elem(struct pri_sequence *pse)
 127{
 128        spin_lock_bh(&pool_lock);
 129        list_add(&pse->head, &pseq_pool);
 130        DFS_POOL_STAT_DEC(pseq_used);
 131        spin_unlock_bh(&pool_lock);
 132}
 133
 134static struct pri_sequence *pool_get_pseq_elem(void)
 135{
 136        struct pri_sequence *pse = NULL;
 137        spin_lock_bh(&pool_lock);
 138        if (!list_empty(&pseq_pool)) {
 139                pse = list_first_entry(&pseq_pool, struct pri_sequence, head);
 140                list_del(&pse->head);
 141                DFS_POOL_STAT_INC(pseq_used);
 142        }
 143        spin_unlock_bh(&pool_lock);
 144        return pse;
 145}
 146
 147static struct pulse_elem *pool_get_pulse_elem(void)
 148{
 149        struct pulse_elem *pe = NULL;
 150        spin_lock_bh(&pool_lock);
 151        if (!list_empty(&pulse_pool)) {
 152                pe = list_first_entry(&pulse_pool, struct pulse_elem, head);
 153                list_del(&pe->head);
 154                DFS_POOL_STAT_INC(pulse_used);
 155        }
 156        spin_unlock_bh(&pool_lock);
 157        return pe;
 158}
 159
 160static struct pulse_elem *pulse_queue_get_tail(struct pri_detector *pde)
 161{
 162        struct list_head *l = &pde->pulses;
 163        if (list_empty(l))
 164                return NULL;
 165        return list_entry(l->prev, struct pulse_elem, head);
 166}
 167
 168static bool pulse_queue_dequeue(struct pri_detector *pde)
 169{
 170        struct pulse_elem *p = pulse_queue_get_tail(pde);
 171        if (p != NULL) {
 172                list_del_init(&p->head);
 173                pde->count--;
 174                /* give it back to pool */
 175                pool_put_pulse_elem(p);
 176        }
 177        return (pde->count > 0);
 178}
 179
 180/* remove pulses older than window */
 181static void pulse_queue_check_window(struct pri_detector *pde)
 182{
 183        u64 min_valid_ts;
 184        struct pulse_elem *p;
 185
 186        /* there is no delta time with less than 2 pulses */
 187        if (pde->count < 2)
 188                return;
 189
 190        if (pde->last_ts <= pde->window_size)
 191                return;
 192
 193        min_valid_ts = pde->last_ts - pde->window_size;
 194        while ((p = pulse_queue_get_tail(pde)) != NULL) {
 195                if (p->ts >= min_valid_ts)
 196                        return;
 197                pulse_queue_dequeue(pde);
 198        }
 199}
 200
 201static bool pulse_queue_enqueue(struct pri_detector *pde, u64 ts)
 202{
 203        struct pulse_elem *p = pool_get_pulse_elem();
 204        if (p == NULL) {
 205                p = kmalloc(sizeof(*p), GFP_ATOMIC);
 206                if (p == NULL) {
 207                        DFS_POOL_STAT_INC(pulse_alloc_error);
 208                        return false;
 209                }
 210                DFS_POOL_STAT_INC(pulse_allocated);
 211                DFS_POOL_STAT_INC(pulse_used);
 212        }
 213        INIT_LIST_HEAD(&p->head);
 214        p->ts = ts;
 215        list_add(&p->head, &pde->pulses);
 216        pde->count++;
 217        pde->last_ts = ts;
 218        pulse_queue_check_window(pde);
 219        if (pde->count >= pde->max_count)
 220                pulse_queue_dequeue(pde);
 221        return true;
 222}
 223
 224static bool pseq_handler_create_sequences(struct pri_detector *pde,
 225                                          u64 ts, u32 min_count)
 226{
 227        struct pulse_elem *p;
 228        list_for_each_entry(p, &pde->pulses, head) {
 229                struct pri_sequence ps, *new_ps;
 230                struct pulse_elem *p2;
 231                u32 tmp_false_count;
 232                u64 min_valid_ts;
 233                u32 delta_ts = ts - p->ts;
 234
 235                if (delta_ts < pde->rs->pri_min)
 236                        /* ignore too small pri */
 237                        continue;
 238
 239                if (delta_ts > pde->rs->pri_max)
 240                        /* stop on too large pri (sorted list) */
 241                        break;
 242
 243                /* build a new sequence with new potential pri */
 244                ps.count = 2;
 245                ps.count_falses = 0;
 246                ps.first_ts = p->ts;
 247                ps.last_ts = ts;
 248                ps.pri = GET_PRI_TO_USE(pde->rs->pri_min,
 249                        pde->rs->pri_max, ts - p->ts);
 250                ps.dur = ps.pri * (pde->rs->ppb - 1)
 251                                + 2 * pde->rs->max_pri_tolerance;
 252
 253                p2 = p;
 254                tmp_false_count = 0;
 255                min_valid_ts = ts - ps.dur;
 256                /* check which past pulses are candidates for new sequence */
 257                list_for_each_entry_continue(p2, &pde->pulses, head) {
 258                        u32 factor;
 259                        if (p2->ts < min_valid_ts)
 260                                /* stop on crossing window border */
 261                                break;
 262                        /* check if pulse match (multi)PRI */
 263                        factor = pde_get_multiple(ps.last_ts - p2->ts, ps.pri,
 264                                                  pde->rs->max_pri_tolerance);
 265                        if (factor > 0) {
 266                                ps.count++;
 267                                ps.first_ts = p2->ts;
 268                                /*
 269                                 * on match, add the intermediate falses
 270                                 * and reset counter
 271                                 */
 272                                ps.count_falses += tmp_false_count;
 273                                tmp_false_count = 0;
 274                        } else {
 275                                /* this is a potential false one */
 276                                tmp_false_count++;
 277                        }
 278                }
 279                if (ps.count <= min_count)
 280                        /* did not reach minimum count, drop sequence */
 281                        continue;
 282
 283                /* this is a valid one, add it */
 284                ps.deadline_ts = ps.first_ts + ps.dur;
 285                new_ps = pool_get_pseq_elem();
 286                if (new_ps == NULL) {
 287                        new_ps = kmalloc(sizeof(*new_ps), GFP_ATOMIC);
 288                        if (new_ps == NULL) {
 289                                DFS_POOL_STAT_INC(pseq_alloc_error);
 290                                return false;
 291                        }
 292                        DFS_POOL_STAT_INC(pseq_allocated);
 293                        DFS_POOL_STAT_INC(pseq_used);
 294                }
 295                memcpy(new_ps, &ps, sizeof(ps));
 296                INIT_LIST_HEAD(&new_ps->head);
 297                list_add(&new_ps->head, &pde->sequences);
 298        }
 299        return true;
 300}
 301
 302/* check new ts and add to all matching existing sequences */
 303static u32
 304pseq_handler_add_to_existing_seqs(struct pri_detector *pde, u64 ts)
 305{
 306        u32 max_count = 0;
 307        struct pri_sequence *ps, *ps2;
 308        list_for_each_entry_safe(ps, ps2, &pde->sequences, head) {
 309                u32 delta_ts;
 310                u32 factor;
 311
 312                /* first ensure that sequence is within window */
 313                if (ts > ps->deadline_ts) {
 314                        list_del_init(&ps->head);
 315                        pool_put_pseq_elem(ps);
 316                        continue;
 317                }
 318
 319                delta_ts = ts - ps->last_ts;
 320                factor = pde_get_multiple(delta_ts, ps->pri,
 321                                          pde->rs->max_pri_tolerance);
 322                if (factor > 0) {
 323                        ps->last_ts = ts;
 324                        ps->count++;
 325
 326                        if (max_count < ps->count)
 327                                max_count = ps->count;
 328                } else {
 329                        ps->count_falses++;
 330                }
 331        }
 332        return max_count;
 333}
 334
 335static struct pri_sequence *
 336pseq_handler_check_detection(struct pri_detector *pde)
 337{
 338        struct pri_sequence *ps;
 339
 340        if (list_empty(&pde->sequences))
 341                return NULL;
 342
 343        list_for_each_entry(ps, &pde->sequences, head) {
 344                /*
 345                 * we assume to have enough matching confidence if we
 346                 * 1) have enough pulses
 347                 * 2) have more matching than false pulses
 348                 */
 349                if ((ps->count >= pde->rs->ppb_thresh) &&
 350                    (ps->count * pde->rs->num_pri >= ps->count_falses))
 351                        return ps;
 352        }
 353        return NULL;
 354}
 355
 356
 357/* free pulse queue and sequences list and give objects back to pools */
 358static void pri_detector_reset(struct pri_detector *pde, u64 ts)
 359{
 360        struct pri_sequence *ps, *ps0;
 361        struct pulse_elem *p, *p0;
 362        list_for_each_entry_safe(ps, ps0, &pde->sequences, head) {
 363                list_del_init(&ps->head);
 364                pool_put_pseq_elem(ps);
 365        }
 366        list_for_each_entry_safe(p, p0, &pde->pulses, head) {
 367                list_del_init(&p->head);
 368                pool_put_pulse_elem(p);
 369        }
 370        pde->count = 0;
 371        pde->last_ts = ts;
 372}
 373
 374static void pri_detector_exit(struct pri_detector *de)
 375{
 376        pri_detector_reset(de, 0);
 377        pool_deregister_ref();
 378        kfree(de);
 379}
 380
 381static struct pri_sequence *pri_detector_add_pulse(struct pri_detector *de,
 382                                                   struct pulse_event *event)
 383{
 384        u32 max_updated_seq;
 385        struct pri_sequence *ps;
 386        u64 ts = event->ts;
 387        const struct radar_detector_specs *rs = de->rs;
 388
 389        /* ignore pulses not within width range */
 390        if ((rs->width_min > event->width) || (rs->width_max < event->width))
 391                return NULL;
 392
 393        if ((ts - de->last_ts) < rs->max_pri_tolerance)
 394                /* if delta to last pulse is too short, don't use this pulse */
 395                return NULL;
 396        /* radar detector spec needs chirp, but not detected */
 397        if (rs->chirp && rs->chirp != event->chirp)
 398                return NULL;
 399
 400        de->last_ts = ts;
 401
 402        max_updated_seq = pseq_handler_add_to_existing_seqs(de, ts);
 403
 404        if (!pseq_handler_create_sequences(de, ts, max_updated_seq)) {
 405                pri_detector_reset(de, ts);
 406                return NULL;
 407        }
 408
 409        ps = pseq_handler_check_detection(de);
 410
 411        if (ps == NULL)
 412                pulse_queue_enqueue(de, ts);
 413
 414        return ps;
 415}
 416
 417struct pri_detector *pri_detector_init(const struct radar_detector_specs *rs)
 418{
 419        struct pri_detector *de;
 420
 421        de = kzalloc(sizeof(*de), GFP_ATOMIC);
 422        if (de == NULL)
 423                return NULL;
 424        de->exit = pri_detector_exit;
 425        de->add_pulse = pri_detector_add_pulse;
 426        de->reset = pri_detector_reset;
 427
 428        INIT_LIST_HEAD(&de->sequences);
 429        INIT_LIST_HEAD(&de->pulses);
 430        de->window_size = rs->pri_max * rs->ppb * rs->num_pri;
 431        de->max_count = rs->ppb * 2;
 432        de->rs = rs;
 433
 434        pool_register_ref();
 435        return de;
 436}
 437