staging: ozwpan: Remove from tree
Ozwpan is completely unmaintained and potentially a security problem. As this is a staging driver, it should be removed, since it has been abandoned. Cc: Shigekatsu Tateno <shigekatsu.tateno@atmel.com> Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
parent
f70d631832
commit
a73e99cb67
|
@ -9734,11 +9734,6 @@ W: http://wiki.laptop.org/go/DCON
|
|||
S: Maintained
|
||||
F: drivers/staging/olpc_dcon/
|
||||
|
||||
STAGING - OZMO DEVICES USB OVER WIFI DRIVER
|
||||
M: Shigekatsu Tateno <shigekatsu.tateno@atmel.com>
|
||||
S: Maintained
|
||||
F: drivers/staging/ozwpan/
|
||||
|
||||
STAGING - PARALLEL LCD/KEYPAD PANEL DRIVER
|
||||
M: Willy Tarreau <willy@meta-x.org>
|
||||
S: Odd Fixes
|
||||
|
|
|
@ -76,8 +76,6 @@ source "drivers/staging/android/Kconfig"
|
|||
|
||||
source "drivers/staging/board/Kconfig"
|
||||
|
||||
source "drivers/staging/ozwpan/Kconfig"
|
||||
|
||||
source "drivers/staging/gdm72xx/Kconfig"
|
||||
|
||||
source "drivers/staging/gdm724x/Kconfig"
|
||||
|
|
|
@ -31,7 +31,6 @@ obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI4) += ste_rmi4/
|
|||
obj-$(CONFIG_MFD_NVEC) += nvec/
|
||||
obj-$(CONFIG_ANDROID) += android/
|
||||
obj-$(CONFIG_STAGING_BOARD) += board/
|
||||
obj-$(CONFIG_USB_WPAN_HCD) += ozwpan/
|
||||
obj-$(CONFIG_WIMAX_GDM72XX) += gdm72xx/
|
||||
obj-$(CONFIG_LTE_GDM724X) += gdm724x/
|
||||
obj-$(CONFIG_FIREWIRE_SERIAL) += fwserial/
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
config USB_WPAN_HCD
|
||||
tristate "USB over WiFi Host Controller"
|
||||
depends on USB && NET
|
||||
help
|
||||
A driver for USB Host Controllers that are compatible with
|
||||
Ozmo Devices USB over WiFi technology.
|
||||
|
||||
To compile this driver a module, choose M here: the module
|
||||
will be called "ozwpan".
|
|
@ -1,16 +0,0 @@
|
|||
# -----------------------------------------------------------------------------
|
||||
# Copyright (c) 2011 Ozmo Inc
|
||||
# Released under the GNU General Public License Version 2 (GPLv2).
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
obj-$(CONFIG_USB_WPAN_HCD) += ozwpan.o
|
||||
ozwpan-y := \
|
||||
ozmain.o \
|
||||
ozpd.o \
|
||||
ozusbsvc.o \
|
||||
ozusbsvc1.o \
|
||||
ozhcd.o \
|
||||
ozeltbuf.o \
|
||||
ozproto.o \
|
||||
ozcdev.o \
|
||||
ozurbparanoia.o
|
|
@ -1,25 +0,0 @@
|
|||
OZWPAN USB Host Controller Driver
|
||||
---------------------------------
|
||||
This driver is a USB HCD driver that does not have an associated a physical
|
||||
device but instead uses Wi-Fi to communicate with the wireless peripheral.
|
||||
The USB requests are converted into a layer 2 network protocol and transmitted
|
||||
on the network using an ethertype (0x892e) regestered to Ozmo Device Inc.
|
||||
This driver is compatible with existing wireless devices that use Ozmo Devices
|
||||
technology.
|
||||
|
||||
To operate the driver must be bound to a suitable network interface. This can
|
||||
be done when the module is loaded (specifying the name of the network interface
|
||||
as a parameter - e.g. 'insmod ozwpan g_net_dev=go0') or can be bound after
|
||||
loading using an ioctl call. See the ozappif.h file and the ioctls
|
||||
OZ_IOCTL_ADD_BINDING and OZ_IOCTL_REMOVE_BINDING.
|
||||
|
||||
The devices connect to the host use Wi-Fi Direct so a network card that supports
|
||||
Wi-Fi direct is required. A recent version (0.8.x or later) version of the
|
||||
wpa_supplicant can be used to setup the network interface to create a persistent
|
||||
autonomous group (for older pre-WFD peripherals) or put in a listen state to
|
||||
allow group negotiation to occur for more recent devices that support WFD.
|
||||
|
||||
The protocol used over the network does not directly mimic the USB bus
|
||||
transactions as this would be rather busy and inefficient. Instead the chapter 9
|
||||
requests are converted into a request/response pair of messages. (See
|
||||
ozprotocol.h for data structures used in the protocol).
|
|
@ -1,14 +0,0 @@
|
|||
TODO:
|
||||
- Convert event tracing code to in-kernel tracing infrastructure
|
||||
- Check for remaining ioctl & check if that can be converted into
|
||||
sysfs entries
|
||||
- Convert debug prints to appropriate dev_debug or something better
|
||||
- Modify Kconfig to add CONFIG option for enabling/disabling event
|
||||
tracing.
|
||||
- check USB HCD implementation is complete and correct.
|
||||
- code review by USB developer community.
|
||||
- testing with as many devices as possible.
|
||||
|
||||
Please send any patches for this driver to
|
||||
Shigekatsu Tateno <shigekatsu.tateno@atmel.com>
|
||||
and Greg Kroah-Hartman <gregkh@linuxfoundation.org>.
|
|
@ -1,36 +0,0 @@
|
|||
/* -----------------------------------------------------------------------------
|
||||
* Copyright (c) 2011 Ozmo Inc
|
||||
* Released under the GNU General Public License Version 2 (GPLv2).
|
||||
* -----------------------------------------------------------------------------
|
||||
*/
|
||||
#ifndef _OZAPPIF_H
|
||||
#define _OZAPPIF_H
|
||||
|
||||
#define OZ_IOCTL_MAGIC 0xf4
|
||||
|
||||
struct oz_mac_addr {
|
||||
__u8 a[6];
|
||||
};
|
||||
|
||||
#define OZ_MAX_PDS 8
|
||||
|
||||
struct oz_pd_list {
|
||||
__u32 count;
|
||||
struct oz_mac_addr addr[OZ_MAX_PDS];
|
||||
};
|
||||
|
||||
#define OZ_MAX_BINDING_LEN 32
|
||||
|
||||
struct oz_binding_info {
|
||||
char name[OZ_MAX_BINDING_LEN];
|
||||
};
|
||||
|
||||
#define OZ_IOCTL_GET_PD_LIST _IOR(OZ_IOCTL_MAGIC, 0, struct oz_pd_list)
|
||||
#define OZ_IOCTL_SET_ACTIVE_PD _IOW(OZ_IOCTL_MAGIC, 1, struct oz_mac_addr)
|
||||
#define OZ_IOCTL_GET_ACTIVE_PD _IOR(OZ_IOCTL_MAGIC, 2, struct oz_mac_addr)
|
||||
#define OZ_IOCTL_ADD_BINDING _IOW(OZ_IOCTL_MAGIC, 3, struct oz_binding_info)
|
||||
#define OZ_IOCTL_REMOVE_BINDING _IOW(OZ_IOCTL_MAGIC, 4, struct oz_binding_info)
|
||||
#define OZ_IOCTL_MAX 5
|
||||
|
||||
|
||||
#endif /* _OZAPPIF_H */
|
|
@ -1,554 +0,0 @@
|
|||
/* -----------------------------------------------------------------------------
|
||||
* Copyright (c) 2011 Ozmo Inc
|
||||
* Released under the GNU General Public License Version 2 (GPLv2).
|
||||
* -----------------------------------------------------------------------------
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/cdev.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/poll.h>
|
||||
#include <linux/sched.h>
|
||||
#include "ozdbg.h"
|
||||
#include "ozprotocol.h"
|
||||
#include "ozappif.h"
|
||||
#include "ozeltbuf.h"
|
||||
#include "ozpd.h"
|
||||
#include "ozproto.h"
|
||||
#include "ozcdev.h"
|
||||
|
||||
#define OZ_RD_BUF_SZ 256
|
||||
struct oz_cdev {
|
||||
dev_t devnum;
|
||||
struct cdev cdev;
|
||||
wait_queue_head_t rdq;
|
||||
spinlock_t lock;
|
||||
u8 active_addr[ETH_ALEN];
|
||||
struct oz_pd *active_pd;
|
||||
};
|
||||
|
||||
/* Per PD context for the serial service stored in the PD. */
|
||||
struct oz_serial_ctx {
|
||||
atomic_t ref_count;
|
||||
u8 tx_seq_num;
|
||||
u8 rx_seq_num;
|
||||
u8 rd_buf[OZ_RD_BUF_SZ];
|
||||
int rd_in;
|
||||
int rd_out;
|
||||
};
|
||||
|
||||
static struct oz_cdev g_cdev;
|
||||
static struct class *g_oz_class;
|
||||
|
||||
/*
|
||||
* Context: process and softirq
|
||||
*/
|
||||
static struct oz_serial_ctx *oz_cdev_claim_ctx(struct oz_pd *pd)
|
||||
{
|
||||
struct oz_serial_ctx *ctx;
|
||||
|
||||
spin_lock_bh(&pd->app_lock[OZ_APPID_SERIAL]);
|
||||
ctx = (struct oz_serial_ctx *) pd->app_ctx[OZ_APPID_SERIAL];
|
||||
if (ctx)
|
||||
atomic_inc(&ctx->ref_count);
|
||||
spin_unlock_bh(&pd->app_lock[OZ_APPID_SERIAL]);
|
||||
return ctx;
|
||||
}
|
||||
|
||||
/*
|
||||
* Context: softirq or process
|
||||
*/
|
||||
static void oz_cdev_release_ctx(struct oz_serial_ctx *ctx)
|
||||
{
|
||||
if (atomic_dec_and_test(&ctx->ref_count)) {
|
||||
oz_dbg(ON, "Dealloc serial context\n");
|
||||
kfree(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Context: process
|
||||
*/
|
||||
static int oz_cdev_open(struct inode *inode, struct file *filp)
|
||||
{
|
||||
struct oz_cdev *dev = container_of(inode->i_cdev, struct oz_cdev, cdev);
|
||||
|
||||
oz_dbg(ON, "major = %d minor = %d\n", imajor(inode), iminor(inode));
|
||||
|
||||
filp->private_data = dev;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Context: process
|
||||
*/
|
||||
static int oz_cdev_release(struct inode *inode, struct file *filp)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Context: process
|
||||
*/
|
||||
static ssize_t oz_cdev_read(struct file *filp, char __user *buf, size_t count,
|
||||
loff_t *fpos)
|
||||
{
|
||||
int n;
|
||||
int ix;
|
||||
|
||||
struct oz_pd *pd;
|
||||
struct oz_serial_ctx *ctx;
|
||||
|
||||
spin_lock_bh(&g_cdev.lock);
|
||||
pd = g_cdev.active_pd;
|
||||
if (pd)
|
||||
oz_pd_get(pd);
|
||||
spin_unlock_bh(&g_cdev.lock);
|
||||
if (pd == NULL)
|
||||
return -1;
|
||||
ctx = oz_cdev_claim_ctx(pd);
|
||||
if (ctx == NULL)
|
||||
goto out2;
|
||||
n = ctx->rd_in - ctx->rd_out;
|
||||
if (n < 0)
|
||||
n += OZ_RD_BUF_SZ;
|
||||
if (count > n)
|
||||
count = n;
|
||||
ix = ctx->rd_out;
|
||||
n = OZ_RD_BUF_SZ - ix;
|
||||
if (n > count)
|
||||
n = count;
|
||||
if (copy_to_user(buf, &ctx->rd_buf[ix], n)) {
|
||||
count = 0;
|
||||
goto out1;
|
||||
}
|
||||
ix += n;
|
||||
if (ix == OZ_RD_BUF_SZ)
|
||||
ix = 0;
|
||||
if (n < count) {
|
||||
if (copy_to_user(&buf[n], ctx->rd_buf, count-n)) {
|
||||
count = 0;
|
||||
goto out1;
|
||||
}
|
||||
ix = count-n;
|
||||
}
|
||||
ctx->rd_out = ix;
|
||||
out1:
|
||||
oz_cdev_release_ctx(ctx);
|
||||
out2:
|
||||
oz_pd_put(pd);
|
||||
return count;
|
||||
}
|
||||
|
||||
/*
|
||||
* Context: process
|
||||
*/
|
||||
static ssize_t oz_cdev_write(struct file *filp, const char __user *buf,
|
||||
size_t count, loff_t *fpos)
|
||||
{
|
||||
struct oz_pd *pd;
|
||||
struct oz_elt_buf *eb;
|
||||
struct oz_elt_info *ei;
|
||||
struct oz_elt *elt;
|
||||
struct oz_app_hdr *app_hdr;
|
||||
struct oz_serial_ctx *ctx;
|
||||
|
||||
if (count > sizeof(ei->data) - sizeof(*elt) - sizeof(*app_hdr))
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock_bh(&g_cdev.lock);
|
||||
pd = g_cdev.active_pd;
|
||||
if (pd)
|
||||
oz_pd_get(pd);
|
||||
spin_unlock_bh(&g_cdev.lock);
|
||||
if (pd == NULL)
|
||||
return -ENXIO;
|
||||
if (!(pd->state & OZ_PD_S_CONNECTED))
|
||||
return -EAGAIN;
|
||||
eb = &pd->elt_buff;
|
||||
ei = oz_elt_info_alloc(eb);
|
||||
if (ei == NULL) {
|
||||
count = 0;
|
||||
goto out;
|
||||
}
|
||||
elt = (struct oz_elt *)ei->data;
|
||||
app_hdr = (struct oz_app_hdr *)(elt+1);
|
||||
elt->length = sizeof(struct oz_app_hdr) + count;
|
||||
elt->type = OZ_ELT_APP_DATA;
|
||||
ei->app_id = OZ_APPID_SERIAL;
|
||||
ei->length = elt->length + sizeof(struct oz_elt);
|
||||
app_hdr->app_id = OZ_APPID_SERIAL;
|
||||
if (copy_from_user(app_hdr+1, buf, count))
|
||||
goto out;
|
||||
spin_lock_bh(&pd->app_lock[OZ_APPID_USB]);
|
||||
ctx = (struct oz_serial_ctx *) pd->app_ctx[OZ_APPID_SERIAL];
|
||||
if (ctx) {
|
||||
app_hdr->elt_seq_num = ctx->tx_seq_num++;
|
||||
if (ctx->tx_seq_num == 0)
|
||||
ctx->tx_seq_num = 1;
|
||||
spin_lock(&eb->lock);
|
||||
if (oz_queue_elt_info(eb, 0, 0, ei) == 0)
|
||||
ei = NULL;
|
||||
spin_unlock(&eb->lock);
|
||||
}
|
||||
spin_unlock_bh(&pd->app_lock[OZ_APPID_USB]);
|
||||
out:
|
||||
if (ei) {
|
||||
count = 0;
|
||||
spin_lock_bh(&eb->lock);
|
||||
oz_elt_info_free(eb, ei);
|
||||
spin_unlock_bh(&eb->lock);
|
||||
}
|
||||
oz_pd_put(pd);
|
||||
return count;
|
||||
}
|
||||
|
||||
/*
|
||||
* Context: process
|
||||
*/
|
||||
static int oz_set_active_pd(const u8 *addr)
|
||||
{
|
||||
int rc = 0;
|
||||
struct oz_pd *pd;
|
||||
struct oz_pd *old_pd;
|
||||
|
||||
pd = oz_pd_find(addr);
|
||||
if (pd) {
|
||||
spin_lock_bh(&g_cdev.lock);
|
||||
ether_addr_copy(g_cdev.active_addr, addr);
|
||||
old_pd = g_cdev.active_pd;
|
||||
g_cdev.active_pd = pd;
|
||||
spin_unlock_bh(&g_cdev.lock);
|
||||
if (old_pd)
|
||||
oz_pd_put(old_pd);
|
||||
} else {
|
||||
if (is_zero_ether_addr(addr)) {
|
||||
spin_lock_bh(&g_cdev.lock);
|
||||
pd = g_cdev.active_pd;
|
||||
g_cdev.active_pd = NULL;
|
||||
memset(g_cdev.active_addr, 0,
|
||||
sizeof(g_cdev.active_addr));
|
||||
spin_unlock_bh(&g_cdev.lock);
|
||||
if (pd)
|
||||
oz_pd_put(pd);
|
||||
} else {
|
||||
rc = -1;
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
* Context: process
|
||||
*/
|
||||
static long oz_cdev_ioctl(struct file *filp, unsigned int cmd,
|
||||
unsigned long arg)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
if (_IOC_TYPE(cmd) != OZ_IOCTL_MAGIC)
|
||||
return -ENOTTY;
|
||||
if (_IOC_NR(cmd) > OZ_IOCTL_MAX)
|
||||
return -ENOTTY;
|
||||
if (_IOC_DIR(cmd) & _IOC_READ)
|
||||
rc = !access_ok(VERIFY_WRITE, (void __user *)arg,
|
||||
_IOC_SIZE(cmd));
|
||||
else if (_IOC_DIR(cmd) & _IOC_WRITE)
|
||||
rc = !access_ok(VERIFY_READ, (void __user *)arg,
|
||||
_IOC_SIZE(cmd));
|
||||
if (rc)
|
||||
return -EFAULT;
|
||||
switch (cmd) {
|
||||
case OZ_IOCTL_GET_PD_LIST: {
|
||||
struct oz_pd_list list;
|
||||
|
||||
oz_dbg(ON, "OZ_IOCTL_GET_PD_LIST\n");
|
||||
memset(&list, 0, sizeof(list));
|
||||
list.count = oz_get_pd_list(list.addr, OZ_MAX_PDS);
|
||||
if (copy_to_user((void __user *)arg, &list,
|
||||
sizeof(list)))
|
||||
return -EFAULT;
|
||||
}
|
||||
break;
|
||||
case OZ_IOCTL_SET_ACTIVE_PD: {
|
||||
u8 addr[ETH_ALEN];
|
||||
|
||||
oz_dbg(ON, "OZ_IOCTL_SET_ACTIVE_PD\n");
|
||||
if (copy_from_user(addr, (void __user *)arg, ETH_ALEN))
|
||||
return -EFAULT;
|
||||
rc = oz_set_active_pd(addr);
|
||||
}
|
||||
break;
|
||||
case OZ_IOCTL_GET_ACTIVE_PD: {
|
||||
u8 addr[ETH_ALEN];
|
||||
|
||||
oz_dbg(ON, "OZ_IOCTL_GET_ACTIVE_PD\n");
|
||||
spin_lock_bh(&g_cdev.lock);
|
||||
ether_addr_copy(addr, g_cdev.active_addr);
|
||||
spin_unlock_bh(&g_cdev.lock);
|
||||
if (copy_to_user((void __user *)arg, addr, ETH_ALEN))
|
||||
return -EFAULT;
|
||||
}
|
||||
break;
|
||||
case OZ_IOCTL_ADD_BINDING:
|
||||
case OZ_IOCTL_REMOVE_BINDING: {
|
||||
struct oz_binding_info b;
|
||||
|
||||
if (copy_from_user(&b, (void __user *)arg,
|
||||
sizeof(struct oz_binding_info))) {
|
||||
return -EFAULT;
|
||||
}
|
||||
/* Make sure name is null terminated. */
|
||||
b.name[OZ_MAX_BINDING_LEN-1] = 0;
|
||||
if (cmd == OZ_IOCTL_ADD_BINDING)
|
||||
oz_binding_add(b.name);
|
||||
else
|
||||
oz_binding_remove(b.name);
|
||||
}
|
||||
break;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
* Context: process
|
||||
*/
|
||||
static unsigned int oz_cdev_poll(struct file *filp, poll_table *wait)
|
||||
{
|
||||
unsigned int ret = 0;
|
||||
struct oz_cdev *dev = filp->private_data;
|
||||
|
||||
oz_dbg(ON, "Poll called wait = %p\n", wait);
|
||||
spin_lock_bh(&dev->lock);
|
||||
if (dev->active_pd) {
|
||||
struct oz_serial_ctx *ctx = oz_cdev_claim_ctx(dev->active_pd);
|
||||
|
||||
if (ctx) {
|
||||
if (ctx->rd_in != ctx->rd_out)
|
||||
ret |= POLLIN | POLLRDNORM;
|
||||
oz_cdev_release_ctx(ctx);
|
||||
}
|
||||
}
|
||||
spin_unlock_bh(&dev->lock);
|
||||
if (wait)
|
||||
poll_wait(filp, &dev->rdq, wait);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
*/
|
||||
static const struct file_operations oz_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = oz_cdev_open,
|
||||
.release = oz_cdev_release,
|
||||
.read = oz_cdev_read,
|
||||
.write = oz_cdev_write,
|
||||
.unlocked_ioctl = oz_cdev_ioctl,
|
||||
.poll = oz_cdev_poll
|
||||
};
|
||||
|
||||
/*
|
||||
* Context: process
|
||||
*/
|
||||
int oz_cdev_register(void)
|
||||
{
|
||||
int err;
|
||||
struct device *dev;
|
||||
|
||||
memset(&g_cdev, 0, sizeof(g_cdev));
|
||||
err = alloc_chrdev_region(&g_cdev.devnum, 0, 1, "ozwpan");
|
||||
if (err < 0)
|
||||
return err;
|
||||
oz_dbg(ON, "Alloc dev number %d:%d\n",
|
||||
MAJOR(g_cdev.devnum), MINOR(g_cdev.devnum));
|
||||
cdev_init(&g_cdev.cdev, &oz_fops);
|
||||
g_cdev.cdev.owner = THIS_MODULE;
|
||||
spin_lock_init(&g_cdev.lock);
|
||||
init_waitqueue_head(&g_cdev.rdq);
|
||||
err = cdev_add(&g_cdev.cdev, g_cdev.devnum, 1);
|
||||
if (err < 0) {
|
||||
oz_dbg(ON, "Failed to add cdev\n");
|
||||
goto unregister;
|
||||
}
|
||||
g_oz_class = class_create(THIS_MODULE, "ozmo_wpan");
|
||||
if (IS_ERR(g_oz_class)) {
|
||||
oz_dbg(ON, "Failed to register ozmo_wpan class\n");
|
||||
err = PTR_ERR(g_oz_class);
|
||||
goto delete;
|
||||
}
|
||||
dev = device_create(g_oz_class, NULL, g_cdev.devnum, NULL, "ozwpan");
|
||||
if (IS_ERR(dev)) {
|
||||
oz_dbg(ON, "Failed to create sysfs entry for cdev\n");
|
||||
err = PTR_ERR(dev);
|
||||
goto delete;
|
||||
}
|
||||
return 0;
|
||||
|
||||
delete:
|
||||
cdev_del(&g_cdev.cdev);
|
||||
unregister:
|
||||
unregister_chrdev_region(g_cdev.devnum, 1);
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* Context: process
|
||||
*/
|
||||
int oz_cdev_deregister(void)
|
||||
{
|
||||
cdev_del(&g_cdev.cdev);
|
||||
unregister_chrdev_region(g_cdev.devnum, 1);
|
||||
if (g_oz_class) {
|
||||
device_destroy(g_oz_class, g_cdev.devnum);
|
||||
class_destroy(g_oz_class);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Context: process
|
||||
*/
|
||||
int oz_cdev_init(void)
|
||||
{
|
||||
oz_app_enable(OZ_APPID_SERIAL, 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Context: process
|
||||
*/
|
||||
void oz_cdev_term(void)
|
||||
{
|
||||
oz_app_enable(OZ_APPID_SERIAL, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Context: softirq-serialized
|
||||
*/
|
||||
int oz_cdev_start(struct oz_pd *pd, int resume)
|
||||
{
|
||||
struct oz_serial_ctx *ctx;
|
||||
struct oz_serial_ctx *old_ctx;
|
||||
|
||||
if (resume) {
|
||||
oz_dbg(ON, "Serial service resumed\n");
|
||||
return 0;
|
||||
}
|
||||
ctx = kzalloc(sizeof(struct oz_serial_ctx), GFP_ATOMIC);
|
||||
if (ctx == NULL)
|
||||
return -ENOMEM;
|
||||
atomic_set(&ctx->ref_count, 1);
|
||||
ctx->tx_seq_num = 1;
|
||||
spin_lock_bh(&pd->app_lock[OZ_APPID_SERIAL]);
|
||||
old_ctx = pd->app_ctx[OZ_APPID_SERIAL];
|
||||
if (old_ctx) {
|
||||
spin_unlock_bh(&pd->app_lock[OZ_APPID_SERIAL]);
|
||||
kfree(ctx);
|
||||
} else {
|
||||
pd->app_ctx[OZ_APPID_SERIAL] = ctx;
|
||||
spin_unlock_bh(&pd->app_lock[OZ_APPID_SERIAL]);
|
||||
}
|
||||
spin_lock(&g_cdev.lock);
|
||||
if ((g_cdev.active_pd == NULL) &&
|
||||
ether_addr_equal(pd->mac_addr, g_cdev.active_addr)) {
|
||||
oz_pd_get(pd);
|
||||
g_cdev.active_pd = pd;
|
||||
oz_dbg(ON, "Active PD arrived\n");
|
||||
}
|
||||
spin_unlock(&g_cdev.lock);
|
||||
oz_dbg(ON, "Serial service started\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Context: softirq or process
|
||||
*/
|
||||
void oz_cdev_stop(struct oz_pd *pd, int pause)
|
||||
{
|
||||
struct oz_serial_ctx *ctx;
|
||||
|
||||
if (pause) {
|
||||
oz_dbg(ON, "Serial service paused\n");
|
||||
return;
|
||||
}
|
||||
spin_lock_bh(&pd->app_lock[OZ_APPID_SERIAL]);
|
||||
ctx = (struct oz_serial_ctx *) pd->app_ctx[OZ_APPID_SERIAL];
|
||||
pd->app_ctx[OZ_APPID_SERIAL] = NULL;
|
||||
spin_unlock_bh(&pd->app_lock[OZ_APPID_SERIAL]);
|
||||
if (ctx)
|
||||
oz_cdev_release_ctx(ctx);
|
||||
spin_lock(&g_cdev.lock);
|
||||
if (pd == g_cdev.active_pd)
|
||||
g_cdev.active_pd = NULL;
|
||||
else
|
||||
pd = NULL;
|
||||
spin_unlock(&g_cdev.lock);
|
||||
if (pd) {
|
||||
oz_pd_put(pd);
|
||||
oz_dbg(ON, "Active PD departed\n");
|
||||
}
|
||||
oz_dbg(ON, "Serial service stopped\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* Context: softirq-serialized
|
||||
*/
|
||||
void oz_cdev_rx(struct oz_pd *pd, struct oz_elt *elt)
|
||||
{
|
||||
struct oz_serial_ctx *ctx;
|
||||
struct oz_app_hdr *app_hdr;
|
||||
u8 *data;
|
||||
int len;
|
||||
int space;
|
||||
int copy_sz;
|
||||
int ix;
|
||||
|
||||
ctx = oz_cdev_claim_ctx(pd);
|
||||
if (ctx == NULL) {
|
||||
oz_dbg(ON, "Cannot claim serial context\n");
|
||||
return;
|
||||
}
|
||||
|
||||
app_hdr = (struct oz_app_hdr *)(elt+1);
|
||||
/* If sequence number is non-zero then check it is not a duplicate.
|
||||
*/
|
||||
if (app_hdr->elt_seq_num != 0) {
|
||||
if (((ctx->rx_seq_num - app_hdr->elt_seq_num) & 0x80) == 0) {
|
||||
/* Reject duplicate element. */
|
||||
oz_dbg(ON, "Duplicate element:%02x %02x\n",
|
||||
app_hdr->elt_seq_num, ctx->rx_seq_num);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
ctx->rx_seq_num = app_hdr->elt_seq_num;
|
||||
len = elt->length - sizeof(struct oz_app_hdr);
|
||||
data = ((u8 *)(elt+1)) + sizeof(struct oz_app_hdr);
|
||||
if (len <= 0)
|
||||
goto out;
|
||||
space = ctx->rd_out - ctx->rd_in - 1;
|
||||
if (space < 0)
|
||||
space += OZ_RD_BUF_SZ;
|
||||
if (len > space) {
|
||||
oz_dbg(ON, "Not enough space:%d %d\n", len, space);
|
||||
len = space;
|
||||
}
|
||||
ix = ctx->rd_in;
|
||||
copy_sz = OZ_RD_BUF_SZ - ix;
|
||||
if (copy_sz > len)
|
||||
copy_sz = len;
|
||||
memcpy(&ctx->rd_buf[ix], data, copy_sz);
|
||||
len -= copy_sz;
|
||||
ix += copy_sz;
|
||||
if (ix == OZ_RD_BUF_SZ)
|
||||
ix = 0;
|
||||
if (len) {
|
||||
memcpy(ctx->rd_buf, data+copy_sz, len);
|
||||
ix = len;
|
||||
}
|
||||
ctx->rd_in = ix;
|
||||
wake_up(&g_cdev.rdq);
|
||||
out:
|
||||
oz_cdev_release_ctx(ctx);
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
/* -----------------------------------------------------------------------------
|
||||
* Copyright (c) 2011 Ozmo Inc
|
||||
* Released under the GNU General Public License Version 2 (GPLv2).
|
||||
* -----------------------------------------------------------------------------
|
||||
*/
|
||||
#ifndef _OZCDEV_H
|
||||
#define _OZCDEV_H
|
||||
|
||||
int oz_cdev_register(void);
|
||||
int oz_cdev_deregister(void);
|
||||
int oz_cdev_init(void);
|
||||
void oz_cdev_term(void);
|
||||
int oz_cdev_start(struct oz_pd *pd, int resume);
|
||||
void oz_cdev_stop(struct oz_pd *pd, int pause);
|
||||
void oz_cdev_rx(struct oz_pd *pd, struct oz_elt *elt);
|
||||
|
||||
#endif /* _OZCDEV_H */
|
|
@ -1,54 +0,0 @@
|
|||
/* -----------------------------------------------------------------------------
|
||||
* Copyright (c) 2011 Ozmo Inc
|
||||
* Released under the GNU General Public License Version 2 (GPLv2).
|
||||
* ---------------------------------------------------------------------------*/
|
||||
|
||||
#ifndef _OZDBG_H
|
||||
#define _OZDBG_H
|
||||
|
||||
#define OZ_WANT_DBG 0
|
||||
#define OZ_WANT_VERBOSE_DBG 1
|
||||
|
||||
#define OZ_DBG_ON 0x0
|
||||
#define OZ_DBG_STREAM 0x1
|
||||
#define OZ_DBG_URB 0x2
|
||||
#define OZ_DBG_CTRL_DETAIL 0x4
|
||||
#define OZ_DBG_HUB 0x8
|
||||
#define OZ_DBG_RX_FRAMES 0x10
|
||||
#define OZ_DBG_TX_FRAMES 0x20
|
||||
|
||||
#define OZ_DEFAULT_DBG_MASK \
|
||||
( \
|
||||
/* OZ_DBG_STREAM | */ \
|
||||
/* OZ_DBG_URB | */ \
|
||||
/* OZ_DBG_CTRL_DETAIL | */ \
|
||||
OZ_DBG_HUB | \
|
||||
/* OZ_DBG_RX_FRAMES | */ \
|
||||
/* OZ_DBG_TX_FRAMES | */ \
|
||||
0)
|
||||
|
||||
extern unsigned int oz_dbg_mask;
|
||||
|
||||
#define oz_want_dbg(mask) \
|
||||
((OZ_WANT_DBG && (OZ_DBG_##mask == OZ_DBG_ON)) || \
|
||||
(OZ_WANT_VERBOSE_DBG && (OZ_DBG_##mask & oz_dbg_mask)))
|
||||
|
||||
#define oz_dbg(mask, fmt, ...) \
|
||||
do { \
|
||||
if (oz_want_dbg(mask)) \
|
||||
pr_debug(fmt, ##__VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
#define oz_cdev_dbg(cdev, mask, fmt, ...) \
|
||||
do { \
|
||||
if (oz_want_dbg(mask)) \
|
||||
netdev_dbg((cdev)->dev, fmt, ##__VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
#define oz_pd_dbg(pd, mask, fmt, ...) \
|
||||
do { \
|
||||
if (oz_want_dbg(mask)) \
|
||||
pr_debug(fmt, ##__VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
#endif /* _OZDBG_H */
|
|
@ -1,252 +0,0 @@
|
|||
/* -----------------------------------------------------------------------------
|
||||
* Copyright (c) 2011 Ozmo Inc
|
||||
* Released under the GNU General Public License Version 2 (GPLv2).
|
||||
* -----------------------------------------------------------------------------
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include "ozdbg.h"
|
||||
#include "ozprotocol.h"
|
||||
#include "ozeltbuf.h"
|
||||
#include "ozpd.h"
|
||||
|
||||
/*
|
||||
* Context: softirq-serialized
|
||||
*/
|
||||
void oz_elt_buf_init(struct oz_elt_buf *buf)
|
||||
{
|
||||
memset(buf, 0, sizeof(struct oz_elt_buf));
|
||||
INIT_LIST_HEAD(&buf->stream_list);
|
||||
INIT_LIST_HEAD(&buf->order_list);
|
||||
INIT_LIST_HEAD(&buf->isoc_list);
|
||||
spin_lock_init(&buf->lock);
|
||||
}
|
||||
|
||||
/*
|
||||
* Context: softirq or process
|
||||
*/
|
||||
void oz_elt_buf_term(struct oz_elt_buf *buf)
|
||||
{
|
||||
struct oz_elt_info *ei, *n;
|
||||
|
||||
list_for_each_entry_safe(ei, n, &buf->isoc_list, link_order)
|
||||
kfree(ei);
|
||||
list_for_each_entry_safe(ei, n, &buf->order_list, link_order)
|
||||
kfree(ei);
|
||||
}
|
||||
|
||||
/*
|
||||
* Context: softirq or process
|
||||
*/
|
||||
struct oz_elt_info *oz_elt_info_alloc(struct oz_elt_buf *buf)
|
||||
{
|
||||
struct oz_elt_info *ei;
|
||||
|
||||
ei = kmem_cache_zalloc(oz_elt_info_cache, GFP_ATOMIC);
|
||||
if (ei) {
|
||||
INIT_LIST_HEAD(&ei->link);
|
||||
INIT_LIST_HEAD(&ei->link_order);
|
||||
}
|
||||
return ei;
|
||||
}
|
||||
|
||||
/*
|
||||
* Precondition: oz_elt_buf.lock must be held.
|
||||
* Context: softirq or process
|
||||
*/
|
||||
void oz_elt_info_free(struct oz_elt_buf *buf, struct oz_elt_info *ei)
|
||||
{
|
||||
if (ei)
|
||||
kmem_cache_free(oz_elt_info_cache, ei);
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------------
|
||||
* Context: softirq
|
||||
*/
|
||||
void oz_elt_info_free_chain(struct oz_elt_buf *buf, struct list_head *list)
|
||||
{
|
||||
struct oz_elt_info *ei, *n;
|
||||
|
||||
spin_lock_bh(&buf->lock);
|
||||
list_for_each_entry_safe(ei, n, list->next, link)
|
||||
oz_elt_info_free(buf, ei);
|
||||
spin_unlock_bh(&buf->lock);
|
||||
}
|
||||
|
||||
int oz_elt_stream_create(struct oz_elt_buf *buf, u8 id, int max_buf_count)
|
||||
{
|
||||
struct oz_elt_stream *st;
|
||||
|
||||
oz_dbg(ON, "%s: (0x%x)\n", __func__, id);
|
||||
|
||||
st = kzalloc(sizeof(struct oz_elt_stream), GFP_ATOMIC);
|
||||
if (st == NULL)
|
||||
return -ENOMEM;
|
||||
atomic_set(&st->ref_count, 1);
|
||||
st->id = id;
|
||||
st->max_buf_count = max_buf_count;
|
||||
INIT_LIST_HEAD(&st->elt_list);
|
||||
spin_lock_bh(&buf->lock);
|
||||
list_add_tail(&st->link, &buf->stream_list);
|
||||
spin_unlock_bh(&buf->lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int oz_elt_stream_delete(struct oz_elt_buf *buf, u8 id)
|
||||
{
|
||||
struct list_head *e, *n;
|
||||
struct oz_elt_stream *st = NULL;
|
||||
|
||||
oz_dbg(ON, "%s: (0x%x)\n", __func__, id);
|
||||
spin_lock_bh(&buf->lock);
|
||||
list_for_each(e, &buf->stream_list) {
|
||||
st = list_entry(e, struct oz_elt_stream, link);
|
||||
if (st->id == id) {
|
||||
list_del(e);
|
||||
break;
|
||||
}
|
||||
st = NULL;
|
||||
}
|
||||
if (!st) {
|
||||
spin_unlock_bh(&buf->lock);
|
||||
return -1;
|
||||
}
|
||||
list_for_each_safe(e, n, &st->elt_list) {
|
||||
struct oz_elt_info *ei =
|
||||
list_entry(e, struct oz_elt_info, link);
|
||||
list_del_init(&ei->link);
|
||||
list_del_init(&ei->link_order);
|
||||
st->buf_count -= ei->length;
|
||||
oz_dbg(STREAM, "Stream down: %d %d %d\n",
|
||||
st->buf_count, ei->length, atomic_read(&st->ref_count));
|
||||
oz_elt_stream_put(st);
|
||||
oz_elt_info_free(buf, ei);
|
||||
}
|
||||
spin_unlock_bh(&buf->lock);
|
||||
oz_elt_stream_put(st);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void oz_elt_stream_get(struct oz_elt_stream *st)
|
||||
{
|
||||
atomic_inc(&st->ref_count);
|
||||
}
|
||||
|
||||
void oz_elt_stream_put(struct oz_elt_stream *st)
|
||||
{
|
||||
if (atomic_dec_and_test(&st->ref_count)) {
|
||||
oz_dbg(ON, "Stream destroyed\n");
|
||||
kfree(st);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Precondition: Element buffer lock must be held.
|
||||
* If this function fails the caller is responsible for deallocating the elt
|
||||
* info structure.
|
||||
*/
|
||||
int oz_queue_elt_info(struct oz_elt_buf *buf, u8 isoc, u8 id,
|
||||
struct oz_elt_info *ei)
|
||||
{
|
||||
struct oz_elt_stream *st = NULL;
|
||||
struct list_head *e;
|
||||
|
||||
if (id) {
|
||||
list_for_each(e, &buf->stream_list) {
|
||||
st = list_entry(e, struct oz_elt_stream, link);
|
||||
if (st->id == id)
|
||||
break;
|
||||
}
|
||||
if (e == &buf->stream_list) {
|
||||
/* Stream specified but stream not known so fail.
|
||||
* Caller deallocates element info. */
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
if (st) {
|
||||
/* If this is an ISOC fixed element that needs a frame number
|
||||
* then insert that now. Earlier we stored the unit count in
|
||||
* this field.
|
||||
*/
|
||||
struct oz_isoc_fixed *body = (struct oz_isoc_fixed *)
|
||||
&ei->data[sizeof(struct oz_elt)];
|
||||
if ((body->app_id == OZ_APPID_USB) && (body->type
|
||||
== OZ_USB_ENDPOINT_DATA) &&
|
||||
(body->format == OZ_DATA_F_ISOC_FIXED)) {
|
||||
u8 unit_count = body->frame_number;
|
||||
|
||||
body->frame_number = st->frame_number;
|
||||
st->frame_number += unit_count;
|
||||
}
|
||||
/* Claim stream and update accounts */
|
||||
oz_elt_stream_get(st);
|
||||
ei->stream = st;
|
||||
st->buf_count += ei->length;
|
||||
/* Add to list in stream. */
|
||||
list_add_tail(&ei->link, &st->elt_list);
|
||||
oz_dbg(STREAM, "Stream up: %d %d\n", st->buf_count, ei->length);
|
||||
/* Check if we have too much buffered for this stream. If so
|
||||
* start dropping elements until we are back in bounds.
|
||||
*/
|
||||
while ((st->buf_count > st->max_buf_count) &&
|
||||
!list_empty(&st->elt_list)) {
|
||||
struct oz_elt_info *ei2 =
|
||||
list_first_entry(&st->elt_list,
|
||||
struct oz_elt_info, link);
|
||||
list_del_init(&ei2->link);
|
||||
list_del_init(&ei2->link_order);
|
||||
st->buf_count -= ei2->length;
|
||||
oz_elt_info_free(buf, ei2);
|
||||
oz_elt_stream_put(st);
|
||||
}
|
||||
}
|
||||
list_add_tail(&ei->link_order, isoc ?
|
||||
&buf->isoc_list : &buf->order_list);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int oz_select_elts_for_tx(struct oz_elt_buf *buf, u8 isoc, unsigned *len,
|
||||
unsigned max_len, struct list_head *list)
|
||||
{
|
||||
int count = 0;
|
||||
struct list_head *el;
|
||||
struct oz_elt_info *ei, *n;
|
||||
|
||||
spin_lock_bh(&buf->lock);
|
||||
if (isoc)
|
||||
el = &buf->isoc_list;
|
||||
else
|
||||
el = &buf->order_list;
|
||||
|
||||
list_for_each_entry_safe(ei, n, el, link_order) {
|
||||
if ((*len + ei->length) <= max_len) {
|
||||
struct oz_app_hdr *app_hdr = (struct oz_app_hdr *)
|
||||
&ei->data[sizeof(struct oz_elt)];
|
||||
app_hdr->elt_seq_num = buf->tx_seq_num[ei->app_id]++;
|
||||
if (buf->tx_seq_num[ei->app_id] == 0)
|
||||
buf->tx_seq_num[ei->app_id] = 1;
|
||||
*len += ei->length;
|
||||
list_del(&ei->link);
|
||||
list_del(&ei->link_order);
|
||||
if (ei->stream) {
|
||||
ei->stream->buf_count -= ei->length;
|
||||
oz_dbg(STREAM, "Stream down: %d %d\n",
|
||||
ei->stream->buf_count, ei->length);
|
||||
oz_elt_stream_put(ei->stream);
|
||||
ei->stream = NULL;
|
||||
}
|
||||
INIT_LIST_HEAD(&ei->link_order);
|
||||
list_add_tail(&ei->link, list);
|
||||
count++;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
spin_unlock_bh(&buf->lock);
|
||||
return count;
|
||||
}
|
||||
|
||||
int oz_are_elts_available(struct oz_elt_buf *buf)
|
||||
{
|
||||
return !list_empty(&buf->order_list);
|
||||
}
|
|
@ -1,65 +0,0 @@
|
|||
/* -----------------------------------------------------------------------------
|
||||
* Copyright (c) 2011 Ozmo Inc
|
||||
* Released under the GNU General Public License Version 2 (GPLv2).
|
||||
* -----------------------------------------------------------------------------
|
||||
*/
|
||||
#ifndef _OZELTBUF_H
|
||||
#define _OZELTBUF_H
|
||||
|
||||
#include "ozprotocol.h"
|
||||
|
||||
/*-----------------------------------------------------------------------------
|
||||
*/
|
||||
struct oz_pd;
|
||||
typedef void (*oz_elt_callback_t)(struct oz_pd *pd, long context);
|
||||
|
||||
struct oz_elt_stream {
|
||||
struct list_head link;
|
||||
struct list_head elt_list;
|
||||
atomic_t ref_count;
|
||||
unsigned buf_count;
|
||||
unsigned max_buf_count;
|
||||
u8 frame_number;
|
||||
u8 id;
|
||||
};
|
||||
|
||||
#define OZ_MAX_ELT_PAYLOAD 255
|
||||
struct oz_elt_info {
|
||||
struct list_head link;
|
||||
struct list_head link_order;
|
||||
u8 flags;
|
||||
u8 app_id;
|
||||
oz_elt_callback_t callback;
|
||||
long context;
|
||||
struct oz_elt_stream *stream;
|
||||
u8 data[sizeof(struct oz_elt) + OZ_MAX_ELT_PAYLOAD];
|
||||
int length;
|
||||
};
|
||||
/* Flags values */
|
||||
#define OZ_EI_F_MARKED 0x1
|
||||
|
||||
struct oz_elt_buf {
|
||||
spinlock_t lock;
|
||||
struct list_head stream_list;
|
||||
struct list_head order_list;
|
||||
struct list_head isoc_list;
|
||||
u8 tx_seq_num[OZ_NB_APPS];
|
||||
};
|
||||
|
||||
void oz_elt_buf_init(struct oz_elt_buf *buf);
|
||||
void oz_elt_buf_term(struct oz_elt_buf *buf);
|
||||
struct oz_elt_info *oz_elt_info_alloc(struct oz_elt_buf *buf);
|
||||
void oz_elt_info_free(struct oz_elt_buf *buf, struct oz_elt_info *ei);
|
||||
void oz_elt_info_free_chain(struct oz_elt_buf *buf, struct list_head *list);
|
||||
int oz_elt_stream_create(struct oz_elt_buf *buf, u8 id, int max_buf_count);
|
||||
int oz_elt_stream_delete(struct oz_elt_buf *buf, u8 id);
|
||||
void oz_elt_stream_get(struct oz_elt_stream *st);
|
||||
void oz_elt_stream_put(struct oz_elt_stream *st);
|
||||
int oz_queue_elt_info(struct oz_elt_buf *buf, u8 isoc, u8 id,
|
||||
struct oz_elt_info *ei);
|
||||
int oz_select_elts_for_tx(struct oz_elt_buf *buf, u8 isoc, unsigned *len,
|
||||
unsigned max_len, struct list_head *list);
|
||||
int oz_are_elts_available(struct oz_elt_buf *buf);
|
||||
|
||||
#endif /* _OZELTBUF_H */
|
||||
|
File diff suppressed because it is too large
Load Diff
|
@ -1,15 +0,0 @@
|
|||
/* -----------------------------------------------------------------------------
|
||||
* Copyright (c) 2011 Ozmo Inc
|
||||
* Released under the GNU General Public License Version 2 (GPLv2).
|
||||
* ---------------------------------------------------------------------------*/
|
||||
#ifndef _OZHCD_H
|
||||
#define _OZHCD_H
|
||||
|
||||
int oz_hcd_init(void);
|
||||
void oz_hcd_term(void);
|
||||
struct oz_port *oz_hcd_pd_arrived(void *ctx);
|
||||
void oz_hcd_pd_departed(struct oz_port *hport);
|
||||
void oz_hcd_pd_reset(void *hpd, void *hport);
|
||||
|
||||
#endif /* _OZHCD_H */
|
||||
|
|
@ -1,71 +0,0 @@
|
|||
/* -----------------------------------------------------------------------------
|
||||
* Copyright (c) 2011 Ozmo Inc
|
||||
* Released under the GNU General Public License Version 2 (GPLv2).
|
||||
* -----------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/ieee80211.h>
|
||||
#include "ozdbg.h"
|
||||
#include "ozpd.h"
|
||||
#include "ozproto.h"
|
||||
#include "ozcdev.h"
|
||||
|
||||
unsigned int oz_dbg_mask = OZ_DEFAULT_DBG_MASK;
|
||||
|
||||
/*
|
||||
* The name of the 802.11 mac device. Empty string is the default value but a
|
||||
* value can be supplied as a parameter to the module. An empty string means
|
||||
* bind to nothing. '*' means bind to all netcards - this includes non-802.11
|
||||
* netcards. Bindings can be added later using an IOCTL.
|
||||
*/
|
||||
static char *g_net_dev = "";
|
||||
module_param(g_net_dev, charp, S_IRUGO);
|
||||
MODULE_PARM_DESC(g_net_dev, "The device(s) to bind to; "
|
||||
"'*' means all, '' (empty string; default) means none.");
|
||||
|
||||
/*
|
||||
* Context: process
|
||||
*/
|
||||
static int __init ozwpan_init(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = oz_cdev_register();
|
||||
if (err)
|
||||
return err;
|
||||
err = oz_protocol_init(g_net_dev);
|
||||
if (err)
|
||||
goto err_protocol;
|
||||
oz_app_enable(OZ_APPID_USB, 1);
|
||||
oz_apps_init();
|
||||
return 0;
|
||||
|
||||
err_protocol:
|
||||
oz_cdev_deregister();
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* Context: process
|
||||
*/
|
||||
static void __exit ozwpan_exit(void)
|
||||
{
|
||||
oz_protocol_term();
|
||||
oz_apps_term();
|
||||
oz_cdev_deregister();
|
||||
}
|
||||
|
||||
module_init(ozwpan_init);
|
||||
module_exit(ozwpan_exit);
|
||||
|
||||
MODULE_AUTHOR("Chris Kelly");
|
||||
MODULE_DESCRIPTION("Ozmo Devices USB over WiFi hcd driver");
|
||||
MODULE_VERSION("1.0.13");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
@ -1,886 +0,0 @@
|
|||
/* -----------------------------------------------------------------------------
|
||||
* Copyright (c) 2011 Ozmo Inc
|
||||
* Released under the GNU General Public License Version 2 (GPLv2).
|
||||
* -----------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/errno.h>
|
||||
#include "ozdbg.h"
|
||||
#include "ozprotocol.h"
|
||||
#include "ozeltbuf.h"
|
||||
#include "ozpd.h"
|
||||
#include "ozproto.h"
|
||||
#include "ozcdev.h"
|
||||
#include "ozusbsvc.h"
|
||||
#include <asm/unaligned.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <net/psnap.h>
|
||||
|
||||
static struct oz_tx_frame *oz_tx_frame_alloc(struct oz_pd *pd);
|
||||
static void oz_tx_frame_free(struct oz_pd *pd, struct oz_tx_frame *f);
|
||||
static void oz_tx_isoc_free(struct oz_pd *pd, struct oz_tx_frame *f);
|
||||
static struct sk_buff *oz_build_frame(struct oz_pd *pd, struct oz_tx_frame *f);
|
||||
static int oz_send_isoc_frame(struct oz_pd *pd);
|
||||
static void oz_retire_frame(struct oz_pd *pd, struct oz_tx_frame *f);
|
||||
static void oz_isoc_stream_free(struct oz_isoc_stream *st);
|
||||
static int oz_send_next_queued_frame(struct oz_pd *pd, int more_data);
|
||||
static void oz_isoc_destructor(struct sk_buff *skb);
|
||||
|
||||
/*
|
||||
* Counts the uncompleted isoc frames submitted to netcard.
|
||||
*/
|
||||
static atomic_t g_submitted_isoc = ATOMIC_INIT(0);
|
||||
|
||||
/* Application handler functions.
|
||||
*/
|
||||
static const struct oz_app_if g_app_if[OZ_NB_APPS] = {
|
||||
[OZ_APPID_USB] = {
|
||||
.init = oz_usb_init,
|
||||
.term = oz_usb_term,
|
||||
.start = oz_usb_start,
|
||||
.stop = oz_usb_stop,
|
||||
.rx = oz_usb_rx,
|
||||
.heartbeat = oz_usb_heartbeat,
|
||||
.farewell = oz_usb_farewell,
|
||||
},
|
||||
[OZ_APPID_SERIAL] = {
|
||||
.init = oz_cdev_init,
|
||||
.term = oz_cdev_term,
|
||||
.start = oz_cdev_start,
|
||||
.stop = oz_cdev_stop,
|
||||
.rx = oz_cdev_rx,
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Context: softirq or process
|
||||
*/
|
||||
void oz_pd_set_state(struct oz_pd *pd, unsigned state)
|
||||
{
|
||||
pd->state = state;
|
||||
switch (state) {
|
||||
case OZ_PD_S_IDLE:
|
||||
oz_pd_dbg(pd, ON, "PD State: OZ_PD_S_IDLE\n");
|
||||
break;
|
||||
case OZ_PD_S_CONNECTED:
|
||||
oz_pd_dbg(pd, ON, "PD State: OZ_PD_S_CONNECTED\n");
|
||||
break;
|
||||
case OZ_PD_S_STOPPED:
|
||||
oz_pd_dbg(pd, ON, "PD State: OZ_PD_S_STOPPED\n");
|
||||
break;
|
||||
case OZ_PD_S_SLEEP:
|
||||
oz_pd_dbg(pd, ON, "PD State: OZ_PD_S_SLEEP\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Context: softirq or process
|
||||
*/
|
||||
void oz_pd_get(struct oz_pd *pd)
|
||||
{
|
||||
atomic_inc(&pd->ref_count);
|
||||
}
|
||||
|
||||
/*
|
||||
* Context: softirq or process
|
||||
*/
|
||||
void oz_pd_put(struct oz_pd *pd)
|
||||
{
|
||||
if (atomic_dec_and_test(&pd->ref_count))
|
||||
oz_pd_destroy(pd);
|
||||
}
|
||||
|
||||
/*
|
||||
* Context: softirq-serialized
|
||||
*/
|
||||
struct oz_pd *oz_pd_alloc(const u8 *mac_addr)
|
||||
{
|
||||
struct oz_pd *pd;
|
||||
int i;
|
||||
|
||||
pd = kzalloc(sizeof(struct oz_pd), GFP_ATOMIC);
|
||||
if (!pd)
|
||||
return NULL;
|
||||
|
||||
atomic_set(&pd->ref_count, 2);
|
||||
for (i = 0; i < OZ_NB_APPS; i++)
|
||||
spin_lock_init(&pd->app_lock[i]);
|
||||
pd->last_rx_pkt_num = 0xffffffff;
|
||||
oz_pd_set_state(pd, OZ_PD_S_IDLE);
|
||||
pd->max_tx_size = OZ_MAX_TX_SIZE;
|
||||
ether_addr_copy(pd->mac_addr, mac_addr);
|
||||
oz_elt_buf_init(&pd->elt_buff);
|
||||
spin_lock_init(&pd->tx_frame_lock);
|
||||
INIT_LIST_HEAD(&pd->tx_queue);
|
||||
INIT_LIST_HEAD(&pd->farewell_list);
|
||||
pd->last_sent_frame = &pd->tx_queue;
|
||||
spin_lock_init(&pd->stream_lock);
|
||||
INIT_LIST_HEAD(&pd->stream_list);
|
||||
tasklet_init(&pd->heartbeat_tasklet, oz_pd_heartbeat_handler,
|
||||
(unsigned long)pd);
|
||||
tasklet_init(&pd->timeout_tasklet, oz_pd_timeout_handler,
|
||||
(unsigned long)pd);
|
||||
hrtimer_init(&pd->heartbeat, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
|
||||
hrtimer_init(&pd->timeout, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
|
||||
pd->heartbeat.function = oz_pd_heartbeat_event;
|
||||
pd->timeout.function = oz_pd_timeout_event;
|
||||
|
||||
return pd;
|
||||
}
|
||||
|
||||
/*
|
||||
* Context: softirq or process
|
||||
*/
|
||||
static void oz_pd_free(struct work_struct *work)
|
||||
{
|
||||
struct list_head *e, *n;
|
||||
struct oz_pd *pd;
|
||||
|
||||
oz_pd_dbg(pd, ON, "Destroying PD\n");
|
||||
pd = container_of(work, struct oz_pd, workitem);
|
||||
/*Disable timer tasklets*/
|
||||
tasklet_kill(&pd->heartbeat_tasklet);
|
||||
tasklet_kill(&pd->timeout_tasklet);
|
||||
|
||||
/* Free streams, queued tx frames and farewells. */
|
||||
|
||||
list_for_each_safe(e, n, &pd->stream_list)
|
||||
oz_isoc_stream_free(list_entry(e, struct oz_isoc_stream, link));
|
||||
|
||||
list_for_each_safe(e, n, &pd->tx_queue) {
|
||||
struct oz_tx_frame *f = list_entry(e, struct oz_tx_frame, link);
|
||||
|
||||
if (f->skb != NULL)
|
||||
kfree_skb(f->skb);
|
||||
oz_retire_frame(pd, f);
|
||||
}
|
||||
|
||||
oz_elt_buf_term(&pd->elt_buff);
|
||||
|
||||
list_for_each_safe(e, n, &pd->farewell_list)
|
||||
kfree(list_entry(e, struct oz_farewell, link));
|
||||
|
||||
if (pd->net_dev)
|
||||
dev_put(pd->net_dev);
|
||||
kfree(pd);
|
||||
}
|
||||
|
||||
/*
|
||||
* Context: softirq or Process
|
||||
*/
|
||||
void oz_pd_destroy(struct oz_pd *pd)
|
||||
{
|
||||
if (hrtimer_active(&pd->timeout))
|
||||
hrtimer_cancel(&pd->timeout);
|
||||
if (hrtimer_active(&pd->heartbeat))
|
||||
hrtimer_cancel(&pd->heartbeat);
|
||||
|
||||
INIT_WORK(&pd->workitem, oz_pd_free);
|
||||
if (!schedule_work(&pd->workitem))
|
||||
oz_pd_dbg(pd, ON, "failed to schedule workitem\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* Context: softirq-serialized
|
||||
*/
|
||||
int oz_services_start(struct oz_pd *pd, u16 apps, int resume)
|
||||
{
|
||||
int i, rc = 0;
|
||||
|
||||
oz_pd_dbg(pd, ON, "%s: (0x%x) resume(%d)\n", __func__, apps, resume);
|
||||
for (i = 0; i < OZ_NB_APPS; i++) {
|
||||
if (g_app_if[i].start && (apps & (1 << i))) {
|
||||
if (g_app_if[i].start(pd, resume)) {
|
||||
rc = -1;
|
||||
oz_pd_dbg(pd, ON,
|
||||
"Unable to start service %d\n", i);
|
||||
break;
|
||||
}
|
||||
spin_lock_bh(&g_polling_lock);
|
||||
pd->total_apps |= (1 << i);
|
||||
if (resume)
|
||||
pd->paused_apps &= ~(1 << i);
|
||||
spin_unlock_bh(&g_polling_lock);
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
* Context: softirq or process
|
||||
*/
|
||||
void oz_services_stop(struct oz_pd *pd, u16 apps, int pause)
|
||||
{
|
||||
int i;
|
||||
|
||||
oz_pd_dbg(pd, ON, "%s: (0x%x) pause(%d)\n", __func__, apps, pause);
|
||||
for (i = 0; i < OZ_NB_APPS; i++) {
|
||||
if (g_app_if[i].stop && (apps & (1 << i))) {
|
||||
spin_lock_bh(&g_polling_lock);
|
||||
if (pause) {
|
||||
pd->paused_apps |= (1 << i);
|
||||
} else {
|
||||
pd->total_apps &= ~(1 << i);
|
||||
pd->paused_apps &= ~(1 << i);
|
||||
}
|
||||
spin_unlock_bh(&g_polling_lock);
|
||||
g_app_if[i].stop(pd, pause);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Context: softirq
|
||||
*/
|
||||
void oz_pd_heartbeat(struct oz_pd *pd, u16 apps)
|
||||
{
|
||||
int i, more = 0;
|
||||
|
||||
for (i = 0; i < OZ_NB_APPS; i++) {
|
||||
if (g_app_if[i].heartbeat && (apps & (1 << i))) {
|
||||
if (g_app_if[i].heartbeat(pd))
|
||||
more = 1;
|
||||
}
|
||||
}
|
||||
if ((!more) && (hrtimer_active(&pd->heartbeat)))
|
||||
hrtimer_cancel(&pd->heartbeat);
|
||||
if (pd->mode & OZ_F_ISOC_ANYTIME) {
|
||||
int count = 8;
|
||||
|
||||
while (count-- && (oz_send_isoc_frame(pd) >= 0))
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Context: softirq or process
|
||||
*/
|
||||
void oz_pd_stop(struct oz_pd *pd)
|
||||
{
|
||||
u16 stop_apps;
|
||||
|
||||
oz_dbg(ON, "oz_pd_stop() State = 0x%x\n", pd->state);
|
||||
oz_pd_indicate_farewells(pd);
|
||||
spin_lock_bh(&g_polling_lock);
|
||||
stop_apps = pd->total_apps;
|
||||
pd->total_apps = 0;
|
||||
pd->paused_apps = 0;
|
||||
spin_unlock_bh(&g_polling_lock);
|
||||
oz_services_stop(pd, stop_apps, 0);
|
||||
spin_lock_bh(&g_polling_lock);
|
||||
oz_pd_set_state(pd, OZ_PD_S_STOPPED);
|
||||
/* Remove from PD list.*/
|
||||
list_del(&pd->link);
|
||||
spin_unlock_bh(&g_polling_lock);
|
||||
oz_dbg(ON, "pd ref count = %d\n", atomic_read(&pd->ref_count));
|
||||
oz_pd_put(pd);
|
||||
}
|
||||
|
||||
/*
|
||||
* Context: softirq
|
||||
*/
|
||||
int oz_pd_sleep(struct oz_pd *pd)
|
||||
{
|
||||
int do_stop = 0;
|
||||
u16 stop_apps;
|
||||
|
||||
spin_lock_bh(&g_polling_lock);
|
||||
if (pd->state & (OZ_PD_S_SLEEP | OZ_PD_S_STOPPED)) {
|
||||
spin_unlock_bh(&g_polling_lock);
|
||||
return 0;
|
||||
}
|
||||
if (pd->keep_alive && pd->session_id)
|
||||
oz_pd_set_state(pd, OZ_PD_S_SLEEP);
|
||||
else
|
||||
do_stop = 1;
|
||||
|
||||
stop_apps = pd->total_apps;
|
||||
spin_unlock_bh(&g_polling_lock);
|
||||
if (do_stop) {
|
||||
oz_pd_stop(pd);
|
||||
} else {
|
||||
oz_services_stop(pd, stop_apps, 1);
|
||||
oz_timer_add(pd, OZ_TIMER_STOP, pd->keep_alive);
|
||||
}
|
||||
return do_stop;
|
||||
}
|
||||
|
||||
/*
|
||||
* Context: softirq
|
||||
*/
|
||||
static struct oz_tx_frame *oz_tx_frame_alloc(struct oz_pd *pd)
|
||||
{
|
||||
struct oz_tx_frame *f;
|
||||
|
||||
f = kmem_cache_alloc(oz_tx_frame_cache, GFP_ATOMIC);
|
||||
if (f) {
|
||||
f->total_size = sizeof(struct oz_hdr);
|
||||
INIT_LIST_HEAD(&f->link);
|
||||
INIT_LIST_HEAD(&f->elt_list);
|
||||
}
|
||||
return f;
|
||||
}
|
||||
|
||||
/*
|
||||
* Context: softirq or process
|
||||
*/
|
||||
static void oz_tx_isoc_free(struct oz_pd *pd, struct oz_tx_frame *f)
|
||||
{
|
||||
pd->nb_queued_isoc_frames--;
|
||||
list_del_init(&f->link);
|
||||
|
||||
kmem_cache_free(oz_tx_frame_cache, f);
|
||||
|
||||
oz_dbg(TX_FRAMES, "Releasing ISOC Frame isoc_nb= %d\n",
|
||||
pd->nb_queued_isoc_frames);
|
||||
}
|
||||
|
||||
/*
|
||||
* Context: softirq or process
|
||||
*/
|
||||
static void oz_tx_frame_free(struct oz_pd *pd, struct oz_tx_frame *f)
|
||||
{
|
||||
kmem_cache_free(oz_tx_frame_cache, f);
|
||||
}
|
||||
|
||||
/*
|
||||
* Context: softirq-serialized
|
||||
*/
|
||||
static void oz_set_more_bit(struct sk_buff *skb)
|
||||
{
|
||||
struct oz_hdr *oz_hdr = (struct oz_hdr *)skb_network_header(skb);
|
||||
|
||||
oz_hdr->control |= OZ_F_MORE_DATA;
|
||||
}
|
||||
|
||||
/*
|
||||
* Context: softirq-serialized
|
||||
*/
|
||||
static void oz_set_last_pkt_nb(struct oz_pd *pd, struct sk_buff *skb)
|
||||
{
|
||||
struct oz_hdr *oz_hdr = (struct oz_hdr *)skb_network_header(skb);
|
||||
|
||||
oz_hdr->last_pkt_num = pd->trigger_pkt_num & OZ_LAST_PN_MASK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Context: softirq
|
||||
*/
|
||||
int oz_prepare_frame(struct oz_pd *pd, int empty)
|
||||
{
|
||||
struct oz_tx_frame *f;
|
||||
|
||||
if ((pd->mode & OZ_MODE_MASK) != OZ_MODE_TRIGGERED)
|
||||
return -1;
|
||||
if (pd->nb_queued_frames >= OZ_MAX_QUEUED_FRAMES)
|
||||
return -1;
|
||||
if (!empty && !oz_are_elts_available(&pd->elt_buff))
|
||||
return -1;
|
||||
f = oz_tx_frame_alloc(pd);
|
||||
if (f == NULL)
|
||||
return -1;
|
||||
f->skb = NULL;
|
||||
f->hdr.control =
|
||||
(OZ_PROTOCOL_VERSION<<OZ_VERSION_SHIFT) | OZ_F_ACK_REQUESTED;
|
||||
++pd->last_tx_pkt_num;
|
||||
put_unaligned(cpu_to_le32(pd->last_tx_pkt_num), &f->hdr.pkt_num);
|
||||
if (empty == 0) {
|
||||
oz_select_elts_for_tx(&pd->elt_buff, 0, &f->total_size,
|
||||
pd->max_tx_size, &f->elt_list);
|
||||
}
|
||||
spin_lock(&pd->tx_frame_lock);
|
||||
list_add_tail(&f->link, &pd->tx_queue);
|
||||
pd->nb_queued_frames++;
|
||||
spin_unlock(&pd->tx_frame_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Context: softirq-serialized
|
||||
*/
|
||||
static struct sk_buff *oz_build_frame(struct oz_pd *pd, struct oz_tx_frame *f)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
struct net_device *dev = pd->net_dev;
|
||||
struct oz_hdr *oz_hdr;
|
||||
struct oz_elt *elt;
|
||||
struct oz_elt_info *ei;
|
||||
|
||||
/* Allocate skb with enough space for the lower layers as well
|
||||
* as the space we need.
|
||||
*/
|
||||
skb = alloc_skb(f->total_size + OZ_ALLOCATED_SPACE(dev), GFP_ATOMIC);
|
||||
if (skb == NULL)
|
||||
return NULL;
|
||||
/* Reserve the head room for lower layers.
|
||||
*/
|
||||
skb_reserve(skb, LL_RESERVED_SPACE(dev));
|
||||
skb_reset_network_header(skb);
|
||||
skb->dev = dev;
|
||||
skb->protocol = htons(OZ_ETHERTYPE);
|
||||
if (dev_hard_header(skb, dev, OZ_ETHERTYPE, pd->mac_addr,
|
||||
dev->dev_addr, skb->len) < 0)
|
||||
goto fail;
|
||||
/* Push the tail to the end of the area we are going to copy to.
|
||||
*/
|
||||
oz_hdr = (struct oz_hdr *)skb_put(skb, f->total_size);
|
||||
f->hdr.last_pkt_num = pd->trigger_pkt_num & OZ_LAST_PN_MASK;
|
||||
memcpy(oz_hdr, &f->hdr, sizeof(struct oz_hdr));
|
||||
/* Copy the elements into the frame body.
|
||||
*/
|
||||
elt = (struct oz_elt *)(oz_hdr+1);
|
||||
list_for_each_entry(ei, &f->elt_list, link) {
|
||||
memcpy(elt, ei->data, ei->length);
|
||||
elt = oz_next_elt(elt);
|
||||
}
|
||||
return skb;
|
||||
fail:
|
||||
kfree_skb(skb);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Context: softirq or process
|
||||
*/
|
||||
static void oz_retire_frame(struct oz_pd *pd, struct oz_tx_frame *f)
|
||||
{
|
||||
struct oz_elt_info *ei, *n;
|
||||
|
||||
list_for_each_entry_safe(ei, n, &f->elt_list, link) {
|
||||
list_del_init(&ei->link);
|
||||
if (ei->callback)
|
||||
ei->callback(pd, ei->context);
|
||||
spin_lock_bh(&pd->elt_buff.lock);
|
||||
oz_elt_info_free(&pd->elt_buff, ei);
|
||||
spin_unlock_bh(&pd->elt_buff.lock);
|
||||
}
|
||||
oz_tx_frame_free(pd, f);
|
||||
}
|
||||
|
||||
/*
|
||||
* Context: softirq-serialized
|
||||
*/
|
||||
static int oz_send_next_queued_frame(struct oz_pd *pd, int more_data)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
struct oz_tx_frame *f;
|
||||
struct list_head *e;
|
||||
|
||||
spin_lock(&pd->tx_frame_lock);
|
||||
e = pd->last_sent_frame->next;
|
||||
if (e == &pd->tx_queue) {
|
||||
spin_unlock(&pd->tx_frame_lock);
|
||||
return -1;
|
||||
}
|
||||
f = list_entry(e, struct oz_tx_frame, link);
|
||||
|
||||
if (f->skb != NULL) {
|
||||
skb = f->skb;
|
||||
oz_tx_isoc_free(pd, f);
|
||||
spin_unlock(&pd->tx_frame_lock);
|
||||
if (more_data)
|
||||
oz_set_more_bit(skb);
|
||||
oz_set_last_pkt_nb(pd, skb);
|
||||
if ((int)atomic_read(&g_submitted_isoc) <
|
||||
OZ_MAX_SUBMITTED_ISOC) {
|
||||
if (dev_queue_xmit(skb) < 0) {
|
||||
oz_dbg(TX_FRAMES, "Dropping ISOC Frame\n");
|
||||
return -1;
|
||||
}
|
||||
atomic_inc(&g_submitted_isoc);
|
||||
oz_dbg(TX_FRAMES, "Sending ISOC Frame, nb_isoc= %d\n",
|
||||
pd->nb_queued_isoc_frames);
|
||||
return 0;
|
||||
}
|
||||
kfree_skb(skb);
|
||||
oz_dbg(TX_FRAMES, "Dropping ISOC Frame>\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
pd->last_sent_frame = e;
|
||||
skb = oz_build_frame(pd, f);
|
||||
spin_unlock(&pd->tx_frame_lock);
|
||||
if (!skb)
|
||||
return -1;
|
||||
if (more_data)
|
||||
oz_set_more_bit(skb);
|
||||
oz_dbg(TX_FRAMES, "TX frame PN=0x%x\n", f->hdr.pkt_num);
|
||||
if (dev_queue_xmit(skb) < 0)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Context: softirq-serialized
|
||||
*/
|
||||
void oz_send_queued_frames(struct oz_pd *pd, int backlog)
|
||||
{
|
||||
while (oz_prepare_frame(pd, 0) >= 0)
|
||||
backlog++;
|
||||
|
||||
switch (pd->mode & (OZ_F_ISOC_NO_ELTS | OZ_F_ISOC_ANYTIME)) {
|
||||
|
||||
case OZ_F_ISOC_NO_ELTS: {
|
||||
backlog += pd->nb_queued_isoc_frames;
|
||||
if (backlog <= 0)
|
||||
goto out;
|
||||
if (backlog > OZ_MAX_SUBMITTED_ISOC)
|
||||
backlog = OZ_MAX_SUBMITTED_ISOC;
|
||||
break;
|
||||
}
|
||||
case OZ_NO_ELTS_ANYTIME: {
|
||||
if ((backlog <= 0) && (pd->isoc_sent == 0))
|
||||
goto out;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
if (backlog <= 0)
|
||||
goto out;
|
||||
break;
|
||||
}
|
||||
}
|
||||
while (backlog--) {
|
||||
if (oz_send_next_queued_frame(pd, backlog) < 0)
|
||||
break;
|
||||
}
|
||||
return;
|
||||
|
||||
out: oz_prepare_frame(pd, 1);
|
||||
oz_send_next_queued_frame(pd, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Context: softirq
|
||||
*/
|
||||
static int oz_send_isoc_frame(struct oz_pd *pd)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
struct net_device *dev = pd->net_dev;
|
||||
struct oz_hdr *oz_hdr;
|
||||
struct oz_elt *elt;
|
||||
struct oz_elt_info *ei;
|
||||
LIST_HEAD(list);
|
||||
int total_size = sizeof(struct oz_hdr);
|
||||
|
||||
oz_select_elts_for_tx(&pd->elt_buff, 1, &total_size,
|
||||
pd->max_tx_size, &list);
|
||||
if (list_empty(&list))
|
||||
return 0;
|
||||
skb = alloc_skb(total_size + OZ_ALLOCATED_SPACE(dev), GFP_ATOMIC);
|
||||
if (skb == NULL) {
|
||||
oz_dbg(ON, "Cannot alloc skb\n");
|
||||
oz_elt_info_free_chain(&pd->elt_buff, &list);
|
||||
return -1;
|
||||
}
|
||||
skb_reserve(skb, LL_RESERVED_SPACE(dev));
|
||||
skb_reset_network_header(skb);
|
||||
skb->dev = dev;
|
||||
skb->protocol = htons(OZ_ETHERTYPE);
|
||||
if (dev_hard_header(skb, dev, OZ_ETHERTYPE, pd->mac_addr,
|
||||
dev->dev_addr, skb->len) < 0) {
|
||||
kfree_skb(skb);
|
||||
return -1;
|
||||
}
|
||||
oz_hdr = (struct oz_hdr *)skb_put(skb, total_size);
|
||||
oz_hdr->control = (OZ_PROTOCOL_VERSION<<OZ_VERSION_SHIFT) | OZ_F_ISOC;
|
||||
oz_hdr->last_pkt_num = pd->trigger_pkt_num & OZ_LAST_PN_MASK;
|
||||
elt = (struct oz_elt *)(oz_hdr+1);
|
||||
|
||||
list_for_each_entry(ei, &list, link) {
|
||||
memcpy(elt, ei->data, ei->length);
|
||||
elt = oz_next_elt(elt);
|
||||
}
|
||||
dev_queue_xmit(skb);
|
||||
oz_elt_info_free_chain(&pd->elt_buff, &list);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Context: softirq-serialized
|
||||
*/
|
||||
void oz_retire_tx_frames(struct oz_pd *pd, u8 lpn)
|
||||
{
|
||||
struct oz_tx_frame *f, *tmp = NULL;
|
||||
u8 diff;
|
||||
u32 pkt_num;
|
||||
|
||||
LIST_HEAD(list);
|
||||
|
||||
spin_lock(&pd->tx_frame_lock);
|
||||
list_for_each_entry(f, &pd->tx_queue, link) {
|
||||
pkt_num = le32_to_cpu(get_unaligned(&f->hdr.pkt_num));
|
||||
diff = (lpn - (pkt_num & OZ_LAST_PN_MASK)) & OZ_LAST_PN_MASK;
|
||||
if ((diff > OZ_LAST_PN_HALF_CYCLE) || (pkt_num == 0))
|
||||
break;
|
||||
oz_dbg(TX_FRAMES, "Releasing pkt_num= %u, nb= %d\n",
|
||||
pkt_num, pd->nb_queued_frames);
|
||||
tmp = f;
|
||||
pd->nb_queued_frames--;
|
||||
}
|
||||
if (tmp)
|
||||
list_cut_position(&list, &pd->tx_queue, &tmp->link);
|
||||
pd->last_sent_frame = &pd->tx_queue;
|
||||
spin_unlock(&pd->tx_frame_lock);
|
||||
|
||||
list_for_each_entry_safe(f, tmp, &list, link)
|
||||
oz_retire_frame(pd, f);
|
||||
}
|
||||
|
||||
/*
|
||||
* Precondition: stream_lock must be held.
|
||||
* Context: softirq
|
||||
*/
|
||||
static struct oz_isoc_stream *pd_stream_find(struct oz_pd *pd, u8 ep_num)
|
||||
{
|
||||
struct oz_isoc_stream *st;
|
||||
|
||||
list_for_each_entry(st, &pd->stream_list, link) {
|
||||
if (st->ep_num == ep_num)
|
||||
return st;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Context: softirq
|
||||
*/
|
||||
int oz_isoc_stream_create(struct oz_pd *pd, u8 ep_num)
|
||||
{
|
||||
struct oz_isoc_stream *st;
|
||||
|
||||
st = kzalloc(sizeof(struct oz_isoc_stream), GFP_ATOMIC);
|
||||
if (!st)
|
||||
return -ENOMEM;
|
||||
st->ep_num = ep_num;
|
||||
spin_lock_bh(&pd->stream_lock);
|
||||
if (!pd_stream_find(pd, ep_num)) {
|
||||
list_add(&st->link, &pd->stream_list);
|
||||
st = NULL;
|
||||
}
|
||||
spin_unlock_bh(&pd->stream_lock);
|
||||
kfree(st);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Context: softirq or process
|
||||
*/
|
||||
static void oz_isoc_stream_free(struct oz_isoc_stream *st)
|
||||
{
|
||||
kfree_skb(st->skb);
|
||||
kfree(st);
|
||||
}
|
||||
|
||||
/*
|
||||
* Context: softirq
|
||||
*/
|
||||
int oz_isoc_stream_delete(struct oz_pd *pd, u8 ep_num)
|
||||
{
|
||||
struct oz_isoc_stream *st;
|
||||
|
||||
spin_lock_bh(&pd->stream_lock);
|
||||
st = pd_stream_find(pd, ep_num);
|
||||
if (st)
|
||||
list_del(&st->link);
|
||||
spin_unlock_bh(&pd->stream_lock);
|
||||
if (st)
|
||||
oz_isoc_stream_free(st);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Context: any
|
||||
*/
|
||||
static void oz_isoc_destructor(struct sk_buff *skb)
|
||||
{
|
||||
atomic_dec(&g_submitted_isoc);
|
||||
}
|
||||
|
||||
/*
|
||||
* Context: softirq
|
||||
*/
|
||||
int oz_send_isoc_unit(struct oz_pd *pd, u8 ep_num, const u8 *data, int len)
|
||||
{
|
||||
struct net_device *dev = pd->net_dev;
|
||||
struct oz_isoc_stream *st;
|
||||
u8 nb_units = 0;
|
||||
struct sk_buff *skb = NULL;
|
||||
struct oz_hdr *oz_hdr = NULL;
|
||||
int size = 0;
|
||||
|
||||
spin_lock_bh(&pd->stream_lock);
|
||||
st = pd_stream_find(pd, ep_num);
|
||||
if (st) {
|
||||
skb = st->skb;
|
||||
st->skb = NULL;
|
||||
nb_units = st->nb_units;
|
||||
st->nb_units = 0;
|
||||
oz_hdr = st->oz_hdr;
|
||||
size = st->size;
|
||||
}
|
||||
spin_unlock_bh(&pd->stream_lock);
|
||||
if (!st)
|
||||
return 0;
|
||||
if (!skb) {
|
||||
/* Allocate enough space for max size frame. */
|
||||
skb = alloc_skb(pd->max_tx_size + OZ_ALLOCATED_SPACE(dev),
|
||||
GFP_ATOMIC);
|
||||
if (skb == NULL)
|
||||
return 0;
|
||||
/* Reserve the head room for lower layers. */
|
||||
skb_reserve(skb, LL_RESERVED_SPACE(dev));
|
||||
skb_reset_network_header(skb);
|
||||
skb->dev = dev;
|
||||
skb->protocol = htons(OZ_ETHERTYPE);
|
||||
/* For audio packet set priority to AC_VO */
|
||||
skb->priority = 0x7;
|
||||
size = sizeof(struct oz_hdr) + sizeof(struct oz_isoc_large);
|
||||
oz_hdr = (struct oz_hdr *)skb_put(skb, size);
|
||||
}
|
||||
memcpy(skb_put(skb, len), data, len);
|
||||
size += len;
|
||||
if (++nb_units < pd->ms_per_isoc) {
|
||||
spin_lock_bh(&pd->stream_lock);
|
||||
st->skb = skb;
|
||||
st->nb_units = nb_units;
|
||||
st->oz_hdr = oz_hdr;
|
||||
st->size = size;
|
||||
spin_unlock_bh(&pd->stream_lock);
|
||||
} else {
|
||||
struct oz_hdr oz;
|
||||
struct oz_isoc_large iso;
|
||||
|
||||
spin_lock_bh(&pd->stream_lock);
|
||||
iso.frame_number = st->frame_num;
|
||||
st->frame_num += nb_units;
|
||||
spin_unlock_bh(&pd->stream_lock);
|
||||
oz.control =
|
||||
(OZ_PROTOCOL_VERSION<<OZ_VERSION_SHIFT) | OZ_F_ISOC;
|
||||
oz.last_pkt_num = pd->trigger_pkt_num & OZ_LAST_PN_MASK;
|
||||
oz.pkt_num = 0;
|
||||
iso.endpoint = ep_num;
|
||||
iso.format = OZ_DATA_F_ISOC_LARGE;
|
||||
iso.ms_data = nb_units;
|
||||
memcpy(oz_hdr, &oz, sizeof(oz));
|
||||
memcpy(oz_hdr+1, &iso, sizeof(iso));
|
||||
if (dev_hard_header(skb, dev, OZ_ETHERTYPE, pd->mac_addr,
|
||||
dev->dev_addr, skb->len) < 0)
|
||||
goto out;
|
||||
|
||||
skb->destructor = oz_isoc_destructor;
|
||||
/*Queue for Xmit if mode is not ANYTIME*/
|
||||
if (!(pd->mode & OZ_F_ISOC_ANYTIME)) {
|
||||
struct oz_tx_frame *isoc_unit = NULL;
|
||||
int nb = pd->nb_queued_isoc_frames;
|
||||
|
||||
if (nb >= pd->isoc_latency) {
|
||||
struct oz_tx_frame *f;
|
||||
|
||||
oz_dbg(TX_FRAMES, "Dropping ISOC Unit nb= %d\n",
|
||||
nb);
|
||||
spin_lock(&pd->tx_frame_lock);
|
||||
list_for_each_entry(f, &pd->tx_queue, link) {
|
||||
if (f->skb != NULL) {
|
||||
oz_tx_isoc_free(pd, f);
|
||||
break;
|
||||
}
|
||||
}
|
||||
spin_unlock(&pd->tx_frame_lock);
|
||||
}
|
||||
isoc_unit = oz_tx_frame_alloc(pd);
|
||||
if (isoc_unit == NULL)
|
||||
goto out;
|
||||
isoc_unit->hdr = oz;
|
||||
isoc_unit->skb = skb;
|
||||
spin_lock_bh(&pd->tx_frame_lock);
|
||||
list_add_tail(&isoc_unit->link, &pd->tx_queue);
|
||||
pd->nb_queued_isoc_frames++;
|
||||
spin_unlock_bh(&pd->tx_frame_lock);
|
||||
oz_dbg(TX_FRAMES,
|
||||
"Added ISOC Frame to Tx Queue isoc_nb= %d, nb= %d\n",
|
||||
pd->nb_queued_isoc_frames, pd->nb_queued_frames);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*In ANYTIME mode Xmit unit immediately*/
|
||||
if (atomic_read(&g_submitted_isoc) < OZ_MAX_SUBMITTED_ISOC) {
|
||||
atomic_inc(&g_submitted_isoc);
|
||||
if (dev_queue_xmit(skb) < 0)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
out: kfree_skb(skb);
|
||||
return -1;
|
||||
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Context: process
|
||||
*/
|
||||
void oz_apps_init(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < OZ_NB_APPS; i++) {
|
||||
if (g_app_if[i].init)
|
||||
g_app_if[i].init();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Context: process
|
||||
*/
|
||||
void oz_apps_term(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* Terminate all the apps. */
|
||||
for (i = 0; i < OZ_NB_APPS; i++) {
|
||||
if (g_app_if[i].term)
|
||||
g_app_if[i].term();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Context: softirq-serialized
|
||||
*/
|
||||
void oz_handle_app_elt(struct oz_pd *pd, u8 app_id, struct oz_elt *elt)
|
||||
{
|
||||
if (app_id < OZ_NB_APPS && g_app_if[app_id].rx)
|
||||
g_app_if[app_id].rx(pd, elt);
|
||||
}
|
||||
|
||||
/*
|
||||
* Context: softirq or process
|
||||
*/
|
||||
void oz_pd_indicate_farewells(struct oz_pd *pd)
|
||||
{
|
||||
struct oz_farewell *f;
|
||||
const struct oz_app_if *ai = &g_app_if[OZ_APPID_USB];
|
||||
|
||||
while (1) {
|
||||
spin_lock_bh(&g_polling_lock);
|
||||
if (list_empty(&pd->farewell_list)) {
|
||||
spin_unlock_bh(&g_polling_lock);
|
||||
break;
|
||||
}
|
||||
f = list_first_entry(&pd->farewell_list,
|
||||
struct oz_farewell, link);
|
||||
list_del(&f->link);
|
||||
spin_unlock_bh(&g_polling_lock);
|
||||
if (ai->farewell)
|
||||
ai->farewell(pd, f->ep_num, f->report, f->len);
|
||||
kfree(f);
|
||||
}
|
||||
}
|
|
@ -1,134 +0,0 @@
|
|||
/* -----------------------------------------------------------------------------
|
||||
* Copyright (c) 2011 Ozmo Inc
|
||||
* Released under the GNU General Public License Version 2 (GPLv2).
|
||||
* -----------------------------------------------------------------------------
|
||||
*/
|
||||
#ifndef _OZPD_H_
|
||||
#define _OZPD_H_
|
||||
|
||||
#include <linux/interrupt.h>
|
||||
#include "ozeltbuf.h"
|
||||
|
||||
/* PD state
|
||||
*/
|
||||
#define OZ_PD_S_IDLE 0x1
|
||||
#define OZ_PD_S_CONNECTED 0x2
|
||||
#define OZ_PD_S_SLEEP 0x4
|
||||
#define OZ_PD_S_STOPPED 0x8
|
||||
|
||||
/* Timer event types.
|
||||
*/
|
||||
#define OZ_TIMER_TOUT 1
|
||||
#define OZ_TIMER_HEARTBEAT 2
|
||||
#define OZ_TIMER_STOP 3
|
||||
|
||||
/*
|
||||
*External spinlock variable
|
||||
*/
|
||||
extern spinlock_t g_polling_lock;
|
||||
|
||||
/* Data structure that hold information on a frame for transmisson. This is
|
||||
* built when the frame is first transmitted and is used to rebuild the frame
|
||||
* if a re-transmission is required.
|
||||
*/
|
||||
struct oz_tx_frame {
|
||||
struct list_head link;
|
||||
struct list_head elt_list;
|
||||
struct oz_hdr hdr;
|
||||
struct sk_buff *skb;
|
||||
int total_size;
|
||||
};
|
||||
|
||||
struct oz_isoc_stream {
|
||||
struct list_head link;
|
||||
u8 ep_num;
|
||||
u8 frame_num;
|
||||
u8 nb_units;
|
||||
int size;
|
||||
struct sk_buff *skb;
|
||||
struct oz_hdr *oz_hdr;
|
||||
};
|
||||
|
||||
struct oz_farewell {
|
||||
struct list_head link;
|
||||
u8 ep_num;
|
||||
u8 index;
|
||||
u8 len;
|
||||
u8 report[0];
|
||||
};
|
||||
|
||||
/* Data structure that holds information on a specific peripheral device (PD).
|
||||
*/
|
||||
struct oz_pd {
|
||||
struct list_head link;
|
||||
atomic_t ref_count;
|
||||
u8 mac_addr[ETH_ALEN];
|
||||
unsigned state;
|
||||
unsigned state_flags;
|
||||
unsigned send_flags;
|
||||
u16 total_apps;
|
||||
u16 paused_apps;
|
||||
u8 session_id;
|
||||
u8 param_rsp_status;
|
||||
u8 pd_info;
|
||||
u8 isoc_sent;
|
||||
u32 last_rx_pkt_num;
|
||||
u32 last_tx_pkt_num;
|
||||
struct timespec last_rx_timestamp;
|
||||
u32 trigger_pkt_num;
|
||||
unsigned long pulse_time;
|
||||
unsigned long pulse_period;
|
||||
unsigned long presleep;
|
||||
unsigned long keep_alive;
|
||||
struct oz_elt_buf elt_buff;
|
||||
void *app_ctx[OZ_NB_APPS];
|
||||
spinlock_t app_lock[OZ_NB_APPS];
|
||||
int max_tx_size;
|
||||
u8 mode;
|
||||
u8 ms_per_isoc;
|
||||
unsigned isoc_latency;
|
||||
unsigned max_stream_buffering;
|
||||
int nb_queued_frames;
|
||||
int nb_queued_isoc_frames;
|
||||
spinlock_t tx_frame_lock;
|
||||
struct list_head *last_sent_frame;
|
||||
struct list_head tx_queue;
|
||||
struct list_head farewell_list;
|
||||
spinlock_t stream_lock;
|
||||
struct list_head stream_list;
|
||||
struct net_device *net_dev;
|
||||
struct hrtimer heartbeat;
|
||||
struct hrtimer timeout;
|
||||
u8 timeout_type;
|
||||
struct tasklet_struct heartbeat_tasklet;
|
||||
struct tasklet_struct timeout_tasklet;
|
||||
struct work_struct workitem;
|
||||
};
|
||||
|
||||
#define OZ_MAX_QUEUED_FRAMES 4
|
||||
|
||||
struct oz_pd *oz_pd_alloc(const u8 *mac_addr);
|
||||
void oz_pd_destroy(struct oz_pd *pd);
|
||||
void oz_pd_get(struct oz_pd *pd);
|
||||
void oz_pd_put(struct oz_pd *pd);
|
||||
void oz_pd_set_state(struct oz_pd *pd, unsigned state);
|
||||
void oz_pd_indicate_farewells(struct oz_pd *pd);
|
||||
int oz_pd_sleep(struct oz_pd *pd);
|
||||
void oz_pd_stop(struct oz_pd *pd);
|
||||
void oz_pd_heartbeat(struct oz_pd *pd, u16 apps);
|
||||
int oz_services_start(struct oz_pd *pd, u16 apps, int resume);
|
||||
void oz_services_stop(struct oz_pd *pd, u16 apps, int pause);
|
||||
int oz_prepare_frame(struct oz_pd *pd, int empty);
|
||||
void oz_send_queued_frames(struct oz_pd *pd, int backlog);
|
||||
void oz_retire_tx_frames(struct oz_pd *pd, u8 lpn);
|
||||
int oz_isoc_stream_create(struct oz_pd *pd, u8 ep_num);
|
||||
int oz_isoc_stream_delete(struct oz_pd *pd, u8 ep_num);
|
||||
int oz_send_isoc_unit(struct oz_pd *pd, u8 ep_num, const u8 *data, int len);
|
||||
void oz_handle_app_elt(struct oz_pd *pd, u8 app_id, struct oz_elt *elt);
|
||||
void oz_apps_init(void);
|
||||
void oz_apps_term(void);
|
||||
|
||||
extern struct kmem_cache *oz_elt_info_cache;
|
||||
extern struct kmem_cache *oz_tx_frame_cache;
|
||||
|
||||
#endif /* Sentry */
|
|
@ -1,813 +0,0 @@
|
|||
/* -----------------------------------------------------------------------------
|
||||
* Copyright (c) 2011 Ozmo Inc
|
||||
* Released under the GNU General Public License Version 2 (GPLv2).
|
||||
* -----------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/ieee80211.h>
|
||||
#include <linux/slab.h>
|
||||
#include "ozdbg.h"
|
||||
#include "ozprotocol.h"
|
||||
#include "ozeltbuf.h"
|
||||
#include "ozpd.h"
|
||||
#include "ozproto.h"
|
||||
#include "ozusbsvc.h"
|
||||
|
||||
#include "ozappif.h"
|
||||
#include <asm/unaligned.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <net/psnap.h>
|
||||
|
||||
#define OZ_CF_CONN_SUCCESS 1
|
||||
#define OZ_CF_CONN_FAILURE 2
|
||||
|
||||
#define OZ_DO_STOP 1
|
||||
#define OZ_DO_SLEEP 2
|
||||
|
||||
struct oz_binding {
|
||||
struct packet_type ptype;
|
||||
char name[OZ_MAX_BINDING_LEN];
|
||||
struct list_head link;
|
||||
};
|
||||
|
||||
/*
|
||||
* External variable
|
||||
*/
|
||||
|
||||
DEFINE_SPINLOCK(g_polling_lock);
|
||||
/*
|
||||
* Static external variables.
|
||||
*/
|
||||
static LIST_HEAD(g_pd_list);
|
||||
static LIST_HEAD(g_binding);
|
||||
static DEFINE_SPINLOCK(g_binding_lock);
|
||||
static struct sk_buff_head g_rx_queue;
|
||||
static u8 g_session_id;
|
||||
static u16 g_apps = 0x1;
|
||||
static int g_processing_rx;
|
||||
|
||||
struct kmem_cache *oz_elt_info_cache;
|
||||
struct kmem_cache *oz_tx_frame_cache;
|
||||
|
||||
/*
|
||||
* Context: softirq-serialized
|
||||
*/
|
||||
static u8 oz_get_new_session_id(u8 exclude)
|
||||
{
|
||||
if (++g_session_id == 0)
|
||||
g_session_id = 1;
|
||||
if (g_session_id == exclude) {
|
||||
if (++g_session_id == 0)
|
||||
g_session_id = 1;
|
||||
}
|
||||
return g_session_id;
|
||||
}
|
||||
|
||||
/*
|
||||
* Context: softirq-serialized
|
||||
*/
|
||||
static void oz_send_conn_rsp(struct oz_pd *pd, u8 status)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
struct net_device *dev = pd->net_dev;
|
||||
struct oz_hdr *oz_hdr;
|
||||
struct oz_elt *elt;
|
||||
struct oz_elt_connect_rsp *body;
|
||||
|
||||
int sz = sizeof(struct oz_hdr) + sizeof(struct oz_elt) +
|
||||
sizeof(struct oz_elt_connect_rsp);
|
||||
skb = alloc_skb(sz + OZ_ALLOCATED_SPACE(dev), GFP_ATOMIC);
|
||||
if (skb == NULL)
|
||||
return;
|
||||
skb_reserve(skb, LL_RESERVED_SPACE(dev));
|
||||
skb_reset_network_header(skb);
|
||||
oz_hdr = (struct oz_hdr *)skb_put(skb, sz);
|
||||
elt = (struct oz_elt *)(oz_hdr+1);
|
||||
body = (struct oz_elt_connect_rsp *)(elt+1);
|
||||
skb->dev = dev;
|
||||
skb->protocol = htons(OZ_ETHERTYPE);
|
||||
/* Fill in device header */
|
||||
if (dev_hard_header(skb, dev, OZ_ETHERTYPE, pd->mac_addr,
|
||||
dev->dev_addr, skb->len) < 0) {
|
||||
kfree_skb(skb);
|
||||
return;
|
||||
}
|
||||
oz_hdr->control = OZ_PROTOCOL_VERSION<<OZ_VERSION_SHIFT;
|
||||
oz_hdr->last_pkt_num = 0;
|
||||
put_unaligned(0, &oz_hdr->pkt_num);
|
||||
elt->type = OZ_ELT_CONNECT_RSP;
|
||||
elt->length = sizeof(struct oz_elt_connect_rsp);
|
||||
memset(body, 0, sizeof(struct oz_elt_connect_rsp));
|
||||
body->status = status;
|
||||
if (status == 0) {
|
||||
body->mode = pd->mode;
|
||||
body->session_id = pd->session_id;
|
||||
put_unaligned(cpu_to_le16(pd->total_apps), &body->apps);
|
||||
}
|
||||
oz_dbg(ON, "TX: OZ_ELT_CONNECT_RSP %d", status);
|
||||
dev_queue_xmit(skb);
|
||||
}
|
||||
|
||||
/*
|
||||
* Context: softirq-serialized
|
||||
*/
|
||||
static void pd_set_keepalive(struct oz_pd *pd, u8 kalive)
|
||||
{
|
||||
unsigned long keep_alive = kalive & OZ_KALIVE_VALUE_MASK;
|
||||
|
||||
switch (kalive & OZ_KALIVE_TYPE_MASK) {
|
||||
case OZ_KALIVE_SPECIAL:
|
||||
pd->keep_alive = keep_alive * 1000*60*60*24*20;
|
||||
break;
|
||||
case OZ_KALIVE_SECS:
|
||||
pd->keep_alive = keep_alive*1000;
|
||||
break;
|
||||
case OZ_KALIVE_MINS:
|
||||
pd->keep_alive = keep_alive*1000*60;
|
||||
break;
|
||||
case OZ_KALIVE_HOURS:
|
||||
pd->keep_alive = keep_alive*1000*60*60;
|
||||
break;
|
||||
default:
|
||||
pd->keep_alive = 0;
|
||||
}
|
||||
oz_dbg(ON, "Keepalive = %lu mSec\n", pd->keep_alive);
|
||||
}
|
||||
|
||||
/*
|
||||
* Context: softirq-serialized
|
||||
*/
|
||||
static void pd_set_presleep(struct oz_pd *pd, u8 presleep, u8 start_timer)
|
||||
{
|
||||
if (presleep)
|
||||
pd->presleep = presleep*100;
|
||||
else
|
||||
pd->presleep = OZ_PRESLEEP_TOUT;
|
||||
if (start_timer) {
|
||||
spin_unlock(&g_polling_lock);
|
||||
oz_timer_add(pd, OZ_TIMER_TOUT, pd->presleep);
|
||||
spin_lock(&g_polling_lock);
|
||||
}
|
||||
oz_dbg(ON, "Presleep time = %lu mSec\n", pd->presleep);
|
||||
}
|
||||
|
||||
/*
|
||||
* Context: softirq-serialized
|
||||
*/
|
||||
static struct oz_pd *oz_connect_req(struct oz_pd *cur_pd, struct oz_elt *elt,
|
||||
const u8 *pd_addr, struct net_device *net_dev)
|
||||
{
|
||||
struct oz_pd *pd;
|
||||
struct oz_elt_connect_req *body =
|
||||
(struct oz_elt_connect_req *)(elt+1);
|
||||
u8 rsp_status = OZ_STATUS_SUCCESS;
|
||||
u8 stop_needed = 0;
|
||||
u16 new_apps = g_apps;
|
||||
struct net_device *old_net_dev = NULL;
|
||||
struct oz_pd *free_pd = NULL;
|
||||
|
||||
if (cur_pd) {
|
||||
pd = cur_pd;
|
||||
spin_lock_bh(&g_polling_lock);
|
||||
} else {
|
||||
struct oz_pd *pd2 = NULL;
|
||||
struct list_head *e;
|
||||
|
||||
pd = oz_pd_alloc(pd_addr);
|
||||
if (pd == NULL)
|
||||
return NULL;
|
||||
getnstimeofday(&pd->last_rx_timestamp);
|
||||
spin_lock_bh(&g_polling_lock);
|
||||
list_for_each(e, &g_pd_list) {
|
||||
pd2 = list_entry(e, struct oz_pd, link);
|
||||
if (ether_addr_equal(pd2->mac_addr, pd_addr)) {
|
||||
free_pd = pd;
|
||||
pd = pd2;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (pd != pd2)
|
||||
list_add_tail(&pd->link, &g_pd_list);
|
||||
}
|
||||
if (pd == NULL) {
|
||||
spin_unlock_bh(&g_polling_lock);
|
||||
return NULL;
|
||||
}
|
||||
if (pd->net_dev != net_dev) {
|
||||
old_net_dev = pd->net_dev;
|
||||
dev_hold(net_dev);
|
||||
pd->net_dev = net_dev;
|
||||
}
|
||||
oz_dbg(ON, "Host vendor: %d\n", body->host_vendor);
|
||||
pd->max_tx_size = OZ_MAX_TX_SIZE;
|
||||
pd->mode = body->mode;
|
||||
pd->pd_info = body->pd_info;
|
||||
if (pd->mode & OZ_F_ISOC_NO_ELTS) {
|
||||
pd->ms_per_isoc = body->ms_per_isoc;
|
||||
if (!pd->ms_per_isoc)
|
||||
pd->ms_per_isoc = 4;
|
||||
|
||||
switch (body->ms_isoc_latency & OZ_LATENCY_MASK) {
|
||||
case OZ_ONE_MS_LATENCY:
|
||||
pd->isoc_latency = (body->ms_isoc_latency &
|
||||
~OZ_LATENCY_MASK) / pd->ms_per_isoc;
|
||||
break;
|
||||
case OZ_TEN_MS_LATENCY:
|
||||
pd->isoc_latency = ((body->ms_isoc_latency &
|
||||
~OZ_LATENCY_MASK) * 10) / pd->ms_per_isoc;
|
||||
break;
|
||||
default:
|
||||
pd->isoc_latency = OZ_MAX_TX_QUEUE_ISOC;
|
||||
}
|
||||
}
|
||||
if (body->max_len_div16)
|
||||
pd->max_tx_size = ((u16)body->max_len_div16)<<4;
|
||||
oz_dbg(ON, "Max frame:%u Ms per isoc:%u\n",
|
||||
pd->max_tx_size, pd->ms_per_isoc);
|
||||
pd->max_stream_buffering = 3*1024;
|
||||
pd->pulse_period = OZ_QUANTUM;
|
||||
pd_set_presleep(pd, body->presleep, 0);
|
||||
pd_set_keepalive(pd, body->keep_alive);
|
||||
|
||||
new_apps &= le16_to_cpu(get_unaligned(&body->apps));
|
||||
if ((new_apps & 0x1) && (body->session_id)) {
|
||||
if (pd->session_id) {
|
||||
if (pd->session_id != body->session_id) {
|
||||
rsp_status = OZ_STATUS_SESSION_MISMATCH;
|
||||
goto done;
|
||||
}
|
||||
} else {
|
||||
new_apps &= ~0x1; /* Resume not permitted */
|
||||
pd->session_id =
|
||||
oz_get_new_session_id(body->session_id);
|
||||
}
|
||||
} else {
|
||||
if (pd->session_id && !body->session_id) {
|
||||
rsp_status = OZ_STATUS_SESSION_TEARDOWN;
|
||||
stop_needed = 1;
|
||||
} else {
|
||||
new_apps &= ~0x1; /* Resume not permitted */
|
||||
pd->session_id =
|
||||
oz_get_new_session_id(body->session_id);
|
||||
}
|
||||
}
|
||||
done:
|
||||
if (rsp_status == OZ_STATUS_SUCCESS) {
|
||||
u16 start_apps = new_apps & ~pd->total_apps & ~0x1;
|
||||
u16 stop_apps = pd->total_apps & ~new_apps & ~0x1;
|
||||
u16 resume_apps = new_apps & pd->paused_apps & ~0x1;
|
||||
|
||||
spin_unlock_bh(&g_polling_lock);
|
||||
oz_pd_set_state(pd, OZ_PD_S_CONNECTED);
|
||||
oz_dbg(ON, "new_apps=0x%x total_apps=0x%x paused_apps=0x%x\n",
|
||||
new_apps, pd->total_apps, pd->paused_apps);
|
||||
if (start_apps) {
|
||||
if (oz_services_start(pd, start_apps, 0))
|
||||
rsp_status = OZ_STATUS_TOO_MANY_PDS;
|
||||
}
|
||||
if (resume_apps)
|
||||
if (oz_services_start(pd, resume_apps, 1))
|
||||
rsp_status = OZ_STATUS_TOO_MANY_PDS;
|
||||
if (stop_apps)
|
||||
oz_services_stop(pd, stop_apps, 0);
|
||||
oz_pd_request_heartbeat(pd);
|
||||
} else {
|
||||
spin_unlock_bh(&g_polling_lock);
|
||||
}
|
||||
oz_send_conn_rsp(pd, rsp_status);
|
||||
if (rsp_status != OZ_STATUS_SUCCESS) {
|
||||
if (stop_needed)
|
||||
oz_pd_stop(pd);
|
||||
oz_pd_put(pd);
|
||||
pd = NULL;
|
||||
}
|
||||
if (old_net_dev)
|
||||
dev_put(old_net_dev);
|
||||
if (free_pd)
|
||||
oz_pd_destroy(free_pd);
|
||||
return pd;
|
||||
}
|
||||
|
||||
/*
|
||||
* Context: softirq-serialized
|
||||
*/
|
||||
static void oz_add_farewell(struct oz_pd *pd, u8 ep_num, u8 index,
|
||||
const u8 *report, u8 len)
|
||||
{
|
||||
struct oz_farewell *f;
|
||||
struct oz_farewell *f2;
|
||||
int found = 0;
|
||||
|
||||
f = kmalloc(sizeof(struct oz_farewell) + len, GFP_ATOMIC);
|
||||
if (!f)
|
||||
return;
|
||||
f->ep_num = ep_num;
|
||||
f->index = index;
|
||||
f->len = len;
|
||||
memcpy(f->report, report, len);
|
||||
oz_dbg(ON, "RX: Adding farewell report\n");
|
||||
spin_lock(&g_polling_lock);
|
||||
list_for_each_entry(f2, &pd->farewell_list, link) {
|
||||
if ((f2->ep_num == ep_num) && (f2->index == index)) {
|
||||
found = 1;
|
||||
list_del(&f2->link);
|
||||
break;
|
||||
}
|
||||
}
|
||||
list_add_tail(&f->link, &pd->farewell_list);
|
||||
spin_unlock(&g_polling_lock);
|
||||
if (found)
|
||||
kfree(f2);
|
||||
}
|
||||
|
||||
/*
|
||||
* Context: softirq-serialized
|
||||
*/
|
||||
static void oz_rx_frame(struct sk_buff *skb)
|
||||
{
|
||||
u8 *mac_hdr;
|
||||
u8 *src_addr;
|
||||
struct oz_elt *elt;
|
||||
int length;
|
||||
struct oz_pd *pd = NULL;
|
||||
struct oz_hdr *oz_hdr = (struct oz_hdr *)skb_network_header(skb);
|
||||
struct timespec current_time;
|
||||
int dup = 0;
|
||||
u32 pkt_num;
|
||||
|
||||
oz_dbg(RX_FRAMES, "RX frame PN=0x%x LPN=0x%x control=0x%x\n",
|
||||
oz_hdr->pkt_num, oz_hdr->last_pkt_num, oz_hdr->control);
|
||||
mac_hdr = skb_mac_header(skb);
|
||||
src_addr = &mac_hdr[ETH_ALEN];
|
||||
length = skb->len;
|
||||
|
||||
/* Check the version field */
|
||||
if (oz_get_prot_ver(oz_hdr->control) != OZ_PROTOCOL_VERSION) {
|
||||
oz_dbg(ON, "Incorrect protocol version: %d\n",
|
||||
oz_get_prot_ver(oz_hdr->control));
|
||||
goto done;
|
||||
}
|
||||
|
||||
pkt_num = le32_to_cpu(get_unaligned(&oz_hdr->pkt_num));
|
||||
|
||||
pd = oz_pd_find(src_addr);
|
||||
if (pd) {
|
||||
if (!(pd->state & OZ_PD_S_CONNECTED))
|
||||
oz_pd_set_state(pd, OZ_PD_S_CONNECTED);
|
||||
getnstimeofday(¤t_time);
|
||||
if ((current_time.tv_sec != pd->last_rx_timestamp.tv_sec) ||
|
||||
(pd->presleep < MSEC_PER_SEC)) {
|
||||
oz_timer_add(pd, OZ_TIMER_TOUT, pd->presleep);
|
||||
pd->last_rx_timestamp = current_time;
|
||||
}
|
||||
if (pkt_num != pd->last_rx_pkt_num) {
|
||||
pd->last_rx_pkt_num = pkt_num;
|
||||
} else {
|
||||
dup = 1;
|
||||
oz_dbg(ON, "Duplicate frame\n");
|
||||
}
|
||||
}
|
||||
|
||||
if (pd && !dup && ((pd->mode & OZ_MODE_MASK) == OZ_MODE_TRIGGERED)) {
|
||||
oz_dbg(RX_FRAMES, "Received TRIGGER Frame\n");
|
||||
pd->last_sent_frame = &pd->tx_queue;
|
||||
if (oz_hdr->control & OZ_F_ACK) {
|
||||
/* Retire completed frames */
|
||||
oz_retire_tx_frames(pd, oz_hdr->last_pkt_num);
|
||||
}
|
||||
if ((oz_hdr->control & OZ_F_ACK_REQUESTED) &&
|
||||
(pd->state == OZ_PD_S_CONNECTED)) {
|
||||
int backlog = pd->nb_queued_frames;
|
||||
|
||||
pd->trigger_pkt_num = pkt_num;
|
||||
/* Send queued frames */
|
||||
oz_send_queued_frames(pd, backlog);
|
||||
}
|
||||
}
|
||||
|
||||
length -= sizeof(struct oz_hdr);
|
||||
elt = (struct oz_elt *)((u8 *)oz_hdr + sizeof(struct oz_hdr));
|
||||
|
||||
while (length >= sizeof(struct oz_elt)) {
|
||||
length -= sizeof(struct oz_elt) + elt->length;
|
||||
if (length < 0)
|
||||
break;
|
||||
switch (elt->type) {
|
||||
case OZ_ELT_CONNECT_REQ:
|
||||
oz_dbg(ON, "RX: OZ_ELT_CONNECT_REQ\n");
|
||||
pd = oz_connect_req(pd, elt, src_addr, skb->dev);
|
||||
break;
|
||||
case OZ_ELT_DISCONNECT:
|
||||
oz_dbg(ON, "RX: OZ_ELT_DISCONNECT\n");
|
||||
if (pd)
|
||||
oz_pd_sleep(pd);
|
||||
break;
|
||||
case OZ_ELT_UPDATE_PARAM_REQ: {
|
||||
struct oz_elt_update_param *body =
|
||||
(struct oz_elt_update_param *)(elt + 1);
|
||||
oz_dbg(ON, "RX: OZ_ELT_UPDATE_PARAM_REQ\n");
|
||||
if (pd && (pd->state & OZ_PD_S_CONNECTED)) {
|
||||
spin_lock(&g_polling_lock);
|
||||
pd_set_keepalive(pd, body->keepalive);
|
||||
pd_set_presleep(pd, body->presleep, 1);
|
||||
spin_unlock(&g_polling_lock);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case OZ_ELT_FAREWELL_REQ: {
|
||||
struct oz_elt_farewell *body =
|
||||
(struct oz_elt_farewell *)(elt + 1);
|
||||
oz_dbg(ON, "RX: OZ_ELT_FAREWELL_REQ\n");
|
||||
oz_add_farewell(pd, body->ep_num,
|
||||
body->index, body->report,
|
||||
elt->length + 1 - sizeof(*body));
|
||||
}
|
||||
break;
|
||||
case OZ_ELT_APP_DATA:
|
||||
if (pd && (pd->state & OZ_PD_S_CONNECTED)) {
|
||||
struct oz_app_hdr *app_hdr =
|
||||
(struct oz_app_hdr *)(elt+1);
|
||||
if (dup)
|
||||
break;
|
||||
oz_handle_app_elt(pd, app_hdr->app_id, elt);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
oz_dbg(ON, "RX: Unknown elt %02x\n", elt->type);
|
||||
}
|
||||
elt = oz_next_elt(elt);
|
||||
}
|
||||
done:
|
||||
if (pd)
|
||||
oz_pd_put(pd);
|
||||
consume_skb(skb);
|
||||
}
|
||||
|
||||
/*
|
||||
* Context: process
|
||||
*/
|
||||
void oz_protocol_term(void)
|
||||
{
|
||||
struct oz_binding *b, *t;
|
||||
|
||||
/* Walk the list of bindings and remove each one.
|
||||
*/
|
||||
spin_lock_bh(&g_binding_lock);
|
||||
list_for_each_entry_safe(b, t, &g_binding, link) {
|
||||
list_del(&b->link);
|
||||
spin_unlock_bh(&g_binding_lock);
|
||||
dev_remove_pack(&b->ptype);
|
||||
if (b->ptype.dev)
|
||||
dev_put(b->ptype.dev);
|
||||
kfree(b);
|
||||
spin_lock_bh(&g_binding_lock);
|
||||
}
|
||||
spin_unlock_bh(&g_binding_lock);
|
||||
/* Walk the list of PDs and stop each one. This causes the PD to be
|
||||
* removed from the list so we can just pull each one from the head
|
||||
* of the list.
|
||||
*/
|
||||
spin_lock_bh(&g_polling_lock);
|
||||
while (!list_empty(&g_pd_list)) {
|
||||
struct oz_pd *pd =
|
||||
list_first_entry(&g_pd_list, struct oz_pd, link);
|
||||
oz_pd_get(pd);
|
||||
spin_unlock_bh(&g_polling_lock);
|
||||
oz_pd_stop(pd);
|
||||
oz_pd_put(pd);
|
||||
spin_lock_bh(&g_polling_lock);
|
||||
}
|
||||
spin_unlock_bh(&g_polling_lock);
|
||||
oz_dbg(ON, "Protocol stopped\n");
|
||||
|
||||
kmem_cache_destroy(oz_tx_frame_cache);
|
||||
kmem_cache_destroy(oz_elt_info_cache);
|
||||
}
|
||||
|
||||
/*
|
||||
* Context: softirq
|
||||
*/
|
||||
void oz_pd_heartbeat_handler(unsigned long data)
|
||||
{
|
||||
struct oz_pd *pd = (struct oz_pd *)data;
|
||||
u16 apps = 0;
|
||||
|
||||
spin_lock_bh(&g_polling_lock);
|
||||
if (pd->state & OZ_PD_S_CONNECTED)
|
||||
apps = pd->total_apps;
|
||||
spin_unlock_bh(&g_polling_lock);
|
||||
if (apps)
|
||||
oz_pd_heartbeat(pd, apps);
|
||||
oz_pd_put(pd);
|
||||
}
|
||||
|
||||
/*
|
||||
* Context: softirq
|
||||
*/
|
||||
void oz_pd_timeout_handler(unsigned long data)
|
||||
{
|
||||
int type;
|
||||
struct oz_pd *pd = (struct oz_pd *)data;
|
||||
|
||||
spin_lock_bh(&g_polling_lock);
|
||||
type = pd->timeout_type;
|
||||
spin_unlock_bh(&g_polling_lock);
|
||||
switch (type) {
|
||||
case OZ_TIMER_TOUT:
|
||||
oz_pd_sleep(pd);
|
||||
break;
|
||||
case OZ_TIMER_STOP:
|
||||
oz_pd_stop(pd);
|
||||
break;
|
||||
}
|
||||
oz_pd_put(pd);
|
||||
}
|
||||
|
||||
/*
|
||||
* Context: Interrupt
|
||||
*/
|
||||
enum hrtimer_restart oz_pd_heartbeat_event(struct hrtimer *timer)
|
||||
{
|
||||
struct oz_pd *pd;
|
||||
|
||||
pd = container_of(timer, struct oz_pd, heartbeat);
|
||||
hrtimer_forward_now(timer, ktime_set(pd->pulse_period /
|
||||
MSEC_PER_SEC, (pd->pulse_period % MSEC_PER_SEC) * NSEC_PER_MSEC));
|
||||
oz_pd_get(pd);
|
||||
tasklet_schedule(&pd->heartbeat_tasklet);
|
||||
return HRTIMER_RESTART;
|
||||
}
|
||||
|
||||
/*
|
||||
* Context: Interrupt
|
||||
*/
|
||||
enum hrtimer_restart oz_pd_timeout_event(struct hrtimer *timer)
|
||||
{
|
||||
struct oz_pd *pd;
|
||||
|
||||
pd = container_of(timer, struct oz_pd, timeout);
|
||||
oz_pd_get(pd);
|
||||
tasklet_schedule(&pd->timeout_tasklet);
|
||||
return HRTIMER_NORESTART;
|
||||
}
|
||||
|
||||
/*
|
||||
* Context: softirq or process
|
||||
*/
|
||||
void oz_timer_add(struct oz_pd *pd, int type, unsigned long due_time)
|
||||
{
|
||||
spin_lock_bh(&g_polling_lock);
|
||||
switch (type) {
|
||||
case OZ_TIMER_TOUT:
|
||||
case OZ_TIMER_STOP:
|
||||
if (hrtimer_active(&pd->timeout)) {
|
||||
hrtimer_set_expires(&pd->timeout, ktime_set(due_time /
|
||||
MSEC_PER_SEC, (due_time % MSEC_PER_SEC) *
|
||||
NSEC_PER_MSEC));
|
||||
hrtimer_start_expires(&pd->timeout, HRTIMER_MODE_REL);
|
||||
} else {
|
||||
hrtimer_start(&pd->timeout, ktime_set(due_time /
|
||||
MSEC_PER_SEC, (due_time % MSEC_PER_SEC) *
|
||||
NSEC_PER_MSEC), HRTIMER_MODE_REL);
|
||||
}
|
||||
pd->timeout_type = type;
|
||||
break;
|
||||
case OZ_TIMER_HEARTBEAT:
|
||||
if (!hrtimer_active(&pd->heartbeat))
|
||||
hrtimer_start(&pd->heartbeat, ktime_set(due_time /
|
||||
MSEC_PER_SEC, (due_time % MSEC_PER_SEC) *
|
||||
NSEC_PER_MSEC), HRTIMER_MODE_REL);
|
||||
break;
|
||||
}
|
||||
spin_unlock_bh(&g_polling_lock);
|
||||
}
|
||||
|
||||
/*
|
||||
* Context: softirq or process
|
||||
*/
|
||||
void oz_pd_request_heartbeat(struct oz_pd *pd)
|
||||
{
|
||||
oz_timer_add(pd, OZ_TIMER_HEARTBEAT, pd->pulse_period > 0 ?
|
||||
pd->pulse_period : OZ_QUANTUM);
|
||||
}
|
||||
|
||||
/*
|
||||
* Context: softirq or process
|
||||
*/
|
||||
struct oz_pd *oz_pd_find(const u8 *mac_addr)
|
||||
{
|
||||
struct oz_pd *pd;
|
||||
|
||||
spin_lock_bh(&g_polling_lock);
|
||||
list_for_each_entry(pd, &g_pd_list, link) {
|
||||
if (ether_addr_equal(pd->mac_addr, mac_addr)) {
|
||||
oz_pd_get(pd);
|
||||
spin_unlock_bh(&g_polling_lock);
|
||||
return pd;
|
||||
}
|
||||
}
|
||||
spin_unlock_bh(&g_polling_lock);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Context: process
|
||||
*/
|
||||
void oz_app_enable(int app_id, int enable)
|
||||
{
|
||||
if (app_id < OZ_NB_APPS) {
|
||||
spin_lock_bh(&g_polling_lock);
|
||||
if (enable)
|
||||
g_apps |= (1<<app_id);
|
||||
else
|
||||
g_apps &= ~(1<<app_id);
|
||||
spin_unlock_bh(&g_polling_lock);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Context: softirq
|
||||
*/
|
||||
static int oz_pkt_recv(struct sk_buff *skb, struct net_device *dev,
|
||||
struct packet_type *pt, struct net_device *orig_dev)
|
||||
{
|
||||
skb = skb_share_check(skb, GFP_ATOMIC);
|
||||
if (skb == NULL)
|
||||
return 0;
|
||||
spin_lock_bh(&g_rx_queue.lock);
|
||||
if (g_processing_rx) {
|
||||
/* We already hold the lock so use __ variant.
|
||||
*/
|
||||
__skb_queue_head(&g_rx_queue, skb);
|
||||
spin_unlock_bh(&g_rx_queue.lock);
|
||||
} else {
|
||||
g_processing_rx = 1;
|
||||
do {
|
||||
|
||||
spin_unlock_bh(&g_rx_queue.lock);
|
||||
oz_rx_frame(skb);
|
||||
spin_lock_bh(&g_rx_queue.lock);
|
||||
if (skb_queue_empty(&g_rx_queue)) {
|
||||
g_processing_rx = 0;
|
||||
spin_unlock_bh(&g_rx_queue.lock);
|
||||
break;
|
||||
}
|
||||
/* We already hold the lock so use __ variant.
|
||||
*/
|
||||
skb = __skb_dequeue(&g_rx_queue);
|
||||
} while (1);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Context: process
|
||||
*/
|
||||
void oz_binding_add(const char *net_dev)
|
||||
{
|
||||
struct oz_binding *binding;
|
||||
|
||||
binding = kzalloc(sizeof(struct oz_binding), GFP_KERNEL);
|
||||
if (!binding)
|
||||
return;
|
||||
|
||||
binding->ptype.type = htons(OZ_ETHERTYPE);
|
||||
binding->ptype.func = oz_pkt_recv;
|
||||
if (net_dev && *net_dev) {
|
||||
memcpy(binding->name, net_dev, OZ_MAX_BINDING_LEN);
|
||||
oz_dbg(ON, "Adding binding: %s\n", net_dev);
|
||||
binding->ptype.dev = dev_get_by_name(&init_net, net_dev);
|
||||
if (binding->ptype.dev == NULL) {
|
||||
oz_dbg(ON, "Netdev %s not found\n", net_dev);
|
||||
kfree(binding);
|
||||
return;
|
||||
}
|
||||
}
|
||||
dev_add_pack(&binding->ptype);
|
||||
spin_lock_bh(&g_binding_lock);
|
||||
list_add_tail(&binding->link, &g_binding);
|
||||
spin_unlock_bh(&g_binding_lock);
|
||||
}
|
||||
|
||||
/*
|
||||
* Context: process
|
||||
*/
|
||||
static void pd_stop_all_for_device(struct net_device *net_dev)
|
||||
{
|
||||
LIST_HEAD(h);
|
||||
struct oz_pd *pd;
|
||||
struct oz_pd *n;
|
||||
|
||||
spin_lock_bh(&g_polling_lock);
|
||||
list_for_each_entry_safe(pd, n, &g_pd_list, link) {
|
||||
if (pd->net_dev == net_dev) {
|
||||
list_move(&pd->link, &h);
|
||||
oz_pd_get(pd);
|
||||
}
|
||||
}
|
||||
spin_unlock_bh(&g_polling_lock);
|
||||
while (!list_empty(&h)) {
|
||||
pd = list_first_entry(&h, struct oz_pd, link);
|
||||
oz_pd_stop(pd);
|
||||
oz_pd_put(pd);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Context: process
|
||||
*/
|
||||
void oz_binding_remove(const char *net_dev)
|
||||
{
|
||||
struct oz_binding *binding;
|
||||
int found = 0;
|
||||
|
||||
oz_dbg(ON, "Removing binding: %s\n", net_dev);
|
||||
spin_lock_bh(&g_binding_lock);
|
||||
list_for_each_entry(binding, &g_binding, link) {
|
||||
if (strncmp(binding->name, net_dev, OZ_MAX_BINDING_LEN) == 0) {
|
||||
oz_dbg(ON, "Binding '%s' found\n", net_dev);
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
spin_unlock_bh(&g_binding_lock);
|
||||
if (found) {
|
||||
dev_remove_pack(&binding->ptype);
|
||||
if (binding->ptype.dev) {
|
||||
dev_put(binding->ptype.dev);
|
||||
pd_stop_all_for_device(binding->ptype.dev);
|
||||
}
|
||||
list_del(&binding->link);
|
||||
kfree(binding);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Context: process
|
||||
*/
|
||||
static char *oz_get_next_device_name(char *s, char *dname, int max_size)
|
||||
{
|
||||
while (*s == ',')
|
||||
s++;
|
||||
while (*s && (*s != ',') && max_size > 1) {
|
||||
*dname++ = *s++;
|
||||
max_size--;
|
||||
}
|
||||
*dname = 0;
|
||||
return s;
|
||||
}
|
||||
|
||||
/*
|
||||
* Context: process
|
||||
*/
|
||||
int oz_protocol_init(char *devs)
|
||||
{
|
||||
oz_elt_info_cache = KMEM_CACHE(oz_elt_info, 0);
|
||||
if (!oz_elt_info_cache)
|
||||
return -ENOMEM;
|
||||
|
||||
oz_tx_frame_cache = KMEM_CACHE(oz_tx_frame, 0);
|
||||
if (!oz_tx_frame_cache) {
|
||||
kmem_cache_destroy(oz_elt_info_cache);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
skb_queue_head_init(&g_rx_queue);
|
||||
if (devs[0] == '*') {
|
||||
oz_binding_add(NULL);
|
||||
} else {
|
||||
char d[32];
|
||||
|
||||
while (*devs) {
|
||||
devs = oz_get_next_device_name(devs, d, sizeof(d));
|
||||
if (d[0])
|
||||
oz_binding_add(d);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Context: process
|
||||
*/
|
||||
int oz_get_pd_list(struct oz_mac_addr *addr, int max_count)
|
||||
{
|
||||
struct oz_pd *pd;
|
||||
int count = 0;
|
||||
|
||||
spin_lock_bh(&g_polling_lock);
|
||||
list_for_each_entry(pd, &g_pd_list, link) {
|
||||
if (count >= max_count)
|
||||
break;
|
||||
ether_addr_copy((u8 *)&addr[count++], pd->mac_addr);
|
||||
}
|
||||
spin_unlock_bh(&g_polling_lock);
|
||||
return count;
|
||||
}
|
||||
|
|
@ -1,62 +0,0 @@
|
|||
/* -----------------------------------------------------------------------------
|
||||
* Copyright (c) 2011 Ozmo Inc
|
||||
* Released under the GNU General Public License Version 2 (GPLv2).
|
||||
* -----------------------------------------------------------------------------
|
||||
*/
|
||||
#ifndef _OZPROTO_H
|
||||
#define _OZPROTO_H
|
||||
|
||||
#include <asm/byteorder.h>
|
||||
#include "ozdbg.h"
|
||||
#include "ozappif.h"
|
||||
|
||||
#define OZ_ALLOCATED_SPACE(__x) (LL_RESERVED_SPACE(__x)+(__x)->needed_tailroom)
|
||||
|
||||
/* Quantum in MS */
|
||||
#define OZ_QUANTUM 8
|
||||
/* Default timeouts.
|
||||
*/
|
||||
#define OZ_PRESLEEP_TOUT 11
|
||||
|
||||
/* Maximun sizes of tx frames. */
|
||||
#define OZ_MAX_TX_SIZE 760
|
||||
|
||||
/* Maximum number of uncompleted isoc frames that can be pending in network. */
|
||||
#define OZ_MAX_SUBMITTED_ISOC 16
|
||||
|
||||
/* Maximum number of uncompleted isoc frames that can be pending in Tx Queue. */
|
||||
#define OZ_MAX_TX_QUEUE_ISOC 32
|
||||
|
||||
/* Application handler functions.
|
||||
*/
|
||||
struct oz_app_if {
|
||||
int (*init)(void);
|
||||
void (*term)(void);
|
||||
int (*start)(struct oz_pd *pd, int resume);
|
||||
void (*stop)(struct oz_pd *pd, int pause);
|
||||
void (*rx)(struct oz_pd *pd, struct oz_elt *elt);
|
||||
int (*heartbeat)(struct oz_pd *pd);
|
||||
void (*farewell)(struct oz_pd *pd, u8 ep_num, u8 *data, u8 len);
|
||||
};
|
||||
|
||||
int oz_protocol_init(char *devs);
|
||||
void oz_protocol_term(void);
|
||||
int oz_get_pd_list(struct oz_mac_addr *addr, int max_count);
|
||||
void oz_app_enable(int app_id, int enable);
|
||||
struct oz_pd *oz_pd_find(const u8 *mac_addr);
|
||||
void oz_binding_add(const char *net_dev);
|
||||
void oz_binding_remove(const char *net_dev);
|
||||
void oz_timer_add(struct oz_pd *pd, int type, unsigned long due_time);
|
||||
void oz_timer_delete(struct oz_pd *pd, int type);
|
||||
void oz_pd_request_heartbeat(struct oz_pd *pd);
|
||||
void oz_pd_heartbeat_handler(unsigned long data);
|
||||
void oz_pd_timeout_handler(unsigned long data);
|
||||
enum hrtimer_restart oz_pd_heartbeat_event(struct hrtimer *timer);
|
||||
enum hrtimer_restart oz_pd_timeout_event(struct hrtimer *timer);
|
||||
int oz_get_pd_status_list(char *pd_list, int max_count);
|
||||
int oz_get_binding_list(char *buf, int max_if);
|
||||
|
||||
extern struct kmem_cache *oz_elt_info_cache;
|
||||
extern struct kmem_cache *oz_tx_frame_cache;
|
||||
|
||||
#endif /* _OZPROTO_H */
|
|
@ -1,375 +0,0 @@
|
|||
/* -----------------------------------------------------------------------------
|
||||
* Copyright (c) 2011 Ozmo Inc
|
||||
* Released under the GNU General Public License Version 2 (GPLv2).
|
||||
* -----------------------------------------------------------------------------
|
||||
*/
|
||||
#ifndef _OZPROTOCOL_H
|
||||
#define _OZPROTOCOL_H
|
||||
|
||||
#define PACKED __packed
|
||||
|
||||
#define OZ_ETHERTYPE 0x892e
|
||||
|
||||
/* Status codes
|
||||
*/
|
||||
#define OZ_STATUS_SUCCESS 0
|
||||
#define OZ_STATUS_INVALID_PARAM 1
|
||||
#define OZ_STATUS_TOO_MANY_PDS 2
|
||||
#define OZ_STATUS_NOT_ALLOWED 4
|
||||
#define OZ_STATUS_SESSION_MISMATCH 5
|
||||
#define OZ_STATUS_SESSION_TEARDOWN 6
|
||||
|
||||
/* This is the generic element header.
|
||||
Every element starts with this.
|
||||
*/
|
||||
struct oz_elt {
|
||||
u8 type;
|
||||
u8 length;
|
||||
} PACKED;
|
||||
|
||||
#define oz_next_elt(__elt) \
|
||||
(struct oz_elt *)((u8 *)((__elt) + 1) + (__elt)->length)
|
||||
|
||||
/* Protocol element IDs.
|
||||
*/
|
||||
#define OZ_ELT_CONNECT_REQ 0x06
|
||||
#define OZ_ELT_CONNECT_RSP 0x07
|
||||
#define OZ_ELT_DISCONNECT 0x08
|
||||
#define OZ_ELT_UPDATE_PARAM_REQ 0x11
|
||||
#define OZ_ELT_FAREWELL_REQ 0x12
|
||||
#define OZ_ELT_APP_DATA 0x31
|
||||
|
||||
/* This is the Ozmo header which is the first Ozmo specific part
|
||||
* of a frame and comes after the MAC header.
|
||||
*/
|
||||
struct oz_hdr {
|
||||
u8 control;
|
||||
u8 last_pkt_num;
|
||||
u32 pkt_num;
|
||||
} PACKED;
|
||||
|
||||
#define OZ_PROTOCOL_VERSION 0x1
|
||||
/* Bits in the control field. */
|
||||
#define OZ_VERSION_MASK 0xc
|
||||
#define OZ_VERSION_SHIFT 2
|
||||
#define OZ_F_ACK 0x10
|
||||
#define OZ_F_ISOC 0x20
|
||||
#define OZ_F_MORE_DATA 0x40
|
||||
#define OZ_F_ACK_REQUESTED 0x80
|
||||
|
||||
#define oz_get_prot_ver(__x) (((__x) & OZ_VERSION_MASK) >> OZ_VERSION_SHIFT)
|
||||
|
||||
/* Used to select the bits of packet number to put in the last_pkt_num.
|
||||
*/
|
||||
#define OZ_LAST_PN_MASK 0x00ff
|
||||
|
||||
#define OZ_LAST_PN_HALF_CYCLE 127
|
||||
|
||||
#define OZ_LATENCY_MASK 0xc0
|
||||
#define OZ_ONE_MS_LATENCY 0x40
|
||||
#define OZ_TEN_MS_LATENCY 0x80
|
||||
|
||||
/* Connect request data structure.
|
||||
*/
|
||||
struct oz_elt_connect_req {
|
||||
u8 mode;
|
||||
u8 resv1[16];
|
||||
u8 pd_info;
|
||||
u8 session_id;
|
||||
u8 presleep;
|
||||
u8 ms_isoc_latency;
|
||||
u8 host_vendor;
|
||||
u8 keep_alive;
|
||||
u16 apps;
|
||||
u8 max_len_div16;
|
||||
u8 ms_per_isoc;
|
||||
u8 resv3[2];
|
||||
} PACKED;
|
||||
|
||||
/* mode field bits.
|
||||
*/
|
||||
#define OZ_MODE_POLLED 0x0
|
||||
#define OZ_MODE_TRIGGERED 0x1
|
||||
#define OZ_MODE_MASK 0xf
|
||||
#define OZ_F_ISOC_NO_ELTS 0x40
|
||||
#define OZ_F_ISOC_ANYTIME 0x80
|
||||
#define OZ_NO_ELTS_ANYTIME 0xc0
|
||||
|
||||
/* Keep alive field.
|
||||
*/
|
||||
#define OZ_KALIVE_TYPE_MASK 0xc0
|
||||
#define OZ_KALIVE_VALUE_MASK 0x3f
|
||||
#define OZ_KALIVE_SPECIAL 0x00
|
||||
#define OZ_KALIVE_SECS 0x40
|
||||
#define OZ_KALIVE_MINS 0x80
|
||||
#define OZ_KALIVE_HOURS 0xc0
|
||||
|
||||
/* Connect response data structure.
|
||||
*/
|
||||
struct oz_elt_connect_rsp {
|
||||
u8 mode;
|
||||
u8 status;
|
||||
u8 resv1[3];
|
||||
u8 session_id;
|
||||
u16 apps;
|
||||
u32 resv2;
|
||||
} PACKED;
|
||||
|
||||
struct oz_elt_farewell {
|
||||
u8 ep_num;
|
||||
u8 index;
|
||||
u8 report[1];
|
||||
} PACKED;
|
||||
|
||||
struct oz_elt_update_param {
|
||||
u8 resv1[16];
|
||||
u8 presleep;
|
||||
u8 resv2;
|
||||
u8 host_vendor;
|
||||
u8 keepalive;
|
||||
} PACKED;
|
||||
|
||||
/* Header common to all application elements.
|
||||
*/
|
||||
struct oz_app_hdr {
|
||||
u8 app_id;
|
||||
u8 elt_seq_num;
|
||||
} PACKED;
|
||||
|
||||
/* Values for app_id.
|
||||
*/
|
||||
#define OZ_APPID_USB 0x1
|
||||
#define OZ_APPID_SERIAL 0x4
|
||||
#define OZ_APPID_MAX OZ_APPID_SERIAL
|
||||
#define OZ_NB_APPS (OZ_APPID_MAX+1)
|
||||
|
||||
/* USB header common to all elements for the USB application.
|
||||
* This header extends the oz_app_hdr and comes directly after
|
||||
* the element header in a USB application.
|
||||
*/
|
||||
struct oz_usb_hdr {
|
||||
u8 app_id;
|
||||
u8 elt_seq_num;
|
||||
u8 type;
|
||||
} PACKED;
|
||||
|
||||
|
||||
|
||||
/* USB requests element subtypes (type field of hs_usb_hdr).
|
||||
*/
|
||||
#define OZ_GET_DESC_REQ 1
|
||||
#define OZ_GET_DESC_RSP 2
|
||||
#define OZ_SET_CONFIG_REQ 3
|
||||
#define OZ_SET_CONFIG_RSP 4
|
||||
#define OZ_SET_INTERFACE_REQ 5
|
||||
#define OZ_SET_INTERFACE_RSP 6
|
||||
#define OZ_VENDOR_CLASS_REQ 7
|
||||
#define OZ_VENDOR_CLASS_RSP 8
|
||||
#define OZ_GET_STATUS_REQ 9
|
||||
#define OZ_GET_STATUS_RSP 10
|
||||
#define OZ_CLEAR_FEATURE_REQ 11
|
||||
#define OZ_CLEAR_FEATURE_RSP 12
|
||||
#define OZ_SET_FEATURE_REQ 13
|
||||
#define OZ_SET_FEATURE_RSP 14
|
||||
#define OZ_GET_CONFIGURATION_REQ 15
|
||||
#define OZ_GET_CONFIGURATION_RSP 16
|
||||
#define OZ_GET_INTERFACE_REQ 17
|
||||
#define OZ_GET_INTERFACE_RSP 18
|
||||
#define OZ_SYNCH_FRAME_REQ 19
|
||||
#define OZ_SYNCH_FRAME_RSP 20
|
||||
#define OZ_USB_ENDPOINT_DATA 23
|
||||
|
||||
#define OZ_REQD_D2H 0x80
|
||||
|
||||
struct oz_get_desc_req {
|
||||
u8 app_id;
|
||||
u8 elt_seq_num;
|
||||
u8 type;
|
||||
u8 req_id;
|
||||
u16 offset;
|
||||
u16 size;
|
||||
u8 req_type;
|
||||
u8 desc_type;
|
||||
__le16 w_index;
|
||||
u8 index;
|
||||
} PACKED;
|
||||
|
||||
/* Values for desc_type field.
|
||||
*/
|
||||
#define OZ_DESC_DEVICE 0x01
|
||||
#define OZ_DESC_CONFIG 0x02
|
||||
#define OZ_DESC_STRING 0x03
|
||||
|
||||
/* Values for req_type field.
|
||||
*/
|
||||
#define OZ_RECP_MASK 0x1F
|
||||
#define OZ_RECP_DEVICE 0x00
|
||||
#define OZ_RECP_INTERFACE 0x01
|
||||
#define OZ_RECP_ENDPOINT 0x02
|
||||
|
||||
#define OZ_REQT_MASK 0x60
|
||||
#define OZ_REQT_STD 0x00
|
||||
#define OZ_REQT_CLASS 0x20
|
||||
#define OZ_REQT_VENDOR 0x40
|
||||
|
||||
struct oz_get_desc_rsp {
|
||||
u8 app_id;
|
||||
u8 elt_seq_num;
|
||||
u8 type;
|
||||
u8 req_id;
|
||||
__le16 offset;
|
||||
__le16 total_size;
|
||||
u8 rcode;
|
||||
u8 data[1];
|
||||
} PACKED;
|
||||
|
||||
struct oz_feature_req {
|
||||
u8 app_id;
|
||||
u8 elt_seq_num;
|
||||
u8 type;
|
||||
u8 req_id;
|
||||
u8 recipient;
|
||||
u8 index;
|
||||
u16 feature;
|
||||
} PACKED;
|
||||
|
||||
struct oz_feature_rsp {
|
||||
u8 app_id;
|
||||
u8 elt_seq_num;
|
||||
u8 type;
|
||||
u8 req_id;
|
||||
u8 rcode;
|
||||
} PACKED;
|
||||
|
||||
struct oz_set_config_req {
|
||||
u8 app_id;
|
||||
u8 elt_seq_num;
|
||||
u8 type;
|
||||
u8 req_id;
|
||||
u8 index;
|
||||
} PACKED;
|
||||
|
||||
struct oz_set_config_rsp {
|
||||
u8 app_id;
|
||||
u8 elt_seq_num;
|
||||
u8 type;
|
||||
u8 req_id;
|
||||
u8 rcode;
|
||||
} PACKED;
|
||||
|
||||
struct oz_set_interface_req {
|
||||
u8 app_id;
|
||||
u8 elt_seq_num;
|
||||
u8 type;
|
||||
u8 req_id;
|
||||
u8 index;
|
||||
u8 alternative;
|
||||
} PACKED;
|
||||
|
||||
struct oz_set_interface_rsp {
|
||||
u8 app_id;
|
||||
u8 elt_seq_num;
|
||||
u8 type;
|
||||
u8 req_id;
|
||||
u8 rcode;
|
||||
} PACKED;
|
||||
|
||||
struct oz_get_interface_req {
|
||||
u8 app_id;
|
||||
u8 elt_seq_num;
|
||||
u8 type;
|
||||
u8 req_id;
|
||||
u8 index;
|
||||
} PACKED;
|
||||
|
||||
struct oz_get_interface_rsp {
|
||||
u8 app_id;
|
||||
u8 elt_seq_num;
|
||||
u8 type;
|
||||
u8 req_id;
|
||||
u8 rcode;
|
||||
u8 alternative;
|
||||
} PACKED;
|
||||
|
||||
struct oz_vendor_class_req {
|
||||
u8 app_id;
|
||||
u8 elt_seq_num;
|
||||
u8 type;
|
||||
u8 req_id;
|
||||
u8 req_type;
|
||||
u8 request;
|
||||
u16 value;
|
||||
u16 index;
|
||||
u8 data[1];
|
||||
} PACKED;
|
||||
|
||||
struct oz_vendor_class_rsp {
|
||||
u8 app_id;
|
||||
u8 elt_seq_num;
|
||||
u8 type;
|
||||
u8 req_id;
|
||||
u8 rcode;
|
||||
u8 data[1];
|
||||
} PACKED;
|
||||
|
||||
struct oz_data {
|
||||
u8 app_id;
|
||||
u8 elt_seq_num;
|
||||
u8 type;
|
||||
u8 endpoint;
|
||||
u8 format;
|
||||
} PACKED;
|
||||
|
||||
struct oz_isoc_fixed {
|
||||
u8 app_id;
|
||||
u8 elt_seq_num;
|
||||
u8 type;
|
||||
u8 endpoint;
|
||||
u8 format;
|
||||
u8 unit_size;
|
||||
u8 frame_number;
|
||||
u8 data[1];
|
||||
} PACKED;
|
||||
|
||||
struct oz_multiple_fixed {
|
||||
u8 app_id;
|
||||
u8 elt_seq_num;
|
||||
u8 type;
|
||||
u8 endpoint;
|
||||
u8 format;
|
||||
u8 unit_size;
|
||||
u8 data[1];
|
||||
} PACKED;
|
||||
|
||||
struct oz_fragmented {
|
||||
u8 app_id;
|
||||
u8 elt_seq_num;
|
||||
u8 type;
|
||||
u8 endpoint;
|
||||
u8 format;
|
||||
u16 total_size;
|
||||
u16 offset;
|
||||
u8 data[1];
|
||||
} PACKED;
|
||||
|
||||
/* Note: the following does not get packaged in an element in the same way
|
||||
* that other data formats are packaged. Instead the data is put in a frame
|
||||
* directly after the oz_header and is the only permitted data in such a
|
||||
* frame. The length of the data is directly determined from the frame size.
|
||||
*/
|
||||
struct oz_isoc_large {
|
||||
u8 endpoint;
|
||||
u8 format;
|
||||
u8 ms_data;
|
||||
u8 frame_number;
|
||||
} PACKED;
|
||||
|
||||
#define OZ_DATA_F_TYPE_MASK 0xF
|
||||
#define OZ_DATA_F_MULTIPLE_FIXED 0x1
|
||||
#define OZ_DATA_F_MULTIPLE_VAR 0x2
|
||||
#define OZ_DATA_F_ISOC_FIXED 0x3
|
||||
#define OZ_DATA_F_ISOC_VAR 0x4
|
||||
#define OZ_DATA_F_FRAGMENTED 0x5
|
||||
#define OZ_DATA_F_ISOC_LARGE 0x7
|
||||
|
||||
#endif /* _OZPROTOCOL_H */
|
|
@ -1,54 +0,0 @@
|
|||
/* -----------------------------------------------------------------------------
|
||||
* Copyright (c) 2011 Ozmo Inc
|
||||
* Released under the GNU General Public License Version 2 (GPLv2).
|
||||
* -----------------------------------------------------------------------------
|
||||
*/
|
||||
#include <linux/usb.h>
|
||||
#include "ozdbg.h"
|
||||
|
||||
#ifdef WANT_URB_PARANOIA
|
||||
|
||||
#include "ozurbparanoia.h"
|
||||
|
||||
#define OZ_MAX_URBS 1000
|
||||
struct urb *g_urb_memory[OZ_MAX_URBS];
|
||||
int g_nb_urbs;
|
||||
DEFINE_SPINLOCK(g_urb_mem_lock);
|
||||
|
||||
void oz_remember_urb(struct urb *urb)
|
||||
{
|
||||
unsigned long irq_state;
|
||||
|
||||
spin_lock_irqsave(&g_urb_mem_lock, irq_state);
|
||||
if (g_nb_urbs < OZ_MAX_URBS) {
|
||||
g_urb_memory[g_nb_urbs++] = urb;
|
||||
oz_dbg(ON, "urb up = %d %p\n", g_nb_urbs, urb);
|
||||
} else {
|
||||
oz_dbg(ON, "ERROR urb buffer full\n");
|
||||
}
|
||||
spin_unlock_irqrestore(&g_urb_mem_lock, irq_state);
|
||||
}
|
||||
|
||||
/*
|
||||
*/
|
||||
int oz_forget_urb(struct urb *urb)
|
||||
{
|
||||
unsigned long irq_state;
|
||||
int i;
|
||||
int rc = -1;
|
||||
|
||||
spin_lock_irqsave(&g_urb_mem_lock, irq_state);
|
||||
for (i = 0; i < g_nb_urbs; i++) {
|
||||
if (g_urb_memory[i] == urb) {
|
||||
rc = 0;
|
||||
if (--g_nb_urbs > i)
|
||||
memcpy(&g_urb_memory[i], &g_urb_memory[i+1],
|
||||
(g_nb_urbs - i) * sizeof(struct urb *));
|
||||
oz_dbg(ON, "urb down = %d %p\n", g_nb_urbs, urb);
|
||||
}
|
||||
}
|
||||
spin_unlock_irqrestore(&g_urb_mem_lock, irq_state);
|
||||
return rc;
|
||||
}
|
||||
#endif /* #ifdef WANT_URB_PARANOIA */
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
#ifndef _OZURBPARANOIA_H
|
||||
#define _OZURBPARANOIA_H
|
||||
/* -----------------------------------------------------------------------------
|
||||
* Released under the GNU General Public License Version 2 (GPLv2).
|
||||
* Copyright (c) 2011 Ozmo Inc
|
||||
* -----------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#ifdef WANT_URB_PARANOIA
|
||||
void oz_remember_urb(struct urb *urb);
|
||||
int oz_forget_urb(struct urb *urb);
|
||||
#else
|
||||
static inline void oz_remember_urb(struct urb *urb) {}
|
||||
static inline int oz_forget_urb(struct urb *urb) { return 0; }
|
||||
#endif /* WANT_URB_PARANOIA */
|
||||
|
||||
|
||||
#endif /* _OZURBPARANOIA_H */
|
||||
|
|
@ -1,43 +0,0 @@
|
|||
/* -----------------------------------------------------------------------------
|
||||
* Copyright (c) 2011 Ozmo Inc
|
||||
* Released under the GNU General Public License Version 2 (GPLv2).
|
||||
* -----------------------------------------------------------------------------
|
||||
*/
|
||||
#ifndef _OZUSBIF_H
|
||||
#define _OZUSBIF_H
|
||||
|
||||
#include <linux/usb.h>
|
||||
|
||||
/* Reference counting functions.
|
||||
*/
|
||||
void oz_usb_get(void *hpd);
|
||||
void oz_usb_put(void *hpd);
|
||||
|
||||
/* Stream functions.
|
||||
*/
|
||||
int oz_usb_stream_create(void *hpd, u8 ep_num);
|
||||
int oz_usb_stream_delete(void *hpd, u8 ep_num);
|
||||
|
||||
/* Request functions.
|
||||
*/
|
||||
int oz_usb_control_req(void *hpd, u8 req_id, struct usb_ctrlrequest *setup,
|
||||
const u8 *data, int data_len);
|
||||
int oz_usb_get_desc_req(void *hpd, u8 req_id, u8 req_type, u8 desc_type,
|
||||
u8 index, __le16 windex, int offset, int len);
|
||||
int oz_usb_send_isoc(void *hpd, u8 ep_num, struct urb *urb);
|
||||
void oz_usb_request_heartbeat(void *hpd);
|
||||
|
||||
/* Confirmation functions.
|
||||
*/
|
||||
void oz_hcd_get_desc_cnf(void *hport, u8 req_id, u8 status,
|
||||
const u8 *desc, u8 length, u16 offset, u16 total_size);
|
||||
void oz_hcd_control_cnf(void *hport, u8 req_id, u8 rcode,
|
||||
const u8 *data, int data_len);
|
||||
|
||||
/* Indication functions.
|
||||
*/
|
||||
void oz_hcd_data_ind(void *hport, u8 endpoint, const u8 *data, int data_len);
|
||||
|
||||
int oz_hcd_heartbeat(void *hport);
|
||||
|
||||
#endif /* _OZUSBIF_H */
|
|
@ -1,263 +0,0 @@
|
|||
/* -----------------------------------------------------------------------------
|
||||
* Copyright (c) 2011 Ozmo Inc
|
||||
* Released under the GNU General Public License Version 2 (GPLv2).
|
||||
*
|
||||
* This file provides protocol independent part of the implementation of the USB
|
||||
* service for a PD.
|
||||
* The implementation of this service is split into two parts the first of which
|
||||
* is protocol independent and the second contains protocol specific details.
|
||||
* This split is to allow alternative protocols to be defined.
|
||||
* The implementation of this service uses ozhcd.c to implement a USB HCD.
|
||||
* -----------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/input.h>
|
||||
#include <asm/unaligned.h>
|
||||
#include "ozdbg.h"
|
||||
#include "ozprotocol.h"
|
||||
#include "ozeltbuf.h"
|
||||
#include "ozpd.h"
|
||||
#include "ozproto.h"
|
||||
#include "ozusbif.h"
|
||||
#include "ozhcd.h"
|
||||
#include "ozusbsvc.h"
|
||||
|
||||
/*
|
||||
* This is called once when the driver is loaded to initialise the USB service.
|
||||
* Context: process
|
||||
*/
|
||||
int oz_usb_init(void)
|
||||
{
|
||||
return oz_hcd_init();
|
||||
}
|
||||
|
||||
/*
|
||||
* This is called once when the driver is unloaded to terminate the USB service.
|
||||
* Context: process
|
||||
*/
|
||||
void oz_usb_term(void)
|
||||
{
|
||||
oz_hcd_term();
|
||||
}
|
||||
|
||||
/*
|
||||
* This is called when the USB service is started or resumed for a PD.
|
||||
* Context: softirq
|
||||
*/
|
||||
int oz_usb_start(struct oz_pd *pd, int resume)
|
||||
{
|
||||
int rc = 0;
|
||||
struct oz_usb_ctx *usb_ctx;
|
||||
struct oz_usb_ctx *old_ctx;
|
||||
|
||||
if (resume) {
|
||||
oz_dbg(ON, "USB service resumed\n");
|
||||
return 0;
|
||||
}
|
||||
oz_dbg(ON, "USB service started\n");
|
||||
/* Create a USB context in case we need one. If we find the PD already
|
||||
* has a USB context then we will destroy it.
|
||||
*/
|
||||
usb_ctx = kzalloc(sizeof(struct oz_usb_ctx), GFP_ATOMIC);
|
||||
if (usb_ctx == NULL)
|
||||
return -ENOMEM;
|
||||
atomic_set(&usb_ctx->ref_count, 1);
|
||||
usb_ctx->pd = pd;
|
||||
usb_ctx->stopped = 0;
|
||||
/* Install the USB context if the PD doesn't already have one.
|
||||
* If it does already have one then destroy the one we have just
|
||||
* created.
|
||||
*/
|
||||
spin_lock_bh(&pd->app_lock[OZ_APPID_USB]);
|
||||
old_ctx = pd->app_ctx[OZ_APPID_USB];
|
||||
if (old_ctx == NULL)
|
||||
pd->app_ctx[OZ_APPID_USB] = usb_ctx;
|
||||
oz_usb_get(pd->app_ctx[OZ_APPID_USB]);
|
||||
spin_unlock_bh(&pd->app_lock[OZ_APPID_USB]);
|
||||
if (old_ctx) {
|
||||
oz_dbg(ON, "Already have USB context\n");
|
||||
kfree(usb_ctx);
|
||||
usb_ctx = old_ctx;
|
||||
} else if (usb_ctx) {
|
||||
/* Take a reference to the PD. This will be released when
|
||||
* the USB context is destroyed.
|
||||
*/
|
||||
oz_pd_get(pd);
|
||||
}
|
||||
/* If we already had a USB context and had obtained a port from
|
||||
* the USB HCD then just reset the port. If we didn't have a port
|
||||
* then report the arrival to the USB HCD so we get one.
|
||||
*/
|
||||
if (usb_ctx->hport) {
|
||||
oz_hcd_pd_reset(usb_ctx, usb_ctx->hport);
|
||||
} else {
|
||||
usb_ctx->hport = oz_hcd_pd_arrived(usb_ctx);
|
||||
if (usb_ctx->hport == NULL) {
|
||||
oz_dbg(ON, "USB hub returned null port\n");
|
||||
spin_lock_bh(&pd->app_lock[OZ_APPID_USB]);
|
||||
pd->app_ctx[OZ_APPID_USB] = NULL;
|
||||
spin_unlock_bh(&pd->app_lock[OZ_APPID_USB]);
|
||||
oz_usb_put(usb_ctx);
|
||||
rc = -1;
|
||||
}
|
||||
}
|
||||
oz_usb_put(usb_ctx);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
* This is called when the USB service is stopped or paused for a PD.
|
||||
* Context: softirq or process
|
||||
*/
|
||||
void oz_usb_stop(struct oz_pd *pd, int pause)
|
||||
{
|
||||
struct oz_usb_ctx *usb_ctx;
|
||||
|
||||
if (pause) {
|
||||
oz_dbg(ON, "USB service paused\n");
|
||||
return;
|
||||
}
|
||||
spin_lock_bh(&pd->app_lock[OZ_APPID_USB]);
|
||||
usb_ctx = (struct oz_usb_ctx *) pd->app_ctx[OZ_APPID_USB];
|
||||
pd->app_ctx[OZ_APPID_USB] = NULL;
|
||||
spin_unlock_bh(&pd->app_lock[OZ_APPID_USB]);
|
||||
if (usb_ctx) {
|
||||
struct timespec ts, now;
|
||||
|
||||
getnstimeofday(&ts);
|
||||
oz_dbg(ON, "USB service stopping...\n");
|
||||
usb_ctx->stopped = 1;
|
||||
/* At this point the reference count on the usb context should
|
||||
* be 2 - one from when we created it and one from the hcd
|
||||
* which claims a reference. Since stopped = 1 no one else
|
||||
* should get in but someone may already be in. So wait
|
||||
* until they leave but timeout after 1 second.
|
||||
*/
|
||||
while ((atomic_read(&usb_ctx->ref_count) > 2)) {
|
||||
getnstimeofday(&now);
|
||||
/*Approx 1 Sec. this is not perfect calculation*/
|
||||
if (now.tv_sec != ts.tv_sec)
|
||||
break;
|
||||
}
|
||||
oz_dbg(ON, "USB service stopped\n");
|
||||
oz_hcd_pd_departed(usb_ctx->hport);
|
||||
/* Release the reference taken in oz_usb_start.
|
||||
*/
|
||||
oz_usb_put(usb_ctx);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* This increments the reference count of the context area for a specific PD.
|
||||
* This ensures this context area does not disappear while still in use.
|
||||
* Context: softirq
|
||||
*/
|
||||
void oz_usb_get(void *hpd)
|
||||
{
|
||||
struct oz_usb_ctx *usb_ctx = (struct oz_usb_ctx *)hpd;
|
||||
|
||||
atomic_inc(&usb_ctx->ref_count);
|
||||
}
|
||||
|
||||
/*
|
||||
* This decrements the reference count of the context area for a specific PD
|
||||
* and destroys the context area if the reference count becomes zero.
|
||||
* Context: irq or process
|
||||
*/
|
||||
void oz_usb_put(void *hpd)
|
||||
{
|
||||
struct oz_usb_ctx *usb_ctx = (struct oz_usb_ctx *)hpd;
|
||||
|
||||
if (atomic_dec_and_test(&usb_ctx->ref_count)) {
|
||||
oz_dbg(ON, "Dealloc USB context\n");
|
||||
oz_pd_put(usb_ctx->pd);
|
||||
kfree(usb_ctx);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Context: softirq
|
||||
*/
|
||||
int oz_usb_heartbeat(struct oz_pd *pd)
|
||||
{
|
||||
struct oz_usb_ctx *usb_ctx;
|
||||
int rc = 0;
|
||||
|
||||
spin_lock_bh(&pd->app_lock[OZ_APPID_USB]);
|
||||
usb_ctx = (struct oz_usb_ctx *) pd->app_ctx[OZ_APPID_USB];
|
||||
if (usb_ctx)
|
||||
oz_usb_get(usb_ctx);
|
||||
spin_unlock_bh(&pd->app_lock[OZ_APPID_USB]);
|
||||
if (usb_ctx == NULL)
|
||||
return rc;
|
||||
if (usb_ctx->stopped)
|
||||
goto done;
|
||||
if (usb_ctx->hport)
|
||||
if (oz_hcd_heartbeat(usb_ctx->hport))
|
||||
rc = 1;
|
||||
done:
|
||||
oz_usb_put(usb_ctx);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
* Context: softirq
|
||||
*/
|
||||
int oz_usb_stream_create(void *hpd, u8 ep_num)
|
||||
{
|
||||
struct oz_usb_ctx *usb_ctx = (struct oz_usb_ctx *)hpd;
|
||||
struct oz_pd *pd = usb_ctx->pd;
|
||||
|
||||
oz_dbg(ON, "%s: (0x%x)\n", __func__, ep_num);
|
||||
if (pd->mode & OZ_F_ISOC_NO_ELTS) {
|
||||
oz_isoc_stream_create(pd, ep_num);
|
||||
} else {
|
||||
oz_pd_get(pd);
|
||||
if (oz_elt_stream_create(&pd->elt_buff, ep_num,
|
||||
4*pd->max_tx_size)) {
|
||||
oz_pd_put(pd);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Context: softirq
|
||||
*/
|
||||
int oz_usb_stream_delete(void *hpd, u8 ep_num)
|
||||
{
|
||||
struct oz_usb_ctx *usb_ctx = (struct oz_usb_ctx *)hpd;
|
||||
|
||||
if (usb_ctx) {
|
||||
struct oz_pd *pd = usb_ctx->pd;
|
||||
|
||||
if (pd) {
|
||||
oz_dbg(ON, "%s: (0x%x)\n", __func__, ep_num);
|
||||
if (pd->mode & OZ_F_ISOC_NO_ELTS) {
|
||||
oz_isoc_stream_delete(pd, ep_num);
|
||||
} else {
|
||||
if (oz_elt_stream_delete(&pd->elt_buff, ep_num))
|
||||
return -1;
|
||||
oz_pd_put(pd);
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Context: softirq or process
|
||||
*/
|
||||
void oz_usb_request_heartbeat(void *hpd)
|
||||
{
|
||||
struct oz_usb_ctx *usb_ctx = (struct oz_usb_ctx *)hpd;
|
||||
|
||||
if (usb_ctx && usb_ctx->pd)
|
||||
oz_pd_request_heartbeat(usb_ctx->pd);
|
||||
}
|
|
@ -1,32 +0,0 @@
|
|||
/* -----------------------------------------------------------------------------
|
||||
* Copyright (c) 2011 Ozmo Inc
|
||||
* Released under the GNU General Public License Version 2 (GPLv2).
|
||||
* -----------------------------------------------------------------------------
|
||||
*/
|
||||
#ifndef _OZUSBSVC_H
|
||||
#define _OZUSBSVC_H
|
||||
|
||||
/*------------------------------------------------------------------------------
|
||||
* Per PD context info stored in application context area of PD.
|
||||
* This object is reference counted to ensure it doesn't disappear while
|
||||
* still in use.
|
||||
*/
|
||||
struct oz_usb_ctx {
|
||||
atomic_t ref_count;
|
||||
u8 tx_seq_num;
|
||||
u8 rx_seq_num;
|
||||
struct oz_pd *pd;
|
||||
void *hport;
|
||||
int stopped;
|
||||
};
|
||||
|
||||
int oz_usb_init(void);
|
||||
void oz_usb_term(void);
|
||||
int oz_usb_start(struct oz_pd *pd, int resume);
|
||||
void oz_usb_stop(struct oz_pd *pd, int pause);
|
||||
void oz_usb_rx(struct oz_pd *pd, struct oz_elt *elt);
|
||||
int oz_usb_heartbeat(struct oz_pd *pd);
|
||||
void oz_usb_farewell(struct oz_pd *pd, u8 ep_num, u8 *data, u8 len);
|
||||
|
||||
#endif /* _OZUSBSVC_H */
|
||||
|
|
@ -1,472 +0,0 @@
|
|||
/* -----------------------------------------------------------------------------
|
||||
* Copyright (c) 2011 Ozmo Inc
|
||||
* Released under the GNU General Public License Version 2 (GPLv2).
|
||||
*
|
||||
* This file implements the protocol specific parts of the USB service for a PD.
|
||||
* -----------------------------------------------------------------------------
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/input.h>
|
||||
#include <asm/unaligned.h>
|
||||
#include "ozdbg.h"
|
||||
#include "ozprotocol.h"
|
||||
#include "ozeltbuf.h"
|
||||
#include "ozpd.h"
|
||||
#include "ozproto.h"
|
||||
#include "ozusbif.h"
|
||||
#include "ozhcd.h"
|
||||
#include "ozusbsvc.h"
|
||||
|
||||
#define MAX_ISOC_FIXED_DATA (253-sizeof(struct oz_isoc_fixed))
|
||||
|
||||
/*
|
||||
* Context: softirq
|
||||
*/
|
||||
static int oz_usb_submit_elt(struct oz_elt_buf *eb, struct oz_elt_info *ei,
|
||||
struct oz_usb_ctx *usb_ctx, u8 strid, u8 isoc)
|
||||
{
|
||||
int ret;
|
||||
struct oz_elt *elt = (struct oz_elt *)ei->data;
|
||||
struct oz_app_hdr *app_hdr = (struct oz_app_hdr *)(elt+1);
|
||||
|
||||
elt->type = OZ_ELT_APP_DATA;
|
||||
ei->app_id = OZ_APPID_USB;
|
||||
ei->length = elt->length + sizeof(struct oz_elt);
|
||||
app_hdr->app_id = OZ_APPID_USB;
|
||||
spin_lock_bh(&eb->lock);
|
||||
if (isoc == 0) {
|
||||
app_hdr->elt_seq_num = usb_ctx->tx_seq_num++;
|
||||
if (usb_ctx->tx_seq_num == 0)
|
||||
usb_ctx->tx_seq_num = 1;
|
||||
}
|
||||
ret = oz_queue_elt_info(eb, isoc, strid, ei);
|
||||
if (ret)
|
||||
oz_elt_info_free(eb, ei);
|
||||
spin_unlock_bh(&eb->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Context: softirq
|
||||
*/
|
||||
int oz_usb_get_desc_req(void *hpd, u8 req_id, u8 req_type, u8 desc_type,
|
||||
u8 index, __le16 windex, int offset, int len)
|
||||
{
|
||||
struct oz_usb_ctx *usb_ctx = hpd;
|
||||
struct oz_pd *pd = usb_ctx->pd;
|
||||
struct oz_elt *elt;
|
||||
struct oz_get_desc_req *body;
|
||||
struct oz_elt_buf *eb = &pd->elt_buff;
|
||||
struct oz_elt_info *ei = oz_elt_info_alloc(&pd->elt_buff);
|
||||
|
||||
oz_dbg(ON, " req_type = 0x%x\n", req_type);
|
||||
oz_dbg(ON, " desc_type = 0x%x\n", desc_type);
|
||||
oz_dbg(ON, " index = 0x%x\n", index);
|
||||
oz_dbg(ON, " windex = 0x%x\n", windex);
|
||||
oz_dbg(ON, " offset = 0x%x\n", offset);
|
||||
oz_dbg(ON, " len = 0x%x\n", len);
|
||||
if (len > 200)
|
||||
len = 200;
|
||||
if (ei == NULL)
|
||||
return -1;
|
||||
elt = (struct oz_elt *)ei->data;
|
||||
elt->length = sizeof(struct oz_get_desc_req);
|
||||
body = (struct oz_get_desc_req *)(elt+1);
|
||||
body->type = OZ_GET_DESC_REQ;
|
||||
body->req_id = req_id;
|
||||
put_unaligned(cpu_to_le16(offset), &body->offset);
|
||||
put_unaligned(cpu_to_le16(len), &body->size);
|
||||
body->req_type = req_type;
|
||||
body->desc_type = desc_type;
|
||||
body->w_index = windex;
|
||||
body->index = index;
|
||||
return oz_usb_submit_elt(eb, ei, usb_ctx, 0, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Context: tasklet
|
||||
*/
|
||||
static int oz_usb_set_config_req(void *hpd, u8 req_id, u8 index)
|
||||
{
|
||||
struct oz_usb_ctx *usb_ctx = hpd;
|
||||
struct oz_pd *pd = usb_ctx->pd;
|
||||
struct oz_elt *elt;
|
||||
struct oz_elt_buf *eb = &pd->elt_buff;
|
||||
struct oz_elt_info *ei = oz_elt_info_alloc(&pd->elt_buff);
|
||||
struct oz_set_config_req *body;
|
||||
|
||||
if (ei == NULL)
|
||||
return -1;
|
||||
elt = (struct oz_elt *)ei->data;
|
||||
elt->length = sizeof(struct oz_set_config_req);
|
||||
body = (struct oz_set_config_req *)(elt+1);
|
||||
body->type = OZ_SET_CONFIG_REQ;
|
||||
body->req_id = req_id;
|
||||
body->index = index;
|
||||
return oz_usb_submit_elt(eb, ei, usb_ctx, 0, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Context: tasklet
|
||||
*/
|
||||
static int oz_usb_set_interface_req(void *hpd, u8 req_id, u8 index, u8 alt)
|
||||
{
|
||||
struct oz_usb_ctx *usb_ctx = hpd;
|
||||
struct oz_pd *pd = usb_ctx->pd;
|
||||
struct oz_elt *elt;
|
||||
struct oz_elt_buf *eb = &pd->elt_buff;
|
||||
struct oz_elt_info *ei = oz_elt_info_alloc(&pd->elt_buff);
|
||||
struct oz_set_interface_req *body;
|
||||
|
||||
if (ei == NULL)
|
||||
return -1;
|
||||
elt = (struct oz_elt *)ei->data;
|
||||
elt->length = sizeof(struct oz_set_interface_req);
|
||||
body = (struct oz_set_interface_req *)(elt+1);
|
||||
body->type = OZ_SET_INTERFACE_REQ;
|
||||
body->req_id = req_id;
|
||||
body->index = index;
|
||||
body->alternative = alt;
|
||||
return oz_usb_submit_elt(eb, ei, usb_ctx, 0, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Context: tasklet
|
||||
*/
|
||||
static int oz_usb_set_clear_feature_req(void *hpd, u8 req_id, u8 type,
|
||||
u8 recipient, u8 index, __le16 feature)
|
||||
{
|
||||
struct oz_usb_ctx *usb_ctx = hpd;
|
||||
struct oz_pd *pd = usb_ctx->pd;
|
||||
struct oz_elt *elt;
|
||||
struct oz_elt_buf *eb = &pd->elt_buff;
|
||||
struct oz_elt_info *ei = oz_elt_info_alloc(&pd->elt_buff);
|
||||
struct oz_feature_req *body;
|
||||
|
||||
if (ei == NULL)
|
||||
return -1;
|
||||
elt = (struct oz_elt *)ei->data;
|
||||
elt->length = sizeof(struct oz_feature_req);
|
||||
body = (struct oz_feature_req *)(elt+1);
|
||||
body->type = type;
|
||||
body->req_id = req_id;
|
||||
body->recipient = recipient;
|
||||
body->index = index;
|
||||
put_unaligned(feature, &body->feature);
|
||||
return oz_usb_submit_elt(eb, ei, usb_ctx, 0, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Context: tasklet
|
||||
*/
|
||||
static int oz_usb_vendor_class_req(void *hpd, u8 req_id, u8 req_type,
|
||||
u8 request, __le16 value, __le16 index, const u8 *data, int data_len)
|
||||
{
|
||||
struct oz_usb_ctx *usb_ctx = hpd;
|
||||
struct oz_pd *pd = usb_ctx->pd;
|
||||
struct oz_elt *elt;
|
||||
struct oz_elt_buf *eb = &pd->elt_buff;
|
||||
struct oz_elt_info *ei = oz_elt_info_alloc(&pd->elt_buff);
|
||||
struct oz_vendor_class_req *body;
|
||||
|
||||
if (ei == NULL)
|
||||
return -1;
|
||||
elt = (struct oz_elt *)ei->data;
|
||||
elt->length = sizeof(struct oz_vendor_class_req) - 1 + data_len;
|
||||
body = (struct oz_vendor_class_req *)(elt+1);
|
||||
body->type = OZ_VENDOR_CLASS_REQ;
|
||||
body->req_id = req_id;
|
||||
body->req_type = req_type;
|
||||
body->request = request;
|
||||
put_unaligned(value, &body->value);
|
||||
put_unaligned(index, &body->index);
|
||||
if (data_len)
|
||||
memcpy(body->data, data, data_len);
|
||||
return oz_usb_submit_elt(eb, ei, usb_ctx, 0, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Context: tasklet
|
||||
*/
|
||||
int oz_usb_control_req(void *hpd, u8 req_id, struct usb_ctrlrequest *setup,
|
||||
const u8 *data, int data_len)
|
||||
{
|
||||
unsigned wvalue = le16_to_cpu(setup->wValue);
|
||||
unsigned windex = le16_to_cpu(setup->wIndex);
|
||||
unsigned wlength = le16_to_cpu(setup->wLength);
|
||||
int rc = 0;
|
||||
|
||||
if ((setup->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) {
|
||||
switch (setup->bRequest) {
|
||||
case USB_REQ_GET_DESCRIPTOR:
|
||||
rc = oz_usb_get_desc_req(hpd, req_id,
|
||||
setup->bRequestType, (u8)(wvalue>>8),
|
||||
(u8)wvalue, setup->wIndex, 0, wlength);
|
||||
break;
|
||||
case USB_REQ_SET_CONFIGURATION:
|
||||
rc = oz_usb_set_config_req(hpd, req_id, (u8)wvalue);
|
||||
break;
|
||||
case USB_REQ_SET_INTERFACE: {
|
||||
u8 if_num = (u8)windex;
|
||||
u8 alt = (u8)wvalue;
|
||||
|
||||
rc = oz_usb_set_interface_req(hpd, req_id,
|
||||
if_num, alt);
|
||||
}
|
||||
break;
|
||||
case USB_REQ_SET_FEATURE:
|
||||
rc = oz_usb_set_clear_feature_req(hpd, req_id,
|
||||
OZ_SET_FEATURE_REQ,
|
||||
setup->bRequestType & 0xf, (u8)windex,
|
||||
setup->wValue);
|
||||
break;
|
||||
case USB_REQ_CLEAR_FEATURE:
|
||||
rc = oz_usb_set_clear_feature_req(hpd, req_id,
|
||||
OZ_CLEAR_FEATURE_REQ,
|
||||
setup->bRequestType & 0xf,
|
||||
(u8)windex, setup->wValue);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
rc = oz_usb_vendor_class_req(hpd, req_id, setup->bRequestType,
|
||||
setup->bRequest, setup->wValue, setup->wIndex,
|
||||
data, data_len);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
* Context: softirq
|
||||
*/
|
||||
int oz_usb_send_isoc(void *hpd, u8 ep_num, struct urb *urb)
|
||||
{
|
||||
struct oz_usb_ctx *usb_ctx = hpd;
|
||||
struct oz_pd *pd = usb_ctx->pd;
|
||||
struct oz_elt_buf *eb;
|
||||
int i;
|
||||
int hdr_size;
|
||||
u8 *data;
|
||||
struct usb_iso_packet_descriptor *desc;
|
||||
|
||||
if (pd->mode & OZ_F_ISOC_NO_ELTS) {
|
||||
for (i = 0; i < urb->number_of_packets; i++) {
|
||||
u8 *data;
|
||||
|
||||
desc = &urb->iso_frame_desc[i];
|
||||
data = ((u8 *)urb->transfer_buffer)+desc->offset;
|
||||
oz_send_isoc_unit(pd, ep_num, data, desc->length);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
hdr_size = sizeof(struct oz_isoc_fixed) - 1;
|
||||
eb = &pd->elt_buff;
|
||||
i = 0;
|
||||
while (i < urb->number_of_packets) {
|
||||
struct oz_elt_info *ei = oz_elt_info_alloc(eb);
|
||||
struct oz_elt *elt;
|
||||
struct oz_isoc_fixed *body;
|
||||
int unit_count;
|
||||
int unit_size;
|
||||
int rem;
|
||||
|
||||
if (ei == NULL)
|
||||
return -1;
|
||||
rem = MAX_ISOC_FIXED_DATA;
|
||||
elt = (struct oz_elt *)ei->data;
|
||||
body = (struct oz_isoc_fixed *)(elt + 1);
|
||||
body->type = OZ_USB_ENDPOINT_DATA;
|
||||
body->endpoint = ep_num;
|
||||
body->format = OZ_DATA_F_ISOC_FIXED;
|
||||
unit_size = urb->iso_frame_desc[i].length;
|
||||
body->unit_size = (u8)unit_size;
|
||||
data = ((u8 *)(elt+1)) + hdr_size;
|
||||
unit_count = 0;
|
||||
while (i < urb->number_of_packets) {
|
||||
desc = &urb->iso_frame_desc[i];
|
||||
if ((unit_size == desc->length) &&
|
||||
(desc->length <= rem)) {
|
||||
memcpy(data, ((u8 *)urb->transfer_buffer) +
|
||||
desc->offset, unit_size);
|
||||
data += unit_size;
|
||||
rem -= unit_size;
|
||||
unit_count++;
|
||||
desc->status = 0;
|
||||
desc->actual_length = desc->length;
|
||||
i++;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
elt->length = hdr_size + MAX_ISOC_FIXED_DATA - rem;
|
||||
/* Store the number of units in body->frame_number for the
|
||||
* moment. This field will be correctly determined before
|
||||
* the element is sent. */
|
||||
body->frame_number = (u8)unit_count;
|
||||
oz_usb_submit_elt(eb, ei, usb_ctx, ep_num,
|
||||
pd->mode & OZ_F_ISOC_ANYTIME);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Context: softirq-serialized
|
||||
*/
|
||||
static void oz_usb_handle_ep_data(struct oz_usb_ctx *usb_ctx,
|
||||
struct oz_usb_hdr *usb_hdr, int len)
|
||||
{
|
||||
struct oz_data *data_hdr = (struct oz_data *)usb_hdr;
|
||||
|
||||
switch (data_hdr->format) {
|
||||
case OZ_DATA_F_MULTIPLE_FIXED: {
|
||||
struct oz_multiple_fixed *body =
|
||||
(struct oz_multiple_fixed *)data_hdr;
|
||||
u8 *data = body->data;
|
||||
unsigned int n;
|
||||
|
||||
if (!body->unit_size ||
|
||||
len < sizeof(struct oz_multiple_fixed) - 1)
|
||||
break;
|
||||
n = (len - (sizeof(struct oz_multiple_fixed) - 1))
|
||||
/ body->unit_size;
|
||||
while (n--) {
|
||||
oz_hcd_data_ind(usb_ctx->hport, body->endpoint,
|
||||
data, body->unit_size);
|
||||
data += body->unit_size;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case OZ_DATA_F_ISOC_FIXED: {
|
||||
struct oz_isoc_fixed *body =
|
||||
(struct oz_isoc_fixed *)data_hdr;
|
||||
int data_len;
|
||||
int unit_size = body->unit_size;
|
||||
u8 *data = body->data;
|
||||
int count;
|
||||
int i;
|
||||
|
||||
if (len < sizeof(struct oz_isoc_fixed) - 1)
|
||||
break;
|
||||
data_len = len - (sizeof(struct oz_isoc_fixed) - 1);
|
||||
|
||||
if (!unit_size)
|
||||
break;
|
||||
count = data_len/unit_size;
|
||||
for (i = 0; i < count; i++) {
|
||||
oz_hcd_data_ind(usb_ctx->hport,
|
||||
body->endpoint, data, unit_size);
|
||||
data += unit_size;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* This is called when the PD has received a USB element. The type of element
|
||||
* is determined and is then passed to an appropriate handler function.
|
||||
* Context: softirq-serialized
|
||||
*/
|
||||
void oz_usb_rx(struct oz_pd *pd, struct oz_elt *elt)
|
||||
{
|
||||
struct oz_usb_hdr *usb_hdr = (struct oz_usb_hdr *)(elt + 1);
|
||||
struct oz_usb_ctx *usb_ctx;
|
||||
|
||||
spin_lock_bh(&pd->app_lock[OZ_APPID_USB]);
|
||||
usb_ctx = (struct oz_usb_ctx *)pd->app_ctx[OZ_APPID_USB];
|
||||
if (usb_ctx)
|
||||
oz_usb_get(usb_ctx);
|
||||
spin_unlock_bh(&pd->app_lock[OZ_APPID_USB]);
|
||||
if (usb_ctx == NULL)
|
||||
return; /* Context has gone so nothing to do. */
|
||||
if (usb_ctx->stopped)
|
||||
goto done;
|
||||
/* If sequence number is non-zero then check it is not a duplicate.
|
||||
* Zero sequence numbers are always accepted.
|
||||
*/
|
||||
if (usb_hdr->elt_seq_num != 0) {
|
||||
if (((usb_ctx->rx_seq_num - usb_hdr->elt_seq_num) & 0x80) == 0)
|
||||
/* Reject duplicate element. */
|
||||
goto done;
|
||||
}
|
||||
usb_ctx->rx_seq_num = usb_hdr->elt_seq_num;
|
||||
switch (usb_hdr->type) {
|
||||
case OZ_GET_DESC_RSP: {
|
||||
struct oz_get_desc_rsp *body =
|
||||
(struct oz_get_desc_rsp *)usb_hdr;
|
||||
u16 offs, total_size;
|
||||
u8 data_len;
|
||||
|
||||
if (elt->length < sizeof(struct oz_get_desc_rsp) - 1)
|
||||
break;
|
||||
data_len = elt->length -
|
||||
(sizeof(struct oz_get_desc_rsp) - 1);
|
||||
offs = le16_to_cpu(get_unaligned(&body->offset));
|
||||
total_size =
|
||||
le16_to_cpu(get_unaligned(&body->total_size));
|
||||
oz_dbg(ON, "USB_REQ_GET_DESCRIPTOR - cnf\n");
|
||||
oz_hcd_get_desc_cnf(usb_ctx->hport, body->req_id,
|
||||
body->rcode, body->data,
|
||||
data_len, offs, total_size);
|
||||
}
|
||||
break;
|
||||
case OZ_SET_CONFIG_RSP: {
|
||||
struct oz_set_config_rsp *body =
|
||||
(struct oz_set_config_rsp *)usb_hdr;
|
||||
oz_hcd_control_cnf(usb_ctx->hport, body->req_id,
|
||||
body->rcode, NULL, 0);
|
||||
}
|
||||
break;
|
||||
case OZ_SET_INTERFACE_RSP: {
|
||||
struct oz_set_interface_rsp *body =
|
||||
(struct oz_set_interface_rsp *)usb_hdr;
|
||||
oz_hcd_control_cnf(usb_ctx->hport,
|
||||
body->req_id, body->rcode, NULL, 0);
|
||||
}
|
||||
break;
|
||||
case OZ_VENDOR_CLASS_RSP: {
|
||||
struct oz_vendor_class_rsp *body =
|
||||
(struct oz_vendor_class_rsp *)usb_hdr;
|
||||
|
||||
if (elt->length <
|
||||
sizeof(struct oz_vendor_class_rsp) - 1)
|
||||
break;
|
||||
|
||||
oz_hcd_control_cnf(usb_ctx->hport, body->req_id,
|
||||
body->rcode, body->data, elt->length-
|
||||
sizeof(struct oz_vendor_class_rsp)+1);
|
||||
}
|
||||
break;
|
||||
case OZ_USB_ENDPOINT_DATA:
|
||||
oz_usb_handle_ep_data(usb_ctx, usb_hdr, elt->length);
|
||||
break;
|
||||
}
|
||||
done:
|
||||
oz_usb_put(usb_ctx);
|
||||
}
|
||||
|
||||
/*
|
||||
* Context: softirq, process
|
||||
*/
|
||||
void oz_usb_farewell(struct oz_pd *pd, u8 ep_num, u8 *data, u8 len)
|
||||
{
|
||||
struct oz_usb_ctx *usb_ctx;
|
||||
|
||||
spin_lock_bh(&pd->app_lock[OZ_APPID_USB]);
|
||||
usb_ctx = (struct oz_usb_ctx *)pd->app_ctx[OZ_APPID_USB];
|
||||
if (usb_ctx)
|
||||
oz_usb_get(usb_ctx);
|
||||
spin_unlock_bh(&pd->app_lock[OZ_APPID_USB]);
|
||||
if (usb_ctx == NULL)
|
||||
return; /* Context has gone so nothing to do. */
|
||||
if (!usb_ctx->stopped) {
|
||||
oz_dbg(ON, "Farewell indicated ep = 0x%x\n", ep_num);
|
||||
oz_hcd_data_ind(usb_ctx->hport, ep_num, data, len);
|
||||
}
|
||||
oz_usb_put(usb_ctx);
|
||||
}
|
Loading…
Reference in New Issue