linux/net/wimax/stack.c
<<
>>
Prefs
   1/*
   2 * Linux WiMAX
   3 * Initialization, addition and removal of wimax devices
   4 *
   5 *
   6 * Copyright (C) 2005-2006 Intel Corporation <linux-wimax@intel.com>
   7 * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
   8 *
   9 * This program is free software; you can redistribute it and/or
  10 * modify it under the terms of the GNU General Public License version
  11 * 2 as published by the Free Software Foundation.
  12 *
  13 * This program is distributed in the hope that it will be useful,
  14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  16 * GNU General Public License for more details.
  17 *
  18 * You should have received a copy of the GNU General Public License
  19 * along with this program; if not, write to the Free Software
  20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  21 * 02110-1301, USA.
  22 *
  23 *
  24 * This implements:
  25 *
  26 *   - basic life cycle of 'struct wimax_dev' [wimax_dev_*()]; on
  27 *     addition/registration initialize all subfields and allocate
  28 *     generic netlink resources for user space communication. On
  29 *     removal/unregistration, undo all that.
  30 *
  31 *   - device state machine [wimax_state_change()] and support to send
  32 *     reports to user space when the state changes
  33 *     [wimax_gnl_re_state_change*()].
  34 *
  35 * See include/net/wimax.h for rationales and design.
  36 *
  37 * ROADMAP
  38 *
  39 * [__]wimax_state_change()     Called by drivers to update device's state
  40 *   wimax_gnl_re_state_change_alloc()
  41 *   wimax_gnl_re_state_change_send()
  42 *
  43 * wimax_dev_init()             Init a device
  44 * wimax_dev_add()              Register
  45 *   wimax_rfkill_add()
  46 *   wimax_gnl_add()            Register all the generic netlink resources.
  47 *   wimax_id_table_add()
  48 * wimax_dev_rm()               Unregister
  49 *   wimax_id_table_rm()
  50 *   wimax_gnl_rm()
  51 *   wimax_rfkill_rm()
  52 */
  53#include <linux/device.h>
  54#include <linux/gfp.h>
  55#include <net/genetlink.h>
  56#include <linux/netdevice.h>
  57#include <linux/wimax.h>
  58#include <linux/module.h>
  59#include "wimax-internal.h"
  60
  61
  62#define D_SUBMODULE stack
  63#include "debug-levels.h"
  64
  65static char wimax_debug_params[128];
  66module_param_string(debug, wimax_debug_params, sizeof(wimax_debug_params),
  67                    0644);
  68MODULE_PARM_DESC(debug,
  69                 "String of space-separated NAME:VALUE pairs, where NAMEs "
  70                 "are the different debug submodules and VALUE are the "
  71                 "initial debug value to set.");
  72
  73/*
  74 * Authoritative source for the RE_STATE_CHANGE attribute policy
  75 *
  76 * We don't really use it here, but /me likes to keep the definition
  77 * close to where the data is generated.
  78 */
  79/*
  80static const struct nla_policy wimax_gnl_re_status_change[WIMAX_GNL_ATTR_MAX + 1] = {
  81        [WIMAX_GNL_STCH_STATE_OLD] = { .type = NLA_U8 },
  82        [WIMAX_GNL_STCH_STATE_NEW] = { .type = NLA_U8 },
  83};
  84*/
  85
  86
  87/*
  88 * Allocate a Report State Change message
  89 *
  90 * @header: save it, you need it for _send()
  91 *
  92 * Creates and fills a basic state change message; different code
  93 * paths can then add more attributes to the message as needed.
  94 *
  95 * Use wimax_gnl_re_state_change_send() to send the returned skb.
  96 *
  97 * Returns: skb with the genl message if ok, IS_ERR() ptr on error
  98 *     with an errno code.
  99 */
 100static
 101struct sk_buff *wimax_gnl_re_state_change_alloc(
 102        struct wimax_dev *wimax_dev,
 103        enum wimax_st new_state, enum wimax_st old_state,
 104        void **header)
 105{
 106        int result;
 107        struct device *dev = wimax_dev_to_dev(wimax_dev);
 108        void *data;
 109        struct sk_buff *report_skb;
 110
 111        d_fnstart(3, dev, "(wimax_dev %p new_state %u old_state %u)\n",
 112                  wimax_dev, new_state, old_state);
 113        result = -ENOMEM;
 114        report_skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
 115        if (report_skb == NULL) {
 116                dev_err(dev, "RE_STCH: can't create message\n");
 117                goto error_new;
 118        }
 119        data = genlmsg_put(report_skb, 0, wimax_gnl_mcg.id, &wimax_gnl_family,
 120                           0, WIMAX_GNL_RE_STATE_CHANGE);
 121        if (data == NULL) {
 122                dev_err(dev, "RE_STCH: can't put data into message\n");
 123                goto error_put;
 124        }
 125        *header = data;
 126
 127        result = nla_put_u8(report_skb, WIMAX_GNL_STCH_STATE_OLD, old_state);
 128        if (result < 0) {
 129                dev_err(dev, "RE_STCH: Error adding OLD attr: %d\n", result);
 130                goto error_put;
 131        }
 132        result = nla_put_u8(report_skb, WIMAX_GNL_STCH_STATE_NEW, new_state);
 133        if (result < 0) {
 134                dev_err(dev, "RE_STCH: Error adding NEW attr: %d\n", result);
 135                goto error_put;
 136        }
 137        result = nla_put_u32(report_skb, WIMAX_GNL_STCH_IFIDX,
 138                             wimax_dev->net_dev->ifindex);
 139        if (result < 0) {
 140                dev_err(dev, "RE_STCH: Error adding IFINDEX attribute\n");
 141                goto error_put;
 142        }
 143        d_fnend(3, dev, "(wimax_dev %p new_state %u old_state %u) = %p\n",
 144                wimax_dev, new_state, old_state, report_skb);
 145        return report_skb;
 146
 147error_put:
 148        nlmsg_free(report_skb);
 149error_new:
 150        d_fnend(3, dev, "(wimax_dev %p new_state %u old_state %u) = %d\n",
 151                wimax_dev, new_state, old_state, result);
 152        return ERR_PTR(result);
 153}
 154
 155
 156/*
 157 * Send a Report State Change message (as created with _alloc).
 158 *
 159 * @report_skb: as returned by wimax_gnl_re_state_change_alloc()
 160 * @header: as returned by wimax_gnl_re_state_change_alloc()
 161 *
 162 * Returns: 0 if ok, < 0 errno code on error.
 163 *
 164 * If the message is  NULL, pretend it didn't happen.
 165 */
 166static
 167int wimax_gnl_re_state_change_send(
 168        struct wimax_dev *wimax_dev, struct sk_buff *report_skb,
 169        void *header)
 170{
 171        int result = 0;
 172        struct device *dev = wimax_dev_to_dev(wimax_dev);
 173        d_fnstart(3, dev, "(wimax_dev %p report_skb %p)\n",
 174                  wimax_dev, report_skb);
 175        if (report_skb == NULL) {
 176                result = -ENOMEM;
 177                goto out;
 178        }
 179        genlmsg_end(report_skb, header);
 180        genlmsg_multicast(report_skb, 0, wimax_gnl_mcg.id, GFP_KERNEL);
 181out:
 182        d_fnend(3, dev, "(wimax_dev %p report_skb %p) = %d\n",
 183                wimax_dev, report_skb, result);
 184        return result;
 185}
 186
 187
 188static
 189void __check_new_state(enum wimax_st old_state, enum wimax_st new_state,
 190                       unsigned int allowed_states_bm)
 191{
 192        if (WARN_ON(((1 << new_state) & allowed_states_bm) == 0)) {
 193                printk(KERN_ERR "SW BUG! Forbidden state change %u -> %u\n",
 194                        old_state, new_state);
 195        }
 196}
 197
 198
 199/*
 200 * Set the current state of a WiMAX device [unlocking version of
 201 * wimax_state_change().
 202 */
 203void __wimax_state_change(struct wimax_dev *wimax_dev, enum wimax_st new_state)
 204{
 205        struct device *dev = wimax_dev_to_dev(wimax_dev);
 206        enum wimax_st old_state = wimax_dev->state;
 207        struct sk_buff *stch_skb;
 208        void *header;
 209
 210        d_fnstart(3, dev, "(wimax_dev %p new_state %u [old %u])\n",
 211                  wimax_dev, new_state, old_state);
 212
 213        if (WARN_ON(new_state >= __WIMAX_ST_INVALID)) {
 214                dev_err(dev, "SW BUG: requesting invalid state %u\n",
 215                        new_state);
 216                goto out;
 217        }
 218        if (old_state == new_state)
 219                goto out;
 220        header = NULL;  /* gcc complains? can't grok why */
 221        stch_skb = wimax_gnl_re_state_change_alloc(
 222                wimax_dev, new_state, old_state, &header);
 223
 224        /* Verify the state transition and do exit-from-state actions */
 225        switch (old_state) {
 226        case __WIMAX_ST_NULL:
 227                __check_new_state(old_state, new_state,
 228                                  1 << WIMAX_ST_DOWN);
 229                break;
 230        case WIMAX_ST_DOWN:
 231                __check_new_state(old_state, new_state,
 232                                  1 << __WIMAX_ST_QUIESCING
 233                                  | 1 << WIMAX_ST_UNINITIALIZED
 234                                  | 1 << WIMAX_ST_RADIO_OFF);
 235                break;
 236        case __WIMAX_ST_QUIESCING:
 237                __check_new_state(old_state, new_state, 1 << WIMAX_ST_DOWN);
 238                break;
 239        case WIMAX_ST_UNINITIALIZED:
 240                __check_new_state(old_state, new_state,
 241                                  1 << __WIMAX_ST_QUIESCING
 242                                  | 1 << WIMAX_ST_RADIO_OFF);
 243                break;
 244        case WIMAX_ST_RADIO_OFF:
 245                __check_new_state(old_state, new_state,
 246                                  1 << __WIMAX_ST_QUIESCING
 247                                  | 1 << WIMAX_ST_READY);
 248                break;
 249        case WIMAX_ST_READY:
 250                __check_new_state(old_state, new_state,
 251                                  1 << __WIMAX_ST_QUIESCING
 252                                  | 1 << WIMAX_ST_RADIO_OFF
 253                                  | 1 << WIMAX_ST_SCANNING
 254                                  | 1 << WIMAX_ST_CONNECTING
 255                                  | 1 << WIMAX_ST_CONNECTED);
 256                break;
 257        case WIMAX_ST_SCANNING:
 258                __check_new_state(old_state, new_state,
 259                                  1 << __WIMAX_ST_QUIESCING
 260                                  | 1 << WIMAX_ST_RADIO_OFF
 261                                  | 1 << WIMAX_ST_READY
 262                                  | 1 << WIMAX_ST_CONNECTING
 263                                  | 1 << WIMAX_ST_CONNECTED);
 264                break;
 265        case WIMAX_ST_CONNECTING:
 266                __check_new_state(old_state, new_state,
 267                                  1 << __WIMAX_ST_QUIESCING
 268                                  | 1 << WIMAX_ST_RADIO_OFF
 269                                  | 1 << WIMAX_ST_READY
 270                                  | 1 << WIMAX_ST_SCANNING
 271                                  | 1 << WIMAX_ST_CONNECTED);
 272                break;
 273        case WIMAX_ST_CONNECTED:
 274                __check_new_state(old_state, new_state,
 275                                  1 << __WIMAX_ST_QUIESCING
 276                                  | 1 << WIMAX_ST_RADIO_OFF
 277                                  | 1 << WIMAX_ST_READY);
 278                netif_tx_disable(wimax_dev->net_dev);
 279                netif_carrier_off(wimax_dev->net_dev);
 280                break;
 281        case __WIMAX_ST_INVALID:
 282        default:
 283                dev_err(dev, "SW BUG: wimax_dev %p is in unknown state %u\n",
 284                        wimax_dev, wimax_dev->state);
 285                WARN_ON(1);
 286                goto out;
 287        }
 288
 289        /* Execute the actions of entry to the new state */
 290        switch (new_state) {
 291        case __WIMAX_ST_NULL:
 292                dev_err(dev, "SW BUG: wimax_dev %p entering NULL state "
 293                        "from %u\n", wimax_dev, wimax_dev->state);
 294                WARN_ON(1);             /* Nobody can enter this state */
 295                break;
 296        case WIMAX_ST_DOWN:
 297                break;
 298        case __WIMAX_ST_QUIESCING:
 299                break;
 300        case WIMAX_ST_UNINITIALIZED:
 301                break;
 302        case WIMAX_ST_RADIO_OFF:
 303                break;
 304        case WIMAX_ST_READY:
 305                break;
 306        case WIMAX_ST_SCANNING:
 307                break;
 308        case WIMAX_ST_CONNECTING:
 309                break;
 310        case WIMAX_ST_CONNECTED:
 311                netif_carrier_on(wimax_dev->net_dev);
 312                netif_wake_queue(wimax_dev->net_dev);
 313                break;
 314        case __WIMAX_ST_INVALID:
 315        default:
 316                BUG();
 317        }
 318        __wimax_state_set(wimax_dev, new_state);
 319        if (!IS_ERR(stch_skb))
 320                wimax_gnl_re_state_change_send(wimax_dev, stch_skb, header);
 321out:
 322        d_fnend(3, dev, "(wimax_dev %p new_state %u [old %u]) = void\n",
 323                wimax_dev, new_state, old_state);
 324}
 325
 326
 327/**
 328 * wimax_state_change - Set the current state of a WiMAX device
 329 *
 330 * @wimax_dev: WiMAX device descriptor (properly referenced)
 331 * @new_state: New state to switch to
 332 *
 333 * This implements the state changes for the wimax devices. It will
 334 *
 335 * - verify that the state transition is legal (for now it'll just
 336 *   print a warning if not) according to the table in
 337 *   linux/wimax.h's documentation for 'enum wimax_st'.
 338 *
 339 * - perform the actions needed for leaving the current state and
 340 *   whichever are needed for entering the new state.
 341 *
 342 * - issue a report to user space indicating the new state (and an
 343 *   optional payload with information about the new state).
 344 *
 345 * NOTE: @wimax_dev must be locked
 346 */
 347void wimax_state_change(struct wimax_dev *wimax_dev, enum wimax_st new_state)
 348{
 349        /*
 350         * A driver cannot take the wimax_dev out of the
 351         * __WIMAX_ST_NULL state unless by calling wimax_dev_add(). If
 352         * the wimax_dev's state is still NULL, we ignore any request
 353         * to change its state because it means it hasn't been yet
 354         * registered.
 355         *
 356         * There is no need to complain about it, as routines that
 357         * call this might be shared from different code paths that
 358         * are called before or after wimax_dev_add() has done its
 359         * job.
 360         */
 361        mutex_lock(&wimax_dev->mutex);
 362        if (wimax_dev->state > __WIMAX_ST_NULL)
 363                __wimax_state_change(wimax_dev, new_state);
 364        mutex_unlock(&wimax_dev->mutex);
 365}
 366EXPORT_SYMBOL_GPL(wimax_state_change);
 367
 368
 369/**
 370 * wimax_state_get() - Return the current state of a WiMAX device
 371 *
 372 * @wimax_dev: WiMAX device descriptor
 373 *
 374 * Returns: Current state of the device according to its driver.
 375 */
 376enum wimax_st wimax_state_get(struct wimax_dev *wimax_dev)
 377{
 378        enum wimax_st state;
 379        mutex_lock(&wimax_dev->mutex);
 380        state = wimax_dev->state;
 381        mutex_unlock(&wimax_dev->mutex);
 382        return state;
 383}
 384EXPORT_SYMBOL_GPL(wimax_state_get);
 385
 386
 387/**
 388 * wimax_dev_init - initialize a newly allocated instance
 389 *
 390 * @wimax_dev: WiMAX device descriptor to initialize.
 391 *
 392 * Initializes fields of a freshly allocated @wimax_dev instance. This
 393 * function assumes that after allocation, the memory occupied by
 394 * @wimax_dev was zeroed.
 395 */
 396void wimax_dev_init(struct wimax_dev *wimax_dev)
 397{
 398        INIT_LIST_HEAD(&wimax_dev->id_table_node);
 399        __wimax_state_set(wimax_dev, __WIMAX_ST_NULL);
 400        mutex_init(&wimax_dev->mutex);
 401        mutex_init(&wimax_dev->mutex_reset);
 402}
 403EXPORT_SYMBOL_GPL(wimax_dev_init);
 404
 405/*
 406 * This extern is declared here because it's easier to keep track --
 407 * both declarations are a list of the same
 408 */
 409extern struct genl_ops
 410        wimax_gnl_msg_from_user,
 411        wimax_gnl_reset,
 412        wimax_gnl_rfkill,
 413        wimax_gnl_state_get;
 414
 415static
 416struct genl_ops *wimax_gnl_ops[] = {
 417        &wimax_gnl_msg_from_user,
 418        &wimax_gnl_reset,
 419        &wimax_gnl_rfkill,
 420        &wimax_gnl_state_get,
 421};
 422
 423
 424static
 425size_t wimax_addr_scnprint(char *addr_str, size_t addr_str_size,
 426                           unsigned char *addr, size_t addr_len)
 427{
 428        unsigned int cnt, total;
 429
 430        for (total = cnt = 0; cnt < addr_len; cnt++)
 431                total += scnprintf(addr_str + total, addr_str_size - total,
 432                                   "%02x%c", addr[cnt],
 433                                   cnt == addr_len - 1 ? '\0' : ':');
 434        return total;
 435}
 436
 437
 438/**
 439 * wimax_dev_add - Register a new WiMAX device
 440 *
 441 * @wimax_dev: WiMAX device descriptor (as embedded in your @net_dev's
 442 *     priv data). You must have called wimax_dev_init() on it before.
 443 *
 444 * @net_dev: net device the @wimax_dev is associated with. The
 445 *     function expects SET_NETDEV_DEV() and register_netdev() were
 446 *     already called on it.
 447 *
 448 * Registers the new WiMAX device, sets up the user-kernel control
 449 * interface (generic netlink) and common WiMAX infrastructure.
 450 *
 451 * Note that the parts that will allow interaction with user space are
 452 * setup at the very end, when the rest is in place, as once that
 453 * happens, the driver might get user space control requests via
 454 * netlink or from debugfs that might translate into calls into
 455 * wimax_dev->op_*().
 456 */
 457int wimax_dev_add(struct wimax_dev *wimax_dev, struct net_device *net_dev)
 458{
 459        int result;
 460        struct device *dev = net_dev->dev.parent;
 461        char addr_str[32];
 462
 463        d_fnstart(3, dev, "(wimax_dev %p net_dev %p)\n", wimax_dev, net_dev);
 464
 465        /* Do the RFKILL setup before locking, as RFKILL will call
 466         * into our functions. */
 467        wimax_dev->net_dev = net_dev;
 468        result = wimax_rfkill_add(wimax_dev);
 469        if (result < 0)
 470                goto error_rfkill_add;
 471
 472        /* Set up user-space interaction */
 473        mutex_lock(&wimax_dev->mutex);
 474        wimax_id_table_add(wimax_dev);
 475        result = wimax_debugfs_add(wimax_dev);
 476        if (result < 0) {
 477                dev_err(dev, "cannot initialize debugfs: %d\n",
 478                        result);
 479                goto error_debugfs_add;
 480        }
 481
 482        __wimax_state_set(wimax_dev, WIMAX_ST_DOWN);
 483        mutex_unlock(&wimax_dev->mutex);
 484
 485        wimax_addr_scnprint(addr_str, sizeof(addr_str),
 486                            net_dev->dev_addr, net_dev->addr_len);
 487        dev_err(dev, "WiMAX interface %s (%s) ready\n",
 488                net_dev->name, addr_str);
 489        d_fnend(3, dev, "(wimax_dev %p net_dev %p) = 0\n", wimax_dev, net_dev);
 490        return 0;
 491
 492error_debugfs_add:
 493        wimax_id_table_rm(wimax_dev);
 494        mutex_unlock(&wimax_dev->mutex);
 495        wimax_rfkill_rm(wimax_dev);
 496error_rfkill_add:
 497        d_fnend(3, dev, "(wimax_dev %p net_dev %p) = %d\n",
 498                wimax_dev, net_dev, result);
 499        return result;
 500}
 501EXPORT_SYMBOL_GPL(wimax_dev_add);
 502
 503
 504/**
 505 * wimax_dev_rm - Unregister an existing WiMAX device
 506 *
 507 * @wimax_dev: WiMAX device descriptor
 508 *
 509 * Unregisters a WiMAX device previously registered for use with
 510 * wimax_add_rm().
 511 *
 512 * IMPORTANT! Must call before calling unregister_netdev().
 513 *
 514 * After this function returns, you will not get any more user space
 515 * control requests (via netlink or debugfs) and thus to wimax_dev->ops.
 516 *
 517 * Reentrancy control is ensured by setting the state to
 518 * %__WIMAX_ST_QUIESCING. rfkill operations coming through
 519 * wimax_*rfkill*() will be stopped by the quiescing state; ops coming
 520 * from the rfkill subsystem will be stopped by the support being
 521 * removed by wimax_rfkill_rm().
 522 */
 523void wimax_dev_rm(struct wimax_dev *wimax_dev)
 524{
 525        d_fnstart(3, NULL, "(wimax_dev %p)\n", wimax_dev);
 526
 527        mutex_lock(&wimax_dev->mutex);
 528        __wimax_state_change(wimax_dev, __WIMAX_ST_QUIESCING);
 529        wimax_debugfs_rm(wimax_dev);
 530        wimax_id_table_rm(wimax_dev);
 531        __wimax_state_change(wimax_dev, WIMAX_ST_DOWN);
 532        mutex_unlock(&wimax_dev->mutex);
 533        wimax_rfkill_rm(wimax_dev);
 534        d_fnend(3, NULL, "(wimax_dev %p) = void\n", wimax_dev);
 535}
 536EXPORT_SYMBOL_GPL(wimax_dev_rm);
 537
 538
 539/* Debug framework control of debug levels */
 540struct d_level D_LEVEL[] = {
 541        D_SUBMODULE_DEFINE(debugfs),
 542        D_SUBMODULE_DEFINE(id_table),
 543        D_SUBMODULE_DEFINE(op_msg),
 544        D_SUBMODULE_DEFINE(op_reset),
 545        D_SUBMODULE_DEFINE(op_rfkill),
 546        D_SUBMODULE_DEFINE(op_state_get),
 547        D_SUBMODULE_DEFINE(stack),
 548};
 549size_t D_LEVEL_SIZE = ARRAY_SIZE(D_LEVEL);
 550
 551
 552struct genl_family wimax_gnl_family = {
 553        .id = GENL_ID_GENERATE,
 554        .name = "WiMAX",
 555        .version = WIMAX_GNL_VERSION,
 556        .hdrsize = 0,
 557        .maxattr = WIMAX_GNL_ATTR_MAX,
 558};
 559
 560struct genl_multicast_group wimax_gnl_mcg = {
 561        .name = "msg",
 562};
 563
 564
 565
 566/* Shutdown the wimax stack */
 567static
 568int __init wimax_subsys_init(void)
 569{
 570        int result, cnt;
 571
 572        d_fnstart(4, NULL, "()\n");
 573        d_parse_params(D_LEVEL, D_LEVEL_SIZE, wimax_debug_params,
 574                       "wimax.debug");
 575
 576        snprintf(wimax_gnl_family.name, sizeof(wimax_gnl_family.name),
 577                 "WiMAX");
 578        result = genl_register_family(&wimax_gnl_family);
 579        if (unlikely(result < 0)) {
 580                printk(KERN_ERR "cannot register generic netlink family: %d\n",
 581                       result);
 582                goto error_register_family;
 583        }
 584
 585        for (cnt = 0; cnt < ARRAY_SIZE(wimax_gnl_ops); cnt++) {
 586                result = genl_register_ops(&wimax_gnl_family,
 587                                           wimax_gnl_ops[cnt]);
 588                d_printf(4, NULL, "registering generic netlink op code "
 589                         "%u: %d\n", wimax_gnl_ops[cnt]->cmd, result);
 590                if (unlikely(result < 0)) {
 591                        printk(KERN_ERR "cannot register generic netlink op "
 592                               "code %u: %d\n",
 593                               wimax_gnl_ops[cnt]->cmd, result);
 594                        goto error_register_ops;
 595                }
 596        }
 597
 598        result = genl_register_mc_group(&wimax_gnl_family, &wimax_gnl_mcg);
 599        if (result < 0)
 600                goto error_mc_group;
 601        d_fnend(4, NULL, "() = 0\n");
 602        return 0;
 603
 604error_mc_group:
 605error_register_ops:
 606        for (cnt--; cnt >= 0; cnt--)
 607                genl_unregister_ops(&wimax_gnl_family,
 608                                    wimax_gnl_ops[cnt]);
 609        genl_unregister_family(&wimax_gnl_family);
 610error_register_family:
 611        d_fnend(4, NULL, "() = %d\n", result);
 612        return result;
 613
 614}
 615module_init(wimax_subsys_init);
 616
 617
 618/* Shutdown the wimax stack */
 619static
 620void __exit wimax_subsys_exit(void)
 621{
 622        int cnt;
 623        wimax_id_table_release();
 624        genl_unregister_mc_group(&wimax_gnl_family, &wimax_gnl_mcg);
 625        for (cnt = ARRAY_SIZE(wimax_gnl_ops) - 1; cnt >= 0; cnt--)
 626                genl_unregister_ops(&wimax_gnl_family,
 627                                    wimax_gnl_ops[cnt]);
 628        genl_unregister_family(&wimax_gnl_family);
 629}
 630module_exit(wimax_subsys_exit);
 631
 632MODULE_AUTHOR("Intel Corporation <linux-wimax@intel.com>");
 633MODULE_DESCRIPTION("Linux WiMAX stack");
 634MODULE_LICENSE("GPL");
 635
 636
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.