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:
7
package/lqtapi/src/tapi/Makefile
Normal file
7
package/lqtapi/src/tapi/Makefile
Normal 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
|
||||
193
package/lqtapi/src/tapi/tapi-control.c
Normal file
193
package/lqtapi/src/tapi/tapi-control.c
Normal 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);
|
||||
}
|
||||
250
package/lqtapi/src/tapi/tapi-core.c
Normal file
250
package/lqtapi/src/tapi/tapi-core.c
Normal 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");
|
||||
99
package/lqtapi/src/tapi/tapi-input.c
Normal file
99
package/lqtapi/src/tapi/tapi-input.c
Normal 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);
|
||||
}
|
||||
62
package/lqtapi/src/tapi/tapi-nl.c
Normal file
62
package/lqtapi/src/tapi/tapi-nl.c
Normal 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);
|
||||
}
|
||||
|
||||
82
package/lqtapi/src/tapi/tapi-port.c
Normal file
82
package/lqtapi/src/tapi/tapi-port.c
Normal 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);
|
||||
}
|
||||
201
package/lqtapi/src/tapi/tapi-stream.c
Normal file
201
package/lqtapi/src/tapi/tapi-stream.c
Normal 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);
|
||||
107
package/lqtapi/src/tapi/tapi-sysfs-port.c
Normal file
107
package/lqtapi/src/tapi/tapi-sysfs-port.c
Normal 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);
|
||||
}
|
||||
Reference in New Issue
Block a user