Commit b3147863 authored by Chris Kelly's avatar Chris Kelly Committed by Greg Kroah-Hartman

staging: ozwpan: Added USB service to protocol

The L2 protocol supports various services, one of which is USB.
This provides the implementation of that service and plumbs it to
the virtual USB HCD.
Signed-off-by: default avatarChris Kelly <ckelly@ozmodevices.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent ae926051
/* -----------------------------------------------------------------------------
* 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,
u8 *data, int data_len);
int oz_usb_get_desc_req(void *hpd, u8 req_id, u8 req_type, u8 desc_type,
u8 index, u16 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, int status,
u8 *desc, int length, int offset, int total_size);
void oz_hcd_control_cnf(void *hport, u8 req_id, u8 rcode,
u8 *data, int data_len);
/* Indication functions.
*/
void oz_hcd_data_ind(void *hport, u8 endpoint, u8 *data, int data_len);
int oz_hcd_heartbeat(void *hport);
#endif /* _OZUSBIF_H */
/* -----------------------------------------------------------------------------
* 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 implemenation of this service uses ozhcd.c to implement a USB HCD.
* -----------------------------------------------------------------------------
*/
#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/input.h>
#include <asm/unaligned.h>
#include "ozconfig.h"
#include "ozprotocol.h"
#include "ozeltbuf.h"
#include "ozpd.h"
#include "ozproto.h"
#include "ozusbif.h"
#include "ozhcd.h"
#include "oztrace.h"
#include "ozalloc.h"
#include "ozusbsvc.h"
#include "ozevent.h"
/*------------------------------------------------------------------------------
* This is called once when the driver is loaded to initialise the USB service.
* Context: process
*/
int oz_usb_init(void)
{
oz_event_log(OZ_EVT_SERVICE, 1, OZ_APPID_USB, 0, 0);
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_event_log(OZ_EVT_SERVICE, 2, OZ_APPID_USB, 0, 0);
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 = 0;
oz_event_log(OZ_EVT_SERVICE, 3, OZ_APPID_USB, 0, resume);
if (resume) {
oz_trace("USB service resumed.\n");
return 0;
}
oz_trace("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 = (struct oz_usb_ctx *)
oz_alloc(sizeof(struct oz_usb_ctx), GFP_ATOMIC);
if (usb_ctx == 0)
return -1;
memset(usb_ctx, 0, sizeof(struct oz_usb_ctx));
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-1]);
old_ctx = pd->app_ctx[OZ_APPID_USB-1];
if (old_ctx == 0)
pd->app_ctx[OZ_APPID_USB-1] = usb_ctx;
oz_usb_get(pd->app_ctx[OZ_APPID_USB-1]);
spin_unlock_bh(&pd->app_lock[OZ_APPID_USB-1]);
if (old_ctx) {
oz_trace("Already have USB context.\n");
oz_free(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 == 0) {
oz_trace("USB hub returned null port.\n");
spin_lock_bh(&pd->app_lock[OZ_APPID_USB-1]);
pd->app_ctx[OZ_APPID_USB-1] = 0;
spin_unlock_bh(&pd->app_lock[OZ_APPID_USB-1]);
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;
oz_event_log(OZ_EVT_SERVICE, 4, OZ_APPID_USB, 0, pause);
if (pause) {
oz_trace("USB service paused.\n");
return;
}
spin_lock_bh(&pd->app_lock[OZ_APPID_USB-1]);
usb_ctx = (struct oz_usb_ctx *)pd->app_ctx[OZ_APPID_USB-1];
pd->app_ctx[OZ_APPID_USB-1] = 0;
spin_unlock_bh(&pd->app_lock[OZ_APPID_USB-1]);
if (usb_ctx) {
unsigned long tout = jiffies + HZ;
oz_trace("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) &&
time_before(jiffies, tout))
;
oz_trace("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: softirq 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_trace("Dealloc USB context.\n");
oz_pd_put(usb_ctx->pd);
oz_free(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-1]);
usb_ctx = (struct oz_usb_ctx *)pd->app_ctx[OZ_APPID_USB-1];
if (usb_ctx)
oz_usb_get(usb_ctx);
spin_unlock_bh(&pd->app_lock[OZ_APPID_USB-1]);
if (usb_ctx == 0)
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_trace("oz_usb_stream_create(0x%x)\n", 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_trace("oz_usb_stream_delete(0x%x)\n", 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);
}
/* -----------------------------------------------------------------------------
* 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 */
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment