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