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/kernel.h>
  23#include <linux/module.h>
  24#include <linux/list.h>
  25#include <linux/skbuff.h>
  26#include <linux/netlink.h>
  27#include <linux/moduleparam.h>
  28#include <linux/connector.h>
  29#include <linux/slab.h>
  30#include <linux/mutex.h>
  31#include <linux/proc_fs.h>
  32#include <linux/spinlock.h>
  33
  34#include <net/sock.h>
  35
  36MODULE_LICENSE("GPL");
  37MODULE_AUTHOR("Evgeniy Polyakov <zbr@ioremap.net>");
  38MODULE_DESCRIPTION("Generic userspace <-> kernelspace connector.");
  39MODULE_ALIAS_NET_PF_PROTO(PF_NETLINK, NETLINK_CONNECTOR);
  40
  41static struct cn_dev cdev;
  42
  43static int cn_already_initialized;
  44
  45/*
  46 * msg->seq and msg->ack are used to determine message genealogy.
  47 * When someone sends message it puts there locally unique sequence
  48 * and random acknowledge numbers.  Sequence number may be copied into
  49 * nlmsghdr->nlmsg_seq too.
  50 *
  51 * Sequence number is incremented with each message to be sent.
  52 *
  53 * If we expect reply to our message then the sequence number in
  54 * received message MUST be the same as in original message, and
  55 * acknowledge number MUST be the same + 1.
  56 *
  57 * If we receive a message and its sequence number is not equal to the
  58 * one we are expecting then it is a new message.
  59 *
  60 * If we receive a message and its sequence number is the same as one
  61 * we are expecting but it's acknowledgement number is not equal to
  62 * the acknowledgement number in the original message + 1, then it is
  63 * a new message.
  64 *
  65 */
  66int cn_netlink_send(struct cn_msg *msg, u32 __group, gfp_t gfp_mask)
  67{
  68        struct cn_callback_entry *__cbq;
  69        unsigned int size;
  70        struct sk_buff *skb;
  71        struct nlmsghdr *nlh;
  72        struct cn_msg *data;
  73        struct cn_dev *dev = &cdev;
  74        u32 group = 0;
  75        int found = 0;
  76
  77        if (!__group) {
  78                spin_lock_bh(&dev->cbdev->queue_lock);
  79                list_for_each_entry(__cbq, &dev->cbdev->queue_list,
  80                                    callback_entry) {
  81                        if (cn_cb_equal(&__cbq->id.id, &msg->id)) {
  82                                found = 1;
  83                                group = __cbq->group;
  84                                break;
  85                        }
  86                }
  87                spin_unlock_bh(&dev->cbdev->queue_lock);
  88
  89                if (!found)
  90                        return -ENODEV;
  91        } else {
  92                group = __group;
  93        }
  94
  95        if (!netlink_has_listeners(dev->nls, group))
  96                return -ESRCH;
  97
  98        size = NLMSG_SPACE(sizeof(*msg) + msg->len);
  99
 100        skb = alloc_skb(size, gfp_mask);
 101        if (!skb)
 102                return -ENOMEM;
 103
 104        nlh = nlmsg_put(skb, 0, msg->seq, NLMSG_DONE, size - sizeof(*nlh), 0);
 105        if (!nlh) {
 106                kfree_skb(skb);
 107                return -EMSGSIZE;
 108        }
 109
 110        data = nlmsg_data(nlh);
 111
 112        memcpy(data, msg, sizeof(*data) + msg->len);
 113
 114        NETLINK_CB(skb).dst_group = group;
 115
 116        return netlink_broadcast(dev->nls, skb, 0, group, gfp_mask);
 117}
 118EXPORT_SYMBOL_GPL(cn_netlink_send);
 119
 120/*
 121 * Callback helper - queues work and setup destructor for given data.
 122 */
 123static int cn_call_callback(struct sk_buff *skb)
 124{
 125        struct cn_callback_entry *i, *cbq = NULL;
 126        struct cn_dev *dev = &cdev;
 127        struct cn_msg *msg = NLMSG_DATA(nlmsg_hdr(skb));
 128        struct netlink_skb_parms *nsp = &NETLINK_CB(skb);
 129        int err = -ENODEV;
 130
 131        spin_lock_bh(&dev->cbdev->queue_lock);
 132        list_for_each_entry(i, &dev->cbdev->queue_list, callback_entry) {
 133                if (cn_cb_equal(&i->id.id, &msg->id)) {
 134                        atomic_inc(&i->refcnt);
 135                        cbq = i;
 136                        break;
 137                }
 138        }
 139        spin_unlock_bh(&dev->cbdev->queue_lock);
 140
 141        if (cbq != NULL) {
 142                err = 0;
 143                cbq->callback(msg, nsp);
 144                kfree_skb(skb);
 145                cn_queue_release_callback(cbq);
 146                err = 0;
 147        }
 148
 149        return err;
 150}
 151
 152/*
 153 * Main netlink receiving function.
 154 *
 155 * It checks skb, netlink header and msg sizes, and calls callback helper.
 156 */
 157static void cn_rx_skb(struct sk_buff *__skb)
 158{
 159        struct nlmsghdr *nlh;
 160        int err;
 161        struct sk_buff *skb;
 162
 163        skb = skb_get(__skb);
 164
 165        if (skb->len >= NLMSG_SPACE(0)) {
 166                nlh = nlmsg_hdr(skb);
 167
 168                if (nlh->nlmsg_len < sizeof(struct cn_msg) ||
 169                    skb->len < nlh->nlmsg_len ||
 170                    nlh->nlmsg_len > CONNECTOR_MAX_MSG_SIZE) {
 171                        kfree_skb(skb);
 172                        return;
 173                }
 174
 175                err = cn_call_callback(skb);
 176                if (err < 0)
 177                        kfree_skb(skb);
 178        }
 179}
 180
 181/*
 182 * Callback add routing - adds callback with given ID and name.
 183 * If there is registered callback with the same ID it will not be added.
 184 *
 185 * May sleep.
 186 */
 187int cn_add_callback(struct cb_id *id, const char *name,
 188                    void (*callback)(struct cn_msg *,
 189                                     struct netlink_skb_parms *))
 190{
 191        int err;
 192        struct cn_dev *dev = &cdev;
 193
 194        if (!cn_already_initialized)
 195                return -EAGAIN;
 196
 197        err = cn_queue_add_callback(dev->cbdev, name, id, callback);
 198        if (err)
 199                return err;
 200
 201        return 0;
 202}
 203EXPORT_SYMBOL_GPL(cn_add_callback);
 204
 205/*
 206 * Callback remove routing - removes callback
 207 * with given ID.
 208 * If there is no registered callback with given
 209 * ID nothing happens.
 210 *
 211 * May sleep while waiting for reference counter to become zero.
 212 */
 213void cn_del_callback(struct cb_id *id)
 214{
 215        struct cn_dev *dev = &cdev;
 216
 217        cn_queue_del_callback(dev->cbdev, id);
 218}
 219EXPORT_SYMBOL_GPL(cn_del_callback);
 220
 221static int cn_proc_show(struct seq_file *m, void *v)
 222{
 223        struct cn_queue_dev *dev = cdev.cbdev;
 224        struct cn_callback_entry *cbq;
 225
 226        seq_printf(m, "Name            ID\n");
 227
 228        spin_lock_bh(&dev->queue_lock);
 229
 230        list_for_each_entry(cbq, &dev->queue_list, callback_entry) {
 231                seq_printf(m, "%-15s %u:%u\n",
 232                           cbq->id.name,
 233                           cbq->id.id.idx,
 234                           cbq->id.id.val);
 235        }
 236
 237        spin_unlock_bh(&dev->queue_lock);
 238
 239        return 0;
 240}
 241
 242static int cn_proc_open(struct inode *inode, struct file *file)
 243{
 244        return single_open(file, cn_proc_show, NULL);
 245}
 246
 247static const struct file_operations cn_file_ops = {
 248        .owner   = THIS_MODULE,
 249        .open    = cn_proc_open,
 250        .read    = seq_read,
 251        .llseek  = seq_lseek,
 252        .release = single_release
 253};
 254
 255static struct cn_dev cdev = {
 256        .input   = cn_rx_skb,
 257};
 258
 259static int cn_init(void)
 260{
 261        struct cn_dev *dev = &cdev;
 262        struct netlink_kernel_cfg cfg = {
 263                .groups = CN_NETLINK_USERS + 0xf,
 264                .input  = dev->input,
 265        };
 266
 267        dev->nls = netlink_kernel_create(&init_net, NETLINK_CONNECTOR, &cfg);
 268        if (!dev->nls)
 269                return -EIO;
 270
 271        dev->cbdev = cn_queue_alloc_dev("cqueue", dev->nls);
 272        if (!dev->cbdev) {
 273                netlink_kernel_release(dev->nls);
 274                return -EINVAL;
 275        }
 276
 277        cn_already_initialized = 1;
 278
 279        proc_net_fops_create(&init_net, "connector", S_IRUGO, &cn_file_ops);
 280
 281        return 0;
 282}
 283
 284static void cn_fini(void)
 285{
 286        struct cn_dev *dev = &cdev;
 287
 288        cn_already_initialized = 0;
 289
 290        proc_net_remove(&init_net, "connector");
 291
 292        cn_queue_free_dev(dev->cbdev);
 293        netlink_kernel_release(dev->nls);
 294}
 295
 296subsys_initcall(cn_init);
 297module_exit(cn_fini);
 298
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.