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:
Jason A. Donenfeld 2015-08-10 17:49:51 +02:00 committed by Greg Kroah-Hartman
parent f70d631832
commit a73e99cb67
27 changed files with 0 additions and 6590 deletions

View File

@ -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

View File

@ -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"

View File

@ -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/

View File

@ -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".

View File

@ -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

View File

@ -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).

View File

@ -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>.

View File

@ -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 */

View File

@ -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);
}

View File

@ -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 */

View File

@ -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 */

View File

@ -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);
}

View File

@ -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

View File

@ -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 */

View File

@ -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");

View File

@ -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);
}
}

View File

@ -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 */

View File

@ -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(&current_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;
}

View File

@ -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 */

View File

@ -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 */

View File

@ -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 */

View File

@ -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 */

View File

@ -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 */

View File

@ -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);
}

View File

@ -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 */

View File

@ -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);
}