X-Git-Url: http://ftp.safe.ca/?a=blobdiff_plain;f=drivers%2Fconnector%2Fconnector.c;h=537c29ac4487bea4e4d27313f442ddbbc0f35ddc;hb=1d40ddd10dab332456e3863fed2a6e71aa6635ab;hp=a905f782033196259dcdb46b5931701d0a642bad;hpb=05e52dd7396514648fba6c275eb7b49eca333c6d;p=safe%2Fjmp%2Flinux-2.6 diff --git a/drivers/connector/connector.c b/drivers/connector/connector.c index a905f78..537c29a 100644 --- a/drivers/connector/connector.c +++ b/drivers/connector/connector.c @@ -1,9 +1,9 @@ /* * connector.c - * - * 2004-2005 Copyright (c) Evgeniy Polyakov + * + * 2004+ Copyright (c) Evgeniy Polyakov * All rights reserved. - * + * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -27,27 +27,18 @@ #include #include #include +#include +#include #include MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Evgeniy Polyakov "); +MODULE_AUTHOR("Evgeniy Polyakov "); MODULE_DESCRIPTION("Generic userspace <-> kernelspace connector."); -static u32 cn_idx = CN_IDX_CONNECTOR; -static u32 cn_val = CN_VAL_CONNECTOR; - -module_param(cn_idx, uint, 0); -module_param(cn_val, uint, 0); -MODULE_PARM_DESC(cn_idx, "Connector's main device idx."); -MODULE_PARM_DESC(cn_val, "Connector's main device val."); - -static DEFINE_MUTEX(notify_lock); -static LIST_HEAD(notify_list); - static struct cn_dev cdev; -int cn_already_initialized = 0; +static int cn_already_initialized; /* * msg->seq and msg->ack are used to determine message genealogy. @@ -88,6 +79,7 @@ int cn_netlink_send(struct cn_msg *msg, u32 __group, gfp_t gfp_mask) if (cn_cb_equal(&__cbq->id.id, &msg->id)) { found = 1; group = __cbq->group; + break; } } spin_unlock_bh(&dev->cbdev->queue_lock); @@ -126,42 +118,41 @@ EXPORT_SYMBOL_GPL(cn_netlink_send); /* * Callback helper - queues work and setup destructor for given data. */ -static int cn_call_callback(struct cn_msg *msg, void (*destruct_data)(void *), void *data) +static int cn_call_callback(struct sk_buff *skb) { struct cn_callback_entry *__cbq, *__new_cbq; struct cn_dev *dev = &cdev; + struct cn_msg *msg = NLMSG_DATA(nlmsg_hdr(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.ddata == NULL)) { - __cbq->data.callback_priv = msg; - - __cbq->data.ddata = data; - __cbq->data.destruct_data = destruct_data; + __cbq->data.skb == NULL)) { + __cbq->data.skb = skb; - if (queue_work(dev->cbdev->cn_queue, - &__cbq->work)) + if (queue_cn_work(__cbq, &__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->callback_priv = msg; + d->skb = skb; d->callback = __cbq->data.callback; - d->ddata = data; - d->destruct_data = destruct_data; d->free = __new_cbq; + __new_cbq->pdev = __cbq->pdev; + INIT_WORK(&__new_cbq->work, &cn_queue_wrapper); - if (queue_work(dev->cbdev->cn_queue, + if (queue_cn_work(__new_cbq, &__new_cbq->work)) err = 0; else { @@ -179,119 +170,32 @@ static int cn_call_callback(struct cn_msg *msg, void (*destruct_data)(void *), v } /* - * Skb receive helper - checks skb and msg size and calls callback - * helper. - */ -static int __cn_rx_skb(struct sk_buff *skb, struct nlmsghdr *nlh) -{ - u32 pid, uid, seq, group; - struct cn_msg *msg; - - pid = NETLINK_CREDS(skb)->pid; - uid = NETLINK_CREDS(skb)->uid; - seq = nlh->nlmsg_seq; - group = NETLINK_CB((skb)).dst_group; - msg = NLMSG_DATA(nlh); - - return cn_call_callback(msg, (void (*)(void *))kfree_skb, skb); -} - -/* * Main netlink receiving function. * - * It checks skb and netlink header sizes and calls the skb receive - * helper with a shared skb. + * It checks skb, netlink header and msg sizes, and calls callback helper. */ static void cn_rx_skb(struct sk_buff *__skb) { struct nlmsghdr *nlh; - u32 len; int err; struct sk_buff *skb; skb = skb_get(__skb); if (skb->len >= NLMSG_SPACE(0)) { - nlh = (struct nlmsghdr *)skb->data; + nlh = nlmsg_hdr(skb); if (nlh->nlmsg_len < sizeof(struct cn_msg) || skb->len < nlh->nlmsg_len || nlh->nlmsg_len > CONNECTOR_MAX_MSG_SIZE) { kfree_skb(skb); - goto out; + return; } - len = NLMSG_ALIGN(nlh->nlmsg_len); - if (len > skb->len) - len = skb->len; - - err = __cn_rx_skb(skb, nlh); + err = cn_call_callback(skb); if (err < 0) kfree_skb(skb); } - -out: - kfree_skb(__skb); -} - -/* - * Netlink socket input callback - dequeues the skbs and calls the - * main netlink receiving function. - */ -static void cn_input(struct sock *sk, int len) -{ - struct sk_buff *skb; - - while ((skb = skb_dequeue(&sk->sk_receive_queue)) != NULL) - cn_rx_skb(skb); -} - -/* - * Notification routing. - * - * Gets id and checks if there are notification request for it's idx - * and val. If there are such requests notify the listeners with the - * given notify event. - * - */ -static void cn_notify(struct cb_id *id, u32 notify_event) -{ - struct cn_ctl_entry *ent; - - mutex_lock(¬ify_lock); - list_for_each_entry(ent, ¬ify_list, notify_entry) { - int i; - struct cn_notify_req *req; - struct cn_ctl_msg *ctl = ent->msg; - int idx_found, val_found; - - idx_found = val_found = 0; - - req = (struct cn_notify_req *)ctl->data; - for (i = 0; i < ctl->idx_notify_num; ++i, ++req) { - if (id->idx >= req->first && - id->idx < req->first + req->range) { - idx_found = 1; - break; - } - } - - for (i = 0; i < ctl->val_notify_num; ++i, ++req) { - if (id->val >= req->first && - id->val < req->first + req->range) { - val_found = 1; - break; - } - } - - if (idx_found && val_found) { - struct cn_msg m = { .ack = notify_event, }; - - memcpy(&m.id, id, sizeof(m.id)); - cn_netlink_send(&m, ctl->group, GFP_KERNEL); - } - } - mutex_unlock(¬ify_lock); } /* @@ -300,7 +204,8 @@ static void cn_notify(struct cb_id *id, u32 notify_event) * * May sleep. */ -int cn_add_callback(struct cb_id *id, char *name, void (*callback)(void *)) +int cn_add_callback(struct cb_id *id, char *name, + void (*callback)(struct cn_msg *, struct netlink_skb_parms *)) { int err; struct cn_dev *dev = &cdev; @@ -312,8 +217,6 @@ int cn_add_callback(struct cb_id *id, char *name, void (*callback)(void *)) if (err) return err; - cn_notify(id, 0); - return 0; } EXPORT_SYMBOL_GPL(cn_add_callback); @@ -331,144 +234,64 @@ void cn_del_callback(struct cb_id *id) struct cn_dev *dev = &cdev; cn_queue_del_callback(dev->cbdev, id); - cn_notify(id, 1); } EXPORT_SYMBOL_GPL(cn_del_callback); -/* - * Checks two connector's control messages to be the same. - * Returns 1 if they are the same or if the first one is corrupted. - */ -static int cn_ctl_msg_equals(struct cn_ctl_msg *m1, struct cn_ctl_msg *m2) +static int cn_proc_show(struct seq_file *m, void *v) { - int i; - struct cn_notify_req *req1, *req2; - - if (m1->idx_notify_num != m2->idx_notify_num) - return 0; - - if (m1->val_notify_num != m2->val_notify_num) - return 0; + struct cn_queue_dev *dev = cdev.cbdev; + struct cn_callback_entry *cbq; - if (m1->len != m2->len) - return 0; + seq_printf(m, "Name ID\n"); - if ((m1->idx_notify_num + m1->val_notify_num) * sizeof(*req1) != - m1->len) - return 1; + spin_lock_bh(&dev->queue_lock); - req1 = (struct cn_notify_req *)m1->data; - req2 = (struct cn_notify_req *)m2->data; - - for (i = 0; i < m1->idx_notify_num; ++i) { - if (req1->first != req2->first || req1->range != req2->range) - return 0; - req1++; - req2++; + list_for_each_entry(cbq, &dev->queue_list, callback_entry) { + seq_printf(m, "%-15s %u:%u\n", + cbq->id.name, + cbq->id.id.idx, + cbq->id.id.val); } - for (i = 0; i < m1->val_notify_num; ++i) { - if (req1->first != req2->first || req1->range != req2->range) - return 0; - req1++; - req2++; - } + spin_unlock_bh(&dev->queue_lock); - return 1; + return 0; } -/* - * Main connector device's callback. - * - * Used for notification of a request's processing. - */ -static void cn_callback(void *data) +static int cn_proc_open(struct inode *inode, struct file *file) { - struct cn_msg *msg = data; - struct cn_ctl_msg *ctl; - struct cn_ctl_entry *ent; - u32 size; - - if (msg->len < sizeof(*ctl)) - return; - - ctl = (struct cn_ctl_msg *)msg->data; - - size = (sizeof(*ctl) + ((ctl->idx_notify_num + - ctl->val_notify_num) * - sizeof(struct cn_notify_req))); - - if (msg->len != size) - return; - - if (ctl->len + sizeof(*ctl) != msg->len) - return; - - /* - * Remove notification. - */ - if (ctl->group == 0) { - struct cn_ctl_entry *n; - - mutex_lock(¬ify_lock); - list_for_each_entry_safe(ent, n, ¬ify_list, notify_entry) { - if (cn_ctl_msg_equals(ent->msg, ctl)) { - list_del(&ent->notify_entry); - kfree(ent); - } - } - mutex_unlock(¬ify_lock); - - return; - } - - size += sizeof(*ent); - - ent = kzalloc(size, GFP_KERNEL); - if (!ent) - return; - - ent->msg = (struct cn_ctl_msg *)(ent + 1); - - memcpy(ent->msg, ctl, size - sizeof(*ent)); - - mutex_lock(¬ify_lock); - list_add(&ent->notify_entry, ¬ify_list); - mutex_unlock(¬ify_lock); + return single_open(file, cn_proc_show, NULL); } +static const struct file_operations cn_file_ops = { + .owner = THIS_MODULE, + .open = cn_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release +}; + static int __devinit cn_init(void) { struct cn_dev *dev = &cdev; - int err; - dev->input = cn_input; - dev->id.idx = cn_idx; - dev->id.val = cn_val; + dev->input = cn_rx_skb; - dev->nls = netlink_kernel_create(NETLINK_CONNECTOR, + dev->nls = netlink_kernel_create(&init_net, NETLINK_CONNECTOR, CN_NETLINK_USERS + 0xf, - dev->input, THIS_MODULE); + dev->input, NULL, THIS_MODULE); if (!dev->nls) return -EIO; dev->cbdev = cn_queue_alloc_dev("cqueue", dev->nls); if (!dev->cbdev) { - if (dev->nls->sk_socket) - sock_release(dev->nls->sk_socket); + netlink_kernel_release(dev->nls); return -EINVAL; } - + cn_already_initialized = 1; - err = cn_add_callback(&dev->id, "connector", &cn_callback); - if (err) { - cn_already_initialized = 0; - cn_queue_free_dev(dev->cbdev); - if (dev->nls->sk_socket) - sock_release(dev->nls->sk_socket); - return -EINVAL; - } + proc_net_fops_create(&init_net, "connector", S_IRUGO, &cn_file_ops); return 0; } @@ -479,10 +302,10 @@ static void __devexit cn_fini(void) cn_already_initialized = 0; - cn_del_callback(&dev->id); + proc_net_remove(&init_net, "connector"); + cn_queue_free_dev(dev->cbdev); - if (dev->nls->sk_socket) - sock_release(dev->nls->sk_socket); + netlink_kernel_release(dev->nls); } subsys_initcall(cn_init);