diff options
Diffstat (limited to 'drivers/connector/connector.c')
| -rw-r--r-- | drivers/connector/connector.c | 139 |
1 files changed, 71 insertions, 68 deletions
diff --git a/drivers/connector/connector.c b/drivers/connector/connector.c index 05117f1ad86..f612d68629d 100644 --- a/drivers/connector/connector.c +++ b/drivers/connector/connector.c @@ -1,5 +1,5 @@ /* - * connector.c + * connector.c * * 2004+ Copyright (c) Evgeniy Polyakov <zbr@ioremap.net> * All rights reserved. @@ -23,7 +23,7 @@ #include <linux/module.h> #include <linux/list.h> #include <linux/skbuff.h> -#include <linux/netlink.h> +#include <net/netlink.h> #include <linux/moduleparam.h> #include <linux/connector.h> #include <linux/slab.h> @@ -43,6 +43,8 @@ static struct cn_dev cdev; static int cn_already_initialized; /* + * Sends mult (multiple) cn_msg at a time. + * * msg->seq and msg->ack are used to determine message genealogy. * When someone sends message it puts there locally unique sequence * and random acknowledge numbers. Sequence number may be copied into @@ -50,7 +52,7 @@ static int cn_already_initialized; * * Sequence number is incremented with each message to be sent. * - * If we expect reply to our message then the sequence number in + * If we expect a reply to our message then the sequence number in * received message MUST be the same as in original message, and * acknowledge number MUST be the same + 1. * @@ -62,8 +64,14 @@ static int cn_already_initialized; * the acknowledgement number in the original message + 1, then it is * a new message. * + * If msg->len != len, then additional cn_msg messages are expected following + * the first msg. + * + * The message is sent to, the portid if given, the group if given, both if + * both, or if both are zero then the group is looked up and sent there. */ -int cn_netlink_send(struct cn_msg *msg, u32 __group, gfp_t gfp_mask) +int cn_netlink_send_mult(struct cn_msg *msg, u16 len, u32 portid, u32 __group, + gfp_t gfp_mask) { struct cn_callback_entry *__cbq; unsigned int size; @@ -74,7 +82,9 @@ int cn_netlink_send(struct cn_msg *msg, u32 __group, gfp_t gfp_mask) u32 group = 0; int found = 0; - if (!__group) { + if (portid || __group) { + group = __group; + } else { spin_lock_bh(&dev->cbdev->queue_lock); list_for_each_entry(__cbq, &dev->cbdev->queue_list, callback_entry) { @@ -88,32 +98,41 @@ int cn_netlink_send(struct cn_msg *msg, u32 __group, gfp_t gfp_mask) if (!found) return -ENODEV; - } else { - group = __group; } - if (!netlink_has_listeners(dev->nls, group)) + if (!portid && !netlink_has_listeners(dev->nls, group)) return -ESRCH; - size = NLMSG_SPACE(sizeof(*msg) + msg->len); + size = sizeof(*msg) + len; - skb = alloc_skb(size, gfp_mask); + skb = nlmsg_new(size, gfp_mask); if (!skb) return -ENOMEM; - nlh = NLMSG_PUT(skb, 0, msg->seq, NLMSG_DONE, size - sizeof(*nlh)); + nlh = nlmsg_put(skb, 0, msg->seq, NLMSG_DONE, size, 0); + if (!nlh) { + kfree_skb(skb); + return -EMSGSIZE; + } - data = NLMSG_DATA(nlh); + data = nlmsg_data(nlh); - memcpy(data, msg, sizeof(*data) + msg->len); + memcpy(data, msg, size); NETLINK_CB(skb).dst_group = group; - return netlink_broadcast(dev->nls, skb, 0, group, gfp_mask); + if (group) + return netlink_broadcast(dev->nls, skb, portid, group, + gfp_mask); + return netlink_unicast(dev->nls, skb, portid, !(gfp_mask&__GFP_WAIT)); +} +EXPORT_SYMBOL_GPL(cn_netlink_send_mult); -nlmsg_failure: - kfree_skb(skb); - return -EINVAL; +/* same as cn_netlink_send_mult except msg->len is used for len */ +int cn_netlink_send(struct cn_msg *msg, u32 portid, u32 __group, + gfp_t gfp_mask) +{ + return cn_netlink_send_mult(msg, msg->len, portid, __group, gfp_mask); } EXPORT_SYMBOL_GPL(cn_netlink_send); @@ -122,51 +141,29 @@ EXPORT_SYMBOL_GPL(cn_netlink_send); */ static int cn_call_callback(struct sk_buff *skb) { - struct cn_callback_entry *__cbq, *__new_cbq; + struct cn_callback_entry *i, *cbq = NULL; struct cn_dev *dev = &cdev; - struct cn_msg *msg = NLMSG_DATA(nlmsg_hdr(skb)); + struct cn_msg *msg = nlmsg_data(nlmsg_hdr(skb)); + struct netlink_skb_parms *nsp = &NETLINK_CB(skb); int err = -ENODEV; spin_lock_bh(&dev->cbdev->queue_lock); - list_for_each_entry(__cbq, &dev->cbdev->queue_list, callback_entry) { - if (cn_cb_equal(&__cbq->id.id, &msg->id)) { - if (likely(!work_pending(&__cbq->work) && - __cbq->data.skb == NULL)) { - __cbq->data.skb = skb; - - if (queue_work(dev->cbdev->cn_queue, - &__cbq->work)) - err = 0; - else - err = -EINVAL; - } else { - struct cn_callback_data *d; - - err = -ENOMEM; - __new_cbq = kzalloc(sizeof(struct cn_callback_entry), GFP_ATOMIC); - if (__new_cbq) { - d = &__new_cbq->data; - d->skb = skb; - d->callback = __cbq->data.callback; - d->free = __new_cbq; - - INIT_WORK(&__new_cbq->work, - &cn_queue_wrapper); - - if (queue_work(dev->cbdev->cn_queue, - &__new_cbq->work)) - err = 0; - else { - kfree(__new_cbq); - err = -EINVAL; - } - } - } + list_for_each_entry(i, &dev->cbdev->queue_list, callback_entry) { + if (cn_cb_equal(&i->id.id, &msg->id)) { + atomic_inc(&i->refcnt); + cbq = i; break; } } spin_unlock_bh(&dev->cbdev->queue_lock); + if (cbq != NULL) { + cbq->callback(msg, nsp); + kfree_skb(skb); + cn_queue_release_callback(cbq); + err = 0; + } + return err; } @@ -178,17 +175,18 @@ static int cn_call_callback(struct sk_buff *skb) static void cn_rx_skb(struct sk_buff *__skb) { struct nlmsghdr *nlh; - int err; struct sk_buff *skb; + int len, err; skb = skb_get(__skb); - if (skb->len >= NLMSG_SPACE(0)) { + if (skb->len >= NLMSG_HDRLEN) { nlh = nlmsg_hdr(skb); + len = nlmsg_len(nlh); - if (nlh->nlmsg_len < sizeof(struct cn_msg) || + if (len < (int)sizeof(struct cn_msg) || skb->len < nlh->nlmsg_len || - nlh->nlmsg_len > CONNECTOR_MAX_MSG_SIZE) { + len > CONNECTOR_MAX_MSG_SIZE) { kfree_skb(skb); return; } @@ -205,8 +203,9 @@ static void cn_rx_skb(struct sk_buff *__skb) * * May sleep. */ -int cn_add_callback(struct cb_id *id, char *name, - void (*callback)(struct cn_msg *, struct netlink_skb_parms *)) +int cn_add_callback(struct cb_id *id, const char *name, + void (*callback)(struct cn_msg *, + struct netlink_skb_parms *)) { int err; struct cn_dev *dev = &cdev; @@ -272,15 +271,19 @@ static const struct file_operations cn_file_ops = { .release = single_release }; -static int __devinit cn_init(void) +static struct cn_dev cdev = { + .input = cn_rx_skb, +}; + +static int cn_init(void) { struct cn_dev *dev = &cdev; + struct netlink_kernel_cfg cfg = { + .groups = CN_NETLINK_USERS + 0xf, + .input = dev->input, + }; - dev->input = cn_rx_skb; - - dev->nls = netlink_kernel_create(&init_net, NETLINK_CONNECTOR, - CN_NETLINK_USERS + 0xf, - dev->input, NULL, THIS_MODULE); + dev->nls = netlink_kernel_create(&init_net, NETLINK_CONNECTOR, &cfg); if (!dev->nls) return -EIO; @@ -292,18 +295,18 @@ static int __devinit cn_init(void) cn_already_initialized = 1; - proc_net_fops_create(&init_net, "connector", S_IRUGO, &cn_file_ops); + proc_create("connector", S_IRUGO, init_net.proc_net, &cn_file_ops); return 0; } -static void __devexit cn_fini(void) +static void cn_fini(void) { struct cn_dev *dev = &cdev; cn_already_initialized = 0; - proc_net_remove(&init_net, "connector"); + remove_proc_entry("connector", init_net.proc_net); cn_queue_free_dev(dev->cbdev); netlink_kernel_release(dev->nls); |
