/* * OMAP STI/XTI communications interface via netlink socket. * * Copyright (C) 2004, 2005, 2006 Nokia Corporation * Written by: Paul Mundt * * This file is subject to the terms and conditions of the GNU General Public * License. See the file "COPYING" in the main directory of this archive * for more details. */ #include #include #include #include #include #include #include #include static struct sock *sti_sock; static DEFINE_MUTEX(sti_netlink_mutex); enum { STI_READ, STI_WRITE, }; static int sti_netlink_read(int pid, int seq, void *payload, int size) { struct sk_buff *skb; struct nlmsghdr *nlh; int ret, len = NLMSG_SPACE(size); unsigned char *tail; skb = alloc_skb(len, GFP_KERNEL); if (!skb) return -ENOMEM; tail = skb->tail; nlh = NLMSG_PUT(skb, pid, seq, STI_READ, len - (sizeof(struct nlmsghdr))); nlh->nlmsg_flags = 0; memcpy(NLMSG_DATA(nlh), payload, size); nlh->nlmsg_len = skb->tail - tail; ret = netlink_unicast(sti_sock, skb, pid, MSG_DONTWAIT); if (ret > 0) ret = 0; return ret; nlmsg_failure: if (skb) kfree_skb(skb); return -EINVAL; } /* * We abuse nlmsg_type and nlmsg_flags for our purposes. * * The ID is encoded into the upper 8 bits of the nlmsg_type, while the * channel number is encoded into the upper 8 bits of the nlmsg_flags. */ static int sti_netlink_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh) { void *data; u8 chan, id; int size, ret = 0, len = 0; data = NLMSG_DATA(nlh); chan = (nlh->nlmsg_flags >> 8) & 0xff; id = (nlh->nlmsg_type >> 8) & 0xff; size = (int)(nlh->nlmsg_len - ((char *)data - (char *)nlh)); switch (nlh->nlmsg_type & 0xff) { case STI_WRITE: sti_channel_write_trace(size, id, data, chan); break; case STI_READ: data = kmalloc(size, GFP_KERNEL); if (!data) return -ENOMEM; memset(data, 0, size); len = sti_read_packet(data, size); ret = sti_netlink_read(NETLINK_CB(skb).pid, nlh->nlmsg_seq, data, len); kfree(data); break; default: return -ENOTTY; } return ret; } static int sti_netlink_receive_skb(struct sk_buff *skb) { while (skb->len >= NLMSG_SPACE(0)) { struct nlmsghdr *nlh; u32 rlen; int ret; nlh = (struct nlmsghdr *)skb->data; if (nlh->nlmsg_len < sizeof(struct nlmsghdr) || skb->len < nlh->nlmsg_len) break; rlen = NLMSG_ALIGN(nlh->nlmsg_len); if (rlen > skb->len) rlen = skb->len; ret = sti_netlink_receive_msg(skb, nlh); if (ret) netlink_ack(skb, nlh, -ret); else if (nlh->nlmsg_flags & NLM_F_ACK) netlink_ack(skb, nlh, 0); skb_pull(skb, rlen); } return 0; } static void sti_netlink_receive(struct sock *sk, int len) { struct sk_buff *skb; if (!mutex_trylock(&sti_netlink_mutex)) return; while ((skb = skb_dequeue(&sk->sk_receive_queue))) if (sti_netlink_receive_skb(skb) && skb->len) skb_queue_head(&sk->sk_receive_queue, skb); else kfree_skb(skb); mutex_unlock(&sti_netlink_mutex); } static int __init sti_netlink_init(void) { sti_sock = netlink_kernel_create(NETLINK_USERSOCK, 0, sti_netlink_receive, THIS_MODULE); if (!sti_sock) { printk(KERN_ERR "STI: Failed to create netlink socket\n"); return -ENODEV; } return 0; } module_init(sti_netlink_init); MODULE_AUTHOR("Paul Mundt"); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("STI netlink-driven communications interface");