"Fossies" - the Fresh Open Source Software Archive

Member "ndiswrapper-1.63/driver/proc.c" (3 May 2020, 16918 Bytes) of package /linux/misc/ndiswrapper-1.63.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "proc.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 1.62_vs_1.63.

    1 /*
    2  *  Copyright (C) 2003-2005 Pontus Fuchs, Giridhar Pemmasani
    3  *
    4  *  This program is free software; you can redistribute it and/or modify
    5  *  it under the terms of the GNU General Public License as published by
    6  *  the Free Software Foundation; either version 2 of the License, or
    7  *  (at your option) any later version.
    8  *
    9  *  This program is distributed in the hope that it will be useful,
   10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
   11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
   12  *  GNU General Public License for more details.
   13  *
   14  */
   15 #include <linux/proc_fs.h>
   16 #include <linux/seq_file.h>
   17 #include <linux/module.h>
   18 #include <asm/uaccess.h>
   19 
   20 #include "ndis.h"
   21 #include "iw_ndis.h"
   22 #include "wrapndis.h"
   23 #include "pnp.h"
   24 #include "wrapper.h"
   25 
   26 #define MAX_PROC_STR_LEN 32
   27 
   28 #if LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0)
   29 static kuid_t proc_kuid;
   30 static kgid_t proc_kgid;
   31 #else
   32 #define proc_kuid proc_uid
   33 #define proc_kgid proc_gid
   34 #define kuid_t uid_t
   35 #define kgid_t gid_t
   36 #endif
   37 
   38 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)
   39 static inline struct inode *file_inode(struct file *f)
   40 {
   41     return f->f_dentry->d_inode;
   42 }
   43 #elif LINUX_VERSION_CODE < KERNEL_VERSION(3,9,0)
   44 static inline struct inode *file_inode(struct file *f)
   45 {
   46     return f->f_path.dentry->d_inode;
   47 }
   48 #endif
   49 
   50 #if LINUX_VERSION_CODE < KERNEL_VERSION(3,10,0)
   51 static inline void proc_set_user(struct proc_dir_entry *de, kuid_t uid,
   52                  kgid_t gid)
   53 {
   54     de->uid = uid;
   55     de->gid = gid;
   56 }
   57 
   58 static inline void proc_remove(struct proc_dir_entry *de)
   59 {
   60     if (de)
   61         remove_proc_entry(de->name, de->parent);
   62 }
   63 
   64 static inline void *PDE_DATA(const struct inode *inode)
   65 {
   66     return PDE(inode)->data;
   67 }
   68 #endif
   69 
   70 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,26)
   71 static inline struct proc_dir_entry *proc_create_data(const char *name,
   72     umode_t mode, struct proc_dir_entry *parent,
   73     struct file_operations *fops, void *data)
   74 {
   75     struct proc_dir_entry *de;
   76 
   77     de = create_proc_entry(name, mode, parent);
   78     if (de) {
   79         de->data = data;
   80         de->proc_fops = fops;
   81     }
   82 
   83     return de;
   84 }
   85 #endif
   86 
   87 static int do_proc_make_entry(const char *name, umode_t mode,
   88                   struct proc_dir_entry *parent,
   89 #if LINUX_VERSION_CODE >= KERNEL_VERSION(5,6,0)
   90                   const struct proc_ops *fops,
   91 #else
   92                   struct file_operations *fops,
   93 #endif
   94                   kuid_t uid, kgid_t gid, struct ndis_device *wnd)
   95 {
   96     struct proc_dir_entry *de;
   97 
   98     de = proc_create_data(name, mode, parent, fops, wnd);
   99     if (de == NULL) {
  100         ERROR("couldn't create proc entry for '%s'", name);
  101         return -ENOMEM;
  102     }
  103     proc_set_user(de, uid, gid);
  104     return 0;
  105 }
  106 
  107 #if LINUX_VERSION_CODE >= KERNEL_VERSION(5,6,0)
  108 #define PROC_DECLARE_RO(name) \
  109     static int proc_##name##_open(struct inode *inode, struct file *file) \
  110     { \
  111         return single_open(file, proc_##name##_read, PDE_DATA(inode)); \
  112     } \
  113     static const struct proc_ops name##_fops = { \
  114         .proc_open = proc_##name##_open, \
  115         .proc_read = seq_read, \
  116         .proc_lseek = seq_lseek, \
  117         .proc_release = single_release, \
  118     };
  119 
  120 #define PROC_DECLARE_RW(name) \
  121     static int proc_##name##_open(struct inode *inode, struct file *file) \
  122     { \
  123         return single_open(file, proc_##name##_read, PDE_DATA(inode)); \
  124     } \
  125     static const struct proc_ops name##_fops = { \
  126         .proc_open = proc_##name##_open, \
  127         .proc_read = seq_read, \
  128         .proc_lseek = seq_lseek, \
  129         .proc_release = single_release, \
  130         .proc_write = proc_##name##_write, \
  131     };
  132 #else
  133 #define PROC_DECLARE_RO(name) \
  134     static int proc_##name##_open(struct inode *inode, struct file *file) \
  135     { \
  136         return single_open(file, proc_##name##_read, PDE_DATA(inode)); \
  137     } \
  138     static struct file_operations name##_fops = { \
  139         .owner = THIS_MODULE, \
  140         .open = proc_##name##_open, \
  141         .read = seq_read, \
  142         .llseek = seq_lseek, \
  143         .release = single_release, \
  144     };
  145 
  146 #define PROC_DECLARE_RW(name) \
  147     static int proc_##name##_open(struct inode *inode, struct file *file) \
  148     { \
  149         return single_open(file, proc_##name##_read, PDE_DATA(inode)); \
  150     } \
  151     static struct file_operations name##_fops = { \
  152         .owner = THIS_MODULE, \
  153         .open = proc_##name##_open, \
  154         .read = seq_read, \
  155         .llseek = seq_lseek, \
  156         .release = single_release, \
  157         .write = proc_##name##_write, \
  158     };
  159 #endif
  160 
  161 #define proc_make_entry_ro(name, parent, wnd) \
  162     do_proc_make_entry(#name, S_IFREG | S_IRUSR | S_IRGRP, parent, \
  163                &name##_fops, proc_kuid, proc_kgid, wnd)
  164 #define proc_make_entry_rw(name, parent, wnd) \
  165     do_proc_make_entry(#name, \
  166                S_IFREG | S_IRUSR | S_IRGRP | S_IWUSR | S_IWGRP, \
  167                parent, &name##_fops, proc_kuid, proc_kgid, wnd)
  168 
  169 #define add_text(fmt, ...) seq_printf(sf, fmt, ##__VA_ARGS__)
  170 
  171 static struct proc_dir_entry *wrap_procfs_entry;
  172 
  173 static int proc_stats_read(struct seq_file *sf, void *v)
  174 {
  175     struct ndis_device *wnd = (struct ndis_device *)sf->private;
  176     struct ndis_wireless_stats stats;
  177     NDIS_STATUS res;
  178     ndis_rssi rssi;
  179 
  180     res = mp_query(wnd, OID_802_11_RSSI, &rssi, sizeof(rssi));
  181     if (!res)
  182         add_text("signal_level=%d dBm\n", (s32)rssi);
  183 
  184     res = mp_query(wnd, OID_802_11_STATISTICS, &stats, sizeof(stats));
  185     if (!res) {
  186         add_text("tx_frames=%llu\n", stats.tx_frag);
  187         add_text("tx_multicast_frames=%llu\n", stats.tx_multi_frag);
  188         add_text("tx_failed=%llu\n", stats.failed);
  189         add_text("tx_retry=%llu\n", stats.retry);
  190         add_text("tx_multi_retry=%llu\n", stats.multi_retry);
  191         add_text("tx_rtss_success=%llu\n", stats.rtss_succ);
  192         add_text("tx_rtss_fail=%llu\n", stats.rtss_fail);
  193         add_text("ack_fail=%llu\n", stats.ack_fail);
  194         add_text("frame_duplicates=%llu\n", stats.frame_dup);
  195         add_text("rx_frames=%llu\n", stats.rx_frag);
  196         add_text("rx_multicast_frames=%llu\n", stats.rx_multi_frag);
  197         add_text("fcs_errors=%llu\n", stats.fcs_err);
  198     }
  199 
  200     return 0;
  201 }
  202 
  203 PROC_DECLARE_RO(stats)
  204 
  205 static int proc_encr_read(struct seq_file *sf, void *v)
  206 {
  207     struct ndis_device *wnd = (struct ndis_device *)sf->private;
  208     int i, encr_status, auth_mode, infra_mode;
  209     NDIS_STATUS res;
  210     struct ndis_essid essid;
  211     mac_address ap_address;
  212 
  213     res = mp_query(wnd, OID_802_11_BSSID,
  214                &ap_address, sizeof(ap_address));
  215     if (res)
  216         memset(ap_address, 0, ETH_ALEN);
  217     add_text("ap_address=" MACSTRSEP "\n", MAC2STR(ap_address));
  218 
  219     res = mp_query(wnd, OID_802_11_SSID, &essid, sizeof(essid));
  220     if (!res)
  221         add_text("essid=%.*s\n", essid.length, essid.essid);
  222 
  223     res = mp_query_int(wnd, OID_802_11_ENCRYPTION_STATUS, &encr_status);
  224     if (!res) {
  225         typeof(&wnd->encr_info.keys[0]) tx_key;
  226         add_text("tx_key=%u\n", wnd->encr_info.tx_key_index);
  227         add_text("key=");
  228         tx_key = &wnd->encr_info.keys[wnd->encr_info.tx_key_index];
  229         if (tx_key->length > 0)
  230             for (i = 0; i < tx_key->length; i++)
  231                 add_text("%2.2X", tx_key->key[i]);
  232         else
  233             add_text("off");
  234         add_text("\n");
  235         add_text("encr_mode=%d\n", encr_status);
  236     }
  237     res = mp_query_int(wnd, OID_802_11_AUTHENTICATION_MODE, &auth_mode);
  238     if (!res)
  239         add_text("auth_mode=%d\n", auth_mode);
  240     res = mp_query_int(wnd, OID_802_11_INFRASTRUCTURE_MODE, &infra_mode);
  241     add_text("mode=%s\n", (infra_mode == Ndis802_11IBSS) ? "adhoc" :
  242          (infra_mode == Ndis802_11Infrastructure) ? "managed" : "auto");
  243 
  244     return 0;
  245 }
  246 
  247 PROC_DECLARE_RO(encr)
  248 
  249 static int proc_hw_read(struct seq_file *sf, void *v)
  250 {
  251     struct ndis_device *wnd = (struct ndis_device *)sf->private;
  252     struct ndis_configuration config;
  253     enum ndis_power power_mode;
  254     NDIS_STATUS res;
  255     ndis_tx_power_level tx_power;
  256     ULONG bit_rate;
  257     ndis_rts_threshold rts_threshold;
  258     ndis_fragmentation_threshold frag_threshold;
  259     ndis_antenna antenna;
  260     ULONG packet_filter;
  261     int n;
  262     mac_address mac;
  263     char *hw_status[] = {"ready", "initializing", "resetting", "closing",
  264                  "not ready"};
  265 
  266     res = mp_query_int(wnd, OID_GEN_HARDWARE_STATUS, &n);
  267     if (res == NDIS_STATUS_SUCCESS && n >= 0 && n < ARRAY_SIZE(hw_status))
  268         add_text("status=%s\n", hw_status[n]);
  269 
  270     res = mp_query(wnd, OID_802_3_CURRENT_ADDRESS, mac, sizeof(mac));
  271     if (!res)
  272         add_text("mac: " MACSTRSEP "\n", MAC2STR(mac));
  273     res = mp_query(wnd, OID_802_11_CONFIGURATION, &config, sizeof(config));
  274     if (!res) {
  275         add_text("beacon_period=%u msec\n", config.beacon_period);
  276         add_text("atim_window=%u msec\n", config.atim_window);
  277         add_text("frequency=%u kHz\n", config.ds_config);
  278         add_text("hop_pattern=%u\n", config.fh_config.hop_pattern);
  279         add_text("hop_set=%u\n", config.fh_config.hop_set);
  280         add_text("dwell_time=%u msec\n", config.fh_config.dwell_time);
  281     }
  282 
  283     res = mp_query(wnd, OID_802_11_TX_POWER_LEVEL,
  284                &tx_power, sizeof(tx_power));
  285     if (!res)
  286         add_text("tx_power=%u mW\n", tx_power);
  287 
  288     res = mp_query(wnd, OID_GEN_LINK_SPEED, &bit_rate, sizeof(bit_rate));
  289     if (!res)
  290         add_text("bit_rate=%u kBps\n", (u32)bit_rate / 10);
  291 
  292     res = mp_query(wnd, OID_802_11_RTS_THRESHOLD,
  293                &rts_threshold, sizeof(rts_threshold));
  294     if (!res)
  295         add_text("rts_threshold=%u bytes\n", rts_threshold);
  296 
  297     res = mp_query(wnd, OID_802_11_FRAGMENTATION_THRESHOLD,
  298                &frag_threshold, sizeof(frag_threshold));
  299     if (!res)
  300         add_text("frag_threshold=%u bytes\n", frag_threshold);
  301 
  302     res = mp_query_int(wnd, OID_802_11_POWER_MODE, &power_mode);
  303     if (!res)
  304         add_text("power_mode=%s\n",
  305              (power_mode == NDIS_POWER_OFF) ? "always_on" :
  306              (power_mode == NDIS_POWER_MAX) ? "max_savings" :
  307                               "min_savings");
  308 
  309     res = mp_query(wnd, OID_802_11_NUMBER_OF_ANTENNAS,
  310                &antenna, sizeof(antenna));
  311     if (!res)
  312         add_text("num_antennas=%u\n", antenna);
  313 
  314     res = mp_query(wnd, OID_802_11_TX_ANTENNA_SELECTED,
  315                &antenna, sizeof(antenna));
  316     if (!res)
  317         add_text("tx_antenna=%u\n", antenna);
  318 
  319     res = mp_query(wnd, OID_802_11_RX_ANTENNA_SELECTED,
  320                &antenna, sizeof(antenna));
  321     if (!res)
  322         add_text("rx_antenna=%u\n", antenna);
  323 
  324     add_text("encryption_modes=%s%s%s%s%s%s%s\n",
  325          test_bit(Ndis802_11Encryption1Enabled, &wnd->capa.encr) ?
  326          "WEP" : "none",
  327          test_bit(Ndis802_11Encryption2Enabled, &wnd->capa.encr) ?
  328          "; TKIP with WPA" : "",
  329          test_bit(Ndis802_11AuthModeWPA2, &wnd->capa.auth) ?
  330          ", WPA2" : "",
  331          test_bit(Ndis802_11AuthModeWPA2PSK, &wnd->capa.auth) ?
  332          ", WPA2PSK" : "",
  333          test_bit(Ndis802_11Encryption3Enabled, &wnd->capa.encr) ?
  334          "; AES/CCMP with WPA" : "",
  335          test_bit(Ndis802_11AuthModeWPA2, &wnd->capa.auth) ?
  336          ", WPA2" : "",
  337          test_bit(Ndis802_11AuthModeWPA2PSK, &wnd->capa.auth) ?
  338          ", WPA2PSK" : "");
  339 
  340     res = mp_query_int(wnd, OID_GEN_CURRENT_PACKET_FILTER, &packet_filter);
  341     if (!res) {
  342         if (packet_filter != wnd->packet_filter)
  343             WARNING("wrong packet_filter? 0x%08x, 0x%08x\n",
  344                 packet_filter, wnd->packet_filter);
  345         add_text("packet_filter: 0x%08x\n", packet_filter);
  346     }
  347 
  348     return 0;
  349 }
  350 
  351 PROC_DECLARE_RO(hw)
  352 
  353 static int proc_settings_read(struct seq_file *sf, void *v)
  354 {
  355     struct ndis_device *wnd = (struct ndis_device *)sf->private;
  356     struct wrap_device_setting *setting;
  357 
  358     add_text("hangcheck_interval=%d\n", (hangcheck_interval == 0) ?
  359          (wnd->hangcheck_interval / HZ) : -1);
  360 
  361     list_for_each_entry(setting, &wnd->wd->settings, list) {
  362         add_text("%s=%s\n", setting->name, setting->value);
  363     }
  364 
  365     list_for_each_entry(setting, &wnd->wd->driver->settings, list) {
  366         add_text("%s=%s\n", setting->name, setting->value);
  367     }
  368 
  369     return 0;
  370 }
  371 
  372 static ssize_t proc_settings_write(struct file *file, const char __user *buf,
  373                    size_t count, loff_t *ppos)
  374 {
  375     struct ndis_device *wnd = PDE_DATA(file_inode(file));
  376     char setting[MAX_PROC_STR_LEN], *p;
  377     unsigned int i;
  378     NDIS_STATUS res;
  379 
  380     if (count > MAX_PROC_STR_LEN)
  381         return -EINVAL;
  382 
  383     memset(setting, 0, sizeof(setting));
  384     if (copy_from_user(setting, buf, count))
  385         return -EFAULT;
  386 
  387     if ((p = strchr(setting, '\n')))
  388         *p = 0;
  389 
  390     if ((p = strchr(setting, '=')))
  391         *p = 0;
  392 
  393     if (!strcmp(setting, "hangcheck_interval")) {
  394         if (!p)
  395             return -EINVAL;
  396         p++;
  397         i = simple_strtol(p, NULL, 10);
  398         hangcheck_del(wnd);
  399         if (i > 0) {
  400             wnd->hangcheck_interval = i * HZ;
  401             hangcheck_add(wnd);
  402         }
  403     } else if (!strcmp(setting, "suspend")) {
  404         if (!p)
  405             return -EINVAL;
  406         p++;
  407         i = simple_strtol(p, NULL, 10);
  408         if (i <= 0 || i > 3)
  409             return -EINVAL;
  410         i = -1;
  411         if (wrap_is_pci_bus(wnd->wd->dev_bus))
  412             i = wrap_pnp_suspend_pci_device(wnd->wd->pci.pdev,
  413                             PMSG_SUSPEND);
  414         else if (wrap_is_usb_bus(wnd->wd->dev_bus))
  415             i = wrap_pnp_suspend_usb_device(wnd->wd->usb.intf,
  416                             PMSG_SUSPEND);
  417         if (i)
  418             return -EINVAL;
  419     } else if (!strcmp(setting, "resume")) {
  420         i = -1;
  421         if (wrap_is_pci_bus(wnd->wd->dev_bus))
  422             i = wrap_pnp_resume_pci_device(wnd->wd->pci.pdev);
  423         else if (wrap_is_usb_bus(wnd->wd->dev_bus))
  424             i = wrap_pnp_resume_usb_device(wnd->wd->usb.intf);
  425         if (i)
  426             return -EINVAL;
  427     } else if (!strcmp(setting, "stats_enabled")) {
  428         if (!p)
  429             return -EINVAL;
  430         p++;
  431         i = simple_strtol(p, NULL, 10);
  432         if (i > 0)
  433             wnd->iw_stats_enabled = TRUE;
  434         else
  435             wnd->iw_stats_enabled = FALSE;
  436     } else if (!strcmp(setting, "packet_filter")) {
  437         if (!p)
  438             return -EINVAL;
  439         p++;
  440         i = simple_strtol(p, NULL, 10);
  441         res = mp_set_int(wnd, OID_GEN_CURRENT_PACKET_FILTER, i);
  442         if (res)
  443             WARNING("setting packet_filter failed: %08X", res);
  444     } else if (!strcmp(setting, "reinit")) {
  445         if (ndis_reinit(wnd) != NDIS_STATUS_SUCCESS)
  446             return -EFAULT;
  447     } else {
  448         struct ndis_configuration_parameter param;
  449         struct unicode_string key;
  450         struct ansi_string ansi;
  451 
  452         if (!p)
  453             return -EINVAL;
  454         p++;
  455         RtlInitAnsiString(&ansi, p);
  456         if (RtlAnsiStringToUnicodeString(&param.data.string, &ansi,
  457                          TRUE) != STATUS_SUCCESS)
  458             EXIT1(return -EFAULT);
  459         param.type = NdisParameterString;
  460         RtlInitAnsiString(&ansi, setting);
  461         if (RtlAnsiStringToUnicodeString(&key, &ansi,
  462                          TRUE) != STATUS_SUCCESS) {
  463             RtlFreeUnicodeString(&param.data.string);
  464             EXIT1(return -EINVAL);
  465         }
  466         NdisWriteConfiguration(&res, wnd->nmb, &key, &param);
  467         RtlFreeUnicodeString(&key);
  468         RtlFreeUnicodeString(&param.data.string);
  469         if (res != NDIS_STATUS_SUCCESS)
  470             return -EFAULT;
  471     }
  472     return count;
  473 }
  474 
  475 PROC_DECLARE_RW(settings)
  476 
  477 int wrap_procfs_add_ndis_device(struct ndis_device *wnd)
  478 {
  479     int ret;
  480 
  481     if (wrap_procfs_entry == NULL)
  482         return -ENOMEM;
  483 
  484     if (wnd->procfs_iface) {
  485         ERROR("%s already registered?", wnd->net_dev->name);
  486         return -EINVAL;
  487     }
  488     wnd->procfs_iface = proc_mkdir(wnd->net_dev->name, wrap_procfs_entry);
  489     if (wnd->procfs_iface == NULL) {
  490         ERROR("couldn't create proc directory");
  491         return -ENOMEM;
  492     }
  493     proc_set_user(wnd->procfs_iface, proc_kuid, proc_kgid);
  494 
  495     ret = proc_make_entry_ro(hw, wnd->procfs_iface, wnd);
  496     if (ret)
  497         goto err_hw;
  498 
  499     ret = proc_make_entry_ro(stats, wnd->procfs_iface, wnd);
  500     if (ret)
  501         goto err_stats;
  502 
  503     ret = proc_make_entry_ro(encr, wnd->procfs_iface, wnd);
  504     if (ret)
  505         goto err_encr;
  506 
  507     ret = proc_make_entry_rw(settings, wnd->procfs_iface, wnd);
  508     if (ret)
  509         goto err_settings;
  510 
  511     return 0;
  512 
  513 err_settings:
  514     remove_proc_entry("encr", wnd->procfs_iface);
  515 err_encr:
  516     remove_proc_entry("stats", wnd->procfs_iface);
  517 err_stats:
  518     remove_proc_entry("hw", wnd->procfs_iface);
  519 err_hw:
  520     proc_remove(wnd->procfs_iface);
  521     wnd->procfs_iface = NULL;
  522     return -ENOMEM;
  523 }
  524 
  525 void wrap_procfs_remove_ndis_device(struct ndis_device *wnd)
  526 {
  527     struct proc_dir_entry *procfs_iface = xchg(&wnd->procfs_iface, NULL);
  528 
  529     if (procfs_iface == NULL)
  530         return;
  531     remove_proc_entry("hw", procfs_iface);
  532     remove_proc_entry("stats", procfs_iface);
  533     remove_proc_entry("encr", procfs_iface);
  534     remove_proc_entry("settings", procfs_iface);
  535     if (wrap_procfs_entry)
  536         proc_remove(procfs_iface);
  537 }
  538 
  539 static int proc_debug_read(struct seq_file *sf, void *v)
  540 {
  541 #if ALLOC_DEBUG
  542     enum alloc_type type;
  543 #endif
  544 
  545     add_text("%d\n", debug);
  546 #if ALLOC_DEBUG
  547     for (type = 0; type < ALLOC_TYPE_MAX; type++)
  548         add_text("total size of allocations in %s: %d\n",
  549              alloc_type_name[type], alloc_size(type));
  550 #endif
  551     return 0;
  552 }
  553 
  554 static ssize_t proc_debug_write(struct file *file, const char __user *buf,
  555                 size_t count, loff_t *ppos)
  556 {
  557     int i;
  558     char setting[MAX_PROC_STR_LEN], *p;
  559 
  560     if (count > MAX_PROC_STR_LEN)
  561         return -EINVAL;
  562 
  563     memset(setting, 0, sizeof(setting));
  564     if (copy_from_user(setting, buf, count))
  565         return -EFAULT;
  566 
  567     if ((p = strchr(setting, '\n')))
  568         *p = 0;
  569 
  570     if ((p = strchr(setting, '=')))
  571         *p = 0;
  572 
  573     i = simple_strtol(setting, NULL, 10);
  574     if (i >= 0 && i < 10)
  575         debug = i;
  576     else
  577         return -EINVAL;
  578     return count;
  579 }
  580 
  581 PROC_DECLARE_RW(debug)
  582 
  583 int wrap_procfs_init(void)
  584 {
  585     int ret;
  586 
  587 #if LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0)
  588     struct user_namespace *ns = current_user_ns();
  589     proc_kuid = make_kuid(ns, proc_uid);
  590     if (!uid_valid(proc_kuid)) {
  591         ERROR("invalid UID\n");
  592         return -EINVAL;
  593     }
  594     proc_kgid = make_kgid(ns, proc_gid);
  595     if (!gid_valid(proc_kgid)) {
  596         ERROR("invalid GID\n");
  597         return -EINVAL;
  598     }
  599 #endif
  600 
  601     wrap_procfs_entry = proc_mkdir(DRIVER_NAME, proc_net_root);
  602     if (wrap_procfs_entry == NULL) {
  603         ERROR("couldn't create procfs directory");
  604         return -ENOMEM;
  605     }
  606     proc_set_user(wrap_procfs_entry, proc_kuid, proc_kgid);
  607 
  608     ret = proc_make_entry_rw(debug, wrap_procfs_entry, NULL);
  609 
  610     return ret;
  611 }
  612 
  613 void wrap_procfs_remove(void)
  614 {
  615     if (wrap_procfs_entry == NULL)
  616         return;
  617     remove_proc_entry("debug", wrap_procfs_entry);
  618     proc_remove(wrap_procfs_entry);
  619 }