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));
 105
 106        data = NLMSG_DATA(nlh);
 107
 108        memcpy(data, msg, sizeof(*data) + msg->len);
 109
 110        NETLINK_CB(skb).dst_group = group;
 111
 112        return netlink_broadcast(dev->nls, skb, 0, group, gfp_mask);
 113
 114nlmsg_failure:
 115        kfree_skb(skb);
 116        return -EINVAL;
 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 *, struct netlink_skb_parms *))
 189{
 190        int err;
 191        struct cn_dev *dev = &cdev;
 192
 193        if (!cn_already_initialized)
 194                return -EAGAIN;
 195
 196        err = cn_queue_add_callback(dev->cbdev, name, id, callback);
 197        if (err)
 198                return err;
 199
 200        return 0;
 201}
 202EXPORT_SYMBOL_GPL(cn_add_callback);
 203
 204/*
 205 * Callback remove routing - removes callback
 206 * with given ID.
 207 * If there is no registered callback with given
 208 * ID nothing happens.
 209 *
 210 * May sleep while waiting for reference counter to become zero.
 211 */
 212void cn_del_callback(struct cb_id *id)
 213{
 214        struct cn_dev *dev = &cdev;
 215
 216        cn_queue_del_callback(dev->cbdev, id);
 217}
 218EXPORT_SYMBOL_GPL(cn_del_callback);
 219
 220static int cn_proc_show(struct seq_file *m, void *v)
 221{
 222        struct cn_queue_dev *dev = cdev.cbdev;
 223        struct cn_callback_entry *cbq;
 224
 225        seq_printf(m, "Name            ID\n");
 226
 227        spin_lock_bh(&dev->queue_lock);
 228
 229        list_for_each_entry(cbq, &dev->queue_list, callback_entry) {
 230                seq_printf(m, "%-15s %u:%u\n",
 231                           cbq->id.name,
 232                           cbq->id.id.idx,
 233                           cbq->id.id.val);
 234        }
 235
 236        spin_unlock_bh(&dev->queue_lock);
 237
 238        return 0;
 239}
 240
 241static int cn_proc_open(struct inode *inode, struct file *file)
 242{
 243        return single_open(file, cn_proc_show, NULL);
 244}
 245
 246static const struct file_operations cn_file_ops = {
 247        .owner   = THIS_MODULE,
 248        .open    = cn_proc_open,
 249        .read    = seq_read,
 250        .llseek  = seq_lseek,
 251        .release = single_release
 252};
 253
 254static int __devinit cn_init(void)
 255{
 256        struct cn_dev *dev = &cdev;
 257
 258        dev->input = cn_rx_skb;
 259
 260        dev->nls = netlink_kernel_create(&init_net, NETLINK_CONNECTOR,
 261                                         CN_NETLINK_USERS + 0xf,
 262                                         dev->input, NULL, THIS_MODULE);
 263        if (!dev->nls)
 264                return -EIO;
 265
 266        dev->cbdev = cn_queue_alloc_dev("cqueue", dev->nls);
 267        if (!dev->cbdev) {
 268                netlink_kernel_release(dev->nls);
 269                return -EINVAL;
 270        }
 271
 272        cn_already_initialized = 1;
 273
 274        proc_net_fops_create(&init_net, "connector", S_IRUGO, &cn_file_ops);
 275
 276        return 0;
 277}
 278
 279static void __devexit cn_fini(void)
 280{
 281        struct cn_dev *dev = &cdev;
 282
 283        cn_already_initialized = 0;
 284
 285        proc_net_remove(&init_net, "connector");
 286
 287        cn_queue_free_dev(dev->cbdev);
 288        netlink_kernel_release(dev->nls);
 289}
 290
 291subsys_initcall(cn_init);
 292module_exit(cn_fini);
 293