linux/drivers/connector/connector.c
<<
>>
Prefs
   1/*
   2 *      connector.c
   3 *
   4 * 2004+ Copyright (c) Evgeniy Polyakov <zbr@ioremap.net>
   5 * All rights reserved.
   6 *
   7 * This program is free software; you can redistribute it and/or modify
   8 * it under the terms of the GNU General Public License as published by
   9 * the Free Software Foundation; either version 2 of the License, or
  10 * (at your option) any later version.
  11 *
  12 * This program is distributed in the hope that it will be useful,
  13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  15 * GNU General Public License for more details.
  16 *
  17 * You should have received a copy of the GNU General Public License
  18 * along with this program; if not, write to the Free Software
  19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  20 */
  21
  22#include <linux/compiler.h>
  23#include <linux/kernel.h>
  24#include <linux/module.h>
  25#include <linux/list.h>
  26#include <linux/skbuff.h>
  27#include <net/netlink.h>
  28#include <linux/moduleparam.h>
  29#include <linux/connector.h>
  30#include <linux/slab.h>
  31#include <linux/mutex.h>
  32#include <linux/proc_fs.h>
  33#include <linux/spinlock.h>
  34
  35#include <net/sock.h>
  36
  37MODULE_LICENSE("GPL");
  38MODULE_AUTHOR("Evgeniy Polyakov <zbr@ioremap.net>");
  39MODULE_DESCRIPTION("Generic userspace <-> kernelspace connector.");
  40MODULE_ALIAS_NET_PF_PROTO(PF_NETLINK, NETLINK_CONNECTOR);
  41
  42static struct cn_dev cdev;
  43
  44static int cn_already_initialized;
  45
  46/*
  47 * Sends mult (multiple) cn_msg at a time.
  48 *
  49 * msg->seq and msg->ack are used to determine message genealogy.
  50 * When someone sends message it puts there locally unique sequence
  51 * and random acknowledge numbers.  Sequence number may be copied into
  52 * nlmsghdr->nlmsg_seq too.
  53 *
  54 * Sequence number is incremented with each message to be sent.
  55 *
  56 * If we expect a reply to our message then the sequence number in
  57 * received message MUST be the same as in original message, and
  58 * acknowledge number MUST be the same + 1.
  59 *
  60 * If we receive a message and its sequence number is not equal to the
  61 * one we are expecting then it is a new message.
  62 *
  63 * If we receive a message and its sequence number is the same as one
  64 * we are expecting but it's acknowledgement number is not equal to
  65 * the acknowledgement number in the original message + 1, then it is
  66 * a new message.
  67 *
  68 * If msg->len != len, then additional cn_msg messages are expected following
  69 * the first msg.
  70 *
  71 * The message is sent to, the portid if given, the group if given, both if
  72 * both, or if both are zero then the group is looked up and sent there.
  73 */
  74int cn_netlink_send_mult(struct cn_msg *msg, u16 len, u32 portid, u32 __group,
  75        gfp_t gfp_mask)
  76{
  77        struct cn_callback_entry *__cbq;
  78        unsigned int size;
  79        struct sk_buff *skb;
  80        struct nlmsghdr *nlh;
  81        struct cn_msg *data;
  82        struct cn_dev *dev = &cdev;
  83        u32 group = 0;
  84        int found = 0;
  85
  86        if (portid || __group) {
  87                group = __group;
  88        } else {
  89                spin_lock_bh(&dev->cbdev->queue_lock);
  90                list_for_each_entry(__cbq, &dev->cbdev->queue_list,
  91                                    callback_entry) {
  92                        if (cn_cb_equal(&__cbq->id.id, &msg->id)) {
  93                                found = 1;
  94                                group = __cbq->group;
  95                                break;
  96                        }
  97                }
  98                spin_unlock_bh(&dev->cbdev->queue_lock);
  99
 100                if (!found)
 101                        return -ENODEV;
 102        }
 103
 104        if (!portid && !netlink_has_listeners(dev->nls, group))
 105                return -ESRCH;
 106
 107        size = sizeof(*msg) + len;
 108
 109        skb = nlmsg_new(size, gfp_mask);
 110        if (!skb)
 111                return -ENOMEM;
 112
 113        nlh = nlmsg_put(skb, 0, msg->seq, NLMSG_DONE, size, 0);
 114        if (!nlh) {
 115                kfree_skb(skb);
 116                return -EMSGSIZE;
 117        }
 118
 119        data = nlmsg_data(nlh);
 120
 121        memcpy(data, msg, size);
 122
 123        NETLINK_CB(skb).dst_group = group;
 124
 125        if (group)
 126                return netlink_broadcast(dev->nls, skb, portid, group,
 127                                         gfp_mask);
 128        return netlink_unicast(dev->nls, skb, portid,
 129                        !gfpflags_allow_blocking(gfp_mask));
 130}
 131EXPORT_SYMBOL_GPL(cn_netlink_send_mult);
 132
 133/* same as cn_netlink_send_mult except msg->len is used for len */
 134int cn_netlink_send(struct cn_msg *msg, u32 portid, u32 __group,
 135        gfp_t gfp_mask)
 136{
 137        return cn_netlink_send_mult(msg, msg->len, portid, __group, gfp_mask);
 138}
 139EXPORT_SYMBOL_GPL(cn_netlink_send);
 140
 141/*
 142 * Callback helper - queues work and setup destructor for given data.
 143 */
 144static int cn_call_callback(struct sk_buff *skb)
 145{
 146        struct nlmsghdr *nlh;
 147        struct cn_callback_entry *i, *cbq = NULL;
 148        struct cn_dev *dev = &cdev;
 149        struct cn_msg *msg = nlmsg_data(nlmsg_hdr(skb));
 150        struct netlink_skb_parms *nsp = &NETLINK_CB(skb);
 151        int err = -ENODEV;
 152
 153        /* verify msg->len is within skb */
 154        nlh = nlmsg_hdr(skb);
 155        if (nlh->nlmsg_len < NLMSG_HDRLEN + sizeof(struct cn_msg) + msg->len)
 156                return -EINVAL;
 157
 158        spin_lock_bh(&dev->cbdev->queue_lock);
 159        list_for_each_entry(i, &dev->cbdev->queue_list, callback_entry) {
 160                if (cn_cb_equal(&i->id.id, &msg->id)) {
 161                        refcount_inc(&i->refcnt);
 162                        cbq = i;
 163                        break;
 164                }
 165        }
 166        spin_unlock_bh(&dev->cbdev->queue_lock);
 167
 168        if (cbq != NULL) {
 169                cbq->callback(msg, nsp);
 170                kfree_skb(skb);
 171                cn_queue_release_callback(cbq);
 172                err = 0;
 173        }
 174
 175        return err;
 176}
 177
 178/*
 179 * Main netlink receiving function.
 180 *
 181 * It checks skb, netlink header and msg sizes, and calls callback helper.
 182 */
 183static void cn_rx_skb(struct sk_buff *skb)
 184{
 185        struct nlmsghdr *nlh;
 186        int len, err;
 187
 188        if (skb->len >= NLMSG_HDRLEN) {
 189                nlh = nlmsg_hdr(skb);
 190                len = nlmsg_len(nlh);
 191
 192                if (len < (int)sizeof(struct cn_msg) ||
 193                    skb->len < nlh->nlmsg_len ||
 194                    len > CONNECTOR_MAX_MSG_SIZE)
 195                        return;
 196
 197                err = cn_call_callback(skb_get(skb));
 198                if (err < 0)
 199                        kfree_skb(skb);
 200        }
 201}
 202
 203/*
 204 * Callback add routing - adds callback with given ID and name.
 205 * If there is registered callback with the same ID it will not be added.
 206 *
 207 * May sleep.
 208 */
 209int cn_add_callback(struct cb_id *id, const char *name,
 210                    void (*callback)(struct cn_msg *,
 211                                     struct netlink_skb_parms *))
 212{
 213        int err;
 214        struct cn_dev *dev = &cdev;
 215
 216        if (!cn_already_initialized)
 217                return -EAGAIN;
 218
 219        err = cn_queue_add_callback(dev->cbdev, name, id, callback);
 220        if (err)
 221                return err;
 222
 223        return 0;
 224}
 225EXPORT_SYMBOL_GPL(cn_add_callback);
 226
 227/*
 228 * Callback remove routing - removes callback
 229 * with given ID.
 230 * If there is no registered callback with given
 231 * ID nothing happens.
 232 *
 233 * May sleep while waiting for reference counter to become zero.
 234 */
 235void cn_del_callback(struct cb_id *id)
 236{
 237        struct cn_dev *dev = &cdev;
 238
 239        cn_queue_del_callback(dev->cbdev, id);
 240}
 241EXPORT_SYMBOL_GPL(cn_del_callback);
 242
 243static int __maybe_unused cn_proc_show(struct seq_file *m, void *v)
 244{
 245        struct cn_queue_dev *dev = cdev.cbdev;
 246        struct cn_callback_entry *cbq;
 247
 248        seq_printf(m, "Name            ID\n");
 249
 250        spin_lock_bh(&dev->queue_lock);
 251
 252        list_for_each_entry(cbq, &dev->queue_list, callback_entry) {
 253                seq_printf(m, "%-15s %u:%u\n",
 254                           cbq->id.name,
 255                           cbq->id.id.idx,
 256                           cbq->id.id.val);
 257        }
 258
 259        spin_unlock_bh(&dev->queue_lock);
 260
 261        return 0;
 262}
 263
 264static struct cn_dev cdev = {
 265        .input   = cn_rx_skb,
 266};
 267
 268static int cn_init(void)
 269{
 270        struct cn_dev *dev = &cdev;
 271        struct netlink_kernel_cfg cfg = {
 272                .groups = CN_NETLINK_USERS + 0xf,
 273                .input  = dev->input,
 274        };
 275
 276        dev->nls = netlink_kernel_create(&init_net, NETLINK_CONNECTOR, &cfg);
 277        if (!dev->nls)
 278                return -EIO;
 279
 280        dev->cbdev = cn_queue_alloc_dev("cqueue", dev->nls);
 281        if (!dev->cbdev) {
 282                netlink_kernel_release(dev->nls);
 283                return -EINVAL;
 284        }
 285
 286        cn_already_initialized = 1;
 287
 288        proc_create_single("connector", S_IRUGO, init_net.proc_net, cn_proc_show);
 289
 290        return 0;
 291}
 292
 293static void cn_fini(void)
 294{
 295        struct cn_dev *dev = &cdev;
 296
 297        cn_already_initialized = 0;
 298
 299        remove_proc_entry("connector", init_net.proc_net);
 300
 301        cn_queue_free_dev(dev->cbdev);
 302        netlink_kernel_release(dev->nls);
 303}
 304
 305subsys_initcall(cn_init);
 306module_exit(cn_fini);
 307