1
0
mirror of git://projects.qi-hardware.com/openwrt-xburst.git synced 2025-04-21 12:27:27 +03:00

[ifxmips]

* adds a rewrite of the tapi drivers + sip app. this is the result of lars' gsoc 2010 project, Thanks !


git-svn-id: svn://svn.openwrt.org/openwrt/trunk@23840 3c298f89-4303-0410-b956-a3cf2f4a3e73
This commit is contained in:
blogic
2010-11-03 19:12:34 +00:00
parent 74f6f2f5a2
commit 6ea7ec75d2
79 changed files with 8271 additions and 0 deletions

View File

@@ -0,0 +1,7 @@
tapi-objs := tapi-core.o tapi-port.o tapi-input.o
tapi-objs += tapi-control.o
tapi-objs += tapi-stream.o
tapi-objs += tapi-sysfs-port.o
obj-m += tapi.o

View File

@@ -0,0 +1,193 @@
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/list.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/tapi/tapi.h>
#include <linux/tapi/tapi-ioctl.h>
/* FIXME Does it acutally make sense to allow more then one application at a
* time to open the control device? For example calling sync from one app will
* also sync all others. */
struct tapi_control_file {
struct tapi_device *tdev;
struct list_head links;
};
static struct tapi_endpoint *tapi_lookup_endpoint(struct tapi_device *tdev,
unsigned int ep_id)
{
struct tapi_stream *stream;
if (ep_id < tdev->num_ports)
return &tdev->ports[ep_id].ep;
list_for_each_entry(stream, &tdev->streams, head) {
if (stream->ep.id == ep_id)
return &stream->ep;
}
return ERR_PTR(-ENOENT);
}
static inline struct tapi_device *inode_to_tdev(struct inode *inode)
{
return container_of(inode->i_cdev, struct tapi_char_device, cdev)->tdev;
}
static int tapi_control_open(struct inode *inode, struct file *file)
{
int ret;
struct tapi_device *tdev = inode_to_tdev(inode);
struct tapi_control_file *tctrl;
get_device(&tdev->dev);
tctrl = kzalloc(sizeof(*tctrl), GFP_KERNEL);
if (!tctrl) {
ret = -ENOMEM;
goto err_put_device;
}
INIT_LIST_HEAD(&tctrl->links);
tctrl->tdev = tdev;
file->private_data = tctrl;
return 0;
err_put_device:
put_device(&tdev->dev);
return ret;
}
static int tapi_control_release(struct inode *inode, struct file *file)
{
struct tapi_control_file *tctrl = file->private_data;
struct tapi_link *link;
if (tctrl) {
list_for_each_entry(link, &tctrl->links, head)
tapi_link_free(tctrl->tdev, link);
put_device(&tctrl->tdev->dev);
}
return 0;
}
static long tapi_control_ioctl_link_alloc(struct tapi_control_file *tctrl,
unsigned long arg)
{
struct tapi_link *link;
struct tapi_endpoint *ep1, *ep2;
ep1 = tapi_lookup_endpoint(tctrl->tdev, arg >> 16);
ep2 = tapi_lookup_endpoint(tctrl->tdev, arg & 0xffff);
link = tapi_link_alloc(tctrl->tdev, ep1, ep2);
if (IS_ERR(link))
return PTR_ERR(link);
list_add_tail(&link->head, &tctrl->links);
return link->id;
}
struct tapi_link *tapi_control_lookup_link(struct tapi_control_file *tctrl,
unsigned int id)
{
struct tapi_link *link;
list_for_each_entry(link, &tctrl->links, head) {
if (link->id == id)
return link;
}
return NULL;
}
static long tapi_control_ioctl_link_free(struct tapi_control_file *tctrl,
unsigned long arg)
{
struct tapi_link *link = tapi_control_lookup_link(tctrl, arg);
if (!link)
return -ENOENT;
tapi_link_free(tctrl->tdev, link);
list_del(&link->head);
return 0;
}
static long tapi_control_ioctl_link_enable(struct tapi_control_file *tctrl,
unsigned long arg)
{
struct tapi_link *link = tapi_control_lookup_link(tctrl, arg);
if (!link)
return -ENOENT;
return tapi_link_enable(tctrl->tdev, link);
}
static long tapi_control_ioctl_link_disable(struct tapi_control_file *tctrl,
unsigned long arg)
{
struct tapi_link *link = tapi_control_lookup_link(tctrl, arg);
if (!link)
return -ENOENT;
return tapi_link_disable(tctrl->tdev, link);
}
static long tapi_control_ioctl_sync(struct tapi_control_file *tctrl)
{
return tapi_sync(tctrl->tdev);
}
static long tapi_control_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
int ret;
struct tapi_control_file *tctrl = file->private_data;
switch (cmd) {
case TAPI_CONTROL_IOCTL_LINK_ALLOC:
ret = tapi_control_ioctl_link_alloc(tctrl, arg);
break;
case TAPI_CONTROL_IOCTL_LINK_FREE:
ret = tapi_control_ioctl_link_free(tctrl, arg);
break;
case TAPI_CONTROL_IOCTL_LINK_ENABLE:
ret = tapi_control_ioctl_link_enable(tctrl, arg);
break;
case TAPI_CONTROL_IOCTL_LINK_DISABLE:
ret = tapi_control_ioctl_link_disable(tctrl, arg);
break;
case TAPI_CONTROL_IOCTL_SYNC:
ret = tapi_control_ioctl_sync(tctrl);
break;
default:
return -EINVAL;
}
return ret;
}
static const struct file_operations tapi_control_file_ops = {
.owner = THIS_MODULE,
.open = tapi_control_open,
.release = tapi_control_release,
.unlocked_ioctl = tapi_control_ioctl,
};
int tapi_register_control_device(struct tapi_device* tdev)
{
dev_set_name(&tdev->control_dev.dev, "tapi%uC", tdev->id);
return tapi_char_device_register(tdev, &tdev->control_dev, &tapi_control_file_ops);
}

View File

@@ -0,0 +1,250 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/list.h>
#include <linux/cdev.h>
#include <linux/err.h>
#include <linux/tapi/tapi.h>
void tapi_alloc_input(struct tapi_device *tdev, struct tapi_port *port);
int tapi_register_port_device(struct tapi_device* tdev, struct tapi_port *port);
int tapi_register_stream_device(struct tapi_device* tdev);
int tapi_register_control_device(struct tapi_device* tdev);
static struct class *tapi_class;
static int tapi_major;
#define TAPI_MAX_MINORS 255
static bool tapi_minors[TAPI_MAX_MINORS];
static int tapi_get_free_minor(void)
{
int i;
for (i = 0; i < TAPI_MAX_MINORS; ++i) {
if (!tapi_minors[i]) {
tapi_minors[i] = true;
return i;
}
}
return -1;
}
/*
int tapi_port_send_dtmf_events(struct tapi_device *tdev, unsigned int port, struct tapi_dtmf *, size_t num_events, unsigned int dealy)
{
}
EXPORT_SYMBOL_GPL(tapi_port_send_dtmf_events);
*/
void tapi_report_hook_event(struct tapi_device *tdev, struct tapi_port *port,
bool on)
{
struct tapi_event event;
event.type = TAPI_EVENT_TYPE_HOOK;
event.port = port->id;
event.hook.on = on;
tapi_report_event(tdev, &event);
}
EXPORT_SYMBOL_GPL(tapi_report_hook_event);
void tapi_report_dtmf_event(struct tapi_device *tdev, struct tapi_port *port,
unsigned char code)
{
struct tapi_event event;
event.type = TAPI_EVENT_TYPE_DTMF;
event.port = port->id;
event.dtmf.code = code;
tapi_report_event(tdev, &event);
}
EXPORT_SYMBOL_GPL(tapi_report_dtmf_event);
struct tapi_stream *tapi_stream_alloc(struct tapi_device *tdev)
{
struct tapi_stream *stream;
printk("tdev %p\n", tdev);
if (!tdev->ops || !tdev->ops->stream_alloc)
return ERR_PTR(-ENOSYS);
stream = tdev->ops->stream_alloc(tdev);
printk("stream %p\n", stream);
if (IS_ERR(stream))
return stream;
stream->id = atomic_inc_return(&tdev->stream_id) - 1;
stream->ep.id = stream->id;
/* mutex_lock(&tdev->lock);*/
list_add_tail(&stream->head, &tdev->streams);
/* mutex_unlock(&tdev->lock);*/
return stream;
}
EXPORT_SYMBOL_GPL(tapi_stream_alloc);
void tapi_stream_free(struct tapi_device *tdev, struct tapi_stream *stream)
{
mutex_lock(&tdev->lock);
list_del(&stream->head);
mutex_unlock(&tdev->lock);
tdev->ops->stream_free(tdev, stream);
}
EXPORT_SYMBOL_GPL(tapi_stream_free);
struct tapi_link *tapi_link_alloc(struct tapi_device *tdev,
struct tapi_endpoint *ep1, struct tapi_endpoint *ep2)
{
struct tapi_link *link;
if (!tdev->ops || !tdev->ops->link_alloc)
return ERR_PTR(-ENOSYS);
link = tdev->ops->link_alloc(tdev, ep1, ep2);
if (IS_ERR(link))
return link;
link->id = atomic_inc_return(&tdev->link_id) - 1;
/*
mutex_lock(&tdev->lock);
list_add_tail(&link->head, &tdev->links);
mutex_unlock(&tdev->lock);
*/
return link;
}
EXPORT_SYMBOL_GPL(tapi_link_alloc);
void tapi_link_free(struct tapi_device *tdev, struct tapi_link *link)
{
/*
mutex_lock(&tdev->lock);
list_del(&link->head);
mutex_unlock(&tdev->lock);
*/
tdev->ops->link_free(tdev, link);
}
EXPORT_SYMBOL_GPL(tapi_link_free);
int tapi_char_device_register(struct tapi_device *tdev,
struct tapi_char_device *tchrdev, const struct file_operations *fops)
{
int ret;
struct device *dev = &tchrdev->dev;
dev_t devt;
int minor = tapi_get_free_minor();
devt = MKDEV(tapi_major, minor);
dev->devt = devt;
dev->class = tapi_class;
dev->parent = &tdev->dev;
tchrdev->tdev = tdev;
cdev_init(&tchrdev->cdev, fops);
tchrdev->cdev.owner = THIS_MODULE;
ret = cdev_add(&tchrdev->cdev, devt, 1);
if (ret)
return ret;
ret = device_register(&tchrdev->dev);
if (ret)
goto err_cdev_del;
return 0;
err_cdev_del:
cdev_del(&tchrdev->cdev);
return ret;
}
int tapi_device_register(struct tapi_device *tdev, const char *name,
struct device *parent)
{
static atomic_t tapi_device_id = ATOMIC_INIT(0);
int ret, i;
tdev->dev.class = tapi_class;
tdev->dev.parent = parent;
dev_set_name(&tdev->dev, "%s", name);
ret = device_register(&tdev->dev);
if (ret)
return ret;
tdev->id = atomic_inc_return(&tapi_device_id) - 1;
mutex_init(&tdev->lock);
INIT_LIST_HEAD(&tdev->streams);
INIT_LIST_HEAD(&tdev->links);
atomic_set(&tdev->link_id, 0);
atomic_set(&tdev->stream_id, tdev->num_ports);
tapi_register_stream_device(tdev);
tapi_register_control_device(tdev);
for (i = 0; i < tdev->num_ports; ++i) {
tapi_port_alloc(tdev, i);
tapi_alloc_input(tdev, &tdev->ports[i]);
tapi_register_port_device(tdev, &tdev->ports[i]);
tdev->ports[i].id = i;
tdev->ports[i].ep.id = i;
}
return 0;
}
EXPORT_SYMBOL_GPL(tapi_device_register);
void tapi_device_unregister(struct tapi_device *tdev)
{
device_unregister(&tdev->dev);
}
EXPORT_SYMBOL_GPL(tapi_device_unregister);
static int __init tapi_class_init(void)
{
int ret;
dev_t dev;
tapi_class = class_create(THIS_MODULE, "tapi");
if (IS_ERR(tapi_class)) {
ret = PTR_ERR(tapi_class);
printk(KERN_ERR "tapi: Failed to create device class: %d\n", ret);
goto err;
}
ret = alloc_chrdev_region(&dev, 0, TAPI_MAX_MINORS, "tapi");
if (ret) {
printk(KERN_ERR "tapi: Failed to allocate chrdev region: %d\n", ret);
goto err_class_destory;
}
tapi_major = MAJOR(dev);
return 0;
err_class_destory:
class_destroy(tapi_class);
err:
return ret;
}
subsys_initcall(tapi_class_init);
static void __exit tapi_class_exit(void)
{
unregister_chrdev_region(MKDEV(tapi_major, 0), TAPI_MAX_MINORS);
class_destroy(tapi_class);
}
module_exit(tapi_class_exit);
MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
MODULE_DESCRIPTION("TAPI class");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,99 @@
#include <linux/tapi/tapi.h>
#include <linux/input.h>
static unsigned short tapi_keycodes[] = {
[0] = KEY_NUMERIC_0,
[1] = KEY_NUMERIC_1,
[2] = KEY_NUMERIC_2,
[3] = KEY_NUMERIC_3,
[4] = KEY_NUMERIC_4,
[5] = KEY_NUMERIC_5,
[6] = KEY_NUMERIC_6,
[7] = KEY_NUMERIC_7,
[8] = KEY_NUMERIC_8,
[9] = KEY_NUMERIC_9,
[10] = KEY_NUMERIC_STAR,
[11] = KEY_NUMERIC_POUND,
[12] = KEY_ENTER,
[13] = KEY_ESC,
};
static int tapi_input_event(struct input_dev *input, unsigned int type,
unsigned int code, int value)
{
struct tapi_device *tdev = dev_to_tapi(input->dev.parent);
struct tapi_port *port = input_get_drvdata(input);
if (type != EV_SND || code != SND_BELL)
return -EINVAL;
tapi_port_set_ring(tdev, port, value);
return 0;
}
void tapi_alloc_input(struct tapi_device *tdev, struct tapi_port *port)
{
struct input_dev *input;
int i;
char *phys;
input = input_allocate_device();
phys = kzalloc(sizeof("tapi/input000"), GFP_KERNEL);
sprintf(phys, "tapi/input%d", port->id);
input->name = "tapi";
input->phys = phys;
input->id.bustype = BUS_HOST;
input->dev.parent = &tdev->dev;
input->evbit[0] = BIT(EV_KEY) | BIT(EV_SND);
input->sndbit[0] = BIT(SND_BELL);
input->event = tapi_input_event;
input->keycodesize = sizeof(unsigned short);
input->keycodemax = ARRAY_SIZE(tapi_keycodes);
input->keycode = tapi_keycodes;
port->input = input;
for (i = 0; i < ARRAY_SIZE(tapi_keycodes); ++i)
__set_bit(tapi_keycodes[i], input->keybit);
input_set_drvdata(input, port);
input_register_device(input);
}
void tapi_report_event(struct tapi_device *tdev,
struct tapi_event *event)
{
unsigned short key_code;
struct input_dev *input;
if (!tdev || !tdev->ports)
return;
switch (event->type) {
case TAPI_EVENT_TYPE_HOOK:
if (event->hook.on)
key_code = KEY_ENTER;
else
key_code = KEY_ESC;
break;
case TAPI_EVENT_TYPE_DTMF:
key_code = tapi_keycodes[event->dtmf.code];
break;
default:
return;
}
input = tdev->ports[event->port].input;
input_report_key(input, key_code, 1);
input_sync(input);
input_report_key(input, key_code, 0);
input_sync(input);
}

View File

@@ -0,0 +1,62 @@
static struct tapi_attr default_port[] = {
[PORTS] = {
.type = TAPI_TYPE_PORTS,
.name = "ports",
.description = "foobar",
.set = tapi_set_ports,
.get = tapi_get_ports,
},
};
static const struct nla_policy tapi_policy[] = {
[TAPI_ATTR_ID] = { .type = NLA_U32 },
[TAPI_ATTR_PORT] = { .type = NLA_U32 },
[TAPI_ATTR_ENDPOINT] = { .type = NLA_U32 },
[TAPI_ATTR_STREAM] = { .type = NLA_U32 }
};
static const struct nla_policy tapi_port_policy[] = {
[TAPI_PORT_ID] = { .type = NLA_U32 },
};
static const struct nla_policy tapi_endpoint_policy[] = {
[TAPI_ENDPOINT_ID] = { .type = NLA_U32 },
};
static const struct nla_policy tapi_stream_policy[] = {
[TAPI_STREAM_ID] = { .type = NLA_U32 },
};
static struct genl_family tapi_nl_family = {
.id = GENL_ID_GENERATE,
.name = "tapi",
.hdrsize = 0,
.version = 1,
.maxattr = ARRAY_SIZE(tapi_policy),
};
static struct genl_ops tapi_nl_ops[] = {
TAPI_NL_OP(TAPI_CMD_LIST, list_attr),
};
static int __init tapi_nl_init(void)
{
ret = genl_unregister_family(&tapi_nl_family);
if (ret)
return ret;
genl_register_ops(&tapi_nl_family, tapi_nl_ops);
return 0;
}
module_init(tapi_nl_init);
static void __exit tapi_nl_exit(void)
{
genl_unregister_family(&tapi_nl_family);
}

View File

@@ -0,0 +1,82 @@
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/list.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/tapi/tapi.h>
#include <linux/tapi/tapi-ioctl.h>
static inline struct tapi_port *tapi_char_device_to_port(struct tapi_char_device *chrdev)
{
return container_of(chrdev, struct tapi_port, chrdev);
}
static int tapi_port_open(struct inode *inode, struct file *file)
{
struct tapi_device *tdev = cdev_to_tapi_char_device(inode->i_cdev)->tdev;
get_device(&tdev->dev);
file->private_data = cdev_to_tapi_char_device(inode->i_cdev);
return 0;
}
static int tapi_port_release(struct inode *inode, struct file *file)
{
struct tapi_device *tdev = cdev_to_tapi_char_device(inode->i_cdev)->tdev;
put_device(&tdev->dev);
return 0;
}
static long tapi_port_ioctl_get_endpoint(struct tapi_device *tdev,
struct tapi_port *port, unsigned long arg)
{
return port->ep.id;
}
static long tapi_port_ioctl_set_ring(struct tapi_device *tdev,
struct tapi_port *port, unsigned long arg)
{
tapi_port_set_ring(tdev, port, arg);
return 0;
}
static long tapi_port_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
int ret;
struct tapi_char_device *tchrdev = file->private_data;
struct tapi_device *tdev = tchrdev->tdev;
struct tapi_port *port = tapi_char_device_to_port(tchrdev);
switch (cmd) {
case TAPI_PORT_IOCTL_GET_ENDPOINT:
ret = tapi_port_ioctl_get_endpoint(tdev, port, arg);
break;
case TAPI_PORT_IOCTL_SET_RING:
ret = tapi_port_ioctl_set_ring(tdev, port, arg);
break;
default:
ret = -EINVAL;
break;
}
return ret;
}
static const struct file_operations tapi_port_file_ops = {
.owner = THIS_MODULE,
.open = tapi_port_open,
.release = tapi_port_release,
.unlocked_ioctl = tapi_port_ioctl,
};
int tapi_register_port_device(struct tapi_device* tdev, struct tapi_port *port)
{
dev_set_name(&port->chrdev.dev, "tapi%uP%u", tdev->id, port->id);
return tapi_char_device_register(tdev, &port->chrdev, &tapi_port_file_ops);
}

View File

@@ -0,0 +1,201 @@
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/list.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/signal.h>
#include <linux/sched.h>
#include <linux/poll.h>
#include <linux/tapi/tapi.h>
#include <linux/tapi/tapi-ioctl.h>
struct tapi_stream_file {
struct tapi_device *tdev;
struct tapi_stream *stream;
};
static inline struct tapi_device *inode_to_tdev(struct inode *inode)
{
return container_of(inode->i_cdev, struct tapi_char_device, cdev)->tdev;
}
static int tapi_stream_open(struct inode *inode, struct file *file)
{
int ret;
struct tapi_device *tdev = inode_to_tdev(inode);
struct tapi_stream_file *stream;
get_device(&tdev->dev);
stream = kzalloc(sizeof(*stream), GFP_KERNEL);
if (!stream) {
ret = -ENOMEM;
goto err_put;
}
stream->stream = tapi_stream_alloc(tdev);
if (IS_ERR(stream->stream)) {
ret = PTR_ERR(stream->stream);
goto err_free;
}
stream->tdev = tdev;
init_waitqueue_head(&stream->stream->recv_wait);
skb_queue_head_init(&stream->stream->recv_queue);
file->private_data = stream;
return 0;
err_free:
kfree(stream);
err_put:
put_device(&tdev->dev);
return ret;
}
static int tapi_stream_release(struct inode *inode, struct file *file)
{
struct tapi_stream_file *stream = file->private_data;
if (stream) {
tapi_stream_free(stream->tdev, stream->stream);
put_device(&stream->tdev->dev);
kfree(stream);
}
return 0;
}
static long tapi_stream_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
int ret = 0;
struct tapi_stream_file *stream = file->private_data;
struct tapi_device *tdev = stream->tdev;
switch (cmd) {
case TAPI_STREAM_IOCTL_GET_ENDPOINT:
ret = stream->stream->ep.id;
break;
case TAPI_STREAM_IOCTL_CONFIGURE:
break;
case TAPI_STREAM_IOCTL_START:
ret = tapi_stream_start(tdev, stream->stream);
break;
case TAPI_STREAM_IOCTL_STOP:
ret = tapi_stream_stop(tdev, stream->stream);
break;
default:
ret = -EINVAL;
break;
}
return ret;
}
static unsigned int tapi_stream_poll(struct file *file, struct poll_table_struct *wait)
{
struct tapi_stream_file *stream = file->private_data;
int ret;
poll_wait(file, &stream->stream->recv_wait, wait);
ret = POLLOUT;
if (!skb_queue_empty(&stream->stream->recv_queue))
ret |= POLLIN;
return ret;
}
static ssize_t tapi_stream_read(struct file *file, char __user *buffer,
size_t count, loff_t *offset)
{
struct tapi_stream_file *stream = file->private_data;
struct sk_buff *skb;
skb = skb_dequeue(&stream->stream->recv_queue);
if (!skb) {
if (file->f_flags & O_NONBLOCK)
return -EAGAIN;
do {
interruptible_sleep_on(&stream->stream->recv_wait);
skb = skb_dequeue(&stream->stream->recv_queue);
} while (skb == NULL && !signal_pending(current));
if (skb == NULL)
return -ERESTARTNOHAND;
}
if (skb->len > count) {
skb_queue_head(&stream->stream->recv_queue, skb);
return -EMSGSIZE;
}
if (copy_to_user(buffer, skb->data, skb->len)) {
skb_queue_head(&stream->stream->recv_queue, skb);
return -EFAULT;
}
count = skb->len;
kfree_skb(skb);
return count;
}
static ssize_t tapi_stream_write(struct file *file, const char __user *buffer,
size_t count, loff_t *ppos)
{
struct tapi_stream_file *stream = file->private_data;
struct tapi_device *tdev = stream->tdev;
struct sk_buff *skb;
if (count == 0)
return 0;
skb = alloc_skb(count, GFP_USER);
if (!skb)
return -ENOMEM;
if (copy_from_user(skb_put(skb, count), buffer, count)) {
kfree_skb(skb);
return -EFAULT;
}
tdev->ops->stream_send(tdev, stream->stream, skb);
return count;
}
static const struct file_operations tapi_stream_file_ops = {
.owner = THIS_MODULE,
.read = tapi_stream_read,
.write = tapi_stream_write,
.open = tapi_stream_open,
.release = tapi_stream_release,
.poll = tapi_stream_poll,
.unlocked_ioctl = tapi_stream_ioctl,
};
int tapi_register_stream_device(struct tapi_device* tdev)
{
dev_set_name(&tdev->stream_dev.dev, "tapi%uS", tdev->id);
return tapi_char_device_register(tdev, &tdev->stream_dev, &tapi_stream_file_ops);
}
int tapi_stream_recv(struct tapi_device *tdev, struct tapi_stream * stream,
struct sk_buff *skb)
{
skb_queue_tail(&stream->recv_queue, skb);
wake_up(&stream->recv_wait);
return 0;
}
EXPORT_SYMBOL_GPL(tapi_stream_recv);

View File

@@ -0,0 +1,107 @@
#include <linux/device.h>
#include <linux/kernel.h>
#include <linux/sysfs.h>
#include <linux/err.h>
#include <linux/tapi/tapi.h>
struct tapi_sysfs_port {
struct tapi_device *tdev;
unsigned int id;
struct kobject kobj;
};
struct tapi_sysfs_entry {
ssize_t (*show)(struct tapi_device *, unsigned int port, char *);
ssize_t (*store)(struct tapi_device *, unsigned int port, const char *, size_t);
struct attribute attr;
};
static ssize_t tapi_port_store(struct kobject *kobj, struct attribute *attr,
const char *s, size_t len)
{
struct tapi_sysfs_port *port = container_of(kobj, struct tapi_sysfs_port, kobj);
struct tapi_sysfs_entry *entry = container_of(attr, struct tapi_sysfs_entry,
attr);
if (!entry->store)
return -ENOSYS;
return entry->store(port->tdev, port->id, s, len);
}
static ssize_t tapi_port_show(struct kobject *kobj, struct attribute *attr,
char *s)
{
return -ENOSYS;
}
#define TAPI_PORT_ATTR(_name, _mode, _show, _store) \
struct tapi_sysfs_entry tapi_port_ ## _name ## _attr = \
__ATTR(_name, _mode, _show, _store)
static int tapi_port_store_ring(struct tapi_device *tdev, unsigned int port,
const char *s, size_t len)
{
int ret;
unsigned long val;
ret = strict_strtoul(s, 10, &val);
if (ret)
return ret;
ret = tapi_port_set_ring(tdev, &tdev->ports[port], val);
if (ret)
return ret;
return len;
}
static TAPI_PORT_ATTR(ring, 0644, NULL, tapi_port_store_ring);
static struct attribute *tapi_port_default_attrs[] = {
&tapi_port_ring_attr.attr,
NULL,
};
static void tapi_port_free(struct kobject *kobj)
{
struct tapi_sysfs_port *port = container_of(kobj, struct tapi_sysfs_port, kobj);
kfree(port);
}
static struct sysfs_ops tapi_port_sysfs_ops = {
.show = tapi_port_show,
.store = tapi_port_store,
};
static struct kobj_type tapi_port_ktype = {
.release = tapi_port_free,
.sysfs_ops = &tapi_port_sysfs_ops,
.default_attrs = tapi_port_default_attrs,
};
struct tapi_sysfs_port *tapi_port_alloc(struct tapi_device *tdev, unsigned int id)
{
struct tapi_sysfs_port *port;
int ret;
port = kzalloc(sizeof(*port), GFP_KERNEL);
port->tdev = tdev;
port->id = id;
ret = kobject_init_and_add(&port->kobj, &tapi_port_ktype, &tdev->dev.kobj,
"port%d", id);
if (ret) {
kfree(port);
return ERR_PTR(ret);
}
return port;
}
void tapi_port_delete(struct tapi_sysfs_port *port)
{
kobject_del(&port->kobj);
kobject_put(&port->kobj);
}